@orbat-mapper/control-measures 0.2.0 → 0.3.1
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 +14 -0
- package/dist/{index-DtgRm_dz.d.mts → index-85rqbfeE.d.mts} +143 -2
- 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-CAfC2nEw.mjs → renderControlMeasure-awInmGJN.mjs} +1280 -105
- package/media/bridgehead-line.svg +1 -0
- package/media/counterattack-by-fire.svg +1 -0
- package/media/disengage.svg +1 -0
- package/media/disrupt-mission-task.svg +1 -0
- package/media/fix-mission-task.svg +1 -0
- package/media/holding-line.svg +1 -0
- package/media/penetrate.svg +1 -0
- package/media/rearward-passage-of-lines.svg +1 -0
- package/media/release-line.svg +1 -0
- package/media/retire.svg +1 -0
- package/media/seize.svg +1 -0
- package/media/text.svg +1 -0
- package/media/withdraw-under-pressure.svg +1 -0
- package/media/withdraw.svg +1 -0
- package/package.json +1 -1
|
@@ -558,7 +558,7 @@ function samePosition(a, b) {
|
|
|
558
558
|
}
|
|
559
559
|
//#endregion
|
|
560
560
|
//#region src/draw-rules/area1.ts
|
|
561
|
-
function derive$
|
|
561
|
+
function derive$11(points) {
|
|
562
562
|
return clonePositions(points);
|
|
563
563
|
}
|
|
564
564
|
/**
|
|
@@ -578,9 +578,9 @@ const area1DrawRule = {
|
|
|
578
578
|
id: "area1",
|
|
579
579
|
minimumUserPoints: 3,
|
|
580
580
|
closedRing: true,
|
|
581
|
-
derive: derive$
|
|
581
|
+
derive: derive$11,
|
|
582
582
|
transform(event) {
|
|
583
|
-
return derive$
|
|
583
|
+
return derive$11(event.next);
|
|
584
584
|
}
|
|
585
585
|
};
|
|
586
586
|
//#endregion
|
|
@@ -606,14 +606,14 @@ const disruptDrawRule = createMidpointPerpendicularDrawRule({
|
|
|
606
606
|
* that fixes both the radius and the bearing of the symbol's opening. Both
|
|
607
607
|
* points are user-clicked, so the canonical array is just the (clamped) input.
|
|
608
608
|
*/
|
|
609
|
-
function derive$
|
|
609
|
+
function derive$10(points) {
|
|
610
610
|
return clonePositions(points.slice(0, 2));
|
|
611
611
|
}
|
|
612
612
|
const centerRadiusDrawRule = {
|
|
613
613
|
id: "area15:center-radius",
|
|
614
614
|
minimumUserPoints: 2,
|
|
615
615
|
canonicalPointCount: 2,
|
|
616
|
-
derive: derive$
|
|
616
|
+
derive: derive$10,
|
|
617
617
|
transform(event) {
|
|
618
618
|
const { previous, next, activePointIndex } = event;
|
|
619
619
|
if (activePointIndex === 0 && previous.length >= 2 && next.length >= 2) {
|
|
@@ -622,12 +622,25 @@ const centerRadiusDrawRule = {
|
|
|
622
622
|
const origin = previous[1];
|
|
623
623
|
return [clonePosition(next[0]), [origin[0] + dx, origin[1] + dy]];
|
|
624
624
|
}
|
|
625
|
-
return derive$
|
|
625
|
+
return derive$10(next);
|
|
626
626
|
}
|
|
627
627
|
};
|
|
628
628
|
/** Backward-compatible doctrinal name for Area15's shared center-radius rule. */
|
|
629
629
|
const isolateDrawRule = centerRadiusDrawRule;
|
|
630
630
|
//#endregion
|
|
631
|
+
//#region src/draw-rules/area17.ts
|
|
632
|
+
/**
|
|
633
|
+
* Area17 anchor draw rule for Penetrate.
|
|
634
|
+
*
|
|
635
|
+
* PT.1/PT.2 are the free barrier endpoints. PT.3 is the arrow tail,
|
|
636
|
+
* constrained to the perpendicular axis through the barrier midpoint.
|
|
637
|
+
*/
|
|
638
|
+
const penetrateDrawRule = createMidpointPerpendicularDrawRule({
|
|
639
|
+
id: "area17:penetrate",
|
|
640
|
+
minimumUserPoints: 3,
|
|
641
|
+
minimumPreviewPoints: 2
|
|
642
|
+
});
|
|
643
|
+
//#endregion
|
|
631
644
|
//#region src/draw-rules/point12.ts
|
|
632
645
|
/**
|
|
633
646
|
* Point12 anchor draw rule for the obstacle-bypass family.
|
|
@@ -645,16 +658,16 @@ const point12DrawRule = createMidpointPerpendicularDrawRule({ id: "point12:obsta
|
|
|
645
658
|
* Point1 — a single anchor point (e.g. Text's center). One user click commits
|
|
646
659
|
* the measure; dragging the point simply translates it.
|
|
647
660
|
*/
|
|
648
|
-
function derive$
|
|
661
|
+
function derive$9(points) {
|
|
649
662
|
return clonePositions(points.slice(0, 1));
|
|
650
663
|
}
|
|
651
664
|
const pointDrawRule = {
|
|
652
665
|
id: "point1:anchor",
|
|
653
666
|
minimumUserPoints: 1,
|
|
654
667
|
canonicalPointCount: 1,
|
|
655
|
-
derive: derive$
|
|
668
|
+
derive: derive$9,
|
|
656
669
|
transform(event) {
|
|
657
|
-
return derive$
|
|
670
|
+
return derive$9(event.next);
|
|
658
671
|
}
|
|
659
672
|
};
|
|
660
673
|
//#endregion
|
|
@@ -685,20 +698,20 @@ const ambushDrawRule = createMidpointPerpendicularDrawRule({
|
|
|
685
698
|
});
|
|
686
699
|
//#endregion
|
|
687
700
|
//#region src/draw-rules/line1.ts
|
|
688
|
-
function derive$
|
|
701
|
+
function derive$8(points) {
|
|
689
702
|
return clonePositions(points);
|
|
690
703
|
}
|
|
691
704
|
const line1DrawRule = {
|
|
692
705
|
id: "line1",
|
|
693
706
|
minimumUserPoints: 2,
|
|
694
|
-
derive: derive$
|
|
707
|
+
derive: derive$8,
|
|
695
708
|
transform(event) {
|
|
696
|
-
return derive$
|
|
709
|
+
return derive$8(event.next);
|
|
697
710
|
}
|
|
698
711
|
};
|
|
699
712
|
//#endregion
|
|
700
713
|
//#region src/draw-rules/line3.ts
|
|
701
|
-
function derive$
|
|
714
|
+
function derive$7(points) {
|
|
702
715
|
return clonePositions(points);
|
|
703
716
|
}
|
|
704
717
|
/**
|
|
@@ -713,23 +726,23 @@ const line3DrawRule = {
|
|
|
713
726
|
id: "line3",
|
|
714
727
|
minimumUserPoints: 3,
|
|
715
728
|
canonicalPointCount: 3,
|
|
716
|
-
derive: derive$
|
|
729
|
+
derive: derive$7,
|
|
717
730
|
transform(event) {
|
|
718
|
-
return derive$
|
|
731
|
+
return derive$7(event.next);
|
|
719
732
|
}
|
|
720
733
|
};
|
|
721
734
|
//#endregion
|
|
722
735
|
//#region src/draw-rules/line9.ts
|
|
723
|
-
function derive$
|
|
736
|
+
function derive$6(points) {
|
|
724
737
|
return clonePositions(points);
|
|
725
738
|
}
|
|
726
739
|
const line9DrawRule = {
|
|
727
740
|
id: "line9",
|
|
728
741
|
minimumUserPoints: 2,
|
|
729
742
|
canonicalPointCount: 2,
|
|
730
|
-
derive: derive$
|
|
743
|
+
derive: derive$6,
|
|
731
744
|
transform(event) {
|
|
732
|
-
return derive$
|
|
745
|
+
return derive$6(event.next);
|
|
733
746
|
}
|
|
734
747
|
};
|
|
735
748
|
//#endregion
|
|
@@ -836,7 +849,7 @@ const line24DrawRule = createMidpointPerpendicularDrawRule({
|
|
|
836
849
|
});
|
|
837
850
|
//#endregion
|
|
838
851
|
//#region src/draw-rules/line26.ts
|
|
839
|
-
function derive$
|
|
852
|
+
function derive$5(points) {
|
|
840
853
|
if (points.length === 2) {
|
|
841
854
|
const [p1, p2] = points;
|
|
842
855
|
const dx = p2[0] - p1[0];
|
|
@@ -875,9 +888,89 @@ const line26DrawRule = {
|
|
|
875
888
|
minimumUserPoints: 4,
|
|
876
889
|
minimumPreviewPoints: 2,
|
|
877
890
|
canonicalPointCount: 4,
|
|
891
|
+
derive: derive$5,
|
|
892
|
+
transform(event) {
|
|
893
|
+
return derive$5(event.next);
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
//#endregion
|
|
897
|
+
//#region src/draw-rules/line27.ts
|
|
898
|
+
function deriveArcPoint(p1, p2, p4) {
|
|
899
|
+
const center = project(p1[0], p1[1]);
|
|
900
|
+
const start = project(p2[0], p2[1]);
|
|
901
|
+
const tip = project(p4[0], p4[1]);
|
|
902
|
+
const arc = createQuarterArc(start, tip, quarterArcSide(start, tip, center) === 1 ? -1 : 1);
|
|
903
|
+
if (!arc) return [(p2[0] + p4[0]) / 2, (p2[1] + p4[1]) / 2];
|
|
904
|
+
return unproject(arc.midpoint[0], arc.midpoint[1]);
|
|
905
|
+
}
|
|
906
|
+
function derive$4(points) {
|
|
907
|
+
if (points.length < 2) return clonePositions(points);
|
|
908
|
+
const [p1, p2] = points;
|
|
909
|
+
if (points.length === 2) {
|
|
910
|
+
const dx = p2[0] - p1[0];
|
|
911
|
+
const dy = p2[1] - p1[1];
|
|
912
|
+
const p4 = [p2[0] + dx * 2.5, p2[1] + dy * 2.5];
|
|
913
|
+
return clonePositions([
|
|
914
|
+
p1,
|
|
915
|
+
p2,
|
|
916
|
+
deriveArcPoint(p1, p2, p4),
|
|
917
|
+
p4
|
|
918
|
+
]);
|
|
919
|
+
}
|
|
920
|
+
if (points.length === 3) {
|
|
921
|
+
const p4 = points[2];
|
|
922
|
+
return clonePositions([
|
|
923
|
+
p1,
|
|
924
|
+
p2,
|
|
925
|
+
deriveArcPoint(p1, p2, p4),
|
|
926
|
+
p4
|
|
927
|
+
]);
|
|
928
|
+
}
|
|
929
|
+
return clonePositions(points.slice(0, 4));
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Line27 anchor draw rule for the Seize mission task.
|
|
933
|
+
*
|
|
934
|
+
* PT. 1 is the circle center, PT. 2 fixes its radius and the curved arrow's
|
|
935
|
+
* start, derived PT. 3 lies on and selects the circular arc, and the user's
|
|
936
|
+
* third click becomes PT. 4 (the arrow tip). PT. 3 remains directly editable
|
|
937
|
+
* after commit. Dragging PT. 1 carries PT. 2 with it so the assigned-unit
|
|
938
|
+
* circle keeps its radius.
|
|
939
|
+
*/
|
|
940
|
+
const line27DrawRule = {
|
|
941
|
+
id: "line27:seize",
|
|
942
|
+
minimumUserPoints: 3,
|
|
943
|
+
minimumPreviewPoints: 2,
|
|
944
|
+
canonicalPointCount: 4,
|
|
878
945
|
derive: derive$4,
|
|
879
946
|
transform(event) {
|
|
880
|
-
|
|
947
|
+
const { previous, next, activePointIndex } = event;
|
|
948
|
+
if (activePointIndex === 0 && previous.length >= 4 && next.length >= 4) {
|
|
949
|
+
const dx = next[0][0] - previous[0][0];
|
|
950
|
+
const dy = next[0][1] - previous[0][1];
|
|
951
|
+
const oldRadiusPoint = previous[1];
|
|
952
|
+
const p1 = clonePosition(next[0]);
|
|
953
|
+
const p2 = [oldRadiusPoint[0] + dx, oldRadiusPoint[1] + dy];
|
|
954
|
+
const p4 = clonePosition(next[3]);
|
|
955
|
+
return [
|
|
956
|
+
p1,
|
|
957
|
+
p2,
|
|
958
|
+
deriveArcPoint(p1, p2, p4),
|
|
959
|
+
p4
|
|
960
|
+
];
|
|
961
|
+
}
|
|
962
|
+
if ((activePointIndex === 1 || activePointIndex === 3) && next.length >= 4) {
|
|
963
|
+
const p1 = clonePosition(next[0]);
|
|
964
|
+
const p2 = clonePosition(next[1]);
|
|
965
|
+
const p4 = clonePosition(next[3]);
|
|
966
|
+
return [
|
|
967
|
+
p1,
|
|
968
|
+
p2,
|
|
969
|
+
deriveArcPoint(p1, p2, p4),
|
|
970
|
+
p4
|
|
971
|
+
];
|
|
972
|
+
}
|
|
973
|
+
return clonePositions(next.slice(0, 4));
|
|
881
974
|
}
|
|
882
975
|
};
|
|
883
976
|
//#endregion
|
|
@@ -965,6 +1058,48 @@ const axis1DrawRule = {
|
|
|
965
1058
|
return packWithWidth(next, projected);
|
|
966
1059
|
}
|
|
967
1060
|
};
|
|
1061
|
+
/**
|
|
1062
|
+
* Recesses the CATK body from P1. The original P1 remains the tip of the fire
|
|
1063
|
+
* arrow, while P2 and the remaining Axis1 points continue to shape the body.
|
|
1064
|
+
*/
|
|
1065
|
+
function counterattackByFireBodyCoordinates(points) {
|
|
1066
|
+
const fireTipCoordinate = points[0];
|
|
1067
|
+
const neckCoordinate = points[1];
|
|
1068
|
+
if (!fireTipCoordinate || !neckCoordinate) return null;
|
|
1069
|
+
const fireTip = project(fireTipCoordinate[0], fireTipCoordinate[1]);
|
|
1070
|
+
const terminal = vecSub(fireTip, project(neckCoordinate[0], neckCoordinate[1]));
|
|
1071
|
+
if (vecMag(terminal) < 1e-6) return null;
|
|
1072
|
+
const bodyTip = vecAdd(fireTip, vecScale(terminal, -.3));
|
|
1073
|
+
return [unproject(bodyTip[0], bodyTip[1]), ...points.slice(1)];
|
|
1074
|
+
}
|
|
1075
|
+
function restoreFireTip(points, fireTip) {
|
|
1076
|
+
if (points.length > 0 && fireTip) points[0] = clonePosition(fireTip);
|
|
1077
|
+
return points;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Axis1 interaction for Counterattack by Fire, with its width frame anchored
|
|
1081
|
+
* to the recessed CATK body tip rather than P1, which belongs to the separate
|
|
1082
|
+
* fire arrow.
|
|
1083
|
+
*/
|
|
1084
|
+
const counterattackByFireDrawRule = {
|
|
1085
|
+
...axis1DrawRule,
|
|
1086
|
+
id: "axis1:counterattack-by-fire",
|
|
1087
|
+
derive(points) {
|
|
1088
|
+
const bodyPoints = counterattackByFireBodyCoordinates(points);
|
|
1089
|
+
if (!bodyPoints) return axis1DrawRule.derive(points);
|
|
1090
|
+
return restoreFireTip(axis1DrawRule.derive(bodyPoints), points[0]);
|
|
1091
|
+
},
|
|
1092
|
+
transform(event) {
|
|
1093
|
+
const previousBody = counterattackByFireBodyCoordinates(event.previous);
|
|
1094
|
+
const nextBody = counterattackByFireBodyCoordinates(event.next);
|
|
1095
|
+
if (!previousBody || !nextBody) return axis1DrawRule.transform(event);
|
|
1096
|
+
return restoreFireTip(axis1DrawRule.transform({
|
|
1097
|
+
...event,
|
|
1098
|
+
previous: previousBody,
|
|
1099
|
+
next: nextBody
|
|
1100
|
+
}), event.next[0]);
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
968
1103
|
//#endregion
|
|
969
1104
|
//#region src/draw-rules/area21.ts
|
|
970
1105
|
function derive$1(points) {
|
|
@@ -4779,28 +4914,29 @@ function generateAntitankDitchTriangles(projectedPoints, toothHeight, toothWidth
|
|
|
4779
4914
|
return triangles;
|
|
4780
4915
|
}
|
|
4781
4916
|
/**
|
|
4782
|
-
*
|
|
4783
|
-
*
|
|
4784
|
-
*
|
|
4785
|
-
* unchanged (no project/unproject round trip) when smoothing is off.
|
|
4917
|
+
* When `options.smooth` is set, curves the projected control points
|
|
4918
|
+
* (centripetal Catmull-Rom) before the teeth are traced — matching Fortified
|
|
4919
|
+
* Line's base-path smoothing.
|
|
4786
4920
|
*/
|
|
4787
|
-
function
|
|
4788
|
-
const projectedPoints = positions.map((position) => project(position[0], position[1]));
|
|
4921
|
+
function ditchPaths(projectedPoints, options) {
|
|
4789
4922
|
const smoothedPath = smoothLineVerts(projectedPoints, options, 16);
|
|
4790
4923
|
const toothHeight = calculateAntitankDitchHeight(options);
|
|
4791
4924
|
const toothWidthRatio = Math.max(EPSILON, options.toothWidthRatio ?? DEFAULT_TOOTH_WIDTH_RATIO$1);
|
|
4792
|
-
return
|
|
4925
|
+
return {
|
|
4926
|
+
smoothedPath,
|
|
4927
|
+
basePath: options.smooth && projectedPoints.length >= 3 ? evenlySpacePath(smoothedPath, toothWidthRatio * toothHeight) : smoothedPath
|
|
4928
|
+
};
|
|
4793
4929
|
}
|
|
4794
4930
|
function buildAntitankDitchTriangles(basePath, options) {
|
|
4795
4931
|
return generateAntitankDitchTriangles(basePath, calculateAntitankDitchHeight(options), options.toothWidthRatio ?? DEFAULT_TOOTH_WIDTH_RATIO$1);
|
|
4796
4932
|
}
|
|
4797
4933
|
function createAntitankDitchUnderConstruction(positions, options = {}, textAmplifiers = {}) {
|
|
4798
|
-
const basePath =
|
|
4934
|
+
const { smoothedPath, basePath } = ditchPaths(positions.map((position) => project(position[0], position[1])), options);
|
|
4799
4935
|
const triangles = buildAntitankDitchTriangles(basePath, options);
|
|
4800
4936
|
const baseLine = options.smooth ? basePath.map((point) => unproject(point[0], point[1])) : positions.map((position) => [position[0], position[1]]);
|
|
4801
4937
|
const triangleLines = triangles.map((triangle) => triangle.map((point) => unproject(point[0], point[1])));
|
|
4802
4938
|
const labels = [];
|
|
4803
|
-
pushHostileEndLabels(labels,
|
|
4939
|
+
pushHostileEndLabels(labels, smoothedPath, textAmplifiers.N, options);
|
|
4804
4940
|
return {
|
|
4805
4941
|
type: "FeatureCollection",
|
|
4806
4942
|
features: [{
|
|
@@ -4814,12 +4950,12 @@ function createAntitankDitchUnderConstruction(positions, options = {}, textAmpli
|
|
|
4814
4950
|
};
|
|
4815
4951
|
}
|
|
4816
4952
|
function createAntitankDitchCompleted(positions, options = {}, textAmplifiers = {}) {
|
|
4817
|
-
const basePath =
|
|
4953
|
+
const { smoothedPath, basePath } = ditchPaths(positions.map((position) => project(position[0], position[1])), options);
|
|
4818
4954
|
const polygons = buildAntitankDitchTriangles(basePath, options).map((triangle) => {
|
|
4819
4955
|
return [[...triangle, triangle[0]].map((point) => unproject(point[0], point[1]))];
|
|
4820
4956
|
});
|
|
4821
4957
|
const labels = [];
|
|
4822
|
-
pushHostileEndLabels(labels,
|
|
4958
|
+
pushHostileEndLabels(labels, smoothedPath, textAmplifiers.N, options);
|
|
4823
4959
|
return {
|
|
4824
4960
|
type: "FeatureCollection",
|
|
4825
4961
|
features: [{
|
|
@@ -4915,10 +5051,9 @@ function createAntitankWall(positions, options = {}, textAmplifiers = {}) {
|
|
|
4915
5051
|
const toothSpacingRatio = options.toothSpacingRatio ?? DEFAULT_TOOTH_SPACING_RATIO;
|
|
4916
5052
|
const projectedPoints = positions.map((position) => project(position[0], position[1]));
|
|
4917
5053
|
const smoothedPath = smoothLineVerts(projectedPoints, options, 16);
|
|
4918
|
-
const
|
|
4919
|
-
const wallLine = generateAntitankWallPoints(basePath, toothHeight, toothWidthRatio, toothSpacingRatio).map((point) => unproject(point[0], point[1]));
|
|
5054
|
+
const wallLine = generateAntitankWallPoints(options.smooth && projectedPoints.length >= 3 ? evenlySpacePath(smoothedPath, Math.max(EPSILON, toothWidthRatio) * toothHeight) : smoothedPath, toothHeight, toothWidthRatio, toothSpacingRatio).map((point) => unproject(point[0], point[1]));
|
|
4920
5055
|
const labels = [];
|
|
4921
|
-
pushHostileEndLabels(labels,
|
|
5056
|
+
pushHostileEndLabels(labels, smoothedPath, textAmplifiers.N, options);
|
|
4922
5057
|
return {
|
|
4923
5058
|
type: "FeatureCollection",
|
|
4924
5059
|
features: [{
|
|
@@ -6448,6 +6583,10 @@ const GENERIC_C2_LINE = defineControlMeasure({
|
|
|
6448
6583
|
});
|
|
6449
6584
|
//#endregion
|
|
6450
6585
|
//#region src/generators/cm34-mission-tasks/shared.ts
|
|
6586
|
+
/** Unprojects a point in Web Mercator meters back to a GeoJSON position. */
|
|
6587
|
+
function toPosition(point) {
|
|
6588
|
+
return unproject(point[0], point[1]);
|
|
6589
|
+
}
|
|
6451
6590
|
/**
|
|
6452
6591
|
* Builds an open chevron arrowhead (left → tip → right) as unprojected
|
|
6453
6592
|
* coordinates. The tip sits at `tip`, pointing along `dir`, with the base set
|
|
@@ -7966,17 +8105,28 @@ function createSupportingAttack(coordinates, options = {}) {
|
|
|
7966
8105
|
properties: {},
|
|
7967
8106
|
geometry: {
|
|
7968
8107
|
type: "LineString",
|
|
7969
|
-
coordinates:
|
|
7970
|
-
...geometry.shaftLeft,
|
|
7971
|
-
geometry.headRing[0],
|
|
7972
|
-
geometry.headRing[1],
|
|
7973
|
-
geometry.headRing[2],
|
|
7974
|
-
...[...geometry.shaftRight].reverse()
|
|
7975
|
-
].map((p) => unproject(p[0], p[1]))
|
|
8108
|
+
coordinates: supportingAttackOutline(geometry)
|
|
7976
8109
|
}
|
|
7977
8110
|
}]
|
|
7978
8111
|
};
|
|
7979
8112
|
}
|
|
8113
|
+
/**
|
|
8114
|
+
* Traces the single-LineString supporting-attack outline from already-computed
|
|
8115
|
+
* attack geometry, as unprojected coordinates. Exposed so measures that build
|
|
8116
|
+
* on the supporting-attack body (Counterattack by Fire) can reuse the trace
|
|
8117
|
+
* without running {@link processAttackGeometry} a second time.
|
|
8118
|
+
*
|
|
8119
|
+
* Path: [ShaftLeft...] -> OuterLeft -> Tip -> OuterRight -> [ShaftRight...(reversed)]
|
|
8120
|
+
*/
|
|
8121
|
+
function supportingAttackOutline(geometry) {
|
|
8122
|
+
return [
|
|
8123
|
+
...geometry.shaftLeft,
|
|
8124
|
+
geometry.headRing[0],
|
|
8125
|
+
geometry.headRing[1],
|
|
8126
|
+
geometry.headRing[2],
|
|
8127
|
+
...[...geometry.shaftRight].reverse()
|
|
8128
|
+
].map((p) => unproject(p[0], p[1]));
|
|
8129
|
+
}
|
|
7980
8130
|
const SUPPORTING_ATTACK = defineControlMeasure({
|
|
7981
8131
|
metadata: SUPPORTING_ATTACK_METADATA,
|
|
7982
8132
|
generator: createSupportingAttack,
|
|
@@ -8082,6 +8232,185 @@ const COUNTERATTACK = defineControlMeasure({
|
|
|
8082
8232
|
}
|
|
8083
8233
|
});
|
|
8084
8234
|
//#endregion
|
|
8235
|
+
//#region src/internal/arrowhead.ts
|
|
8236
|
+
/**
|
|
8237
|
+
* Builds a closed, filled arrowhead triangle ring in unprojected coordinates.
|
|
8238
|
+
*
|
|
8239
|
+
* `tip` is the point of the arrow, `dir` the unit direction the arrow points,
|
|
8240
|
+
* `length` the distance from tip back to the base, and `width` the base width.
|
|
8241
|
+
* The returned ring is `[left, tip, right, left]`.
|
|
8242
|
+
*/
|
|
8243
|
+
function createArrowHeadRing(tip, dir, length, width) {
|
|
8244
|
+
const perp = [-dir[1], dir[0]];
|
|
8245
|
+
const base = vecSub(tip, vecScale(dir, length));
|
|
8246
|
+
const left = vecAdd(base, vecScale(perp, width / 2));
|
|
8247
|
+
const right = vecSub(base, vecScale(perp, width / 2));
|
|
8248
|
+
return [
|
|
8249
|
+
unproject(left[0], left[1]),
|
|
8250
|
+
unproject(tip[0], tip[1]),
|
|
8251
|
+
unproject(right[0], right[1]),
|
|
8252
|
+
unproject(left[0], left[1])
|
|
8253
|
+
];
|
|
8254
|
+
}
|
|
8255
|
+
//#endregion
|
|
8256
|
+
//#region src/generators/cm34-mission-tasks/counterattack-by-fire.ts
|
|
8257
|
+
const DEFAULT_COUNTERATTACK_BY_FIRE_OPTIONS = { ...DEFAULT_COUNTERATTACK_OPTIONS };
|
|
8258
|
+
/** Length of the solid fire arrowhead relative to the recessed P1 extension. */
|
|
8259
|
+
const COUNTERATTACK_BY_FIRE_HEAD_RATIO = .18;
|
|
8260
|
+
const COUNTERATTACK_BY_FIRE_METADATA = {
|
|
8261
|
+
id: "counterattack-by-fire",
|
|
8262
|
+
name: "Counterattack by Fire",
|
|
8263
|
+
description: "An attack against an enemy attacking force using fires",
|
|
8264
|
+
entity: "Mission Tasks",
|
|
8265
|
+
entityType: "Counterattack by Fire",
|
|
8266
|
+
value: "340700",
|
|
8267
|
+
minCoordinates: 3,
|
|
8268
|
+
geometry: "line",
|
|
8269
|
+
geometryTypes: [
|
|
8270
|
+
"LineString",
|
|
8271
|
+
"MultiLineString",
|
|
8272
|
+
"Polygon",
|
|
8273
|
+
"Point"
|
|
8274
|
+
],
|
|
8275
|
+
paints: {
|
|
8276
|
+
stroke: true,
|
|
8277
|
+
fill: "fixed",
|
|
8278
|
+
text: true
|
|
8279
|
+
},
|
|
8280
|
+
drawRule: "Axis1",
|
|
8281
|
+
capturesLabelSize: true,
|
|
8282
|
+
params: [...ATTACK_SHAFT_PARAMS, {
|
|
8283
|
+
key: "labelPosition",
|
|
8284
|
+
label: "Label position",
|
|
8285
|
+
description: "Position of the CATK label along the shaft, from tail (0) to head (1)",
|
|
8286
|
+
type: "number",
|
|
8287
|
+
min: 0,
|
|
8288
|
+
max: 1,
|
|
8289
|
+
step: .01
|
|
8290
|
+
}],
|
|
8291
|
+
textAmplifiers: [{
|
|
8292
|
+
key: "T",
|
|
8293
|
+
label: "Unique designation",
|
|
8294
|
+
description: "Field T — the counterattack's unique designation.",
|
|
8295
|
+
placeholder: "1",
|
|
8296
|
+
maxLength: 21
|
|
8297
|
+
}]
|
|
8298
|
+
};
|
|
8299
|
+
/** Generates the Counterattack by Fire mission task (340700). */
|
|
8300
|
+
function createCounterattackByFire(coordinates, options = {}, textAmplifiers = {}) {
|
|
8301
|
+
const bodyCoordinates = counterattackByFireBodyCoordinates(coordinates);
|
|
8302
|
+
if (!bodyCoordinates) return {
|
|
8303
|
+
type: "FeatureCollection",
|
|
8304
|
+
features: []
|
|
8305
|
+
};
|
|
8306
|
+
const { geometry } = processAttackGeometry(bodyCoordinates, options);
|
|
8307
|
+
const fireTipCoordinate = coordinates[0];
|
|
8308
|
+
if (!geometry || !fireTipCoordinate) return {
|
|
8309
|
+
type: "FeatureCollection",
|
|
8310
|
+
features: []
|
|
8311
|
+
};
|
|
8312
|
+
const fireTip = project(fireTipCoordinate[0], fireTipCoordinate[1]);
|
|
8313
|
+
const bodyTip = geometry.ptTip;
|
|
8314
|
+
const fireExtension = vecSub(fireTip, bodyTip);
|
|
8315
|
+
const extensionLength = vecMag(fireExtension);
|
|
8316
|
+
if (extensionLength < 1e-6) return {
|
|
8317
|
+
type: "FeatureCollection",
|
|
8318
|
+
features: []
|
|
8319
|
+
};
|
|
8320
|
+
const along = vecNorm(fireExtension);
|
|
8321
|
+
const perpendicular = [-along[1], along[0]];
|
|
8322
|
+
const headHalfWidth = vecMag(vecSub(geometry.headRing[0], geometry.ptBase));
|
|
8323
|
+
const headLength = vecMag(vecSub(geometry.ptTip, geometry.ptBase));
|
|
8324
|
+
const bracketCenter = vecAdd(bodyTip, vecScale(fireExtension, .5));
|
|
8325
|
+
const bracketUpper = vecAdd(bracketCenter, vecScale(perpendicular, headHalfWidth));
|
|
8326
|
+
const bracketLower = vecAdd(bracketCenter, vecScale(perpendicular, -headHalfWidth));
|
|
8327
|
+
const flare = headHalfWidth * .55;
|
|
8328
|
+
const bracketOuterUpper = vecAdd(vecAdd(bracketUpper, vecScale(along, -headLength)), vecScale(perpendicular, flare));
|
|
8329
|
+
const bracketOuterLower = vecAdd(vecAdd(bracketLower, vecScale(along, -headLength)), vecScale(perpendicular, -flare));
|
|
8330
|
+
const fireHeadLength = extensionLength * COUNTERATTACK_BY_FIRE_HEAD_RATIO;
|
|
8331
|
+
const fireHeadBase = vecAdd(fireTip, vecScale(along, -fireHeadLength));
|
|
8332
|
+
const features = [
|
|
8333
|
+
{
|
|
8334
|
+
type: "Feature",
|
|
8335
|
+
properties: {
|
|
8336
|
+
part: "counterattack-body",
|
|
8337
|
+
style: { strokeDash: [...COUNTERATTACK_DASH] }
|
|
8338
|
+
},
|
|
8339
|
+
geometry: {
|
|
8340
|
+
type: "LineString",
|
|
8341
|
+
coordinates: supportingAttackOutline(geometry)
|
|
8342
|
+
}
|
|
8343
|
+
},
|
|
8344
|
+
{
|
|
8345
|
+
type: "Feature",
|
|
8346
|
+
properties: {
|
|
8347
|
+
part: "fire-attachment",
|
|
8348
|
+
style: { strokeDash: [...COUNTERATTACK_DASH] }
|
|
8349
|
+
},
|
|
8350
|
+
geometry: {
|
|
8351
|
+
type: "MultiLineString",
|
|
8352
|
+
coordinates: [[
|
|
8353
|
+
toPosition(bracketOuterUpper),
|
|
8354
|
+
toPosition(bracketUpper),
|
|
8355
|
+
toPosition(bracketLower),
|
|
8356
|
+
toPosition(bracketOuterLower)
|
|
8357
|
+
], [toPosition(bracketCenter), toPosition(fireHeadBase)]]
|
|
8358
|
+
}
|
|
8359
|
+
},
|
|
8360
|
+
{
|
|
8361
|
+
type: "Feature",
|
|
8362
|
+
properties: {
|
|
8363
|
+
part: "fire-arrowhead",
|
|
8364
|
+
fill: true,
|
|
8365
|
+
style: SOLID_ACCENT_FILL
|
|
8366
|
+
},
|
|
8367
|
+
geometry: {
|
|
8368
|
+
type: "Polygon",
|
|
8369
|
+
coordinates: [createArrowHeadRing(fireTip, along, fireHeadLength, fireHeadLength * 1.15)]
|
|
8370
|
+
}
|
|
8371
|
+
}
|
|
8372
|
+
];
|
|
8373
|
+
const { segments, totalLength } = polylineSegments(geometry.shaftCenterline);
|
|
8374
|
+
const frame = pointAlongPolyline(segments, totalLength, options.labelPosition ?? DEFAULT_COUNTERATTACK_BY_FIRE_OPTIONS.labelPosition);
|
|
8375
|
+
if (frame) {
|
|
8376
|
+
const designation = textAmplifiers.T?.trim();
|
|
8377
|
+
features.push({
|
|
8378
|
+
type: "Feature",
|
|
8379
|
+
properties: {
|
|
8380
|
+
part: "label",
|
|
8381
|
+
labelPlacement: false,
|
|
8382
|
+
amplifierField: "T",
|
|
8383
|
+
text: designation ? `CATK ${designation}` : "CATK",
|
|
8384
|
+
rotation: labelRotationAlong(frame.along),
|
|
8385
|
+
...labelSizeProps(options)
|
|
8386
|
+
},
|
|
8387
|
+
geometry: {
|
|
8388
|
+
type: "Point",
|
|
8389
|
+
coordinates: toPosition(frame.point)
|
|
8390
|
+
}
|
|
8391
|
+
});
|
|
8392
|
+
}
|
|
8393
|
+
return {
|
|
8394
|
+
type: "FeatureCollection",
|
|
8395
|
+
features
|
|
8396
|
+
};
|
|
8397
|
+
}
|
|
8398
|
+
const COUNTERATTACK_BY_FIRE = defineControlMeasure({
|
|
8399
|
+
metadata: COUNTERATTACK_BY_FIRE_METADATA,
|
|
8400
|
+
generator: createCounterattackByFire,
|
|
8401
|
+
defaultOptions: DEFAULT_COUNTERATTACK_BY_FIRE_OPTIONS,
|
|
8402
|
+
rule: counterattackByFireDrawRule,
|
|
8403
|
+
previewSample: {
|
|
8404
|
+
controlPoints: [
|
|
8405
|
+
[1.35, 0],
|
|
8406
|
+
[-.2, -.1],
|
|
8407
|
+
[-1, -.75],
|
|
8408
|
+
[.45, .7]
|
|
8409
|
+
],
|
|
8410
|
+
options: { labelSize: 120 }
|
|
8411
|
+
}
|
|
8412
|
+
});
|
|
8413
|
+
//#endregion
|
|
8085
8414
|
//#region src/internal/field-of-fire.ts
|
|
8086
8415
|
/**
|
|
8087
8416
|
* Builds the open (stroked) arrowhead at the outward tip of one arm. The two
|
|
@@ -8492,6 +8821,44 @@ const DEFAULT_DELAY_OPTIONS = {
|
|
|
8492
8821
|
labelGapRatio: .18,
|
|
8493
8822
|
arcSegments: 32
|
|
8494
8823
|
};
|
|
8824
|
+
const DELAY_PARAMS = [
|
|
8825
|
+
{
|
|
8826
|
+
key: "arrowheadLengthRatio",
|
|
8827
|
+
label: "Arrowhead length",
|
|
8828
|
+
description: "Arrowhead length as a ratio of the straight-line length",
|
|
8829
|
+
type: "number",
|
|
8830
|
+
min: .01,
|
|
8831
|
+
max: 1,
|
|
8832
|
+
step: .01
|
|
8833
|
+
},
|
|
8834
|
+
{
|
|
8835
|
+
key: "arrowheadWidthRatio",
|
|
8836
|
+
label: "Arrowhead width",
|
|
8837
|
+
description: "Arrowhead base width as a ratio of the straight-line length",
|
|
8838
|
+
type: "number",
|
|
8839
|
+
min: .01,
|
|
8840
|
+
max: 1,
|
|
8841
|
+
step: .01
|
|
8842
|
+
},
|
|
8843
|
+
{
|
|
8844
|
+
key: "labelGapRatio",
|
|
8845
|
+
label: "Label gap",
|
|
8846
|
+
description: "Size of the shaft gap holding the task label, as a ratio of the shaft length",
|
|
8847
|
+
type: "number",
|
|
8848
|
+
min: 0,
|
|
8849
|
+
max: 1,
|
|
8850
|
+
step: .01
|
|
8851
|
+
},
|
|
8852
|
+
{
|
|
8853
|
+
key: "arcSegments",
|
|
8854
|
+
label: "Arc segments",
|
|
8855
|
+
description: "Number of straight segments used to approximate the semicircular arc",
|
|
8856
|
+
type: "number",
|
|
8857
|
+
min: 4,
|
|
8858
|
+
max: 96,
|
|
8859
|
+
step: 1
|
|
8860
|
+
}
|
|
8861
|
+
];
|
|
8495
8862
|
const DELAY_METADATA = {
|
|
8496
8863
|
id: "delay",
|
|
8497
8864
|
name: "Delay",
|
|
@@ -8510,44 +8877,7 @@ const DELAY_METADATA = {
|
|
|
8510
8877
|
},
|
|
8511
8878
|
drawRule: "Line24",
|
|
8512
8879
|
capturesLabelSize: true,
|
|
8513
|
-
params:
|
|
8514
|
-
{
|
|
8515
|
-
key: "arrowheadLengthRatio",
|
|
8516
|
-
label: "Arrowhead length",
|
|
8517
|
-
description: "Arrowhead length as a ratio of the straight-line length",
|
|
8518
|
-
type: "number",
|
|
8519
|
-
min: .01,
|
|
8520
|
-
max: 1,
|
|
8521
|
-
step: .01
|
|
8522
|
-
},
|
|
8523
|
-
{
|
|
8524
|
-
key: "arrowheadWidthRatio",
|
|
8525
|
-
label: "Arrowhead width",
|
|
8526
|
-
description: "Arrowhead base width as a ratio of the straight-line length",
|
|
8527
|
-
type: "number",
|
|
8528
|
-
min: .01,
|
|
8529
|
-
max: 1,
|
|
8530
|
-
step: .01
|
|
8531
|
-
},
|
|
8532
|
-
{
|
|
8533
|
-
key: "labelGapRatio",
|
|
8534
|
-
label: "Label gap",
|
|
8535
|
-
description: "Size of the shaft gap holding the 'D' label, as a ratio of the shaft length",
|
|
8536
|
-
type: "number",
|
|
8537
|
-
min: 0,
|
|
8538
|
-
max: 1,
|
|
8539
|
-
step: .01
|
|
8540
|
-
},
|
|
8541
|
-
{
|
|
8542
|
-
key: "arcSegments",
|
|
8543
|
-
label: "Arc segments",
|
|
8544
|
-
description: "Number of straight segments used to approximate the semicircular arc",
|
|
8545
|
-
type: "number",
|
|
8546
|
-
min: 4,
|
|
8547
|
-
max: 96,
|
|
8548
|
-
step: 1
|
|
8549
|
-
}
|
|
8550
|
-
]
|
|
8880
|
+
params: DELAY_PARAMS
|
|
8551
8881
|
};
|
|
8552
8882
|
function createDelaySymbol(coordinates, options = {}) {
|
|
8553
8883
|
const [c1, c2, c3] = coordinates;
|
|
@@ -8608,6 +8938,26 @@ function createSemicircleArc(start, end, bulgeDir, requestedSegments) {
|
|
|
8608
8938
|
}
|
|
8609
8939
|
return points;
|
|
8610
8940
|
}
|
|
8941
|
+
/**
|
|
8942
|
+
* Line24 mission tasks that share Delay's geometry and differ only in the
|
|
8943
|
+
* glyph shown in the shaft gap and the `part` tag on the line feature.
|
|
8944
|
+
*/
|
|
8945
|
+
function createDelayVariantSymbol(coordinates, config, options = {}) {
|
|
8946
|
+
return {
|
|
8947
|
+
type: "FeatureCollection",
|
|
8948
|
+
features: createDelaySymbol(coordinates, options).features.map((feature) => ({
|
|
8949
|
+
...feature,
|
|
8950
|
+
properties: feature.geometry.type === "Point" ? {
|
|
8951
|
+
...feature.properties,
|
|
8952
|
+
part: "label",
|
|
8953
|
+
text: config.glyph
|
|
8954
|
+
} : {
|
|
8955
|
+
...feature.properties,
|
|
8956
|
+
part: config.part
|
|
8957
|
+
}
|
|
8958
|
+
}))
|
|
8959
|
+
};
|
|
8960
|
+
}
|
|
8611
8961
|
const DELAY = defineControlMeasure({
|
|
8612
8962
|
metadata: DELAY_METADATA,
|
|
8613
8963
|
generator: createDelaySymbol,
|
|
@@ -8615,6 +8965,204 @@ const DELAY = defineControlMeasure({
|
|
|
8615
8965
|
rule: line24DrawRule
|
|
8616
8966
|
});
|
|
8617
8967
|
//#endregion
|
|
8968
|
+
//#region src/generators/cm34-mission-tasks/disengage.ts
|
|
8969
|
+
const DEFAULT_DISENGAGE_OPTIONS = DEFAULT_DELAY_OPTIONS;
|
|
8970
|
+
const DISENGAGE_METADATA = {
|
|
8971
|
+
id: "disengage",
|
|
8972
|
+
name: "Disengage",
|
|
8973
|
+
description: "Breaks contact with the enemy to enable another mission or avoid decisive engagement.",
|
|
8974
|
+
entity: "Mission Tasks",
|
|
8975
|
+
entityType: "Disengage",
|
|
8976
|
+
value: "344400",
|
|
8977
|
+
minCoordinates: 3,
|
|
8978
|
+
maxCoordinates: 3,
|
|
8979
|
+
geometry: "line",
|
|
8980
|
+
geometryTypes: ["MultiLineString", "Point"],
|
|
8981
|
+
paints: {
|
|
8982
|
+
stroke: true,
|
|
8983
|
+
fill: "none",
|
|
8984
|
+
text: true
|
|
8985
|
+
},
|
|
8986
|
+
drawRule: "Line24",
|
|
8987
|
+
capturesLabelSize: true,
|
|
8988
|
+
params: DELAY_PARAMS
|
|
8989
|
+
};
|
|
8990
|
+
function createDisengageSymbol(coordinates, options = {}) {
|
|
8991
|
+
return createDelayVariantSymbol(coordinates, {
|
|
8992
|
+
glyph: "DIS",
|
|
8993
|
+
part: "disengage"
|
|
8994
|
+
}, options);
|
|
8995
|
+
}
|
|
8996
|
+
const DISENGAGE = defineControlMeasure({
|
|
8997
|
+
metadata: DISENGAGE_METADATA,
|
|
8998
|
+
generator: createDisengageSymbol,
|
|
8999
|
+
defaultOptions: DEFAULT_DISENGAGE_OPTIONS,
|
|
9000
|
+
rule: line24DrawRule,
|
|
9001
|
+
previewSample: {
|
|
9002
|
+
controlPoints: [
|
|
9003
|
+
[-1, -.4],
|
|
9004
|
+
[1, -.4],
|
|
9005
|
+
[0, .6]
|
|
9006
|
+
],
|
|
9007
|
+
options: { labelGapRatio: .36 }
|
|
9008
|
+
}
|
|
9009
|
+
});
|
|
9010
|
+
//#endregion
|
|
9011
|
+
//#region src/generators/cm34-mission-tasks/disrupt.ts
|
|
9012
|
+
const DEFAULT_DISRUPT_MISSION_TASK_OPTIONS = {
|
|
9013
|
+
middleArrowLengthRatio: .65,
|
|
9014
|
+
bottomArrowLengthRatio: .45,
|
|
9015
|
+
shaftLengthRatio: .25,
|
|
9016
|
+
arrowheadLengthRatio: .15,
|
|
9017
|
+
arrowheadWidthRatio: .2,
|
|
9018
|
+
labelGapRatio: .2
|
|
9019
|
+
};
|
|
9020
|
+
const DISRUPT_MISSION_TASK_METADATA = {
|
|
9021
|
+
id: "disrupt-mission-task",
|
|
9022
|
+
name: "Disrupt",
|
|
9023
|
+
description: "Upsets an enemy's formation or tempo and causes the enemy force to attack prematurely or in a piecemeal fashion.",
|
|
9024
|
+
entity: "Mission Tasks",
|
|
9025
|
+
entityType: "Disrupt",
|
|
9026
|
+
value: "341000",
|
|
9027
|
+
minCoordinates: 2,
|
|
9028
|
+
maxCoordinates: 3,
|
|
9029
|
+
geometry: "line",
|
|
9030
|
+
geometryTypes: ["MultiLineString", "Point"],
|
|
9031
|
+
paints: {
|
|
9032
|
+
stroke: true,
|
|
9033
|
+
fill: "none",
|
|
9034
|
+
text: true
|
|
9035
|
+
},
|
|
9036
|
+
drawRule: "Area12",
|
|
9037
|
+
capturesLabelSize: true,
|
|
9038
|
+
params: [
|
|
9039
|
+
{
|
|
9040
|
+
key: "middleArrowLengthRatio",
|
|
9041
|
+
label: "Middle arrow length",
|
|
9042
|
+
description: "Middle arrow length as a ratio of the PT.3-controlled top arrow",
|
|
9043
|
+
type: "number",
|
|
9044
|
+
min: .01,
|
|
9045
|
+
max: 1,
|
|
9046
|
+
step: .01
|
|
9047
|
+
},
|
|
9048
|
+
{
|
|
9049
|
+
key: "bottomArrowLengthRatio",
|
|
9050
|
+
label: "Bottom arrow length",
|
|
9051
|
+
description: "Bottom arrow length as a ratio of the PT.3-controlled top arrow",
|
|
9052
|
+
type: "number",
|
|
9053
|
+
min: .01,
|
|
9054
|
+
max: 1,
|
|
9055
|
+
step: .01
|
|
9056
|
+
},
|
|
9057
|
+
{
|
|
9058
|
+
key: "shaftLengthRatio",
|
|
9059
|
+
label: "Rear shaft length",
|
|
9060
|
+
description: "Middle shaft extension behind the spine, as a ratio of the top arrow",
|
|
9061
|
+
type: "number",
|
|
9062
|
+
min: 0,
|
|
9063
|
+
max: 1,
|
|
9064
|
+
step: .01
|
|
9065
|
+
},
|
|
9066
|
+
{
|
|
9067
|
+
key: "arrowheadLengthRatio",
|
|
9068
|
+
label: "Arrowhead length",
|
|
9069
|
+
description: "Arrowhead length as a ratio of the spine length",
|
|
9070
|
+
type: "number",
|
|
9071
|
+
min: .01,
|
|
9072
|
+
max: 1,
|
|
9073
|
+
step: .01
|
|
9074
|
+
},
|
|
9075
|
+
{
|
|
9076
|
+
key: "arrowheadWidthRatio",
|
|
9077
|
+
label: "Arrowhead width",
|
|
9078
|
+
description: "Arrowhead base width as a ratio of the spine length",
|
|
9079
|
+
type: "number",
|
|
9080
|
+
min: .01,
|
|
9081
|
+
max: 1,
|
|
9082
|
+
step: .01
|
|
9083
|
+
},
|
|
9084
|
+
{
|
|
9085
|
+
key: "labelGapRatio",
|
|
9086
|
+
label: "Label gap",
|
|
9087
|
+
description: "Size of the middle-shaft gap holding the 'D' label",
|
|
9088
|
+
type: "number",
|
|
9089
|
+
min: 0,
|
|
9090
|
+
max: 1,
|
|
9091
|
+
step: .01
|
|
9092
|
+
}
|
|
9093
|
+
]
|
|
9094
|
+
};
|
|
9095
|
+
/** Generates the DISRUPT mission task symbol (341000). */
|
|
9096
|
+
function createDisruptMissionTaskSymbol(coordinates, options = {}) {
|
|
9097
|
+
const p1 = project(coordinates[0][0], coordinates[0][1]);
|
|
9098
|
+
const p2 = project(coordinates[1][0], coordinates[1][1]);
|
|
9099
|
+
const frame = createProjectedBaselineFrame(p1, p2, {
|
|
9100
|
+
origin: "p1",
|
|
9101
|
+
normal: "left"
|
|
9102
|
+
});
|
|
9103
|
+
if (!frame) return {
|
|
9104
|
+
type: "FeatureCollection",
|
|
9105
|
+
features: []
|
|
9106
|
+
};
|
|
9107
|
+
const c3 = coordinates[2];
|
|
9108
|
+
const signedTopLength = c3 ? frame.signedNormalDistance(c3) : frame.length * 1.5;
|
|
9109
|
+
const topLength = Math.abs(signedTopLength);
|
|
9110
|
+
if (topLength < 1e-6) return {
|
|
9111
|
+
type: "FeatureCollection",
|
|
9112
|
+
features: []
|
|
9113
|
+
};
|
|
9114
|
+
const arrowDirection = vecScale(frame.normal, signedTopLength < 0 ? -1 : 1);
|
|
9115
|
+
const middle = vecScale(vecAdd(p1, p2), .5);
|
|
9116
|
+
/** Scales `base` by the named ratio option, falling back to its default and clamping negatives. */
|
|
9117
|
+
const scaled = (base, key) => base * Math.max(0, options[key] ?? DEFAULT_DISRUPT_MISSION_TASK_OPTIONS[key]);
|
|
9118
|
+
const middleLength = scaled(topLength, "middleArrowLengthRatio");
|
|
9119
|
+
const bottomLength = scaled(topLength, "bottomArrowLengthRatio");
|
|
9120
|
+
const rearLength = scaled(topLength, "shaftLengthRatio");
|
|
9121
|
+
const headLength = scaled(frame.length, "arrowheadLengthRatio");
|
|
9122
|
+
const headWidth = scaled(frame.length, "arrowheadWidthRatio");
|
|
9123
|
+
const topTip = vecAdd(p1, vecScale(arrowDirection, topLength));
|
|
9124
|
+
const middleTip = vecAdd(middle, vecScale(arrowDirection, middleLength));
|
|
9125
|
+
const bottomTip = vecAdd(p2, vecScale(arrowDirection, bottomLength));
|
|
9126
|
+
const rearEnd = vecAdd(middle, vecScale(arrowDirection, -rearLength));
|
|
9127
|
+
const labelPoint = vecScale(vecAdd(middle, middleTip), .5);
|
|
9128
|
+
const labelGapRatio = clamp01(options.labelGapRatio ?? DEFAULT_DISRUPT_MISSION_TASK_OPTIONS.labelGapRatio);
|
|
9129
|
+
const gapHalf = Math.min(topLength * labelGapRatio / 2, middleLength / 2);
|
|
9130
|
+
const gapStart = vecAdd(labelPoint, vecScale(arrowDirection, -gapHalf));
|
|
9131
|
+
const gapEnd = vecAdd(labelPoint, vecScale(arrowDirection, gapHalf));
|
|
9132
|
+
const p1LonLat = unproject(p1[0], p1[1]);
|
|
9133
|
+
const p2LonLat = unproject(p2[0], p2[1]);
|
|
9134
|
+
return {
|
|
9135
|
+
type: "FeatureCollection",
|
|
9136
|
+
features: [{
|
|
9137
|
+
type: "Feature",
|
|
9138
|
+
properties: { part: "disrupt" },
|
|
9139
|
+
geometry: {
|
|
9140
|
+
type: "MultiLineString",
|
|
9141
|
+
coordinates: [
|
|
9142
|
+
[p1LonLat, p2LonLat],
|
|
9143
|
+
[[...p1LonLat], unproject(topTip[0], topTip[1])],
|
|
9144
|
+
createArrowHead$1(topTip, arrowDirection, headLength, headWidth),
|
|
9145
|
+
[unproject(rearEnd[0], rearEnd[1]), unproject(gapStart[0], gapStart[1])],
|
|
9146
|
+
[unproject(gapEnd[0], gapEnd[1]), unproject(middleTip[0], middleTip[1])],
|
|
9147
|
+
createArrowHead$1(middleTip, arrowDirection, headLength, headWidth),
|
|
9148
|
+
[[...p2LonLat], unproject(bottomTip[0], bottomTip[1])],
|
|
9149
|
+
createArrowHead$1(bottomTip, arrowDirection, headLength, headWidth)
|
|
9150
|
+
]
|
|
9151
|
+
}
|
|
9152
|
+
}, createLabelFeature(labelPoint, "D", labelRotationAlong(arrowDirection), {
|
|
9153
|
+
labelSize: options.labelSize,
|
|
9154
|
+
labelSizePixels: options.labelSizePixels
|
|
9155
|
+
})]
|
|
9156
|
+
};
|
|
9157
|
+
}
|
|
9158
|
+
const DISRUPT_MISSION_TASK = defineControlMeasure({
|
|
9159
|
+
metadata: DISRUPT_MISSION_TASK_METADATA,
|
|
9160
|
+
generator: createDisruptMissionTaskSymbol,
|
|
9161
|
+
defaultOptions: DEFAULT_DISRUPT_MISSION_TASK_OPTIONS,
|
|
9162
|
+
rule: disruptDrawRule,
|
|
9163
|
+
previewSample: { controlPoints: [[0, .4], [0, -.4]] }
|
|
9164
|
+
});
|
|
9165
|
+
//#endregion
|
|
8618
9166
|
//#region src/generators/cm34-mission-tasks/guard.ts
|
|
8619
9167
|
const DEFAULT_GUARD_OPTIONS = DEFAULT_SECURITY_TASK_OPTIONS;
|
|
8620
9168
|
const GUARD_METADATA = {
|
|
@@ -8660,27 +9208,6 @@ const GUARD = defineControlMeasure({
|
|
|
8660
9208
|
}
|
|
8661
9209
|
});
|
|
8662
9210
|
//#endregion
|
|
8663
|
-
//#region src/internal/arrowhead.ts
|
|
8664
|
-
/**
|
|
8665
|
-
* Builds a closed, filled arrowhead triangle ring in unprojected coordinates.
|
|
8666
|
-
*
|
|
8667
|
-
* `tip` is the point of the arrow, `dir` the unit direction the arrow points,
|
|
8668
|
-
* `length` the distance from tip back to the base, and `width` the base width.
|
|
8669
|
-
* The returned ring is `[left, tip, right, left]`.
|
|
8670
|
-
*/
|
|
8671
|
-
function createArrowHeadRing(tip, dir, length, width) {
|
|
8672
|
-
const perp = [-dir[1], dir[0]];
|
|
8673
|
-
const base = vecSub(tip, vecScale(dir, length));
|
|
8674
|
-
const left = vecAdd(base, vecScale(perp, width / 2));
|
|
8675
|
-
const right = vecSub(base, vecScale(perp, width / 2));
|
|
8676
|
-
return [
|
|
8677
|
-
unproject(left[0], left[1]),
|
|
8678
|
-
unproject(tip[0], tip[1]),
|
|
8679
|
-
unproject(right[0], right[1]),
|
|
8680
|
-
unproject(left[0], left[1])
|
|
8681
|
-
];
|
|
8682
|
-
}
|
|
8683
|
-
//#endregion
|
|
8684
9211
|
//#region src/generators/cm27-protection-areas/disrupt.ts
|
|
8685
9212
|
const DEFAULT_DISRUPT_OPTIONS = {
|
|
8686
9213
|
middleArrowLengthRatio: .65,
|
|
@@ -9467,6 +9994,175 @@ const FIX = defineControlMeasure({
|
|
|
9467
9994
|
previewSample: { controlPoints: [[1, 0], [-1, 0]] }
|
|
9468
9995
|
});
|
|
9469
9996
|
//#endregion
|
|
9997
|
+
//#region src/generators/cm34-mission-tasks/fix.ts
|
|
9998
|
+
const DEFAULT_FIX_MISSION_TASK_OPTIONS = {
|
|
9999
|
+
arrowheadLengthRatio: .11,
|
|
10000
|
+
arrowheadWidthRatio: .18,
|
|
10001
|
+
zigzagStartRatio: .18,
|
|
10002
|
+
zigzagEndRatio: .8,
|
|
10003
|
+
zigzagPeakCount: 5,
|
|
10004
|
+
labelPositionRatio: .09,
|
|
10005
|
+
labelGapRatio: .1
|
|
10006
|
+
};
|
|
10007
|
+
const FIX_MISSION_TASK_METADATA = {
|
|
10008
|
+
id: "fix-mission-task",
|
|
10009
|
+
name: "Fix",
|
|
10010
|
+
description: "Prevents an enemy force from moving from a specific location for a specified period of time.",
|
|
10011
|
+
entity: "Mission Tasks",
|
|
10012
|
+
entityType: "Fix",
|
|
10013
|
+
value: "341100",
|
|
10014
|
+
minCoordinates: 2,
|
|
10015
|
+
maxCoordinates: 2,
|
|
10016
|
+
geometry: "line",
|
|
10017
|
+
geometryTypes: ["MultiLineString", "Point"],
|
|
10018
|
+
paints: {
|
|
10019
|
+
stroke: true,
|
|
10020
|
+
fill: "none",
|
|
10021
|
+
text: true
|
|
10022
|
+
},
|
|
10023
|
+
drawRule: "Line9",
|
|
10024
|
+
capturesLabelSize: true,
|
|
10025
|
+
params: [
|
|
10026
|
+
{
|
|
10027
|
+
key: "arrowheadLengthRatio",
|
|
10028
|
+
label: "Arrowhead length",
|
|
10029
|
+
description: "Arrowhead length as a ratio of the axis length",
|
|
10030
|
+
type: "number",
|
|
10031
|
+
min: .01,
|
|
10032
|
+
max: .5,
|
|
10033
|
+
step: .01
|
|
10034
|
+
},
|
|
10035
|
+
{
|
|
10036
|
+
key: "arrowheadWidthRatio",
|
|
10037
|
+
label: "Arrowhead width",
|
|
10038
|
+
description: "Arrowhead base width as a ratio of the axis length",
|
|
10039
|
+
type: "number",
|
|
10040
|
+
min: .01,
|
|
10041
|
+
max: .5,
|
|
10042
|
+
step: .01
|
|
10043
|
+
},
|
|
10044
|
+
{
|
|
10045
|
+
key: "zigzagStartRatio",
|
|
10046
|
+
label: "Zigzag start",
|
|
10047
|
+
description: "Position where the zigzag begins, measured from PT2",
|
|
10048
|
+
type: "number",
|
|
10049
|
+
min: 0,
|
|
10050
|
+
max: 1,
|
|
10051
|
+
step: .01
|
|
10052
|
+
},
|
|
10053
|
+
{
|
|
10054
|
+
key: "zigzagEndRatio",
|
|
10055
|
+
label: "Zigzag end",
|
|
10056
|
+
description: "Position where the zigzag ends, measured from PT2",
|
|
10057
|
+
type: "number",
|
|
10058
|
+
min: 0,
|
|
10059
|
+
max: 1,
|
|
10060
|
+
step: .01
|
|
10061
|
+
},
|
|
10062
|
+
{
|
|
10063
|
+
key: "zigzagPeakCount",
|
|
10064
|
+
label: "Zigzag peaks",
|
|
10065
|
+
description: "Number of upward peaks in the zigzag body",
|
|
10066
|
+
type: "number",
|
|
10067
|
+
min: 1,
|
|
10068
|
+
max: 12,
|
|
10069
|
+
step: 1
|
|
10070
|
+
},
|
|
10071
|
+
{
|
|
10072
|
+
key: "labelPositionRatio",
|
|
10073
|
+
label: "Label position",
|
|
10074
|
+
description: "Position of the F label along the axis, measured from PT2",
|
|
10075
|
+
type: "number",
|
|
10076
|
+
min: 0,
|
|
10077
|
+
max: 1,
|
|
10078
|
+
step: .01
|
|
10079
|
+
},
|
|
10080
|
+
{
|
|
10081
|
+
key: "labelGapRatio",
|
|
10082
|
+
label: "Label gap",
|
|
10083
|
+
description: "Size of the shaft gap holding the F label",
|
|
10084
|
+
type: "number",
|
|
10085
|
+
min: 0,
|
|
10086
|
+
max: 1,
|
|
10087
|
+
step: .01
|
|
10088
|
+
}
|
|
10089
|
+
]
|
|
10090
|
+
};
|
|
10091
|
+
/** Generates the Fix mission task symbol (341100). */
|
|
10092
|
+
function createFixMissionTaskSymbol(coordinates, options = {}) {
|
|
10093
|
+
const c1 = coordinates[0];
|
|
10094
|
+
const c2 = coordinates[1];
|
|
10095
|
+
const tip = project(c1[0], c1[1]);
|
|
10096
|
+
const tail = project(c2[0], c2[1]);
|
|
10097
|
+
const axis = vecSub(tip, tail);
|
|
10098
|
+
const axisLength = vecMag(axis);
|
|
10099
|
+
if (axisLength < 1e-6) return {
|
|
10100
|
+
type: "FeatureCollection",
|
|
10101
|
+
features: []
|
|
10102
|
+
};
|
|
10103
|
+
const along = vecNorm(axis);
|
|
10104
|
+
const perpendicular = [-along[1], along[0]];
|
|
10105
|
+
const arrowheadLength = axisLength * Math.max(0, options.arrowheadLengthRatio ?? DEFAULT_FIX_MISSION_TASK_OPTIONS.arrowheadLengthRatio);
|
|
10106
|
+
const arrowheadWidth = axisLength * Math.max(0, options.arrowheadWidthRatio ?? DEFAULT_FIX_MISSION_TASK_OPTIONS.arrowheadWidthRatio);
|
|
10107
|
+
const requestedStart = clamp01(options.zigzagStartRatio ?? DEFAULT_FIX_MISSION_TASK_OPTIONS.zigzagStartRatio);
|
|
10108
|
+
const requestedEnd = clamp01(options.zigzagEndRatio ?? DEFAULT_FIX_MISSION_TASK_OPTIONS.zigzagEndRatio);
|
|
10109
|
+
const zigzagStart = Math.min(requestedStart, requestedEnd);
|
|
10110
|
+
const zigzagEnd = Math.max(requestedStart, requestedEnd);
|
|
10111
|
+
const extremumCount = Math.round(clamp(options.zigzagPeakCount ?? DEFAULT_FIX_MISSION_TASK_OPTIONS.zigzagPeakCount, 1, 12)) * 2 + 1;
|
|
10112
|
+
const amplitude = arrowheadWidth / 2;
|
|
10113
|
+
const zigzag = [];
|
|
10114
|
+
for (let index = 0; index < extremumCount; index += 1) {
|
|
10115
|
+
const progress = extremumCount === 1 ? 0 : index / (extremumCount - 1);
|
|
10116
|
+
const distance = axisLength * (zigzagStart + (zigzagEnd - zigzagStart) * progress);
|
|
10117
|
+
const side = index % 2 === 0 ? -1 : 1;
|
|
10118
|
+
zigzag.push(vecAdd(vecAdd(tail, vecScale(along, distance)), vecScale(perpendicular, amplitude * side)));
|
|
10119
|
+
}
|
|
10120
|
+
const toothStep = extremumCount > 1 ? (zigzagEnd - zigzagStart) / (extremumCount - 1) : 0;
|
|
10121
|
+
const entryRatio = Math.max(0, zigzagStart - toothStep / 2);
|
|
10122
|
+
const exitRatio = Math.min(1, zigzagEnd + toothStep / 2);
|
|
10123
|
+
const zigzagEntry = vecAdd(tail, vecScale(along, axisLength * entryRatio));
|
|
10124
|
+
const zigzagExit = vecAdd(tail, vecScale(along, axisLength * exitRatio));
|
|
10125
|
+
const labelPosition = clamp01(options.labelPositionRatio ?? DEFAULT_FIX_MISSION_TASK_OPTIONS.labelPositionRatio);
|
|
10126
|
+
const labelGap = axisLength * clamp01(options.labelGapRatio ?? DEFAULT_FIX_MISSION_TASK_OPTIONS.labelGapRatio);
|
|
10127
|
+
const labelDistance = axisLength * labelPosition;
|
|
10128
|
+
const labelCenter = vecAdd(tail, vecScale(along, labelDistance));
|
|
10129
|
+
const gapStartDistance = Math.max(0, labelDistance - labelGap / 2);
|
|
10130
|
+
const gapEndDistance = Math.min(axisLength, labelDistance + labelGap / 2);
|
|
10131
|
+
const gapStart = vecAdd(tail, vecScale(along, gapStartDistance));
|
|
10132
|
+
const gapEnd = vecAdd(tail, vecScale(along, gapEndDistance));
|
|
10133
|
+
return {
|
|
10134
|
+
type: "FeatureCollection",
|
|
10135
|
+
features: [{
|
|
10136
|
+
type: "Feature",
|
|
10137
|
+
properties: { part: "fix-mission-task" },
|
|
10138
|
+
geometry: {
|
|
10139
|
+
type: "MultiLineString",
|
|
10140
|
+
coordinates: [
|
|
10141
|
+
[toPosition(tail), toPosition(gapStart)],
|
|
10142
|
+
[
|
|
10143
|
+
toPosition(gapEnd),
|
|
10144
|
+
toPosition(zigzagEntry),
|
|
10145
|
+
...zigzag.map(toPosition),
|
|
10146
|
+
toPosition(zigzagExit),
|
|
10147
|
+
toPosition(tip)
|
|
10148
|
+
],
|
|
10149
|
+
createArrowHead$1(tip, along, arrowheadLength, arrowheadWidth)
|
|
10150
|
+
]
|
|
10151
|
+
}
|
|
10152
|
+
}, createLabelFeature(labelCenter, "F", labelRotationAlong(along), options)]
|
|
10153
|
+
};
|
|
10154
|
+
}
|
|
10155
|
+
const FIX_MISSION_TASK = defineControlMeasure({
|
|
10156
|
+
metadata: FIX_MISSION_TASK_METADATA,
|
|
10157
|
+
generator: createFixMissionTaskSymbol,
|
|
10158
|
+
defaultOptions: DEFAULT_FIX_MISSION_TASK_OPTIONS,
|
|
10159
|
+
rule: line9DrawRule,
|
|
10160
|
+
previewSample: {
|
|
10161
|
+
controlPoints: [[1, 0], [-1, 0]],
|
|
10162
|
+
options: { labelSize: 110 }
|
|
10163
|
+
}
|
|
10164
|
+
});
|
|
10165
|
+
//#endregion
|
|
9470
10166
|
//#region src/generators/cm14-maneuver-lines/flot.ts
|
|
9471
10167
|
const DEFAULT_RADIUS = 50;
|
|
9472
10168
|
const DEFAULT_RADIUS_PIXELS = 10;
|
|
@@ -9653,7 +10349,7 @@ function createFLOT(positions, options = {}, textAmplifiers = {}) {
|
|
|
9653
10349
|
}
|
|
9654
10350
|
};
|
|
9655
10351
|
const labels = [];
|
|
9656
|
-
pushHostileEndLabels(labels,
|
|
10352
|
+
pushHostileEndLabels(labels, smoothedPath, textAmplifiers.N, options);
|
|
9657
10353
|
return {
|
|
9658
10354
|
type: "FeatureCollection",
|
|
9659
10355
|
features: [feature, ...labels]
|
|
@@ -10332,17 +11028,17 @@ function createFortifiedLine(positions, options = {}, textAmplifiers = {}) {
|
|
|
10332
11028
|
const { smooth = DEFAULT_FORTIFIED_LINE_OPTIONS.smooth, smoothResolution = DEFAULT_FORTIFIED_LINE_OPTIONS.smoothResolution } = options;
|
|
10333
11029
|
const safeSize = calculateEffectiveSize(options);
|
|
10334
11030
|
const projectedPoints = positions.map((p) => project(p[0], p[1]));
|
|
10335
|
-
const
|
|
11031
|
+
const smoothedPath = smooth ? catmullRom(projectedPoints, normalizeSmoothResolution$2(smoothResolution, DEFAULT_SMOOTH_RESOLUTION$1)) : projectedPoints;
|
|
10336
11032
|
const feature = {
|
|
10337
11033
|
type: "Feature",
|
|
10338
11034
|
properties: {},
|
|
10339
11035
|
geometry: {
|
|
10340
11036
|
type: "LineString",
|
|
10341
|
-
coordinates:
|
|
11037
|
+
coordinates: generateFortifiedPoints(smooth ? evenlySpacePath(smoothedPath, 2 * safeSize) : smoothedPath, safeSize).map((p) => unproject(p[0], p[1]))
|
|
10342
11038
|
}
|
|
10343
11039
|
};
|
|
10344
11040
|
const labels = [];
|
|
10345
|
-
pushHostileEndLabels(labels,
|
|
11041
|
+
pushHostileEndLabels(labels, smoothedPath, textAmplifiers.N, options);
|
|
10346
11042
|
return {
|
|
10347
11043
|
type: "FeatureCollection",
|
|
10348
11044
|
features: [feature, ...labels]
|
|
@@ -10784,6 +11480,200 @@ const ISOLATE = defineControlMeasure({
|
|
|
10784
11480
|
rule: isolateDrawRule
|
|
10785
11481
|
});
|
|
10786
11482
|
//#endregion
|
|
11483
|
+
//#region src/generators/cm34-mission-tasks/penetrate.ts
|
|
11484
|
+
const DEFAULT_PENETRATE_OPTIONS = {
|
|
11485
|
+
arrowheadLengthRatio: .15,
|
|
11486
|
+
arrowheadWidthRatio: .2,
|
|
11487
|
+
labelGapRatio: .2
|
|
11488
|
+
};
|
|
11489
|
+
const PENETRATE_METADATA = {
|
|
11490
|
+
id: "penetrate",
|
|
11491
|
+
name: "Penetrate",
|
|
11492
|
+
description: "Breaks through an enemy defense and disrupts the continuity of the defensive system.",
|
|
11493
|
+
entity: "Mission Tasks",
|
|
11494
|
+
entityType: "Penetrate",
|
|
11495
|
+
value: "341800",
|
|
11496
|
+
minCoordinates: 3,
|
|
11497
|
+
maxCoordinates: 3,
|
|
11498
|
+
geometry: "line",
|
|
11499
|
+
geometryTypes: ["MultiLineString", "Point"],
|
|
11500
|
+
paints: {
|
|
11501
|
+
stroke: true,
|
|
11502
|
+
fill: "none",
|
|
11503
|
+
text: true
|
|
11504
|
+
},
|
|
11505
|
+
drawRule: "Area17",
|
|
11506
|
+
capturesLabelSize: true,
|
|
11507
|
+
params: [
|
|
11508
|
+
{
|
|
11509
|
+
key: "arrowheadLengthRatio",
|
|
11510
|
+
label: "Arrowhead length",
|
|
11511
|
+
description: "Arrowhead length as a ratio of the barrier length",
|
|
11512
|
+
type: "number",
|
|
11513
|
+
min: .01,
|
|
11514
|
+
max: 1,
|
|
11515
|
+
step: .01
|
|
11516
|
+
},
|
|
11517
|
+
{
|
|
11518
|
+
key: "arrowheadWidthRatio",
|
|
11519
|
+
label: "Arrowhead width",
|
|
11520
|
+
description: "Arrowhead base width as a ratio of the barrier length",
|
|
11521
|
+
type: "number",
|
|
11522
|
+
min: .01,
|
|
11523
|
+
max: 1,
|
|
11524
|
+
step: .01
|
|
11525
|
+
},
|
|
11526
|
+
{
|
|
11527
|
+
key: "labelGapRatio",
|
|
11528
|
+
label: "Label gap",
|
|
11529
|
+
description: "Size of the shaft gap holding the 'P' label",
|
|
11530
|
+
type: "number",
|
|
11531
|
+
min: 0,
|
|
11532
|
+
max: 1,
|
|
11533
|
+
step: .01
|
|
11534
|
+
}
|
|
11535
|
+
]
|
|
11536
|
+
};
|
|
11537
|
+
/** Generates the PENETRATE mission task symbol (341800). */
|
|
11538
|
+
function createPenetrateSymbol(coordinates, options = {}) {
|
|
11539
|
+
const frame = createProjectedBaselineFrame(project(coordinates[0][0], coordinates[0][1]), project(coordinates[1][0], coordinates[1][1]), {
|
|
11540
|
+
origin: "midpoint",
|
|
11541
|
+
normal: "right"
|
|
11542
|
+
});
|
|
11543
|
+
if (!frame) return {
|
|
11544
|
+
type: "FeatureCollection",
|
|
11545
|
+
features: []
|
|
11546
|
+
};
|
|
11547
|
+
const c3 = coordinates[2];
|
|
11548
|
+
const signedTailDistance = c3 ? frame.signedNormalDistance(c3) : frame.length * 1.5;
|
|
11549
|
+
const shaftLength = Math.abs(signedTailDistance);
|
|
11550
|
+
if (shaftLength < 1e-6) return {
|
|
11551
|
+
type: "FeatureCollection",
|
|
11552
|
+
features: []
|
|
11553
|
+
};
|
|
11554
|
+
const tip = frame.origin;
|
|
11555
|
+
const tail = vecAdd(frame.origin, vecScale(frame.normal, signedTailDistance));
|
|
11556
|
+
const arrowDirection = vecScale(vecSub(tip, tail), 1 / shaftLength);
|
|
11557
|
+
const labelPoint = vecScale(vecAdd(tail, tip), .5);
|
|
11558
|
+
const gapHalf = shaftLength * Math.min(1, Math.max(0, options.labelGapRatio ?? DEFAULT_PENETRATE_OPTIONS.labelGapRatio)) / 2;
|
|
11559
|
+
const gapStart = vecAdd(labelPoint, vecScale(arrowDirection, -gapHalf));
|
|
11560
|
+
const gapEnd = vecAdd(labelPoint, vecScale(arrowDirection, gapHalf));
|
|
11561
|
+
const headLength = frame.length * Math.max(0, options.arrowheadLengthRatio ?? DEFAULT_PENETRATE_OPTIONS.arrowheadLengthRatio);
|
|
11562
|
+
const headWidth = frame.length * Math.max(0, options.arrowheadWidthRatio ?? DEFAULT_PENETRATE_OPTIONS.arrowheadWidthRatio);
|
|
11563
|
+
const lineFeature = {
|
|
11564
|
+
type: "Feature",
|
|
11565
|
+
properties: { part: "penetrate" },
|
|
11566
|
+
geometry: {
|
|
11567
|
+
type: "MultiLineString",
|
|
11568
|
+
coordinates: [
|
|
11569
|
+
[coordinates[0], coordinates[1]],
|
|
11570
|
+
[unproject(tail[0], tail[1]), unproject(gapStart[0], gapStart[1])],
|
|
11571
|
+
[unproject(gapEnd[0], gapEnd[1]), unproject(tip[0], tip[1])],
|
|
11572
|
+
createArrowHead$1(tip, arrowDirection, headLength, headWidth)
|
|
11573
|
+
]
|
|
11574
|
+
}
|
|
11575
|
+
};
|
|
11576
|
+
const shaftRotation = Math.atan2(arrowDirection[1], arrowDirection[0]);
|
|
11577
|
+
const renderedLabelRotation = Math.PI - shaftRotation;
|
|
11578
|
+
return {
|
|
11579
|
+
type: "FeatureCollection",
|
|
11580
|
+
features: [lineFeature, createLabelFeature(labelPoint, "P", normalizeRadians(Math.PI - keepTextLeftToRight(renderedLabelRotation)), {
|
|
11581
|
+
labelSize: options.labelSize,
|
|
11582
|
+
labelSizePixels: options.labelSizePixels
|
|
11583
|
+
})]
|
|
11584
|
+
};
|
|
11585
|
+
}
|
|
11586
|
+
const PENETRATE = defineControlMeasure({
|
|
11587
|
+
metadata: PENETRATE_METADATA,
|
|
11588
|
+
generator: createPenetrateSymbol,
|
|
11589
|
+
defaultOptions: DEFAULT_PENETRATE_OPTIONS,
|
|
11590
|
+
rule: penetrateDrawRule,
|
|
11591
|
+
previewSample: { controlPoints: [
|
|
11592
|
+
[0, .4],
|
|
11593
|
+
[0, -.4],
|
|
11594
|
+
[-1.2, 0]
|
|
11595
|
+
] }
|
|
11596
|
+
});
|
|
11597
|
+
//#endregion
|
|
11598
|
+
//#region src/generators/cm34-mission-tasks/rearward-passage-of-lines.ts
|
|
11599
|
+
const DEFAULT_REARWARD_PASSAGE_OF_LINES_OPTIONS = DEFAULT_DELAY_OPTIONS;
|
|
11600
|
+
const REARWARD_PASSAGE_OF_LINES_METADATA = {
|
|
11601
|
+
id: "rearward-passage-of-lines",
|
|
11602
|
+
name: "Rearward Passage of Lines",
|
|
11603
|
+
description: "Passes one force through another force's positions toward the rear.",
|
|
11604
|
+
entity: "Mission Tasks",
|
|
11605
|
+
entityType: "Rearward Passage of Lines",
|
|
11606
|
+
value: "344200",
|
|
11607
|
+
minCoordinates: 3,
|
|
11608
|
+
maxCoordinates: 3,
|
|
11609
|
+
geometry: "line",
|
|
11610
|
+
geometryTypes: ["MultiLineString", "Point"],
|
|
11611
|
+
paints: {
|
|
11612
|
+
stroke: true,
|
|
11613
|
+
fill: "none",
|
|
11614
|
+
text: true
|
|
11615
|
+
},
|
|
11616
|
+
drawRule: "Line24",
|
|
11617
|
+
capturesLabelSize: true,
|
|
11618
|
+
params: DELAY_PARAMS
|
|
11619
|
+
};
|
|
11620
|
+
function createRearwardPassageOfLinesSymbol(coordinates, options = {}) {
|
|
11621
|
+
return createDelayVariantSymbol(coordinates, {
|
|
11622
|
+
glyph: "P(R)",
|
|
11623
|
+
part: "rearward-passage-of-lines"
|
|
11624
|
+
}, options);
|
|
11625
|
+
}
|
|
11626
|
+
const REARWARD_PASSAGE_OF_LINES = defineControlMeasure({
|
|
11627
|
+
metadata: REARWARD_PASSAGE_OF_LINES_METADATA,
|
|
11628
|
+
generator: createRearwardPassageOfLinesSymbol,
|
|
11629
|
+
defaultOptions: DEFAULT_REARWARD_PASSAGE_OF_LINES_OPTIONS,
|
|
11630
|
+
rule: line24DrawRule,
|
|
11631
|
+
previewSample: {
|
|
11632
|
+
controlPoints: [
|
|
11633
|
+
[-1, -.4],
|
|
11634
|
+
[1, -.4],
|
|
11635
|
+
[0, .6]
|
|
11636
|
+
],
|
|
11637
|
+
options: { labelGapRatio: .42 }
|
|
11638
|
+
}
|
|
11639
|
+
});
|
|
11640
|
+
//#endregion
|
|
11641
|
+
//#region src/generators/cm34-mission-tasks/retire.ts
|
|
11642
|
+
const DEFAULT_RETIRE_OPTIONS = DEFAULT_DELAY_OPTIONS;
|
|
11643
|
+
const RETIRE_METADATA = {
|
|
11644
|
+
id: "retire",
|
|
11645
|
+
name: "Retire/Retirement",
|
|
11646
|
+
description: "Moves a force out of contact away from the enemy.",
|
|
11647
|
+
entity: "Mission Tasks",
|
|
11648
|
+
entityType: "Retire/Retirement",
|
|
11649
|
+
value: "342000",
|
|
11650
|
+
minCoordinates: 3,
|
|
11651
|
+
maxCoordinates: 3,
|
|
11652
|
+
geometry: "line",
|
|
11653
|
+
geometryTypes: ["MultiLineString", "Point"],
|
|
11654
|
+
paints: {
|
|
11655
|
+
stroke: true,
|
|
11656
|
+
fill: "none",
|
|
11657
|
+
text: true
|
|
11658
|
+
},
|
|
11659
|
+
drawRule: "Line24",
|
|
11660
|
+
capturesLabelSize: true,
|
|
11661
|
+
params: DELAY_PARAMS
|
|
11662
|
+
};
|
|
11663
|
+
/** Generates the RETIRE/RETIREMENT mission task symbol (342000). */
|
|
11664
|
+
function createRetireSymbol(coordinates, options = {}) {
|
|
11665
|
+
return createDelayVariantSymbol(coordinates, {
|
|
11666
|
+
glyph: "R",
|
|
11667
|
+
part: "retire"
|
|
11668
|
+
}, options);
|
|
11669
|
+
}
|
|
11670
|
+
const RETIRE = defineControlMeasure({
|
|
11671
|
+
metadata: RETIRE_METADATA,
|
|
11672
|
+
generator: createRetireSymbol,
|
|
11673
|
+
defaultOptions: DEFAULT_RETIRE_OPTIONS,
|
|
11674
|
+
rule: line24DrawRule
|
|
11675
|
+
});
|
|
11676
|
+
//#endregion
|
|
10787
11677
|
//#region src/generators/cm34-mission-tasks/screen.ts
|
|
10788
11678
|
const DEFAULT_SCREEN_OPTIONS = DEFAULT_SECURITY_TASK_OPTIONS;
|
|
10789
11679
|
const SCREEN_METADATA = {
|
|
@@ -10829,6 +11719,281 @@ const SCREEN = defineControlMeasure({
|
|
|
10829
11719
|
}
|
|
10830
11720
|
});
|
|
10831
11721
|
//#endregion
|
|
11722
|
+
//#region src/generators/cm34-mission-tasks/seize.ts
|
|
11723
|
+
const DEFAULT_SEIZE_OPTIONS = {
|
|
11724
|
+
arrowheadLengthRatio: .1,
|
|
11725
|
+
arrowheadWidthRatio: .12,
|
|
11726
|
+
labelGapRatio: .13,
|
|
11727
|
+
curveSegments: 48,
|
|
11728
|
+
circleSegments: 64
|
|
11729
|
+
};
|
|
11730
|
+
const SEIZE_METADATA = {
|
|
11731
|
+
id: "seize",
|
|
11732
|
+
name: "Seize",
|
|
11733
|
+
description: "Takes possession of a designated area using overwhelming force and establishes control of it.",
|
|
11734
|
+
entity: "Mission Tasks",
|
|
11735
|
+
entityType: "Seize",
|
|
11736
|
+
value: "342300",
|
|
11737
|
+
minCoordinates: 4,
|
|
11738
|
+
maxCoordinates: 4,
|
|
11739
|
+
geometry: "line",
|
|
11740
|
+
geometryTypes: ["MultiLineString", "Point"],
|
|
11741
|
+
paints: {
|
|
11742
|
+
stroke: true,
|
|
11743
|
+
fill: "none",
|
|
11744
|
+
text: true
|
|
11745
|
+
},
|
|
11746
|
+
drawRule: "Line27",
|
|
11747
|
+
capturesLabelSize: true,
|
|
11748
|
+
params: [
|
|
11749
|
+
{
|
|
11750
|
+
key: "arrowheadLengthRatio",
|
|
11751
|
+
label: "Arrowhead length",
|
|
11752
|
+
description: "Arrowhead length as a ratio of the PT. 2-to-PT. 4 span",
|
|
11753
|
+
type: "number",
|
|
11754
|
+
min: .01,
|
|
11755
|
+
max: .5,
|
|
11756
|
+
step: .01
|
|
11757
|
+
},
|
|
11758
|
+
{
|
|
11759
|
+
key: "arrowheadWidthRatio",
|
|
11760
|
+
label: "Arrowhead width",
|
|
11761
|
+
description: "Arrowhead base width as a ratio of the PT. 2-to-PT. 4 span",
|
|
11762
|
+
type: "number",
|
|
11763
|
+
min: .01,
|
|
11764
|
+
max: .5,
|
|
11765
|
+
step: .01
|
|
11766
|
+
},
|
|
11767
|
+
{
|
|
11768
|
+
key: "labelGapRatio",
|
|
11769
|
+
label: "Label gap",
|
|
11770
|
+
description: "Size of the curve gap holding the S label",
|
|
11771
|
+
type: "number",
|
|
11772
|
+
min: 0,
|
|
11773
|
+
max: .5,
|
|
11774
|
+
step: .01
|
|
11775
|
+
},
|
|
11776
|
+
{
|
|
11777
|
+
key: "curveSegments",
|
|
11778
|
+
label: "Curve segments",
|
|
11779
|
+
description: "Number of straight segments used to approximate the circular arc",
|
|
11780
|
+
type: "number",
|
|
11781
|
+
min: 4,
|
|
11782
|
+
max: 128,
|
|
11783
|
+
step: 1
|
|
11784
|
+
},
|
|
11785
|
+
{
|
|
11786
|
+
key: "circleSegments",
|
|
11787
|
+
label: "Circle segments",
|
|
11788
|
+
description: "Number of straight segments used to approximate the assigned-unit circle",
|
|
11789
|
+
type: "number",
|
|
11790
|
+
min: 12,
|
|
11791
|
+
max: 128,
|
|
11792
|
+
step: 1
|
|
11793
|
+
}
|
|
11794
|
+
]
|
|
11795
|
+
};
|
|
11796
|
+
function positiveAngle(angle) {
|
|
11797
|
+
const normalized = angle % (Math.PI * 2);
|
|
11798
|
+
return normalized < 0 ? normalized + Math.PI * 2 : normalized;
|
|
11799
|
+
}
|
|
11800
|
+
/** Returns the unique circular arc from start to end that passes through middle. */
|
|
11801
|
+
function threePointArc(start, middle, end) {
|
|
11802
|
+
const [ax, ay] = start;
|
|
11803
|
+
const [bx, by] = middle;
|
|
11804
|
+
const [cx, cy] = end;
|
|
11805
|
+
const determinant = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
|
|
11806
|
+
const scale = Math.max(vecMag(vecSub(middle, start)), vecMag(vecSub(end, start)), 1);
|
|
11807
|
+
if (Math.abs(determinant) < 1e-6 * scale * scale) return null;
|
|
11808
|
+
const a2 = ax * ax + ay * ay;
|
|
11809
|
+
const b2 = bx * bx + by * by;
|
|
11810
|
+
const c2 = cx * cx + cy * cy;
|
|
11811
|
+
const center = [(a2 * (by - cy) + b2 * (cy - ay) + c2 * (ay - by)) / determinant, (a2 * (cx - bx) + b2 * (ax - cx) + c2 * (bx - ax)) / determinant];
|
|
11812
|
+
const radius = vecMag(vecSub(start, center));
|
|
11813
|
+
if (radius < 1e-6) return null;
|
|
11814
|
+
const startAngle = Math.atan2(ay - center[1], ax - center[0]);
|
|
11815
|
+
const middleAngle = Math.atan2(by - center[1], bx - center[0]);
|
|
11816
|
+
const counterclockwiseSweep = positiveAngle(Math.atan2(cy - center[1], cx - center[0]) - startAngle);
|
|
11817
|
+
return {
|
|
11818
|
+
center,
|
|
11819
|
+
radius,
|
|
11820
|
+
startAngle,
|
|
11821
|
+
sweepAngle: positiveAngle(middleAngle - startAngle) <= counterclockwiseSweep ? counterclockwiseSweep : counterclockwiseSweep - Math.PI * 2
|
|
11822
|
+
};
|
|
11823
|
+
}
|
|
11824
|
+
/** Generates the Seize mission task symbol (342300). */
|
|
11825
|
+
function createSeizeSymbol(coordinates, options = {}) {
|
|
11826
|
+
const [c1, c2, c3, c4] = coordinates;
|
|
11827
|
+
const center = project(c1[0], c1[1]);
|
|
11828
|
+
const start = project(c2[0], c2[1]);
|
|
11829
|
+
const curvePoint = project(c3[0], c3[1]);
|
|
11830
|
+
const tip = project(c4[0], c4[1]);
|
|
11831
|
+
const radiusVector = vecSub(start, center);
|
|
11832
|
+
const radius = vecMag(radiusVector);
|
|
11833
|
+
const span = vecMag(vecSub(tip, start));
|
|
11834
|
+
if (radius < 1e-6 || span < 1e-6) return {
|
|
11835
|
+
type: "FeatureCollection",
|
|
11836
|
+
features: []
|
|
11837
|
+
};
|
|
11838
|
+
const curveSegments = Math.round(Math.min(128, Math.max(4, options.curveSegments ?? DEFAULT_SEIZE_OPTIONS.curveSegments)));
|
|
11839
|
+
const circleSegments = Math.round(Math.min(128, Math.max(12, options.circleSegments ?? DEFAULT_SEIZE_OPTIONS.circleSegments)));
|
|
11840
|
+
const arc = threePointArc(start, curvePoint, tip);
|
|
11841
|
+
let firstArcPart;
|
|
11842
|
+
let secondArcPart;
|
|
11843
|
+
let arrowDirection;
|
|
11844
|
+
let labelPoint;
|
|
11845
|
+
let tangentAtLabel;
|
|
11846
|
+
if (arc) {
|
|
11847
|
+
const gapHalf = clamp01(options.labelGapRatio ?? DEFAULT_SEIZE_OPTIONS.labelGapRatio) / 2;
|
|
11848
|
+
const gapStart = .5 - gapHalf;
|
|
11849
|
+
const gapEnd = .5 + gapHalf;
|
|
11850
|
+
const sampleArc = (from, to) => {
|
|
11851
|
+
const count = Math.max(1, Math.ceil(curveSegments * (to - from)));
|
|
11852
|
+
const points = [];
|
|
11853
|
+
for (let index = 0; index <= count; index += 1) {
|
|
11854
|
+
const progress = from + (to - from) * index / count;
|
|
11855
|
+
const angle = arc.startAngle + arc.sweepAngle * progress;
|
|
11856
|
+
points.push(toPosition(vecAdd(arc.center, [arc.radius * Math.cos(angle), arc.radius * Math.sin(angle)])));
|
|
11857
|
+
}
|
|
11858
|
+
return points;
|
|
11859
|
+
};
|
|
11860
|
+
firstArcPart = sampleArc(0, gapStart);
|
|
11861
|
+
secondArcPart = sampleArc(gapEnd, 1);
|
|
11862
|
+
const endAngle = arc.startAngle + arc.sweepAngle;
|
|
11863
|
+
const sweepSign = Math.sign(arc.sweepAngle);
|
|
11864
|
+
arrowDirection = [-Math.sin(endAngle) * sweepSign, Math.cos(endAngle) * sweepSign];
|
|
11865
|
+
const labelAngle = arc.startAngle + arc.sweepAngle / 2;
|
|
11866
|
+
labelPoint = vecAdd(arc.center, [arc.radius * Math.cos(labelAngle), arc.radius * Math.sin(labelAngle)]);
|
|
11867
|
+
tangentAtLabel = [-Math.sin(labelAngle) * sweepSign, Math.cos(labelAngle) * sweepSign];
|
|
11868
|
+
} else {
|
|
11869
|
+
labelPoint = vecScale(vecAdd(start, tip), .5);
|
|
11870
|
+
const gapHalf = span * clamp01(options.labelGapRatio ?? DEFAULT_SEIZE_OPTIONS.labelGapRatio) / 2;
|
|
11871
|
+
const direction = vecNorm(vecSub(tip, start));
|
|
11872
|
+
const before = vecAdd(labelPoint, vecScale(direction, -gapHalf));
|
|
11873
|
+
const after = vecAdd(labelPoint, vecScale(direction, gapHalf));
|
|
11874
|
+
firstArcPart = [toPosition(start), toPosition(before)];
|
|
11875
|
+
secondArcPart = [toPosition(after), toPosition(tip)];
|
|
11876
|
+
const terminal = vecSub(tip, curvePoint);
|
|
11877
|
+
arrowDirection = vecMag(terminal) >= 1e-6 ? vecNorm(terminal) : vecNorm(vecSub(tip, start));
|
|
11878
|
+
tangentAtLabel = direction;
|
|
11879
|
+
}
|
|
11880
|
+
const startAngle = Math.atan2(radiusVector[1], radiusVector[0]);
|
|
11881
|
+
const circle = [];
|
|
11882
|
+
for (let index = 0; index <= circleSegments; index += 1) {
|
|
11883
|
+
const angle = startAngle + index / circleSegments * Math.PI * 2;
|
|
11884
|
+
circle.push(toPosition(vecAdd(center, [radius * Math.cos(angle), radius * Math.sin(angle)])));
|
|
11885
|
+
}
|
|
11886
|
+
return {
|
|
11887
|
+
type: "FeatureCollection",
|
|
11888
|
+
features: [{
|
|
11889
|
+
type: "Feature",
|
|
11890
|
+
properties: { part: "seize" },
|
|
11891
|
+
geometry: {
|
|
11892
|
+
type: "MultiLineString",
|
|
11893
|
+
coordinates: [
|
|
11894
|
+
circle,
|
|
11895
|
+
firstArcPart,
|
|
11896
|
+
secondArcPart,
|
|
11897
|
+
createArrowHead$1(tip, arrowDirection, span * Math.max(0, options.arrowheadLengthRatio ?? DEFAULT_SEIZE_OPTIONS.arrowheadLengthRatio), span * Math.max(0, options.arrowheadWidthRatio ?? DEFAULT_SEIZE_OPTIONS.arrowheadWidthRatio))
|
|
11898
|
+
]
|
|
11899
|
+
}
|
|
11900
|
+
}, createLabelFeature(labelPoint, "S", labelRotationAlong(tangentAtLabel), options)]
|
|
11901
|
+
};
|
|
11902
|
+
}
|
|
11903
|
+
const SEIZE = defineControlMeasure({
|
|
11904
|
+
metadata: SEIZE_METADATA,
|
|
11905
|
+
generator: createSeizeSymbol,
|
|
11906
|
+
defaultOptions: DEFAULT_SEIZE_OPTIONS,
|
|
11907
|
+
rule: line27DrawRule,
|
|
11908
|
+
previewSample: {
|
|
11909
|
+
controlPoints: [
|
|
11910
|
+
[-.8, .35],
|
|
11911
|
+
[-.45, .35],
|
|
11912
|
+
[.15, .25],
|
|
11913
|
+
[.85, -.55]
|
|
11914
|
+
],
|
|
11915
|
+
options: { labelSize: 100 }
|
|
11916
|
+
}
|
|
11917
|
+
});
|
|
11918
|
+
//#endregion
|
|
11919
|
+
//#region src/generators/cm34-mission-tasks/withdraw.ts
|
|
11920
|
+
const DEFAULT_WITHDRAW_OPTIONS = DEFAULT_DELAY_OPTIONS;
|
|
11921
|
+
const WITHDRAW_METADATA = {
|
|
11922
|
+
id: "withdraw",
|
|
11923
|
+
name: "Withdraw",
|
|
11924
|
+
description: "Disengages a force from the enemy and moves it away from enemy contact.",
|
|
11925
|
+
entity: "Mission Tasks",
|
|
11926
|
+
entityType: "Withdraw",
|
|
11927
|
+
value: "342400",
|
|
11928
|
+
minCoordinates: 3,
|
|
11929
|
+
maxCoordinates: 3,
|
|
11930
|
+
geometry: "line",
|
|
11931
|
+
geometryTypes: ["MultiLineString", "Point"],
|
|
11932
|
+
paints: {
|
|
11933
|
+
stroke: true,
|
|
11934
|
+
fill: "none",
|
|
11935
|
+
text: true
|
|
11936
|
+
},
|
|
11937
|
+
drawRule: "Line24",
|
|
11938
|
+
capturesLabelSize: true,
|
|
11939
|
+
params: DELAY_PARAMS
|
|
11940
|
+
};
|
|
11941
|
+
function createWithdrawSymbol(coordinates, options = {}) {
|
|
11942
|
+
return createDelayVariantSymbol(coordinates, {
|
|
11943
|
+
glyph: "W",
|
|
11944
|
+
part: "withdraw"
|
|
11945
|
+
}, options);
|
|
11946
|
+
}
|
|
11947
|
+
const WITHDRAW = defineControlMeasure({
|
|
11948
|
+
metadata: WITHDRAW_METADATA,
|
|
11949
|
+
generator: createWithdrawSymbol,
|
|
11950
|
+
defaultOptions: DEFAULT_WITHDRAW_OPTIONS,
|
|
11951
|
+
rule: line24DrawRule
|
|
11952
|
+
});
|
|
11953
|
+
//#endregion
|
|
11954
|
+
//#region src/generators/cm34-mission-tasks/withdraw-under-pressure.ts
|
|
11955
|
+
const DEFAULT_WITHDRAW_UNDER_PRESSURE_OPTIONS = DEFAULT_DELAY_OPTIONS;
|
|
11956
|
+
const WITHDRAW_UNDER_PRESSURE_METADATA = {
|
|
11957
|
+
id: "withdraw-under-pressure",
|
|
11958
|
+
name: "Withdraw Under Pressure",
|
|
11959
|
+
description: "Disengages a force from the enemy and moves it away while under enemy pressure.",
|
|
11960
|
+
entity: "Mission Tasks",
|
|
11961
|
+
entityType: "Withdraw Under Pressure",
|
|
11962
|
+
value: "342500",
|
|
11963
|
+
minCoordinates: 3,
|
|
11964
|
+
maxCoordinates: 3,
|
|
11965
|
+
geometry: "line",
|
|
11966
|
+
geometryTypes: ["MultiLineString", "Point"],
|
|
11967
|
+
paints: {
|
|
11968
|
+
stroke: true,
|
|
11969
|
+
fill: "none",
|
|
11970
|
+
text: true
|
|
11971
|
+
},
|
|
11972
|
+
drawRule: "Line24",
|
|
11973
|
+
capturesLabelSize: true,
|
|
11974
|
+
params: DELAY_PARAMS
|
|
11975
|
+
};
|
|
11976
|
+
function createWithdrawUnderPressureSymbol(coordinates, options = {}) {
|
|
11977
|
+
return createDelayVariantSymbol(coordinates, {
|
|
11978
|
+
glyph: "WP",
|
|
11979
|
+
part: "withdraw-under-pressure"
|
|
11980
|
+
}, options);
|
|
11981
|
+
}
|
|
11982
|
+
const WITHDRAW_UNDER_PRESSURE = defineControlMeasure({
|
|
11983
|
+
metadata: WITHDRAW_UNDER_PRESSURE_METADATA,
|
|
11984
|
+
generator: createWithdrawUnderPressureSymbol,
|
|
11985
|
+
defaultOptions: DEFAULT_WITHDRAW_UNDER_PRESSURE_OPTIONS,
|
|
11986
|
+
rule: line24DrawRule,
|
|
11987
|
+
previewSample: {
|
|
11988
|
+
controlPoints: [
|
|
11989
|
+
[-1, -.4],
|
|
11990
|
+
[1, -.4],
|
|
11991
|
+
[0, .6]
|
|
11992
|
+
],
|
|
11993
|
+
options: { labelGapRatio: .3 }
|
|
11994
|
+
}
|
|
11995
|
+
});
|
|
11996
|
+
//#endregion
|
|
10832
11997
|
//#region src/generators/cm15-maneuver-areas/jointTacticalActionArea.ts
|
|
10833
11998
|
/** Default options for the Joint tactical action area control measure. */
|
|
10834
11999
|
const DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS = {
|
|
@@ -12699,11 +13864,20 @@ const DEFINITIONS = {
|
|
|
12699
13864
|
canalize: CANALIZE,
|
|
12700
13865
|
clear: CLEAR,
|
|
12701
13866
|
counterattack: COUNTERATTACK,
|
|
13867
|
+
"counterattack-by-fire": COUNTERATTACK_BY_FIRE,
|
|
12702
13868
|
cover: COVER,
|
|
12703
13869
|
delay: DELAY,
|
|
13870
|
+
disengage: DISENGAGE,
|
|
13871
|
+
"disrupt-mission-task": DISRUPT_MISSION_TASK,
|
|
12704
13872
|
guard: GUARD,
|
|
12705
13873
|
isolate: ISOLATE,
|
|
13874
|
+
penetrate: PENETRATE,
|
|
13875
|
+
"rearward-passage-of-lines": REARWARD_PASSAGE_OF_LINES,
|
|
13876
|
+
retire: RETIRE,
|
|
12706
13877
|
screen: SCREEN,
|
|
13878
|
+
seize: SEIZE,
|
|
13879
|
+
withdraw: WITHDRAW,
|
|
13880
|
+
"withdraw-under-pressure": WITHDRAW_UNDER_PRESSURE,
|
|
12707
13881
|
"antitank-ditch-under-construction": ANTITANK_DITCH_UNDER_CONSTRUCTION,
|
|
12708
13882
|
"antitank-ditch-completed": ANTITANK_DITCH_COMPLETED,
|
|
12709
13883
|
"antitank-wall": ANTITANK_WALL,
|
|
@@ -12733,6 +13907,7 @@ const DEFINITIONS = {
|
|
|
12733
13907
|
block: BLOCK,
|
|
12734
13908
|
disrupt: DISRUPT,
|
|
12735
13909
|
fix: FIX,
|
|
13910
|
+
"fix-mission-task": FIX_MISSION_TASK,
|
|
12736
13911
|
turn: TURN,
|
|
12737
13912
|
"obstacle-bypass-easy": OBSTACLE_BYPASS_EASY,
|
|
12738
13913
|
"obstacle-bypass-difficult": OBSTACLE_BYPASS_DIFFICULT,
|
|
@@ -13100,4 +14275,4 @@ function assertNever(value) {
|
|
|
13100
14275
|
throw new Error(`Unhandled control measure kind: ${String(value)}`);
|
|
13101
14276
|
}
|
|
13102
14277
|
//#endregion
|
|
13103
|
-
export {
|
|
14278
|
+
export { DEFAULT_COUNTERATTACK_BY_FIRE_OPTIONS as $, createMidpointPerpendicularDrawRule as $t, DEFAULT_PENETRATE_OPTIONS as A, TEXT_AMPLIFIER_FIELDS as At, DEFAULT_PHASE_LINE_OPTIONS as B, line26DrawRule as Bt, DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS as C, DEFAULT_ANTITANK_DITCH_OPTIONS as Ct, DEFAULT_SCREEN_OPTIONS as D, DEFAULT_DIRECTION_OF_SUPPORTING_ATTACK_OPTIONS as Dt, DEFAULT_SEIZE_OPTIONS as E, DEFAULT_AREA_DEFENSE_OPTIONS as Et, DEFAULT_HANDOVER_LINE_OPTIONS as F, DEFAULT_AIRBORNE_ATTACK_OPTIONS as Ft, DEFAULT_ENCIRCLEMENT_OPTIONS as G, ambushDrawRule as Gt, DEFAULT_FIX_MISSION_TASK_OPTIONS as H, line23DrawRule as Ht, DEFAULT_FORWARD_EDGE_OF_BATTLE_AREA_OPTIONS as I, rectangleDrawRule as It, DEFAULT_DISRUPT_MISSION_TASK_OPTIONS as J, penetrateDrawRule as Jt, DEFAULT_DISRUPT_OPTIONS as K, attackByFireDrawRule as Kt, DEFAULT_RELEASE_LINE_OPTIONS as L, axis1DrawRule as Lt, DEFAULT_FORTIFIED_AREA_OPTIONS as M, normalizeTextAmplifiers as Mt, DEFAULT_FORTIFIED_LINE_OPTIONS as N, resolveAmplifierPlacement as Nt, DEFAULT_RETIRE_OPTIONS as O, DEFAULT_DIRECTION_OF_MAIN_ATTACK_OPTIONS as Ot, DEFAULT_BATTLE_HANDOVER_LINE_OPTIONS as P, DEFAULT_AMBUSH_OPTIONS as Pt, DEFAULT_TACTICAL_ARROW_OPTIONS as Q, computeDefaultMidpointPerpendicularPoint as Qt, DEFAULT_HOLDING_LINE_OPTIONS as R, supportByFireDrawRule as Rt, DEFAULT_LANDING_ZONE_OPTIONS as S, DEFAULT_ANTITANK_WALL_OPTIONS as St, DEFAULT_WITHDRAW_OPTIONS as T, DEFAULT_AREA_OF_OPERATIONS_OPTIONS as Tt, DEFAULT_FIX_OPTIONS as U, turnDrawRule as Ut, DEFAULT_FLOT_OPTIONS as V, line24DrawRule as Vt, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as W, line1DrawRule as Wt, DEFAULT_DELAY_OPTIONS as X, disruptDrawRule as Xt, DEFAULT_DISENGAGE_OPTIONS as Y, centerRadiusDrawRule as Yt, DEFAULT_COVER_OPTIONS as Z, blockDrawRule as Zt, DEFAULT_PICKUP_ZONE_OPTIONS as _, DEFAULT_BLOCK_ARROW_OPTIONS as _t, DEFINITIONS as a, computeInitialWidthPoint as an, DEFAULT_GENERIC_POLYGON_OPTIONS as at, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as b, DEFAULT_ATTACK_HELICOPTER_OPTIONS as bt, getDefaultOptions as c, unproject as cn, DEFAULT_CLASSIC_ARROW_OPTIONS as ct, DEFAULT_TURN_OPTIONS as d, roundToFixed as dn, DEFAULT_BREACH_OPTIONS as dt, getMidpointPerpendicularSignedDistance as en, DEFAULT_COUNTERATTACK_OPTIONS as et, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as f, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as ft, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as g, DEFAULT_BOUNDARY_OPTIONS as gt, SECONDARY_DIRECTION_OF_FIRE_DASH as h, DEFAULT_LIGHT_LINE_OPTIONS as ht, CONTROL_MEASURE_METADATA as i, calculateMetrics as in, DEFAULT_GENERIC_RECTANGLE_OPTIONS as it, DEFAULT_FRONTAL_ATTACK_OPTIONS as j, canonicalTextAmplifierKey as jt, DEFAULT_REARWARD_PASSAGE_OF_LINES_OPTIONS as k, DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS as kt, listControlMeasureMetadata as l, EPSILON as ln, DEFAULT_CANALIZE_OPTIONS as lt, DEFAULT_SECONDARY_DIRECTION_OF_FIRE_OPTIONS as m, DEFAULT_ENGINEER_WORK_LINE_OPTIONS as mt, resolveStyleHints as n, snapToMidpointPerpendicular as nn, DEFAULT_CLEAR_OPTIONS as nt, getControlMeasureMetadata as o, haversineDistance as on, DEFAULT_GENERIC_LINE_OPTIONS as ot, DEFAULT_STRONG_POINT_OPTIONS as p, DEFAULT_GENERIC_C2_LINE_OPTIONS as pt, DEFAULT_GUARD_OPTIONS as q, point12DrawRule as qt, CONTROL_MEASURE_IDS as r, createBaselineFrame as rn, DEFAULT_GENERIC_TEXT_OPTIONS as rt, getControlMeasureMetadataByValue as s, project as sn, DEFAULT_GENERIC_CIRCLE_OPTIONS as st, renderControlMeasure as t, pointOnMidpointPerpendicularAxis as tn, DEFAULT_SUPPORTING_ATTACK_OPTIONS as tt, DEFAULT_TURNING_MOVEMENT_OPTIONS as u, getMetersPerPixel as un, DEFAULT_BYPASS_OPTIONS as ut, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as v, DEFAULT_BLOCK_OPTIONS as vt, DEFAULT_WITHDRAW_UNDER_PRESSURE_OPTIONS as w, DEFAULT_ASSEMBLY_AREA_OPTIONS as wt, DEFAULT_MAIN_ATTACK_OPTIONS as x, DEFAULT_ATTACK_BY_FIRE_OPTIONS as xt, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as y, DEFAULT_BATTLE_POSITION_OPTIONS as yt, DEFAULT_BRIDGEHEAD_LINE_OPTIONS as z, line27DrawRule as zt };
|