lineadraw 0.7.0 → 0.8.0

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.
@@ -2,21 +2,7 @@ import {
2
2
  __export
3
3
  } from "./chunk-PZ5AY32C.js";
4
4
 
5
- // ../core/src/export/dxf/exportDxf.ts
6
- import {
7
- DxfWriter,
8
- Units,
9
- LayerFlags,
10
- LWPolylineFlags,
11
- TextHorizontalAlignment,
12
- TextVerticalAlignment,
13
- HatchBoundaryPaths,
14
- HatchPolylineBoundary,
15
- HatchPredefinedPatterns,
16
- pattern
17
- } from "@tarikjabiri/dxf";
18
-
19
- // ../core/src/model/objects.ts
5
+ // ../core/src/model/hatchPatterns.ts
20
6
  import { z as z2 } from "zod";
21
7
 
22
8
  // ../core/src/model/common.ts
@@ -40,6 +26,716 @@ var ObjectBaseSchema = z.object({
40
26
  opacity: z.number().min(0).max(1).optional()
41
27
  });
42
28
 
29
+ // ../core/src/paint/sink.ts
30
+ var matIdentity = [1, 0, 0, 1, 0, 0];
31
+ var matMultiply = (m, n) => [
32
+ m[0] * n[0] + m[2] * n[1],
33
+ m[1] * n[0] + m[3] * n[1],
34
+ m[0] * n[2] + m[2] * n[3],
35
+ m[1] * n[2] + m[3] * n[3],
36
+ m[0] * n[4] + m[2] * n[5] + m[4],
37
+ m[1] * n[4] + m[3] * n[5] + m[5]
38
+ ];
39
+ var matApply = (m, p) => ({
40
+ x: m[0] * p.x + m[2] * p.y + m[4],
41
+ y: m[1] * p.x + m[3] * p.y + m[5]
42
+ });
43
+ var signedArcSweep = (startAngle, endAngle, ccw) => {
44
+ const TAU = Math.PI * 2;
45
+ let sweep = ccw ? ((endAngle - startAngle) % TAU + TAU) % TAU : -(((startAngle - endAngle) % TAU + TAU) % TAU);
46
+ if (sweep === 0) sweep = ccw ? TAU : -TAU;
47
+ return sweep;
48
+ };
49
+ var arcToBeziers = (center, r, startAngle, sweep) => {
50
+ const n = Math.max(1, Math.ceil(Math.abs(sweep) / (Math.PI / 2)));
51
+ const step = sweep / n;
52
+ const k = 4 / 3 * Math.tan(step / 4);
53
+ const result = [];
54
+ for (let i = 0; i < n; i++) {
55
+ const a0 = startAngle + step * i;
56
+ const a1 = a0 + step;
57
+ const p0 = {
58
+ x: center.x + r * Math.cos(a0),
59
+ y: center.y + r * Math.sin(a0)
60
+ };
61
+ const p1 = {
62
+ x: center.x + r * Math.cos(a1),
63
+ y: center.y + r * Math.sin(a1)
64
+ };
65
+ result.push({
66
+ start: p0,
67
+ c1: { x: p0.x - r * k * Math.sin(a0), y: p0.y + r * k * Math.cos(a0) },
68
+ c2: { x: p1.x + r * k * Math.sin(a1), y: p1.y - r * k * Math.cos(a1) },
69
+ end: p1
70
+ });
71
+ }
72
+ return result;
73
+ };
74
+ var vExtent = (rings, v) => {
75
+ let min = Infinity;
76
+ let max = -Infinity;
77
+ for (const ring of rings) {
78
+ for (const p of ring) {
79
+ const dv = p.x * v.x + p.y * v.y;
80
+ if (dv < min) min = dv;
81
+ if (dv > max) max = dv;
82
+ }
83
+ }
84
+ return Number.isFinite(min) ? { min, max } : null;
85
+ };
86
+ var scanlineCrossings = (rings, u, v, off) => {
87
+ const crossings = [];
88
+ for (const ring of rings) {
89
+ for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
90
+ const a = ring[j];
91
+ const b = ring[i];
92
+ const va = a.x * v.x + a.y * v.y;
93
+ const vb = b.x * v.x + b.y * v.y;
94
+ if (va < off === vb < off) continue;
95
+ const t = (off - va) / (vb - va);
96
+ const px = a.x + (b.x - a.x) * t;
97
+ const py = a.y + (b.y - a.y) * t;
98
+ crossings.push(px * u.x + py * u.y);
99
+ }
100
+ }
101
+ crossings.sort((x, y) => x - y);
102
+ return crossings;
103
+ };
104
+ var computeHatchLines = (rings, angle2, spacing) => spacing <= 0 ? [] : computeHatchFamily(rings, {
105
+ angle: angle2,
106
+ origin: { x: 0, y: 0 },
107
+ deltaAlong: 0,
108
+ deltaPerp: spacing
109
+ });
110
+ var DOT_LENGTH_MM = 0.35;
111
+ var splitByDashes = (a, b, u, dashes, along0, out) => {
112
+ const len3 = (b.x - a.x) * u.x + (b.y - a.y) * u.y;
113
+ if (len3 <= 1e-9) return;
114
+ const cycle = dashes.reduce((s, d) => s + Math.abs(d), 0);
115
+ if (cycle <= 1e-9) {
116
+ out.push({ a, b });
117
+ return;
118
+ }
119
+ const pt = (d) => ({ x: a.x + u.x * d, y: a.y + u.y * d });
120
+ const base = a.x * u.x + a.y * u.y - along0;
121
+ let phase = (base % cycle + cycle) % cycle;
122
+ let idx = 0;
123
+ for (; idx < dashes.length; idx++) {
124
+ const w = Math.abs(dashes[idx]);
125
+ if (phase < w || w <= 1e-9) break;
126
+ phase -= w;
127
+ }
128
+ if (idx >= dashes.length) {
129
+ idx = 0;
130
+ phase = 0;
131
+ }
132
+ let remaining = Math.max(0, Math.abs(dashes[idx]) - phase);
133
+ let pos = 0;
134
+ let guard = 0;
135
+ while (pos < len3 - 1e-9 && guard++ < 1e5) {
136
+ const w = Math.abs(dashes[idx]);
137
+ const draw = dashes[idx] >= 0;
138
+ if (w <= 1e-9) {
139
+ if (draw) {
140
+ const s0 = Math.max(0, pos - DOT_LENGTH_MM / 2);
141
+ const s1 = Math.min(len3, pos + DOT_LENGTH_MM / 2);
142
+ if (s1 > s0) out.push({ a: pt(s0), b: pt(s1) });
143
+ }
144
+ idx = (idx + 1) % dashes.length;
145
+ remaining = Math.abs(dashes[idx]);
146
+ continue;
147
+ }
148
+ const step = Math.min(remaining, len3 - pos);
149
+ if (draw) out.push({ a: pt(pos), b: pt(pos + step) });
150
+ pos += step;
151
+ remaining -= step;
152
+ if (remaining <= 1e-9) {
153
+ idx = (idx + 1) % dashes.length;
154
+ remaining = Math.abs(dashes[idx]);
155
+ }
156
+ }
157
+ };
158
+ var computeHatchFamily = (rings, family) => {
159
+ const { deltaPerp } = family;
160
+ if (rings.length === 0 || Math.abs(deltaPerp) <= 1e-9) return [];
161
+ const u = { x: Math.cos(family.angle), y: Math.sin(family.angle) };
162
+ const v = { x: -u.y, y: u.x };
163
+ const ext = vExtent(rings, v);
164
+ if (!ext) return [];
165
+ const oV = family.origin.x * v.x + family.origin.y * v.y;
166
+ const oU = family.origin.x * u.x + family.origin.y * u.y;
167
+ const iA = (ext.min - oV) / deltaPerp;
168
+ const iB = (ext.max - oV) / deltaPerp;
169
+ const iLo = Math.ceil(Math.min(iA, iB));
170
+ const iHi = Math.floor(Math.max(iA, iB));
171
+ const dashes = family.dashesMm && family.dashesMm.length ? family.dashesMm : null;
172
+ const out = [];
173
+ for (let i = iLo; i <= iHi; i++) {
174
+ const off = oV + i * deltaPerp;
175
+ const along0 = oU + i * family.deltaAlong;
176
+ const crossings = scanlineCrossings(rings, u, v, off);
177
+ for (let k = 0; k + 1 < crossings.length; k += 2) {
178
+ const c0 = crossings[k];
179
+ const c1 = crossings[k + 1];
180
+ if (c1 - c0 < 1e-9) continue;
181
+ const a = { x: u.x * c0 + v.x * off, y: u.y * c0 + v.y * off };
182
+ const b = { x: u.x * c1 + v.x * off, y: u.y * c1 + v.y * off };
183
+ if (dashes) splitByDashes(a, b, u, dashes, along0, out);
184
+ else out.push({ a, b });
185
+ }
186
+ }
187
+ return out;
188
+ };
189
+ var ellipseArcToBeziers = (center, rx, ry, rotation, startAngle, sweep) => {
190
+ const cosR = Math.cos(rotation);
191
+ const sinR = Math.sin(rotation);
192
+ const map = (p) => ({
193
+ x: center.x + p.x * cosR - p.y * sinR,
194
+ y: center.y + p.x * sinR + p.y * cosR
195
+ });
196
+ return arcToBeziers({ x: 0, y: 0 }, 1, startAngle, sweep).map((b) => ({
197
+ start: map({ x: b.start.x * rx, y: b.start.y * ry }),
198
+ c1: map({ x: b.c1.x * rx, y: b.c1.y * ry }),
199
+ c2: map({ x: b.c2.x * rx, y: b.c2.y * ry }),
200
+ end: map({ x: b.end.x * rx, y: b.end.y * ry })
201
+ }));
202
+ };
203
+
204
+ // ../core/src/model/builtinPat.ts
205
+ var BUILTIN_PAT = `;; Linea built-in hatch pattern library.
206
+ ;;
207
+ ;; Format: .pat syntax - a \`*NAME,description\` header followed by one line
208
+ ;; family per row: \`angle, x-origin, y-origin, delta-x, delta-y [, dash...]\`
209
+ ;; (angle in degrees CCW; delta-x shifts successive lines along their own
210
+ ;; direction - dash phase - and delta-y is the perpendicular spacing;
211
+ ;; dashes: >0 pen-down, <0 gap, 0 dot). All lengths are millimeters (ISO
212
+ ;; scaling).
213
+ ;;
214
+ ;; The pattern geometry is functional interchange data, kept name- and
215
+ ;; value-compatible with the predefined DXF HATCH patterns other CAD
216
+ ;; applications resolve - ANSI/ISO/JIS/GOST section conventions plus common
217
+ ;; architectural textures. Descriptions and organization are Linea's
218
+ ;; own. There is no SOLID entry: solid fill is its own fill kind, not a
219
+ ;; line-family pattern.
220
+ ;;
221
+ ;; After editing, regenerate the embedded TS mirror:
222
+ ;; npm run gen:patterns -w @lineadraw/core
223
+ ;;
224
+
225
+ ;; Material and texture patterns.
226
+
227
+ *ANGLE,Steel angle framing
228
+ 0, 0, 0, 0, 6.985, 5.08, -1.905
229
+ 90, 0, 0, 0, 6.985, 5.08, -1.905
230
+ *ANSI31,Cast iron; general section (ANSI)
231
+ 45, 0, 0, 0, 3.175
232
+ *ANSI32,Steel (ANSI)
233
+ 45, 0, 0, 0, 9.525
234
+ 45, 4.49013, 0, 0, 9.525
235
+ *ANSI33,Bronze, brass, copper (ANSI)
236
+ 45, 0, 0, 0, 6.35
237
+ 45, 4.49013, 0, 0, 6.35, 3.175, -1.5875
238
+ *ANSI34,Plastic, rubber (ANSI)
239
+ 45, 0, 0, 0, 19.05
240
+ 45, 4.49013, 0, 0, 19.05
241
+ 45, 8.98026, 0, 0, 19.05
242
+ 45, 13.4704, 0, 0, 19.05
243
+ *ANSI35,Fire brick, refractory (ANSI)
244
+ 45, 0, 0, 0, 6.35
245
+ 45, 4.49013, 0, 0, 6.35, 7.9375, -1.5875, 0, -1.5875
246
+ *ANSI36,Marble, slate, glass (ANSI)
247
+ 45, 0, 0, 5.55625, 3.175, 7.9375, -1.5875, 0, -1.5875
248
+ *ANSI37,Lead, zinc, insulation (ANSI)
249
+ 45, 0, 0, 0, 3.175
250
+ 135, 0, 0, 0, 3.175
251
+ *ANSI38,Aluminum (ANSI)
252
+ 45, 0, 0, 0, 3.175
253
+ 135, 0, 0, 6.35, 3.175, 7.9375, -4.7625
254
+ *AR-B816,Block wall 8x16, running bond
255
+ 0, 0, 0, 0, 203.2
256
+ 90, 0, 0, 203.2, 203.2, 203.2, -203.2
257
+ *AR-B816C,Block wall 8x16 with mortar joints
258
+ 0, 0, 0, 203.2, 203.2, 396.875, -9.525
259
+ 0, -203.2, 9.525, 203.2, 203.2, 396.875, -9.525
260
+ 90, 0, 0, 203.2, 203.2, -212.725, 193.675
261
+ 90, -9.525, 0, 203.2, 203.2, -212.725, 193.675
262
+ *AR-B88,Block wall 8x8, running bond
263
+ 0, 0, 0, 0, 203.2
264
+ 90, 0, 0, 203.2, 101.6, 203.2, -203.2
265
+ *AR-BRELM,Brick wall, English bond, mortar joints
266
+ 0, 0, 0, 0, 135.484, 193.675, -9.525
267
+ 0, 0, 57.15, 0, 135.484, 193.675, -9.525
268
+ 0, 50.8, 67.7418, 0, 135.484, 92.075, -9.525
269
+ 0, 50.8, 124.892, 0, 135.484, 92.075, -9.525
270
+ 90, 0, 0, 0, 203.2, 57.15, -78.334
271
+ 90, -9.525, 0, 0, 203.2, 57.15, -78.334
272
+ 90, 50.8, 67.7418, 0, 101.6, 57.15, -78.334
273
+ 90, 41.275, 67.7418, 0, 101.6, 57.15, -78.334
274
+ *AR-BRSTD,Brick wall, running bond
275
+ 0, 0, 0, 0, 67.7418
276
+ 90, 0, 0, 67.7418, 101.6, 67.7418, -67.7418
277
+ *AR-CONC,Concrete, random aggregate
278
+ 50, 0, 0, 104.896, -149.807, 19.05, -209.55
279
+ 355, 0, 0, -51.76101082,187.25814969, 15.24, -167.64058417
280
+ 100.4514447,15.182007,-1.3282535,145.5569059,-176.270089,16.1900088,-178.0902446
281
+ 46.1842, 0, 50.8, 157.343, -224.71, 28.575, -314.325
282
+ 96.63555761,22.5899,47.2965,218.33577212,-264.40480444,24.28502314,-267.13560816
283
+ 351.18415117,0,50.8,196.67912063,280.88740361,22.85996707,-251.45973192
284
+ 21, 25.4, 38.1, 104.89565868, -149.80652586, 19.05, -209.55
285
+ 326, 25.4, 38.1, -51.7604, 187.258, 15.24, -167.64
286
+ 71.451445,38.0345326,29.5779001,145.5567546,-176.2700748,16.1900088,-178.0899376
287
+ 37.5, 0, 0, 53.9242, 65.2018, 0, -165.608, 0, -170.18, 0, -168.275
288
+ 7.5, 0, 0, 79.3242, 90.6018, 0, -97.028, 0, -161.798, 0, -64.135
289
+ -32.5, -56.642, 0, 117.434, 68.0212, 0, -63.5, 0, -198.12, 0, -262.89
290
+ -42.5, -82.042, 0, 92.0344, 118.821, 0, -82.55, 0, -131.572, 0, -186.69
291
+ *AR-HBONE,Brick paving, herringbone at 45 deg
292
+ 45, 0, 0, 101.6, 101.6, 304.8, -101.6
293
+ 135, 71.842, 71.842, 101.6, -101.6, 304.8, -101.6
294
+ *AR-PARQ1,Parquet flooring, basket weave
295
+ 90, 0, 0, 304.8, 304.8, 304.8, -304.8
296
+ 90, 50.8, 0, 304.8, 304.8, 304.8, -304.8
297
+ 90, 101.6, 0, 304.8, 304.8, 304.8, -304.8
298
+ 90, 152.4, 0, 304.8, 304.8, 304.8, -304.8
299
+ 90, 203.2, 0, 304.8, 304.8, 304.8, -304.8
300
+ 90, 254, 0, 304.8, 304.8, 304.8, -304.8
301
+ 90, 304.8, 0, 304.8, 304.8, 304.8, -304.8
302
+ 0, 0, 304.8, 304.8, -304.8, 304.8, -304.8
303
+ 0, 0, 355.6, 304.8, -304.8, 304.8, -304.8
304
+ 0, 0, 406.4, 304.8, -304.8, 304.8, -304.8
305
+ 0, 0, 457.2, 304.8, -304.8, 304.8, -304.8
306
+ 0, 0, 508, 304.8, -304.8, 304.8, -304.8
307
+ 0, 0, 558.8, 304.8, -304.8, 304.8, -304.8
308
+ 0, 0, 609.6, 304.8, -304.8, 304.8, -304.8
309
+ *AR-RROOF,Roof shingles
310
+ 0, 0, 0, 55.88, 25.4, 381, -50.8, 127, -25.4
311
+ 0, 33.782, 12.7, -25.4, 33.782, 76.2, -8.382, 152.4, -19.05
312
+ 0, 12.7, 21.59, 132.08, 17.018, 203.2, -35.56, 101.6, -25.4
313
+ *AR-RSHKE,Roof wood shakes
314
+ 0, 0, 0, 647.7, 304.8, 152.4, -127, 177.8, -76.2, 228.6, -101.6
315
+ 0, 152.4, 12.7, 647.7, 304.8, 127, -482.6, 101.6, -152.4
316
+ 0, 457.2, -19.05, 647.7, 304.8, 76.2, -787.4
317
+ 90, 0, 0, 304.8, 215.9, 292.1, -927.1
318
+ 90, 152.4, 0, 304.8, 215.9, 285.75, -933.45
319
+ 90, 279.4, 0, 304.8, 215.9, 266.7, -952.5
320
+ 90, 457.2, -19.05, 304.8, 215.9, 292.1, -927.1
321
+ 90, 533.4, -19.05, 304.8, 215.9, 292.1, -927.1
322
+ 90, 762, 0, 304.8, 215.9, 279.4, -939.8
323
+ *AR-SAND,Sand stipple
324
+ 37.5, 0, 0, 28.5242, 39.8018, 0, -38.608, 0, -43.18, 0, -41.275
325
+ 7.5, 0, 0, 53.9242, 65.2018, 0, -20.828, 0, -34.798, 0, -13.335
326
+ -32.5, -31.242, 0, 66.6344, 42.6212, 0, -12.7, 0, -45.72, 0, -59.69
327
+ -42.5, -31.242, 0, 41.2344, 68.0212, 0, -6.35, 0, -29.972, 0, -34.29
328
+ *BOX,Interlocking box weave
329
+ 90, 0, 0, 0, 25.4
330
+ 90, 6.35, 0, 0, 25.4
331
+ 0, 0, 0, 0, 25.4, -6.35, 6.35
332
+ 0, 0, 6.35, 0, 25.4, -6.35, 6.35
333
+ 0, 0, 12.7, 0, 25.4, 6.35, -6.35
334
+ 0, 0, 19.05, 0, 25.4, 6.35, -6.35
335
+ 90, 12.7, 0, 0, 25.4, 6.35, -6.35
336
+ 90, 19.05, 0, 0, 25.4, 6.35, -6.35
337
+ *BRASS,Brass
338
+ 0, 0, 0, 0, 6.35
339
+ 0, 0, 3.175, 0, 6.35, 3.175, -1.5875
340
+ *BRICK,Brick coursing
341
+ 0, 0, 0, 0, 6.35
342
+ 90, 0, 0, 0, 12.7, 6.35, -6.35
343
+ 90, 6.35, 0, 0, 12.7, -6.35, 6.35
344
+ *BRSTONE,Brick and stone mix
345
+ 0, 0, 0, 0, 8.382
346
+ 90, 22.86, 0, 8.382, 12.7, 8.382, -8.382
347
+ 90, 20.32, 0, 8.382, 12.7, 8.382, -8.382
348
+ 0, 22.86, 1.397, 12.7, 8.382, -22.86, 2.54
349
+ 0, 22.86, 2.794, 12.7, 8.382, -22.86, 2.54
350
+ 0, 22.86, 4.191, 12.7, 8.382, -22.86, 2.54
351
+ 0, 22.86, 5.588, 12.7, 8.382, -22.86, 2.54
352
+ 0, 22.86, 6.985, 12.7, 8.382, -22.86, 2.54
353
+ *CLAY,Clay
354
+ 0, 0, 0, 0, 4.7625
355
+ 0, 0, 0.79375, 0, 4.7625
356
+ 0, 0, 1.5875, 0, 4.7625
357
+ 0, 0, 3.175, 0, 4.7625, 4.7625, -3.175
358
+ *CORK,Cork
359
+ 0, 0, 0, 0, 3.175
360
+ 135, 1.5875, -1.5875, 0, 8.98026, 4.49013, -4.49013
361
+ 135, 2.38125, -1.5875, 0, 8.98026, 4.49013, -4.49013
362
+ 135, 3.175, -1.5875, 0, 8.98026, 4.49013, -4.49013
363
+ *CROSS,Cross tiling
364
+ 0, 0, 0, 6.35, 6.35, 3.175, -9.525
365
+ 90, 1.5875, -1.5875, 6.35, 6.35, 3.175, -9.525
366
+ *DASH,Staggered dashes
367
+ 0, 0, 0, 3.175, 3.175, 3.175, -3.175
368
+ *DOLMIT,Layered rock (dolomite)
369
+ 0, 0, 0, 0, 6.35
370
+ 45, 0, 0, 0, 17.9605,8.98025612106915355989072339,-17.9605122421383071197814467
371
+ *DOTS,Dot screen
372
+ 0, 0, 0, 0.79375, 1.5875, 0, -1.5875
373
+ *EARTH,Earth (section)
374
+ 0, 0, 0, 6.35, 6.35, 6.35, -6.35
375
+ 0, 0, 2.38125, 6.35, 6.35, 6.35, -6.35
376
+ 0, 0, 4.7625, 6.35, 6.35, 6.35, -6.35
377
+ 90, 0.79375, 5.55625, 6.35, 6.35, 6.35, -6.35
378
+ 90, 3.175, 5.55625, 6.35, 6.35, 6.35, -6.35
379
+ 90, 5.55625, 5.55625, 6.35, 6.35, 6.35, -6.35
380
+ *ESCHER,Isometric cube tiling
381
+ 60, 0, 0, -15.24, 26.3964542936, 27.94, -2.54
382
+ 180, 0, 0, -15.24, 26.3964542936, 27.94, -2.54
383
+ 300, 0, 0, 15.24, 26.3964542936, 27.94, -2.54
384
+ 60, 2.54, 0, -15.24, 26.3964542936, 5.08, -25.4
385
+ 300, 2.54, 0, 15.24, 26.3964542936, 5.08, -25.4
386
+ 60, -1.27, 2.199704516, -15.24, 26.3964542936, 5.08, -25.4
387
+ 180, -1.27, 2.199704516, -15.24, 26.3964542936, 5.08, -25.4
388
+ 300, -1.27, -2.199704516, 15.24, 26.3964542936, 5.08, -25.4
389
+ 180, -1.27, -2.199704516, -15.24, 26.3964542936, 5.08, -25.4
390
+ 60, -10.16, 0, -15.24, 26.3964542936, 5.08, -25.4
391
+ 300, -10.16, 0, 15.24, 26.3964542936, 5.08, -25.4
392
+ 60, 5.08, -8.7988180894, -15.24, 26.3964542936, 5.08, -25.4
393
+ 180, 5.08, -8.7988180894, -15.24, 26.3964542936, 5.08, -25.4
394
+ 300, 5.08, 8.7988180894, 15.24, 26.3964542936, 5.08, -25.4
395
+ 180, 5.08, 8.7988180894, -15.24, 26.3964542936, 5.08, -25.4
396
+ 0, 5.08, 4.3994090574, -15.24, 26.3964542936, 17.78, -12.7
397
+ 0, 5.08, -4.3994090574, -15.24, 26.3964542936, 17.78, -12.7
398
+ 120, 1.27, 6.5991135734, 15.24, 26.3964542936, 17.78, -12.7
399
+ 120, -6.35, 2.199704516, 15.24, 26.3964542936, 17.78, -12.7
400
+ 240, -6.35, -2.199704516, 15.24, 26.3964542936, 17.78, -12.7
401
+ 240, 1.27, -6.5991135734, 15.24, 26.3964542936, 17.78, -12.7
402
+ *FLEX,Flexible material
403
+ 0, 0, 0, 0, 6.35, 6.35, -6.35
404
+ 45,6.35,0,4.490128053,4.490128053,1.5875,-5.8052561314,1.5875,-8.9802561314
405
+ *GOST_GLASS,Glass (GOST)
406
+ 45, 0, 0, 6, -6, 5, -7
407
+ 45, 2.121320, 0, 6, -6, 2, -10
408
+ 45, 0, 2.121320, 6, -6, 2, -10
409
+ *GOST_GROUND,Ground (GOST)
410
+ 45, 0, 0, 10, -10, 20
411
+ 45, 3, 0, 10, -10, 20
412
+ 45, 6, 0, 10, -10, 20
413
+ *GOST_WOOD,Wood (GOST)
414
+ 90, 0, 0, 0, -6, 10, -2
415
+ 90, 2, -2, 0, -6, 6, -1.5, 3, -1.5
416
+ 90, 4, -5, 0, -6, 10, -2
417
+ *GRASS,Grass
418
+ 90, 0, 0, 17.96051224, 17.96051224, 4.7625, -31.15852448
419
+ 45, 0, 0, 0, 25.4, 4.7625, -20.6375
420
+ 135, 0, 0, 0, 25.4, 4.7625, -20.6375
421
+ *GRATE,Grating
422
+ 0, 0, 0, 0, 0.79375
423
+ 90, 0, 0, 0, 3.175
424
+ *GRAVEL,Gravel, random cobbles
425
+ 228.0127875,18.288,25.4,305.85067529778,1.88796713245,3.4172144,-338.30483639565
426
+ 184.969741,16.002,22.86,-305.8545235377,1.10019612724,5.8640472,-580.54048893524
427
+ 132.5104471,10.16,22.352,-377.59492241548,1.56030959675,4.1348152,-409.347227941
428
+ 267.273689,.254,16.002,-508.63316875916,1.20815479432,5.3400452,-528.66437425738
429
+ 292.83365418,0.0,10.668,-330.19770134945,1.23208097566,5.236337,-518.39807745344
430
+ 357.273689,2.032,5.842,-508.63316875916,1.20815479432,5.3400452,-528.66437425738
431
+ 37.69424047,7.366,5.588,-416.58997273292,.91357450169,7.0619366,-699.13115314247
432
+ 72.25532837,12.954,9.906,586.40373773403,.96766293399,6.6671952,-660.05256601905
433
+ 121.42956562,14.986,16.256,387.71230339293,1.2040754753,5.35813,-530.45545698712
434
+ 175.2363583,12.192,20.828,-280.5442400419,2.10935518695,6.1171328,-299.7393695
435
+ 222.3974378,6.096,21.336,413.48123885686,.81554484621,7.9107792,-783.16772512177
436
+ 138.81407483,25.4,15.748,234.164238558,2.38943100688,2.7000454,-267.30565824344
437
+ 171.4692344,23.368,17.526,-334.082478726,1.25594916784,5.1368198,-508.5463899704
438
+ 225.00000,18.288,18.288,17.96051224214,17.96051224214,3.5920934,-32.32893108428
439
+ 203.19859051,16.51,21.336,-136.74251918,3.33518339548,1.9344132,-191.50622368894
440
+ 291.80140949,14.732,20.574,-80.18324702488,4.71666158921,2.7356562,-134.0475299
441
+ 30.96375653,15.748,18.034,91.47734531502,4.35606406258,4.4431966,-143.6629815291
442
+ 161.56505118,19.558,20.32,-56.2252967978,8.03218525675,3.2128714,-77.10898116828
443
+ 16.389540334,0.0,20.574,265.17991128726,1.43340492604,4.50088,-445.58826672539
444
+ 70.34617594,4.318,21.844,-297.29446803469,1.70858889651,3.7759894,-373.822156782
445
+ 293.19859051,19.558,25.4,-136.7425191801,3.33518339548,3.868801,-189.57183588894
446
+ 343.61045967,21.082,21.844,-265.17991128725,1.433404926,4.50088,-445.5882667254
447
+ 339.44395478,0.0,4.826,-136.75087638398,2.97284513779,4.340352,-212.67734313106
448
+ 294.7751406,4.064,3.302,-306.90424056705,1.77401295215,3.6367212,-360.0359338072
449
+ 66.80140949,19.812,0.0,136.74251918012,3.33518339452,3.868801,-189.57183588894
450
+ 17.35402464,21.336,3.556,-345.47402804977,1.51523696536,4.2578274,-421.523759802
451
+ 69.44395478,7.366,0.0,-136.75087638396,2.97284513874,2.170176,-214.84751913106
452
+ 101.309932474,18.288,0.0,104.60834648271,4.98134983255,1.295146,-128.21994964526
453
+ 165.963756532,18.034,1.27,-80.085263387,6.16040487582,5.236337,-99.49054589069
454
+ 186.00900596,12.954,2.54,-255.26337856879,1.32949676118,4.85267,-480.41364863337
455
+ 303.69006753,15.748,15.748,-56.35753993648,7.0446924921,3.6632388,-87.9177635968
456
+ 353.15722659,17.78,12.7,434.77679606606,1.0087628707,6.3955676,-633.16009065031
457
+ 60.9453959,24.13,11.938,-204.76648550216,2.46706609031,2.6150824,-258.8939231811
458
+ 90.0000000,25.4,14.224,25.4,25.4,1.524,-23.876
459
+ 120.25643716,12.446,3.302,-204.77318477297,1.8283320086,3.5286696,-349.339407732
460
+ 48.01278750,10.668,6.35,305.85067529778,1.88796713138,6.8344288,-334.88762199565
461
+ 0.000000000,15.24,11.43,25.4,25.4,6.604,-18.796
462
+ 325.3048465,21.844,11.43,310.04235091354,-1.6064370526,4.0160956,-397.5931672414
463
+ 254.0546041,25.146,9.144,104.6687497289,3.48895832444,3.6982908,-181.21650038772
464
+ 207.64597536,24.13,5.588,545.36007557253,1.07143433066,6.021451,-596.12464422938
465
+ 175.42607874,18.796,2.794,331.1739336186,1.01276432357,6.3702946,-630.6584645624
466
+ *HEX,Hexagonal mesh
467
+ 0, 0, 0, 0, 5.4992613154, 3.175, -6.35
468
+ 120, 0, 0, 0, 5.4992613154, 3.175, -6.35
469
+ 60, 3.175, 0, 0, 5.4992613154, 3.175, -6.35
470
+ *HONEY,Honeycomb
471
+ 0, 0, 0, 4.7625, 2.749630645, 3.175, -6.35
472
+ 120, 0, 0, 4.7625, 2.749630645, 3.175, -6.35
473
+ 60, 0, 0, 4.7625, 2.749630645, -6.35, 3.175
474
+ *HOUND,Houndstooth
475
+ 0, 0, 0, 6.35, 1.5875, 25.4, -12.7
476
+ 90, 0, 0, -6.35, 1.5875, 25.4, -12.7
477
+ *INSUL,Insulation batt
478
+ 0, 0, 0, 0, 9.525
479
+ 0, 0, 3.175, 0, 9.525, 3.175, -3.175
480
+ 0, 0, 6.35, 0, 9.525, 3.175, -3.175
481
+ *LINE,Horizontal lines
482
+ 0, 0, 0, 0, 3.175
483
+ *MUDST,Mud / sand (geological)
484
+ 0, 0, 0, 12.7, 6.35, 6.35, -6.35, 0, -6.35, 0, -6.35
485
+ *NET,Square grid
486
+ 0, 0, 0, 0, 3.175
487
+ 90, 0, 0, 0, 3.175
488
+ *NET3,Triangular grid
489
+ 0, 0, 0, 0, 3.175
490
+ 60, 0, 0, 0, 3.175
491
+ 120, 0, 0, 0, 3.175
492
+ *PLAST,Plastic
493
+ 0, 0, 0, 0, 6.35
494
+ 0, 0, 0.79375, 0, 6.35
495
+ 0, 0, 1.5875, 0, 6.35
496
+ *PLASTI,Plastic, dense
497
+ 0, 0, 0, 0, 6.35
498
+ 0, 0, 0.79375, 0, 6.35
499
+ 0, 0, 1.5875, 0, 6.35
500
+ 0, 0, 3.96875, 0, 6.35
501
+ *SACNCR,Concrete, dotted diagonal
502
+ 45, 0, 0, 0, 2.38125
503
+ 45, 1.6838, 0, 0, 2.38125, 0, -2.38125
504
+ *SQUARE,Dashed square lattice
505
+ 0, 0, 0, 0, 3.175, 3.175, -3.175
506
+ 90, 0, 0, 0, 3.175, 3.175, -3.175
507
+ *STARS,Six-pointed star lattice
508
+ 0, 0, 0, 0, 5.4992613154, 3.175, -3.175
509
+ 60, 0, 0, 0, 5.4992613154, 3.175, -3.175
510
+ 120, 1.5875, 2.7496306704, 0, 5.4992613154, 3.175, -3.175
511
+ *STEEL,Steel, paired lines
512
+ 45, 0, 0, 0, 3.175
513
+ 45, 0, 1.5875, 0, 3.175
514
+ *SWAMP,Swamp / marsh
515
+ 0, 0, 0, 12.7, 21.9970452362, 3.175, -22.225
516
+ 90, 1.5875, 0, 21.9970452362, 12.7, 1.5875, -42.4065904724
517
+ 90, 1.984375, 0, 21.9970452362, 12.7, 1.27, -42.7240904724
518
+ 90, 1.190625, 0, 21.9970452362, 12.7, 1.27, -42.7240904724
519
+ 60, 2.38125, 0, 12.7, 21.9970452362, 1.016, -24.384
520
+ 120, 0.79375, 0, 12.7, 21.9970452362, 1.016, -24.384
521
+ *TRANS,Heat-transfer surface
522
+ 0, 0, 0, 0, 6.35
523
+ 0, 0, 3.175, 0, 6.35, 3.175, -3.175
524
+ *TRIANG,Triangle tiling
525
+ 60, 0, 0, 4.7625, 8.2488919604, 4.7625, -4.7625
526
+ 120, 0, 0, 4.7625, 8.2488919604, 4.7625, -4.7625
527
+ 0, -2.38125, 4.1244459802, 4.7625, 8.2488919604, 4.7625, -4.7625
528
+ *ZIGZAG,Stair-step zigzag
529
+ 0, 0, 0, 3.175, 3.175, 3.175, -3.175
530
+ 90, 3.175, 0, 3.175, 3.175, 3.175, -3.175
531
+
532
+ ;; Hatch companions to the ISO 128 line types (1 mm pen base).
533
+
534
+ *ACAD_ISO02W100,ISO dashed
535
+ 0, 0,0, 0,5, 12,-3
536
+ *ACAD_ISO03W100,ISO dashed, wide gaps
537
+ 0, 0,0, 0,5, 12,-18
538
+ *ACAD_ISO04W100,ISO long-dash dot
539
+ 0, 0,0, 0,5, 24,-3,.5,-3
540
+ *ACAD_ISO05W100,ISO long-dash double-dot
541
+ 0, 0,0, 0,5, 24,-3,.5,-3,.5,-3
542
+ *ACAD_ISO06W100,ISO long-dash triple-dot
543
+ 0, 0,0, 0,5, 24,-3,.5,-3,.5,-6.5
544
+ 0, 0,0, 0,5, -34,.5,-3
545
+ *ACAD_ISO07W100,ISO dotted
546
+ 0, 0,0, 0,5, .5,-3
547
+ *ACAD_ISO08W100,ISO long-dash short-dash
548
+ 0, 0,0, 0,5, 24,-3,6,-3
549
+ *ACAD_ISO09W100,ISO long-dash double short-dash
550
+ 0, 0,0, 0,5, 24,-3,6,-3,6,-3
551
+ *ACAD_ISO10W100,ISO dash dot
552
+ 0, 0,0, 0,5, 12,-3,.5,-3
553
+ *ACAD_ISO11W100,ISO double-dash dot
554
+ 0, 0,0, 0,5, 12,-3,12,-3,.5,-3
555
+ *ACAD_ISO12W100,ISO dash double-dot
556
+ 0, 0,0, 0,5, 12,-3,.5,-3,.5,-3
557
+ *ACAD_ISO13W100,ISO double-dash double-dot
558
+ 0, 0,0, 0,5, 12,-3,12,-3,.5,-6.5
559
+ 0, 0,0, 0,5, -33.5,.5,-3
560
+ *ACAD_ISO14W100,ISO dash triple-dot
561
+ 0, 0,0, 0,5, 12,-3,.5,-3,.5,-6.5
562
+ 0, 0,0, 0,5, -22,.5,-3
563
+ *ACAD_ISO15W100,ISO double-dash triple-dot
564
+ 0, 0,0, 0,5, 12,-3,12,-3,.5,-10
565
+ 0, 0,0, 0,5, -33.5,.5,-3,.5,-3
566
+
567
+ ;; JIS A 0150 section patterns.
568
+
569
+ *JIS_LC_20,Lightweight concrete, 20 mm (JIS)
570
+ 45,0,0,0,20
571
+ 45,.4,0,0,20
572
+ *JIS_LC_20A,Lightweight concrete, 20 mm wide (JIS)
573
+ 45,0,0,0,20
574
+ 45,1,0,0,20
575
+ *JIS_LC_8,Lightweight concrete, 8 mm (JIS)
576
+ 45,0,0,0,7.8
577
+ 45,.4,0,0,7.8
578
+ *JIS_LC_8A,Lightweight concrete, 8 mm wide (JIS)
579
+ 45,0,0,0,7.8
580
+ 45,1,0,0,7.8
581
+ *JIS_RC_10,Reinforced concrete, 10 mm (JIS)
582
+ 45,0,0,0,10
583
+ 45,.725,0,0,10
584
+ 45,1.45,0,0,10
585
+ *JIS_RC_15,Reinforced concrete, 15 mm (JIS)
586
+ 45,0,0,0,15
587
+ 45,.725,0,0,15
588
+ 45,1.45,0,0,15
589
+ *JIS_RC_18,Reinforced concrete, 18 mm (JIS)
590
+ 45,0,0,0,18
591
+ 45,1,0,0,18
592
+ 45,2,0,0,18
593
+ *JIS_RC_30,Reinforced concrete, 30 mm (JIS)
594
+ 45,0,0,0,30
595
+ 45,1,0,0,30
596
+ 45,2,0,0,30
597
+ *JIS_STN_1E,Stone, 1 mm (JIS)
598
+ 45, 0, 0, 0, 1
599
+ 45, .705, 0, 0,1, 1, -.5
600
+ *JIS_STN_2.5,Stone, 2.5 mm (JIS)
601
+ 45, 0, 0, 0, 2.5
602
+ 45, 1.765, 0, 0,2.5, 1.2, -.5
603
+ *JIS_WOOD,Wood (JIS)
604
+ 45,0,0,0,.70710678
605
+ `;
606
+
607
+ // ../core/src/model/hatchPatterns.ts
608
+ var parsePatternDefinitions = (text, unitScale = 25.4) => {
609
+ const DEG2RAD = Math.PI / 180;
610
+ const patterns = [];
611
+ let current = null;
612
+ for (const raw of text.split(/\r?\n/)) {
613
+ const line = raw.trim();
614
+ if (!line || line.startsWith(";")) continue;
615
+ if (line.startsWith("*")) {
616
+ const [name, ...desc] = line.slice(1).split(",");
617
+ current = { name: name.trim(), lines: [] };
618
+ const d = desc.join(",").trim();
619
+ if (d) current.description = d;
620
+ patterns.push(current);
621
+ continue;
622
+ }
623
+ if (!current) continue;
624
+ const n = line.split(",").map((s) => Number(s.trim()));
625
+ if (n.length < 5 || n.some((v) => Number.isNaN(v))) continue;
626
+ const [angle2, x, y, dx, dy, ...dashes] = n;
627
+ current.lines.push({
628
+ angle: angle2 * DEG2RAD,
629
+ origin: { x: x * unitScale, y: y * unitScale },
630
+ deltaX: dx * unitScale,
631
+ deltaY: dy * unitScale,
632
+ dashesMm: dashes.length ? dashes.map((v) => v * unitScale) : void 0
633
+ });
634
+ }
635
+ return patterns.filter((p) => p.lines.length > 0);
636
+ };
637
+ var BUILT_IN_PATTERNS = parsePatternDefinitions(
638
+ BUILTIN_PAT,
639
+ 1
640
+ ).filter((p) => p.name.toUpperCase() !== "SOLID");
641
+ var normalizeName = (name) => name.trim().toUpperCase().replace(/[\s.-]+/g, "_");
642
+ var BUILT_IN_BY_NAME = new Map(
643
+ BUILT_IN_PATTERNS.map((p) => [normalizeName(p.name), p])
644
+ );
645
+ var HATCH_PATTERN_NAMES = BUILT_IN_PATTERNS.map(
646
+ (p) => p.name
647
+ );
648
+ var getHatchPattern = (name) => BUILT_IN_BY_NAME.get(normalizeName(name));
649
+ var MAX_HATCH_SEGMENTS = 1e5;
650
+ var estimatePatternSegments = (rings, pattern2, scale3) => {
651
+ if (scale3 <= 0) return 0;
652
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
653
+ for (const ring of rings)
654
+ for (const p of ring) {
655
+ if (p.x < minX) minX = p.x;
656
+ if (p.x > maxX) maxX = p.x;
657
+ if (p.y < minY) minY = p.y;
658
+ if (p.y > maxY) maxY = p.y;
659
+ }
660
+ if (minX > maxX) return 0;
661
+ const diag = Math.hypot(maxX - minX, maxY - minY);
662
+ let count = 0;
663
+ for (const fam of pattern2.lines) {
664
+ const perp2 = Math.abs(fam.deltaY) * scale3;
665
+ if (perp2 <= 1e-9) continue;
666
+ const lines = diag / perp2 + 1;
667
+ const period = (fam.dashesMm?.reduce((a, b) => a + Math.abs(b), 0) ?? 0) * scale3;
668
+ const perLine = period > 1e-9 ? diag / period + 1 : 1;
669
+ count += lines * perLine;
670
+ }
671
+ return count;
672
+ };
673
+ var computePatternGeometry = (rings, pattern2, angle2, scale3, origin = { x: 0, y: 0 }) => {
674
+ const out = [];
675
+ if (scale3 <= 0) return out;
676
+ const c = Math.cos(angle2);
677
+ const s = Math.sin(angle2);
678
+ for (const fam of pattern2.lines) {
679
+ if (Math.abs(fam.deltaY) * scale3 <= 1e-9) continue;
680
+ const ox = fam.origin.x * scale3;
681
+ const oy = fam.origin.y * scale3;
682
+ const segs = computeHatchFamily(rings, {
683
+ angle: angle2 + fam.angle,
684
+ origin: { x: ox * c - oy * s + origin.x, y: ox * s + oy * c + origin.y },
685
+ deltaAlong: fam.deltaX * scale3,
686
+ deltaPerp: fam.deltaY * scale3,
687
+ dashesMm: fam.dashesMm?.map((d) => d * scale3)
688
+ });
689
+ for (const seg of segs) out.push(seg);
690
+ }
691
+ return out;
692
+ };
693
+ var PatternLineSchema = z2.object({
694
+ /** Radians CCW, in the pattern's own frame. */
695
+ angle: z2.number(),
696
+ /** Base point, mm. */
697
+ origin: Vec2Schema,
698
+ /** Shift ALONG the line between successive lines (dash phase), mm. */
699
+ deltaX: z2.number(),
700
+ /** Perpendicular spacing between successive lines, mm. */
701
+ deltaY: z2.number().positive(),
702
+ /** Dash cycle along the line: >0 dash, <0 gap, 0 dot; absent = solid. */
703
+ dashesMm: z2.array(z2.number()).optional()
704
+ });
705
+ var HatchPatternSchema = z2.object({
706
+ /** Stored on fills as `fill.name`; case-insensitive, unique per document. */
707
+ name: z2.string().min(1),
708
+ description: z2.string().optional(),
709
+ lines: z2.array(PatternLineSchema).min(1)
710
+ });
711
+ var hatchPatternKey = (name) => normalizeName(name);
712
+ var resolveHatchPattern = (name, hatchPatterns) => {
713
+ if (!name) return void 0;
714
+ if (hatchPatterns && hatchPatterns.length > 0) {
715
+ const key = normalizeName(name);
716
+ const own = hatchPatterns.find((p) => normalizeName(p.name) === key);
717
+ if (own) return own;
718
+ }
719
+ return getHatchPattern(name);
720
+ };
721
+
722
+ // ../core/src/export/dxf/exportDxf.ts
723
+ import {
724
+ DxfWriter,
725
+ Units,
726
+ LayerFlags,
727
+ LWPolylineFlags,
728
+ TextHorizontalAlignment,
729
+ TextVerticalAlignment,
730
+ HatchBoundaryPaths,
731
+ HatchPolylineBoundary,
732
+ HatchPredefinedPatterns,
733
+ pattern
734
+ } from "@tarikjabiri/dxf";
735
+
736
+ // ../core/src/model/objects.ts
737
+ import { z as z3 } from "zod";
738
+
43
739
  // ../core/src/model/fonts.ts
