build-dxf 0.1.106 → 0.1.108

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.108",
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
@@ -9536,6 +9572,10 @@ const holeTypeMap = {
9536
9572
  passageEntrance: "WALL_HOLE",
9537
9573
  bay_window: "BAY_WINDOW"
9538
9574
  };
9575
+ const placeHoldersMap = {
9576
+ switch: "开关",
9577
+ socket: "插座"
9578
+ };
9539
9579
  const WALL_HEIGHT_KEY = /* @__PURE__ */ Symbol("height");
9540
9580
  class ThreeVJiaPipeline extends Pipeline {
9541
9581
  manager;
@@ -9568,17 +9608,10 @@ class ThreeVJiaPipeline extends Pipeline {
9568
9608
  if (lines2.length < 4) return lines2;
9569
9609
  let newLine = null;
9570
9610
  let wallWidth = 0;
9571
- const poly = Polygon.fromByLinePath(lines2).getMinimumBoundingRectangle();
9572
- 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));
9575
- wallWidth = Math.min(len1, len2);
9576
- } else {
9577
- lines2 = [...lines2].sort((a2, b4) => a2.length() - b4.length());
9578
- const line1 = lines2[0], line2 = lines2[1];
9579
- newLine = new LineSegment(line1.center.clone(), line2.center.clone());
9580
- wallWidth = line1.length();
9581
- }
9611
+ lines2 = [...lines2].sort((a2, b4) => a2.length() - b4.length());
9612
+ const line1 = lines2[0], line2 = lines2[1];
9613
+ newLine = new LineSegment(line1.center.clone(), line2.center.clone());
9614
+ wallWidth = line1.length();
9582
9615
  mergeLineUserData(newLine, lines2);
9583
9616
  newLine.userData.wallWidth = wallWidth;
9584
9617
  return [newLine];
@@ -9727,7 +9760,6 @@ class ThreeVJiaPipeline extends Pipeline {
9727
9760
  type: "LINE",
9728
9761
  isDoor: false,
9729
9762
  loadBearingWall: false,
9730
- // height: line.userData.height ?? DEFAULT_WALL_HEIGHT
9731
9763
  height: precision4(line.currentData[WALL_HEIGHT_KEY] ?? this.maxHeight)
9732
9764
  });
9733
9765
  });
