@orbat-mapper/tactical-draw 0.14.1 → 0.15.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.d.mts CHANGED
@@ -699,6 +699,8 @@ interface InteractionStyle {
699
699
  * `optionHandle` when unset.
700
700
  */
701
701
  unsetOptionHandle?: HandleStyle;
702
+ /** Draw-rule handle that reshapes control points without being one, such as Sector's rotate grip. */
703
+ geometryHandle?: HandleStyle;
702
704
  /** Visible grab target for a draggable rendered text label. */
703
705
  labelHandle?: HandleStyle;
704
706
  scaleHandle?: HandleStyle;
package/dist/index.mjs CHANGED
@@ -2635,13 +2635,14 @@ function runFixedLengthDraw(options) {
2635
2635
  return;
2636
2636
  }
2637
2637
  const previewThreshold = rule?.minimumPreviewPoints ?? required;
2638
- const features = buildPreviewFeatures$1(adapter, kind, rule && raw.length >= previewThreshold ? rule.derive(raw) : raw, measureOptions, measureStyle, measureTextAmplifiers, renderOpts);
2638
+ const points = rule && raw.length >= previewThreshold ? rule.derive(raw) : raw;
2639
+ const features = buildPreviewFeatures$1(adapter, kind, points, measureOptions, measureStyle, measureTextAmplifiers, renderOpts);
2639
2640
  ctx.writeLayer(previewLayer, [{
2640
2641
  id: PREVIEW_CM_ID$1,
2641
2642
  features,
2642
2643
  iconResources: []
2643
2644
  }]);
2644
- const guidePoints = rule?.guidePoints ? rule.guidePoints(raw) : raw;
2645
+ const guidePoints = rule?.guidePoints ? rule.guidePoints(points) : raw;
2645
2646
  const guideFeatures = guideEnabled ? buildGuideFeatures(guidePoints, isAreaKind) : [];
2646
2647
  ctx.writeLayer(guideLayer, [{
2647
2648
  id: GUIDE_GROUP_ID,
@@ -3285,6 +3286,7 @@ function handleFeatureId(prefix, kind, index) {
3285
3286
  case "vertex": return `${prefix}:handle-vertex:${index}`;
3286
3287
  case "midpoint": return `${prefix}:handle-midpoint:${index}`;
3287
3288
  case "option": return `${prefix}:handle-option:${index}`;
3289
+ case "geometry": return `${prefix}:handle-geometry:${index}`;
3288
3290
  case "scale": return `${prefix}:handle-scale:${index}`;
3289
3291
  case "rotate": return `${prefix}:handle-rotate`;
3290
3292
  }
@@ -3295,6 +3297,7 @@ function handleStyleFor(kind, interactionStyle, unset = false) {
3295
3297
  case "vertex": return interactionStyle?.vertexHandle;
3296
3298
  case "midpoint": return interactionStyle?.midpointHandle;
3297
3299
  case "option": return interactionStyle?.optionHandle;
3300
+ case "geometry": return interactionStyle?.geometryHandle;
3298
3301
  case "scale": return interactionStyle?.scaleHandle;
3299
3302
  case "rotate": return interactionStyle?.rotateHandle;
3300
3303
  }
@@ -3436,6 +3439,17 @@ function beginHandleDrag(args) {
3436
3439
  return move;
3437
3440
  };
3438
3441
  if (hit.kind === "vertex") return begun("vertex", preDragPoints, dragOnePoint(preDragPoints, hit.index));
3442
+ if (hit.kind === "geometry") {
3443
+ const geometryHandles = rule?.geometryHandles;
3444
+ const handleId = hit.geometryId;
3445
+ if (!geometryHandles || handleId === void 0) throw new TypeError("A geometry handle drag needs a rule with geometryHandles");
3446
+ return begun("geometry", preDragPoints, (pixel, modifiers) => ({ points: geometryHandles.drag({
3447
+ previous: preDragPoints,
3448
+ handleId,
3449
+ position: fromPixel(pixel),
3450
+ constrainAngles: modifiers?.constrainAngles
3451
+ }) }));
3452
+ }
3439
3453
  if (hit.kind !== "midpoint") throw new TypeError(`Unsupported control-point handle kind: ${hit.kind}`);
3440
3454
  const insertIndex = hit.index + 1;
3441
3455
  const segmentEndIndex = (hit.index + 1) % preDragPoints.length;
@@ -4671,6 +4685,13 @@ function runEdit(opts) {
4671
4685
  closedRing
4672
4686
  });
4673
4687
  if (modes.includes("reshape")) {
4688
+ const geometryHandles = rule?.geometryHandles?.get(workingPoints) ?? [];
4689
+ handles.push(...geometryHandles.map((handle, index) => ({
4690
+ kind: "geometry",
4691
+ index,
4692
+ geometryId: handle.id,
4693
+ position: [...handle.position]
4694
+ })));
4674
4695
  const optionHandles = getControlMeasureOptionHandles(measure.kind, workingPoints, workingOptions, {
4675
4696
  detail: detailHandles,
4676
4697
  ...optionHandleContext()
@@ -5217,7 +5238,7 @@ function runEdit(opts) {
5217
5238
  pixel,
5218
5239
  interaction,
5219
5240
  graphicId: measure.id,
5220
- controlPointIndex,
5241
+ controlPointIndex: controlPointIndex ?? void 0,
5221
5242
  originalEvent: info?.native
5222
5243
  })?.pixel ?? pixel;
5223
5244
  }
@@ -5359,7 +5380,7 @@ function runEdit(opts) {
5359
5380
  fsm,
5360
5381
  preDragPoints,
5361
5382
  preDragOptions: workingOptions,
5362
- controlPointIndex: hit.kind === "midpoint" ? hit.index + 1 : hit.index
5383
+ controlPointIndex: hit.kind === "geometry" ? null : hit.kind === "midpoint" ? hit.index + 1 : hit.index
5363
5384
  };
5364
5385
  emitTransient("start", fsm.kind === "midpoint" ? initialPoints : preDragPoints, activeDrag.controlPointIndex);
5365
5386
  if (fsm.kind === "midpoint") {
@@ -6225,6 +6246,7 @@ function runGroupTransform(opts) {
6225
6246
  optionsOverride: null,
6226
6247
  box: null,
6227
6248
  lastBoxPixels: [],
6249
+ lastFeatures: [],
6228
6250
  usesGraphicStack: isOnGraphicsLayer(graphic.id)
6229
6251
  };
6230
6252
  }
@@ -6241,6 +6263,7 @@ function runGroupTransform(opts) {
6241
6263
  optionsOverride: null,
6242
6264
  box: null,
6243
6265
  lastBoxPixels: [],
6266
+ lastFeatures: [],
6244
6267
  usesGraphicStack: isOnGraphicsLayer(graphic.id)
6245
6268
  };
6246
6269
  }
@@ -6418,6 +6441,15 @@ function runGroupTransform(opts) {
6418
6441
  return box ? boxCorners(box) : null;
6419
6442
  }
6420
6443
  /**
6444
+ * The pixels a measure member's box is fitted from. A Text member renders a
6445
+ * single Point; use its rotated-text footprint corners so the group box grows
6446
+ * to contain the text extent (else the lone Point contributes a degenerate,
6447
+ * unscalable box).
6448
+ */
6449
+ function measureBoxPixels(kind, features) {
6450
+ return (kind === "text" ? textFootprintCorners(features) : null) ?? pixelsFromFeatures(features);
6451
+ }
6452
+ /**
6421
6453
  * Fold a box gesture's delta (`box` vs the pre-drag box) into each measure
6422
6454
  * member that persists orientation/size as options (e.g. Text), off the
6423
6455
  * per-member `baseOverrides` so live ticks recompute rather than compound.
@@ -6524,13 +6556,14 @@ function runGroupTransform(opts) {
6524
6556
  } else {
6525
6557
  const working = workingGraphicFor(i);
6526
6558
  features = renderMeasureForAdapter(adapter, working, renderOpts).features;
6527
- boxPixels = (working.kind === "text" ? textFootprintCorners(features) : null) ?? pixelsFromFeatures(features);
6559
+ boxPixels = measureBoxPixels(working.kind, features);
6528
6560
  }
6529
6561
  } catch {
6530
6562
  features = [];
6531
6563
  boxPixels = [];
6532
6564
  }
6533
6565
  member.lastBoxPixels = boxPixels;
6566
+ member.lastFeatures = features;
6534
6567
  (member.usesGraphicStack ? workingGroups : previewGroups).push({
6535
6568
  id: member.graphic.id,
6536
6569
  features,
@@ -6546,7 +6579,10 @@ function runGroupTransform(opts) {
6546
6579
  function rescaleOnViewChange() {
6547
6580
  if (ctx.settled) return;
6548
6581
  if (members.some((m) => isAnchoredMember(m) || measureRendersFromViewState(m.graphic))) renderAllPreviews();
6549
- else refitBoxes();
6582
+ else {
6583
+ for (const member of members) member.lastBoxPixels = measureBoxPixels(member.graphic.kind, member.lastFeatures);
6584
+ refitBoxes();
6585
+ }
6550
6586
  renderHandlesLayer();
6551
6587
  }
6552
6588
  function renderHandlesLayer() {
@@ -9812,6 +9848,12 @@ const BUILT_IN_INTERACTION_STYLE = {
9812
9848
  strokeColor: "#f59e0b",
9813
9849
  strokeWidth: 2
9814
9850
  },
9851
+ geometryHandle: {
9852
+ pointRadius: 5,
9853
+ fillColor: "#ffffff",
9854
+ strokeColor: "#4f7cff",
9855
+ strokeWidth: 2
9856
+ },
9815
9857
  labelHandle: {
9816
9858
  pointRadius: 6,
9817
9859
  fillColor: "#7c3aed",
@@ -9836,6 +9878,7 @@ const HANDLE_SLOTS = [
9836
9878
  "midpointHandle",
9837
9879
  "optionHandle",
9838
9880
  "unsetOptionHandle",
9881
+ "geometryHandle",
9839
9882
  "labelHandle",
9840
9883
  "scaleHandle",
9841
9884
  "rotateHandle"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbat-mapper/tactical-draw",
3
- "version": "0.14.1",
3
+ "version": "0.15.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.33.0"
42
+ "@orbat-mapper/control-measures": "^0.34.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "vitest": ">=4.0.0"