@linkiez/dxf-renew 5.3.1 → 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.
- package/{docs/CODE_PATTERNS.md → .github/instructions/code-patterns.instructions.md} +4 -1
- package/.github/instructions/exdxf.instruction.md +161 -0
- package/.github/instructions/tdd.instructions.md +271 -0
- package/.github/workflows/release.yml +4 -5
- package/.releaserc.json +1 -1
- package/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +59 -0
- package/CONTRIBUTING.md +16 -14
- package/PLAN.md +34 -84
- package/README.md +43 -8
- package/dist/dxf.js +1388 -376
- package/docs/DIMENSION_SUMMARY.md +11 -5
- package/docs/DXF_VERSION_SUPPORT.md +45 -0
- package/docs/ENTITY_SVG_ROADMAP.md +96 -0
- package/docs/EZDXF_REFERENCE_SITEMAP.md +55 -0
- package/docs/FIXTURE_VALIDATION_EZDXF.md +62 -0
- package/docs/README.md +22 -0
- package/docs/SVG_RENDERING_INTEGRATION_TESTS.md +119 -0
- package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.md +1 -1
- package/lib/Helper.cjs +2 -2
- package/lib/Helper.cjs.map +2 -2
- package/lib/Helper.js +2 -2
- package/lib/Helper.js.map +2 -2
- package/lib/denormalise.cjs +131 -91
- package/lib/denormalise.cjs.map +2 -2
- package/lib/denormalise.js +131 -91
- package/lib/denormalise.js.map +2 -2
- package/lib/dimensionToSVG.cjs +318 -53
- package/lib/dimensionToSVG.cjs.map +3 -3
- package/lib/dimensionToSVG.js +316 -52
- package/lib/dimensionToSVG.js.map +2 -2
- package/lib/handlers/entities.cjs +90 -26
- package/lib/handlers/entities.cjs.map +3 -3
- package/lib/handlers/entities.js +90 -26
- package/lib/handlers/entities.js.map +3 -3
- package/lib/handlers/entity/dgnUnderlay.cjs +106 -0
- package/lib/handlers/entity/dgnUnderlay.cjs.map +7 -0
- package/lib/handlers/entity/dgnUnderlay.js +71 -0
- package/lib/handlers/entity/dgnUnderlay.js.map +7 -0
- package/lib/handlers/entity/dimension.cjs +24 -0
- package/lib/handlers/entity/dimension.cjs.map +2 -2
- package/lib/handlers/entity/dimension.js +24 -0
- package/lib/handlers/entity/dimension.js.map +2 -2
- package/lib/handlers/entity/dwfUnderlay.cjs +106 -0
- package/lib/handlers/entity/dwfUnderlay.cjs.map +7 -0
- package/lib/handlers/entity/dwfUnderlay.js +71 -0
- package/lib/handlers/entity/dwfUnderlay.js.map +7 -0
- package/lib/handlers/entity/image.cjs +123 -0
- package/lib/handlers/entity/image.cjs.map +7 -0
- package/lib/handlers/entity/image.js +88 -0
- package/lib/handlers/entity/image.js.map +7 -0
- package/lib/handlers/entity/leader.cjs +148 -0
- package/lib/handlers/entity/leader.cjs.map +7 -0
- package/lib/handlers/entity/leader.js +113 -0
- package/lib/handlers/entity/leader.js.map +7 -0
- package/lib/handlers/entity/pdfUnderlay.cjs +106 -0
- package/lib/handlers/entity/pdfUnderlay.cjs.map +7 -0
- package/lib/handlers/entity/pdfUnderlay.js +71 -0
- package/lib/handlers/entity/pdfUnderlay.js.map +7 -0
- package/lib/handlers/entity/tolerance.cjs +90 -0
- package/lib/handlers/entity/tolerance.cjs.map +7 -0
- package/lib/handlers/entity/tolerance.js +55 -0
- package/lib/handlers/entity/tolerance.js.map +7 -0
- package/lib/handlers/objects.cjs +257 -136
- package/lib/handlers/objects.cjs.map +2 -2
- package/lib/handlers/objects.js +257 -136
- package/lib/handlers/objects.js.map +2 -2
- package/lib/toSVG.cjs +71 -8
- package/lib/toSVG.cjs.map +3 -3
- package/lib/toSVG.js +72 -9
- package/lib/toSVG.js.map +2 -2
- package/lib/types/dimension-entity.cjs.map +1 -1
- package/lib/types/entity.cjs.map +1 -1
- package/lib/types/image-entity.cjs +17 -0
- package/lib/types/image-entity.cjs.map +7 -0
- package/lib/types/image-entity.js +1 -0
- package/lib/types/image-entity.js.map +7 -0
- package/lib/types/index.cjs +8 -0
- package/lib/types/index.cjs.map +2 -2
- package/lib/types/index.js +4 -0
- package/lib/types/index.js.map +2 -2
- package/lib/types/leader-entity.cjs +17 -0
- package/lib/types/leader-entity.cjs.map +7 -0
- package/lib/types/leader-entity.js +1 -0
- package/lib/types/leader-entity.js.map +7 -0
- package/lib/types/options.cjs.map +1 -1
- package/lib/types/tables.cjs.map +1 -1
- package/lib/types/tolerance-entity.cjs +17 -0
- package/lib/types/tolerance-entity.cjs.map +7 -0
- package/lib/types/tolerance-entity.js +1 -0
- package/lib/types/tolerance-entity.js.map +7 -0
- package/lib/types/underlay-entity.cjs +17 -0
- package/lib/types/underlay-entity.cjs.map +7 -0
- package/lib/types/underlay-entity.js +1 -0
- package/lib/types/underlay-entity.js.map +7 -0
- package/lib/util/escapeXmlText.cjs +27 -0
- package/lib/util/escapeXmlText.cjs.map +7 -0
- package/lib/util/escapeXmlText.js +7 -0
- package/lib/util/escapeXmlText.js.map +7 -0
- package/package.json +13 -4
- package/playwright.config.cjs +20 -0
- package/src/Helper.ts +3 -3
- package/src/denormalise.ts +182 -116
- package/src/dimensionToSVG.ts +466 -54
- package/src/handlers/entities.ts +109 -34
- package/src/handlers/entity/dgnUnderlay.ts +94 -0
- package/src/handlers/entity/dimension.ts +27 -1
- package/src/handlers/entity/dwfUnderlay.ts +94 -0
- package/src/handlers/entity/image.ts +118 -0
- package/src/handlers/entity/leader.ts +153 -0
- package/src/handlers/entity/pdfUnderlay.ts +94 -0
- package/src/handlers/entity/tolerance.ts +75 -0
- package/src/handlers/objects.ts +323 -139
- package/src/toSVG.ts +98 -7
- package/src/types/dimension-entity.ts +11 -0
- package/src/types/entity.ts +10 -0
- package/src/types/image-entity.ts +35 -0
- package/src/types/index.ts +4 -0
- package/src/types/leader-entity.ts +40 -0
- package/src/types/options.ts +41 -0
- package/src/types/tables.ts +84 -0
- package/src/types/tolerance-entity.ts +20 -0
- package/src/types/underlay-entity.ts +35 -0
- package/src/util/escapeXmlText.ts +10 -0
- package/tools/browser_test_server.cjs +87 -0
- package/tools/ezdxf_generate_dimensions_all_types.py +246 -0
- package/tools/ezdxf_generate_dimensions_angular_3p.py +59 -0
- package/tools/ezdxf_generate_dimensions_large_scale.py +87 -0
- package/tools/ezdxf_regenerate_problem_fixtures.py +184 -0
- package/tools/ezdxf_validate_fixtures.py +165 -0
- package/docs/DIMENSION_SUMMARY.pt-BR.md +0 -248
- package/docs/IMPLEMENTED-2D-ENTITIES.pt-BR.md +0 -54
- package/docs/TEXT-MTEXT-DIMENSION-SUPPORT.pt-BR.md +0 -169
package/src/dimensionToSVG.ts
CHANGED
|
@@ -1,11 +1,256 @@
|
|
|
1
1
|
import { Box2 } from 'vecks'
|
|
2
2
|
|
|
3
3
|
import colors from './util/colors'
|
|
4
|
+
import escapeXmlText from './util/escapeXmlText'
|
|
5
|
+
import round10 from './util/round10'
|
|
4
6
|
|
|
5
|
-
import type { DimensionEntity } from './types'
|
|
7
|
+
import type { DimensionEntity, ToSVGOptions } from './types'
|
|
6
8
|
import type { DimStyleTable } from './types/dxf'
|
|
7
9
|
import type { BoundsAndElement } from './types/svg'
|
|
8
10
|
|
|
11
|
+
const DEFAULT_DIMENSION_DECIMALS = 2
|
|
12
|
+
|
|
13
|
+
export interface DimensionViewport {
|
|
14
|
+
width: number
|
|
15
|
+
height: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// AutoScale is meant to improve readability of dimension graphics.
|
|
19
|
+
// Scale is derived from the drawing viewport (final SVG viewBox size).
|
|
20
|
+
// No min/max clamp by design.
|
|
21
|
+
const AUTOSCALE_VIEWPORT_REFERENCE = 40
|
|
22
|
+
|
|
23
|
+
const computeViewportAutoScaleFactor = (
|
|
24
|
+
viewport: DimensionViewport,
|
|
25
|
+
options: ToSVGOptions | undefined,
|
|
26
|
+
): number => {
|
|
27
|
+
const viewportMin = Math.min(Math.abs(viewport.width), Math.abs(viewport.height))
|
|
28
|
+
if (!Number.isFinite(viewportMin) || viewportMin <= 0) return 1
|
|
29
|
+
|
|
30
|
+
const reference = options?.dimension?.autoScaleViewportReference
|
|
31
|
+
const safeReference = Number.isFinite(reference) && (reference ?? 0) > 0
|
|
32
|
+
? (reference as number)
|
|
33
|
+
: AUTOSCALE_VIEWPORT_REFERENCE
|
|
34
|
+
|
|
35
|
+
return viewportMin / safeReference
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const getViewportMin = (viewport: DimensionViewport): number => {
|
|
39
|
+
const viewportMin = Math.min(Math.abs(viewport.width), Math.abs(viewport.height))
|
|
40
|
+
return Number.isFinite(viewportMin) ? viewportMin : Number.NaN
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const getViewportPercentageSize = (
|
|
44
|
+
viewport: DimensionViewport,
|
|
45
|
+
percent: number | undefined,
|
|
46
|
+
): number | undefined => {
|
|
47
|
+
if (!Number.isFinite(percent) || (percent ?? 0) <= 0) return undefined
|
|
48
|
+
const viewportMin = getViewportMin(viewport)
|
|
49
|
+
if (!Number.isFinite(viewportMin) || viewportMin <= 0) return undefined
|
|
50
|
+
return viewportMin * ((percent as number) / 100)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const getDimensionGeometryBBox = (entity: DimensionEntity): Box2 => {
|
|
54
|
+
const bbox = new Box2()
|
|
55
|
+
|
|
56
|
+
const points = [
|
|
57
|
+
entity.start,
|
|
58
|
+
entity.angleVertex,
|
|
59
|
+
entity.arcPoint,
|
|
60
|
+
entity.textMidpoint,
|
|
61
|
+
entity.measureStart,
|
|
62
|
+
entity.measureEnd,
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
for (const p of points) {
|
|
66
|
+
if (!p) continue
|
|
67
|
+
const x = p.x
|
|
68
|
+
const y = p.y
|
|
69
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) continue
|
|
70
|
+
bbox.expandByPoint({ x, y })
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return bbox
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const getScaledDimensionSizes = (
|
|
77
|
+
dimStyle: DimStyleTable | undefined,
|
|
78
|
+
options: ToSVGOptions | undefined,
|
|
79
|
+
viewport: DimensionViewport | undefined,
|
|
80
|
+
): {
|
|
81
|
+
arrowSize: number
|
|
82
|
+
textHeight: number
|
|
83
|
+
extLineOffset: number
|
|
84
|
+
extLineExtension: number
|
|
85
|
+
} => {
|
|
86
|
+
const autoScale = options?.dimension?.autoScale === true
|
|
87
|
+
|
|
88
|
+
const baseArrowSize = dimStyle?.dimAsz ?? 2.5
|
|
89
|
+
const baseTextHeight = dimStyle?.dimTxt ?? 2.5
|
|
90
|
+
const baseExtLineOffset = dimStyle?.dimExo ?? 0.625
|
|
91
|
+
const baseExtLineExtension = dimStyle?.dimExe ?? 1.25
|
|
92
|
+
|
|
93
|
+
if (!autoScale || !viewport) {
|
|
94
|
+
return {
|
|
95
|
+
arrowSize: baseArrowSize,
|
|
96
|
+
textHeight: baseTextHeight,
|
|
97
|
+
extLineOffset: baseExtLineOffset,
|
|
98
|
+
extLineExtension: baseExtLineExtension,
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const scale = computeViewportAutoScaleFactor(viewport, options)
|
|
103
|
+
|
|
104
|
+
const perc = options?.dimension?.autoScaleViewportPercentages
|
|
105
|
+
const arrowFromPct = getViewportPercentageSize(viewport, perc?.arrowSize)
|
|
106
|
+
const textFromPct = getViewportPercentageSize(viewport, perc?.textHeight)
|
|
107
|
+
const offsetFromPct = getViewportPercentageSize(viewport, perc?.extLineOffset)
|
|
108
|
+
const extensionFromPct = getViewportPercentageSize(viewport, perc?.extLineExtension)
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
arrowSize: arrowFromPct ?? (baseArrowSize * scale),
|
|
112
|
+
textHeight: textFromPct ?? (baseTextHeight * scale),
|
|
113
|
+
extLineOffset: offsetFromPct ?? (baseExtLineOffset * scale),
|
|
114
|
+
extLineExtension: extensionFromPct ?? (baseExtLineExtension * scale),
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const formatDimensionValue = (
|
|
119
|
+
value: number,
|
|
120
|
+
decimals: number = DEFAULT_DIMENSION_DECIMALS,
|
|
121
|
+
): string => {
|
|
122
|
+
if (!Number.isFinite(value)) return ''
|
|
123
|
+
const rounded = round10(value, -decimals)
|
|
124
|
+
return rounded.toFixed(decimals)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const computeRadiusFallback = (entity: DimensionEntity): number => {
|
|
128
|
+
const cx = entity.start?.x ?? 0
|
|
129
|
+
const cy = entity.start?.y ?? 0
|
|
130
|
+
const x1 = entity.measureStart?.x ?? 0
|
|
131
|
+
const y1 = entity.measureStart?.y ?? 0
|
|
132
|
+
const x2 = entity.measureEnd?.x ?? 0
|
|
133
|
+
const y2 = entity.measureEnd?.y ?? 0
|
|
134
|
+
|
|
135
|
+
const r1 = Math.hypot(x1 - cx, y1 - cy)
|
|
136
|
+
const r2 = Math.hypot(x2 - cx, y2 - cy)
|
|
137
|
+
const chord = Math.hypot(x2 - x1, y2 - y1)
|
|
138
|
+
return Math.max(r1, r2, chord)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const computeLinearDistance = (
|
|
142
|
+
x1: number,
|
|
143
|
+
y1: number,
|
|
144
|
+
x2: number,
|
|
145
|
+
y2: number,
|
|
146
|
+
): number => Math.hypot(x2 - x1, y2 - y1)
|
|
147
|
+
|
|
148
|
+
const computeAngularDegreesMinimal = (
|
|
149
|
+
cx: number,
|
|
150
|
+
cy: number,
|
|
151
|
+
x1: number,
|
|
152
|
+
y1: number,
|
|
153
|
+
x2: number,
|
|
154
|
+
y2: number,
|
|
155
|
+
): number => {
|
|
156
|
+
const a1 = Math.atan2(y1 - cy, x1 - cx)
|
|
157
|
+
const a2 = Math.atan2(y2 - cy, x2 - cx)
|
|
158
|
+
let delta = Math.abs(a2 - a1)
|
|
159
|
+
while (delta > Math.PI * 2) delta -= Math.PI * 2
|
|
160
|
+
if (delta > Math.PI) delta = Math.PI * 2 - delta
|
|
161
|
+
return (delta * 180) / Math.PI
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const computeAngularDegreesCCW = (
|
|
165
|
+
cx: number,
|
|
166
|
+
cy: number,
|
|
167
|
+
x1: number,
|
|
168
|
+
y1: number,
|
|
169
|
+
x2: number,
|
|
170
|
+
y2: number,
|
|
171
|
+
): number => {
|
|
172
|
+
const a1 = Math.atan2(y1 - cy, x1 - cx)
|
|
173
|
+
const a2 = Math.atan2(y2 - cy, x2 - cx)
|
|
174
|
+
let delta = a2 - a1
|
|
175
|
+
while (delta < 0) delta += Math.PI * 2
|
|
176
|
+
while (delta >= Math.PI * 2) delta -= Math.PI * 2
|
|
177
|
+
return (delta * 180) / Math.PI
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const computeDimensionMeasurement = (entity: DimensionEntity): string => {
|
|
181
|
+
const x1 = entity.measureStart?.x ?? 0
|
|
182
|
+
const y1 = entity.measureStart?.y ?? 0
|
|
183
|
+
const x2 = entity.measureEnd?.x ?? 0
|
|
184
|
+
const y2 = entity.measureEnd?.y ?? 0
|
|
185
|
+
|
|
186
|
+
switch (entity.dimensionType) {
|
|
187
|
+
case 0:
|
|
188
|
+
case 1:
|
|
189
|
+
case 6: {
|
|
190
|
+
const dist = computeLinearDistance(x1, y1, x2, y2)
|
|
191
|
+
return formatDimensionValue(dist)
|
|
192
|
+
}
|
|
193
|
+
case 3: {
|
|
194
|
+
const dist = computeLinearDistance(x1, y1, x2, y2)
|
|
195
|
+
if (dist > 0) return formatDimensionValue(dist)
|
|
196
|
+
const radius = computeRadiusFallback(entity)
|
|
197
|
+
return formatDimensionValue(radius * 2)
|
|
198
|
+
}
|
|
199
|
+
case 4: {
|
|
200
|
+
const dist = computeLinearDistance(x1, y1, x2, y2)
|
|
201
|
+
if (dist > 0) return formatDimensionValue(dist)
|
|
202
|
+
const radius = computeRadiusFallback(entity)
|
|
203
|
+
return formatDimensionValue(radius)
|
|
204
|
+
}
|
|
205
|
+
case 2: {
|
|
206
|
+
const cx = entity.start?.x ?? 0
|
|
207
|
+
const cy = entity.start?.y ?? 0
|
|
208
|
+
const degrees = computeAngularDegreesMinimal(cx, cy, x1, y1, x2, y2)
|
|
209
|
+
const formatted = formatDimensionValue(degrees)
|
|
210
|
+
return formatted ? `${formatted}°` : ''
|
|
211
|
+
}
|
|
212
|
+
case 5: {
|
|
213
|
+
const cx = entity.angleVertex?.x ?? 0
|
|
214
|
+
const cy = entity.angleVertex?.y ?? 0
|
|
215
|
+
const degrees = computeAngularDegreesCCW(cx, cy, x1, y1, x2, y2)
|
|
216
|
+
const formatted = formatDimensionValue(degrees)
|
|
217
|
+
return formatted ? `${formatted}°` : ''
|
|
218
|
+
}
|
|
219
|
+
default:
|
|
220
|
+
return ''
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const resolveDimensionText = (entity: DimensionEntity): string => {
|
|
225
|
+
const raw = typeof entity.text === 'string' ? entity.text : ''
|
|
226
|
+
const trimmed = raw.trim()
|
|
227
|
+
const measured = computeDimensionMeasurement(entity)
|
|
228
|
+
|
|
229
|
+
if (!trimmed) return measured
|
|
230
|
+
if (trimmed.includes('<>')) {
|
|
231
|
+
return trimmed.split('<>').join(measured)
|
|
232
|
+
}
|
|
233
|
+
return trimmed
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const expandBBoxForMarker = (bbox: Box2, x: number, y: number, size: number) => {
|
|
237
|
+
bbox.expandByPoint({ x: x - size, y: y - size })
|
|
238
|
+
bbox.expandByPoint({ x: x + size, y: y + size })
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const expandBBoxForText = (
|
|
242
|
+
bbox: Box2,
|
|
243
|
+
x: number,
|
|
244
|
+
y: number,
|
|
245
|
+
height: number,
|
|
246
|
+
content: string,
|
|
247
|
+
) => {
|
|
248
|
+
const textWidth = content.length * height * 0.6
|
|
249
|
+
// text-anchor="middle" is used everywhere in DIMENSION rendering
|
|
250
|
+
bbox.expandByPoint({ x: x - textWidth / 2, y: y - height })
|
|
251
|
+
bbox.expandByPoint({ x: x + textWidth / 2, y: y + height })
|
|
252
|
+
}
|
|
253
|
+
|
|
9
254
|
/**
|
|
10
255
|
* Convert DXF color number to SVG color string
|
|
11
256
|
*/
|
|
@@ -53,26 +298,150 @@ function getDimensionColors(dimStyle?: DimStyleTable): {
|
|
|
53
298
|
export default function dimensionToSVG(
|
|
54
299
|
entity: DimensionEntity,
|
|
55
300
|
dimStyle?: DimStyleTable,
|
|
301
|
+
options?: ToSVGOptions,
|
|
302
|
+
viewport?: DimensionViewport,
|
|
56
303
|
): BoundsAndElement {
|
|
57
304
|
// Dispatch to appropriate renderer based on dimension type
|
|
58
305
|
switch (entity.dimensionType) {
|
|
59
306
|
case 0: // Rotated, horizontal, or vertical
|
|
60
307
|
case 1: // Aligned
|
|
61
|
-
return renderLinearDimension(entity, dimStyle)
|
|
308
|
+
return renderLinearDimension(entity, dimStyle, options, viewport)
|
|
62
309
|
case 2: // Angular
|
|
63
|
-
return renderAngularDimension(entity, dimStyle)
|
|
310
|
+
return renderAngularDimension(entity, dimStyle, options, viewport)
|
|
311
|
+
case 5: // Angular 3-point
|
|
312
|
+
return renderAngular3PointDimension(entity, dimStyle, options, viewport)
|
|
64
313
|
case 3: // Diameter
|
|
65
|
-
return renderDiameterDimension(entity, dimStyle)
|
|
314
|
+
return renderDiameterDimension(entity, dimStyle, options, viewport)
|
|
66
315
|
case 4: // Radius
|
|
67
|
-
return renderRadialDimension(entity, dimStyle)
|
|
316
|
+
return renderRadialDimension(entity, dimStyle, options, viewport)
|
|
68
317
|
case 6: // Ordinate
|
|
69
|
-
return renderOrdinateDimension(entity, dimStyle)
|
|
318
|
+
return renderOrdinateDimension(entity, dimStyle, options, viewport)
|
|
70
319
|
default:
|
|
71
320
|
// Fallback to simple line rendering
|
|
72
321
|
return renderFallbackDimension(entity)
|
|
73
322
|
}
|
|
74
323
|
}
|
|
75
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Render angular 3-point dimension (type 5).
|
|
327
|
+
*
|
|
328
|
+
* Based on DXF reference + ezdxf: angle is measured from p1 to p2
|
|
329
|
+
* counter-clockwise around the vertex.
|
|
330
|
+
*/
|
|
331
|
+
function renderAngular3PointDimension(
|
|
332
|
+
entity: DimensionEntity,
|
|
333
|
+
dimStyle?: DimStyleTable,
|
|
334
|
+
options?: ToSVGOptions,
|
|
335
|
+
viewport?: DimensionViewport,
|
|
336
|
+
): BoundsAndElement {
|
|
337
|
+
const bbox = new Box2()
|
|
338
|
+
const elements: string[] = []
|
|
339
|
+
const markers: string[] = []
|
|
340
|
+
|
|
341
|
+
const { arrowSize, textHeight } = getScaledDimensionSizes(dimStyle, options, viewport)
|
|
342
|
+
const { dimLineColor, extLineColor, textColor, dimLineWeight, extLineWeight } = getDimensionColors(dimStyle)
|
|
343
|
+
|
|
344
|
+
const vertexX = entity.angleVertex?.x ?? 0
|
|
345
|
+
const vertexY = entity.angleVertex?.y ?? 0
|
|
346
|
+
const x1 = entity.measureStart?.x ?? 0
|
|
347
|
+
const y1 = entity.measureStart?.y ?? 0
|
|
348
|
+
const x2 = entity.measureEnd?.x ?? 0
|
|
349
|
+
const y2 = entity.measureEnd?.y ?? 0
|
|
350
|
+
|
|
351
|
+
// DXF reference: point (10,20,30) specifies the dimension line arc location.
|
|
352
|
+
// In practice, ezdxf may also provide (16,26,36); prefer arcPoint only if it
|
|
353
|
+
// yields a meaningful radius away from the vertex.
|
|
354
|
+
const startArcX = entity.start?.x ?? 0
|
|
355
|
+
const startArcY = entity.start?.y ?? 0
|
|
356
|
+
const arcPointX = entity.arcPoint?.x
|
|
357
|
+
const arcPointY = entity.arcPoint?.y
|
|
358
|
+
|
|
359
|
+
const arcPointRadius =
|
|
360
|
+
Number.isFinite(arcPointX) && Number.isFinite(arcPointY)
|
|
361
|
+
? Math.hypot((arcPointX as number) - vertexX, (arcPointY as number) - vertexY)
|
|
362
|
+
: Number.NaN
|
|
363
|
+
|
|
364
|
+
const useArcPoint = Number.isFinite(arcPointRadius) && arcPointRadius > 1e-9
|
|
365
|
+
const arcLocationX = useArcPoint ? (arcPointX as number) : startArcX
|
|
366
|
+
const arcLocationY = useArcPoint ? (arcPointY as number) : startArcY
|
|
367
|
+
|
|
368
|
+
const textX = entity.textMidpoint?.x ?? arcLocationX
|
|
369
|
+
const textY = entity.textMidpoint?.y ?? arcLocationY
|
|
370
|
+
|
|
371
|
+
bbox.expandByPoint({ x: vertexX, y: vertexY })
|
|
372
|
+
bbox.expandByPoint({ x: x1, y: y1 })
|
|
373
|
+
bbox.expandByPoint({ x: x2, y: y2 })
|
|
374
|
+
bbox.expandByPoint({ x: arcLocationX, y: arcLocationY })
|
|
375
|
+
bbox.expandByPoint({ x: textX, y: textY })
|
|
376
|
+
|
|
377
|
+
const a1 = Math.atan2(y1 - vertexY, x1 - vertexX)
|
|
378
|
+
const a2 = Math.atan2(y2 - vertexY, x2 - vertexX)
|
|
379
|
+
|
|
380
|
+
let radius = Math.hypot(arcLocationX - vertexX, arcLocationY - vertexY)
|
|
381
|
+
if (!Number.isFinite(radius) || radius <= 1e-9) {
|
|
382
|
+
radius = Math.hypot(textX - vertexX, textY - vertexY)
|
|
383
|
+
}
|
|
384
|
+
if (!Number.isFinite(radius) || radius <= 1e-9) {
|
|
385
|
+
radius = Math.max(
|
|
386
|
+
Math.hypot(x1 - vertexX, y1 - vertexY),
|
|
387
|
+
Math.hypot(x2 - vertexX, y2 - vertexY),
|
|
388
|
+
)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const arcStartX = vertexX + radius * Math.cos(a1)
|
|
392
|
+
const arcStartY = vertexY + radius * Math.sin(a1)
|
|
393
|
+
const arcEndX = vertexX + radius * Math.cos(a2)
|
|
394
|
+
const arcEndY = vertexY + radius * Math.sin(a2)
|
|
395
|
+
|
|
396
|
+
bbox.expandByPoint({ x: arcStartX, y: arcStartY })
|
|
397
|
+
bbox.expandByPoint({ x: arcEndX, y: arcEndY })
|
|
398
|
+
|
|
399
|
+
// Create arrow markers
|
|
400
|
+
const markerId1 = `dim-angular-3p-arrow-start-${Date.now()}`
|
|
401
|
+
const markerId2 = `dim-angular-3p-arrow-end-${Date.now()}`
|
|
402
|
+
markers.push(
|
|
403
|
+
createArrowMarker(markerId1, arrowSize, dimLineColor, 'backward'),
|
|
404
|
+
createArrowMarker(markerId2, arrowSize, dimLineColor, 'forward'),
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
// Extension lines from definition points to arc endpoints.
|
|
408
|
+
elements.push(
|
|
409
|
+
`<line x1="${x1}" y1="${y1}" x2="${arcStartX}" y2="${arcStartY}" stroke="${extLineColor}" stroke-width="${extLineWeight}" />`,
|
|
410
|
+
`<line x1="${x2}" y1="${y2}" x2="${arcEndX}" y2="${arcEndY}" stroke="${extLineColor}" stroke-width="${extLineWeight}" />`,
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
// Arc from a1 to a2 in CCW orientation.
|
|
414
|
+
let delta = a2 - a1
|
|
415
|
+
while (delta < 0) delta += Math.PI * 2
|
|
416
|
+
while (delta >= Math.PI * 2) delta -= Math.PI * 2
|
|
417
|
+
const largeArcFlag = delta > Math.PI ? 1 : 0
|
|
418
|
+
const sweepFlag = 1
|
|
419
|
+
|
|
420
|
+
expandBBoxForMarker(bbox, arcStartX, arcStartY, arrowSize)
|
|
421
|
+
expandBBoxForMarker(bbox, arcEndX, arcEndY, arrowSize)
|
|
422
|
+
|
|
423
|
+
elements.push(
|
|
424
|
+
`<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})" />`,
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
const resolvedText = resolveDimensionText(entity)
|
|
428
|
+
if (resolvedText) {
|
|
429
|
+
const midAngle = a1 + delta / 2
|
|
430
|
+
const textRotation = (midAngle * 180) / Math.PI
|
|
431
|
+
|
|
432
|
+
expandBBoxForText(bbox, textX, textY, textHeight, resolvedText)
|
|
433
|
+
|
|
434
|
+
elements.push(
|
|
435
|
+
`<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>`,
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return {
|
|
440
|
+
bbox,
|
|
441
|
+
element: `<defs>${markers.join('')}</defs><g>${elements.join('')}</g>`,
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
76
445
|
/**
|
|
77
446
|
* Create SVG marker definition for dimension arrows
|
|
78
447
|
*/
|
|
@@ -80,9 +449,14 @@ export function createArrowMarker(
|
|
|
80
449
|
id: string,
|
|
81
450
|
size: number,
|
|
82
451
|
color: string,
|
|
452
|
+
direction: 'forward' | 'backward' = 'forward',
|
|
83
453
|
): string {
|
|
84
|
-
const arrowPath =
|
|
85
|
-
|
|
454
|
+
const arrowPath = direction === 'forward'
|
|
455
|
+
? `M 0 0 L ${size} ${size / 2} L 0 ${size} z`
|
|
456
|
+
: `M ${size} 0 L 0 ${size / 2} L ${size} ${size} z`
|
|
457
|
+
const refX = direction === 'forward' ? size : 0
|
|
458
|
+
|
|
459
|
+
return `<marker id="${id}" markerWidth="${size}" markerHeight="${size}" refX="${refX}" refY="${size / 2}" orient="auto" markerUnits="userSpaceOnUse">
|
|
86
460
|
<path d="${arrowPath}" fill="${color}" />
|
|
87
461
|
</marker>`
|
|
88
462
|
}
|
|
@@ -93,16 +467,16 @@ export function createArrowMarker(
|
|
|
93
467
|
function renderLinearDimension(
|
|
94
468
|
entity: DimensionEntity,
|
|
95
469
|
dimStyle?: DimStyleTable,
|
|
470
|
+
options?: ToSVGOptions,
|
|
471
|
+
viewport?: DimensionViewport,
|
|
96
472
|
): BoundsAndElement {
|
|
97
473
|
const bbox = new Box2()
|
|
98
474
|
const elements: string[] = []
|
|
99
475
|
const markers: string[] = []
|
|
100
476
|
|
|
101
|
-
// Get dimension style properties with defaults
|
|
102
|
-
const arrowSize
|
|
103
|
-
|
|
104
|
-
const extLineOffset = dimStyle?.dimExo ?? 0.625
|
|
105
|
-
const extLineExtension = dimStyle?.dimExe ?? 1.25
|
|
477
|
+
// Get dimension style properties with defaults (optionally auto-scaled)
|
|
478
|
+
const { arrowSize, textHeight, extLineOffset, extLineExtension } =
|
|
479
|
+
getScaledDimensionSizes(dimStyle, options, viewport)
|
|
106
480
|
const { dimLineColor, extLineColor, textColor, dimLineWeight, extLineWeight } = getDimensionColors(dimStyle)
|
|
107
481
|
|
|
108
482
|
// Extract dimension geometry
|
|
@@ -137,8 +511,8 @@ function renderLinearDimension(
|
|
|
137
511
|
|
|
138
512
|
// Create arrow markers with dimension line color
|
|
139
513
|
markers.push(
|
|
140
|
-
createArrowMarker(markerId1, arrowSize, dimLineColor),
|
|
141
|
-
createArrowMarker(markerId2, arrowSize, dimLineColor),
|
|
514
|
+
createArrowMarker(markerId1, arrowSize, dimLineColor, 'backward'),
|
|
515
|
+
createArrowMarker(markerId2, arrowSize, dimLineColor, 'forward'),
|
|
142
516
|
)
|
|
143
517
|
|
|
144
518
|
// Draw extension lines
|
|
@@ -152,6 +526,14 @@ function renderLinearDimension(
|
|
|
152
526
|
const extLine2EndX = dimLine2X + Math.cos(perpAngle) * extLineExtension
|
|
153
527
|
const extLine2EndY = dimLine2Y + Math.sin(perpAngle) * extLineExtension
|
|
154
528
|
|
|
529
|
+
// Expand bounding box to include full extension lines and arrow markers
|
|
530
|
+
bbox.expandByPoint({ x: extLine1StartX, y: extLine1StartY })
|
|
531
|
+
bbox.expandByPoint({ x: extLine1EndX, y: extLine1EndY })
|
|
532
|
+
bbox.expandByPoint({ x: extLine2StartX, y: extLine2StartY })
|
|
533
|
+
bbox.expandByPoint({ x: extLine2EndX, y: extLine2EndY })
|
|
534
|
+
expandBBoxForMarker(bbox, dimLine1X, dimLine1Y, arrowSize)
|
|
535
|
+
expandBBoxForMarker(bbox, dimLine2X, dimLine2Y, arrowSize)
|
|
536
|
+
|
|
155
537
|
elements.push(
|
|
156
538
|
`<line x1="${extLine1StartX}" y1="${extLine1StartY}" x2="${extLine1EndX}" y2="${extLine1EndY}" stroke="${extLineColor}" stroke-width="${extLineWeight}" />`,
|
|
157
539
|
`<line x1="${extLine2StartX}" y1="${extLine2StartY}" x2="${extLine2EndX}" y2="${extLine2EndY}" stroke="${extLineColor}" stroke-width="${extLineWeight}" />`,
|
|
@@ -159,10 +541,12 @@ function renderLinearDimension(
|
|
|
159
541
|
)
|
|
160
542
|
|
|
161
543
|
// Add dimension text
|
|
162
|
-
|
|
544
|
+
const resolvedText = resolveDimensionText(entity)
|
|
545
|
+
if (resolvedText) {
|
|
163
546
|
const textRotation = (angle * 180) / Math.PI
|
|
547
|
+
expandBBoxForText(bbox, textX, textY, textHeight, resolvedText)
|
|
164
548
|
elements.push(
|
|
165
|
-
`<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})">${
|
|
549
|
+
`<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>`,
|
|
166
550
|
)
|
|
167
551
|
}
|
|
168
552
|
|
|
@@ -178,14 +562,15 @@ function renderLinearDimension(
|
|
|
178
562
|
function renderAngularDimension(
|
|
179
563
|
entity: DimensionEntity,
|
|
180
564
|
dimStyle?: DimStyleTable,
|
|
565
|
+
options?: ToSVGOptions,
|
|
566
|
+
viewport?: DimensionViewport,
|
|
181
567
|
): BoundsAndElement {
|
|
182
568
|
const bbox = new Box2()
|
|
183
569
|
const elements: string[] = []
|
|
184
570
|
const markers: string[] = []
|
|
185
571
|
|
|
186
|
-
// Get dimension style properties
|
|
187
|
-
const arrowSize = dimStyle
|
|
188
|
-
const textHeight = dimStyle?.dimTxt ?? 2.5
|
|
572
|
+
// Get dimension style properties (optionally auto-scaled)
|
|
573
|
+
const { arrowSize, textHeight } = getScaledDimensionSizes(dimStyle, options, viewport)
|
|
189
574
|
const { dimLineColor, extLineColor, textColor, dimLineWeight, extLineWeight } = getDimensionColors(dimStyle)
|
|
190
575
|
|
|
191
576
|
// Extract points
|
|
@@ -207,8 +592,8 @@ function renderAngularDimension(
|
|
|
207
592
|
const markerId1 = `dim-angular-arrow-start-${Date.now()}`
|
|
208
593
|
const markerId2 = `dim-angular-arrow-end-${Date.now()}`
|
|
209
594
|
markers.push(
|
|
210
|
-
createArrowMarker(markerId1, arrowSize, dimLineColor),
|
|
211
|
-
createArrowMarker(markerId2, arrowSize, dimLineColor),
|
|
595
|
+
createArrowMarker(markerId1, arrowSize, dimLineColor, 'backward'),
|
|
596
|
+
createArrowMarker(markerId2, arrowSize, dimLineColor, 'forward'),
|
|
212
597
|
)
|
|
213
598
|
|
|
214
599
|
// Draw extension lines from center to definition points
|
|
@@ -234,12 +619,15 @@ function renderAngularDimension(
|
|
|
234
619
|
)
|
|
235
620
|
|
|
236
621
|
// Add dimension text
|
|
237
|
-
|
|
622
|
+
const resolvedText = resolveDimensionText(entity)
|
|
623
|
+
if (resolvedText) {
|
|
238
624
|
const midAngle = (startAngle + endAngle) / 2
|
|
239
625
|
const textRotation = (midAngle * 180) / Math.PI
|
|
240
626
|
|
|
627
|
+
expandBBoxForText(bbox, textX, textY, textHeight, resolvedText)
|
|
628
|
+
|
|
241
629
|
elements.push(
|
|
242
|
-
`<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})">${
|
|
630
|
+
`<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>`,
|
|
243
631
|
)
|
|
244
632
|
}
|
|
245
633
|
|
|
@@ -255,14 +643,15 @@ function renderAngularDimension(
|
|
|
255
643
|
function renderDiameterDimension(
|
|
256
644
|
entity: DimensionEntity,
|
|
257
645
|
dimStyle?: DimStyleTable,
|
|
646
|
+
options?: ToSVGOptions,
|
|
647
|
+
viewport?: DimensionViewport,
|
|
258
648
|
): BoundsAndElement {
|
|
259
649
|
const bbox = new Box2()
|
|
260
650
|
const elements: string[] = []
|
|
261
651
|
const markers: string[] = []
|
|
262
652
|
|
|
263
|
-
// Get dimension style properties
|
|
264
|
-
const arrowSize = dimStyle
|
|
265
|
-
const textHeight = dimStyle?.dimTxt ?? 2.5
|
|
653
|
+
// Get dimension style properties (optionally auto-scaled)
|
|
654
|
+
const { arrowSize, textHeight } = getScaledDimensionSizes(dimStyle, options, viewport)
|
|
266
655
|
const { dimLineColor, textColor, dimLineWeight } = getDimensionColors(dimStyle)
|
|
267
656
|
|
|
268
657
|
// Extract geometry
|
|
@@ -277,22 +666,30 @@ function renderDiameterDimension(
|
|
|
277
666
|
bbox.expandByPoint({ x: x2, y: y2 })
|
|
278
667
|
bbox.expandByPoint({ x: textX, y: textY })
|
|
279
668
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
669
|
+
const diameterLen = Math.hypot(x2 - x1, y2 - y1)
|
|
670
|
+
if (Number.isFinite(diameterLen) && diameterLen > 1e-6) {
|
|
671
|
+
// Create arrow markers
|
|
672
|
+
const markerId = `dim-diameter-arrow-${Date.now()}`
|
|
673
|
+
markers.push(createArrowMarker(markerId, arrowSize, dimLineColor, 'backward'))
|
|
283
674
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
675
|
+
// Create diameter line with arrow at the end
|
|
676
|
+
elements.push(
|
|
677
|
+
`<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${dimLineColor}" stroke-width="${dimLineWeight}" marker-end="url(#${markerId})" />`,
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
expandBBoxForMarker(bbox, x2, y2, arrowSize)
|
|
681
|
+
}
|
|
288
682
|
|
|
289
683
|
// Add dimension text with diameter symbol
|
|
290
|
-
const
|
|
684
|
+
const resolvedText = resolveDimensionText(entity)
|
|
685
|
+
const diameterText = resolvedText ? `⌀${resolvedText}` : '⌀'
|
|
291
686
|
const angle = Math.atan2(y2 - y1, x2 - x1)
|
|
292
687
|
const textRotation = (angle * 180) / Math.PI
|
|
293
688
|
|
|
689
|
+
expandBBoxForText(bbox, textX, textY, textHeight, diameterText)
|
|
690
|
+
|
|
294
691
|
elements.push(
|
|
295
|
-
`<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>`,
|
|
692
|
+
`<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>`,
|
|
296
693
|
)
|
|
297
694
|
|
|
298
695
|
return {
|
|
@@ -307,14 +704,15 @@ function renderDiameterDimension(
|
|
|
307
704
|
function renderRadialDimension(
|
|
308
705
|
entity: DimensionEntity,
|
|
309
706
|
dimStyle?: DimStyleTable,
|
|
707
|
+
options?: ToSVGOptions,
|
|
708
|
+
viewport?: DimensionViewport,
|
|
310
709
|
): BoundsAndElement {
|
|
311
710
|
const bbox = new Box2()
|
|
312
711
|
const elements: string[] = []
|
|
313
712
|
const markers: string[] = []
|
|
314
713
|
|
|
315
|
-
// Get dimension style properties
|
|
316
|
-
const arrowSize = dimStyle
|
|
317
|
-
const textHeight = dimStyle?.dimTxt ?? 2.5
|
|
714
|
+
// Get dimension style properties (optionally auto-scaled)
|
|
715
|
+
const { arrowSize, textHeight } = getScaledDimensionSizes(dimStyle, options, viewport)
|
|
318
716
|
const { dimLineColor, textColor, dimLineWeight } = getDimensionColors(dimStyle)
|
|
319
717
|
|
|
320
718
|
// Extract geometry
|
|
@@ -329,22 +727,30 @@ function renderRadialDimension(
|
|
|
329
727
|
bbox.expandByPoint({ x: x2, y: y2 })
|
|
330
728
|
bbox.expandByPoint({ x: textX, y: textY })
|
|
331
729
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
730
|
+
const radiusLen = Math.hypot(x2 - x1, y2 - y1)
|
|
731
|
+
if (Number.isFinite(radiusLen) && radiusLen > 1e-6) {
|
|
732
|
+
// Create arrow markers
|
|
733
|
+
const markerId = `dim-radius-arrow-${Date.now()}`
|
|
734
|
+
markers.push(createArrowMarker(markerId, arrowSize, dimLineColor, 'backward'))
|
|
335
735
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
736
|
+
// Create radius line with arrow at the end
|
|
737
|
+
elements.push(
|
|
738
|
+
`<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${dimLineColor}" stroke-width="${dimLineWeight}" marker-end="url(#${markerId})" />`,
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
expandBBoxForMarker(bbox, x2, y2, arrowSize)
|
|
742
|
+
}
|
|
340
743
|
|
|
341
744
|
// Add dimension text with radius symbol
|
|
342
|
-
const
|
|
745
|
+
const resolvedText = resolveDimensionText(entity)
|
|
746
|
+
const radiusText = resolvedText ? `R${resolvedText}` : 'R'
|
|
343
747
|
const angle = Math.atan2(y2 - y1, x2 - x1)
|
|
344
748
|
const textRotation = (angle * 180) / Math.PI
|
|
345
749
|
|
|
750
|
+
expandBBoxForText(bbox, textX, textY, textHeight, radiusText)
|
|
751
|
+
|
|
346
752
|
elements.push(
|
|
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})">${radiusText}</text>`,
|
|
753
|
+
`<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>`,
|
|
348
754
|
)
|
|
349
755
|
|
|
350
756
|
return {
|
|
@@ -359,12 +765,14 @@ function renderRadialDimension(
|
|
|
359
765
|
function renderOrdinateDimension(
|
|
360
766
|
entity: DimensionEntity,
|
|
361
767
|
dimStyle?: DimStyleTable,
|
|
768
|
+
options?: ToSVGOptions,
|
|
769
|
+
viewport?: DimensionViewport,
|
|
362
770
|
): BoundsAndElement {
|
|
363
771
|
const bbox = new Box2()
|
|
364
772
|
const elements: string[] = []
|
|
365
773
|
|
|
366
|
-
// Get dimension style properties
|
|
367
|
-
const textHeight = dimStyle
|
|
774
|
+
// Get dimension style properties (optionally auto-scaled)
|
|
775
|
+
const { textHeight } = getScaledDimensionSizes(dimStyle, options, viewport)
|
|
368
776
|
const { dimLineColor, textColor, dimLineWeight } = getDimensionColors(dimStyle)
|
|
369
777
|
|
|
370
778
|
// Extract geometry
|
|
@@ -385,12 +793,15 @@ function renderOrdinateDimension(
|
|
|
385
793
|
)
|
|
386
794
|
|
|
387
795
|
// Add dimension text
|
|
388
|
-
|
|
796
|
+
const resolvedText = resolveDimensionText(entity)
|
|
797
|
+
if (resolvedText) {
|
|
389
798
|
const angle = Math.atan2(y2 - y1, x2 - x1)
|
|
390
799
|
const textRotation = (angle * 180) / Math.PI
|
|
391
800
|
|
|
801
|
+
expandBBoxForText(bbox, textX, textY, textHeight, resolvedText)
|
|
802
|
+
|
|
392
803
|
elements.push(
|
|
393
|
-
`<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})">${
|
|
804
|
+
`<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>`,
|
|
394
805
|
)
|
|
395
806
|
}
|
|
396
807
|
|
|
@@ -413,9 +824,10 @@ function renderFallbackDimension(entity: DimensionEntity): BoundsAndElement {
|
|
|
413
824
|
const textY = entity.textMidpoint.y ?? 0
|
|
414
825
|
bbox.expandByPoint({ x: textX, y: textY })
|
|
415
826
|
|
|
416
|
-
|
|
827
|
+
const resolvedText = resolveDimensionText(entity)
|
|
828
|
+
if (resolvedText) {
|
|
417
829
|
elements.push(
|
|
418
|
-
`<text x="${textX}" y="${textY}" font-size="2.5" text-anchor="middle" transform="scale(1,-1) translate(0 ${-2 * textY})">${
|
|
830
|
+
`<text x="${textX}" y="${textY}" font-size="2.5" text-anchor="middle" transform="scale(1,-1) translate(0 ${-2 * textY})">${escapeXmlText(resolvedText)}</text>`,
|
|
419
831
|
)
|
|
420
832
|
}
|
|
421
833
|
}
|