@orbat-mapper/control-measures 0.21.0 → 0.23.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.
Files changed (38) hide show
  1. package/README.md +13 -0
  2. package/dist/{index-DuWUI8Se.d.mts → index-5ZXJfifM.d.mts} +42 -2
  3. package/dist/index.d.mts +3 -3
  4. package/dist/index.mjs +2 -2
  5. package/dist/{patterns-CcQmmOuJ.d.mts → patterns-DMO-zeaM.d.mts} +21 -1
  6. package/dist/patterns.d.mts +1 -1
  7. package/dist/preview/index.d.mts +23 -3
  8. package/dist/preview/index.mjs +29 -3
  9. package/dist/{renderControlMeasure-Cn4Yh917.mjs → renderControlMeasure-8PAUAwk_.mjs} +704 -148
  10. package/media/antitank-ditch-completed.svg +1 -1
  11. package/media/antitank-ditch-under-construction.svg +1 -1
  12. package/media/antitank-wall.svg +1 -1
  13. package/media/block.svg +1 -1
  14. package/media/disrupt.svg +1 -1
  15. package/media/double-apron-fence.svg +1 -0
  16. package/media/double-fence.svg +1 -0
  17. package/media/double-strand-concertina.svg +1 -0
  18. package/media/fix.svg +1 -1
  19. package/media/high-wire-fence.svg +1 -0
  20. package/media/limited-access-area.svg +1 -1
  21. package/media/low-wire-fence.svg +1 -0
  22. package/media/mined-area.svg +1 -1
  23. package/media/minefield-dynamic.svg +1 -1
  24. package/media/minefield.svg +1 -1
  25. package/media/no-fire-area-irregular.svg +1 -1
  26. package/media/obstacle-belt.svg +1 -0
  27. package/media/obstacle-bypass-difficult.svg +1 -1
  28. package/media/obstacle-bypass-easy.svg +1 -1
  29. package/media/obstacle-bypass-impossible.svg +1 -1
  30. package/media/obstacle-free-zone.svg +1 -0
  31. package/media/obstacle-restricted-zone.svg +1 -0
  32. package/media/obstacle-zone.svg +1 -0
  33. package/media/single-concertina.svg +1 -0
  34. package/media/single-fence.svg +1 -0
  35. package/media/triple-strand-concertina.svg +1 -0
  36. package/media/turn.svg +1 -1
  37. package/media/wire-obstacle-unspecified.svg +1 -0
  38. package/package.json +1 -1
@@ -1,40 +1,3 @@
1
- //#region src/style.ts
2
- /**
3
- * Per-feature style hint pinning a filled part to a solid interior. Attach to
4
- * a generator's intrinsic doctrinal accents — arrowheads, teeth, echelon
5
- * glyphs, barbs, blades — so a patterned (`hatch`, `dots`, …) fill set at the graphicsStyle
6
- * or measure layer never bleeds into a small silhouette and ruins its
7
- * legibility. The renderer only reads style hints, so one shared instance is
8
- * safe to reuse across every accent feature.
9
- */
10
- const SOLID_ACCENT_FILL = { fillPattern: "solid" };
11
- /**
12
- * Ultimate fallback for the symbol color when no `color` or per-channel
13
- * override is supplied at any layer. Keeps a zero-config render monocolor
14
- * black and guarantees filled parts still render filled. See ADR-0011.
15
- */
16
- const DEFAULT_SYMBOL_COLOR = "#000000";
17
- //#endregion
18
- //#region src/portrayal.ts
19
- const DEFAULT_STROKE_WIDTH_CSS_PIXELS = 2;
20
- const DEFAULT_STROKE_DASH_CSS_PIXELS = Object.freeze([]);
21
- const DEFAULT_LINE_CAP = "round";
22
- const DEFAULT_LINE_JOIN = "round";
23
- const DEFAULT_LABEL_HEIGHT_CSS_PIXELS = 14;
24
- const DEFAULT_LABEL_SIZE_CLAMP_CSS_PIXELS = {
25
- min: 8,
26
- max: 24
27
- };
28
- /** Shared absent-value portrayal defaults; render output remains sparse. */
29
- const DEFAULT_PORTRAYAL = Object.freeze({
30
- symbolColor: DEFAULT_SYMBOL_COLOR,
31
- strokeWidthCssPixels: 2,
32
- strokeDashCssPixels: DEFAULT_STROKE_DASH_CSS_PIXELS,
33
- lineCap: DEFAULT_LINE_CAP,
34
- lineJoin: DEFAULT_LINE_JOIN,
35
- labelHeightCssPixels: 14
36
- });
37
- //#endregion
38
1
  //#region src/define.ts
39
2
  /**
40
3
  * Identity helper that infers a definition's literal `Id` and precise generator
@@ -1809,6 +1772,50 @@ const line25DrawRule = {
1809
1772
  transform: (event) => clonePositions(event.next.slice(0, 2))
1810
1773
  };
1811
1774
  //#endregion
1775
+ //#region src/style.ts
1776
+ /**
1777
+ * Per-feature style hint pinning a filled part to a solid interior. Attach to
1778
+ * a generator's intrinsic doctrinal accents — arrowheads, teeth, echelon
1779
+ * glyphs, barbs, blades — so a patterned (`hatch`, `dots`, …) fill set at the graphicsStyle
1780
+ * or measure layer never bleeds into a small silhouette and ruins its
1781
+ * legibility. The renderer only reads style hints, so one shared instance is
1782
+ * safe to reuse across every accent feature.
1783
+ */
1784
+ const SOLID_ACCENT_FILL = { fillPattern: "solid" };
1785
+ /**
1786
+ * Ultimate fallback for the symbol color when no `color` or per-channel
1787
+ * override or doctrinal metadata default is supplied. Keeps other measures
1788
+ * monocolor black and guarantees filled parts still render filled. See ADR-0011.
1789
+ */
1790
+ const DEFAULT_SYMBOL_COLOR = "#000000";
1791
+ /** MIL-STD-2525D Tables XV–XVI. Fill shades are neutral identity palette samples. */
1792
+ const MIL_STD_GREEN = {
1793
+ neon: "#00ff00",
1794
+ dark: "#00a000",
1795
+ medium: "#00e200",
1796
+ light: "#aaffaa"
1797
+ };
1798
+ //#endregion
1799
+ //#region src/portrayal.ts
1800
+ const DEFAULT_STROKE_WIDTH_CSS_PIXELS = 2;
1801
+ const DEFAULT_STROKE_DASH_CSS_PIXELS = Object.freeze([]);
1802
+ const DEFAULT_LINE_CAP = "round";
1803
+ const DEFAULT_LINE_JOIN = "round";
1804
+ const DEFAULT_LABEL_HEIGHT_CSS_PIXELS = 14;
1805
+ const DEFAULT_LABEL_SIZE_CLAMP_CSS_PIXELS = {
1806
+ min: 8,
1807
+ max: 24
1808
+ };
1809
+ /** Shared absent-value portrayal defaults; render output remains sparse. */
1810
+ const DEFAULT_PORTRAYAL = Object.freeze({
1811
+ symbolColor: DEFAULT_SYMBOL_COLOR,
1812
+ strokeWidthCssPixels: 2,
1813
+ strokeDashCssPixels: DEFAULT_STROKE_DASH_CSS_PIXELS,
1814
+ lineCap: DEFAULT_LINE_CAP,
1815
+ lineJoin: DEFAULT_LINE_JOIN,
1816
+ labelHeightCssPixels: 14
1817
+ });
1818
+ //#endregion
1812
1819
  //#region src/internal/angle-utils.ts
