Overview

A few interfaces in the Material Handling Abstract related to the Animation Tool Spreading functionality has been reworked to be easier to use and customize.

  • MhSpreadPatternBehavior has been extended to allow for easier customization of snapper spreading cache keys. As a result, MhSpreadPatternBehavior and MhSpreadCacheKeyBehavior functionalities has been merged, with MhSpreadCacheKeyBehavior removed.
  • Some of the redundant SpreadPattern classes in the abstract has been removed, e.g. MhUnitLoadLevelSpreadPattern, MhUnitLoadNoSpreadPattern; along with the behaviors that existed solely to enable usage of these customized spread patterns.
  • MhSnapperSpreadVessel, MhSnapperRemoverVessel, MhSnapperSpreadToolAnimation, and MhSnapperInsertToolAnimation refactoring to consolidate spreading-related functionalities and various caching, with the introduction of a MhSpreadToolEnv structure.

Compile Time Changes

Changes to after engine initial export behavior

MhAfterEngineInitialExportBehavior interface changes:

    Old : extend public void afterInitialExport(MhSnapper parent, MhSnapper owner) {}
    New : extend public void afterInitialExport(MhSystemCollection system, MhSnapper parent, MhSnapper owner) {}

MhAfterEngineInitialExportBehavior::void afterInitialExport(MhSystemCollection system) now has a default implementation, moved from MhRowAfterEngineInitialExportBehavior.

    extend public void afterInitialExport(MhSystemCollection system) {
        Snapper[] sortedSnappers = sortedSystemSnappers(system);

        for (MhSnapper snapper in sortedSnappers) {
            if (!system.exportedFromEngine(snapper)) continue;

            ?MhSnapper parent = snapper.parent;
            forAllBehaviors (b in snapper) {
            if (b as MhAfterEngineInitialExportBehavior) {
                b.afterInitialExport(system, parent, snapper);
            }
        }

            snapper.validateBehaviors(sAfterInitialExport);
        }
    }

MhCheckValidFrameChildBehavior now extends from MhAfterEngineInitialExportBehavior.

    Old : public class MhCheckValidFrameChildBehavior extends MhBehavior
    New : public class MhCheckValidFrameChildBehavior extends MhAfterEngineInitialExportBehavior

