@linkiez/dxf-renew 7.0.0 → 7.1.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 (131) hide show
  1. package/.github/instructions/code-patterns.instructions.md +1 -1
  2. package/.github/instructions/exdxf.instruction.md +161 -0
  3. package/.github/instructions/tdd.instructions.md +271 -0
  4. package/.yarn/install-state.gz +0 -0
  5. package/CHANGELOG.md +23 -0
  6. package/CONTRIBUTING.md +16 -14
  7. package/PLAN.md +34 -84
  8. package/README.md +43 -8
  9. package/dist/dxf.js +1388 -376
  10. package/docs/DIMENSION_SUMMARY.md +11 -5
  11. package/docs/DXF_VERSION_SUPPORT.md +45 -0
  12. package/docs/ENTITY_SVG_ROADMAP.md +96 -0
  13. package/docs/EZDXF_REFERENCE_SITEMAP.md +55 -0
  14. package/docs/FIXTURE_VALIDATION_EZDXF.md +62 -0
  15. package/docs/README.md +22 -0
  16. package/docs/SVG_RENDERING_INTEGRATION_TESTS.md +119 -0
  17. package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.md +1 -1
  18. package/lib/Helper.cjs +2 -2
  19. package/lib/Helper.cjs.map +2 -2
  20. package/lib/Helper.js +2 -2
  21. package/lib/Helper.js.map +2 -2
  22. package/lib/denormalise.cjs +131 -91
  23. package/lib/denormalise.cjs.map +2 -2
  24. package/lib/denormalise.js +131 -91
  25. package/lib/denormalise.js.map +2 -2
  26. package/lib/dimensionToSVG.cjs +318 -53
  27. package/lib/dimensionToSVG.cjs.map +3 -3
  28. package/lib/dimensionToSVG.js +316 -52
  29. package/lib/dimensionToSVG.js.map +2 -2
  30. package/lib/handlers/entities.cjs +90 -26
  31. package/lib/handlers/entities.cjs.map +3 -3
  32. package/lib/handlers/entities.js +90 -26
  33. package/lib/handlers/entities.js.map +3 -3
  34. package/lib/handlers/entity/dgnUnderlay.cjs +106 -0
  35. package/lib/handlers/entity/dgnUnderlay.cjs.map +7 -0
  36. package/lib/handlers/entity/dgnUnderlay.js +71 -0
  37. package/lib/handlers/entity/dgnUnderlay.js.map +7 -0
  38. package/lib/handlers/entity/dimension.cjs +24 -0
  39. package/lib/handlers/entity/dimension.cjs.map +2 -2
  40. package/lib/handlers/entity/dimension.js +24 -0
  41. package/lib/handlers/entity/dimension.js.map +2 -2
  42. package/lib/handlers/entity/dwfUnderlay.cjs +106 -0
  43. package/lib/handlers/entity/dwfUnderlay.cjs.map +7 -0
  44. package/lib/handlers/entity/dwfUnderlay.js +71 -0
  45. package/lib/handlers/entity/dwfUnderlay.js.map +7 -0
  46. package/lib/handlers/entity/image.cjs +123 -0
  47. package/lib/handlers/entity/image.cjs.map +7 -0
  48. package/lib/handlers/entity/image.js +88 -0
  49. package/lib/handlers/entity/image.js.map +7 -0
  50. package/lib/handlers/entity/leader.cjs +148 -0
  51. package/lib/handlers/entity/leader.cjs.map +7 -0
  52. package/lib/handlers/entity/leader.js +113 -0
  53. package/lib/handlers/entity/leader.js.map +7 -0
  54. package/lib/handlers/entity/pdfUnderlay.cjs +106 -0
  55. package/lib/handlers/entity/pdfUnderlay.cjs.map +7 -0
  56. package/lib/handlers/entity/pdfUnderlay.js +71 -0
  57. package/lib/handlers/entity/pdfUnderlay.js.map +7 -0
  58. package/lib/handlers/entity/tolerance.cjs +90 -0
  59. package/lib/handlers/entity/tolerance.cjs.map +7 -0
  60. package/lib/handlers/entity/tolerance.js +55 -0
  61. package/lib/handlers/entity/tolerance.js.map +7 -0
  62. package/lib/handlers/objects.cjs +257 -136
  63. package/lib/handlers/objects.cjs.map +2 -2
  64. package/lib/handlers/objects.js +257 -136
  65. package/lib/handlers/objects.js.map +2 -2
  66. package/lib/toSVG.cjs +71 -8
  67. package/lib/toSVG.cjs.map +3 -3
  68. package/lib/toSVG.js +72 -9
  69. package/lib/toSVG.js.map +2 -2
  70. package/lib/types/dimension-entity.cjs.map +1 -1
  71. package/lib/types/entity.cjs.map +1 -1
  72. package/lib/types/image-entity.cjs +17 -0
  73. package/lib/types/image-entity.cjs.map +7 -0
  74. package/lib/types/image-entity.js +1 -0
  75. package/lib/types/image-entity.js.map +7 -0
  76. package/lib/types/index.cjs +8 -0
  77. package/lib/types/index.cjs.map +2 -2
  78. package/lib/types/index.js +4 -0
  79. package/lib/types/index.js.map +2 -2
  80. package/lib/types/leader-entity.cjs +17 -0
  81. package/lib/types/leader-entity.cjs.map +7 -0
  82. package/lib/types/leader-entity.js +1 -0
  83. package/lib/types/leader-entity.js.map +7 -0
  84. package/lib/types/options.cjs.map +1 -1
  85. package/lib/types/tables.cjs.map +1 -1
  86. package/lib/types/tolerance-entity.cjs +17 -0
  87. package/lib/types/tolerance-entity.cjs.map +7 -0
  88. package/lib/types/tolerance-entity.js +1 -0
  89. package/lib/types/tolerance-entity.js.map +7 -0
  90. package/lib/types/underlay-entity.cjs +17 -0
  91. package/lib/types/underlay-entity.cjs.map +7 -0
  92. package/lib/types/underlay-entity.js +1 -0
  93. package/lib/types/underlay-entity.js.map +7 -0
  94. package/lib/util/escapeXmlText.cjs +27 -0
  95. package/lib/util/escapeXmlText.cjs.map +7 -0
  96. package/lib/util/escapeXmlText.js +7 -0
  97. package/lib/util/escapeXmlText.js.map +7 -0
  98. package/package.json +6 -1
  99. package/playwright.config.cjs +20 -0
  100. package/src/Helper.ts +3 -3
  101. package/src/denormalise.ts +182 -116
  102. package/src/dimensionToSVG.ts +466 -54
  103. package/src/handlers/entities.ts +109 -34
  104. package/src/handlers/entity/dgnUnderlay.ts +94 -0
  105. package/src/handlers/entity/dimension.ts +27 -1
  106. package/src/handlers/entity/dwfUnderlay.ts +94 -0
  107. package/src/handlers/entity/image.ts +118 -0
  108. package/src/handlers/entity/leader.ts +153 -0
  109. package/src/handlers/entity/pdfUnderlay.ts +94 -0
  110. package/src/handlers/entity/tolerance.ts +75 -0
  111. package/src/handlers/objects.ts +323 -139
  112. package/src/toSVG.ts +98 -7
  113. package/src/types/dimension-entity.ts +11 -0
  114. package/src/types/entity.ts +10 -0
  115. package/src/types/image-entity.ts +35 -0
  116. package/src/types/index.ts +4 -0
  117. package/src/types/leader-entity.ts +40 -0
  118. package/src/types/options.ts +41 -0
  119. package/src/types/tables.ts +84 -0
  120. package/src/types/tolerance-entity.ts +20 -0
  121. package/src/types/underlay-entity.ts +35 -0
  122. package/src/util/escapeXmlText.ts +10 -0
  123. package/tools/browser_test_server.cjs +87 -0
  124. package/tools/ezdxf_generate_dimensions_all_types.py +246 -0
  125. package/tools/ezdxf_generate_dimensions_angular_3p.py +59 -0
  126. package/tools/ezdxf_generate_dimensions_large_scale.py +87 -0
  127. package/tools/ezdxf_regenerate_problem_fixtures.py +184 -0
  128. package/tools/ezdxf_validate_fixtures.py +165 -0
  129. package/docs/DIMENSION_SUMMARY.pt-BR.md +0 -248
  130. package/docs/IMPLEMENTED-2D-ENTITIES.pt-BR.md +0 -54
  131. package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.pt-BR.md +0 -169
