@itwin/core-frontend 5.4.0-dev.7 → 5.5.0-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/lib/cjs/IModelApp.d.ts +12 -0
  3. package/lib/cjs/IModelApp.d.ts.map +1 -1
  4. package/lib/cjs/IModelApp.js +9 -0
  5. package/lib/cjs/IModelApp.js.map +1 -1
  6. package/lib/cjs/IModelConnection.d.ts.map +1 -1
  7. package/lib/cjs/IModelConnection.js +6 -2
  8. package/lib/cjs/IModelConnection.js.map +1 -1
  9. package/lib/cjs/SheetViewState.d.ts +7 -5
  10. package/lib/cjs/SheetViewState.d.ts.map +1 -1
  11. package/lib/cjs/SheetViewState.js +52 -683
  12. package/lib/cjs/SheetViewState.js.map +1 -1
  13. package/lib/cjs/internal/SheetViewAttachments.d.ts +57 -0
  14. package/lib/cjs/internal/SheetViewAttachments.d.ts.map +1 -0
  15. package/lib/cjs/internal/SheetViewAttachments.js +336 -0
  16. package/lib/cjs/internal/SheetViewAttachments.js.map +1 -0
  17. package/lib/cjs/internal/ViewAttachmentRenderer.d.ts +32 -0
  18. package/lib/cjs/internal/ViewAttachmentRenderer.d.ts.map +1 -0
  19. package/lib/cjs/internal/ViewAttachmentRenderer.js +462 -0
  20. package/lib/cjs/internal/ViewAttachmentRenderer.js.map +1 -0
  21. package/lib/cjs/internal/render/webgl/SceneCompositor.js +1 -1
  22. package/lib/cjs/internal/render/webgl/SceneCompositor.js.map +1 -1
  23. package/lib/cjs/internal/render/webgl/ShaderBuilder.d.ts.map +1 -1
  24. package/lib/cjs/internal/render/webgl/ShaderBuilder.js +8 -1
  25. package/lib/cjs/internal/render/webgl/ShaderBuilder.js.map +1 -1
  26. package/lib/cjs/properties/AngleDescription.js +4 -4
  27. package/lib/cjs/properties/AngleDescription.js.map +1 -1
  28. package/lib/cjs/properties/LengthDescription.js +9 -9
  29. package/lib/cjs/properties/LengthDescription.js.map +1 -1
  30. package/lib/cjs/quantity-formatting/QuantityFormatter.d.ts.map +1 -1
  31. package/lib/cjs/quantity-formatting/QuantityFormatter.js +9 -8
  32. package/lib/cjs/quantity-formatting/QuantityFormatter.js.map +1 -1
  33. package/lib/cjs/tools/EventController.js +1 -1
  34. package/lib/cjs/tools/EventController.js.map +1 -1
  35. package/lib/cjs/tools/MeasureTool.js +21 -21
  36. package/lib/cjs/tools/MeasureTool.js.map +1 -1
  37. package/lib/esm/IModelApp.d.ts +12 -0
  38. package/lib/esm/IModelApp.d.ts.map +1 -1
  39. package/lib/esm/IModelApp.js +9 -0
  40. package/lib/esm/IModelApp.js.map +1 -1
  41. package/lib/esm/IModelConnection.d.ts.map +1 -1
  42. package/lib/esm/IModelConnection.js +7 -3
  43. package/lib/esm/IModelConnection.js.map +1 -1
  44. package/lib/esm/SheetViewState.d.ts +7 -5
  45. package/lib/esm/SheetViewState.d.ts.map +1 -1
  46. package/lib/esm/SheetViewState.js +55 -686
  47. package/lib/esm/SheetViewState.js.map +1 -1
  48. package/lib/esm/internal/SheetViewAttachments.d.ts +57 -0
  49. package/lib/esm/internal/SheetViewAttachments.d.ts.map +1 -0
  50. package/lib/esm/internal/SheetViewAttachments.js +332 -0
  51. package/lib/esm/internal/SheetViewAttachments.js.map +1 -0
  52. package/lib/esm/internal/ViewAttachmentRenderer.d.ts +32 -0
  53. package/lib/esm/internal/ViewAttachmentRenderer.d.ts.map +1 -0
  54. package/lib/esm/internal/ViewAttachmentRenderer.js +459 -0
  55. package/lib/esm/internal/ViewAttachmentRenderer.js.map +1 -0
  56. package/lib/esm/internal/render/webgl/SceneCompositor.js +1 -1
  57. package/lib/esm/internal/render/webgl/SceneCompositor.js.map +1 -1
  58. package/lib/esm/internal/render/webgl/ShaderBuilder.d.ts.map +1 -1
  59. package/lib/esm/internal/render/webgl/ShaderBuilder.js +8 -1
  60. package/lib/esm/internal/render/webgl/ShaderBuilder.js.map +1 -1
  61. package/lib/esm/properties/AngleDescription.js +4 -4
  62. package/lib/esm/properties/AngleDescription.js.map +1 -1
  63. package/lib/esm/properties/LengthDescription.js +9 -9
  64. package/lib/esm/properties/LengthDescription.js.map +1 -1
  65. package/lib/esm/quantity-formatting/QuantityFormatter.d.ts.map +1 -1
  66. package/lib/esm/quantity-formatting/QuantityFormatter.js +9 -8
  67. package/lib/esm/quantity-formatting/QuantityFormatter.js.map +1 -1
  68. package/lib/esm/tools/EventController.js +1 -1
  69. package/lib/esm/tools/EventController.js.map +1 -1
  70. package/lib/esm/tools/MeasureTool.js +21 -21
  71. package/lib/esm/tools/MeasureTool.js.map +1 -1
  72. package/lib/public/scripts/parse-imdl-worker.js +1 -1
  73. package/lib/workers/webpack/parse-imdl-worker.js +1 -1
  74. package/package.json +21 -21
