Runtime/Behavior Changes

Construction engine entry cache key

The cacheKey() of MhEngineConstructionEntry has been updated to also include the additionalShapeArgs() map. This change was made as entries which were equal in dimensions and classifications but had a different additionalShapeArgs() would end up only using one of the entries to generate snappers as they shared the same cacheKey(). An example of this are levels that are identical except that they reference unit loads with different materials (an the unit load reference were stored in the additionalShapeArgs()).

Additionally, the generated cache key is now converted into a 32-character hash string to reduce the string length for further optimzation of key comparisons.

public class MhEngineConstructionEntry extends MhEngineSnapperEntry {

Old:
    public str cacheKey() {
        if (_cacheKey.any) return _cacheKey;
        return _cacheKey = concat(class.id.hex, classification.id.toS, localBound.toS);
    }


New:
    public str cacheKey() {
        if (_cacheKey.any) return _cacheKey;
        return _cacheKey = initCacheKey();
    }


    extend public str initCacheKey() {
        str res = concat(class.id.hex, classification.id.toS, localBound.toS);

        // Add additional shape args to cache key.
        str argsKey = cacheKeyArg(additionalShapeArgs());
        if (argsKey.any) res = concat(res, ";args=", argsKey);

        // Convert to 32-char hash.
        res = Sha256(res).hex().substring(0, 31);

        return res;
    }
    
    
    extend public str cacheKeyArg(Object obj, int depth=0) {
        if (depth >= 32) {
            if (developMode) ptrace("Too deep cache key"; #obj; #depth);
            return null;
        }
        if (!obj) return null;

        StrBuf buf();
        if (obj as Map) {
            str[] pairs();
            for (key, value in obj) {
                str keyKey = cacheKeyArg(key, depth+1);
                str valueKey = cacheKeyArg(value, depth+1);
                pairs << keyKey # valueKey;
            }
            for (pair in pairs.sorted) buf << pair;
        } else if (obj as Seq) {
            for (value in obj) {
                str valueKey = cacheKeyArg(value, depth+1);
                buf << valueKey;
            }
        } else if (obj as Set) {
            str[] keys();
            for (value in obj) {
                str valueKey = cacheKeyArg(value, depth+1);
                keys << valueKey;
            }
            for (key in keys.sorted) buf << key;
        } else if (obj as Material) {
            buf << cacheKeyArg(obj.pkg, depth+1);
            buf << cacheKeyArg(obj.id, depth+1);
        } else if (obj as UnitLoad) {
            buf << obj.uniqueKey();
        } else {
            buf << obj;
        }
        str text = buf.toS();
        return obj.class.id.hex # ':' # text.count # ':' # text;
    }
}