@vitrosoftware/common-ui-ts 1.1.121 → 1.1.123

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 (135) hide show
  1. package/css/std/controls/checkbox/checkbox.css +4 -0
  2. package/css/std/controls/checkbox/img/checkbox-indeterminate.svg +4 -0
  3. package/css/std/controls/date-picker/date-picker.css +1 -25
  4. package/css/std/controls/dxf-viewer/annotation.css +85 -0
  5. package/css/std/controls/dxf-viewer/common.css +24 -0
  6. package/css/std/controls/dxf-viewer/dxf-viewer-index.css +14081 -0
  7. package/css/std/controls/dxf-viewer/dxf-viewer.css +194 -0
  8. package/css/std/controls/dxf-viewer/img/cancel-dark-grey.svg +5 -0
  9. package/css/std/controls/dxf-viewer/img/collapse-bottom.svg +5 -0
  10. package/css/std/controls/dxf-viewer/img/collapse-up-blue.svg +5 -0
  11. package/css/std/controls/dxf-viewer/img/delete-active.svg +11 -0
  12. package/css/std/controls/dxf-viewer/img/delete.svg +11 -0
  13. package/css/std/controls/dxf-viewer/img/draw-annotation.svg +3 -0
  14. package/css/std/controls/dxf-viewer/img/invisible-eye.svg +4 -0
  15. package/css/std/controls/dxf-viewer/img/show-annotation.svg +3 -0
  16. package/css/std/controls/dxf-viewer/img/sidebar-layers-toggle.svg +6 -0
  17. package/css/std/controls/dxf-viewer/img/sidebar-notes-toggle.svg +5 -0
  18. package/css/std/controls/dxf-viewer/img/sidebar-resizer.svg +6 -0
  19. package/css/std/controls/dxf-viewer/img/sidebar-toggle.svg +7 -0
  20. package/css/std/controls/dxf-viewer/img/visible-eye.svg +4 -0
  21. package/css/std/controls/dxf-viewer/img/zoom-in.svg +6 -0
  22. package/css/std/controls/dxf-viewer/img/zoom-out.svg +5 -0
  23. package/css/std/controls/dxf-viewer/layer-list.css +104 -0
  24. package/css/std/controls/dxf-viewer/panel.css +34 -0
  25. package/css/std/controls/dxf-viewer/prop-inspector.css +102 -0
  26. package/css/std/controls/dxf-viewer/select.css +111 -0
  27. package/css/std/controls/dxf-viewer/sidebar.css +190 -0
  28. package/css/std/controls/dxf-viewer/thumbnail-list.css +65 -0
  29. package/css/std/controls/dxf-viewer/toolbar.css +117 -0
  30. package/css/std/controls/dxf-viewer/treeview.css +3 -0
  31. package/css/std/controls/dxf-viewer/treeview.panel.css +108 -0
  32. package/css/std/controls/error-message/error-message.css +22 -0
  33. package/css/std/controls/image-picker/image-picker.css +0 -26
  34. package/css/std/controls/input/input.css +1 -24
  35. package/css/std/controls/issue-tile/issue-tile-header.css +1 -0
  36. package/css/std/controls/login/ntlm-authentication-form.css +9 -12
  37. package/css/std/controls/lookup-picker/lookup-picker-value-list.css +38 -2
  38. package/css/std/controls/lookup-picker/lookup-picker.css +1 -25
  39. package/css/std/controls/table-view/treegrid-context-menu.css +44 -18
  40. package/css/std/controls/table-view/treegrid-message.css +4 -4
  41. package/css/std/controls/time-picker/time-picker.css +1 -25
  42. package/dist/index.css +81 -143
  43. package/dist/index.js +15137 -489
  44. package/dist/index.js.map +1 -1
  45. package/dist/src/controls/Checkbox/Checkbox.d.ts +1 -0
  46. package/dist/src/controls/DxfViewer/DxfViewer.d.ts +6 -0
  47. package/dist/src/controls/DxfViewer/DxfViewerContext.d.ts +31 -0
  48. package/dist/src/controls/DxfViewer/Layer.d.ts +9 -0
  49. package/dist/src/controls/DxfViewer/LayerList.d.ts +11 -0
  50. package/dist/src/controls/DxfViewer/Thumbnail.d.ts +7 -0
  51. package/dist/src/controls/DxfViewer/ThumbnailList.d.ts +6 -0
  52. package/dist/src/controls/DxfViewer/Viewer.d.ts +6 -0
  53. package/dist/src/controls/ErrorMessage/ErrorMessage.d.ts +6 -0
  54. package/dist/src/controls/Login/FormRef.d.ts +3 -0
  55. package/dist/src/controls/Login/LoginConstants.d.ts +2 -1
  56. package/dist/src/controls/Login/LoginFormRef.d.ts +2 -2
  57. package/dist/src/controls/Login/NTLMAuthenticationForm.d.ts +5 -2
  58. package/dist/src/controls/LookupPicker/LookupPicker.d.ts +2 -0
  59. package/dist/src/controls/LookupPicker/ValueList.d.ts +2 -0
  60. package/dist/src/controls/TableView/TableViewConstants.d.ts +11 -0
  61. package/dist/src/controls/TableView/TreeGridTableViewContextImpl.d.ts +1 -0
  62. package/dist/src/controls/TreeView/TreeView.d.ts +4 -0
  63. package/dist/src/controls/TreeView/TreeViewConfig.d.ts +3 -0
  64. package/dist/src/controls/TreeView/TreeViewConstants.d.ts +2 -1
  65. package/dist/src/index.d.ts +7 -1
  66. package/lib/dxf-viewer/BatchingKey.js +91 -0
  67. package/lib/dxf-viewer/DxfFetcher.js +39 -0
  68. package/lib/dxf-viewer/DxfScene.js +2695 -0
  69. package/lib/dxf-viewer/DxfViewer.js +1056 -0
  70. package/lib/dxf-viewer/DxfWorker.js +229 -0
  71. package/lib/dxf-viewer/DynamicBuffer.js +100 -0
  72. package/lib/dxf-viewer/HatchCalculator.js +345 -0
  73. package/lib/dxf-viewer/LinearDimension.js +323 -0
  74. package/lib/dxf-viewer/MTextFormatParser.js +211 -0
  75. package/lib/dxf-viewer/MaterialKey.js +37 -0
  76. package/lib/dxf-viewer/OrbitControls.js +1253 -0
  77. package/lib/dxf-viewer/Pattern.js +94 -0
  78. package/lib/dxf-viewer/RBTree.js +471 -0
  79. package/lib/dxf-viewer/TextRenderer.js +1038 -0
  80. package/lib/dxf-viewer/index.js +42 -0
  81. package/lib/dxf-viewer/math/Matrix2.js +77 -0
  82. package/lib/dxf-viewer/math/utils.js +59 -0
  83. package/lib/dxf-viewer/parser/AutoCadColorIndex.js +265 -0
  84. package/lib/dxf-viewer/parser/DimStyleCodes.js +33 -0
  85. package/lib/dxf-viewer/parser/DxfArrayScanner.js +143 -0
  86. package/lib/dxf-viewer/parser/DxfParser.js +980 -0
  87. package/lib/dxf-viewer/parser/ExtendedDataParse-My.js +91 -0
  88. package/lib/dxf-viewer/parser/ExtendedDataParser.js +123 -0
  89. package/lib/dxf-viewer/parser/ParseHelpers.js +142 -0
  90. package/lib/dxf-viewer/parser/entities/3dface.js +83 -0
  91. package/lib/dxf-viewer/parser/entities/arc.js +38 -0
  92. package/lib/dxf-viewer/parser/entities/attdef.js +89 -0
  93. package/lib/dxf-viewer/parser/entities/attrib.js +34 -0
  94. package/lib/dxf-viewer/parser/entities/attribute.js +109 -0
  95. package/lib/dxf-viewer/parser/entities/circle.js +43 -0
  96. package/lib/dxf-viewer/parser/entities/dimension.js +72 -0
  97. package/lib/dxf-viewer/parser/entities/ellipse.js +46 -0
  98. package/lib/dxf-viewer/parser/entities/hatch.js +343 -0
  99. package/lib/dxf-viewer/parser/entities/insert.js +62 -0
  100. package/lib/dxf-viewer/parser/entities/leader.js +84 -0
  101. package/lib/dxf-viewer/parser/entities/line.js +34 -0
  102. package/lib/dxf-viewer/parser/entities/lwpolyline.js +100 -0
  103. package/lib/dxf-viewer/parser/entities/mtext.js +54 -0
  104. package/lib/dxf-viewer/parser/entities/point.js +35 -0
  105. package/lib/dxf-viewer/parser/entities/polyline.js +92 -0
  106. package/lib/dxf-viewer/parser/entities/solid.js +40 -0
  107. package/lib/dxf-viewer/parser/entities/spline.js +70 -0
  108. package/lib/dxf-viewer/parser/entities/text.js +47 -0
  109. package/lib/dxf-viewer/parser/entities/vertex.js +62 -0
  110. package/lib/dxf-viewer/parser/entities/viewport.js +56 -0
  111. package/lib/dxf-viewer/parser/objects/dictionary.js +29 -0
  112. package/lib/dxf-viewer/parser/objects/layout.js +35 -0
  113. package/lib/dxf-viewer/parser/objects/xrecord.js +29 -0
  114. package/lib/opentype/opentype.module.js +14571 -0
  115. package/lib/three/CSS2DRenderer.js +235 -0
  116. package/lib/three/three.module.js +49912 -0
  117. package/lib/xeokit/xeokit-sdk-2.6.10.es.js +136629 -0
  118. package/lib/xeokit/xeokit-sdk-2.6.10.min.es.js +299 -0
  119. package/package.json +12 -10
  120. package/src/controls/BimViewer/js/bim-viewer-models.js +93 -0
  121. package/src/controls/BimViewer/js/bim-viewer.js +194 -5
  122. package/src/controls/DxfViewer/js/dxf-viewer.js +3541 -0
  123. package/src/controls/PdfViewer/js/pdf-viewer.js +1 -1
  124. package/css/std/controls/input/img/error-message.svg +0 -6
  125. package/css/std/controls/lookup-picker/img/error-message.svg +0 -6
  126. package/css/std/controls/time-picker/img/error-message.svg +0 -6
  127. package/dist/src/controls/ActionHandler/ActionInfo.d.ts +0 -12
  128. package/dist/src/controls/ActionHandler/ActionInfoItem.d.ts +0 -13
  129. package/dist/src/controls/ActionHandler/UpdatingPopover.d.ts +0 -2
  130. package/dist/src/controls/Dialog/DialogButton.d.ts +0 -8
  131. package/dist/src/controls/Dialog/DialogCloseButton.d.ts +0 -8
  132. package/dist/src/controls/Icon/Icon.d.ts +0 -11
  133. package/dist/src/controls/Search/Input.d.ts +0 -21
  134. package/dist/src/controls/Search/SearchConstants.d.ts +0 -4
  135. /package/css/std/controls/{date-picker → error-message}/img/error-message.svg +0 -0