1813
1820
  /**
1814
1821
  * Wraps an angle (radians) into the half-open range (-π, π].
@@ -2621,6 +2628,409 @@ function fixedLabelLineFeatures(positions, options, textForEnd, hostileText, con
2621
2628
  features
2622
2629
  };
2623
2630
  }
2631
+ function normalizeArcResolution(value) {
2632
+ if (typeof value !== "number" || !Number.isFinite(value)) return 64;
2633
+ return Math.min(360, Math.max(4, Math.round(value)));
2634
+ }
2635
+ const ARC_RESOLUTION_PARAMS = [{
2636
+ key: "resolution",
2637
+ presentationTier: "advanced",
2638
+ label: "Resolution",
2639
+ description: "Number of segments in a full 360° sweep.",
2640
+ type: "number",
2641
+ min: 4,
2642
+ max: 360,
2643
+ step: 1
2644
+ }];
2645
+ //#endregion
2646
+ //#region src/generators/cm29-protection-lines/params.ts
2647
+ const FORTIFIED_PARAMS = [
2648
+ ...SMOOTH_LINE_PARAMS,
2649
+ {
2650
+ key: "toothSizePixels",
2651
+ label: "Tooth size",
2652
+ description: "Side length of the square crenellations",
2653
+ type: "number",
2654
+ min: 5,
2655
+ max: 50,
2656
+ step: 1,
2657
+ unit: "px"
2658
+ },
2659
+ {
2660
+ key: "toothSize",
2661
+ label: "Tooth size",
2662
+ description: "Side length of the square crenellations",
2663
+ type: "number",
2664
+ min: 1,
2665
+ max: 500,
2666
+ step: 1,
2667
+ unit: "m"
2668
+ },
2669
+ LABEL_PADDING_PARAM
2670
+ ];
2671
+ const ANTITANK_DITCH_PARAMS = [
2672
+ ...SMOOTH_LINE_PARAMS,
2673
+ {
2674
+ key: "toothHeightPixels",
2675
+ label: "Tooth height",
2676
+ description: "Perpendicular depth of each triangular tooth",
2677
+ type: "number",
2678
+ min: 5,
2679
+ max: 50,
2680
+ step: 1,
2681
+ unit: "px"
2682
+ },
2683
+ {
2684
+ key: "toothHeight",
2685
+ label: "Tooth height",
2686
+ description: "Perpendicular depth of each triangular tooth",
2687
+ type: "number",
2688
+ min: 1,
2689
+ max: 500,
2690
+ step: 1,
2691
+ unit: "m"
2692
+ },
2693
+ {
2694
+ key: "toothWidthRatio",
2695
+ presentationTier: "advanced",
2696
+ label: "Tooth width ratio",
2697
+ description: "Tooth base span as a multiple of height",
2698
+ type: "number",
2699
+ min: .5,
2700
+ max: 3,
2701
+ step: .05,
2702
+ unit: "x"
2703
+ },
2704
+ LABEL_PADDING_PARAM
2705
+ ];
2706
+ const ANTITANK_WALL_PARAMS = [...ANTITANK_DITCH_PARAMS, {
2707
+ key: "toothSpacingRatio",
2708
+ presentationTier: "advanced",
2709
+ label: "Tooth spacing ratio",
2710
+ description: "Gap between teeth as a multiple of base width",
2711
+ type: "number",
2712
+ min: 0,
2713
+ max: 3,
2714
+ step: .05,
2715
+ unit: "x"
2716
+ }];
2717
+ const WIRE_MARKER_PARAMS = [{
2718
+ key: "markerSize",
2719
+ label: "Marker size",
2720
+ description: "Full height of each wire marker in meters",
2721
+ type: "number",
2722
+ min: 1,
2723
+ max: 500,
2724
+ step: 1,
2725
+ unit: "m"
2726
+ }, {
2727
+ key: "markerSizePixels",
2728
+ label: "Marker size",
2729
+ description: "Full height of each wire marker in pixels",
2730
+ type: "number",
2731
+ min: 1,
2732
+ max: 50,
2733
+ step: 1,
2734
+ unit: "px"
2735
+ }];
2736
+ /** The lower bound varies per wire kind, so callers spread this and override `min`. */
2737
+ const WIRE_SPACING_PARAM = {
2738
+ key: "markerSpacingRatio",
2739
+ label: "Marker spacing",
2740
+ description: "Target distance between marker groups as a multiple of marker height",
2741
+ presentationTier: "advanced",
2742
+ type: "number",
2743
+ min: .8,
2744
+ max: 10,
2745
+ step: .1
2746
+ };
2747
+ //#endregion
2748
+ //#region src/generators/cm29-protection-lines/wire-obstacles.ts
2749
+ const DEFAULT_MARKER_SIZE = 100;
2750
+ const DEFAULT_MARKER_SIZE_PIXELS = 20;
2751
+ const WIRE_KINDS = {
2752
+ unspecified: {
2753
+ subtype: "Unspecified",
2754
+ value: "290301",
2755
+ description: "A wire obstacle whose fence type is unspecified.",
2756
+ spacing: 1.5,
2757
+ minSpacing: .8,
2758
+ concertina: false,
2759
+ oneSided: false,
2760
+ paired: false,
2761
+ rails: 0,
2762
+ railSeparation: 0,
2763
+ previewMarkerSize: 96
2764
+ },
2765
+ single: {
2766
+ subtype: "Single Fence",
2767
+ value: "290302",
2768
+ description: "A wire obstacle consisting of a single fence.",
2769
+ spacing: 3,
2770
+ minSpacing: .8,
2771
+ concertina: false,
2772
+ oneSided: false,
2773
+ paired: false,
2774
+ rails: 1,
2775
+ railSeparation: 0,
2776
+ previewMarkerSize: 115
2777
+ },
2778
+ double: {
2779
+ subtype: "Double Fence",
2780
+ value: "290303",
2781
+ description: "A wire obstacle consisting of a double fence.",
2782
+ spacing: 3.6,
2783
+ minSpacing: 1.6,
2784
+ concertina: false,
2785
+ oneSided: false,
2786
+ paired: true,
2787
+ rails: 1,
2788
+ railSeparation: 0,
2789
+ previewMarkerSize: 115
2790
+ },
2791
+ apron: {
2792
+ subtype: "Double Apron Fence",
2793
+ value: "290304",
2794
+ description: "A wire fence with an apron on each side.",
2795
+ spacing: 1.5,
2796
+ minSpacing: .8,
2797
+ concertina: false,
2798
+ oneSided: false,
2799
+ paired: false,
2800
+ rails: 1,
2801
+ railSeparation: 0,
2802
+ previewMarkerSize: 96
2803
+ },
2804
+ low: {
2805
+ subtype: "Low Wire Fence",
2806
+ value: "290305",
2807
+ description: "A low wire fence obstacle.",
2808
+ spacing: 1.5,
2809
+ minSpacing: .8,
2810
+ concertina: false,
2811
+ oneSided: true,
2812
+ paired: false,
2813
+ rails: 1,
2814
+ railSeparation: 0,
2815
+ previewMarkerSize: 96
2816
+ },
2817
+ high: {
2818
+ subtype: "High Wire Fence",
2819
+ value: "290306",
2820
+ description: "A high wire fence obstacle.",
2821
+ spacing: 1.5,
2822
+ minSpacing: .8,
2823
+ concertina: false,
2824
+ oneSided: true,
2825
+ paired: false,
2826
+ rails: 2,
2827
+ railSeparation: 1,
2828
+ previewMarkerSize: 96
2829
+ },
2830
+ concertina: {
2831
+ subtype: "Single Concertina",
2832
+ value: "290307",
2833
+ description: "A wire obstacle consisting of a single concertina coil.",
2834
+ spacing: 1.25,
2835
+ minSpacing: .8,
2836
+ concertina: true,
2837
+ oneSided: true,
2838
+ paired: false,
2839
+ rails: 1,
2840
+ railSeparation: 0,
2841
+ previewMarkerSize: 96
2842
+ },
2843
+ "double-concertina": {
2844
+ subtype: "Double Strand Concertina",
2845
+ value: "290308",
2846
+ description: "A wire obstacle consisting of double-strand concertina.",
2847
+ spacing: 1.25,
2848
+ minSpacing: .8,
2849
+ concertina: true,
2850
+ oneSided: true,
2851
+ paired: false,
2852
+ rails: 2,
2853
+ railSeparation: .5,
2854
+ previewMarkerSize: 96
2855
+ },
2856
+ "triple-concertina": {
2857
+ subtype: "Triple Strand Concertina",
2858
+ value: "290309",
2859
+ description: "A wire obstacle consisting of triple-strand concertina.",
2860
+ spacing: 1.25,
2861
+ minSpacing: .8,
2862
+ concertina: true,
2863
+ oneSided: true,
2864
+ paired: false,
2865
+ rails: 2,
2866
+ railSeparation: 1,
2867
+ previewMarkerSize: 96
2868
+ }
2869
+ };
2870
+ function createWireObstacle(positions, options, text, context, kind) {
2871
+ const spec = WIRE_KINDS[kind];
2872
+ const projected = positions.map(([x, y]) => project(x, y));
2873
+ const path = smoothLineVerts(projected.filter((point, index) => index === 0 || Math.hypot(point[0] - projected[index - 1][0], point[1] - projected[index - 1][1]) >= 1e-6), options, 16);
2874
+ const { segments, totalLength } = polylineSegments(path);
2875
+ if (totalLength < 1e-6) return {
2876
+ type: "FeatureCollection",
2877
+ features: []
2878
+ };
2879
+ const resolution = normalizeArcResolution(options.resolution);
2880
+ const size = Math.max(EPSILON, options.markerSizePixels !== void 0 && (options.metersPerPixel ?? 0) > 0 ? options.markerSizePixels * options.metersPerPixel : options.markerSize ?? DEFAULT_MARKER_SIZE);
2881
+ const pitch = Math.max(spec.minSpacing, options.markerSpacingRatio ?? spec.spacing) * size;
2882
+ const halfWidth = .4 * size;
2883
+ const pairOffset = spec.paired ? halfWidth : 0;
2884
+ const corners = [];
2885
+ let distance = 0;
2886
+ for (let i = 0; i < segments.length - 1; i++) {
2887
+ const current = segments[i];
2888
+ const next = segments[i + 1];
2889
+ distance += current.length;
2890
+ const dot = current.along[0] * next.along[0] + current.along[1] * next.along[1];
2891
+ if (dot >= .95) continue;
2892
+ const turn = current.along[0] * next.along[1] - current.along[1] * next.along[0];
2893
+ const tangent = Math.sqrt((1 - dot) / Math.max(EPSILON, 1 + dot));
2894
+ const insideTangent = spec.oneSided && turn >= 0 ? 0 : tangent;
2895
+ const clearance = spec.concertina ? size * (insideTangent / 2 + Math.hypot(.25, insideTangent / 2)) : halfWidth + pairOffset + size * (spec.oneSided ? 1 : .5) * insideTangent;
2896
+ corners.push({
2897
+ distance,
2898
+ clearance: Math.max(clearance, pitch / 2)
2899
+ });
2900
+ }
2901
+ const inset = Math.min(totalLength / 2, halfWidth + pairOffset + (spec.rails > 0 ? .25 * size : 0));
2902
+ const centers = [];
2903
+ const spread = (start, end, count) => {
2904
+ for (let i = 0; i < count; i++) centers.push(count === 1 ? (start + end) / 2 : start + i * (end - start) / (count - 1));
2905
+ };
2906
+ if (corners.length === 0) spread(inset, totalLength - inset, Math.max(1, Math.round(totalLength / pitch)));
2907
+ else {
2908
+ let start = inset;
2909
+ for (const corner of [...corners, {
2910
+ distance: totalLength,
2911
+ clearance: inset
2912
+ }]) {
2913
+ const end = corner.distance - corner.clearance;
2914
+ if (end >= start) spread(start, end, Math.max(1, Math.round((end - start) / pitch) + 1));
2915
+ start = corner.distance + corner.clearance;
2916
+ }
2917
+ }
2918
+ const coil = spec.concertina ? Array.from({ length: resolution + 1 }, (_, index) => {
2919
+ const angle = index / resolution * Math.PI * 2;
2920
+ return [.25 * size * Math.cos(angle), -size / 2 + size / 2 * Math.sin(angle)];
2921
+ }) : [];
2922
+ const markers = [];
2923
+ for (const center of centers) for (const offset of spec.paired ? [-pairOffset, pairOffset] : [0]) {
2924
+ const { point, along, perp } = pointAlongPolyline(segments, totalLength, (center + offset) / totalLength);
2925
+ const at = (x, y) => toPosition([point[0] + along[0] * x + perp[0] * y, point[1] + along[1] * x + perp[1] * y]);
2926
+ if (spec.concertina) {
2927
+ const loop = coil.map(([x, y]) => at(x, y));
2928
+ loop[loop.length - 1] = loop[0];
2929
+ markers.push(loop);
2930
+ continue;
2931
+ }
2932
+ const lift = spec.oneSided ? -size / 2 : 0;
2933
+ markers.push([at(-halfWidth, lift - size / 2), at(halfWidth, lift + size / 2)], [at(-halfWidth, lift + size / 2), at(halfWidth, lift - size / 2)]);
2934
+ }
2935
+ const features = [];
2936
+ if (spec.rails > 0) {
2937
+ const coordinates = [path.map(toPosition)];
2938
+ if (spec.rails === 2) {
2939
+ const { right } = offsetPolylineSides(path, path.map(() => spec.railSeparation * size));
2940
+ coordinates.push(right.map(toPosition));
2941
+ }
2942
+ features.push({
2943
+ type: "Feature",
2944
+ properties: { part: "wire" },
2945
+ geometry: {
2946
+ type: "MultiLineString",
2947
+ coordinates
2948
+ }
2949
+ });
2950
+ }
2951
+ features.push({
2952
+ type: "Feature",
2953
+ properties: { part: spec.concertina ? "loops" : "crosses" },
2954
+ geometry: {
2955
+ type: "MultiLineString",
2956
+ coordinates: markers
2957
+ }
2958
+ });
2959
+ const labels = [];
2960
+ pushHostileEndLabels(labels, path, text.N, options, "", context);
2961
+ return {
2962
+ type: "FeatureCollection",
2963
+ features: [...features, ...labels]
2964
+ };
2965
+ }
2966
+ function definition$1(id, name, kind) {
2967
+ const spec = WIRE_KINDS[kind];
2968
+ return defineControlMeasure({
2969
+ metadata: {
2970
+ id,
2971
+ name,
2972
+ description: spec.description,
2973
+ entity: "Protection Lines",
2974
+ colorRole: "obstacle",
2975
+ entityType: "Wire Obstacles",
2976
+ entitySubtype: spec.subtype,
2977
+ value: spec.value,
2978
+ minCoordinates: 2,
2979
+ geometry: "line",
2980
+ geometryTypes: ["MultiLineString", "Point"],
2981
+ paints: {
2982
+ stroke: true,
2983
+ fill: "none",
2984
+ text: true
2985
+ },
2986
+ drawRule: "Line1",
2987
+ capturesLabelSize: true,
2988
+ labelGeometryUsesResolvedMetrics: ["N"],
2989
+ textAmplifiers: [HOSTILE_LINE_AMPLIFIER],
2990
+ sizePairs: [{
2991
+ id: "cross-size",
2992
+ label: "Marker size",
2993
+ pixels: "markerSizePixels",
2994
+ meters: "markerSize",
2995
+ defaultUnit: "m",
2996
+ rendering: "geometry"
2997
+ }],
2998
+ params: [
2999
+ ...SMOOTH_LINE_PARAMS,
3000
+ ...spec.concertina ? ARC_RESOLUTION_PARAMS : [],
3001
+ ...WIRE_MARKER_PARAMS,
3002
+ {
3003
+ ...WIRE_SPACING_PARAM,
3004
+ min: spec.minSpacing
3005
+ },
3006
+ LABEL_PADDING_PARAM
3007
+ ]
3008
+ },
3009
+ generator: (positions, options = {}, text = {}, context = {}) => createWireObstacle(positions, options, text, context, kind),
3010
+ defaultOptions: {
3011
+ ...spec.concertina ? { resolution: 64 } : {},
3012
+ markerSize: DEFAULT_MARKER_SIZE,
3013
+ markerSizePixels: DEFAULT_MARKER_SIZE_PIXELS,
3014
+ markerSpacingRatio: spec.spacing,
3015
+ smooth: false,
3016
+ smoothResolution: 16
3017
+ },
3018
+ rule: line1DrawRule,
3019
+ previewSample: {
3020
+ controlPoints: [[1, 0], [-1, 0]],
3021
+ options: { markerSize: spec.previewMarkerSize }
3022
+ }
3023
+ });
3024
+ }
3025
+ const WIRE_OBSTACLE_UNSPECIFIED = definition$1("wire-obstacle-unspecified", "Wire Obstacles – Unspecified", "unspecified");
3026
+ const SINGLE_FENCE = definition$1("single-fence", "Single Fence", "single");
3027
+ const DOUBLE_FENCE = definition$1("double-fence", "Double Fence", "double");
3028
+ const DOUBLE_APRON_FENCE = definition$1("double-apron-fence", "Double Apron Fence", "apron");
3029
+ const LOW_WIRE_FENCE = definition$1("low-wire-fence", "Low Wire Fence", "low");
3030
+ const HIGH_WIRE_FENCE = definition$1("high-wire-fence", "High Wire Fence", "high");
3031
+ const SINGLE_CONCERTINA = definition$1("single-concertina", "Single Concertina", "concertina");
3032
+ const DOUBLE_STRAND_CONCERTINA = definition$1("double-strand-concertina", "Double Strand Concertina", "double-concertina");
3033
+ const TRIPLE_STRAND_CONCERTINA = definition$1("triple-strand-concertina", "Triple Strand Concertina", "triple-concertina");
2624
3034
  //#endregion
