@orbat-mapper/control-measures 0.2.0-alpha.2 → 0.2.0-alpha.3

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.
@@ -339,6 +339,23 @@ interface BattlePositionOptions {
339
339
  */
340
340
  declare function createBattlePosition(positions: Position[], options?: BattlePositionOptions): FeatureCollection<MultiLineString | MultiPolygon>;
341
341
  //#endregion
342
+ //#region src/generators/cm15-maneuver-areas/strongPoint.d.ts
343
+ /** Configuration options for the Strong Point control measure. */
344
+ type StrongPointOptions = BattlePositionOptions;
345
+ /** Default options for the Strong Point control measure. */
346
+ declare const DEFAULT_STRONG_POINT_OPTIONS: StrongPointOptions;
347
+ /**
348
+ * Creates a Strong Point control measure: a Battle Position boundary/echelon
349
+ * with outward tics whose length and spacing are the Field B echelon height.
350
+ * Shares the perimeter and echelon placement with Battle Position via the
351
+ * `area-echelon` primitive (single source of truth for ring and gap).
352
+ *
353
+ * @param positions - Area anchor points (>=3); the input contract is enforced
354
+ * at the render seam (ADR-0014).
355
+ * @param options - {@link StrongPointOptions}.
356
+ */
357
+ declare function createStrongPoint(positions: Position[], options?: StrongPointOptions): FeatureCollection<MultiLineString | MultiPolygon>;
358
+ //#endregion
342
359
  //#region src/generators/cm14-maneuver-lines/principalDirectionOfFire.d.ts
343
360
  /**
344
361
  * Configuration options for the Principal Direction of Fire tactical symbol.
@@ -390,6 +407,47 @@ type FpfProps = {
390
407
  declare function createFinalProtectiveFireLeft(coordinates: Position[], options?: FinalProtectiveFireOptions): FeatureCollection<MultiLineString | Polygon, FpfProps>;
391
408
  declare function createFinalProtectiveFireRight(coordinates: Position[], options?: FinalProtectiveFireOptions): FeatureCollection<MultiLineString | Polygon, FpfProps>;
392
409
  //#endregion
410
+ //#region src/generators/cm15-maneuver-areas/encirclement.d.ts
411
+ /** Configuration options for the Encirclement control measure. */
412
+ interface EncirclementOptions {
413
+ /**
414
+ * Spacing between adjacent barbs as a ratio of the symbol's mean radius;
415
+ * smaller values pack the barbs more densely. The barbs are then distributed
416
+ * evenly so they close the ring exactly.
417
+ * @default 0.4
418
+ */
419
+ barbSpacingRatio?: number;
420
+ /**
421
+ * Barb length (height/amplitude) as a ratio of the symbol's mean radius.
422
+ * @default 0.16
423
+ */
424
+ barbLengthRatio?: number;
425
+ /**
426
+ * Round the boundary by curving it through the control points (closed
427
+ * centripetal Catmull-Rom) before tracing the barbs.
428
+ * @default true
429
+ */
430
+ smooth?: boolean;
431
+ /**
432
+ * Number of samples per segment when `smooth` is enabled.
433
+ * @default 16
434
+ */
435
+ smoothResolution?: number;
436
+ }
437
+ /** Default options for the Encirclement control measure. */
438
+ declare const DEFAULT_ENCIRCLEMENT_OPTIONS: Required<EncirclementOptions>;
439
+ /**
440
+ * Creates an Encirclement tactical graphic: a complete smooth boundary ring
441
+ * with outward arrowhead barbs resting on it. The boundary is an unfilled
442
+ * Polygon; the barbs are open "V" strokes (a MultiLineString) whose base is the
443
+ * boundary arc itself.
444
+ *
445
+ * @param positions - Area anchor points (>=3); the input contract is enforced
446
+ * at the render seam (ADR-0014).
447
+ * @param options - {@link EncirclementOptions}.
448
+ */
449
+ declare function createEncirclement(positions: Position[], options?: EncirclementOptions): FeatureCollection<Polygon | MultiLineString>;
450
+ //#endregion
393
451
  //#region src/projection.d.ts
394
452
  type Point2D = [number, number];
395
453
  /**
@@ -1193,6 +1251,7 @@ declare function createObstacleBypassImpossible(coordinates: Position[], options
1193
1251
  declare const DEFINITIONS: {
1194
1252
  boundary: ControlMeasureDefinition<"boundary", typeof createBoundary>;
1195
1253
  "battle-position": ControlMeasureDefinition<"battle-position", typeof createBattlePosition>;
1254
+ "strong-point": ControlMeasureDefinition<"strong-point", typeof createStrongPoint>;
1196
1255
  ambush: ControlMeasureDefinition<"ambush", (controlPoints: import("geojson").Position[], options?: AmbushOptions) => import("geojson").FeatureCollection<import("geojson").MultiLineString, Record<string, never>>>;
1197
1256
  "principal-direction-of-fire": ControlMeasureDefinition<"principal-direction-of-fire", typeof createPrincipalDirectionOfFire>;
1198
1257
  "final-protective-fire-left": ControlMeasureDefinition<"final-protective-fire-left", typeof createFinalProtectiveFireLeft>;
@@ -1201,6 +1260,7 @@ declare const DEFINITIONS: {
1201
1260
  part: "shaft" | "head";
1202
1261
  fill?: boolean;
1203
1262
  }>>;
1263
+ encirclement: ControlMeasureDefinition<"encirclement", typeof createEncirclement>;
1204
1264
  "main-attack": ControlMeasureDefinition<"main-attack", typeof createMainAttack>;
1205
1265
  "supporting-attack": ControlMeasureDefinition<"supporting-attack", typeof createSupportingAttack>;
1206
1266
  "classic-arrow": ControlMeasureDefinition<"classic-arrow", typeof createClassicArrow>;
@@ -1650,4 +1710,4 @@ interface TacticalArrowOptions {
1650
1710
  */
1651
1711
  declare const DEFAULT_TACTICAL_ARROW_OPTIONS: Required<TacticalArrowOptions>;
1652
1712
  //#endregion
1653
- export { listControlMeasureMetadata as $, DEFAULT_FINAL_PROTECTIVE_FIRE_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, ControlMeasureMetadata as an, ObstacleBypassEasyOptions as at, pointOnMidpointPerpendicularAxis as b, AntitankDitchOptions as bt, line23DrawRule as c, ControlMeasureDrawRule as cn, DEFAULT_FIX_OPTIONS as ct, ambushDrawRule as d, DisruptOptions as dt, FinalProtectiveFireOptions as en, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as et, attackByFireDrawRule as f, BlockOptions as ft, MidpointPerpendicularDrawRuleOptions as g, DEFAULT_FORTIFIED_LINE_OPTIONS as gt, blockDrawRule as h, FortifiedAreaOptions as ht, DEFAULT_AMBUSH_OPTIONS as i, ControlMeasureGeometryType 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, FixOptions as lt, disruptDrawRule as m, DEFAULT_FORTIFIED_AREA_OPTIONS as mt, TacticalArrowOptions as n, PrincipalDirectionOfFireOptions as nn, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as nt, supportByFireDrawRule as o, ParamDescriptor as on, DEFAULT_TURN_OPTIONS as ot, point12DrawRule as p, DEFAULT_BLOCK_OPTIONS as pt, ControlMeasureId as q, MainAttackOptions as qt, AmbushOptions as r, ControlMeasureGeometry as rn, ObstacleBypassDifficultOptions as rt, line24DrawRule as s, AnchorTransformEvent as sn, TurnOptions as st, DEFAULT_TACTICAL_ARROW_OPTIONS as t, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as tn, ObstacleBypassImpossibleOptions as tt, line1DrawRule as u, 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 };
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 };
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as listControlMeasureMetadata, $t as DEFAULT_FINAL_PROTECTIVE_FIRE_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 ControlMeasureMetadata, at as ObstacleBypassEasyOptions, b as pointOnMidpointPerpendicularAxis, bt as AntitankDitchOptions, c as line23DrawRule, cn as ControlMeasureDrawRule, ct as DEFAULT_FIX_OPTIONS, d as ambushDrawRule, dt as DisruptOptions, en as FinalProtectiveFireOptions, et as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, f as attackByFireDrawRule, 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 ControlMeasureGeometryType, 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, lt as FixOptions, m as disruptDrawRule, mt as DEFAULT_FORTIFIED_AREA_OPTIONS, n as TacticalArrowOptions, nn as PrincipalDirectionOfFireOptions, nt as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, o as supportByFireDrawRule, on as ParamDescriptor, ot as DEFAULT_TURN_OPTIONS, p as point12DrawRule, pt as DEFAULT_BLOCK_OPTIONS, q as ControlMeasureId, qt as MainAttackOptions, r as AmbushOptions, rn as ControlMeasureGeometry, rt as ObstacleBypassDifficultOptions, s as line24DrawRule, sn as AnchorTransformEvent, st as TurnOptions, t as DEFAULT_TACTICAL_ARROW_OPTIONS, tn as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, tt as ObstacleBypassImpossibleOptions, u as line1DrawRule, 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-D7uBPw7l.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_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_SUPPORTING_ATTACK_OPTIONS, DEFAULT_SUPPORT_BY_FIRE_OPTIONS, DEFAULT_TACTICAL_ARROW_OPTIONS, DEFAULT_TURN_OPTIONS, type DelayOptions, type DisruptOptions, EPSILON, 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 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 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 };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as snapToMidpointPerpendicular, A as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, B as line24DrawRule, C as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, D as DEFAULT_CANALIZE_OPTIONS, E as DEFAULT_CLEAR_OPTIONS, F as DEFAULT_ANTITANK_DITCH_OPTIONS, G as attackByFireDrawRule, H as turnDrawRule, I as DEFAULT_AMBUSH_OPTIONS, J as blockDrawRule, K as point12DrawRule, L as DEFAULT_AIRBORNE_ATTACK_OPTIONS, M as DEFAULT_ATTACK_HELICOPTER_OPTIONS, N as DEFAULT_ATTACK_BY_FIRE_OPTIONS, O as DEFAULT_BYPASS_OPTIONS, P as DEFAULT_ANTITANK_WALL_OPTIONS, Q as pointOnMidpointPerpendicularAxis, R as axis1DrawRule, S as DEFAULT_FIX_OPTIONS, T as DEFAULT_DELAY_OPTIONS, U as line1DrawRule, V as line23DrawRule, W as ambushDrawRule, X as createMidpointPerpendicularDrawRule, Y as computeDefaultMidpointPerpendicularPoint, Z as getMidpointPerpendicularSignedDistance, _ as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, at as EPSILON, b as DEFAULT_FORTIFIED_LINE_OPTIONS, c as getDefaultOptions, d as DEFAULT_SUPPORTING_ATTACK_OPTIONS, et as createBaselineFrame, f as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, g as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, h as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, i as CONTROL_MEASURE_METADATA, it as unproject, j as DEFAULT_BLOCK_OPTIONS, k as DEFAULT_BREACH_OPTIONS, l as listControlMeasureMetadata, m as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, n as resolveStyleHints, nt as computeInitialWidthPoint, o as getControlMeasureMetadata, ot as getMetersPerPixel, p as DEFAULT_TACTICAL_ARROW_OPTIONS, q as disruptDrawRule, r as CONTROL_MEASURE_IDS, rt as project, s as getControlMeasureMetadataByValue, st as roundToFixed, t as renderControlMeasure, tt as calculateMetrics, u as DEFAULT_TURN_OPTIONS, v as DEFAULT_MAIN_ATTACK_OPTIONS, w as DEFAULT_DISRUPT_OPTIONS, x as DEFAULT_FLOT_OPTIONS, y as DEFAULT_FORTIFIED_AREA_OPTIONS, z as supportByFireDrawRule } from "./renderControlMeasure-C7pY-TcL.mjs";
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-DkXDSrLz.mjs";
2
2
  //#region src/instance.ts