@@ -1,5 +1,167 @@
1
1
  import { Box2 } from "vecks";
2
2
  import colors from "./util/colors";
3
+ import escapeXmlText from "./util/escapeXmlText";
4
+ import round10 from "./util/round10";
5
+ const DEFAULT_DIMENSION_DECIMALS = 2;
6
+ const AUTOSCALE_VIEWPORT_REFERENCE = 40;
7
+ const computeViewportAutoScaleFactor = (viewport, options) => {
8
+ const viewportMin = Math.min(Math.abs(viewport.width), Math.abs(viewport.height));
9
+ if (!Number.isFinite(viewportMin) || viewportMin <= 0) return 1;
10
+ const reference = options?.dimension?.autoScaleViewportReference;
11
+ const safeReference = Number.isFinite(reference) && (reference ?? 0) > 0 ? reference : AUTOSCALE_VIEWPORT_REFERENCE;
12
+ return viewportMin / safeReference;
13
+ };
14
+ const getViewportMin = (viewport) => {
15
+ const viewportMin = Math.min(Math.abs(viewport.width), Math.abs(viewport.height));
16
+ return Number.isFinite(viewportMin) ? viewportMin : Number.NaN;
17
+ };
18
+ const getViewportPercentageSize = (viewport, percent) => {
19
+ if (!Number.isFinite(percent) || (percent ?? 0) <= 0) return void 0;
20
+ const viewportMin = getViewportMin(viewport);
21
+ if (!Number.isFinite(viewportMin) || viewportMin <= 0) return void 0;
22
+ return viewportMin * (percent / 100);
23
+ };
24
+ const getDimensionGeometryBBox = (entity) => {
25
+ const bbox = new Box2();
26
+ const points = [
27
+ entity.start,
28
+ entity.angleVertex,
29
+ entity.arcPoint,
30
+ entity.textMidpoint,
31
+ entity.measureStart,
32
+ entity.measureEnd
33
+ ];
34
+ for (const p of points) {
35
+ if (!p) continue;
36
+ const x = p.x;
37
+ const y = p.y;
38
+ if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
39
+ bbox.expandByPoint({ x, y });
40
+ }
41
+ return bbox;
42
+ };
43
+ const getScaledDimensionSizes = (dimStyle, options, viewport) => {
44
+ const autoScale = options?.dimension?.autoScale === true;
45
+ const baseArrowSize = dimStyle?.dimAsz ?? 2.5;
46
+ const baseTextHeight = dimStyle?.dimTxt ?? 2.5;
47
+ const baseExtLineOffset = dimStyle?.dimExo ?? 0.625;
48
+ const baseExtLineExtension = dimStyle?.dimExe ?? 1.25;
49
+ if (!autoScale || !viewport) {
50
+ return {
51
+ arrowSize: baseArrowSize,
52
+ textHeight: baseTextHeight,
53
+ extLineOffset: baseExtLineOffset,
54
+ extLineExtension: baseExtLineExtension
55
+ };
56
+ }
57
+ const scale = computeViewportAutoScaleFactor(viewport, options);
58
+ const perc = options?.dimension?.autoScaleViewportPercentages;
59
+ const arrowFromPct = getViewportPercentageSize(viewport, perc?.arrowSize);
60
+ const textFromPct = getViewportPercentageSize(viewport, perc?.textHeight);
61
+ const offsetFromPct = getViewportPercentageSize(viewport, perc?.extLineOffset);
62
+ const extensionFromPct = getViewportPercentageSize(viewport, perc?.extLineExtension);
63
+ return {
64
+ arrowSize: arrowFromPct ?? baseArrowSize * scale,
65
+ textHeight: textFromPct ?? baseTextHeight * scale,
66
+ extLineOffset: offsetFromPct ?? baseExtLineOffset * scale,
67
+ extLineExtension: extensionFromPct ?? baseExtLineExtension * scale
68
+ };
69
+ };
70
+ const formatDimensionValue = (value, decimals = DEFAULT_DIMENSION_DECIMALS) => {
71
+ if (!Number.isFinite(value)) return "";
72
+ const rounded = round10(value, -decimals);
73
+ return rounded.toFixed(decimals);
74
+ };
75
+ const computeRadiusFallback = (entity) => {
76
+ const cx = entity.start?.x ?? 0;
77
+ const cy = entity.start?.y ?? 0;
78
+ const x1 = entity.measureStart?.x ?? 0;
79
+ const y1 = entity.measureStart?.y ?? 0;
80
+ const x2 = entity.measureEnd?.x ?? 0;
81
+ const y2 = entity.measureEnd?.y ?? 0;
82
+ const r1 = Math.hypot(x1 - cx, y1 - cy);
83
+ const r2 = Math.hypot(x2 - cx, y2 - cy);
84
+ const chord = Math.hypot(x2 - x1, y2 - y1);
85
+ return Math.max(r1, r2, chord);
86
+ };
87
+ const computeLinearDistance = (x1, y1, x2, y2) => Math.hypot(x2 - x1, y2 - y1);
88
+ const computeAngularDegreesMinimal = (cx, cy, x1, y1, x2, y2) => {
89
+ const a1 = Math.atan2(y1 - cy, x1 - cx);
90
+ const a2 = Math.atan2(y2 - cy, x2 - cx);
91
+ let delta = Math.abs(a2 - a1);
92
+ while (delta > Math.PI * 2) delta -= Math.PI * 2;
93
+ if (delta > Math.PI) delta = Math.PI * 2 - delta;
94
+ return delta * 180 / Math.PI;
95
+ };
96
+ const computeAngularDegreesCCW = (cx, cy, x1, y1, x2, y2) => {
97
+ const a1 = Math.atan2(y1 - cy, x1 - cx);
98
+ const a2 = Math.atan2(y2 - cy, x2 - cx);
99
+ let delta = a2 - a1;
100
+ while (delta < 0) delta += Math.PI * 2;
101
+ while (delta >= Math.PI * 2) delta -= Math.PI * 2;
102
+ return delta * 180 / Math.PI;
103
+ };
104
+ const computeDimensionMeasurement = (entity) => {
105
+ const x1 = entity.measureStart?.x ?? 0;
106
+ const y1 = entity.measureStart?.y ?? 0;
107
+ const x2 = entity.measureEnd?.x ?? 0;
108
+ const y2 = entity.measureEnd?.y ?? 0;
109
+ switch (entity.dimensionType) {
110
+ case 0:
111
+ case 1:
112
+ case 6: {
113
+ const dist = computeLinearDistance(x1, y1, x2, y2);
114
+ return formatDimensionValue(dist);
115
+ }
116
+ case 3: {
117
+ const dist = computeLinearDistance(x1, y1, x2, y2);
118
+ if (dist > 0) return formatDimensionValue(dist);
119
+ const radius = computeRadiusFallback(entity);
120
+ return formatDimensionValue(radius * 2);
121
+ }
122
+ case 4: {
123
+ const dist = computeLinearDistance(x1, y1, x2, y2);
124
+ if (dist > 0) return formatDimensionValue(dist);
125
+ const radius = computeRadiusFallback(entity);
126
+ return formatDimensionValue(radius);
127
+ }
128
+ case 2: {
129
+ const cx = entity.start?.x ?? 0;
130
+ const cy = entity.start?.y ?? 0;
131
+ const degrees = computeAngularDegreesMinimal(cx, cy, x1, y1, x2, y2);
132
+ const formatted = formatDimensionValue(degrees);
133
+ return formatted ? `${formatted}\xB0` : "";
134
+ }
135
+ case 5: {
136
+ const cx = entity.angleVertex?.x ?? 0;
137
+ const cy = entity.angleVertex?.y ?? 0;
138
+ const degrees = computeAngularDegreesCCW(cx, cy, x1, y1, x2, y2);
139
+ const formatted = formatDimensionValue(degrees);
140
+ return formatted ? `${formatted}\xB0` : "";
141
+ }
142
+ default:
143
+ return "";
144
+ }
145
+ };
146
+ const resolveDimensionText = (entity) => {
147
+ const raw = typeof entity.text === "string" ? entity.text : "";
148
+ const trimmed = raw.trim();
149
+ const measured = computeDimensionMeasurement(entity);
150
+ if (!trimmed) return measured;
151
+ if (trimmed.includes("<>")) {
152
+ return trimmed.split("<>").join(measured);
153
+ }
154
+ return trimmed;
155
+ };
156
+ const expandBBoxForMarker = (bbox, x, y, size) => {
157
+ bbox.expandByPoint({ x: x - size, y: y - size });
158
+ bbox.expandByPoint({ x: x + size, y: y + size });
159
+ };
160
+ const expandBBoxForText = (bbox, x, y, height, content) => {
161
+ const textWidth = content.length * height * 0.6;
162
+ bbox.expandByPoint({ x: x - textWidth / 2, y: y - height });
163
+ bbox.expandByPoint({ x: x + textWidth / 2, y: y + height });
164
+ };
3
165
  function colorNumberToSVG(colorNumber) {
4
166
  if (colorNumber === void 0 || colorNumber < 0) {
5
167
  return "currentColor";
@@ -22,38 +184,117 @@ function getDimensionColors(dimStyle) {
22
184
  extLineWeight: dimStyle?.dimLwe ?? 0.5
23
185
  };
24
186
  }
25
- function dimensionToSVG(entity, dimStyle) {
187
+ function dimensionToSVG(entity, dimStyle, options, viewport) {
26
188
  switch (entity.dimensionType) {
27
189
  case 0:
28
190
  // Rotated, horizontal, or vertical
29
191
  case 1:
30
- return renderLinearDimension(entity, dimStyle);
192
+ return renderLinearDimension(entity, dimStyle, options, viewport);
31
193
  case 2:
32
- return renderAngularDimension(entity, dimStyle);
194
+ return renderAngularDimension(entity, dimStyle, options, viewport);
195
+ case 5:
196
+ return renderAngular3PointDimension(entity, dimStyle, options, viewport);
33
197
  case 3:
34
- return renderDiameterDimension(entity, dimStyle);
198
+ return renderDiameterDimension(entity, dimStyle, options, viewport);
35
199
  case 4:
36
- return renderRadialDimension(entity, dimStyle);
200
+ return renderRadialDimension(entity, dimStyle, options, viewport);
37
201
  case 6:
38
- return renderOrdinateDimension(entity, dimStyle);
202
+ return renderOrdinateDimension(entity, dimStyle, options, viewport);
39
203
  default:
40
204
  return renderFallbackDimension(entity);
41
205
  }
42
206
  }
43
- function createArrowMarker(id, size, color) {
44
- const arrowPath = `M 0 0 L ${size} ${size / 2} L 0 ${size} z`;
45
- return `<marker id="${id}" markerWidth="${size}" markerHeight="${size}" refX="${size}" refY="${size / 2}" orient="auto" markerUnits="strokeWidth">
207
+ function renderAngular3PointDimension(entity, dimStyle, options, viewport) {
208
+ const bbox = new Box2();
209
+ const elements = [];
210
+ const markers = [];
211
+ const { arrowSize, textHeight } = getScaledDimensionSizes(dimStyle, options, viewport);
212
+ const { dimLineColor, extLineColor, textColor, dimLineWeight, extLineWeight } = getDimensionColors(dimStyle);
213
+ const vertexX = entity.angleVertex?.x ?? 0;
214
+ const vertexY = entity.angleVertex?.y ?? 0;
215
+ const x1 = entity.measureStart?.x ?? 0;
216
+ const y1 = entity.measureStart?.y ?? 0;
217
+ const x2 = entity.measureEnd?.x ?? 0;
218
+ const y2 = entity.measureEnd?.y ?? 0;
219
+ const startArcX = entity.start?.x ?? 0;
220
+ const startArcY = entity.start?.y ?? 0;
221
+ const arcPointX = entity.arcPoint?.x;
222
+ const arcPointY = entity.arcPoint?.y;
223
+ const arcPointRadius = Number.isFinite(arcPointX) && Number.isFinite(arcPointY) ? Math.hypot(arcPointX - vertexX, arcPointY - vertexY) : Number.NaN;
224
+ const useArcPoint = Number.isFinite(arcPointRadius) && arcPointRadius > 1e-9;
225
+ const arcLocationX = useArcPoint ? arcPointX : startArcX;
226
+ const arcLocationY = useArcPoint ? arcPointY : startArcY;
227
+ const textX = entity.textMidpoint?.x ?? arcLocationX;
228
+ const textY = entity.textMidpoint?.y ?? arcLocationY;
229
+ bbox.expandByPoint({ x: vertexX, y: vertexY });
230
+ bbox.expandByPoint({ x: x1, y: y1 });
231
+ bbox.expandByPoint({ x: x2, y: y2 });
232
+ bbox.expandByPoint({ x: arcLocationX, y: arcLocationY });
233
+ bbox.expandByPoint({ x: textX, y: textY });
234
+ const a1 = Math.atan2(y1 - vertexY, x1 - vertexX);
235
+ const a2 = Math.atan2(y2 - vertexY, x2 - vertexX);
236
+ let radius = Math.hypot(arcLocationX - vertexX, arcLocationY - vertexY);
237
+ if (!Number.isFinite(radius) || radius <= 1e-9) {
238
+ radius = Math.hypot(textX - vertexX, textY - vertexY);
239
+ }
240
+ if (!Number.isFinite(radius) || radius <= 1e-9) {
241
+ radius = Math.max(
242
+ Math.hypot(x1 - vertexX, y1 - vertexY),
243
+ Math.hypot(x2 - vertexX, y2 - vertexY)
244
+ );
245
+ }
246
+ const arcStartX = vertexX + radius * Math.cos(a1);
247
+ const arcStartY = vertexY + radius * Math.sin(a1);
248
+ const arcEndX = vertexX + radius * Math.cos(a2);
249
+ const arcEndY = vertexY + radius * Math.sin(a2);
250
+ bbox.expandByPoint({ x: arcStartX, y: arcStartY });
251
+ bbox.expandByPoint({ x: arcEndX, y: arcEndY });
252
+ const markerId1 = `dim-angular-3p-arrow-start-${Date.now()}`;
253
+ const markerId2 = `dim-angular-3p-arrow-end-${Date.now()}`;
254
+ markers.push(
255
+ createArrowMarker(markerId1, arrowSize, dimLineColor, "backward"),
256
+ createArrowMarker(markerId2, arrowSize, dimLineColor, "forward")
257
+ );
258
+ elements.push(
259
+ `<line x1="${x1}" y1="${y1}" x2="${arcStartX}" y2="${arcStartY}" stroke="${extLineColor}" stroke-width="${extLineWeight}" />`,
260
+ `<line x1="${x2}" y1="${y2}" x2="${arcEndX}" y2="${arcEndY}" stroke="${extLineColor}" stroke-width="${extLineWeight}" />`
261
+ );
262
+ let delta = a2 - a1;
263
+ while (delta < 0) delta += Math.PI * 2;
264
+ while (delta >= Math.PI * 2) delta -= Math.PI * 2;
265
+ const largeArcFlag = delta > Math.PI ? 1 : 0;
266
+ const sweepFlag = 1;
267
+ expandBBoxForMarker(bbox, arcStartX, arcStartY, arrowSize);
268
+ expandBBoxForMarker(bbox, arcEndX, arcEndY, arrowSize);
269
+ elements.push(
270
+ `<path d="M ${arcStartX} ${arcStartY} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${arcEndX} ${arcEndY}" fill="none" stroke="${dimLineColor}" stroke-width="${dimLineWeight}" marker-start="url(#${markerId1})" marker-end="url(#${markerId2})" />`
271
+ );
272
+ const resolvedText = resolveDimensionText(entity);
273
+ if (resolvedText) {
274
+ const midAngle = a1 + delta / 2;
275
+ const textRotation = midAngle * 180 / Math.PI;
276
+ expandBBoxForText(bbox, textX, textY, textHeight, resolvedText);
277
+ elements.push(
278
+ `<text x="${textX}" y="${textY}" font-size="${textHeight}" fill="${textColor}" text-anchor="middle" transform="rotate(${-textRotation} ${textX} ${textY}) scale(1,-1) translate(0 ${-2 * textY})">${escapeXmlText(resolvedText)}</text>`
279
+ );
280
+ }
281
+ return {
282
+ bbox,
283
+ element: `<defs>${markers.join("")}</defs><g>${elements.join("")}</g>`
284
+ };
285
+ }
286
+ function createArrowMarker(id, size, color, direction = "forward") {
287
+ const arrowPath = direction === "forward" ? `M 0 0 L ${size} ${size / 2} L 0 ${size} z` : `M ${size} 0 L 0 ${size / 2} L ${size} ${size} z`;
288
+ const refX = direction === "forward" ? size : 0;
289
+ return `<marker id="${id}" markerWidth="${size}" markerHeight="${size}" refX="${refX}" refY="${size / 2}" orient="auto" markerUnits="userSpaceOnUse">
46
290
  <path d="${arrowPath}" fill="${color}" />
47
291
  </marker>`;
48
292
  }
49
- function renderLinearDimension(entity, dimStyle) {
293
+ function renderLinearDimension(entity, dimStyle, options, viewport) {
50
294
  const bbox = new Box2();
51
295
  const elements = [];
52
296
  const markers = [];
53
- const arrowSize = dimStyle?.dimAsz ?? 2.5;
54
- const textHeight = dimStyle?.dimTxt ?? 2.5;
55
- const extLineOffset = dimStyle?.dimExo ?? 0.625;
56
- const extLineExtension = dimStyle?.dimExe ?? 1.25;
297
+ const { arrowSize, textHeight, extLineOffset, extLineExtension } = getScaledDimensionSizes(dimStyle, options, viewport);
57
298
  const { dimLineColor, extLineColor, textColor, dimLineWeight, extLineWeight } = getDimensionColors(dimStyle);
58
299
  const defPoint1X = entity.measureStart?.x ?? 0;
59
300
  const defPoint1Y = entity.measureStart?.y ?? 0;
@@ -76,8 +317,8 @@ function renderLinearDimension(entity, dimStyle) {
76
317
  const markerId1 = `dim-arrow-start-${Date.now()}`;
77
318
  const markerId2 = `dim-arrow-end-${Date.now()}`;
78
319
  markers.push(
79
- createArrowMarker(markerId1, arrowSize, dimLineColor),
80
- createArrowMarker(markerId2, arrowSize, dimLineColor)
320
+ createArrowMarker(markerId1, arrowSize, dimLineColor, "backward"),
321
+ createArrowMarker(markerId2, arrowSize, dimLineColor, "forward")
81
322
  );
82
323
  const extLine1StartX = defPoint1X + Math.cos(perpAngle) * extLineOffset;
83
324
  const extLine1StartY = defPoint1Y + Math.sin(perpAngle) * extLineOffset;
@@ -87,15 +328,23 @@ function renderLinearDimension(entity, dimStyle) {
87
328
  const extLine2StartY = defPoint2Y + Math.sin(perpAngle) * extLineOffset;
88
329
  const extLine2EndX = dimLine2X + Math.cos(perpAngle) * extLineExtension;
89
330
  const extLine2EndY = dimLine2Y + Math.sin(perpAngle) * extLineExtension;
331
+ bbox.expandByPoint({ x: extLine1StartX, y: extLine1StartY });
332
+ bbox.expandByPoint({ x: extLine1EndX, y: extLine1EndY });
333
+ bbox.expandByPoint({ x: extLine2StartX, y: extLine2StartY });
334
+ bbox.expandByPoint({ x: extLine2EndX, y: extLine2EndY });
335
+ expandBBoxForMarker(bbox, dimLine1X, dimLine1Y, arrowSize);
336
+ expandBBoxForMarker(bbox, dimLine2X, dimLine2Y, arrowSize);
90
337
  elements.push(
91
338
  `<line x1="${extLine1StartX}" y1="${extLine1StartY}" x2="${extLine1EndX}" y2="${extLine1EndY}" stroke="${extLineColor}" stroke-width="${extLineWeight}" />`,
92
339
  `<line x1="${extLine2StartX}" y1="${extLine2StartY}" x2="${extLine2EndX}" y2="${extLine2EndY}" stroke="${extLineColor}" stroke-width="${extLineWeight}" />`,
93
340
  `<line x1="${dimLine1X}" y1="${dimLine1Y}" x2="${dimLine2X}" y2="${dimLine2Y}" stroke="${dimLineColor}" stroke-width="${dimLineWeight}" marker-start="url(#${markerId1})" marker-end="url(#${markerId2})" />`
94
341
  );
95
- if (entity.text) {
342
+ const resolvedText = resolveDimensionText(entity);
343
+ if (resolvedText) {
96
344
  const textRotation = angle * 180 / Math.PI;
345
+ expandBBoxForText(bbox, textX, textY, textHeight, resolvedText);
97
346
  elements.push(
98
- `<text x="${textX}" y="${textY}" font-size="${textHeight}" fill="${textColor}" text-anchor="middle" transform="rotate(${-textRotation} ${textX} ${textY}) scale(1,-1) translate(0 ${-2 * textY})">${entity.text}</text>`
347
+ `<text x="${textX}" y="${textY}" font-size="${textHeight}" fill="${textColor}" text-anchor="middle" transform="rotate(${-textRotation} ${textX} ${textY}) scale(1,-1) translate(0 ${-2 * textY})">${escapeXmlText(resolvedText)}</text>`
99
348
  );
100
349
  }
101
350
  return {
@@ -103,12 +352,11 @@ function renderLinearDimension(entity, dimStyle) {
103
352
  element: `<defs>${markers.join("")}</defs><g>${elements.join("")}</g>`
104
353
  };
105
354
  }
106
- function renderAngularDimension(entity, dimStyle) {
355
+ function renderAngularDimension(entity, dimStyle, options, viewport) {
107
356
  const bbox = new Box2();
108
357
  const elements = [];
109
358
  const markers = [];
110
- const arrowSize = dimStyle?.dimAsz ?? 2.5;
111
- const textHeight = dimStyle?.dimTxt ?? 2.5;
359
+ const { arrowSize, textHeight } = getScaledDimensionSizes(dimStyle, options, viewport);
112
360
  const { dimLineColor, extLineColor, textColor, dimLineWeight, extLineWeight } = getDimensionColors(dimStyle);
113
361
  const centerX = entity.start?.x ?? 0;
114
362
  const centerY = entity.start?.y ?? 0;
@@ -125,8 +373,8 @@ function renderAngularDimension(entity, dimStyle) {
125
373
  const markerId1 = `dim-angular-arrow-start-${Date.now()}`;
126
374
  const markerId2 = `dim-angular-arrow-end-${Date.now()}`;
127
375
  markers.push(
128
- createArrowMarker(markerId1, arrowSize, dimLineColor),
129
- createArrowMarker(markerId2, arrowSize, dimLineColor)
376
+ createArrowMarker(markerId1, arrowSize, dimLineColor, "backward"),
377
+ createArrowMarker(markerId2, arrowSize, dimLineColor, "forward")
130
378
  );
131
379
  elements.push(
132
380
  `<line x1="${centerX}" y1="${centerY}" x2="${x1}" y2="${y1}" stroke="${extLineColor}" stroke-width="${extLineWeight}" />`,
@@ -143,11 +391,13 @@ function renderAngularDimension(entity, dimStyle) {
143
391
  elements.push(
144
392
  `<path d="M ${arcStartX} ${arcStartY} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${arcEndX} ${arcEndY}" fill="none" stroke="${dimLineColor}" stroke-width="${dimLineWeight}" marker-start="url(#${markerId1})" marker-end="url(#${markerId2})" />`
145
393
  );
146
- if (entity.text) {
394
+ const resolvedText = resolveDimensionText(entity);
395
+ if (resolvedText) {
147
396
  const midAngle = (startAngle + endAngle) / 2;
148
397
  const textRotation = midAngle * 180 / Math.PI;
398
+ expandBBoxForText(bbox, textX, textY, textHeight, resolvedText);
149
399
  elements.push(
150
- `<text x="${textX}" y="${textY}" font-size="${textHeight}" fill="${textColor}" text-anchor="middle" transform="rotate(${-textRotation} ${textX} ${textY}) scale(1,-1) translate(0 ${-2 * textY})">${entity.text}</text>`
400
+ `<text x="${textX}" y="${textY}" font-size="${textHeight}" fill="${textColor}" text-anchor="middle" transform="rotate(${-textRotation} ${textX} ${textY}) scale(1,-1) translate(0 ${-2 * textY})">${escapeXmlText(resolvedText)}</text>`
151
401
  );
152
402
  }
153
403
  return {
@@ -155,12 +405,11 @@ function renderAngularDimension(entity, dimStyle) {
155
405
  element: `<defs>${markers.join("")}</defs><g>${elements.join("")}</g>`
156
406
  };
157
407
  }
158
- function renderDiameterDimension(entity, dimStyle) {
408
+ function renderDiameterDimension(entity, dimStyle, options, viewport) {
159
409
  const bbox = new Box2();
160
410
  const elements = [];
161
411
  const markers = [];
162
- const arrowSize = dimStyle?.dimAsz ?? 2.5;
163
- const textHeight = dimStyle?.dimTxt ?? 2.5;
412
+ const { arrowSize, textHeight } = getScaledDimensionSizes(dimStyle, options, viewport);
164
413
  const { dimLineColor, textColor, dimLineWeight } = getDimensionColors(dimStyle);
165
414
  const x1 = entity.measureStart?.x ?? 0;
166
415
  const y1 = entity.measureStart?.y ?? 0;
@@ -171,28 +420,33 @@ function renderDiameterDimension(entity, dimStyle) {
171
420
  bbox.expandByPoint({ x: x1, y: y1 });
172
421
  bbox.expandByPoint({ x: x2, y: y2 });
173
422
  bbox.expandByPoint({ x: textX, y: textY });
174
- const markerId = `dim-diameter-arrow-${Date.now()}`;
175
- markers.push(createArrowMarker(markerId, arrowSize, dimLineColor));
176
- elements.push(
177
- `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${dimLineColor}" stroke-width="${dimLineWeight}" marker-end="url(#${markerId})" />`
178
- );
179
- const diameterText = entity.text ? `\u2300${entity.text}` : "\u2300";
423
+ const diameterLen = Math.hypot(x2 - x1, y2 - y1);
424
+ if (Number.isFinite(diameterLen) && diameterLen > 1e-6) {
425
+ const markerId = `dim-diameter-arrow-${Date.now()}`;
426
+ markers.push(createArrowMarker(markerId, arrowSize, dimLineColor, "backward"));
427
+ elements.push(
428
+ `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${dimLineColor}" stroke-width="${dimLineWeight}" marker-end="url(#${markerId})" />`
429
+ );
430
+ expandBBoxForMarker(bbox, x2, y2, arrowSize);
431
+ }
432
+ const resolvedText = resolveDimensionText(entity);
433
+ const diameterText = resolvedText ? `\u2300${resolvedText}` : "\u2300";
180
434
  const angle = Math.atan2(y2 - y1, x2 - x1);
181
435
  const textRotation = angle * 180 / Math.PI;
436
+ expandBBoxForText(bbox, textX, textY, textHeight, diameterText);
182
437
  elements.push(
183
- `<text x="${textX}" y="${textY}" font-size="${textHeight}" fill="${textColor}" text-anchor="middle" transform="rotate(${-textRotation} ${textX} ${textY}) scale(1,-1) translate(0 ${-2 * textY})">${diameterText}</text>`
438
+ `<text x="${textX}" y="${textY}" font-size="${textHeight}" fill="${textColor}" text-anchor="middle" transform="rotate(${-textRotation} ${textX} ${textY}) scale(1,-1) translate(0 ${-2 * textY})">${escapeXmlText(diameterText)}</text>`
184
439
  );
185
440
  return {
186
441
  bbox,
187
442
  element: `<defs>${markers.join("")}</defs><g>${elements.join("")}</g>`
188
443
  };
189
444
  }
190
- function renderRadialDimension(entity, dimStyle) {
445
+ function renderRadialDimension(entity, dimStyle, options, viewport) {
191
446
  const bbox = new Box2();
192
447
  const elements = [];
193
448
  const markers = [];
194
- const arrowSize = dimStyle?.dimAsz ?? 2.5;
195
- const textHeight = dimStyle?.dimTxt ?? 2.5;
449
+ const { arrowSize, textHeight } = getScaledDimensionSizes(dimStyle, options, viewport);
196
450
  const { dimLineColor, textColor, dimLineWeight } = getDimensionColors(dimStyle);
197
451
  const x1 = entity.measureStart?.x ?? 0;
198
452
  const y1 = entity.measureStart?.y ?? 0;
@@ -203,26 +457,32 @@ function renderRadialDimension(entity, dimStyle) {
203
457
  bbox.expandByPoint({ x: x1, y: y1 });
204
458
  bbox.expandByPoint({ x: x2, y: y2 });
205
459
  bbox.expandByPoint({ x: textX, y: textY });
206
- const markerId = `dim-radius-arrow-${Date.now()}`;
207
- markers.push(createArrowMarker(markerId, arrowSize, dimLineColor));
208
- elements.push(
209
- `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${dimLineColor}" stroke-width="${dimLineWeight}" marker-end="url(#${markerId})" />`
210
- );
211
- const radiusText = entity.text ? `R${entity.text}` : "R";
460
+ const radiusLen = Math.hypot(x2 - x1, y2 - y1);
461
+ if (Number.isFinite(radiusLen) && radiusLen > 1e-6) {
462
+ const markerId = `dim-radius-arrow-${Date.now()}`;
463
+ markers.push(createArrowMarker(markerId, arrowSize, dimLineColor, "backward"));
464
+ elements.push(
465
+ `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${dimLineColor}" stroke-width="${dimLineWeight}" marker-end="url(#${markerId})" />`
466
+ );
467
+ expandBBoxForMarker(bbox, x2, y2, arrowSize);
468
+ }
469
+ const resolvedText = resolveDimensionText(entity);
470
+ const radiusText = resolvedText ? `R${resolvedText}` : "R";
212
471
  const angle = Math.atan2(y2 - y1, x2 - x1);
213
472
  const textRotation = angle * 180 / Math.PI;
473
+ expandBBoxForText(bbox, textX, textY, textHeight, radiusText);
214
474
  elements.push(
215
- `<text x="${textX}" y="${textY}" font-size="${textHeight}" fill="${textColor}" text-anchor="middle" transform="rotate(${-textRotation} ${textX} ${textY}) scale(1,-1) translate(0 ${-2 * textY})">${radiusText}</text>`
475
+ `<text x="${textX}" y="${textY}" font-size="${textHeight}" fill="${textColor}" text-anchor="middle" transform="rotate(${-textRotation} ${textX} ${textY}) scale(1,-1) translate(0 ${-2 * textY})">${escapeXmlText(radiusText)}</text>`
216
476
  );
217
477
  return {
218
478
  bbox,
219
479
  element: `<defs>${markers.join("")}</defs><g>${elements.join("")}</g>`
220
480
  };
221
481
  }
222
- function renderOrdinateDimension(entity, dimStyle) {
482
+ function renderOrdinateDimension(entity, dimStyle, options, viewport) {
223
483
  const bbox = new Box2();
224
484
  const elements = [];
225
- const textHeight = dimStyle?.dimTxt ?? 2.5;
485
+ const { textHeight } = getScaledDimensionSizes(dimStyle, options, viewport);
226
486
  const { dimLineColor, textColor, dimLineWeight } = getDimensionColors(dimStyle);
227
487
  const x1 = entity.measureStart?.x ?? 0;
228
488
  const y1 = entity.measureStart?.y ?? 0;
@@ -236,11 +496,13 @@ function renderOrdinateDimension(entity, dimStyle) {
236
496
  elements.push(
237
497
  `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${dimLineColor}" stroke-width="${dimLineWeight}" />`
238
498
  );
239
- if (entity.text) {
499
+ const resolvedText = resolveDimensionText(entity);
500
+ if (resolvedText) {
240
501
  const angle = Math.atan2(y2 - y1, x2 - x1);
241
502
  const textRotation = angle * 180 / Math.PI;
503
+ expandBBoxForText(bbox, textX, textY, textHeight, resolvedText);
242
504
  elements.push(
243
- `<text x="${textX}" y="${textY}" font-size="${textHeight}" fill="${textColor}" text-anchor="middle" transform="rotate(${-textRotation} ${textX} ${textY}) scale(1,-1) translate(0 ${-2 * textY})">${entity.text}</text>`
505
+ `<text x="${textX}" y="${textY}" font-size="${textHeight}" fill="${textColor}" text-anchor="middle" transform="rotate(${-textRotation} ${textX} ${textY}) scale(1,-1) translate(0 ${-2 * textY})">${escapeXmlText(resolvedText)}</text>`
244
506
  );
245
507
  }
246
508
  return {
@@ -255,9 +517,10 @@ function renderFallbackDimension(entity) {
255
517
  const textX = entity.textMidpoint.x ?? 0;
256
518
  const textY = entity.textMidpoint.y ?? 0;
257
519
  bbox.expandByPoint({ x: textX, y: textY });
258
- if (entity.text) {
520
+ const resolvedText = resolveDimensionText(entity);
521
+ if (resolvedText) {
259
522
  elements.push(
260
- `<text x="${textX}" y="${textY}" font-size="2.5" text-anchor="middle" transform="scale(1,-1) translate(0 ${-2 * textY})">${entity.text}</text>`
523
+ `<text x="${textX}" y="${textY}" font-size="2.5" text-anchor="middle" transform="scale(1,-1) translate(0 ${-2 * textY})">${escapeXmlText(resolvedText)}</text>`
261
524
  );
262
525
  }
263
526
  }
@@ -268,6 +531,7 @@ function renderFallbackDimension(entity) {
268
531
  }
269
532
  export {
270
533
  createArrowMarker,
271
- dimensionToSVG as default
534
+ dimensionToSVG as default,
535
+ getDimensionGeometryBBox
272
536
  };
273
537
  //# sourceMappingURL=dimensionToSVG.js.map