Compile Time Changes

public class MhSnapperInsertToolVessel extends Vessel {
- Removed:  public Snapper- Old: >str cachedGraphicsKeys() : deprecated;

public class MhSnapperSpawner extends SnapperSpawner {
- Old:     extend public CollisionPrimitive engineConstructionCollision(MhSnapperShape shape) {
New:     extend public CollisionPrimitive engineConstructionCollision(MhSnapperShape shape, MhEngineConstructionEntry entry) {

public class MhFrameGfxBehavior extends MhGenericGfxBehavior {
- Old:     extend public double uprighTh(MhSnapper snapper) {
New:     extend public double uprightTh(MhSnapper snapper) {

- Old:  public class MhBraceInfoDouble extends MhBraceInfo {
New:  public class MhSideElementBracingInfo extends MhBraceInfo {

- Old:  public class MhFrameEditBracingInsertAnimation extends MhSnapperInsertToolAnimation {
New:  public class MhFrameEditElementsInsertAnimation extends MhSnapperInsertToolAnimation {
New:  public class MhFrameEditBracingInsertAnimation extends MhFrameEditElementsInsertAnimation {

public class MhFrameEditorShape extends MhSnapperShape {
- Old:     extend public Snapper{} children(line[] lines) {
New:     extend public Snapper{} children(MhBracingBehavior behavior, MhBraceInfo[] infos, line[] lines) {

cm/abstract/materialHandling/mhSnapperInsertToolVessel.cm
- Removed: [Global Variable] public const double arrowMargin = 5cm;
New: [MhArrowBehavior method] extend public double arrowMargin(MhSnapper snapper, Animation a)
Note: Replace all `arrowMargin` references to use the new margin values from an `MhArrowBehavior`.

public class MhConfigManager : unstreamable {
- Old:    extend public void setNewName(MhConfigRef config) {
- New:    extend public bool setNewName(MhConfigRef config) {

CollisionPrimitive interface cleanups

Some of the key methods relating to collection of CollisionPrimitives in the abstract has been reworked, utilizing a MhCollisionFetchEnv to pass arguments to simplify the methods, and allow for better control of CollisionPrimitive generation through a new reason argument that accepts a symbol.

For clearance related collisions, MhClearanceCollisionPrimEnv has also been reworked and merged into MhCollisionFetchEnv, MhClearanceCollisionFetchEnv and MhStorageClearanceCollisionFetchEnv for similar reasons.

Most of the compile errors could be resolved by using the new methods, wrapping arguments like Transform t into a MhCollisionFetchEnv, or using a null value.

New: public class MhCollisionFetchEnv

- Old: public class MhClearanceCollisionPrimEnv
New: public class MhClearanceCollisionFetchEnv extends MhCollisionFetchEnv

- Old: public class MhStorageClearanceCollisionPrimEnv extends MhClearanceCollisionPrimEnv
New: public class MhStorageClearanceCollisionFetchEnv extends MhClearanceCollisionFetchEnv


cm/abstract/materialHandling/functions.cm
- Deprecated: public CollisionPrimitive localCollisionPrimitive(MhSnapper this)
New: public CollisionPrimitive localCollisionPrimitive(MhSnapper this, MhCollisionFetchEnv env)

MhSnapper
- Deprecated: extend public CollisionPrimitive collisionPrimitive(Transform t, bool includeChildren, Object env=null)
New: extend public CollisionPrimitive collisionPrimitive(MhCollisionFetchEnv env)

MhSnapperGeometry
- Old: extend public CollisionPrimitive collisionPrimitive(Transform t)
- New: extend public CollisionPrimitive collisionPrimitive(MhCollisionFetchEnv env)

MhSnapperShape
- Old: extend public CollisionPrimitive collisionPrimitive(Transform t=null)
New: extend public CollisionPrimitive collisionPrimitive(MhCollisionFetchEnv env)

- Old: extend public void appendBehaviorCollisionPrimitives(CollisionPrimitiveSet res, Transform t=null, MhSnapper parent=null)
New: extend public void appendBehaviorCollisionPrimitives(MhCollisionFetchEnv env, CollisionPrimitiveSet res, MhSnapper parent=null)

MhSnapperSpawner
- Old: extend public CollisionPrimitive engineConstructionCollision(MhSnapperShape shape)
New: extend public CollisionPrimitive engineConstructionCollision(MhSnapperShape shape, MhEngineConstructionEntry entry)

MhSnapperInfo
- Deprecated: extend public CollisionPrimitive collisionPrimitive(Transform t, MhSnapper p)
New: extend public CollisionPrimitive collisionPrimitive(MhCollisionFetchEnv env, MhSnapper p)

MhClearanceSpec
- Removed: extend public CollisionPrimitive[] collisionPrimitives(MhSnapperShape shape, Transform t, double elevation)
- Removed: extend public CollisionPrimitive[] collisionPrimitives(MhSnapperShape shape, Transform t, double elevation, LayerSet classification)
New: extend public CollisionPrimitive[] collisionPrimitives(MhSnapperShape shape, MhCollisionFetchEnv env)

- Old: extend public CollisionPrimitive[] levelClearanceCollisionPrimitives(MhSnapperShape shape, Transform t, double elevation, LayerSet classification)
New: extend public CollisionPrimitive[] levelClearanceCollision(MhSnapperShape shape, MhCollisionFetchEnv env)

MhClearanceBehavior
- Old: extend public void appendCollisionPrimitives(MhSnapperShape shape, Transform t, CollisionPrimitive{} geos, MhClearanceCollisionPrimEnv clearanceEnv, Object env=null)
New: extend public void appendCollisionPrimitives(MhSnapperShape shape, MhCollisionFetchEnv env, CollisionPrimitive{} res)

MhClearanceSpecBehavior
- Old: extend public void appendClearanceSpecCollisionPrimitives(MhSnapperShape shape, Transform t, MhClearanceCollisionPrimEnv clearanceEnv, MhClearanceSpec clearanceSpec,Object env=null) {
New: extend public void appendSpecCollision(MhSnapperShape shape, CollisionPrimitive{} res, MhClearanceSpec spec, MhCollisionFetchEnv env)

- Old: extend public MhClearanceSpec clearanceSpec(MhClearanceCollisionPrimEnv clEnv, MhSnapperShape shape, Object env=null) {
New: extend public MhClearanceSpec clearanceSpec(MhCollisionFetchEnv env, MhSnapperShape shape) {

MhLevelInsertHeightBehavior interface changes

Removed: extend public void updateDomains(Snapper owner, Snapper toSnapper, Object env=null) : deprecated {

Old: extend public bool updateHeightDomains(Snapper owner, Snapper toSnapper, Object env=null) {
New: extend public bool updateHeightDomains(MhSnapper owner, MhSnapper toSnapper, Object env=null) {

Old: extend public SubSet restrictedHeightDomain(Snapper owner, Snapper toSnapper, Object env) {
New: extend public SubSet restrictedHeightDomain(MhSnapper firstAbove, MhSnapper firstBelow, MhSnapper owner, MhSnapper toSnapper, Object env) {

Old: extend public bool isConflicting(Snapper owner, double z, MhCollisionAlternative alt, CollisionPrimitiveSet ownerPrims) {
New: extend public bool isConflicting(MhSnapper owner, double z, MhCollisionAlternative alt, CollisionPrimitiveSet ownerPrims) {

Old: extend public void updateHeightProperties(Snapper owner, Snapper toSnapper, Object env=null) {
New: extend public void updateHeightProperties(MhSnapper owner, MhSnapper toSnapper, Object env=null) {

Old: extend public <Double, Double> calculateFreeSpaceAboveAndBelow(Snapper owner, Snapper toSnapper, double insertH, Snapper specificAboveSnapper=null, Snapper specificBelowSnapper=null) {
New: extend public <Double, Double> calculateFreeSpaceAboveAndBelow(MhSnapper owner, MhSnapper toSnapper, double insertH, Snapper specificAboveSnapper=null, Snapper specificBelowSnapper=null) {

Old: extend public <Snapper, Snapper> getFirstAboveAndBelowSnappers(Snapper owner, Snapper toSnapper, double insertH) {
New: extend public <MhSnapper, MhSnapper> getFirstAboveAndBelowSnappers(MhSnapper owner, MhSnapper toSnapper, double insertH) {

Old: extend public void showHeightProperties() {
New: extend public void showHeightProperties(MhSnapper owner, MhSnapper toSnapper) {
Removed: public bool isShelfBackPanel(Object o) { return evalClassification(o, sBackPanel); }

MhSpreadPatternBehavior interface changes

Removed: extend public SubSet spreadPatternDomain(MhSnapper snapper, MhSnapper parent, str event) : deprecated {

MhDeepstorageUnitLoadPopulateFunction interface changes

Old: extend public point nextPopulatorPos(MhEngineEntry edit, point startPos, double bedWidth) {
New: extend public point nextPopulatorPos(MhEngineEntry edit, point startPos, double bedWidth, MhSystemEngineEnvironment env) {

MhBackFenceShape interface changes

Removed: public double bracketD;

MhBackstopShape interface changes

Removed: public double beamOffset;
Removed: public double bracketD;

MhRowDownAisleInsertAnimation interface changes

Removed: public Graph->MhPolylineColor[] map;

MhSnapperApplyAnimation interface changes

Removed: extend public void relinkNeighbors(MhSnapper oldSnapper, MhSnapper newSnapper) : deprecated {

MhSnapperMultiApplyAnimation interface changes

Renamed field selection to pickedUpSelection to avoid confusion with MhSnapperSpreadToolAnimation.selection().

Old: public SnapperSelection selection;
New: public SnapperSelection pickedUpSelection;

Old: public constructor(MhSnapper snapper, SnapperSelection selection, SnapperFilter f, bool includeChildren, bool showPickup=true) {
New: public constructor(MhSnapper snapper, SnapperSelection pickedUpSelection, SnapperFilter f, bool includeChildren, bool showPickup=true) {

MhRowEndSnapperFilter interface changes

Removed: public Snapper leftSnapper;
Removed: public Snapper rightSnapper;
Removed: extend public void setLeftAndRightSnapper(MhSnapper row, Snapper{} ss) : deprecated {

MhBraceInfo changes

Undeprecated a method in MhBraceInfo.

Old: extend public line2D braceLine(double frame_depth, double brace_offset) : deprecated {
New: extend public line2D braceLine(double frame_depth, double brace_offset) {

Frame editor snapper changes

Removed deprecated fields in MhFrameEditContentSnapper and MhFrameEditorShape.

MhFrameEditContentSnapper
Removed: public MhSnapper baySnapper : copy=null,  deprecated;

MhFrameEditorShape
Removed: public MhSnapper frameSnapper : copy=null, deprecated;

Removed backstop constants

Removed some constants in cm/abstract/materialHandling/storage/behavior/mhBackstopSnappingBehavior.cm .

Removed: public const double cBackstopDepthMargin = 10cm;
Removed: public const double cBackstopHeightMargin = 20cm;

Removed deprecated frame editor function

Replace any calls to mhDrawingFrameEditConfigurations(Space space) with mhDrawingFrameEditConfigurations(Space space, symbol configPkg).

Removed: public MhFrameEditConfiguration[] mhDrawingFrameEditConfigurations(Space space) : deprecated {

Remove deprecated multi-span frames function

Removed a deprecated function in cm/abstract/materialHandling/storage/racking/mhFlankProtectorFixedPointCollisionAlternative.cm.

Removed: public MhSnapper[] getMultiSpanFrames(MhSnapper frame) : deprecated {

Runtime/Behavior Changes

public class MhStorageSpawnerSelector extends MhSystemSpawnerSelector
    public SnapperSpawner spawner(LayerSet classification, Object env=null) {
        Bugfix: Fixed spawner selection to prioritize exact match, over an equally-attractive-but-partial match.
        There were cases where an exact-match come later due to us iterating in alphabetical order (through allSorted)
        and we would return only a partial match.


MhStorageEditorDialog
- Issues with having leftover `Windows` leaking UI controls in `MhStorageEditorDialog` and `MhStorageConfigurator` has been fixed. You may need to double check your UI behaviors if you do any update or removal of UI controls dynamically.
- `buildPropertiesSubWindow()` for building `MhStorageEditorItem` now use `item.class.toS` instead of `item.label` as key for the `Card`.


MhSnapperShape
- Note: There are two versions of the localBound method which can be easily confused: localBound() and localBound(symbol[])
- localBound() now checks for owner before executing the localBoundWithChildren() case, and will return localBound(null) which returns the geometry's bound in other cases. This fixes the inconsistent bound returned during contexts in which the shape have not obtained an owner yet.


MhLevelInsertHeightBehavior changes

The freeSpaceAbove property is no longer visible when there is no level above the currently selected snapper.

Old:
    /**
     * Show height prop.
     */
    extend public void showHeightProperties() {
        for (k, _ in getPropsWithAttribute(#height))
          if (corePropertyHidden(k)) showCoreProperty(k);
    }


New:
    /**
     * Show height prop.
     */
    extend public void showHeightProperties(MhSnapper owner, MhSnapper toSnapper) {
        for (k, _ in getPropsWithAttribute(#height))
          if (corePropertyHidden(k) and propIsVisible(k, owner, toSnapper)) showCoreProperty(k);
    }


    /**
     * Property is visible?
     */
    extend public bool propIsVisible(str key, MhSnapper owner, MhSnapper toSnapper) {
        if (key == "freeSpaceAbove") {
            <MhSnapper firstAbove, MhSnapper firstBelow> = getFirstAboveAndBelowSnappers(owner, toSnapper, owner.pos.z);
            if (!firstAbove) return false;
        }
        return true;
    }

These methods have been introduced to allow more control over the bounds used to calculate freeSpaceAbove and freeSpaceBelow. Override geoSymbolsForCalculation() to control what geometries should be included in the bound.

    /**
     * Free spaces domains.
     * v0 = freeSpaceAbove domain.
     * v1 = freeSpaceBelow domain.
     */
    extend public <SubSet, SubSet> freeSpacesDomains(MhSnapper firstAbove, MhSnapper firstBelow, MhSnapper owner, MhSnapper toSnapper, Object env=null) {
        return <coreProperty("freeSpaceAbove").?domain, coreProperty("freeSpaceBelow").?domain>;
    }


    /**
     * Get snapper bound used for calculation.
     */
    extend public box boundForCalculation(MhSnapper snapper, bool toSpace=false) {
        Box res = cachedBounds.get(snapper);
        if (!res) {
            res = shapeBoundWithChildren(snapper, geoSymbolsForCalculation());
            cachedBounds.put(snapper, res);
        }
        if (toSpace) return res.v.transformed(snapper.toSpaceTransform);
        return res.v;
    }


    /**
     * Symbols used to retrieve geometries for snapper bound used for calculation.
     */
    extend public symbol[] geoSymbolsForCalculation() {
        return [sLevel, sUnitLoad, sTunnel];
    }

calculateFreeSpaceAboveAndBelow() has been updated to use boundForCalculation().

Old:
    /**
     * Calculate free space above and below.
     */
    extend public <Double, Double> calculateFreeSpaceAboveAndBelow(Snapper owner, Snapper toSnapper,
                                                                   double insertH,
                                                                   Snapper specificAboveSnapper=null,
                                                                   Snapper specificBelowSnapper=null) {
        box b = owner.localBound();
        box b0 = toSnapper.localBound;
        Double above = b0.h - insertH;
        Double below = insertH b.p0.z;

        <Snapper firstAbove, Snapper firstBelow> = getFirstAboveAndBelowSnappers(owner, toSnapper, insertH);

        if (firstAbove) {
            above = firstAbove.pos.z - insertH - b.h;
        }

        if (firstBelow) {
            below = insertH - firstBelow.pos.z - firstBelow.localBound.h;
        }

        if (specificAboveSnapper and specificAboveSnapper != firstAbove) above = null;
        if (specificBelowSnapper and specificBelowSnapper != firstBelow) below = null;

        return <above, below>;
    }


New:
    /**
     * Calculate free space above and below.
     */
    extend public <Double, Double> calculateFreeSpaceAboveAndBelow(MhSnapper owner, MhSnapper toSnapper,
                                                                   double insertH,
                                                                   Snapper specificAboveSnapper=null,
                                                                   Snapper specificBelowSnapper=null) {
        box ownerB = boundForCalculation(owner);
        box b0 = toSnapper.localBound;
        Double above = b0.h - insertH;
        Double below = insertH ownerB.p0.z;

        <MhSnapper firstAbove, MhSnapper firstBelow> = getFirstAboveAndBelowSnappers(owner, toSnapper, insertH);

        if (firstAbove) {
            above = boundForCalculation(firstAbove, toSpace=true).p0.z - insertH - ownerB.p1.z;
        }

        if (firstBelow) {
            below = insertH ownerB.p0.z - boundForCalculation(firstBelow, toSpace=true).p1.z;
        }

        if (specificAboveSnapper and specificAboveSnapper != firstAbove) above = null;
        if (specificBelowSnapper and specificBelowSnapper != firstBelow) below = null;

        return <above, below>;
    }

The updateHeightProperties() method has been broken up into multiple methods to make it easier to override. These methods have also had behavioral changes.

  1. The insertHeight property is unlocked if the locked height is no longer valid (e.g. switching candidates to a different bay with a different height domain).
Old:
    if (heightProp.?locked and heightProp == currentProp) {
        p.z = heightProp.value.safeDouble;
        aboveProp.?unlock();
        belowProp.?unlock();
    }


New:
    /**
     * Check insert height locked.
     */
    extend public void checkInsertHeightLocked(point& p, SubSet heightDomain) {
        CoreProperty heightProp = coreProperty("insertHeight");
        if (heightProp.?locked) { // Height is locked, maintain current height.
            if (heightProp.value !in heightDomain) {
                heightProp.unlock();
            } else {
                p.z = heightProp.value.safeDouble;
                coreProperty("freeSpaceAbove").?unlock();
                coreProperty("freeSpaceBelow").?unlock();
            }
        }
    }
  1. The freeSpaceAbove property is unlocked if the locked value is no longer valid or if there is no longer a snapper above the selected snapper. The z-position calculation has also been modified to utilize the boundForCalculation() method.
Old:
    if (aboveProp.?locked and aboveProp == currentProp) {
        p.z = (firstAbove ? firstAbove.pos.z - b.h : maxH) - aboveProp.value.safeDouble;
        heightProp.?unlock();
        belowProp.?unlock();
    }


New:
    /**
     * Check free space above locked.
     */
    extend public void checkFreeSpaceAboveLocked(point& p, SubSet aboveDomain, MhSnapper firstAbove, MhSnapper owner, MhSnapper toSnapper) {
        CoreProperty aboveProp = coreProperty("freeSpaceAbove");
        if (aboveProp.?locked) {
            if (aboveProp.value !in aboveDomain or !firstAbove) {
                aboveProp.unlock();
            } else {
                p.z = (firstAbove ? firstAbove.pos.z boundForCalculation(firstAbove).p0.z - boundForCalculation(owner).p1.z : toSnapper.localBound.h) - aboveProp.value.safeDouble;
                coreProperty("insertHeight").?unlock();
                coreProperty("freeSpaceBelow").?unlock();
            }
        }
    }
  1. The freeSpaceBelow property is unlocked if the locked value is no longer valid. The z-position calculation has also been modified to utilize the boundForCalculation() method.
Old:
    if (belowProp.?locked and belowProp == currentProp) {
        p.z = firstBelow.MhSnapper.?localBound().p1.z
          (firstBelow? firstBelow.pos.z : 0)
          belowProp.value.safeDouble
          - b.p0.z;

        heightProp.?unlock();
        aboveProp.?unlock();
    }


New:
    /**
     * Check free space below locked.
     */
    extend public void checkFreeSpaceBelowLocked(point& p, SubSet belowDomain, MhSnapper firstBelow, MhSnapper owner, MhSnapper toSnapper) {
        CoreProperty belowProp = coreProperty("freeSpaceBelow");
        if (belowProp.?locked) {
            if (belowProp.value !in belowDomain) {
                belowProp.unlock();
            } else {
                p.z = (firstBelow ? boundForCalculation(firstBelow, toSpace=true).p1.z : 0) belowProp.value.safeDouble - boundForCalculation(owner).p0.z;
                coreProperty("insertHeight").?unlock();
                coreProperty("freeSpaceAbove").?unlock();
            }
        }
    }
  1. The freeSpaceAbove and freeSpaceBelow properties will now be refreshed if their values change but insertHeight did not.
Old:
    double oldInsertHeight = insertHeight;
    if (owner.pos != p) owner.setPos(p);
    insertHeight = p.z;

    <Double above, Double below> = calculateFreeSpaceAboveAndBelow(owner, toSnapper, insertHeight);

    freeSpaceAbove = above.?v;
    freeSpaceBelow = below.?v;

    if (oldInsertHeight != insertHeight) {
        heightProp.?refreshProperty();
        aboveProp.?refreshProperty();
        belowProp.?refreshProperty();

        // Calling diff rebuild instead causes the prop UI to constantly flicker.
        if (domainsUpdated) {
            for (prop in [heightProp, aboveProp, belowProp]) {
                for (CoreDistanceField ctrl in prop.controls) {
                    ctrl.validSubSet = prop.domain;
                    break;
                }
            }
        }
    }


New:
    /**
     * Update height and space values.
     */
    extend public void updateHeightAndSpaceValues(point p, MhSnapper owner, MhSnapper toSnapper, bool domainsUpdated) {
        double oldInsertHeight = insertHeight;
        if (owner.pos != p) owner.setPos(p);
        insertHeight = p.z;

        <Double above, Double below> = calculateFreeSpaceAboveAndBelow(owner, toSnapper, insertHeight);

        double oldFreeSpaceAbove = freeSpaceAbove;
        double oldFreeSpaceBelow = freeSpaceBelow;
        freeSpaceAbove = above.?v;
        freeSpaceBelow = below.?v;

        CoreProperty heightProp = coreProperty("insertHeight");
        CoreProperty aboveProp = coreProperty("freeSpaceAbove");
        CoreProperty belowProp = coreProperty("freeSpaceBelow");
        if (oldFreeSpaceAbove != freeSpaceAbove) aboveProp.?refreshProperty();
        if (oldFreeSpaceBelow != freeSpaceBelow) belowProp.?refreshProperty();
        if (oldInsertHeight != insertHeight) heightProp.?refreshProperty();
        // Calling diff rebuild instead causes the prop UI to constantly flicker.
        if (domainsUpdated) {
            for (prop in [heightProp, aboveProp, belowProp]) {
                for (CoreDistanceField ctrl in prop.controls) {
                    ctrl.validSubSet = prop.domain;
                    break;
                }
            }
        }
    }

MhBracingPattern changes

MhBracingPattern now inherits from MhStorageSingleton and should be treated as singletons.

The following functions have been added to retrieve the generic bracing patterns available in the abstract.

/**
 * Bracing pattern singletons.
 */
public MhDBracePattern mhDBracePattern() {
    static MhDBracePattern pattern;
    if (!pattern) ?pattern = mhStorageSingleton(MhDBracePattern());
    return pattern;
}
public MhKBracePattern mhKBracePattern() {
    static MhKBracePattern pattern;
    if (!pattern) ?pattern = mhStorageSingleton(MhKBracePattern());
    return pattern;
}
public MhXBracePattern mhXBracePattern() {
    static MhXBracePattern pattern;
    if (!pattern) ?pattern = mhStorageSingleton(MhXBracePattern());
    return pattern;
}
public MhZBracePattern mhZBracePattern() {
    static MhZBracePattern pattern;
    if (!pattern) ?pattern = mhStorageSingleton(MhZBracePattern());
    return pattern;
}

MhBayLevelEditorItem changes

applySnappers() previously only returned the main snapper of the editor space selection. It has been changed to now return all level snappers and unit load snappers in the selection.

Old:
    /**
     * Apply snappers.
     */
    public MhSnapper{} applySnappers(Space space) {
        if (SpaceSelection selection = space.selection) {
            ?MhSnapper main = selection.main;
            if (main.isLevel) return {MhSnapper: main};
        }

        return super(..);
    }


New:
    /**
     * Apply snappers.
     */
    public MhSnapper{} applySnappers(Space space) {
        if (SpaceSelection selection = space.selection) {
            MhSnapper{} res();
            for (MhSnapper snapper in selection.snappers) {
                if (snapper.isLevel or snapper.isUnitLoad) {
                    res << snapper;
                }
            }
            return res;
        }

        return super(..);
    }

Pre configurator preview number of aisles

Previously only 1 aisle would be generated for the pre configurator preview snappers. It is now possible to override this to generate different number of aisles based on the selected row layout. We now also generate 2 aisles for when the selected row layout is of class MhRowSingleEndLayout.

Old:
    /**
     * Setup row animation info for preview population.
     */
    extend public MhRowAnimationInfo rowAnimationInfo(MhStorageConfiguration config, MhSnapper row) {
        ...
        info.crossAisleDist = config.calcWidthFromAisles(info, 1);
        return info;
    }


New:
    /**
     * Setup row animation info for preview population.
     */
    extend public MhRowAnimationInfo rowAnimationInfo(MhStorageConfiguration config, MhSnapper row) {
        ...
        info.crossAisleDist = config.calcWidthFromAisles(info, rowNumAisles(info.rowLayout));
        return info;
    }


    /**
     * Row number of aisles for preview population.
     */
    extend public int rowNumAisles(MhRowLayout layout) {
        if (layout in MhRowSingleEndLayout) return 2;
        return 1;
    }

There are now new MhStorageConfigurationPreview classes to handle number of aisles for the MhRowSingleEndDoubleDeepLayout or MhCantileverSingleEndRowLayout layouts.

New:
/**
 * Racking configuration preview.
 */
public class MhRackingConfigurationPreview extends MhStorageConfigurationPreview {

    /**
     * Row number of aisles for preview population.
     */
    public int rowNumAisles(MhRowLayout layout) {
        if (layout in MhRowSingleEndDoubleDeepLayout) return 2;
        return super(..);
    }
}


/**
 * Cantilever configuration preview.
 */
public class MhCantileverConfigurationPreview extends MhRackingConfigurationPreview {

    /**
     * Row number of aisles for preview population.
     */
    public int rowNumAisles(MhRowLayout layout) {
        if (layout in MhCantileverSingleEndRowLayout) return 2;
        return super(..);
    }
}


Use:
public class MhRackingConfiguration extends MhStorageRowConfiguration {
    /**
     * Preview behavior.
     */
    public MhStorageConfigurationPreview configurationPreview() {
        if (!preview) preview = MhRackingConfigurationPreview();
        return preview;
    }
}

Miscellaneous

Some common abstract material handling debug tools have been moved and consolidated into cm.abstract.materialHandling.debug package, and some automated test methods have been renamed for clarity:

cm/abstract/materialHandling/debug/library.cm
public void appendMhDebugContainerLimbs(LibraryLimb root, symbol pkg=#:package : caller eval)


cm/abstract/materialHandling/storage/racking/test/initTest.cm
- Old: public void initTestSuite() {
New: public void mhInitTestSuite() {

- Old: public void runnerTestSuite() {
New: public void mhRunnerTestSuite() {

- Old: public void compileAll() {
New: public void mhCompileAll() {