@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,229 @@
|
|
|
1
|
+
import {DxfFetcher} from "./DxfFetcher.js"
|
|
2
|
+
import {DxfScene} from "./DxfScene.js"
|
|
3
|
+
import opentype from "/resource/dxfViewer/js/opentype/opentype.module.js"
|
|
4
|
+
|
|
5
|
+
const MSG_SIGNATURE = "DxfWorkerMsg"
|
|
6
|
+
|
|
7
|
+
/** Wraps web-worker instance and provides unified interface to its services, including the when
|
|
8
|
+
* web-worker is not used and all heavy operations are performed in main thread.
|
|
9
|
+
*/
|
|
10
|
+
export class DxfWorker {
|
|
11
|
+
/** @param worker Web worker instance with DxfViewer.SetupWorker() function called. Can be null
|
|
12
|
+
* for synchronous operations.
|
|
13
|
+
* @param isWorker True for worker-side wrapper.
|
|
14
|
+
*/
|
|
15
|
+
constructor(worker, isWorker = false) {
|
|
16
|
+
this.worker = worker
|
|
17
|
+
if (isWorker) {
|
|
18
|
+
worker.onmessage = this._ProcessRequest.bind(this)
|
|
19
|
+
} else if (worker) {
|
|
20
|
+
worker.addEventListener("message", this._ProcessResponse.bind(this), false)
|
|
21
|
+
worker.addEventListener("error", this._OnError.bind(this), false)
|
|
22
|
+
this.reqSeq = 1
|
|
23
|
+
/* Indexed by sequence. */
|
|
24
|
+
this.requests = new Map()
|
|
25
|
+
this.progressCbk = null
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param url DXF file URL.
|
|
31
|
+
* @param fonts {?string[]} Fonts URLs.
|
|
32
|
+
* @param options Viewer options. See DxfViewer.DefaultOptions.
|
|
33
|
+
* @param progressCbk {Function?} (phase, processedSize, totalSize)
|
|
34
|
+
*/
|
|
35
|
+
async Load(url, fonts, options, progressCbk) {
|
|
36
|
+
if (this.worker) {
|
|
37
|
+
return this._SendRequest(DxfWorker.WorkerMsg.LOAD,
|
|
38
|
+
{ url, fonts, options: this._CloneOptions(options) },
|
|
39
|
+
progressCbk)
|
|
40
|
+
} else {
|
|
41
|
+
return this._Load(url, fonts, options, progressCbk)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async Destroy(noWait = false) {
|
|
46
|
+
if (this.worker) {
|
|
47
|
+
if (!noWait) {
|
|
48
|
+
await this._SendRequest(DxfWorker.WorkerMsg.DESTROY)
|
|
49
|
+
}
|
|
50
|
+
/* close() in the worker is not enough, instance is still visible in dev tools. */
|
|
51
|
+
this.worker.terminate()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async _ProcessRequest(event) {
|
|
56
|
+
const msg = event.data
|
|
57
|
+
if (msg.signature !== MSG_SIGNATURE) {
|
|
58
|
+
console.log("Message with bad signature", msg)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
const resp = {seq: msg.seq, type: msg.type, signature: MSG_SIGNATURE}
|
|
62
|
+
const transfers = []
|
|
63
|
+
try {
|
|
64
|
+
resp.data = await this._ProcessRequestMessage(msg.type, msg.data, transfers, msg.seq)
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error(error)
|
|
67
|
+
resp.error = String(error)
|
|
68
|
+
}
|
|
69
|
+
this.worker.postMessage(resp, transfers)
|
|
70
|
+
if (msg.type === DxfWorker.WorkerMsg.DESTROY) {
|
|
71
|
+
this.worker.onmessage = null
|
|
72
|
+
this.worker.close()
|
|
73
|
+
this.worker = null
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async _ProcessRequestMessage(type, data, transfers, seq) {
|
|
78
|
+
switch (type) {
|
|
79
|
+
case DxfWorker.WorkerMsg.LOAD: {
|
|
80
|
+
const {scene, dxf} = await this._Load(
|
|
81
|
+
data.url,
|
|
82
|
+
data.fonts,
|
|
83
|
+
data.options,
|
|
84
|
+
(phase, size, totalSize) => this._SendProgress(seq, phase, size, totalSize))
|
|
85
|
+
transfers.push(scene.vertices)
|
|
86
|
+
transfers.push(scene.indices)
|
|
87
|
+
transfers.push(scene.transforms)
|
|
88
|
+
return {scene, dxf}
|
|
89
|
+
}
|
|
90
|
+
case DxfWorker.WorkerMsg.DESTROY:
|
|
91
|
+
return null
|
|
92
|
+
default:
|
|
93
|
+
throw "Unknown message type: " + type
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async _ProcessResponse(event) {
|
|
98
|
+
const msg = event.data
|
|
99
|
+
if (msg.signature !== MSG_SIGNATURE) {
|
|
100
|
+
console.log("Message with bad signature", msg)
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
const seq = msg.seq
|
|
104
|
+
const req = this.requests.get(seq)
|
|
105
|
+
if (!req) {
|
|
106
|
+
console.error("Unmatched message sequence: ", seq)
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
const data = msg.data
|
|
110
|
+
if (msg.type === DxfWorker.WorkerMsg.PROGRESS && req.progressCbk) {
|
|
111
|
+
req.progressCbk(data.phase, data.size, data.totalSize)
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
this.requests.delete(seq)
|
|
115
|
+
if (msg.hasOwnProperty("error")) {
|
|
116
|
+
req.SetError(msg.error)
|
|
117
|
+
} else {
|
|
118
|
+
req.SetResponse(data)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async _OnError(error) {
|
|
123
|
+
console.error("DxfWorker worker error", error)
|
|
124
|
+
const reqs = Array.from(this.requests.values)
|
|
125
|
+
this.requests.clear()
|
|
126
|
+
reqs.forEach(req => req.SetError(error))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async _SendRequest(type, data = null, progressCbk = null) {
|
|
130
|
+
const seq = this.reqSeq++
|
|
131
|
+
const req = new DxfWorker.Request(seq, progressCbk)
|
|
132
|
+
this.requests.set(seq, req)
|
|
133
|
+
this.worker.postMessage({ seq, type, data, signature: MSG_SIGNATURE})
|
|
134
|
+
return await req.GetResponse()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
_SendProgress(seq, phase, size, totalSize) {
|
|
138
|
+
this.worker.postMessage({
|
|
139
|
+
seq,
|
|
140
|
+
type: DxfWorker.WorkerMsg.PROGRESS,
|
|
141
|
+
data: {phase, size, totalSize},
|
|
142
|
+
signature: MSG_SIGNATURE
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** @return {Object} DxfScene serialized scene. */
|
|
147
|
+
async _Load(url, fonts, options, progressCbk) {
|
|
148
|
+
let fontFetchers
|
|
149
|
+
if (fonts) {
|
|
150
|
+
fontFetchers = this._CreateFontFetchers(fonts, progressCbk)
|
|
151
|
+
} else {
|
|
152
|
+
fontFetchers = []
|
|
153
|
+
}
|
|
154
|
+
const dxf = await new DxfFetcher(url).Fetch(progressCbk)
|
|
155
|
+
if (progressCbk) {
|
|
156
|
+
progressCbk("prepare", 0, null)
|
|
157
|
+
}
|
|
158
|
+
const dxfScene = new DxfScene(options)
|
|
159
|
+
await dxfScene.Build(dxf, fontFetchers)
|
|
160
|
+
return {scene: dxfScene.scene, dxf: options.retainParsedDxf === true ? dxf : undefined }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
_CreateFontFetchers(urls, progressCbk) {
|
|
164
|
+
|
|
165
|
+
function CreateFetcher(url) {
|
|
166
|
+
return async function() {
|
|
167
|
+
if (progressCbk) {
|
|
168
|
+
progressCbk("font", 0, null)
|
|
169
|
+
}
|
|
170
|
+
const data = await fetch(url).then(response => response.arrayBuffer())
|
|
171
|
+
if (progressCbk) {
|
|
172
|
+
progressCbk("prepare", 0, null)
|
|
173
|
+
}
|
|
174
|
+
return opentype.parse(data)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const fetchers = []
|
|
179
|
+
for (const url of urls) {
|
|
180
|
+
fetchers.push(CreateFetcher(url))
|
|
181
|
+
}
|
|
182
|
+
return fetchers
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
_CloneOptions(options) {
|
|
186
|
+
/* Default options values are taken from prototype so need to implement deep clone here. */
|
|
187
|
+
if (Array.isArray(options)) {
|
|
188
|
+
return options.map(o => this._CloneOptions(o))
|
|
189
|
+
} else if (typeof options === "object" && options !== null) {
|
|
190
|
+
const result = {}
|
|
191
|
+
for (const propName in options) {
|
|
192
|
+
// noinspection JSUnfilteredForInLoop
|
|
193
|
+
result[propName] = this._CloneOptions(options[propName])
|
|
194
|
+
}
|
|
195
|
+
return result
|
|
196
|
+
} else {
|
|
197
|
+
return options
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
DxfWorker.WorkerMsg = {
|
|
203
|
+
LOAD: "LOAD",
|
|
204
|
+
PROGRESS: "PROGRESS",
|
|
205
|
+
DESTROY: "DESTROY"
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
DxfWorker.Request = class {
|
|
209
|
+
constructor(seq, progressCbk) {
|
|
210
|
+
this.seq = seq
|
|
211
|
+
this.progressCbk = progressCbk
|
|
212
|
+
this.promise = new Promise((resolve, reject) => {
|
|
213
|
+
this._Resolve = resolve
|
|
214
|
+
this._Reject = reject
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async GetResponse() {
|
|
219
|
+
return await this.promise
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
SetResponse(response) {
|
|
223
|
+
this._Resolve(response)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
SetError(error) {
|
|
227
|
+
this._Reject(error)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/** Typed-array-based buffer which can be dynamically extended. */
|
|
2
|
+
export class DynamicBuffer {
|
|
3
|
+
/**
|
|
4
|
+
* @param type Array type, see NativeType.
|
|
5
|
+
* @param initialCapacity Initial capacity, number of elements.
|
|
6
|
+
*/
|
|
7
|
+
constructor(type, initialCapacity = 16) {
|
|
8
|
+
this.type = type
|
|
9
|
+
this.capacity = initialCapacity
|
|
10
|
+
this.size = 0
|
|
11
|
+
this.buffer = new (NativeArray(type))(initialCapacity)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
GetSize() {
|
|
15
|
+
return this.size
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Append new value to the buffer end.
|
|
20
|
+
* @return Appended value position in the buffer.
|
|
21
|
+
*/
|
|
22
|
+
Push(value) {
|
|
23
|
+
this._CheckGrow()
|
|
24
|
+
const pos = this.size
|
|
25
|
+
this.buffer[pos] = value
|
|
26
|
+
this.size++
|
|
27
|
+
return pos
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Get(index) {
|
|
31
|
+
if (index >= this.size) {
|
|
32
|
+
throw new Error(`Index out of range: ${index}/${this.size}`)
|
|
33
|
+
}
|
|
34
|
+
return this.buffer[index]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Copy content to the specified buffer.
|
|
38
|
+
* @param dstBuffer Destination buffer, should be typed array of the same type.
|
|
39
|
+
* @param dstOffset {number} Offset in elements in the destination buffer.
|
|
40
|
+
* @param srcOffset {number} Offset in elements in this buffer.
|
|
41
|
+
* @param size {number} Number of elements to copy, -1 (default) to copy till this buffer end.
|
|
42
|
+
*/
|
|
43
|
+
CopyTo(dstBuffer, dstOffset, srcOffset = 0, size = -1) {
|
|
44
|
+
if (size === -1) {
|
|
45
|
+
size = this.size - srcOffset
|
|
46
|
+
}
|
|
47
|
+
const src = new (NativeArray(this.type))(this.buffer.buffer, srcOffset, size)
|
|
48
|
+
dstBuffer.set(src, dstOffset)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
_CheckGrow() {
|
|
52
|
+
if (this.size < this.capacity) {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
this.capacity *= 2
|
|
56
|
+
const newBuffer = new (NativeArray(this.type))(this.capacity)
|
|
57
|
+
newBuffer.set(this.buffer)
|
|
58
|
+
this.buffer = newBuffer
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const NativeType = {
|
|
63
|
+
INT8: 0,
|
|
64
|
+
UINT8: 1,
|
|
65
|
+
UINT8_CLAMPED: 2,
|
|
66
|
+
INT16: 3,
|
|
67
|
+
UINT16: 4,
|
|
68
|
+
INT32: 5,
|
|
69
|
+
UINT32: 6,
|
|
70
|
+
INT64: 7,
|
|
71
|
+
UINT64: 8,
|
|
72
|
+
FLOAT32: 9,
|
|
73
|
+
FLOAT64: 10
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Get TypedArray type corresponding to the specified NativeType. */
|
|
77
|
+
export function NativeArray(type) {
|
|
78
|
+
switch (type) {
|
|
79
|
+
case NativeType.INT8:
|
|
80
|
+
return Int8Array
|
|
81
|
+
case NativeType.UINT8:
|
|
82
|
+
return Uint8Array
|
|
83
|
+
case NativeType.UINT8_CLAMPED:
|
|
84
|
+
return Uint8ClampedArray
|
|
85
|
+
case NativeType.INT16:
|
|
86
|
+
return Int16Array
|
|
87
|
+
case NativeType.UINT16:
|
|
88
|
+
return Uint16Array
|
|
89
|
+
case NativeType.INT32:
|
|
90
|
+
return Int32Array
|
|
91
|
+
case NativeType.UINT32:
|
|
92
|
+
return Uint32Array
|
|
93
|
+
case NativeType.FLOAT32:
|
|
94
|
+
return Float32Array
|
|
95
|
+
case NativeType.FLOAT64:
|
|
96
|
+
return Float64Array
|
|
97
|
+
default:
|
|
98
|
+
throw new Error("Unrecognized native type: " + type)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { Vector2, Matrix3, Box2 } from "/resource/dxfViewer/js/three/three.module.js"
|
|
2
|
+
import { IntersectSegmentsParametric } from "./math/utils"
|
|
3
|
+
|
|
4
|
+
export const HatchStyle = Object.freeze({
|
|
5
|
+
ODD_PARITY: 0,
|
|
6
|
+
OUTERMOST: 1,
|
|
7
|
+
THROUGH_ENTIRE_AREA: 2
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
/** Force intersection at this distance from edge endpoint (parameter value). */
|
|
11
|
+
const ENDPOINT_MARGIN = 1e-4
|
|
12
|
+
|
|
13
|
+
/** @return {boolean} True if both edges crossed from the same side, false otherwise. */
|
|
14
|
+
function EdgeSameSide(e1, e2) {
|
|
15
|
+
return (e1.intersection[2] > 0 && e2.intersection[2]) > 0 ||
|
|
16
|
+
(e1.intersection[2] < 0 && e2.intersection[2] < 0)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Context for one line clipping calculations. */
|
|
20
|
+
class ClipCalculator {
|
|
21
|
+
|
|
22
|
+
constructor(boundaryLoops, style, line) {
|
|
23
|
+
this.style = style
|
|
24
|
+
this.line = line
|
|
25
|
+
this.lineDir = line[1].clone().sub(line[0]).normalize()
|
|
26
|
+
|
|
27
|
+
this.loops = []
|
|
28
|
+
for (let loopIdx = 0; loopIdx < boundaryLoops.length; loopIdx++) {
|
|
29
|
+
const loop = boundaryLoops[loopIdx]
|
|
30
|
+
const _loop = []
|
|
31
|
+
for (let vtxIdx = 0; vtxIdx < loop.length; vtxIdx++) {
|
|
32
|
+
_loop.push({
|
|
33
|
+
idx: vtxIdx,
|
|
34
|
+
start: loop[vtxIdx],
|
|
35
|
+
end: loop[vtxIdx == loop.length - 1 ? 0 : vtxIdx + 1],
|
|
36
|
+
loopIdx
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
this.loops.push(_loop)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @return {number[2][]} List of resulting line segments in parametric form. Parameter value 0
|
|
45
|
+
* corresponds to the provided line start point, 1 - to end point.
|
|
46
|
+
*/
|
|
47
|
+
Calculate() {
|
|
48
|
+
this._ProcessEdges()
|
|
49
|
+
this._CreateNodes()
|
|
50
|
+
/* Sort from line start towards end. */
|
|
51
|
+
this.nodes.sort((e1, e2) => e1.intersection[0] - e2.intersection[0])
|
|
52
|
+
if (this.style == HatchStyle.ODD_PARITY) {
|
|
53
|
+
return this._GenerateOddParitySegments()
|
|
54
|
+
}
|
|
55
|
+
//XXX assume through all for all the reset style
|
|
56
|
+
return this._GenerateThroughAllSegments()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
_ProcessEdges() {
|
|
60
|
+
for (const loop of this.loops) {
|
|
61
|
+
for (const edge of loop) {
|
|
62
|
+
const edgeVec = edge.end.clone().sub(edge.start)
|
|
63
|
+
const len = edgeVec.length()
|
|
64
|
+
edge.isZero = len <= Number.EPSILON
|
|
65
|
+
if (edge.isZero) {
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
edgeVec.divideScalar(len)
|
|
69
|
+
const a = edgeVec.cross(this.lineDir)
|
|
70
|
+
edge.isParallel = Math.abs(a) <= 1e-6
|
|
71
|
+
if (edge.isParallel) {
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
edge.intersection = IntersectSegmentsParametric(this.line[0], this.line[1],
|
|
75
|
+
edge.start, edge.end, true)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Create intersection nodes. Each node with `toggle` property set causes line state change, so
|
|
81
|
+
* unnecessary changes should be filtered out inside this method. Node also can suppress or
|
|
82
|
+
* un-suppress line if currently enabled, this is done by setting `suppress` and
|
|
83
|
+
* `unsuppress` properties on the edge.
|
|
84
|
+
*/
|
|
85
|
+
_CreateNodes() {
|
|
86
|
+
this.nodes = []
|
|
87
|
+
for (const loop of this.loops) {
|
|
88
|
+
for (let edge of loop) {
|
|
89
|
+
if (edge.isZero || edge.isParallel || edge.isProcessed || !edge.intersection) {
|
|
90
|
+
continue
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (edge.intersection[1] < -ENDPOINT_MARGIN ||
|
|
94
|
+
edge.intersection[1] > 1 + ENDPOINT_MARGIN) {
|
|
95
|
+
/* No intersection. */
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Some intersection exists, check if near endpoints. */
|
|
100
|
+
const isStartVtx = edge.intersection[1] <= ENDPOINT_MARGIN
|
|
101
|
+
if (isStartVtx || edge.intersection[1] >= 1 - ENDPOINT_MARGIN) {
|
|
102
|
+
/* Intersection near start or end vertex, force connected edge check. */
|
|
103
|
+
let [connEdge, isDirect] = this._GetConnectedEdge(edge, isStartVtx)
|
|
104
|
+
if (!connEdge) {
|
|
105
|
+
/* Some invalid case, ignore. */
|
|
106
|
+
continue
|
|
107
|
+
}
|
|
108
|
+
edge.isProcessed = true
|
|
109
|
+
connEdge.isProcessed = true
|
|
110
|
+
if (isDirect) {
|
|
111
|
+
if (EdgeSameSide(edge, connEdge)) {
|
|
112
|
+
edge.toggle = true
|
|
113
|
+
this.nodes.push(edge)
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
/** Connected through colinear edge(s). Mark the first edge to temporarily
|
|
117
|
+
* disable line if it is enabled. Second edge either toggles the state or
|
|
118
|
+
* restores previous one.
|
|
119
|
+
*/
|
|
120
|
+
if (edge.intersection[0] > connEdge.intersection[0]) {
|
|
121
|
+
/* Set proper order, `edge` is the first intersection, `connEdge` - the
|
|
122
|
+
* second one.
|
|
123
|
+
*/
|
|
124
|
+
const tmp = connEdge
|
|
125
|
+
connEdge = edge
|
|
126
|
+
edge = tmp
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
edge.suppress = true
|
|
130
|
+
connEdge.unsuppress = true
|
|
131
|
+
|
|
132
|
+
this.nodes.push(edge)
|
|
133
|
+
|
|
134
|
+
if (EdgeSameSide(edge, connEdge)) {
|
|
135
|
+
connEdge.toggle = true
|
|
136
|
+
}
|
|
137
|
+
this.nodes.push(connEdge)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
} else {
|
|
141
|
+
/* Clean inner intersection. */
|
|
142
|
+
edge.isProcessed = true
|
|
143
|
+
edge.toggle = true
|
|
144
|
+
this.nodes.push(edge)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @param {Edge} edge
|
|
152
|
+
* @param {boolean} isStartVtx True for connected through start vertex, false for end vertex.
|
|
153
|
+
* @return {[?Edge, boolean]} Connected valid edge if found, null if not found (e.g. is the same
|
|
154
|
+
* edge for some reason). Second value is true if directly connected, false if though colinear
|
|
155
|
+
* edges.
|
|
156
|
+
*/
|
|
157
|
+
_GetConnectedEdge(edge, isStartVtx) {
|
|
158
|
+
const loop = this.loops[edge.loopIdx]
|
|
159
|
+
let i = edge.idx
|
|
160
|
+
let isDirect = true
|
|
161
|
+
do {
|
|
162
|
+
if (isStartVtx) {
|
|
163
|
+
if (i == 0) {
|
|
164
|
+
i = loop.length - 1
|
|
165
|
+
} else {
|
|
166
|
+
i--
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
if (i == loop.length - 1) {
|
|
170
|
+
i = 0
|
|
171
|
+
} else {
|
|
172
|
+
i++
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const connEdge = loop[i]
|
|
176
|
+
if (connEdge.isZero || connEdge.isParallel) {
|
|
177
|
+
isDirect = false
|
|
178
|
+
} else {
|
|
179
|
+
return [connEdge, isDirect]
|
|
180
|
+
}
|
|
181
|
+
} while (i != edge.idx)
|
|
182
|
+
return [null, false]
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
_GenerateOddParitySegments() {
|
|
186
|
+
const result = []
|
|
187
|
+
let state = false
|
|
188
|
+
/* Incremented with each suppression, decremented with each un-suppression. */
|
|
189
|
+
let suppress = 0
|
|
190
|
+
/* Previous node when line was enabled. */
|
|
191
|
+
let prevNode = null
|
|
192
|
+
|
|
193
|
+
for (const node of this.nodes) {
|
|
194
|
+
if (node.suppress) {
|
|
195
|
+
suppress++
|
|
196
|
+
}
|
|
197
|
+
if (node.unsuppress) {
|
|
198
|
+
suppress--
|
|
199
|
+
}
|
|
200
|
+
if (node.toggle) {
|
|
201
|
+
state = !state
|
|
202
|
+
}
|
|
203
|
+
if (suppress == 0 && state && (node.unsuppress || node.toggle)) {
|
|
204
|
+
/* Just started new segment. */
|
|
205
|
+
prevNode = node
|
|
206
|
+
} else if ((suppress || !state) && prevNode) {
|
|
207
|
+
if (node.intersection[0] - prevNode.intersection[0] > Number.EPSILON) {
|
|
208
|
+
result.push([prevNode.intersection[0], node.intersection[0]])
|
|
209
|
+
}
|
|
210
|
+
prevNode = null
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return result
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
_GenerateThroughAllSegments() {
|
|
218
|
+
const result = []
|
|
219
|
+
/* Incremented with each suppression, decremented with each un-suppression. */
|
|
220
|
+
let suppress = 0
|
|
221
|
+
/* Previous node when line was enabled. */
|
|
222
|
+
let prevNode = null
|
|
223
|
+
/** For each loop count number of crossing from each side. One side increments corresponding
|
|
224
|
+
* loop value, other decrements. When all values are zero, line is outside of any loop and
|
|
225
|
+
* should not be rendered.
|
|
226
|
+
*/
|
|
227
|
+
const loopStack = new Array(this.loops.length).fill(0);
|
|
228
|
+
|
|
229
|
+
function IsOutside() {
|
|
230
|
+
for (const n of loopStack) {
|
|
231
|
+
if (n != 0) {
|
|
232
|
+
return false
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return true
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
for (const node of this.nodes) {
|
|
239
|
+
if (node.suppress) {
|
|
240
|
+
suppress++
|
|
241
|
+
}
|
|
242
|
+
if (node.unsuppress) {
|
|
243
|
+
suppress--
|
|
244
|
+
}
|
|
245
|
+
const wasOutside = IsOutside()
|
|
246
|
+
if (node.toggle) {
|
|
247
|
+
if (node.intersection[2] > 0) {
|
|
248
|
+
loopStack[node.loopIdx]++
|
|
249
|
+
} else {
|
|
250
|
+
loopStack[node.loopIdx]--
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (suppress == 0 && !IsOutside() && (node.unsuppress || wasOutside)) {
|
|
254
|
+
/* Just started new segment. */
|
|
255
|
+
prevNode = node
|
|
256
|
+
} else if ((suppress || IsOutside()) && prevNode) {
|
|
257
|
+
if (node.intersection[0] - prevNode.intersection[0] > Number.EPSILON) {
|
|
258
|
+
result.push([prevNode.intersection[0], node.intersection[0]])
|
|
259
|
+
}
|
|
260
|
+
prevNode = null
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return result
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export class HatchCalculator {
|
|
269
|
+
boundaryLoops
|
|
270
|
+
style
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Arrays of `Path` to use as boundary, and each `Path` is array of `Point`.
|
|
274
|
+
*
|
|
275
|
+
* @param {Vector2[][]} boundaryLoops
|
|
276
|
+
* @param {HatchStyle} style
|
|
277
|
+
*/
|
|
278
|
+
constructor(boundaryLoops, style) {
|
|
279
|
+
this.boundaryLoops = boundaryLoops
|
|
280
|
+
this.style = style
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Clip `line` using strategy defined by `this.style`
|
|
285
|
+
*
|
|
286
|
+
* @param {[Vector2, Vector2]} line Line segment defined by start and end points. Assuming start
|
|
287
|
+
* and end points lie out of the boundary loops specified in the constructor.
|
|
288
|
+
* @returns {[Vector2, Vector2][]} clipped line segments
|
|
289
|
+
*/
|
|
290
|
+
ClipLine(line) {
|
|
291
|
+
return new ClipCalculator(this.boundaryLoops, this.style, line).Calculate()
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* @param {Vector2} seedPoint Pattern seed point coordinates in OCS.
|
|
296
|
+
* @param {?number} angle Pattern rotation angle in radians.
|
|
297
|
+
* @param {?number} scale Pattern scale.
|
|
298
|
+
* @return {Matrix3} Transformation from OCS to pattern space.
|
|
299
|
+
*/
|
|
300
|
+
GetPatternTransform({seedPoint, angle, scale}) {
|
|
301
|
+
const m = new Matrix3().makeTranslation(-seedPoint.x, -seedPoint.y)
|
|
302
|
+
if (angle) {
|
|
303
|
+
/* Matrix3.rotate() inverts angle sign. */
|
|
304
|
+
m.rotate(angle)
|
|
305
|
+
}
|
|
306
|
+
if ((scale ?? 1) != 1) {
|
|
307
|
+
m.scale(1 / scale, 1 / scale)
|
|
308
|
+
}
|
|
309
|
+
return m
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @param {Matrix3} patTransform Transformation from OCS to pattern space previously obtained by
|
|
314
|
+
* GetPatternTransform() method.
|
|
315
|
+
* @param {?Vector2} basePoint Line base point coordinate in pattern space.
|
|
316
|
+
* @param {?number} angle Line direction angle in radians, CCW from +X direction.
|
|
317
|
+
* @return {Matrix3} Transformation from OCS to pattern line space. Line is started at origin
|
|
318
|
+
* and directed into position X axis direction.
|
|
319
|
+
*/
|
|
320
|
+
GetLineTransform({patTransform, basePoint, angle}) {
|
|
321
|
+
const m = patTransform.clone()
|
|
322
|
+
if (basePoint) {
|
|
323
|
+
m.translate(-basePoint.x, -basePoint.y)
|
|
324
|
+
}
|
|
325
|
+
if (angle) {
|
|
326
|
+
/* Matrix3.rotate() inverts angle sign. */
|
|
327
|
+
m.rotate(angle)
|
|
328
|
+
}
|
|
329
|
+
return m
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* @param {Matrix3} transform Transformation from OCS to target coordinates space.
|
|
334
|
+
* @return {Box2} Pattern AABB in target coordinate space.
|
|
335
|
+
*/
|
|
336
|
+
GetBoundingBox(transform) {
|
|
337
|
+
const box = new Box2()
|
|
338
|
+
for (const path of this.boundaryLoops) {
|
|
339
|
+
for (const v of path) {
|
|
340
|
+
box.expandByPoint(v.clone().applyMatrix3(transform))
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return box
|
|
344
|
+
}
|
|
345
|
+
}
|