2625
3035
  //#region src/generators/cm34-mission-tasks/shared.ts
2626
3036
  /**
@@ -6467,78 +6877,6 @@ const ASSEMBLY_AREA = defineControlMeasure({
6467
6877
  }
6468
6878
  });
6469
6879
  //#endregion
6470
- //#region src/generators/cm29-protection-lines/params.ts
6471
- const FORTIFIED_PARAMS = [
6472
- ...SMOOTH_LINE_PARAMS,
6473
- {
6474
- key: "toothSizePixels",
6475
- label: "Tooth size",
6476
- description: "Side length of the square crenellations",
6477
- type: "number",
6478
- min: 5,
6479
- max: 50,
6480
- step: 1,
6481
- unit: "px"
6482
- },
6483
- {
6484
- key: "toothSize",
6485
- label: "Tooth size",
6486
- description: "Side length of the square crenellations",
6487
- type: "number",
6488
- min: 1,
6489
- max: 500,
6490
- step: 1,
6491
- unit: "m"
6492
- },
6493
- LABEL_PADDING_PARAM
6494
- ];
6495
- const ANTITANK_DITCH_PARAMS = [
6496
- ...SMOOTH_LINE_PARAMS,
6497
- {
6498
- key: "toothHeightPixels",
6499
- label: "Tooth height",
6500
- description: "Perpendicular depth of each triangular tooth",
6501
- type: "number",
6502
- min: 5,
6503
- max: 50,
6504
- step: 1,
6505
- unit: "px"
6506
- },
6507
- {
6508
- key: "toothHeight",
6509
- label: "Tooth height",
6510
- description: "Perpendicular depth of each triangular tooth",
6511
- type: "number",
6512
- min: 1,
6513
- max: 500,
6514
- step: 1,
6515
- unit: "m"
6516
- },
6517
- {
6518
- key: "toothWidthRatio",
6519
- presentationTier: "advanced",
6520
- label: "Tooth width ratio",
6521
- description: "Tooth base span as a multiple of height",
6522
- type: "number",
6523
- min: .5,
6524
- max: 3,
6525
- step: .05,
6526
- unit: "x"
6527
- },
6528
- LABEL_PADDING_PARAM
6529
- ];
6530
- const ANTITANK_WALL_PARAMS = [...ANTITANK_DITCH_PARAMS, {
6531
- key: "toothSpacingRatio",
6532
- presentationTier: "advanced",
6533
- label: "Tooth spacing ratio",
6534
- description: "Gap between teeth as a multiple of base width",
6535
- type: "number",
6536
- min: 0,
6537
- max: 3,
6538
- step: .05,
6539
- unit: "x"
6540
- }];
6541
- //#endregion
6542
6880
  //#region src/generators/cm29-protection-lines/antitankDitch.ts
6543
6881
  const DEFAULT_TOOTH_HEIGHT = 50;
6544
6882
  const DEFAULT_TOOTH_HEIGHT_PIXELS = 10;
@@ -6565,6 +6903,7 @@ const ANTITANK_DITCH_UNDER_CONSTRUCTION_METADATA = {
6565
6903
  name: "Ditch - Under Construction",
6566
6904
  description: "Antitank ditch obstacle under construction.",
6567
6905
  entity: "Protection Lines",
6906
+ colorRole: "obstacle",
6568
6907
  entityType: "Antitank Obstacles",
6569
6908
  entitySubtype: "Ditch - Under Construction",
6570
6909
  value: "290201",
@@ -6588,6 +6927,7 @@ const ANTITANK_DITCH_COMPLETED_METADATA = {
6588
6927
  name: "Ditch - Completed",
6589
6928
  description: "Completed antitank ditch obstacle.",
6590
6929
  entity: "Protection Lines",
6930
+ colorRole: "obstacle",
6591
6931
  entityType: "Antitank Obstacles",
6592
6932
  entitySubtype: "Ditch - Completed",
6593
6933
  value: "290202",
@@ -6722,6 +7062,7 @@ const ANTITANK_WALL_METADATA = {
6722
7062
  name: "Antitank Wall",
6723
7063
  description: "Antitank wall obstacle.",
6724
7064
  entity: "Protection Lines",
7065
+ colorRole: "obstacle",
6725
7066
  entityType: "Antitank Obstacles",
6726
7067
  entitySubtype: "Antitank Wall",
6727
7068
  value: "290204",
@@ -7574,6 +7915,7 @@ const BLOCK_METADATA = {
7574
7915
  name: "Block",
7575
7916
  description: "Integrates fires and obstacles to stop an attacker along an avenue of approach or prevent it from passing through an engagement area. Establishes a limit of enemy advance.",
7576
7917
  entity: "Protection Areas",
7918
+ colorRole: "obstacle",
7577
7919
  entityType: "Obstacle Effects",
7578
7920
  entitySubtype: "Block",
7579
7921
  value: "270501",
@@ -9571,20 +9913,6 @@ function normalizeSmoothResolution(value) {
9571
9913
  if (!Number.isFinite(value)) return DEFAULT_SMOOTH_RESOLUTION$3;
9572
9914
  return Math.min(MAX_SMOOTH_RESOLUTION, Math.max(MIN_SMOOTH_RESOLUTION, Math.round(value)));
9573
9915
  }
9574
- function normalizeArcResolution(value) {
9575
- if (typeof value !== "number" || !Number.isFinite(value)) return 64;
9576
- return Math.min(360, Math.max(4, Math.round(value)));
9577
- }
9578
- const ARC_RESOLUTION_PARAMS = [{
9579
- key: "resolution",
9580
- presentationTier: "advanced",
9581
- label: "Resolution",
9582
- description: "Number of segments in a full 360° sweep.",
9583
- type: "number",
9584
- min: 4,
9585
- max: 360,
9586
- step: 1
9587
- }];
9588
9916
  //#endregion
9589
9917
  //#region src/generators/cm99-generic-graphics/params.ts
9590
9918
  const SMOOTH_PATH_PARAMS = [{
@@ -11435,7 +11763,7 @@ function createSecurityTaskSymbol(coordinates, config, options = {}) {
11435
11763
  ...options
11436
11764
  };
11437
11765
  const arrowOptions = resolveTacticalArrowOptions(resolved);
11438
- const lines = [createTacticalArrow(p2, p1, p3, arrowOptions), createTacticalArrow(p3, p4, p2, arrowOptions)].flatMap((arrow) => arrow ?? []);
11766
+ const lines = [createTacticalArrow(p2, p1, arrowOptions, -1), createTacticalArrow(p3, p4, arrowOptions, 1)].flatMap((arrow) => arrow ?? []);
11439
11767
  if (lines.length === 0) return {
11440
11768
  type: "FeatureCollection",
11441
11769
  features: []
@@ -11465,13 +11793,12 @@ function createSecurityTaskSymbol(coordinates, config, options = {}) {
11465
11793
  };
11466
11794
  }
11467
11795
  /** A Search Area-style lightning shaft with a Principal Direction of Fire open head. */
