@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.
@@ -1,4 +1,17 @@
1
1
  export declare function rgbIntToCss(rgb: number): string;
2
2
  /** Resolve an ACI colour number (1-255) to a CSS hex colour. */
3
3
  export declare function aciToCss(index: number): string;
4
+ /**
5
+ * Resolve a CSS colour (`#rrggbb`/`#rgb` hex, or an `rgb(...)`/`rgba(...)`
6
+ * function — the only formats {@link parseCssColor} handles; bare colour
7
+ * keywords are NOT parsed and take the ACI-7 fallback) to the nearest
8
+ * AutoCAD Color Index (1-255), for the DXF writer
9
+ * (issue #1861): DXF LAYER/entity colour is always an ACI, so an exported
10
+ * layer's CSS style colour needs a reverse lookup against the same table
11
+ * {@link aciToCss} reads forward. Ties break toward the lowest index (the
12
+ * classic 1-9 entries sort first, so a primary hue prefers its named ACI
13
+ * over a near-identical generated band entry). Unparseable input falls back
14
+ * to ACI 7 (black/white — the classic "on paper" colour).
15
+ */
16
+ export declare function cssToAci(css: string): number;
4
17
  //# sourceMappingURL=aci-colors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"aci-colors.d.ts","sourceRoot":"","sources":["../../src/dxf/aci-colors.ts"],"names":[],"mappings":"AA+DA,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI9C"}
1
+ {"version":3,"file":"aci-colors.d.ts","sourceRoot":"","sources":["../../src/dxf/aci-colors.ts"],"names":[],"mappings":"AA+DA,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAqB5C"}
@@ -71,4 +71,72 @@ export function aciToCss(index) {
71
71
  return '#000000';
72
72
  return rgbIntToCss(ACI_TABLE[i]);
73
73
  }
74
+ /**
75
+ * Resolve a CSS colour (`#rrggbb`/`#rgb` hex, or an `rgb(...)`/`rgba(...)`
76
+ * function — the only formats {@link parseCssColor} handles; bare colour
77
+ * keywords are NOT parsed and take the ACI-7 fallback) to the nearest
78
+ * AutoCAD Color Index (1-255), for the DXF writer
79
+ * (issue #1861): DXF LAYER/entity colour is always an ACI, so an exported
80
+ * layer's CSS style colour needs a reverse lookup against the same table
81
+ * {@link aciToCss} reads forward. Ties break toward the lowest index (the
82
+ * classic 1-9 entries sort first, so a primary hue prefers its named ACI
83
+ * over a near-identical generated band entry). Unparseable input falls back
84
+ * to ACI 7 (black/white — the classic "on paper" colour).
85
+ */
86
+ export function cssToAci(css) {
87
+ const rgb = parseCssColor(css);
88
+ if (rgb === null)
89
+ return 7;
90
+ const r = (rgb >> 16) & 0xff;
91
+ const g = (rgb >> 8) & 0xff;
92
+ const b = rgb & 0xff;
93
+ let best = 7;
94
+ let bestDist = Infinity;
95
+ for (let i = 1; i <= 255; i++) {
96
+ const c = ACI_TABLE[i];
97
+ const dr = r - ((c >> 16) & 0xff);
98
+ const dg = g - ((c >> 8) & 0xff);
99
+ const db = b - (c & 0xff);
100
+ const dist = dr * dr + dg * dg + db * db;
101
+ if (dist < bestDist) {
102
+ bestDist = dist;
103
+ best = i;
104
+ if (dist === 0)
105
+ break;
106
+ }
107
+ }
108
+ return best;
109
+ }
110
+ /**
111
+ * Parse `#rgb`, `#rrggbb`, `rgb(r,g,b)`, or `rgba(r,g,b,a)` into a 24-bit
112
+ * integer; null on failure. The function form is fully anchored (closing
113
+ * paren required, nothing before/after) and arity-checked — `rgb(...)` takes
114
+ * exactly three components and `rgba(...)` exactly four — so malformed
115
+ * strings (`rgb(1,2,3`, `rgb(1,2,3)junk`, `rgba(1,2,3)`) fall back to the
116
+ * caller's ACI-7 default instead of silently half-parsing (PR #1871 review).
117
+ */
118
+ function parseCssColor(css) {
119
+ const s = css.trim();
120
+ const hex3 = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(s);
121
+ if (hex3) {
122
+ const r = parseInt(hex3[1] + hex3[1], 16);
123
+ const g = parseInt(hex3[2] + hex3[2], 16);
124
+ const b = parseInt(hex3[3] + hex3[3], 16);
125
+ return (r << 16) | (g << 8) | b;
126
+ }
127
+ const hex6 = /^#([0-9a-f]{6})$/i.exec(s);
128
+ if (hex6)
129
+ return parseInt(hex6[1], 16);
130
+ const rgbFn = /^(rgb|rgba)\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d*\.?\d+)\s*)?\)$/i.exec(s);
131
+ if (rgbFn) {
132
+ const hasAlpha = rgbFn[5] !== undefined;
133
+ if ((rgbFn[1].length === 3) === hasAlpha)
134
+ return null; // rgb: 3 args; rgba: 4 args
135
+ const r = Math.min(255, parseInt(rgbFn[2], 10));
136
+ const g = Math.min(255, parseInt(rgbFn[3], 10));
137
+ const b = Math.min(255, parseInt(rgbFn[4], 10));
138
+ return (r << 16) | (g << 8) | b;
139
+ }
140
+ return null;
141
+ }
74
142
  //# sourceMappingURL=aci-colors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"aci-colors.js","sourceRoot":"","sources":["../../src/dxf/aci-colors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aci-colors.js","sourceRoot":"","sources":["../../src/dxf/aci-colors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,111 @@
