packageDocumentation module import { DbResult , Id64 , Id64String } from "@itwin/core-bentley" ; import { GeometricElement , GeometricElement3d , IModelDb } from "@itwin/core-backend" ; import { InstanceKey , KeySet , PresentationError , PresentationStatus , SelectionScope , SelectionScopeRequestOptions , } from "@itwin/presentation-common" ; import { getElementKey } from "./Utils" ; internal export class SelectionScopesHelper { private constructor ( ) { } public static getSelectionScopes ( ) { const createSelectionScope = ( scopeId , label , description ) : SelectionScope => ( { id : scopeId , label , description } ) ; return [ createSelectionScope ( "element" , "Element" , "Select the picked element" ) , createSelectionScope ( "assembly" , "Assembly" , "Select parent of the picked element" ) , createSelectionScope ( "top-assembly" , "Top Assembly" , "Select the topmost parent of the picked element" ) , ] ; } private static computeElementSelection ( requestOptions , ids ) { const keys = new KeySet ( ) ; ids . forEach ( skipTransients ( ( id ) => { const key = getElementKey ( requestOptions . imodel , id ) ; if ( key ) keys . add ( key ) ; } ) ) ; return keys ; } private static getParentInstanceKey ( imodel , id ) { const elementProps = imodel . elements . tryGetElementProps ( id ) ; if ( ! elementProps ?. parent ) return undefined ; return getElementKey ( imodel , elementProps . parent . id ) ; } private static getAssemblyKey ( imodel , id ) { const parentKey = this . getParentInstanceKey ( imodel , id ) ; if ( parentKey ) return parentKey ; return getElementKey ( imodel , id ) ; } private static computeAssemblySelection ( requestOptions , ids ) { const parentKeys = new KeySet ( ) ; ids . forEach ( skipTransients ( ( id ) => { const key = this . getAssemblyKey ( requestOptions . imodel , id ) ; if ( key ) parentKeys . add ( key ) ; } ) ) ; return parentKeys ; } private static getTopAssemblyKey ( imodel , id ) { let currKey ; let parentKey = this . getParentInstanceKey ( imodel , id ) ; while ( parentKey ) { currKey = parentKey ; parentKey = this . getParentInstanceKey ( imodel , currKey . id ) ; } return currKey ?? getElementKey ( imodel , id ) ; } private static computeTopAssemblySelection ( requestOptions , ids ) { const parentKeys = new KeySet ( ) ; ids . forEach ( skipTransients ( ( id ) => { const key = this . getTopAssemblyKey ( requestOptions . imodel , id ) ; if ( key ) parentKeys . add ( key ) ; } ) ) ; return parentKeys ; } private static computeCategorySelection ( requestOptions , ids ) { const categoryKeys = new KeySet ( ) ; ids . forEach ( skipTransients ( ( id ) => { const el = requestOptions . imodel . elements . tryGetElement ( id ) ; if ( el instanceof GeometricElement ) { const category = requestOptions . imodel . elements . tryGetElementProps ( el . category ) ; if ( category ) categoryKeys . add ( { className : category . classFullName , id : category . id ! } ) ; } } ) ) ; return categoryKeys ; } private static computeModelSelection ( requestOptions , ids ) { const modelKeys = new KeySet ( ) ; ids . forEach ( skipTransients ( ( id ) => { const el = requestOptions . imodel . elements . tryGetElementProps ( id ) ; const model = el ? requestOptions . imodel . models . tryGetModelProps ( el . model ) : undefined ; if ( model ) modelKeys . add ( { className : model . classFullName , id : model . id ! } ) ; } ) ) ; return modelKeys ; } private static getRelatedFunctionalElementKey ( imodel , graphicalElementId ) { const query = `
      SELECT funcSchemaDef.Name || '.' || funcClassDef.Name funcElClassName, fe.ECInstanceId funcElId
        FROM bis.Element e
        LEFT JOIN func.PhysicalElementFulfillsFunction rel1 ON rel1.SourceECInstanceId = e.ECInstanceId
        LEFT JOIN func.DrawingGraphicRepresentsFunctionalElement rel2 ON rel2.SourceECInstanceId = e.ECInstanceId
        LEFT JOIN func.FunctionalElement fe ON fe.ECInstanceId IN (rel1.TargetECInstanceId, rel2.TargetECInstanceId)
        INNER JOIN meta.ECClassDef funcClassDef ON funcClassDef.ECInstanceId = fe.ECClassId
        INNER JOIN meta.ECSchemaDef funcSchemaDef ON funcSchemaDef.ECInstanceId = funcClassDef.Schema.Id
       WHERE e.ECInstanceId = ?
      ` ; return imodel . withPreparedStatement ( query , ( stmt ) : InstanceKey | undefined => { stmt . bindId ( 1 , graphicalElementId ) ; if ( DbResult . BE_SQLITE_ROW === stmt . step ( ) ) { const row = stmt . getRow ( ) ; if ( row . funcElClassName && row . funcElId ) return { className : row . funcElClassName . replace ( "." , ":" ) , id : row . funcElId } ; } return undefined ; } ) ; } private static findFirstRelatedFunctionalElementKey ( imodel , graphicalElementId ) { let currId = graphicalElementId ; while ( currId ) { const relatedFunctionalKey = this . getRelatedFunctionalElementKey ( imodel , currId ) ; if ( relatedFunctionalKey ) return relatedFunctionalKey ; currId = this . getParentInstanceKey ( imodel , currId ) ?. id ; } return undefined ; } private static elementClassDerivesFrom ( imodel , elementId , baseClassFullName ) { const query = `
      SELECT 1
        FROM bis.Element e
        INNER JOIN meta.ClassHasAllBaseClasses baseClassRels ON baseClassRels.SourceECInstanceId = e.ECClassId
        INNER JOIN meta.ECClassDef baseClass ON baseClass.ECInstanceId = baseClassRels.TargetECInstanceId
        INNER JOIN meta.ECSchemaDef baseSchema ON baseSchema.ECInstanceId = baseClass.Schema.Id
       WHERE e.ECInstanceId = ? AND (baseSchema.Name || ':' || baseClass.Name) = ?
      ` ; return imodel . withPreparedStatement ( query , ( stmt ) : boolean => { stmt . bindId ( 1 , elementId ) ; stmt . bindString ( 2 , baseClassFullName ) ; return ( DbResult . BE_SQLITE_ROW === stmt . step ( ) ) ; } ) ; } private static computeFunctionalElementSelection ( requestOptions , ids ) { const keys = new KeySet ( ) ; ids . forEach ( skipTransients ( ( id ) : void => { const is3d = this . elementClassDerivesFrom ( requestOptions . imodel , id , GeometricElement3d . classFullName ) ; if ( ! is3d ) { const firstFunctionalKey = this . findFirstRelatedFunctionalElementKey ( requestOptions . imodel , id ) ; if ( firstFunctionalKey ) { keys . add ( firstFunctionalKey ) ; return ; } } let keyToAdd ; if ( is3d ) { keyToAdd = this . getRelatedFunctionalElementKey ( requestOptions . imodel , id ) ; } if ( ! keyToAdd ) keyToAdd = getElementKey ( requestOptions . imodel , id ) ; keyToAdd && keys . add ( keyToAdd ) ; } ) ) ; return keys ; } private static computeFunctionalAssemblySelection ( requestOptions , ids ) { const keys = new KeySet ( ) ; ids . forEach ( skipTransients ( ( id ) : void => { let idToGetAssemblyFor = id ; const is3d = this . elementClassDerivesFrom ( requestOptions . imodel , id , GeometricElement3d . classFullName ) ; if ( ! is3d ) { const firstFunctionalKey = this . findFirstRelatedFunctionalElementKey ( requestOptions . imodel , id ) ; if ( firstFunctionalKey ) idToGetAssemblyFor = firstFunctionalKey . id ; } const assemblyKey = this . getAssemblyKey ( requestOptions . imodel , idToGetAssemblyFor ) ; let keyToAdd = assemblyKey ; if ( is3d && keyToAdd ) { const relatedFunctionalKey = this . getRelatedFunctionalElementKey ( requestOptions . imodel , keyToAdd . id ) ; if ( relatedFunctionalKey ) keyToAdd = relatedFunctionalKey ; } keyToAdd && keys . add ( keyToAdd ) ; } ) ) ; return keys ; } private static computeFunctionalTopAssemblySelection ( requestOptions , ids ) { const keys = new KeySet ( ) ; ids . forEach ( skipTransients ( ( id ) : void => { let idToGetAssemblyFor = id ; const is3d = this . elementClassDerivesFrom ( requestOptions . imodel , id , GeometricElement3d . classFullName ) ; if ( ! is3d ) { const firstFunctionalKey = this . findFirstRelatedFunctionalElementKey ( requestOptions . imodel , id ) ; if ( firstFunctionalKey ) idToGetAssemblyFor = firstFunctionalKey . id ; } const topAssemblyKey = this . getTopAssemblyKey ( requestOptions . imodel , idToGetAssemblyFor ) ; let keyToAdd = topAssemblyKey ; if ( is3d && keyToAdd ) { const relatedFunctionalKey = this . getRelatedFunctionalElementKey ( requestOptions . imodel , keyToAdd . id ) ; if ( relatedFunctionalKey ) keyToAdd = relatedFunctionalKey ; } keyToAdd && keys . add ( keyToAdd ) ; } ) ) ; return keys ; } param requestOptions param keys param scopeId public static async computeSelection ( requestOptions , ids , scopeId ) { switch ( scopeId ) { case "element" : return this . computeElementSelection ( requestOptions , ids ) ; case "assembly" : return this . computeAssemblySelection ( requestOptions , ids ) ; case "top-assembly" : return this . computeTopAssemblySelection ( requestOptions , ids ) ; case "category" : return this . computeCategorySelection ( requestOptions , ids ) ; case "model" : return this . computeModelSelection ( requestOptions , ids ) ; case "functional" : case "functional-element" : return this . computeFunctionalElementSelection ( requestOptions , ids ) ; case "functional-assembly" : return this . computeFunctionalAssemblySelection ( requestOptions , ids ) ; case "functional-top-assembly" : return this . computeFunctionalTopAssemblySelection ( requestOptions , ids ) ; } throw new PresentationError ( PresentationStatus . InvalidArgument , "scopeId" ) ; } } const skipTransients = ( callback ) => { return ( id ) => { if ( ! Id64 . isTransient ( id ) ) callback ( id ) ; } ; } ;