build-dxf 0.0.20-14 → 0.0.20-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/build.js +1915 -1194
- package/src/index.css +629 -1
- package/src/index.js +7 -7
- package/src/index2.js +528 -327
- package/src/index3.js +1714 -1660
- package/src/selectLocalFile.js +3145 -1955
- package/src/utils/CommandManager/CommandFlow.d.ts +0 -17
- package/src/utils/CommandManager/CommandManager.d.ts +0 -23
- package/src/utils/DxfSystem/components/Dxf.d.ts +0 -1
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/CommandFlowComponent.d.ts +1 -4
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/Default.d.ts +20 -0
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/DrawDoorLine.d.ts +4 -24
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/DrawLine.d.ts +4 -20
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/DrawWindow.d.ts +2 -25
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/PointDrag.d.ts +1 -14
- package/src/utils/DxfSystem/plugin/Editor/components/index.d.ts +0 -1
- package/src/utils/Quadtree/LineSegment.d.ts +0 -1
- package/src/utils/Quadtree/Point.d.ts +1 -2
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/ConnectionLine.d.ts +0 -33
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/DeleteSelectLine.d.ts +0 -28
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/DeleteSelectWindow.d.ts +0 -33
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/IntersectionConnectionLine.d.ts +0 -33
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/MergeLine.d.ts +0 -33
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/SelectAll.d.ts +0 -30
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/VerticalCorrection.d.ts +0 -76
- package/src/utils/DxfSystem/plugin/Editor/components/CommandFlow/ViewAngle.d.ts +0 -15
package/src/build.js
CHANGED
|
@@ -1,55 +1,70 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { EventDispatcher as
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import { OBJExporter
|
|
6
|
-
import { GLTFExporter
|
|
7
|
-
function
|
|
8
|
-
return "xxxx-xxxx-4xxx-yxxx-xxxx".replace(/[xy]/g, function(
|
|
9
|
-
var
|
|
10
|
-
return
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { EventDispatcher as EventDispatcher$1 } from "three";
|
|
3
|
+
import ClipperLib from "clipper-lib";
|
|
4
|
+
import Drawing from "dxf-writer";
|
|
5
|
+
import { OBJExporter } from "three/examples/jsm/exporters/OBJExporter.js";
|
|
6
|
+
import { GLTFExporter } from "three/examples/jsm/exporters/GLTFExporter.js";
|
|
7
|
+
function uuid() {
|
|
8
|
+
return "xxxx-xxxx-4xxx-yxxx-xxxx".replace(/[xy]/g, function(c) {
|
|
9
|
+
var r = Math.random() * 16 | 0, v = c == "x" ? r : r & 3 | 8;
|
|
10
|
+
return v.toString(16);
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
|
-
class
|
|
14
|
-
uuid =
|
|
15
|
-
addEventListener(
|
|
16
|
-
const { once
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
class EventDispatcher extends EventDispatcher$1 {
|
|
14
|
+
uuid = uuid();
|
|
15
|
+
addEventListener(type, listener, option) {
|
|
16
|
+
const { once = false } = option ?? {};
|
|
17
|
+
const callBack = (event) => {
|
|
18
|
+
listener(event);
|
|
19
|
+
once && canceCallBack();
|
|
20
|
+
};
|
|
21
|
+
const canceCallBack = () => this.removeEventListener(type, callBack);
|
|
22
|
+
super.addEventListener(type, callBack);
|
|
23
|
+
return canceCallBack;
|
|
20
24
|
}
|
|
21
25
|
eventRecordStack = /* @__PURE__ */ new Map();
|
|
22
|
-
addEventRecord(
|
|
23
|
-
this.eventRecordStack.has(
|
|
24
|
-
|
|
26
|
+
addEventRecord(name, ...cancels) {
|
|
27
|
+
if (!this.eventRecordStack.has(name)) this.eventRecordStack.set(name, []);
|
|
28
|
+
cancels.forEach((cancel) => {
|
|
29
|
+
this.eventRecordStack.get(name)?.push(cancel);
|
|
25
30
|
});
|
|
26
|
-
const
|
|
27
|
-
|
|
31
|
+
const add = (cancel) => {
|
|
32
|
+
this.addEventRecord(name, cancel);
|
|
33
|
+
return { add };
|
|
34
|
+
};
|
|
35
|
+
return { add };
|
|
28
36
|
}
|
|
29
|
-
canceEventRecord(
|
|
30
|
-
const
|
|
31
|
-
|
|
37
|
+
canceEventRecord(name) {
|
|
38
|
+
const list = this.eventRecordStack.get(name);
|
|
39
|
+
if (list) {
|
|
40
|
+
list.forEach((cancel) => cancel());
|
|
41
|
+
this.eventRecordStack.delete(name);
|
|
42
|
+
}
|
|
32
43
|
}
|
|
33
44
|
}
|
|
34
|
-
class
|
|
45
|
+
class Component extends EventDispatcher {
|
|
35
46
|
parent;
|
|
36
|
-
destroyed =
|
|
37
|
-
constructor(...
|
|
38
|
-
super()
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
this.
|
|
47
|
+
destroyed = false;
|
|
48
|
+
constructor(...arg) {
|
|
49
|
+
super();
|
|
50
|
+
this.addEventListener("addFromParent", (e) => {
|
|
51
|
+
this.parent = e.parent;
|
|
52
|
+
this.onAddFromParent(e.parent);
|
|
53
|
+
});
|
|
54
|
+
this.addEventListener("removeFromParent", (e) => {
|
|
55
|
+
this.parent = void 0;
|
|
56
|
+
this.onRemoveFromParent(e.parent);
|
|
42
57
|
});
|
|
43
58
|
}
|
|
44
|
-
onAddFromParent(
|
|
59
|
+
onAddFromParent(parent) {
|
|
45
60
|
}
|
|
46
|
-
onRemoveFromParent(
|
|
61
|
+
onRemoveFromParent(parent) {
|
|
47
62
|
}
|
|
48
63
|
destroy() {
|
|
49
|
-
this.destroyed =
|
|
64
|
+
this.destroyed = true;
|
|
50
65
|
}
|
|
51
66
|
}
|
|
52
|
-
class
|
|
67
|
+
class ComponentManager extends EventDispatcher {
|
|
53
68
|
static EventType = {
|
|
54
69
|
ADD_COMPONENT: "addComponent"
|
|
55
70
|
};
|
|
@@ -58,66 +73,75 @@ class et extends $ {
|
|
|
58
73
|
* 添加组件
|
|
59
74
|
* @param component
|
|
60
75
|
*/
|
|
61
|
-
addComponent(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
addComponent(component) {
|
|
77
|
+
if (component) {
|
|
78
|
+
this.components.push(component);
|
|
79
|
+
this.dispatchEvent({
|
|
80
|
+
type: "addComponent",
|
|
81
|
+
component
|
|
82
|
+
});
|
|
83
|
+
component.dispatchEvent({
|
|
84
|
+
type: "addFromParent",
|
|
85
|
+
parent: this
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return this;
|
|
69
89
|
}
|
|
70
90
|
/**
|
|
71
91
|
* 移除组件
|
|
72
92
|
* @param component
|
|
73
93
|
*/
|
|
74
|
-
removeComponent(
|
|
75
|
-
if (
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
removeComponent(component) {
|
|
95
|
+
if (component instanceof Component) {
|
|
96
|
+
const index2 = this.components.indexOf(component);
|
|
97
|
+
if (index2 > -1) {
|
|
98
|
+
this.components.splice(index2, 1);
|
|
99
|
+
this.dispatchEvent({
|
|
100
|
+
type: "removeComponent",
|
|
101
|
+
component
|
|
102
|
+
});
|
|
103
|
+
component.dispatchEvent({
|
|
104
|
+
type: "removeFromParent",
|
|
105
|
+
parent: this
|
|
106
|
+
});
|
|
107
|
+
}
|
|
84
108
|
}
|
|
85
109
|
}
|
|
86
110
|
/**
|
|
87
111
|
* 查找符合条件的第一个组件
|
|
88
112
|
* @param callBack
|
|
89
113
|
*/
|
|
90
|
-
findComponent(
|
|
91
|
-
for (let
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
114
|
+
findComponent(predicate) {
|
|
115
|
+
for (let i = 0; i < this.components.length; i++) {
|
|
116
|
+
const component = this.components[i];
|
|
117
|
+
if (predicate(component, i)) return component;
|
|
94
118
|
}
|
|
95
119
|
}
|
|
96
120
|
/**
|
|
97
121
|
* 查找所有符合条件的组件
|
|
98
122
|
* @param callBack
|
|
99
123
|
*/
|
|
100
|
-
findComponents(
|
|
101
|
-
return this.components.filter(
|
|
124
|
+
findComponents(predicate) {
|
|
125
|
+
return this.components.filter(predicate);
|
|
102
126
|
}
|
|
103
127
|
/**
|
|
104
128
|
*
|
|
105
129
|
* @param type
|
|
106
130
|
*/
|
|
107
|
-
findComponentByType(
|
|
108
|
-
const
|
|
109
|
-
return
|
|
131
|
+
findComponentByType(type) {
|
|
132
|
+
const component = this.findComponent((c) => c instanceof type);
|
|
133
|
+
return component ? component : null;
|
|
110
134
|
}
|
|
111
135
|
/**
|
|
112
136
|
*
|
|
113
137
|
* @param type
|
|
114
138
|
*/
|
|
115
|
-
findComponentByName(
|
|
116
|
-
const
|
|
117
|
-
return
|
|
139
|
+
findComponentByName(name) {
|
|
140
|
+
const component = this.findComponent((c) => c.constructor.name === name);
|
|
141
|
+
return component ? component : null;
|
|
118
142
|
}
|
|
119
143
|
}
|
|
120
|
-
class
|
|
144
|
+
class Point {
|
|
121
145
|
x;
|
|
122
146
|
y;
|
|
123
147
|
get X() {
|
|
@@ -132,32 +156,39 @@ class f {
|
|
|
132
156
|
* @param x
|
|
133
157
|
* @param y
|
|
134
158
|
*/
|
|
135
|
-
constructor(
|
|
136
|
-
this.x =
|
|
159
|
+
constructor(x = 0, y = 0) {
|
|
160
|
+
this.x = x;
|
|
161
|
+
this.y = y;
|
|
137
162
|
}
|
|
138
|
-
set(
|
|
139
|
-
|
|
163
|
+
set(x, y) {
|
|
164
|
+
this.x = x;
|
|
165
|
+
this.y = y;
|
|
166
|
+
return this;
|
|
140
167
|
}
|
|
141
|
-
setX(
|
|
142
|
-
|
|
168
|
+
setX(x) {
|
|
169
|
+
this.x = x;
|
|
170
|
+
return this;
|
|
143
171
|
}
|
|
144
|
-
setY(
|
|
145
|
-
|
|
172
|
+
setY(y) {
|
|
173
|
+
this.y = y;
|
|
174
|
+
return this;
|
|
146
175
|
}
|
|
147
176
|
/**
|
|
148
177
|
*
|
|
149
178
|
* @param point
|
|
150
179
|
* @returns
|
|
151
180
|
*/
|
|
152
|
-
equal(
|
|
153
|
-
return
|
|
181
|
+
equal(point) {
|
|
182
|
+
return point.x === this.x && point.y === this.y;
|
|
154
183
|
}
|
|
155
184
|
/**
|
|
156
185
|
*
|
|
157
186
|
* @param arr
|
|
158
187
|
*/
|
|
159
|
-
setByArray(
|
|
160
|
-
|
|
188
|
+
setByArray(arr) {
|
|
189
|
+
this.x = arr[0];
|
|
190
|
+
this.y = arr[1];
|
|
191
|
+
return this;
|
|
161
192
|
}
|
|
162
193
|
/**
|
|
163
194
|
*
|
|
@@ -169,19 +200,25 @@ class f {
|
|
|
169
200
|
* multiplyScalar
|
|
170
201
|
* @param scalar
|
|
171
202
|
*/
|
|
172
|
-
mutiplyScalar(
|
|
173
|
-
|
|
203
|
+
mutiplyScalar(scalar) {
|
|
204
|
+
this.x *= scalar;
|
|
205
|
+
this.y *= scalar;
|
|
206
|
+
return this;
|
|
174
207
|
}
|
|
175
|
-
multiplyScalar(
|
|
176
|
-
|
|
208
|
+
multiplyScalar(scalar) {
|
|
209
|
+
this.x *= scalar;
|
|
210
|
+
this.y *= scalar;
|
|
211
|
+
return this;
|
|
177
212
|
}
|
|
178
213
|
/**
|
|
179
214
|
*
|
|
180
215
|
* @param scalar
|
|
181
216
|
* @returns
|
|
182
217
|
*/
|
|
183
|
-
divisionScalar(
|
|
184
|
-
|
|
218
|
+
divisionScalar(scalar) {
|
|
219
|
+
this.x /= scalar;
|
|
220
|
+
this.y /= scalar;
|
|
221
|
+
return this;
|
|
185
222
|
}
|
|
186
223
|
/**
|
|
187
224
|
* 减法
|
|
@@ -189,8 +226,10 @@ class f {
|
|
|
189
226
|
* @param point
|
|
190
227
|
* @returns
|
|
191
228
|
*/
|
|
192
|
-
division(
|
|
193
|
-
|
|
229
|
+
division(point) {
|
|
230
|
+
this.x -= point.x;
|
|
231
|
+
this.y -= point.y;
|
|
232
|
+
return this;
|
|
194
233
|
}
|
|
195
234
|
/**
|
|
196
235
|
* 加法
|
|
@@ -198,15 +237,19 @@ class f {
|
|
|
198
237
|
* @param point
|
|
199
238
|
* @returns
|
|
200
239
|
*/
|
|
201
|
-
add(
|
|
202
|
-
|
|
240
|
+
add(point) {
|
|
241
|
+
this.x += point.x;
|
|
242
|
+
this.y += point.y;
|
|
243
|
+
return this;
|
|
203
244
|
}
|
|
204
245
|
/**
|
|
205
246
|
* 保留小数位数
|
|
206
247
|
* @param count
|
|
207
248
|
*/
|
|
208
|
-
fixed(
|
|
209
|
-
|
|
249
|
+
fixed(count) {
|
|
250
|
+
this.x = Number(this.x.toFixed(count));
|
|
251
|
+
this.y = Number(this.y.toFixed(count));
|
|
252
|
+
return this;
|
|
210
253
|
}
|
|
211
254
|
/**
|
|
212
255
|
* 归一化
|
|
@@ -214,8 +257,11 @@ class f {
|
|
|
214
257
|
* @returns
|
|
215
258
|
*/
|
|
216
259
|
normalize() {
|
|
217
|
-
const
|
|
218
|
-
|
|
260
|
+
const length = Math.sqrt(this.x * this.x + this.y * this.y);
|
|
261
|
+
if (length === 0) return this;
|
|
262
|
+
this.x /= length;
|
|
263
|
+
this.y /= length;
|
|
264
|
+
return this;
|
|
219
265
|
}
|
|
220
266
|
/**
|
|
221
267
|
* 获取单位法向量
|
|
@@ -223,9 +269,13 @@ class f {
|
|
|
223
269
|
* @param point
|
|
224
270
|
* @returns
|
|
225
271
|
*/
|
|
226
|
-
normal(
|
|
227
|
-
const
|
|
228
|
-
|
|
272
|
+
normal(point) {
|
|
273
|
+
const dx = this.x - point.x;
|
|
274
|
+
const dy = this.y - point.y;
|
|
275
|
+
const length = Math.sqrt(dx * dx + dy * dy);
|
|
276
|
+
const nx = -dy / length;
|
|
277
|
+
const ny = dx / length;
|
|
278
|
+
return new Point(nx, ny);
|
|
229
279
|
}
|
|
230
280
|
/**
|
|
231
281
|
* 获取由传入的点到该点的单位方向向量
|
|
@@ -233,9 +283,12 @@ class f {
|
|
|
233
283
|
* @param point
|
|
234
284
|
* @returns
|
|
235
285
|
*/
|
|
236
|
-
direction(
|
|
237
|
-
const
|
|
238
|
-
|
|
286
|
+
direction(point) {
|
|
287
|
+
const dx = this.x - point.x;
|
|
288
|
+
const dy = this.y - point.y;
|
|
289
|
+
const length = Math.sqrt(dx * dx + dy * dy);
|
|
290
|
+
if (length === 0) return new Point(0, 0);
|
|
291
|
+
return new Point(dx / length, dy / length);
|
|
239
292
|
}
|
|
240
293
|
/**
|
|
241
294
|
* 计算模长
|
|
@@ -249,8 +302,8 @@ class f {
|
|
|
249
302
|
* @param point
|
|
250
303
|
* @returns
|
|
251
304
|
*/
|
|
252
|
-
dot(
|
|
253
|
-
return this.x *
|
|
305
|
+
dot(point) {
|
|
306
|
+
return this.x * point.x + this.y * point.y;
|
|
254
307
|
}
|
|
255
308
|
/** 求两个点叉积
|
|
256
309
|
* @description 如果叉积大于 0,a 到 b 为逆时针方向。
|
|
@@ -258,8 +311,8 @@ class f {
|
|
|
258
311
|
* @param point
|
|
259
312
|
* @returns
|
|
260
313
|
*/
|
|
261
|
-
cross(
|
|
262
|
-
return this.x *
|
|
314
|
+
cross(point) {
|
|
315
|
+
return this.x * point.y - this.y * this.x;
|
|
263
316
|
}
|
|
264
317
|
/** 计算两个向量夹角
|
|
265
318
|
* @description 公式:a · b = |a| × |b| × cosθ
|
|
@@ -267,25 +320,34 @@ class f {
|
|
|
267
320
|
* @param point
|
|
268
321
|
* @returns
|
|
269
322
|
*/
|
|
270
|
-
angleBetween(
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
const
|
|
274
|
-
|
|
323
|
+
angleBetween(point, type = "radian", angle = "180") {
|
|
324
|
+
const dotProduct = this.dot(point);
|
|
325
|
+
const magnitude1 = this.magnitude();
|
|
326
|
+
const magnitude2 = point.magnitude();
|
|
327
|
+
if (magnitude1 === 0 || magnitude2 === 0) return 0;
|
|
328
|
+
const cosTheta = dotProduct / (magnitude1 * magnitude2);
|
|
329
|
+
const clampedCosTheta = Math.max(-1, Math.min(1, cosTheta));
|
|
330
|
+
if (type === "radian") return Math.acos(clampedCosTheta);
|
|
331
|
+
if (type === "cos") return clampedCosTheta;
|
|
332
|
+
if (angle === "180" || this.cross(point) < 0) return Math.acos(clampedCosTheta) / (Math.PI / 180);
|
|
333
|
+
return 360 - Math.acos(clampedCosTheta) / (Math.PI / 180);
|
|
275
334
|
}
|
|
276
335
|
/** 获取向量长度
|
|
277
336
|
*/
|
|
278
337
|
length() {
|
|
279
|
-
const
|
|
280
|
-
|
|
338
|
+
const magnitude = Math.sqrt(this.x * this.x + this.y * this.y);
|
|
339
|
+
if (Math.abs(magnitude - Math.round(magnitude)) < 1e-9) {
|
|
340
|
+
return Math.round(magnitude);
|
|
341
|
+
}
|
|
342
|
+
return magnitude;
|
|
281
343
|
}
|
|
282
344
|
/**
|
|
283
345
|
* 获取两个点长度
|
|
284
346
|
* @param point
|
|
285
347
|
*/
|
|
286
|
-
distance(
|
|
348
|
+
distance(point) {
|
|
287
349
|
return Math.sqrt(
|
|
288
|
-
(this.x -
|
|
350
|
+
(this.x - point.x) * (this.x - point.x) + (this.y - point.y) * (this.y - point.y)
|
|
289
351
|
);
|
|
290
352
|
}
|
|
291
353
|
/**
|
|
@@ -293,40 +355,47 @@ class f {
|
|
|
293
355
|
* @returns
|
|
294
356
|
*/
|
|
295
357
|
clone() {
|
|
296
|
-
return new
|
|
358
|
+
return new Point(this.x, this.y);
|
|
297
359
|
}
|
|
298
360
|
/**
|
|
299
361
|
* 克隆
|
|
300
362
|
* @returns
|
|
301
363
|
*/
|
|
302
|
-
copy(
|
|
303
|
-
this.x =
|
|
364
|
+
copy(p) {
|
|
365
|
+
this.x = p.x ?? 0;
|
|
366
|
+
this.y = p.y ?? 0;
|
|
304
367
|
}
|
|
305
|
-
toJson(
|
|
368
|
+
toJson() {
|
|
306
369
|
return {
|
|
307
370
|
x: this.x,
|
|
308
|
-
y: this.y
|
|
309
|
-
z: t
|
|
371
|
+
y: this.y
|
|
310
372
|
};
|
|
311
373
|
}
|
|
312
|
-
static from(
|
|
313
|
-
|
|
374
|
+
static from(arr) {
|
|
375
|
+
if (Array.isArray(arr)) {
|
|
376
|
+
return new Point(arr[0], arr[1]);
|
|
377
|
+
} else if ("x" in arr && "y" in arr) {
|
|
378
|
+
return new Point(arr.x, arr.y);
|
|
379
|
+
} else if ("X" in arr && "Y" in arr) {
|
|
380
|
+
return new Point(arr.X, arr.Y);
|
|
381
|
+
}
|
|
382
|
+
return this.zero();
|
|
314
383
|
}
|
|
315
384
|
static zero() {
|
|
316
|
-
return new
|
|
385
|
+
return new Point(0, 0);
|
|
317
386
|
}
|
|
318
387
|
}
|
|
319
|
-
class
|
|
388
|
+
class Box2 {
|
|
320
389
|
minX = 0;
|
|
321
390
|
maxX = 0;
|
|
322
391
|
minY = 0;
|
|
323
392
|
maxY = 0;
|
|
324
393
|
get points() {
|
|
325
394
|
return [
|
|
326
|
-
new
|
|
327
|
-
new
|
|
328
|
-
new
|
|
329
|
-
new
|
|
395
|
+
new Point(this.minX, this.minY),
|
|
396
|
+
new Point(this.maxX, this.minY),
|
|
397
|
+
new Point(this.maxX, this.maxY),
|
|
398
|
+
new Point(this.minX, this.maxY)
|
|
330
399
|
];
|
|
331
400
|
}
|
|
332
401
|
get width() {
|
|
@@ -336,122 +405,163 @@ class A {
|
|
|
336
405
|
return this.maxY - this.minY;
|
|
337
406
|
}
|
|
338
407
|
get center() {
|
|
339
|
-
return new
|
|
408
|
+
return new Point(
|
|
340
409
|
this.minX + (this.maxX - this.minX) * 0.5,
|
|
341
410
|
this.minY + (this.maxY - this.minY) * 0.5
|
|
342
411
|
);
|
|
343
412
|
}
|
|
344
|
-
constructor(
|
|
345
|
-
this.minX =
|
|
413
|
+
constructor(minX = 0, maxX = 0, minY = 0, maxY = 0) {
|
|
414
|
+
this.minX = minX;
|
|
415
|
+
this.maxX = maxX;
|
|
416
|
+
this.minY = minY;
|
|
417
|
+
this.maxY = maxY;
|
|
346
418
|
}
|
|
347
419
|
/**
|
|
348
420
|
*
|
|
349
421
|
* @param z
|
|
350
422
|
* @returns
|
|
351
423
|
*/
|
|
352
|
-
getPaths3D(
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
424
|
+
getPaths3D(z = 0) {
|
|
425
|
+
const points = this.points, list = [];
|
|
426
|
+
points.forEach((p, i) => {
|
|
427
|
+
const nextP = points[(i + 1) % points.length];
|
|
428
|
+
list.push(p.x, p.y, z);
|
|
429
|
+
list.push(nextP.x, nextP.y, z);
|
|
430
|
+
});
|
|
431
|
+
return list;
|
|
358
432
|
}
|
|
359
433
|
/**
|
|
360
434
|
* 判断线段是与包围盒相交
|
|
361
435
|
* @description Liang-Barsky算法的变种
|
|
362
436
|
* @param line
|
|
363
437
|
*/
|
|
364
|
-
intersectLineSegment(
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
if (
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if (
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
438
|
+
intersectLineSegment(line) {
|
|
439
|
+
const p1 = line.points[0];
|
|
440
|
+
const p2 = line.points[1];
|
|
441
|
+
const dx = p2.x - p1.x;
|
|
442
|
+
const dy = p2.y - p1.y;
|
|
443
|
+
if (dx === 0 && dy === 0) {
|
|
444
|
+
return this.minX <= p1.x && p1.x <= this.maxX && this.minY <= p1.y && p1.y <= this.maxY;
|
|
445
|
+
}
|
|
446
|
+
let tNear = Number.NEGATIVE_INFINITY;
|
|
447
|
+
let tFar = Number.POSITIVE_INFINITY;
|
|
448
|
+
if (dx !== 0) {
|
|
449
|
+
const tx1 = (this.minX - p1.x) / dx;
|
|
450
|
+
const tx2 = (this.maxX - p1.x) / dx;
|
|
451
|
+
tNear = Math.max(tNear, Math.min(tx1, tx2));
|
|
452
|
+
tFar = Math.min(tFar, Math.max(tx1, tx2));
|
|
453
|
+
} else if (p1.x < this.minX || p1.x > this.maxX) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
if (dy !== 0) {
|
|
457
|
+
const ty1 = (this.minY - p1.y) / dy;
|
|
458
|
+
const ty2 = (this.maxY - p1.y) / dy;
|
|
459
|
+
tNear = Math.max(tNear, Math.min(ty1, ty2));
|
|
460
|
+
tFar = Math.min(tFar, Math.max(ty1, ty2));
|
|
461
|
+
} else if (p1.y < this.minY || p1.y > this.maxY) {
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
return tNear <= tFar && tNear <= 1 && tFar >= 0;
|
|
380
465
|
}
|
|
381
466
|
/**
|
|
382
467
|
* 判断线段是在包围盒内
|
|
383
468
|
* @param line
|
|
384
469
|
*/
|
|
385
|
-
containsLineSegment(
|
|
386
|
-
const [
|
|
387
|
-
return this.minX <=
|
|
470
|
+
containsLineSegment(line) {
|
|
471
|
+
const [p1, p2] = line.points;
|
|
472
|
+
return this.minX <= p1.x && p1.x <= this.maxX && this.minY <= p1.y && p1.y <= this.maxY && this.minX <= p2.x && p2.x <= this.maxX && this.minY <= p2.y && p2.y <= this.maxY;
|
|
388
473
|
}
|
|
389
474
|
/**
|
|
390
475
|
* 判断矩形与包围盒相交
|
|
391
476
|
* @param rectangle
|
|
392
477
|
*/
|
|
393
|
-
intersectRectangle(
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
478
|
+
intersectRectangle(rectangle) {
|
|
479
|
+
const isPointInBox = (p) => this.minX <= p.x && p.x <= this.maxX && this.minY <= p.y && p.y <= this.maxY;
|
|
480
|
+
const isPointInRect = (point) => {
|
|
481
|
+
let sign = 0;
|
|
482
|
+
for (let i = 0; i < 4; i++) {
|
|
483
|
+
const p1 = rectangle.points[i];
|
|
484
|
+
const p2 = rectangle.points[(i + 1) % 4];
|
|
485
|
+
const edge = { x: p2.x - p1.x, y: p2.y - p1.y };
|
|
486
|
+
const toPoint = { x: point.x - p1.x, y: point.y - p1.y };
|
|
487
|
+
const cross = edge.x * toPoint.y - edge.y * toPoint.x;
|
|
488
|
+
if (cross === 0) {
|
|
489
|
+
const t = edge.x !== 0 ? (point.x - p1.x) / edge.x : (point.y - p1.y) / edge.y;
|
|
490
|
+
if (t >= 0 && t <= 1) return true;
|
|
401
491
|
} else {
|
|
402
|
-
const
|
|
403
|
-
if (
|
|
492
|
+
const currentSign = cross > 0 ? 1 : -1;
|
|
493
|
+
if (sign === 0) sign = currentSign;
|
|
494
|
+
if (sign !== currentSign) return false;
|
|
404
495
|
}
|
|
405
496
|
}
|
|
406
|
-
return
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
497
|
+
return true;
|
|
498
|
+
};
|
|
499
|
+
const doIntersect = (l1p1, l1p2, l2p1, l2p2) => {
|
|
500
|
+
const orientation = (p, q, r) => {
|
|
501
|
+
const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
502
|
+
if (val === 0) return 0;
|
|
503
|
+
return val > 0 ? 1 : 2;
|
|
504
|
+
};
|
|
505
|
+
const onSegment = (p, q, r) => {
|
|
506
|
+
return Math.min(p.x, r.x) <= q.x && q.x <= Math.max(p.x, r.x) && Math.min(p.y, r.y) <= q.y && q.y <= Math.max(p.y, r.y);
|
|
507
|
+
};
|
|
508
|
+
const o1 = orientation(l1p1, l1p2, l2p1);
|
|
509
|
+
const o2 = orientation(l1p1, l1p2, l2p2);
|
|
510
|
+
const o3 = orientation(l2p1, l2p2, l1p1);
|
|
511
|
+
const o4 = orientation(l2p1, l2p2, l1p2);
|
|
512
|
+
if (o1 !== o2 && o3 !== o4) return true;
|
|
513
|
+
if (o1 === 0 && onSegment(l1p1, l2p1, l1p2)) return true;
|
|
514
|
+
if (o2 === 0 && onSegment(l1p1, l2p2, l1p2)) return true;
|
|
515
|
+
if (o3 === 0 && onSegment(l2p1, l1p1, l2p2)) return true;
|
|
516
|
+
if (o4 === 0 && onSegment(l2p1, l1p2, l2p2)) return true;
|
|
517
|
+
return false;
|
|
518
|
+
};
|
|
519
|
+
const boxPoints = this.points;
|
|
520
|
+
for (let i = 0; i < 4; i++) {
|
|
521
|
+
const bp1 = boxPoints[i];
|
|
522
|
+
const bp2 = boxPoints[(i + 1) % 4];
|
|
523
|
+
for (let j = 0; j < 4; j++) {
|
|
524
|
+
const rp1 = rectangle.points[j];
|
|
525
|
+
const rp2 = rectangle.points[(j + 1) % 4];
|
|
526
|
+
if (doIntersect(bp1, bp2, rp1, rp2)) return true;
|
|
419
527
|
}
|
|
420
528
|
}
|
|
421
|
-
for (let
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
529
|
+
for (let p of rectangle.points) {
|
|
530
|
+
if (isPointInBox(p)) return true;
|
|
531
|
+
}
|
|
532
|
+
for (let p of boxPoints) {
|
|
533
|
+
if (isPointInRect(p)) return true;
|
|
534
|
+
}
|
|
535
|
+
return false;
|
|
426
536
|
}
|
|
427
537
|
/**
|
|
428
538
|
* 判断矩形是在包围盒内
|
|
429
539
|
* @param rectangle
|
|
430
540
|
*/
|
|
431
|
-
containsRectangle(
|
|
432
|
-
return
|
|
433
|
-
(
|
|
541
|
+
containsRectangle(rectangle) {
|
|
542
|
+
return rectangle.points.every(
|
|
543
|
+
(p) => this.minX <= p.x && p.x <= this.maxX && this.minY <= p.y && p.y <= this.maxY
|
|
434
544
|
);
|
|
435
545
|
}
|
|
436
546
|
/**
|
|
437
547
|
* 判断包围盒与包围盒相交
|
|
438
548
|
* @param box
|
|
439
549
|
*/
|
|
440
|
-
intersectBox(
|
|
441
|
-
return !(this.maxX <
|
|
550
|
+
intersectBox(box) {
|
|
551
|
+
return !(this.maxX < box.minX || this.minX > box.maxX || this.maxY < box.minY || this.minY > box.maxY);
|
|
442
552
|
}
|
|
443
553
|
/**
|
|
444
554
|
* 判断包围盒是在包围盒内
|
|
445
555
|
* @param box
|
|
446
556
|
*/
|
|
447
|
-
containsBox(
|
|
448
|
-
return this.minX <=
|
|
557
|
+
containsBox(box) {
|
|
558
|
+
return this.minX <= box.minX && box.maxX <= this.maxX && this.minY <= box.minY && box.maxY <= this.maxY;
|
|
449
559
|
}
|
|
450
560
|
/** 判断点是在包围盒内
|
|
451
561
|
* @param point
|
|
452
562
|
*/
|
|
453
|
-
containsPoint(
|
|
454
|
-
return
|
|
563
|
+
containsPoint(point) {
|
|
564
|
+
return point.x >= this.minX && point.x <= this.maxX && point.y >= this.minY && point.y <= this.maxY;
|
|
455
565
|
}
|
|
456
566
|
/**
|
|
457
567
|
*
|
|
@@ -461,8 +571,12 @@ class A {
|
|
|
461
571
|
* @param maxY
|
|
462
572
|
* @returns
|
|
463
573
|
*/
|
|
464
|
-
set(
|
|
465
|
-
|
|
574
|
+
set(minX, minY, maxX, maxY) {
|
|
575
|
+
this.minX = minX;
|
|
576
|
+
this.minY = minY;
|
|
577
|
+
this.maxX = maxX;
|
|
578
|
+
this.maxY = maxY;
|
|
579
|
+
return this;
|
|
466
580
|
}
|
|
467
581
|
/**
|
|
468
582
|
*
|
|
@@ -471,46 +585,57 @@ class A {
|
|
|
471
585
|
* @param mode
|
|
472
586
|
* @returns
|
|
473
587
|
*/
|
|
474
|
-
scaleSize(
|
|
475
|
-
const
|
|
476
|
-
|
|
588
|
+
scaleSize(maxWidth, maxHeight, mode = "min") {
|
|
589
|
+
const scaleX = maxWidth / this.width;
|
|
590
|
+
const scaleY = maxHeight / this.height;
|
|
591
|
+
return Math[mode](scaleX, scaleY);
|
|
477
592
|
}
|
|
478
593
|
/**
|
|
479
594
|
*
|
|
480
595
|
* @param scalar
|
|
481
596
|
* @returns
|
|
482
597
|
*/
|
|
483
|
-
multiplyScalar(
|
|
484
|
-
|
|
598
|
+
multiplyScalar(scalar) {
|
|
599
|
+
this.minX *= scalar;
|
|
600
|
+
this.minY *= scalar;
|
|
601
|
+
this.maxX *= scalar;
|
|
602
|
+
this.maxY *= scalar;
|
|
603
|
+
return this;
|
|
485
604
|
}
|
|
486
|
-
expansion(
|
|
487
|
-
|
|
605
|
+
expansion(w) {
|
|
606
|
+
this.minX -= w;
|
|
607
|
+
this.minY -= w;
|
|
608
|
+
this.maxX += w;
|
|
609
|
+
this.maxY += w;
|
|
610
|
+
return this;
|
|
488
611
|
}
|
|
489
612
|
/**
|
|
490
613
|
*
|
|
491
614
|
* @returns
|
|
492
615
|
*/
|
|
493
616
|
clone() {
|
|
494
|
-
return new
|
|
617
|
+
return new Box2(this.minX, this.maxX, this.minY, this.maxY);
|
|
495
618
|
}
|
|
496
619
|
/**
|
|
497
620
|
*
|
|
498
621
|
* @param points
|
|
499
622
|
* @returns
|
|
500
623
|
*/
|
|
501
|
-
static fromByPoints(...
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
Math.min(...
|
|
509
|
-
Math.max(...
|
|
624
|
+
static fromByPoints(...points) {
|
|
625
|
+
const xList = [], yList = [];
|
|
626
|
+
points.forEach((p) => {
|
|
627
|
+
xList.push(p.x);
|
|
628
|
+
yList.push(p.y);
|
|
629
|
+
});
|
|
630
|
+
return new Box2(
|
|
631
|
+
Math.min(...xList),
|
|
632
|
+
Math.max(...xList),
|
|
633
|
+
Math.min(...yList),
|
|
634
|
+
Math.max(...yList)
|
|
510
635
|
);
|
|
511
636
|
}
|
|
512
637
|
}
|
|
513
|
-
class
|
|
638
|
+
class Rectangle {
|
|
514
639
|
points;
|
|
515
640
|
get p0() {
|
|
516
641
|
return this.points[0];
|
|
@@ -525,15 +650,16 @@ class C {
|
|
|
525
650
|
return this.points[3];
|
|
526
651
|
}
|
|
527
652
|
get path() {
|
|
528
|
-
return this.points.flatMap((
|
|
529
|
-
const
|
|
530
|
-
return [
|
|
653
|
+
return this.points.flatMap((p, i) => {
|
|
654
|
+
const np = this.points[(i + 1) % this.points.length];
|
|
655
|
+
return [p.x, p.y, 0, np.x, np.y, 0];
|
|
531
656
|
});
|
|
532
657
|
}
|
|
533
|
-
path2D(
|
|
534
|
-
return this.points.flatMap((
|
|
535
|
-
const
|
|
536
|
-
|
|
658
|
+
path2D(callback) {
|
|
659
|
+
return this.points.flatMap((p, i) => {
|
|
660
|
+
const np = this.points[(i + 1) % this.points.length];
|
|
661
|
+
callback && callback(new Point(p.x, p.y), new Point(np.x, np.y));
|
|
662
|
+
return [p.x, p.y, np.x, np.y];
|
|
537
663
|
});
|
|
538
664
|
}
|
|
539
665
|
createGeometry() {
|
|
@@ -544,58 +670,85 @@ class C {
|
|
|
544
670
|
this.p2,
|
|
545
671
|
this.p0,
|
|
546
672
|
this.p3
|
|
547
|
-
].flatMap((
|
|
673
|
+
].flatMap((p) => [p.x, p.y, 0]);
|
|
548
674
|
}
|
|
549
|
-
constructor(
|
|
550
|
-
if (
|
|
675
|
+
constructor(points) {
|
|
676
|
+
if (points.length !== 4) {
|
|
551
677
|
throw new Error("Rectangle must be defined by exactly 4 points");
|
|
552
|
-
|
|
678
|
+
}
|
|
679
|
+
this.points = points;
|
|
553
680
|
}
|
|
554
681
|
/**
|
|
555
682
|
* 判断线段是否与矩形相交
|
|
556
683
|
* @param line 线段
|
|
557
684
|
* @returns 是否与矩形相交
|
|
558
685
|
*/
|
|
559
|
-
intersectLineSegment(
|
|
560
|
-
if (
|
|
686
|
+
intersectLineSegment(line) {
|
|
687
|
+
if (line.points.length !== 2) {
|
|
561
688
|
throw new Error("LineSegment must have exactly 2 points");
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
689
|
+
}
|
|
690
|
+
const [p1, p2] = line.points;
|
|
691
|
+
const doIntersect = (l1p1, l1p2, l2p1, l2p2) => {
|
|
692
|
+
const orientation = (p, q, r) => {
|
|
693
|
+
const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
694
|
+
if (val === 0) return 0;
|
|
695
|
+
return val > 0 ? 1 : 2;
|
|
696
|
+
};
|
|
697
|
+
const onSegment = (p, q, r) => {
|
|
698
|
+
return Math.min(p.x, r.x) <= q.x && q.x <= Math.max(p.x, r.x) && Math.min(p.y, r.y) <= q.y && q.y <= Math.max(p.y, r.y);
|
|
699
|
+
};
|
|
700
|
+
const o1 = orientation(l1p1, l1p2, l2p1);
|
|
701
|
+
const o2 = orientation(l1p1, l1p2, l2p2);
|
|
702
|
+
const o3 = orientation(l2p1, l2p2, l1p1);
|
|
703
|
+
const o4 = orientation(l2p1, l2p2, l1p2);
|
|
704
|
+
if (o1 !== o2 && o3 !== o4) return true;
|
|
705
|
+
if (o1 === 0 && onSegment(l1p1, l2p1, l1p2)) return true;
|
|
706
|
+
if (o2 === 0 && onSegment(l1p1, l2p2, l1p2)) return true;
|
|
707
|
+
if (o3 === 0 && onSegment(l2p1, l1p1, l2p2)) return true;
|
|
708
|
+
if (o4 === 0 && onSegment(l2p1, l1p2, l2p2)) return true;
|
|
709
|
+
return false;
|
|
568
710
|
};
|
|
569
|
-
for (let
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
711
|
+
for (let i = 0; i < 4; i++) {
|
|
712
|
+
const rectP1 = this.points[i];
|
|
713
|
+
const rectP2 = this.points[(i + 1) % 4];
|
|
714
|
+
if (doIntersect(p1, p2, rectP1, rectP2)) {
|
|
715
|
+
return true;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (this.containsLineSegment(line)) {
|
|
719
|
+
return true;
|
|
573
720
|
}
|
|
574
|
-
return
|
|
721
|
+
return false;
|
|
575
722
|
}
|
|
576
723
|
/**
|
|
577
724
|
* 判断线段是否完全位于矩形内部
|
|
578
725
|
* @param line 线段
|
|
579
726
|
* @returns 是否完全在矩形内部
|
|
580
727
|
*/
|
|
581
|
-
containsLineSegment(
|
|
582
|
-
if (
|
|
728
|
+
containsLineSegment(line) {
|
|
729
|
+
if (line.points.length !== 2) {
|
|
583
730
|
throw new Error("LineSegment must have exactly 2 points");
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
731
|
+
}
|
|
732
|
+
const isPointInRectangle = (point) => {
|
|
733
|
+
let sign = 0;
|
|
734
|
+
for (let i = 0; i < 4; i++) {
|
|
735
|
+
const p1 = this.points[i];
|
|
736
|
+
const p2 = this.points[(i + 1) % 4];
|
|
737
|
+
const edge = { x: p2.x - p1.x, y: p2.y - p1.y };
|
|
738
|
+
const toPoint = { x: point.x - p1.x, y: point.y - p1.y };
|
|
739
|
+
const cross = edge.x * toPoint.y - edge.y * toPoint.x;
|
|
740
|
+
if (cross === 0) {
|
|
741
|
+
const t = edge.x !== 0 ? (point.x - p1.x) / edge.x : (point.y - p1.y) / edge.y;
|
|
742
|
+
if (t >= 0 && t <= 1) return true;
|
|
591
743
|
} else {
|
|
592
|
-
const
|
|
593
|
-
if (
|
|
744
|
+
const currentSign = cross > 0 ? 1 : -1;
|
|
745
|
+
if (sign === 0) sign = currentSign;
|
|
746
|
+
if (sign !== currentSign) return false;
|
|
594
747
|
}
|
|
595
748
|
}
|
|
596
|
-
return
|
|
749
|
+
return true;
|
|
597
750
|
};
|
|
598
|
-
return
|
|
751
|
+
return isPointInRectangle(line.points[0]) && isPointInRectangle(line.points[1]);
|
|
599
752
|
}
|
|
600
753
|
/**
|
|
601
754
|
* 判断矩形是否与矩形相交
|
|
@@ -603,53 +756,64 @@ class C {
|
|
|
603
756
|
* @param rectangle 矩形
|
|
604
757
|
* @returns 是否与矩形相交
|
|
605
758
|
*/
|
|
606
|
-
intersectRectangle(
|
|
607
|
-
const
|
|
608
|
-
for (let
|
|
609
|
-
const
|
|
610
|
-
|
|
759
|
+
intersectRectangle(rectangle) {
|
|
760
|
+
const axes = [];
|
|
761
|
+
for (let i = 0; i < 4; i++) {
|
|
762
|
+
const p1 = this.points[i];
|
|
763
|
+
const p2 = this.points[(i + 1) % 4];
|
|
764
|
+
axes.push(p1.normal(p2));
|
|
611
765
|
}
|
|
612
|
-
for (let
|
|
613
|
-
const
|
|
614
|
-
|
|
766
|
+
for (let i = 0; i < 4; i++) {
|
|
767
|
+
const p1 = rectangle.points[i];
|
|
768
|
+
const p2 = rectangle.points[(i + 1) % 4];
|
|
769
|
+
axes.push(p1.normal(p2));
|
|
615
770
|
}
|
|
616
|
-
function
|
|
617
|
-
const
|
|
618
|
-
return [Math.min(...
|
|
771
|
+
function projectRectangle(rect, axis) {
|
|
772
|
+
const projections = rect.points.map((point) => point.dot(axis));
|
|
773
|
+
return [Math.min(...projections), Math.max(...projections)];
|
|
619
774
|
}
|
|
620
|
-
function
|
|
621
|
-
return
|
|
775
|
+
function isProjectionOverlap(proj1, proj2) {
|
|
776
|
+
return proj1[0] < proj2[1] && proj2[0] < proj1[1];
|
|
622
777
|
}
|
|
623
|
-
for (const
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
778
|
+
for (const axis of axes) {
|
|
779
|
+
const proj1 = projectRectangle(this, axis);
|
|
780
|
+
const proj2 = projectRectangle(rectangle, axis);
|
|
781
|
+
if (!isProjectionOverlap(proj1, proj2)) {
|
|
782
|
+
return false;
|
|
783
|
+
}
|
|
627
784
|
}
|
|
628
|
-
return
|
|
785
|
+
return true;
|
|
629
786
|
}
|
|
630
787
|
/**
|
|
631
788
|
* 判断点是否完全位于矩形内部
|
|
632
789
|
* @param point
|
|
633
790
|
*/
|
|
634
|
-
containsPoint(
|
|
635
|
-
let
|
|
791
|
+
containsPoint(point) {
|
|
792
|
+
let positiveCount = 0;
|
|
793
|
+
let negativeCount = 0;
|
|
636
794
|
for (let i = 0; i < 4; i++) {
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
795
|
+
const p1 = this.points[i];
|
|
796
|
+
const p2 = this.points[(i + 1) % 4];
|
|
797
|
+
const cross = (p2.x - p1.x) * (point.y - p1.y) - (p2.y - p1.y) * (point.x - p1.x);
|
|
798
|
+
if (cross > 0) positiveCount++;
|
|
799
|
+
else if (cross < 0) negativeCount++;
|
|
800
|
+
else return false;
|
|
641
801
|
}
|
|
642
|
-
return
|
|
802
|
+
return positiveCount === 4 || negativeCount === 4;
|
|
643
803
|
}
|
|
644
804
|
/**
|
|
645
805
|
*
|
|
646
806
|
* @returns
|
|
647
807
|
*/
|
|
648
808
|
toBox() {
|
|
649
|
-
let
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
809
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
810
|
+
this.points.forEach((p) => {
|
|
811
|
+
maxX = Math.max(p.x, maxX);
|
|
812
|
+
minX = Math.min(p.x, minX);
|
|
813
|
+
maxY = Math.max(p.x, maxY);
|
|
814
|
+
minY = Math.min(p.x, minY);
|
|
815
|
+
});
|
|
816
|
+
return new Box2(minX, maxX, minY, maxY);
|
|
653
817
|
}
|
|
654
818
|
/**
|
|
655
819
|
*
|
|
@@ -657,22 +821,24 @@ class C {
|
|
|
657
821
|
* @param width
|
|
658
822
|
* @returns
|
|
659
823
|
*/
|
|
660
|
-
static fromByLineSegment(
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
new
|
|
666
|
-
new
|
|
824
|
+
static fromByLineSegment(line, width = 0.1, horizontal = false, hScale = 0.5) {
|
|
825
|
+
const p1 = line.points[0], p2 = line.points[1], normal = p2.normal(p1), pDirect = horizontal ? p2.direction(p1).mutiplyScalar(width * hScale) : Point.zero(), nDirect = horizontal ? p1.direction(p2).mutiplyScalar(width * hScale) : Point.zero();
|
|
826
|
+
const offsetX = normal.x * width * 0.5;
|
|
827
|
+
const offsetY = normal.y * width * 0.5;
|
|
828
|
+
return new Rectangle([
|
|
829
|
+
new Point(p1.x + offsetX, p1.y + offsetY).add(nDirect),
|
|
830
|
+
new Point(p2.x + offsetX, p2.y + offsetY).add(pDirect),
|
|
831
|
+
new Point(p2.x - offsetX, p2.y - offsetY).add(pDirect),
|
|
832
|
+
new Point(p1.x - offsetX, p1.y - offsetY).add(nDirect)
|
|
667
833
|
]);
|
|
668
834
|
}
|
|
669
835
|
}
|
|
670
|
-
class
|
|
671
|
-
points = [new
|
|
836
|
+
class LineSegment {
|
|
837
|
+
points = [new Point(), new Point()];
|
|
672
838
|
userData = {};
|
|
673
839
|
line;
|
|
674
840
|
get center() {
|
|
675
|
-
return new
|
|
841
|
+
return new Point(
|
|
676
842
|
this.points[0].x + (this.points[1].x - this.points[0].x) * 0.5,
|
|
677
843
|
this.points[0].y + (this.points[1].y - this.points[0].y) * 0.5
|
|
678
844
|
);
|
|
@@ -683,60 +849,71 @@ class b {
|
|
|
683
849
|
get end() {
|
|
684
850
|
return this.points[1];
|
|
685
851
|
}
|
|
686
|
-
constructor(
|
|
687
|
-
this.points = [
|
|
688
|
-
}
|
|
689
|
-
set(t, e) {
|
|
690
|
-
this.start.copy(t), this.end.copy(e);
|
|
852
|
+
constructor(p1 = new Point(), p2 = new Point()) {
|
|
853
|
+
this.points = [p1, p2];
|
|
691
854
|
}
|
|
692
855
|
/** 膨胀
|
|
693
856
|
* @description 向线段的两个端点分别膨胀 width
|
|
694
857
|
* @param width
|
|
695
858
|
*/
|
|
696
|
-
expansion(
|
|
697
|
-
const
|
|
698
|
-
|
|
859
|
+
expansion(width, direction = "all") {
|
|
860
|
+
const step = this.direction().multiplyScalar(width);
|
|
861
|
+
if (direction === "end" || direction === "all") this.end.add(step);
|
|
862
|
+
if (direction === "start" || direction === "all") this.start.add(step.multiplyScalar(-1));
|
|
863
|
+
return this;
|
|
699
864
|
}
|
|
700
865
|
/** 向前
|
|
701
866
|
* @description 向前移动 width
|
|
702
867
|
* @param width
|
|
703
868
|
*/
|
|
704
|
-
forward(
|
|
705
|
-
const
|
|
706
|
-
|
|
869
|
+
forward(width) {
|
|
870
|
+
const step = this.direction().multiplyScalar(width);
|
|
871
|
+
this.start.add(step);
|
|
872
|
+
this.end.add(step);
|
|
873
|
+
return this;
|
|
707
874
|
}
|
|
708
875
|
/** 向前
|
|
709
876
|
* @description 向前移动 width
|
|
710
877
|
* @param width
|
|
711
878
|
*/
|
|
712
|
-
backward(
|
|
713
|
-
const
|
|
714
|
-
|
|
879
|
+
backward(width) {
|
|
880
|
+
const step = this.direction().multiplyScalar(-width);
|
|
881
|
+
this.start.add(step);
|
|
882
|
+
this.end.add(step);
|
|
883
|
+
return this;
|
|
715
884
|
}
|
|
716
885
|
/**
|
|
717
886
|
* 向指定方向平移
|
|
718
887
|
* @param direct
|
|
719
888
|
* @param size
|
|
720
889
|
*/
|
|
721
|
-
directionMove(
|
|
722
|
-
const
|
|
723
|
-
|
|
890
|
+
directionMove(direct, size) {
|
|
891
|
+
const step = direct.clone().multiplyScalar(size);
|
|
892
|
+
this.start.add(step);
|
|
893
|
+
this.end.add(step);
|
|
894
|
+
return this;
|
|
724
895
|
}
|
|
725
896
|
/** 膨胀为矩形
|
|
726
897
|
*
|
|
727
898
|
* @param width
|
|
728
899
|
* @returns {Rectangle}
|
|
729
900
|
*/
|
|
730
|
-
expandToRectangle(
|
|
731
|
-
const
|
|
901
|
+
expandToRectangle(width = 0.1, direct = "all") {
|
|
902
|
+
const p1 = this.start, p2 = this.end;
|
|
903
|
+
const normal = p2.normal(p1);
|
|
904
|
+
const pDirect = direct === "bothSides" ? Point.zero() : p2.direction(p1).mutiplyScalar(width * 0.5);
|
|
905
|
+
const nDirect = direct === "bothSides" ? Point.zero() : p1.direction(p2).mutiplyScalar(width * 0.5);
|
|
906
|
+
const offsetX = normal.x * width * 0.5;
|
|
907
|
+
const offsetY = normal.y * width * 0.5;
|
|
908
|
+
const point = [
|
|
732
909
|
// 第一条线
|
|
733
|
-
new
|
|
734
|
-
new
|
|
910
|
+
new Point(p1.x + offsetX, p1.y + offsetY).add(nDirect),
|
|
911
|
+
new Point(p2.x + offsetX, p2.y + offsetY).add(pDirect),
|
|
735
912
|
// 第二条线
|
|
736
|
-
new
|
|
737
|
-
new
|
|
913
|
+
new Point(p1.x - offsetX, p1.y - offsetY).add(nDirect),
|
|
914
|
+
new Point(p2.x - offsetX, p2.y - offsetY).add(pDirect)
|
|
738
915
|
];
|
|
739
|
-
return new
|
|
916
|
+
return new Rectangle([0, 1, 3, 2].map((i) => point[i]));
|
|
740
917
|
}
|
|
741
918
|
/**
|
|
742
919
|
* 计算线段的长度
|
|
@@ -770,27 +947,50 @@ class b {
|
|
|
770
947
|
* @param line 要投影的线段
|
|
771
948
|
* @returns 投影并裁剪后的线段
|
|
772
949
|
*/
|
|
773
|
-
projectLineSegment(
|
|
774
|
-
if (
|
|
950
|
+
projectLineSegment(line) {
|
|
951
|
+
if (line.points.length !== 2 || this.points.length !== 2) {
|
|
775
952
|
throw new Error("每条线段必须由两个点定义");
|
|
776
|
-
|
|
777
|
-
|
|
953
|
+
}
|
|
954
|
+
const [p1, p2] = line.points;
|
|
955
|
+
const [q1, q2] = this.points;
|
|
956
|
+
const dir = new Point(q2.x - q1.x, q2.y - q1.y);
|
|
957
|
+
if (dir.x === 0 && dir.y === 0) {
|
|
778
958
|
throw new Error("投影目标线段的两个点不能重合");
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
959
|
+
}
|
|
960
|
+
const projectPoint = (point) => {
|
|
961
|
+
const pq = new Point(point.x - q1.x, point.y - q1.y);
|
|
962
|
+
const dirLengthSquared = dir.x * dir.x + dir.y * dir.y;
|
|
963
|
+
const dotProduct = pq.x * dir.x + pq.y * dir.y;
|
|
964
|
+
const t = dotProduct / dirLengthSquared;
|
|
965
|
+
const projX = q1.x + t * dir.x;
|
|
966
|
+
const projY = q1.y + t * dir.y;
|
|
967
|
+
return new Point(projX, projY);
|
|
782
968
|
};
|
|
783
|
-
let
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
969
|
+
let projP1 = projectPoint(p1);
|
|
970
|
+
let projP2 = projectPoint(p2);
|
|
971
|
+
const getT = (point) => {
|
|
972
|
+
const pq = new Point(point.x - q1.x, point.y - q1.y);
|
|
973
|
+
const dirLengthSquared = dir.x * dir.x + dir.y * dir.y;
|
|
974
|
+
return (pq.x * dir.x + pq.y * dir.y) / dirLengthSquared;
|
|
787
975
|
};
|
|
788
|
-
let
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
976
|
+
let t1 = getT(projP1);
|
|
977
|
+
let t2 = getT(projP2);
|
|
978
|
+
const clampPoint = (t) => {
|
|
979
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
980
|
+
const x = q1.x + clampedT * dir.x;
|
|
981
|
+
const y = q1.y + clampedT * dir.y;
|
|
982
|
+
return new Point(x, y);
|
|
792
983
|
};
|
|
793
|
-
|
|
984
|
+
if (t1 < 0 || t1 > 1) {
|
|
985
|
+
projP1 = clampPoint(t1);
|
|
986
|
+
}
|
|
987
|
+
if (t2 < 0 || t2 > 1) {
|
|
988
|
+
projP2 = clampPoint(t2);
|
|
989
|
+
}
|
|
990
|
+
if (projP1.x === projP2.x && projP1.y === projP2.y) {
|
|
991
|
+
return new LineSegment(projP1, projP1);
|
|
992
|
+
}
|
|
993
|
+
return new LineSegment(projP1, projP2);
|
|
794
994
|
}
|
|
795
995
|
/**
|
|
796
996
|
* 计算一条线段在另一条直线上的投影
|
|
@@ -798,106 +998,155 @@ class b {
|
|
|
798
998
|
* @param clip 是否裁剪超出目标线段的部分,默认裁剪
|
|
799
999
|
* @returns 投影并裁剪后的线段
|
|
800
1000
|
*/
|
|
801
|
-
projectPoint(
|
|
802
|
-
const [
|
|
803
|
-
|
|
1001
|
+
projectPoint(p1, clip = true) {
|
|
1002
|
+
const [q1, q2] = this.points;
|
|
1003
|
+
const dir = new Point(q2.x - q1.x, q2.y - q1.y);
|
|
1004
|
+
if (dir.x === 0 && dir.y === 0) {
|
|
804
1005
|
throw new Error("投影目标线段的两个点不能重合");
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1006
|
+
}
|
|
1007
|
+
const projectPoint = (point) => {
|
|
1008
|
+
const pq = new Point(point.x - q1.x, point.y - q1.y);
|
|
1009
|
+
const dirLengthSquared = dir.x * dir.x + dir.y * dir.y;
|
|
1010
|
+
const dotProduct = pq.x * dir.x + pq.y * dir.y;
|
|
1011
|
+
const t = dotProduct / dirLengthSquared;
|
|
1012
|
+
const projX = q1.x + t * dir.x;
|
|
1013
|
+
const projY = q1.y + t * dir.y;
|
|
1014
|
+
return new Point(projX, projY);
|
|
1015
|
+
};
|
|
1016
|
+
let projP1 = projectPoint(p1);
|
|
1017
|
+
if (!clip) return projP1;
|
|
1018
|
+
const getT = (point) => {
|
|
1019
|
+
const pq = new Point(point.x - q1.x, point.y - q1.y);
|
|
1020
|
+
const dirLengthSquared = dir.x * dir.x + dir.y * dir.y;
|
|
1021
|
+
return (pq.x * dir.x + pq.y * dir.y) / dirLengthSquared;
|
|
1022
|
+
};
|
|
1023
|
+
let t1 = getT(projP1);
|
|
1024
|
+
if (t1 < 0 || t1 > 1) {
|
|
1025
|
+
return null;
|
|
1026
|
+
}
|
|
1027
|
+
return projP1;
|
|
815
1028
|
}
|
|
816
1029
|
/**
|
|
817
1030
|
* 判断线段是否与另一条线段相交(包含共用端点或部分重合的情况)
|
|
818
1031
|
* @param line
|
|
819
1032
|
*/
|
|
820
|
-
intersectLineSegment(
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
1033
|
+
intersectLineSegment(line) {
|
|
1034
|
+
const p1 = this.start;
|
|
1035
|
+
const p2 = this.end;
|
|
1036
|
+
const p3 = line.start;
|
|
1037
|
+
const p4 = line.end;
|
|
1038
|
+
function crossProduct(a, b, c) {
|
|
1039
|
+
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
1040
|
+
}
|
|
1041
|
+
function isPointOnSegment(pt, segStart, segEnd) {
|
|
1042
|
+
return Math.min(segStart.x, segEnd.x) - 1e-10 <= pt.x && pt.x <= Math.max(segStart.x, segEnd.x) + 1e-10 && Math.min(segStart.y, segEnd.y) - 1e-10 <= pt.y && pt.y <= Math.max(segStart.y, segEnd.y) + 1e-10;
|
|
1043
|
+
}
|
|
1044
|
+
const d1 = crossProduct(p1, p2, p3);
|
|
1045
|
+
const d2 = crossProduct(p1, p2, p4);
|
|
1046
|
+
const d3 = crossProduct(p3, p4, p1);
|
|
1047
|
+
const d4 = crossProduct(p3, p4, p2);
|
|
1048
|
+
if (d1 * d2 < 0 && d3 * d4 < 0) {
|
|
1049
|
+
return true;
|
|
1050
|
+
}
|
|
1051
|
+
if (Math.abs(d1) < 1e-10 && isPointOnSegment(p3, p1, p2)) {
|
|
1052
|
+
return true;
|
|
824
1053
|
}
|
|
825
|
-
|
|
826
|
-
return
|
|
1054
|
+
if (Math.abs(d2) < 1e-10 && isPointOnSegment(p4, p1, p2)) {
|
|
1055
|
+
return true;
|
|
827
1056
|
}
|
|
828
|
-
|
|
829
|
-
|
|
1057
|
+
if (Math.abs(d3) < 1e-10 && isPointOnSegment(p1, p3, p4)) {
|
|
1058
|
+
return true;
|
|
1059
|
+
}
|
|
1060
|
+
if (Math.abs(d4) < 1e-10 && isPointOnSegment(p2, p3, p4)) {
|
|
1061
|
+
return true;
|
|
1062
|
+
}
|
|
1063
|
+
return false;
|
|
830
1064
|
}
|
|
831
1065
|
/**
|
|
832
1066
|
* 获取交点
|
|
833
1067
|
* @param line
|
|
834
1068
|
* @returns
|
|
835
1069
|
*/
|
|
836
|
-
getIntersection(
|
|
837
|
-
const
|
|
838
|
-
|
|
1070
|
+
getIntersection(line) {
|
|
1071
|
+
const p1 = this.start;
|
|
1072
|
+
const p2 = this.end;
|
|
1073
|
+
const p3 = line.start;
|
|
1074
|
+
const p4 = line.end;
|
|
1075
|
+
const denom = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x);
|
|
1076
|
+
if (Math.abs(denom) < 1e-10) {
|
|
839
1077
|
return null;
|
|
840
|
-
|
|
841
|
-
|
|
1078
|
+
}
|
|
1079
|
+
const t = ((p1.x - p3.x) * (p3.y - p4.y) - (p1.y - p3.y) * (p3.x - p4.x)) / denom;
|
|
1080
|
+
const x = p1.x + t * (p2.x - p1.x);
|
|
1081
|
+
const y = p1.y + t * (p2.y - p1.y);
|
|
1082
|
+
return new Point(x, y);
|
|
842
1083
|
}
|
|
843
1084
|
/**
|
|
844
1085
|
* 获取两条线段夹角
|
|
845
1086
|
* @param line
|
|
846
1087
|
*/
|
|
847
|
-
includedAngle(
|
|
848
|
-
const
|
|
849
|
-
return
|
|
1088
|
+
includedAngle(line) {
|
|
1089
|
+
const d1 = this.direction(), d2 = line.direction();
|
|
1090
|
+
return d1.angleBetween(d2) / (Math.PI / 180);
|
|
850
1091
|
}
|
|
851
1092
|
/**
|
|
852
1093
|
* 两条线段方向是否一致
|
|
853
1094
|
* @param line
|
|
854
1095
|
*/
|
|
855
|
-
directionEqual(
|
|
856
|
-
return this.includedAngle(
|
|
1096
|
+
directionEqual(line, errAngle = 0.1) {
|
|
1097
|
+
return this.includedAngle(line) < errAngle;
|
|
857
1098
|
}
|
|
858
1099
|
/**
|
|
859
1100
|
* 两条线段方向相反否一致
|
|
860
1101
|
* @param line
|
|
861
1102
|
*/
|
|
862
|
-
directionOpposite(
|
|
863
|
-
return 180 - this.includedAngle(
|
|
1103
|
+
directionOpposite(line, errAngle = 0.1) {
|
|
1104
|
+
return 180 - this.includedAngle(line) < errAngle;
|
|
864
1105
|
}
|
|
865
1106
|
/**
|
|
866
1107
|
* 判断两条线是否平行
|
|
867
1108
|
* @param line
|
|
868
1109
|
*/
|
|
869
|
-
isParallel(
|
|
870
|
-
const
|
|
871
|
-
return
|
|
1110
|
+
isParallel(line, errAngle = 4) {
|
|
1111
|
+
const angle = this.includedAngle(line);
|
|
1112
|
+
return angle < errAngle || angle > 180 - errAngle;
|
|
872
1113
|
}
|
|
873
1114
|
/**
|
|
874
1115
|
* 判断两条直线是否重合
|
|
875
1116
|
* @param line
|
|
876
1117
|
* @returns
|
|
877
1118
|
*/
|
|
878
|
-
areLinesCoincident(
|
|
879
|
-
const
|
|
880
|
-
|
|
1119
|
+
areLinesCoincident(line) {
|
|
1120
|
+
const p1 = this.start, p2 = this.end, p3 = line.start, p4 = line.end;
|
|
1121
|
+
const m1 = (p2.y - p1.y) / (p2.x - p1.x);
|
|
1122
|
+
const b1 = p1.y - m1 * p1.x;
|
|
1123
|
+
const m2 = (p4.y - p3.y) / (p4.x - p3.x);
|
|
1124
|
+
const b2 = p3.y - m2 * p3.x;
|
|
1125
|
+
if (!isFinite(m1) && !isFinite(m2)) {
|
|
1126
|
+
return p1.x === p3.x && p2.x === p3.x;
|
|
1127
|
+
}
|
|
1128
|
+
return Math.abs(m1 - m2) < 1e-3 && Math.abs(b1 - b2) < 1e-3;
|
|
881
1129
|
}
|
|
882
1130
|
clone() {
|
|
883
|
-
return new
|
|
1131
|
+
return new LineSegment(
|
|
884
1132
|
this.points[0].clone(),
|
|
885
1133
|
this.points[1].clone()
|
|
886
1134
|
);
|
|
887
1135
|
}
|
|
888
1136
|
}
|
|
889
|
-
async function
|
|
890
|
-
if (typeof global
|
|
891
|
-
return require(
|
|
892
|
-
{
|
|
893
|
-
let
|
|
1137
|
+
async function include(path, exportDefault = true) {
|
|
1138
|
+
if (typeof global !== "undefined" && typeof require !== "undefined") {
|
|
1139
|
+
return require(path);
|
|
1140
|
+
} else {
|
|
1141
|
+
let pack = await import(
|
|
894
1142
|
/* @vite-ignore */
|
|
895
|
-
|
|
1143
|
+
path
|
|
896
1144
|
);
|
|
897
|
-
|
|
1145
|
+
if (exportDefault) pack = pack.default;
|
|
1146
|
+
return pack;
|
|
898
1147
|
}
|
|
899
1148
|
}
|
|
900
|
-
const
|
|
1149
|
+
const units = {
|
|
901
1150
|
Unitless: 1,
|
|
902
1151
|
// 无单位,1米 = 1(无单位)
|
|
903
1152
|
Inches: 39.37007874015748,
|
|
@@ -941,27 +1190,31 @@ const nt = {
|
|
|
941
1190
|
Parsecs: 3240779289666404e-32
|
|
942
1191
|
// 秒差距,1米 ≈ 0.00000000000000003240779289666404秒差距
|
|
943
1192
|
};
|
|
944
|
-
function
|
|
945
|
-
const
|
|
946
|
-
for (let
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1193
|
+
function pathToLines(path) {
|
|
1194
|
+
const lineSegments = [];
|
|
1195
|
+
for (let i = 0; i < path.length; i++) {
|
|
1196
|
+
lineSegments.push(new LineSegment(
|
|
1197
|
+
path[i].clone(),
|
|
1198
|
+
path[(i + 1) % path.length].clone()
|
|
950
1199
|
));
|
|
951
|
-
|
|
1200
|
+
}
|
|
1201
|
+
return lineSegments;
|
|
952
1202
|
}
|
|
953
|
-
function
|
|
954
|
-
return
|
|
1203
|
+
function linesToPath(lineSegments) {
|
|
1204
|
+
return lineSegments.flatMap((line, index2) => {
|
|
1205
|
+
if (index2 === lineSegments.length - 1) [...line.points, lineSegments[0].points[0]];
|
|
1206
|
+
return [line.points[0]];
|
|
1207
|
+
});
|
|
955
1208
|
}
|
|
956
|
-
class
|
|
1209
|
+
class Dxf extends Component {
|
|
957
1210
|
static name = "Dxf";
|
|
958
1211
|
shortLine = 0.04;
|
|
959
1212
|
width = 0.04;
|
|
960
1213
|
scale = 1;
|
|
961
1214
|
originalData = [];
|
|
962
1215
|
data = [];
|
|
963
|
-
originalBox = new
|
|
964
|
-
box = new
|
|
1216
|
+
originalBox = new Box2(0, 0, 0, 0);
|
|
1217
|
+
box = new Box2(0, 0, 0, 0);
|
|
965
1218
|
pointsGroups = [];
|
|
966
1219
|
wallsGroup = [];
|
|
967
1220
|
doors = [];
|
|
@@ -990,8 +1243,11 @@ class I extends X {
|
|
|
990
1243
|
* @param width 墙体宽度
|
|
991
1244
|
* @param scale 缩放比例
|
|
992
1245
|
*/
|
|
993
|
-
constructor(
|
|
994
|
-
super()
|
|
1246
|
+
constructor(width = 0.04, scale = 1) {
|
|
1247
|
+
super();
|
|
1248
|
+
this.width = width;
|
|
1249
|
+
this.scale = scale;
|
|
1250
|
+
this.shortLine = width * 0.4;
|
|
995
1251
|
}
|
|
996
1252
|
/**
|
|
997
1253
|
* 设置
|
|
@@ -999,36 +1255,52 @@ class I extends X {
|
|
|
999
1255
|
* @param width
|
|
1000
1256
|
* @param scale
|
|
1001
1257
|
*/
|
|
1002
|
-
async set(
|
|
1003
|
-
if (typeof
|
|
1004
|
-
if (typeof global
|
|
1005
|
-
const
|
|
1258
|
+
async set(data, width = this.width, scale = this.scale) {
|
|
1259
|
+
if (typeof data === "string") {
|
|
1260
|
+
if (typeof global !== "undefined") {
|
|
1261
|
+
const packageName = "fs";
|
|
1262
|
+
const { default: fs } = await import(
|
|
1006
1263
|
/* @vite-ignore */
|
|
1007
|
-
|
|
1008
|
-
)
|
|
1009
|
-
|
|
1010
|
-
|
|
1264
|
+
packageName
|
|
1265
|
+
);
|
|
1266
|
+
const buffer = fs.readFileSync(data);
|
|
1267
|
+
const json = JSON.parse(buffer.toString("utf-8"));
|
|
1268
|
+
return this.set(json, width, scale);
|
|
1269
|
+
} else {
|
|
1011
1270
|
throw new Error("非node环境不允许使用路径");
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
this.
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
this.scale = scale;
|
|
1274
|
+
this.width = width;
|
|
1275
|
+
this.originalData = data;
|
|
1276
|
+
this.lineSegments.length = 0;
|
|
1277
|
+
const zList = [];
|
|
1278
|
+
this.data = data.map(({ start, end, insetionArr, isDoor = false, isWindow, drawDoorData }, index2) => {
|
|
1279
|
+
zList.push(start.z ?? 0, end.z ?? 0);
|
|
1280
|
+
const lineSegment = new LineSegment(
|
|
1281
|
+
Point.from(start).mutiplyScalar(scale),
|
|
1282
|
+
Point.from(end).mutiplyScalar(scale)
|
|
1019
1283
|
);
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1284
|
+
lineSegment.userData = { isDoor, isWindow, drawDoorData };
|
|
1285
|
+
this.lineSegments.push(lineSegment);
|
|
1286
|
+
return [
|
|
1287
|
+
lineSegment.points[0],
|
|
1288
|
+
lineSegment.points[1],
|
|
1289
|
+
(insetionArr ?? []).map((i) => i.index),
|
|
1290
|
+
isDoor,
|
|
1291
|
+
index2
|
|
1026
1292
|
];
|
|
1027
|
-
})
|
|
1293
|
+
});
|
|
1294
|
+
this.originalZAverage = zList.reduce((count, num) => count + num, 0) / zList.length;
|
|
1295
|
+
this.computedOriginalSize(data, this.originalBox);
|
|
1296
|
+
this.dispatchEvent({
|
|
1028
1297
|
type: "setDta",
|
|
1029
1298
|
originalData: this.originalData,
|
|
1030
1299
|
data: this.data
|
|
1031
|
-
})
|
|
1300
|
+
});
|
|
1301
|
+
this.createGroups();
|
|
1302
|
+
this.computedSize();
|
|
1303
|
+
this.dispatchEvent({
|
|
1032
1304
|
type: "createGroup",
|
|
1033
1305
|
groups: this.pointsGroups
|
|
1034
1306
|
});
|
|
@@ -1038,20 +1310,32 @@ class I extends X {
|
|
|
1038
1310
|
* @returns
|
|
1039
1311
|
*/
|
|
1040
1312
|
createGroups() {
|
|
1041
|
-
const
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1313
|
+
const groups = [], visited = /* @__PURE__ */ new Set(), doorSet = /* @__PURE__ */ new Set(), doorVisitedRecord = /* @__PURE__ */ new Map();
|
|
1314
|
+
const dfs = (index2, group, preIndex = -1) => {
|
|
1315
|
+
const [start, end, insetionArr, isDoor] = this.data[index2];
|
|
1316
|
+
visited.add(index2);
|
|
1317
|
+
if (isDoor) {
|
|
1318
|
+
if (!doorVisitedRecord.has(index2)) doorVisitedRecord.set(index2, []);
|
|
1319
|
+
doorVisitedRecord.get(index2)?.push(preIndex);
|
|
1320
|
+
return doorSet.add(this.data[index2]);
|
|
1321
|
+
}
|
|
1322
|
+
group.push([start, end]);
|
|
1323
|
+
insetionArr.forEach((i) => {
|
|
1324
|
+
if (!visited.has(i)) {
|
|
1325
|
+
dfs(i, group, index2);
|
|
1326
|
+
}
|
|
1047
1327
|
});
|
|
1048
1328
|
};
|
|
1049
|
-
|
|
1050
|
-
if (!
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1329
|
+
this.data.forEach((_, index2) => {
|
|
1330
|
+
if (!visited.has(index2)) {
|
|
1331
|
+
const group = [];
|
|
1332
|
+
dfs(index2, group);
|
|
1333
|
+
groups.push(group);
|
|
1053
1334
|
}
|
|
1054
|
-
})
|
|
1335
|
+
});
|
|
1336
|
+
this.doors = [...doorSet];
|
|
1337
|
+
this.pointsGroups = groups;
|
|
1338
|
+
return groups;
|
|
1055
1339
|
}
|
|
1056
1340
|
/** 计算当前墙体数据的边界框
|
|
1057
1341
|
* @description 根据分组数据pointsGroups,计算包围盒, pointsGroups数据为缩放后数据。
|
|
@@ -1059,163 +1343,219 @@ class I extends X {
|
|
|
1059
1343
|
* @returns
|
|
1060
1344
|
*/
|
|
1061
1345
|
computedSize() {
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1346
|
+
const xArr = this.pointsGroups.flatMap((points) => points.flatMap((p) => [p[0].x, p[1].x]));
|
|
1347
|
+
const yArr = this.pointsGroups.flatMap((points) => points.flatMap((p) => [p[0].y, p[1].y]));
|
|
1348
|
+
const minX = Math.min(...xArr);
|
|
1349
|
+
const minY = Math.min(...yArr);
|
|
1350
|
+
const maxX = Math.max(...xArr);
|
|
1351
|
+
const maxY = Math.max(...yArr);
|
|
1352
|
+
this.box.set(minX, minY, maxX, maxY);
|
|
1353
|
+
return this.box;
|
|
1064
1354
|
}
|
|
1065
1355
|
/** 线路拓扑
|
|
1066
1356
|
* @description 处理线路拓扑,使线路有序链接,形成长路径
|
|
1067
1357
|
* @param lines
|
|
1068
1358
|
*/
|
|
1069
|
-
lineTopology(
|
|
1070
|
-
const
|
|
1071
|
-
function
|
|
1072
|
-
const [
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1359
|
+
lineTopology(lines) {
|
|
1360
|
+
const visited = [];
|
|
1361
|
+
function dfs(index2, linePath) {
|
|
1362
|
+
const [_0, a2] = lines[index2];
|
|
1363
|
+
visited[index2] = true;
|
|
1364
|
+
linePath.push(a2);
|
|
1365
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1366
|
+
const [b1, _1] = lines[i];
|
|
1367
|
+
if (!visited[i]) {
|
|
1368
|
+
if (Math.abs(a2.x - b1.x) < 1e-6 && Math.abs(a2.y - b1.y) < 1e-6) {
|
|
1369
|
+
return dfs(i, linePath);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1078
1372
|
}
|
|
1079
1373
|
}
|
|
1080
|
-
const
|
|
1081
|
-
for (let
|
|
1082
|
-
if (!
|
|
1083
|
-
const
|
|
1084
|
-
|
|
1374
|
+
const linePaths = [];
|
|
1375
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1376
|
+
if (!visited[i]) {
|
|
1377
|
+
const linePath = [lines[i][0]];
|
|
1378
|
+
dfs(i, linePath);
|
|
1379
|
+
linePaths.push(linePath);
|
|
1085
1380
|
}
|
|
1086
|
-
|
|
1381
|
+
}
|
|
1382
|
+
return linePaths;
|
|
1087
1383
|
}
|
|
1088
1384
|
/** 合并方向相同的线段
|
|
1089
1385
|
* @param lines
|
|
1090
1386
|
* @param errAngle
|
|
1091
1387
|
*/
|
|
1092
|
-
mergeSameDirectionLine(
|
|
1093
|
-
if (
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1388
|
+
mergeSameDirectionLine(lines, errAngle = 3) {
|
|
1389
|
+
if (lines[0].includedAngle(lines[lines.length - 1]) < 0.1) {
|
|
1390
|
+
const line = lines.pop();
|
|
1391
|
+
line.end.copy(lines[0].end);
|
|
1392
|
+
lines.splice(0, 1, line);
|
|
1096
1393
|
}
|
|
1097
|
-
const
|
|
1098
|
-
for (let i = 1; i <
|
|
1099
|
-
const
|
|
1100
|
-
|
|
1394
|
+
const filterLines = [lines[0]];
|
|
1395
|
+
for (let i = 1; i < lines.length; i++) {
|
|
1396
|
+
const line = lines[i];
|
|
1397
|
+
const preLine = lines[i - 1];
|
|
1398
|
+
if (preLine.includedAngle(line) < errAngle) {
|
|
1399
|
+
preLine.end.copy(line.end);
|
|
1400
|
+
} else {
|
|
1401
|
+
filterLines.push(line);
|
|
1402
|
+
}
|
|
1101
1403
|
}
|
|
1102
|
-
return
|
|
1404
|
+
return filterLines;
|
|
1103
1405
|
}
|
|
1104
1406
|
/** etOpenRound 去除毛刺
|
|
1105
1407
|
* @description 检查连续的短线段数量,去除合并后产生的毛刺
|
|
1106
1408
|
*/
|
|
1107
|
-
squareRemoveBurr(
|
|
1108
|
-
if (
|
|
1109
|
-
const
|
|
1110
|
-
for (let
|
|
1111
|
-
const
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1409
|
+
squareRemoveBurr(path) {
|
|
1410
|
+
if (path.length < 3) return path;
|
|
1411
|
+
const filterLines = [path[0]];
|
|
1412
|
+
for (let i = 1; i < path.length; i++) {
|
|
1413
|
+
const prev = path[i - 1];
|
|
1414
|
+
const curr = path[i];
|
|
1415
|
+
const len = prev.distance(curr);
|
|
1416
|
+
if (len < this.width * 0.5) {
|
|
1417
|
+
let count = 0;
|
|
1418
|
+
for (let j = i + 1; j < path.length; j++) {
|
|
1419
|
+
const prev2 = path[j - 1];
|
|
1420
|
+
const curr2 = path[j];
|
|
1421
|
+
const len2 = prev2.distance(curr2);
|
|
1422
|
+
if (len2 < this.width * 0.8) count++;
|
|
1117
1423
|
else break;
|
|
1118
1424
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1425
|
+
if (count === 0 && i + count === path.length - 1) ;
|
|
1426
|
+
else if (i == 1 && count === 1) ;
|
|
1427
|
+
else if (count === 3) {
|
|
1428
|
+
filterLines.push(path[i + 1]);
|
|
1429
|
+
i += count;
|
|
1430
|
+
} else if (count === 5) {
|
|
1431
|
+
filterLines.push(path[i + 2]);
|
|
1432
|
+
i += count;
|
|
1433
|
+
} else {
|
|
1434
|
+
filterLines.push(curr);
|
|
1435
|
+
}
|
|
1436
|
+
} else {
|
|
1437
|
+
filterLines.push(curr);
|
|
1438
|
+
}
|
|
1122
1439
|
}
|
|
1123
|
-
return
|
|
1440
|
+
return filterLines;
|
|
1124
1441
|
}
|
|
1125
1442
|
/**
|
|
1126
1443
|
* 线段矫直, 线段中心突刺
|
|
1127
1444
|
* @description 突变长度小于墙体宽度,该线段可能为突起线段,
|
|
1128
1445
|
* @description 判断后续第2线段与上一条线段是否方向相同,相同就为突刺
|
|
1129
1446
|
*/
|
|
1130
|
-
lineSegmentStraightening(
|
|
1131
|
-
for (let i = 0; i <
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1447
|
+
lineSegmentStraightening(path) {
|
|
1448
|
+
for (let i = 0; i < path.length; i++) {
|
|
1449
|
+
const p1 = path[i];
|
|
1450
|
+
const p2 = path[(i + 1) % path.length];
|
|
1451
|
+
if (p1.distance(p2) > this.shortLine) {
|
|
1452
|
+
path.push(...path.slice(0, i + 1));
|
|
1453
|
+
path.splice(0, i + 1);
|
|
1135
1454
|
break;
|
|
1136
1455
|
}
|
|
1137
1456
|
}
|
|
1138
|
-
const
|
|
1139
|
-
for (let i = 1; i <
|
|
1140
|
-
const
|
|
1141
|
-
|
|
1142
|
-
|
|
1457
|
+
const lines = this.mergeSameDirectionLine(pathToLines(path)), filterLines = [lines[0]];
|
|
1458
|
+
for (let i = 1; i < lines.length; i++) {
|
|
1459
|
+
const line = lines[i];
|
|
1460
|
+
const preLine = lines[(lines.length + i - 1) % lines.length];
|
|
1461
|
+
if (line.length() > this.width * 0.9) {
|
|
1462
|
+
filterLines.push(line);
|
|
1143
1463
|
continue;
|
|
1144
1464
|
}
|
|
1145
|
-
const
|
|
1146
|
-
if (
|
|
1147
|
-
|
|
1465
|
+
const line1 = lines[i + 1];
|
|
1466
|
+
if (line1 && line1.length() > this.width * 0.9) {
|
|
1467
|
+
filterLines.push(line);
|
|
1468
|
+
filterLines.push(line1);
|
|
1469
|
+
i = i + 1;
|
|
1148
1470
|
continue;
|
|
1149
1471
|
}
|
|
1150
|
-
const
|
|
1151
|
-
|
|
1472
|
+
const line2 = lines[i + 2];
|
|
1473
|
+
if (line2 && preLine.includedAngle(line2) < 2) {
|
|
1474
|
+
i = i + 2;
|
|
1475
|
+
filterLines.push(line2);
|
|
1476
|
+
} else filterLines.push(line);
|
|
1152
1477
|
}
|
|
1153
|
-
return
|
|
1478
|
+
return filterLines.length > 2 ? linesToPath(this.mergeSameDirectionLine(filterLines)) : [];
|
|
1154
1479
|
}
|
|
1155
1480
|
/**
|
|
1156
1481
|
* 移除短线段
|
|
1157
1482
|
* @todo 根据线段两端线段长度,选取参照物_|▔▔
|
|
1158
1483
|
* @param path
|
|
1159
1484
|
*/
|
|
1160
|
-
removeShortLine(
|
|
1161
|
-
const
|
|
1162
|
-
for (let
|
|
1163
|
-
const
|
|
1164
|
-
if (
|
|
1165
|
-
|
|
1485
|
+
removeShortLine(path, shortLine = this.shortLine) {
|
|
1486
|
+
const lines = pathToLines(path), filterLines = [], PI_1 = Math.PI / 180;
|
|
1487
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1488
|
+
const line = lines[i], len = line.length();
|
|
1489
|
+
if (len > shortLine || filterLines.length === 0) {
|
|
1490
|
+
filterLines.push(line);
|
|
1166
1491
|
continue;
|
|
1167
1492
|
}
|
|
1168
|
-
let
|
|
1169
|
-
const
|
|
1170
|
-
|
|
1171
|
-
const
|
|
1172
|
-
if (
|
|
1173
|
-
|
|
1174
|
-
else break;
|
|
1493
|
+
let nextline = lines[++i];
|
|
1494
|
+
const preLine = filterLines[filterLines.length - 1], d1 = preLine.direction();
|
|
1495
|
+
while (i < lines.length) {
|
|
1496
|
+
const angle = d1.angleBetween(nextline.direction()) / PI_1;
|
|
1497
|
+
if (nextline.length() <= shortLine || angle < 4 || angle > 180 - 4) {
|
|
1498
|
+
nextline = lines[++i];
|
|
1499
|
+
} else break;
|
|
1175
1500
|
}
|
|
1176
|
-
if (!
|
|
1177
|
-
const
|
|
1178
|
-
if (
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1501
|
+
if (!nextline) continue;
|
|
1502
|
+
const intersectPoint = preLine.getIntersection(nextline);
|
|
1503
|
+
if (intersectPoint) {
|
|
1504
|
+
const p0 = preLine.points[1].clone(), p1 = nextline.points[0].clone();
|
|
1505
|
+
preLine.points[1].copy(intersectPoint);
|
|
1506
|
+
nextline.points[0].copy(intersectPoint);
|
|
1507
|
+
if (preLine.length() < this.width) {
|
|
1508
|
+
preLine.points[1].copy(p0);
|
|
1509
|
+
nextline.points[0].copy(p0);
|
|
1510
|
+
} else if (nextline.length() < this.width) {
|
|
1511
|
+
preLine.points[1].copy(p1);
|
|
1512
|
+
nextline.points[0].copy(p1);
|
|
1513
|
+
}
|
|
1514
|
+
} else {
|
|
1515
|
+
preLine.points[1].copy(nextline.points[0]);
|
|
1516
|
+
}
|
|
1517
|
+
filterLines.push(nextline);
|
|
1518
|
+
}
|
|
1519
|
+
return filterLines.length > 2 ? linesToPath(filterLines) : [];
|
|
1190
1520
|
}
|
|
1191
1521
|
/** 线偏移
|
|
1192
1522
|
* @description 使用 ClipperLib 对每个点组进行线偏移处理,生成具有指定宽度的墙体路径
|
|
1193
1523
|
*/
|
|
1194
|
-
lineOffset(
|
|
1195
|
-
let
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
const
|
|
1199
|
-
|
|
1200
|
-
})
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1524
|
+
lineOffset(endType = Dxf.EndType.etOpenSquare, joinType = Dxf.JoinType.jtMiter, scale = 1e4) {
|
|
1525
|
+
let solutions = new ClipperLib.Paths();
|
|
1526
|
+
const offset = new ClipperLib.ClipperOffset(20, 0.25);
|
|
1527
|
+
this.pointsGroups.forEach((points) => {
|
|
1528
|
+
const linePaths = this.lineTopology(points).map((linePath) => linePath.map((p) => p.clone().mutiplyScalar(scale)));
|
|
1529
|
+
offset.AddPaths(linePaths, joinType, endType);
|
|
1530
|
+
});
|
|
1531
|
+
offset.Execute(solutions, this.width / 2 * scale);
|
|
1532
|
+
this.wallsGroup = solutions.map((ps) => {
|
|
1533
|
+
let path = ps.map((p) => Point.from(p).divisionScalar(scale));
|
|
1534
|
+
path = this.lineSegmentStraightening(path);
|
|
1535
|
+
if (endType == Dxf.EndType.etOpenSquare) path = this.squareRemoveBurr(path);
|
|
1536
|
+
path = this.removeShortLine(path);
|
|
1537
|
+
return path;
|
|
1538
|
+
});
|
|
1539
|
+
this.dispatchEvent({
|
|
1204
1540
|
type: "lineOffset",
|
|
1205
1541
|
wallsGroup: this.wallsGroup
|
|
1206
|
-
})
|
|
1542
|
+
});
|
|
1543
|
+
return this.wallsGroup;
|
|
1207
1544
|
}
|
|
1208
1545
|
/**
|
|
1209
1546
|
* 将点云结构转换为Float32Array
|
|
1210
1547
|
*/
|
|
1211
|
-
to3DArray(
|
|
1212
|
-
const
|
|
1213
|
-
|
|
1214
|
-
for (let
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1548
|
+
to3DArray(scale, z = this.originalZAverage) {
|
|
1549
|
+
const array = [];
|
|
1550
|
+
this.wallsGroup.forEach((points) => {
|
|
1551
|
+
for (let i = 0; i < points.length; i++) {
|
|
1552
|
+
const point1 = points[i];
|
|
1553
|
+
const nextIndex = i === points.length - 1 ? 0 : i + 1;
|
|
1554
|
+
const point2 = points[nextIndex];
|
|
1555
|
+
array.push(point1.X * scale, point1.Y * scale, z, point2.X * scale, point2.Y * scale, z);
|
|
1217
1556
|
}
|
|
1218
|
-
})
|
|
1557
|
+
});
|
|
1558
|
+
return new Float32Array(array);
|
|
1219
1559
|
}
|
|
1220
1560
|
/** 获取角度范围
|
|
1221
1561
|
* @param center
|
|
@@ -1223,140 +1563,196 @@ class I extends X {
|
|
|
1223
1563
|
* @param p2
|
|
1224
1564
|
* @returns
|
|
1225
1565
|
*/
|
|
1226
|
-
getArcAngleRange(
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1566
|
+
getArcAngleRange(center, p1, p2) {
|
|
1567
|
+
const x1 = p1.x - center.x;
|
|
1568
|
+
const y1 = p1.y - center.y;
|
|
1569
|
+
const x2 = p2.x - center.x;
|
|
1570
|
+
const y2 = p2.y - center.y;
|
|
1571
|
+
let angle1 = Math.atan2(y1, x1);
|
|
1572
|
+
let angle2 = Math.atan2(y2, x2);
|
|
1573
|
+
angle1 = angle1 < 0 ? angle1 + 2 * Math.PI : angle1;
|
|
1574
|
+
angle2 = angle2 < 0 ? angle2 + 2 * Math.PI : angle2;
|
|
1575
|
+
let r1, r2;
|
|
1576
|
+
const diff = Math.abs(angle2 - angle1);
|
|
1577
|
+
if (diff <= Math.PI) {
|
|
1578
|
+
r1 = Math.min(angle1, angle2);
|
|
1579
|
+
r2 = Math.max(angle1, angle2);
|
|
1580
|
+
} else {
|
|
1581
|
+
r1 = Math.max(angle1, angle2);
|
|
1582
|
+
r2 = Math.min(angle1, angle2) + 2 * Math.PI;
|
|
1583
|
+
}
|
|
1584
|
+
return [r1 / (Math.PI / 180), r2 / (Math.PI / 180)];
|
|
1232
1585
|
}
|
|
1233
1586
|
/**
|
|
1234
1587
|
* 将点云结构转换为string
|
|
1235
1588
|
*/
|
|
1236
|
-
toDxfString(
|
|
1237
|
-
const
|
|
1238
|
-
|
|
1239
|
-
const
|
|
1240
|
-
function
|
|
1241
|
-
|
|
1242
|
-
}
|
|
1243
|
-
this.wallsGroup.forEach((
|
|
1244
|
-
for (let
|
|
1245
|
-
const
|
|
1246
|
-
i
|
|
1589
|
+
toDxfString(unit = "Millimeters") {
|
|
1590
|
+
const d = new Drawing();
|
|
1591
|
+
d.setUnits("Millimeters");
|
|
1592
|
+
const s = units[unit];
|
|
1593
|
+
function drawLine(p1, p2) {
|
|
1594
|
+
d.drawLine(p1.X * s, p1.Y * s, p2.X * s, p2.Y * s);
|
|
1595
|
+
}
|
|
1596
|
+
this.wallsGroup.forEach((points) => {
|
|
1597
|
+
for (let i = 0; i < points.length; i++) {
|
|
1598
|
+
const point1 = points[i];
|
|
1599
|
+
const nextIndex = i === points.length - 1 ? 0 : i + 1;
|
|
1600
|
+
const point2 = points[nextIndex];
|
|
1601
|
+
drawLine(point1, point2);
|
|
1247
1602
|
}
|
|
1248
1603
|
});
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
if (
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1604
|
+
const doorThickness = this.width * 0.2;
|
|
1605
|
+
const list = [];
|
|
1606
|
+
d.addLayer("l_cyan", Drawing.ACI.CYAN, "DOTTED");
|
|
1607
|
+
this.doorLineSegment.forEach((lineSegment) => {
|
|
1608
|
+
if (lineSegment.length() < 0.4) return;
|
|
1609
|
+
const line = lineSegment.clone().expansion(-this.width * 0.5);
|
|
1610
|
+
d.setActiveLayer("l_cyan");
|
|
1611
|
+
if (line.length() < 1.2) {
|
|
1612
|
+
line.expansion(-doorThickness * 0.5);
|
|
1613
|
+
const normal = lineSegment.normal();
|
|
1614
|
+
let door = new LineSegment(
|
|
1615
|
+
line.start.clone(),
|
|
1616
|
+
line.start.clone().add(normal.clone().multiplyScalar(line.length()))
|
|
1259
1617
|
);
|
|
1260
|
-
const
|
|
1261
|
-
for (let
|
|
1262
|
-
if (
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1618
|
+
const box = door.clone().directionMove(door.normal(), line.length() * -0.5).expandToRectangle(line.length(), "bothSides");
|
|
1619
|
+
for (let j = 0; j < list.length; j++) {
|
|
1620
|
+
if (list[j].intersectRectangle(box)) {
|
|
1621
|
+
door = new LineSegment(
|
|
1622
|
+
line.start.clone(),
|
|
1623
|
+
line.start.clone().add(normal.clone().multiplyScalar(-line.length()))
|
|
1266
1624
|
);
|
|
1267
1625
|
break;
|
|
1268
1626
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1627
|
+
}
|
|
1628
|
+
door.expansion(-doorThickness * 0.5).expandToRectangle(this.width * 0.2, "bothSides").path2D((p1, p2) => drawLine(p1, p2));
|
|
1629
|
+
const a = line.length(), b = door.length(), r = (a ** 2 + b ** 2) / (2 * b), center = door.end.clone().add(door.direction().multiplyScalar(-r)), [startAngle, endAngle] = this.getArcAngleRange(center, line.end, door.end);
|
|
1630
|
+
d.drawArc(center.x * s, center.y * s, r * s, Math.min(startAngle, endAngle), Math.max(startAngle, endAngle));
|
|
1631
|
+
list.push(box);
|
|
1632
|
+
} else {
|
|
1633
|
+
line.clone().expansion(-this.width * 0.5).expandToRectangle(this.width).path2D((p1, p2) => drawLine(p1, p2));
|
|
1634
|
+
line.clone().directionMove(line.normal(), doorThickness * 0.5).directionMove(line.direction(), doorThickness * 0.5).expansion(-line.length() * 0.45, "end").forward(doorThickness * 0.5).expandToRectangle(doorThickness).path2D((p1, p2) => drawLine(p1, p2));
|
|
1635
|
+
line.clone().directionMove(line.normal(), -doorThickness * 0.5).directionMove(line.direction(), -doorThickness * 0.5).expansion(-line.length() * 0.45, "start").forward(-doorThickness * 0.5).expandToRectangle(doorThickness).path2D((p1, p2) => drawLine(p1, p2));
|
|
1636
|
+
}
|
|
1637
|
+
});
|
|
1638
|
+
d.addLayer("l_yellow", Drawing.ACI.YELLOW, "DOTTED");
|
|
1639
|
+
d.setActiveLayer("l_yellow");
|
|
1640
|
+
this.lineSegments.forEach((line) => {
|
|
1641
|
+
if (!line.userData.isWindow) return false;
|
|
1642
|
+
if (Array.isArray(line.userData.drawDoorData)) {
|
|
1643
|
+
line.userData.drawDoorData.forEach((w) => {
|
|
1644
|
+
const { p, width } = w;
|
|
1645
|
+
const center = Point.from(p);
|
|
1646
|
+
const start = center.clone().add(line.direction().multiplyScalar(width * 0.5));
|
|
1647
|
+
const end = center.clone().add(line.direction().multiplyScalar(-width * 0.5));
|
|
1648
|
+
const blinds = new LineSegment(start, end);
|
|
1649
|
+
drawLine(blinds.start, blinds.end);
|
|
1650
|
+
blinds.expandToRectangle(this.width, "bothSides").path2D((p1, p2) => drawLine(p1, p2));
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
});
|
|
1654
|
+
return d.toDxfString();
|
|
1281
1655
|
}
|
|
1282
1656
|
/**
|
|
1283
1657
|
* 将点云结构转换为DXF格式
|
|
1284
1658
|
* @returns
|
|
1285
1659
|
*/
|
|
1286
|
-
toDxfBlob(
|
|
1287
|
-
|
|
1660
|
+
toDxfBlob(unit = "Millimeters") {
|
|
1661
|
+
const blob = new Blob([this.toDxfString(unit)]);
|
|
1662
|
+
return blob;
|
|
1288
1663
|
}
|
|
1289
1664
|
/**
|
|
1290
1665
|
* 下载
|
|
1291
1666
|
* @param filename
|
|
1292
1667
|
*/
|
|
1293
|
-
async download(
|
|
1294
|
-
if (typeof window
|
|
1295
|
-
const
|
|
1296
|
-
|
|
1297
|
-
|
|
1668
|
+
async download(filename, unit = "Millimeters") {
|
|
1669
|
+
if (typeof window !== "undefined") {
|
|
1670
|
+
const blob = this.toDxfBlob(unit);
|
|
1671
|
+
const a = document.createElement("a");
|
|
1672
|
+
a.href = URL.createObjectURL(blob);
|
|
1673
|
+
a.download = filename + ".dxf";
|
|
1674
|
+
a.click();
|
|
1675
|
+
} else if (typeof global !== "undefined") {
|
|
1676
|
+
const fs = await include("fs", false);
|
|
1677
|
+
fs.writeFileSync(filename, this.toDxfString(unit));
|
|
1678
|
+
}
|
|
1298
1679
|
}
|
|
1299
1680
|
/**
|
|
1300
1681
|
* 计算原始数据的边界框
|
|
1301
1682
|
* @description 计算所有线段的起点和终点的最小最大值,形成一个边界框
|
|
1302
1683
|
* @returns
|
|
1303
1684
|
*/
|
|
1304
|
-
computedOriginalSize(
|
|
1305
|
-
const
|
|
1306
|
-
|
|
1685
|
+
computedOriginalSize(data, originalBox = new Box2(0, 0, 0, 0)) {
|
|
1686
|
+
const xArr = data.flatMap((item) => [item.start.x, item.end.x]);
|
|
1687
|
+
const yArr = data.flatMap((item) => [item.start.y, item.end.y]);
|
|
1688
|
+
const minX = Math.min(...xArr);
|
|
1689
|
+
const minY = Math.min(...yArr);
|
|
1690
|
+
const maxX = Math.max(...xArr);
|
|
1691
|
+
const maxY = Math.max(...yArr);
|
|
1692
|
+
originalBox.set(minX, minY, maxX, maxY);
|
|
1693
|
+
return originalBox;
|
|
1307
1694
|
}
|
|
1308
1695
|
/**
|
|
1309
1696
|
* 创建数据
|
|
1310
1697
|
* @param pointsGroups
|
|
1311
1698
|
* @returns
|
|
1312
1699
|
*/
|
|
1313
|
-
static createData(
|
|
1314
|
-
let
|
|
1315
|
-
|
|
1316
|
-
const
|
|
1317
|
-
const
|
|
1700
|
+
static createData(pointsGroups, sealed = true) {
|
|
1701
|
+
let count = 0;
|
|
1702
|
+
const data = pointsGroups.flatMap((points) => {
|
|
1703
|
+
const lines = points.map((point, index2) => {
|
|
1704
|
+
const nextIndex = index2 === points.length - 1 ? 0 : index2 + 1;
|
|
1705
|
+
const nextPoint = points[nextIndex];
|
|
1318
1706
|
return {
|
|
1319
|
-
start: { x:
|
|
1320
|
-
end: { x:
|
|
1707
|
+
start: { x: point.x, y: point.y },
|
|
1708
|
+
end: { x: nextPoint.x, y: nextPoint.y },
|
|
1321
1709
|
insetionArr: [
|
|
1322
1710
|
{
|
|
1323
|
-
index:
|
|
1711
|
+
index: nextIndex + count
|
|
1324
1712
|
}
|
|
1325
1713
|
]
|
|
1326
1714
|
};
|
|
1327
1715
|
});
|
|
1328
|
-
|
|
1716
|
+
count += points.length;
|
|
1717
|
+
if (!sealed) {
|
|
1718
|
+
lines.pop();
|
|
1719
|
+
lines[lines.length - 1].insetionArr.length = 0;
|
|
1720
|
+
count--;
|
|
1721
|
+
}
|
|
1722
|
+
return lines;
|
|
1329
1723
|
});
|
|
1724
|
+
return data;
|
|
1330
1725
|
}
|
|
1331
1726
|
}
|
|
1332
|
-
class
|
|
1727
|
+
class Variable extends Component {
|
|
1333
1728
|
static name = "Variable";
|
|
1334
|
-
originalLineVisible =
|
|
1335
|
-
dxfVisible =
|
|
1336
|
-
whiteModelVisible =
|
|
1337
|
-
isLook =
|
|
1729
|
+
originalLineVisible = true;
|
|
1730
|
+
dxfVisible = true;
|
|
1731
|
+
whiteModelVisible = true;
|
|
1732
|
+
isLook = false;
|
|
1338
1733
|
currentWheel = 0;
|
|
1339
1734
|
pointerMove = { x: 0, y: 0 };
|
|
1340
1735
|
currentKeyUp = "";
|
|
1341
1736
|
currentKeyDown = "";
|
|
1342
1737
|
currentMouseUp = "";
|
|
1343
1738
|
currentMouseDown = "";
|
|
1344
|
-
focus =
|
|
1345
|
-
set(
|
|
1346
|
-
if (
|
|
1347
|
-
const
|
|
1348
|
-
this[
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1739
|
+
focus = false;
|
|
1740
|
+
set(key, value) {
|
|
1741
|
+
if (key in this) {
|
|
1742
|
+
const oldValue = this[key];
|
|
1743
|
+
this[key] = value;
|
|
1744
|
+
this.dispatchEvent({
|
|
1745
|
+
type: key,
|
|
1746
|
+
value,
|
|
1747
|
+
oldValue
|
|
1352
1748
|
});
|
|
1353
1749
|
}
|
|
1354
1750
|
}
|
|
1355
|
-
get(
|
|
1356
|
-
if (
|
|
1751
|
+
get(key) {
|
|
1752
|
+
if (key in this) return this[key];
|
|
1357
1753
|
}
|
|
1358
1754
|
}
|
|
1359
|
-
class
|
|
1755
|
+
class Quadtree {
|
|
1360
1756
|
bounds;
|
|
1361
1757
|
// 包围盒
|
|
1362
1758
|
capacity;
|
|
@@ -1365,7 +1761,7 @@ class B {
|
|
|
1365
1761
|
// 最大深度
|
|
1366
1762
|
depth;
|
|
1367
1763
|
// 当前深度
|
|
1368
|
-
isLeaf =
|
|
1764
|
+
isLeaf = true;
|
|
1369
1765
|
// 是否为叶子节点
|
|
1370
1766
|
children = null;
|
|
1371
1767
|
// 子节点数组
|
|
@@ -1373,72 +1769,110 @@ class B {
|
|
|
1373
1769
|
// 存储的节点
|
|
1374
1770
|
color = [Math.random(), Math.random(), Math.random()];
|
|
1375
1771
|
// 颜色
|
|
1376
|
-
constructor(
|
|
1377
|
-
this.bounds =
|
|
1772
|
+
constructor(bounds, capacity = 8, maxDepth = 10, depth = 1) {
|
|
1773
|
+
this.bounds = bounds;
|
|
1774
|
+
this.capacity = capacity;
|
|
1775
|
+
this.depth = depth;
|
|
1776
|
+
this.maxDepth = maxDepth;
|
|
1378
1777
|
}
|
|
1379
1778
|
/**
|
|
1380
1779
|
* 插入线段节点
|
|
1381
1780
|
* @param node 线段节点
|
|
1382
1781
|
*/
|
|
1383
|
-
insert(
|
|
1782
|
+
insert(node) {
|
|
1384
1783
|
if (!this.isLeaf) {
|
|
1385
|
-
const
|
|
1386
|
-
if (
|
|
1387
|
-
this.children[
|
|
1784
|
+
const quadrant = this.getQuadrant(node.line);
|
|
1785
|
+
if (quadrant !== -1) {
|
|
1786
|
+
this.children[quadrant].insert(node);
|
|
1388
1787
|
return;
|
|
1389
1788
|
}
|
|
1390
1789
|
}
|
|
1391
|
-
|
|
1790
|
+
this.nodes.push(node);
|
|
1791
|
+
node.parent = this;
|
|
1792
|
+
if (this.isLeaf && this.nodes.length > this.capacity && this.depth < this.maxDepth) {
|
|
1392
1793
|
this.subdivide();
|
|
1393
|
-
const
|
|
1794
|
+
const nodes = this.nodes;
|
|
1394
1795
|
this.nodes = [];
|
|
1395
|
-
for (const n of
|
|
1396
|
-
const
|
|
1397
|
-
|
|
1796
|
+
for (const n of nodes) {
|
|
1797
|
+
const quadrant = this.getQuadrant(n.line);
|
|
1798
|
+
if (quadrant !== -1) {
|
|
1799
|
+
this.children[quadrant].insert(n);
|
|
1800
|
+
} else {
|
|
1801
|
+
n.parent = this;
|
|
1802
|
+
this.nodes.push(n);
|
|
1803
|
+
}
|
|
1398
1804
|
}
|
|
1399
1805
|
}
|
|
1400
1806
|
}
|
|
1401
1807
|
/** 移除
|
|
1402
1808
|
* @param node
|
|
1403
1809
|
*/
|
|
1404
|
-
remove(
|
|
1405
|
-
const
|
|
1406
|
-
|
|
1810
|
+
remove(node) {
|
|
1811
|
+
const index2 = node.parent?.nodes.indexOf(node);
|
|
1812
|
+
if (index2 > -1) {
|
|
1813
|
+
node.parent?.nodes.splice(index2, 1);
|
|
1814
|
+
}
|
|
1407
1815
|
}
|
|
1408
1816
|
/**
|
|
1409
1817
|
* 获取线段所属的象限
|
|
1410
1818
|
* @param line 线段
|
|
1411
1819
|
* @returns 象限索引(0:西北,1:东北,2:西南,3:东南)或-1(跨多个象限)
|
|
1412
1820
|
*/
|
|
1413
|
-
getQuadrant(
|
|
1414
|
-
const
|
|
1415
|
-
|
|
1416
|
-
|
|
1821
|
+
getQuadrant(line) {
|
|
1822
|
+
const intersectsNW = this.children[0].bounds.intersectLineSegment(line);
|
|
1823
|
+
const intersectsNE = this.children[1].bounds.intersectLineSegment(line);
|
|
1824
|
+
const intersectsSW = this.children[2].bounds.intersectLineSegment(line);
|
|
1825
|
+
const intersectsSE = this.children[3].bounds.intersectLineSegment(line);
|
|
1826
|
+
let count = 0;
|
|
1827
|
+
let quadrant = -1;
|
|
1828
|
+
if (intersectsNW) {
|
|
1829
|
+
count++;
|
|
1830
|
+
quadrant = 0;
|
|
1831
|
+
}
|
|
1832
|
+
if (intersectsNE) {
|
|
1833
|
+
count++;
|
|
1834
|
+
quadrant = 1;
|
|
1835
|
+
}
|
|
1836
|
+
if (intersectsSW) {
|
|
1837
|
+
count++;
|
|
1838
|
+
quadrant = 2;
|
|
1839
|
+
}
|
|
1840
|
+
if (intersectsSE) {
|
|
1841
|
+
count++;
|
|
1842
|
+
quadrant = 3;
|
|
1843
|
+
}
|
|
1844
|
+
if (count === 1) return quadrant;
|
|
1845
|
+
return -1;
|
|
1417
1846
|
}
|
|
1418
1847
|
/**
|
|
1419
1848
|
* 细分当前节点为四个子节点
|
|
1420
1849
|
*/
|
|
1421
1850
|
subdivide() {
|
|
1422
1851
|
if (!this.isLeaf) return;
|
|
1423
|
-
this.isLeaf =
|
|
1424
|
-
|
|
1425
|
-
this.
|
|
1426
|
-
|
|
1852
|
+
this.isLeaf = false;
|
|
1853
|
+
this.children = [];
|
|
1854
|
+
const midX = (this.bounds.minX + this.bounds.maxX) / 2;
|
|
1855
|
+
const midY = (this.bounds.minY + this.bounds.maxY) / 2;
|
|
1856
|
+
this.children[0] = new Quadtree(
|
|
1857
|
+
new Box2(this.bounds.minX, midX, this.bounds.minY, midY),
|
|
1427
1858
|
this.capacity,
|
|
1428
1859
|
this.maxDepth,
|
|
1429
1860
|
this.depth + 1
|
|
1430
|
-
)
|
|
1431
|
-
|
|
1861
|
+
);
|
|
1862
|
+
this.children[1] = new Quadtree(
|
|
1863
|
+
new Box2(midX, this.bounds.maxX, this.bounds.minY, midY),
|
|
1432
1864
|
this.capacity,
|
|
1433
1865
|
this.maxDepth,
|
|
1434
1866
|
this.depth + 1
|
|
1435
|
-
)
|
|
1436
|
-
|
|
1867
|
+
);
|
|
1868
|
+
this.children[2] = new Quadtree(
|
|
1869
|
+
new Box2(this.bounds.minX, midX, midY, this.bounds.maxY),
|
|
1437
1870
|
this.capacity,
|
|
1438
1871
|
this.maxDepth,
|
|
1439
1872
|
this.depth + 1
|
|
1440
|
-
)
|
|
1441
|
-
|
|
1873
|
+
);
|
|
1874
|
+
this.children[3] = new Quadtree(
|
|
1875
|
+
new Box2(midX, this.bounds.maxX, midY, this.bounds.maxY),
|
|
1442
1876
|
this.capacity,
|
|
1443
1877
|
this.maxDepth,
|
|
1444
1878
|
this.depth + 1
|
|
@@ -1449,16 +1883,22 @@ class B {
|
|
|
1449
1883
|
* @param box2 包围盒
|
|
1450
1884
|
* @returns 相交的节点数组
|
|
1451
1885
|
*/
|
|
1452
|
-
queryBox(
|
|
1453
|
-
const
|
|
1454
|
-
if (!this.bounds.intersectBox(
|
|
1455
|
-
return
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1886
|
+
queryBox(box2) {
|
|
1887
|
+
const result = [];
|
|
1888
|
+
if (!this.bounds.intersectBox(box2)) {
|
|
1889
|
+
return result;
|
|
1890
|
+
}
|
|
1891
|
+
for (const node of this.nodes) {
|
|
1892
|
+
if (box2.intersectLineSegment(node.line)) {
|
|
1893
|
+
result.push(node);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
if (!this.isLeaf) {
|
|
1897
|
+
for (const child of this.children) {
|
|
1898
|
+
result.push(...child.queryBox(box2));
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
return result;
|
|
1462
1902
|
}
|
|
1463
1903
|
/**
|
|
1464
1904
|
* 查询与圆形区域相交的线段节点
|
|
@@ -1466,58 +1906,81 @@ class B {
|
|
|
1466
1906
|
* @param radius 半径
|
|
1467
1907
|
* @returns 相交的节点数组
|
|
1468
1908
|
*/
|
|
1469
|
-
queryCircle(
|
|
1470
|
-
const
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1909
|
+
queryCircle(pos, radius) {
|
|
1910
|
+
const result = [];
|
|
1911
|
+
const circleBox = new Box2(
|
|
1912
|
+
pos.x - radius,
|
|
1913
|
+
pos.x + radius,
|
|
1914
|
+
pos.y - radius,
|
|
1915
|
+
pos.y + radius
|
|
1475
1916
|
);
|
|
1476
|
-
if (!this.bounds.intersectBox(
|
|
1477
|
-
return
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
const
|
|
1483
|
-
|
|
1917
|
+
if (!this.bounds.intersectBox(circleBox)) {
|
|
1918
|
+
return result;
|
|
1919
|
+
}
|
|
1920
|
+
for (const node of this.nodes) {
|
|
1921
|
+
const [p1, p2] = node.line.points;
|
|
1922
|
+
const dx = p2.x - p1.x;
|
|
1923
|
+
const dy = p2.y - p1.y;
|
|
1924
|
+
const l2 = dx * dx + dy * dy;
|
|
1925
|
+
let t = ((pos.x - p1.x) * dx + (pos.y - p1.y) * dy) / l2;
|
|
1926
|
+
t = Math.max(0, Math.min(1, t));
|
|
1927
|
+
const closestX = p1.x + t * dx;
|
|
1928
|
+
const closestY = p1.y + t * dy;
|
|
1929
|
+
const distance = pos.distance(new Point(closestX, closestY));
|
|
1930
|
+
if (distance <= radius) {
|
|
1931
|
+
result.push(node);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
if (!this.isLeaf) {
|
|
1935
|
+
for (const child of this.children) {
|
|
1936
|
+
result.push(...child.queryCircle(pos, radius));
|
|
1937
|
+
}
|
|
1484
1938
|
}
|
|
1485
|
-
|
|
1486
|
-
for (const o of this.children)
|
|
1487
|
-
n.push(...o.queryCircle(t, e));
|
|
1488
|
-
return n;
|
|
1939
|
+
return result;
|
|
1489
1940
|
}
|
|
1490
1941
|
/**
|
|
1491
1942
|
* 查询与矩形相交的线段节点
|
|
1492
1943
|
* @param rectangle 矩形
|
|
1493
1944
|
* @returns 相交的节点数组
|
|
1494
1945
|
*/
|
|
1495
|
-
queryRect(
|
|
1496
|
-
const
|
|
1497
|
-
if (!this.bounds.intersectRectangle(
|
|
1498
|
-
return
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1946
|
+
queryRect(rectangle) {
|
|
1947
|
+
const result = [];
|
|
1948
|
+
if (!this.bounds.intersectRectangle(rectangle)) {
|
|
1949
|
+
return result;
|
|
1950
|
+
}
|
|
1951
|
+
for (const node of this.nodes) {
|
|
1952
|
+
if (rectangle.intersectLineSegment(node.line)) {
|
|
1953
|
+
result.push(node);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
if (!this.isLeaf) {
|
|
1957
|
+
for (const child of this.children) {
|
|
1958
|
+
result.push(...child.queryRect(rectangle));
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
return result;
|
|
1505
1962
|
}
|
|
1506
1963
|
/**
|
|
1507
1964
|
* 查询与线段相交的线段节点
|
|
1508
1965
|
* @param lineSegment 线段
|
|
1509
1966
|
* @returns 相交的节点数组
|
|
1510
1967
|
*/
|
|
1511
|
-
queryLineSegment(
|
|
1512
|
-
const
|
|
1513
|
-
if (!this.bounds.intersectLineSegment(
|
|
1514
|
-
return
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1968
|
+
queryLineSegment(lineSegment) {
|
|
1969
|
+
const result = [];
|
|
1970
|
+
if (!this.bounds.intersectLineSegment(lineSegment)) {
|
|
1971
|
+
return result;
|
|
1972
|
+
}
|
|
1973
|
+
for (const node of this.nodes) {
|
|
1974
|
+
if (lineSegment.intersectLineSegment(node.line)) {
|
|
1975
|
+
result.push(node);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
if (!this.isLeaf) {
|
|
1979
|
+
for (const child of this.children) {
|
|
1980
|
+
result.push(...child.queryLineSegment(lineSegment));
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
return result;
|
|
1521
1984
|
}
|
|
1522
1985
|
/**
|
|
1523
1986
|
* 包围盒转换为数组
|
|
@@ -1525,80 +1988,96 @@ class B {
|
|
|
1525
1988
|
* @param colors
|
|
1526
1989
|
* @returns
|
|
1527
1990
|
*/
|
|
1528
|
-
boundsToArray(
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1991
|
+
boundsToArray(array = [], colors, recursion = true) {
|
|
1992
|
+
if (!this.isLeaf && recursion) {
|
|
1993
|
+
this.children?.forEach((child) => child.boundsToArray(array, colors));
|
|
1994
|
+
}
|
|
1995
|
+
array.push(...this.bounds.points.flatMap((p, i, array2) => {
|
|
1996
|
+
const np = array2[(i + 1) % array2.length];
|
|
1997
|
+
colors?.push(...this.color);
|
|
1998
|
+
colors?.push(...this.color);
|
|
1999
|
+
return [p.x, p.y, 0, np.x, np.y, 0];
|
|
2000
|
+
}));
|
|
2001
|
+
return array;
|
|
1533
2002
|
}
|
|
1534
2003
|
}
|
|
1535
|
-
class
|
|
2004
|
+
class PointVirtualGrid {
|
|
1536
2005
|
map = /* @__PURE__ */ new Map();
|
|
1537
2006
|
gridSize;
|
|
1538
|
-
constructor(
|
|
1539
|
-
this.gridSize =
|
|
2007
|
+
constructor(gridSize = 2) {
|
|
2008
|
+
this.gridSize = gridSize;
|
|
1540
2009
|
}
|
|
1541
2010
|
/**
|
|
1542
2011
|
* 插入
|
|
1543
2012
|
* @param point
|
|
1544
2013
|
* @param userData
|
|
1545
2014
|
*/
|
|
1546
|
-
insert(
|
|
1547
|
-
if (!
|
|
2015
|
+
insert(point, userData) {
|
|
2016
|
+
if (!point || isNaN(point.x) || isNaN(point.y)) {
|
|
1548
2017
|
throw new Error("无效的点坐标");
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
2018
|
+
}
|
|
2019
|
+
const id = this.getGridId(point);
|
|
2020
|
+
if (!this.map.has(id)) this.map.set(id, /* @__PURE__ */ new Set());
|
|
2021
|
+
const set = this.map.get(id);
|
|
2022
|
+
const target = { point, userData };
|
|
2023
|
+
set.add(target);
|
|
2024
|
+
point.userData.pointVirtualGrid = { set, target };
|
|
1553
2025
|
}
|
|
1554
2026
|
/**
|
|
1555
2027
|
* 批量加入
|
|
1556
2028
|
* @param points
|
|
1557
2029
|
*/
|
|
1558
|
-
insertBatch(
|
|
1559
|
-
for (const { point
|
|
1560
|
-
this.insert(
|
|
2030
|
+
insertBatch(points) {
|
|
2031
|
+
for (const { point, userData } of points) {
|
|
2032
|
+
this.insert(point, userData);
|
|
2033
|
+
}
|
|
1561
2034
|
}
|
|
1562
2035
|
/** 移除点
|
|
1563
2036
|
* @param point
|
|
1564
2037
|
*/
|
|
1565
|
-
remove(
|
|
1566
|
-
const { set
|
|
1567
|
-
|
|
2038
|
+
remove(point) {
|
|
2039
|
+
const { set, target } = point?.userData?.pointVirtualGrid;
|
|
2040
|
+
if (set) {
|
|
2041
|
+
set.delete(target);
|
|
2042
|
+
delete point?.userData?.pointVirtualGridMap;
|
|
2043
|
+
}
|
|
1568
2044
|
}
|
|
1569
2045
|
/**
|
|
1570
2046
|
* 获取通过坐标,获取唯一网格索引
|
|
1571
2047
|
* @param point
|
|
1572
2048
|
* @returns
|
|
1573
2049
|
*/
|
|
1574
|
-
getGridId(
|
|
1575
|
-
const
|
|
1576
|
-
return `${
|
|
2050
|
+
getGridId(point) {
|
|
2051
|
+
const i = Math.ceil(point.x / this.gridSize), j = Math.ceil(point.y / this.gridSize);
|
|
2052
|
+
return `${i}.${j}`;
|
|
1577
2053
|
}
|
|
1578
2054
|
/**
|
|
1579
2055
|
*
|
|
1580
2056
|
* @param gridId
|
|
1581
2057
|
* @returns
|
|
1582
2058
|
*/
|
|
1583
|
-
decodeGridId(
|
|
1584
|
-
const [
|
|
1585
|
-
return new
|
|
2059
|
+
decodeGridId(gridId) {
|
|
2060
|
+
const [i, j] = gridId.split(".").map(Number);
|
|
2061
|
+
return new Point(i, j);
|
|
1586
2062
|
}
|
|
1587
2063
|
/**
|
|
1588
2064
|
* 查询与矩形相交的点
|
|
1589
2065
|
* @param rectangle 矩形
|
|
1590
2066
|
* @returns 相交的节点数组
|
|
1591
2067
|
*/
|
|
1592
|
-
queryRect(
|
|
1593
|
-
const
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
this.map.
|
|
1599
|
-
|
|
2068
|
+
queryRect(rectangle) {
|
|
2069
|
+
const box2 = rectangle.toBox();
|
|
2070
|
+
const minI = Math.ceil(box2.minX / this.gridSize), maxI = Math.ceil(box2.maxX / this.gridSize), minJ = Math.ceil(box2.minY / this.gridSize), maxJ = Math.ceil(box2.maxY / this.gridSize);
|
|
2071
|
+
for (let i = minI; i <= maxI; i++) {
|
|
2072
|
+
for (let j = minJ; j <= maxJ; j++) {
|
|
2073
|
+
const id = `${i}.${j}`;
|
|
2074
|
+
if (!this.map.has(id)) continue;
|
|
2075
|
+
const set = this.map.get(id);
|
|
2076
|
+
set?.forEach((item) => {
|
|
2077
|
+
if (rectangle.containsPoint(item.point)) ;
|
|
1600
2078
|
});
|
|
1601
2079
|
}
|
|
2080
|
+
}
|
|
1602
2081
|
}
|
|
1603
2082
|
/**
|
|
1604
2083
|
* 查询与圆形区域相交的点
|
|
@@ -1606,53 +2085,62 @@ class U {
|
|
|
1606
2085
|
* @param radius 半径
|
|
1607
2086
|
* @returns 相交的节点数组
|
|
1608
2087
|
*/
|
|
1609
|
-
queryCircle(
|
|
1610
|
-
const
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
this.map.
|
|
1616
|
-
|
|
2088
|
+
queryCircle(pos, radius) {
|
|
2089
|
+
const box2 = new Box2(pos.x - radius, pos.x + radius, pos.y - radius, pos.y + radius);
|
|
2090
|
+
const minI = Math.ceil(box2.minX / this.gridSize), maxI = Math.ceil(box2.maxX / this.gridSize), minJ = Math.ceil(box2.minY / this.gridSize), maxJ = Math.ceil(box2.maxY / this.gridSize), list = [];
|
|
2091
|
+
for (let i = minI; i <= maxI; i++) {
|
|
2092
|
+
for (let j = minJ; j <= maxJ; j++) {
|
|
2093
|
+
const id = `${i}.${j}`;
|
|
2094
|
+
if (!this.map.has(id)) continue;
|
|
2095
|
+
const set = this.map.get(id);
|
|
2096
|
+
set?.forEach((item) => {
|
|
2097
|
+
if (pos.distance(item.point) <= radius) list.push(item);
|
|
1617
2098
|
});
|
|
1618
2099
|
}
|
|
1619
|
-
|
|
2100
|
+
}
|
|
2101
|
+
return list;
|
|
1620
2102
|
}
|
|
1621
2103
|
/**
|
|
1622
2104
|
* 查询与包围盒相交的点
|
|
1623
2105
|
* @param box2 包围盒
|
|
1624
2106
|
* @returns 相交的节点数组
|
|
1625
2107
|
*/
|
|
1626
|
-
queryBox(
|
|
1627
|
-
const
|
|
1628
|
-
for (let
|
|
1629
|
-
for (let
|
|
1630
|
-
const
|
|
1631
|
-
if (!this.map.has(
|
|
1632
|
-
this.map.get(
|
|
1633
|
-
|
|
2108
|
+
queryBox(box2) {
|
|
2109
|
+
const minI = Math.ceil(box2.minX / this.gridSize), maxI = Math.ceil(box2.maxX / this.gridSize), minJ = Math.ceil(box2.minY / this.gridSize), maxJ = Math.ceil(box2.maxY / this.gridSize), list = [];
|
|
2110
|
+
for (let i = minI; i <= maxI; i++) {
|
|
2111
|
+
for (let j = minJ; j <= maxJ; j++) {
|
|
2112
|
+
const id = `${i}.${j}`;
|
|
2113
|
+
if (!this.map.has(id)) continue;
|
|
2114
|
+
const set = this.map.get(id);
|
|
2115
|
+
set?.forEach((item) => {
|
|
2116
|
+
if (box2.containsPoint(item.point)) list.push(item);
|
|
1634
2117
|
});
|
|
1635
2118
|
}
|
|
1636
|
-
|
|
2119
|
+
}
|
|
2120
|
+
return list;
|
|
1637
2121
|
}
|
|
1638
2122
|
/**
|
|
1639
2123
|
* 查找相同点
|
|
1640
2124
|
* @param point
|
|
1641
2125
|
*/
|
|
1642
|
-
queryPoint(
|
|
1643
|
-
const
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
2126
|
+
queryPoint(point) {
|
|
2127
|
+
const id = this.getGridId(point), list = [];
|
|
2128
|
+
if (this.map.has(id)) {
|
|
2129
|
+
const set = this.map.get(id);
|
|
2130
|
+
set?.forEach((item) => {
|
|
2131
|
+
if (point.equal(item.point)) list.push(item);
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
return list;
|
|
1647
2135
|
}
|
|
1648
2136
|
}
|
|
1649
|
-
class
|
|
2137
|
+
class DoorsAnalysis {
|
|
1650
2138
|
// 所有可查找的点位
|
|
1651
2139
|
possibleDoorPoints = [];
|
|
1652
2140
|
doorPoint = [];
|
|
1653
2141
|
dxf;
|
|
1654
2142
|
// 包含所有点的虚拟网格
|
|
1655
|
-
pointVirtualGrid = new
|
|
2143
|
+
pointVirtualGrid = new PointVirtualGrid();
|
|
1656
2144
|
// 只包含可查找点的虚拟网格
|
|
1657
2145
|
findPointVirtualGrid;
|
|
1658
2146
|
quadtree;
|
|
@@ -1663,111 +2151,158 @@ class it {
|
|
|
1663
2151
|
doorSearchDistance = 2;
|
|
1664
2152
|
doors = [];
|
|
1665
2153
|
lineAnalysis;
|
|
1666
|
-
continueFind =
|
|
1667
|
-
constructor(
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
this.
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
2154
|
+
continueFind = true;
|
|
2155
|
+
constructor(lineAnalysis) {
|
|
2156
|
+
this.lineAnalysis = lineAnalysis;
|
|
2157
|
+
this.dxf = lineAnalysis.Dxf;
|
|
2158
|
+
this.findPointVirtualGrid = new PointVirtualGrid();
|
|
2159
|
+
this.quadtree = lineAnalysis.quadtree;
|
|
2160
|
+
this.resultList = lineAnalysis.resultList;
|
|
2161
|
+
this.lineSegments = lineAnalysis.lineSegmentList;
|
|
2162
|
+
this.dxf.doorLineSegment.length = 0;
|
|
2163
|
+
this.lineSegments.forEach((line) => {
|
|
2164
|
+
this.pointVirtualGrid.insert(line.start, line);
|
|
2165
|
+
this.pointVirtualGrid.insert(line.end, line);
|
|
2166
|
+
});
|
|
2167
|
+
this.doorPoint = this.getDoorPoint();
|
|
2168
|
+
if (!this.continueFind) return;
|
|
2169
|
+
const excludeIndexMap = this.searchDoubleLinePoint();
|
|
2170
|
+
this.addPointsExcludeRule((line, _2, pointIndex) => {
|
|
2171
|
+
const index2 = this.lineSegments.indexOf(line);
|
|
2172
|
+
const excludeMode = excludeIndexMap.get(index2);
|
|
2173
|
+
if (typeof excludeMode === "number") {
|
|
2174
|
+
return excludeMode === -1 || excludeMode === pointIndex;
|
|
2175
|
+
}
|
|
2176
|
+
return false;
|
|
2177
|
+
});
|
|
2178
|
+
this.addPointsExcludeRule((_1, point) => {
|
|
2179
|
+
return !!this.doorPoint.find((p1) => p1.point.equal(point));
|
|
2180
|
+
});
|
|
2181
|
+
this.possibleDoorPoints = this.getPossiblePoints();
|
|
2182
|
+
this.possibleDoorPoints.forEach((p) => this.findPointVirtualGrid.insert(p.point, p.line));
|
|
2183
|
+
this.handle();
|
|
1676
2184
|
}
|
|
1677
2185
|
handle() {
|
|
1678
|
-
this.dxf.doorLineSegment.push(...this.search(this.doorPoint, this.possibleDoorPoints, 0.5))
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
2186
|
+
this.dxf.doorLineSegment.push(...this.search(this.doorPoint, this.possibleDoorPoints, 0.5));
|
|
2187
|
+
if (this.doorPoint.length < 2) this.dxf.doorLineSegment.push(...this.search(this.possibleDoorPoints, this.possibleDoorPoints, 0.6));
|
|
2188
|
+
}
|
|
2189
|
+
search(doorPoints, possibleDoorPoints = [], minDoorWidth = 0.6, doorSearchDistance = this.doorSearchDistance, doorSearchNearAngle = this.doorSearchNearAngle) {
|
|
2190
|
+
const dxf = this.dxf;
|
|
2191
|
+
const doors = this.searchNearby(doorPoints, possibleDoorPoints, doorSearchDistance, doorSearchNearAngle);
|
|
2192
|
+
doors.push(
|
|
2193
|
+
...doorPoints.map((item) => {
|
|
2194
|
+
const res2 = this.searchAlongDirection(item, doorSearchDistance);
|
|
2195
|
+
if (res2) return {
|
|
2196
|
+
start: item.point,
|
|
2197
|
+
end: res2.point
|
|
1688
2198
|
};
|
|
1689
|
-
const
|
|
1690
|
-
if (
|
|
1691
|
-
start:
|
|
1692
|
-
end:
|
|
2199
|
+
const res3 = this.searchAlongNormalDirection(item, doorSearchDistance);
|
|
2200
|
+
if (res3) return {
|
|
2201
|
+
start: item.point,
|
|
2202
|
+
end: res3.point
|
|
1693
2203
|
};
|
|
1694
|
-
}).filter((
|
|
2204
|
+
}).filter((i) => !!i && i.start.distance(i.end) < doorSearchDistance)
|
|
1695
2205
|
);
|
|
1696
|
-
const
|
|
1697
|
-
|
|
1698
|
-
const
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
2206
|
+
const doorLineSegment = [];
|
|
2207
|
+
doors.forEach((p) => {
|
|
2208
|
+
const line = new LineSegment(p?.start, p?.end);
|
|
2209
|
+
const len = line.length();
|
|
2210
|
+
if (len < minDoorWidth) return;
|
|
2211
|
+
const normal = line.normal(), direction = line.direction(), step = (len - dxf.width * 2) / 2;
|
|
2212
|
+
for (let i = 0; i < 3; i++) {
|
|
2213
|
+
const point = line.start.clone().add(direction.clone().multiplyScalar(dxf.width + step * i));
|
|
2214
|
+
const rLine = new LineSegment(
|
|
2215
|
+
point,
|
|
2216
|
+
point.clone().add(normal.clone().multiplyScalar(1))
|
|
1705
2217
|
);
|
|
1706
|
-
|
|
2218
|
+
rLine.directionMove(normal, -0.5);
|
|
2219
|
+
const res = this.quadtree.queryLineSegment(rLine);
|
|
2220
|
+
if (res.length) return;
|
|
1707
2221
|
}
|
|
1708
|
-
|
|
1709
|
-
})
|
|
2222
|
+
doorLineSegment.push(line);
|
|
2223
|
+
});
|
|
2224
|
+
return doorLineSegment;
|
|
1710
2225
|
}
|
|
1711
2226
|
/** 添加可查找点的过滤规则
|
|
1712
2227
|
* @param rule
|
|
1713
2228
|
*/
|
|
1714
|
-
addPointsExcludeRule(
|
|
1715
|
-
this._pointsExcludeRule.push(
|
|
2229
|
+
addPointsExcludeRule(rule) {
|
|
2230
|
+
this._pointsExcludeRule.push(rule);
|
|
1716
2231
|
}
|
|
1717
2232
|
_pointsExcludeRule = [];
|
|
1718
2233
|
/**
|
|
1719
2234
|
* 查找所有可能为门的点位
|
|
1720
2235
|
*/
|
|
1721
2236
|
getPossiblePoints() {
|
|
1722
|
-
const
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
for (let
|
|
1726
|
-
this.pointVirtualGrid.queryPoint(
|
|
2237
|
+
const doorPoints = [];
|
|
2238
|
+
this.lineSegments.forEach((line) => {
|
|
2239
|
+
line.points.forEach((p, j) => {
|
|
2240
|
+
for (let i = 0; i < this._pointsExcludeRule.length; i++) if (this._pointsExcludeRule[i](line, p, j)) return;
|
|
2241
|
+
const res = this.pointVirtualGrid.queryPoint(p).filter((d) => d.userData !== line);
|
|
2242
|
+
if (res.length === 0) {
|
|
2243
|
+
doorPoints.push({ line, point: p, uuid: uuid() });
|
|
2244
|
+
}
|
|
1727
2245
|
});
|
|
1728
|
-
})
|
|
2246
|
+
});
|
|
2247
|
+
return doorPoints;
|
|
1729
2248
|
}
|
|
1730
2249
|
/**
|
|
1731
2250
|
* 查找已知为门的点位
|
|
1732
2251
|
*/
|
|
1733
2252
|
getDoorPoint() {
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
const
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
2253
|
+
const doorPoints = [], dxf = this.dxf, pointVirtualGrid = this.pointVirtualGrid;
|
|
2254
|
+
dxf.doors.forEach((item) => {
|
|
2255
|
+
const doorLine = dxf.lineSegments[item[4]];
|
|
2256
|
+
const doorData = dxf.originalData[item[4]];
|
|
2257
|
+
if (doorData.drawDoorData) {
|
|
2258
|
+
const point = Point.from(doorData.drawDoorData.start);
|
|
2259
|
+
const direct = Point.from(doorData.drawDoorData.n);
|
|
2260
|
+
const resList = pointVirtualGrid.queryPoint(point).filter((res) => {
|
|
2261
|
+
if (res.userData === doorLine) return false;
|
|
2262
|
+
const line = res.userData;
|
|
2263
|
+
const direct2 = line.direction();
|
|
2264
|
+
if (line.start.equal(point)) direct2.multiplyScalar(-1);
|
|
2265
|
+
const angle = direct.angleBetween(direct2, "angle");
|
|
2266
|
+
return angle > 80 || angle < 10;
|
|
1744
2267
|
});
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
2268
|
+
if (resList.length) {
|
|
2269
|
+
doorPoints.push({
|
|
2270
|
+
line: resList[0].userData,
|
|
2271
|
+
point,
|
|
2272
|
+
uuid: uuid()
|
|
2273
|
+
});
|
|
2274
|
+
}
|
|
2275
|
+
} else if (doorData.doorDirectConnection) {
|
|
2276
|
+
this.continueFind = false;
|
|
2277
|
+
const line = new LineSegment(Point.from(doorData.start), Point.from(doorData.end));
|
|
2278
|
+
line.userData = {
|
|
2279
|
+
doorDirectConnection: true,
|
|
2280
|
+
isDoor: true
|
|
2281
|
+
};
|
|
2282
|
+
this.dxf.doorLineSegment.push(line);
|
|
2283
|
+
} else {
|
|
2284
|
+
console.warn(`门的线段顺序${item[4]} 没有drawDoorData属性`);
|
|
2285
|
+
}
|
|
2286
|
+
});
|
|
2287
|
+
console.log("门点位数量:", doorPoints.length);
|
|
2288
|
+
return doorPoints;
|
|
1760
2289
|
}
|
|
1761
2290
|
/**
|
|
1762
2291
|
* 查找双线墙的点位
|
|
1763
2292
|
* @returns
|
|
1764
2293
|
*/
|
|
1765
2294
|
searchDoubleLinePoint() {
|
|
1766
|
-
const
|
|
1767
|
-
|
|
1768
|
-
const
|
|
1769
|
-
|
|
1770
|
-
|
|
2295
|
+
const excludeIndexMap = /* @__PURE__ */ new Map();
|
|
2296
|
+
this.resultList.flatMap((p) => {
|
|
2297
|
+
const line0 = this.lineSegments[p.sourceIndex], line1 = this.lineSegments[p.targetIndex], start0 = line1.projectPoint(line0.start), end0 = line1.projectPoint(line0.end), start1 = line0.projectPoint(line1.start), end1 = line0.projectPoint(line1.end), mode0 = start0 && end0 ? -1 : start0 ? 0 : end0 ? 1 : -1, mode1 = start1 && end1 ? -1 : start1 ? 0 : end1 ? 1 : -1;
|
|
2298
|
+
if (excludeIndexMap.has(p.sourceIndex)) {
|
|
2299
|
+
if (excludeIndexMap.get(p.sourceIndex) != mode0) excludeIndexMap.set(p.sourceIndex, -1);
|
|
2300
|
+
} else excludeIndexMap.set(p.sourceIndex, mode0);
|
|
2301
|
+
if (excludeIndexMap.has(p.targetIndex)) {
|
|
2302
|
+
if (excludeIndexMap.get(p.targetIndex) != mode1) excludeIndexMap.set(p.targetIndex, -1);
|
|
2303
|
+
} else excludeIndexMap.set(p.targetIndex, mode1);
|
|
2304
|
+
});
|
|
2305
|
+
return excludeIndexMap;
|
|
1771
2306
|
}
|
|
1772
2307
|
/** 查找方案一:最近点查找
|
|
1773
2308
|
* @description 以点为圆心,查找半径内符合角度的点
|
|
@@ -1777,113 +2312,160 @@ class it {
|
|
|
1777
2312
|
* @param doorSearchNearAngle 查找的角度
|
|
1778
2313
|
* @returns
|
|
1779
2314
|
*/
|
|
1780
|
-
searchNearby(
|
|
1781
|
-
const
|
|
1782
|
-
function
|
|
1783
|
-
const
|
|
1784
|
-
|
|
1785
|
-
const
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
2315
|
+
searchNearby(doorPoints, possibleDoorPoints = [], doorSearchDistance = this.doorSearchDistance, doorSearchNearAngle = this.doorSearchNearAngle) {
|
|
2316
|
+
const findPointVirtualGrid = this.findPointVirtualGrid, quadtree = this.quadtree;
|
|
2317
|
+
function find({ point, line }, doorIndex, record2) {
|
|
2318
|
+
const direct = line.direction();
|
|
2319
|
+
if (line.start === point) direct.multiplyScalar(-1);
|
|
2320
|
+
const res = findPointVirtualGrid.queryCircle(point, doorSearchDistance).filter((r) => r.userData !== line).sort((a, b) => a.point.distance(point) - b.point.distance(point));
|
|
2321
|
+
const list = [];
|
|
2322
|
+
for (let i = 0; i < res.length; i++) {
|
|
2323
|
+
const doorIndex2 = possibleDoorPoints.findIndex((p) => p.point === res[i].point);
|
|
2324
|
+
const id1 = doorPoints[doorIndex].uuid, id2 = possibleDoorPoints[doorIndex2].uuid;
|
|
2325
|
+
if (record2.has(`${id1}.${id2}`)) continue;
|
|
2326
|
+
record2.add(`${id1}.${id2}`);
|
|
2327
|
+
record2.add(`${id2}.${id1}`);
|
|
2328
|
+
const targetPoint = res[i].point, line2 = new LineSegment(point.clone(), targetPoint.clone()), angle = line2.direction().angleBetween(direct, "angle");
|
|
2329
|
+
if (angle < doorSearchNearAngle) {
|
|
2330
|
+
const direct2 = doorPoints[doorIndex2].line.direction();
|
|
2331
|
+
const line22 = res[i].userData;
|
|
2332
|
+
if (line22.start.equal(res[i].point)) direct2.multiplyScalar(-1);
|
|
2333
|
+
const angle2 = line2.direction().multiplyScalar(-1).angleBetween(direct2, "angle");
|
|
2334
|
+
if (angle2 < doorSearchNearAngle) {
|
|
2335
|
+
if (!quadtree.queryLineSegment(line2).length) {
|
|
2336
|
+
list.push({
|
|
2337
|
+
findData: res[i],
|
|
2338
|
+
findUuid: id2,
|
|
2339
|
+
doorLine: line2,
|
|
2340
|
+
doorUuid: id1
|
|
2341
|
+
});
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
1799
2344
|
}
|
|
1800
2345
|
}
|
|
1801
|
-
return
|
|
1802
|
-
}
|
|
1803
|
-
function
|
|
1804
|
-
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1807
|
-
for (let
|
|
1808
|
-
const
|
|
1809
|
-
if (
|
|
1810
|
-
const
|
|
1811
|
-
|
|
1812
|
-
} else
|
|
2346
|
+
return list;
|
|
2347
|
+
}
|
|
2348
|
+
function deepSearchNearHandle(uuid2, snFindRecord2, list, record2, other) {
|
|
2349
|
+
record2.add(uuid2);
|
|
2350
|
+
const newList = [];
|
|
2351
|
+
if (other) newList.push(other);
|
|
2352
|
+
for (let i = 0; i < list.length; i++) {
|
|
2353
|
+
const item = list[i];
|
|
2354
|
+
if (snFindRecord2.has(item.findUuid)) {
|
|
2355
|
+
const list2 = snFindRecord2.get(item.findUuid);
|
|
2356
|
+
if (deepSearchNearHandle(item.findUuid, snFindRecord2, list2, record2, item)) newList.push(item);
|
|
2357
|
+
} else newList.push(item);
|
|
1813
2358
|
}
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
const y = r(u, m, c);
|
|
1819
|
-
y.length && h.set(u.uuid, y);
|
|
1820
|
-
}), c.clear();
|
|
1821
|
-
const l = /* @__PURE__ */ new Map();
|
|
1822
|
-
h.forEach((u, m) => {
|
|
1823
|
-
if (!c.has(m) && u.length && a(m, h, u, c), u.length) {
|
|
1824
|
-
const y = u[0];
|
|
1825
|
-
l.has(y.doorUuid) || l.set(y.doorUuid, []), l.get(y.doorUuid)?.push(y), l.has(y.findUuid) || l.set(y.findUuid, []), l.get(y.findUuid)?.push(y);
|
|
2359
|
+
newList.sort((a, b) => a.doorLine.length() - b.doorLine.length());
|
|
2360
|
+
if (other && newList[0] === other) {
|
|
2361
|
+
list.splice(0);
|
|
2362
|
+
return true;
|
|
1826
2363
|
}
|
|
2364
|
+
list.splice(1);
|
|
2365
|
+
return false;
|
|
2366
|
+
}
|
|
2367
|
+
const record = /* @__PURE__ */ new Set();
|
|
2368
|
+
const snFindRecord = /* @__PURE__ */ new Map();
|
|
2369
|
+
doorPoints.map((p, index2) => {
|
|
2370
|
+
const list = find(p, index2, record);
|
|
2371
|
+
if (list.length) snFindRecord.set(p.uuid, list);
|
|
1827
2372
|
});
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
2373
|
+
record.clear();
|
|
2374
|
+
const temMap = /* @__PURE__ */ new Map();
|
|
2375
|
+
snFindRecord.forEach((list, key) => {
|
|
2376
|
+
if (!record.has(key) && list.length) {
|
|
2377
|
+
deepSearchNearHandle(key, snFindRecord, list, record);
|
|
2378
|
+
}
|
|
2379
|
+
if (list.length) {
|
|
2380
|
+
const item = list[0];
|
|
2381
|
+
if (!temMap.has(item.doorUuid)) temMap.set(item.doorUuid, []);
|
|
2382
|
+
temMap.get(item.doorUuid)?.push(item);
|
|
2383
|
+
if (!temMap.has(item.findUuid)) temMap.set(item.findUuid, []);
|
|
2384
|
+
temMap.get(item.findUuid)?.push(item);
|
|
1833
2385
|
}
|
|
1834
2386
|
});
|
|
1835
|
-
const
|
|
1836
|
-
|
|
1837
|
-
if (
|
|
1838
|
-
|
|
1839
|
-
|
|
2387
|
+
const deleteSet = /* @__PURE__ */ new Set();
|
|
2388
|
+
temMap.forEach((list) => {
|
|
2389
|
+
if (list.length > 1) {
|
|
2390
|
+
list.sort((a, b) => a.doorLine.length() - b.doorLine.length());
|
|
2391
|
+
for (let i = 1; i < list.length; i++) deleteSet.add(list[i]);
|
|
1840
2392
|
}
|
|
1841
2393
|
});
|
|
1842
|
-
const
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
2394
|
+
const searchNearRasult = [], removeDoorPointsUuid = [];
|
|
2395
|
+
snFindRecord.forEach((list) => {
|
|
2396
|
+
if (list.length) {
|
|
2397
|
+
const item = list[0];
|
|
2398
|
+
if (!deleteSet.has(item)) {
|
|
2399
|
+
searchNearRasult.push(item);
|
|
2400
|
+
removeDoorPointsUuid.push(item.doorUuid, item.findUuid);
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
});
|
|
2404
|
+
const doors = [];
|
|
2405
|
+
searchNearRasult.forEach((item) => {
|
|
2406
|
+
const doorIndex = doorPoints.findIndex((p2) => p2.uuid === item.doorUuid);
|
|
2407
|
+
const findDoorIndex = possibleDoorPoints.findIndex((p2) => p2.uuid === item.findUuid);
|
|
2408
|
+
const start = doorPoints[doorIndex].point.clone();
|
|
2409
|
+
const end = possibleDoorPoints[findDoorIndex].point.clone();
|
|
2410
|
+
const startLine = this.findLongLineSegment(doorPoints[doorIndex].line);
|
|
2411
|
+
const endLine = this.findLongLineSegment(possibleDoorPoints[findDoorIndex].line);
|
|
2412
|
+
const p = startLine.projectPoint(end);
|
|
2413
|
+
if (p) {
|
|
2414
|
+
start.copy(p);
|
|
2415
|
+
const l = new LineSegment(start, end);
|
|
2416
|
+
const angle = endLine.includedAngle(l);
|
|
2417
|
+
if (angle < 10 || angle > 170 || Math.abs(90 - angle) < 10) {
|
|
2418
|
+
doors.push({
|
|
2419
|
+
start,
|
|
2420
|
+
end
|
|
2421
|
+
});
|
|
2422
|
+
}
|
|
1852
2423
|
} else {
|
|
1853
|
-
const
|
|
1854
|
-
|
|
1855
|
-
const
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
2424
|
+
const p2 = endLine.projectPoint(start);
|
|
2425
|
+
if (p2) end.copy(p2);
|
|
2426
|
+
const l = new LineSegment(start, end);
|
|
2427
|
+
const angle = startLine.includedAngle(l);
|
|
2428
|
+
if (angle < 10 || angle > 170 || Math.abs(90 - angle) < 10) {
|
|
2429
|
+
doors.push({
|
|
2430
|
+
start,
|
|
2431
|
+
end
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
1860
2434
|
}
|
|
1861
|
-
})
|
|
2435
|
+
});
|
|
2436
|
+
possibleDoorPoints.splice(
|
|
1862
2437
|
0,
|
|
1863
|
-
|
|
1864
|
-
...
|
|
1865
|
-
)
|
|
2438
|
+
possibleDoorPoints.length,
|
|
2439
|
+
...possibleDoorPoints.filter((p) => removeDoorPointsUuid.indexOf(p.uuid) === -1)
|
|
2440
|
+
);
|
|
2441
|
+
doorPoints.splice(
|
|
1866
2442
|
0,
|
|
1867
|
-
|
|
1868
|
-
...
|
|
1869
|
-
)
|
|
2443
|
+
doorPoints.length,
|
|
2444
|
+
...doorPoints.filter((p) => removeDoorPointsUuid.indexOf(p.uuid) === -1)
|
|
2445
|
+
);
|
|
2446
|
+
return doors;
|
|
1870
2447
|
}
|
|
1871
2448
|
/** 方案二: 沿方向查找
|
|
1872
2449
|
* @description
|
|
1873
2450
|
* @param param0
|
|
1874
2451
|
* @returns
|
|
1875
2452
|
*/
|
|
1876
|
-
searchAlongDirection({ point
|
|
1877
|
-
const
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2453
|
+
searchAlongDirection({ point, line }, doorSearchDistance = this.doorSearchDistance) {
|
|
2454
|
+
const quadtree = this.quadtree;
|
|
2455
|
+
const direct = line.direction();
|
|
2456
|
+
if (line.start === point) direct.multiplyScalar(-1);
|
|
2457
|
+
const endPoint = point.clone().add(direct.clone().multiplyScalar(doorSearchDistance)), rline = new LineSegment(point.clone(), endPoint), result = quadtree.queryLineSegment(rline).map((l) => {
|
|
2458
|
+
const res = l.line.getIntersection(rline);
|
|
2459
|
+
return {
|
|
2460
|
+
point: res,
|
|
2461
|
+
line: l.line
|
|
2462
|
+
};
|
|
2463
|
+
}).filter((i) => i.point).sort((a, b) => point.distance(a.point) - point.distance(b.point));
|
|
2464
|
+
if (result.length) {
|
|
2465
|
+
const item = result[0];
|
|
2466
|
+
if (Math.abs(90 - item.line.direction().angleBetween(direct, "angle")) < 5) {
|
|
2467
|
+
return item;
|
|
2468
|
+
}
|
|
1887
2469
|
}
|
|
1888
2470
|
}
|
|
1889
2471
|
/** 方案三: 沿法线方向查找
|
|
@@ -1892,25 +2474,35 @@ class it {
|
|
|
1892
2474
|
* @param doorSearchDistance
|
|
1893
2475
|
* @returns
|
|
1894
2476
|
*/
|
|
1895
|
-
searchAlongNormalDirection({ point
|
|
1896
|
-
const
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2477
|
+
searchAlongNormalDirection({ point, line }, doorSearchDistance = this.doorSearchDistance) {
|
|
2478
|
+
const pointVirtualGrid = this.pointVirtualGrid, quadtree = this.quadtree, direct = line.direction(), normal = line.start.normal(line.end), prePoint = line.start.clone();
|
|
2479
|
+
if (line.start === point) direct.multiplyScalar(-1);
|
|
2480
|
+
if (line.start === point) prePoint.copy(line.end);
|
|
2481
|
+
const result = pointVirtualGrid.queryPoint(prePoint).filter((r) => r.userData !== line);
|
|
2482
|
+
for (let i = 0; i < result.length; i++) {
|
|
2483
|
+
const element = result[i];
|
|
2484
|
+
const l = element.userData;
|
|
2485
|
+
const d1 = l.direction();
|
|
2486
|
+
if (l.start === element.point) direct.multiplyScalar(-1);
|
|
2487
|
+
const angle = d1.angleBetween(normal) / (Math.PI / 180);
|
|
2488
|
+
if (angle > 90) {
|
|
2489
|
+
normal.multiplyScalar(-1);
|
|
1903
2490
|
break;
|
|
1904
2491
|
}
|
|
1905
2492
|
}
|
|
1906
|
-
const
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
2493
|
+
const rline3 = new LineSegment(point.clone(), point.clone().add(normal.multiplyScalar(doorSearchDistance)));
|
|
2494
|
+
const r3 = quadtree.queryLineSegment(rline3).map((l) => {
|
|
2495
|
+
const res = l.line.getIntersection(rline3);
|
|
2496
|
+
return {
|
|
2497
|
+
point: res,
|
|
2498
|
+
line: l.line
|
|
2499
|
+
};
|
|
2500
|
+
}).filter((i) => i.point).sort((a, b) => point.distance(a.point) - point.distance(b.point));
|
|
2501
|
+
if (r3.length) {
|
|
2502
|
+
const item = r3[0];
|
|
2503
|
+
if (Math.abs(90 - item.line.direction().angleBetween(normal, "angle")) < 5) {
|
|
2504
|
+
return item;
|
|
2505
|
+
}
|
|
1914
2506
|
}
|
|
1915
2507
|
}
|
|
1916
2508
|
/**
|
|
@@ -1918,31 +2510,37 @@ class it {
|
|
|
1918
2510
|
* @param line
|
|
1919
2511
|
* @returns
|
|
1920
2512
|
*/
|
|
1921
|
-
findLongLineSegment(
|
|
1922
|
-
const
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
2513
|
+
findLongLineSegment(line) {
|
|
2514
|
+
const resLine = line.clone();
|
|
2515
|
+
const res1 = this.pointVirtualGrid.queryPoint(line.start);
|
|
2516
|
+
const res2 = this.pointVirtualGrid.queryPoint(line.end);
|
|
2517
|
+
for (let i = 0; i < res1.length; i++) {
|
|
2518
|
+
const { userData: line2 } = res1[i];
|
|
2519
|
+
if (line2 === line) continue;
|
|
2520
|
+
if (line2 && line2.directionEqual(line)) {
|
|
2521
|
+
if (line2.start.equal(line.start)) resLine.start.copy(line2.end);
|
|
2522
|
+
else resLine.start.copy(line2.start);
|
|
1927
2523
|
break;
|
|
1928
2524
|
}
|
|
1929
2525
|
}
|
|
1930
|
-
for (let
|
|
1931
|
-
const { userData:
|
|
1932
|
-
if (
|
|
1933
|
-
|
|
2526
|
+
for (let i = 0; i < res2.length; i++) {
|
|
2527
|
+
const { userData: line2 } = res2[i];
|
|
2528
|
+
if (line2 === line) continue;
|
|
2529
|
+
if (line2 && line2.directionEqual(line)) {
|
|
2530
|
+
if (line2.end.equal(line.end)) resLine.end.copy(line2.start);
|
|
2531
|
+
else resLine.end.copy(line2.end);
|
|
1934
2532
|
break;
|
|
1935
2533
|
}
|
|
1936
2534
|
}
|
|
1937
|
-
return
|
|
2535
|
+
return resLine;
|
|
1938
2536
|
}
|
|
1939
2537
|
}
|
|
1940
|
-
class
|
|
2538
|
+
class LineAnalysis extends Component {
|
|
1941
2539
|
static name = "LineAnalysis";
|
|
1942
2540
|
Dxf = null;
|
|
1943
2541
|
Variable = null;
|
|
1944
2542
|
lineSegmentList = [];
|
|
1945
|
-
container = new
|
|
2543
|
+
container = new THREE.Group();
|
|
1946
2544
|
// 误差角度
|
|
1947
2545
|
errorAngle = 4;
|
|
1948
2546
|
width = 0.4;
|
|
@@ -1950,8 +2548,11 @@ class st extends X {
|
|
|
1950
2548
|
*
|
|
1951
2549
|
* @param parent
|
|
1952
2550
|
*/
|
|
1953
|
-
onAddFromParent(
|
|
1954
|
-
this.Dxf =
|
|
2551
|
+
onAddFromParent(parent) {
|
|
2552
|
+
this.Dxf = parent.findComponentByType(Dxf);
|
|
2553
|
+
this.Variable = this.parent?.findComponentByType(Variable);
|
|
2554
|
+
this.Dxf.addEventListener("setDta", this.lineAnalysis.bind(this));
|
|
2555
|
+
this.Dxf.addEventListener("createGroup", this.doorsAnalysis.bind(this));
|
|
1955
2556
|
}
|
|
1956
2557
|
/**
|
|
1957
2558
|
*
|
|
@@ -1960,16 +2561,20 @@ class st extends X {
|
|
|
1960
2561
|
* @param width
|
|
1961
2562
|
* @returns
|
|
1962
2563
|
*/
|
|
1963
|
-
expandLineSegment(
|
|
1964
|
-
const
|
|
2564
|
+
expandLineSegment(p1, p2, width = 0.1) {
|
|
2565
|
+
const normal = p2.normal(p1);
|
|
2566
|
+
const pDirect = p2.direction(p1).mutiplyScalar(width * 0.5);
|
|
2567
|
+
const nDirect = p1.direction(p2).mutiplyScalar(width * 0.5);
|
|
2568
|
+
const offsetX = normal.x * width * 0.5;
|
|
2569
|
+
const offsetY = normal.y * width * 0.5;
|
|
1965
2570
|
return {
|
|
1966
2571
|
points: [
|
|
1967
2572
|
// 第一条线
|
|
1968
|
-
new
|
|
1969
|
-
new
|
|
2573
|
+
new Point(p1.x + offsetX, p1.y + offsetY).add(nDirect),
|
|
2574
|
+
new Point(p2.x + offsetX, p2.y + offsetY).add(pDirect),
|
|
1970
2575
|
// 第二条线
|
|
1971
|
-
new
|
|
1972
|
-
new
|
|
2576
|
+
new Point(p1.x - offsetX, p1.y - offsetY).add(nDirect),
|
|
2577
|
+
new Point(p2.x - offsetX, p2.y - offsetY).add(pDirect)
|
|
1973
2578
|
],
|
|
1974
2579
|
indices: [0, 1, 1, 3, 3, 2, 2, 0],
|
|
1975
2580
|
rectIndices: [0, 1, 3, 2, 0]
|
|
@@ -1981,25 +2586,31 @@ class st extends X {
|
|
|
1981
2586
|
* @param p1
|
|
1982
2587
|
* @param p2
|
|
1983
2588
|
*/
|
|
1984
|
-
addData(
|
|
1985
|
-
const
|
|
1986
|
-
|
|
2589
|
+
addData(p1, p2) {
|
|
2590
|
+
const dxf = this.Dxf;
|
|
2591
|
+
dxf.data.push([p1.clone(), p2.clone(), [], false, dxf.data.length]);
|
|
2592
|
+
this.appendLineSegmentList.push(new LineSegment(p1.clone(), p2.clone()));
|
|
1987
2593
|
}
|
|
1988
2594
|
/** 结果分析创建矩形
|
|
1989
2595
|
* @param result
|
|
1990
2596
|
*/
|
|
1991
|
-
createRectangle(
|
|
1992
|
-
const
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2597
|
+
createRectangle(result) {
|
|
2598
|
+
const dxf = this.Dxf;
|
|
2599
|
+
const project0 = result.project, project1 = result.project2;
|
|
2600
|
+
if (project0.includedAngle(project1) > 135) {
|
|
2601
|
+
project1.points = [project1.points[1], project1.points[0]];
|
|
2602
|
+
}
|
|
2603
|
+
this.addData(project0.points[0], project1.points[0]);
|
|
2604
|
+
this.addData(project0.points[1], project1.points[1]);
|
|
2605
|
+
const leftHeight = project0.points[0].distance(project1.points[0]), rightHeight = project0.points[1].distance(project1.points[1]), count = Math.ceil(Math.max(leftHeight, rightHeight) / dxf.width), leftFragment = leftHeight / count, rightFragment = rightHeight / count, leftDirection = project1.points[0].direction(project0.points[0]), rightDirection = project1.points[1].direction(project0.points[1]), leftP = project0.points[0].clone(), rightP = project0.points[1].clone(), direction = rightP.direction(leftP);
|
|
2606
|
+
direction.multiplyScalar(dxf.width * 0.5);
|
|
2607
|
+
const _leftP = leftP.clone().add(direction), _rightP = rightP.clone().add(direction.multiplyScalar(-1)), d1 = leftP.direction(rightP), d2 = _leftP.direction(_rightP);
|
|
2608
|
+
if (d1.x > 0 && d2.x < 0 || d1.x < 0 && d2.x > 0 || d1.y > 0 && d2.y < 0 || d1.y < 0 && d2.y > 0) return;
|
|
2609
|
+
leftP.set(_leftP.x, _leftP.y);
|
|
2610
|
+
rightP.set(_rightP.x, _rightP.y);
|
|
2611
|
+
for (let i = 1; i < count; i++) {
|
|
2612
|
+
const left = leftDirection.clone().multiplyScalar(leftFragment * i), right = rightDirection.clone().multiplyScalar(rightFragment * i), p1 = leftP.clone().add(left), p2 = rightP.clone().add(right);
|
|
2613
|
+
this.addData(p1, p2);
|
|
2003
2614
|
}
|
|
2004
2615
|
}
|
|
2005
2616
|
quadtree;
|
|
@@ -2007,13 +2618,18 @@ class st extends X {
|
|
|
2007
2618
|
* 构建线段四叉树,快速查找,
|
|
2008
2619
|
*/
|
|
2009
2620
|
buildQuadtree() {
|
|
2010
|
-
const
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2621
|
+
const dxf = this.Dxf;
|
|
2622
|
+
const lineSegmentList = [];
|
|
2623
|
+
this.quadtree = new Quadtree(dxf.originalBox, 2);
|
|
2624
|
+
dxf.lineSegments.forEach((lineSegment) => {
|
|
2625
|
+
if (lineSegment.userData?.isDoor) return;
|
|
2626
|
+
this.quadtree?.insert({
|
|
2627
|
+
line: lineSegment,
|
|
2628
|
+
userData: lineSegmentList.length
|
|
2629
|
+
});
|
|
2630
|
+
lineSegmentList.push(lineSegment);
|
|
2631
|
+
});
|
|
2632
|
+
this.lineSegmentList = lineSegmentList;
|
|
2017
2633
|
}
|
|
2018
2634
|
resultList = [];
|
|
2019
2635
|
mergeWallLines = [];
|
|
@@ -2023,18 +2639,24 @@ class st extends X {
|
|
|
2023
2639
|
*/
|
|
2024
2640
|
lineAnalysis() {
|
|
2025
2641
|
this.buildQuadtree();
|
|
2026
|
-
const
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2642
|
+
const quadtree = this.quadtree;
|
|
2643
|
+
const lineSegmentList = this.lineSegmentList;
|
|
2644
|
+
const visited = /* @__PURE__ */ new Set(), resultList = [];
|
|
2645
|
+
lineSegmentList.forEach((_0, i) => {
|
|
2646
|
+
const sourceLineSegment = lineSegmentList[i], rectangle = Rectangle.fromByLineSegment(sourceLineSegment, this.width * 2, false, -0.01), ids = quadtree.queryRect(rectangle).map((i2) => i2.userData).filter((index2) => index2 !== i);
|
|
2647
|
+
ids.forEach((id) => {
|
|
2030
2648
|
try {
|
|
2031
|
-
if (
|
|
2032
|
-
const
|
|
2033
|
-
|
|
2034
|
-
|
|
2649
|
+
if (visited.has(`${i}-${id}`) || visited.has(`${id}-${i}`)) return;
|
|
2650
|
+
const res = this.projectionAnalysis(id, i, sourceLineSegment, lineSegmentList);
|
|
2651
|
+
if (res) resultList.push(res);
|
|
2652
|
+
visited.add(`${i}-${id}`);
|
|
2653
|
+
} catch (error) {
|
|
2035
2654
|
}
|
|
2036
2655
|
});
|
|
2037
|
-
})
|
|
2656
|
+
});
|
|
2657
|
+
this.appendLineSegmentList.length = 0;
|
|
2658
|
+
resultList.forEach(this.createRectangle.bind(this));
|
|
2659
|
+
this.resultList = resultList;
|
|
2038
2660
|
}
|
|
2039
2661
|
/** 线段投影分析
|
|
2040
2662
|
* @param index
|
|
@@ -2042,36 +2664,42 @@ class st extends X {
|
|
|
2042
2664
|
* @param lineSegmentList
|
|
2043
2665
|
* @returns
|
|
2044
2666
|
*/
|
|
2045
|
-
projectionAnalysis(
|
|
2046
|
-
const
|
|
2047
|
-
if (
|
|
2048
|
-
let
|
|
2049
|
-
const
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2667
|
+
projectionAnalysis(index2, sourceIndex, sourceLineSegment, lineSegmentList) {
|
|
2668
|
+
const temLineSegment = lineSegmentList[index2], direct = sourceLineSegment.direction(), temDirect = temLineSegment.direction(), angle = direct.angleBetween(temDirect) / (Math.PI / 180);
|
|
2669
|
+
if (angle < this.errorAngle || angle > 180 - this.errorAngle) {
|
|
2670
|
+
let data;
|
|
2671
|
+
const p1 = temLineSegment.projectLineSegment(sourceLineSegment), p2 = sourceLineSegment.projectLineSegment(temLineSegment);
|
|
2672
|
+
if (p1.getLength() > p2.getLength()) {
|
|
2673
|
+
data = {
|
|
2674
|
+
target: temLineSegment,
|
|
2675
|
+
targetIndex: index2,
|
|
2676
|
+
source: sourceLineSegment,
|
|
2677
|
+
sourceIndex,
|
|
2678
|
+
project: p1,
|
|
2679
|
+
project2: p2
|
|
2680
|
+
};
|
|
2681
|
+
} else {
|
|
2682
|
+
data = {
|
|
2683
|
+
target: sourceLineSegment,
|
|
2684
|
+
targetIndex: sourceIndex,
|
|
2685
|
+
source: temLineSegment,
|
|
2686
|
+
sourceIndex: index2,
|
|
2687
|
+
project: p2,
|
|
2688
|
+
project2: p1
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
if (!data || data.project.getLength() < 0.2 || data.project2.getLength() < 0.2) return;
|
|
2692
|
+
return data;
|
|
2065
2693
|
}
|
|
2066
2694
|
}
|
|
2067
2695
|
doorSearchNearAngle = 110;
|
|
2068
2696
|
doorSearchDistance = 2;
|
|
2069
2697
|
doors = [];
|
|
2070
2698
|
doorsAnalysis() {
|
|
2071
|
-
new
|
|
2699
|
+
new DoorsAnalysis(this);
|
|
2072
2700
|
}
|
|
2073
2701
|
}
|
|
2074
|
-
class
|
|
2702
|
+
class DxfSystem extends ComponentManager {
|
|
2075
2703
|
Dxf;
|
|
2076
2704
|
Variable;
|
|
2077
2705
|
wallWidth;
|
|
@@ -2080,87 +2708,116 @@ class ot extends et {
|
|
|
2080
2708
|
* @param wallWidth 输出墙壁厚度,该墙壁厚度不受缩放影响
|
|
2081
2709
|
* @param scale 原始数据缩放比例
|
|
2082
2710
|
*/
|
|
2083
|
-
constructor(
|
|
2084
|
-
super()
|
|
2711
|
+
constructor(wallWidth = 0.1, scale = 1) {
|
|
2712
|
+
super();
|
|
2713
|
+
this.environment = typeof window !== "undefined" ? "browser" : typeof global !== "undefined" ? "node" : "unknown";
|
|
2714
|
+
this.wallWidth = wallWidth;
|
|
2715
|
+
this.Dxf = new Dxf(this.wallWidth, scale);
|
|
2716
|
+
this.Variable = new Variable();
|
|
2717
|
+
this.addComponent(this.Variable);
|
|
2718
|
+
this.addComponent(this.Dxf);
|
|
2719
|
+
this.addComponent(new LineAnalysis());
|
|
2085
2720
|
}
|
|
2086
|
-
usePlugin(
|
|
2087
|
-
|
|
2721
|
+
usePlugin(plugin) {
|
|
2722
|
+
if (typeof plugin === "function") plugin.call(this, this);
|
|
2723
|
+
return this;
|
|
2088
2724
|
}
|
|
2089
2725
|
destroy() {
|
|
2090
|
-
[...this.components].forEach((
|
|
2091
|
-
|
|
2092
|
-
})
|
|
2093
|
-
|
|
2726
|
+
[...this.components].forEach((com) => {
|
|
2727
|
+
com.destroy();
|
|
2728
|
+
});
|
|
2729
|
+
[...this.components].forEach((com) => {
|
|
2730
|
+
this.removeComponent(com);
|
|
2094
2731
|
});
|
|
2095
2732
|
}
|
|
2096
2733
|
}
|
|
2097
|
-
const
|
|
2098
|
-
|
|
2099
|
-
|
|
2734
|
+
const exporter = new OBJExporter();
|
|
2735
|
+
const glbExporter = new GLTFExporter();
|
|
2736
|
+
function lineSqueezing(p1, p2, width = 0.1) {
|
|
2737
|
+
const normal = p2.normal(p1);
|
|
2738
|
+
const pDirect = p2.direction(p1).mutiplyScalar(width * 0.5);
|
|
2739
|
+
const nDirect = p1.direction(p2).mutiplyScalar(width * 0.5);
|
|
2740
|
+
const offsetX = normal.x * width * 0.5;
|
|
2741
|
+
const offsetY = normal.y * width * 0.5;
|
|
2100
2742
|
return {
|
|
2101
2743
|
points: [
|
|
2102
2744
|
// 第一条线
|
|
2103
|
-
new
|
|
2104
|
-
new
|
|
2745
|
+
new Point(p1.x + offsetX, p1.y + offsetY).add(nDirect),
|
|
2746
|
+
new Point(p2.x + offsetX, p2.y + offsetY).add(pDirect),
|
|
2105
2747
|
// 第二条线
|
|
2106
|
-
new
|
|
2107
|
-
new
|
|
2748
|
+
new Point(p1.x - offsetX, p1.y - offsetY).add(nDirect),
|
|
2749
|
+
new Point(p2.x - offsetX, p2.y - offsetY).add(pDirect)
|
|
2108
2750
|
],
|
|
2109
2751
|
indices: [0, 1, 1, 3, 3, 2, 2, 0],
|
|
2110
2752
|
rectIndices: [0, 1, 3, 2, 0]
|
|
2111
2753
|
};
|
|
2112
2754
|
}
|
|
2113
|
-
class
|
|
2755
|
+
class WhiteModel extends Component {
|
|
2114
2756
|
static name = "WhiteModel";
|
|
2115
2757
|
Dxf = null;
|
|
2116
2758
|
Variable = null;
|
|
2117
2759
|
// dxf数据白模
|
|
2118
|
-
whiteModelGroup = new
|
|
2760
|
+
whiteModelGroup = new THREE.Group();
|
|
2119
2761
|
// dxf数据白模边缘线
|
|
2120
|
-
whiteModelLineGroup = new
|
|
2762
|
+
whiteModelLineGroup = new THREE.Group();
|
|
2121
2763
|
// 原始数据白模
|
|
2122
|
-
originalWhiteMode = new
|
|
2123
|
-
material = new
|
|
2124
|
-
onAddFromParent(
|
|
2125
|
-
this.Dxf =
|
|
2764
|
+
originalWhiteMode = new THREE.Group();
|
|
2765
|
+
material = new THREE.MeshBasicMaterial({ color: 16777215, transparent: true, opacity: 0.8, side: THREE.DoubleSide });
|
|
2766
|
+
onAddFromParent(parent) {
|
|
2767
|
+
this.Dxf = parent.findComponentByName("Dxf");
|
|
2768
|
+
this.Variable = parent.findComponentByName("Variable");
|
|
2769
|
+
this.originalWhiteMode.visible = false;
|
|
2770
|
+
this.Dxf?.addEventListener("lineOffset", () => {
|
|
2126
2771
|
this.updateModel();
|
|
2127
2772
|
});
|
|
2128
2773
|
}
|
|
2129
2774
|
updateModel() {
|
|
2130
|
-
this.Variable?.set("whiteModelVisible",
|
|
2131
|
-
const
|
|
2132
|
-
this.originalWhiteMode.clear()
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2775
|
+
this.Variable?.set("whiteModelVisible", false);
|
|
2776
|
+
const dxf = this.Dxf;
|
|
2777
|
+
this.originalWhiteMode.clear();
|
|
2778
|
+
this.whiteModelGroup.clear();
|
|
2779
|
+
this.whiteModelLineGroup.clear();
|
|
2780
|
+
this.whiteModelGroup.add(this.whiteModelLineGroup);
|
|
2781
|
+
this.whiteModelGroup.position.z = dxf.originalZAverage;
|
|
2782
|
+
this.originalWhiteMode.position.z = dxf.originalZAverage;
|
|
2783
|
+
dxf.wallsGroup.forEach((points) => {
|
|
2784
|
+
const shape = new THREE.Shape();
|
|
2785
|
+
points.forEach((p, i) => i === 0 ? shape.moveTo(p.x / dxf.scale, p.y / dxf.scale) : shape.lineTo(p.x / dxf.scale, p.y / dxf.scale));
|
|
2786
|
+
const geometry = new THREE.ExtrudeGeometry(shape, {
|
|
2136
2787
|
depth: 2.8,
|
|
2137
2788
|
bevelSize: 0
|
|
2138
|
-
})
|
|
2139
|
-
|
|
2140
|
-
|
|
2789
|
+
});
|
|
2790
|
+
const mesh = new THREE.Mesh(geometry, this.material);
|
|
2791
|
+
this.whiteModelGroup.add(mesh);
|
|
2792
|
+
this.whiteModelLineGroup.add(
|
|
2793
|
+
new THREE.LineSegments(new THREE.EdgesGeometry(geometry), new THREE.LineBasicMaterial({ color: 0 }))
|
|
2141
2794
|
);
|
|
2142
|
-
})
|
|
2143
|
-
|
|
2795
|
+
});
|
|
2796
|
+
const walls = dxf.originalData.map(({ start, end, insetionArr }) => {
|
|
2797
|
+
const startVec3 = new Point(start.x, start.y).mutiplyScalar(dxf.scale), endVec3 = new Point(end.x, end.y).mutiplyScalar(dxf.scale), { points, indices, rectIndices } = lineSqueezing(startVec3, endVec3, dxf.width);
|
|
2144
2798
|
return {
|
|
2145
|
-
points
|
|
2146
|
-
indices
|
|
2147
|
-
rectIndices
|
|
2148
|
-
insetions: (
|
|
2799
|
+
points,
|
|
2800
|
+
indices,
|
|
2801
|
+
rectIndices,
|
|
2802
|
+
insetions: (insetionArr ?? []).map((insetion) => insetion.index)
|
|
2149
2803
|
};
|
|
2150
|
-
})
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2804
|
+
});
|
|
2805
|
+
walls.forEach((wall) => {
|
|
2806
|
+
const shape = new THREE.Shape();
|
|
2807
|
+
wall.rectIndices.forEach((index2, i) => {
|
|
2808
|
+
const p = wall.points[index2];
|
|
2809
|
+
if (i === 0) shape.moveTo(p.x, p.y);
|
|
2810
|
+
else shape.lineTo(p.x, p.y);
|
|
2155
2811
|
});
|
|
2156
|
-
const
|
|
2812
|
+
const geometry = new THREE.ExtrudeGeometry(shape, {
|
|
2157
2813
|
depth: 2.8,
|
|
2158
2814
|
bevelSize: 0
|
|
2159
2815
|
});
|
|
2160
|
-
if (
|
|
2161
|
-
const
|
|
2162
|
-
this.originalWhiteMode?.add(
|
|
2163
|
-
})
|
|
2816
|
+
if (geometry.attributes.position.array.filter((num) => Number.isNaN(num)).length) return;
|
|
2817
|
+
const mesh = new THREE.Mesh(geometry);
|
|
2818
|
+
this.originalWhiteMode?.add(mesh);
|
|
2819
|
+
});
|
|
2820
|
+
this.dispatchEvent({
|
|
2164
2821
|
type: "updateModel",
|
|
2165
2822
|
originalWhiteMode: this.originalWhiteMode,
|
|
2166
2823
|
whiteModelGroup: this.whiteModelGroup
|
|
@@ -2171,9 +2828,13 @@ class J extends X {
|
|
|
2171
2828
|
* @returns
|
|
2172
2829
|
*/
|
|
2173
2830
|
toOBJ() {
|
|
2174
|
-
return new Promise((
|
|
2175
|
-
this.material.opacity = 1
|
|
2176
|
-
|
|
2831
|
+
return new Promise((resolve) => {
|
|
2832
|
+
this.material.opacity = 1;
|
|
2833
|
+
this.material.needsUpdate = true;
|
|
2834
|
+
setTimeout(() => {
|
|
2835
|
+
resolve(exporter.parse(this.whiteModelGroup));
|
|
2836
|
+
this.material.opacity = 0.8;
|
|
2837
|
+
this.material.transparent = true;
|
|
2177
2838
|
}, 20);
|
|
2178
2839
|
});
|
|
2179
2840
|
}
|
|
@@ -2182,28 +2843,38 @@ class J extends X {
|
|
|
2182
2843
|
* @param binary
|
|
2183
2844
|
* @returns
|
|
2184
2845
|
*/
|
|
2185
|
-
toGltf(
|
|
2186
|
-
return new Promise((
|
|
2187
|
-
this.material.opacity = 1
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2846
|
+
toGltf(binary = true) {
|
|
2847
|
+
return new Promise((resolve) => {
|
|
2848
|
+
this.material.opacity = 1;
|
|
2849
|
+
this.material.needsUpdate = true;
|
|
2850
|
+
setTimeout(async () => {
|
|
2851
|
+
if (typeof window === "object") {
|
|
2852
|
+
glbExporter.parse(this.whiteModelGroup.children, (gltf) => {
|
|
2853
|
+
resolve(gltf);
|
|
2854
|
+
this.material.opacity = 0.8;
|
|
2855
|
+
this.material.transparent = true;
|
|
2191
2856
|
}, () => {
|
|
2192
|
-
|
|
2857
|
+
resolve(void 0);
|
|
2193
2858
|
}, {
|
|
2194
|
-
binary
|
|
2859
|
+
binary
|
|
2195
2860
|
});
|
|
2196
|
-
else if (typeof global
|
|
2861
|
+
} else if (typeof global !== "function") {
|
|
2197
2862
|
try {
|
|
2198
|
-
const
|
|
2199
|
-
|
|
2200
|
-
const
|
|
2201
|
-
|
|
2863
|
+
const obj2gltf = await include("obj2gltf", true);
|
|
2864
|
+
const fs = await include("fs", false);
|
|
2865
|
+
const obj = await this.toOBJ();
|
|
2866
|
+
fs.writeFileSync(this.uuid, obj);
|
|
2867
|
+
const result = await obj2gltf(this.uuid, {
|
|
2868
|
+
binary
|
|
2202
2869
|
});
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2870
|
+
fs.unlinkSync(this.uuid);
|
|
2871
|
+
if (binary) resolve(result);
|
|
2872
|
+
else resolve(JSON.stringify(result));
|
|
2873
|
+
} catch (error) {
|
|
2874
|
+
resolve(void 0);
|
|
2875
|
+
console.log(error);
|
|
2206
2876
|
}
|
|
2877
|
+
}
|
|
2207
2878
|
}, 20);
|
|
2208
2879
|
});
|
|
2209
2880
|
}
|
|
@@ -2212,33 +2883,40 @@ class J extends X {
|
|
|
2212
2883
|
* @returns
|
|
2213
2884
|
*/
|
|
2214
2885
|
async toOBJBlob() {
|
|
2215
|
-
const
|
|
2216
|
-
if (
|
|
2217
|
-
return new Blob([
|
|
2886
|
+
const buffer = await this.toOBJ();
|
|
2887
|
+
if (buffer) {
|
|
2888
|
+
return new Blob([buffer], { type: "application/octet-stream" });
|
|
2889
|
+
}
|
|
2218
2890
|
}
|
|
2219
2891
|
/**
|
|
2220
2892
|
* 转为 GltfBlob
|
|
2221
2893
|
* @returns
|
|
2222
2894
|
*/
|
|
2223
|
-
async toGltfBlob(
|
|
2224
|
-
const
|
|
2225
|
-
if (
|
|
2226
|
-
return new Blob([
|
|
2895
|
+
async toGltfBlob(binary = true) {
|
|
2896
|
+
const buffer = await this.toGltf(binary);
|
|
2897
|
+
if (buffer) {
|
|
2898
|
+
return new Blob([buffer], { type: "application/octet-stream" });
|
|
2899
|
+
}
|
|
2227
2900
|
}
|
|
2228
2901
|
/**
|
|
2229
2902
|
* 下载 OBJ
|
|
2230
2903
|
* @param filename
|
|
2231
2904
|
* @returns
|
|
2232
2905
|
*/
|
|
2233
|
-
async downloadOBJ(
|
|
2234
|
-
if (typeof window
|
|
2235
|
-
const
|
|
2236
|
-
if (!
|
|
2237
|
-
const
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2906
|
+
async downloadOBJ(filename) {
|
|
2907
|
+
if (typeof window !== "undefined") {
|
|
2908
|
+
const blob = await this.toOBJBlob();
|
|
2909
|
+
if (!blob) return;
|
|
2910
|
+
const a = document.createElement("a");
|
|
2911
|
+
a.href = URL.createObjectURL(blob);
|
|
2912
|
+
a.download = filename;
|
|
2913
|
+
a.click();
|
|
2914
|
+
} else if (typeof global !== "undefined") {
|
|
2915
|
+
const buffer = await this.toOBJ();
|
|
2916
|
+
if (buffer) {
|
|
2917
|
+
const fs = await include("fs", false);
|
|
2918
|
+
fs.writeFileSync(filename, buffer);
|
|
2919
|
+
}
|
|
2242
2920
|
}
|
|
2243
2921
|
}
|
|
2244
2922
|
/**
|
|
@@ -2246,19 +2924,24 @@ class J extends X {
|
|
|
2246
2924
|
* @param filename
|
|
2247
2925
|
* @returns
|
|
2248
2926
|
*/
|
|
2249
|
-
async downloadGltf(
|
|
2250
|
-
if (typeof window
|
|
2251
|
-
const
|
|
2252
|
-
if (!
|
|
2253
|
-
const
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2927
|
+
async downloadGltf(filename, binary = true) {
|
|
2928
|
+
if (typeof window !== "undefined") {
|
|
2929
|
+
const blob = await this.toGltfBlob(binary);
|
|
2930
|
+
if (!blob) return;
|
|
2931
|
+
const a = document.createElement("a");
|
|
2932
|
+
a.href = URL.createObjectURL(blob);
|
|
2933
|
+
a.download = filename;
|
|
2934
|
+
a.click();
|
|
2935
|
+
} else if (typeof global !== "undefined") {
|
|
2936
|
+
const buffer = await this.toGltf(binary);
|
|
2937
|
+
if (buffer) {
|
|
2938
|
+
const fs = await include("fs", false);
|
|
2939
|
+
fs.writeFileSync(filename, binary ? buffer : Buffer.from(buffer));
|
|
2940
|
+
}
|
|
2258
2941
|
}
|
|
2259
2942
|
}
|
|
2260
2943
|
}
|
|
2261
|
-
class
|
|
2944
|
+
class DetailsPoint extends Component {
|
|
2262
2945
|
static name = "DetailsPoint";
|
|
2263
2946
|
Dxf = null;
|
|
2264
2947
|
WhiteModel = null;
|
|
@@ -2266,8 +2949,10 @@ class _ extends X {
|
|
|
2266
2949
|
desPoints = [];
|
|
2267
2950
|
raylines = [];
|
|
2268
2951
|
data = [];
|
|
2269
|
-
onAddFromParent(
|
|
2270
|
-
this.Dxf =
|
|
2952
|
+
onAddFromParent(parent) {
|
|
2953
|
+
this.Dxf = parent.findComponentByName("Dxf");
|
|
2954
|
+
this.Variable = parent.findComponentByName("Variable");
|
|
2955
|
+
this.Dxf?.addEventListener("setDta", () => {
|
|
2271
2956
|
this.updateModel();
|
|
2272
2957
|
});
|
|
2273
2958
|
}
|
|
@@ -2275,29 +2960,37 @@ class _ extends X {
|
|
|
2275
2960
|
* 设置值
|
|
2276
2961
|
* @param data
|
|
2277
2962
|
*/
|
|
2278
|
-
async set(
|
|
2279
|
-
if (typeof
|
|
2280
|
-
if (typeof global
|
|
2281
|
-
const
|
|
2963
|
+
async set(data) {
|
|
2964
|
+
if (typeof data === "string") {
|
|
2965
|
+
if (typeof global !== "undefined") {
|
|
2966
|
+
const packageName = "fs";
|
|
2967
|
+
const { default: fs } = await import(
|
|
2282
2968
|
/* @vite-ignore */
|
|
2283
|
-
|
|
2284
|
-
)
|
|
2285
|
-
|
|
2969
|
+
packageName
|
|
2970
|
+
);
|
|
2971
|
+
const buffer = fs.readFileSync(data);
|
|
2972
|
+
const json = JSON.parse(buffer.toString("utf-8"));
|
|
2973
|
+
this.set(json);
|
|
2286
2974
|
return;
|
|
2287
|
-
} else
|
|
2975
|
+
} else {
|
|
2288
2976
|
throw new Error("非node环境不允许使用路径");
|
|
2289
|
-
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
this.data = data;
|
|
2980
|
+
this.updateModel();
|
|
2290
2981
|
}
|
|
2291
2982
|
/**
|
|
2292
2983
|
* 设置射线辅助
|
|
2293
2984
|
*/
|
|
2294
|
-
racasterHelper(
|
|
2985
|
+
racasterHelper(position, direction, far) {
|
|
2295
2986
|
this.raylines.push([
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
])
|
|
2299
|
-
|
|
2300
|
-
|
|
2987
|
+
position.clone(),
|
|
2988
|
+
position.clone().add(direction.clone().multiplyScalar(far))
|
|
2989
|
+
]);
|
|
2990
|
+
direction.z = 0;
|
|
2991
|
+
this.raylines.push([
|
|
2992
|
+
position.clone(),
|
|
2993
|
+
position.clone().add(direction.clone().multiplyScalar(far))
|
|
2301
2994
|
]);
|
|
2302
2995
|
}
|
|
2303
2996
|
_timer = null;
|
|
@@ -2305,133 +2998,161 @@ class _ extends X {
|
|
|
2305
2998
|
* 更新模型
|
|
2306
2999
|
*/
|
|
2307
3000
|
updateModel() {
|
|
2308
|
-
|
|
3001
|
+
if (this._timer) clearTimeout(this._timer);
|
|
3002
|
+
this._timer = setTimeout(() => {
|
|
2309
3003
|
this._timer = null;
|
|
2310
|
-
const
|
|
2311
|
-
this.raylines.length = 0
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
3004
|
+
const whiteModel = this.parent?.findComponentByName("WhiteModel");
|
|
3005
|
+
this.raylines.length = 0;
|
|
3006
|
+
this.desPoints.length = 0;
|
|
3007
|
+
this.data.forEach((item) => {
|
|
3008
|
+
const position = new THREE.Vector3(
|
|
3009
|
+
item.position.x,
|
|
3010
|
+
item.position.y,
|
|
3011
|
+
item.position.z
|
|
3012
|
+
);
|
|
3013
|
+
const direction = new THREE.Vector3(
|
|
3014
|
+
item.direction.x,
|
|
3015
|
+
item.direction.y,
|
|
3016
|
+
item.direction.z
|
|
3017
|
+
);
|
|
3018
|
+
const far = 100;
|
|
3019
|
+
this.racasterHelper(position, direction, far);
|
|
3020
|
+
direction.z = 0;
|
|
3021
|
+
const raycaster = new THREE.Raycaster(position, direction, 0, far);
|
|
3022
|
+
const list = raycaster.intersectObject(whiteModel.originalWhiteMode);
|
|
3023
|
+
if (list.length) {
|
|
3024
|
+
const { point } = list[0];
|
|
2325
3025
|
this.desPoints.push({
|
|
2326
|
-
message:
|
|
2327
|
-
position
|
|
2328
|
-
intersection:
|
|
3026
|
+
message: item.desc,
|
|
3027
|
+
position,
|
|
3028
|
+
intersection: point
|
|
2329
3029
|
});
|
|
2330
3030
|
}
|
|
2331
|
-
})
|
|
3031
|
+
});
|
|
3032
|
+
this.dispatchEvent({
|
|
2332
3033
|
type: "handleSuccess",
|
|
2333
3034
|
desPoints: this.desPoints
|
|
2334
3035
|
});
|
|
2335
3036
|
}, 50);
|
|
2336
3037
|
}
|
|
2337
3038
|
}
|
|
2338
|
-
class
|
|
3039
|
+
class DxfLineModel extends Component {
|
|
2339
3040
|
static name = "DxfLineModel";
|
|
2340
|
-
dxfLineModel = new
|
|
2341
|
-
dxfDoorsLineModel = new
|
|
2342
|
-
dxfModelGroup = new
|
|
2343
|
-
onAddFromParent(
|
|
2344
|
-
const
|
|
2345
|
-
this.dxfModelGroup.add(this.dxfLineModel)
|
|
3041
|
+
dxfLineModel = new THREE.LineSegments();
|
|
3042
|
+
dxfDoorsLineModel = new THREE.LineSegments();
|
|
3043
|
+
dxfModelGroup = new THREE.Group();
|
|
3044
|
+
onAddFromParent(parent) {
|
|
3045
|
+
const dxf = parent.findComponentByName("Dxf");
|
|
3046
|
+
this.dxfModelGroup.add(this.dxfLineModel);
|
|
3047
|
+
this.dxfModelGroup.add(this.dxfDoorsLineModel);
|
|
3048
|
+
this.dxfDoorsLineModel.material = new THREE.LineBasicMaterial({ color: 16776960, vertexColors: true });
|
|
3049
|
+
dxf?.addEventListener("lineOffset", () => this.updateMode());
|
|
2346
3050
|
}
|
|
2347
3051
|
updateMode() {
|
|
2348
|
-
const
|
|
3052
|
+
const dxf = this.parent?.findComponentByName("Dxf");
|
|
2349
3053
|
this.dxfLineModel.clear();
|
|
2350
|
-
const
|
|
2351
|
-
this.dxfLineModel.geometry = new
|
|
2352
|
-
const
|
|
2353
|
-
|
|
2354
|
-
).map((
|
|
2355
|
-
|
|
3054
|
+
const dxfArray = dxf.to3DArray(1 / dxf.scale, 0);
|
|
3055
|
+
this.dxfLineModel.geometry = new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(dxfArray, 3, true));
|
|
3056
|
+
const doorsArray = new Float32Array(
|
|
3057
|
+
dxf.doorLineSegment.flatMap(({ start, end }) => [start.x, start.y, 0, end.x, end.y, 0])
|
|
3058
|
+
).map((n) => n / dxf.scale);
|
|
3059
|
+
const doorsColorArray = new Float32Array(dxf.doorLineSegment.flatMap(() => [1, 0, 0, 0, 1, 0]));
|
|
3060
|
+
this.dxfDoorsLineModel.geometry = new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(doorsArray, 3, true)).setAttribute("color", new THREE.BufferAttribute(doorsColorArray, 3));
|
|
3061
|
+
this.dxfModelGroup.position.z = dxf.originalZAverage;
|
|
3062
|
+
this.dispatchEvent({
|
|
2356
3063
|
type: "modelUpdate",
|
|
2357
3064
|
model: this.dxfModelGroup
|
|
2358
3065
|
});
|
|
2359
3066
|
}
|
|
2360
3067
|
}
|
|
2361
|
-
const
|
|
3068
|
+
const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2362
3069
|
__proto__: null,
|
|
2363
|
-
DetailsPoint
|
|
2364
|
-
DxfLineModel
|
|
2365
|
-
WhiteModel
|
|
3070
|
+
DetailsPoint,
|
|
3071
|
+
DxfLineModel,
|
|
3072
|
+
WhiteModel
|
|
2366
3073
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
2367
|
-
function
|
|
3074
|
+
function ModelDataPlugin_(dxfSystem, option = {}) {
|
|
2368
3075
|
const {
|
|
2369
|
-
detailsPoint
|
|
2370
|
-
whiteModel
|
|
2371
|
-
dxfLineModel
|
|
2372
|
-
} =
|
|
2373
|
-
|
|
3076
|
+
detailsPoint = true,
|
|
3077
|
+
whiteModel = true,
|
|
3078
|
+
dxfLineModel = true
|
|
3079
|
+
} = option;
|
|
3080
|
+
dxfLineModel && dxfSystem.addComponent(new DxfLineModel());
|
|
3081
|
+
whiteModel && dxfSystem.addComponent(new WhiteModel());
|
|
3082
|
+
detailsPoint && dxfSystem.addComponent(new DetailsPoint());
|
|
2374
3083
|
}
|
|
2375
|
-
const
|
|
2376
|
-
create(
|
|
2377
|
-
return (
|
|
3084
|
+
const ModelDataPlugin = Object.assign(ModelDataPlugin_, {
|
|
3085
|
+
create(option = {}) {
|
|
3086
|
+
return (dxfSystem) => ModelDataPlugin_(dxfSystem, option);
|
|
2378
3087
|
}
|
|
2379
|
-
})
|
|
3088
|
+
});
|
|
3089
|
+
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2380
3090
|
__proto__: null,
|
|
2381
|
-
ModelDataPlugin
|
|
2382
|
-
components:
|
|
3091
|
+
ModelDataPlugin,
|
|
3092
|
+
components: index$1
|
|
2383
3093
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
2384
|
-
function
|
|
3094
|
+
function loadRenderPlugin() {
|
|
2385
3095
|
return import("./index2.js");
|
|
2386
3096
|
}
|
|
2387
|
-
function
|
|
3097
|
+
function loadEditorPlugin() {
|
|
2388
3098
|
return import("./index3.js");
|
|
2389
3099
|
}
|
|
2390
|
-
let
|
|
2391
|
-
async function
|
|
2392
|
-
const
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
3100
|
+
let gloabalDxfSystem = null;
|
|
3101
|
+
async function createEditor(dom, camera, orbitControls = false, viewPermission) {
|
|
3102
|
+
const mp = await Promise.resolve().then(() => index);
|
|
3103
|
+
const rp = await loadRenderPlugin();
|
|
3104
|
+
const editor = await loadEditorPlugin();
|
|
3105
|
+
const dxfSystem = new DxfSystem().usePlugin(mp.ModelDataPlugin.create({
|
|
3106
|
+
detailsPoint: false,
|
|
3107
|
+
whiteModel: true
|
|
3108
|
+
})).usePlugin(rp.RenderPlugin.create({
|
|
3109
|
+
originalLine: false,
|
|
3110
|
+
modelData: false,
|
|
3111
|
+
detailsPoint: false,
|
|
3112
|
+
orbitControls,
|
|
3113
|
+
camera
|
|
3114
|
+
})).usePlugin(editor.Editor.create({ viewPermission }));
|
|
3115
|
+
const domContainer = dxfSystem.findComponentByType(rp.components.DomContainer);
|
|
3116
|
+
domContainer && dom.appendChild(domContainer.domElement);
|
|
3117
|
+
gloabalDxfSystem = dxfSystem;
|
|
3118
|
+
return {
|
|
3119
|
+
dxfSystem,
|
|
3120
|
+
getFileAll: () => getFileAll(dxfSystem)
|
|
2405
3121
|
};
|
|
2406
3122
|
}
|
|
2407
|
-
async function
|
|
2408
|
-
const
|
|
3123
|
+
async function getFileAll(dxfSystem = gloabalDxfSystem) {
|
|
3124
|
+
const whiteModel = dxfSystem.findComponentByName("WhiteModel");
|
|
3125
|
+
const dxf = new File([dxfSystem.Dxf.toDxfBlob()], "dxf.dxf", { type: "application/dxf" });
|
|
3126
|
+
const obj = new File([await whiteModel.toOBJBlob()], "model.obj", { type: "application/octet-stream" });
|
|
3127
|
+
const glb = new File([await whiteModel.toGltfBlob(true)], "model.glb", { type: "application/octet-stream" });
|
|
3128
|
+
const gltf = new File([await whiteModel.toGltfBlob(false)], "model.gltf", { type: "application/json" });
|
|
3129
|
+
const json = new File([JSON.stringify(dxfSystem.Dxf.originalData)], "json.json", { type: "application/json" });
|
|
2409
3130
|
return {
|
|
2410
|
-
dxf
|
|
2411
|
-
obj
|
|
2412
|
-
glb
|
|
2413
|
-
gltf
|
|
2414
|
-
json
|
|
3131
|
+
dxf,
|
|
3132
|
+
obj,
|
|
3133
|
+
glb,
|
|
3134
|
+
gltf,
|
|
3135
|
+
json
|
|
2415
3136
|
};
|
|
2416
3137
|
}
|
|
2417
|
-
function
|
|
2418
|
-
return
|
|
3138
|
+
function getGlobalDxfSystem() {
|
|
3139
|
+
return gloabalDxfSystem;
|
|
2419
3140
|
}
|
|
2420
3141
|
export {
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
3142
|
+
Box2 as B,
|
|
3143
|
+
Component as C,
|
|
3144
|
+
DxfSystem as D,
|
|
3145
|
+
EventDispatcher as E,
|
|
3146
|
+
LineSegment as L,
|
|
3147
|
+
ModelDataPlugin as M,
|
|
3148
|
+
Point as P,
|
|
3149
|
+
Quadtree as Q,
|
|
3150
|
+
Variable as V,
|
|
3151
|
+
WhiteModel as W,
|
|
3152
|
+
DetailsPoint as a,
|
|
3153
|
+
PointVirtualGrid as b,
|
|
3154
|
+
createEditor as c,
|
|
3155
|
+
getGlobalDxfSystem as d,
|
|
3156
|
+
getFileAll as g,
|
|
3157
|
+
index$1 as i
|
|
2437
3158
|
};
|