44
740
  var DEFAULT_FONT = "arial";
45
741
  var FONTS = {
@@ -172,102 +868,185 @@ var fontTypeface = (def) => def.cssStack.split(",")[0].replace(/["']/g, "").trim
172
868
  var FONT_OPTIONS = Object.values(
173
869
  FONTS
174
870
  ).filter((f) => f.id !== "osifont-italic").map((f) => ({ value: f.id, label: f.label }));
871
+ var SHX_STEMS = /* @__PURE__ */ new Set([
872
+ "txt",
873
+ "monotxt",
874
+ "simplex",
875
+ "complex",
876
+ "romans",
877
+ "romand",
878
+ "romanc",
879
+ "romant",
880
+ "italic",
881
+ "italicc",
882
+ "italict",
883
+ "scripts",
884
+ "scriptc",
885
+ "gothice",
886
+ "gothicg",
887
+ "gothici",
888
+ "greeks",
889
+ "greekc",
890
+ "isocp",
891
+ "isocp2",
892
+ "isocp3",
893
+ "isoct",
894
+ "isoct2",
895
+ "isoct3",
896
+ "iso",
897
+ "amgdt",
898
+ "gdt",
899
+ "symap",
900
+ "syastro",
901
+ "symath",
902
+ "symeteo",
903
+ "symusic",
904
+ "bigfont",
905
+ "ltypeshp",
906
+ "swiss",
907
+ "swissb",
908
+ "swissi",
909
+ "swissbi",
910
+ "sansserif",
911
+ "sanss",
912
+ "sansserifb",
913
+ "sanssb",
914
+ "arc",
915
+ "ic-romans",
916
+ "ic-simplex",
917
+ "txtalt"
918
+ ]);
919
+ var fontFromDxfStyle = (fontFile, typeface) => {
920
+ const file = (fontFile ?? "").trim();
921
+ const stem = file.replace(/\.[a-z0-9]+$/i, "").toLowerCase();
922
+ const family = (typeface ?? "").trim();
923
+ const familyLower = family.toLowerCase();
924
+ for (const def of Object.values(FONTS)) {
925
+ if (file && def.dxfFontFile.toLowerCase() === file.toLowerCase() || familyLower && fontTypeface(def).toLowerCase() === familyLower)
926
+ return def.id === "osifont-italic" ? { font: "osifont", italic: true } : { font: def.id };
927
+ }
928
+ const arialFace = /^arial(bd|bi|i)$/.exec(stem);
929
+ if (arialFace)
930
+ return {
931
+ font: "arial",
932
+ ...arialFace[1].includes("b") ? { bold: true } : {},
933
+ ...arialFace[1].includes("i") ? { italic: true } : {}
934
+ };
935
+ const key = `${familyLower} ${stem}`;
936
+ if (/narrow|condensed|arialn/.test(key)) return { font: "roboto-condensed" };
937
+ if (/mono|courier|\bcour\b|consola|lucon|menlo/.test(key))
938
+ return { font: "jetbrains-mono" };
939
+ if (/times|georgia|cambria|garamond|serif|\bbook/.test(key))
940
+ return { font: "source-serif" };
941
+ if (/comic/.test(key)) return { font: "comic-neue" };
942
+ if (/\.shx$/i.test(file) || !file && !family || SHX_STEMS.has(stem))
943
+ return {
944
+ font: "osifont",
945
+ .../italic/.test(stem) ? { italic: true } : {}
946
+ };
947
+ return { font: family || stem };
948
+ };
175
949
 
176
950
  // ../core/src/model/objects.ts
177
951
  var PointSchema = ObjectBaseSchema.extend({
178
- type: z2.literal("point"),
952
+ type: z3.literal("point"),
179
953
  p: Vec2Schema
180
954
  });
181
955
  var LineSchema = ObjectBaseSchema.extend({
182
- type: z2.literal("line"),
956
+ type: z3.literal("line"),
183
957
  a: Vec2Schema,
184
958
  b: Vec2Schema
185
959
  });
186
- var PolylineVertexSchema = z2.preprocess(
960
+ var PolylineVertexSchema = z3.preprocess(
187
961
  (v) => Array.isArray(v) && v.length >= 2 ? { x: v[0], y: v[1], bulge: v[2] ?? 0 } : v,
188
- z2.object({ x: z2.number(), y: z2.number(), bulge: z2.number().default(0) })
962
+ z3.object({ x: z3.number(), y: z3.number(), bulge: z3.number().default(0) })
189
963
  );
190
964
  var PolylineSchema = ObjectBaseSchema.extend({
191
- type: z2.literal("polyline"),
192
- points: z2.array(PolylineVertexSchema).min(2),
193
- closed: z2.boolean().default(false)
965
+ type: z3.literal("polyline"),
966
+ points: z3.array(PolylineVertexSchema).min(2),
967
+ closed: z3.boolean().default(false)
194
968
  });
195
969
  var CircleSchema = ObjectBaseSchema.extend({
196
- type: z2.literal("circle"),
970
+ type: z3.literal("circle"),
197
971
  center: Vec2Schema,
198
- radius: z2.number().positive()
972
+ radius: z3.number().positive()
199
973
  });
200
974
  var ArcSchema = ObjectBaseSchema.extend({
201
- type: z2.literal("arc"),
975
+ type: z3.literal("arc"),
202
976
  center: Vec2Schema,
203
- radius: z2.number().positive(),
204
- startAngle: z2.number(),
205
- endAngle: z2.number()
977
+ radius: z3.number().positive(),
978
+ startAngle: z3.number(),
979
+ endAngle: z3.number()
206
980
  });
207
981
  var EllipseSchema = ObjectBaseSchema.extend({
208
- type: z2.literal("ellipse"),
982
+ type: z3.literal("ellipse"),
209
983
  center: Vec2Schema,
210
984
  majorAxis: Vec2Schema,
211
- ratio: z2.number().gt(0).max(1),
212
- startAngle: z2.number().default(0),
213
- endAngle: z2.number().default(Math.PI * 2)
985
+ ratio: z3.number().gt(0).max(1),
986
+ startAngle: z3.number().default(0),
987
+ endAngle: z3.number().default(Math.PI * 2)
214
988
  });
215
- var HatchLoopSchema = z2.object({
216
- points: z2.array(PolylineVertexSchema).min(3)
989
+ var HatchLoopSchema = z3.object({
990
+ points: z3.array(PolylineVertexSchema).min(3)
217
991
  });
218
- var HatchFillSchema = z2.discriminatedUnion("kind", [
219
- z2.object({ kind: z2.literal("solid") }),
220
- z2.object({
221
- kind: z2.literal("pattern"),
222
- name: z2.string(),
223
- angle: z2.number(),
224
- scale: z2.number().positive()
992
+ var HatchFillSchema = z3.discriminatedUnion("kind", [
993
+ z3.object({ kind: z3.literal("solid") }),
994
+ z3.object({
995
+ kind: z3.literal("pattern"),
996
+ name: z3.string(),
997
+ angle: z3.number(),
998
+ scale: z3.number().positive(),
999
+ /** Where the pattern frame is anchored, world mm (absent = the drawing
1000
+ * origin, like a hatch drawn here). Set by import for hatches that came
1001
+ * out of blocks — AutoCAD anchors a block's hatch to the block, so the
1002
+ * pattern phase follows the insertion — and carried by the transforms. */
1003
+ origin: Vec2Schema.optional()
225
1004
  }),
226
- z2.object({
227
- kind: z2.literal("lines"),
228
- angle: z2.number(),
229
- spacing: z2.number().positive()
1005
+ z3.object({
1006
+ kind: z3.literal("lines"),
1007
+ angle: z3.number(),
1008
+ spacing: z3.number().positive()
230
1009
  }),
231
- z2.object({
232
- kind: z2.literal("gradient"),
1010
+ z3.object({
1011
+ kind: z3.literal("gradient"),
233
1012
  /** linear = color1→color2 along `angle`; radial = color1 at the
234
1013
  * boundary's center fading to color2 at its edge. */
235
- shape: z2.enum(["linear", "radial"]).default("linear"),
236
- color1: z2.string().default("#808080"),
237
- color2: z2.string().default("#2d2d2d"),
1014
+ shape: z3.enum(["linear", "radial"]).default("linear"),
1015
+ color1: z3.string().default("#808080"),
1016
+ color2: z3.string().default("#2d2d2d"),
238
1017
  /** Direction, radians CCW: 0 = color1 on the left (linear only). */
239
- angle: z2.number().default(0)
1018
+ angle: z3.number().default(0)
240
1019
  })
241
1020
  ]);
242
1021
  var HatchSchema = ObjectBaseSchema.extend({
243
- type: z2.literal("hatch"),
244
- loops: z2.array(HatchLoopSchema).min(1),
1022
+ type: z3.literal("hatch"),
1023
+ loops: z3.array(HatchLoopSchema).min(1),
245
1024
  fill: HatchFillSchema.default({
246
1025
  kind: "lines",
247
1026
  angle: Math.PI / 4,
248
1027
  spacing: 5
249
1028
  }),
250
1029
  /** Pattern spacing multiplier (same idea as DimStyle.scale). */
251
- scale: z2.number().positive().default(1)
1030
+ scale: z3.number().positive().default(1)
252
1031
  });
253
- var ArrowTypeSchema = z2.enum(["filled", "open", "tick", "dot", "none"]);
254
- var TextStyleSchema = z2.object({
255
- id: z2.string().default(() => crypto.randomUUID()),
256
- name: z2.string().default("Standard"),
1032
+ var ArrowTypeSchema = z3.enum(["filled", "open", "tick", "dot", "none"]);
1033
+ var TextStyleSchema = z3.object({
1034
+ id: z3.string().default(() => crypto.randomUUID()),
1035
+ name: z3.string().default("Standard"),
257
1036
  /** Font id (model/fonts.ts). */
258
- font: z2.string().default("arial"),
1037
+ font: z3.string().default("arial"),
259
1038
  /** AutoCAD-style width factor: horizontal glyph scale. */
260
- widthFactor: z2.number().positive().default(1),
1039
+ widthFactor: z3.number().positive().default(1),
261
1040
  /** Bold / italic face (bundled outlines where the font has them, else
262
1041
  * synthesized — model/fonts.ts `fontFace`). Absent = regular. */
263
- bold: z2.boolean().optional(),
264
- italic: z2.boolean().optional(),
1042
+ bold: z3.boolean().optional(),
1043
+ italic: z3.boolean().optional(),
265
1044
  /** Cap height in mm (world), before the object's `scale`. */
266
- textHeight: z2.number().positive().default(2.5),
1045
+ textHeight: z3.number().positive().default(2.5),
267
1046
  /** Leader arrowhead style. */
268
1047
  arrowType: ArrowTypeSchema.default("filled"),
269
1048
  /** Leader arrowhead size (mm), before the object's `scale`. */
270
- arrowSize: z2.number().positive().default(2.5)
1049
+ arrowSize: z3.number().positive().default(2.5)
271
1050
  });
272
1051
  var DEFAULT_TEXT_STYLE = {
273
1052
  id: "standard",
@@ -278,14 +1057,14 @@ var DEFAULT_TEXT_STYLE = {
278
1057
  arrowType: "filled",
279
1058
  arrowSize: 2.5
280
1059
  };
281
- var TextStyleOverrideSchema = z2.object({
282
- font: z2.string().optional(),
283
- widthFactor: z2.number().positive().optional(),
284
- bold: z2.boolean().optional(),
285
- italic: z2.boolean().optional(),
286
- textHeight: z2.number().positive().optional(),
1060
+ var TextStyleOverrideSchema = z3.object({
1061
+ font: z3.string().optional(),
1062
+ widthFactor: z3.number().positive().optional(),
1063
+ bold: z3.boolean().optional(),
1064
+ italic: z3.boolean().optional(),
1065
+ textHeight: z3.number().positive().optional(),
287
1066
  arrowType: ArrowTypeSchema.optional(),
288
- arrowSize: z2.number().positive().optional()
1067
+ arrowSize: z3.number().positive().optional()
289
1068
  });
290
1069
  var textStyleFor = (obj, styles) => styles.find((s) => s.id === obj.styleId) ?? styles[0] ?? DEFAULT_TEXT_STYLE;
291
1070
  var resolveTextStyle = (obj, styles) => ({ ...textStyleFor(obj, styles), ...obj.styleOverride });
@@ -301,62 +1080,79 @@ var effectiveTextFont = (obj, styles) => {
301
1080
  widthFactor: o?.widthFactor ?? base.widthFactor
302
1081
  };
303
1082
  };
1083
+ var TextSpanSchema = z3.object({
1084
+ start: z3.number().int().min(0),
1085
+ end: z3.number().int().min(0),
1086
+ /** Font id (model/fonts.ts). */
1087
+ font: z3.string().optional(),
1088
+ bold: z3.boolean().optional(),
1089
+ italic: z3.boolean().optional(),
1090
+ underline: z3.boolean().optional(),
1091
+ /** Literal colour; absent = the text's own (possibly ByLayer) colour. */
1092
+ color: ColorSchema.optional(),
1093
+ /** × the text's cap height. */
1094
+ height: z3.number().positive().optional(),
1095
+ script: z3.enum(["sub", "super"]).optional()
1096
+ });
304
1097
  var TextSchema = ObjectBaseSchema.extend({
305
- type: z2.literal("text"),
1098
+ type: z3.literal("text"),
306
1099
  position: Vec2Schema,
307
- content: z2.string(),
1100
+ content: z3.string(),
1101
+ /** Character formatting over `content`; absent = all plain. Replacing
1102
+ * `content` without new spans drops them (offsets would be stale). */
1103
+ spans: z3.array(TextSpanSchema).optional(),
308
1104
  /** Arrow polylines pointing at this text (arrowhead at [0]). */
309
- leaderLines: z2.array(z2.array(Vec2Schema).min(2)).default([]),
1105
+ leaderLines: z3.array(z3.array(Vec2Schema).min(2)).default([]),
310
1106
  /**
311
1107
  * Max text box width in mm (before `scale`) — the word-wrap boundary and the
312
1108
  * frame width. Undefined = no wrapping (lines break only on explicit
313
1109
  * newlines). Maps to the DXF MTEXT reference rectangle width.
314
1110
  */
315
- width: z2.number().positive().optional(),
316
- hAlign: z2.enum(["left", "center", "right"]).default("center"),
317
- vAlign: z2.enum(["bottom", "center", "top"]).default("center"),
1111
+ width: z3.number().positive().optional(),
1112
+ hAlign: z3.enum(["left", "center", "right"]).default("center"),
1113
+ vAlign: z3.enum(["bottom", "center", "top"]).default("center"),
318
1114
  /** Border drawn around the text: a rectangle, a circle, a slot (rounded
319
1115
  * ends), a line under the text or under its first line. */
320
- frame: z2.enum(["none", "rectangle", "circle", "slot", "bottom", "firstLine"]).optional(),
1116
+ frame: z3.enum(["none", "rectangle", "circle", "slot", "bottom", "firstLine"]).optional(),
321
1117
  /** The document text style this text follows (absent/dangling = the
322
1118
  * document's first style). */
323
- styleId: z2.string().optional(),
1119
+ styleId: z3.string().optional(),
324
1120
  /** Style keys the user set on THIS text (font, height, arrows, …);
325
1121
  * absent = inherit the referenced style. */
326
1122
  styleOverride: TextStyleOverrideSchema.optional(),
327
- rotation: z2.number().default(0),
1123
+ rotation: z3.number().default(0),
328
1124
  /** Size multiplier for height + arrow size (same idea as
329
1125
  * Dimension.scale). */
330
- scale: z2.number().positive().default(1)
1126
+ scale: z3.number().positive().default(1)
331
1127
  });
332
- var DimensionKindSchema = z2.enum([
1128
+ var DimensionKindSchema = z3.enum([
333
1129
  "linear",
334
1130
  "angular",
335
1131
  "radial",
336
1132
  "diameter"
337
1133
  ]);
338
- var DimStyleSchema = z2.object({
339
- id: z2.string().default(() => crypto.randomUUID()),
340
- name: z2.string().default("Standard"),
1134
+ var DimStyleSchema = z3.object({
1135
+ id: z3.string().default(() => crypto.randomUUID()),
1136
+ name: z3.string().default("Standard"),
341
1137
  /** Font id for dimension text (model/fonts.ts). */
342
- font: z2.string().default("arial"),
1138
+ font: z3.string().default("arial"),
343
1139
  /** Horizontal glyph scale for dimension text. */
344
- widthFactor: z2.number().positive().default(1),
1140
+ widthFactor: z3.number().positive().default(1),
345
1141
  /** Bold / italic dimension text (same faces as TextStyle). */
346
- bold: z2.boolean().optional(),
347
- italic: z2.boolean().optional(),
348
- textHeight: z2.number().positive().default(2.5),
1142
+ bold: z3.boolean().optional(),
1143
+ italic: z3.boolean().optional(),
1144
+ textHeight: z3.number().positive().default(2.5),
349
1145
  /** Dimension line → text center distance (mm, × scale; DXF DIMGAP-ish). */
350
- textOffset: z2.number().default(1.8),
1146
+ textOffset: z3.number().default(1.8),
351
1147
  arrowType: ArrowTypeSchema.default("tick"),
352
- arrowSize: z2.number().positive().default(2.5),
1148
+ arrowSize: z3.number().positive().default(2.5),
353
1149
  /** "long" runs each extension line from the measured point to the
354
1150
  * dimension line; "short" draws only a 2×overshoot tick across it. */
355
- extensionType: z2.enum(["long", "short"]).default("long"),
356
- extensionOffset: z2.number().default(1),
357
- extensionOvershoot: z2.number().default(1.25),
1151
+ extensionType: z3.enum(["long", "short"]).default("long"),
1152
+ extensionOffset: z3.number().default(1),
1153
+ extensionOvershoot: z3.number().default(1.25),
358
1154
  /** Decimal places for the measurement text. */
359
- precision: z2.number().int().min(0).max(6).default(1)
1155
+ precision: z3.number().int().min(0).max(6).default(1)
360
1156
  });
361
1157
  var DEFAULT_DIM_STYLE = {
362
1158
  id: "standard",
@@ -372,19 +1168,19 @@ var DEFAULT_DIM_STYLE = {
372
1168
  extensionOvershoot: 1,
373
1169
  precision: 1
374
1170
  };
375
- var DimStyleOverrideSchema = z2.object({
376
- font: z2.string().optional(),
377
- widthFactor: z2.number().positive().optional(),
378
- bold: z2.boolean().optional(),
379
- italic: z2.boolean().optional(),
380
- textHeight: z2.number().positive().optional(),
381
- textOffset: z2.number().optional(),
1171
+ var DimStyleOverrideSchema = z3.object({
1172
+ font: z3.string().optional(),
1173
+ widthFactor: z3.number().positive().optional(),
1174
+ bold: z3.boolean().optional(),
1175
+ italic: z3.boolean().optional(),
1176
+ textHeight: z3.number().positive().optional(),
1177
+ textOffset: z3.number().optional(),
382
1178
  arrowType: ArrowTypeSchema.optional(),
383
- arrowSize: z2.number().positive().optional(),
384
- extensionType: z2.enum(["long", "short"]).optional(),
385
- extensionOffset: z2.number().optional(),
386
- extensionOvershoot: z2.number().optional(),
387
- precision: z2.number().int().min(0).max(6).optional()
1179
+ arrowSize: z3.number().positive().optional(),
1180
+ extensionType: z3.enum(["long", "short"]).optional(),
1181
+ extensionOffset: z3.number().optional(),
1182
+ extensionOvershoot: z3.number().optional(),
1183
+ precision: z3.number().int().min(0).max(6).optional()
388
1184
  });
389
1185
  var unknownStyleOverrideKeys = (input) => {
390
1186
  const so = input.styleOverride;
@@ -403,48 +1199,114 @@ var assertKnownStyleOverrideKeys = (input, where) => {
403
1199
  };
404
1200
  var dimStyleFor = (obj, styles) => styles.find((s) => s.id === obj.styleId) ?? styles[0] ?? DEFAULT_DIM_STYLE;
405
1201
  var dimFontFace = (style) => fontFace(style.font, style.bold, style.italic);
406
- var DimensionSchema = ObjectBaseSchema.extend({
407
- type: z2.literal("dimension"),
1202
+ var DIM_VALUE_TOKEN = "{value}";
1203
+ var DimLabelSchema = z3.object({
1204
+ /** mm along the dimension line from the segment's origin (linear: the
1205
+ * foot of the segment's first point; angular: where ray a meets the arc,
1206
+ * as arc length; radial/diameter: the center). Undefined = automatic
1207
+ * placement: centered when the text fits, else outside. */
1208
+ along: z3.number().optional(),
1209
+ /** mm perpendicular to the dimension line, positive on the side the
1210
+ * dimension line is on (linear/angular) or to the left of `along`
1211
+ * (radial/diameter). Undefined or 0 = on the line; otherwise the text
1212
+ * floats, with a leader when it no longer overlaps its segment. */
1213
+ across: z3.number().optional(),
1214
+ /** Text; `{value}` is replaced by the formatted measurement. Undefined =
1215
+ * the kind's default template ("{value}", "{value}°", "R{value}",
1216
+ * "Ø{value}"). */
1217
+ content: z3.string().optional(),
1218
+ /** Character formatting over `content` (the TEMPLATE: a span covering
1219
+ * `{value}` formats the measurement). Needs `content` set. */
1220
+ spans: z3.array(TextSpanSchema).optional()
1221
+ });
1222
+ var DimensionShapeSchema = ObjectBaseSchema.extend({
1223
+ type: z3.literal("dimension"),
408
1224
  kind: DimensionKindSchema.default("linear"),
409
1225
  /**
410
- * Measured points. Linear dims with MORE than two points render as a
411
- * CHAIN: one measurement per consecutive pair, sharing the dimension
412
- * line (text placement is derived — see `offset`, not a text anchor).
1226
+ * Measured points. linear: 2+ (more than two render as a CHAIN — one
1227
+ * measurement per consecutive pair sharing the dimension line);
1228
+ * angular: [vertex, a, b]; radial: [center, curve point]; diameter:
1229
+ * [p1, p2] opposite on the curve.
1230
+ */
1231
+ points: z3.array(Vec2Schema).min(2),
1232
+ /**
1233
+ * linear only: the measuring direction, radians CCW from +X (DXF's
1234
+ * "rotated" dimension, code 50). Undefined = aligned to
1235
+ * points[0] → points[last] (DXF "aligned"), so dragging a point turns the
1236
+ * dimension; set, the direction is locked and a point drag only changes
1237
+ * the measurement.
413
1238
  */
414
- points: z2.array(Vec2Schema).min(1),
1239
+ rotation: z3.number().optional(),
415
1240
  /**
416
- * Dimension line placement, anchored at the kind's base point (points[0];
417
- * diameter: the points' midpoint): anchor + offset lies on the dimension
418
- * line (angular: on the arc — its length is the arc radius; radial /
419
- * diameter: at the text). The DIRECTION is set at creation and preserved
420
- * afterwards — the offset grip only rescales the length.
1241
+ * linear: signed distance of the dimension line from points[0] along the
1242
+ * LEFT perpendicular of the measuring direction. angular: the arc radius,
1243
+ * positive for the arc running CCW from a to b, negative for the
1244
+ * complementary arc. radial/diameter: absent — the line's direction comes
1245
+ * from the points and the text position is labels[0].along.
421
1246
  */
422
- offset: Vec2Schema,
423
- textOverride: z2.string().optional(),
1247
+ offset: z3.number().optional(),
1248
+ /** One label per measured segment (linear chain: points.length − 1,
1249
+ * every other kind: 1). Missing or short = automatic. */
1250
+ labels: z3.array(DimLabelSchema).default([]),
424
1251
  /** The document dimension style this dimension follows (absent/dangling =
425
1252
  * the document's first style). */
426
- styleId: z2.string().optional(),
1253
+ styleId: z3.string().optional(),
427
1254
  styleOverride: DimStyleOverrideSchema.optional(),
428
1255
  /** Annotation size multiplier (text/arrows/offsets — same idea as
429
1256
  * Text.scale; rides as DIMSCALE in DXF). */
430
- scale: z2.number().positive().default(1)
1257
+ scale: z3.number().positive().default(1)
431
1258
  });
1259
+ var dimensionRefinement = (d, ctx) => {
1260
+ const need = d.kind === "angular" ? 3 : 2;
1261
+ if (d.points.length < need)
1262
+ ctx.addIssue({
1263
+ code: "custom",
1264
+ path: ["points"],
1265
+ message: `a ${d.kind} dimension needs at least ${need} points`
1266
+ });
1267
+ if (d.kind !== "linear" && d.kind !== "angular") {
1268
+ if (d.offset !== void 0)
1269
+ ctx.addIssue({
1270
+ code: "custom",
1271
+ path: ["offset"],
1272
+ message: `offset does not apply to a ${d.kind} dimension (the text position is labels[0].along)`
1273
+ });
1274
+ } else if (d.offset === void 0)
1275
+ ctx.addIssue({
1276
+ code: "custom",
1277
+ path: ["offset"],
1278
+ message: `a ${d.kind} dimension needs offset`
1279
+ });
1280
+ if (d.kind !== "linear" && d.rotation !== void 0)
1281
+ ctx.addIssue({
1282
+ code: "custom",
1283
+ path: ["rotation"],
1284
+ message: "rotation applies to linear dimensions only"
1285
+ });
1286
+ };
1287
+ var DimensionSchema = DimensionShapeSchema.superRefine(dimensionRefinement);
1288
+ var DIM_DEFAULT_CONTENT = {
1289
+ linear: DIM_VALUE_TOKEN,
1290
+ angular: `${DIM_VALUE_TOKEN}\xB0`,
1291
+ radial: `R${DIM_VALUE_TOKEN}`,
1292
+ diameter: `\xD8${DIM_VALUE_TOKEN}`
1293
+ };
432
1294
  var BlockSchema = ObjectBaseSchema.extend({
433
- type: z2.literal("block"),
434
- definitionId: z2.string(),
1295
+ type: z3.literal("block"),
1296
+ definitionId: z3.string(),
435
1297
  /** Applied LAST, about the pivot (the first point input). */
436
- rotation: z2.number().default(0),
437
- scale: z2.number().positive().default(1),
1298
+ rotation: z3.number().default(0),
1299
+ scale: z3.number().positive().default(1),
438
1300
  /** Per-instance parameter values (merged over the definition defaults). */
439
- params: z2.record(
440
- z2.string(),
441
- z2.union([
442
- z2.number(),
443
- z2.string(),
444
- z2.boolean(),
1301
+ params: z3.record(
1302
+ z3.string(),
1303
+ z3.union([
1304
+ z3.number(),
1305
+ z3.string(),
1306
+ z3.boolean(),
445
1307
  // numberList / stringList parameters.
446
- z2.array(z2.number()),
447
- z2.array(z2.string())
1308
+ z3.array(z3.number()),
1309
+ z3.array(z3.string())
448
1310
  ])
449
1311
  ).default({}),
450
1312
  /**
@@ -456,63 +1318,73 @@ var BlockSchema = ObjectBaseSchema.extend({
456
1318
  * Transforms map the points and the script recomputes, which is what
457
1319
  * makes mirror correct for script-derived geometry.
458
1320
  */
459
- inputs: z2.array(z2.union([Vec2Schema, z2.string()])).default([])
1321
+ inputs: z3.array(z3.union([Vec2Schema, z3.string()])).default([])
460
1322
  });
461
1323
  var CommentSchema = ObjectBaseSchema.extend({
462
- type: z2.literal("comment"),
1324
+ type: z3.literal("comment"),
463
1325
  /** Tail tip of the bubble (world/sheet mm). */
464
1326
  position: Vec2Schema,
465
- text: z2.string().default(""),
466
- tags: z2.array(z2.string()).default([])
1327
+ text: z3.string().default(""),
1328
+ tags: z3.array(z3.string()).default([])
467
1329
  });
468
- var TableCellSchema = z2.object({
469
- /** Plain text; "\n" = line break. Long lines wrap at the cell width. */
470
- text: z2.string().default(""),
1330
+ var cellInput = (v) => {
1331
+ if (typeof v === "string") return { content: v };
1332
+ if (v && typeof v === "object" && "text" in v && !("content" in v)) {
1333
+ const { text, ...rest } = v;
1334
+ return { ...rest, content: text };
1335
+ }
1336
+ return v;
1337
+ };
1338
+ var TableCellSchema = z3.object({
1339
+ /** Plain text; "\n" = line break. Long lines wrap at the cell width.
1340
+ * (Named `text` before document v22 — still read, see `cellInput`.) */
1341
+ content: z3.string().default(""),
1342
+ /** Character formatting over `content` (core/table.ts `cellWithContent`
1343
+ * keeps it consistent when the content changes). */
1344
+ spans: z3.array(TextSpanSchema).optional(),
471
1345
  /** Merge, set on the ANCHOR (top-left) cell. Covered cells stay in the
472
1346
  * grid with their content ignored — AutoCAD's model. */
473
- span: z2.object({
474
- cols: z2.number().int().min(1),
475
- rows: z2.number().int().min(1)
1347
+ span: z3.object({
1348
+ cols: z3.number().int().min(1),
1349
+ rows: z3.number().int().min(1)
476
1350
  }).optional(),
477
1351
  /** Where the text sits in the cell; absent = left / center. */
478
- hAlign: z2.enum(["left", "center", "right"]).optional(),
479
- vAlign: z2.enum(["top", "center", "bottom"]).optional(),
480
- styleId: z2.string().optional(),
1352
+ hAlign: z3.enum(["left", "center", "right"]).optional(),
1353
+ vAlign: z3.enum(["top", "center", "bottom"]).optional(),
1354
+ styleId: z3.string().optional(),
481
1355
  styleOverride: TextStyleOverrideSchema.optional(),
482
1356
  /** Text color; absent = the table's. */
483
1357
  color: ColorSchema.optional()
484
1358
  });
485
- var TableSchema = ObjectBaseSchema.extend({
486
- type: z2.literal("table"),
1359
+ var TableShapeSchema = ObjectBaseSchema.extend({
1360
+ type: z3.literal("table"),
487
1361
  position: Vec2Schema,
488
- rotation: z2.number().default(0),
489
- scale: z2.number().positive().default(1),
1362
+ rotation: z3.number().default(0),
1363
+ scale: z3.number().positive().default(1),
490
1364
  /** Column widths (mm). */
491
- columns: z2.array(z2.number().positive()).min(1),
1365
+ columns: z3.array(z3.number().positive()).min(1),
492
1366
  /** Per-row settings. Input may give a row COUNT (all auto-height). */
493
- rows: z2.preprocess(
1367
+ rows: z3.preprocess(
494
1368
  (v) => typeof v === "number" ? Array.from({ length: v }, () => ({})) : v,
495
- z2.array(z2.object({ minHeight: z2.number().positive().optional() })).min(1)
1369
+ z3.array(z3.object({ minHeight: z3.number().positive().optional() })).min(1)
496
1370
  ),
497
1371
  /** Row-major: `cells[row][column]`, exactly rows × columns. Input may
498
1372
  * give a cell as a plain string (= `{ text }`). */
499
- cells: z2.array(
500
- z2.array(
501
- z2.preprocess(
502
- (v) => typeof v === "string" ? { text: v } : v,
503
- TableCellSchema
504
- )
1373
+ cells: z3.array(
1374
+ z3.array(
1375
+ z3.preprocess(cellInput, TableCellSchema)
505
1376
  )
506
1377
  ),
507
1378
  /** Rows above the heavier header rule. */
508
- headerRows: z2.number().int().min(0).default(0),
1379
+ headerRows: z3.number().int().min(0).default(0),
509
1380
  /** Text inset from the cell edges (mm). */
510
- cellMargin: z2.number().min(0).default(1),
511
- borders: z2.enum(["all", "outer", "rows", "none"]).default("all"),
1381
+ cellMargin: z3.number().min(0).default(1),
1382
+ borders: z3.enum(["all", "outer", "rows", "none"]).default("all"),
512
1383
  /** Document text style for every cell (cells may override). */
513
- styleId: z2.string().optional(),
1384
+ styleId: z3.string().optional(),
514
1385
  styleOverride: TextStyleOverrideSchema.optional()
515
- }).superRefine((t, ctx) => {
1386
+ });
1387
+ var tableRefinement = (t, ctx) => {
516
1388
  const issue = (message) => ctx.addIssue({ code: "custom", message });
517
1389
  const nr = t.rows.length;
518
1390
  const nc = t.columns.length;
@@ -536,38 +1408,39 @@ var TableSchema = ObjectBaseSchema.extend({
536
1408
  }
537
1409
  })
538
1410
  );
539
- });
1411
+ };
1412
+ var TableSchema = TableShapeSchema.superRefine(tableRefinement);
540
1413
  var blockPivot = (b) => {
541
1414
  for (const v of b.inputs) if (typeof v !== "string") return v;
542
1415
  return { x: 0, y: 0 };
543
1416
  };
544
1417
  var ViewportSchema = ObjectBaseSchema.extend({
545
- type: z2.literal("viewport"),
1418
+ type: z3.literal("viewport"),
546
1419
  /** Placement on the sheet, mm, origin bottom-left of sheet. */
547
- rect: z2.object({
548
- x: z2.number(),
549
- y: z2.number(),
550
- w: z2.number().positive(),
551
- h: z2.number().positive()
1420
+ rect: z3.object({
1421
+ x: z3.number(),
1422
+ y: z3.number(),
1423
+ w: z3.number().positive(),
1424
+ h: z3.number().positive()
552
1425
  }),
553
1426
  /** World point shown at the center of the rect. */
554
1427
  modelCenter: Vec2Schema.default({ x: 0, y: 0 }),
555
1428
  /** Drawing-scale denominator (model mm per paper mm); 1:50 => 50. */
556
- scale: z2.number().positive().default(1),
1429
+ scale: z3.number().positive().default(1),
557
1430
  /** Pinned: no pan/zoom-through, no move/resize grips. */
558
- locked: z2.boolean().default(false),
559
- hiddenLayerIds: z2.array(z2.string()).default([])
1431
+ locked: z3.boolean().default(false),
1432
+ hiddenLayerIds: z3.array(z3.string()).default([])
560
1433
  });
561
1434
  var ImageSchema = ObjectBaseSchema.extend({
562
- type: z2.literal("image"),
1435
+ type: z3.literal("image"),
563
1436
  /** Key into `document.assets`. */
564
- assetId: z2.string(),
1437
+ assetId: z3.string(),
565
1438
  /** Bottom-left corner (world/sheet mm) — rotation pivots here. */
566
1439
  origin: Vec2Schema,
567
- width: z2.number().positive(),
568
- height: z2.number().positive(),
1440
+ width: z3.number().positive(),
1441
+ height: z3.number().positive(),
569
1442
  /** Radians CCW about `origin`. */
570
- rotation: z2.number().default(0)
1443
+ rotation: z3.number().default(0)
571
1444
  // (opacity moved to ObjectBaseSchema — every object paints with it now;
572
1445
  // documents that stored it per-image parse unchanged.)
573
1446
  });
@@ -579,54 +1452,54 @@ var REFERENCE_FORMATS = [
579
1452
  "png",
580
1453
  "jpg"
581
1454
  ];
582
- var ReferenceSourceSchema = z2.discriminatedUnion("kind", [
583
- z2.object({
584
- kind: z2.literal("path"),
585
- path: z2.string().min(1),
586
- cacheKey: z2.string().min(1).optional()
1455
+ var ReferenceSourceSchema = z3.discriminatedUnion("kind", [
1456
+ z3.object({
1457
+ kind: z3.literal("path"),
1458
+ path: z3.string().min(1),
1459
+ cacheKey: z3.string().min(1).optional()
587
1460
  }),
588
- z2.object({ kind: z2.literal("url"), url: z2.string().url() })
1461
+ z3.object({ kind: z3.literal("url"), url: z3.string().url() })
589
1462
  ]);
590
1463
  var ReferenceSchema = ObjectBaseSchema.extend({
591
- type: z2.literal("reference"),
592
- format: z2.enum(REFERENCE_FORMATS),
593
- name: z2.string().min(1),
1464
+ type: z3.literal("reference"),
1465
+ format: z3.enum(REFERENCE_FORMATS),
1466
+ name: z3.string().min(1),
594
1467
  source: ReferenceSourceSchema,
595
1468
  position: Vec2Schema,
596
1469
  /** Radians CCW about `position`. */
597
- rotation: z2.number().default(0),
598
- scale: z2.number().positive().default(1),
599
- mirrored: z2.boolean().optional(),
1470
+ rotation: z3.number().default(0),
1471
+ scale: z3.number().positive().default(1),
1472
+ mirrored: z3.boolean().optional(),
600
1473
  /** PDF: the 1-based page drawn, and how (vector lines + text / raster). */
601
- pdfPage: z2.number().int().positive().optional(),
602
- pdfMode: z2.enum(["vector", "raster"]).optional(),
1474
+ pdfPage: z3.number().int().positive().optional(),
1475
+ pdfMode: z3.enum(["vector", "raster"]).optional(),
603
1476
  /** The FILE's layers not drawn, by name (stable across reloads). */
604
- hiddenLayers: z2.array(z2.string()).default([]),
1477
+ hiddenLayers: z3.array(z3.string()).default([]),
605
1478
  /** Switched off in the References panel: not drawn, picked or snapped. */
606
- hidden: z2.boolean().optional(),
1479
+ hidden: z3.boolean().optional(),
607
1480
  /** Locked in the References panel: drawn and snapped, but never hovered
608
1481
  * or picked on the canvas — select it from the panel instead. */
609
- locked: z2.boolean().optional(),
1482
+ locked: z3.boolean().optional(),
610
1483
  /** Crop rectangle in FILE coordinates: only content inside is drawn and
611
1484
  * snapped (AutoCAD's XCLIP, rectangular). */
612
- clip: z2.object({
613
- minX: z2.number(),
614
- minY: z2.number(),
615
- maxX: z2.number(),
616
- maxY: z2.number()
1485
+ clip: z3.object({
1486
+ minX: z3.number(),
1487
+ minY: z3.number(),
1488
+ maxX: z3.number(),
1489
+ maxY: z3.number()
617
1490
  }).optional(),
618
1491
  /**
619
1492
  * The content's bounds in file coordinates, recorded on each successful
620
1493
  * load: a missing file still has a frame to draw, pick and snap to.
621
1494
  */
622
- extent: z2.object({
623
- minX: z2.number(),
624
- minY: z2.number(),
625
- maxX: z2.number(),
626
- maxY: z2.number()
1495
+ extent: z3.object({
1496
+ minX: z3.number(),
1497
+ minY: z3.number(),
1498
+ maxX: z3.number(),
1499
+ maxY: z3.number()
627
1500
  }).optional()
628
1501
  });
629
- var ModelObjectSchema = z2.discriminatedUnion("type", [
1502
+ var ModelObjectSchema = z3.discriminatedUnion("type", [
630
1503
  PointSchema,
631
1504
  LineSchema,
632
1505
  PolylineSchema,
@@ -644,6 +1517,9 @@ var ModelObjectSchema = z2.discriminatedUnion("type", [
644
1517
  ReferenceSchema
645
1518
  ]);
646
1519
 
1520
+ // ../core/src/model/lineTypes.ts
1521
+ import { z as z4 } from "zod";
1522
+
647
1523
  // ../core/src/model/builtinLin.ts
648
1524
  var BUILTIN_LIN = `;; Linea built-in line type library.
649
1525
  ;;
@@ -799,7 +1675,7 @@ var CURATED = [
799
1675
  dxfName: "DIVIDE"
800
1676
  }
801
1677
  ];
802
- var normalizeName = (name) => name.trim().toUpperCase().replace(/[\s.-]+/g, "_");
1678
+ var normalizeName2 = (name) => name.trim().toUpperCase().replace(/[\s.-]+/g, "_");
803
1679
  var asciiPreview = (dashesMm) => {
804
1680
  const WIDTH = 15;
805
1681
  if (!dashesMm.length) return "_".repeat(WIDTH * 0.8);
@@ -820,9 +1696,9 @@ var withPreview = (p) => {
820
1696
  return { ...p, label: `${p.label} ${asciiPreview(p.dashesMm)}` };
821
1697
  };
822
1698
  var BUILT_IN = CURATED.map(withPreview);
823
- var BY_NAME = new Map(BUILT_IN.map((p) => [normalizeName(p.name), p]));
1699
+ var BY_NAME = new Map(BUILT_IN.map((p) => [normalizeName2(p.name), p]));
824
1700
  for (const p of parseLinDefinitions(BUILTIN_LIN)) {
825
- const key = normalizeName(p.name);
1701
+ const key = normalizeName2(p.name);
826
1702
  if (BY_NAME.has(key)) continue;
827
1703
  const pattern2 = withPreview({
828
1704
  name: p.name,
@@ -840,15 +1716,40 @@ var scaleVariantBase = (name) => {
840
1716
  };
841
1717
  for (const p of BUILT_IN) {
842
1718
  const base = scaleVariantBase(p.name);
843
- const target = base ? BY_NAME.get(normalizeName(base)) : void 0;
1719
+ const target = base ? BY_NAME.get(normalizeName2(base)) : void 0;
844
1720
  if (target && target !== p) p.hidden = true;
845
1721
  }
846
1722
  var LINE_TYPE_NAMES = BUILT_IN.filter((p) => !p.hidden).map(
847
1723
  (p) => p.name
848
1724
  );
849
1725
  var LINE_TYPE_DEFS = BUILT_IN;
850
- var getLineType = (name) => BY_NAME.get(normalizeName(name));
1726
+ var getLineType = (name) => BY_NAME.get(normalizeName2(name));
851
1727
  var renderDashes = (pattern2) => pattern2.dashesMm.length ? pattern2.dashesMm.map((v) => v === 0 ? 0.1 : Math.abs(v)) : void 0;
1728
+ var LineTypeSchema = z4.object({
1729
+ /** Stored on objects/layers as `lineType`; case-insensitive, unique per
1730
+ * document. */
1731
+ name: z4.string().min(1),
1732
+ description: z4.string().optional(),
1733
+ /** Signed dash cycle, mm per 1 mm of line width: >0 dash, <0 gap, 0 dot.
1734
+ * Empty = continuous. */
1735
+ dashesMm: z4.array(z4.number())
1736
+ });
1737
+ var lineTypeKey = (name) => normalizeName2(name);
1738
+ var lineTypeAsPattern = (t) => ({
1739
+ name: t.name,
1740
+ label: t.description ?? t.name,
1741
+ dashesMm: t.dashesMm,
1742
+ dxfName: t.name.toUpperCase()
1743
+ });
1744
+ var resolveLineType = (name, lineTypes) => {
1745
+ if (!name) return void 0;
1746
+ if (lineTypes && lineTypes.length > 0) {
1747
+ const key = normalizeName2(name);
1748
+ const own = lineTypes.find((t) => normalizeName2(t.name) === key);
1749
+ if (own) return lineTypeAsPattern(own);
1750
+ }
1751
+ return getLineType(name);
1752
+ };
852
1753
 
853
1754
  // ../core/src/geometry/vec2.ts
854
1755
  var add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
@@ -939,105 +1840,105 @@ var readListParam = (type, value) => {
939
1840
  };
940
1841
 
941
1842
  // ../core/src/model/blockDefinition.ts
942
- import { z as z3 } from "zod";
1843
+ import { z as z5 } from "zod";
943
1844
  var paramBase = {
944
- name: z3.string().regex(/^[a-zA-Z_]\w*$/),
945
- label: z3.string().optional(),
1845
+ name: z5.string().regex(/^[a-zA-Z_]\w*$/),
1846
+ label: z5.string().optional(),
946
1847
  /** What the parameter means: the label's tooltip in the panel. */
947
- hint: z3.string().optional()
1848
+ hint: z5.string().optional()
948
1849
  };
949
1850
  var HEX_COLOR = /^#[0-9a-f]{6}$/i;
950
- var EnumOptionSchema = z3.preprocess(
1851
+ var EnumOptionSchema = z5.preprocess(
951
1852
  (raw) => typeof raw === "string" ? { value: raw } : raw,
952
- z3.object({
953
- value: z3.string(),
954
- label: z3.string().optional(),
1853
+ z5.object({
1854
+ value: z5.string(),
1855
+ label: z5.string().optional(),
955
1856
  /** A `#rrggbb` color square before the option's label. */
956
- swatch: z3.string().regex(HEX_COLOR).optional()
1857
+ swatch: z5.string().regex(HEX_COLOR).optional()
957
1858
  })
958
1859
  );
959
- var NumberParam = z3.object({
1860
+ var NumberParam = z5.object({
960
1861
  ...paramBase,
961
- type: z3.literal("number"),
962
- default: z3.number(),
963
- min: z3.number().optional(),
964
- max: z3.number().optional(),
1862
+ type: z5.literal("number"),
1863
+ default: z5.number(),
1864
+ min: z5.number().optional(),
1865
+ max: z5.number().optional(),
965
1866
  /** Whole numbers only. */
966
- integer: z3.boolean().optional(),
1867
+ integer: z5.boolean().optional(),
967
1868
  /** ArrowUp / ArrowDown increment in the panel (default 1). */
968
- step: z3.number().positive().optional(),
1869
+ step: z5.number().positive().optional(),
969
1870
  /** Shown inside the field before / after the value ("1:", "mm"). */
970
- prefix: z3.string().optional(),
971
- suffix: z3.string().optional(),
1871
+ prefix: z5.string().optional(),
1872
+ suffix: z5.string().optional(),
972
1873
  /** Standard values offered in the field's dropdown; typing stays free. */
973
- presets: z3.array(z3.number()).optional()
1874
+ presets: z5.array(z5.number()).optional()
974
1875
  });
975
- var StringParam = z3.object({
1876
+ var StringParam = z5.object({
976
1877
  ...paramBase,
977
- type: z3.literal("string"),
978
- default: z3.string(),
979
- placeholder: z3.string().optional(),
1878
+ type: z5.literal("string"),
1879
+ default: z5.string(),
1880
+ placeholder: z5.string().optional(),
980
1881
  /** A textarea; Shift+Enter starts a new line. */
981
- multiline: z3.boolean().optional(),
982
- presets: z3.array(z3.string()).optional()
1882
+ multiline: z5.boolean().optional(),
1883
+ presets: z5.array(z5.string()).optional()
983
1884
  });
984
- var BooleanParam = z3.object({
1885
+ var BooleanParam = z5.object({
985
1886
  ...paramBase,
986
- type: z3.literal("boolean"),
987
- default: z3.boolean(),
1887
+ type: z5.literal("boolean"),
1888
+ default: z5.boolean(),
988
1889
  /** "toggle": a toggle button showing the label (compact, e.g. in a row
989
1890
  * beside a picker) instead of a switch. */
990
- display: z3.enum(["switch", "toggle"]).optional()
1891
+ display: z5.enum(["switch", "toggle"]).optional()
991
1892
  });
992
- var NumberListParam = z3.object({
1893
+ var NumberListParam = z5.object({
993
1894
  ...paramBase,
994
- type: z3.literal("numberList"),
995
- default: z3.array(z3.number()),
1895
+ type: z5.literal("numberList"),
1896
+ default: z5.array(z5.number()),
996
1897
  /** Shown while the list is empty. */
997
- placeholder: z3.string().optional(),
998
- min: z3.number().optional(),
999
- max: z3.number().optional(),
1000
- presets: z3.array(z3.array(z3.number())).optional()
1898
+ placeholder: z5.string().optional(),
1899
+ min: z5.number().optional(),
1900
+ max: z5.number().optional(),
1901
+ presets: z5.array(z5.array(z5.number())).optional()
1001
1902
  });
1002
- var StringListParam = z3.object({
1903
+ var StringListParam = z5.object({
1003
1904
  ...paramBase,
1004
- type: z3.literal("stringList"),
1005
- default: z3.array(z3.string()),
1905
+ type: z5.literal("stringList"),
1906
+ default: z5.array(z5.string()),
1006
1907
  /** Shown while the list is empty. */
1007
- placeholder: z3.string().optional(),
1008
- presets: z3.array(z3.array(z3.string())).optional()
1908
+ placeholder: z5.string().optional(),
1909
+ presets: z5.array(z5.array(z5.string())).optional()
1009
1910
  });
1010
- var ColorParam = z3.object({
1911
+ var ColorParam = z5.object({
1011
1912
  ...paramBase,
1012
- type: z3.literal("color"),
1013
- default: z3.string().regex(HEX_COLOR),
1014
- presets: z3.array(z3.string().regex(HEX_COLOR)).optional()
1913
+ type: z5.literal("color"),
1914
+ default: z5.string().regex(HEX_COLOR),
1915
+ presets: z5.array(z5.string().regex(HEX_COLOR)).optional()
1015
1916
  });
1016
- var EnumParam = z3.object({
1917
+ var EnumParam = z5.object({
1017
1918
  ...paramBase,
1018
- type: z3.literal("enum"),
1019
- default: z3.string(),
1020
- options: z3.array(EnumOptionSchema).min(1),
1919
+ type: z5.literal("enum"),
1920
+ default: z5.string(),
1921
+ options: z5.array(EnumOptionSchema).min(1),
1021
1922
  /** "buttons": a segmented toggle group of the options (short choices);
1022
1923
  * the panel falls back to the dropdown when they don't fit. */
1023
- display: z3.enum(["dropdown", "buttons"]).optional()
1924
+ display: z5.enum(["dropdown", "buttons"]).optional()
1024
1925
  });
1025
- var resourceParam = (type, fallback) => z3.object({
1926
+ var resourceParam = (type, fallback) => z5.object({
1026
1927
  ...paramBase,
1027
- type: z3.literal(type),
1028
- default: z3.string().default(fallback)
1928
+ type: z5.literal(type),
1929
+ default: z5.string().default(fallback)
1029
1930
  });
1030
1931
  var TextStyleParam = resourceParam("textStyle", "");
1031
1932
  var DimStyleParam = resourceParam("dimStyle", "");
1032
1933
  var FontParam = resourceParam("font", "arial");
1033
1934
  var LineTypeParam = resourceParam("lineType", "continuous");
1034
1935
  var HatchPatternParam = resourceParam("hatchPattern", "ANSI31");
1035
- var LabelParam = z3.object({ ...paramBase, type: z3.literal("label") });
1936
+ var LabelParam = z5.object({ ...paramBase, type: z5.literal("label") });
1036
1937
  var enumDefaultIsAnOption = (p) => p.type !== "enum" || p.options.some(
1037
1938
  (o) => o.value === p.default
1038
1939
  );
1039
1940
  var ENUM_DEFAULT = { message: "enum default must be one of its options" };
1040
- var RowItemSchema = z3.discriminatedUnion("type", [
1941
+ var RowItemSchema = z5.discriminatedUnion("type", [
1041
1942
  NumberParam,
1042
1943
  StringParam,
1043
1944
  BooleanParam,
@@ -1049,12 +1950,12 @@ var RowItemSchema = z3.discriminatedUnion("type", [
1049
1950
  LineTypeParam,
1050
1951
  HatchPatternParam
1051
1952
  ]).refine(enumDefaultIsAnOption, ENUM_DEFAULT);
1052
- var RowParam = z3.object({
1953
+ var RowParam = z5.object({
1053
1954
  ...paramBase,
1054
- type: z3.literal("row"),
1055
- items: z3.array(RowItemSchema).min(2).max(3)
1955
+ type: z5.literal("row"),
1956
+ items: z5.array(RowItemSchema).min(2).max(3)
1056
1957
  });
1057
- var BlockParamSchema = z3.discriminatedUnion("type", [
1958
+ var BlockParamSchema = z5.discriminatedUnion("type", [
1058
1959
  NumberParam,
1059
1960
  StringParam,
1060
1961
  BooleanParam,
@@ -1075,7 +1976,7 @@ var valueParams = (defs) => defs.flatMap(
1075
1976
  );
1076
1977
 
1077
1978
  // ../core/src/extensions/evalBlock.ts
1078
- import { z as z5 } from "zod";
1979
+ import { z as z7 } from "zod";
1079
1980
 
1080
1981
  // ../core/src/geometry/line.ts
1081
1982
  var projectParam = (p, a, b) => {
@@ -1133,6 +2034,8 @@ var mapObjectPoints = (obj, fn) => {
1133
2034
  case "hatch":
1134
2035
  return {
1135
2036
  ...obj,
2037
+ // An anchored pattern frame moves with its region.
2038
+ fill: obj.fill.kind === "pattern" && obj.fill.origin ? { ...obj.fill, origin: fn(obj.fill.origin) } : obj.fill,
1136
2039
  loops: obj.loops.map((loop) => ({
1137
2040
  points: loop.points.map((v) => ({ ...v, ...fn(v) }))
1138
2041
  }))
@@ -1178,6 +2081,7 @@ var moveViewportCenter = (obj, map) => {
1178
2081
  const nc = map(c);
1179
2082
  return translateObject(obj, sub(nc, c));
1180
2083
  };
2084
+ var turnFill = (fill, f) => fill.kind === "pattern" || fill.kind === "lines" ? { ...fill, angle: f(fill.angle) } : fill;
1181
2085
  var rotateObject = (obj, center, ang) => {
1182
2086
  const r = (p) => rotAround(p, center, ang);
1183
2087
  const mapped = mapObjectPoints(obj, r);
@@ -1194,7 +2098,7 @@ var rotateObject = (obj, center, ang) => {
1194
2098
  case "table":
1195
2099
  return { ...mapped, rotation: mapped.rotation + ang };
1196
2100
  case "dimension":
1197
- return { ...mapped, offset: rot(mapped.offset, ang) };
2101
+ return mapped.rotation === void 0 ? mapped : { ...mapped, rotation: mapped.rotation + ang };
1198
2102
  case "block":
1199
2103
  return pointsCarryPose(mapped) ? mapped : { ...mapped, rotation: mapped.rotation + ang };
1200
2104
  case "viewport":
@@ -1202,11 +2106,12 @@ var rotateObject = (obj, center, ang) => {
1202
2106
  case "image":
1203
2107
  case "reference":
1204
2108
  return { ...mapped, rotation: normAngle(mapped.rotation + ang) };
2109
+ case "hatch":
2110
+ return { ...mapped, fill: turnFill(mapped.fill, (a) => a + ang) };
1205
2111
  case "line":
1206
2112
  case "point":
1207
2113
  case "polyline":
1208
2114
  case "circle":
1209
- case "hatch":
1210
2115
  case "comment":
1211
2116
  return mapped;
1212
2117
  default:
@@ -1231,7 +2136,16 @@ var scaleObjectUniform = (obj, s) => {
1231
2136
  case "table":
1232
2137
  return { ...mapped, scale: mapped.scale * s };
1233
2138
  case "dimension":
1234
- return { ...mapped, offset: m(mapped.offset), scale: mapped.scale * s };
2139
+ return {
2140
+ ...mapped,
2141
+ ...mapped.offset === void 0 ? {} : { offset: mapped.offset * s },
2142
+ labels: mapped.labels.map((l) => ({
2143
+ ...l,
2144
+ ...l.along === void 0 ? {} : { along: l.along * s },
2145
+ ...l.across === void 0 ? {} : { across: l.across * s }
2146
+ })),
2147
+ scale: mapped.scale * s
2148
+ };
1235
2149
  case "block":
1236
2150
  return pointsCarryPose(mapped) ? mapped : { ...mapped, scale: mapped.scale * s };
1237
2151
  case "viewport":
@@ -1317,6 +2231,7 @@ var mirrorObject = (obj, a, b) => {
1317
2231
  case "hatch":
1318
2232
  return {
1319
2233
  ...mapped,
2234
+ fill: turnFill(mapped.fill, mirrorAngle),
1320
2235
  loops: mapped.loops.map((loop) => ({
1321
2236
  points: loop.points.map((v) => ({ ...v, bulge: -v.bulge }))
1322
2237
  }))
@@ -1331,8 +2246,17 @@ var mirrorObject = (obj, a, b) => {
1331
2246
  hAlign: readable ? mapped.hAlign : swap[mapped.hAlign]
1332
2247
  };
1333
2248
  }
1334
- case "dimension":
1335
- return { ...mapped, offset: sub(m(add(a, mapped.offset)), a) };
2249
+ case "dimension": {
2250
+ const handed = mapped.kind === "radial" || mapped.kind === "diameter";
2251
+ return {
2252
+ ...mapped,
2253
+ ...mapped.rotation === void 0 ? {} : { rotation: mirrorAngle(mapped.rotation) },
2254
+ ...mapped.offset === void 0 ? {} : { offset: -mapped.offset },
2255
+ labels: handed ? mapped.labels.map(
2256
+ (l) => l.across === void 0 ? l : { ...l, across: -l.across }
2257
+ ) : mapped.labels
2258
+ };
2259
+ }
1336
2260
  case "block":
1337
2261
  return pointsCarryPose(mapped) ? mapped : { ...mapped, rotation: mirrorAngle(mapped.rotation) };
1338
2262
  case "viewport":
@@ -1354,7 +2278,7 @@ var mirrorObject = (obj, a, b) => {
1354
2278
  };
1355
2279
 
1356
2280
  // ../core/src/extensions/blockDtos.ts
1357
- import { z as z4 } from "zod";
2281
+ import { z as z6 } from "zod";
1358
2282
 
1359
2283
  // ../core/src/extensions/jsLiteral.ts
1360
2284
  var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
@@ -1386,7 +2310,7 @@ ${indent}}`;
1386
2310
  };
1387
2311
 
1388
2312
  // ../core/src/model/migrations.ts
1389
- var CURRENT_DOCUMENT_VERSION = 20;
2313
+ var CURRENT_DOCUMENT_VERSION = 22;
1390
2314
  var leaderRawToText = (raw) => {
1391
2315
  const points = Array.isArray(raw.points) ? raw.points : [];
1392
2316
  const extraLines = Array.isArray(raw.extraLines) ? raw.extraLines : [];
@@ -1888,6 +2812,92 @@ var migrateV18toV19 = (doc) => {
1888
2812
  };
1889
2813
  };
1890
2814
  var migrateV19toV20 = (doc) => ({ ...swapTextAlignments(doc), version: 20 });
2815
+ var dimensionPointCount = (kind) => kind === "angular" ? 3 : 2;
2816
+ var wrapAngle = (a) => {
2817
+ const twoPi = 2 * Math.PI;
2818
+ const r = a % twoPi;
2819
+ return r < 0 ? r + twoPi : r;
2820
+ };
2821
+ var dimensionRawToLabels = (raw) => {
2822
+ if (raw.type !== "dimension") return raw;
2823
+ const legacyOffset = Array.isArray(raw.offset) ? { x: Number(raw.offset[0]), y: Number(raw.offset[1]) } : raw.offset !== null && typeof raw.offset === "object" ? raw.offset : null;
2824
+ if (legacyOffset === null && !("textOverride" in raw)) return raw;
2825
+ const kind = typeof raw.kind === "string" ? raw.kind : "linear";
2826
+ const points = Array.isArray(raw.points) ? raw.points : [];
2827
+ const tooFew = points.length < dimensionPointCount(kind);
2828
+ const out = { ...raw };
2829
+ delete out.textOverride;
2830
+ const labels = Array.isArray(raw.labels) ? raw.labels.map((l) => ({ ...l })) : [];
2831
+ const label = (i) => labels[i] ??= {};
2832
+ if (legacyOffset) {
2833
+ const v = legacyOffset;
2834
+ const vLen = Math.hypot(v.x, v.y);
2835
+ if (kind === "linear") {
2836
+ delete out.rotation;
2837
+ if (vLen < 1e-9 || tooFew) out.offset = 0;
2838
+ else {
2839
+ let ux = -v.y / vLen;
2840
+ let uy = v.x / vLen;
2841
+ if (ux < 0 || ux === 0 && uy < 0) {
2842
+ ux = -ux;
2843
+ uy = -uy;
2844
+ }
2845
+ out.rotation = Math.atan2(uy, ux);
2846
+ out.offset = v.x * -uy + v.y * ux;
2847
+ }
2848
+ } else if (kind === "angular") {
2849
+ if (tooFew) out.offset = vLen;
2850
+ else {
2851
+ const [vertex, a, b] = points;
2852
+ const angA = Math.atan2(a.y - vertex.y, a.x - vertex.x);
2853
+ const angB = Math.atan2(b.y - vertex.y, b.x - vertex.x);
2854
+ const angL = Math.atan2(v.y, v.x);
2855
+ const inAB = wrapAngle(angL - angA) <= wrapAngle(angB - angA);
2856
+ out.offset = inAB ? vLen : -vLen;
2857
+ }
2858
+ } else {
2859
+ delete out.offset;
2860
+ const [p0, p1] = points;
2861
+ const d = tooFew ? 0 : Math.hypot(p1.x - p0.x, p1.y - p0.y);
2862
+ if (d > 1e-9) {
2863
+ const ux = (p1.x - p0.x) / d;
2864
+ const uy = (p1.y - p0.y) / d;
2865
+ label(0).along = v.x * ux + v.y * uy;
2866
+ }
2867
+ }
2868
+ }
2869
+ if (typeof raw.textOverride === "string") {
2870
+ const template = {
2871
+ linear: "{value}",
2872
+ angular: "{value}\xB0",
2873
+ radial: "R{value}",
2874
+ diameter: "\xD8{value}"
2875
+ };
2876
+ const content = raw.textOverride.replaceAll(
2877
+ "<>",
2878
+ template[kind] ?? "{value}"
2879
+ );
2880
+ const segments = kind === "linear" ? Math.max(1, points.length - 1) : 1;
2881
+ for (let i = 0; i < segments; i++) label(i).content = content;
2882
+ }
2883
+ if (labels.length > 0) out.labels = labels;
2884
+ return out;
2885
+ };
2886
+ var migrateV20toV21 = (doc) => {
2887
+ const degenerate = (raw) => raw.type === "dimension" && (Array.isArray(raw.points) ? raw.points.length : 0) < dimensionPointCount(typeof raw.kind === "string" ? raw.kind : "linear");
2888
+ const mapObjects = (list) => Array.isArray(list) ? list.filter(
2889
+ (raw) => raw === null || typeof raw !== "object" || !degenerate(raw)
2890
+ ).map(
2891
+ (raw) => raw !== null && typeof raw === "object" ? dimensionRawToLabels(raw) : raw
2892
+ ) : list;
2893
+ const layouts = Array.isArray(doc.layouts) ? doc.layouts.map((raw) => {
2894
+ if (raw === null || typeof raw !== "object") return raw;
2895
+ const layout = raw;
2896
+ return { ...layout, objects: mapObjects(layout.objects) };
2897
+ }) : doc.layouts;
2898
+ return { ...doc, objects: mapObjects(doc.objects), layouts, version: 21 };
2899
+ };
2900
+ var migrateV21toV22 = (doc) => ({ ...doc, version: 22 });
1891
2901
  var MIGRATION_STEPS = [
1892
2902
  migrateV1toV2,
1893
2903
  migrateV2toV3,
@@ -1907,7 +2917,9 @@ var MIGRATION_STEPS = [
1907
2917
  migrateV16toV17,
1908
2918
  migrateV17toV18,
1909
2919
  migrateV18toV19,
1910
- migrateV19toV20
2920
+ migrateV19toV20,
2921
+ migrateV20toV21,
2922
+ migrateV21toV22
1911
2923
  ];
1912
2924
  function migrateDocument(raw) {
1913
2925
  if (raw === null || typeof raw !== "object") return raw;
@@ -1926,7 +2938,8 @@ function migrateDocument(raw) {
1926
2938
 
1927
2939
  // ../core/src/extensions/blockDtos.ts
1928
2940
  var CHILD_OMIT = { id: true, layerId: true };
1929
- var BlockChildSchema = z4.preprocess(
2941
+ var CHILD_LAYER = { layerId: z6.string().optional() };
2942
+ var BlockChildSchema = z6.preprocess(
1930
2943
  // Back-compat: scripts written before document v6 may still emit "leader"
1931
2944
  // children (→ text with leaderLines), pre-v9 scripts may emit dimensions
1932
2945
  // in the linePoint/rotation shape (→ offset), pre-v17 scripts may put
@@ -1939,27 +2952,29 @@ var BlockChildSchema = z4.preprocess(
1939
2952
  let obj = raw;
1940
2953
  if (obj.type === "leader") obj = leaderRawToText(obj);
1941
2954
  if (obj.type === "dimension")
1942
- return dimRawScaleToField(dimensionRawToOffset(obj));
2955
+ return dimensionRawToLabels(
2956
+ dimRawScaleToField(dimensionRawToOffset(obj))
2957
+ );
1943
2958
  if (obj.type === "text") return textRawStyleToOverride(obj);
1944
2959
  return obj;
1945
2960
  },
1946
- z4.discriminatedUnion("type", [
1947
- LineSchema.omit(CHILD_OMIT),
1948
- PointSchema.omit(CHILD_OMIT),
1949
- PolylineSchema.omit(CHILD_OMIT),
1950
- CircleSchema.omit(CHILD_OMIT),
1951
- ArcSchema.omit(CHILD_OMIT),
1952
- EllipseSchema.omit(CHILD_OMIT),
1953
- HatchSchema.omit(CHILD_OMIT),
1954
- TextSchema.omit(CHILD_OMIT),
1955
- DimensionSchema.omit(CHILD_OMIT),
1956
- BlockSchema.omit(CHILD_OMIT).refine(
1957
- (b) => b.inputs.every((v) => typeof v !== "string"),
1958
- { message: "nested block instances may not carry object references" }
1959
- )
2961
+ z6.discriminatedUnion("type", [
2962
+ LineSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
2963
+ PointSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
2964
+ PolylineSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
2965
+ CircleSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
2966
+ ArcSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
2967
+ EllipseSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
2968
+ HatchSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
2969
+ TextSchema.omit(CHILD_OMIT).extend(CHILD_LAYER),
2970
+ TableShapeSchema.omit(CHILD_OMIT).extend(CHILD_LAYER).superRefine(tableRefinement),
2971
+ DimensionShapeSchema.omit(CHILD_OMIT).extend(CHILD_LAYER).superRefine(dimensionRefinement),
2972
+ BlockSchema.omit(CHILD_OMIT).extend(CHILD_LAYER).refine((b) => b.inputs.every((v) => typeof v !== "string"), {
2973
+ message: "nested block instances may not carry object references"
2974
+ })
1960
2975
  ])
1961
2976
  );
1962
- var BlockScriptResultSchema = z4.array(BlockChildSchema);
2977
+ var BlockScriptResultSchema = z6.array(BlockChildSchema);
1963
2978
 
1964
2979
  // ../core/src/extensions/scriptHelpers.ts
1965
2980
  var scriptHelpers_exports = {};
@@ -2146,13 +3161,13 @@ var toBlockModule = (mod) => {
2146
3161
  );
2147
3162
  return { ...mod, draw, place };
2148
3163
  };
2149
- var ParamsExportSchema = z5.array(BlockParamSchema);
2150
- var OptionalBlockMetaSchema = z5.object({
2151
- description: z5.string().optional(),
2152
- version: z5.string().optional(),
2153
- authors: z5.array(z5.string()).optional(),
2154
- tags: z5.array(z5.string()).optional(),
2155
- dependencies: z5.array(z5.string()).optional()
3164
+ var ParamsExportSchema = z7.array(BlockParamSchema);
3165
+ var OptionalBlockMetaSchema = z7.object({
3166
+ description: z7.string().optional(),
3167
+ version: z7.string().optional(),
3168
+ authors: z7.array(z7.string()).optional(),
3169
+ tags: z7.array(z7.string()).optional(),
3170
+ dependencies: z7.array(z7.string()).optional()
2156
3171
  });
2157
3172
  var parseParamsExport = (paramsExport) => {
2158
3173
  if (typeof paramsExport === "function") {
@@ -2288,7 +3303,7 @@ var stylesSig = (s) => {
2288
3303
  };
2289
3304
  return `${sig(s.textStyles)}.${sig(s.dimStyles)}`;
2290
3305
  };
2291
- var ParamVisibilityResultSchema = z5.record(z5.string(), z5.boolean());
3306
+ var ParamVisibilityResultSchema = z7.record(z7.string(), z7.boolean());
2292
3307
  var paramsKey = (params) => JSON.stringify(Object.entries(params).sort(([a], [b]) => a.localeCompare(b)));
2293
3308
  var r6 = (v) => Math.round(v * 1e6) / 1e6;
2294
3309
  var sigOf = (v) => typeof v === "string" ? `#${v}` : `${r6(v.x)},${r6(v.y)}`;
@@ -2314,7 +3329,7 @@ var resolveInputs = (inputs, pivot, resolve) => {
2314
3329
  return obj ? translateObject(obj, neg) : null;
2315
3330
  });
2316
3331
  };
2317
- var BlockInputsSchema = z5.array(z5.union([Vec2Schema, z5.string()]));
3332
+ var BlockInputsSchema = z7.array(z7.union([Vec2Schema, z7.string()]));
2318
3333
  var acquireBlockInputs = async (script, pickers) => {
2319
3334
  const { mod, meta } = compileBlock(script);
2320
3335
  if (!mod.place) return [await pickers.pickPoint("Insertion point")];
@@ -2418,7 +3433,7 @@ var childToObject = (child, instance, index) => {
2418
3433
  lineWidth: instance.lineWidth,
2419
3434
  ...child,
2420
3435
  id: `${instance.id}:${index}`,
2421
- layerId: instance.layerId
3436
+ layerId: child.layerId ?? instance.layerId
2422
3437
  };
2423
3438
  };
2424
3439
  var evaluating = /* @__PURE__ */ new Set();
@@ -2516,259 +3531,42 @@ var indexOf = (doc) => {
2516
3531
  var resolveDocumentObject = (doc, id) => indexOf(doc).get(id);
2517
3532
  var documentObjectResolver = (doc) => (id) => resolveDocumentObject(doc, id);
2518
3533
 
2519
- // ../core/src/paint/dimensionGeometry.ts
2520
- var RAD2DEG = 180 / Math.PI;
2521
- var formatDimValue = (value, precision) => {
2522
- const fixed = value.toFixed(precision);
2523
- return fixed.includes(".") ? fixed.replace(/\.?0+$/, "") : fixed;
3534
+ // ../core/src/paint/textMetrics.ts
3535
+ var LINE_HEIGHT_FACTOR = 1.66;
3536
+ var TAB_SIZE_FACTOR = 4;
3537
+ var boundMeasurer = (measure, font, widthFactor) => font === void 0 && widthFactor === void 0 ? measure : {
3538
+ measureTextWidth: (text, heightMm) => measure.measureTextWidth(text, heightMm, font, widthFactor)
2524
3539
  };
2525
- var resolveDimStyle = (obj, styles) => ({
2526
- ...dimStyleFor(obj, styles),
2527
- ...obj.styleOverride,
2528
- scale: obj.scale
2529
- });
2530
- var readableRotation = (u) => {
2531
- const raw = normAngle(angle(u));
2532
- const flipped = raw > Math.PI / 2 + 1e-9 && raw <= 3 * Math.PI / 2 + 1e-9;
2533
- return flipped ? raw - Math.PI : raw;
3540
+ var lineSegments = (line, heightMm, measure) => {
3541
+ if (!line.includes(" ")) return [{ text: line, x: 0 }];
3542
+ const tab = heightMm * TAB_SIZE_FACTOR;
3543
+ const out = [];
3544
+ let x = 0;
3545
+ const parts = line.split(" ");
3546
+ for (let i = 0; i < parts.length; i++) {
3547
+ if (i > 0) x = (Math.floor(x / tab + 1e-9) + 1) * tab;
3548
+ out.push({ text: parts[i], x });
3549
+ x += measure.measureTextWidth(parts[i], heightMm);
3550
+ }
3551
+ return out;
2534
3552
  };
2535
- var textFor = (content, at, along, style) => {
2536
- const rotation = readableRotation(along);
2537
- const textUp = perp(fromAngle(rotation));
2538
- return {
2539
- content,
2540
- anchor: add(at, scale(textUp, style.textOffset * style.scale)),
2541
- rotation,
2542
- height: style.textHeight * style.scale,
2543
- font: dimFontFace(style),
2544
- widthFactor: style.widthFactor
2545
- };
3553
+ var lineWidthWithTabs = (line, heightMm, measure) => {
3554
+ const segs = lineSegments(line, heightMm, measure);
3555
+ const last = segs[segs.length - 1];
3556
+ return last.x + measure.measureTextWidth(last.text, heightMm);
2546
3557
  };
2547
- var linearGeometry = (obj, style) => {
2548
- if (obj.points.length < 2) return null;
2549
- const s = style.scale;
2550
- const points = obj.points;
2551
- const first = points[0];
2552
- const last = points[points.length - 1];
2553
- const upLen = len(obj.offset);
2554
- const u = upLen > 1e-9 ? perp(scale(obj.offset, 1 / upLen)) : dist(first, last) > 1e-9 ? norm(sub(last, first)) : { x: 1, y: 0 };
2555
- const v = perp(u);
2556
- const h = dot(obj.offset, v);
2557
- const feet = points.map((p) => add(p, scale(v, h - dot(sub(p, first), v))));
2558
- const extensionLines = [];
2559
- points.forEach((p, i) => {
2560
- const f = feet[i];
2561
- const gap = dist(p, f);
2562
- if (gap < 1e-9) return;
2563
- const w = norm(sub(f, p));
2564
- extensionLines.push({
2565
- a: style.extensionType === "short" ? sub(f, scale(w, style.extensionOvershoot * s)) : add(p, scale(w, Math.min(style.extensionOffset * s, gap))),
2566
- b: add(f, scale(w, style.extensionOvershoot * s))
2567
- });
2568
- });
2569
- const dimLines = [];
2570
- const arrows = [];
2571
- const texts = [];
2572
- for (let i = 0; i + 1 < feet.length; i++) {
2573
- const a = feet[i];
2574
- const b = feet[i + 1];
2575
- const value = dist(a, b);
2576
- if (value < 1e-9) continue;
2577
- dimLines.push({ a, b });
2578
- arrows.push(
2579
- { tip: a, toward: b, size: style.arrowSize * s },
2580
- { tip: b, toward: a, size: style.arrowSize * s }
2581
- );
2582
- texts.push(
2583
- textFor(
2584
- obj.textOverride ?? formatDimValue(value, style.precision),
2585
- mid(a, b),
2586
- u,
2587
- style
2588
- )
2589
- );
2590
- }
2591
- if (dimLines.length === 0) return null;
2592
- return {
2593
- extensionLines,
2594
- dimLines,
2595
- dimArc: null,
2596
- arrows,
2597
- arrowType: style.arrowType,
2598
- texts,
2599
- value: dist(feet[0], feet[feet.length - 1])
2600
- };
2601
- };
2602
- var angularGeometry = (obj, style) => {
2603
- if (obj.points.length < 3) return null;
2604
- const s = style.scale;
2605
- const [vertex, a, b] = obj.points;
2606
- const radius = len(obj.offset);
2607
- const da = dist(vertex, a);
2608
- const db = dist(vertex, b);
2609
- if (radius < 1e-9 || da < 1e-9 || db < 1e-9) return null;
2610
- const angA = angle(sub(a, vertex));
2611
- const angB = angle(sub(b, vertex));
2612
- const angL = angle(obj.offset);
2613
- const sweepAB = normAngle(angB - angA);
2614
- const inAB = normAngle(angL - angA) <= sweepAB;
2615
- const start = inAB ? angA : angB;
2616
- const sweep = inAB ? sweepAB : normAngle(angA - angB);
2617
- if (sweep < 1e-9) return null;
2618
- const at = (t) => add(vertex, scale(fromAngle(start + t), radius));
2619
- const extensionLines = [];
2620
- for (const [p, d] of [
2621
- [a, da],
2622
- [b, db]
2623
- ]) {
2624
- const w = norm(sub(p, vertex));
2625
- const inner = style.extensionType === "short" ? radius - style.extensionOvershoot * s : Math.min(d + style.extensionOffset * s, radius);
2626
- const outer = radius + style.extensionOvershoot * s;
2627
- if (outer - inner > 1e-9)
2628
- extensionLines.push({
2629
- a: add(vertex, scale(w, inner)),
2630
- b: add(vertex, scale(w, outer))
2631
- });
2632
- }
2633
- const tangentStep = Math.min(sweep / 3, style.arrowSize * s * 2 / radius);
2634
- const valueDeg = sweep * RAD2DEG;
2635
- const midPoint = at(sweep / 2);
2636
- const midTangent = perp(norm(sub(midPoint, vertex)));
2637
- return {
2638
- extensionLines,
2639
- dimLines: [],
2640
- dimArc: {
2641
- center: vertex,
2642
- radius,
2643
- startAngle: start,
2644
- endAngle: start + sweep
2645
- },
2646
- arrows: [
2647
- { tip: at(0), toward: at(tangentStep), size: style.arrowSize * s },
2648
- {
2649
- tip: at(sweep),
2650
- toward: at(sweep - tangentStep),
2651
- size: style.arrowSize * s
2652
- }
2653
- ],
2654
- arrowType: style.arrowType,
2655
- texts: [
2656
- textFor(
2657
- obj.textOverride ?? `${formatDimValue(valueDeg, style.precision)}\xB0`,
2658
- midPoint,
2659
- midTangent,
2660
- style
2661
- )
2662
- ],
2663
- value: sweep
2664
- };
2665
- };
2666
- var radialGeometry = (obj, style) => {
2667
- if (obj.points.length < 2) return null;
2668
- const s = style.scale;
2669
- const [center, p] = obj.points;
2670
- const r = dist(center, p);
2671
- if (r < 1e-9) return null;
2672
- const u = norm(sub(p, center));
2673
- const t = Math.max(dot(obj.offset, u), r * 0.2);
2674
- const textAt = add(center, scale(u, t));
2675
- return {
2676
- extensionLines: [],
2677
- // The line runs center → text position (through the curve point).
2678
- dimLines: [{ a: center, b: t > r ? textAt : p }],
2679
- dimArc: null,
2680
- arrows: [{ tip: p, toward: center, size: style.arrowSize * s }],
2681
- arrowType: style.arrowType,
2682
- texts: [
2683
- textFor(
2684
- obj.textOverride ?? `R${formatDimValue(r, style.precision)}`,
2685
- textAt,
2686
- u,
2687
- style
2688
- )
2689
- ],
2690
- value: r
2691
- };
2692
- };
2693
- var diameterGeometry = (obj, style) => {
2694
- if (obj.points.length < 2) return null;
2695
- const s = style.scale;
2696
- const [p1, p2] = obj.points;
2697
- const d = dist(p1, p2);
2698
- if (d < 1e-9) return null;
2699
- const u = norm(sub(p2, p1));
2700
- const t = dot(sub(add(mid(p1, p2), obj.offset), p1), u);
2701
- const textAt = add(p1, scale(u, Math.min(Math.max(t, d * 0.15), d * 0.85)));
2702
- return {
2703
- extensionLines: [],
2704
- dimLines: [{ a: p1, b: p2 }],
2705
- dimArc: null,
2706
- arrows: [
2707
- { tip: p1, toward: p2, size: style.arrowSize * s },
2708
- { tip: p2, toward: p1, size: style.arrowSize * s }
2709
- ],
2710
- arrowType: style.arrowType,
2711
- texts: [
2712
- textFor(
2713
- obj.textOverride ?? `\xD8${formatDimValue(d, style.precision)}`,
2714
- textAt,
2715
- u,
2716
- style
2717
- )
2718
- ],
2719
- value: d
2720
- };
2721
- };
2722
- var dimensionGeometry = (obj, styles) => {
2723
- const style = resolveDimStyle(obj, styles);
2724
- switch (obj.kind) {
2725
- case "angular":
2726
- return angularGeometry(obj, style);
2727
- case "radial":
2728
- return radialGeometry(obj, style);
2729
- case "diameter":
2730
- return diameterGeometry(obj, style);
2731
- case "linear":
2732
- return linearGeometry(obj, style);
2733
- }
2734
- };
2735
-
2736
- // ../core/src/paint/textMetrics.ts
2737
- var LINE_HEIGHT_FACTOR = 1.66;
2738
- var TAB_SIZE_FACTOR = 4;
2739
- var boundMeasurer = (measure, font, widthFactor) => font === void 0 && widthFactor === void 0 ? measure : {
2740
- measureTextWidth: (text, heightMm) => measure.measureTextWidth(text, heightMm, font, widthFactor)
2741
- };
2742
- var lineSegments = (line, heightMm, measure) => {
2743
- if (!line.includes(" ")) return [{ text: line, x: 0 }];
2744
- const tab = heightMm * TAB_SIZE_FACTOR;
2745
- const out = [];
2746
- let x = 0;
2747
- const parts = line.split(" ");
2748
- for (let i = 0; i < parts.length; i++) {
2749
- if (i > 0) x = (Math.floor(x / tab + 1e-9) + 1) * tab;
2750
- out.push({ text: parts[i], x });
2751
- x += measure.measureTextWidth(parts[i], heightMm);
2752
- }
2753
- return out;
2754
- };
2755
- var lineWidthWithTabs = (line, heightMm, measure) => {
2756
- const segs = lineSegments(line, heightMm, measure);
2757
- const last = segs[segs.length - 1];
2758
- return last.x + measure.measureTextWidth(last.text, heightMm);
2759
- };
2760
- var wrapWords = (line, heightMm, measure, maxWidthMm) => {
2761
- const words = line.split(" ");
2762
- const out = [];
2763
- let cur = "";
2764
- for (const w of words) {
2765
- const trial = cur ? `${cur} ${w}` : w;
2766
- if (cur && measure.measureTextWidth(trial, heightMm) > maxWidthMm) {
2767
- out.push(cur);
2768
- cur = w;
2769
- } else {
2770
- cur = trial;
2771
- }
3558
+ var wrapWords = (line, heightMm, measure, maxWidthMm) => {
3559
+ const words = line.split(" ");
3560
+ const out = [];
3561
+ let cur = "";
3562
+ for (const w of words) {
3563
+ const trial = cur ? `${cur} ${w}` : w;
3564
+ if (cur && measure.measureTextWidth(trial, heightMm) > maxWidthMm) {
3565
+ out.push(cur);
3566
+ cur = w;
3567
+ } else {
3568
+ cur = trial;
3569
+ }
2772
3570
  }
2773
3571
  out.push(cur);
2774
3572
  return out;
@@ -2802,6 +3600,203 @@ var textBlock = (content, heightMm, measure, maxWidthMm) => {
2802
3600
  };
2803
3601
  };
2804
3602
 
3603
+ // ../core/src/paint/richText.ts
3604
+ var ATTR_KEYS = [
3605
+ "font",
3606
+ "bold",
3607
+ "italic",
3608
+ "underline",
3609
+ "color",
3610
+ "height",
3611
+ "script"
3612
+ ];
3613
+ var SCRIPT_SCALE = 0.6;
3614
+ var SUPER_RISE = 0.45;
3615
+ var SUB_DROP = 0.2;
3616
+ var attrsOf = (s) => {
3617
+ const out = {};
3618
+ for (const k of ATTR_KEYS) if (s[k] !== void 0) out[k] = s[k];
3619
+ return out;
3620
+ };
3621
+ var sameAttrs = (a, b) => ATTR_KEYS.every((k) => a[k] === b[k]);
3622
+ var isEmpty = (a) => ATTR_KEYS.every((k) => a[k] === void 0);
3623
+ var snap = (i, content) => {
3624
+ const c = Math.max(0, Math.min(content.length, Math.round(i)));
3625
+ const code = content.charCodeAt(c);
3626
+ return c > 0 && code >= 56320 && code <= 57343 ? c - 1 : c;
3627
+ };
3628
+ var toIntervals = (spans, content, extra = []) => {
3629
+ const cuts = /* @__PURE__ */ new Set([0, content.length]);
3630
+ for (const s of spans ?? []) {
3631
+ cuts.add(snap(s.start, content));
3632
+ cuts.add(snap(s.end, content));
3633
+ }
3634
+ for (const c of extra) cuts.add(snap(c, content));
3635
+ const sorted = [...cuts].sort((a, b) => a - b);
3636
+ const out = [];
3637
+ for (let i = 0; i + 1 < sorted.length; i++) {
3638
+ const start = sorted[i];
3639
+ const end = sorted[i + 1];
3640
+ let attrs = {};
3641
+ for (const s of spans ?? [])
3642
+ if (snap(s.start, content) <= start && snap(s.end, content) >= end)
3643
+ attrs = { ...attrs, ...attrsOf(s) };
3644
+ out.push({ start, end, attrs });
3645
+ }
3646
+ return out;
3647
+ };
3648
+ var fromIntervals = (intervals) => {
3649
+ const out = [];
3650
+ for (const iv of intervals) {
3651
+ if (iv.end <= iv.start || isEmpty(iv.attrs)) continue;
3652
+ const last = out[out.length - 1];
3653
+ if (last && last.end === iv.start && sameAttrs(attrsOf(last), iv.attrs))
3654
+ last.end = iv.end;
3655
+ else out.push({ start: iv.start, end: iv.end, ...iv.attrs });
3656
+ }
3657
+ return out;
3658
+ };
3659
+ var normalizeSpans = (spans, content) => fromIntervals(toIntervals(spans, content));
3660
+ var sliceSpans = (spans, start, end, to = 0) => (spans ?? []).filter((s) => s.end > start && s.start < end).map((s) => ({
3661
+ ...s,
3662
+ start: Math.max(s.start, start) - start + to,
3663
+ end: Math.min(s.end, end) - start + to
3664
+ }));
3665
+ var richTextBlock = (content, spans, base, measure, tabMeasure, maxWidthMm) => {
3666
+ const h = base.heightMm;
3667
+ const baseFace = parseFontFace(base.face);
3668
+ const runs = toIntervals(spans, content).map((iv) => {
3669
+ const a = iv.attrs;
3670
+ const cap = h * (a.height ?? 1);
3671
+ return {
3672
+ ...iv,
3673
+ face: fontFace(
3674
+ a.font ?? baseFace.font,
3675
+ a.bold ?? baseFace.bold,
3676
+ a.italic ?? baseFace.italic
3677
+ ),
3678
+ cap,
3679
+ glyph: a.script ? cap * SCRIPT_SCALE : cap,
3680
+ dy: a.script === "super" ? cap * SUPER_RISE : a.script === "sub" ? -cap * SUB_DROP : 0
3681
+ };
3682
+ });
3683
+ const runsIn = (a, b) => runs.filter((r) => r.end > a && r.start < b);
3684
+ const widthOf = (a, b, m) => runsIn(a, b).reduce(
3685
+ (w, r) => w + m.measureTextWidth(
3686
+ content.slice(Math.max(a, r.start), Math.min(b, r.end)),
3687
+ r.glyph,
3688
+ r.face,
3689
+ base.widthFactor
3690
+ ),
3691
+ 0
3692
+ );
3693
+ const tab = h * TAB_SIZE_FACTOR;
3694
+ const nextStop = (x) => (Math.floor(x / tab + 1e-9) + 1) * tab;
3695
+ const segments = (a, b) => {
3696
+ const out = [];
3697
+ let x = 0;
3698
+ let s = a;
3699
+ for (let i = a; i <= b; i++) {
3700
+ if (i < b && content[i] !== " ") continue;
3701
+ out.push({ start: s, end: i, x });
3702
+ x = nextStop(x + widthOf(s, i, tabMeasure));
3703
+ s = i + 1;
3704
+ }
3705
+ return out;
3706
+ };
3707
+ const wrapWords2 = (a, b, max) => {
3708
+ const out = [];
3709
+ let lineStart = a;
3710
+ let cur = -1;
3711
+ for (let i = a; i <= b; i++) {
3712
+ if (i < b && content[i] !== " ") continue;
3713
+ if (cur >= 0 && widthOf(lineStart, i, measure) > max) {
3714
+ out.push([lineStart, cur]);
3715
+ lineStart = cur + 1;
3716
+ }
3717
+ cur = i;
3718
+ }
3719
+ out.push([lineStart, b]);
3720
+ return out;
3721
+ };
3722
+ const raw = [];
3723
+ let p0 = 0;
3724
+ for (const para of content.split("\n")) {
3725
+ const p1 = p0 + para.length;
3726
+ const tabbed = para.includes(" ");
3727
+ const wrap = maxWidthMm !== void 0 && maxWidthMm > 0;
3728
+ if (!wrap) raw.push({ start: p0, end: p1, tabbed });
3729
+ else if (!tabbed)
3730
+ for (const [s, e] of wrapWords2(p0, p1, maxWidthMm))
3731
+ raw.push({ start: s, end: e, tabbed });
3732
+ else {
3733
+ const segs = segments(p0, p1);
3734
+ const last = segs[segs.length - 1];
3735
+ const width = last.x + widthOf(last.start, last.end, measure);
3736
+ if (width <= maxWidthMm || maxWidthMm - last.x <= h)
3737
+ raw.push({ start: p0, end: p1, tabbed });
3738
+ else
3739
+ wrapWords2(last.start, p1, maxWidthMm - last.x).forEach(
3740
+ ([s, e], i) => raw.push(
3741
+ i === 0 ? { start: p0, end: e, tabbed } : { start: s, end: e, hang: last.x, tabbed }
3742
+ )
3743
+ );
3744
+ }
3745
+ p0 = p1 + 1;
3746
+ }
3747
+ const lines = [];
3748
+ let baseline = 0;
3749
+ let prevCap = 0;
3750
+ for (const r of raw) {
3751
+ const segs = r.hang !== void 0 ? [{ start: r.start, end: r.end, x: r.hang }] : segments(r.start, r.end);
3752
+ const pieces = [];
3753
+ for (const seg of segs) {
3754
+ let x = seg.x;
3755
+ for (const run of runsIn(seg.start, seg.end)) {
3756
+ const text = content.slice(
3757
+ Math.max(seg.start, run.start),
3758
+ Math.min(seg.end, run.end)
3759
+ );
3760
+ const width = measure.measureTextWidth(
3761
+ text,
3762
+ run.glyph,
3763
+ run.face,
3764
+ base.widthFactor
3765
+ );
3766
+ pieces.push({
3767
+ text,
3768
+ x,
3769
+ width,
3770
+ dy: run.dy,
3771
+ heightMm: run.glyph,
3772
+ face: run.face,
3773
+ ...run.attrs.color ? { color: run.attrs.color } : {},
3774
+ ...run.attrs.underline ? { underline: true } : {}
3775
+ });
3776
+ x += width;
3777
+ }
3778
+ }
3779
+ const at = runsIn(r.start, Math.max(r.end, r.start + 1));
3780
+ const cap = Math.max(...at.map((run) => run.cap), at.length ? 0 : h);
3781
+ baseline += lines.length === 0 ? cap : LINE_HEIGHT_FACTOR * (prevCap + cap) / 2;
3782
+ prevCap = cap;
3783
+ const last = pieces[pieces.length - 1];
3784
+ lines.push({
3785
+ start: r.start,
3786
+ end: r.end,
3787
+ pieces,
3788
+ width: last ? last.x + last.width : 0,
3789
+ fromBlockEdge: r.tabbed,
3790
+ baseline
3791
+ });
3792
+ }
3793
+ return {
3794
+ lines,
3795
+ width: maxWidthMm !== void 0 && maxWidthMm > 0 ? maxWidthMm : Math.max(...lines.map((l) => l.width), 0),
3796
+ height: baseline
3797
+ };
3798
+ };
3799
+
2805
3800
  // ../core/src/paint/advanceTables.ts
2806
3801
  var EXTRACTED_RANGES = [
2807
3802
  [32, 126],
@@ -6284,6 +7279,473 @@ var approximateTextMeasurer = {
6284
7279
  }
6285
7280
  };
6286
7281
 
7282
+ // ../core/src/paint/dimensionGeometry.ts
7283
+ var RAD2DEG = 180 / Math.PI;
7284
+ var formatDimValue = (value, precision) => {
7285
+ const fixed = value.toFixed(precision);
7286
+ return fixed.includes(".") ? fixed.replace(/\.?0+$/, "") : fixed;
7287
+ };
7288
+ var dimLabelContent = (kind, label, formatted) => {
7289
+ const parts = (label?.content ?? DIM_DEFAULT_CONTENT[kind]).split(
7290
+ DIM_VALUE_TOKEN
7291
+ );
7292
+ const content = parts.join(formatted);
7293
+ if (!label?.spans?.length || label.content === void 0) return { content };
7294
+ const T = DIM_VALUE_TOKEN.length;
7295
+ const map = (p, end) => {
7296
+ let t = 0;
7297
+ let c = 0;
7298
+ for (let i = 0; i < parts.length; i++) {
7299
+ if (p <= t + parts[i].length) return c + (p - t);
7300
+ t += parts[i].length;
7301
+ c += parts[i].length;
7302
+ if (i === parts.length - 1) break;
7303
+ if (p < t + T) return end ? c + formatted.length : c;
7304
+ t += T;
7305
+ c += formatted.length;
7306
+ }
7307
+ return content.length;
7308
+ };
7309
+ const spans = normalizeSpans(
7310
+ label.spans.map((s) => ({
7311
+ ...s,
7312
+ start: map(s.start, false),
7313
+ end: map(s.end, true)
7314
+ })),
7315
+ content
7316
+ );
7317
+ return spans.length ? { content, spans } : { content };
7318
+ };
7319
+ var labelWidth = (label, h, font, widthFactor) => label.spans?.length ? richTextBlock(
7320
+ label.content,
7321
+ label.spans,
7322
+ { face: font, heightMm: h, widthFactor },
7323
+ approximateTextMeasurer,
7324
+ approximateTextMeasurer
7325
+ ).width : approximateTextMeasurer.measureTextWidth(
7326
+ label.content,
7327
+ h,
7328
+ font,
7329
+ widthFactor
7330
+ );
7331
+ var resolveDimStyle = (obj, styles) => ({
7332
+ ...dimStyleFor(obj, styles),
7333
+ ...obj.styleOverride,
7334
+ scale: obj.scale
7335
+ });
7336
+ var readableRotation = (u) => {
7337
+ const raw = normAngle(angle(u));
7338
+ const flipped = raw > Math.PI / 2 + 1e-9 && raw <= 3 * Math.PI / 2 + 1e-9;
7339
+ return flipped ? raw - Math.PI : raw;
7340
+ };
7341
+ var straightFrame = (origin, u, n, s0, s1) => ({
7342
+ s0,
7343
+ s1,
7344
+ toWorld: (along, across) => add(origin, add(scale(u, along), scale(n, across))),
7345
+ fromWorld: (p) => {
7346
+ const d = sub(p, origin);
7347
+ return { along: dot(d, u), across: dot(d, n) };
7348
+ },
7349
+ tangent: () => u
7350
+ });
7351
+ var linearAxis = (obj) => {
7352
+ const first = obj.points[0];
7353
+ const last = obj.points[obj.points.length - 1];
7354
+ const u = obj.rotation !== void 0 ? fromAngle(obj.rotation) : dist(first, last) > 1e-9 ? norm(sub(last, first)) : { x: 1, y: 0 };
7355
+ return { u, n: perp(u) };
7356
+ };
7357
+ var linearFeet = (obj) => {
7358
+ const { u, n } = linearAxis(obj);
7359
+ const first = obj.points[0];
7360
+ const h = obj.offset ?? 0;
7361
+ const feet = obj.points.map(
7362
+ (p) => add(p, scale(n, h - dot(sub(p, first), n)))
7363
+ );
7364
+ return { feet, u, n };
7365
+ };
7366
+ var dimensionLabelFrames = (obj) => {
7367
+ switch (obj.kind) {
7368
+ case "linear": {
7369
+ if (obj.points.length < 2) return [];
7370
+ const { feet, u, n } = linearFeet(obj);
7371
+ const across = (obj.offset ?? 0) >= 0 ? n : scale(n, -1);
7372
+ const frames = [];
7373
+ for (let i = 0; i + 1 < feet.length; i++) {
7374
+ const L = dot(sub(feet[i + 1], feet[i]), u);
7375
+ if (Math.abs(L) < 1e-9) continue;
7376
+ const dir = L > 0 ? u : scale(u, -1);
7377
+ frames.push(straightFrame(feet[i], dir, across, 0, Math.abs(L)));
7378
+ }
7379
+ return frames;
7380
+ }
7381
+ case "angular": {
7382
+ const polar2 = angularFrame(obj);
7383
+ return polar2 ? [polar2.frame] : [];
7384
+ }
7385
+ case "radial": {
7386
+ if (obj.points.length < 2) return [];
7387
+ const [center, p] = obj.points;
7388
+ const r = dist(center, p);
7389
+ if (r < 1e-9) return [];
7390
+ const u = norm(sub(p, center));
7391
+ return [straightFrame(center, u, perp(u), 0, r)];
7392
+ }
7393
+ case "diameter": {
7394
+ if (obj.points.length < 2) return [];
7395
+ const [p1, p2] = obj.points;
7396
+ const d = dist(p1, p2);
7397
+ if (d < 1e-9) return [];
7398
+ const u = norm(sub(p2, p1));
7399
+ const center = add(p1, scale(u, d / 2));
7400
+ return [straightFrame(center, u, perp(u), -d / 2, d / 2)];
7401
+ }
7402
+ }
7403
+ };
7404
+ var angularFrame = (obj) => {
7405
+ if (obj.points.length < 3) return null;
7406
+ const [vertex, a, b] = obj.points;
7407
+ const signed = obj.offset ?? 0;
7408
+ const radius = Math.abs(signed);
7409
+ if (radius < 1e-9 || dist(vertex, a) < 1e-9 || dist(vertex, b) < 1e-9)
7410
+ return null;
7411
+ const angA = angle(sub(a, vertex));
7412
+ const angB = angle(sub(b, vertex));
7413
+ const start = signed >= 0 ? angA : angB;
7414
+ const sweep = signed >= 0 ? normAngle(angB - angA) : normAngle(angA - angB);
7415
+ if (sweep < 1e-9) return null;
7416
+ const frame = {
7417
+ s0: 0,
7418
+ s1: radius * sweep,
7419
+ toWorld: (along, across) => add(vertex, scale(fromAngle(start + along / radius), radius + across)),
7420
+ fromWorld: (p) => {
7421
+ const d = sub(p, vertex);
7422
+ let w = normAngle(angle(d) - start);
7423
+ if (w > Math.PI) w -= 2 * Math.PI;
7424
+ return { along: w * radius, across: len(d) - radius };
7425
+ },
7426
+ tangent: (along) => perp(fromAngle(start + along / radius))
7427
+ };
7428
+ return { vertex, radius, start, sweep, frame };
7429
+ };
7430
+ var overlaps = (a, b) => a[0] < b[1] - 1e-9 && b[0] < a[1] - 1e-9;
7431
+ var placeLabel = (frame, spec, index, label, style, arrowCount, mode, stack) => {
7432
+ const s = style.scale;
7433
+ const h = style.textHeight * s;
7434
+ const gap = style.textOffset * s;
7435
+ const A = style.arrowSize * s;
7436
+ const font = dimFontFace(style);
7437
+ const width = labelWidth(label, h, font, style.widthFactor);
7438
+ const L = frame.s1 - frame.s0;
7439
+ const arrowsInside = L >= arrowCount * A - 1e-9;
7440
+ let along = spec?.along;
7441
+ let across = spec?.across ?? 0;
7442
+ let row = 0;
7443
+ if (along === void 0) {
7444
+ const fits = L >= width + 2 * gap + arrowCount * A - 1e-9;
7445
+ const push = (arrowsInside ? 0 : A) + gap + width / 2;
7446
+ if (fits) along = (frame.s0 + frame.s1) / 2;
7447
+ else if (mode === "end") along = frame.s1 + push;
7448
+ else if (mode === "start") along = frame.s0 - push;
7449
+ else {
7450
+ along = (frame.s0 + frame.s1) / 2;
7451
+ row = 1;
7452
+ const interval2 = [along - width / 2, along + width / 2];
7453
+ if (stack && stack.row >= 1 && overlaps(interval2, stack.interval))
7454
+ row = stack.row + 1;
7455
+ across = row * (h + gap);
7456
+ }
7457
+ }
7458
+ const interval = [along - width / 2, along + width / 2];
7459
+ let cover = null;
7460
+ if (across === 0 && (interval[1] > frame.s1 || interval[0] < frame.s0))
7461
+ cover = [
7462
+ Math.min(frame.s0, interval[0] - gap),
7463
+ Math.max(frame.s1, interval[1] + gap)
7464
+ ];
7465
+ const leaders = [];
7466
+ if (spec?.across !== void 0 && spec.across !== 0) {
7467
+ const landingA = frame.toWorld(interval[0], across);
7468
+ const landingB = frame.toWorld(interval[1], across);
7469
+ const from = frame.toWorld((frame.s0 + frame.s1) / 2, 0);
7470
+ const to = dist(from, landingA) <= dist(from, landingB) ? landingA : landingB;
7471
+ leaders.push({ a: from, b: to }, { a: landingA, b: landingB });
7472
+ }
7473
+ const tangent = frame.tangent(along);
7474
+ const rotation = readableRotation(tangent);
7475
+ const textUp = perp(fromAngle(rotation));
7476
+ const text = {
7477
+ content: label.content,
7478
+ ...label.spans ? { spans: label.spans } : {},
7479
+ anchor: add(frame.toWorld(along, across), scale(textUp, gap)),
7480
+ rotation,
7481
+ height: h,
7482
+ width,
7483
+ font,
7484
+ widthFactor: style.widthFactor,
7485
+ segment: index,
7486
+ along,
7487
+ across
7488
+ };
7489
+ return { text, cover, leaders, row };
7490
+ };
7491
+ var linearGeometry = (obj, style) => {
7492
+ if (obj.points.length < 2) return null;
7493
+ const s = style.scale;
7494
+ const A = style.arrowSize * s;
7495
+ const { feet, u } = linearFeet(obj);
7496
+ const frames = dimensionLabelFrames(obj);
7497
+ if (frames.length === 0) return null;
7498
+ const extensionLines = [];
7499
+ obj.points.forEach((p, i) => {
7500
+ const f = feet[i];
7501
+ const gap = dist(p, f);
7502
+ if (gap < 1e-9) return;
7503
+ const w = norm(sub(f, p));
7504
+ extensionLines.push({
7505
+ a: style.extensionType === "short" ? sub(f, scale(w, style.extensionOvershoot * s)) : add(p, scale(w, Math.min(style.extensionOffset * s, gap))),
7506
+ b: add(f, scale(w, style.extensionOvershoot * s))
7507
+ });
7508
+ });
7509
+ const dimLines = [];
7510
+ const arrows = [];
7511
+ const labels = [];
7512
+ const leaders = [];
7513
+ let stack = null;
7514
+ frames.forEach((frame, i) => {
7515
+ const L = frame.s1 - frame.s0;
7516
+ const value = L;
7517
+ const mode = frames.length === 1 || i === frames.length - 1 ? "end" : i === 0 ? "start" : "raise";
7518
+ const placed = placeLabel(
7519
+ frame,
7520
+ obj.labels[i],
7521
+ i,
7522
+ dimLabelContent(
7523
+ obj.kind,
7524
+ obj.labels[i],
7525
+ formatDimValue(value, style.precision)
7526
+ ),
7527
+ style,
7528
+ 2,
7529
+ mode,
7530
+ stack
7531
+ );
7532
+ stack = placed.row > 0 ? {
7533
+ row: placed.row,
7534
+ interval: [
7535
+ placed.text.along - placed.text.width / 2,
7536
+ placed.text.along + placed.text.width / 2
7537
+ ]
7538
+ } : null;
7539
+ labels.push(placed.text);
7540
+ leaders.push(...placed.leaders);
7541
+ const a = frame.toWorld(frame.s0, 0);
7542
+ const b = frame.toWorld(frame.s1, 0);
7543
+ const arrowsInside = L >= 2 * A - 1e-9;
7544
+ let from = frame.s0;
7545
+ let to = frame.s1;
7546
+ if (!arrowsInside) {
7547
+ from -= 2 * A;
7548
+ to += 2 * A;
7549
+ }
7550
+ if (placed.cover) {
7551
+ from = Math.min(from, placed.cover[0]);
7552
+ to = Math.max(to, placed.cover[1]);
7553
+ }
7554
+ dimLines.push({ a: frame.toWorld(from, 0), b: frame.toWorld(to, 0) });
7555
+ arrows.push(
7556
+ arrowsInside ? { tip: a, toward: b, size: A } : { tip: a, toward: frame.toWorld(from, 0), size: A },
7557
+ arrowsInside ? { tip: b, toward: a, size: A } : { tip: b, toward: frame.toWorld(to, 0), size: A }
7558
+ );
7559
+ });
7560
+ return {
7561
+ extensionLines,
7562
+ dimLines,
7563
+ dimArc: null,
7564
+ leaders,
7565
+ arrows,
7566
+ arrowType: style.arrowType,
7567
+ labels,
7568
+ value: Math.abs(dot(sub(feet[feet.length - 1], feet[0]), u))
7569
+ };
7570
+ };
7571
+ var angularGeometry = (obj, style) => {
7572
+ const polar2 = angularFrame(obj);
7573
+ if (!polar2) return null;
7574
+ const { vertex, radius, start, sweep, frame } = polar2;
7575
+ const s = style.scale;
7576
+ const A = style.arrowSize * s;
7577
+ const [, a, b] = obj.points;
7578
+ const extensionLines = [];
7579
+ for (const p of [a, b]) {
7580
+ const d = dist(vertex, p);
7581
+ const w = norm(sub(p, vertex));
7582
+ const inner = style.extensionType === "short" ? radius - style.extensionOvershoot * s : Math.min(d + style.extensionOffset * s, radius);
7583
+ const outer = radius + style.extensionOvershoot * s;
7584
+ if (outer - inner > 1e-9)
7585
+ extensionLines.push({
7586
+ a: add(vertex, scale(w, inner)),
7587
+ b: add(vertex, scale(w, outer))
7588
+ });
7589
+ }
7590
+ const placed = placeLabel(
7591
+ frame,
7592
+ obj.labels[0],
7593
+ 0,
7594
+ dimLabelContent(
7595
+ obj.kind,
7596
+ obj.labels[0],
7597
+ formatDimValue(sweep * RAD2DEG, style.precision)
7598
+ ),
7599
+ style,
7600
+ 2,
7601
+ "end",
7602
+ null
7603
+ );
7604
+ const L = frame.s1;
7605
+ const arrowsInside = L >= 2 * A - 1e-9;
7606
+ let from = 0;
7607
+ let to = L;
7608
+ if (!arrowsInside) {
7609
+ from -= 2 * A;
7610
+ to += 2 * A;
7611
+ }
7612
+ if (placed.cover) {
7613
+ from = Math.min(from, placed.cover[0]);
7614
+ to = Math.max(to, placed.cover[1]);
7615
+ }
7616
+ const step = Math.min(sweep / 3, A * 2 / radius) * radius;
7617
+ const at = (along) => frame.toWorld(along, 0);
7618
+ return {
7619
+ extensionLines,
7620
+ dimLines: [],
7621
+ dimArc: {
7622
+ center: vertex,
7623
+ radius,
7624
+ startAngle: start + from / radius,
7625
+ endAngle: start + to / radius
7626
+ },
7627
+ leaders: placed.leaders,
7628
+ arrows: [
7629
+ { tip: at(0), toward: at(arrowsInside ? step : -step), size: A },
7630
+ { tip: at(L), toward: at(arrowsInside ? L - step : L + step), size: A }
7631
+ ],
7632
+ arrowType: style.arrowType,
7633
+ labels: [placed.text],
7634
+ value: sweep
7635
+ };
7636
+ };
7637
+ var radialGeometry = (obj, style) => {
7638
+ const [frame] = dimensionLabelFrames(obj);
7639
+ if (!frame) return null;
7640
+ const s = style.scale;
7641
+ const A = style.arrowSize * s;
7642
+ const [center, p] = obj.points;
7643
+ const r = frame.s1;
7644
+ const spec = obj.labels[0];
7645
+ const content = dimLabelContent(
7646
+ obj.kind,
7647
+ spec,
7648
+ formatDimValue(r, style.precision)
7649
+ );
7650
+ const width = labelWidth(
7651
+ content,
7652
+ style.textHeight * s,
7653
+ dimFontFace(style),
7654
+ style.widthFactor
7655
+ );
7656
+ const gap = style.textOffset * s;
7657
+ const autoInside = spec?.along === void 0 && r >= width + 2 * gap + A - 1e-9 ? { ...spec, along: r - A - gap - width / 2 } : spec;
7658
+ const placed = placeLabel(
7659
+ frame,
7660
+ autoInside,
7661
+ 0,
7662
+ content,
7663
+ style,
7664
+ 1,
7665
+ "end",
7666
+ null
7667
+ );
7668
+ const outside = placed.text.along > r + 1e-9;
7669
+ let to = r;
7670
+ if (placed.cover) to = Math.max(to, placed.cover[1]);
7671
+ return {
7672
+ extensionLines: [],
7673
+ dimLines: [{ a: center, b: frame.toWorld(to, 0) }],
7674
+ dimArc: null,
7675
+ leaders: placed.leaders,
7676
+ // Head at the curve: pointing outward with the text inside, inward
7677
+ // (body on the outside) with the text outside.
7678
+ arrows: [
7679
+ { tip: p, toward: outside ? frame.toWorld(r + A, 0) : center, size: A }
7680
+ ],
7681
+ arrowType: style.arrowType,
7682
+ labels: [placed.text],
7683
+ value: r
7684
+ };
7685
+ };
7686
+ var diameterGeometry = (obj, style) => {
7687
+ const [frame] = dimensionLabelFrames(obj);
7688
+ if (!frame) return null;
7689
+ const s = style.scale;
7690
+ const A = style.arrowSize * s;
7691
+ const [p1, p2] = obj.points;
7692
+ const d = frame.s1 - frame.s0;
7693
+ const placed = placeLabel(
7694
+ frame,
7695
+ obj.labels[0],
7696
+ 0,
7697
+ dimLabelContent(
7698
+ obj.kind,
7699
+ obj.labels[0],
7700
+ formatDimValue(d, style.precision)
7701
+ ),
7702
+ style,
7703
+ 2,
7704
+ "end",
7705
+ null
7706
+ );
7707
+ const arrowsInside = d >= 2 * A - 1e-9;
7708
+ let from = frame.s0;
7709
+ let to = frame.s1;
7710
+ if (!arrowsInside) {
7711
+ from -= 2 * A;
7712
+ to += 2 * A;
7713
+ }
7714
+ if (placed.cover) {
7715
+ from = Math.min(from, placed.cover[0]);
7716
+ to = Math.max(to, placed.cover[1]);
7717
+ }
7718
+ return {
7719
+ extensionLines: [],
7720
+ dimLines: [{ a: frame.toWorld(from, 0), b: frame.toWorld(to, 0) }],
7721
+ dimArc: null,
7722
+ leaders: placed.leaders,
7723
+ arrows: arrowsInside ? [
7724
+ { tip: p1, toward: p2, size: A },
7725
+ { tip: p2, toward: p1, size: A }
7726
+ ] : [
7727
+ { tip: p1, toward: frame.toWorld(from, 0), size: A },
7728
+ { tip: p2, toward: frame.toWorld(to, 0), size: A }
7729
+ ],
7730
+ arrowType: style.arrowType,
7731
+ labels: [placed.text],
7732
+ value: d
7733
+ };
7734
+ };
7735
+ var dimensionGeometry = (obj, styles) => {
7736
+ const style = resolveDimStyle(obj, styles);
7737
+ switch (obj.kind) {
7738
+ case "linear":
7739
+ return linearGeometry(obj, style);
7740
+ case "angular":
7741
+ return angularGeometry(obj, style);
7742
+ case "radial":
7743
+ return radialGeometry(obj, style);
7744
+ case "diameter":
7745
+ return diameterGeometry(obj, style);
7746
+ }
7747
+ };
7748
+
6287
7749
  // ../core/src/paint/textGeometry.ts
6288
7750
  var textHeight = (obj, textStyles) => (obj.styleOverride?.textHeight ?? textStyleFor(obj, textStyles).textHeight) * obj.scale;
6289
7751
  var textArrowSize = (obj, textStyles) => (obj.styleOverride?.arrowSize ?? textStyleFor(obj, textStyles).arrowSize) * obj.scale;
@@ -6292,11 +7754,24 @@ var textWidth = (obj) => obj.width === void 0 ? void 0 : obj.width * obj.scale;
6292
7754
  var FRAME_MARGIN_FACTOR = 0.35;
6293
7755
  var textBoxLocal = (obj, measure, textStyles) => {
6294
7756
  const { font, widthFactor } = effectiveTextFont(obj, textStyles);
6295
- const m = boundMeasurer(measure, font, widthFactor);
6296
- const block = textBlock(
7757
+ const block = obj.spans?.length ? (
7758
+ // Formatted: per-run fonts, so the measurer stays unbound.
7759
+ richTextBlock(
7760
+ obj.content,
7761
+ obj.spans,
7762
+ {
7763
+ face: font,
7764
+ heightMm: textHeight(obj, textStyles),
7765
+ widthFactor
7766
+ },
7767
+ measure,
7768
+ approximateTextMeasurer,
7769
+ textWidth(obj)
7770
+ )
7771
+ ) : textBlock(
6297
7772
  obj.content,
6298
7773
  textHeight(obj, textStyles),
6299
- m,
7774
+ boundMeasurer(measure, font, widthFactor),
6300
7775
  textWidth(obj)
6301
7776
  );
6302
7777
  const u = fromAngle(obj.rotation);
@@ -6397,7 +7872,7 @@ var cellText = (t, row, col) => {
6397
7872
  id: `${t.id}:${row}:${col}`,
6398
7873
  layerId: t.layerId,
6399
7874
  position: { x: 0, y: 0 },
6400
- content: cell.text,
7875
+ content: cell.content,
6401
7876
  leaderLines: [],
6402
7877
  hAlign: "center",
6403
7878
  vAlign: "center",
@@ -6462,9 +7937,32 @@ var tableLayout = (t, textStyles) => {
6462
7937
  const h = textHeight(text, textStyles);
6463
7938
  const { font, widthFactor } = effectiveTextFont(text, textStyles);
6464
7939
  const inner2 = colX[c + cols] - colX[c] - 2 * margin;
7940
+ if (cell.spans?.length) {
7941
+ const rich = richTextBlock(
7942
+ cell.content,
7943
+ cell.spans,
7944
+ { face: font, heightMm: h, widthFactor },
7945
+ approximateTextMeasurer,
7946
+ approximateTextMeasurer,
7947
+ inner2 > 0 ? inner2 : void 0
7948
+ );
7949
+ let content = "";
7950
+ const spans = [];
7951
+ rich.lines.forEach((l, k) => {
7952
+ if (k > 0) content += "\n";
7953
+ spans.push(...sliceSpans(cell.spans, l.start, l.end, content.length));
7954
+ content += cell.content.slice(l.start, l.end);
7955
+ });
7956
+ wrapped.push({
7957
+ text: { ...text, spans },
7958
+ lines: [content],
7959
+ height: rich.height + 2 * margin
7960
+ });
7961
+ return;
7962
+ }
6465
7963
  const measure = boundMeasurer(approximateTextMeasurer, font, widthFactor);
6466
7964
  const block = textBlock(
6467
- cell.text,
7965
+ cell.content,
6468
7966
  h,
6469
7967
  measure,
6470
7968
  inner2 > 0 ? inner2 : void 0
@@ -6474,7 +7972,7 @@ var tableLayout = (t, textStyles) => {
6474
7972
  text,
6475
7973
  lines,
6476
7974
  // An empty cell still reserves one line of its text height.
6477
- height: (cell.text ? h + (lines.length - 1) * block.lineHeight : h) + 2 * margin
7975
+ height: (cell.content ? h + (lines.length - 1) * block.lineHeight : h) + 2 * margin
6478
7976
  });
6479
7977
  })
6480
7978
  );
@@ -6553,7 +8051,7 @@ var tableLayout = (t, textStyles) => {
6553
8051
  }));
6554
8052
  anchors.forEach((a, i) => {
6555
8053
  const { text, lines } = wrapped[i];
6556
- if (!t.cells[a.row][a.col].text) return;
8054
+ if (!t.cells[a.row][a.col].content) return;
6557
8055
  children.push({
6558
8056
  ...text,
6559
8057
  ...cellAnchor(t, colX, rowY, a),
@@ -6869,6 +8367,303 @@ var hexToTrueColor = (hex) => {
6869
8367
  return rgb ? rgb[0] << 16 | rgb[1] << 8 | rgb[2] : null;
6870
8368
  };
6871
8369
 
8370
+ // ../core/src/export/dxf/codecs.ts
8371
+ var LINE_WEIGHTS = [
8372
+ 0,
8373
+ 5,
8374
+ 9,
8375
+ 13,
8376
+ 15,
8377
+ 18,
8378
+ 20,
8379
+ 25,
8380
+ 30,
8381
+ 35,
8382
+ 40,
8383
+ 50,
8384
+ 53,
8385
+ 60,
8386
+ 70,
8387
+ 80,
8388
+ 90,
8389
+ 100,
8390
+ 106,
8391
+ 120,
8392
+ 140,
8393
+ 158,
8394
+ 200,
8395
+ 211
8396
+ ];
8397
+ var lineWeightToMm = (lineWeight) => lineWeight === void 0 || lineWeight < 0 ? void 0 : Math.max(lineWeight, 5) / 100;
8398
+ var mmToLineWeight = (mm) => LINE_WEIGHTS.reduce(
8399
+ (best, w) => Math.abs(w - mm * 100) < Math.abs(best - mm * 100) ? w : best
8400
+ );
8401
+ var encodeMtext = (s) => s.replace(/\\/g, "\\\\").replace(/\{/g, "\\{").replace(/\}/g, "\\}").replace(/\^/g, "^ ").replace(/\n/g, "\\P").replace(/\t/g, "^I");
8402
+ var decodeTextEscapes = (text) => text.replace(/%%([dpcuo%])/gi, (_, ch) => {
8403
+ switch (ch.toLowerCase()) {
8404
+ case "d":
8405
+ return "\xB0";
8406
+ case "p":
8407
+ return "\xB1";
8408
+ case "c":
8409
+ return "\xD8";
8410
+ case "u":
8411
+ case "o":
8412
+ return "";
8413
+ default:
8414
+ return "%";
8415
+ }
8416
+ });
8417
+ var SUPERSCRIPT = {
8418
+ "0": "\u2070",
8419
+ "1": "\xB9",
8420
+ "2": "\xB2",
8421
+ "3": "\xB3",
8422
+ "4": "\u2074",
8423
+ "5": "\u2075",
8424
+ "6": "\u2076",
8425
+ "7": "\u2077",
8426
+ "8": "\u2078",
8427
+ "9": "\u2079",
8428
+ "+": "\u207A",
8429
+ "-": "\u207B",
8430
+ "=": "\u207C",
8431
+ "(": "\u207D",
8432
+ ")": "\u207E",
8433
+ n: "\u207F",
8434
+ i: "\u2071"
8435
+ };
8436
+ var SUBSCRIPT = {
8437
+ "0": "\u2080",
8438
+ "1": "\u2081",
8439
+ "2": "\u2082",
8440
+ "3": "\u2083",
8441
+ "4": "\u2084",
8442
+ "5": "\u2085",
8443
+ "6": "\u2086",
8444
+ "7": "\u2087",
8445
+ "8": "\u2088",
8446
+ "9": "\u2089",
8447
+ "+": "\u208A",
8448
+ "-": "\u208B",
8449
+ "=": "\u208C",
8450
+ "(": "\u208D",
8451
+ ")": "\u208E"
8452
+ };
8453
+ var toScript = (s, map) => [...s].every((c) => map[c]) ? [...s].map((c) => map[c]).join("") : s;
8454
+ var stackText = (body) => {
8455
+ const m = /^(.*?)([\^#/])(.*)$/s.exec(body);
8456
+ if (!m) return body;
8457
+ const top = m[1].trim();
8458
+ const bottom = m[3].trim();
8459
+ if (top && !bottom) return toScript(top, SUPERSCRIPT);
8460
+ if (!top && bottom) return toScript(bottom, SUBSCRIPT);
8461
+ return `${top}/${bottom}`;
8462
+ };
8463
+ var SYMBOL_FONT = {
8464
+ a: "\u03B1",
8465
+ b: "\u03B2",
8466
+ c: "\u03C7",
8467
+ d: "\u03B4",
8468
+ e: "\u03B5",
8469
+ f: "\u03C6",
8470
+ g: "\u03B3",
8471
+ h: "\u03B7",
8472
+ i: "\u03B9",
8473
+ k: "\u03BA",
8474
+ l: "\u03BB",
8475
+ m: "\u03BC",
8476
+ n: "\u03BD",
8477
+ p: "\u03C0",
8478
+ q: "\u03B8",
8479
+ r: "\u03C1",
8480
+ s: "\u03C3",
8481
+ t: "\u03C4",
8482
+ u: "\u03C5",
8483
+ w: "\u03C9",
8484
+ x: "\u03BE",
8485
+ y: "\u03C8",
8486
+ z: "\u03B6",
8487
+ D: "\u0394",
8488
+ F: "\u03A6",
8489
+ G: "\u0393",
8490
+ L: "\u039B",
8491
+ P: "\u03A0",
8492
+ Q: "\u0398",
8493
+ S: "\u03A3",
8494
+ W: "\u03A9",
8495
+ X: "\u039E",
8496
+ Y: "\u03A8",
8497
+ "\xA3": "\u2264",
8498
+ "\xB3": "\u2265",
8499
+ "\xB9": "\u2260",
8500
+ "\xBB": "\u2248",
8501
+ "\xA5": "\u221E",
8502
+ "\xB4": "\xD7",
8503
+ "\xB8": "\xF7",
8504
+ \u00D6: "\u221A",
8505
+ "\xAE": "\u2192",
8506
+ "\xAC": "\u2190",
8507
+ \u00C6: "\u2205",
8508
+ \u00CE: "\u2208",
8509
+ "\xB6": "\u2202"
8510
+ };
8511
+ var isSymbolFont = (fontCode) => /^symbol(\.ttf)?$/i.test(fontCode.split("|")[0].trim());
8512
+ var scanMtext = (text, opts) => {
8513
+ const chunks = [];
8514
+ let st = { symbol: false };
8515
+ const stack = [];
8516
+ const attrs = (extra) => opts ? {
8517
+ ...st.font !== void 0 ? { font: st.font } : {},
8518
+ ...st.bold !== void 0 ? { bold: st.bold } : {},
8519
+ ...st.italic !== void 0 ? { italic: st.italic } : {},
8520
+ ...st.height !== void 0 ? { height: st.height } : {},
8521
+ ...st.color !== void 0 ? { color: st.color } : {},
8522
+ ...st.underline ? { underline: true } : {},
8523
+ ...extra
8524
+ } : {};
8525
+ const emit = (t, extra) => {
8526
+ if (!t) return;
8527
+ const a = attrs(extra);
8528
+ const last = chunks[chunks.length - 1];
8529
+ if (!extra && last && JSON.stringify(last.attrs) === JSON.stringify(a))
8530
+ last.text += t;
8531
+ else chunks.push({ text: t, attrs: a });
8532
+ };
8533
+ const codeBody = (i2) => {
8534
+ const end = text.indexOf(";", i2);
8535
+ return end === -1 ? ["", i2] : [text.slice(i2, end), end + 1];
8536
+ };
8537
+ let i = 0;
8538
+ while (i < text.length) {
8539
+ const ch = text[i];
8540
+ if (ch === "{") {
8541
+ stack.push(st);
8542
+ st = { ...st };
8543
+ i++;
8544
+ } else if (ch === "}") {
8545
+ st = stack.pop() ?? st;
8546
+ i++;
8547
+ } else if (ch !== "\\") {
8548
+ emit(st.symbol ? SYMBOL_FONT[ch] ?? ch : ch);
8549
+ i++;
8550
+ } else {
8551
+ const next = text[i + 1] ?? "";
8552
+ if (next === "\\" || next === "{" || next === "}") {
8553
+ emit(next);
8554
+ i += 2;
8555
+ } else if (next === "P") {
8556
+ emit("\n");
8557
+ i += 2;
8558
+ } else if (next === "~") {
8559
+ emit(" ");
8560
+ i += 2;
8561
+ } else if (next === "L" || next === "l") {
8562
+ st = { ...st, underline: next === "L" };
8563
+ i += 2;
8564
+ } else if ("OoKk".includes(next)) {
8565
+ i += 2;
8566
+ } else if (next === "S") {
8567
+ const [body, at] = codeBody(i + 2);
8568
+ const m = /^(.*?)([\^#/])(.*)$/s.exec(body);
8569
+ const top = m?.[1].trim() ?? "";
8570
+ const bottom = m?.[3].trim() ?? "";
8571
+ if (opts && m && m[2] === "^" && top && !bottom)
8572
+ emit(top, { script: "super" });
8573
+ else if (opts && m && m[2] === "^" && !top && bottom)
8574
+ emit(bottom, { script: "sub" });
8575
+ else emit(stackText(body));
8576
+ i = at === i + 2 ? i + 2 : at;
8577
+ } else if ((next === "U" || next === "u") && /^\+[0-9a-fA-F]{4}/.test(text.slice(i + 2, i + 7))) {
8578
+ emit(String.fromCodePoint(parseInt(text.slice(i + 3, i + 7), 16)));
8579
+ i += 7;
8580
+ } else if (next === "f" || next === "F") {
8581
+ const [body, at] = codeBody(i + 2);
8582
+ st = { ...st, symbol: isSymbolFont(body) };
8583
+ if (opts && !st.symbol && body) {
8584
+ const [name, ...flags] = body.split("|");
8585
+ const file = name.includes(".");
8586
+ const face = fontFromDxfStyle(
8587
+ file ? name : void 0,
8588
+ file ? void 0 : name
8589
+ );
8590
+ const flag = (k) => flags.find((f) => f[0] === k)?.slice(1);
8591
+ st.font = face.font;
8592
+ st.bold = flag("b") !== void 0 ? flag("b") === "1" : face.bold;
8593
+ st.italic = flag("i") !== void 0 ? flag("i") === "1" : face.italic;
8594
+ }
8595
+ i = at === i + 2 ? i + 2 : at;
8596
+ } else if (opts && next === "H") {
8597
+ const [body, at] = codeBody(i + 2);
8598
+ const v = Number.parseFloat(body);
8599
+ if (Number.isFinite(v) && v > 0)
8600
+ st = {
8601
+ ...st,
8602
+ height: /x$/i.test(body) ? (st.height ?? 1) * v : v / (opts.heightMm > 0 ? opts.heightMm : v)
8603
+ };
8604
+ i = at === i + 2 ? i + 2 : at;
8605
+ } else if (opts && next === "C") {
8606
+ const [body, at] = codeBody(i + 2);
8607
+ st = { ...st, color: aciToHex(Number.parseInt(body, 10)) };
8608
+ i = at === i + 2 ? i + 2 : at;
8609
+ } else {
8610
+ const end = text.indexOf(";", i + 1);
8611
+ i = end === -1 ? i + 2 : end + 1;
8612
+ }
8613
+ }
8614
+ }
8615
+ return chunks;
8616
+ };
8617
+ var stripMtextCodes = (text) => scanMtext(text).map((c) => c.text).join("");
8618
+ var mtextWidthFactor = (text) => {
8619
+ const m = /\\W([0-9.]+);/.exec(text);
8620
+ const v = m ? Number(m[1]) : NaN;
8621
+ return Number.isFinite(v) && v > 0 && v !== 1 ? v : void 0;
8622
+ };
8623
+ var mtextFontFlags = (text) => {
8624
+ const m = /\\f[^|;]*((?:\|[a-z]\d+)*)/.exec(text);
8625
+ if (!m) return {};
8626
+ return {
8627
+ .../\|b1/.test(m[1]) ? { bold: true } : {},
8628
+ .../\|i1/.test(m[1]) ? { italic: true } : {}
8629
+ };
8630
+ };
8631
+ var decodeControlCodes = (text) => text.replace(
8632
+ /\^M\^J|\^[IJM ]|\r\n?/g,
8633
+ (m) => m === "^I" ? " " : m === "^ " ? "^" : "\n"
8634
+ );
8635
+ var cleanText = (raw, mtext) => decodeControlCodes(decodeTextEscapes(mtext ? stripMtextCodes(raw) : raw));
8636
+ var cleanRichText = (raw, heightMm) => {
8637
+ let content = "";
8638
+ const spans = [];
8639
+ for (const c of scanMtext(raw, { heightMm })) {
8640
+ const text = decodeControlCodes(decodeTextEscapes(c.text));
8641
+ spans.push({
8642
+ start: content.length,
8643
+ end: content.length + text.length,
8644
+ ...c.attrs
8645
+ });
8646
+ content += text;
8647
+ }
8648
+ return { content, spans: normalizeSpans(spans, content) };
8649
+ };
8650
+ var encodeStack = (s) => encodeMtext(s).replace(/[#/;]/g, (c) => `\\${c}`);
8651
+ var encodeRichMtext = (content, spans, face) => {
8652
+ const base = parseFontFace(face);
8653
+ let out = "";
8654
+ let at = 0;
8655
+ for (const s of normalizeSpans(spans, content)) {
8656
+ out += encodeMtext(content.slice(at, s.start));
8657
+ const fontCode = s.font !== void 0 || s.bold !== void 0 || s.italic !== void 0 ? `\\f${fontTypeface(resolveFont(s.font ?? base.font))}|b${s.bold ?? base.bold ? 1 : 0}|i${s.italic ?? base.italic ? 1 : 0};` : "";
8658
+ const codes = fontCode + (s.height !== void 0 ? `\\H${s.height}x;` : "") + (s.color ? `\\C${nearestAci(s.color)};` : "") + (s.underline ? "\\L" : "");
8659
+ const body = content.slice(s.start, s.end);
8660
+ const text = s.script && !/[\n\t]/.test(body) ? s.script === "super" ? `\\S${encodeStack(body)}^;` : `\\S^${encodeStack(body)};` : encodeMtext(body);
8661
+ out += codes || s.script ? `{${codes}${text}}` : text;
8662
+ at = s.end;
8663
+ }
8664
+ return out + encodeMtext(content.slice(at));
8665
+ };
8666
+
6872
8667
  // ../core/src/export/dxf/dxfExtras.ts
6873
8668
  import { Point, DxfObject } from "@tarikjabiri/dxf";
6874
8669
  var RAW_COLOR_BY_BLOCK = -1056964608;
@@ -7030,7 +8825,7 @@ var MultiLeaderEntity = class extends Point {
7030
8825
  dx.push(45, 1);
7031
8826
  dx.push(170, 1);
7032
8827
  dx.push(90, RAW_COLOR_BY_BLOCK);
7033
- dx.push(171, 1);
8828
+ dx.push(171, a.attachment);
7034
8829
  dx.push(172, 1);
7035
8830
  dx.push(91, RAW_COLOR_NONE);
7036
8831
  dx.push(141, 1.5);
@@ -7187,17 +8982,35 @@ var mtextAttachment = (hAlign, vAlign) => {
7187
8982
  const col = hAlign === "left" ? 1 : hAlign === "center" ? 2 : 3;
7188
8983
  return row + col;
7189
8984
  };
7190
- var escapeMtext = (s) => s.replace(/\\/g, "\\\\").replace(/\{/g, "\\{").replace(/\}/g, "\\}").replace(/\n/g, "\\P").replace(/\t/g, "^I");
7191
- var dxfLineTypeName = (lineType) => {
8985
+ var dxfLineTypeName = (lineType, lineTypes) => {
7192
8986
  if (!lineType) return void 0;
7193
- const p = getLineType(lineType);
8987
+ const p = resolveLineType(lineType, lineTypes);
7194
8988
  if (!p || p.dashesMm.length === 0) return void 0;
7195
8989
  return p.dxfName ?? p.name.toUpperCase();
7196
8990
  };
7197
- var DIM_STYLE_NAME = "LINEA";
8991
+ var setLineWeight = (target, weight) => {
8992
+ const emit = target.dxfy.bind(target);
8993
+ target.dxfy = (dx) => {
8994
+ const start = dx.lines.length;
8995
+ emit(dx);
8996
+ const lines = dx.lines;
8997
+ for (let i = start; i < lines.length; i += 2)
8998
+ if (lines[i] === 370) {
8999
+ lines[i + 1] = weight;
9000
+ return;
9001
+ }
9002
+ let markers = 0;
9003
+ for (let i = start; i < lines.length; i += 2)
9004
+ if (lines[i] === 100 && ++markers === 2) {
9005
+ lines.splice(i, 0, 370, weight);
9006
+ return;
9007
+ }
9008
+ lines.push(370, weight);
9009
+ };
9010
+ };
7198
9011
  var optionsFor = (obj, ctx) => {
7199
9012
  const layer = ctx.doc.layers.find((l) => l.id === obj.layerId);
7200
- const dashed = (getLineType(obj.lineType ?? layer?.lineType ?? "")?.dashesMm.length ?? 0) > 0;
9013
+ const dashed = (resolveLineType(obj.lineType ?? layer?.lineType ?? "", ctx.doc.lineTypes)?.dashesMm.length ?? 0) > 0;
7201
9014
  const lineTypeScale = (obj.lineTypeScale ?? 1) * (obj.lineWidth ?? layer?.lineWidth ?? 0.25);
7202
9015
  const trueColor = obj.color ? hexToTrueColor(obj.color) : null;
7203
9016
  return {
@@ -7206,7 +9019,7 @@ var optionsFor = (obj, ctx) => {
7206
9019
  // color (420) which wins wherever it is supported.
7207
9020
  ...obj.color ? { colorNumber: nearestAci(obj.color) } : {},
7208
9021
  ...trueColor !== null ? { trueColor: String(trueColor) } : {},
7209
- ...dxfLineTypeName(obj.lineType) ? { lineType: dxfLineTypeName(obj.lineType) } : {},
9022
+ ...dxfLineTypeName(obj.lineType, ctx.doc.lineTypes) ? { lineType: dxfLineTypeName(obj.lineType, ctx.doc.lineTypes) } : {},
7210
9023
  ...dashed && lineTypeScale !== 1 ? { lineTypeScale } : {}
7211
9024
  };
7212
9025
  };
@@ -7251,28 +9064,87 @@ var addArrowhead = (space, tip, toward, size, type, opts) => {
7251
9064
  break;
7252
9065
  }
7253
9066
  };
7254
- var textStyleFor2 = (ctx, font) => {
9067
+ var uniqueTableName = (taken, base) => {
9068
+ let name = base;
9069
+ for (let i = 2; taken.has(name.toUpperCase()); i++) name = `${base}_${i}`;
9070
+ taken.add(name.toUpperCase());
9071
+ return name;
9072
+ };
9073
+ var writableFace = (font) => {
7255
9074
  const def = resolveFont(font);
7256
- const { bold, italic } = parseFontFace(font);
7257
- const suffix = (bold ? "B" : "") + (italic ? "I" : "");
7258
- const key = fontFace(def.id, bold, italic);
7259
- let rec = ctx.textStyles.get(key);
7260
- if (!rec) {
7261
- const style = ctx.writer.document.tables.styleTable.addStyle(
7262
- `LINEA_${def.id.toUpperCase()}${suffix ? `_${suffix}` : ""}`
7263
- );
7264
- style.fontFileName = def.dxfFontFile;
7265
- rec = { name: style.name, handle: style.handle };
7266
- ctx.textStyles.set(key, rec);
7267
- }
9075
+ const { font: bare, bold, italic } = parseFontFace(font);
9076
+ const foreign = bare !== void 0 && bare !== "" && bare !== def.id;
9077
+ const fontId = foreign ? bare : def.id;
9078
+ return {
9079
+ face: fontFace(fontId, bold, italic),
9080
+ fontFile: foreign ? `${fontId}.ttf` : def.dxfFontFile,
9081
+ fontId,
9082
+ foreign
9083
+ };
9084
+ };
9085
+ var addStyleRecord = (ctx, key, name, font, widthFactor = 1, fixedHeight = 0) => {
9086
+ const { face, fontFile } = writableFace(font);
9087
+ const table = ctx.writer.document.tables.styleTable;
9088
+ const predefined = name.toUpperCase() === "STANDARD" && !ctx.styleNames.has("STANDARD") ? table.records.find((r) => r.name.toUpperCase() === "STANDARD") : void 0;
9089
+ const style = predefined ?? table.addStyle(uniqueTableName(ctx.styleNames, name));
9090
+ if (predefined) ctx.styleNames.add("STANDARD");
9091
+ style.fontFileName = fontFile;
9092
+ style.widthFactor = widthFactor;
9093
+ style.fixedTextHeight = fixedHeight;
9094
+ const rec = { name: style.name, handle: style.handle, face };
9095
+ ctx.textStyles.set(key, rec);
7268
9096
  return rec;
7269
9097
  };
9098
+ var createDocTextStyles = (ctx) => {
9099
+ for (const s of ctx.doc.textStyles)
9100
+ addStyleRecord(
9101
+ ctx,
9102
+ `doc:${s.id}`,
9103
+ sanitizeLayerName(s.name),
9104
+ fontFace(s.font, s.bold, s.italic),
9105
+ s.widthFactor,
9106
+ s.textHeight
9107
+ );
9108
+ };
9109
+ var dimTextStyleFor = (ctx, face, widthFactor) => {
9110
+ const want = writableFace(face).face;
9111
+ const own = ctx.doc.textStyles.find(
9112
+ (s) => writableFace(fontFace(s.font, s.bold, s.italic)).face === want && Math.abs(s.widthFactor - widthFactor) < 1e-9
9113
+ );
9114
+ const ownRec = own && ctx.textStyles.get(`doc:${own.id}`);
9115
+ if (ownRec) return ownRec;
9116
+ if (Math.abs(widthFactor - 1) < 1e-9) return textStyleFor2(ctx, face);
9117
+ const key = `${want}|w${widthFactor}`;
9118
+ return ctx.textStyles.get(key) ?? addStyleRecord(
9119
+ ctx,
9120
+ key,
9121
+ `${textStyleFor2(ctx, face).name}_W${Math.round(widthFactor * 100)}`,
9122
+ face,
9123
+ widthFactor
9124
+ );
9125
+ };
9126
+ var textStyleFor2 = (ctx, font) => {
9127
+ const { face, fontId, foreign } = writableFace(font);
9128
+ const { bold, italic } = parseFontFace(face);
9129
+ const suffix = (bold ? "B" : "") + (italic ? "I" : "");
9130
+ return ctx.textStyles.get(face) ?? addStyleRecord(
9131
+ ctx,
9132
+ face,
9133
+ `LINEA_${(foreign ? fontId.replace(/[^A-Za-z0-9_-]/g, "_") : fontId).toUpperCase()}${suffix ? `_${suffix}` : ""}`,
9134
+ face
9135
+ );
9136
+ };
9137
+ var textRecordFor = (ctx, obj, face) => {
9138
+ const base = textStyleFor(obj, ctx.doc.textStyles);
9139
+ const own = ctx.textStyles.get(`doc:${base.id}`);
9140
+ return own && writableFace(fontFace(base.font, base.bold, base.italic)).face === writableFace(face).face ? own : textStyleFor2(ctx, face);
9141
+ };
7270
9142
  var addTextLines = (space, content, position, height, rotation, hAlign, vAlign, opts, measure, mtextWidth, fontOpts = {}) => {
7271
- const { styleName, font, widthFactor = 1 } = fontOpts;
9143
+ const { styleName, font, widthFactor = 1, spans } = fontOpts;
7272
9144
  const m = boundMeasurer(measure, font, widthFactor);
7273
9145
  const block = textBlock(content, height, m, mtextWidth);
7274
- if (block.lines.length > 1 || mtextWidth !== void 0 && mtextWidth > 0 || content.includes(" ")) {
7275
- const value = (widthFactor !== 1 ? `\\W${widthFactor};` : "") + escapeMtext(content);
9146
+ if (block.lines.length > 1 || mtextWidth !== void 0 && mtextWidth > 0 || content.includes(" ") || !!spans?.length) {
9147
+ const value = (widthFactor !== 1 ? `\\W${widthFactor};` : "") + (spans?.length ? encodeRichMtext(content, spans, font) : encodeMtext(content));
7276
9148
  const mtext = space.addMText(v3(position), height, value, {
7277
9149
  rotation: rotation * RAD2DEG2,
7278
9150
  attachmentPoint: mtextAttachment(hAlign, vAlign),
@@ -7298,11 +9170,84 @@ var addTextLines = (space, content, position, height, rotation, hAlign, vAlign,
7298
9170
  if (styleName) text.textStyle = styleName;
7299
9171
  });
7300
9172
  };
9173
+ var documentPattern = (ctx, name) => ctx.doc.hatchPatterns.find(
9174
+ (p) => hatchPatternKey(p.name) === hatchPatternKey(name)
9175
+ );
9176
+ var injectHatchPatternLines = (content, ctx) => {
9177
+ if (ctx.doc.hatchPatterns.length === 0 && ctx.hatchOrigins.size === 0)
9178
+ return content;
9179
+ const out = [];
9180
+ let pos = 0;
9181
+ for (; ; ) {
9182
+ const start = content.indexOf("\n0\nHATCH\n", pos);
9183
+ if (start === -1) break;
9184
+ const rest = content.slice(start + 8);
9185
+ const m = /\n0\n(?=[A-Z][A-Z0-9_]*\n)/.exec(rest);
9186
+ const end = m ? start + 8 + m.index : content.length;
9187
+ let record = content.slice(start, end);
9188
+ const handle = /\n5\n([0-9A-Fa-f]+)\n/.exec(record)?.[1];
9189
+ const bodyAt = record.indexOf("\nAcDbHatch\n");
9190
+ const body = bodyAt === -1 ? record : record.slice(bodyAt);
9191
+ const name = /\n2\n([^\n]*)\n/.exec(body)?.[1];
9192
+ const anchor = handle ? ctx.hatchOrigins.get(handle) : void 0;
9193
+ const own = name ? documentPattern(ctx, name) : void 0;
9194
+ const pat = own ?? (name && anchor ? resolveHatchPattern(name, []) : void 0);
9195
+ const m77 = pat ? /\n77\n[^\n]*\n/.exec(body) : null;
9196
+ if (pat && m77 && bodyAt !== -1) {
9197
+ const angle2 = Number(/\n52\n([^\n]*)\n/.exec(body)?.[1] ?? 0) * Math.PI / 180;
9198
+ const scale3 = Number(/\n41\n([^\n]*)\n/.exec(body)?.[1] ?? 1) || 1;
9199
+ const cos = Math.cos(angle2);
9200
+ const sin = Math.sin(angle2);
9201
+ const bake = (x, y) => [
9202
+ (cos * x - sin * y) * scale3,
9203
+ (sin * x + cos * y) * scale3
9204
+ ];
9205
+ const lines = pat.lines.map((l) => {
9206
+ const [bx, by] = bake(l.origin.x, l.origin.y);
9207
+ const ox = bx + (anchor?.x ?? 0);
9208
+ const oy = by + (anchor?.y ?? 0);
9209
+ const bakedAngle = l.angle + angle2;
9210
+ const dx = (Math.cos(bakedAngle) * l.deltaX - Math.sin(bakedAngle) * l.deltaY) * scale3;
9211
+ const dy = (Math.sin(bakedAngle) * l.deltaX + Math.cos(bakedAngle) * l.deltaY) * scale3;
9212
+ const dashes = l.dashesMm ?? [];
9213
+ return `53
9214
+ ${(l.angle * 180 / Math.PI + angle2 * 180 / Math.PI).toFixed(6)}
9215
+ 43
9216
+ ${ox.toFixed(6)}
9217
+ 44
9218
+ ${oy.toFixed(6)}
9219
+ 45
9220
+ ${dx.toFixed(6)}
9221
+ 46
9222
+ ${dy.toFixed(6)}
9223
+ 79
9224
+ ${dashes.length}
9225
+ ` + dashes.map((d) => `49
9226
+ ${(d * scale3).toFixed(6)}
9227
+ `).join("");
9228
+ }).join("");
9229
+ const at = bodyAt + m77.index + m77[0].length;
9230
+ const tail = record.slice(at);
9231
+ const writerBlock = tail.startsWith("78\n") ? (() => {
9232
+ const stop = /\n(?:47|98)\n/.exec(tail);
9233
+ return stop ? stop.index + 1 : tail.length;
9234
+ })() : 0;
9235
+ record = record.slice(0, at) + `78
9236
+ ${pat.lines.length}
9237
+ ` + lines + tail.slice(writerBlock);
9238
+ if (own) record = record.replace(/\n76\n1\n/, "\n76\n2\n");
9239
+ }
9240
+ out.push(content.slice(pos, start), record);
9241
+ pos = end;
9242
+ }
9243
+ out.push(content.slice(pos));
9244
+ return out.join("");
9245
+ };
7301
9246
  var dxfPatternName = (name) => {
7302
9247
  const key = name.toUpperCase().replace(/-/g, "_").replace(/\./g, "_");
7303
9248
  return HatchPredefinedPatterns[key] ?? HatchPredefinedPatterns.ANSI31;
7304
9249
  };
7305
- var writeHatch = (space, obj, opts) => {
9250
+ var writeHatch = (space, obj, opts, ctx) => {
7306
9251
  if (obj.type !== "hatch") return;
7307
9252
  if (obj.fill.kind === "gradient") {
7308
9253
  opts = {
@@ -7326,9 +9271,11 @@ var writeHatch = (space, obj, opts) => {
7326
9271
  obj.fill.kind === "solid" || obj.fill.kind === "gradient" ? pattern({ name: HatchPredefinedPatterns.SOLID }) : obj.fill.kind === "pattern" ? (
7327
9272
  // The curated library names are the standard predefined patterns, so
7328
9273
  // this is a direct mapping; fill.angle/scale ARE the DXF rotation
7329
- // (code 52) and scale (code 41).
9274
+ // (code 52) and scale (code 41). A DOCUMENT pattern goes out under
9275
+ // its own name; its definition lines are spliced in afterwards
9276
+ // (injectHatchPatternLines — the writer has no custom patterns).
7330
9277
  pattern({
7331
- name: dxfPatternName(obj.fill.name),
9278
+ name: documentPattern(ctx, obj.fill.name) ? obj.fill.name.toUpperCase() : dxfPatternName(obj.fill.name),
7332
9279
  angle: obj.fill.angle * RAD2DEG2,
7333
9280
  scale: obj.fill.scale * obj.scale
7334
9281
  })
@@ -7339,10 +9286,12 @@ var writeHatch = (space, obj, opts) => {
7339
9286
  scale: obj.fill.spacing * obj.scale / ANSI31_BASE_SPACING
7340
9287
  })
7341
9288
  );
7342
- space.addHatch(boundary, fill, opts);
9289
+ const written = space.addHatch(boundary, fill, opts);
9290
+ if (obj.fill.kind === "pattern" && obj.fill.origin)
9291
+ ctx.hatchOrigins.set(written.handle, obj.fill.origin);
7343
9292
  };
7344
9293
  var writeDimensionGeometry = (target, geom, opts, ctx) => {
7345
- for (const seg of [...geom.extensionLines, ...geom.dimLines])
9294
+ for (const seg of [...geom.extensionLines, ...geom.dimLines, ...geom.leaders])
7346
9295
  target.addLine(v3(seg.a), v3(seg.b), opts);
7347
9296
  if (geom.dimArc)
7348
9297
  target.addArc(
@@ -7361,7 +9310,7 @@ var writeDimensionGeometry = (target, geom, opts, ctx) => {
7361
9310
  geom.arrowType,
7362
9311
  opts
7363
9312
  );
7364
- for (const text of geom.texts)
9313
+ for (const text of geom.labels)
7365
9314
  addTextLines(
7366
9315
  target,
7367
9316
  text.content,
@@ -7376,13 +9325,43 @@ var writeDimensionGeometry = (target, geom, opts, ctx) => {
7376
9325
  {
7377
9326
  styleName: textStyleFor2(ctx, text.font).name,
7378
9327
  font: text.font,
7379
- widthFactor: text.widthFactor
9328
+ widthFactor: text.widthFactor,
9329
+ spans: text.spans
7380
9330
  }
7381
9331
  );
7382
9332
  };
7383
- var dimStyleFor2 = (ctx, style) => {
9333
+ var addDimStyleRecord = (ctx, name, style) => {
9334
+ const table = ctx.writer.document.tables.dimStyleTable;
9335
+ const predefined = name.toUpperCase() === "STANDARD" && !ctx.dimStyleNames.has("STANDARD") ? table.records.find((r) => r.name.toUpperCase() === "STANDARD") : void 0;
9336
+ const rec = predefined ?? ctx.writer.addDimStyle(uniqueTableName(ctx.dimStyleNames, name));
9337
+ if (predefined) ctx.dimStyleNames.add("STANDARD");
7384
9338
  const scale3 = style.scale ?? 1;
9339
+ rec.DIMASZ = style.arrowSize;
9340
+ rec.DIMEXO = style.extensionOffset;
9341
+ rec.DIMEXE = style.extensionOvershoot;
9342
+ rec.DIMTXT = style.textHeight;
9343
+ rec.DIMGAP = style.textOffset;
9344
+ rec.DIMDEC = style.precision;
9345
+ if (scale3 !== 1) rec.DIMSCALE = scale3;
9346
+ if (style.arrowType !== "filled") rec.DIMBLK = DIMBLK_NAMES[style.arrowType];
9347
+ rec.DIMTXSTY = dimTextStyleFor(
9348
+ ctx,
9349
+ dimFontFace(style),
9350
+ style.widthFactor
9351
+ ).handle;
9352
+ return rec.name;
9353
+ };
9354
+ var createDocDimStyles = (ctx) => {
9355
+ for (const s of ctx.doc.dimStyles)
9356
+ ctx.dimStyleRecords.set(
9357
+ s.id,
9358
+ addDimStyleRecord(ctx, sanitizeLayerName(s.name), s)
9359
+ );
9360
+ };
9361
+ var dimOverrideStyleFor = (ctx, base, style) => {
9362
+ const baseName = ctx.dimStyleRecords.get(base.id) ?? ctx.dimStyleRecords.values().next().value ?? "Standard";
7385
9363
  const key = JSON.stringify([
9364
+ baseName,
7386
9365
  style.arrowSize,
7387
9366
  style.arrowType,
7388
9367
  style.extensionOffset,
@@ -7391,21 +9370,13 @@ var dimStyleFor2 = (ctx, style) => {
7391
9370
  style.textOffset,
7392
9371
  style.precision,
7393
9372
  dimFontFace(style),
7394
- scale3
9373
+ style.scale ?? 1
7395
9374
  ]);
7396
- let name = ctx.dimStyles.get(key);
7397
- if (name) return name;
7398
- name = ctx.dimStyles.size === 0 ? DIM_STYLE_NAME : `${DIM_STYLE_NAME}_${ctx.dimStyles.size}`;
7399
- const rec = ctx.writer.addDimStyle(name);
7400
- rec.DIMASZ = style.arrowSize;
7401
- rec.DIMEXO = style.extensionOffset;
7402
- rec.DIMEXE = style.extensionOvershoot;
7403
- rec.DIMTXT = style.textHeight;
7404
- rec.DIMGAP = style.textOffset;
7405
- rec.DIMDEC = style.precision;
7406
- if (scale3 !== 1) rec.DIMSCALE = scale3;
7407
- if (style.arrowType !== "filled") rec.DIMBLK = DIMBLK_NAMES[style.arrowType];
7408
- rec.DIMTXSTY = textStyleFor2(ctx, dimFontFace(style)).handle;
9375
+ const cached = ctx.dimStyles.get(key);
9376
+ if (cached) return cached;
9377
+ const n = (ctx.dimOverrideCounts.get(baseName) ?? 0) + 1;
9378
+ ctx.dimOverrideCounts.set(baseName, n);
9379
+ const name = addDimStyleRecord(ctx, `${baseName}_${n}`, style);
7409
9380
  ctx.dimStyles.set(key, name);
7410
9381
  return name;
7411
9382
  };
@@ -7431,26 +9402,38 @@ var writeDimension = (space, obj, opts, ctx) => {
7431
9402
  const blockName = `LINEA_DIM_${ctx.dimBlockCount++}`;
7432
9403
  const block = ctx.writer.addBlock(blockName);
7433
9404
  writeDimensionGeometry(block, geom, opts, ctx);
7434
- const styleName = obj.styleId || obj.styleOverride || obj.scale !== 1 ? dimStyleFor2(ctx, {
7435
- ...dimStyleFor(obj, ctx.doc.dimStyles),
9405
+ const baseStyle = dimStyleFor(obj, ctx.doc.dimStyles);
9406
+ const hasOverride = obj.styleOverride !== void 0 && Object.values(obj.styleOverride).some((v) => v !== void 0);
9407
+ const styleName = hasOverride || obj.scale !== 1 ? dimOverrideStyleFor(ctx, baseStyle, {
9408
+ ...baseStyle,
7436
9409
  ...obj.styleOverride,
7437
9410
  scale: obj.scale
7438
- }) : DIM_STYLE_NAME;
9411
+ }) : ctx.dimStyleRecords.get(baseStyle.id) ?? dimOverrideStyleFor(ctx, baseStyle, baseStyle);
9412
+ const content = obj.labels[0]?.content;
9413
+ const precision = {
9414
+ ...dimStyleFor(obj, ctx.doc.dimStyles),
9415
+ ...obj.styleOverride
9416
+ }.precision;
9417
+ const text = content === void 0 ? void 0 : content.split(DIM_DEFAULT_CONTENT[obj.kind]).join("<>").split(DIM_VALUE_TOKEN).join(
9418
+ obj.kind === "radial" || obj.kind === "diameter" ? formatDimValue(geom.value, precision) : "<>"
9419
+ );
7439
9420
  const dimOpts = {
7440
9421
  blockName,
7441
- text: geom.texts[0]?.content,
9422
+ ...text === void 0 ? {} : { text },
7442
9423
  styleName,
7443
9424
  // Text middle point (group 11) — without it a regenerating CAD places
7444
9425
  // the value at the style default instead of where the user saw it.
7445
- ...geom.texts[0] ? { middlePoint: v3(geom.texts[0].anchor) } : {},
9426
+ ...geom.labels[0] ? { middlePoint: v3(geom.labels[0].anchor) } : {},
7446
9427
  ...opts
7447
9428
  };
7448
9429
  switch (obj.kind) {
7449
9430
  case "angular": {
7450
9431
  const [vertex, a, b] = obj.points;
9432
+ const [frame] = dimensionLabelFrames(obj);
7451
9433
  space.addAngularPointsDim(v3(vertex), v3(a), v3(b), {
7452
9434
  ...dimOpts,
7453
- definitionPoint: v3(add(vertex, obj.offset))
9435
+ // A point on the arc (its middle): the radius and which arc.
9436
+ definitionPoint: v3(frame.toWorld((frame.s0 + frame.s1) / 2, 0))
7454
9437
  });
7455
9438
  break;
7456
9439
  }
@@ -7466,8 +9449,7 @@ var writeDimension = (space, obj, opts, ctx) => {
7466
9449
  }
7467
9450
  case "linear": {
7468
9451
  const [p1, p2] = obj.points;
7469
- const upLen = Math.hypot(obj.offset.x, obj.offset.y);
7470
- const u = upLen > 1e-9 ? perp(scale(obj.offset, 1 / upLen)) : norm(sub(p2, p1));
9452
+ const { u } = linearAxis(obj);
7471
9453
  space.addLinearDim(v3(p1), v3(p2), {
7472
9454
  ...dimOpts,
7473
9455
  definitionPoint: v3(geom.dimLines[0].b),
@@ -7479,6 +9461,13 @@ var writeDimension = (space, obj, opts, ctx) => {
7479
9461
  return true;
7480
9462
  };
7481
9463
  var writeObject = (space, obj, ctx) => {
9464
+ const before = space.entities.length;
9465
+ writeObjectEntities(space, obj, ctx);
9466
+ if (obj.lineWidth !== void 0)
9467
+ for (const entity of space.entities.slice(before))
9468
+ setLineWeight(entity, mmToLineWeight(obj.lineWidth));
9469
+ };
9470
+ var writeObjectEntities = (space, obj, ctx) => {
7482
9471
  const opts = optionsFor(obj, ctx);
7483
9472
  switch (obj.type) {
7484
9473
  case "line":
@@ -7523,12 +9512,27 @@ var writeObject = (space, obj, ctx) => {
7523
9512
  );
7524
9513
  break;
7525
9514
  case "hatch":
7526
- writeHatch(space, obj, opts);
9515
+ writeHatch(space, obj, opts, ctx);
7527
9516
  break;
7528
9517
  case "text": {
7529
9518
  const eff = resolveTextStyle(obj, ctx.doc.textStyles);
7530
9519
  const { arrowSize, arrowType } = eff;
7531
9520
  const face = fontFace(eff.font, eff.bold, eff.italic);
9521
+ for (const part of textFrameGeometry(
9522
+ obj,
9523
+ ctx.measure,
9524
+ ctx.doc.textStyles
9525
+ ))
9526
+ space.addLWPolyline(
9527
+ part.points.map((v) => ({
9528
+ point: { x: v.x, y: v.y },
9529
+ bulge: v.bulge
9530
+ })),
9531
+ {
9532
+ flags: part.closed ? LWPolylineFlags.Closed : LWPolylineFlags.None,
9533
+ ...opts
9534
+ }
9535
+ );
7532
9536
  if (obj.leaderLines.length > 0 && ctx.explodeLeaders) {
7533
9537
  for (const line of obj.leaderLines) {
7534
9538
  space.addLWPolyline(
@@ -7562,13 +9566,15 @@ var writeObject = (space, obj, ctx) => {
7562
9566
  junction,
7563
9567
  doglegDirection: { x: toText.x / gap, y: toText.y / gap },
7564
9568
  doglegLength: gap,
7565
- content: escapeMtext(obj.content),
9569
+ content: obj.spans?.length ? encodeRichMtext(obj.content, obj.spans, face) : encodeMtext(obj.content),
7566
9570
  textHeight: eff.textHeight,
7567
9571
  arrowSize,
7568
9572
  overallScale: obj.scale,
7569
9573
  textLocation: obj.position,
9574
+ // Model alignment → MTEXT-style attachment point (1–9).
9575
+ attachment: 1 + 3 * ["top", "center", "bottom"].indexOf(obj.vAlign) + ["left", "center", "right"].indexOf(obj.hAlign),
7570
9576
  styleHandle: ctx.mleaderStyleHandle,
7571
- textStyleHandle: textStyleFor2(ctx, face).handle,
9577
+ textStyleHandle: textRecordFor(ctx, obj, face).handle,
7572
9578
  lineTypeHandle: ctx.lineTypeHandle
7573
9579
  });
7574
9580
  entity.layerName = ctx.layerNames.get(obj.layerId) ?? "0";
@@ -7592,9 +9598,10 @@ var writeObject = (space, obj, ctx) => {
7592
9598
  ctx.measure,
7593
9599
  obj.width === void 0 ? void 0 : obj.width * obj.scale,
7594
9600
  {
7595
- styleName: textStyleFor2(ctx, face).name,
9601
+ styleName: textRecordFor(ctx, obj, face).name,
7596
9602
  font: face,
7597
- widthFactor: eff.widthFactor
9603
+ widthFactor: eff.widthFactor,
9604
+ spans: obj.spans
7598
9605
  }
7599
9606
  );
7600
9607
  break;
@@ -7694,6 +9701,14 @@ var buildDxf = (doc, measure, options = {}) => {
7694
9701
  const writer = new DxfWriter();
7695
9702
  writer.setUnits(Units.Millimeters);
7696
9703
  const emittedLtypes = /* @__PURE__ */ new Set();
9704
+ for (const t of doc.lineTypes) {
9705
+ if (t.dashesMm.length === 0) continue;
9706
+ const dxfName = t.name.toUpperCase();
9707
+ if (emittedLtypes.has(dxfName)) continue;
9708
+ emittedLtypes.add(dxfName);
9709
+ const descriptive = (t.description ?? t.name).replace(/[^\x20-\x7e]/g, "").slice(0, 47);
9710
+ writer.addLType(dxfName, descriptive, t.dashesMm);
9711
+ }
7697
9712
  for (const p of LINE_TYPE_DEFS) {
7698
9713
  if (p.dashesMm.length === 0) continue;
7699
9714
  const dxfName = p.dxfName ?? p.name.toUpperCase();
@@ -7715,18 +9730,22 @@ var buildDxf = (doc, measure, options = {}) => {
7715
9730
  layerNames.set(layer.id, name);
7716
9731
  if (name === "0") {
7717
9732
  const zero = writer.layer("0");
7718
- if (zero) layerRecords.set(layer.id, zero);
9733
+ if (zero) {
9734
+ setLineWeight(zero, mmToLineWeight(layer.lineWidth));
9735
+ layerRecords.set(layer.id, zero);
9736
+ }
7719
9737
  continue;
7720
9738
  }
7721
9739
  const flags = (layer.visible ? 0 : LayerFlags.Frozen) | (layer.locked ? LayerFlags.Locked : 0);
7722
9740
  const record = writer.addLayer(
7723
9741
  name,
7724
9742
  nearestAci(layer.color),
7725
- dxfLineTypeName(layer.lineType) ?? "Continuous",
9743
+ dxfLineTypeName(layer.lineType, doc.lineTypes) ?? "Continuous",
7726
9744
  flags || void 0
7727
9745
  );
7728
9746
  const tc = hexToTrueColor(layer.color);
7729
9747
  if (tc !== null) record.trueColor = tc;
9748
+ setLineWeight(record, mmToLineWeight(layer.lineWidth));
7730
9749
  layerRecords.set(layer.id, record);
7731
9750
  }
7732
9751
  const textStyleHandle = writer.document.tables.styleTable.records.find((r) => r.name === "Standard")?.handle ?? "0";
@@ -7762,9 +9781,15 @@ var buildDxf = (doc, measure, options = {}) => {
7762
9781
  textStyleHandle,
7763
9782
  lineTypeHandle,
7764
9783
  textStyles: /* @__PURE__ */ new Map(),
7765
- dimStyles: /* @__PURE__ */ new Map()
9784
+ styleNames: /* @__PURE__ */ new Set(),
9785
+ dimStyleRecords: /* @__PURE__ */ new Map(),
9786
+ dimStyleNames: /* @__PURE__ */ new Set(),
9787
+ dimStyles: /* @__PURE__ */ new Map(),
9788
+ dimOverrideCounts: /* @__PURE__ */ new Map(),
9789
+ hatchOrigins: /* @__PURE__ */ new Map()
7766
9790
  };
7767
- dimStyleFor2(ctx, doc.dimStyles[0]);
9791
+ createDocTextStyles(ctx);
9792
+ createDocDimStyles(ctx);
7768
9793
  for (const obj of doc.objects)
7769
9794
  writeObject(writer.document.blocks.modelSpace, obj, ctx);
7770
9795
  const layoutRecords = [];
@@ -7837,15 +9862,21 @@ var buildDxf = (doc, measure, options = {}) => {
7837
9862
  if (record) record.layoutObject = layoutObj.handle;
7838
9863
  }
7839
9864
  return {
7840
- content: injectStyleTypefaces(writer.stringify(), ctx.textStyles),
9865
+ content: injectHatchPatternLines(
9866
+ injectStyleTypefaces(writer.stringify(), ctx.textStyles),
9867
+ ctx
9868
+ ),
7841
9869
  exported: ctx.exported,
7842
9870
  skipped: ctx.skipped
7843
9871
  };
7844
9872
  };
7845
9873
  var injectStyleTypefaces = (content, textStyles) => {
7846
9874
  let out = content;
7847
- for (const [fontId, rec] of textStyles) {
7848
- const typeface = fontTypeface(resolveFont(fontId));
9875
+ for (const rec of textStyles.values()) {
9876
+ const fontId = rec.face;
9877
+ const { font: bare } = parseFontFace(fontId);
9878
+ const def = resolveFont(fontId);
9879
+ const typeface = bare !== void 0 && bare !== def.id ? bare : fontTypeface(def);
7849
9880
  const { bold, italic } = parseFontFace(fontId);
7850
9881
  const flags = (italic ? 16777216 : 0) | (bold ? 33554432 : 0);
7851
9882
  const marker = `
@@ -7875,6 +9906,7 @@ export {
7875
9906
  add,
7876
9907
  sub,
7877
9908
  scale,
9909
+ dot,
7878
9910
  len,
7879
9911
  dist,
7880
9912
  norm,
@@ -7894,10 +9926,12 @@ export {
7894
9926
  Vec2Schema,
7895
9927
  ColorSchema,
7896
9928
  FONTS,
9929
+ fontFace,
7897
9930
  parseFontFace,
7898
9931
  REAL_FACES,
7899
9932
  realFace,
7900
9933
  resolveFont,
9934
+ fontFromDxfStyle,
7901
9935
  TextStyleSchema,
7902
9936
  DEFAULT_TEXT_STYLE,
7903
9937
  textStyleFor,
@@ -7916,6 +9950,8 @@ export {
7916
9950
  approximateTextMeasurer,
7917
9951
  lineSegments,
7918
9952
  textBlock,
9953
+ normalizeSpans,
9954
+ richTextBlock,
7919
9955
  textHeight,
7920
9956
  textArrowSize,
7921
9957
  textArrowType,
@@ -7923,15 +9959,34 @@ export {
7923
9959
  textBoxCorners,
7924
9960
  textFrameGeometry,
7925
9961
  tableLayout,
9962
+ matIdentity,
9963
+ matMultiply,
9964
+ matApply,
9965
+ signedArcSweep,
9966
+ arcToBeziers,
9967
+ computeHatchLines,
9968
+ ellipseArcToBeziers,
9969
+ getHatchPattern,
9970
+ MAX_HATCH_SEGMENTS,
9971
+ estimatePatternSegments,
9972
+ computePatternGeometry,
9973
+ HatchPatternSchema,
9974
+ hatchPatternKey,
9975
+ resolveHatchPattern,
7926
9976
  getLineType,
7927
9977
  renderDashes,
9978
+ LineTypeSchema,
9979
+ lineTypeKey,
9980
+ resolveLineType,
7928
9981
  CURRENT_DOCUMENT_VERSION,
7929
9982
  dimensionRawToOffset,
9983
+ dimensionRawToLabels,
7930
9984
  migrateDocument,
7931
9985
  HEX_COLOR,
7932
9986
  valueParams,
7933
9987
  resolveDocumentObject,
7934
9988
  documentObjectResolver,
9989
+ BlockChildSchema,
7935
9990
  ensureScriptTranspiler,
7936
9991
  executeScriptModule,
7937
9992
  scriptTranspilerReady,
@@ -7949,9 +10004,17 @@ export {
7949
10004
  testBlockDefinition,
7950
10005
  getEvaluatedWorldChildren,
7951
10006
  ANSI31_BASE_SPACING,
10007
+ linearAxis,
7952
10008
  dimensionGeometry,
10009
+ hexToRgb,
7953
10010
  rgbToHex,
7954
10011
  aciToHex,
10012
+ lineWeightToMm,
10013
+ decodeTextEscapes,
10014
+ mtextWidthFactor,
10015
+ mtextFontFlags,
10016
+ cleanText,
10017
+ cleanRichText,
7955
10018
  buildDxf
7956
10019
  };
7957
- //# sourceMappingURL=chunk-NCNZ7A6M.js.map
10020
+ //# sourceMappingURL=chunk-VWED6V6F.js.map