@mlightcad/data-model 1.7.34 → 1.7.36

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 (39) hide show
  1. package/dist/data-model.cjs +6 -6
  2. package/dist/data-model.js +3174 -2708
  3. package/lib/converter/AcDbDxfConverter.d.ts.map +1 -1
  4. package/lib/converter/AcDbDxfConverter.js +20 -9
  5. package/lib/converter/AcDbDxfConverter.js.map +1 -1
  6. package/lib/converter/AcDbEntitiyConverter.d.ts.map +1 -1
  7. package/lib/converter/AcDbEntitiyConverter.js +3 -7
  8. package/lib/converter/AcDbEntitiyConverter.js.map +1 -1
  9. package/lib/database/AcDbDatabase.d.ts +127 -11
  10. package/lib/database/AcDbDatabase.d.ts.map +1 -1
  11. package/lib/database/AcDbDatabase.js +200 -18
  12. package/lib/database/AcDbDatabase.js.map +1 -1
  13. package/lib/database/AcDbSysVarManager.d.ts +1 -0
  14. package/lib/database/AcDbSysVarManager.d.ts.map +1 -1
  15. package/lib/database/AcDbSysVarManager.js +126 -15
  16. package/lib/database/AcDbSysVarManager.js.map +1 -1
  17. package/lib/database/AcDbSystemVariables.d.ts +25 -9
  18. package/lib/database/AcDbSystemVariables.d.ts.map +1 -1
  19. package/lib/database/AcDbSystemVariables.js +24 -8
  20. package/lib/database/AcDbSystemVariables.js.map +1 -1
  21. package/lib/misc/AcDbConstants.d.ts +26 -0
  22. package/lib/misc/AcDbConstants.d.ts.map +1 -1
  23. package/lib/misc/AcDbConstants.js +9 -0
  24. package/lib/misc/AcDbConstants.js.map +1 -1
  25. package/lib/misc/AcDbFormatter.d.ts +215 -0
  26. package/lib/misc/AcDbFormatter.d.ts.map +1 -0
  27. package/lib/misc/AcDbFormatter.js +863 -0
  28. package/lib/misc/AcDbFormatter.js.map +1 -0
  29. package/lib/misc/AcDbLinearUnits.d.ts +21 -0
  30. package/lib/misc/AcDbLinearUnits.d.ts.map +1 -0
  31. package/lib/misc/AcDbLinearUnits.js +22 -0
  32. package/lib/misc/AcDbLinearUnits.js.map +1 -0
  33. package/lib/misc/index.d.ts +2 -0
  34. package/lib/misc/index.d.ts.map +1 -1
  35. package/lib/misc/index.js +2 -0
  36. package/lib/misc/index.js.map +1 -1
  37. package/lib/misc/pat/AcDbPatSvgRenderer.d.ts.map +1 -1
  38. package/lib/misc/pat/AcDbPatSvgRenderer.js.map +1 -1
  39. package/package.json +4 -4