3
3
  function isKind(cm, kind) {
4
4
  return cm.kind === kind;
@@ -99,4 +99,4 @@ function toHexChannel(value) {
99
99
  return Math.max(0, Math.min(255, Math.round(value))).toString(16).padStart(2, "0");
100
100
  }
101
101
  //#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_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_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 };
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 };
@@ -1,4 +1,4 @@
1
- import { q as ControlMeasureId } from "../index-D7uBPw7l.mjs";
1
+ import { q as ControlMeasureId } from "../index-BJYbQlzo.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-C7pY-TcL.mjs";
1
+ import { a as DEFINITIONS, t as renderControlMeasure } from "../renderControlMeasure-DkXDSrLz.mjs";
2
2
  //#region src/preview/index.ts
3
3
  /**
4
4
  * Unitless `previewSample` points (~`[-1, 1]`) are scaled by this degree offset
@@ -875,11 +875,30 @@ const FIRE_ARROW_PARAMS = [
875
875
  step: 1
876
876
  }
877
877
  ];
878
- const FORTIFIED_AREA_PARAMS = [
878
+ const FORTIFIED_AREA_SIZE_PARAMS = [{
879
+ key: "sizePixels",
880
+ label: "Size",
881
+ description: "Size of the fortified-area teeth, in pixels",
882
+ type: "number",
883
+ min: 5,
884
+ max: 50,
885
+ step: 1,
886
+ unit: "px"
887
+ }, {
888
+ key: "size",
889
+ label: "Size",
890
+ description: "Size of the fortified-area teeth, in meters",
891
+ type: "number",
892
+ min: 1,
893
+ max: 500,
894
+ step: 1,
895
+ unit: "m"
896
+ }];
897
+ const ENCIRCLEMENT_PARAMS = [
879
898
  {
880
899
  key: "smooth",
881
900
  label: "Smooth",
882
- description: "Round the area's corners by curving the boundary through the control points.",
901
+ description: "Round the boundary by curving it through the control points.",
883
902
  type: "boolean"
884
903
  },
885
904
  {
@@ -892,25 +911,43 @@ const FORTIFIED_AREA_PARAMS = [
892
911
  step: 1,
893
912
  visibleWhen: (opts) => Boolean(opts.smooth)
894
913
  },
895
- ...[{
896
- key: "sizePixels",
897
- label: "Size",
898
- description: "Size of the fortified-area teeth, in pixels",
914
+ {
915
+ key: "barbSpacingRatio",
916
+ label: "Spacing",
917
+ description: "Spacing between barbs as a ratio of the symbol radius (smaller is denser).",
899
918
  type: "number",
900
- min: 5,
901
- max: 50,
902
- step: 1,
903
- unit: "px"
904
- }, {
905
- key: "size",
906
- label: "Size",
907
- description: "Size of the fortified-area teeth, in meters",
919
+ min: .15,
920
+ max: 1.5,
921
+ step: .05
922
+ },
923
+ {
924
+ key: "barbLengthRatio",
925
+ label: "Barb length",
926
+ description: "Barb height as a ratio of the symbol radius (amplitude).",
908
927
  type: "number",
909
- min: 1,
910
- max: 500,
928
+ min: .02,
929
+ max: .5,
930
+ step: .01
931
+ }
932
+ ];
933
+ const FORTIFIED_AREA_PARAMS = [
934
+ {
935
+ key: "smooth",
936
+ label: "Smooth",
937
+ description: "Round the area's corners by curving the boundary through the control points.",
938
+ type: "boolean"
939
+ },
940
+ {
941
+ key: "smoothResolution",
942
+ label: "Smooth resolution",
943
+ description: "Number of samples per segment when smooth mode is enabled.",
944
+ type: "number",
945
+ min: 2,
946
+ max: 64,
911
947
  step: 1,
912
- unit: "m"
913
- }]
948
+ visibleWhen: (opts) => Boolean(opts.smooth)
949
+ },
950
+ ...FORTIFIED_AREA_SIZE_PARAMS
914
951
  ];
915
952
  //#endregion
916
953
  //#region src/generators/cm15-maneuver-areas/airborneAttack.ts
@@ -2181,8 +2218,121 @@ function closedCatmullRom(points, samples) {
2181
2218
  return out;
2182
2219
  }
2183
2220
  //#endregion
2221
+ //#region src/internal/area-echelon.ts
2222
+ /** Mean of the ring's distinct vertices, used as the outward-normal reference. */
2223
+ function ringCentroid(verts, count) {
2224
+ let cx = 0;
2225
+ let cy = 0;
2226
+ for (let i = 0; i < count; i++) {
2227
+ cx += verts[i][0];
2228
+ cy += verts[i][1];
2229
+ }
2230
+ return [cx / count, cy / count];
2231
+ }
2232
+ /**
2233
+ * Projects `positions` and returns a closed ring (first == last), optionally
2234
+ * smoothed through the control points with a closed centripetal Catmull-Rom
2235
+ * spline. A trailing vertex that duplicates the first (already-closed input)
2236
+ * is dropped before closing/smoothing.
2237
+ */
2238
+ function buildClosedRing(positions, smooth, smoothResolution, defaultSmoothResolution) {
2239
+ const projected = positions.map((p) => project(p[0], p[1]));
2240
+ const ringPts = projected.length > 1 && Math.abs(projected[0][0] - projected[projected.length - 1][0]) < 1e-6 && Math.abs(projected[0][1] - projected[projected.length - 1][1]) < 1e-6 ? projected.slice(0, -1) : projected;
2241
+ return smooth ? closedCatmullRom(ringPts, normalizeSmoothResolution$2(smoothResolution, defaultSmoothResolution)) : [...ringPts, ringPts[0]];
2242
+ }
2243
+ /**
2244
+ * Walks a closed ring into per-segment metadata (unit direction, outward
2245
+ * normal, cumulative arc length) plus the total perimeter and the arc-length
2246
+ * anchor of the bottom edge — the segment whose midpoint sits lowest (min
2247
+ * projected y), the side opposite the front.
2248
+ */
2249
+ function buildAreaPerimeter(verts) {
2250
+ const centroid = ringCentroid(verts, verts.length - 1);
2251
+ const segs = [];
2252
+ let totalLength = 0;
2253
+ let anchorTarget = 0;
2254
+ let lowest = Infinity;
2255
+ for (let i = 0; i < verts.length - 1; i++) {
2256
+ const a = verts[i];
2257
+ const b = verts[i + 1];
2258
+ const edge = vecSub(b, a);
2259
+ const length = vecMag(edge);
2260
+ if (length < 1e-6) continue;
2261
+ const along = vecNorm(edge);
2262
+ const mid = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
2263
+ let perp = [-along[1], along[0]];
2264
+ if (perp[0] * (mid[0] - centroid[0]) + perp[1] * (mid[1] - centroid[1]) < 0) perp = [-perp[0], -perp[1]];
2265
+ segs.push({
2266
+ start: a,
2267
+ along,
2268
+ perp,
2269
+ length,
2270
+ arcStart: totalLength
2271
+ });
2272
+ if (mid[1] < lowest) {
2273
+ lowest = mid[1];
2274
+ anchorTarget = totalLength + length / 2;
2275
+ }
2276
+ totalLength += length;
2277
+ }
2278
+ return {
2279
+ verts,
2280
+ segs,
2281
+ totalLength,
2282
+ anchorTarget
2283
+ };
2284
+ }
2285
+ /** Resolves the arc-length `target` to a point and the segment frame it lands on. */
2286
+ function pointOnRing(segs, target) {
2287
+ let seg = segs[segs.length - 1];
2288
+ for (const candidate of segs) if (target < candidate.arcStart + candidate.length) {
2289
+ seg = candidate;
2290
+ break;
2291
+ }
2292
+ return {
2293
+ point: vecAdd(seg.start, vecScale(seg.along, target - seg.arcStart)),
2294
+ seg
2295
+ };
2296
+ }
2297
+ /**
2298
+ * Effective echelon glyph height in meters. Screen-anchored when a pixel size
2299
+ * is supplied with a live resolution; ground-anchored (meters) otherwise.
2300
+ * Clamped strictly positive.
2301
+ */
2302
+ function resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel) {
2303
+ let h = echelonSize;
2304
+ if (echelonSizePixels !== void 0 && metersPerPixel !== void 0 && metersPerPixel > 0) h = echelonSizePixels * metersPerPixel;
2305
+ return Math.max(EPSILON, h);
2306
+ }
2307
+ /**
2308
+ * Places the Field B echelon glyph on the perimeter and computes its masking
2309
+ * gap, anchored to the bottom edge and slid by `echelonPosition` (a signed
2310
+ * fraction of the perimeter, wrapping at the seam). Returns empty geometry
2311
+ * when the echelon resolves to none or the ring is degenerate.
2312
+ */
2313
+ function placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition) {
2314
+ const { segs, totalLength, anchorTarget } = perimeter;
2315
+ const spec = resolveEchelon(echelon);
2316
+ if (!spec || segs.length === 0 || totalLength < 1e-6) return {
2317
+ strokes: [],
2318
+ fills: [],
2319
+ gaps: []
2320
+ };
2321
+ const target = ((anchorTarget + echelonPosition * totalLength) % totalLength + totalLength) % totalLength;
2322
+ const { point, seg } = pointOnRing(segs, target);
2323
+ const { strokes, fills, width } = buildEchelonGlyph(point, seg.along, seg.perp, h, spec);
2324
+ const gaps = [];
2325
+ const halfGap = width / 2 + Math.max(0, echelonPadding) * h;
2326
+ pushWrappedGap(gaps, target - halfGap, target + halfGap, totalLength);
2327
+ return {
2328
+ strokes,
2329
+ fills,
2330
+ gaps
2331
+ };
2332
+ }
2333
+ //#endregion
2184
2334
  //#region src/generators/cm15-maneuver-areas/battlePosition.ts
