@orbat-mapper/control-measures 0.8.0 → 0.10.0

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.
@@ -214,6 +214,9 @@ function vecNorm(v) {
214
214
  const m = vecMag(v);
215
215
  return m === 0 ? [0, 0] : [v[0] / m, v[1] / m];
216
216
  }
217
+ function midpoint(a, b) {
218
+ return vecScale(vecAdd(a, b), .5);
219
+ }
217
220
  function pointToPointDistance(p1, p2) {
218
221
  const dx = p2[0] - p1[0];
219
222
  const dy = p2[1] - p1[1];
@@ -586,6 +589,24 @@ const area1DrawRule = {
586
589
  }
587
590
  };
588
591
  //#endregion
592
+ //#region src/draw-rules/area27.ts
593
+ /**
594
+ * Area27 anchor draw rule for Mobile Defense.
595
+ *
596
+ * PT. 1 is the arrow tip and PT. 2 is the end of the straight-line portion.
597
+ * PT. 3 fixes the diameter and side of the 180-degree arc, constrained to the
598
+ * perpendicular axis through PT. 2 so the arc stays perpendicular to the
599
+ * action line when any of the three handles is edited.
600
+ */
601
+ const area27DrawRule = createMidpointPerpendicularDrawRule({
602
+ id: "area27:mobile-defense",
603
+ minimumUserPoints: 3,
604
+ minimumPreviewPoints: 2,
605
+ origin: "p2",
606
+ normal: "left",
607
+ defaultDistanceRatio: .6
608
+ });
609
+ //#endregion
589
610
  //#region src/draw-rules/area5.ts
590
611
  /**
591
612
  * Area5 — PT. 1/PT. 2 define the open diameter and PT. 3 defines the arrow end.
@@ -723,7 +744,7 @@ const SECTOR_ANGLE_SNAP_RADIANS = Math.PI / 36;
723
744
  function derive$9(points) {
724
745
  return clonePositions(points.slice(0, 3));
725
746
  }
726
- function guidePoints$1(points) {
747
+ function guidePoints$2(points) {
727
748
  return points.length < 3 ? clonePositions(points) : [];
728
749
  }
729
750
  /** Snap one radial Sector handle to a true-north angular increment. */
