@orbat-mapper/tactical-draw 0.10.0 → 0.10.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
@@ -1662,6 +1662,12 @@ declare class TacticalDraw {
1662
1662
  private transformCounter;
1663
1663
  /** Snapshot of the host's latest authoritative Graphic membership and order. */
1664
1664
  private lastRenderedMeasures;
1665
+ /**
1666
+ * Id index over {@link lastRenderedMeasures}, rebuilt alongside it so
1667
+ * per-id lookups (e.g. {@link renderHighlights}) are O(1) instead of an
1668
+ * O(document) linear scan on every highlighted id, every frame.
1669
+ */
1670
+ private lastRenderedMeasuresById;
1665
1671
  /** Whether any last-rendered graphic reads the whole camera, not just scale. */
1666
1672
  private lastRenderedHasViewDependent;
1667
1673
  private snapGeometryFeatures;
@@ -1703,6 +1709,15 @@ declare class TacticalDraw {
1703
1709
  * that removes outlines when the set transitions to empty.
1704
1710
  */
1705
1711
  private hasHighlightOutlines;
1712
+ /**
1713
+ * Coalesces bursts of {@link setHighlightedGraphics} (e.g. hover across a
1714
+ * list of rows) into at most one {@link renderHighlights} per animation
1715
+ * frame. Every other caller — `render()`, view-change, session start /
1716
+ * settle — needs highlight state updated synchronously and in step with
1717
+ * its own write, so those call {@link flushHighlights} instead, which
1718
+ * cancels any pending coalesced frame before rendering immediately.
1719
+ */
1720
+ private readonly highlightCoalescer;
1706
1721
  constructor(adapter: MapAdapter, options?: TacticalDrawOptions);
1707
1722
  setPinPresentation(defaults: PinPresentationPatch): void;
1708
1723
  setPinOverride(id: string, patch: PinOverridePatch): void;
@@ -1830,6 +1845,14 @@ declare class TacticalDraw {
1830
1845
  * "locked selection" chrome.
1831
1846
  */
1832
1847
  setHighlightedGraphics(ids: readonly string[]): void;
1848
+ /**
1849
+ * Cancel any pending coalesced highlight frame and re-project highlights
1850
+ * immediately. Used by every caller other than `setHighlightedGraphics`
1851
+ * (`render()`, view-change, session start / settle) so their own
1852
+ * synchronous effects and the highlight layer update together, with no
1853
+ * frame of lag and no stale coalesced call left to fire afterwards.
1854
+ */
1855
+ private flushHighlights;
1833
1856
  /**
1834
1857
  * Project the current {@link highlightedIds} into one outline `LineString`
1835
1858
  * per graphic and reconcile them onto the highlight layer. Idempotent and
package/dist/index.mjs CHANGED
@@ -7644,6 +7644,12 @@ var TacticalDraw = class {
7644
7644
  transformCounter = 0;
7645
7645
  /** Snapshot of the host's latest authoritative Graphic membership and order. */
7646
7646
  lastRenderedMeasures = [];
7647
+ /**
7648
+ * Id index over {@link lastRenderedMeasures}, rebuilt alongside it so
7649
+ * per-id lookups (e.g. {@link renderHighlights}) are O(1) instead of an
7650
+ * O(document) linear scan on every highlighted id, every frame.
7651
+ */
7652
+ lastRenderedMeasuresById = /* @__PURE__ */ new Map();
7647
7653
  /** Whether any last-rendered graphic reads the whole camera, not just scale. */
7648
7654
  lastRenderedHasViewDependent = false;
7649
7655
  snapGeometryFeatures = [];
@@ -7685,6 +7691,15 @@ var TacticalDraw = class {
7685
7691
  * that removes outlines when the set transitions to empty.
7686
7692
  */
7687
7693
  hasHighlightOutlines = false;
7694
+ /**
7695
+ * Coalesces bursts of {@link setHighlightedGraphics} (e.g. hover across a
7696
+ * list of rows) into at most one {@link renderHighlights} per animation
7697
+ * frame. Every other caller — `render()`, view-change, session start /
7698
+ * settle — needs highlight state updated synchronously and in step with
7699
+ * its own write, so those call {@link flushHighlights} instead, which
7700
+ * cancels any pending coalesced frame before rendering immediately.
7701
+ */
7702
+ highlightCoalescer = createFrameCoalescer(() => this.renderHighlights());
7688
7703
  constructor(adapter, options) {
7689
7704
  this.adapter = adapter;
7690
7705
  this.offsetDragModifier = options?.offsetDragModifier ?? false;
@@ -7717,7 +7732,7 @@ var TacticalDraw = class {
7717
7732
  this.invalidateGraphicGroupCache(false);
7718
7733
  this.refreshActiveEditPresentation();
7719
7734
  this.reconcileGraphicsStack();
7720
- this.renderHighlights();
7735
+ this.flushHighlights();
7721
7736
  });
7722
7737
  }
7723
7738
  setPinPresentation(defaults) {
@@ -7951,7 +7966,7 @@ var TacticalDraw = class {
7951
7966
  const resolutionChanged = !Object.is(resolution, this.lastViewResolution);
7952
7967
  this.lastViewResolution = resolution;
7953
7968
  if (!this.hasRendered) return;
7954
- this.renderHighlights();
7969
+ this.flushHighlights();
7955
7970
  const nativePointSymbolScaling = this.adapter.scalesGroundPointSymbolsNatively === true;
7956
7971
  if (!resolutionChanged && !this.lastRenderedHasViewDependent) return;
7957
7972
  const excluded = this.graphicsStackExcludedIds();
@@ -8040,6 +8055,7 @@ var TacticalDraw = class {
8040
8055
  }
8041
8056
  }
8042
8057
  this.lastRenderedMeasures = measures.slice();
8058
+ this.lastRenderedMeasuresById = new Map(this.lastRenderedMeasures.map((g) => [g.id, g]));
8043
8059
  this.lastRenderedHasViewDependent = this.lastRenderedMeasures.some((g) => !this.renderGraphicGroup(g).pin && graphicViewDependence(g, this.adapter.scalesGroundPointSymbolsNatively === true) === "view");
8044
8060
  this.graphicStack.acknowledgeHostRender(measures);
8045
8061
  if (activeEdit && "syncHostMembership" in activeEdit && measures.some((graphic) => graphic.id === activeEdit.measureId && graphic.kind === activeEdit.graphicKind)) activeEdit.syncHostMembership(true);
@@ -8058,7 +8074,7 @@ var TacticalDraw = class {
8058
8074
  });
8059
8075
  else if (this.activeInteraction.isStarting) this.activeInteraction.cancel("removed");
8060
8076
  }
8061
- this.renderHighlights();
8077
+ this.flushHighlights();
8062
8078
  }
8063
8079
  /**
8064
8080
  * Replace the passive selection-highlight set with `ids`. Declarative and
@@ -8073,6 +8089,17 @@ var TacticalDraw = class {
8073
8089
  setHighlightedGraphics(ids) {
8074
8090
  this.assertAlive();
8075
8091
  this.highlightedIds = ids.slice();
8092
+ this.highlightCoalescer.schedule();
8093
+ }
8094
+ /**
8095
+ * Cancel any pending coalesced highlight frame and re-project highlights
8096
+ * immediately. Used by every caller other than `setHighlightedGraphics`
8097
+ * (`render()`, view-change, session start / settle) so their own
8098
+ * synchronous effects and the highlight layer update together, with no
8099
+ * frame of lag and no stale coalesced call left to fire afterwards.
8100
+ */
8101
+ flushHighlights() {
8102
+ this.highlightCoalescer.cancel();
8076
8103
  this.renderHighlights();
8077
8104
  }
8078
8105
  /**
@@ -8098,7 +8125,7 @@ var TacticalDraw = class {
8098
8125
  const pinGroups = [];
8099
8126
  for (const id of this.highlightedIds) {
8100
8127
  if (excluded.has(id)) continue;
8101
- const graphic = this.lastRenderedMeasures.find((g) => g.id === id);
8128
+ const graphic = this.lastRenderedMeasuresById.get(id);
8102
8129
  if (!graphic) continue;
8103
8130
  if (isPointSymbol(graphic)) {
8104
8131
  const pin = this.renderGraphicGroup(graphic).pin;
@@ -8151,16 +8178,13 @@ var TacticalDraw = class {
8151
8178
  if (!this.images) return null;
8152
8179
  return imageFootprint(graphic, renderImageForAdapter(this.adapter, this.images, graphic).render, this.adapter)?.box ?? null;
8153
8180
  }
8154
- const render = renderMeasureForAdapter(this.adapter, graphic, {
8155
- graphicsStyle: this.graphicsStyle,
8156
- symbolColors: this.symbolColors
8157
- });
8158
- if (graphic.kind === "text") return fitTextFootprintFromFeatures(render.features, (lonLat) => this.adapter.getPixelFromCoordinate(lonLat), {
8181
+ const features = this.renderGraphicGroup(graphic).features;
8182
+ if (graphic.kind === "text") return fitTextFootprintFromFeatures(features, (lonLat) => this.adapter.getPixelFromCoordinate(lonLat), {
8159
8183
  resolution: this.adapter.getResolution(),
8160
8184
  bearing
8161
8185
  });
8162
8186
  const positions = [];
8163
- for (const feature of render.features) if (feature.geometry) collectFeaturePositions(feature.geometry, positions);
8187
+ for (const feature of features) if (feature.geometry) collectFeaturePositions(feature.geometry, positions);
8164
8188
  const pixels = [];
8165
8189
  for (const position of positions) {
8166
8190
  const pixel = this.adapter.getPixelFromCoordinate(position);
@@ -8221,7 +8245,7 @@ var TacticalDraw = class {
8221
8245
  clearSlot();
8222
8246
  this.clearSnapResult();
8223
8247
  if (extraSettle) extraSettle();
8224
- this.renderHighlights();
8248
+ this.flushHighlights();
8225
8249
  };
8226
8250
  this.activeInteraction.begin();
8227
8251
  try {
@@ -8235,7 +8259,7 @@ var TacticalDraw = class {
8235
8259
  this.activeInteraction.set(handle);
8236
8260
  if (activate && !handle.settled) {
8237
8261
  deactivate = activate(handle) ?? null;
8238
- this.renderHighlights();
8262
+ this.flushHighlights();
8239
8263
  }
8240
8264
  }
8241
8265
  handle.promise.then(clearSlot, clearSlot);
@@ -8782,6 +8806,7 @@ var TacticalDraw = class {
8782
8806
  this.unsubscribePins();
8783
8807
  if (this.destroyed) return;
8784
8808
  this.destroyed = true;
8809
+ this.highlightCoalescer.cancel();
8785
8810
  if (this.activeInteraction.isActive) {
8786
8811
  this.activeEdit = null;
8787
8812
  this.activeTransform = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbat-mapper/tactical-draw",
3
- "version": "0.10.0",
3
+ "version": "0.10.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",