build-dxf 0.0.13 → 0.0.15

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/src/build.js CHANGED
@@ -273,20 +273,32 @@ class Point {
273
273
  dot(point) {
274
274
  return this.x * point.x + this.y * point.y;
275
275
  }
276
+ /** 求两个点叉积
277
+ * @description 如果叉积大于 0,a 到 b 为逆时针方向。
278
+ * @description 如果叉积小于 0,a 到 b 为顺时针方向。
279
+ * @param point
280
+ * @returns
281
+ */
282
+ cross(point) {
283
+ return this.x * point.y - this.y * this.x;
284
+ }
276
285
  /** 计算两个向量夹角
277
286
  * @description 公式:a · b = |a| × |b| × cosθ
278
287
  * @description 结果为0(0度),两个向量方向一致,结果为3.1415926(180度, PI),两个向量方向相反
279
288
  * @param point
280
289
  * @returns
281
290
  */
282
- angleBetween(point) {
291
+ angleBetween(point, type = "radian", angle = "180") {
283
292
  const dotProduct = this.dot(point);
284
293
  const magnitude1 = this.magnitude();
285
294
  const magnitude2 = point.magnitude();
286
295
  if (magnitude1 === 0 || magnitude2 === 0) return 0;
287
296
  const cosTheta = dotProduct / (magnitude1 * magnitude2);
288
297
  const clampedCosTheta = Math.max(-1, Math.min(1, cosTheta));
289
- return Math.acos(clampedCosTheta);
298
+ if (type === "radian") return Math.acos(clampedCosTheta);
299
+ if (type === "cos") return clampedCosTheta;
300
+ if (angle === "180" || this.cross(point) < 0) return Math.acos(clampedCosTheta) / (Math.PI / 180);
301
+ return 360 - Math.acos(clampedCosTheta) / (Math.PI / 180);
290
302
  }
291
303
  /** 获取向量长度
292
304
  */
@@ -578,6 +590,148 @@ class Box2 {
578
590
  );
579
591
  }
580
592
  }
