build-dxf 0.1.144 → 0.1.146
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
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,144 @@ 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
|
+
let { ratio = 0.6, axisLine } = opt ?? {};
|
|
8574
|
+
if (!axisLine) {
|
|
8575
|
+
const rectangle = new Polygon(
|
|
8576
|
+
points.map((p2) => new Point(p2.x, p2.y))
|
|
8577
|
+
).toRectangle();
|
|
8578
|
+
axisLine = rectangleToLine(rectangle);
|
|
8579
|
+
} else if (!(axisLine instanceof LineSegment)) {
|
|
8580
|
+
axisLine = new LineSegment(Point.from(axisLine.start), Point.from(axisLine.end));
|
|
8581
|
+
}
|
|
8582
|
+
const xline = axisLine;
|
|
8583
|
+
const start = new Vector3(), end = new Vector3();
|
|
8584
|
+
start.set(xline.start.x, xline.start.y, 0);
|
|
8585
|
+
end.set(xline.end.x, xline.end.y, 0);
|
|
8586
|
+
const xts = this.projectPoint(points, start, end);
|
|
8587
|
+
if (!this.continuity(xts, xline.length, ratio)) return false;
|
|
8588
|
+
const yline2 = xline.clone().rotate(Math.PI * 0.5);
|
|
8589
|
+
start.set(yline2.start.x, yline2.start.y, 0);
|
|
8590
|
+
end.set(yline2.end.x, yline2.end.y, 0);
|
|
8591
|
+
const yts = this.projectPoint(points, start, end);
|
|
8592
|
+
if (!this.continuity(yts, yline2.length, ratio)) return false;
|
|
8593
|
+
const center = xline.center.clone();
|
|
8594
|
+
start.set(center.x, center.y, 10);
|
|
8595
|
+
end.set(center.x, center.y, -10);
|
|
8596
|
+
const zts = this.projectPoint(points, start, end);
|
|
8597
|
+
if (!this.continuity(zts, 20, ratio)) return false;
|
|
8598
|
+
return true;
|
|
8599
|
+
}
|
|
8600
|
+
/** 通过区域投影面积占比检测
|
|
8601
|
+
* @param points
|
|
8602
|
+
* @returns
|
|
8603
|
+
*/
|
|
8604
|
+
static checkByVoxelDensity(points, opt) {
|
|
8605
|
+
let { gridSize = 0.08, xRatio = 0.5, yRatio = 0.4, overallRatio = 0.5, axisLine } = opt ?? {};
|
|
8606
|
+
if (!axisLine) {
|
|
8607
|
+
const rectangle = new Polygon(
|
|
8608
|
+
points.map((p2) => new Point(p2.x, p2.y))
|
|
8609
|
+
).toRectangle();
|
|
8610
|
+
axisLine = rectangleToLine(rectangle);
|
|
8611
|
+
} else if (!(axisLine instanceof LineSegment)) {
|
|
8612
|
+
axisLine = new LineSegment(Point.from(axisLine.start), Point.from(axisLine.end));
|
|
8613
|
+
}
|
|
8614
|
+
let xline = axisLine;
|
|
8615
|
+
if (!xline) {
|
|
8616
|
+
const rectangle = new Polygon(points.map((p2) => new Point(p2.x, p2.y))).toRectangle();
|
|
8617
|
+
xline = rectangleToLine(rectangle);
|
|
8618
|
+
}
|
|
8619
|
+
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();
|
|
8620
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
8621
|
+
points.forEach((p2) => {
|
|
8622
|
+
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;
|
|
8623
|
+
if (occupied.has(key)) return;
|
|
8624
|
+
occupied.add(key);
|
|
8625
|
+
xMap.increment(x, 1);
|
|
8626
|
+
yMap.increment(y, 1);
|
|
8627
|
+
voxelizationPoints.push(x, y);
|
|
8628
|
+
minX = Math.min(minX, x);
|
|
8629
|
+
minY = Math.min(minY, y);
|
|
8630
|
+
maxX = Math.max(maxX, x);
|
|
8631
|
+
maxY = Math.max(maxY, y);
|
|
8632
|
+
});
|
|
8633
|
+
const xRange = maxX - minX + 1, yRange = maxY - minY + 1;
|
|
8634
|
+
let satisfyQuantity = 0;
|
|
8635
|
+
for (let i = 0; i < voxelizationPoints.length; i += 2) {
|
|
8636
|
+
const x = voxelizationPoints[i], y = voxelizationPoints[i + 1], xCount = xMap.get(x), yCount = yMap.get(y);
|
|
8637
|
+
if (xCount / yRange > xRatio && yCount / xRange > yRatio) satisfyQuantity++;
|
|
8638
|
+
}
|
|
8639
|
+
return satisfyQuantity / (voxelizationPoints.length / 2) > overallRatio;
|
|
8640
|
+
}
|
|
8641
|
+
/** 获取投影值
|
|
8642
|
+
* @param points
|
|
8643
|
+
* @param start
|
|
8644
|
+
* @param end
|
|
8645
|
+
* @returns
|
|
8646
|
+
*/
|
|
8647
|
+
static projectPoint(points, start, end) {
|
|
8648
|
+
const point2 = new Vector3();
|
|
8649
|
+
return points.map((p2) => {
|
|
8650
|
+
point2.copy(p2);
|
|
8651
|
+
return projectPointToSegment(point2, start, end);
|
|
8652
|
+
}).sort((a2, b4) => a2 - b4);
|
|
8653
|
+
}
|
|
8654
|
+
}
|
|
8483
8655
|
const index$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
8484
8656
|
__proto__: null,
|
|
8485
8657
|
ArrayMap,
|
|
8486
8658
|
Box2,
|
|
8659
|
+
CheckPointCloudContinuity,
|
|
8487
8660
|
CommandFlow,
|
|
8488
8661
|
CommandManager,
|
|
8489
8662
|
Component,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Vector3 } from 'three';
|
|
2
|
+
type Point3D = {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
z: number;
|
|
6
|
+
};
|
|
7
|
+
type LineSegmentType = {
|
|
8
|
+
start: {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
z?: number;
|
|
12
|
+
};
|
|
13
|
+
end: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
z?: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* 检测点云在分部轴上的连续性,z朝上
|
|
21
|
+
*/
|
|
22
|
+
export declare class CheckPointCloudContinuity {
|
|
23
|
+
static continuity(ts: number[], len: number, gridSize?: number, ratio?: number): boolean;
|
|
24
|
+
/** 多轴投影连续性检测
|
|
25
|
+
* @param points
|
|
26
|
+
*/
|
|
27
|
+
static checkByProjectionContinuity(points: Point3D[], opt?: {
|
|
28
|
+
ratio?: number;
|
|
29
|
+
axisLine?: LineSegmentType;
|
|
30
|
+
}): boolean;
|
|
31
|
+
/** 通过区域投影面积占比检测
|
|
32
|
+
* @param points
|
|
33
|
+
* @returns
|
|
34
|
+
*/
|
|
35
|
+
static checkByVoxelDensity(points: Point3D[], opt?: {
|
|
36
|
+
/**
|
|
37
|
+
* 体素大小
|
|
38
|
+
*/
|
|
39
|
+
gridSize?: number;
|
|
40
|
+
/**
|
|
41
|
+
* 竖直轴占比比例
|
|
42
|
+
*/
|
|
43
|
+
xRatio?: number;
|
|
44
|
+
/**
|
|
45
|
+
* 水平轴占比比例
|
|
46
|
+
*/
|
|
47
|
+
yRatio?: number;
|
|
48
|
+
/**
|
|
49
|
+
* 总占比比例
|
|
50
|
+
*/
|
|
51
|
+
overallRatio?: number;
|
|
52
|
+
axisLine?: LineSegmentType;
|
|
53
|
+
}): boolean;
|
|
54
|
+
/** 获取投影值
|
|
55
|
+
* @param points
|
|
56
|
+
* @param start
|
|
57
|
+
* @param end
|
|
58
|
+
* @returns
|
|
59
|
+
*/
|
|
60
|
+
static projectPoint(points: Point3D[], start: Vector3, end: Vector3): number[];
|
|
61
|
+
}
|
|
62
|
+
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>>;
|
package/src/utils/index.d.ts
CHANGED