@itwin/core-frontend
Version:
iTwin.js frontend components
314 lines • 20.2 kB
TypeScript
/** @packageDocumentation
* @module Tools
*/
import { CompressedId64Set, Id64Arg, Id64Array, Id64String, OrderedId64Array } from "@itwin/core-bentley";
import { Point3d, XAndY } from "@itwin/core-geometry";
import { LocateFilterStatus, LocateResponse } from "../ElementLocateManager";
import { HitDetail } from "../HitDetail";
import { IModelConnection } from "../IModelConnection";
import { SelectionSet } from "../SelectionSet";
import { DecorateContext } from "../ViewContext";
import { Viewport } from "../Viewport";
import { PrimitiveTool } from "./PrimitiveTool";
import { SelectionMethod } from "./SelectTool";
import { BeButtonEvent, BeModifierKeys, EventHandled } from "./Tool";
import { ToolAssistanceInstruction } from "./ToolAssistance";
/** Identifies the source of the elements in the agenda.
* @public
*/
export declare enum ModifyElementSource {
/** The source for the element is unknown - not caused by a modification command. */
Unknown = 0,
/** The element is selected by the user. */
Selected = 1,
/** The element is processed because it is in the selection set. */
SelectionSet = 2,// eslint-disable-line @typescript-eslint/no-shadow
/** The element is selected by the user using drag box or crossing line selection. */
DragSelect = 3
}
/** Identifies groups of elements added to agenda along with the source.
* @public
*/
export interface GroupMark {
start: number;
source: ModifyElementSource;
}
/** The ElementAgenda class is used by [[ElementSetTool]] to hold the collection of elements it will operate on
* and to manage their hilite state.
* @see [[ElementSetTool]]
* @public
*/
export declare class ElementAgenda {
iModel: IModelConnection;
/** The IDs of the elements in this agenda.
* @note Prefer methods like [[ElementAgenda.add]] instead of modifying directly.
*/
readonly elements: Id64Array;
/** The group source identifiers for the elements in this agenda.
* @note Prefer methods like [[ElementAgenda.add]] instead of modifying directly.
*/
readonly groupMarks: GroupMark[];
manageHiliteState: boolean;
constructor(iModel: IModelConnection);
/** Get the source for the last group added to this agenda, if applicable. The "source" is merely an indication of what the collection of elements represents. */
getSource(): ModifyElementSource;
/** Set the source for the last group added to this agenda. */
setSource(val: ModifyElementSource): void;
get isEmpty(): boolean;
get count(): number;
get length(): number;
/** Create [[OrderedId64Array]] from agenda. */
orderIds(): OrderedId64Array;
/** Create [[CompressedId64Set]] from agenda. */
compressIds(): CompressedId64Set;
/** Empties the agenda and clears hilite state when manageHiliteState is true. */
clear(): void;
private setEntriesHiliteState;
/** Removes the last group of elements added to this agenda. */
popGroup(): void;
/** Return true if elementId is already in this agenda. */
has(id: string): boolean;
/** Return true if elementId is already in this agenda. */
find(id: Id64String): boolean;
/** Add elements to this agenda. */
add(arg: Id64Arg): boolean;
private removeOne;
remove(arg: Id64Arg): boolean;
/** Add elements not currently in the agenda and remove elements currently in the agenda. */
invert(arg: Id64Arg): boolean;
}
/** The ElementSetTool class is a specialization of [[PrimitiveTool]] designed to unify operations on sets of elements.
* Use to query or modify existing elements as well as to create new elements from existing elements.
* Basic tool sequence:
* - Populate [[ElementSetTool.agenda]] with the element ids to query or modify.
* - Gather any additional input and if requested, enable dynamics to preview result.
* - Call [[ElementSetTool.processAgenda]] to apply operation to [[ElementSetTool.agenda]].
* - Call [[ElementSetTool.onProcessComplete]] to restart or exit.
* Common element sources:
* - Pre-selected elements from an active [[SelectionSet]].
* - Clicking in a view to identify elements using [[ElementLocateManager]].
* - Drag box and crossing line selection.
* Default behavior:
* - Identify a single element with left-click.
* - Immediately apply operation.
* - Restart.
* Sub-classes are required to opt-in to additional element sources, dynamics, AccuSnap, additional input, etc.
* @public
*/
export declare abstract class ElementSetTool extends PrimitiveTool {
private _agenda?;
private _useSelectionSet;
private _processDataButtonUp;
/** The accept point for a selection set, drag select, or final located element. */
protected anchorPoint?: Point3d;
/** The button down location that initiated box or crossing line selection. */
protected dragStartPoint?: Point3d;
/** Get the [[ElementAgenda]] the tool will operate on. */
protected get agenda(): ElementAgenda;
/** Convenience method to get current count from [[ElementSetTool.agenda]]. */
protected get currentElementCount(): number;
/** Minimum required number of elements for tool to be able to complete.
* @return number to compare with [[ElementSetTool.currentElementCount]] to determine if more elements remain to be identified.
* @note A tool to subtract elements is an example where returning 2 would be necessary.
*/
protected get requiredElementCount(): number;
/** Whether to allow element identification by drag box or crossing line selection.
* @return true to allow drag select as an element source when the ctrl key is down.
* @note Use ctrl+left drag for box selection. Inside/overlap is based on left/right direction (shift key inverts).
* @note Use ctrl+right drag for crossing line selection.
*/
protected get allowDragSelect(): boolean;
/** Support operations on groups/assemblies independent of selection scope.
* @return true to add or remove all members of an assembly from [[ElementSetTool.agenda]] when any single member is identified.
* @note Applies to [[ElementSetTool.getLocateCandidates]] only.
*/
protected get allowGroups(): boolean;
/** Whether [[ElementSetTool.agenda]] should be populated from an active selection set.
* @return true to allow selection sets as an element source.
* @note A selection set must have at least [[ElementSetTool.requiredElementCount]] elements to be considered.
*/
protected get allowSelectionSet(): boolean;
/** Whether to clear the active selection set for tools that return false for [[ElementSetTool.allowSelectionSet]].
* @return true to clear unsupported selection sets (desired default behavior).
* @note It is expected that the selection set be cleared before using [[ElementLocateManager]] to identify elements.
* This allows the element hilite to be a visual representation of the [[ElementSetTool.agenda]] contents.
*/
protected get clearSelectionSet(): boolean;
/** Whether a selection set should be processed immediately upon installation or require a data button to accept.
* @return false only for tools without settings or a need for confirmation.
* @note A tool to delete elements is an example where returning false could be desirable.
*/
protected get requireAcceptForSelectionSetOperation(): boolean;
/** Whether to begin dynamics for a selection set immediately or wait for a data button.
* @return false for tools that can start showing dynamics without any additional input.
* @note A tool to rotate elements by an active angle setting is an example where returning false could be desirable.
*/
protected get requireAcceptForSelectionSetDynamics(): boolean;
/** Whether original source of elements being modified was the active selection set.
* @return true when [[ElementSetTool.allowSelectionSet]] and active selection set count >= [[ElementSetTool.requiredElementCount]].
*/
protected get isSelectionSetModify(): boolean;
/** Whether drag box or crossing line selection is currently active.
* @return true when [[ElementSetTool.allowDragSelect]] and corner points are currently being defined.
*/
protected get isSelectByPoints(): boolean;
/** Whether to continue selection of additional elements by holding the ctrl key down.
* @return true to continue the element identification phase beyond [[ElementSetTool.requiredElementCount]] by holding down the ctrl key.
*/
protected get controlKeyContinuesSelection(): boolean;
/** Whether to invert selection of elements identified with the ctrl key held down.
* @return true to allow ctrl to deselect already selected elements.
*/
protected get controlKeyInvertsSelection(): boolean;
/** Whether [[ElementSetTool.setupAndPromptForNextAction]] should call [[AccuSnap.enableSnap]] for current tool phase.
* @return true to enable snapping to elements.
* @note A tool that just needs to identify elements and doesn't care about location should not enable snapping.
*/
protected get wantAccuSnap(): boolean;
/** Whether to automatically start element dynamics after all required elements have been identified.
* @return true if tool will implement [[InteractiveTool.onDynamicFrame]] to show element dynamics.
*/
protected get wantDynamics(): boolean;
/** Whether tool is done identifying elements and is ready to move to the next phase.
* @return true when [[ElementSetTool.requiredElementCount]] is not yet satisfied or ctrl key is being used to extend selection.
*/
protected get wantAdditionalElements(): boolean;
/** Whether the tool has gathered enough input to call [[ElementSetTool.processAgenda]].
* Sub-classes should override to check for additional point input they collected in [[ElementSetTool.wantProcessAgenda]].
* @return true if tool does not yet have enough information to complete.
* @note When [[ElementSetTool.wantDynamics]] is true an additional point is automatically required to support the dynamic preview.
*/
protected get wantAdditionalInput(): boolean;
/** Whether the tool is ready for [[ElementSetTool.processAgenda]] to be called to complete the tool operation.
* Sub-classes should override to collect additional point input before calling super or [[ElementSetTool.wantAdditionalInput]].
* @return true if tool has enough information and is ready to complete.
*/
protected wantProcessAgenda(_ev: BeButtonEvent): boolean;
/** Whether tool should operate on an existing selection set or instead prompt user to identity elements.
* Unsupported selection sets will be cleared when [[ElementSetTool.clearSelectionSet]] is true.
*/
protected setPreferredElementSource(): void;
/** Get element ids to process from the active selection set.
* Sub-classes may override to support selection scopes or apply tool specific filtering.
*/
protected getSelectionSetCandidates(ss: SelectionSet): Promise<Id64Arg>;
/** Populate [[ElementSetTool.agenda]] from a [[SelectionSet]].
* @see [[ElementSetTool.getSelectionSetCandidates]] to filter or augment the set of elements.
*/
protected buildSelectionSetAgenda(ss: SelectionSet): Promise<boolean>;
/** If the supplied element is part of an assembly, return all member ids. */
protected getGroupIds(id: Id64String): Promise<Id64Arg>;
/** Get element id(s) to process from a [[HitDetail]] already accepted by [[ElementSetTool.isElementValidForOperation]].
* Sub-classes may override to support selection scopes.
*/
protected getLocateCandidates(hit: HitDetail): Promise<Id64Arg>;
/** Populate [[ElementSetTool.agenda]] from a [[HitDetail]].
* @see [[ElementSetTool.getLocateCandidates]] to add additional elements.
*/
protected buildLocateAgenda(hit: HitDetail): Promise<boolean>;
/** Get ids of spatial elements to process from a clip volume created by drag box selection. */
private static getVolumeSelectionCandidates;
/** Get ids of visible elements to process from drag box or crossing line selection. */
private static getAreaSelectionCandidates;
/** Get ids of elements to process from drag box or crossing line selection using either the depth buffer or clip vector...
* @internal
*/
static getAreaOrVolumeSelectionCandidates(vp: Viewport, origin: XAndY, corner: XAndY, method: SelectionMethod, allowOverlaps: boolean, filter?: (id: Id64String) => boolean, includeDecorationsForVolume?: boolean): Promise<Set<Id64String>>;
/** Get element ids to process from drag box or crossing line selection.
* Sub-classes may override to support selection scopes or apply tool specific filtering.
*/
protected getDragSelectCandidates(vp: Viewport, origin: Point3d, corner: Point3d, method: SelectionMethod, overlap: boolean): Promise<Id64Arg>;
/** Populate [[ElementSetTool.agenda]] by drag box or crossing line information.
* @see [[ElementSetTool.getDragSelectCandidates]] to filter or augment the set of elements.
*/
protected buildDragSelectAgenda(vp: Viewport, origin: Point3d, corner: Point3d, method: SelectionMethod, overlap: boolean): Promise<boolean>;
/** Quick id validity check. Sub-classes that wish to allow pickable decorations from selection sets can override. */
protected isElementIdValid(id: Id64String, source: ModifyElementSource): boolean;
/** Sub-classes should override to apply tool specific filtering and to provide an explanation for rejection. */
protected isElementValidForOperation(hit: HitDetail, _out?: LocateResponse): Promise<boolean>;
/** Called from [[ElementSetTool.doLocate]] as well as auto-locate to accept or reject elements under the cursor. */
filterHit(hit: HitDetail, out?: LocateResponse): Promise<LocateFilterStatus>;
/** Identify an element and update the element agenda.
* @param newSearch true to locate new elements, false to cycle between elements within locate tolerance from a previous locate.
* @return true if [[ElementSetTool.agenda]] was changed.
*/
protected doLocate(ev: BeButtonEvent, newSearch: boolean): Promise<boolean>;
/** Whether drag box selection only identifies elements that are wholly inside or also allows those that overlap
* the selection rectangle.
* @note Inside/overlap is based on left/right direction of corner points (shift key inverts check).
*/
protected useOverlapSelection(ev: BeButtonEvent): boolean;
/** Initiate tool state for start of drag selection. */
protected selectByPointsStart(ev: BeButtonEvent): Promise<boolean>;
/** Finish drag selection and update [[ElementSetTool.agenda]] with any elements that may have been identified. */
protected selectByPointsEnd(ev: BeButtonEvent): Promise<boolean>;
/** Display drag box and crossing line selection graphics. */
protected selectByPointsDecorate(context: DecorateContext): void;
/** Show graphics for when drag selection is active. */
decorate(context: DecorateContext): void;
/** Make sure drag selection graphics are updated when mouse moves. */
onMouseMotion(ev: BeButtonEvent): Promise<void>;
/** Support initiating drag selection on mouse start drag event when [[ElementSetTool.allowDragSelect]] is true. */
onMouseStartDrag(ev: BeButtonEvent): Promise<EventHandled>;
/** Support completing active drag selection on mouse end drag event and update [[ElementSetTool.agenda]]. */
onMouseEndDrag(ev: BeButtonEvent): Promise<EventHandled>;
/** Update prompts, cursor, graphics, etc. as appropriate on ctrl and shift key transitions. */
onModifierKeyTransition(_wentDown: boolean, modifier: BeModifierKeys, _event: KeyboardEvent): Promise<EventHandled>;
/** Allow reset to cycle between elements identified for overlapping the locate circle.
* Advances to next pre-located hit from [[AccuSnap.aSnapHits]] or changes last accepted hit to next hit from [[ElementLocateManger.hitList]].
* @returns EventHandled.Yes if onReinitialize was called to restart or exit tool.
*/
protected chooseNextHit(ev: BeButtonEvent): Promise<EventHandled>;
/** Orchestrates updating the internal state of the tool on a reset button event.
* @returns EventHandled.Yes if onReinitialize was called to restart or exit tool.
*/
protected processResetButton(ev: BeButtonEvent): Promise<EventHandled>;
onResetButtonUp(ev: BeButtonEvent): Promise<EventHandled>;
onResetButtonDown(ev: BeButtonEvent): Promise<EventHandled>;
/** Collect element input until tool has a sufficient number to complete. */
protected gatherElements(ev: BeButtonEvent): Promise<EventHandled | undefined>;
/** Collect point input until tool has a sufficient number to complete. */
protected gatherInput(ev: BeButtonEvent): Promise<EventHandled | undefined>;
/** Orchestrates advancing the internal state of the tool on a data button event.
* - Collect elements: Add to the element agenda until no additional elements are requested.
* - Gather input: Initiates element dynamics and accepts additional points as required.
* - Complete operation: Process agenda entries, restart or exit tool.
* @returns EventHandled.Yes if onReinitialize was called to restart or exit tool.
*/
protected processDataButton(ev: BeButtonEvent): Promise<EventHandled>;
onDataButtonUp(ev: BeButtonEvent): Promise<EventHandled>;
onDataButtonDown(ev: BeButtonEvent): Promise<EventHandled>;
protected initAgendaDynamics(): Promise<boolean>;
/** Sub-classes can override to be notified of [[ElementSetTool.agenda]] changes by other methods.
* @note Tools should not modify [[ElementSetTool.agenda]] in this method, it should merely serve as a convenient place
* to update information, such as element graphics once dynamics has started, ex. [[ElementSetTool.chooseNextHit]].
*/
protected onAgendaModified(): Promise<void>;
/** Sub-classes can override to continue with current [[ElementSetTool.agenda]] or restart after processing has completed. */
protected onProcessComplete(): Promise<void>;
/** Sub-classes that return false for [[ElementSetTool.requireAcceptForSelectionSetOperation]] should override to apply the tool operation to [[ElementSetTool.agenda]]. */
protected processAgendaImmediate(): Promise<void>;
/** Sub-classes that require and use the accept point should override to apply the tool operation to [[ElementSetTool.agenda]].
* @note Not called for [[ElementSetTool.isSelectionSetModify]] when [[ElementSetTool.requireAcceptForSelectionSetOperation]] is false.
*/
protected processAgenda(_ev: BeButtonEvent): Promise<void>;
/** Support either [[ElementSetTool.requireAcceptForSelectionSetOperation]] or [[ElementSetTool.requireAcceptForSelectionSetDynamics]] returning false. */
protected doProcessSelectionSetImmediate(): Promise<void>;
/** Setup initial element state, prompts, check [[SelectionSet]], etc. */
onPostInstall(): Promise<void>;
/** Make sure elements from [[ElementSetTool.agenda]] that aren't also from [[SelectionSet]] aren't left hilited. */
onCleanup(): Promise<void>;
/** Exit and start default tool when [[ElementSetTool.isSelectionSetModify]] is true to allow [[SelectionSet]] to be modified,
* or call [[PrimitiveTool.onRestartTool]] to install a new tool instance.
*/
onReinitialize(): Promise<void>;
/** Restore tool assistance after no longer being suspended by either a [[ViewTool]] or [[InputCollector]]. */
onUnsuspend(): Promise<void>;
protected get shouldEnableLocate(): boolean;
protected get shouldEnableSnap(): boolean;
/** Setup auto-locate, AccuSnap, AccuDraw, and supply tool assistance. */
protected setupAndPromptForNextAction(): void;
/** Sub-classes should override to provide tool specific instructions. */
protected provideToolAssistance(mainInstrText?: string, additionalInstr?: ToolAssistanceInstruction[]): void;
}
//# sourceMappingURL=ElementSetTool.d.ts.map