Compile Time Changes

cm.abstract.materialHandling

MhEngineEntryBase changes

Deprecated the following and replaced with a new method. This is so that an Object arg can be passed into sortFunc.

Deprecated: extend public MhEngineEntry[] sortedChildren(MhEngine engine, function(MhEngineEntry, MhEngineEntry, Object):int sortFunc) : deprecated {
New: extend public MhEngineEntry[] sortedChildren(MhEngine engine, function(MhEngineEntry, MhEngineEntry, Object):int sortFunc, Object arg=null) {

MhEngineConstructionEntry changes

Now MhEngineConstructionEntry has a field assortmentClassification, determining which assortment this entry comes from and belongs to.

Added: public LayerSet assortmentClassification : copy=reference;
Added: extend public LayerSet assortmentClassification() {
Added: extend public LayerSet assortmentClassification=(LayerSet a) {

Thus, exporting entry now at MhRowExportFunction will check for the entry assortmentClassification, to get the correct spawner selector and assortment.

Old: 
    s = entry.spawnSnapper(engine.MhEngine, getSpawnerSelector());
New: 
        LayerSet assortClassi = entry.assortmentClassification;
        if (assortClassi) s = entry.spawnSnapper(engine.MhEngine, assortment.spawnerSelector(assortClassi));
        else s = entry.spawnSnapper(engine.MhEngine, configuration.spawnerSelector);

Add assortmentClassification as an argument on the constructors.

Old: public constructor(box b, Transform t, LayerSet classification=null) : deprecated {
Old: public constructor(box b, LayerSet classification) : deprecated {
New: public constructor(box b, Transform t, LayerSet classification=null, LayerSet assortClassification=null) {
New: public constructor(box b, LayerSet classification, LayerSet assortClassification=null) {

Therefore, all MhEngineConstructionEntry instantiations now pass in the assortment's classification as an argument.

cm.abstract.materialHandling.storage

Deprecate mhCalculatedBayHeight

- Deprecated: public <double, int, double> mhCalculatedBayHeight(MhSystemConfigurationItem this, MhSnapper bay, int stepCount,
                                                   bool loadWithinLimit=false,
                                                   SnapperFilter filter=mhBayChildrenFilter,
                                                   double maxBayHeight=maxDouble, bool xtrace=false) {
                                                   double maxBayHeight=maxDouble, bool xtrace=false) : deprecated {

This function is no longer needed. It has grown so huge, and very patchy. All instances that call this function should be replaced.

MhBaySizeEditorItem change

Old:
            return mhCalculatedBayHeight(bay, stepCount=stepCount,
                                         loadWithinLimit=loadWithinBayLimit(),
                                         maxBayHeight=maxBayHeight, xtrace=xtrace);

new:
            double h = mhCalculatedBayHeight(bay, stepCount, loadWithinLimit=loadWithinBayLimit());
            return <h, stepCount, 0d>;

Instead of using the mhCalculatedBayHeight() to calculate new bay height at the function calculatedBayHeight(), we replace it with a similar method, mhCalculatedBayHeight(MhSystemConfigurationItem this, MhSnapper bay, int stepCount, bool loadWithinLimit=false), which uses bruteforce to calculate bay height.

MhBayConfigurationItem change

Old: max = mhCalculatedBayHeight(getBay(), max, maxBayHeight=maxBayH).v1;
New: max = mhCalculatedMaxLevelCount(bay);

Instead of using mhCalculatedBayHeight() to calculate number of levels domain at the function numLevelsDomain(), we use mhCalculatedMaxLevelCount().

cm.abstract.unitLoad

UnitLoadDialog apply unit load changes

Deprecated the following and replaced with a new method. The deprecated method would loop through and gather all Snappers in mainWorld that use a unit load. This was too broad for the intention of the method and would gather significantly more snappers than required. The new method will gather all Snappers in mainWorld that use one of the unit load objects passed in as an argument.

Deprecated: extend public CoreObject{} getAffectedObjects() : deprecated {
New: extend public CoreObject{} getAffectedObjects(UnitLoad[] unitLoads) {

Similarly, we have deprecated and replaced the following method as well for the same reason.

Deprecated: extend public bool hasLoad(CoreObject obj) : deprecated {
New: extend public bool usesLoad(CoreObject obj, UnitLoad[] unitLoads) {

The method applyAllCallback(UnitLoad[] applyUnitLoads) has been updated to use the new method getAffectedObjects(UnitLoad[] unitLoads) so if you have overridden it, check if you need to update your method call as well.

Runtime/Behavior Changes

cm.abstract.materialHandling

mhEngine changes

Added additional PropDefs to MhEngine during developMode when getting MhEngine using MhEngine mhEngine(Space space, bool createIfNone=true, class MhSystemEngineEnvironment environment=null) for engine visualiser functionality. Additional PropDefs include:

  • spaceKey representing space id string
  • plugIn representing a flag if the engine originates from a regular space or a MhDebugPlugInFunctionSpace plug in function space
/**
 * MH Engine.
 */
public MhEngine mhEngine(Space space, bool createIfNone=true, class MhSystemEngineEnvironment environment=null) {
    ...
        if (developMode) {
            engine."spaceKey" = space.id;
            engine."plugIn" = space in MhDebugPlugInFunctionSpace;
        }
    ...
}

MhEngineFunctionLibrary changes

Modified Object exec(CxEngine engine, str key, str->Object args) to run the engine plug in function replacement process during developMode when the engine visualiser alternate plug in space is enabled and engine is from the plug in function space.

/**
 * Library of functions for an CxEngine.
 */
public class MhEngineFunctionLibrary extends CxFunctionLibrary {

Old:
    /**
     * Run.
     */
    public Object exec(CxEngine engine, str key, str->Object args) {
        ...
            if (developMode) appendToRecentRunned(MhEngineFunctionRun(key, args), fun);
            if (dbg_engineVisualisationEnabled()) engineVisualiserBeforeRun(key, args, fun);

            Object res = fun.exec(args);

            if (dbg_engineVisualisationEnabled()) engineVisualiserAfterRun(key, args, fun);

            return res;
        ...
    }


New:
    /**
     * Run.
     */
    public Object exec(CxEngine engine, str key, str->Object args) {
        ...
            if (developMode and dbg_engineVisualisationEnabled) {
                return dbg_engineVisualisationExecFunction(this, fun, args);
            }

            if (developMode) appendToRecentRunned(MhEngineFunctionRun(key, args), fun);

            return fun.exec(args);
        }
        ...
    }

MhSnapperShape changes

Modified Object setPropertyValue(str key, Object value, CoreProperty property, CoreProperties properties, Object env=null) to pipe changes from the main space to the plug in function space when the engine visualiser alternate plug in space is enabled.

/**
 * Mh snapper shape.
 */
public class MhSnapperShape extends CorePropObj {

    /**
     * Set property value.
     */
    public Object setPropertyValue(str key, Object value, CoreProperty property,
                                   CoreProperties properties, Object env=null) {
        if (dbg_engineVisualisationDebugSpaceEnabled and value and !value.equal(get(key, env))) {
            mhDebugPlugInFunctionSpaceManager.pipeSetPropertyValue(owner, key, value, property, properties, env);
        }
        return super(..);
    }
}

MhCollisionFetchEnv new fetch reason #engine

We now use a #engine symbol as a new fetch reason for collection of collision primitives with MhCollisionFetchEnv. This symbol is used specifically in MhSnapper.setEngineEntryCollisionPrimitive(MhEngineEntry entry) where we collect collision primitives and assign it to an engine entry.

Check for this new fetch reason when appending collision primitives that are meant only for use with engine functions.

/**
 * Abstract industry (generation 2) snapper base class.
 */
public class MhSnapper extends Snapper {

Old:
    /**
     * Update engine entry collision primitive.
     */
    extend public void setEngineEntryCollisionPrimitive(MhEngineEntry entry) {
        if (entry as MhEngineCollisionEntry) {
            MhCollisionFetchEnv env();
            CollisionPrimitive prim = localCollisionPrimitive(env);
            CollisionPrimitiveSet set(null, null);
            set.subPrims <<? prim;
            entry.setPrim(set);
        }
    }


New:
    /**
     * Update engine entry collision primitive.
     */
    extend public void setEngineEntryCollisionPrimitive(MhEngineEntry entry) {
        if (entry as MhEngineCollisionEntry) {
            MhCollisionFetchEnv env(#engine);
            CollisionPrimitive prim = localCollisionPrimitive(env);
            CollisionPrimitiveSet set(null, null);
            set.subPrims <<? prim;
            entry.setPrim(set);
        }
    }

cm.abstract.materialHandling.storage

MhLevelArrangeFunction2 changes

MhLevelArrangeFunction2.arrangeLevelsSteps(MhPopulator populator, MhEngineEntry[] processedEntries) has been updated so that when there is a collision, instead of just moving the next level above, it will now move all subsequent levels above.

/**
 * Level Arrange engine (ensure clearance between levels).
 */
public class MhLevelArrangeFunction2 extends MhSystemEngineFunction {

Old:
    /**
     * Arrange levels by stepping through populator.
     */
    extend public void arrangeLevelsSteps(MhPopulator populator, MhEngineEntry[] processedEntries) {
        ...
                if (!ar and more and MhEngineEntry next = processedEntries[i + 1]) {
                    if (next.pos.z < e.pos.z) next.move(v);
                }
        ...
    }


New:
    /**
     * Arrange levels by stepping through populator.
     */
    extend public void arrangeLevelsSteps(MhPopulator populator, MhEngineEntry[] processedEntries) {
        ...
                if (!ar and more and v.z > 0) {
                    int nextIdx = i;
                    while (processedEntries.count > ++nextIdx and
                           (MhEngineEntry next = processedEntries[nextIdx]) and
                           (MhEngineEntry prev = processedEntries[nextIdx - 1])) {
                        if (next.pos.z < prev.pos.z) {
                            next.move(v);
                        }
                    }
                }
        ...
    }

cm.abstract.unitLoad

UnitLoadDialog Redesign Changes

Important to note: You do not have to make any changes for 15.5 Minor, but they will be required in 16.0 Major. The individual interface changes will also be included in the 16.0 Major migration guide.

See Unit Load Editor Redesign in the New Features page to learn about how to switch to the new design for your extension. This redesign is opt-in and the dialog will still default to the old design.

As a general rule of thumb, you can search for useV2 in order to find the new code and the old code for each of those methods, where the old code will be in the else block. You can also search for FIXME 16.0 remove to identify all methods that are used for the old design and will be removed.

The following fields and methods in UnitLoadDialog are only used for the old design and will be removed in 16.0 Major. Copy them over to your custom class if you wish to maintain the old design in 16.0 Major.

public class UnitLoadDialog extends DialogWindow {
    // Fields.
    public TreeView userRegisteredTree;
    public DropDownTreeView systemRegisteredTree;

    // Methods.
    extend public SubWindow buildAddTemplateWin(Window parent) {
    extend public void buildUserUnitLoadWin(Window sub, Window templateInsert) {
    extend public void buildApplyButtons() {
    extend public pointI paneMargin() {
    extend public void updateUnitLoadSelectorWin() {
    extend public void updatePreviewWin() {
    extend public void updatePropsWin() {
    extend public void updatePanes() {
    extend public void addSelectedTemplateToUserRegistry(bool refresh) {
    extend public str propsLabel(UnitLoad unitLoad) {
    extend public TreeViewItem getSelectedSystemUnitLoad() {
}

The following fields and methods in UnitLoadDialog have been introduced for the new design. Extend these methods in your custom class if you wish to adopt the new design.

public class UnitLoadDialog extends DialogWindow {
    // Fields.
    public Window mainSub;
    public UnitLoadDropDownTreeView registeredUnitLoadTV;
    public Window buttonSub;
    public bool useV2; // Will be removed!

    // Methods.
    public constructor(bool useV2, Window parent=null, pointI pos=(-1, -1)) { // Will be removed!
    extend public void buildMenuBar() {
    extend public void buildMenuBarSubMenus() {
    extend public void buildMenuBarFileMenu() {
    extend public void buildMenuBarEditMenu() {
    extend public void appendMenuBarFileItems() {
    extend public void appendMenuBarEditItems() {
    extend public void toggleMenuBarItems() {
    extend public void buildMainSub() {
    extend public void buildUnitLoadSelectorWin2() { // Will be renamed to buildUnitLoadSelectorWin
    extend public void fillInRegisteredUnitLoadTV() {
    extend public void updateUnitLoadTreeView() {
    extend public void buildPropertiesWin2() { // Will be renamed to buildPropertiesWin
    extend public void buildPreviewWin2() { // Will be renamed to buildPreviewWin
    extend public void buildButtonsSub() {
    extend public sizeI defaultSize() {
    extend public void handleEvent(str event) {
    extend public bool allowModifyUnitLoad() {
    extend public bool allowRemoveUnitLoad() {
    extend public void toggleSaveApplyButtons() {
    extend public bool allowSaveCurrentUnitLoad() {
    extend public bool allowSaveAllUnitLoads() {
    extend public bool allowSelectApplyUnitLoad() {
    extend public void addSelectedTemplateToUserRegistry(UnitLoad ul, UnitLoadGroup grp) {
    extend public void openTemplateSelectorDialog() {
    extend public void openRenamePopup() {
    extend public void renameUnitLoadItem(UnitLoadTreeViewItem2 item, str newName) {
    extend public void duplicateUnitLoad() {
    extend public void removeUnitLoad() {
    extend public int promptUserRemoveUnitLoad() {
    extend public UnitLoadTreeViewItem2 getSelectedUnitLoadTVI() {
}

The following methods in UnitLoadDialog will remain but have been modified to have different behaviors for the old and new design. If you wish to maintain the old design, override these methods and copy over the old code in the else blocks of the if (useV2) check.

public class UnitLoadDialog extends DialogWindow {
    extend public void build() {
    extend public void unitLoadSelectionChanged() {
    public void rebuild() {
    extend public void populatePropsWindow(UnitLoad mtbh) {
    extend public void removeProps() {
    extend public void createPropsUI(UnitLoad mtbh) {
    extend public void animApplyCallback() {
    extend public void applyAllCallback(UnitLoad[] applyUnitLoads) {
    extend public void afterPropertyChanged(CoreProperty property, Object oldValue) {
    extend public void clearModifiedUnitLoads(str->UnitLoad newLoads=null) {
    extend public UnitLoad getSelectedUserUnitLoad(bool actual=false) {
    extend public bool selectUserUnitLoad(UnitLoad mtbh) {
    extend public void validateUserUnitLoadsInUse() {
}

Some other details to be mentioned:

  1. When using the new design, refer to registeredUnitLoadTV instead for the list of unit loads in the drawing.
public class UnitLoadDialog extends DialogWindow {
    Old: public TreeView userRegisteredTree; // To be removed in 16.0 major.
    New: public UnitLoadDropDownTreeView registeredUnitLoadTV
}
  1. The feature of adding new unit loads objects into the drawing has been modified. systemRegisteredTree is not used in the new design and has been replaced with a new dialog UnitLoadTemplateSelectorDialog.
public class UnitLoadDialog extends DialogWindow {
    Old: public DropDownTreeView systemRegisteredTree; // To be removed in 16.0 major.
    New:
    /**
     * Open unit load template selector dialog.
     */
    extend public void openTemplateSelectorDialog() {
        UnitLoadTemplateSelectorDialog(this);
    }


    Old: extend public void addSelectedTemplateToUserRegistry(bool refresh) { // To be removed in 16.0 major.
    New: extend public void addSelectedTemplateToUserRegistry(UnitLoad ul, UnitLoadGroup grp) {
}
  1. We have moved the responsibility of duplicating/renaming/removing unit loads from UnitLoadTreeViewItem into UnitLoadDialog for the new design. If you have extended UnitLoadTreeViewItem and modified these features, considering extending these methods in your UnitLoadDialog class.
public class UnitLoadDialog extends DialogWindow {
    // Methods.
    extend public bool allowModifyUnitLoad() {
    extend public bool allowRemoveUnitLoad() {
    extend public void openRenamePopup() {
    extend public void renameUnitLoadItem(UnitLoadTreeViewItem2 item, str newName) {
    extend public void duplicateUnitLoad() {
    extend public void removeUnitLoad() {
    extend public int promptUserRemoveUnitLoad() {
}

Due to this, in 16.0 Major we will be removing the following fields and methods in UnitLoadTreeViewItem. It will basically be replaced by UnitLoadTreeViewItem2 (which will take its class name).

public class UnitLoadTreeViewItem extends TreeViewItem : inherit constructors {
    // Fields.
    public rectI editButtonRect;
    public rectI cpyButtonRect;
    public rectI rmButtonRect;
    public byte over = 0;

    // Methods.
    extend public void drawColoredRect(PixelDevice c, rectI r, rectI clipRect, treeViewItemState state, treeViewSelectionStyle style) {
    extend public void drawMainArrow(PixelDevice d, rectI r, rectI clipRect) {
    extend public rectI drawBtn(str btnK, Image img, PixelDevice d, rectI r, rectI clipRect) {
    extend public rectI drawBtn(str btnK, Image img, PixelDevice d, rectI r, rectI clipRect, bool disable) {
    extend public pointI getBtnPos(str btnK, Image img, rectI r) {
    extend public Brush backgroundBrush(treeViewItemState state) {
    extend public bool showSelectedAsWhite(treeViewItemState state, treeViewSelectionStyle style) {
    extend public ScrollBar vScrollBar(bool ifVisible=false) {
    extend public void updateApplyButtons(TreeView tv) {
    extend public void openEditNamePopup(TreeView tv) {
    extend public void editName(str newName, TreeView tv) {
    extend public void copyUnitLoadDown(TreeView tv) {
    extend public void removeUnitLoad(TreeView tv) {
    extend public void updateTreeView(TreeView tv) {
    extend public bool isMainSelection() {
    extend public bool isMultiSelect() {

cm.core.visibility.categorize

A new feature has been added that enables users to add extra categories to snappers during insert. In code the feature is called AutoCategorization. In general, the new functionality should work out of the box for all animations that inherit either InsertAnimation or InsertAnimationG2. Other insert animations may need some adjustments to fully support it.

There are two parts to the feature. One is that all snappers inserted when the feature is enabled should append the categories specified by the user. And secondly, while an insert animation is active there is an information toolbar in the active view showing a preview of all categories that the snapper will receive when inserted.

In order to support the adding of extra categories your animation needs to do one of the following:

  1. Insert snappers using Space::undoableInsert(Snapper z, bool putInBsp=true)
  2. Include the code if (isAutoCategorizationEnabled()) applyAutoCategories(snapper); when inserting snappers.

To support the information toolbar showing what categories are about to be added to the snapper your animation is required to:

  1. Override and return true for public bool supportsAutoCategorization(). This will ensure that the toolbar gets displayed during the animation.
  2. Return a valid SnapperSelection containing all snappers about to be inserted. See the function private SnapperSelection getAnimationSelection(Animation a) { in cm/core/visibility/categorize/contextualCategoryInfoToolbar.cm for all the all the ways this can be achieved. The toolbar will still work without returning a valid selection, but a selection is required in order to correctly display what categories will be applied by the snapper and the view mode.

Here is a minimal code example of a custom insert animation that supports auto categorization

/**
 * My custom insert animation.
 */
public class MyCustomInsertAnimation extends Animation {
    /**
     * Supports auto categorization.
     */
    public bool supportsAutoCategorization() {
	return true;
    }


    /**
     * Return all the snappers in this animation.
     */
    public AnimationSelection getSelection() {
	return AnimationSelection(snappersToInsert);
    }


    /**
     * End.
     */
    public PropObj end() {
	super();

	// Insert snappers...

	// Append auto categories to inserted snappers
	if (isAutoCategorizationEnabled()) {
	    applyAutoCategories(insertedSnappers);
	}
    }
}