Overview

CM Language

Multiple developers have discovered that the reverse keyword, used as part of the for syntax, will not always behave as intended. It is the intention of the syntax to cause the loop to reverse iterate over the members of the collection with no other changes/syntaxes/keywords necessary. However, in the current state, Ranges require the developer to also define the collection in reverse order, which shouldn't be necessary.

With this release, this behavior has been fixed and the reverse keyword now functions as intended for Ranges. As a consequnce of the fix, however, existing workarounds will no longer function. If you have used the "define the range in reverse" trick to workaround the problem, you will need un-reverse the range. Failure to make this change will cause the loop to be skipped entirely.

For example, if you have code that is currently written as:

for (i in 3..1, reverse) {

you will need to re-write it as:

for (i in 1..3, reverse) {

cm.abstract.dataSymbol

Component Tab and Component Tab Creator End of Life

In the CET 17.0 release in May 2026, Catalogue Component Tab and Component Tab Creator will reach their End-of-Life (EOL) and be retired completely.

As part of the EOL effort,

  • All component tab related interfaces will be removed. They will no longer receive bugfixes or be shipped as part of CET abstract/custom builds, and you will no longer be able to reference these classes in your packages. See compile-time changes below under "Component Tab and Component Tab Creator End of Life" section.
  • The Component Tab file format (.cmtbxt) will also be retired and no longer be supported starting from 17.0. See runtime/behaviour changes below under "Component Tab File Format (*.cmtbxc) End of Life"

All developers are strongly encouraged to migrate to the new Table of Content (ToC) Toolbox introduced in 15.5 Major as the replacement for the Component Tab - [read more and learn how to migrate or build toolbox with ToC here] (https://dev-docs.configura.com/design-system/cet-developer-guides/catalogue-table-of-contents-with-the-new-ui)

cm.core

Improved feature snapping in 3D

We've identified that feature snapping in 3D works poorly for most snappers, and that many extensions do not provide their own snap features. To fix this with minimal migration effort, 3D features are now generated from each snapper's mesh by default.

cm.core3D

Improved the texture mapping of the basic 3D shapes for rectangles, cylinders and pyramids.

cm.std.wall

Improved the texture mapping of many wall symbols such as arced walls, windows and curtains.

Compile Time Changes

CM Language

Definining a literal Range in reverse order as part of the for syntax will now cause the loop to be skipped entirely. Rewrite the Range literal to correct order. The regular expression for.*\(.*[.][.].*reverse can be used to quickly find all places in your code where you are using the reverse keyword with a literal Range.

cm.abstract.dataSymInterface

The constructors of DsiConstraintSelPath have been changed.

Old:
public constructor auto(path, feature, option);
public constructor auto(path, feature, option, checkedV);
public constructor auto(path, feature, option, numV);
New:
public constructor(str path, SFeature feature, Option option, DsiPDataOption dataOption, DsiPDataOption parentDataOption)

Component Tab and Component Tab Creator End of Life

The following interfaces will be removed as part of the Component Tab and Component Tab Creator EOL effort:

cm.abstract.dataSymInterface.catalog

class ProductCatalog

Removed: extend public void appendToolbox(str url)

cm.abstract.dataSymbol

https://git.configura.com/cet/external/base/-/merge_requests/42378<-->

Component Tab and Component Tab Creator End of Life

The following interfaces will be removed as part of the Component Tab and Component Tab Creator EOL effort:

cm.abstract.dataSymbol

Functions:
Removed: public bool dsCloneToolboxes(DataCatalog catalog, ProductCatalog productCatalog, DsToolboxCreatorToolboxCards toolboxCards)
Removed: public str dsToolboxFileStr(DataCatalog catalog, DsToolboxCreatorToolboxCards cards, DsToolboxCreatorToolboxCard card)

cm.abstract.dataSymbol.ui.toolboxCreator

Note: cm.abstract.dataSymbol.ui.toolboxCreator package is completely removed. If your extension has dependency to this package, please remove it.

// Constants
Removed: public const str dsToolboxCreatorFileType = ".cmtbxc";

// Functions
Removed: public Image DsToolboxCreatorLoadToolboxImage(Url file)
Removed: public DsToolboxCreatorExtensionToolboxCard dsToolboxCreatorCreateExtensionToolboxCard(Window parent, symbol pkg, DsToolboxCreatorToolboxCard card, str sortKey, str groupId, str libraryFunctionName=null, Image image=null, str label=null)
Removed: public str tbCatCardStatsEntityKey(DsCatalogToolboxData cData)
Removed: public void dsToolboxCreatorToolboxCardSetSectionLabelAnimationEnv(Window window, DsDragAnimationEnv env)
Removed: public void dsToolboxCreatorToolboxCardSetSectionAnimationEnv(Window window, DsDragAnimationEnv env)
Removed: public void dsToolboxCreatorToolboxCardSetThumbnailViewAnimationEnv(Window window, DsDragAnimationEnv env)
Removed: public void dsToolboxCreatorToolboxCardSetToolboxSectionWindowAnimationEnv(Window window, DsDragAnimationEnv env)
Removed: public void dsToolboxCreatorToolboxCardSetHeaderAnimationEnv(Window window, DsDragAnimationEnv env)
Removed: public void dsToolboxCreatorToolboxCardSetImageSectionAnimationEnv(Window window, DsDragAnimationEnv env)
Removed: public void dsToolboxCreatorToolboxCardSetSchemeButtonAnimationEnv(Window window, DsDragAnimationEnv env)
Removed: public Library dsToolboxCreatorLibraryFromCard(Card card, symbol pkg)
Removed: public void faceliftPreprocess(Library library)
Removed: public str->str dsDefaultSchemeDescriptionMap(str{} languages)

// Classes
Removed: public class DsToolboxCreatorCardPropertyWindow extends SubWindow
Removed: public class DsToolboxCreatorCardWindow extends CardWindow
Removed: public class DsToolboxCreatorControlWindow extends SubWindow
Removed: public class DsToolboxCreatorDataCatalogHeader extends DsDataCatalogHeader
Removed: public class DsToolboxCreatorDataCatalogSectionedLabel extends DsDataCatalogSectionedLabel
Removed: public class DsToolboxCreatorDataCatalogSectionedScrollableSubWindow extends DsDataCatalogSectionedScrollableSubWindow
Removed: public class DsToolboxCreatorDesignWindow extends SubWindow
Removed: public class DsToolboxCreatorDialog extends DialogWindow
Removed: public class DsToolboxCreatorDeletionSubWindow extends SubWindow
Removed: public class DsToolboxCreatorDragAnimation extends DsDragAnimation
Removed: public class DsToolboxCreatorDropDownTreeView extends DropDownTreeView
Removed: public class DsToolboxCreatorDynamicCard extends DsDynamicCard
Removed: public class DsToolboxCreatorExtensionToolboxCard extends ExtensionToolboxCard
Removed: public class DsToolboxCreatorFillerInsertDragAnimation extends DsToolboxCreatorThumbnailViewMoveDragAnimation
Removed: public class DsToolboxCreatorFillerMoveDragAnimation extends DsToolboxCreatorThumbnailViewMoveDragAnimation
Removed: public class DsToolboxCreatorFillerThumbnail extends DsThumbnail
Removed: public class DsToolboxCreatorFocusLines
Removed: public class DsToolboxCreatorFocusRect
Removed: public class DsToolboxCreatorGroupInsertDragAnimation extends DsToolboxCreatorDragAnimation
Removed: public class DsToolboxCreatorGroupMoveDragAnimation extends DsToolboxCreatorGroupInsertDragAnimation
Removed: public class DsToolboxCreatorHeaderInsertDragAnimation extends DsToolboxCreatorGroupInsertDragAnimation
Removed: public class DsToolboxCreatorHeaderLimb extends DsDynamicHeaderLimb
Removed: public class DsToolboxCreatorHeaderMoveDragAnimation extends DsToolboxCreatorGroupMoveDragAnimation
Removed: public class DsToolboxCreatorHeaderPropertyWindow extends SubWindow
Removed: public class DsToolboxCreatorImageSectionInsertDragAnimation extends DsToolboxCreatorDragAnimation
Removed: public class DsToolboxCreatorImageSectionMoveDragAnimation extends DsToolboxCreatorImageSectionInsertDragAnimation
Removed: public class DsToolboxCreatorImageSectionPropertyWindow extends ShrinkWindow
Removed: public class DsToolboxCreatorLibraryLimb extends DsDynamicLibraryLimb
Removed: public class DsToolboxCreatorProductCatalogLimbBuilder extends DsProductCatalogLimbBuilder
Removed: public class DsToolboxCreatorProductLevelPropertySubWindow extends ShrinkWindow
Removed: public class DsToolboxCreatorProductViewInsertDragAnimation extends DsDragAnimation
Removed: public class DsToolboxCreatorPropertyWindow extends SubWindow
Removed: public class DsToolboxCreatorSchemeButton extends DsDataCatalogSchemeButton
Removed: public class DsToolboxCreatorSchemeButtonInsertDragAnimation extends DsToolboxCreatorDragAnimation
Removed: public class DsToolboxCreatorSchemeButtonMoveDragAnimation extends DsToolboxCreatorSchemeButtonInsertDragAnimation
Removed: public class DsToolboxCreatorSchemeButtonPropertyWindow extends ShrinkWindow
Removed: public class DsToolboxCreatorSectionLabelPropertyWindow extends SubWindow
Removed: public class DsToolboxCreatorSideBarPropertiesPropertyWindow extends DsInWindowSideBarProperties
Removed: public class DsToolboxCreatorThumbnailsLimb extends DsThumbnailsLimb
Removed: public class DsToolboxCreatorThumbnailPropertyWindow extends SubWindow
Removed: public class DsToolboxCreatorThumbnailViewMoveDragAnimation extends DsThumbnailViewMoveDragAnimation
Removed: public class DsToolboxCreatorToolboxCard
Removed: public class DsToolboxCreatorToolboxCards
Removed: public class DsToolboxCreatorToolboxSectionedScrollableSubWindow extends ToolboxSectionedScrollableSubWindow
Removed: public class DsToolboxCreatorToolboxThumbnail extends DsToolboxThumbnail
Removed: public class DsToolboxCreatorToolboxThumbnailView extends DsToolboxThumbnailView
Removed: public class DsToolboxCreatorTooltip
Removed: public class DsToolboxCreatorTreeViewItem extends TreeViewItem
Removed: public class DsToolboxCreatorHeaderTreeViewItem extends DsToolboxCreatorTreeViewItem
Removed: public class DsToolboxCreatorGroupTreeViewItem extends DsToolboxCreatorTreeViewItem
Removed: public class DsToolboxCreatorProductViewTreeViewItem extends DsToolboxCreatorTreeViewItem
Removed: public class DsToolboxCreatorFillerTreeViewItem extends DsToolboxCreatorTreeViewItem
Removed: public class DsToolboxCreatorImageSectionTreeViewItem extends DsToolboxCreatorTreeViewItem
Removed: public class DsToolboxCreatorSchemeButtonTreeViewItem extends DsToolboxCreatorTreeViewItem
Removed: public class DsToolboxCreatorUIBuilder extends DsNewUIBuilder
Removed: public class DsToolboxCreatorUIGroupBuilder extends DsUIGroupBuilder

cm.abstract.draw

class DrawLine

Changed the length setter function from final to extend.

Old: final public double length=(double v) {
New: extend public double length=(double v) {
Removed: extend public void lengthChanged() {

class DrawMeasure

Renamed two methods to more clearly convey what they do.

Old: extend public void setP0(point p, Angle forcedAngle=null, bool updateD=true) {
New: extend public void setSpaceP0(point p, Angle forcedAngle=null, bool updateD=true) {
Old: extend public void setP1(point p, Angle forcedAngle=null) {
New: extend public void setSpaceP1(point p, Angle forcedAngle=null) {

class DrawText

Removed the following functions:

Removed: final public void drawHelpFrameIfSelected(GInstance gs) {
Removed: extend public void drawMarginHelpFrameIfSelected(GInstance gs, rect mr) {
Removed: final public void drawHelpFrameIfSelectedOrZoomedOut(GInstance gs, GTextBox gText) {

Instead, use one of these:

Added: final public void drawHelpFrame(GInstance gs) {
Added: final public void drawMarginHelpFrame(GInstance gs) {

class LinearReplicateAction

Old: extend public void addSuperStretchVessel(point p) {
New: extend public void addSuperStretchVessel(point p, angle a) {

cm.abstract.extrusions

class AbsExtrCollection

Changed the constructor to optionally support custom labels for collections.

Old: public constructor(str collectionName, str dbPath, str namespace, str uom, symbol[] tags, int version, Material m) {
New: public constructor(str collectionName, str dbPath, str namespace, str uom, symbol[] tags, int version, Material m, str collectionLabel=null) {

Steal was removed and made inaccessible, not meant to be used from the outside.

Old: extend public void steal(AbsExtrCollection f) {
New: extend package void registryCopyContent(AbsExtrCollection f) {

Minor name changes:

Old: final public AbsExtrProfile get(str name) {
New: final public AbsExtrProfile get(str id) {

class AbsExtrProfile

Made member not intended to be changed from the outside private but public readable.

Old: public str id;
New: private str id : public readable;

class AbsExtrProfileDef

Added a optional construction argument to decide whether the profile shape should be closed or not.

Old: public constructor(str coll, str name, AbsExtrShapeDefItem[] shapeDef) {
New: public constructor(str coll, str name, AbsExtrShapeDefItem[] shapeDef, bool closeProfile=true) {

cm.abstract.material

AbsMaterial thumbnailType

AbsMaterial now includes thumbnailType method, subclasses will need to remove extend keyword if they had previously defined this method.

AbsMaterial
public class AbsMaterial extends CachedMaterial {
    extend public gmThumbnailType thumbnailType() {..}
}
public class FOMaterial extends OfficeMaterial {
    Old: extend public gmThumbnailType thumbnailType() {..}
    New: public gmThumbnailType thumbnailType() {..}
}

cm.abstract.materialHandling

System configuration changes

The abstract class MhSystemConfiguration, and the child classes MhStorageConfiguration and MhStorageEditorConfiguration have had similar methods declared in them.

MhStorageConfiguration.configUserPropertyChanged(str key, Object value, Object oldValue, Object propOwner, MhStorageConfigurationItem item) and MhStorageEditorConfiguration.userPropertyChange(MhStorageEditorItem item, CoreProperty property, Object oldValue) were both doing similar logic and so we have now created a new method in the parent class MhSystemConfiguration that calls this similar functionality. If you had previously overridden either of these two methods, you should now override configUserPropertyChanged(MhSystemConfigurationItem item, CoreProperty property, Object oldValue) instead.

public class MhSystemConfiguration extends CoreObject {

    Added:
    /**
     * Config user property changed.
     */
    extend public void configUserPropertyChanged(MhSystemConfigurationItem item, CoreProperty property, Object oldValue) {
        if (property.owner != item) {
            item.?additionalPropChanged(property.key, property.value, oldValue, property.owner);
        }

        configUserPropertyChanged(property.key, property.value, oldValue, property.owner, item);
    }
}


public class MhStorageConfiguration extends MhSystemConfiguration {

    Removed:
    /**
     * configUserPropertyChanged - user changed property. Adjust configuration accordingly.
     */
    extend public void configUserPropertyChanged(str key, Object value, Object oldValue, Object propOwner, MhStorageConfigurationItem item) {
        for (it in items) {
            it.configUserPropertyChanged(..);
        }
    }
}


public class MhStorageEditorConfiguration extends MhSystemConfiguration {

    Removed:
    /**
     * User property changed.
     */
    extend public void userPropertyChange(MhStorageEditorItem item, CoreProperty property, Object oldValue) {
        if (property.owner != item) {
            item.additionalPropChanged(property.key, property.value, oldValue, property.owner);
        }
        
        if (space and !skipPushToPreview(..-)) {
            beforePushPropToPreview(..);
            pushPropToPreview(space, ..-);
            afterPushPropToPreview(..);
        }
    }
}

Aisle function changes

The function Snapper getNextSingleAisle(MhSnapper snapper, bool top=true) has been removed and a function Snapper getNextAisle(MhSnapper snapper, bool top=true) has been added.

Removed:
public Snapper getNextSingleAisle(MhSnapper snapper, bool top=true) {
    for (r in getAllRows(snapper, top=top, reverse=false)) {
        if (r == snapper) continue;
        if (r.isSingle and r.isAisle) return r;
    }

    return null;
}


Added:
public Snapper getNextAisle(MhSnapper snapper, bool top=true) {
    for (r in getAllRows(snapper, top=top, reverse=false)) {
        if (r == snapper) continue;
        if ((r.isDouble and r.isAisle and !r.isFlueGap) or
            (r.isSingle and r.isAisle)) return r;
    }

    return null;
}

Engine entry additional shape arguments

Previously MhEngineEntry has a map field str->Object additionalShapeArgs that allows storing key-value data. This has now been changed to a private field str->Object _additionalShapeArgs and new public methods have been introduced with the same name. This is so that the source of this map can be overridden.

For example, MhEngineConstructionEntry overrides the additionalShapeArgs methods to put/retrieve data from the owned constructionInfo() object instead. The class MhEngineConstructionEntryInfo now has a new field str->Object _additionalShapeArgs and method str->Object additionalShapeArgs(). This allows the map of the info class to be set independently and for the data to later be assigned to a MhEngineConstructionEntry at a later time.

// cm/abstract/materialHandling/engine/mhEngineEntry.cm
public class MhEngineEntry extends MhEngineEntryBase {
    Old: public str->Object additionalShapeArgs;
    New: private str->Object _additionalShapeArgs;
    New: extend public str->Object additionalShapeArgs() { }
    New: extend public str->Object additionalShapeArgs=(str->Object args) { }
    New: extend public Object getAdditionalShapeArg(str key) { }
    New: extend public void putAdditionalShapeArg(str key, Object val) { }
}

// cm/abstract/materialHandling/engine/mhEngineConstructionEntry.cm
public class MhEngineConstructionEntryInfo {
    New: private str->Object _additionalShapeArgs;
    New: extend public str->Object additionalShapeArgs() { }
}

// cm/abstract/materialHandling/mhSnapperSpawner.cm
public class MhSnapperSpawner extends SnapperSpawner {
    Old: extend public MhEngineConstructionEntry engineConstructionEntry() { }
    New: extend public MhEngineConstructionEntry engineConstructionEntry(MhEngineConstructionEntryInfo info=null) { }
}

The additionalShapeArgs are primarily used to push property values to spawned snappers on constructions. They can also be used to hold distinct entry values to be used during engine functions.

These additionalShapeArgs are applied in three areas during construction, one during instantiation of an MhEngineConstructionEntry, one during spawning of a snapper, and one more time after export of entry.

1. Instantiation of `MhEngineConstructionEntry`
public class MhSnapperSpawner extends SnapperSpawner {

    /**
     * Engine construction entry (used by entry layout only).
     */
    extend public MhEngineConstructionEntry engineConstructionEntry(MhEngineConstructionEntryInfo info=null) {
        MhSnapperShape shape = createShape();
        for (key, val in info.?additionalShapeArgs) shape.?changeShape(key, val);
        ...
    }
}

2. Spawning of a snapper
public class MhEngineConstructionEntry extends MhEngineSnapperEntry {

    /**
     * Spawn snapper.
     */
    extend public MhSnapper spawnSnapper(MhEngine engine, MhSystemSpawnerSelector spawnerSelector=null) {
        ...
        if (MhSnapper s = spawnerSelector.?spawnSnapper(classification, shapeArgs=shapeArgs+additionalShapeArgs, env=this)) {
            return s;
        }
        ...
    }
}

3. After export of entry.
public class MhSnapperShape extends CorePropObj {

    /**
     * Entry exported.
     */
    extend public void entryExported(MhSnapper snapper, MhEngineEntry entry) {
        ...

        // Apply additional shape args on exported as they are not included in entry cache key so export function might have spawned snapper without using correct shape args from mhSystemEngineCache.
        if (entry as MhEngineConstructionEntry) {
            for (key, val in entry.additionalShapeArgs()) {
                changeShape(key, val);
            }
        }
    }
}

Level clearance changes

Several changes have been made for handling level clearances in the abstract. First of all, the class MhClearanceSpecLevelClearanceBehavior and the field levelClearanceZ field in MhClearanceSpec have been removed.

How this would be used is the MhClearanceSpecLevelClearanceBehavior class would call spec.?collisionPrimitives(shape, env) which by default would call MhClearanceSpec.levelClearanceCollision(MhSnapperShape shape, MhCollisionFetchEnv env) that returns a CollisionPrimitive built based on spec.levelClearanceZ. This would result in a CollisionPrimitive always being built for the level.

Removed: public mhLazyGlobal MhBehavior mhClearanceSpecLevelClearanceBehavior = MhClearanceSpecLevelClearanceBehavior();
Removed: public class MhClearanceSpecLevelClearanceBehavior extends MhClearanceSpecBehavior 

public class MhClearanceSpec extends CorePropObj {
    Removed: public Double levelClearanceZ;
    Removed: Prop cMhLevelClearanceZPK : attributes={#allowPush, #allowPushNull};
}

Instead of always building a CollisionPrimitive for the level to represent the level clearance, we now only append this primitive during the construction of entries.

public class MhLevelShape extends MhBoxSnapperShape {

    Old:
    extend public void appendLevelClearancePrimitives(CollisionPrimitive{} prims,
                                                      MhCollisionFetchEnv env) {
        if (?MhClearanceBehavior b = owner.?behavior("levelClearance")) {
            if (!env) env = MhCollisionFetchEnv();
            Transform t = env.transform;

            double elevation;
            if (t) elevation = t.pos.z;
            else elevation = owner.toSpaceTransform().pos.z;
            MhClearanceCollisionFetchEnv clearanceEnv(env, elevation,
                                                      owner.rootParent.MhSnapper.?classification);
            b.appendCollisionPrimitives(this, clearanceEnv, prims);
        }
    }


    New:
    extend public void appendLevelClearancePrimitives(CollisionPrimitive{} prims,
                                                      MhCollisionFetchEnv env) {
    }
}


public class MhLevelSpawner extends MhStorageSpawner {

    New:
    /**
     * Return construction entry collision primitive.
     */
    public CollisionPrimitive engineConstructionCollision(MhSnapperShape shape,
                                                          MhEngineConstructionEntry entry) {
        CollisionPrimitive res = super(..);
        appendLevelClearancePrimitive(res, shape, entry);
        return res;
    }


    New:
    /**
     * Append level clearance primitive.
     * Simple implementation of level clearance when using MhCompartmentType with no unit loads. For custom logic, override either this or MhClearanceSpec.levelClearanceCollision().
     */
    extend public void appendLevelClearancePrimitive(CollisionPrimitive prim,
                                                     MhSnapperShape shape,
                                                     MhEngineConstructionEntry entry) {
        if (prim as CollisionPrimitiveSet) {
            if (?MhClearanceBehavior b = statelessBehavior("Clearance")) {
                MhCollisionFetchEnv env(reason=#construction, entry=entry);
                b.appendCollisionPrimitives(shape, env, prim.subPrims);
            }
        }
    }
}

Due to the above change in behavior, the ownership of a level clearance value has been changed from the clearance spec to the engine entry, more specifically it is stored in the additionalShapeArgs() of MhEngineEntry.

public class MhClearanceSpec extends CorePropObj {

    Old:
    /**
     * Collision primitives based on `levelClearanceZ`.
     */
    extend public CollisionPrimitive[] levelClearanceCollision(MhSnapperShape shape,
                                                               MhCollisionFetchEnv env) {
        CollisionPrimitive[] res(4);
        if (levelClearanceZ and shape) {
            box b = shape.localBound();
            b.h = levelClearanceZ.v;
            static Layer layer = Layer(layerSet(sClearance, sLevelClearanceZ), layerSet(sLevel, sLevelFrame));
            res << mhGetBoxCollisionPrimitive(b, layer, env.transform);
        }
        return res.any ? res : null;
    }


    New:
    /**
     * Collision primitives for level clearance.
     */
    extend public CollisionPrimitive[] levelClearanceCollision(MhSnapperShape shape,
                                                               MhCollisionFetchEnv env) {
        CollisionPrimitive[] res(4);

        // Append level clearance collision from MhCompartmentType during construction.
        if (env.?reason == #construction) {
            MhEngineEntry entry = env.entry;
            if (?Double levelClearance = entry.?getAdditionalShapeArg("levelClearance")) {
                box b = shape.?localBound([sLevelFrame]);
                b.h += levelClearance.v;
                static Layer layer = Layer(layerSet(sClearance, sLevelClearanceZ),
                                           layerSet(sLevel, sLevelFrame));
                res << mhGetBoxCollisionPrimitive(b, layer);
            }
        }

        return res.any ? res : null;
    }
}


public class MhCollisionFetchEnv {
    New: public MhEngineEntry entry;

    Old: public constructor(symbol reason=#none, Transform transform=null, bool includeChildren=false, Object arg=null)
    New: public constructor(symbol reason=#none, Transform transform=null, bool includeChildren=false, MhEngineEntry entry=null, Object arg=null)
}

Unit load clearance changes

Changes have been made to support different level entries having different unit load clearances. MhUnitLoadSpawner can pass in different clearance specs for each level entry instead of always using the same clearance spec from the configuration.

public class MhEntryLayoutEnv {
    New: public MhEngineEntry entry;
}

public class MhUnitLoadSpawner extends MhStorageSpawner {

    Old:
    extend public void appendAdditionalConstructionCollision(MhEngineConstructionEntry entry, str unitLoadKey, LayerSet rootParentClassification, MhEntryLayoutEnv env) {
        ...
        for (MhClearanceSpecBehavior behavior in collection.behaviors) {
            MhStorageClearanceCollisionFetchEnv clearanceEnv(..., clearanceSpec=config.?clearanceSpec);
            behavior.appendCollisionPrimitives(loadShape, clearanceEnv, set.subPrims);
        }
    }


    New:
    extend public void appendAdditionalConstructionCollision(MhEngineConstructionEntry entry, str unitLoadKey, LayerSet rootParentClassification, MhEntryLayoutEnv env) {
        ...
        MhClearanceSpec spec;
        if (?MhEngineConstructionEntry parent = env.entry) {
            if (?MhClearanceSpec cSpec = parent.getAdditionalShapeArg(cMhClearanceSpecPK)) {
                spec = cSpec;
            }
        }
        if (!spec) spec = config.?clearanceSpec;
        for (MhClearanceSpecBehavior behavior in collection.behaviors) {
            MhStorageClearanceCollisionFetchEnv clearanceEnv(..., clearanceSpec=spec);
            behavior.appendCollisionPrimitives(loadShape, clearanceEnv, set.subPrims);
        }
    }
}

Unit load construction changes

We have added further control over the number of unit loads populated during construction.

MhUnitLoadConstructionalPopulateFunction class (unitLoadConstructionalPopulate) had the argument loadCount, which was passed to MhConstructionUnitLoadArrangement to populate unit loads perpendicular to the populate vector. It has been renamed to repeatCount. Additionally, a new argument ulCount has been added to represent the number of unit loads to populate in the direction of population. If left as null, the previous behavior will be used which is to populate as many loads as possible. Similarly the MhConstructionUnitLoadArrangement class now holds fields repeatCount and ulCount in place of the previous field count.

Effectively, previous specification of loadCount will now need to specify repeatCount instead. Make sure to search for loadCount in your extension as engine function execute arguments do not throw compile errors for incorrect arguments.

// cm/abstract/materialHandling/storage/engine/mhUnitLoadConstructionalPopulateFunction.cm
public class MhUnitLoadConstructionalPopulateFunction extends MhSystemEngineFunction {
    Old: public Int loadCount;
    New: public Int repeatCount; //replaces loadCount
    New: public int ulCount;
}

// cm/abstract/materialHandling/storage/mhUnitLoadArrangement.cm
public class MhConstructionUnitLoadArrangement extends MhUnitLoadArrangement {
    Old: public int count;
    New: public int repeatCount; //replaces count
    New: public int ulCount;

    Old: public constructor(MhPopulator populator, MhEngineEntry defaultLoadEntry, int count=1, vector direction=(0, 1, 0), bool spreadEvenly=true) { }
    New: public constructor(MhPopulator populator, MhEngineEntry defaultLoadEntry, int ulCount=0, int repeatCount=1, vector direction=(0, 1, 0), bool spreadEvenly=true) { }
}

The repeatCount and ulCount fields for MhUnitLoadConstructionalPopulateFunction are typically passed in from MhLevelConstructionalPopulateFunction.levelChildConstruction(MhEngineConstructionEntry entry) where it passes the values from entry.constructionInfo. Additionally in MhEngineLevelConstructionEntryInfo, the field noOfUnitLoads was renamed to noOfUnitLoadsX and both noOfUnitLoadsX and noOfUnitLoadsY fields are now boxed Int types instead of primitive int.

// cm/abstract/materialHandling/storage/engine/mhLevelConstructionalPopulateFunction.cm
public class MhLevelConstructionalPopulateFunction extends MhSystemEngineFunction {

    /**
     * levelChildContruction
     */
    extend public void levelChildConstruction(MhEngineConstructionEntry entry) {
        ...
            ?MhEngineLevelConstructionEntryInfo info = entry.constructionInfo;
            Int ulXCount = info.?noOfUnitLoadsX;
            Int ulYCount = info.?noOfUnitLoadsY;
            if (!ulYCount) ulYCount = configuration.unitLoadCountY();

            engine.exec("unitLoadConstruction", ...,
                        ulCount=ulXCount, repeatCount=ulYCount,
                        ...);
        ...
    }
}

public class MhEngineLevelConstructionEntryInfo extends MhEngineConstructionEntryInfo {
    Old: public int noOfUnitLoads;
    New: public Int noOfUnitLoadsX;
    Old: public int noOfUnitLoadsY;
    New: public Int noOfUnitLoadsY;
}

MhStorageEditorConfigPropModifyUndoOp changes

Undo operation class MhStorageEditorConfigPropModifyUndoOp now stores the Guid to the config instead of the config object itself.

public class MhStorageEditorConfigPropModifyUndoOp extends MhStorageEditorUndoOp {
    Removed: public MhStorageEditorConfiguration config : copy=reference;
    New: public Guid configGid;
    New: public constructor(MhStorageEditorDialog dialog, Guid configGid, str propKey, Object propVal) {
}

Storage editor visibility toggle changes

Removed classes MhStorageEditorDimensionToolbarModelItem and MhBayEditorElevArrowToolbarModelItem. Both functionalities are now implemented in MhStorageEditorConfiguration. Instead of separate toolbar items, they have been combined into one visibility toolbar item.

Removed: public class MhStorageEditorDimensionToolbarModelItem extends ToolbarModelItem {
Removed: public class MhBayEditorElevArrowToolbarModelItem extends ToolbarModelItem {
Removed: public const str cMhArrowVisibilityStateKey = "arrowVisibilityState";


public class MhStorageEditorConfiguration extends MhSystemConfiguration {

    New:
    /**
     * Allow dimension visibility toggle?
     */
    extend public bool allowDimensionVisibilityToggle(View view) {
        return false;
    }


    New:
    /**
     * Allow arrow visibility toggle?
     */
    extend public bool allowArrowVisibilityToggle(View view) {
        return false;
    }


    New:
    /**
     * Dimension visibility toggle changed.
     */
    extend public void dimensionVisibilityToggleChanged(bool state, View view) {
        if (Space space = view.?space) {
            mhStorageEditorDimensionTogglePutCached(space, value=state);
            for (s in space.snappers) mhUpdateEditorDimensionVisibility(s);
        }
    }


    New:
    /**
     * Arrow visibility toggle changed.
     */
    extend public void arrowVisibilityToggleChanged(bool state, View view) {
        if (Space space = view.?space) {
            mhStorageEditorArrowTogglePutCached(space, value=state);
            insertArrowVessel(space);
        }
    }


    New:
    /**
     * Update snappers visibility.
     */
    extend public void updateSnappersVisibility() {
        for (s in space.snappers) mhUpdateEditorDimensionVisibility(s);
        insertArrowVessel(space);
    }
}

The dimensions toggle is visible by default for bay editor. The arrow toggle is visible by default for frame editor, and bay editor's 3D view.

Editor visibility toggles

cm.abstract.mezzanine

AbsMezzLevelRectInsertAnimation changes

Removed field str volumeId and replaced with a private field private symbol volumeId. Replaced methods str volId=(str id) with symbol volId=(symbol id) and str volId() with symbol volId().

public class AbsMezzLevelRectInsertAnimation extends DrawShapeRectAnimation {

    Old: public str volumeId;
    New: private symbol volumeId;

    Old: extend public str volId=(str id) {
    New: extend public symbol volId=(symbol id) {

    Old: extend public str volId() {
    New: extend public symbol volId() {
}

Mezzanine function changes

Removed function void mezzInsertRails(APolyline2D poly, AbsMezzDecking{} deckings, Space space). Call function void mezzInsertRailGroup(APolyline2D poly, APolyline2D[] holes, AbsMezzDecking{} deckings, Space space) instead.

[runtime-behavior] Run-time/behavior changes that may require developers to account for

cm.application

releaseDebugActionCard.cm

Removed: public bool dbg_verboseProfilerDump = false;
Removed: public void repairBlocks() {

cm.core

featureFilter.cm

Removed enum that is not used anywhere in the base repository.

Removed: public enum featureDetail : field access {

ContextualViewDropDownItem

The visibility of dropDownImage field has been altered, you will need to use setDropDownImage to change the image.

Old: public Image dropDownImage : copy=reference;
New: private Image dropDownImage : copy=reference, public readable;
New: final public void setDropDownImage(Image i)

InfoTipToggle

This class has been removed. You can replace it with FaceliftToggle

Old: public class InfoTipToggle extends CheckBox {
New: public class FaceliftToggle extends CheckBox {

selectLasso2DAnimation.cm

The following interfaces in SelectLasso2DAnimation have been changed:

Old: extend public bool isTarget(Snapper s, APolyline2D pline=null, bool quickElim=true) {
New: extend public bool isTarget(Snapper s, APolyline2D pline) {

The following interfaces in SelectLasso2DAnimation have been removed:

Removed: final public void appendTargets(line2D l) {
Removed: extend public bool contained(Snapper s, APolyline2D pline=null) {
Removed: extend public bool ridiculous(rect sb, APolyline2D pline) {
Removed: extend public bool plausible(rect sb, APolyline2D pline) {
Removed: extend public GeometricFinder[] getFinders(APolyline2D pline) {
Removed: extend public int snapperBoundInPolyline(Snapper s, APolyline2D pline) {
Removed: extend public int graphInPolyline(GraphPrimitive gp, APolyline2D pline, line2D e, bool oneUncontained, bool seen) {

Query language

Deprecated functions have been removed.

Old: public void removeOldCetQLParamsFromSnapper(Snapper z) : deprecated {
New: public void removeCetQLParamsFromSnapper(Snapper z) {

cm.core.selection

selectionVisualizer.cm

Removed: public class FunkySelectionVisualizer extends SelectionVisualizer {

cm.core.ui

libHelpText.cm

Made showLibHelpText return the constructed window.

Old: public void showLibHelpText(Window parent, str text="No help available in BETA version.", str title=null, str key=null, int width=300, PointI pos=null, symbol pkg=null) {
New: public DialogWindow showLibHelpText(Window parent, str text="No help available in BETA version.", str title=null, str key=null, int width=300, PointI pos=null, symbol pkg=null) {
Removed: public DialogWindow showLibHelpText2(str pkg="cm.std.tools", Window parent=null, str text="No help available in BETA version.", str key=null, PointI p=null) {

cm.core.user

class userDimensionStyle

Added a bool suppressZeroFraction argument to the constructor.

Old: public constructor(symbol pkg, str key, str name, alignment textAlignment, distanceUnit unit, int precision1, distanceUnit unit2, int precision2, bool suppressZeroFraction, double arrowHSize, Class arrowHType, Class arrowHType2, double gap, double extLineExt, bool onlyPaperView=false, bool stackDims=false, Bool showDistanceUnit=null) {
New: public constructor(symbol pkg, str key, str name, alignment textAlignment, distanceUnit unit, int precision1, distanceUnit unit2, int precision2, bool suppressZeroFraction, double arrowHSize, Class arrowHType, Class arrowHType2, double gap, double extLineExt, bool suppressZeroFeet=false, bool onlyPaperView=false, bool stackDims=false, Bool showDistanceUnit=null) {

cm.draw

class LineType

Combined all 5 constructors into 1. The default argument values are the same as before in all cases except for the constructor with the bool real argument. There shouldn't have been any reason to use that constructor since "real" width lines have never been supported. But if you want to ensure the same behavior as before, specify widthStyle=lineWidthStyle.variable for any instance that previously set the bool real argument to any value.

In the majority of cases though, all that needs to be done is to add the argument hint for the width (e.g. LineType(.., .., w=5)).

Old: public constructor() {
Old: public constructor(color lineColor) {
Old: public constructor(color lineColor, lineStyle style, int width=0) {
Old: public constructor(color lineColor, double width, bool real=false) {
Old: public constructor(color lineColor, lineStyle style, lineWidthStyle widthStyle, double w) {
New: public constructor(color lineColor=black, lineStyle style=lineStyle.solid, lineWidthStyle widthStyle=lineWidthStyle.unit, int w=0) {

Also consider if you should be using the LineType constructor at all. As long as the LineType instance does not need to be modified, it is recommended to get it using the lineType(..)-function instead, which returns a cached copy for improved performance.

lineType.cm

Change the width argument from a double to an integer, which is what the value has always been converted to internally. If you have previously passed it a fractional value and is unsure what to replace it with, you can just cast it to an integer (e.g. myVar.int) to maintain the same behaviour as before.

Old: public str lineCacheKey(color c, lineStyle style, lineWidthStyle widthStyle, double width) : inline {
New: public str lineCacheKey(color c, lineStyle style, lineWidthStyle widthStyle, int width) : inline {
Old: public LineType lineType(color c, lineStyle style=lineStyle.solid, lineWidthStyle widthStyle=lineWidthStyle.unit, double width=0) {
New: public LineType lineType(color c, lineStyle style=lineStyle.solid, lineWidthStyle widthStyle=lineWidthStyle.unit, int width=0) {

cm.extension

class ExtensionManager

The interfaces to getStateContaining and getInfoContaining have been changed to allow bypassing the pkgStateCache:

Old: final public ExtensionState getStateContaining(symbol pkg)
New: final public ExtensionState getStateContaining(symbol pkg, bool skipCache=false)

Old: final public ExtensionInfo getInfoContaining(symbol pkg)
New: final public ExtensionInfo getInfoContaining(symbol pkg, bool skipCache=false)

cm.geometry

class PickLineInfo

The constructor now requires you to specify the start point and the end point of the line, in world coordinates.

Removed: public constructor(pointF ipWC, int lineIndex) {
Added: public constructor(pointF ipWC, pointF p0WC, pointF p1WC, int lineIndex) {

cm.geometry2D

angle.cm

Renamed the atan functions that take 2 arguments to be correctly called atan2.

Old: public angle atan(double y, double x) : nonfglue = ang_atan2;
New: public angle atan2(double y, double x) : nonfglue = ang_atan2;
Old: public double atand(double y, double x) : nonfglue = ang_atan2d;
New: public double atan2Rad(double y, double x) : nonfglue = ang_atan2d;
Old: public double atanquick(double y, double x) : nonfglue = ang_qatan2d;
New: public double quickAtan2Rad(double y, double x) : nonfglue = ang_qatan2d;

angleF.cm

Renamed the atan functions that take 2 arguments to be correctly called atan2.

Old: public angle atan(float y, float x) : nonfglue = angF_atan2;
New: public angle atan2(float y, float x) : nonfglue = angF_atan2;
Old: public float atand(float y, float x) : nonfglue = angF_atan2f;
New: public float atan2Rad(float y, float x) : nonfglue = angF_atan2f;
Old: public float atanquick(float y, float x) : nonfglue = angF_qatan2f;
New: public float quickAtan2Rad(float y, float x) : nonfglue = angF_qatan2f;

cm.statistics.reporter.test

Moved AEV dialog to cm.statistics.reporter to make it available for QA Tools

Moved classes AEVEventReciever,AEVButton and AEVDialog from cm.statistics.reporter.test to cm.statistics.reporter. This also moves the methods public void startAnalyticsEventViewer() and public void stopAnalyticsEventViewer().

cm.std.architectural

library.cm

Removed the classic library. Update references to the facelifted library or remove them.

Removed: public Library stdArchitecturalLibrary() {

cm.std.tools

library.cm

Removed the classic library. Update references to the facelifted library or remove them.

Removed: public Library stdWallLibrary() {

paperLibrary.cm

Removed the classic library. Update references to the facelifted library or remove them.

Removed: public Library stdPaperToolsLibrary() {
Removed: public class ViewportSnapperLimbVisibility extends LibraryLimbVisibility {
Removed: public class XCLipLimbVisibility extends LibraryLimbVisibility {

cm.std.wall

library.cm

Removed the classic library. Update references to the facelifted library or remove them.

Removed: public Library stdWallLibrary() {

class Pillar

Replaced the thickness field with a width and depth component.

Old: public double thickness;
New: public double width;
New: public double depth;
Removed: public const DistanceRange pillarThicknessDomain(1cm, 100m);

class SpecialWallFeatureSearch

Removed: extend public void drawGraphsLocal(Snapper snapper, LayerBuffer lb) {

cm.test.cmunit

testcase.cm

Added caller eval to the testPackage(..) package-argument, which causes the package argument to be set from the call location rather than the function definition location. This means that if the function is called for example from the package "cm.core.dwg.test", it will now run all tests in that same package even if you don't pass any arguments to the function.

Old: public void testPackage(symbol pkg=#:package, bool verbose=false, bool hideErrors=false) {
New: public void testPackage(symbol pkg=#:package : caller eval, bool verbose=false, bool hideErrors=false) {
Removed: public void testPackage(symbol pkg=#:package : caller eval) {

For example, if you previously called the function like so:

testPackage(#:package, verbose=true);

You can now call it like this and it will still behave the same:

testPackage(verbose=true);

cm.test.cmunit.testInstructions

class GetWindowInstruction

Some classes in getWindowInstruction.cm has been moved to their own files: See getParentWindowInstruction.cm and getChildInstruction.cm.

Two deprecated constructors have been removed:

Removed: public constructor(str windowKey, str outputKey, SrcRef src=#:src) {
Removed: public constructor(Class windowClass, str outputKey, SrcRef src=#:src) {

Instead, use one of these constructors:

public constructor(str windowKey, str outputKey, bool ensureFound=true, bool ensureUnique=true, bool ensureValid=true, bool ensureVisible=true, SrcRef src=#:src) {
public constructor(Class windowClass, str outputKey, bool ensureFound=true, bool ensureUnique=true, bool ensureValid=true, bool ensureVisible=true, SrcRef src=#:src) {

class GetChildWindowInstruction

Two deprecated constructors have been removed:

Removed: public constructor(str windowKey, str parentKey, str outputKey, SrcRef src=#:src) {
Removed: public constructor(Class windowClass, str parentKey, str outputKey, SrcRef src=#:src) {

Instead, use one of these constructors:

public constructor(str windowKey, str parentKey|, str outputKey, bool ensureFound=true, bool ensureUnique=true, bool ensureValid=true, bool ensureVisible=true, SrcRef src=#:src) {
public constructor(Class windowClass, str parentKey|, str outputKey, bool ensureFound=true, bool ensureUnique=true, bool ensureValid=true, bool ensureVisible=true, SrcRef src=#:src) {

cm.win

Icons

Changes that have been done:

  1. We now return a DibImage instead of MemoryImage. For more details, check the run-time/behavior changes section.
  2. The argument bool newSvgRenderer now defaults to true for icon loading functions.
Old: public Image icon(str name, symbol key=#default, bool debug=false)
Old: public Image icon(str name, symbol key=#default, bool newSvgRenderer=false, bool debug=false)
New: public Image icon(str name, symbol key=#default, bool newSvgRenderer=true, bool debug=false)

Old: public Image icon(str name, bool enabled, symbol key=#default)
New: public Image icon(str name, bool enabled, symbol key=#default, bool newSvgRenderer=true)

Old: public Image disabledIcon(str name, symbol key=#default)
New: public Image disabledIcon(str name, symbol key=#default, bool newSvgRenderer=true)

Old: public Image dibIcon(str name, symbol key=#default, bool debug=false)
Old: public Image dibIcon(str name, symbol key=#default, bool newSvgRenderer=false, bool debug=false)
New: public Image dibIcon(str name, symbol key=#default, bool newSvgRenderer=true, bool debug=false)

Old: public Image disabledDibIcon(str name, symbol key=#default)
New: public Image disabledDibIcon(str name, symbol key=#default, bool newSvgRenderer=true)

IconFinder

The following fields and methods have been removed:

public str->DibImage dibIconLookup();
final public Image getDib(str name, bool debug=false)
final public Image getDib(str name, bool newSvgRenderer=false, bool debug=false)
final public Image getDisabledDib(str name, bool debug=false)

The following methods have been updated:

Old: final public Image get(str name, bool debug=false)
Old: final public Image get(str name, bool newSvgRenderer=false, bool debug=false)
New: final public Image get(str name, bool newSvgRenderer=true, bool debug=false)

IconDB

The following methods have been updated to remove bool dib=false:

Old: final public Image get(str name, symbol key, bool dib=false, bool debug=false)
Old: final public Image get(str name, symbol key, bool dib=false, bool newSvgRenderer=false, bool debug=false)
New: final public Image get(str name, symbol key, bool newSvgRenderer=true, bool debug=false)

Old: final public Image getDisabled(str name, symbol key, bool dib=false)
New: final public Image getDisabled(str name, symbol key)

SvgImage

Old: public constructor(Url imageFile, sizeI size=(-1, -1), SrcRef src=#:src)
Old: public constructor(Url imageFile, bool newSvgRenderer, sizeI size=(-1, -1)
New: public constructor(Url imageFile, bool newSvgRenderer=true, sizeI size=(-1, -1)

Old: public SvgImage vectorImage(Url url, bool use=false, SrcRef src=#:src)
Old: public SvgImage vectorImage(Url url, bool newSvgRenderer, bool use=false, SrcRef src=#:src)
New: public SvgImage vectorImage(Url url, bool newSvgRenderer=true, bool use=false, SrcRef src=#:src)

Foreign windows

The function showForeignWindow no longer restores the window when setting its visibility. Use restoreForeignWindow if you want the old behavior of restoring the window.

Old: public void showForeignWindow2(mwnd hwnd) = win_showForeignWindow;
New: public void showForeignWindow(mwnd hwnd) = win_showForeignWindow;

Old: public void showForeignWindow(mwnd hwnd) = win_restoreForeignWindow;
New: public void restoreForeignWindow(mwnd hwnd) = win_restoreForeignWindow;

Removed deprecated functionality

The following functions have been removed.

public bool faceliftDeveloper() : deprecated { return staffan or prismDeveloper; }
public bool isAlphaLegacy(char c) { return spellChecker.isAlpha(c); }

Drop down functionality

AppWindow

Old: final public pointI dropDownShow(pointI p, bool autoSize=true)
New: final public pointI dropDownShow(pointI p, bool autoSize=true, bool enforceMinWidth=true)

Window

Old: extend public pointI showDropDownMenu(pointI p, bool pInScreenCoords=false, bool autoSize=true)
New: extend public pointI showDropDownMenu(pointI p, bool pInScreenCoords=false,
                                           bool autoSize=true, bool enforceMinWidth=true)

WebViewConfig

We have added a new argument bool establishSocketConnection=false into the constructor.

Old: public constructor(str key,
		       str url,
		       int port=-1,
		       bool launch=false,
		       str label="CET",
		       bool showPageTitle=false,
		       bool showNavigation=true,
		       bool alwaysOnTop=false,
		       bool hideUrl=false,
		       pointI initPos=(-1, -1),
		       sizeI initSize=(-1, -1),
		       bool resizable=false,
		       sizeI minSize=(-1, -1),
		       sizeI maxSize=(-1, -1),
		       bool devMode=false,
		       webViewCB msgCallback=null) 
New: public constructor(str key,
		       str url,
		       int port=-1,
		       bool launch=false,
		       str label="CET",
		       bool showPageTitle=false,
		       bool showNavigation=true,
		       bool alwaysOnTop=false,
		       bool hideUrl=false,
		       pointI initPos=(-1, -1),
		       sizeI initSize=(-1, -1),
		       bool resizable=false,
		       sizeI minSize=(-1, -1),
		       sizeI maxSize=(-1, -1),
		       bool devMode=false,
		       webViewCB msgCallback=null,
		       bool establishSocketConnection=false)

MessageWindow

The sizeI size argument has been changed to int width.

Old: public constructor(Window parent,
                        str message,
                        Image image,
                        str key=null,
                        sizeI size=(0, 0),
                        int internalMargin=7,
                        Brush brush=ultraLightGrayBrush,
                        FrameStyle frameStyle=lightGrayPenFrame,
                        color textColor=black,
                        color linkColor=primary600,
                        color linkHoverColor=primary600,
                        int textSize=12,
                        str fontFace=null,
                        function(Control button, str key):bool linkCallback=null,
                        SrcRef src=#:src) {
New: public constructor(Window parent,
                        str message,
                        Image image,
                        str key=null,
                        int width=0,
                        int internalMargin=7,
                        Brush brush=ultraLightGrayBrush,
                        FrameStyle frameStyle=lightGrayPenFrame,
                        color textColor=black,
                        color linkColor=primary600,
                        color linkHoverColor=primary600,
                        int textSize=12,
                        str fontFace=null,
                        function(Control button, str key):bool linkCallback=null,
                        SrcRef src=#:src) {

This MessageWindow constructor now takes in argument int width.

Old: public constructor(Window parent,
                        str message,
                        messageType msgType=messageType.undefined,
                        str key=null,
                        int internalMargin=7,
                        function(Control button, str key):bool linkCallback=null,
                        SrcRef src=#:src) {
New: public constructor(Window parent,
                        str message,
                        messageType msgType=messageType.undefined,
                        str key=null,
                        int width=0,
                        int internalMargin=7,
                        function(Control button, str key):bool linkCallback=null,
                        SrcRef src=#:src) {

eventLogG3.cm

statsMainExtensionPackage now accepts argument bool allowGuess to try guessing the main extension package via the provided package.

Old: public symbol statsMainExtensionPackage(symbol pkg)
New: public symbol statsMainExtensionPackage(symbol pkg, bool allowGuess=false)

getMainExtensionPackage now accepts argument bool skipCache to allow bypassing cached results

Old: public symbol getMainExtensionPackage(symbol pkg)
New: public symbol getMainExtensionPackage(symbol pkg, bool skipCache=false)

cm.win.advanced

FilmStripView

The following classes have been removed as film strip view functionality has been removed.

Removed: public class FilmStripView extends ThumbNailView {
Removed: public class PreviewWindow extends SubWindow {

The following fields and methods have been removed in ExploreDialog

Removed: public PreviewWindow previewWin;
Removed: extend public PreviewWindow buildPreviewWin() {
Removed: extend public void refreshPreviewWin() {
Removed: extend public void setPreviewContent(Image image=null, str label=null) {
Removed: extend public void updatePreviewButtons(bool disablePrev, bool disableNext) {
Removed: extend public sizeI previewImageSize() {
Removed: extend public bool filmStripMode() {

custom.accessories.genericElectrical

library.cm

Removed the classic library. Update references to the facelifted library or remove them.

Removed: public Library genericElectricalLibrary() {
Old: public Library faceliftElectricalLibrary() {
New: private Library faceliftElectricalLibrary() {

custom.accessories.lights

library.cm

Removed the classic library. Update references to the facelifted library or remove them.

Removed: public Library classicStdLightsLibrary() {
Old: public Library faceliftStdLightsLibrary() {
New: private Library faceliftStdLightsLibrary() {

custom.accessories.medical

library.cm

Removed the classic library. Update references to the facelifted library or remove them.

Removed: public Library medicalLibrary() {
Old: public Library faceliftMedicalLibrary() {
New: private Library faceliftMedicalLibrary() {

custom.accessories.plants

catalogues/library.cm

Removed the classic library. Update references to the facelifted library or remove them.

Removed: public Library stdPlantsLibrary() {
Old: public Library faceliftPlantsLibrary() {
New: private Library faceliftPlantsLibrary() {

custom.dataCatalog

Component Tab and Component Tab Creator End of Life

The following interfaces will be removed as part of the Component Tab and Component Tab Creator EOL effort:

custom.dataCatalog

// Functions
Removed: public void dcShowToolboxCreatorVendorChangeWarning(DataCatalog catalog)

custom.dataCatalog.builder

// Functions
Removed: public DcToolboxCreatorDialog dcToolboxCreatorDialog()
Removed: public DcToolboxCreatorDialog dcShowToolboxCreatorDialog()
Removed: public DcToolboxCreatorDialog dcShowToolboxCreatorDialog(bool setFocus)
Removed: public void dcCloseToolboxCreatorDialog()

// Classes
Removed: public class DcToolboxCreatorDialog extends DsToolboxCreatorDialog
Removed: public class DcAnimationTreeViewAnimationEnv extends DsDragAnimationEnv
Removed: public class DcDataCatalogLimbEnv extends DsDataCatalogLimbEnv
Removed: public class DcToolboxCreatorToolboxCards extends DsToolboxCreatorToolboxCards

custom.dataCatalog.builder.productCatalog

// Classes
Removed: public class DcProductCatalogToolboxProductReferencesEnv extends DcProductCatalogReferencesValidationEnv {
Removed: public class DcProductCatalogToolboxReferencesEnv extends DcValidationEnv
Removed: public class DcProductCatalogToolboxProductOptionsEnv extends DcValidationEnv

class DcProductLevelEditSubWindow

Removed: extend public void updateActivation()

class DcTableOfContentsSubWindow

// Field
public NoteWindow noteWindow;

// Methods
Removed: extend public void openToolboxCreator()
Removed: extend public void buildNoteWindow()
Removed: extend public void updateNote()

class DcTableOfContentToolboxSubWindow

Removed: public DsButton toolboxCreatorBtn;
Removed: extend public void updateActivation()

class DcTableOfContentsTreeView

Removed: public bool keyCode(Window originator, KeyCode keyCode)
Removed: public bool acceptTVI(TreeViewItem item)

custom.dataCatalog.builder.undo

// Classes:
Removed: public class DcTbOptionsValidationUndoOp extends DcUndoOp
Removed: public class DcProductCatalogAddToolboxesUndoOp extends DcUndoOp
Removed: public class DcProductCatalogRemoveToolboxesUndoOp extends DcProductCatalogAddToolboxesUndoOp

custom.dataCatalog.builder.scheme

// Classes
Removed: public class DcPsLayoutReferencesValidationEnv extends DcValidationEnv

custom.dataCatalog.builder.validation

// Classes
Removed: public class DcCompTabValidationResult extends DcValidationResult

Functions:
Removed: public void dcNavigateToToolboxCreator()
Removed: public void dcNavigateToToolboxCreatorCard(str code)

custom.dataCatalog.builder.geometry

class DcProductListSubWindow

Added 2 new parameters to updateProductDD to flag that first/last product is selected.

Old: extend public void updateProductDD(bool updateCard=true)
New: extend public void updateProductDD(bool updateCard=true, bool selFirstProd=false, bool selLastProd=false)

Added 2 new parameters to updateSelProd to flag that first/last product is selected.

Old: extend public void updateSelProd(int start) 
New: extend public void updateSelProd(int start, bool selFirstProd=false, bool selLastProd=false)

custom.dataCatalog.builder.ui.catalogTreeView

class DcAddProductTreeViewItem

Added 1 new parameter to 'dcBuildAddProductTreeViewItem' to keep track of the additional product's parent product.

Old: public DcAddProductTreeViewItem dcBuildAddProductTreeViewItem(DsiAddProductRefType prod, CatalogTreeViewEnv env)
New: public DcAddProductTreeViewItem dcBuildAddProductTreeViewItem(DsiAddProductRefType prod, CatalogTreeViewEnv env, DsProductType parentProd)

Runtime/Behavior Changes

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

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"];

Miscellaneous

cm.abstract.dataSymbol

DsPData now defaults to using G2 properties

Previously DsPData used G2 properties by default for its base class. This has now been changed so that subclasses of DsPData will also use G2 properties by default as well.

Old:
extend public bool usingG2() {
  if (blockDataProxy == 0 and proxy) return proxy.usingG2(DsPDataProxyEnv(this));
  return (this.class.toS in {"DsPData", "DcPData", "DcPRenderData"} and
	 (!proxy or proxy.class.toS == "DsPDataProxy"));

}
New:
extend public bool usingG2() {
  if (blockDataProxy == 0 and proxy) return proxy.usingG2(DsPDataProxyEnv(this));
  return true;

}

To override the default behavior, override the usingG2 method in your DsPData subclass, or register a DsPDataProxy and override the public bool usingG2(DsPDataProxyEnv env) method to return false.

cm.abstract.ifc

Handling import of IfcMappedRepresentation

Previously IFC import did not account for the mapping target of an IfcMappedRepresentation (only the mapping source's mapping origin) when calculating the IFC snapper's transform which resulted in multiple snappers being placed in the same location on import. Updated this so now the mapping target transform is applied onto the mapping source's mapping origin's transform on import to determine its position relative to the IFC project.

IFC export includes user drawn multifloors

Improved IFC export to include mezzanines, and user placed floors when the "Include architectural objects on exporting" setting is enabled.