build-dxf 0.1.106 → 0.1.107

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.106",
3
+ "version": "0.1.107",
4
4
  "description": "线段构建双线墙壁的dxf版本",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
package/src/build.js CHANGED
@@ -5487,13 +5487,49 @@ class Polygon extends Array {
5487
5487
  this.length = 0;
5488
5488
  this.push(...points);
5489
5489
  }
5490
- getCenter() {
5491
- let countX = 0, countY = 0;
5492
- this.forEach((p2) => {
5493
- countX += p2.x;
5494
- countY += p2.y;
5495
- });
5496
- return new Point(countX / this.length, countY / this.length);
5490
+ /**
5491
+ * 获取多边形几何中心(面积质心)
5492
+ * @description 使用多边形面积质心公式计算
5493
+ * @description 适用于闭合简单多边形(顺时针或逆时针均可)
5494
+ * @description 如果面积为 0,则退化为顶点平均中心
5495
+ * @param out 输出对象,用于减少 GC
5496
+ * @returns
5497
+ */
5498
+ getCenter(out = new Point()) {
5499
+ const len = this.length;
5500
+ if (len === 0) {
5501
+ return out.set(0, 0);
5502
+ }
5503
+ let area2 = 0;
5504
+ let centerX = 0;
5505
+ let centerY = 0;
5506
+ for (let i = 0; i < len; i++) {
5507
+ const p1 = this[i];
5508
+ const p2 = this[(i + 1) % len];
5509
+ const cross = p1.x * p2.y - p2.x * p1.y;
5510
+ area2 += cross;
5511
+ centerX += (p1.x + p2.x) * cross;
5512
+ centerY += (p1.y + p2.y) * cross;
5513
+ }
5514
+ area2 *= 0.5;
5515
+ if (Math.abs(area2) < 1e-9) {
5516
+ let sumX = 0;
5517
+ let sumY = 0;
5518
+ for (let i = 0; i < len; i++) {
5519
+ sumX += this[i].x;
5520
+ sumY += this[i].y;
5521
+ }
5522
+ const inv = 1 / len;
5523
+ return out.set(
5524
+ sumX * inv,
5525
+ sumY * inv
5526
+ );
5527
+ }
5528
+ const factor = 1 / (6 * area2);
5529
+ return out.set(
5530
+ centerX * factor,
5531
+ centerY * factor
5532
+ );
5497
5533
  }
5498
5534
  /** 计算二维包围盒
5499
5535
  * @returns
@@ -9570,8 +9606,8 @@ class ThreeVJiaPipeline extends Pipeline {
9570
9606
  let wallWidth = 0;
9571
9607
  const poly = Polygon.fromByLinePath(lines2).getMinimumBoundingRectangle();
9572
9608
  if (poly) {
9573
- 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();
9574
- newLine = center.expandAsLine(direct, 1).setLength(Math.max(len1, len2));
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);
9575
9611
  wallWidth = Math.min(len1, len2);
9576
9612
  } else {
9577
9613
  lines2 = [...lines2].sort((a2, b4) => a2.length() - b4.length());
@@ -9727,7 +9763,6 @@ class ThreeVJiaPipeline extends Pipeline {
9727
9763
  type: "LINE",
9728
9764
  isDoor: false,
9729
9765
  loadBearingWall: false,
9730
- // height: line.userData.height ?? DEFAULT_WALL_HEIGHT
9731
9766
  height: precision4(line.currentData[WALL_HEIGHT_KEY] ?? this.maxHeight)
9732
9767
  });
9733
9768
  });
@@ -9760,6 +9795,7 @@ class ThreeVJiaPipeline extends Pipeline {
9760
9795
  const rooms = json.rooms, roomPloys = rooms.map((room) => new Polygon(room.polygon.map((p2) => Point.from(p2))));
9761
9796
  if (roomPloys.length === 0) return json;
9762
9797
  publicInfo.itemInfo.forEach((item, i) => {
9798
+ if (item.category !== "switch" && item.category !== "socket") return;
9763
9799
  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));
9764
9800
  if (index2 < 0) return;
9765
9801
  json.placeHolders.push({
@@ -9782,7 +9818,6 @@ class ThreeVJiaPipeline extends Pipeline {
9782
9818
  * @returns
9783
9819
  */
9784
9820
  static toThreeVJiaJson(lineSegments, angle = 0, updateGroup = true) {
9785
- angle = precision4(angle);
9786
9821
  let centerVec2 = null;
9787
9822
  lineSegments = LineSegmentUndirectedGraph.rotate(lineSegments.map((line) => line.clone()), angle, (line, center, angle2) => {
9788
9823
  if (!centerVec2) centerVec2 = { x: center.x, y: center.y };
@@ -10679,13 +10714,19 @@ class DoubleWallHelper {
10679
10714
  const newLine1 = new LineSegment(project0.start.clone(), project1.start.clone());
10680
10715
  const newLine2 = new LineSegment(project0.end.clone(), project1.end.clone());
10681
10716
  newLine1.userData.height = line0.userData.height;
10717
+ newLine1.userData.rooftopPz = line0.userData.rooftopPz;
10682
10718
  newLine2.userData.height = line1.userData.height;
10719
+ newLine2.userData.rooftopPz = line1.userData.rooftopPz;
10683
10720
  appendLines.push(newLine1, newLine2);
10684
10721
  addClipingMap(line0, project0);
10685
10722
  addClipingMap(line1, project1);
10686
10723
  });
10687
10724
  clipingMap.forEach((list, line) => {
10688
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
+ });
10689
10730
  lines.push(...newLines);
10690
10731
  });
10691
10732
  lines = lines.filter((line) => !removeLines.has(line));
@@ -10696,9 +10737,11 @@ class DoubleWallHelper {
10696
10737
  const queryLines = quadtree.queryRect(line.toRectangle(1e-3, "butt")).filter((item) => item.line.isParallelTo(line)).map((item) => item.line);
10697
10738
  if (queryLines.length) {
10698
10739
  const newLines = LineSegmentUtils.clippingByLines(line, queryLines);
10699
- if (newLines) return newLines.forEach((line2) => {
10700
- quadtree.insert(line2);
10701
- lines.push(line2);
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);
10702
10745
  });
10703
10746
  }
10704
10747
  quadtree.insert(line);
@@ -11031,7 +11074,7 @@ class AlignToParallelSegments {
11031
11074
  * @param esp 垂直投影值分组距离
11032
11075
  * @param gap 平行轴投影区间间隙
11033
11076
  */
11034
- static group(lines, parallelAxis, verticalAxis, esp = 0.05, gap = 0.01) {
11077
+ static group(lines, parallelAxis, verticalAxis, esp = 0.05, gap = 0.01, onGroup) {
11035
11078
  esp = esp / verticalAxis.len;
11036
11079
  gap = gap / parallelAxis.len;
11037
11080
  const vls = lines.map((line) => [
@@ -11039,16 +11082,29 @@ class AlignToParallelSegments {
11039
11082
  line,
11040
11083
  [parallelAxis.projectPointValue(line.start), parallelAxis.projectPointValue(line.end)].sort((a2, b4) => a2 - b4)
11041
11084
  ]).sort((a2, b4) => a2[0] - b4[0]), unionFindSet = new UnionFindSet(vls.length);
11042
- for (let i = 0; i < vls.length; i++) {
11043
- const [ipv, _iline, [istart, iend]] = vls[i];
11044
- for (let j = i + 1; j < vls.length; j++) {
11085
+ for (let i2 = 0; i2 < vls.length; i2++) {
11086
+ const [ipv, _iline, [istart, iend]] = vls[i2];
11087
+ for (let j = i2 + 1; j < vls.length; j++) {
11045
11088
  const [jpv, _jline, [jstart, jend]] = vls[j];
11046
11089
  if (jpv - ipv > esp) break;
11047
11090
  const overlap = Math.min(iend, jend) - Math.max(istart, jstart);
11048
- if (overlap > -gap) unionFindSet.union(i, j);
11049
- }
11050
- }
11051
- const groups = unionFindSet.getAllSets().map((v2) => v2.map((v22) => vls[v22][1]));
11091
+ if (overlap > -gap) unionFindSet.union(i2, j);
11092
+ }
11093
+ }
11094
+ const map = unionFindSet.getAllSets();
11095
+ const groups = new Array(map.size);
11096
+ let i = 0;
11097
+ map.forEach((list) => {
11098
+ const group = new Array(list.length);
11099
+ const pvList = new Array(list.length);
11100
+ groups[i++] = group;
11101
+ for (let i2 = 0; i2 < list.length; i2++) {
11102
+ const [pv, line] = vls[list[i2]];
11103
+ group[i2] = line;
11104
+ pvList[i2] = pv;
11105
+ }
11106
+ onGroup?.(group, pvList, verticalAxis);
11107
+ });
11052
11108
  return groups;
11053
11109
  }
11054
11110
  /** 拟合并对齐线段
@@ -11088,13 +11144,13 @@ class AlignToParallelSegments {
11088
11144
  * @returns
11089
11145
  */
11090
11146
  static align(lines, opt = {}) {
11091
- const { esp = 0.06, gap = 0.05 } = opt;
11147
+ const { esp = 0.08, gap = 0.05 } = opt;
11092
11148
  const axisLine = lines[LineSegmentUtils.maxLengthLineIndex(lines)].clone().setLength(100), axisLineV = axisLine.clone().rotate(Math.PI * 0.5, axisLine.center);
11093
11149
  let [pllLines, verticalLines] = LineSegmentUtils.groupByParallelToAxis(lines, axisLine);
11094
- const groups = this.group(pllLines, axisLine, axisLineV, esp, gap);
11150
+ const groups = this.group(pllLines, axisLine, axisLineV, esp, gap, opt.onGroup);
11095
11151
  pllLines = this.fittingAlignment(groups, verticalLines, opt);
11096
11152
  verticalLines = verticalLines.filter((line) => line.length() > 1e-6);
11097
- const groups2 = this.group(verticalLines, axisLineV, axisLine, esp, gap);
11153
+ const groups2 = this.group(verticalLines, axisLineV, axisLine, esp, gap, opt.onGroup);
11098
11154
  verticalLines = this.fittingAlignment(groups2, pllLines, opt);
11099
11155
  pllLines = pllLines.filter((line) => line.length() > 1e-6);
11100
11156
  return [
@@ -11230,7 +11286,17 @@ function correction(lines, targettLine, option) {
11230
11286
  lines = AxisAlignCorr.start(lines, targettLine);
11231
11287
  lines = lineSegmentClipping(lines, 1e-9);
11232
11288
  lines = AlignToParallelSegments.align(lines, {
11233
- onMergeLine: mergeLineUserData
11289
+ onMergeLine: mergeLineUserData,
11290
+ onGroup(group, projValues, axis) {
11291
+ if (group.length <= 1) return;
11292
+ const max = Math.max(...projValues);
11293
+ const min = Math.min(...projValues);
11294
+ const dist = precision4((max - min) * axis.len);
11295
+ if (dist < DEFAULT_WALL_WIDTH) return;
11296
+ group.forEach((line) => {
11297
+ line.userData.wallWidth = dist;
11298
+ });
11299
+ }
11234
11300
  // esp: 0.4
11235
11301
  });
11236
11302
  lines = adsorption(lines, option);
@@ -11767,9 +11833,17 @@ function wallHeightHandle(lineSegments, option) {
11767
11833
  Object.defineProperty(option.publicInfo.rootTopContourInfo, "quadtree", { value: quadtree });
11768
11834
  }
11769
11835
  }
11836
+ const psh = PointSpatialHash.fromByLines(lineSegments);
11770
11837
  lineSegments.forEach((line) => {
11771
11838
  if (!("height" in line.userData)) {
11772
- if (!line.userData.rooftopPz && option?.publicInfo?.rootTopContourInfo) line.userData.rooftopPz = HeightQuery.query(line, option.publicInfo.rootTopContourInfo);
11839
+ if (!line.userData.rooftopPz && option?.publicInfo?.rootTopContourInfo) {
11840
+ const list = psh.queryPoints(line.points, true).map((item) => item.userData?.userData.rooftopPz ?? 0);
11841
+ if (list.length) {
11842
+ line.userData.rooftopPz = Math.max(...list);
11843
+ } else {
11844
+ line.userData.rooftopPz = HeightQuery.query(line, option.publicInfo.rootTopContourInfo);
11845
+ }
11846
+ }
11773
11847
  if ("rooftopPz" in line.userData) line.userData.height = Math.abs(line.userData.rooftopPz - (option.originalZ ?? 0));
11774
11848
  }
11775
11849
  });
@@ -1,8 +1,10 @@
1
1
  import { LineSegment } from './LineSegment';
2
+ type OnGroupFun = (group: LineSegment[], projValues: number[], axis: LineSegment) => void;
2
3
  export interface AlignDescriptor {
3
4
  esp?: number;
4
5
  gap?: number;
5
6
  onMergeLine?: (target: LineSegment, source: LineSegment) => void;
7
+ onGroup?: OnGroupFun;
6
8
  }
7
9
  /** 通过平行轴和垂直轴分组对齐线段,搭配垂直纠正使用
8
10
  */
@@ -14,7 +16,7 @@ export declare class AlignToParallelSegments {
14
16
  * @param esp 垂直投影值分组距离
15
17
  * @param gap 平行轴投影区间间隙
16
18
  */
17
- static group(lines: LineSegment[], parallelAxis: LineSegment, verticalAxis: LineSegment, esp?: number, gap?: number): LineSegment<Record<string, any>>[][];
19
+ static group(lines: LineSegment[], parallelAxis: LineSegment, verticalAxis: LineSegment, esp?: number, gap?: number, onGroup?: OnGroupFun): LineSegment<Record<string, any>>[][];
18
20
  /** 拟合并对齐线段
19
21
  * @param groups
20
22
  * @param verticalLines
@@ -26,3 +28,4 @@ export declare class AlignToParallelSegments {
26
28
  */
27
29
  static align(lines: LineSegment[], opt?: AlignDescriptor): LineSegment<Record<string, any>>[];
28
30
  }
31
+ export {};
@@ -13,7 +13,15 @@ export declare class Polygon<T = any> extends Array<Point<T>> {
13
13
  [Symbol.iterator](): ArrayIterator<Point<T>>;
14
14
  constructor(points?: Point<T>[]);
15
15
  set(points?: Point<T>[]): void;
16
- getCenter(): Point<Record<string, any>>;
16
+ /**
17
+ * 获取多边形几何中心(面积质心)
18
+ * @description 使用多边形面积质心公式计算
19
+ * @description 适用于闭合简单多边形(顺时针或逆时针均可)
20
+ * @description 如果面积为 0,则退化为顶点平均中心
21
+ * @param out 输出对象,用于减少 GC
22
+ * @returns
23
+ */
24
+ getCenter(out?: Point<Record<string, any>>): Point<Record<string, any>>;
17
25
  /** 计算二维包围盒
18
26
  * @returns
19
27
  */