1
+ import type { Point2D } from '../types.js';
2
+ /** Line style resolved to a DXF linetype. `dashed` maps to the `DASHED` LTYPE. */
3
+ export type DxfLinetype = 'CONTINUOUS' | 'DASHED';
4
+ /** Horizontal text justification (subset of DXF group 72). */
5
+ export type DxfTextHAlign = 'left' | 'center' | 'right';
6
+ /** Vertical text justification (subset of DXF group 73). */
7
+ export type DxfTextVAlign = 'baseline' | 'bottom' | 'middle' | 'top';
8
+ /**
9
+ * Sanitize a free-text label into a valid DXF R12 LAYER name: every
10
+ * character outside the R12 symbol-name set (`A-Z a-z 0-9 $ - _` — spaces,
11
+ * unicode and punctuation included) becomes `_`, runs of `_` produced by
12
+ * adjacent replacements are collapsed, the result is made non-empty, and
13
+ * truncated to the R12 31-character symbol-name limit.
14
+ *
15
+ * Distinct inputs can collide after this mapping (`Wände`/`Wønde` →
16
+ * `W_nde`); {@link DxfWriter.layer} disambiguates with a numeric suffix so
17
+ * two source layers never silently merge.
18
+ */
19
+ export declare function sanitizeDxfLayerName(name: string): string;
20
+ export interface DxfWriterOptions {
21
+ /**
22
+ * `999` comment written at the very top of the file, before the HEADER
23
+ * section (valid DXF in every version; the conventional place to state
24
+ * metadata a reader should show a human but never needs to parse — here,
25
+ * the unit R12 can't declare via `$INSUNITS`). Defaults to a generic
26
+ * "units: metres" note; pass a longer string (e.g. including the
27
+ * `IfcProjectedCRS` name) for a georeferenced export.
28
+ */
29
+ headerComment?: string;
30
+ }
31
+ /**
32
+ * Accumulates DXF entities and layer definitions, then assembles the full
33
+ * ASCII DXF R12 (AC1009) document text. One writer instance == one DXF
34
+ * file; layers are registered on first use (`addPolyline`/`addLine`/
35
+ * `addText`) via a CSS colour, resolved to the nearest ACI with
36
+ * {@link cssToAci}.
37
+ */
38
+ export declare class DxfWriter {
39
+ private readonly layers;
40
+ /** Raw (pre-sanitization) name → assigned safe name, for stable reuse + collision disambiguation. */
41
+ private readonly layerNameBySource;
42
+ private readonly entities;
43
+ private readonly headerComment;
44
+ private minX;
45
+ private minY;
46
+ private maxX;
47
+ private maxY;
48
+ /** One warning per document for non-finite input coordinates — see extend(). */
49
+ private warnedNonFinite;
50
+ constructor(options?: DxfWriterOptions);
51
+ private extend;
52
+ /**
53
+ * Register (or reuse) a layer. Returns the sanitized layer name to pass to
54
+ * `addPolyline`/`addLine`/`addText`. Re-registering the same source name
55
+ * with a different colour/linetype is a no-op (first registration wins) —
56
+ * callers should derive one style per logical layer. DIFFERENT source
57
+ * names whose sanitized forms collide (`Wände`/`Wønde` → `W_nde`) get a
58
+ * numeric suffix (`W_nde`, `W_nde_2`, …) instead of silently merging onto
59
+ * one layer.
60
+ */
61
+ layer(name: string, cssColor: string, linetype?: DxfLinetype): string;
62
+ /** `62\n<aci>\n` when an entity overrides its layer's colour, else ''. */
63
+ private colorOverrideGroup;
64
+ /**
65
+ * Add a closed or open polyline (tessellated arcs/circles are pre-sampled
66
+ * by the caller), written as classic `POLYLINE` + `VERTEX`* + `SEQEND` —
67
+ * `LWPOLYLINE` does not exist before R14. `colorOverride` (CSS colour)
68
+ * emits a per-entity ACI (group 62) on the POLYLINE header instead of
69
+ * inheriting the layer colour — used when several IFC types share one
70
+ * category layer but render with distinct colours in the source drawing
71
+ * (matching the SVG exporter's per-entity fill/stroke).
72
+ */
73
+ addPolyline(points: readonly Point2D[], layer: string, closed: boolean, colorOverride?: string): void;
74
+ /** Add a single straight segment. `colorOverride`: see {@link addPolyline}. */
75
+ addLine(start: Point2D, end: Point2D, layer: string, colorOverride?: string): void;
76
+ /**
77
+ * Add single-line text. Multiline callers (annotations/MTEXT) should split
78
+ * on `\n` and call this once per line, offsetting `position` themselves
79
+ * (matching the SVG exporter's tspan stacking) — DXF `TEXT` group 1 is a
80
+ * single line. `colorOverride`: see {@link addPolyline}.
81
+ */
82
+ addText(position: Point2D, text: string, height: number, layer: string, options?: {
83
+ rotationDeg?: number;
84
+ hAlign?: DxfTextHAlign;
85
+ vAlign?: DxfTextVAlign;
86
+ colorOverride?: string;
87
+ }): void;
88
+ /** True once at least one finite point has been written (controls $EXTMIN/$EXTMAX). */
89
+ private hasExtents;
90
+ private buildHeader;
91
+ private buildLtypeTable;
92
+ /**
93
+ * STYLE table with the single `STANDARD` text style. Every TEXT entity
94
+ * this writer emits carries no group 7 and therefore implicitly
95
+ * references STANDARD; defining it keeps strict R12 readers from having
96
+ * to invent it. (Fixed-height 0, width factor 1, `txt` font — the
97
+ * classic defaults.)
98
+ */
99
+ private buildStyleTable;
100
+ private buildLayerTable;
101
+ private buildTables;
102
+ private buildEntities;
103
+ /**
104
+ * Assemble the full ASCII DXF R12 document: a leading `999` comment
105
+ * (units — see class docs), then HEADER + TABLES + ENTITIES + EOF. No
106
+ * BLOCKS/OBJECTS sections and no BLOCK_RECORD table — R12 doesn't require
107
+ * them (unlike R13+, where their absence would make the file invalid).
108
+ */
109
+ toString(): string;
110
+ }
111
+ //# sourceMappingURL=writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../src/dxf/writer.ts"],"names":[],"mappings":"AAiDA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,kFAAkF;AAClF,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC;AAElD,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AACxD,4DAA4D;AAC5D,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;AA4CrE;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKzD;AAyBD,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqC;IAC5D,qGAAqG;IACrG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA6B;IAC/D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAa;IACzB,gFAAgF;IAChF,OAAO,CAAC,eAAe,CAAS;gBAEpB,OAAO,GAAE,gBAAqB;IAI1C,OAAO,CAAC,MAAM;IAsBd;;;;;;;;OAQG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,WAA0B,GAAG,MAAM;IAcnF,0EAA0E;IAC1E,OAAO,CAAC,kBAAkB;IAI1B;;;;;;;;OAQG;IACH,WAAW,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IAmBrG,+EAA+E;IAC/E,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IAYlF;;;;;OAKG;IACH,OAAO,CACL,QAAQ,EAAE,OAAO,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,aAAa,CAAC;QACvB,MAAM,CAAC,EAAE,aAAa,CAAC;QACvB,aAAa,CAAC,EAAE,MAAM,CAAC;KACnB,GACL,IAAI;IA8BP,uFAAuF;IACvF,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,eAAe;IAcvB;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,aAAa;IAOrB;;;;;OAKG;IACH,QAAQ,IAAI,MAAM;CAYnB"}
@@ -0,0 +1,338 @@
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
+ * ASCII DXF writer (issue #1861): the counterpart to `parser.ts` (DXF
6
+ * import, #1782). Produces DXF R12 (AC1009) text — HEADER, TABLES (LTYPE,
7
+ * STYLE, LAYER), ENTITIES — from generic entities (POLYLINE/VERTEX/SEQEND,
8
+ * LINE, TEXT) with a layer name and colour, so any CAD/BIM tool that reads
9
+ * plain ASCII DXF can open the result.
10
+ *
11
+ * R12 is deliberate, not a placeholder: entity handles (group 5) and
12
+ * subclass markers (group 100, e.g. `AcDbEntity`/`AcDbPolyline`) are
13
+ * MANDATORY from R13 onward, and a genuinely valid R2000+ file additionally
14
+ * needs a BLOCK_RECORD table, `*Model_Space`/`*Paper_Space` BLOCKS, and an
15
+ * OBJECTS section with the root dictionary — none of which this writer
16
+ * produces. Declaring a later `$ACADVER` while omitting all of that would
17
+ * make the file reject or force-repair in strict readers (AutoCAD,
18
+ * ODA/Teigha-based BIM tools). R12 requires none of it and is the universal
19
+ * interop baseline every DXF-capable tool still reads; a full R2000+
20
+ * skeleton (handles, subclass markers, BLOCK_RECORD, paper space, OBJECTS)
21
+ * is a possible follow-up, not this writer's job.
22
+ *
23
+ * Consequences of the R12 target:
24
+ * - No `$INSUNITS` header variable (introduced in R14) — the exported
25
+ * unit (always metres) is instead stated in a `999` comment at the very
26
+ * top of the file (valid in every DXF version, ignored by every reader).
27
+ * - `LWPOLYLINE` doesn't exist until R14 either; polylines are written as
28
+ * classic `POLYLINE` + `VERTEX`* + `SEQEND`.
29
+ * - No group 5 (handle) or group 100 (subclass marker) anywhere in this
30
+ * file — `writer.test.ts` asserts that invariant so a future edit can't
31
+ * reintroduce a half-upgraded, invalid hybrid. The same goes for LTYPE
32
+ * group 74 (complex-linetype element type, R13+): R12 linetype patterns
33
+ * are plain `49` dash lengths only.
34
+ * - Symbol (layer) names follow the R12 rules: at most 31 characters from
35
+ * `A-Z a-z 0-9 $ - _` — see {@link sanitizeDxfLayerName}. Distinct
36
+ * source names that collide after sanitizing get a numeric suffix so
37
+ * they never silently merge into one layer.
38
+ * - TABLES carries LTYPE, STYLE (the `STANDARD` text style every TEXT
39
+ * entity implicitly references), and LAYER. VPORT/VIEW/UCS/APPID/
40
+ * DIMSTYLE are optional in R12 (readers create defaults) and are
41
+ * omitted.
42
+ *
43
+ * Coordinates are written verbatim in the caller's unit (drawing metres);
44
+ * see `dxf-exporter.ts` for how the viewer maps its render-frame
45
+ * coordinates into true world/map metres before they reach this writer.
46
+ */
47
+ import { cssToAci } from './aci-colors.js';
48
+ const H_ALIGN_CODE = { left: 0, center: 1, right: 2 };
49
+ const V_ALIGN_CODE = { baseline: 0, bottom: 1, middle: 2, top: 3 };
50
+ /** Default `999` comment prepended to the file (states units — R12 has no `$INSUNITS`). */
51
+ const DEFAULT_HEADER_COMMENT = 'ifc-lite section export - units: metres';
52
+ /**
53
+ * R12 symbol (table-entry) names allow only letters, digits, `$`, `-` and
54
+ * `_` — anything else (unicode, spaces, punctuation) must be replaced.
55
+ * Later DXF versions relax this, but this writer targets R12 (see module
56
+ * docs), so it enforces the strict R12 set.
57
+ */
58
+ const INVALID_LAYER_CHARS = /[^A-Za-z0-9$_-]/g;
59
+ /** R12 symbol names are limited to 31 characters (255 is an R2000+ limit). */
60
+ const MAX_LAYER_NAME_LENGTH = 31;
61
+ /**
62
+ * True for an ASCII control character (C0 range or DEL) — these are never
63
+ * legal inside a DXF layer name or TEXT string. Written as a codepoint
64
+ * comparison rather than a control-character regex literal (`\x00`-`\x1f`)
65
+ * so the disallowed bytes never appear as raw source text.
66
+ */
67
+ function isControlCharCode(code) {
68
+ return code <= 31 || code === 127;
69
+ }
70
+ function stripControlChars(text, replacement) {
71
+ let out = '';
72
+ for (const ch of text) {
73
+ out += isControlCharCode(ch.codePointAt(0) ?? 0) ? replacement : ch;
74
+ }
75
+ return out;
76
+ }
77
+ /**
78
+ * Sanitize a free-text label into a valid DXF R12 LAYER name: every
79
+ * character outside the R12 symbol-name set (`A-Z a-z 0-9 $ - _` — spaces,
80
+ * unicode and punctuation included) becomes `_`, runs of `_` produced by
81
+ * adjacent replacements are collapsed, the result is made non-empty, and
82
+ * truncated to the R12 31-character symbol-name limit.
83
+ *
84
+ * Distinct inputs can collide after this mapping (`Wände`/`Wønde` →
85
+ * `W_nde`); {@link DxfWriter.layer} disambiguates with a numeric suffix so
86
+ * two source layers never silently merge.
87
+ */
88
+ export function sanitizeDxfLayerName(name) {
89
+ const stripped = name.replace(INVALID_LAYER_CHARS, '_').replace(/_{2,}/g, '_');
90
+ const trimmed = stripped.replace(/^_+|_+$/g, '') || stripped;
91
+ const safe = trimmed.length > 0 ? trimmed : 'LAYER';
92
+ return safe.slice(0, MAX_LAYER_NAME_LENGTH);
93
+ }
94
+ /** Sanitize a single line of DXF TEXT content: strip control characters (no raw newlines). */
95
+ function sanitizeDxfText(text) {
96
+ return stripControlChars(text, ' ');
97
+ }
98
+ /** Sanitize a `999` comment line: no raw newlines/control characters. */
99
+ function sanitizeDxfComment(text) {
100
+ return stripControlChars(text, ' ');
101
+ }
102
+ function fmt(n) {
103
+ // Non-finite input stays deterministic ('0.0'); the condition is surfaced
104
+ // to the caller via DxfWriter.extend()'s once-per-document console.warn.
105
+ if (!Number.isFinite(n))
106
+ return '0.0';
107
+ // Fixed precision keeps output deterministic (stable for golden/round-trip
108
+ // tests) while giving sub-micrometre resolution at metre scale.
109
+ return n.toFixed(6);
110
+ }
111
+ /**
112
+ * Accumulates DXF entities and layer definitions, then assembles the full
113
+ * ASCII DXF R12 (AC1009) document text. One writer instance == one DXF
114
+ * file; layers are registered on first use (`addPolyline`/`addLine`/
115
+ * `addText`) via a CSS colour, resolved to the nearest ACI with
116
+ * {@link cssToAci}.
117
+ */
118
+ export class DxfWriter {
119
+ layers = new Map();
120
+ /** Raw (pre-sanitization) name → assigned safe name, for stable reuse + collision disambiguation. */
121
+ layerNameBySource = new Map();
122
+ entities = [];
123
+ headerComment;
124
+ minX = Infinity;
125
+ minY = Infinity;
126
+ maxX = -Infinity;
127
+ maxY = -Infinity;
128
+ /** One warning per document for non-finite input coordinates — see extend(). */
129
+ warnedNonFinite = false;
130
+ constructor(options = {}) {
131
+ this.headerComment = sanitizeDxfComment(options.headerComment ?? DEFAULT_HEADER_COMMENT);
132
+ }
133
+ extend(p) {
134
+ if (!Number.isFinite(p.x) || !Number.isFinite(p.y)) {
135
+ // A bad upstream coordinate (e.g. a pathological georeference scale)
136
+ // would otherwise vanish silently: NaN fails every comparison below, so
137
+ // it never extends $EXTMIN/$EXTMAX, and fmt() writes it as a
138
+ // deterministic 0.0. Keep both behaviours (the file stays valid and
139
+ // reproducible) but surface the problem — once per document, not per
140
+ // vertex, so a fully-degenerate polyline can't spam the console.
141
+ if (!this.warnedNonFinite) {
142
+ this.warnedNonFinite = true;
143
+ console.warn(`[DxfWriter] non-finite coordinate (${p.x}, ${p.y}) — written as 0.0 and excluded from $EXTMIN/$EXTMAX (further occurrences in this document are not logged)`);
144
+ }
145
+ return;
146
+ }
147
+ if (p.x < this.minX)
148
+ this.minX = p.x;
149
+ if (p.x > this.maxX)
150
+ this.maxX = p.x;
151
+ if (p.y < this.minY)
152
+ this.minY = p.y;
153
+ if (p.y > this.maxY)
154
+ this.maxY = p.y;
155
+ }
156
+ /**
157
+ * Register (or reuse) a layer. Returns the sanitized layer name to pass to
158
+ * `addPolyline`/`addLine`/`addText`. Re-registering the same source name
159
+ * with a different colour/linetype is a no-op (first registration wins) —
160
+ * callers should derive one style per logical layer. DIFFERENT source
161
+ * names whose sanitized forms collide (`Wände`/`Wønde` → `W_nde`) get a
162
+ * numeric suffix (`W_nde`, `W_nde_2`, …) instead of silently merging onto
163
+ * one layer.
164
+ */
165
+ layer(name, cssColor, linetype = 'CONTINUOUS') {
166
+ const assigned = this.layerNameBySource.get(name);
167
+ if (assigned !== undefined)
168
+ return assigned;
169
+ const base = sanitizeDxfLayerName(name);
170
+ let safe = base;
171
+ for (let n = 2; this.layers.has(safe); n++) {
172
+ const suffix = `_${n}`;
173
+ safe = base.slice(0, MAX_LAYER_NAME_LENGTH - suffix.length) + suffix;
174
+ }
175
+ this.layers.set(safe, { name: safe, aci: cssToAci(cssColor), linetype });
176
+ this.layerNameBySource.set(name, safe);
177
+ return safe;
178
+ }
179
+ /** `62\n<aci>\n` when an entity overrides its layer's colour, else ''. */
180
+ colorOverrideGroup(cssColor) {
181
+ return cssColor !== undefined ? '62\n' + cssToAci(cssColor) + '\n' : '';
182
+ }
183
+ /**
184
+ * Add a closed or open polyline (tessellated arcs/circles are pre-sampled
185
+ * by the caller), written as classic `POLYLINE` + `VERTEX`* + `SEQEND` —
186
+ * `LWPOLYLINE` does not exist before R14. `colorOverride` (CSS colour)
187
+ * emits a per-entity ACI (group 62) on the POLYLINE header instead of
188
+ * inheriting the layer colour — used when several IFC types share one
189
+ * category layer but render with distinct colours in the source drawing
190
+ * (matching the SVG exporter's per-entity fill/stroke).
191
+ */
192
+ addPolyline(points, layer, closed, colorOverride) {
193
+ if (points.length < 2)
194
+ return;
195
+ for (const p of points)
196
+ this.extend(p);
197
+ const pts = points.slice();
198
+ const colorGroup = this.colorOverrideGroup(colorOverride);
199
+ this.entities.push(() => {
200
+ // The 10/20/30 "dummy point" (always 0) is part of the R12 POLYLINE
201
+ // entity — the real coordinates live on the VERTEX chain.
202
+ let s = '0\nPOLYLINE\n8\n' + layer + '\n' + colorGroup +
203
+ '66\n1\n10\n0.0\n20\n0.0\n30\n0.0\n70\n' + (closed ? 1 : 0) + '\n';
204
+ for (const p of pts) {
205
+ s += '0\nVERTEX\n8\n' + layer + '\n10\n' + fmt(p.x) + '\n20\n' + fmt(p.y) + '\n30\n0.0\n';
206
+ }
207
+ s += '0\nSEQEND\n8\n' + layer + '\n';
208
+ return s;
209
+ });
210
+ }
211
+ /** Add a single straight segment. `colorOverride`: see {@link addPolyline}. */
212
+ addLine(start, end, layer, colorOverride) {
213
+ this.extend(start);
214
+ this.extend(end);
215
+ const colorGroup = this.colorOverrideGroup(colorOverride);
216
+ this.entities.push(() => '0\nLINE\n8\n' + layer + '\n' + colorGroup +
217
+ '10\n' + fmt(start.x) + '\n20\n' + fmt(start.y) + '\n30\n0.0\n' +
218
+ '11\n' + fmt(end.x) + '\n21\n' + fmt(end.y) + '\n31\n0.0\n');
219
+ }
220
+ /**
221
+ * Add single-line text. Multiline callers (annotations/MTEXT) should split
222
+ * on `\n` and call this once per line, offsetting `position` themselves
223
+ * (matching the SVG exporter's tspan stacking) — DXF `TEXT` group 1 is a
224
+ * single line. `colorOverride`: see {@link addPolyline}.
225
+ */
226
+ addText(position, text, height, layer, options = {}) {
227
+ const clean = sanitizeDxfText(text);
228
+ // `height <= 0` alone lets NaN/Infinity through (both compare false), and
229
+ // `fmt` would then write them as the deterministic '0.0' fallback — i.e. a
230
+ // zero-height, invisible TEXT entity, exactly what the guard exists to
231
+ // skip. Reject non-finite heights outright instead.
232
+ if (!clean.trim() || !Number.isFinite(height) || height <= 0)
233
+ return;
234
+ this.extend(position);
235
+ const rotationDeg = options.rotationDeg ?? 0;
236
+ const hAlign = options.hAlign ?? 'left';
237
+ const vAlign = options.vAlign ?? 'baseline';
238
+ const hCode = H_ALIGN_CODE[hAlign];
239
+ const vCode = V_ALIGN_CODE[vAlign];
240
+ const needsAlignPoint = hCode !== 0 || vCode !== 0;
241
+ const colorGroup = this.colorOverrideGroup(options.colorOverride);
242
+ this.entities.push(() => {
243
+ let s = '0\nTEXT\n8\n' + layer + '\n' + colorGroup +
244
+ '10\n' + fmt(position.x) + '\n20\n' + fmt(position.y) + '\n30\n0.0\n' +
245
+ '40\n' + fmt(height) + '\n1\n' + clean + '\n' +
246
+ '50\n' + fmt(rotationDeg) + '\n' +
247
+ '72\n' + hCode + '\n' +
248
+ '73\n' + vCode + '\n';
249
+ if (needsAlignPoint) {
250
+ s += '11\n' + fmt(position.x) + '\n21\n' + fmt(position.y) + '\n31\n0.0\n';
251
+ }
252
+ return s;
253
+ });
254
+ }
255
+ /** True once at least one finite point has been written (controls $EXTMIN/$EXTMAX). */
256
+ hasExtents() {
257
+ return Number.isFinite(this.minX) && Number.isFinite(this.maxX);
258
+ }
259
+ buildHeader() {
260
+ const [minX, minY, maxX, maxY] = this.hasExtents()
261
+ ? [this.minX, this.minY, this.maxX, this.maxY]
262
+ : [0, 0, 0, 0];
263
+ // R12 (AC1009): no $INSUNITS (introduced R14) — see the `999` comment
264
+ // this.toString() prepends ahead of this section instead.
265
+ return ('0\nSECTION\n2\nHEADER\n' +
266
+ '9\n$ACADVER\n1\nAC1009\n' +
267
+ '9\n$EXTMIN\n10\n' + fmt(minX) + '\n20\n' + fmt(minY) + '\n30\n0.0\n' +
268
+ '9\n$EXTMAX\n10\n' + fmt(maxX) + '\n20\n' + fmt(maxY) + '\n30\n0.0\n' +
269
+ '0\nENDSEC\n');
270
+ }
271
+ buildLtypeTable() {
272
+ let s = '0\nTABLE\n2\nLTYPE\n70\n2\n';
273
+ s += '0\nLTYPE\n2\nCONTINUOUS\n70\n0\n3\nSolid line\n72\n65\n73\n0\n40\n0.0\n';
274
+ // Dash 0.2, gap 0.1 (metres) — a reasonable default at typical drawing
275
+ // scale. R12 pattern elements are plain `49` lengths; group 74
276
+ // (complex-linetype element type) is an R13+ construct and must NOT
277
+ // appear here.
278
+ s +=
279
+ '0\nLTYPE\n2\nDASHED\n70\n0\n3\nDashed line\n72\n65\n73\n2\n40\n0.3\n' +
280
+ '49\n0.2\n49\n-0.1\n';
281
+ s += '0\nENDTAB\n';
282
+ return s;
283
+ }
284
+ /**
285
+ * STYLE table with the single `STANDARD` text style. Every TEXT entity
286
+ * this writer emits carries no group 7 and therefore implicitly
287
+ * references STANDARD; defining it keeps strict R12 readers from having
288
+ * to invent it. (Fixed-height 0, width factor 1, `txt` font — the
289
+ * classic defaults.)
290
+ */
291
+ buildStyleTable() {
292
+ return ('0\nTABLE\n2\nSTYLE\n70\n1\n' +
293
+ '0\nSTYLE\n2\nSTANDARD\n70\n0\n40\n0.0\n41\n1.0\n50\n0.0\n71\n0\n42\n0.2\n3\ntxt\n4\n\n' +
294
+ '0\nENDTAB\n');
295
+ }
296
+ buildLayerTable() {
297
+ let s = '0\nTABLE\n2\nLAYER\n70\n' + this.layers.size + '\n';
298
+ for (const l of this.layers.values()) {
299
+ s += '0\nLAYER\n2\n' + l.name + '\n70\n0\n62\n' + l.aci + '\n6\n' + l.linetype + '\n';
300
+ }
301
+ s += '0\nENDTAB\n';
302
+ return s;
303
+ }
304
+ buildTables() {
305
+ // LTYPE before LAYER (layers reference linetypes by name); STYLE for
306
+ // the implicit STANDARD text style. VPORT/VIEW/UCS/APPID/DIMSTYLE are
307
+ // optional in R12 — readers supply defaults — and are omitted.
308
+ return ('0\nSECTION\n2\nTABLES\n' +
309
+ this.buildLtypeTable() +
310
+ this.buildStyleTable() +
311
+ this.buildLayerTable() +
312
+ '0\nENDSEC\n');
313
+ }
314
+ buildEntities() {
315
+ let s = '0\nSECTION\n2\nENTITIES\n';
316
+ for (const write of this.entities)
317
+ s += write();
318
+ s += '0\nENDSEC\n';
319
+ return s;
320
+ }
321
+ /**
322
+ * Assemble the full ASCII DXF R12 document: a leading `999` comment
323
+ * (units — see class docs), then HEADER + TABLES + ENTITIES + EOF. No
324
+ * BLOCKS/OBJECTS sections and no BLOCK_RECORD table — R12 doesn't require
325
+ * them (unlike R13+, where their absence would make the file invalid).
326
+ */
327
+ toString() {
328
+ // Any point written through addPolyline/addLine/addText must land on a
329
+ // registered layer, so ensure at least the "0" default layer exists for
330
+ // callers that add geometry before their first `layer()` call.
331
+ if (!this.layers.has('0')) {
332
+ this.layers.set('0', { name: '0', aci: 7, linetype: 'CONTINUOUS' });
333
+ }
334
+ return ('999\n' + this.headerComment + '\n' +
335
+ this.buildHeader() + this.buildTables() + this.buildEntities() + '0\nEOF\n');
336
+ }
337
+ }
338
+ //# sourceMappingURL=writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.js","sourceRoot":"","sources":["../../src/dxf/writer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * DXF Exporter - Export 2D drawings to ASCII DXF (issue #1861)
3
+ *
4
+ * Mirrors `svg-exporter.ts`'s input contract (a `Drawing2D` plus reference
5
+ * underlays) so the two exporters read the same generated-drawing model and
6
+ * cannot drift: same polylines/edges, same hatch-boundary polygons, same
7
+ * per-style layer/colour derivation.
8
+ *
9
+ * Unlike SVG, DXF has no "paper" concept — coordinates are written verbatim
10
+ * in the drawing's own unit (metres) with no scale/padding transform. A
11
+ * `Drawing2D` generated without a render-frame shift (the package-level
12
+ * contract; see `svg-exporter.ts`'s underlay caveat) therefore round-trips
13
+ * through this exporter unchanged. Callers that need real-world or
14
+ * map-projected output (the viewer's georeferenced plan-section export)
15
+ * supply `coordinateTransform`, applied to every point — drawing and
16
+ * underlay alike — right before it reaches the writer.
17
+ *
18
+ * Output targets DXF R12 (AC1009) — see `dxf/writer.ts`'s module docs for
19
+ * why (handles/subclass markers are mandatory from R13 on; R12 needs
20
+ * neither and is the universal interop baseline). There is accordingly no
21
+ * `$INSUNITS` header variable; the unit (always metres) is stated in a
22
+ * `999` comment at the top of the file instead, optionally naming the
23
+ * `IfcProjectedCRS` via `metadataComment`.
24
+ */
25
+ import type { Drawing2D, Point2D } from './types.js';
26
+ import { type DxfPlacement, type DxfUnderlay } from './dxf/types.js';
27
+ export interface DXFExportOptions {
28
+ /** Include lines whose `visibility === 'hidden'` (default true, matching SVG). */
29
+ showHiddenLines?: boolean;
30
+ /** Include cut-polygon boundaries (hatch regions, represented as closed POLYLINE boundaries — see module docs). Default true. */
31
+ showHatching?: boolean;
32
+ /** DXF reference underlays composited alongside the drawing (issue #1782 parity). */
33
+ underlays?: DXFUnderlayOptions[];
34
+ /**
35
+ * Applied to every emitted point (drawing geometry and underlays alike)
36
+ * as the very last step before it reaches the writer. Identity by
37
+ * default. The viewer uses this to re-derive true world/map coordinates
38
+ * for a plan-view export — see `apps/viewer/src/hooks/dxfExportGeoref.ts`.
39
+ */
40
+ coordinateTransform?: (p: Point2D) => Point2D;
41
+ /**
42
+ * `999` comment written at the top of the file (R12 has no `$INSUNITS`
43
+ * to state the unit instead — see `dxf/writer.ts`). Defaults to a
44
+ * generic "units: metres" note; pass one naming the `IfcProjectedCRS`
45
+ * for a georeferenced export.
46
+ */
47
+ metadataComment?: string;
48
+ }
49
+ /** One DXF reference underlay to embed (mirrors `SVGUnderlayOptions`). */
50
+ export interface DXFUnderlayOptions {
51
+ underlay: DxfUnderlay;
52
+ /** Placement in drawing space; identity when omitted. */
53
+ placement?: DxfPlacement;
54
+ /** Per-layer visibility override; falls back to the DXF layer's own state. */
55
+ layerVisibility?: Record<string, boolean>;
56
+ }
57
+ /** Builds a DXF document from a `Drawing2D`, mirroring `SVGExporter`. */
58
+ export declare class DXFExporter {
59
+ export(drawing: Drawing2D, options?: DXFExportOptions): string;
60
+ private writeLine;
61
+ private writePolygonBoundary;
62
+ private writeUnderlay;
63
+ }
64
+ /** Export a `Drawing2D` to an ASCII DXF string. */
65
+ export declare function exportToDXF(drawing: Drawing2D, options?: DXFExportOptions): string;
66
+ //# sourceMappingURL=dxf-exporter.d.ts.map