@@ -746,7 +767,7 @@ const sectorDrawRule = {
746
767
  minimumUserPoints: 3,
747
768
  canonicalPointCount: 3,
748
769
  showGuide: true,
749
- guidePoints: guidePoints$1,
770
+ guidePoints: guidePoints$2,
750
771
  constrainAngles,
751
772
  derive: derive$9,
752
773
  transform(event) {
@@ -934,6 +955,28 @@ const turnDrawRule = {
934
955
  }
935
956
  };
936
957
  //#endregion
958
+ //#region src/draw-rules/line11.ts
959
+ function firstFour(points) {
960
+ return clonePositions(points.slice(0, 4));
961
+ }
962
+ function guidePoints$1(points) {
963
+ if (points.length <= 2) return clonePositions(points);
964
+ if (points.length === 3) return [];
965
+ return clonePositions(points.slice(2, 4));
966
+ }
967
+ /** Line11 — four independent anchors defining the two sides of a bridge or gap. */
968
+ const bridgeOrGapDrawRule = {
969
+ id: "line11:bridge-or-gap",
970
+ minimumUserPoints: 4,
971
+ canonicalPointCount: 4,
972
+ showGuide: true,
973
+ guidePoints: guidePoints$1,
974
+ derive: firstFour,
975
+ transform(event) {
976
+ return firstFour(event.next);
977
+ }
978
+ };
979
+ //#endregion
937
980
  //#region src/draw-rules/line23.ts
938
981
  /**
939
982
  * Line23 anchor draw rule for the Clear mission task.
@@ -2823,7 +2866,7 @@ function pushEndLabelsAbove(out, verts, textForEnd, options = {}) {
2823
2866
  * as a complete FeatureCollection.
2824
2867
  */
2825
2868
  function fixedLabelLineFeatures(positions, options, textForEnd, hostileText, context) {
2826
- const { part, placement = "beyond", phaseLineName, includePrefix = true, smooth, smoothResolution, ...sizeOptions } = options;
2869
+ const { part, placement = "beyond", phaseLineName, includePrefix = true, lineStyle, smooth, smoothResolution, ...sizeOptions } = options;
2827
2870
  const verts = smoothLineVerts(positions.map((p) => project(p[0], p[1])), {
2828
2871
  smooth,
2829
2872
  smoothResolution
@@ -2831,7 +2874,10 @@ function fixedLabelLineFeatures(positions, options, textForEnd, hostileText, con
2831
2874
  const lineCoordinates = smooth ? verts.map((v) => unproject(v[0], v[1])) : positions;
2832
2875
  const features = [{
2833
2876
  type: "Feature",
2834
- properties: { part },
2877
+ properties: lineStyle === void 0 ? { part } : {
2878
+ part,
2879
+ style: lineStyle
2880
+ },
2835
2881
  geometry: {
2836
2882
  type: "LineString",
2837
2883
  coordinates: lineCoordinates
@@ -4361,6 +4407,273 @@ const AREA_DEFENSE = defineControlMeasure({
4361
4407
  }
4362
4408
  });
4363
4409
  //#endregion
4410
+ //#region src/generators/cm15-maneuver-areas/engagementArea.ts
4411
+ /** Default options for the Engagement area control measure. */
4412
+ const DEFAULT_ENGAGEMENT_AREA_OPTIONS = {
4413
+ smooth: false,
4414
+ smoothResolution: 16
4415
+ };
4416
+ const ENGAGEMENT_AREA_METADATA = {
4417
+ id: "engagement-area",
4418
+ name: "Engagement area",
4419
+ description: "An area where the commander intends to contain and destroy an enemy force with the massed effects of all available weapons and supporting systems.",
4420
+ entity: "Maneuver Areas",
4421
+ entityType: "Engagement Area",
4422
+ value: "151300",
4423
+ minCoordinates: 3,
4424
+ geometry: "area",
4425
+ geometryTypes: [
4426
+ "Polygon",
4427
+ "MultiLineString",
4428
+ "Point"
4429
+ ],
4430
+ paints: {
4431
+ stroke: true,
4432
+ fill: "none",
4433
+ text: true
4434
+ },
4435
+ drawRule: "Area1",
4436
+ capturesLabelSize: true,
4437
+ params: LABELED_AREA_PARAMS,
4438
+ textAmplifiers: AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
4439
+ };
4440
+ /**
4441
+ * Creates an Engagement area control measure: a closed area through
4442
+ * `positions` labeled at its center with the doctrinal `EA` designation plus
4443
+ * an optional Field T unique designation and Field N ENY marker.
4444
+ */
4445
+ function createEngagementArea(positions, options = {}, textAmplifiers = {}, context = {}) {
4446
+ return labeledAreaFeatures(positions, options, composeAreaLabelTexts(textAmplifiers, "EA"), context.amplifierPlacements, context);
4447
+ }
4448
+ const ENGAGEMENT_AREA = defineControlMeasure({
4449
+ metadata: ENGAGEMENT_AREA_METADATA,
4450
+ generator: createEngagementArea,
4451
+ defaultOptions: DEFAULT_ENGAGEMENT_AREA_OPTIONS,
4452
+ rule: area1DrawRule,
4453
+ previewSample: {
4454
+ controlPoints: [
4455
+ [-1, -.5],
4456
+ [1, -.5],
4457
+ [1, .5],
4458
+ [-1, .5]
4459
+ ],
4460
+ textAmplifiers: { T: "ALPHA" },
4461
+ options: { labelSize: 96 }
4462
+ }
4463
+ });
4464
+ //#endregion
4465
+ //#region src/internal/arrowhead.ts
4466
+ /**
4467
+ * Builds a closed, filled arrowhead triangle ring in unprojected coordinates.
4468
+ *
4469
+ * `tip` is the point of the arrow, `dir` the unit direction the arrow points,
4470
+ * `length` the distance from tip back to the base, and `width` the base width.
4471
+ * The returned ring is `[left, tip, right, left]`.
4472
+ */
4473
+ function createArrowHeadRing(tip, dir, length, width) {
4474
+ const perp = [-dir[1], dir[0]];
4475
+ const base = vecSub(tip, vecScale(dir, length));
4476
+ const left = vecAdd(base, vecScale(perp, width / 2));
4477
+ const right = vecSub(base, vecScale(perp, width / 2));
4478
+ return [
4479
+ unproject(left[0], left[1]),
4480
+ unproject(tip[0], tip[1]),
4481
+ unproject(right[0], right[1]),
4482
+ unproject(left[0], left[1])
4483
+ ];
4484
+ }
4485
+ //#endregion
4486
+ //#region src/internal/projected-arc.ts
4487
+ /** The arc point at `angle`, in projected meters. */
4488
+ function arcPointAt(center, startRadius, bulgeRadius, angle) {
4489
+ return vecAdd(center, vecAdd(vecScale(startRadius, Math.cos(angle)), vecScale(bulgeRadius, Math.sin(angle))));
4490
+ }
4491
+ /**
4492
+ * Samples the arc from `start` to `end` radians as unprojected positions.
4493
+ * `resolution` is the segment count a full half-turn would use, so the density
4494
+ * of a partial arc stays proportional to how much of the circle it covers.
4495
+ */
4496
+ function sampleProjectedArc(center, startRadius, bulgeRadius, start, end, resolution) {
4497
+ const segments = Math.max(2, Math.ceil(resolution * Math.abs(end - start) / Math.PI));
4498
+ return Array.from({ length: segments + 1 }, (_, index) => {
4499
+ const point = arcPointAt(center, startRadius, bulgeRadius, start + (end - start) * index / segments);
4500
+ return unproject(point[0], point[1]);
4501
+ });
4502
+ }
4503
+ //#endregion
4504
+ //#region src/generators/cm15-maneuver-areas/mobile-defense.ts
4505
+ const DEFAULT_MOBILE_DEFENSE_OPTIONS = { labelPadding: 0 };
4506
+ const MOBILE_DEFENSE_METADATA = {
4507
+ id: "mobile-defense",
4508
+ name: "Mobile Defense",
4509
+ description: "A defensive maneuver that uses terrain, fires, and maneuver to defeat an attacking enemy force.",
4510
+ entity: "Maneuver Areas",
4511
+ entityType: "Mobile Defense",
4512
+ value: "152800",
4513
+ minCoordinates: 3,
4514
+ maxCoordinates: 3,
4515
+ geometry: "area",
4516
+ geometryTypes: [
4517
+ "MultiLineString",
4518
+ "MultiPolygon",
4519
+ "Point"
4520
+ ],
4521
+ paints: {
4522
+ stroke: true,
4523
+ fill: "fixed",
4524
+ text: true
4525
+ },
4526
+ drawRule: "Area27",
4527
+ capturesLabelSize: true,
4528
+ params: [{
4529
+ key: "labelPadding",
4530
+ presentationTier: "advanced",
4531
+ label: "Label padding",
4532
+ description: "Extra clearance around MD, as a ratio of label height",
4533
+ type: "number",
4534
+ min: 0,
4535
+ max: 2,
4536
+ step: .05
4537
+ }]
4538
+ };
4539
+ const ARC_RESOLUTION = 48;
4540
+ const STRAIGHT_ARROW_LENGTH_RATIO = .12;
4541
+ const STRAIGHT_ARROW_WIDTH_RATIO = .12;
4542
+ const TOP_STRAIGHT_BARB_POSITION_RATIO = .25;
4543
+ const FIRST_BARB_POSITION_RATIO = .29;
4544
+ const FIRST_BARB_CLEARANCE_RATIO = .01;
4545
+ const BARB_WIDTH_RATIO = .45;
4546
+ const BARB_LENGTH_RATIO = .42;
4547
+ const ARC_BARB_BASE_SEGMENTS = 8;
4548
+ function createBarb(center, along, outward, width, length) {
4549
+ const left = vecAdd(center, vecScale(along, width / 2));
4550
+ const right = vecSub(center, vecScale(along, width / 2));
4551
+ const tip = vecAdd(center, vecScale(outward, length));
4552
+ return [
4553
+ toPosition(left),
4554
+ toPosition(tip),
4555
+ toPosition(right),
4556
+ toPosition(left)
4557
+ ];
4558
+ }
4559
+ function createArcBarb(center, startRadius, bulgeRadius, angle, halfWidthAngle, length) {
4560
+ const point = arcPointAt(center, startRadius, bulgeRadius, angle);
4561
+ const outward = vecNorm(vecSub(point, center));
4562
+ const baseStart = arcPointAt(center, startRadius, bulgeRadius, angle - halfWidthAngle);
4563
+ const baseEnd = arcPointAt(center, startRadius, bulgeRadius, angle + halfWidthAngle);
4564
+ const sagitta = vecMag(startRadius) * (1 - Math.cos(halfWidthAngle));
4565
+ const tip = vecAdd(point, vecScale(outward, Math.max(0, length - sagitta)));
4566
+ const curvedBase = Array.from({ length: 9 }, (_, index) => toPosition(arcPointAt(center, startRadius, bulgeRadius, angle - halfWidthAngle + 2 * halfWidthAngle * index / ARC_BARB_BASE_SEGMENTS)));
4567
+ return [
4568
+ toPosition(baseStart),
4569
+ toPosition(tip),
4570
+ toPosition(baseEnd),
4571
+ ...curvedBase.slice(1, -1).reverse(),
4572
+ toPosition(baseStart)
4573
+ ];
4574
+ }
4575
+ function createMobileDefense(coordinates, options = {}, _textAmplifiers = {}, context = {}) {
4576
+ const p1 = project(coordinates[0][0], coordinates[0][1]);
4577
+ const p2 = project(coordinates[1][0], coordinates[1][1]);
4578
+ const p3 = project(coordinates[2][0], coordinates[2][1]);
4579
+ const lineVector = vecSub(p2, p1);
4580
+ const diameterVector = vecSub(p3, p2);
4581
+ const lineLength = vecMag(lineVector);
4582
+ const diameter = vecMag(diameterVector);
4583
+ if (lineLength < 1e-6 || diameter < 1e-6) return {
4584
+ type: "FeatureCollection",
4585
+ features: []
4586
+ };
4587
+ const lineDirection = vecNorm(lineVector);
4588
+ const lineNormal = [-lineDirection[1], lineDirection[0]];
4589
+ const center = vecAdd(p2, vecScale(diameterVector, .5));
4590
+ const startRadius = vecScale(diameterVector, .5);
4591
+ const bulgeRadius = [startRadius[1], -startRadius[0]];
4592
+ const arrowLength = lineLength * STRAIGHT_ARROW_LENGTH_RATIO;
4593
+ const arrowWidth = lineLength * STRAIGHT_ARROW_WIDTH_RATIO;
4594
+ const topStart = vecAdd(vecAdd(p1, diameterVector), vecScale(lineDirection, arrowLength));
4595
+ const labelPoint = arcPointAt(center, startRadius, bulgeRadius, Math.PI / 2);
4596
+ const metrics = resolveTextMetrics("MD", options, context);
4597
+ const padding = Math.max(0, options.labelPadding ?? DEFAULT_MOBILE_DEFENSE_OPTIONS.labelPadding);
4598
+ const halfGap = Math.min(Math.PI * diameter / 4 - EPSILON, metrics.height / 2 + metrics.height * padding);
4599
+ const gapAngle = Math.max(0, halfGap / (diameter / 2));
4600
+ const upperArc = sampleProjectedArc(center, startRadius, bulgeRadius, 0, Math.PI / 2 - gapAngle, ARC_RESOLUTION);
4601
+ const lowerArc = sampleProjectedArc(center, startRadius, bulgeRadius, Math.PI / 2 + gapAngle, Math.PI, ARC_RESOLUTION);
4602
+ const boundary = [
4603
+ [toPosition(topStart), toPosition(p3)],
4604
+ [toPosition(p1), toPosition(p2)],
4605
+ upperArc,
4606
+ lowerArc,
4607
+ createArrowHeadRing(p1, vecScale(lineDirection, -1), arrowLength, arrowWidth).slice(0, -1)
4608
+ ];
4609
+ const firstBarbClearance = lineLength * FIRST_BARB_CLEARANCE_RATIO;
4610
+ const availableStraightLength = lineLength - arrowLength - firstBarbClearance;
4611
+ const barbScale = Math.min(diameter, availableStraightLength / BARB_WIDTH_RATIO);
4612
+ const barbWidth = barbScale * BARB_WIDTH_RATIO;
4613
+ const barbLength = barbScale * BARB_LENGTH_RATIO;
4614
+ const firstBarbDistance = Math.min(lineLength - barbWidth / 2, Math.max(lineLength * FIRST_BARB_POSITION_RATIO, arrowLength + barbWidth / 2 + firstBarbClearance));
4615
+ const arcBarbHalfWidthAngle = Math.asin(Math.min(1, barbWidth / diameter));
4616
+ const barbs = [
4617
+ [createBarb(vecAdd(topStart, vecScale(vecSub(p3, topStart), TOP_STRAIGHT_BARB_POSITION_RATIO)), lineDirection, lineNormal, barbWidth, barbLength)],
4618
+ [createBarb(vecAdd(p1, vecScale(lineDirection, firstBarbDistance)), lineDirection, vecScale(lineNormal, -1), barbWidth, barbLength)],
4619
+ [createArcBarb(center, startRadius, bulgeRadius, arcBarbHalfWidthAngle, arcBarbHalfWidthAngle, barbLength)],
4620
+ [createArcBarb(center, startRadius, bulgeRadius, Math.PI - arcBarbHalfWidthAngle, arcBarbHalfWidthAngle, barbLength)]
4621
+ ];
4622
+ return {
4623
+ type: "FeatureCollection",
4624
+ features: [
4625
+ {
4626
+ type: "Feature",
4627
+ properties: { part: "mobile-defense" },
4628
+ geometry: {
4629
+ type: "MultiLineString",
4630
+ coordinates: boundary
4631
+ }
4632
+ },
4633
+ {
4634
+ type: "Feature",
4635
+ properties: {
4636
+ part: "barbs",
4637
+ fill: true,
4638
+ style: SOLID_ACCENT_FILL
4639
+ },
4640
+ geometry: {
4641
+ type: "MultiPolygon",
4642
+ coordinates: barbs
4643
+ }
4644
+ },
4645
+ {
4646
+ type: "Feature",
4647
+ properties: {
4648
+ part: "label",
4649
+ labelPlacement: false,
4650
+ text: "MD",
4651
+ rotation: labelRotationAlong(lineDirection),
4652
+ ...labelSizeProps(options)
4653
+ },
4654
+ geometry: {
4655
+ type: "Point",
4656
+ coordinates: toPosition(labelPoint)
4657
+ }
4658
+ }
4659
+ ]
4660
+ };
4661
+ }
4662
+ const MOBILE_DEFENSE = defineControlMeasure({
4663
+ metadata: MOBILE_DEFENSE_METADATA,
4664
+ generator: createMobileDefense,
4665
+ defaultOptions: DEFAULT_MOBILE_DEFENSE_OPTIONS,
4666
+ rule: area27DrawRule,
4667
+ previewSample: {
4668
+ controlPoints: [
4669
+ [-1, -.25],
4670
+ [.65, -.25],
4671
+ [.65, .35]
4672
+ ],
4673
+ options: { labelSize: 90 }
4674
+ }
4675
+ });
4676
+ //#endregion
4364
4677
  //#region src/generators/cm12-command-control-areas/area-of-operations.ts
4365
4678
  /** Default options for the Area of operations control measure. */
4366
4679
  const DEFAULT_AREA_OF_OPERATIONS_OPTIONS = {
@@ -5684,33 +5997,36 @@ const BATTLE_POSITION_METADATA = {
5684
5997
  textAmplifiers: AREA_UNIT_DESIGNATION_HOSTILE_AMPLIFIERS
5685
5998
  };
5686
5999
  /**
5687
- * Creates a Battle Position control measure: a closed area through `positions`
5688
- * carrying a single echelon (Field B) glyph straddling its boundary — anchored
5689
- * to the bottom edge by default, slidable via `echelonPosition`.
6000
+ * Shared body of Battle Position and its dashed sibling, Battle Position
6001
+ * Prepared (P) but not Occupied: the closed (optionally smoothed) ring, the
6002
+ * Field B echelon glyph straddling the boundary with its masking gap, the
6003
+ * centered Field T designation and Field N "ENY" markers, and the gapped
6004
+ * boundary that merges every mask (ADR-0023). The two measures differ only in
6005
+ * the `namePrefix` put in front of Field T and the intrinsic `boundaryStyle`
6006
+ * the boundary feature carries, so both are parameters here — mirroring
6007
+ * `echelon-labeled-area.ts`'s role for Base Camp / Guerrilla Base.
5690
6008
  *
5691
- * @param positions - Area anchor points (≥3); the input contract is enforced at
5692
- * the render seam (ADR-0014).
5693
- * @param options - {@link BattlePositionOptions}.
5694
- * @param textAmplifiers - Field T (designation, centered in the area) plus an
5695
- * optional Field N "ENY" hostile marker at the west/east edges (no H/W rows —
5696
- * this measure's template pairs the designation with the Field B glyph, not
5697
- * the plain L-15 boundary template).
6009
+ * Strong Point deliberately keeps its own copy: its perimeter tics are new
6010
+ * geometry, not a parameter swap.
5698
6011
  */
5699
- function createBattlePosition(positions, options = {}, textAmplifiers = {}, context = {}) {
6012
+ function battlePositionAreaFeatures(positions, options, textAmplifiers, context, variant = {}) {
5700
6013
  const { echelon = DEFAULT_BATTLE_POSITION_OPTIONS.echelon, echelonSize = DEFAULT_BATTLE_POSITION_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPadding, echelonPosition = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPosition, echelonAnchor, metersPerPixel, smooth = DEFAULT_BATTLE_POSITION_OPTIONS.smooth, smoothResolution = DEFAULT_BATTLE_POSITION_OPTIONS.smoothResolution } = options;
5701
6014
  const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
5702
6015
  const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$6);
5703
6016
  const { strokes, fills, gaps } = placeEchelon(buildAreaPerimeter(verts), h, echelon, echelonPadding, echelonPosition, echelonAnchor);
5704
6017
  const labelFeatures = [];
5705
6018
  const enyGaps = pushAreaLabels(labelFeatures, verts, {
5706
- name: textAmplifiers.T,
6019
+ name: variant.namePrefix ? composeAreaName(variant.namePrefix, textAmplifiers.T) : textAmplifiers.T,
5707
6020
  hostile: textAmplifiers.N
5708
6021
  }, options, context.amplifierPlacements, void 0, context);
5709
6022
  const boundaryCoords = buildGappedLine(verts, [...gaps, ...enyGaps]);
5710
6023
  const features = [];
5711
6024
  if (boundaryCoords.length > 0) features.push({
5712
6025
  type: "Feature",
5713
- properties: { part: "boundary" },
6026
+ properties: variant.boundaryStyle ? {
6027
+ part: "boundary",
6028
+ style: variant.boundaryStyle
6029
+ } : { part: "boundary" },
5714
6030
  geometry: {
5715
6031
  type: "MultiLineString",
5716
6032
  coordinates: boundaryCoords
@@ -5743,6 +6059,22 @@ function createBattlePosition(positions, options = {}, textAmplifiers = {}, cont
5743
6059
  };
5744
6060
  }
5745
6061
  /**
6062
+ * Creates a Battle Position control measure: a closed area through `positions`
6063
+ * carrying a single echelon (Field B) glyph straddling its boundary — anchored
6064
+ * to the bottom edge by default, slidable via `echelonPosition`.
6065
+ *
6066
+ * @param positions - Area anchor points (≥3); the input contract is enforced at
6067
+ * the render seam (ADR-0014).
6068
+ * @param options - {@link BattlePositionOptions}.
6069
+ * @param textAmplifiers - Field T (designation, centered in the area) plus an
6070
+ * optional Field N "ENY" hostile marker at the west/east edges (no H/W rows —
6071
+ * this measure's template pairs the designation with the Field B glyph, not
6072
+ * the plain L-15 boundary template).
6073
+ */
6074
+ function createBattlePosition(positions, options = {}, textAmplifiers = {}, context = {}) {
6075
+ return battlePositionAreaFeatures(positions, options, textAmplifiers, context);
6076
+ }
6077
+ /**
5746
6078
  * `freezeOrientation` hook (ADR-0023) shared by Battle Position and Strong
5747
6079
  * Point: pins the echelon anchor to its current bottom-edge fraction so a
5748
6080
  * subsequent rigid rotation of `controlPoints` (e.g. the transform box's
@@ -5780,44 +6112,76 @@ const BATTLE_POSITION = defineControlMeasure({
5780
6112
  }
5781
6113
  });
5782
6114
  //#endregion
5783
- //#region src/internal/arrowhead.ts
6115
+ //#region src/generators/cm15-maneuver-areas/battlePositionPrepared.ts
6116
+ /** Intrinsic dash pattern of the prepared-but-not-occupied boundary. */
6117
+ const BATTLE_POSITION_PREPARED_DASH = [6, 4];
6118
+ /** Default options for the Battle Position Prepared control measure. */
6119
+ const DEFAULT_BATTLE_POSITION_PREPARED_OPTIONS = { ...DEFAULT_BATTLE_POSITION_OPTIONS };
6120
+ const BATTLE_POSITION_PREPARED_METADATA = {
6121
+ id: "battle-position-prepared",
6122
+ name: "Battle Position Prepared (P) but not Occupied",
6123
+ description: "A battle position that has been prepared for occupation but is not yet occupied, drawn with a dashed boundary and a (P)-prefixed designation.",
6124
+ entity: "Maneuver Areas",
6125
+ entityType: "Battle Position",
6126
+ entitySubtype: "Battle Position Prepared (P) but not Occupied",
6127
+ value: "151202",
6128
+ minCoordinates: 3,
6129
+ geometry: "area",
6130
+ geometryTypes: [
6131
+ "MultiLineString",
6132
+ "MultiPolygon",
6133
+ "Point"
6134
+ ],
6135
+ paints: {
6136
+ stroke: true,
6137
+ fill: "fixed",
6138
+ text: true
6139
+ },
6140
+ drawRule: "Area1",
6141
+ capturesLabelSize: true,
6142
+ params: ECHELON_AREA_PARAMS,
6143
+ textAmplifiers: AREA_UNIT_DESIGNATION_HOSTILE_AMPLIFIERS
6144
+ };
5784
6145
  /**
5785
- * Builds a closed, filled arrowhead triangle ring in unprojected coordinates.
6146
+ * Creates a Battle Position Prepared (P) but not Occupied control measure: a
6147
+ * Battle Position whose boundary carries an intrinsic dash and whose centered
6148
+ * designation is prefixed with `(P)`. Delegates to
6149
+ * {@link battlePositionAreaFeatures}, the shared Battle Position body.
5786
6150
  *
5787
- * `tip` is the point of the arrow, `dir` the unit direction the arrow points,
5788
- * `length` the distance from tip back to the base, and `width` the base width.
5789
- * The returned ring is `[left, tip, right, left]`.
5790
- */
5791
- function createArrowHeadRing(tip, dir, length, width) {
5792
- const perp = [-dir[1], dir[0]];
5793
- const base = vecSub(tip, vecScale(dir, length));
5794
- const left = vecAdd(base, vecScale(perp, width / 2));
5795
- const right = vecSub(base, vecScale(perp, width / 2));
5796
- return [
5797
- unproject(left[0], left[1]),
5798
- unproject(tip[0], tip[1]),
5799
- unproject(right[0], right[1]),
5800
- unproject(left[0], left[1])
5801
- ];
5802
- }
5803
- //#endregion
5804
- //#region src/internal/projected-arc.ts
5805
- /** The arc point at `angle`, in projected meters. */
5806
- function arcPointAt(center, startRadius, bulgeRadius, angle) {
5807
- return vecAdd(center, vecAdd(vecScale(startRadius, Math.cos(angle)), vecScale(bulgeRadius, Math.sin(angle))));
5808
- }
5809
- /**
5810
- * Samples the arc from `start` to `end` radians as unprojected positions.
5811
- * `resolution` is the segment count a full half-turn would use, so the density
5812
- * of a partial arc stays proportional to how much of the circle it covers.
6151
+ * @param positions - Area anchor points (≥3); the input contract is enforced at
6152
+ * the render seam (ADR-0014).
6153
+ * @param options - {@link BattlePositionPreparedOptions}.
6154
+ * @param textAmplifiers - Field T (designation, centered in the area and
6155
+ * prefixed with `(P)`) plus an optional Field N "ENY" hostile marker at the
6156
+ * west/east edges.
5813
6157
  */
5814
- function sampleProjectedArc(center, startRadius, bulgeRadius, start, end, resolution) {
5815
- const segments = Math.max(2, Math.ceil(resolution * Math.abs(end - start) / Math.PI));
5816
- return Array.from({ length: segments + 1 }, (_, index) => {
5817
- const point = arcPointAt(center, startRadius, bulgeRadius, start + (end - start) * index / segments);
5818
- return unproject(point[0], point[1]);
6158
+ function createBattlePositionPrepared(positions, options = {}, textAmplifiers = {}, context = {}) {
6159
+ return battlePositionAreaFeatures(positions, options, textAmplifiers, context, {
6160
+ namePrefix: "(P)",
6161
+ boundaryStyle: { strokeDash: [...BATTLE_POSITION_PREPARED_DASH] }
5819
6162
  });
5820
6163
  }
6164
+ const BATTLE_POSITION_PREPARED = defineControlMeasure({
6165
+ metadata: BATTLE_POSITION_PREPARED_METADATA,
6166
+ generator: createBattlePositionPrepared,
6167
+ defaultOptions: DEFAULT_BATTLE_POSITION_PREPARED_OPTIONS,
6168
+ rule: area1DrawRule,
6169
+ freezeOrientation: freezeEchelonAnchor,
6170
+ previewSample: {
6171
+ controlPoints: [
6172
+ [-1, -.5],
6173
+ [1, -.5],
6174
+ [1, .5],
6175
+ [-1, .5]
6176
+ ],
6177
+ textAmplifiers: { T: "MARS" },
6178
+ options: {
6179
+ echelonSize: 200,
6180
+ smooth: true,
6181
+ labelSize: 96
6182
+ }
6183
+ }
6184
+ });
5821
6185
  //#endregion
5822
6186
  //#region src/generators/cm15-maneuver-areas/contain.ts
5823
6187
  const DEFAULT_CONTAIN_OPTIONS = {
@@ -6063,6 +6427,167 @@ const BLOCK = defineControlMeasure({
6063
6427
  previewSample: { controlPoints: [[0, .4], [0, -.4]] }
6064
6428
  });
6065
6429
  //#endregion
6430
+ //#region src/generators/cm27-protection-areas/bridge-or-gap.ts
6431
+ const FLARE_COMPONENT_RATIO = .25;
6432
+ const DEFAULT_BRIDGE_OR_GAP_OPTIONS = {};
6433
+ const BRIDGE_OR_GAP_METADATA = {
6434
+ id: "bridge-or-gap",
6435
+ name: "Bridge or Gap",
6436
+ description: "An area within a minefield or obstacle belt, free of live mines or obstacles, whose width and direction allow a friendly force to pass through in tactical formation.",
6437
+ entity: "Protection Areas",
6438
+ entityType: "Bridge or Gap",
6439
+ value: "271100",
6440
+ minCoordinates: 4,
6441
+ maxCoordinates: 4,
6442
+ geometry: "line",
6443
+ geometryTypes: ["LineString", "Point"],
6444
+ paints: {
6445
+ stroke: true,
6446
+ fill: "none",
6447
+ text: true
6448
+ },
6449
+ drawRule: "Line11",
6450
+ capturesLabelSize: true,
6451
+ textAmplifiers: [
6452
+ {
6453
+ key: "T",
6454
+ label: "Unique designation",
6455
+ description: "Field T — the bridge or gap's unique designation.",
6456
+ maxLength: 21
6457
+ },
6458
+ {
6459
+ key: "W",
6460
+ label: "Effective from (DTG)",
6461
+ description: "Field W — date-time group the bridge or gap becomes effective.",
6462
+ placeholder: "051030Z",
6463
+ maxLength: 16
6464
+ },
6465
+ {
6466
+ key: "W1",
6467
+ label: "Effective to (DTG)",
6468
+ description: "Field W1 — date-time group the bridge or gap ceases to be effective.",
6469
+ placeholder: "051600Z",
6470
+ maxLength: 16
6471
+ }
6472
+ ]
6473
+ };
6474
+ /**
6475
+ * One side: its two anchors, plus a 45-degree flare at each end splaying away
6476
+ * from the gap. `outward` only picks which way the side's own perpendicular
6477
+ * points -- the flare stays square to this side, so both ends flare by the
6478
+ * same quarter-length in each axis.
6479
+ */
6480
+ function sideCoordinates(start, end, outward) {
6481
+ const side = vecSub(end, start);
6482
+ const length = vecMag(side);
6483
+ if (length < 1e-6) return void 0;
6484
+ const along = vecScale(side, 1 / length);
6485
+ const normal = [-along[1], along[0]];
6486
+ const component = length * FLARE_COMPONENT_RATIO;
6487
+ const flareOut = vecScale(normal, vecDot(normal, outward) >= 0 ? component : -component);
6488
+ return {
6489
+ coordinates: [
6490
+ vecAdd(vecAdd(start, vecScale(along, -component)), flareOut),
6491
+ start,
6492
+ end,
6493
+ vecAdd(vecAdd(end, vecScale(along, component)), flareOut)
6494
+ ].map((point) => unproject(point[0], point[1])),
6495
+ along,
6496
+ length
6497
+ };
6498
+ }
6499
+ function addLabels(features, frame, options, textAmplifiers) {
6500
+ const { points, side1, side2, across, center } = frame;
6501
+ const averageDirection = vecAdd(side1.along, side2.along);
6502
+ const along = vecMag(averageDirection) >= 1e-6 ? vecNorm(averageDirection) : side1.along;
6503
+ const sizeProps = labelSizeProps(options);
6504
+ const labels = [];
6505
+ if (textAmplifiers.T) pushLabel(labels, center, textAmplifiers.T, labelRotationAlong(along), sizeProps, void 0, "T");
6506
+ const row = [];
6507
+ if (textAmplifiers.W) row.push([
6508
+ "W",
6509
+ textAmplifiers.W,
6510
+ -1,
6511
+ "end"
6512
+ ]);
6513
+ if (textAmplifiers.W1) row.push([
6514
+ "W1",
6515
+ textAmplifiers.W1,
6516
+ 1,
6517
+ "start"
6518
+ ]);
6519
+ if (row.length > 0) {
6520
+ const averageLength = (side1.length + side2.length) / 2;
6521
+ const windowRotation = labelRotationAlong(across);
6522
+ const rowCenter = vecAdd(midpoint(points[1], points[3]), vecScale(along, averageLength * FLARE_COMPONENT_RATIO + resolveLabelOffsetMeters(options, 1)));
6523
+ const separatorOffset = resolveLabelOffsetMeters(options, .15);
6524
+ const paired = row.length === 2;
6525
+ for (const [field, text, sign, anchor] of row) pushLabel(labels, paired ? vecAdd(rowCenter, vecScale(across, sign * separatorOffset)) : rowCenter, text, windowRotation, sizeProps, paired ? anchor : void 0, field);
6526
+ }
6527
+ features.push(...labels);
6528
+ }
6529
+ function createBridgeOrGap(coordinates, options = {}, textAmplifiers = {}) {
6530
+ const points = coordinates.slice(0, 4).map((position) => project(position[0], position[1]));
6531
+ const [p1, p2, p3, p4] = points;
6532
+ const mid12 = midpoint(p1, p2);
6533
+ const mid34 = midpoint(p3, p4);
6534
+ const betweenMidpoints = vecSub(mid12, mid34);
6535
+ const outward1 = vecMag(betweenMidpoints) >= 1e-6 ? vecNorm(betweenMidpoints) : vecNorm([p1[1] - p2[1], p2[0] - p1[0]]);
6536
+ const outward2 = vecScale(outward1, -1);
6537
+ const side1 = sideCoordinates(p1, p2, outward1);
6538
+ const side2 = sideCoordinates(p3, p4, outward2);
6539
+ if (!side1 || !side2) return {
6540
+ type: "FeatureCollection",
6541
+ features: []
6542
+ };
6543
+ const features = [{
6544
+ type: "Feature",
6545
+ properties: { part: "side-1" },
6546
+ geometry: {
6547
+ type: "LineString",
6548
+ coordinates: side1.coordinates
6549
+ }
6550
+ }, {
6551
+ type: "Feature",
6552
+ properties: { part: "side-2" },
6553
+ geometry: {
6554
+ type: "LineString",
6555
+ coordinates: side2.coordinates
6556
+ }
6557
+ }];
6558
+ addLabels(features, {
6559
+ points,
6560
+ side1,
6561
+ side2,
6562
+ across: outward2,
6563
+ center: midpoint(mid12, mid34)
6564
+ }, options, textAmplifiers);
6565
+ return {
6566
+ type: "FeatureCollection",
6567
+ features
6568
+ };
6569
+ }
6570
+ const BRIDGE_OR_GAP = defineControlMeasure({
6571
+ metadata: BRIDGE_OR_GAP_METADATA,
6572
+ generator: createBridgeOrGap,
6573
+ defaultOptions: DEFAULT_BRIDGE_OR_GAP_OPTIONS,
6574
+ rule: bridgeOrGapDrawRule,
6575
+ previewSample: {
6576
+ controlPoints: [
6577
+ [-.35, -.7],
6578
+ [-.35, .45],
6579
+ [.35, -.7],
6580
+ [.35, .45]
6581
+ ],
6582
+ textAmplifiers: {
6583
+ T: "T",
6584
+ W: "W",
6585
+ W1: "W1"
6586
+ },
6587
+ options: { labelSize: 55 }
6588
+ }
6589
+ });
6590
+ //#endregion
6066
6591
  //#region src/generators/cm99-generic-arrows/blockArrow.ts
6067
6592
  const DEFAULT_SMOOTH_RESOLUTION$5 = 12;
6068
6593
  const MIN_SMOOTH_RESOLUTION$1 = 2;
@@ -11227,6 +11752,155 @@ const BRIDGEHEAD_LINE = defineControlMeasure({
11227
11752
  }
11228
11753
  });
11229
11754
  //#endregion
11755
+ //#region src/generators/cm14-maneuver-lines/params.ts
11756
+ /** Phase-line naming controls shared by the fixed-label Line1 maneuver lines. */
11757
+ function phaseLineNameParams(placeholder) {
11758
+ return [
11759
+ {
11760
+ key: "phaseLineName",
11761
+ presentationTier: "primary",
11762
+ semanticRole: "doctrinal",
11763
+ label: "Phase line name",
11764
+ description: "Name the line as a phase line — rendered \"PL <name>\" beyond each end of the line.",
11765
+ type: "text",
11766
+ placeholder,
11767
+ maxLength: 21,
11768
+ field: "T"
11769
+ },
11770
+ {
11771
+ key: "includePrefix",
11772
+ label: "Show PL prefix",
11773
+ description: "Prefix the phase line name with \"PL\" (or show the bare name).",
11774
+ type: "boolean",
11775
+ visibleWhen: (opts) => Boolean(String(opts.phaseLineName ?? "").trim())
11776
+ },
11777
+ LINE_LABEL_PADDING_PARAM
11778
+ ];
11779
+ }
11780
+ //#endregion
11781
+ //#region src/generators/cm14-maneuver-lines/final-coordination-line.ts
11782
+ /** Default options for the Final coordination line control measure. */
11783
+ const DEFAULT_FINAL_COORDINATION_LINE_OPTIONS = {
11784
+ includePrefix: true,
11785
+ smooth: false,
11786
+ smoothResolution: 12
11787
+ };
11788
+ const FINAL_COORDINATION_LINE_METADATA = {
11789
+ id: "final-coordination-line",
11790
+ name: "Final coordination line",
11791
+ description: "A line close to the enemy position used to coordinate the lifting or shifting of supporting fires with the final deployment of maneuver elements.",
11792
+ entity: "Maneuver Lines",
11793
+ entityType: "Final Coordination Line",
11794
+ value: "140700",
11795
+ minCoordinates: 2,
11796
+ geometry: "line",
11797
+ geometryTypes: ["LineString", "Point"],
11798
+ paints: {
11799
+ stroke: true,
11800
+ fill: "none",
11801
+ text: true
11802
+ },
11803
+ drawRule: "Line1",
11804
+ capturesLabelSize: true,
11805
+ params: [...SMOOTH_LINE_PARAMS, ...phaseLineNameParams("OPAL")],
11806
+ textAmplifiers: [HOSTILE_LINE_AMPLIFIER]
11807
+ };
11808
+ /** Creates a Final coordination line labeled "FCL" above both ends. */
11809
+ function createFinalCoordinationLine(positions, options = {}, textAmplifiers = {}, context = {}) {
11810
+ return fixedLabelLineFeatures(positions, {
11811
+ part: "final-coordination-line",
11812
+ placement: "above",
11813
+ ...options
11814
+ }, "FCL", textAmplifiers.N, context);
11815
+ }
11816
+ const FINAL_COORDINATION_LINE = defineControlMeasure({
11817
+ metadata: FINAL_COORDINATION_LINE_METADATA,
11818
+ generator: createFinalCoordinationLine,
11819
+ defaultOptions: DEFAULT_FINAL_COORDINATION_LINE_OPTIONS,
11820
+ rule: line1DrawRule,
11821
+ previewSample: {
11822
+ controlPoints: [[-1, 0], [1, 0]],
11823
+ options: { labelSize: 96 }
11824
+ }
11825
+ });
11826
+ //#endregion
11827
+ //#region src/generators/cm14-maneuver-lines/airhead-line.ts
11828
+ /** Clearance between the boundary's southernmost point and the caption, as a
11829
+ * ratio of the caption's height. */
11830
+ const CAPTION_CLEARANCE_RATIO = .9;
11831
+ /** Default options for the Airhead line control measure. */
11832
+ const DEFAULT_AIRHEAD_LINE_OPTIONS = {
11833
+ smooth: false,
11834
+ smoothResolution: 16
11835
+ };
11836
+ const AIRHEAD_LINE_METADATA = {
11837
+ id: "airhead-line",
11838
+ name: "Airhead line",
11839
+ description: "A line denoting the limits of the objective area for an airborne assault.",
11840
+ entity: "Maneuver Lines",
11841
+ entityType: "Airhead Line",
11842
+ value: "141300",
11843
+ minCoordinates: 3,
11844
+ geometry: "area",
11845
+ geometryTypes: [
11846
+ "Polygon",
11847
+ "MultiLineString",
11848
+ "Point"
11849
+ ],
11850
+ paints: {
11851
+ stroke: true,
11852
+ fill: "none",
11853
+ text: true
11854
+ },
11855
+ drawRule: "Area1",
11856
+ capturesLabelSize: true,
11857
+ params: LABELED_AREA_PARAMS,
11858
+ textAmplifiers: AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
11859
+ };
11860
+ /** Creates an Airhead line: a closed boundary captioned "AIRHEAD LINE" below. */
11861
+ function createAirheadLine(positions, options = {}, textAmplifiers = {}, context = {}) {
11862
+ const { smooth = false, smoothResolution = 16, ...labelOptions } = options;
11863
+ const verts = buildClosedRing(positions, smooth, smoothResolution, 16);
11864
+ const labelFeatures = [];
11865
+ const gaps = pushAreaLabels(labelFeatures, verts, composeAreaLabelTexts(textAmplifiers, ""), labelOptions, context.amplifierPlacements, void 0, context);
11866
+ const [cx] = ringCentroid(verts);
11867
+ const minY = Math.min(...verts.map((vertex) => vertex[1]));
11868
+ const clearance = resolveLabelOffsetMeters(labelOptions, CAPTION_CLEARANCE_RATIO + Math.max(0, labelOptions.labelPadding ?? 0));
11869
+ labelFeatures.push({
11870
+ type: "Feature",
11871
+ properties: {
11872
+ part: "label",
11873
+ labelPlacementKey: "label:airhead-line",
11874
+ text: "AIRHEAD LINE",
11875
+ rotation: HORIZONTAL_LABEL_ROTATION,
11876
+ ...labelSizeProps(labelOptions)
11877
+ },
11878
+ geometry: {
11879
+ type: "Point",
11880
+ coordinates: unproject(cx, minY - clearance)
11881
+ }
11882
+ });
11883
+ return {
11884
+ type: "FeatureCollection",
11885
+ features: [boundaryFeature(verts, gaps), ...labelFeatures]
11886
+ };
11887
+ }
11888
+ const AIRHEAD_LINE = defineControlMeasure({
11889
+ metadata: AIRHEAD_LINE_METADATA,
11890
+ generator: createAirheadLine,
11891
+ defaultOptions: DEFAULT_AIRHEAD_LINE_OPTIONS,
11892
+ rule: area1DrawRule,
11893
+ previewSample: {
11894
+ controlPoints: [
11895
+ [-1, -.4],
11896
+ [1, -.4],
11897
+ [1, .6],
11898
+ [-1, .6]
11899
+ ],
11900
+ options: { labelSize: 96 }
11901
+ }
11902
+ });
11903
+ //#endregion
11230
11904
  //#region src/generators/cm14-maneuver-lines/holding-line.ts
11231
11905
  /** Default options for the Holding line control measure. */
11232
11906
  const DEFAULT_HOLDING_LINE_OPTIONS = {
@@ -11616,6 +12290,193 @@ const BATTLE_HANDOVER_LINE = defineControlMeasure({
11616
12290
  }
11617
12291
  });
11618
12292
  //#endregion
12293
+ //#region src/generators/cm14-maneuver-lines/limit-of-advance.ts
12294
+ /** Default options for the Limit of advance control measure. */
12295
+ const DEFAULT_LIMIT_OF_ADVANCE_OPTIONS = {
12296
+ includePrefix: true,
12297
+ smooth: false,
12298
+ smoothResolution: 12
12299
+ };
12300
+ const LIMIT_OF_ADVANCE_METADATA = {
12301
+ id: "limit-of-advance",
12302
+ name: "Limit of advance",
12303
+ description: "An easily recognized terrain feature beyond which attacking elements will not advance.",
12304
+ entity: "Maneuver Lines",
12305
+ entityType: "Limit of Advance",
12306
+ value: "140900",
12307
+ minCoordinates: 2,
12308
+ geometry: "line",
12309
+ geometryTypes: ["LineString", "Point"],
12310
+ paints: {
12311
+ stroke: true,
12312
+ fill: "none",
12313
+ text: true
12314
+ },
12315
+ drawRule: "Line1",
12316
+ capturesLabelSize: true,
12317
+ params: [...SMOOTH_LINE_PARAMS, ...phaseLineNameParams("RUBY")],
12318
+ textAmplifiers: [HOSTILE_LINE_AMPLIFIER]
12319
+ };
12320
+ /** Creates a Limit of advance line labeled "LOA" above both ends. */
12321
+ function createLimitOfAdvance(positions, options = {}, textAmplifiers = {}, context = {}) {
12322
+ return fixedLabelLineFeatures(positions, {
12323
+ part: "limit-of-advance",
12324
+ placement: "above",
12325
+ ...options
12326
+ }, "LOA", textAmplifiers.N, context);
12327
+ }
12328
+ const LIMIT_OF_ADVANCE = defineControlMeasure({
12329
+ metadata: LIMIT_OF_ADVANCE_METADATA,
12330
+ generator: createLimitOfAdvance,
12331
+ defaultOptions: DEFAULT_LIMIT_OF_ADVANCE_OPTIONS,
12332
+ rule: line1DrawRule,
12333
+ previewSample: {
12334
+ controlPoints: [[-1, 0], [1, 0]],
12335
+ options: { labelSize: 96 }
12336
+ }
12337
+ });
12338
+ //#endregion
12339
+ //#region src/generators/cm14-maneuver-lines/line-of-departure.ts
12340
+ /** Default options for the Line of departure control measure. */
12341
+ const DEFAULT_LINE_OF_DEPARTURE_OPTIONS = {
12342
+ includePrefix: true,
12343
+ smooth: false,
12344
+ smoothResolution: 12
12345
+ };
12346
+ const LINE_OF_DEPARTURE_METADATA = {
12347
+ id: "line-of-departure",
12348
+ name: "Line of departure",
12349
+ description: "A line designated to coordinate the departure of attacking elements.",
12350
+ entity: "Maneuver Lines",
12351
+ entityType: "Line of Departure",
12352
+ value: "141000",
12353
+ minCoordinates: 2,
12354
+ geometry: "line",
12355
+ geometryTypes: ["LineString", "Point"],
12356
+ paints: {
12357
+ stroke: true,
12358
+ fill: "none",
12359
+ text: true
12360
+ },
12361
+ drawRule: "Line1",
12362
+ capturesLabelSize: true,
12363
+ params: [...SMOOTH_LINE_PARAMS, ...phaseLineNameParams("JADE")],
12364
+ textAmplifiers: [HOSTILE_LINE_AMPLIFIER]
12365
+ };
12366
+ /** Creates a Line of departure labeled "LD" above both ends. */
12367
+ function createLineOfDeparture(positions, options = {}, textAmplifiers = {}, context = {}) {
12368
+ return fixedLabelLineFeatures(positions, {
12369
+ part: "line-of-departure",
12370
+ placement: "above",
12371
+ ...options
12372
+ }, "LD", textAmplifiers.N, context);
12373
+ }
12374
+ const LINE_OF_DEPARTURE = defineControlMeasure({
12375
+ metadata: LINE_OF_DEPARTURE_METADATA,
12376
+ generator: createLineOfDeparture,
12377
+ defaultOptions: DEFAULT_LINE_OF_DEPARTURE_OPTIONS,
12378
+ rule: line1DrawRule,
12379
+ previewSample: {
12380
+ controlPoints: [[-1, 0], [1, 0]],
12381
+ options: { labelSize: 96 }
12382
+ }
12383
+ });
12384
+ //#endregion
12385
+ //#region src/generators/cm14-maneuver-lines/line-of-departure-contact.ts
12386
+ /** Default options for the Line of departure / line of contact control measure. */
12387
+ const DEFAULT_LINE_OF_DEPARTURE_CONTACT_OPTIONS = {
12388
+ includePrefix: true,
12389
+ smooth: false,
12390
+ smoothResolution: 12
12391
+ };
12392
+ const LINE_OF_DEPARTURE_CONTACT_METADATA = {
12393
+ id: "line-of-departure-contact",
12394
+ name: "Line of departure / line of contact",
12395
+ description: "The designation of forward friendly positions as the line of departure when opposing forces are in contact.",
12396
+ entity: "Maneuver Lines",
12397
+ entityType: "Line of Departure / Line of Contact",
12398
+ value: "141100",
12399
+ minCoordinates: 2,
12400
+ geometry: "line",
12401
+ geometryTypes: ["LineString", "Point"],
12402
+ paints: {
12403
+ stroke: true,
12404
+ fill: "none",
12405
+ text: true
12406
+ },
12407
+ drawRule: "Line1",
12408
+ capturesLabelSize: true,
12409
+ params: [...SMOOTH_LINE_PARAMS, ...phaseLineNameParams("AMBER")],
12410
+ textAmplifiers: [HOSTILE_LINE_AMPLIFIER]
12411
+ };
12412
+ /** Creates a Line of departure / line of contact labeled "LD/LC" above both ends. */
12413
+ function createLineOfDepartureContact(positions, options = {}, textAmplifiers = {}, context = {}) {
12414
+ return fixedLabelLineFeatures(positions, {
12415
+ part: "line-of-departure-contact",
12416
+ placement: "above",
12417
+ ...options
12418
+ }, "LD/LC", textAmplifiers.N, context);
12419
+ }
12420
+ const LINE_OF_DEPARTURE_CONTACT = defineControlMeasure({
12421
+ metadata: LINE_OF_DEPARTURE_CONTACT_METADATA,
12422
+ generator: createLineOfDepartureContact,
12423
+ defaultOptions: DEFAULT_LINE_OF_DEPARTURE_CONTACT_OPTIONS,
12424
+ rule: line1DrawRule,
12425
+ previewSample: {
12426
+ controlPoints: [[-1, 0], [1, 0]],
12427
+ options: { labelSize: 96 }
12428
+ }
12429
+ });
12430
+ //#endregion
12431
+ //#region src/generators/cm14-maneuver-lines/probable-line-of-deployment.ts
12432
+ /** Default options for the Probable line of deployment control measure. */
12433
+ const DEFAULT_PROBABLE_LINE_OF_DEPLOYMENT_OPTIONS = {
12434
+ includePrefix: true,
12435
+ smooth: false,
12436
+ smoothResolution: 12
12437
+ };
12438
+ /** Intrinsic doctrinal dash pattern for Probable Line of Deployment. */
12439
+ const PROBABLE_LINE_OF_DEPLOYMENT_DASH = [6, 4];
12440
+ const PROBABLE_LINE_OF_DEPLOYMENT_METADATA = {
12441
+ id: "probable-line-of-deployment",
12442
+ name: "Probable line of deployment",
12443
+ description: "A line selected on the ground, usually the last covered and concealed position prior to the objective and forward of the line of departure, where attacking units deploy prior to beginning an assault.",
12444
+ entity: "Maneuver Lines",
12445
+ entityType: "Probable Line of Deployment",
12446
+ value: "141200",
12447
+ minCoordinates: 2,
12448
+ geometry: "line",
12449
+ geometryTypes: ["LineString", "Point"],
12450
+ paints: {
12451
+ stroke: true,
12452
+ fill: "none",
12453
+ text: true
12454
+ },
12455
+ drawRule: "Line1",
12456
+ capturesLabelSize: true,
12457
+ params: [...SMOOTH_LINE_PARAMS, ...phaseLineNameParams("ONYX")],
12458
+ textAmplifiers: [HOSTILE_LINE_AMPLIFIER]
12459
+ };
12460
+ /** Creates a Probable line of deployment labeled "PLD" above both ends, with a dashed stroke. */
12461
+ function createProbableLineOfDeployment(positions, options = {}, textAmplifiers = {}, context = {}) {
12462
+ return fixedLabelLineFeatures(positions, {
12463
+ part: "probable-line-of-deployment",
12464
+ placement: "above",
12465
+ lineStyle: { strokeDash: [...PROBABLE_LINE_OF_DEPLOYMENT_DASH] },
12466
+ ...options
12467
+ }, "PLD", textAmplifiers.N, context);
12468
+ }
12469
+ const PROBABLE_LINE_OF_DEPLOYMENT = defineControlMeasure({
12470
+ metadata: PROBABLE_LINE_OF_DEPLOYMENT_METADATA,
12471
+ generator: createProbableLineOfDeployment,
12472
+ defaultOptions: DEFAULT_PROBABLE_LINE_OF_DEPLOYMENT_OPTIONS,
12473
+ rule: line1DrawRule,
12474
+ previewSample: {
12475
+ controlPoints: [[-1, 0], [1, 0]],
12476
+ options: { labelSize: 96 }
12477
+ }
12478
+ });
12479
+ //#endregion
11619
12480
  //#region src/generators/cm29-protection-lines/fortifiedLine.ts
11620
12481
  const DEFAULT_TOOTH_SIZE = 50;
11621
12482
  const DEFAULT_TOOTH_SIZE_PIXELS = 10;
@@ -12856,6 +13717,57 @@ const LANDING_ZONE = defineControlMeasure({
12856
13717
  }
12857
13718
  });
12858
13719
  //#endregion
13720
+ //#region src/generators/cm15-maneuver-areas/penetration-box.ts
13721
+ /** Default options for the Penetration box control measure. */
13722
+ const DEFAULT_PENETRATION_BOX_OPTIONS = {
13723
+ smooth: false,
13724
+ smoothResolution: 16
13725
+ };
13726
+ const PENETRATION_BOX_METADATA = {
13727
+ id: "penetration-box",
13728
+ name: "Penetration box",
13729
+ description: "An area in which a force ruptures the enemy's defense on a narrow front to break the continuity of the defensive system.",
13730
+ entity: "Maneuver Areas",
13731
+ entityType: "Penetration Box",
13732
+ value: "151900",
13733
+ minCoordinates: 3,
13734
+ geometry: "area",
13735
+ geometryTypes: [
13736
+ "Polygon",
13737
+ "MultiLineString",
13738
+ "Point"
13739
+ ],
13740
+ paints: {
13741
+ stroke: true,
13742
+ fill: "none",
13743
+ text: true
13744
+ },
13745
+ drawRule: "Area1",
13746
+ capturesLabelSize: true,
13747
+ params: LABELED_AREA_PARAMS,
13748
+ textAmplifiers: AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
13749
+ };
13750
+ /** Creates a Penetration box: a bare closed area through `positions`. */
13751
+ function createPenetrationBox(positions, options = {}, textAmplifiers = {}, context = {}) {
13752
+ return labeledAreaFeatures(positions, options, composeAreaLabelTexts(textAmplifiers, ""), context.amplifierPlacements, context);
13753
+ }
13754
+ const PENETRATION_BOX = defineControlMeasure({
13755
+ metadata: PENETRATION_BOX_METADATA,
13756
+ generator: createPenetrationBox,
13757
+ defaultOptions: DEFAULT_PENETRATION_BOX_OPTIONS,
13758
+ rule: area1DrawRule,
13759
+ previewSample: {
13760
+ controlPoints: [
13761
+ [-1, -.5],
13762
+ [1, -.5],
13763
+ [1, .5],
13764
+ [-1, .5]
13765
+ ],
13766
+ textAmplifiers: {},
13767
+ options: { labelSize: 96 }
13768
+ }
13769
+ });
13770
+ //#endregion
12859
13771
  //#region src/generators/cm15-maneuver-areas/limitedAccessArea.ts
12860
13772
  /** Default options for the Limited Access Area control measure. */
12861
13773
  const DEFAULT_LIMITED_ACCESS_AREA_OPTIONS = {
@@ -15233,6 +16145,7 @@ const DEFINITIONS = {
15233
16145
  "engineer-work-line": ENGINEER_WORK_LINE,
15234
16146
  "generic-c2-line": GENERIC_C2_LINE,
15235
16147
  "battle-position": BATTLE_POSITION,
16148
+ "battle-position-prepared": BATTLE_POSITION_PREPARED,
15236
16149
  contain: CONTAIN,
15237
16150
  "strong-point": STRONG_POINT,
15238
16151
  retain: RETAIN,
@@ -15248,6 +16161,8 @@ const DEFINITIONS = {
15248
16161
  encirclement: ENCIRCLEMENT,
15249
16162
  area: AREA,
15250
16163
  "area-defense": AREA_DEFENSE,
16164
+ "engagement-area": ENGAGEMENT_AREA,
16165
+ "mobile-defense": MOBILE_DEFENSE,
15251
16166
  "assembly-area": ASSEMBLY_AREA,
15252
16167
  "joint-tactical-action-area": JOINT_TACTICAL_ACTION_AREA,
15253
16168
  "submarine-action-area": SUBMARINE_ACTION_AREA,
@@ -15256,6 +16171,7 @@ const DEFINITIONS = {
15256
16171
  "extraction-zone": EXTRACTION_ZONE,
15257
16172
  "landing-zone": LANDING_ZONE,
15258
16173
  "limited-access-area": LIMITED_ACCESS_AREA,
16174
+ "penetration-box": PENETRATION_BOX,
15259
16175
  "no-fire-area-irregular": NO_FIRE_AREA_IRREGULAR,
15260
16176
  "minefield-dynamic": DYNAMIC_MINEFIELD,
15261
16177
  "mined-area": MINED_AREA,
@@ -15289,10 +16205,16 @@ const DEFINITIONS = {
15289
16205
  "phase-line": PHASE_LINE,
15290
16206
  "forward-edge-of-battle-area": FORWARD_EDGE_OF_BATTLE_AREA,
15291
16207
  "bridgehead-line": BRIDGEHEAD_LINE,
16208
+ "final-coordination-line": FINAL_COORDINATION_LINE,
16209
+ "airhead-line": AIRHEAD_LINE,
15292
16210
  "holding-line": HOLDING_LINE,
15293
16211
  "release-line": RELEASE_LINE,
15294
16212
  "handover-line": HANDOVER_LINE,
15295
16213
  "battle-handover-line": BATTLE_HANDOVER_LINE,
16214
+ "limit-of-advance": LIMIT_OF_ADVANCE,
16215
+ "line-of-departure": LINE_OF_DEPARTURE,
16216
+ "line-of-departure-contact": LINE_OF_DEPARTURE_CONTACT,
16217
+ "probable-line-of-deployment": PROBABLE_LINE_OF_DEPLOYMENT,
15296
16218
  "block-mission-task": BLOCK_MISSION_TASK,
15297
16219
  breach: BREACH,
15298
16220
  bypass: BYPASS,
@@ -15340,6 +16262,7 @@ const DEFINITIONS = {
15340
16262
  }
15341
16263
  }),
15342
16264
  block: BLOCK,
16265
+ "bridge-or-gap": BRIDGE_OR_GAP,
15343
16266
  disrupt: DISRUPT,
15344
16267
  fix: FIX,
15345
16268
  "fix-mission-task": FIX_MISSION_TASK,
@@ -15735,4 +16658,4 @@ function assertNever(value) {
15735
16658
  throw new Error(`Unhandled control measure kind: ${String(value)}`);
15736
16659
  }
15737
16660
  //#endregion
15738
- export { DEFAULT_COVER_OPTIONS as $, supportByFireDrawRule as $t, DEFAULT_RETIRE_OPTIONS as A, getMetersPerPixel as An, DEFAULT_ANTITANK_DITCH_OPTIONS as At, DEFAULT_HOLDING_LINE_OPTIONS as B, resolveAmplifierPlacement as Bt, DEFAULT_MAIN_ATTACK_OPTIONS as C, calculateMetrics as Cn, DEFAULT_BLOCK_ARROW_OPTIONS as Ct, DEFAULT_WITHDRAW_OPTIONS as D, sphericalBearing as Dn, DEFAULT_ATTACK_HELICOPTER_OPTIONS as Dt, DEFAULT_WITHDRAW_UNDER_PRESSURE_OPTIONS as E, project as En, DEFAULT_BATTLE_POSITION_OPTIONS as Et, DEFAULT_FORTIFIED_LINE_OPTIONS as F, DEFAULT_DIRECTION_OF_MAIN_ATTACK_OPTIONS as Ft, DEFAULT_FIX_OPTIONS as G, DEFAULT_PORTRAYAL as Gt, DEFAULT_PHASE_LINE_OPTIONS as H, DEFAULT_LABEL_SIZE_CLAMP_CSS_PIXELS as Ht, DEFAULT_BATTLE_HANDOVER_LINE_OPTIONS as I, DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS as It, DEFAULT_DISRUPT_OPTIONS as J, DEFAULT_SYMBOL_COLOR as Jt, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as K, DEFAULT_STROKE_DASH_CSS_PIXELS as Kt, DEFAULT_HANDOVER_LINE_OPTIONS as L, TEXT_AMPLIFIER_FIELDS as Lt, DEFAULT_PENETRATE_OPTIONS as M, DEFAULT_AREA_OF_OPERATIONS_OPTIONS as Mt, DEFAULT_FRONTAL_ATTACK_OPTIONS as N, DEFAULT_AREA_DEFENSE_OPTIONS as Nt, DEFAULT_SEIZE_OPTIONS as O, unproject as On, DEFAULT_ATTACK_BY_FIRE_OPTIONS as Ot, DEFAULT_FORTIFIED_AREA_OPTIONS as P, DEFAULT_DIRECTION_OF_SUPPORTING_ATTACK_OPTIONS as Pt, DEFAULT_DELAY_OPTIONS as Q, axis1DrawRule as Qt, DEFAULT_FORWARD_EDGE_OF_BATTLE_AREA_OPTIONS as R, canonicalTextAmplifierKey as Rt, DEFAULT_MINEFIELD_OPTIONS as S, createBaselineFrame as Sn, DEFAULT_BOUNDARY_OPTIONS as St, DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS as T, haversineDistance as Tn, DEFAULT_CONTAIN_OPTIONS as Tt, DEFAULT_FLOT_OPTIONS as U, DEFAULT_LINE_CAP as Ut, DEFAULT_BRIDGEHEAD_LINE_OPTIONS as V, DEFAULT_LABEL_HEIGHT_CSS_PIXELS as Vt, DEFAULT_FIX_MISSION_TASK_OPTIONS as W, DEFAULT_LINE_JOIN as Wt, DEFAULT_DISRUPT_MISSION_TASK_OPTIONS as X, DEFAULT_AIRBORNE_ATTACK_OPTIONS as Xt, DEFAULT_GUARD_OPTIONS as Y, DEFAULT_AMBUSH_OPTIONS as Yt, DEFAULT_DISENGAGE_OPTIONS as Z, rectangleDrawRule as Zt, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as _, computeDefaultMidpointPerpendicularPoint as _n, DEFAULT_BREACH_OPTIONS as _t, DEFINITIONS as a, line1DrawRule as an, DEFAULT_RADAR_SEARCH_DOCTRINE_OPTIONS as at, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as b, pointOnMidpointPerpendicularAxis as bn, DEFAULT_ENGINEER_WORK_LINE_OPTIONS as bt, getDefaultOptions as c, dynamicPointDrawRule as cn, DEFAULT_GENERIC_TEXT_OPTIONS as ct, DEFAULT_TURN_OPTIONS as d, point12DrawRule as dn, DEFAULT_GENERIC_POLYGON_OPTIONS as dt, line27DrawRule as en, DEFAULT_TACTICAL_ARROW_OPTIONS as et, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as f, penetrateDrawRule as fn, DEFAULT_GENERIC_LINE_OPTIONS as ft, SECONDARY_DIRECTION_OF_FIRE_DASH as g, containDrawRule as gn, DEFAULT_BYPASS_OPTIONS as gt, DEFAULT_SECONDARY_DIRECTION_OF_FIRE_OPTIONS as h, centerRadiusDrawRule as hn, DEFAULT_CANALIZE_OPTIONS as ht, CONTROL_MEASURE_METADATA as i, turnDrawRule as in, DEFAULT_CLEAR_OPTIONS as it, DEFAULT_REARWARD_PASSAGE_OF_LINES_OPTIONS as j, roundToFixed as jn, DEFAULT_ASSEMBLY_AREA_OPTIONS as jt, DEFAULT_SCREEN_OPTIONS as k, EPSILON as kn, DEFAULT_ANTITANK_WALL_OPTIONS as kt, listControlMeasureMetadata as l, sectorDrawRule as ln, DEFAULT_GENERIC_SECTOR_OPTIONS as lt, DEFAULT_STRONG_POINT_OPTIONS as m, blockDrawRule as mn, DEFAULT_CLASSIC_ARROW_OPTIONS as mt, resolveStyleHints as n, line24DrawRule as nn, DEFAULT_COUNTERATTACK_OPTIONS as nt, getControlMeasureMetadata as o, ambushDrawRule as on, RADAR_SEARCH_DOCTRINE_FILL_COLOR as ot, DEFAULT_RETAIN_OPTIONS as p, disruptDrawRule as pn, DEFAULT_GENERIC_CIRCLE_OPTIONS as pt, DEFAULT_ENCIRCLEMENT_OPTIONS as q, DEFAULT_STROKE_WIDTH_CSS_PIXELS as qt, CONTROL_MEASURE_IDS as r, line23DrawRule as rn, DEFAULT_SUPPORTING_ATTACK_OPTIONS as rt, getControlMeasureMetadataByValue as s, attackByFireDrawRule as sn, RADAR_SEARCH_DOCTRINE_STROKE_COLOR as st, renderControlMeasure as t, line26DrawRule as tn, DEFAULT_COUNTERATTACK_BY_FIRE_OPTIONS as tt, DEFAULT_TURNING_MOVEMENT_OPTIONS as u, staticPointDrawRule as un, DEFAULT_GENERIC_RECTANGLE_OPTIONS as ut, DEFAULT_PICKUP_ZONE_OPTIONS as v, createMidpointPerpendicularDrawRule as vn, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as vt, DEFAULT_LANDING_ZONE_OPTIONS as w, computeInitialWidthPoint as wn, DEFAULT_BLOCK_OPTIONS as wt, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as x, snapToMidpointPerpendicular as xn, DEFAULT_LIGHT_LINE_OPTIONS as xt, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as y, getMidpointPerpendicularSignedDistance as yn, DEFAULT_GENERIC_C2_LINE_OPTIONS as yt, DEFAULT_RELEASE_LINE_OPTIONS as z, normalizeTextAmplifiers as zt };
16661
+ export { DEFAULT_DISRUPT_OPTIONS as $, DEFAULT_STROKE_DASH_CSS_PIXELS as $t, DEFAULT_RETIRE_OPTIONS as A, createBaselineFrame as An, DEFAULT_CONTAIN_OPTIONS as At, DEFAULT_LIMIT_OF_ADVANCE_OPTIONS as B, DEFAULT_AREA_DEFENSE_OPTIONS as Bt, DEFAULT_MAIN_ATTACK_OPTIONS as C, containDrawRule as Cn, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as Ct, DEFAULT_WITHDRAW_OPTIONS as D, getMidpointPerpendicularSignedDistance as Dn, DEFAULT_BOUNDARY_OPTIONS as Dt, DEFAULT_WITHDRAW_UNDER_PRESSURE_OPTIONS as E, createMidpointPerpendicularDrawRule as En, DEFAULT_LIGHT_LINE_OPTIONS as Et, DEFAULT_FORTIFIED_LINE_OPTIONS as F, sphericalBearing as Fn, DEFAULT_ANTITANK_DITCH_OPTIONS as Ft, DEFAULT_HOLDING_LINE_OPTIONS as G, canonicalTextAmplifierKey as Gt, DEFAULT_HANDOVER_LINE_OPTIONS as H, DEFAULT_DIRECTION_OF_MAIN_ATTACK_OPTIONS as Ht, DEFAULT_PROBABLE_LINE_OF_DEPLOYMENT_OPTIONS as I, unproject as In, DEFAULT_ASSEMBLY_AREA_OPTIONS as It, DEFAULT_FLOT_OPTIONS as J, DEFAULT_LABEL_HEIGHT_CSS_PIXELS as Jt, DEFAULT_BRIDGEHEAD_LINE_OPTIONS as K, normalizeTextAmplifiers as Kt, PROBABLE_LINE_OF_DEPLOYMENT_DASH as L, EPSILON as Ln, DEFAULT_AREA_OF_OPERATIONS_OPTIONS as Lt, DEFAULT_PENETRATE_OPTIONS as M, computeInitialWidthPoint as Mn, DEFAULT_ATTACK_HELICOPTER_OPTIONS as Mt, DEFAULT_FRONTAL_ATTACK_OPTIONS as N, haversineDistance as Nn, DEFAULT_ATTACK_BY_FIRE_OPTIONS as Nt, DEFAULT_SEIZE_OPTIONS as O, pointOnMidpointPerpendicularAxis as On, DEFAULT_BLOCK_ARROW_OPTIONS as Ot, DEFAULT_FORTIFIED_AREA_OPTIONS as P, project as Pn, DEFAULT_ANTITANK_WALL_OPTIONS as Pt, DEFAULT_ENCIRCLEMENT_OPTIONS as Q, DEFAULT_PORTRAYAL as Qt, DEFAULT_LINE_OF_DEPARTURE_CONTACT_OPTIONS as R, getMetersPerPixel as Rn, DEFAULT_MOBILE_DEFENSE_OPTIONS as Rt, DEFAULT_MINEFIELD_OPTIONS as S, centerRadiusDrawRule as Sn, DEFAULT_BREACH_OPTIONS as St, DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS as T, computeDefaultMidpointPerpendicularPoint as Tn, DEFAULT_ENGINEER_WORK_LINE_OPTIONS as Tt, DEFAULT_FORWARD_EDGE_OF_BATTLE_AREA_OPTIONS as U, DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS as Ut, DEFAULT_BATTLE_HANDOVER_LINE_OPTIONS as V, DEFAULT_DIRECTION_OF_SUPPORTING_ATTACK_OPTIONS as Vt, DEFAULT_RELEASE_LINE_OPTIONS as W, TEXT_AMPLIFIER_FIELDS as Wt, DEFAULT_FIX_OPTIONS as X, DEFAULT_LINE_CAP as Xt, DEFAULT_FIX_MISSION_TASK_OPTIONS as Y, DEFAULT_LABEL_SIZE_CLAMP_CSS_PIXELS as Yt, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as Z, DEFAULT_LINE_JOIN as Zt, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as _, staticPointDrawRule as _n, DEFAULT_GENERIC_LINE_OPTIONS as _t, DEFINITIONS as a, axis1DrawRule as an, DEFAULT_TACTICAL_ARROW_OPTIONS as at, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as b, disruptDrawRule as bn, DEFAULT_CANALIZE_OPTIONS as bt, getDefaultOptions as c, line26DrawRule as cn, DEFAULT_SUPPORTING_ATTACK_OPTIONS as ct, DEFAULT_TURN_OPTIONS as d, turnDrawRule as dn, RADAR_SEARCH_DOCTRINE_FILL_COLOR as dt, DEFAULT_STROKE_WIDTH_CSS_PIXELS as en, DEFAULT_GUARD_OPTIONS as et, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as f, line1DrawRule as fn, RADAR_SEARCH_DOCTRINE_STROKE_COLOR as ft, SECONDARY_DIRECTION_OF_FIRE_DASH as g, sectorDrawRule as gn, DEFAULT_GENERIC_POLYGON_OPTIONS as gt, DEFAULT_SECONDARY_DIRECTION_OF_FIRE_OPTIONS as h, dynamicPointDrawRule as hn, DEFAULT_GENERIC_RECTANGLE_OPTIONS as ht, CONTROL_MEASURE_METADATA as i, rectangleDrawRule as in, DEFAULT_COVER_OPTIONS as it, DEFAULT_REARWARD_PASSAGE_OF_LINES_OPTIONS as j, calculateMetrics as jn, DEFAULT_BATTLE_POSITION_OPTIONS as jt, DEFAULT_SCREEN_OPTIONS as k, snapToMidpointPerpendicular as kn, DEFAULT_BLOCK_OPTIONS as kt, listControlMeasureMetadata as l, line24DrawRule as ln, DEFAULT_CLEAR_OPTIONS as lt, DEFAULT_STRONG_POINT_OPTIONS as m, attackByFireDrawRule as mn, DEFAULT_GENERIC_SECTOR_OPTIONS as mt, resolveStyleHints as n, DEFAULT_AMBUSH_OPTIONS as nn, DEFAULT_DISENGAGE_OPTIONS as nt, getControlMeasureMetadata as o, supportByFireDrawRule as on, DEFAULT_COUNTERATTACK_BY_FIRE_OPTIONS as ot, DEFAULT_RETAIN_OPTIONS as p, ambushDrawRule as pn, DEFAULT_GENERIC_TEXT_OPTIONS as pt, DEFAULT_PHASE_LINE_OPTIONS as q, resolveAmplifierPlacement as qt, CONTROL_MEASURE_IDS as r, DEFAULT_AIRBORNE_ATTACK_OPTIONS as rn, DEFAULT_DELAY_OPTIONS as rt, getControlMeasureMetadataByValue as s, line27DrawRule as sn, DEFAULT_COUNTERATTACK_OPTIONS as st, renderControlMeasure as t, DEFAULT_SYMBOL_COLOR as tn, DEFAULT_DISRUPT_MISSION_TASK_OPTIONS as tt, DEFAULT_TURNING_MOVEMENT_OPTIONS as u, line23DrawRule as un, DEFAULT_RADAR_SEARCH_DOCTRINE_OPTIONS as ut, DEFAULT_PICKUP_ZONE_OPTIONS as v, point12DrawRule as vn, DEFAULT_GENERIC_CIRCLE_OPTIONS as vt, DEFAULT_LANDING_ZONE_OPTIONS as w, area27DrawRule as wn, DEFAULT_GENERIC_C2_LINE_OPTIONS as wt, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as x, blockDrawRule as xn, DEFAULT_BYPASS_OPTIONS as xt, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as y, penetrateDrawRule as yn, DEFAULT_CLASSIC_ARROW_OPTIONS as yt, DEFAULT_LINE_OF_DEPARTURE_OPTIONS as z, roundToFixed as zn, DEFAULT_ENGAGEMENT_AREA_OPTIONS as zt };