@ifc-lite/drawing-2d 1.19.0 → 1.21.1
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/dist/dxf/aci-colors.d.ts +13 -0
- package/dist/dxf/aci-colors.d.ts.map +1 -1
- package/dist/dxf/aci-colors.js +68 -0
- package/dist/dxf/aci-colors.js.map +1 -1
- package/dist/dxf/writer.d.ts +111 -0
- package/dist/dxf/writer.d.ts.map +1 -0
- package/dist/dxf/writer.js +338 -0
- package/dist/dxf/writer.js.map +1 -0
- package/dist/dxf-exporter.d.ts +66 -0
- package/dist/dxf-exporter.d.ts.map +1 -0
- package/dist/dxf-exporter.js +137 -0
- package/dist/dxf-exporter.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/pdf-scale.d.ts +96 -0
- package/dist/pdf-scale.d.ts.map +1 -0
- package/dist/pdf-scale.js +123 -0
- package/dist/pdf-scale.js.map +1 -0
- package/dist/polygon-builder.d.ts +10 -2
- package/dist/polygon-builder.d.ts.map +1 -1
- package/dist/polygon-builder.js +47 -21
- package/dist/polygon-builder.js.map +1 -1
- package/dist/svg-exporter.d.ts +11 -0
- package/dist/svg-exporter.d.ts.map +1 -1
- package/dist/svg-exporter.js +57 -8
- package/dist/svg-exporter.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dxf-exporter.d.ts","sourceRoot":"","sources":["../src/dxf-exporter.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAA6C,OAAO,EAAE,MAAM,YAAY,CAAC;AAGhG,OAAO,EAAyB,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG5F,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iIAAiI;IACjI,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qFAAqF;IACrF,SAAS,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACjC;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC;IAC9C;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,WAAW,CAAC;IACtB,yDAAyD;IACzD,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,8EAA8E;IAC9E,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAaD,yEAAyE;AACzE,qBAAa,WAAW;IACtB,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM;IA2ClE,OAAO,CAAC,SAAS;IAwBjB,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,aAAa;CA+DtB;AAED,mDAAmD;AACnD,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAElF"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { getLineStyle, getHatchPattern } from './styles.js';
|
|
5
|
+
import { applyDxfPlacement } from './dxf/convert.js';
|
|
6
|
+
import { DEFAULT_DXF_PLACEMENT } from './dxf/types.js';
|
|
7
|
+
import { DxfWriter } from './dxf/writer.js';
|
|
8
|
+
const CATEGORY_LAYER = {
|
|
9
|
+
cut: 'IFC-CUT',
|
|
10
|
+
projection: 'IFC-PROJECTION',
|
|
11
|
+
hidden: 'IFC-HIDDEN',
|
|
12
|
+
silhouette: 'IFC-SILHOUETTE',
|
|
13
|
+
crease: 'IFC-CREASE',
|
|
14
|
+
boundary: 'IFC-BOUNDARY',
|
|
15
|
+
annotation: 'IFC-ANNOTATION',
|
|
16
|
+
};
|
|
17
|
+
const FILL_LAYER = 'IFC-FILL';
|
|
18
|
+
/** Builds a DXF document from a `Drawing2D`, mirroring `SVGExporter`. */
|
|
19
|
+
export class DXFExporter {
|
|
20
|
+
export(drawing, options = {}) {
|
|
21
|
+
const { showHiddenLines = true, showHatching = true, underlays = [], coordinateTransform = (p) => p, metadataComment, } = options;
|
|
22
|
+
const writer = new DxfWriter({ headerComment: metadataComment });
|
|
23
|
+
const map = (p) => coordinateTransform(p);
|
|
24
|
+
// Layer per line category, DASHED linetype for the hidden layer only —
|
|
25
|
+
// matches how SVGExporter groups its <g> layers. Lines whose
|
|
26
|
+
// `visibility` is 'hidden' are routed onto the hidden layer regardless
|
|
27
|
+
// of category — see writeLine.
|
|
28
|
+
const categoryLayers = new Map();
|
|
29
|
+
for (const [category, name] of Object.entries(CATEGORY_LAYER)) {
|
|
30
|
+
const linetype = category === 'hidden' ? 'DASHED' : 'CONTINUOUS';
|
|
31
|
+
categoryLayers.set(category, writer.layer(name, getLineStyle(category).color, linetype));
|
|
32
|
+
}
|
|
33
|
+
const fillLayer = writer.layer(FILL_LAYER, '#666666');
|
|
34
|
+
// Hatching first (bottom-most, like SVGExporter's hatching layer).
|
|
35
|
+
if (showHatching) {
|
|
36
|
+
for (const polygon of drawing.cutPolygons) {
|
|
37
|
+
this.writePolygonBoundary(writer, polygon, fillLayer, map);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Underlays beneath the drawing lines (SVGExporter renders them first too).
|
|
41
|
+
for (const underlayOptions of underlays) {
|
|
42
|
+
this.writeUnderlay(writer, underlayOptions, map);
|
|
43
|
+
}
|
|
44
|
+
for (const line of drawing.lines) {
|
|
45
|
+
if (!showHiddenLines && line.visibility === 'hidden')
|
|
46
|
+
continue;
|
|
47
|
+
this.writeLine(writer, line, categoryLayers, map);
|
|
48
|
+
}
|
|
49
|
+
return writer.toString();
|
|
50
|
+
}
|
|
51
|
+
writeLine(writer, line, categoryLayers, map) {
|
|
52
|
+
// Visibility wins over category (PR #1871 review, P2): hidden-line
|
|
53
|
+
// removal / construction projection can mark e.g. a `projection` line
|
|
54
|
+
// `visibility: 'hidden'`. Mapping by category alone would land it on the
|
|
55
|
+
// CONTINUOUS `IFC-PROJECTION` layer and occluded/overhead edges would
|
|
56
|
+
// read as solid in CAD. Mirror `exportToSVG`'s hidden-lines grouping
|
|
57
|
+
// (and the viewer canvas's dashed override): every `visibility ===
|
|
58
|
+
// 'hidden'` line joins the DASHED `IFC-HIDDEN` layer with the hidden
|
|
59
|
+
// line style. No new layer/LTYPE names — `IFC-HIDDEN` and `DASHED` are
|
|
60
|
+
// already declared in TABLES, so the output stays R12-legal.
|
|
61
|
+
const styleCategory = line.visibility === 'hidden' ? 'hidden' : line.category;
|
|
62
|
+
const layer = categoryLayers.get(styleCategory);
|
|
63
|
+
if (!layer)
|
|
64
|
+
return;
|
|
65
|
+
const style = getLineStyle(styleCategory, line.ifcType);
|
|
66
|
+
const start = map(line.line.start);
|
|
67
|
+
const end = map(line.line.end);
|
|
68
|
+
writer.addLine(start, end, layer, style.color);
|
|
69
|
+
}
|
|
70
|
+
writePolygonBoundary(writer, polygon, layer, map) {
|
|
71
|
+
const pattern = getHatchPattern(polygon.ifcType);
|
|
72
|
+
if (pattern.type === 'none')
|
|
73
|
+
return;
|
|
74
|
+
const outer = polygon.polygon.outer.map(map);
|
|
75
|
+
writer.addPolyline(outer, layer, true, pattern.strokeColor);
|
|
76
|
+
for (const hole of polygon.polygon.holes) {
|
|
77
|
+
writer.addPolyline(hole.map(map), layer, true, pattern.strokeColor);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
writeUnderlay(writer, options, map) {
|
|
81
|
+
const { underlay, placement = DEFAULT_DXF_PLACEMENT, layerVisibility = {} } = options;
|
|
82
|
+
// Underlay points are already in world plan coordinates (metres, +Y
|
|
83
|
+
// north) — the same convention DXF itself uses, so (unlike SVG, which
|
|
84
|
+
// is +Y down) no sign flip is needed before the placement transform.
|
|
85
|
+
const mapPoint = (p) => map(applyDxfPlacement(p, placement));
|
|
86
|
+
const underlayPrefix = `DXF_${underlay.name}`;
|
|
87
|
+
for (const dxfLayer of underlay.layers) {
|
|
88
|
+
const visible = layerVisibility[dxfLayer.name] ?? dxfLayer.visible;
|
|
89
|
+
if (!visible)
|
|
90
|
+
continue;
|
|
91
|
+
const layer = writer.layer(`${underlayPrefix}_${dxfLayer.name}`, dxfLayer.color);
|
|
92
|
+
for (const fill of dxfLayer.fills) {
|
|
93
|
+
const outer = fill.polygon.outer.map(mapPoint);
|
|
94
|
+
writer.addPolyline(outer, layer, true, fill.color);
|
|
95
|
+
for (const hole of fill.polygon.holes) {
|
|
96
|
+
writer.addPolyline(hole.map(mapPoint), layer, true, fill.color);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const path of dxfLayer.paths) {
|
|
100
|
+
if (path.points.length < 2)
|
|
101
|
+
continue;
|
|
102
|
+
writer.addPolyline(path.points.map(mapPoint), layer, path.closed, path.color);
|
|
103
|
+
}
|
|
104
|
+
for (const text of dxfLayer.texts) {
|
|
105
|
+
if (!text.text.trim())
|
|
106
|
+
continue;
|
|
107
|
+
const anchor = mapPoint(text.position);
|
|
108
|
+
const tip = mapPoint({ x: text.position.x + text.dirX, y: text.position.y + text.dirY });
|
|
109
|
+
const angle = (Math.atan2(tip.y - anchor.y, tip.x - anchor.x) * 180) / Math.PI;
|
|
110
|
+
// Placement scales the geometry through mapPoint; the glyph height
|
|
111
|
+
// must follow or labels detach from their linework (same rule as
|
|
112
|
+
// SVGExporter's underlay text).
|
|
113
|
+
const height = text.height * placement.scale;
|
|
114
|
+
const lines = text.text.split('\n');
|
|
115
|
+
for (let i = 0; i < lines.length; i++) {
|
|
116
|
+
// Stack subsequent lines by offsetting in LOCAL space, then map:
|
|
117
|
+
// the stacking direction must rotate with the text when
|
|
118
|
+
// placement.rotationDeg is non-zero or coordinateTransform itself
|
|
119
|
+
// rotates (georeferenced rotated grid) — exactly like
|
|
120
|
+
// SVGExporter's rotate() wrapper around its tspan stack. An
|
|
121
|
+
// output-space `anchor.y - offset` would shift lines straight
|
|
122
|
+
// down regardless of the text's actual direction (PR #1871
|
|
123
|
+
// review).
|
|
124
|
+
const lineAnchor = i === 0
|
|
125
|
+
? anchor
|
|
126
|
+
: mapPoint({ x: text.position.x, y: text.position.y - i * text.height * 1.3 });
|
|
127
|
+
writer.addText(lineAnchor, lines[i], height, layer, { rotationDeg: angle, hAlign: text.align, vAlign: text.valign, colorOverride: text.color });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** Export a `Drawing2D` to an ASCII DXF string. */
|
|
134
|
+
export function exportToDXF(drawing, options) {
|
|
135
|
+
return new DXFExporter().export(drawing, options);
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=dxf-exporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dxf-exporter.js","sourceRoot":"","sources":["../src/dxf-exporter.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -32,10 +32,14 @@ export { HatchGenerator } from './hatch-generator.js';
|
|
|
32
32
|
export type { HatchLine, HatchResult, CustomHatchSettings } from './hatch-generator.js';
|
|
33
33
|
export { SVGExporter, exportToSVG } from './svg-exporter.js';
|
|
34
34
|
export type { SVGExportOptions } from './svg-exporter.js';
|
|
35
|
+
export { computePdfScaleLayout, worldPointToPdfMm, worldLengthToPdfMm, flipBounds2D, formatScaleFactorLabel, } from './pdf-scale.js';
|
|
36
|
+
export type { PdfScaleTransform, PdfPage, PdfScaleLayout, AxisFlip } from './pdf-scale.js';
|
|
37
|
+
export { DXFExporter, exportToDXF } from './dxf-exporter.js';
|
|
38
|
+
export type { DXFExportOptions, DXFUnderlayOptions } from './dxf-exporter.js';
|
|
35
39
|
export { GPUSectionCutter, isGPUComputeAvailable } from './gpu-section-cutter.js';
|
|
36
40
|
export { Drawing2DGenerator, createSectionConfig, generateFloorPlan, generateSection, } from './drawing-generator.js';
|
|
37
41
|
export type { GeneratorOptions, GeneratorProgress } from './drawing-generator.js';
|
|
38
|
-
export { EPSILON, vec3, vec3Add, vec3Sub, vec3Scale, vec3Dot, vec3Cross, vec3Length, vec3Normalize, vec3Lerp, vec3Equals, vec3Distance, point2D, point2DAdd, point2DSub, point2DScale, point2DDot, point2DLength, point2DDistance, point2DLerp, point2DEquals, point2DNormalize, point2DCross, lineLength, lineMidpoint, lineDirection, linesCollinear, projectPointOnLine, boundsEmpty, boundsExtendPoint, boundsExtendLine, boundsCenter, boundsSize, boundsValid, signedDistanceToPlane, getAxisNormal, getProjectionAxes, projectTo2D, polygonSignedArea, isCounterClockwise, reversePolygon, ensureCCW, ensureCW, } from './math.js';
|
|
42
|
+
export { EPSILON, vec3, vec3Add, vec3Sub, vec3Scale, vec3Dot, vec3Cross, vec3Length, vec3Normalize, vec3Lerp, vec3Equals, vec3Distance, point2D, point2DAdd, point2DSub, point2DScale, point2DDot, point2DLength, point2DDistance, point2DLerp, point2DEquals, point2DNormalize, point2DCross, lineLength, lineMidpoint, lineDirection, linesCollinear, projectPointOnLine, boundsEmpty, boundsExtendPoint, boundsExtendLine, boundsCenter, boundsSize, boundsValid, signedDistanceToPlane, getAxisNormal, getProjectionAxes, projectTo2D, projectTo2DBasis, polygonSignedArea, isCounterClockwise, reversePolygon, ensureCCW, ensureCW, } from './math.js';
|
|
39
43
|
export { OpeningRelationshipBuilder, OpeningFilter, buildOpeningRelationships, getOpeningsForHost, getFillingElement, isOpeningElement, isDoorOrWindow, } from './openings/index.js';
|
|
40
44
|
export type { OpeningInfo, OpeningRelationships, VoidRelationship, FillRelationship, DoorOperationType, WindowPartitioningType, EntityMetadata, DrawingContext, } from './types.js';
|
|
41
45
|
export { DoorSymbolGenerator, WindowSymbolGenerator, SymbolRenderer, generateDoorSymbol, generateWindowSymbol, generateStairArrow, } from './symbols/index.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAMH,YAAY,EAEV,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,MAAM,EACN,UAAU,EACV,SAAS,EACT,QAAQ,EAGR,WAAW,EACX,kBAAkB,EAClB,aAAa,EAGb,YAAY,EACZ,eAAe,EAGf,WAAW,EACX,cAAc,EAGd,UAAU,EACV,aAAa,EACb,gBAAgB,EAGhB,SAAS,EAGT,QAAQ,EAGR,YAAY,EAGZ,aAAa,EAGb,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMnF,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACxE,YAAY,EAAE,6BAA6B,EAAE,MAAM,qBAAqB,CAAC;AAMzE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAMtF,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAM1D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAMzD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAGlF,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAM7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAM/F,OAAO,EAEL,cAAc,EACd,eAAe,EAGf,WAAW,EACX,iBAAiB,EACjB,YAAY,EAGZ,aAAa,EACb,mBAAmB,EAGnB,WAAW,GACZ,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,SAAS,GACV,MAAM,aAAa,CAAC;AAMrB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAMxF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -53,6 +53,18 @@ export { HatchGenerator } from './hatch-generator.js';
|
|
|
53
53
|
// SVG EXPORT
|
|
54
54
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
55
55
|
export { SVGExporter, exportToSVG } from './svg-exporter.js';
|
|
56
|
+
// Scaled PDF export (issue #2042): pure scale/extent arithmetic only —
|
|
57
|
+
// the PDF is assembled by the viewer using jsPDF, which is not a
|
|
58
|
+
// dependency of this package.
|
|
59
|
+
export { computePdfScaleLayout, worldPointToPdfMm, worldLengthToPdfMm, flipBounds2D, formatScaleFactorLabel, } from './pdf-scale.js';
|
|
60
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
61
|
+
// DXF EXPORT (issue #1861)
|
|
62
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
63
|
+
// Only the exporter facade is public. The low-level writer internals
|
|
64
|
+
// (DxfWriter, sanitizeDxfLayerName, cssToAci, the linetype/justification
|
|
65
|
+
// types) stay package-private: no external consumer exists, and an unused
|
|
66
|
+
// public export is permanent semver liability (PR #1871 review).
|
|
67
|
+
export { DXFExporter, exportToDXF } from './dxf-exporter.js';
|
|
56
68
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
57
69
|
// GPU ACCELERATION
|
|
58
70
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -76,7 +88,7 @@ lineLength, lineMidpoint, lineDirection, linesCollinear, projectPointOnLine,
|
|
|
76
88
|
// Bounds operations
|
|
77
89
|
boundsEmpty, boundsExtendPoint, boundsExtendLine, boundsCenter, boundsSize, boundsValid,
|
|
78
90
|
// Plane operations
|
|
79
|
-
signedDistanceToPlane, getAxisNormal, getProjectionAxes, projectTo2D,
|
|
91
|
+
signedDistanceToPlane, getAxisNormal, getProjectionAxes, projectTo2D, projectTo2DBasis,
|
|
80
92
|
// Polygon operations
|
|
81
93
|
polygonSignedArea, isCounterClockwise, reversePolygon, ensureCCW, ensureCW, } from './math.js';
|
|
82
94
|
// ═══════════════════════════════════════════════════════════════════════════
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure scale/extent arithmetic for exporting a section drawing to a
|
|
3
|
+
* dimensionally accurate ("to scale") PDF page (issue #2042).
|
|
4
|
+
*
|
|
5
|
+
* The drawing model (`Drawing2D.bounds`) carries its own real-world extent
|
|
6
|
+
* in metres (see `types.ts`). To print "1:100" correctly, 1 metre in the
|
|
7
|
+
* world must become exactly 10mm on paper — no rounding, no silent re-fit.
|
|
8
|
+
*
|
|
9
|
+
* This module deliberately sizes the PAGE to the drawing extent at the
|
|
10
|
+
* chosen scale (plus a margin), rather than fitting the drawing into a
|
|
11
|
+
* fixed named paper size (A3, A4, ...). That avoids the failure mode in
|
|
12
|
+
* `sheet/sheet-types.ts`'s `calculateDrawingTransform`, which silently
|
|
13
|
+
* shrinks the drawing (`fitScale = min(scaleX, scaleY, 1)`) when it does
|
|
14
|
+
* not fit the configured viewport at the nominal scale — correct for an
|
|
15
|
+
* on-screen preview, but wrong for a PDF whose entire purpose is to be
|
|
16
|
+
* measured at the scale the user explicitly picked.
|
|
17
|
+
*/
|
|
18
|
+
import type { Bounds2D, Point2D } from './types.js';
|
|
19
|
+
/** World-to-paper transform: maps a world-space point (metres) to a point
|
|
20
|
+
* on the PDF page (millimetres), with Y flipped (paper Y grows downward,
|
|
21
|
+
* world Y grows "up"/"north" depending on section axis). */
|
|
22
|
+
export interface PdfScaleTransform {
|
|
23
|
+
/** mm on paper per metre in the world. E.g. 1:100 -> 10; 1:50 -> 20. */
|
|
24
|
+
worldToMm: number;
|
|
25
|
+
offsetXMm: number;
|
|
26
|
+
offsetYMm: number;
|
|
27
|
+
}
|
|
28
|
+
export interface PdfPage {
|
|
29
|
+
widthMm: number;
|
|
30
|
+
heightMm: number;
|
|
31
|
+
}
|
|
32
|
+
export interface PdfScaleLayout {
|
|
33
|
+
transform: PdfScaleTransform;
|
|
34
|
+
page: PdfPage;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Compute the page size and world->paper-mm transform for exporting
|
|
38
|
+
* `bounds` (metres) at an exact `scaleFactor` (the "100" in "1:100"),
|
|
39
|
+
* with `marginMm` of blank paper around the drawing on every side.
|
|
40
|
+
*
|
|
41
|
+
* Throws on a non-finite/non-positive scale factor, a non-finite/negative
|
|
42
|
+
* margin, or degenerate bounds — silently producing a mis-scaled or
|
|
43
|
+
* zero-size PDF would look plausible and be wrong, which is the failure
|
|
44
|
+
* that matters most for a document someone measures from.
|
|
45
|
+
*/
|
|
46
|
+
export declare function computePdfScaleLayout(bounds: Bounds2D, scaleFactor: number, marginMm?: number): PdfScaleLayout;
|
|
47
|
+
/** Which axes a caller intends to negate before mapping a point (matches the
|
|
48
|
+
* "as displayed" flips `useDrawingExport.ts` applies per section axis —
|
|
49
|
+
* `front`/`side` mirror the world X and/or Y before mapping to paper). */
|
|
50
|
+
export interface AxisFlip {
|
|
51
|
+
flipX: boolean;
|
|
52
|
+
flipY: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Flip `bounds` around world zero on the requested axes — i.e. the bounds
|
|
56
|
+
* of the geometry *as it will actually be drawn* after a caller negates
|
|
57
|
+
* point coordinates per `flip`.
|
|
58
|
+
*
|
|
59
|
+
* This exists because `computePdfScaleLayout`'s offsets are derived from
|
|
60
|
+
* `bounds.min`, which only lands the drawing on the page if the points
|
|
61
|
+
* handed to `worldPointToPdfMm` are drawn in that same, un-negated frame.
|
|
62
|
+
* A caller that negates X/Y when mapping points (as `front`/`side` section
|
|
63
|
+
* exports do) but derives the layout from the *original* bounds gets
|
|
64
|
+
* offsets computed for a frame the geometry was never actually drawn in —
|
|
65
|
+
* correct only when `bounds` happens to be symmetric about zero, and
|
|
66
|
+
* silently off-page for the common case of a model at positive world
|
|
67
|
+
* coordinates. Always deriving the layout from `flipBounds2D(bounds, flip)`
|
|
68
|
+
* keeps the two in sync regardless of where the drawing sits in world space.
|
|
69
|
+
*/
|
|
70
|
+
export declare function flipBounds2D(bounds: Bounds2D, flip: AxisFlip): Bounds2D;
|
|
71
|
+
/**
|
|
72
|
+
* Format a "1:N" scale factor for a filename/label, rounding float noise
|
|
73
|
+
* away rather than truncating it: 2 decimal places, trailing zeros (and a
|
|
74
|
+
* bare trailing dot) stripped, so `99.5` reads "99.5" and `100.0000001`
|
|
75
|
+
* reads "100" rather than either carrying noise or rounding to a materially
|
|
76
|
+
* different scale.
|
|
77
|
+
*
|
|
78
|
+
* Extracted from `SVGExporter`'s title-block scale label (PR #2131, "stop
|
|
79
|
+
* the title block from lying about a clamped scale") so every place that
|
|
80
|
+
* turns a scale factor into a human-readable "N" — the title block, and the
|
|
81
|
+
* PDF export filename below — agrees on one rounding rule instead of each
|
|
82
|
+
* re-deriving (and potentially disagreeing on) its own.
|
|
83
|
+
*/
|
|
84
|
+
export declare function formatScaleFactorLabel(factor: number): string;
|
|
85
|
+
/** Map one world-space point (metres) to paper space (mm) via `transform`. */
|
|
86
|
+
export declare function worldPointToPdfMm(point: Point2D, transform: PdfScaleTransform): Point2D;
|
|
87
|
+
/** Convert a line weight / length already expressed in mm-on-paper terms
|
|
88
|
+
* is a no-op; this converts a MODEL-space length (metres) to mm on paper
|
|
89
|
+
* at the given transform's scale — e.g. for stroke widths authored in
|
|
90
|
+
* world units. Most line/hatch weights in this codebase are already
|
|
91
|
+
* mm-on-paper constants, so this is only needed where a length is
|
|
92
|
+
* genuinely world-space (kept separate from `worldPointToPdfMm` so
|
|
93
|
+
* callers can't accidentally apply the Y-flip/offset to a scalar).
|
|
94
|
+
*/
|
|
95
|
+
export declare function worldLengthToPdfMm(lengthMeters: number, transform: PdfScaleTransform): number;
|
|
96
|
+
//# sourceMappingURL=pdf-scale.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-scale.d.ts","sourceRoot":"","sources":["../src/pdf-scale.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEpD;;6DAE6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,QAAQ,EAChB,WAAW,EAAE,MAAM,EACnB,QAAQ,SAAK,GACZ,cAAc,CA6DhB;AAED;;2EAE2E;AAC3E,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAWvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,iBAAiB,GAAG,OAAO,CAKvF;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB,GAAG,MAAM,CAE7F"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Compute the page size and world->paper-mm transform for exporting
|
|
6
|
+
* `bounds` (metres) at an exact `scaleFactor` (the "100" in "1:100"),
|
|
7
|
+
* with `marginMm` of blank paper around the drawing on every side.
|
|
8
|
+
*
|
|
9
|
+
* Throws on a non-finite/non-positive scale factor, a non-finite/negative
|
|
10
|
+
* margin, or degenerate bounds — silently producing a mis-scaled or
|
|
11
|
+
* zero-size PDF would look plausible and be wrong, which is the failure
|
|
12
|
+
* that matters most for a document someone measures from.
|
|
13
|
+
*/
|
|
14
|
+
export function computePdfScaleLayout(bounds, scaleFactor, marginMm = 10) {
|
|
15
|
+
if (!Number.isFinite(scaleFactor) || scaleFactor <= 0) {
|
|
16
|
+
throw new Error(`Invalid PDF export scale factor: ${scaleFactor}. Expected a positive finite number (the "N" in "1:N").`);
|
|
17
|
+
}
|
|
18
|
+
if (!Number.isFinite(marginMm) || marginMm < 0) {
|
|
19
|
+
throw new Error(`Invalid PDF export margin: ${marginMm}mm. Expected a non-negative finite number.`);
|
|
20
|
+
}
|
|
21
|
+
const { min, max } = bounds;
|
|
22
|
+
if (!Number.isFinite(min.x) || !Number.isFinite(min.y) ||
|
|
23
|
+
!Number.isFinite(max.x) || !Number.isFinite(max.y)) {
|
|
24
|
+
throw new Error('Invalid drawing bounds for PDF export: bounds must be finite.');
|
|
25
|
+
}
|
|
26
|
+
const width = max.x - min.x;
|
|
27
|
+
const height = max.y - min.y;
|
|
28
|
+
if (width < 0 || height < 0) {
|
|
29
|
+
throw new Error('Invalid drawing bounds for PDF export: max must be >= min on both axes.');
|
|
30
|
+
}
|
|
31
|
+
// mm on paper per metre in the world. 1:100 means 1m -> 10mm on paper.
|
|
32
|
+
const worldToMm = 1000 / scaleFactor;
|
|
33
|
+
const drawingWidthMm = width * worldToMm;
|
|
34
|
+
const drawingHeightMm = height * worldToMm;
|
|
35
|
+
const page = {
|
|
36
|
+
widthMm: drawingWidthMm + marginMm * 2,
|
|
37
|
+
heightMm: drawingHeightMm + marginMm * 2,
|
|
38
|
+
};
|
|
39
|
+
// x: bounds.min.x -> marginMm (left edge of drawing area)
|
|
40
|
+
const offsetXMm = marginMm - min.x * worldToMm;
|
|
41
|
+
// y: flipped. bounds.min.y (world "bottom") -> page.heightMm - marginMm
|
|
42
|
+
// (paper bottom edge of drawing area); bounds.max.y -> marginMm (top).
|
|
43
|
+
const offsetYMm = page.heightMm - marginMm + min.y * worldToMm;
|
|
44
|
+
// Inputs were validated finite above, but finite inputs can still
|
|
45
|
+
// multiply/divide out to a non-finite output — e.g. a vanishingly small
|
|
46
|
+
// (but positive, so it passes the scaleFactor check) scale factor makes
|
|
47
|
+
// `worldToMm` overflow to Infinity, or enormous bounds combined with a
|
|
48
|
+
// large worldToMm overflow `page.widthMm`/`heightMm`. These are exactly
|
|
49
|
+
// the numbers that reach jsPDF's page-size/coordinate arguments, so catch
|
|
50
|
+
// it here rather than let jsPDF fail (or silently emit a garbage page).
|
|
51
|
+
if (!Number.isFinite(worldToMm) ||
|
|
52
|
+
!Number.isFinite(page.widthMm) || !Number.isFinite(page.heightMm) ||
|
|
53
|
+
!Number.isFinite(offsetXMm) || !Number.isFinite(offsetYMm)) {
|
|
54
|
+
throw new Error(`PDF export layout produced non-finite dimensions (worldToMm=${worldToMm}, ` +
|
|
55
|
+
`page=${page.widthMm}x${page.heightMm}mm) from scaleFactor=${scaleFactor} and bounds ` +
|
|
56
|
+
`${width}x${height}. Choose a less extreme scale.`);
|
|
57
|
+
}
|
|
58
|
+
return { transform: { worldToMm, offsetXMm, offsetYMm }, page };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Flip `bounds` around world zero on the requested axes — i.e. the bounds
|
|
62
|
+
* of the geometry *as it will actually be drawn* after a caller negates
|
|
63
|
+
* point coordinates per `flip`.
|
|
64
|
+
*
|
|
65
|
+
* This exists because `computePdfScaleLayout`'s offsets are derived from
|
|
66
|
+
* `bounds.min`, which only lands the drawing on the page if the points
|
|
67
|
+
* handed to `worldPointToPdfMm` are drawn in that same, un-negated frame.
|
|
68
|
+
* A caller that negates X/Y when mapping points (as `front`/`side` section
|
|
69
|
+
* exports do) but derives the layout from the *original* bounds gets
|
|
70
|
+
* offsets computed for a frame the geometry was never actually drawn in —
|
|
71
|
+
* correct only when `bounds` happens to be symmetric about zero, and
|
|
72
|
+
* silently off-page for the common case of a model at positive world
|
|
73
|
+
* coordinates. Always deriving the layout from `flipBounds2D(bounds, flip)`
|
|
74
|
+
* keeps the two in sync regardless of where the drawing sits in world space.
|
|
75
|
+
*/
|
|
76
|
+
export function flipBounds2D(bounds, flip) {
|
|
77
|
+
return {
|
|
78
|
+
min: {
|
|
79
|
+
x: flip.flipX ? -bounds.max.x : bounds.min.x,
|
|
80
|
+
y: flip.flipY ? -bounds.max.y : bounds.min.y,
|
|
81
|
+
},
|
|
82
|
+
max: {
|
|
83
|
+
x: flip.flipX ? -bounds.min.x : bounds.max.x,
|
|
84
|
+
y: flip.flipY ? -bounds.min.y : bounds.max.y,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Format a "1:N" scale factor for a filename/label, rounding float noise
|
|
90
|
+
* away rather than truncating it: 2 decimal places, trailing zeros (and a
|
|
91
|
+
* bare trailing dot) stripped, so `99.5` reads "99.5" and `100.0000001`
|
|
92
|
+
* reads "100" rather than either carrying noise or rounding to a materially
|
|
93
|
+
* different scale.
|
|
94
|
+
*
|
|
95
|
+
* Extracted from `SVGExporter`'s title-block scale label (PR #2131, "stop
|
|
96
|
+
* the title block from lying about a clamped scale") so every place that
|
|
97
|
+
* turns a scale factor into a human-readable "N" — the title block, and the
|
|
98
|
+
* PDF export filename below — agrees on one rounding rule instead of each
|
|
99
|
+
* re-deriving (and potentially disagreeing on) its own.
|
|
100
|
+
*/
|
|
101
|
+
export function formatScaleFactorLabel(factor) {
|
|
102
|
+
const rounded = Math.round(factor * 100) / 100;
|
|
103
|
+
return rounded % 1 === 0 ? rounded.toFixed(0) : rounded.toFixed(2).replace(/0+$/, '').replace(/\.$/, '');
|
|
104
|
+
}
|
|
105
|
+
/** Map one world-space point (metres) to paper space (mm) via `transform`. */
|
|
106
|
+
export function worldPointToPdfMm(point, transform) {
|
|
107
|
+
return {
|
|
108
|
+
x: point.x * transform.worldToMm + transform.offsetXMm,
|
|
109
|
+
y: -point.y * transform.worldToMm + transform.offsetYMm,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/** Convert a line weight / length already expressed in mm-on-paper terms
|
|
113
|
+
* is a no-op; this converts a MODEL-space length (metres) to mm on paper
|
|
114
|
+
* at the given transform's scale — e.g. for stroke widths authored in
|
|
115
|
+
* world units. Most line/hatch weights in this codebase are already
|
|
116
|
+
* mm-on-paper constants, so this is only needed where a length is
|
|
117
|
+
* genuinely world-space (kept separate from `worldPointToPdfMm` so
|
|
118
|
+
* callers can't accidentally apply the Y-flip/offset to a scalar).
|
|
119
|
+
*/
|
|
120
|
+
export function worldLengthToPdfMm(lengthMeters, transform) {
|
|
121
|
+
return lengthMeters * transform.worldToMm;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=pdf-scale.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-scale.js","sourceRoot":"","sources":["../src/pdf-scale.ts"],"names":[],"mappings":"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"}
|
|
@@ -122,8 +122,16 @@ export declare class PolygonBuilder {
|
|
|
122
122
|
*/
|
|
123
123
|
private findConnectingSegment;
|
|
124
124
|
/**
|
|
125
|
-
* Classify loops as outer boundaries or holes
|
|
126
|
-
*
|
|
125
|
+
* Classify loops as outer boundaries or holes.
|
|
126
|
+
*
|
|
127
|
+
* Nesting can go more than one level deep — an island (e.g. a mullion
|
|
128
|
+
* cross-section, or a column stub) fully inside a hole (a window opening,
|
|
129
|
+
* a shaft) must become its OWN solid outer polygon, not a second hole of
|
|
130
|
+
* the outermost boundary. Each loop's classification is therefore relative
|
|
131
|
+
* to its NEAREST containing ancestor (the smallest-area loop that still
|
|
132
|
+
* contains it), not the top-level outer: walk the ancestor chain to get a
|
|
133
|
+
* nesting depth, then even depth (0, 2, 4, …) = solid outer boundary, odd
|
|
134
|
+
* depth (1, 3, …) = hole of its immediate (even-depth) parent.
|
|
127
135
|
*/
|
|
128
136
|
private classifyLoops;
|
|
129
137
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"polygon-builder.d.ts","sourceRoot":"","sources":["../src/polygon-builder.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAa,UAAU,EAAE,cAAc,EAAa,MAAM,YAAY,CAAC;AA8B5F,qBAAa,cAAc;IACzB,oCAAoC;IACpC,OAAO,CAAC,SAAS,CAAS;gBAEd,SAAS,GAAE,MAAe;IAItC;;;OAGG;IACH,aAAa,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE;IAuBvD;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE;IAsB3D;;;;;;;;;OASG;IACH,OAAO,CAAC,mBAAmB;IA0B3B;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAqC/B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,UAAU;IA8BlB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,cAAc;IAKtB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,oBAAoB;IAwF5B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IA4BrB;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IA8CxB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,eAAe;IAuDvB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA2B7B
|
|
1
|
+
{"version":3,"file":"polygon-builder.d.ts","sourceRoot":"","sources":["../src/polygon-builder.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAa,UAAU,EAAE,cAAc,EAAa,MAAM,YAAY,CAAC;AA8B5F,qBAAa,cAAc;IACzB,oCAAoC;IACpC,OAAO,CAAC,SAAS,CAAS;gBAEd,SAAS,GAAE,MAAe;IAItC;;;OAGG;IACH,aAAa,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE;IAuBvD;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE;IAsB3D;;;;;;;;;OASG;IACH,OAAO,CAAC,mBAAmB;IA0B3B;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAqC/B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,UAAU;IA8BlB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,cAAc;IAKtB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,oBAAoB;IAwF5B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IA4BrB;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IA8CxB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,eAAe;IAuDvB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA2B7B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IAyDrB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;OAEG;IACH,OAAO,CAAC,cAAc;CAkBvB;AAMD;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,SAAS,GAAE,MAAc,GAAG,OAAO,EAAE,CAiBvF;AAWD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,EAAE,GAChB;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,CAiBhC"}
|
package/dist/polygon-builder.js
CHANGED
|
@@ -476,36 +476,62 @@ export class PolygonBuilder {
|
|
|
476
476
|
return bestIdx;
|
|
477
477
|
}
|
|
478
478
|
/**
|
|
479
|
-
* Classify loops as outer boundaries or holes
|
|
480
|
-
*
|
|
479
|
+
* Classify loops as outer boundaries or holes.
|
|
480
|
+
*
|
|
481
|
+
* Nesting can go more than one level deep — an island (e.g. a mullion
|
|
482
|
+
* cross-section, or a column stub) fully inside a hole (a window opening,
|
|
483
|
+
* a shaft) must become its OWN solid outer polygon, not a second hole of
|
|
484
|
+
* the outermost boundary. Each loop's classification is therefore relative
|
|
485
|
+
* to its NEAREST containing ancestor (the smallest-area loop that still
|
|
486
|
+
* contains it), not the top-level outer: walk the ancestor chain to get a
|
|
487
|
+
* nesting depth, then even depth (0, 2, 4, …) = solid outer boundary, odd
|
|
488
|
+
* depth (1, 3, …) = hole of its immediate (even-depth) parent.
|
|
481
489
|
*/
|
|
482
490
|
classifyLoops(loops) {
|
|
483
491
|
if (loops.length === 0)
|
|
484
492
|
return [];
|
|
485
|
-
// Sort by absolute area (largest first)
|
|
493
|
+
// Sort by absolute area (largest first). The parent search below only
|
|
494
|
+
// considers EARLIER (larger-or-equal-area) loops as containers: a genuine
|
|
495
|
+
// container can never be smaller than what it contains, and restricting
|
|
496
|
+
// parents to earlier indices keeps the relation acyclic even when
|
|
497
|
+
// overlapping or duplicate loops make the single-point containment test
|
|
498
|
+
// mutual (A "contains" B's start point and B "contains" A's) — without it
|
|
499
|
+
// the ancestor walk below would spin forever on such input.
|
|
486
500
|
const sorted = [...loops].sort((a, b) => Math.abs(b.area) - Math.abs(a.area));
|
|
501
|
+
const n = sorted.length;
|
|
502
|
+
// Nearest containing ancestor: walking earlier loops from smallest area
|
|
503
|
+
// upward, the first one that contains this loop. -1 = top-level.
|
|
504
|
+
const parent = new Array(n).fill(-1);
|
|
505
|
+
for (let i = 0; i < n; i++) {
|
|
506
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
507
|
+
if (this.isLoopContainedIn(sorted[i].points, sorted[j].points)) {
|
|
508
|
+
parent[i] = j;
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
// Nesting depth = length of the ancestor chain to the top level.
|
|
514
|
+
const depth = new Array(n).fill(0);
|
|
515
|
+
for (let i = 0; i < n; i++) {
|
|
516
|
+
let d = 0;
|
|
517
|
+
let p = parent[i];
|
|
518
|
+
while (p !== -1) {
|
|
519
|
+
d++;
|
|
520
|
+
p = parent[p];
|
|
521
|
+
}
|
|
522
|
+
depth[i] = d;
|
|
523
|
+
}
|
|
487
524
|
const result = [];
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
const outer = sorted[i];
|
|
493
|
-
// Ensure outer boundary is CCW
|
|
494
|
-
const outerPoints = ensureCCW(outer.points);
|
|
495
|
-
// Find holes (smaller loops contained within this one)
|
|
525
|
+
for (let i = 0; i < n; i++) {
|
|
526
|
+
if (depth[i] % 2 !== 0)
|
|
527
|
+
continue; // holes are emitted as part of their parent below
|
|
528
|
+
const outerPoints = ensureCCW(sorted[i].points);
|
|
496
529
|
const holes = [];
|
|
497
|
-
for (let j =
|
|
498
|
-
if (
|
|
499
|
-
|
|
500
|
-
const inner = sorted[j];
|
|
501
|
-
// Check if inner is contained in outer
|
|
502
|
-
if (this.isLoopContainedIn(inner.points, outerPoints)) {
|
|
503
|
-
// Ensure hole is CW (opposite winding)
|
|
504
|
-
holes.push(ensureCW(inner.points));
|
|
505
|
-
assigned.add(j);
|
|
530
|
+
for (let j = 0; j < n; j++) {
|
|
531
|
+
if (parent[j] === i && depth[j] % 2 === 1) {
|
|
532
|
+
holes.push(ensureCW(sorted[j].points));
|
|
506
533
|
}
|
|
507
534
|
}
|
|
508
|
-
assigned.add(i);
|
|
509
535
|
result.push({ outer: outerPoints, holes });
|
|
510
536
|
}
|
|
511
537
|
return result;
|