@@ -5,23 +5,15 @@
5
5
  /** @packageDocumentation
6
6
  * @module Views
7
7
  */
8
- import { assert, CompressedId64Set, dispose, expectDefined } from "@itwin/core-bentley";
9
- import { Angle, ClipShape, ClipVector, Constant, Matrix3d, Point2d, Point3d, PolyfaceBuilder, Range2d, Range3d, StrokeOptions, Transform } from "@itwin/core-geometry";
10
- import { ColorDef, Feature, FeatureTable, Frustum, Gradient, GraphicParams, PackedFeatureTable, Placement2d, TextureTransparency, } from "@itwin/core-common";
8
+ import { BeEvent, dispose, expectDefined } from "@itwin/core-bentley";
9
+ import { Angle, Constant, Point2d, Point3d, Range2d, Range3d } from "@itwin/core-geometry";
10
+ import { ColorDef, Gradient, GraphicParams, } from "@itwin/core-common";
11
11
  import { CategorySelectorState } from "./CategorySelectorState";
12
12
  import { DisplayStyle2dState } from "./DisplayStyleState";
13
- import { GraphicBranch } from "./render/GraphicBranch";
14
13
  import { Frustum2d } from "./Frustum2d";
15
- import { MockRender } from "./internal/render/MockRender";
16
- import { FeatureSymbology } from "./render/FeatureSymbology";
17
- import { IModelApp } from "./IModelApp";
18
- import { CoordSystem } from "./CoordSystem";
19
- import { OffScreenViewport } from "./Viewport";
20
14
  import { ViewState2d } from "./ViewState";
21
- import { createDefaultViewFlagOverrides, TileGraphicType } from "./tile/internal";
22
- import { imageBufferToPngDataUrl, openImageDataUrlInNewWindow } from "./common/ImageUtil";
23
- import { ViewRect } from "./common/ViewRect";
24
15
  import { GraphicType } from "./common/render/GraphicType";
16
+ import { SheetViewAttachments } from "./internal/SheetViewAttachments";
25
17
  // cSpell:ignore ovrs
26
18
  /** Describes the geometry and styling of a sheet border decoration.
27
19
  * The sheet border decoration mimics a sheet of paper with a drop shadow.
@@ -95,189 +87,6 @@ class SheetBorder {
95
87
  builder.addLineString2d(this._rect, 0);
96
88
  }
97
89
  }
98
- /** The information required to instantiate an ViewAttachments object to draw ViewAttachments into a sheet. The list of view attachment Ids is
99
- * supplied to SheetViewState via the constructor. The corresponding ViewAttachmentProps for each attachment are obtained asynchronously in
100
- * SheetViewState.load(). The Attachments object is created in SheetViewState.attachToViewport and disposed of in SheetViewState.detachFromViewport.
101
- */
102
- class ViewAttachmentsInfo {
103
- _attachments;
104
- get attachments() { return this._attachments; }
105
- constructor(attachments) {
106
- this._attachments = attachments;
107
- }
108
- get isLoaded() {
109
- return 0 === this._attachments.length || "string" !== typeof this._attachments[0];
110
- }
111
- get viewAttachmentProps() {
112
- return this.isLoaded ? this._props : [];
113
- }
114
- get _props() {
115
- assert(this.isLoaded);
116
- return this._attachments;
117
- }
118
- get _ids() {
119
- assert(!this.isLoaded);
120
- return this._attachments;
121
- }
122
- static fromJSON(ids = []) {
123
- return new ViewAttachmentsInfo(ids);
124
- }
125
- toJSON() {
126
- return this.isLoaded ? this._props.map((x) => expectDefined(x.id)) : [...this._ids];
127
- }
128
- clone(iModel) {
129
- let attachments = this._attachments;
130
- if (this.isLoaded) {
131
- // Need to clone the attached ViewStates.
132
- attachments = attachments.map((attachment) => {
133
- assert(typeof attachment !== "string");
134
- return {
135
- ...attachment,
136
- attachedView: attachment.attachedView.clone(iModel),
137
- };
138
- });
139
- }
140
- return new ViewAttachmentsInfo(attachments);
141
- }
142
- preload(options) {
143
- if (this.isLoaded)
144
- return;
145
- options.sheetViewAttachmentIds = CompressedId64Set.sortAndCompress(this._ids);
146
- options.viewStateLoadProps = {
147
- displayStyle: {
148
- omitScheduleScriptElementIds: !IModelApp.tileAdmin.enableFrontendScheduleScripts,
149
- compressExcludedElementIds: true,
150
- },
151
- };
152
- }
153
- async postload(options, iModel) {
154
- if (options.sheetViewViews === undefined)
155
- return;
156
- if (options.sheetViewAttachmentProps === undefined)
157
- return;
158
- const viewStateProps = options.sheetViewViews; // This is viewstateProps, need to turn this into ViewState
159
- const promises = [];
160
- for (const viewProps of viewStateProps) {
161
- const loadView = async () => {
162
- try {
163
- if (viewProps === undefined)
164
- return undefined;
165
- const view = await iModel.views.convertViewStatePropsToViewState(viewProps);
166
- return view;
167
- }
168
- catch {
169
- return undefined;
170
- }
171
- };
172
- promises.push(loadView());
173
- }
174
- const views = await Promise.all(promises);
175
- const attachmentProps = options.sheetViewAttachmentProps;
176
- assert(views.length === attachmentProps.length);
177
- const attachments = [];
178
- for (let i = 0; i < views.length; i++) {
179
- const view = views[i];
180
- if (view && !(view instanceof SheetViewState)) {
181
- const props = attachmentProps[i];
182
- props.attachedView = view;
183
- attachments.push(props);
184
- }
185
- }
186
- this._attachments = attachments;
187
- }
188
- async load(iModel) {
189
- if (this.isLoaded)
190
- return;
191
- const attachmentProps = await iModel.elements.getProps(this._ids);
192
- const promises = [];
193
- for (const attachment of attachmentProps) {
194
- const loadView = async () => {
195
- try {
196
- const view = await iModel.views.load(attachment.view.id);
197
- return view;
198
- }
199
- catch {
200
- return undefined;
201
- }
202
- };
203
- promises.push(loadView());
204
- }
205
- const views = await Promise.all(promises);
206
- assert(views.length === attachmentProps.length);
207
- const attachments = [];
208
- for (let i = 0; i < views.length; i++) {
209
- const view = views[i];
210
- if (view && !(view instanceof SheetViewState)) {
211
- const props = attachmentProps[i];
212
- props.attachedView = view;
213
- attachments.push(props);
214
- }
215
- }
216
- this._attachments = attachments;
217
- }
218
- createAttachments(sheetView) {
219
- return this.isLoaded ? new ViewAttachments(this._props, sheetView) : undefined;
220
- }
221
- }
222
- /** The set of view attachments to be displayed in a Viewport via a SheetViewState. Allocated when the view becomes attached to a Viewport;
223
- * disposed of when it becomes detached from the viewport.
224
- */
225
- class ViewAttachments {
226
- _attachments = [];
227
- maxDepth = Frustum2d.minimumZDistance;
228
- constructor(infos, sheetView) {
229
- for (const info of infos) {
230
- const drawAsRaster = info.jsonProperties?.displayOptions?.drawAsRaster || (info.attachedView.is3d() && info.attachedView.isCameraOn);
231
- const ctor = drawAsRaster ? RasterAttachment : OrthographicAttachment;
232
- const attachment = new ctor(info.attachedView, info, sheetView);
233
- this._attachments.push(attachment);
234
- this.maxDepth = Math.max(this.maxDepth, attachment.zDepth);
235
- }
236
- }
237
- [Symbol.dispose]() {
238
- for (const attachment of this._attachments)
239
- attachment[Symbol.dispose]();
240
- this._attachments.length = 0;
241
- }
242
- [Symbol.iterator]() {
243
- return this._attachments[Symbol.iterator]();
244
- }
245
- /** For tests. */
246
- get attachments() {
247
- return this._attachments;
248
- }
249
- get isEmpty() {
250
- return 0 === this._attachments.length;
251
- }
252
- areAllTileTreesLoaded(displayedExtents) {
253
- return this._attachments.every((x) => {
254
- const placement = Placement2d.fromJSON(x.viewAttachmentProps.placement);
255
- const attachmentRange = placement.calculateRange();
256
- if (!attachmentRange.intersectsRangeXY(displayedExtents))
257
- return true;
258
- return x.areAllTileTreesLoaded;
259
- });
260
- }
261
- /** Strictly for testing purposes */
262
- areAllAttachmentsLoaded() {
263
- return this._attachments.every((attachment) => attachment.areAllTileTreesLoaded);
264
- }
265
- discloseTileTrees(trees) {
266
- for (const attachment of this._attachments)
267
- trees.disclose(attachment);
268
- }
269
- collectStatistics(stats) {
270
- for (const attachment of this._attachments)
271
- attachment.collectStatistics(stats);
272
- }
273
- addToScene(context) {
274
- for (const attachment of this._attachments)
275
- attachment.addToScene(context);
276
- }
277
- findById(attachmentId) {
278
- return this._attachments.find((attachment) => attachment.viewAttachmentProps.id === attachmentId);
279
- }
280
- }
281
90
  /** A view of a [SheetModel]($backend).
282
91
  * @public
283
92
  * @extensions
@@ -285,11 +94,13 @@ class ViewAttachments {
285
94
  export class SheetViewState extends ViewState2d {
286
95
  /** The width and height of the sheet in world coordinates. */
