build-dxf 0.1.144 → 0.1.145

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.144",
3
+ "version": "0.1.145",
4
4
  "description": "",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
package/src/build.js CHANGED
@@ -4337,6 +4337,35 @@ class LineSegmentUtils {
4337
4337
  }
4338
4338
  return result;
4339
4339
  }
4340
+ /** 线段最佳吸附参考线查找
4341
+ * @param opt
4342
+ */
4343
+ static findBestReferenceSegment(opt) {
4344
+ const { lines } = opt;
4345
+ const lsh = LineSegmentSpatialHash.from(opt.referenceLines);
4346
+ const results = new Array(lines.length);
4347
+ function parallelWeight(angle) {
4348
+ return Math.abs(angle - 90) / 90;
4349
+ }
4350
+ for (let i = 0; i < lines.length; i++) {
4351
+ const line = lines[i];
4352
+ const rectangle = line.toRectangle(0.5, "butt");
4353
+ const findResult = lsh.queryRectangle(rectangle).filter((quLine) => quLine.isParallelTo(line, 20)).sort((l1, l2) => {
4354
+ const angle1 = line.angle(l1, { unit: "degree", range: "180" });
4355
+ const angle2 = line.angle(l2, { unit: "degree", range: "180" });
4356
+ const aW1 = parallelWeight(angle1) * 5;
4357
+ const aW2 = parallelWeight(angle2) * 5;
4358
+ const dist1 = line.distanceToSegment(l1);
4359
+ const dist2 = line.distanceToSegment(l2);
4360
+ const maxDist = Math.max(dist1, dist2);
4361
+ const dW1 = (1 - dist1 / maxDist) * 3;
4362
+ const dW2 = (1 - dist2 / maxDist) * 3;
4363
+ return aW2 + dW2 - (aW1 + dW1);
4364
+ });
4365
+ results[i] = findResult.length ? findResult[0] : null;
4366
+ }
4367
+ return results;
4368
+ }
4340
4369
  }
4341
4370
  class Lines extends THREE.LineSegments {
4342
4371
  geometry = new THREE.BufferGeometry();
@@ -6267,6 +6296,16 @@ class Polygon extends Array {
6267
6296
  }
6268
6297
  return lines.filter((line) => line.length > 1e-9);
6269
6298
  }
6299
+ /** 转为矩形
6300
+ * @param out
6301
+ */
6302
+ toRectangle(out = new Rectangle()) {
6303
+ const polygon2 = this.getMinimumBoundingRectangle();
6304
+ if (polygon2) {
6305
+ out.points = [polygon2[0], polygon2[1], polygon2[2], polygon2[3]];
6306
+ return out;
6307
+ }
6308
+ }
6270
6309
  toArrays() {
6271
6310
  return this.map((p2) => p2.toArray());
6272
6311
  }
@@ -8480,10 +8519,134 @@ class PCSparseOctree {
8480
8519
  }
8481
8520
  }
8482
8521
  const box3$1 = new THREE.Box3();
