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.
PHBodyInsertAnimationG2
PHBodyWidthAdjustingAnimationG2
PlacingMultiplePHBodyAnimationG2
ShelvingPropertyStretchAnimation
(superStretch)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.ignore modify notice
and/or stream=null
are automatically discarded to prevent them from being part of the soul key.#stream_null
are automatically discarded to prevent them from being part of the soul key.LOPosition
flags front
, inner
and back
into account.LOPosition
according to compareLOPosition()
function. The str->tuple maps only sorted on point.toS
.Added symbol and text categories for all labs categories.
Added warp space domain registration for LabsElevationSpace
on abstract.labs init to give new viewports visibility to those views.
acceptScheme()
has been overridden on the following classes:
LabsWorktop
LabsRackContent
LabsRackFront
LabsSideSplash
Other classes that were previously overridden:
LabsSink
LabsTap
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(); }
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; }
OfficeMaterialSelection
to actually use the default size if none is provided.PanelFrame
.animationTrySnap()
on worksurface actually return whether it was successful or not, previously it would always return false.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.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.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.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()
.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 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.
Motivation:
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:
adjusted()
remains in PropDef as deprecated_adjusted()
.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 differsuse 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)
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(..); }
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.
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.
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"); }
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); } }
Simply add // !lc
to the end of the line of code.
We have made some changes to Line Styles in 11.0 This affects how they look in:
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.
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; }
/** * 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.
For a more complete guide/explanation, please refer to this page We have shrunk the default calculations that are visible to these 7:
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.
DsPRdAreaColumn
DsPrdMeasurementColumn
DsFeatureDescPartColumn
DsPrdMeasurementColumn
DsPrdMeasurementColumn
DsPackageCountColumn
DsPreviewColumn
DsPricelistColumn
DsPrdSKUColumn
DsVendorNameColumn
DsPrdVolumeColumn
DsPrdWeightColumn
DsPrdMeasurementColumn
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:
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.
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
.
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);
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.
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.
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.
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
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:
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.
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.
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.
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!
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.
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.
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.
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); } ... }
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.).
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.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 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.
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]; }
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
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) {
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
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.