2.19.0 Change Notes
Decoration graphics enhancements
Visible edges
Graphics produced by a GraphicBuilder can now produce edges for surfaces. By default, edges are only produced for graphics of type GraphicType.Scene, and only if the Viewport's ViewFlags specify that edges should be displayed. To generate edges for other types of graphics, or to prevent them from being generated, override GraphicBuilderOptions.generateEdges or GraphicBuilder.wantEdges when creating the graphic. Note that surfaces will z-fight with their edges to a degree unless the graphic is also pickable - see GraphicBuilderOptions.pickable.
Solid primitives in decorations
Decoration graphics can now be produced from SolidPrimitives - e.g., spheres, cones, slabs, swept surfaces, and so on - using GraphicBuilder.addSolidPrimitive.
Presentation changes
Added RelatedPropertiesSpecificationNew.skipIfDuplicate
attribute to allow specification to be overriden by specifications from higher priority content modifiers. Set this attribute to all related properties' specifications in the default BisCore ruleset.
Dictionary enhancements
Dictionary.keys and Dictionary.values enable iteration of the dictionary's keys and values in the same manner as a standard Map.
Dictionary.findOrInsert returns the existing value associated with a key, or - if none yet exists - inserts a new value with that key. It also returns a flag indicating whether or not a new value was inserted. This allows the following code that requires two lookups of the key:
let value = dictionary.get(key);
let inserted = undefined !== value;
if (undefined === value)
inserted = dictionary.insert(key, value = newValue);
alert(`${value} was ${inserted ? "inserted" : "already present"}`);
To be replaced with a more efficient version that requires only one lookup:
const result = dictionary.findOrInsert(key, value);
alert(`${result.value} was ${result.inserted ? "inserted" : "already present"}`);
ChangesetIndex vs. ChangesetId
A changeset represents the delta (i.e. the "set of changes") between two points on an iModel's timeline. It can be identified by two means: a ChangesetId and a ChangesetIndex - every changeset has both once it has been pushed to iModelHub. A ChangesetId
is a string that is formed from the checksum of the contents of the changeset and its parent ChangesetId
. A ChangesetIndex
is a small sequential integer representing the position of the changeset on the iModel's timeline. Later changesets will always have a larger ChangesetIndex
than earlier changesets. However, it is not possible to compare two ChangesetId
s and tell anything about their relative position on the timeline.
Much of the iTwin.js
api that refers to changesets takes a ChangesetId
as an argument. That is unfortunate, since ChangesetIndex
is often required to determine order of changesets. Obtaining the ChangesetIndex
from a ChangesetId
requires a round-trip to iModelHub. This version begins the process of reworking the api to prefer ChangesetIndex
as the identifier for changesets. However, for backwards compatibility, the new types ChangesetIndexAndId (both values are known) and ChangesetIdWithIndex (Id is known, index may be undefined) are used many places. Ultimately only ChangesetIndex
will be used to identify changesets, and you should prefer it in any new api that identifies changesets.
Breaking change
The return type of the methods BriefcaseDb.pullAndMergeChanges
and BriefcaseDb.pushChanges was changed from a string ChangesetId
to ChangesetIndexAndId.
FederationGuid Policy Change
In previous versions, if you inserted an element with ElementProps.federationGuid undefined
, its value would be NULL
in the inserted element. In this version, if federationGuid === undefined
, a new valid Guid will be created for the new element. This is to better facilitate tracking element identity across iModels in IModelTransformer
.
Note: if
federationGuid
is a valid Guid, its value will be preserved.
To insert an element with a NULL
federationGuid, set it to an illegal value (e.g. Guid.empty
).
const elementId = imodel1.elements.insertElement( {
classFullName: SpatialCategory.classFullName,
model: IModel.dictionaryId,
federationGuid: Guid.empty,
code: SpatialCategory.createCode(imodel1, IModel.dictionaryId, "TestCategory")
});
Last Updated: 02 February, 2022