@orbat-mapper/control-measures 0.2.0-alpha.8 → 0.2.0-alpha.9

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.
@@ -59,6 +59,118 @@ interface ControlMeasureDrawRule {
59
59
  transform(event: AnchorTransformEvent): Position[];
60
60
  }
61
61
  //#endregion
62
+ //#region src/text-amplifiers.d.ts
63
+ /**
64
+ * MIL-STD-2525E Appendix L Table L-II text amplifier fields.
65
+ *
66
+ * A control measure instance carries free-text amplifier fields (unique
67
+ * designation, country, date-time group, …) keyed by their doctrinal field
68
+ * code. Repeated fields use numbered suffixes per the standard (`T`, `T1`,
69
+ * `AS1`, `W1`, …). Graphic amplifiers (echelon `B`, direction of movement `Q`,
70
+ * offset location `BA`) are deliberately excluded — they are geometry
71
+ * concerns, rendered from generator options rather than free text.
72
+ *
73
+ * This module is a leaf: it imports nothing from `./metadata` or
74
+ * `./registry`, so both can depend on it without a cycle.
75
+ */
76
+ type Suffix = "" | "1" | "2";
77
+ /** Doctrinal text amplifier field codes (Table L-II); graphic amplifier fields excluded. */
78
+ type TextAmplifierField = "C" | "H" | "N" | "T" | "V" | "W" | "X" | "Y" | "AM" | "AN" | "AP" | "AS";
79
+ /** A doctrinal field code with its optional repetition suffix, e.g. `"T1"`. */
80
+ type TextAmplifierKey = `${TextAmplifierField}${Suffix}`;
81
+ /**
82
+ * The vocabulary of text amplifier fields: doctrinal code, milsymbol-style
83
+ * friendly alias, and a human-readable label. The single source of truth for
84
+ * both the runtime catalog and the derived alias→code lookup used by
85
+ * {@link normalizeTextAmplifiers}.
86
+ */
87
+ declare const TEXT_AMPLIFIER_FIELDS: {
88
+ readonly C: {
89
+ readonly alias: "quantity";
90
+ readonly label: "Quantity";
91
+ };
92
+ readonly H: {
93
+ readonly alias: "additionalInformation";
94
+ readonly label: "Additional information";
95
+ };
96
+ readonly N: {
97
+ readonly alias: "hostile";
98
+ readonly label: "Hostile (ENY) marker";
99
+ };
100
+ readonly T: {
101
+ readonly alias: "uniqueDesignation";
102
+ readonly label: "Unique designation";
103
+ };
104
+ readonly V: {
105
+ readonly alias: "type";
106
+ readonly label: "Equipment/type";
107
+ };
108
+ readonly W: {
109
+ readonly alias: "dtg";
110
+ readonly label: "Date-time group / O/O";
111
+ };
112
+ readonly X: {
113
+ readonly alias: "altitudeDepth";
114
+ readonly label: "Altitude/depth";
115
+ };
116
+ readonly Y: {
117
+ readonly alias: "location";
118
+ readonly label: "Location";
119
+ };
120
+ readonly AM: {
121
+ readonly alias: "distance";
122
+ readonly label: "Distance/range/radius";
123
+ };
124
+ readonly AN: {
125
+ readonly alias: "azimuth";
126
+ readonly label: "Azimuth";
127
+ };
128
+ readonly AP: {
129
+ readonly alias: "targetNumber";
130
+ readonly label: "Target number";
131
+ };
132
+ readonly AS: {
133
+ readonly alias: "country";
134
+ readonly label: "Country code";
135
+ };
136
+ };
137
+ /** The milsymbol-style friendly alias for a text amplifier field, e.g. `"uniqueDesignation"`. */
138
+ type FriendlyName = (typeof TEXT_AMPLIFIER_FIELDS)[TextAmplifierField]["alias"];
139
+ /** A friendly alias with its optional repetition suffix, e.g. `"country1"`. */
140
+ type FriendlyKey = `${FriendlyName}${Suffix}`;
141
+ /**
142
+ * User-facing amplifier input: either canonical doctrinal keys (`T`, `AS1`,
143
+ * …) or friendly aliases (`uniqueDesignation`, `country1`, …), optionally
144
+ * mixed. Values are free text; see {@link normalizeTextAmplifiers} for how
145
+ * this is resolved to canonical form.
146
+ */
147
+ type TextAmplifiers = Partial<Record<TextAmplifierKey | FriendlyKey, string>>;
148
+ /** Amplifier values resolved to canonical doctrinal keys, as generators receive them. */
149
+ type CanonicalTextAmplifiers = Readonly<Partial<Record<TextAmplifierKey, string>>>;
150
+ /**
151
+ * Resolves any accepted key (canonical or friendly alias, with an optional
152
+ * `1`/`2` suffix) to its canonical key, or `undefined` if unknown — e.g.
153
+ * `"T"` → `"T"`, `"uniqueDesignation1"` → `"T1"`, `"bogus"` → `undefined`.
154
+ */
155
+ declare function canonicalTextAmplifierKey(key: string): TextAmplifierKey | undefined;
156
+ /**
157
+ * Resolves a `TextAmplifiers` record (canonical keys, friendly aliases, or a
158
+ * mix) to `CanonicalTextAmplifiers` keyed purely by doctrinal field code.
159
+ *
160
+ * - Friendly aliases (with an optional `1`/`2` suffix) map to their canonical
161
+ * code (e.g. `country1` → `AS1`).
162
+ * - Values are trimmed; entries that are empty/whitespace-only after
163
+ * trimming, or not strings, are dropped.
164
+ * - When both a canonical key and its friendly alias are present for the same
165
+ * field + suffix, the **canonical key wins** regardless of insertion order.
166
+ * This holds even for an empty/whitespace canonical value: it suppresses
167
+ * (clears) the alias entry rather than letting the alias survive.
168
+ * - Unknown keys are ignored.
169
+ *
170
+ * `undefined` or an empty input returns a shared frozen empty object.
171
+ */
172
+ declare function normalizeTextAmplifiers(input: TextAmplifiers | undefined): CanonicalTextAmplifiers;
173
+ //#endregion
62
174
  //#region src/metadata.d.ts
63
175
  type ControlMeasureGeometryType = Geometry["type"];
64
176
  type ControlMeasureGeometry = "point" | "line" | "area";
@@ -96,6 +208,15 @@ interface TextParamDescriptor extends BaseParamDescriptor {
96
208
  maxLength?: number;
97
209
  }
98
210
  type ParamDescriptor = NumberParamDescriptor | BooleanParamDescriptor | ColorParamDescriptor | TextParamDescriptor | EnumParamDescriptor;
