build-dxf 0.0.8 → 0.0.9
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 +34 -2
- package/src/index.d.ts +5 -0
- package/src/index.js +11 -3
- package/src/plugins/ModelDataPlugin/index.d.ts +10 -2
- package/src/plugins/ModelDataPlugin/index.js +61 -747
- package/src/plugins/RenderPlugin/index.d.ts +206 -32
- package/src/plugins/RenderPlugin/index.js +18372 -208
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
2
|
import { EventDispatcher as EventDispatcher$1 } from "three";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import "clipper-lib";
|
|
4
|
+
import "dxf-writer";
|
|
5
|
+
import { OBJExporter } from "three/examples/jsm/exporters/OBJExporter.js";
|
|
5
6
|
function uuid() {
|
|
6
7
|
return "xxxx-xxxx-4xxx-yxxx-xxxx".replace(/[xy]/g, function(c) {
|
|
7
8
|
var r = Math.random() * 16 | 0, v = c == "x" ? r : r & 3 | 8;
|
|
@@ -247,740 +248,7 @@ class Point {
|
|
|
247
248
|
return new Point(0, 0);
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
|
-
|
|
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
|
-
}
|
|
251
|
+
const exporter = new OBJExporter();
|
|
984
252
|
function lineSqueezing(p1, p2, width = 0.1) {
|
|
985
253
|
const normal = p2.normal(p1);
|
|
986
254
|
const pDirect = p2.direction(p1).mutiplyScalar(width * 0.5);
|
|
@@ -1008,8 +276,8 @@ class WhiteModel extends Component {
|
|
|
1008
276
|
// 原始数据白模
|
|
1009
277
|
originalWhiteMode = new THREE.Group();
|
|
1010
278
|
onAddFromParent(parent) {
|
|
1011
|
-
this.Dxf = parent.
|
|
1012
|
-
this.Variable = parent.
|
|
279
|
+
this.Dxf = parent.findComponentByName("Dxf");
|
|
280
|
+
this.Variable = parent.findComponentByName("Variable");
|
|
1013
281
|
this.originalWhiteMode.visible = false;
|
|
1014
282
|
this.Dxf?.addEventListener("lineOffset", () => {
|
|
1015
283
|
this.updateModel();
|
|
@@ -1030,7 +298,7 @@ class WhiteModel extends Component {
|
|
|
1030
298
|
bevelSize: 0
|
|
1031
299
|
});
|
|
1032
300
|
const mesh = new THREE.Mesh(geometry);
|
|
1033
|
-
mesh.material = new THREE.
|
|
301
|
+
mesh.material = new THREE.MeshStandardMaterial({ color: 16777215, transparent: true, opacity: 0.8 });
|
|
1034
302
|
this.whiteModelGroup.add(mesh);
|
|
1035
303
|
this.whiteModelGroup.add(
|
|
1036
304
|
new THREE.LineSegments(new THREE.EdgesGeometry(geometry), new THREE.LineBasicMaterial({ color: 6710886 }))
|
|
@@ -1066,6 +334,37 @@ class WhiteModel extends Component {
|
|
|
1066
334
|
whiteModelGroup: this.whiteModelGroup
|
|
1067
335
|
});
|
|
1068
336
|
}
|
|
337
|
+
toOBJ() {
|
|
338
|
+
return new Promise((resolve) => {
|
|
339
|
+
resolve(exporter.parse(this.whiteModelGroup));
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
async toBlob() {
|
|
343
|
+
const buffer = await this.toOBJ();
|
|
344
|
+
if (buffer) {
|
|
345
|
+
return new Blob([buffer], { type: "application/octet-stream" });
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
async download(filename) {
|
|
349
|
+
if (typeof window !== "undefined") {
|
|
350
|
+
const blob = await this.toBlob();
|
|
351
|
+
if (!blob) return;
|
|
352
|
+
const a = document.createElement("a");
|
|
353
|
+
a.href = URL.createObjectURL(blob);
|
|
354
|
+
a.download = filename;
|
|
355
|
+
a.click();
|
|
356
|
+
} else if (typeof global !== "undefined") {
|
|
357
|
+
const buffer = await this.toOBJ();
|
|
358
|
+
if (buffer) {
|
|
359
|
+
const packageName = "fs";
|
|
360
|
+
const { default: fs } = await import(
|
|
361
|
+
/* @vite-ignore */
|
|
362
|
+
packageName
|
|
363
|
+
);
|
|
364
|
+
fs.writeFileSync(filename, buffer);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
1069
368
|
}
|
|
1070
369
|
class DetailsPoint extends Component {
|
|
1071
370
|
Dxf = null;
|
|
@@ -1075,8 +374,8 @@ class DetailsPoint extends Component {
|
|
|
1075
374
|
raylines = [];
|
|
1076
375
|
data = [];
|
|
1077
376
|
onAddFromParent(parent) {
|
|
1078
|
-
this.Dxf = parent.
|
|
1079
|
-
this.Variable = parent.
|
|
377
|
+
this.Dxf = parent.findComponentByName("Dxf");
|
|
378
|
+
this.Variable = parent.findComponentByName("Variable");
|
|
1080
379
|
this.Dxf?.addEventListener("setDta", () => {
|
|
1081
380
|
this.updateModel();
|
|
1082
381
|
});
|
|
@@ -1085,7 +384,22 @@ class DetailsPoint extends Component {
|
|
|
1085
384
|
* 设置值
|
|
1086
385
|
* @param data
|
|
1087
386
|
*/
|
|
1088
|
-
set(data) {
|
|
387
|
+
async set(data) {
|
|
388
|
+
if (typeof data === "string") {
|
|
389
|
+
if (typeof global !== "undefined") {
|
|
390
|
+
const packageName = "fs";
|
|
391
|
+
const { default: fs } = await import(
|
|
392
|
+
/* @vite-ignore */
|
|
393
|
+
packageName
|
|
394
|
+
);
|
|
395
|
+
const buffer = fs.readFileSync(data);
|
|
396
|
+
const json = JSON.parse(buffer.toString("utf-8"));
|
|
397
|
+
this.set(json);
|
|
398
|
+
return;
|
|
399
|
+
} else {
|
|
400
|
+
throw new Error("非node环境不允许使用路径");
|
|
401
|
+
}
|
|
402
|
+
}
|
|
1089
403
|
this.data = data;
|
|
1090
404
|
this.updateModel();
|
|
1091
405
|
}
|
|
@@ -1111,7 +425,7 @@ class DetailsPoint extends Component {
|
|
|
1111
425
|
if (this._timer) clearTimeout(this._timer);
|
|
1112
426
|
this._timer = setTimeout(() => {
|
|
1113
427
|
this._timer = null;
|
|
1114
|
-
const whiteModel = this.parent?.
|
|
428
|
+
const whiteModel = this.parent?.findComponentByName("WhiteModel");
|
|
1115
429
|
this.raylines.length = 0;
|
|
1116
430
|
this.desPoints.length = 0;
|
|
1117
431
|
this.data.forEach((item) => {
|
|
@@ -1151,14 +465,14 @@ class DxfLineModel extends Component {
|
|
|
1151
465
|
dxfDoorsLineModel = new THREE.LineSegments();
|
|
1152
466
|
dxfModelGroup = new THREE.Group();
|
|
1153
467
|
onAddFromParent(parent) {
|
|
1154
|
-
const dxf = parent.
|
|
468
|
+
const dxf = parent.findComponentByName("Dxf");
|
|
1155
469
|
this.dxfModelGroup.add(this.dxfLineModel);
|
|
1156
470
|
this.dxfModelGroup.add(this.dxfDoorsLineModel);
|
|
1157
471
|
this.dxfDoorsLineModel.material = new THREE.LineBasicMaterial({ color: 16776960, vertexColors: true });
|
|
1158
472
|
dxf?.addEventListener("lineOffset", () => this.updateMode());
|
|
1159
473
|
}
|
|
1160
474
|
updateMode() {
|
|
1161
|
-
const dxf = this.parent?.
|
|
475
|
+
const dxf = this.parent?.findComponentByName("Dxf");
|
|
1162
476
|
this.dxfLineModel.clear();
|
|
1163
477
|
const dxfArray = dxf.to3DArray(1 / dxf.scale);
|
|
1164
478
|
this.dxfLineModel.geometry = new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(dxfArray, 3, true));
|
|
@@ -1173,12 +487,12 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
|
|
|
1173
487
|
DxfLineModel,
|
|
1174
488
|
WhiteModel
|
|
1175
489
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
1176
|
-
function
|
|
490
|
+
function ModelDataPlugin(dxfSystem) {
|
|
1177
491
|
dxfSystem.addComponent(new DxfLineModel());
|
|
1178
492
|
dxfSystem.addComponent(new WhiteModel());
|
|
1179
493
|
dxfSystem.addComponent(new DetailsPoint());
|
|
1180
494
|
}
|
|
1181
495
|
export {
|
|
1182
|
-
|
|
496
|
+
ModelDataPlugin,
|
|
1183
497
|
index as components
|
|
1184
498
|
};
|