build-dxf 0.1.107 → 0.1.109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "build-dxf",
3
- "version": "0.1.107",
3
+ "version": "0.1.109",
4
4
  "description": "线段构建双线墙壁的dxf版本",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
package/src/build.js CHANGED
@@ -905,6 +905,7 @@ class LineSegment {
905
905
  * @returns
906
906
  */
907
907
  intersectLineSegment(line, endpoint = true) {
908
+ const EPSILON = 1e-10;
908
909
  const p1 = this.start;
909
910
  const p2 = this.end;
910
911
  const p3 = line.start;
@@ -913,7 +914,7 @@ class LineSegment {
913
914
  return (b4.x - a2.x) * (c.y - a2.y) - (b4.y - a2.y) * (c.x - a2.x);
914
915
  }
915
916
  function isPointOnSegment(pt2, segStart, segEnd) {
916
- return Math.min(segStart.x, segEnd.x) - 1e-10 <= pt2.x && pt2.x <= Math.max(segStart.x, segEnd.x) + 1e-10 && Math.min(segStart.y, segEnd.y) - 1e-10 <= pt2.y && pt2.y <= Math.max(segStart.y, segEnd.y) + 1e-10;
917
+ return Math.min(segStart.x, segEnd.x) - EPSILON <= pt2.x && pt2.x <= Math.max(segStart.x, segEnd.x) + EPSILON && Math.min(segStart.y, segEnd.y) - EPSILON <= pt2.y && pt2.y <= Math.max(segStart.y, segEnd.y) + EPSILON;
917
918
  }
918
919
  const d1 = crossProduct(p1, p2, p3);
919
920
  const d2 = crossProduct(p1, p2, p4);
@@ -923,16 +924,16 @@ class LineSegment {
923
924
  return true;
924
925
  }
925
926
  if (endpoint) {
926
- if (Math.abs(d1) < 1e-10 && isPointOnSegment(p3, p1, p2)) {
927
+ if (Math.abs(d1) < EPSILON && isPointOnSegment(p3, p1, p2)) {
927
928
  return true;
928
929
  }
929
- if (Math.abs(d2) < 1e-10 && isPointOnSegment(p4, p1, p2)) {
930
+ if (Math.abs(d2) < EPSILON && isPointOnSegment(p4, p1, p2)) {
930
931
  return true;
931
932
  }
932
- if (Math.abs(d3) < 1e-10 && isPointOnSegment(p1, p3, p4)) {
933
+ if (Math.abs(d3) < EPSILON && isPointOnSegment(p1, p3, p4)) {
933
934
  return true;
934
935
  }
935
- if (Math.abs(d4) < 1e-10 && isPointOnSegment(p2, p3, p4)) {
936
+ if (Math.abs(d4) < EPSILON && isPointOnSegment(p2, p3, p4)) {
936
937
  return true;
937
938
  }
938
939
  }
@@ -2460,7 +2461,11 @@ class WallInsertObject {
2460
2461
  */
2461
2462
  static isDouble(line) {
2462
2463
  if (line.userData.isPassageEntrance && line.userData.passageEntrance?.length) {
2463
- return line.userData.passageEntrance.some((item) => typeof this.getNearId(item) === "number");
2464
+ return line.userData.passageEntrance.some((item) => {
2465
+ const nearId = this.getNearId(item);
2466
+ const id = this.getId(item);
2467
+ return typeof nearId === "number" && typeof id === "number" && id !== nearId;
2468
+ });
2464
2469
  }
2465
2470
  return false;
2466
2471
  }
@@ -3193,6 +3198,23 @@ class LineSegmentUtils {
3193
3198
  }
3194
3199
  return new Point(x / totalWeight, y / totalWeight);
3195
3200
  }
3201
+ /** 自定义分组
3202
+ * @param lines
3203
+ * @returns
3204
+ */
3205
+ static group(lines, callBackFun) {
3206
+ const arrayMap = new ArrayMap();
3207
+ for (let i = 0; i < lines.length; i++) {
3208
+ const line = lines[i];
3209
+ const key = callBackFun(line);
3210
+ arrayMap.append(key, line);
3211
+ }
3212
+ const result = arrayMap.reduce((list, lines2, key) => {
3213
+ list[key] = lines2;
3214
+ return list;
3215
+ }, {});
3216
+ return result;
3217
+ }
3196
3218
  /** 通过在同一路径上且平行的线段分组
3197
3219
  * @param lines
3198
3220
  */
@@ -5991,7 +6013,7 @@ function lineSegmentClipping(lines, minLen = 1e-9, dedup = true) {
5991
6013
  const quadtree = new Quadtree(Box2.fromByLineSegment(...lines));
5992
6014
  lines.forEach((line) => quadtree.insert({ line, userData: void 0 }));
5993
6015
  let newLines = lines.flatMap((line) => {
5994
- const points = quadtree.queryRect(line.toRectangle(0.01, "butt")).map((res) => {
6016
+ const points = quadtree.queryLineSegment(line).map((res) => {
5995
6017
  if (res.line === line) return;
5996
6018
  const point2 = res.line.getIntersection(line);
5997
6019
  if (!point2 || line.start.equal(point2) || line.end.equal(point2)) return;
@@ -9479,7 +9501,7 @@ function initData(lines) {
9479
9501
  continue;
9480
9502
  }
9481
9503
  if (line.userData.isPassageEntrance && line.userData.passageEntrance?.length) {
9482
- const isDoubleDoor = line.userData.passageEntrance.some((item) => typeof WIO.getNearId(item) === "number");
9504
+ const isDoubleDoor = WIO.isDouble(line);
9483
9505
  if (isDoubleDoor) peDoubleDoors.push(line);
9484
9506
  else peDoors.push(line);
9485
9507
  continue;
@@ -9572,6 +9594,10 @@ const holeTypeMap = {
9572
9594
  passageEntrance: "WALL_HOLE",
9573
9595
  bay_window: "BAY_WINDOW"
9574
9596
  };
9597
+ const placeHoldersMap = {
9598
+ switch: "开关",
9599
+ socket: "插座"
9600
+ };
9575
9601
  const WALL_HEIGHT_KEY = /* @__PURE__ */ Symbol("height");
9576
9602
  class ThreeVJiaPipeline extends Pipeline {
9577
9603
  manager;
@@ -9604,17 +9630,10 @@ class ThreeVJiaPipeline extends Pipeline {
9604
9630
  if (lines2.length < 4) return lines2;
9605
9631
  let newLine = null;
9606
9632
  let wallWidth = 0;
9607
- const poly = Polygon.fromByLinePath(lines2).getMinimumBoundingRectangle();
9608
- if (poly) {
9609
- const len1 = poly[0].distance(poly[1]), len2 = poly[1].distance(poly[2]), direct = len1 > len2 ? poly[0].directionFrom(poly[1]) : poly[1].directionFrom(poly[2]), center = poly.getCenter(), len = Math.max(len1, len2);
9610
- newLine = center.expandAsLine(direct, len).translate(-len * 0.5, direct);
9611
- wallWidth = Math.min(len1, len2);
9612
- } else {
9613
- lines2 = [...lines2].sort((a2, b4) => a2.length() - b4.length());
9614
- const line1 = lines2[0], line2 = lines2[1];
9615
- newLine = new LineSegment(line1.center.clone(), line2.center.clone());
9616
- wallWidth = line1.length();
9617
- }
9633
+ lines2 = [...lines2].sort((a2, b4) => a2.length() - b4.length());
9634
+ const line1 = lines2[0], line2 = lines2[1];
9635
+ newLine = new LineSegment(line1.center.clone(), line2.center.clone());
9636
+ wallWidth = line1.length();
9618
9637
  mergeLineUserData(newLine, lines2);
9619
9638
  newLine.userData.wallWidth = wallWidth;
9620
9639
  return [newLine];
@@ -9795,13 +9814,13 @@ class ThreeVJiaPipeline extends Pipeline {
9795
9814
  const rooms = json.rooms, roomPloys = rooms.map((room) => new Polygon(room.polygon.map((p2) => Point.from(p2))));
9796
9815
  if (roomPloys.length === 0) return json;
9797
9816
  publicInfo.itemInfo.forEach((item, i) => {
9798
- if (item.category !== "switch" && item.category !== "socket") return;
9817
+ if (!placeHoldersMap[item.category]) return;
9799
9818
  const itemPoly = new Polygon(item.contour.map((p2) => Point.from(p2).rotate(json.center, json.angle))), center = itemPoly.getCenter(), index2 = roomPloys.findIndex((poly) => poly.pointWithin(center));
9800
9819
  if (index2 < 0) return;
9801
9820
  json.placeHolders.push({
9802
- name: item.category,
9803
- polygon: item.contour,
9804
- direction: item.direction,
9821
+ name: placeHoldersMap[item.category],
9822
+ polygon: itemPoly.getMinimumBoundingRectangle().map((p2) => p2.toJson2D()),
9823
+ direction: Point.from(item.direction).rotate(Point.zero(), json.angle),
9805
9824
  height: item.box.max.z - item.box.min.z,
9806
9825
  sillHeight: item.box.min.z - z,
9807
9826
  roomId: rooms[index2].roomTypeId,
@@ -10568,6 +10587,10 @@ class DxfDataPlugin extends Pipeline {
10568
10587
  super();
10569
10588
  this.lines = lines;
10570
10589
  this.manager = WallGroupManager.fromByLines(lines);
10590
+ this.init();
10591
+ }
10592
+ init() {
10593
+ this.manager.forEach((group) => this.run(group.type, { lines: group.lines }));
10571
10594
  }
10572
10595
  install(cad) {
10573
10596
  const wallGroupManager = this.manager, lines = this.lines;
@@ -10610,168 +10633,6 @@ class DxfDataPlugin extends Pipeline {
10610
10633
  cad.addGroup(bayWindowLines, "bayWindow");
10611
10634
  }
10612
10635
  }
10613
- class DoubleWallHelper {
10614
- static errorAngle = 4;
10615
- /** 线段投影分析
10616
- * @param index
10617
- * @param sourceLineSegment
10618
- * @param lineSegmentList
10619
- * @returns
10620
- */
10621
- static projectionAnalysis(index2, sourceIndex, sourceLineSegment, lineSegmentList) {
10622
- const temLineSegment = lineSegmentList[index2], direct = sourceLineSegment.direction(), temDirect = temLineSegment.direction(), angle = direct.angle(temDirect, { unit: "degree", range: "180" });
10623
- if (angle < this.errorAngle || angle > 180 - this.errorAngle) {
10624
- let data;
10625
- let dist = sourceLineSegment.distanceToSegment(temLineSegment);
10626
- const p1 = temLineSegment.projectLineSegment(sourceLineSegment), p2 = sourceLineSegment.projectLineSegment(temLineSegment);
10627
- if (p1.length() > p2.length()) {
10628
- data = {
10629
- target: temLineSegment,
10630
- targetIndex: index2,
10631
- source: sourceLineSegment,
10632
- sourceIndex,
10633
- project: p1,
10634
- project2: p2,
10635
- minDistance: dist
10636
- };
10637
- } else {
10638
- data = {
10639
- target: sourceLineSegment,
10640
- targetIndex: sourceIndex,
10641
- source: temLineSegment,
10642
- sourceIndex: index2,
10643
- project: p2,
10644
- project2: p1,
10645
- minDistance: dist
10646
- };
10647
- }
10648
- if (!data || data.project.length() < 0.08 || data.project2.length() < 0.08) return;
10649
- return data;
10650
- }
10651
- }
10652
- /** 查找
10653
- * @param lines
10654
- * @param wallWidth
10655
- * @returns
10656
- */
10657
- static findDoubleLine(lines, wallWidth = 0.4) {
10658
- let walls = lines.filter((line) => !line.userData.isDoor), visited = /* @__PURE__ */ new Set(), resultList = [];
10659
- walls = walls.filter((line) => !line.userData.isWindowWall);
10660
- let quadtree = new Quadtree(Box2.fromByLineSegment(...walls));
10661
- walls.forEach((line, index2) => quadtree.insert({ line, userData: index2 }));
10662
- walls.forEach((sourceLineSegment, i) => {
10663
- const rectangle = Rectangle.fromByLineSegment(sourceLineSegment, wallWidth * 2, false, -0.01), ids = quadtree.queryRect(rectangle).map((i2) => i2.userData).filter((index2) => index2 !== i);
10664
- ids.forEach((id) => {
10665
- try {
10666
- if (visited.has(`${i}-${id}`) || visited.has(`${id}-${i}`)) return;
10667
- const res = this.projectionAnalysis(id, i, sourceLineSegment, walls);
10668
- if (res) resultList.push(res);
10669
- visited.add(`${i}-${id}`);
10670
- } catch (error) {
10671
- console.log(error);
10672
- }
10673
- });
10674
- });
10675
- resultList.forEach((result) => {
10676
- const project0 = result.project, project1 = result.project2;
10677
- if (project0.angle(project1, { unit: "degree", range: "180" }) > 135) {
10678
- project1.points = [project1.points[1], project1.points[0]];
10679
- }
10680
- });
10681
- return {
10682
- resultList,
10683
- walls,
10684
- quadtree
10685
- };
10686
- }
10687
- /** 补双线墙壁
10688
- * @param lines
10689
- * @param wallWidth
10690
- * @returns
10691
- */
10692
- static complementSide(lines, obstacleAegment = [], wallWidth = 0.4) {
10693
- const otherLines = [];
10694
- lines = lines.filter((line) => {
10695
- if (line.userData.isBayWindow || line.userData.isBalconyRailing || LineGroupType.hasType(line, "bayWindow") && line.userData.isWindow) {
10696
- otherLines.push(line);
10697
- return false;
10698
- }
10699
- return true;
10700
- });
10701
- let { resultList, quadtree } = this.findDoubleLine(lines, wallWidth), removeLines = /* @__PURE__ */ new Set(), clipingMap = /* @__PURE__ */ new Map(), appendLines = [];
10702
- function addClipingMap(line, line1) {
10703
- if (!clipingMap.has(line)) clipingMap.set(line, []);
10704
- const startProj = line.projectPoint(line1.start);
10705
- const endProj = line.projectPoint(line1.end);
10706
- const list = clipingMap.get(line);
10707
- if (startProj) list.push(startProj);
10708
- if (endProj) list.push(endProj);
10709
- removeLines.add(line);
10710
- quadtree.remove(line);
10711
- }
10712
- resultList.forEach((result) => {
10713
- const project0 = result.project, project1 = result.project2, line0 = result.source, line1 = result.target;
10714
- const newLine1 = new LineSegment(project0.start.clone(), project1.start.clone());
10715
- const newLine2 = new LineSegment(project0.end.clone(), project1.end.clone());
10716
- newLine1.userData.height = line0.userData.height;
10717
- newLine1.userData.rooftopPz = line0.userData.rooftopPz;
10718
- newLine2.userData.height = line1.userData.height;
10719
- newLine2.userData.rooftopPz = line1.userData.rooftopPz;
10720
- appendLines.push(newLine1, newLine2);
10721
- addClipingMap(line0, project0);
10722
- addClipingMap(line1, project1);
10723
- });
10724
- clipingMap.forEach((list, line) => {
10725
- const newLines = LineSegmentUtils.clippingByPoints(line, list);
10726
- newLines.forEach((newLine) => {
10727
- newLine.userData.height = line.userData.height;
10728
- newLine.userData.rooftopPz = line.userData.rooftopPz;
10729
- });
10730
- lines.push(...newLines);
10731
- });
10732
- lines = lines.filter((line) => !removeLines.has(line));
10733
- quadtree.clear();
10734
- quadtree = createQuadtree([...lines, ...otherLines, ...obstacleAegment]);
10735
- appendLines = lineSegmentClipping(appendLines, 1e-9);
10736
- appendLines.flatMap((line) => {
10737
- const queryLines = quadtree.queryRect(line.toRectangle(1e-3, "butt")).filter((item) => item.line.isParallelTo(line)).map((item) => item.line);
10738
- if (queryLines.length) {
10739
- const newLines = LineSegmentUtils.clippingByLines(line, queryLines);
10740
- if (newLines) return newLines.forEach((newLine) => {
10741
- newLine.userData.height = line.userData.height;
10742
- newLine.userData.rooftopPz = line.userData.rooftopPz;
10743
- quadtree.insert(newLine);
10744
- lines.push(newLine);
10745
- });
10746
- }
10747
- quadtree.insert(line);
10748
- lines.push(line);
10749
- });
10750
- lines = lineSegmentClipping(lines, 1e-9);
10751
- quadtree.clear();
10752
- lines.push(...otherLines);
10753
- PointUtils.adsorb(lines.flatMap((line) => line.points), 1e-4);
10754
- return lines.filter((line) => line.length() > 1e-9);
10755
- }
10756
- /**
10757
- * 创建分组
10758
- */
10759
- static buildGroup(lineSegments) {
10760
- const otherLines = [];
10761
- lineSegments = lineSegments.filter((line) => {
10762
- if (line.userData.isDoor || line.userData.isBalconyRailing || line.userData.isBayWindowWin || line.userData.isBayWindow) {
10763
- otherLines.push(line);
10764
- return false;
10765
- }
10766
- return true;
10767
- });
10768
- lineSegments = BuildGroup.doubleWall(lineSegments, true);
10769
- lineSegments = this.complementSide(lineSegments, otherLines);
10770
- WIO.recomputed(lineSegments);
10771
- lineSegments = BuildGroup.doubleWall(lineSegments, true);
10772
- return [...lineSegments, ...otherLines];
10773
- }
10774
- }
10775
10636
  function parallel(line, baseline) {
10776
10637
  const currentAngle = Math.atan2(line.end.y - line.start.y, line.end.x - line.start.x);
10777
10638
  const targetAngle = Math.atan2(baseline.end.y - baseline.start.y, baseline.end.x - baseline.start.x);
@@ -11159,6 +11020,168 @@ class AlignToParallelSegments {
11159
11020
  ];
11160
11021
  }
11161
11022
  }
11023
+ class DoubleWallHelper {
11024
+ static errorAngle = 4;
11025
+ /** 线段投影分析
11026
+ * @param index
11027
+ * @param sourceLineSegment
11028
+ * @param lineSegmentList
11029
+ * @returns
11030
+ */
11031
+ static projectionAnalysis(index2, sourceIndex, sourceLineSegment, lineSegmentList) {
11032
+ const temLineSegment = lineSegmentList[index2], direct = sourceLineSegment.direction(), temDirect = temLineSegment.direction(), angle = direct.angle(temDirect, { unit: "degree", range: "180" });
11033
+ if (angle < this.errorAngle || angle > 180 - this.errorAngle) {
11034
+ let data;
11035
+ let dist = sourceLineSegment.distanceToSegment(temLineSegment);
11036
+ const p1 = temLineSegment.projectLineSegment(sourceLineSegment), p2 = sourceLineSegment.projectLineSegment(temLineSegment);
11037
+ if (p1.length() > p2.length()) {
11038
+ data = {
11039
+ target: temLineSegment,
11040
+ targetIndex: index2,
11041
+ source: sourceLineSegment,
11042
+ sourceIndex,
11043
+ project: p1,
11044
+ project2: p2,
11045
+ minDistance: dist
11046
+ };
11047
+ } else {
11048
+ data = {
11049
+ target: sourceLineSegment,
11050
+ targetIndex: sourceIndex,
11051
+ source: temLineSegment,
11052
+ sourceIndex: index2,
11053
+ project: p2,
11054
+ project2: p1,
11055
+ minDistance: dist
11056
+ };
11057
+ }
11058
+ if (!data || data.project.length() < 0.08 || data.project2.length() < 0.08) return;
11059
+ return data;
11060
+ }
11061
+ }
11062
+ /** 查找
11063
+ * @param lines
11064
+ * @param wallWidth
11065
+ * @returns
11066
+ */
11067
+ static findDoubleLine(lines, wallWidth = 0.4) {
11068
+ let walls = lines.filter((line) => !line.userData.isDoor), visited = /* @__PURE__ */ new Set(), resultList = [];
11069
+ walls = walls.filter((line) => !line.userData.isWindowWall);
11070
+ let quadtree = new Quadtree(Box2.fromByLineSegment(...walls));
11071
+ walls.forEach((line, index2) => quadtree.insert({ line, userData: index2 }));
11072
+ walls.forEach((sourceLineSegment, i) => {
11073
+ const rectangle = Rectangle.fromByLineSegment(sourceLineSegment, wallWidth * 2, false, -0.01), ids = quadtree.queryRect(rectangle).map((i2) => i2.userData).filter((index2) => index2 !== i);
11074
+ ids.forEach((id) => {
11075
+ try {
11076
+ if (visited.has(`${i}-${id}`) || visited.has(`${id}-${i}`)) return;
11077
+ const res = this.projectionAnalysis(id, i, sourceLineSegment, walls);
11078
+ if (res) resultList.push(res);
11079
+ visited.add(`${i}-${id}`);
11080
+ } catch (error) {
11081
+ console.log(error);
11082
+ }
11083
+ });
11084
+ });
11085
+ resultList.forEach((result) => {
11086
+ const project0 = result.project, project1 = result.project2;
11087
+ if (project0.angle(project1, { unit: "degree", range: "180" }) > 135) {
11088
+ project1.points = [project1.points[1], project1.points[0]];
11089
+ }
11090
+ });
11091
+ return {
11092
+ resultList,
11093
+ walls,
11094
+ quadtree
11095
+ };
11096
+ }
11097
+ /** 补双线墙壁
11098
+ * @param lines
11099
+ * @param wallWidth
11100
+ * @returns
11101
+ */
11102
+ static complementSide(lines, obstacleAegment = [], wallWidth = 0.4) {
11103
+ const otherLines = [];
11104
+ lines = lines.filter((line) => {
11105
+ if (line.userData.isBayWindow || line.userData.isBalconyRailing || LineGroupType.hasType(line, "bayWindow") && line.userData.isWindow) {
11106
+ otherLines.push(line);
11107
+ return false;
11108
+ }
11109
+ return true;
11110
+ });
11111
+ let { resultList, quadtree } = this.findDoubleLine(lines, wallWidth), removeLines = /* @__PURE__ */ new Set(), clipingMap = /* @__PURE__ */ new Map(), appendLines = [];
11112
+ function addClipingMap(line, line1) {
11113
+ if (!clipingMap.has(line)) clipingMap.set(line, []);
11114
+ const startProj = line.projectPoint(line1.start);
11115
+ const endProj = line.projectPoint(line1.end);
11116
+ const list = clipingMap.get(line);
11117
+ if (startProj) list.push(startProj);
11118
+ if (endProj) list.push(endProj);
11119
+ removeLines.add(line);
11120
+ quadtree.remove(line);
11121
+ }
11122
+ resultList.forEach((result) => {
11123
+ const project0 = result.project, project1 = result.project2, line0 = result.source, line1 = result.target;
11124
+ const newLine1 = new LineSegment(project0.start.clone(), project1.start.clone());
11125
+ const newLine2 = new LineSegment(project0.end.clone(), project1.end.clone());
11126
+ newLine1.userData.height = line0.userData.height;
11127
+ newLine1.userData.rooftopPz = line0.userData.rooftopPz;
11128
+ newLine2.userData.height = line1.userData.height;
11129
+ newLine2.userData.rooftopPz = line1.userData.rooftopPz;
11130
+ appendLines.push(newLine1, newLine2);
11131
+ addClipingMap(line0, project0);
11132
+ addClipingMap(line1, project1);
11133
+ });
11134
+ clipingMap.forEach((list, line) => {
11135
+ const newLines = LineSegmentUtils.clippingByPoints(line, list);
11136
+ newLines.forEach((newLine) => {
11137
+ newLine.userData.height = line.userData.height;
11138
+ newLine.userData.rooftopPz = line.userData.rooftopPz;
11139
+ });
11140
+ lines.push(...newLines);
11141
+ });
11142
+ lines = lines.filter((line) => !removeLines.has(line));
11143
+ quadtree.clear();
11144
+ quadtree = createQuadtree([...lines, ...otherLines, ...obstacleAegment]);
11145
+ appendLines = lineSegmentClipping(appendLines, 1e-9);
11146
+ appendLines.flatMap((line) => {
11147
+ const queryLines = quadtree.queryRect(line.toRectangle(1e-3, "butt")).filter((item) => item.line.isParallelTo(line)).map((item) => item.line);
11148
+ if (queryLines.length) {
11149
+ const newLines = LineSegmentUtils.clippingByLines(line, queryLines);
11150
+ if (newLines) return newLines.forEach((newLine) => {
11151
+ newLine.userData.height = line.userData.height;
11152
+ newLine.userData.rooftopPz = line.userData.rooftopPz;
11153
+ quadtree.insert(newLine);
11154
+ lines.push(newLine);
11155
+ });
11156
+ }
11157
+ quadtree.insert(line);
11158
+ lines.push(line);
11159
+ });
11160
+ lines = lineSegmentClipping(lines, 1e-9);
11161
+ quadtree.clear();
11162
+ lines.push(...otherLines);
11163
+ PointUtils.adsorb(lines.flatMap((line) => line.points), 1e-4);
11164
+ return lines.filter((line) => line.length() > 1e-9);
11165
+ }
11166
+ /**
11167
+ * 创建分组
11168
+ */
11169
+ static buildGroup(lineSegments) {
11170
+ const otherLines = [];
11171
+ lineSegments = lineSegments.filter((line) => {
11172
+ if (line.userData.isDoor || line.userData.isBalconyRailing || line.userData.isBayWindowWin || line.userData.isBayWindow) {
11173
+ otherLines.push(line);
11174
+ return false;
11175
+ }
11176
+ return true;
11177
+ });
11178
+ lineSegments = BuildGroup.doubleWall(lineSegments, true);
11179
+ lineSegments = this.complementSide(lineSegments, otherLines);
11180
+ WIO.recomputed(lineSegments);
11181
+ lineSegments = BuildGroup.doubleWall(lineSegments, true);
11182
+ return [...lineSegments, ...otherLines];
11183
+ }
11184
+ }
11162
11185
  function shortDistanceLink(lines, radius = 0.1) {
11163
11186
  const dpSet = findDiscretePoint(lines.filter((line) => !line.userData.isDoor)), pointVirtualGrid = createPointSpatialHash(dpSet.map((v2) => v2)), appendLines = [], visited = /* @__PURE__ */ new Set();
11164
11187
  const getWeight2 = (target, point2, line) => {
@@ -11303,8 +11326,10 @@ function correction(lines, targettLine, option) {
11303
11326
  lines = lineSegmentClipping(lines, 1e-9);
11304
11327
  lines = removeDangline(lines, 0.1, false);
11305
11328
  lines = removeShortDoor(lines);
11306
- let newLines = lines.filter((line) => !line.userData.isDoor && !line.userData.isBalconyRailing);
11307
- let otherLines = lines.filter((line) => line.userData.isDoor || line.userData.isBalconyRailing);
11329
+ let { otherLines = [], newLines = [] } = LineSegmentUtils.group(lines, (line) => {
11330
+ if (line.userData.isDoor || line.userData.isBalconyRailing) return "otherLines";
11331
+ return "newLines";
11332
+ });
11308
11333
  newLines = BuildGroup.bayWindow(newLines, false);
11309
11334
  const { wallGroup = true } = option ?? {};
11310
11335
  if (wallGroup) {
@@ -11335,7 +11360,22 @@ function axisAlignCorr(lines, option, verticalReferenceLine) {
11335
11360
  }
11336
11361
  return lines;
11337
11362
  }
11363
+ function sparse(trajectory2, size2 = 0.4) {
11364
+ const idSet = /* @__PURE__ */ new Set();
11365
+ Object.keys(trajectory2 ?? {}).forEach((key) => {
11366
+ const p2 = trajectory2[key], i = Math.round(p2.x / size2), j = Math.round(p2.y / size2), id = BigInt(i) << 32n | BigInt(j) & 0xffffffffn;
11367
+ if (idSet.has(id)) {
11368
+ delete trajectory2[key];
11369
+ return;
11370
+ }
11371
+ idSet.add(id);
11372
+ });
11373
+ }
11338
11374
  function init(lines, option) {
11375
+ if (option.trajectory) {
11376
+ sparse(option.trajectory, 0.2);
11377
+ sparse(option.trajectory, 0.5);
11378
+ }
11339
11379
  BuildGroup.doubleWall.setTrajectory(option.trajectory);
11340
11380
  lines = lines.map((line) => line.clone());
11341
11381
  return lines;
@@ -11652,6 +11692,8 @@ class BoundExt {
11652
11692
  let appendLines = [];
11653
11693
  exteriorLines.forEach((line) => {
11654
11694
  const mode = line.userData.expandDirect, direction = mode === "left" ? line.getRightDirection() : line.getLeftDirection();
11695
+ const intersectCount = quadtree.raycaster(line.center, direction, 1e-3, wallWidth).length;
11696
+ if (intersectCount > 0) return;
11655
11697
  line.points.forEach((point2) => {
11656
11698
  const id = point2.hashCode();
11657
11699
  arrayMap.append(id, { point: point2, line });
@@ -30,7 +30,7 @@ export declare class Dxf<TEventMap extends {} = {}> extends Component<{
30
30
  doorLineSegment: LineSegment[];
31
31
  verticalReferenceLine?: LineSegment<LineUserData>;
32
32
  originalZAverage: number;
33
- linePipeline: LinePipeline;
33
+ linePipeline: LinePipeline<SetDataOption>;
34
34
  /** 原始数据组
35
35
  */
36
36
  get lines(): LineSegment<LineUserData>[];
@@ -1,4 +1,4 @@
1
- import { DxfSystem } from '../../../../build';
1
+ import { DxfSystem } from '../../../../DxfSystem';
2
2
  type __VLS_Props = {
3
3
  dxfSystem: DxfSystem;
4
4
  permission?: "admin";
@@ -0,0 +1 @@
1
+ export {};
@@ -152,17 +152,8 @@ export declare class DxfDrawPlugin implements ICADPlugin {
152
152
  private getArcAngleRange;
153
153
  install(cad: CAD): void;
154
154
  }
155
- type DrawDataOption = {
156
- openPaths: LineSegment[][];
157
- closedPath: LineSegment[][];
158
- hole: {
159
- line: LineSegment;
160
- belong: number;
161
- }[];
162
- };
163
155
  type DataOption = {
164
156
  lines: LineSegment[];
165
- drawData: DrawDataOption;
166
157
  };
167
158
  /** dxf 数据处理插件
168
159
  */
@@ -170,6 +161,7 @@ export declare class DxfDataPlugin extends Pipeline<DataOption> implements ICADP
170
161
  manager: WallGroupManager;
171
162
  lines: LineSegment<LineUserData>[];
172
163
  constructor(lines: LineSegment<LineUserData>[]);
164
+ init(): void;
173
165
  install(cad: CAD): void;
174
166
  }
175
167
  export {};
@@ -1,5 +1,5 @@
1
1
  export * from './lineSegmentClipping';
2
- export * from './DoubleWallHelper';
2
+ export * from './linePipeline/builtin/DoubleWallHelper';
3
3
  export * from './clippingDoubleWall';
4
4
  export * from './clippingLineUserData';
5
5
  export * from './findDiscrete';
@@ -1,6 +1,6 @@
1
- import { LineSegment } from '../../utils/algorithms/LineSegment';
2
- import { Quadtree } from '../../utils/algorithms/Quadtree';
3
- import { LineUserData } from '../type';
1
+ import { LineSegment } from '../../../../utils/algorithms/LineSegment';
2
+ import { Quadtree } from '../../../../utils/algorithms/Quadtree';
3
+ import { LineUserData } from '../../../type';
4
4
  type ProjectionAnalysisResult = {
5
5
  source: LineSegment;
6
6
  sourceIndex: number;
@@ -1,7 +1,7 @@
1
1
  import { LineSegment } from '../../../utils/algorithms/LineSegment';
2
2
  import { Pipeline } from '../../../utils/algorithms/Pipeline';
3
3
  import { LineUserData, SetDataOption } from '../../type';
4
- export declare class LinePipeline extends Pipeline<LineSegment[], SetDataOption> {
4
+ export declare class LinePipeline<T = SetDataOption> extends Pipeline<LineSegment[], T> {
5
5
  static get builtin(): {
6
6
  init: typeof import('./builtin/init').init;
7
7
  DoorToHole: typeof import('./builtin/doorToHole').doorToHole;
@@ -14,7 +14,7 @@ export declare class LinePipeline extends Pipeline<LineSegment[], SetDataOption>
14
14
  RemoveShortDoubleWall: typeof import('./builtin/removeShortDoubleWall').removeShortDoubleWall;
15
15
  };
16
16
  constructor();
17
- add(handle: (lines: LineSegment<LineUserData>[], option: SetDataOption) => LineSegment<LineUserData>[]): this;
17
+ add(handle: (lines: LineSegment<LineUserData>[], option: T) => LineSegment<LineUserData>[]): this;
18
18
  clear(): this;
19
- execute(data: LineSegment<Record<string, any>>[], option: SetDataOption): LineSegment<Record<string, any>>[];
19
+ execute(data: LineSegment<Record<string, any>>[], option: T): LineSegment<Record<string, any>>[];
20
20
  }
package/src/index.css CHANGED
@@ -1236,10 +1236,10 @@ button[data-v-624dc8f8]:active {
1236
1236
  color: #a7a7a7
1237
1237
  }
1238
1238
 
1239
- [data-v-978973e0] {
1239
+ [data-v-e23c82bd] {
1240
1240
  font-family: 宋体;
1241
1241
  }
1242
- .button[data-v-978973e0] {
1242
+ .button[data-v-e23c82bd] {
1243
1243
  padding: 5px 10px;
1244
1244
  border: none;
1245
1245
  background: var(--primary-color);
package/src/index3.js CHANGED
@@ -16663,6 +16663,22 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
16663
16663
  line1.angle(line2, { unit: "degree", range: "180" })
16664
16664
  );
16665
16665
  }
16666
+ function intersectionTest() {
16667
+ if (defaultComponent.selectLines.length !== 2) {
16668
+ ElMessage.warning({ message: "请选择两条线段" });
16669
+ return;
16670
+ }
16671
+ const line1 = defaultComponent.selectLines[0];
16672
+ const line2 = defaultComponent.selectLines[1];
16673
+ const point = line1.getIntersection(line2);
16674
+ if (point) {
16675
+ drawPoint(point);
16676
+ } else {
16677
+ ElMessage.warning({
16678
+ message: "没有交点"
16679
+ });
16680
+ }
16681
+ }
16666
16682
  function commandConfirm() {
16667
16683
  variable.set("currentKeyDown", "enter");
16668
16684
  queueMicrotask(() => variable.set("currentKeyUp", "enter"));
@@ -16919,7 +16935,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
16919
16935
  title: "取消命令(Esc)",
16920
16936
  class: "active:scale-[0.7] transition-all flex items-center justify-center",
16921
16937
  onClick: _cache[0] || (_cache[0] = (e) => (unref(editor).cancelCommand(), e.stopPropagation()))
16922
- }, [..._cache[21] || (_cache[21] = [
16938
+ }, [..._cache[22] || (_cache[22] = [
16923
16939
  createElementVNode("svg", {
16924
16940
  fill: "#fff",
16925
16941
  width: "16",
@@ -16937,7 +16953,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
16937
16953
  onClick: commandConfirm,
16938
16954
  title: "确认命令(Enter)",
16939
16955
  class: "active:scale-[0.7] transition-all flex items-center justify-center"
16940
- }, [..._cache[22] || (_cache[22] = [
16956
+ }, [..._cache[23] || (_cache[23] = [
16941
16957
  createElementVNode("svg", {
16942
16958
  fill: "#fff",
16943
16959
  width: "16",
@@ -16960,7 +16976,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
16960
16976
  modelValue: dxfVisible.value,
16961
16977
  "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => dxfVisible.value = $event)
16962
16978
  }, {
16963
- default: withCtx(() => [..._cache[23] || (_cache[23] = [
16979
+ default: withCtx(() => [..._cache[24] || (_cache[24] = [
16964
16980
  createTextVNode("Dxf", -1)
16965
16981
  ])]),
16966
16982
  _: 1
@@ -16970,7 +16986,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
16970
16986
  modelValue: unref(editor).renderManager.lineAdsorption.value,
16971
16987
  "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => unref(editor).renderManager.lineAdsorption.value = $event)
16972
16988
  }, {
16973
- default: withCtx(() => [..._cache[24] || (_cache[24] = [
16989
+ default: withCtx(() => [..._cache[25] || (_cache[25] = [
16974
16990
  createTextVNode("线吸附", -1)
16975
16991
  ])]),
16976
16992
  _: 1
@@ -16981,7 +16997,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
16981
16997
  modelValue: unref(editor).renderManager.pointAdsorption.value,
16982
16998
  "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => unref(editor).renderManager.pointAdsorption.value = $event)
16983
16999
  }, {
16984
- default: withCtx(() => [..._cache[25] || (_cache[25] = [
17000
+ default: withCtx(() => [..._cache[26] || (_cache[26] = [
16985
17001
  createTextVNode("点吸附", -1)
16986
17002
  ])]),
16987
17003
  _: 1
@@ -17027,7 +17043,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17027
17043
  createVNode(unref(ElDropdownMenu), null, {
17028
17044
  default: withCtx(() => [
17029
17045
  createVNode(unref(ElDropdownItem), { onClick: selectLocalFile }, {
17030
- default: withCtx(() => [..._cache[27] || (_cache[27] = [
17046
+ default: withCtx(() => [..._cache[28] || (_cache[28] = [
17031
17047
  createTextVNode(" 选择文件 ", -1)
17032
17048
  ])]),
17033
17049
  _: 1
@@ -17035,7 +17051,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17035
17051
  createVNode(unref(ElDropdownItem), {
17036
17052
  onClick: _cache[4] || (_cache[4] = ($event) => unref(dxfSystem).CorrectionDxf.downloadOriginalData("json.json"))
17037
17053
  }, {
17038
- default: withCtx(() => [..._cache[28] || (_cache[28] = [
17054
+ default: withCtx(() => [..._cache[29] || (_cache[29] = [
17039
17055
  createTextVNode(" 下载Json ", -1)
17040
17056
  ])]),
17041
17057
  _: 1
@@ -17043,7 +17059,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17043
17059
  createVNode(unref(ElDropdownItem), {
17044
17060
  onClick: _cache[5] || (_cache[5] = ($event) => unref(dxfSystem).CorrectionDxf.downloadDxf("test.dxf"))
17045
17061
  }, {
17046
- default: withCtx(() => [..._cache[29] || (_cache[29] = [
17062
+ default: withCtx(() => [..._cache[30] || (_cache[30] = [
17047
17063
  createTextVNode(" 下载DXF ", -1)
17048
17064
  ])]),
17049
17065
  _: 1
@@ -17051,7 +17067,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17051
17067
  createVNode(unref(ElDropdownItem), {
17052
17068
  onClick: _cache[6] || (_cache[6] = ($event) => unref(dxfSystem).CorrectionDxf.downloadDxfImage("dxf.jpg"))
17053
17069
  }, {
17054
- default: withCtx(() => [..._cache[30] || (_cache[30] = [
17070
+ default: withCtx(() => [..._cache[31] || (_cache[31] = [
17055
17071
  createTextVNode(" 下载JPG ", -1)
17056
17072
  ])]),
17057
17073
  _: 1
@@ -17059,7 +17075,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17059
17075
  createVNode(unref(ElDropdownItem), {
17060
17076
  onClick: _cache[7] || (_cache[7] = ($event) => unref(whiteModel).downloadGltf("test.glb", true))
17061
17077
  }, {
17062
- default: withCtx(() => [..._cache[31] || (_cache[31] = [
17078
+ default: withCtx(() => [..._cache[32] || (_cache[32] = [
17063
17079
  createTextVNode(" 下载白膜 ", -1)
17064
17080
  ])]),
17065
17081
  _: 1
@@ -17067,7 +17083,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17067
17083
  createVNode(unref(ElDropdownItem), {
17068
17084
  onClick: _cache[8] || (_cache[8] = ($event) => unref(threeVJia).download())
17069
17085
  }, {
17070
- default: withCtx(() => [..._cache[32] || (_cache[32] = [
17086
+ default: withCtx(() => [..._cache[33] || (_cache[33] = [
17071
17087
  createTextVNode(" 下载三维家JSON ", -1)
17072
17088
  ])]),
17073
17089
  _: 1
@@ -17089,7 +17105,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17089
17105
  }, null, 8, ["modelValue"])
17090
17106
  ]),
17091
17107
  default: withCtx(() => [
17092
- _cache[33] || (_cache[33] = createElementVNode("div", { class: "w-full" }, "z值调整", -1))
17108
+ _cache[34] || (_cache[34] = createElementVNode("div", { class: "w-full" }, "z值调整", -1))
17093
17109
  ]),
17094
17110
  _: 1
17095
17111
  })) : createCommentVNode("", true)
@@ -17128,6 +17144,17 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17128
17144
  }, "角度测试", 32)
17129
17145
  ]),
17130
17146
  _: 1
17147
+ }),
17148
+ createVNode(unref(ElDropdownItem), null, {
17149
+ default: withCtx(() => [
17150
+ createElementVNode("div", {
17151
+ class: "w-full",
17152
+ onMousedown: _cache[14] || (_cache[14] = withModifiers(() => {
17153
+ }, ["stop"])),
17154
+ onClick: withModifiers(intersectionTest, ["stop", "prevent"])
17155
+ }, "交点测试", 32)
17156
+ ]),
17157
+ _: 1
17131
17158
  })
17132
17159
  ]),
17133
17160
  _: 1
@@ -17138,7 +17165,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17138
17165
  size: "small",
17139
17166
  class: "mt-[10px] w-full"
17140
17167
  }, {
17141
- default: withCtx(() => [..._cache[26] || (_cache[26] = [
17168
+ default: withCtx(() => [..._cache[27] || (_cache[27] = [
17142
17169
  createTextVNode("其他功能", -1)
17143
17170
  ])]),
17144
17171
  _: 1
@@ -17149,16 +17176,16 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17149
17176
  ])) : (openBlock(), createElementBlock("div", {
17150
17177
  key: 1,
17151
17178
  class: normalizeClass([{ "translate-y-full": isMainCommand.value }, "z-8 absolute left-0 bottom-0 transition-transform w-full bg-white p-[5px] box-border text-[14px]"]),
17152
- onMousedown: _cache[17] || (_cache[17] = (e) => e.stopPropagation()),
17153
- onPointerdown: _cache[18] || (_cache[18] = (e) => e.stopPropagation())
17179
+ onMousedown: _cache[18] || (_cache[18] = (e) => e.stopPropagation()),
17180
+ onPointerdown: _cache[19] || (_cache[19] = (e) => e.stopPropagation())
17154
17181
  }, [
17155
17182
  createElementVNode("div", _hoisted_14, [
17156
17183
  createVNode(unref(ElCheckbox), {
17157
17184
  size: "small",
17158
17185
  modelValue: dxfVisible.value,
17159
- "onUpdate:modelValue": _cache[14] || (_cache[14] = ($event) => dxfVisible.value = $event)
17186
+ "onUpdate:modelValue": _cache[15] || (_cache[15] = ($event) => dxfVisible.value = $event)
17160
17187
  }, {
17161
- default: withCtx(() => [..._cache[34] || (_cache[34] = [
17188
+ default: withCtx(() => [..._cache[35] || (_cache[35] = [
17162
17189
  createTextVNode("Dxf", -1)
17163
17190
  ])]),
17164
17191
  _: 1
@@ -17166,9 +17193,9 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17166
17193
  createVNode(unref(ElCheckbox), {
17167
17194
  size: "small",
17168
17195
  modelValue: unref(editor).renderManager.lineAdsorption.value,
17169
- "onUpdate:modelValue": _cache[15] || (_cache[15] = ($event) => unref(editor).renderManager.lineAdsorption.value = $event)
17196
+ "onUpdate:modelValue": _cache[16] || (_cache[16] = ($event) => unref(editor).renderManager.lineAdsorption.value = $event)
17170
17197
  }, {
17171
- default: withCtx(() => [..._cache[35] || (_cache[35] = [
17198
+ default: withCtx(() => [..._cache[36] || (_cache[36] = [
17172
17199
  createTextVNode("线吸附", -1)
17173
17200
  ])]),
17174
17201
  _: 1
@@ -17177,9 +17204,9 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17177
17204
  disabled: !unref(editor).renderManager.lineAdsorption.value,
17178
17205
  size: "small",
17179
17206
  modelValue: unref(editor).renderManager.pointAdsorption.value,
17180
- "onUpdate:modelValue": _cache[16] || (_cache[16] = ($event) => unref(editor).renderManager.pointAdsorption.value = $event)
17207
+ "onUpdate:modelValue": _cache[17] || (_cache[17] = ($event) => unref(editor).renderManager.pointAdsorption.value = $event)
17181
17208
  }, {
17182
- default: withCtx(() => [..._cache[36] || (_cache[36] = [
17209
+ default: withCtx(() => [..._cache[37] || (_cache[37] = [
17183
17210
  createTextVNode("点吸附", -1)
17184
17211
  ])]),
17185
17212
  _: 1
@@ -17238,13 +17265,13 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17238
17265
  }, [
17239
17266
  isMainCommand.value ? (openBlock(), createElementBlock("div", {
17240
17267
  key: 0,
17241
- onMousemove: _cache[20] || (_cache[20] = (e) => e.stopPropagation()),
17268
+ onMousemove: _cache[21] || (_cache[21] = (e) => e.stopPropagation()),
17242
17269
  class: "cursor-pointer z-8 box-border bg-[rgba(0,0,0,0.5)] rounded-[6px] p-[5px] absolute left-[50%] translate-x-[-50%] top-[20px] flex gap-[5px] items-center"
17243
17270
  }, [
17244
17271
  createElementVNode("button", {
17245
- onClick: _cache[19] || (_cache[19] = (e) => (unref(editor).cancelCommand(), e.stopPropagation())),
17272
+ onClick: _cache[20] || (_cache[20] = (e) => (unref(editor).cancelCommand(), e.stopPropagation())),
17246
17273
  class: "bg-transparent! button cursor-pointer"
17247
- }, [..._cache[37] || (_cache[37] = [
17274
+ }, [..._cache[38] || (_cache[38] = [
17248
17275
  createElementVNode("svg", {
17249
17276
  fill: "#fff",
17250
17277
  viewBox: "0 0 1024 1024",
@@ -17260,7 +17287,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17260
17287
  key: 0,
17261
17288
  onClick: commandConfirm,
17262
17289
  class: "button bg-transparent! cursor-pointer"
17263
- }, [..._cache[38] || (_cache[38] = [
17290
+ }, [..._cache[39] || (_cache[39] = [
17264
17291
  createElementVNode("svg", {
17265
17292
  fill: "#28c932",
17266
17293
  viewBox: "0 0 1026 1024",
@@ -17281,7 +17308,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
17281
17308
  };
17282
17309
  }
17283
17310
  });
17284
- const EditorToolContent = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-978973e0"]]);
17311
+ const EditorToolContent = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-e23c82bd"]]);
17285
17312
  class StorageHelper {
17286
17313
  static get(key, defaultValue = void 0) {
17287
17314
  const value = localStorage.getItem(key);
@@ -18,6 +18,11 @@ export declare class LineSegmentUtils {
18
18
  * @param lines
19
19
  */
20
20
  static getLinesCenter(lines: LineSegment[]): Point<Record<string, any>>;
21
+ /** 自定义分组
22
+ * @param lines
23
+ * @returns
24
+ */
25
+ static group<T = any, K extends string | number | symbol = string>(lines: LineSegment<T>[], callBackFun: (line: LineSegment<T>) => K): Partial<Record<K, LineSegment<T>[]>>;
21
26
  /** 通过在同一路径上且平行的线段分组
22
27
  * @param lines
23
28
  */