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
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
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
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,
|
|
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((
|
|
10700
|
-
|
|
10701
|
-
|
|
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
|
|
11043
|
-
const [ipv, _iline, [istart, iend]] = vls[
|
|
11044
|
-
for (let 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(
|
|
11049
|
-
}
|
|
11050
|
-
}
|
|
11051
|
-
const
|
|
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.
|
|
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)
|
|
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
|
-
|
|
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
|
*/
|