2185
- const DEFAULT_SMOOTH_RESOLUTION$4 = 16;
2335
+ const DEFAULT_SMOOTH_RESOLUTION$6 = 16;
2186
2336
  /** Default options for the Battle Position control measure. */
2187
2337
  const DEFAULT_BATTLE_POSITION_OPTIONS = {
2188
2338
  echelon: "battalion",
@@ -2191,7 +2341,7 @@ const DEFAULT_BATTLE_POSITION_OPTIONS = {
2191
2341
  echelonPadding: .3,
2192
2342
  echelonPosition: 0,
2193
2343
  smooth: false,
2194
- smoothResolution: DEFAULT_SMOOTH_RESOLUTION$4
2344
+ smoothResolution: DEFAULT_SMOOTH_RESOLUTION$6
2195
2345
  };
2196
2346
  const BATTLE_POSITION_METADATA = {
2197
2347
  id: "battle-position",
@@ -2268,16 +2418,6 @@ const BATTLE_POSITION_METADATA = {
2268
2418
  }
2269
2419
  ]
2270
2420
  };
2271
- /** Mean of the ring's distinct vertices, used as the outward-normal reference. */
2272
- function ringCentroid(verts, count) {
2273
- let cx = 0;
2274
- let cy = 0;
2275
- for (let i = 0; i < count; i++) {
2276
- cx += verts[i][0];
2277
- cy += verts[i][1];
2278
- }
2279
- return [cx / count, cy / count];
2280
- }
2281
2421
  /**
2282
2422
  * Creates a Battle Position control measure: a closed area through `positions`
2283
2423
  * carrying a single echelon (Field B) glyph straddling its boundary — anchored
@@ -2289,59 +2429,9 @@ function ringCentroid(verts, count) {
2289
2429
  */