@@ -9760,12 +9792,13 @@ class ThreeVJiaPipeline extends Pipeline {
9760
9792
  const rooms = json.rooms, roomPloys = rooms.map((room) => new Polygon(room.polygon.map((p2) => Point.from(p2))));
9761
9793
  if (roomPloys.length === 0) return json;
9762
9794
  publicInfo.itemInfo.forEach((item, i) => {
9795
+ if (!placeHoldersMap[item.category]) return;
9763
9796
  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
9797
  if (index2 < 0) return;
9765
9798
  json.placeHolders.push({
9766
- name: item.category,
9767
- polygon: item.contour,
9768
- direction: item.direction,
9799
+ name: placeHoldersMap[item.category],
9800
+ polygon: itemPoly.getMinimumBoundingRectangle().map((p2) => p2.toJson2D()),
9801
+ direction: Point.from(item.direction).rotate(Point.zero(), json.angle),
9769
9802
  height: item.box.max.z - item.box.min.z,
9770
9803
  sillHeight: item.box.min.z - z,
9771
9804
  roomId: rooms[index2].roomTypeId,
@@ -9782,7 +9815,6 @@ class ThreeVJiaPipeline extends Pipeline {
9782
9815
  * @returns
9783
9816
  */
9784
9817
  static toThreeVJiaJson(lineSegments, angle = 0, updateGroup = true) {
9785
- angle = precision4(angle);
9786
9818
  let centerVec2 = null;
9787
9819
  lineSegments = LineSegmentUndirectedGraph.rotate(lineSegments.map((line) => line.clone()), angle, (line, center, angle2) => {
9788
9820
  if (!centerVec2) centerVec2 = { x: center.x, y: center.y };
@@ -10679,13 +10711,19 @@ class DoubleWallHelper {
10679
10711
  const newLine1 = new LineSegment(project0.start.clone(), project1.start.clone());
10680
10712
  const newLine2 = new LineSegment(project0.end.clone(), project1.end.clone());
10681
10713
  newLine1.userData.height = line0.userData.height;
10714
+ newLine1.userData.rooftopPz = line0.userData.rooftopPz;
10682
10715
  newLine2.userData.height = line1.userData.height;
10716
+ newLine2.userData.rooftopPz = line1.userData.rooftopPz;
10683
10717
  appendLines.push(newLine1, newLine2);
10684
10718
  addClipingMap(line0, project0);
10685
10719
  addClipingMap(line1, project1);
10686
10720
  });
10687
10721
  clipingMap.forEach((list, line) => {
10688
10722
  const newLines = LineSegmentUtils.clippingByPoints(line, list);
10723
+ newLines.forEach((newLine) => {
10724
+ newLine.userData.height = line.userData.height;
10725
+ newLine.userData.rooftopPz = line.userData.rooftopPz;
10726
+ });
10689
10727
  lines.push(...newLines);
10690
10728
  });
10691
10729
  lines = lines.filter((line) => !removeLines.has(line));
@@ -10696,9 +10734,11 @@ class DoubleWallHelper {
10696
10734
  const queryLines = quadtree.queryRect(line.toRectangle(1e-3, "butt")).filter((item) => item.line.isParallelTo(line)).map((item) => item.line);
10697
10735
  if (queryLines.length) {
10698
10736
  const newLines = LineSegmentUtils.clippingByLines(line, queryLines);
10699
- if (newLines) return newLines.forEach((line2) => {
10700
- quadtree.insert(line2);
10701
- lines.push(line2);
10737
+ if (newLines) return newLines.forEach((newLine) => {
10738
+ newLine.userData.height = line.userData.height;
10739
+ newLine.userData.rooftopPz = line.userData.rooftopPz;
10740
+ quadtree.insert(newLine);
10741
+ lines.push(newLine);
10702
10742
  });
10703
10743
  }
10704
10744
  quadtree.insert(line);
@@ -11031,7 +11071,7 @@ class AlignToParallelSegments {
11031
11071
  * @param esp 垂直投影值分组距离
11032
11072
  * @param gap 平行轴投影区间间隙
11033
11073
  */
11034
- static group(lines, parallelAxis, verticalAxis, esp = 0.05, gap = 0.01) {
11074
+ static group(lines, parallelAxis, verticalAxis, esp = 0.05, gap = 0.01, onGroup) {
11035
11075
  esp = esp / verticalAxis.len;
11036
11076
  gap = gap / parallelAxis.len;
11037
11077
  const vls = lines.map((line) => [
@@ -11039,16 +11079,29 @@ class AlignToParallelSegments {
11039
11079
  line,
11040
11080
  [parallelAxis.projectPointValue(line.start), parallelAxis.projectPointValue(line.end)].sort((a2, b4) => a2 - b4)
11041
11081
  ]).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++) {
11082
+ for (let i2 = 0; i2 < vls.length; i2++) {
11083
+ const [ipv, _iline, [istart, iend]] = vls[i2];
11084
+ for (let j = i2 + 1; j < vls.length; j++) {
11045
11085
  const [jpv, _jline, [jstart, jend]] = vls[j];
11046
11086
  if (jpv - ipv > esp) break;
11047
11087
  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]));
11088
+ if (overlap > -gap) unionFindSet.union(i2, j);
11089
+ }
11090
+ }
11091
+ const map = unionFindSet.getAllSets();
11092
+ const groups = new Array(map.size);
11093
+ let i = 0;
11094
+ map.forEach((list) => {
11095
+ const group = new Array(list.length);
11096
+ const pvList = new Array(list.length);
11097
+ groups[i++] = group;
11098
+ for (let i2 = 0; i2 < list.length; i2++) {
11099
+ const [pv, line] = vls[list[i2]];
11100
+ group[i2] = line;
11101
+ pvList[i2] = pv;
11102
+ }
11103
+ onGroup?.(group, pvList, verticalAxis);
11104
+ });
11052
11105
  return groups;
11053
11106
  }
11054
11107
  /** 拟合并对齐线段
@@ -11088,13 +11141,13 @@ class AlignToParallelSegments {
11088
11141
  * @returns
11089
11142
  */
11090
11143
  static align(lines, opt = {}) {
11091
- const { esp = 0.06, gap = 0.05 } = opt;
11144
+ const { esp = 0.08, gap = 0.05 } = opt;
11092
11145
  const axisLine = lines[LineSegmentUtils.maxLengthLineIndex(lines)].clone().setLength(100), axisLineV = axisLine.clone().rotate(Math.PI * 0.5, axisLine.center);
11093
11146
  let [pllLines, verticalLines] = LineSegmentUtils.groupByParallelToAxis(lines, axisLine);
11094
- const groups = this.group(pllLines, axisLine, axisLineV, esp, gap);
11147
+ const groups = this.group(pllLines, axisLine, axisLineV, esp, gap, opt.onGroup);
11095
11148
  pllLines = this.fittingAlignment(groups, verticalLines, opt);
11096
11149
  verticalLines = verticalLines.filter((line) => line.length() > 1e-6);
11097
- const groups2 = this.group(verticalLines, axisLineV, axisLine, esp, gap);
11150
+ const groups2 = this.group(verticalLines, axisLineV, axisLine, esp, gap, opt.onGroup);
11098
11151
  verticalLines = this.fittingAlignment(groups2, pllLines, opt);
11099
11152
  pllLines = pllLines.filter((line) => line.length() > 1e-6);
11100
11153
  return [
@@ -11230,7 +11283,17 @@ function correction(lines, targettLine, option) {
11230
11283
  lines = AxisAlignCorr.start(lines, targettLine);
11231
11284
  lines = lineSegmentClipping(lines, 1e-9);
11232
11285
  lines = AlignToParallelSegments.align(lines, {
11233
- onMergeLine: mergeLineUserData
11286
+ onMergeLine: mergeLineUserData,
11287
+ onGroup(group, projValues, axis) {
11288
+ if (group.length <= 1) return;
11289
+ const max = Math.max(...projValues);
11290
+ const min = Math.min(...projValues);
11291
+ const dist = precision4((max - min) * axis.len);
11292
+ if (dist < DEFAULT_WALL_WIDTH) return;
11293
+ group.forEach((line) => {
11294
+ line.userData.wallWidth = dist;
11295
+ });
11296
+ }
11234
11297
  // esp: 0.4
11235
11298
  });
11236
11299
  lines = adsorption(lines, option);
@@ -11269,7 +11332,22 @@ function axisAlignCorr(lines, option, verticalReferenceLine) {
11269
11332
  }
11270
11333
  return lines;
11271
11334
  }
11335
+ function sparse(trajectory2, size2 = 0.4) {
11336
+ const idSet = /* @__PURE__ */ new Set();
11337
+ Object.keys(trajectory2 ?? {}).forEach((key) => {
11338
+ const p2 = trajectory2[key], i = Math.round(p2.x / size2), j = Math.round(p2.y / size2), id = BigInt(i) << 32n | BigInt(j) & 0xffffffffn;
11339
+ if (idSet.has(id)) {
11340
+ delete trajectory2[key];
11341
+ return;
11342
+ }
11343
+ idSet.add(id);
11344
+ });
11345
+ }
11272
11346
  function init(lines, option) {
11347
+ if (option.trajectory) {
11348
+ sparse(option.trajectory, 0.2);
11349
+ sparse(option.trajectory, 0.5);
11350
+ }
11273
11351
  BuildGroup.doubleWall.setTrajectory(option.trajectory);
11274
11352
  lines = lines.map((line) => line.clone());
11275
11353
  return lines;
@@ -11767,9 +11845,17 @@ function wallHeightHandle(lineSegments, option) {
11767
11845
  Object.defineProperty(option.publicInfo.rootTopContourInfo, "quadtree", { value: quadtree });
11768
11846
  }
11769
11847
  }
11848
+ const psh = PointSpatialHash.fromByLines(lineSegments);
11770
11849
  lineSegments.forEach((line) => {
11771
11850
  if (!("height" in line.userData)) {
11772
- if (!line.userData.rooftopPz && option?.publicInfo?.rootTopContourInfo) line.userData.rooftopPz = HeightQuery.query(line, option.publicInfo.rootTopContourInfo);
11851
+ if (!line.userData.rooftopPz && option?.publicInfo?.rootTopContourInfo) {
11852
+ const list = psh.queryPoints(line.points, true).map((item) => item.userData?.userData.rooftopPz ?? 0);
11853
+ if (list.length) {
11854
+ line.userData.rooftopPz = Math.max(...list);
11855
+ } else {
11856
+ line.userData.rooftopPz = HeightQuery.query(line, option.publicInfo.rootTopContourInfo);
11857
+ }
11858
+ }
11773
11859
  if ("rooftopPz" in line.userData) line.userData.height = Math.abs(line.userData.rooftopPz - (option.originalZ ?? 0));
11774
11860
  }
11775
11861
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -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
  */