@orbat-mapper/control-measures 0.12.1 → 0.14.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 +4 -0
- package/dist/{index-DelKQdBf.d.mts → index-CXadfQRk.d.mts} +37 -3
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/preview/index.d.mts +1 -1
- package/dist/preview/index.mjs +1 -1
- package/dist/{renderControlMeasure-DYcehsHK.mjs → renderControlMeasure-BgPfnjvc.mjs} +481 -198
- package/media/bridge-or-gap.svg +1 -0
- package/media/envelopment.svg +1 -0
- package/media/infiltration-lane.svg +1 -0
- package/media/infiltration.svg +1 -0
- package/package.json +1 -1
|
@@ -280,7 +280,7 @@ const SHAFT_WIDTH_OPTION_HANDLE_ID = "shaft-width";
|
|
|
280
280
|
/**
|
|
281
281
|
* Creates the definition-owned rear-/shaft-width handle pair shared by every
|
|
282
282
|
* variable-width arrow body. Callers supply only how their own geometry maps
|
|
283
|
-
* onto {@link WidthHandleGeometry}; placement (the two ends of the
|
|
283
|
+
* onto {@link WidthHandleGeometry}; placement (the two ends of the right edge)
|
|
284
284
|
* and the drag math (perpendicular distance from the dragged point to the
|
|
285
285
|
* matching centerline segment, over the reference half-width) live here once.
|
|
286
286
|
*/
|
|
@@ -288,15 +288,15 @@ function createWidthOptionHandles(config) {
|
|
|
288
288
|
return {
|
|
289
289
|
get(controlPoints, options) {
|
|
290
290
|
const geometry = config.geometry(controlPoints, options);
|
|
291
|
-
const
|
|
292
|
-
const
|
|
293
|
-
if (!
|
|
291
|
+
const rearRight = geometry?.rightEdge[0];
|
|
292
|
+
const neckRight = geometry?.rightEdge.at(-1);
|
|
293
|
+
if (!rearRight || !neckRight) return [];
|
|
294
294
|
return [{
|
|
295
295
|
id: REAR_WIDTH_OPTION_HANDLE_ID,
|
|
296
|
-
position: unproject(
|
|
296
|
+
position: unproject(rearRight[0], rearRight[1])
|
|
297
297
|
}, {
|
|
298
298
|
id: SHAFT_WIDTH_OPTION_HANDLE_ID,
|
|
299
|
-
position: unproject(
|
|
299
|
+
position: unproject(neckRight[0], neckRight[1])
|
|
300
300
|
}];
|
|
301
301
|
},
|
|
302
302
|
drag({ controlPoints, options, handleId, position }) {
|
|
@@ -328,7 +328,7 @@ function createVariableWidthAttackOptionHandles(resolveCoordinates = (points) =>
|
|
|
328
328
|
if (!geometry || !outerLeft) return null;
|
|
329
329
|
return {
|
|
330
330
|
spine: geometry.shaftCenterline,
|
|
331
|
-
|
|
331
|
+
rightEdge: geometry.shaftRight,
|
|
332
332
|
referenceHalfWidth: vecMag(vecSub(outerLeft, geometry.ptBase))
|
|
333
333
|
};
|
|
334
334
|
},
|
|
@@ -1349,6 +1349,23 @@ const line32DrawRule = {
|
|
|
1349
1349
|
}
|
|
1350
1350
|
};
|
|
1351
1351
|
//#endregion
|
|
1352
|
+
//#region src/draw-rules/polyline1.ts
|
|
1353
|
+
/**
|
|
1354
|
+
* Polyline1 anchors for the Infiltration Lane.
|
|
1355
|
+
*
|
|
1356
|
+
* PT1/PT2 are the two user clicks and define the invisible lane centerline. PT3 is an
|
|
1357
|
+
* automatically derived, editable width handle on the perpendicular through
|
|
1358
|
+
* its midpoint. It rests on one parallel boundary, so its signed distance
|
|
1359
|
+
* from PT1/PT2 is the lane half-width.
|
|
1360
|
+
*/
|
|
1361
|
+
const polyline1DrawRule = createMidpointPerpendicularDrawRule({
|
|
1362
|
+
id: "polyline1:infiltration-lane",
|
|
1363
|
+
defaultDistanceRatio: .18,
|
|
1364
|
+
minimumUserPoints: 2,
|
|
1365
|
+
minimumPreviewPoints: 2,
|
|
1366
|
+
normal: "left"
|
|
1367
|
+
});
|
|
1368
|
+
//#endregion
|
|
1352
1369
|
//#region src/draw-rules/area8.ts
|
|
1353
1370
|
function derive$3(points) {
|
|
1354
1371
|
if (points.length < 2) return clonePositions(points);
|
|
@@ -1697,6 +1714,16 @@ const ECHELON_PARAM_OPTIONS = [{
|
|
|
1697
1714
|
label: spec.label,
|
|
1698
1715
|
value
|
|
1699
1716
|
}))];
|
|
1717
|
+
/**
|
|
1718
|
+
* Effective echelon glyph height in meters. Screen-anchored when a pixel size
|
|
1719
|
+
* is supplied with a live resolution; ground-anchored (meters) otherwise.
|
|
1720
|
+
* Clamped strictly positive.
|
|
1721
|
+
*/
|
|
1722
|
+
function resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel) {
|
|
1723
|
+
let h = echelonSize;
|
|
1724
|
+
if (echelonSizePixels !== void 0 && metersPerPixel !== void 0 && metersPerPixel > 0) h = echelonSizePixels * metersPerPixel;
|
|
1725
|
+
return Math.max(EPSILON, h);
|
|
1726
|
+
}
|
|
1700
1727
|
function resolveEchelon(echelon) {
|
|
1701
1728
|
const key = echelon.trim().toLowerCase();
|
|
1702
1729
|
if (key.length === 0) return void 0;
|
|
@@ -2677,6 +2704,26 @@ function labelRotationAlong(dir) {
|
|
|
2677
2704
|
const rendered = -Math.atan2(dir[1], dir[0]);
|
|
2678
2705
|
return normalizeRadians(Math.PI - keepTextLeftToRight(rendered));
|
|
2679
2706
|
}
|
|
2707
|
+
/** Resolves two echelon-unit rows from a caller-selected set of amplifiers. */
|
|
2708
|
+
function unitLabelsFromAmplifiers(amplifiers, fields) {
|
|
2709
|
+
return [{
|
|
2710
|
+
side: 1,
|
|
2711
|
+
designator: amplifiers[fields.unit1.designator] ?? "",
|
|
2712
|
+
country: amplifiers[fields.unit1.country] ?? ""
|
|
2713
|
+
}, {
|
|
2714
|
+
side: -1,
|
|
2715
|
+
designator: amplifiers[fields.unit2.designator] ?? "",
|
|
2716
|
+
country: amplifiers[fields.unit2.country] ?? ""
|
|
2717
|
+
}];
|
|
2718
|
+
}
|
|
2719
|
+
/** Text size shared by an echelon glyph and its associated unit labels. */
|
|
2720
|
+
function echelonUnitLabelSize(echelonHeight, echelonSizePixels, metersPerPixel) {
|
|
2721
|
+
const screenAnchored = echelonSizePixels !== void 0 && metersPerPixel !== void 0 && metersPerPixel > 0;
|
|
2722
|
+
return {
|
|
2723
|
+
properties: screenAnchored ? { textSizePixels: echelonSizePixels } : { textSizeMeters: echelonHeight },
|
|
2724
|
+
screenAnchored
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2680
2727
|
/**
|
|
2681
2728
|
* Emits a single combined `T (AS)` label per unit side (APP-6/MIL-STD-2525):
|
|
2682
2729
|
* designator and country code share one text row rather than stacking.
|
|
@@ -2693,6 +2740,43 @@ function pushUnitLabels(out, frame, units) {
|
|
|
2693
2740
|
}
|
|
2694
2741
|
}
|
|
2695
2742
|
/**
|
|
2743
|
+
* Default clearance between an echelon glyph and its unit labels, as a ratio of
|
|
2744
|
+
* the glyph height — the value Boundary declares as its `labelPadding` default,
|
|
2745
|
+
* shared so other echelon-carrying lines land on the same spacing.
|
|
2746
|
+
*/
|
|
2747
|
+
const ECHELON_UNIT_LABEL_PADDING = .4;
|
|
2748
|
+
/**
|
|
2749
|
+
* Emits Boundary-style unit labels around echelon groups. Along-line groups
|
|
2750
|
+
* straddle the line; standard-orientation groups on north/south segments form
|
|
2751
|
+
* one row crossing it, anchored outward from the glyph.
|
|
2752
|
+
*/
|
|
2753
|
+
function pushEchelonUnitLabels(out, groups, units, echelonHeight, labelPadding, labelSize) {
|
|
2754
|
+
for (const { center, along, perp, rotateGroup, readingAxis, glyphWidth } of groups) {
|
|
2755
|
+
if (rotateGroup) {
|
|
2756
|
+
const sign = vecDot(perp, readingAxis) >= 0 ? 1 : -1;
|
|
2757
|
+
const clearance = glyphWidth / 2 + Math.max(0, labelPadding) * echelonHeight;
|
|
2758
|
+
pushUnitLabels(out, {
|
|
2759
|
+
center,
|
|
2760
|
+
perp: vecScale(readingAxis, sign),
|
|
2761
|
+
offset: clearance,
|
|
2762
|
+
rotation: labelRotationAlong(readingAxis),
|
|
2763
|
+
labelSize
|
|
2764
|
+
}, units.map((unit) => ({
|
|
2765
|
+
...unit,
|
|
2766
|
+
textAnchor: unit.side === sign ? "start" : "end"
|
|
2767
|
+
})));
|
|
2768
|
+
continue;
|
|
2769
|
+
}
|
|
2770
|
+
pushUnitLabels(out, {
|
|
2771
|
+
center,
|
|
2772
|
+
perp,
|
|
2773
|
+
offset: (.45 + Math.max(0, labelPadding)) * echelonHeight,
|
|
2774
|
+
rotation: labelRotationAlong(along),
|
|
2775
|
+
labelSize
|
|
2776
|
+
}, units);
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
/**
|
|
2696
2780
|
* Walks `verts` (projected meters) into non-degenerate {@link LineSegment}s,
|
|
2697
2781
|
* skipping coincident vertices (ADR-0014, silent) the same way Boundary's
|
|
2698
2782
|
* gap-carving does. Empty or fully-degenerate input yields `totalLength: 0`
|
|
@@ -2725,6 +2809,15 @@ function clampLinePosition(value, fallback) {
|
|
|
2725
2809
|
return Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : fallback;
|
|
2726
2810
|
}
|
|
2727
2811
|
/**
|
|
2812
|
+
* Resolves a label/group repetition count to a positive whole number. Takes
|
|
2813
|
+
* `unknown` because the params panel's `visibleWhen` callbacks receive loosely
|
|
2814
|
+
* typed options; non-finite input falls back to a single group.
|
|
2815
|
+
*/
|
|
2816
|
+
function resolveLabelRepetitions(value) {
|
|
2817
|
+
const numeric = Number(value ?? 1);
|
|
2818
|
+
return Number.isFinite(numeric) ? Math.max(1, Math.round(numeric)) : 1;
|
|
2819
|
+
}
|
|
2820
|
+
/**
|
|
2728
2821
|
* Resolves the point at `fraction` (clamped to `[0, 1]`) of `totalLength`
|
|
2729
2822
|
* along `segments`, walking cumulative arc length exactly like Boundary's
|
|
2730
2823
|
* label-group placement: the segment containing the target distance absorbs
|
|
@@ -3466,14 +3559,14 @@ const DEFAULT_ARROWHEAD_WIDTH_RATIO$1 = 1.15;
|
|
|
3466
3559
|
const DEFAULT_BOW_TIE_LENGTH_RATIO = .1;
|
|
3467
3560
|
const DEFAULT_BOW_TIE_WIDTH_RATIO = .5;
|
|
3468
3561
|
const DEFAULT_BOW_TIE_POSITION = 1 / 3;
|
|
3469
|
-
const DEFAULT_LABEL_POSITION = 2 / 3;
|
|
3562
|
+
const DEFAULT_LABEL_POSITION$1 = 2 / 3;
|
|
3470
3563
|
const DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS = {
|
|
3471
3564
|
arrowheadLengthRatio: DEFAULT_ARROWHEAD_LENGTH_RATIO$1,
|
|
3472
3565
|
arrowheadWidthRatio: DEFAULT_ARROWHEAD_WIDTH_RATIO$1,
|
|
3473
3566
|
bowTieLengthRatio: DEFAULT_BOW_TIE_LENGTH_RATIO,
|
|
3474
3567
|
bowTieWidthRatio: DEFAULT_BOW_TIE_WIDTH_RATIO,
|
|
3475
3568
|
bowTiePosition: DEFAULT_BOW_TIE_POSITION,
|
|
3476
|
-
labelPosition: DEFAULT_LABEL_POSITION,
|
|
3569
|
+
labelPosition: DEFAULT_LABEL_POSITION$1,
|
|
3477
3570
|
labelPadding: 0,
|
|
3478
3571
|
smooth: false,
|
|
3479
3572
|
smoothResolution: 12
|
|
@@ -3620,7 +3713,7 @@ function createDirectionOfAttackAviation(coordinates, options = {}, textAmplifie
|
|
|
3620
3713
|
};
|
|
3621
3714
|
const bowTieLength = Math.min(totalLength, totalLength * nonNegative(resolved.bowTieLengthRatio, DEFAULT_BOW_TIE_LENGTH_RATIO));
|
|
3622
3715
|
const bowTiePosition = clampLinePosition(resolved.bowTiePosition, DEFAULT_BOW_TIE_POSITION);
|
|
3623
|
-
const labelPosition = clampLinePosition(resolved.labelPosition, DEFAULT_LABEL_POSITION);
|
|
3716
|
+
const labelPosition = clampLinePosition(resolved.labelPosition, DEFAULT_LABEL_POSITION$1);
|
|
3624
3717
|
const bowTieCenterDistance = totalLength * bowTiePosition;
|
|
3625
3718
|
const bowTieStartDistance = Math.max(0, bowTieCenterDistance - bowTieLength / 2);
|
|
3626
3719
|
const bowTieEndDistance = Math.min(totalLength, bowTieCenterDistance + bowTieLength / 2);
|
|
@@ -3906,16 +3999,6 @@ function pointOnRing(segs, target) {
|
|
|
3906
3999
|
seg
|
|
3907
4000
|
};
|
|
3908
4001
|
}
|
|
3909
|
-
/**
|
|
3910
|
-
* Effective echelon glyph height in meters. Screen-anchored when a pixel size
|
|
3911
|
-
* is supplied with a live resolution; ground-anchored (meters) otherwise.
|
|
3912
|
-
* Clamped strictly positive.
|
|
3913
|
-
*/
|
|
3914
|
-
function resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel) {
|
|
3915
|
-
let h = echelonSize;
|
|
3916
|
-
if (echelonSizePixels !== void 0 && metersPerPixel !== void 0 && metersPerPixel > 0) h = echelonSizePixels * metersPerPixel;
|
|
3917
|
-
return Math.max(EPSILON, h);
|
|
3918
|
-
}
|
|
3919
4002
|
/** Wraps `x` into `[0, 1)`, treating `x` as a fraction (matches the arc-length wrap below). */
|
|
3920
4003
|
function wrap01(x) {
|
|
3921
4004
|
return (x % 1 + 1) % 1;
|
|
@@ -4004,7 +4087,7 @@ const BAND_SAMPLE_FRACTIONS = [
|
|
|
4004
4087
|
* rerender); striding holds it to a fixed multiple of one ring scan. Rings
|
|
4005
4088
|
* with no more bands than this are still probed exhaustively. */
|
|
4006
4089
|
const MAX_SAMPLED_BANDS = 48;
|
|
4007
|
-
const DEFAULT_SMOOTH_RESOLUTION$
|
|
4090
|
+
const DEFAULT_SMOOTH_RESOLUTION$8 = 16;
|
|
4008
4091
|
/**
|
|
4009
4092
|
* Composes an area's main label text from a `prefix` (e.g. `"AO"`) and a
|
|
4010
4093
|
* `designation` (e.g. `"GREEN"`), mirroring Phase Line's "PL" precedent: a
|
|
@@ -4404,8 +4487,8 @@ function boundaryFeature(verts, gaps) {
|
|
|
4404
4487
|
* common (no `N`) case is unaffected.
|
|
4405
4488
|
*/
|
|
4406
4489
|
function labeledAreaFeatures(positions, options, texts, amplifierPlacements, context) {
|
|
4407
|
-
const { smooth = false, smoothResolution = DEFAULT_SMOOTH_RESOLUTION$
|
|
4408
|
-
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$
|
|
4490
|
+
const { smooth = false, smoothResolution = DEFAULT_SMOOTH_RESOLUTION$8, ...labelOptions } = options;
|
|
4491
|
+
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$8);
|
|
4409
4492
|
const labelFeatures = [];
|
|
4410
4493
|
const features = [boundaryFeature(verts, pushAreaLabels(labelFeatures, verts, texts, labelOptions, amplifierPlacements, void 0, context))];
|
|
4411
4494
|
features.push(...labelFeatures);
|
|
@@ -5122,7 +5205,7 @@ const TARGET_AREA_OF_INTEREST = defineControlMeasure({
|
|
|
5122
5205
|
});
|
|
5123
5206
|
//#endregion
|
|
5124
5207
|
//#region src/generators/cm12-command-control-areas/airfield-zone.ts
|
|
5125
|
-
const DEFAULT_SMOOTH_RESOLUTION$
|
|
5208
|
+
const DEFAULT_SMOOTH_RESOLUTION$7 = 16;
|
|
5126
5209
|
/** Long runway line's tilt off the horizontal, in radians (+15°). */
|
|
5127
5210
|
const LONG_LINE_ANGLE = 15 * Math.PI / 180;
|
|
5128
5211
|
/** Short runway line's tilt off the horizontal, in radians (+50°). */
|
|
@@ -5137,7 +5220,7 @@ const H_LABEL_OFFSET_RATIO = .75;
|
|
|
5137
5220
|
*/
|
|
5138
5221
|
const DEFAULT_AIRFIELD_ZONE_OPTIONS = {
|
|
5139
5222
|
smooth: true,
|
|
5140
|
-
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$
|
|
5223
|
+
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$7,
|
|
5141
5224
|
glyphSizeRatio: .55
|
|
5142
5225
|
};
|
|
5143
5226
|
const AIRFIELD_ZONE_METADATA = {
|
|
@@ -5195,7 +5278,7 @@ const AIRFIELD_ZONE_METADATA = {
|
|
|
5195
5278
|
*/
|
|
5196
5279
|
function createAirfieldZone(positions, options = {}, textAmplifiers = {}, context = {}) {
|
|
5197
5280
|
const { smooth = DEFAULT_AIRFIELD_ZONE_OPTIONS.smooth, smoothResolution = DEFAULT_AIRFIELD_ZONE_OPTIONS.smoothResolution, glyphSizeRatio = DEFAULT_AIRFIELD_ZONE_OPTIONS.glyphSizeRatio, ...labelOptions } = options;
|
|
5198
|
-
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$
|
|
5281
|
+
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$7);
|
|
5199
5282
|
const enyLabels = [];
|
|
5200
5283
|
const features = [boundaryFeature(verts, textAmplifiers.N ? pushAreaLabels(enyLabels, verts, { hostile: textAmplifiers.N }, labelOptions, context.amplifierPlacements, void 0, context) : [])];
|
|
5201
5284
|
let minX = Infinity;
|
|
@@ -5253,7 +5336,7 @@ const AIRFIELD_ZONE = defineControlMeasure({
|
|
|
5253
5336
|
});
|
|
5254
5337
|
//#endregion
|
|
5255
5338
|
//#region src/generators/cm12-command-control-areas/echelon-labeled-area.ts
|
|
5256
|
-
const DEFAULT_SMOOTH_RESOLUTION$
|
|
5339
|
+
const DEFAULT_SMOOTH_RESOLUTION$6 = 16;
|
|
5257
5340
|
/** Default options shared by Base Camp and Guerrilla Base — echelon off, boundary rounded. */
|
|
5258
5341
|
const DEFAULT_ECHELON_LABELED_AREA_OPTIONS = {
|
|
5259
5342
|
echelon: "none",
|
|
@@ -5262,7 +5345,7 @@ const DEFAULT_ECHELON_LABELED_AREA_OPTIONS = {
|
|
|
5262
5345
|
echelonPadding: .3,
|
|
5263
5346
|
echelonPosition: 0,
|
|
5264
5347
|
smooth: true,
|
|
5265
|
-
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$
|
|
5348
|
+
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$6
|
|
5266
5349
|
};
|
|
5267
5350
|
/**
|
|
5268
5351
|
* Builds the shared Base Camp / Guerrilla Base geometry: a closed area
|
|
@@ -5276,7 +5359,7 @@ const DEFAULT_ECHELON_LABELED_AREA_OPTIONS = {
|
|
|
5276
5359
|
function echelonLabeledAreaFeatures(positions, options, texts, amplifierPlacements, context) {
|
|
5277
5360
|
const { echelon = DEFAULT_ECHELON_LABELED_AREA_OPTIONS.echelon, echelonSize = DEFAULT_ECHELON_LABELED_AREA_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_ECHELON_LABELED_AREA_OPTIONS.echelonPadding, echelonPosition = DEFAULT_ECHELON_LABELED_AREA_OPTIONS.echelonPosition, echelonAnchor, metersPerPixel, smooth = DEFAULT_ECHELON_LABELED_AREA_OPTIONS.smooth, smoothResolution = DEFAULT_ECHELON_LABELED_AREA_OPTIONS.smoothResolution, ...labelOptions } = options;
|
|
5278
5361
|
const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
|
|
5279
|
-
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$
|
|
5362
|
+
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$6);
|
|
5280
5363
|
const { strokes, fills, gaps: echelonGaps } = placeEchelon(buildAreaPerimeter(verts), h, echelon, echelonPadding, echelonPosition, echelonAnchor);
|
|
5281
5364
|
const labelFeatures = [];
|
|
5282
5365
|
const enyGaps = pushAreaLabels(labelFeatures, verts, texts, labelOptions, amplifierPlacements, void 0, context);
|
|
@@ -6237,7 +6320,7 @@ const ATTACK_HELICOPTER = defineControlMeasure({
|
|
|
6237
6320
|
});
|
|
6238
6321
|
//#endregion
|
|
6239
6322
|
//#region src/generators/cm15-maneuver-areas/battlePosition.ts
|
|
6240
|
-
const DEFAULT_SMOOTH_RESOLUTION$
|
|
6323
|
+
const DEFAULT_SMOOTH_RESOLUTION$5 = 16;
|
|
6241
6324
|
/** Default options for the Battle Position control measure. */
|
|
6242
6325
|
const DEFAULT_BATTLE_POSITION_OPTIONS = {
|
|
6243
6326
|
echelon: "battalion",
|
|
@@ -6246,7 +6329,7 @@ const DEFAULT_BATTLE_POSITION_OPTIONS = {
|
|
|
6246
6329
|
echelonPadding: .3,
|
|
6247
6330
|
echelonPosition: 0,
|
|
6248
6331
|
smooth: false,
|
|
6249
|
-
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$
|
|
6332
|
+
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$5
|
|
6250
6333
|
};
|
|
6251
6334
|
const BATTLE_POSITION_METADATA = {
|
|
6252
6335
|
id: "battle-position",
|
|
@@ -6288,7 +6371,7 @@ const BATTLE_POSITION_METADATA = {
|
|
|
6288
6371
|
function battlePositionAreaFeatures(positions, options, textAmplifiers, context, variant = {}) {
|
|
6289
6372
|
const { echelon = DEFAULT_BATTLE_POSITION_OPTIONS.echelon, echelonSize = DEFAULT_BATTLE_POSITION_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPadding, echelonPosition = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPosition, echelonAnchor, metersPerPixel, smooth = DEFAULT_BATTLE_POSITION_OPTIONS.smooth, smoothResolution = DEFAULT_BATTLE_POSITION_OPTIONS.smoothResolution } = options;
|
|
6290
6373
|
const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
|
|
6291
|
-
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$
|
|
6374
|
+
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$5);
|
|
6292
6375
|
const { strokes, fills, gaps } = placeEchelon(buildAreaPerimeter(verts), h, echelon, echelonPadding, echelonPosition, echelonAnchor);
|
|
6293
6376
|
const labelFeatures = [];
|
|
6294
6377
|
const enyGaps = pushAreaLabels(labelFeatures, verts, composeAreaLabelTexts(textAmplifiers, variant.namePrefix), options, context.amplifierPlacements, void 0, context);
|
|
@@ -6862,7 +6945,7 @@ const BRIDGE_OR_GAP = defineControlMeasure({
|
|
|
6862
6945
|
});
|
|
6863
6946
|
//#endregion
|
|
6864
6947
|
//#region src/generators/cm99-generic-arrows/blockArrow.ts
|
|
6865
|
-
const DEFAULT_SMOOTH_RESOLUTION$
|
|
6948
|
+
const DEFAULT_SMOOTH_RESOLUTION$4 = 12;
|
|
6866
6949
|
const MIN_SMOOTH_RESOLUTION$1 = 2;
|
|
6867
6950
|
const MAX_SMOOTH_RESOLUTION$1 = 64;
|
|
6868
6951
|
const DEFAULT_SHAFT_WIDTH_RATIO = .06;
|
|
@@ -6875,7 +6958,7 @@ const DEFAULT_BLOCK_ARROW_OPTIONS = {
|
|
|
6875
6958
|
arrowheadWidthRatio: .18,
|
|
6876
6959
|
arrowheadLengthRatio: .22,
|
|
6877
6960
|
smooth: false,
|
|
6878
|
-
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$
|
|
6961
|
+
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$4,
|
|
6879
6962
|
filled: true
|
|
6880
6963
|
};
|
|
6881
6964
|
const BLOCK_ARROW_METADATA = {
|
|
@@ -7110,6 +7193,7 @@ function calculateBlockArrowGeometry(coordinates, options) {
|
|
|
7110
7193
|
ring,
|
|
7111
7194
|
spine,
|
|
7112
7195
|
leftSide,
|
|
7196
|
+
rightSide,
|
|
7113
7197
|
pathLength
|
|
7114
7198
|
};
|
|
7115
7199
|
}
|
|
@@ -7134,7 +7218,7 @@ const BLOCK_ARROW = defineControlMeasure({
|
|
|
7134
7218
|
if (!geometry) return null;
|
|
7135
7219
|
return {
|
|
7136
7220
|
spine: geometry.spine,
|
|
7137
|
-
|
|
7221
|
+
rightEdge: geometry.rightSide,
|
|
7138
7222
|
referenceHalfWidth: geometry.pathLength / 2
|
|
7139
7223
|
};
|
|
7140
7224
|
},
|
|
@@ -7164,74 +7248,26 @@ function axis1Geometry(coordinates) {
|
|
|
7164
7248
|
};
|
|
7165
7249
|
}
|
|
7166
7250
|
function normalizeSmoothResolution$1(value) {
|
|
7167
|
-
if (!Number.isFinite(value)) return DEFAULT_SMOOTH_RESOLUTION$
|
|
7251
|
+
if (!Number.isFinite(value)) return DEFAULT_SMOOTH_RESOLUTION$4;
|
|
7168
7252
|
return Math.min(MAX_SMOOTH_RESOLUTION$1, Math.max(MIN_SMOOTH_RESOLUTION$1, Math.round(value)));
|
|
7169
7253
|
}
|
|
7170
7254
|
//#endregion
|
|
7171
|
-
//#region src/
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
echelonSizePixels: 16,
|
|
7182
|
-
echelonPadding: .3,
|
|
7183
|
-
labelRepetitions: 1,
|
|
7184
|
-
labelSpacing: 1,
|
|
7185
|
-
labelPosition: .5,
|
|
7186
|
-
labelPadding: .4,
|
|
7187
|
-
labelOrientation: "along",
|
|
7188
|
-
smooth: false,
|
|
7189
|
-
smoothResolution: DEFAULT_SMOOTH_RESOLUTION$4
|
|
7190
|
-
};
|
|
7191
|
-
const BOUNDARY_METADATA = {
|
|
7192
|
-
id: "boundary",
|
|
7193
|
-
name: "Boundary",
|
|
7194
|
-
description: "Delineates the area of responsibility between two adjacent units, labeled with the echelon and the designators of the units it separates.",
|
|
7195
|
-
entity: "Command and Control Lines",
|
|
7196
|
-
entityType: "Boundary",
|
|
7197
|
-
value: "110100",
|
|
7198
|
-
minCoordinates: 2,
|
|
7199
|
-
geometry: "line",
|
|
7200
|
-
geometryTypes: [
|
|
7201
|
-
"MultiLineString",
|
|
7202
|
-
"MultiPolygon",
|
|
7203
|
-
"Point"
|
|
7204
|
-
],
|
|
7205
|
-
paints: {
|
|
7206
|
-
stroke: true,
|
|
7207
|
-
fill: "fixed",
|
|
7208
|
-
text: true
|
|
7209
|
-
},
|
|
7210
|
-
drawRule: "Line1",
|
|
7211
|
-
params: [
|
|
7212
|
-
{
|
|
7213
|
-
key: "smooth",
|
|
7214
|
-
label: "Smooth",
|
|
7215
|
-
description: "Round the boundary's corners by curving it through the control points.",
|
|
7216
|
-
type: "boolean"
|
|
7217
|
-
},
|
|
7218
|
-
{
|
|
7219
|
-
key: "smoothResolution",
|
|
7220
|
-
presentationTier: "advanced",
|
|
7221
|
-
label: "Smooth resolution",
|
|
7222
|
-
description: "Number of samples per segment when smooth mode is enabled.",
|
|
7223
|
-
type: "number",
|
|
7224
|
-
min: 2,
|
|
7225
|
-
max: 64,
|
|
7226
|
-
step: 1,
|
|
7227
|
-
visibleWhen: (opts) => Boolean(opts.smooth)
|
|
7228
|
-
},
|
|
7255
|
+
//#region src/internal/line-echelon.ts
|
|
7256
|
+
/**
|
|
7257
|
+
* The echelon and group-distribution controls shared by the line measures that
|
|
7258
|
+
* stamp Field B onto their geometry (Boundary, Generic command and control
|
|
7259
|
+
* line). `subject` names the geometry in the user-facing copy ("line",
|
|
7260
|
+
* "boundary"). Callers spread these into their own `params` and add whatever
|
|
7261
|
+
* else they carry (e.g. Boundary's unit-label padding).
|
|
7262
|
+
*/
|
|
7263
|
+
function echelonLineParams(subject) {
|
|
7264
|
+
return [
|
|
7229
7265
|
{
|
|
7230
7266
|
key: "echelon",
|
|
7231
7267
|
presentationTier: "primary",
|
|
7232
7268
|
semanticRole: "doctrinal",
|
|
7233
7269
|
label: "Echelon",
|
|
7234
|
-
description:
|
|
7270
|
+
description: `Field B unit-size amplifier, drawn on the ${subject}`,
|
|
7235
7271
|
type: "enum",
|
|
7236
7272
|
options: ECHELON_PARAM_OPTIONS
|
|
7237
7273
|
},
|
|
@@ -7269,7 +7305,7 @@ const BOUNDARY_METADATA = {
|
|
|
7269
7305
|
key: "labelRepetitions",
|
|
7270
7306
|
presentationTier: "advanced",
|
|
7271
7307
|
label: "Label groups",
|
|
7272
|
-
description:
|
|
7308
|
+
description: `Number of label groups spaced evenly along the whole ${subject}`,
|
|
7273
7309
|
type: "number",
|
|
7274
7310
|
min: 1,
|
|
7275
7311
|
max: 5,
|
|
@@ -7279,7 +7315,7 @@ const BOUNDARY_METADATA = {
|
|
|
7279
7315
|
key: "labelPosition",
|
|
7280
7316
|
presentationTier: "advanced",
|
|
7281
7317
|
label: "Label position",
|
|
7282
|
-
description:
|
|
7318
|
+
description: `Position of the label group along the ${subject}, from the first point (0) to the last (1)`,
|
|
7283
7319
|
type: "number",
|
|
7284
7320
|
min: 0,
|
|
7285
7321
|
max: 1,
|
|
@@ -7297,30 +7333,160 @@ const BOUNDARY_METADATA = {
|
|
|
7297
7333
|
step: .05,
|
|
7298
7334
|
visibleWhen: (opts) => resolveLabelRepetitions(opts.labelRepetitions) > 1
|
|
7299
7335
|
},
|
|
7300
|
-
{
|
|
7301
|
-
key: "labelPadding",
|
|
7302
|
-
presentationTier: "advanced",
|
|
7303
|
-
label: "Label padding",
|
|
7304
|
-
description: "Gap between the line/echelon and the unit labels, as a ratio of the echelon height",
|
|
7305
|
-
type: "number",
|
|
7306
|
-
min: 0,
|
|
7307
|
-
max: 2,
|
|
7308
|
-
step: .05
|
|
7309
|
-
},
|
|
7310
7336
|
{
|
|
7311
7337
|
key: "labelOrientation",
|
|
7312
7338
|
presentationTier: "advanced",
|
|
7313
7339
|
label: "Label orientation",
|
|
7314
|
-
description:
|
|
7340
|
+
description: `Orient the label group along the ${subject}, or per MIL-STD-2525E Figure L-10: on mostly north–south segments the group rotates perpendicular to the ${subject} as a single row crossing it.`,
|
|
7315
7341
|
type: "enum",
|
|
7316
7342
|
options: [{
|
|
7317
7343
|
value: "along",
|
|
7318
|
-
label:
|
|
7344
|
+
label: `Along the ${subject}`
|
|
7319
7345
|
}, {
|
|
7320
7346
|
value: "standard",
|
|
7321
7347
|
label: "Standard (MIL-STD-2525E)"
|
|
7322
7348
|
}]
|
|
7323
7349
|
}
|
|
7350
|
+
];
|
|
7351
|
+
}
|
|
7352
|
+
/** Along-line position used when a measure leaves `labelPosition` unset. */
|
|
7353
|
+
const DEFAULT_LABEL_POSITION = .5;
|
|
7354
|
+
/** A placement with nothing in it — the result for a degenerate line. */
|
|
7355
|
+
const EMPTY_LINE_ECHELON_PLACEMENT = {
|
|
7356
|
+
strokes: [],
|
|
7357
|
+
fills: [],
|
|
7358
|
+
gaps: [],
|
|
7359
|
+
groups: []
|
|
7360
|
+
};
|
|
7361
|
+
/**
|
|
7362
|
+
* Distributes `labelRepetitions` echelon groups along `verts` and builds the
|
|
7363
|
+
* glyph geometry for each. `spec` may be `undefined` — the groups are still
|
|
7364
|
+
* placed (so a caller can label them) but no glyph or gap is emitted.
|
|
7365
|
+
*/
|
|
7366
|
+
function placeEchelonsAlongLine(verts, spec, h, options) {
|
|
7367
|
+
const { labelRepetitions, labelPosition, labelSpacing, labelOrientation, echelonPadding } = options;
|
|
7368
|
+
const { segments, totalLength } = polylineSegments(verts);
|
|
7369
|
+
if (totalLength < 1e-6) return EMPTY_LINE_ECHELON_PLACEMENT;
|
|
7370
|
+
const reps = resolveLabelRepetitions(labelRepetitions);
|
|
7371
|
+
const singleGroupPosition = clampLinePosition(labelPosition, DEFAULT_LABEL_POSITION);
|
|
7372
|
+
const strokes = [];
|
|
7373
|
+
const fills = [];
|
|
7374
|
+
const gaps = [];
|
|
7375
|
+
const groups = [];
|
|
7376
|
+
for (let k = 0; k < reps; k++) {
|
|
7377
|
+
const evenFraction = (k + 1) / (reps + 1);
|
|
7378
|
+
const fraction = Math.min(1, Math.max(0, reps === 1 ? singleGroupPosition : .5 + (evenFraction - .5) * labelSpacing));
|
|
7379
|
+
const target = fraction * totalLength;
|
|
7380
|
+
const { point: center, along, perp } = pointAlongPolyline(segments, totalLength, fraction);
|
|
7381
|
+
const rotateGroup = labelOrientation === "standard" && Math.abs(along[0]) < Math.abs(along[1]);
|
|
7382
|
+
const readingAxis = perp[0] >= 0 ? perp : vecScale(perp, -1);
|
|
7383
|
+
let glyphWidth = 0;
|
|
7384
|
+
if (spec) {
|
|
7385
|
+
const glyph = buildEchelonGlyph(center, rotateGroup ? readingAxis : along, rotateGroup ? [-readingAxis[1], readingAxis[0]] : perp, h, spec);
|
|
7386
|
+
glyphWidth = glyph.width;
|
|
7387
|
+
strokes.push(...glyph.strokes);
|
|
7388
|
+
fills.push(...glyph.fills);
|
|
7389
|
+
const halfGap = (rotateGroup ? h : glyphWidth) / 2 + Math.max(0, echelonPadding) * h;
|
|
7390
|
+
gaps.push({
|
|
7391
|
+
g0: target - halfGap,
|
|
7392
|
+
g1: target + halfGap
|
|
7393
|
+
});
|
|
7394
|
+
}
|
|
7395
|
+
groups.push({
|
|
7396
|
+
center,
|
|
7397
|
+
along,
|
|
7398
|
+
perp,
|
|
7399
|
+
rotateGroup,
|
|
7400
|
+
readingAxis,
|
|
7401
|
+
glyphWidth
|
|
7402
|
+
});
|
|
7403
|
+
}
|
|
7404
|
+
return {
|
|
7405
|
+
strokes,
|
|
7406
|
+
fills,
|
|
7407
|
+
gaps,
|
|
7408
|
+
groups
|
|
7409
|
+
};
|
|
7410
|
+
}
|
|
7411
|
+
//#endregion
|
|
7412
|
+
//#region src/generators/cm11-command-control-lines/boundary.ts
|
|
7413
|
+
/**
|
|
7414
|
+
* Boundary's Field T/AS mapping: unit 1 (`T`/`AS`) on the left-of-travel side,
|
|
7415
|
+
* unit 2 (`T1`/`AS1`) on the right.
|
|
7416
|
+
*/
|
|
7417
|
+
const BOUNDARY_UNIT_LABEL_FIELDS = {
|
|
7418
|
+
unit1: {
|
|
7419
|
+
designator: "T",
|
|
7420
|
+
country: "AS"
|
|
7421
|
+
},
|
|
7422
|
+
unit2: {
|
|
7423
|
+
designator: "T1",
|
|
7424
|
+
country: "AS1"
|
|
7425
|
+
}
|
|
7426
|
+
};
|
|
7427
|
+
/** Default options for the Boundary control measure. */
|
|
7428
|
+
const DEFAULT_BOUNDARY_OPTIONS = {
|
|
7429
|
+
echelon: "battalion",
|
|
7430
|
+
echelonSize: 750,
|
|
7431
|
+
echelonSizePixels: 16,
|
|
7432
|
+
echelonPadding: .3,
|
|
7433
|
+
labelRepetitions: 1,
|
|
7434
|
+
labelSpacing: 1,
|
|
7435
|
+
labelPosition: .5,
|
|
7436
|
+
labelPadding: ECHELON_UNIT_LABEL_PADDING,
|
|
7437
|
+
labelOrientation: "along",
|
|
7438
|
+
smooth: false,
|
|
7439
|
+
smoothResolution: 12
|
|
7440
|
+
};
|
|
7441
|
+
const BOUNDARY_METADATA = {
|
|
7442
|
+
id: "boundary",
|
|
7443
|
+
name: "Boundary",
|
|
7444
|
+
description: "Delineates the area of responsibility between two adjacent units, labeled with the echelon and the designators of the units it separates.",
|
|
7445
|
+
entity: "Command and Control Lines",
|
|
7446
|
+
entityType: "Boundary",
|
|
7447
|
+
value: "110100",
|
|
7448
|
+
minCoordinates: 2,
|
|
7449
|
+
geometry: "line",
|
|
7450
|
+
geometryTypes: [
|
|
7451
|
+
"MultiLineString",
|
|
7452
|
+
"MultiPolygon",
|
|
7453
|
+
"Point"
|
|
7454
|
+
],
|
|
7455
|
+
paints: {
|
|
7456
|
+
stroke: true,
|
|
7457
|
+
fill: "fixed",
|
|
7458
|
+
text: true
|
|
7459
|
+
},
|
|
7460
|
+
drawRule: "Line1",
|
|
7461
|
+
params: [
|
|
7462
|
+
{
|
|
7463
|
+
key: "smooth",
|
|
7464
|
+
label: "Smooth",
|
|
7465
|
+
description: "Round the boundary's corners by curving it through the control points.",
|
|
7466
|
+
type: "boolean"
|
|
7467
|
+
},
|
|
7468
|
+
{
|
|
7469
|
+
key: "smoothResolution",
|
|
7470
|
+
presentationTier: "advanced",
|
|
7471
|
+
label: "Smooth resolution",
|
|
7472
|
+
description: "Number of samples per segment when smooth mode is enabled.",
|
|
7473
|
+
type: "number",
|
|
7474
|
+
min: 2,
|
|
7475
|
+
max: 64,
|
|
7476
|
+
step: 1,
|
|
7477
|
+
visibleWhen: (opts) => Boolean(opts.smooth)
|
|
7478
|
+
},
|
|
7479
|
+
...echelonLineParams("boundary"),
|
|
7480
|
+
{
|
|
7481
|
+
key: "labelPadding",
|
|
7482
|
+
presentationTier: "advanced",
|
|
7483
|
+
label: "Label padding",
|
|
7484
|
+
description: "Gap between the line/echelon and the unit labels, as a ratio of the echelon height",
|
|
7485
|
+
type: "number",
|
|
7486
|
+
min: 0,
|
|
7487
|
+
max: 2,
|
|
7488
|
+
step: .05
|
|
7489
|
+
}
|
|
7324
7490
|
],
|
|
7325
7491
|
textAmplifiers: [
|
|
7326
7492
|
{
|
|
@@ -7362,80 +7528,22 @@ const BOUNDARY_METADATA = {
|
|
|
7362
7528
|
*/
|
|
7363
7529
|
function createBoundary(positions, options = {}, textAmplifiers = {}, context = {}) {
|
|
7364
7530
|
const { echelon = DEFAULT_BOUNDARY_OPTIONS.echelon, echelonSize = DEFAULT_BOUNDARY_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_BOUNDARY_OPTIONS.echelonPadding, metersPerPixel, labelRepetitions = DEFAULT_BOUNDARY_OPTIONS.labelRepetitions, labelSpacing = DEFAULT_BOUNDARY_OPTIONS.labelSpacing, labelPosition = DEFAULT_BOUNDARY_OPTIONS.labelPosition, labelPadding = DEFAULT_BOUNDARY_OPTIONS.labelPadding, labelOrientation = DEFAULT_BOUNDARY_OPTIONS.labelOrientation, smooth = DEFAULT_BOUNDARY_OPTIONS.smooth, smoothResolution = DEFAULT_BOUNDARY_OPTIONS.smoothResolution } = options;
|
|
7365
|
-
const
|
|
7366
|
-
const unit1Country = textAmplifiers.AS ?? "";
|
|
7367
|
-
const unit2Designator = textAmplifiers.T1 ?? "";
|
|
7368
|
-
const unit2Country = textAmplifiers.AS1 ?? "";
|
|
7369
|
-
let h = echelonSize;
|
|
7370
|
-
if (echelonSizePixels !== void 0 && metersPerPixel !== void 0 && metersPerPixel > 0) h = echelonSizePixels * metersPerPixel;
|
|
7371
|
-
h = Math.max(EPSILON, h);
|
|
7372
|
-
const reps = resolveLabelRepetitions(labelRepetitions);
|
|
7373
|
-
const singleGroupPosition = clampLinePosition(labelPosition, DEFAULT_BOUNDARY_OPTIONS.labelPosition);
|
|
7531
|
+
const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
|
|
7374
7532
|
const spec = resolveEchelon(echelon);
|
|
7375
|
-
const
|
|
7376
|
-
const
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
const glyphFills = [];
|
|
7533
|
+
const { properties: labelSize, screenAnchored } = echelonUnitLabelSize(h, echelonSizePixels, metersPerPixel);
|
|
7534
|
+
const verts = smoothLineVerts(positions.map((p) => project(p[0], p[1])), {
|
|
7535
|
+
smooth,
|
|
7536
|
+
smoothResolution
|
|
7537
|
+
}, 12);
|
|
7381
7538
|
const labelFeatures = [];
|
|
7382
|
-
const gaps =
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
const seg = {
|
|
7391
|
-
along: frame.along,
|
|
7392
|
-
perp: frame.perp
|
|
7393
|
-
};
|
|
7394
|
-
const units = [{
|
|
7395
|
-
side: 1,
|
|
7396
|
-
designator: unit1Designator,
|
|
7397
|
-
country: unit1Country
|
|
7398
|
-
}, {
|
|
7399
|
-
side: -1,
|
|
7400
|
-
designator: unit2Designator,
|
|
7401
|
-
country: unit2Country
|
|
7402
|
-
}];
|
|
7403
|
-
const rotateGroup = labelOrientation === "standard" && Math.abs(seg.along[0]) < Math.abs(seg.along[1]);
|
|
7404
|
-
const readingAxis = seg.perp[0] >= 0 ? seg.perp : vecScale(seg.perp, -1);
|
|
7405
|
-
const glyphAlong = rotateGroup ? readingAxis : seg.along;
|
|
7406
|
-
const glyphPerp = rotateGroup ? [-readingAxis[1], readingAxis[0]] : seg.perp;
|
|
7407
|
-
let glyphWidth = 0;
|
|
7408
|
-
if (spec) {
|
|
7409
|
-
const { strokes, fills, width } = buildEchelonGlyph(center, glyphAlong, glyphPerp, h, spec);
|
|
7410
|
-
glyphWidth = width;
|
|
7411
|
-
glyphStrokes.push(...strokes);
|
|
7412
|
-
glyphFills.push(...fills);
|
|
7413
|
-
const halfGap = (rotateGroup ? h : width) / 2 + Math.max(0, echelonPadding) * h;
|
|
7414
|
-
gaps.push({
|
|
7415
|
-
g0: target - halfGap,
|
|
7416
|
-
g1: target + halfGap
|
|
7417
|
-
});
|
|
7418
|
-
}
|
|
7419
|
-
if (rotateGroup) {
|
|
7420
|
-
const sign = vecDot(seg.perp, readingAxis) >= 0 ? 1 : -1;
|
|
7421
|
-
const clearance = glyphWidth / 2 + Math.max(0, labelPadding) * h;
|
|
7422
|
-
units[0].textAnchor = sign > 0 ? "start" : "end";
|
|
7423
|
-
units[1].textAnchor = sign > 0 ? "end" : "start";
|
|
7424
|
-
pushUnitLabels(labelFeatures, {
|
|
7425
|
-
center,
|
|
7426
|
-
perp: vecScale(readingAxis, sign),
|
|
7427
|
-
offset: clearance,
|
|
7428
|
-
rotation: labelRotationAlong(readingAxis),
|
|
7429
|
-
labelSize
|
|
7430
|
-
}, units);
|
|
7431
|
-
} else pushUnitLabels(labelFeatures, {
|
|
7432
|
-
center,
|
|
7433
|
-
perp: seg.perp,
|
|
7434
|
-
offset: (.45 + Math.max(0, labelPadding)) * h,
|
|
7435
|
-
rotation: labelRotationAlong(seg.along),
|
|
7436
|
-
labelSize
|
|
7437
|
-
}, units);
|
|
7438
|
-
}
|
|
7539
|
+
const { strokes: glyphStrokes, fills: glyphFills, gaps, groups } = placeEchelonsAlongLine(verts, spec, h, {
|
|
7540
|
+
labelRepetitions,
|
|
7541
|
+
labelPosition,
|
|
7542
|
+
labelSpacing,
|
|
7543
|
+
labelOrientation,
|
|
7544
|
+
echelonPadding
|
|
7545
|
+
});
|
|
7546
|
+
pushEchelonUnitLabels(labelFeatures, groups, unitLabelsFromAmplifiers(textAmplifiers, BOUNDARY_UNIT_LABEL_FIELDS), h, labelPadding, labelSize);
|
|
7439
7547
|
const hostileLabelOptions = screenAnchored ? {
|
|
7440
7548
|
labelSizePixels: echelonSizePixels,
|
|
7441
7549
|
metersPerPixel,
|
|
@@ -7722,8 +7830,26 @@ const ENGINEER_WORK_LINE = defineControlMeasure({
|
|
|
7722
7830
|
});
|
|
7723
7831
|
//#endregion
|
|
7724
7832
|
//#region src/generators/cm11-command-control-lines/generic-c2-line.ts
|
|
7833
|
+
const GENERIC_C2_UNIT_LABEL_FIELDS = {
|
|
7834
|
+
unit1: {
|
|
7835
|
+
designator: "T1",
|
|
7836
|
+
country: "AS1"
|
|
7837
|
+
},
|
|
7838
|
+
unit2: {
|
|
7839
|
+
designator: "T2",
|
|
7840
|
+
country: "AS2"
|
|
7841
|
+
}
|
|
7842
|
+
};
|
|
7725
7843
|
/** Default options for the Generic command and control line. */
|
|
7726
7844
|
const DEFAULT_GENERIC_C2_LINE_OPTIONS = {
|
|
7845
|
+
echelon: "none",
|
|
7846
|
+
echelonSize: 750,
|
|
7847
|
+
echelonSizePixels: 16,
|
|
7848
|
+
echelonPadding: .3,
|
|
7849
|
+
labelRepetitions: 1,
|
|
7850
|
+
labelSpacing: 1,
|
|
7851
|
+
labelPosition: .5,
|
|
7852
|
+
labelOrientation: "along",
|
|
7727
7853
|
includePrefix: true,
|
|
7728
7854
|
smooth: false,
|
|
7729
7855
|
smoothResolution: 12
|
|
@@ -7731,22 +7857,28 @@ const DEFAULT_GENERIC_C2_LINE_OPTIONS = {
|
|
|
7731
7857
|
const GENERIC_C2_LINE_METADATA = {
|
|
7732
7858
|
id: "generic-c2-line",
|
|
7733
7859
|
name: "Generic command and control line",
|
|
7734
|
-
description: "Illustrative command-and-control-style line (not part of MIL-STD-2525E) carrying optional additional-information/designation text above it and an effective date-time-group window below it.",
|
|
7860
|
+
description: "Illustrative command-and-control-style line (not part of MIL-STD-2525E) carrying optional echelon glyphs, additional-information/designation text above it, and an effective date-time-group window below it.",
|
|
7735
7861
|
entity: "Command and Control Lines",
|
|
7736
7862
|
entityType: "Generic",
|
|
7737
7863
|
value: "110400",
|
|
7738
7864
|
minCoordinates: 2,
|
|
7739
7865
|
geometry: "line",
|
|
7740
|
-
geometryTypes: [
|
|
7866
|
+
geometryTypes: [
|
|
7867
|
+
"LineString",
|
|
7868
|
+
"MultiLineString",
|
|
7869
|
+
"MultiPolygon",
|
|
7870
|
+
"Point"
|
|
7871
|
+
],
|
|
7741
7872
|
paints: {
|
|
7742
7873
|
stroke: true,
|
|
7743
|
-
fill: "
|
|
7874
|
+
fill: "fixed",
|
|
7744
7875
|
text: true
|
|
7745
7876
|
},
|
|
7746
7877
|
drawRule: "Line1",
|
|
7747
7878
|
capturesLabelSize: true,
|
|
7748
7879
|
params: [
|
|
7749
7880
|
...SMOOTH_LINE_PARAMS,
|
|
7881
|
+
...echelonLineParams("line"),
|
|
7750
7882
|
{
|
|
7751
7883
|
key: "phaseLineName",
|
|
7752
7884
|
presentationTier: "primary",
|
|
@@ -7765,16 +7897,7 @@ const GENERIC_C2_LINE_METADATA = {
|
|
|
7765
7897
|
type: "boolean",
|
|
7766
7898
|
visibleWhen: (opts) => Boolean(String(opts.phaseLineName ?? "").trim())
|
|
7767
7899
|
},
|
|
7768
|
-
|
|
7769
|
-
key: "labelPadding",
|
|
7770
|
-
presentationTier: "advanced",
|
|
7771
|
-
label: "Label padding",
|
|
7772
|
-
description: "Extra clearance between the line and its labels, as a ratio of the label height",
|
|
7773
|
-
type: "number",
|
|
7774
|
-
min: 0,
|
|
7775
|
-
max: 2,
|
|
7776
|
-
step: .05
|
|
7777
|
-
}
|
|
7900
|
+
LINE_LABEL_PADDING_PARAM
|
|
7778
7901
|
],
|
|
7779
7902
|
textAmplifiers: [
|
|
7780
7903
|
{
|
|
@@ -7789,6 +7912,30 @@ const GENERIC_C2_LINE_METADATA = {
|
|
|
7789
7912
|
description: "Field T — unique designation of the line, shown above it near each end.",
|
|
7790
7913
|
maxLength: 21
|
|
7791
7914
|
},
|
|
7915
|
+
{
|
|
7916
|
+
key: "T1",
|
|
7917
|
+
label: "Unit 1 designation",
|
|
7918
|
+
description: "Field T1 — designation of unit 1 on the left-of-travel side.",
|
|
7919
|
+
maxLength: 32
|
|
7920
|
+
},
|
|
7921
|
+
{
|
|
7922
|
+
key: "AS1",
|
|
7923
|
+
label: "Unit 1 country",
|
|
7924
|
+
description: "Field AS1 — country code of unit 1 on the left-of-travel side.",
|
|
7925
|
+
maxLength: 8
|
|
7926
|
+
},
|
|
7927
|
+
{
|
|
7928
|
+
key: "T2",
|
|
7929
|
+
label: "Unit 2 designation",
|
|
7930
|
+
description: "Field T2 — designation of unit 2 on the right-of-travel side.",
|
|
7931
|
+
maxLength: 32
|
|
7932
|
+
},
|
|
7933
|
+
{
|
|
7934
|
+
key: "AS2",
|
|
7935
|
+
label: "Unit 2 country",
|
|
7936
|
+
description: "Field AS2 — country code of unit 2 on the right-of-travel side.",
|
|
7937
|
+
maxLength: 8
|
|
7938
|
+
},
|
|
7792
7939
|
{
|
|
7793
7940
|
key: "W",
|
|
7794
7941
|
label: "Effective from (DTG)",
|
|
@@ -7814,19 +7961,62 @@ const GENERIC_C2_LINE_METADATA = {
|
|
|
7814
7961
|
* `base + perp·offset` and the other at `base − perp·offset`.
|
|
7815
7962
|
*/
|
|
7816
7963
|
function createGenericC2Line(positions, options = {}, textAmplifiers = {}, context = {}) {
|
|
7817
|
-
const { phaseLineName, includePrefix = true, smooth, smoothResolution, ...sizeOptions } = options;
|
|
7964
|
+
const { echelon = DEFAULT_GENERIC_C2_LINE_OPTIONS.echelon, echelonSize = DEFAULT_GENERIC_C2_LINE_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_GENERIC_C2_LINE_OPTIONS.echelonPadding, labelRepetitions = DEFAULT_GENERIC_C2_LINE_OPTIONS.labelRepetitions, labelSpacing = DEFAULT_GENERIC_C2_LINE_OPTIONS.labelSpacing, labelPosition = DEFAULT_GENERIC_C2_LINE_OPTIONS.labelPosition, labelOrientation = DEFAULT_GENERIC_C2_LINE_OPTIONS.labelOrientation, phaseLineName, includePrefix = true, smooth, smoothResolution, ...sizeOptions } = options;
|
|
7818
7965
|
const verts = smoothLineVerts(positions.map((p) => project(p[0], p[1])), {
|
|
7819
7966
|
smooth,
|
|
7820
7967
|
smoothResolution
|
|
7821
7968
|
}, 12);
|
|
7822
|
-
const features = [
|
|
7969
|
+
const features = [];
|
|
7970
|
+
const spec = resolveEchelon(echelon);
|
|
7971
|
+
const h = resolveEchelonHeight(echelonSize, echelonSizePixels, sizeOptions.metersPerPixel);
|
|
7972
|
+
const { strokes: glyphStrokes, fills: glyphFills, gaps, groups } = spec ? placeEchelonsAlongLine(verts, spec, h, {
|
|
7973
|
+
labelRepetitions,
|
|
7974
|
+
labelPosition,
|
|
7975
|
+
labelSpacing,
|
|
7976
|
+
labelOrientation,
|
|
7977
|
+
echelonPadding
|
|
7978
|
+
}) : EMPTY_LINE_ECHELON_PLACEMENT;
|
|
7979
|
+
const gappedCoordinates = gaps.length > 0 ? buildGappedLine(verts, gaps) : [];
|
|
7980
|
+
if (gappedCoordinates.length > 0) features.push({
|
|
7981
|
+
type: "Feature",
|
|
7982
|
+
properties: { part: "generic-c2-line" },
|
|
7983
|
+
geometry: {
|
|
7984
|
+
type: "MultiLineString",
|
|
7985
|
+
coordinates: gappedCoordinates
|
|
7986
|
+
}
|
|
7987
|
+
});
|
|
7988
|
+
else if (gaps.length === 0) features.push({
|
|
7823
7989
|
type: "Feature",
|
|
7824
7990
|
properties: { part: "generic-c2-line" },
|
|
7825
7991
|
geometry: {
|
|
7826
7992
|
type: "LineString",
|
|
7827
7993
|
coordinates: smooth ? verts.map((v) => unproject(v[0], v[1])) : positions
|
|
7828
7994
|
}
|
|
7829
|
-
}
|
|
7995
|
+
});
|
|
7996
|
+
if (glyphStrokes.length > 0) features.push({
|
|
7997
|
+
type: "Feature",
|
|
7998
|
+
properties: { part: "echelon" },
|
|
7999
|
+
geometry: {
|
|
8000
|
+
type: "MultiLineString",
|
|
8001
|
+
coordinates: glyphStrokes
|
|
8002
|
+
}
|
|
8003
|
+
});
|
|
8004
|
+
if (glyphFills.length > 0) features.push({
|
|
8005
|
+
type: "Feature",
|
|
8006
|
+
properties: {
|
|
8007
|
+
part: "echelon",
|
|
8008
|
+
fill: true,
|
|
8009
|
+
style: SOLID_ACCENT_FILL
|
|
8010
|
+
},
|
|
8011
|
+
geometry: {
|
|
8012
|
+
type: "MultiPolygon",
|
|
8013
|
+
coordinates: glyphFills.map((ring) => [ring])
|
|
8014
|
+
}
|
|
8015
|
+
});
|
|
8016
|
+
const { properties: unitLabelSize } = echelonUnitLabelSize(h, echelonSizePixels, sizeOptions.metersPerPixel);
|
|
8017
|
+
const unitLabels = [];
|
|
8018
|
+
pushEchelonUnitLabels(unitLabels, groups, unitLabelsFromAmplifiers(textAmplifiers, GENERIC_C2_UNIT_LABEL_FIELDS), h, sizeOptions.labelPadding ?? .4, unitLabelSize);
|
|
8019
|
+
features.push(...unitLabels);
|
|
7830
8020
|
const above = [textAmplifiers.H, textAmplifiers.T].filter((v) => Boolean(v)).join(" ");
|
|
7831
8021
|
const below = [textAmplifiers.W, textAmplifiers.W1].filter((v) => Boolean(v)).join(" - ");
|
|
7832
8022
|
const trimmedPhaseLineName = phaseLineName?.trim();
|
|
@@ -12442,6 +12632,98 @@ const FINAL_COORDINATION_LINE = defineControlMeasure({
|
|
|
12442
12632
|
}
|
|
12443
12633
|
});
|
|
12444
12634
|
//#endregion
|
|
12635
|
+
//#region src/generators/cm14-maneuver-lines/infiltration-lane.ts
|
|
12636
|
+
const DEFAULT_INFILTRATION_LANE_OPTIONS = {};
|
|
12637
|
+
const INFILTRATION_LANE_METADATA = {
|
|
12638
|
+
id: "infiltration-lane",
|
|
12639
|
+
name: "Infiltration Lane",
|
|
12640
|
+
description: "A control measure that coordinates forward and lateral movement of infiltrating units and fixes fire planning responsibilities.",
|
|
12641
|
+
entity: "Maneuver Lines",
|
|
12642
|
+
entityType: "Infiltration Lane",
|
|
12643
|
+
value: "140800",
|
|
12644
|
+
minCoordinates: 3,
|
|
12645
|
+
maxCoordinates: 3,
|
|
12646
|
+
geometry: "line",
|
|
12647
|
+
geometryTypes: ["MultiLineString", "Point"],
|
|
12648
|
+
paints: {
|
|
12649
|
+
stroke: true,
|
|
12650
|
+
fill: "none",
|
|
12651
|
+
text: true
|
|
12652
|
+
},
|
|
12653
|
+
drawRule: "Polyline1",
|
|
12654
|
+
capturesLabelSize: true,
|
|
12655
|
+
textAmplifiers: [{
|
|
12656
|
+
key: "T",
|
|
12657
|
+
label: "Unique designation",
|
|
12658
|
+
description: "Field T — the infiltration lane's unique designation.",
|
|
12659
|
+
placeholder: "GREEN",
|
|
12660
|
+
maxLength: 21
|
|
12661
|
+
}]
|
|
12662
|
+
};
|
|
12663
|
+
/** Creates the two parallel boundaries and centered Field T lane designation. */
|
|
12664
|
+
function createInfiltrationLane(coordinates, options = {}, textAmplifiers = {}) {
|
|
12665
|
+
const [c1, c2, c3] = coordinates;
|
|
12666
|
+
const p1 = project(c1[0], c1[1]);
|
|
12667
|
+
const p2 = project(c2[0], c2[1]);
|
|
12668
|
+
const frame = createProjectedBaselineFrame(p1, p2, {
|
|
12669
|
+
origin: "midpoint",
|
|
12670
|
+
normal: "left"
|
|
12671
|
+
});
|
|
12672
|
+
if (!frame) return {
|
|
12673
|
+
type: "FeatureCollection",
|
|
12674
|
+
features: []
|
|
12675
|
+
};
|
|
12676
|
+
const signedHalfWidth = frame.signedNormalDistance(c3);
|
|
12677
|
+
if (Math.abs(signedHalfWidth) < 1e-6) return {
|
|
12678
|
+
type: "FeatureCollection",
|
|
12679
|
+
features: []
|
|
12680
|
+
};
|
|
12681
|
+
const midpoint = frame.origin;
|
|
12682
|
+
const offset = vecScale(frame.normal, signedHalfWidth);
|
|
12683
|
+
const features = [{
|
|
12684
|
+
type: "Feature",
|
|
12685
|
+
properties: { part: "infiltration-lane" },
|
|
12686
|
+
geometry: {
|
|
12687
|
+
type: "MultiLineString",
|
|
12688
|
+
coordinates: [[vecAdd(p1, offset), vecAdd(p2, offset)], [vecSub(p1, offset), vecSub(p2, offset)]].map((line) => line.map((point) => unproject(point[0], point[1])))
|
|
12689
|
+
}
|
|
12690
|
+
}];
|
|
12691
|
+
if (textAmplifiers.T) features.push({
|
|
12692
|
+
type: "Feature",
|
|
12693
|
+
properties: {
|
|
12694
|
+
part: "label",
|
|
12695
|
+
labelPlacement: false,
|
|
12696
|
+
amplifierField: "T",
|
|
12697
|
+
text: textAmplifiers.T,
|
|
12698
|
+
rotation: labelRotationAlong(frame.direction),
|
|
12699
|
+
...labelSizeProps(options)
|
|
12700
|
+
},
|
|
12701
|
+
geometry: {
|
|
12702
|
+
type: "Point",
|
|
12703
|
+
coordinates: unproject(midpoint[0], midpoint[1])
|
|
12704
|
+
}
|
|
12705
|
+
});
|
|
12706
|
+
return {
|
|
12707
|
+
type: "FeatureCollection",
|
|
12708
|
+
features
|
|
12709
|
+
};
|
|
12710
|
+
}
|
|
12711
|
+
const INFILTRATION_LANE = defineControlMeasure({
|
|
12712
|
+
metadata: INFILTRATION_LANE_METADATA,
|
|
12713
|
+
generator: createInfiltrationLane,
|
|
12714
|
+
defaultOptions: DEFAULT_INFILTRATION_LANE_OPTIONS,
|
|
12715
|
+
rule: polyline1DrawRule,
|
|
12716
|
+
previewSample: {
|
|
12717
|
+
controlPoints: [
|
|
12718
|
+
[-1, 0],
|
|
12719
|
+
[1, 0],
|
|
12720
|
+
[0, .36]
|
|
12721
|
+
],
|
|
12722
|
+
textAmplifiers: { T: "GREEN" },
|
|
12723
|
+
options: { labelSize: 100 }
|
|
12724
|
+
}
|
|
12725
|
+
});
|
|
12726
|
+
//#endregion
|
|
12445
12727
|
//#region src/generators/cm14-maneuver-lines/airhead-line.ts
|
|
12446
12728
|
/** Clearance between the boundary's southernmost point and the caption, as a
|
|
12447
12729
|
* ratio of the caption's height. */
|
|
@@ -16837,6 +17119,7 @@ const DEFINITIONS = {
|
|
|
16837
17119
|
"forward-edge-of-battle-area": FORWARD_EDGE_OF_BATTLE_AREA,
|
|
16838
17120
|
"bridgehead-line": BRIDGEHEAD_LINE,
|
|
16839
17121
|
"final-coordination-line": FINAL_COORDINATION_LINE,
|
|
17122
|
+
"infiltration-lane": INFILTRATION_LANE,
|
|
16840
17123
|
"airhead-line": AIRHEAD_LINE,
|
|
16841
17124
|
"holding-line": HOLDING_LINE,
|
|
16842
17125
|
"release-line": RELEASE_LINE,
|
|
@@ -17292,4 +17575,4 @@ function assertNever(value) {
|
|
|
17292
17575
|
throw new Error(`Unhandled control measure kind: ${String(value)}`);
|
|
17293
17576
|
}
|
|
17294
17577
|
//#endregion
|
|
17295
|
-
export {
|
|
17578
|
+
export { DEFAULT_ENCIRCLEMENT_OPTIONS as $, DEFAULT_PORTRAYAL as $t, DEFAULT_RETIRE_OPTIONS as A, pointOnMidpointPerpendicularAxis as An, DEFAULT_BLOCK_OPTIONS as At, DEFAULT_LIMIT_OF_ADVANCE_OPTIONS as B, getMetersPerPixel as Bn, DEFAULT_ENGAGEMENT_AREA_OPTIONS as Bt, DEFAULT_MAIN_ATTACK_OPTIONS as C, blockDrawRule as Cn, DEFAULT_BREACH_OPTIONS as Ct, DEFAULT_WITHDRAW_OPTIONS as D, computeDefaultMidpointPerpendicularPoint as Dn, DEFAULT_LIGHT_LINE_OPTIONS as Dt, DEFAULT_WITHDRAW_UNDER_PRESSURE_OPTIONS as E, area27DrawRule as En, DEFAULT_ENGINEER_WORK_LINE_OPTIONS as Et, DEFAULT_FORTIFIED_LINE_OPTIONS as F, haversineDistance as Fn, DEFAULT_ANTITANK_WALL_OPTIONS as Ft, DEFAULT_HOLDING_LINE_OPTIONS as G, TEXT_AMPLIFIER_FIELDS as Gt, DEFAULT_HANDOVER_LINE_OPTIONS as H, DEFAULT_DIRECTION_OF_SUPPORTING_ATTACK_OPTIONS as Ht, DEFAULT_PROBABLE_LINE_OF_DEPLOYMENT_OPTIONS as I, project as In, DEFAULT_ANTITANK_DITCH_OPTIONS as It, DEFAULT_PHASE_LINE_OPTIONS as J, resolveAmplifierPlacement as Jt, DEFAULT_INFILTRATION_LANE_OPTIONS as K, canonicalTextAmplifierKey as Kt, PROBABLE_LINE_OF_DEPLOYMENT_DASH as L, sphericalBearing as Ln, DEFAULT_ASSEMBLY_AREA_OPTIONS as Lt, DEFAULT_PENETRATE_OPTIONS as M, createBaselineFrame as Mn, DEFAULT_BATTLE_POSITION_OPTIONS as Mt, DEFAULT_FRONTAL_ATTACK_OPTIONS as N, calculateMetrics as Nn, DEFAULT_ATTACK_HELICOPTER_OPTIONS as Nt, DEFAULT_SEIZE_OPTIONS as O, createMidpointPerpendicularDrawRule as On, DEFAULT_BOUNDARY_OPTIONS as Ot, DEFAULT_FORTIFIED_AREA_OPTIONS as P, computeInitialWidthPoint as Pn, DEFAULT_ATTACK_BY_FIRE_OPTIONS as Pt, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as Q, DEFAULT_LINE_JOIN as Qt, DEFAULT_LINE_OF_DEPARTURE_CONTACT_OPTIONS as R, unproject as Rn, DEFAULT_AREA_OF_OPERATIONS_OPTIONS as Rt, DEFAULT_MINEFIELD_OPTIONS as S, disruptDrawRule as Sn, DEFAULT_BYPASS_OPTIONS as St, DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS as T, containDrawRule as Tn, DEFAULT_GENERIC_C2_LINE_OPTIONS as Tt, DEFAULT_FORWARD_EDGE_OF_BATTLE_AREA_OPTIONS as U, DEFAULT_DIRECTION_OF_MAIN_ATTACK_OPTIONS as Ut, DEFAULT_BATTLE_HANDOVER_LINE_OPTIONS as V, roundToFixed as Vn, DEFAULT_AREA_DEFENSE_OPTIONS as Vt, DEFAULT_RELEASE_LINE_OPTIONS as W, DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS as Wt, DEFAULT_FIX_MISSION_TASK_OPTIONS as X, DEFAULT_LABEL_SIZE_CLAMP_CSS_PIXELS as Xt, DEFAULT_FLOT_OPTIONS as Y, DEFAULT_LABEL_HEIGHT_CSS_PIXELS as Yt, DEFAULT_FIX_OPTIONS as Z, DEFAULT_LINE_CAP as Zt, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as _, dynamicPointDrawRule as _n, DEFAULT_GENERIC_POLYGON_OPTIONS as _t, DEFINITIONS as a, rectangleDrawRule as an, DEFAULT_COVER_OPTIONS as at, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as b, point12DrawRule as bn, DEFAULT_CLASSIC_ARROW_OPTIONS as bt, getDefaultOptions as c, polyline1DrawRule as cn, DEFAULT_COUNTERATTACK_OPTIONS as ct, DEFAULT_TURN_OPTIONS as d, line24DrawRule as dn, DEFAULT_RADAR_SEARCH_DOCTRINE_OPTIONS as dt, DEFAULT_STROKE_DASH_CSS_PIXELS as en, DEFAULT_DISRUPT_OPTIONS as et, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as f, line23DrawRule as fn, RADAR_SEARCH_DOCTRINE_FILL_COLOR as ft, SECONDARY_DIRECTION_OF_FIRE_DASH as g, attackByFireDrawRule as gn, DEFAULT_GENERIC_RECTANGLE_OPTIONS as gt, DEFAULT_SECONDARY_DIRECTION_OF_FIRE_OPTIONS as h, ambushDrawRule as hn, DEFAULT_GENERIC_SECTOR_OPTIONS as ht, CONTROL_MEASURE_METADATA as i, DEFAULT_AIRBORNE_ATTACK_OPTIONS as in, DEFAULT_DELAY_OPTIONS as it, DEFAULT_REARWARD_PASSAGE_OF_LINES_OPTIONS as j, snapToMidpointPerpendicular as jn, DEFAULT_CONTAIN_OPTIONS as jt, DEFAULT_SCREEN_OPTIONS as k, getMidpointPerpendicularSignedDistance as kn, DEFAULT_BLOCK_ARROW_OPTIONS as kt, listControlMeasureMetadata as l, line27DrawRule as ln, DEFAULT_SUPPORTING_ATTACK_OPTIONS as lt, DEFAULT_STRONG_POINT_OPTIONS as m, line1DrawRule as mn, DEFAULT_GENERIC_TEXT_OPTIONS as mt, resolveStyleHints as n, DEFAULT_SYMBOL_COLOR as nn, DEFAULT_DISRUPT_MISSION_TASK_OPTIONS as nt, getControlMeasureMetadata as o, axis1DrawRule as on, DEFAULT_TACTICAL_ARROW_OPTIONS as ot, DEFAULT_RETAIN_OPTIONS as p, turnDrawRule as pn, RADAR_SEARCH_DOCTRINE_STROKE_COLOR as pt, DEFAULT_BRIDGEHEAD_LINE_OPTIONS as q, normalizeTextAmplifiers as qt, CONTROL_MEASURE_IDS as r, DEFAULT_AMBUSH_OPTIONS as rn, DEFAULT_DISENGAGE_OPTIONS as rt, getControlMeasureMetadataByValue as s, supportByFireDrawRule as sn, DEFAULT_COUNTERATTACK_BY_FIRE_OPTIONS as st, renderControlMeasure as t, DEFAULT_STROKE_WIDTH_CSS_PIXELS as tn, DEFAULT_GUARD_OPTIONS as tt, DEFAULT_TURNING_MOVEMENT_OPTIONS as u, line26DrawRule as un, DEFAULT_CLEAR_OPTIONS as ut, DEFAULT_PICKUP_ZONE_OPTIONS as v, sectorDrawRule as vn, DEFAULT_GENERIC_LINE_OPTIONS as vt, DEFAULT_LANDING_ZONE_OPTIONS as w, centerRadiusDrawRule as wn, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as wt, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as x, penetrateDrawRule as xn, DEFAULT_CANALIZE_OPTIONS as xt, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as y, staticPointDrawRule as yn, DEFAULT_GENERIC_CIRCLE_OPTIONS as yt, DEFAULT_LINE_OF_DEPARTURE_OPTIONS as z, EPSILON as zn, DEFAULT_MOBILE_DEFENSE_OPTIONS as zt };
|