2290
2430
  function createBattlePosition(positions, options = {}) {
2291
2431
  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;
2292
- let h = echelonSize;
2293
- if (echelonSizePixels !== void 0 && metersPerPixel !== void 0 && metersPerPixel > 0) h = echelonSizePixels * metersPerPixel;
2294
- h = Math.max(EPSILON, h);
2295
- const spec = resolveEchelon(echelon);
2296
- const projected = positions.map((p) => project(p[0], p[1]));
2297
- const ringPts = projected.length > 1 && Math.abs(projected[0][0] - projected[projected.length - 1][0]) < 1e-6 && Math.abs(projected[0][1] - projected[projected.length - 1][1]) < 1e-6 ? projected.slice(0, -1) : projected;
2298
- const verts = smooth ? closedCatmullRom(ringPts, normalizeSmoothResolution$2(smoothResolution, DEFAULT_SMOOTH_RESOLUTION$4)) : [...ringPts, ringPts[0]];
2299
- const glyphStrokes = [];
2300
- const glyphFills = [];
2301
- const gaps = [];
2302
- if (spec) {
2303
- const centroid = ringCentroid(verts, verts.length - 1);
2304
- const segs = [];
2305
- let totalLength = 0;
2306
- let anchorTarget = 0;
2307
- let lowest = Infinity;
2308
- for (let i = 0; i < verts.length - 1; i++) {
2309
- const a = verts[i];
2310
- const b = verts[i + 1];
2311
- const seg = vecSub(b, a);
2312
- const L = vecMag(seg);
2313
- if (L < 1e-6) continue;
2314
- const along = vecNorm(seg);
2315
- const mid = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
2316
- let perp = [-along[1], along[0]];
2317
- if (perp[0] * (mid[0] - centroid[0]) + perp[1] * (mid[1] - centroid[1]) < 0) perp = [-perp[0], -perp[1]];
2318
- segs.push({
2319
- start: a,
2320
- along,
2321
- perp,
2322
- length: L,
2323
- acc: totalLength
2324
- });
2325
- if (mid[1] < lowest) {
2326
- lowest = mid[1];
2327
- anchorTarget = totalLength + L / 2;
2328
- }
2329
- totalLength += L;
2330
- }
2331
- if (segs.length > 0 && totalLength >= 1e-6) {
2332
- const target = ((anchorTarget + echelonPosition * totalLength) % totalLength + totalLength) % totalLength;
2333
- let seg = segs[segs.length - 1];
2334
- for (const candidate of segs) if (target < candidate.acc + candidate.length) {
2335
- seg = candidate;
2336
- break;
2337
- }
2338
- const { strokes, fills, width } = buildEchelonGlyph(vecAdd(seg.start, vecScale(seg.along, target - seg.acc)), seg.along, seg.perp, h, spec);
2339
- glyphStrokes.push(...strokes);
2340
- glyphFills.push(...fills);
2341
- const halfGap = width / 2 + Math.max(0, echelonPadding) * h;
2342
- pushWrappedGap(gaps, target - halfGap, target + halfGap, totalLength);
2343
- }
2344
- }
2432
+ const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
2433
+ const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$6);
2434
+ const { strokes, fills, gaps } = placeEchelon(buildAreaPerimeter(verts), h, echelon, echelonPadding, echelonPosition);
2345
2435
  const boundaryCoords = buildGappedLine(verts, gaps);
2346
2436
  const features = [];
2347
2437
  if (boundaryCoords.length > 0) features.push({
@@ -2352,15 +2442,15 @@ function createBattlePosition(positions, options = {}) {
2352
2442
  coordinates: boundaryCoords
2353
2443
  }
2354
2444
  });