8522
+ function projectPointToSegment(point2, start, end) {
8523
+ const dir = end.clone().sub(start);
8524
+ const len2 = dir.lengthSq();
8525
+ if (len2 < 1e-12) {
8526
+ return 0;
8527
+ }
8528
+ let t2 = point2.clone().sub(start).dot(dir) / len2;
8529
+ t2 = Math.max(0, Math.min(1, t2));
8530
+ return t2;
8531
+ }
8532
+ function rectangleToLine(rectangle) {
8533
+ const [l0, l1, l2, l3] = rectangle.toLines();
8534
+ if (l0.length < l1.length) return new LineSegment(l0.center, l2.center);
8535
+ return new LineSegment(l1.center, l3.center);
8536
+ }
8537
+ class CheckPointCloudContinuity {
8538
+ static continuity(ts2, len, gridSize = 0.02, ratio = 0.6) {
8539
+ if (ts2.length < 2) return false;
8540
+ const voxels = /* @__PURE__ */ new Set();
8541
+ let min = Infinity;
8542
+ let max = -Infinity;
8543
+ for (let i2 = 0; i2 < ts2.length; i2++) {
8544
+ const dist = ts2[i2] * len;
8545
+ const voxel = Math.floor(dist / gridSize);
8546
+ voxels.add(voxel);
8547
+ min = Math.min(min, voxel);
8548
+ max = Math.max(max, voxel);
8549
+ }
8550
+ const values = [...voxels].sort((a2, b4) => a2 - b4);
8551
+ let start = values[0], i = 0;
8552
+ const ranges = [];
8553
+ for (i = 1; i < values.length; i++) {
8554
+ const pre = values[i - 1];
8555
+ const v2 = values[i];
8556
+ if (v2 - pre > 2) {
8557
+ ranges.push([start, pre]);
8558
+ start = v2;
8559
+ }
8560
+ }
8561
+ ranges.push([start, values[values.length - 1]]);
8562
+ const occupiedLength = ranges.reduce((sum, [start2, end]) => sum + (end - start2 + 1), 0);
8563
+ const totalLength = max - min + 1;
8564
+ if (occupiedLength / totalLength < ratio) {
8565
+ return false;
8566
+ }
8567
+ return true;
8568
+ }
8569
+ /** 多轴投影连续性检测
8570
+ * @param points
8571
+ */
8572
+ static checkByProjectionContinuity(points, opt) {
8573
+ const { ratio = 0.6, axisLine } = opt ?? {};
8574
+ let xline = axisLine;
8575
+ if (!xline) {
8576
+ const rectangle = new Polygon(
8577
+ points.map((p2) => new Point(p2.x, p2.y))
8578
+ ).toRectangle();
8579
+ xline = rectangleToLine(rectangle);
8580
+ }
8581
+ const start = new Vector3(), end = new Vector3();
8582
+ start.set(xline.start.x, xline.start.y, 0);
8583
+ end.set(xline.end.x, xline.end.y, 0);
8584
+ const xts = this.projectPoint(points, start, end);
8585
+ if (!this.continuity(xts, xline.length, ratio)) return false;
8586
+ const yline2 = xline.clone().rotate(Math.PI * 0.5);
8587
+ start.set(yline2.start.x, yline2.start.y, 0);
8588
+ end.set(yline2.end.x, yline2.end.y, 0);
8589
+ const yts = this.projectPoint(points, start, end);
8590
+ if (!this.continuity(yts, yline2.length, ratio)) return false;
8591
+ const center = xline.center.clone();
8592
+ start.set(center.x, center.y, 10);
8593
+ end.set(center.x, center.y, -10);
8594
+ const zts = this.projectPoint(points, start, end);
8595
+ if (!this.continuity(zts, 20, ratio)) return false;
8596
+ return true;
8597
+ }
8598
+ /** 通过区域投影面积占比检测
8599
+ * @param points
8600
+ * @returns
8601
+ */
8602
+ static checkByVoxelDensity(points, opt) {
8603
+ const { gridSize = 0.08, xRatio = 0.5, yRatio = 0.4, overallRatio = 0.5, axisLine } = opt ?? {};
8604
+ let xline = axisLine;
8605
+ if (!xline) {
8606
+ const rectangle = new Polygon(points.map((p2) => new Point(p2.x, p2.y))).toRectangle();
8607
+ xline = rectangleToLine(rectangle);
8608
+ }
8609
+ const dir = xline.direction(), angle = Math.atan2(dir.y, dir.x), center = xline.center, cos = Math.cos(-angle), sin = Math.sin(-angle), voxelizationPoints = [], xMap = new CounterMap(), yMap = new CounterMap(), occupied = /* @__PURE__ */ new Set();
8610
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
8611
+ points.forEach((p2) => {
8612
+ const dx = p2.x - center.x, dy = p2.y - center.y, x = Math.floor((dx * cos - dy * sin) / gridSize), y = Math.floor(p2.z / gridSize), key = BigInt(x) << 32n ^ BigInt(y) & 0xffffffffn;
8613
+ if (occupied.has(key)) return;
8614
+ occupied.add(key);
8615
+ xMap.increment(x, 1);
8616
+ yMap.increment(y, 1);
8617
+ voxelizationPoints.push(x, y);
8618
+ minX = Math.min(minX, x);
8619
+ minY = Math.min(minY, y);
8620
+ maxX = Math.max(maxX, x);
8621
+ maxY = Math.max(maxY, y);
8622
+ });
8623
+ const xRange = maxX - minX + 1, yRange = maxY - minY + 1;
8624
+ let satisfyQuantity = 0;
8625
+ for (let i = 0; i < voxelizationPoints.length; i += 2) {
8626
+ const x = voxelizationPoints[i], y = voxelizationPoints[i + 1], xCount = xMap.get(x), yCount = yMap.get(y);
8627
+ if (xCount / yRange > xRatio && yCount / xRange > yRatio) satisfyQuantity++;
8628
+ }
8629
+ return satisfyQuantity / (voxelizationPoints.length / 2) > overallRatio;
8630
+ }
8631
+ /** 获取投影值
8632
+ * @param points
8633
+ * @param start
8634
+ * @param end
8635
+ * @returns
8636
+ */
8637
+ static projectPoint(points, start, end) {
8638
+ const point2 = new Vector3();
8639
+ return points.map((p2) => {
8640
+ point2.copy(p2);
8641
+ return projectPointToSegment(point2, start, end);
8642
+ }).sort((a2, b4) => a2 - b4);
8643
+ }
8644
+ }
8483
8645
  const index$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
