@orbat-mapper/tactical-draw 0.2.0-alpha.8 → 0.2.0
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 +27 -7
- package/dist/index.d.mts +1078 -376
- package/dist/index.mjs +4701 -1081
- package/dist/map-adapter-84irgVJR.d.mts +372 -0
- package/dist/testing.d.mts +25 -0
- package/dist/testing.mjs +247 -0
- package/package.json +11 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,248 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as PointerEventInfo, _ as fillPatternImageDimension, a as MapAdapter, b as EditPointerDriver, c as MapEventHandler, d as PickOptions, f as PixelCoordinate, g as HatchPattern, h as HATCH_PATTERNS, i as LayerId, l as PickEvent, m as TextStyleOptions, n as HandleStyle, o as MapCursor, p as StyleOptions, r as IconResource, s as MapEvent, t as FeatureGroup, u as PickHandler, v as fillPatternImageId, x as PointerCallback, y as makeFillPatternImage } from "./map-adapter-84irgVJR.mjs";
|
|
2
|
+
import { AmplifierPlacement, AmplifierPlacements, ControlMeasure, ControlMeasureKind, ControlMeasureRender, ControlMeasureStyle, TextAmplifiers } from "@orbat-mapper/control-measures";
|
|
2
3
|
import { Feature, Position } from "geojson";
|
|
3
4
|
|
|
4
|
-
//#region src/gestures/types.d.ts
|
|
5
|
-
interface PointerEventInfo {
|
|
6
|
-
/** Per-event hit-tolerance override (e.g. larger for touch). */
|
|
7
|
-
hitTolerance?: number;
|
|
8
|
-
/**
|
|
9
|
-
* Whether the Alt/Option modifier was held for this pointer event. The edit
|
|
10
|
-
* controller reads this on pointer-down to turn a vertex hit into a delete
|
|
11
|
-
* (the GIS-standard Alt+click-to-delete gesture) instead of a drag. Only the
|
|
12
|
-
* mouse paths populate it; touch leaves it `undefined`.
|
|
13
|
-
*/
|
|
14
|
-
altKey?: boolean;
|
|
15
|
-
/** Engine-native event, for drivers that need to call preventDefault etc. */
|
|
16
|
-
native?: unknown;
|
|
17
|
-
}
|
|
18
|
-
interface PointerCallback {
|
|
19
|
-
(pixel: PixelCoordinate, info?: PointerEventInfo): void;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Engine-agnostic pointer driver consumed by `TacticalDraw`'s edit controller.
|
|
23
|
-
* The controller owns handle rendering and hit-tests handles in lonLat space;
|
|
24
|
-
* the driver only carries raw pointer events, drag-pan toggle, and pixel
|
|
25
|
-
* projection.
|
|
26
|
-
*/
|
|
27
|
-
interface EditPointerDriver {
|
|
28
|
-
onPointerDown(cb: PointerCallback): () => void;
|
|
29
|
-
onPointerMove(cb: PointerCallback): () => void;
|
|
30
|
-
onPointerUp(cb: PointerCallback): () => void;
|
|
31
|
-
setDragPanEnabled(enabled: boolean): void;
|
|
32
|
-
toPixel(lonLat: Position): PixelCoordinate | null;
|
|
33
|
-
fromPixel(pixel: PixelCoordinate): Position;
|
|
34
|
-
/** Idempotent. */
|
|
35
|
-
dispose(): void;
|
|
36
|
-
}
|
|
37
|
-
//#endregion
|
|
38
|
-
//#region src/map-adapter.d.ts
|
|
39
|
-
type PixelCoordinate = [number, number];
|
|
40
|
-
type MapCursor = "" | "default" | "crosshair" | "grab" | "grabbing" | "pointer";
|
|
41
|
-
interface MapEvent {
|
|
42
|
-
type: string;
|
|
43
|
-
coordinate: Position;
|
|
44
|
-
pixel: PixelCoordinate;
|
|
45
|
-
originalEvent: unknown;
|
|
46
|
-
}
|
|
47
|
-
type MapEventHandler = (event: MapEvent) => void;
|
|
48
|
-
interface StyleOptions {
|
|
49
|
-
strokeColor?: string;
|
|
50
|
-
strokeWidth?: number;
|
|
51
|
-
strokeDash?: number[];
|
|
52
|
-
fillColor?: string;
|
|
53
|
-
pointFillColor?: string;
|
|
54
|
-
pointRadius?: number;
|
|
55
|
-
text?: TextStyleOptions;
|
|
56
|
-
}
|
|
57
|
-
interface TextStyleOptions {
|
|
58
|
-
text?: string;
|
|
59
|
-
labelProperty?: string;
|
|
60
|
-
font?: string;
|
|
61
|
-
fillColor?: string;
|
|
62
|
-
strokeColor?: string;
|
|
63
|
-
strokeWidth?: number;
|
|
64
|
-
rotation?: number;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Per-feature style for edit handles (vertex / midpoint / translate / rotate).
|
|
68
|
-
* The producer (`TacticalDraw`) rides this on `properties.style`; the adapter
|
|
69
|
-
* reads it back when rendering the handle, so it is part of the adapter
|
|
70
|
-
* contract rather than the tactical-draw authoring surface. `pointRadius` is a
|
|
71
|
-
* UI affordance not present on `ControlMeasureStyle`. See ADR-0006.
|
|
72
|
-
*/
|
|
73
|
-
interface HandleStyle {
|
|
74
|
-
strokeColor?: string;
|
|
75
|
-
strokeWidth?: number;
|
|
76
|
-
strokeDash?: readonly number[];
|
|
77
|
-
fillColor?: string;
|
|
78
|
-
pointRadius?: number;
|
|
79
|
-
}
|
|
80
|
-
type LayerId = string;
|
|
81
|
-
/**
|
|
82
|
-
* Click-style hit event delivered by {@link MapAdapter.onPick}.
|
|
83
|
-
*
|
|
84
|
-
* `id` is the control measure id, already split off the feature id by the
|
|
85
|
-
* adapter (see `controlMeasureIdFromFeature`).
|
|
86
|
-
*
|
|
87
|
-
* `originalEvent` is whatever the engine surfaces — OpenLayers normalises to
|
|
88
|
-
* `PointerEvent`, MapLibre delivers `MouseEvent`, Leaflet delivers
|
|
89
|
-
* `MouseEvent | TouchEvent`. Do not narrow to `PointerEvent` at the consumer.
|
|
90
|
-
*/
|
|
91
|
-
interface PickEvent {
|
|
92
|
-
id: string;
|
|
93
|
-
feature: Feature;
|
|
94
|
-
pixel: PixelCoordinate;
|
|
95
|
-
coordinate: Position;
|
|
96
|
-
originalEvent: MouseEvent | PointerEvent | TouchEvent;
|
|
97
|
-
}
|
|
98
|
-
type PickHandler = (event: PickEvent) => void;
|
|
99
|
-
interface PickOptions {
|
|
100
|
-
signal?: AbortSignal;
|
|
101
|
-
}
|
|
102
|
-
interface MapAdapter {
|
|
103
|
-
/**
|
|
104
|
-
* Add a vector layer to the map.
|
|
105
|
-
* @returns unique layer ID
|
|
106
|
-
*/
|
|
107
|
-
addVectorLayer(options?: {
|
|
108
|
-
style?: StyleOptions;
|
|
109
|
-
}): LayerId;
|
|
110
|
-
/**
|
|
111
|
-
* Remove a layer from the map.
|
|
112
|
-
*/
|
|
113
|
-
removeLayer(layerId: LayerId): void;
|
|
114
|
-
/**
|
|
115
|
-
* Set the style for a specific layer.
|
|
116
|
-
*/
|
|
117
|
-
setLayerStyle(layerId: LayerId, style: StyleOptions): void;
|
|
118
|
-
/**
|
|
119
|
-
* Set features on a vector layer.
|
|
120
|
-
* Features should be standard GeoJSON Features.
|
|
121
|
-
*/
|
|
122
|
-
setLayerFeatures(layerId: LayerId, features: Feature[]): void;
|
|
123
|
-
/**
|
|
124
|
-
* Clear all features from a layer.
|
|
125
|
-
*/
|
|
126
|
-
clearLayer(layerId: LayerId): void;
|
|
127
|
-
/**
|
|
128
|
-
* Reconcile features on a layer against the incoming set, keyed by `id`.
|
|
129
|
-
*
|
|
130
|
-
* Semantics:
|
|
131
|
-
* - features with an `id` that already exists are updated in place;
|
|
132
|
-
* - features with a fresh `id` are added;
|
|
133
|
-
* - features in the layer whose `id` is missing from the incoming set are
|
|
134
|
-
* removed.
|
|
135
|
-
*
|
|
136
|
-
* Features must carry a stable `id`. Use this when you can compute the
|
|
137
|
-
* complete desired state of a layer in one go; for partial removals use
|
|
138
|
-
* {@link removeFeatures}.
|
|
139
|
-
*/
|
|
140
|
-
updateLayerFeatures(layerId: LayerId, features: readonly Feature[]): void;
|
|
141
|
-
/**
|
|
142
|
-
* Remove features by `id` from a layer. Ids that are not present are
|
|
143
|
-
* silently ignored.
|
|
144
|
-
*/
|
|
145
|
-
removeFeatures(layerId: LayerId, ids: readonly string[]): void;
|
|
146
|
-
/**
|
|
147
|
-
* Subscribe to click-style "pick" events for a specific layer. Basemap and
|
|
148
|
-
* other-layer clicks never fire the handler.
|
|
149
|
-
*
|
|
150
|
-
* For control-measure features, adapters must set `PickEvent.id` to the
|
|
151
|
-
* originating control-measure id (not the rendered part's feature id).
|
|
152
|
-
* Features that do not satisfy the control-measure feature-id contract must
|
|
153
|
-
* be ignored rather than dispatched.
|
|
154
|
-
*
|
|
155
|
-
* Bound to the engine's tap-suppressing click event (OL `singleclick`,
|
|
156
|
-
* MapLibre `click`, Leaflet `click`) so the handler does not fire at the
|
|
157
|
-
* end of a pan or pinch-zoom gesture.
|
|
158
|
-
*
|
|
159
|
-
* Hit tolerance is pointer-type aware: 4 px for mouse/pen, 12 px for touch.
|
|
160
|
-
*
|
|
161
|
-
* Returns an unsubscribe function. The returned function and the optional
|
|
162
|
-
* `signal` are both honoured; either teardown path stops further events,
|
|
163
|
-
* and both are idempotent.
|
|
164
|
-
*/
|
|
165
|
-
onPick(layerId: LayerId, handler: PickHandler, opts?: PickOptions): () => void;
|
|
166
|
-
/**
|
|
167
|
-
* Map Event Listeners
|
|
168
|
-
*
|
|
169
|
-
* Ordering contract: a `"click"` handler and the `onPick` handlers for the
|
|
170
|
-
* same user click MUST observe the pick first — adapters deliver picks and
|
|
171
|
-
* `"click"` from the same underlying engine event, in registration order.
|
|
172
|
-
* The draw/edit controllers register their pick subscriptions before their
|
|
173
|
-
* click handler and read the stashed pick inside it (click-away close /
|
|
174
|
-
* close-then-pick re-dispatch); an adapter that fires `"click"` from an
|
|
175
|
-
* earlier engine event than its picks breaks that contract (e.g. OL
|
|
176
|
-
* `click` vs `singleclick`).
|
|
177
|
-
*/
|
|
178
|
-
on(eventType: "click" | "pointermove" | "dblclick", handler: MapEventHandler): void;
|
|
179
|
-
off(eventType: "click" | "pointermove" | "dblclick", handler: MapEventHandler): void;
|
|
180
|
-
/**
|
|
181
|
-
* Projection / Coordinate Utilities
|
|
182
|
-
*/
|
|
183
|
-
toLonLat(coordinate: number[]): Position;
|
|
184
|
-
fromLonLat(position: Position): number[];
|
|
185
|
-
getPixelFromCoordinate(position: Position): PixelCoordinate | null;
|
|
186
|
-
/**
|
|
187
|
-
* Get current viewport size in CSS pixels.
|
|
188
|
-
*/
|
|
189
|
-
getViewportSize(): {
|
|
190
|
-
width: number;
|
|
191
|
-
height: number;
|
|
192
|
-
};
|
|
193
|
-
/**
|
|
194
|
-
* Pan the map by pixel delta.
|
|
195
|
-
* Positive delta means desired on-screen feature shift (right/down).
|
|
196
|
-
*/
|
|
197
|
-
panByPixels(delta: PixelCoordinate, options?: {
|
|
198
|
-
durationMs?: number;
|
|
199
|
-
}): void;
|
|
200
|
-
/**
|
|
201
|
-
* Set the map viewport cursor. Pass an empty string to restore engine default styling.
|
|
202
|
-
*/
|
|
203
|
-
setCursor(cursor: MapCursor): void;
|
|
204
|
-
/**
|
|
205
|
-
* Toggle the engine's native double-click-to-zoom gesture. `TacticalDraw`
|
|
206
|
-
* disables this for the lifetime of a variable-length draw so the commit
|
|
207
|
-
* `dblclick` doesn't also zoom the map.
|
|
208
|
-
*/
|
|
209
|
-
setDoubleClickZoomEnabled(enabled: boolean): void;
|
|
210
|
-
/**
|
|
211
|
-
* Get current view resolution (meters per pixel at center or generally).
|
|
212
|
-
* returns undefined if not supported or not ready.
|
|
213
|
-
*/
|
|
214
|
-
getResolution(): number | undefined;
|
|
215
|
-
/**
|
|
216
|
-
* Get current map zoom level
|
|
217
|
-
*/
|
|
218
|
-
getZoom(): number | undefined;
|
|
219
|
-
/**
|
|
220
|
-
* Listen for view changes (zoom/resolution)
|
|
221
|
-
*/
|
|
222
|
-
onViewChange(handler: () => void): void;
|
|
223
|
-
offViewChange(handler: () => void): void;
|
|
224
|
-
/**
|
|
225
|
-
* Construct an {@link EditPointerDriver} for `TacticalDraw`'s edit
|
|
226
|
-
* controller. The controller subscribes to pointer events on the returned
|
|
227
|
-
* driver to wire handle drags (vertex / midpoint / translate / rotate).
|
|
228
|
-
*/
|
|
229
|
-
createEditPointerDriver(): EditPointerDriver;
|
|
230
|
-
/**
|
|
231
|
-
* Release all adapter-owned map resources. Adapters must not be used after
|
|
232
|
-
* destruction.
|
|
233
|
-
*/
|
|
234
|
-
destroy(): void;
|
|
235
|
-
}
|
|
236
|
-
//#endregion
|
|
237
5
|
//#region src/base-map-adapter.d.ts
|
|
238
|
-
interface FeatureOps {
|
|
239
|
-
adds: Feature[];
|
|
240
|
-
updates: Feature[];
|
|
241
|
-
/** Features whose id was absent from the incoming set. */
|
|
242
|
-
removes?: Feature[];
|
|
243
|
-
full: Feature[];
|
|
244
|
-
replace?: boolean;
|
|
245
|
-
}
|
|
246
6
|
type NormalizedEventHandler = (lonLat: Position, pixel: PixelCoordinate, originalEvent: unknown) => void;
|
|
247
7
|
interface NativeEventEntry<TEventToken> {
|
|
248
8
|
type: string;
|
|
@@ -252,10 +12,8 @@ interface NativeEventEntry<TEventToken> {
|
|
|
252
12
|
* Abstract base class shared by all `MapAdapter` implementations.
|
|
253
13
|
*
|
|
254
14
|
* Owns shared bookkeeping (layer registry, feature cache, event-token
|
|
255
|
-
* tracking) and orchestrates the public methods over a small
|
|
256
|
-
*
|
|
257
|
-
* `docs/adr/0001-map-adapter-base-class.md` for the rationale, in particular
|
|
258
|
-
* around the `applyFeatureOps` primitive shape.
|
|
15
|
+
* tracking) and orchestrates the public methods over a small protected
|
|
16
|
+
* grouped desired-state primitive that subclasses implement.
|
|
259
17
|
*/
|
|
260
18
|
declare abstract class BaseMapAdapter<TLayerHandle, TEventToken, TViewToken> implements MapAdapter {
|
|
261
19
|
protected layers: Map<string, TLayerHandle>;
|
|
@@ -263,6 +21,9 @@ declare abstract class BaseMapAdapter<TLayerHandle, TEventToken, TViewToken> imp
|
|
|
263
21
|
protected viewChangeTokens: Map<() => void, TViewToken>;
|
|
264
22
|
protected featureCache: Map<string, Feature<import("geojson").Geometry, import("geojson").GeoJsonProperties>[]>;
|
|
265
23
|
protected featuresChangedListeners: Map<string, Set<(features: Feature[]) => void>>;
|
|
24
|
+
protected iconResources: Map<string, IconResource>;
|
|
25
|
+
private featureGroupState;
|
|
26
|
+
private layerIconResourceIds;
|
|
266
27
|
private layerCounter;
|
|
267
28
|
private destroyed;
|
|
268
29
|
protected nextLayerId(): LayerId;
|
|
@@ -270,12 +31,9 @@ declare abstract class BaseMapAdapter<TLayerHandle, TEventToken, TViewToken> imp
|
|
|
270
31
|
style?: StyleOptions;
|
|
271
32
|
}): LayerId;
|
|
272
33
|
removeLayer(layerId: LayerId): void;
|
|
273
|
-
|
|
274
|
-
updateLayerFeatures(layerId: LayerId, features: readonly Feature[]): void;
|
|
275
|
-
removeFeatures(layerId: LayerId, ids: readonly string[]): void;
|
|
34
|
+
setLayerFeatureGroups(layerId: LayerId, groups: readonly FeatureGroup[]): void;
|
|
276
35
|
abstract onPick(layerId: LayerId, handler: PickHandler, opts?: PickOptions): () => void;
|
|
277
36
|
abstract createEditPointerDriver(): EditPointerDriver;
|
|
278
|
-
clearLayer(layerId: LayerId): void;
|
|
279
37
|
on(eventType: "click" | "pointermove" | "dblclick", handler: MapEventHandler): void;
|
|
280
38
|
off(eventType: "click" | "pointermove" | "dblclick", handler: MapEventHandler): void;
|
|
281
39
|
onViewChange(handler: () => void): void;
|
|
@@ -284,9 +42,15 @@ declare abstract class BaseMapAdapter<TLayerHandle, TEventToken, TViewToken> imp
|
|
|
284
42
|
protected emitLayerFeaturesChanged(layerId: LayerId, features: Feature[]): void;
|
|
285
43
|
protected onLayerFeaturesChanged(layerId: LayerId, listener: (features: Feature[]) => void): () => void;
|
|
286
44
|
protected destroyEngineResources(): void;
|
|
45
|
+
protected applyIconResources(resources: readonly IconResource[]): void;
|
|
46
|
+
protected getIconResource(id: string): IconResource | undefined;
|
|
47
|
+
protected getFeatureGroupOrder(layerId: LayerId, featureId: string | number | undefined): number | undefined;
|
|
48
|
+
protected removeIconResource(id: string): void;
|
|
49
|
+
private pruneIconResources;
|
|
287
50
|
protected abstract createLayer(layerId: LayerId, style?: StyleOptions): TLayerHandle;
|
|
288
51
|
protected abstract destroyLayer(handle: TLayerHandle): void;
|
|
289
|
-
|
|
52
|
+
/** Apply one validated, complete grouped desired state. */
|
|
53
|
+
protected abstract applyFeatureGroups(layerId: LayerId, groups: readonly FeatureGroup[]): void;
|
|
290
54
|
protected abstract attachNativeEvent(type: "click" | "pointermove" | "dblclick", onEvent: NormalizedEventHandler): NativeEventEntry<TEventToken>;
|
|
291
55
|
protected abstract detachNativeEvent(entry: NativeEventEntry<TEventToken>): void;
|
|
292
56
|
protected abstract attachViewChange(handler: () => void): TViewToken;
|
|
@@ -306,9 +70,17 @@ declare abstract class BaseMapAdapter<TLayerHandle, TEventToken, TViewToken> imp
|
|
|
306
70
|
abstract setDoubleClickZoomEnabled(enabled: boolean): void;
|
|
307
71
|
abstract getResolution(): number | undefined;
|
|
308
72
|
abstract getZoom(): number | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Default for engines/views that don't rotate (Leaflet). Rotating engines
|
|
75
|
+
* (MapLibre, OpenLayers) override this.
|
|
76
|
+
*/
|
|
77
|
+
getBearing(): number;
|
|
78
|
+
getLayerFeatures(layerId: LayerId): readonly Feature[];
|
|
309
79
|
}
|
|
310
80
|
//#endregion
|
|
311
81
|
//#region src/pick-utils.d.ts
|
|
82
|
+
type PickPointerType = "mouse" | "pen" | "touch";
|
|
83
|
+
declare function pickPointerType(originalEvent: unknown): PickPointerType;
|
|
312
84
|
declare function pickHitTolerance(originalEvent: unknown): number;
|
|
313
85
|
declare function tryControlMeasureIdFromFeature(feature: Feature): string | null;
|
|
314
86
|
/**
|
|
@@ -319,6 +91,126 @@ declare function tryControlMeasureIdFromFeature(feature: Feature): string | null
|
|
|
319
91
|
*/
|
|
320
92
|
declare function bindAbortable(signal: AbortSignal | undefined, attach: () => () => void): () => void;
|
|
321
93
|
//#endregion
|
|
94
|
+
//#region src/pick-candidates.d.ts
|
|
95
|
+
interface ScreenBounds {
|
|
96
|
+
minX: number;
|
|
97
|
+
minY: number;
|
|
98
|
+
maxX: number;
|
|
99
|
+
maxY: number;
|
|
100
|
+
}
|
|
101
|
+
interface RotatedScreenBounds {
|
|
102
|
+
center: PixelCoordinate;
|
|
103
|
+
halfWidth: number;
|
|
104
|
+
halfHeight: number;
|
|
105
|
+
angle: number;
|
|
106
|
+
}
|
|
107
|
+
type LabelExtent = ScreenBounds | RotatedScreenBounds;
|
|
108
|
+
interface ResolvedPickPresentation {
|
|
109
|
+
stroke?: {
|
|
110
|
+
color?: string;
|
|
111
|
+
width?: number;
|
|
112
|
+
dash?: readonly number[];
|
|
113
|
+
};
|
|
114
|
+
fill?: {
|
|
115
|
+
color?: string;
|
|
116
|
+
pattern?: string;
|
|
117
|
+
opacity: number;
|
|
118
|
+
};
|
|
119
|
+
point?: {
|
|
120
|
+
fillColor?: string;
|
|
121
|
+
radius?: number;
|
|
122
|
+
};
|
|
123
|
+
text?: TextStyleOptions;
|
|
124
|
+
}
|
|
125
|
+
type PickCandidateHitKind = "native" | "point" | "line" | "polygon-boundary" | "polygon-interior" | "icon" | "label";
|
|
126
|
+
interface PickCandidateHit {
|
|
127
|
+
kind: PickCandidateHitKind;
|
|
128
|
+
/** CSS-pixel distance to the visible mark, or null when only the engine knows the hit. */
|
|
129
|
+
distance: number | null;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Engine-neutral description of one rendered part eligible at a pointer location.
|
|
133
|
+
* This is deliberately richer than PickEvent but remains adapter-author plumbing:
|
|
134
|
+
* ordered candidates are not exposed by the public picking event.
|
|
135
|
+
*/
|
|
136
|
+
interface PickCandidate {
|
|
137
|
+
feature: Feature;
|
|
138
|
+
graphicId: string | null;
|
|
139
|
+
renderedPartId: string | number | null;
|
|
140
|
+
hit: PickCandidateHit;
|
|
141
|
+
viewport: ScreenBounds;
|
|
142
|
+
presentation: ResolvedPickPresentation;
|
|
143
|
+
visualOrder: number;
|
|
144
|
+
/** Lower values mean a visually higher atomic feature group. */
|
|
145
|
+
featureGroupOrder?: number;
|
|
146
|
+
iconFootprint?: readonly PixelCoordinate[];
|
|
147
|
+
labelExtent?: LabelExtent;
|
|
148
|
+
}
|
|
149
|
+
/** A rendered part supplied by an adapter or TacticalDraw for shared evaluation. */
|
|
150
|
+
interface PickCandidateInput {
|
|
151
|
+
feature: Feature;
|
|
152
|
+
/**
|
|
153
|
+
* Lower values mean visually topmost. Suppliers must normalize their
|
|
154
|
+
* native ordering to topmost-first before calling into evaluation.
|
|
155
|
+
*/
|
|
156
|
+
visualOrder: number;
|
|
157
|
+
/**
|
|
158
|
+
* Authoritative bottom-to-top group order normalized to lower = visually
|
|
159
|
+
* higher. Unlike `visualOrder`, native engine query order cannot override it.
|
|
160
|
+
*/
|
|
161
|
+
featureGroupOrder?: number;
|
|
162
|
+
/** The engine already reported this part as hit; retained as a fallback for engine-only marks. */
|
|
163
|
+
nativeHit?: boolean;
|
|
164
|
+
icon?: IconResource;
|
|
165
|
+
labelExtent?: LabelExtent;
|
|
166
|
+
presentation?: ResolvedPickPresentation;
|
|
167
|
+
layerStyle?: StyleOptions;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The `visualOrder` for the feature at `index` of a render-ordered
|
|
171
|
+
* (bottom-first) list of `total` features: reversing the index makes the
|
|
172
|
+
* topmost-rendered feature the lowest, and therefore winning, order.
|
|
173
|
+
*/
|
|
174
|
+
declare function renderOrderToVisualOrder(index: number, total: number): number;
|
|
175
|
+
interface MergedPickFeature {
|
|
176
|
+
feature: Feature;
|
|
177
|
+
visualOrder: number;
|
|
178
|
+
nativeHit: boolean;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Preserve native visual ordering/hit evidence while keeping cached source
|
|
182
|
+
* geometry authoritative. Native polygon geometry may be tile-clipped and
|
|
183
|
+
* therefore contain artificial edges that are not part of the graphic.
|
|
184
|
+
*
|
|
185
|
+
* Normalizes to the shared `visualOrder` contract (lower = visually
|
|
186
|
+
* topmost): native hits already arrive topmost-first from the engines and
|
|
187
|
+
* keep that order at the front. Cached-only features (not among the native
|
|
188
|
+
* hits) are appended after them, in reverse cache order — cache arrays are
|
|
189
|
+
* render order (bottom-first), so reversing yields topmost-first — with
|
|
190
|
+
* `visualOrder` numbering continuing on from the native section.
|
|
191
|
+
*/
|
|
192
|
+
declare function mergeNativeAndCachedPickFeatures(nativeFeatures: readonly Feature[], cachedFeatures: readonly Feature[]): MergedPickFeature[];
|
|
193
|
+
interface PickEvaluationContext {
|
|
194
|
+
pixel: PixelCoordinate;
|
|
195
|
+
tolerance: number;
|
|
196
|
+
pointerType?: PickPointerType;
|
|
197
|
+
toPixel: (position: Position) => PixelCoordinate | null;
|
|
198
|
+
viewport: ScreenBounds;
|
|
199
|
+
bearing?: number;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Evaluate every relevant rendered part in screen space, then return the
|
|
203
|
+
* primary candidate under the visible-intent ranking policy: direct visible
|
|
204
|
+
* marks (strokes, points, icons, labels) outrank visibly filled interiors,
|
|
205
|
+
* which outrank inferred outline-only reach; competing direct marks rank by
|
|
206
|
+
* screen-space distance; visual topmost order is only the final tie-break.
|
|
207
|
+
* See `rankPickCandidates` for the full ordered list — that ordering stays
|
|
208
|
+
* internal adapter-author plumbing; the public pick event exposes only this
|
|
209
|
+
* primary candidate.
|
|
210
|
+
*/
|
|
211
|
+
declare function evaluatePickCandidates(inputs: readonly PickCandidateInput[], context: PickEvaluationContext): PickCandidate | null;
|
|
212
|
+
declare function resolvePickPresentation(feature: Feature, layerStyle?: StyleOptions): ResolvedPickPresentation;
|
|
213
|
+
//#endregion
|
|
322
214
|
//#region src/feature-style.d.ts
|
|
323
215
|
/**
|
|
324
216
|
* Coerce a raw `properties.style` value into a `HandleStyle` for the
|
|
@@ -326,6 +218,61 @@ declare function bindAbortable(signal: AbortSignal | undefined, attach: () => ()
|
|
|
326
218
|
* when missing or shaped wrong so callers fall through to the layer style.
|
|
327
219
|
*/
|
|
328
220
|
declare function coerceHandleStyle(raw: unknown): HandleStyle | undefined;
|
|
221
|
+
/** Default lower edge of the ground-anchored label clamp band (ADR-0028). */
|
|
222
|
+
declare const DEFAULT_TEXT_SIZE_MIN_PIXELS = 8;
|
|
223
|
+
/** Default upper edge of the ground-anchored label clamp band (ADR-0028). */
|
|
224
|
+
declare const DEFAULT_TEXT_SIZE_MAX_PIXELS = 24;
|
|
225
|
+
/**
|
|
226
|
+
* Rendered pixel size of a ground-anchored label (ADR-0028):
|
|
227
|
+
* `textSizeMeters / resolution`, clamped to the layer's text-style band.
|
|
228
|
+
* Within the band the text scales with its graphic; past either edge it pins
|
|
229
|
+
* to the bound, so zooming out never yields unreadable smudges and zooming in
|
|
230
|
+
* never yields screen-covering text. Shared by the adapters that resolve text
|
|
231
|
+
* size in code (OpenLayers, Leaflet); the MapLibre adapter evaluates the same
|
|
232
|
+
* clamp inside its per-zoom-stop style expression.
|
|
233
|
+
*/
|
|
234
|
+
declare function resolveGroundTextSizePixels(textSizeMeters: number, resolution: number, text?: Pick<TextStyleOptions, "textSizeMinPixels" | "textSizeMaxPixels">): number;
|
|
235
|
+
/**
|
|
236
|
+
* The size properties a Text graphic's feature carries (see `text.ts` /
|
|
237
|
+
* `FeaturePartProps`). Mutually exclusive size anchors: `textSizePixels`
|
|
238
|
+
* (screen-anchored) or `textSizeMeters` (ground-anchored). `textMaxSizePixels`
|
|
239
|
+
* is the ADR-0037 hide threshold; the min/max band fields are the amplifier
|
|
240
|
+
* -label clamp bounds, applied only when no max-size threshold is present.
|
|
241
|
+
*/
|
|
242
|
+
interface TextSizeProps {
|
|
243
|
+
textSizeMeters?: number;
|
|
244
|
+
textSizePixels?: number;
|
|
245
|
+
textMaxSizePixels?: number;
|
|
246
|
+
textSizeMinPixels?: number;
|
|
247
|
+
textSizeMaxPixels?: number;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Resolve a Text graphic's on-screen size in pixels, honoring the
|
|
251
|
+
* `textMaxSizePixels` hide threshold (ADR-0037). Returns `null` — the "hidden"
|
|
252
|
+
* signal — when the graphic should render no text this frame; adapters emit no
|
|
253
|
+
* text style for a `null`. Distinct from [[resolveGroundTextSizePixels]], which
|
|
254
|
+
* keeps the 8–24 px clamp band for amplifier labels: this helper is the Text
|
|
255
|
+
* graphic's own sizing rule.
|
|
256
|
+
*
|
|
257
|
+
* - screen-anchored (`textSizePixels`): the fixed pixel size, never scaled or
|
|
258
|
+
* hidden by zoom.
|
|
259
|
+
* - ground-anchored (`textSizeMeters`) with a `textMaxSizePixels` threshold:
|
|
260
|
+
* *unclamped* `meters / resolution` (the 8–24 band does NOT apply), hidden
|
|
261
|
+
* (`null`) once that exceeds the threshold — text shrinks freely zooming out
|
|
262
|
+
* and vanishes rather than clamps zooming in.
|
|
263
|
+
* - ground-anchored without a threshold: falls back to the clamped band.
|
|
264
|
+
* - no usable size or resolution: `null`.
|
|
265
|
+
*/
|
|
266
|
+
declare function resolveTextSizePixels(props: TextSizeProps, resolution: number): number | null;
|
|
267
|
+
/** Fallback halo color when a `textHalo` feature carries no `textHaloColor`. */
|
|
268
|
+
declare const TEXT_HALO_COLOR = "#ffffff";
|
|
269
|
+
/**
|
|
270
|
+
* Outward depth (px) of a `textHalo` halo from the glyph edge, scaled to font
|
|
271
|
+
* size. This is the halo's *outward* reach, not a stroke width — a canvas
|
|
272
|
+
* stroke straddles the glyph edge (half in, half out), so a caller drawing
|
|
273
|
+
* one must double this value to get the same visible depth.
|
|
274
|
+
*/
|
|
275
|
+
declare function resolveTextHaloWidth(textSizePixels?: number): number;
|
|
329
276
|
//#endregion
|
|
330
277
|
//#region src/gestures/hit-test.d.ts
|
|
331
278
|
type ToPixel = (lonLat: Position) => PixelCoordinate | null;
|
|
@@ -340,7 +287,14 @@ type ToPixel = (lonLat: Position) => PixelCoordinate | null;
|
|
|
340
287
|
*
|
|
341
288
|
* Returns the first matching feature in iteration order, or null.
|
|
342
289
|
*/
|
|
343
|
-
declare function hitTestFeature(pixel: PixelCoordinate, features: Feature[], hitTolerance: number, toPixel: ToPixel): Feature | null;
|
|
290
|
+
declare function hitTestFeature(pixel: PixelCoordinate, features: readonly Feature[], hitTolerance: number, toPixel: ToPixel): Feature | null;
|
|
291
|
+
/**
|
|
292
|
+
* Test a rendered icon-backed Point feature against its actual pixel footprint.
|
|
293
|
+
* The feature carries the adapter-neutral `iconScale` / `iconRotation` values
|
|
294
|
+
* emitted by TacticalDraw; `resource.anchor` is the placement anchor within the
|
|
295
|
+
* unscaled SVG viewport. A positive tolerance pads all four sides.
|
|
296
|
+
*/
|
|
297
|
+
declare function hitTestIconFeature(pixel: PixelCoordinate, feature: Feature, resource: IconResource, hitTolerance: number, toPixel: ToPixel, bearing?: number): boolean;
|
|
344
298
|
//#endregion
|
|
345
299
|
//#region src/gestures/hit-tolerance.d.ts
|
|
346
300
|
declare const TOUCH_HIT_TOLERANCE_PX = 22;
|
|
@@ -375,6 +329,104 @@ declare class PointerCallbackHub {
|
|
|
375
329
|
clear(): void;
|
|
376
330
|
}
|
|
377
331
|
//#endregion
|
|
332
|
+
//#region src/point-symbol.d.ts
|
|
333
|
+
type PointSymbolSize = {
|
|
334
|
+
value: number;
|
|
335
|
+
unit: "pixels";
|
|
336
|
+
} | {
|
|
337
|
+
value: number;
|
|
338
|
+
unit: "meters"; /** Floor for the on-screen projection of the ground-based size; none when omitted. */
|
|
339
|
+
minPixels?: number; /** Cap on the on-screen projection of the ground-based size — typically the pixel size the symbol had when anchored to ground; unclamped when omitted. */
|
|
340
|
+
maxPixels?: number;
|
|
341
|
+
};
|
|
342
|
+
interface PointSymbolGraphicModifiers {
|
|
343
|
+
/** Radians clockwise, relative to the point symbol's own orientation. */
|
|
344
|
+
direction?: number;
|
|
345
|
+
}
|
|
346
|
+
interface PointSymbolColors {
|
|
347
|
+
fill?: string;
|
|
348
|
+
frame?: string;
|
|
349
|
+
icon?: string;
|
|
350
|
+
info?: string;
|
|
351
|
+
outline?: string;
|
|
352
|
+
}
|
|
353
|
+
interface PointSymbolStyle {
|
|
354
|
+
colors?: PointSymbolColors;
|
|
355
|
+
monoColor?: string;
|
|
356
|
+
opacity?: number;
|
|
357
|
+
outlineWidth?: number;
|
|
358
|
+
fill?: boolean;
|
|
359
|
+
frame?: boolean;
|
|
360
|
+
icon?: boolean;
|
|
361
|
+
}
|
|
362
|
+
interface PointSymbol {
|
|
363
|
+
id: string;
|
|
364
|
+
kind: "point-symbol";
|
|
365
|
+
sidc: string;
|
|
366
|
+
position: Position;
|
|
367
|
+
/** Normalized radians clockwise from geographic north. */
|
|
368
|
+
rotation: number;
|
|
369
|
+
/** Milsymbol's octagon-based L dimension. */
|
|
370
|
+
size: PointSymbolSize;
|
|
371
|
+
textAmplifiers?: TextAmplifiers;
|
|
372
|
+
graphicModifiers?: PointSymbolGraphicModifiers;
|
|
373
|
+
style?: PointSymbolStyle;
|
|
374
|
+
rendererOptions?: {
|
|
375
|
+
milsymbol?: Record<string, unknown>;
|
|
376
|
+
};
|
|
377
|
+
properties?: Record<string, unknown>;
|
|
378
|
+
schemaVersion?: 1;
|
|
379
|
+
}
|
|
380
|
+
interface PointSymbolAnchor {
|
|
381
|
+
x: number;
|
|
382
|
+
y: number;
|
|
383
|
+
}
|
|
384
|
+
/** Canonical-size, renderer-neutral point-symbol output. */
|
|
385
|
+
interface PointSymbolRender {
|
|
386
|
+
/** Content-derived identity for adapter-wide native-resource reuse. */
|
|
387
|
+
resourceId: string;
|
|
388
|
+
svg: string;
|
|
389
|
+
/** Renderer-space octagon size used to interpret persisted symbol size. */
|
|
390
|
+
octagonSize: number;
|
|
391
|
+
width: number;
|
|
392
|
+
height: number;
|
|
393
|
+
anchor: PointSymbolAnchor;
|
|
394
|
+
octagonAnchor: PointSymbolAnchor;
|
|
395
|
+
valid: boolean;
|
|
396
|
+
validation?: unknown;
|
|
397
|
+
}
|
|
398
|
+
interface PointSymbolCapability {
|
|
399
|
+
readonly defaultSize: PointSymbolSize;
|
|
400
|
+
readonly defaultRotation: number;
|
|
401
|
+
render(symbol: PointSymbol): PointSymbolRender;
|
|
402
|
+
invalidate(): void;
|
|
403
|
+
/**
|
|
404
|
+
* Monotonic counter that increments on each `invalidate()` call. Consumers
|
|
405
|
+
* (e.g. a render cache keyed on graphic snapshot identity) may read this to
|
|
406
|
+
* detect that previously rendered point-symbol output is stale even though
|
|
407
|
+
* the graphic snapshot itself is unchanged. Optional — when absent,
|
|
408
|
+
* invalidation cannot be detected by cache layers, so they must not attempt
|
|
409
|
+
* to key on it.
|
|
410
|
+
*/
|
|
411
|
+
readonly generation?: number;
|
|
412
|
+
}
|
|
413
|
+
declare function isPointSymbol(graphic: unknown): graphic is PointSymbol;
|
|
414
|
+
declare function clonePointSymbol(symbol: PointSymbol): PointSymbol;
|
|
415
|
+
/**
|
|
416
|
+
* Replace a point-symbol size value as a user-driven resize. Ground-size display
|
|
417
|
+
* bounds scale with the value so they continue to constrain zoom changes rather
|
|
418
|
+
* than preventing the resize itself.
|
|
419
|
+
*/
|
|
420
|
+
declare function resizePointSymbolSize(size: PointSymbolSize, value: number): PointSymbolSize;
|
|
421
|
+
//#endregion
|
|
422
|
+
//#region src/graphic.d.ts
|
|
423
|
+
type Graphic = ControlMeasure | PointSymbol;
|
|
424
|
+
type GraphicRender = ControlMeasureRender | PointSymbolRender;
|
|
425
|
+
interface GraphicSnapshot<G extends Graphic = Graphic> {
|
|
426
|
+
graphic: G;
|
|
427
|
+
render: G extends PointSymbol ? PointSymbolRender : ControlMeasureRender;
|
|
428
|
+
}
|
|
429
|
+
//#endregion
|
|
378
430
|
//#region src/tactical-draw/abort.d.ts
|
|
379
431
|
/**
|
|
380
432
|
* AbortPlumbing for the TacticalDraw façade.
|
|
@@ -425,6 +477,7 @@ interface CombinedAbort {
|
|
|
425
477
|
declare function combineWithHostSignal(hostSignal?: AbortSignal): CombinedAbort;
|
|
426
478
|
//#endregion
|
|
427
479
|
//#region src/tactical-draw/measure-options.d.ts
|
|
480
|
+
declare const DEFAULT_LABEL_SIZE_PIXELS = 14;
|
|
428
481
|
/**
|
|
429
482
|
* Where a control measure's size is anchored (ADR-0020). `ground` (the
|
|
430
483
|
* default) means a meter size fixed to the terrain — static, never re-rendered
|
|
@@ -466,9 +519,20 @@ interface InteractionStyle {
|
|
|
466
519
|
guide?: GuideStyle;
|
|
467
520
|
vertexHandle?: HandleStyle;
|
|
468
521
|
midpointHandle?: HandleStyle;
|
|
522
|
+
/** Visible grab target for a draggable rendered text label. */
|
|
523
|
+
labelHandle?: HandleStyle;
|
|
469
524
|
scaleHandle?: HandleStyle;
|
|
470
525
|
rotateHandle?: HandleStyle;
|
|
471
526
|
boxOutline?: GuideStyle;
|
|
527
|
+
/**
|
|
528
|
+
* Line style for the passive, non-interactive selection highlight rendered
|
|
529
|
+
* by {@link TacticalDraw.setHighlightedGraphics}. A dashed, muted variant of
|
|
530
|
+
* `boxOutline` so a locked/highlighted selection reads as visually distinct
|
|
531
|
+
* from an active edit's oriented transform box. Delivered per-feature via
|
|
532
|
+
* `properties.style` on the highlight outline `LineString`, resolved through
|
|
533
|
+
* the same built-in → façade → per-call slot system as every other field.
|
|
534
|
+
*/
|
|
535
|
+
highlightOutline?: GuideStyle;
|
|
472
536
|
/**
|
|
473
537
|
* Pixel padding added on every side of the fitted transform box
|
|
474
538
|
* (`fitTransformBox`, ADR-0017) so the box doesn't tightly hug the rendered
|
|
@@ -483,12 +547,68 @@ interface InteractionStyle {
|
|
|
483
547
|
* `reshape` renders vertex/midpoint handles; `transform` renders the oriented
|
|
484
548
|
* selection box (4 corner scale handles + a rotate grip + drag-anywhere
|
|
485
549
|
* body translate) via `transform-box.ts`. The two can be combined freely —
|
|
486
|
-
* single-measure edits show both by default. `
|
|
550
|
+
* single-measure edits show both by default. `labeldrag` enables dragging
|
|
551
|
+
* rendered text labels. `delete` is an explicit
|
|
487
552
|
* destructive mode: it renders the vertex handles (so they can be tapped to
|
|
488
553
|
* remove) but no midpoints or box, and the edit controller deletes the tapped
|
|
489
554
|
* vertex instead of dragging it.
|
|
490
555
|
*/
|
|
491
|
-
type EditMode = "reshape" | "transform" | "delete";
|
|
556
|
+
type EditMode = "reshape" | "transform" | "labeldrag" | "delete";
|
|
557
|
+
//#endregion
|
|
558
|
+
//#region src/tactical-draw/session-events.d.ts
|
|
559
|
+
/** A gesture's phase within a stream of {@link TransientChangeEvent}s. */
|
|
560
|
+
type TransientPhase = "start" | "move" | "end";
|
|
561
|
+
/**
|
|
562
|
+
* An in-flight mutation of a graphic's working geometry during a gesture —
|
|
563
|
+
* streamed continuously while the gesture runs (`EditSession.onTransientChange`
|
|
564
|
+
* / `DrawSession.onTransientChange`), distinct from the once-per-completed-
|
|
565
|
+
* gesture `onChange`. Hosts use it for live feedback, e.g. a radius readout
|
|
566
|
+
* while dragging a circle handle.
|
|
567
|
+
*/
|
|
568
|
+
interface TransientChangeEvent {
|
|
569
|
+
/** Gesture phase. `end` fires exactly once per started gesture — on commit AND on cancel/abort. */
|
|
570
|
+
readonly phase: TransientPhase;
|
|
571
|
+
/** The full working control points at this instant (lon/lat). Fresh array per emission. */
|
|
572
|
+
readonly points: readonly Position[];
|
|
573
|
+
/**
|
|
574
|
+
* Index of the live point in `points` — the dragged vertex in edit, the
|
|
575
|
+
* rubber-band point in draw — or `null` when the whole geometry moves at
|
|
576
|
+
* once (box translate / rotate / scale).
|
|
577
|
+
*/
|
|
578
|
+
readonly pointIndex: number | null;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* The reason a session (`TransformSession`, `EditSession`, or `DrawSession`)
|
|
582
|
+
* settled, delivered to that session's `onSettled` handler (#191 / #194 /
|
|
583
|
+
* #197). `"close"` covers every commit path of the owning session —
|
|
584
|
+
* `TransformSession.close()` / `EditSession.close()` / `DrawSession.commit()`,
|
|
585
|
+
* a click-away close, a gesture commit (dblclick, a second click/tap on the
|
|
586
|
+
* finishing vertex, a click on another graphics-layer measure), or
|
|
587
|
+
* `TransformSession.setGraphics([])`. `"error"` is the commit-time snapshot
|
|
588
|
+
* build throwing (the outer promise rejects with that error, not a
|
|
589
|
+
* `TacticalDrawAbortError`). The remaining values replay
|
|
590
|
+
* `TacticalDrawAbortError.reason` verbatim: `"escape"`, `"signal"`,
|
|
591
|
+
* `"session"` (`session.abort()` / `event.abort()`), `"preempted"`,
|
|
592
|
+
* `"destroyed"`, and `"removed"` (edit / transform only — a measure dropped
|
|
593
|
+
* from `td.render()`'s measure set).
|
|
594
|
+
*/
|
|
595
|
+
type SettleReason = "close" | "error" | TacticalDrawAbortReason;
|
|
596
|
+
//#endregion
|
|
597
|
+
//#region src/tactical-draw/session-history.d.ts
|
|
598
|
+
type SessionChangeSource = "gesture" | "undo" | "redo" | "programmatic";
|
|
599
|
+
interface SessionHistoryState {
|
|
600
|
+
readonly canUndo: boolean;
|
|
601
|
+
readonly canRedo: boolean;
|
|
602
|
+
readonly undoLabel?: string;
|
|
603
|
+
readonly redoLabel?: string;
|
|
604
|
+
}
|
|
605
|
+
interface SessionHistory {
|
|
606
|
+
/** Current availability and optional labels. Each session retains at most 100 completed steps. */
|
|
607
|
+
readonly state: SessionHistoryState;
|
|
608
|
+
undo(): boolean;
|
|
609
|
+
redo(): boolean;
|
|
610
|
+
subscribe(listener: (state: SessionHistoryState) => void): () => void;
|
|
611
|
+
}
|
|
492
612
|
//#endregion
|
|
493
613
|
//#region src/tactical-draw/edit-session.d.ts
|
|
494
614
|
/**
|
|
@@ -497,17 +617,39 @@ type EditMode = "reshape" | "transform" | "delete";
|
|
|
497
617
|
*
|
|
498
618
|
* `measure` is the immutable edit-start input. `controlPoints` is live working
|
|
499
619
|
* state. After the session settles (`close` / `abort`), every method is a
|
|
500
|
-
* silent no-op
|
|
620
|
+
* silent no-op (except [[onSettled]] — late registration still delivers,
|
|
621
|
+
* asynchronously).
|
|
501
622
|
*/
|
|
502
623
|
interface EditSession {
|
|
624
|
+
/** Undo/redo for completed mutations within this live session. */
|
|
625
|
+
readonly history: SessionHistory;
|
|
503
626
|
/** Immutable edit-start input. */
|
|
504
|
-
readonly
|
|
627
|
+
readonly graphic: ControlMeasure;
|
|
628
|
+
/**
|
|
629
|
+
* Live complete working graphic. Copy on read. `properties` (host-owned
|
|
630
|
+
* metadata, e.g. `standardIdentity`/`colorMode`) is session-owned like every
|
|
631
|
+
* other working field: it starts at the edit-start value, is replaced
|
|
632
|
+
* wholesale by [[updateGraphic]], survives every subsequent gesture/setter
|
|
633
|
+
* call and in-session undo/redo, and is rolled back by `event.reject()`
|
|
634
|
+
* (issue #354). Absent when the edit-start measure carried none and
|
|
635
|
+
* [[updateGraphic]] has not set any since.
|
|
636
|
+
*/
|
|
637
|
+
readonly workingGraphic: ControlMeasure;
|
|
505
638
|
/** Live working state. Copy on read. */
|
|
506
639
|
readonly controlPoints: readonly Position[];
|
|
507
640
|
/** Live working options. Copy on read. */
|
|
508
641
|
readonly options: ControlMeasure["options"];
|
|
509
642
|
/** Live working style. Copy on read. */
|
|
510
643
|
readonly style: ControlMeasureStyle | undefined;
|
|
644
|
+
/**
|
|
645
|
+
* Live working text amplifiers (MIL-STD-2525E Appendix L Table L-II). Copy
|
|
646
|
+
* on read. `undefined` when the edit-start measure carried none and none
|
|
647
|
+
* have been set since. Keys are stored as given — canonicalisation to
|
|
648
|
+
* doctrinal field codes happens in the core render seam, not here.
|
|
649
|
+
*/
|
|
650
|
+
readonly textAmplifiers: TextAmplifiers | undefined;
|
|
651
|
+
/** Live user-adjusted anchors for individually rendered text amplifiers. */
|
|
652
|
+
readonly amplifierPlacements: AmplifierPlacements | undefined;
|
|
511
653
|
/** `true` after the first working-copy mutation; back to `false` within tolerance. */
|
|
512
654
|
readonly dirty: boolean;
|
|
513
655
|
readonly modes: readonly EditMode[];
|
|
@@ -518,12 +660,12 @@ interface EditSession {
|
|
|
518
660
|
*/
|
|
519
661
|
readonly sizeAnchor: SizeAnchor;
|
|
520
662
|
/**
|
|
521
|
-
* Commit: returns the `
|
|
663
|
+
* Commit: returns the `GraphicSnapshot<ControlMeasure>` of the current working state
|
|
522
664
|
* and resolves the outer `edit` promise with the same value (the same
|
|
523
665
|
* reference — no double-bake of ground-anchored sizes). Returns `undefined`
|
|
524
666
|
* after the session has settled (idempotent no-op).
|
|
525
667
|
*/
|
|
526
|
-
close():
|
|
668
|
+
close(): GraphicSnapshot<ControlMeasure> | undefined;
|
|
527
669
|
/** Reject the outer `edit` promise with reason `"session"`. */
|
|
528
670
|
abort(): void;
|
|
529
671
|
/**
|
|
@@ -534,6 +676,8 @@ interface EditSession {
|
|
|
534
676
|
* is rejected with `TypeError`.
|
|
535
677
|
*/
|
|
536
678
|
setModes(modes: readonly EditMode[]): void;
|
|
679
|
+
/** Atomically replace the live working graphic while preserving id and kind. */
|
|
680
|
+
updateGraphic(updater: (graphic: ControlMeasure) => ControlMeasure): void;
|
|
537
681
|
/** Shallow-merge per-measure generator options into the live working copy. */
|
|
538
682
|
setOptions(partial: ControlMeasure["options"]): void;
|
|
539
683
|
/**
|
|
@@ -541,10 +685,36 @@ interface EditSession {
|
|
|
541
685
|
* and on `close()`. A no-op for measures with no pixel-denominated size. The
|
|
542
686
|
* in-flight preview is unaffected (it renders from the working options), so a
|
|
543
687
|
* screen-locked symbol stays screen-locked until commit.
|
|
544
|
-
|
|
545
|
-
|
|
688
|
+
*
|
|
689
|
+
* `options` shallow-merges a size-option patch in the *same* mutation, as
|
|
690
|
+
* [[setOptions]] would. Changing a measure's size mode means moving its value
|
|
691
|
+
* between the pixel and meter halves of a size pair *and* re-anchoring, and
|
|
692
|
+
* the halves are only meaningful together: options carrying a pixel size
|
|
693
|
+
* under a `"ground"` anchor bake that size away on commit, so a host that
|
|
694
|
+
* emitted the two as separate mutations would leave an undo step parked on a
|
|
695
|
+
* state whose committed size mode contradicts its own options. Passed here
|
|
696
|
+
* they are one change event and one history entry, which undo cannot land
|
|
697
|
+
* inside.
|
|
698
|
+
*/
|
|
699
|
+
setSizeAnchor(anchor: SizeAnchor, options?: ControlMeasure["options"]): void;
|
|
546
700
|
/** Shallow-merge per-measure style into the live working copy. */
|
|
547
701
|
setStyle(partial: ControlMeasureStyle): void;
|
|
702
|
+
/**
|
|
703
|
+
* Shallow-merge into the live working text amplifiers. A value of
|
|
704
|
+
* `undefined` or an empty/whitespace-only string deletes that key; the
|
|
705
|
+
* working record collapses back to `undefined` once it holds no keys. A
|
|
706
|
+
* silent no-op after the session has settled (like [[setOptions]]).
|
|
707
|
+
*/
|
|
708
|
+
setTextAmplifiers(partial: Record<string, string | undefined>): void;
|
|
709
|
+
/**
|
|
710
|
+
* Merge field-keyed label overrides (position and/or rotation). The merge is
|
|
711
|
+
* **field-wise per key**: a bare `Position` value means `{ position }`;
|
|
712
|
+
* fields absent from the incoming value (e.g. `rotation` when only
|
|
713
|
+
* `position` is supplied) are preserved from the existing entry; an
|
|
714
|
+
* explicit `undefined` value deletes the key entirely — a full reset to the
|
|
715
|
+
* doctrinal placement (both position and rotation).
|
|
716
|
+
*/
|
|
717
|
+
setAmplifierPlacements(partial: Record<string, AmplifierPlacement | undefined>): void;
|
|
548
718
|
/**
|
|
549
719
|
* Subscribe to working-copy changes. The listener receives an
|
|
550
720
|
* [[EditChangeEvent]] for each completed gesture (drag end, insert/delete
|
|
@@ -557,6 +727,69 @@ interface EditSession {
|
|
|
557
727
|
* have run.
|
|
558
728
|
*/
|
|
559
729
|
onChange(listener: (event: EditChangeEvent) => void): () => void;
|
|
730
|
+
/**
|
|
731
|
+
* Subscribe to the committed snapshot — the single delivery channel for the
|
|
732
|
+
* `GraphicSnapshot<ControlMeasure>` this session ever commits (#197, mirroring
|
|
733
|
+
* `TransformSession.onCommit` #191). Fires exactly once, synchronously from
|
|
734
|
+
* within the [[close]] call that produced it (`session.close()`,
|
|
735
|
+
* `event.close()` from [[onChange]], a click-away close, or a click on
|
|
736
|
+
* another graphics-layer measure), with the same reference [[close]]
|
|
737
|
+
* returns and the outer `edit` promise resolves with. Never fires on abort.
|
|
738
|
+
*
|
|
739
|
+
* Listener exceptions are collected; after every listener has run, they are
|
|
740
|
+
* rethrown (one if single, `AggregateError` otherwise) — mirrors
|
|
741
|
+
* [[onChange]]. Returns an unsubscribe function; registering after the
|
|
742
|
+
* session has settled returns a no-op unsubscribe and the listener never
|
|
743
|
+
* fires.
|
|
744
|
+
*/
|
|
745
|
+
onCommit(handler: (snapshot: GraphicSnapshot<ControlMeasure>) => void): () => void;
|
|
746
|
+
/**
|
|
747
|
+
* Subscribe to the session's settlement — the lifecycle complement of
|
|
748
|
+
* [[onCommit]]: `onCommit` delivers *what* was committed, `onSettled` says
|
|
749
|
+
* the session is *over* and why (#197, mirroring
|
|
750
|
+
* `TransformSession.onSettled` #194). Fires exactly once, when the session
|
|
751
|
+
* settles by any path, commit or abort, with a `SettleReason`.
|
|
752
|
+
*
|
|
753
|
+
* - On the commit path it fires **after** [[onCommit]]'s emission, so a
|
|
754
|
+
* listener that clears selection state observes a document that already
|
|
755
|
+
* contains the committed snapshot.
|
|
756
|
+
* - On every path it fires **after** the facade has already cleared
|
|
757
|
+
* `td.activeSession` — so fold-then-render works from inside the handler.
|
|
758
|
+
* - `reason` distinguishes commit from abort but is deliberately coarse —
|
|
759
|
+
* no payload; the snapshot was already delivered through [[onCommit]].
|
|
760
|
+
*
|
|
761
|
+
* Registering after the session has already settled is **not** a no-op
|
|
762
|
+
* (unlike [[onChange]] / [[onCommit]]): the handler is invoked
|
|
763
|
+
* asynchronously (on a microtask) exactly once with the settle reason;
|
|
764
|
+
* unsubscribing before that microtask runs cancels the delivery.
|
|
765
|
+
*
|
|
766
|
+
* Listener exceptions are collected; after every listener has run, they are
|
|
767
|
+
* rethrown (one if single, `AggregateError` otherwise) — mirrors
|
|
768
|
+
* [[onChange]] / [[onCommit]]. Returns an unsubscribe function.
|
|
769
|
+
*/
|
|
770
|
+
onSettled(handler: (reason: SettleReason) => void): () => void;
|
|
771
|
+
/**
|
|
772
|
+
* Subscribe to the continuous stream of in-flight geometry mutations —
|
|
773
|
+
* vertex/midpoint drags and box scale/rotate/translate gestures — fired
|
|
774
|
+
* throughout the gesture rather than once at its end (contrast
|
|
775
|
+
* [[onChange]]). `start` fires at gesture begin, `move` on every pointer
|
|
776
|
+
* tick, and `end` fires exactly once when the gesture finishes by any
|
|
777
|
+
* path: commit, a click-slop no-op (a box body click that never becomes a
|
|
778
|
+
* drag), or session abort/teardown mid-drag. `end` fires **before** the
|
|
779
|
+
* gesture's [[onChange]] emission (and, on the commit path, before
|
|
780
|
+
* [[onCommit]]/[[onSettled]] too), so a listener observing both channels
|
|
781
|
+
* sees the transient stream close first.
|
|
782
|
+
*
|
|
783
|
+
* Label-placement drags (`labeldrag` mode) do not fire this — they move
|
|
784
|
+
* amplifier placements, not control points.
|
|
785
|
+
*
|
|
786
|
+
* Listener exceptions are collected; after every listener has run, they
|
|
787
|
+
* are rethrown (one if single, `AggregateError` otherwise) — mirrors
|
|
788
|
+
* [[onChange]]. Returns an unsubscribe function; registering after the
|
|
789
|
+
* session has settled returns a no-op unsubscribe and the listener never
|
|
790
|
+
* fires.
|
|
791
|
+
*/
|
|
792
|
+
onTransientChange(listener: (event: TransientChangeEvent) => void): () => void;
|
|
560
793
|
}
|
|
561
794
|
/**
|
|
562
795
|
* Delivered to `EditSession.onChange` listeners once per completed gesture.
|
|
@@ -567,10 +800,12 @@ interface EditSession {
|
|
|
567
800
|
* observe partial state. See PRD #40 user-stories 23–24.
|
|
568
801
|
*/
|
|
569
802
|
interface EditChangeEvent {
|
|
803
|
+
/** How this working-copy transition was initiated. */
|
|
804
|
+
readonly source: SessionChangeSource;
|
|
570
805
|
/** Working state after the completed gesture. */
|
|
571
|
-
readonly
|
|
806
|
+
readonly graphic: GraphicSnapshot<ControlMeasure>;
|
|
572
807
|
/** State immediately before this gesture. */
|
|
573
|
-
readonly previous:
|
|
808
|
+
readonly previous: GraphicSnapshot<ControlMeasure>;
|
|
574
809
|
/** The session the event belongs to. */
|
|
575
810
|
readonly session: EditSession;
|
|
576
811
|
/**
|
|
@@ -585,92 +820,157 @@ interface EditChangeEvent {
|
|
|
585
820
|
abort(): void;
|
|
586
821
|
}
|
|
587
822
|
//#endregion
|
|
823
|
+
//#region src/tactical-draw/graphic-edit-session.d.ts
|
|
824
|
+
interface GraphicEditChangeEvent<G extends Graphic = Graphic> {
|
|
825
|
+
readonly source: SessionChangeSource;
|
|
826
|
+
readonly graphic: GraphicSnapshot<G>;
|
|
827
|
+
readonly previous: GraphicSnapshot<G>;
|
|
828
|
+
readonly session: GraphicEditSession<G>;
|
|
829
|
+
reject(): void;
|
|
830
|
+
close(): void;
|
|
831
|
+
abort(): void;
|
|
832
|
+
}
|
|
833
|
+
/** Common selection/editing contract shared by control measures and point symbols. */
|
|
834
|
+
interface GraphicEditSession<G extends Graphic = Graphic> {
|
|
835
|
+
readonly history: SessionHistory;
|
|
836
|
+
/** Immutable edit-start input. */
|
|
837
|
+
readonly graphic: G;
|
|
838
|
+
/** Live working graphic. Copy on read. */
|
|
839
|
+
readonly workingGraphic: G;
|
|
840
|
+
readonly dirty: boolean;
|
|
841
|
+
readonly modes: readonly EditMode[];
|
|
842
|
+
close(): GraphicSnapshot<G> | undefined;
|
|
843
|
+
abort(): void;
|
|
844
|
+
setModes(modes: readonly EditMode[]): void;
|
|
845
|
+
/** Atomically replace any part of the live working graphic. */
|
|
846
|
+
updateGraphic(updater: (graphic: G) => G): void;
|
|
847
|
+
onChange(listener: (event: GraphicEditChangeEvent<G>) => void): () => void;
|
|
848
|
+
onCommit(handler: (snapshot: GraphicSnapshot<G>) => void): () => void;
|
|
849
|
+
onSettled(handler: (reason: SettleReason) => void): () => void;
|
|
850
|
+
}
|
|
851
|
+
//#endregion
|
|
588
852
|
//#region src/tactical-draw/transform-session.d.ts
|
|
589
853
|
/**
|
|
590
|
-
* Surface delivered via `td.editMany(
|
|
854
|
+
* Surface delivered via `td.editMany(graphics, opts).onSession` and
|
|
591
855
|
* synchronously available from `td.activeTransformSession` while the group
|
|
592
856
|
* transform is live. The group sibling of `EditSession` (`edit-session.ts`):
|
|
593
857
|
* no reshape, no per-measure `setOptions`/`setStyle` — only the shared
|
|
594
858
|
* transform box's corner-scale / rotate / body-translate gestures, applied to
|
|
595
859
|
* every member at once. See the Phase 3 ("Multi-select") slice of ADR-0017.
|
|
596
860
|
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
861
|
+
* Typed on `Graphic` (ADR-0032): a member is either a `ControlMeasure` or a
|
|
862
|
+
* point symbol, and the two coexist in one session. Hosts narrow with
|
|
863
|
+
* `isPointSymbol`. `controlPointsFor` is a control-measure accessor and returns
|
|
864
|
+
* `undefined` for a point-symbol id.
|
|
865
|
+
*
|
|
866
|
+
* The member set is mutable via [[setGraphics]] (#187): members can join or
|
|
867
|
+
* leave a live session without a session boundary — point symbols exactly like
|
|
868
|
+
* measures. Because membership changes over the session's lifetime, snapshot
|
|
869
|
+
* arrays are keyed by **graphic id**, not by index — a snapshot's identity is
|
|
870
|
+
* `snapshot.graphic.id`. `graphics` is a live view of the current members'
|
|
871
|
+
* join-time inputs; a `TransformChangeEvent`'s arrays are index-aligned with
|
|
872
|
+
* `session.graphics` *as of that emission*. After the session settles (`close`
|
|
873
|
+
* / `abort` / an emptying `setGraphics`), every method is a silent no-op
|
|
874
|
+
* (except [[onSettled]] — late registration still delivers, asynchronously).
|
|
605
875
|
*/
|
|
606
876
|
interface TransformSession {
|
|
877
|
+
/** Undo/redo for completed gestures within the current membership baseline. */
|
|
878
|
+
readonly history: SessionHistory;
|
|
607
879
|
/**
|
|
608
|
-
* Live view of the current members' join-time input
|
|
609
|
-
* on each read, in current membership order. Grows/shrinks as [[
|
|
880
|
+
* Live view of the current members' join-time input graphics — a fresh array
|
|
881
|
+
* on each read, in current membership order. Grows/shrinks as [[setGraphics]]
|
|
610
882
|
* adds or removes members. Not stable across membership changes: key
|
|
611
|
-
* snapshots by `snapshot.
|
|
883
|
+
* snapshots by `snapshot.graphic.id` rather than relying on index alignment.
|
|
884
|
+
*/
|
|
885
|
+
readonly graphics: readonly Graphic[];
|
|
886
|
+
/**
|
|
887
|
+
* Live view of the current members' working graphics — a fresh array on each
|
|
888
|
+
* read, in current membership order, reflecting every gesture applied so far.
|
|
889
|
+
* Measures carry their working control points (and any transform-frozen
|
|
890
|
+
* options); point symbols carry their working position, size value, and
|
|
891
|
+
* rotation. Copy on read.
|
|
612
892
|
*/
|
|
613
|
-
readonly
|
|
893
|
+
readonly workingGraphics: readonly Graphic[];
|
|
614
894
|
/**
|
|
615
|
-
* Live working control points for the member whose id is
|
|
616
|
-
* `undefined` when no member carries that id
|
|
895
|
+
* Live working control points for the control-measure member whose id is
|
|
896
|
+
* `graphicId`, or `undefined` when no such member carries that id — including
|
|
897
|
+
* when `graphicId` names a point symbol (which has no control points; read
|
|
898
|
+
* its working `position` from [[workingGraphics]] instead). Copy on read.
|
|
617
899
|
*/
|
|
618
|
-
controlPointsFor(
|
|
619
|
-
/** `true` when any member's working
|
|
900
|
+
controlPointsFor(graphicId: string): readonly Position[] | undefined;
|
|
901
|
+
/** `true` when any member's working state differs from its edit-start input beyond tolerance. */
|
|
620
902
|
readonly dirty: boolean;
|
|
621
903
|
/**
|
|
622
|
-
* Live size anchor, applied group-wide
|
|
623
|
-
*
|
|
904
|
+
* Live size anchor, applied group-wide to the **control-measure** members
|
|
905
|
+
* only (ADR-0020). `"ground"` bakes each measure's pixel size to meters on
|
|
906
|
+
* commit; `"screen"` keeps it zoom-aware. A point symbol's persisted
|
|
907
|
+
* pixel/meter size is its own anchor and is never rewritten by this setting.
|
|
624
908
|
* Initialised from `EditManyOptions.sizeAnchor` and mutated by [[setSizeAnchor]].
|
|
625
909
|
*/
|
|
626
910
|
readonly sizeAnchor: SizeAnchor;
|
|
627
|
-
/**
|
|
911
|
+
/**
|
|
912
|
+
* Switch the group's size anchor (ADR-0020) for its control-measure members.
|
|
913
|
+
* Takes effect on the next emitted snapshot and on `close()`. Point-symbol
|
|
914
|
+
* members are unaffected.
|
|
915
|
+
*/
|
|
628
916
|
setSizeAnchor(anchor: SizeAnchor): void;
|
|
629
917
|
/**
|
|
630
918
|
* Replace the member set in place — the primitive behind shift-click toggle,
|
|
631
919
|
* marquee replace, and single-edit → group folding, without tearing the
|
|
632
|
-
* session down (#187). Members
|
|
920
|
+
* session down (#187). Members (measures and point symbols alike) are matched
|
|
921
|
+
* to the current set by graphic id:
|
|
633
922
|
*
|
|
634
923
|
* - **Retained** members (id in both the current and next sets) carry their
|
|
635
|
-
* current working state forward; the passed
|
|
924
|
+
* current working state forward; the passed graphic object is ignored for
|
|
636
925
|
* them. Their `dirty` baseline stays their original join-time input, so a
|
|
637
926
|
* session that has already moved things stays dirty across the call.
|
|
638
927
|
* - **Removed** members (id in the current set, absent from the next) are
|
|
639
928
|
* committed at their current working state.
|
|
640
929
|
* - **Added** members (id only in the next set) join at their passed input
|
|
641
|
-
* state
|
|
642
|
-
* like initial members.
|
|
930
|
+
* state and use their host-provided Graphic-stack slot when present.
|
|
643
931
|
*
|
|
644
932
|
* The transform box re-derives from the next member set at the session's
|
|
645
933
|
* current box angle. Session-scoped state (`sizeAnchor`, box angle,
|
|
646
934
|
* non-geometry dirty) is unchanged.
|
|
647
935
|
*
|
|
936
|
+
* Calling this establishes a new session-history baseline: both undo and
|
|
937
|
+
* redo are cleared after retained members carry their working state
|
|
938
|
+
* forward. Membership changes and already-emitted exit snapshots are not
|
|
939
|
+
* undoable within the live session.
|
|
940
|
+
*
|
|
648
941
|
* **Return value** — the committed exit snapshots of the members removed *by
|
|
649
|
-
* this call*, built exactly like [[close]]'s snapshots (
|
|
650
|
-
* ground unless `sizeAnchor` is `"screen"`,
|
|
651
|
-
*
|
|
652
|
-
* removed
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
* same contract as [[close]].
|
|
657
|
-
*
|
|
942
|
+
* this call*, built exactly like [[close]]'s snapshots (a measure's pixel
|
|
943
|
+
* size baked to ground unless `sizeAnchor` is `"screen"`, a point symbol's
|
|
944
|
+
* size left untouched, then cloned and rendered); an empty array when nothing
|
|
945
|
+
* was removed. The outer `editMany` promise resolves with the snapshots of
|
|
946
|
+
* the members in the set **at close time only**. Removed members are NOT
|
|
947
|
+
* retained in its Graphic-stack slot until the host acknowledges it by
|
|
948
|
+
* folding the returned snapshot into its document and calling `td.render()`,
|
|
949
|
+
* the same contract as [[close]]. A removed member's exit snapshot is also
|
|
950
|
+
* delivered through [[onCommit]] — the recommended channel, since it covers
|
|
951
|
+
* this return value and the close-time promise resolution through a single
|
|
952
|
+
* subscription.
|
|
953
|
+
* Returns `undefined` (a silent no-op) after the session has settled.
|
|
658
954
|
*
|
|
659
|
-
* `
|
|
955
|
+
* `setGraphics([])` on a live session delegates to [[close]]: it commits every
|
|
660
956
|
* current member, resolves the promise with their snapshots, and returns that
|
|
661
|
-
* same array. Duplicate ids in `
|
|
662
|
-
* without mutating session state.
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
*
|
|
670
|
-
*
|
|
671
|
-
* the
|
|
672
|
-
|
|
673
|
-
|
|
957
|
+
* same array. Duplicate ids in `graphics` throw `TypeError` synchronously,
|
|
958
|
+
* without mutating session state. A point-symbol member entering a session
|
|
959
|
+
* whose controller was created without a point-symbol capability throws
|
|
960
|
+
* `PointSymbolCapabilityRequiredError` synchronously, also without mutating
|
|
961
|
+
* session state.
|
|
962
|
+
*/
|
|
963
|
+
setGraphics(graphics: readonly Graphic[]): GraphicSnapshot[] | undefined;
|
|
964
|
+
/**
|
|
965
|
+
* Commit: returns the members' `GraphicSnapshot[]` (the members in the set at
|
|
966
|
+
* close time) and resolves the outer `editMany` promise with the same value
|
|
967
|
+
* (the same reference — no double-bake of ground-anchored measure sizes). A
|
|
968
|
+
* member removed mid-session via [[setGraphics]] is not included here; its exit
|
|
969
|
+
* snapshot was returned from that `setGraphics` call (and delivered to
|
|
970
|
+
* [[onCommit]] at that time). Returns `undefined` after the session has
|
|
971
|
+
* settled (idempotent no-op).
|
|
972
|
+
*/
|
|
973
|
+
close(): GraphicSnapshot[] | undefined;
|
|
674
974
|
/** Reject the outer `editMany` promise with reason `"session"`. */
|
|
675
975
|
abort(): void;
|
|
676
976
|
/**
|
|
@@ -686,29 +986,84 @@ interface TransformSession {
|
|
|
686
986
|
* have run — mirrors `EditSession.onChange` (issue #49).
|
|
687
987
|
*/
|
|
688
988
|
onChange(listener: (event: TransformChangeEvent) => void): () => void;
|
|
989
|
+
/**
|
|
990
|
+
* Subscribe to committed exit-snapshot batches — the single delivery channel
|
|
991
|
+
* for every `GraphicSnapshot[]` this session ever commits (#191). Fires
|
|
992
|
+
* exactly once per batch:
|
|
993
|
+
*
|
|
994
|
+
* - The members [[setGraphics]] removes, but only when that call actually
|
|
995
|
+
* removed at least one member.
|
|
996
|
+
* - The close-time members on any commit path: [[close]], `setGraphics([])`,
|
|
997
|
+
* a click-away close, or `TransformChangeEvent.close()`.
|
|
998
|
+
*
|
|
999
|
+
* Never fires on an abort/reject path — pair it with [[onSettled]] to also
|
|
1000
|
+
* observe abort settles. Fires synchronously from within the
|
|
1001
|
+
* `setGraphics`/`close` call that produced the batch, before that call
|
|
1002
|
+
* returns, and the array delivered here is the same reference that call
|
|
1003
|
+
* returns (and, for the close batch, the same reference the `editMany`
|
|
1004
|
+
* promise resolves with). This is the recommended single fold path for exit
|
|
1005
|
+
* state — subscribe once in `onSession` and every committed snapshot, from
|
|
1006
|
+
* either delivery site, arrives here exactly once; the `setGraphics` return
|
|
1007
|
+
* value and the promise resolution are unchanged and remain valid for
|
|
1008
|
+
* backward compatibility.
|
|
1009
|
+
*
|
|
1010
|
+
* Listener exceptions are collected; after every listener has run, they are
|
|
1011
|
+
* rethrown (one if single, `AggregateError` otherwise) — mirrors
|
|
1012
|
+
* [[onChange]]. Returns an unsubscribe function; registering after the
|
|
1013
|
+
* session has settled returns a no-op unsubscribe and the listener never
|
|
1014
|
+
* fires.
|
|
1015
|
+
*/
|
|
1016
|
+
onCommit(handler: (snapshots: GraphicSnapshot[]) => void): () => void;
|
|
1017
|
+
/**
|
|
1018
|
+
* Subscribe to the session's settlement — the lifecycle complement of
|
|
1019
|
+
* [[onCommit]]: `onCommit` delivers *what* was committed, `onSettled` says
|
|
1020
|
+
* the session is *over* and why (#194). Fires exactly once, when the
|
|
1021
|
+
* session settles by any path, commit or abort, with a [[SettleReason]].
|
|
1022
|
+
*
|
|
1023
|
+
* - On commit paths it fires **after** the close batch's `onCommit`
|
|
1024
|
+
* emission, so a listener that clears selection state observes a document
|
|
1025
|
+
* that already contains the exit snapshots.
|
|
1026
|
+
* - On every path it fires **after** the facade has already cleared its
|
|
1027
|
+
* slot — `td.activeTransformSession` is `null`, so fold-then-render works
|
|
1028
|
+
* from inside the handler.
|
|
1029
|
+
* - `reason` distinguishes commit from abort but is deliberately coarse —
|
|
1030
|
+
* no payload; snapshots were already delivered through [[onCommit]].
|
|
1031
|
+
*
|
|
1032
|
+
* Registering after the session has already settled is **not** a no-op
|
|
1033
|
+
* (unlike [[onChange]] / [[onCommit]]): the handler is invoked
|
|
1034
|
+
* asynchronously (on a microtask) exactly once with the settle reason;
|
|
1035
|
+
* unsubscribing before that microtask runs cancels the delivery.
|
|
1036
|
+
*
|
|
1037
|
+
* Listener exceptions are collected; after every listener has run, they are
|
|
1038
|
+
* rethrown (one if single, `AggregateError` otherwise) — mirrors
|
|
1039
|
+
* [[onChange]] / [[onCommit]]. Returns an unsubscribe function.
|
|
1040
|
+
*/
|
|
1041
|
+
onSettled(handler: (reason: SettleReason) => void): () => void;
|
|
689
1042
|
}
|
|
690
1043
|
/**
|
|
691
1044
|
* Delivered to `TransformSession.onChange` listeners once per completed group
|
|
692
|
-
* gesture. Both `
|
|
693
|
-
* `session.
|
|
694
|
-
* relying on index alignment across emissions. A `
|
|
1045
|
+
* gesture. Both `graphics` and `previous` are index-aligned with
|
|
1046
|
+
* `session.graphics` *as of this emission*. Key by `graphic.id` rather than
|
|
1047
|
+
* relying on index alignment across emissions. A `setGraphics` call from within
|
|
695
1048
|
* a listener takes effect immediately and supersedes this emission's
|
|
696
1049
|
* `reject()` (the rollback baseline no longer matches the member set);
|
|
697
1050
|
* `close()` and `abort()` still apply, against the new membership. See
|
|
698
1051
|
* `EditChangeEvent` for the identical emission contract this mirrors.
|
|
699
1052
|
*/
|
|
700
1053
|
interface TransformChangeEvent {
|
|
701
|
-
/**
|
|
702
|
-
readonly
|
|
703
|
-
/**
|
|
704
|
-
readonly
|
|
1054
|
+
/** How this working-copy transition was initiated. */
|
|
1055
|
+
readonly source: SessionChangeSource;
|
|
1056
|
+
/** Working state after the completed gesture, index-aligned with `session.graphics` as of this emission. */
|
|
1057
|
+
readonly graphics: GraphicSnapshot[];
|
|
1058
|
+
/** State immediately before this gesture, index-aligned with `session.graphics` as of this emission. */
|
|
1059
|
+
readonly previous: GraphicSnapshot[];
|
|
705
1060
|
/** The session the event belongs to. */
|
|
706
1061
|
readonly session: TransformSession;
|
|
707
1062
|
/**
|
|
708
1063
|
* Roll every member's working preview back to `previous` after all
|
|
709
1064
|
* listeners have run. Multiple calls collapse to a single rollback.
|
|
710
1065
|
* Overridden by `close()` or `abort()` if either is also called during this
|
|
711
|
-
* emission, and dropped if a listener called `session.
|
|
1066
|
+
* emission, and dropped if a listener called `session.setGraphics` (the
|
|
712
1067
|
* rollback baseline no longer matches the member set).
|
|
713
1068
|
*/
|
|
714
1069
|
reject(): void;
|
|
@@ -724,7 +1079,8 @@ interface TransformChangeEvent {
|
|
|
724
1079
|
* kinds. Also available from `td.activeSession` while the draw is live.
|
|
725
1080
|
*
|
|
726
1081
|
* After the draw has settled — either via `commit()` returning `true` or
|
|
727
|
-
* `abort()` resolving — every method is a silent no-op
|
|
1082
|
+
* `abort()` resolving — every method is a silent no-op (except [[onSettled]]
|
|
1083
|
+
* — late registration still delivers, asynchronously).
|
|
728
1084
|
*/
|
|
729
1085
|
interface DrawSession {
|
|
730
1086
|
/** Live snapshot of the committed control points. Copy on read. */
|
|
@@ -746,7 +1102,116 @@ interface DrawSession {
|
|
|
746
1102
|
* are collected and rethrown after all listeners run.
|
|
747
1103
|
*/
|
|
748
1104
|
onChange(listener: (session: DrawSession) => void): () => void;
|
|
1105
|
+
/**
|
|
1106
|
+
* Subscribe to the committed snapshot — the single delivery channel for the
|
|
1107
|
+
* `GraphicSnapshot<ControlMeasure>` this session ever commits (#197, mirroring
|
|
1108
|
+
* `TransformSession.onCommit` #191). Fires exactly once, synchronously from
|
|
1109
|
+
* within the commit that produced it (`session.commit()`, dblclick, or a
|
|
1110
|
+
* second click/tap on the finishing vertex), with the same reference the
|
|
1111
|
+
* outer `draw` promise resolves with — the committed snapshot, not just the
|
|
1112
|
+
* measure. Never fires on abort.
|
|
1113
|
+
*
|
|
1114
|
+
* Listener exceptions are collected; after every listener has run, they are
|
|
1115
|
+
* rethrown (one if single, `AggregateError` otherwise) — mirrors
|
|
1116
|
+
* [[onChange]]. Returns an unsubscribe function; registering after the
|
|
1117
|
+
* session has settled returns a no-op unsubscribe and the listener never
|
|
1118
|
+
* fires.
|
|
1119
|
+
*/
|
|
1120
|
+
onCommit(handler: (snapshot: GraphicSnapshot<ControlMeasure>) => void): () => void;
|
|
1121
|
+
/**
|
|
1122
|
+
* Subscribe to the session's settlement — the lifecycle complement of
|
|
1123
|
+
* [[onCommit]]: `onCommit` delivers *what* was committed, `onSettled` says
|
|
1124
|
+
* the session is *over* and why (#197, mirroring
|
|
1125
|
+
* `TransformSession.onSettled` #194). Fires exactly once, when the session
|
|
1126
|
+
* settles by any path, commit or abort, with a `SettleReason`.
|
|
1127
|
+
*
|
|
1128
|
+
* - On the commit path it fires **after** [[onCommit]]'s emission.
|
|
1129
|
+
* - On every path it fires **after** the facade has already cleared
|
|
1130
|
+
* `td.activeSession` — so fold-then-render works from inside the handler.
|
|
1131
|
+
* - `reason` distinguishes commit from abort but is deliberately coarse —
|
|
1132
|
+
* no payload; the snapshot was already delivered through [[onCommit]].
|
|
1133
|
+
*
|
|
1134
|
+
* Registering after the session has already settled is **not** a no-op
|
|
1135
|
+
* (unlike [[onChange]] / [[onCommit]]): the handler is invoked
|
|
1136
|
+
* asynchronously (on a microtask) exactly once with the settle reason;
|
|
1137
|
+
* unsubscribing before that microtask runs cancels the delivery.
|
|
1138
|
+
*
|
|
1139
|
+
* Listener exceptions are collected; after every listener has run, they are
|
|
1140
|
+
* rethrown (one if single, `AggregateError` otherwise) — mirrors
|
|
1141
|
+
* [[onChange]] / [[onCommit]]. Returns an unsubscribe function.
|
|
1142
|
+
*/
|
|
1143
|
+
onSettled(handler: (reason: SettleReason) => void): () => void;
|
|
1144
|
+
/**
|
|
1145
|
+
* Subscribe to the continuous stream of rubber-band pointer ticks while the
|
|
1146
|
+
* draw is live — contrast [[onChange]], which fires once per committed
|
|
1147
|
+
* point. `start` fires on the first rubber-band tick, `move` on every
|
|
1148
|
+
* subsequent tick; a click that commits a point does not itself emit — the
|
|
1149
|
+
* next rubber-band tick after it does. `end` fires exactly once, when the
|
|
1150
|
+
* session settles (commit or abort).
|
|
1151
|
+
*
|
|
1152
|
+
* `points` is the committed points with the live rubber-band point spliced
|
|
1153
|
+
* in at its insertion index; `pointIndex` is that index.
|
|
1154
|
+
*
|
|
1155
|
+
* Listener exceptions are collected; after every listener has run, they
|
|
1156
|
+
* are rethrown (one if single, `AggregateError` otherwise) — mirrors
|
|
1157
|
+
* [[onChange]]. Returns an unsubscribe function; registering after the
|
|
1158
|
+
* session has settled returns a no-op unsubscribe and the listener never
|
|
1159
|
+
* fires.
|
|
1160
|
+
*/
|
|
1161
|
+
onTransientChange(listener: (event: TransientChangeEvent) => void): () => void;
|
|
1162
|
+
}
|
|
1163
|
+
//#endregion
|
|
1164
|
+
//#region src/tactical-draw/snapping.d.ts
|
|
1165
|
+
type SnapInteraction = "draw" | "reshape" | "translate" | "label-drag";
|
|
1166
|
+
interface SnapCandidateRequest {
|
|
1167
|
+
coordinate: Position;
|
|
1168
|
+
pixel: PixelCoordinate;
|
|
1169
|
+
interaction: SnapInteraction;
|
|
1170
|
+
graphicId?: string;
|
|
1171
|
+
controlPointIndex?: number;
|
|
1172
|
+
originalEvent?: unknown;
|
|
1173
|
+
}
|
|
1174
|
+
interface SnapCandidate {
|
|
1175
|
+
id: string;
|
|
1176
|
+
coordinate: Position;
|
|
1177
|
+
kind: string;
|
|
1178
|
+
graphicId?: string;
|
|
1179
|
+
controlPointIndex?: number;
|
|
1180
|
+
priority?: number;
|
|
749
1181
|
}
|
|
1182
|
+
type SnapCandidateProvider = (request: SnapCandidateRequest) => readonly SnapCandidate[];
|
|
1183
|
+
interface SnapResult {
|
|
1184
|
+
coordinate: Position;
|
|
1185
|
+
pixel: PixelCoordinate;
|
|
1186
|
+
candidate: SnapCandidate;
|
|
1187
|
+
distancePixels: number;
|
|
1188
|
+
}
|
|
1189
|
+
interface GraphicGeometrySnappingOptions {
|
|
1190
|
+
/** Snap to the nearest point along generated linework and polygon boundaries. Default `true`. */
|
|
1191
|
+
edges?: boolean;
|
|
1192
|
+
/** Snap to generated geometry vertices. Default `false`. */
|
|
1193
|
+
vertices?: boolean;
|
|
1194
|
+
}
|
|
1195
|
+
interface SnappingOptions {
|
|
1196
|
+
enabled?: boolean;
|
|
1197
|
+
tolerancePixels?: number;
|
|
1198
|
+
sources?: {
|
|
1199
|
+
graphics?: boolean;
|
|
1200
|
+
/**
|
|
1201
|
+
* Snap to generated control-measure geometry. `true` enables edges with
|
|
1202
|
+
* vertices disabled; pass an object to select either candidate type.
|
|
1203
|
+
* Text/label point features and the active graphic are excluded.
|
|
1204
|
+
*/
|
|
1205
|
+
graphicGeometry?: boolean | GraphicGeometrySnappingOptions;
|
|
1206
|
+
external?: SnapCandidateProvider;
|
|
1207
|
+
};
|
|
1208
|
+
bypass?: (originalEvent: unknown) => boolean;
|
|
1209
|
+
/** Called for each resolution attempt; `null` clears a host-rendered indicator. */
|
|
1210
|
+
onSnap?: (result: SnapResult | null) => void;
|
|
1211
|
+
}
|
|
1212
|
+
//#endregion
|
|
1213
|
+
//#region src/tactical-draw/point-symbol-draw-controller.d.ts
|
|
1214
|
+
type DrawPointSymbolDraft = Pick<PointSymbol, "kind" | "sidc"> & Partial<Omit<PointSymbol, "kind" | "sidc">>;
|
|
750
1215
|
//#endregion
|
|
751
1216
|
//#region src/tactical-draw/tactical-draw.d.ts
|
|
752
1217
|
/**
|
|
@@ -755,12 +1220,32 @@ interface DrawSession {
|
|
|
755
1220
|
*/
|
|
756
1221
|
interface TacticalDrawLayerOptions {
|
|
757
1222
|
graphics?: LayerId;
|
|
1223
|
+
/**
|
|
1224
|
+
* Passive selection-highlight outlines ({@link TacticalDraw.setHighlightedGraphics}).
|
|
1225
|
+
* Resolved immediately above `graphics` so highlight boxes stack over base
|
|
1226
|
+
* graphics but below the preview / guide / handles interaction chrome.
|
|
1227
|
+
*/
|
|
1228
|
+
highlight?: LayerId;
|
|
758
1229
|
preview?: LayerId;
|
|
759
1230
|
guide?: LayerId;
|
|
760
1231
|
handles?: LayerId;
|
|
761
1232
|
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Result of {@link TacticalDraw.ownsInteractionAt} — which layer claimed the
|
|
1235
|
+
* pixel and the feature that matched.
|
|
1236
|
+
*/
|
|
1237
|
+
interface InteractionHit {
|
|
1238
|
+
/** Which TacticalDraw layer owns the pixel — topmost match wins. */
|
|
1239
|
+
layer: "handles" | "preview" | "graphics";
|
|
1240
|
+
/** The hit feature (do not mutate). */
|
|
1241
|
+
feature: Feature;
|
|
1242
|
+
/** Control-measure id when the hit feature belongs to one (graphics/preview parts). */
|
|
1243
|
+
measureId?: string;
|
|
1244
|
+
}
|
|
762
1245
|
interface TacticalDrawOptions {
|
|
763
1246
|
layers?: TacticalDrawLayerOptions;
|
|
1247
|
+
/** Optional renderer capability for first-class point symbols (ADR-0029). */
|
|
1248
|
+
pointSymbols?: PointSymbolCapability;
|
|
764
1249
|
/**
|
|
765
1250
|
* Façade-level default style passed through to `renderControlMeasure` as the
|
|
766
1251
|
* third (lowest priority) style layer per StyleResolver (#44). Shallow-merged
|
|
@@ -781,6 +1266,8 @@ interface TacticalDrawOptions {
|
|
|
781
1266
|
* the draw API itself are deferred (PRD #40 user-story-5 note).
|
|
782
1267
|
*/
|
|
783
1268
|
generateId?: () => string;
|
|
1269
|
+
/** Optional engine-level snapping for draw and edit interactions. */
|
|
1270
|
+
snapping?: SnappingOptions;
|
|
784
1271
|
}
|
|
785
1272
|
interface EditOptions {
|
|
786
1273
|
signal?: AbortSignal;
|
|
@@ -788,7 +1275,7 @@ interface EditOptions {
|
|
|
788
1275
|
* Called synchronously once when the edit starts, before any pointer event
|
|
789
1276
|
* is processed. Lets a host wire commit / cancel buttons to the active edit.
|
|
790
1277
|
*/
|
|
791
|
-
onSession?: (session: EditSession) => void;
|
|
1278
|
+
onSession?: (session: EditSession | GraphicEditSession<PointSymbol>) => void;
|
|
792
1279
|
/**
|
|
793
1280
|
* Default `true`. When `true`, a click on empty map (or on another graphics
|
|
794
1281
|
* layer measure) closes the active edit. Clicks on preview / handle
|
|
@@ -801,7 +1288,8 @@ interface EditOptions {
|
|
|
801
1288
|
* renders vertex / midpoint handles; `transform` renders the oriented
|
|
802
1289
|
* selection box — 4 corner scale handles, a rotate grip above the top edge,
|
|
803
1290
|
* and drag-anywhere-inside-the-box translate (ADR-0017). The two can be
|
|
804
|
-
* combined freely (the default does). `"
|
|
1291
|
+
* combined freely (the default does). `"labeldrag"` opt-in enables repositioning
|
|
1292
|
+
* rendered text labels. `"delete"` is exclusive: it collapses
|
|
805
1293
|
* the set to vertex-tap removal only. An unknown mode throws `TypeError`.
|
|
806
1294
|
*/
|
|
807
1295
|
modes?: readonly EditMode[];
|
|
@@ -878,9 +1366,13 @@ type DrawMeasureDraft<K extends ControlMeasureKind = ControlMeasureKind> = {
|
|
|
878
1366
|
interface DrawOptions {
|
|
879
1367
|
signal?: AbortSignal;
|
|
880
1368
|
/**
|
|
881
|
-
* Called synchronously once when a
|
|
882
|
-
*
|
|
883
|
-
*
|
|
1369
|
+
* Called synchronously once when a draw starts, before any pointer event
|
|
1370
|
+
* is processed. The session lets a host wire a Done/Cancel toolbar to the
|
|
1371
|
+
* active draw and subscribe to live control-point / transient-geometry
|
|
1372
|
+
* feedback. Surfaced for both variable- and fixed-length kinds — a
|
|
1373
|
+
* fixed-length session auto-commits on the last required click, so its
|
|
1374
|
+
* main value is `onTransientChange` (e.g. a live radius readout) and
|
|
1375
|
+
* `abort()`.
|
|
884
1376
|
*/
|
|
885
1377
|
onSession?: (session: DrawSession) => void;
|
|
886
1378
|
/**
|
|
@@ -920,6 +1412,7 @@ interface DrawOptions {
|
|
|
920
1412
|
declare class TacticalDraw {
|
|
921
1413
|
private readonly adapter;
|
|
922
1414
|
private readonly graphicsLayer;
|
|
1415
|
+
private readonly highlightLayer;
|
|
923
1416
|
private readonly previewLayer;
|
|
924
1417
|
private readonly guideLayer;
|
|
925
1418
|
private readonly handlesLayer;
|
|
@@ -927,6 +1420,10 @@ declare class TacticalDraw {
|
|
|
927
1420
|
private readonly graphicsStyle;
|
|
928
1421
|
private readonly defaultInteractionStyle;
|
|
929
1422
|
private readonly generateId;
|
|
1423
|
+
private readonly pointSymbols;
|
|
1424
|
+
private snappingOptions;
|
|
1425
|
+
private readonly resolveSnap;
|
|
1426
|
+
private readonly graphicStack;
|
|
930
1427
|
private destroyed;
|
|
931
1428
|
private hasRendered;
|
|
932
1429
|
private readonly activeInteraction;
|
|
@@ -941,7 +1438,7 @@ declare class TacticalDraw {
|
|
|
941
1438
|
* Tracked alongside `activeInteraction` whenever the interaction is a group
|
|
942
1439
|
* transform (`editMany`). The `render()` / view-change paths need every
|
|
943
1440
|
* member's id (to exclude all of them from reconcile) — mirrors `activeEdit`
|
|
944
|
-
* but for `TransformHandle`'s `
|
|
1441
|
+
* but for `TransformHandle`'s `graphicIds` array.
|
|
945
1442
|
*/
|
|
946
1443
|
private activeTransform;
|
|
947
1444
|
/**
|
|
@@ -952,7 +1449,7 @@ declare class TacticalDraw {
|
|
|
952
1449
|
*/
|
|
953
1450
|
private activeTransformSessionRef;
|
|
954
1451
|
/**
|
|
955
|
-
* Host pick handlers registered via `td.onPick` / `td.
|
|
1452
|
+
* Host pick handlers registered via `td.onPick` / `td.onGraphicPick`. The
|
|
956
1453
|
* façade subscribes once to `adapter.onPick` per layer and routes events
|
|
957
1454
|
* through this map so that close-then-pick ordering during an active edit can
|
|
958
1455
|
* be enforced (issue #52).
|
|
@@ -970,38 +1467,96 @@ declare class TacticalDraw {
|
|
|
970
1467
|
private idCounter;
|
|
971
1468
|
private editCounter;
|
|
972
1469
|
private transformCounter;
|
|
1470
|
+
/** Snapshot of the host's latest authoritative Graphic membership and order. */
|
|
1471
|
+
private lastRenderedMeasures;
|
|
1472
|
+
private snapGeometryFeatures;
|
|
973
1473
|
/**
|
|
974
|
-
*
|
|
975
|
-
*
|
|
976
|
-
*
|
|
977
|
-
* surrounding collection state.
|
|
1474
|
+
* Rendered graphics-layer groups, keyed on Graphic snapshot identity. See
|
|
1475
|
+
* {@link renderGraphicGroup} for the invalidation contract; swapped for a
|
|
1476
|
+
* fresh map (not cleared entry-by-entry) so stale entries are collectable.
|
|
978
1477
|
*/
|
|
979
|
-
private
|
|
980
|
-
|
|
1478
|
+
private graphicGroupCache;
|
|
1479
|
+
/**
|
|
1480
|
+
* Last {@link PointSymbolCapability.generation} observed from `pointSymbols`,
|
|
1481
|
+
* initialised from the capability at construction. Compared at the top of
|
|
1482
|
+
* every {@link reconcileGraphicsStack} so a host calling `invalidate()` after
|
|
1483
|
+
* changing renderer-global config (e.g. milsymbol options) drops the render
|
|
1484
|
+
* cache even though the Graphic snapshots handed to `render()` are
|
|
1485
|
+
* unchanged — otherwise the cache would keep serving pre-invalidation
|
|
1486
|
+
* output and the capability would never be re-consulted. `undefined` when
|
|
1487
|
+
* there is no capability, or the capability doesn't expose `generation`.
|
|
1488
|
+
*/
|
|
1489
|
+
private lastSeenPointSymbolGeneration;
|
|
981
1490
|
/**
|
|
982
|
-
*
|
|
983
|
-
*
|
|
984
|
-
*
|
|
985
|
-
* `render()`
|
|
986
|
-
* `restoreOriginalToGraphics` (no exclusion, the edit is ending).
|
|
1491
|
+
* Ids of graphics currently carrying a passive selection-highlight outline
|
|
1492
|
+
* ({@link setHighlightedGraphics}). Declarative set-replace, no session —
|
|
1493
|
+
* re-projected on every view change and re-evaluated at the end of every
|
|
1494
|
+
* `render()` so outlines track geometry, style, and dropped graphics.
|
|
987
1495
|
*/
|
|
988
|
-
private
|
|
1496
|
+
private highlightedIds;
|
|
1497
|
+
/**
|
|
1498
|
+
* Whether the highlight layer currently holds any outline. Lets
|
|
1499
|
+
* `renderHighlights` skip a redundant empty write on every view change /
|
|
1500
|
+
* render while no highlights are set, but still emit the one clearing write
|
|
1501
|
+
* that removes outlines when the set transitions to empty.
|
|
1502
|
+
*/
|
|
1503
|
+
private hasHighlightOutlines;
|
|
1504
|
+
constructor(adapter: MapAdapter, options?: TacticalDrawOptions);
|
|
1505
|
+
/** Replace snapping configuration without recreating the map adapter or façade. */
|
|
1506
|
+
setSnappingOptions(options?: SnappingOptions): void;
|
|
1507
|
+
/** Clear transient host affordances when snapping or its interaction ends. */
|
|
1508
|
+
private clearSnapResult;
|
|
1509
|
+
/**
|
|
1510
|
+
* Render one atomic Graphic group for the engine-neutral graphics stack,
|
|
1511
|
+
* reusing the previous render when nothing that feeds it has changed.
|
|
1512
|
+
*
|
|
1513
|
+
* The cache is keyed on the Graphic snapshot's object identity: hosts hand
|
|
1514
|
+
* `render()` a fresh object whenever a graphic's data changes, so a changed
|
|
1515
|
+
* graphic misses by construction. (A host that mutates a Graphic in place and
|
|
1516
|
+
* re-renders the same reference is outside the contract — `render()` has
|
|
1517
|
+
* always been documented as taking snapshots.) Rendered output also depends
|
|
1518
|
+
* on live view state and on the resolved graphics style; the style is fixed
|
|
1519
|
+
* at construction, and every view change drops the whole cache. Crucially,
|
|
1520
|
+
* the view cannot change during a box drag (drag-pan is disabled), so every
|
|
1521
|
+
* graphic outside the selection stays a cache hit for the whole gesture and
|
|
1522
|
+
* per-frame reconcile cost becomes O(selection) rather than O(document).
|
|
1523
|
+
*/
|
|
1524
|
+
private renderGraphicGroup;
|
|
1525
|
+
/**
|
|
1526
|
+
* Render a Graphic group from scratch and stamp it with a fresh revision,
|
|
1527
|
+
* drawn from the module-scoped {@link nextGraphicGroupRevision} counter (see
|
|
1528
|
+
* its doc comment for why it isn't a per-instance field).
|
|
1529
|
+
*/
|
|
1530
|
+
private buildGraphicGroup;
|
|
1531
|
+
/**
|
|
1532
|
+
* Drop every cached render. Called whenever an input shared by all graphic
|
|
1533
|
+
* renders changes — the live view state, or (via {@link reconcileGraphicsStack})
|
|
1534
|
+
* the point-symbol capability's `generation`.
|
|
1535
|
+
*/
|
|
1536
|
+
private invalidateGraphicGroupCache;
|
|
1537
|
+
/** Submit one complete grouped desired state for the host stack plus overrides. */
|
|
1538
|
+
private reconcileGraphicsStack;
|
|
1539
|
+
/** Atomically publish several live renders into their host-owned Graphic slots. */
|
|
1540
|
+
private publishWorkingGraphics;
|
|
1541
|
+
/** Settle several stack overrides in one graphics-layer reconciliation. */
|
|
1542
|
+
private settleWorkingGraphics;
|
|
989
1543
|
/**
|
|
990
1544
|
* Re-render the graphics layer on a view (zoom) change so pixel-denominated
|
|
991
|
-
* measures rescale live. Skipped entirely
|
|
992
|
-
*
|
|
993
|
-
*
|
|
994
|
-
*
|
|
995
|
-
*
|
|
1545
|
+
* and not-yet-committed label-sized measures rescale live. Skipped entirely
|
|
1546
|
+
* when nothing rendered still reads live view state — meter geometry with a
|
|
1547
|
+
* captured label size is zoom-independent, so a rebuild would be wasted
|
|
1548
|
+
* layer writes. A single-control-measure edit publishes through the stack
|
|
1549
|
+
* coordinator; point symbols and transform members already present in the
|
|
1550
|
+
* host stack do the same. Graphics without a host slot remain on preview.
|
|
996
1551
|
*/
|
|
997
1552
|
private readonly handleViewChange;
|
|
998
1553
|
/**
|
|
999
|
-
* Every
|
|
1000
|
-
*
|
|
1001
|
-
* reconciliation because the preview layer (not graphics) owns their
|
|
1002
|
-
* working copy. Empty while idle.
|
|
1554
|
+
* Every Graphic id owned by an active interaction. Used for highlight and
|
|
1555
|
+
* pick routing; not every such Graphic is excluded from stack reconciliation.
|
|
1003
1556
|
*/
|
|
1004
1557
|
private excludedInteractionIds;
|
|
1558
|
+
/** Interaction ids still rendered on the legacy preview surface. */
|
|
1559
|
+
private graphicsStackExcludedIds;
|
|
1005
1560
|
/**
|
|
1006
1561
|
* Live member ids of the active group transform, or `undefined` when none.
|
|
1007
1562
|
* Falls back to the session ref because `onSession` fires from inside the
|
|
@@ -1015,27 +1570,55 @@ declare class TacticalDraw {
|
|
|
1015
1570
|
* Idempotent. Throws synchronously on duplicate measure ids *before*
|
|
1016
1571
|
* mutating any layer. An empty list before any prior render is a no-op.
|
|
1017
1572
|
*
|
|
1018
|
-
* Cross-layer semantics with an active edit
|
|
1019
|
-
* - The
|
|
1020
|
-
*
|
|
1021
|
-
* must stay in place.
|
|
1573
|
+
* Cross-layer semantics with an active edit:
|
|
1574
|
+
* - The stack coordinator replaces the edited Graphic's host-owned slot
|
|
1575
|
+
* with its working render; membership and order remain host-authoritative.
|
|
1022
1576
|
* - If the edited measure is absent from `measures`, the pending `edit`
|
|
1023
1577
|
* promise rejects on the **next microtask** with reason `"removed"`.
|
|
1024
1578
|
* `render` itself returns normally so host collection ops stay synchronous.
|
|
1025
|
-
* - If the edited measure is present and its `style` changed,
|
|
1026
|
-
* picks up the new style on this render. `controlPoints` changes on the
|
|
1579
|
+
* - If the edited measure is present and its `style` changed, its working
|
|
1580
|
+
* group picks up the new style on this render. `controlPoints` changes on the
|
|
1027
1581
|
* edited measure are routed nowhere — the user's in-flight drag is never
|
|
1028
|
-
* yanked out from under them.
|
|
1582
|
+
* yanked out from under them. A point symbol's complete working group remains
|
|
1583
|
+
* authoritative until settle, including any changed icon resource.
|
|
1029
1584
|
*
|
|
1030
1585
|
* Cross-layer semantics with an active draw: `render` only touches the
|
|
1031
1586
|
* graphics layer, so the draw's preview is undisturbed.
|
|
1032
1587
|
*
|
|
1033
1588
|
* Reentrancy: calling `td.render(...)` from inside `EditSession.onChange` is
|
|
1034
|
-
* safe — `render` never re-enters the active edit or its
|
|
1035
|
-
*
|
|
1589
|
+
* safe — `render` never re-enters the active edit or its handle layer (the
|
|
1590
|
+
* only edit-facing side effect, `notifyExternalMeasure`, is a
|
|
1036
1591
|
* pure style replay).
|
|
1037
1592
|
*/
|
|
1038
|
-
render(measures: readonly
|
|
1593
|
+
render(measures: readonly Graphic[]): void;
|
|
1594
|
+
/**
|
|
1595
|
+
* Replace the passive selection-highlight set with `ids`. Declarative and
|
|
1596
|
+
* idempotent (mirrors {@link render}): a non-interactive outline box is drawn
|
|
1597
|
+
* around each still-rendered graphic on the dedicated highlight layer — no
|
|
1598
|
+
* draw / edit / transform session, no promise, no pick subscription. Passing
|
|
1599
|
+
* `[]` clears every highlight. Unknown ids and ids currently active in an
|
|
1600
|
+
* active edit / transform session are skipped (the live session's own visuals
|
|
1601
|
+
* supersede the passive outline). Hosts use it for Excalidraw-style
|
|
1602
|
+
* "locked selection" chrome.
|
|
1603
|
+
*/
|
|
1604
|
+
setHighlightedGraphics(ids: readonly string[]): void;
|
|
1605
|
+
/**
|
|
1606
|
+
* Project the current {@link highlightedIds} into one outline `LineString`
|
|
1607
|
+
* per graphic and reconcile them onto the highlight layer. Idempotent and
|
|
1608
|
+
* grouped by graphic id, so desired-state replacement removes stale outlines
|
|
1609
|
+
* and an unchanged set is a cheap no-op write. Skips ids that are
|
|
1610
|
+
* unknown (not in the last render) or active in an interaction session, and
|
|
1611
|
+
* bails gracefully when the adapter cannot invert pixels to coordinates
|
|
1612
|
+
* (`getCoordinateFromPixel` is optional in the MapAdapter ABI).
|
|
1613
|
+
*/
|
|
1614
|
+
private renderHighlights;
|
|
1615
|
+
/**
|
|
1616
|
+
* Fit the passive-highlight transform box for one graphic: the point-symbol
|
|
1617
|
+
* footprint for symbols, the snug text footprint for Text graphics, and the
|
|
1618
|
+
* projected-geometry bounding box (at the current bearing) for every other
|
|
1619
|
+
* control measure. Returns `null` when nothing projects this frame.
|
|
1620
|
+
*/
|
|
1621
|
+
private highlightBoxForGraphic;
|
|
1039
1622
|
/**
|
|
1040
1623
|
* Nested-call guard + preempt, shared by `draw` and `edit`. Returns a
|
|
1041
1624
|
* pre-rejected promise when called re-entrantly from inside another
|
|
@@ -1070,42 +1653,102 @@ declare class TacticalDraw {
|
|
|
1070
1653
|
*/
|
|
1071
1654
|
private trackTransformSession;
|
|
1072
1655
|
/**
|
|
1073
|
-
* Start an interactive draw.
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
1076
|
-
*
|
|
1077
|
-
*
|
|
1656
|
+
* Start an interactive draw. Both fixed- and variable-length kinds surface
|
|
1657
|
+
* a `DrawSession` via `opts.onSession`. Fixed-length kinds commit
|
|
1658
|
+
* automatically on the last required click; variable-length kinds commit
|
|
1659
|
+
* on `session.commit()`, `dblclick`, or a second click on the last added
|
|
1660
|
+
* control point. All paths reject with `TacticalDrawAbortError` on
|
|
1661
|
+
* signal / Escape / destroy / `session.abort()`.
|
|
1662
|
+
*
|
|
1663
|
+
* `DrawSession.onCommit` is the single delivery channel for the committed
|
|
1664
|
+
* snapshot and `DrawSession.onSettled` is the lifecycle signal for when the
|
|
1665
|
+
* session is over and why (#197) — together they make the returned promise
|
|
1666
|
+
* non-load-bearing for hosts that adopt them. `DrawSession.onTransientChange`
|
|
1667
|
+
* streams live rubber-band geometry while the draw is in flight, for both
|
|
1668
|
+
* kinds of session.
|
|
1078
1669
|
*/
|
|
1079
|
-
draw
|
|
1670
|
+
draw(draft: DrawPointSymbolDraft, options?: DrawOptions): Promise<GraphicSnapshot<PointSymbol>>;
|
|
1671
|
+
draw<K extends ControlMeasureKind>(draft: DrawMeasureDraft<K>, options?: DrawOptions): Promise<GraphicSnapshot<ControlMeasure<K>>>;
|
|
1080
1672
|
/**
|
|
1081
1673
|
* Start an interactive edit on a single control measure. Returns a Promise
|
|
1082
|
-
* that resolves with a `
|
|
1674
|
+
* that resolves with a `GraphicSnapshot<ControlMeasure>` of the working state on
|
|
1083
1675
|
* `session.close()` and rejects with `TacticalDrawAbortError` on
|
|
1084
1676
|
* signal / Escape / destroy / preempt / `session.abort()`.
|
|
1085
1677
|
*
|
|
1086
1678
|
* Default click-away (`closeOnClickAway: true`) closes the edit on a click
|
|
1087
1679
|
* outside the preview / handle features. Hosts that own close policy pass
|
|
1088
1680
|
* `false`.
|
|
1681
|
+
*
|
|
1682
|
+
* `EditSession.onCommit` is the single delivery channel for the committed
|
|
1683
|
+
* snapshot and `EditSession.onSettled` is the lifecycle signal for when the
|
|
1684
|
+
* session is over and why (#197) — together they make the returned promise
|
|
1685
|
+
* non-load-bearing for hosts that adopt them.
|
|
1089
1686
|
*/
|
|
1090
|
-
edit(
|
|
1687
|
+
edit(graphic: PointSymbol, options?: EditOptions): Promise<GraphicSnapshot<PointSymbol>>;
|
|
1688
|
+
edit(measure: ControlMeasure, options?: EditOptions): Promise<GraphicSnapshot<ControlMeasure>>;
|
|
1091
1689
|
/**
|
|
1092
1690
|
* Start an interactive group transform (multi-select) over `measures`
|
|
1093
|
-
* (ADR-0017 "Multi-select" slice). Every member
|
|
1094
|
-
*
|
|
1095
|
-
*
|
|
1096
|
-
*
|
|
1691
|
+
* (ADR-0017 "Multi-select" slice). Every host-provided member stays in its
|
|
1692
|
+
* Graphic-stack slot; only a member without a host slot uses preview. The box
|
|
1693
|
+
* (corner scale, rotate grip, drag-anywhere body translate) applies rigidly
|
|
1694
|
+
* to all of them at once. Returns a Promise that
|
|
1695
|
+
* resolves with `GraphicSnapshot<ControlMeasure>[]` on `session.close()` and rejects
|
|
1097
1696
|
* with `TacticalDrawAbortError` on signal / Escape / destroy / preempt /
|
|
1098
|
-
* `session.abort()`. Key the resolved snapshots by `snapshot.
|
|
1099
|
-
* the member set is mutable (`session.
|
|
1697
|
+
* `session.abort()`. Key the resolved snapshots by `snapshot.graphic.id`:
|
|
1698
|
+
* the member set is mutable (`session.setGraphics`, #187) so snapshot arrays
|
|
1100
1699
|
* are id-keyed, not index-aligned with this input. The promise resolves with
|
|
1101
1700
|
* the members in the set at close time; a member removed mid-session had its
|
|
1102
|
-
* exit snapshot returned from the `
|
|
1701
|
+
* exit snapshot returned from the `setGraphics` call that removed it.
|
|
1103
1702
|
*
|
|
1104
1703
|
* `measures.length` must be >= 1; a single-element array behaves like an
|
|
1105
1704
|
* `edit` with only `transform` mode. Throws `TypeError` synchronously (before
|
|
1106
1705
|
* any preemption) for an empty array or duplicate ids.
|
|
1107
|
-
|
|
1108
|
-
|
|
1706
|
+
*
|
|
1707
|
+
* For a selection that changes over time (shift-click toggle, marquee
|
|
1708
|
+
* replace), prefer [[syncTransformGraphics]] over hand-rolling the
|
|
1709
|
+
* live-session-vs-fresh-`editMany` branch, and subscribe to
|
|
1710
|
+
* `TransformSession.onCommit` as the single fold path for exit snapshots
|
|
1711
|
+
* and `TransformSession.onSettled` as the lifecycle signal for when the
|
|
1712
|
+
* session is over and why.
|
|
1713
|
+
*/
|
|
1714
|
+
editMany(measures: ControlMeasure[], options?: EditManyOptions): Promise<GraphicSnapshot<ControlMeasure>[]>;
|
|
1715
|
+
editMany(graphics: Graphic[], options?: EditManyOptions): Promise<GraphicSnapshot[]>;
|
|
1716
|
+
/**
|
|
1717
|
+
* One-call selection-sync: mutate a live transform's member set in place, or
|
|
1718
|
+
* start a fresh `editMany` when none is live (#191). Any mix of control
|
|
1719
|
+
* measures and point symbols is handled by the one Graphic-typed session
|
|
1720
|
+
* (ADR-0032): a point symbol joins or leaves the live session exactly like a
|
|
1721
|
+
* measure via `setGraphics`, preserving box angle, size anchor, and history
|
|
1722
|
+
* baseline — no preempt-and-restart. A point symbol in the selection with no
|
|
1723
|
+
* point-symbol capability configured throws `PointSymbolCapabilityRequiredError`
|
|
1724
|
+
* synchronously (from `setGraphics` on a live session, or from `editMany` on a
|
|
1725
|
+
* fresh one).
|
|
1726
|
+
*
|
|
1727
|
+
* The facade knows session liveness synchronously — `activeTransformSessionRef`
|
|
1728
|
+
* is cleared inside the settle path, before control returns to host code — so
|
|
1729
|
+
* unlike a host's own `activeTransformSession ? setGraphics : editMany` check,
|
|
1730
|
+
* this can never observe a stale ref pointing at an already-settled session.
|
|
1731
|
+
* The settled-race fall-through hosts had to hand-roll for that gap is owned
|
|
1732
|
+
* here instead: a `setGraphics` call that (implausibly) still returns
|
|
1733
|
+
* `undefined` falls through to a fresh `editMany` rather than silently
|
|
1734
|
+
* dropping the selection.
|
|
1735
|
+
*
|
|
1736
|
+
* `graphics: []` closes any live session (committing every member) and is a
|
|
1737
|
+
* no-op while idle. With a live session, `options` is ignored — session-scoped
|
|
1738
|
+
* state (`sizeAnchor`, box angle, close-on-click-away policy) is sticky per
|
|
1739
|
+
* `setGraphics`'s contract; `options` applies only when this call starts a
|
|
1740
|
+
* fresh `editMany`. Exit snapshots are delivered via the session's
|
|
1741
|
+
* `onCommit` — subscribe once in `options.onSession`. Session lifecycle —
|
|
1742
|
+
* including an Escape / preempt / destroy abort the host did not itself
|
|
1743
|
+
* initiate — is observed via the session's `onSettled` (#194); together
|
|
1744
|
+
* `onCommit` and `onSettled` make the underlying `editMany` promise
|
|
1745
|
+
* irrelevant to hosts. That promise is owned by the facade; its abort
|
|
1746
|
+
* rejections are swallowed here (non-abort errors surface as unhandled
|
|
1747
|
+
* rejections). Throws `TypeError` synchronously on duplicate ids, from
|
|
1748
|
+
* either path. Starting a fresh transform preempts any other in-flight
|
|
1749
|
+
* draw/edit exactly like `editMany`.
|
|
1750
|
+
*/
|
|
1751
|
+
syncTransformGraphics(graphics: readonly Graphic[], options?: EditManyOptions): void;
|
|
1109
1752
|
/**
|
|
1110
1753
|
* Subscribe to picks on committed control measures.
|
|
1111
1754
|
*
|
|
@@ -1117,7 +1760,7 @@ declare class TacticalDraw {
|
|
|
1117
1760
|
* Returns an idempotent unsubscribe function. An optional `signal` removes
|
|
1118
1761
|
* the handler when aborted.
|
|
1119
1762
|
*/
|
|
1120
|
-
|
|
1763
|
+
onGraphicPick(handler: PickHandler, opts?: PickOptions): () => void;
|
|
1121
1764
|
/**
|
|
1122
1765
|
* Subscribe to pick events on `layerId`. Routed through the façade so the
|
|
1123
1766
|
* close-then-pick contract (issue #52) is observable: when an active edit
|
|
@@ -1129,6 +1772,51 @@ declare class TacticalDraw {
|
|
|
1129
1772
|
* Returns an unsubscribe function. Optional `signal` mirrors `adapter.onPick`.
|
|
1130
1773
|
*/
|
|
1131
1774
|
onPick(layerId: LayerId, handler: PickHandler, opts?: PickOptions): () => void;
|
|
1775
|
+
/**
|
|
1776
|
+
* Stateless hit test answering "does TacticalDraw own the pixel at
|
|
1777
|
+
* `pixel`?" — for hosts composing their own click-away / deselection logic
|
|
1778
|
+
* (issue #216) alongside `TacticalDraw`'s draw/edit sessions. Today hosts
|
|
1779
|
+
* hack this together from the `@internal` `layerIds` plus engine-specific
|
|
1780
|
+
* layer queries; this is the supported replacement.
|
|
1781
|
+
*
|
|
1782
|
+
* Tests, topmost first, the three interactive layers — `handlesLayer`,
|
|
1783
|
+
* then `previewLayer`, then `graphicsLayer` — against each layer's current
|
|
1784
|
+
* `adapter.getLayerFeatures(...)` cache. The `guideLayer` is deliberately
|
|
1785
|
+
* excluded: it is non-interactive draw-guide scaffolding, never a
|
|
1786
|
+
* legitimate "owned" click target. Within each layer, candidates follow
|
|
1787
|
+
* the shared visible-intent ranking (direct marks, then filled interiors,
|
|
1788
|
+
* then inferred outline-only reach, distance within direct marks, then
|
|
1789
|
+
* visual topmost order) — this explicit-affordance layer order is the
|
|
1790
|
+
* tier above all of it.
|
|
1791
|
+
*
|
|
1792
|
+
* `preview` and `graphics` each get a label text-extent fallback after the
|
|
1793
|
+
* geometry pass misses on that layer: for every `Point` feature carrying a
|
|
1794
|
+
* string `properties.text`, the anchor is projected and
|
|
1795
|
+
* `fitLabelBoxFromFeature` (shared with the edit controller's `pickLabel`,
|
|
1796
|
+
* see commit f908434) estimates the rendered text's box; a pixel inside
|
|
1797
|
+
* that box counts as owned even though it's off the anchor point itself.
|
|
1798
|
+
* This matters most for the active preview, since a click can land
|
|
1799
|
+
* anywhere across a rotated/offset label during an edit.
|
|
1800
|
+
*
|
|
1801
|
+
* `tolerance` defaults to `pickHitTolerance(options?.originalEvent)` —
|
|
1802
|
+
* pass the host's raw click event as `originalEvent` to get the same
|
|
1803
|
+
* pointer-type-aware tolerance (4px mouse/pen, 12px touch) `onPick` uses;
|
|
1804
|
+
* pass an explicit `tolerance` to override it outright.
|
|
1805
|
+
*
|
|
1806
|
+
* `measureId` is set when the hit feature resolves to a control-measure id
|
|
1807
|
+
* via `tryControlMeasureIdFromFeature` — present for graphics/preview
|
|
1808
|
+
* parts, generally absent for handles.
|
|
1809
|
+
*
|
|
1810
|
+
* This is a pure synchronous query over adapter-cached state: it does not
|
|
1811
|
+
* depend on `onPick`/`"click"` dispatch having run first, so it is safe to
|
|
1812
|
+
* call from any host click handler regardless of ordering relative to
|
|
1813
|
+
* TacticalDraw's own pick/click pipeline.
|
|
1814
|
+
*/
|
|
1815
|
+
ownsInteractionAt(pixel: PixelCoordinate, options?: {
|
|
1816
|
+
tolerance?: number;
|
|
1817
|
+
originalEvent?: unknown;
|
|
1818
|
+
}): InteractionHit | null;
|
|
1819
|
+
private toInteractionHit;
|
|
1132
1820
|
/**
|
|
1133
1821
|
* Abort the in-flight draw or edit. The default `"session"` reason matches
|
|
1134
1822
|
* `DrawSession.abort()` / `EditSession.abort()`.
|
|
@@ -1140,7 +1828,7 @@ declare class TacticalDraw {
|
|
|
1140
1828
|
* The live variable-draw or edit session, or `null` while idle and during
|
|
1141
1829
|
* fixed-length draws. Safe to read after `destroy()`.
|
|
1142
1830
|
*/
|
|
1143
|
-
get activeSession(): DrawSession | EditSession | null;
|
|
1831
|
+
get activeSession(): DrawSession | EditSession | GraphicEditSession<PointSymbol> | null;
|
|
1144
1832
|
/**
|
|
1145
1833
|
* The live `editMany` group-transform session, or `null` while idle (and
|
|
1146
1834
|
* during any other interaction). Separate from `activeSession` because
|
|
@@ -1167,6 +1855,7 @@ declare class TacticalDraw {
|
|
|
1167
1855
|
/** @internal — exposed for tests and future controllers. */
|
|
1168
1856
|
get layerIds(): {
|
|
1169
1857
|
graphics: LayerId;
|
|
1858
|
+
highlight: LayerId;
|
|
1170
1859
|
preview: LayerId;
|
|
1171
1860
|
guide: LayerId;
|
|
1172
1861
|
handles: LayerId;
|
|
@@ -1178,6 +1867,7 @@ declare class TacticalDraw {
|
|
|
1178
1867
|
*/
|
|
1179
1868
|
private applyGuideStyle;
|
|
1180
1869
|
private baseGuideStyle;
|
|
1870
|
+
private baseHighlightStyle;
|
|
1181
1871
|
/**
|
|
1182
1872
|
* Resolve handle slots (plus `boxOutline`) across built-in → façade →
|
|
1183
1873
|
* per-call with per-slot, per-property shallow merge. Explicit `undefined`
|
|
@@ -1209,6 +1899,9 @@ declare class TacticalDraw {
|
|
|
1209
1899
|
declare class TacticalDrawDestroyedError extends Error {
|
|
1210
1900
|
constructor(message?: string);
|
|
1211
1901
|
}
|
|
1902
|
+
declare class PointSymbolCapabilityRequiredError extends Error {
|
|
1903
|
+
constructor();
|
|
1904
|
+
}
|
|
1212
1905
|
//#endregion
|
|
1213
1906
|
//#region src/tactical-draw/transform-box.d.ts
|
|
1214
1907
|
/**
|
|
@@ -1244,6 +1937,15 @@ interface TransformBox {
|
|
|
1244
1937
|
angle: number;
|
|
1245
1938
|
}
|
|
1246
1939
|
//#endregion
|
|
1940
|
+
//#region src/tactical-draw/point-symbol-rendering.d.ts
|
|
1941
|
+
interface PointSymbolFootprint {
|
|
1942
|
+
box: TransformBox;
|
|
1943
|
+
corners: PixelCoordinate[];
|
|
1944
|
+
}
|
|
1945
|
+
declare function effectivePointSymbolPixelSize(symbol: PointSymbol, resolution: number | undefined): number;
|
|
1946
|
+
/** Complete rotated logical footprint for selection and transform chrome. */
|
|
1947
|
+
declare function pointSymbolFootprint(symbol: PointSymbol, render: PointSymbolRender, adapter: Pick<MapAdapter, "getBearing" | "getPixelFromCoordinate" | "getResolution">): PointSymbolFootprint | null;
|
|
1948
|
+
//#endregion
|
|
1247
1949
|
//#region src/world-wrap.d.ts
|
|
1248
1950
|
/**
|
|
1249
1951
|
* Shift `lng` by whole world widths (360°) onto the copy nearest `refLng`.
|
|
@@ -1258,4 +1960,4 @@ declare function nearestWorldCopyLng(lng: number, refLng: number): number;
|
|
|
1258
1960
|
/** `nearestWorldCopyLng` applied to a lon/lat position. */
|
|
1259
1961
|
declare function nearestWorldCopyPosition(position: Position, refLng: number): Position;
|
|
1260
1962
|
//#endregion
|
|
1261
|
-
export { BaseMapAdapter, type CombinedAbort, type DrawMeasureDraft, type DrawOptions, type DrawSession, type EditChangeEvent, type EditManyOptions, type EditMode, type EditOptions, type EditPointerDriver, type EditSession, type
|
|
1963
|
+
export { BaseMapAdapter, type CombinedAbort, DEFAULT_LABEL_SIZE_PIXELS, DEFAULT_TEXT_SIZE_MAX_PIXELS, DEFAULT_TEXT_SIZE_MIN_PIXELS, type DrawMeasureDraft, type DrawOptions, type DrawPointSymbolDraft, type DrawSession, type EditChangeEvent, type EditManyOptions, type EditMode, type EditOptions, type EditPointerDriver, type EditSession, type FeatureGroup, type Graphic, type GraphicEditChangeEvent, type GraphicEditSession, type GraphicGeometrySnappingOptions, type GraphicRender, type GraphicSnapshot, type GuideStyle, HATCH_PATTERNS, type HandleStyle, type HatchPattern, type IconResource, type InteractionHit, type InteractionStyle, type LabelExtent, type LayerId, type MapAdapter, type MapCursor, type MapEvent, type MapEventHandler, type MergedPickFeature, type NativeEventEntry, type PickCandidate, type PickCandidateHit, type PickCandidateHitKind, type PickCandidateInput, type PickEvaluationContext, type PickEvent, type PickHandler, type PickOptions, type PickPointerType, type PixelCoordinate, type PointSymbol, type PointSymbolAnchor, type PointSymbolCapability, PointSymbolCapabilityRequiredError, type PointSymbolColors, type PointSymbolFootprint, type PointSymbolGraphicModifiers, type PointSymbolRender, type PointSymbolSize, type PointSymbolStyle, type PointerCallback, PointerCallbackHub, type PointerEventInfo, type ResolvedPickPresentation, type RotatedScreenBounds, type ScreenBounds, type SessionChangeSource, type SessionHistory, type SessionHistoryState, type SettleReason, type SizeAnchor, type SnapCandidate, type SnapCandidateProvider, type SnapCandidateRequest, type SnapInteraction, type SnapResult, type SnappingOptions, type StyleOptions, TEXT_HALO_COLOR, TOUCH_HIT_TOLERANCE_PX, TacticalDraw, TacticalDrawAbortError, type TacticalDrawAbortReason, TacticalDrawDestroyedError, type TacticalDrawLayerOptions, type TacticalDrawOptions, type TextSizeProps, type TextStyleOptions, type ToPixel, type TransformBox, type TransformChangeEvent, type TransformSession, type TransientChangeEvent, type TransientPhase, bindAbortable, clonePointSymbol, coerceHandleStyle, combineWithHostSignal, effectivePointSymbolPixelSize, evaluatePickCandidates, fillPatternImageDimension, fillPatternImageId, getSizeAnchor, hitTestFeature, hitTestIconFeature, ignoreAbort, isPointSymbol, isTacticalDrawAbortError, makeFillPatternImage, mergeNativeAndCachedPickFeatures, nearestWorldCopyLng, nearestWorldCopyPosition, pickHitTolerance, pickPointerType, pointSymbolFootprint, renderOrderToVisualOrder, resizePointSymbolSize, resolveGroundTextSizePixels, resolvePickPresentation, resolveTextHaloWidth, resolveTextSizePixels, tryControlMeasureIdFromFeature };
|