lineadraw 0.10.0 → 0.11.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.
Files changed (35) hide show
  1. package/README.md +18 -7
  2. package/bin/lineadraw.mjs +60 -10
  3. package/dist/bridge.d.ts +1 -0
  4. package/dist/bridge.js +1 -1
  5. package/dist/{chunk-YYJAXJOY.js → chunk-7UFXC7OI.js} +1680 -141
  6. package/dist/chunk-7UFXC7OI.js.map +1 -0
  7. package/dist/chunk-FDLANLIC.js +1161 -0
  8. package/dist/chunk-FDLANLIC.js.map +1 -0
  9. package/dist/chunk-M3EDRSHW.js +81 -0
  10. package/dist/chunk-M3EDRSHW.js.map +1 -0
  11. package/dist/chunk-MYP7RUYK.js +9 -0
  12. package/dist/chunk-MYP7RUYK.js.map +1 -0
  13. package/dist/{chunk-L2CWZAKW.js → chunk-RNA2RDGL.js} +13 -6
  14. package/dist/chunk-RNA2RDGL.js.map +1 -0
  15. package/dist/dist-56K6CNML.js +188 -0
  16. package/dist/dist-56K6CNML.js.map +1 -0
  17. package/dist/dist-IEN64QSV.js +10 -0
  18. package/dist/dist-IEN64QSV.js.map +1 -0
  19. package/dist/emscripten-module-Q67P5WYC-ITCU5LXL.js +2424 -0
  20. package/dist/emscripten-module-Q67P5WYC-ITCU5LXL.js.map +1 -0
  21. package/dist/{exportDxf-C4CLQCC3.js → exportDxf-6XTXQ4XN.js} +2 -2
  22. package/dist/ffi-SHMEF5YV.js +82 -0
  23. package/dist/ffi-SHMEF5YV.js.map +1 -0
  24. package/dist/module-ES6BEMUI-BJFWC656.js +14 -0
  25. package/dist/module-ES6BEMUI-BJFWC656.js.map +1 -0
  26. package/dist/module-asyncify-2EFITU5U-7F5WZXRA.js +9 -0
  27. package/dist/module-asyncify-2EFITU5U-7F5WZXRA.js.map +1 -0
  28. package/dist/sdk.d.ts +868 -95
  29. package/dist/sdk.js +619 -558
  30. package/dist/sdk.js.map +1 -1
  31. package/lineadraw.d.ts +162 -8
  32. package/package.json +2 -2
  33. package/dist/chunk-L2CWZAKW.js.map +0 -1
  34. package/dist/chunk-YYJAXJOY.js.map +0 -1
  35. /package/dist/{exportDxf-C4CLQCC3.js.map → exportDxf-6XTXQ4XN.js.map} +0 -0