2355
- if (glyphStrokes.length > 0) features.push({
2445
+ if (strokes.length > 0) features.push({
2356
2446
  type: "Feature",
2357
2447
  properties: { part: "echelon" },
2358
2448
  geometry: {
2359
2449
  type: "MultiLineString",
2360
- coordinates: glyphStrokes
2450
+ coordinates: strokes
2361
2451
  }
2362
2452
  });
2363
- if (glyphFills.length > 0) features.push({
2453
+ if (fills.length > 0) features.push({
2364
2454
  type: "Feature",
2365
2455
  properties: {
2366
2456
  part: "echelon",
@@ -2368,7 +2458,7 @@ function createBattlePosition(positions, options = {}) {
2368
2458
  },
2369
2459
  geometry: {
2370
2460
  type: "MultiPolygon",
2371
- coordinates: glyphFills.map((ring) => [ring])
2461
+ coordinates: fills.map((ring) => [ring])
2372
2462
  }
2373
2463
  });
2374
2464
  return {
@@ -2387,7 +2477,10 @@ const BATTLE_POSITION = defineControlMeasure({
2387
2477
  [1, .5],
2388
2478
  [-1, .5]
2389
2479
  ],
2390
- options: { echelonSize: 375 }
2480
+ options: {
2481
+ echelonSize: 200,
2482
+ smooth: true
2483
+ }
2391
2484
  }
2392
2485
  });
2393
2486
  //#endregion
@@ -2458,7 +2551,7 @@ const BLOCK = defineControlMeasure({
2458
2551
  });
2459
2552
  //#endregion
2460
2553
  //#region src/generators/cm99-generic-arrows/blockArrow.ts
2461
- const DEFAULT_SMOOTH_RESOLUTION$3 = 12;
2554
+ const DEFAULT_SMOOTH_RESOLUTION$5 = 12;
2462
2555
  const MIN_SMOOTH_RESOLUTION$1 = 2;
2463
2556
  const MAX_SMOOTH_RESOLUTION$1 = 64;
2464
2557
  const DEFAULT_BLOCK_ARROW_OPTIONS = {
@@ -2467,7 +2560,7 @@ const DEFAULT_BLOCK_ARROW_OPTIONS = {
2467
2560
  arrowheadWidthRatio: .18,
2468
2561
  arrowheadLengthRatio: .22,
2469
2562
  smooth: false,
2470
- smoothResolution: DEFAULT_SMOOTH_RESOLUTION$3,
2563
+ smoothResolution: DEFAULT_SMOOTH_RESOLUTION$5,
2471
2564
  filled: true
2472
2565
  };
2473
2566
  const BLOCK_ARROW_METADATA = {
@@ -2704,7 +2797,7 @@ function axis1Geometry(coordinates) {
2704
2797
  };
2705
2798
  }
2706
2799
  function normalizeSmoothResolution$1(value) {
2707
- if (!Number.isFinite(value)) return DEFAULT_SMOOTH_RESOLUTION$3;
2800
+ if (!Number.isFinite(value)) return DEFAULT_SMOOTH_RESOLUTION$5;
2708
2801
  return Math.min(MAX_SMOOTH_RESOLUTION$1, Math.max(MIN_SMOOTH_RESOLUTION$1, Math.round(value)));
2709
2802
  }
2710
2803
  //#endregion
@@ -2730,7 +2823,7 @@ function keepTextLeftToRight(rotation) {
2730
2823
  }
2731
2824
  //#endregion
2732
2825
  //#region src/generators/cm11-command-control-lines/boundary.ts
2733
- const DEFAULT_SMOOTH_RESOLUTION$2 = 12;
2826
+ const DEFAULT_SMOOTH_RESOLUTION$4 = 12;
2734
2827
  /** Default options for the Boundary control measure. */
2735
2828
  const DEFAULT_BOUNDARY_OPTIONS = {
2736
2829
  echelon: "battalion",
@@ -2744,7 +2837,7 @@ const DEFAULT_BOUNDARY_OPTIONS = {
2744
2837
  unit2Designator: "",
2745
2838
  unit2Country: "",
2746
2839
  smooth: false,
2747
- smoothResolution: DEFAULT_SMOOTH_RESOLUTION$2
2840
+ smoothResolution: DEFAULT_SMOOTH_RESOLUTION$4
2748
2841
  };
2749
2842
  const BOUNDARY_METADATA = {
2750
2843
  id: "boundary",
@@ -2897,7 +2990,7 @@ function createBoundary(positions, options = {}) {
2897
2990
  if (labelSizeZoom !== void 0) labelSize.textSizeZoom = labelSizeZoom;
2898
2991
  }
2899
2992
  const rawVerts = positions.map((p) => project(p[0], p[1]));
2900
- const verts = smooth ? catmullRom(rawVerts, normalizeSmoothResolution$2(smoothResolution, DEFAULT_SMOOTH_RESOLUTION$2)) : rawVerts;
2993
+ const verts = smooth ? catmullRom(rawVerts, normalizeSmoothResolution$2(smoothResolution, DEFAULT_SMOOTH_RESOLUTION$4)) : rawVerts;
2901
2994
  const glyphStrokes = [];
2902
2995
  const glyphFills = [];
2903
2996
  const labelFeatures = [];
@@ -3488,13 +3581,13 @@ const CANALIZE = defineControlMeasure({
3488
3581
  });
3489
3582
  //#endregion
3490
3583
  //#region src/generators/cm99-generic-arrows/classicArrow.ts
3491
- const DEFAULT_SMOOTH_RESOLUTION$1 = 12;
3584
+ const DEFAULT_SMOOTH_RESOLUTION$3 = 12;
3492
3585
  const MIN_SMOOTH_RESOLUTION = 2;
3493
3586
  const MAX_SMOOTH_RESOLUTION = 64;
3494
3587
  const DEFAULT_CLASSIC_ARROW_OPTIONS = {
3495
3588
  arrowheadStyle: "triangle",
3496
3589
  smooth: false,
3497
- smoothResolution: DEFAULT_SMOOTH_RESOLUTION$1,
3590
+ smoothResolution: DEFAULT_SMOOTH_RESOLUTION$3,
3498
3591
  arrowheadLengthRatio: .2,
3499
3592
  arrowheadWidthRatio: .14
3500
3593
  };
@@ -3781,7 +3874,7 @@ const CLASSIC_ARROW = defineControlMeasure({
3781
3874
  rule: line1DrawRule
3782
3875
  });
3783
3876
  function normalizeSmoothResolution(value) {
3784
- if (!Number.isFinite(value)) return DEFAULT_SMOOTH_RESOLUTION$1;
3877
+ if (!Number.isFinite(value)) return DEFAULT_SMOOTH_RESOLUTION$3;
3785
3878
  return Math.min(MAX_SMOOTH_RESOLUTION, Math.max(MIN_SMOOTH_RESOLUTION, Math.round(value)));
3786
3879
  }
3787
3880
  //#endregion
@@ -4271,6 +4364,138 @@ const DISRUPT = defineControlMeasure({
4271
4364
  previewSample: { controlPoints: [[0, .4], [0, -.4]] }
4272
4365
  });
4273
4366
  //#endregion
4367
+ //#region src/generators/cm15-maneuver-areas/encirclement.ts
4368
+ const DEFAULT_BARB_SPACING_RATIO = .4;
4369
+ const DEFAULT_BARB_LENGTH_RATIO = .16;
4370
+ const DEFAULT_SMOOTH_RESOLUTION$2 = 16;
4371
+ /** Default options for the Encirclement control measure. */
4372
+ const DEFAULT_ENCIRCLEMENT_OPTIONS = {
4373
+ barbSpacingRatio: DEFAULT_BARB_SPACING_RATIO,
4374
+ barbLengthRatio: DEFAULT_BARB_LENGTH_RATIO,
4375
+ smooth: true,
4376
+ smoothResolution: DEFAULT_SMOOTH_RESOLUTION$2
4377
+ };
4378
+ const ENCIRCLEMENT_METADATA = {
4379
+ id: "encirclement",
4380
+ name: "Encirclement",
4381
+ description: "The loss of freedom of maneuver resulting from enemy control of all ground routes of evacuation and reinforcement.",
4382
+ entity: "Maneuver Areas",
4383
+ entityType: "Encirclement",
4384
+ value: "151800",
4385
+ minCoordinates: 3,
4386
+ geometry: "area",
4387
+ geometryTypes: ["Polygon", "MultiLineString"],
4388
+ drawRule: "Area1",
4389
+ params: ENCIRCLEMENT_PARAMS
4390
+ };
4391
+ /** Mean distance from the ring centroid to its distinct vertices. */
4392
+ function meanRadius(verts) {
4393
+ const n = verts.length - 1;
4394
+ if (n <= 0) return 0;
4395
+ let cx = 0;
4396
+ let cy = 0;
4397
+ for (let i = 0; i < n; i++) {
4398
+ cx += verts[i][0];
4399
+ cy += verts[i][1];
4400
+ }
4401
+ cx /= n;
4402
+ cy /= n;
4403
+ let sum = 0;
4404
+ for (let i = 0; i < n; i++) sum += Math.hypot(verts[i][0] - cx, verts[i][1] - cy);
4405
+ return sum / n;
4406
+ }
4407
+ /**
4408
+ * Builds the outward barbs as open "V" strokes sitting on the boundary, evenly
4409
+ * spaced around the perimeter. The requested `spacing` (arc length) is rounded
4410
+ * to a whole number of barbs so they close the ring exactly. Each barb runs
4411
+ * base-start → apex → base-end with the apex pushed out along the local outward
4412
+ * normal; the base ends are sampled on the ring at ±`barbLength/2` of arc so the
4413
+ * barb's base width equals its length (a clean arrowhead, as in Isolate) and
4414
+ * follows the boundary curvature. The base half-width is capped so dense barbs
4415
+ * never overlap.
4416
+ */
4417
+ function buildBarbs(perimeter, spacing, barbLength) {
4418
+ const { segs, totalLength } = perimeter;
4419
+ if (segs.length === 0 || totalLength < 1e-6 || barbLength < 1e-6 || spacing < 1e-6) return [];
4420
+ const count = Math.max(3, Math.round(totalLength / spacing));
4421
+ const half = Math.min(barbLength / 2, totalLength / (2 * count));
4422
+ const at = (arc) => pointOnRing(segs, (arc % totalLength + totalLength) % totalLength);
4423
+ const barbs = [];
4424
+ for (let k = 0; k < count; k++) {
4425
+ const center = k * totalLength / count;
4426
+ const baseStart = at(center - half).point;
4427
+ const { point, seg } = at(center);
4428
+ const apex = vecAdd(point, vecScale(seg.perp, barbLength));
4429
+ const baseEnd = at(center + half).point;
4430
+ barbs.push([
4431
+ baseStart,
4432
+ apex,
4433
+ baseEnd
4434
+ ].map((p) => unproject(p[0], p[1])));
4435
+ }
4436
+ return barbs;
4437
+ }
4438
+ /**
4439
+ * Creates an Encirclement tactical graphic: a complete smooth boundary ring
4440
+ * with outward arrowhead barbs resting on it. The boundary is an unfilled
4441
+ * Polygon; the barbs are open "V" strokes (a MultiLineString) whose base is the
4442
+ * boundary arc itself.
4443
+ *
4444
+ * @param positions - Area anchor points (>=3); the input contract is enforced
4445
+ * at the render seam (ADR-0014).
4446
+ * @param options - {@link EncirclementOptions}.
4447
+ */
4448
+ function createEncirclement(positions, options = {}) {
4449
+ const { barbSpacingRatio = DEFAULT_BARB_SPACING_RATIO, barbLengthRatio = DEFAULT_BARB_LENGTH_RATIO, smooth = DEFAULT_ENCIRCLEMENT_OPTIONS.smooth, smoothResolution = DEFAULT_ENCIRCLEMENT_OPTIONS.smoothResolution } = options;
4450
+ const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$2);
4451
+ const perimeter = buildAreaPerimeter(verts);
4452
+ const radius = meanRadius(verts);
4453
+ const barbLength = radius * Math.max(0, barbLengthRatio);
4454
+ const barbs = buildBarbs(perimeter, radius * Math.max(0, barbSpacingRatio), barbLength);
4455
+ const features = [{
4456
+ type: "Feature",
4457
+ properties: { part: "boundary" },
4458
+ geometry: {
4459
+ type: "Polygon",
4460
+ coordinates: [verts.map((p) => unproject(p[0], p[1]))]
4461
+ }
4462
+ }];
4463
+ if (barbs.length > 0) features.push({
4464
+ type: "Feature",
4465
+ properties: { part: "barbs" },
4466
+ geometry: {
4467
+ type: "MultiLineString",
4468
+ coordinates: barbs
4469
+ }
4470
+ });
4471
+ return {
4472
+ type: "FeatureCollection",
4473
+ features
4474
+ };
4475
+ }
4476
+ const ENCIRCLEMENT = defineControlMeasure({
4477
+ metadata: ENCIRCLEMENT_METADATA,
4478
+ generator: createEncirclement,
4479
+ defaultOptions: DEFAULT_ENCIRCLEMENT_OPTIONS,
4480
+ previewSample: {
4481
+ controlPoints: [
4482
+ [-1, -.5],
4483
+ [-.4, -.85],
4484
+ [.4, -.85],
4485
+ [1, -.5],
4486
+ [1, .5],
4487
+ [.4, .85],
4488
+ [-.4, .85],
4489
+ [-1, .5]
4490
+ ],
4491
+ options: {
4492
+ barbSpacingRatio: .4,
4493
+ barbLengthRatio: .18,
4494
+ smooth: true
4495
+ }
4496
+ }
4497
+ });
4498
+ //#endregion
4274
4499
  //#region src/internal/field-of-fire.ts
4275
4500
  /**
4276
4501
  * Builds the open (stroked) arrowhead at the outward tip of one arm. The two
@@ -4810,7 +5035,7 @@ const FLOT = defineControlMeasure({
4810
5035
  //#region src/generators/cm29-protection-lines/fortifiedLine.ts
4811
5036
  const DEFAULT_SIZE = 50;
4812
5037
  const DEFAULT_SIZE_PIXELS = 10;
4813
- const DEFAULT_SMOOTH_RESOLUTION = 16;
5038
+ const DEFAULT_SMOOTH_RESOLUTION$1 = 16;
4814
5039
  /**
4815
5040
  * Default options for the Fortified Line graphic.
4816
5041
  */
@@ -4818,7 +5043,7 @@ const DEFAULT_FORTIFIED_LINE_OPTIONS = {
4818
5043
  size: DEFAULT_SIZE,
4819
5044
  sizePixels: DEFAULT_SIZE_PIXELS,
4820
5045
  smooth: false,
4821
- smoothResolution: DEFAULT_SMOOTH_RESOLUTION
5046
+ smoothResolution: DEFAULT_SMOOTH_RESOLUTION$1
4822
5047
  };
4823
5048
  const FORTIFIED_LINE_METADATA = {
4824
5049
  id: "fortified-line",
@@ -4950,7 +5175,7 @@ function createFortifiedLine(positions, options = {}) {
4950
5175
  properties: {},
4951
5176
  geometry: {
4952
5177
  type: "LineString",
4953
- coordinates: generateFortifiedPoints(smooth ? catmullRom(projectedPoints, normalizeSmoothResolution$2(smoothResolution, DEFAULT_SMOOTH_RESOLUTION)) : projectedPoints, safeSize).map((p) => unproject(p[0], p[1]))
5178
+ coordinates: generateFortifiedPoints(smooth ? catmullRom(projectedPoints, normalizeSmoothResolution$2(smoothResolution, DEFAULT_SMOOTH_RESOLUTION$1)) : projectedPoints, safeSize).map((p) => unproject(p[0], p[1]))
4954
5179
  }
4955
5180
  }]
4956
5181
  };
@@ -5875,6 +6100,191 @@ const SEARCH_AREA = defineControlMeasure({
5875
6100
  rule: searchAreaDrawRule
5876
6101
  });
5877
6102
  //#endregion
6103
+ //#region src/generators/cm15-maneuver-areas/strongPoint.ts
6104
+ const DEFAULT_SMOOTH_RESOLUTION = 16;
6105
+ /** Default options for the Strong Point control measure. */
6106
+ const DEFAULT_STRONG_POINT_OPTIONS = { ...DEFAULT_BATTLE_POSITION_OPTIONS };
6107
+ const STRONG_POINT_METADATA = {
6108
+ id: "strong-point",
6109
+ name: "Strong Point",
6110
+ description: "A key point in a defensive position, usually strongly fortified and heavily armed, around which other positions are grouped for its protection.",
6111
+ entity: "Maneuver Areas",
6112
+ entityType: "Battle Position",
6113
+ entitySubtype: "Strong Point",
6114
+ value: "151203",
6115
+ minCoordinates: 3,
6116
+ geometry: "area",
6117
+ geometryTypes: ["MultiLineString", "MultiPolygon"],
6118
+ drawRule: "Area1",
6119
+ params: [
6120
+ {
6121
+ key: "smooth",
6122
+ label: "Smooth",
6123
+ description: "Round the area's corners by curving the boundary through the control points.",
6124
+ type: "boolean"
6125
+ },
6126
+ {
6127
+ key: "smoothResolution",
6128
+ label: "Smooth resolution",
6129
+ description: "Number of samples per segment when smooth mode is enabled.",
6130
+ type: "number",
6131
+ min: 2,
6132
+ max: 64,
6133
+ step: 1,
6134
+ visibleWhen: (opts) => Boolean(opts.smooth)
6135
+ },
6136
+ {
6137
+ key: "echelon",
6138
+ label: "Echelon",
6139
+ description: "Field B unit-size amplifier, drawn on the boundary.",
6140
+ type: "enum",
6141
+ options: ECHELON_PARAM_OPTIONS
6142
+ },
6143
+ {
6144
+ key: "echelonSizePixels",
6145
+ label: "Echelon size",
6146
+ description: "Echelon glyph height in screen pixels.",
6147
+ type: "number",
6148
+ min: 8,
6149
+ max: 48,
6150
+ step: 1,
6151
+ unit: "px"
6152
+ },
6153
+ {
6154
+ key: "echelonSize",
6155
+ label: "Echelon size",
6156
+ description: "Echelon glyph height in meters.",
6157
+ type: "number",
6158
+ min: 50,
6159
+ max: 2e4,
6160
+ step: 50,
6161
+ unit: "m"
6162
+ },
6163
+ {
6164
+ key: "echelonPadding",
6165
+ label: "Echelon padding",
6166
+ description: "Gap on each side of the echelon, as a ratio of its height.",
6167
+ type: "number",
6168
+ min: 0,
6169
+ max: 2,
6170
+ step: .05
6171
+ },
6172
+ {
6173
+ key: "echelonPosition",
6174
+ label: "Echelon position",
6175
+ description: "Slide the echelon along the boundary, as a fraction of the perimeter from the bottom edge.",
6176
+ type: "number",
6177
+ min: -.5,
6178
+ max: .5,
6179
+ step: .01
6180
+ }
6181
+ ]
6182
+ };
6183
+ /** True when the arc-length `target` falls inside any echelon masking gap. */
6184
+ function inGap(target, gaps) {
6185
+ return gaps.some((gap) => target >= gap.g0 && target <= gap.g1);
6186
+ }
6187
+ /**
6188
+ * Outward perimeter tics: a stroke every `h` meters along the ring, each `h`
6189
+ * meters long along the segment's outward normal. Tic length, spacing, and the
6190
+ * first-tic phase are all the Field B echelon height `h`. Targets inside an
6191
+ * echelon gap are suppressed so the tics don't collide with the glyph.
6192
+ *
6193
+ * The `target` grows monotonically, so a single forward segment cursor walks
6194
+ * the perimeter in O(segments + tics) instead of rescanning per tic.
6195
+ */
6196
+ function buildStrongPointTics(perimeter, gaps, h) {
6197
+ const { segs, totalLength } = perimeter;
6198
+ if (segs.length === 0 || totalLength < 1e-6) return [];
6199
+ const tics = [];
6200
+ let cursor = 0;
6201
+ for (let target = h / 2; target < totalLength; target += h) {
6202
+ if (inGap(target, gaps)) continue;
6203
+ while (cursor < segs.length - 1 && target >= segs[cursor].arcStart + segs[cursor].length) cursor++;
6204
+ const seg = segs[cursor];
6205
+ const point = vecAdd(seg.start, vecScale(seg.along, target - seg.arcStart));
6206
+ const end = vecAdd(point, vecScale(seg.perp, h));
6207
+ tics.push([unproject(point[0], point[1]), unproject(end[0], end[1])]);
6208
+ }
6209
+ return tics;
6210
+ }
6211
+ /**
6212
+ * Creates a Strong Point control measure: a Battle Position boundary/echelon
6213
+ * with outward tics whose length and spacing are the Field B echelon height.
6214
+ * Shares the perimeter and echelon placement with Battle Position via the
6215
+ * `area-echelon` primitive (single source of truth for ring and gap).
6216
+ *
6217
+ * @param positions - Area anchor points (>=3); the input contract is enforced
6218
+ * at the render seam (ADR-0014).
6219
+ * @param options - {@link StrongPointOptions}.
6220
+ */
6221
+ function createStrongPoint(positions, options = {}) {
6222
+ 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;
6223
+ const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
6224
+ const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION);
6225
+ const perimeter = buildAreaPerimeter(verts);
6226
+ const { strokes, fills, gaps } = placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition);
6227
+ const boundaryCoords = buildGappedLine(verts, gaps);
6228
+ const tics = buildStrongPointTics(perimeter, gaps, h);
6229
+ const features = [];
6230
+ if (boundaryCoords.length > 0) features.push({
6231
+ type: "Feature",
6232
+ properties: { part: "boundary" },
6233
+ geometry: {
6234
+ type: "MultiLineString",
6235
+ coordinates: boundaryCoords
6236
+ }
6237
+ });
6238
+ if (tics.length > 0) features.push({
6239
+ type: "Feature",
6240
+ properties: { part: "tics" },
6241
+ geometry: {
6242
+ type: "MultiLineString",
6243
+ coordinates: tics
6244
+ }
6245
+ });
6246
+ if (strokes.length > 0) features.push({
6247
+ type: "Feature",
6248
+ properties: { part: "echelon" },
6249
+ geometry: {
6250
+ type: "MultiLineString",
6251
+ coordinates: strokes
6252
+ }
6253
+ });
6254
+ if (fills.length > 0) features.push({
6255
+ type: "Feature",
6256
+ properties: {
6257
+ part: "echelon",
6258
+ fill: true
6259
+ },
6260
+ geometry: {
6261
+ type: "MultiPolygon",
6262
+ coordinates: fills.map((ring) => [ring])
6263
+ }
6264
+ });
6265
+ return {
6266
+ type: "FeatureCollection",
6267
+ features
6268
+ };
6269
+ }
6270
+ const STRONG_POINT = defineControlMeasure({
6271
+ metadata: STRONG_POINT_METADATA,
6272
+ generator: createStrongPoint,
6273
+ defaultOptions: DEFAULT_STRONG_POINT_OPTIONS,
6274
+ previewSample: {
6275
+ controlPoints: [
6276
+ [-1, -.5],
6277
+ [1, -.5],
6278
+ [1, .5],
6279
+ [-1, .5]
6280
+ ],
6281
+ options: {
6282
+ echelonSize: 200,
6283
+ smooth: true
6284
+ }
6285
+ }
6286
+ });
6287
+ //#endregion
5878
6288
  //#region src/generators/cm15-maneuver-areas/supportByFire.ts
5879
6289
  const DEFAULT_SUPPORT_BY_FIRE_OPTIONS = {
5880
6290
  arrowheadWidthRatio: .3,
@@ -6203,11 +6613,13 @@ function rotate(vector, angle) {
6203
6613
  const DEFINITIONS = {
6204
6614
  boundary: BOUNDARY,
6205
6615
  "battle-position": BATTLE_POSITION,
6616
+ "strong-point": STRONG_POINT,
6206
6617
  ambush: AMBUSH,
6207
6618
  "principal-direction-of-fire": PRINCIPAL_DIRECTION_OF_FIRE,
6208
6619
  "final-protective-fire-left": FINAL_PROTECTIVE_FIRE_LEFT,
6209
6620
  "final-protective-fire-right": FINAL_PROTECTIVE_FIRE_RIGHT,
6210
6621
  "search-area": SEARCH_AREA,
6622
+ encirclement: ENCIRCLEMENT,
6211
6623
  "main-attack": MAIN_ATTACK,
6212
6624
  "supporting-attack": SUPPORTING_ATTACK,
6213
6625
  "classic-arrow": CLASSIC_ARROW,
@@ -6462,4 +6874,4 @@ function assertNever(value) {
6462
6874
  throw new Error(`Unhandled control measure kind: ${String(value)}`);
6463
6875
  }
6464
6876
  //#endregion
6465
- export { snapToMidpointPerpendicular as $, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as A, line24DrawRule as B, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as C, DEFAULT_CANALIZE_OPTIONS as D, DEFAULT_CLEAR_OPTIONS as E, DEFAULT_ANTITANK_DITCH_OPTIONS as F, attackByFireDrawRule as G, turnDrawRule as H, DEFAULT_AMBUSH_OPTIONS as I, blockDrawRule as J, point12DrawRule as K, DEFAULT_AIRBORNE_ATTACK_OPTIONS as L, DEFAULT_ATTACK_HELICOPTER_OPTIONS as M, DEFAULT_ATTACK_BY_FIRE_OPTIONS as N, DEFAULT_BYPASS_OPTIONS as O, DEFAULT_ANTITANK_WALL_OPTIONS as P, pointOnMidpointPerpendicularAxis as Q, axis1DrawRule as R, DEFAULT_FIX_OPTIONS as S, DEFAULT_DELAY_OPTIONS as T, line1DrawRule as U, line23DrawRule as V, ambushDrawRule as W, createMidpointPerpendicularDrawRule as X, computeDefaultMidpointPerpendicularPoint as Y, getMidpointPerpendicularSignedDistance as Z, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as _, DEFINITIONS as a, EPSILON as at, DEFAULT_FORTIFIED_LINE_OPTIONS as b, getDefaultOptions as c, DEFAULT_SUPPORTING_ATTACK_OPTIONS as d, createBaselineFrame as et, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as f, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as g, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as h, CONTROL_MEASURE_METADATA as i, unproject as it, DEFAULT_BLOCK_OPTIONS as j, DEFAULT_BREACH_OPTIONS as k, listControlMeasureMetadata as l, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as m, resolveStyleHints as n, computeInitialWidthPoint as nt, getControlMeasureMetadata as o, getMetersPerPixel as ot, DEFAULT_TACTICAL_ARROW_OPTIONS as p, disruptDrawRule as q, CONTROL_MEASURE_IDS as r, project as rt, getControlMeasureMetadataByValue as s, roundToFixed as st, renderControlMeasure as t, calculateMetrics as tt, DEFAULT_TURN_OPTIONS as u, DEFAULT_MAIN_ATTACK_OPTIONS as v, DEFAULT_DISRUPT_OPTIONS as w, DEFAULT_FLOT_OPTIONS as x, DEFAULT_FORTIFIED_AREA_OPTIONS as y, supportByFireDrawRule as z };
6877
+ 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbat-mapper/control-measures",
3
- "version": "0.2.0-alpha.2",
3
+ "version": "0.2.0-alpha.3",
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",