@orbat-mapper/control-measures 0.11.0 → 0.12.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/dist/{index-CwSQl5SE.d.mts → index-DelKQdBf.d.mts} +48 -10
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +24 -2
- package/dist/preview/index.d.mts +15 -2
- package/dist/preview/index.mjs +38 -6
- package/dist/{renderControlMeasure-DJo_s-lN.mjs → renderControlMeasure-DYcehsHK.mjs} +305 -84
- package/media/area-of-operations.svg +1 -1
- package/media/generic-c2-line.svg +1 -1
- package/media/turning-movement.svg +1 -1
- package/package.json +1 -1
|
@@ -130,15 +130,27 @@ const destinationPoint = (origin, distance, bearing) => {
|
|
|
130
130
|
//#endregion
|
|
131
131
|
//#region src/internal/vector-utils.ts
|
|
132
132
|
/**
|
|
133
|
-
* Offsets a polyline using Miter Joins or Round Joins.
|
|
133
|
+
* Offsets a polyline to both sides at once, using Miter Joins or Round Joins.
|
|
134
|
+
* `offsets` carries one half-width per vertex, so the outline may taper along
|
|
135
|
+
* its centerline; both sides read the same array rather than materialising a
|
|
136
|
+
* negated copy per geometry pass.
|
|
134
137
|
*/
|
|
135
|
-
function
|
|
138
|
+
function offsetPolylineSides(points, offsets, rounded = false, segments = 5) {
|
|
139
|
+
return {
|
|
140
|
+
left: offsetPolylineWithOffsets(points, offsets, rounded, segments, 1),
|
|
141
|
+
right: offsetPolylineWithOffsets(points, offsets, rounded, segments, -1)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** One side of {@link offsetPolylineSides}; `sign` picks which. */
|
|
145
|
+
function offsetPolylineWithOffsets(points, offsets, rounded, segments, sign) {
|
|
136
146
|
if (points.length < 2) return points;
|
|
147
|
+
if (offsets.length !== points.length) return points;
|
|
137
148
|
const result = [];
|
|
138
149
|
const N = points.length;
|
|
139
150
|
for (let i = 0; i < N; i++) {
|
|
140
151
|
const p = points[i];
|
|
141
152
|
if (!p) continue;
|
|
153
|
+
const offset = offsets[i] * sign;
|
|
142
154
|
if (i === 0) {
|
|
143
155
|
const next = points[i + 1];
|
|
144
156
|
if (next) {
|
|
@@ -195,6 +207,17 @@ function offsetPolyline(points, offset, rounded = false, segments = 5) {
|
|
|
195
207
|
}
|
|
196
208
|
return result;
|
|
197
209
|
}
|
|
210
|
+
/** Linearly interpolates values by distance along a polyline, not vertex count. */
|
|
211
|
+
function interpolatePolylineValues(points, startValue, endValue) {
|
|
212
|
+
const distances = [0];
|
|
213
|
+
for (let i = 1; i < points.length; i++) distances.push(distances[i - 1] + vecMag(vecSub(points[i], points[i - 1])));
|
|
214
|
+
const totalLength = distances.at(-1) ?? 0;
|
|
215
|
+
if (totalLength < 1e-6) return points.map(() => endValue);
|
|
216
|
+
return distances.map((distance) => {
|
|
217
|
+
const progress = distance / totalLength;
|
|
218
|
+
return startValue + (endValue - startValue) * progress;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
198
221
|
function vecAdd(a, b) {
|
|
199
222
|
return [a[0] + b[0], a[1] + b[1]];
|
|
200
223
|
}
|
|
@@ -248,9 +271,81 @@ function lineIntersection(p1, p2, p3, p4) {
|
|
|
248
271
|
}
|
|
249
272
|
//#endregion
|
|
250
273
|
//#region src/attack-utils.ts
|
|
251
|
-
const DEFAULT_SHAFT_WIDTH_RATIO = .6;
|
|
274
|
+
const DEFAULT_SHAFT_WIDTH_RATIO$1 = .6;
|
|
275
|
+
const DEFAULT_REAR_WIDTH_RATIO = DEFAULT_SHAFT_WIDTH_RATIO$1;
|
|
252
276
|
const SHAFT_MIN_RATIO = .1;
|
|
253
277
|
const SHAFT_MAX_RATIO = .9;
|
|
278
|
+
const REAR_WIDTH_OPTION_HANDLE_ID = "rear-width";
|
|
279
|
+
const SHAFT_WIDTH_OPTION_HANDLE_ID = "shaft-width";
|
|
280
|
+
/**
|
|
281
|
+
* Creates the definition-owned rear-/shaft-width handle pair shared by every
|
|
282
|
+
* variable-width arrow body. Callers supply only how their own geometry maps
|
|
283
|
+
* onto {@link WidthHandleGeometry}; placement (the two ends of the left edge)
|
|
284
|
+
* and the drag math (perpendicular distance from the dragged point to the
|
|
285
|
+
* matching centerline segment, over the reference half-width) live here once.
|
|
286
|
+
*/
|
|
287
|
+
function createWidthOptionHandles(config) {
|
|
288
|
+
return {
|
|
289
|
+
get(controlPoints, options) {
|
|
290
|
+
const geometry = config.geometry(controlPoints, options);
|
|
291
|
+
const rearLeft = geometry?.leftEdge[0];
|
|
292
|
+
const neckLeft = geometry?.leftEdge.at(-1);
|
|
293
|
+
if (!rearLeft || !neckLeft) return [];
|
|
294
|
+
return [{
|
|
295
|
+
id: REAR_WIDTH_OPTION_HANDLE_ID,
|
|
296
|
+
position: unproject(rearLeft[0], rearLeft[1])
|
|
297
|
+
}, {
|
|
298
|
+
id: SHAFT_WIDTH_OPTION_HANDLE_ID,
|
|
299
|
+
position: unproject(neckLeft[0], neckLeft[1])
|
|
300
|
+
}];
|
|
301
|
+
},
|
|
302
|
+
drag({ controlPoints, options, handleId, position }) {
|
|
303
|
+
const geometry = config.geometry(controlPoints, options);
|
|
304
|
+
if (!geometry || geometry.referenceHalfWidth < 1e-6) return void 0;
|
|
305
|
+
const rear = handleId === REAR_WIDTH_OPTION_HANDLE_ID;
|
|
306
|
+
if (!rear && !(handleId === "shaft-width")) return void 0;
|
|
307
|
+
const from = rear ? geometry.spine[0] : geometry.spine.at(-2);
|
|
308
|
+
const to = rear ? geometry.spine[1] : geometry.spine.at(-1);
|
|
309
|
+
if (!from || !to) return void 0;
|
|
310
|
+
const ratio = clamp(pointToInfiniteLineDistance(project(position[0], position[1]), from, to) / geometry.referenceHalfWidth, config.minRatio, rear ? config.maxRearRatio : config.maxShaftRatio);
|
|
311
|
+
return rear ? { rearWidthRatio: ratio } : { shaftWidthRatio: ratio };
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* The width handles for attack bodies built by {@link processAttackGeometry},
|
|
317
|
+
* whose ratios are fractions of the arrowhead half-width. A coordinate resolver
|
|
318
|
+
* lets derivative measures (Counterattack by Fire) map their authored Axis1
|
|
319
|
+
* points to the body coordinates first.
|
|
320
|
+
*/
|
|
321
|
+
function createVariableWidthAttackOptionHandles(resolveCoordinates = (points) => points) {
|
|
322
|
+
return createWidthOptionHandles({
|
|
323
|
+
geometry(controlPoints, options) {
|
|
324
|
+
const coordinates = resolveCoordinates(controlPoints);
|
|
325
|
+
if (!coordinates) return null;
|
|
326
|
+
const { geometry } = processAttackGeometry(coordinates, options);
|
|
327
|
+
const outerLeft = geometry?.headRing[0];
|
|
328
|
+
if (!geometry || !outerLeft) return null;
|
|
329
|
+
return {
|
|
330
|
+
spine: geometry.shaftCenterline,
|
|
331
|
+
leftEdge: geometry.shaftLeft,
|
|
332
|
+
referenceHalfWidth: vecMag(vecSub(outerLeft, geometry.ptBase))
|
|
333
|
+
};
|
|
334
|
+
},
|
|
335
|
+
minRatio: SHAFT_MIN_RATIO,
|
|
336
|
+
maxRearRatio: 3,
|
|
337
|
+
maxShaftRatio: SHAFT_MAX_RATIO
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const variableWidthAttackOptionHandles = createVariableWidthAttackOptionHandles();
|
|
341
|
+
/**
|
|
342
|
+
* The single home of the "rear inherits shaft" rule: an omitted `rearWidthRatio`
|
|
343
|
+
* leaves the body parallel by matching the effective shaft ratio. Every
|
|
344
|
+
* variable-width body resolves the option through here so the rule cannot drift.
|
|
345
|
+
*/
|
|
346
|
+
function resolveRearWidthRatio(options, shaftRatio) {
|
|
347
|
+
return options.rearWidthRatio ?? shaftRatio;
|
|
348
|
+
}
|
|
254
349
|
/**
|
|
255
350
|
* Processes input coordinates and options to generate the core symbol geometry.
|
|
256
351
|
* This handles validation, default values, projection, and geometry calculation.
|
|
@@ -259,6 +354,7 @@ function processAttackGeometry(coordinates, options = {}) {
|
|
|
259
354
|
const shaftRatio = clamp(options.shaftWidthRatio ?? .6, SHAFT_MIN_RATIO, SHAFT_MAX_RATIO);
|
|
260
355
|
const smooth = options.smooth ?? false;
|
|
261
356
|
const smoothResolution = options.smoothResolution ?? 5;
|
|
357
|
+
const rearRatio = clamp(resolveRearWidthRatio(options, shaftRatio), SHAFT_MIN_RATIO, 3);
|
|
262
358
|
const points = coordinates.map((c) => project(c[0], c[1]));
|
|
263
359
|
const numPoints = points.length;
|
|
264
360
|
const ptTip = points[0];
|
|
@@ -268,9 +364,9 @@ function processAttackGeometry(coordinates, options = {}) {
|
|
|
268
364
|
const p = points[i];
|
|
269
365
|
if (p) spinePoints.push(p);
|
|
270
366
|
}
|
|
271
|
-
return { geometry: calculateSymbolGeometry(ptTip, ptWidth, spinePoints, shaftRatio, smooth, smoothResolution) };
|
|
367
|
+
return { geometry: calculateSymbolGeometry(ptTip, ptWidth, spinePoints, shaftRatio, rearRatio, smooth, smoothResolution) };
|
|
272
368
|
}
|
|
273
|
-
function calculateSymbolGeometry(ptTip, ptWidth, spine, shaftRatio, smooth, smoothResolution) {
|
|
369
|
+
function calculateSymbolGeometry(ptTip, ptWidth, spine, shaftRatio, rearWidthRatio, smooth, smoothResolution) {
|
|
274
370
|
const initialNeck = spine[spine.length - 1];
|
|
275
371
|
if (!initialNeck) return null;
|
|
276
372
|
const initialTipDir = vecNorm(vecSub(ptTip, initialNeck));
|
|
@@ -323,10 +419,11 @@ function calculateSymbolGeometry(ptTip, ptWidth, spine, shaftRatio, smooth, smoo
|
|
|
323
419
|
outerLeft
|
|
324
420
|
];
|
|
325
421
|
const fullSpine = [...remainingSpine, shaftEndCenter];
|
|
422
|
+
const { left: shaftLeft, right: shaftRight } = offsetPolylineSides(fullSpine, interpolatePolylineValues(fullSpine, headHalfWidth * rearWidthRatio, shaftHalfWidth), smooth, smoothResolution);
|
|
326
423
|
return {
|
|
327
424
|
shaftCenterline: fullSpine,
|
|
328
|
-
shaftLeft
|
|
329
|
-
shaftRight
|
|
425
|
+
shaftLeft,
|
|
426
|
+
shaftRight,
|
|
330
427
|
headRing,
|
|
331
428
|
ptTip,
|
|
332
429
|
ptNeck,
|
|
@@ -1726,6 +1823,16 @@ const ATTACK_SHAFT_PARAMS = [
|
|
|
1726
1823
|
max: 1,
|
|
1727
1824
|
step: .05
|
|
1728
1825
|
},
|
|
1826
|
+
{
|
|
1827
|
+
key: "rearWidthRatio",
|
|
1828
|
+
presentationTier: "advanced",
|
|
1829
|
+
label: "Rear width",
|
|
1830
|
+
description: "Width at the rear of the shaft as a ratio of the arrowhead width.",
|
|
1831
|
+
type: "number",
|
|
1832
|
+
min: .1,
|
|
1833
|
+
max: 2,
|
|
1834
|
+
step: .05
|
|
1835
|
+
},
|
|
1729
1836
|
{
|
|
1730
1837
|
key: "smooth",
|
|
1731
1838
|
label: "Smooth",
|
|
@@ -1845,9 +1952,19 @@ const AREA_LABEL_PADDING_PARAM = {
|
|
|
1845
1952
|
max: 2,
|
|
1846
1953
|
step: .05
|
|
1847
1954
|
};
|
|
1955
|
+
const MULTILINE_AREA_LABEL_PARAM = {
|
|
1956
|
+
key: "multilineLabel",
|
|
1957
|
+
label: "Multiline label",
|
|
1958
|
+
description: "Place the doctrinal label above Field T on separate centered lines",
|
|
1959
|
+
type: "boolean"
|
|
1960
|
+
};
|
|
1848
1961
|
const LABELED_AREA_PARAMS = [...AREA_SMOOTH_PARAMS, AREA_LABEL_PADDING_PARAM];
|
|
1849
|
-
const
|
|
1962
|
+
const PREFIXED_LABELED_AREA_PARAMS = [
|
|
1850
1963
|
...AREA_SMOOTH_PARAMS,
|
|
1964
|
+
MULTILINE_AREA_LABEL_PARAM,
|
|
1965
|
+
AREA_LABEL_PADDING_PARAM
|
|
1966
|
+
];
|
|
1967
|
+
const ECHELON_GLYPH_PARAMS = [
|
|
1851
1968
|
{
|
|
1852
1969
|
key: "echelon",
|
|
1853
1970
|
label: "Echelon",
|
|
@@ -1896,7 +2013,17 @@ const ECHELON_AREA_PARAMS = [
|
|
|
1896
2013
|
min: -.5,
|
|
1897
2014
|
max: .5,
|
|
1898
2015
|
step: .01
|
|
1899
|
-
}
|
|
2016
|
+
}
|
|
2017
|
+
];
|
|
2018
|
+
const ECHELON_AREA_PARAMS = [
|
|
2019
|
+
...AREA_SMOOTH_PARAMS,
|
|
2020
|
+
...ECHELON_GLYPH_PARAMS,
|
|
2021
|
+
AREA_LABEL_PADDING_PARAM
|
|
2022
|
+
];
|
|
2023
|
+
const PREFIXED_ECHELON_AREA_PARAMS = [
|
|
2024
|
+
...AREA_SMOOTH_PARAMS,
|
|
2025
|
+
MULTILINE_AREA_LABEL_PARAM,
|
|
2026
|
+
...ECHELON_GLYPH_PARAMS,
|
|
1900
2027
|
AREA_LABEL_PADDING_PARAM
|
|
1901
2028
|
];
|
|
1902
2029
|
const AREA_DESIGNATION_AMPLIFIER = {
|
|
@@ -1954,7 +2081,8 @@ const AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE = AREA_TEXT_AMPLIFIERS.filter((d)
|
|
|
1954
2081
|
//#endregion
|
|
1955
2082
|
//#region src/generators/cm15-maneuver-areas/airborneAttack.ts
|
|
1956
2083
|
const DEFAULT_AIRBORNE_ATTACK_OPTIONS = {
|
|
1957
|
-
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO,
|
|
2084
|
+
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO$1,
|
|
2085
|
+
rearWidthRatio: DEFAULT_REAR_WIDTH_RATIO,
|
|
1958
2086
|
smooth: false,
|
|
1959
2087
|
smoothResolution: 5
|
|
1960
2088
|
};
|
|
@@ -2019,7 +2147,8 @@ const AIRBORNE_ATTACK = defineControlMeasure({
|
|
|
2019
2147
|
metadata: AIRBORNE_ATTACK_METADATA,
|
|
2020
2148
|
generator: createAirborneAttack,
|
|
2021
2149
|
defaultOptions: DEFAULT_AIRBORNE_ATTACK_OPTIONS,
|
|
2022
|
-
rule: axis1DrawRule
|
|
2150
|
+
rule: axis1DrawRule,
|
|
2151
|
+
optionHandles: variableWidthAttackOptionHandles
|
|
2023
2152
|
});
|
|
2024
2153
|
/**
|
|
2025
2154
|
* Reacts to an **input-contract** violation according to `mode`: `throw`
|
|
@@ -3897,8 +4026,10 @@ function composeAreaName(prefix, designation, separator = " ") {
|
|
|
3897
4026
|
* which differ only in `prefix` (and JTAA's empty `separator`).
|
|
3898
4027
|
*/
|
|
3899
4028
|
function composeAreaLabelTexts(textAmplifiers, prefix = "", separator = " ") {
|
|
4029
|
+
const designation = textAmplifiers.T?.trim() ?? "";
|
|
3900
4030
|
return {
|
|
3901
|
-
name: composeAreaName(prefix,
|
|
4031
|
+
name: composeAreaName(prefix, designation, separator),
|
|
4032
|
+
multilineName: prefix && designation ? `${prefix}\n${designation}` : void 0,
|
|
3902
4033
|
additionalInformation: textAmplifiers.H,
|
|
3903
4034
|
dtgStart: textAmplifiers.W,
|
|
3904
4035
|
dtgEnd: textAmplifiers.W1,
|
|
@@ -4134,19 +4265,41 @@ function pushAreaLabels(out, ringVerts, texts, options = {}, amplifierPlacements
|
|
|
4134
4265
|
const padding = Math.max(0, options.labelPadding ?? 0);
|
|
4135
4266
|
const maskRingVerts = maskBoundary?.ringVerts ?? ringVerts;
|
|
4136
4267
|
const [cx, cy] = ringLabelAnchor(ringVerts);
|
|
4268
|
+
const multilineName = options.multilineLabel ? texts.multilineName : void 0;
|
|
4137
4269
|
const rows = [
|
|
4138
|
-
[
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4270
|
+
[
|
|
4271
|
+
multilineName ?? texts.name,
|
|
4272
|
+
"T",
|
|
4273
|
+
multilineName ? multilineName.split("\n").length : 1
|
|
4274
|
+
],
|
|
4275
|
+
[
|
|
4276
|
+
texts.additionalInformation,
|
|
4277
|
+
"H",
|
|
4278
|
+
1
|
|
4279
|
+
],
|
|
4280
|
+
[
|
|
4281
|
+
texts.dtgStart,
|
|
4282
|
+
"W",
|
|
4283
|
+
1
|
|
4284
|
+
],
|
|
4285
|
+
[
|
|
4286
|
+
texts.dtgEnd,
|
|
4287
|
+
"W1",
|
|
4288
|
+
1
|
|
4289
|
+
]
|
|
4290
|
+
].flatMap(([text, field, lineCount]) => text ? [{
|
|
4143
4291
|
text,
|
|
4144
|
-
field
|
|
4292
|
+
field,
|
|
4293
|
+
lineCount
|
|
4145
4294
|
}] : []);
|
|
4146
4295
|
if (rows.length > 0) {
|
|
4147
4296
|
const pitch = resolveLabelOffsetMeters(options, ROW_PITCH_RATIO + padding);
|
|
4148
|
-
rows.
|
|
4149
|
-
|
|
4297
|
+
const totalLineCount = rows.reduce((sum, row) => sum + row.lineCount, 0);
|
|
4298
|
+
let precedingLineCount = 0;
|
|
4299
|
+
rows.forEach(({ text, field, lineCount }) => {
|
|
4300
|
+
const rowCenter = precedingLineCount + (lineCount - 1) / 2;
|
|
4301
|
+
pushLabel(out, [cx, cy + ((totalLineCount - 1) / 2 - rowCenter) * pitch], text, HORIZONTAL_LABEL_ROTATION, sizeProps, void 0, field);
|
|
4302
|
+
precedingLineCount += lineCount;
|
|
4150
4303
|
});
|
|
4151
4304
|
}
|
|
4152
4305
|
if (!texts.hostile) return [];
|
|
@@ -4552,7 +4705,7 @@ const ENGAGEMENT_AREA_METADATA = {
|
|
|
4552
4705
|
},
|
|
4553
4706
|
drawRule: "Area1",
|
|
4554
4707
|
capturesLabelSize: true,
|
|
4555
|
-
params:
|
|
4708
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
4556
4709
|
textAmplifiers: AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
|
|
4557
4710
|
};
|
|
4558
4711
|
/**
|
|
@@ -4819,7 +4972,7 @@ const AREA_OF_OPERATIONS_METADATA = {
|
|
|
4819
4972
|
},
|
|
4820
4973
|
drawRule: "Area1",
|
|
4821
4974
|
capturesLabelSize: true,
|
|
4822
|
-
params:
|
|
4975
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
4823
4976
|
textAmplifiers: AREA_TEXT_AMPLIFIERS
|
|
4824
4977
|
};
|
|
4825
4978
|
/**
|
|
@@ -4843,7 +4996,10 @@ const AREA_OF_OPERATIONS = defineControlMeasure({
|
|
|
4843
4996
|
[-1, .5]
|
|
4844
4997
|
],
|
|
4845
4998
|
textAmplifiers: { T: "GREEN" },
|
|
4846
|
-
options: {
|
|
4999
|
+
options: {
|
|
5000
|
+
labelSize: 96,
|
|
5001
|
+
multilineLabel: true
|
|
5002
|
+
}
|
|
4847
5003
|
}
|
|
4848
5004
|
});
|
|
4849
5005
|
//#endregion
|
|
@@ -4878,7 +5034,7 @@ const NAMED_AREA_OF_INTEREST_METADATA = {
|
|
|
4878
5034
|
},
|
|
4879
5035
|
drawRule: "Area1",
|
|
4880
5036
|
capturesLabelSize: true,
|
|
4881
|
-
params:
|
|
5037
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
4882
5038
|
textAmplifiers: AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
|
|
4883
5039
|
};
|
|
4884
5040
|
/**
|
|
@@ -4937,7 +5093,7 @@ const TARGET_AREA_OF_INTEREST_METADATA = {
|
|
|
4937
5093
|
},
|
|
4938
5094
|
drawRule: "Area1",
|
|
4939
5095
|
capturesLabelSize: true,
|
|
4940
|
-
params:
|
|
5096
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
4941
5097
|
textAmplifiers: AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
|
|
4942
5098
|
};
|
|
4943
5099
|
/**
|
|
@@ -5189,7 +5345,7 @@ const BASE_CAMP_METADATA = {
|
|
|
5189
5345
|
},
|
|
5190
5346
|
drawRule: "Area1",
|
|
5191
5347
|
capturesLabelSize: true,
|
|
5192
|
-
params:
|
|
5348
|
+
params: PREFIXED_ECHELON_AREA_PARAMS,
|
|
5193
5349
|
textAmplifiers: AREA_TEXT_AMPLIFIERS
|
|
5194
5350
|
};
|
|
5195
5351
|
/**
|
|
@@ -5247,7 +5403,7 @@ const GUERRILLA_BASE_METADATA = {
|
|
|
5247
5403
|
},
|
|
5248
5404
|
drawRule: "Area1",
|
|
5249
5405
|
capturesLabelSize: true,
|
|
5250
|
-
params:
|
|
5406
|
+
params: PREFIXED_ECHELON_AREA_PARAMS,
|
|
5251
5407
|
textAmplifiers: AREA_TEXT_AMPLIFIERS
|
|
5252
5408
|
};
|
|
5253
5409
|
/**
|
|
@@ -5365,7 +5521,7 @@ const ASSEMBLY_AREA_METADATA = {
|
|
|
5365
5521
|
},
|
|
5366
5522
|
drawRule: "Area1",
|
|
5367
5523
|
capturesLabelSize: true,
|
|
5368
|
-
params:
|
|
5524
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
5369
5525
|
textAmplifiers: AREA_TEXT_AMPLIFIERS
|
|
5370
5526
|
};
|
|
5371
5527
|
/**
|
|
@@ -5856,7 +6012,8 @@ const ATTACK_BY_FIRE = defineControlMeasure({
|
|
|
5856
6012
|
//#endregion
|
|
5857
6013
|
//#region src/generators/cm15-maneuver-areas/attackHelicopter.ts
|
|
5858
6014
|
const DEFAULT_ATTACK_HELICOPTER_OPTIONS = {
|
|
5859
|
-
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO,
|
|
6015
|
+
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO$1,
|
|
6016
|
+
rearWidthRatio: DEFAULT_REAR_WIDTH_RATIO,
|
|
5860
6017
|
smooth: false,
|
|
5861
6018
|
smoothResolution: 5,
|
|
5862
6019
|
symbolHeightRatio: .45,
|
|
@@ -6075,7 +6232,8 @@ const ATTACK_HELICOPTER = defineControlMeasure({
|
|
|
6075
6232
|
metadata: ATTACK_HELICOPTER_METADATA,
|
|
6076
6233
|
generator: createAttackHelicopter,
|
|
6077
6234
|
defaultOptions: DEFAULT_ATTACK_HELICOPTER_OPTIONS,
|
|
6078
|
-
rule: axis1DrawRule
|
|
6235
|
+
rule: axis1DrawRule,
|
|
6236
|
+
optionHandles: variableWidthAttackOptionHandles
|
|
6079
6237
|
});
|
|
6080
6238
|
//#endregion
|
|
6081
6239
|
//#region src/generators/cm15-maneuver-areas/battlePosition.ts
|
|
@@ -6133,10 +6291,7 @@ function battlePositionAreaFeatures(positions, options, textAmplifiers, context,
|
|
|
6133
6291
|
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$6);
|
|
6134
6292
|
const { strokes, fills, gaps } = placeEchelon(buildAreaPerimeter(verts), h, echelon, echelonPadding, echelonPosition, echelonAnchor);
|
|
6135
6293
|
const labelFeatures = [];
|
|
6136
|
-
const enyGaps = pushAreaLabels(labelFeatures, verts,
|
|
6137
|
-
name: variant.namePrefix ? composeAreaName(variant.namePrefix, textAmplifiers.T) : textAmplifiers.T,
|
|
6138
|
-
hostile: textAmplifiers.N
|
|
6139
|
-
}, options, context.amplifierPlacements, void 0, context);
|
|
6294
|
+
const enyGaps = pushAreaLabels(labelFeatures, verts, composeAreaLabelTexts(textAmplifiers, variant.namePrefix), options, context.amplifierPlacements, void 0, context);
|
|
6140
6295
|
const boundaryCoords = buildGappedLine(verts, [...gaps, ...enyGaps]);
|
|
6141
6296
|
const features = [];
|
|
6142
6297
|
if (boundaryCoords.length > 0) features.push({
|
|
@@ -6257,7 +6412,7 @@ const BATTLE_POSITION_PREPARED_METADATA = {
|
|
|
6257
6412
|
},
|
|
6258
6413
|
drawRule: "Area1",
|
|
6259
6414
|
capturesLabelSize: true,
|
|
6260
|
-
params:
|
|
6415
|
+
params: PREFIXED_ECHELON_AREA_PARAMS,
|
|
6261
6416
|
textAmplifiers: AREA_UNIT_DESIGNATION_HOSTILE_AMPLIFIERS
|
|
6262
6417
|
};
|
|
6263
6418
|
/**
|
|
@@ -6710,8 +6865,12 @@ const BRIDGE_OR_GAP = defineControlMeasure({
|
|
|
6710
6865
|
const DEFAULT_SMOOTH_RESOLUTION$5 = 12;
|
|
6711
6866
|
const MIN_SMOOTH_RESOLUTION$1 = 2;
|
|
6712
6867
|
const MAX_SMOOTH_RESOLUTION$1 = 64;
|
|
6868
|
+
const DEFAULT_SHAFT_WIDTH_RATIO = .06;
|
|
6869
|
+
const MIN_SHAFT_WIDTH_RATIO = .01;
|
|
6870
|
+
const MAX_SHAFT_WIDTH_RATIO = .3;
|
|
6713
6871
|
const DEFAULT_BLOCK_ARROW_OPTIONS = {
|
|
6714
|
-
shaftWidthRatio:
|
|
6872
|
+
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO,
|
|
6873
|
+
rearWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO,
|
|
6715
6874
|
arrowheadStyle: "triangle",
|
|
6716
6875
|
arrowheadWidthRatio: .18,
|
|
6717
6876
|
arrowheadLengthRatio: .22,
|
|
@@ -6743,8 +6902,18 @@ const BLOCK_ARROW_METADATA = {
|
|
|
6743
6902
|
label: "Shaft width",
|
|
6744
6903
|
description: "Shaft band width as a fraction of the total path length",
|
|
6745
6904
|
type: "number",
|
|
6746
|
-
min:
|
|
6747
|
-
max:
|
|
6905
|
+
min: MIN_SHAFT_WIDTH_RATIO,
|
|
6906
|
+
max: MAX_SHAFT_WIDTH_RATIO,
|
|
6907
|
+
step: .01
|
|
6908
|
+
},
|
|
6909
|
+
{
|
|
6910
|
+
key: "rearWidthRatio",
|
|
6911
|
+
presentationTier: "advanced",
|
|
6912
|
+
label: "Rear width",
|
|
6913
|
+
description: "Width at the rear of the shaft as a fraction of the total path length",
|
|
6914
|
+
type: "number",
|
|
6915
|
+
min: MIN_SHAFT_WIDTH_RATIO,
|
|
6916
|
+
max: MAX_SHAFT_WIDTH_RATIO,
|
|
6748
6917
|
step: .01
|
|
6749
6918
|
},
|
|
6750
6919
|
{
|
|
@@ -6835,25 +7004,42 @@ const BLOCK_ARROW_METADATA = {
|
|
|
6835
7004
|
* right side of the shaft.
|
|
6836
7005
|
*/
|
|
6837
7006
|
function createBlockArrow(coordinates, options = {}) {
|
|
6838
|
-
const
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
};
|
|
6842
|
-
const axis1 = axis1Geometry(coordinates);
|
|
6843
|
-
const axis = arrowAxis(axis1.path);
|
|
6844
|
-
if (!axis) return {
|
|
7007
|
+
const resolved = resolveBlockArrowOptions(options);
|
|
7008
|
+
const geometry = calculateBlockArrowGeometry(coordinates, resolved);
|
|
7009
|
+
if (!geometry) return {
|
|
6845
7010
|
type: "FeatureCollection",
|
|
6846
7011
|
features: []
|
|
6847
7012
|
};
|
|
7013
|
+
const { ring } = geometry;
|
|
7014
|
+
return {
|
|
7015
|
+
type: "FeatureCollection",
|
|
7016
|
+
features: [{
|
|
7017
|
+
type: "Feature",
|
|
7018
|
+
properties: {
|
|
7019
|
+
part: "body",
|
|
7020
|
+
fill: resolved.filled
|
|
7021
|
+
},
|
|
7022
|
+
geometry: {
|
|
7023
|
+
type: "Polygon",
|
|
7024
|
+
coordinates: [ring.map((p) => unproject(p[0], p[1]))]
|
|
7025
|
+
}
|
|
7026
|
+
}]
|
|
7027
|
+
};
|
|
7028
|
+
}
|
|
7029
|
+
function calculateBlockArrowGeometry(coordinates, options) {
|
|
7030
|
+
const axis1 = axis1Geometry(coordinates);
|
|
7031
|
+
const axis = arrowAxis(axis1.path);
|
|
7032
|
+
if (!axis) return null;
|
|
6848
7033
|
const { pts, pathLength, tip, dir, perp, segLength } = axis;
|
|
6849
|
-
const requestedHeadLen = axis1.headLength ?? pathLength * arrowheadLengthRatio;
|
|
7034
|
+
const requestedHeadLen = axis1.headLength ?? pathLength * options.arrowheadLengthRatio;
|
|
6850
7035
|
const headLen = Math.min(requestedHeadLen, segLength * .95);
|
|
6851
|
-
const halfShaft = pathLength * shaftWidthRatio / 2;
|
|
6852
|
-
const
|
|
7036
|
+
const halfShaft = pathLength * options.shaftWidthRatio / 2;
|
|
7037
|
+
const halfRear = pathLength * options.rearWidthRatio / 2;
|
|
7038
|
+
const halfHead = axis1.headHalfWidth ?? pathLength * options.arrowheadWidthRatio / 2;
|
|
6853
7039
|
const onAxis = (d, side = 0) => [tip[0] - dir[0] * d + perp[0] * side, tip[1] - dir[1] * d + perp[1] * side];
|
|
6854
7040
|
let shaftEndDist;
|
|
6855
7041
|
let headPts;
|
|
6856
|
-
switch (arrowheadStyle) {
|
|
7042
|
+
switch (options.arrowheadStyle) {
|
|
6857
7043
|
case "barbed":
|
|
6858
7044
|
shaftEndDist = headLen * .55;
|
|
6859
7045
|
headPts = [
|
|
@@ -6911,9 +7097,9 @@ function createBlockArrow(coordinates, options = {}) {
|
|
|
6911
7097
|
break;
|
|
6912
7098
|
}
|
|
6913
7099
|
let spine = [...pts.slice(0, -1), onAxis(shaftEndDist)];
|
|
6914
|
-
if (smooth) spine = catmullRom(spine, normalizeSmoothResolution$1(smoothResolution));
|
|
6915
|
-
const
|
|
6916
|
-
const rightSide =
|
|
7100
|
+
if (options.smooth) spine = catmullRom(spine, normalizeSmoothResolution$1(options.smoothResolution));
|
|
7101
|
+
const widths = interpolatePolylineValues(spine, halfRear, halfShaft);
|
|
7102
|
+
const { left: leftSide, right: rightSide } = offsetPolylineSides(spine, widths);
|
|
6917
7103
|
const ring = [
|
|
6918
7104
|
...leftSide,
|
|
6919
7105
|
...headPts,
|
|
@@ -6921,18 +7107,20 @@ function createBlockArrow(coordinates, options = {}) {
|
|
|
6921
7107
|
];
|
|
6922
7108
|
ring.push(ring[0]);
|
|
6923
7109
|
return {
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
7110
|
+
ring,
|
|
7111
|
+
spine,
|
|
7112
|
+
leftSide,
|
|
7113
|
+
pathLength
|
|
7114
|
+
};
|
|
7115
|
+
}
|
|
7116
|
+
function resolveBlockArrowOptions(options) {
|
|
7117
|
+
const resolved = {
|
|
7118
|
+
...DEFAULT_BLOCK_ARROW_OPTIONS,
|
|
7119
|
+
...options
|
|
7120
|
+
};
|
|
7121
|
+
return {
|
|
7122
|
+
...resolved,
|
|
7123
|
+
rearWidthRatio: resolveRearWidthRatio(options, resolved.shaftWidthRatio)
|
|
6936
7124
|
};
|
|
6937
7125
|
}
|
|
6938
7126
|
const BLOCK_ARROW = defineControlMeasure({
|
|
@@ -6940,6 +7128,20 @@ const BLOCK_ARROW = defineControlMeasure({
|
|
|
6940
7128
|
generator: createBlockArrow,
|
|
6941
7129
|
defaultOptions: DEFAULT_BLOCK_ARROW_OPTIONS,
|
|
6942
7130
|
rule: axis1DrawRule,
|
|
7131
|
+
optionHandles: createWidthOptionHandles({
|
|
7132
|
+
geometry(controlPoints, options) {
|
|
7133
|
+
const geometry = calculateBlockArrowGeometry(controlPoints, resolveBlockArrowOptions(options));
|
|
7134
|
+
if (!geometry) return null;
|
|
7135
|
+
return {
|
|
7136
|
+
spine: geometry.spine,
|
|
7137
|
+
leftEdge: geometry.leftSide,
|
|
7138
|
+
referenceHalfWidth: geometry.pathLength / 2
|
|
7139
|
+
};
|
|
7140
|
+
},
|
|
7141
|
+
minRatio: MIN_SHAFT_WIDTH_RATIO,
|
|
7142
|
+
maxRearRatio: MAX_SHAFT_WIDTH_RATIO,
|
|
7143
|
+
maxShaftRatio: MAX_SHAFT_WIDTH_RATIO
|
|
7144
|
+
}),
|
|
6943
7145
|
previewSample: {
|
|
6944
7146
|
controlPoints: [
|
|
6945
7147
|
[1.4, 0],
|
|
@@ -9392,7 +9594,8 @@ const CLEAR = defineControlMeasure({
|
|
|
9392
9594
|
//#endregion
|
|
9393
9595
|
//#region src/generators/cm15-maneuver-areas/supportingAttack.ts
|
|
9394
9596
|
const DEFAULT_SUPPORTING_ATTACK_OPTIONS = {
|
|
9395
|
-
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO,
|
|
9597
|
+
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO$1,
|
|
9598
|
+
rearWidthRatio: DEFAULT_REAR_WIDTH_RATIO,
|
|
9396
9599
|
smooth: false,
|
|
9397
9600
|
smoothResolution: 5
|
|
9398
9601
|
};
|
|
@@ -9464,12 +9667,14 @@ const SUPPORTING_ATTACK = defineControlMeasure({
|
|
|
9464
9667
|
metadata: SUPPORTING_ATTACK_METADATA,
|
|
9465
9668
|
generator: createSupportingAttack,
|
|
9466
9669
|
defaultOptions: DEFAULT_SUPPORTING_ATTACK_OPTIONS,
|
|
9467
|
-
rule: axis1DrawRule
|
|
9670
|
+
rule: axis1DrawRule,
|
|
9671
|
+
optionHandles: variableWidthAttackOptionHandles
|
|
9468
9672
|
});
|
|
9469
9673
|
//#endregion
|
|
9470
9674
|
//#region src/generators/cm34-mission-tasks/counterattack.ts
|
|
9471
9675
|
const DEFAULT_COUNTERATTACK_OPTIONS = {
|
|
9472
|
-
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO,
|
|
9676
|
+
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO$1,
|
|
9677
|
+
rearWidthRatio: DEFAULT_REAR_WIDTH_RATIO,
|
|
9473
9678
|
smooth: false,
|
|
9474
9679
|
smoothResolution: 5,
|
|
9475
9680
|
labelPosition: 1
|
|
@@ -9554,6 +9759,7 @@ const COUNTERATTACK = defineControlMeasure({
|
|
|
9554
9759
|
generator: createCounterattack,
|
|
9555
9760
|
defaultOptions: DEFAULT_COUNTERATTACK_OPTIONS,
|
|
9556
9761
|
rule: axis1DrawRule,
|
|
9762
|
+
optionHandles: variableWidthAttackOptionHandles,
|
|
9557
9763
|
previewSample: {
|
|
9558
9764
|
controlPoints: [
|
|
9559
9765
|
[1, 0],
|
|
@@ -9714,6 +9920,7 @@ const COUNTERATTACK_BY_FIRE = defineControlMeasure({
|
|
|
9714
9920
|
generator: createCounterattackByFire,
|
|
9715
9921
|
defaultOptions: DEFAULT_COUNTERATTACK_BY_FIRE_OPTIONS,
|
|
9716
9922
|
rule: counterattackByFireDrawRule,
|
|
9923
|
+
optionHandles: createVariableWidthAttackOptionHandles(counterattackByFireBodyCoordinates),
|
|
9717
9924
|
previewSample: {
|
|
9718
9925
|
controlPoints: [
|
|
9719
9926
|
[1.35, 0],
|
|
@@ -11061,7 +11268,7 @@ const DROP_ZONE_METADATA = {
|
|
|
11061
11268
|
},
|
|
11062
11269
|
drawRule: "Area1",
|
|
11063
11270
|
capturesLabelSize: true,
|
|
11064
|
-
params:
|
|
11271
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
11065
11272
|
textAmplifiers: AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
|
|
11066
11273
|
};
|
|
11067
11274
|
/**
|
|
@@ -11278,7 +11485,7 @@ const EXTRACTION_ZONE_METADATA = {
|
|
|
11278
11485
|
},
|
|
11279
11486
|
drawRule: "Area1",
|
|
11280
11487
|
capturesLabelSize: true,
|
|
11281
|
-
params:
|
|
11488
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
11282
11489
|
textAmplifiers: AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
|
|
11283
11490
|
};
|
|
11284
11491
|
/**
|
|
@@ -13153,7 +13360,8 @@ const FORTIFIED_AREA = defineControlMeasure({
|
|
|
13153
13360
|
//#endregion
|
|
13154
13361
|
//#region src/generators/cm15-maneuver-areas/maneuver-arrow-task-shared.ts
|
|
13155
13362
|
const DEFAULT_MANEUVER_ARROW_TASK_OPTIONS = {
|
|
13156
|
-
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO,
|
|
13363
|
+
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO$1,
|
|
13364
|
+
rearWidthRatio: DEFAULT_REAR_WIDTH_RATIO,
|
|
13157
13365
|
smooth: false,
|
|
13158
13366
|
smoothResolution: 5,
|
|
13159
13367
|
crossbarLengthRatio: 1.1,
|
|
@@ -13162,10 +13370,14 @@ const DEFAULT_MANEUVER_ARROW_TASK_OPTIONS = {
|
|
|
13162
13370
|
labelPadding: 0
|
|
13163
13371
|
};
|
|
13164
13372
|
function createManeuverArrowTask(coordinates, options, textAmplifiers, config) {
|
|
13165
|
-
const
|
|
13373
|
+
const merged = {
|
|
13166
13374
|
...DEFAULT_MANEUVER_ARROW_TASK_OPTIONS,
|
|
13167
13375
|
...options
|
|
13168
13376
|
};
|
|
13377
|
+
const resolved = {
|
|
13378
|
+
...merged,
|
|
13379
|
+
rearWidthRatio: resolveRearWidthRatio(options, merged.shaftWidthRatio)
|
|
13380
|
+
};
|
|
13169
13381
|
const { geometry } = processAttackGeometry(coordinates, resolved);
|
|
13170
13382
|
if (!geometry) return {
|
|
13171
13383
|
type: "FeatureCollection",
|
|
@@ -13186,17 +13398,22 @@ function createManeuverArrowTask(coordinates, options, textAmplifiers, config) {
|
|
|
13186
13398
|
};
|
|
13187
13399
|
const tipVector = vecSub(geometry.ptTip, geometry.ptBase);
|
|
13188
13400
|
const tipDirection = vecNorm(tipVector);
|
|
13189
|
-
const
|
|
13401
|
+
const headWidth = vecMag(vecSub(geometry.headRing[0], geometry.headRing[2]));
|
|
13190
13402
|
let crossbarCenter;
|
|
13191
13403
|
let crossbarAlong;
|
|
13404
|
+
let crossbarBaseWidth = headWidth;
|
|
13192
13405
|
if (config.crossbarAt === "tip") {
|
|
13193
13406
|
crossbarCenter = geometry.ptTip;
|
|
13194
13407
|
crossbarAlong = tipDirection;
|
|
13195
13408
|
} else {
|
|
13196
|
-
const
|
|
13409
|
+
const shaftPosition = Number.isFinite(config.crossbarAt.shaftPosition) ? clamp01(config.crossbarAt.shaftPosition) : 0;
|
|
13410
|
+
const crossbarFrame = pointAlongPolyline(segments, totalLength, shaftPosition);
|
|
13197
13411
|
crossbarCenter = crossbarFrame.point;
|
|
13198
13412
|
crossbarAlong = crossbarFrame.along;
|
|
13413
|
+
const rearWidth = vecMag(vecSub(geometry.shaftLeft[0], geometry.shaftRight[0]));
|
|
13414
|
+
crossbarBaseWidth = rearWidth + (vecMag(vecSub(geometry.shaftLeft.at(-1), geometry.shaftRight.at(-1))) - rearWidth) * shaftPosition;
|
|
13199
13415
|
}
|
|
13416
|
+
const crossbarLength = crossbarBaseWidth * Math.max(1, resolved.crossbarLengthRatio);
|
|
13200
13417
|
const crossbarPerp = [-crossbarAlong[1], crossbarAlong[0]];
|
|
13201
13418
|
const crossbar = [vecAdd(crossbarCenter, vecScale(crossbarPerp, crossbarLength / 2)), vecSub(crossbarCenter, vecScale(crossbarPerp, crossbarLength / 2))];
|
|
13202
13419
|
const features = [{
|
|
@@ -13336,6 +13553,7 @@ const FRONTAL_ATTACK = defineControlMeasure({
|
|
|
13336
13553
|
generator: createFrontalAttack,
|
|
13337
13554
|
defaultOptions: DEFAULT_FRONTAL_ATTACK_OPTIONS,
|
|
13338
13555
|
rule: axis1DrawRule,
|
|
13556
|
+
optionHandles: variableWidthAttackOptionHandles,
|
|
13339
13557
|
previewSample: {
|
|
13340
13558
|
controlPoints: [
|
|
13341
13559
|
[1, 0],
|
|
@@ -14040,7 +14258,7 @@ const JOINT_TACTICAL_ACTION_AREA_METADATA = {
|
|
|
14040
14258
|
},
|
|
14041
14259
|
drawRule: "Area1",
|
|
14042
14260
|
capturesLabelSize: true,
|
|
14043
|
-
params:
|
|
14261
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
14044
14262
|
textAmplifiers: AREA_TEXT_AMPLIFIERS
|
|
14045
14263
|
};
|
|
14046
14264
|
/**
|
|
@@ -14100,7 +14318,7 @@ const LANDING_ZONE_METADATA = {
|
|
|
14100
14318
|
},
|
|
14101
14319
|
drawRule: "Area1",
|
|
14102
14320
|
capturesLabelSize: true,
|
|
14103
|
-
params:
|
|
14321
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
14104
14322
|
textAmplifiers: AREA_TEXT_AMPLIFIERS
|
|
14105
14323
|
};
|
|
14106
14324
|
/**
|
|
@@ -14373,7 +14591,8 @@ const NO_FIRE_AREA_IRREGULAR = defineControlMeasure({
|
|
|
14373
14591
|
//#endregion
|
|
14374
14592
|
//#region src/generators/cm15-maneuver-areas/mainAttack.ts
|
|
14375
14593
|
const DEFAULT_MAIN_ATTACK_OPTIONS = {
|
|
14376
|
-
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO,
|
|
14594
|
+
shaftWidthRatio: DEFAULT_SHAFT_WIDTH_RATIO$1,
|
|
14595
|
+
rearWidthRatio: DEFAULT_REAR_WIDTH_RATIO,
|
|
14377
14596
|
smooth: false,
|
|
14378
14597
|
smoothResolution: 5
|
|
14379
14598
|
};
|
|
@@ -14401,7 +14620,7 @@ const MAIN_ATTACK_METADATA = {
|
|
|
14401
14620
|
*
|
|
14402
14621
|
* The symbol consists of a single MultiLineString feature containing:
|
|
14403
14622
|
* 1. **Arrowhead**: A line forming a "chevron" or "roof" shape.
|
|
14404
|
-
* 2. **Shaft**: Two
|
|
14623
|
+
* 2. **Shaft**: Two boundary lines behind the arrow, optionally flared toward the rear.
|
|
14405
14624
|
*
|
|
14406
14625
|
* The shaft is calculated to terminate exactly where it touches the inner walls
|
|
14407
14626
|
* of the arrowhead, creating a seamless connection.
|
|
@@ -14440,7 +14659,8 @@ const MAIN_ATTACK = defineControlMeasure({
|
|
|
14440
14659
|
metadata: MAIN_ATTACK_METADATA,
|
|
14441
14660
|
generator: createMainAttack,
|
|
14442
14661
|
defaultOptions: DEFAULT_MAIN_ATTACK_OPTIONS,
|
|
14443
|
-
rule: axis1DrawRule
|
|
14662
|
+
rule: axis1DrawRule,
|
|
14663
|
+
optionHandles: variableWidthAttackOptionHandles
|
|
14444
14664
|
});
|
|
14445
14665
|
//#endregion
|
|
14446
14666
|
//#region src/generators/cm27-protection-areas/mine-types.ts
|
|
@@ -15398,7 +15618,7 @@ const PICKUP_ZONE_METADATA = {
|
|
|
15398
15618
|
},
|
|
15399
15619
|
drawRule: "Area1",
|
|
15400
15620
|
capturesLabelSize: true,
|
|
15401
|
-
params:
|
|
15621
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
15402
15622
|
textAmplifiers: AREA_TEXT_AMPLIFIERS
|
|
15403
15623
|
};
|
|
15404
15624
|
/**
|
|
@@ -15453,7 +15673,7 @@ const ASSAULT_POSITION_METADATA = {
|
|
|
15453
15673
|
},
|
|
15454
15674
|
drawRule: "Area1",
|
|
15455
15675
|
capturesLabelSize: true,
|
|
15456
|
-
params:
|
|
15676
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
15457
15677
|
textAmplifiers: AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
|
|
15458
15678
|
};
|
|
15459
15679
|
/**
|
|
@@ -15508,7 +15728,7 @@ const ATTACK_POSITION_METADATA = {
|
|
|
15508
15728
|
},
|
|
15509
15729
|
drawRule: "Area1",
|
|
15510
15730
|
capturesLabelSize: true,
|
|
15511
|
-
params:
|
|
15731
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
15512
15732
|
textAmplifiers: AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
|
|
15513
15733
|
};
|
|
15514
15734
|
/**
|
|
@@ -15563,7 +15783,7 @@ const OBJECTIVE_AREA_METADATA = {
|
|
|
15563
15783
|
},
|
|
15564
15784
|
drawRule: "Area1",
|
|
15565
15785
|
capturesLabelSize: true,
|
|
15566
|
-
params:
|
|
15786
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
15567
15787
|
textAmplifiers: AREA_TEXT_AMPLIFIERS_DESIGNATION_HOSTILE
|
|
15568
15788
|
};
|
|
15569
15789
|
/**
|
|
@@ -16126,7 +16346,7 @@ const SUBMARINE_ACTION_AREA_METADATA = {
|
|
|
16126
16346
|
},
|
|
16127
16347
|
drawRule: "Area1",
|
|
16128
16348
|
capturesLabelSize: true,
|
|
16129
|
-
params:
|
|
16349
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
16130
16350
|
textAmplifiers: AREA_TEXT_AMPLIFIERS_NO_ADDITIONAL_INFO
|
|
16131
16351
|
};
|
|
16132
16352
|
/**
|
|
@@ -16186,7 +16406,7 @@ const SUBMARINE_GENERATED_ACTION_AREA_METADATA = {
|
|
|
16186
16406
|
},
|
|
16187
16407
|
drawRule: "Area1",
|
|
16188
16408
|
capturesLabelSize: true,
|
|
16189
|
-
params:
|
|
16409
|
+
params: PREFIXED_LABELED_AREA_PARAMS,
|
|
16190
16410
|
textAmplifiers: AREA_TEXT_AMPLIFIERS_NO_ADDITIONAL_INFO
|
|
16191
16411
|
};
|
|
16192
16412
|
/**
|
|
@@ -16659,6 +16879,7 @@ const DEFINITIONS = {
|
|
|
16659
16879
|
generator: createTurningMovement,
|
|
16660
16880
|
defaultOptions: DEFAULT_TURNING_MOVEMENT_OPTIONS,
|
|
16661
16881
|
rule: axis1DrawRule,
|
|
16882
|
+
optionHandles: variableWidthAttackOptionHandles,
|
|
16662
16883
|
previewSample: {
|
|
16663
16884
|
controlPoints: [
|
|
16664
16885
|
[1, 0],
|