Abstract Elevations

Elevation Space

Elevation Arrows now works with Favorites, Copy/Paste and Collaboration. Prior to the update, the Space was reset and any added Snapper(s) to the ElevSpace was not coming with the Favorite/Paste/Collaboration load.

To ensure proper behavior any Customization code that uses Elevation Arrow and ElevSpace should consider removing special code that circumvents the previous limitations and that may make the update not work as intended.

Abstract Industry

Animation changes

  • Added animation properties 'Number of racks' and 'Number of rows'.
    • PHBodyInsertAnimationG2
    • PHBodyWidthAdjustingAnimationG2
    • PlacingMultiplePHBodyAnimationG2
  • Added animation properties 'Total length' and 'Total rack count'.
    • ShelvingPropertyStretchAnimation (superStretch)

Shelving insert changes

  • ShelvingSystemInfo.positionShelving() wrongfully offset the first upright by its own width.
  • PHBody.afterShelvingInsert() is called for every PHBody after the shelving has been inserted. This allows for any adjustments needed to be done after the insert. The calls to PHBody.afterShelvingInsert() is done in the same Undo step as the insertion.

Automatic soul key changes

  • Fields marked ignore modify notice and/or stream=null are automatically discarded to prevent them from being part of the soul key.
  • Props with the attribute #stream_null are automatically discarded to prevent them from being part of the soul key.

Compare functions for LOPosition and Layouts

  • Improved the compare functions to take the LOPosition flags front, inner and back into account.
  • Replaced all str->tuple maps with sorted maps that sort on LOPosition according to compareLOPosition() function. The str->tuple maps only sorted on point.toS.

Abstract Labs

Categories

Added symbol and text categories for all labs categories.

Warp space domain registration

Added warp space domain registration for LabsElevationSpace on abstract.labs init to give new viewports visibility to those views.

acceptScheme() overridden

acceptScheme() has been overridden on the following classes:

  • LabsWorktop
  • LabsRackContent
  • LabsRackFront
  • LabsSideSplash

Other classes that were previously overridden:

  • LabsSink
  • LabsTap
  • All Rack types

LabsElevationAutoInsertEnv

LabsElevationAutoInsertEnv needs class types passed in as argument rather than an instantiated LabsElevationArrow object.

Previous constructor cm.abstract.labs.LabsElevationAutoInsertEnv :

    /**
     * Creation.
     */
    public constructor(LabsElevationArrow frontElev, LabsElevationArrow sideElev, LabsElevationArrow worktopElev, LabsElevationArrow rightSideElev=null, LabsElevationArrow backElev=null) {
	elevationAutoInsert(frontElev, sideElev, worktopElev, rightSideElev, backElev);
    }

New constructor cm.abstract.labs.LabsElevationAutoInsertEnv :

    /**
     * Creation.
     */
    public constructor(Class frontElev, Class leftElev, Class worktopElev, Class rightElev=null, Class backElev=null) {

	if (frontElev.extends() in ElevArrow) this.frontElev = frontElev;
	if (leftElev.extends() in ElevArrow) this.leftElev = leftElev;
	if (worktopElev.extends() in ElevArrow) this.worktopElev = worktopElev;
	if (rightElev.extends() in ElevArrow) this.rightElev = rightElev;
	if (backElev.extends() in ElevArrow) this.backElev = backElev;

	elevationAutoInsert();
    }

ElevArrow args

