4.9.0 Change Notes

Table of contents:

Quantity

  • The minWidth property on FormatProps now works as documented.
  • The spacer property on FormatProps now indicates the space used between composite components, it defaults to a single space, and there is no longer a ':' prepended. If a ':' spacer is desired, spacer has to be set accordingly. This is to streamline the behavior with the documentation and native APIs.
  • Added support for bearing and azimuth format types (e.g. bearing N45°30'10"E). A new phenomenon "Direction" for these will be added to our units library soon, but they work just as well with the angle phenomenon for now. Persistence values for both bearing and azimuth are to be provided counter-clockwise from an east-base (inspired by PowerPlatform).

Electron 32 support

In addition to already supported Electron versions, iTwin.js now supports Electron 32.

Geometry

Approximating an elliptical arc with a circular arc chain

Arc3d.constructCircularArcChainApproximation returns a CurveChain of circular arcs that approximates the elliptical instance arc. Each arc in the chain starts and ends on the ellipse. The ellipse major/minor axis points and tangents are also interpolated, as well as those at the elliptical arc start/end, and the arcs are arranged to preserve ellipse symmetry. Various settings in the optional EllipticalArcApproximationOptions input object control the approximation accuracy. The default method is EllipticalArcSampleMethod.AdaptiveSubdivision, which is controlled by a maximum error distance, options.maxError. Other values of options.sampleMethod interpolate the ellipse in other ways, controlled by the number of points interpolated in a given quadrant, options.numSamplesInQuadrant. For a fixed number of samples, the default method usually yields the most accurate approximation.

Pictured below in order of decreasing error are some example approximations in blue, with ellipses in black, sample sites circled, and maximum error segment in red.

Approximation using options.sampleMethod = EllipticalArcSampleMethod.UniformCurvature and options.numSamplesInQuadrant = 5, yielding error 0.18:

Uniform Curvature

Approximation using options.sampleMethod = EllipticalArcSampleMethod.UniformParameter and options.numSamplesInQuadrant = 5, yielding error 0.12:

Uniform Parameter

Approximation using options.sampleMethod = EllipticalArcSampleMethod.NonUniformCurvature, options.remapFunction = (x) => x*x, and options.numSamplesInQuadrant = 5, yielding error 0.05:

Quadratic Curvature

Approximation using options.sampleMethod = EllipticalArcSampleMethod.AdaptiveSubdivision and options.maxError === 0.05, yielding error 0.03:

Adaptive Subdivision

Triangulating points

PolyfaceBuilder.pointsToTriangulatedPolyface, which creates a Polyface from an xy-triangulation of input points, now uses the StrokeOptions input setting options.chordTol to control the maximum xy-distance for equating points. This method preserves the highest z-coordinate among points equated in this manner. The default for this setting is Geometry.smallMetricDistance, however for typical DTM datasets, a larger tolerance can be used (e.g., 1-2mm) to eliminate extraneous "skirt" points that lie underneath the terrain boundary.

Pictured below are triangulations of a DTM dataset with skirt points. At top is the result using default tolerance. Due to the skirt points having xy-distance greater than the default tolerance from actual terrain sites, they are included in the triangulation, resulting in undesirable near-vertical facets. At bottom is the result using options.chordTol = 0.002, which is sufficiently large to remove these artifacts:

Toleranced Triangulations

Display

Dynamic clip masks

PlanarClipMaskSettings permit you to mask out (render partially or fully transparent) portions of the background map based on its intersection with other geometry in the scene. Previously, only GeometricModels and reality models could contribute to the mask. Now, geometry added to the scene dynamically via TiledGraphicsProviders can also contribute to the mask. As with reality models, TiledGraphicsProviders' geometry only contributes to the mask in PlanarClipMaskMode.Priority. You can optionally configure a custom mask priority using TileTreeReference.planarClipMaskPriority or the newly-added RenderGraphicTileTreeArgs.planarClipMaskPriority. Here's an example of the latter:


/** Mask out portions of the viewport's background map where it intersects a set of spherical regions. */
export function maskBackgroundMap(viewport: Viewport, regions: Iterable<Sphere>): void {
  // Use a GraphicBuilder to define the mask geometry.
  const builder = IModelApp.renderSystem.createGraphic({
    type: GraphicType.Scene,
    computeChordTolerance: () => 0.1,
  });

  for (const region of regions) {
    builder.addSolidPrimitive(region);
  }

  // Create a tile tree reference to provide the graphics defining the mask.
  const tileTreeReference = TileTreeReference.createFromRenderGraphic({
    modelId: viewport.iModel.transientIds.getNext(),
    graphic: builder.finish(),
    iModel: viewport.iModel,
    // Set the priority higher than the default of PlanarClipMaskPriority.DesignModel.
    planarClipMaskPriority: 4100,
  });

  // Add the tile tree reference to the viewport as a TiledGraphicsProvider.
  viewport.addTiledGraphicsProvider({
    forEachTileTreeRef: (_vp, func) => func(tileTreeReference),
  });

  // Enable masking by priority, with priority set just below that of our TileTreeReference so that the map will only be masked by
  // our geometry, not by any design models that may be present in the scene.
  viewport.changeBackgroundMapProps({
    planarClipMask: {
      mode: PlanarClipMaskMode.Priority,
      priority: 4000,
    },
  });
  viewport.invalidateRenderPlan();
}

Presentation

Custom content parser for creating element properties

The getElementProperties function on the backend PresentationManager has two overloads:

  • For single element case, taking elementId and returning an data structure in the form of ElementProperties.
  • For multiple elements case, taking an optional list of elementClasses and returning properties of those elements. While the default form of the returned data structure is ElementProperties, just like in single element case, the overload allows for a custom parser function to be provided. In that case the parser function determines the form of the returned data structure.

In this release the overload for single element case was enhanced to also take an optional custom content parser to make the two overloads consistent in this regard. In addition, the getElementProperties method on the frontend PresentationManager has also been enhanced with this new feature to be consistent with the similar method on the backend.

A new GetRelatedDisplayLabel function symbol has been added to ECInstance ECExpressions context, allowing retrieval of related instance label. The function takes 3 arguments: full name of a relationship, its direction and related class name. Example usage in calculated properties specification:

{
  "label": "My Calculated Property",
  "value": "this.GetRelatedDisplayLabel(\"BisCore:ModelContainsElements\", \"Backward\", \"BisCore:Model\")"
}

The above specification, when applied to BisCore:Element content, will include a "My Calculated Property" property whose value equals to the label of the model that contains the element.

Referencing schema-based categories in property overrides and calculated properties

In some cases there may be a need to place specific property in the same group as other specific properties. One way to do that is by creating a property category specification and assigning it to all such properties. However, what if want to place a property next to other properties, which are categorized through a schema-based category? This is now possible through the new SchemaCategory category identifier. For example, to place a calculated property next to an ECProperty that uses MySchema:MyCategory category:

{
  "label": "My calculated property",
  "categoryId": {
    "type": "SchemaCategory",
    "categoryName": "MySchema:MyCategory"
  }
}

Calculated properties specification enhancements

A number of enhancements have been made to calculated properties specification:

  • The value is now optional. If not provided, the value of resulting property will be undefined.

  • A new optional type attribute has been added. The attribute allows specifying value type of the calculated property, allowing the property to have other types than string. The default value is string.

  • A new optional extendedData attribute has been added. The attribute allows associating resulting calculated properties field with some extra information, which may be especially useful for dynamically created calculated properties.

API deprecations

@itwin/appui-abstract

  • LayoutFragmentProps, ContentLayoutProps, LayoutSplitPropsBase, LayoutHorizontalSplitProps, LayoutVerticalSplitProps, and StandardContentLayouts have been deprecated. Use the same APIs from @itwin/appui-react instead.

  • BackendItemsManager is internal and should never have been consumed. It has been deprecated and will be removed in 5.0.0. Use UiFramework.backstage from @itwin/appui-react instead.

Last Updated: 02 October, 2024