593
+ class Rectangle {
594
+ points;
595
+ get path() {
596
+ return this.points.flatMap((p, i) => {
597
+ const np = this.points[(i + 1) % this.points.length];
598
+ return [p.x, p.y, 0, np.x, np.y, 0];
599
+ });
600
+ }
601
+ path2D(callback) {
602
+ return this.points.flatMap((p, i) => {
603
+ const np = this.points[(i + 1) % this.points.length];
604
+ callback && callback(new Point(p.x, p.y), new Point(np.x, np.y));
605
+ return [p.x, p.y, np.x, np.y];
606
+ });
607
+ }
608
+ constructor(points) {
609
+ if (points.length !== 4) {
610
+ throw new Error("Rectangle must be defined by exactly 4 points");
611
+ }
612
+ this.points = points;
613
+ }
614
+ /**
615
+ * 判断线段是否与矩形相交
616
+ * @param line 线段
617
+ * @returns 是否与矩形相交
618
+ */
619
+ intersectLineSegment(line) {
620
+ if (line.points.length !== 2) {
621
+ throw new Error("LineSegment must have exactly 2 points");
622
+ }
623
+ const [p1, p2] = line.points;
624
+ const doIntersect = (l1p1, l1p2, l2p1, l2p2) => {
625
+ const orientation = (p, q, r) => {
626
+ const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
627
+ if (val === 0) return 0;
628
+ return val > 0 ? 1 : 2;
629
+ };
630
+ const onSegment = (p, q, r) => {
631
+ 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);
632
+ };
633
+ const o1 = orientation(l1p1, l1p2, l2p1);
634
+ const o2 = orientation(l1p1, l1p2, l2p2);
635
+ const o3 = orientation(l2p1, l2p2, l1p1);
636
+ const o4 = orientation(l2p1, l2p2, l1p2);
637
+ if (o1 !== o2 && o3 !== o4) return true;
638
+ if (o1 === 0 && onSegment(l1p1, l2p1, l1p2)) return true;
639
+ if (o2 === 0 && onSegment(l1p1, l2p2, l1p2)) return true;
640
+ if (o3 === 0 && onSegment(l2p1, l1p1, l2p2)) return true;
641
+ if (o4 === 0 && onSegment(l2p1, l1p2, l2p2)) return true;
642
+ return false;
643
+ };
644
+ for (let i = 0; i < 4; i++) {
645
+ const rectP1 = this.points[i];
646
+ const rectP2 = this.points[(i + 1) % 4];
647
+ if (doIntersect(p1, p2, rectP1, rectP2)) {
648
+ return true;
649
+ }
650
+ }
651
+ if (this.containsLineSegment(line)) {
652
+ return true;
653
+ }
654
+ return false;
655
+ }
656
+ /**
657
+ * 判断线段是否完全位于矩形内部
658
+ * @param line 线段
659
+ * @returns 是否完全在矩形内部
660
+ */
661
+ containsLineSegment(line) {
662
+ if (line.points.length !== 2) {
663
+ throw new Error("LineSegment must have exactly 2 points");
664
+ }
665
+ const isPointInRectangle = (point) => {
666
+ let sign = 0;
667
+ for (let i = 0; i < 4; i++) {
668
+ const p1 = this.points[i];
669
+ const p2 = this.points[(i + 1) % 4];
670
+ const edge = { x: p2.x - p1.x, y: p2.y - p1.y };
671
+ const toPoint = { x: point.x - p1.x, y: point.y - p1.y };
672
+ const cross = edge.x * toPoint.y - edge.y * toPoint.x;
673
+ if (cross === 0) {
674
+ const t = edge.x !== 0 ? (point.x - p1.x) / edge.x : (point.y - p1.y) / edge.y;
675
+ if (t >= 0 && t <= 1) return true;
676
+ } else {
677
+ const currentSign = cross > 0 ? 1 : -1;
678
+ if (sign === 0) sign = currentSign;
679
+ if (sign !== currentSign) return false;
680
+ }
681
+ }
682
+ return true;
683
+ };
684
+ return isPointInRectangle(line.points[0]) && isPointInRectangle(line.points[1]);
685
+ }
686
+ /**
687
+ * 判断点是否完全位于矩形内部
688
+ * @param point
689
+ */
690
+ containsPoint(point) {
691
+ let positiveCount = 0;
692
+ let negativeCount = 0;
693
+ for (let i = 0; i < 4; i++) {
694
+ const p1 = this.points[i];
695
+ const p2 = this.points[(i + 1) % 4];
696
+ const cross = (p2.x - p1.x) * (point.y - p1.y) - (p2.y - p1.y) * (point.x - p1.x);
697
+ if (cross > 0) positiveCount++;
698
+ else if (cross < 0) negativeCount++;
699
+ else return false;
700
+ }
701
+ return positiveCount === 4 || negativeCount === 4;
702
+ }
703
+ /**
704
+ *
705
+ * @returns
706
+ */
707
+ toBox() {
708
+ let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
709
+ this.points.forEach((p) => {
710
+ maxX = Math.max(p.x, maxX);
711
+ minX = Math.min(p.x, minX);
712
+ maxY = Math.max(p.x, maxY);
713
+ minY = Math.min(p.x, minY);
714
+ });
715
+ return new Box2(minX, maxX, minY, maxY);
716
+ }
717
+ /**
718
+ *
719
+ * @param line
720
+ * @param width
721
+ * @returns
722
+ */
723
+ static fromByLineSegment(line, width = 0.1, horizontal = false, hScale = 0.5) {
724
+ 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();
725
+ const offsetX = normal.x * width * 0.5;
726
+ const offsetY = normal.y * width * 0.5;
727
+ return new Rectangle([
728
+ new Point(p1.x + offsetX, p1.y + offsetY).add(nDirect),
729
+ new Point(p2.x + offsetX, p2.y + offsetY).add(pDirect),
730
+ new Point(p2.x - offsetX, p2.y - offsetY).add(pDirect),
731
+ new Point(p1.x - offsetX, p1.y - offsetY).add(nDirect)
732
+ ]);
733
+ }
734
+ }
581
735
  class LineSegment {
582
736
  points = [new Point(), new Point()];
583
737
  userData;
@@ -596,6 +750,69 @@ class LineSegment {
596
750
  constructor(p1 = new Point(), p2 = new Point()) {
597
751
  this.points = [p1, p2];
598
752
  }
753
+ /** 膨胀
754
+ * @description 向线段的两个端点分别膨胀 width
755
+ * @param width
756
+ */
757
+ expansion(width, direction = "all") {
758
+ const step = this.direction().multiplyScalar(width);
759
+ if (direction === "end" || direction === "all") this.end.add(step);
760
+ if (direction === "start" || direction === "all") this.start.add(step.multiplyScalar(-1));
761
+ return this;
762
+ }
763
+ /** 向前
764
+ * @description 向前移动 width
765
+ * @param width
766
+ */
767
+ forward(width) {
768
+ const step = this.direction().multiplyScalar(width);
769
+ this.start.add(step);
770
+ this.end.add(step);
771
+ return this;
772
+ }
773
+ /** 向前
774
+ * @description 向前移动 width
775
+ * @param width
776
+ */
777
+ backward(width) {
778
+ const step = this.direction().multiplyScalar(-width);
779
+ this.start.add(step);
780
+ this.end.add(step);
781
+ return this;
782
+ }
783
+ /**
784
+ * 向指定方向平移
785
+ * @param direct
786
+ * @param size
787
+ */
788
+ directionMove(direct, size) {
789
+ const step = direct.clone().multiplyScalar(size);
790
+ this.start.add(step);
791
+ this.end.add(step);
792
+ return this;
793
+ }
794
+ /** 膨胀为矩形
795
+ *
796
+ * @param width
797
+ * @returns {Rectangle}
798
+ */
799
+ expandToRectangle(width = 0.1) {
800
+ const p1 = this.start, p2 = this.end;
801
+ const normal = p2.normal(p1);
802
+ const pDirect = p2.direction(p1).mutiplyScalar(width * 0.5);
803
+ const nDirect = p1.direction(p2).mutiplyScalar(width * 0.5);
804
+ const offsetX = normal.x * width * 0.5;
805
+ const offsetY = normal.y * width * 0.5;
806
+ const point = [
807
+ // 第一条线
808
+ new Point(p1.x + offsetX, p1.y + offsetY).add(nDirect),
809
+ new Point(p2.x + offsetX, p2.y + offsetY).add(pDirect),
810
+ // 第二条线
811
+ new Point(p1.x - offsetX, p1.y - offsetY).add(nDirect),
812
+ new Point(p2.x - offsetX, p2.y - offsetY).add(pDirect)
813
+ ];
814
+ return new Rectangle([0, 1, 3, 2].map((i) => point[i]));
815
+ }
599
816
  /**
600
817
  * 计算线段的长度
601
818
  * @returns 线段的长度
@@ -610,6 +827,12 @@ class LineSegment {
610
827
  direction() {
611
828
  return this.points[1].direction(this.points[0]);
612
829
  }
830
+ /**
831
+ * 获取发向量
832
+ */
833
+ normal() {
834
+ return this.points[1].normal(this.points[0]);
835
+ }
613
836
  /**
614
837
  * 线段长度
615
838
  * @returns
@@ -667,6 +890,38 @@ class LineSegment {
667
890
  }
668
891
  return new LineSegment(projP1, projP2);
669
892
  }
893
+ /**
894
+ * 计算一条线段在另一条直线上的投影,并裁剪超出目标线段的部分
895
+ * @param line 要投影的线段
896
+ * @returns 投影并裁剪后的线段
897
+ */
898
+ projectPoint(p1) {
899
+ const [q1, q2] = this.points;
900
+ const dir = new Point(q2.x - q1.x, q2.y - q1.y);
901
+ if (dir.x === 0 && dir.y === 0) {
902
+ throw new Error("投影目标线段的两个点不能重合");
903
+ }
904
+ const projectPoint = (point) => {
905
+ const pq = new Point(point.x - q1.x, point.y - q1.y);
906
+ const dirLengthSquared = dir.x * dir.x + dir.y * dir.y;
907
+ const dotProduct = pq.x * dir.x + pq.y * dir.y;
908
+ const t = dotProduct / dirLengthSquared;
909
+ const projX = q1.x + t * dir.x;
910
+ const projY = q1.y + t * dir.y;
911
+ return new Point(projX, projY);
912
+ };
913
+ let projP1 = projectPoint(p1);
914
+ const getT = (point) => {
915
+ const pq = new Point(point.x - q1.x, point.y - q1.y);
916
+ const dirLengthSquared = dir.x * dir.x + dir.y * dir.y;
917
+ return (pq.x * dir.x + pq.y * dir.y) / dirLengthSquared;
918
+ };
919
+ let t1 = getT(projP1);
920
+ if (t1 < 0 || t1 > 1) {
921
+ return null;
922
+ }
923
+ return projP1;
924
+ }
670
925
  /**
671
926
  * 判断线段是与线段相交
672
927
  * @param line
@@ -757,6 +1012,18 @@ class LineSegment {
757
1012
  );
758
1013
  }
759
1014
  }
1015
+ async function include(path, exportDefault = true) {
1016
+ if (typeof global !== "undefined" && typeof require !== "undefined") {
1017
+ return require(path);
1018
+ } else {
1019
+ let pack = await import(
1020
+ /* @vite-ignore */
1021
+ path
1022
+ );
1023
+ if (exportDefault) pack = pack.default;
1024
+ return pack;
1025
+ }
1026
+ }
760
1027
  const units = {
761
1028
  Unitless: 1,
762
1029
  // 无单位,1米 = 1(无单位)
@@ -805,8 +1072,8 @@ function pathToLines(path) {
805
1072
  const lineSegments = [];
806
1073
  for (let i = 0; i < path.length; i++) {
807
1074
  lineSegments.push(new LineSegment(
808
- path[i],
809
- path[(i + 1) % path.length]
1075
+ path[i].clone(),
1076
+ path[(i + 1) % path.length].clone()
810
1077
  ));
811
1078
  }
812
1079
  return lineSegments;
@@ -829,6 +1096,7 @@ class Dxf extends Component {
829
1096
  pointsGroups = [];
830
1097
  wallsGroup = [];
831
1098
  doors = [];
1099
+ doorLineSegment = [];
832
1100
  lineSegments = [];
833
1101
  originalZAverage = 0;
834
1102
  static EndType = {
@@ -857,7 +1125,7 @@ class Dxf extends Component {
857
1125
  super();
858
1126
  this.width = width;
859
1127
  this.scale = scale;
860
- this.shortLine = width * 0.8;
1128
+ this.shortLine = width * 0.4;
861
1129
  }
862
1130
  /**
863
1131
  * 设置
@@ -991,6 +1259,28 @@ class Dxf extends Component {
991
1259
  }
992
1260
  return linePaths;
993
1261
  }
1262
+ /** 合并方向相同的线段
1263
+ * @param lines
1264
+ * @param errAngle
1265
+ */
1266
+ mergeSameDirectionLine(lines, errAngle = 3) {
1267
+ if (lines[0].includedAngle(lines[lines.length - 1]) < 0.1) {
1268
+ const line = lines.pop();
1269
+ line.end.copy(lines[0].end);
1270
+ lines.splice(0, 1, line);
1271
+ }
1272
+ const filterLines = [lines[0]];
1273
+ for (let i = 1; i < lines.length; i++) {
1274
+ const line = lines[i];
1275
+ const preLine = lines[i - 1];
1276
+ if (preLine.includedAngle(line) < errAngle) {
1277
+ preLine.end.copy(line.end);
1278
+ } else {
1279
+ filterLines.push(line);
1280
+ }
1281
+ }
1282
+ return filterLines;
1283
+ }
994
1284
  /** etOpenRound 去除毛刺
995
1285
  * @description 检查连续的短线段数量,去除合并后产生的毛刺
996
1286
  */
@@ -1027,44 +1317,22 @@ class Dxf extends Component {
1027
1317
  }
1028
1318
  return filterLines;
1029
1319
  }
1030
- epsilon = 1e-6;
1031
- // 距离阈值,用于比较点
1032
1320
  /**
1033
- * 移除共线点
1034
- * @param path
1035
- * @returns
1321
+ * 线段矫直, 线段中心突刺
1322
+ * @description 突变长度小于墙体宽度,该线段可能为突起线段,
1323
+ * @description 判断后续第2线段与上一条线段是否方向相同,相同就为突刺
1036
1324
  */
1037
- removeCollinearPoints(path) {
1325
+ lineSegmentStraightening(path) {
1038
1326
  for (let i = 0; i < path.length; i++) {
1039
1327
  const p1 = path[i];
1040
1328
  const p2 = path[(i + 1) % path.length];
1041
1329
  if (p1.distance(p2) > this.shortLine) {
1042
- path.push(...path.slice(0, i));
1043
- path.splice(0, i);
1330
+ path.push(...path.slice(0, i + 1));
1331
+ path.splice(0, i + 1);
1044
1332
  break;
1045
1333
  }
1046
1334
  }
1047
- if (path.length < 3) return path;
1048
- const cleanedPath = [];
1049
- const n = path.length;
1050
- for (let i = 0; i < n; i++) {
1051
- const p1 = path[i];
1052
- const p2 = path[(i + 1) % n];
1053
- const p3 = path[(i + 2) % n];
1054
- const cross = (p2.X - p1.X) * (p3.Y - p2.Y) - (p2.Y - p1.Y) * (p3.X - p2.X);
1055
- if (Math.abs(cross) > this.epsilon) {
1056
- cleanedPath.push(p1);
1057
- }
1058
- }
1059
- return cleanedPath;
1060
- }
1061
- /**
1062
- * 线段矫直, 线段中心突刺
1063
- * @description 突变长度小于墙体宽度,该线段可能为突起线段,
1064
- * @description 判断后续第2线段与上一条线段是否方向相同,相同就为突刺
1065
- */
1066
- lineSegmentStraightening(path) {
1067
- const lines = pathToLines(path), filterLines = [lines[0]];
1335
+ const lines = this.mergeSameDirectionLine(pathToLines(path)), filterLines = [lines[0]];
1068
1336
  for (let i = 1; i < lines.length; i++) {
1069
1337
  const line = lines[i];
1070
1338
  const preLine = lines[(lines.length + i - 1) % lines.length];
@@ -1080,15 +1348,16 @@ class Dxf extends Component {
1080
1348
  continue;
1081
1349
  }
1082
1350
  const line2 = lines[i + 2];
1083
- if (line2 && preLine.includedAngle(line2) < 1) {
1351
+ if (line2 && preLine.includedAngle(line2) < 2) {
1084
1352
  i = i + 2;
1085
1353
  filterLines.push(line2);
1086
1354
  } else filterLines.push(line);
1087
1355
  }
1088
- return filterLines.length > 3 ? linesToPath(filterLines) : [];
1356
+ return filterLines.length > 3 ? linesToPath(this.mergeSameDirectionLine(filterLines)) : [];
1089
1357
  }
1090
1358
  /**
1091
1359
  * 移除短线段
1360
+ * @todo 根据线段两端线段长度,选取参照物_|▔▔
1092
1361
  * @param path
1093
1362
  */
1094
1363
  removeShortLine(path, shortLine = this.shortLine) {
@@ -1141,9 +1410,9 @@ class Dxf extends Component {
1141
1410
  offset.Execute(solutions, this.width / 2 * scale);
1142
1411
  this.wallsGroup = solutions.map((ps) => {
1143
1412
  let path = ps.map((p) => Point.from(p).divisionScalar(scale));
1144
- path = this.removeCollinearPoints(path);
1145
1413
  path = this.lineSegmentStraightening(path);
1146
1414
  if (endType == Dxf.EndType.etOpenSquare) path = this.squareRemoveBurr(path);
1415
+ path = this.removeShortLine(path);
1147
1416
  return path;
1148
1417
  });
1149
1418
  this.dispatchEvent({
@@ -1167,6 +1436,32 @@ class Dxf extends Component {
1167
1436
  });
1168
1437
  return new Float32Array(array);
1169
1438
  }
1439
+ /** 获取角度范围
1440
+ * @param center
1441
+ * @param p1
1442
+ * @param p2
1443
+ * @returns
1444
+ */
1445
+ getArcAngleRange(center, p1, p2) {
1446
+ const x1 = p1.x - center.x;
1447
+ const y1 = p1.y - center.y;
1448
+ const x2 = p2.x - center.x;
1449
+ const y2 = p2.y - center.y;
1450
+ let angle1 = Math.atan2(y1, x1);
1451
+ let angle2 = Math.atan2(y2, x2);
1452
+ angle1 = angle1 < 0 ? angle1 + 2 * Math.PI : angle1;
1453
+ angle2 = angle2 < 0 ? angle2 + 2 * Math.PI : angle2;
1454
+ let r1, r2;
1455
+ const diff = Math.abs(angle2 - angle1);
1456
+ if (diff <= Math.PI) {
1457
+ r1 = Math.min(angle1, angle2);
1458
+ r2 = Math.max(angle1, angle2);
1459
+ } else {
1460
+ r1 = Math.max(angle1, angle2);
1461
+ r2 = Math.min(angle1, angle2) + 2 * Math.PI;
1462
+ }
1463
+ return [r1 / (Math.PI / 180), r2 / (Math.PI / 180)];
1464
+ }
1170
1465
  /**
1171
1466
  * 将点云结构转换为string
1172
1467
  */
@@ -1174,12 +1469,37 @@ class Dxf extends Component {
1174
1469
  const d = new Drawing();
1175
1470
  d.setUnits("Millimeters");
1176
1471
  const s = units[unit];
1472
+ function drawLine(p1, p2) {
1473
+ d.drawLine(p1.X * s, p1.Y * s, p2.X * s, p2.Y * s);
1474
+ }
1177
1475
  this.wallsGroup.forEach((points) => {
1178
1476
  for (let i = 0; i < points.length; i++) {
1179
1477
  const point1 = points[i];
1180
1478
  const nextIndex = i === points.length - 1 ? 0 : i + 1;
1181
1479
  const point2 = points[nextIndex];
1182
- d.drawLine(point1.X * s, point1.Y * s, point2.X * s, point2.Y * s);
1480
+ drawLine(point1, point2);
1481
+ }
1482
+ });
1483
+ const doorThickness = this.width * 0.2;
1484
+ d.addLayer("l_yellow", Drawing.ACI.CYAN, "DOTTED");
1485
+ this.doorLineSegment.forEach((lineSegment) => {
1486
+ if (lineSegment.length() < 0.4) return;
1487
+ const line = lineSegment.clone().expansion(-this.width * 0.5);
1488
+ d.setActiveLayer("l_yellow");
1489
+ if (line.length() < 1.2) {
1490
+ line.expansion(-doorThickness * 0.5);
1491
+ const normal = lineSegment.normal();
1492
+ const door = new LineSegment(
1493
+ line.start.clone(),
1494
+ line.start.clone().add(normal.clone().multiplyScalar(line.length()))
1495
+ );
1496
+ door.expansion(-doorThickness * 0.5).expandToRectangle(this.width * 0.2).path2D((p1, p2) => drawLine(p1, p2));
1497
+ 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);
1498
+ d.drawArc(center.x * s, center.y * s, r * s, Math.min(startAngle, endAngle), Math.max(startAngle, endAngle));
1499
+ } else {
1500
+ line.clone().expansion(-this.width * 0.5).expandToRectangle(this.width).path2D((p1, p2) => drawLine(p1, p2));
1501
+ line.clone().directionMove(line.normal(), doorThickness * 0.5).expansion(-line.length() * 0.4, "end").forward(doorThickness * 0.5).expandToRectangle(doorThickness).path2D((p1, p2) => drawLine(p1, p2));
1502
+ line.clone().directionMove(line.normal(), -doorThickness * 0.5).expansion(-line.length() * 0.4, "start").forward(-doorThickness * 0.5).expandToRectangle(doorThickness).path2D((p1, p2) => drawLine(p1, p2));
1183
1503
  }
1184
1504
  });
1185
1505
  return d.toDxfString();
@@ -1204,11 +1524,7 @@ class Dxf extends Component {
1204
1524
  a.download = filename + ".dxf";
1205
1525
  a.click();
1206
1526
  } else if (typeof global !== "undefined") {
1207
- const packageName = "fs";
1208
- const { default: fs } = await import(
1209
- /* @vite-ignore */
1210
- packageName
1211
- );
1527
+ const fs = await include("fs", false);
1212
1528
  fs.writeFileSync(filename, this.toDxfString(unit));
1213
1529
  }
1214
1530
  }
@@ -1283,141 +1599,6 @@ class Variable extends Component {
1283
1599
  if (key in this) return this[key];
1284
1600
  }
1285
1601
  }
1286
- class Rectangle {
1287
- points;
1288
- get path() {
1289
- return this.points.flatMap((p, i) => {
1290
- const np = this.points[(i + 1) % this.points.length];
1291
- return [p.x, p.y, 0, np.x, np.y, 0];
1292
- });
1293
- }
1294
- constructor(points) {
1295
- if (points.length !== 4) {
1296
- throw new Error("Rectangle must be defined by exactly 4 points");
1297
- }
1298
- this.points = points;
1299
- }
1300
- /**
1301
- * 判断线段是否与矩形相交
1302
- * @param line 线段
1303
- * @returns 是否与矩形相交
1304
- */
1305
- intersectLineSegment(line) {
1306
- if (line.points.length !== 2) {
1307
- throw new Error("LineSegment must have exactly 2 points");
1308
- }
1309
- const [p1, p2] = line.points;
1310
- const doIntersect = (l1p1, l1p2, l2p1, l2p2) => {
1311
- const orientation = (p, q, r) => {
1312
- const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
1313
- if (val === 0) return 0;
1314
- return val > 0 ? 1 : 2;
1315
- };
1316
- const onSegment = (p, q, r) => {
1317
- 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);
1318
- };
1319
- const o1 = orientation(l1p1, l1p2, l2p1);
1320
- const o2 = orientation(l1p1, l1p2, l2p2);
1321
- const o3 = orientation(l2p1, l2p2, l1p1);
1322
- const o4 = orientation(l2p1, l2p2, l1p2);
1323
- if (o1 !== o2 && o3 !== o4) return true;
1324
- if (o1 === 0 && onSegment(l1p1, l2p1, l1p2)) return true;
1325
- if (o2 === 0 && onSegment(l1p1, l2p2, l1p2)) return true;
1326
- if (o3 === 0 && onSegment(l2p1, l1p1, l2p2)) return true;
1327
- if (o4 === 0 && onSegment(l2p1, l1p2, l2p2)) return true;
1328
- return false;
1329
- };
1330
- for (let i = 0; i < 4; i++) {
1331
- const rectP1 = this.points[i];
1332
- const rectP2 = this.points[(i + 1) % 4];
1333
- if (doIntersect(p1, p2, rectP1, rectP2)) {
1334
- return true;
1335
- }
1336
- }
1337
- if (this.containsLineSegment(line)) {
1338
- return true;
1339
- }
1340
- return false;
1341
- }
1342
- /**
1343
- * 判断线段是否完全位于矩形内部
1344
- * @param line 线段
1345
- * @returns 是否完全在矩形内部
1346
- */
1347
- containsLineSegment(line) {
1348
- if (line.points.length !== 2) {
1349
- throw new Error("LineSegment must have exactly 2 points");
1350
- }
1351
- const isPointInRectangle = (point) => {
1352
- let sign = 0;
1353
- for (let i = 0; i < 4; i++) {
1354
- const p1 = this.points[i];
1355
- const p2 = this.points[(i + 1) % 4];
1356
- const edge = { x: p2.x - p1.x, y: p2.y - p1.y };
1357
- const toPoint = { x: point.x - p1.x, y: point.y - p1.y };
1358
- const cross = edge.x * toPoint.y - edge.y * toPoint.x;
1359
- if (cross === 0) {
1360
- const t = edge.x !== 0 ? (point.x - p1.x) / edge.x : (point.y - p1.y) / edge.y;
1361
- if (t >= 0 && t <= 1) return true;
1362
- } else {
1363
- const currentSign = cross > 0 ? 1 : -1;
1364
- if (sign === 0) sign = currentSign;
1365
- if (sign !== currentSign) return false;
1366
- }
1367
- }
1368
- return true;
1369
- };
1370
- return isPointInRectangle(line.points[0]) && isPointInRectangle(line.points[1]);
1371
- }
1372
- /**
1373
- * 判断点是否完全位于矩形内部
1374
- * @param point
1375
- */
1376
- containsPoint(point) {
1377
- let positiveCount = 0;
1378
- let negativeCount = 0;
1379
- for (let i = 0; i < 4; i++) {
1380
- const p1 = this.points[i];
1381
- const p2 = this.points[(i + 1) % 4];
1382
- const cross = (p2.x - p1.x) * (point.y - p1.y) - (p2.y - p1.y) * (point.x - p1.x);
1383
- if (cross > 0) positiveCount++;
1384
- else if (cross < 0) negativeCount++;
1385
- else return false;
1386
- }
1387
- return positiveCount === 4 || negativeCount === 4;
1388
- }
1389
- /**
1390
- *
1391
- * @returns
1392
- */
1393
- toBox() {
1394
- let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
1395
- this.points.forEach((p) => {
1396
- maxX = Math.max(p.x, maxX);
1397
- minX = Math.min(p.x, minX);
1398
- maxY = Math.max(p.x, maxY);
1399
- minY = Math.min(p.x, minY);
1400
- });
1401
- return new Box2(minX, maxX, minY, maxY);
1402
- }
1403
- /**
1404
- *
1405
- * @param line
1406
- * @param width
1407
- * @returns
1408
- */
1409
- static fromByLineSegment(line, width = 0.1, horizontal = false, hScale = 0.5) {
1410
- 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();
1411
- const offsetX = normal.x * width * 0.5;
1412
- const offsetY = normal.y * width * 0.5;
1413
- return new Rectangle([
1414
- new Point(p1.x + offsetX, p1.y + offsetY).add(nDirect),
1415
- new Point(p2.x + offsetX, p2.y + offsetY).add(pDirect),
1416
- new Point(p2.x - offsetX, p2.y - offsetY).add(pDirect),
1417
- new Point(p1.x - offsetX, p1.y - offsetY).add(nDirect)
1418
- ]);
1419
- }
1420
- }
1421
1602
  class Quadtree {
1422
1603
  bounds;
1423
1604
  // 包围盒
@@ -1615,6 +1796,28 @@ class Quadtree {
1615
1796
  }
1616
1797
  return result;
1617
1798
  }
1799
+ /**
1800
+ * 查询与线段相交的线段节点
1801
+ * @param lineSegment 线段
1802
+ * @returns 相交的节点数组
1803
+ */
1804
+ queryLineSegment(lineSegment) {
1805
+ const result = [];
1806
+ if (!this.bounds.intersectLineSegment(lineSegment)) {
1807
+ return result;
1808
+ }
1809
+ for (const node of this.nodes) {
1810
+ if (lineSegment.intersectLineSegment(node.line)) {
1811
+ result.push(node);
1812
+ }
1813
+ }
1814
+ if (!this.isLeaf) {
1815
+ for (const child of this.children) {
1816
+ result.push(...child.queryLineSegment(lineSegment));
1817
+ }
1818
+ }
1819
+ return result;
1820
+ }
1618
1821
  /**
1619
1822
  * 包围盒转换为数组
1620
1823
  * @param array
@@ -1772,6 +1975,7 @@ class LineAnalysis extends Component {
1772
1975
  this.Dxf = parent.findComponentByType(Dxf);
1773
1976
  this.Variable = this.parent?.findComponentByType(Variable);
1774
1977
  this.Dxf.addEventListener("setDta", this.lineAnalysis.bind(this));
1978
+ this.Dxf.addEventListener("createGroup", this.doorsAnalysis.bind(this));
1775
1979
  }
1776
1980
  /**
1777
1981
  *
@@ -1839,8 +2043,9 @@ class LineAnalysis extends Component {
1839
2043
  buildVirtualGrid() {
1840
2044
  const dxf = this.Dxf;
1841
2045
  const pointVirtualGrid = new PointVirtualGrid();
1842
- dxf.originalData.forEach((d, index2) => {
1843
- const [p1, p2] = [Point.from(d.start), Point.from(d.end)];
2046
+ dxf.lineSegments.forEach((d, index2) => {
2047
+ if (d.userData?.isDoor) return;
2048
+ const [p1, p2] = [d.start, d.end];
1844
2049
  pointVirtualGrid.insert(p1, { index: index2, type: "start" });
1845
2050
  pointVirtualGrid.insert(p2, { index: index2, type: "end" });
1846
2051
  });
@@ -1865,6 +2070,7 @@ class LineAnalysis extends Component {
1865
2070
  this.lineSegmentList = lineSegmentList;
1866
2071
  }
1867
2072
  resultList = [];
2073
+ mergeWallLines = [];
1868
2074
  /** 线段分析
1869
2075
  * @description 判断两条线段距离是否较短且趋近平行,然后查找两条线段的重合部分的投影线,以此判断两根线是否需要合并
1870
2076
  * @param data
@@ -1889,7 +2095,7 @@ class LineAnalysis extends Component {
1889
2095
  });
1890
2096
  this.appendLineSegmentList.length = 0;
1891
2097
  resultList.forEach(this.createRectangle.bind(this));
1892
- this.resultList = [];
2098
+ this.resultList = resultList;
1893
2099
  }
1894
2100
  /** 线段投影分析
1895
2101
  * @param index
@@ -1921,10 +2127,259 @@ class LineAnalysis extends Component {
1921
2127
  project2: p1
1922
2128
  };
1923
2129
  }
1924
- if (!data || data.project.getLength() < 0.01) return;
2130
+ if (!data || data.project.getLength() < 0.2 || data.project2.getLength() < 0.2) return;
1925
2131
  return data;
1926
2132
  }
1927
2133
  }
2134
+ doorSearchNearAngle = 110;
2135
+ doorSearchDistance = 2;
2136
+ doors = [];
2137
+ /**
2138
+ * 门的位置判断
2139
+ */
2140
+ doorsAnalysis() {
2141
+ const dxf = this.Dxf;
2142
+ const doorPoints = [];
2143
+ const pointVirtualGrid = this.pointVirtualGrid;
2144
+ const quadtree = this.quadtree;
2145
+ const doorSearchNearAngle = this.doorSearchNearAngle;
2146
+ const doorSearchDistance = this.doorSearchDistance;
2147
+ const doors = [];
2148
+ const excludePoints = dxf.doors.flatMap((item) => {
2149
+ const index2 = item[4];
2150
+ const doorData = dxf.originalData[index2];
2151
+ if (doorData.drawDoorData) {
2152
+ const point = Point.from(doorData.drawDoorData.start);
2153
+ const direct = Point.from(doorData.drawDoorData.n);
2154
+ const resList = pointVirtualGrid.queryPoint(point).filter((res) => {
2155
+ if (res.userData?.index === index2) return false;
2156
+ const line = dxf.lineSegments[res.userData?.index];
2157
+ const direct2 = line.direction();
2158
+ if (line.start.equal(point)) direct2.multiplyScalar(-1);
2159
+ const angle = direct.angleBetween(direct2, "angle");
2160
+ return angle > 80 || angle < 10;
2161
+ });
2162
+ if (resList.length) {
2163
+ const index22 = resList[0].userData?.index;
2164
+ doorPoints.push({
2165
+ line: dxf.lineSegments[index22],
2166
+ point: Point.from(doorData.drawDoorData.start),
2167
+ index: index22,
2168
+ direct,
2169
+ sure: true
2170
+ });
2171
+ return [point];
2172
+ }
2173
+ }
2174
+ return [];
2175
+ });
2176
+ const excludeIndexMap = /* @__PURE__ */ new Map();
2177
+ this.resultList.flatMap((p) => {
2178
+ const line0 = this.lineSegmentList[p.sourceIndex], line1 = this.lineSegmentList[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;
2179
+ if (excludeIndexMap.has(p.sourceIndex)) {
2180
+ if (excludeIndexMap.get(p.sourceIndex) != mode0) excludeIndexMap.set(p.sourceIndex, -1);
2181
+ } else excludeIndexMap.set(p.sourceIndex, mode0);
2182
+ if (excludeIndexMap.has(p.targetIndex)) {
2183
+ if (excludeIndexMap.get(p.targetIndex) != mode1) excludeIndexMap.set(p.targetIndex, -1);
2184
+ } else excludeIndexMap.set(p.targetIndex, mode1);
2185
+ });
2186
+ dxf.lineSegments.forEach((line, i) => {
2187
+ if (line.userData?.isDoor) return;
2188
+ const excludeMode = excludeIndexMap.get(i);
2189
+ if (excludeMode === -1) return;
2190
+ line.points.forEach((p, j) => {
2191
+ if (excludeMode === j) return;
2192
+ if (excludePoints.find((p1) => p1.equal(p))) return;
2193
+ const res = this.pointVirtualGrid.queryPoint(p).filter((d) => d.userData?.index !== i);
2194
+ if (res.length === 0) {
2195
+ doorPoints.push({
2196
+ line,
2197
+ point: p,
2198
+ index: i
2199
+ });
2200
+ }
2201
+ });
2202
+ });
2203
+ function searchNearby({ point, line, index: index2 }, doorIndex, record2) {
2204
+ const direct = line.direction();
2205
+ if (line.start === point) direct.multiplyScalar(-1);
2206
+ const res = pointVirtualGrid.queryCircle(point, doorSearchDistance).filter(
2207
+ (r) => (
2208
+ // 不能是自己
2209
+ r.userData?.index !== index2 && // 存在于可疑点位内,且不能是已知门的点位
2210
+ !!doorPoints.find((p) => !p.sure && p.point === r.point)
2211
+ )
2212
+ ).sort((a, b) => a.point.distance(point) - b.point.distance(point));
2213
+ const list = [];
2214
+ for (let i = 0; i < res.length; i++) {
2215
+ const doorIndex2 = doorPoints.findIndex((p) => p.point === res[i].point);
2216
+ if (record2.has(`${doorIndex}.${doorIndex2}`)) continue;
2217
+ record2.add(`${doorIndex}.${doorIndex2}`);
2218
+ record2.add(`${doorIndex2}.${doorIndex}`);
2219
+ const targetPoint = res[i].point, line2 = new LineSegment(point.clone(), targetPoint.clone()), angle = line2.direction().angleBetween(direct, "angle");
2220
+ if (angle < doorSearchNearAngle) {
2221
+ const direct2 = doorPoints[doorIndex2].line.direction();
2222
+ const line22 = dxf.lineSegments[res[i].userData?.index];
2223
+ if (line22.start.equal(res[i].point)) direct2.multiplyScalar(-1);
2224
+ const angle2 = line2.direction().multiplyScalar(-1).angleBetween(direct2, "angle");
2225
+ if (angle2 < doorSearchNearAngle) {
2226
+ if (!quadtree.queryLineSegment(line2).length) {
2227
+ list.push({
2228
+ findData: res[i],
2229
+ findDoorIndex: doorIndex2,
2230
+ findAngle: angle2,
2231
+ doorAngle: angle,
2232
+ doorLine: line2,
2233
+ doorIndex
2234
+ });
2235
+ }
2236
+ }
2237
+ }
2238
+ }
2239
+ return list;
2240
+ }
2241
+ function searchAlongDirection({ point, line }) {
2242
+ const direct = line.direction();
2243
+ if (line.start === point) direct.multiplyScalar(-1);
2244
+ const endPoint = point.clone().add(direct.clone().multiplyScalar(doorSearchDistance)), rline = new LineSegment(point.clone(), endPoint), result = quadtree.queryLineSegment(rline).map((l) => {
2245
+ const res = l.line.getIntersection(rline);
2246
+ return {
2247
+ point: res,
2248
+ line: l.line
2249
+ };
2250
+ }).filter((i) => i.point).sort((a, b) => point.distance(a.point) - point.distance(b.point));
2251
+ if (result.length) {
2252
+ const item = result[0];
2253
+ if (Math.abs(90 - item.line.direction().angleBetween(direct, "angle")) < 5) {
2254
+ return item;
2255
+ }
2256
+ }
2257
+ }
2258
+ function searchAlongNormalDirection({ point, line, index: index2 }) {
2259
+ const direct = line.direction();
2260
+ if (line.start === point) direct.multiplyScalar(-1);
2261
+ const normal = line.start.normal(line.end);
2262
+ const prePoint = line.start.clone();
2263
+ if (line.start === point) prePoint.copy(line.end);
2264
+ const result = pointVirtualGrid.queryPoint(prePoint).filter((r) => r.userData?.index !== index2);
2265
+ for (let i = 0; i < result.length; i++) {
2266
+ const element = result[i];
2267
+ const l = dxf.lineSegments[element.userData?.index ?? 0];
2268
+ const d1 = l.direction();
2269
+ if (l.start === element.point) direct.multiplyScalar(-1);
2270
+ const angle = d1.angleBetween(normal) / (Math.PI / 180);
2271
+ if (angle > 90) {
2272
+ normal.multiplyScalar(-1);
2273
+ break;
2274
+ }
2275
+ }
2276
+ const rline3 = new LineSegment(point.clone(), point.clone().add(normal.multiplyScalar(doorSearchDistance)));
2277
+ const r3 = quadtree.queryLineSegment(rline3).map((l) => {
2278
+ const res = l.line.getIntersection(rline3);
2279
+ return {
2280
+ point: res,
2281
+ line: l.line
2282
+ };
2283
+ }).filter((i) => i.point).sort((a, b) => point.distance(a.point) - point.distance(b.point));
2284
+ if (r3.length) {
2285
+ const item = r3[0];
2286
+ if (Math.abs(90 - item.line.direction().angleBetween(normal, "angle")) < 5) {
2287
+ return item;
2288
+ }
2289
+ }
2290
+ }
2291
+ function deepSearchNearHandle(index2, snFindRecord2, list, record2, other) {
2292
+ record2.add(`${index2}`);
2293
+ const newList = [];
2294
+ if (other) newList.push(other);
2295
+ for (let i = 0; i < list.length; i++) {
2296
+ const item = list[i];
2297
+ if (snFindRecord2.has(item.findDoorIndex)) {
2298
+ const list2 = snFindRecord2.get(item.findDoorIndex);
2299
+ if (deepSearchNearHandle(item.findDoorIndex, snFindRecord2, list2, record2, item)) newList.push(item);
2300
+ } else newList.push(item);
2301
+ }
2302
+ newList.sort((a, b) => a.doorLine.length() - b.doorLine.length());
2303
+ if (other && newList[0] === other) {
2304
+ list.splice(0);
2305
+ return true;
2306
+ }
2307
+ list.splice(1);
2308
+ return false;
2309
+ }
2310
+ const record = /* @__PURE__ */ new Set();
2311
+ const snFindRecord = /* @__PURE__ */ new Map();
2312
+ doorPoints.map((p, index2) => {
2313
+ if (dxf.doors.length >= 2 && !p.sure) return;
2314
+ const list = searchNearby(p, index2, record);
2315
+ if (list.length) snFindRecord.set(index2, list);
2316
+ });
2317
+ record.clear();
2318
+ const temMap = /* @__PURE__ */ new Map();
2319
+ snFindRecord.forEach((list, key) => {
2320
+ if (!record.has(`${key}`) && list.length) {
2321
+ deepSearchNearHandle(key, snFindRecord, list, record);
2322
+ }
2323
+ if (list.length) {
2324
+ const item = list[0];
2325
+ if (!temMap.has(item.doorIndex)) temMap.set(item.doorIndex, []);
2326
+ temMap.get(item.doorIndex)?.push(item);
2327
+ if (!temMap.has(item.findDoorIndex)) temMap.set(item.findDoorIndex, []);
2328
+ temMap.get(item.findDoorIndex)?.push(item);
2329
+ }
2330
+ });
2331
+ const deleteSet = /* @__PURE__ */ new Set();
2332
+ temMap.forEach((list) => {
2333
+ if (list.length > 1) {
2334
+ list.sort((a, b) => a.doorLine.length() - b.doorLine.length());
2335
+ for (let i = 1; i < list.length; i++) deleteSet.add(list[i]);
2336
+ }
2337
+ });
2338
+ const searchNearRasult = [], removeDoorPointsIndex = [];
2339
+ snFindRecord.forEach((list) => {
2340
+ if (list.length) {
2341
+ const item = list[0];
2342
+ if (!deleteSet.has(item)) {
2343
+ searchNearRasult.push(item);
2344
+ removeDoorPointsIndex.push(item.doorIndex, item.findDoorIndex);
2345
+ }
2346
+ }
2347
+ });
2348
+ const doors_ = doorPoints.map((item, index2) => {
2349
+ if (removeDoorPointsIndex.includes(index2)) return;
2350
+ if (dxf.doors.length >= 2 && !item.sure) return;
2351
+ const res2 = searchAlongDirection(item);
2352
+ if (res2) return {
2353
+ start: item.point,
2354
+ end: res2.point,
2355
+ index: item.index
2356
+ };
2357
+ const res3 = searchAlongNormalDirection(item);
2358
+ if (res3) return {
2359
+ start: item.point,
2360
+ end: res3.point,
2361
+ index: item.index
2362
+ };
2363
+ }).filter((i) => !!i && i.start.distance(i.end) < doorSearchDistance);
2364
+ doors.push(...doors_);
2365
+ searchNearRasult.forEach((item) => {
2366
+ doors.push({
2367
+ start: doorPoints[item.doorIndex].point,
2368
+ end: doorPoints[item.findDoorIndex].point,
2369
+ index: doorPoints[item.doorIndex].index
2370
+ });
2371
+ });
2372
+ dxf.doorLineSegment.length = 0;
2373
+ doors.forEach((p) => dxf.doorLineSegment.push(new LineSegment(p?.start, p?.end)));
2374
+ dxf.doorLineSegment = dxf.doorLineSegment.filter((i) => {
2375
+ const len = i.length();
2376
+ if (len < 0.4) return false;
2377
+ if (len > 1.2) {
2378
+ this.addData(i.start.clone(), i.end.clone());
2379
+ }
2380
+ return true;
2381
+ });
2382
+ }
1928
2383
  }
1929
2384
  class DxfSystem extends ComponentManager {
1930
2385
  Dxf;
@@ -2012,11 +2467,10 @@ class WhiteModel extends Component {
2012
2467
  depth: 2.8,
2013
2468
  bevelSize: 0
2014
2469
  });
2015
- const mesh = new THREE.Mesh(geometry);
2016
- mesh.material = this.material;
2470
+ const mesh = new THREE.Mesh(geometry, this.material);
2017
2471
  this.whiteModelGroup.add(mesh);
2018
2472
  this.whiteModelLineGroup.add(
2019
- new THREE.LineSegments(new THREE.EdgesGeometry(geometry), new THREE.LineBasicMaterial({ color: 6710886 }))
2473
+ new THREE.LineSegments(new THREE.EdgesGeometry(geometry), new THREE.LineBasicMaterial({ color: 0 }))
2020
2474
  );
2021
2475
  });
2022
2476
  const walls = dxf.originalData.map(({ start, end, insetionArr }) => {
@@ -2049,41 +2503,87 @@ class WhiteModel extends Component {
2049
2503
  whiteModelGroup: this.whiteModelGroup
2050
2504
  });
2051
2505
  }
2506
+ /**
2507
+ * 转为obj
2508
+ * @returns
2509
+ */
2052
2510
  toOBJ() {
2053
2511
  return new Promise((resolve) => {
2054
- resolve(exporter.parse(this.whiteModelGroup));
2512
+ this.material.opacity = 1;
2513
+ this.material.needsUpdate = true;
2514
+ setTimeout(() => {
2515
+ resolve(exporter.parse(this.whiteModelGroup));
2516
+ this.material.opacity = 0.8;
2517
+ this.material.transparent = true;
2518
+ }, 20);
2055
2519
  });
2056
2520
  }
2057
- toGlb() {
2521
+ /**
2522
+ * 转为 glb
2523
+ * @param binary
2524
+ * @returns
2525
+ */
2526
+ toGltf(binary = true) {
2058
2527
  return new Promise((resolve) => {
2059
2528
  this.material.opacity = 1;
2060
2529
  this.material.needsUpdate = true;
2061
- setTimeout(() => {
2062
- glbExporter.parse(this.whiteModelGroup.children, (gltf) => {
2063
- resolve(gltf);
2064
- this.material.opacity = 0.8;
2065
- this.material.transparent = true;
2066
- }, () => {
2067
- resolve(void 0);
2068
- }, {
2069
- binary: true
2070
- });
2071
- }, 100);
2530
+ setTimeout(async () => {
2531
+ if (typeof window === "object") {
2532
+ glbExporter.parse(this.whiteModelGroup.children, (gltf) => {
2533
+ resolve(gltf);
2534
+ this.material.opacity = 0.8;
2535
+ this.material.transparent = true;
2536
+ }, () => {
2537
+ resolve(void 0);
2538
+ }, {
2539
+ binary
2540
+ });
2541
+ } else if (typeof global !== "function") {
2542
+ try {
2543
+ const obj2gltf = await include("obj2gltf", true);
2544
+ const fs = await include("fs", false);
2545
+ const obj = await this.toOBJ();
2546
+ fs.writeFileSync(this.uuid, obj);
2547
+ const result = await obj2gltf(this.uuid, {
2548
+ binary
2549
+ });
2550
+ fs.unlinkSync(this.uuid);
2551
+ if (binary) resolve(result);
2552
+ else resolve(JSON.stringify(result));
2553
+ } catch (error) {
2554
+ resolve(void 0);
2555
+ console.log(error);
2556
+ }
2557
+ }
2558
+ }, 20);
2072
2559
  });
2073
2560
  }
2561
+ /**
2562
+ * 转为 OBJBlob
2563
+ * @returns
2564
+ */
2074
2565
  async toOBJBlob() {
2075
2566
  const buffer = await this.toOBJ();
2076
2567
  if (buffer) {
2077
2568
  return new Blob([buffer], { type: "application/octet-stream" });
2078
2569
  }
2079
2570
  }
2080
- async toGlbBlob() {
2081
- const buffer = await this.toGlb();
2571
+ /**
2572
+ * 转为 GltfBlob
2573
+ * @returns
2574
+ */
2575
+ async toGltfBlob(binary = true) {
2576
+ const buffer = await this.toGltf(binary);
2082
2577
  if (buffer) {
2083
2578
  return new Blob([buffer], { type: "application/octet-stream" });
2084
2579
  }
2085
2580
  }
2086
- async download(filename) {
2581
+ /**
2582
+ * 下载 OBJ
2583
+ * @param filename
2584
+ * @returns
2585
+ */
2586
+ async downloadOBJ(filename) {
2087
2587
  if (typeof window !== "undefined") {
2088
2588
  const blob = await this.toOBJBlob();
2089
2589
  if (!blob) return;
@@ -2094,32 +2594,29 @@ class WhiteModel extends Component {
2094
2594
  } else if (typeof global !== "undefined") {
2095
2595
  const buffer = await this.toOBJ();
2096
2596
  if (buffer) {
2097
- const packageName = "fs";
2098
- const { default: fs } = await import(
2099
- /* @vite-ignore */
2100
- packageName
2101
- );
2597
+ const fs = await include("fs", false);
2102
2598
  fs.writeFileSync(filename, buffer);
2103
2599
  }
2104
2600
  }
2105
2601
  }
2106
- async downloadGlb(filename) {
2602
+ /**
2603
+ * 下载 Gltf
2604
+ * @param filename
2605
+ * @returns
2606
+ */
2607
+ async downloadGltf(filename, binary = true) {
2107
2608
  if (typeof window !== "undefined") {
2108
- const blob = await this.toGlbBlob();
2609
+ const blob = await this.toGltfBlob(binary);
2109
2610
  if (!blob) return;
2110
2611
  const a = document.createElement("a");
2111
2612
  a.href = URL.createObjectURL(blob);
2112
2613
  a.download = filename;
2113
2614
  a.click();
2114
2615
  } else if (typeof global !== "undefined") {
2115
- const buffer = await this.toOBJ();
2616
+ const buffer = await this.toGltf(binary);
2116
2617
  if (buffer) {
2117
- const packageName = "fs";
2118
- const { default: fs } = await import(
2119
- /* @vite-ignore */
2120
- packageName
2121
- );
2122
- fs.writeFileSync(filename, buffer);
2618
+ const fs = await include("fs", false);
2619
+ fs.writeFileSync(filename, binary ? buffer : Buffer.from(buffer));
2123
2620
  }
2124
2621
  }
2125
2622
  }
@@ -2236,8 +2733,10 @@ class DxfLineModel extends Component {
2236
2733
  this.dxfLineModel.clear();
2237
2734
  const dxfArray = dxf.to3DArray(1 / dxf.scale);
2238
2735
  this.dxfLineModel.geometry = new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(dxfArray, 3, true));
2239
- 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);
2240
- const doorsColorArray = new Float32Array(dxf.doors.flatMap(() => [1, 0, 0, 0, 1, 0]));
2736
+ const doorsArray = new Float32Array(
2737
+ dxf.doorLineSegment.flatMap(({ start, end }) => [start.x, start.y, dxf.originalZAverage * 0.99, end.x, end.y, dxf.originalZAverage * 0.99])
2738
+ ).map((n) => n / dxf.scale);
2739
+ const doorsColorArray = new Float32Array(dxf.doorLineSegment.flatMap(() => [1, 0, 0, 0, 1, 0]));
2241
2740
  this.dxfDoorsLineModel.geometry = new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(doorsArray, 3, true)).setAttribute("color", new THREE.BufferAttribute(doorsColorArray, 3));
2242
2741
  }
2243
2742
  }