build-dxf 0.0.8 → 0.0.10

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.
@@ -1,1184 +0,0 @@
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
- function uuid() {
6
- return "xxxx-xxxx-4xxx-yxxx-xxxx".replace(/[xy]/g, function(c) {
7
- var r = Math.random() * 16 | 0, v = c == "x" ? r : r & 3 | 8;
8
- return v.toString(16);
9
- });
10
- }
11
- class EventDispatcher extends EventDispatcher$1 {
12
- uuid = uuid();
13
- }
14
- class Component extends EventDispatcher {
15
- parent;
16
- constructor(...arg) {
17
- super();
18
- this.addEventListener("addFromParent", (e) => {
19
- this.parent = e.parent;
20
- this.onAddFromParent(e.parent);
21
- });
22
- this.addEventListener("removeFromParent", (e) => {
23
- this.parent = void 0;
24
- this.onRemoveFromParent(e.parent);
25
- });
26
- }
27
- onAddFromParent(parent) {
28
- }
29
- onRemoveFromParent(parent) {
30
- }
31
- destroy() {
32
- }
33
- }
34
- class Point {
35
- x;
36
- y;
37
- get X() {
38
- return this.x;
39
- }
40
- get Y() {
41
- return this.y;
42
- }
43
- /**
44
- *
45
- * @param x
46
- * @param y
47
- */
48
- constructor(x = 0, y = 0) {
49
- this.x = x;
50
- this.y = y;
51
- }
52
- set(x, y) {
53
- this.x = x;
54
- this.y = y;
55
- return this;
56
- }
57
- setX(x) {
58
- this.x = x;
59
- return this;
60
- }
61
- setY(y) {
62
- this.y = y;
63
- return this;
64
- }
65
- /**
66
- *
67
- * @param point
68
- * @returns
69
- */
70
- equal(point) {
71
- return point.x === this.x && point.y === this.y;
72
- }
73
- /**
74
- *
75
- * @param arr
76
- */
77
- setByArray(arr) {
78
- this.x = arr[0];
79
- this.y = arr[1];
80
- return this;
81
- }
82
- /**
83
- *
84
- */
85
- toArray() {
86
- return [this.x, this.y];
87
- }
88
- /**
89
- * multiplyScalar
90
- * @param scalar
91
- */
92
- mutiplyScalar(scalar) {
93
- this.x *= scalar;
94
- this.y *= scalar;
95
- return this;
96
- }
97
- multiplyScalar(scalar) {
98
- this.x *= scalar;
99
- this.y *= scalar;
100
- return this;
101
- }
102
- /**
103
- *
104
- * @param scalar
105
- * @returns
106
- */
107
- divisionScalar(scalar) {
108
- this.x /= scalar;
109
- this.y /= scalar;
110
- return this;
111
- }
112
- /**
113
- * 减法
114
- * @description 将当前点的坐标减去指定点的坐标
115
- * @param point
116
- * @returns
117
- */
118
- division(point) {
119
- this.x -= point.x;
120
- this.y -= point.y;
121
- return this;
122
- }
123
- /**
124
- * 加法
125
- * @description 将当前点的坐标加上指定点的坐标
126
- * @param point
127
- * @returns
128
- */
129
- add(point) {
130
- this.x += point.x;
131
- this.y += point.y;
132
- return this;
133
- }
134
- /**
135
- * 保留小数位数
136
- * @param count
137
- */
138
- fixed(count) {
139
- this.x = Number(this.x.toFixed(count));
140
- this.y = Number(this.y.toFixed(count));
141
- return this;
142
- }
143
- /**
144
- * 归一化
145
- * @description 将当前点的坐标归一化为单位向量
146
- * @returns
147
- */
148
- normalize() {
149
- const length = Math.sqrt(this.x * this.x + this.y * this.y);
150
- if (length === 0) return this;
151
- this.x /= length;
152
- this.y /= length;
153
- return this;
154
- }
155
- /**
156
- * 获取单位法向量
157
- * @description 计算当前点到指定点的方向向量,并返回逆时针旋转90度后的单位法向量
158
- * @param point
159
- * @returns
160
- */
161
- normal(point) {
162
- const dx = this.x - point.x;
163
- const dy = this.y - point.y;
164
- const length = Math.sqrt(dx * dx + dy * dy);
165
- const nx = -dy / length;
166
- const ny = dx / length;
167
- return new Point(nx, ny);
168
- }
169
- /**
170
- * 获取由传入的点到该点的单位方向向量
171
- * @description 计算当前点到指定点的方向向量,并返回单位方向
172
- * @param point
173
- * @returns
174
- */
175
- direction(point) {
176
- const dx = this.x - point.x;
177
- const dy = this.y - point.y;
178
- const length = Math.sqrt(dx * dx + dy * dy);
179
- if (length === 0) return new Point(0, 0);
180
- return new Point(dx / length, dy / length);
181
- }
182
- /**
183
- * 计算模长
184
- * @returns
185
- */
186
- magnitude() {
187
- return Math.sqrt(this.x * this.x + this.y * this.y);
188
- }
189
- /**
190
- * 计算点点积
191
- * @param point
192
- * @returns
193
- */
194
- dot(point) {
195
- return this.x * point.x + this.y * point.y;
196
- }
197
- /** 计算两个向量夹角
198
- * @description 公式:a · b = |a| × |b| × cosθ
199
- * @param point
200
- * @returns
201
- */
202
- angleBetween(point) {
203
- const dotProduct = this.dot(point);
204
- const magnitude1 = this.magnitude();
205
- const magnitude2 = point.magnitude();
206
- if (magnitude1 === 0 || magnitude2 === 0) return 0;
207
- const cosTheta = dotProduct / (magnitude1 * magnitude2);
208
- const clampedCosTheta = Math.max(-1, Math.min(1, cosTheta));
209
- return Math.acos(clampedCosTheta);
210
- }
211
- /** 获取向量长度
212
- */
213
- length() {
214
- const magnitude = Math.sqrt(this.x * this.x + this.y * this.y);
215
- if (Math.abs(magnitude - Math.round(magnitude)) < 1e-9) {
216
- return Math.round(magnitude);
217
- }
218
- return magnitude;
219
- }
220
- /**
221
- * 获取两个点长度
222
- * @param point
223
- */
224
- distance(point) {
225
- return Math.sqrt(
226
- (this.x - point.x) * (this.x - point.x) + (this.y - point.y) * (this.y - point.y)
227
- );
228
- }
229
- /**
230
- * 克隆
231
- * @returns
232
- */
233
- clone() {
234
- return new Point(this.x, this.y);
235
- }
236
- static from(arr) {
237
- if (Array.isArray(arr)) {
238
- return new Point(arr[0], arr[1]);
239
- } else if ("x" in arr && "y" in arr) {
240
- return new Point(arr.x, arr.y);
241
- } else if ("X" in arr && "Y" in arr) {
242
- return new Point(arr.X, arr.Y);
243
- }
244
- return this.zero();
245
- }
246
- static zero() {
247
- return new Point(0, 0);
248
- }
249
- }
250
- class Box2 {
251
- minX = 0;
252
- maxX = 0;
253
- minY = 0;
254
- maxY = 0;
255
- get points() {
256
- return [
257
- new Point(this.minX, this.minY),
258
- new Point(this.maxX, this.minY),
259
- new Point(this.maxX, this.maxY),
260
- new Point(this.minX, this.maxY)
261
- ];
262
- }
263
- get width() {
264
- return this.maxX - this.minX;
265
- }
266
- get height() {
267
- return this.maxY - this.minY;
268
- }
269
- get center() {
270
- return new Point(
271
- this.minX + (this.maxX - this.minX) * 0.5,
272
- this.minY + (this.maxY - this.minY) * 0.5
273
- );
274
- }
275
- constructor(minX = 0, maxX = 0, minY = 0, maxY = 0) {
276
- this.minX = minX;
277
- this.maxX = maxX;
278
- this.minY = minY;
279
- this.maxY = maxY;
280
- }
281
- /**
282
- *
283
- * @param z
284
- * @returns
285
- */
286
- getPaths3D(z = 0) {
287
- const points = this.points, list = [];
288
- points.forEach((p, i) => {
289
- const nextP = points[(i + 1) % points.length];
290
- list.push(p.x, p.y, z);
291
- list.push(nextP.x, nextP.y, z);
292
- });
293
- return list;
294
- }
295
- /**
296
- * 判断线段是与包围盒相交
297
- * @description Liang-Barsky算法的变种
298
- * @param line
299
- */
300
- intersectLineSegment(line) {
301
- const p1 = line.points[0];
302
- const p2 = line.points[1];
303
- const dx = p2.x - p1.x;
304
- const dy = p2.y - p1.y;
305
- if (dx === 0 && dy === 0) {
306
- return this.minX <= p1.x && p1.x <= this.maxX && this.minY <= p1.y && p1.y <= this.maxY;
307
- }
308
- let tNear = Number.NEGATIVE_INFINITY;
309
- let tFar = Number.POSITIVE_INFINITY;
310
- if (dx !== 0) {
311
- const tx1 = (this.minX - p1.x) / dx;
312
- const tx2 = (this.maxX - p1.x) / dx;
313
- tNear = Math.max(tNear, Math.min(tx1, tx2));
314
- tFar = Math.min(tFar, Math.max(tx1, tx2));
315
- } else if (p1.x < this.minX || p1.x > this.maxX) {
316
- return false;
317
- }
318
- if (dy !== 0) {
319
- const ty1 = (this.minY - p1.y) / dy;
320
- const ty2 = (this.maxY - p1.y) / dy;
321
- tNear = Math.max(tNear, Math.min(ty1, ty2));
322
- tFar = Math.min(tFar, Math.max(ty1, ty2));
323
- } else if (p1.y < this.minY || p1.y > this.maxY) {
324
- return false;
325
- }
326
- return tNear <= tFar && tNear <= 1 && tFar >= 0;
327
- }
328
- /**
329
- * 判断线段是在包围盒内
330
- * @param line
331
- */
332
- containsLineSegment(line) {
333
- const [p1, p2] = line.points;
334
- 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;
335
- }
336
- /**
337
- * 判断矩形与包围盒相交
338
- * @param rectangle
339
- */
340
- intersectRectangle(rectangle) {
341
- const isPointInBox = (p) => this.minX <= p.x && p.x <= this.maxX && this.minY <= p.y && p.y <= this.maxY;
342
- const isPointInRect = (point) => {
343
- let sign = 0;
344
- for (let i = 0; i < 4; i++) {
345
- const p1 = rectangle.points[i];
346
- const p2 = rectangle.points[(i + 1) % 4];
347
- const edge = { x: p2.x - p1.x, y: p2.y - p1.y };
348
- const toPoint = { x: point.x - p1.x, y: point.y - p1.y };
349
- const cross = edge.x * toPoint.y - edge.y * toPoint.x;
350
- if (cross === 0) {
351
- const t = edge.x !== 0 ? (point.x - p1.x) / edge.x : (point.y - p1.y) / edge.y;
352
- if (t >= 0 && t <= 1) return true;
353
- } else {
354
- const currentSign = cross > 0 ? 1 : -1;
355
- if (sign === 0) sign = currentSign;
356
- if (sign !== currentSign) return false;
357
- }
358
- }
359
- return true;
360
- };
361
- const doIntersect = (l1p1, l1p2, l2p1, l2p2) => {
362
- const orientation = (p, q, r) => {
363
- const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
364
- if (val === 0) return 0;
365
- return val > 0 ? 1 : 2;
366
- };
367
- const onSegment = (p, q, r) => {
368
- 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);
369
- };
370
- const o1 = orientation(l1p1, l1p2, l2p1);
371
- const o2 = orientation(l1p1, l1p2, l2p2);
372
- const o3 = orientation(l2p1, l2p2, l1p1);
373
- const o4 = orientation(l2p1, l2p2, l1p2);
374
- if (o1 !== o2 && o3 !== o4) return true;
375
- if (o1 === 0 && onSegment(l1p1, l2p1, l1p2)) return true;
376
- if (o2 === 0 && onSegment(l1p1, l2p2, l1p2)) return true;
377
- if (o3 === 0 && onSegment(l2p1, l1p1, l2p2)) return true;
378
- if (o4 === 0 && onSegment(l2p1, l1p2, l2p2)) return true;
379
- return false;
380
- };
381
- const boxPoints = this.points;
382
- for (let i = 0; i < 4; i++) {
383
- const bp1 = boxPoints[i];
384
- const bp2 = boxPoints[(i + 1) % 4];
385
- for (let j = 0; j < 4; j++) {
386
- const rp1 = rectangle.points[j];
387
- const rp2 = rectangle.points[(j + 1) % 4];
388
- if (doIntersect(bp1, bp2, rp1, rp2)) return true;
389
- }
390
- }
391
- for (let p of rectangle.points) {
392
- if (isPointInBox(p)) return true;
393
- }
394
- for (let p of boxPoints) {
395
- if (isPointInRect(p)) return true;
396
- }
397
- return false;
398
- }
399
- /**
400
- * 判断矩形是在包围盒内
401
- * @param rectangle
402
- */
403
- containsRectangle(rectangle) {
404
- return rectangle.points.every(
405
- (p) => this.minX <= p.x && p.x <= this.maxX && this.minY <= p.y && p.y <= this.maxY
406
- );
407
- }
408
- /**
409
- * 判断包围盒与包围盒相交
410
- * @param box
411
- */
412
- intersectBox(box) {
413
- return !(this.maxX < box.minX || this.minX > box.maxX || this.maxY < box.minY || this.minY > box.maxY);
414
- }
415
- /**
416
- * 判断包围盒是在包围盒内
417
- * @param box
418
- */
419
- containsBox(box) {
420
- return this.minX <= box.minX && box.maxX <= this.maxX && this.minY <= box.minY && box.maxY <= this.maxY;
421
- }
422
- /** 判断点是在包围盒内
423
- * @param point
424
- */
425
- containsPoint(point) {
426
- return point.x >= this.minX && point.x <= this.maxX && point.y >= this.minY && point.y <= this.maxY;
427
- }
428
- /**
429
- *
430
- * @param minX
431
- * @param minY
432
- * @param maxX
433
- * @param maxY
434
- * @returns
435
- */
436
- set(minX, minY, maxX, maxY) {
437
- this.minX = minX;
438
- this.minY = minY;
439
- this.maxX = maxX;
440
- this.maxY = maxY;
441
- return this;
442
- }
443
- /**
444
- *
445
- * @param maxWidth
446
- * @param maxHeight
447
- * @param mode
448
- * @returns
449
- */
450
- scaleSize(maxWidth, maxHeight, mode = "min") {
451
- const scaleX = maxWidth / this.width;
452
- const scaleY = maxHeight / this.height;
453
- return Math[mode](scaleX, scaleY);
454
- }
455
- /**
456
- *
457
- * @param scalar
458
- * @returns
459
- */
460
- multiplyScalar(scalar) {
461
- this.minX *= scalar;
462
- this.minY *= scalar;
463
- this.maxX *= scalar;
464
- this.maxY *= scalar;
465
- return this;
466
- }
467
- /**
468
- *
469
- * @returns
470
- */
471
- clone() {
472
- return new Box2(this.minX, this.minY, this.maxX, this.maxY);
473
- }
474
- /**
475
- *
476
- * @param points
477
- * @returns
478
- */
479
- static fromByPoints(...points) {
480
- const xList = [], yList = [];
481
- points.forEach((p) => {
482
- xList.push(p.x);
483
- yList.push(p.y);
484
- });
485
- return new Box2(
486
- Math.min(...xList),
487
- Math.max(...xList),
488
- Math.min(...yList),
489
- Math.max(...yList)
490
- );
491
- }
492
- }
493
- class LineSegment {
494
- points = [new Point(), new Point()];
495
- userData;
496
- get center() {
497
- return new Point(
498
- this.points[0].x + (this.points[1].x - this.points[0].x) * 0.5,
499
- this.points[0].y + (this.points[1].y - this.points[0].y) * 0.5
500
- );
501
- }
502
- get start() {
503
- return this.points[0];
504
- }
505
- get end() {
506
- return this.points[1];
507
- }
508
- constructor(p1 = new Point(), p2 = new Point()) {
509
- this.points = [p1, p2];
510
- }
511
- /**
512
- * 计算线段的长度
513
- * @returns 线段的长度
514
- */
515
- getLength() {
516
- return this.points[0].distance(this.points[1]);
517
- }
518
- /**
519
- * 获取方向
520
- * @returns
521
- */
522
- direction() {
523
- return this.points[1].direction(this.points[0]);
524
- }
525
- /**
526
- * 计算一条线段在另一条直线上的投影,并裁剪超出目标线段的部分
527
- * @param line 要投影的线段
528
- * @returns 投影并裁剪后的线段
529
- */
530
- projectLineSegment(line) {
531
- if (line.points.length !== 2 || this.points.length !== 2) {
532
- throw new Error("每条线段必须由两个点定义");
533
- }
534
- const [p1, p2] = line.points;
535
- const [q1, q2] = this.points;
536
- const dir = new Point(q2.x - q1.x, q2.y - q1.y);
537
- if (dir.x === 0 && dir.y === 0) {
538
- throw new Error("投影目标线段的两个点不能重合");
539
- }
540
- const projectPoint = (point) => {
541
- const pq = new Point(point.x - q1.x, point.y - q1.y);
542
- const dirLengthSquared = dir.x * dir.x + dir.y * dir.y;
543
- const dotProduct = pq.x * dir.x + pq.y * dir.y;
544
- const t = dotProduct / dirLengthSquared;
545
- const projX = q1.x + t * dir.x;
546
- const projY = q1.y + t * dir.y;
547
- return new Point(projX, projY);
548
- };
549
- let projP1 = projectPoint(p1);
550
- let projP2 = projectPoint(p2);
551
- const getT = (point) => {
552
- const pq = new Point(point.x - q1.x, point.y - q1.y);
553
- const dirLengthSquared = dir.x * dir.x + dir.y * dir.y;
554
- return (pq.x * dir.x + pq.y * dir.y) / dirLengthSquared;
555
- };
556
- let t1 = getT(projP1);
557
- let t2 = getT(projP2);
558
- const clampPoint = (t) => {
559
- const clampedT = Math.max(0, Math.min(1, t));
560
- const x = q1.x + clampedT * dir.x;
561
- const y = q1.y + clampedT * dir.y;
562
- return new Point(x, y);
563
- };
564
- if (t1 < 0 || t1 > 1) {
565
- projP1 = clampPoint(t1);
566
- }
567
- if (t2 < 0 || t2 > 1) {
568
- projP2 = clampPoint(t2);
569
- }
570
- if (projP1.x === projP2.x && projP1.y === projP2.y) {
571
- return new LineSegment(projP1, projP1);
572
- }
573
- return new LineSegment(projP1, projP2);
574
- }
575
- clone() {
576
- return new LineSegment(
577
- this.points[0].clone(),
578
- this.points[1].clone()
579
- );
580
- }
581
- }
582
- const units = {
583
- Unitless: 1,
584
- // 无单位,1米 = 1(无单位)
585
- Inches: 39.37007874015748,
586
- // 英寸,1米 = 39.37007874015748英寸
587
- Feet: 3.280839895013123,
588
- // 英尺,1米 = 3.280839895013123英尺
589
- Miles: 6213711922373339e-19,
590
- // 英里,1米 = 0.0006213711922373339英里
591
- Millimeters: 1e3,
592
- // 毫米,1米 = 1000毫米
593
- Centimeters: 100,
594
- // 厘米,1米 = 100厘米
595
- Meters: 1,
596
- // 米,1米 = 1米
597
- Kilometers: 1e-3,
598
- // 千米,1米 = 0.001千米
599
- Microinches: 3937007874015748e-8,
600
- // 微英寸,1米 = 39370078.74015748微英寸
601
- Mils: 39370.07874015748,
602
- // 密耳,1米 = 39370.07874015748密耳
603
- Yards: 1.0936132983377078,
604
- // 码,1米 = 1.0936132983377078码
605
- Angstroms: 1e10,
606
- // 埃,1米 = 10^10埃
607
- Nanometers: 1e9,
608
- // 纳米,1米 = 10^9纳米
609
- Microns: 1e6,
610
- // 微米,1米 = 10^6微米
611
- Decimeters: 10,
612
- // 分米,1米 = 10分米
613
- Decameters: 0.1,
614
- // 十米,1米 = 0.1十米
615
- Hectometers: 0.01,
616
- // 百米,1米 = 0.01百米
617
- Gigameters: 1e-9,
618
- // 吉米,1米 = 10^-9吉米
619
- "Astronomical units": 6684587122268445e-27,
620
- // 天文单位,1米 = 0.000000000006684587122268445天文单位
621
- "Light years": 10570008340246154e-32,
622
- // 光年,1米 ≈ 0.00000000000000010570008340246154光年
623
- Parsecs: 3240779289666404e-32
624
- // 秒差距,1米 ≈ 0.00000000000000003240779289666404秒差距
625
- };
626
- class Dxf extends Component {
627
- width = 0.04;
628
- scale = 1;
629
- originalData = [];
630
- data = [];
631
- originalBox = new Box2(0, 0, 0, 0);
632
- box = new Box2(0, 0, 0, 0);
633
- pointsGroups = [];
634
- wallsGroup = [];
635
- doors = [];
636
- lineSegments = [];
637
- originalZAverage = 0;
638
- static EndType = {
639
- etOpenSquare: 0,
640
- etOpenRound: 1,
641
- etOpenButt: 2
642
- // etClosedLine: 3,
643
- // etClosedPolygon: 4
644
- };
645
- static JoinType = {
646
- jtSquare: 0,
647
- jtRound: 1,
648
- jtMiter: 2
649
- };
650
- /** 原始数据组
651
- */
652
- get lines() {
653
- return this.lineSegments;
654
- }
655
- /**初始化
656
- * @param data 点云数据
657
- * @param width 墙体宽度
658
- * @param scale 缩放比例
659
- */
660
- constructor(width = 0.04, scale = 1) {
661
- super();
662
- this.width = width;
663
- this.scale = scale;
664
- }
665
- /**
666
- * 设置
667
- * @param data
668
- * @param width
669
- * @param scale
670
- */
671
- async set(data, width = this.width, scale = this.scale) {
672
- if (typeof data === "string") {
673
- if (typeof global !== "undefined") {
674
- const packageName = "fs";
675
- const { default: fs } = await import(
676
- /* @vite-ignore */
677
- packageName
678
- );
679
- const buffer = fs.readFileSync(data);
680
- const json = JSON.parse(buffer.toString("utf-8"));
681
- return this.set(json, width, scale);
682
- } else {
683
- throw new Error("非node环境不允许使用路径");
684
- }
685
- }
686
- this.scale = scale;
687
- this.width = width;
688
- this.originalData = data;
689
- const zList = [];
690
- this.data = data.map(({ start, end, insetionArr, isDoor = false }, index2) => {
691
- zList.push(start.z ?? 0, end.z ?? 0);
692
- const lineSegment = new LineSegment(
693
- Point.from(start).mutiplyScalar(scale),
694
- Point.from(end).mutiplyScalar(scale)
695
- );
696
- lineSegment.userData = { isDoor };
697
- this.lineSegments.push(lineSegment);
698
- return [
699
- lineSegment.points[0],
700
- lineSegment.points[1],
701
- (insetionArr ?? []).map((i) => i.index),
702
- isDoor,
703
- index2
704
- ];
705
- });
706
- this.originalZAverage = zList.reduce((count, num) => count + num, 0) / zList.length;
707
- this.computedOriginalSize(data, this.originalBox);
708
- this.dispatchEvent({
709
- type: "setDta",
710
- originalData: this.originalData,
711
- data: this.data
712
- });
713
- this.createGroups();
714
- this.computedSize();
715
- this.dispatchEvent({
716
- type: "createGroup",
717
- groups: this.pointsGroups
718
- });
719
- }
720
- /** 创建分组
721
- * @description 根据相交数组insetionArr, 把相交的线段,划分到一组内,方便后续路径查找合并使用
722
- * @returns
723
- */
724
- createGroups() {
725
- const groups = [], visited = /* @__PURE__ */ new Set(), doorSet = /* @__PURE__ */ new Set(), doorVisitedRecord = /* @__PURE__ */ new Map();
726
- const dfs = (index2, group, preIndex = -1) => {
727
- const [start, end, insetionArr, isDoor] = this.data[index2];
728
- visited.add(index2);
729
- if (isDoor) {
730
- if (!doorVisitedRecord.has(index2)) doorVisitedRecord.set(index2, []);
731
- doorVisitedRecord.get(index2)?.push(preIndex);
732
- return doorSet.add(this.data[index2]);
733
- }
734
- group.push([start, end]);
735
- insetionArr.forEach((i) => {
736
- if (!visited.has(i)) {
737
- dfs(i, group, index2);
738
- }
739
- });
740
- };
741
- this.data.forEach((_, index2) => {
742
- if (!visited.has(index2)) {
743
- const group = [];
744
- dfs(index2, group);
745
- groups.push(group);
746
- }
747
- });
748
- this.doors = [...doorSet];
749
- this.pointsGroups = groups;
750
- return groups;
751
- }
752
- /** 计算当前墙体数据的边界框
753
- * @description 根据分组数据pointsGroups,计算包围盒, pointsGroups数据为缩放后数据。
754
- * @description 可通过box属性查看计算结果。
755
- * @returns
756
- */
757
- computedSize() {
758
- const xArr = this.pointsGroups.flatMap((points) => points.flatMap((p) => [p[0].x, p[1].x]));
759
- const yArr = this.pointsGroups.flatMap((points) => points.flatMap((p) => [p[0].y, p[1].y]));
760
- const minX = Math.min(...xArr);
761
- const minY = Math.min(...yArr);
762
- const maxX = Math.max(...xArr);
763
- const maxY = Math.max(...yArr);
764
- this.box.set(minX, minY, maxX, maxY);
765
- return this.box;
766
- }
767
- /** 线路拓扑
768
- * @description 处理线路拓扑,使线路有序链接,形成长路径
769
- * @param lines
770
- */
771
- lineTopology(lines) {
772
- const visited = [];
773
- function dfs(index2, linePath) {
774
- const [_0, a2] = lines[index2];
775
- visited[index2] = true;
776
- linePath.push(a2);
777
- for (let i = 0; i < lines.length; i++) {
778
- const [b1, _1] = lines[i];
779
- if (!visited[i]) {
780
- if (Math.abs(a2.x - b1.x) < 1e-6 && Math.abs(a2.y - b1.y) < 1e-6) {
781
- return dfs(i, linePath);
782
- }
783
- }
784
- }
785
- }
786
- const linePaths = [];
787
- for (let i = 0; i < lines.length; i++) {
788
- if (!visited[i]) {
789
- const linePath = [lines[i][0]];
790
- dfs(i, linePath);
791
- linePaths.push(linePath);
792
- }
793
- }
794
- return linePaths;
795
- }
796
- /** etOpenRound 去除毛刺
797
- * @description 检查连续的短线段数量,去除合并后产生的毛刺
798
- */
799
- squareRemoveBurr() {
800
- this.wallsGroup = this.wallsGroup.map((lines) => {
801
- if (lines.length < 3) return lines;
802
- const filterLines = [lines[0]];
803
- for (let i = 1; i < lines.length; i++) {
804
- const prev = lines[i - 1];
805
- const curr = lines[i];
806
- const len = prev.distance(curr);
807
- if (len < this.width * 0.5) {
808
- let count = 0;
809
- for (let j = i + 1; j < lines.length; j++) {
810
- const prev2 = lines[j - 1];
811
- const curr2 = lines[j];
812
- const len2 = prev2.distance(curr2);
813
- if (len2 < this.width * 0.8) count++;
814
- else break;
815
- }
816
- if (count === 0 && i + count === lines.length - 1) ;
817
- else if (i == 1 && count === 1) ;
818
- else if (count === 3) {
819
- filterLines.push(lines[i + 1]);
820
- i += count;
821
- } else if (count === 5) {
822
- filterLines.push(lines[i + 2]);
823
- i += count;
824
- } else {
825
- filterLines.push(curr);
826
- }
827
- } else {
828
- filterLines.push(curr);
829
- }
830
- }
831
- return filterLines;
832
- });
833
- }
834
- /** 线偏移
835
- * @description 使用 ClipperLib 对每个点组进行线偏移处理,生成具有指定宽度的墙体路径
836
- */
837
- lineOffset(endType = Dxf.EndType.etOpenSquare, joinType = Dxf.JoinType.jtMiter, scale = 1e4) {
838
- const solutions = new ClipperLib.Paths();
839
- const offset = new ClipperLib.ClipperOffset(20, 0.25);
840
- this.pointsGroups.forEach((points) => {
841
- const linePaths = this.lineTopology(points).map((linePath) => linePath.map((p) => p.clone().mutiplyScalar(scale)));
842
- offset.AddPaths(linePaths, joinType, endType);
843
- });
844
- offset.Execute(solutions, this.width / 2 * scale);
845
- offset.Execute(solutions, this.width / 2 * scale);
846
- this.wallsGroup = solutions.map((ps) => ps.map((p) => Point.from(p).divisionScalar(scale)));
847
- if (endType == Dxf.EndType.etOpenSquare) this.squareRemoveBurr();
848
- this.dispatchEvent({
849
- type: "lineOffset",
850
- wallsGroup: this.wallsGroup
851
- });
852
- return this.wallsGroup;
853
- }
854
- /**
855
- * 将点云结构转换为Float32Array
856
- */
857
- to3DArray(scale) {
858
- const array = [];
859
- this.wallsGroup.forEach((points) => {
860
- for (let i = 0; i < points.length; i++) {
861
- const point1 = points[i];
862
- const nextIndex = i === points.length - 1 ? 0 : i + 1;
863
- const point2 = points[nextIndex];
864
- array.push(point1.X * scale, point1.Y * scale, this.originalZAverage, point2.X * scale, point2.Y * scale, this.originalZAverage);
865
- }
866
- });
867
- return new Float32Array(array);
868
- }
869
- /**
870
- * 将点云结构转换为string
871
- */
872
- toDxfString(unit = "Millimeters") {
873
- const d = new Drawing();
874
- d.setUnits("Millimeters");
875
- const s = units[unit];
876
- this.wallsGroup.forEach((points) => {
877
- for (let i = 0; i < points.length; i++) {
878
- const point1 = points[i];
879
- const nextIndex = i === points.length - 1 ? 0 : i + 1;
880
- const point2 = points[nextIndex];
881
- d.drawLine(point1.X * s, point1.Y * s, point2.X * s, point2.Y * s);
882
- }
883
- });
884
- return d.toDxfString();
885
- }
886
- /**
887
- * 将点云结构转换为DXF格式
888
- * @returns
889
- */
890
- toDxfBlob(unit = "Millimeters") {
891
- const blob = new Blob([this.toDxfString(unit)]);
892
- return blob;
893
- }
894
- /**
895
- * 下载
896
- * @param filename
897
- */
898
- async download(filename, unit = "Millimeters") {
899
- if (typeof window !== "undefined") {
900
- const blob = this.toDxfBlob(unit);
901
- const a = document.createElement("a");
902
- a.href = URL.createObjectURL(blob);
903
- a.download = filename + ".dxf";
904
- a.click();
905
- } else if (typeof global !== "undefined") {
906
- const packageName = "fs";
907
- const { default: fs } = await import(
908
- /* @vite-ignore */
909
- packageName
910
- );
911
- fs.writeFileSync(filename, this.toDxfString(unit));
912
- }
913
- }
914
- /**
915
- * 计算原始数据的边界框
916
- * @description 计算所有线段的起点和终点的最小最大值,形成一个边界框
917
- * @returns
918
- */
919
- computedOriginalSize(data, originalBox = new Box2(0, 0, 0, 0)) {
920
- const xArr = data.flatMap((item) => [item.start.x, item.end.x]);
921
- const yArr = data.flatMap((item) => [item.start.y, item.end.y]);
922
- const minX = Math.min(...xArr);
923
- const minY = Math.min(...yArr);
924
- const maxX = Math.max(...xArr);
925
- const maxY = Math.max(...yArr);
926
- originalBox.set(minX, minY, maxX, maxY);
927
- return originalBox;
928
- }
929
- /**
930
- * 创建数据
931
- * @param pointsGroups
932
- * @returns
933
- */
934
- static createData(pointsGroups, sealed = true) {
935
- let count = 0;
936
- const data = pointsGroups.flatMap((points) => {
937
- const lines = points.map((point, index2) => {
938
- const nextIndex = index2 === points.length - 1 ? 0 : index2 + 1;
939
- const nextPoint = points[nextIndex];
940
- return {
941
- start: { x: point.x, y: point.y },
942
- end: { x: nextPoint.x, y: nextPoint.y },
943
- insetionArr: [
944
- {
945
- index: nextIndex + count
946
- }
947
- ]
948
- };
949
- });
950
- count += points.length;
951
- if (!sealed) {
952
- lines.pop();
953
- lines[lines.length - 1].insetionArr.length = 0;
954
- count--;
955
- }
956
- return lines;
957
- });
958
- return data;
959
- }
960
- }
961
- class Variable extends Component {
962
- originalLineVisible = true;
963
- dxfVisible = true;
964
- whiteModelVisible = true;
965
- isLook = false;
966
- currentWheel = 0;
967
- pointerMove = { x: 0, y: 0 };
968
- currentKeyUp = "";
969
- set(key, value) {
970
- if (key in this) {
971
- const oldValue = this[key];
972
- this[key] = value;
973
- this.dispatchEvent({
974
- type: key,
975
- value,
976
- oldValue
977
- });
978
- }
979
- }
980
- get(key) {
981
- if (key in this) return this[key];
982
- }
983
- }
984
- function lineSqueezing(p1, p2, width = 0.1) {
985
- const normal = p2.normal(p1);
986
- const pDirect = p2.direction(p1).mutiplyScalar(width * 0.5);
987
- const nDirect = p1.direction(p2).mutiplyScalar(width * 0.5);
988
- const offsetX = normal.x * width * 0.5;
989
- const offsetY = normal.y * width * 0.5;
990
- return {
991
- points: [
992
- // 第一条线
993
- new Point(p1.x + offsetX, p1.y + offsetY).add(nDirect),
994
- new Point(p2.x + offsetX, p2.y + offsetY).add(pDirect),
995
- // 第二条线
996
- new Point(p1.x - offsetX, p1.y - offsetY).add(nDirect),
997
- new Point(p2.x - offsetX, p2.y - offsetY).add(pDirect)
998
- ],
999
- indices: [0, 1, 1, 3, 3, 2, 2, 0],
1000
- rectIndices: [0, 1, 3, 2, 0]
1001
- };
1002
- }
1003
- class WhiteModel extends Component {
1004
- Dxf = null;
1005
- Variable = null;
1006
- // dxf数据白模
1007
- whiteModelGroup = new THREE.Group();
1008
- // 原始数据白模
1009
- originalWhiteMode = new THREE.Group();
1010
- onAddFromParent(parent) {
1011
- this.Dxf = parent.findComponentByType(Dxf);
1012
- this.Variable = parent.findComponentByType(Variable);
1013
- this.originalWhiteMode.visible = false;
1014
- this.Dxf?.addEventListener("lineOffset", () => {
1015
- this.updateModel();
1016
- });
1017
- }
1018
- updateModel() {
1019
- this.Variable?.set("whiteModelVisible", false);
1020
- const dxf = this.Dxf;
1021
- this.originalWhiteMode.clear();
1022
- this.whiteModelGroup.clear();
1023
- this.whiteModelGroup.position.z = dxf.originalZAverage;
1024
- this.originalWhiteMode.position.z = dxf.originalZAverage;
1025
- dxf.wallsGroup.forEach((points) => {
1026
- const shape = new THREE.Shape();
1027
- 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));
1028
- const geometry = new THREE.ExtrudeGeometry(shape, {
1029
- depth: 2.8,
1030
- bevelSize: 0
1031
- });
1032
- const mesh = new THREE.Mesh(geometry);
1033
- mesh.material = new THREE.MeshPhongMaterial({ color: 16777215, transparent: true, opacity: 0.8 });
1034
- this.whiteModelGroup.add(mesh);
1035
- this.whiteModelGroup.add(
1036
- new THREE.LineSegments(new THREE.EdgesGeometry(geometry), new THREE.LineBasicMaterial({ color: 6710886 }))
1037
- );
1038
- });
1039
- const walls = dxf.originalData.map(({ start, end, insetionArr }) => {
1040
- 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);
1041
- return {
1042
- points,
1043
- indices,
1044
- rectIndices,
1045
- insetions: (insetionArr ?? []).map((insetion) => insetion.index)
1046
- };
1047
- });
1048
- walls.forEach((wall) => {
1049
- const shape = new THREE.Shape();
1050
- wall.rectIndices.forEach((index2, i) => {
1051
- const p = wall.points[index2];
1052
- if (i === 0) shape.moveTo(p.x, p.y);
1053
- else shape.lineTo(p.x, p.y);
1054
- });
1055
- const geometry = new THREE.ExtrudeGeometry(shape, {
1056
- depth: 2.8,
1057
- bevelSize: 0
1058
- });
1059
- if (geometry.attributes.position.array.filter((num) => Number.isNaN(num)).length) return;
1060
- const mesh = new THREE.Mesh(geometry);
1061
- this.originalWhiteMode?.add(mesh);
1062
- });
1063
- this.dispatchEvent({
1064
- type: "updateModel",
1065
- originalWhiteMode: this.originalWhiteMode,
1066
- whiteModelGroup: this.whiteModelGroup
1067
- });
1068
- }
1069
- }
1070
- class DetailsPoint extends Component {
1071
- Dxf = null;
1072
- WhiteModel = null;
1073
- Variable = null;
1074
- desPoints = [];
1075
- raylines = [];
1076
- data = [];
1077
- onAddFromParent(parent) {
1078
- this.Dxf = parent.findComponentByType(Dxf);
1079
- this.Variable = parent.findComponentByType(Variable);
1080
- this.Dxf?.addEventListener("setDta", () => {
1081
- this.updateModel();
1082
- });
1083
- }
1084
- /**
1085
- * 设置值
1086
- * @param data
1087
- */
1088
- set(data) {
1089
- this.data = data;
1090
- this.updateModel();
1091
- }
1092
- /**
1093
- * 设置射线辅助
1094
- */
1095
- racasterHelper(position, direction, far) {
1096
- this.raylines.push([
1097
- position.clone(),
1098
- position.clone().add(direction.clone().multiplyScalar(far))
1099
- ]);
1100
- direction.z = 0;
1101
- this.raylines.push([
1102
- position.clone(),
1103
- position.clone().add(direction.clone().multiplyScalar(far))
1104
- ]);
1105
- }
1106
- _timer = null;
1107
- /**
1108
- * 更新模型
1109
- */
1110
- updateModel() {
1111
- if (this._timer) clearTimeout(this._timer);
1112
- this._timer = setTimeout(() => {
1113
- this._timer = null;
1114
- const whiteModel = this.parent?.findComponentByType(WhiteModel);
1115
- this.raylines.length = 0;
1116
- this.desPoints.length = 0;
1117
- this.data.forEach((item) => {
1118
- const position = new THREE.Vector3(
1119
- item.position.x,
1120
- item.position.y,
1121
- item.position.z
1122
- );
1123
- const direction = new THREE.Vector3(
1124
- item.direction.x,
1125
- item.direction.y,
1126
- item.direction.z
1127
- );
1128
- const far = 100;
1129
- this.racasterHelper(position, direction, far);
1130
- direction.z = 0;
1131
- const raycaster = new THREE.Raycaster(position, direction, 0, far);
1132
- const list = raycaster.intersectObject(whiteModel.originalWhiteMode);
1133
- if (list.length) {
1134
- const { point } = list[0];
1135
- this.desPoints.push({
1136
- message: item.desc,
1137
- position,
1138
- intersection: point
1139
- });
1140
- }
1141
- });
1142
- this.dispatchEvent({
1143
- type: "handleSuccess",
1144
- desPoints: this.desPoints
1145
- });
1146
- }, 50);
1147
- }
1148
- }
1149
- class DxfLineModel extends Component {
1150
- dxfLineModel = new THREE.LineSegments();
1151
- dxfDoorsLineModel = new THREE.LineSegments();
1152
- dxfModelGroup = new THREE.Group();
1153
- onAddFromParent(parent) {
1154
- const dxf = parent.findComponentByType(Dxf);
1155
- this.dxfModelGroup.add(this.dxfLineModel);
1156
- this.dxfModelGroup.add(this.dxfDoorsLineModel);
1157
- this.dxfDoorsLineModel.material = new THREE.LineBasicMaterial({ color: 16776960, vertexColors: true });
1158
- dxf?.addEventListener("lineOffset", () => this.updateMode());
1159
- }
1160
- updateMode() {
1161
- const dxf = this.parent?.findComponentByType(Dxf);
1162
- this.dxfLineModel.clear();
1163
- const dxfArray = dxf.to3DArray(1 / dxf.scale);
1164
- this.dxfLineModel.geometry = new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(dxfArray, 3, true));
1165
- const doorsArray = new Float32Array(dxf.doors.flatMap(([p1, p2]) => [p1.x, p1.y, dxf.originalZAverage, p2.x, p2.y, dxf.originalZAverage])).map((n) => n / dxf.scale);
1166
- const doorsColorArray = new Float32Array(dxf.doors.flatMap(() => [1, 0, 0, 0, 1, 0]));
1167
- this.dxfDoorsLineModel.geometry = new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(doorsArray, 3, true)).setAttribute("color", new THREE.BufferAttribute(doorsColorArray, 3));
1168
- }
1169
- }
1170
- const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1171
- __proto__: null,
1172
- DetailsPoint,
1173
- DxfLineModel,
1174
- WhiteModel
1175
- }, Symbol.toStringTag, { value: "Module" }));
1176
- function ModeDataPlugin(dxfSystem) {
1177
- dxfSystem.addComponent(new DxfLineModel());
1178
- dxfSystem.addComponent(new WhiteModel());
1179
- dxfSystem.addComponent(new DetailsPoint());
1180
- }
1181
- export {
1182
- ModeDataPlugin,
1183
- index as components
1184
- };