MhCheckValidFrameChildBehavior interface changes:

    Old : extend public bool isInvalidFrameChild(MhSnapper parent, MhSnapper child) {
    New : extend public bool isInvalidFrameChild(MhSnapper parent, MhSnapper child, MhEngine engine=null) {

    Old : extend public void removeInvalidChild(MhSnapper parent, MhSnapper child) {
    New : extend public void removeInvalidChild(MhSnapper parent, MhSnapper child, MhEngine engine=null) {

MhCheckValidFrameChildBehavior's execution logic has been moved:

    Old : public void validate(Object owner, symbol k, MhSnapperChangedEnv env=null) {
    New : public void afterInitialExport(MhSystemCollection system, MhSnapper parent, MhSnapper owner) {

Changes to engine manager

MhRunEngineEnv interface changes:

    Old :
    public constructor(bool cleanup=true, bool blockImport=false, bool blockExport=false,
                       bool blockFinalize=true, bool showProgressBar=false) {
        set*(this: cleanup, blockImport, blockExport, blockFinalize, showProgressBar);
    }

    New :
    public constructor(bool cleanup=true, bool blockImport=false, bool blockExport=false,
                       bool blockFinalize=true, bool removeFailedEntry=true, bool showProgressBar=false) {
        set*(this: cleanup, blockImport, blockExport, blockFinalize, removeFailedEntry, showProgressBar);
    }

MhEngineRun interface changes:

    Old :
    extend public Snapper{} export(MhEngine engine, MhSystemEngineEnvironment env,
                                   MhEngineEntry[] entries, Space space=null) {

    New :
    extend public Snapper{} export(MhEngine engine, MhSystemEngineEnvironment env,
                                   MhEngineEntry[] entries, Space space=null,
                                   MhRunEngineEnv runEnv=null) {

Swapping class inheritance

The MhRowPopulator and MhStorageRowPopulator classes have swapped positions in the parent-child class hierarchy. For classes that extend from MhRowPopulator, they now need to extend from MhStorageRowPopulator.

    Old : public class MhStorageRowPopulator extends MhPopulator
    New : public class MhRowPopulator extends MhPopulator

    Old : public class MhRowPopulator extends MhStorageRowPopulator
    New : public class MhStorageRowPopulator extends MhRowPopulator    

Interface changes in MhRowLayout:

    Old : public MhRowLayout::MhStorageRowPopulator populator;
    New : public MhRowLayout::MhRowPopulator populator;

    Old : extend public MhRowLayout::MhStorageRowPopulator rowPopulator(MhStorageConfiguration config, rect systemRect,
			                                                            	bool isAisle, bool first, bool lastRow) {
    New : extend public MhRowLayout::MhRowPopulator rowPopulator(MhStorageConfiguration config, rect systemRect,
			                                            			 bool isAisle, bool first, bool lastRow) {

Changes to clearance behaviors

MhClearanceBehavior constructor interface change. Now able to set a different purposeKey for clearance behaviors.

    Old : public constructor() { super("Clearance"); }
    New : public constructor(str key="Clearance") { super(key); }

Changes to animation row export

Introduced a new engine function MhAnimRowConstructionalFunction. In 13.5, MhSystemAnimationExportFunction also handled constructing row entries but in 14.0, we have now moved out that functionality into the new class MhAnimRowConstructionalFunction.

The class MhSystemAnimationLevelPopulateExportFunction has also been replaced by MhAnimRowLevelConstructionalFunction.

    Old : res << MhSystemAnimationLevelPopulateExportFunction("animationExport");
    New : res << MhAnimRowLevelConstructionalFunction("animRowConstruction");
    New : res << MhSystemAnimationExportFunction("animationExport");

    Old : public class MhSystemAnimationLevelPopulateExportFunction extends MhSystemAnimationExportFunction {
    New : public class MhAnimRowLevelConstructionalFunction extends MhAnimRowConstructionalFunction {

Important to note is that "animRowConstruction" is executed before "animationExport".

public class MhRowAnimationEngineBehavior extends MhEngineBehavior {

    public void putEngineRunFunctions(MhSnapper snapper, symbol event="", Object env=null) {
        ...
            if (event == sSnapperInserted) {
                bool addFloorLevel = mhStorageConfiguration(snapper).?addFloorLevel();
                mhPutEngineRunFunction(engine, "animRowConstruction", snapper=snapper,
                                       info=info,
                                       holeZDomain=holeZDomain,
                                       addFloorLevel=addFloorLevel,
                                       additionalPrim=additionalPrim);
		
                mhPutEngineRunFunction(engine, "animationExport", snapper=snapper, space=space,
                                       tryConnectToOtherRow=info.tryConnectToOtherRow,
                                       populateOffset=info.unitLoadPopulateOffset(snapper));
	    }
        ...
    }

Check any classes that extend from MhSystemAnimationExportFunction to see if they should be executed before "animationExport". If this is the case, then they should instead extend from MhAnimRowConstructionalFunction. One example of this is MhAnimCantileverConstructionalFunction which used to extend from MhSystemAnimationExportFunction but now extends from MhAnimRowConstructionalFunction.

With these changes, several fields have also been removed from MhSystemAnimationExportFunction.

The fields below have been moved from MhSystemAnimationExportFunction to MhAnimRowConstructionalFunction.

    public SubSet holeZDomain;
    public Bool addFloorLevel;
    public CollisionPrimitive additionalPrim;
    public MhRowAnimationInfo info;

The fields below have been removed from MhSystemAnimationExportFunction without replacement. MhAnimRowConstructionalFunction now retrieves these values directly from the MhRowAnimationInfo field.

    // removed
    public Point pos;
    public Angle rot;

    // MhRowAnimationInfo
    extend public MhEngineConstructionEntry createRowEntry(MhEngineEntryBlock block, MhEngineConstructionEntry e) {
        ...
        point pos = info.p0;
        orientation rot = info.a;
        ...
    }

Unrolling collision primitives

New functions mhUnrollCollisionPrim() which are used to remove roll angles from collision primitives.

/**
 * Unroll primitive transform.
 * Collision resolver does not support rolled primitive.
 */
public CollisionPrimitive mhUnrollCollisionPrim(CollisionPrimitive prim) {
    CollisionPrimitiveSet set(null, null);
    CollisionPrimitive[] prims();
    mhUnrollCollisionPrim(prim, prims);
    for (p in prims) set.subPrims << p;
    return set;
}


/**
 * Unroll collision primitive.
 * @list : result will be appended to the list.
 */
public void mhUnrollCollisionPrim(CollisionPrimitive prim, CollisionPrimitive[] list) : inline {
    if (!list) init list();
    prim.?explode(list);
    for (p in list) if (p.t.rot.roll != 0deg) p.t -= Transform(orientation(0deg, 0deg, p.t.rot.roll));
}

Deprecated MhEngineSnapperEntry::zeroTransformSnapperEntry() method, calls to this method should be replaced with mhUnrollCollisionPrim().

Removed MhLevelPopulateFunction::orientedCollisionEntry() method, calls to this method should be replaced with mhUnrollCollisionPrim().

Changes to mhCalculatedBayHeight()

Two additional parameters have been added to this function.

  • loadWithinLimit : Set this parameter to true to ensure that the calculated bay height includes all unit loads within the bay bound.
  • filter : You can now pass in a SnapperFilter different from mhBayChildrenFilter.
    Old : public <double, int, double> mhCalculatedBayHeight(MhSystemConfigurationItem this, MhSnapper bay, int stepCount,
                                                   double maxBayHeight=maxDouble, bool xtrace=false) {
    New : public <double, int, double> mhCalculatedBayHeight(MhSystemConfigurationItem this, MhSnapper bay, int stepCount,
                                                   bool loadWithinLimit=false,
                                                   SnapperFilter filter=mhBayChildrenFilter,
                                                   double maxBayHeight=maxDouble, bool xtrace=false) {

This function has been updated to support top levels (top level will be processed last by populator). It also better supports non-selective racks (e.g. levels with a roll angle).

Renaming of row width and length

There has been various changes to rename "row width" to "row cross aisle" and "row length" to "row down aisle".

The following classes have been renamed:

    Old : MhRowWidthInsertAnimation
    New : MhRowCrossAisleInsertAnimation

    Old : MhRowLengthInsertAnimation
    New : MhRowDownAisleInsertAnimation

MhRowAnimationInfo interface changes:

// Fields
    Old :
    /**
     * Length. X-axis.
     */
    public double length = 1m;

    New :
    /**
     * Down aisle dimension. X-axis.
     */
    public double downAisleDist = 1m;


    Old :
    /**
     * Width. Y-axis.
     */
    public double width = 1.5m;

    New :
    /**
     * Cross aisle dimension. Y-axis.
     */
    public double crossAisleDist = 1.5m;


    Old :
    public props : cached=false {
        ...
        "length";
        "width";
        ...
    }

    New :
    public props : cached=false {
        ...
        "downAisleDist";
        "crossAisleDist";
        ...
    }


// Methods
    Old : extend public double minRowLength() {
    New : extend public double minRowDownAisleDist() {

    Old : extend public double minRowWidth() {
    New : extend public double minRowCrossAisleDist() {

MhRowInsertAnimation interface changes:

    Old : extend public MhRowLengthInsertAnimation lengthAnimation() {
    New : extend public MhRowDownAisleInsertAnimation lengthAnimation() {

MhRowDownAisleInsertAnimation interface changes:

    Old : extend public MhRowWidthInsertAnimation widthAnimation() {
    New : extend public MhRowCrossAisleInsertAnimation widthAnimation() {

MhRowLayout interface changes:

    Old : public double aisleW;
    New : public double aisleWidth : stream=null;

cm.abstract.materialHandling

    // public class MhSnapperRemoverVessel
    // Consolidated into MhSpreadToolEnv
    Removed field: public MhSnapperSpreadPattern spreadPattern;
    New: extend public MhSnapperSpreadPattern spreadPattern()
    New: extend public MhSnapperSpreadPattern spreadPattern=(MhSnapperSpreadPattern p)

    Removed field: public SpreadPatternGroup currentSpreadGroup;
    New: extend public SpreadPatternGroup currentSpreadGroup()
    New: extend public SpreadPatternGroup currentSpreadGroup=(SpreadPatternGroup g)


    // public class MhSnapperSpreadVessel
    // Consolidated to MhSpreadToolEnv
    Removed field: public MhSnapperSpreadPattern spreadPattern;
    Removed field: public SpreadPatternGroup currentSpreadGroup;

    Old: extend public CoreObject[] getCandidates()
    New: extend public CoreObject{} getCandidates()


    // cachedSpreadSnappers globals and public functions - moved into MhSnapperSpreadVessel
    Removed function: public void clearCachedSpread()
    Removed function: public (CoreObject{}) cachedSpreadSnappers(str key)
    Removed function: public void putToCachedSpreadSnappers(str key, CoreObject{} ss)

    Removed function: public void updateCachedSpreadAfterExplode(Snapper parent, box[] markedInfoBounds, MhSnapper[] explodedChildren, str cacheKey)
    New: extend public void updateSpreadGroupAfterExplode(Snapper parent, box[] markedInfoBounds, MhSnapper[] explodedChildren, str cacheKey)

    // cached spreadgroups globals and public functions - consolidated into MhSpreadToolEnv
    Removed function: public void clearSpreadGroups()
    Removed function: public SpreadPatternGroup spreadGroup(CoreObject[] snappers)
    Removed function: public SpreadPatternGroup addSpreadGroup(str cacheKey, CoreObject[] snappers)


    // public class SpreadPatternGroup
    Removed: public str cacheKey;
    Old: public constructor(str cacheKey, CoreObject[] snappers)
    New: public constructor(str key, CoreObject{} candidates)

    Old: public CoreObject[] snappers();
    New: public CoreObject{} candidates();

    Old: extend public bool sameGroup(CoreObject[] incomings)
    New: xtend public bool sameGroup(CoreObject{} incomings)


    // public class MhSnapperSpreadPattern
    Old: extend public str cacheKey(MhTrySnapAlternative mainAlt, CoreObject{} candidates)
    New: extend public void appendCacheKey(StrBuf buf, SnapAlternative mainAlt, CoreObject{} candidates)

    Removed: extend public MhSnapperInsertEntry[] getCachedEntries(MhTrySnapAlternative mainAlt, CoreObject{} candidates)
    Removed: extend public void cacheEntries(MhTrySnapAlternative mainAlt, CoreObject{} candidates, MhSnapperInsertEntry[] entries)

cm.abstract.materialHandling.behavior

    // globals
    Removed: public mhLazyGlobal MhBehavior mhStdSpreadCacheKeyBehavior = MhSpreadCacheKeyBehavior();


    // public class MhSpreadPatternBehavior extends MhBehavior
    New: extend public MhSnapperSpreadPattern spreadPattern(MhSnapperSpreadPattern current, MhSnapper candidate)
    New: extend public void appendCacheKey(StrBuf buf, MhSnapper main, SnapAlternative alternative, MhSnapperSpreadPattern pattern)

cm.abstract.materialHandling.storage

    // public class MhStorageSystemSpreadPattern
    Removed field: public str->MhSnapperInsertEntry[] cache(); // Moved to SpreadPatternGroup

    // Various SpreadPattern subclasses that existed solely to customize cacheKey
    Removed: public class MhUnitLoadNoSpreadPattern extends MhNoSpreadPattern
    Removed: public class MhUnitLoadBaySpreadPattern extends MhStorageBaySpreadPattern
    Removed: public class MhUnitLoadLevelSpreadPattern extends MhStorageLevelSpreadPattern
    Removed: public class MhUnitLoadRowLevelSpreadPattern extends MhStorageRowLevelSpreadPattern
    Removed: public class MhUnitLoadRowSpreadPattern extends MhStorageRowSpreadPattern
    Removed: public class MhUnitLoadDoubleDeepColumnLevelSpreadPattern extends MhDoubleDeepColumnLevelSpreadPattern
    Removed: public class MhUnitLoadColumnLevelSpreadPattern extends MhStorageColumnLevelSpreadPattern
    Removed: public class MhUnitLoadColumnSpreadPattern extends MhStorageColumnSpreadPattern
    Removed: public class MhUnitLoadSystemSpreadPattern extends MhStorageSystemSpreadPattern

cm.abstract.materialHandling.storage.racking

    Removed: public class MhNamePlateColumnSpreadPattern

cm.abstract.materialHandling.storage.racking.behavior

    Removed: public class MhFlankProtectorSpreadCacheKeyBehavior

    // globals
    Removed: public mhLazyGlobal MhBehavior mhFlankProtectorSpreadCacheKeyBehavior

Runtime/Behavior Changes

Engine export

There is now a devmode warning during system export that is printed when an entry is deemed to have failed a consistency check.

    /**
     * Check for consistency. Return true if exported is consistent with entry.
     */
    extend public bool exportFailed(MhEngineEntry entry, box exportedBound) {
        box eb = exportedBound;
        return eb.w != entry.w or eb.d != entry.d or eb.h != entry.h;
    }
    extend public void exportEntry(Space space, MhEngineConstructionEntry entry,
				   MhSnapper parent=null, Snapper{} visited=null) {
        ...
        box eb = info ? info.shape.?engineEntryBound() : s.?engineEntryBound();
        if (exportFailed(entry, eb)) {
            if (developMode) pln("Export failed for ".eRed; entry; entry.classification);
            if (removeFailedEntry.?v) return;
        }
        ...
    }

Unstreaming clearance spec

When loading MhLevelShape and unstreaming the saved MhClearanceSpec, we check existing clearance specs in the MhClearanceSpecContainer of a world. Previously this always checked mainWorld's clearance specs, but this has now been changed to check against the shape owner's world. This change addresses checking against incorrect clearance specs when loading a drawing.

    extend public void unstreamStoredSpec(ObjectFormatter formatter) {
        ...
        Old : if (MhClearanceSpecContainer container = clearanceSpecContainer()) {
        New : if (MhClearanceSpecContainer container = clearanceSpecContainer(owner.?space.world)) {

Changes to MhLevelPopulateFunction

Usage of the limitZ field in MhLevelPopulateFunction has been updated. In 13.5 the accepts() method would immediately check against the limitZ value if it exists, ignoring the maxZ() method. In 14.0 the accepts() method will always call maxZ(), and maxZ() now returns limitZ if it exists.

    extend public bool accepts(MhEngineEntry parent, MhEngineEntry newLevel) {
        ...
        double maxZ = maxZ(parent, prim);
        ...
    }


    extend public double maxZ(MhEngineEntry levelParentEntry, CollisionPrimitive prim) {
        if (limitZ) return limitZ.safeDouble();
    }

Updated the behavior of the accepts() method. It now returns highestZ <= maxZ instead of highestZ < maxZ. Additionally the highestZ calculation when loadWithinLimit=true has been updated.

    Old :
        if (loadWithinLimit.?v) {
            double max = 0;

            if (CollisionPrimitive prim = newLevel.collisionPrim(engine.MhEngine)) {
                max = prim.bound.h;
            } else {
                for (c in newLevel.children(engine.MhEngine)) {
                    double h = c.localBound.h;
                    max = max(max, h);
                }
            }
            highestZ += max;
        }


    New :
        if (loadWithinLimit.?v) {
            double max = 0;
            for (c in newLevel.children(engine.MhEngine)) {
                if (!c.isUnitLoad) continue;
                double h = c.localBound.h;
                max = max(max, h);
            }

            highestZ += max;
        }

Mobile behavior keys

MhMobileFloorUpdateBehavior now has the following key "updateMobileFloorAfterInitialExport". MhMobilePlatformUpdateBehavior now has the following key "updateMobilePlatformAfterInitialExport".

Frame accessory classification

The following spawners have had the sFrameAccessory layer added to their classifications.

  • MhCornerProtectorSpawner
  • MhFlankProtectorSpawner
  • MhUprightProtectorSpawner

MhSnapperMultiApplyAnimation changes

applyToCandidateGroup() no longer always selects the first snapper in the given MhSnapperGroup. It now also checks for and returns the first snapper in the MhSnapperGroup that matches the classification of the selection's main snapper.

    Old:
    extend public void applyToCandidateGroup(MhSnapperGroup grp) {
        if (MhSnapper grpMain = grp.?snappers.get) {
    }


    new:
    extend public void applyToCandidateGroup(MhSnapperGroup grp) {
        if (MhSnapper grpMain = getGrpMain(grp)) {
    }


    /**
     * Get SnapperGroup main.
     */
    extend public MhSnapper getGrpMain(MhSnapperGroup grp) {
        if (!grp) return null;
        for (s in grp.snappers)
          if (s.classification.eval(selection.?main.MhSnapper.classification))
            return s;

        return grp.?snappers.get;
    }

bottomStorageLevel changes

The bottomStorageLevel field in MhBayShape is now only used when the bay has a floor level.

public class MhBayShape extends MhBoxSnapperShape {
    /**
     * HoleZDomain
     */
    extend public SubSet holeZDomain() {
        ...
        if (!hasFloorLevel()) res.minV = res.closestSucceeding(bottomStorageLevel).safeDouble;
        return res;
    }


    /**
     * Append Bottom Storage Primitives
     */
    extend public void appendBottomStoragePrimitives(CollisionPrimitive{} prims, Transform t=null) {
        double floorZ = -1mm; // Under the floor to ensure collition with first level
        Bool actualHasFloorLevel = get("configLoadOnFloor").?Bool;
        if (!actualHasFloorLevel) actualHasFloorLevel = hasFloorLevel();
        double bottomZ = (!actualHasFloorLevel.v ? bottomStorageLevel : 0);
        ...
    }
}


public class MhStorageConfiguration extends MhSystemConfiguration {
    /**
     * HoleZDomain
     */
    extend public SubSet holeZDomain() {
        ...
        double bottomZ = !addFloorLevel() ? bottomStorageLevel.safeDouble : 0;
        res.minV = res.closestSucceeding(bottomZ).safeDouble;
        return res;
    }

Additionally, MhBaySpawner now also returns a loadOnFloor value in shapeCreationProps() from the current configuration.

    /**
     * ShapeCreationProps
     */
    public str->Object shapeCreationProps() {
        if (currentConfig) {
            str->Object res = props { ...
                                      loadOnFloor=currentConfig.addFloorLevel()
        ...
    }

Changes to MhCollisionResolver

MhCollisionResolver now always unrolls collision primitives when they are appended to the resolver. This change was made as collision primitives currently do not support resolving collisions with a roll or pitch angle.

    /**
     * Append fixed.
     */
    public void appendFixed(CollisionPrimitive z) {
        CollisionPrimitive[] list();
        mhUnrollCollisionPrim(z, list);
        for (p in list) fixed << p;
    }


    /**
     * Append.
     */
    public void append(CollisionPrimitive z) {
        CollisionPrimitive[] list();
        mhUnrollCollisionPrim(z, list);
        for (p in list) prims << p;
    }

All previous abstract code that initialized CollisionResolver have been replaced with MhCollisionResolver.

public class MhCollisionAlternative extends MhTrySnapAlternative {
    extend public CollisionResolver collisionResolver(Snapper z) {
        if (!_resolver) _resolver = MhCollisionResolver();
        return _resolver;
    }
}


public class MhSnapperSpreadPattern : abstract {
    extend public SnapAlternative[] snapAlternatives(Snapper candidate, MhTrySnapAlternative mainAlt, MhSnapBehavior[] behaviors) {
        ...
        MhCollisionResolver cr();
        ...
    }
}


// cm/abstract/materialHandling/mhResolverFunctions.cm
public Point resolveNextTo(CollisionPrimitive prim, CollisionPrimitive fixedPrim,
                           CollisionPrimitive additionalPrim, CollisionVectorOption option) {
    ...
    MhCollisionResolver resolver();
    resolver.append(option);
    ...
}
public bool primConflicts(CollisionPrimitive prim, CollisionPrimitive fixedPrim) {
    MhCollisionResolver resolver();
    ...
}

Changes to system configuration

The logic in the load() method (to load a configuration from a file) has been modified. It now calls setItemProps() instead of initItems(). With this change, it will no longer set owner of items, create MhConfigurationItemGroup for items, and it will no longer call item.afterInit(). Additionally there is a new call to afterLoad(), which calls afterConfigLoad() in all items.

    /**
     * After load.
     */
    extend public void afterLoad(RobustFormatter formatter) {
        for (item in _items) {
            item.afterConfigLoad(formatter);
        }
    }

If there is some logic for classes extending from MhSystemConfigurationItem that needs to be done after loading a configuration from a file, you may have previously added that code to afterInit(). You should now override the new method afterConfigLoad() instead.

    /**
     * After configuration load.
     */
    extend public void afterConfigLoad(RobustFormatter formatter) { }

Changes to deep storage level engine behavior

MhDeepstorageLevelEngineBehavior now responds different to the sUnitLoadChanged and sChildShapeChanged events. It will now run "unitLoadPopulate" regardless of what spread pattern is used, so it will no longer run "unitLoadEnsureClearance" instead.

Changes to row selection behavior

MhRowSelectionBehavior has been updated to use the MhRowBackToBackFilter for flue gap rows (excluding double-deep flue gaps). With this change, selecting flue gap rows will now also select the directly connected top and bottom rows.

Changes to MhSnapper rotatable method

MhSnapperShape now has a new method rotatable().

    /**
     * Return true if s is a 'rotation snap'.
     */
    extend public bool rotatable(MhSnapper snapper, Connector s) {
        return true;
    }

This is called from MhSnapper.rotatable().

    /**
     * Return true if s is a 'rotation snap'.
     */
    public bool rotatable(Connector s) {
        if (parent) return false;
        return super(..) and shape.?rotatable(this, s);
    }

Spread pattern changes

  • public class SpreadPatternGroup is now unstreamable.
  • MhSnapper.spreadFilter() now returns null if no MhSpreadSnapperFilterBehavior has been defined. Consider defining the behavior if required.
  • MhTrySnapAlternative now sets lastAlternativeSelected before candidateChanged and candidateChosen to ensure the animation acts on the current information.
  • MhUnitLoadSpreadPatternBehavior.spreadPatternDomain() now returns a ClassSubSet domain of MhStorageXXXXXXXSpreadPattern (old: MhUnitLoadXXXXXXXXSpreadPattern).

Miscellaneous

Repository migration

The Material Handling extension (extensions/custom/materialHandling) has been moved to the base repository (base/custom/materialHandling).

The Essential Guarding extension (extensions/custom/guarding) has been moved to the base repoitory (base/custom/guarding).