/
Element.ts
1630 lines (1444 loc) · 72.8 KB
/
Element.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Elements
*/
import { CompressedId64Set, GuidString, Id64, Id64Set, Id64String, JsonUtils, OrderedId64Array } from "@itwin/core-bentley";
import {
AxisAlignedBox3d, BisCodeSpec, Code, CodeScopeProps, CodeSpec, ConcreteEntityTypes, DefinitionElementProps, ElementAlignedBox3d, ElementGeometryBuilderParams,
ElementGeometryBuilderParamsForPart, ElementProps, EntityMetaData, EntityReferenceSet, GeometricElement2dProps, GeometricElement3dProps, GeometricElementProps,
GeometricModel2dProps, GeometricModel3dProps, GeometryPartProps, GeometryStreamProps, IModel, InformationPartitionElementProps, LineStyleProps,
ModelProps, PhysicalElementProps, PhysicalTypeProps, Placement2d, Placement3d, RelatedElement, RenderSchedule, RenderTimelineProps,
RepositoryLinkProps, SectionDrawingLocationProps, SectionDrawingProps, SectionType, SheetBorderTemplateProps, SheetProps, SheetTemplateProps,
SubjectProps, TypeDefinition, TypeDefinitionElementProps, UrlLinkProps,
} from "@itwin/core-common";
import { ClipVector, Range3d, Transform } from "@itwin/core-geometry";
import { Entity } from "./Entity";
import { IModelDb } from "./IModelDb";
import { IModelElementCloneContext } from "./IModelElementCloneContext";
import { DefinitionModel, DrawingModel, PhysicalModel, SectionDrawingModel } from "./Model";
import { SubjectOwnsSubjects } from "./NavigationRelationship";
/** Argument for the `Element.onXxx` static methods
* @beta
*/
export interface OnElementArg {
/** The iModel for the Element affected by this method. */
iModel: IModelDb;
}
/** Argument for `Element.onInsert` and `Element.onUpdate` static methods.
* @beta
*/
export interface OnElementPropsArg extends OnElementArg {
/** The properties of the Element affected by this method.
* @note the properties may be modified. If so the modified values will be inserted/updated.
*/
props: ElementProps;
}
/** Argument for the `Element.onXxx` static methods that notify of operations to an existing Element supplying its Id, ModelId and FederationGuid.
* @beta
*/
export interface OnElementIdArg extends OnElementArg {
/** The Id of the Element affected by this method */
id: Id64String;
/** The ModelId of the element affected by this method */
model: Id64String;
/** The federationGuid of the element affected by this method */
federationGuid: GuidString;
}
/** Argument for the `Element.onChildXxx` static methods
* @beta
*/
export interface OnChildElementArg extends OnElementArg {
parentId: Id64String;
}
/** Argument for the `Element.onChildXxx` static methods that supply the properties of the child Element to be inserted or updated.
* @beta
*/
export interface OnChildElementPropsArg extends OnChildElementArg {
/** The new properties of the child Element for this method. */
childProps: Readonly<ElementProps>;
}
/** Argument for the `Element.onChildXxx` static methods that only supply the Id of the child Element.
* @beta
*/
export interface OnChildElementIdArg extends OnChildElementArg {
/** The Id of the child element for this method */
childId: Id64String;
}
/** Argument for the `Element.onSubModelInsert` static method
* @beta
*/
export interface OnSubModelPropsArg extends OnElementArg {
/** The properties of the prospective sub-model */
subModelProps: ModelProps;
}
/** Argument for several `Element.onSubModelXxx` static methods
* @beta
*/
export interface OnSubModelIdArg extends OnElementArg {
/** The modelId of the sub Model */
subModelId: Id64String;
}
/** The smallest individually identifiable building block for modeling the real world in an iModel.
* Each element represents an [[Entity]] in the real world. Sets of Elements (contained in [[Model]]s) are used to model
* other Elements that represent larger scale real world entities. Using this recursive modeling strategy,
* Elements can represent entities at any scale. Elements can represent physical things or abstract concepts
* or simply be information records.
*
* Every Element has a 64-bit id (inherited from Entity) that uniquely identifies it within an iModel. Every Element also
* has a [[code]] that identifies its meaning in the real world. Additionally, Elements may have a [[federationGuid]]
* to hold a GUID, if the element was assigned that GUID by some other federated database. The iModel database enforces
* uniqueness of id, code, and federationGuid.
*
* The Element class provides `static` methods like [[onInsert]], [[onUpdated]], [[onCloned]], and [[onChildAdded]] that enable
* it to customize persistence operations. For example, the base implementations of [[onInsert]], [[onUpdate]], and [[onDelete]]
* validate that the appropriate [locks]($docs/learning/backend/ConcurrencyControl.md), [codes]($docs/learning/backend/CodeService.md),
* and [channel permissions]($docs/learning/backend/Channel.md) are obtained before a change to the element is written to the iModel.
* A subclass of Element that overrides any of these methods **must** call the `super` method as well. An application that supplies its
* own Element subclasses should register them at startup via [[ClassRegistry.registerModule]] or [[ClassRegistry.register]].
*
* See:
* * [Element Fundamentals]($docs/bis/guide/fundamentals/element-fundamentals.md)
* * [Working with schemas and elements in TypeScript]($docs/learning/backend/SchemasAndElementsInTypeScript.md)
* * [Creating elements]($docs/learning/backend/CreateElements.md)
* @public
*/
export class Element extends Entity {
/** @internal */
public static override get className(): string { return "Element"; }
/** @internal */
public static override get protectedOperations() { return ["onInsert", "onUpdate", "onDelete"]; }
/** The ModelId of the [Model]($docs/bis/guide/fundamentals/model-fundamentals.md) containing this element */
public readonly model: Id64String;
/** The [Code]($docs/bis/guide/fundamentals/codes.md) for this element */
public code: Code;
/** The parent element, if present, of this element. */
public parent?: RelatedElement;
/** A [FederationGuid]($docs/bis/guide/fundamentals/element-fundamentals.md#federationguid) assigned to this element by some other federated database */
public federationGuid?: GuidString;
/** A [user-assigned label]($docs/bis/guide/fundamentals/element-fundamentals.md#userlabel) for this element. */
public userLabel?: string;
/** Optional [json properties]($docs/bis/guide/fundamentals/element-fundamentals.md#jsonproperties) of this element. */
public readonly jsonProperties: { [key: string]: any };
protected constructor(props: ElementProps, iModel: IModelDb) {
super(props, iModel);
this.code = Code.fromJSON(props.code); // TODO: Validate props.code - don't silently fail if it is the wrong type
this.model = RelatedElement.idFromJson(props.model);
this.parent = RelatedElement.fromJSON(props.parent);
this.federationGuid = props.federationGuid;
this.userLabel = props.userLabel;
this.jsonProperties = { ...props.jsonProperties }; // make sure we have our own copy
}
/** Called before a new Element is inserted.
* @note throw an exception to disallow the insert
* @note If you override this method, you must call super.
* @note `this` is the class of the Element to be inserted
* @beta
*/
protected static onInsert(arg: OnElementPropsArg): void {
const { iModel, props } = arg;
const operation = "insert";
iModel.channels.verifyChannel(arg.props.model);
iModel.locks.checkSharedLock(props.model, "model", operation); // inserting requires shared lock on model
if (props.parent) // inserting requires shared lock on parent, if present
iModel.locks.checkSharedLock(props.parent.id, "parent", operation);
iModel.codeService?.verifyCode(arg);
}
/** Called after a new Element was inserted.
* @note If you override this method, you must call super.
* @note `this` is the class of the Element that was inserted
* @beta
*/
protected static onInserted(arg: OnElementIdArg): void {
arg.iModel.locks.elementWasCreated(arg.id);
}
/** Called before an Element is updated.
* @note throw an exception to disallow the update
* @note If you override this method, you must call super.
* @note `this` is the class of the Element to be updated
* @beta
*/
protected static onUpdate(arg: OnElementPropsArg): void {
const { iModel, props } = arg;
iModel.channels.verifyChannel(props.model);
iModel.locks.checkExclusiveLock(props.id!, "element", "update"); // eslint-disable-line @typescript-eslint/no-non-null-assertion
iModel.codeService?.verifyCode(arg);
}
/** Called after an Element was updated.
* @note If you override this method, you must call super.
* @note `this` is the class of the Element that was updated
* @beta
*/
protected static onUpdated(_arg: OnElementIdArg): void { }
/** Called before an Element is deleted.
* @note throw an exception to disallow the delete
* @note If you override this method, you must call super.
* @note `this` is the class of the Element to be deleted
* @beta
*/
protected static onDelete(arg: OnElementIdArg): void {
arg.iModel.channels.verifyChannel(arg.model);
arg.iModel.locks.checkExclusiveLock(arg.id, "element", "delete");
}
/** Called after an Element was deleted.
* @note If you override this method, you must call super.
* @note `this` is the class of the Element that was deleted
* @beta
*/
protected static onDeleted(_arg: OnElementIdArg): void { }
/** Called when an element with an instance of this class as its parent is about to be deleted.
* @note throw an exception if the element should not be deleted
* @note implementers should not presume that the element was deleted if this method does not throw,
* since the delete may fail for other reasons. Instead, rely on [[onChildDeleted]] for that purpose.
* @note `this` is the class of the parent Element whose child will be deleted
* @beta
*/
protected static onChildDelete(_arg: OnChildElementIdArg): void { }
/** Called after an element with an instance of this class as its parent was successfully deleted.
* @note `this` is the class of the parent Element whose child was deleted
* @beta
*/
protected static onChildDeleted(_arg: OnChildElementIdArg): void { }
/** Called when a *new element* with an instance of this class as its parent is about to be inserted.
* @note throw an exception if the element should not be inserted
* @note `this` is the class of the prospective parent Element.
* @beta
*/
protected static onChildInsert(_arg: OnChildElementPropsArg): void { }
/** Called after a *new element* with an instance of this class as its parent was inserted.
* @note `this` is the class of the parent Element.
* @beta
*/
protected static onChildInserted(_arg: OnChildElementIdArg): void { }
/** Called when an element with an instance of this class as its parent is about to be updated.
* @note throw an exception if the element should not be updated
* @note `this` is the class of the parent Element.
* @beta
*/
protected static onChildUpdate(_arg: OnChildElementPropsArg): void { }
/** Called after an element with an instance of this the class as its parent was updated.
* @note `this` is the class of the parent Element.
* @beta
*/
protected static onChildUpdated(_arg: OnChildElementIdArg): void { }
/** Called when an *existing element* is about to be updated so that an instance of this class will become its new parent.
* @note throw an exception if the element should not be added
* @note `this` is the class of the prospective parent Element.
* @beta
*/
protected static onChildAdd(_arg: OnChildElementPropsArg): void { }
/** Called after an *existing element* has been updated so that an instance of this class is its new parent.
* @note `this` is the class of the new parent Element.
* @beta
*/
protected static onChildAdded(_arg: OnChildElementIdArg): void { }
/** Called when an element with an instance of this class as its parent is about to be updated change to a different parent.
* @note throw an exception if the element should not be dropped
* @note `this` is the class of the parent Element.
* @beta
*/
protected static onChildDrop(_arg: OnChildElementIdArg): void { }
/** Called after an element with an instance of this class as its previous parent was updated to have a new parent.
* @note `this` is the class of the previous parent Element.
* @beta
*/
protected static onChildDropped(_arg: OnChildElementIdArg): void { }
/** Called when an instance of this class is being *sub-modeled* by a new Model.
* @note throw an exception if model should not be inserted
* @note `this` is the class of Element to be sub-modeled.
* @beta
*/
protected static onSubModelInsert(_arg: OnSubModelPropsArg): void { }
/** Called after an instance of this class was *sub-modeled* by a new Model.
* @note `this` is the class of Element that is now sub-modeled.
* @beta
*/
protected static onSubModelInserted(_arg: OnSubModelIdArg): void { }
/** Called when a sub-model of an instance of this class is being deleted.
* @note throw an exception if model should not be deleted
* @note `this` is the class of Element that is sub-modeled.
* @beta
*/
protected static onSubModelDelete(_arg: OnSubModelIdArg): void { }
/** Called after a sub-model of an instance of this class was deleted.
* @note `this` is the class of Element that was sub-modeled.
* @beta
*/
protected static onSubModelDeleted(_arg: OnSubModelIdArg): void { }
/** Called during the iModel transformation process after an Element from the source iModel was *cloned* for the target iModel.
* The transformation process automatically handles remapping BisCore properties and those that are properly described in ECSchema.
* This callback is only meant to be overridden if there are other Ids in non-standard locations that need to be remapped or other data that needs to be fixed up after the clone.
* @param _context The context that persists any remapping between the source iModel and target iModel.
* @param _sourceProps The ElementProps for the source Element that was cloned.
* @param _targetProps The ElementProps that are a result of the clone. These can be further modified.
* @note If you override this method, you must call super.
* @beta
*/
protected static onCloned(_context: IModelElementCloneContext, _sourceProps: ElementProps, _targetProps: ElementProps): void { }
/** Called when a *root* element in a subgraph is changed and before its outputs are processed.
* This special callback is made when:
* * the element is part of an [[ElementDrivesElement]] graph, and
* * the element has no inputs, and
* * none of the element's outputs have been processed.
* @see [[ElementDrivesElement]] for more on element dependency graphs.
* @beta
*/
protected static onBeforeOutputsHandled(_id: Id64String, _iModel: IModelDb): void { }
/** Called on an element in a graph after all of its inputs have been processed and before its outputs are processed.
* This callback is made when:
* * the specified element is part of an [[ElementDrivesElement]] graph, and
* * there was a direct change to some element upstream in the dependency graph.
* * all upstream elements in the graph have been processed.
* * none of the downstream elements have been processed.
* This method is not called if none of the element's inputs were changed.
* @see [[ElementDrivesElement]] for more on element dependency graphs.
* @beta
*/
protected static onAllInputsHandled(_id: Id64String, _iModel: IModelDb): void { }
public override toJSON(): ElementProps {
const val = super.toJSON() as ElementProps;
if (Code.isValid(this.code))
val.code = this.code;
val.model = this.model;
if (undefined !== this.userLabel) // NOTE: blank string should be included in JSON
val.userLabel = this.userLabel;
if (this.federationGuid)
val.federationGuid = this.federationGuid;
if (this.parent)
val.parent = this.parent;
if (Object.keys(this.jsonProperties).length > 0)
val.jsonProperties = this.jsonProperties;
return val;
}
/** Collect the Ids of this element's *references* at this level of the class hierarchy.
* @deprecated in 3.x. use [[collectReferenceIds]] instead, the use of the term *predecessors* was confusing and became inaccurate when the transformer could handle cycles
* @beta
*/
protected collectPredecessorIds(predecessorIds: EntityReferenceSet): void {
return this.collectReferenceIds(predecessorIds);
}
protected override collectReferenceIds(referenceIds: EntityReferenceSet): void {
super.collectReferenceIds(referenceIds);
referenceIds.addModel(this.model); // The modeledElement is a reference
if (this.code.scope && Id64.isValidId64(this.code.scope))
referenceIds.addElement(this.code.scope); // The element that scopes the code is a reference
if (this.parent)
referenceIds.addElement(this.parent.id); // A parent element is a reference
}
/** Get the Ids of this element's *references*. A *reference* is any element whose id is stored in the EC data of this element
* This is important for cloning operations but can be useful in other situations as well.
* @beta
* @deprecated in 3.x. use [[getReferenceIds]] instead, the use of the term *predecessors* was confusing and became inaccurate when the transformer could handle cycles
*/
public getPredecessorIds(): Id64Set {
return this.getReferenceIds();
}
/** A *required reference* is an element that had to be inserted before this element could have been inserted.
* This is the list of property keys on this element that store references to those elements
* @note This should be overridden (with `super` called) at each level of the class hierarchy that introduces required references.
* @note any property listed here must be added to the reference ids in [[collectReferenceIds]]
* @beta
*/
public static readonly requiredReferenceKeys: ReadonlyArray<string> = ["parent", "model"];
/** A map of every [[requiredReferenceKeys]] on this class to their entity type.
* @note This should be overridden (with `super` called) at each level of the class hierarchy that introduces required references.
* @alpha
*/
public static readonly requiredReferenceKeyTypeMap: Record<string, ConcreteEntityTypes> = {
parent: ConcreteEntityTypes.Element,
model: ConcreteEntityTypes.Model,
};
/** Get the class metadata for this element. */
public getClassMetaData(): EntityMetaData | undefined { return this.iModel.classMetaDataRegistry.find(this.classFullName); }
private getAllUserProperties(): any {
if (!this.jsonProperties.UserProps)
this.jsonProperties.UserProps = new Object();
return this.jsonProperties.UserProps;
}
/** Get a set of JSON user properties by namespace */
public getUserProperties(namespace: string) { return this.getAllUserProperties()[namespace]; }
/** Change a set of user JSON properties of this Element by namespace. */
public setUserProperties(nameSpace: string, value: any) { this.getAllUserProperties()[nameSpace] = value; }
/** Remove a set of JSON user properties, specified by namespace, from this Element */
public removeUserProperties(nameSpace: string) { delete this.getAllUserProperties()[nameSpace]; }
/** Get a JSON property of this element, by namespace */
public getJsonProperty(nameSpace: string): any { return this.jsonProperties[nameSpace]; }
public setJsonProperty(nameSpace: string, value: any) { this.jsonProperties[nameSpace] = value; }
/** Get a display label for this Element. By default returns userLabel if present, otherwise code value. */
public getDisplayLabel(): string { return this.userLabel ?? this.code.value; }
/** Get a list of HTML strings that describe this Element for the tooltip. Strings will be listed on separate lines in the tooltip.
* Any instances of the pattern `%{tag}` will be replaced by the localized value of tag.
*/
public getToolTipMessage(): string[] {
const addKey = (key: string) => `<b>%{iModelJs:Element.${key}}:</b> `; // %{iModelJs:Element.xxx} is replaced with localized value of xxx in frontend.
const msg: string[] = [];
const display = this.getDisplayLabel();
msg.push(display ? display : `${addKey("Id") + this.id}, ${addKey("Type")}${this.className}`);
if (this instanceof GeometricElement)
msg.push(addKey("Category") + this.iModel.elements.getElement(this.category).getDisplayLabel());
msg.push(addKey("Model") + this.iModel.elements.getElement(this.model).getDisplayLabel());
return msg;
}
/**
* Insert this Element into the iModel.
* @see [[IModelDb.Elements.insertElement]]
* @note For convenience, the value of `this.id` is updated to reflect the resultant element's id.
* However when `this.federationGuid` is not present or undefined, a new Guid will be generated and stored on the resultant element. But
* the value of `this.federationGuid` is *not* updated. Generally, it is best to re-read the element after inserting (e.g. via [[IModelDb.Elements.getElement]])
* if you intend to continue working with it. That will ensure its values reflect the persistent state.
*/
public insert() {
return this.id = this.iModel.elements.insertElement(this.toJSON());
}
/** Update this Element in the iModel. */
public update() { this.iModel.elements.updateElement(this.toJSON()); }
/** Delete this Element from the iModel. */
public delete() { this.iModel.elements.deleteElement(this.id); }
}
/** An abstract base class to model real world entities that intrinsically have geometry.
* @public
*/
export abstract class GeometricElement extends Element {
/** @internal */
public static override get className(): string { return "GeometricElement"; }
/** The Id of the [[Category]] for this GeometricElement. */
public category: Id64String;
/** The GeometryStream for this GeometricElement. */
public geom?: GeometryStreamProps;
/** How to build the element's GeometryStream. This is used for insert and update only. It is not a persistent property. It will be undefined in the properties returned by functions that read a persistent element. It may be specified as an alternative to `geom` when inserting or updating an element.
* @beta
*/
public elementGeometryBuilderParams?: ElementGeometryBuilderParams;
/** The origin, orientation, and bounding box of this GeometricElement. */
public abstract get placement(): Placement2d | Placement3d;
protected constructor(props: GeometricElementProps, iModel: IModelDb) {
super(props, iModel);
this.category = Id64.fromJSON(props.category);
this.geom = props.geom;
}
/** Type guard for instanceof [[GeometricElement3d]] */
public is3d(): this is GeometricElement3d { return this instanceof GeometricElement3d; }
/** Type guard for instanceof [[GeometricElement2d]] */
public is2d(): this is GeometricElement2d { return this instanceof GeometricElement2d; }
/** Get the [Transform]($geometry) from the Placement of this GeometricElement */
public getPlacementTransform(): Transform { return this.placement.transform; }
public calculateRange3d(): AxisAlignedBox3d { return this.placement.calculateRange(); }
public override toJSON(): GeometricElementProps {
const val = super.toJSON() as GeometricElementProps;
val.category = this.category;
if (this.geom)
val.geom = this.geom;
return val;
}
protected override collectReferenceIds(referenceIds: EntityReferenceSet): void {
super.collectReferenceIds(referenceIds);
referenceIds.addElement(this.category);
// TODO: GeometryPartIds?
}
/** @beta */
public static override readonly requiredReferenceKeys: ReadonlyArray<string> = [...super.requiredReferenceKeys, "category"];
/** @alpha */
public static override readonly requiredReferenceKeyTypeMap: Record<string, ConcreteEntityTypes> = {
...super.requiredReferenceKeyTypeMap,
category: ConcreteEntityTypes.Element,
};
}
/** An abstract base class to model real world entities that intrinsically have 3d geometry.
* See [how to create a GeometricElement3d]($docs/learning/backend/CreateElements.md#GeometricElement3d).
* @public
*/
export abstract class GeometricElement3d extends GeometricElement {
/** @internal */
public static override get className(): string { return "GeometricElement3d"; }
public placement: Placement3d;
public typeDefinition?: TypeDefinition;
protected constructor(props: GeometricElement3dProps, iModel: IModelDb) {
super(props, iModel);
this.placement = Placement3d.fromJSON(props.placement);
if (props.typeDefinition)
this.typeDefinition = TypeDefinition.fromJSON(props.typeDefinition);
}
public override toJSON(): GeometricElement3dProps {
const val = super.toJSON() as GeometricElement3dProps;
val.placement = this.placement;
if (undefined !== this.typeDefinition)
val.typeDefinition = this.typeDefinition;
return val;
}
protected override collectReferenceIds(referenceIds: EntityReferenceSet): void {
super.collectReferenceIds(referenceIds);
if (undefined !== this.typeDefinition)
referenceIds.addElement(this.typeDefinition.id);
}
}
/** A 3d Graphical Element
* @public
*/
export abstract class GraphicalElement3d extends GeometricElement3d {
/** @internal */
public static override get className(): string { return "GraphicalElement3d"; }
protected constructor(props: GeometricElement3dProps, iModel: IModelDb) { super(props, iModel); }
}
/** An abstract base class to model information entities that intrinsically have 2d geometry.
* @public
*/
export abstract class GeometricElement2d extends GeometricElement {
/** @internal */
public static override get className(): string { return "GeometricElement2d"; }
public placement: Placement2d;
public typeDefinition?: TypeDefinition;
protected constructor(props: GeometricElement2dProps, iModel: IModelDb) {
super(props, iModel);
this.placement = Placement2d.fromJSON(props.placement);
if (props.typeDefinition)
this.typeDefinition = TypeDefinition.fromJSON(props.typeDefinition);
}
public override toJSON(): GeometricElement2dProps {
const val = super.toJSON() as GeometricElement2dProps;
val.placement = this.placement;
if (undefined !== this.typeDefinition)
val.typeDefinition = this.typeDefinition;
return val;
}
protected override collectReferenceIds(referenceIds: EntityReferenceSet): void {
super.collectReferenceIds(referenceIds);
if (undefined !== this.typeDefinition)
referenceIds.addElement(this.typeDefinition.id);
}
}
/** An abstract base class for 2d Geometric Elements that are used to convey information within graphical presentations (like drawings).
* @public
*/
export abstract class GraphicalElement2d extends GeometricElement2d {
/** @internal */
public static override get className(): string { return "GraphicalElement2d"; }
protected constructor(props: GeometricElement2dProps, iModel: IModelDb) { super(props, iModel); }
}
/** 2d element used to annotate drawings and sheets.
* @public
*/
export class AnnotationElement2d extends GraphicalElement2d {
/** @internal */
public static override get className(): string { return "AnnotationElement2d"; }
protected constructor(props: GeometricElement2dProps, iModel: IModelDb) { super(props, iModel); }
}
/** 2d element used to persist graphics for use in drawings.
* @public
*/
export class DrawingGraphic extends GraphicalElement2d {
/** @internal */
public static override get className(): string { return "DrawingGraphic"; }
protected constructor(props: GeometricElement2dProps, iModel: IModelDb) { super(props, iModel); }
}
/** An Element that occupies real world space. Its coordinates are in the project space of its iModel.
* @public
*/
export abstract class SpatialElement extends GeometricElement3d {
/** @internal */
public static override get className(): string { return "SpatialElement"; }
protected constructor(props: GeometricElement3dProps, iModel: IModelDb) { super(props, iModel); }
}
/** An Element that is spatially located, has mass, and can be *touched*.
* @public
*/
export abstract class PhysicalElement extends SpatialElement {
/** @internal */
public static override get className(): string { return "PhysicalElement"; }
/** If defined, the [[PhysicalMaterial]] that makes up this PhysicalElement. */
public physicalMaterial?: RelatedElement;
protected constructor(props: PhysicalElementProps, iModel: IModelDb) {
super(props, iModel);
this.physicalMaterial = RelatedElement.fromJSON(props.physicalMaterial);
}
public override toJSON(): PhysicalElementProps {
const val = super.toJSON() as PhysicalElementProps;
val.physicalMaterial = this.physicalMaterial?.toJSON();
return val;
}
}
/** Identifies a *tracked* real world location but has no mass and cannot be *touched*.
* @public
*/
export abstract class SpatialLocationElement extends SpatialElement {
/** @internal */
public static override get className(): string { return "SpatialLocationElement"; }
protected constructor(props: GeometricElement3dProps, iModel: IModelDb) { super(props, iModel); }
}
/** A Volume Element is a Spatial Location Element that is restricted to defining a volume.
* @public
*/
export class VolumeElement extends SpatialLocationElement {
/** @internal */
public static override get className(): string { return "VolumeElement"; }
protected constructor(props: GeometricElement3dProps, iModel: IModelDb) { super(props, iModel); }
}
/** A SectionDrawingLocation element identifies the location of a [[SectionDrawing]] in the context of a [[SpatialModel]],
* enabling [HyperModeling]($hypermodeling).
* @note The associated ECClass was added to the BisCore schema in version 1.0.11.
* @public
*/
export class SectionDrawingLocation extends SpatialLocationElement {
/** The Id of the [[ViewDefinition]] to which this location refers. */
public sectionView: RelatedElement;
/** @internal */
public static override get className(): string { return "SectionDrawingLocation"; }
public constructor(props: SectionDrawingLocationProps, iModel: IModelDb) {
super(props, iModel);
this.sectionView = RelatedElement.fromJSON(props.sectionView) ?? RelatedElement.none;
}
public override toJSON(): SectionDrawingLocationProps {
return {
...super.toJSON(),
sectionView: this.sectionView.toJSON(),
};
}
}
/** Information Content Element is an abstract base class for modeling pure information entities. Only the
* core framework should directly subclass from Information Content Element. Domain and application developers
* should start with the most appropriate subclass of Information Content Element.
* @public
*/
export abstract class InformationContentElement extends Element {
/** @internal */
public static override get className(): string { return "InformationContentElement"; }
protected constructor(props: ElementProps, iModel: IModelDb) { super(props, iModel); }
}
/** Element used in conjunction with bis:ElementDrivesElement relationships to bundle multiple inputs before
* driving the output element.
* @beta
*/
export abstract class DriverBundleElement extends InformationContentElement {
/** @internal */
public static override get className(): string { return "DriverBundleElement"; }
protected constructor(props: ElementProps, iModel: IModelDb) { super(props, iModel); }
}
/** Information Reference is an abstract base class for modeling entities whose main purpose is to reference something else.
* @public
*/
export abstract class InformationReferenceElement extends InformationContentElement {
/** @internal */
public static override get className(): string { return "InformationReferenceElement"; }
protected constructor(props: ElementProps, iModel: IModelDb) { super(props, iModel); }
}
/** A Subject is an information element that describes what this repository (or part thereof) is about.
* See [how to create a Subject element]$(docs/learning/backend/CreateElements.md#Subject).
* @public
*/
export class Subject extends InformationReferenceElement {
/** @internal */
public static override get className(): string { return "Subject"; }
public description?: string;
protected constructor(props: SubjectProps, iModel: IModelDb) { super(props, iModel); }
public override toJSON(): SubjectProps { // This override only specializes the return type
return super.toJSON() as SubjectProps; // Entity.toJSON takes care of auto-handled properties
}
/** Create a Code for a Subject given a name that is meant to be unique within the scope of its parent Subject.
* @param iModelDb The IModelDb
* @param parentSubjectId The Id of the parent Subject that provides the scope for names of its child Subjects.
* @param codeValue The child Subject name
*/
public static createCode(iModelDb: IModelDb, parentSubjectId: CodeScopeProps, codeValue: string): Code {
const codeSpec: CodeSpec = iModelDb.codeSpecs.getByName(BisCodeSpec.subject);
return new Code({ spec: codeSpec.id, scope: parentSubjectId, value: codeValue });
}
/** Create a Subject
* @param iModelDb The IModelDb
* @param parentSubjectId The new Subject will be a child of this Subject
* @param name The name (codeValue) of the Subject
* @param description The optional description of the Subject
* @returns The newly constructed Subject
* @throws [[IModelError]] if there is a problem creating the Subject
*/
public static create(iModelDb: IModelDb, parentSubjectId: Id64String, name: string, description?: string): Subject {
const subjectProps: SubjectProps = {
classFullName: this.classFullName,
model: IModel.repositoryModelId,
parent: new SubjectOwnsSubjects(parentSubjectId),
code: this.createCode(iModelDb, parentSubjectId, name),
description,
};
return new Subject(subjectProps, iModelDb);
}
/** Insert a Subject
* @param iModelDb Insert into this IModelDb
* @param parentSubjectId The new Subject will be inserted as a child of this Subject
* @param name The name (codeValue) of the Subject
* @param description The optional description of the Subject
* @returns The Id of the newly inserted Subject
* @throws [[IModelError]] if there is a problem inserting the Subject
*/
public static insert(iModelDb: IModelDb, parentSubjectId: Id64String, name: string, description?: string): Id64String {
const subject = this.create(iModelDb, parentSubjectId, name, description);
return iModelDb.elements.insertElement(subject.toJSON());
}
}
/** An InformationContentElement that identifies the content of a document.
* The realized form of a document is called a DocumentCarrier (different class than Document).
* For example, a will is a legal document. The will published into a PDF file is an ElectronicDocumentCopy.
* The will printed onto paper is a PrintedDocumentCopy.
* In this example, the Document only identifies, names, and tracks the content of the will.
* @public
*/
export abstract class Document extends InformationContentElement {
/** @internal */
public static override get className(): string { return "Document"; }
protected constructor(props: ElementProps, iModel: IModelDb) { super(props, iModel); }
}
/** A document that represents a drawing, that is, a two-dimensional graphical representation of engineering data. A Drawing element is usually modelled by a [[DrawingModel]].
* @public
*/
export class Drawing extends Document {
/** @internal */
public static override get className(): string { return "Drawing"; }
protected constructor(props: ElementProps, iModel: IModelDb) { super(props, iModel); }
/** The name of the DrawingModel class modeled by this element type.
* @internal
*/
protected static get drawingModelFullClassName(): string { return DrawingModel.classFullName; }
/** Create a Code for a Drawing given a name that is meant to be unique within the scope of the specified DocumentListModel.
* @param iModel The IModelDb
* @param scopeModelId The Id of the DocumentListModel that contains the Drawing and provides the scope for its name.
* @param codeValue The Drawing name
*/
public static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code {
const codeSpec: CodeSpec = iModel.codeSpecs.getByName(BisCodeSpec.drawing);
return new Code({ spec: codeSpec.id, scope: scopeModelId, value: codeValue });
}
/** Insert a Drawing element and a DrawingModel that breaks it down.
* @param iModelDb Insert into this iModel
* @param documentListModelId Insert the new Drawing into this DocumentListModel
* @param name The name of the Drawing.
* @returns The Id of the newly inserted Drawing element and the DrawingModel that breaks it down (same value).
* @throws [[IModelError]] if unable to insert the element.
*/
public static insert(iModelDb: IModelDb, documentListModelId: Id64String, name: string): Id64String {
const drawingProps: ElementProps = {
classFullName: this.classFullName,
model: documentListModelId,
code: this.createCode(iModelDb, documentListModelId, name),
};
const drawingId: Id64String = iModelDb.elements.insertElement(drawingProps);
const model: DrawingModel = iModelDb.models.createModel({
classFullName: this.drawingModelFullClassName,
modeledElement: { id: drawingId },
});
return iModelDb.models.insertModel(model.toJSON());
}
}
/** A document that represents a section drawing, that is, a graphical documentation derived from a planar
* section of a spatial view. A SectionDrawing element is modelled by a [[SectionDrawingModel]] or a [[GraphicalModel3d]].
* A [[SectionDrawingLocation]] can associate the drawing with a spatial location, enabling [HyperModeling]($hypermodeling).
* @public
*/
export class SectionDrawing extends Drawing {
/** The type of section used to generate the drawing. */
public sectionType: SectionType;
/** The spatial view from which the section was generated. */
public spatialView: RelatedElement;
/** A transform from the section drawing model's coordinates to spatial coordinates. */
public drawingToSpatialTransform?: Transform;
/** If the section drawing is placed onto a [[Sheet]] via a [[ViewAttachment]], a transform from the sheet's coordinates to spatial coordinates. */
public sheetToSpatialTransform?: Transform;
/** If the section drawing is placed onto a [[Sheet]] via a [[ViewAttachment]], the clip to apply to the sheet graphics when drawn in the context
* of the spatial view.
* @note The ClipVector is defined in spatial coordinates.
*/
public drawingBoundaryClip?: ClipVector;
/** If true, when displaying the section drawing as a [DrawingViewState]($frontend), the [[spatialView]] will also be displayed. */
public displaySpatialView: boolean;
/** @internal */
public static override get className(): string { return "SectionDrawing"; }
/** @internal */
protected static override get drawingModelFullClassName(): string { return SectionDrawingModel.classFullName; }
protected constructor(props: SectionDrawingProps, iModel: IModelDb) {
super(props, iModel);
this.sectionType = JsonUtils.asInt(props.sectionType, SectionType.Section);
this.spatialView = RelatedElement.fromJSON(props.spatialView) ?? RelatedElement.none;
this.displaySpatialView = JsonUtils.asBool(props.jsonProperties?.displaySpatialView);
const json = props.jsonProperties;
if (!json)
return;
if (json.drawingToSpatialTransform)
this.drawingToSpatialTransform = Transform.fromJSON(json.drawingToSpatialTransform);
if (json.sheetToSpatialTransform)
this.sheetToSpatialTransform = Transform.fromJSON(json.sheetToSpatialTransform);
if (json.drawingBoundaryClip)
this.drawingBoundaryClip = ClipVector.fromJSON(json.drawingBoundaryClip);
}
public override toJSON(): SectionDrawingProps {
const props: SectionDrawingProps = {
...super.toJSON(),
sectionType: this.sectionType,
spatialView: this.spatialView.toJSON(),
};
if (!props.jsonProperties)
props.jsonProperties = {};
props.jsonProperties.displaySpatialView = this.displaySpatialView ? true : undefined;
props.jsonProperties.drawingToSpatialTransform = this.drawingToSpatialTransform?.toJSON();
props.jsonProperties.sheetToSpatialTransform = this.sheetToSpatialTransform?.toJSON();
props.jsonProperties.drawingBoundaryClip = this.drawingBoundaryClip?.toJSON();
return props;
}
}
/** The template for a SheetBorder
* @public
*/
export class SheetBorderTemplate extends Document {
/** @internal */
public static override get className(): string { return "SheetBorderTemplate"; }
public height?: number;
public width?: number;
protected constructor(props: SheetBorderTemplateProps, iModel: IModelDb) { super(props, iModel); }
}
/** The template for a [[Sheet]]
* @public
*/
export class SheetTemplate extends Document {
/** @internal */
public static override get className(): string { return "SheetTemplate"; }
public height?: number;
public width?: number;
public border?: Id64String;
protected constructor(props: SheetTemplateProps, iModel: IModelDb) { super(props, iModel); }
protected override collectReferenceIds(referenceIds: EntityReferenceSet): void {
super.collectReferenceIds(referenceIds);
if (undefined !== this.border)
referenceIds.addElement(this.border);
}
}
/** A digital representation of a *sheet of paper*. Modeled by a [[SheetModel]].
* @public
*/
export class Sheet extends Document {
/** @internal */
public static override get className(): string { return "Sheet"; }
public height: number;
public width: number;
public scale?: number;
public sheetTemplate?: Id64String;
protected constructor(props: SheetProps, iModel: IModelDb) {
super(props, iModel);
this.height = JsonUtils.asDouble(props.height);
this.width = JsonUtils.asDouble(props.width);
this.scale = props.scale;
this.sheetTemplate = props.sheetTemplate ? Id64.fromJSON(props.sheetTemplate) : undefined;
}
protected override collectReferenceIds(referenceIds: EntityReferenceSet): void {
super.collectReferenceIds(referenceIds);
if (undefined !== this.sheetTemplate)
referenceIds.addElement(this.sheetTemplate);
}
/** Create a Code for a Sheet given a name that is meant to be unique within the scope of the specified DocumentListModel.
* @param iModel The IModelDb
* @param scopeModelId The Id of the DocumentListModel that contains the Sheet and provides the scope for its name.
* @param codeValue The Sheet name
*/
public static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code {
const codeSpec: CodeSpec = iModel.codeSpecs.getByName(BisCodeSpec.sheet);
return new Code({ spec: codeSpec.id, scope: scopeModelId, value: codeValue });
}
}
/** Information Record Element is an abstract base class for modeling information records. Information Record
* Element is the default choice if no other subclass of Information Content Element makes sense.
* @public
*/
export abstract class InformationRecordElement extends InformationContentElement {
/** @internal */
public static override get className(): string { return "InformationRecordElement"; }
protected constructor(props: ElementProps, iModel: IModelDb) { super(props, iModel); }
}
/** A Definition Element holds configuration-related information that is meant to be referenced / shared.
* @public
*/
export abstract class DefinitionElement extends InformationContentElement {
/** @internal */
public static override get className(): string { return "DefinitionElement"; }
/** If true, don't show this DefinitionElement in user interface lists. */
public isPrivate: boolean;
protected constructor(props: DefinitionElementProps, iModel: IModelDb) {
super(props, iModel);
this.isPrivate = true === props.isPrivate;
}
public override toJSON(): DefinitionElementProps {
const val = super.toJSON() as DefinitionElementProps;
val.isPrivate = this.isPrivate;
return val;
}
}
/** This abstract class unifies DefinitionGroup and DefinitionContainer for relationship endpoint purposes.
* @note The associated ECClass was added to the BisCore schema in version 1.0.10
* @public
*/
export abstract class DefinitionSet extends DefinitionElement {
/** @internal */
public static override get className(): string { return "DefinitionSet"; }