@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.
- package/README.md +13 -0
- package/dist/{index-DuWUI8Se.d.mts → index-5ZXJfifM.d.mts} +42 -2
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/{patterns-CcQmmOuJ.d.mts → patterns-DMO-zeaM.d.mts} +21 -1
- package/dist/patterns.d.mts +1 -1
- package/dist/preview/index.d.mts +23 -3
- package/dist/preview/index.mjs +29 -3
- package/dist/{renderControlMeasure-Cn4Yh917.mjs → renderControlMeasure-8PAUAwk_.mjs} +704 -148
- package/media/antitank-ditch-completed.svg +1 -1
- package/media/antitank-ditch-under-construction.svg +1 -1
- package/media/antitank-wall.svg +1 -1
- package/media/block.svg +1 -1
- package/media/disrupt.svg +1 -1
- package/media/double-apron-fence.svg +1 -0
- package/media/double-fence.svg +1 -0
- package/media/double-strand-concertina.svg +1 -0
- package/media/fix.svg +1 -1
- package/media/high-wire-fence.svg +1 -0
- package/media/limited-access-area.svg +1 -1
- package/media/low-wire-fence.svg +1 -0
- package/media/mined-area.svg +1 -1
- package/media/minefield-dynamic.svg +1 -1
- package/media/minefield.svg +1 -1
- package/media/no-fire-area-irregular.svg +1 -1
- package/media/obstacle-belt.svg +1 -0
- package/media/obstacle-bypass-difficult.svg +1 -1
- package/media/obstacle-bypass-easy.svg +1 -1
- package/media/obstacle-bypass-impossible.svg +1 -1
- package/media/obstacle-free-zone.svg +1 -0
- package/media/obstacle-restricted-zone.svg +1 -0
- package/media/obstacle-zone.svg +1 -0
- package/media/single-concertina.svg +1 -0
- package/media/single-fence.svg +1 -0
- package/media/triple-strand-concertina.svg +1 -0
- package/media/turn.svg +1 -1
- package/media/wire-obstacle-unspecified.svg +1 -0
- 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,
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
18391
|
-
addLeg(p1, p3,
|
|
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
|
-
*
|
|
19380
|
-
*
|
|
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
|
|
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
|
-
*
|
|
19413
|
-
*
|
|
19414
|
-
|
|
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
|
|
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 {
|
|
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 };
|