@vitrosoftware/common-ui-ts 1.1.122 → 1.1.124
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.
- package/css/std/controls/checkbox/checkbox.css +4 -0
- package/css/std/controls/checkbox/img/checkbox-indeterminate.svg +4 -0
- package/css/std/controls/date-picker/date-picker.css +1 -25
- package/css/std/controls/dxf-viewer/annotation.css +85 -0
- package/css/std/controls/dxf-viewer/common.css +24 -0
- package/css/std/controls/dxf-viewer/dxf-viewer-index.css +14081 -0
- package/css/std/controls/dxf-viewer/dxf-viewer.css +194 -0
- package/css/std/controls/dxf-viewer/img/cancel-dark-grey.svg +5 -0
- package/css/std/controls/dxf-viewer/img/collapse-bottom.svg +5 -0
- package/css/std/controls/dxf-viewer/img/collapse-up-blue.svg +5 -0
- package/css/std/controls/dxf-viewer/img/delete-active.svg +11 -0
- package/css/std/controls/dxf-viewer/img/delete.svg +11 -0
- package/css/std/controls/dxf-viewer/img/draw-annotation.svg +3 -0
- package/css/std/controls/dxf-viewer/img/invisible-eye.svg +4 -0
- package/css/std/controls/dxf-viewer/img/show-annotation.svg +3 -0
- package/css/std/controls/dxf-viewer/img/sidebar-layers-toggle.svg +6 -0
- package/css/std/controls/dxf-viewer/img/sidebar-notes-toggle.svg +5 -0
- package/css/std/controls/dxf-viewer/img/sidebar-resizer.svg +6 -0
- package/css/std/controls/dxf-viewer/img/sidebar-toggle.svg +7 -0
- package/css/std/controls/dxf-viewer/img/visible-eye.svg +4 -0
- package/css/std/controls/dxf-viewer/img/zoom-in.svg +6 -0
- package/css/std/controls/dxf-viewer/img/zoom-out.svg +5 -0
- package/css/std/controls/dxf-viewer/layer-list.css +104 -0
- package/css/std/controls/dxf-viewer/panel.css +34 -0
- package/css/std/controls/dxf-viewer/prop-inspector.css +102 -0
- package/css/std/controls/dxf-viewer/select.css +111 -0
- package/css/std/controls/dxf-viewer/sidebar.css +190 -0
- package/css/std/controls/dxf-viewer/thumbnail-list.css +65 -0
- package/css/std/controls/dxf-viewer/toolbar.css +117 -0
- package/css/std/controls/dxf-viewer/treeview.css +3 -0
- package/css/std/controls/dxf-viewer/treeview.panel.css +108 -0
- package/css/std/controls/error-message/error-message.css +22 -0
- package/css/std/controls/image-picker/image-picker.css +0 -26
- package/css/std/controls/input/input.css +1 -24
- package/css/std/controls/issue-tile/issue-tile-header.css +1 -0
- package/css/std/controls/login/ntlm-authentication-form.css +9 -12
- package/css/std/controls/lookup-picker/lookup-picker-value-list.css +38 -2
- package/css/std/controls/lookup-picker/lookup-picker.css +1 -25
- package/css/std/controls/table-view/treegrid-context-menu.css +44 -18
- package/css/std/controls/table-view/treegrid-message.css +4 -4
- package/css/std/controls/time-picker/time-picker.css +1 -25
- package/dist/index.css +81 -143
- package/dist/index.js +15137 -489
- package/dist/index.js.map +1 -1
- package/dist/src/controls/Checkbox/Checkbox.d.ts +1 -0
- package/dist/src/controls/DxfViewer/DxfViewer.d.ts +6 -0
- package/dist/src/controls/DxfViewer/DxfViewerContext.d.ts +31 -0
- package/dist/src/controls/DxfViewer/Layer.d.ts +9 -0
- package/dist/src/controls/DxfViewer/LayerList.d.ts +11 -0
- package/dist/src/controls/DxfViewer/Thumbnail.d.ts +7 -0
- package/dist/src/controls/DxfViewer/ThumbnailList.d.ts +6 -0
- package/dist/src/controls/DxfViewer/Viewer.d.ts +6 -0
- package/dist/src/controls/ErrorMessage/ErrorMessage.d.ts +6 -0
- package/dist/src/controls/Login/FormRef.d.ts +3 -0
- package/dist/src/controls/Login/LoginConstants.d.ts +2 -1
- package/dist/src/controls/Login/LoginFormRef.d.ts +2 -2
- package/dist/src/controls/Login/NTLMAuthenticationForm.d.ts +5 -2
- package/dist/src/controls/LookupPicker/LookupPicker.d.ts +2 -0
- package/dist/src/controls/LookupPicker/ValueList.d.ts +2 -0
- package/dist/src/controls/TableView/TableViewConstants.d.ts +11 -0
- package/dist/src/controls/TableView/TreeGridTableViewContextImpl.d.ts +1 -0
- package/dist/src/controls/TreeView/TreeView.d.ts +4 -0
- package/dist/src/controls/TreeView/TreeViewConfig.d.ts +3 -0
- package/dist/src/controls/TreeView/TreeViewConstants.d.ts +2 -1
- package/dist/src/index.d.ts +7 -1
- package/lib/dxf-viewer/BatchingKey.js +91 -0
- package/lib/dxf-viewer/DxfFetcher.js +39 -0
- package/lib/dxf-viewer/DxfScene.js +2695 -0
- package/lib/dxf-viewer/DxfViewer.js +1056 -0
- package/lib/dxf-viewer/DxfWorker.js +229 -0
- package/lib/dxf-viewer/DynamicBuffer.js +100 -0
- package/lib/dxf-viewer/HatchCalculator.js +345 -0
- package/lib/dxf-viewer/LinearDimension.js +323 -0
- package/lib/dxf-viewer/MTextFormatParser.js +211 -0
- package/lib/dxf-viewer/MaterialKey.js +37 -0
- package/lib/dxf-viewer/OrbitControls.js +1253 -0
- package/lib/dxf-viewer/Pattern.js +94 -0
- package/lib/dxf-viewer/RBTree.js +471 -0
- package/lib/dxf-viewer/TextRenderer.js +1038 -0
- package/lib/dxf-viewer/index.js +42 -0
- package/lib/dxf-viewer/math/Matrix2.js +77 -0
- package/lib/dxf-viewer/math/utils.js +59 -0
- package/lib/dxf-viewer/parser/AutoCadColorIndex.js +265 -0
- package/lib/dxf-viewer/parser/DimStyleCodes.js +33 -0
- package/lib/dxf-viewer/parser/DxfArrayScanner.js +143 -0
- package/lib/dxf-viewer/parser/DxfParser.js +980 -0
- package/lib/dxf-viewer/parser/ExtendedDataParse-My.js +91 -0
- package/lib/dxf-viewer/parser/ExtendedDataParser.js +123 -0
- package/lib/dxf-viewer/parser/ParseHelpers.js +142 -0
- package/lib/dxf-viewer/parser/entities/3dface.js +83 -0
- package/lib/dxf-viewer/parser/entities/arc.js +38 -0
- package/lib/dxf-viewer/parser/entities/attdef.js +89 -0
- package/lib/dxf-viewer/parser/entities/attrib.js +34 -0
- package/lib/dxf-viewer/parser/entities/attribute.js +109 -0
- package/lib/dxf-viewer/parser/entities/circle.js +43 -0
- package/lib/dxf-viewer/parser/entities/dimension.js +72 -0
- package/lib/dxf-viewer/parser/entities/ellipse.js +46 -0
- package/lib/dxf-viewer/parser/entities/hatch.js +343 -0
- package/lib/dxf-viewer/parser/entities/insert.js +62 -0
- package/lib/dxf-viewer/parser/entities/leader.js +84 -0
- package/lib/dxf-viewer/parser/entities/line.js +34 -0
- package/lib/dxf-viewer/parser/entities/lwpolyline.js +100 -0
- package/lib/dxf-viewer/parser/entities/mtext.js +54 -0
- package/lib/dxf-viewer/parser/entities/point.js +35 -0
- package/lib/dxf-viewer/parser/entities/polyline.js +92 -0
- package/lib/dxf-viewer/parser/entities/solid.js +40 -0
- package/lib/dxf-viewer/parser/entities/spline.js +70 -0
- package/lib/dxf-viewer/parser/entities/text.js +47 -0
- package/lib/dxf-viewer/parser/entities/vertex.js +62 -0
- package/lib/dxf-viewer/parser/entities/viewport.js +56 -0
- package/lib/dxf-viewer/parser/objects/dictionary.js +29 -0
- package/lib/dxf-viewer/parser/objects/layout.js +35 -0
- package/lib/dxf-viewer/parser/objects/xrecord.js +29 -0
- package/lib/opentype/opentype.module.js +14571 -0
- package/lib/three/CSS2DRenderer.js +235 -0
- package/lib/three/three.module.js +49912 -0
- package/package.json +12 -10
- package/src/controls/BimViewer/js/bim-viewer.js +2 -2
- package/src/controls/DxfViewer/js/dxf-viewer.js +3580 -0
- package/src/controls/PdfViewer/js/pdf-viewer.js +1 -1
- package/css/std/controls/input/img/error-message.svg +0 -6
- package/css/std/controls/lookup-picker/img/error-message.svg +0 -6
- package/css/std/controls/time-picker/img/error-message.svg +0 -6
- /package/css/std/controls/{date-picker → error-message}/img/error-message.svg +0 -0
|
@@ -0,0 +1,1056 @@
|
|
|
1
|
+
import * as three from "/resource/dxfViewer/js/three/three.module.js"
|
|
2
|
+
import { BatchingKey } from "./BatchingKey.js"
|
|
3
|
+
import { DxfWorker } from "./DxfWorker.js"
|
|
4
|
+
import { MaterialKey } from "./MaterialKey.js"
|
|
5
|
+
import { ColorCode, DxfScene } from "./DxfScene.js"
|
|
6
|
+
import { OrbitControls } from "./OrbitControls.js"
|
|
7
|
+
import { RBTree } from "./RBTree.js"
|
|
8
|
+
|
|
9
|
+
export { Batch, Layer, Block };
|
|
10
|
+
|
|
11
|
+
/** Level in "message" events. */
|
|
12
|
+
const MessageLevel = Object.freeze({
|
|
13
|
+
INFO: "info",
|
|
14
|
+
WARN: "warn",
|
|
15
|
+
ERROR: "error"
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
/** The representation class for the viewer, based on Three.js WebGL renderer. */
|
|
19
|
+
export class DxfViewer {
|
|
20
|
+
|
|
21
|
+
/** @param domContainer Container element to create the canvas in. Usually empty div. Should not
|
|
22
|
+
* have padding if auto-resize feature is used.
|
|
23
|
+
* @param options Some options can be overridden if specified. See DxfViewer.DefaultOptions.
|
|
24
|
+
*/
|
|
25
|
+
constructor(domContainer, options = null) {
|
|
26
|
+
this.domContainer = domContainer
|
|
27
|
+
this.options = Object.create(DxfViewer.DefaultOptions)
|
|
28
|
+
if (options) {
|
|
29
|
+
Object.assign(this.options, options)
|
|
30
|
+
}
|
|
31
|
+
options = this.options
|
|
32
|
+
|
|
33
|
+
this.clearColor = this.options.clearColor.getHex()
|
|
34
|
+
|
|
35
|
+
this.scene = new three.Scene()
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
this.renderer = new three.WebGLRenderer({
|
|
39
|
+
alpha: options.canvasAlpha,
|
|
40
|
+
premultipliedAlpha: options.canvasPremultipliedAlpha,
|
|
41
|
+
antialias: options.antialias,
|
|
42
|
+
depth: false,
|
|
43
|
+
preserveDrawingBuffer: true
|
|
44
|
+
})
|
|
45
|
+
} catch (e) {
|
|
46
|
+
console.log("Failed to create renderer: " + e)
|
|
47
|
+
this.renderer = null
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
const renderer = this.renderer
|
|
51
|
+
renderer.setPixelRatio(window.devicePixelRatio)
|
|
52
|
+
|
|
53
|
+
const camera = this.camera = new three.OrthographicCamera(-1, 1, 1, -1, 0.1, 2);
|
|
54
|
+
camera.position.z = 1
|
|
55
|
+
camera.position.x = 0
|
|
56
|
+
camera.position.y = 0
|
|
57
|
+
|
|
58
|
+
this.simpleColorMaterial = []
|
|
59
|
+
this.simplePointMaterial = []
|
|
60
|
+
for (let i = 0; i < InstanceType.MAX; i++) {
|
|
61
|
+
this.simpleColorMaterial[i] = this._CreateSimpleColorMaterial(i)
|
|
62
|
+
this.simplePointMaterial[i] = this._CreateSimplePointMaterial(i)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
renderer.setClearColor(options.clearColor, options.clearAlpha)
|
|
66
|
+
|
|
67
|
+
if (options.autoResize) {
|
|
68
|
+
this.canvasWidth = domContainer.clientWidth
|
|
69
|
+
this.canvasHeight = domContainer.clientHeight
|
|
70
|
+
domContainer.style.position = "relative"
|
|
71
|
+
} else {
|
|
72
|
+
this.canvasWidth = options.canvasWidth
|
|
73
|
+
this.canvasHeight = options.canvasHeight
|
|
74
|
+
this.resizeObserver = null
|
|
75
|
+
}
|
|
76
|
+
renderer.setSize(this.canvasWidth, this.canvasHeight)
|
|
77
|
+
|
|
78
|
+
this.canvas = renderer.domElement
|
|
79
|
+
domContainer.style.display = "block"
|
|
80
|
+
if (options.autoResize) {
|
|
81
|
+
this.canvas.style.position = "absolute"
|
|
82
|
+
this.resizeObserver = new ResizeObserver(entries => this._OnResize(entries[0]))
|
|
83
|
+
this.resizeObserver.observe(domContainer)
|
|
84
|
+
}
|
|
85
|
+
domContainer.appendChild(this.canvas)
|
|
86
|
+
|
|
87
|
+
this.canvas.addEventListener("pointerdown", this._OnPointerEvent.bind(this))
|
|
88
|
+
this.canvas.addEventListener("pointerup", this._OnPointerEvent.bind(this))
|
|
89
|
+
|
|
90
|
+
this.Render()
|
|
91
|
+
|
|
92
|
+
/* Indexed by MaterialKey, value is {key, material}. */
|
|
93
|
+
this.materials = new RBTree((m1, m2) => m1.key.Compare(m2.key))
|
|
94
|
+
/* Indexed by layer name, value is Layer instance. */
|
|
95
|
+
this.layers = new Map()
|
|
96
|
+
/* Indexed by block name, value is Block instance. */
|
|
97
|
+
this.blocks = new Map()
|
|
98
|
+
|
|
99
|
+
/** Set during data loading. */
|
|
100
|
+
this.worker = null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** @return {boolean} True if renderer exists. May be false in case when WebGL context is lost
|
|
104
|
+
* (e.g. after wake up from sleep). In such case page should be reloaded.
|
|
105
|
+
*/
|
|
106
|
+
HasRenderer() {
|
|
107
|
+
return Boolean(this.renderer)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @returns {three.WebGLRenderer | null} Returns the created Three.js renderer.
|
|
112
|
+
*/
|
|
113
|
+
GetRenderer(){
|
|
114
|
+
return this.renderer;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
GetCanvas() {
|
|
118
|
+
return this.canvas
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
GetDxf() {
|
|
122
|
+
return this.parsedDxf
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
SetSize(width, height) {
|
|
126
|
+
this._EnsureRenderer()
|
|
127
|
+
|
|
128
|
+
const hScale = width / this.canvasWidth
|
|
129
|
+
const vScale = height / this.canvasHeight
|
|
130
|
+
|
|
131
|
+
const cam = this.camera
|
|
132
|
+
const centerX = (cam.left + cam.right) / 2
|
|
133
|
+
const centerY = (cam.bottom + cam.top) / 2
|
|
134
|
+
const camWidth = cam.right - cam.left
|
|
135
|
+
const camHeight = cam.top - cam.bottom
|
|
136
|
+
cam.left = centerX - hScale * camWidth / 2
|
|
137
|
+
cam.right = centerX + hScale * camWidth / 2
|
|
138
|
+
cam.bottom = centerY - vScale * camHeight / 2
|
|
139
|
+
cam.top = centerY + vScale * camHeight / 2
|
|
140
|
+
cam.updateProjectionMatrix()
|
|
141
|
+
|
|
142
|
+
this.canvasWidth = width
|
|
143
|
+
this.canvasHeight = height
|
|
144
|
+
this.renderer.setSize(width, height)
|
|
145
|
+
if (this.controls) {
|
|
146
|
+
this.controls.update()
|
|
147
|
+
}
|
|
148
|
+
this._Emit("resized", {width, height})
|
|
149
|
+
this._Emit("viewChanged")
|
|
150
|
+
this.Render()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Load DXF into the viewer. Old content is discarded, state is reset.
|
|
154
|
+
* @param url {string} DXF file URL.
|
|
155
|
+
* @param fonts {?string[]} List of font URLs. Files should have typeface.js format. Fonts are
|
|
156
|
+
* used in the specified order, each one is checked until necessary glyph is found. Text is not
|
|
157
|
+
* rendered if fonts are not specified.
|
|
158
|
+
* @param progressCbk {?Function} (phase, processedSize, totalSize)
|
|
159
|
+
* Possible phase values:
|
|
160
|
+
* * "font"
|
|
161
|
+
* * "fetch"
|
|
162
|
+
* * "parse"
|
|
163
|
+
* * "prepare"
|
|
164
|
+
* @param workerFactory {?Function} Factory for worker creation. The worker script should
|
|
165
|
+
* invoke DxfViewer.SetupWorker() function.
|
|
166
|
+
*/
|
|
167
|
+
async Load({url, fonts = null, progressCbk = null, workerFactory = null}) {
|
|
168
|
+
if (url === null || url === undefined) {
|
|
169
|
+
throw new Error("`url` parameter is not specified")
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this._EnsureRenderer()
|
|
173
|
+
|
|
174
|
+
this.Clear()
|
|
175
|
+
|
|
176
|
+
this.worker = new DxfWorker(workerFactory ? workerFactory() : null)
|
|
177
|
+
const {scene, dxf} = await this.worker.Load(url, fonts, this.options, progressCbk)
|
|
178
|
+
await this.worker.Destroy()
|
|
179
|
+
this.worker = null
|
|
180
|
+
this.parsedDxf = dxf
|
|
181
|
+
|
|
182
|
+
this.origin = scene.origin
|
|
183
|
+
this.bounds = scene.bounds
|
|
184
|
+
this.hasMissingChars = scene.hasMissingChars
|
|
185
|
+
|
|
186
|
+
for (const layer of scene.layers) {
|
|
187
|
+
this.layers.set(layer.name, new Layer(layer.name, layer.displayName, layer.color))
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Load all blocks on the first pass. */
|
|
191
|
+
for (const batch of scene.batches) {
|
|
192
|
+
if (batch.key.blockName !== null &&
|
|
193
|
+
batch.key.geometryType !== BatchingKey.GeometryType.BLOCK_INSTANCE &&
|
|
194
|
+
batch.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE) {
|
|
195
|
+
|
|
196
|
+
let block = this.blocks.get(batch.key.blockName)
|
|
197
|
+
if (!block) {
|
|
198
|
+
block = new Block()
|
|
199
|
+
this.blocks.set(batch.key.blockName, block)
|
|
200
|
+
}
|
|
201
|
+
block.PushBatch(new Batch(this, scene, batch))
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
console.log(`DXF scene:
|
|
206
|
+
${scene.batches.length} batches,
|
|
207
|
+
${this.layers.size} layers,
|
|
208
|
+
${this.blocks.size} blocks,
|
|
209
|
+
vertices ${scene.vertices.byteLength} B,
|
|
210
|
+
indices ${scene.indices.byteLength} B
|
|
211
|
+
transforms ${scene.transforms.byteLength} B`)
|
|
212
|
+
|
|
213
|
+
/* Instantiate all entities. */
|
|
214
|
+
for (const batch of scene.batches) {
|
|
215
|
+
this._LoadBatch(scene, batch)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
this._Emit("loaded")
|
|
219
|
+
|
|
220
|
+
if (scene.bounds) {
|
|
221
|
+
this.FitView(scene.bounds.minX - scene.origin.x, scene.bounds.maxX - scene.origin.x,
|
|
222
|
+
scene.bounds.minY - scene.origin.y, scene.bounds.maxY - scene.origin.y)
|
|
223
|
+
} else {
|
|
224
|
+
this._Message("Empty document", MessageLevel.WARN)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (this.hasMissingChars) {
|
|
228
|
+
this._Message("Some characters cannot be properly displayed due to missing fonts",
|
|
229
|
+
MessageLevel.WARN)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
this._CreateControls()
|
|
233
|
+
this.Render()
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
Render() {
|
|
237
|
+
this._EnsureRenderer()
|
|
238
|
+
this.renderer.render(this.scene, this.camera)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** @return {Iterable<{name:String, color:number}>} List of layer names. */
|
|
242
|
+
GetLayers() {
|
|
243
|
+
const result = []
|
|
244
|
+
for (const lyr of this.layers.values()) {
|
|
245
|
+
result.push({
|
|
246
|
+
name: lyr.name,
|
|
247
|
+
displayName: lyr.displayName,
|
|
248
|
+
color: this._TransformColor(lyr.color)
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
return result
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
ShowLayer(name, show) {
|
|
255
|
+
this._EnsureRenderer()
|
|
256
|
+
const layer = this.layers.get(name)
|
|
257
|
+
if (!layer) {
|
|
258
|
+
return
|
|
259
|
+
}
|
|
260
|
+
for (const obj of layer.objects) {
|
|
261
|
+
obj.visible = show
|
|
262
|
+
}
|
|
263
|
+
this.Render()
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** Reset the viewer state. */
|
|
267
|
+
Clear() {
|
|
268
|
+
this._EnsureRenderer()
|
|
269
|
+
if (this.worker) {
|
|
270
|
+
this.worker.Destroy(true)
|
|
271
|
+
this.worker = null
|
|
272
|
+
}
|
|
273
|
+
if (this.controls) {
|
|
274
|
+
this.controls.dispose()
|
|
275
|
+
this.controls = null
|
|
276
|
+
}
|
|
277
|
+
this.scene.clear()
|
|
278
|
+
for (const layer of this.layers.values()) {
|
|
279
|
+
layer.Dispose()
|
|
280
|
+
}
|
|
281
|
+
this.layers.clear()
|
|
282
|
+
this.blocks.clear()
|
|
283
|
+
this.materials.each(e => e.material.dispose())
|
|
284
|
+
this.materials.clear()
|
|
285
|
+
this.SetView({x: 0, y: 0}, 2)
|
|
286
|
+
this._Emit("cleared")
|
|
287
|
+
this.Render()
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Free all resources. The viewer object should not be used after this method was called. */
|
|
291
|
+
Destroy() {
|
|
292
|
+
if (!this.HasRenderer()) {
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
if (this.resizeObserver) {
|
|
296
|
+
this.resizeObserver.disconnect()
|
|
297
|
+
}
|
|
298
|
+
this.Clear()
|
|
299
|
+
this._Emit("destroyed")
|
|
300
|
+
for (const m of this.simplePointMaterial) {
|
|
301
|
+
m.dispose()
|
|
302
|
+
}
|
|
303
|
+
for (const m of this.simpleColorMaterial) {
|
|
304
|
+
m.dispose()
|
|
305
|
+
}
|
|
306
|
+
this.simplePointMaterial = null
|
|
307
|
+
this.simpleColorMaterial = null
|
|
308
|
+
this.renderer.dispose()
|
|
309
|
+
this.renderer = null
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
SetView(center, width) {
|
|
313
|
+
const aspect = this.canvasWidth / this.canvasHeight
|
|
314
|
+
const height = width / aspect
|
|
315
|
+
const cam = this.camera
|
|
316
|
+
cam.left = -width / 2
|
|
317
|
+
cam.right = width / 2
|
|
318
|
+
cam.top = height / 2
|
|
319
|
+
cam.bottom = -height / 2
|
|
320
|
+
cam.zoom = 1
|
|
321
|
+
cam.position.set(center.x, center.y, 1)
|
|
322
|
+
cam.rotation.set(0, 0, 0)
|
|
323
|
+
cam.updateMatrix()
|
|
324
|
+
cam.updateProjectionMatrix()
|
|
325
|
+
this._Emit("viewChanged")
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** Set view to fit the specified bounds. */
|
|
329
|
+
FitView(minX, maxX, minY, maxY, padding = 0.1) {
|
|
330
|
+
const aspect = this.canvasWidth / this.canvasHeight
|
|
331
|
+
let width = maxX - minX
|
|
332
|
+
const height = maxY - minY
|
|
333
|
+
const center = {x: minX + width / 2, y: minY + height / 2}
|
|
334
|
+
if (height * aspect > width) {
|
|
335
|
+
width = height * aspect
|
|
336
|
+
}
|
|
337
|
+
if (width <= Number.MIN_VALUE * 2) {
|
|
338
|
+
width = 1
|
|
339
|
+
}
|
|
340
|
+
this.SetView(center, width * (1 + padding))
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** @return {Scene} three.js scene for the viewer. Can be used to add custom entities on the
|
|
344
|
+
* scene. Remember to apply scene origin available via GetOrigin() method.
|
|
345
|
+
*/
|
|
346
|
+
GetScene() {
|
|
347
|
+
return this.scene
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** @return {Camera} three.js camera for the viewer. */
|
|
351
|
+
GetCamera() {
|
|
352
|
+
return this.camera
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/** @return {Vector2} Scene origin in global drawing coordinates. */
|
|
356
|
+
GetOrigin() {
|
|
357
|
+
return this.origin
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Subscribe to the specified event. The following events are defined:
|
|
361
|
+
* * "loaded" - new scene loaded.
|
|
362
|
+
* * "cleared" - current scene cleared.
|
|
363
|
+
* * "destroyed" - viewer instance destroyed.
|
|
364
|
+
* * "resized" - viewport size changed. Details: {width, height}
|
|
365
|
+
* * "pointerdown" - Details: {domEvent, position:{x,y}}, position is in scene coordinates.
|
|
366
|
+
* * "pointerup"
|
|
367
|
+
* * "viewChanged"
|
|
368
|
+
* * "message" - Some message from the viewer. {message: string, level: string}.
|
|
369
|
+
*
|
|
370
|
+
* @param eventName {string}
|
|
371
|
+
* @param eventHandler {function} Accepts event object.
|
|
372
|
+
*/
|
|
373
|
+
Subscribe(eventName, eventHandler) {
|
|
374
|
+
this._EnsureRenderer()
|
|
375
|
+
this.canvas.addEventListener(EVENT_NAME_PREFIX + eventName, eventHandler)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** Unsubscribe from previously subscribed event. The arguments should match previous
|
|
379
|
+
* Subscribe() call.
|
|
380
|
+
*
|
|
381
|
+
* @param eventName {string}
|
|
382
|
+
* @param eventHandler {function}
|
|
383
|
+
*/
|
|
384
|
+
Unsubscribe(eventName, eventHandler) {
|
|
385
|
+
this._EnsureRenderer()
|
|
386
|
+
this.canvas.removeEventListener(EVENT_NAME_PREFIX + eventName, eventHandler)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// /////////////////////////////////////////////////////////////////////////////////////////////
|
|
390
|
+
|
|
391
|
+
_EnsureRenderer() {
|
|
392
|
+
if (!this.HasRenderer()) {
|
|
393
|
+
throw new Error("WebGL renderer not available. " +
|
|
394
|
+
"Probable WebGL context loss, try refreshing the page.")
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
_CreateControls() {
|
|
399
|
+
const controls = this.controls = new OrbitControls(this.camera, this.canvas)
|
|
400
|
+
controls.enableRotate = false
|
|
401
|
+
controls.mouseButtons = {
|
|
402
|
+
LEFT: three.MOUSE.PAN,
|
|
403
|
+
MIDDLE: three.MOUSE.DOLLY
|
|
404
|
+
}
|
|
405
|
+
controls.touches = {
|
|
406
|
+
ONE: three.TOUCH.PAN,
|
|
407
|
+
TWO: three.TOUCH.DOLLY_PAN
|
|
408
|
+
}
|
|
409
|
+
controls.zoomSpeed = 3
|
|
410
|
+
controls.mouseZoomSpeedFactor = 0.05
|
|
411
|
+
controls.target = new three.Vector3(this.camera.position.x, this.camera.position.y, 0)
|
|
412
|
+
controls.addEventListener("change", () => {
|
|
413
|
+
this._Emit("viewChanged")
|
|
414
|
+
this.Render()
|
|
415
|
+
})
|
|
416
|
+
controls.update()
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
_Emit(eventName, data = null) {
|
|
420
|
+
this.canvas.dispatchEvent(new CustomEvent(EVENT_NAME_PREFIX + eventName, { detail: data }))
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
_Message(message, level = MessageLevel.INFO) {
|
|
424
|
+
this._Emit("message", {message, level})
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
_OnPointerEvent(e) {
|
|
428
|
+
const canvasRect = e.target.getBoundingClientRect()
|
|
429
|
+
const canvasCoord = {x: e.clientX - canvasRect.left, y: e.clientY - canvasRect.top}
|
|
430
|
+
this._Emit(e.type, {
|
|
431
|
+
domEvent: e,
|
|
432
|
+
canvasCoord,
|
|
433
|
+
position: this._CanvasToSceneCoord(canvasCoord.x, canvasCoord.y)
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** @return {{x,y}} Scene coordinate corresponding to the specified canvas pixel coordinates. */
|
|
438
|
+
_CanvasToSceneCoord(x, y) {
|
|
439
|
+
const v = new three.Vector3(x * 2 / this.canvasWidth - 1,
|
|
440
|
+
-y * 2 / this.canvasHeight + 1,
|
|
441
|
+
1).unproject(this.camera)
|
|
442
|
+
return {x: v.x, y: v.y}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
_OnResize(entry) {
|
|
446
|
+
this.SetSize(Math.floor(entry.contentRect.width), Math.floor(entry.contentRect.height))
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
_LoadBatch(scene, batch) {
|
|
450
|
+
if (batch.key.blockName !== null &&
|
|
451
|
+
batch.key.geometryType !== BatchingKey.GeometryType.BLOCK_INSTANCE &&
|
|
452
|
+
batch.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE) {
|
|
453
|
+
/* Block definition. */
|
|
454
|
+
return
|
|
455
|
+
}
|
|
456
|
+
const objects = new Batch(this, scene, batch).CreateObjects()
|
|
457
|
+
|
|
458
|
+
const layer = this.layers.get(batch.key.layerName)
|
|
459
|
+
|
|
460
|
+
for (const obj of objects) {
|
|
461
|
+
this.scene.add(obj)
|
|
462
|
+
if (layer) {
|
|
463
|
+
layer.PushObject(obj)
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
_GetSimpleColorMaterial(color, instanceType = InstanceType.NONE) {
|
|
469
|
+
const key = new MaterialKey(instanceType, null, color, 0)
|
|
470
|
+
let entry = this.materials.find({key})
|
|
471
|
+
if (entry !== null) {
|
|
472
|
+
return entry.material
|
|
473
|
+
}
|
|
474
|
+
entry = {
|
|
475
|
+
key,
|
|
476
|
+
material: this._CreateSimpleColorMaterialInstance(color, instanceType)
|
|
477
|
+
}
|
|
478
|
+
this.materials.insert(entry)
|
|
479
|
+
return entry.material
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
_CreateSimpleColorMaterial(instanceType = InstanceType.NONE) {
|
|
483
|
+
const shaders = this._GenerateShaders(instanceType, false)
|
|
484
|
+
return new three.RawShaderMaterial({
|
|
485
|
+
uniforms: {
|
|
486
|
+
color: {
|
|
487
|
+
value: new three.Color(0xff00ff)
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
vertexShader: shaders.vertex,
|
|
491
|
+
fragmentShader: shaders.fragment,
|
|
492
|
+
depthTest: false,
|
|
493
|
+
depthWrite: false,
|
|
494
|
+
glslVersion: three.GLSL3,
|
|
495
|
+
side: three.DoubleSide
|
|
496
|
+
})
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** @param color {number} Color RGB numeric value.
|
|
500
|
+
* @param instanceType {number}
|
|
501
|
+
*/
|
|
502
|
+
_CreateSimpleColorMaterialInstance(color, instanceType = InstanceType.NONE) {
|
|
503
|
+
const src = this.simpleColorMaterial[instanceType]
|
|
504
|
+
/* Should reuse compiled shaders. */
|
|
505
|
+
const m = src.clone()
|
|
506
|
+
m.uniforms.color = { value: new three.Color(color) }
|
|
507
|
+
return m
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
_GetSimplePointMaterial(color, instanceType = InstanceType.NONE) {
|
|
511
|
+
const key = new MaterialKey(instanceType, BatchingKey.GeometryType.POINTS, color, 0)
|
|
512
|
+
let entry = this.materials.find({key})
|
|
513
|
+
if (entry !== null) {
|
|
514
|
+
return entry.material
|
|
515
|
+
}
|
|
516
|
+
entry = {
|
|
517
|
+
key,
|
|
518
|
+
material: this._CreateSimplePointMaterialInstance(color, this.options.pointSize,
|
|
519
|
+
instanceType)
|
|
520
|
+
}
|
|
521
|
+
this.materials.insert(entry)
|
|
522
|
+
return entry.material
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
_CreateSimplePointMaterial(instanceType = InstanceType.NONE) {
|
|
526
|
+
const shaders = this._GenerateShaders(instanceType, true)
|
|
527
|
+
return new three.RawShaderMaterial({
|
|
528
|
+
uniforms: {
|
|
529
|
+
color: {
|
|
530
|
+
value: new three.Color(0xff00ff)
|
|
531
|
+
},
|
|
532
|
+
pointSize: {
|
|
533
|
+
value: 2
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
vertexShader: shaders.vertex,
|
|
537
|
+
fragmentShader: shaders.fragment,
|
|
538
|
+
depthTest: false,
|
|
539
|
+
depthWrite: false,
|
|
540
|
+
glslVersion: three.GLSL3
|
|
541
|
+
})
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/** @param color {number} Color RGB numeric value.
|
|
545
|
+
* @param size {number} Rasterized point size in pixels.
|
|
546
|
+
* @param instanceType {number}
|
|
547
|
+
*/
|
|
548
|
+
_CreateSimplePointMaterialInstance(color, size = 2, instanceType = InstanceType.NONE) {
|
|
549
|
+
const src = this.simplePointMaterial[instanceType]
|
|
550
|
+
/* Should reuse compiled shaders. */
|
|
551
|
+
const m = src.clone()
|
|
552
|
+
m.uniforms.color = { value: new three.Color(color) }
|
|
553
|
+
m.uniforms.size = { value: size }
|
|
554
|
+
return m
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
_GenerateShaders(instanceType, pointSize) {
|
|
558
|
+
const fullInstanceAttr = instanceType === InstanceType.FULL ?
|
|
559
|
+
`
|
|
560
|
+
/* First row. */
|
|
561
|
+
in vec3 instanceTransform0;
|
|
562
|
+
/* Second row. */
|
|
563
|
+
in vec3 instanceTransform1;
|
|
564
|
+
` : ""
|
|
565
|
+
const fullInstanceTransform = instanceType === InstanceType.FULL ?
|
|
566
|
+
`
|
|
567
|
+
pos.xy = mat2(instanceTransform0[0], instanceTransform1[0],
|
|
568
|
+
instanceTransform0[1], instanceTransform1[1]) * pos.xy +
|
|
569
|
+
vec2(instanceTransform0[2], instanceTransform1[2]);
|
|
570
|
+
` : ""
|
|
571
|
+
|
|
572
|
+
const pointInstanceAttr = instanceType === InstanceType.POINT ?
|
|
573
|
+
`
|
|
574
|
+
in vec2 instanceTransform;
|
|
575
|
+
` : ""
|
|
576
|
+
const pointInstanceTransform = instanceType === InstanceType.POINT ?
|
|
577
|
+
`
|
|
578
|
+
pos.xy += instanceTransform;
|
|
579
|
+
` : ""
|
|
580
|
+
|
|
581
|
+
const pointSizeUniform = pointSize ? "uniform float pointSize;" : ""
|
|
582
|
+
const pointSizeAssigment = pointSize ? "gl_PointSize = pointSize;" : ""
|
|
583
|
+
|
|
584
|
+
return {
|
|
585
|
+
vertex: `
|
|
586
|
+
|
|
587
|
+
precision highp float;
|
|
588
|
+
precision highp int;
|
|
589
|
+
in vec2 position;
|
|
590
|
+
${fullInstanceAttr}
|
|
591
|
+
${pointInstanceAttr}
|
|
592
|
+
uniform mat4 modelViewMatrix;
|
|
593
|
+
uniform mat4 projectionMatrix;
|
|
594
|
+
${pointSizeUniform}
|
|
595
|
+
|
|
596
|
+
void main() {
|
|
597
|
+
vec4 pos = vec4(position, 0.0, 1.0);
|
|
598
|
+
${fullInstanceTransform}
|
|
599
|
+
${pointInstanceTransform}
|
|
600
|
+
gl_Position = projectionMatrix * modelViewMatrix * pos;
|
|
601
|
+
${pointSizeAssigment}
|
|
602
|
+
}
|
|
603
|
+
`,
|
|
604
|
+
fragment: `
|
|
605
|
+
|
|
606
|
+
precision highp float;
|
|
607
|
+
precision highp int;
|
|
608
|
+
uniform vec3 color;
|
|
609
|
+
out vec4 fragColor;
|
|
610
|
+
|
|
611
|
+
void main() {
|
|
612
|
+
fragColor = vec4(color, 1.0);
|
|
613
|
+
}
|
|
614
|
+
`
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/** Ensure the color is contrast enough with current background color.
|
|
619
|
+
* @param color {number} RGB value.
|
|
620
|
+
* @return {number} RGB value to use for rendering.
|
|
621
|
+
*/
|
|
622
|
+
_TransformColor(color) {
|
|
623
|
+
if (!this.options.colorCorrection && !this.options.blackWhiteInversion) {
|
|
624
|
+
return color
|
|
625
|
+
}
|
|
626
|
+
/* Just black and white inversion. */
|
|
627
|
+
const bkgLum = Luminance(this.clearColor)
|
|
628
|
+
if (color === 0xffffff && bkgLum >= 0.8) {
|
|
629
|
+
return 0
|
|
630
|
+
}
|
|
631
|
+
if (color === 0 && bkgLum <= 0.2) {
|
|
632
|
+
return 0xffffff
|
|
633
|
+
}
|
|
634
|
+
if (!this.options.colorCorrection) {
|
|
635
|
+
return color
|
|
636
|
+
}
|
|
637
|
+
const fgLum = Luminance(color)
|
|
638
|
+
const MIN_TARGET_RATIO = 1.5
|
|
639
|
+
const contrast = ContrastRatio(color, this.clearColor)
|
|
640
|
+
const diff = contrast >= 1 ? contrast : 1 / contrast
|
|
641
|
+
if (diff < MIN_TARGET_RATIO) {
|
|
642
|
+
let targetLum
|
|
643
|
+
if (bkgLum > 0.5) {
|
|
644
|
+
targetLum = bkgLum / 2
|
|
645
|
+
} else {
|
|
646
|
+
targetLum = bkgLum * 2
|
|
647
|
+
}
|
|
648
|
+
if (targetLum > fgLum) {
|
|
649
|
+
color = Lighten(color, targetLum / fgLum)
|
|
650
|
+
} else {
|
|
651
|
+
color = Darken(color, fgLum / targetLum)
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return color
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
DxfViewer.MessageLevel = MessageLevel
|
|
659
|
+
|
|
660
|
+
DxfViewer.DefaultOptions = {
|
|
661
|
+
canvasWidth: 400,
|
|
662
|
+
canvasHeight: 300,
|
|
663
|
+
/** Automatically resize canvas when the container is resized. This options
|
|
664
|
+
* utilizes ResizeObserver API which is still not fully standardized. The specified canvas size
|
|
665
|
+
* is ignored if the option is enabled.
|
|
666
|
+
*/
|
|
667
|
+
autoResize: false,
|
|
668
|
+
/** Frame buffer clear color. */
|
|
669
|
+
clearColor: new three.Color("#000"),
|
|
670
|
+
/** Frame buffer clear color alpha value. */
|
|
671
|
+
clearAlpha: 1.0,
|
|
672
|
+
/** Use alpha channel in a framebuffer. */
|
|
673
|
+
canvasAlpha: false,
|
|
674
|
+
/** Assume premultiplied alpha in a framebuffer. */
|
|
675
|
+
canvasPremultipliedAlpha: true,
|
|
676
|
+
/** Use antialiasing. May degrade performance on poor hardware. */
|
|
677
|
+
antialias: true,
|
|
678
|
+
/** Correct entities colors to ensure that they are always visible with the current background
|
|
679
|
+
* color.
|
|
680
|
+
*/
|
|
681
|
+
colorCorrection: false,
|
|
682
|
+
/** Simpler version of colorCorrection - just invert pure white or black entities if they are
|
|
683
|
+
* invisible on current background color.
|
|
684
|
+
*/
|
|
685
|
+
blackWhiteInversion: true,
|
|
686
|
+
/** Size in pixels for rasterized points (dot mark). */
|
|
687
|
+
pointSize: 2,
|
|
688
|
+
/** Scene generation options. */
|
|
689
|
+
sceneOptions: DxfScene.DefaultOptions,
|
|
690
|
+
/** Retain the simple object representing the parsed DXF - will consume a lot of additional memory */
|
|
691
|
+
retainParsedDxf: false
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
DxfViewer.SetupWorker = function () {
|
|
695
|
+
new DxfWorker(self, true)
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const InstanceType = Object.freeze({
|
|
699
|
+
/** Not instanced. */
|
|
700
|
+
NONE: 0,
|
|
701
|
+
/** Full affine transform per instance. */
|
|
702
|
+
FULL: 1,
|
|
703
|
+
/** Point instances, 2D-translation vector per instance. */
|
|
704
|
+
POINT: 2,
|
|
705
|
+
|
|
706
|
+
/** Number of types. */
|
|
707
|
+
MAX: 3
|
|
708
|
+
})
|
|
709
|
+
|
|
710
|
+
class Batch {
|
|
711
|
+
/**
|
|
712
|
+
* @param viewer {DxfViewer}
|
|
713
|
+
* @param scene Serialized scene.
|
|
714
|
+
* @param batch Serialized scene batch.
|
|
715
|
+
*/
|
|
716
|
+
constructor(viewer, scene, batch) {
|
|
717
|
+
this.viewer = viewer
|
|
718
|
+
this.key = batch.key
|
|
719
|
+
|
|
720
|
+
if (batch.hasOwnProperty("verticesOffset")) {
|
|
721
|
+
const verticesArray =
|
|
722
|
+
new Float32Array(scene.vertices,
|
|
723
|
+
batch.verticesOffset * Float32Array.BYTES_PER_ELEMENT,
|
|
724
|
+
batch.verticesSize)
|
|
725
|
+
if (this.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE ||
|
|
726
|
+
scene.pointShapeHasDot) {
|
|
727
|
+
this.vertices = new three.BufferAttribute(verticesArray, 2)
|
|
728
|
+
}
|
|
729
|
+
if (this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
|
|
730
|
+
this.transforms = new three.InstancedBufferAttribute(verticesArray, 2)
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (batch.hasOwnProperty("chunks")) {
|
|
735
|
+
this.chunks = []
|
|
736
|
+
for (const rawChunk of batch.chunks) {
|
|
737
|
+
|
|
738
|
+
const verticesArray =
|
|
739
|
+
new Float32Array(scene.vertices,
|
|
740
|
+
rawChunk.verticesOffset * Float32Array.BYTES_PER_ELEMENT,
|
|
741
|
+
rawChunk.verticesSize)
|
|
742
|
+
const indicesArray =
|
|
743
|
+
new Uint16Array(scene.indices,
|
|
744
|
+
rawChunk.indicesOffset * Uint16Array.BYTES_PER_ELEMENT,
|
|
745
|
+
rawChunk.indicesSize)
|
|
746
|
+
this.chunks.push({
|
|
747
|
+
vertices: new three.BufferAttribute(verticesArray, 2),
|
|
748
|
+
indices: new three.BufferAttribute(indicesArray, 1)
|
|
749
|
+
})
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
if (batch.hasOwnProperty("transformsOffset")) {
|
|
754
|
+
const transformsArray =
|
|
755
|
+
new Float32Array(scene.transforms,
|
|
756
|
+
batch.transformsOffset * Float32Array.BYTES_PER_ELEMENT,
|
|
757
|
+
batch.transformsSize)
|
|
758
|
+
/* Each transform is 3x2 matrix which is split into two 3D vectors which will occupy two
|
|
759
|
+
* attribute slots.
|
|
760
|
+
*/
|
|
761
|
+
const buf = new three.InstancedInterleavedBuffer(transformsArray, 6)
|
|
762
|
+
this.transforms0 = new three.InterleavedBufferAttribute(buf, 3, 0)
|
|
763
|
+
this.transforms1 = new three.InterleavedBufferAttribute(buf, 3, 3)
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if (this.key.geometryType === BatchingKey.GeometryType.BLOCK_INSTANCE ||
|
|
767
|
+
this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
|
|
768
|
+
|
|
769
|
+
const layer = this.viewer.layers.get(this.key.layerName)
|
|
770
|
+
if (layer) {
|
|
771
|
+
this.layerColor = layer.color
|
|
772
|
+
} else {
|
|
773
|
+
this.layerColor = 0
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
GetInstanceType() {
|
|
779
|
+
switch (this.key.geometryType) {
|
|
780
|
+
case BatchingKey.GeometryType.BLOCK_INSTANCE:
|
|
781
|
+
return InstanceType.FULL
|
|
782
|
+
case BatchingKey.GeometryType.POINT_INSTANCE:
|
|
783
|
+
return InstanceType.POINT
|
|
784
|
+
default:
|
|
785
|
+
return InstanceType.NONE
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/** Create scene objects corresponding to batch data.
|
|
790
|
+
* @param instanceBatch {?Batch} Batch with instance transform. Null for non-instanced object.
|
|
791
|
+
*/
|
|
792
|
+
*CreateObjects(instanceBatch = null) {
|
|
793
|
+
if (this.key.geometryType === BatchingKey.GeometryType.BLOCK_INSTANCE ||
|
|
794
|
+
this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
|
|
795
|
+
|
|
796
|
+
if (instanceBatch !== null) {
|
|
797
|
+
throw new Error("Unexpected instance batch specified for instance batch")
|
|
798
|
+
}
|
|
799
|
+
yield* this._CreateBlockInstanceObjects()
|
|
800
|
+
return
|
|
801
|
+
}
|
|
802
|
+
yield* this._CreateObjects(instanceBatch)
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
*_CreateObjects(instanceBatch) {
|
|
806
|
+
const color = instanceBatch ?
|
|
807
|
+
instanceBatch._GetInstanceColor(this.key.color) : this.key.color
|
|
808
|
+
|
|
809
|
+
//XXX line type
|
|
810
|
+
const materialFactory =
|
|
811
|
+
this.key.geometryType === BatchingKey.GeometryType.POINTS ||
|
|
812
|
+
this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE ?
|
|
813
|
+
this.viewer._GetSimplePointMaterial : this.viewer._GetSimpleColorMaterial
|
|
814
|
+
|
|
815
|
+
const material = materialFactory.call(this.viewer, this.viewer._TransformColor(color),
|
|
816
|
+
instanceBatch?.GetInstanceType() ?? InstanceType.NONE)
|
|
817
|
+
|
|
818
|
+
let objConstructor
|
|
819
|
+
switch (this.key.geometryType) {
|
|
820
|
+
case BatchingKey.GeometryType.POINTS:
|
|
821
|
+
/* This method also called for creating dots for shaped point instances. */
|
|
822
|
+
case BatchingKey.GeometryType.POINT_INSTANCE:
|
|
823
|
+
objConstructor = three.Points
|
|
824
|
+
break
|
|
825
|
+
case BatchingKey.GeometryType.LINES:
|
|
826
|
+
case BatchingKey.GeometryType.INDEXED_LINES:
|
|
827
|
+
objConstructor = three.LineSegments
|
|
828
|
+
break
|
|
829
|
+
case BatchingKey.GeometryType.TRIANGLES:
|
|
830
|
+
case BatchingKey.GeometryType.INDEXED_TRIANGLES:
|
|
831
|
+
objConstructor = three.Mesh
|
|
832
|
+
break
|
|
833
|
+
default:
|
|
834
|
+
throw new Error("Unexpected geometry type:" + this.key.geometryType)
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
function CreateObject(vertices, indices) {
|
|
838
|
+
const geometry = instanceBatch ?
|
|
839
|
+
new three.InstancedBufferGeometry() : new three.BufferGeometry()
|
|
840
|
+
geometry.setAttribute("position", vertices)
|
|
841
|
+
instanceBatch?._SetInstanceTransformAttribute(geometry)
|
|
842
|
+
if (indices) {
|
|
843
|
+
geometry.setIndex(indices)
|
|
844
|
+
}
|
|
845
|
+
const obj = new objConstructor(geometry, material)
|
|
846
|
+
obj.frustumCulled = false
|
|
847
|
+
obj.matrixAutoUpdate = false
|
|
848
|
+
return obj
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
if (this.chunks) {
|
|
852
|
+
for (const chunk of this.chunks) {
|
|
853
|
+
yield CreateObject(chunk.vertices, chunk.indices)
|
|
854
|
+
}
|
|
855
|
+
} else {
|
|
856
|
+
yield CreateObject(this.vertices)
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* @param geometry {InstancedBufferGeometry}
|
|
862
|
+
*/
|
|
863
|
+
_SetInstanceTransformAttribute(geometry) {
|
|
864
|
+
if (!geometry.isInstancedBufferGeometry) {
|
|
865
|
+
throw new Error("InstancedBufferGeometry expected")
|
|
866
|
+
}
|
|
867
|
+
if (this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
|
|
868
|
+
geometry.setAttribute("instanceTransform", this.transforms)
|
|
869
|
+
} else {
|
|
870
|
+
geometry.setAttribute("instanceTransform0", this.transforms0)
|
|
871
|
+
geometry.setAttribute("instanceTransform1", this.transforms1)
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
*_CreateBlockInstanceObjects() {
|
|
876
|
+
const block = this.viewer.blocks.get(this.key.blockName)
|
|
877
|
+
if (!block) {
|
|
878
|
+
return
|
|
879
|
+
}
|
|
880
|
+
for (const batch of block.batches) {
|
|
881
|
+
yield* batch.CreateObjects(this)
|
|
882
|
+
}
|
|
883
|
+
if (this.hasOwnProperty("vertices")) {
|
|
884
|
+
/* Dots for point shapes. */
|
|
885
|
+
yield* this._CreateObjects()
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* @param defColor {number} Color value for block definition batch.
|
|
891
|
+
* @return {number} RGB color value for a block instance.
|
|
892
|
+
*/
|
|
893
|
+
_GetInstanceColor(defColor) {
|
|
894
|
+
if (defColor === ColorCode.BY_BLOCK) {
|
|
895
|
+
return this.key.color
|
|
896
|
+
} else if (defColor === ColorCode.BY_LAYER) {
|
|
897
|
+
return this.layerColor
|
|
898
|
+
} else {
|
|
899
|
+
return defColor
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
class Layer {
|
|
905
|
+
constructor(name, displayName, color) {
|
|
906
|
+
this.name = name
|
|
907
|
+
this.displayName = displayName
|
|
908
|
+
this.color = color
|
|
909
|
+
this.objects = []
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
PushObject(obj) {
|
|
913
|
+
this.objects.push(obj)
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
Dispose() {
|
|
917
|
+
for (const obj of this.objects) {
|
|
918
|
+
obj.geometry.dispose()
|
|
919
|
+
}
|
|
920
|
+
this.objects = null
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
class Block {
|
|
925
|
+
constructor() {
|
|
926
|
+
this.batches = []
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/** @param batch {Batch} */
|
|
930
|
+
PushBatch(batch) {
|
|
931
|
+
this.batches.push(batch)
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/** Custom viewer event names are prefixed with this string. */
|
|
936
|
+
const EVENT_NAME_PREFIX = "__dxf_"
|
|
937
|
+
|
|
938
|
+
/** Transform sRGB color component to linear color space. */
|
|
939
|
+
function LinearColor(c) {
|
|
940
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/** Transform linear color component to sRGB color space. */
|
|
944
|
+
function SRgbColor(c) {
|
|
945
|
+
return c < 0.003 ? c * 12.92 : Math.pow(c, 1 / 2.4) * 1.055 - 0.055
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/** Get relative luminance value for a color.
|
|
949
|
+
* https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
|
950
|
+
* @param color {number} RGB color value.
|
|
951
|
+
* @return {number} Luminance value in range [0; 1].
|
|
952
|
+
*/
|
|
953
|
+
function Luminance(color) {
|
|
954
|
+
const r = LinearColor(((color & 0xff0000) >>> 16) / 255)
|
|
955
|
+
const g = LinearColor(((color & 0xff00) >>> 8) / 255)
|
|
956
|
+
const b = LinearColor((color & 0xff) / 255)
|
|
957
|
+
|
|
958
|
+
return r * 0.2126 + g * 0.7152 + b * 0.0722
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Get contrast ratio for a color pair.
|
|
963
|
+
* https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
|
|
964
|
+
* @param c1
|
|
965
|
+
* @param c2
|
|
966
|
+
* @return {number} Contrast ratio between the colors. Greater than one if the first color color is
|
|
967
|
+
* brighter than the second one.
|
|
968
|
+
*/
|
|
969
|
+
function ContrastRatio(c1, c2) {
|
|
970
|
+
return (Luminance(c1) + 0.05) / (Luminance(c2) + 0.05)
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
function HlsToRgb({h, l, s}) {
|
|
974
|
+
let r, g, b
|
|
975
|
+
if (s === 0) {
|
|
976
|
+
/* Achromatic */
|
|
977
|
+
r = g = b = l
|
|
978
|
+
} else {
|
|
979
|
+
function hue2rgb(p, q, t) {
|
|
980
|
+
if (t < 0) {
|
|
981
|
+
t += 1
|
|
982
|
+
}
|
|
983
|
+
if (t > 1) {
|
|
984
|
+
t -= 1
|
|
985
|
+
}
|
|
986
|
+
if (t < 1/6) {
|
|
987
|
+
return p + (q - p) * 6 * t
|
|
988
|
+
}
|
|
989
|
+
if (t < 1/2) {
|
|
990
|
+
return q
|
|
991
|
+
}
|
|
992
|
+
if (t < 2/3) {
|
|
993
|
+
return p + (q - p) * (2/3 - t) * 6
|
|
994
|
+
}
|
|
995
|
+
return p
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s
|
|
999
|
+
const p = 2 * l - q
|
|
1000
|
+
r = hue2rgb(p, q, h + 1/3)
|
|
1001
|
+
g = hue2rgb(p, q, h)
|
|
1002
|
+
b = hue2rgb(p, q, h - 1/3)
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
return (Math.min(Math.floor(SRgbColor(r) * 256), 255) << 16) |
|
|
1006
|
+
(Math.min(Math.floor(SRgbColor(g) * 256), 255) << 8) |
|
|
1007
|
+
Math.min(Math.floor(SRgbColor(b) * 256), 255)
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function RgbToHls(color) {
|
|
1011
|
+
const r = LinearColor(((color & 0xff0000) >>> 16) / 255)
|
|
1012
|
+
const g = LinearColor(((color & 0xff00) >>> 8) / 255)
|
|
1013
|
+
const b = LinearColor((color & 0xff) / 255)
|
|
1014
|
+
|
|
1015
|
+
const max = Math.max(r, g, b)
|
|
1016
|
+
const min = Math.min(r, g, b)
|
|
1017
|
+
let h, s
|
|
1018
|
+
const l = (max + min) / 2
|
|
1019
|
+
|
|
1020
|
+
if (max === min) {
|
|
1021
|
+
/* Achromatic */
|
|
1022
|
+
h = s = 0
|
|
1023
|
+
} else {
|
|
1024
|
+
const d = max - min
|
|
1025
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
|
|
1026
|
+
switch (max) {
|
|
1027
|
+
case r:
|
|
1028
|
+
h = (g - b) / d + (g < b ? 6 : 0)
|
|
1029
|
+
break;
|
|
1030
|
+
case g:
|
|
1031
|
+
h = (b - r) / d + 2
|
|
1032
|
+
break
|
|
1033
|
+
case b:
|
|
1034
|
+
h = (r - g) / d + 4
|
|
1035
|
+
break
|
|
1036
|
+
}
|
|
1037
|
+
h /= 6
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
return {h, l, s}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
function Lighten(color, factor) {
|
|
1044
|
+
const hls = RgbToHls(color)
|
|
1045
|
+
hls.l *= factor
|
|
1046
|
+
if (hls.l > 1) {
|
|
1047
|
+
hls.l = 1
|
|
1048
|
+
}
|
|
1049
|
+
return HlsToRgb(hls)
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
function Darken(color, factor) {
|
|
1053
|
+
const hls = RgbToHls(color)
|
|
1054
|
+
hls.l /= factor
|
|
1055
|
+
return HlsToRgb(hls)
|
|
1056
|
+
}
|