8484
8646
  __proto__: null,
8485
8647
  ArrayMap,
8486
8648
  Box2,
8649
+ CheckPointCloudContinuity,
8487
8650
  CommandFlow,
8488
8651
  CommandManager,
8489
8652
  Component,
@@ -0,0 +1,51 @@
1
+ import { LineSegment } from './LineSegment';
2
+ import { Vector3 } from 'three';
3
+ type Point3D = {
4
+ x: number;
5
+ y: number;
6
+ z: number;
7
+ };
8
+ /**
9
+ * 检测点云在分部轴上的连续性,z朝上
10
+ */
11
+ export declare class CheckPointCloudContinuity {
12
+ static continuity(ts: number[], len: number, gridSize?: number, ratio?: number): boolean;
13
+ /** 多轴投影连续性检测
14
+ * @param points
15
+ */
16
+ static checkByProjectionContinuity(points: Point3D[], opt?: {
17
+ ratio?: number;
18
+ axisLine?: LineSegment;
19
+ }): boolean;
20
+ /** 通过区域投影面积占比检测
21
+ * @param points
22
+ * @returns
23
+ */
24
+ static checkByVoxelDensity(points: Point3D[], opt?: {
25
+ /**
26
+ * 体素大小
27
+ */
28
+ gridSize?: number;
29
+ /**
30
+ * 竖直轴占比比例
31
+ */
32
+ xRatio?: number;
33
+ /**
34
+ * 水平轴占比比例
35
+ */
36
+ yRatio?: number;
37
+ /**
38
+ * 总占比比例
39
+ */
40
+ overallRatio?: number;
41
+ axisLine?: LineSegment;
42
+ }): boolean;
43
+ /** 获取投影值
44
+ * @param points
45
+ * @param start
46
+ * @param end
47
+ * @returns
48
+ */
49
+ static projectPoint(points: Point3D[], start: Vector3, end: Vector3): number[];
50
+ }
51
+ export {};
@@ -1,5 +1,6 @@
1
1
  import { LineSegment } from './LineSegment';
2
2
  import { Point } from './Point';
3
+ import { Rectangle } from './Rectangle';
3
4
  export declare class LineSegmentUtils {
4
5
  static clone(lines: LineSegment[]): LineSegment<Record<string, any>>[];
5
6
  /**保留小数位数
@@ -156,4 +157,17 @@ export declare class LineSegmentUtils {
156
157
  * @param pattern
157
158
  */
158
159
  static dash(line: LineSegment, pattern: number[]): LineSegment<Record<string, any>>[];
160
+ /** 线段最佳吸附参考线查找
161
+ * @param opt
162
+ */
163
+ static findBestReferenceSegment<TLines extends readonly LineSegment[], TReferenceLines extends readonly LineSegment[]>(opt: {
164
+ lines: TLines;
165
+ linesScope: {
166
+ [K in keyof TLines]: Rectangle;
167
+ };
168
+ referenceLines: TReferenceLines;
169
+ referenceLinesScope: {
170
+ [K in keyof TReferenceLines]: Rectangle;
171
+ };
172
+ }): (LineSegment<Record<string, any>> | null)[];
159
173
  }
@@ -1,6 +1,7 @@
1
1
  import { Box2 } from './Box2';
2
2
  import { LineSegment } from './LineSegment';
3
3
  import { Point } from './Point';
4
+ import { Rectangle } from './Rectangle';
4
5
  type BooleanOpType = "Union" | "Intersection" | "Difference" | "Xor";
5
6
  type FillType = "EvenOdd" | "NonZero" | "Positive" | "Negative";
6
7
  type BooleanOpOption = {
@@ -105,6 +106,10 @@ export declare class Polygon<T = any> extends Array<Point<T>> {
105
106
  * @returns
106
107
  */
107
108
  toLines(closed?: boolean): LineSegment<Record<string, any>>[];
109
+ /** 转为矩形
110
+ * @param out
111
+ */
112
+ toRectangle(out?: Rectangle): Rectangle | undefined;
108
113
  toArrays(): [number, number][];
109
114
  close(): this;
110
115
  clone(): Polygon<Record<string, any>>;
@@ -21,3 +21,4 @@ export * from './algorithms/MaxiCircles';
21
21
  export * from './algorithms/MiniCircles';
22
22
  export * from './algorithms/OBB';
23
23
  export * from './algorithms/PCSparseOctree';
24
+ export * from './algorithms/CheckPointCloudContinuity';