In addition to this, you can leverage the arrowArgs() on LabsRectRack to set fields post-construction as needed. (Don't forget your PropDef).

    /**
     * Arrow Args for auto generated arrows.
     */
    extend public str->Object arrowArgs(str dir=null) { return null; }

Abstract Office

Abstract Office Runtime Changes

  • Fixed an issue where typicals were inconsistently updating their 2D (FO-392)
  • Office material dialog cleanup and adding a method to enable the selection callback to be ran when the dialog tries to select a material.
  • Fix OfficeMaterialSelection to actually use the default size if none is provided.
  • Minor cleanup of tmount methods on PanelFrame.
  • Make animationTrySnap() on worksurface actually return whether it was successful or not, previously it would always return false.
  • Remove arbitrary 1inch inset of left/right snap.
    • Up until now, the left and right snap on PanelFrame were inset by one inch on each side. This was to visually offset the connector graphics since there was code in place to have them functionally behave as if they weren't inset at all.
    • As a result, when subclassing PanelFrame, one would have to either program around this offset by including the one inch in the connectsX() locations for any PanelJunctions, or override the initialLeftSnap(), initialRightSnap(), updateLRSnaps(), and the connectOffset() methods.
    • If you were relying on that offset, you can update your junction's connectsX() method accordingly to no longer use the one inch offset. There is load code on PanelFrame to adjust old drawings to make sure no connections are lost/broken on load.
    • If you want to keep relying on the old offset, you can implement the four methods mentioned in the second bullet point above and add the one inch offsets back in.
  • Move data inititialization for data surfaces to initializeWorksurface()
  • OfficeCabinet now uses visiblePosition() instead of visible3DPosition() for adjusting connector graphics. The visible3DPosition() and visible2DPosition() methods call visiblePosition() by default.
  • PickSkinAnimation now shows the discard property after a skin has been selected.
  • WorksurfaceDragAnimation has been updated to reflect updates to DragAnimation.
  • WorksurfaceInsertAnimationG2:update() has been updated to reflect past updates to InsertAnimationG2:update().
  • Worksurface cutouts have 2D graphics drawn for the holes automatically.

Category Changes/Migration

Coming with 11.0, Categories registered at the Abstract Office level have been drastically changed. This was done with feedback from the F5 and past feedback about categories in general. As such, code updates will be required as part of these changes.

To properly migrate/update to the new categories, a few things need to be done.

First, any place where category symbols were explicitly called out need to be updated. This includes view modes, layers, addSystemCategoriesTo() method on Snapper, systemCategories() method on ItemTag, etc.

Second, consider updating/removing extension specific categories in your custom packages. The new categories cover most of the common office product types so chances are there will be overlap with your current categories.

Because users can manually apply categories to snappers and item tags and can make custom view modes with them, to migrate these over, a migration map must be registered. This has already been done for Abstract Office, but if you are going to map your own categories over to new ones or ones in Abstract Office, you will also need to do this. An example of this is shown below and is taken from Abstract Office.

putCategoryMigratoryMap(cAOPkg, cF5CatUpdateVer, aoCategoryMigrateMap());
/**
 * Since some of the old categories are very generalized and the new
 * sub categories are very specific, we can't accurately map user
 * applied categories, view modes, or view ports.
 *
 * In those cases, we will map the the most general category we can and let
 * the user adjust to a more specific category.
 */
public symbol->symbol aoCategoryMigrateMap() {
    static symbol->symbol cats();

    if (!cats.any) {
	cats.put(#walls, cAOArchWallsCat);
	cats.put(#panels, cAOPanelsPanelCat);
	cats.put(#electric, cAOElectricalCat);
	cats.put(#storage, cAOStorageCat);
	cats.put(#desking, cAOCasegoodsCat);
	cats.put(#seating, cAOSeatingCat);
	cats.put(#worksurfaces, cAOWksfWorksurfaceCat);
	cats.put(#tables, cAOTablesTableCat);
	cats.put(#lighting, cAOLightAccessoryCat);
	cats.put(#legend, cAOLegendsCat);
	cats.put(#isoviews, cAOElevationCat);

	// old sub categories
	cats.put(#panelAnnotation, cAOPanelsPanelTxt);
	cats.put(#panelSym, cAOPanelsPanelSym);

	cats.put(#tableText, cAOTablesTableTxt);

	cats.put(#wsText, cAOWksfWorksurfaceTxt);
	cats.put(#wsSym, cAOWksfWorksurfaceSym);

	cats.put(#ohStorage, cAOStorageUpperCat);
	cats.put(#ohStorageTxt, cAOStorageUpperTxt);
	cats.put(#ohStorageSym, cAOStorageUpperSym);

	cats.put(#storageSym, cAOStorageLowerSym);
	cats.put(#storageText, cAOStorageLowerTxt);
    }

    return cats;
}

The map is defined as old category to new category (symbol->symbol). With this map, you then register it to a global migrator by using the function putCategoryMigratoryMap(). This function takes in symbol that represents the package the categories were registered with (for example cm.abstract.office), a version when the migration is happening (so in this case 11.0.0), and then the map of old to new categories.

/**
 * Package symbol to map of old category and new category.
 */
private CategoryMigrator _catMigrator : keep;

public CategoryMigrator categoryMigrator() {
    if (!_catMigrator) init _catMigrator();

    return _catMigrator;
}


/**
 * Put a migratory map (old category, new category) for a specific package.
 */
public void putCategoryMigratoryMap(symbol pkg, version v, symbol->symbol pairs) {
    categoryMigrator().putMigrator(pkg, v, pairs);
}


/**
 * Put a migratory map (old category, new category) for a specific package.
 */
extend public void putMigrator(symbol pkg, version v, symbol->symbol pairs) {

    version->symbol->symbol verMap = versionMigrateMap.get(pkg);
    if (verMap) {
	  verMap.put(v, pairs);
    } else {
	  version->symbol->symbol temp();
	  temp.put(v, pairs);
	  versionMigrateMap.put(pkg, temp);
    }
}

This registered map will be used when snappers, item tags, view ports, elevations, and custom view modes load to 11.0 to migrate their user applied categories correctly. This happens during loaded1() of these objects. The migrator and its code can be found in cm/core/category/categoryMigrator.cm.

    public void loaded1(ObjectFormatter formatter, LoadFailInfo failInfo) {
	super(formatter, failInfo);

	if (dbg_traceSnapperEvents) ptrace();
	if (dbg_traceSnapperEvWPln) pln("EV: ", this, ".loaded");

	#if (trackSnapperHistory) history(", ld1");
	initVessels();

	if (pkgVersion(#"core", formatter) < version(11, 0, 0)) {
	    migrateCategories(formatter);
	}
    }

    /**
     * Manually migrate any category failures after the migrator has mad an initial pass.
     */
    extend public void migrateCategoryFailures(symbol{} failures) {
	// Subclass responsiblity!
    }

How this works is that when the first object tries to migrate, all registered migration maps will be consolidated down based on their versions. For example, if the current version of Fika is 11.5 and a 10.5 drawing is loaded, migration for 11.0 and 11.5 will be checked and consolidated down for each package. If in 11.0, category A changed to category B, and in 11.5 category B changed to category C, the migrator will have a migrate A to C for all objects whose class is under custom.fika. Categories from core/abstracts will always be migrated after ones for a custom. This can also be handled at the snapper and item tag levels through the migrateCategories() method. This is the method that passes the object to the migrator to be processed. There is also the migrateCategoryFailures() method that gets called after the first pass of migration where extension specific categories should have been migrated. Here you can change how categories from core/abstracts get migrated.

Alt-i consistency improved

Alt-i was specifically inconsistent in 10.5 if a file was saved as UTF-8 instead of UTF-16 because CmScanner skipped the byte-order-mark (BOM). This is corrected in 11.0 and some other adjustments to alt-i should make it more consistent and correct.

In 10.5 alt-i sometimes does not move to the target even though a next-error-list was output. This has been fixed as well in 11.0.

Change to PropDef.adjusted()

Motivation:

  • Cleanup of messy conditional code
  • Speedup for common cases
  • Allow widening conversions (for instance double = int) in assign to make PropObj behave more as the cm language in general.

PropDef.adjusted() is extremely central to all customization code and we need to keep a thorough lookout for any trouble. If any trouble arises, the following debug-tools have been prepared:

  • The original adjusted() remains in PropDef as deprecated_adjusted().
  • cm.props propDef.cm has a tracing constant: private const bool dbg_trace_put = true;
  • PropObj has a deprecated_put()
  • PropObj has a compare_put() which runs both new and old versions to pinpoint what potentially differs
use cm.props, cm.subset, cm.basic;
private class T1 extends PropObj {
    public props {
	double "w" v=0.2 : domain=DoubleEnum(0.2, 0.4);
	distance "d" v=0.3 : domain=DistanceEnum(0.3, 0.5);
	distance "e" v=distance(0.3) : domain=DistanceEnum(0.3, 0.5);
    }
}

{
    T1 z();
    pln(#z."w");
    z."w" = 1; // uses the new PropDef adjusted which allows us to widen Int(1) -> double PropDef field
    pln(#z."w");
    pln(#z."w".class);
}
output:
  z."w"=0.2
  z."w"=1
  z."w".class=Double

{
    T1 z();
    pln(#z."w");
    z.deprecated_put("w", 1); // same behavior as in 10.5
    pln(#z."w");
    pln(#z."w".class);
}
output:
  z."w"=0.2
    PropDef(double w [ reg boxClass=Double]).depr_adj() null value or wrong boxClass double=1.Int, pType.boxClass=Double
  z."w"=0.2
  z."w".class=Double

Upgraded error reporting

{
    T1 z();
    pln(#z."w");
    z."w" = "ddd"; // "w" is a double field
}
output:
  z."w"=0.2
    PropDef(double w [ reg boxClass=Double]).adjusted() value type mismatch: double=ddd.Str

Extended tracing and compare put

cm.props propDef.cm
private const bool dbg_trace_put = true;
{
    T1 z();
    pln(#z."w");
    z."w" = 1;
    pln(z.compare_put("w", "dd"));
}
output:
  z."w"=0.2
  compare_put:
    new
      PropDef(double w [ reg boxClass=Double]).adjusted() value type mismatch: double=dd.Str
    old
      PropDef(double w [ reg boxClass=Double]).depr_adj() null value or wrong boxClass double=dd.Str, pType.boxClass=Double
    new=dd
    OLD=dd
  <dd, dd>
{
    T1 z();
    pln(#z."w");
    z."w" = 1;
    pln(z.compare_put("w", Byte(2)));
}
output:
  z."w"=0.2
  compare_put:
    new
    old
      PropDef(double w [ reg boxClass=Double]).depr_adj() null value or wrong boxClass double=2.Byte, pType.boxClass=Double
    new=2
    OLD=2
  <2, 2>

If you wish to add your own widening conversions to PropDef.put(), please contact dev support.

Supported widening conversions

private Object angleF_sangleF_widener(Object z)
private Object angleF_sangleF_widener(Object z)
private Object angle_orientation_widener(Object z)
private Object angle_orientation_widener(Object z)
private Object angle_sangle_widener(Object z)
private Object angle_sangle_widener(Object z)
private Object byte_double_widener(Object z)
private Object byte_double_widener(Object z)
private Object byte_float_widener(Object z)
private Object byte_float_widener(Object z)
private Object byte_int16_widener(Object z)
private Object byte_int16_widener(Object z)
private Object byte_int64_widener(Object z)
private Object byte_int64_widener(Object z)
private Object byte_int_widener(Object z)
private Object byte_int_widener(Object z)
private Object byte_nat64_widener(Object z)
private Object byte_nat64_widener(Object z)
private Object byte_nat_widener(Object z)
private Object byte_nat_widener(Object z)
private Object byte_word_widener(Object z)
private Object byte_word_widener(Object z)
private Object color_colorF_widener(Object z)
private Object color_colorF_widener(Object z)
private Object distance_double_widener(Object z)
private Object distance_double_widener(Object z)
private Object float_double_widener(Object z)
private Object float_double_widener(Object z)
private Object int16_double_widener(Object z)
private Object int16_double_widener(Object z)
private Object int16_float_widener(Object z)
private Object int16_float_widener(Object z)
private Object int16_int64_widener(Object z)
private Object int16_int64_widener(Object z)
private Object int16_int_widener(Object z)
private Object int16_int_widener(Object z)
private Object int16_nat64_widener(Object z)
private Object int16_nat64_widener(Object z)
private Object int64_double_widener(Object z)
private Object int64_double_widener(Object z)
private Object int8_double_widener(Object z)
private Object int8_double_widener(Object z)
private Object int8_float_widener(Object z)
private Object int8_float_widener(Object z)
private Object int8_int16_widener(Object z)
private Object int8_int16_widener(Object z)
private Object int8_int64_widener(Object z)
private Object int8_int64_widener(Object z)
private Object int8_int_widener(Object z)
private Object int8_int_widener(Object z)
private Object int8_nat64_widener(Object z)
private Object int8_nat64_widener(Object z)
private Object int_double_widener(Object z)
private Object int_double_widener(Object z)
private Object int_float_widener(Object z)
private Object int_float_widener(Object z)
private Object int_int64_widener(Object z)
private Object int_int64_widener(Object z)
private Object int_nat64_widener(Object z)
private Object int_nat64_widener(Object z)
private Object nat_double_widener(Object z)
private Object nat_double_widener(Object z)
private Object nat_float_widener(Object z)
private Object nat_float_widener(Object z)
private Object nat_int64_widener(Object z)
private Object nat_int64_widener(Object z)
private Object nat_nat64_widener(Object z)
private Object nat_nat64_widener(Object z)
private Object point2D_rect_widener(Object z)
private Object point2D_rect_widener(Object z)
private Object point2D_size2D_widener(Object z)
private Object point2D_size2D_widener(Object z)
private Object pointI_point2D_widener(Object z)
private Object pointI_point2D_widener(Object z)
private Object pointI_rect_widener(Object z)
private Object pointI_rect_widener(Object z)
private Object pointI_size2D_widener(Object z)
private Object pointI_size2D_widener(Object z)
private Object point_colorF_widener(Object z)
private Object point_colorF_widener(Object z)
private Object point_color_widener(Object z)
private Object point_color_widener(Object z)
private Object point_version_widener(Object z)
private Object point_version_widener(Object z)
private Object rangeI_range_widener(Object z)
private Object rangeI_range_widener(Object z)
private Object size2D_rect_widener(Object z)
private Object size2D_rect_widener(Object z)
private Object word_double_widener(Object z)
private Object word_double_widener(Object z)
private Object word_float_widener(Object z)
private Object word_float_widener(Object z)
private Object word_int64_widener(Object z)
private Object word_int64_widener(Object z)
private Object word_int_widener(Object z)
private Object word_int_widener(Object z)
private Object word_nat64_widener(Object z)
private Object word_nat64_widener(Object z)
private Object word_nat_widener(Object z)
private Object word_nat_widener(Object z)

Changes for background reports

In 11.0 its a lot easier to get started using background reports. Now there is a single interface in cet/runtime/errorlogger.cm. To send an background report simply call this function with the data you want to include in the report.

New for 11.0 is that files now can be added to the report using the files field.

/**
 * Log error.
 *
 * Origin is an optional reference to issue origin.
 */
public void logError(str message=null,
		     Exception exception=null,
		     CallStack stack=null,
		     StrBuf logOutput=null,
		     SrcRef origin=null,
		     SrcRef loggedAt=#:src,
		     Url[] files=null) {
    errorLogger.logError(..);
}

Changes in Locale Independence for String Comparisons

In 11.0, all built in string type (str) comparison methods have been changed to be locale-independent to ensure deterministic behavior. This includes compare(), icompare(), gt(), lt(), ge(), le(), sort(), isort(), >, >=, <, <=, and sorted maps with str key types.

In previous versions, certain low level drivers were overriding the global locale which resulted in unpredictable behaviors with CET, usually manifesting in issues with displaying the correct $RS value or crashes in worst cases. This change means that the C locale environment variable (LC_ALL) will now be ignored.

What are the effects?

In practical scenarios, this should not affect general behavior such as sorting or localization, as CM has been using the default LC_ALL=C locale, and most UI in CET should be handled transparently with our localization wrappers. However, testing should still be done to ensure there are no undesirable side effects.

How to check if I have any code that is affected?

We have provided a detector for any such methods that may be affected by the changes above. Simply run the code below, and evaluate if each warning need to be fixed (see next sections).

{
  enableLocaleStrCompareWarnings(enable=true, warnings=true);
  cm.runtime.util.qmegaCAB("custom.extensionName");
}

How do I fix the warnings or revert to the old behavior?

We have also provided versions of the methods above, prefixed with lc_, which will behave the same as older versions of CM, including lc_compare(), lc_icompare(), lc_gt(), lc_lt(), lc_ge(), lc_le(), lc_sort(), lc_isort(). These methods would respect the C Locale, however we would recommend to only use them if you are sure you need to.

For sorted maps, you can fix by setting a custom compare function:

{
    sorted str->int test = ["dd"->3, "ee"->4];
    test.setCompare(function lc_compare3);

    public int lc_compare3(str a, str b, Object env) { return lc_compare(a, b); }
}

How to silence (ignore) the warnings?

Simply add // !lc to the end of the line of code.

Changes to Line Styles and Thickness for 2D View and PaperSpace

We have made some changes to Line Styles in 11.0 This affects how they look in:

  • Standard 2D views and paper space (RED/GDI)
  • Print preview and physical print (GDI)
  • Print to PDF

Both line thickness and the look of stippled lines (dot, dash, dash-dot etc) are affected. The default thickness of solid lines should look the same as before.

Note that this will affect any PDF DIFF Tests with references made before 11.0. If you have any references with lines for PDF Diff Tests made before 11.0, the different line styles will cause the diff test to fail if run in 11.0. You will need to make new references in 11.0 for those tests.

You may find more information and some comparison images on this page.

Component

  • selected() now has a CoreObject owner passed through as needed.
    /**
     * Selected.
     * From componentVessel in space OR from pickSurface in view.
     * Return true if taken care of.
     */
    extend public bool selected(View view, WindowViewMouseInfo mi, PickSurface3D surface, CoreObject owner=null) {
	/** Subclass */
	return false;
    }
  • updating methods have been added:
    /**
     * Update Components.
     */
    public void updateComponents() {
	update();
	super(..);
    }


    /**
     * Update
     */
    extend public void update() { /** Subclass */ }
  • CoreObject now has:
    /**
     * Update Components.
     */
    extend public void updateComponents() {
	for (c in components()) c.updateComponents();
    }
     /**
      * Rotate component on insert.
      */
     extend public Orientation componentInsertRotation(Component component, Animation insertAnimation) {
	return null;
     }

Note: Nothing inherently calling this. You still need to manually invoke this as needed.

  • 2D selection has been fixed.

Default Calculation Columns

For a more complete guide/explanation, please refer to this page We have shrunk the default calculations that are visible to these 7:

  • Complete
  • Description
  • Quantity
  • List
  • Manual Sort
  • Options
  • Part Number Every other default core calculation column set to be initially not visible.

Visibility of Abstract DataSymbol Columns

We have made abstract dataSymbol columns not visible by default so you do not get 15 visible columns for turning on an extension with dataSymbol.

  • Area - DsPRdAreaColumn
  • Depth - DsPrdMeasurementColumn
  • Feature Description - DsFeatureDescPartColumn
  • Height - DsPrdMeasurementColumn
  • Length - DsPrdMeasurementColumn
  • Package Count - DsPackageCountColumn
  • Preview - DsPreviewColumn
  • Pricelist - DsPricelistColumn
  • SKU - DsPrdSKUColumn
  • Vendor - DsVendorNameColumn
  • Volume - DsPrdVolumeColumn
  • Weight - DsPrdWeightColumn
  • Width - DsPrdMeasurementColumn

Default Visible Columns in Main Article View

Instead of showing every registered column set to initially visible, we now set a limit of 10 columns to show in the calculation view. These will be determined by votes cast by each extension. The 10 columns with the most votes will show.

These votes should be placed in the extension start. They will be cast once the extension is turned on (for lazy extensions, this means when it is selected in toolbox).

Voting for columns is done like this:

upvotePartColumnVisibility(absUpchargePartColumn.key);
upvotePartColumnVisibility(absSourcePartColumn.key);

For tag columns, there are no public PartColumn classes so you have to specify the key like this:

upvotePartColumnVisibility("#cm.core;TAG1")

You can find core/abstract part columns in the following places:

  • cm/core/init/corePartColumns.cm
  • cm/abstract/dataSymbol/init.cm
  • cm/abstract/part/partColumns.cm

Empty Feature Description

If the feature descriptions are empty in the calculation dialog, do check your overridden DsPart class's infoTrees() method. When constructing a DsOptionInfoTree, pass in the groupDescription argument. As that is what will be shown in the calculation dialog.

ItemTag Change

Version 11.0 introduces a new inspection tool for users to view categories, tags, level, collaboration section, and other information associated with Snapper(s) and ItemTag(s). In order to generically obtain the tag text of an ItemTag (for identification purposes in the dialog), the following method has been added to ItemTag:

    /**
     * Tag Text.
     */
    extend public str tagText() {
	if (info and !info.tagText.emptyOrOnlyWhiteSpace) return info.tagText;
	return $noTagTextFoundFor # " " # toS;
    }

If you implement an ItemTag that does not store its text in its ItemTagInfo, it is imperative that you override tagText() to point towards either the actual displayed text or some other text that identifies the ItemTag.

JsonInt now supports 64-bit integers

cm.format.json.JsonInt now stores the numeric value as int64 instead of int. Be careful when calling JsonInt.int() as the returned value will be incorrect if the real value is not between minInt and maxInt.

// Max int value.
public const int maxInt = 2147483647;


// Min int value.
public const int minInt = (-2147483647 - 1);

Nested soulModify and quickSoulModify

The syntax soulModify and quickSoulModify now supports nesting. The temporarliy spawned soul copy gets replaced by a pure soul at the end of the outermost soulModify/quickSoulModify block.

Optimizations for searching and iterating in a sym

SymNode(s) now hold a mask of all components both on the node and everything below. This means we can now iterate efficiently over the structure if we're searching for something specific.

For example this code would in 10.5 always visit the whole graph, but in 11.0 it will instantly abort if there's no mesh below a node:

for (node in root, filter=SymNodeMeshFilter()) {
    ...
}

Most filters (with the exception of searching for specific node ids or leafs) are now efficient. For example if you would like to visit nodes that contain geometry, with a specific rep and that is tagged you can do this:

SymNodeFilter f = and(SymNodeRepFilter(rep),
		      SymNodeGeometryFilter(),
		      SymNodeComponentFilter(symTags));
for (node in root, filter=f) {
    ...
}

Internally this is used in many places to speed up the handling of syms, and should be used if possible.

Orientation basis vectors

We have corrected the perpendicular() function for orientation and orientationF. Previously it did not return a vector that was orthogonal against the direction/normal vector.

PropScheme and Selection Tool

We have upgraded props scheme with a new look together with the new selection tool dialog(Tools > Advanced Selection ...). Most of the changes for props scheme are related to UI elements.

All related files to PropsScheme has been moved into cm.core.propsScheme directory.

More information can be found in PropsScheme Migration

Sym stretching

The parameters used to control stretching behavior for CmSym models have been changed. Previously the stretch operation took two parameters as input:

  • stretchingEP (bool)
  • wantedLength (double)

This works well for most scenarios but if you change the stretchingEP parameter between different stretch operations you were forced to keep track of how much you stretched in each direction by yourself. To overcome this we have replaced the stretchingEP param with a new param called spOffset. This means that we now use the following two parameters to control the stretch operation:

  • spOffset (double)
  • wantedLength (double)

This means that we from now on always "right stretch" first (stretch the end point of the measure) and then adjust everything stretched by applying an offset - spOffset. If spOffset if positive "everything" will move in the sp -> ep direction. If spOffset is negative "everything" will move in the ep -> sp direction. This gives us two benefits:

  • It means that we always can reproduce the same stretch result by applying these parameters.
  • We can stretch both the starting point and the end point of the measure in a single operation (for instance center stretch).

Note A: Old CmSym files with stretching behavior will continue to function and you may still change the stretchingEP param for these old CmSym files.

Note B: If you import an old CmSym file, with stretching already defined, to Model Lab the old stretch programs will be translated to the new spOffset approach. So if you for some reason want to change an old CmSym model with stretch beavior already defined you must adapt the new way of controlling the stretch operation.

SymProps allowed keys

Keys prefixed with "_" are now reserved for internal use in SymProps only. Any internal key, like localBound, will now use _localBound instead. If you use the props API make sure you don't use the "_" prefix.

SymProps geometry flag has been removed

The hasGeometry flag in SymProps has been removed (corresponding to the symGeometryKey id). It has been replaced with matching against the component mask. The call SymNode.hasGeometry(descend=true) is still fast, and the prop can simply be ignored.

Toolbox cards

In CET 10.5 and older versions, we used to call rebuildAllToolboxCards() when the main app window got resize and resizeEnd. These calls (of course) produce a lot of slow response and flickering, so they have been removed. This means that your custom toolbox cards will no longer get rebuilt on app window resize, and there is no reason they should have to. However, if you really need this, please contact developer support!

Viewport2D deprecated

In order to convert the legacy viewports to XClip view clips, four new methods has been added to the Viewport2D class:

public XClipWormholeSnapper2D createXClipFromViewport() { ... }
public XClipWormholeSnapper2D createXClipFromWormholeCollapsed(Space space = null) { ... }
public void copyConfiguration(XClipWormholeSnapper2D wh) { ... }
public void createCompanionsFromViewport(XClipWormholeSnapper2D wh, XClipWormholeCompanionSnapper[] companions) { ... }

The first two methods generates a XClipWormholeSnapper2D object from the current ViewportSnapper2D instance and can be overridden in case your extended Viewport2D requires some intricate conversion logic.

Copying the Configuration

If your viewport has some additional configuration, this can be converted by simply overriding the copyConfiguration() method, which is called in both viewport conversion methods. Let's look at the base method:

/**
 * Copy configuration.
 */
extend public void copyConfiguration(XClipWormholeSnapper wh) {
    if (!wh.settings) return;
    if (blackAndWhite) wh."whBlackWhite" = true;

    if (ViewMode vm = getRegisteredViewMode(this.?viewModeKey.str)) {
	wh.setViewMode(vm);
    } else {
	wh.setViewMode(ViewMode("cm.core.visibility","newViewModeName", viewModeLayers));
    }

    if (ViewContentFilter filter = viewContentFilter) {
	wh.setViewContentFilter(copyContentFilter(filter));
    }
}

The ViewportSnapper2D class has a member called blackAndWhite, indicating whether the viewport should be without colors. This corresponds to the whBlackAndWhite property of the XClip view clip. The conversion transfers this configuration option to the new view clip in line 3.

Attaching Companions

In some cases, some of your extended viewport functionality can be abstracted into a companion. If this is the case, the method createCompanionsFromViewport() can be extended to automatically create and attach companions to the new XClip view clip based on the current ViewportSnapper2D. Let's look at the implementation of the base method:

/**
 * Create companions from the give viewport.
 * XClipWormholeSnapper wh currently unused, still kept since subclass might need it.
 * XClipWormholeCompanionSnapper[] companions is the list which should be populated with new companions by this method.
 */
extend public void createCompanionsFromViewport(XClipWormholeSnapper wh, XClipWormholeCompanionSnapper[] companions) {
    if (!frameType.none) {
	XClipWormholeFrameCompanionSnapper frame();
	frame."frameType" = frameType;
	frame."lStyle" = usedLineStyle;
	frame."lineWidth" = lineWidth;
	frame."lineColor" = lineColor;

	companions << frame;
    }

    if (showScale) {
	XClipWormholeScaleCompanionSnapper scale();
	scale."frame" = !frameType.none;

	companions << scale;
    }
}

The ViewportSnapper2D object uses the frametype member to indicate its frame. In XClip view clips, however, the frame functionality exists in a companion object which is attached to the viewport. So, in the conversion, if the ViewportSnapper2D instance has a frame, an XClipWormholeFrameCompanion instance is created, the frame type is set, and the companion gets appended to the companions list. That list will later be used to attach all companions to the new XClip view clip. The creation of the frame companion is implemented on lines 7-15.

Show content from other spaces

The XClip view clip supports showing contents from other spaces then the main 2D space. If multiple spaces exists in the drawing (e.g. alternatives, elevation views, etc.), the user will see a dropdown option for selecting other spaces than the main 2D space when inserting a new view clip. In order to add your own custom spaces to the list of available spaces you need to add an XClipWarpSpaceDomainEnv for your type of space.

The code below illustrates how the XClipWarpSpaceDomainEnv looks for elevation spaces.

public class ElevWarpSpaceDomainEnv extends XClipWarpSpaceDomainEnv  {
    public constructor() {
	spaceClass = ElevSpace;
    }


    /**
     * Return the name of the space.
     * if the return string is null the space will not be added.
     */
    public Str spaceName(Space space) {
	if (space as ElevSpace) {
	    if (space.arrow) {
	    return space.arrow.label();
	    }
	}
	return null;
    }
}

Once your XClipWarpSpaceDomain subclass has been implemented, it has to be registered in the xclip system. This is done using the global method xclipAddWarpSpaceDomain(XClipWarpSpaceDomain env).

xclipAddWarpSpaceDomain(ElevWarpSpaceDomainEnv());

Your specific space might also want a specific XClipSpaceManager that has some special logic for handling XClip-related events in your space. Refer to XClipSpaceManager and ElevXClipSpaceManager for further details on the class implementation. Once your space manager is implemented, connect it to your space by overriding the initXClips() method in your space.

public class ElevSpace extends Space {
    ...


    /**
     * init Elevation xclip space manager.
     */
    public void initXClips() {
	    if (!xclips) xclips = ElevXClipSpaceManager(this);
    }


    ...
}

Companions

There are currently 2 types of Companions for XClip view clips:

1.Graph additions - Changes the graphic of the view clip snapper, but does not alter the content (e.g. add a frame around the view clip, draw out the used scale of the view clip, etc.).

  1. Editors - Alters the content of the view clip (e.g. draw snappers inside view clip with part tagging colors, draw snappers in black and white, etc).

The Companion connects to the XClip Wormhole using normal connectors. They connect at (0, 0, 0) and shares rotation. This means the XClip Wormhole and the Companions connected have the same local coordinate systems (I.e. 5mm x-axis into the XClip Wormhole is 5mm x-axis into the Companion.

All Companions must inherit from XClipWormholeCompanionSnapper and the class must be registered using the registerXClipWormholeCompanionClass() function. When registered the Companion will show up in the same box in the Paperspace Toolbox under the XClipWormhole icon.

To create a Graphical Companion implement as a normal GraphSnapper usingG2() = true subclassing the base class directly XClipWormholeCompanionSnapper.

To create an Editor Companion inherit from the XClipWormholeEditor2DCompanionSnapper class and override the createEditor() method. Standard CET Wormhole Companions:

  • XClipWormholeScaleCompanionSnapper - Shows the scale of the XClip Wormhole.
  • XClipWormholeFrameCompanionSnapper - Creates a border around the clipped area in the XClip Wormhole.
  • XClipColumnBalloonCompanion - Shows and dimensions Column balloons outside edge of XClip Wormhole.
  • XClipWormholeTagCompanionSnapper - Colors content of XClip Wormhole with tagged Part Tags from chosen Part Tag Category.

Black and White changes from 10.5 to 11.0:

Starting from version 11.0, it is possible to give graphs a black fill color when using the BlackAndWhite companion. To do this, add a property called blackAndWhiteAllowFill to your snapper and set it to true. This will fill all graphs in that snapper where the line color is the same as the fill color.

Column Balloons

Column balloons are reference balloons for Columns in the drawing. They can be named and are interfacing with the XClip Column Balloon Companion.

If your Extension has Column Balloons it is strongly recommended to subclass the CET standard Column Balloon for future compatibility.

Auto Papers

If you have auto papers that previously generated viewports, these will now be automatically converted to view clips. All configuration and functionality of the AbsAPViewport2D will be transferred to the view clip through a companion called AbsXClipApWormholeSettingsCompanionSnapper. However, since the conversion is performed by the makePapers() method, you need to manually add the code line below in your subclass if this method has been overridden. The code below shows how and where the conversion is made in the BasicAPPaperMaker class.

    /**
     * Create paper(s) from the env.
     */
    public PaperSpace[] makePapers(AbsAPGroupEnv env) {
	StreamPaper stream(templateUrl);
	PaperSpace newSpace = stream.load(env.world);
	if (!newSpace) return null;
	if (paperGroup) newSpace.group = paperGroup;
	for (z in newSpace.snappers) {
	    if (developMode) {
		// Not needed in release.
		// FIXME: Will be unneeded when StreamPaper is fixed.
		z.initEntityId(newSpace);
	    }
	    if (z as ImageSnapper) {
		str fileName = z.originalFilename;

		Url imUrl;

		for (im in filesIn(templateUrl, fileName # ".*")) {
		    imUrl = im;
		    break;
		}

		z.file = imUrl;
		z.createGImage();
	    }
	    z.propDefs;
	    if (PropDef def = z.propDef(cAbsAPPushEnvProp)) {
		def.put(z, this, env);
		z.invalidate(dirty2D.rebuild);
	    }

	    if (z as AbsAPViewport2D) z.replaceWithXClip(); // <================ This line converts the viewport
	}
	return [newSpace];
    }

Custom View Clip Buttons

If you want to create a button for inserting a view clip that automatically modifies the view clip in some fashion (e.g. attaching companions, changing some configuration etc.) you can use the method xclipWormholeSnapperLimb(). This method has one parameter called configureCallback which will be invoked once the view clip has been created, providing a straight-forward method of editing your newly created view clip. For instance, the function createCustomXClipLimb below creates a button which starts the animation for inserting a view clip and automatically attaches an AbsXClipAPWormholeSettingsCompanionSnapper to each.

use cm.core.xclip;

package const symbol pkgXClip = #"cm.core.xclip";


/**
 * Configure the wormhole however you like
 */
package void configureWormhole(XClipWormholeSnapper wh) {
    AbsXClipAPWormholeSettingsCompanionSnapper c();
    c.z2m = apViewportZoomMode.group;
    wh.connectCompanion(c);
}


/**
 * Create our own button which lets us configure the wormhole snapper using the method above.
 */
package ComponentLimb createCustomXClipLimb(LibraryLimb parent, symbol pkg, str key, UIHint hint=null) {
   return xclipWormholeSnapperLimb(parent, pkg, key, hint, function configureWormhole);
}

checkfile in loadSym now defaults to true

checkFile was previously true, but it's now false because of the possibly severe performance penalty of including modification date for a cached request, if it's just going to use the cache anyway.

public SymNode loadSym(Url url, bool lazy=true, bool cache=true, bool checkFile=false) {

eqBox(Object, Object) method has been deprecated

In 10.5 eqBox(Str, Str) and other cases produces incorrect results. eqBox() now uses the eqBoxedValue() function which always has produced correct results for all boxed values including strings.

use cm.runtime;
{
    str a = "dd";
    str b = "dd";
    str c = "d" # "d";
    str d = b.copy;
    pln(#a.eqBox(b));
    pln(#a.eqBox(c));
    pln(#a.eqBox(d));
    pln(#a.eqBoxedValue(b));
    pln(#a.eqBoxedValue(c));
    pln(#a.eqBoxedValue(d));
}

10.5:
a.eqBox(b)=true
a.eqBox(c)=false
a.eqBox(d)=false
a.eqBoxedValue(b)=true
a.eqBoxedValue(c)=true
a.eqBoxedValue(d)=true

11.0:
a.eqBox(b)=true
a.eqBox(c)=true
a.eqBox(d)=true
a.eqBoxedValue(b)=true
a.eqBoxedValue(c)=true
a.eqBoxedValue(d)=true

putAuxillaryObject() and getAuxillaryObject() for calcArticleView

All functions in cm.core.calc.articleViewsInSpace.cm now use putAuxillaryObject()/getAuxillaryObject() instead of putAuxillary()/getAuxillary() for the key "calcArticleViews". The motivation for this change is to reduce dependencies. If you are getting or putting "calcArticleView" in your extensions you will have to do the same change.