@@ -0,0 +1,863 @@
1
+ var __read = (this && this.__read) || function (o, n) {
2
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
3
+ if (!m) return o;
4
+ var i = m.call(o), r, ar = [], e;
5
+ try {
6
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
7
+ }
8
+ catch (error) { e = { error: error }; }
9
+ finally {
10
+ try {
11
+ if (r && !r.done && (m = i["return"])) m.call(i);
12
+ }
13
+ finally { if (e) throw e.error; }
14
+ }
15
+ return ar;
16
+ };
17
+ import { AcGeMathUtil } from '@mlightcad/geometry-engine';
18
+ import { AcDbAngleUnits } from './AcDbAngleUnits';
19
+ import { AcDbLinearUnits } from './AcDbLinearUnits';
20
+ import { AcDbUnitsValue, isImperialUnits, isMetricUnits } from './AcDbUnitsValue';
21
+ /** Radians per gradian (400 grads = full circle). */
22
+ var RAD_PER_GRAD = Math.PI / 200;
23
+ /**
24
+ * Formats linear distances, point coordinates, and angles for display in the UI,
25
+ * matching AutoCAD drawing-unit behavior as closely as the supported system variables allow.
26
+ *
27
+ * An instance is bound to one {@link AcDbDatabase}. On each call it reads the current values of
28
+ * the relevant header/system variables from that database (not a snapshot taken at construction).
29
+ *
30
+ * ### Linear distances and coordinates
31
+ *
32
+ * | Variable | Role |
33
+ * |----------|------|
34
+ * | **LUNITS** | Display format (scientific, decimal, engineering, …); see {@link AcDbDatabase.lunits} |
35
+ * | **LUPREC** | Precision (decimal places or fractional denominator); see {@link AcDbDatabase.luprec} |
36
+ * | **INSUNITS** | Unit suffix when {@link AcDbFormatterOptions.showUnits} is `true`; see {@link AcDbDatabase.insunits} |
37
+ * | **UNITMODE** | Report vs input delimiters for feet-inch and fractions; see {@link AcDbDatabase.unitmode} |
38
+ * | **MEASUREMENT** | Imperial vs metric suffix when **INSUNITS** is unitless; see {@link AcDbDatabase.measurement} |
39
+ *
40
+ * Engineering (**LUNITS** = 3) and architectural (**LUNITS** = 4) formats treat the numeric
41
+ * value as **inches**, consistent with AutoCAD.
42
+ *
43
+ * ### Angles
44
+ *
45
+ * Input angles are **radians in WCS**. Before formatting, the value is adjusted by
46
+ * {@link AcDbDatabase.angbase | ANGBASE} and {@link AcDbDatabase.angdir | ANGDIR}, then converted
47
+ * per {@link AcDbDatabase.aunits | AUNITS} and {@link AcDbDatabase.auprec | AUPREC}.
48
+ *
49
+ * ### Out of scope
50
+ *
51
+ * Dimension entity text uses per-style **DIM\*** variables (for example **DIMLUNIT**, **DIMDEC**),
52
+ * not this class. **SNAPANG** and UCS settings affect snapping/input, not WCS coordinate strings here.
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * database.lunits = AcDbLinearUnits.Decimal;
57
+ * database.luprec = 2;
58
+ *
59
+ * database.formatter.formatLength(12.3456); // "12.35"
60
+ * database.formatter.formatLength(12.3456, { showUnits: true }); // "12.35 mm" (with INSUNITS = mm)
61
+ * database.formatter.formatPoint2d(new AcGePoint2d(1, 2)); // "1, 2"
62
+ * database.formatter.formatAngle(Math.PI / 4, { showUnits: true }); // "45°"
63
+ * ```
64
+ */
65
+ var AcDbFormatter = /** @class */ (function () {
66
+ /**
67
+ * Creates a formatter that reads unit settings from the given database.
68
+ *
69
+ * @param database - Drawing database whose **LUNITS**, **AUNITS**, and related variables
70
+ * are consulted on every format call.
71
+ */
72
+ function AcDbFormatter(database) {
73
+ this.database = database;
74
+ }
75
+ /**
76
+ * Formats a single linear distance in **drawing units**.
77
+ *
78
+ * The output pattern follows {@link AcDbDatabase.lunits | LUNITS} and
79
+ * {@link AcDbDatabase.luprec | LUPREC}. When {@link AcDbFormatterOptions.showUnits} is `true`,
80
+ * a suffix may be appended based on {@link AcDbDatabase.insunits | INSUNITS} and
81
+ * {@link AcDbDatabase.measurement | MEASUREMENT}.
82
+ *
83
+ * | **LUNITS** | Typical output (no unit suffix) |
84
+ * |-----------:|--------------------------------|
85
+ * | `1` Scientific | `1.23E+02` |
86
+ * | `2` Decimal | `12.35` |
87
+ * | `3` Engineering | `1'-3.5` (value interpreted as inches) |
88
+ * | `4` Architectural | `1'-3 1/2` |
89
+ * | `5` Fractional | `15 1/2` |
90
+ * | `6` Windows desktop | Same as decimal |
91
+ *
92
+ * {@link AcDbDatabase.unitmode | UNITMODE} changes delimiters for engineering, architectural,
93
+ * and fractional modes (for example `1'3-1/2` when **UNITMODE** = 1).
94
+ *
95
+ * @param value - Distance in drawing units (WCS length, not pre-converted to feet or degrees).
96
+ * @param options - Optional display flags; see {@link AcDbFormatterOptions}.
97
+ * @returns Formatted string suitable for status bars, measurement overlays, or tooltips.
98
+ *
99
+ * @remarks
100
+ * Non-finite values (`NaN`, `±Infinity`) are formatted as `"0"`.
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * database.lunits = AcDbLinearUnits.Architectural;
105
+ * database.luprec = 2;
106
+ * formatter.formatLength(15.5); // "1'-3 1/2"
107
+ * ```
108
+ */
109
+ AcDbFormatter.prototype.formatLength = function (value, options) {
110
+ var ctx = this.createContext(options);
111
+ return formatLengthWithContext(value, ctx);
112
+ };
113
+ /**
114
+ * Formats a 2D point as `"x, y"`, formatting each component with {@link formatLength}.
115
+ *
116
+ * Both coordinates use the same {@link AcDbDatabase.lunits | LUNITS} / **LUPREC** rules and the
117
+ * same {@link AcDbFormatterOptions} for the call. The separator is a comma followed by a space
118
+ * (`, `), which matches common Cartesian display in measurement UIs.
119
+ *
120
+ * @param point - Point in drawing units (WCS **x** / **y**).
121
+ * @param options - Optional display flags applied to each coordinate.
122
+ * @returns Formatted coordinate pair, for example `"1.2, 4.6"` or `"1'-0, 2'-6"` depending on **LUNITS**.
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * database.lunits = AcDbLinearUnits.Decimal;
127
+ * database.luprec = 1;
128
+ * formatter.formatPoint2d(new AcGePoint2d(1.23, 4.56)); // "1.2, 4.6"
129
+ * ```
130
+ */
131
+ AcDbFormatter.prototype.formatPoint2d = function (point, options) {
132
+ var ctx = this.createContext(options);
133
+ return "".concat(formatLengthWithContext(point.x, ctx), ", ").concat(formatLengthWithContext(point.y, ctx));
134
+ };
135
+ /**
136
+ * Formats a 3D point as `"x, y, z"`, formatting each component with {@link formatLength}.
137
+ *
138
+ * Behavior is the same as {@link formatPoint2d} for **x** and **y**, with **z** appended using
139
+ * the same linear formatting and options.
140
+ *
141
+ * @param point - Point in drawing units (WCS **x** / **y** / **z**).
142
+ * @param options - Optional display flags applied to each coordinate.
143
+ * @returns Formatted coordinate triple, for example `"1.2, 4.6, 7.9"`.
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * formatter.formatPoint3d(new AcGePoint3d(1, 2, 3)); // "1, 2, 3" (with default decimal LUNITS)
148
+ * ```
149
+ */
150
+ AcDbFormatter.prototype.formatPoint3d = function (point, options) {
151
+ var ctx = this.createContext(options);
152
+ return "".concat(formatLengthWithContext(point.x, ctx), ", ").concat(formatLengthWithContext(point.y, ctx), ", ").concat(formatLengthWithContext(point.z, ctx));
153
+ };
154
+ /**
155
+ * Formats an angle given in **radians** (WCS), for display according to the drawing angle units.
156
+ *
157
+ * Processing order:
158
+ * 1. Subtract {@link AcDbDatabase.angbase | ANGBASE} (zero direction).
159
+ * 2. Negate if {@link AcDbDatabase.angdir | ANGDIR} = 1 (clockwise positive).
160
+ * 3. Normalize to \([0, 2\pi)\) via {@link AcGeMathUtil.normalizeAngle}.
161
+ * 4. Format using {@link AcDbDatabase.aunits | AUNITS} and {@link AcDbDatabase.auprec | AUPREC}.
162
+ *
163
+ * | **AUNITS** | Typical output (no unit suffix) |
164
+ * |-----------:|--------------------------------|
165
+ * | `0` Decimal degrees | `45` or `45.5` |
166
+ * | `1` Degrees/minutes/seconds | `45d30'15"` |
167
+ * | `2` Gradians | `50` |
168
+ * | `3` Radians | `0.785` |
169
+ * | `4` Surveyor's | `N 45d30'15" E` |
170
+ *
171
+ * @param radians - Angle in radians. When {@link AcDbFormatterOptions.applyAngbaseAngdir} is
172
+ * `true` (default), this is an absolute WCS angle (counterclockwise from +X before
173
+ * **ANGBASE** / **ANGDIR** adjustment). When `false`, this is the scalar angle to display.
174
+ * @param options - Optional display flags; when {@link AcDbFormatterOptions.showUnits} is `true`,
175
+ * unit markers such as `°`, `g`, or `rad` may be appended.
176
+ * @returns Formatted angle string.
177
+ *
178
+ * @remarks
179
+ * For included or relative angles, pass {@link AcDbFormatterOptions.applyAngbaseAngdir} = `false`.
180
+ * For arc sweep or dimension overrides, supply the appropriate radians value and flags.
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * database.aunits = AcDbAngleUnits.DecimalDegrees;
185
+ * database.auprec = 0;
186
+ * formatter.formatAngle(Math.PI / 4); // "45"
187
+ * formatter.formatAngle(Math.PI / 4, { showUnits: true }); // "45°"
188
+ * ```
189
+ */
190
+ AcDbFormatter.prototype.formatAngle = function (radians, options) {
191
+ var _a;
192
+ var ctx = this.createContext(options);
193
+ var applyAngbaseAngdir = (_a = options === null || options === void 0 ? void 0 : options.applyAngbaseAngdir) !== null && _a !== void 0 ? _a : true;
194
+ var displayRadians = applyAngbaseAngdir
195
+ ? toDisplayAngleRadians(radians, this.database.angbase, this.database.angdir)
196
+ : AcGeMathUtil.normalizeAngle(radians);
197
+ return formatAngleWithContext(displayRadians, ctx);
198
+ };
199
+ /**
200
+ * Builds the formatting context from the bound database and call options.
201
+ *
202
+ * @param options - Per-call overrides; omitted fields use database defaults.
203
+ * @returns Snapshot of linear and angular unit settings for one format call.
204
+ * @internal
205
+ */
206
+ AcDbFormatter.prototype.createContext = function (options) {
207
+ var _a, _b;
208
+ return {
209
+ lunits: this.database.lunits,
210
+ luprec: clampPrecision(this.database.luprec),
211
+ insunits: this.database.insunits,
212
+ unitmode: this.database.unitmode,
213
+ measurement: this.database.measurement,
214
+ aunits: this.database.aunits,
215
+ auprec: clampPrecision(this.database.auprec),
216
+ showUnits: (_a = options === null || options === void 0 ? void 0 : options.showUnits) !== null && _a !== void 0 ? _a : false,
217
+ showApproximate: (_b = options === null || options === void 0 ? void 0 : options.showApproximate) !== null && _b !== void 0 ? _b : false
218
+ };
219
+ };
220
+ return AcDbFormatter;
221
+ }());
222
+ export { AcDbFormatter };
223
+ /**
224
+ * Formats one linear distance using a pre-built {@link AcDbFormatContext}.
225
+ *
226
+ * @param value - Distance in drawing units.
227
+ * @param ctx - Unit settings snapshot for this call.
228
+ * @returns Formatted linear string.
229
+ * @internal
230
+ */
231
+ function formatLengthWithContext(value, ctx) {
232
+ var formatted;
233
+ switch (ctx.lunits) {
234
+ case AcDbLinearUnits.Scientific:
235
+ formatted = appendLinearUnitSuffix(formatScientific(value, ctx.luprec), ctx);
236
+ break;
237
+ case AcDbLinearUnits.Engineering:
238
+ formatted = formatEngineering(value, ctx);
239
+ break;
240
+ case AcDbLinearUnits.Architectural:
241
+ formatted = formatArchitectural(value, ctx);
242
+ break;
243
+ case AcDbLinearUnits.Fractional:
244
+ formatted = appendLinearUnitSuffix(formatFractionalLinear(value, ctx), ctx);
245
+ break;
246
+ case AcDbLinearUnits.WindowsDesktop:
247
+ case AcDbLinearUnits.Decimal:
248
+ default:
249
+ formatted = appendLinearUnitSuffix(formatDecimal(value, ctx.luprec), ctx);
250
+ }
251
+ return applyApproximatePrefix(formatted, isLinearValueApproximate(value, ctx), ctx);
252
+ }
253
+ /**
254
+ * Formats one angle (already adjusted for **ANGBASE** / **ANGDIR**) using {@link AcDbFormatContext}.
255
+ *
256
+ * @param radians - Display angle in radians, in range \([0, 2\pi)\).
257
+ * @param ctx - Unit settings snapshot for this call.
258
+ * @returns Formatted angle string.
259
+ * @internal
260
+ */
261
+ function formatAngleWithContext(radians, ctx) {
262
+ var formatted;
263
+ switch (ctx.aunits) {
264
+ case AcDbAngleUnits.DegreesMinutesSeconds:
265
+ formatted = formatAngleDms(radians, ctx);
266
+ break;
267
+ case AcDbAngleUnits.Gradians:
268
+ formatted = formatAngleGradians(radians, ctx);
269
+ break;
270
+ case AcDbAngleUnits.Radians:
271
+ formatted = formatAngleRadians(radians, ctx);
272
+ break;
273
+ case AcDbAngleUnits.SurveyorsUnits:
274
+ formatted = formatAngleSurveyor(radians, ctx);
275
+ break;
276
+ case AcDbAngleUnits.DecimalDegrees:
277
+ default:
278
+ formatted = formatAngleDecimalDegrees(radians, ctx);
279
+ }
280
+ return applyApproximatePrefix(formatted, isAngleValueApproximate(radians, ctx), ctx);
281
+ }
282
+ /**
283
+ * Converts a WCS angle to the drawing's display angle before **AUNITS** formatting.
284
+ *
285
+ * @param radians - Raw angle in radians (WCS, counterclockwise from +X).
286
+ * @param angbase - **ANGBASE** zero direction in radians.
287
+ * @param angdir - **ANGDIR** (`0` = CCW positive, `1` = CW positive).
288
+ * @returns Normalized display angle in radians.
289
+ * @internal
290
+ */
291
+ function toDisplayAngleRadians(radians, angbase, angdir) {
292
+ var angle = radians - angbase;
293
+ if (angdir === 1) {
294
+ angle = -angle;
295
+ }
296
+ return AcGeMathUtil.normalizeAngle(angle);
297
+ }
298
+ /**
299
+ * Resolves feet-inch delimiters from **UNITMODE** and {@link AcDbFormatContext.showUnits}.
300
+ *
301
+ * @param ctx - Formatting context.
302
+ * @returns Delimiter pattern for imperial linear modes.
303
+ * @internal
304
+ */
305
+ function imperialDelimiterStyle(ctx) {
306
+ var inchSuffix = ctx.showUnits ? '"' : '';
307
+ if (ctx.unitmode === 1) {
308
+ return {
309
+ feetInchSeparator: '',
310
+ fractionSeparator: '-',
311
+ inchSuffix: inchSuffix
312
+ };
313
+ }
314
+ return {
315
+ feetInchSeparator: '-',
316
+ fractionSeparator: ' ',
317
+ inchSuffix: inchSuffix
318
+ };
319
+ }
320
+ /**
321
+ * Rounds and clamps a precision value to a safe integer range.
322
+ *
323
+ * @param value - Raw precision (for example **LUPREC** or **AUPREC**).
324
+ * @param min - Minimum allowed precision (default `0`).
325
+ * @param max - Maximum allowed precision (default `8`).
326
+ * @returns Clamped integer precision, or `min` when `value` is non-finite.
327
+ * @internal
328
+ */
329
+ function clampPrecision(value, min, max) {
330
+ if (min === void 0) { min = 0; }
331
+ if (max === void 0) { max = 8; }
332
+ if (!Number.isFinite(value))
333
+ return min;
334
+ return Math.max(min, Math.min(max, Math.round(value)));
335
+ }
336
+ /**
337
+ * Maps negative zero to positive zero for consistent string output.
338
+ *
339
+ * @param value - Input number.
340
+ * @returns `0` when `value` is `-0`, otherwise `value`.
341
+ * @internal
342
+ */
343
+ function normalizeZero(value) {
344
+ return Object.is(value, -0) ? 0 : value;
345
+ }
346
+ /**
347
+ * Prefixes `~ ` when approximate display is enabled and the value was rounded.
348
+ *
349
+ * @param formatted - Fully formatted display string (including unit suffixes).
350
+ * @param approximate - Whether the input differs from the quantized display value.
351
+ * @param ctx - Formatting context.
352
+ * @returns Original text, or `~ ${formatted}` when flagged approximate.
353
+ * @internal
354
+ */
355
+ function applyApproximatePrefix(formatted, approximate, ctx) {
356
+ if (!ctx.showApproximate || !approximate)
357
+ return formatted;
358
+ return "~ ".concat(formatted);
359
+ }
360
+ /**
361
+ * Returns whether a linear value differs from its **LUPREC**-quantized representation.
362
+ *
363
+ * @param value - Raw distance in drawing units.
364
+ * @param ctx - Formatting context.
365
+ * @internal
366
+ */
367
+ function isLinearValueApproximate(value, ctx) {
368
+ if (!Number.isFinite(value))
369
+ return false;
370
+ var quantized = quantizeLinearValue(value, ctx);
371
+ return differsAfterQuantization(value, quantized);
372
+ }
373
+ /**
374
+ * Returns whether an angle differs from its **AUPREC**-quantized representation.
375
+ *
376
+ * @param radians - Display angle in radians (after **ANGBASE** / **ANGDIR**).
377
+ * @param ctx - Formatting context.
378
+ * @internal
379
+ */
380
+ function isAngleValueApproximate(radians, ctx) {
381
+ if (!Number.isFinite(radians))
382
+ return false;
383
+ if (ctx.aunits === AcDbAngleUnits.SurveyorsUnits) {
384
+ return isSurveyorAngleApproximate(radians, ctx.auprec);
385
+ }
386
+ var quantized = quantizeAngleValue(radians, ctx);
387
+ return differsAfterQuantization(radians, quantized);
388
+ }
389
+ /**
390
+ * Returns whether the surveyor deflection angle would be rounded in DMS output.
391
+ *
392
+ * @internal
393
+ */
394
+ function isSurveyorAngleApproximate(radians, auprec) {
395
+ var degrees = AcGeMathUtil.radToDeg(radians);
396
+ var bearing = (((90 - degrees) % 360) + 360) % 360;
397
+ var quadrant = bearingToQuadrant(bearing);
398
+ var deflection = Math.min(Math.abs(bearing - quadrant.base), Math.abs(quadrant.base + 90 - bearing));
399
+ var deflectionRadians = AcGeMathUtil.degToRad(deflection);
400
+ var quantizedDeflection = quantizeAngleDmsRadians(deflectionRadians, auprec);
401
+ return differsAfterQuantization(deflectionRadians, quantizedDeflection);
402
+ }
403
+ /**
404
+ * Quantizes a linear value to the precision implied by **LUNITS** and **LUPREC**.
405
+ *
406
+ * @internal
407
+ */
408
+ function quantizeLinearValue(value, ctx) {
409
+ var normalized = normalizeZero(value);
410
+ switch (ctx.lunits) {
411
+ case AcDbLinearUnits.Scientific:
412
+ return quantizeScientific(normalized, ctx.luprec);
413
+ case AcDbLinearUnits.Engineering:
414
+ case AcDbLinearUnits.Architectural:
415
+ case AcDbLinearUnits.Fractional:
416
+ return quantizeImperialInches(normalized, ctx);
417
+ case AcDbLinearUnits.WindowsDesktop:
418
+ case AcDbLinearUnits.Decimal:
419
+ default:
420
+ return quantizeDecimal(normalized, ctx.luprec);
421
+ }
422
+ }
423
+ /**
424
+ * Quantizes an angle to the precision implied by **AUNITS** and **AUPREC**.
425
+ *
426
+ * @internal
427
+ */
428
+ function quantizeAngleValue(radians, ctx) {
429
+ var normalized = normalizeZero(radians);
430
+ switch (ctx.aunits) {
431
+ case AcDbAngleUnits.DegreesMinutesSeconds:
432
+ return quantizeAngleDmsRadians(normalized, ctx.auprec);
433
+ case AcDbAngleUnits.Gradians:
434
+ return quantizeDecimal(normalized / RAD_PER_GRAD, ctx.auprec) * RAD_PER_GRAD;
435
+ case AcDbAngleUnits.Radians:
436
+ return quantizeDecimal(normalized, ctx.auprec);
437
+ case AcDbAngleUnits.DecimalDegrees:
438
+ default:
439
+ return AcGeMathUtil.degToRad(quantizeDecimal(AcGeMathUtil.radToDeg(normalized), ctx.auprec));
440
+ }
441
+ }
442
+ /**
443
+ * Rounds a number to a fixed number of decimal places.
444
+ *
445
+ * @internal
446
+ */
447
+ function quantizeDecimal(value, precision) {
448
+ if (!Number.isFinite(value))
449
+ return 0;
450
+ var factor = Math.pow(10, precision);
451
+ return Math.round(normalizeZero(value) * factor) / factor;
452
+ }
453
+ /**
454
+ * Rounds a value to the mantissa precision used by {@link formatScientific}.
455
+ *
456
+ * @internal
457
+ */
458
+ function quantizeScientific(value, precision) {
459
+ if (!Number.isFinite(value))
460
+ return 0;
461
+ var normalized = normalizeZero(value);
462
+ if (normalized === 0)
463
+ return 0;
464
+ var formatted = normalized.toExponential(precision);
465
+ return Number(formatted);
466
+ }
467
+ /**
468
+ * Quantizes an inch-based length for engineering, architectural, or fractional **LUNITS**.
469
+ *
470
+ * @internal
471
+ */
472
+ function quantizeImperialInches(inches, ctx) {
473
+ if (!Number.isFinite(inches))
474
+ return 0;
475
+ var sign = inches < 0 ? -1 : 1;
476
+ var absInches = Math.abs(inches);
477
+ var feet = Math.floor(absInches / 12);
478
+ var inchPart = absInches - feet * 12;
479
+ if (ctx.lunits === AcDbLinearUnits.Architectural ||
480
+ ctx.lunits === AcDbLinearUnits.Fractional) {
481
+ var denominator = 1 << clampPrecision(ctx.luprec, 0, 8);
482
+ var quantizedInches = quantizeFractionalInches(inchPart, denominator);
483
+ return sign * (feet * 12 + quantizedInches);
484
+ }
485
+ return sign * (feet * 12 + quantizeDecimal(inchPart, ctx.luprec));
486
+ }
487
+ /**
488
+ * Quantizes the inch component using the same rounding as {@link formatFractionalInches}.
489
+ *
490
+ * @internal
491
+ */
492
+ function quantizeFractionalInches(inches, denominator) {
493
+ var whole = Math.floor(inches);
494
+ var remainder = inches - whole;
495
+ var numerator = Math.round(remainder * denominator);
496
+ if (numerator >= denominator) {
497
+ return whole + 1;
498
+ }
499
+ return whole + numerator / denominator;
500
+ }
501
+ /**
502
+ * Quantizes an angle in radians using DMS seconds precision (**AUPREC**).
503
+ *
504
+ * @internal
505
+ */
506
+ function quantizeAngleDmsRadians(radians, auprec) {
507
+ var degrees = AcGeMathUtil.radToDeg(radians);
508
+ var sign = degrees < 0 ? -1 : 1;
509
+ var absDegrees = Math.abs(degrees);
510
+ var d = Math.floor(absDegrees);
511
+ var minutesFloat = (absDegrees - d) * 60;
512
+ var m = Math.floor(minutesFloat);
513
+ var seconds = quantizeDecimal((minutesFloat - m) * 60, auprec);
514
+ var quantizedDegrees = d + m / 60 + seconds / 3600;
515
+ return AcGeMathUtil.degToRad(sign * quantizedDegrees);
516
+ }
517
+ /**
518
+ * Returns whether quantization changed the numeric value (beyond float noise).
519
+ *
520
+ * @internal
521
+ */
522
+ function differsAfterQuantization(original, quantized) {
523
+ if (original === quantized)
524
+ return false;
525
+ var scale = Math.max(Math.abs(original), Math.abs(quantized), 1);
526
+ return Math.abs(original - quantized) > scale * Number.EPSILON * 8;
527
+ }
528
+ /**
529
+ * Formats a number in plain decimal form with trailing zeros trimmed.
530
+ *
531
+ * @param value - Numeric value to format.
532
+ * @param precision - Number of decimal places (**LUPREC** / **AUPREC**).
533
+ * @returns Decimal string, or `"0"` for non-finite input.
534
+ * @internal
535
+ */
536
+ function formatDecimal(value, precision) {
537
+ if (!Number.isFinite(value))
538
+ return '0';
539
+ var normalized = normalizeZero(value);
540
+ if (Number.isInteger(normalized) && precision === 0) {
541
+ return String(normalized);
542
+ }
543
+ var fixed = normalized.toFixed(precision);
544
+ var trimmed = fixed.replace(/\.?0+$/, '');
545
+ if (trimmed === '' || trimmed === '-')
546
+ return '0';
547
+ return trimmed;
548
+ }
549
+ /**
550
+ * Formats a number in scientific notation (**LUNITS** = scientific).
551
+ *
552
+ * @param value - Numeric value to format.
553
+ * @param precision - Mantissa decimal places.
554
+ * @returns Uppercase exponent string (for example `1.23E+02`), or `"0"` for non-finite/zero input.
555
+ * @internal
556
+ */
557
+ function formatScientific(value, precision) {
558
+ if (!Number.isFinite(value))
559
+ return '0';
560
+ var normalized = normalizeZero(value);
561
+ if (normalized === 0)
562
+ return '0';
563
+ var match = normalized.toExponential(precision).match(/^(.+)e([+-])(\d+)$/i);
564
+ if (!match)
565
+ return '0';
566
+ var _a = __read(match, 4), mantissa = _a[1], sign = _a[2], exponentDigits = _a[3];
567
+ var paddedExponent = exponentDigits.length < 2 ? exponentDigits.padStart(2, '0') : exponentDigits;
568
+ return "".concat(mantissa, "E").concat(sign).concat(paddedExponent).toUpperCase();
569
+ }
570
+ /**
571
+ * Formats a length in engineering feet-and-decimal-inches (**LUNITS** = 3).
572
+ *
573
+ * @param inches - Length interpreted as inches.
574
+ * @param ctx - Formatting context (**LUPREC**, **UNITMODE**, suffix flags).
575
+ * @returns Engineering string (for example `1'-3.5`).
576
+ * @internal
577
+ */
578
+ function formatEngineering(inches, ctx) {
579
+ if (!Number.isFinite(inches))
580
+ return '0';
581
+ var style = imperialDelimiterStyle(ctx);
582
+ var sign = inches < 0 ? '-' : '';
583
+ var absInches = Math.abs(inches);
584
+ var feet = Math.floor(absInches / 12);
585
+ var inchPart = absInches - feet * 12;
586
+ var inchText = formatDecimal(inchPart, ctx.luprec);
587
+ if (feet === 0) {
588
+ return "".concat(sign).concat(inchText).concat(style.inchSuffix);
589
+ }
590
+ return "".concat(sign).concat(feet, "'").concat(style.feetInchSeparator).concat(inchText).concat(style.inchSuffix);
591
+ }
592
+ /**
593
+ * Formats a length in architectural feet-and-fractional-inches (**LUNITS** = 4).
594
+ *
595
+ * @param inches - Length interpreted as inches.
596
+ * @param ctx - Formatting context; **LUPREC** selects fractional denominator \(2^{LUPREC}\).
597
+ * @returns Architectural string (for example `1'-3 1/2`).
598
+ * @internal
599
+ */
600
+ function formatArchitectural(inches, ctx) {
601
+ if (!Number.isFinite(inches))
602
+ return '0';
603
+ var style = imperialDelimiterStyle(ctx);
604
+ var sign = inches < 0 ? '-' : '';
605
+ var absInches = Math.abs(inches);
606
+ var feet = Math.floor(absInches / 12);
607
+ var inchPart = absInches - feet * 12;
608
+ var denominator = 1 << clampPrecision(ctx.luprec, 0, 8);
609
+ var inchText = formatFractionalInches(inchPart, denominator, style.fractionSeparator);
610
+ if (feet === 0) {
611
+ return "".concat(sign).concat(inchText).concat(style.inchSuffix);
612
+ }
613
+ return "".concat(sign).concat(feet, "'").concat(style.feetInchSeparator).concat(inchText).concat(style.inchSuffix);
614
+ }
615
+ /**
616
+ * Formats a length as a whole number plus fraction (**LUNITS** = fractional).
617
+ *
618
+ * @param inches - Length interpreted as inches.
619
+ * @param ctx - Formatting context.
620
+ * @returns Fractional string (for example `15 1/2`).
621
+ * @internal
622
+ */
623
+ function formatFractionalLinear(inches, ctx) {
624
+ if (!Number.isFinite(inches))
625
+ return '0';
626
+ var style = imperialDelimiterStyle(ctx);
627
+ var sign = inches < 0 ? '-' : '';
628
+ var absInches = Math.abs(inches);
629
+ var denominator = 1 << clampPrecision(ctx.luprec, 0, 8);
630
+ return "".concat(sign).concat(formatFractionalInches(absInches, denominator, style.fractionSeparator));
631
+ }
632
+ /**
633
+ * Formats the inch component as a whole number and reduced fraction.
634
+ *
635
+ * @param inches - Inch value in \([0, 12)\) (caller supplies fractional part of feet-inch).
636
+ * @param denominator - Fraction denominator (power of two from **LUPREC**).
637
+ * @param fractionSeparator - Space or dash between whole inches and fraction.
638
+ * @returns Inch-only fractional text (for example `3 1/2`).
639
+ * @internal
640
+ */
641
+ function formatFractionalInches(inches, denominator, fractionSeparator) {
642
+ var whole = Math.floor(inches);
643
+ var remainder = inches - whole;
644
+ var numerator = Math.round(remainder * denominator);
645
+ if (numerator >= denominator) {
646
+ return String(whole + 1);
647
+ }
648
+ if (numerator === 0) {
649
+ return String(whole);
650
+ }
651
+ var divisor = gcd(numerator, denominator);
652
+ numerator /= divisor;
653
+ var reducedDenominator = denominator / divisor;
654
+ var fraction = "".concat(numerator, "/").concat(reducedDenominator);
655
+ if (whole === 0) {
656
+ return fraction;
657
+ }
658
+ return "".concat(whole).concat(fractionSeparator).concat(fraction);
659
+ }
660
+ /**
661
+ * Formats an angle as decimal degrees (**AUNITS** = 0).
662
+ *
663
+ * @param radians - Display angle in radians.
664
+ * @param ctx - Formatting context.
665
+ * @returns Decimal degree string, optionally suffixed with `°`.
666
+ * @internal
667
+ */
668
+ function formatAngleDecimalDegrees(radians, ctx) {
669
+ var degrees = AcGeMathUtil.radToDeg(radians);
670
+ return "".concat(formatDecimal(degrees, ctx.auprec)).concat(ctx.showUnits ? '°' : '');
671
+ }
672
+ /**
673
+ * Formats an angle as radians (**AUNITS** = 3).
674
+ *
675
+ * @param radians - Display angle in radians.
676
+ * @param ctx - Formatting context.
677
+ * @returns Radian string, optionally suffixed with ` rad`.
678
+ * @internal
679
+ */
680
+ function formatAngleRadians(radians, ctx) {
681
+ return "".concat(formatDecimal(radians, ctx.auprec)).concat(ctx.showUnits ? ' rad' : '');
682
+ }
683
+ /**
684
+ * Formats an angle as gradians (**AUNITS** = 2).
685
+ *
686
+ * @param radians - Display angle in radians.
687
+ * @param ctx - Formatting context.
688
+ * @returns Gradian string, optionally suffixed with `g`.
689
+ * @internal
690
+ */
691
+ function formatAngleGradians(radians, ctx) {
692
+ var grads = radians / RAD_PER_GRAD;
693
+ return "".concat(formatDecimal(grads, ctx.auprec)).concat(ctx.showUnits ? 'g' : '');
694
+ }
695
+ /**
696
+ * Formats an angle as degrees, minutes, and seconds (**AUNITS** = 1).
697
+ *
698
+ * @param radians - Display angle in radians.
699
+ * @param ctx - Formatting context; **AUPREC** applies to the seconds field.
700
+ * @returns DMS string (for example `45d30'15"`).
701
+ * @internal
702
+ */
703
+ function formatAngleDms(radians, ctx) {
704
+ var degrees = AcGeMathUtil.radToDeg(radians);
705
+ var sign = degrees < 0 ? '-' : '';
706
+ var absDegrees = Math.abs(degrees);
707
+ var d = Math.floor(absDegrees);
708
+ var minutesFloat = (absDegrees - d) * 60;
709
+ var m = Math.floor(minutesFloat);
710
+ var seconds = (minutesFloat - m) * 60;
711
+ var secondsText = formatDecimal(seconds, ctx.auprec);
712
+ var secondSuffix = ctx.showUnits ? '"' : '';
713
+ return "".concat(sign).concat(d, "d").concat(m, "'").concat(secondsText).concat(secondSuffix);
714
+ }
715
+ /**
716
+ * Formats an angle in surveyor's bearing notation (**AUNITS** = 4).
717
+ *
718
+ * @param radians - Display angle in radians.
719
+ * @param ctx - Formatting context; DMS portion uses {@link formatAngleDms}.
720
+ * @returns Bearing string (for example `N 45d30'15" E`).
721
+ * @internal
722
+ */
723
+ function formatAngleSurveyor(radians, ctx) {
724
+ var degrees = AcGeMathUtil.radToDeg(radians);
725
+ var bearing = (((90 - degrees) % 360) + 360) % 360;
726
+ var quadrant = bearingToQuadrant(bearing);
727
+ var deflection = Math.min(Math.abs(bearing - quadrant.base), Math.abs(quadrant.base + 90 - bearing));
728
+ var dms = formatAngleDms(AcGeMathUtil.degToRad(deflection), ctx);
729
+ var dmsBody = dms.replace(/^-/, '');
730
+ return "".concat(quadrant.prefix, " ").concat(dmsBody, " ").concat(quadrant.suffix).trim();
731
+ }
732
+ /**
733
+ * Maps a north-based bearing in degrees to a surveyor quadrant label and reference azimuth.
734
+ *
735
+ * @param bearing - Bearing from north, clockwise, in \([0, 360)\) degrees.
736
+ * @returns Quadrant prefix/suffix (N/S + E/W) and the quadrant's base bearing in degrees.
737
+ * @internal
738
+ */
739
+ function bearingToQuadrant(bearing) {
740
+ if (bearing < 90) {
741
+ return { prefix: 'N', suffix: 'E', base: 0 };
742
+ }
743
+ if (bearing < 180) {
744
+ return { prefix: 'S', suffix: 'E', base: 90 };
745
+ }
746
+ if (bearing < 270) {
747
+ return { prefix: 'S', suffix: 'W', base: 180 };
748
+ }
749
+ return { prefix: 'N', suffix: 'W', base: 270 };
750
+ }
751
+ /**
752
+ * Appends an **INSUNITS** / **MEASUREMENT** suffix to a linear string when enabled.
753
+ *
754
+ * Engineering and architectural strings are returned unchanged (units are already embedded).
755
+ *
756
+ * @param text - Formatted numeric linear text.
757
+ * @param ctx - Formatting context.
758
+ * @returns Text with optional suffix (for example `12.35 mm`).
759
+ * @internal
760
+ */
761
+ function appendLinearUnitSuffix(text, ctx) {
762
+ if (!ctx.showUnits)
763
+ return text;
764
+ if (ctx.lunits === AcDbLinearUnits.Engineering ||
765
+ ctx.lunits === AcDbLinearUnits.Architectural) {
766
+ return text;
767
+ }
768
+ var suffix = linearUnitSuffix(ctx.insunits, ctx.measurement);
769
+ return suffix ? "".concat(text).concat(suffix) : text;
770
+ }
771
+ /**
772
+ * Resolves the suffix for decimal/scientific/fractional linear output.
773
+ *
774
+ * @param insunits - **INSUNITS** value.
775
+ * @param measurement - **MEASUREMENT** flag when **INSUNITS** is unitless.
776
+ * @returns Suffix string, or empty when unknown or suffix not applicable.
777
+ * @internal
778
+ */
779
+ function linearUnitSuffix(insunits, measurement) {
780
+ if (insunits === AcDbUnitsValue.Undefined) {
781
+ return measurement === 0 ? '"' : ' mm';
782
+ }
783
+ if (isMetricUnits(insunits)) {
784
+ return metricUnitSuffix(insunits);
785
+ }
786
+ if (isImperialUnits(insunits)) {
787
+ return imperialUnitSuffix(insunits);
788
+ }
789
+ return '';
790
+ }
791
+ /**
792
+ * Returns the display suffix for a metric {@link AcDbUnitsValue}.
793
+ *
794
+ * @param insunits - Metric insertion unit code.
795
+ * @returns Suffix such as ` mm`, or empty for unsupported codes.
796
+ * @internal
797
+ */
798
+ function metricUnitSuffix(insunits) {
799
+ switch (insunits) {
800
+ case AcDbUnitsValue.Millimeters:
801
+ return ' mm';
802
+ case AcDbUnitsValue.Centimeters:
803
+ return ' cm';
804
+ case AcDbUnitsValue.Meters:
805
+ return ' m';
806
+ case AcDbUnitsValue.Kilometers:
807
+ return ' km';
808
+ case AcDbUnitsValue.Decimeters:
809
+ return ' dm';
810
+ case AcDbUnitsValue.Microns:
811
+ return ' µm';
812
+ case AcDbUnitsValue.Nanometers:
813
+ return ' nm';
814
+ default:
815
+ return '';
816
+ }
817
+ }
818
+ /**
819
+ * Returns the display suffix for an imperial {@link AcDbUnitsValue}.
820
+ *
821
+ * @param insunits - Imperial insertion unit code.
822
+ * @returns Suffix such as `"` or ` mi`, or empty for unsupported codes.
823
+ * @internal
824
+ */
825
+ function imperialUnitSuffix(insunits) {
826
+ switch (insunits) {
827
+ case AcDbUnitsValue.Inches:
828
+ case AcDbUnitsValue.Microinches:
829
+ case AcDbUnitsValue.Mils:
830
+ case AcDbUnitsValue.USSurveyInch:
831
+ return '"';
832
+ case AcDbUnitsValue.Feet:
833
+ case AcDbUnitsValue.USSurveyFeet:
834
+ return '\'';
835
+ case AcDbUnitsValue.Miles:
836
+ case AcDbUnitsValue.USSurveyMile:
837
+ return ' mi';
838
+ case AcDbUnitsValue.Yards:
839
+ case AcDbUnitsValue.USSurveyYard:
840
+ return ' yd';
841
+ default:
842
+ return '';
843
+ }
844
+ }
845
+ /**
846
+ * Greatest common divisor of two integers (Euclidean algorithm).
847
+ *
848
+ * @param a - First integer.
849
+ * @param b - Second integer.
850
+ * @returns GCD of `|a|` and `|b|`, minimum `1`.
851
+ * @internal
852
+ */
853
+ function gcd(a, b) {
854
+ var x = Math.abs(a);
855
+ var y = Math.abs(b);
856
+ while (y !== 0) {
857
+ var temp = y;
858
+ y = x % y;
859
+ x = temp;
860
+ }
861
+ return x || 1;
862
+ }
863
+ //# sourceMappingURL=AcDbFormatter.js.map