@orbat-mapper/control-measures 0.2.0-alpha.4 → 0.2.0-alpha.6

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.
@@ -23,6 +23,19 @@ interface ControlMeasureDrawRule {
23
23
  * `minimumUserPoints` (no early preview).
24
24
  */
25
25
  readonly minimumPreviewPoints?: number;
26
+ /**
27
+ * Opt a fixed-length draw into the standard dashed interaction guide. Fixed
28
+ * shapes suppress guides by default because their generator preview is
29
+ * normally sufficient. Use this when the guide communicates an incomplete
30
+ * construction state, such as Rectangle's P1-P2 baseline.
31
+ */
32
+ readonly showGuide?: boolean;
33
+ /**
34
+ * Convert raw draw points into the spine rendered by the interaction guide.
35
+ * Defaults to the raw points. A rule may return the first point again at the
36
+ * end when it needs to provide its own closed guide geometry.
37
+ */
38
+ guidePoints?(points: readonly Position[]): Position[];
26
39
  /**
27
40
  * Number of trailing slots in the canonical control-point array that are
28
41
  * NOT user-clicked spine vertices. For Axis1, this is 1 (the width handle
@@ -36,6 +49,12 @@ interface ControlMeasureDrawRule {
36
49
  * Defaults to 0 (the entire array is user-clicked spine). See ADR-0008.
37
50
  */
38
51
  readonly trailingFixedSlots?: number;
52
+ /**
53
+ * The user-spine points form a closed ring even though the first point is not
54
+ * duplicated at the end. TacticalDraw uses this to treat the last→first edge
55
+ * like every other edge for completion and midpoint insertion.
56
+ */
57
+ readonly closedRing?: boolean;
39
58
  derive(points: readonly Position[]): Position[];
40
59
  transform(event: AnchorTransformEvent): Position[];
41
60
  }
@@ -43,7 +62,7 @@ interface ControlMeasureDrawRule {
43
62
  //#region src/metadata.d.ts
44
63
  type ControlMeasureGeometryType = Geometry["type"];
45
64
  type ControlMeasureGeometry = "point" | "line" | "area";
46
- declare const DRAW_RULE_IDS: readonly ["Area1", "Area7", "Area8", "Area11", "Area12", "Area15", "Area21", "Axis1", "Line1", "Line3", "Line9", "Line10", "Line23", "Line24", "Line29", "Point12"];
65
+ declare const DRAW_RULE_IDS: readonly ["Area1", "Area7", "Area8", "Area11", "Area12", "Area15", "Area21", "Axis1", "Line1", "Line3", "Line9", "Line10", "Line23", "Line24", "Line29", "Point12", "Rectangle"];
47
66
  type DrawRuleId = (typeof DRAW_RULE_IDS)[number];
48
67
  interface BaseParamDescriptor {
49
68
  key: string;
@@ -156,6 +175,18 @@ interface ControlMeasureDefinition<Id extends string, G extends ControlMeasureGe
156
175
  * ADR-0025.
157
176
  */
158
177
  previewSample?: PreviewSample<NonNullable<Parameters<G>[1]>>;
178
+ /**
179
+ * Pins orientation-derived defaults — options whose default value depends on
180
+ * the shape's current screen orientation (e.g. Battle Position's echelon
181
+ * anchor, which defaults to the bottom edge) — to their current concrete
182
+ * values. Returns an options patch to merge onto the working options, or
183
+ * `undefined` when there is nothing to freeze (already frozen, or nothing
184
+ * visible to pin). Called by edit hosts when a rotate gesture starts, so a
185
+ * subsequent rigid rotation of the control points preserves the rendered
186
+ * appearance instead of an orientation-relative default silently re-deriving
187
+ * against the new orientation. See ADR-0023.
188
+ */
189
+ freezeOrientation?: (controlPoints: Position[], options: NonNullable<Parameters<G>[1]>) => Partial<NonNullable<Parameters<G>[1]>> | undefined;
159
190
  }
160
191
  /**
161
192
  * A measure's representative preview input. Shared between the typed
@@ -309,6 +340,25 @@ interface BattlePositionOptions {
309
340
  * @default 0
310
341
  */
311
342
  echelonPosition?: number;
343
+ /**
344
+ * Absolute anchor for the echelon glyph, as a fraction of the total
345
+ * perimeter measured from the ring seam (the ring's first vertex), wrapped
346
+ * into `[0, 1)`. When omitted, the anchor is "auto": the bottom edge's
347
+ * midpoint (the side opposite the front), recomputed from the current
348
+ * geometry on every render — the long-standing default behavior.
349
+ * `echelonPosition` still applies on top of this anchor as a signed offset,
350
+ * exactly as it does against the auto anchor.
351
+ *
352
+ * Edit controllers freeze this from "auto" to a concrete fraction the
353
+ * moment a rotate gesture starts (see `freezeEchelonAnchor` /
354
+ * `freezeOrientationOptions`): the transform box rotates only the control
355
+ * points and re-renders, so without a pinned anchor the "bottom edge" would
356
+ * keep re-deriving against the rotated geometry and the glyph would jump to
357
+ * a different edge each time a new edge becomes lowest. Freezing the anchor
358
+ * first makes the rotation rigid — the glyph turns with the shape instead.
359
+ * See ADR-0023.
360
+ */
361
+ echelonAnchor?: number;
312
362
  /**
313
363
  * Meters-per-pixel at the current zoom/latitude. Stamped by the host when the
314
364
  * measure is screen-anchored; required to resolve `echelonSizePixels`.
@@ -613,6 +663,47 @@ declare function createBlockArrow(coordinates: Position[], options?: BlockArrowO
613
663
  fill: boolean;
614
664
  }>;
615
665
  //#endregion
666
+ //#region src/generators/cm99-generic-graphics/params.d.ts
667
+ interface SmoothPathOptions {
668
+ /** Curve the rendered path through the unchanged control points. */
669
+ smooth?: boolean;
670
+ /** Number of samples per segment when smoothing is enabled. */
671
+ smoothResolution?: number;
672
+ }
673
+ interface FilledAreaOptions {
674
+ /** Fill the polygon interior; false renders only its outline. */
675
+ filled?: boolean;
676
+ }
677
+ //#endregion
678
+ //#region src/generators/cm99-generic-graphics/line.d.ts
679
+ type GenericLineOptions = SmoothPathOptions;
680
+ declare const DEFAULT_GENERIC_LINE_OPTIONS: Required<GenericLineOptions>;
681
+ declare function createGenericLine(coordinates: Position[], options?: GenericLineOptions): FeatureCollection<LineString>;
682
+ //#endregion
683
+ //#region src/generators/cm99-generic-graphics/polygon.d.ts
684
+ interface GenericPolygonOptions extends SmoothPathOptions, FilledAreaOptions {}
685
+ declare const DEFAULT_GENERIC_POLYGON_OPTIONS: Required<GenericPolygonOptions>;
686
+ declare function createGenericPolygon(coordinates: Position[], options?: GenericPolygonOptions): FeatureCollection<Polygon, {
687
+ part: string;
688
+ fill: boolean;
689
+ }>;
690
+ //#endregion
691
+ //#region src/generators/cm99-generic-graphics/rectangle.d.ts
692
+ type GenericRectangleOptions = FilledAreaOptions;
693
+ declare const DEFAULT_GENERIC_RECTANGLE_OPTIONS: Required<GenericRectangleOptions>;
694
+ declare function createGenericRectangle(coordinates: Position[], options?: GenericRectangleOptions): FeatureCollection<Polygon, {
695
+ part: string;
696
+ fill: boolean;
697
+ }>;
698
+ //#endregion
699
+ //#region src/generators/cm99-generic-graphics/circle.d.ts
700
+ type GenericCircleOptions = FilledAreaOptions;
701
+ declare const DEFAULT_GENERIC_CIRCLE_OPTIONS: Required<GenericCircleOptions>;
702
+ declare function createGenericCircle(coordinates: Position[], options?: GenericCircleOptions): FeatureCollection<Polygon, {
703
+ part: string;
704
+ fill: boolean;
705
+ }>;
706
+ //#endregion
616
707
  //#region src/generators/cm15-maneuver-areas/airborneAttack.d.ts
617
708
  type AirborneAttackOptions = AttackOptions;
618
709
  declare const DEFAULT_AIRBORNE_ATTACK_OPTIONS: Required<AirborneAttackOptions>;
