@orbat-mapper/tactical-draw 0.12.0 → 0.13.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.mjs +40 -12
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -3164,6 +3164,17 @@ function runEditInteraction(spec) {
|
|
|
3164
3164
|
}
|
|
3165
3165
|
//#endregion
|
|
3166
3166
|
//#region src/tactical-draw/handles.ts
|
|
3167
|
+
/** Size of a `compact` option handle relative to its slot's style. */
|
|
3168
|
+
const COMPACT_HANDLE_SCALE = 2 / 3;
|
|
3169
|
+
/** Shrink a handle style's radius and stroke for a `compact` handle. */
|
|
3170
|
+
function compactHandleStyle(style) {
|
|
3171
|
+
const { pointRadius, strokeWidth } = style;
|
|
3172
|
+
return {
|
|
3173
|
+
...style,
|
|
3174
|
+
...pointRadius !== void 0 && { pointRadius: pointRadius * COMPACT_HANDLE_SCALE },
|
|
3175
|
+
...strokeWidth !== void 0 && { strokeWidth: strokeWidth * COMPACT_HANDLE_SCALE }
|
|
3176
|
+
};
|
|
3177
|
+
}
|
|
3167
3178
|
/**
|
|
3168
3179
|
* Midpoint of the segment between two positions, in lonLat. The single source
|
|
3169
3180
|
* for "where a midpoint handle sits": `buildHandleSet` places the handle here
|
|
@@ -3249,7 +3260,8 @@ function handleStyleFor(kind, interactionStyle, unset = false) {
|
|
|
3249
3260
|
*/
|
|
3250
3261
|
function toFeatures(handles, featureIdPrefix, interactionStyle) {
|
|
3251
3262
|
return handles.map((handle) => {
|
|
3252
|
-
const
|
|
3263
|
+
const slotStyle = handleStyleFor(handle.kind, interactionStyle, handle.unset);
|
|
3264
|
+
const style = slotStyle && handle.compact ? compactHandleStyle(slotStyle) : slotStyle;
|
|
3253
3265
|
return {
|
|
3254
3266
|
type: "Feature",
|
|
3255
3267
|
id: handleFeatureId(featureIdPrefix, handle.kind, handle.index),
|
|
@@ -4365,6 +4377,7 @@ function runEdit(opts) {
|
|
|
4365
4377
|
const isAreaKind = metadata.geometry === "area";
|
|
4366
4378
|
const isTextGraphic = measure.kind === "text";
|
|
4367
4379
|
const foldsTransformIntoOptions = foldsBoxTransformOptions(measure.kind);
|
|
4380
|
+
const holdsAnchorOnFold = foldsTransformIntoOptions && metadata.maxCoordinates === 1;
|
|
4368
4381
|
const requestedModes = normaliseModes(initialModes);
|
|
4369
4382
|
const editStartPoints = measure.controlPoints.map((p) => [...p]);
|
|
4370
4383
|
const workingPoints = editStartPoints.map((p) => [...p]);
|
|
@@ -4523,7 +4536,7 @@ function runEdit(opts) {
|
|
|
4523
4536
|
const px = driver.toPixel(p);
|
|
4524
4537
|
if (px) pixels.push(px);
|
|
4525
4538
|
}
|
|
4526
|
-
return fitTransformBox(pixels,
|
|
4539
|
+
return fitTransformBox(pixels, anchoredOptionTransformInFlight() ? activeBoxDrag.lastSnapshot.box.angle : boxAngle, interactionStyle?.boxPadding ?? 0);
|
|
4527
4540
|
}
|
|
4528
4541
|
/**
|
|
4529
4542
|
* The Text footprint box (rotated rectangle hugging the rendered text),
|
|
@@ -4542,12 +4555,12 @@ function runEdit(opts) {
|
|
|
4542
4555
|
* Re-fit `box` from `lastWorkingFeatures`. A no-op while a box gesture is
|
|
4543
4556
|
* in flight — that drag paints the box rigidly from its own snapshot
|
|
4544
4557
|
* (`BoxDragSnapshot.box`) instead of a re-fit, so a corner/rotate/body drag
|
|
4545
|
-
* doesn't fight its own paint.
|
|
4546
|
-
* their anchor points stay fixed while their rendered footprint changes, so
|
|
4558
|
+
* doesn't fight its own paint. Single-anchor option-backed transforms are the
|
|
4559
|
+
* exception: their anchor points stay fixed while their rendered footprint changes, so
|
|
4547
4560
|
* the box must re-fit from that live render on every tick.
|
|
4548
4561
|
*/
|
|
4549
4562
|
function refitBox() {
|
|
4550
|
-
if (activeBoxDrag && !
|
|
4563
|
+
if (activeBoxDrag && !anchoredOptionTransformInFlight()) return;
|
|
4551
4564
|
box = fitBoxFromFeatures(lastWorkingFeatures);
|
|
4552
4565
|
}
|
|
4553
4566
|
function renderWorkingGraphic() {
|
|
@@ -4596,7 +4609,7 @@ function runEdit(opts) {
|
|
|
4596
4609
|
const features = toFeatures(buildCurrentHandleSet(), handleIdPrefix, interactionStyle);
|
|
4597
4610
|
if (modes.includes("labeldrag")) features.push(...labels.handleFeatures());
|
|
4598
4611
|
if (modes.includes("transform")) {
|
|
4599
|
-
const liveBox = activeBoxDrag && !
|
|
4612
|
+
const liveBox = activeBoxDrag && !anchoredOptionTransformInFlight() ? activeBoxDrag.lastSnapshot.box : box;
|
|
4600
4613
|
if (liveBox) {
|
|
4601
4614
|
features.push(boxOutlineFeature(liveBox, dragProjection.fromPixel, `${handleIdPrefix}:box-outline`, interactionStyle?.boxOutline));
|
|
4602
4615
|
features.push(...toFeatures(boxToHandles(liveBox, dragProjection.fromPixel), handleIdPrefix, interactionStyle));
|
|
@@ -4608,6 +4621,11 @@ function runEdit(opts) {
|
|
|
4608
4621
|
iconResources: []
|
|
4609
4622
|
}]);
|
|
4610
4623
|
}
|
|
4624
|
+
/** Live view scale for option handles behind pixel-authored sizes. */
|
|
4625
|
+
function optionHandleContext() {
|
|
4626
|
+
const resolution = adapter.getResolution();
|
|
4627
|
+
return hasUsableResolution(resolution) ? { constructionMetersPerCssPixel: resolution } : {};
|
|
4628
|
+
}
|
|
4611
4629
|
function buildCurrentHandleSet() {
|
|
4612
4630
|
const handles = buildHandleSet(workingPoints, modes, {
|
|
4613
4631
|
includeMidpoints: midpointHandlesEnabled,
|
|
@@ -4616,7 +4634,10 @@ function runEdit(opts) {
|
|
|
4616
4634
|
closedRing
|
|
4617
4635
|
});
|
|
4618
4636
|
if (modes.includes("reshape")) {
|
|
4619
|
-
const optionHandles = getControlMeasureOptionHandles(measure.kind, workingPoints, workingOptions, {
|
|
4637
|
+
const optionHandles = getControlMeasureOptionHandles(measure.kind, workingPoints, workingOptions, {
|
|
4638
|
+
detail: detailHandles,
|
|
4639
|
+
...optionHandleContext()
|
|
4640
|
+
});
|
|
4620
4641
|
for (let index = 0; index < optionHandles.length; index++) {
|
|
4621
4642
|
const handle = optionHandles[index];
|
|
4622
4643
|
handles.push({
|
|
@@ -4624,7 +4645,8 @@ function runEdit(opts) {
|
|
|
4624
4645
|
index,
|
|
4625
4646
|
optionId: handle.id,
|
|
4626
4647
|
position: [...handle.position],
|
|
4627
|
-
...handle.unset ? { unset: true } : {}
|
|
4648
|
+
...handle.unset ? { unset: true } : {},
|
|
4649
|
+
...handle.compact ? { compact: true } : {}
|
|
4628
4650
|
});
|
|
4629
4651
|
}
|
|
4630
4652
|
}
|
|
@@ -5145,7 +5167,7 @@ function runEdit(opts) {
|
|
|
5145
5167
|
let activeDrag = null;
|
|
5146
5168
|
let activeOptionDrag = null;
|
|
5147
5169
|
function optionsForOptionHandle(position, drag) {
|
|
5148
|
-
const patch = dragControlMeasureOptionHandle(measure.kind, workingPoints, drag.preDragOptions, drag.handleId, position);
|
|
5170
|
+
const patch = dragControlMeasureOptionHandle(measure.kind, workingPoints, drag.preDragOptions, drag.handleId, position, optionHandleContext());
|
|
5149
5171
|
return patch ? {
|
|
5150
5172
|
...drag.preDragOptions ?? {},
|
|
5151
5173
|
...patch
|
|
@@ -5270,7 +5292,7 @@ function runEdit(opts) {
|
|
|
5270
5292
|
return;
|
|
5271
5293
|
}
|
|
5272
5294
|
if (hit.kind === "option" && hit.optionId && info?.altKey) {
|
|
5273
|
-
const patch = resetControlMeasureOptionHandle(measure.kind, workingPoints, workingOptions, hit.optionId);
|
|
5295
|
+
const patch = resetControlMeasureOptionHandle(measure.kind, workingPoints, workingOptions, hit.optionId, optionHandleContext());
|
|
5274
5296
|
if (patch) {
|
|
5275
5297
|
setOptions(patch, "gesture", "Reset option handle");
|
|
5276
5298
|
shell.suppressNextClickAway();
|
|
@@ -5396,6 +5418,10 @@ function runEdit(opts) {
|
|
|
5396
5418
|
const kind = activeBoxDrag.fsm.kind;
|
|
5397
5419
|
return kind === "rotate" || kind === "scale";
|
|
5398
5420
|
}
|
|
5421
|
+
/** Whether the live option-backed gesture also holds a single anchor fixed. */
|
|
5422
|
+
function anchoredOptionTransformInFlight() {
|
|
5423
|
+
return holdsAnchorOnFold && activeOptionTransformInFlight();
|
|
5424
|
+
}
|
|
5399
5425
|
function paintBoxDragSnapshot(pixel, info, constrainRotation = false) {
|
|
5400
5426
|
if (!activeBoxDrag) return;
|
|
5401
5427
|
const resolvedPixel = translatedBoxPixel(pixel, info, activeBoxDrag);
|
|
@@ -5403,9 +5429,11 @@ function runEdit(opts) {
|
|
|
5403
5429
|
activeBoxDrag.lastSnapshot = snapshot;
|
|
5404
5430
|
if (activeOptionTransformInFlight()) {
|
|
5405
5431
|
const foldedOptions = foldOptionTransform(activeBoxDrag.preDragBox, snapshot.box, activeBoxDrag.fsm.kind, activeBoxDrag.preDragOptions);
|
|
5406
|
-
workingPoints.splice(0, workingPoints.length, ...activeBoxDrag.preDragPoints);
|
|
5407
5432
|
if (foldedOptions) workingOptions = foldedOptions;
|
|
5408
5433
|
nonGeometryDirty = true;
|
|
5434
|
+
}
|
|
5435
|
+
if (anchoredOptionTransformInFlight()) {
|
|
5436
|
+
workingPoints.splice(0, workingPoints.length, ...activeBoxDrag.preDragPoints);
|
|
5409
5437
|
renderWorkingGraphic();
|
|
5410
5438
|
renderHandlesLayer();
|
|
5411
5439
|
recomputeDirty();
|
|
@@ -5528,7 +5556,7 @@ function runEdit(opts) {
|
|
|
5528
5556
|
if (foldedOptions) workingOptions = foldedOptions;
|
|
5529
5557
|
nonGeometryDirty = true;
|
|
5530
5558
|
}
|
|
5531
|
-
const committedPoints = optionFold ? preDragPoints : finalSnapshot.points;
|
|
5559
|
+
const committedPoints = optionFold && holdsAnchorOnFold ? preDragPoints : finalSnapshot.points;
|
|
5532
5560
|
emitTransient("end", committedPoints, null);
|
|
5533
5561
|
commitGesture(committedPoints, preDragOptions, transformAmplifierPlacements(preDragAmplifierPlacements, preDragBox, finalSnapshot.box, driver), preDragBox.angle);
|
|
5534
5562
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orbat-mapper/tactical-draw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "Engine-agnostic draw/edit core for tactical control measures (MIL-STD-2525 / APP-6). The MapAdapter ABI and bring-your-own-engine entry point for the tactical-draw package family.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Orbat Mapper",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@orbat-mapper/control-measures": "^0.
|
|
42
|
+
"@orbat-mapper/control-measures": "^0.31.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"vitest": ">=4.0.0"
|