@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,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
+ }