build-dxf 0.0.14 → 0.0.16
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/README.md +1 -1
- package/package.json +1 -1
- package/src/build.js +678 -179
- package/src/index2.js +1 -1
- package/src/utils/DxfSystem/components/Dxf.d.ts +27 -8
- package/src/utils/DxfSystem/components/LineAnalysis.d.ts +9 -0
- package/src/utils/Quadtree/LineSegment.d.ts +38 -0
- package/src/utils/Quadtree/Point.d.ts +8 -1
- package/src/utils/Quadtree/Quadtree.d.ts +6 -0
- package/src/utils/Quadtree/Rectangle.d.ts +1 -0
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ detailsPoint.addEventListener("handleSuccess", () => {
|
|
|
29
29
|
// 下载白模,obj格式
|
|
30
30
|
whiteModel.downloadOBJ("001.obj")
|
|
31
31
|
// 下载白模,gltf或glb格式, 第二个参数为true时是glb,默认为true
|
|
32
|
-
whiteModel.downloadGltf("001.
|
|
32
|
+
whiteModel.downloadGltf("001.glb", true)
|
|
33
33
|
|
|
34
34
|
// desPoints 为射线点集合,根据需要使用
|
|
35
35
|
console.log("handleSuccess", detailsPoint.desPoints)
|
package/package.json
CHANGED
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
|
|
@@ -817,8 +1072,8 @@ function pathToLines(path) {
|
|
|
817
1072
|
const lineSegments = [];
|
|
818
1073
|
for (let i = 0; i < path.length; i++) {
|
|
819
1074
|
lineSegments.push(new LineSegment(
|
|
820
|
-
path[i],
|
|
821
|
-
path[(i + 1) % path.length]
|
|
1075
|
+
path[i].clone(),
|
|
1076
|
+
path[(i + 1) % path.length].clone()
|
|
822
1077
|
));
|
|
823
1078
|
}
|
|
824
1079
|
return lineSegments;
|
|
@@ -841,6 +1096,7 @@ class Dxf extends Component {
|
|
|
841
1096
|
pointsGroups = [];
|
|
842
1097
|
wallsGroup = [];
|
|
843
1098
|
doors = [];
|
|
1099
|
+
doorLineSegment = [];
|
|
844
1100
|
lineSegments = [];
|
|
845
1101
|
originalZAverage = 0;
|
|
846
1102
|
static EndType = {
|
|
@@ -869,7 +1125,7 @@ class Dxf extends Component {
|
|
|
869
1125
|
super();
|
|
870
1126
|
this.width = width;
|
|
871
1127
|
this.scale = scale;
|
|
872
|
-
this.shortLine = width * 0.
|
|
1128
|
+
this.shortLine = width * 0.4;
|
|
873
1129
|
}
|
|
874
1130
|
/**
|
|
875
1131
|
* 设置
|
|
@@ -1003,6 +1259,28 @@ class Dxf extends Component {
|
|
|
1003
1259
|
}
|
|
1004
1260
|
return linePaths;
|
|
1005
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
|
+
}
|
|
1006
1284
|
/** etOpenRound 去除毛刺
|
|
1007
1285
|
* @description 检查连续的短线段数量,去除合并后产生的毛刺
|
|
1008
1286
|
*/
|
|
@@ -1039,44 +1317,22 @@ class Dxf extends Component {
|
|
|
1039
1317
|
}
|
|
1040
1318
|
return filterLines;
|
|
1041
1319
|
}
|
|
1042
|
-
epsilon = 1e-6;
|
|
1043
|
-
// 距离阈值,用于比较点
|
|
1044
1320
|
/**
|
|
1045
|
-
*
|
|
1046
|
-
* @
|
|
1047
|
-
* @
|
|
1321
|
+
* 线段矫直, 线段中心突刺
|
|
1322
|
+
* @description 突变长度小于墙体宽度,该线段可能为突起线段,
|
|
1323
|
+
* @description 判断后续第2线段与上一条线段是否方向相同,相同就为突刺
|
|
1048
1324
|
*/
|
|
1049
|
-
|
|
1325
|
+
lineSegmentStraightening(path) {
|
|
1050
1326
|
for (let i = 0; i < path.length; i++) {
|
|
1051
1327
|
const p1 = path[i];
|
|
1052
1328
|
const p2 = path[(i + 1) % path.length];
|
|
1053
1329
|
if (p1.distance(p2) > this.shortLine) {
|
|
1054
|
-
path.push(...path.slice(0, i));
|
|
1055
|
-
path.splice(0, i);
|
|
1330
|
+
path.push(...path.slice(0, i + 1));
|
|
1331
|
+
path.splice(0, i + 1);
|
|
1056
1332
|
break;
|
|
1057
1333
|
}
|
|
1058
1334
|
}
|
|
1059
|
-
|
|
1060
|
-
const cleanedPath = [];
|
|
1061
|
-
const n = path.length;
|
|
1062
|
-
for (let i = 0; i < n; i++) {
|
|
1063
|
-
const p1 = path[i];
|
|
1064
|
-
const p2 = path[(i + 1) % n];
|
|
1065
|
-
const p3 = path[(i + 2) % n];
|
|
1066
|
-
const cross = (p2.X - p1.X) * (p3.Y - p2.Y) - (p2.Y - p1.Y) * (p3.X - p2.X);
|
|
1067
|
-
if (Math.abs(cross) > this.epsilon) {
|
|
1068
|
-
cleanedPath.push(p1);
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
return cleanedPath;
|
|
1072
|
-
}
|
|
1073
|
-
/**
|
|
1074
|
-
* 线段矫直, 线段中心突刺
|
|
1075
|
-
* @description 突变长度小于墙体宽度,该线段可能为突起线段,
|
|
1076
|
-
* @description 判断后续第2线段与上一条线段是否方向相同,相同就为突刺
|
|
1077
|
-
*/
|
|
1078
|
-
lineSegmentStraightening(path) {
|
|
1079
|
-
const lines = pathToLines(path), filterLines = [lines[0]];
|
|
1335
|
+
const lines = this.mergeSameDirectionLine(pathToLines(path)), filterLines = [lines[0]];
|
|
1080
1336
|
for (let i = 1; i < lines.length; i++) {
|
|
1081
1337
|
const line = lines[i];
|
|
1082
1338
|
const preLine = lines[(lines.length + i - 1) % lines.length];
|
|
@@ -1092,15 +1348,16 @@ class Dxf extends Component {
|
|
|
1092
1348
|
continue;
|
|
1093
1349
|
}
|
|
1094
1350
|
const line2 = lines[i + 2];
|
|
1095
|
-
if (line2 && preLine.includedAngle(line2) <
|
|
1351
|
+
if (line2 && preLine.includedAngle(line2) < 2) {
|
|
1096
1352
|
i = i + 2;
|
|
1097
1353
|
filterLines.push(line2);
|
|
1098
1354
|
} else filterLines.push(line);
|
|
1099
1355
|
}
|
|
1100
|
-
return filterLines.length > 3 ? linesToPath(filterLines) : [];
|
|
1356
|
+
return filterLines.length > 3 ? linesToPath(this.mergeSameDirectionLine(filterLines)) : [];
|
|
1101
1357
|
}
|
|
1102
1358
|
/**
|
|
1103
1359
|
* 移除短线段
|
|
1360
|
+
* @todo 根据线段两端线段长度,选取参照物_|▔▔
|
|
1104
1361
|
* @param path
|
|
1105
1362
|
*/
|
|
1106
1363
|
removeShortLine(path, shortLine = this.shortLine) {
|
|
@@ -1153,9 +1410,9 @@ class Dxf extends Component {
|
|
|
1153
1410
|
offset.Execute(solutions, this.width / 2 * scale);
|
|
1154
1411
|
this.wallsGroup = solutions.map((ps) => {
|
|
1155
1412
|
let path = ps.map((p) => Point.from(p).divisionScalar(scale));
|
|
1156
|
-
path = this.removeCollinearPoints(path);
|
|
1157
1413
|
path = this.lineSegmentStraightening(path);
|
|
1158
1414
|
if (endType == Dxf.EndType.etOpenSquare) path = this.squareRemoveBurr(path);
|
|
1415
|
+
path = this.removeShortLine(path);
|
|
1159
1416
|
return path;
|
|
1160
1417
|
});
|
|
1161
1418
|
this.dispatchEvent({
|
|
@@ -1179,6 +1436,32 @@ class Dxf extends Component {
|
|
|
1179
1436
|
});
|
|
1180
1437
|
return new Float32Array(array);
|
|
1181
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
|
+
}
|
|
1182
1465
|
/**
|
|
1183
1466
|
* 将点云结构转换为string
|
|
1184
1467
|
*/
|
|
@@ -1186,12 +1469,37 @@ class Dxf extends Component {
|
|
|
1186
1469
|
const d = new Drawing();
|
|
1187
1470
|
d.setUnits("Millimeters");
|
|
1188
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
|
+
}
|
|
1189
1475
|
this.wallsGroup.forEach((points) => {
|
|
1190
1476
|
for (let i = 0; i < points.length; i++) {
|
|
1191
1477
|
const point1 = points[i];
|
|
1192
1478
|
const nextIndex = i === points.length - 1 ? 0 : i + 1;
|
|
1193
1479
|
const point2 = points[nextIndex];
|
|
1194
|
-
|
|
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));
|
|
1195
1503
|
}
|
|
1196
1504
|
});
|
|
1197
1505
|
return d.toDxfString();
|
|
@@ -1291,141 +1599,6 @@ class Variable extends Component {
|
|
|
1291
1599
|
if (key in this) return this[key];
|
|
1292
1600
|
}
|
|
1293
1601
|
}
|
|
1294
|
-
class Rectangle {
|
|
1295
|
-
points;
|
|
1296
|
-
get path() {
|
|
1297
|
-
return this.points.flatMap((p, i) => {
|
|
1298
|
-
const np = this.points[(i + 1) % this.points.length];
|
|
1299
|
-
return [p.x, p.y, 0, np.x, np.y, 0];
|
|
1300
|
-
});
|
|
1301
|
-
}
|
|
1302
|
-
constructor(points) {
|
|
1303
|
-
if (points.length !== 4) {
|
|
1304
|
-
throw new Error("Rectangle must be defined by exactly 4 points");
|
|
1305
|
-
}
|
|
1306
|
-
this.points = points;
|
|
1307
|
-
}
|
|
1308
|
-
/**
|
|
1309
|
-
* 判断线段是否与矩形相交
|
|
1310
|
-
* @param line 线段
|
|
1311
|
-
* @returns 是否与矩形相交
|
|
1312
|
-
*/
|
|
1313
|
-
intersectLineSegment(line) {
|
|
1314
|
-
if (line.points.length !== 2) {
|
|
1315
|
-
throw new Error("LineSegment must have exactly 2 points");
|
|
1316
|
-
}
|
|
1317
|
-
const [p1, p2] = line.points;
|
|
1318
|
-
const doIntersect = (l1p1, l1p2, l2p1, l2p2) => {
|
|
1319
|
-
const orientation = (p, q, r) => {
|
|
1320
|
-
const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
1321
|
-
if (val === 0) return 0;
|
|
1322
|
-
return val > 0 ? 1 : 2;
|
|
1323
|
-
};
|
|
1324
|
-
const onSegment = (p, q, r) => {
|
|
1325
|
-
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);
|
|
1326
|
-
};
|
|
1327
|
-
const o1 = orientation(l1p1, l1p2, l2p1);
|
|
1328
|
-
const o2 = orientation(l1p1, l1p2, l2p2);
|
|
1329
|
-
const o3 = orientation(l2p1, l2p2, l1p1);
|
|
1330
|
-
const o4 = orientation(l2p1, l2p2, l1p2);
|
|
1331
|
-
if (o1 !== o2 && o3 !== o4) return true;
|
|
1332
|
-
if (o1 === 0 && onSegment(l1p1, l2p1, l1p2)) return true;
|
|
1333
|
-
if (o2 === 0 && onSegment(l1p1, l2p2, l1p2)) return true;
|
|
1334
|
-
if (o3 === 0 && onSegment(l2p1, l1p1, l2p2)) return true;
|
|
1335
|
-
if (o4 === 0 && onSegment(l2p1, l1p2, l2p2)) return true;
|
|
1336
|
-
return false;
|
|
1337
|
-
};
|
|
1338
|
-
for (let i = 0; i < 4; i++) {
|
|
1339
|
-
const rectP1 = this.points[i];
|
|
1340
|
-
const rectP2 = this.points[(i + 1) % 4];
|
|
1341
|
-
if (doIntersect(p1, p2, rectP1, rectP2)) {
|
|
1342
|
-
return true;
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
if (this.containsLineSegment(line)) {
|
|
1346
|
-
return true;
|
|
1347
|
-
}
|
|
1348
|
-
return false;
|
|
1349
|
-
}
|
|
1350
|
-
/**
|
|
1351
|
-
* 判断线段是否完全位于矩形内部
|
|
1352
|
-
* @param line 线段
|
|
1353
|
-
* @returns 是否完全在矩形内部
|
|
1354
|
-
*/
|
|
1355
|
-
containsLineSegment(line) {
|
|
1356
|
-
if (line.points.length !== 2) {
|
|
1357
|
-
throw new Error("LineSegment must have exactly 2 points");
|
|
1358
|
-
}
|
|
1359
|
-
const isPointInRectangle = (point) => {
|
|
1360
|
-
let sign = 0;
|
|
1361
|
-
for (let i = 0; i < 4; i++) {
|
|
1362
|
-
const p1 = this.points[i];
|
|
1363
|
-
const p2 = this.points[(i + 1) % 4];
|
|
1364
|
-
const edge = { x: p2.x - p1.x, y: p2.y - p1.y };
|
|
1365
|
-
const toPoint = { x: point.x - p1.x, y: point.y - p1.y };
|
|
1366
|
-
const cross = edge.x * toPoint.y - edge.y * toPoint.x;
|
|
1367
|
-
if (cross === 0) {
|
|
1368
|
-
const t = edge.x !== 0 ? (point.x - p1.x) / edge.x : (point.y - p1.y) / edge.y;
|
|
1369
|
-
if (t >= 0 && t <= 1) return true;
|
|
1370
|
-
} else {
|
|
1371
|
-
const currentSign = cross > 0 ? 1 : -1;
|
|
1372
|
-
if (sign === 0) sign = currentSign;
|
|
1373
|
-
if (sign !== currentSign) return false;
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
return true;
|
|
1377
|
-
};
|
|
1378
|
-
return isPointInRectangle(line.points[0]) && isPointInRectangle(line.points[1]);
|
|
1379
|
-
}
|
|
1380
|
-
/**
|
|
1381
|
-
* 判断点是否完全位于矩形内部
|
|
1382
|
-
* @param point
|
|
1383
|
-
*/
|
|
1384
|
-
containsPoint(point) {
|
|
1385
|
-
let positiveCount = 0;
|
|
1386
|
-
let negativeCount = 0;
|
|
1387
|
-
for (let i = 0; i < 4; i++) {
|
|
1388
|
-
const p1 = this.points[i];
|
|
1389
|
-
const p2 = this.points[(i + 1) % 4];
|
|
1390
|
-
const cross = (p2.x - p1.x) * (point.y - p1.y) - (p2.y - p1.y) * (point.x - p1.x);
|
|
1391
|
-
if (cross > 0) positiveCount++;
|
|
1392
|
-
else if (cross < 0) negativeCount++;
|
|
1393
|
-
else return false;
|
|
1394
|
-
}
|
|
1395
|
-
return positiveCount === 4 || negativeCount === 4;
|
|
1396
|
-
}
|
|
1397
|
-
/**
|
|
1398
|
-
*
|
|
1399
|
-
* @returns
|
|
1400
|
-
*/
|
|
1401
|
-
toBox() {
|
|
1402
|
-
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
1403
|
-
this.points.forEach((p) => {
|
|
1404
|
-
maxX = Math.max(p.x, maxX);
|
|
1405
|
-
minX = Math.min(p.x, minX);
|
|
1406
|
-
maxY = Math.max(p.x, maxY);
|
|
1407
|
-
minY = Math.min(p.x, minY);
|
|
1408
|
-
});
|
|
1409
|
-
return new Box2(minX, maxX, minY, maxY);
|
|
1410
|
-
}
|
|
1411
|
-
/**
|
|
1412
|
-
*
|
|
1413
|
-
* @param line
|
|
1414
|
-
* @param width
|
|
1415
|
-
* @returns
|
|
1416
|
-
*/
|
|
1417
|
-
static fromByLineSegment(line, width = 0.1, horizontal = false, hScale = 0.5) {
|
|
1418
|
-
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();
|
|
1419
|
-
const offsetX = normal.x * width * 0.5;
|
|
1420
|
-
const offsetY = normal.y * width * 0.5;
|
|
1421
|
-
return new Rectangle([
|
|
1422
|
-
new Point(p1.x + offsetX, p1.y + offsetY).add(nDirect),
|
|
1423
|
-
new Point(p2.x + offsetX, p2.y + offsetY).add(pDirect),
|
|
1424
|
-
new Point(p2.x - offsetX, p2.y - offsetY).add(pDirect),
|
|
1425
|
-
new Point(p1.x - offsetX, p1.y - offsetY).add(nDirect)
|
|
1426
|
-
]);
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
1602
|
class Quadtree {
|
|
1430
1603
|
bounds;
|
|
1431
1604
|
// 包围盒
|
|
@@ -1623,6 +1796,28 @@ class Quadtree {
|
|
|
1623
1796
|
}
|
|
1624
1797
|
return result;
|
|
1625
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
|
+
}
|
|
1626
1821
|
/**
|
|
1627
1822
|
* 包围盒转换为数组
|
|
1628
1823
|
* @param array
|
|
@@ -1780,6 +1975,7 @@ class LineAnalysis extends Component {
|
|
|
1780
1975
|
this.Dxf = parent.findComponentByType(Dxf);
|
|
1781
1976
|
this.Variable = this.parent?.findComponentByType(Variable);
|
|
1782
1977
|
this.Dxf.addEventListener("setDta", this.lineAnalysis.bind(this));
|
|
1978
|
+
this.Dxf.addEventListener("createGroup", this.doorsAnalysis.bind(this));
|
|
1783
1979
|
}
|
|
1784
1980
|
/**
|
|
1785
1981
|
*
|
|
@@ -1847,8 +2043,9 @@ class LineAnalysis extends Component {
|
|
|
1847
2043
|
buildVirtualGrid() {
|
|
1848
2044
|
const dxf = this.Dxf;
|
|
1849
2045
|
const pointVirtualGrid = new PointVirtualGrid();
|
|
1850
|
-
dxf.
|
|
1851
|
-
|
|
2046
|
+
dxf.lineSegments.forEach((d, index2) => {
|
|
2047
|
+
if (d.userData?.isDoor) return;
|
|
2048
|
+
const [p1, p2] = [d.start, d.end];
|
|
1852
2049
|
pointVirtualGrid.insert(p1, { index: index2, type: "start" });
|
|
1853
2050
|
pointVirtualGrid.insert(p2, { index: index2, type: "end" });
|
|
1854
2051
|
});
|
|
@@ -1873,6 +2070,7 @@ class LineAnalysis extends Component {
|
|
|
1873
2070
|
this.lineSegmentList = lineSegmentList;
|
|
1874
2071
|
}
|
|
1875
2072
|
resultList = [];
|
|
2073
|
+
mergeWallLines = [];
|
|
1876
2074
|
/** 线段分析
|
|
1877
2075
|
* @description 判断两条线段距离是否较短且趋近平行,然后查找两条线段的重合部分的投影线,以此判断两根线是否需要合并
|
|
1878
2076
|
* @param data
|
|
@@ -1897,7 +2095,7 @@ class LineAnalysis extends Component {
|
|
|
1897
2095
|
});
|
|
1898
2096
|
this.appendLineSegmentList.length = 0;
|
|
1899
2097
|
resultList.forEach(this.createRectangle.bind(this));
|
|
1900
|
-
this.resultList =
|
|
2098
|
+
this.resultList = resultList;
|
|
1901
2099
|
}
|
|
1902
2100
|
/** 线段投影分析
|
|
1903
2101
|
* @param index
|
|
@@ -1929,10 +2127,309 @@ class LineAnalysis extends Component {
|
|
|
1929
2127
|
project2: p1
|
|
1930
2128
|
};
|
|
1931
2129
|
}
|
|
1932
|
-
if (!data || data.project.getLength() < 0.
|
|
2130
|
+
if (!data || data.project.getLength() < 0.2 || data.project2.getLength() < 0.2) return;
|
|
1933
2131
|
return data;
|
|
1934
2132
|
}
|
|
1935
2133
|
}
|
|
2134
|
+
doorSearchNearAngle = 110;
|
|
2135
|
+
doorSearchDistance = 2;
|
|
2136
|
+
doors = [];
|
|
2137
|
+
/**
|
|
2138
|
+
* 门的位置判断
|
|
2139
|
+
*/
|
|
2140
|
+
doorsAnalysis() {
|
|
2141
|
+
this.parent?.findComponentByName("Renderer");
|
|
2142
|
+
const dxf = this.Dxf, doorPoints = [], pointVirtualGrid = this.pointVirtualGrid, quadtree = this.quadtree, doorSearchNearAngle = this.doorSearchNearAngle, doorSearchDistance = this.doorSearchDistance, doors = [];
|
|
2143
|
+
const excludePoints = dxf.doors.flatMap((item) => {
|
|
2144
|
+
const index2 = item[4];
|
|
2145
|
+
const doorData = dxf.originalData[index2];
|
|
2146
|
+
if (doorData.drawDoorData) {
|
|
2147
|
+
const point = Point.from(doorData.drawDoorData.start);
|
|
2148
|
+
const direct = Point.from(doorData.drawDoorData.n);
|
|
2149
|
+
const resList = pointVirtualGrid.queryPoint(point).filter((res) => {
|
|
2150
|
+
if (res.userData?.index === index2) return false;
|
|
2151
|
+
const line = dxf.lineSegments[res.userData?.index];
|
|
2152
|
+
const direct2 = line.direction();
|
|
2153
|
+
if (line.start.equal(point)) direct2.multiplyScalar(-1);
|
|
2154
|
+
const angle = direct.angleBetween(direct2, "angle");
|
|
2155
|
+
return angle > 80 || angle < 10;
|
|
2156
|
+
});
|
|
2157
|
+
if (resList.length) {
|
|
2158
|
+
const index22 = resList[0].userData?.index;
|
|
2159
|
+
doorPoints.push({
|
|
2160
|
+
line: dxf.lineSegments[index22],
|
|
2161
|
+
point: Point.from(doorData.drawDoorData.start),
|
|
2162
|
+
index: index22,
|
|
2163
|
+
direct,
|
|
2164
|
+
sure: true
|
|
2165
|
+
});
|
|
2166
|
+
return [point];
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
return [];
|
|
2170
|
+
});
|
|
2171
|
+
const excludeIndexMap = /* @__PURE__ */ new Map();
|
|
2172
|
+
this.resultList.flatMap((p) => {
|
|
2173
|
+
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;
|
|
2174
|
+
if (excludeIndexMap.has(p.sourceIndex)) {
|
|
2175
|
+
if (excludeIndexMap.get(p.sourceIndex) != mode0) excludeIndexMap.set(p.sourceIndex, -1);
|
|
2176
|
+
} else excludeIndexMap.set(p.sourceIndex, mode0);
|
|
2177
|
+
if (excludeIndexMap.has(p.targetIndex)) {
|
|
2178
|
+
if (excludeIndexMap.get(p.targetIndex) != mode1) excludeIndexMap.set(p.targetIndex, -1);
|
|
2179
|
+
} else excludeIndexMap.set(p.targetIndex, mode1);
|
|
2180
|
+
});
|
|
2181
|
+
dxf.lineSegments.forEach((line, i) => {
|
|
2182
|
+
if (line.userData?.isDoor) return;
|
|
2183
|
+
const excludeMode = excludeIndexMap.get(i);
|
|
2184
|
+
if (excludeMode === -1) return;
|
|
2185
|
+
line.points.forEach((p, j) => {
|
|
2186
|
+
if (excludeMode === j) return;
|
|
2187
|
+
if (excludePoints.find((p1) => p1.equal(p))) return;
|
|
2188
|
+
const res = this.pointVirtualGrid.queryPoint(p).filter((d) => d.userData?.index !== i);
|
|
2189
|
+
if (res.length === 0) {
|
|
2190
|
+
doorPoints.push({
|
|
2191
|
+
line,
|
|
2192
|
+
point: p,
|
|
2193
|
+
index: i
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
});
|
|
2198
|
+
function searchNearby({ point, line, index: index2 }, doorIndex, record2) {
|
|
2199
|
+
const direct = line.direction();
|
|
2200
|
+
if (line.start === point) direct.multiplyScalar(-1);
|
|
2201
|
+
const res = pointVirtualGrid.queryCircle(point, doorSearchDistance).filter(
|
|
2202
|
+
(r) => (
|
|
2203
|
+
// 不能是自己
|
|
2204
|
+
r.userData?.index !== index2 && // 存在于可疑点位内,且不能是已知门的点位
|
|
2205
|
+
!!doorPoints.find((p) => !p.sure && p.point === r.point)
|
|
2206
|
+
)
|
|
2207
|
+
).sort((a, b) => a.point.distance(point) - b.point.distance(point));
|
|
2208
|
+
const list = [];
|
|
2209
|
+
for (let i = 0; i < res.length; i++) {
|
|
2210
|
+
const doorIndex2 = doorPoints.findIndex((p) => p.point === res[i].point);
|
|
2211
|
+
if (record2.has(`${doorIndex}.${doorIndex2}`)) continue;
|
|
2212
|
+
record2.add(`${doorIndex}.${doorIndex2}`);
|
|
2213
|
+
record2.add(`${doorIndex2}.${doorIndex}`);
|
|
2214
|
+
const targetPoint = res[i].point, line2 = new LineSegment(point.clone(), targetPoint.clone()), angle = line2.direction().angleBetween(direct, "angle");
|
|
2215
|
+
if (angle < doorSearchNearAngle) {
|
|
2216
|
+
const direct2 = doorPoints[doorIndex2].line.direction();
|
|
2217
|
+
const line22 = dxf.lineSegments[res[i].userData?.index];
|
|
2218
|
+
if (line22.start.equal(res[i].point)) direct2.multiplyScalar(-1);
|
|
2219
|
+
const angle2 = line2.direction().multiplyScalar(-1).angleBetween(direct2, "angle");
|
|
2220
|
+
if (angle2 < doorSearchNearAngle) {
|
|
2221
|
+
if (!quadtree.queryLineSegment(line2).length) {
|
|
2222
|
+
list.push({
|
|
2223
|
+
findData: res[i],
|
|
2224
|
+
findDoorIndex: doorIndex2,
|
|
2225
|
+
findAngle: angle2,
|
|
2226
|
+
doorAngle: angle,
|
|
2227
|
+
doorLine: line2,
|
|
2228
|
+
doorIndex
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
return list;
|
|
2235
|
+
}
|
|
2236
|
+
function searchAlongDirection({ point, line }) {
|
|
2237
|
+
const direct = line.direction();
|
|
2238
|
+
if (line.start === point) direct.multiplyScalar(-1);
|
|
2239
|
+
const endPoint = point.clone().add(direct.clone().multiplyScalar(doorSearchDistance)), rline = new LineSegment(point.clone(), endPoint), result = quadtree.queryLineSegment(rline).map((l) => {
|
|
2240
|
+
const res = l.line.getIntersection(rline);
|
|
2241
|
+
return {
|
|
2242
|
+
point: res,
|
|
2243
|
+
line: l.line
|
|
2244
|
+
};
|
|
2245
|
+
}).filter((i) => i.point).sort((a, b) => point.distance(a.point) - point.distance(b.point));
|
|
2246
|
+
if (result.length) {
|
|
2247
|
+
const item = result[0];
|
|
2248
|
+
if (Math.abs(90 - item.line.direction().angleBetween(direct, "angle")) < 5) {
|
|
2249
|
+
return item;
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
function searchAlongNormalDirection({ point, line, index: index2 }) {
|
|
2254
|
+
const direct = line.direction();
|
|
2255
|
+
if (line.start === point) direct.multiplyScalar(-1);
|
|
2256
|
+
const normal = line.start.normal(line.end);
|
|
2257
|
+
const prePoint = line.start.clone();
|
|
2258
|
+
if (line.start === point) prePoint.copy(line.end);
|
|
2259
|
+
const result = pointVirtualGrid.queryPoint(prePoint).filter((r) => r.userData?.index !== index2);
|
|
2260
|
+
for (let i = 0; i < result.length; i++) {
|
|
2261
|
+
const element = result[i];
|
|
2262
|
+
const l = dxf.lineSegments[element.userData?.index ?? 0];
|
|
2263
|
+
const d1 = l.direction();
|
|
2264
|
+
if (l.start === element.point) direct.multiplyScalar(-1);
|
|
2265
|
+
const angle = d1.angleBetween(normal) / (Math.PI / 180);
|
|
2266
|
+
if (angle > 90) {
|
|
2267
|
+
normal.multiplyScalar(-1);
|
|
2268
|
+
break;
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
const rline3 = new LineSegment(point.clone(), point.clone().add(normal.multiplyScalar(doorSearchDistance)));
|
|
2272
|
+
const r3 = quadtree.queryLineSegment(rline3).map((l) => {
|
|
2273
|
+
const res = l.line.getIntersection(rline3);
|
|
2274
|
+
return {
|
|
2275
|
+
point: res,
|
|
2276
|
+
line: l.line
|
|
2277
|
+
};
|
|
2278
|
+
}).filter((i) => i.point).sort((a, b) => point.distance(a.point) - point.distance(b.point));
|
|
2279
|
+
if (r3.length) {
|
|
2280
|
+
const item = r3[0];
|
|
2281
|
+
if (Math.abs(90 - item.line.direction().angleBetween(normal, "angle")) < 5) {
|
|
2282
|
+
return item;
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
function deepSearchNearHandle(index2, snFindRecord2, list, record2, other) {
|
|
2287
|
+
record2.add(`${index2}`);
|
|
2288
|
+
const newList = [];
|
|
2289
|
+
if (other) newList.push(other);
|
|
2290
|
+
for (let i = 0; i < list.length; i++) {
|
|
2291
|
+
const item = list[i];
|
|
2292
|
+
if (snFindRecord2.has(item.findDoorIndex)) {
|
|
2293
|
+
const list2 = snFindRecord2.get(item.findDoorIndex);
|
|
2294
|
+
if (deepSearchNearHandle(item.findDoorIndex, snFindRecord2, list2, record2, item)) newList.push(item);
|
|
2295
|
+
} else newList.push(item);
|
|
2296
|
+
}
|
|
2297
|
+
newList.sort((a, b) => a.doorLine.length() - b.doorLine.length());
|
|
2298
|
+
if (other && newList[0] === other) {
|
|
2299
|
+
list.splice(0);
|
|
2300
|
+
return true;
|
|
2301
|
+
}
|
|
2302
|
+
list.splice(1);
|
|
2303
|
+
return false;
|
|
2304
|
+
}
|
|
2305
|
+
const record = /* @__PURE__ */ new Set();
|
|
2306
|
+
const snFindRecord = /* @__PURE__ */ new Map();
|
|
2307
|
+
doorPoints.map((p, index2) => {
|
|
2308
|
+
if (dxf.doors.length >= 2 && !p.sure) return;
|
|
2309
|
+
const list = searchNearby(p, index2, record);
|
|
2310
|
+
if (list.length) snFindRecord.set(index2, list);
|
|
2311
|
+
});
|
|
2312
|
+
record.clear();
|
|
2313
|
+
const temMap = /* @__PURE__ */ new Map();
|
|
2314
|
+
snFindRecord.forEach((list, key) => {
|
|
2315
|
+
if (!record.has(`${key}`) && list.length) {
|
|
2316
|
+
deepSearchNearHandle(key, snFindRecord, list, record);
|
|
2317
|
+
}
|
|
2318
|
+
if (list.length) {
|
|
2319
|
+
const item = list[0];
|
|
2320
|
+
if (!temMap.has(item.doorIndex)) temMap.set(item.doorIndex, []);
|
|
2321
|
+
temMap.get(item.doorIndex)?.push(item);
|
|
2322
|
+
if (!temMap.has(item.findDoorIndex)) temMap.set(item.findDoorIndex, []);
|
|
2323
|
+
temMap.get(item.findDoorIndex)?.push(item);
|
|
2324
|
+
}
|
|
2325
|
+
});
|
|
2326
|
+
const deleteSet = /* @__PURE__ */ new Set();
|
|
2327
|
+
temMap.forEach((list) => {
|
|
2328
|
+
if (list.length > 1) {
|
|
2329
|
+
list.sort((a, b) => a.doorLine.length() - b.doorLine.length());
|
|
2330
|
+
for (let i = 1; i < list.length; i++) deleteSet.add(list[i]);
|
|
2331
|
+
}
|
|
2332
|
+
});
|
|
2333
|
+
const searchNearRasult = [], removeDoorPointsIndex = [];
|
|
2334
|
+
snFindRecord.forEach((list) => {
|
|
2335
|
+
if (list.length) {
|
|
2336
|
+
const item = list[0];
|
|
2337
|
+
if (!deleteSet.has(item)) {
|
|
2338
|
+
searchNearRasult.push(item);
|
|
2339
|
+
removeDoorPointsIndex.push(item.doorIndex, item.findDoorIndex);
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
});
|
|
2343
|
+
const doors_ = doorPoints.map((item, index2) => {
|
|
2344
|
+
if (removeDoorPointsIndex.includes(index2)) return;
|
|
2345
|
+
if (dxf.doors.length >= 2 && !item.sure) return;
|
|
2346
|
+
const res2 = searchAlongDirection(item);
|
|
2347
|
+
if (res2) return {
|
|
2348
|
+
start: item.point,
|
|
2349
|
+
end: res2.point,
|
|
2350
|
+
index: item.index
|
|
2351
|
+
};
|
|
2352
|
+
const res3 = searchAlongNormalDirection(item);
|
|
2353
|
+
if (res3) return {
|
|
2354
|
+
start: item.point,
|
|
2355
|
+
end: res3.point,
|
|
2356
|
+
index: item.index
|
|
2357
|
+
};
|
|
2358
|
+
}).filter((i) => !!i && i.start.distance(i.end) < doorSearchDistance);
|
|
2359
|
+
doors.push(...doors_);
|
|
2360
|
+
searchNearRasult.forEach((item, i) => {
|
|
2361
|
+
const start = doorPoints[item.doorIndex].point.clone();
|
|
2362
|
+
const end = doorPoints[item.findDoorIndex].point.clone();
|
|
2363
|
+
const startLine = this.findLongLineSegment(doorPoints[item.doorIndex].line);
|
|
2364
|
+
const endLine = this.findLongLineSegment(doorPoints[item.findDoorIndex].line);
|
|
2365
|
+
const p = startLine.projectPoint(end);
|
|
2366
|
+
if (p) {
|
|
2367
|
+
start.copy(p);
|
|
2368
|
+
const l = new LineSegment(start, end);
|
|
2369
|
+
const angle = endLine.includedAngle(l);
|
|
2370
|
+
if (angle < 10 || angle > 170 || Math.abs(90 - angle) < 10) {
|
|
2371
|
+
doors.push({
|
|
2372
|
+
start,
|
|
2373
|
+
end,
|
|
2374
|
+
index: doorPoints[item.doorIndex].index
|
|
2375
|
+
});
|
|
2376
|
+
}
|
|
2377
|
+
} else {
|
|
2378
|
+
const p2 = endLine.projectPoint(start);
|
|
2379
|
+
if (p2) end.copy(p2);
|
|
2380
|
+
const l = new LineSegment(start, end);
|
|
2381
|
+
const angle = startLine.includedAngle(l);
|
|
2382
|
+
if (angle < 10 || angle > 170 || Math.abs(90 - angle) < 10) {
|
|
2383
|
+
doors.push({
|
|
2384
|
+
start,
|
|
2385
|
+
end,
|
|
2386
|
+
index: doorPoints[item.doorIndex].index
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
});
|
|
2391
|
+
dxf.doorLineSegment.length = 0;
|
|
2392
|
+
doors.forEach((p) => {
|
|
2393
|
+
const line = new LineSegment(p?.start, p?.end);
|
|
2394
|
+
const len = line.length();
|
|
2395
|
+
if (len < 0.4) return;
|
|
2396
|
+
const center = line.center, normal = line.normal();
|
|
2397
|
+
const rLine = new LineSegment(
|
|
2398
|
+
center.clone(),
|
|
2399
|
+
center.clone().add(normal.clone().multiplyScalar(1))
|
|
2400
|
+
);
|
|
2401
|
+
rLine.directionMove(normal, -0.5);
|
|
2402
|
+
const res = this.quadtree?.queryLineSegment(rLine);
|
|
2403
|
+
if (res?.length) return;
|
|
2404
|
+
dxf.doorLineSegment.push(line);
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
findLongLineSegment(line) {
|
|
2408
|
+
const resLine = line.clone();
|
|
2409
|
+
const res1 = this.pointVirtualGrid.queryPoint(line.start);
|
|
2410
|
+
const res2 = this.pointVirtualGrid.queryPoint(line.end);
|
|
2411
|
+
for (let i = 0; i < res1.length; i++) {
|
|
2412
|
+
const { userData } = res1[i];
|
|
2413
|
+
const line2 = this.lineSegmentList[userData?.index];
|
|
2414
|
+
if (line2 === line) continue;
|
|
2415
|
+
if (line2 && line2.directionEqual(line)) {
|
|
2416
|
+
if (line2.start.equal(line.start)) resLine.start.copy(line2.end);
|
|
2417
|
+
else resLine.start.copy(line2.start);
|
|
2418
|
+
break;
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
for (let i = 0; i < res2.length; i++) {
|
|
2422
|
+
const { userData } = res2[i];
|
|
2423
|
+
const line2 = this.lineSegmentList[userData?.index];
|
|
2424
|
+
if (line2 === line) continue;
|
|
2425
|
+
if (line2 && line2.directionEqual(line)) {
|
|
2426
|
+
if (line2.end.equal(line.end)) resLine.end.copy(line2.start);
|
|
2427
|
+
else resLine.end.copy(line2.end);
|
|
2428
|
+
break;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
return resLine;
|
|
2432
|
+
}
|
|
1936
2433
|
}
|
|
1937
2434
|
class DxfSystem extends ComponentManager {
|
|
1938
2435
|
Dxf;
|
|
@@ -2286,8 +2783,10 @@ class DxfLineModel extends Component {
|
|
|
2286
2783
|
this.dxfLineModel.clear();
|
|
2287
2784
|
const dxfArray = dxf.to3DArray(1 / dxf.scale);
|
|
2288
2785
|
this.dxfLineModel.geometry = new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(dxfArray, 3, true));
|
|
2289
|
-
const doorsArray = new Float32Array(
|
|
2290
|
-
|
|
2786
|
+
const doorsArray = new Float32Array(
|
|
2787
|
+
dxf.doorLineSegment.flatMap(({ start, end }) => [start.x, start.y, dxf.originalZAverage * 0.99, end.x, end.y, dxf.originalZAverage * 0.99])
|
|
2788
|
+
).map((n) => n / dxf.scale);
|
|
2789
|
+
const doorsColorArray = new Float32Array(dxf.doorLineSegment.flatMap(() => [1, 0, 0, 0, 1, 0]));
|
|
2291
2790
|
this.dxfDoorsLineModel.geometry = new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(doorsArray, 3, true)).setAttribute("color", new THREE.BufferAttribute(doorsColorArray, 3));
|
|
2292
2791
|
}
|
|
2293
2792
|
}
|
package/src/index2.js
CHANGED
|
@@ -7127,7 +7127,7 @@ class ModelDataRender extends Component {
|
|
|
7127
7127
|
group.clear();
|
|
7128
7128
|
dxf.wallsGroup.forEach((path, j) => {
|
|
7129
7129
|
path.forEach((p, i) => {
|
|
7130
|
-
renderer.createText(`${i}`, p, { color: "#ff00ff", fontSize: "10px" }, group).position.z = dxf.originalZAverage * 0.99;
|
|
7130
|
+
renderer.createText(`${j}-${i}`, p, { color: "#ff00ff", fontSize: "10px" }, group).position.z = dxf.originalZAverage * 0.99;
|
|
7131
7131
|
});
|
|
7132
7132
|
});
|
|
7133
7133
|
}
|
|
@@ -23,6 +23,18 @@ export interface OriginalDataItem {
|
|
|
23
23
|
};
|
|
24
24
|
}[];
|
|
25
25
|
isDoor?: boolean;
|
|
26
|
+
drawDoorData?: {
|
|
27
|
+
start: {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
z: number;
|
|
31
|
+
};
|
|
32
|
+
n: {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
z: number;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
26
38
|
}
|
|
27
39
|
/**
|
|
28
40
|
* [开始点, 结束点, 相交点, 是否是门, 索引]
|
|
@@ -61,6 +73,7 @@ export declare class Dxf extends Component<{
|
|
|
61
73
|
pointsGroups: Point[][][];
|
|
62
74
|
wallsGroup: Point[][];
|
|
63
75
|
doors: DataItem[];
|
|
76
|
+
doorLineSegment: LineSegment[];
|
|
64
77
|
lineSegments: LineSegment<{
|
|
65
78
|
isDoor: boolean;
|
|
66
79
|
}>[];
|
|
@@ -108,18 +121,16 @@ export declare class Dxf extends Component<{
|
|
|
108
121
|
* @description 处理线路拓扑,使线路有序链接,形成长路径
|
|
109
122
|
* @param lines
|
|
110
123
|
*/
|
|
111
|
-
|
|
124
|
+
lineTopology(lines: Point[][]): Point[][];
|
|
125
|
+
/** 合并方向相同的线段
|
|
126
|
+
* @param lines
|
|
127
|
+
* @param errAngle
|
|
128
|
+
*/
|
|
129
|
+
private mergeSameDirectionLine;
|
|
112
130
|
/** etOpenRound 去除毛刺
|
|
113
131
|
* @description 检查连续的短线段数量,去除合并后产生的毛刺
|
|
114
132
|
*/
|
|
115
133
|
private squareRemoveBurr;
|
|
116
|
-
epsilon: number;
|
|
117
|
-
/**
|
|
118
|
-
* 移除共线点
|
|
119
|
-
* @param path
|
|
120
|
-
* @returns
|
|
121
|
-
*/
|
|
122
|
-
removeCollinearPoints(path: Point[]): Point[];
|
|
123
134
|
/**
|
|
124
135
|
* 线段矫直, 线段中心突刺
|
|
125
136
|
* @description 突变长度小于墙体宽度,该线段可能为突起线段,
|
|
@@ -128,6 +139,7 @@ export declare class Dxf extends Component<{
|
|
|
128
139
|
lineSegmentStraightening(path: Point[]): Point[];
|
|
129
140
|
/**
|
|
130
141
|
* 移除短线段
|
|
142
|
+
* @todo 根据线段两端线段长度,选取参照物_|▔▔
|
|
131
143
|
* @param path
|
|
132
144
|
*/
|
|
133
145
|
removeShortLine(path: Point[], shortLine?: number): Point[];
|
|
@@ -139,6 +151,13 @@ export declare class Dxf extends Component<{
|
|
|
139
151
|
* 将点云结构转换为Float32Array
|
|
140
152
|
*/
|
|
141
153
|
to3DArray(scale: number): Float32Array<ArrayBuffer>;
|
|
154
|
+
/** 获取角度范围
|
|
155
|
+
* @param center
|
|
156
|
+
* @param p1
|
|
157
|
+
* @param p2
|
|
158
|
+
* @returns
|
|
159
|
+
*/
|
|
160
|
+
private getArcAngleRange;
|
|
142
161
|
/**
|
|
143
162
|
* 将点云结构转换为string
|
|
144
163
|
*/
|
|
@@ -64,6 +64,7 @@ export declare class LineAnalysis extends Component {
|
|
|
64
64
|
*/
|
|
65
65
|
buildQuadtree(): void;
|
|
66
66
|
resultList: ProjectionAnalysisResult[];
|
|
67
|
+
mergeWallLines: LineSegment[][];
|
|
67
68
|
/** 线段分析
|
|
68
69
|
* @description 判断两条线段距离是否较短且趋近平行,然后查找两条线段的重合部分的投影线,以此判断两根线是否需要合并
|
|
69
70
|
* @param data
|
|
@@ -76,5 +77,13 @@ export declare class LineAnalysis extends Component {
|
|
|
76
77
|
* @returns
|
|
77
78
|
*/
|
|
78
79
|
private projectionAnalysis;
|
|
80
|
+
doorSearchNearAngle: number;
|
|
81
|
+
doorSearchDistance: number;
|
|
82
|
+
doors: LineSegment[];
|
|
83
|
+
/**
|
|
84
|
+
* 门的位置判断
|
|
85
|
+
*/
|
|
86
|
+
doorsAnalysis(): void;
|
|
87
|
+
findLongLineSegment(line: LineSegment): LineSegment<Record<string, any>>;
|
|
79
88
|
}
|
|
80
89
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Point } from './Point';
|
|
2
|
+
import { Rectangle } from './Rectangle';
|
|
2
3
|
/**
|
|
3
4
|
* 非轴对称线段
|
|
4
5
|
*/
|
|
@@ -9,6 +10,33 @@ export declare class LineSegment<T = Record<string, any>> {
|
|
|
9
10
|
get start(): Point;
|
|
10
11
|
get end(): Point;
|
|
11
12
|
constructor(p1?: Point, p2?: Point);
|
|
13
|
+
/** 膨胀
|
|
14
|
+
* @description 向线段的两个端点分别膨胀 width
|
|
15
|
+
* @param width
|
|
16
|
+
*/
|
|
17
|
+
expansion(width: number, direction?: "start" | "end" | "all"): this;
|
|
18
|
+
/** 向前
|
|
19
|
+
* @description 向前移动 width
|
|
20
|
+
* @param width
|
|
21
|
+
*/
|
|
22
|
+
forward(width: number): this;
|
|
23
|
+
/** 向前
|
|
24
|
+
* @description 向前移动 width
|
|
25
|
+
* @param width
|
|
26
|
+
*/
|
|
27
|
+
backward(width: number): this;
|
|
28
|
+
/**
|
|
29
|
+
* 向指定方向平移
|
|
30
|
+
* @param direct
|
|
31
|
+
* @param size
|
|
32
|
+
*/
|
|
33
|
+
directionMove(direct: Point, size: number): this;
|
|
34
|
+
/** 膨胀为矩形
|
|
35
|
+
*
|
|
36
|
+
* @param width
|
|
37
|
+
* @returns {Rectangle}
|
|
38
|
+
*/
|
|
39
|
+
expandToRectangle(width?: number): Rectangle;
|
|
12
40
|
/**
|
|
13
41
|
* 计算线段的长度
|
|
14
42
|
* @returns 线段的长度
|
|
@@ -19,6 +47,10 @@ export declare class LineSegment<T = Record<string, any>> {
|
|
|
19
47
|
* @returns
|
|
20
48
|
*/
|
|
21
49
|
direction(): Point;
|
|
50
|
+
/**
|
|
51
|
+
* 获取发向量
|
|
52
|
+
*/
|
|
53
|
+
normal(): Point;
|
|
22
54
|
/**
|
|
23
55
|
* 线段长度
|
|
24
56
|
* @returns
|
|
@@ -30,6 +62,12 @@ export declare class LineSegment<T = Record<string, any>> {
|
|
|
30
62
|
* @returns 投影并裁剪后的线段
|
|
31
63
|
*/
|
|
32
64
|
projectLineSegment(line: LineSegment): LineSegment;
|
|
65
|
+
/**
|
|
66
|
+
* 计算一条线段在另一条直线上的投影,并裁剪超出目标线段的部分
|
|
67
|
+
* @param line 要投影的线段
|
|
68
|
+
* @returns 投影并裁剪后的线段
|
|
69
|
+
*/
|
|
70
|
+
projectPoint(p1: Point): Point | null;
|
|
33
71
|
/**
|
|
34
72
|
* 判断线段是与线段相交
|
|
35
73
|
* @param line
|
|
@@ -89,13 +89,20 @@ export declare class Point {
|
|
|
89
89
|
* @returns
|
|
90
90
|
*/
|
|
91
91
|
dot(point: Point): number;
|
|
92
|
+
/** 求两个点叉积
|
|
93
|
+
* @description 如果叉积大于 0,a 到 b 为逆时针方向。
|
|
94
|
+
* @description 如果叉积小于 0,a 到 b 为顺时针方向。
|
|
95
|
+
* @param point
|
|
96
|
+
* @returns
|
|
97
|
+
*/
|
|
98
|
+
cross(point: Point): number;
|
|
92
99
|
/** 计算两个向量夹角
|
|
93
100
|
* @description 公式:a · b = |a| × |b| × cosθ
|
|
94
101
|
* @description 结果为0(0度),两个向量方向一致,结果为3.1415926(180度, PI),两个向量方向相反
|
|
95
102
|
* @param point
|
|
96
103
|
* @returns
|
|
97
104
|
*/
|
|
98
|
-
angleBetween(point: Point): number;
|
|
105
|
+
angleBetween(point: Point, type?: 'radian' | 'cos' | 'angle', angle?: "180" | "360"): number;
|
|
99
106
|
/** 获取向量长度
|
|
100
107
|
*/
|
|
101
108
|
length(): number;
|
|
@@ -50,6 +50,12 @@ export declare class Quadtree<T = any> {
|
|
|
50
50
|
* @returns 相交的节点数组
|
|
51
51
|
*/
|
|
52
52
|
queryRect(rectangle: Rectangle): QuadtreeNode<T>[];
|
|
53
|
+
/**
|
|
54
|
+
* 查询与线段相交的线段节点
|
|
55
|
+
* @param lineSegment 线段
|
|
56
|
+
* @returns 相交的节点数组
|
|
57
|
+
*/
|
|
58
|
+
queryLineSegment(lineSegment: LineSegment): QuadtreeNode<T>[];
|
|
53
59
|
/**
|
|
54
60
|
* 包围盒转换为数组
|
|
55
61
|
* @param array
|