211
+ /** Editor-facing description of one text amplifier field a measure accepts. */
212
+ interface TextAmplifierDescriptor {
213
+ key: TextAmplifierKey;
214
+ label: string;
215
+ description?: string;
216
+ placeholder?: string;
217
+ /** Editor hint only — the renderer never truncates the value. */
218
+ maxLength?: number;
219
+ }
99
220
  interface ControlMeasureMetadata {
100
221
  id: string;
101
222
  name: string;
@@ -117,6 +238,8 @@ interface ControlMeasureMetadata {
117
238
  */
118
239
  rule?: ControlMeasureDrawRule;
119
240
  params?: readonly ParamDescriptor[];
241
+ /** Text amplifier fields (MIL-STD-2525E Appendix L Table L-II) this measure accepts. */
242
+ textAmplifiers?: readonly TextAmplifierDescriptor[];
120
243
  /**
121
244
  * Marks a measure that emits a text label whose pixel size should be captured
122
245
  * once at draw-commit time (rather than re-resolved on every zoom). Hosts use
@@ -140,8 +263,12 @@ type AnyFeatureCollection = FeatureCollection<Geometry, Record<string, unknown>
140
263
  * `(o: BlockOptions) => …` is assignable to `(o: never) => …`. The actual
141
264
  * options type is recovered covariantly via {@link OptsOf}, never through this
142
265
  * bound. See ADR-0013.
266
+ *
267
+ * The optional third parameter carries the instance's normalized text
268
+ * amplifiers (ADR-0027); a generator that ignores text amplifiers just omits
269
+ * the parameter — fewer params is still assignable to this bound.
143
270
  */
144
- type ControlMeasureGenerator = (controlPoints: Position[], options: never) => AnyFeatureCollection;
271
+ type ControlMeasureGenerator = (controlPoints: Position[], options: never, textAmplifiers?: CanonicalTextAmplifiers) => AnyFeatureCollection;
145
272
  /**
146
273
  * Everything that defines one control measure, co-located with its generator.
147
274
  * `registry.ts` collects these into the single `DEFINITIONS` map, from which
@@ -270,14 +397,31 @@ interface BoundaryOptions {
270
397
  * @default 1
271
398
  */
272
399
  labelSpacing?: number;
273
- /** Field T (unique designator) for unit 1 — the left-of-travel side. */
274
- unit1Designator?: string;
275
- /** Field AS (country code) for unit 1 — the left-of-travel side. */
276
- unit1Country?: string;
277
- /** Field T (unique designator) for unit 2 — the right-of-travel side. */
278
- unit2Designator?: string;
279
- /** Field AS (country code) for unit 2 — the right-of-travel side. */
280
- unit2Country?: string;
400
+ /**
401
+ * Clearance between the line/echelon glyph and the unit labels, as a ratio
402
+ * of the echelon glyph height (like `echelonPadding`). In `"along"` mode
403
+ * (and on `"standard"` east–west segments) it grows the labels'
404
+ * perpendicular offset from the line; in a `"standard"` north–south crossing
405
+ * row it grows each label's gap to the echelon glyph. Negative values clamp
406
+ * to 0.
407
+ * @default 0.4
408
+ */
409
+ labelPadding?: number;
410
+ /**
411
+ * How the T/AS unit labels and the echelon glyph are oriented:
412
+ *
413
+ * - `"along"` — text runs along the line, offset perpendicular to it, and
414
+ * the echelon glyph turns with the segment (the default).
415
+ * - `"standard"` — MIL-STD-2525E Appendix L §L.5.5.2 / Figure L-10
416
+ * placement. On mostly east–west segments this reads exactly like
417
+ * `"along"`. On mostly north–south segments the whole group rotates to run
418
+ * *perpendicular* to the line instead: a single row of
419
+ * `<unit 1> <echelon> <unit 2>` crossing it (unit 1 on its left-of-travel
420
+ * side), each label aligned away from the glyph so long designations never
421
+ * overlap the line.
422
+ * @default "along"
423
+ */
424
+ labelOrientation?: "along" | "standard";
281
425
  /**
282
426
  * Round the boundary's corners by curving it through the control points
283
427
  * (centripetal Catmull-Rom spline). The spline preserves the endpoints, so
@@ -301,8 +445,10 @@ declare const DEFAULT_BOUNDARY_OPTIONS: BoundaryOptions;
301
445
  * @param positions - Boundary anchor points (≥2); the input contract is
302
446
  * enforced at the render seam (ADR-0014).
303
447
  * @param options - {@link BoundaryOptions}.
448
+ * @param textAmplifiers - Normalized text amplifiers (ADR-0027): `T`/`AS` label
449
+ * unit 1 (left-of-travel side), `T1`/`AS1` label unit 2 (right-of-travel side).
304
450
  */
305
- declare function createBoundary(positions: Position[], options?: BoundaryOptions): FeatureCollection<MultiLineString | MultiPolygon | Point>;
451
+ declare function createBoundary(positions: Position[], options?: BoundaryOptions, textAmplifiers?: CanonicalTextAmplifiers): FeatureCollection<MultiLineString | MultiPolygon | Point>;
306
452
  //#endregion
307
453
  //#region src/generators/cm15-maneuver-areas/battlePosition.d.ts
308
454
  /** Configuration options for the Battle Position control measure. */
@@ -1448,6 +1594,8 @@ interface ControlMeasure<K extends ControlMeasureKind = ControlMeasureKind> {
1448
1594
  options?: OptionsByKind[K];
1449
1595
  style?: ControlMeasureStyle;
1450
1596
  properties?: Record<string, unknown>;
1597
+ /** Free-text amplifier fields (MIL-STD-2525E Appendix L Table L-II); see `./text-amplifiers`. */
1598
+ textAmplifiers?: TextAmplifiers;
1451
1599
  schemaVersion?: 1;
1452
1600
  }
1453
1601
  declare function isKind<K extends ControlMeasureKind>(cm: ControlMeasure, kind: K): cm is ControlMeasure<K>;
@@ -1455,9 +1603,9 @@ declare function isKind<K extends ControlMeasureKind>(cm: ControlMeasure, kind:
1455
1603
  * Deep-clone a `ControlMeasure` so held references survive mutations to the
1456
1604
  * original (and vice versa). Used by the session façade to build the
1457
1605
  * `measure` half of `ControlMeasureSnapshot`. Nested values inside
1458
- * `options`, `style`, and `properties` are cloned too — `properties` is
1459
- * `Record<string, unknown>` and may carry arbitrary host metadata, so a
1460
- * shallow copy would leak shared references.
1606
+ * `options`, `style`, `properties`, and `textAmplifiers` are cloned too —
1607
+ * `properties` is `Record<string, unknown>` and may carry arbitrary host
1608
+ * metadata, so a shallow copy would leak shared references.
1461
1609
  *
1462
1610
  * Backed by `structuredClone`: only structured-cloneable values are
1463
1611
  * supported (no functions, DOM nodes, class instances). Absent optional
@@ -1501,6 +1649,11 @@ interface FeaturePartProps {
1501
1649
  text?: string;
1502
1650
  /** Label rotation in radians, emitted by generators. */
1503
1651
  rotation?: number;
1652
+ /**
1653
+ * Horizontal text alignment relative to the label point: `"start"` = the
1654
+ * text begins at the point, `"end"` = it ends there. Absent = centered.
1655
+ */
1656
+ textAnchor?: "start" | "end";
1504
1657
  /** Label size in CSS pixels at the draw/reference zoom. */
1505
1658
  textSizePixels?: number;
1506
1659
  /** Map zoom at which textSizePixels was captured. */
@@ -1828,4 +1981,4 @@ interface TacticalArrowOptions {
1828
1981
  */
1829
1982
  declare const DEFAULT_TACTICAL_ARROW_OPTIONS: Required<TacticalArrowOptions>;
1830
1983
  //#endregion
1831
- export { getControlMeasureMetadata as $, DEFAULT_GENERIC_LINE_OPTIONS as $t, getMetersPerPixel as A, ControlMeasureMetadata as An, DEFAULT_CANALIZE_OPTIONS as At, ControlMeasureSnapshot as B, DEFAULT_ATTACK_BY_FIRE_OPTIONS as Bt, snapToMidpointPerpendicular as C, StrongPointOptions as Cn, AntitankDitchOptions as Ct, BaselineFrameOrigin as D, DEFAULT_BOUNDARY_OPTIONS as Dn, ClearOptions as Dt, BaselineFrameOptions as E, BoundaryOptions as En, DelayOptions as Et, resolveStyleHints as F, BlockMissionTaskOptions as Ft, cloneControlMeasure as G, AirborneAttackOptions as Gt, StyleHints as H, SupportByFireOptions as Ht, freezeOrientationOptions as I, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as It, CONTROL_MEASURE_IDS as J, GenericCircleOptions as Jt, isKind as K, DEFAULT_AIRBORNE_ATTACK_OPTIONS as Kt, RenderOptions as L, DEFAULT_FLOT_OPTIONS as Lt, SimpleStyleProps as M, AnchorTransformEvent as Mn, DEFAULT_BYPASS_OPTIONS as Mt, SimpleStyleRender as N, ControlMeasureDrawRule as Nn, BreachOptions as Nt, createBaselineFrame as O, ControlMeasureGeometry as On, DEFAULT_CLEAR_OPTIONS as Ot, toSimpleStyle as P, DEFAULT_BREACH_OPTIONS as Pt, OptionsByKind as Q, GenericPolygonOptions as Qt, renderControlMeasure as R, FLOTOptions as Rt, pointOnMidpointPerpendicularAxis as S, DEFAULT_STRONG_POINT_OPTIONS as Sn, DEFAULT_ANTITANK_WALL_OPTIONS as St, BaselineFrameNormal as T, DEFAULT_BATTLE_POSITION_OPTIONS as Tn, DEFAULT_DELAY_OPTIONS as Tt, controlMeasureIdFromFeature as U, AttackHelicopterOptions as Ut, FeaturePartProps as V, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as Vt, ControlMeasure as W, DEFAULT_ATTACK_HELICOPTER_OPTIONS as Wt, ControlMeasureId as X, GenericRectangleOptions as Xt, CONTROL_MEASURE_METADATA as Y, DEFAULT_GENERIC_RECTANGLE_OPTIONS as Yt, ControlMeasureKind as Z, DEFAULT_GENERIC_POLYGON_OPTIONS as Zt, blockDrawRule as _, EncirclementOptions as _n, DEFAULT_FORTIFIED_AREA_OPTIONS as _t, rectangleDrawRule as a, ClassicArrowOptions as an, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as at, createMidpointPerpendicularDrawRule as b, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as bn, FortifiedLineOptions as bt, line24DrawRule as c, SupportingAttackOptions as cn, ObstacleBypassEasyOptions as ct, line1DrawRule as d, calculateMetrics as dn, DEFAULT_FIX_OPTIONS as dt, GenericLineOptions as en, getControlMeasureMetadataByValue as et, ambushDrawRule as f, computeInitialWidthPoint as fn, FixOptions as ft, disruptDrawRule as g, DEFAULT_ENCIRCLEMENT_OPTIONS as gn, DEFAULT_BLOCK_OPTIONS as gt, centerRadiusDrawRule as h, unproject as hn, BlockOptions as ht, DEFAULT_AMBUSH_OPTIONS as i, ClassicArrowHeadStyle as in, ObstacleBypassImpossibleOptions as it, roundToFixed as j, ParamDescriptor as jn, BypassOptions as jt, EPSILON as k, ControlMeasureGeometryType as kn, CanalizeOptions as kt, line23DrawRule as l, DEFAULT_MAIN_ATTACK_OPTIONS as ln, DEFAULT_TURN_OPTIONS as lt, point12DrawRule as m, project as mn, DisruptOptions as mt, TacticalArrowOptions as n, BlockArrowOptions as nn, listControlMeasureMetadata as nt, axis1DrawRule as o, DEFAULT_CLASSIC_ARROW_OPTIONS as on, ObstacleBypassDifficultOptions as ot, attackByFireDrawRule as p, Point2D as pn, DEFAULT_DISRUPT_OPTIONS as pt, ControlMeasureStyle as q, DEFAULT_GENERIC_CIRCLE_OPTIONS as qt, AmbushOptions as r, DEFAULT_BLOCK_ARROW_OPTIONS as rn, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as rt, supportByFireDrawRule as s, DEFAULT_SUPPORTING_ATTACK_OPTIONS as sn, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as st, DEFAULT_TACTICAL_ARROW_OPTIONS as t, BlockArrowHeadStyle as tn, getDefaultOptions as tt, turnDrawRule as u, MainAttackOptions as un, TurnOptions as ut, MidpointPerpendicularDrawRuleOptions as v, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as vn, FortifiedAreaOptions as vt, BaselineFrame as w, BattlePositionOptions as wn, DEFAULT_ANTITANK_DITCH_OPTIONS as wt, getMidpointPerpendicularSignedDistance as x, PrincipalDirectionOfFireOptions as xn, AntitankWallOptions as xt, computeDefaultMidpointPerpendicularPoint as y, FinalProtectiveFireOptions as yn, DEFAULT_FORTIFIED_LINE_OPTIONS as yt, ControlMeasureRender as z, AttackByFireOptions as zt };
1984
+ export { getControlMeasureMetadata as $, DEFAULT_GENERIC_LINE_OPTIONS as $t, getMetersPerPixel as A, ControlMeasureMetadata as An, DEFAULT_CANALIZE_OPTIONS as At, ControlMeasureSnapshot as B, AnchorTransformEvent as Bn, DEFAULT_ATTACK_BY_FIRE_OPTIONS as Bt, snapToMidpointPerpendicular as C, StrongPointOptions as Cn, AntitankDitchOptions as Ct, BaselineFrameOrigin as D, DEFAULT_BOUNDARY_OPTIONS as Dn, ClearOptions as Dt, BaselineFrameOptions as E, BoundaryOptions as En, DelayOptions as Et, resolveStyleHints as F, TextAmplifierField as Fn, BlockMissionTaskOptions as Ft, cloneControlMeasure as G, AirborneAttackOptions as Gt, StyleHints as H, SupportByFireOptions as Ht, freezeOrientationOptions as I, TextAmplifierKey as In, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as It, CONTROL_MEASURE_IDS as J, GenericCircleOptions as Jt, isKind as K, DEFAULT_AIRBORNE_ATTACK_OPTIONS as Kt, RenderOptions as L, TextAmplifiers as Ln, DEFAULT_FLOT_OPTIONS as Lt, SimpleStyleProps as M, TextAmplifierDescriptor as Mn, DEFAULT_BYPASS_OPTIONS as Mt, SimpleStyleRender as N, CanonicalTextAmplifiers as Nn, BreachOptions as Nt, createBaselineFrame as O, ControlMeasureGeometry as On, DEFAULT_CLEAR_OPTIONS as Ot, toSimpleStyle as P, TEXT_AMPLIFIER_FIELDS as Pn, DEFAULT_BREACH_OPTIONS as Pt, OptionsByKind as Q, GenericPolygonOptions as Qt, renderControlMeasure as R, canonicalTextAmplifierKey as Rn, FLOTOptions as Rt, pointOnMidpointPerpendicularAxis as S, DEFAULT_STRONG_POINT_OPTIONS as Sn, DEFAULT_ANTITANK_WALL_OPTIONS as St, BaselineFrameNormal as T, DEFAULT_BATTLE_POSITION_OPTIONS as Tn, DEFAULT_DELAY_OPTIONS as Tt, controlMeasureIdFromFeature as U, AttackHelicopterOptions as Ut, FeaturePartProps as V, ControlMeasureDrawRule as Vn, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as Vt, ControlMeasure as W, DEFAULT_ATTACK_HELICOPTER_OPTIONS as Wt, ControlMeasureId as X, GenericRectangleOptions as Xt, CONTROL_MEASURE_METADATA as Y, DEFAULT_GENERIC_RECTANGLE_OPTIONS as Yt, ControlMeasureKind as Z, DEFAULT_GENERIC_POLYGON_OPTIONS as Zt, blockDrawRule as _, EncirclementOptions as _n, DEFAULT_FORTIFIED_AREA_OPTIONS as _t, rectangleDrawRule as a, ClassicArrowOptions as an, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as at, createMidpointPerpendicularDrawRule as b, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as bn, FortifiedLineOptions as bt, line24DrawRule as c, SupportingAttackOptions as cn, ObstacleBypassEasyOptions as ct, line1DrawRule as d, calculateMetrics as dn, DEFAULT_FIX_OPTIONS as dt, GenericLineOptions as en, getControlMeasureMetadataByValue as et, ambushDrawRule as f, computeInitialWidthPoint as fn, FixOptions as ft, disruptDrawRule as g, DEFAULT_ENCIRCLEMENT_OPTIONS as gn, DEFAULT_BLOCK_OPTIONS as gt, centerRadiusDrawRule as h, unproject as hn, BlockOptions as ht, DEFAULT_AMBUSH_OPTIONS as i, ClassicArrowHeadStyle as in, ObstacleBypassImpossibleOptions as it, roundToFixed as j, ParamDescriptor as jn, BypassOptions as jt, EPSILON as k, ControlMeasureGeometryType as kn, CanalizeOptions as kt, line23DrawRule as l, DEFAULT_MAIN_ATTACK_OPTIONS as ln, DEFAULT_TURN_OPTIONS as lt, point12DrawRule as m, project as mn, DisruptOptions as mt, TacticalArrowOptions as n, BlockArrowOptions as nn, listControlMeasureMetadata as nt, axis1DrawRule as o, DEFAULT_CLASSIC_ARROW_OPTIONS as on, ObstacleBypassDifficultOptions as ot, attackByFireDrawRule as p, Point2D as pn, DEFAULT_DISRUPT_OPTIONS as pt, ControlMeasureStyle as q, DEFAULT_GENERIC_CIRCLE_OPTIONS as qt, AmbushOptions as r, DEFAULT_BLOCK_ARROW_OPTIONS as rn, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as rt, supportByFireDrawRule as s, DEFAULT_SUPPORTING_ATTACK_OPTIONS as sn, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as st, DEFAULT_TACTICAL_ARROW_OPTIONS as t, BlockArrowHeadStyle as tn, getDefaultOptions as tt, turnDrawRule as u, MainAttackOptions as un, TurnOptions as ut, MidpointPerpendicularDrawRuleOptions as v, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as vn, FortifiedAreaOptions as vt, BaselineFrame as w, BattlePositionOptions as wn, DEFAULT_ANTITANK_DITCH_OPTIONS as wt, getMidpointPerpendicularSignedDistance as x, PrincipalDirectionOfFireOptions as xn, AntitankWallOptions as xt, computeDefaultMidpointPerpendicularPoint as y, FinalProtectiveFireOptions as yn, DEFAULT_FORTIFIED_LINE_OPTIONS as yt, ControlMeasureRender as z, normalizeTextAmplifiers as zn, AttackByFireOptions as zt };
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as getControlMeasureMetadata, $t as DEFAULT_GENERIC_LINE_OPTIONS, A as getMetersPerPixel, An as ControlMeasureMetadata, At as DEFAULT_CANALIZE_OPTIONS, B as ControlMeasureSnapshot, Bt as DEFAULT_ATTACK_BY_FIRE_OPTIONS, C as snapToMidpointPerpendicular, Cn as StrongPointOptions, Ct as AntitankDitchOptions, D as BaselineFrameOrigin, Dn as DEFAULT_BOUNDARY_OPTIONS, Dt as ClearOptions, E as BaselineFrameOptions, En as BoundaryOptions, Et as DelayOptions, F as resolveStyleHints, Ft as BlockMissionTaskOptions, G as cloneControlMeasure, Gt as AirborneAttackOptions, H as StyleHints, Ht as SupportByFireOptions, I as freezeOrientationOptions, It as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, J as CONTROL_MEASURE_IDS, Jt as GenericCircleOptions, K as isKind, Kt as DEFAULT_AIRBORNE_ATTACK_OPTIONS, L as RenderOptions, Lt as DEFAULT_FLOT_OPTIONS, M as SimpleStyleProps, Mn as AnchorTransformEvent, Mt as DEFAULT_BYPASS_OPTIONS, N as SimpleStyleRender, Nn as ControlMeasureDrawRule, Nt as BreachOptions, O as createBaselineFrame, On as ControlMeasureGeometry, Ot as DEFAULT_CLEAR_OPTIONS, P as toSimpleStyle, Pt as DEFAULT_BREACH_OPTIONS, Q as OptionsByKind, Qt as GenericPolygonOptions, R as renderControlMeasure, Rt as FLOTOptions, S as pointOnMidpointPerpendicularAxis, Sn as DEFAULT_STRONG_POINT_OPTIONS, St as DEFAULT_ANTITANK_WALL_OPTIONS, T as BaselineFrameNormal, Tn as DEFAULT_BATTLE_POSITION_OPTIONS, Tt as DEFAULT_DELAY_OPTIONS, U as controlMeasureIdFromFeature, Ut as AttackHelicopterOptions, V as FeaturePartProps, Vt as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, W as ControlMeasure, Wt as DEFAULT_ATTACK_HELICOPTER_OPTIONS, X as ControlMeasureId, Xt as GenericRectangleOptions, Y as CONTROL_MEASURE_METADATA, Yt as DEFAULT_GENERIC_RECTANGLE_OPTIONS, Z as ControlMeasureKind, Zt as DEFAULT_GENERIC_POLYGON_OPTIONS, _ as blockDrawRule, _n as EncirclementOptions, _t as DEFAULT_FORTIFIED_AREA_OPTIONS, a as rectangleDrawRule, an as ClassicArrowOptions, at as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, b as createMidpointPerpendicularDrawRule, bn as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, bt as FortifiedLineOptions, c as line24DrawRule, cn as SupportingAttackOptions, ct as ObstacleBypassEasyOptions, d as line1DrawRule, dn as calculateMetrics, dt as DEFAULT_FIX_OPTIONS, en as GenericLineOptions, et as getControlMeasureMetadataByValue, f as ambushDrawRule, fn as computeInitialWidthPoint, ft as FixOptions, g as disruptDrawRule, gn as DEFAULT_ENCIRCLEMENT_OPTIONS, gt as DEFAULT_BLOCK_OPTIONS, h as centerRadiusDrawRule, hn as unproject, ht as BlockOptions, i as DEFAULT_AMBUSH_OPTIONS, in as ClassicArrowHeadStyle, it as ObstacleBypassImpossibleOptions, j as roundToFixed, jn as ParamDescriptor, jt as BypassOptions, k as EPSILON, kn as ControlMeasureGeometryType, kt as CanalizeOptions, l as line23DrawRule, ln as DEFAULT_MAIN_ATTACK_OPTIONS, lt as DEFAULT_TURN_OPTIONS, m as point12DrawRule, mn as project, mt as DisruptOptions, n as TacticalArrowOptions, nn as BlockArrowOptions, nt as listControlMeasureMetadata, o as axis1DrawRule, on as DEFAULT_CLASSIC_ARROW_OPTIONS, ot as ObstacleBypassDifficultOptions, p as attackByFireDrawRule, pn as Point2D, pt as DEFAULT_DISRUPT_OPTIONS, q as ControlMeasureStyle, qt as DEFAULT_GENERIC_CIRCLE_OPTIONS, r as AmbushOptions, rn as DEFAULT_BLOCK_ARROW_OPTIONS, rt as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, s as supportByFireDrawRule, sn as DEFAULT_SUPPORTING_ATTACK_OPTIONS, st as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, t as DEFAULT_TACTICAL_ARROW_OPTIONS, tn as BlockArrowHeadStyle, tt as getDefaultOptions, u as turnDrawRule, un as MainAttackOptions, ut as TurnOptions, v as MidpointPerpendicularDrawRuleOptions, vn as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, vt as FortifiedAreaOptions, w as BaselineFrame, wn as BattlePositionOptions, wt as DEFAULT_ANTITANK_DITCH_OPTIONS, x as getMidpointPerpendicularSignedDistance, xn as PrincipalDirectionOfFireOptions, xt as AntitankWallOptions, y as computeDefaultMidpointPerpendicularPoint, yn as FinalProtectiveFireOptions, yt as DEFAULT_FORTIFIED_LINE_OPTIONS, z as ControlMeasureRender, zt as AttackByFireOptions } from "./index-BHWESyWA.mjs";
2
- export { type AirborneAttackOptions, type AmbushOptions, type AnchorTransformEvent, type AntitankDitchOptions, type AntitankWallOptions, type AttackByFireOptions, type AttackHelicopterOptions, type BaselineFrame, type BaselineFrameNormal, type BaselineFrameOptions, type BaselineFrameOrigin, type BattlePositionOptions, type BlockArrowHeadStyle, type BlockArrowOptions, type BlockMissionTaskOptions, type BlockOptions, type BoundaryOptions, type BreachOptions, type BypassOptions, CONTROL_MEASURE_IDS, CONTROL_MEASURE_METADATA, type CanalizeOptions, type ClassicArrowHeadStyle, type ClassicArrowOptions, type ClearOptions, type ControlMeasure, type ControlMeasureDrawRule, type ControlMeasureGeometry, type ControlMeasureGeometryType, type ControlMeasureId, type ControlMeasureKind, type ControlMeasureMetadata, type ControlMeasureRender, type ControlMeasureSnapshot, type ControlMeasureStyle, DEFAULT_AIRBORNE_ATTACK_OPTIONS, DEFAULT_AMBUSH_OPTIONS, DEFAULT_ANTITANK_DITCH_OPTIONS, DEFAULT_ANTITANK_WALL_OPTIONS, DEFAULT_ATTACK_BY_FIRE_OPTIONS, DEFAULT_ATTACK_HELICOPTER_OPTIONS, DEFAULT_BATTLE_POSITION_OPTIONS, DEFAULT_BLOCK_ARROW_OPTIONS, DEFAULT_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BOUNDARY_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_OPTIONS, DEFAULT_CLASSIC_ARROW_OPTIONS, DEFAULT_CLEAR_OPTIONS, DEFAULT_DELAY_OPTIONS, DEFAULT_DISRUPT_OPTIONS, DEFAULT_ENCIRCLEMENT_OPTIONS, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, DEFAULT_FIX_OPTIONS, DEFAULT_FLOT_OPTIONS, DEFAULT_FORTIFIED_AREA_OPTIONS, DEFAULT_FORTIFIED_LINE_OPTIONS, DEFAULT_GENERIC_CIRCLE_OPTIONS, DEFAULT_GENERIC_LINE_OPTIONS, DEFAULT_GENERIC_POLYGON_OPTIONS, DEFAULT_GENERIC_RECTANGLE_OPTIONS, DEFAULT_MAIN_ATTACK_OPTIONS, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, DEFAULT_STRONG_POINT_OPTIONS, DEFAULT_SUPPORTING_ATTACK_OPTIONS, DEFAULT_SUPPORT_BY_FIRE_OPTIONS, DEFAULT_TACTICAL_ARROW_OPTIONS, DEFAULT_TURN_OPTIONS, type DelayOptions, type DisruptOptions, EPSILON, type EncirclementOptions, type FLOTOptions, type FeaturePartProps, type FinalProtectiveFireOptions, type FixOptions, type FortifiedAreaOptions, type FortifiedLineOptions, type GenericCircleOptions, type GenericLineOptions, type GenericPolygonOptions, type GenericRectangleOptions, type MainAttackOptions, type MidpointPerpendicularDrawRuleOptions, type ObstacleBypassDifficultOptions, type ObstacleBypassEasyOptions, type ObstacleBypassImpossibleOptions, type OptionsByKind, type ParamDescriptor, type Point2D, type PrincipalDirectionOfFireOptions, type RenderOptions, type SimpleStyleProps, type SimpleStyleRender, type StrongPointOptions, type StyleHints, type SupportByFireOptions, type SupportingAttackOptions, type TacticalArrowOptions, type TurnOptions, ambushDrawRule, attackByFireDrawRule, axis1DrawRule, blockDrawRule, calculateMetrics, centerRadiusDrawRule, cloneControlMeasure, computeDefaultMidpointPerpendicularPoint, computeInitialWidthPoint, controlMeasureIdFromFeature, createBaselineFrame, createMidpointPerpendicularDrawRule, disruptDrawRule, freezeOrientationOptions, getControlMeasureMetadata, getControlMeasureMetadataByValue, getDefaultOptions, getMetersPerPixel, getMidpointPerpendicularSignedDistance, isKind, line1DrawRule, line23DrawRule, line24DrawRule, listControlMeasureMetadata, point12DrawRule, pointOnMidpointPerpendicularAxis, project, rectangleDrawRule, renderControlMeasure, resolveStyleHints, roundToFixed, snapToMidpointPerpendicular, supportByFireDrawRule, toSimpleStyle, turnDrawRule, unproject };
1
+ import { $ as getControlMeasureMetadata, $t as DEFAULT_GENERIC_LINE_OPTIONS, A as getMetersPerPixel, An as ControlMeasureMetadata, At as DEFAULT_CANALIZE_OPTIONS, B as ControlMeasureSnapshot, Bn as AnchorTransformEvent, Bt as DEFAULT_ATTACK_BY_FIRE_OPTIONS, C as snapToMidpointPerpendicular, Cn as StrongPointOptions, Ct as AntitankDitchOptions, D as BaselineFrameOrigin, Dn as DEFAULT_BOUNDARY_OPTIONS, Dt as ClearOptions, E as BaselineFrameOptions, En as BoundaryOptions, Et as DelayOptions, F as resolveStyleHints, Fn as TextAmplifierField, Ft as BlockMissionTaskOptions, G as cloneControlMeasure, Gt as AirborneAttackOptions, H as StyleHints, Ht as SupportByFireOptions, I as freezeOrientationOptions, In as TextAmplifierKey, It as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, J as CONTROL_MEASURE_IDS, Jt as GenericCircleOptions, K as isKind, Kt as DEFAULT_AIRBORNE_ATTACK_OPTIONS, L as RenderOptions, Ln as TextAmplifiers, Lt as DEFAULT_FLOT_OPTIONS, M as SimpleStyleProps, Mn as TextAmplifierDescriptor, Mt as DEFAULT_BYPASS_OPTIONS, N as SimpleStyleRender, Nn as CanonicalTextAmplifiers, Nt as BreachOptions, O as createBaselineFrame, On as ControlMeasureGeometry, Ot as DEFAULT_CLEAR_OPTIONS, P as toSimpleStyle, Pn as TEXT_AMPLIFIER_FIELDS, Pt as DEFAULT_BREACH_OPTIONS, Q as OptionsByKind, Qt as GenericPolygonOptions, R as renderControlMeasure, Rn as canonicalTextAmplifierKey, Rt as FLOTOptions, S as pointOnMidpointPerpendicularAxis, Sn as DEFAULT_STRONG_POINT_OPTIONS, St as DEFAULT_ANTITANK_WALL_OPTIONS, T as BaselineFrameNormal, Tn as DEFAULT_BATTLE_POSITION_OPTIONS, Tt as DEFAULT_DELAY_OPTIONS, U as controlMeasureIdFromFeature, Ut as AttackHelicopterOptions, V as FeaturePartProps, Vn as ControlMeasureDrawRule, Vt as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, W as ControlMeasure, Wt as DEFAULT_ATTACK_HELICOPTER_OPTIONS, X as ControlMeasureId, Xt as GenericRectangleOptions, Y as CONTROL_MEASURE_METADATA, Yt as DEFAULT_GENERIC_RECTANGLE_OPTIONS, Z as ControlMeasureKind, Zt as DEFAULT_GENERIC_POLYGON_OPTIONS, _ as blockDrawRule, _n as EncirclementOptions, _t as DEFAULT_FORTIFIED_AREA_OPTIONS, a as rectangleDrawRule, an as ClassicArrowOptions, at as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, b as createMidpointPerpendicularDrawRule, bn as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, bt as FortifiedLineOptions, c as line24DrawRule, cn as SupportingAttackOptions, ct as ObstacleBypassEasyOptions, d as line1DrawRule, dn as calculateMetrics, dt as DEFAULT_FIX_OPTIONS, en as GenericLineOptions, et as getControlMeasureMetadataByValue, f as ambushDrawRule, fn as computeInitialWidthPoint, ft as FixOptions, g as disruptDrawRule, gn as DEFAULT_ENCIRCLEMENT_OPTIONS, gt as DEFAULT_BLOCK_OPTIONS, h as centerRadiusDrawRule, hn as unproject, ht as BlockOptions, i as DEFAULT_AMBUSH_OPTIONS, in as ClassicArrowHeadStyle, it as ObstacleBypassImpossibleOptions, j as roundToFixed, jn as ParamDescriptor, jt as BypassOptions, k as EPSILON, kn as ControlMeasureGeometryType, kt as CanalizeOptions, l as line23DrawRule, ln as DEFAULT_MAIN_ATTACK_OPTIONS, lt as DEFAULT_TURN_OPTIONS, m as point12DrawRule, mn as project, mt as DisruptOptions, n as TacticalArrowOptions, nn as BlockArrowOptions, nt as listControlMeasureMetadata, o as axis1DrawRule, on as DEFAULT_CLASSIC_ARROW_OPTIONS, ot as ObstacleBypassDifficultOptions, p as attackByFireDrawRule, pn as Point2D, pt as DEFAULT_DISRUPT_OPTIONS, q as ControlMeasureStyle, qt as DEFAULT_GENERIC_CIRCLE_OPTIONS, r as AmbushOptions, rn as DEFAULT_BLOCK_ARROW_OPTIONS, rt as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, s as supportByFireDrawRule, sn as DEFAULT_SUPPORTING_ATTACK_OPTIONS, st as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, t as DEFAULT_TACTICAL_ARROW_OPTIONS, tn as BlockArrowHeadStyle, tt as getDefaultOptions, u as turnDrawRule, un as MainAttackOptions, ut as TurnOptions, v as MidpointPerpendicularDrawRuleOptions, vn as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, vt as FortifiedAreaOptions, w as BaselineFrame, wn as BattlePositionOptions, wt as DEFAULT_ANTITANK_DITCH_OPTIONS, x as getMidpointPerpendicularSignedDistance, xn as PrincipalDirectionOfFireOptions, xt as AntitankWallOptions, y as computeDefaultMidpointPerpendicularPoint, yn as FinalProtectiveFireOptions, yt as DEFAULT_FORTIFIED_LINE_OPTIONS, z as ControlMeasureRender, zn as normalizeTextAmplifiers, zt as AttackByFireOptions } from "./index-26Xb77jE.mjs";
2
+ export { type AirborneAttackOptions, type AmbushOptions, type AnchorTransformEvent, type AntitankDitchOptions, type AntitankWallOptions, type AttackByFireOptions, type AttackHelicopterOptions, type BaselineFrame, type BaselineFrameNormal, type BaselineFrameOptions, type BaselineFrameOrigin, type BattlePositionOptions, type BlockArrowHeadStyle, type BlockArrowOptions, type BlockMissionTaskOptions, type BlockOptions, type BoundaryOptions, type BreachOptions, type BypassOptions, CONTROL_MEASURE_IDS, CONTROL_MEASURE_METADATA, type CanalizeOptions, type CanonicalTextAmplifiers, type ClassicArrowHeadStyle, type ClassicArrowOptions, type ClearOptions, type ControlMeasure, type ControlMeasureDrawRule, type ControlMeasureGeometry, type ControlMeasureGeometryType, type ControlMeasureId, type ControlMeasureKind, type ControlMeasureMetadata, type ControlMeasureRender, type ControlMeasureSnapshot, type ControlMeasureStyle, DEFAULT_AIRBORNE_ATTACK_OPTIONS, DEFAULT_AMBUSH_OPTIONS, DEFAULT_ANTITANK_DITCH_OPTIONS, DEFAULT_ANTITANK_WALL_OPTIONS, DEFAULT_ATTACK_BY_FIRE_OPTIONS, DEFAULT_ATTACK_HELICOPTER_OPTIONS, DEFAULT_BATTLE_POSITION_OPTIONS, DEFAULT_BLOCK_ARROW_OPTIONS, DEFAULT_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BOUNDARY_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_OPTIONS, DEFAULT_CLASSIC_ARROW_OPTIONS, DEFAULT_CLEAR_OPTIONS, DEFAULT_DELAY_OPTIONS, DEFAULT_DISRUPT_OPTIONS, DEFAULT_ENCIRCLEMENT_OPTIONS, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, DEFAULT_FIX_OPTIONS, DEFAULT_FLOT_OPTIONS, DEFAULT_FORTIFIED_AREA_OPTIONS, DEFAULT_FORTIFIED_LINE_OPTIONS, DEFAULT_GENERIC_CIRCLE_OPTIONS, DEFAULT_GENERIC_LINE_OPTIONS, DEFAULT_GENERIC_POLYGON_OPTIONS, DEFAULT_GENERIC_RECTANGLE_OPTIONS, DEFAULT_MAIN_ATTACK_OPTIONS, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, DEFAULT_STRONG_POINT_OPTIONS, DEFAULT_SUPPORTING_ATTACK_OPTIONS, DEFAULT_SUPPORT_BY_FIRE_OPTIONS, DEFAULT_TACTICAL_ARROW_OPTIONS, DEFAULT_TURN_OPTIONS, type DelayOptions, type DisruptOptions, EPSILON, type EncirclementOptions, type FLOTOptions, type FeaturePartProps, type FinalProtectiveFireOptions, type FixOptions, type FortifiedAreaOptions, type FortifiedLineOptions, type GenericCircleOptions, type GenericLineOptions, type GenericPolygonOptions, type GenericRectangleOptions, type MainAttackOptions, type MidpointPerpendicularDrawRuleOptions, type ObstacleBypassDifficultOptions, type ObstacleBypassEasyOptions, type ObstacleBypassImpossibleOptions, type OptionsByKind, type ParamDescriptor, type Point2D, type PrincipalDirectionOfFireOptions, type RenderOptions, type SimpleStyleProps, type SimpleStyleRender, type StrongPointOptions, type StyleHints, type SupportByFireOptions, type SupportingAttackOptions, TEXT_AMPLIFIER_FIELDS, type TacticalArrowOptions, type TextAmplifierDescriptor, type TextAmplifierField, type TextAmplifierKey, type TextAmplifiers, type TurnOptions, ambushDrawRule, attackByFireDrawRule, axis1DrawRule, blockDrawRule, calculateMetrics, canonicalTextAmplifierKey, centerRadiusDrawRule, cloneControlMeasure, computeDefaultMidpointPerpendicularPoint, computeInitialWidthPoint, controlMeasureIdFromFeature, createBaselineFrame, createMidpointPerpendicularDrawRule, disruptDrawRule, freezeOrientationOptions, getControlMeasureMetadata, getControlMeasureMetadataByValue, getDefaultOptions, getMetersPerPixel, getMidpointPerpendicularSignedDistance, isKind, line1DrawRule, line23DrawRule, line24DrawRule, listControlMeasureMetadata, normalizeTextAmplifiers, point12DrawRule, pointOnMidpointPerpendicularAxis, project, rectangleDrawRule, renderControlMeasure, resolveStyleHints, roundToFixed, snapToMidpointPerpendicular, supportByFireDrawRule, toSimpleStyle, turnDrawRule, unproject };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as turnDrawRule, A as DEFAULT_GENERIC_POLYGON_OPTIONS, B as DEFAULT_BLOCK_OPTIONS, C as DEFAULT_FIX_OPTIONS, D as DEFAULT_DELAY_OPTIONS, E as DEFAULT_DISRUPT_OPTIONS, F as DEFAULT_BYPASS_OPTIONS, G as DEFAULT_ANTITANK_DITCH_OPTIONS, H as DEFAULT_ATTACK_HELICOPTER_OPTIONS, I as DEFAULT_BREACH_OPTIONS, J as rectangleDrawRule, K as DEFAULT_AMBUSH_OPTIONS, L as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, M as DEFAULT_GENERIC_CIRCLE_OPTIONS, N as DEFAULT_CLASSIC_ARROW_OPTIONS, O as DEFAULT_CLEAR_OPTIONS, P as DEFAULT_CANALIZE_OPTIONS, Q as line23DrawRule, R as DEFAULT_BOUNDARY_OPTIONS, S as DEFAULT_FLOT_OPTIONS, T as DEFAULT_ENCIRCLEMENT_OPTIONS, U as DEFAULT_ATTACK_BY_FIRE_OPTIONS, V as DEFAULT_BATTLE_POSITION_OPTIONS, W as DEFAULT_ANTITANK_WALL_OPTIONS, X as supportByFireDrawRule, Y as axis1DrawRule, Z as line24DrawRule, _ as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, _t as EPSILON, a as DEFINITIONS, at as disruptDrawRule, b as DEFAULT_FORTIFIED_AREA_OPTIONS, c as getDefaultOptions, ct as createMidpointPerpendicularDrawRule, d as DEFAULT_SUPPORTING_ATTACK_OPTIONS, dt as snapToMidpointPerpendicular, et as line1DrawRule, f as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, ft as createBaselineFrame, g as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, gt as unproject, h as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, ht as project, i as CONTROL_MEASURE_METADATA, it as centerRadiusDrawRule, j as DEFAULT_GENERIC_LINE_OPTIONS, k as DEFAULT_GENERIC_RECTANGLE_OPTIONS, l as listControlMeasureMetadata, lt as getMidpointPerpendicularSignedDistance, m as DEFAULT_TACTICAL_ARROW_OPTIONS, mt as computeInitialWidthPoint, n as resolveStyleHints, nt as attackByFireDrawRule, o as getControlMeasureMetadata, ot as blockDrawRule, p as DEFAULT_STRONG_POINT_OPTIONS, pt as calculateMetrics, q as DEFAULT_AIRBORNE_ATTACK_OPTIONS, r as CONTROL_MEASURE_IDS, rt as point12DrawRule, s as getControlMeasureMetadataByValue, st as computeDefaultMidpointPerpendicularPoint, t as renderControlMeasure, tt as ambushDrawRule, u as DEFAULT_TURN_OPTIONS, ut as pointOnMidpointPerpendicularAxis, v as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, vt as getMetersPerPixel, w as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, x as DEFAULT_FORTIFIED_LINE_OPTIONS, y as DEFAULT_MAIN_ATTACK_OPTIONS, yt as roundToFixed, z as DEFAULT_BLOCK_ARROW_OPTIONS } from "./renderControlMeasure-DnM2w9jS.mjs";
1
+ import { $ as supportByFireDrawRule, A as DEFAULT_DELAY_OPTIONS, B as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, C as DEFAULT_FORTIFIED_AREA_OPTIONS, D as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, E as DEFAULT_FIX_OPTIONS, F as DEFAULT_GENERIC_CIRCLE_OPTIONS, G as DEFAULT_ATTACK_HELICOPTER_OPTIONS, H as DEFAULT_BLOCK_ARROW_OPTIONS, I as DEFAULT_CLASSIC_ARROW_OPTIONS, J as DEFAULT_ANTITANK_DITCH_OPTIONS, K as DEFAULT_ATTACK_BY_FIRE_OPTIONS, L as DEFAULT_CANALIZE_OPTIONS, M as DEFAULT_GENERIC_RECTANGLE_OPTIONS, N as DEFAULT_GENERIC_POLYGON_OPTIONS, O as DEFAULT_ENCIRCLEMENT_OPTIONS, P as DEFAULT_GENERIC_LINE_OPTIONS, Q as axis1DrawRule, R as DEFAULT_BYPASS_OPTIONS, S as DEFAULT_MAIN_ATTACK_OPTIONS, St as roundToFixed, T as DEFAULT_FLOT_OPTIONS, U as DEFAULT_BLOCK_OPTIONS, V as DEFAULT_BOUNDARY_OPTIONS, W as DEFAULT_BATTLE_POSITION_OPTIONS, X as DEFAULT_AIRBORNE_ATTACK_OPTIONS, Y as DEFAULT_AMBUSH_OPTIONS, Z as rectangleDrawRule, _ as DEFAULT_TACTICAL_ARROW_OPTIONS, _t as computeInitialWidthPoint, a as resolveStyleHints, at as attackByFireDrawRule, b as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, bt as EPSILON, c as DEFINITIONS, ct as disruptDrawRule, d as getDefaultOptions, dt as createMidpointPerpendicularDrawRule, et as line24DrawRule, f as listControlMeasureMetadata, ft as getMidpointPerpendicularSignedDistance, g as DEFAULT_STRONG_POINT_OPTIONS, gt as calculateMetrics, h as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, ht as createBaselineFrame, i as normalizeTextAmplifiers, it as ambushDrawRule, j as DEFAULT_CLEAR_OPTIONS, k as DEFAULT_DISRUPT_OPTIONS, l as getControlMeasureMetadata, lt as blockDrawRule, m as DEFAULT_SUPPORTING_ATTACK_OPTIONS, mt as snapToMidpointPerpendicular, n as TEXT_AMPLIFIER_FIELDS, nt as turnDrawRule, o as CONTROL_MEASURE_IDS, ot as point12DrawRule, p as DEFAULT_TURN_OPTIONS, pt as pointOnMidpointPerpendicularAxis, q as DEFAULT_ANTITANK_WALL_OPTIONS, r as canonicalTextAmplifierKey, rt as line1DrawRule, s as CONTROL_MEASURE_METADATA, st as centerRadiusDrawRule, t as renderControlMeasure, tt as line23DrawRule, u as getControlMeasureMetadataByValue, ut as computeDefaultMidpointPerpendicularPoint, v as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, vt as project, w as DEFAULT_FORTIFIED_LINE_OPTIONS, x as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, xt as getMetersPerPixel, y as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, yt as unproject, z as DEFAULT_BREACH_OPTIONS } from "./renderControlMeasure-CFdgTURO.mjs";
2
2
  //#region src/freeze-orientation.ts
3
3
  /**
4
4
  * Dispatches to a measure kind's `freezeOrientation` hook (see
@@ -22,9 +22,9 @@ function isKind(cm, kind) {
22
22
  * Deep-clone a `ControlMeasure` so held references survive mutations to the
23
23
  * original (and vice versa). Used by the session façade to build the
24
24
  * `measure` half of `ControlMeasureSnapshot`. Nested values inside
25
- * `options`, `style`, and `properties` are cloned too — `properties` is
26
- * `Record<string, unknown>` and may carry arbitrary host metadata, so a
27
- * shallow copy would leak shared references.
25
+ * `options`, `style`, `properties`, and `textAmplifiers` are cloned too —
26
+ * `properties` is `Record<string, unknown>` and may carry arbitrary host
27
+ * metadata, so a shallow copy would leak shared references.
28
28
  *
29
29
  * Backed by `structuredClone`: only structured-cloneable values are
30
30
  * supported (no functions, DOM nodes, class instances). Absent optional
@@ -114,4 +114,4 @@ function toHexChannel(value) {
114
114
  return Math.max(0, Math.min(255, Math.round(value))).toString(16).padStart(2, "0");
115
115
  }
116
116
  //#endregion
117
- export { CONTROL_MEASURE_IDS, CONTROL_MEASURE_METADATA, DEFAULT_AIRBORNE_ATTACK_OPTIONS, DEFAULT_AMBUSH_OPTIONS, DEFAULT_ANTITANK_DITCH_OPTIONS, DEFAULT_ANTITANK_WALL_OPTIONS, DEFAULT_ATTACK_BY_FIRE_OPTIONS, DEFAULT_ATTACK_HELICOPTER_OPTIONS, DEFAULT_BATTLE_POSITION_OPTIONS, DEFAULT_BLOCK_ARROW_OPTIONS, DEFAULT_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BOUNDARY_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_OPTIONS, DEFAULT_CLASSIC_ARROW_OPTIONS, DEFAULT_CLEAR_OPTIONS, DEFAULT_DELAY_OPTIONS, DEFAULT_DISRUPT_OPTIONS, DEFAULT_ENCIRCLEMENT_OPTIONS, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, DEFAULT_FIX_OPTIONS, DEFAULT_FLOT_OPTIONS, DEFAULT_FORTIFIED_AREA_OPTIONS, DEFAULT_FORTIFIED_LINE_OPTIONS, DEFAULT_GENERIC_CIRCLE_OPTIONS, DEFAULT_GENERIC_LINE_OPTIONS, DEFAULT_GENERIC_POLYGON_OPTIONS, DEFAULT_GENERIC_RECTANGLE_OPTIONS, DEFAULT_MAIN_ATTACK_OPTIONS, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, DEFAULT_STRONG_POINT_OPTIONS, DEFAULT_SUPPORTING_ATTACK_OPTIONS, DEFAULT_SUPPORT_BY_FIRE_OPTIONS, DEFAULT_TACTICAL_ARROW_OPTIONS, DEFAULT_TURN_OPTIONS, EPSILON, ambushDrawRule, attackByFireDrawRule, axis1DrawRule, blockDrawRule, calculateMetrics, centerRadiusDrawRule, cloneControlMeasure, computeDefaultMidpointPerpendicularPoint, computeInitialWidthPoint, controlMeasureIdFromFeature, createBaselineFrame, createMidpointPerpendicularDrawRule, disruptDrawRule, freezeOrientationOptions, getControlMeasureMetadata, getControlMeasureMetadataByValue, getDefaultOptions, getMetersPerPixel, getMidpointPerpendicularSignedDistance, isKind, line1DrawRule, line23DrawRule, line24DrawRule, listControlMeasureMetadata, point12DrawRule, pointOnMidpointPerpendicularAxis, project, rectangleDrawRule, renderControlMeasure, resolveStyleHints, roundToFixed, snapToMidpointPerpendicular, supportByFireDrawRule, toSimpleStyle, turnDrawRule, unproject };
117
+ export { CONTROL_MEASURE_IDS, CONTROL_MEASURE_METADATA, DEFAULT_AIRBORNE_ATTACK_OPTIONS, DEFAULT_AMBUSH_OPTIONS, DEFAULT_ANTITANK_DITCH_OPTIONS, DEFAULT_ANTITANK_WALL_OPTIONS, DEFAULT_ATTACK_BY_FIRE_OPTIONS, DEFAULT_ATTACK_HELICOPTER_OPTIONS, DEFAULT_BATTLE_POSITION_OPTIONS, DEFAULT_BLOCK_ARROW_OPTIONS, DEFAULT_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BOUNDARY_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_OPTIONS, DEFAULT_CLASSIC_ARROW_OPTIONS, DEFAULT_CLEAR_OPTIONS, DEFAULT_DELAY_OPTIONS, DEFAULT_DISRUPT_OPTIONS, DEFAULT_ENCIRCLEMENT_OPTIONS, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, DEFAULT_FIX_OPTIONS, DEFAULT_FLOT_OPTIONS, DEFAULT_FORTIFIED_AREA_OPTIONS, DEFAULT_FORTIFIED_LINE_OPTIONS, DEFAULT_GENERIC_CIRCLE_OPTIONS, DEFAULT_GENERIC_LINE_OPTIONS, DEFAULT_GENERIC_POLYGON_OPTIONS, DEFAULT_GENERIC_RECTANGLE_OPTIONS, DEFAULT_MAIN_ATTACK_OPTIONS, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, DEFAULT_STRONG_POINT_OPTIONS, DEFAULT_SUPPORTING_ATTACK_OPTIONS, DEFAULT_SUPPORT_BY_FIRE_OPTIONS, DEFAULT_TACTICAL_ARROW_OPTIONS, DEFAULT_TURN_OPTIONS, EPSILON, TEXT_AMPLIFIER_FIELDS, ambushDrawRule, attackByFireDrawRule, axis1DrawRule, blockDrawRule, calculateMetrics, canonicalTextAmplifierKey, centerRadiusDrawRule, cloneControlMeasure, computeDefaultMidpointPerpendicularPoint, computeInitialWidthPoint, controlMeasureIdFromFeature, createBaselineFrame, createMidpointPerpendicularDrawRule, disruptDrawRule, freezeOrientationOptions, getControlMeasureMetadata, getControlMeasureMetadataByValue, getDefaultOptions, getMetersPerPixel, getMidpointPerpendicularSignedDistance, isKind, line1DrawRule, line23DrawRule, line24DrawRule, listControlMeasureMetadata, normalizeTextAmplifiers, point12DrawRule, pointOnMidpointPerpendicularAxis, project, rectangleDrawRule, renderControlMeasure, resolveStyleHints, roundToFixed, snapToMidpointPerpendicular, supportByFireDrawRule, toSimpleStyle, turnDrawRule, unproject };
@@ -1,4 +1,4 @@
1
- import { X as ControlMeasureId } from "../index-BHWESyWA.mjs";
1
+ import { X as ControlMeasureId } from "../index-26Xb77jE.mjs";
2
2
  import { FeatureCollection, Geometry } from "geojson";
3
3
 
4
4
  //#region src/preview/index.d.ts
@@ -1,4 +1,4 @@
1
- import { a as DEFINITIONS, t as renderControlMeasure } from "../renderControlMeasure-DnM2w9jS.mjs";
1
+ import { c as DEFINITIONS, t as renderControlMeasure } from "../renderControlMeasure-CFdgTURO.mjs";
2
2
  //#region src/preview/index.ts
3
3
  /**
4
4
  * Unitless `previewSample` points (~`[-1, 1]`) are scaled by this degree offset
@@ -3006,10 +3006,8 @@ const DEFAULT_BOUNDARY_OPTIONS = {
3006
3006
  echelonPadding: .3,
3007
3007
  labelRepetitions: 1,
3008
3008
  labelSpacing: 1,
3009
- unit1Designator: "",
3010
- unit1Country: "",
3011
- unit2Designator: "",
3012
- unit2Country: "",
3009
+ labelPadding: .4,
3010
+ labelOrientation: "along",
3013
3011
  smooth: false,
3014
3012
  smoothResolution: DEFAULT_SMOOTH_RESOLUTION$4
3015
3013
  };
@@ -3100,6 +3098,54 @@ const BOUNDARY_METADATA = {
3100
3098
  max: 2,
3101
3099
  step: .05,
3102
3100
  visibleWhen: (opts) => Number(opts.labelRepetitions ?? 1) > 1
3101
+ },
3102
+ {
3103
+ key: "labelPadding",
3104
+ label: "Label padding",
3105
+ description: "Gap between the line/echelon and the unit labels, as a ratio of the echelon height",
3106
+ type: "number",
3107
+ min: 0,
3108
+ max: 2,
3109
+ step: .05
3110
+ },
3111
+ {
3112
+ key: "labelOrientation",
3113
+ label: "Label orientation",
3114
+ description: "Unit labels along the line, or per MIL-STD-2525E Figure L-10: on mostly north–south segments the label group rotates perpendicular to the line as a single row crossing it.",
3115
+ type: "enum",
3116
+ options: [{
3117
+ value: "along",
3118
+ label: "Along the line"
3119
+ }, {
3120
+ value: "standard",
3121
+ label: "Standard (MIL-STD-2525E)"
3122
+ }]
3123
+ }
3124
+ ],
3125
+ textAmplifiers: [
3126
+ {
3127
+ key: "T",
3128
+ label: "Unit designation",
3129
+ description: "Field T — unique designation of the unit on the left-of-travel side.",
3130
+ maxLength: 32
3131
+ },
3132
+ {
3133
+ key: "AS",
3134
+ label: "Country",
3135
+ description: "Field AS — country code of the unit on the left-of-travel side.",
3136
+ maxLength: 8
3137
+ },
3138
+ {
3139
+ key: "T1",
3140
+ label: "Unit designation (other side)",
3141
+ description: "Field T1 — unique designation of the unit on the right-of-travel side.",
3142
+ maxLength: 32
3143
+ },
3144
+ {
3145
+ key: "AS1",
3146
+ label: "Country (other side)",
3147
+ description: "Field AS1 — country code of the unit on the right-of-travel side.",
3148
+ maxLength: 8
3103
3149
  }
3104
3150
  ]
3105
3151
  };
@@ -3114,30 +3160,33 @@ function labelRotationAlong(dir) {
3114
3160
  const rendered = -Math.atan2(dir[1], dir[0]);
3115
3161
  return normalizeRadians(Math.PI - keepTextLeftToRight(rendered));
3116
3162
  }
3117
- /** Emits the T (designator) and AS (country) labels for both unit sides. */
3163
+ /**
3164
+ * Emits a single combined `T (AS)` label per unit side (APP-6/MIL-STD-2525):
3165
+ * designator and country code share one text row rather than stacking.
3166
+ */
3118
3167
  function pushUnitLabels(out, frame, units) {
3119
- const { center, perp, h, rotation, labelSize } = frame;
3120
- const designatorOffset = h * .85;
3121
- const countryOffset = designatorOffset + h * 1.1;
3122
- const label = (side, offset, text) => {
3168
+ const { center, perp, offset, rotation, labelSize } = frame;
3169
+ for (const { side, designator, country, textAnchor } of units) {
3170
+ let text;
3171
+ if (designator.length > 0 && country.length > 0) text = `${designator} (${country})`;
3172
+ else if (designator.length > 0) text = designator;
3173
+ else if (country.length > 0) text = `(${country})`;
3174
+ else continue;
3123
3175
  const q = vecAdd(center, vecScale(perp, side * offset));
3124
- return {
3176
+ out.push({
3125
3177
  type: "Feature",
3126
3178
  properties: {
3127
3179
  part: "label",
3128
3180
  text,
3129
3181
  rotation,
3182
+ ...textAnchor ? { textAnchor } : {},
3130
3183
  ...labelSize
3131
3184
  },
3132
3185
  geometry: {
3133
3186
  type: "Point",
3134
3187
  coordinates: unproject(q[0], q[1])
3135
3188
  }
3136
- };
3137
- };
3138
- for (const { side, designator, country } of units) {
3139
- if (designator.length > 0) out.push(label(side, designatorOffset, designator));
3140
- if (country.length > 0) out.push(label(side, countryOffset, country));
3189
+ });
3141
3190
  }
3142
3191
  }
3143
3192
  /**
@@ -3147,9 +3196,15 @@ function pushUnitLabels(out, frame, units) {
3147
3196
  * @param positions - Boundary anchor points (≥2); the input contract is
3148
3197
  * enforced at the render seam (ADR-0014).
3149
3198
  * @param options - {@link BoundaryOptions}.
3199
+ * @param textAmplifiers - Normalized text amplifiers (ADR-0027): `T`/`AS` label
3200
+ * unit 1 (left-of-travel side), `T1`/`AS1` label unit 2 (right-of-travel side).
3150
3201
  */
3151
- function createBoundary(positions, options = {}) {
3152
- const { echelon = DEFAULT_BOUNDARY_OPTIONS.echelon, echelonSize = DEFAULT_BOUNDARY_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_BOUNDARY_OPTIONS.echelonPadding, metersPerPixel, labelSizeZoom, labelSizeResolution, labelRepetitions = DEFAULT_BOUNDARY_OPTIONS.labelRepetitions, labelSpacing = DEFAULT_BOUNDARY_OPTIONS.labelSpacing, unit1Designator = "", unit1Country = "", unit2Designator = "", unit2Country = "", smooth = DEFAULT_BOUNDARY_OPTIONS.smooth, smoothResolution = DEFAULT_BOUNDARY_OPTIONS.smoothResolution } = options;
3202
+ function createBoundary(positions, options = {}, textAmplifiers = {}) {
3203
+ const { echelon = DEFAULT_BOUNDARY_OPTIONS.echelon, echelonSize = DEFAULT_BOUNDARY_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_BOUNDARY_OPTIONS.echelonPadding, metersPerPixel, labelSizeZoom, labelSizeResolution, labelRepetitions = DEFAULT_BOUNDARY_OPTIONS.labelRepetitions, labelSpacing = DEFAULT_BOUNDARY_OPTIONS.labelSpacing, labelPadding = DEFAULT_BOUNDARY_OPTIONS.labelPadding, labelOrientation = DEFAULT_BOUNDARY_OPTIONS.labelOrientation, smooth = DEFAULT_BOUNDARY_OPTIONS.smooth, smoothResolution = DEFAULT_BOUNDARY_OPTIONS.smoothResolution } = options;
3204
+ const unit1Designator = textAmplifiers.T ?? "";
3205
+ const unit1Country = textAmplifiers.AS ?? "";
3206
+ const unit2Designator = textAmplifiers.T1 ?? "";
3207
+ const unit2Country = textAmplifiers.AS1 ?? "";
3153
3208
  let h = echelonSize;
3154
3209
  if (echelonSizePixels !== void 0 && metersPerPixel !== void 0 && metersPerPixel > 0) h = echelonSizePixels * metersPerPixel;
3155
3210
  h = Math.max(EPSILON, h);
@@ -3199,32 +3254,50 @@ function createBoundary(positions, options = {}) {
3199
3254
  }
3200
3255
  const d = Math.max(0, Math.min(seg.length, target - acc));
3201
3256
  const center = vecAdd(seg.start, vecScale(seg.along, d));
3202
- const rotation = labelRotationAlong(seg.along);
3257
+ const units = [{
3258
+ side: 1,
3259
+ designator: unit1Designator,
3260
+ country: unit1Country
3261
+ }, {
3262
+ side: -1,
3263
+ designator: unit2Designator,
3264
+ country: unit2Country
3265
+ }];
3266
+ const rotateGroup = labelOrientation === "standard" && Math.abs(seg.along[0]) < Math.abs(seg.along[1]);
3267
+ const readingAxis = seg.perp[0] >= 0 ? seg.perp : vecScale(seg.perp, -1);
3268
+ const glyphAlong = rotateGroup ? readingAxis : seg.along;
3269
+ const glyphPerp = rotateGroup ? [-readingAxis[1], readingAxis[0]] : seg.perp;
3270
+ let glyphWidth = 0;
3203
3271
  if (spec) {
3204
- const { strokes, fills, width } = buildEchelonGlyph(center, seg.along, seg.perp, h, spec);
3272
+ const { strokes, fills, width } = buildEchelonGlyph(center, glyphAlong, glyphPerp, h, spec);
3273
+ glyphWidth = width;
3205
3274
  glyphStrokes.push(...strokes);
3206
3275
  glyphFills.push(...fills);
3207
- const halfGap = width / 2 + Math.max(0, echelonPadding) * h;
3276
+ const halfGap = (rotateGroup ? h : width) / 2 + Math.max(0, echelonPadding) * h;
3208
3277
  gaps.push({
3209
3278
  g0: target - halfGap,
3210
3279
  g1: target + halfGap
3211
3280
  });
3212
3281
  }
3213
- pushUnitLabels(labelFeatures, {
3282
+ if (rotateGroup) {
3283
+ const sign = vecDot(seg.perp, readingAxis) >= 0 ? 1 : -1;
3284
+ const clearance = glyphWidth / 2 + Math.max(0, labelPadding) * h;
3285
+ units[0].textAnchor = sign > 0 ? "start" : "end";
3286
+ units[1].textAnchor = sign > 0 ? "end" : "start";
3287
+ pushUnitLabels(labelFeatures, {
3288
+ center,
3289
+ perp: vecScale(readingAxis, sign),
3290
+ offset: clearance,
3291
+ rotation: labelRotationAlong(readingAxis),
3292
+ labelSize
3293
+ }, units);
3294
+ } else pushUnitLabels(labelFeatures, {
3214
3295
  center,
3215
3296
  perp: seg.perp,
3216
- h,
3217
- rotation,
3297
+ offset: (.45 + Math.max(0, labelPadding)) * h,
3298
+ rotation: labelRotationAlong(seg.along),
3218
3299
  labelSize
3219
- }, [{
3220
- side: 1,
3221
- designator: unit1Designator,
3222
- country: unit1Country
3223
- }, {
3224
- side: -1,
3225
- designator: unit2Designator,
3226
- country: unit2Country
3227
- }]);
3300
+ }, units);
3228
3301
  }
3229
3302
  const boundaryCoords = buildGappedLine(verts, gaps);
3230
3303
  const features = [];
@@ -7208,6 +7281,127 @@ function applyLayer(target, layer) {
7208
7281
  }
7209
7282
  }
7210
7283
  //#endregion
7284
+ //#region src/text-amplifiers.ts
7285
+ /**
7286
+ * The vocabulary of text amplifier fields: doctrinal code, milsymbol-style
7287
+ * friendly alias, and a human-readable label. The single source of truth for
7288
+ * both the runtime catalog and the derived alias→code lookup used by
7289
+ * {@link normalizeTextAmplifiers}.
7290
+ */
7291
+ const TEXT_AMPLIFIER_FIELDS = {
7292
+ C: {
7293
+ alias: "quantity",
7294
+ label: "Quantity"
7295
+ },
7296
+ H: {
7297
+ alias: "additionalInformation",
7298
+ label: "Additional information"
7299
+ },
7300
+ N: {
7301
+ alias: "hostile",
7302
+ label: "Hostile (ENY) marker"
7303
+ },
7304
+ T: {
7305
+ alias: "uniqueDesignation",
7306
+ label: "Unique designation"
7307
+ },
7308
+ V: {
7309
+ alias: "type",
7310
+ label: "Equipment/type"
7311
+ },
7312
+ W: {
7313
+ alias: "dtg",
7314
+ label: "Date-time group / O/O"
7315
+ },
7316
+ X: {
7317
+ alias: "altitudeDepth",
7318
+ label: "Altitude/depth"
7319
+ },
7320
+ Y: {
7321
+ alias: "location",
7322
+ label: "Location"
7323
+ },
7324
+ AM: {
7325
+ alias: "distance",
7326
+ label: "Distance/range/radius"
7327
+ },
7328
+ AN: {
7329
+ alias: "azimuth",
7330
+ label: "Azimuth"
7331
+ },
7332
+ AP: {
7333
+ alias: "targetNumber",
7334
+ label: "Target number"
7335
+ },
7336
+ AS: {
7337
+ alias: "country",
7338
+ label: "Country code"
7339
+ }
7340
+ };
7341
+ /** Derived alias→code lookup, so the vocabulary table stays the single source of truth. */
7342
+ const CODE_BY_ALIAS = Object.fromEntries(Object.entries(TEXT_AMPLIFIER_FIELDS).map(([code, { alias }]) => [alias, code]));
7343
+ const CANONICAL_KEYS = new Set(Object.keys(TEXT_AMPLIFIER_FIELDS).flatMap((code) => [
7344
+ code,
7345
+ `${code}1`,
7346
+ `${code}2`
7347
+ ]));
7348
+ const EMPTY_TEXT_AMPLIFIERS = Object.freeze({});
7349
+ /** Splits a key like `"country1"` into its base (`"country"`) and suffix (`"1"`). */
7350
+ function splitSuffix(key) {
7351
+ const last = key.charAt(key.length - 1);
7352
+ if (last === "1" || last === "2") return {
7353
+ base: key.slice(0, -1),
7354
+ suffix: last
7355
+ };
7356
+ return {
7357
+ base: key,
7358
+ suffix: ""
7359
+ };
7360
+ }
7361
+ /**
7362
+ * Resolves any accepted key (canonical or friendly alias, with an optional
7363
+ * `1`/`2` suffix) to its canonical key, or `undefined` if unknown — e.g.
7364
+ * `"T"` → `"T"`, `"uniqueDesignation1"` → `"T1"`, `"bogus"` → `undefined`.
7365
+ */
7366
+ function canonicalTextAmplifierKey(key) {
7367
+ if (CANONICAL_KEYS.has(key)) return key;
7368
+ const { base, suffix } = splitSuffix(key);
7369
+ const code = CODE_BY_ALIAS[base];
7370
+ return code ? `${code}${suffix}` : void 0;
7371
+ }
7372
+ /**
7373
+ * Resolves a `TextAmplifiers` record (canonical keys, friendly aliases, or a
7374
+ * mix) to `CanonicalTextAmplifiers` keyed purely by doctrinal field code.
7375
+ *
7376
+ * - Friendly aliases (with an optional `1`/`2` suffix) map to their canonical
7377
+ * code (e.g. `country1` → `AS1`).
7378
+ * - Values are trimmed; entries that are empty/whitespace-only after
7379
+ * trimming, or not strings, are dropped.
7380
+ * - When both a canonical key and its friendly alias are present for the same
7381
+ * field + suffix, the **canonical key wins** regardless of insertion order.
7382
+ * This holds even for an empty/whitespace canonical value: it suppresses
7383
+ * (clears) the alias entry rather than letting the alias survive.
7384
+ * - Unknown keys are ignored.
7385
+ *
7386
+ * `undefined` or an empty input returns a shared frozen empty object.
7387
+ */
7388
+ function normalizeTextAmplifiers(input) {
7389
+ if (!input) return EMPTY_TEXT_AMPLIFIERS;
7390
+ const canonicalPresent = /* @__PURE__ */ new Set();
7391
+ for (const [key, rawValue] of Object.entries(input)) if (typeof rawValue === "string" && CANONICAL_KEYS.has(key)) canonicalPresent.add(key);
7392
+ const result = {};
7393
+ for (const [key, rawValue] of Object.entries(input)) {
7394
+ if (typeof rawValue !== "string") continue;
7395
+ const value = rawValue.trim();
7396
+ if (value.length === 0) continue;
7397
+ const canonicalKey = canonicalTextAmplifierKey(key);
7398
+ if (!canonicalKey) continue;
7399
+ if (!CANONICAL_KEYS.has(key) && canonicalPresent.has(canonicalKey)) continue;
7400
+ result[canonicalKey] = value;
7401
+ }
7402
+ return Object.keys(result).length > 0 ? Object.freeze(result) : EMPTY_TEXT_AMPLIFIERS;
7403
+ }
7404
+ //#endregion
7211
7405
  //#region src/renderControlMeasure.ts
7212
7406
  /**
7213
7407
  * Pure render: `ControlMeasure` → `ControlMeasureRender` (a
@@ -7233,7 +7427,8 @@ function dispatchControlMeasure(cm, opts) {
7233
7427
  const definition = DEFINITIONS[cm.kind];
7234
7428
  if (!validateInputContract(cm.controlPoints, definition.metadata, opts?.validationMode)) return EMPTY_COLLECTION;
7235
7429
  const generator = definition.generator;
7236
- return generator(cm.controlPoints, cm.options ?? {});
7430
+ const textAmplifiers = normalizeTextAmplifiers(cm.textAmplifiers);
7431
+ return generator(cm.controlPoints, cm.options ?? {}, textAmplifiers);
7237
7432
  }
7238
7433
  /**
7239
7434
  * The control-point *input contract*: at least `metadata.minCoordinates` points,
@@ -7259,6 +7454,7 @@ function normalizeFeature(id, feature, index, measureStyle, graphicsStyle) {
7259
7454
  const textSizePixels = typeof sourceProps.textSizePixels === "number" ? sourceProps.textSizePixels : void 0;
7260
7455
  const textSizeZoom = typeof sourceProps.textSizeZoom === "number" ? sourceProps.textSizeZoom : void 0;
7261
7456
  const textSizeResolution = typeof sourceProps.textSizeResolution === "number" ? sourceProps.textSizeResolution : void 0;
7457
+ const textAnchor = sourceProps.textAnchor === "start" || sourceProps.textAnchor === "end" ? sourceProps.textAnchor : void 0;
7262
7458
  return {
7263
7459
  type: "Feature",
7264
7460
  id: `${id}:${part}:${index}`,
@@ -7268,6 +7464,7 @@ function normalizeFeature(id, feature, index, measureStyle, graphicsStyle) {
7268
7464
  style,
7269
7465
  ...text !== void 0 ? { text } : {},
7270
7466
  ...rotation !== void 0 ? { rotation } : {},
7467
+ ...textAnchor !== void 0 ? { textAnchor } : {},
7271
7468
  ...textSizePixels !== void 0 ? { textSizePixels } : {},
7272
7469
  ...textSizeZoom !== void 0 ? { textSizeZoom } : {},
7273
7470
  ...textSizeResolution !== void 0 ? { textSizeResolution } : {}
@@ -7321,4 +7518,4 @@ function assertNever(value) {
7321
7518
  throw new Error(`Unhandled control measure kind: ${String(value)}`);
7322
7519
  }
7323
7520
  //#endregion
7324
- export { turnDrawRule as $, DEFAULT_GENERIC_POLYGON_OPTIONS as A, DEFAULT_BLOCK_OPTIONS as B, DEFAULT_FIX_OPTIONS as C, DEFAULT_DELAY_OPTIONS as D, DEFAULT_DISRUPT_OPTIONS as E, DEFAULT_BYPASS_OPTIONS as F, DEFAULT_ANTITANK_DITCH_OPTIONS as G, DEFAULT_ATTACK_HELICOPTER_OPTIONS as H, DEFAULT_BREACH_OPTIONS as I, rectangleDrawRule as J, DEFAULT_AMBUSH_OPTIONS as K, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as L, DEFAULT_GENERIC_CIRCLE_OPTIONS as M, DEFAULT_CLASSIC_ARROW_OPTIONS as N, DEFAULT_CLEAR_OPTIONS as O, DEFAULT_CANALIZE_OPTIONS as P, line23DrawRule as Q, DEFAULT_BOUNDARY_OPTIONS as R, DEFAULT_FLOT_OPTIONS as S, DEFAULT_ENCIRCLEMENT_OPTIONS as T, DEFAULT_ATTACK_BY_FIRE_OPTIONS as U, DEFAULT_BATTLE_POSITION_OPTIONS as V, DEFAULT_ANTITANK_WALL_OPTIONS as W, supportByFireDrawRule as X, axis1DrawRule as Y, line24DrawRule as Z, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as _, EPSILON as _t, DEFINITIONS as a, disruptDrawRule as at, DEFAULT_FORTIFIED_AREA_OPTIONS as b, getDefaultOptions as c, createMidpointPerpendicularDrawRule as ct, DEFAULT_SUPPORTING_ATTACK_OPTIONS as d, snapToMidpointPerpendicular as dt, line1DrawRule as et, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as f, createBaselineFrame as ft, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as g, unproject as gt, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as h, project as ht, CONTROL_MEASURE_METADATA as i, centerRadiusDrawRule as it, DEFAULT_GENERIC_LINE_OPTIONS as j, DEFAULT_GENERIC_RECTANGLE_OPTIONS as k, listControlMeasureMetadata as l, getMidpointPerpendicularSignedDistance as lt, DEFAULT_TACTICAL_ARROW_OPTIONS as m, computeInitialWidthPoint as mt, resolveStyleHints as n, attackByFireDrawRule as nt, getControlMeasureMetadata as o, blockDrawRule as ot, DEFAULT_STRONG_POINT_OPTIONS as p, calculateMetrics as pt, DEFAULT_AIRBORNE_ATTACK_OPTIONS as q, CONTROL_MEASURE_IDS as r, point12DrawRule as rt, getControlMeasureMetadataByValue as s, computeDefaultMidpointPerpendicularPoint as st, renderControlMeasure as t, ambushDrawRule as tt, DEFAULT_TURN_OPTIONS as u, pointOnMidpointPerpendicularAxis as ut, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as v, getMetersPerPixel as vt, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as w, DEFAULT_FORTIFIED_LINE_OPTIONS as x, DEFAULT_MAIN_ATTACK_OPTIONS as y, roundToFixed as yt, DEFAULT_BLOCK_ARROW_OPTIONS as z };
7521
+ export { supportByFireDrawRule as $, DEFAULT_DELAY_OPTIONS as A, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as B, DEFAULT_FORTIFIED_AREA_OPTIONS as C, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as D, DEFAULT_FIX_OPTIONS as E, DEFAULT_GENERIC_CIRCLE_OPTIONS as F, DEFAULT_ATTACK_HELICOPTER_OPTIONS as G, DEFAULT_BLOCK_ARROW_OPTIONS as H, DEFAULT_CLASSIC_ARROW_OPTIONS as I, DEFAULT_ANTITANK_DITCH_OPTIONS as J, DEFAULT_ATTACK_BY_FIRE_OPTIONS as K, DEFAULT_CANALIZE_OPTIONS as L, DEFAULT_GENERIC_RECTANGLE_OPTIONS as M, DEFAULT_GENERIC_POLYGON_OPTIONS as N, DEFAULT_ENCIRCLEMENT_OPTIONS as O, DEFAULT_GENERIC_LINE_OPTIONS as P, axis1DrawRule as Q, DEFAULT_BYPASS_OPTIONS as R, DEFAULT_MAIN_ATTACK_OPTIONS as S, roundToFixed as St, DEFAULT_FLOT_OPTIONS as T, DEFAULT_BLOCK_OPTIONS as U, DEFAULT_BOUNDARY_OPTIONS as V, DEFAULT_BATTLE_POSITION_OPTIONS as W, DEFAULT_AIRBORNE_ATTACK_OPTIONS as X, DEFAULT_AMBUSH_OPTIONS as Y, rectangleDrawRule as Z, DEFAULT_TACTICAL_ARROW_OPTIONS as _, computeInitialWidthPoint as _t, resolveStyleHints as a, attackByFireDrawRule as at, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as b, EPSILON as bt, DEFINITIONS as c, disruptDrawRule as ct, getDefaultOptions as d, createMidpointPerpendicularDrawRule as dt, line24DrawRule as et, listControlMeasureMetadata as f, getMidpointPerpendicularSignedDistance as ft, DEFAULT_STRONG_POINT_OPTIONS as g, calculateMetrics as gt, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as h, createBaselineFrame as ht, normalizeTextAmplifiers as i, ambushDrawRule as it, DEFAULT_CLEAR_OPTIONS as j, DEFAULT_DISRUPT_OPTIONS as k, getControlMeasureMetadata as l, blockDrawRule as lt, DEFAULT_SUPPORTING_ATTACK_OPTIONS as m, snapToMidpointPerpendicular as mt, TEXT_AMPLIFIER_FIELDS as n, turnDrawRule as nt, CONTROL_MEASURE_IDS as o, point12DrawRule as ot, DEFAULT_TURN_OPTIONS as p, pointOnMidpointPerpendicularAxis as pt, DEFAULT_ANTITANK_WALL_OPTIONS as q, canonicalTextAmplifierKey as r, line1DrawRule as rt, CONTROL_MEASURE_METADATA as s, centerRadiusDrawRule as st, renderControlMeasure as t, line23DrawRule as tt, getControlMeasureMetadataByValue as u, computeDefaultMidpointPerpendicularPoint as ut, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as v, project as vt, DEFAULT_FORTIFIED_LINE_OPTIONS as w, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as x, getMetersPerPixel as xt, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as y, unproject as yt, DEFAULT_BREACH_OPTIONS as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbat-mapper/control-measures",
3
- "version": "0.2.0-alpha.8",
3
+ "version": "0.2.0-alpha.9",
4
4
  "description": "Library for drawing tactical graphics and control measures according to MIL-STD-2525 and APP-6 standards.",
5
5
  "license": "MIT",
6
6
  "author": "Orbat Mapper",