11468
- function createTacticalArrow(origin, tip, opposite, options) {
11796
+ function createTacticalArrow(origin, tip, options, bendSide) {
11469
11797
  const leg = vecSub(tip, origin);
11470
11798
  const totalLegLength = vecMag(leg);
11471
11799
  if (totalLegLength < 1e-6) return void 0;
11472
11800
  const legDirection = vecNorm(leg);
11473
- let normal = [-legDirection[1], legDirection[0]];
11474
- if (vecDot(normal, vecSub(opposite, origin)) > 0) normal = vecScale(normal, -1);
11801
+ const normal = [-legDirection[1] * bendSide, legDirection[0] * bendSide];
11475
11802
  const amplitude = totalLegLength * options.zigzagAmplitudeRatio;
11476
11803
  const zigzagPointA = vecAdd(vecAdd(origin, vecScale(leg, options.zigzagStart)), vecScale(normal, amplitude));
11477
11804
  const zigzagPointB = vecAdd(vecAdd(origin, vecScale(leg, options.zigzagEnd)), vecScale(normal, -amplitude));
@@ -12585,6 +12912,7 @@ const DISRUPT_METADATA = {
12585
12912
  name: "Disrupt",
12586
12913
  description: "Integrates fires and obstacles to break up an enemy's formation and tempo, interrupt its timetable, commit breaching assets prematurely, and force a piecemeal attack.",
12587
12914
  entity: "Protection Areas",
12915
+ colorRole: "obstacle",
12588
12916
  entityType: "Obstacle Effects",
12589
12917
  entitySubtype: "Disrupt",
12590
12918
  value: "270502",
@@ -13242,6 +13570,7 @@ const FIX_METADATA = {
13242
13570
  name: "Fix",
13243
13571
  description: "Slows an attacker within a specified area, giving friendly forces time to acquire, target, and destroy it throughout the area's depth.",
13244
13572
  entity: "Protection Areas",
13573
+ colorRole: "obstacle",
13245
13574
  entityType: "Obstacle Effects",
13246
13575
  entitySubtype: "Fix",
13247
13576
  value: "270503",
@@ -16346,6 +16675,7 @@ const TURN_METADATA = {
16346
16675
  name: "Turn",
16347
16676
  description: "Diverts an enemy formation from one avenue of approach to another or into an engagement area. The arrow indicates the desired direction of turn.",
16348
16677
  entity: "Protection Areas",
16678
+ colorRole: "obstacle",
16349
16679
  entityType: "Obstacle Effects",
16350
16680
  entitySubtype: "Turn",
16351
16681
  value: "270504",
@@ -17038,6 +17368,7 @@ const MINEFIELD_METADATA = {
17038
17368
  name: "Minefield",
17039
17369
  description: "A static point symbol identifying a minefield and its mine type.",
17040
17370
  entity: "Protection Areas",
17371
+ colorRole: "obstacle",
17041
17372
  entityType: "Minefield Symbol",
17042
17373
  entitySubtype: "Minefield (Static)",
17043
17374
  value: "270701",
@@ -17337,6 +17668,207 @@ const MINEFIELD = defineControlMeasure({
17337
17668
  }
17338
17669
  });
17339
17670
  //#endregion
17671
+ //#region src/generators/cm27-protection-areas/obstacle-areas.ts
17672
+ const DEFAULT_OBSTACLE_AREA_OPTIONS = {
17673
+ toothSize: 50,
17674
+ toothSizePixels: 10,
17675
+ smooth: false,
17676
+ smoothResolution: 16
17677
+ };
17678
+ /** Preserve control-point corners and keep tooth direction independent of winding. */
17679
+ function toothedRing(ring, size, inward) {
17680
+ const side = (ringSignedArea2(ring) >= 0 ? -1 : 1) * (inward ? -1 : 1);
17681
+ const width = size * (inward ? 1 : .8);
17682
+ const pitch = size * (inward ? 2.4 : 1.6);
17683
+ const result = [ring[0]];
17684
+ for (let i = 1; i < ring.length; i++) {
17685
+ const a = ring[i - 1];
17686
+ const b = ring[i];
17687
+ const dx = b[0] - a[0];
17688
+ const dy = b[1] - a[1];
17689
+ const length = Math.hypot(dx, dy);
17690
+ if (length < 1e-6) continue;
17691
+ const margin = inward ? Math.min(length / 4, .75 * size) : 0;
17692
+ const available = length - 2 * margin;
17693
+ const count = Math.max(1, Math.round(available / pitch));
17694
+ const fittedWidth = Math.min(width, available / count);
17695
+ const offsets = [
17696
+ -fittedWidth / 2,
17697
+ 0,
17698
+ fittedWidth / 2
17699
+ ];
17700
+ for (let tooth = 0; tooth < count; tooth++) {
17701
+ const center = margin + (tooth + .5) / count * available;
17702
+ for (const offset of offsets) {
17703
+ const t = (center + offset) / length;
17704
+ const height = offset === 0 ? side * size : 0;
17705
+ result.push([a[0] + dx * t - dy / length * height, a[1] + dy * t + dx / length * height]);
17706
+ }
17707
+ }
17708
+ result.push(b);
17709
+ }
17710
+ result[result.length - 1] = result[0];
17711
+ return result;
17712
+ }
17713
+ const OBSTACLE_AREA_KINDS = {
17714
+ belt: {
17715
+ inward: false,
17716
+ previewToothSize: 170,
17717
+ description: "A brigade-level control measure within an obstacle zone that limits friendly obstacle employment and focuses the defense."
17718
+ },
17719
+ zone: {
17720
+ inward: false,
17721
+ previewToothSize: 140,
17722
+ description: "A division-level control measure designating land areas where subordinate units are authorized to employ tactical obstacles."
17723
+ },
17724
+ free: {
17725
+ inward: true,
17726
+ previewToothSize: 130,
17727
+ description: "An area designated to remain completely free of reinforcing obstacles."
17728
+ },
17729
+ restricted: {
17730
+ inward: true,
17731
+ previewToothSize: 130,
17732
+ description: "A control measure limiting the types or number of obstacles permitted within an area."
17733
+ }
17734
+ };
17735
+ function createObstacleArea(positions, options, text, context, kind) {
17736
+ const ring = buildClosedRing(positions, options.smooth ?? false, options.smoothResolution ?? 16, 16);
17737
+ const size = calculateAntitankDitchHeight({
17738
+ toothHeight: options.toothSize,
17739
+ toothHeightPixels: options.toothSizePixels,
17740
+ metersPerPixel: options.metersPerPixel
17741
+ });
17742
+ const { inward } = OBSTACLE_AREA_KINDS[kind];
17743
+ const boundary = toothedRing(options.smooth ? evenlySpacePath(ring, inward ? 8 * size : 4 * size) : ring, size, inward);
17744
+ const labels = [];
17745
+ const [cx, cy] = ringLabelAnchor(ring);
17746
+ const hasWindow = inward && Boolean(text.W || text.W1);
17747
+ const rows = Number(kind === "free") + Number(Boolean(text.T)) + Number(hasWindow);
17748
+ const pitch = resolveLabelOffsetMeters(options, 1.2 + Math.max(0, options.labelPadding ?? 0));
17749
+ let row = 0;
17750
+ const rowY = () => cy + ((rows - 1) / 2 - row++) * pitch;
17751
+ const sizeProps = labelSizeProps(options);
17752
+ const addLabel = (x, y, value, props = {}) => {
17753
+ pushLabel(labels, [x, y], value, HORIZONTAL_LABEL_ROTATION, sizeProps, props.anchor, props.field, props.key);
17754
+ if (props.labelPlacement === false) labels[labels.length - 1].properties.labelPlacement = false;
17755
+ };
17756
+ if (kind === "free") addLabel(cx, rowY(), "FREE", { key: "label:free" });
17757
+ if (text.T) addLabel(cx, rowY(), text.T, { field: "T" });
17758
+ if (hasWindow) {
17759
+ const y = rowY();
17760
+ if (text.W && text.W1) {
17761
+ const gap = resolveLabelOffsetMeters(options, 1.8);
17762
+ addLabel(cx - gap, y, text.W, {
17763
+ field: "W",
17764
+ anchor: "end"
17765
+ });
17766
+ addLabel(cx, y, "–", { labelPlacement: false });
17767
+ addLabel(cx + gap, y, text.W1, {
17768
+ field: "W1",
17769
+ anchor: "start"
17770
+ });
17771
+ } else if (text.W) addLabel(cx, y, text.W, { field: "W" });
17772
+ else if (text.W1) addLabel(cx, y, text.W1, { field: "W1" });
17773
+ }
17774
+ const gaps = pushAreaLabels(labels, ring, { hostile: text.N }, options, context.amplifierPlacements, {
17775
+ ringVerts: boundary,
17776
+ extraClearanceMeters: size
17777
+ }, context);
17778
+ if (kind === "restricted") for (const label of labels) label.properties.textBackground = true;
17779
+ return {
17780
+ type: "FeatureCollection",
17781
+ features: [...kind === "restricted" ? patternedAreaFeatures(boundary, gaps, "reverse-hatch") : [boundaryFeature(boundary, gaps)], ...labels]
17782
+ };
17783
+ }
17784
+ const COMMON_METADATA$1 = {
17785
+ entity: "Protection Areas",
17786
+ colorRole: "obstacle",
17787
+ minCoordinates: 3,
17788
+ geometry: "area",
17789
+ geometryTypes: [
17790
+ "Polygon",
17791
+ "MultiLineString",
17792
+ "Point"
17793
+ ],
17794
+ drawRule: "Area1",
17795
+ capturesLabelSize: true,
17796
+ labelGeometryUsesResolvedMetrics: ["N"],
17797
+ sizePairs: [TOOTH_SIZE_SIZE_PAIR],
17798
+ params: [
17799
+ {
17800
+ key: "toothSize",
17801
+ label: "Tooth size",
17802
+ description: "Boundary tooth height in meters",
17803
+ type: "number",
17804
+ min: 1,
17805
+ max: 500,
17806
+ step: 1,
17807
+ unit: "m"
17808
+ },
17809
+ {
17810
+ key: "toothSizePixels",
17811
+ label: "Tooth size",
17812
+ description: "Boundary tooth height in pixels",
17813
+ type: "number",
17814
+ min: 1,
17815
+ max: 50,
17816
+ step: 1,
17817
+ unit: "px"
17818
+ },
17819
+ ...AREA_SMOOTH_PARAMS,
17820
+ AREA_LABEL_PADDING_PARAM
17821
+ ]
17822
+ };
17823
+ function definition(id, name, value, kind) {
17824
+ const { inward, previewToothSize, description } = OBSTACLE_AREA_KINDS[kind];
17825
+ return defineControlMeasure({
17826
+ metadata: {
17827
+ ...COMMON_METADATA$1,
17828
+ id,
17829
+ name,
17830
+ value,
17831
+ entityType: name,
17832
+ description,
17833
+ paints: {
17834
+ stroke: true,
17835
+ fill: kind === "restricted" ? "fixed" : "none",
17836
+ text: true
17837
+ },
17838
+ textAmplifiers: inward ? AREA_TEXT_AMPLIFIERS_NO_ADDITIONAL_INFO : AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
17839
+ },
17840
+ generator: (positions, options = {}, text = {}, context = {}) => createObstacleArea(positions, options, text, context, kind),
17841
+ defaultOptions: DEFAULT_OBSTACLE_AREA_OPTIONS,
17842
+ rule: area1DrawRule,
17843
+ previewSample: {
17844
+ controlPoints: kind === "belt" ? [
17845
+ [-1, -.5],
17846
+ [1, -.5],
17847
+ [1, .5],
17848
+ [-1, .5]
17849
+ ] : [
17850
+ [-1, -.7],
17851
+ [1, -.7],
17852
+ [.95, .25],
17853
+ [-1, .8]
17854
+ ],
17855
+ options: {
17856
+ toothSize: previewToothSize,
17857
+ labelSize: inward ? 70 : 96
17858
+ },
17859
+ textAmplifiers: inward ? {
17860
+ T: "T",
17861
+ W: "W",
17862
+ W1: "W1"
17863
+ } : { T: "T" }
17864
+ }
17865
+ });
17866
+ }
17867
+ const OBSTACLE_BELT = definition("obstacle-belt", "Obstacle Belt", "270100", "belt");
17868
+ const OBSTACLE_ZONE = definition("obstacle-zone", "Obstacle Zone", "270200", "zone");
17869
+ const OBSTACLE_FREE_ZONE = definition("obstacle-free-zone", "Obstacle Free Zone", "270300", "free");
17870
+ const OBSTACLE_RESTRICTED_ZONE = definition("obstacle-restricted-zone", "Obstacle Restricted Zone", "270400", "restricted");
17871
+ //#endregion
17340
17872
  //#region src/generators/cm27-protection-areas/mine-areas.ts
17341
17873
  const DEFAULT_MINE_AREA_OPTIONS = {
17342
17874
  mineType: "unspecified",
@@ -17345,6 +17877,7 @@ const DEFAULT_MINE_AREA_OPTIONS = {
17345
17877
  };
17346
17878
  const COMMON_METADATA = {
17347
17879
  entity: "Protection Areas",
17880
+ colorRole: "obstacle",
17348
17881
  minCoordinates: 3,
17349
17882
  geometry: "area",
17350
17883
  geometryTypes: [
@@ -17516,6 +18049,7 @@ const OBSTACLE_BYPASS_DIFFICULT_METADATA = {
17516
18049
  name: "Obstacle Bypass Difficult",
17517
18050
  description: "Obstacle bypass (difficult) with zigzag rear line and arrowheads.",
17518
18051
  entity: "Protection Areas",
18052
+ colorRole: "obstacle",
17519
18053
  entityType: "Obstacle Bypass",
17520
18054
  entitySubtype: "Difficult",
17521
18055
  value: "270602",
@@ -17656,6 +18190,7 @@ const OBSTACLE_BYPASS_EASY_METADATA = {
17656
18190
  name: "Obstacle Bypass Easy",
17657
18191
  description: "Obstacle bypass (easy) with parallel rails and two arrowheads.",
17658
18192
  entity: "Protection Areas",
18193
+ colorRole: "obstacle",
17659
18194
  entityType: "Obstacle Bypass",
17660
18195
  entitySubtype: "Easy",
17661
18196
  value: "270601",
@@ -17770,6 +18305,7 @@ const OBSTACLE_BYPASS_IMPOSSIBLE_METADATA = {
17770
18305
  name: "Obstacle Bypass Impossible",
17771
18306
  description: "Obstacle bypass (impossible) with rear barrier marks and arrowheads.",
17772
18307
  entity: "Protection Areas",
18308
+ colorRole: "obstacle",
17773
18309
  entityType: "Obstacle Bypass",
17774
18310
  entitySubtype: "Impossible",
17775
18311
  value: "270603",
@@ -18353,14 +18889,12 @@ function createSearchArea(coordinates, options = {}) {
18353
18889
  ];
18354
18890
  const shaftSegments = [];
18355
18891
  const headPolygons = [];
18356
- const addLeg = (origin, tip, opposite) => {
18892
+ const addLeg = (origin, tip, bendSide) => {
18357
18893
  const v = vecSub(tip, origin);
18358
18894
  const totalLen = vecMag(v);
18359
18895
  if (totalLen < 1e-6) return;
18360
18896
  const u = vecNorm(v);
18361
- let n = [-u[1], u[0]];
18362
- const toOpp = vecSub(opposite, origin);
18363
- if (vecDot(n, toOpp) > 0) n = vecScale(n, -1);
18897
+ const n = [-u[1] * bendSide, u[0] * bendSide];
18364
18898
  const offset = totalLen * safeZigzagAmplitudeRatio;
18365
18899
  const ptA = vecAdd(vecAdd(origin, vecScale(v, safeZigzagStart)), vecScale(n, offset));
18366
18900
  const ptB = vecAdd(vecAdd(origin, vecScale(v, safeZigzagEnd)), vecScale(n, -offset));
@@ -18387,8 +18921,8 @@ function createSearchArea(coordinates, options = {}) {
18387
18921
  tip
18388
18922
  ].map((p) => unproject(p[0], p[1]))]);
18389
18923
  };
18390
- addLeg(p1, p2, p3);
18391
- addLeg(p1, p3, p2);
18924
+ addLeg(p1, p2, 1);
18925
+ addLeg(p1, p3, -1);
18392
18926
  const features = [];
18393
18927
  if (shaftSegments.length > 0) features.push({
18394
18928
  type: "Feature",
@@ -19109,6 +19643,10 @@ const DEFINITIONS = {
19109
19643
  "no-fire-area-irregular": NO_FIRE_AREA_IRREGULAR,
19110
19644
  "minefield-dynamic": DYNAMIC_MINEFIELD,
19111
19645
  "mined-area": MINED_AREA,
19646
+ "obstacle-belt": OBSTACLE_BELT,
19647
+ "obstacle-zone": OBSTACLE_ZONE,
19648
+ "obstacle-free-zone": OBSTACLE_FREE_ZONE,
19649
+ "obstacle-restricted-zone": OBSTACLE_RESTRICTED_ZONE,
19112
19650
  "pickup-zone": PICKUP_ZONE,
19113
19651
  "assault-position": ASSAULT_POSITION,
19114
19652
  "attack-position": ATTACK_POSITION,
@@ -19193,6 +19731,15 @@ const DEFINITIONS = {
19193
19731
  "antitank-ditch-under-construction": ANTITANK_DITCH_UNDER_CONSTRUCTION,
19194
19732
  "antitank-ditch-completed": ANTITANK_DITCH_COMPLETED,
19195
19733
  "antitank-wall": ANTITANK_WALL,
19734
+ "wire-obstacle-unspecified": WIRE_OBSTACLE_UNSPECIFIED,
19735
+ "single-fence": SINGLE_FENCE,
19736
+ "double-fence": DOUBLE_FENCE,
19737
+ "double-apron-fence": DOUBLE_APRON_FENCE,
19738
+ "low-wire-fence": LOW_WIRE_FENCE,
19739
+ "high-wire-fence": HIGH_WIRE_FENCE,
19740
+ "single-concertina": SINGLE_CONCERTINA,
19741
+ "double-strand-concertina": DOUBLE_STRAND_CONCERTINA,
19742
+ "triple-strand-concertina": TRIPLE_STRAND_CONCERTINA,
19196
19743
  "fortified-line": FORTIFIED_LINE,
19197
19744
  "fortified-area": FORTIFIED_AREA,
19198
19745
  "frontal-attack": FRONTAL_ATTACK,
@@ -19376,8 +19923,8 @@ function resolveStyleHints(graphicsStyle, measureStyle, generatorStyle) {
19376
19923
  *
19377
19924
  * A control measure is monocolor: the single `color` paints both stroke and
19378
19925
  * fill. `strokeColor` / `fillColor` are optional per-channel overrides, and
19379
- * `DEFAULT_SYMBOL_COLOR` is the ultimate fallback so a zero-config render is
19380
- * still black and filled parts still render filled.
19926
+ * the measure's doctrinal default is the fallback (otherwise black), so
19927
+ * filled parts still render filled without host styling.
19381
19928
  *
19382
19929
  * `filled` is the generator's intrinsic, color-free marker that a part is a
19383
19930
  * solid shape. Non-filled parts never carry a `fillColor` — filledness is
@@ -19385,37 +19932,45 @@ function resolveStyleHints(graphicsStyle, measureStyle, generatorStyle) {
19385
19932
  * input-only and is stripped from the result.
19386
19933
  *
19387
19934
  * Also folds the whole-graphic `opacity` multiplier (ADR-0040) into the
19388
- * resolved `strokeColor` / `fillColor` alpha, after the `DEFAULT_SYMBOL_COLOR`
19935
+ * resolved `strokeColor` / `fillColor` alpha, after the default-color
19389
19936
  * fallback so a dimmed zero-config render dims too. `opacity` is clamped to
19390
19937
  * `[0, 1]` (non-finite treated as absent) and is never present on the
19391
19938
  * returned `StyleHints` — it is consumed here, not passed through.
19392
19939
  *
19393
19940
  * Pure: the input is not mutated; a fresh object is always returned.
19394
19941
  */
19395
- function resolveSymbolColor(merged, filled) {
19942
+ function resolveSymbolColor(merged, filled, defaultColor = DEFAULT_SYMBOL_COLOR) {
19396
19943
  const { color, strokeColor, fillColor, fillPattern, opacity, ...rest } = merged ?? {};
19397
19944
  delete rest.textHalo;
19398
- const rawStroke = firstColor(strokeColor, color);
19945
+ const rawStroke = firstColor(defaultColor, strokeColor, color);
19399
19946
  const resolvedStroke = foldGraphicOpacity(rawStroke, opacity);
19400
19947
  const resolved = {
19401
19948
  ...rest,
19402
19949
  strokeColor: resolvedStroke
19403
19950
  };
19404
19951
  if (filled) {
19405
- const rawFill = firstColor(fillColor, color);
19952
+ const rawFill = firstColor(defaultColor, fillColor, color);
19406
19953
  resolved.fillColor = rawFill === rawStroke ? resolvedStroke : foldGraphicOpacity(rawFill, opacity);
19407
19954
  if (fillPattern !== void 0) resolved.fillPattern = fillPattern;
19408
19955
  }
19409
19956
  return resolved;
19410
19957
  }
19411
19958
  /**
19412
- * First usable color among the candidates, else `DEFAULT_SYMBOL_COLOR`. An
19413
- * empty string is treated as "not set" (e.g. a cleared color input) rather
19414
- * than kept as an invalid paint, so the fallback chain still reaches black.
19959
+ * Resolve a doctrinal color role. This is the fallback *below* every authored
19960
+ * style layer, so it never looks at `graphicsStyle` / `cm.style`.
19961
+ */
19962
+ function resolveDefaultSymbolColor(role, colors) {
19963
+ return role === "obstacle" ? colors?.obstacle || MIL_STD_GREEN.neon : DEFAULT_SYMBOL_COLOR;
19964
+ }
19965
+ /**
19966
+ * First usable color among the candidates, else `fallback` — the caller's
19967
+ * already-resolved default, so the ultimate fallback is decided in one place.
19968
+ * An empty string is treated as "not set" (e.g. a cleared color input) rather
19969
+ * than kept as an invalid paint.
19415
19970
  */
19416
- function firstColor(...candidates) {
19971
+ function firstColor(fallback, ...candidates) {
19417
19972
  for (const candidate of candidates) if (candidate !== void 0 && candidate !== "") return candidate;
19418
- return DEFAULT_SYMBOL_COLOR;
19973
+ return fallback;
19419
19974
  }
19420
19975
  function applyLayer(target, layer) {
19421
19976
  if (!layer) return;
@@ -19435,6 +19990,7 @@ function applyLayer(target, layer) {
19435
19990
  */
19436
19991
  function renderControlMeasure(cm, opts) {
19437
19992
  const raw = dispatchControlMeasure(cm, opts);
19993
+ const defaultSymbolColor = resolveDefaultSymbolColor(DEFINITIONS[cm.kind].metadata.colorRole, opts?.symbolColors);
19438
19994
  const generatedLabelOrdinals = /* @__PURE__ */ new Map();
19439
19995
  const features = raw.features.map((feature, index) => {
19440
19996
  const sourceProps = feature.properties ?? {};
@@ -19442,7 +19998,7 @@ function renderControlMeasure(cm, opts) {
19442
19998
  const isGeneratedLabel = sourceProps.labelPlacement !== false && !(typeof sourceProps.amplifierField === "string" && canonicalTextAmplifierKey(sourceProps.amplifierField)) && !(typeof sourceProps.labelPlacementKey === "string" && sourceProps.labelPlacementKey.length > 0) && typeof sourceProps.text === "string" && feature.geometry.type === "Point";
19443
19999
  const generatedLabelOrdinal = isGeneratedLabel ? generatedLabelOrdinals.get(part) ?? 0 : index;
19444
20000
  if (isGeneratedLabel) generatedLabelOrdinals.set(part, generatedLabelOrdinal + 1);
19445
- return normalizeFeature(cm.id, feature, index, generatedLabelOrdinal, cm.style, opts?.graphicsStyle, cm.amplifierPlacements);
20001
+ return normalizeFeature(cm.id, feature, index, generatedLabelOrdinal, cm.style, opts?.graphicsStyle, cm.amplifierPlacements, defaultSymbolColor);
19446
20002
  });
19447
20003
  const bbox = computeBBox(features);
19448
20004
  return {
@@ -19526,13 +20082,13 @@ function resolveLabelPlacementKey(sourceProps, amplifierField, text, geometryTyp
19526
20082
  if (amplifierField) return amplifierField;
19527
20083
  if (text !== void 0 && geometryType === "Point") return `label:${part}:${generatedLabelOrdinal}`;
19528
20084
  }
19529
- function normalizeFeature(id, feature, index, generatedLabelOrdinal, measureStyle, graphicsStyle, amplifierPlacements) {
20085
+ function normalizeFeature(id, feature, index, generatedLabelOrdinal, measureStyle, graphicsStyle, amplifierPlacements, defaultSymbolColor) {
19530
20086
  const sourceProps = feature.properties ?? {};
19531
20087
  const part = typeof sourceProps.part === "string" && sourceProps.part.length > 0 ? sourceProps.part : "main";
19532
20088
  const generatorStyle = typeof sourceProps.style === "object" && sourceProps.style !== null ? sourceProps.style : void 0;
19533
20089
  const filled = sourceProps.fill === true;
19534
20090
  const merged = resolveStyleHints(graphicsStyle, measureStyle, generatorStyle);
19535
- const style = resolveSymbolColor(merged, filled);
20091
+ const style = resolveSymbolColor(merged, filled, defaultSymbolColor);
19536
20092
  const text = typeof sourceProps.text === "string" ? sourceProps.text : void 0;
19537
20093
  const rotation = typeof sourceProps.rotation === "number" ? sourceProps.rotation : void 0;
19538
20094
  const textSizePixels = typeof sourceProps.textSizePixels === "number" ? sourceProps.textSizePixels : void 0;
@@ -19623,4 +20179,4 @@ function assertNever(value) {
19623
20179
  throw new Error(`Unhandled control measure kind: ${String(value)}`);
19624
20180
  }
19625
20181
  //#endregion
19626
- export { DEFAULT_PHASE_LINE_OPTIONS as $, DEFAULT_LABEL_SIZE_CLAMP_CSS_PIXELS as $n, DEFAULT_DIRECTION_OF_SUPPORTING_ATTACK_OPTIONS as $t, DEFAULT_SECURE_OPTIONS as A, secureDrawRule as An, DEFAULT_GENERIC_CIRCLE_OPTIONS as At, DEFAULT_FORTIFIED_LINE_OPTIONS as B, getMidpointPerpendicularSignedDistance as Bn, DEFAULT_BLOCK_ARROW_OPTIONS as Bt, DEFAULT_LANDING_ZONE_OPTIONS as C, line1DrawRule as Cn, RADAR_SEARCH_DOCTRINE_FILL_COLOR as Ct, DEFAULT_WITHDRAW_OPTIONS as D, sectorDrawRule as Dn, DEFAULT_GENERIC_RECTANGLE_OPTIONS as Dt, DEFAULT_WITHDRAW_UNDER_PRESSURE_OPTIONS as E, dynamicPointDrawRule as En, DEFAULT_GENERIC_SECTOR_OPTIONS as Et, DEFAULT_PENETRATE_OPTIONS as F, centerRadiusDrawRule as Fn, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as Ft, DEFAULT_LIMIT_OF_ADVANCE_OPTIONS as G, project as Gn, DEFAULT_ATTACK_BY_FIRE_OPTIONS as Gt, PROBABLE_LINE_OF_DEPLOYMENT_DASH as H, snapToMidpointPerpendicular as Hn, DEFAULT_CONTAIN_OPTIONS as Ht, DEFAULT_OCCUPY_OPTIONS as I, containDrawRule as In, DEFAULT_GENERIC_C2_LINE_OPTIONS as It, DEFAULT_FORWARD_EDGE_OF_BATTLE_AREA_OPTIONS as J, EPSILON as Jn, DEFAULT_ASSEMBLY_AREA_OPTIONS as Jt, DEFAULT_BATTLE_HANDOVER_LINE_OPTIONS as K, sphericalBearing as Kn, DEFAULT_ANTITANK_WALL_OPTIONS as Kt, DEFAULT_MOVEMENT_TO_CONTACT_OPTIONS as L, area27DrawRule as Ln, DEFAULT_ENGINEER_WORK_LINE_OPTIONS as Lt, DEFAULT_RETIRE_OPTIONS as M, occupyDrawRule as Mn, DEFAULT_CANALIZE_OPTIONS as Mt, DEFAULT_RELIEF_IN_PLACE_OPTIONS as N, disruptDrawRule as Nn, DEFAULT_BYPASS_OPTIONS as Nt, DEFAULT_EVACUATE_OPTIONS as O, staticPointDrawRule as On, DEFAULT_GENERIC_POLYGON_OPTIONS as Ot, DEFAULT_REARWARD_PASSAGE_OF_LINES_OPTIONS as P, blockDrawRule as Pn, DEFAULT_BREACH_OPTIONS as Pt, DEFAULT_BRIDGEHEAD_LINE_OPTIONS as Q, DEFAULT_LABEL_HEIGHT_CSS_PIXELS as Qn, DEFAULT_AREA_DEFENSE_OPTIONS as Qt, DEFAULT_FRONTAL_ATTACK_OPTIONS as R, computeDefaultMidpointPerpendicularPoint as Rn, DEFAULT_LIGHT_LINE_OPTIONS as Rt, DEFAULT_MAIN_ATTACK_OPTIONS as S, turnDrawRule as Sn, DEFAULT_RADAR_SEARCH_DOCTRINE_OPTIONS as St, DEFAULT_TURN_OPTIONS as T, attackByFireDrawRule as Tn, DEFAULT_GENERIC_TEXT_OPTIONS as Tt, DEFAULT_LINE_OF_DEPARTURE_CONTACT_OPTIONS as U, createBaselineFrame as Un, DEFAULT_BATTLE_POSITION_OPTIONS as Ut, DEFAULT_PROBABLE_LINE_OF_DEPLOYMENT_OPTIONS as V, pointOnMidpointPerpendicularAxis as Vn, DEFAULT_BLOCK_OPTIONS as Vt, DEFAULT_LINE_OF_DEPARTURE_OPTIONS as W, haversineDistance as Wn, DEFAULT_ATTACK_HELICOPTER_OPTIONS as Wt, DEFAULT_HOLDING_LINE_OPTIONS as X, isFiniteNumber as Xn, DEFAULT_MOBILE_DEFENSE_OPTIONS as Xt, DEFAULT_RELEASE_LINE_OPTIONS as Y, getMetersPerPixel as Yn, DEFAULT_AREA_OF_OPERATIONS_OPTIONS as Yt, DEFAULT_INFILTRATION_LANE_OPTIONS as Z, roundToFixed as Zn, DEFAULT_ENGAGEMENT_AREA_OPTIONS as Zt, DEFAULT_PICKUP_ZONE_OPTIONS as _, polyline1DrawRule as _n, DEFAULT_SUPPORTING_ATTACK_OPTIONS as _t, DEFINITIONS as a, resolveAmplifierPlacement as an, DEFAULT_SYMBOL_COLOR as ar, DEFAULT_DISRUPT_OPTIONS as at, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as b, line24DrawRule as bn, DEFAULT_ISOLATE_OPTIONS as bt, getDefaultOptions as c, DEFAULT_DESTROY_OPTIONS as cn, createFollowAndAssume as ct, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as d, DEFAULT_DEFEAT_OPTIONS as dn, DEFAULT_DEMONSTRATE_OPTIONS as dt, DEFAULT_DIRECTION_OF_MAIN_ATTACK_OPTIONS as en, DEFAULT_LINE_CAP as er, DEFAULT_FLOT_OPTIONS as et, DEFAULT_RETAIN_OPTIONS as f, rectangleDrawRule as fn, DEFAULT_DELAY_OPTIONS as ft, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as g, supportByFireDrawRule as gn, DEFAULT_COUNTERATTACK_OPTIONS as gt, SECONDARY_DIRECTION_OF_FIRE_DASH as h, computeInitialWidthPoint as hn, DEFAULT_COUNTERATTACK_BY_FIRE_OPTIONS as ht, CONTROL_MEASURE_METADATA as i, normalizeTextAmplifiers as in, DEFAULT_STROKE_WIDTH_CSS_PIXELS as ir, DEFAULT_ENCIRCLEMENT_OPTIONS as it, DEFAULT_SCREEN_OPTIONS as j, penetrateDrawRule as jn, DEFAULT_CLASSIC_ARROW_OPTIONS as jt, DEFAULT_SEIZE_OPTIONS as k, point12DrawRule as kn, DEFAULT_GENERIC_LINE_OPTIONS as kt, listControlMeasureMetadata as l, DEFAULT_NEUTRALIZE_OPTIONS as ln, DEFAULT_DISRUPT_MISSION_TASK_OPTIONS as lt, DEFAULT_SECONDARY_DIRECTION_OF_FIRE_OPTIONS as m, calculateMetrics as mn, DEFAULT_TACTICAL_ARROW_OPTIONS as mt, resolveStyleHints as n, TEXT_AMPLIFIER_FIELDS as nn, DEFAULT_PORTRAYAL as nr, DEFAULT_FIX_OPTIONS as nt, getControlMeasureMetadata as o, DEFAULT_AMBUSH_OPTIONS as on, DEFAULT_GUARD_OPTIONS as ot, DEFAULT_STRONG_POINT_OPTIONS as p, axis1DrawRule as pn, DEFAULT_COVER_OPTIONS as pt, DEFAULT_HANDOVER_LINE_OPTIONS as q, unproject as qn, DEFAULT_ANTITANK_DITCH_OPTIONS as qt, CONTROL_MEASURE_IDS as r, canonicalTextAmplifierKey as rn, DEFAULT_STROKE_DASH_CSS_PIXELS as rr, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as rt, getControlMeasureMetadataByValue as s, DEFAULT_AIRBORNE_ATTACK_OPTIONS as sn, createFollowAndSupport as st, renderControlMeasure as t, DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS as tn, DEFAULT_LINE_JOIN as tr, DEFAULT_FIX_MISSION_TASK_OPTIONS as tt, DEFAULT_TURNING_MOVEMENT_OPTIONS as u, DEFAULT_SUPPRESS_OPTIONS as un, DEFAULT_DISENGAGE_OPTIONS as ut, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as v, line27DrawRule as vn, DEFAULT_CORDON_AND_SEARCH_OPTIONS as vt, DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS as w, ambushDrawRule as wn, RADAR_SEARCH_DOCTRINE_STROKE_COLOR as wt, DEFAULT_MINEFIELD_OPTIONS as x, line23DrawRule as xn, DEFAULT_CLEAR_OPTIONS as xt, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as y, line26DrawRule as yn, DEFAULT_CORDON_AND_KNOCK_OPTIONS as yt, DEFAULT_FORTIFIED_AREA_OPTIONS as z, createMidpointPerpendicularDrawRule as zn, DEFAULT_BOUNDARY_OPTIONS as zt };
20182
+ export { DEFAULT_BRIDGEHEAD_LINE_OPTIONS as $, createBaselineFrame as $n, DEFAULT_AREA_DEFENSE_OPTIONS as $t, DEFAULT_SEIZE_OPTIONS as A, line24DrawRule as An, DEFAULT_GENERIC_LINE_OPTIONS as At, DEFAULT_FORTIFIED_AREA_OPTIONS as B, secureDrawRule as Bn, DEFAULT_BOUNDARY_OPTIONS as Bt, DEFAULT_MAIN_ATTACK_OPTIONS as C, axis1DrawRule as Cn, DEFAULT_RADAR_SEARCH_DOCTRINE_OPTIONS as Ct, DEFAULT_WITHDRAW_UNDER_PRESSURE_OPTIONS as D, polyline1DrawRule as Dn, DEFAULT_GENERIC_SECTOR_OPTIONS as Dt, DEFAULT_TURN_OPTIONS as E, supportByFireDrawRule as En, DEFAULT_GENERIC_TEXT_OPTIONS as Et, DEFAULT_REARWARD_PASSAGE_OF_LINES_OPTIONS as F, attackByFireDrawRule as Fn, DEFAULT_BREACH_OPTIONS as Ft, DEFAULT_LINE_OF_DEPARTURE_OPTIONS as G, centerRadiusDrawRule as Gn, DEFAULT_ATTACK_HELICOPTER_OPTIONS as Gt, DEFAULT_PROBABLE_LINE_OF_DEPLOYMENT_OPTIONS as H, occupyDrawRule as Hn, DEFAULT_BLOCK_OPTIONS as Ht, DEFAULT_PENETRATE_OPTIONS as I, dynamicPointDrawRule as In, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as It, DEFAULT_HANDOVER_LINE_OPTIONS as J, computeDefaultMidpointPerpendicularPoint as Jn, DEFAULT_ANTITANK_DITCH_OPTIONS as Jt, DEFAULT_LIMIT_OF_ADVANCE_OPTIONS as K, containDrawRule as Kn, DEFAULT_ATTACK_BY_FIRE_OPTIONS as Kt, DEFAULT_OCCUPY_OPTIONS as L, sectorDrawRule as Ln, DEFAULT_GENERIC_C2_LINE_OPTIONS as Lt, DEFAULT_SCREEN_OPTIONS as M, turnDrawRule as Mn, DEFAULT_CLASSIC_ARROW_OPTIONS as Mt, DEFAULT_RETIRE_OPTIONS as N, line1DrawRule as Nn, DEFAULT_CANALIZE_OPTIONS as Nt, DEFAULT_WITHDRAW_OPTIONS as O, line27DrawRule as On, DEFAULT_GENERIC_RECTANGLE_OPTIONS as Ot, DEFAULT_RELIEF_IN_PLACE_OPTIONS as P, ambushDrawRule as Pn, DEFAULT_BYPASS_OPTIONS as Pt, DEFAULT_INFILTRATION_LANE_OPTIONS as Q, snapToMidpointPerpendicular as Qn, DEFAULT_ENGAGEMENT_AREA_OPTIONS as Qt, DEFAULT_MOVEMENT_TO_CONTACT_OPTIONS as R, staticPointDrawRule as Rn, DEFAULT_ENGINEER_WORK_LINE_OPTIONS as Rt, DEFAULT_MINEFIELD_OPTIONS as S, rectangleDrawRule as Sn, DEFAULT_CLEAR_OPTIONS as St, DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS as T, computeInitialWidthPoint as Tn, RADAR_SEARCH_DOCTRINE_STROKE_COLOR as Tt, PROBABLE_LINE_OF_DEPLOYMENT_DASH as U, disruptDrawRule as Un, DEFAULT_CONTAIN_OPTIONS as Ut, DEFAULT_FORTIFIED_LINE_OPTIONS as V, penetrateDrawRule as Vn, DEFAULT_BLOCK_ARROW_OPTIONS as Vt, DEFAULT_LINE_OF_DEPARTURE_CONTACT_OPTIONS as W, blockDrawRule as Wn, DEFAULT_BATTLE_POSITION_OPTIONS as Wt, DEFAULT_RELEASE_LINE_OPTIONS as X, getMidpointPerpendicularSignedDistance as Xn, DEFAULT_AREA_OF_OPERATIONS_OPTIONS as Xt, DEFAULT_FORWARD_EDGE_OF_BATTLE_AREA_OPTIONS as Y, createMidpointPerpendicularDrawRule as Yn, DEFAULT_ASSEMBLY_AREA_OPTIONS as Yt, DEFAULT_HOLDING_LINE_OPTIONS as Z, pointOnMidpointPerpendicularAxis as Zn, DEFAULT_MOBILE_DEFENSE_OPTIONS as Zt, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as _, DEFAULT_PORTRAYAL as _n, DEFAULT_COUNTERATTACK_OPTIONS as _t, CONTROL_MEASURE_METADATA as a, normalizeTextAmplifiers as an, getMetersPerPixel as ar, DEFAULT_ENCIRCLEMENT_OPTIONS as at, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as b, DEFAULT_SYMBOL_COLOR as bn, DEFAULT_CORDON_AND_KNOCK_OPTIONS as bt, getControlMeasureMetadataByValue as c, DEFAULT_AIRBORNE_ATTACK_OPTIONS as cn, createFollowAndSupport as ct, DEFAULT_TURNING_MOVEMENT_OPTIONS as d, DEFAULT_SUPPRESS_OPTIONS as dn, DEFAULT_DISENGAGE_OPTIONS as dt, DEFAULT_DIRECTION_OF_SUPPORTING_ATTACK_OPTIONS as en, haversineDistance as er, DEFAULT_PHASE_LINE_OPTIONS as et, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as f, DEFAULT_DEFEAT_OPTIONS as fn, DEFAULT_DEMONSTRATE_OPTIONS as ft, SECONDARY_DIRECTION_OF_FIRE_DASH as g, DEFAULT_LINE_JOIN as gn, DEFAULT_COUNTERATTACK_BY_FIRE_OPTIONS as gt, DEFAULT_SECONDARY_DIRECTION_OF_FIRE_OPTIONS as h, DEFAULT_LINE_CAP as hn, DEFAULT_TACTICAL_ARROW_OPTIONS as ht, CONTROL_MEASURE_IDS as i, canonicalTextAmplifierKey as in, EPSILON as ir, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as it, DEFAULT_SECURE_OPTIONS as j, line23DrawRule as jn, DEFAULT_GENERIC_CIRCLE_OPTIONS as jt, DEFAULT_EVACUATE_OPTIONS as k, line26DrawRule as kn, DEFAULT_GENERIC_POLYGON_OPTIONS as kt, getDefaultOptions as l, DEFAULT_DESTROY_OPTIONS as ln, createFollowAndAssume as lt, DEFAULT_STRONG_POINT_OPTIONS as m, DEFAULT_LABEL_SIZE_CLAMP_CSS_PIXELS as mn, DEFAULT_COVER_OPTIONS as mt, resolveDefaultSymbolColor as n, DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS as nn, sphericalBearing as nr, DEFAULT_FIX_MISSION_TASK_OPTIONS as nt, DEFINITIONS as o, resolveAmplifierPlacement as on, isFiniteNumber as or, DEFAULT_DISRUPT_OPTIONS as ot, DEFAULT_RETAIN_OPTIONS as p, DEFAULT_LABEL_HEIGHT_CSS_PIXELS as pn, DEFAULT_DELAY_OPTIONS as pt, DEFAULT_BATTLE_HANDOVER_LINE_OPTIONS as q, area27DrawRule as qn, DEFAULT_ANTITANK_WALL_OPTIONS as qt, resolveStyleHints as r, TEXT_AMPLIFIER_FIELDS as rn, unproject as rr, DEFAULT_FIX_OPTIONS as rt, getControlMeasureMetadata as s, DEFAULT_AMBUSH_OPTIONS as sn, roundToFixed as sr, DEFAULT_GUARD_OPTIONS as st, renderControlMeasure as t, DEFAULT_DIRECTION_OF_MAIN_ATTACK_OPTIONS as tn, project as tr, DEFAULT_FLOT_OPTIONS as tt, listControlMeasureMetadata as u, DEFAULT_NEUTRALIZE_OPTIONS as un, DEFAULT_DISRUPT_MISSION_TASK_OPTIONS as ut, DEFAULT_PICKUP_ZONE_OPTIONS as v, DEFAULT_STROKE_DASH_CSS_PIXELS as vn, DEFAULT_SUPPORTING_ATTACK_OPTIONS as vt, DEFAULT_LANDING_ZONE_OPTIONS as w, calculateMetrics as wn, RADAR_SEARCH_DOCTRINE_FILL_COLOR as wt, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as x, MIL_STD_GREEN as xn, DEFAULT_ISOLATE_OPTIONS as xt, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as y, DEFAULT_STROKE_WIDTH_CSS_PIXELS as yn, DEFAULT_CORDON_AND_SEARCH_OPTIONS as yt, DEFAULT_FRONTAL_ATTACK_OPTIONS as z, point12DrawRule as zn, DEFAULT_LIGHT_LINE_OPTIONS as zt };