@orbat-mapper/control-measures 0.2.0-alpha.2 → 0.2.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-D7uBPw7l.d.mts → index-BJYbQlzo.d.mts} +61 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/preview/index.d.mts +1 -1
- package/dist/preview/index.mjs +1 -1
- package/dist/{renderControlMeasure-C7pY-TcL.mjs → renderControlMeasure-CONa8M6M.mjs} +543 -101
- package/package.json +1 -1
|
@@ -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 $,
|
|
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
|
|
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
|
|
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";
|
|
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 };
|
package/dist/preview/index.d.mts
CHANGED
package/dist/preview/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as DEFINITIONS, t as renderControlMeasure } from "../renderControlMeasure-
|
|
1
|
+
import { a as DEFINITIONS, t as renderControlMeasure } from "../renderControlMeasure-CONa8M6M.mjs";
|
|
2
2
|
//#region src/preview/index.ts
|
|
3
3
|
/**
|
|
4
4
|
* Unitless `previewSample` points (~`[-1, 1]`) are scaled by this degree offset
|
|
@@ -483,6 +483,32 @@ function samePosition(a, b) {
|
|
|
483
483
|
return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
|
|
484
484
|
}
|
|
485
485
|
//#endregion
|
|
486
|
+
//#region src/draw-rules/area1.ts
|
|
487
|
+
function derive$7(points) {
|
|
488
|
+
return clonePositions(points);
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Area1 anchor draw rule — a free closed area (Battle Position, Strong Point,
|
|
492
|
+
* Encirclement, Fortified Area).
|
|
493
|
+
*
|
|
494
|
+
* Every anchor is a fully free, user-clicked ring vertex; the rule imposes no
|
|
495
|
+
* constraint and derives no extra slots, so `derive`/`transform` pass the
|
|
496
|
+
* points through unchanged. It is the area analog of the free-line {@link
|
|
497
|
+
* line1DrawRule}: variable-length (no `canonicalPointCount`) with a three-point
|
|
498
|
+
* floor to close a polygon. Behaviorally a no-op over the bare variable-length
|
|
499
|
+
* path — it exists so these drawable areas expose a named rule on
|
|
500
|
+
* `metadata.rule` rather than leaving it `undefined`, matching every other
|
|
501
|
+
* doctrinal draw rule. Ring closure is a generator concern, not a draw-rule one.
|
|
502
|
+
*/
|
|
503
|
+
const area1DrawRule = {
|
|
504
|
+
id: "area1",
|
|
505
|
+
minimumUserPoints: 3,
|
|
506
|
+
derive: derive$7,
|
|
507
|
+
transform(event) {
|
|
508
|
+
return derive$7(event.next);
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
//#endregion
|
|
486
512
|
//#region src/draw-rules/area11.ts
|
|
487
513
|
const blockDrawRule = createMidpointPerpendicularDrawRule({ id: "area11:block" });
|
|
488
514
|
//#endregion
|
|
@@ -875,11 +901,30 @@ const FIRE_ARROW_PARAMS = [
|
|
|
875
901
|
step: 1
|
|
876
902
|
}
|
|
877
903
|
];
|
|
878
|
-
const
|
|
904
|
+
const FORTIFIED_AREA_SIZE_PARAMS = [{
|
|
905
|
+
key: "sizePixels",
|
|
906
|
+
label: "Size",
|
|
907
|
+
description: "Size of the fortified-area teeth, in pixels",
|
|
908
|
+
type: "number",
|
|
909
|
+
min: 5,
|
|
910
|
+
max: 50,
|
|
911
|
+
step: 1,
|
|
912
|
+
unit: "px"
|
|
913
|
+
}, {
|
|
914
|
+
key: "size",
|
|
915
|
+
label: "Size",
|
|
916
|
+
description: "Size of the fortified-area teeth, in meters",
|
|
917
|
+
type: "number",
|
|
918
|
+
min: 1,
|
|
919
|
+
max: 500,
|
|
920
|
+
step: 1,
|
|
921
|
+
unit: "m"
|
|
922
|
+
}];
|
|
923
|
+
const ENCIRCLEMENT_PARAMS = [
|
|
879
924
|
{
|
|
880
925
|
key: "smooth",
|
|
881
926
|
label: "Smooth",
|
|
882
|
-
description: "Round the
|
|
927
|
+
description: "Round the boundary by curving it through the control points.",
|
|
883
928
|
type: "boolean"
|
|
884
929
|
},
|
|
885
930
|
{
|
|
@@ -892,25 +937,43 @@ const FORTIFIED_AREA_PARAMS = [
|
|
|
892
937
|
step: 1,
|
|
893
938
|
visibleWhen: (opts) => Boolean(opts.smooth)
|
|
894
939
|
},
|
|
895
|
-
|
|
896
|
-
key: "
|
|
897
|
-
label: "
|
|
898
|
-
description: "
|
|
940
|
+
{
|
|
941
|
+
key: "barbSpacingRatio",
|
|
942
|
+
label: "Spacing",
|
|
943
|
+
description: "Spacing between barbs as a ratio of the symbol radius (smaller is denser).",
|
|
899
944
|
type: "number",
|
|
900
|
-
min:
|
|
901
|
-
max:
|
|
902
|
-
step:
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
key: "
|
|
906
|
-
label: "
|
|
907
|
-
description: "
|
|
945
|
+
min: .15,
|
|
946
|
+
max: 1.5,
|
|
947
|
+
step: .05
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
key: "barbLengthRatio",
|
|
951
|
+
label: "Barb length",
|
|
952
|
+
description: "Barb height as a ratio of the symbol radius (amplitude).",
|
|
908
953
|
type: "number",
|
|
909
|
-
min:
|
|
910
|
-
max:
|
|
954
|
+
min: .02,
|
|
955
|
+
max: .5,
|
|
956
|
+
step: .01
|
|
957
|
+
}
|
|
958
|
+
];
|
|
959
|
+
const FORTIFIED_AREA_PARAMS = [
|
|
960
|
+
{
|
|
961
|
+
key: "smooth",
|
|
962
|
+
label: "Smooth",
|
|
963
|
+
description: "Round the area's corners by curving the boundary through the control points.",
|
|
964
|
+
type: "boolean"
|
|
965
|
+
},
|
|
966
|
+
{
|
|
967
|
+
key: "smoothResolution",
|
|
968
|
+
label: "Smooth resolution",
|
|
969
|
+
description: "Number of samples per segment when smooth mode is enabled.",
|
|
970
|
+
type: "number",
|
|
971
|
+
min: 2,
|
|
972
|
+
max: 64,
|
|
911
973
|
step: 1,
|
|
912
|
-
|
|
913
|
-
}
|
|
974
|
+
visibleWhen: (opts) => Boolean(opts.smooth)
|
|
975
|
+
},
|
|
976
|
+
...FORTIFIED_AREA_SIZE_PARAMS
|
|
914
977
|
];
|
|
915
978
|
//#endregion
|
|
916
979
|
//#region src/generators/cm15-maneuver-areas/airborneAttack.ts
|
|
@@ -2181,8 +2244,121 @@ function closedCatmullRom(points, samples) {
|
|
|
2181
2244
|
return out;
|
|
2182
2245
|
}
|
|
2183
2246
|
//#endregion
|
|
2247
|
+
//#region src/internal/area-echelon.ts
|
|
2248
|
+
/** Mean of the ring's distinct vertices, used as the outward-normal reference. */
|
|
2249
|
+
function ringCentroid(verts, count) {
|
|
2250
|
+
let cx = 0;
|
|
2251
|
+
let cy = 0;
|
|
2252
|
+
for (let i = 0; i < count; i++) {
|
|
2253
|
+
cx += verts[i][0];
|
|
2254
|
+
cy += verts[i][1];
|
|
2255
|
+
}
|
|
2256
|
+
return [cx / count, cy / count];
|
|
2257
|
+
}
|
|
2258
|
+
/**
|
|
2259
|
+
* Projects `positions` and returns a closed ring (first == last), optionally
|
|
2260
|
+
* smoothed through the control points with a closed centripetal Catmull-Rom
|
|
2261
|
+
* spline. A trailing vertex that duplicates the first (already-closed input)
|
|
2262
|
+
* is dropped before closing/smoothing.
|
|
2263
|
+
*/
|
|
2264
|
+
function buildClosedRing(positions, smooth, smoothResolution, defaultSmoothResolution) {
|
|
2265
|
+
const projected = positions.map((p) => project(p[0], p[1]));
|
|
2266
|
+
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;
|
|
2267
|
+
return smooth ? closedCatmullRom(ringPts, normalizeSmoothResolution$2(smoothResolution, defaultSmoothResolution)) : [...ringPts, ringPts[0]];
|
|
2268
|
+
}
|
|
2269
|
+
/**
|
|
2270
|
+
* Walks a closed ring into per-segment metadata (unit direction, outward
|
|
2271
|
+
* normal, cumulative arc length) plus the total perimeter and the arc-length
|
|
2272
|
+
* anchor of the bottom edge — the segment whose midpoint sits lowest (min
|
|
2273
|
+
* projected y), the side opposite the front.
|
|
2274
|
+
*/
|
|
2275
|
+
function buildAreaPerimeter(verts) {
|
|
2276
|
+
const centroid = ringCentroid(verts, verts.length - 1);
|
|
2277
|
+
const segs = [];
|
|
2278
|
+
let totalLength = 0;
|
|
2279
|
+
let anchorTarget = 0;
|
|
2280
|
+
let lowest = Infinity;
|
|
2281
|
+
for (let i = 0; i < verts.length - 1; i++) {
|
|
2282
|
+
const a = verts[i];
|
|
2283
|
+
const b = verts[i + 1];
|
|
2284
|
+
const edge = vecSub(b, a);
|
|
2285
|
+
const length = vecMag(edge);
|
|
2286
|
+
if (length < 1e-6) continue;
|
|
2287
|
+
const along = vecNorm(edge);
|
|
2288
|
+
const mid = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
|
|
2289
|
+
let perp = [-along[1], along[0]];
|
|
2290
|
+
if (perp[0] * (mid[0] - centroid[0]) + perp[1] * (mid[1] - centroid[1]) < 0) perp = [-perp[0], -perp[1]];
|
|
2291
|
+
segs.push({
|
|
2292
|
+
start: a,
|
|
2293
|
+
along,
|
|
2294
|
+
perp,
|
|
2295
|
+
length,
|
|
2296
|
+
arcStart: totalLength
|
|
2297
|
+
});
|
|
2298
|
+
if (mid[1] < lowest) {
|
|
2299
|
+
lowest = mid[1];
|
|
2300
|
+
anchorTarget = totalLength + length / 2;
|
|
2301
|
+
}
|
|
2302
|
+
totalLength += length;
|
|
2303
|
+
}
|
|
2304
|
+
return {
|
|
2305
|
+
verts,
|
|
2306
|
+
segs,
|
|
2307
|
+
totalLength,
|
|
2308
|
+
anchorTarget
|
|
2309
|
+
};
|
|
2310
|
+
}
|
|
2311
|
+
/** Resolves the arc-length `target` to a point and the segment frame it lands on. */
|
|
2312
|
+
function pointOnRing(segs, target) {
|
|
2313
|
+
let seg = segs[segs.length - 1];
|
|
2314
|
+
for (const candidate of segs) if (target < candidate.arcStart + candidate.length) {
|
|
2315
|
+
seg = candidate;
|
|
2316
|
+
break;
|
|
2317
|
+
}
|
|
2318
|
+
return {
|
|
2319
|
+
point: vecAdd(seg.start, vecScale(seg.along, target - seg.arcStart)),
|
|
2320
|
+
seg
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2323
|
+
/**
|
|
2324
|
+
* Effective echelon glyph height in meters. Screen-anchored when a pixel size
|
|
2325
|
+
* is supplied with a live resolution; ground-anchored (meters) otherwise.
|
|
2326
|
+
* Clamped strictly positive.
|
|
2327
|
+
*/
|
|
2328
|
+
function resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel) {
|
|
2329
|
+
let h = echelonSize;
|
|
2330
|
+
if (echelonSizePixels !== void 0 && metersPerPixel !== void 0 && metersPerPixel > 0) h = echelonSizePixels * metersPerPixel;
|
|
2331
|
+
return Math.max(EPSILON, h);
|
|
2332
|
+
}
|
|
2333
|
+
/**
|
|
2334
|
+
* 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.
|
|
2338
|
+
*/
|
|
2339
|
+
function placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition) {
|
|
2340
|
+
const { segs, totalLength, anchorTarget } = perimeter;
|
|
2341
|
+
const spec = resolveEchelon(echelon);
|
|
2342
|
+
if (!spec || segs.length === 0 || totalLength < 1e-6) return {
|
|
2343
|
+
strokes: [],
|
|
2344
|
+
fills: [],
|
|
2345
|
+
gaps: []
|
|
2346
|
+
};
|
|
2347
|
+
const target = ((anchorTarget + echelonPosition * totalLength) % totalLength + totalLength) % totalLength;
|
|
2348
|
+
const { point, seg } = pointOnRing(segs, target);
|
|
2349
|
+
const { strokes, fills, width } = buildEchelonGlyph(point, seg.along, seg.perp, h, spec);
|
|
2350
|
+
const gaps = [];
|
|
2351
|
+
const halfGap = width / 2 + Math.max(0, echelonPadding) * h;
|
|
2352
|
+
pushWrappedGap(gaps, target - halfGap, target + halfGap, totalLength);
|
|
2353
|
+
return {
|
|
2354
|
+
strokes,
|
|
2355
|
+
fills,
|
|
2356
|
+
gaps
|
|
2357
|
+
};
|
|
2358
|
+
}
|
|
2359
|
+
//#endregion
|
|
2184
2360
|
//#region src/generators/cm15-maneuver-areas/battlePosition.ts
|
|
2185
|
-
const DEFAULT_SMOOTH_RESOLUTION$
|
|
2361
|
+
const DEFAULT_SMOOTH_RESOLUTION$6 = 16;
|
|
2186
2362
|
/** Default options for the Battle Position control measure. */
|
|
2187
2363
|
const DEFAULT_BATTLE_POSITION_OPTIONS = {
|
|
2188
2364
|
echelon: "battalion",
|
|
@@ -2191,7 +2367,7 @@ const DEFAULT_BATTLE_POSITION_OPTIONS = {
|
|
|
2191
2367
|
echelonPadding: .3,
|
|
2192
2368
|
echelonPosition: 0,
|
|
2193
2369
|
smooth: false,
|
|
2194
|
-
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$
|
|
2370
|
+
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$6
|
|
2195
2371
|
};
|
|
2196
2372
|
const BATTLE_POSITION_METADATA = {
|
|
2197
2373
|
id: "battle-position",
|
|
@@ -2268,16 +2444,6 @@ const BATTLE_POSITION_METADATA = {
|
|
|
2268
2444
|
}
|
|
2269
2445
|
]
|
|
2270
2446
|
};
|
|
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
2447
|
/**
|
|
2282
2448
|
* Creates a Battle Position control measure: a closed area through `positions`
|
|
2283
2449
|
* carrying a single echelon (Field B) glyph straddling its boundary — anchored
|
|
@@ -2289,59 +2455,9 @@ function ringCentroid(verts, count) {
|
|
|
2289
2455
|
*/
|
|
2290
2456
|
function createBattlePosition(positions, options = {}) {
|
|
2291
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;
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
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
|
-
}
|
|
2458
|
+
const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
|
|
2459
|
+
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$6);
|
|
2460
|
+
const { strokes, fills, gaps } = placeEchelon(buildAreaPerimeter(verts), h, echelon, echelonPadding, echelonPosition);
|
|
2345
2461
|
const boundaryCoords = buildGappedLine(verts, gaps);
|
|
2346
2462
|
const features = [];
|
|
2347
2463
|
if (boundaryCoords.length > 0) features.push({
|
|
@@ -2352,15 +2468,15 @@ function createBattlePosition(positions, options = {}) {
|
|
|
2352
2468
|
coordinates: boundaryCoords
|
|
2353
2469
|
}
|
|
2354
2470
|
});
|
|
2355
|
-
if (
|
|
2471
|
+
if (strokes.length > 0) features.push({
|
|
2356
2472
|
type: "Feature",
|
|
2357
2473
|
properties: { part: "echelon" },
|
|
2358
2474
|
geometry: {
|
|
2359
2475
|
type: "MultiLineString",
|
|
2360
|
-
coordinates:
|
|
2476
|
+
coordinates: strokes
|
|
2361
2477
|
}
|
|
2362
2478
|
});
|
|
2363
|
-
if (
|
|
2479
|
+
if (fills.length > 0) features.push({
|
|
2364
2480
|
type: "Feature",
|
|
2365
2481
|
properties: {
|
|
2366
2482
|
part: "echelon",
|
|
@@ -2368,7 +2484,7 @@ function createBattlePosition(positions, options = {}) {
|
|
|
2368
2484
|
},
|
|
2369
2485
|
geometry: {
|
|
2370
2486
|
type: "MultiPolygon",
|
|
2371
|
-
coordinates:
|
|
2487
|
+
coordinates: fills.map((ring) => [ring])
|
|
2372
2488
|
}
|
|
2373
2489
|
});
|
|
2374
2490
|
return {
|
|
@@ -2380,6 +2496,7 @@ const BATTLE_POSITION = defineControlMeasure({
|
|
|
2380
2496
|
metadata: BATTLE_POSITION_METADATA,
|
|
2381
2497
|
generator: createBattlePosition,
|
|
2382
2498
|
defaultOptions: DEFAULT_BATTLE_POSITION_OPTIONS,
|
|
2499
|
+
rule: area1DrawRule,
|
|
2383
2500
|
previewSample: {
|
|
2384
2501
|
controlPoints: [
|
|
2385
2502
|
[-1, -.5],
|
|
@@ -2387,7 +2504,10 @@ const BATTLE_POSITION = defineControlMeasure({
|
|
|
2387
2504
|
[1, .5],
|
|
2388
2505
|
[-1, .5]
|
|
2389
2506
|
],
|
|
2390
|
-
options: {
|
|
2507
|
+
options: {
|
|
2508
|
+
echelonSize: 200,
|
|
2509
|
+
smooth: true
|
|
2510
|
+
}
|
|
2391
2511
|
}
|
|
2392
2512
|
});
|
|
2393
2513
|
//#endregion
|
|
@@ -2458,7 +2578,7 @@ const BLOCK = defineControlMeasure({
|
|
|
2458
2578
|
});
|
|
2459
2579
|
//#endregion
|
|
2460
2580
|
//#region src/generators/cm99-generic-arrows/blockArrow.ts
|
|
2461
|
-
const DEFAULT_SMOOTH_RESOLUTION$
|
|
2581
|
+
const DEFAULT_SMOOTH_RESOLUTION$5 = 12;
|
|
2462
2582
|
const MIN_SMOOTH_RESOLUTION$1 = 2;
|
|
2463
2583
|
const MAX_SMOOTH_RESOLUTION$1 = 64;
|
|
2464
2584
|
const DEFAULT_BLOCK_ARROW_OPTIONS = {
|
|
@@ -2467,7 +2587,7 @@ const DEFAULT_BLOCK_ARROW_OPTIONS = {
|
|
|
2467
2587
|
arrowheadWidthRatio: .18,
|
|
2468
2588
|
arrowheadLengthRatio: .22,
|
|
2469
2589
|
smooth: false,
|
|
2470
|
-
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$
|
|
2590
|
+
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$5,
|
|
2471
2591
|
filled: true
|
|
2472
2592
|
};
|
|
2473
2593
|
const BLOCK_ARROW_METADATA = {
|
|
@@ -2704,7 +2824,7 @@ function axis1Geometry(coordinates) {
|
|
|
2704
2824
|
};
|
|
2705
2825
|
}
|
|
2706
2826
|
function normalizeSmoothResolution$1(value) {
|
|
2707
|
-
if (!Number.isFinite(value)) return DEFAULT_SMOOTH_RESOLUTION$
|
|
2827
|
+
if (!Number.isFinite(value)) return DEFAULT_SMOOTH_RESOLUTION$5;
|
|
2708
2828
|
return Math.min(MAX_SMOOTH_RESOLUTION$1, Math.max(MIN_SMOOTH_RESOLUTION$1, Math.round(value)));
|
|
2709
2829
|
}
|
|
2710
2830
|
//#endregion
|
|
@@ -2730,7 +2850,7 @@ function keepTextLeftToRight(rotation) {
|
|
|
2730
2850
|
}
|
|
2731
2851
|
//#endregion
|
|
2732
2852
|
//#region src/generators/cm11-command-control-lines/boundary.ts
|
|
2733
|
-
const DEFAULT_SMOOTH_RESOLUTION$
|
|
2853
|
+
const DEFAULT_SMOOTH_RESOLUTION$4 = 12;
|
|
2734
2854
|
/** Default options for the Boundary control measure. */
|
|
2735
2855
|
const DEFAULT_BOUNDARY_OPTIONS = {
|
|
2736
2856
|
echelon: "battalion",
|
|
@@ -2744,7 +2864,7 @@ const DEFAULT_BOUNDARY_OPTIONS = {
|
|
|
2744
2864
|
unit2Designator: "",
|
|
2745
2865
|
unit2Country: "",
|
|
2746
2866
|
smooth: false,
|
|
2747
|
-
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$
|
|
2867
|
+
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$4
|
|
2748
2868
|
};
|
|
2749
2869
|
const BOUNDARY_METADATA = {
|
|
2750
2870
|
id: "boundary",
|
|
@@ -2897,7 +3017,7 @@ function createBoundary(positions, options = {}) {
|
|
|
2897
3017
|
if (labelSizeZoom !== void 0) labelSize.textSizeZoom = labelSizeZoom;
|
|
2898
3018
|
}
|
|
2899
3019
|
const rawVerts = positions.map((p) => project(p[0], p[1]));
|
|
2900
|
-
const verts = smooth ? catmullRom(rawVerts, normalizeSmoothResolution$2(smoothResolution, DEFAULT_SMOOTH_RESOLUTION$
|
|
3020
|
+
const verts = smooth ? catmullRom(rawVerts, normalizeSmoothResolution$2(smoothResolution, DEFAULT_SMOOTH_RESOLUTION$4)) : rawVerts;
|
|
2901
3021
|
const glyphStrokes = [];
|
|
2902
3022
|
const glyphFills = [];
|
|
2903
3023
|
const labelFeatures = [];
|
|
@@ -3488,13 +3608,13 @@ const CANALIZE = defineControlMeasure({
|
|
|
3488
3608
|
});
|
|
3489
3609
|
//#endregion
|
|
3490
3610
|
//#region src/generators/cm99-generic-arrows/classicArrow.ts
|
|
3491
|
-
const DEFAULT_SMOOTH_RESOLUTION$
|
|
3611
|
+
const DEFAULT_SMOOTH_RESOLUTION$3 = 12;
|
|
3492
3612
|
const MIN_SMOOTH_RESOLUTION = 2;
|
|
3493
3613
|
const MAX_SMOOTH_RESOLUTION = 64;
|
|
3494
3614
|
const DEFAULT_CLASSIC_ARROW_OPTIONS = {
|
|
3495
3615
|
arrowheadStyle: "triangle",
|
|
3496
3616
|
smooth: false,
|
|
3497
|
-
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$
|
|
3617
|
+
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$3,
|
|
3498
3618
|
arrowheadLengthRatio: .2,
|
|
3499
3619
|
arrowheadWidthRatio: .14
|
|
3500
3620
|
};
|
|
@@ -3781,7 +3901,7 @@ const CLASSIC_ARROW = defineControlMeasure({
|
|
|
3781
3901
|
rule: line1DrawRule
|
|
3782
3902
|
});
|
|
3783
3903
|
function normalizeSmoothResolution(value) {
|
|
3784
|
-
if (!Number.isFinite(value)) return DEFAULT_SMOOTH_RESOLUTION$
|
|
3904
|
+
if (!Number.isFinite(value)) return DEFAULT_SMOOTH_RESOLUTION$3;
|
|
3785
3905
|
return Math.min(MAX_SMOOTH_RESOLUTION, Math.max(MIN_SMOOTH_RESOLUTION, Math.round(value)));
|
|
3786
3906
|
}
|
|
3787
3907
|
//#endregion
|
|
@@ -4271,6 +4391,139 @@ const DISRUPT = defineControlMeasure({
|
|
|
4271
4391
|
previewSample: { controlPoints: [[0, .4], [0, -.4]] }
|
|
4272
4392
|
});
|
|
4273
4393
|
//#endregion
|
|
4394
|
+
//#region src/generators/cm15-maneuver-areas/encirclement.ts
|
|
4395
|
+
const DEFAULT_BARB_SPACING_RATIO = .4;
|
|
4396
|
+
const DEFAULT_BARB_LENGTH_RATIO = .16;
|
|
4397
|
+
const DEFAULT_SMOOTH_RESOLUTION$2 = 16;
|
|
4398
|
+
/** Default options for the Encirclement control measure. */
|
|
4399
|
+
const DEFAULT_ENCIRCLEMENT_OPTIONS = {
|
|
4400
|
+
barbSpacingRatio: DEFAULT_BARB_SPACING_RATIO,
|
|
4401
|
+
barbLengthRatio: DEFAULT_BARB_LENGTH_RATIO,
|
|
4402
|
+
smooth: true,
|
|
4403
|
+
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$2
|
|
4404
|
+
};
|
|
4405
|
+
const ENCIRCLEMENT_METADATA = {
|
|
4406
|
+
id: "encirclement",
|
|
4407
|
+
name: "Encirclement",
|
|
4408
|
+
description: "The loss of freedom of maneuver resulting from enemy control of all ground routes of evacuation and reinforcement.",
|
|
4409
|
+
entity: "Maneuver Areas",
|
|
4410
|
+
entityType: "Encirclement",
|
|
4411
|
+
value: "151800",
|
|
4412
|
+
minCoordinates: 3,
|
|
4413
|
+
geometry: "area",
|
|
4414
|
+
geometryTypes: ["Polygon", "MultiLineString"],
|
|
4415
|
+
drawRule: "Area1",
|
|
4416
|
+
params: ENCIRCLEMENT_PARAMS
|
|
4417
|
+
};
|
|
4418
|
+
/** Mean distance from the ring centroid to its distinct vertices. */
|
|
4419
|
+
function meanRadius(verts) {
|
|
4420
|
+
const n = verts.length - 1;
|
|
4421
|
+
if (n <= 0) return 0;
|
|
4422
|
+
let cx = 0;
|
|
4423
|
+
let cy = 0;
|
|
4424
|
+
for (let i = 0; i < n; i++) {
|
|
4425
|
+
cx += verts[i][0];
|
|
4426
|
+
cy += verts[i][1];
|
|
4427
|
+
}
|
|
4428
|
+
cx /= n;
|
|
4429
|
+
cy /= n;
|
|
4430
|
+
let sum = 0;
|
|
4431
|
+
for (let i = 0; i < n; i++) sum += Math.hypot(verts[i][0] - cx, verts[i][1] - cy);
|
|
4432
|
+
return sum / n;
|
|
4433
|
+
}
|
|
4434
|
+
/**
|
|
4435
|
+
* Builds the outward barbs as open "V" strokes sitting on the boundary, evenly
|
|
4436
|
+
* spaced around the perimeter. The requested `spacing` (arc length) is rounded
|
|
4437
|
+
* to a whole number of barbs so they close the ring exactly. Each barb runs
|
|
4438
|
+
* base-start → apex → base-end with the apex pushed out along the local outward
|
|
4439
|
+
* normal; the base ends are sampled on the ring at ±`barbLength/2` of arc so the
|
|
4440
|
+
* barb's base width equals its length (a clean arrowhead, as in Isolate) and
|
|
4441
|
+
* follows the boundary curvature. The base half-width is capped so dense barbs
|
|
4442
|
+
* never overlap.
|
|
4443
|
+
*/
|
|
4444
|
+
function buildBarbs(perimeter, spacing, barbLength) {
|
|
4445
|
+
const { segs, totalLength } = perimeter;
|
|
4446
|
+
if (segs.length === 0 || totalLength < 1e-6 || barbLength < 1e-6 || spacing < 1e-6) return [];
|
|
4447
|
+
const count = Math.max(3, Math.round(totalLength / spacing));
|
|
4448
|
+
const half = Math.min(barbLength / 2, totalLength / (2 * count));
|
|
4449
|
+
const at = (arc) => pointOnRing(segs, (arc % totalLength + totalLength) % totalLength);
|
|
4450
|
+
const barbs = [];
|
|
4451
|
+
for (let k = 0; k < count; k++) {
|
|
4452
|
+
const center = k * totalLength / count;
|
|
4453
|
+
const baseStart = at(center - half).point;
|
|
4454
|
+
const { point, seg } = at(center);
|
|
4455
|
+
const apex = vecAdd(point, vecScale(seg.perp, barbLength));
|
|
4456
|
+
const baseEnd = at(center + half).point;
|
|
4457
|
+
barbs.push([
|
|
4458
|
+
baseStart,
|
|
4459
|
+
apex,
|
|
4460
|
+
baseEnd
|
|
4461
|
+
].map((p) => unproject(p[0], p[1])));
|
|
4462
|
+
}
|
|
4463
|
+
return barbs;
|
|
4464
|
+
}
|
|
4465
|
+
/**
|
|
4466
|
+
* Creates an Encirclement tactical graphic: a complete smooth boundary ring
|
|
4467
|
+
* with outward arrowhead barbs resting on it. The boundary is an unfilled
|
|
4468
|
+
* Polygon; the barbs are open "V" strokes (a MultiLineString) whose base is the
|
|
4469
|
+
* boundary arc itself.
|
|
4470
|
+
*
|
|
4471
|
+
* @param positions - Area anchor points (>=3); the input contract is enforced
|
|
4472
|
+
* at the render seam (ADR-0014).
|
|
4473
|
+
* @param options - {@link EncirclementOptions}.
|
|
4474
|
+
*/
|
|
4475
|
+
function createEncirclement(positions, options = {}) {
|
|
4476
|
+
const { barbSpacingRatio = DEFAULT_BARB_SPACING_RATIO, barbLengthRatio = DEFAULT_BARB_LENGTH_RATIO, smooth = DEFAULT_ENCIRCLEMENT_OPTIONS.smooth, smoothResolution = DEFAULT_ENCIRCLEMENT_OPTIONS.smoothResolution } = options;
|
|
4477
|
+
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$2);
|
|
4478
|
+
const perimeter = buildAreaPerimeter(verts);
|
|
4479
|
+
const radius = meanRadius(verts);
|
|
4480
|
+
const barbLength = radius * Math.max(0, barbLengthRatio);
|
|
4481
|
+
const barbs = buildBarbs(perimeter, radius * Math.max(0, barbSpacingRatio), barbLength);
|
|
4482
|
+
const features = [{
|
|
4483
|
+
type: "Feature",
|
|
4484
|
+
properties: { part: "boundary" },
|
|
4485
|
+
geometry: {
|
|
4486
|
+
type: "Polygon",
|
|
4487
|
+
coordinates: [verts.map((p) => unproject(p[0], p[1]))]
|
|
4488
|
+
}
|
|
4489
|
+
}];
|
|
4490
|
+
if (barbs.length > 0) features.push({
|
|
4491
|
+
type: "Feature",
|
|
4492
|
+
properties: { part: "barbs" },
|
|
4493
|
+
geometry: {
|
|
4494
|
+
type: "MultiLineString",
|
|
4495
|
+
coordinates: barbs
|
|
4496
|
+
}
|
|
4497
|
+
});
|
|
4498
|
+
return {
|
|
4499
|
+
type: "FeatureCollection",
|
|
4500
|
+
features
|
|
4501
|
+
};
|
|
4502
|
+
}
|
|
4503
|
+
const ENCIRCLEMENT = defineControlMeasure({
|
|
4504
|
+
metadata: ENCIRCLEMENT_METADATA,
|
|
4505
|
+
generator: createEncirclement,
|
|
4506
|
+
defaultOptions: DEFAULT_ENCIRCLEMENT_OPTIONS,
|
|
4507
|
+
rule: area1DrawRule,
|
|
4508
|
+
previewSample: {
|
|
4509
|
+
controlPoints: [
|
|
4510
|
+
[-1, -.5],
|
|
4511
|
+
[-.4, -.85],
|
|
4512
|
+
[.4, -.85],
|
|
4513
|
+
[1, -.5],
|
|
4514
|
+
[1, .5],
|
|
4515
|
+
[.4, .85],
|
|
4516
|
+
[-.4, .85],
|
|
4517
|
+
[-1, .5]
|
|
4518
|
+
],
|
|
4519
|
+
options: {
|
|
4520
|
+
barbSpacingRatio: .4,
|
|
4521
|
+
barbLengthRatio: .18,
|
|
4522
|
+
smooth: true
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
});
|
|
4526
|
+
//#endregion
|
|
4274
4527
|
//#region src/internal/field-of-fire.ts
|
|
4275
4528
|
/**
|
|
4276
4529
|
* Builds the open (stroked) arrowhead at the outward tip of one arm. The two
|
|
@@ -4810,7 +5063,7 @@ const FLOT = defineControlMeasure({
|
|
|
4810
5063
|
//#region src/generators/cm29-protection-lines/fortifiedLine.ts
|
|
4811
5064
|
const DEFAULT_SIZE = 50;
|
|
4812
5065
|
const DEFAULT_SIZE_PIXELS = 10;
|
|
4813
|
-
const DEFAULT_SMOOTH_RESOLUTION = 16;
|
|
5066
|
+
const DEFAULT_SMOOTH_RESOLUTION$1 = 16;
|
|
4814
5067
|
/**
|
|
4815
5068
|
* Default options for the Fortified Line graphic.
|
|
4816
5069
|
*/
|
|
@@ -4818,7 +5071,7 @@ const DEFAULT_FORTIFIED_LINE_OPTIONS = {
|
|
|
4818
5071
|
size: DEFAULT_SIZE,
|
|
4819
5072
|
sizePixels: DEFAULT_SIZE_PIXELS,
|
|
4820
5073
|
smooth: false,
|
|
4821
|
-
smoothResolution: DEFAULT_SMOOTH_RESOLUTION
|
|
5074
|
+
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$1
|
|
4822
5075
|
};
|
|
4823
5076
|
const FORTIFIED_LINE_METADATA = {
|
|
4824
5077
|
id: "fortified-line",
|
|
@@ -4950,7 +5203,7 @@ function createFortifiedLine(positions, options = {}) {
|
|
|
4950
5203
|
properties: {},
|
|
4951
5204
|
geometry: {
|
|
4952
5205
|
type: "LineString",
|
|
4953
|
-
coordinates: generateFortifiedPoints(smooth ? catmullRom(projectedPoints, normalizeSmoothResolution$2(smoothResolution, DEFAULT_SMOOTH_RESOLUTION)) : projectedPoints, safeSize).map((p) => unproject(p[0], p[1]))
|
|
5206
|
+
coordinates: generateFortifiedPoints(smooth ? catmullRom(projectedPoints, normalizeSmoothResolution$2(smoothResolution, DEFAULT_SMOOTH_RESOLUTION$1)) : projectedPoints, safeSize).map((p) => unproject(p[0], p[1]))
|
|
4954
5207
|
}
|
|
4955
5208
|
}]
|
|
4956
5209
|
};
|
|
@@ -5018,6 +5271,7 @@ const FORTIFIED_AREA = defineControlMeasure({
|
|
|
5018
5271
|
metadata: FORTIFIED_AREA_METADATA,
|
|
5019
5272
|
generator: createFortifiedArea,
|
|
5020
5273
|
defaultOptions: DEFAULT_FORTIFIED_AREA_OPTIONS,
|
|
5274
|
+
rule: area1DrawRule,
|
|
5021
5275
|
previewSample: { controlPoints: [
|
|
5022
5276
|
[-1, -.5],
|
|
5023
5277
|
[1, -.5],
|
|
@@ -5875,6 +6129,192 @@ const SEARCH_AREA = defineControlMeasure({
|
|
|
5875
6129
|
rule: searchAreaDrawRule
|
|
5876
6130
|
});
|
|
5877
6131
|
//#endregion
|
|
6132
|
+
//#region src/generators/cm15-maneuver-areas/strongPoint.ts
|
|
6133
|
+
const DEFAULT_SMOOTH_RESOLUTION = 16;
|
|
6134
|
+
/** Default options for the Strong Point control measure. */
|
|
6135
|
+
const DEFAULT_STRONG_POINT_OPTIONS = { ...DEFAULT_BATTLE_POSITION_OPTIONS };
|
|
6136
|
+
const STRONG_POINT_METADATA = {
|
|
6137
|
+
id: "strong-point",
|
|
6138
|
+
name: "Strong Point",
|
|
6139
|
+
description: "A key point in a defensive position, usually strongly fortified and heavily armed, around which other positions are grouped for its protection.",
|
|
6140
|
+
entity: "Maneuver Areas",
|
|
6141
|
+
entityType: "Battle Position",
|
|
6142
|
+
entitySubtype: "Strong Point",
|
|
6143
|
+
value: "151203",
|
|
6144
|
+
minCoordinates: 3,
|
|
6145
|
+
geometry: "area",
|
|
6146
|
+
geometryTypes: ["MultiLineString", "MultiPolygon"],
|
|
6147
|
+
drawRule: "Area1",
|
|
6148
|
+
params: [
|
|
6149
|
+
{
|
|
6150
|
+
key: "smooth",
|
|
6151
|
+
label: "Smooth",
|
|
6152
|
+
description: "Round the area's corners by curving the boundary through the control points.",
|
|
6153
|
+
type: "boolean"
|
|
6154
|
+
},
|
|
6155
|
+
{
|
|
6156
|
+
key: "smoothResolution",
|
|
6157
|
+
label: "Smooth resolution",
|
|
6158
|
+
description: "Number of samples per segment when smooth mode is enabled.",
|
|
6159
|
+
type: "number",
|
|
6160
|
+
min: 2,
|
|
6161
|
+
max: 64,
|
|
6162
|
+
step: 1,
|
|
6163
|
+
visibleWhen: (opts) => Boolean(opts.smooth)
|
|
6164
|
+
},
|
|
6165
|
+
{
|
|
6166
|
+
key: "echelon",
|
|
6167
|
+
label: "Echelon",
|
|
6168
|
+
description: "Field B unit-size amplifier, drawn on the boundary.",
|
|
6169
|
+
type: "enum",
|
|
6170
|
+
options: ECHELON_PARAM_OPTIONS
|
|
6171
|
+
},
|
|
6172
|
+
{
|
|
6173
|
+
key: "echelonSizePixels",
|
|
6174
|
+
label: "Echelon size",
|
|
6175
|
+
description: "Echelon glyph height in screen pixels.",
|
|
6176
|
+
type: "number",
|
|
6177
|
+
min: 8,
|
|
6178
|
+
max: 48,
|
|
6179
|
+
step: 1,
|
|
6180
|
+
unit: "px"
|
|
6181
|
+
},
|
|
6182
|
+
{
|
|
6183
|
+
key: "echelonSize",
|
|
6184
|
+
label: "Echelon size",
|
|
6185
|
+
description: "Echelon glyph height in meters.",
|
|
6186
|
+
type: "number",
|
|
6187
|
+
min: 50,
|
|
6188
|
+
max: 2e4,
|
|
6189
|
+
step: 50,
|
|
6190
|
+
unit: "m"
|
|
6191
|
+
},
|
|
6192
|
+
{
|
|
6193
|
+
key: "echelonPadding",
|
|
6194
|
+
label: "Echelon padding",
|
|
6195
|
+
description: "Gap on each side of the echelon, as a ratio of its height.",
|
|
6196
|
+
type: "number",
|
|
6197
|
+
min: 0,
|
|
6198
|
+
max: 2,
|
|
6199
|
+
step: .05
|
|
6200
|
+
},
|
|
6201
|
+
{
|
|
6202
|
+
key: "echelonPosition",
|
|
6203
|
+
label: "Echelon position",
|
|
6204
|
+
description: "Slide the echelon along the boundary, as a fraction of the perimeter from the bottom edge.",
|
|
6205
|
+
type: "number",
|
|
6206
|
+
min: -.5,
|
|
6207
|
+
max: .5,
|
|
6208
|
+
step: .01
|
|
6209
|
+
}
|
|
6210
|
+
]
|
|
6211
|
+
};
|
|
6212
|
+
/** True when the arc-length `target` falls inside any echelon masking gap. */
|
|
6213
|
+
function inGap(target, gaps) {
|
|
6214
|
+
return gaps.some((gap) => target >= gap.g0 && target <= gap.g1);
|
|
6215
|
+
}
|
|
6216
|
+
/**
|
|
6217
|
+
* Outward perimeter tics: a stroke every `h` meters along the ring, each `h`
|
|
6218
|
+
* meters long along the segment's outward normal. Tic length, spacing, and the
|
|
6219
|
+
* first-tic phase are all the Field B echelon height `h`. Targets inside an
|
|
6220
|
+
* echelon gap are suppressed so the tics don't collide with the glyph.
|
|
6221
|
+
*
|
|
6222
|
+
* The `target` grows monotonically, so a single forward segment cursor walks
|
|
6223
|
+
* the perimeter in O(segments + tics) instead of rescanning per tic.
|
|
6224
|
+
*/
|
|
6225
|
+
function buildStrongPointTics(perimeter, gaps, h) {
|
|
6226
|
+
const { segs, totalLength } = perimeter;
|
|
6227
|
+
if (segs.length === 0 || totalLength < 1e-6) return [];
|
|
6228
|
+
const tics = [];
|
|
6229
|
+
let cursor = 0;
|
|
6230
|
+
for (let target = h / 2; target < totalLength; target += h) {
|
|
6231
|
+
if (inGap(target, gaps)) continue;
|
|
6232
|
+
while (cursor < segs.length - 1 && target >= segs[cursor].arcStart + segs[cursor].length) cursor++;
|
|
6233
|
+
const seg = segs[cursor];
|
|
6234
|
+
const point = vecAdd(seg.start, vecScale(seg.along, target - seg.arcStart));
|
|
6235
|
+
const end = vecAdd(point, vecScale(seg.perp, h));
|
|
6236
|
+
tics.push([unproject(point[0], point[1]), unproject(end[0], end[1])]);
|
|
6237
|
+
}
|
|
6238
|
+
return tics;
|
|
6239
|
+
}
|
|
6240
|
+
/**
|
|
6241
|
+
* Creates a Strong Point control measure: a Battle Position boundary/echelon
|
|
6242
|
+
* with outward tics whose length and spacing are the Field B echelon height.
|
|
6243
|
+
* Shares the perimeter and echelon placement with Battle Position via the
|
|
6244
|
+
* `area-echelon` primitive (single source of truth for ring and gap).
|
|
6245
|
+
*
|
|
6246
|
+
* @param positions - Area anchor points (>=3); the input contract is enforced
|
|
6247
|
+
* at the render seam (ADR-0014).
|
|
6248
|
+
* @param options - {@link StrongPointOptions}.
|
|
6249
|
+
*/
|
|
6250
|
+
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;
|
|
6252
|
+
const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
|
|
6253
|
+
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION);
|
|
6254
|
+
const perimeter = buildAreaPerimeter(verts);
|
|
6255
|
+
const { strokes, fills, gaps } = placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition);
|
|
6256
|
+
const boundaryCoords = buildGappedLine(verts, gaps);
|
|
6257
|
+
const tics = buildStrongPointTics(perimeter, gaps, h);
|
|
6258
|
+
const features = [];
|
|
6259
|
+
if (boundaryCoords.length > 0) features.push({
|
|
6260
|
+
type: "Feature",
|
|
6261
|
+
properties: { part: "boundary" },
|
|
6262
|
+
geometry: {
|
|
6263
|
+
type: "MultiLineString",
|
|
6264
|
+
coordinates: boundaryCoords
|
|
6265
|
+
}
|
|
6266
|
+
});
|
|
6267
|
+
if (tics.length > 0) features.push({
|
|
6268
|
+
type: "Feature",
|
|
6269
|
+
properties: { part: "tics" },
|
|
6270
|
+
geometry: {
|
|
6271
|
+
type: "MultiLineString",
|
|
6272
|
+
coordinates: tics
|
|
6273
|
+
}
|
|
6274
|
+
});
|
|
6275
|
+
if (strokes.length > 0) features.push({
|
|
6276
|
+
type: "Feature",
|
|
6277
|
+
properties: { part: "echelon" },
|
|
6278
|
+
geometry: {
|
|
6279
|
+
type: "MultiLineString",
|
|
6280
|
+
coordinates: strokes
|
|
6281
|
+
}
|
|
6282
|
+
});
|
|
6283
|
+
if (fills.length > 0) features.push({
|
|
6284
|
+
type: "Feature",
|
|
6285
|
+
properties: {
|
|
6286
|
+
part: "echelon",
|
|
6287
|
+
fill: true
|
|
6288
|
+
},
|
|
6289
|
+
geometry: {
|
|
6290
|
+
type: "MultiPolygon",
|
|
6291
|
+
coordinates: fills.map((ring) => [ring])
|
|
6292
|
+
}
|
|
6293
|
+
});
|
|
6294
|
+
return {
|
|
6295
|
+
type: "FeatureCollection",
|
|
6296
|
+
features
|
|
6297
|
+
};
|
|
6298
|
+
}
|
|
6299
|
+
const STRONG_POINT = defineControlMeasure({
|
|
6300
|
+
metadata: STRONG_POINT_METADATA,
|
|
6301
|
+
generator: createStrongPoint,
|
|
6302
|
+
defaultOptions: DEFAULT_STRONG_POINT_OPTIONS,
|
|
6303
|
+
rule: area1DrawRule,
|
|
6304
|
+
previewSample: {
|
|
6305
|
+
controlPoints: [
|
|
6306
|
+
[-1, -.5],
|
|
6307
|
+
[1, -.5],
|
|
6308
|
+
[1, .5],
|
|
6309
|
+
[-1, .5]
|
|
6310
|
+
],
|
|
6311
|
+
options: {
|
|
6312
|
+
echelonSize: 200,
|
|
6313
|
+
smooth: true
|
|
6314
|
+
}
|
|
6315
|
+
}
|
|
6316
|
+
});
|
|
6317
|
+
//#endregion
|
|
5878
6318
|
//#region src/generators/cm15-maneuver-areas/supportByFire.ts
|
|
5879
6319
|
const DEFAULT_SUPPORT_BY_FIRE_OPTIONS = {
|
|
5880
6320
|
arrowheadWidthRatio: .3,
|
|
@@ -6203,11 +6643,13 @@ function rotate(vector, angle) {
|
|
|
6203
6643
|
const DEFINITIONS = {
|
|
6204
6644
|
boundary: BOUNDARY,
|
|
6205
6645
|
"battle-position": BATTLE_POSITION,
|
|
6646
|
+
"strong-point": STRONG_POINT,
|
|
6206
6647
|
ambush: AMBUSH,
|
|
6207
6648
|
"principal-direction-of-fire": PRINCIPAL_DIRECTION_OF_FIRE,
|
|
6208
6649
|
"final-protective-fire-left": FINAL_PROTECTIVE_FIRE_LEFT,
|
|
6209
6650
|
"final-protective-fire-right": FINAL_PROTECTIVE_FIRE_RIGHT,
|
|
6210
6651
|
"search-area": SEARCH_AREA,
|
|
6652
|
+
encirclement: ENCIRCLEMENT,
|
|
6211
6653
|
"main-attack": MAIN_ATTACK,
|
|
6212
6654
|
"supporting-attack": SUPPORTING_ATTACK,
|
|
6213
6655
|
"classic-arrow": CLASSIC_ARROW,
|
|
@@ -6462,4 +6904,4 @@ function assertNever(value) {
|
|
|
6462
6904
|
throw new Error(`Unhandled control measure kind: ${String(value)}`);
|
|
6463
6905
|
}
|
|
6464
6906
|
//#endregion
|
|
6465
|
-
export {
|
|
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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orbat-mapper/control-measures",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.4",
|
|
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",
|