UNPKG

@itwin/core-frontend

Version:
563 lines • 27.2 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * 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 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.EmphasizeElements = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); /** An implementation of [[FeatureOverrideProvider]] for emphasizing selected elements through simple color/transparency appearance overrides. * @public * @extensions */ class EmphasizeElements { _defaultAppearance; _unanimatedAppearance; _emphasizeIsolated; _overrideAppearance; _emphasizedAppearance = core_common_1.FeatureAppearance.fromJSON({ emphasized: true }); /** If true, all overridden and emphasized elements will also have the "emphasis" effect applied to them. This causes them to be hilited using the current [[Viewport.emphasisSettings]]. */ wantEmphasis = false; /** Establish active feature overrides to emphasize elements and apply color/transparency overrides. * @see [[Viewport.addFeatureOverrideProvider]] */ addFeatureOverrides(overrides, vp) { const emphasizedElements = this.getEmphasizedElements(vp); if (undefined !== emphasizedElements) { overrides.setDefaultOverrides(this._defaultAppearance); const appearance = this.wantEmphasis ? this._emphasizedAppearance : core_common_1.FeatureAppearance.defaults; // Avoid creating a new object per-element inside the loop const args = { elementId: "", appearance }; for (const elementId of emphasizedElements) { args.elementId = elementId; overrides.override(args); } // Do not apply animation overrides to de-emphasized elements. overrides.ignoreAnimationOverrides((args) => { const id = core_bentley_1.Id64.fromUint32Pair(args.elementId.lower, args.elementId.upper); return !emphasizedElements.has(id); }); } const overriddenElements = this.getOverriddenElements(); if (undefined !== overriddenElements) { if (undefined !== this._defaultAppearance) overrides.setDefaultOverrides(this._defaultAppearance); // Avoid creating a new object per-element inside the loop const args = { elementId: "", appearance: core_common_1.FeatureAppearance.defaults }; for (const [key, ids] of overriddenElements) { args.appearance = this.createAppearanceFromKey(key); for (const elementId of ids) { args.elementId = elementId; overrides.override(args); } } } if (this._unanimatedAppearance) { if (this._unanimatedAppearance.isFullyTransparent) overrides.neverDrawnAnimationNodes.add(0); else overrides.animationNodeOverrides.set(0, this._unanimatedAppearance); } } /** @internal */ createAppearanceFromKey(key) { let transparency; let rgb; if (key < 0) { transparency = Math.abs(key); } else { const color = core_common_1.ColorDef.fromJSON(key); rgb = core_common_1.RgbColor.fromColorDef(color); if (0 !== color.getAlpha()) // Fully transparent signifies to use color only... transparency = color.getTransparency() / 255; } const emphasized = this.wantEmphasis ? true : undefined; return core_common_1.FeatureAppearance.fromJSON({ rgb, transparency, emphasized }); } /** Get override key from color and override type */ createOverrideKey(color, override) { const colorValues = color.colors; switch (override) { case core_common_1.FeatureOverrideType.ColorAndAlpha: return 255 === colorValues.t ? undefined : color.tbgr; // Hiding elements should be done using neverDrawn, not transparency... case core_common_1.FeatureOverrideType.ColorOnly: return core_common_1.ColorDef.from(colorValues.r, colorValues.g, colorValues.b, 255).tbgr; case core_common_1.FeatureOverrideType.AlphaOnly: return -(colorValues.t / 255); } } /** Get color and override type for the given key. */ getOverrideFromKey(key) { let overrideType; let color; if (key < 0) { color = core_common_1.ColorDef.from(0, 0, 0, 255 * Math.abs(key)); overrideType = core_common_1.FeatureOverrideType.AlphaOnly; } else { color = core_common_1.ColorDef.fromJSON(key); if (0 === color.getAlpha()) { color = color.withAlpha(255); overrideType = core_common_1.FeatureOverrideType.ColorOnly; } else { overrideType = core_common_1.FeatureOverrideType.ColorAndAlpha; } } return { overrideType, color }; } /** Establish a default appearance to apply to elements without overrides. If changing the default appearance * without also calling overrideElements, an explicit refresh must be requested for the change to take effect. * @see [[Viewport.setFeatureOverrideProviderChanged]] */ get defaultAppearance() { return this._defaultAppearance; } set defaultAppearance(appearance) { this._defaultAppearance = appearance; } /** Establish a default appearance to apply to elements that are not animated by the view's [RenderSchedule.Script]($common). * @note If this is the only change made to EmphasizeElements, you must call [[Viewport.setFeatureOverrideProviderChanged]] for * the change to take immediate effect. * @see [[createDefaultAppearance]] to create an appearance suitable for de-emphasizing the non-animated elements. */ get unanimatedAppearance() { return this._unanimatedAppearance; } set unanimatedAppearance(appearance) { this._unanimatedAppearance = appearance; } /** Create default appearance to use for emphasizeElements when not supplied by caller. */ createDefaultAppearance() { return core_common_1.FeatureAppearance.fromJSON({ rgb: new core_common_1.RgbColor(0xe4, 0xe4, 0xe4), transparency: 0.8, nonLocatable: true, }); } /** Get the IDs of the currently never drawn elements. */ getNeverDrawnElements(vp) { return (undefined !== vp.neverDrawn && 0 !== vp.neverDrawn.size ? vp.neverDrawn : undefined); } /** Get the IDs of the currently always drawn elements. */ getAlwaysDrawnElements(vp) { return (undefined !== vp.alwaysDrawn && 0 !== vp.alwaysDrawn.size ? vp.alwaysDrawn : undefined); } /** Get the IDs of the currently hidden elements. */ getHiddenElements(vp) { return this.getNeverDrawnElements(vp); } /** Get the IDs of the currently isolated elements. */ getIsolatedElements(vp) { return (vp.isAlwaysDrawnExclusive ? this.getAlwaysDrawnElements(vp) : undefined); } /** Get the IDs of the currently emphasized isolated elements. */ getEmphasizedIsolatedElements() { return (undefined !== this._defaultAppearance && undefined !== this._emphasizeIsolated && 0 !== this._emphasizeIsolated.size ? this._emphasizeIsolated : undefined); } /** Get the IDs of the currently emphasized elements. */ getEmphasizedElements(vp) { return (undefined !== this.getEmphasizedIsolatedElements() ? this._emphasizeIsolated : (undefined !== this._defaultAppearance && !vp.isAlwaysDrawnExclusive ? this.getAlwaysDrawnElements(vp) : undefined)); } /** Get the map of current elements with color/transparency overrides. */ getOverriddenElements() { return (undefined !== this._overrideAppearance && 0 !== this._overrideAppearance.size ? this._overrideAppearance : undefined); } /** Get the IDs of current elements with the specified color/transparency override. */ getOverriddenElementsByKey(key) { return (undefined !== this._overrideAppearance ? this._overrideAppearance.get(key) : undefined); } /** Clear never drawn elements. * @return false if nothing to clear. */ clearNeverDrawnElements(vp) { if (undefined === this.getNeverDrawnElements(vp)) return false; vp.clearNeverDrawn(); return true; } /** Clear always drawn elements. * @return false if nothing to clear. */ clearAlwaysDrawnElements(vp) { if (undefined === this.getAlwaysDrawnElements(vp)) return false; vp.clearAlwaysDrawn(); return true; } /** Clear hidden elements. * @return false if nothing to clear. */ clearHiddenElements(vp) { return this.clearNeverDrawnElements(vp); } /** Clear isolated elements. * @return false if nothing to clear. */ clearIsolatedElements(vp) { if (undefined === this.getIsolatedElements(vp)) return false; if (this.clearEmphasizedIsolatedElements(vp, true)) return true; return this.clearAlwaysDrawnElements(vp); } /** Clear emphasized elements. * @return false if nothing to clear. */ clearEmphasizedElements(vp) { if (undefined === this.getEmphasizedElements(vp)) return false; if (this.clearEmphasizedIsolatedElements(vp, false)) return true; if (!this.clearAlwaysDrawnElements(vp)) return false; this._defaultAppearance = undefined; return true; } /** Clear emphasized isolated elements. * @return false if nothing to clear. */ clearEmphasizedIsolatedElements(vp, setToAlwaysDrawn) { const emphasizedIsolated = this.getEmphasizedIsolatedElements(); this._emphasizeIsolated = undefined; // Always clear in case default appearance was unset... if (undefined === emphasizedIsolated) return false; if (setToAlwaysDrawn && this.setAlwaysDrawnElements(emphasizedIsolated, vp, false)) return true; this._defaultAppearance = undefined; vp.setFeatureOverrideProviderChanged(); return true; } /** Clear color/transparency overrides from elements. Removes all overrides when keyOrIds isn't supplied. * @param keyOrIds Specify a key value from [[EmphasizeElements.getOverriddenElements]] or [[EmphasizeElements.createOverrideKey]] * to remove a single color/transparency override for the corresponding elements or specify the IDs of elements to * remove any color/transparency override from. * @return false if nothing to clear. */ clearOverriddenElements(vp, keyOrIds) { if (undefined === this._overrideAppearance) return false; if (undefined !== keyOrIds) { if (typeof keyOrIds === "number") { if (!this._overrideAppearance.delete(keyOrIds)) return false; } else { let changed = false; for (const [otherKey, otherIds] of this._overrideAppearance) { const oldSize = otherIds.size; for (const id of core_bentley_1.Id64.iterable(keyOrIds)) otherIds.delete(id); if (oldSize !== otherIds.size) changed = true; if (0 === otherIds.size) this._overrideAppearance.delete(otherKey); } if (!changed) return false; } } else { this._overrideAppearance = undefined; } vp.setFeatureOverrideProviderChanged(); return true; } /** @internal */ updateIdSet(ids, replace, existingIds) { const newIds = new Set(); for (const id of core_bentley_1.Id64.iterable(ids)) newIds.add(id); if (0 === newIds.size) return undefined; const oldSize = (!replace && undefined !== existingIds ? existingIds.size : 0); if (0 !== oldSize && undefined !== existingIds) for (const id of existingIds) newIds.add(id); if (oldSize === newIds.size) return undefined; return newIds; } /** Set the element IDs to be never drawn. * @param ids The IDs of the elements to never draw. * @param vp The viewport. * @param replace true to replace currently hidden elements (if any) or false to add to the existing set. * @return true if overrides were changed. * @see [[Viewport.neverDrawn]] * @internal */ setNeverDrawnElements(ids, vp, replace = true) { const hiddenIds = this.updateIdSet(ids, replace, vp.neverDrawn); if (undefined === hiddenIds) return false; vp.setNeverDrawn(hiddenIds); return true; } /** Set the element IDs to be always drawn. * @param ids The IDs of the elements to always draw. * @param vp The viewport. * @param exclusive If true, *only* the specified elements will be drawn. * @param replace true to replace currently always drawn elements (if any) or false to add to the existing set. * @return true if overrides were changed. * @see [[Viewport.alwaysDrawn]] * @see [[Viewport.isAlwaysDrawnExclusive]] * @internal */ setAlwaysDrawnElements(ids, vp, exclusive = true, replace = true) { const visibleIds = this.updateIdSet(ids, replace, vp.alwaysDrawn); if (undefined === visibleIds) return false; vp.setAlwaysDrawn(visibleIds, exclusive); return true; } /** Set the element IDs to be never drawn. * @param ids The IDs of the elements to never draw. * @param vp The viewport. * @param replace true to replace currently hidden elements (if any) or false to add to the existing set. * @return true if overrides were changed. * @see [[Viewport.neverDrawn]] */ hideElements(ids, vp, replace = false) { return this.setNeverDrawnElements(ids, vp, replace); } /** Set the currently selected elements to be never drawn. * @param vp The viewport. * @param replace true to replace currently hidden elements (if any) or false to add to the existing set. * @param clearSelection true to clear current selection after setting appearance override, false to leave selected. * @return true if overrides were changed. * @see [[Viewport.neverDrawn]] */ hideSelectedElements(vp, replace = false, clearSelection = true) { const selection = vp.view.iModel.selectionSet; if (!selection.isActive || !this.hideElements(selection.elements, vp, replace)) return false; if (clearSelection) selection.emptyAll(); return true; } /** Set the element IDs to be always drawn exclusively. * @param ids The IDs of the elements to always draw. * @param vp The viewport. * @param replace true to replace currently isolated elements (if any) or false to add to the existing set. * @return true if overrides were changed. * @see [[Viewport.alwaysDrawn]] * @see [[Viewport.isAlwaysDrawnExclusive]] */ isolateElements(ids, vp, replace = true) { const wasEmphasized = (undefined !== this.getEmphasizedElements(vp)); if (!this.setAlwaysDrawnElements(ids, vp, true, replace)) return false; if (wasEmphasized) this._defaultAppearance = this._emphasizeIsolated = undefined; // Don't clear defaultAppearance unless it was established by emphasize... return true; } /** Set the currently selected elements to be always drawn exclusively. * @param vp The viewport. * @param replace true to replace currently isolated elements (if any) or false to add to the existing set. * @param clearSelection true to clear current selection after setting appearance override, false to leave selected. * @return true if overrides were changed. * @see [[Viewport.alwaysDrawn]] * @see [[Viewport.isAlwaysDrawnExclusive]] */ isolateSelectedElements(vp, replace = true, clearSelection = true) { const selection = vp.view.iModel.selectionSet; if (!selection.isActive || !this.isolateElements(selection.elements, vp, replace)) return false; if (clearSelection) selection.emptyAll(); return true; } /** Set the element IDs to be always drawn normally with all other elements in the view overridden to draw using a default appearance. * @param ids The IDs of the elements to always draw. * @param vp The viewport. * @param defaultAppearance Optional default appearance, uses non-locatable transparent grey if not specified. * @param replace true to replace currently overridden elements (if any) or false to add to the existing set. * @return true if overrides were changed. * @see [[Viewport.alwaysDrawn]] * @see [[Viewport.isAlwaysDrawnExclusive]] */ emphasizeElements(ids, vp, defaultAppearance, replace = true) { if (undefined !== this.getIsolatedElements(vp)) { const emphasizeIds = this.updateIdSet(ids, replace, this._emphasizeIsolated); if (undefined === emphasizeIds) return false; this._emphasizeIsolated = emphasizeIds; vp.setFeatureOverrideProviderChanged(); } else { if (!this.setAlwaysDrawnElements(ids, vp, false, replace)) return false; this._emphasizeIsolated = undefined; } this._defaultAppearance = (undefined === defaultAppearance ? this.createDefaultAppearance() : defaultAppearance); return true; } /** Set the currently selected elements to be always drawn normally with all other elements in the view overridden to draw using a default appearance. * @param vp The viewport. * @param defaultAppearance Optional default appearance, uses transparent grey if not specified. * @param replace true to replace currently overridden elements (if any) or false to add to the existing set. * @param clearSelection true to clear current selection after setting appearance override, false to leave selected. * @return true if overrides were changed. * @see [[Viewport.alwaysDrawn]] * @see [[Viewport.isAlwaysDrawnExclusive]] */ emphasizeSelectedElements(vp, defaultAppearance, replace = true, clearSelection = true) { const selection = vp.view.iModel.selectionSet; if (!selection.isActive || !this.emphasizeElements(selection.elements, vp, defaultAppearance, replace)) return false; if (clearSelection) selection.emptyAll(); return true; } /** Set the element IDs to display with a color/transparency override. * @param ids The IDs of the elements. * @param vp The viewport. * @param color ColorDef to specify override rgb and alpha. * @param override Whether to use color and alpha, only color, or only alpha from the supplied ColorDef. * @param replace true to replace currently overridden elements (if any) or false to add to the existing set. * @return true if overrides were changed. * @see [[Viewport.addFeatureOverrideProvider]] */ overrideElements(ids, vp, color, override = core_common_1.FeatureOverrideType.ColorOnly, replace = false) { const ovrKey = this.createOverrideKey(color, override); if (undefined === ovrKey) return false; const overrideIds = new Set(); for (const id of core_bentley_1.Id64.iterable(ids)) overrideIds.add(id); if (0 === overrideIds.size) return false; const existingIds = (!replace ? this.getOverriddenElementsByKey(ovrKey) : undefined); const oldSize = (undefined !== existingIds ? existingIds.size : 0); if (0 !== oldSize && undefined !== existingIds) for (const id of existingIds) overrideIds.add(id); if (oldSize === overrideIds.size) return false; if (undefined === this._overrideAppearance) { this._overrideAppearance = new Map(); } else { for (const [key, otherIds] of this._overrideAppearance) { if (key === ovrKey) // Make sure these ids are unique to this color/transparency key... continue; for (const id of core_bentley_1.Id64.iterable(ids)) otherIds.delete(id); if (0 !== otherIds.size) continue; this._overrideAppearance.delete(key); } } this._overrideAppearance.set(ovrKey, overrideIds); vp.setFeatureOverrideProviderChanged(); return true; } /** Set the currently selected elements to display with a color/transparency override. * @param vp The viewport. * @param color ColorDef to specify override rgb and alpha. * @param override Whether to use color and alpha, only color, or only alpha from the supplied ColorDef. * @param replace true to replace currently overridden elements (if any) or false to add to the existing set. * @param clearSelection true to clear current selection after setting appearance override, false to leave selected. * @return true if overrides were changed. * @see [[Viewport.addFeatureOverrideProvider]] */ overrideSelectedElements(vp, color, override = core_common_1.FeatureOverrideType.ColorOnly, replace = false, clearSelection = true) { const selection = vp.view.iModel.selectionSet; if (!selection.isActive || !this.overrideElements(selection.elements, vp, color, override, replace)) return false; if (clearSelection) selection.emptyAll(); return true; } /** @return true if provider is currently overriding the display of any elements. */ isActive(vp) { return (undefined !== this.getNeverDrawnElements(vp) || undefined !== this.getAlwaysDrawnElements(vp) || undefined !== this.getOverriddenElements()); } /** Serialize to JSON representation. * @see [[EmphasizeElements.fromJSON]] */ toJSON(vp) { const props = {}; const neverDrawn = this.getNeverDrawnElements(vp); if (undefined !== neverDrawn) props.neverDrawn = [...neverDrawn]; const alwaysDrawn = this.getAlwaysDrawnElements(vp); if (undefined !== alwaysDrawn) props.alwaysDrawn = [...alwaysDrawn]; if (vp.isAlwaysDrawnExclusive) props.isAlwaysDrawnExclusive = true; // isolate const alwaysDrawnExclusiveEmphasized = this.getEmphasizedIsolatedElements(); if (undefined !== alwaysDrawnExclusiveEmphasized) props.alwaysDrawnExclusiveEmphasized = [...alwaysDrawnExclusiveEmphasized]; if (undefined !== this.defaultAppearance) props.defaultAppearance = this.defaultAppearance; // emphasize (or specifically set for override) if (this.unanimatedAppearance) props.unanimatedAppearance = this.unanimatedAppearance; const overriddenElements = this.getOverriddenElements(); if (undefined !== overriddenElements) { const appearanceOverride = []; for (const [key, ovrIds] of overriddenElements) { const { color, overrideType } = { ...this.getOverrideFromKey(key) }; const ids = [...ovrIds]; appearanceOverride.push({ overrideType, color: color.toJSON(), ids }); } props.appearanceOverride = appearanceOverride; } if (this.wantEmphasis) props.wantEmphasis = true; return props; } /** Initialize from JSON representation. * @see [[EmphasizeElements.toJSON]] */ fromJSON(props, vp) { let changed = false; if (undefined !== props.neverDrawn && this.setNeverDrawnElements(new Set(props.neverDrawn), vp, true)) changed = true; if (undefined !== props.alwaysDrawn && this.setAlwaysDrawnElements(new Set(props.alwaysDrawn), vp, undefined !== props.isAlwaysDrawnExclusive && props.isAlwaysDrawnExclusive)) changed = true; if (undefined !== props.alwaysDrawnExclusiveEmphasized) this._emphasizeIsolated = new Set(props.alwaysDrawnExclusiveEmphasized); // changed status determined by setAlwaysDrawnElements... if (undefined !== props.defaultAppearance) this.defaultAppearance = core_common_1.FeatureAppearance.fromJSON(props.defaultAppearance); // changed status determined by setAlwaysDrawnElements or overrideElements... if (props.unanimatedAppearance) this.unanimatedAppearance = core_common_1.FeatureAppearance.fromJSON(props.unanimatedAppearance); if (undefined !== props.appearanceOverride) { for (const ovrApp of props.appearanceOverride) { if (undefined === ovrApp.ids) continue; if (this.overrideElements(new Set(ovrApp.ids), vp, core_common_1.ColorDef.fromJSON(ovrApp.color), ovrApp.overrideType, true)) changed = true; } } const wantEmphasis = true === props.wantEmphasis; if (wantEmphasis !== this.wantEmphasis) { this.wantEmphasis = wantEmphasis; changed = true; } return changed; } /** Return the EmphasizeElements provider currently registered with the specified Viewport, if one is already registered. */ static get(vp) { return vp.findFeatureOverrideProviderOfType(EmphasizeElements); } /** Return the EmphasizeElements provider currently registered with the specified Viewport, or register a new one and return it. */ static getOrCreate(vp) { let provider = this.get(vp); if (!provider) { provider = new EmphasizeElements(); vp.addFeatureOverrideProvider(provider); } return provider; } /** Drop the EmphasizeElements provider currently registered with the specified Viewport, if any is registered. */ static clear(vp, inactiveOnly = false) { const provider = this.get(vp); if (undefined === provider || (inactiveOnly && provider.isActive(vp))) return; vp.clearNeverDrawn(); vp.clearAlwaysDrawn(); vp.dropFeatureOverrideProvider(provider); } } exports.EmphasizeElements = EmphasizeElements; //# sourceMappingURL=EmphasizeElements.js.map