@@ -0,0 +1,3541 @@
1
+ import * as three from '/resource/dxfViewer/js/three/three.module.js?version=1.1.123';
2
+ import { Matrix3, Vector2 } from '/resource/dxfViewer/js/three/three.module.js?version=1.1.123';
3
+ import { Batch, DxfViewer, Layer } from '/resource/dxfViewer/js/viewer/DxfViewer.js?version=1.1.123';
4
+ import { Block as SceneBlock } from '/resource/dxfViewer/js/viewer/DxfScene.js?version=1.1.123';
5
+ import { Block } from '/resource/dxfViewer/js/viewer/DxfViewer.js?version=1.1.123';
6
+ import { DxfScene, Entity, ColorCode } from '/resource/dxfViewer/js/viewer/DxfScene.js?version=1.1.123';
7
+ import { TextRenderer, ParseSpecialChars, HAlign, VAlign } from '/resource/dxfViewer/js/viewer/TextRenderer.js?version=1.1.123';
8
+ import { DxfWorker } from '/resource/dxfViewer/js/viewer/DxfWorker.js?version=1.1.123';
9
+ import { DxfFetcher } from '/resource/dxfViewer/js/viewer/DxfFetcher.js?version=1.1.123';
10
+ import DxfParser from '/resource/dxfViewer/js/viewer/parser/DxfParser.js?version=1.1.123';
11
+ import { RenderBatch } from '/resource/dxfViewer/js/viewer/DxfScene.js?version=1.1.123';
12
+ import { DynamicBuffer, NativeType } from '/resource/dxfViewer/js/viewer/DynamicBuffer.js?version=1.1.123';
13
+ import { OrbitControls } from '/resource/dxfViewer/js/viewer/OrbitControls.js?version=1.1.123';
14
+ import { CSS2DRenderer, CSS2DObject } from '/resource/dxfViewer/js/three/three.module.js?version=1.1.123';
15
+
16
+ let treeViewData = [];
17
+ let notes = [];
18
+ let customMeta = {};
19
+ const InstanceType = Object.freeze({
20
+ /** Not instanced. */
21
+ NONE: 0,
22
+ /** Full affine transform per instance. */
23
+ FULL: 1,
24
+ /** Point instances, 2D-translation vector per instance. */
25
+ POINT: 2,
26
+
27
+ /** Number of types. */
28
+ MAX: 3
29
+ })
30
+
31
+
32
+ export class VitroBatch {
33
+ /**
34
+ * @param viewer {DxfViewer}
35
+ * @param scene Serialized scene.
36
+ * @param batch Serialized scene batch.
37
+ */
38
+ constructor(viewer, scene, batch) {
39
+ this.viewer = viewer
40
+ this.key = batch.key
41
+
42
+ if (batch.hasOwnProperty("verticesOffset")) {
43
+ const verticesArray =
44
+ new Float32Array(scene.vertices,
45
+ batch.verticesOffset * Float32Array.BYTES_PER_ELEMENT,
46
+ batch.verticesSize)
47
+ if (this.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE ||
48
+ scene.pointShapeHasDot) {
49
+ this.vertices = new three.BufferAttribute(verticesArray, 3)//vitro 2 => 3
50
+ }
51
+ if (this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
52
+ this.transforms = new three.InstancedBufferAttribute(verticesArray, 3)//vitro 2 => 3
53
+ }
54
+ }
55
+
56
+ if (batch.hasOwnProperty("chunks")) {
57
+ this.chunks = []
58
+ for (const rawChunk of batch.chunks) {
59
+
60
+ const verticesArray =
61
+ new Float32Array(scene.vertices,
62
+ rawChunk.verticesOffset * Float32Array.BYTES_PER_ELEMENT,
63
+ rawChunk.verticesSize)
64
+ const indicesArray =
65
+ new Uint16Array(scene.indices,
66
+ rawChunk.indicesOffset * Uint16Array.BYTES_PER_ELEMENT,
67
+ rawChunk.indicesSize)
68
+ this.chunks.push({
69
+ vertices: new three.BufferAttribute(verticesArray, 3),//vitro 2 => 3
70
+ indices: new three.BufferAttribute(indicesArray, 1),
71
+ handle: rawChunk.handle//vitro
72
+ })
73
+ }
74
+ }
75
+
76
+ if (batch.hasOwnProperty("transformsOffset")) {
77
+ const transformsArray =
78
+ new Float32Array(scene.transforms,
79
+ batch.transformsOffset * Float32Array.BYTES_PER_ELEMENT,
80
+ batch.transformsSize)
81
+ /* Each transform is 3x2 matrix which is split into two 3D vectors which will occupy two
82
+ * attribute slots.
83
+ */
84
+ const buf = new three.InstancedInterleavedBuffer(transformsArray, 6)
85
+ this.transforms0 = new three.InterleavedBufferAttribute(buf, 3, 0)
86
+ this.transforms1 = new three.InterleavedBufferAttribute(buf, 3, 3)
87
+ }
88
+
89
+ if (this.key.geometryType === BatchingKey.GeometryType.BLOCK_INSTANCE ||
90
+ this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
91
+
92
+ const layer = this.viewer.layers.get(this.key.layerName)
93
+ if (layer) {
94
+ this.layerColor = layer.color
95
+ } else {
96
+ this.layerColor = 0
97
+ }
98
+ }
99
+ }
100
+
101
+ GetInstanceType() {
102
+ switch (this.key.geometryType) {
103
+ case BatchingKey.GeometryType.BLOCK_INSTANCE:
104
+ return InstanceType.FULL
105
+ case BatchingKey.GeometryType.POINT_INSTANCE:
106
+ return InstanceType.POINT
107
+ default:
108
+ return InstanceType.NONE
109
+ }
110
+ }
111
+
112
+ /** Create scene objects corresponding to batch data.
113
+ * @param instanceBatch {?Batch} Batch with instance transform. Null for non-instanced object.
114
+ */
115
+ *CreateObjects(instanceBatch = null) {
116
+ if (this.key.geometryType === BatchingKey.GeometryType.BLOCK_INSTANCE ||
117
+ this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
118
+
119
+ if (instanceBatch !== null) {
120
+ throw new Error("Unexpected instance batch specified for instance batch")
121
+ }
122
+ yield* this._CreateBlockInstanceObjects()
123
+ return
124
+ }
125
+ yield* this._CreateObjects(instanceBatch)
126
+ }
127
+
128
+ *_CreateObjects(instanceBatch) {
129
+ const color = instanceBatch ?
130
+ instanceBatch._GetInstanceColor(this.key.color) : this.key.color
131
+
132
+ //XXX line type
133
+ const materialFactory =
134
+ this.key.geometryType === BatchingKey.GeometryType.POINTS ||
135
+ this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE ?
136
+ this.viewer._GetSimplePointMaterial : this.viewer._GetSimpleColorMaterial
137
+
138
+ const material = materialFactory.call(this.viewer, this.viewer._TransformColor(color),
139
+ instanceBatch?.GetInstanceType() ?? InstanceType.NONE)
140
+
141
+ let objConstructor
142
+ switch (this.key.geometryType) {
143
+ case BatchingKey.GeometryType.POINTS:
144
+ /* This method also called for creating dots for shaped point instances. */
145
+ case BatchingKey.GeometryType.POINT_INSTANCE:
146
+ objConstructor = three.Points
147
+ break
148
+ case BatchingKey.GeometryType.LINES:
149
+ case BatchingKey.GeometryType.INDEXED_LINES:
150
+ objConstructor = three.LineSegments
151
+ break
152
+ case BatchingKey.GeometryType.TRIANGLES:
153
+ case BatchingKey.GeometryType.INDEXED_TRIANGLES:
154
+ objConstructor = three.Mesh
155
+ break
156
+ default:
157
+ throw new Error("Unexpected geometry type:" + this.key.geometryType)
158
+ }
159
+
160
+ function CreateObject(vertices, chunk) {
161
+ const geometry = instanceBatch ?
162
+ new three.InstancedBufferGeometry() : new three.BufferGeometry()
163
+ geometry.setAttribute("position", vertices)
164
+ instanceBatch?._SetInstanceTransformAttribute(geometry)
165
+ if (chunk.indices) {
166
+ geometry.setIndex(chunk.indices)
167
+ }
168
+ const obj = new objConstructor(geometry, material)
169
+ obj.frustumCulled = false
170
+ obj.matrixAutoUpdate = false
171
+ obj.name = chunk.handle
172
+ return obj
173
+ }
174
+
175
+ if (this.chunks) {
176
+ for (const chunk of this.chunks) {
177
+ yield CreateObject(chunk.vertices, chunk)
178
+ }
179
+ } else {
180
+ yield CreateObject(this.vertices)
181
+ }
182
+ }
183
+
184
+ /**
185
+ * @param geometry {InstancedBufferGeometry}
186
+ */
187
+ _SetInstanceTransformAttribute(geometry) {
188
+ if (!geometry.isInstancedBufferGeometry) {
189
+ throw new Error("InstancedBufferGeometry expected")
190
+ }
191
+ if (this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
192
+ geometry.setAttribute("instanceTransform", this.transforms)
193
+ } else {
194
+ geometry.setAttribute("instanceTransform0", this.transforms0)
195
+ geometry.setAttribute("instanceTransform1", this.transforms1)
196
+ }
197
+ }
198
+
199
+ *_CreateBlockInstanceObjects() {
200
+ const blockName = this.key.instanceName ? this.key.instanceName : this.key.blockName
201
+ const block = this.viewer.blocks.get(blockName)
202
+ if (!block) {
203
+ return
204
+ }
205
+ for (const batch of block.batches) {
206
+ yield* batch.CreateObjects(this)
207
+ }
208
+ if (this.hasOwnProperty("vertices")) {
209
+ /* Dots for point shapes. */
210
+ yield* this._CreateObjects()
211
+ }
212
+ }
213
+
214
+ /**
215
+ * @param defColor {number} Color value for block definition batch.
216
+ * @return {number} RGB color value for a block instance.
217
+ */
218
+ _GetInstanceColor(defColor) {
219
+ if (defColor === ColorCode.BY_BLOCK) {
220
+ return this.key.color
221
+ } else if (defColor === ColorCode.BY_LAYER) {
222
+ return this.layerColor
223
+ } else {
224
+ return defColor
225
+ }
226
+ }
227
+ }
228
+ /** Key for render batches. */
229
+ export class BatchingKey {
230
+ /**
231
+ * Components order matters for lookup by prefix.
232
+ * @param layerName {?String} Layer name, null if not bound to a layer (e.g. block definition).
233
+ * @param blockName {?String} Block name if applicable. If specified and geometryType is not
234
+ * BLOCK_INSTANCE, the batch is part of block definition. Otherwise it is block instance.
235
+ * @param geometryType {?number} One of BatchingKey.GeometryType.
236
+ * @param color {number} Color ARGB value.
237
+ * @param lineType {?number} Line type ID, null for non-lines. Zero is default type (solid
238
+ * line).
239
+ */
240
+ constructor(layerName, blockName, geometryType, color, lineType) {
241
+ this.layerName = layerName ?? null
242
+ this.blockName = blockName ?? null
243
+ this.geometryType = geometryType ?? null
244
+ this.color = color
245
+ this.lineType = lineType ?? null
246
+ }
247
+
248
+ /** Comparator function. Fields lexical order corresponds to the constructor arguments order.
249
+ * Null values are always first.
250
+ */
251
+ Compare(other) {
252
+ let c = CompareValues(this.layerName, other.layerName)
253
+ if (c !== 0) {
254
+ return c
255
+ }
256
+ c = CompareValues(this.blockName, other.blockName)
257
+ if (c !== 0) {
258
+ return c
259
+ }
260
+ c = CompareValues(this.instanceName, other.instanceName)
261
+ if (c !== 0) {
262
+ return c
263
+ }
264
+ c = CompareValues(this.geometryType, other.geometryType)
265
+ if (c !== 0) {
266
+ return c
267
+ }
268
+ c = CompareValues(this.color, other.color)
269
+ if (c !== 0) {
270
+ return c
271
+ }
272
+ return CompareValues(this.lineType, other.lineType)
273
+ }
274
+
275
+ IsIndexed() {
276
+ return this.geometryType === BatchingKey.GeometryType.INDEXED_LINES ||
277
+ this.geometryType === BatchingKey.GeometryType.INDEXED_TRIANGLES ||
278
+ this.geometryType === BatchingKey.GeometryType.POINTS
279
+ }
280
+
281
+ IsInstanced() {
282
+ return this.geometryType === BatchingKey.GeometryType.BLOCK_INSTANCE ||
283
+ this.geometryType === BatchingKey.GeometryType.POINT_INSTANCE
284
+ }
285
+ }
286
+
287
+ BatchingKey.GeometryType = Object.freeze({
288
+ POINTS: 0,
289
+ LINES: 1,
290
+ INDEXED_LINES: 2,
291
+ TRIANGLES: 3,
292
+ INDEXED_TRIANGLES: 4,
293
+ BLOCK_INSTANCE: 5,
294
+ /** Shaped point instances. */
295
+ POINT_INSTANCE: 6
296
+ })
297
+
298
+ /** Comparator function for arbitrary types. Null is always first. This is used just to make some
299
+ * ordering for keys in tree structures, so no locale-aware string comparison.
300
+ */
301
+ export function CompareValues(v1, v2) {
302
+ if (v1 === null) {
303
+ if (v2 === null) {
304
+ return 0
305
+ }
306
+ return -1
307
+ }
308
+ if (v2 === null) {
309
+ return 1
310
+ }
311
+ if (v1 < v2) {
312
+ return -1
313
+ }
314
+ if (v1 > v2) {
315
+ return 1
316
+ }
317
+ return 0
318
+ }
319
+
320
+ const MODEL_SPACE = "*Model_Space"
321
+ const PAPER_SPACE = "*Paper_Space"
322
+
323
+ export function IsSpace(name) {
324
+ return name && (name.indexOf(MODEL_SPACE) == 0 || name.indexOf(PAPER_SPACE) == 0)
325
+ }
326
+
327
+ export function IsModelSpace(name) {
328
+ return name && name.indexOf(MODEL_SPACE) == 0
329
+ }
330
+
331
+ // JavaScript source code
332
+ //export class VitroBlock extends Block {
333
+ export class VitroBlock extends SceneBlock {
334
+ /** @param data {{}} Raw DXF entity. */
335
+ constructor(data) {
336
+ super(data)
337
+ if (!this.HasEntities()) {
338
+ this.GetEntitiesFromXref(data)
339
+ }
340
+ }
341
+
342
+ /** Set block flattening flag based on usage statistics.
343
+ * @return {Boolean} New flatten flag state.
344
+ */
345
+ SetFlatten() {
346
+ if (!this.HasGeometry()) {
347
+ return false
348
+ }
349
+ /* Flatten if a block is used once (pure optimization if shares its layer with other
350
+ * geometry) or if total instanced vertices number is less than a threshold (trade some
351
+ * space for draw calls number).
352
+ */
353
+ this.flatten = true;//vitro
354
+ return this.flatten
355
+ }
356
+
357
+
358
+ HasEntities() {
359
+ return this.data.hasOwnProperty("entities")
360
+ }
361
+
362
+ IsSpace() {
363
+ return IsSpace(this.data.name)
364
+ }
365
+
366
+ async GetEntitiesFromXref(data) {
367
+ if (data.xrefPath) {
368
+ const url = data.xrefPath.replace('.dwg', '.dxf').replace('\\', '/')
369
+ var filename = url.substring(url.lastIndexOf('/') + 1)
370
+ const dxf = window.dxfViewer.xrefMap[filename]
371
+ this.data.entities = []
372
+ for (const entity of dxf.entities) {
373
+ if (!entity.inPaperSpace) {
374
+ this.data.entities.push(entity)
375
+ }
376
+ }
377
+ }
378
+ }
379
+ }
380
+ export class VitroDxfScene extends DxfScene {
381
+
382
+ constructor(options) {
383
+ super(options)
384
+
385
+ this.origins = new Map()
386
+ /* Indexed by block referense handle, value is Insert. */
387
+ this.inserts = new Map()
388
+ this.bounds = new Map()
389
+ this.overlays = new Map()
390
+ }
391
+
392
+ async Build(dxf, xrefMap, fontFetchers) {
393
+ const header = dxf.header || {}
394
+
395
+ for (const [name, value] of Object.entries(header)) {
396
+ if (name.startsWith("$")) {
397
+ this.vars.set(name.slice(1), value)
398
+ }
399
+ }
400
+
401
+ /* Zero angle direction, 0 is +X. */
402
+ this.angBase = this.vars.get("ANGBASE") ?? 0
403
+ /* 0 - CCW, 1 - CW */
404
+ this.angDir = this.vars.get("ANGDIR") ?? 0
405
+ this.pdSize = this.vars.get("PDSIZE") ?? 0
406
+ this.isMetric = (this.vars.get("MEASUREMENT") ?? 1) == 1
407
+
408
+ this._ProcessLayers(dxf, xrefMap)
409
+
410
+ if (dxf.tables && dxf.tables.dimstyle) {
411
+ for (const [, style] of Object.entries(dxf.tables.dimstyle.dimStyles)) {
412
+ this.dimStyles.set(style.name, style)
413
+ }
414
+ }
415
+
416
+ if (dxf.tables && dxf.tables.style) {
417
+ for (const [, style] of Object.entries(dxf.tables.style.styles)) {
418
+ this.fontStyles.set(style.styleName, style);
419
+ }
420
+ }
421
+
422
+ this._ProcessBlocks(dxf, xrefMap)
423
+
424
+ this.textRenderer = new TextRenderer(fontFetchers, this.options.textOptions)
425
+ this.hasMissingChars = false
426
+ await this._FetchFonts(dxf)
427
+
428
+ /* Scan all entities to analyze block usage statistics and create viewport vertices. */
429
+ this.layouts = []
430
+ this.viewports = []
431
+ this._ProcessInserts(dxf)
432
+ this._ProcessEntities(dxf)
433
+ this._ProcessLayouts(dxf)
434
+
435
+ this.scene = this._BuildScene()
436
+ this.scene.layouts = this.layouts
437
+ this.scene.tileMode = dxf.header["$TILEMODE"] || 0
438
+
439
+ delete this.batches
440
+ delete this.layers
441
+ delete this.blocks
442
+ delete this.textRenderer
443
+ delete this.layouts
444
+ delete this.viewports
445
+ delete this.origins
446
+ delete this.bounds
447
+ delete this.overlays
448
+ }
449
+
450
+ _ProcessLayers(dxf, xrefMap) {
451
+ if (xrefMap) {
452
+ for (const [, xref] of Object.entries(xrefMap)) {
453
+ if (xref.tables && xref.tables.layer) {
454
+ this._ProcessLayersMap(xref.tables.layer.layers)
455
+ }
456
+ }
457
+ }
458
+ if (dxf.tables && dxf.tables.layer) {
459
+ this._ProcessLayersMap(dxf.tables.layer.layers)
460
+ }
461
+ }
462
+
463
+ _ProcessLayersMap(layers) {
464
+ for (const [, layer] of Object.entries(layers)) {
465
+ const ind1 = layer.name.indexOf('|')
466
+ const ind2 = layer.name.lastIndexOf('|')
467
+ if (ind1 === ind2) {
468
+ layer.displayName = ParseSpecialChars(layer.name)
469
+ this.layers.set(layer.name, layer)
470
+ }
471
+ }
472
+ }
473
+
474
+ _ProcessBlocks(dxf, xrefMap) {
475
+ if (xrefMap) {
476
+ for (const [name, xref] of Object.entries(xrefMap)) {
477
+ if (xref.blocks) {
478
+ this._ProcessBlocksMap(xref.blocks, name)
479
+ }
480
+ }
481
+ }
482
+
483
+ if (dxf.blocks) {
484
+ this._ProcessBlocksMap(dxf.blocks)
485
+ }
486
+ }
487
+
488
+ _ProcessBlocksMap(blocks, xrefName) {
489
+ const prefix = xrefName ? xrefName.substring(0, xrefName.lastIndexOf('.')) : null
490
+ for (const [, block] of Object.entries(blocks)) {
491
+ this.blocks.set(block.name, new VitroBlock(block))
492
+ if (prefix && this._IsOverlay(block)) {
493
+ this.overlays.set(prefix + "|" + block.name, new VitroBlock(block.name))//register overlay
494
+ }
495
+ }
496
+ }
497
+
498
+ _IsOverlay(block) {
499
+ return block.type & 8
500
+ }
501
+
502
+
503
+ _ProcessLayouts(dxf) {
504
+ for (const layout of this.layouts) {
505
+ layout.origin = this.origins.get(layout.spaceHandle)
506
+ layout.bounds = this.bounds.get(layout.spaceHandle)
507
+ for (const viewport of this.viewports) {
508
+ if (layout.spaceHandle === viewport.ownerHandle) {
509
+ layout.viewports.set(viewport.status, viewport)
510
+ }
511
+ }
512
+
513
+ const blockCtx = layout.block.DefinitionContext()
514
+ const entities = layout.block.data.entities ? layout.block.data.entities : dxf.entities
515
+ for (const viewport of layout.viewports.values()) {
516
+ this._CreateViewportVertices(viewport, entities)
517
+ this._ProcessDxfEntity(viewport, blockCtx)
518
+ }
519
+ }
520
+ }
521
+
522
+ _ProcessEntities(dxf) {
523
+ for (const block of this.blocks.values()) {
524
+ if (block.IsSpace() && block.HasEntities()) {//Process only Spaces next
525
+ const blockCtx = block.DefinitionContext()
526
+ for (const entity of block.data.entities) {
527
+ this._ProcessDxfEntity(entity, blockCtx)
528
+ }
529
+ }
530
+ this._CreateLayout(dxf, block)
531
+ }
532
+ console.log(`${this.numBlocksFlattened} blocks flattened`)
533
+
534
+ for (const entity of dxf.entities) {
535
+ const blockCtx = this._CreateSpaceBlockConext(entity)
536
+ this._ProcessDxfEntity(entity, blockCtx)
537
+ }
538
+ }
539
+
540
+ _ProcessInserts(dxf) {
541
+ /* Scan all entities to analyze block usage statistics. */
542
+ for (const entity of dxf.entities) {
543
+ if (entity.type === "INSERT") {
544
+ this.inserts.set(entity.handle, entity)
545
+ const block = this.blocks.get(entity.name)
546
+ block?.RegisterInsert(entity)
547
+
548
+ } else if (entity.type == "DIMENSION") {
549
+ if ((entity.block ?? null) !== null) {
550
+ const block = this.blocks.get(entity.block)
551
+ block?.RegisterInsert(entity)
552
+ }
553
+ }
554
+ }
555
+
556
+ for (const block of this.blocks.values()) {
557
+ if (block.HasEntities()) {
558
+ const blockCtx = block.DefinitionContext()
559
+ for (const entity of block.data.entities) {
560
+ if (block.IsSpace()) {
561
+ if (entity.type === "INSERT") {//analyze block usage statistics
562
+ this.inserts.set(entity.handle, entity)
563
+ const blkDef = this.blocks.get(entity.name)
564
+ blkDef?.RegisterInsert(entity)
565
+ }
566
+ }
567
+ else {//Process only BlockDefinition first
568
+ this._ProcessDxfEntity(entity, blockCtx, block.data)
569
+ }
570
+ }
571
+ }
572
+ if (block.SetFlatten()) {
573
+ this.numBlocksFlattened++
574
+ }
575
+ }
576
+ }
577
+
578
+
579
+ _CreateViewportVertices(viewport, entities) {
580
+ if (viewport.id === 1)//exlude main viewports
581
+ return;
582
+ if (viewport.boundaryHandle) {
583
+ for (const entity of entities) {
584
+ if (entity.handle === viewport.boundaryHandle) {
585
+ viewport.vertices = entity.vertices
586
+ break
587
+ }
588
+ }
589
+ return
590
+ }
591
+
592
+ const vertex0 = { x: viewport.center.x - viewport.width / 2, y: viewport.center.y - viewport.height / 2, z: viewport.center.z }
593
+ viewport.vertices.push(vertex0)
594
+ const vertex1 = Object.assign({}, vertex0)
595
+ vertex1.y += viewport.height
596
+ viewport.vertices.push(vertex1)
597
+ const vertex2 = Object.assign({}, vertex1)
598
+ vertex2.x += viewport.width
599
+ viewport.vertices.push(vertex2)
600
+ const vertex3 = Object.assign({}, vertex2)
601
+ vertex3.y -= viewport.height
602
+ viewport.vertices.push(vertex3)
603
+ const vertex4 = Object.assign({}, vertex3)
604
+ vertex4.x -= viewport.width
605
+ viewport.vertices.push(vertex4)
606
+ };
607
+
608
+ _CreateLayout(dxf, block) {
609
+ for (const object of dxf.objects) {
610
+ if (object.type === "LAYOUT" && object.spaceHandle === block.data.ownerHandle) {
611
+ const layout = {
612
+ block: block,
613
+ name: object.name,
614
+ space: block.data.name,
615
+ tabOrder: object.tabOrder,
616
+ spaceHandle: object.spaceHandle,
617
+ viewports: new Map()
618
+ }
619
+ for (let i = 1; i < this.layouts.length; i++) {
620
+ if (this.layouts[i].tabOrder > layout.tabOrder) {
621
+ this.layouts.splice(i, 0, layout)
622
+ return
623
+ }
624
+ }
625
+ this.layouts.push(layout)
626
+ return
627
+ }
628
+ }
629
+ }
630
+
631
+ _CreateSpaceBlockConext(entity) {
632
+ const block = this._FindSpaceBlock(entity.ownerHandle)
633
+ if (block) {
634
+ return block.DefinitionContext()
635
+ }
636
+
637
+ return this.layouts[0].block.DefinitionContext()//if not found -> to Model Space
638
+ }
639
+
640
+ _FindSpaceBlock(ownerHandle) {
641
+ for (const block of this.blocks.values()) {
642
+ if (block.IsSpace() && block.data.ownerHandle === ownerHandle) {
643
+ return block
644
+ }
645
+ }
646
+
647
+ const insert = this.inserts.get(ownerHandle)
648
+ if (insert) {
649
+ return this._FindSpaceBlock(insert.ownerHandle)
650
+ }
651
+ }
652
+
653
+ _IsSpaceContext(blockCtx) {
654
+ return !blockCtx || blockCtx.block.IsSpace()
655
+ }
656
+
657
+
658
+ _CreatePointShapeBlock() {//to override to new VitroBlock
659
+ if (this.pointShapeBlock) {
660
+ return
661
+ }
662
+ /* This mimics DXF block entity. */
663
+ this.pointShapeBlock = new VitroBlock({
664
+ name: POINT_SHAPE_BLOCK_NAME,
665
+ position: { x: 0, y: 0 }
666
+ })
667
+ /* Fix block origin at zero. */
668
+ this.pointShapeBlock.offset = new Vector2(0, 0)
669
+ const blockCtx = this.pointShapeBlock.DefinitionContext()
670
+
671
+ const markType = this.pdMode & PdMode.MARK_MASK
672
+ if (markType !== PdMode.DOT && markType !== PdMode.NONE) {
673
+ const vertices = []
674
+ this._CreatePointMarker(vertices, markType)
675
+ const entity = new Entity({
676
+ type: Entity.Type.LINE_SEGMENTS,
677
+ vertices,
678
+ color: ColorCode.BY_BLOCK
679
+ })
680
+ this._ProcessEntity(entity, blockCtx)
681
+ }
682
+
683
+ if (this.pdMode & PdMode.SQUARE) {
684
+ const r = this.pdSize * 0.5
685
+ const vertices = [
686
+ { x: -r, y: r },
687
+ { x: r, y: r },
688
+ { x: r, y: -r },
689
+ { x: -r, y: -r }
690
+ ]
691
+ const entity = new Entity({
692
+ type: Entity.Type.POLYLINE, vertices,
693
+ color: ColorCode.BY_BLOCK,
694
+ shape: true
695
+ })
696
+ this._ProcessEntity(entity, blockCtx)
697
+ }
698
+ if (this.pdMode & PdMode.CIRCLE) {
699
+ const vertices = []
700
+ this._GenerateArcVertices({
701
+ vertices, center: { x: 0, y: 0 },
702
+ radius: this.pdSize * 0.5,
703
+ tessellationAngle: POINT_CIRCLE_TESSELLATION_ANGLE
704
+ })
705
+ const entity = new Entity({
706
+ type: Entity.Type.POLYLINE, vertices,
707
+ color: ColorCode.BY_BLOCK,
708
+ shape: true
709
+ })
710
+ this._ProcessEntity(entity, blockCtx)
711
+ }
712
+ }
713
+
714
+ _ProcessDxfEntity(entity, blockCtx = null, mainEntity = null) {
715
+ let renderEntities
716
+ switch (entity.type) {
717
+ case "LINE":
718
+ renderEntities = this._DecomposeLine(entity, blockCtx)
719
+ break
720
+ case "POLYLINE":
721
+ case "LWPOLYLINE":
722
+ case "LEADER":
723
+ renderEntities = this._DecomposePolyline(entity, blockCtx)
724
+ break
725
+ case "VIEWPORT":
726
+ if (entity.vertices && entity.vertices.length > 0) {
727
+ renderEntities = this._DecomposePolyline(entity, blockCtx)
728
+ }
729
+ else {
730
+ this.viewports.push(entity)//to process later
731
+ return
732
+ }
733
+ break
734
+ case "ARC":
735
+ renderEntities = this._DecomposeArc(entity, blockCtx)
736
+ break
737
+ case "CIRCLE":
738
+ renderEntities = this._DecomposeCircle(entity, blockCtx)
739
+ break
740
+ case "ELLIPSE":
741
+ renderEntities = this._DecomposeEllipse(entity, blockCtx)
742
+ break
743
+ case "POINT":
744
+ renderEntities = this._DecomposePoint(entity, blockCtx)
745
+ break
746
+ case "SPLINE":
747
+ renderEntities = this._DecomposeSpline(entity, blockCtx)
748
+ break
749
+ case "INSERT":
750
+ /* Works with rendering batches without intermediate entities. */
751
+ this._ProcessInsert(entity, blockCtx, mainEntity)
752
+ return
753
+ case "ATTRIB":
754
+ renderEntities = this._DecomposeAttribute(entity, blockCtx)
755
+ break
756
+ case "TEXT":
757
+ renderEntities = this._DecomposeText(entity, blockCtx)
758
+ break
759
+ case "MTEXT":
760
+ renderEntities = this._DecomposeMText(entity, blockCtx)
761
+ break
762
+ case "3DFACE":
763
+ renderEntities = this._Decompose3DFace(entity, blockCtx)
764
+ break
765
+ case "SOLID":
766
+ renderEntities = this._DecomposeSolid(entity, blockCtx)
767
+ break
768
+ case "DIMENSION":
769
+ renderEntities = this._DecomposeDimension(entity, blockCtx)
770
+ break
771
+ default:
772
+ console.log("Unhandled entity type: " + entity.type)
773
+ return
774
+ }
775
+ for (const renderEntity of renderEntities) {
776
+ this._ProcessEntity(renderEntity, blockCtx, mainEntity ? mainEntity : entity)
777
+ }
778
+ }
779
+
780
+ /**
781
+ * @param entity {Entity}
782
+ * @param blockCtx {?BlockContext}
783
+ */
784
+ _ProcessEntity(entity, blockCtx, mainEntity) {//vitro: add @param mainEntity {Entity}
785
+ this._CheckBlockColor(entity, mainEntity)
786
+ switch (entity.type) {
787
+ case Entity.Type.POINTS:
788
+ this._ProcessPoints(entity, blockCtx, mainEntity)
789
+ break
790
+ case Entity.Type.LINE_SEGMENTS:
791
+ this._ProcessPolyline(entity, blockCtx, mainEntity)//vitro: process line as polyline
792
+ break
793
+ case Entity.Type.POLYLINE:
794
+ this._ProcessPolyline(entity, blockCtx, mainEntity)
795
+ break
796
+ case Entity.Type.TRIANGLES:
797
+ this._ProcessTriangles(entity, blockCtx, mainEntity)
798
+ break
799
+ default:
800
+ throw new Error("Unhandled entity type: " + entity.type)
801
+ }
802
+ }
803
+ /**
804
+ * @param entity {Entity}
805
+ * @param blockCtx {?BlockContext}
806
+ */
807
+ _ProcessPoints(entity, blockCtx, mainEntity) {//vitro: add @param mainEntity {Entity}
808
+ const key = new BatchingKey(entity.layer, blockCtx?.name,
809
+ BatchingKey.GeometryType.POINTS, entity.color, 0)
810
+ const batch = this._GetBatch(key)
811
+ const chunk = batch.PushChunk(entity.vertices.length, mainEntity)//vitro
812
+ for (const v of entity.vertices) {
813
+ chunk.PushVertex(this._TransformVertex(v, blockCtx))
814
+ }
815
+ for (let i = 0; i < entity.vertices.length; i++) {
816
+ chunk.PushIndex(i)
817
+ }
818
+ chunk.Finish()
819
+ }
820
+
821
+ /**
822
+ * @param entity {Entity}
823
+ * @param blockCtx {?BlockContext}
824
+ */
825
+ _ProcessPolyline(entity, blockCtx, mainEntity) {//vitro: add @param mainEntity {Entity}
826
+ if (entity.vertices.length < 2) {
827
+ return
828
+ }
829
+ /* It is more optimal to render short polylines un-indexed. Also DXF often contains
830
+ * polylines with just two points.
831
+ */
832
+ const verticesCount = entity.vertices.length
833
+ if (verticesCount == 3 && entity.shape) {//vitro == instead <= and entity.shape (only shapes with 3 vertex - will never work)
834
+ const key = new BatchingKey(entity.layer, blockCtx?.name,
835
+ BatchingKey.GeometryType.LINES, entity.color,
836
+ entity.lineType)
837
+ const batch = this._GetBatch(key)
838
+ let prev = null
839
+ for (const v of entity.vertices) {
840
+ if (prev !== null) {
841
+ batch.PushVertex(this._TransformVertex(prev, blockCtx))
842
+ batch.PushVertex(this._TransformVertex(v, blockCtx))
843
+ }
844
+ prev = v
845
+ }
846
+ if (entity.shape && verticesCount > 2) {
847
+ batch.PushVertex(this._TransformVertex(entity.vertices[verticesCount - 1], blockCtx))
848
+ batch.PushVertex(this._TransformVertex(entity.vertices[0], blockCtx))
849
+ }
850
+ return
851
+ }
852
+
853
+ const key = new BatchingKey(entity.layer, blockCtx?.name,
854
+ BatchingKey.GeometryType.INDEXED_LINES,
855
+ entity.color, entity.lineType)
856
+ const batch = this._GetBatch(key)
857
+ /* Line may be split if exceeds chunk limit. */
858
+ for (const lineChunk of entity._IterateLineChunks()) {
859
+ const chunk = batch.PushChunk(lineChunk.verticesCount, mainEntity)//vitro
860
+ for (const v of lineChunk.vertices) {
861
+ chunk.PushVertex(this._TransformVertex(v, blockCtx))
862
+ }
863
+ for (const idx of lineChunk.indices) {
864
+ chunk.PushIndex(idx)
865
+ }
866
+ chunk.Finish()
867
+ }
868
+ }
869
+
870
+ /**
871
+ * @param entity {Entity}
872
+ * @param blockCtx {?BlockContext}
873
+ */
874
+ _ProcessTriangles(entity, blockCtx = null, mainEntity = null) {//vitro add mainEntity
875
+ if (entity.vertices.length < 3) {
876
+ return
877
+ }
878
+ if (entity.indices.length % 3 !== 0) {
879
+ console.error("Unexpected size of indices array: " + entity.indices.length)
880
+ return
881
+ }
882
+ const key = new BatchingKey(entity.layer, blockCtx?.name,
883
+ BatchingKey.GeometryType.INDEXED_TRIANGLES,
884
+ entity.color, 0)
885
+ const batch = this._GetBatch(key)
886
+ //XXX splitting into chunks is not yet implemented. Currently used only for text glyphs so
887
+ // should fit into one chunk
888
+ const chunk = batch.PushChunk(entity.vertices.length, mainEntity)
889
+ for (const v of entity.vertices) {
890
+ chunk.PushVertex(this._TransformVertex(v, blockCtx))
891
+ }
892
+ for (const idx of entity.indices) {
893
+ chunk.PushIndex(idx)
894
+ }
895
+ chunk.Finish()
896
+ }
897
+
898
+ _CheckBlockColor(entity, mainEntity) {
899
+ if (entity.color === ColorCode.BY_BLOCK) {
900
+ if (mainEntity && mainEntity.type === "INSERT") {
901
+ const layer = this.layers.get(mainEntity.layer)
902
+ entity.color = layer.color
903
+ }
904
+ }
905
+ else if (entity.color === ColorCode.BY_LAYER){
906
+ if (mainEntity && this._IsSystemLayer(entity.layer)) {
907
+ const blockLayer = this.layers.get(mainEntity.layer)
908
+ entity.color = blockLayer?.color ?? 0
909
+ } else {
910
+ const layer = this.layers.get(entity.layer)
911
+ entity.color = layer?.color ?? 0
912
+ }
913
+ }
914
+ }
915
+
916
+
917
+ *_DecomposeDimension(entity, blockCtx) {
918
+ if ((entity.block ?? null) !== null && this.blocks.has(entity.block)) {
919
+ const name = entity.name
920
+ entity.name = entity.block
921
+ entity.position = { x: 0, y:0, z: 0 }
922
+ this._ProcessInsert(entity, blockCtx)
923
+ entity.name = name
924
+ return
925
+ }
926
+ super._DecomposeDimension(entity, blockCtx)
927
+ }
928
+
929
+ *_DecomposeAttr(entity, blockCtx) {
930
+ if (entity.invisible) {
931
+ return
932
+ }
933
+ yield* this._DecomposeText(entity, blockCtx)
934
+ }
935
+
936
+ /**
937
+ * Updates batches directly.
938
+ * @param entity
939
+ * @param blockCtx {?BlockContext} Nested block insert when non-null.
940
+ */
941
+ _ProcessInsert(entity, blockCtx, mainEntity) {
942
+ if (blockCtx && !blockCtx.block.IsSpace() && blockCtx.block.HasEntities()) {
943
+ //XXX handle indirect recursion
944
+ if (blockCtx.name === entity.name) {
945
+ console.warn("Recursive block reference: " + blockCtx.name)
946
+ return
947
+ }
948
+ /* Flatten nested blocks definition. */
949
+ var name = entity.name ? entity.name : entity.block
950
+ const block = this.blocks.get(name)
951
+ if (!block) {
952
+ console.warn("Unresolved nested block reference: " + entity.name)
953
+ }
954
+
955
+ var isOverlay = false
956
+ if (mainEntity && mainEntity.name) {
957
+ const fullName = mainEntity.name + "|" + name
958
+ isOverlay = this.overlays.has(fullName)
959
+ }
960
+
961
+ const nestedCtx = blockCtx.NestedBlockContext(block, entity)
962
+ if (!isOverlay && block.data.entities) {
963
+ for (const subEntity of block.data.entities) {
964
+ this._ProcessDxfEntity(subEntity, nestedCtx, entity)
965
+ }
966
+ }
967
+ return
968
+ }
969
+
970
+ var name = entity.name ? entity.name : entity.block//vitro
971
+ const block = this.blocks.get(name)
972
+ if (block === null) {
973
+ console.warn("Unresolved block reference in INSERT: " + name)
974
+ return
975
+ }
976
+ if (!block.HasGeometry()) {
977
+ return
978
+ }
979
+
980
+ const layer = this._GetEntityLayer(entity, null)
981
+ const color = this._GetEntityColor(entity, null)
982
+ const lineType = this._GetLineType(entity, null, null)
983
+ //XXX apply extrusion direction
984
+ const transform = block.InstantiationContext().GetInsertionTransform(entity)
985
+
986
+ /* Update bounding box and origin with transformed block bounds corner points. */
987
+ const bounds = block.bounds
988
+ const origin = this._UpdateBounds(new Vector2(bounds.minX, bounds.minY).applyMatrix3(transform), blockCtx)
989
+ this._UpdateBounds(new Vector2(bounds.maxX, bounds.maxY).applyMatrix3(transform), blockCtx)
990
+ this._UpdateBounds(new Vector2(bounds.minX, bounds.maxY).applyMatrix3(transform), blockCtx)
991
+ this._UpdateBounds(new Vector2(bounds.maxX, bounds.minY).applyMatrix3(transform), blockCtx)
992
+
993
+ transform.translate(-origin.x, -origin.y)
994
+ //XXX grid instancing not supported yet
995
+ if (block.flatten) {
996
+ for (const batch of block.batches) {
997
+ this._FlattenBatch(batch, layer, blockCtx.block.data.name, color, lineType, transform, entity)//vitro add entity
998
+ }
999
+ } else {
1000
+ const key = new BatchingKey(layer, blockCtx.block.data.name, BatchingKey.GeometryType.BLOCK_INSTANCE,
1001
+ color, lineType)
1002
+ key.instanceName = entity.name
1003
+ const batch = this._GetBatch(key)
1004
+ batch.PushInstanceTransform(transform)
1005
+ }
1006
+ }
1007
+
1008
+ _IsSystemLayer(name) {
1009
+ return name === "0" || name === "Defpoints"
1010
+ }
1011
+
1012
+ /** Flatten block definition batch. It is merged into suitable instant rendering batch. */
1013
+ _FlattenBatch(blockBatch, layerName, blockName, blockColor, blockLineType, transform, entity) {//vitro add entity
1014
+ const layer = this.layers.get(layerName)
1015
+ let color, lineType = 0
1016
+ if (blockBatch.key.color === ColorCode.BY_BLOCK) {
1017
+ color = blockColor
1018
+ } else if (blockBatch.key.color === ColorCode.BY_LAYER) {
1019
+ color = layer?.color ?? 0
1020
+ } else {
1021
+ color = blockBatch.key.color
1022
+ }
1023
+ //XXX line type
1024
+ const key = new BatchingKey(layerName, blockName, blockBatch.key.geometryType, color, lineType)
1025
+ const batch = this._GetBatch(key)
1026
+ batch.Merge(blockBatch, transform, entity)//vitro add entity
1027
+ }
1028
+
1029
+ /** @return {RenderBatch} */
1030
+ _GetBatch(key) {
1031
+ let batch = this.batches.find({ key })
1032
+ if (batch !== null) {
1033
+ return batch
1034
+ }
1035
+ batch = new VitroRenderBatch(key)
1036
+ this.batches.insert(batch)
1037
+ if (key.blockName !== null && !key.IsInstanced()) {
1038
+ /* Block definition batch. */
1039
+ const block = this.blocks.get(key.blockName)
1040
+ if (block) {
1041
+ block.batches.push(batch)
1042
+ }
1043
+ }
1044
+ return batch
1045
+ }
1046
+
1047
+ /** Resolve entity color.
1048
+ *
1049
+ * @param entity
1050
+ * @param blockCtx {?BlockContext}
1051
+ * @return {number} RGB color value. For block entity it also may be one of ColorCode values
1052
+ * which are resolved on block instantiation.
1053
+ */
1054
+ _GetEntityColor(entity, blockCtx = null) {
1055
+ let color = ColorCode.BY_LAYER
1056
+ if (entity.colorIndex === 0) {
1057
+ color = ColorCode.BY_BLOCK
1058
+ } else if (entity.colorIndex === 256) {
1059
+ color = ColorCode.BY_LAYER
1060
+ } else if (entity.hasOwnProperty("color")) {
1061
+ color = entity.color
1062
+ }
1063
+
1064
+ if (!this._IsSpaceContext(blockCtx)) {//vitro
1065
+ return color
1066
+ }
1067
+ if (color === ColorCode.BY_LAYER || color === ColorCode.BY_BLOCK) {
1068
+ /* BY_BLOCK is not useful when not in block so replace it by layer as well. */
1069
+ if (entity.hasOwnProperty("layer")) {
1070
+ const layer = this.layers.get(entity.layer)
1071
+ if (layer) {
1072
+ return layer.color
1073
+ }
1074
+ }
1075
+ } else {
1076
+ return color
1077
+ }
1078
+ /* Fallback to black. */
1079
+ return 0
1080
+ }
1081
+
1082
+ /** @return {?string} Layer name, null for block entity. */
1083
+ _GetEntityLayer(entity, blockCtx = null) {
1084
+ if (entity.hasOwnProperty("layer")) {
1085
+ return entity.layer
1086
+ }
1087
+ return "0"
1088
+ }
1089
+
1090
+ /**
1091
+ * Apply all necessary final transforms to a vertex before just before storing it in a rendering
1092
+ * batch.
1093
+ * @param v {{x: number, y: number}}
1094
+ * @param blockCtx {BlockContext}
1095
+ * @return {{x: number, y: number}}
1096
+ */
1097
+ _TransformVertex(v, blockCtx = null) {
1098
+ if (!this._IsSpaceContext(blockCtx)) {
1099
+ /* Block definition in block coordinates. So it should not touch bounds and origin. */
1100
+ return blockCtx.TransformVertex(v)
1101
+ }
1102
+ const origin = this._UpdateBounds(v, blockCtx)
1103
+ return { x: v.x - origin.x, y: v.y - origin.y }
1104
+ }
1105
+
1106
+ /*
1107
+ * @param blockCtx {BlockContext}
1108
+ * @param v {{x,y}} Vertex to extend bounding box with and set origin.
1109
+ */
1110
+ _UpdateBounds(v, blockCtx) {
1111
+ const handle = blockCtx.block.data.ownerHandle
1112
+ var bounds = this.bounds.get(handle)
1113
+ if (!bounds) {
1114
+ bounds = { minX: v.x, maxX: v.x, minY: v.y, maxY: v.y }
1115
+ this.bounds.set(handle, bounds)
1116
+ } else {
1117
+ if (v.x < bounds.minX) {
1118
+ bounds.minX = v.x
1119
+ } else if (v.x > bounds.maxX) {
1120
+ bounds.maxX = v.x
1121
+ }
1122
+ if (v.y < bounds.minY) {
1123
+ bounds.minY = v.y
1124
+ } else if (v.y > bounds.maxY) {
1125
+ bounds.maxY = v.y
1126
+ }
1127
+ }
1128
+ var origin = this.origins.get(handle)
1129
+ if (!origin) {
1130
+ origin = { x: v.x, y: v.y }
1131
+ //origin = { x: 0, y: 0 }//vitro
1132
+ this.origins.set(handle, origin)
1133
+ }
1134
+ return origin
1135
+ }
1136
+
1137
+ }
1138
+ export class VitroDxfWorker extends DxfWorker {
1139
+
1140
+ /** @return {Object} DxfScene serialized scene. */
1141
+ async _Load(url, fonts, options, progressCbk) {
1142
+ let fontFetchers
1143
+ if (fonts) {
1144
+ fontFetchers = this._CreateFontFetchers(fonts, progressCbk)
1145
+ } else {
1146
+ fontFetchers = []
1147
+ }
1148
+ const dxf = await new DxfFetcher(url).Fetch(progressCbk)
1149
+ if (progressCbk) {
1150
+ progressCbk("prepare", 0, null)
1151
+ }
1152
+ const dxfScene = new VitroDxfScene(options)
1153
+ if (options.buildScene === true) {
1154
+ await dxfScene.Build(dxf, options.xrefMap, fontFetchers)
1155
+ }
1156
+ return {
1157
+ scene: options.buildScene === true ? dxfScene.scene : undefined,
1158
+ dxf: options.retainParsedDxf === true ? dxf : undefined
1159
+ }
1160
+ }
1161
+
1162
+ }
1163
+ export const DrawMode = {
1164
+ ANNOTATIONS: 1
1165
+ }
1166
+
1167
+ const MessageLevel = Object.freeze({
1168
+ INFO: "info",
1169
+ WARN: "warn",
1170
+ ERROR: "error"
1171
+ })
1172
+
1173
+
1174
+ export class Viewer extends DxfViewer {
1175
+
1176
+ constructor(domContainer, options = null) {
1177
+
1178
+ if (!options) {
1179
+ options = DxfViewer.DefaultOptions
1180
+ options.autoResize = true
1181
+ options.colorCorrection = true
1182
+ options.clearColor = new three.Color("#fff")
1183
+ options.buildScene = true
1184
+ options.retainParsedDxf = true
1185
+ }
1186
+
1187
+ super(domContainer, options)
1188
+ this.canvas.addEventListener("mousemove", this._OnPointerEvent.bind(this))
1189
+ document.addEventListener("keydown", this.OnKeyDown)
1190
+
1191
+ /* Indexed by space name, value is scene instance. */
1192
+ this.spaces = new Map();
1193
+
1194
+ this.objects = []
1195
+ this.objectMap = new Map()
1196
+ this.selectedObjects = []
1197
+ this.drawMode = null
1198
+ this.drawables = []
1199
+ this.drawablePlugins = []
1200
+ this.xrefMap = new Map()
1201
+
1202
+ var mainFont = "/resource/dxfViewer/assets/fonts/Roboto-LightItalic.ttf";
1203
+ var aux1Font = "/resource/dxfViewer/assets/fonts/NotoSansDisplay-SemiCondensedLightItalic.ttf";
1204
+ var aux2Font = "/resource/dxfViewer/assets/fonts/HanaMinA.ttf";
1205
+ var aux3Font = "/resource/dxfViewer/assets/fonts/NanumGothic-Regular.ttf";
1206
+ window.fonts = [mainFont, aux1Font, aux2Font, aux3Font]
1207
+
1208
+ const Subscribe = eventName => {
1209
+ this.Subscribe(eventName, e => this.Emit("dxf-" + eventName, e))
1210
+ }
1211
+ for (const eventName of ["loaded", "cleared", "destroyed", "resized", "pointerdown",
1212
+ "pointerup", "viewChanged", "message", "added"]) {
1213
+ Subscribe(eventName)
1214
+ }
1215
+ }
1216
+
1217
+ Destroy() {
1218
+ super.Destroy();
1219
+
1220
+ this.objects = null
1221
+ this.objectMap = null
1222
+ this.selectedObjects = null
1223
+ this.drawables = null
1224
+ this.drawablePlugins = null
1225
+ this.xrefMap = null
1226
+
1227
+ window.dxfViewer = null
1228
+ }
1229
+
1230
+
1231
+ async Load(url, xrefList) {
1232
+ window.isLoading = true
1233
+ window.error = null
1234
+ try {
1235
+ if (xrefList) {
1236
+ await this.loadXrefList(xrefList)
1237
+ }
1238
+ await this.LoadInternal({
1239
+ url,
1240
+ xrefList: xrefList,
1241
+ fonts: window.fonts,
1242
+ progressCbk: this.OnProgress.bind(window),
1243
+ workerFactory: null
1244
+ })
1245
+ } catch (error) {
1246
+ console.warn(error)
1247
+ window.error = error.toString()
1248
+ } finally {
1249
+ window.isLoading = false
1250
+ window.progressText = null
1251
+ window.progress = null
1252
+ window.curProgressPhase = null
1253
+ }
1254
+ }
1255
+
1256
+ async loadXrefList(xrefList) {
1257
+ for (const xref of xrefList) {
1258
+ var fileName = xref.substring(xref.lastIndexOf('/') + 1)
1259
+ const prefix = fileName.substring(0, fileName.lastIndexOf('.'))
1260
+
1261
+ const worker = new VitroDxfWorker()
1262
+ const { scene, dxf } = await worker.Load(xref, window.fonts, { retainParsedDxf: true, buildScene: false })
1263
+ //add file prefix to layers
1264
+ if (dxf.tables && dxf.tables.layer) {
1265
+ for (const [, layer] of Object.entries(dxf.tables.layer.layers)) {
1266
+ layer.name = this._GetXrefLayerName(layer.name, prefix)
1267
+ }
1268
+ }
1269
+ //add file prefix to entities layer name
1270
+ if (dxf.entities) {
1271
+ this._ProcessXrefEntities(dxf.entities, prefix)
1272
+ }
1273
+ if (dxf.blocks) {
1274
+ for (const [, block] of Object.entries(dxf.blocks)) {
1275
+ if (block.entities) {
1276
+ this._ProcessXrefEntities(block.entities, prefix)
1277
+ }
1278
+ }
1279
+ }
1280
+
1281
+ this.xrefMap[fileName] = dxf
1282
+ await worker.Destroy()
1283
+ }
1284
+ }
1285
+
1286
+ _ProcessXrefEntities(entities, prefix) {
1287
+ for (const entity of entities) {
1288
+ entity.layer = this._GetXrefLayerName(entity.layer, prefix)
1289
+ }
1290
+ }
1291
+
1292
+ _GetXrefLayerName(name, prefix) {
1293
+ if (name != "Defpoints" && name != "0") {
1294
+ return prefix + "|" + name
1295
+ }
1296
+ return name
1297
+ }
1298
+
1299
+
1300
+ async LoadInternal({ url, fonts = null, progressCbk = null, workerFactory = null }) {
1301
+ if (url === null || url === undefined) {
1302
+ throw new Error("`url` parameter is not specified")
1303
+ }
1304
+
1305
+ this._EnsureRenderer()
1306
+
1307
+ this.Clear()
1308
+
1309
+ this.worker = this.CreateDxfWorker(workerFactory)// new DxfWorker(workerFactory ? workerFactory() : null)
1310
+ this.options.xrefMap = this.xrefMap
1311
+ const { scene, dxf } = await this.worker.Load(url, fonts, this.options, progressCbk)
1312
+ this.scene = scene
1313
+ this.dxf = dxf
1314
+ await this.worker.Destroy()
1315
+ this.worker = null
1316
+
1317
+ this.hasMissingChars = scene.hasMissingChars
1318
+
1319
+ for (const layer of scene.layers) {
1320
+ this.layers.set(layer.name, new Layer(layer.name, layer.displayName, layer.color))
1321
+ }
1322
+
1323
+ /* Load all blocks on the first pass. */
1324
+ for (const batch of scene.batches) {
1325
+ if (batch.key.blockName !== null &&
1326
+ batch.key.geometryType !== BatchingKey.GeometryType.BLOCK_INSTANCE &&
1327
+ batch.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE) {
1328
+
1329
+ let block = this.blocks.get(batch.key.blockName)
1330
+ if (!block) {
1331
+ block = new Block()
1332
+ this.blocks.set(batch.key.blockName, block)
1333
+ }
1334
+ block.PushBatch(new VitroBatch(this, scene, batch))
1335
+ }
1336
+ }
1337
+
1338
+ console.log(`DXF scene:
1339
+ ${scene.batches.length} batches,
1340
+ ${this.layers.size} layers,
1341
+ ${this.blocks.size} blocks,
1342
+ vertices ${scene.vertices.byteLength} B,
1343
+ indices ${scene.indices.byteLength} B
1344
+ transforms ${scene.transforms.byteLength} B`);
1345
+
1346
+ for (const layout of scene.layouts) {
1347
+ const space = this.CreateSpace(layout);
1348
+ this.LoadBatches();
1349
+ this.Render();
1350
+ this._Emit("loaded", { space });
1351
+ }
1352
+
1353
+ this.SwitchSpace(scene.layouts[0]);
1354
+
1355
+ if (this.hasMissingChars) {
1356
+ this._Message("Some characters cannot be properly displayed due to missing fonts",
1357
+ MessageLevel.WARN)
1358
+ }
1359
+
1360
+ if (!scene.tileMode) {
1361
+ this.SwitchSpace(scene.layouts[1])
1362
+ return
1363
+ }
1364
+ }
1365
+
1366
+ Render() {
1367
+ if (!this.spaceName)
1368
+ return
1369
+
1370
+ this._EnsureRenderer()
1371
+ const isPaperSpace = !IsModelSpace(this.spaceName)
1372
+ this.renderer.clear()
1373
+ this.renderer.autoClear = false
1374
+ this.renderer.setScissorTest(isPaperSpace)
1375
+
1376
+ const space = this.GetSpace()
1377
+ if (!space || !space.camera || !space.controls)
1378
+ return
1379
+ this.camera = space.camera
1380
+
1381
+ if (this.controls)
1382
+ this.controls.enabled = false
1383
+ this.controls = space.controls
1384
+ this.controls.enabled = true
1385
+
1386
+ let isFirstRenderVieports = false;
1387
+ if (isPaperSpace)//render viewports for current space (layout)
1388
+ {
1389
+ isFirstRenderVieports = this.RenderVieports(space);
1390
+ }
1391
+
1392
+ //render current space entities (viewport borders (for layouts) and etc.)
1393
+ this.renderer.setViewport(0, 0, this.canvasWidth, this.canvasHeight);
1394
+ this.renderer.setScissor(0, 0, this.canvasWidth, this.canvasHeight);
1395
+ this.renderer.render(space.scene, this.camera)
1396
+
1397
+ if (isFirstRenderVieports) {
1398
+ this.DoFakeZoomToRedrawScene()
1399
+ }
1400
+ }
1401
+
1402
+ CreateDxfWorker(workerFactory) {
1403
+ return new VitroDxfWorker(workerFactory ? workerFactory() : null)
1404
+ }
1405
+
1406
+ _LoadBatch(scene, batch) {
1407
+ if (!IsSpace(batch.key.blockName) &&
1408
+ batch.key.geometryType !== BatchingKey.GeometryType.BLOCK_INSTANCE &&
1409
+ batch.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE) {
1410
+ /* Block definition. */
1411
+ return
1412
+ }
1413
+ if (batch.key.blockName !== this.spaceName)
1414
+ return;
1415
+ const objects = new VitroBatch(this, scene, batch).CreateObjects()
1416
+
1417
+ const layer = this.layers.get(batch.key.layerName)
1418
+
1419
+ const space = this.GetSpace()
1420
+ for (const obj of objects) {
1421
+ space.scene.add(obj)
1422
+ this._Emit("added", { obj })
1423
+ if (layer) {
1424
+ layer.PushObject(obj)
1425
+ }
1426
+ }
1427
+ }
1428
+
1429
+ OnProgress(phase, size, totalSize) {
1430
+ if (phase !== window.curProgressPhase) {
1431
+ switch (phase) {
1432
+ case "font":
1433
+ window.progressText = "Fetching fonts..."
1434
+ break
1435
+ case "fetch":
1436
+ window.progressText = "Fetching file..."
1437
+ break
1438
+ case "parse":
1439
+ window.progressText = "Parsing file..."
1440
+ break
1441
+ case "prepare":
1442
+ window.progressText = "Preparing rendering data..."
1443
+ break
1444
+ }
1445
+ window.curProgressPhase = phase
1446
+ }
1447
+ if (totalSize === null) {
1448
+ window.progress = -1
1449
+ } else {
1450
+ window.progress = size / totalSize
1451
+ }
1452
+ }
1453
+
1454
+ Emit(eventName, event) {
1455
+ if (eventName == "dxf-added") {
1456
+ this.AddObject(event.detail.obj)
1457
+ }
1458
+ else if (eventName == "dxf-destroyed") {
1459
+ this.Destroy()
1460
+ }
1461
+ }
1462
+
1463
+ SetSize(width, height) {
1464
+ if (!this.camera) {
1465
+ this.camera = this.CreateCamera();
1466
+ }
1467
+
1468
+ super.SetSize(width, height)
1469
+ }
1470
+
1471
+ LoadBatches() {
1472
+ for (const batch of this.scene.batches) {
1473
+ this._LoadBatch(this.scene, batch)
1474
+ }
1475
+ }
1476
+
1477
+ SceneToCanvasCoord(v) {
1478
+ var width = this.canvasWidth, height = this.canvasHeight;
1479
+ var widthHalf = width / 2, heightHalf = height / 2;
1480
+ var pos = v.clone();
1481
+ pos.project(this.camera);
1482
+
1483
+ pos.x = (pos.x + 1) * widthHalf;
1484
+ pos.y = (pos.y + 1) * heightHalf;
1485
+ pos.z = 0;
1486
+ return pos
1487
+ }
1488
+
1489
+ SwitchSpace(layout) {
1490
+ this.Cancel()
1491
+ this.spaceName = layout.space
1492
+ var space = this.GetSpace()
1493
+ if (!space) {
1494
+ space = this.CreateSpace(layout)
1495
+
1496
+ this.LoadBatches()
1497
+ this._Emit("loaded", { space })
1498
+ }
1499
+ this.Render()
1500
+ }
1501
+
1502
+ CreateSpace(layout, camera) {
1503
+ var scene = new three.Scene()
1504
+ if (!camera) {
1505
+ camera = this.CreateCamera();
1506
+ }
1507
+
1508
+ this.camera = camera
1509
+ if (layout.bounds) {
1510
+ this.FitView(layout.bounds.minX - layout.origin.x, layout.bounds.maxX - layout.origin.x,
1511
+ layout.bounds.minY - layout.origin.y, layout.bounds.maxY - layout.origin.y)
1512
+ } else {
1513
+ this._Message("Empty document space", MessageLevel.WARN)
1514
+ }
1515
+ const controls = this.CreateControls()
1516
+
1517
+ const space = { scene: scene, camera: camera, controls: controls, layout: layout }
1518
+ this.spaces.set(layout.space, space)
1519
+ this.spaceName = layout.space
1520
+ return space
1521
+ }
1522
+
1523
+ CreateCamera() {
1524
+ const camera = new three.OrthographicCamera(-1, 1, 1, -1, 0.1, 2);
1525
+ camera.position.z = 1
1526
+ camera.position.x = 0
1527
+ camera.position.y = 0
1528
+ return camera
1529
+ }
1530
+
1531
+ Clear() {
1532
+ this._EnsureRenderer()
1533
+ if (this.worker) {
1534
+ this.worker.Destroy(true)
1535
+ this.worker = null
1536
+ }
1537
+ if (this.controls) {
1538
+ this.controls.dispose()
1539
+ this.controls = null
1540
+ }
1541
+ for (const space of this.spaces.values()) {
1542
+ space.scene.clear()
1543
+ }
1544
+ for (const layer of this.layers.values()) {
1545
+ layer.Dispose()
1546
+ }
1547
+ this.spaces.clear()
1548
+ this.layers.clear()
1549
+ this.blocks.clear()
1550
+ this.materials.each(e => e.material.dispose())
1551
+ this.materials.clear()
1552
+ this.SetView({ x: 0, y: 0 }, 2)
1553
+ this._Emit("cleared")
1554
+ this.Render()
1555
+ }
1556
+
1557
+ DoFakeZoomToRedrawScene() {//todo to refactor
1558
+ let wheelEvent = new WheelEvent('wheel', {
1559
+ deltaY: 1,
1560
+ deltaMode: 0
1561
+ })
1562
+ this.canvas.dispatchEvent(wheelEvent)
1563
+ wheelEvent = new WheelEvent('wheel', {
1564
+ deltaY: -1,
1565
+ deltaMode: 0,
1566
+ position: [0, 0, 0]
1567
+ })
1568
+ this.canvas.dispatchEvent(wheelEvent)
1569
+ }
1570
+
1571
+ RenderVieports(space) {
1572
+ var modelSpace = this.GetSpace("*Model_Space")
1573
+ const modelScene = modelSpace.scene
1574
+ let isFirstRender = false;
1575
+ //create temporary scenes with viewport borders to calculate viewport position and size
1576
+ for (var viewport of space.layout.viewports.values()) {
1577
+ var tmpScene = new three.Scene()
1578
+ const points = [];
1579
+ for (const vertex of viewport.vertices) {
1580
+ points.push(this.TransformVertex(vertex, space));
1581
+ }
1582
+ var geom = new three.BufferGeometry().setFromPoints(points);
1583
+ var line = new three.LineSegments(geom, new three.LineBasicMaterial({ color: 0xff00ff }));
1584
+ tmpScene.add(line);//viewport border
1585
+
1586
+ if (!viewport.camera) {
1587
+ isFirstRender = true;
1588
+ viewport.camera = this.CreateCamera();
1589
+ const center = this.TransformVertex(viewport.viewCenter, modelSpace)
1590
+ center.x += viewport.viewTarget.x
1591
+ center.y += viewport.viewTarget.y
1592
+ center.z += viewport.viewTarget.z
1593
+ const width = viewport.viewHeight * viewport.width / viewport.height
1594
+ this.SetView(center, width, viewport.viewHeight, viewport.camera)
1595
+ viewport.camera.rotation.set(0, 0, -viewport.twist / 180 * Math.PI)
1596
+ }
1597
+ this.renderer.render(tmpScene, viewport.camera)
1598
+
1599
+ //calculate viewport position and size
1600
+ var box = new three.Box3().setFromObject(line)
1601
+ var vmin = this.SceneToCanvasCoord(box.min)
1602
+ var vmax = this.SceneToCanvasCoord(box.max)
1603
+
1604
+ viewport.x = vmin.x
1605
+ viewport.y = vmin.y
1606
+ viewport.width = vmax.x - vmin.x
1607
+ viewport.height = Math.abs(vmax.y - vmin.y)
1608
+ }
1609
+ this.renderer.clear()//clear temporary scenes
1610
+
1611
+ for (var viewport of space.layout.viewports.values()) //render each viewport - clipped modelScene (main scene) using viewport position and size and his camera
1612
+ {
1613
+ if (viewport.camera) {
1614
+ this.renderer.setViewport(viewport.x, viewport.y, viewport.width, viewport.height)
1615
+ this.renderer.setScissor(viewport.x, viewport.y, viewport.width, viewport.height)
1616
+ this.renderer.render(modelScene, viewport.camera)
1617
+ }
1618
+ }
1619
+
1620
+ return isFirstRender
1621
+ }
1622
+
1623
+ GetSpace(name) {
1624
+ name = name || this.spaceName
1625
+ return this.spaces.get(name)
1626
+ }
1627
+
1628
+ GetSpaceByHandle(handle) {
1629
+ for (const space of this.spaces.values()) {
1630
+ if (space.layout.spaceHandle === handle) {
1631
+ return space
1632
+ }
1633
+ }
1634
+
1635
+ return null
1636
+ }
1637
+
1638
+
1639
+ GetLayoutBySpaceHandle(spaceHandle) {
1640
+ for (const layout of this.scene.layouts) {
1641
+ if (spaceHandle === layout.spaceHandle) {
1642
+ return layout;
1643
+ }
1644
+ }
1645
+
1646
+ return null
1647
+ }
1648
+
1649
+
1650
+ GetOrigin(layout) {
1651
+ return layout.origin
1652
+ }
1653
+
1654
+ CreateControls() {
1655
+ if (this.controls)
1656
+ this.controls.enabled = false
1657
+ const controls = this.controls = new OrbitControls(this.camera, this.canvas)
1658
+ controls.enableRotate = false
1659
+ controls.mouseButtons = {
1660
+ RIGHT: three.MOUSE.PAN,
1661
+ MIDDLE: three.MOUSE.DOLLY
1662
+ }
1663
+ controls.touches = {
1664
+ ONE: three.TOUCH.PAN,
1665
+ TWO: three.TOUCH.DOLLY_PAN
1666
+ }
1667
+ controls.zoomSpeed = 3
1668
+ controls.mouseZoomSpeedFactor = 0.05
1669
+ controls.target = new three.Vector3(this.camera.position.x, this.camera.position.y, 0)
1670
+ controls.addEventListener("change", () => {
1671
+ this._Emit("viewChanged")
1672
+ this.Render()
1673
+ })
1674
+ controls.update()
1675
+ return controls
1676
+ }
1677
+
1678
+ SetView(center, width, height = null, camera = null) {
1679
+ const cam = camera ? camera : this.camera
1680
+ if (!cam) {
1681
+ return;
1682
+ }
1683
+ if (!height) {
1684
+ const aspect = this.canvasWidth / this.canvasHeight
1685
+ height = width / aspect
1686
+ }
1687
+ cam.left = -width / 2
1688
+ cam.right = width / 2
1689
+ cam.top = height / 2
1690
+ cam.bottom = -height / 2
1691
+ cam.zoom = 1
1692
+ cam.position.set(center.x, center.y, 1)
1693
+ cam.rotation.set(0, 0, 0)
1694
+ cam.updateMatrix()
1695
+ cam.updateProjectionMatrix()
1696
+ this._Emit("viewChanged")
1697
+ }
1698
+
1699
+ TransformVertex(v, space) {
1700
+ return new three.Vector3(v.x - space.layout.origin.x, v.y - space.layout.origin.y, v.z)
1701
+ }
1702
+
1703
+ SelectObj(event) {
1704
+ var raycaster = new three.Raycaster();
1705
+ raycaster.params.Line.threshold = 15;
1706
+
1707
+ var mouse = new three.Vector2();
1708
+ var e = event.detail.domEvent
1709
+ const canvasRect = event.target.getBoundingClientRect()
1710
+ const canvasCoord = { x: e.clientX - canvasRect.left, y: e.clientY - canvasRect.top }
1711
+ mouse.x = (canvasCoord.x / this.renderer.domElement.clientWidth) * 2 - 1;
1712
+ mouse.y = - (canvasCoord.y / this.renderer.domElement.clientHeight) * 2 + 1;
1713
+ mouse.z = 0;
1714
+
1715
+ raycaster.setFromCamera(mouse, this.camera);
1716
+ var intersects = raycaster.intersectObjects(this.objects, true);
1717
+ if (intersects && intersects.length > 0) {
1718
+ let selectedIndex = 0
1719
+ if (intersects[selectedIndex].object.name) {
1720
+ const name = intersects[selectedIndex].object.name
1721
+ const objectList = this.objectMap.get(name)
1722
+ if (objectList) {
1723
+ this.SetSelected(objectList)
1724
+ return name
1725
+ }
1726
+ }
1727
+ }
1728
+ return null
1729
+ }
1730
+
1731
+ GetSelectHandle() {
1732
+ if (this.selectedObjects && this.selectedObjects.length > 0) {
1733
+ return selectedObjects[0].name
1734
+ }
1735
+ return null
1736
+ }
1737
+
1738
+ HighlightColor(color, factor = 0.3) {
1739
+ color.r += factor;
1740
+ if (color.r > 1)
1741
+ color.r -= 1;
1742
+ if (color.r < 0)
1743
+ color.r += 1;
1744
+ color.g += factor;
1745
+ if (color.g > 1)
1746
+ color.g -= 1;
1747
+ if (color.g < 0)
1748
+ color.g += 1;
1749
+ color.b += factor;
1750
+ if (color.b > 1)
1751
+ color.b -= 1;
1752
+ if (color.b < 0)
1753
+ color.b += 1;
1754
+ }
1755
+
1756
+
1757
+ SetSelected(objectList) {
1758
+ if (this.selectedObjects && this.selectedObjects.length > 0) {
1759
+ for (const object of this.selectedObjects) {
1760
+ const drawable = this.GetDrawable(object.name);
1761
+ if (!(drawable && drawable.drawMode === DrawMode.ANNOTATIONS)) {
1762
+ if (object.material.uniforms) {
1763
+ object.material.uniforms.color.value = object.color;
1764
+ }
1765
+ else {
1766
+ object.material.color = object.color;
1767
+ }
1768
+ }
1769
+ }
1770
+ }
1771
+
1772
+ this.selectedObjects = objectList
1773
+ if (this.selectedObjects && this.selectedObjects.length > 0) {
1774
+ for (const object of this.selectedObjects) {
1775
+ const drawable = this.GetDrawable(object.name);
1776
+ if (!(drawable && drawable.drawMode === DrawMode.ANNOTATIONS)) {
1777
+ let mat_wire = object.material.clone();
1778
+ if (object.material.uniforms) {
1779
+ object.color = object.material.uniforms.color.value;
1780
+ this.HighlightColor(mat_wire.uniforms.color.value);
1781
+ }
1782
+ else {
1783
+ object.color = object.material.color;
1784
+ this.HighlightColor(mat_wire.color);
1785
+ }
1786
+ object.material = mat_wire;
1787
+ }
1788
+ }
1789
+ }
1790
+ this.Render()
1791
+ const id = this.GetSelectedHandle()
1792
+ this._Emit("selected", { id })
1793
+ }
1794
+
1795
+ GetSelectedHandle() {
1796
+ if (this.selectedObjects && this.selectedObjects.length > 0) {
1797
+ return this.selectedObjects[0].name
1798
+ }
1799
+
1800
+ return null
1801
+ }
1802
+
1803
+ AddObject(obj) {
1804
+ this.objects.push(obj)
1805
+ const name = obj.name
1806
+ if (name) {
1807
+ let objectList = this.objectMap.get(name)
1808
+ if (!objectList) {
1809
+ objectList = []
1810
+ this.objectMap.set(name, objectList)
1811
+ }
1812
+ objectList.push(obj)
1813
+ }
1814
+ }
1815
+
1816
+ ZoomTo(input, offset) {
1817
+ const object = Array.isArray(input) ? input[0] : input //todo to calc boundingBox of all objects
1818
+ offset = offset || 1.25;
1819
+
1820
+ const boundingBox = new three.Box3();
1821
+ // get bounding box of object - this will be used to setup controls and camera
1822
+ boundingBox.setFromObject(object);
1823
+
1824
+ const center = boundingBox.getCenter(new three.Vector3());
1825
+ const size = boundingBox.getSize(new three.Vector3());
1826
+ this.FitView(boundingBox.min.x, boundingBox.max.x, boundingBox.min.y, boundingBox.max.y)
1827
+ this.controls.target.copy(center);
1828
+
1829
+ this.controls.update()
1830
+ }
1831
+
1832
+ ZoomToDrawable(spaceHandle, id) {
1833
+ const space = this.GetSpaceByHandle(spaceHandle)
1834
+ var layout = null
1835
+ if (!space) {//space has not been loaded yet
1836
+ layout = this.GetLayoutBySpaceHandle(spaceHandle)
1837
+ }
1838
+ else {
1839
+ layout = space.layout
1840
+ }
1841
+ if (layout) {
1842
+ if (this.spaceName != layout.space) {
1843
+ this.SwitchSpace(layout)
1844
+ }
1845
+ const drawable = this.GetDrawable(id)
1846
+ if (drawable?.drawMode === DrawMode.ANNOTATIONS) {//this is annotation
1847
+ this.ZoomTo(drawable.objects)
1848
+ return drawable
1849
+ }
1850
+ }
1851
+ return null
1852
+ }
1853
+
1854
+ RemoveFromScene(scene, objects) {
1855
+ for (const object of objects) {
1856
+ scene.remove(object);
1857
+ }
1858
+ }
1859
+
1860
+ GetDrawable(id) {
1861
+ for (const drawable of this.drawables) {
1862
+ if (drawable.id == id) {
1863
+ return drawable
1864
+ }
1865
+ }
1866
+ return null
1867
+ }
1868
+
1869
+ VisibleDrawable(drawMode, visible) {
1870
+ var isDirty = false
1871
+ for (const drawable of this.drawables) {
1872
+ if (drawable.drawMode === drawMode) {
1873
+ for (const object of drawable.objects) {
1874
+ object.visible = visible;
1875
+ isDirty = true
1876
+ }
1877
+ }
1878
+ }
1879
+ if (isDirty) {
1880
+ this.Render()
1881
+ }
1882
+ }
1883
+
1884
+ DeleteDrawable(id) {
1885
+ const drawable = this.GetDrawable(id)
1886
+ if (drawable) {
1887
+ const space = drawable.space
1888
+ this.RemoveFromScene(space.scene, drawable.objects)
1889
+ const index = this.drawables.indexOf(drawable);
1890
+
1891
+ if (index !== - 1) {
1892
+ this.drawables.splice(index, 1)
1893
+ }
1894
+ const currentSpace = this.GetSpace()
1895
+ if (space === currentSpace) {
1896
+ this.Render()
1897
+ }
1898
+ }
1899
+ }
1900
+
1901
+ Cancel() {
1902
+ this.SetSelected(null)
1903
+ for (const plugin of this.drawablePlugins) {
1904
+ plugin.Cancel()
1905
+ }
1906
+ }
1907
+
1908
+ RegisterDrawablePlugin(plugin) {
1909
+ this.drawablePlugins.push(plugin)
1910
+ }
1911
+
1912
+ OnKeyDown = event => {
1913
+ this.DoKeyDown(event)
1914
+ }
1915
+
1916
+ DoKeyDown(event) {
1917
+ if (event.keyCode == 27) {//ESC
1918
+ this.Cancel()
1919
+ }
1920
+ }
1921
+
1922
+ FindDxfEntity(id) {
1923
+ if (!this.dxf) {
1924
+ return null
1925
+ }
1926
+
1927
+ let entity = this.FindEntityInList(id, this.dxf.entities)
1928
+ if (!entity && this.dxf.blocks) {
1929
+ for (const [, block] of Object.entries(this.dxf.blocks)) {
1930
+ entity = this.FindEntityInList(id, block.entities)
1931
+ if (entity) {
1932
+ break
1933
+ }
1934
+ }
1935
+ }
1936
+
1937
+ return entity
1938
+ }
1939
+
1940
+ FindEntityInList(id, entities) {
1941
+ if (entities) {
1942
+ for (const entity of entities) {
1943
+ if (id === entity.handle) {
1944
+ return entity
1945
+ }
1946
+ }
1947
+ }
1948
+
1949
+ return null
1950
+ }
1951
+
1952
+ ZoomIn(scale) {
1953
+ const centerX = this.canvasWidth / 2 * window.devicePixelRatio;
1954
+ const centerY = this.canvasHeight / 2 * window.devicePixelRatio;
1955
+ scale = scale ? scale : Math.pow(0.95, 3);
1956
+ this.controls.zoomIn(scale, new three.Vector2(centerX, centerY));
1957
+ this.controls.update();
1958
+ }
1959
+
1960
+ ZoomOut(scale) {
1961
+ const centerX = this.canvasWidth / 2 * window.devicePixelRatio;
1962
+ const centerY = this.canvasHeight / 2 * window.devicePixelRatio;
1963
+ scale = scale ? scale : Math.pow(0.95, 3);
1964
+ this.controls.zoomOut(scale, new three.Vector2(centerX, centerY));
1965
+ this.controls.update();
1966
+ }
1967
+ }
1968
+ export class Drawable {
1969
+ constructor(options) {
1970
+
1971
+ this.viewer = options.viewer
1972
+ this.drawMode = null
1973
+ this.idPrefix = ""
1974
+ }
1975
+
1976
+ Create() {
1977
+ this.viewer.Cancel()
1978
+
1979
+ this.objects = []
1980
+ this.viewer.drawableSpace = this.viewer.GetSpace()
1981
+
1982
+ this.id = this.CreateId()
1983
+ this.viewer.drawMode = this.drawMode
1984
+ this.viewer.Subscribe("pointerdown", this.OnPointerDown)
1985
+ this.viewer.Subscribe("mousemove", this.OnMouseMove)
1986
+ }
1987
+
1988
+ Add(spaceHandle, id) {
1989
+ const space = this.viewer.GetSpaceByHandle(spaceHandle)
1990
+ if (!space) {
1991
+ return false
1992
+ }
1993
+ this.viewer.drawableSpace = space
1994
+ this.objects = []
1995
+ this.id = id || this.CreateId()
1996
+ this.viewer.drawMode = this.drawMode
1997
+ return true
1998
+ }
1999
+
2000
+ Cancel() {
2001
+ this.CancelDraw()
2002
+ this.Finish()
2003
+ }
2004
+
2005
+
2006
+ OnPointerDown = event => {
2007
+ if (event.detail.domEvent.button == 0) {
2008
+ this.DoLeftPointerDown(event)
2009
+ }
2010
+ }
2011
+
2012
+ DoLeftPointerDown(event) {
2013
+ if (!this.IsValidDrawMode()) {
2014
+ return false
2015
+ }
2016
+
2017
+ return true
2018
+ }
2019
+
2020
+ OnMouseMove = event => {
2021
+ this.DoMouseMove(event)
2022
+ }
2023
+
2024
+ DoMouseMove(event) {
2025
+ }
2026
+
2027
+ CancelDraw(){
2028
+ if (this.RemoveCancelledObjects()) {
2029
+ const drawMode = this.drawMode
2030
+ this.viewer._Emit("drawCancelled", { drawMode })
2031
+ }
2032
+ if (this.IsValidDrawMode()) {
2033
+ this.viewer.drawMode = null
2034
+ }
2035
+ }
2036
+
2037
+ RemoveCancelledObjects() {
2038
+ if (!this.IsValidDrawMode()) {
2039
+ return false
2040
+ }
2041
+ if (!this.objects || this.objects.length == 0) {
2042
+ return false
2043
+ }
2044
+
2045
+ this.viewer.RemoveFromScene(this.viewer.drawableSpace.scene, this.objects)
2046
+ this.objects = []
2047
+
2048
+ this.RenderIfNeeded()
2049
+ this.viewer.drawableSpace = null
2050
+
2051
+ return true
2052
+ }
2053
+
2054
+ RenderIfNeeded() {
2055
+ const currentSpace = this.viewer.GetSpace()
2056
+ if (this.viewer.drawableSpace == currentSpace) {
2057
+ this.viewer.Render()
2058
+ }
2059
+ }
2060
+
2061
+ CreateId() {
2062
+ const uuid = self.crypto.randomUUID();
2063
+ return this.idPrefix + uuid
2064
+ }
2065
+
2066
+ IsValidDrawMode() {
2067
+ return this.viewer.drawMode === this.drawMode
2068
+ }
2069
+
2070
+
2071
+ Finish() {
2072
+ this.viewer.Unsubscribe("mousemove", this.OnMouseMove)
2073
+ this.viewer.Unsubscribe("pointerdown", this.OnPointerDown)
2074
+ document.removeEventListener("keydown", this.OnKeyDown)
2075
+ if (this.IsValidDrawMode()) {
2076
+ this.viewer.drawMode = null
2077
+ }
2078
+ this.objects = []
2079
+ }
2080
+
2081
+ CreateVector(pt) {
2082
+ return new three.Vector3(pt.x, pt.y, 0)
2083
+ }
2084
+
2085
+ AddDrawableObjectToScene(object) {
2086
+ const space = this.viewer.GetSpace()
2087
+ space.scene.add(object);
2088
+ this.objects.push(object)
2089
+ }
2090
+
2091
+ AddToAllViewerObjects() {
2092
+ for (const object of this.objects) {
2093
+ this.viewer.AddObject(object)
2094
+ }
2095
+ }
2096
+
2097
+ AddAndFinish(drawable) {
2098
+ this.viewer.drawables.push(drawable)
2099
+ this.viewer._Emit("drawableCreated", drawable)
2100
+
2101
+ this.AddToAllViewerObjects()
2102
+ this.Finish()
2103
+ }
2104
+ }
2105
+
2106
+ const ID_PREFIX = "anno_"
2107
+
2108
+ export class Annotation extends Drawable {
2109
+ constructor(options) {
2110
+
2111
+ super(options);
2112
+ this.idPrefix = ID_PREFIX;
2113
+ this.color = options.color;
2114
+ this.drawMode = DrawMode.ANNOTATIONS;
2115
+ this.labelRenderer = this.InitLabelRenderer();
2116
+ }
2117
+
2118
+ Create() {
2119
+ super.Create();
2120
+ this.FirstPoint = this.SecondPoint = null;
2121
+ }
2122
+
2123
+ Add(spaceHandle, firstPoint, secondPoint, id, color) {
2124
+ if (!super.Add(spaceHandle, id)) {
2125
+ return null;
2126
+ }
2127
+
2128
+ this.FirstPoint = firstPoint;
2129
+ this.SecondPoint = secondPoint;
2130
+ this.color = color;
2131
+ return this.UpdateAndAdd();
2132
+ }
2133
+
2134
+ DoLeftPointerDown(event) {
2135
+ if (!super.DoLeftPointerDown()) {
2136
+ return;
2137
+ }
2138
+
2139
+ if (!this.FirstPoint) {
2140
+ this.FirstPoint = this.SecondPoint = this.CreateVector(event.detail.position);
2141
+ this.Update();
2142
+ }
2143
+ else {
2144
+ this.SecondPoint = this.CreateVector(event.detail.position);
2145
+ this.UpdateAndAdd();
2146
+ }
2147
+ }
2148
+
2149
+ UpdateAndAdd() {
2150
+ this.Update();
2151
+
2152
+ const firstPoint = this.FirstPoint;
2153
+ const secondPoint = this.SecondPoint;
2154
+ const space = this.viewer.drawableSpace;
2155
+ const id = this.id;
2156
+ const objects = this.objects;
2157
+ const drawMode = this.drawMode;
2158
+ var annotation = { drawMode, id, firstPoint, secondPoint, space, objects };
2159
+
2160
+ this.AddAndFinish(annotation);
2161
+ return annotation;
2162
+ }
2163
+
2164
+ DoMouseMove(event) {
2165
+ this.SecondPoint = this.CreateVector(event.detail.position);
2166
+ this.Update();
2167
+ }
2168
+
2169
+ Update() {
2170
+ if (!this.FirstPoint || !this.SecondPoint) {
2171
+ return
2172
+ }
2173
+ const points = [];
2174
+ points.push(this.FirstPoint)
2175
+ let pt = this.FirstPoint.clone()
2176
+ pt.x = this.SecondPoint.x
2177
+ points.push(pt)
2178
+ points.push(pt)
2179
+ points.push(this.SecondPoint)
2180
+ points.push(this.SecondPoint)
2181
+ pt = this.FirstPoint.clone()
2182
+ pt.y = this.SecondPoint.y
2183
+ points.push(pt)
2184
+ points.push(pt)
2185
+ points.push(this.FirstPoint)
2186
+ var geom = new three.BufferGeometry().setFromPoints(points);
2187
+ if (this.objects.length > 0) {
2188
+ this.objects[0].geometry.dispose()
2189
+ this.objects[0].geometry = geom
2190
+ }
2191
+ else {
2192
+ const material = new three.LineBasicMaterial({ color: this.color })
2193
+ const object = new three.LineSegments(geom, material);
2194
+ object.name = this.id
2195
+
2196
+ if (!this.id.includes(this.idPrefix)) {
2197
+ this.InitLabel(object);
2198
+ }
2199
+
2200
+ this.AddDrawableObjectToScene(object)
2201
+ }
2202
+
2203
+ this.RenderIfNeeded()
2204
+ }
2205
+
2206
+ InitLabel(annotation) {
2207
+ const id = this.id;
2208
+ const labelDiv = document.createElement('div');
2209
+ labelDiv.className = 'vitro-marker-label';
2210
+ labelDiv.textContent = id;
2211
+ labelDiv.style.backgroundColor = this.color;
2212
+ labelDiv.style.backgroundColor = this.color;
2213
+ if (this.onLabelClick) {
2214
+ labelDiv.onclick = () => this.onLabelClick(id);
2215
+ }
2216
+
2217
+ const label = new CSS2DObject(labelDiv);
2218
+ label.position.copy(this.FirstPoint);
2219
+ label.center.set(0.5, 0.5);
2220
+ annotation.add(label);
2221
+ }
2222
+
2223
+ UpdateLabelPosition() {
2224
+ if (this.labelRenderer && window.dxfViewer.GetSpace()) {
2225
+ this.labelRenderer.render(window.dxfViewer.GetSpace().scene, window.dxfViewer.GetSpace().camera);
2226
+ }
2227
+ }
2228
+
2229
+ RemoveLabels() {
2230
+ if (this.labelRenderer) {
2231
+ $(this.labelRenderer.domElement).html(null);
2232
+ }
2233
+ }
2234
+
2235
+ InitLabelRenderer() {
2236
+ const labelRenderer = new CSS2DRenderer();
2237
+ labelRenderer.setSize($('#canvasContainer').innerWidth(), $('#canvasContainer').innerHeight());
2238
+ labelRenderer.domElement.style.position = 'absolute';
2239
+ labelRenderer.domElement.style.top = '0px';
2240
+ labelRenderer.domElement.style.pointerEvents = 'none';
2241
+ document.getElementById('canvasContainer').appendChild(labelRenderer.domElement);
2242
+
2243
+ return labelRenderer;
2244
+ }
2245
+
2246
+ UpdateLabelRendererSize() {
2247
+ this.labelRenderer.setSize(this.viewer.canvasWidth, this.viewer.canvasHeight);
2248
+ }
2249
+ }
2250
+
2251
+ /** Fetches and parses DXF file. */
2252
+ export class VitroDxfFetcher extends DxfFetcher {
2253
+
2254
+ async SyncFetch() {
2255
+ //const response = fetch(this.url)
2256
+ const dxf = await SyncFetchDxf(this.url)
2257
+ /*
2258
+ const reader = response.body.getReader()
2259
+ //XXX streaming parsing is not supported in dxf-parser for now (its parseStream() method
2260
+ // just accumulates chunks in a string buffer before parsing. Fix it later.
2261
+ let buffer = ""
2262
+ let decoder = new TextDecoder("utf-8")
2263
+ while(true) {
2264
+ const {done, value} = reader.read()
2265
+ if (done) {
2266
+ buffer += decoder.decode(new ArrayBuffer(0), {stream: false})
2267
+ break
2268
+ }
2269
+ buffer += decoder.decode(value, {stream: true})
2270
+ }
2271
+ */
2272
+ const parser = new DxfParser()
2273
+ return parser.parseSync(buffer)
2274
+ }
2275
+
2276
+ async SyncFetchDxf(url) {
2277
+ const response = await fetch(url)
2278
+ const txt = await response.json()
2279
+ return txt
2280
+ }
2281
+ }
2282
+ const INDEXED_CHUNK_SIZE = 0x10000
2283
+
2284
+ export class VitroRenderBatch extends RenderBatch {
2285
+ constructor(key) {
2286
+ super(key)
2287
+ }
2288
+
2289
+ PushVertex(v) {
2290
+ const idx = this.vertices.Push(v.x)
2291
+ this.vertices.Push(v.y)
2292
+ this.vertices.Push(0)//vitro
2293
+ return idx
2294
+ }
2295
+
2296
+ PushChunk(verticesCount, entity = null) {
2297
+ if (verticesCount > INDEXED_CHUNK_SIZE) {
2298
+ throw new Error("Vertices count exceeds chunk limit: " + verticesCount)
2299
+ }
2300
+ /* Find suitable chunk with minimal remaining space to fill them as fully as possible. */
2301
+ let curChunk = null
2302
+ //vitro - always create new chunk
2303
+ let curSpace = 0
2304
+ for (const chunk of this.chunks) {
2305
+ const space = INDEXED_CHUNK_SIZE - chunk.vertices.GetSize() / 3 //vitro 3 instead 2
2306
+ if (space < verticesCount) {
2307
+ continue
2308
+ }
2309
+ if (entity && entity.handle && chunk.handle != entity.handle) {//vitro
2310
+ continue
2311
+ }
2312
+ if (curChunk === null || space < curSpace) {
2313
+ curChunk = chunk
2314
+ curSpace = space
2315
+ }
2316
+ }
2317
+
2318
+ if (curChunk === null) {
2319
+ curChunk = this._NewChunk(verticesCount)
2320
+ if (entity && entity.handle) {//vitro
2321
+ curChunk.handle = entity.handle
2322
+ }
2323
+ }
2324
+ return new IndexedChunkWriter(curChunk, verticesCount)
2325
+ }
2326
+
2327
+ /** Merge other batch into this one. They should have the same geometry type. Instanced batches
2328
+ * are disallowed.
2329
+ *
2330
+ * @param batch {RenderBatch}
2331
+ * @param transform {?Matrix3} Optional transform to apply for merged vertices.
2332
+ */
2333
+ Merge(batch, transform = null, entity = null) {//vitro add entity
2334
+ if (this.key.geometryType !== batch.key.geometryType) {
2335
+ throw new Error("Rendering batch merging geometry type mismatch: " +
2336
+ `${this.key.geometryType} !== ${batch.key.geometryType}`)
2337
+ }
2338
+ if (this.key.IsInstanced()) {
2339
+ throw new Error("Attempted to merge instanced batch")
2340
+ }
2341
+ if (this.key.IsIndexed()) {
2342
+ /* Merge chunks. */
2343
+ for (const chunk of batch.chunks) {
2344
+ const verticesSize = chunk.vertices.size
2345
+ const chunkWriter = this.PushChunk(verticesSize / 3, entity)//vitro 2 => 3
2346
+ for (let i = 0; i < verticesSize; i += 3) {//vitro 2 => 3
2347
+ const v = new Vector2(chunk.vertices.Get(i), chunk.vertices.Get(i + 1))
2348
+ if (transform) {
2349
+ v.applyMatrix3(transform)
2350
+ }
2351
+ chunkWriter.PushVertex(v)
2352
+ }
2353
+ const numIndices = chunk.indices.size
2354
+ for (let i = 0; i < numIndices; i++) {
2355
+ chunkWriter.PushIndex(chunk.indices.Get(i))
2356
+ }
2357
+ chunkWriter.Finish()
2358
+ }
2359
+ } else {
2360
+ const n = batch.vertices.size
2361
+ for (let i = 0; i < n; i += 3) {//vitro 2 => 3
2362
+ const v = new Vector2(batch.vertices.Get(i), batch.vertices.Get(i + 1))
2363
+ if (transform) {
2364
+ v.applyMatrix3(transform)
2365
+ }
2366
+ this.PushVertex(v)
2367
+ }
2368
+ }
2369
+ }
2370
+
2371
+ _NewChunk(initialCapacity) {
2372
+ const chunk = new IndexedChunk(initialCapacity)
2373
+ this.chunks.push(chunk)
2374
+ return chunk
2375
+ }
2376
+ }
2377
+
2378
+ class IndexedChunk {
2379
+ constructor(initialCapacity) {
2380
+ if (initialCapacity < 16) {
2381
+ initialCapacity = 16
2382
+ }
2383
+ /* Average two indices per vertex. */
2384
+ this.indices = new DynamicBuffer(NativeType.UINT16, initialCapacity * 2)
2385
+ /* Two components per vertex. */
2386
+ this.vertices = new DynamicBuffer(NativeType.FLOAT32, initialCapacity * 2)
2387
+ }
2388
+
2389
+ Serialize(buffers) {
2390
+ const chunk = {}
2391
+ {
2392
+ const size = this.vertices.GetSize()
2393
+ chunk.verticesOffset = buffers.verticesOffset
2394
+ chunk.verticesSize = size
2395
+ this.vertices.CopyTo(buffers.vertices, buffers.verticesOffset)
2396
+ buffers.verticesOffset += size
2397
+ }
2398
+ {
2399
+ const size = this.indices.GetSize()
2400
+ chunk.indicesOffset = buffers.indicesOffset
2401
+ chunk.indicesSize = size
2402
+ this.indices.CopyTo(buffers.indices, buffers.indicesOffset)
2403
+ buffers.indicesOffset += size
2404
+ }
2405
+ chunk.handle = this.handle
2406
+ return chunk
2407
+ }
2408
+ }
2409
+
2410
+
2411
+ class IndexedChunkWriter {
2412
+ constructor(chunk, verticesCount) {
2413
+ this.chunk = chunk
2414
+ this.verticesCount = verticesCount
2415
+ this.verticesOffset = this.chunk.vertices.GetSize() / 3 //vitro 2 => 3
2416
+ this.numVerticesPushed = 0
2417
+ }
2418
+
2419
+ PushVertex(v) {
2420
+ if (this.numVerticesPushed === this.verticesCount) {
2421
+ throw new Error()
2422
+ }
2423
+ this.chunk.vertices.Push(v.x)
2424
+ this.chunk.vertices.Push(v.y)
2425
+ this.chunk.vertices.Push(0)//vitro
2426
+ this.numVerticesPushed++
2427
+ }
2428
+
2429
+ PushIndex(idx) {
2430
+ if (idx < 0 || idx >= this.verticesCount) {
2431
+ throw new Error(`Index out of range: ${idx}/${this.verticesCount}`)
2432
+ }
2433
+ this.chunk.indices.Push(idx + this.verticesOffset)
2434
+ }
2435
+
2436
+ Finish() {
2437
+ if (this.numVerticesPushed !== this.verticesCount) {
2438
+ throw new Error(`Not all vertices pushed: ${this.numVerticesPushed}/${this.verticesCount}`)
2439
+ }
2440
+ }
2441
+ }window.initDxfViewer = function(context) {var labelDlgAddNote = 'Add Note';
2442
+ var labelExpand = 'Развернуть';
2443
+ var labelCollapse = 'Свернуть';
2444
+ var labelBtnDelete = 'Удалить';
2445
+ function initPropertySetsResizable() {
2446
+ $('#propInspector').resizable({
2447
+ handles: "w",
2448
+ minWidth: 150
2449
+ });
2450
+ }
2451
+
2452
+
2453
+ function showPropertyInspector(pickResult) {
2454
+ // var objectId = pickResult.entity.id;
2455
+ // TODO - only for Test
2456
+ var objectId = '0K1IpmnK10iuhGzDjMLoJt';
2457
+
2458
+ var metaObject = customMeta[objectId];
2459
+
2460
+ if(metaObject) {
2461
+ setPropertySets(metaObject);
2462
+ } else {
2463
+ console.log('No object meta-data');
2464
+ return false;
2465
+ }
2466
+ }
2467
+
2468
+ function setPropertySets(metaObject) {
2469
+ const html = [];
2470
+ var propertySets = metaObject.propertySets;
2471
+
2472
+ html.push('<div class="element-attributes">');
2473
+ if (!metaObject) {
2474
+ html.push('<p class="subsubtitle">No object selected</p>');
2475
+ } else {
2476
+ html.push('<table class="xeokit-table">');
2477
+ html.push('<tr><td class="td1">Name:</td><td class="td2">' + metaObject.name + '</td></tr>');
2478
+ if (metaObject.type) {
2479
+ html.push('<tr><td class="td1">Class:</td><td class="td2">' + metaObject.type + '</td></tr>');
2480
+ }
2481
+ html.push('<tr><td class="td1">UUID:</td><td class="td2">' + metaObject.id + '</td></tr>');
2482
+ html.push('</table>');
2483
+ if (!propertySets || propertySets.length === 0) {
2484
+ html.push('<p class="subtitle xeokit-no-prop-set-warning">No properties sets found for this object.</p>');
2485
+ html.push('</div>');
2486
+ } else {
2487
+ html.push('</div>');
2488
+ html.push('<div class="xeokit-accordion">');
2489
+ for (let i = 0, len = propertySets.length; i < len; i++) {
2490
+ const propertySet = propertySets[i];
2491
+ const properties = propertySet.properties || [];
2492
+ if (properties.length > 0) {
2493
+ html.push('<div class="xeokit-accordion-container">' +
2494
+ '<h3><span>'+ (propertySet.name ? propertySet.name : propertySet.propertySetName) + '</span></h3>' +
2495
+ '<div class="xeokit-accordion-panel">' +
2496
+ '<table class="xeokit-table"><tbody>'
2497
+ );
2498
+ for (let i = 0, len = properties.length; i < len; i++) {
2499
+ const property = properties[i];
2500
+ html.push('<tr><td class="td1">' + (property.name || property.Name || property.label) + ':</td><td class="td2">' + (property.value || property.Value) + '</td></tr>');
2501
+ }
2502
+ html.push('</tbody></table>'+
2503
+ '</div>'+
2504
+ '</div>');
2505
+ } else {
2506
+ html.push('<p class="subtitle">No properties sets found.</p>');
2507
+ }
2508
+ }
2509
+ html.push('</div>');
2510
+ }
2511
+ }
2512
+ $('#propInspector .prop-inspector-content')[0].innerHTML = html.join("");
2513
+ $('.xeokit-accordion-container').accordion({
2514
+ collapsible: true,
2515
+ heightStyle: "content"
2516
+ });
2517
+ $('#propInspector').show();
2518
+
2519
+ // Hide all right-side positioned panels
2520
+ $('.sidebar').hide();
2521
+ $('#btnToggleSidebarNotes').removeClass('toggled');
2522
+ }
2523
+
2524
+ function bindPropInspectorClose() {
2525
+ $('.prop-inspector-close').on('click', function() {
2526
+ $('#propInspector').hide();
2527
+ })
2528
+ }
2529
+ //------------------------------------------------------------------------------------------------------------------
2530
+ // Sidebar Notes
2531
+ //------------------------------------------------------------------------------------------------------------------
2532
+
2533
+ function initSidebarNotes() {
2534
+ clearSidebarNotes();
2535
+ createSidebarNotes();
2536
+ }
2537
+
2538
+ function clearSidebarNotes() {
2539
+ $('.sidebar-note-wrap').remove();
2540
+ }
2541
+
2542
+ function createSidebarNotes() {
2543
+ $.each(notes, function(index, note) {
2544
+ var noteElm = createSidebarNotesItem(note);
2545
+ $('.sidebar-content').append(noteElm);
2546
+ });
2547
+ }
2548
+
2549
+ function createSidebarNotesItem(noteData) {
2550
+ var noteWrap = $('<div class="sidebar-note-wrap" id="noteWrap-'+noteData.id+'" />');
2551
+
2552
+ var noteHeader = createSidebarNotesItemHeader(noteData);
2553
+ var noteBody = createSidebarNotesItemBody(noteData);
2554
+
2555
+ noteWrap.append(noteHeader);
2556
+ noteWrap.append(noteBody);
2557
+
2558
+ return noteWrap;
2559
+ }
2560
+
2561
+ function createSidebarNotesItemBody(noteData) {
2562
+ var noteContent = noteData;
2563
+
2564
+ var noteBody = $('<div class="sidebar-note-body" />');
2565
+ var noteTextarea = $('<textarea data-id="' + noteData.id + '"></textarea>');
2566
+ bindTextareaHandlers(noteTextarea);
2567
+ noteTextarea.val(noteContent.values.description);
2568
+
2569
+ var expandBtnWrap = $('<div class="sidebar-note-expand-wrap"/>');
2570
+ var expandBtnWrapInner = $('<div class="sidebar-note-expand-btn collapsed"/>');
2571
+ var expandBtnExpand = $('<a class="btn-expand">' + labelExpand + '</a>');
2572
+ var expandBtnCollapse = $('<a class="btn-collapse">'+ labelCollapse + '</a>');
2573
+ bindOnClickTextareaExpand(expandBtnExpand);
2574
+ bindOnClickTextareaCollapse(expandBtnCollapse);
2575
+ expandBtnWrapInner.append(expandBtnExpand);
2576
+ expandBtnWrapInner.append(expandBtnCollapse);
2577
+
2578
+
2579
+ expandBtnWrap.append(expandBtnWrapInner);
2580
+ noteBody.append(noteTextarea);
2581
+ noteBody.append(expandBtnWrap);
2582
+
2583
+ if(noteContent.values.imageSrc) {
2584
+ var noteImage = $('<div class="sidebar-note-img-wrap"><img src="' + noteContent.values.imageSrc + '" /><div>');
2585
+ noteBody.append(noteImage);
2586
+ }
2587
+
2588
+ return noteBody;
2589
+ }
2590
+
2591
+ function bindTextareaHandlers(textarea) {
2592
+ textarea.on('focus', function() {
2593
+ doTextareaOnFocus($(this));
2594
+ });
2595
+
2596
+ textarea.on('change', function() {
2597
+ doNoteOnChange($(this));
2598
+ });
2599
+ }
2600
+
2601
+ function doTextareaOnFocus(textarea) {
2602
+ $('.sidebar-note-wrap').removeClass('active');
2603
+ var wrap = textarea.closest('.sidebar-note-wrap');
2604
+ wrap.addClass('active');
2605
+ autosize(textarea);
2606
+ wrap.find('.sidebar-note-expand-btn').removeClass('collapsed');
2607
+ }
2608
+
2609
+ function doNoteOnChange(textarea) {
2610
+ // TODO - Ajax -> save data
2611
+
2612
+ var noteId = textarea.data('id');
2613
+ var noteDesc = textarea.val();
2614
+ var date = new Date(Date.now()).toLocaleDateString("ru-RU"); // TODO
2615
+ var user = ''; // TODO
2616
+
2617
+ $.each(notes, function(index, val) {
2618
+ if(val.id == noteId) {
2619
+ // notes[index]['values']['glyph'] = noteGlyph; // ?
2620
+ // notes[index]['values']['title'] = noteTitle; // ?
2621
+ // notes[index]['creator'] = user;
2622
+ notes[index]['values']['description'] = noteDesc;
2623
+ notes[index]['date'] = date;
2624
+ }
2625
+ });
2626
+
2627
+ alert('noteId=' + noteId + ' | Data changed: \r\n' + noteDesc);
2628
+ }
2629
+
2630
+ function bindOnClickTextareaExpand(btnElm) {
2631
+ btnElm.on('click', function() {
2632
+ doTextareaExpand(btnElm);
2633
+ $(this).closest('.sidebar-note-expand-btn').removeClass('collapsed');
2634
+ });
2635
+ }
2636
+
2637
+ function doTextareaExpand(btnElm) {
2638
+ var textarea = btnElm.closest('.sidebar-note-body').find('textarea');
2639
+ autosize(textarea);
2640
+ }
2641
+
2642
+ function bindOnClickTextareaCollapse(btnElm) {
2643
+ btnElm.on('click', function() {
2644
+ doTextareaCollapse(btnElm);
2645
+ $(this).closest('.sidebar-note-expand-btn').addClass('collapsed');
2646
+ });
2647
+ }
2648
+
2649
+ function doTextareaCollapse(btnElm) {
2650
+ var textarea = btnElm.closest('.sidebar-note-body').find('textarea');
2651
+ autosize.destroy(textarea);
2652
+ }
2653
+
2654
+ function createSidebarNotesItemHeader(noteData) {
2655
+ var noteContent = noteData;
2656
+
2657
+ var noteHeader = $('<div class="sidebar-note-header" />');
2658
+ var noteName = $('<div class="sidebar-note-name" />');
2659
+ noteName.html(noteContent.creator);
2660
+ var noteDate = $('<div class="sidebar-note-date" />');
2661
+ noteDate.html(noteContent.date);
2662
+
2663
+ var noteTitle = $('<div class="sidebar-note-title">' + noteData.values.glyph + ' - ' + noteData.values.title + '</div>'); // TODO
2664
+ noteTitle.on('click', function() {
2665
+ goToNoteMarker($(this));
2666
+ });
2667
+
2668
+ var deleteBtn = $('<a title="' + labelBtnDelete + '" class="sidebar-note-btn-delete"><span>' + labelBtnDelete + '</span></a>');
2669
+ deleteBtn.data('id', noteData.id);
2670
+ bindSidebarNotesBtnDelete(deleteBtn);
2671
+
2672
+ noteHeader.append(noteName);
2673
+ noteHeader.append(noteDate);
2674
+ noteHeader.append(noteTitle);
2675
+ noteHeader.append(deleteBtn);
2676
+
2677
+ return noteHeader;
2678
+ }
2679
+
2680
+ // TODO
2681
+ function goToNoteMarker(noteTitle) {
2682
+ var sidebarNoteWrap = noteTitle.closest('.sidebar-note-wrap');
2683
+ var sidebarNoteWrapId = sidebarNoteWrap.attr('id');
2684
+ var noteId = sidebarNoteWrapId.replace("noteWrap-", "");
2685
+ //console.log('noteId', noteId);
2686
+
2687
+ // TODO - go to marker
2688
+ // Highlight annotation object
2689
+ // Go to annotation...
2690
+ // ...
2691
+ alert('Go to note ' + noteId);
2692
+ }
2693
+
2694
+ function bindSidebarNotesBtnDelete(btn) {
2695
+ btn.on('click', function(e) {
2696
+ $(document).trigger('deleteNote', [$(this)]);
2697
+ });
2698
+ }
2699
+
2700
+ function bindDeleteNoteEventHandler() {
2701
+ $(document).on('deleteNote', function(e, item) {
2702
+ var id = item.attr('id') ? item.attr('id') : item.data('id');
2703
+
2704
+ // TODO -> Ajax -> send data to server
2705
+ // ...
2706
+ // on success -> deleteNote
2707
+
2708
+ deleteNote(id);
2709
+ });
2710
+ }
2711
+
2712
+ // TODO
2713
+ function deleteNote(id) {
2714
+ notes = jQuery.grep(notes, function(elm, index) {
2715
+ return (elm.id != id);
2716
+ });
2717
+
2718
+ // TODO
2719
+ // Delete annotation
2720
+ // ...
2721
+ alert('Delete annotation ' + id);
2722
+
2723
+
2724
+ if($('.sidebar').is(':visible')) {
2725
+ initSidebarNotes();
2726
+ }
2727
+ }
2728
+
2729
+ function fixNotesTextareaBlur() {
2730
+ $('#xeokitCanvas').on('click', function() {
2731
+ //console.log('click - xeokitCanvas');
2732
+ if($('.sidebar-content').is(':visible')) {
2733
+ var textareas = $('.sidebar-content').find('textarea');
2734
+ if(textareas.length) {
2735
+ textareas.trigger('blur');
2736
+ }
2737
+ }
2738
+ });
2739
+ }
2740
+
2741
+ function highlightSidebarNote(noteId) {
2742
+ $('.sidebar-note-wrap').removeClass('active');
2743
+ var notetWrap = $('#noteWrap-' + noteId);
2744
+ notetWrap.addClass('active');
2745
+ autosize(notetWrap.find('textarea'));
2746
+ notetWrap.find('.sidebar-note-expand-btn').removeClass('collapsed');
2747
+ var sidebarContent = $('.sidebar-content');
2748
+ var sidebarOffsetTop = parseInt(sidebarContent[0].offsetTop);
2749
+ var sidebarScrollTop = parseInt(sidebarContent[0].scrollTop);
2750
+ sidebarContent.animate({
2751
+ scrollTop: notetWrap.position().top - sidebarOffsetTop + sidebarScrollTop
2752
+ }, 1000);
2753
+ }
2754
+
2755
+ function showNoteDesc(noteId) {
2756
+ if(!$('.sidebar').is(':visible')) {
2757
+ $('#btnToggleSidebarNotes').addClass('toggled');
2758
+ initSidebarNotes();
2759
+ expandSidebarNotes();
2760
+ }
2761
+ highlightSidebarNote(noteId);
2762
+ }
2763
+ //------------------------------------------------------------------------------------------------------------------
2764
+ // Toolbar
2765
+ //------------------------------------------------------------------------------------------------------------------
2766
+
2767
+ // btnToggleNotes
2768
+ function bindToggleNotes() {
2769
+ $(document).on('click', '#btnToggleNotes', function() {
2770
+ $(this).toggleClass('toggled');
2771
+
2772
+ if ($(this).hasClass('toggled')) {
2773
+ showAnnotationMarkers();
2774
+ } else {
2775
+ hideAnnotationMarkers();
2776
+ }
2777
+ });
2778
+ }
2779
+
2780
+ function hideAnnotationMarkers() {
2781
+ $('body').addClass('hide-annotations');
2782
+ $('.vitro-marker-label').hide();
2783
+ window.dxfViewer.VisibleDrawable(DrawMode.ANNOTATIONS, false);
2784
+ }
2785
+
2786
+ function showAnnotationMarkers() {
2787
+ $('body').removeClass('hide-annotations');
2788
+ $('.vitro-marker-label').show()
2789
+ window.dxfViewer.VisibleDrawable(DrawMode.ANNOTATIONS, true);
2790
+ }
2791
+
2792
+ // btnToggleSidebarNotes
2793
+ function bindBtnToggleSidebarNotes() {
2794
+ $(document).on('click', '#btnToggleSidebarNotes', function() {
2795
+ $(this).toggleClass('toggled');
2796
+
2797
+ if($(this).hasClass('toggled')) {
2798
+ if(!$('.sidebar').is(':visible')) {
2799
+ expandSidebarNotes();
2800
+ $('body').addClass('vitro-sidebar-notes-expanded');
2801
+ }
2802
+ } else {
2803
+ collapseSidebarNotes();
2804
+ }
2805
+ updateCanvasContainerWidth();
2806
+ });
2807
+ }
2808
+
2809
+ function expandSidebarNotes() {
2810
+ context.initIssueList();
2811
+
2812
+ if ($('#btnCreateNotes').hasClass('toggled')) {
2813
+ $('#btnCreateNotes').removeClass('toggled');
2814
+ removeNewAnnotations();
2815
+ window.dxfViewer.Cancel();
2816
+ }
2817
+
2818
+ collapseSidebarIssueDetail();
2819
+
2820
+ var body = $('body');
2821
+ var bodyScrollWidth = body[0].offsetWidth - body[0].scrollWidth;
2822
+ var vScrollWidth = bodyScrollWidth ? bodyScrollWidth : 0;
2823
+ $('.sidebar').css('right', (vScrollWidth + 'px')).show("slide", { direction: "right" }, 200);
2824
+ body.addClass('vitro-sidebar-notes-expanded');
2825
+ }
2826
+
2827
+ function bindSidebarNotesClose() {
2828
+ $('.sidebar-close').on('click', function() {
2829
+ collapseSidebarNotes();
2830
+ updateCanvasContainerWidth();
2831
+ });
2832
+ }
2833
+
2834
+ // btnToggleSidebarTreeView
2835
+ function bindBtnToggleSidebarTreeView() {
2836
+ $(document).on('click', '#btnToggleSidebarTreeView', function() {
2837
+ $(this).toggleClass('toggled');
2838
+
2839
+ if($(this).hasClass('toggled')) {
2840
+ $('body').addClass('treeview-expanded');
2841
+ $('#treeViewContainerWrap').show();
2842
+ } else {
2843
+ $('#treeViewContainerWrap').hide();
2844
+ $('body').removeClass('treeview-expanded');
2845
+ }
2846
+ });
2847
+ }
2848
+
2849
+ // btnCreateNotes
2850
+ function bindBtnToggleCreateNotes() {
2851
+ $(document).on('click', '#btnCreateNotes', function () {
2852
+ $(this).toggleClass('toggled');
2853
+
2854
+ if ($(this).hasClass('toggled')) {
2855
+ showAnnotationMarkers();
2856
+ $('#btnToggleNotes').addClass('toggled');
2857
+ collapseSidebarNotes();
2858
+ anno.Create();
2859
+ } else {
2860
+ collapseSidebarIssueDetail();
2861
+ removeNewAnnotations();
2862
+ window.dxfViewer.Cancel();
2863
+ }
2864
+ updateCanvasContainerWidth();
2865
+ });
2866
+ }
2867
+
2868
+ function collapseSidebarNotes() {
2869
+ $('.sidebar').hide();
2870
+ $('body').removeClass('vitro-sidebar-notes-expanded');
2871
+ $('#btnToggleSidebarNotes').removeClass('toggled');
2872
+ }
2873
+
2874
+ function collapseSidebarIssueDetail() {
2875
+ $('#issueDetail').hide();
2876
+ $('body').removeClass('vitro-sidebar-notes-expanded');
2877
+ }
2878
+
2879
+ function expandSidebarIssueDetail() {
2880
+ $('#issueDetail').show();
2881
+ $('body').addClass('vitro-sidebar-notes-expanded');
2882
+ }
2883
+
2884
+ // btnToggleSidebarLayers
2885
+ function bindBtnToggleSidebarLayers() {
2886
+ $(document).on('click', '#btnToggleSidebarLayers', function () {
2887
+ $(this).toggleClass('toggled');
2888
+
2889
+ if ($(this).hasClass('toggled')) {
2890
+ $('body').addClass('vitro-sidebar-layers-expanded');
2891
+ $('#layersContainer').show();
2892
+ collapseSidebarSheets();
2893
+ } else {
2894
+ $('#layersContainer').hide();
2895
+ $('body').removeClass('vitro-sidebar-layers-expanded');
2896
+ }
2897
+ updateCanvasContainerWidth();
2898
+ });
2899
+ }
2900
+
2901
+ function collapseSidebarLayers() {
2902
+ $('body').removeClass('vitro-sidebar-layers-expanded');
2903
+ $('#layersContainer').hide();
2904
+ $('#btnToggleSidebarLayers').removeClass('toggled');
2905
+ }
2906
+
2907
+ // btnToggleSidebarSheets
2908
+ function bindBtnToggleSidebarSheets() {
2909
+ $(document).on('click', '#btnToggleSidebarSheets', function () {
2910
+ $(this).toggleClass('toggled');
2911
+
2912
+ if ($(this).hasClass('toggled')) {
2913
+ $('#sidebarContainer').show();
2914
+ $('body').addClass('vitro-sidebar-sheets-expanded');
2915
+ collapseSidebarLayers();
2916
+ } else {
2917
+ $('#sidebarContainer').hide();
2918
+ $('body').removeClass('vitro-sidebar-sheets-expanded');
2919
+ }
2920
+
2921
+ updateCanvasContainerWidth();
2922
+ });
2923
+ }
2924
+
2925
+ function collapseSidebarSheets() {
2926
+ $('#sidebarContainer').hide();
2927
+ $('body').removeClass('vitro-sidebar-sheets-expanded');
2928
+ $('#btnToggleSidebarSheets').removeClass('toggled');
2929
+ }
2930
+
2931
+ function bindBtnZoomIn() {
2932
+ $(document).on('click', '#zoomIn', function () {
2933
+ window.dxfViewer.ZoomIn();
2934
+ });
2935
+ }
2936
+
2937
+ function bindBtnZoomOut() {
2938
+ $(document).on('click', '#zoomOut', function () {
2939
+ window.dxfViewer.ZoomOut();
2940
+ });
2941
+ }
2942
+
2943
+
2944
+ // TODO
2945
+ function bindTreeViewSpanOnClickHandler() {
2946
+ $(document).on('treeViewSpan', function(e, itemId) {
2947
+ // TODO
2948
+ alert('click treeViewSpan, id = ' + itemId);
2949
+ var pickResult = {
2950
+ entity: {
2951
+ id: itemId
2952
+ }
2953
+ };
2954
+ showPropertyInspector(pickResult);
2955
+ });
2956
+ }
2957
+
2958
+ function isTreeViewItemChecked(id) {
2959
+ return $('#checkbox-'+id).is(':checked');
2960
+ }
2961
+
2962
+ // TODO
2963
+ function bindTreeViewCheckboxOnChangeHandler() {
2964
+ $(document).on('treeViewCheckboxChange', function(e, itemId) {
2965
+ // TODO
2966
+ alert('change treeViewCheckbox | id = ' + itemId + ' | checked = ' + isTreeViewItemChecked(itemId));
2967
+ // Get all children
2968
+ //console.log(getChildren(itemId));
2969
+ });
2970
+ }
2971
+ // Super Fast Tree View in JavaScript by Chris Smith - like Xeokit
2972
+ function orphans() {
2973
+ return treeViewData.filter(function(item) {
2974
+ return item.parent === null;
2975
+ });
2976
+ }
2977
+
2978
+ function hasChildren(parentId) {
2979
+ return treeViewData.some(function(item) {
2980
+ return item.parent === parentId;
2981
+ });
2982
+ }
2983
+
2984
+ function getChildren(parentId) {
2985
+ return treeViewData.filter(function(item) {
2986
+ return item.parent === parentId;
2987
+ });
2988
+ }
2989
+
2990
+ function generateListItem(item) {
2991
+ // console.time('generateListItem');
2992
+ const li = document.createElement('li');
2993
+ li.id = 'item-' + item.id;
2994
+
2995
+ if (hasChildren(item.id)) {
2996
+ const a = document.createElement('a');
2997
+ a.href = '#';
2998
+ a.textContent = '+';
2999
+ a.classList.add('plus');
3000
+ a.addEventListener('click', expand);
3001
+ li.appendChild(a);
3002
+ }
3003
+
3004
+ const checkbox = document.createElement('input');
3005
+ checkbox.type = 'checkbox';
3006
+ checkbox.id = 'checkbox-' + item.id;
3007
+
3008
+ if(item.parent === null) {
3009
+ $(checkbox).prop('checked', true);
3010
+ }
3011
+
3012
+ $(checkbox).off('change');
3013
+ $(checkbox).on('change', function(){
3014
+ var _t = $(this);
3015
+ var childInputs = _t.parent().find('ul').find('input[type="checkbox"]');
3016
+ childInputs.prop('checked', _t.is(':checked'));
3017
+ $(document).trigger('treeViewCheckboxChange', [item.id]);
3018
+ });
3019
+ li.appendChild(checkbox);
3020
+
3021
+ const span = document.createElement('span');
3022
+ span.textContent = item.name;
3023
+ $(span).off('click');
3024
+ $(span).on('click', function() {
3025
+ $(document).trigger('treeViewSpan', [item.id]);
3026
+ });
3027
+ li.appendChild(span);
3028
+ // console.timeEnd('generateListItem');
3029
+ return li;
3030
+ }
3031
+
3032
+ function expand(event) {
3033
+ // console.time('expand');
3034
+ event.preventDefault();
3035
+ event.stopPropagation();
3036
+ const et = event.target,
3037
+ parent = et.parentElement,
3038
+ id = parent.id.replace('item-', ''),
3039
+ kids = getChildren(id),
3040
+ items = kids.map(generateListItem),
3041
+ ul = document.createElement('ul');
3042
+ items.forEach(function(li) {
3043
+ if($('#item-'+id + ' > input[type="checkbox"]').length && !$('#item-'+id + ' > input[type="checkbox"]').is(':checked')) {
3044
+ $(li).find('> input[type="checkbox"]').prop('checked', false);
3045
+ } else {
3046
+ $(li).find('> input[type="checkbox"]').prop('checked', true);
3047
+ }
3048
+ ul.appendChild(li);
3049
+ });
3050
+ parent.appendChild(ul);
3051
+ et.classList.remove('plus');
3052
+ et.classList.add('minus');
3053
+ et.textContent = '-';
3054
+ et.removeEventListener('click', expand);
3055
+ et.addEventListener('click', collapse);
3056
+ // console.timeEnd('expand');
3057
+ }
3058
+
3059
+ function collapse(event) {
3060
+ console.time('collapse');
3061
+ event.preventDefault();
3062
+ event.stopPropagation();
3063
+ const et = event.target,
3064
+ parent = et.parentElement,
3065
+ ul = parent.querySelector('ul');
3066
+ parent.removeChild(ul);
3067
+ et.classList.remove('minus');
3068
+ et.classList.add('plus');
3069
+ et.textContent = '+';
3070
+ et.removeEventListener('click', collapse);
3071
+ et.addEventListener('click', expand);
3072
+ // console.timeEnd('collapse');
3073
+ }
3074
+
3075
+ function initTreeView(treeViewContainerSelector) {
3076
+ const root = document.querySelector(treeViewContainerSelector),
3077
+ orphansArray = orphans();
3078
+ if (orphansArray.length) {
3079
+ const items = orphansArray.map(generateListItem),
3080
+ ul = document.createElement('ul');
3081
+ items.forEach(function(li) {
3082
+ ul.appendChild(li);
3083
+ });
3084
+ root.appendChild(ul);
3085
+ }
3086
+ }
3087
+ //------------------------------------------------------------------------------------------------------------------
3088
+ // TreeView Panel resizable
3089
+ //------------------------------------------------------------------------------------------------------------------
3090
+ function initTreeViewPanelResizable() {
3091
+ $('#treeViewContainerWrap').resizable({
3092
+ handles: "e",
3093
+ minWidth: 150
3094
+ });
3095
+ }
3096
+ var canvas = document.getElementById('canvasContainer');
3097
+ window.dxfViewer = new Viewer(canvas, null);
3098
+
3099
+ window.dxfViewer.Load('/api/file/getByItemId/' + context.file.id).then(() => {
3100
+ anno.RemoveLabels();
3101
+ anno.UpdateLabelPosition();
3102
+ document.getElementById(window.dxfViewer.spaceName)?.classList.add('vitro-active');
3103
+ bindThumbnailClick();
3104
+ if (context.onLoaded) {
3105
+ context.onLoaded();
3106
+ }
3107
+ });
3108
+
3109
+ var strokeColorVal = '#2d9cdb';
3110
+
3111
+ var anno = new Annotation({
3112
+ viewer: window.dxfViewer,
3113
+ color: strokeColorVal,
3114
+ onLabelClick: onSelectAnnotation
3115
+ });
3116
+ window.dxfViewer.RegisterDrawablePlugin(anno);
3117
+
3118
+ document.addEventListener('keydown', KeyDown);
3119
+
3120
+ function KeyDown(event) {
3121
+ const code = event.code;
3122
+ if (code == 'Delete') {
3123
+ const selectedId = window.dxfViewer.GetSelectedHandle();
3124
+ if (selectedId) {initi
3125
+ const drawable = window.dxfViewer.GetDrawable(selectedId);
3126
+ if (drawable?.drawMode === DrawMode.ANNOTATIONS) {
3127
+ window.dxfViewer.DeleteDrawable(selectedId);
3128
+ }
3129
+ }
3130
+ }
3131
+ }
3132
+
3133
+ const Subscribe = eventName => {
3134
+ window.dxfViewer.Subscribe(eventName, e => emit('dxf-' + eventName, e));
3135
+ };
3136
+ for (const eventName of ['loaded', 'cleared', 'destroyed', 'resized', 'pointerdown',
3137
+ 'pointerup', 'viewChanged', 'message', 'added', 'selected', 'drawableCreated', 'drawCancelled']) {
3138
+ Subscribe(eventName);
3139
+ }
3140
+
3141
+ function emit(eventName, event) {
3142
+ if (eventName == 'dxf-pointerdown' && event.detail.domEvent.button == 1) {
3143
+ var layout = null;
3144
+ const viewer = window.dxfViewer;
3145
+ const scene = viewer.scene;
3146
+ for (let i = 0; i < scene.layouts.length; i++) {
3147
+ if (window.dxfViewer.spaceName === scene.layouts[i].space) {
3148
+ if (i == scene.layouts.length - 1)
3149
+ layout = scene.layouts[0];
3150
+ else
3151
+ layout = scene.layouts[i + 1];
3152
+
3153
+ break;
3154
+ }
3155
+ }
3156
+
3157
+ if (layout) {
3158
+ window.dxfViewer.SwitchSpace(layout);
3159
+ }
3160
+ }
3161
+ else if (eventName == 'dxf-pointerdown' && event.detail.domEvent.button == 0) {
3162
+ if (!window.dxfViewer.drawMode) {
3163
+ const selectedId = window.dxfViewer.SelectObj(event);
3164
+ }
3165
+ }
3166
+ else if (eventName == 'dxf-selected') {
3167
+ const selectedId = window.dxfViewer.GetSelectedHandle();
3168
+ const annotation = window.dxfViewer.GetDrawable(selectedId);
3169
+ if (annotation && annotation.space.layout.space === dxfViewer.spaceName) {
3170
+ onSelectAnnotation(selectedId);
3171
+ }
3172
+ }
3173
+ else if (eventName == 'dxf-loaded') {
3174
+ const spaceHandle = event.detail.space.layout.spaceHandle;
3175
+ createThumbnail(event.detail.space.layout.name, window.dxfViewer.GetSpace());
3176
+ initNotesBySpaceHandle(spaceHandle);
3177
+ initLayers();
3178
+ window.dxfViewer.controls.minZoom = 0.1;
3179
+ window.dxfViewer.controls.maxZoom = 10;
3180
+ }
3181
+ else if (eventName == 'dxf-drawableCreated') {
3182
+ const markupData = event.detail;
3183
+ if (markupData && markupData.id && markupData.id.includes(ID_PREFIX)) {
3184
+ context.onCreateIssue(markupData);
3185
+ expandSidebarIssueDetail();
3186
+ }
3187
+ }
3188
+ else if (eventName == 'dxf-drawCancelled') {
3189
+
3190
+ }
3191
+ else if (eventName == 'dxf-resized') {
3192
+ anno.UpdateLabelRendererSize();
3193
+ anno.UpdateLabelPosition();
3194
+ if ($('#scaleSelect').data('value') === 'auto') {
3195
+ doZoomAuto();
3196
+ }
3197
+ }
3198
+ else if (eventName == 'dxf-viewChanged') {
3199
+ if (+$('#scaleSelect').data('value') && dxfViewer.camera.zoom !== 1) {
3200
+ updateScaleSelectValue();
3201
+ }
3202
+ anno.UpdateLabelPosition();
3203
+ }
3204
+
3205
+ }
3206
+
3207
+ function destroyed() {
3208
+ window.dxfViewer.Destroy();
3209
+ window.dxfViewer = null;
3210
+ }
3211
+
3212
+ function initNotesBySpaceHandle(spaceHandle) {
3213
+ $.each(context.issueList, function (index, note) {
3214
+ initNote(note, spaceHandle);
3215
+ });
3216
+
3217
+ anno.UpdateLabelPosition();
3218
+ }
3219
+
3220
+ function initLayers() {
3221
+ if (context.initLayers) {
3222
+ context.initLayers(window.dxfViewer.scene.layers);
3223
+ }
3224
+ }
3225
+
3226
+ function initNote(note, spaceHandle) {
3227
+ const markup = JSON.parse(note.markup);
3228
+ if (markup.space.layout.spaceHandle === spaceHandle) {
3229
+ const firstPoint = new three.Vector3(markup.firstPoint.x, markup.firstPoint.y, markup.firstPoint.z);
3230
+ const secondPoint = new three.Vector3(markup.secondPoint.x, markup.secondPoint.y, markup.secondPoint.z);
3231
+ const color = note.statusColor || strokeColorVal;
3232
+ const id = note.id.toString();
3233
+ anno.Add(spaceHandle, firstPoint, secondPoint, id, color);
3234
+ }
3235
+ }
3236
+
3237
+ function bindUpdatePage() {
3238
+ if (context.deleteIssueEvent) {
3239
+ window.addEventListener(context.deleteIssueEvent, updatePage);
3240
+ }
3241
+
3242
+ if (context.updateIssueEvent) {
3243
+ window.addEventListener(context.updateIssueEvent, updatePage);
3244
+ }
3245
+ }
3246
+
3247
+ function updatePage(e) {
3248
+ const item = e.detail.itemList[0];
3249
+ const markup = item.fieldValueMap.markup;
3250
+ context.initIssueList(true);
3251
+ anno.RemoveLabels();
3252
+ removeNewAnnotations();
3253
+
3254
+ if (e.type === context.deleteIssueEvent && markup) {
3255
+ const selectedId = item.fieldValueMap.item_id;
3256
+ deleteNote(selectedId);
3257
+ }
3258
+
3259
+ if (e.type === context.updateIssueEvent && markup) {
3260
+ const newNote = {
3261
+ id: item.fieldValueMap.item_id,
3262
+ markup: markup,
3263
+ statusColor: item.fieldValueMap['task_status']?.fieldValueMap?.color || '',
3264
+ }
3265
+ const spaceHandle = getCurrentSpaceHandle();
3266
+ if (spaceHandle) {
3267
+ deleteNote(newNote.id);
3268
+ initNote(newNote, spaceHandle);
3269
+ }
3270
+
3271
+ if ($('#btnCreateNotes').hasClass('toggled')) {
3272
+ anno.Create();
3273
+ }
3274
+ }
3275
+
3276
+ anno.UpdateLabelPosition();
3277
+ }
3278
+
3279
+ function removeNewAnnotations() {
3280
+ const drawables = window.dxfViewer.drawables;
3281
+ const newAnnotations = drawables.filter(drawable => drawable.drawMode === DrawMode.ANNOTATIONS && drawable.id.includes(ID_PREFIX));
3282
+ if (newAnnotations && newAnnotations.length) {
3283
+ $.each(newAnnotations, function (index, anno) {
3284
+ window.dxfViewer.DeleteDrawable(anno.id);
3285
+ })
3286
+ }
3287
+ }
3288
+
3289
+ function deleteNote(id) {
3290
+ if (id) {
3291
+ const drawable = window.dxfViewer.GetDrawable(id);
3292
+ if (drawable?.drawMode === DrawMode.ANNOTATIONS) {//this is annotation
3293
+ window.dxfViewer.DeleteDrawable(id);
3294
+ }
3295
+ }
3296
+ }
3297
+
3298
+ function getCurrentSpaceHandle() {
3299
+ const spaces = window.dxfViewer.spaces;
3300
+ const currentSpaceName = window.dxfViewer.spaceName;
3301
+ const currentSpace = spaces.get(currentSpaceName);
3302
+ if (currentSpace) {
3303
+ return currentSpace.layout.spaceHandle;
3304
+ }
3305
+
3306
+ return null;
3307
+ }
3308
+
3309
+ function onSelectAnnotation(id) {
3310
+ if (context.selectIssue) {
3311
+ context.selectIssue(id, true);
3312
+ }
3313
+ zoomToAnnotation(id);
3314
+
3315
+ if (!$('#btnToggleSidebarNotes').hasClass('toggled')) {
3316
+ $('#btnToggleSidebarNotes').addClass('toggled');
3317
+ expandSidebarNotes();
3318
+ }
3319
+ updateCanvasContainerWidth();
3320
+ }
3321
+
3322
+ function zoomToAnnotation(id) {
3323
+ const drawable = window.dxfViewer.GetDrawable(id);
3324
+ if (drawable?.drawMode === DrawMode.ANNOTATIONS && drawable.space.layout.space === window.dxfViewer.spaceName) {
3325
+ window.dxfViewer.ZoomTo(drawable.objects);
3326
+ updateScaleSelectValue();
3327
+ } else {
3328
+ window.dxfViewer.FindDxfEntity(id);
3329
+ }
3330
+ }
3331
+
3332
+ function switchSpace(space) {
3333
+ const layout = window.dxfViewer.scene.layouts.find(layout => layout.space === space);
3334
+
3335
+ if (layout) {
3336
+ window.dxfViewer.SwitchSpace(layout);
3337
+ dxfViewer.ZoomTo(dxfViewer.GetSpace(dxfViewer.spaceName).scene);
3338
+ dxfViewer.DoFakeZoomToRedrawScene();
3339
+ anno.RemoveLabels();
3340
+ anno.UpdateLabelPosition();
3341
+ }
3342
+ }
3343
+
3344
+ function createThumbnail(name, space) {
3345
+ if (context.initThumbnail) {
3346
+ const imgUrl = window.dxfViewer.renderer.domElement.toDataURL();
3347
+ context.initThumbnail(space.layout.space, name, imgUrl);
3348
+ }
3349
+ }
3350
+
3351
+ function bindThumbnailClick() {
3352
+ $('.vitro-thumbnail').on('click', function () {
3353
+ switchSpace($(this).attr('id'));
3354
+ $('.vitro-thumbnail').removeClass('vitro-active');
3355
+ $(this).addClass('vitro-active');
3356
+ });
3357
+ }
3358
+
3359
+ function getSpaceImage(space, renderer) {
3360
+ const dataURL = window.dxfViewer.renderer.domElement.toDataURL();
3361
+
3362
+ const img = document.createElement('img');
3363
+ img.src = dataURL;
3364
+
3365
+ return img;
3366
+ }
3367
+
3368
+ function onChangeLayerVisibility(name, show) {
3369
+ window.dxfViewer.ShowLayer(name, show);
3370
+ }
3371
+
3372
+ function bindSelect(select) {
3373
+ $(select).click(function (e) {
3374
+ let input = $(select).children()[0];
3375
+ if (e.target.localName == 'li') {
3376
+ let value = $(e.target).attr('value');
3377
+ let text = $(e.target).text();
3378
+ $(input).val(text);
3379
+ $(input).data('value', value);
3380
+ input.dispatchEvent(new CustomEvent('change', { detail: { value: value } }));
3381
+ $(select).removeClass('vitro-active');
3382
+ }
3383
+ else if (e.currentTarget == select && !$(select).hasClass('vitro-active')) {
3384
+ $(select).addClass('vitro-active');
3385
+ } else {
3386
+ $(select).removeClass('vitro-active');
3387
+ }
3388
+ });
3389
+
3390
+ $(document).click(function (e) {
3391
+ if (!Array.from($(select).children()).includes(e.target)) {
3392
+ $(select).removeClass('vitro-active');
3393
+ }
3394
+ });
3395
+ }
3396
+
3397
+ function bindSelectList() {
3398
+ $('.vitro-select').each(function (i, item) {
3399
+ bindSelect(item);
3400
+ });
3401
+ }
3402
+
3403
+ function bindScaleSelectChange() {
3404
+ $('#pageAutoOption').text(context.scaleAutoLabel);
3405
+ $('#scaleSelect').data('value', $('#scaleSelect').val());
3406
+ $('#scaleSelect').val(context.scaleAutoLabel);
3407
+ $('#scaleSelect').on('change', function (e) {
3408
+ const scale = e.detail.value;
3409
+ changeZoom(scale);
3410
+ });
3411
+ }
3412
+
3413
+ function doZoomPageFit() {
3414
+ const space = dxfViewer.GetSpace(dxfViewer.spaceName);
3415
+ if (space) {
3416
+ dxfViewer.ZoomTo(space.scene);
3417
+ }
3418
+ }
3419
+
3420
+ function doZoomAuto() {
3421
+ doZoomPageFit();
3422
+ window.dxfViewer.DoFakeZoomToRedrawScene();
3423
+ }
3424
+
3425
+ function changeZoom(scale) {
3426
+ if (scale === 'auto') {
3427
+ doZoomAuto();
3428
+ } else {
3429
+ scale = +scale;
3430
+ if (scale === 1) {
3431
+ doZoomPageFit();
3432
+ } else {
3433
+ doZoomPageFit();
3434
+ window.dxfViewer.ZoomOut(scale);
3435
+ }
3436
+ }
3437
+ }
3438
+
3439
+ function updateScaleSelectValue() {
3440
+ const cameraZoom = window.dxfViewer.camera.zoom;
3441
+ const cameraZoomVal = Math.round(cameraZoom * 100);
3442
+ const newVal = cameraZoomVal + '%';
3443
+ if ($('#scaleSelect').val() !== newVal) {
3444
+ $('#scaleSelect').val(newVal);
3445
+ $('#scaleSelect').data('value', cameraZoom);
3446
+ }
3447
+ }
3448
+
3449
+ context.zoomToAnnotation = zoomToAnnotation;
3450
+ context.onChangeLayerVisibility = onChangeLayerVisibility;
3451
+ context.expandSidebarIssueDetail = () => {
3452
+ expandSidebarIssueDetail();
3453
+ collapseSidebarNotes();
3454
+ };
3455
+
3456
+ function bindPanelResize() {
3457
+ bindResizerMousedown();
3458
+ bindResizerMouseup();
3459
+ bindResizerMousemove();
3460
+ }
3461
+
3462
+ function bindResizerMousedown() {
3463
+ $('.vitro-resizer').on('mousedown', function (e) {
3464
+ e.preventDefault();
3465
+ e.stopPropagation();
3466
+ $(this).addClass('vitro-draggable');
3467
+ });
3468
+ }
3469
+
3470
+ function bindResizerMouseup() {
3471
+ $(document).on('mouseup', function (e) {
3472
+ e.preventDefault();
3473
+ e.stopPropagation();
3474
+ $('.vitro-resizer').removeClass('vitro-draggable');
3475
+ });
3476
+ }
3477
+
3478
+ function bindResizerMousemove() {
3479
+ $(document).on('mousemove', function (e) {
3480
+ if ($('.vitro-draggable').length) {
3481
+ e.preventDefault();
3482
+ e.stopPropagation();
3483
+ if ($('.vitro-draggable').hasClass('vitro-left')) {
3484
+ $('.vitro-draggable').parent().width($(document).width() - e.clientX);
3485
+ } else {
3486
+ $('.vitro-draggable').parent().width(e.clientX);
3487
+ }
3488
+ updateCanvasContainerWidth();
3489
+ }
3490
+ });
3491
+ }
3492
+
3493
+ function updateCanvasContainerWidth() {
3494
+ let insetStart = 0;
3495
+ let insetEnd = 0;
3496
+ $('.vitro-panel').each((idx, panel) => {
3497
+ const rect = panel.getBoundingClientRect();
3498
+ if (rect.left === 0) {
3499
+ insetStart += panel.clientWidth;
3500
+ } else if (rect.right) {
3501
+ insetEnd += panel.clientWidth;
3502
+ }
3503
+ });
3504
+ $('#canvasContainer').css('width', `calc(100% - ${insetStart + insetEnd}px)`);
3505
+ $('#canvasContainer').css('inset-inline-start', `${insetStart}px`);
3506
+ $('#canvasContainer').css('inset-inline-end', `${insetEnd}px`);
3507
+ }
3508
+ $(document).ready(function () {
3509
+ bindBtnToggleSidebarNotes();
3510
+ bindSidebarNotesClose();
3511
+ bindDeleteNoteEventHandler();
3512
+
3513
+ fixNotesTextareaBlur();
3514
+
3515
+ bindToggleNotes();
3516
+
3517
+ bindPropInspectorClose();
3518
+ initPropertySetsResizable();
3519
+
3520
+ bindBtnToggleCreateNotes();
3521
+ bindUpdatePage();
3522
+ bindBtnToggleSidebarLayers();
3523
+ bindBtnToggleSidebarSheets();
3524
+
3525
+ bindBtnZoomIn();
3526
+ bindBtnZoomOut();
3527
+
3528
+ bindSelectList();
3529
+ bindScaleSelectChange();
3530
+ bindPanelResize();
3531
+ });
3532
+
3533
+ var userLocale =
3534
+ navigator.languages && navigator.languages.length
3535
+ ? navigator.languages[0]
3536
+ : navigator.language;
3537
+
3538
+ document.webL10n.setLanguage(userLocale, function () {
3539
+ document.webL10n.translate();
3540
+ });
3541
+ }