@@ -1265,6 +1356,10 @@ declare const DEFINITIONS: {
1265
1356
  "supporting-attack": ControlMeasureDefinition<"supporting-attack", typeof createSupportingAttack>;
1266
1357
  "classic-arrow": ControlMeasureDefinition<"classic-arrow", typeof createClassicArrow>;
1267
1358
  "block-arrow": ControlMeasureDefinition<"block-arrow", typeof createBlockArrow>;
1359
+ line: ControlMeasureDefinition<"line", typeof createGenericLine>;
1360
+ polygon: ControlMeasureDefinition<"polygon", typeof createGenericPolygon>;
1361
+ rectangle: ControlMeasureDefinition<"rectangle", typeof createGenericRectangle>;
1362
+ circle: ControlMeasureDefinition<"circle", typeof createGenericCircle>;
1268
1363
  "airborne-attack": ControlMeasureDefinition<"airborne-attack", typeof createAirborneAttack>;
1269
1364
  "attack-helicopter": ControlMeasureDefinition<"attack-helicopter", typeof createAttackHelicopter>;
1270
1365
  "support-by-fire": ControlMeasureDefinition<"support-by-fire", typeof createSupportByFire>;
@@ -1454,6 +1549,17 @@ interface RenderOptions {
1454
1549
  */
1455
1550
  declare function renderControlMeasure<K extends ControlMeasureKind>(cm: ControlMeasure<K>, opts?: RenderOptions): ControlMeasureRender;
1456
1551
  //#endregion
1552
+ //#region src/freeze-orientation.d.ts
1553
+ /**
1554
+ * Dispatches to a measure kind's `freezeOrientation` hook (see
1555
+ * {@link import("./define").ControlMeasureDefinition.freezeOrientation}),
1556
+ * pinning orientation-derived option defaults to their current concrete
1557
+ * values ahead of a rigid rotation of `controlPoints`. Returns `undefined`
1558
+ * when the kind declares no hook, or when the hook itself finds nothing to
1559
+ * freeze. Edit hosts call this when a rotate gesture starts (ADR-0023).
1560
+ */
1561
+ declare function freezeOrientationOptions<K extends ControlMeasureKind>(kind: K, controlPoints: Position[], options: OptionsByKind[K] | undefined): Partial<OptionsByKind[K]> | undefined;
1562
+ //#endregion
1457
1563
  //#region src/styleResolver.d.ts
1458
1564
  /**
1459
1565
  * Three-layer style precedence for `renderControlMeasure`.
@@ -1599,6 +1705,9 @@ declare const blockDrawRule: ControlMeasureDrawRule;
1599
1705
  */
1600
1706
  declare const disruptDrawRule: ControlMeasureDrawRule;
1601
1707
  //#endregion
1708
+ //#region src/draw-rules/area15.d.ts
1709
+ declare const centerRadiusDrawRule: ControlMeasureDrawRule;
1710
+ //#endregion
1602
1711
  //#region src/draw-rules/point12.d.ts
1603
1712
  /**
1604
1713
  * Point12 anchor draw rule for the obstacle-bypass family.
@@ -1669,6 +1778,9 @@ declare const supportByFireDrawRule: ControlMeasureDrawRule;
1669
1778
  //#region src/draw-rules/axis1.d.ts
1670
1779
  declare const axis1DrawRule: ControlMeasureDrawRule;
1671
1780
  //#endregion
1781
+ //#region src/draw-rules/rectangle.d.ts
1782
+ declare const rectangleDrawRule: ControlMeasureDrawRule;
1783
+ //#endregion
1672
1784
  //#region src/generators/cm14-maneuver-lines/ambush.d.ts
1673
1785
  /**
1674
1786
  * Configuration options for the Ambush tactical symbol.
@@ -1710,4 +1822,4 @@ interface TacticalArrowOptions {
1710
1822
  */
1711
1823
  declare const DEFAULT_TACTICAL_ARROW_OPTIONS: Required<TacticalArrowOptions>;
1712
1824
  //#endregion
1713
- export { listControlMeasureMetadata as $, DEFAULT_ENCIRCLEMENT_OPTIONS as $t, SimpleStyleProps as A, BreachOptions as At, controlMeasureIdFromFeature as B, AttackHelicopterOptions as Bt, BaselineFrameNormal as C, DelayOptions as Ct, EPSILON as D, DEFAULT_CANALIZE_OPTIONS as Dt, createBaselineFrame as E, CanalizeOptions as Et, renderControlMeasure as F, FLOTOptions as Ft, CONTROL_MEASURE_IDS as G, SupportingAttackOptions as Gt, cloneControlMeasure as H, AirborneAttackOptions as Ht, ControlMeasureRender as I, AttackByFireOptions as It, ControlMeasureKind as J, calculateMetrics as Jt, CONTROL_MEASURE_METADATA as K, DEFAULT_MAIN_ATTACK_OPTIONS as Kt, ControlMeasureSnapshot as L, DEFAULT_ATTACK_BY_FIRE_OPTIONS as Lt, toSimpleStyle as M, BlockMissionTaskOptions as Mt, resolveStyleHints as N, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as Nt, getMetersPerPixel as O, BypassOptions as Ot, RenderOptions as P, DEFAULT_FLOT_OPTIONS as Pt, getDefaultOptions as Q, unproject as Qt, FeaturePartProps as R, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as Rt, BaselineFrame as S, DEFAULT_DELAY_OPTIONS as St, BaselineFrameOrigin as T, DEFAULT_CLEAR_OPTIONS as Tt, isKind as U, DEFAULT_AIRBORNE_ATTACK_OPTIONS as Ut, ControlMeasure as V, DEFAULT_ATTACK_HELICOPTER_OPTIONS as Vt, ControlMeasureStyle as W, DEFAULT_SUPPORTING_ATTACK_OPTIONS as Wt, getControlMeasureMetadata as X, Point2D as Xt, OptionsByKind as Y, computeInitialWidthPoint as Yt, getControlMeasureMetadataByValue as Z, project as Zt, computeDefaultMidpointPerpendicularPoint as _, FortifiedLineOptions as _t, axis1DrawRule as a, DEFAULT_STRONG_POINT_OPTIONS as an, ObstacleBypassEasyOptions as at, pointOnMidpointPerpendicularAxis as b, AntitankDitchOptions as bt, line23DrawRule as c, ControlMeasureGeometryType as cn, DEFAULT_FIX_OPTIONS as ct, ambushDrawRule as d, AnchorTransformEvent as dn, DisruptOptions as dt, EncirclementOptions as en, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as et, attackByFireDrawRule as f, ControlMeasureDrawRule as fn, BlockOptions as ft, MidpointPerpendicularDrawRuleOptions as g, DEFAULT_FORTIFIED_LINE_OPTIONS as gt, blockDrawRule as h, FortifiedAreaOptions as ht, DEFAULT_AMBUSH_OPTIONS as i, PrincipalDirectionOfFireOptions as in, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as it, SimpleStyleRender as j, DEFAULT_BREACH_OPTIONS as jt, roundToFixed as k, DEFAULT_BYPASS_OPTIONS as kt, turnDrawRule as l, ControlMeasureMetadata as ln, FixOptions as lt, disruptDrawRule as m, DEFAULT_FORTIFIED_AREA_OPTIONS as mt, TacticalArrowOptions as n, FinalProtectiveFireOptions as nn, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as nt, supportByFireDrawRule as o, StrongPointOptions as on, DEFAULT_TURN_OPTIONS as ot, point12DrawRule as p, DEFAULT_BLOCK_OPTIONS as pt, ControlMeasureId as q, MainAttackOptions as qt, AmbushOptions as r, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as rn, ObstacleBypassDifficultOptions as rt, line24DrawRule as s, ControlMeasureGeometry as sn, TurnOptions as st, DEFAULT_TACTICAL_ARROW_OPTIONS as t, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as tn, ObstacleBypassImpossibleOptions as tt, line1DrawRule as u, ParamDescriptor as un, DEFAULT_DISRUPT_OPTIONS as ut, createMidpointPerpendicularDrawRule as v, AntitankWallOptions as vt, BaselineFrameOptions as w, ClearOptions as wt, snapToMidpointPerpendicular as x, DEFAULT_ANTITANK_DITCH_OPTIONS as xt, getMidpointPerpendicularSignedDistance as y, DEFAULT_ANTITANK_WALL_OPTIONS as yt, StyleHints as z, SupportByFireOptions as zt };
1825
+ export { getControlMeasureMetadata as $, DEFAULT_GENERIC_LINE_OPTIONS as $t, getMetersPerPixel as A, DEFAULT_CANALIZE_OPTIONS as At, ControlMeasureSnapshot as B, DEFAULT_ATTACK_BY_FIRE_OPTIONS as Bt, snapToMidpointPerpendicular as C, ControlMeasureDrawRule as Cn, AntitankDitchOptions as Ct, BaselineFrameOrigin as D, ClearOptions as Dt, BaselineFrameOptions as E, 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, DEFAULT_BYPASS_OPTIONS as Mt, SimpleStyleRender as N, BreachOptions as Nt, createBaselineFrame as O, 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, AnchorTransformEvent as Sn, DEFAULT_ANTITANK_WALL_OPTIONS as St, BaselineFrameNormal as T, 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 _, StrongPointOptions as _n, DEFAULT_FORTIFIED_AREA_OPTIONS as _t, rectangleDrawRule as a, calculateMetrics as an, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as at, createMidpointPerpendicularDrawRule as b, ControlMeasureMetadata as bn, FortifiedLineOptions as bt, line24DrawRule as c, project as cn, ObstacleBypassEasyOptions as ct, line1DrawRule as d, EncirclementOptions as dn, DEFAULT_FIX_OPTIONS as dt, GenericLineOptions as en, getControlMeasureMetadataByValue as et, ambushDrawRule as f, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as fn, FixOptions as ft, disruptDrawRule as g, DEFAULT_STRONG_POINT_OPTIONS as gn, DEFAULT_BLOCK_OPTIONS as gt, centerRadiusDrawRule as h, PrincipalDirectionOfFireOptions as hn, BlockOptions as ht, DEFAULT_AMBUSH_OPTIONS as i, MainAttackOptions as in, ObstacleBypassImpossibleOptions as it, roundToFixed as j, BypassOptions as jt, EPSILON as k, CanalizeOptions as kt, line23DrawRule as l, unproject as ln, DEFAULT_TURN_OPTIONS as lt, point12DrawRule as m, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as mn, DisruptOptions as mt, TacticalArrowOptions as n, SupportingAttackOptions as nn, listControlMeasureMetadata as nt, axis1DrawRule as o, computeInitialWidthPoint as on, ObstacleBypassDifficultOptions as ot, attackByFireDrawRule as p, FinalProtectiveFireOptions as pn, DEFAULT_DISRUPT_OPTIONS as pt, ControlMeasureStyle as q, DEFAULT_GENERIC_CIRCLE_OPTIONS as qt, AmbushOptions as r, DEFAULT_MAIN_ATTACK_OPTIONS as rn, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as rt, supportByFireDrawRule as s, Point2D as sn, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as st, DEFAULT_TACTICAL_ARROW_OPTIONS as t, DEFAULT_SUPPORTING_ATTACK_OPTIONS as tn, getDefaultOptions as tt, turnDrawRule as u, DEFAULT_ENCIRCLEMENT_OPTIONS as un, TurnOptions as ut, MidpointPerpendicularDrawRuleOptions as v, ControlMeasureGeometry as vn, FortifiedAreaOptions as vt, BaselineFrame as w, DEFAULT_ANTITANK_DITCH_OPTIONS as wt, getMidpointPerpendicularSignedDistance as x, ParamDescriptor as xn, AntitankWallOptions as xt, computeDefaultMidpointPerpendicularPoint as y, ControlMeasureGeometryType as yn, DEFAULT_FORTIFIED_LINE_OPTIONS as yt, ControlMeasureRender as z, AttackByFireOptions as zt };
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as listControlMeasureMetadata, $t as DEFAULT_ENCIRCLEMENT_OPTIONS, A as SimpleStyleProps, At as BreachOptions, B as controlMeasureIdFromFeature, Bt as AttackHelicopterOptions, C as BaselineFrameNormal, Ct as DelayOptions, D as EPSILON, Dt as DEFAULT_CANALIZE_OPTIONS, E as createBaselineFrame, Et as CanalizeOptions, F as renderControlMeasure, Ft as FLOTOptions, G as CONTROL_MEASURE_IDS, Gt as SupportingAttackOptions, H as cloneControlMeasure, Ht as AirborneAttackOptions, I as ControlMeasureRender, It as AttackByFireOptions, J as ControlMeasureKind, Jt as calculateMetrics, K as CONTROL_MEASURE_METADATA, Kt as DEFAULT_MAIN_ATTACK_OPTIONS, L as ControlMeasureSnapshot, Lt as DEFAULT_ATTACK_BY_FIRE_OPTIONS, M as toSimpleStyle, Mt as BlockMissionTaskOptions, N as resolveStyleHints, Nt as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, O as getMetersPerPixel, Ot as BypassOptions, P as RenderOptions, Pt as DEFAULT_FLOT_OPTIONS, Q as getDefaultOptions, Qt as unproject, R as FeaturePartProps, Rt as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, S as BaselineFrame, St as DEFAULT_DELAY_OPTIONS, T as BaselineFrameOrigin, Tt as DEFAULT_CLEAR_OPTIONS, U as isKind, Ut as DEFAULT_AIRBORNE_ATTACK_OPTIONS, V as ControlMeasure, Vt as DEFAULT_ATTACK_HELICOPTER_OPTIONS, W as ControlMeasureStyle, Wt as DEFAULT_SUPPORTING_ATTACK_OPTIONS, X as getControlMeasureMetadata, Xt as Point2D, Y as OptionsByKind, Yt as computeInitialWidthPoint, Z as getControlMeasureMetadataByValue, Zt as project, _ as computeDefaultMidpointPerpendicularPoint, _t as FortifiedLineOptions, a as axis1DrawRule, an as DEFAULT_STRONG_POINT_OPTIONS, at as ObstacleBypassEasyOptions, b as pointOnMidpointPerpendicularAxis, bt as AntitankDitchOptions, c as line23DrawRule, cn as ControlMeasureGeometryType, ct as DEFAULT_FIX_OPTIONS, d as ambushDrawRule, dn as AnchorTransformEvent, dt as DisruptOptions, en as EncirclementOptions, et as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, f as attackByFireDrawRule, fn as ControlMeasureDrawRule, ft as BlockOptions, g as MidpointPerpendicularDrawRuleOptions, gt as DEFAULT_FORTIFIED_LINE_OPTIONS, h as blockDrawRule, ht as FortifiedAreaOptions, i as DEFAULT_AMBUSH_OPTIONS, in as PrincipalDirectionOfFireOptions, it as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, j as SimpleStyleRender, jt as DEFAULT_BREACH_OPTIONS, k as roundToFixed, kt as DEFAULT_BYPASS_OPTIONS, l as turnDrawRule, ln as ControlMeasureMetadata, lt as FixOptions, m as disruptDrawRule, mt as DEFAULT_FORTIFIED_AREA_OPTIONS, n as TacticalArrowOptions, nn as FinalProtectiveFireOptions, nt as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, o as supportByFireDrawRule, on as StrongPointOptions, ot as DEFAULT_TURN_OPTIONS, p as point12DrawRule, pt as DEFAULT_BLOCK_OPTIONS, q as ControlMeasureId, qt as MainAttackOptions, r as AmbushOptions, rn as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, rt as ObstacleBypassDifficultOptions, s as line24DrawRule, sn as ControlMeasureGeometry, st as TurnOptions, t as DEFAULT_TACTICAL_ARROW_OPTIONS, tn as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, tt as ObstacleBypassImpossibleOptions, u as line1DrawRule, un as ParamDescriptor, ut as DEFAULT_DISRUPT_OPTIONS, v as createMidpointPerpendicularDrawRule, vt as AntitankWallOptions, w as BaselineFrameOptions, wt as ClearOptions, x as snapToMidpointPerpendicular, xt as DEFAULT_ANTITANK_DITCH_OPTIONS, y as getMidpointPerpendicularSignedDistance, yt as DEFAULT_ANTITANK_WALL_OPTIONS, z as StyleHints, zt as SupportByFireOptions } from "./index-BJYbQlzo.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 BlockMissionTaskOptions, type BlockOptions, type BreachOptions, type BypassOptions, CONTROL_MEASURE_IDS, CONTROL_MEASURE_METADATA, type CanalizeOptions, 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_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_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_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 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, cloneControlMeasure, computeDefaultMidpointPerpendicularPoint, computeInitialWidthPoint, controlMeasureIdFromFeature, createBaselineFrame, createMidpointPerpendicularDrawRule, disruptDrawRule, getControlMeasureMetadata, getControlMeasureMetadataByValue, getDefaultOptions, getMetersPerPixel, getMidpointPerpendicularSignedDistance, isKind, line1DrawRule, line23DrawRule, line24DrawRule, listControlMeasureMetadata, point12DrawRule, pointOnMidpointPerpendicularAxis, project, renderControlMeasure, resolveStyleHints, roundToFixed, snapToMidpointPerpendicular, supportByFireDrawRule, toSimpleStyle, turnDrawRule, unproject };
1
+ import { $ as getControlMeasureMetadata, $t as DEFAULT_GENERIC_LINE_OPTIONS, A as getMetersPerPixel, At as DEFAULT_CANALIZE_OPTIONS, B as ControlMeasureSnapshot, Bt as DEFAULT_ATTACK_BY_FIRE_OPTIONS, C as snapToMidpointPerpendicular, Cn as ControlMeasureDrawRule, Ct as AntitankDitchOptions, D as BaselineFrameOrigin, Dt as ClearOptions, E as BaselineFrameOptions, 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, Mt as DEFAULT_BYPASS_OPTIONS, N as SimpleStyleRender, Nt as BreachOptions, O as createBaselineFrame, 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 AnchorTransformEvent, St as DEFAULT_ANTITANK_WALL_OPTIONS, T as BaselineFrameNormal, 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 StrongPointOptions, _t as DEFAULT_FORTIFIED_AREA_OPTIONS, a as rectangleDrawRule, an as calculateMetrics, at as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, b as createMidpointPerpendicularDrawRule, bn as ControlMeasureMetadata, bt as FortifiedLineOptions, c as line24DrawRule, cn as project, ct as ObstacleBypassEasyOptions, d as line1DrawRule, dn as EncirclementOptions, dt as DEFAULT_FIX_OPTIONS, en as GenericLineOptions, et as getControlMeasureMetadataByValue, f as ambushDrawRule, fn as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, ft as FixOptions, g as disruptDrawRule, gn as DEFAULT_STRONG_POINT_OPTIONS, gt as DEFAULT_BLOCK_OPTIONS, h as centerRadiusDrawRule, hn as PrincipalDirectionOfFireOptions, ht as BlockOptions, i as DEFAULT_AMBUSH_OPTIONS, in as MainAttackOptions, it as ObstacleBypassImpossibleOptions, j as roundToFixed, jt as BypassOptions, k as EPSILON, kt as CanalizeOptions, l as line23DrawRule, ln as unproject, lt as DEFAULT_TURN_OPTIONS, m as point12DrawRule, mn as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, mt as DisruptOptions, n as TacticalArrowOptions, nn as SupportingAttackOptions, nt as listControlMeasureMetadata, o as axis1DrawRule, on as computeInitialWidthPoint, ot as ObstacleBypassDifficultOptions, p as attackByFireDrawRule, pn as FinalProtectiveFireOptions, pt as DEFAULT_DISRUPT_OPTIONS, q as ControlMeasureStyle, qt as DEFAULT_GENERIC_CIRCLE_OPTIONS, r as AmbushOptions, rn as DEFAULT_MAIN_ATTACK_OPTIONS, rt as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, s as supportByFireDrawRule, sn as Point2D, st as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, t as DEFAULT_TACTICAL_ARROW_OPTIONS, tn as DEFAULT_SUPPORTING_ATTACK_OPTIONS, tt as getDefaultOptions, u as turnDrawRule, un as DEFAULT_ENCIRCLEMENT_OPTIONS, ut as TurnOptions, v as MidpointPerpendicularDrawRuleOptions, vn as ControlMeasureGeometry, vt as FortifiedAreaOptions, w as BaselineFrame, wt as DEFAULT_ANTITANK_DITCH_OPTIONS, x as getMidpointPerpendicularSignedDistance, xn as ParamDescriptor, xt as AntitankWallOptions, y as computeDefaultMidpointPerpendicularPoint, yn as ControlMeasureGeometryType, yt as DEFAULT_FORTIFIED_LINE_OPTIONS, z as ControlMeasureRender, zt as AttackByFireOptions } from "./index-DUGzYzPy.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 BlockMissionTaskOptions, type BlockOptions, type BreachOptions, type BypassOptions, CONTROL_MEASURE_IDS, CONTROL_MEASURE_METADATA, type CanalizeOptions, 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_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_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 };
package/dist/index.mjs CHANGED
@@ -1,4 +1,19 @@
1
- import { $ as getMidpointPerpendicularSignedDistance, A as DEFAULT_BYPASS_OPTIONS, B as axis1DrawRule, C as DEFAULT_FIX_OPTIONS, D as DEFAULT_DELAY_OPTIONS, E as DEFAULT_DISRUPT_OPTIONS, F as DEFAULT_ATTACK_BY_FIRE_OPTIONS, G as line1DrawRule, H as line24DrawRule, I as DEFAULT_ANTITANK_WALL_OPTIONS, J as point12DrawRule, K as ambushDrawRule, L as DEFAULT_ANTITANK_DITCH_OPTIONS, M as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, N as DEFAULT_BLOCK_OPTIONS, O as DEFAULT_CLEAR_OPTIONS, P as DEFAULT_ATTACK_HELICOPTER_OPTIONS, Q as createMidpointPerpendicularDrawRule, R as DEFAULT_AMBUSH_OPTIONS, S as DEFAULT_FLOT_OPTIONS, T as DEFAULT_ENCIRCLEMENT_OPTIONS, U as line23DrawRule, V as supportByFireDrawRule, W as turnDrawRule, X as blockDrawRule, Y as disruptDrawRule, Z as computeDefaultMidpointPerpendicularPoint, _ as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, at as project, b as DEFAULT_FORTIFIED_AREA_OPTIONS, c as getDefaultOptions, ct as getMetersPerPixel, d as DEFAULT_SUPPORTING_ATTACK_OPTIONS, et as pointOnMidpointPerpendicularAxis, f as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, g as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, h as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, i as CONTROL_MEASURE_METADATA, it as computeInitialWidthPoint, j as DEFAULT_BREACH_OPTIONS, k as DEFAULT_CANALIZE_OPTIONS, l as listControlMeasureMetadata, lt as roundToFixed, m as DEFAULT_TACTICAL_ARROW_OPTIONS, n as resolveStyleHints, nt as createBaselineFrame, o as getControlMeasureMetadata, ot as unproject, p as DEFAULT_STRONG_POINT_OPTIONS, q as attackByFireDrawRule, r as CONTROL_MEASURE_IDS, rt as calculateMetrics, s as getControlMeasureMetadataByValue, st as EPSILON, t as renderControlMeasure, tt as snapToMidpointPerpendicular, u as DEFAULT_TURN_OPTIONS, v as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, w as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, x as DEFAULT_FORTIFIED_LINE_OPTIONS, y as DEFAULT_MAIN_ATTACK_OPTIONS, z as DEFAULT_AIRBORNE_ATTACK_OPTIONS } from "./renderControlMeasure-CONa8M6M.mjs";
1
+ import { $ as point12DrawRule, A as DEFAULT_GENERIC_POLYGON_OPTIONS, B as DEFAULT_ANTITANK_WALL_OPTIONS, C as DEFAULT_FIX_OPTIONS, D as DEFAULT_DELAY_OPTIONS, E as DEFAULT_DISRUPT_OPTIONS, F as DEFAULT_BREACH_OPTIONS, G as axis1DrawRule, H as DEFAULT_AMBUSH_OPTIONS, I as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, J as line23DrawRule, K as supportByFireDrawRule, L as DEFAULT_BLOCK_OPTIONS, M as DEFAULT_GENERIC_CIRCLE_OPTIONS, N as DEFAULT_CANALIZE_OPTIONS, O as DEFAULT_CLEAR_OPTIONS, P as DEFAULT_BYPASS_OPTIONS, Q as attackByFireDrawRule, R as DEFAULT_ATTACK_HELICOPTER_OPTIONS, S as DEFAULT_FLOT_OPTIONS, T as DEFAULT_ENCIRCLEMENT_OPTIONS, U as DEFAULT_AIRBORNE_ATTACK_OPTIONS, V as DEFAULT_ANTITANK_DITCH_OPTIONS, W as rectangleDrawRule, X as line1DrawRule, Y as turnDrawRule, Z as ambushDrawRule, _ as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, a as DEFINITIONS, at as getMidpointPerpendicularSignedDistance, b as DEFAULT_FORTIFIED_AREA_OPTIONS, c as getDefaultOptions, ct as createBaselineFrame, d as DEFAULT_SUPPORTING_ATTACK_OPTIONS, dt as project, et as centerRadiusDrawRule, f as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, ft as unproject, g as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, h as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, ht as roundToFixed, i as CONTROL_MEASURE_METADATA, it as createMidpointPerpendicularDrawRule, j as DEFAULT_GENERIC_LINE_OPTIONS, k as DEFAULT_GENERIC_RECTANGLE_OPTIONS, l as listControlMeasureMetadata, lt as calculateMetrics, m as DEFAULT_TACTICAL_ARROW_OPTIONS, mt as getMetersPerPixel, n as resolveStyleHints, nt as blockDrawRule, o as getControlMeasureMetadata, ot as pointOnMidpointPerpendicularAxis, p as DEFAULT_STRONG_POINT_OPTIONS, pt as EPSILON, q as line24DrawRule, r as CONTROL_MEASURE_IDS, rt as computeDefaultMidpointPerpendicularPoint, s as getControlMeasureMetadataByValue, st as snapToMidpointPerpendicular, t as renderControlMeasure, tt as disruptDrawRule, u as DEFAULT_TURN_OPTIONS, ut as computeInitialWidthPoint, v as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, w as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, x as DEFAULT_FORTIFIED_LINE_OPTIONS, y as DEFAULT_MAIN_ATTACK_OPTIONS, z as DEFAULT_ATTACK_BY_FIRE_OPTIONS } from "./renderControlMeasure-D5NKuws0.mjs";
2
+ //#region src/freeze-orientation.ts
3
+ /**
4
+ * Dispatches to a measure kind's `freezeOrientation` hook (see
5
+ * {@link import("./define").ControlMeasureDefinition.freezeOrientation}),
6
+ * pinning orientation-derived option defaults to their current concrete
7
+ * values ahead of a rigid rotation of `controlPoints`. Returns `undefined`
8
+ * when the kind declares no hook, or when the hook itself finds nothing to
9
+ * freeze. Edit hosts call this when a rotate gesture starts (ADR-0023).
10
+ */
11
+ function freezeOrientationOptions(kind, controlPoints, options) {
12
+ const definition = DEFINITIONS[kind];
13
+ if (!definition.freezeOrientation) return void 0;
14
+ return definition.freezeOrientation(controlPoints, options ?? {});
15
+ }
16
+ //#endregion
2
17
  //#region src/instance.ts
3
18
  function isKind(cm, kind) {
4
19
  return cm.kind === kind;
@@ -99,4 +114,4 @@ function toHexChannel(value) {
99
114
  return Math.max(0, Math.min(255, Math.round(value))).toString(16).padStart(2, "0");
100
115
  }
101
116
  //#endregion
102
- 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_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_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_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, cloneControlMeasure, computeDefaultMidpointPerpendicularPoint, computeInitialWidthPoint, controlMeasureIdFromFeature, createBaselineFrame, createMidpointPerpendicularDrawRule, disruptDrawRule, getControlMeasureMetadata, getControlMeasureMetadataByValue, getDefaultOptions, getMetersPerPixel, getMidpointPerpendicularSignedDistance, isKind, line1DrawRule, line23DrawRule, line24DrawRule, listControlMeasureMetadata, point12DrawRule, pointOnMidpointPerpendicularAxis, project, 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_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_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 };
@@ -1,4 +1,4 @@
1
- import { q as ControlMeasureId } from "../index-BJYbQlzo.mjs";
1
+ import { X as ControlMeasureId } from "../index-DUGzYzPy.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-CONa8M6M.mjs";
1
+ import { a as DEFINITIONS, t as renderControlMeasure } from "../renderControlMeasure-D5NKuws0.mjs";
2
2
  //#region src/preview/index.ts
3
3
  /**
4
4
  * Unitless `previewSample` points (~`[-1, 1]`) are scaled by this degree offset
@@ -484,7 +484,7 @@ function samePosition(a, b) {
484
484
  }
485
485
  //#endregion
486
486
  //#region src/draw-rules/area1.ts
487
- function derive$7(points) {
487
+ function derive$8(points) {
488
488
  return clonePositions(points);
489
489
  }
490
490
  /**
@@ -503,9 +503,10 @@ function derive$7(points) {
503
503
  const area1DrawRule = {
504
504
  id: "area1",
505
505
  minimumUserPoints: 3,
506
- derive: derive$7,
506
+ closedRing: true,
507
+ derive: derive$8,
507
508
  transform(event) {
508
- return derive$7(event.next);
509
+ return derive$8(event.next);
509
510
  }
510
511
  };
511
512
  //#endregion
@@ -531,14 +532,14 @@ const disruptDrawRule = createMidpointPerpendicularDrawRule({
531
532
  * that fixes both the radius and the bearing of the symbol's opening. Both
532
533
  * points are user-clicked, so the canonical array is just the (clamped) input.
533
534
  */
534
- function derive$6(points) {
535
+ function derive$7(points) {
535
536
  return clonePositions(points.slice(0, 2));
536
537
  }
537
- const isolateDrawRule = {
538
- id: "area15:isolate",
538
+ const centerRadiusDrawRule = {
539
+ id: "area15:center-radius",
539
540
  minimumUserPoints: 2,
540
541
  canonicalPointCount: 2,
541
- derive: derive$6,
542
+ derive: derive$7,
542
543
  transform(event) {
543
544
  const { previous, next, activePointIndex } = event;
544
545
  if (activePointIndex === 0 && previous.length >= 2 && next.length >= 2) {
@@ -547,9 +548,11 @@ const isolateDrawRule = {
547
548
  const origin = previous[1];
548
549
  return [clonePosition(next[0]), [origin[0] + dx, origin[1] + dy]];
549
550
  }
550
- return derive$6(next);
551
+ return derive$7(next);
551
552
  }
552
553
  };
554
+ /** Backward-compatible doctrinal name for Area15's shared center-radius rule. */
555
+ const isolateDrawRule = centerRadiusDrawRule;
553
556
  //#endregion
554
557
  //#region src/draw-rules/point12.ts
555
558
  /**
@@ -590,20 +593,20 @@ const ambushDrawRule = createMidpointPerpendicularDrawRule({
590
593
  });
591
594
  //#endregion
592
595
  //#region src/draw-rules/line1.ts
593
- function derive$5(points) {
596
+ function derive$6(points) {
594
597
  return clonePositions(points);
595
598
  }
596
599
  const line1DrawRule = {
597
600
  id: "line1",
598
601
  minimumUserPoints: 2,
599
- derive: derive$5,
602
+ derive: derive$6,
600
603
  transform(event) {
601
- return derive$5(event.next);
604
+ return derive$6(event.next);
602
605
  }
603
606
  };
604
607
  //#endregion
605
608
  //#region src/draw-rules/line3.ts
606
- function derive$4(points) {
609
+ function derive$5(points) {
607
610
  return clonePositions(points);
608
611
  }
609
612
  /**
@@ -618,23 +621,23 @@ const line3DrawRule = {
618
621
  id: "line3",
619
622
  minimumUserPoints: 3,
620
623
  canonicalPointCount: 3,
621
- derive: derive$4,
624
+ derive: derive$5,
622
625
  transform(event) {
623
- return derive$4(event.next);
626
+ return derive$5(event.next);
624
627
  }
625
628
  };
626
629
  //#endregion
627
630
  //#region src/draw-rules/line9.ts
628
- function derive$3(points) {
631
+ function derive$4(points) {
629
632
  return clonePositions(points);
630
633
  }
631
634
  const line9DrawRule = {
632
635
  id: "line9",
633
636
  minimumUserPoints: 2,
634
637
  canonicalPointCount: 2,
635
- derive: derive$3,
638
+ derive: derive$4,
636
639
  transform(event) {
637
- return derive$3(event.next);
640
+ return derive$4(event.next);
638
641
  }
639
642
  };
640
643
  //#endregion
@@ -740,7 +743,7 @@ const line24DrawRule = createMidpointPerpendicularDrawRule({
740
743
  });
741
744
  //#endregion
742
745
  //#region src/draw-rules/area8.ts
743
- function derive$2(points) {
746
+ function derive$3(points) {
744
747
  if (points.length < 2) return clonePositions(points);
745
748
  if (points.length !== 2) return clonePositions(points.slice(0, 4));
746
749
  const p1 = clonePosition(points[0]);
@@ -764,16 +767,16 @@ const supportByFireDrawRule = {
764
767
  id: "area8:support-by-fire",
765
768
  minimumUserPoints: 2,
766
769
  canonicalPointCount: 4,
767
- derive: derive$2,
770
+ derive: derive$3,
768
771
  transform(event) {
769
772
  const { next } = event;
770
773
  if (next.length === 4) return clonePositions(next);
771
- return derive$2(next);
774
+ return derive$3(next);
772
775
  }
773
776
  };
774
777
  //#endregion
775
778
  //#region src/draw-rules/axis1.ts
776
- function derive$1(points) {
779
+ function derive$2(points) {
777
780
  if (points.length < 2) return clonePositions(points);
778
781
  if (points.length === 2) {
779
782
  const tip = clonePosition(points[0]);
@@ -811,10 +814,10 @@ const axis1DrawRule = {
811
814
  id: "axis1",
812
815
  minimumUserPoints: 2,
813
816
  trailingFixedSlots: 1,
814
- derive: derive$1,
817
+ derive: derive$2,
815
818
  transform(event) {
816
819
  const { previous, next, activePointIndex } = event;
817
- if (next.length < 3 || previous.length < 3) return derive$1(next);
820
+ if (next.length < 3 || previous.length < 3) return derive$2(next);
818
821
  if (!(activePointIndex === 0 || activePointIndex === 1)) return clonePositions(next);
819
822
  const metrics = calculateMetrics(previous);
820
823
  if (!metrics) return clonePositions(next);
@@ -825,7 +828,7 @@ const axis1DrawRule = {
825
828
  };
826
829
  //#endregion
827
830
  //#region src/draw-rules/area21.ts
828
- function derive(points) {
831
+ function derive$1(points) {
829
832
  if (points.length === 2) return clonePositions([
830
833
  points[0],
831
834
  points[1],
@@ -838,9 +841,99 @@ const searchAreaDrawRule = {
838
841
  minimumUserPoints: 3,
839
842
  minimumPreviewPoints: 2,
840
843
  canonicalPointCount: 3,
844
+ derive: derive$1,
845
+ transform(event) {
846
+ return derive$1(event.next);
847
+ }
848
+ };
849
+ //#endregion
850
+ //#region src/draw-rules/rectangle.ts
851
+ /**
852
+ * Oriented rectangle: P1 and P2 are adjacent corners and define the first
853
+ * edge. P3 is the next corner, constrained to the perpendicular through P2.
854
+ * The generator derives the fourth corner.
855
+ */
856
+ function derive(points) {
857
+ if (points.length < 2) return clonePositions(points);
858
+ const p1 = clonePosition(points[0]);
859
+ const p2 = clonePosition(points[1]);
860
+ if (points.length === 2) return [
861
+ p1,
862
+ p2,
863
+ clonePosition(p2)
864
+ ];
865
+ const frame = createBaselineFrame(p1, p2, {
866
+ origin: "p2",
867
+ normal: "right"
868
+ });
869
+ return [
870
+ p1,
871
+ p2,
872
+ frame?.pointAtNormalDistance(frame.signedNormalDistance(points[2])) ?? clonePosition(points[2])
873
+ ];
874
+ }
875
+ function guidePoints(points) {
876
+ const canonical = derive(points);
877
+ if (canonical.length < 3) return canonical;
878
+ const [c1, c2, c3] = canonical;
879
+ if (!c1 || !c2 || !c3) return canonical;
880
+ if (c2[0] === c3[0] && c2[1] === c3[1]) return [c1, c2];
881
+ const p1 = project(c1[0], c1[1]);
882
+ const p2 = project(c2[0], c2[1]);
883
+ const p4 = vecAdd(p1, vecSub(project(c3[0], c3[1]), p2));
884
+ return [
885
+ c1,
886
+ c2,
887
+ c3,
888
+ unproject(p4[0], p4[1]),
889
+ clonePosition(c1)
890
+ ];
891
+ }
892
+ function snapToAxis(point, origin, direction) {
893
+ const snapped = vecAdd(origin, vecScale(direction, vecDot(vecSub(project(point[0], point[1]), origin), direction)));
894
+ return unproject(snapped[0], snapped[1]);
895
+ }
896
+ const rectangleDrawRule = {
897
+ id: "rectangle",
898
+ minimumUserPoints: 3,
899
+ minimumPreviewPoints: 2,
900
+ canonicalPointCount: 3,
901
+ showGuide: true,
902
+ guidePoints,
841
903
  derive,
842
904
  transform(event) {
843
- return derive(event.next);
905
+ const { previous, next, activePointIndex } = event;
906
+ if (previous.length < 3 || next.length < 3) return derive(next);
907
+ const frame = createBaselineFrame(previous[0], previous[1], {
908
+ origin: "p2",
909
+ normal: "right"
910
+ });
911
+ if (!frame) return derive(next);
912
+ if (activePointIndex === 0) return [
913
+ snapToAxis(next[0], frame.p2, frame.direction),
914
+ clonePosition(previous[1]),
915
+ clonePosition(previous[2])
916
+ ];
917
+ if (activePointIndex === 1) {
918
+ const p2 = snapToAxis(next[1], frame.p1, frame.direction);
919
+ const delta = vecSub(project(p2[0], p2[1]), frame.p2);
920
+ const projectedP3 = vecAdd(project(previous[2][0], previous[2][1]), delta);
921
+ const p3 = unproject(projectedP3[0], projectedP3[1]);
922
+ return [
923
+ clonePosition(previous[0]),
924
+ p2,
925
+ p3
926
+ ];
927
+ }
928
+ if (activePointIndex === 2) {
929
+ const p3 = frame.pointAtNormalDistance(frame.signedNormalDistance(next[2]));
930
+ if (p3) return [
931
+ clonePosition(previous[0]),
932
+ clonePosition(previous[1]),
933
+ p3
934
+ ];
935
+ }
936
+ return derive(next);
844
937
  }
845
938
  };
846
939
  //#endregion
@@ -2330,13 +2423,21 @@ function resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel) {
2330
2423
  if (echelonSizePixels !== void 0 && metersPerPixel !== void 0 && metersPerPixel > 0) h = echelonSizePixels * metersPerPixel;
2331
2424
  return Math.max(EPSILON, h);
2332
2425
  }
2426
+ /** Wraps `x` into `[0, 1)`, treating `x` as a fraction (matches the arc-length wrap below). */
2427
+ function wrap01(x) {
2428
+ return (x % 1 + 1) % 1;
2429
+ }
2333
2430
  /**
2334
2431
  * Places the Field B echelon glyph on the perimeter and computes its masking
2335
- * gap, anchored to the bottom edge and slid by `echelonPosition` (a signed
2336
- * fraction of the perimeter, wrapping at the seam). Returns empty geometry
2337
- * when the echelon resolves to none or the ring is degenerate.
2432
+ * gap, anchored by default to the bottom edge and slid by `echelonPosition`
2433
+ * (a signed fraction of the perimeter, wrapping at the seam). When
2434
+ * `echelonAnchor` is supplied, it replaces the bottom-edge anchor with an
2435
+ * absolute perimeter fraction (measured from the ring seam) — see
2436
+ * `BattlePositionOptions.echelonAnchor` for why (ADR-0023 rotate-freeze).
2437
+ * Returns empty geometry when the echelon resolves to none or the ring is
2438
+ * degenerate.
2338
2439
  */
2339
- function placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition) {
2440
+ function placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition, echelonAnchor) {
2340
2441
  const { segs, totalLength, anchorTarget } = perimeter;
2341
2442
  const spec = resolveEchelon(echelon);
2342
2443
  if (!spec || segs.length === 0 || totalLength < 1e-6) return {
@@ -2344,7 +2445,7 @@ function placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition) {
2344
2445
  fills: [],
2345
2446
  gaps: []
2346
2447
  };
2347
- const target = ((anchorTarget + echelonPosition * totalLength) % totalLength + totalLength) % totalLength;
2448
+ const target = (((echelonAnchor !== void 0 ? wrap01(echelonAnchor) * totalLength : anchorTarget) + echelonPosition * totalLength) % totalLength + totalLength) % totalLength;
2348
2449
  const { point, seg } = pointOnRing(segs, target);
2349
2450
  const { strokes, fills, width } = buildEchelonGlyph(point, seg.along, seg.perp, h, spec);
2350
2451
  const gaps = [];
@@ -2454,10 +2555,10 @@ const BATTLE_POSITION_METADATA = {
2454
2555
  * @param options - {@link BattlePositionOptions}.
2455
2556
  */
2456
2557
  function createBattlePosition(positions, options = {}) {
2457
- const { echelon = DEFAULT_BATTLE_POSITION_OPTIONS.echelon, echelonSize = DEFAULT_BATTLE_POSITION_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPadding, echelonPosition = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPosition, metersPerPixel, smooth = DEFAULT_BATTLE_POSITION_OPTIONS.smooth, smoothResolution = DEFAULT_BATTLE_POSITION_OPTIONS.smoothResolution } = options;
2558
+ const { echelon = DEFAULT_BATTLE_POSITION_OPTIONS.echelon, echelonSize = DEFAULT_BATTLE_POSITION_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPadding, echelonPosition = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPosition, echelonAnchor, metersPerPixel, smooth = DEFAULT_BATTLE_POSITION_OPTIONS.smooth, smoothResolution = DEFAULT_BATTLE_POSITION_OPTIONS.smoothResolution } = options;
2458
2559
  const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
2459
2560
  const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$6);
2460
- const { strokes, fills, gaps } = placeEchelon(buildAreaPerimeter(verts), h, echelon, echelonPadding, echelonPosition);
2561
+ const { strokes, fills, gaps } = placeEchelon(buildAreaPerimeter(verts), h, echelon, echelonPadding, echelonPosition, echelonAnchor);
2461
2562
  const boundaryCoords = buildGappedLine(verts, gaps);
2462
2563
  const features = [];
2463
2564
  if (boundaryCoords.length > 0) features.push({
@@ -2492,11 +2593,29 @@ function createBattlePosition(positions, options = {}) {
2492
2593
  features
2493
2594
  };
2494
2595
  }
2596
+ /**
2597
+ * `freezeOrientation` hook (ADR-0023) shared by Battle Position and Strong
2598
+ * Point: pins the echelon anchor to its current bottom-edge fraction so a
2599
+ * subsequent rigid rotation of `controlPoints` (e.g. the transform box's
2600
+ * rotate grip) does not re-derive a different "bottom edge" against the
2601
+ * rotated geometry. Returns `undefined` — nothing to freeze — when:
2602
+ * - `echelonAnchor` is already set (previously frozen or user-authored), or
2603
+ * - the echelon resolves to none (nothing visible to pin), or
2604
+ * - the ring is degenerate (no segments, or a near-zero perimeter).
2605
+ */
2606
+ function freezeEchelonAnchor(controlPoints, options) {
2607
+ if (options.echelonAnchor !== void 0) return void 0;
2608
+ if (!resolveEchelon(options.echelon ?? DEFAULT_BATTLE_POSITION_OPTIONS.echelon)) return void 0;
2609
+ const perimeter = buildAreaPerimeter(buildClosedRing(controlPoints, options.smooth ?? DEFAULT_BATTLE_POSITION_OPTIONS.smooth, options.smoothResolution ?? DEFAULT_BATTLE_POSITION_OPTIONS.smoothResolution, DEFAULT_SMOOTH_RESOLUTION$6));
2610
+ if (perimeter.segs.length === 0 || perimeter.totalLength < 1e-6) return void 0;
2611
+ return { echelonAnchor: perimeter.anchorTarget / perimeter.totalLength };
2612
+ }
2495
2613
  const BATTLE_POSITION = defineControlMeasure({
2496
2614
  metadata: BATTLE_POSITION_METADATA,
2497
2615
  generator: createBattlePosition,
2498
2616
  defaultOptions: DEFAULT_BATTLE_POSITION_OPTIONS,
2499
2617
  rule: area1DrawRule,
2618
+ freezeOrientation: freezeEchelonAnchor,
2500
2619
  previewSample: {
2501
2620
  controlPoints: [
2502
2621
  [-1, -.5],
@@ -2594,7 +2713,7 @@ const BLOCK_ARROW_METADATA = {
2594
2713
  id: "block-arrow",
2595
2714
  name: "Block Arrow",
2596
2715
  description: "Solid filled arrow with a controllable shaft width and selectable head (triangle, barbed, concave, diamond, harpoon, swallowtail, tee). Not a doctrinal symbol.",
2597
- entity: "Generic Arrows",
2716
+ entity: "Generic Graphics",
2598
2717
  entityType: "Illustrative Arrow",
2599
2718
  entitySubtype: "Block Arrow",
2600
2719
  value: "990001",
@@ -3622,7 +3741,7 @@ const CLASSIC_ARROW_METADATA = {
3622
3741
  id: "classic-arrow",
3623
3742
  name: "Classic Arrow",
3624
3743
  description: "Clean straight arrow with a selectable head (triangle, barbed, concave, diamond, harpoon, swallowtail, chevron, circle, tee, open). Not a doctrinal symbol.",
3625
- entity: "Generic Arrows",
3744
+ entity: "Generic Graphics",
3626
3745
  entityType: "Illustrative Arrow",
3627
3746
  entitySubtype: "Classic Arrow",
3628
3747
  value: "990002",
@@ -3905,6 +4024,271 @@ function normalizeSmoothResolution(value) {
3905
4024
  return Math.min(MAX_SMOOTH_RESOLUTION, Math.max(MIN_SMOOTH_RESOLUTION, Math.round(value)));
3906
4025
  }
3907
4026
  //#endregion
4027
+ //#region src/generators/cm99-generic-graphics/params.ts
4028
+ const SMOOTH_PATH_PARAMS = [{
4029
+ key: "smooth",
4030
+ label: "Smooth",
4031
+ description: "Curve the path through the control points.",
4032
+ type: "boolean"
4033
+ }, {
4034
+ key: "smoothResolution",
4035
+ label: "Smooth resolution",
4036
+ description: "Number of samples per segment when smooth mode is enabled.",
4037
+ type: "number",
4038
+ min: 2,
4039
+ max: 64,
4040
+ step: 1,
4041
+ visibleWhen: (opts) => Boolean(opts.smooth)
4042
+ }];
4043
+ const FILLED_AREA_PARAMS = [{
4044
+ key: "filled",
4045
+ label: "Filled",
4046
+ description: "Fill the interior instead of rendering an outline only.",
4047
+ type: "boolean"
4048
+ }];
4049
+ //#endregion
4050
+ //#region src/generators/cm99-generic-graphics/circle.ts
4051
+ const CIRCLE_SEGMENTS = 64;
4052
+ const DEFAULT_GENERIC_CIRCLE_OPTIONS = { filled: false };
4053
+ const GENERIC_CIRCLE_METADATA = {
4054
+ id: "circle",
4055
+ name: "Circle",
4056
+ description: "A non-doctrinal circle defined by a center and radius point.",
4057
+ entity: "Generic Graphics",
4058
+ entityType: "Basic Shape",
4059
+ entitySubtype: "Circle",
4060
+ value: "990104",
4061
+ minCoordinates: 2,
4062
+ maxCoordinates: 2,
4063
+ geometry: "area",
4064
+ geometryTypes: ["Polygon"],
4065
+ drawRule: "Area15",
4066
+ params: FILLED_AREA_PARAMS
4067
+ };
4068
+ function createGenericCircle(coordinates, options = {}) {
4069
+ const center = project(coordinates[0][0], coordinates[0][1]);
4070
+ const radiusVector = vecSub(project(coordinates[1][0], coordinates[1][1]), center);
4071
+ const radius = vecMag(radiusVector);
4072
+ if (radius < 1e-6) return {
4073
+ type: "FeatureCollection",
4074
+ features: []
4075
+ };
4076
+ const startAngle = Math.atan2(radiusVector[1], radiusVector[0]);
4077
+ const ring = [];
4078
+ for (let index = 0; index <= CIRCLE_SEGMENTS; index++) {
4079
+ const angle = startAngle + index / CIRCLE_SEGMENTS * Math.PI * 2;
4080
+ ring.push(unproject(center[0] + Math.cos(angle) * radius, center[1] + Math.sin(angle) * radius));
4081
+ }
4082
+ return {
4083
+ type: "FeatureCollection",
4084
+ features: [{
4085
+ type: "Feature",
4086
+ properties: {
4087
+ part: "circle",
4088
+ fill: options.filled ?? DEFAULT_GENERIC_CIRCLE_OPTIONS.filled
4089
+ },
4090
+ geometry: {
4091
+ type: "Polygon",
4092
+ coordinates: [ring]
4093
+ }
4094
+ }]
4095
+ };
4096
+ }
4097
+ const GENERIC_CIRCLE = defineControlMeasure({
4098
+ metadata: GENERIC_CIRCLE_METADATA,
4099
+ generator: createGenericCircle,
4100
+ defaultOptions: DEFAULT_GENERIC_CIRCLE_OPTIONS,
4101
+ rule: centerRadiusDrawRule,
4102
+ previewSample: { controlPoints: [[0, 0], [.8, 0]] }
4103
+ });
4104
+ //#endregion
4105
+ //#region src/generators/cm99-generic-graphics/line.ts
4106
+ const DEFAULT_GENERIC_LINE_OPTIONS = {
4107
+ smooth: false,
4108
+ smoothResolution: 12
4109
+ };
4110
+ const GENERIC_LINE_METADATA = {
4111
+ id: "line",
4112
+ name: "Line",
4113
+ description: "A non-doctrinal free polyline through two or more control points.",
4114
+ entity: "Generic Graphics",
4115
+ entityType: "Basic Shape",
4116
+ entitySubtype: "Line",
4117
+ value: "990101",
4118
+ minCoordinates: 2,
4119
+ geometry: "line",
4120
+ geometryTypes: ["LineString"],
4121
+ drawRule: "Line1",
4122
+ params: SMOOTH_PATH_PARAMS
4123
+ };
4124
+ function createGenericLine(coordinates, options = {}) {
4125
+ const projected = coordinates.map((point) => project(point[0], point[1]));
4126
+ if (!projected.some((point, index) => {
4127
+ const next = projected[index + 1];
4128
+ return next ? Math.hypot(next[0] - point[0], next[1] - point[1]) >= 1e-6 : false;
4129
+ })) return {
4130
+ type: "FeatureCollection",
4131
+ features: []
4132
+ };
4133
+ const { smooth, smoothResolution } = {
4134
+ ...DEFAULT_GENERIC_LINE_OPTIONS,
4135
+ ...options
4136
+ };
4137
+ const resolution = normalizeSmoothResolution$2(smoothResolution, 12);
4138
+ return {
4139
+ type: "FeatureCollection",
4140
+ features: [{
4141
+ type: "Feature",
4142
+ properties: { part: "line" },
4143
+ geometry: {
4144
+ type: "LineString",
4145
+ coordinates: (smooth ? catmullRom(projected, resolution) : projected).map((point) => unproject(point[0], point[1]))
4146
+ }
4147
+ }]
4148
+ };
4149
+ }
4150
+ const GENERIC_LINE = defineControlMeasure({
4151
+ metadata: GENERIC_LINE_METADATA,
4152
+ generator: createGenericLine,
4153
+ defaultOptions: DEFAULT_GENERIC_LINE_OPTIONS,
4154
+ rule: line1DrawRule,
4155
+ previewSample: { controlPoints: [
4156
+ [-1, -.4],
4157
+ [0, .5],
4158
+ [1, -.2]
4159
+ ] }
4160
+ });
4161
+ //#endregion
4162
+ //#region src/generators/cm99-generic-graphics/polygon.ts
4163
+ const DEFAULT_GENERIC_POLYGON_OPTIONS = {
4164
+ smooth: false,
4165
+ smoothResolution: 12,
4166
+ filled: false
4167
+ };
4168
+ const GENERIC_POLYGON_METADATA = {
4169
+ id: "polygon",
4170
+ name: "Polygon",
4171
+ description: "A non-doctrinal free polygon with one outer ring.",
4172
+ entity: "Generic Graphics",
4173
+ entityType: "Basic Shape",
4174
+ entitySubtype: "Polygon",
4175
+ value: "990102",
4176
+ minCoordinates: 3,
4177
+ geometry: "area",
4178
+ geometryTypes: ["Polygon"],
4179
+ drawRule: "Area1",
4180
+ params: [...SMOOTH_PATH_PARAMS, ...FILLED_AREA_PARAMS]
4181
+ };
4182
+ function createGenericPolygon(coordinates, options = {}) {
4183
+ const projected = coordinates.map((point) => project(point[0], point[1]));
4184
+ const ringPoints = projected.length > 1 && vecMag(vecSub(projected[0], projected.at(-1))) < 1e-6 ? projected.slice(0, -1) : projected;
4185
+ if (ringPoints.length < 3) return {
4186
+ type: "FeatureCollection",
4187
+ features: []
4188
+ };
4189
+ const { smooth, smoothResolution, filled } = {
4190
+ ...DEFAULT_GENERIC_POLYGON_OPTIONS,
4191
+ ...options
4192
+ };
4193
+ const resolution = normalizeSmoothResolution$2(smoothResolution, 12);
4194
+ const ring = smooth ? closedCatmullRom(ringPoints, resolution) : [...ringPoints, ringPoints[0]];
4195
+ return {
4196
+ type: "FeatureCollection",
4197
+ features: [{
4198
+ type: "Feature",
4199
+ properties: {
4200
+ part: "polygon",
4201
+ fill: filled
4202
+ },
4203
+ geometry: {
4204
+ type: "Polygon",
4205
+ coordinates: [ring.map((point) => unproject(point[0], point[1]))]
4206
+ }
4207
+ }]
4208
+ };
4209
+ }
4210
+ const GENERIC_POLYGON = defineControlMeasure({
4211
+ metadata: GENERIC_POLYGON_METADATA,
4212
+ generator: createGenericPolygon,
4213
+ defaultOptions: DEFAULT_GENERIC_POLYGON_OPTIONS,
4214
+ rule: area1DrawRule,
4215
+ previewSample: { controlPoints: [
4216
+ [-.9, -.7],
4217
+ [.8, -.6],
4218
+ [1, .5],
4219
+ [-.4, .8]
4220
+ ] }
4221
+ });
4222
+ //#endregion
4223
+ //#region src/generators/cm99-generic-graphics/rectangle.ts
4224
+ const DEFAULT_GENERIC_RECTANGLE_OPTIONS = { filled: false };
4225
+ const GENERIC_RECTANGLE_METADATA = {
4226
+ id: "rectangle",
4227
+ name: "Rectangle",
4228
+ description: "A non-doctrinal oriented rectangle defined by three adjacent corners.",
4229
+ entity: "Generic Graphics",
4230
+ entityType: "Basic Shape",
4231
+ entitySubtype: "Rectangle",
4232
+ value: "990103",
4233
+ minCoordinates: 3,
4234
+ maxCoordinates: 3,
4235
+ geometry: "area",
4236
+ geometryTypes: ["Polygon"],
4237
+ drawRule: "Rectangle",
4238
+ params: FILLED_AREA_PARAMS
4239
+ };
4240
+ function createGenericRectangle(coordinates, options = {}) {
4241
+ const [c1, c2, c3] = rectangleDrawRule.derive(coordinates);
4242
+ if (!c1 || !c2 || !c3) return {
4243
+ type: "FeatureCollection",
4244
+ features: []
4245
+ };
4246
+ const p1 = project(c1[0], c1[1]);
4247
+ const p2 = project(c2[0], c2[1]);
4248
+ const p3 = project(c3[0], c3[1]);
4249
+ if (Math.hypot(p2[0] - p1[0], p2[1] - p1[1]) < 1e-6) return {
4250
+ type: "FeatureCollection",
4251
+ features: []
4252
+ };
4253
+ const fill = options.filled ?? DEFAULT_GENERIC_RECTANGLE_OPTIONS.filled;
4254
+ if (Math.hypot(p3[0] - p2[0], p3[1] - p2[1]) < 1e-6) return {
4255
+ type: "FeatureCollection",
4256
+ features: []
4257
+ };
4258
+ const ring = [
4259
+ p1,
4260
+ p2,
4261
+ p3,
4262
+ [p1[0] + p3[0] - p2[0], p1[1] + p3[1] - p2[1]],
4263
+ p1
4264
+ ].map((point) => unproject(point[0], point[1]));
4265
+ return {
4266
+ type: "FeatureCollection",
4267
+ features: [{
4268
+ type: "Feature",
4269
+ properties: {
4270
+ part: "rectangle",
4271
+ fill
4272
+ },
4273
+ geometry: {
4274
+ type: "Polygon",
4275
+ coordinates: [ring]
4276
+ }
4277
+ }]
4278
+ };
4279
+ }
4280
+ const GENERIC_RECTANGLE = defineControlMeasure({
4281
+ metadata: GENERIC_RECTANGLE_METADATA,
4282
+ generator: createGenericRectangle,
4283
+ defaultOptions: DEFAULT_GENERIC_RECTANGLE_OPTIONS,
4284
+ rule: rectangleDrawRule,
4285
+ previewSample: { controlPoints: [
4286
+ [-.9, -.55],
4287
+ [.9, -.55],
4288
+ [.9, .55]
4289
+ ] }
4290
+ });
4291
+ //#endregion
3908
4292
  //#region src/generators/cm34-mission-tasks/clear.ts
3909
4293
  /**
3910
4294
  * Default options for the CLEAR symbol.
@@ -6248,11 +6632,11 @@ function buildStrongPointTics(perimeter, gaps, h) {
6248
6632
  * @param options - {@link StrongPointOptions}.
6249
6633
  */
6250
6634
  function createStrongPoint(positions, options = {}) {
6251
- const { echelon = DEFAULT_STRONG_POINT_OPTIONS.echelon, echelonSize = DEFAULT_STRONG_POINT_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_STRONG_POINT_OPTIONS.echelonPadding, echelonPosition = DEFAULT_STRONG_POINT_OPTIONS.echelonPosition, metersPerPixel, smooth = DEFAULT_STRONG_POINT_OPTIONS.smooth, smoothResolution = DEFAULT_STRONG_POINT_OPTIONS.smoothResolution } = options;
6635
+ const { echelon = DEFAULT_STRONG_POINT_OPTIONS.echelon, echelonSize = DEFAULT_STRONG_POINT_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_STRONG_POINT_OPTIONS.echelonPadding, echelonPosition = DEFAULT_STRONG_POINT_OPTIONS.echelonPosition, echelonAnchor, metersPerPixel, smooth = DEFAULT_STRONG_POINT_OPTIONS.smooth, smoothResolution = DEFAULT_STRONG_POINT_OPTIONS.smoothResolution } = options;
6252
6636
  const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
6253
6637
  const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION);
6254
6638
  const perimeter = buildAreaPerimeter(verts);
6255
- const { strokes, fills, gaps } = placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition);
6639
+ const { strokes, fills, gaps } = placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition, echelonAnchor);
6256
6640
  const boundaryCoords = buildGappedLine(verts, gaps);
6257
6641
  const tics = buildStrongPointTics(perimeter, gaps, h);
6258
6642
  const features = [];
@@ -6301,6 +6685,7 @@ const STRONG_POINT = defineControlMeasure({
6301
6685
  generator: createStrongPoint,
6302
6686
  defaultOptions: DEFAULT_STRONG_POINT_OPTIONS,
6303
6687
  rule: area1DrawRule,
6688
+ freezeOrientation: freezeEchelonAnchor,
6304
6689
  previewSample: {
6305
6690
  controlPoints: [
6306
6691
  [-1, -.5],
@@ -6654,6 +7039,10 @@ const DEFINITIONS = {
6654
7039
  "supporting-attack": SUPPORTING_ATTACK,
6655
7040
  "classic-arrow": CLASSIC_ARROW,
6656
7041
  "block-arrow": BLOCK_ARROW,
7042
+ line: GENERIC_LINE,
7043
+ polygon: GENERIC_POLYGON,
7044
+ rectangle: GENERIC_RECTANGLE,
7045
+ circle: GENERIC_CIRCLE,
6657
7046
  "airborne-attack": AIRBORNE_ATTACK,
6658
7047
  "attack-helicopter": ATTACK_HELICOPTER,
6659
7048
  "support-by-fire": SUPPORT_BY_FIRE,
@@ -6904,4 +7293,4 @@ function assertNever(value) {
6904
7293
  throw new Error(`Unhandled control measure kind: ${String(value)}`);
6905
7294
  }
6906
7295
  //#endregion
6907
- export { getMidpointPerpendicularSignedDistance as $, DEFAULT_BYPASS_OPTIONS as A, axis1DrawRule as B, DEFAULT_FIX_OPTIONS as C, DEFAULT_DELAY_OPTIONS as D, DEFAULT_DISRUPT_OPTIONS as E, DEFAULT_ATTACK_BY_FIRE_OPTIONS as F, line1DrawRule as G, line24DrawRule as H, DEFAULT_ANTITANK_WALL_OPTIONS as I, point12DrawRule as J, ambushDrawRule as K, DEFAULT_ANTITANK_DITCH_OPTIONS as L, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as M, DEFAULT_BLOCK_OPTIONS as N, DEFAULT_CLEAR_OPTIONS as O, DEFAULT_ATTACK_HELICOPTER_OPTIONS as P, createMidpointPerpendicularDrawRule as Q, DEFAULT_AMBUSH_OPTIONS as R, DEFAULT_FLOT_OPTIONS as S, DEFAULT_ENCIRCLEMENT_OPTIONS as T, line23DrawRule as U, supportByFireDrawRule as V, turnDrawRule as W, blockDrawRule as X, disruptDrawRule as Y, computeDefaultMidpointPerpendicularPoint as Z, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as _, DEFINITIONS as a, project as at, DEFAULT_FORTIFIED_AREA_OPTIONS as b, getDefaultOptions as c, getMetersPerPixel as ct, DEFAULT_SUPPORTING_ATTACK_OPTIONS as d, pointOnMidpointPerpendicularAxis as et, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as f, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as g, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as h, CONTROL_MEASURE_METADATA as i, computeInitialWidthPoint as it, DEFAULT_BREACH_OPTIONS as j, DEFAULT_CANALIZE_OPTIONS as k, listControlMeasureMetadata as l, roundToFixed as lt, DEFAULT_TACTICAL_ARROW_OPTIONS as m, resolveStyleHints as n, createBaselineFrame as nt, getControlMeasureMetadata as o, unproject as ot, DEFAULT_STRONG_POINT_OPTIONS as p, attackByFireDrawRule as q, CONTROL_MEASURE_IDS as r, calculateMetrics as rt, getControlMeasureMetadataByValue as s, EPSILON as st, renderControlMeasure as t, snapToMidpointPerpendicular as tt, DEFAULT_TURN_OPTIONS as u, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as v, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as w, DEFAULT_FORTIFIED_LINE_OPTIONS as x, DEFAULT_MAIN_ATTACK_OPTIONS as y, DEFAULT_AIRBORNE_ATTACK_OPTIONS as z };
7296
+ export { point12DrawRule as $, DEFAULT_GENERIC_POLYGON_OPTIONS as A, DEFAULT_ANTITANK_WALL_OPTIONS as B, DEFAULT_FIX_OPTIONS as C, DEFAULT_DELAY_OPTIONS as D, DEFAULT_DISRUPT_OPTIONS as E, DEFAULT_BREACH_OPTIONS as F, axis1DrawRule as G, DEFAULT_AMBUSH_OPTIONS as H, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as I, line23DrawRule as J, supportByFireDrawRule as K, DEFAULT_BLOCK_OPTIONS as L, DEFAULT_GENERIC_CIRCLE_OPTIONS as M, DEFAULT_CANALIZE_OPTIONS as N, DEFAULT_CLEAR_OPTIONS as O, DEFAULT_BYPASS_OPTIONS as P, attackByFireDrawRule as Q, DEFAULT_ATTACK_HELICOPTER_OPTIONS as R, DEFAULT_FLOT_OPTIONS as S, DEFAULT_ENCIRCLEMENT_OPTIONS as T, DEFAULT_AIRBORNE_ATTACK_OPTIONS as U, DEFAULT_ANTITANK_DITCH_OPTIONS as V, rectangleDrawRule as W, line1DrawRule as X, turnDrawRule as Y, ambushDrawRule as Z, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as _, DEFINITIONS as a, getMidpointPerpendicularSignedDistance as at, DEFAULT_FORTIFIED_AREA_OPTIONS as b, getDefaultOptions as c, createBaselineFrame as ct, DEFAULT_SUPPORTING_ATTACK_OPTIONS as d, project as dt, centerRadiusDrawRule as et, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as f, unproject as ft, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as g, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as h, roundToFixed as ht, CONTROL_MEASURE_METADATA as i, createMidpointPerpendicularDrawRule as it, DEFAULT_GENERIC_LINE_OPTIONS as j, DEFAULT_GENERIC_RECTANGLE_OPTIONS as k, listControlMeasureMetadata as l, calculateMetrics as lt, DEFAULT_TACTICAL_ARROW_OPTIONS as m, getMetersPerPixel as mt, resolveStyleHints as n, blockDrawRule as nt, getControlMeasureMetadata as o, pointOnMidpointPerpendicularAxis as ot, DEFAULT_STRONG_POINT_OPTIONS as p, EPSILON as pt, line24DrawRule as q, CONTROL_MEASURE_IDS as r, computeDefaultMidpointPerpendicularPoint as rt, getControlMeasureMetadataByValue as s, snapToMidpointPerpendicular as st, renderControlMeasure as t, disruptDrawRule as tt, DEFAULT_TURN_OPTIONS as u, computeInitialWidthPoint as ut, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as v, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as w, DEFAULT_FORTIFIED_LINE_OPTIONS as x, DEFAULT_MAIN_ATTACK_OPTIONS as y, DEFAULT_ATTACK_BY_FIRE_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.4",
3
+ "version": "0.2.0-alpha.6",
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",