cm.abstract.dataSymbol

DsAPIPData which is the data used for snappers imported from stage has been removed. Stage snappers will now use DsPData which is used by regular catalogue symbols to be more consistent with CET catalogue symbols.

Component Tab File Format (*.cmtbxc) End of Life

As part of the EOL effort, the Component Tab file format (.cmtbxt) will also be retired and no longer be loadable as the streamed Component Tab class packages are removed. Ideally the Component Tab interfaces should only be streamed as part of the cmtbxt file format, and should not (and is not supposed to) be streamed with other cm file format (e.g., drawings, favorites). However, if the classes were somehow streamed as part of other file format, a new PackageStreamRenamer class is introduced to redirect these missing classes to a temporary placeholder class. Do note that this class serves no meaningful purpose, as it merely exists to suppress or bypass any load failure/errors caused by missing Component Tab related packages.

In developMode, the renamer will print extra info and a short stack dump so that it won't go fully unnoticed.

In cm.abstract.dataSymbol.renamer.cm:

/**
 * Stream renamer for removed toolbox creator package (cm.abstract.dataSymbol.ui.toolboxCreator).
 * which was permanently removed starting 17.0.
 *
 * Handles and redirect the streamed objs to a dummy class so it does not crash CET.
 */
private class DsToolboxCreatorRenamer extends PackageStreamRenamer {
    public void type(version v, Symbol pkg, Str name, Str fileName=null) {
	if (pkg.v == "cm.abstract.dataSymbol.ui.toolboxCreator") {
	    if (developMode) {
		pln("Attempting to load Component Tab interfaces!".eAngry; #pkg; #name);
		stackDump(3);
	    }

	    pkg.v = "cm.abstract.dataSymbol";
	    name.v = "DsDummyTBCreatorStreamPlaceHolder";
	}
    }


    /**
     * Deprecate old packages.
     */
    final private void deprecateOldPackages() {
	// Used to deprecate packages info stored in drawings to avoid load warnings about missing packages
	if (StreamRenamer r = globalStreamRenamer) {
	    r.deprecatePkg((#"cm.abstract.dataSymbol.ui.toolboxCreator"));
	}
    }
}


/**
 * Placeholder for streamed classes from 'cm.abstract.dataSymbol.ui.toolboxCreator'.
 */
public class DsDummyTBCreatorStreamPlaceHolder : unstreamable {
    /**
     * Load failure event.
     */
    public loadFailedResult loadFailed(ObjectFormatter formatter, LoadFailure failure) {
	return loadFailedResult.ignore;
    }
}

cm.abstract.ifc

Control of using a snapper's sym representation for IFC export delegated to IfcCoreFactory.

IFC export was using a snapper's sym representation to generate the IFC geometry of the IFC symbol. This was because sym supports geometries such as extrusions, which can be exported as solids. However, due to how certain SymNodes were generated, this resulted in compound solids which cause the geometries to go missing when viewed in certain IFC viewer softwares (such as Solibri and Revit) due to the rendering rules of those softwares. Defaulted this now to export using the snapper's mesh instead to improve the consistency of the visibility of IFC symbol geometries in IFC viewer softwares. However, if a manufacturer wishes to still export their snapper using the sym based representation (possibly to export as a solid), they can override extend public bool useSymBasedRepresentations() method in IfcExportCoreObjectFactory to return true.

cm.abstract.materialHandling

Unit load absorption load code

In 17.0 Major we have fixed an issue involving level snappers inserted without any unit loads. They do not have a MhSnapperBehavior in its stateBehaviorCollection field after insertion. The problem is that after inserting a UnitLoad onto a level, it does not get absorbed as a MhSnapperInfo due to the level missing the MhSnapperBehavior, and this behavior does not get initialized for existing levels.

We have since fixed it to start initializing MhSnapperBehavior when needed, and to ensure old drawings are handled we added this load code. If you have a custom level shape class that does not extend from MhLevelShape, you will need to copy the below code into your class.

public class MhLevelShape extends MhBoxSnapperShape {

    public void loaded1(ObjectFormatter formatter, LoadFailInfo failInfo) {
        super(..);
        mhUnstreamStoredSpec(formatter);

        // CETC-134365.
        if (formatter.version(#:package) < version(17, 0, 0)) {
            if (owner and !owner.snapperInfosBehavior()) {
                if (MhSystemConfiguration config = owner.configuration()) {
                    forChildren(MhSnapper child in owner) {
                        if (config.exportAsBehavior(child.classification)) {
                            owner.initSnapperInfosBehavior();
                            break;
                        }
                    }
                }
            }
        }
    }
}

Selection behavior in accessories

The following spawners have had the mhRowChildSelectionBehavior added to them. This changes their group selection behavior such that the row is no longer included in the selection when the accessory is selected. You may need to exclude this behavior if your custom class already appends a selection behavior.

  • MhCornerProtectorSpawner
  • MhDepthBeamSpawner
  • MhFlankProtectorSpawner
  • MhNamePlateSpawner
  • MhUprightProtectorSpawner
  • MhLevelAccessorySpawner

cm.core

cm.core

snapper.cm

The drawFeatures(..) method has been changed so that features for the 3D view are now generated from the snappers 3D mesh instead of using the same features as for the 2D view, which were often not accurate in 3D space. The method can be overriden to define a custom behaviour.

New Behavior

  • If in 2D view and the snapper overrides drawFeatures → Use the override
  • If in 2D view and the snapper does not override drawFeatures → Take features based on drawGraphs (2D graphics)
  • If in 3D view and the snapper overrides drawFeatures → Use the override
  • If in 3D view and the snapper does not override drawFeatures → Add features based on 3D mesh

Old Behavior

  • If in 2D view and the snapper overrides drawFeatures → Use the override
  • If in 2D view and the snapper does not override drawFeatures → Take features based on drawGraphs (2D graphics)
  • If in 3D view and the snapper overrides drawFeatures → Use the override
  • If in 3D view and the snapper does not override drawFeatures → Take features based on drawGraphs (2D graphics)

Core Snappers Overriding New Behavior

Some core snappers override drawFeature in order to retain the old behavior. The reasons for this vary, but one example is lines where we want it to snap to the mathematical (infinitely small) line rather than to the cylindrical mesh of the line. The following snappers have overriden the behavior:

  • Ceilings
  • Walls
  • Windows
  • Doors
  • Custom shapes
  • Lines

Required Developer Actions

  • If one of your snappers relies on the old behavior:
    • Test it and verify how it works. To restore the old behavior, override drawFeatures to return drawGraphs(..).
  • If one of your snapper's 3D model is not accurate to its real-world measures:
    • Override drawFeatures and define your own more accurate features.
  • If one of your snapper's already override drawFeatures or systemDrawFeatures:
    • Verify the override produces the desired output. If not, either update the override or remove it to use the new default 3D features based on the mesh.

In general, it's recommended to test the feature snapping of your extension and ensure it's working as expected. For example, check if the measure tool can be used to accurately measure the dimensions your objects.

cm.core3D

class Cylinder3D

Improved UV mapping so that textures get applied uniformly across the mesh at a UV-to-world scale of 1 UV unit = 1m. This change should generally not require any migration effort, unless your extension has taken measure to counteract the previously incorrect UV mapping. Please note that this change does not affect ClosedCylinder3D.

Below are comparisons of how it looked before (left) and how it looks after the change (right).

Vertical cylinder{ width=400px } Horizontal cylinder{ width=400px }

class Pyramid3D

Improved UV mapping so that textures get applied uniformly across the mesh at a UV-to-world scale of 1 UV unit = 1m. This change should generally not require any migration effort, unless your extension has taken measure to counteract the previously incorrect UV mapping.

Below are comparisons of how it looked before (left) and how it looks after the change (right).

Pyramid 1{ width=400px } Pyramid 2{ width=400px } Pyramid 3{ width=400px } Pyramid 4{ width=400px }

class Rect3D

Corrected the UV mapping of Rect3D's constructed with the type rect3DtypeWH. Previously, the width and depth component of the UV coordinates were swapped. This change should generally not require any migration effort, unless your extension has taken measure to counteract the previously incorrect UV mapping.

Below is a comparison of how it looked before (left) and how it looks after the change (right).

Rect3D{ width=400px }

cm.geometry2D

Previously, the inchesS(double v, bool showUnit, lcid local, int decimals, unitMagnitude magnitude) function would disregard the decimals value. This behavior has been updated to respect that argument.

cm.startup

class UserSettings

The location of saved preferences has changed from "\Documents\CET Documents\Preferences\" to "\AppData\Local\CET Preferences\". If CET can't find any saved preferences in the new location, it falls back to the old location.

When loading saved UserSettings (such as RtSettings and CoreSettings) CET looks for the file in the folder in "\CET Preferences\", where versionID is replaced with the versionID of the currently running CET ("64-bit" for the release, "Beta-64-bit" for the beta or the name of the workspace in develop mode for example). Previously, if the file was not found there, CET would search for older versions first by looking in the same folder with the same version ID, and secondly by recursively traversing all folders in "\CET Preferences" and use the first one found. This has now been changed so that CET will now only look for previous versions in the folder with the same version ID.

cm.std.accessories

class TestCubeSnapper

Changed the origin from the bottom-left corner to the bottom-center of the cube.

cm.std.wall

UV Improvements

Improved UV mapping of many snappers in the wall package. In general, all symbols should now apply textures more uniformly across the mesh at a UV-to-world scale of 1 UV unit = 1m. However, smaller inaccuracies may still exist.

This change affect the following symbols (and possibly more):

  • WallDoor
    • Frameless glass
    • Two panel
    • Two panel, top curved
    • Three panel
    • Panel with glass
    • Four panel
    • Five panel
  • WallWindow
    • Straight, Frameless glass
    • Straight, Glass with vault
    • Curved, None
    • Curved, Solid
    • Curved, Glass
    • Curved, Frameless glass
    • Curved, Circular
    • Curved, On edge vault
    • Curved, Glass with vault
  • WallSectionalDoor
  • Curtain
  • VenetiansBlinds
  • WindowSill
  • WallRadiator
  • WallImageSnapper
  • SimpleDrain
  • WallArc

Below are some sample comparisons of how it looked before (left) and how it looks after the change (right).

Door, 3 panel{ width=400px } Window, top curved{ width=400px } Curved windows{ width=400px } Curved 'On edge square' window{ width=400px } Curtain{ width=400px } Arced wall{ width=400px } Image on wall{ width=400px } Radiator{ width=400px }

cm.test.cmunit.testInstructions

class ClickInstruction

Now also triggers the snapper's click animation to more closely mimic an actual user click. Tests that start failing due to this are recommended be repeated by hand to figure out whether it's a false negative or not.

cm.win

Icon now returns DibImage

In 16.5, we introduced dibIcon(..) which loads icon as DibImage instead of MemoryImage. For 17.0, we are replacing the existing icon(..) to behave exactly as dibIcon(..).

Benefits of DibImage:

  • Can be resized with less artifacts as DibImage do not premultiply alpha values
  • Does not consume a windows GDI object, making it ideal for loading icons

For existing code that calls icon(..) and expects a MemoryImage, migration will be required. Common symptoms are:

  • Images not loading
  • Toolbox icons get rendered instead of loading an icon
  • Disabled images are not grayed out (blend is not applied)

How to resolve this:

  • Perform a Find in Files, search for "as MemoryImage" and "in MemoryImage"
  • Review the code if it can be generalized or change its logic accordingly
  • Perform the necessary changes to make it work with DibImage

In core, we had 2 common scenarios.

  1. Blend not being applied:
// Previous logic only handles MemoryImage.
byte beforeBlend = 255;
if (image as MemoryImage) {
    beforeBlend = image.blend;
    image.blend = 100;
}
image.transparentDraw(c.hdc, imgPos);
if (image as MemoryImage) image.blend = beforeBlend;

// New logic now handles MemoryImage, DibImage and SvgImage.
byte beforeBlend = image.blend;
image.blend = 100;
image.transparentDraw(c.hdc, imgPos);
image.blend = beforeBlend;
  1. UIBuilder not handling DibImage:
// Previous logic only handles MemoryImage and Icon
if (limb as SnapperLimb) {
	if (limb.image in MemoryImage or limb.image in Icon) {
		button = snapperImage(window, label, limb, limb.src);
	}
} else if (limb as AnimationLimb or limb.image in Icon) {
	if (limb.image in MemoryImage) {
		button = animationImage(window, label, limb, limb.src);
	}
}

// New logic now handles MemoryImage, Icon, DibImage and SvgImage
if (limb as SnapperLimb) {
	if (limb.image in MemoryImage or limb.image in Icon or limb.image in DibImage or limb.image in SvgImage) {
		button = snapperImage(window, label, limb, limb.src);
	}
} else if (limb as AnimationLimb) {
	if (limb.image in MemoryImage or limb.image in Icon or limb.image in DibImage or limb.image in SvgImage) {
		button = animationImage(window, label, limb, limb.src);
	}
}

New SVG renderer

In 16.5 Minor:

  • We have introduced a new SVG renderer that better supports the SVG spec.
  • It is only used for CET Icon Library, not affecting SVG loaded anywhere else.
  • The default SVG renderer a number of limitations, more information can be found here

For 17.0 Major:

  • We are defaulting to use the new SVG Renderer everywhere in CET.
  • You can choose to opt-out for cases the new renderer is incorrectly displaying the icons.
  • The new default SVG renderer aims to support the whole SVG spec, more information can be found here

If you spot issues loading your svg icons, you can pass newSvgRenderer=false when you call icon(..)

// New svg renderer causing issues
icon("myIcon", #:pkg)
icon("myIcon", false, #:pkg)
vectorImage(url);

// Fallback to svg renderer used in CET 16.5
icon("myIcon", #:pkg, newSvgRenderer=false)
icon("myIcon", false, #:pkg, newSvgRenderer=false)
vectorImage(url, newSvgRenderer=false);

IconFinder

As we now have better SVG rendering capabilities in CET, IconFinder will search for SVG first before other suffixes.

Old: public str[] suffixSearchPriority = [".png", ".bmp", ".cmpng", ".cmbmp", ".svg"];
New: public str[] suffixSearchPriority = [".svg", ".png", ".bmp", ".cmpng", ".cmbmp"];