UNPKG

@itwin/core-common

Version:

iTwin.js components common to frontend and backend

694 lines • 36.9 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Rendering */ import { assert, Id64 } from "@itwin/core-bentley"; import { BatchType } from "./FeatureTable"; import { GeometryClass } from "./GeometryParams"; import { RgbColor } from "./RgbColor"; function copyIdSetToUint32Set(dst, src) { dst.clear(); if (typeof src === "string") { dst.addId(src); } else { for (const id of src) dst.addId(id); } } function propsMatchDefaults(props) { return !props.rgb && !props.lineRgb && undefined === props.weight && undefined === props.linePixels && undefined === props.transparency && undefined === props.lineTransparency && !props.ignoresMaterial && !props.nonLocatable && !props.emphasized; } function equalRgb(a, b) { if (a === b) { return true; } else if (!a || !b) { return false; } else { return a.equals(b); } } function equalLineRgb(a, b) { if (a === b) { return true; } else if (a instanceof RgbColor && b instanceof RgbColor) { return equalRgb(a, b); } else { return false; } } function equalTransparency(a, b) { if (a === b) { return true; } else if (undefined === a || undefined === b) { return false; } else { return Math.floor(a * 0xff) === Math.floor(b * 0xff); } } function equalLineTransparency(a, b) { if (a === b) { return true; } else if (typeof a === "number" && typeof b === "number") { return equalTransparency(a, b); } else { assert(a === undefined || a === false); assert(b === undefined || b === false); return false; } } function transparencyFromJSON(transp) { if (undefined === transp) { return undefined; } transp = Math.max(0, Math.min(transp, 1)); // Fix up rounding errors... const smallDelta = 0.0001; if (1.0 - transp < smallDelta) { transp = 1.0; } else if (transp < smallDelta) { transp = 0.0; } return transp; } /** Defines overrides for selected aspects of a [[Feature]]'s symbology. * Any member defined in the appearance overrides that aspect of symbology for all [[Feature]]s to which the appearance is applied. * * The [[rgb]] and [[transparency]] overrides, if defined, apply to all geometry by default. * However, the color and transparency of "linear" geometry can optionally be controlled independently from the rest of the geometry via [[lineRgb]] and [[lineTransparency]]. * Linear geometry consists of any of the following: * - Curves and line strings; * - Points and point strings; and * - The outlines of planar regions. * The edges of 3d surfaces like spheres are not considered linear geometry. * * @see [[FeatureOverrides]] to customize the appearance of multiple features. * @public */ export class FeatureAppearance { /** Overrides the feature's color. * If [[lineRgb]] is `undefined`, this color also applies to linear geometry. */ rgb; /** If defined, overrides the color of linear geometry independently of [[rgb]]. * If `false`, linear geometry draws using its normal color; otherwise, it uses the specified color. */ lineRgb; /** The width of lines and edges in pixels as an integer in [1, 31]. */ weight; /** The transparency in the range [0, 1] where 0 indicates fully opaque and 1 indicates fully transparent. * If [[lineTransparency]] is `undefined`, this transparency also applies to linear geometry. * @see [[viewDependentTransparency]] for details on how this override interacts with the [DisplayStyle]($backend). */ transparency; /** If defined, overrides the transparency of linear geometry independently of [[transparency]]. * If `false`, linear geometry draws using its normal transparency; otherwise, it uses the specified transparency. * @see [[viewDependentTransparency]] for details on how this override interacts with the [DisplayStyle]($backend). */ lineTransparency; /** The pixel pattern applied to lines and edges. */ linePixels; /** If true, don't apply the [[RenderMaterial]] to the feature's surfaces. */ ignoresMaterial; /** If true, the feature will not be drawn when using [Viewport.readPixels]($frontend), meaning [Tool]($frontend)s will not be able to interact with it. */ nonLocatable; /** If true, the feature will be rendered using the [[Hilite.Settings]] defined by [Viewport.emphasisSettings]($frontend) to make it stand out. */ emphasized; /** If true, then [[transparency]] and/or [[lineTransparency]] will only be applied if [[ViewFlags.transparency]] is enabled and the current [[RenderMode]] supports transparency. * Default: false, meaning the transparency will be applied regardless of view flags or render mode. * This property has no effect if this appearance does not override transparency. */ viewDependentTransparency; /** An appearance that overrides nothing. */ static defaults = new FeatureAppearance({}); static fromJSON(props) { if (!props || propsMatchDefaults(props)) { return this.defaults; } return new FeatureAppearance(props); } /** Create a FeatureAppearance that overrides only the RGB color. * @note The transparency component of the ColorDef is ignored. */ static fromRgb(color) { return new FeatureAppearance({ rgb: RgbColor.fromColorDef(color) }); } /** Create a FeatureAppearance that overrides the RGB and transparency. * The appearance's transparency is derived from the transparency component of the ColorDef. */ static fromRgba(color, viewDependentTransparency = false) { return new FeatureAppearance({ rgb: RgbColor.fromColorDef(color), transparency: color.colors.t / 255, viewDependentTransparency: viewDependentTransparency ? true : undefined, }); } /** Create a FeatureAppearance that overrides only the transparency */ static fromTransparency(transparencyValue, viewDependent = false) { return new FeatureAppearance({ transparency: transparencyValue, viewDependentTransparency: viewDependent ? true : undefined, }); } /** Create a FeatureAppearance with overrides corresponding to those defined by the supplied SubCategoryOverride. * @note Subcategory overrides set [[viewDependentTransparency]] to `true`. */ static fromSubCategoryOverride(ovr) { const rgb = undefined !== ovr.color ? RgbColor.fromColorDef(ovr.color) : undefined; const transparency = ovr.transparency; const weight = ovr.weight; const ignoresMaterial = undefined !== ovr.material && Id64.isValid(ovr.material) ? true : undefined; return this.fromJSON({ rgb, transparency, weight, ignoresMaterial, viewDependentTransparency: true }); } /** Returns true if this appearance does not override any aspects of symbology. */ get matchesDefaults() { return this.equals(FeatureAppearance.defaults); } get overridesRgb() { return undefined !== this.rgb; } get overridesTransparency() { return undefined !== this.transparency; } get overridesLinePixels() { return undefined !== this.linePixels; } get overridesWeight() { return undefined !== this.weight; } /** Get the color that will be applied to linear geometry, or undefined if not overridden. * This is the same as [[rgb]] if [[lineRgb]] is `undefined`. */ getLineRgb() { return false !== this.lineRgb ? (this.lineRgb ?? this.rgb) : undefined; } /** Get the transparency that will be applied to linear geometry, or undefined if not overridden. * This is the same as [[transparency]] if [[lineTransparency]] is `undefined`. */ getLineTransparency() { return false !== this.lineTransparency ? (this.lineTransparency ?? this.transparency) : undefined; } get overridesSymbology() { return this.overridesRgb || this.overridesTransparency || this.overridesWeight || this.overridesLinePixels || !!this.ignoresMaterial || this.emphasized || this.overridesNonLocatable || undefined !== this.getLineRgb() || undefined !== this.getLineTransparency(); } get overridesNonLocatable() { return undefined !== this.nonLocatable; } get isFullyTransparent() { const surf = this.transparency ?? 0; const line = this.getLineTransparency() ?? 0; return surf >= 1 && line >= 1; } /** Returns true if any aspect of the appearance is overridden (i.e., if any member is not undefined). */ get anyOverridden() { return this.overridesSymbology || this.overridesNonLocatable; } equals(other) { if (this === other) return true; return equalRgb(this.rgb, other.rgb) && this.weight === other.weight && equalTransparency(this.transparency, other.transparency) && this.linePixels === other.linePixels && this.ignoresMaterial === other.ignoresMaterial && this.nonLocatable === other.nonLocatable && this.emphasized === other.emphasized && this.viewDependentTransparency === other.viewDependentTransparency && equalLineTransparency(this.lineTransparency, other.lineTransparency) && equalLineRgb(this.lineRgb, other.lineRgb); } toJSON() { const props = {}; if (this.rgb) props.rgb = this.rgb.toJSON(); if (undefined !== this.weight) props.weight = this.weight; if (undefined !== this.transparency) { props.transparency = this.transparency; if (this.viewDependentTransparency) props.viewDependentTransparency = true; } if (undefined !== this.linePixels) props.linePixels = this.linePixels; if (true === this.ignoresMaterial) props.ignoresMaterial = true; if (true === this.nonLocatable) props.nonLocatable = true; if (true === this.emphasized) props.emphasized = true; if (undefined !== this.lineTransparency) props.lineTransparency = this.lineTransparency; if (this.lineRgb) { props.lineRgb = this.lineRgb; if (this.viewDependentTransparency) props.viewDependentTransparency = true; } return props; } /** Convert this appearance to JSON, and override any properties explicitly specified by `changedProps` in the result. * Example: * ```ts * const base = FeatureAppearance.fromRgba(ColorDef.white); // transparency=0, rgb=white * const clone = base.cloneProps({ transparency: undefined, weight: 5 }); // transparency=undefined, rgb=white, weight=5 * ``` * @see [[FeatureAppearance.clone]]. */ cloneProps(changedProps) { return { ...this.toJSON(), ...changedProps, }; } /** Create a copy of this appearance, overriding any properties explicitly specified by `changedProps`. * Example: * ```ts * const base = FeatureAppearance.fromRgba(ColorDef.white); // transparency=0, rgb=white * const clone = base.clone({ transparency: undefined, weight: 5 }); // transparency=undefined, rgb=white, weight=5 * ``` * @see [[FeatureAppearance.cloneProps]]. */ clone(changedProps) { return FeatureAppearance.fromJSON(this.cloneProps(changedProps)); } /** Produce a FeatureAppearance from the supplied appearance in which any aspect not defined by the base appearance is overridden by this appearance. */ extendAppearance(base) { if (!this.overridesSymbology) return base; const props = base.toJSON(); if (undefined === props.rgb) props.rgb = this.rgb; if (undefined === props.transparency) props.transparency = this.transparency; if (undefined === props.linePixels) props.linePixels = this.linePixels; if (undefined === props.weight) props.weight = this.weight; if (undefined === props.ignoresMaterial && this.ignoresMaterial) props.ignoresMaterial = true; if (undefined === props.nonLocatable && this.nonLocatable) props.nonLocatable = true; if (undefined === props.emphasized && this.emphasized) props.emphasized = true; if (!props.lineRgb) props.lineRgb = this.lineRgb; if (undefined === props.lineTransparency) props.lineTransparency = this.lineTransparency; if (this.viewDependentTransparency && (undefined !== props.transparency || undefined !== props.lineTransparency)) props.viewDependentTransparency = true; return FeatureAppearance.fromJSON(props); } constructor(props) { this.rgb = undefined !== props.rgb ? RgbColor.fromJSON(props.rgb) : undefined; this.lineRgb = typeof props.lineRgb === "object" ? RgbColor.fromJSON(props.lineRgb) : (false === props.lineRgb ? false : undefined); this.transparency = transparencyFromJSON(props.transparency); this.lineTransparency = typeof props.lineTransparency === "number" ? transparencyFromJSON(props.lineTransparency) : (false === props.lineTransparency ? false : undefined); this.weight = props.weight; this.linePixels = props.linePixels; this.ignoresMaterial = props.ignoresMaterial; this.nonLocatable = props.nonLocatable; this.emphasized = props.emphasized; if (undefined !== this.weight) { this.weight = Math.max(1, Math.min(this.weight, 32)); } if (props.viewDependentTransparency && (undefined !== this.transparency || undefined !== this.getLineTransparency())) { this.viewDependentTransparency = true; } } } const scratchIgnoreAnimationOverridesArgs = { elementId: { lower: 0, upper: 0 }, animationNodeId: 0, }; /** Specifies how to customize the appearance of individual [[Feature]]s, typically within the context of a [Viewport]($frontend). * Individual aspects of a feature's appearance - like visibility, color, and transparency - are overridden by supplying a [[FeatureAppearance]]. * Those overrides can be specified on the basis of the feature's model, element, and/or subcategory. A default set of overrides can also be specified to * apply to the appearance of any feature not otherwise overridden. * * It is possible to override multiple aspects of a feature on different bases. For example, you might specify that all features belonging to subcategory "A" should be drawn in red, * and that all features belonging to model "B" should be drawn 50% transparent. In this case, a feature belonging to both subcategory "A" and model "B" will be drawn as 50% transparent red - * the separate overrides are combined to produce the feature's overall appearance. * * In the case of conflicts, there is an order of precedence: * - Model overrides take highest precedence. * - Element overrides are of higher precedence than subcategory and animation overrides. * - Overrides applied by a [[RenderSchedule.Script]]'s [[RenderSchedule.ElementTimeline]] are of higher precedence than subcategory overrides, but can be suppressed on a per-element basis via [[ignoreAnimationOverrides]]. * - Subcategory overrides have lowest precedence. * * For example, you might specify that all features belonging to subcategory "A" should be drawn in red, and all those belonging to model "B" should be drawn in green. * Then a feature belonging to subcategory "A" and model "B" will be drawn in green, because the model overrides take precedence. * * Instances of this class are not typically instantiated by an application directly; instead, an application can implement a [FeatureOverrideProvider]($frontend) * that augments the overrides supplied by a viewport. * * @see [FeatureSymbology.Overrides]($frontend) to create overrides specific to a [Viewport]($frontend) or [ViewState]($frontend). * @see [FeatureOverrideProvider]($frontend) to customize the appearance of features within a [Viewport]($frontend). * @public */ export class FeatureOverrides { /** @internal */ _ignoreAnimationOverrides = []; /** Ids of elements that should never be drawn. This takes precedence over [[alwaysDrawn]]. @internal */ _neverDrawn = new Id64.Uint32Set(); /** Ids of elements that should always be drawn. [[neverDrawn]] takes precedence over this set. @internal */ _alwaysDrawn = new Id64.Uint32Set(); /** If true, no elements *except* those defined in the "always drawn" set will be drawn. * @see [[setAlwaysDrawn]] */ isAlwaysDrawnExclusive = false; /** If true, the always-drawn elements are drawn even if their subcategories are not visible. * @see [[setAlwaysDrawn]] */ alwaysDrawnIgnoresSubCategory = true; /** If true, all subcategories are considered visible. This is used for drawing sheets via section callouts in the absence of an actual sheet view. */ ignoreSubCategory = false; /** Overrides applied to any feature not explicitly overridden. @internal */ _defaultOverrides = FeatureAppearance.defaults; /** Whether construction geometry should be drawn. */ _constructions = false; /** Whether dimensions should be drawn. */ _dimensions = false; /** Whether area patterns should be drawn. */ _patterns = false; /** Whether line weights should be applied. If false, all lines are rendered 1-pixel wide. */ _lineWeights = true; /** Overrides applied to all elements belonging to each model. @internal */ _modelOverrides = new Id64.Uint32Map(); /** Overrides applied to specific elements. */ _elementOverrides = new Id64.Uint32Map(); /** Overrides applied to geometry belonging to each subcategory. */ _subCategoryOverrides = new Id64.Uint32Map(); /** The set of displayed subcategories. Geometry belonging to subcategories not included in this set will not be drawn. */ _visibleSubCategories = new Id64.Uint32Set(); /** Display priorities assigned to subcategories, possibly overridden by display style. Only applicable for plan projection models. */ _subCategoryPriorities = new Id64.Uint32Map(); /** Per-model, a set of subcategories whose visibility should be inverted for elements within that model. * Populated by Viewport. */ _modelSubCategoryOverrides = new Id64.Uint32Map(); /** Ids of animation nodes that should never be drawn. */ neverDrawnAnimationNodes = new Set(); /** Mapping of animation node Ids to overrides applied to the corresponding animation nodes. * @internal */ animationNodeOverrides = new Map(); /** Accepts a criterion that determines whether color and transparency overrides originating from the view's [[RenderSchedule.Script]] should be ignored for a given element. * The function receives a description of the element in question and returns `true` if the script's overrides should be ignored. * Any number of such functions can be registered; if any one of them returns `true`, the script's overrides are not applied to the specified element. * * For example, applications commonly emphasize a set of elements by applying a [[FeatureAppearance.emphasized]] override to them, and specifying a highly-transparent * default appearance to de-emphasize the rest of the elements in the view. If some of the de-emphasized elements' appearances are also being overridden by the schedule script, then * they won't appear de-emphasized, making it difficult for the emphasized elements to stand out. In situations like this, [FeatureOverrideProvider]($frontend)s like [EmphasizeElements]($frontend) can register an [[IgnoreAnimationOverrides]] function that returns true if the element in question is not in the set of emphasized elements. */ ignoreAnimationOverrides(ignore) { this._ignoreAnimationOverrides.push(ignore); } /** Overrides applied to features for which no other overrides are defined */ get defaultOverrides() { return this._defaultOverrides; } /** Whether or not line weights are applied. If false, all lines are drawn with a weight of 1. */ get lineWeights() { return this._lineWeights; } /** A set of elements that are always invisible. * @note If an element is present in both `alwaysDrawn` and [[neverDrawn]], it will not be displayed - `neverDrawn` takes precedence. */ get neverDrawn() { return this._neverDrawn; } /** A set of elements that are unconditionally displayed. * @see [[isAlwaysDrawnExclusive]] to specify that *only* elements in this set will be displayed. * @note If an element is present in both `alwaysDrawn` and [[neverDrawn]], it will not be displayed - `neverDrawn` takes precedence. */ get alwaysDrawn() { return this._alwaysDrawn; } isNeverDrawn(elemIdLo, elemIdHi, animationNodeId) { if (this._neverDrawn.has(elemIdLo, elemIdHi)) return true; else return this.neverDrawnAnimationNodes.has(animationNodeId); } isAlwaysDrawn(idLo, idHi) { return this._alwaysDrawn.has(idLo, idHi); } /** Returns true if the [SubCategory]($backend) specified by Id is in the set of visible subcategories. */ isSubCategoryVisible(idLo, idHi) { return this._visibleSubCategories.has(idLo, idHi); } /** Returns true if the [SubCategory]($backend) specified by Id is in the set of visible subcategories in the context of the specified model. */ isSubCategoryVisibleInModel(subcatLo, subcatHi, modelLo, modelHi) { if (this.ignoreSubCategory) return true; let vis = this.isSubCategoryVisible(subcatLo, subcatHi); const modelOvr = this._modelSubCategoryOverrides.get(modelLo, modelHi); if (undefined !== modelOvr && modelOvr.has(subcatLo, subcatHi)) vis = !vis; return vis; } getModelOverrides(idLo, idHi) { return this._modelOverrides.get(idLo, idHi); } getElementAnimationOverrides(idLo, idHi, animationNodeId) { if (this.animationNodeOverrides.size === 0) return undefined; // NB: An animation node Id of zero means "not animated". Some providers like EmphasizeElements may provide an appearance override for unanimated nodes. // That should be preserved. const app = this.animationNodeOverrides.get(animationNodeId); if (!app || 0 === animationNodeId || this._ignoreAnimationOverrides.length === 0) return app; const args = scratchIgnoreAnimationOverridesArgs; args.elementId.lower = idLo; args.elementId.upper = idHi; args.animationNodeId = animationNodeId; return this._ignoreAnimationOverrides.some((ignore) => ignore(args)) ? undefined : app; } getElementOverrides(idLo, idHi, animationNodeId) { const elemApp = this._elementOverrides.get(idLo, idHi); const nodeApp = this.getElementAnimationOverrides(idLo, idHi, animationNodeId); if (elemApp) return nodeApp ? nodeApp.extendAppearance(elemApp) : elemApp; return nodeApp; } getSubCategoryOverrides(idLo, idHi) { return this._subCategoryOverrides.get(idLo, idHi); } /** Add a [SubCategory]($backend) to the set of visible subcategories. */ setVisibleSubCategory(id) { this._visibleSubCategories.addId(id); } /** Specify the Id of an element that should never be drawn. */ setNeverDrawn(id) { this._neverDrawn.addId(id); } /** Specify the Id of an element that should always be drawn. */ setAlwaysDrawn(id) { this._alwaysDrawn.addId(id); } /** Specify the Id of a animation node that should never be drawn. */ setAnimationNodeNeverDrawn(id) { this.neverDrawnAnimationNodes.add(id); } /** Specify the Ids of elements that should never be drawn. */ setNeverDrawnSet(ids) { copyIdSetToUint32Set(this._neverDrawn, ids); } /** Specify the Ids of elements that should always be drawn. */ setAlwaysDrawnSet(ids, exclusive, ignoreSubCategory = true) { copyIdSetToUint32Set(this._alwaysDrawn, ids); this.isAlwaysDrawnExclusive = exclusive; this.alwaysDrawnIgnoresSubCategory = ignoreSubCategory; } /** Returns the feature's appearance overrides, or undefined if the feature is not visible. */ getFeatureAppearance(feature, modelId, type = BatchType.Primary, animationNodeId = 0) { return this.getAppearance(Id64.getLowerUint32(feature.elementId), Id64.getUpperUint32(feature.elementId), Id64.getLowerUint32(feature.subCategoryId), Id64.getUpperUint32(feature.subCategoryId), feature.geometryClass, Id64.getLowerUint32(modelId), Id64.getUpperUint32(modelId), type, animationNodeId); } static _weight1Appearance = FeatureAppearance.fromJSON({ weight: 1 }); /** Returns a feature's appearance overrides, or undefined if the feature is not visible. * Takes Id64s as pairs of unsigned 32-bit integers for efficiency, because that is how they are stored by the PackedFeatureTable associated with each batch of graphics. * @see [[getFeatureAppearance]] for an equivalent function that accepts [Id64String]($core-bentley)s instead of integer pairs. */ getAppearance(elemLo, elemHi, subcatLo, subcatHi, geomClass, modelLo, modelHi, type, animationNodeId) { if (BatchType.VolumeClassifier === type || BatchType.PlanarClassifier === type) return this.getClassifierAppearance(elemLo, elemHi, subcatLo, subcatHi, modelLo, modelHi, animationNodeId); let app = !this._lineWeights ? FeatureOverrides._weight1Appearance : FeatureAppearance.defaults; const modelApp = this.getModelOverrides(modelLo, modelHi); if (undefined !== modelApp) app = modelApp.extendAppearance(app); // Is the element visible? let elemApp, alwaysDrawn = false; if (Id64.isValidUint32Pair(elemLo, elemHi)) { if (this.isNeverDrawn(elemLo, elemHi, animationNodeId)) return undefined; alwaysDrawn = this.isAlwaysDrawn(elemLo, elemHi); if (!alwaysDrawn && this.isAlwaysDrawnExclusive) return undefined; // Element overrides take precedence elemApp = this.getElementOverrides(elemLo, elemHi, animationNodeId); if (undefined !== elemApp) app = undefined !== modelApp ? elemApp.extendAppearance(app) : elemApp; } let subCatApp; if (!this.ignoreSubCategory && Id64.isValidUint32Pair(subcatLo, subcatHi)) { if ((!alwaysDrawn || !this.alwaysDrawnIgnoresSubCategory) && !this.isSubCategoryVisibleInModel(subcatLo, subcatHi, modelLo, modelHi)) return undefined; subCatApp = this.getSubCategoryOverrides(subcatLo, subcatHi); if (undefined !== subCatApp) app = subCatApp.extendAppearance(app); } // Only apply default if *no* appearance was explicitly registered (doesn't matter if registered appearance does not actually override anything) if (undefined === elemApp && undefined === modelApp && undefined === subCatApp) app = this._defaultOverrides.extendAppearance(app); let visible = alwaysDrawn || this.isClassVisible(geomClass); if (visible && app.isFullyTransparent) visible = false; // don't bother rendering something with full transparency... return visible ? app : undefined; } /** Classifiers behave totally differently...in particular they are never invisible unless fully-transparent. */ getClassifierAppearance(elemLo, elemHi, subcatLo, subcatHi, modelLo, modelHi, animationNodeId) { let app = FeatureAppearance.defaults; const modelApp = this.getModelOverrides(modelLo, modelHi); if (undefined !== modelApp) app = modelApp.extendAppearance(app); const elemApp = this.getElementOverrides(elemLo, elemHi, animationNodeId); if (undefined !== elemApp) app = undefined !== modelApp ? elemApp.extendAppearance(app) : elemApp; if (!this.ignoreSubCategory && Id64.isValidUint32Pair(subcatLo, subcatHi)) { const subCat = this.getSubCategoryOverrides(subcatLo, subcatHi); if (undefined !== subCat) app = subCat.extendAppearance(app); } if (undefined === elemApp && undefined === modelApp) app = this._defaultOverrides.extendAppearance(app); // NB: A fully-transparent classifier means the classifier is a clip mask - classified pixels will be discarded. return app; } /** Return whether geometry of the specified class should be drawn. * @see [[ViewFlags.constructions]], [[ViewFlags.dimensions]], and [[ViewFlags.patterns]]. */ isClassVisible(geomClass) { switch (geomClass) { case GeometryClass.Construction: return this._constructions; case GeometryClass.Dimension: return this._dimensions; case GeometryClass.Pattern: return this._patterns; default: return true; } } /** Specify overrides for all elements belonging to a specified [GeometricModel]($backend), or all geometry belonging to a specified [GeometricElement]($backend) or [SubCategory]($backend). */ override(args) { let id; let map; if (undefined !== args.elementId) { id = args.elementId; map = this._elementOverrides; } else if (undefined !== args.modelId) { id = args.modelId; map = this._modelOverrides; } else { id = args.subCategoryId; map = this._subCategoryOverrides; } let app = args.appearance; const idLo = Id64.getLowerUint32(id); const idHi = Id64.getUpperUint32(id); if (undefined !== args.elementId && this.isNeverDrawn(idLo, idHi, 0)) return; const replace = "replace" === args.onConflict; const existing = replace ? undefined : map.get(idLo, idHi); if (existing) { assert("replace" !== args.onConflict); switch (args.onConflict) { case "skip": return; case "extend": app = app.extendAppearance(existing); break; default: app = existing.extendAppearance(app); break; } } map.set(idLo, idHi, app); } /** Specify overrides for all geometry originating from the specified animation node. * @param id The Id of the animation node. * @param app The symbology overrides. * @note These overrides do not take precedence over element overrides. */ overrideAnimationNode(id, app) { this.animationNodeOverrides.set(id, app); } /** Defines a default appearance to be applied to any [[Feature]] *not* explicitly overridden. * @param appearance The symbology overrides. * @param replaceExisting Specifies whether to replace the current default overrides if they are already defined. */ setDefaultOverrides(appearance, replaceExisting = true) { if (replaceExisting || !appearance.overridesSymbology) this._defaultOverrides = appearance; } /** Get the display priority of a subcategory. This is only relevant when using [[PlanProjectionSettings]]. */ getSubCategoryPriority(idLo, idHi) { return this._subCategoryPriorities.get(idLo, idHi) ?? 0; } /** Adds all fully transparent elements to the _neverDrawn set. This is used for BatchedModels planar masks. */ addInvisibleElementOverridesToNeverDrawn() { this._elementOverrides.forEach((lo, hi) => { const app = this.getElementOverrides(lo, hi, 0); if (app?.isFullyTransparent) this._neverDrawn.add(lo, hi); }); } /** Construct a new Overrides that overrides nothing. * @see [FeatureSymbology.Overrides]($frontend) to construct overrides based on a [ViewState]($frontend) or [Viewport]($frontend). */ constructor() { // } /** Returns true if geometry belonging to the specified subcategory will be drawn. */ isSubCategoryIdVisible(id) { return this.isSubCategoryVisible(Id64.getLowerUint32(id), Id64.getUpperUint32(id)); } /** Returns the overrides applied to geometry belonging to the specified model, if any such are defined. */ getModelOverridesById(id) { return this.getModelOverrides(Id64.getLowerUint32(id), Id64.getUpperUint32(id)); } /** Returns the overrides applied to geometry belonging to the specified element, if any such are defined. */ getElementOverridesById(id) { return this.getElementOverrides(Id64.getLowerUint32(id), Id64.getUpperUint32(id), 0); } /** Returns the overrides applied to geometry belonging to the specified subcategory, if any such are defined. */ getSubCategoryOverridesById(id) { return this.getSubCategoryOverrides(Id64.getLowerUint32(id), Id64.getUpperUint32(id)); } /** Returns true if the specified Feature will be drawn. */ isFeatureVisible(feature) { const { elementId, subCategoryId, geometryClass } = feature; const isValidElemId = !Id64.isInvalid(elementId); const elemIdParts = isValidElemId ? Id64.getUint32Pair(elementId) : undefined; if (undefined !== elemIdParts && this.isNeverDrawn(elemIdParts.lower, elemIdParts.upper, 0)) return false; const alwaysDrawn = undefined !== elemIdParts && this.isAlwaysDrawn(elemIdParts.lower, elemIdParts.upper); if (alwaysDrawn || this.isAlwaysDrawnExclusive) return alwaysDrawn; // NB: This ignores per-model subcategory visibility overrides, because caller did not specify a model. if (!this.isSubCategoryIdVisible(subCategoryId)) return false; return this.isClassVisible(geometryClass); } } /** @public */ export var FeatureAppearanceProvider; (function (FeatureAppearanceProvider) { /** Produce a FeatureAppearanceSource for which `getAppearance()` returns the appearance specified in `source`, potentially modified by `provider`. */ function wrap(source, provider) { return { getAppearance: (elemLo, elemHi, subcatLo, subcatHi, geomClass, modelLo, modelHi, type, animationNodeId) => { return provider.getFeatureAppearance(source, elemLo, elemHi, subcatLo, subcatHi, geomClass, modelLo, modelHi, type, animationNodeId); }, }; } /** Create a provider that obtains each feature's appearance from the source, and if the feature is visible, modifies the appearance. * @param supplementAppearance A function accepting the feature's base appearance and returning a supplemental appearance. * @public */ function supplement(supplementAppearance) { return { getFeatureAppearance: (source, elemLo, elemHi, subcatLo, subcatHi, geomClass, modelLo, modelHi, type, animationNodeId) => { const app = source.getAppearance(elemLo, elemHi, subcatLo, subcatHi, geomClass, modelLo, modelHi, type, animationNodeId); return app ? supplementAppearance(app) : app; }, }; } FeatureAppearanceProvider.supplement = supplement; /** Chain two FeatureAppearanceProviders together such that `first`'s `getFeatureAppearance` function is applied before `second`'s. * If `second` invokes `source.getAppearance()`, the returned appearance will include any modifications applied by `first`. * @public */ function chain(first, second) { if (first === second) return first; return { getFeatureAppearance: (source, elemLo, elemHi, subcatLo, subcatHi, geomClass, modelLo, modelHi, type, animationNodeId) => { return second.getFeatureAppearance(wrap(source, first), elemLo, elemHi, subcatLo, subcatHi, geomClass, modelLo, modelHi, type, animationNodeId); }, }; } FeatureAppearanceProvider.chain = chain; })(FeatureAppearanceProvider || (FeatureAppearanceProvider = {})); //# sourceMappingURL=FeatureSymbology.js.map