package/dist/sdk.js CHANGED
@@ -1,10 +1,3 @@
1
- import {
2
- BridgeError,
3
- DEFAULT_BRIDGE_PORT,
4
- bridgeFilePath,
5
- connectEditors,
6
- createBridge
7
- } from "./chunk-L2CWZAKW.js";
8
1
  import {
9
2
  ADVANCE_TABLES,
10
3
  ANSI31_BASE_SPACING,
@@ -13,6 +6,7 @@ import {
13
6
  ColorSchema,
14
7
  DEFAULT_DIM_STYLE,
15
8
  DEFAULT_TEXT_STYLE,
9
+ DimStyleOverrideSchema,
16
10
  DimStyleSchema,
17
11
  EXTRACTED_EXTRAS,
18
12
  EXTRACTED_RANGES,
@@ -24,18 +18,28 @@ import {
24
18
  PX_TO_MM,
25
19
  REAL_FACES,
26
20
  REFERENCE_FORMATS,
21
+ TextStyleOverrideSchema,
27
22
  TextStyleSchema,
28
23
  Vec2Schema,
29
24
  aciToHex,
30
25
  acquireInputTable,
31
26
  add,
32
27
  angle,
28
+ angleInArc,
33
29
  approximateTextMeasurer,
30
+ arcEndPoint,
31
+ arcFrom3Points,
32
+ arcMidPoint,
33
+ arcStartPoint,
34
+ arcSweep,
34
35
  arcToBeziers,
36
+ arrowLineStart,
35
37
  assertKnownStyleOverrideKeys,
36
38
  blockDefinitionExists,
37
39
  blockPivot,
38
40
  blockScriptId,
41
+ bulgeFrom3Points,
42
+ bulgeToArc,
39
43
  bytesToDataUrl,
40
44
  checkNamedInputs,
41
45
  cleanRichText,
@@ -43,6 +47,7 @@ import {
43
47
  closestPointOnSegment,
44
48
  computeHatchLines,
45
49
  computePatternGeometry,
50
+ curveTolerance,
46
51
  dataUrlBytes,
47
52
  decodeTextEscapes,
48
53
  dimFontFace,
@@ -51,6 +56,7 @@ import {
51
56
  dimensionRawToLabels,
52
57
  dimensionRawToOffset,
53
58
  dist,
59
+ distToArc,
54
60
  documentBlocks,
55
61
  documentObjectResolver,
56
62
  dot,
@@ -59,10 +65,12 @@ import {
59
65
  effectiveTextFont,
60
66
  ellipseArcToBeziers,
61
67
  embeddedAssetId,
68
+ emitLoopPath,
62
69
  ensureScriptTranspiler,
63
70
  estimatePatternSegments,
64
71
  executeScriptModule,
65
72
  expandWithBlockDependents,
73
+ findBlockScript,
66
74
  fontFace,
67
75
  fontFromDxfStyle,
68
76
  fromAngle,
@@ -73,6 +81,8 @@ import {
73
81
  getHatchPattern,
74
82
  getLineType,
75
83
  hashAsset,
84
+ hatchArea,
85
+ hatchLoop,
76
86
  hatchPatternKey,
77
87
  hexToRgb,
78
88
  hostedClosure,
@@ -80,14 +90,20 @@ import {
80
90
  inputLabel,
81
91
  inputPoints,
82
92
  inputRefs,
93
+ interpolateClosed,
94
+ interpolateFitPoints,
83
95
  invalidateBlockCache,
84
96
  isRequiredInput,
97
+ isValidNurbs,
85
98
  len,
86
99
  lineSegments,
87
100
  lineTypeKey,
88
101
  lineWeightToMm,
89
102
  linearAxis,
90
103
  listBlockMeta,
104
+ loopCorners,
105
+ loopCurves,
106
+ loopPolygon,
91
107
  mapInputValues,
92
108
  matApply,
93
109
  matIdentity,
@@ -97,15 +113,19 @@ import {
97
113
  mirrorObject,
98
114
  mtextFontFlags,
99
115
  mtextWidthFactor,
100
- norm,
101
116
  normAngle,
102
117
  normalizeSpans,
118
+ nurbsToBeziers,
119
+ onSandboxReset,
120
+ paintArrowhead,
103
121
  paramMismatch,
104
122
  parseFontFace,
105
123
  parseInputTable,
106
124
  parseParamsExport,
107
125
  perp,
126
+ pointOnArc,
108
127
  realFace,
128
+ releaseScriptModule,
109
129
  remapBlockInputRefs,
110
130
  renderDashes,
111
131
  resolveDocumentObject,
@@ -115,14 +135,17 @@ import {
115
135
  rgbToHex,
116
136
  richTextBlock,
117
137
  rotateObject,
138
+ sampleNurbs,
118
139
  scale,
119
140
  scaleObjectUniform,
120
141
  scanScriptId,
121
142
  scriptTranspilerReady,
122
143
  selectionFits,
123
144
  signedArcSweep,
145
+ splineThrough,
124
146
  sub,
125
147
  tableLayout,
148
+ tessellateArc,
126
149
  testBlockDefinition,
127
150
  textArrowSize,
128
151
  textArrowType,
@@ -134,108 +157,25 @@ import {
134
157
  textWidth,
135
158
  translateObject,
136
159
  valueParams,
160
+ verticesToLoop,
137
161
  withEmptyLists
138
- } from "./chunk-YYJAXJOY.js";
162
+ } from "./chunk-7UFXC7OI.js";
139
163
  import {
140
164
  embeddedPdfFonts
141
165
  } from "./chunk-L4FNH35X.js";
166
+ import {
167
+ BridgeError,
168
+ DEFAULT_BRIDGE_PORT,
169
+ bridgeFilePath,
170
+ connectEditors,
171
+ createBridge
172
+ } from "./chunk-RNA2RDGL.js";
142
173
  import "./chunk-PZ5AY32C.js";
143
174
 
144
175
  // src/document.ts
145
176
  import { readFile, writeFile } from "fs/promises";
146
177
  import { dirname, isAbsolute, resolve } from "path";
147
178
 
148
- // ../core/src/geometry/arcs.ts
149
- var arcSweep = (a) => {
150
- const s = normAngle(a.endAngle - a.startAngle);
151
- return s === 0 ? Math.PI * 2 : s;
152
- };
153
- var pointOnArc = (a, angle2) => ({
154
- x: a.center.x + Math.cos(angle2) * a.radius,
155
- y: a.center.y + Math.sin(angle2) * a.radius
156
- });
157
- var arcStartPoint = (a) => pointOnArc(a, a.startAngle);
158
- var arcEndPoint = (a) => pointOnArc(a, a.endAngle);
159
- var arcMidPoint = (a) => pointOnArc(a, a.startAngle + arcSweep(a) / 2);
160
- var angleInArc = (a, angle2) => normAngle(angle2 - a.startAngle) <= arcSweep(a) + 1e-12;
161
- var bulgeToArc = (a, b, bulge) => {
162
- if (Math.abs(bulge) < 1e-12) return null;
163
- const chord = dist(a, b);
164
- if (chord < 1e-12) return null;
165
- const sweep = 4 * Math.atan(bulge);
166
- const radius = Math.abs(chord / (2 * Math.sin(sweep / 2)));
167
- const m = mid(a, b);
168
- const dir = scale(sub(b, a), 1 / chord);
169
- const h = chord / 2 / Math.tan(sweep / 2);
170
- const center = add(m, scale(perp(dir), h));
171
- const angA = angle(sub(a, center));
172
- const angB = angle(sub(b, center));
173
- return bulge > 0 ? { center, radius, startAngle: angA, endAngle: angB } : { center, radius, startAngle: angB, endAngle: angA };
174
- };
175
- var bulgeFrom3Points = (a, m, b) => {
176
- let turn = angle(sub(b, m)) - angle(sub(m, a));
177
- while (turn > Math.PI) turn -= 2 * Math.PI;
178
- while (turn < -Math.PI) turn += 2 * Math.PI;
179
- return Math.tan(turn / 2);
180
- };
181
- var circleFrom3Points = (p1, p2, p3) => {
182
- const ax = p1.x;
183
- const ay = p1.y;
184
- const bx = p2.x;
185
- const by = p2.y;
186
- const cx = p3.x;
187
- const cy = p3.y;
188
- const d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
189
- if (Math.abs(d) < 1e-12) return null;
190
- const a2 = ax * ax + ay * ay;
191
- const b2 = bx * bx + by * by;
192
- const c2 = cx * cx + cy * cy;
193
- const center = {
194
- x: (a2 * (by - cy) + b2 * (cy - ay) + c2 * (ay - by)) / d,
195
- y: (a2 * (cx - bx) + b2 * (ax - cx) + c2 * (bx - ax)) / d
196
- };
197
- return { center, radius: dist(center, p1) };
198
- };
199
- var arcFrom3Points = (start, on, end) => {
200
- const c = circleFrom3Points(start, on, end);
201
- if (!c) return null;
202
- const angS = angle(sub(start, c.center));
203
- const angO = angle(sub(on, c.center));
204
- const angE = angle(sub(end, c.center));
205
- const ccw = {
206
- center: c.center,
207
- radius: c.radius,
208
- startAngle: angS,
209
- endAngle: angE
210
- };
211
- if (angleInArc(ccw, angO)) return ccw;
212
- return {
213
- center: c.center,
214
- radius: c.radius,
215
- startAngle: angE,
216
- endAngle: angS
217
- };
218
- };
219
- var closestPointOnArc = (a, p) => {
220
- const v = sub(p, a.center);
221
- const ang = angle(v);
222
- if (angleInArc(a, ang)) return pointOnArc(a, ang);
223
- const s = arcStartPoint(a);
224
- const e = arcEndPoint(a);
225
- return dist(p, s) <= dist(p, e) ? s : e;
226
- };
227
- var distToArc = (a, p) => dist(p, closestPointOnArc(a, p));
228
- var tessellateArc = (a, chordTolerance = 0.1) => {
229
- const sweep = arcSweep(a);
230
- const maxStep = a.radius <= chordTolerance ? sweep : 2 * Math.acos(Math.max(-1, 1 - chordTolerance / a.radius));
231
- const n2 = Math.max(2, Math.ceil(sweep / Math.max(maxStep, 1e-3)));
232
- const pts = [];
233
- for (let i = 0; i <= n2; i++) {
234
- pts.push(pointOnArc(a, a.startAngle + sweep * i / n2));
235
- }
236
- return pts;
237
- };
238
-
239
179
  // ../core/src/geometry/ellipse.ts
240
180
  var ellipseSweep = (e) => {
241
181
  const s = normAngle(e.endAngle - e.startAngle);
@@ -353,12 +293,6 @@ var chainLength = (points, closed) => {
353
293
  return sum;
354
294
  };
355
295
  var polylineArea = (points) => polygonArea(verticesToPolygon(points));
356
- var hatchArea = (loops) => {
357
- const areas = loops.map((l) => polygonArea(verticesToPolygon(l.points)));
358
- if (areas.length === 0) return 0;
359
- const holes = areas.slice(1).reduce((a, b) => a + b, 0);
360
- return Math.max(areas[0] - holes, 0);
361
- };
362
296
 
363
297
  // ../core/src/model/document.ts
364
298
  import { z as z2 } from "zod";
@@ -366,11 +300,13 @@ import { z as z2 } from "zod";
366
300
  // ../core/src/model/paperLayout.ts
367
301
  import { z } from "zod";
368
302
  var SHEET_PRESETS = {
303
+ A5: [148, 210],
369
304
  A4: [210, 297],
370
305
  A3: [297, 420],
371
306
  A2: [420, 594],
372
307
  A1: [594, 841],
373
- A0: [841, 1189]
308
+ A0: [841, 1189],
309
+ Letter: [215.9, 279.4]
374
310
  };
375
311
  var resolveSheetSize = (sheet = "A3", defaultOrientation = "landscape") => {
376
312
  if (typeof sheet !== "string" && "width" in sheet)
@@ -421,7 +357,8 @@ var LayerSchema = z2.object({
421
357
  });
422
358
  var DEFAULT_DOCUMENT_NAME = "Untitled project";
423
359
  var DocumentSettingsSchema = z2.object({
424
- units: z2.enum(["mm", "in"]).default("mm")
360
+ units: z2.enum(["mm", "in"]).default("mm"),
361
+ modelScale: z2.number().positive().default(1)
425
362
  });
426
363
  var LineaDocumentSchema = z2.preprocess(
427
364
  migrateDocument,
@@ -438,7 +375,7 @@ var LineaDocumentSchema = z2.preprocess(
438
375
  /** Document-wide settings (`DocumentSettingsSchema`). `units` is
439
376
  * declarative for now: geometry is always stored in mm and nothing
440
377
  * renders in inches yet. */
441
- settings: DocumentSettingsSchema.default({ units: "mm" }),
378
+ settings: DocumentSettingsSchema.default({ units: "mm", modelScale: 1 }),
442
379
  layers: z2.array(LayerSchema).min(1),
443
380
  currentLayerId: z2.string(),
444
381
  /** Array order is draw order (bottom to top).
@@ -672,10 +609,10 @@ var SvgSink = class {
672
609
  transforms.push(
673
610
  `translate(${n(tp.x)} ${n(-tp.y)}) scale(${n(w)} 1) translate(${n(-tp.x)} ${n(tp.y)})`
674
611
  );
675
- const rot2 = transforms.length > 0 ? ` transform="${transforms.join(" ")}"` : "";
612
+ const rot = transforms.length > 0 ? ` transform="${transforms.join(" ")}"` : "";
676
613
  const alpha = t.opacity !== void 0 && t.opacity < 1 ? ` fill-opacity="${n(t.opacity)}"` : "";
677
614
  this.els.push(
678
- `<text x="${n(tp.x)}" y="${n(-tp.y)}" font-size="${n(t.heightMm / def.capHeight * scale2)}" font-family="${def.cssStack}"${face} fill="${t.color}"${alpha} text-anchor="${anchor}"${rot2}>${escapeXml(t.text)}</text>`
615
+ `<text x="${n(tp.x)}" y="${n(-tp.y)}" font-size="${n(t.heightMm / def.capHeight * scale2)}" font-family="${def.cssStack}"${face} fill="${t.color}"${alpha} text-anchor="${anchor}"${rot}>${escapeXml(t.text)}</text>`
679
616
  );
680
617
  }
681
618
  measureText(text, heightMm, font, widthFactor) {
@@ -690,10 +627,10 @@ var SvgSink = class {
690
627
  });
691
628
  const scale2 = Math.hypot(this.m[0], this.m[1]) || 1;
692
629
  const deg = -i.rotation * 180 / Math.PI;
693
- const rot2 = deg !== 0 ? ` transform="rotate(${n(deg)} ${n(topLeft.x)} ${n(-topLeft.y)})"` : "";
630
+ const rot = deg !== 0 ? ` transform="rotate(${n(deg)} ${n(topLeft.x)} ${n(-topLeft.y)})"` : "";
694
631
  const opacity = i.opacity < 1 ? ` opacity="${n(i.opacity)}"` : "";
695
632
  this.els.push(
696
- `<image x="${n(topLeft.x)}" y="${n(-topLeft.y)}" width="${n(i.widthMm * scale2)}" height="${n(i.heightMm * scale2)}" preserveAspectRatio="none"${opacity}${rot2} href="${i.data}"/>`
633
+ `<image x="${n(topLeft.x)}" y="${n(-topLeft.y)}" width="${n(i.widthMm * scale2)}" height="${n(i.heightMm * scale2)}" preserveAspectRatio="none"${opacity}${rot} href="${i.data}"/>`
697
634
  );
698
635
  }
699
636
  pushTransform(m) {
@@ -826,6 +763,22 @@ var paintEllipse = (obj, sink, env) => {
826
763
  sink.stroke(env.style);
827
764
  };
828
765
 
766
+ // ../core/src/paint/painters/spline.ts
767
+ var paintSpline = (obj, sink, env) => {
768
+ sink.beginPath();
769
+ const pieces = nurbsToBeziers(obj);
770
+ if (pieces && pieces.length > 0) {
771
+ sink.moveTo(pieces[0].start);
772
+ for (const b of pieces) sink.bezierTo(b.c1, b.c2, b.end);
773
+ } else {
774
+ const pts = sampleNurbs(obj, curveTolerance(obj));
775
+ if (pts.length < 2) return;
776
+ sink.moveTo(pts[0]);
777
+ for (let i = 1; i < pts.length; i++) sink.lineTo(pts[i]);
778
+ }
779
+ sink.stroke(env.style);
780
+ };
781
+
829
782
  // ../core/src/paint/painters/hatch.ts
830
783
  var SINGLE_FAMILY = {
831
784
  name: "",
@@ -833,7 +786,7 @@ var SINGLE_FAMILY = {
833
786
  };
834
787
  var paintSolid = (obj, sink, env) => {
835
788
  sink.beginPath();
836
- for (const loop of obj.loops) emitVertexPath(sink, loop.points, true);
789
+ for (const loop of obj.loops) emitLoopPath(sink, loop);
837
790
  sink.fill({ color: env.style.color, opacity: env.style.opacity });
838
791
  };
839
792
  var paintGradient = (obj, fill, sink, env) => {
@@ -842,7 +795,7 @@ var paintGradient = (obj, fill, sink, env) => {
842
795
  let maxX = -Infinity;
843
796
  let maxY = -Infinity;
844
797
  for (const loop of obj.loops)
845
- for (const p of loop.points) {
798
+ for (const p of loopPolygon(loop)) {
846
799
  if (p.x < minX) minX = p.x;
847
800
  if (p.y < minY) minY = p.y;
848
801
  if (p.x > maxX) maxX = p.x;
@@ -865,7 +818,7 @@ var paintGradient = (obj, fill, sink, env) => {
865
818
  to = { x: c.x + dir.x * r, y: c.y + dir.y * r };
866
819
  }
867
820
  sink.beginPath();
868
- for (const loop of obj.loops) emitVertexPath(sink, loop.points, true);
821
+ for (const loop of obj.loops) emitLoopPath(sink, loop);
869
822
  sink.fill({
870
823
  color: fill.color1,
871
824
  opacity: env.style.opacity,
@@ -883,7 +836,7 @@ var paintHatch = (obj, sink, env) => {
883
836
  }
884
837
  let lines = env.cache?.hatchLines.get(obj);
885
838
  if (!lines) {
886
- const rings = obj.loops.map((loop) => verticesToPolygon(loop.points));
839
+ const rings = obj.loops.map((loop) => loopPolygon(loop));
887
840
  const s = obj.fill.kind === "pattern" ? obj.fill.scale * obj.scale : obj.scale;
888
841
  const pat = obj.fill.kind === "pattern" ? resolveHatchPattern(obj.fill.name, env.hatchPatterns) : void 0;
889
842
  const estimate = pat ? estimatePatternSegments(rings, pat, s) : estimatePatternSegments(
@@ -925,50 +878,6 @@ var paintHatch = (obj, sink, env) => {
925
878
  });
926
879
  };
927
880
 
928
- // ../core/src/paint/painters/arrows.ts
929
- var paintArrowhead = (sink, tip, toward, size, color, type = "filled", opacity) => {
930
- if (type === "none") return;
931
- const dir = norm(sub(toward, tip));
932
- if (dir.x === 0 && dir.y === 0) return;
933
- switch (type) {
934
- case "filled": {
935
- const base = add(tip, scale(dir, size));
936
- const half = scale(perp(dir), size / 3);
937
- sink.beginPath();
938
- sink.moveTo(tip);
939
- sink.lineTo(add(base, half));
940
- sink.lineTo(sub(base, half));
941
- sink.closePath();
942
- sink.fill({ color, opacity });
943
- break;
944
- }
945
- case "open": {
946
- const base = add(tip, scale(dir, size));
947
- const half = scale(perp(dir), size / 3);
948
- sink.beginPath();
949
- sink.moveTo(add(base, half));
950
- sink.lineTo(tip);
951
- sink.lineTo(sub(base, half));
952
- sink.stroke({ color, widthMm: Math.min(size / 10, 0.25), opacity });
953
- break;
954
- }
955
- case "tick": {
956
- const diag = scale(norm(add(dir, perp(dir))), size / 2);
957
- sink.beginPath();
958
- sink.moveTo(sub(tip, diag));
959
- sink.lineTo(add(tip, diag));
960
- sink.stroke({ color, widthMm: Math.min(size / 8, 0.35), opacity });
961
- break;
962
- }
963
- case "dot": {
964
- sink.beginPath();
965
- sink.arc(tip, size / 4, 0, Math.PI * 2, true);
966
- sink.fill({ color, opacity });
967
- break;
968
- }
969
- }
970
- };
971
-
972
881
  // ../core/src/paint/painters/text.ts
973
882
  var paintText = (obj, sink, env) => {
974
883
  const { font, widthFactor } = effectiveTextFont(obj, env.textStyles);
@@ -984,18 +893,20 @@ var paintText = (obj, sink, env) => {
984
893
  )
985
894
  };
986
895
  const { color, widthMm, opacity } = env.style;
896
+ const arrowSize = textArrowSize(obj, env.textStyles);
897
+ const arrowType = textArrowType(obj, env.textStyles);
987
898
  for (const line of obj.leaderLines) {
988
899
  sink.beginPath();
989
- sink.moveTo(line[0]);
900
+ sink.moveTo(arrowLineStart(line[0], line[1], arrowSize, arrowType));
990
901
  for (let i = 1; i < line.length; i++) sink.lineTo(line[i]);
991
902
  sink.stroke({ color, widthMm, opacity });
992
903
  paintArrowhead(
993
904
  sink,
994
905
  line[0],
995
906
  line[1],
996
- textArrowSize(obj, env.textStyles),
907
+ arrowSize,
997
908
  color,
998
- textArrowType(obj, env.textStyles),
909
+ arrowType,
999
910
  opacity
1000
911
  );
1001
912
  }
@@ -1123,15 +1034,21 @@ var paintDimension = (obj, sink, env) => {
1123
1034
  dashMm: env.style.dashMm,
1124
1035
  opacity: env.style.opacity
1125
1036
  };
1037
+ const trim = (p, other) => {
1038
+ const arrow = geom.arrows.find(
1039
+ (ar) => dist(ar.tip, p) < 1e-9 && dot(sub(ar.toward, ar.tip), sub(other, p)) > 0
1040
+ );
1041
+ return arrow ? arrowLineStart(p, other, arrow.size, geom.arrowType) : p;
1042
+ };
1126
1043
  sink.beginPath();
1127
- for (const seg of [
1128
- ...geom.extensionLines,
1129
- ...geom.dimLines,
1130
- ...geom.leaders
1131
- ]) {
1044
+ for (const seg of geom.extensionLines) {
1132
1045
  sink.moveTo(seg.a);
1133
1046
  sink.lineTo(seg.b);
1134
1047
  }
1048
+ for (const seg of [...geom.dimLines, ...geom.leaders]) {
1049
+ sink.moveTo(trim(seg.a, seg.b));
1050
+ sink.lineTo(trim(seg.b, seg.a));
1051
+ }
1135
1052
  if (geom.dimArc) {
1136
1053
  const arc = geom.dimArc;
1137
1054
  sink.moveTo(add(arc.center, scale(fromAngle(arc.startAngle), arc.radius)));
@@ -1246,7 +1163,7 @@ var paintChildren = (children, sink, env, parentLayerId) => {
1246
1163
  // Same width-proportional dash rule as resolveStyle.
1247
1164
  dashMm: scaleDash(
1248
1165
  dashForLineType(child.lineType, env.lineTypes),
1249
- (child.lineWidth ?? env.style.widthMm) * (child.lineTypeScale ?? 1)
1166
+ (child.lineWidth ?? env.style.widthMm) * (child.lineTypeScale ?? 1) * (env.lineTypeScale ?? 1)
1250
1167
  ) ?? env.style.dashMm,
1251
1168
  opacity: child.opacity ?? env.style.opacity
1252
1169
  },
@@ -1256,6 +1173,7 @@ var paintChildren = (children, sink, env, parentLayerId) => {
1256
1173
  lineTypes: env.lineTypes,
1257
1174
  hatchPatterns: env.hatchPatterns,
1258
1175
  blockDefinitions: env.blockDefinitions,
1176
+ lineTypeScale: env.lineTypeScale,
1259
1177
  resolveObject: env.resolveObject,
1260
1178
  assets: env.assets,
1261
1179
  // A reference child (linked / embedded file) loads like any other.
@@ -1464,6 +1382,17 @@ var buildEllipse = (obj) => {
1464
1382
  ]
1465
1383
  };
1466
1384
  };
1385
+ var buildSpline = (obj) => {
1386
+ const pts = sampleNurbs(obj, curveTolerance(obj) / 10);
1387
+ const segments = [];
1388
+ for (let i = 1; i < pts.length; i++)
1389
+ segments.push({ a: pts[i - 1], b: pts[i], facet: true });
1390
+ const points = obj.closed || pts.length < 2 ? [] : [
1391
+ { p: pts[0], type: "endpoint" },
1392
+ { p: pts[pts.length - 1], type: "endpoint" }
1393
+ ];
1394
+ return { bounds: boundsOfPoints(pts), points, segments, arcs: [] };
1395
+ };
1467
1396
  var buildHatch = (obj) => {
1468
1397
  let bounds = emptyBounds();
1469
1398
  const points = [];
@@ -1471,12 +1400,11 @@ var buildHatch = (obj) => {
1471
1400
  const arcs = [];
1472
1401
  const rings = [];
1473
1402
  for (const loop of obj.loops) {
1474
- const curves = verticesToCurves(loop.points, true);
1403
+ const curves = loopCurves(loop);
1475
1404
  segments.push(...curves.segments);
1476
1405
  arcs.push(...curves.arcs);
1477
- for (const v of loop.points)
1478
- points.push({ p: { x: v.x, y: v.y }, type: "endpoint" });
1479
- rings.push(verticesToPolygon(loop.points));
1406
+ for (const p of loopCorners(loop)) points.push({ p, type: "endpoint" });
1407
+ rings.push(loopPolygon(loop));
1480
1408
  bounds = unionBounds(bounds, curveBounds(curves.segments, curves.arcs));
1481
1409
  }
1482
1410
  return { bounds, points, segments, arcs, rings };
@@ -1662,6 +1590,8 @@ var buildEntry = (obj, ctx) => {
1662
1590
  }
1663
1591
  case "ellipse":
1664
1592
  return buildEllipse(obj);
1593
+ case "spline":
1594
+ return buildSpline(obj);
1665
1595
  case "hatch":
1666
1596
  return buildHatch(obj);
1667
1597
  case "text":
@@ -1825,7 +1755,8 @@ var referenceEntry = (obj, ctx) => {
1825
1755
  segments.push({
1826
1756
  a: applyMat(m, kept[0]),
1827
1757
  b: applyMat(m, kept[1]),
1828
- chord: seg.chord
1758
+ chord: seg.chord,
1759
+ facet: seg.facet
1829
1760
  });
1830
1761
  }
1831
1762
  for (const a of e.arcs) {
@@ -2061,174 +1992,6 @@ var mergeStyles = (existing, incoming, same, opts) => {
2061
1992
  return { styles, idByName, created };
2062
1993
  };
2063
1994
 
2064
- // ../core/src/geometry/nurbs.ts
2065
- var nurbsDomain = (c) => {
2066
- const n2 = c.controlPoints.length;
2067
- return [c.knots[c.degree], c.knots[n2]];
2068
- };
2069
- var findSpan = (c, u) => {
2070
- const n2 = c.controlPoints.length;
2071
- const p = c.degree;
2072
- if (u >= c.knots[n2]) return n2 - 1;
2073
- if (u <= c.knots[p]) return p;
2074
- let lo = p;
2075
- let hi = n2;
2076
- let mid2 = lo + hi >> 1;
2077
- while (u < c.knots[mid2] || u >= c.knots[mid2 + 1]) {
2078
- if (u < c.knots[mid2]) hi = mid2;
2079
- else lo = mid2;
2080
- mid2 = lo + hi >> 1;
2081
- }
2082
- return mid2;
2083
- };
2084
- var evaluateNurbs = (c, u) => {
2085
- const p = c.degree;
2086
- const k = findSpan(c, u);
2087
- const d = [];
2088
- for (let j = 0; j <= p; j++) {
2089
- const i = k - p + j;
2090
- const cp = c.controlPoints[i];
2091
- const w2 = c.weights?.[i] ?? 1;
2092
- d.push([cp.x * w2, cp.y * w2, w2]);
2093
- }
2094
- for (let r = 1; r <= p; r++)
2095
- for (let j = p; j >= r; j--) {
2096
- const i = k - p + j;
2097
- const den = c.knots[i + p - r + 1] - c.knots[i];
2098
- const a = den === 0 ? 0 : (u - c.knots[i]) / den;
2099
- d[j] = [
2100
- (1 - a) * d[j - 1][0] + a * d[j][0],
2101
- (1 - a) * d[j - 1][1] + a * d[j][1],
2102
- (1 - a) * d[j - 1][2] + a * d[j][2]
2103
- ];
2104
- }
2105
- const [x, y, w] = d[p];
2106
- return w === 0 ? { x, y } : { x: x / w, y: y / w };
2107
- };
2108
- var isValidNurbs = (c) => c.degree >= 1 && c.controlPoints.length > c.degree && c.knots.length === c.controlPoints.length + c.degree + 1 && (c.weights === void 0 || c.weights.length === c.controlPoints.length) && c.knots.every((v, i) => i === 0 || v >= c.knots[i - 1]);
2109
- var sampleNurbs = (c, tolerance, maxDepth = 10) => {
2110
- const [u0, u1] = nurbsDomain(c);
2111
- const out = [evaluateNurbs(c, u0)];
2112
- const push = (q) => {
2113
- const last = out[out.length - 1];
2114
- if (Math.hypot(q.x - last.x, q.y - last.y) > 1e-9) out.push(q);
2115
- };
2116
- const refine = (a, pa, b, pb, depth) => {
2117
- const m = (a + b) / 2;
2118
- const pm = evaluateNurbs(c, m);
2119
- const chord = { x: (pa.x + pb.x) / 2, y: (pa.y + pb.y) / 2 };
2120
- const dev = Math.hypot(pm.x - chord.x, pm.y - chord.y);
2121
- if (depth >= maxDepth || dev <= tolerance) {
2122
- if (dev > tolerance / 4) push(pm);
2123
- push(pb);
2124
- return;
2125
- }
2126
- refine(a, pa, m, pm, depth + 1);
2127
- refine(m, pm, b, pb, depth + 1);
2128
- };
2129
- const spans = [];
2130
- for (let i = c.degree; i < c.controlPoints.length; i++) {
2131
- const a = Math.max(c.knots[i], u0);
2132
- const b = Math.min(c.knots[i + 1], u1);
2133
- if (b > a) spans.push(a, b);
2134
- }
2135
- for (let i = 0; i < spans.length; i += 2) {
2136
- const a = spans[i];
2137
- const b = spans[i + 1];
2138
- const pa = evaluateNurbs(c, a);
2139
- const pb = evaluateNurbs(c, b);
2140
- const m = (a + b) / 2;
2141
- const pm = evaluateNurbs(c, m);
2142
- refine(a, pa, m, pm, 1);
2143
- refine(m, pm, b, pb, 1);
2144
- }
2145
- return dropCollinear(out, tolerance / 10);
2146
- };
2147
- var dropCollinear = (pts, eps) => {
2148
- if (pts.length < 3) return pts;
2149
- const out = [pts[0]];
2150
- for (let i = 1; i < pts.length - 1; i++) {
2151
- const a = out[out.length - 1];
2152
- const b = pts[i + 1];
2153
- const q = pts[i];
2154
- const abx = b.x - a.x;
2155
- const aby = b.y - a.y;
2156
- const len2 = abx * abx + aby * aby;
2157
- const t = len2 === 0 ? 0 : Math.max(
2158
- 0,
2159
- Math.min(1, ((q.x - a.x) * abx + (q.y - a.y) * aby) / len2)
2160
- );
2161
- const dist2 = Math.hypot(q.x - (a.x + t * abx), q.y - (a.y + t * aby));
2162
- if (dist2 > eps) out.push(q);
2163
- }
2164
- out.push(pts[pts.length - 1]);
2165
- return out;
2166
- };
2167
- var interpolateFitPoints = (points, degree = 3) => {
2168
- const n2 = points.length;
2169
- if (n2 < 2) return null;
2170
- const p = Math.min(degree, n2 - 1);
2171
- const u = [0];
2172
- let total2 = 0;
2173
- for (let i = 1; i < n2; i++) {
2174
- total2 += Math.hypot(
2175
- points[i].x - points[i - 1].x,
2176
- points[i].y - points[i - 1].y
2177
- );
2178
- u.push(total2);
2179
- }
2180
- if (total2 === 0) return null;
2181
- for (let i = 0; i < n2; i++) u[i] /= total2;
2182
- const knots = new Array(p + 1).fill(0);
2183
- for (let j = 1; j <= n2 - p - 1; j++) {
2184
- let s = 0;
2185
- for (let i = j; i <= j + p - 1; i++) s += u[i];
2186
- knots.push(s / p);
2187
- }
2188
- knots.push(...new Array(p + 1).fill(1));
2189
- const basisRow = (ui) => {
2190
- const row = new Array(n2).fill(0);
2191
- const probe = {
2192
- degree: p,
2193
- controlPoints: points.map(() => ({ x: 0, y: 0 })),
2194
- knots
2195
- };
2196
- for (let j = 0; j < n2; j++) {
2197
- probe.controlPoints = points.map((_, k) => ({
2198
- x: k === j ? 1 : 0,
2199
- y: 0
2200
- }));
2201
- row[j] = evaluateNurbs(probe, ui).x;
2202
- }
2203
- return row;
2204
- };
2205
- const A = u.map((ui) => basisRow(ui));
2206
- const bx = points.map((q) => q.x);
2207
- const by = points.map((q) => q.y);
2208
- for (let col = 0; col < n2; col++) {
2209
- let pivot = col;
2210
- for (let r = col + 1; r < n2; r++)
2211
- if (Math.abs(A[r][col]) > Math.abs(A[pivot][col])) pivot = r;
2212
- if (Math.abs(A[pivot][col]) < 1e-12) return null;
2213
- [A[col], A[pivot]] = [A[pivot], A[col]];
2214
- [bx[col], bx[pivot]] = [bx[pivot], bx[col]];
2215
- [by[col], by[pivot]] = [by[pivot], by[col]];
2216
- for (let r = 0; r < n2; r++) {
2217
- if (r === col) continue;
2218
- const k = A[r][col] / A[col][col];
2219
- if (k === 0) continue;
2220
- for (let c = col; c < n2; c++) A[r][c] -= k * A[col][c];
2221
- bx[r] -= k * bx[col];
2222
- by[r] -= k * by[col];
2223
- }
2224
- }
2225
- const controlPoints = points.map((_, i) => ({
2226
- x: bx[i] / A[i][i],
2227
- y: by[i] / A[i][i]
2228
- }));
2229
- return { degree: p, controlPoints, knots };
2230
- };
2231
-
2232
1995
  // ../core/src/geometry/band.ts
2233
1996
  var left = (d) => ({ x: -d.y, y: d.x });
2234
1997
  var unit = (a, b) => {
@@ -3202,7 +2965,6 @@ var unitFactor = (insunits) => {
3202
2965
  }
3203
2966
  };
3204
2967
  var IMPORT_LINE_WIDTH_MM = 0.25;
3205
- var SPLINE_TOLERANCE_MM = 0.02;
3206
2968
  var TEXT_TYPES = /* @__PURE__ */ new Set(["TEXT", "MTEXT", "ATTRIB", "ATTDEF"]);
3207
2969
  var DIM_TYPES = /* @__PURE__ */ new Set(["DIMENSION", "LEADER"]);
3208
2970
  var scanTags = (lines) => {
@@ -3412,48 +3174,55 @@ var baseFor = (e, ctx) => {
3412
3174
  hidden: false
3413
3175
  };
3414
3176
  };
3415
- var hatchLoopPoints = (path, f, mirrored) => {
3416
- const points = hatchLoopPointsOcs(path, f);
3417
- return mirrored ? points.map((p) => ({ x: -p.x, y: p.y, bulge: -p.bulge })) : points;
3418
- };
3419
- var HATCH_ELLIPSE_TOLERANCE_MM = 0.1;
3420
- var hatchLoopPointsOcs = (path, f) => {
3421
- if (path.isPolyline) {
3422
- return (path.vertices ?? []).map((v) => ({
3423
- x: v.x * f,
3424
- y: v.y * f,
3425
- bulge: v.bulge ?? 0
3426
- }));
3427
- }
3428
- const points = [];
3429
- const push = (x, y, bulge = 0) => points.push({ x: x * f, y: y * f, bulge });
3177
+ var hatchLoopEdges = (path, f, mirrored) => {
3178
+ const edges = hatchLoopEdgesOcs(path, f);
3179
+ if (!mirrored || edges.length === 0) return edges;
3180
+ const reflected = mirrorObject(
3181
+ {
3182
+ type: "hatch",
3183
+ id: "",
3184
+ layerId: "",
3185
+ loops: [{ edges }],
3186
+ fill: { kind: "solid" },
3187
+ scale: 1
3188
+ },
3189
+ { x: 0, y: 0 },
3190
+ { x: 0, y: 1 }
3191
+ );
3192
+ return reflected.loops[0].edges;
3193
+ };
3194
+ var hatchLoopEdgesOcs = (path, f) => {
3195
+ const pt2 = (p) => ({ x: p.x * f, y: p.y * f });
3196
+ if (path.isPolyline)
3197
+ return verticesToLoop(
3198
+ (path.vertices ?? []).map((v) => ({
3199
+ ...pt2(v),
3200
+ bulge: v.bulge ?? 0
3201
+ }))
3202
+ ).edges;
3203
+ const edges = [];
3430
3204
  for (const edge of path.edges ?? []) {
3431
3205
  switch (edge.kind) {
3432
3206
  case 1: {
3433
- const p = edge.points[0];
3434
- if (p) push(p.x, p.y);
3207
+ const a = edge.points[0];
3208
+ const b = edge.end;
3209
+ if (a && b) edges.push({ kind: "line", a: pt2(a), b: pt2(b) });
3435
3210
  break;
3436
3211
  }
3437
3212
  case 2: {
3438
3213
  const c = edge.points[0];
3439
3214
  if (!c || !(edge.r40 > 0)) break;
3440
- const ccw = edge.ccw !== false;
3441
3215
  const a0 = (edge.startAngle ?? 0) * DEG2RAD;
3442
- let a1 = (edge.endAngle ?? 360) * DEG2RAD;
3443
- if (a1 <= a0 + 1e-12) a1 += Math.PI * 2;
3444
- const sweep = Math.min(a1 - a0, Math.PI * 2);
3445
- const sign = ccw ? 1 : -1;
3446
- const start = sign * a0;
3447
- const pieces = sweep > Math.PI + 1e-9 ? 2 : 1;
3448
- const bulge = sign * Math.tan(sweep / pieces / 4);
3449
- for (let i = 0; i < pieces; i++) {
3450
- const ang = start + sign * sweep * i / pieces;
3451
- push(
3452
- c.x + edge.r40 * Math.cos(ang),
3453
- c.y + edge.r40 * Math.sin(ang),
3454
- bulge
3455
- );
3456
- }
3216
+ const a1 = (edge.endAngle ?? 360) * DEG2RAD;
3217
+ const ccw = edge.ccw !== false;
3218
+ const full = Math.abs(a1 - a0) >= Math.PI * 2 - 1e-9;
3219
+ edges.push({
3220
+ kind: "arc",
3221
+ center: pt2(c),
3222
+ radius: edge.r40 * f,
3223
+ startAngle: ccw ? a0 : -a1,
3224
+ endAngle: full ? ccw ? a0 : -a1 : ccw ? a1 : -a0
3225
+ });
3457
3226
  break;
3458
3227
  }
3459
3228
  case 3: {
@@ -3463,29 +3232,21 @@ var hatchLoopPointsOcs = (path, f) => {
3463
3232
  const ratio = edge.r40 > 0 ? edge.r40 : 1;
3464
3233
  const ccw = edge.ccw !== false;
3465
3234
  const a0 = (edge.startAngle ?? 0) * DEG2RAD;
3466
- let a1 = (edge.endAngle ?? 360) * DEG2RAD;
3467
- if (a1 <= a0 + 1e-12) a1 += Math.PI * 2;
3468
- const sweep = Math.min(a1 - a0, Math.PI * 2);
3469
- const sign = ccw ? 1 : -1;
3470
- const L = Math.hypot(major.x, major.y);
3471
- const tol = HATCH_ELLIPSE_TOLERANCE_MM / f;
3472
- const maxStep = L <= tol ? sweep : 2 * Math.acos(Math.max(-1, 1 - tol / L));
3473
- const n2 = Math.max(8, Math.ceil(sweep / Math.max(maxStep, 1e-3)));
3474
- for (let i = 0; i < n2; i++) {
3475
- const theta = sign * (a0 + sweep * i / n2);
3476
- const t = Math.atan2(Math.sin(theta) / ratio, Math.cos(theta));
3477
- push(
3478
- c.x + major.x * Math.cos(t) - major.y * ratio * Math.sin(t),
3479
- c.y + major.y * Math.cos(t) + major.x * ratio * Math.sin(t)
3480
- );
3481
- }
3235
+ const a1 = (edge.endAngle ?? 360) * DEG2RAD;
3236
+ const full = Math.abs(a1 - a0) >= Math.PI * 2 - 1e-9;
3237
+ const param = (theta) => Math.atan2(Math.sin(theta) / ratio, Math.cos(theta));
3238
+ edges.push({
3239
+ kind: "ellipse",
3240
+ center: pt2(c),
3241
+ majorAxis: pt2(major),
3242
+ ratio: Math.min(1, ratio),
3243
+ startAngle: full ? 0 : param(ccw ? a0 : -a1),
3244
+ endAngle: full ? Math.PI * 2 : param(ccw ? a1 : -a0)
3245
+ });
3482
3246
  break;
3483
3247
  }
3484
3248
  case 4: {
3485
- const control = (edge.points ?? []).map((p) => ({
3486
- x: p.x,
3487
- y: p.y
3488
- }));
3249
+ const control = (edge.points ?? []).map(pt2);
3489
3250
  const weights = edge.weights ?? [];
3490
3251
  const curve = {
3491
3252
  degree: edge.degree ?? 3,
@@ -3493,36 +3254,24 @@ var hatchLoopPointsOcs = (path, f) => {
3493
3254
  knots: edge.knots ?? [],
3494
3255
  ...edge.rational && weights.length === control.length ? { weights } : {}
3495
3256
  };
3496
- const fit = (edge.fitPoints ?? []).map((p) => ({
3497
- x: p.x,
3498
- y: p.y
3499
- }));
3500
- const fitted = isValidNurbs(curve) ? null : interpolateFitPoints(fit);
3501
- const sampled = isValidNurbs(curve) ? sampleNurbs(curve, SPLINE_TOLERANCE_MM / f) : fitted ? sampleNurbs(fitted, SPLINE_TOLERANCE_MM / f) : fit.length >= 2 ? fit : control;
3502
- for (const p of sampled.slice(0, -1)) push(p.x, p.y);
3257
+ const fit = (edge.fitPoints ?? []).map(pt2);
3258
+ const exact = isValidNurbs(curve) ? curve : interpolateFitPoints(fit);
3259
+ if (exact) {
3260
+ edges.push({
3261
+ kind: "spline",
3262
+ ...exact,
3263
+ ...fit.length >= 2 ? { fitPoints: fit } : {}
3264
+ });
3265
+ break;
3266
+ }
3267
+ const poly = fit.length >= 2 ? fit : control;
3268
+ for (let i = 1; i < poly.length; i++)
3269
+ edges.push({ kind: "line", a: poly[i - 1], b: poly[i] });
3503
3270
  break;
3504
3271
  }
3505
3272
  }
3506
3273
  }
3507
- return points.filter(
3508
- (p, i) => i === 0 || Math.abs(p.x - points[i - 1].x) > 1e-9 || Math.abs(p.y - points[i - 1].y) > 1e-9 || points[i - 1].bulge !== 0
3509
- );
3510
- };
3511
- var atLeastThreeVertices = (points) => {
3512
- if (points.length !== 2 || points.every((p) => p.bulge === 0)) return points;
3513
- const out = [];
3514
- for (let i = 0; i < 2; i++) {
3515
- const a = points[i];
3516
- const b = points[1 - i];
3517
- out.push(a);
3518
- const arc = a.bulge === 0 ? null : bulgeToArc(a, b, a.bulge);
3519
- if (!arc) continue;
3520
- const half = Math.tan(Math.atan(a.bulge) / 2);
3521
- const mid2 = arcMidPoint(arc);
3522
- out[out.length - 1] = { ...a, bulge: half };
3523
- out.push({ x: mid2.x, y: mid2.y, bulge: half });
3524
- }
3525
- return out;
3274
+ return edges;
3526
3275
  };
3527
3276
  var convertHatch = (e, ctx) => {
3528
3277
  const anyE = e;
@@ -3530,8 +3279,8 @@ var convertHatch = (e, ctx) => {
3530
3279
  const mirrored = ctx.tags.mirroredHandles.has(String(anyE.handle));
3531
3280
  const flip = mirrored ? -1 : 1;
3532
3281
  const loops = (anyE.boundaryPaths ?? []).map((path) => ({
3533
- points: atLeastThreeVertices(hatchLoopPoints(path, ctx.f, mirrored))
3534
- })).filter((loop) => loop.points.length >= 3);
3282
+ edges: hatchLoopEdges(path, ctx.f, mirrored)
3283
+ })).filter((loop) => loop.edges.length > 0);
3535
3284
  if (loops.length === 0) return [];
3536
3285
  let fill;
3537
3286
  const patternMatch = typeof anyE.patternName === "string" ? getHatchPattern(anyE.patternName) : void 0;
@@ -3995,9 +3744,9 @@ var convertEntityInner = (e, dxf, ctx) => {
3995
3744
  return [
3996
3745
  {
3997
3746
  type: "hatch",
3998
- loops: loops.map((l) => ({
3999
- points: l.map((p) => ({ ...p, bulge: 0 }))
4000
- })),
3747
+ loops: loops.map(
3748
+ (l) => verticesToLoop(l.map((p) => ({ ...p, bulge: 0 })))
3749
+ ),
4001
3750
  fill: { kind: "solid" },
4002
3751
  scale: 1,
4003
3752
  ...base
@@ -4127,32 +3876,46 @@ var convertEntityInner = (e, dxf, ctx) => {
4127
3876
  case "SPLINE": {
4128
3877
  const control = anyE.controlPoints ?? [];
4129
3878
  const weights = ctx.tags.splineWeightsByHandle.get(String(anyE.handle));
3879
+ const fit = (anyE.fitPoints ?? []).map((p) => ({
3880
+ x: p.x * f,
3881
+ y: p.y * f
3882
+ }));
4130
3883
  const curve = {
4131
3884
  degree: anyE.degreeOfSplineCurve ?? 3,
4132
- controlPoints: control.map((p) => ({ x: p.x, y: p.y })),
3885
+ controlPoints: control.map((p) => ({ x: p.x * f, y: p.y * f })),
4133
3886
  knots: anyE.knotValues ?? [],
4134
3887
  ...weights && weights.length === control.length ? { weights } : {}
4135
3888
  };
4136
- const fit = anyE.fitPoints ?? [];
4137
- const fitted = isValidNurbs(curve) ? null : interpolateFitPoints(fit.map((p) => ({ x: p.x, y: p.y })));
4138
- let points;
4139
- if (isValidNurbs(curve)) {
4140
- points = sampleNurbs(curve, SPLINE_TOLERANCE_MM / f);
4141
- bump(ctx.degraded, "SPLINE\u2192polyline");
4142
- } else if (fitted) {
4143
- points = sampleNurbs(fitted, SPLINE_TOLERANCE_MM / f);
4144
- bump(ctx.degraded, "SPLINE\u2192polyline (fit points)");
4145
- } else {
4146
- const raw = fit.length >= 2 ? fit : control;
4147
- if (raw.length < 2) return [];
4148
- points = raw.map((p) => ({ x: p.x, y: p.y }));
4149
- bump(ctx.degraded, "SPLINE\u2192polyline (control points)");
4150
- }
4151
- if (points.length < 2) return [];
3889
+ const tangent = (t) => t && Math.hypot(t.x, t.y) > 1e-12 ? { x: t.x, y: t.y } : void 0;
3890
+ const startTangent = tangent(anyE.startTangent);
3891
+ const endTangent = tangent(anyE.endTangent);
3892
+ const closed = anyE.closed === true;
3893
+ const fitted = isValidNurbs(curve) ? curve : closed ? interpolateClosed(fit) : interpolateFitPoints(fit, 3, {
3894
+ start: startTangent,
3895
+ end: endTangent
3896
+ });
3897
+ if (fitted && isValidNurbs(fitted))
3898
+ return [
3899
+ {
3900
+ type: "spline",
3901
+ degree: fitted.degree,
3902
+ controlPoints: fitted.controlPoints,
3903
+ knots: fitted.knots,
3904
+ ...fitted.weights ? { weights: fitted.weights } : {},
3905
+ ...fit.length >= 2 ? { fitPoints: fit } : {},
3906
+ ...fit.length >= 2 && !closed && startTangent ? { startTangent } : {},
3907
+ ...fit.length >= 2 && !closed && endTangent ? { endTangent } : {},
3908
+ closed,
3909
+ ...base
3910
+ }
3911
+ ];
3912
+ const raw = fit.length >= 2 ? fit : control.map((p) => ({ x: p.x * f, y: p.y * f }));
3913
+ if (raw.length < 2) return [];
3914
+ bump(ctx.degraded, "SPLINE\u2192polyline (control points)");
4152
3915
  return [
4153
3916
  {
4154
3917
  type: "polyline",
4155
- points: points.map((p) => ({ x: p.x * f, y: p.y * f, bulge: 0 })),
3918
+ points: raw.map((p) => ({ x: p.x, y: p.y, bulge: 0 })),
4156
3919
  closed: anyE.closed === true,
4157
3920
  ...base
4158
3921
  }
@@ -4167,7 +3930,9 @@ var convertEntityInner = (e, dxf, ctx) => {
4167
3930
  return [
4168
3931
  {
4169
3932
  type: "hatch",
4170
- loops: [{ points: points.map((p) => ({ ...pt(p, f), bulge: 0 })) }],
3933
+ loops: [
3934
+ verticesToLoop(points.map((p) => ({ ...pt(p, f), bulge: 0 })))
3935
+ ],
4171
3936
  fill: { kind: "solid" },
4172
3937
  scale: 1,
4173
3938
  ...base
@@ -4183,9 +3948,7 @@ var convertEntityInner = (e, dxf, ctx) => {
4183
3948
  {
4184
3949
  type: "hatch",
4185
3950
  loops: [
4186
- {
4187
- points: ordered.map((p) => ({ ...ocs(p), bulge: 0 }))
4188
- }
3951
+ verticesToLoop(ordered.map((p) => ({ ...ocs(p), bulge: 0 })))
4189
3952
  ],
4190
3953
  fill: { kind: "solid" },
4191
3954
  scale: 1,
@@ -4397,7 +4160,7 @@ var convertInsertInner = (e, dxf, ctx, depth, inheritedColor) => {
4397
4160
  const sx = anyE.xScale ?? 1;
4398
4161
  const sy = (anyE.yScale ?? 1) * (mirrored ? -1 : 1);
4399
4162
  const rot0 = (anyE.rotation ?? 0) * DEG2RAD;
4400
- const rot2 = mirrored ? Math.PI - rot0 : rot0;
4163
+ const rot = mirrored ? Math.PI - rot0 : rot0;
4401
4164
  const pos = anyE.position ?? { x: 0, y: 0 };
4402
4165
  const insert = mirrored ? { x: -pos.x * ctx.f, y: pos.y * ctx.f } : pt(pos, ctx.f);
4403
4166
  if (e.type === "INSERT" && depth === 0 && !mirrored && sx > 0 && sy > 0 && Math.abs(sx - sy) < 1e-9) {
@@ -4408,7 +4171,7 @@ var convertInsertInner = (e, dxf, ctx, depth, inheritedColor) => {
4408
4171
  type: "block",
4409
4172
  definitionId: def.id,
4410
4173
  inputs: [insert],
4411
- rotation: rot2,
4174
+ rotation: rot,
4412
4175
  scale: sx,
4413
4176
  params: {},
4414
4177
  ...baseFor(e, ctx)
@@ -4417,8 +4180,8 @@ var convertInsertInner = (e, dxf, ctx, depth, inheritedColor) => {
4417
4180
  }
4418
4181
  if (e.type === "INSERT" && depth === 0) bump(ctx.degraded, "INSERT\u2192exploded");
4419
4182
  const base = pt(block.position ?? { x: 0, y: 0 }, ctx.f);
4420
- const cos = Math.cos(rot2);
4421
- const sin = Math.sin(rot2);
4183
+ const cos = Math.cos(rot);
4184
+ const sin = Math.sin(rot);
4422
4185
  const reflect = sx * sy < 0;
4423
4186
  const meanScale = Math.sqrt(Math.abs(sx * sy)) || 1e-6;
4424
4187
  const lin = (v) => ({
@@ -4471,7 +4234,7 @@ var convertInsertInner = (e, dxf, ctx, depth, inheritedColor) => {
4471
4234
  return t;
4472
4235
  }
4473
4236
  const majorLen = Math.max(ax, ay);
4474
- const ang = rot2 + (ax >= ay ? 0 : Math.PI / 2);
4237
+ const ang = rot + (ax >= ay ? 0 : Math.PI / 2);
4475
4238
  return {
4476
4239
  type: "ellipse",
4477
4240
  center,
@@ -4519,7 +4282,18 @@ var convertInsertInner = (e, dxf, ctx, depth, inheritedColor) => {
4519
4282
  return t;
4520
4283
  case "hatch": {
4521
4284
  t.loops = t.loops.map((loop) => ({
4522
- points: mapBulged(loop.points, true)
4285
+ edges: loop.edges.flatMap((edge) => {
4286
+ const { kind, ...geometry } = edge;
4287
+ const moved = transform({
4288
+ ...geometry,
4289
+ type: kind,
4290
+ layerId: t.layerId
4291
+ });
4292
+ if (!moved || moved.type !== kind) return [];
4293
+ const out = { kind };
4294
+ for (const key of Object.keys(geometry)) out[key] = moved[key];
4295
+ return [out];
4296
+ })
4523
4297
  }));
4524
4298
  if (t.fill.kind === "pattern" || t.fill.kind === "lines") {
4525
4299
  const d = lin({
@@ -4536,9 +4310,15 @@ var convertInsertInner = (e, dxf, ctx, depth, inheritedColor) => {
4536
4310
  }
4537
4311
  return t;
4538
4312
  }
4313
+ case "spline":
4314
+ t.controlPoints = t.controlPoints.map(map);
4315
+ if (t.fitPoints) t.fitPoints = t.fitPoints.map(map);
4316
+ if (t.startTangent) t.startTangent = lin(t.startTangent);
4317
+ if (t.endTangent) t.endTangent = lin(t.endTangent);
4318
+ return t;
4539
4319
  case "text":
4540
4320
  t.position = map(t.position);
4541
- t.rotation += rot2;
4321
+ t.rotation += rot;
4542
4322
  if (meanScale !== 1) {
4543
4323
  const style = t.styleId && ctx.textStyleById.get(t.styleId) || ctx.defaultTextStyle;
4544
4324
  const o = t.styleOverride ?? {};
@@ -5148,7 +4928,7 @@ var FALLBACK_STYLE = {
5148
4928
  color: "#e8e8e8",
5149
4929
  widthMm: 0.25
5150
4930
  };
5151
- var resolveStyle = (obj, layers, lineTypes) => {
4931
+ var resolveStyle = (obj, layers, lineTypes, lineTypeScale = 1) => {
5152
4932
  const layer = layers.find((l) => l.id === obj.layerId);
5153
4933
  const widthMm = obj.lineWidth ?? layer?.lineWidth ?? FALLBACK_STYLE.widthMm;
5154
4934
  return {
@@ -5156,10 +4936,11 @@ var resolveStyle = (obj, layers, lineTypes) => {
5156
4936
  widthMm,
5157
4937
  // Dash patterns are defined per 1 mm of line width (ISO 128 style —
5158
4938
  // pattern legibility tracks stroke weight), so the resolved lineweight
5159
- // scales them; lineTypeScale (CELTSCALE) multiplies on top.
4939
+ // scales them; lineTypeScale (CELTSCALE) and the model scale multiply
4940
+ // on top.
5160
4941
  dashMm: scaleDash(
5161
4942
  dashForLineType(obj.lineType ?? layer?.lineType, lineTypes),
5162
- widthMm * (obj.lineTypeScale ?? 1)
4943
+ widthMm * (obj.lineTypeScale ?? 1) * lineTypeScale
5163
4944
  ),
5164
4945
  opacity: obj.opacity ?? layer?.opacity
5165
4946
  };
@@ -5176,7 +4957,7 @@ var rebaseSink = (sink, origin) => {
5176
4957
  lineTo: (p) => sink.lineTo(at(p)),
5177
4958
  bezierTo: (c1, c2, end) => sink.bezierTo(at(c1), at(c2), at(end)),
5178
4959
  arc: (center, r, a0, a1, ccw) => sink.arc(at(center), r, a0, a1, ccw),
5179
- ellipse: (center, rx, ry, rot2, a0, a1) => sink.ellipse(at(center), rx, ry, rot2, a0, a1),
4960
+ ellipse: (center, rx, ry, rot, a0, a1) => sink.ellipse(at(center), rx, ry, rot, a0, a1),
5180
4961
  closePath: () => sink.closePath(),
5181
4962
  stroke: (s) => sink.stroke(s),
5182
4963
  fill: (f) => sink.fill(
@@ -5264,7 +5045,8 @@ var paintReference = (obj, sink, env) => {
5264
5045
  const own = resolveStyle(
5265
5046
  child,
5266
5047
  content.layers,
5267
- content.lineTypes
5048
+ content.lineTypes,
5049
+ env.lineTypeScale
5268
5050
  );
5269
5051
  paintObject(child, out, {
5270
5052
  // A highlight paints the whole reference in one style.
@@ -5333,6 +5115,7 @@ var painters = {
5333
5115
  circle: paintCircle,
5334
5116
  arc: paintArc,
5335
5117
  ellipse: paintEllipse,
5118
+ spline: paintSpline,
5336
5119
  hatch: paintHatch,
5337
5120
  text: paintText,
5338
5121
  dimension: paintDimension,
@@ -5580,6 +5363,7 @@ var extractCommandMeta = (mod) => {
5580
5363
  };
5581
5364
  };
5582
5365
  var compileCache = /* @__PURE__ */ new Map();
5366
+ onSandboxReset(() => compileCache.clear());
5583
5367
  var COMPILE_CACHE_MAX = 100;
5584
5368
  var compileCommand = (script) => {
5585
5369
  const cached = compileCache.get(script);
@@ -5588,16 +5372,24 @@ var compileCommand = (script) => {
5588
5372
  compileCache.set(script, cached);
5589
5373
  return cached;
5590
5374
  }
5591
- const mod = executeScriptModule(script);
5592
- const meta = extractCommandMeta(mod);
5593
- const run = mod.run;
5594
- if (typeof run !== "function")
5595
- throw new Error("Command script must export a `run` function");
5375
+ const mod = executeScriptModule(script, "command");
5376
+ let meta;
5377
+ try {
5378
+ meta = extractCommandMeta(mod);
5379
+ if (typeof mod.run !== "function")
5380
+ throw new Error("Command script must export a `run` function");
5381
+ } catch (e) {
5382
+ releaseScriptModule(mod);
5383
+ throw e;
5384
+ }
5596
5385
  if (compileCache.size >= COMPILE_CACHE_MAX) {
5597
5386
  const oldest = compileCache.keys().next().value;
5598
- if (oldest !== void 0) compileCache.delete(oldest);
5387
+ if (oldest !== void 0) {
5388
+ releaseScriptModule(compileCache.get(oldest).mod);
5389
+ compileCache.delete(oldest);
5390
+ }
5599
5391
  }
5600
- const compiled = { meta, run };
5392
+ const compiled = { mod, meta, run: mod.run };
5601
5393
  compileCache.set(script, compiled);
5602
5394
  return compiled;
5603
5395
  };
@@ -6789,12 +6581,19 @@ var renderModelToSvg = (model, opts = {}) => {
6789
6581
  const origin = far ? { x: bbox.minX, y: bbox.minY } : { x: 0, y: 0 };
6790
6582
  const out = rebaseSink(sink, origin);
6791
6583
  for (const obj of toPaint) {
6792
- const style = resolveStyle(obj, model.layers, model.lineTypes);
6584
+ const lineTypeScale = opts.lineTypeScale ?? 1;
6585
+ const style = resolveStyle(
6586
+ obj,
6587
+ model.layers,
6588
+ model.lineTypes,
6589
+ lineTypeScale
6590
+ );
6793
6591
  sink.beginObject(obj.id, layerName.get(obj.layerId));
6794
6592
  paintObject(obj, out, {
6795
6593
  style: { ...style, color: mapColor(style.color) },
6796
6594
  ...model,
6797
- mapColor
6595
+ mapColor,
6596
+ lineTypeScale
6798
6597
  });
6799
6598
  sink.endObject();
6800
6599
  }
@@ -6805,7 +6604,12 @@ var renderModelToSvg = (model, opts = {}) => {
6805
6604
  maxX: bbox.maxX - origin.x,
6806
6605
  maxY: bbox.maxY - origin.y
6807
6606
  },
6808
- opts.padding
6607
+ // Default: 2% of the frame (at least 5 mm), so strokes and arrowheads
6608
+ // at the extents aren't cut by the image edge.
6609
+ opts.padding ?? Math.max(
6610
+ 5,
6611
+ 0.02 * Math.max(bbox.maxX - bbox.minX, bbox.maxY - bbox.minY)
6612
+ )
6809
6613
  );
6810
6614
  };
6811
6615
  var FAR_MM = 1e7;
@@ -8379,17 +8183,188 @@ var DocumentKernel = class {
8379
8183
 
8380
8184
  // ../core/src/drawing/localDrawing.ts
8381
8185
  import { z as z6 } from "zod";
8186
+
8187
+ // ../core/src/drawing/inputChecks.ts
8188
+ var DrawingError = class extends Error {
8189
+ code;
8190
+ issues;
8191
+ constructor(code, message, issues) {
8192
+ super(message);
8193
+ this.code = code;
8194
+ if (issues) this.issues = issues;
8195
+ }
8196
+ };
8197
+ var MAX_COORDINATE_MM = 1e9;
8198
+ var STYLE_OVERRIDE_KEYS = {
8199
+ text: TextStyleOverrideSchema.shape,
8200
+ table: TextStyleOverrideSchema.shape,
8201
+ dimension: DimStyleOverrideSchema.shape
8202
+ };
8203
+ var hintFor = (type, key) => {
8204
+ if (type && key in (STYLE_OVERRIDE_KEYS[type] ?? {}))
8205
+ return ` \u2014 style keys go in styleOverride: { ${key}: \u2026 }`;
8206
+ if (type === "layer" && key === "hidden") return " \u2014 use visible: false";
8207
+ if (type === "dimension" && (key === "upVector" || key === "linePoint"))
8208
+ return " \u2014 `offset` is the signed distance of the dimension line";
8209
+ if (type === "dimension" && key === "textOverride")
8210
+ return " \u2014 use labels[i].content (with {value} for the measurement)";
8211
+ return "";
8212
+ };
8213
+ var assertNoUnknownKeys = (sent, parsed, context, type) => {
8214
+ const unknown = Object.keys(sent).filter(
8215
+ (k) => sent[k] !== void 0 && !(k in parsed)
8216
+ );
8217
+ if (unknown.length > 0)
8218
+ throw new DrawingError(
8219
+ "unknownField",
8220
+ `${context}: unknown field(s) ${unknown.map((k) => `"${k}"${hintFor(type, k)}`).join(", ")}`
8221
+ );
8222
+ };
8223
+ var assertSaneNumbers = (value, context) => {
8224
+ const walk = (v, path) => {
8225
+ if (typeof v === "number") {
8226
+ if (Math.abs(v) > MAX_COORDINATE_MM)
8227
+ throw new DrawingError(
8228
+ "outOfRange",
8229
+ `${context}: ${path || "value"} = ${v} is out of range (|value| \u2264 ${MAX_COORDINATE_MM} mm)`
8230
+ );
8231
+ } else if (Array.isArray(v)) v.forEach((x, i) => walk(x, `${path}[${i}]`));
8232
+ else if (v !== null && typeof v === "object")
8233
+ for (const [k, x] of Object.entries(v))
8234
+ walk(x, path ? `${path}.${k}` : k);
8235
+ };
8236
+ walk(value, "");
8237
+ };
8238
+ var knownLineType = (name, sources) => resolveLineType(name, sources.lineTypes) !== void 0 || getLineType(name) !== void 0;
8239
+ var knownFont = (font) => (font.includes(";") ? font.slice(0, font.indexOf(";")) : font) in FONTS;
8240
+ var fontList = Object.keys(FONTS).join(", ");
8241
+ var assertLineType = (name, sources, context) => {
8242
+ if (name !== void 0 && !knownLineType(name, sources))
8243
+ throw new DrawingError(
8244
+ "unknownLineType",
8245
+ `${context}: unknown line type "${name}" (the built-in catalogue or linea_list_line_types)`
8246
+ );
8247
+ };
8248
+ var assertFont = (font, context) => {
8249
+ if (typeof font === "string" && !knownFont(font))
8250
+ throw new DrawingError(
8251
+ "unknownFont",
8252
+ `${context}: unknown font "${font}" (one of ${fontList})`
8253
+ );
8254
+ };
8255
+ var assertObjectFields = (obj, fields, sources, context) => {
8256
+ const o = obj;
8257
+ if (fields.has("lineType"))
8258
+ assertLineType(o.lineType, sources, context);
8259
+ if (fields.has("font")) assertFont(o.font, context);
8260
+ if (fields.has("styleOverride"))
8261
+ assertFont(
8262
+ o.styleOverride?.font,
8263
+ context
8264
+ );
8265
+ if (obj.type === "hatch" && fields.has("fill") && obj.fill.kind === "pattern") {
8266
+ if (!resolveHatchPattern(obj.fill.name, sources.hatchPatterns))
8267
+ throw new DrawingError(
8268
+ "unknownHatchPattern",
8269
+ `${context}: unknown hatch pattern "${obj.fill.name}" (the built-in catalogue or linea_list_hatch_patterns)`
8270
+ );
8271
+ }
8272
+ if (obj.type === "text" && (fields.has("spans") || fields.has("content")))
8273
+ for (const [i, s] of (obj.spans ?? []).entries()) {
8274
+ if (s.end > obj.content.length || s.start >= s.end)
8275
+ throw new DrawingError(
8276
+ "invalidSpan",
8277
+ `${context}: spans[${i}] ${s.start}\u2013${s.end} must satisfy start < end \u2264 ${obj.content.length} (the content's length)`
8278
+ );
8279
+ assertFont(s.font, `${context}: spans[${i}]`);
8280
+ }
8281
+ if (obj.type === "block" && fields.has("params"))
8282
+ assertBlockParams(obj, sources, context);
8283
+ };
8284
+ var assertBlockParams = (obj, sources, context) => {
8285
+ let script;
8286
+ try {
8287
+ script = findBlockScript(sources.blockDefinitions, obj.definitionId);
8288
+ } catch {
8289
+ return;
8290
+ }
8291
+ if (!script) return;
8292
+ const meta = getBlockMetaResult(script);
8293
+ if (!meta.ok) return;
8294
+ const defs = valueParams(meta.meta.params);
8295
+ for (const [name, value] of Object.entries(obj.params ?? {})) {
8296
+ const def = defs.find((d) => d.name === name);
8297
+ if (!def)
8298
+ throw new DrawingError(
8299
+ "invalidParam",
8300
+ `${context}: block "${obj.definitionId}" has no parameter "${name}"${defs.length ? ` (declares ${defs.map((d) => d.name).join(", ")})` : ""}`
8301
+ );
8302
+ const mismatch = paramMismatch(def, value);
8303
+ if (mismatch)
8304
+ throw new DrawingError(
8305
+ "invalidParam",
8306
+ `${context}: parameter "${name}" \u2014 ${mismatch}`
8307
+ );
8308
+ }
8309
+ };
8310
+
8311
+ // ../core/src/drawing/localDrawing.ts
8382
8312
  var toVec = (p) => Vec2Schema.parse(p);
8313
+ var refuseRejected = (report, kind) => {
8314
+ if (report.rejected.length === 0) return;
8315
+ const lines = report.rejected.map(
8316
+ (r, i) => `${i + 1}) ${scanScriptId(r.script, /define(Block|Command)\(|export\s+default\s*\{/) ?? "(no id)"}: ${r.error}`
8317
+ );
8318
+ throw Object.assign(
8319
+ new Error(
8320
+ `${report.rejected.length} ${kind} source(s) rejected, so none were added:
8321
+ ${lines.join("\n")}`
8322
+ ),
8323
+ { code: "moduleRejected" }
8324
+ );
8325
+ };
8326
+ var checkedStyle = (schema, full, sent, context) => {
8327
+ let style;
8328
+ try {
8329
+ style = schema.parse(full);
8330
+ } catch (e) {
8331
+ throw wrapZod(e, context);
8332
+ }
8333
+ assertNoUnknownKeys(sent, style, context);
8334
+ if ("font" in sent) assertFont(sent.font, context);
8335
+ return style;
8336
+ };
8337
+ var assertDataUrl = (dataUrl) => {
8338
+ const m = /^data:([\w.+-]+\/[\w.+-]+)(;[^,]*)?;base64,([A-Za-z0-9+/]*={0,2})$/.exec(
8339
+ dataUrl
8340
+ );
8341
+ if (!m)
8342
+ throw new DrawingError(
8343
+ "invalidAsset",
8344
+ "An asset must be a base64 data URL (data:<type>;base64,...)"
8345
+ );
8346
+ const bytes = Math.floor(m[3].length * 3 / 4) - (m[3].match(/=/g)?.length ?? 0);
8347
+ if (bytes < 16)
8348
+ throw new DrawingError(
8349
+ "invalidAsset",
8350
+ `The ${m[1]} data URL holds only ${bytes} bytes, not a real file`
8351
+ );
8352
+ };
8353
+ var MAX_COPIES = 1e4;
8383
8354
  var wrapZod = (e, context) => {
8384
8355
  if (!(e instanceof z6.ZodError))
8385
8356
  return e instanceof Error ? e : new Error(String(e));
8386
- return new Error(`${context}: ${z6.prettifyError(e)}`);
8357
+ return new DrawingError(
8358
+ "validation",
8359
+ `${context}: ${z6.prettifyError(e)}`,
8360
+ e.issues
8361
+ );
8387
8362
  };
8388
8363
  var PDF_PAGE_GAP_MM = 50;
8389
8364
  var selectPdfLayouts = (layouts, opts, fallback) => {
8390
8365
  const byRef = (ref) => {
8391
8366
  const l = layouts.find((x) => x.name === ref || x.id === ref);
8392
- if (!l) throw new Error(`Unknown layout "${ref}"`);
8367
+ if (!l) throw new DrawingError("unknownLayout", `Unknown layout "${ref}"`);
8393
8368
  return l;
8394
8369
  };
8395
8370
  let selected = opts.layouts !== void 0 ? opts.layouts.map(byRef) : layouts.length > 0 ? [...layouts] : [fallback()];
@@ -8496,11 +8471,6 @@ var kernelHooks = (doc) => {
8496
8471
  revision: () => rev
8497
8472
  };
8498
8473
  };
8499
- var withVisible = (layer) => {
8500
- const { hidden, ...rest } = layer;
8501
- if (hidden === void 0) return layer;
8502
- return { ...rest, visible: !hidden };
8503
- };
8504
8474
  var LocalDrawing = class {
8505
8475
  hooks;
8506
8476
  /** Schema-version migration applied on open; null when none was needed. */
@@ -8556,7 +8526,7 @@ var LocalDrawing = class {
8556
8526
  findObject(id) {
8557
8527
  const doc = this.state;
8558
8528
  const obj = resolveDocumentObject(doc, id);
8559
- if (!obj) throw new Error(`Unknown object "${id}"`);
8529
+ if (!obj) throw new DrawingError("unknownObject", `Unknown object "${id}"`);
8560
8530
  const layout = doc.layouts.find((l) => l.objects.includes(obj));
8561
8531
  return { obj, space: layout ? { layoutId: layout.id } : "model" };
8562
8532
  }
@@ -8638,8 +8608,11 @@ var LocalDrawing = class {
8638
8608
  (l) => l.name.toLowerCase() === layer.name.toLowerCase()
8639
8609
  );
8640
8610
  if (clash)
8641
- throw new Error(`addLayer(): layer "${clash.name}" already exists`);
8642
- const full = LayerSchema.parse(withVisible(layer));
8611
+ throw new DrawingError(
8612
+ "duplicateName",
8613
+ `Layer "${clash.name}" already exists`
8614
+ );
8615
+ const full = this.checkedLayer(layer, layer, "add layer");
8643
8616
  this.applyForward({ type: "add-layer", layer: full });
8644
8617
  return full.id;
8645
8618
  }
@@ -8647,7 +8620,8 @@ var LocalDrawing = class {
8647
8620
  const l = this.state.layers.find(
8648
8621
  (x) => x.id === nameOrId || x.name === nameOrId
8649
8622
  );
8650
- if (!l) throw new Error(`Unknown layer "${nameOrId}"`);
8623
+ if (!l)
8624
+ throw new DrawingError("unknownLayer", `Unknown layer "${nameOrId}"`);
8651
8625
  return l;
8652
8626
  }
8653
8627
  /** Patch a layer (rename, color, lineWidth, visible, locked) by name or id. */
@@ -8658,13 +8632,36 @@ var LocalDrawing = class {
8658
8632
  (l) => l.name.toLowerCase() === patch.name.toLowerCase()
8659
8633
  );
8660
8634
  if (clash)
8661
- throw new Error(`updateLayer(): layer "${clash.name}" already exists`);
8635
+ throw new DrawingError(
8636
+ "duplicateName",
8637
+ `Layer "${clash.name}" already exists`
8638
+ );
8662
8639
  }
8663
8640
  this.applyForward({
8664
8641
  type: "update-layer",
8665
- layer: LayerSchema.parse({ ...cur, ...withVisible(patch), id: cur.id })
8642
+ layer: this.checkedLayer(
8643
+ { ...cur, ...patch, id: cur.id },
8644
+ patch,
8645
+ `update layer "${cur.name}"`
8646
+ )
8666
8647
  });
8667
8648
  }
8649
+ /** A layer parsed from `full`, refusing an empty name, unknown fields
8650
+ * and an unknown line type among what the caller `sent`. */
8651
+ checkedLayer(full, sent, context) {
8652
+ if (typeof sent.name === "string" && sent.name.trim() === "")
8653
+ throw new DrawingError("validation", `${context}: the name is empty`);
8654
+ let layer;
8655
+ try {
8656
+ layer = LayerSchema.parse(full);
8657
+ } catch (e) {
8658
+ throw wrapZod(e, context);
8659
+ }
8660
+ assertNoUnknownKeys(sent, layer, context, "layer");
8661
+ if ("lineType" in sent)
8662
+ assertLineType(layer.lineType, this.nameSources, context);
8663
+ return layer;
8664
+ }
8668
8665
  /** Remove an EMPTY, non-current layer (the kernel refuses otherwise). */
8669
8666
  removeLayer(nameOrId) {
8670
8667
  this.applyForward({
@@ -8699,6 +8696,7 @@ var LocalDrawing = class {
8699
8696
  * Content-keyed: the same data reuses one entry.
8700
8697
  */
8701
8698
  addAsset(dataUrl) {
8699
+ assertDataUrl(dataUrl);
8702
8700
  const id = hashAsset(dataUrl);
8703
8701
  if (this.state.assets[id] !== dataUrl)
8704
8702
  this.applyForward({ type: "set-asset", id, dataUrl });
@@ -8806,25 +8804,32 @@ var LocalDrawing = class {
8806
8804
  // (case-insensitive) on add and rename.
8807
8805
  resolveStyle(styles, nameOrId, what) {
8808
8806
  const s = styles.find((x) => x.id === nameOrId || x.name === nameOrId);
8809
- if (!s) throw new Error(`Unknown ${what} "${nameOrId}"`);
8807
+ if (!s)
8808
+ throw new DrawingError("unknownStyle", `Unknown ${what} "${nameOrId}"`);
8810
8809
  return s;
8811
8810
  }
8812
- assertStyleName(styles, name, context, except) {
8811
+ assertStyleName(styles, name, except) {
8813
8812
  const clash = styles.find(
8814
8813
  (s) => s.id !== except && s.name.toLowerCase() === name.toLowerCase()
8815
8814
  );
8816
8815
  if (clash)
8817
- throw new Error(`${context}: style "${clash.name}" already exists`);
8816
+ throw new DrawingError(
8817
+ "duplicateName",
8818
+ `Style "${clash.name}" already exists`
8819
+ );
8818
8820
  }
8819
8821
  /** Layouts are addressed by name everywhere (`layout` on the object
8820
8822
  * tools, remove, reorder, render) — a duplicate would silently resolve to
8821
8823
  * the first match. */
8822
- assertLayoutName(name, context, except) {
8824
+ assertLayoutName(name, except) {
8823
8825
  const clash = this.state.layouts.find(
8824
8826
  (l) => l.id !== except && l.name.toLowerCase() === name.toLowerCase()
8825
8827
  );
8826
8828
  if (clash)
8827
- throw new Error(`${context}: layout "${clash.name}" already exists`);
8829
+ throw new DrawingError(
8830
+ "duplicateName",
8831
+ `Layout "${clash.name}" already exists`
8832
+ );
8828
8833
  }
8829
8834
  /** Objects reference styles by id, but callers often pass the NAME:
8830
8835
  * resolve it (text/table → text styles, dimension → dim styles). An
@@ -8836,7 +8841,8 @@ var LocalDrawing = class {
8836
8841
  if (!styles) return {};
8837
8842
  const s = styles.find((x) => x.id === obj.styleId) ?? styles.find((x) => x.name === obj.styleId);
8838
8843
  if (!s)
8839
- throw new Error(
8844
+ throw new DrawingError(
8845
+ "unknownStyle",
8840
8846
  `${context}: unknown ${obj.type === "dimension" ? "dim" : "text"} style "${obj.styleId}"`
8841
8847
  );
8842
8848
  return { styleId: s.id };
@@ -8850,8 +8856,8 @@ var LocalDrawing = class {
8850
8856
  /** Create a text style (unique name; unset fields take the defaults);
8851
8857
  * returns its id. */
8852
8858
  addTextStyle(props) {
8853
- this.assertStyleName(this.state.textStyles, props.name, "addTextStyle()");
8854
- const style = TextStyleSchema.parse(props);
8859
+ this.assertStyleName(this.state.textStyles, props.name);
8860
+ const style = checkedStyle(TextStyleSchema, props, props, "add text style");
8855
8861
  this.applyForward({ type: "add-text-style", style });
8856
8862
  return style.id;
8857
8863
  }
@@ -8862,15 +8868,15 @@ var LocalDrawing = class {
8862
8868
  "text style"
8863
8869
  );
8864
8870
  if (patch.name !== void 0)
8865
- this.assertStyleName(
8866
- this.state.textStyles,
8867
- patch.name,
8868
- "updateTextStyle()",
8869
- cur.id
8870
- );
8871
+ this.assertStyleName(this.state.textStyles, patch.name, cur.id);
8871
8872
  this.applyForward({
8872
8873
  type: "update-text-style",
8873
- style: TextStyleSchema.parse({ ...cur, ...patch, id: cur.id })
8874
+ style: checkedStyle(
8875
+ TextStyleSchema,
8876
+ { ...cur, ...patch, id: cur.id },
8877
+ patch,
8878
+ `update text style "${cur.name}"`
8879
+ )
8874
8880
  });
8875
8881
  }
8876
8882
  // --- line types / hatch patterns (name-keyed definitions) -----------------
@@ -8889,7 +8895,8 @@ var LocalDrawing = class {
8889
8895
  const cur = this.state.lineTypes.find(
8890
8896
  (t) => lineTypeKey(t.name) === lineTypeKey(name)
8891
8897
  );
8892
- if (!cur) throw new Error(`Unknown line type "${name}"`);
8898
+ if (!cur)
8899
+ throw new DrawingError("unknownLineType", `Unknown line type "${name}"`);
8893
8900
  const next = LineTypeSchema.parse({ ...cur, ...patch });
8894
8901
  if (lineTypeKey(next.name) !== lineTypeKey(cur.name)) {
8895
8902
  this.transaction(() => {
@@ -8916,7 +8923,11 @@ var LocalDrawing = class {
8916
8923
  const cur = this.state.hatchPatterns.find(
8917
8924
  (p) => hatchPatternKey(p.name) === hatchPatternKey(name)
8918
8925
  );
8919
- if (!cur) throw new Error(`Unknown hatch pattern "${name}"`);
8926
+ if (!cur)
8927
+ throw new DrawingError(
8928
+ "unknownHatchPattern",
8929
+ `Unknown hatch pattern "${name}"`
8930
+ );
8920
8931
  const next = HatchPatternSchema.parse({ ...cur, ...patch });
8921
8932
  if (hatchPatternKey(next.name) !== hatchPatternKey(cur.name)) {
8922
8933
  this.transaction(() => {
@@ -8939,23 +8950,23 @@ var LocalDrawing = class {
8939
8950
  /** Create a dimension style (unique name; unset fields take the
8940
8951
  * defaults); returns its id. */
8941
8952
  addDimStyle(props) {
8942
- this.assertStyleName(this.state.dimStyles, props.name, "addDimStyle()");
8943
- const style = DimStyleSchema.parse(props);
8953
+ this.assertStyleName(this.state.dimStyles, props.name);
8954
+ const style = checkedStyle(DimStyleSchema, props, props, "add dim style");
8944
8955
  this.applyForward({ type: "add-dim-style", style });
8945
8956
  return style.id;
8946
8957
  }
8947
8958
  updateDimStyle(nameOrId, patch) {
8948
8959
  const cur = this.resolveStyle(this.state.dimStyles, nameOrId, "dim style");
8949
8960
  if (patch.name !== void 0)
8950
- this.assertStyleName(
8951
- this.state.dimStyles,
8952
- patch.name,
8953
- "updateDimStyle()",
8954
- cur.id
8955
- );
8961
+ this.assertStyleName(this.state.dimStyles, patch.name, cur.id);
8956
8962
  this.applyForward({
8957
8963
  type: "update-dim-style",
8958
- style: DimStyleSchema.parse({ ...cur, ...patch, id: cur.id })
8964
+ style: checkedStyle(
8965
+ DimStyleSchema,
8966
+ { ...cur, ...patch, id: cur.id },
8967
+ patch,
8968
+ `update dim style "${cur.name}"`
8969
+ )
8959
8970
  });
8960
8971
  }
8961
8972
  /** Remove a dimension style no dimension uses (the kernel refuses
@@ -9031,6 +9042,7 @@ var LocalDrawing = class {
9031
9042
  async addBlockDefinitions(sources) {
9032
9043
  await ensureScriptTranspiler();
9033
9044
  const report = mergeBlockModules(this.state.blockDefinitions, sources);
9045
+ refuseRejected(report, "block definition");
9034
9046
  this.applyForwardAll(
9035
9047
  diffModuleOps(
9036
9048
  this.state.blockDefinitions,
@@ -9043,6 +9055,7 @@ var LocalDrawing = class {
9043
9055
  async addUserCommands(sources) {
9044
9056
  await ensureScriptTranspiler();
9045
9057
  const report = mergeCommandModules(this.state.commands, sources);
9058
+ refuseRejected(report, "user command");
9046
9059
  this.applyForwardAll(
9047
9060
  diffModuleOps(this.state.commands, report.merged, "commands")
9048
9061
  );
@@ -9132,7 +9145,6 @@ var LocalDrawing = class {
9132
9145
  };
9133
9146
  for (const plan of plans) {
9134
9147
  const r = plan.kind === "block" ? await this.addBlockDefinitions(plan.sources) : await this.addUserCommands(plan.sources);
9135
- if (r.rejected.length > 0) throw new Error(r.rejected[0].error);
9136
9148
  report.merged = r.merged;
9137
9149
  report.added.push(...r.added);
9138
9150
  report.replaced.push(...r.replaced);
@@ -9146,7 +9158,8 @@ var LocalDrawing = class {
9146
9158
  const source = this.state.blockDefinitions.find(
9147
9159
  (s) => listBlockMeta([s]).some((r) => r.ok && r.meta.id === id)
9148
9160
  );
9149
- if (source === void 0) throw new Error(`Unknown block "${id}"`);
9161
+ if (source === void 0)
9162
+ throw new DrawingError("unknownBlock", `Unknown block "${id}"`);
9150
9163
  this.applyForward({ type: "remove-block-definition", source });
9151
9164
  }
9152
9165
  /** Remove a command module by its exported id (unknown id → error). */
@@ -9154,7 +9167,8 @@ var LocalDrawing = class {
9154
9167
  const source = this.state.commands.find(
9155
9168
  (s) => listCommandMeta([s]).some((r) => r.ok && r.meta.id === id)
9156
9169
  );
9157
- if (source === void 0) throw new Error(`Unknown command "${id}"`);
9170
+ if (source === void 0)
9171
+ throw new DrawingError("unknownCommand", `Unknown command "${id}"`);
9158
9172
  this.applyForward({ type: "remove-document-command", source });
9159
9173
  }
9160
9174
  /**
@@ -9230,7 +9244,7 @@ var LocalDrawing = class {
9230
9244
  * to the model extents. Returns the layout id.
9231
9245
  */
9232
9246
  addLayout(name, opts = {}) {
9233
- this.assertLayoutName(name, "addLayout()");
9247
+ this.assertLayoutName(name);
9234
9248
  const layout = this.makeLayout(name, opts);
9235
9249
  this.applyForward({ type: "add-layout", layout, index: opts.index });
9236
9250
  return layout.id;
@@ -9253,8 +9267,7 @@ var LocalDrawing = class {
9253
9267
  /** Patch a layout's name / sheet (preset or mm) / print palette. */
9254
9268
  updateLayout(nameOrId, patch) {
9255
9269
  const layout = this.resolveLayout(nameOrId);
9256
- if (patch.name !== void 0)
9257
- this.assertLayoutName(patch.name, "updateLayout()", layout.id);
9270
+ if (patch.name !== void 0) this.assertLayoutName(patch.name, layout.id);
9258
9271
  const { sheet, ...rest } = patch;
9259
9272
  this.applyForward({
9260
9273
  type: "update-layout-meta",
@@ -9264,6 +9277,14 @@ var LocalDrawing = class {
9264
9277
  });
9265
9278
  }
9266
9279
  // --- object mutations ----------------------------------------------------
9280
+ /** What names in a new or changed object must resolve against. */
9281
+ get nameSources() {
9282
+ return {
9283
+ lineTypes: this.state.lineTypes,
9284
+ hatchPatterns: this.state.hatchPatterns,
9285
+ blockDefinitions: this.state.blockDefinitions
9286
+ };
9287
+ }
9267
9288
  normalize(input, index) {
9268
9289
  const { layer, layerId, id, ...rest } = input;
9269
9290
  const context = `add(): objects[${index ?? 0}] (${String(input.type ?? "?")})`;
@@ -9276,9 +9297,16 @@ var LocalDrawing = class {
9276
9297
  layerId: this.resolveLayerId(layer, layerId),
9277
9298
  id: id ?? crypto.randomUUID()
9278
9299
  };
9279
- return ModelObjectSchema.parse(
9280
- raw.type === "dimension" ? dimensionRawToLabels(dimensionRawToOffset(raw)) : raw
9300
+ const obj = ModelObjectSchema.parse(raw);
9301
+ assertNoUnknownKeys(raw, obj, context, obj.type);
9302
+ assertSaneNumbers(obj, context);
9303
+ assertObjectFields(
9304
+ obj,
9305
+ new Set(Object.keys(rest)),
9306
+ this.nameSources,
9307
+ context
9281
9308
  );
9309
+ return obj;
9282
9310
  } catch (e) {
9283
9311
  throw wrapZod(
9284
9312
  e,
@@ -9310,18 +9338,25 @@ var LocalDrawing = class {
9310
9338
  `update(): "${id}" (${cur.type})`
9311
9339
  ) : {};
9312
9340
  const staleSpans = cur.type === "text" && "content" in rest && !("spans" in rest) ? { spans: void 0 } : {};
9341
+ const context = `update(): "${id}" (${cur.type})`;
9313
9342
  try {
9314
- return {
9315
- space,
9316
- obj: ModelObjectSchema.parse({
9317
- ...cur,
9318
- ...rest,
9319
- ...staleSpans,
9320
- ...styleRef,
9321
- ...resolvedLayerId ? { layerId: resolvedLayerId } : {},
9322
- id
9323
- })
9324
- };
9343
+ const obj = ModelObjectSchema.parse({
9344
+ ...cur,
9345
+ ...rest,
9346
+ ...staleSpans,
9347
+ ...styleRef,
9348
+ ...resolvedLayerId ? { layerId: resolvedLayerId } : {},
9349
+ id
9350
+ });
9351
+ assertNoUnknownKeys(rest, obj, context, cur.type);
9352
+ assertSaneNumbers(rest, context);
9353
+ assertObjectFields(
9354
+ obj,
9355
+ new Set(Object.keys(rest)),
9356
+ this.nameSources,
9357
+ context
9358
+ );
9359
+ return { space, obj };
9325
9360
  } catch (e) {
9326
9361
  throw wrapZod(e, `update(): "${id}" (${cur.type})`);
9327
9362
  }
@@ -9430,22 +9465,50 @@ var LocalDrawing = class {
9430
9465
  mirror(ids, a, b) {
9431
9466
  const pa = toVec(a);
9432
9467
  const pb = toVec(b);
9468
+ if (pa.x === pb.x && pa.y === pb.y)
9469
+ throw new DrawingError(
9470
+ "validation",
9471
+ "Mirror needs two different points for its axis"
9472
+ );
9433
9473
  this.transformEach(ids, (o) => mirrorObject(o, pa, pb));
9434
9474
  }
9435
9475
  /** Duplicate objects (into their own space) displaced by `delta`. */
9436
9476
  copy(ids, delta) {
9437
- if (ids.length === 0) return [];
9477
+ return this.copySteps(ids, [toVec(delta)]);
9478
+ }
9479
+ /**
9480
+ * Linear array: `count` TOTAL instances (including the originals) stepped
9481
+ * by `delta`. Returns the new ids, one batch per step, in one undo step.
9482
+ */
9483
+ array(ids, count, delta) {
9484
+ if (!Number.isInteger(count) || count < 1)
9485
+ throw new Error(`Invalid array count ${count}`);
9438
9486
  const d = toVec(delta);
9439
- const idMap = new Map(ids.map((id) => [id, crypto.randomUUID()]));
9440
- const clones = ids.map((id) => {
9441
- const { obj, space } = this.findObject(id);
9442
- return {
9487
+ const steps = Array.from({ length: count - 1 }, (_, i) => ({
9488
+ x: d.x * (i + 1),
9489
+ y: d.y * (i + 1)
9490
+ }));
9491
+ return this.copySteps(ids, steps);
9492
+ }
9493
+ /** One displaced copy of `ids` per step, applied as ONE add op per space
9494
+ * (an op per step re-copied the whole space each time: quadratic). */
9495
+ copySteps(ids, steps) {
9496
+ if (ids.length === 0 || steps.length === 0) return [];
9497
+ const total2 = ids.length * steps.length;
9498
+ if (total2 > MAX_COPIES)
9499
+ throw new Error(
9500
+ `That would create ${total2} objects; one copy or array makes at most ${MAX_COPIES}.`
9501
+ );
9502
+ const sources = ids.map((id) => this.findObject(id));
9503
+ const clones = steps.flatMap((d) => {
9504
+ const idMap = new Map(ids.map((id) => [id, crypto.randomUUID()]));
9505
+ return sources.map(({ obj, space }) => ({
9443
9506
  space,
9444
9507
  obj: ModelObjectSchema.parse({
9445
9508
  ...remapBlockInputRefs(translateObject(obj, d), idMap),
9446
- id: idMap.get(id)
9509
+ id: idMap.get(obj.id)
9447
9510
  })
9448
- };
9511
+ }));
9449
9512
  });
9450
9513
  this.applyBySpace(clones, (space, objects) => ({
9451
9514
  type: "add-objects",
@@ -9454,21 +9517,6 @@ var LocalDrawing = class {
9454
9517
  }));
9455
9518
  return clones.map((c) => c.obj.id);
9456
9519
  }
9457
- /**
9458
- * Linear array: `count` TOTAL instances (including the originals) stepped
9459
- * by `delta`. Returns the new ids, one batch per step, in one undo step.
9460
- */
9461
- array(ids, count, delta) {
9462
- if (!Number.isInteger(count) || count < 1)
9463
- throw new Error(`Invalid array count ${count}`);
9464
- const d = toVec(delta);
9465
- return this.transaction(() => {
9466
- const created = [];
9467
- for (let i = 1; i < count; i++)
9468
- created.push(...this.copy(ids, { x: d.x * i, y: d.y * i }));
9469
- return created;
9470
- });
9471
- }
9472
9520
  // --- reads ---------------------------------------------------------------
9473
9521
  get measurer() {
9474
9522
  return this.hooks.measure ?? approximateTextMeasurer;
@@ -9632,7 +9680,8 @@ var LocalDrawing = class {
9632
9680
  const layout = this.state.layouts.find(
9633
9681
  (l) => l.name === nameOrId || l.id === nameOrId
9634
9682
  );
9635
- if (!layout) throw new Error(`Unknown layout "${nameOrId}"`);
9683
+ if (!layout)
9684
+ throw new DrawingError("unknownLayout", `Unknown layout "${nameOrId}"`);
9636
9685
  return layout;
9637
9686
  }
9638
9687
  /** Render model space (or a paper layout via `layout`) to an SVG string. */
@@ -9643,11 +9692,18 @@ var LocalDrawing = class {
9643
9692
  this.paintContext(),
9644
9693
  opts
9645
9694
  );
9646
- return renderModelToSvg(this.paintContext(), opts);
9695
+ return renderModelToSvg(this.paintContext(), {
9696
+ lineTypeScale: this.document.settings.modelScale,
9697
+ // Model lineweights are true mm: on a building-sized drawing they
9698
+ // rasterize sub-pixel and read as a blank sheet. Keep every stroke
9699
+ // ≥ ~1.5 px at the output width (PNG) or a 1024 px view (SVG).
9700
+ minStrokeFrac: 1.5 / (opts.width ?? 1024),
9701
+ ...opts
9702
+ });
9647
9703
  }
9648
9704
  /** Export the document to a DXF string. */
9649
9705
  async toDxf() {
9650
- const { buildDxf } = await import("./exportDxf-C4CLQCC3.js");
9706
+ const { buildDxf } = await import("./exportDxf-6XTXQ4XN.js");
9651
9707
  return buildDxf(this.state, this.measurer).content;
9652
9708
  }
9653
9709
  /** Export the document to DWG bytes (the DXF content, wasm-transcoded).
@@ -9656,7 +9712,7 @@ var LocalDrawing = class {
9656
9712
  * exploded to plain geometry (DWG_DXF_OPTIONS — the transcoder's DWG
9657
9713
  * output for them is not accepted by AutoCAD). */
9658
9714
  async toDwg(opts) {
9659
- const { buildDxf, DWG_DXF_OPTIONS } = await import("./exportDxf-C4CLQCC3.js");
9715
+ const { buildDxf, DWG_DXF_OPTIONS } = await import("./exportDxf-6XTXQ4XN.js");
9660
9716
  const dxf = buildDxf(this.state, this.measurer, DWG_DXF_OPTIONS).content;
9661
9717
  return dxfToDwg(dxf, opts?.version ?? "AC1027");
9662
9718
  }
@@ -9790,7 +9846,7 @@ var runCommandModule = async (source, opts) => {
9790
9846
  }
9791
9847
  };
9792
9848
  var namedEditorError = (name, e) => {
9793
- const m = e instanceof TypeError ? /reading '(pickPoint|pickPoints|pickObject|pickObjects|prompt|ask|toast|state|setSelection|setActiveSpace|zoom|listCommands|runCommand)'/.exec(
9849
+ const m = e instanceof TypeError ? /(?:reading|property) '(pickPoint|pickPoints|pickObject|pickObjects|prompt|ask|toast|state|setSelection|setActiveSpace|zoom|listCommands|runCommand)'/.exec(
9794
9850
  e.message
9795
9851
  ) : null;
9796
9852
  if (!m) return e;
@@ -9982,7 +10038,10 @@ var Drawing = class extends LocalDrawing {
9982
10038
  this.paintContext(),
9983
10039
  opts
9984
10040
  );
9985
- return renderModelToPng(this.paintContext(), opts);
10041
+ return renderModelToPng(this.paintContext(), {
10042
+ lineTypeScale: this.document.settings.modelScale,
10043
+ ...opts
10044
+ });
9986
10045
  }
9987
10046
  /** Multi-page PDF of the layouts (default: all) with the embedded fonts. */
9988
10047
  async renderPdf(opts = {}) {
@@ -10094,10 +10153,12 @@ export {
10094
10153
  ensureScriptTranspiler,
10095
10154
  getBlockMetaResult,
10096
10155
  getCommandMetaResult,
10156
+ hatchLoop,
10097
10157
  newDrawing,
10098
10158
  openDrawing,
10099
10159
  parseScale,
10100
10160
  renderBlockThumbnailSvg,
10161
+ splineThrough,
10101
10162
  testBlockDefinition
10102
10163
  };
10103
10164
  //# sourceMappingURL=sdk.js.map