287
96
  sheetSize;
288
- _attachmentsInfo;
289
- _attachments;
97
+ _viewAttachments;
290
98
  _viewedExtents;
99
+ _onViewAttachmentsReloaded = () => undefined;
100
+ /** Strictly for tests. */
101
+ onViewAttachmentsReloaded = new BeEvent();
291
102
  get attachmentIds() {
292
- return this._attachmentsInfo.toJSON();
103
+ return this._viewAttachments.attachmentIds;
293
104
  }
294
105
  static get className() { return "SheetViewDefinition"; }
295
106
  static createFromProps(viewStateData, iModel) {
@@ -300,7 +111,7 @@ export class SheetViewState extends ViewState2d {
300
111
  }
301
112
  toProps() {
302
113
  const props = super.toProps();
303
- props.sheetAttachments = this._attachmentsInfo.toJSON();
114
+ props.sheetAttachments = [...this.attachmentIds];
304
115
  // For sheetProps all that is actually used is the size, so just null out everything else.
305
116
  const codeProps = { spec: "", scope: "", value: "" };
306
117
  props.sheetProps = {
@@ -315,20 +126,15 @@ export class SheetViewState extends ViewState2d {
315
126
  }
316
127
  /** Strictly for testing. @internal */
317
128
  get viewAttachmentProps() {
318
- return this._attachmentsInfo.viewAttachmentProps.map((x) => {
319
- return {
320
- ...x,
321
- attachedView: undefined,
322
- };
323
- });
129
+ return this._viewAttachments.attachmentProps;
324
130
  }
325
131
  /** Strictly for testing. @internal */
326
132
  get viewAttachmentInfos() {
327
- return this._attachmentsInfo.attachments;
133
+ return this._viewAttachments.attachmentInfos;
328
134
  }
329
135
  /** Strictly for testing. @internal */
330
136
  get attachments() {
331
- return this._attachments?.attachments;
137
+ return this._viewAttachments.attachments;
332
138
  }
333
139
  isDrawingView() { return false; }
334
140
  isSheetView() { return true; }
@@ -337,41 +143,53 @@ export class SheetViewState extends ViewState2d {
337
143
  if (categories instanceof SheetViewState) {
338
144
  // we are coming from clone...
339
145
  this.sheetSize = categories.sheetSize.clone();
340
- this._attachmentsInfo = categories._attachmentsInfo.clone(iModel);
146
+ this._viewAttachments = categories._viewAttachments.clone(iModel);
341
147
  this._viewedExtents = categories._viewedExtents.clone();
342
148
  }
343
149
  else {
344
150
  this.sheetSize = Point2d.create(sheetProps.width, sheetProps.height);
345
- this._attachmentsInfo = ViewAttachmentsInfo.fromJSON(attachments);
151
+ this._viewAttachments = SheetViewAttachments.create(attachments);
346
152
  const extents = new Range3d(0, 0, 0, this.sheetSize.x, this.sheetSize.y, 0);
347
153
  const margin = 1.1;
348
154
  extents.scaleAboutCenterInPlace(margin);
349
155
  this._viewedExtents = extents;
350
156
  }
157
+ if (iModel.isBriefcaseConnection()) {
158
+ iModel.txns.onElementsChanged.addListener(async (changes) => {
159
+ let reload = false;
160
+ for (const change of changes.filter({ includeMetadata: (meta) => meta.is("BisCore:ViewAttachment") })) {
161
+ if (change.type === "inserted" || this._viewAttachments.attachmentIds.includes(change.id)) {
162
+ reload = true;
163
+ break;
164
+ }
165
+ }
166
+ if (reload) {
167
+ await this._viewAttachments.reload(this.baseModelId, iModel);
168
+ this._onViewAttachmentsReloaded();
169
+ this.onViewAttachmentsReloaded.raiseEvent();
170
+ }
171
+ });
172
+ }
351
173
  }
352
174
  getOrigin() {
353
175
  const origin = super.getOrigin();
354
- if (this._attachments)
355
- origin.z = -this._attachments.maxDepth;
176
+ origin.z = -this._viewAttachments.maxDepth;
356
177
  return origin;
357
178
  }
358
179
  getExtents() {
359
180
  const extents = super.getExtents();
360
- if (this._attachments)
361
- extents.z = this._attachments.maxDepth + Frustum2d.minimumZDistance;
181
+ extents.z = this._viewAttachments.maxDepth + Frustum2d.minimumZDistance;
362
182
  return extents;
363
183
  }
364
184
  /** Overrides [[ViewState.discloseTileTrees]] to include tile trees associated with [ViewAttachment]($backend)s displayed on this sheet. */
365
185
  discloseTileTrees(trees) {
366
186
  super.discloseTileTrees(trees);
367
- if (this._attachments)
368
- trees.disclose(this._attachments);
187
+ trees.disclose(this._viewAttachments);
369
188
  }
370
189
  /** @internal */
371
190
  collectNonTileTreeStatistics(stats) {
372
191
  super.collectNonTileTreeStatistics(stats);
373
- if (this._attachments)
374
- this._attachments.collectStatistics(stats);
192
+ this._viewAttachments.collectStatistics(stats);
375
193
  }
376
194
  get defaultExtentLimits() {
377
195
  return { min: Constant.oneMillimeter, max: this.sheetSize.magnitude() * 10 };
@@ -382,36 +200,23 @@ export class SheetViewState extends ViewState2d {
382
200
  /** @internal */
383
201
  preload(hydrateRequest) {
384
202
  super.preload(hydrateRequest);
385
- this._attachmentsInfo.preload(hydrateRequest);
203
+ this._viewAttachments.preload(hydrateRequest);
386
204
  }
387
205
  /** @internal */
388
206
  async postload(hydrateResponse) {
389
207
  const promises = [];
390
208
  promises.push(super.postload(hydrateResponse));
391
- promises.push(this._attachmentsInfo.postload(hydrateResponse, this.iModel));
209
+ promises.push(this._viewAttachments.postload(hydrateResponse, this.iModel));
392
210
  await Promise.all(promises);
393
211
  }
394
212
  /** @internal */
395
213
  createScene(context) {
396
214
  super.createScene(context);
397
- if (this._attachments)
398
- this._attachments.addToScene(context);
215
+ this._viewAttachments.addToScene(context);
399
216
  }
400
217
  /** @internal */
401
218
  get secondaryViewports() {
402
- if (this._attachments === undefined)
403
- return super.secondaryViewports;
404
- const attachments = this._attachments;
405
- function* iterator() {
406
- for (const attachment of attachments) {
407
- const vp = attachment.viewport;
408
- if (vp)
409
- yield vp;
410
- }
411
- }
412
- return {
413
- [Symbol.iterator]: () => iterator(),
414
- };
219
+ return this._viewAttachments.getSecondaryViewports();
415
220
  }
416
221
  /** @internal */
417
222
  async queryAttachmentIds() {
@@ -424,34 +229,38 @@ export class SheetViewState extends ViewState2d {
424
229
  async changeViewedModel(modelId) {
425
230
  await super.changeViewedModel(modelId);
426
231
  const attachmentIds = await this.queryAttachmentIds();
427
- this._attachmentsInfo = ViewAttachmentsInfo.fromJSON(attachmentIds);
428
- assert(undefined === this._attachments);
232
+ dispose(this._viewAttachments);
233
+ this._viewAttachments = SheetViewAttachments.create(attachmentIds);
429
234
  }
430
235
  /** See [[ViewState.attachToViewport]]. */
431
236
  attachToViewport(args) {
432
237
  super.attachToViewport(args);
433
- assert(undefined === this._attachments);
434
- this._attachments = this._attachmentsInfo.createAttachments(this);
238
+ this._viewAttachments.attachToViewport({
239
+ backgroundColor: this.displayStyle.backgroundColor,
240
+ sheetModelId: this.baseModelId,
241
+ });
242
+ this._onViewAttachmentsReloaded = () => args.invalidateController();
435
243
  }
436
244
  /** See [[ViewState.detachFromViewport]]. */
437
245
  detachFromViewport() {
438
246
  super.detachFromViewport();
439
- this._attachments = dispose(this._attachments);
247
+ this._viewAttachments.detachFromViewport();
248
+ this._onViewAttachmentsReloaded = () => undefined;
440
249
  }
441
250
  get areAllTileTreesLoaded() {
251
+ if (!super.areAllTileTreesLoaded) {
252
+ return false;
253
+ }
442
254
  let displayedExtents = this._viewedExtents;
443
255
  const frustum = this.calculateFrustum();
444
256
  if (frustum) {
445
257
  displayedExtents = frustum.toRange();
446
258
  }
447
- return super.areAllTileTreesLoaded && (!this._attachments || this._attachments.areAllTileTreesLoaded(displayedExtents));
259
+ return this._viewAttachments.areAllTileTreesLoaded(displayedExtents);
448
260
  }
449
261
  /** @internal Strictly for testing */
450
262
  areAllAttachmentsLoaded() {
451
- if (this._attachments) {
452
- return this._attachments.areAllAttachmentsLoaded();
453
- }
454
- return true;
263
+ return this._viewAttachments.areAllAttachmentsLoaded();
455
264
  }
456
265
  /** Create a sheet border decoration graphic. */
457
266
  createBorder(width, height, context) {
@@ -476,453 +285,13 @@ export class SheetViewState extends ViewState2d {
476
285
  }
477
286
  /** @internal */
478
287
  getAttachmentViewport(args) {
479
- const attachment = args.viewAttachmentId ? this._attachments?.findById(args.viewAttachmentId) : undefined;
480
- if (!attachment) {
481
- return undefined;
482
- }
483
- return args.inSectionDrawingAttachment ? attachment.viewport?.view.getAttachmentViewport({ inSectionDrawingAttachment: true }) : attachment.viewport;
288
+ return this._viewAttachments.getAttachmentViewport(args);
484
289
  }
485
290
  /** @beta */
486
291
  computeDisplayTransform(args) {
487
292
  // ###TODO we're currently ignoring model and element Id in args, assuming irrelevant for sheets.
488
293
  // Should probably call super or have super call us.
489
- const attachment = undefined !== args.viewAttachmentId ? this._attachments?.findById(args.viewAttachmentId) : undefined;
490
- if (!attachment || !(attachment instanceof OrthographicAttachment)) {
491
- return undefined;
492
- }
493
- const sheetTransform = attachment.toSheet;
494
- const sectionTransform = args.inSectionDrawingAttachment ? attachment.view.computeDisplayTransform(args) : undefined;
495
- if (!sectionTransform) {
496
- return sheetTransform.clone(args.output);
497
- }
498
- return sheetTransform.multiplyTransformTransform(sectionTransform, args.output);
499
- }
500
- }
501
- /** A mostly no-op RenderTarget for an Attachment.
502
- * its Scene and symbology overrides.
503
- */
504
- class AttachmentTarget extends MockRender.OffScreenTarget {
505
- _attachment;
506
- constructor(attachment) {
507
- // The dimensions don't matter - we're not drawing anything.
508
- const rect = new ViewRect(1, 1);
509
- super(IModelApp.renderSystem, rect);
510
- this._attachment = attachment;
511
- }
512
- changeScene(scene) {
513
- this._attachment.scene = scene;
514
- }
515
- overrideFeatureSymbology(ovrs) {
516
- this._attachment.symbologyOverrides = ovrs;
517
- }
518
- }
519
- /** Draws the contents a 2d or orthographic 3d view directly into a sheet view.
520
- * We select tiles for the view in the context of a light-weight offscreen viewport with a no-op RenderTarget, then
521
- * collect the resultant graphics and add them to the sheet view's scene.
522
- */
523
- class OrthographicAttachment {
524
- _viewport;
525
- _props;
526
- _sheetModelId;
527
- _viewFlagOverrides;
528
- _toSheet;
529
- _fromSheet;
530
- _sizeInMeters;
531
- _range;
532
- _viewRect = new ViewRect(0, 0, 1, 1);
533
- _originalFrustum = new Frustum();
534
- _clipVolume;
535
- _hiddenLineSettings;
536
- _scale;
537
- _debugFeatureTable;
538
- scene;
539
- symbologyOverrides;
540
- zDepth;
541
- get view() {
542
- return this._viewport.view;
543
- }
544
- get viewAttachmentProps() {
545
- return this._props;
546
- }
547
- get viewport() {
548
- return this._viewport;
549
- }
550
- constructor(view, props, sheetView) {
551
- this.symbologyOverrides = new FeatureSymbology.Overrides(view);
552
- const target = new AttachmentTarget(this);
553
- this._viewport = OffScreenViewport.createViewport(view, target, true);
554
- this._props = props;
555
- this._sheetModelId = sheetView.baseModelId;
556
- const applyClip = true; // set to false for debugging
557
- this._viewFlagOverrides = {
558
- ...view.viewFlags,
559
- clipVolume: applyClip,
560
- lighting: false,
561
- shadows: false,
562
- };
563
- const placement = Placement2d.fromJSON(props.placement);
564
- const range = placement.calculateRange();
565
- this._range = range;
566
- this._sizeInMeters = new Point2d(range.xLength(), range.yLength());
567
- // Compute transform from attached view's world coordinates to sheet's world coordinates.
568
- // NB: We obtain the extents and origin from the *viewport* not the *view* - they may have been adjusted by the viewport.
569
- const applySkew = true; // set to false for debugging
570
- const skew = applySkew ? view.getAspectRatioSkew() : 1;
571
- const extents = this._viewport.viewingSpace.viewDelta.clone();
572
- const zDepth = Math.abs(extents.z);
573
- const scaleX = this._sizeInMeters.x / Math.abs(extents.x);
574
- const scaleY = skew * this._sizeInMeters.y / Math.abs(extents.y);
575
- this._scale = { x: 1 / scaleX, y: 1 / scaleY };
576
- const zBias = Frustum2d.depthFromDisplayPriority(props.jsonProperties?.displayPriority ?? 0);
577
- this.zDepth = 1.01 * (zDepth - zBias); // give a little padding so that geometry right up against far plane doesn't get clipped.
578
- // View origin is at the *back* of the view. Align *front* of view based on display priority.
579
- const viewRot = view.getRotation();
580
- const viewOrg = viewRot.multiplyVector(this._viewport.viewingSpace.viewOrigin);
581
- viewOrg.z += zDepth;
582
- viewRot.multiplyTransposeVectorInPlace(viewOrg);
583
- const matrix = Matrix3d.createScale(scaleX, scaleY, 1);
584
- matrix.multiplyMatrixMatrix(viewRot, matrix);
585
- const origin = Matrix3d.xyzMinusMatrixTimesXYZ(viewOrg, matrix, viewOrg);
586
- const attachmentOrigin = Point3d.createFrom(placement.origin);
587
- attachmentOrigin.z = zBias;
588
- const viewOrgToAttachment = attachmentOrigin.minus(viewOrg);
589
- origin.addInPlace(viewOrgToAttachment);
590
- this._toSheet = Transform.createRefs(origin, matrix);
591
- this._fromSheet = expectDefined(this._toSheet.inverse());
592
- // If the attached view is a section drawing, it may itself have an attached spatial view with a clip.
593
- // The clip needs to be transformed into sheet space.
594
- if (view.isDrawingView())
595
- this._viewport.drawingToSheetTransform = this._toSheet;
596
- // ###TODO? If we also apply the attachment's clip to the attached view, we may get additional culling during tile selection.
597
- // However the attached view's frustum is already clipped by intersection with sheet view's frustum, and additional clipping planes
598
- // introduce additional computation, so possibly not worth it.
599
- // Transform the view's clip (if any) to sheet space
600
- let viewClip = view.viewFlags.clipVolume ? view.getViewClip()?.clone() : undefined;
601
- if (viewClip)
602
- viewClip.transformInPlace(this._toSheet);
603
- else
604
- viewClip = ClipVector.createEmpty();
605
- let sheetClip;
606
- if (undefined !== props.jsonProperties?.clip)
607
- sheetClip = ClipVector.fromJSON(props.jsonProperties?.clip);
608
- if (sheetClip && sheetClip.isValid) {
609
- // Clip to view attachment's clip. NB: clip is in sheet coordinate space.
610
- for (const clip of sheetClip.clips)
611
- viewClip.clips.push(clip);
612
- }
613
- else {
614
- // Clip to view attachment's bounding box
615
- viewClip.appendShape([
616
- Point3d.create(this._range.low.x, this._range.low.y),
617
- Point3d.create(this._range.high.x, this._range.low.y),
618
- Point3d.create(this._range.high.x, this._range.high.y),
619
- Point3d.create(this._range.low.x, this._range.high.y),
620
- ]);
621
- }
622
- this._clipVolume = IModelApp.renderSystem.createClipVolume(viewClip);
623
- // Save off the original frustum (potentially adjusted by viewport).
624
- this._viewport.setupFromView();
625
- this._viewport.viewingSpace.getFrustum(CoordSystem.World, true, this._originalFrustum);
626
- const applyHiddenLineSettings = true; // for debugging edge display, set to false...
627
- const style = view.displayStyle;
628
- if (style.is3d() && applyHiddenLineSettings)
629
- this._hiddenLineSettings = style.settings.hiddenLineSettings;
630
- }
631
- [Symbol.dispose]() {
632
- this._viewport[Symbol.dispose]();
633
- }
634
- discloseTileTrees(trees) {
635
- trees.disclose(this._viewport);
636
- }
637
- addToScene(context) {
638
- if (context.viewport.freezeScene)
639
- return;
640
- if (!context.viewport.view.viewsCategory(this._props.category))
641
- return;
642
- const wantBounds = context.viewport.wantViewAttachmentBoundaries;
643
- const wantClipShapes = context.viewport.wantViewAttachmentClipShapes;
644
- if (wantBounds || wantClipShapes) {
645
- const builder = context.createSceneGraphicBuilder();
646
- if (wantBounds) {
647
- builder.setSymbology(ColorDef.red, ColorDef.red, 2);
648
- builder.addRangeBox(this._range);
649
- }
650
- if (wantClipShapes && this._clipVolume) {
651
- builder.setSymbology(ColorDef.blue, ColorDef.blue, 2);
652
- for (const prim of this._clipVolume.clipVector.clips) {
653
- if (!(prim instanceof ClipShape))
654
- continue; // ###TODO handle non-shape primitives, if any such ever encountered
655
- const pts = [];
656
- const tf = prim.transformFromClip;
657
- for (const pt of prim.polygon) {
658
- const tfPt = tf ? tf.multiplyPoint3d(pt) : pt;
659
- pts.push(new Point2d(tfPt.x, tfPt.y));
660
- }
661
- builder.addLineString2d(pts, 0);
662
- }
663
- }
664
- // Put into a Batch so that we can see tooltip with attachment Id on mouseover.
665
- const batch = context.target.renderSystem.createBatch(builder.finish(), this.getDebugFeatureTable(), this._range);
666
- context.outputGraphic(batch);
667
- }
668
- if (!context.viewport.wantViewAttachments)
669
- return;
670
- // Pixel size used to compute size of ViewRect so that tiles of appropriate LOD are selected.
671
- const pixelSize = context.viewport.getPixelSizeAtPoint();
672
- if (0 === pixelSize)
673
- return;
674
- // Adjust attached view frustum based on intersection with sheet view frustum.
675
- const attachFrustum = this._originalFrustum.transformBy(this._toSheet);
676
- const attachFrustumRange = attachFrustum.toRange();
677
- const sheetFrustum = context.viewport.getWorldFrustum();
678
- const sheetFrustumRange = sheetFrustum.toRange();
679
- const intersect = attachFrustumRange.intersect(sheetFrustumRange);
680
- if (intersect.isNull)
681
- return;
682
- attachFrustum.initFromRange(intersect);
683
- attachFrustum.transformBy(this._fromSheet, attachFrustum);
684
- this._viewport.setupViewFromFrustum(attachFrustum);
685
- // Adjust view rect based on size of attachment on screen so that tiles of appropriate LOD are selected.
686
- const width = this._sizeInMeters.x * intersect.xLength() / attachFrustumRange.xLength();
687
- const height = this._sizeInMeters.y * intersect.yLength() / attachFrustumRange.yLength();
688
- this._viewRect.width = Math.max(1, Math.round(width / pixelSize));
689
- this._viewRect.height = Math.max(1, Math.round(height / pixelSize));
690
- this._viewport.setRect(this._viewRect);
691
- // Propagate settings from on-screen viewport.
692
- this._viewport.debugBoundingBoxes = context.viewport.debugBoundingBoxes;
693
- this._viewport.setTileSizeModifier(context.viewport.tileSizeModifier);
694
- // Create the scene.
695
- this._viewport.renderFrame();
696
- const scene = this.scene;
697
- if (!scene)
698
- return;
699
- // Extract scene graphics and insert into on-screen scene context.
700
- const options = {
701
- viewAttachmentId: this._props.id,
702
- clipVolume: this._clipVolume,
703
- hline: this._hiddenLineSettings,
704
- frustum: {
705
- is3d: this.view.is3d(),
706
- scale: this._scale,
707
- },
708
- };
709
- const outputGraphics = (source) => {
710
- if (0 === source.length)
711
- return;
712
- const graphics = new GraphicBranch();
713
- graphics.setViewFlagOverrides(this._viewFlagOverrides);
714
- graphics.symbologyOverrides = this.symbologyOverrides;
715
- for (const graphic of source)
716
- graphics.entries.push(graphic);
717
- const branch = context.createGraphicBranch(graphics, this._toSheet, options);
718
- context.outputGraphic(branch);
719
- };
720
- outputGraphics(scene.foreground);
721
- context.withGraphicType(TileGraphicType.BackgroundMap, () => outputGraphics(scene.background));
722
- context.withGraphicType(TileGraphicType.Overlay, () => outputGraphics(scene.overlay));
723
- // Report tile statistics to sheet view's viewport.
724
- const tileAdmin = IModelApp.tileAdmin;
725
- const selectedAndReady = tileAdmin.getTilesForUser(this._viewport);
726
- const requested = tileAdmin.getRequestsForUser(this._viewport);
727
- tileAdmin.addExternalTilesForUser(context.viewport, {
728
- requested: requested?.size ?? 0,
729
- selected: selectedAndReady?.selected.size ?? 0,
730
- ready: selectedAndReady?.ready.size ?? 0,
731
- });
732
- }
733
- getDebugFeatureTable() {
734
- if (this._debugFeatureTable)
735
- return this._debugFeatureTable;
736
- const featureTable = new FeatureTable(1, this._sheetModelId);
737
- featureTable.insert(new Feature(this._props.id));
738
- this._debugFeatureTable = PackedFeatureTable.pack(featureTable);
739
- return this._debugFeatureTable;
740
- }
741
- get areAllTileTreesLoaded() {
742
- return this.view.areAllTileTreesLoaded;
743
- }
744
- collectStatistics(_stats) {
745
- // Handled by discloseTileTrees()
746
- }
747
- get toSheet() {
748
- return this._toSheet;
749
- }
750
- }
751
- function createRasterAttachmentViewport(_view, _rect, _attachment) {
752
- class RasterAttachmentViewport extends OffScreenViewport {
753
- _sceneContext;
754
- _isSceneReady = false;
755
- _attachment;
756
- constructor(view, rect, attachment) {
757
- super(IModelApp.renderSystem.createOffscreenTarget(rect));
758
- this._attachment = attachment;
759
- this._isAspectRatioLocked = true;
760
- this.changeView(view);
761
- }
762
- createSceneContext() {
763
- assert(!this._isSceneReady);
764
- this._sceneContext = super.createSceneContext();
765
- return this._sceneContext;
766
- }
767
- renderFrame() {
768
- assert(!this._isSceneReady);
769
- this.clearSceneContext();
770
- super.renderFrame();
771
- if (undefined !== this._sceneContext) {
772
- this._isSceneReady = !this._sceneContext.hasMissingTiles && this.view.areAllTileTreesLoaded;
773
- if (this._isSceneReady)
774
- this._attachment.produceGraphics(this._sceneContext);
775
- this._sceneContext = undefined;
776
- }
777
- }
778
- clearSceneContext() {
779
- this._sceneContext = undefined;
780
- }
781
- addDecorations(_decorations) {
782
- // ###TODO: skybox, ground plane, possibly grid. DecorateContext requires a ScreenViewport...
783
- }
784
- }
785
- return new RasterAttachmentViewport(_view, _rect, _attachment);
786
- }
787
- /** Draws a 3d view with camera enabled into a sheet view by producing an image of the view's contents offscreen. */
788
- class RasterAttachment {
789
- _props;
790
- _placement;
791
- _transform;
792
- zDepth;
793
- _viewport;
794
- _graphics;
795
- constructor(view, props, sheetView) {
796
- // Render to a 2048x2048 view rect. Scale in Y to preserve aspect ratio.
797
- const maxSize = 2048;
798
- const rect = new ViewRect(0, 0, maxSize, maxSize);
799
- const height = maxSize * view.getAspectRatio() * view.getAspectRatioSkew();
800
- const skew = maxSize / height;
801
- view.setAspectRatioSkew(skew);
802
- if (true !== props.jsonProperties?.displayOptions?.preserveBackground) {
803
- // Make background color 100% transparent so that Viewport.readImageBuffer() will discard transparent pixels.
804
- const bgColor = sheetView.displayStyle.backgroundColor.withAlpha(0);
805
- view.displayStyle.backgroundColor = bgColor;
806
- }
807
- this._viewport = createRasterAttachmentViewport(view, rect, this);
808
- this._props = props;
809
- this._placement = Placement2d.fromJSON(props.placement);
810
- this._transform = this._placement.transform;
811
- this.zDepth = Frustum2d.depthFromDisplayPriority(props.jsonProperties?.displayPriority ?? 0);
812
- }
813
- [Symbol.dispose]() {
814
- this._viewport?.[Symbol.dispose]();
815
- }
816
- get viewAttachmentProps() {
817
- return this._props;
818
- }
819
- get viewport() {
820
- return this._viewport;
821
- }
822
- get areAllTileTreesLoaded() {
823
- return this._viewport?.areAllTileTreesLoaded ?? true;
824
- }
825
- addToScene(context) {
826
- // ###TODO: check viewport.wantViewAttachmentClipShapes
827
- if (!context.viewport.view.viewsCategory(this._props.category))
828
- return;
829
- if (context.viewport.wantViewAttachmentBoundaries) {
830
- const builder = context.createSceneGraphicBuilder(this._transform);
831
- builder.setSymbology(ColorDef.red, ColorDef.red, 2);
832
- builder.addRangeBox(Range3d.createRange2d(this._placement.bbox));
833
- context.outputGraphic(builder.finish());
834
- }
835
- if (!context.viewport.wantViewAttachments)
836
- return;
837
- if (this._graphics) {
838
- context.outputGraphic(this._graphics);
839
- return;
840
- }
841
- if (undefined === this._viewport)
842
- return;
843
- this._viewport.debugBoundingBoxes = context.viewport.debugBoundingBoxes;
844
- this._viewport.setTileSizeModifier(context.viewport.tileSizeModifier);
845
- this._viewport.renderFrame();
846
- if (this._graphics)
847
- context.outputGraphic(this._graphics);
848
- }
849
- discloseTileTrees(trees) {
850
- if (this._viewport)
851
- trees.disclose(this._viewport);
852
- }
853
- produceGraphics(context) {
854
- assert(context.viewport === this._viewport);
855
- this._graphics = this.createGraphics(this._viewport);
856
- this._viewport = dispose(this._viewport);
857
- if (undefined !== this._graphics)
858
- context.outputGraphic(this._graphics);
859
- }
860
- createGraphics(vp) {
861
- // Create a texture from the contents of the view.
862
- const image = vp.readImageBuffer({ upsideDown: true });
863
- if (undefined === image)
864
- return undefined;
865
- const debugImage = false; // set to true to open a window displaying the captured image.
866
- if (debugImage) {
867
- const url = imageBufferToPngDataUrl(image, false);
868
- if (url)
869
- openImageDataUrlInNewWindow(url, "Attachment");
870
- }
871
- const texture = IModelApp.renderSystem.createTexture({
872
- image: { source: image, transparency: TextureTransparency.Opaque },
873
- });
874
- if (!texture)
875
- return undefined;
876
- // Create a material for the texture
877
- const graphicParams = new GraphicParams();
878
- graphicParams.material = IModelApp.renderSystem.createRenderMaterial({ textureMapping: { texture } });
879
- // Apply the texture to a rectangular polyface.
880
- const depth = this.zDepth;
881
- const east = this._placement.bbox.low.x;
882
- const west = this._placement.bbox.high.x;
883
- const north = this._placement.bbox.low.y;
884
- const south = this._placement.bbox.high.y;
885
- const corners = [
886
- Point3d.create(east, north, depth),
887
- Point3d.create(west, north, depth),
888
- Point3d.create(west, south, depth),
889
- Point3d.create(east, south, depth),
890
- ];
891
- const params = [
892
- Point2d.create(0, 0),
893
- Point2d.create(1, 0),
894
- Point2d.create(1, 1),
895
- Point2d.create(0, 1),
896
- ];
897
- const strokeOptions = new StrokeOptions();
898
- strokeOptions.needParams = strokeOptions.shouldTriangulate = true;
899
- const polyfaceBuilder = PolyfaceBuilder.create(strokeOptions);
900
- polyfaceBuilder.addQuadFacet(corners, params);
901
- const polyface = polyfaceBuilder.claimPolyface();
902
- const graphicBuilder = IModelApp.renderSystem.createGraphicBuilder(Transform.createIdentity(), GraphicType.Scene, vp, this._props.id);
903
- graphicBuilder.activateGraphicParams(graphicParams);
904
- graphicBuilder.addPolyface(polyface, false);
905
- const graphic = graphicBuilder.finish();
906
- // Wrap the polyface in a GraphicBranch.
907
- const branch = new GraphicBranch(true);
908
- const vfOvrs = createDefaultViewFlagOverrides({ clipVolume: true, shadows: false, lighting: false, thematic: false });
909
- // Disable transparency - background pixels are 100% transparent so they will be discarded anyway. Other pixels are 100% opaque.
910
- vfOvrs.transparency = false;
911
- branch.setViewFlagOverrides(vfOvrs);
912
- branch.symbologyOverrides = new FeatureSymbology.Overrides();
913
- branch.entries.push(graphic);
914
- // Apply the attachment's clip, if any.
915
- let clipVolume;
916
- if (this._props.jsonProperties?.clip) {
917
- const clipVector = ClipVector.fromJSON(this._props.jsonProperties?.clip);
918
- if (clipVector.isValid)
919
- clipVolume = IModelApp.renderSystem.createClipVolume(clipVector);
920
- }
921
- return IModelApp.renderSystem.createGraphicBranch(branch, this._transform, { clipVolume });
922
- }
923
- collectStatistics(stats) {
924
- if (this._graphics)
925
- this._graphics.collectStatistics(stats);
294
+ return this._viewAttachments.computeDisplayTransform(args);
926
295
  }
927
296
  }
928
297
  //# sourceMappingURL=SheetViewState.js.map