@orbat-mapper/control-measures 0.2.0-alpha.5 → 0.2.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-Ddt-obht.d.mts → index-DUGzYzPy.d.mts} +56 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +17 -2
- package/dist/preview/index.d.mts +1 -1
- package/dist/preview/index.mjs +1 -1
- package/dist/{renderControlMeasure-CDCoQlZ-.mjs → renderControlMeasure-DGWf44sn.mjs} +188 -46
- package/package.json +1 -1
|
@@ -23,6 +23,19 @@ interface ControlMeasureDrawRule {
|
|
|
23
23
|
* `minimumUserPoints` (no early preview).
|
|
24
24
|
*/
|
|
25
25
|
readonly minimumPreviewPoints?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Opt a fixed-length draw into the standard dashed interaction guide. Fixed
|
|
28
|
+
* shapes suppress guides by default because their generator preview is
|
|
29
|
+
* normally sufficient. Use this when the guide communicates an incomplete
|
|
30
|
+
* construction state, such as Rectangle's P1-P2 baseline.
|
|
31
|
+
*/
|
|
32
|
+
readonly showGuide?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Convert raw draw points into the spine rendered by the interaction guide.
|
|
35
|
+
* Defaults to the raw points. A rule may return the first point again at the
|
|
36
|
+
* end when it needs to provide its own closed guide geometry.
|
|
37
|
+
*/
|
|
38
|
+
guidePoints?(points: readonly Position[]): Position[];
|
|
26
39
|
/**
|
|
27
40
|
* Number of trailing slots in the canonical control-point array that are
|
|
28
41
|
* NOT user-clicked spine vertices. For Axis1, this is 1 (the width handle
|
|
@@ -162,6 +175,18 @@ interface ControlMeasureDefinition<Id extends string, G extends ControlMeasureGe
|
|
|
162
175
|
* ADR-0025.
|
|
163
176
|
*/
|
|
164
177
|
previewSample?: PreviewSample<NonNullable<Parameters<G>[1]>>;
|
|
178
|
+
/**
|
|
179
|
+
* Pins orientation-derived defaults — options whose default value depends on
|
|
180
|
+
* the shape's current screen orientation (e.g. Battle Position's echelon
|
|
181
|
+
* anchor, which defaults to the bottom edge) — to their current concrete
|
|
182
|
+
* values. Returns an options patch to merge onto the working options, or
|
|
183
|
+
* `undefined` when there is nothing to freeze (already frozen, or nothing
|
|
184
|
+
* visible to pin). Called by edit hosts when a rotate gesture starts, so a
|
|
185
|
+
* subsequent rigid rotation of the control points preserves the rendered
|
|
186
|
+
* appearance instead of an orientation-relative default silently re-deriving
|
|
187
|
+
* against the new orientation. See ADR-0023.
|
|
188
|
+
*/
|
|
189
|
+
freezeOrientation?: (controlPoints: Position[], options: NonNullable<Parameters<G>[1]>) => Partial<NonNullable<Parameters<G>[1]>> | undefined;
|
|
165
190
|
}
|
|
166
191
|
/**
|
|
167
192
|
* A measure's representative preview input. Shared between the typed
|
|
@@ -315,6 +340,25 @@ interface BattlePositionOptions {
|
|
|
315
340
|
* @default 0
|
|
316
341
|
*/
|
|
317
342
|
echelonPosition?: number;
|
|
343
|
+
/**
|
|
344
|
+
* Absolute anchor for the echelon glyph, as a fraction of the total
|
|
345
|
+
* perimeter measured from the ring seam (the ring's first vertex), wrapped
|
|
346
|
+
* into `[0, 1)`. When omitted, the anchor is "auto": the bottom edge's
|
|
347
|
+
* midpoint (the side opposite the front), recomputed from the current
|
|
348
|
+
* geometry on every render — the long-standing default behavior.
|
|
349
|
+
* `echelonPosition` still applies on top of this anchor as a signed offset,
|
|
350
|
+
* exactly as it does against the auto anchor.
|
|
351
|
+
*
|
|
352
|
+
* Edit controllers freeze this from "auto" to a concrete fraction the
|
|
353
|
+
* moment a rotate gesture starts (see `freezeEchelonAnchor` /
|
|
354
|
+
* `freezeOrientationOptions`): the transform box rotates only the control
|
|
355
|
+
* points and re-renders, so without a pinned anchor the "bottom edge" would
|
|
356
|
+
* keep re-deriving against the rotated geometry and the glyph would jump to
|
|
357
|
+
* a different edge each time a new edge becomes lowest. Freezing the anchor
|
|
358
|
+
* first makes the rotation rigid — the glyph turns with the shape instead.
|
|
359
|
+
* See ADR-0023.
|
|
360
|
+
*/
|
|
361
|
+
echelonAnchor?: number;
|
|
318
362
|
/**
|
|
319
363
|
* Meters-per-pixel at the current zoom/latitude. Stamped by the host when the
|
|
320
364
|
* measure is screen-anchored; required to resolve `echelonSizePixels`.
|
|
@@ -1505,6 +1549,17 @@ interface RenderOptions {
|
|
|
1505
1549
|
*/
|
|
1506
1550
|
declare function renderControlMeasure<K extends ControlMeasureKind>(cm: ControlMeasure<K>, opts?: RenderOptions): ControlMeasureRender;
|
|
1507
1551
|
//#endregion
|
|
1552
|
+
//#region src/freeze-orientation.d.ts
|
|
1553
|
+
/**
|
|
1554
|
+
* Dispatches to a measure kind's `freezeOrientation` hook (see
|
|
1555
|
+
* {@link import("./define").ControlMeasureDefinition.freezeOrientation}),
|
|
1556
|
+
* pinning orientation-derived option defaults to their current concrete
|
|
1557
|
+
* values ahead of a rigid rotation of `controlPoints`. Returns `undefined`
|
|
1558
|
+
* when the kind declares no hook, or when the hook itself finds nothing to
|
|
1559
|
+
* freeze. Edit hosts call this when a rotate gesture starts (ADR-0023).
|
|
1560
|
+
*/
|
|
1561
|
+
declare function freezeOrientationOptions<K extends ControlMeasureKind>(kind: K, controlPoints: Position[], options: OptionsByKind[K] | undefined): Partial<OptionsByKind[K]> | undefined;
|
|
1562
|
+
//#endregion
|
|
1508
1563
|
//#region src/styleResolver.d.ts
|
|
1509
1564
|
/**
|
|
1510
1565
|
* Three-layer style precedence for `renderControlMeasure`.
|
|
@@ -1767,4 +1822,4 @@ interface TacticalArrowOptions {
|
|
|
1767
1822
|
*/
|
|
1768
1823
|
declare const DEFAULT_TACTICAL_ARROW_OPTIONS: Required<TacticalArrowOptions>;
|
|
1769
1824
|
//#endregion
|
|
1770
|
-
export {
|
|
1825
|
+
export { getControlMeasureMetadata as $, DEFAULT_GENERIC_LINE_OPTIONS as $t, getMetersPerPixel as A, DEFAULT_CANALIZE_OPTIONS as At, ControlMeasureSnapshot as B, DEFAULT_ATTACK_BY_FIRE_OPTIONS as Bt, snapToMidpointPerpendicular as C, ControlMeasureDrawRule as Cn, AntitankDitchOptions as Ct, BaselineFrameOrigin as D, ClearOptions as Dt, BaselineFrameOptions as E, DelayOptions as Et, resolveStyleHints as F, BlockMissionTaskOptions as Ft, cloneControlMeasure as G, AirborneAttackOptions as Gt, StyleHints as H, SupportByFireOptions as Ht, freezeOrientationOptions as I, DEFAULT_BLOCK_MISSION_TASK_OPTIONS as It, CONTROL_MEASURE_IDS as J, GenericCircleOptions as Jt, isKind as K, DEFAULT_AIRBORNE_ATTACK_OPTIONS as Kt, RenderOptions as L, DEFAULT_FLOT_OPTIONS as Lt, SimpleStyleProps as M, DEFAULT_BYPASS_OPTIONS as Mt, SimpleStyleRender as N, BreachOptions as Nt, createBaselineFrame as O, DEFAULT_CLEAR_OPTIONS as Ot, toSimpleStyle as P, DEFAULT_BREACH_OPTIONS as Pt, OptionsByKind as Q, GenericPolygonOptions as Qt, renderControlMeasure as R, FLOTOptions as Rt, pointOnMidpointPerpendicularAxis as S, AnchorTransformEvent as Sn, DEFAULT_ANTITANK_WALL_OPTIONS as St, BaselineFrameNormal as T, DEFAULT_DELAY_OPTIONS as Tt, controlMeasureIdFromFeature as U, AttackHelicopterOptions as Ut, FeaturePartProps as V, DEFAULT_SUPPORT_BY_FIRE_OPTIONS as Vt, ControlMeasure as W, DEFAULT_ATTACK_HELICOPTER_OPTIONS as Wt, ControlMeasureId as X, GenericRectangleOptions as Xt, CONTROL_MEASURE_METADATA as Y, DEFAULT_GENERIC_RECTANGLE_OPTIONS as Yt, ControlMeasureKind as Z, DEFAULT_GENERIC_POLYGON_OPTIONS as Zt, blockDrawRule as _, StrongPointOptions as _n, DEFAULT_FORTIFIED_AREA_OPTIONS as _t, rectangleDrawRule as a, calculateMetrics as an, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS as at, createMidpointPerpendicularDrawRule as b, ControlMeasureMetadata as bn, FortifiedLineOptions as bt, line24DrawRule as c, project as cn, ObstacleBypassEasyOptions as ct, line1DrawRule as d, EncirclementOptions as dn, DEFAULT_FIX_OPTIONS as dt, GenericLineOptions as en, getControlMeasureMetadataByValue as et, ambushDrawRule as f, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS as fn, FixOptions as ft, disruptDrawRule as g, DEFAULT_STRONG_POINT_OPTIONS as gn, DEFAULT_BLOCK_OPTIONS as gt, centerRadiusDrawRule as h, PrincipalDirectionOfFireOptions as hn, BlockOptions as ht, DEFAULT_AMBUSH_OPTIONS as i, MainAttackOptions as in, ObstacleBypassImpossibleOptions as it, roundToFixed as j, BypassOptions as jt, EPSILON as k, CanalizeOptions as kt, line23DrawRule as l, unproject as ln, DEFAULT_TURN_OPTIONS as lt, point12DrawRule as m, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS as mn, DisruptOptions as mt, TacticalArrowOptions as n, SupportingAttackOptions as nn, listControlMeasureMetadata as nt, axis1DrawRule as o, computeInitialWidthPoint as on, ObstacleBypassDifficultOptions as ot, attackByFireDrawRule as p, FinalProtectiveFireOptions as pn, DEFAULT_DISRUPT_OPTIONS as pt, ControlMeasureStyle as q, DEFAULT_GENERIC_CIRCLE_OPTIONS as qt, AmbushOptions as r, DEFAULT_MAIN_ATTACK_OPTIONS as rn, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS as rt, supportByFireDrawRule as s, Point2D as sn, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS as st, DEFAULT_TACTICAL_ARROW_OPTIONS as t, DEFAULT_SUPPORTING_ATTACK_OPTIONS as tn, getDefaultOptions as tt, turnDrawRule as u, DEFAULT_ENCIRCLEMENT_OPTIONS as un, TurnOptions as ut, MidpointPerpendicularDrawRuleOptions as v, ControlMeasureGeometry as vn, FortifiedAreaOptions as vt, BaselineFrame as w, DEFAULT_ANTITANK_DITCH_OPTIONS as wt, getMidpointPerpendicularSignedDistance as x, ParamDescriptor as xn, AntitankWallOptions as xt, computeDefaultMidpointPerpendicularPoint as y, ControlMeasureGeometryType as yn, DEFAULT_FORTIFIED_LINE_OPTIONS as yt, ControlMeasureRender as z, AttackByFireOptions as zt };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
export { type AirborneAttackOptions, type AmbushOptions, type AnchorTransformEvent, type AntitankDitchOptions, type AntitankWallOptions, type AttackByFireOptions, type AttackHelicopterOptions, type BaselineFrame, type BaselineFrameNormal, type BaselineFrameOptions, type BaselineFrameOrigin, type BlockMissionTaskOptions, type BlockOptions, type BreachOptions, type BypassOptions, CONTROL_MEASURE_IDS, CONTROL_MEASURE_METADATA, type CanalizeOptions, type ClearOptions, type ControlMeasure, type ControlMeasureDrawRule, type ControlMeasureGeometry, type ControlMeasureGeometryType, type ControlMeasureId, type ControlMeasureKind, type ControlMeasureMetadata, type ControlMeasureRender, type ControlMeasureSnapshot, type ControlMeasureStyle, DEFAULT_AIRBORNE_ATTACK_OPTIONS, DEFAULT_AMBUSH_OPTIONS, DEFAULT_ANTITANK_DITCH_OPTIONS, DEFAULT_ANTITANK_WALL_OPTIONS, DEFAULT_ATTACK_BY_FIRE_OPTIONS, DEFAULT_ATTACK_HELICOPTER_OPTIONS, DEFAULT_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_OPTIONS, DEFAULT_CLEAR_OPTIONS, DEFAULT_DELAY_OPTIONS, DEFAULT_DISRUPT_OPTIONS, DEFAULT_ENCIRCLEMENT_OPTIONS, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, DEFAULT_FIX_OPTIONS, DEFAULT_FLOT_OPTIONS, DEFAULT_FORTIFIED_AREA_OPTIONS, DEFAULT_FORTIFIED_LINE_OPTIONS, DEFAULT_GENERIC_CIRCLE_OPTIONS, DEFAULT_GENERIC_LINE_OPTIONS, DEFAULT_GENERIC_POLYGON_OPTIONS, DEFAULT_GENERIC_RECTANGLE_OPTIONS, DEFAULT_MAIN_ATTACK_OPTIONS, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, DEFAULT_STRONG_POINT_OPTIONS, DEFAULT_SUPPORTING_ATTACK_OPTIONS, DEFAULT_SUPPORT_BY_FIRE_OPTIONS, DEFAULT_TACTICAL_ARROW_OPTIONS, DEFAULT_TURN_OPTIONS, type DelayOptions, type DisruptOptions, EPSILON, type EncirclementOptions, type FLOTOptions, type FeaturePartProps, type FinalProtectiveFireOptions, type FixOptions, type FortifiedAreaOptions, type FortifiedLineOptions, type GenericCircleOptions, type GenericLineOptions, type GenericPolygonOptions, type GenericRectangleOptions, type MainAttackOptions, type MidpointPerpendicularDrawRuleOptions, type ObstacleBypassDifficultOptions, type ObstacleBypassEasyOptions, type ObstacleBypassImpossibleOptions, type OptionsByKind, type ParamDescriptor, type Point2D, type PrincipalDirectionOfFireOptions, type RenderOptions, type SimpleStyleProps, type SimpleStyleRender, type StrongPointOptions, type StyleHints, type SupportByFireOptions, type SupportingAttackOptions, type TacticalArrowOptions, type TurnOptions, ambushDrawRule, attackByFireDrawRule, axis1DrawRule, blockDrawRule, calculateMetrics, centerRadiusDrawRule, cloneControlMeasure, computeDefaultMidpointPerpendicularPoint, computeInitialWidthPoint, controlMeasureIdFromFeature, createBaselineFrame, createMidpointPerpendicularDrawRule, disruptDrawRule, getControlMeasureMetadata, getControlMeasureMetadataByValue, getDefaultOptions, getMetersPerPixel, getMidpointPerpendicularSignedDistance, isKind, line1DrawRule, line23DrawRule, line24DrawRule, listControlMeasureMetadata, point12DrawRule, pointOnMidpointPerpendicularAxis, project, rectangleDrawRule, renderControlMeasure, resolveStyleHints, roundToFixed, snapToMidpointPerpendicular, supportByFireDrawRule, toSimpleStyle, turnDrawRule, unproject };
|
|
1
|
+
import { $ as getControlMeasureMetadata, $t as DEFAULT_GENERIC_LINE_OPTIONS, A as getMetersPerPixel, At as DEFAULT_CANALIZE_OPTIONS, B as ControlMeasureSnapshot, Bt as DEFAULT_ATTACK_BY_FIRE_OPTIONS, C as snapToMidpointPerpendicular, Cn as ControlMeasureDrawRule, Ct as AntitankDitchOptions, D as BaselineFrameOrigin, Dt as ClearOptions, E as BaselineFrameOptions, Et as DelayOptions, F as resolveStyleHints, Ft as BlockMissionTaskOptions, G as cloneControlMeasure, Gt as AirborneAttackOptions, H as StyleHints, Ht as SupportByFireOptions, I as freezeOrientationOptions, It as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, J as CONTROL_MEASURE_IDS, Jt as GenericCircleOptions, K as isKind, Kt as DEFAULT_AIRBORNE_ATTACK_OPTIONS, L as RenderOptions, Lt as DEFAULT_FLOT_OPTIONS, M as SimpleStyleProps, Mt as DEFAULT_BYPASS_OPTIONS, N as SimpleStyleRender, Nt as BreachOptions, O as createBaselineFrame, Ot as DEFAULT_CLEAR_OPTIONS, P as toSimpleStyle, Pt as DEFAULT_BREACH_OPTIONS, Q as OptionsByKind, Qt as GenericPolygonOptions, R as renderControlMeasure, Rt as FLOTOptions, S as pointOnMidpointPerpendicularAxis, Sn as AnchorTransformEvent, St as DEFAULT_ANTITANK_WALL_OPTIONS, T as BaselineFrameNormal, Tt as DEFAULT_DELAY_OPTIONS, U as controlMeasureIdFromFeature, Ut as AttackHelicopterOptions, V as FeaturePartProps, Vt as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, W as ControlMeasure, Wt as DEFAULT_ATTACK_HELICOPTER_OPTIONS, X as ControlMeasureId, Xt as GenericRectangleOptions, Y as CONTROL_MEASURE_METADATA, Yt as DEFAULT_GENERIC_RECTANGLE_OPTIONS, Z as ControlMeasureKind, Zt as DEFAULT_GENERIC_POLYGON_OPTIONS, _ as blockDrawRule, _n as StrongPointOptions, _t as DEFAULT_FORTIFIED_AREA_OPTIONS, a as rectangleDrawRule, an as calculateMetrics, at as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, b as createMidpointPerpendicularDrawRule, bn as ControlMeasureMetadata, bt as FortifiedLineOptions, c as line24DrawRule, cn as project, ct as ObstacleBypassEasyOptions, d as line1DrawRule, dn as EncirclementOptions, dt as DEFAULT_FIX_OPTIONS, en as GenericLineOptions, et as getControlMeasureMetadataByValue, f as ambushDrawRule, fn as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, ft as FixOptions, g as disruptDrawRule, gn as DEFAULT_STRONG_POINT_OPTIONS, gt as DEFAULT_BLOCK_OPTIONS, h as centerRadiusDrawRule, hn as PrincipalDirectionOfFireOptions, ht as BlockOptions, i as DEFAULT_AMBUSH_OPTIONS, in as MainAttackOptions, it as ObstacleBypassImpossibleOptions, j as roundToFixed, jt as BypassOptions, k as EPSILON, kt as CanalizeOptions, l as line23DrawRule, ln as unproject, lt as DEFAULT_TURN_OPTIONS, m as point12DrawRule, mn as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, mt as DisruptOptions, n as TacticalArrowOptions, nn as SupportingAttackOptions, nt as listControlMeasureMetadata, o as axis1DrawRule, on as computeInitialWidthPoint, ot as ObstacleBypassDifficultOptions, p as attackByFireDrawRule, pn as FinalProtectiveFireOptions, pt as DEFAULT_DISRUPT_OPTIONS, q as ControlMeasureStyle, qt as DEFAULT_GENERIC_CIRCLE_OPTIONS, r as AmbushOptions, rn as DEFAULT_MAIN_ATTACK_OPTIONS, rt as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, s as supportByFireDrawRule, sn as Point2D, st as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, t as DEFAULT_TACTICAL_ARROW_OPTIONS, tn as DEFAULT_SUPPORTING_ATTACK_OPTIONS, tt as getDefaultOptions, u as turnDrawRule, un as DEFAULT_ENCIRCLEMENT_OPTIONS, ut as TurnOptions, v as MidpointPerpendicularDrawRuleOptions, vn as ControlMeasureGeometry, vt as FortifiedAreaOptions, w as BaselineFrame, wt as DEFAULT_ANTITANK_DITCH_OPTIONS, x as getMidpointPerpendicularSignedDistance, xn as ParamDescriptor, xt as AntitankWallOptions, y as computeDefaultMidpointPerpendicularPoint, yn as ControlMeasureGeometryType, yt as DEFAULT_FORTIFIED_LINE_OPTIONS, z as ControlMeasureRender, zt as AttackByFireOptions } from "./index-DUGzYzPy.mjs";
|
|
2
|
+
export { type AirborneAttackOptions, type AmbushOptions, type AnchorTransformEvent, type AntitankDitchOptions, type AntitankWallOptions, type AttackByFireOptions, type AttackHelicopterOptions, type BaselineFrame, type BaselineFrameNormal, type BaselineFrameOptions, type BaselineFrameOrigin, type BlockMissionTaskOptions, type BlockOptions, type BreachOptions, type BypassOptions, CONTROL_MEASURE_IDS, CONTROL_MEASURE_METADATA, type CanalizeOptions, type ClearOptions, type ControlMeasure, type ControlMeasureDrawRule, type ControlMeasureGeometry, type ControlMeasureGeometryType, type ControlMeasureId, type ControlMeasureKind, type ControlMeasureMetadata, type ControlMeasureRender, type ControlMeasureSnapshot, type ControlMeasureStyle, DEFAULT_AIRBORNE_ATTACK_OPTIONS, DEFAULT_AMBUSH_OPTIONS, DEFAULT_ANTITANK_DITCH_OPTIONS, DEFAULT_ANTITANK_WALL_OPTIONS, DEFAULT_ATTACK_BY_FIRE_OPTIONS, DEFAULT_ATTACK_HELICOPTER_OPTIONS, DEFAULT_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_OPTIONS, DEFAULT_CLEAR_OPTIONS, DEFAULT_DELAY_OPTIONS, DEFAULT_DISRUPT_OPTIONS, DEFAULT_ENCIRCLEMENT_OPTIONS, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, DEFAULT_FIX_OPTIONS, DEFAULT_FLOT_OPTIONS, DEFAULT_FORTIFIED_AREA_OPTIONS, DEFAULT_FORTIFIED_LINE_OPTIONS, DEFAULT_GENERIC_CIRCLE_OPTIONS, DEFAULT_GENERIC_LINE_OPTIONS, DEFAULT_GENERIC_POLYGON_OPTIONS, DEFAULT_GENERIC_RECTANGLE_OPTIONS, DEFAULT_MAIN_ATTACK_OPTIONS, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, DEFAULT_STRONG_POINT_OPTIONS, DEFAULT_SUPPORTING_ATTACK_OPTIONS, DEFAULT_SUPPORT_BY_FIRE_OPTIONS, DEFAULT_TACTICAL_ARROW_OPTIONS, DEFAULT_TURN_OPTIONS, type DelayOptions, type DisruptOptions, EPSILON, type EncirclementOptions, type FLOTOptions, type FeaturePartProps, type FinalProtectiveFireOptions, type FixOptions, type FortifiedAreaOptions, type FortifiedLineOptions, type GenericCircleOptions, type GenericLineOptions, type GenericPolygonOptions, type GenericRectangleOptions, type MainAttackOptions, type MidpointPerpendicularDrawRuleOptions, type ObstacleBypassDifficultOptions, type ObstacleBypassEasyOptions, type ObstacleBypassImpossibleOptions, type OptionsByKind, type ParamDescriptor, type Point2D, type PrincipalDirectionOfFireOptions, type RenderOptions, type SimpleStyleProps, type SimpleStyleRender, type StrongPointOptions, type StyleHints, type SupportByFireOptions, type SupportingAttackOptions, type TacticalArrowOptions, type TurnOptions, ambushDrawRule, attackByFireDrawRule, axis1DrawRule, blockDrawRule, calculateMetrics, centerRadiusDrawRule, cloneControlMeasure, computeDefaultMidpointPerpendicularPoint, computeInitialWidthPoint, controlMeasureIdFromFeature, createBaselineFrame, createMidpointPerpendicularDrawRule, disruptDrawRule, freezeOrientationOptions, getControlMeasureMetadata, getControlMeasureMetadataByValue, getDefaultOptions, getMetersPerPixel, getMidpointPerpendicularSignedDistance, isKind, line1DrawRule, line23DrawRule, line24DrawRule, listControlMeasureMetadata, point12DrawRule, pointOnMidpointPerpendicularAxis, project, rectangleDrawRule, renderControlMeasure, resolveStyleHints, roundToFixed, snapToMidpointPerpendicular, supportByFireDrawRule, toSimpleStyle, turnDrawRule, unproject };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
import { $ as point12DrawRule, A as DEFAULT_GENERIC_POLYGON_OPTIONS, B as DEFAULT_ANTITANK_WALL_OPTIONS, C as DEFAULT_FIX_OPTIONS, D as DEFAULT_DELAY_OPTIONS, E as DEFAULT_DISRUPT_OPTIONS, F as DEFAULT_BREACH_OPTIONS, G as axis1DrawRule, H as DEFAULT_AMBUSH_OPTIONS, I as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, J as line23DrawRule, K as supportByFireDrawRule, L as DEFAULT_BLOCK_OPTIONS, M as DEFAULT_GENERIC_CIRCLE_OPTIONS, N as DEFAULT_CANALIZE_OPTIONS, O as DEFAULT_CLEAR_OPTIONS, P as DEFAULT_BYPASS_OPTIONS, Q as attackByFireDrawRule, R as DEFAULT_ATTACK_HELICOPTER_OPTIONS, S as DEFAULT_FLOT_OPTIONS, T as DEFAULT_ENCIRCLEMENT_OPTIONS, U as DEFAULT_AIRBORNE_ATTACK_OPTIONS, V as DEFAULT_ANTITANK_DITCH_OPTIONS, W as rectangleDrawRule, X as line1DrawRule, Y as turnDrawRule, Z as ambushDrawRule, _ as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, at as getMidpointPerpendicularSignedDistance, b as DEFAULT_FORTIFIED_AREA_OPTIONS, c as getDefaultOptions, ct as createBaselineFrame, d as DEFAULT_SUPPORTING_ATTACK_OPTIONS, dt as project, et as centerRadiusDrawRule, f as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, ft as unproject, g as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, h as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, ht as roundToFixed, i as CONTROL_MEASURE_METADATA, it as createMidpointPerpendicularDrawRule, j as DEFAULT_GENERIC_LINE_OPTIONS, k as DEFAULT_GENERIC_RECTANGLE_OPTIONS, l as listControlMeasureMetadata, lt as calculateMetrics, m as DEFAULT_TACTICAL_ARROW_OPTIONS, mt as getMetersPerPixel, n as resolveStyleHints, nt as blockDrawRule, o as getControlMeasureMetadata, ot as pointOnMidpointPerpendicularAxis, p as DEFAULT_STRONG_POINT_OPTIONS, pt as EPSILON, q as line24DrawRule, r as CONTROL_MEASURE_IDS, rt as computeDefaultMidpointPerpendicularPoint, s as getControlMeasureMetadataByValue, st as snapToMidpointPerpendicular, t as renderControlMeasure, tt as disruptDrawRule, u as DEFAULT_TURN_OPTIONS, ut as computeInitialWidthPoint, v as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, w as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, x as DEFAULT_FORTIFIED_LINE_OPTIONS, y as DEFAULT_MAIN_ATTACK_OPTIONS, z as DEFAULT_ATTACK_BY_FIRE_OPTIONS } from "./renderControlMeasure-
|
|
1
|
+
import { $ as point12DrawRule, A as DEFAULT_GENERIC_POLYGON_OPTIONS, B as DEFAULT_ANTITANK_WALL_OPTIONS, C as DEFAULT_FIX_OPTIONS, D as DEFAULT_DELAY_OPTIONS, E as DEFAULT_DISRUPT_OPTIONS, F as DEFAULT_BREACH_OPTIONS, G as axis1DrawRule, H as DEFAULT_AMBUSH_OPTIONS, I as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, J as line23DrawRule, K as supportByFireDrawRule, L as DEFAULT_BLOCK_OPTIONS, M as DEFAULT_GENERIC_CIRCLE_OPTIONS, N as DEFAULT_CANALIZE_OPTIONS, O as DEFAULT_CLEAR_OPTIONS, P as DEFAULT_BYPASS_OPTIONS, Q as attackByFireDrawRule, R as DEFAULT_ATTACK_HELICOPTER_OPTIONS, S as DEFAULT_FLOT_OPTIONS, T as DEFAULT_ENCIRCLEMENT_OPTIONS, U as DEFAULT_AIRBORNE_ATTACK_OPTIONS, V as DEFAULT_ANTITANK_DITCH_OPTIONS, W as rectangleDrawRule, X as line1DrawRule, Y as turnDrawRule, Z as ambushDrawRule, _ as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, a as DEFINITIONS, at as getMidpointPerpendicularSignedDistance, b as DEFAULT_FORTIFIED_AREA_OPTIONS, c as getDefaultOptions, ct as createBaselineFrame, d as DEFAULT_SUPPORTING_ATTACK_OPTIONS, dt as project, et as centerRadiusDrawRule, f as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, ft as unproject, g as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, h as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, ht as roundToFixed, i as CONTROL_MEASURE_METADATA, it as createMidpointPerpendicularDrawRule, j as DEFAULT_GENERIC_LINE_OPTIONS, k as DEFAULT_GENERIC_RECTANGLE_OPTIONS, l as listControlMeasureMetadata, lt as calculateMetrics, m as DEFAULT_TACTICAL_ARROW_OPTIONS, mt as getMetersPerPixel, n as resolveStyleHints, nt as blockDrawRule, o as getControlMeasureMetadata, ot as pointOnMidpointPerpendicularAxis, p as DEFAULT_STRONG_POINT_OPTIONS, pt as EPSILON, q as line24DrawRule, r as CONTROL_MEASURE_IDS, rt as computeDefaultMidpointPerpendicularPoint, s as getControlMeasureMetadataByValue, st as snapToMidpointPerpendicular, t as renderControlMeasure, tt as disruptDrawRule, u as DEFAULT_TURN_OPTIONS, ut as computeInitialWidthPoint, v as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, w as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, x as DEFAULT_FORTIFIED_LINE_OPTIONS, y as DEFAULT_MAIN_ATTACK_OPTIONS, z as DEFAULT_ATTACK_BY_FIRE_OPTIONS } from "./renderControlMeasure-DGWf44sn.mjs";
|
|
2
|
+
//#region src/freeze-orientation.ts
|
|
3
|
+
/**
|
|
4
|
+
* Dispatches to a measure kind's `freezeOrientation` hook (see
|
|
5
|
+
* {@link import("./define").ControlMeasureDefinition.freezeOrientation}),
|
|
6
|
+
* pinning orientation-derived option defaults to their current concrete
|
|
7
|
+
* values ahead of a rigid rotation of `controlPoints`. Returns `undefined`
|
|
8
|
+
* when the kind declares no hook, or when the hook itself finds nothing to
|
|
9
|
+
* freeze. Edit hosts call this when a rotate gesture starts (ADR-0023).
|
|
10
|
+
*/
|
|
11
|
+
function freezeOrientationOptions(kind, controlPoints, options) {
|
|
12
|
+
const definition = DEFINITIONS[kind];
|
|
13
|
+
if (!definition.freezeOrientation) return void 0;
|
|
14
|
+
return definition.freezeOrientation(controlPoints, options ?? {});
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
2
17
|
//#region src/instance.ts
|
|
3
18
|
function isKind(cm, kind) {
|
|
4
19
|
return cm.kind === kind;
|
|
@@ -99,4 +114,4 @@ function toHexChannel(value) {
|
|
|
99
114
|
return Math.max(0, Math.min(255, Math.round(value))).toString(16).padStart(2, "0");
|
|
100
115
|
}
|
|
101
116
|
//#endregion
|
|
102
|
-
export { CONTROL_MEASURE_IDS, CONTROL_MEASURE_METADATA, DEFAULT_AIRBORNE_ATTACK_OPTIONS, DEFAULT_AMBUSH_OPTIONS, DEFAULT_ANTITANK_DITCH_OPTIONS, DEFAULT_ANTITANK_WALL_OPTIONS, DEFAULT_ATTACK_BY_FIRE_OPTIONS, DEFAULT_ATTACK_HELICOPTER_OPTIONS, DEFAULT_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_OPTIONS, DEFAULT_CLEAR_OPTIONS, DEFAULT_DELAY_OPTIONS, DEFAULT_DISRUPT_OPTIONS, DEFAULT_ENCIRCLEMENT_OPTIONS, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, DEFAULT_FIX_OPTIONS, DEFAULT_FLOT_OPTIONS, DEFAULT_FORTIFIED_AREA_OPTIONS, DEFAULT_FORTIFIED_LINE_OPTIONS, DEFAULT_GENERIC_CIRCLE_OPTIONS, DEFAULT_GENERIC_LINE_OPTIONS, DEFAULT_GENERIC_POLYGON_OPTIONS, DEFAULT_GENERIC_RECTANGLE_OPTIONS, DEFAULT_MAIN_ATTACK_OPTIONS, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, DEFAULT_STRONG_POINT_OPTIONS, DEFAULT_SUPPORTING_ATTACK_OPTIONS, DEFAULT_SUPPORT_BY_FIRE_OPTIONS, DEFAULT_TACTICAL_ARROW_OPTIONS, DEFAULT_TURN_OPTIONS, EPSILON, ambushDrawRule, attackByFireDrawRule, axis1DrawRule, blockDrawRule, calculateMetrics, centerRadiusDrawRule, cloneControlMeasure, computeDefaultMidpointPerpendicularPoint, computeInitialWidthPoint, controlMeasureIdFromFeature, createBaselineFrame, createMidpointPerpendicularDrawRule, disruptDrawRule, getControlMeasureMetadata, getControlMeasureMetadataByValue, getDefaultOptions, getMetersPerPixel, getMidpointPerpendicularSignedDistance, isKind, line1DrawRule, line23DrawRule, line24DrawRule, listControlMeasureMetadata, point12DrawRule, pointOnMidpointPerpendicularAxis, project, rectangleDrawRule, renderControlMeasure, resolveStyleHints, roundToFixed, snapToMidpointPerpendicular, supportByFireDrawRule, toSimpleStyle, turnDrawRule, unproject };
|
|
117
|
+
export { CONTROL_MEASURE_IDS, CONTROL_MEASURE_METADATA, DEFAULT_AIRBORNE_ATTACK_OPTIONS, DEFAULT_AMBUSH_OPTIONS, DEFAULT_ANTITANK_DITCH_OPTIONS, DEFAULT_ANTITANK_WALL_OPTIONS, DEFAULT_ATTACK_BY_FIRE_OPTIONS, DEFAULT_ATTACK_HELICOPTER_OPTIONS, DEFAULT_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_OPTIONS, DEFAULT_CLEAR_OPTIONS, DEFAULT_DELAY_OPTIONS, DEFAULT_DISRUPT_OPTIONS, DEFAULT_ENCIRCLEMENT_OPTIONS, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, DEFAULT_FIX_OPTIONS, DEFAULT_FLOT_OPTIONS, DEFAULT_FORTIFIED_AREA_OPTIONS, DEFAULT_FORTIFIED_LINE_OPTIONS, DEFAULT_GENERIC_CIRCLE_OPTIONS, DEFAULT_GENERIC_LINE_OPTIONS, DEFAULT_GENERIC_POLYGON_OPTIONS, DEFAULT_GENERIC_RECTANGLE_OPTIONS, DEFAULT_MAIN_ATTACK_OPTIONS, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, DEFAULT_STRONG_POINT_OPTIONS, DEFAULT_SUPPORTING_ATTACK_OPTIONS, DEFAULT_SUPPORT_BY_FIRE_OPTIONS, DEFAULT_TACTICAL_ARROW_OPTIONS, DEFAULT_TURN_OPTIONS, EPSILON, ambushDrawRule, attackByFireDrawRule, axis1DrawRule, blockDrawRule, calculateMetrics, centerRadiusDrawRule, cloneControlMeasure, computeDefaultMidpointPerpendicularPoint, computeInitialWidthPoint, controlMeasureIdFromFeature, createBaselineFrame, createMidpointPerpendicularDrawRule, disruptDrawRule, freezeOrientationOptions, getControlMeasureMetadata, getControlMeasureMetadataByValue, getDefaultOptions, getMetersPerPixel, getMidpointPerpendicularSignedDistance, isKind, line1DrawRule, line23DrawRule, line24DrawRule, listControlMeasureMetadata, point12DrawRule, pointOnMidpointPerpendicularAxis, project, rectangleDrawRule, renderControlMeasure, resolveStyleHints, roundToFixed, snapToMidpointPerpendicular, supportByFireDrawRule, toSimpleStyle, turnDrawRule, unproject };
|
package/dist/preview/index.d.mts
CHANGED
package/dist/preview/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as DEFINITIONS, t as renderControlMeasure } from "../renderControlMeasure-
|
|
1
|
+
import { a as DEFINITIONS, t as renderControlMeasure } from "../renderControlMeasure-DGWf44sn.mjs";
|
|
2
2
|
//#region src/preview/index.ts
|
|
3
3
|
/**
|
|
4
4
|
* Unitless `previewSample` points (~`[-1, 1]`) are scaled by this degree offset
|
|
@@ -181,7 +181,7 @@ function pointToInfiniteLineDistance(p, a, b) {
|
|
|
181
181
|
}
|
|
182
182
|
/**
|
|
183
183
|
* Calculates intersection of two line segments p1-p2 and p3-p4.
|
|
184
|
-
* Returns null if parallel
|
|
184
|
+
* Returns null if parallel or if the infinite lines intersect outside either segment.
|
|
185
185
|
*/
|
|
186
186
|
function lineIntersection(p1, p2, p3, p4) {
|
|
187
187
|
const x1 = p1[0], y1 = p1[1];
|
|
@@ -191,6 +191,8 @@ function lineIntersection(p1, p2, p3, p4) {
|
|
|
191
191
|
const denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
|
|
192
192
|
if (Math.abs(denom) < 1e-6) return null;
|
|
193
193
|
const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
|
|
194
|
+
const ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
|
|
195
|
+
if (ua < -1e-6 || ua > 1.000001 || ub < -1e-6 || ub > 1.000001) return null;
|
|
194
196
|
return [x1 + ua * (x2 - x1), y1 + ua * (y2 - y1)];
|
|
195
197
|
}
|
|
196
198
|
//#endregion
|
|
@@ -218,15 +220,38 @@ function processAttackGeometry(coordinates, options = {}) {
|
|
|
218
220
|
return { geometry: calculateSymbolGeometry(ptTip, ptWidth, spinePoints, shaftRatio, roundedBends, bendSegments) };
|
|
219
221
|
}
|
|
220
222
|
function calculateSymbolGeometry(ptTip, ptWidth, spine, shaftRatio, roundedBends, bendSegments) {
|
|
221
|
-
const
|
|
223
|
+
const initialNeck = spine[spine.length - 1];
|
|
224
|
+
if (!initialNeck) return null;
|
|
225
|
+
const initialTipDir = vecNorm(vecSub(ptTip, initialNeck));
|
|
226
|
+
if (vecMag(initialTipDir) < 1e-6) return null;
|
|
227
|
+
const headHalfWidth = pointToInfiniteLineDistance(ptWidth, initialNeck, ptTip);
|
|
228
|
+
const vecTipToWidth = vecSub(ptTip, ptWidth);
|
|
229
|
+
const requestedHeadLength = Math.max(EPSILON, vecDot(vecTipToWidth, initialTipDir));
|
|
230
|
+
const path = [...spine, ptTip];
|
|
231
|
+
let totalArcLength = 0;
|
|
232
|
+
for (let i = 1; i < path.length; i++) totalArcLength += vecMag(vecSub(path[i], path[i - 1]));
|
|
233
|
+
const minimumShaftLength = Math.max(EPSILON, totalArcLength * .01);
|
|
234
|
+
let distanceRemaining = clamp(requestedHeadLength, EPSILON, Math.max(EPSILON, totalArcLength - minimumShaftLength));
|
|
235
|
+
let ptBase = ptTip;
|
|
236
|
+
let remainingSpine = spine;
|
|
237
|
+
for (let i = path.length - 1; i > 0; i--) {
|
|
238
|
+
const segmentEnd = path[i];
|
|
239
|
+
const segmentStart = path[i - 1];
|
|
240
|
+
const segment = vecSub(segmentStart, segmentEnd);
|
|
241
|
+
const segmentLength = vecMag(segment);
|
|
242
|
+
if (segmentLength < 1e-6) continue;
|
|
243
|
+
if (distanceRemaining < segmentLength) {
|
|
244
|
+
ptBase = vecAdd(segmentEnd, vecScale(segment, distanceRemaining / segmentLength));
|
|
245
|
+
remainingSpine = path.slice(0, i);
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
distanceRemaining -= segmentLength;
|
|
249
|
+
}
|
|
250
|
+
const ptNeck = remainingSpine[remainingSpine.length - 1];
|
|
222
251
|
if (!ptNeck) return null;
|
|
223
252
|
const tipDir = vecNorm(vecSub(ptTip, ptNeck));
|
|
224
253
|
if (vecMag(tipDir) < 1e-6) return null;
|
|
225
|
-
const
|
|
226
|
-
const vecTipToWidth = vecSub(ptTip, ptWidth);
|
|
227
|
-
const maxHeadLength = Math.max(0, vecMag(vecSub(ptTip, ptNeck)) - .1);
|
|
228
|
-
const headLength = clamp(vecDot(vecTipToWidth, tipDir), EPSILON, maxHeadLength);
|
|
229
|
-
const ptBase = vecSub(ptTip, vecScale(tipDir, headLength));
|
|
254
|
+
const headLength = vecMag(vecSub(ptTip, ptBase));
|
|
230
255
|
const shaftHalfWidth = headHalfWidth * shaftRatio;
|
|
231
256
|
const perpDir = [-tipDir[1], tipDir[0]];
|
|
232
257
|
const outerLeft = vecAdd(ptBase, vecScale(perpDir, headHalfWidth));
|
|
@@ -234,7 +259,8 @@ function calculateSymbolGeometry(ptTip, ptWidth, spine, shaftRatio, roundedBends
|
|
|
234
259
|
const innerLeft = vecAdd(ptBase, vecScale(perpDir, shaftHalfWidth));
|
|
235
260
|
const innerRight = vecAdd(ptBase, vecScale(perpDir, -shaftHalfWidth));
|
|
236
261
|
const notchDepth = headLength * shaftRatio;
|
|
237
|
-
const
|
|
262
|
+
const safeNotchDepth = Math.min(notchDepth, headLength - EPSILON);
|
|
263
|
+
const innerTip = vecAdd(ptBase, vecScale(tipDir, safeNotchDepth));
|
|
238
264
|
const shaftEndCenter = ptBase;
|
|
239
265
|
const headRing = [
|
|
240
266
|
outerLeft,
|
|
@@ -245,7 +271,7 @@ function calculateSymbolGeometry(ptTip, ptWidth, spine, shaftRatio, roundedBends
|
|
|
245
271
|
innerLeft,
|
|
246
272
|
outerLeft
|
|
247
273
|
];
|
|
248
|
-
const fullSpine = [...
|
|
274
|
+
const fullSpine = [...remainingSpine, shaftEndCenter];
|
|
249
275
|
return {
|
|
250
276
|
shaftLeft: offsetPolyline(fullSpine, shaftHalfWidth, roundedBends, bendSegments),
|
|
251
277
|
shaftRight: offsetPolyline(fullSpine, -shaftHalfWidth, roundedBends, bendSegments),
|
|
@@ -849,19 +875,91 @@ const searchAreaDrawRule = {
|
|
|
849
875
|
//#endregion
|
|
850
876
|
//#region src/draw-rules/rectangle.ts
|
|
851
877
|
/**
|
|
852
|
-
*
|
|
853
|
-
*
|
|
878
|
+
* Oriented rectangle: P1 and P2 are adjacent corners and define the first
|
|
879
|
+
* edge. P3 is the next corner, constrained to the perpendicular through P2.
|
|
880
|
+
* The generator derives the fourth corner.
|
|
854
881
|
*/
|
|
855
882
|
function derive(points) {
|
|
856
|
-
|
|
883
|
+
if (points.length < 2) return clonePositions(points);
|
|
884
|
+
const p1 = clonePosition(points[0]);
|
|
885
|
+
const p2 = clonePosition(points[1]);
|
|
886
|
+
if (points.length === 2) return [
|
|
887
|
+
p1,
|
|
888
|
+
p2,
|
|
889
|
+
clonePosition(p2)
|
|
890
|
+
];
|
|
891
|
+
const frame = createBaselineFrame(p1, p2, {
|
|
892
|
+
origin: "p2",
|
|
893
|
+
normal: "right"
|
|
894
|
+
});
|
|
895
|
+
return [
|
|
896
|
+
p1,
|
|
897
|
+
p2,
|
|
898
|
+
frame?.pointAtNormalDistance(frame.signedNormalDistance(points[2])) ?? clonePosition(points[2])
|
|
899
|
+
];
|
|
900
|
+
}
|
|
901
|
+
function guidePoints(points) {
|
|
902
|
+
const canonical = derive(points);
|
|
903
|
+
if (canonical.length < 3) return canonical;
|
|
904
|
+
const [c1, c2, c3] = canonical;
|
|
905
|
+
if (!c1 || !c2 || !c3) return canonical;
|
|
906
|
+
if (c2[0] === c3[0] && c2[1] === c3[1]) return [c1, c2];
|
|
907
|
+
const p1 = project(c1[0], c1[1]);
|
|
908
|
+
const p2 = project(c2[0], c2[1]);
|
|
909
|
+
const p4 = vecAdd(p1, vecSub(project(c3[0], c3[1]), p2));
|
|
910
|
+
return [
|
|
911
|
+
c1,
|
|
912
|
+
c2,
|
|
913
|
+
c3,
|
|
914
|
+
unproject(p4[0], p4[1]),
|
|
915
|
+
clonePosition(c1)
|
|
916
|
+
];
|
|
917
|
+
}
|
|
918
|
+
function snapToAxis(point, origin, direction) {
|
|
919
|
+
const snapped = vecAdd(origin, vecScale(direction, vecDot(vecSub(project(point[0], point[1]), origin), direction)));
|
|
920
|
+
return unproject(snapped[0], snapped[1]);
|
|
857
921
|
}
|
|
858
922
|
const rectangleDrawRule = {
|
|
859
923
|
id: "rectangle",
|
|
860
|
-
minimumUserPoints:
|
|
861
|
-
|
|
924
|
+
minimumUserPoints: 3,
|
|
925
|
+
minimumPreviewPoints: 2,
|
|
926
|
+
canonicalPointCount: 3,
|
|
927
|
+
showGuide: true,
|
|
928
|
+
guidePoints,
|
|
862
929
|
derive,
|
|
863
930
|
transform(event) {
|
|
864
|
-
|
|
931
|
+
const { previous, next, activePointIndex } = event;
|
|
932
|
+
if (previous.length < 3 || next.length < 3) return derive(next);
|
|
933
|
+
const frame = createBaselineFrame(previous[0], previous[1], {
|
|
934
|
+
origin: "p2",
|
|
935
|
+
normal: "right"
|
|
936
|
+
});
|
|
937
|
+
if (!frame) return derive(next);
|
|
938
|
+
if (activePointIndex === 0) return [
|
|
939
|
+
snapToAxis(next[0], frame.p2, frame.direction),
|
|
940
|
+
clonePosition(previous[1]),
|
|
941
|
+
clonePosition(previous[2])
|
|
942
|
+
];
|
|
943
|
+
if (activePointIndex === 1) {
|
|
944
|
+
const p2 = snapToAxis(next[1], frame.p1, frame.direction);
|
|
945
|
+
const delta = vecSub(project(p2[0], p2[1]), frame.p2);
|
|
946
|
+
const projectedP3 = vecAdd(project(previous[2][0], previous[2][1]), delta);
|
|
947
|
+
const p3 = unproject(projectedP3[0], projectedP3[1]);
|
|
948
|
+
return [
|
|
949
|
+
clonePosition(previous[0]),
|
|
950
|
+
p2,
|
|
951
|
+
p3
|
|
952
|
+
];
|
|
953
|
+
}
|
|
954
|
+
if (activePointIndex === 2) {
|
|
955
|
+
const p3 = frame.pointAtNormalDistance(frame.signedNormalDistance(next[2]));
|
|
956
|
+
if (p3) return [
|
|
957
|
+
clonePosition(previous[0]),
|
|
958
|
+
clonePosition(previous[1]),
|
|
959
|
+
p3
|
|
960
|
+
];
|
|
961
|
+
}
|
|
962
|
+
return derive(next);
|
|
865
963
|
}
|
|
866
964
|
};
|
|
867
965
|
//#endregion
|
|
@@ -1754,7 +1852,7 @@ function createAttackHelicopter(coordinates, options = {}) {
|
|
|
1754
1852
|
type: "FeatureCollection",
|
|
1755
1853
|
features: []
|
|
1756
1854
|
};
|
|
1757
|
-
const center = lineIntersection(pL, pIR, pR, pIL);
|
|
1855
|
+
const center = lineIntersection(pL, pIR, pR, pIL) ?? geometry.ptBase;
|
|
1758
1856
|
const features = [{
|
|
1759
1857
|
type: "Feature",
|
|
1760
1858
|
properties: {},
|
|
@@ -2266,15 +2364,18 @@ function closedCatmullRom(points, samples) {
|
|
|
2266
2364
|
}
|
|
2267
2365
|
//#endregion
|
|
2268
2366
|
//#region src/internal/area-echelon.ts
|
|
2269
|
-
/**
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2367
|
+
/**
|
|
2368
|
+
* Twice the ring's signed area (shoelace). Positive when the ring winds
|
|
2369
|
+
* counter-clockwise in projected (y-up) coordinates.
|
|
2370
|
+
*/
|
|
2371
|
+
function ringSignedArea2(verts) {
|
|
2372
|
+
let area2 = 0;
|
|
2373
|
+
for (let i = 0; i < verts.length - 1; i++) {
|
|
2374
|
+
const a = verts[i];
|
|
2375
|
+
const b = verts[i + 1];
|
|
2376
|
+
area2 += a[0] * b[1] - b[0] * a[1];
|
|
2276
2377
|
}
|
|
2277
|
-
return
|
|
2378
|
+
return area2;
|
|
2278
2379
|
}
|
|
2279
2380
|
/**
|
|
2280
2381
|
* Projects `positions` and returns a closed ring (first == last), optionally
|
|
@@ -2294,7 +2395,7 @@ function buildClosedRing(positions, smooth, smoothResolution, defaultSmoothResol
|
|
|
2294
2395
|
* projected y), the side opposite the front.
|
|
2295
2396
|
*/
|
|
2296
2397
|
function buildAreaPerimeter(verts) {
|
|
2297
|
-
const
|
|
2398
|
+
const ccw = ringSignedArea2(verts) > 0;
|
|
2298
2399
|
const segs = [];
|
|
2299
2400
|
let totalLength = 0;
|
|
2300
2401
|
let anchorTarget = 0;
|
|
@@ -2307,8 +2408,7 @@ function buildAreaPerimeter(verts) {
|
|
|
2307
2408
|
if (length < 1e-6) continue;
|
|
2308
2409
|
const along = vecNorm(edge);
|
|
2309
2410
|
const mid = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
|
|
2310
|
-
|
|
2311
|
-
if (perp[0] * (mid[0] - centroid[0]) + perp[1] * (mid[1] - centroid[1]) < 0) perp = [-perp[0], -perp[1]];
|
|
2411
|
+
const perp = ccw ? [along[1], -along[0]] : [-along[1], along[0]];
|
|
2312
2412
|
segs.push({
|
|
2313
2413
|
start: a,
|
|
2314
2414
|
along,
|
|
@@ -2351,13 +2451,21 @@ function resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel) {
|
|
|
2351
2451
|
if (echelonSizePixels !== void 0 && metersPerPixel !== void 0 && metersPerPixel > 0) h = echelonSizePixels * metersPerPixel;
|
|
2352
2452
|
return Math.max(EPSILON, h);
|
|
2353
2453
|
}
|
|
2454
|
+
/** Wraps `x` into `[0, 1)`, treating `x` as a fraction (matches the arc-length wrap below). */
|
|
2455
|
+
function wrap01(x) {
|
|
2456
|
+
return (x % 1 + 1) % 1;
|
|
2457
|
+
}
|
|
2354
2458
|
/**
|
|
2355
2459
|
* Places the Field B echelon glyph on the perimeter and computes its masking
|
|
2356
|
-
* gap, anchored to the bottom edge and slid by `echelonPosition`
|
|
2357
|
-
* fraction of the perimeter, wrapping at the seam).
|
|
2358
|
-
*
|
|
2460
|
+
* gap, anchored by default to the bottom edge and slid by `echelonPosition`
|
|
2461
|
+
* (a signed fraction of the perimeter, wrapping at the seam). When
|
|
2462
|
+
* `echelonAnchor` is supplied, it replaces the bottom-edge anchor with an
|
|
2463
|
+
* absolute perimeter fraction (measured from the ring seam) — see
|
|
2464
|
+
* `BattlePositionOptions.echelonAnchor` for why (ADR-0023 rotate-freeze).
|
|
2465
|
+
* Returns empty geometry when the echelon resolves to none or the ring is
|
|
2466
|
+
* degenerate.
|
|
2359
2467
|
*/
|
|
2360
|
-
function placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition) {
|
|
2468
|
+
function placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition, echelonAnchor) {
|
|
2361
2469
|
const { segs, totalLength, anchorTarget } = perimeter;
|
|
2362
2470
|
const spec = resolveEchelon(echelon);
|
|
2363
2471
|
if (!spec || segs.length === 0 || totalLength < 1e-6) return {
|
|
@@ -2365,7 +2473,7 @@ function placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition) {
|
|
|
2365
2473
|
fills: [],
|
|
2366
2474
|
gaps: []
|
|
2367
2475
|
};
|
|
2368
|
-
const target = ((anchorTarget + echelonPosition * totalLength) % totalLength + totalLength) % totalLength;
|
|
2476
|
+
const target = (((echelonAnchor !== void 0 ? wrap01(echelonAnchor) * totalLength : anchorTarget) + echelonPosition * totalLength) % totalLength + totalLength) % totalLength;
|
|
2369
2477
|
const { point, seg } = pointOnRing(segs, target);
|
|
2370
2478
|
const { strokes, fills, width } = buildEchelonGlyph(point, seg.along, seg.perp, h, spec);
|
|
2371
2479
|
const gaps = [];
|
|
@@ -2475,10 +2583,10 @@ const BATTLE_POSITION_METADATA = {
|
|
|
2475
2583
|
* @param options - {@link BattlePositionOptions}.
|
|
2476
2584
|
*/
|
|
2477
2585
|
function createBattlePosition(positions, options = {}) {
|
|
2478
|
-
const { echelon = DEFAULT_BATTLE_POSITION_OPTIONS.echelon, echelonSize = DEFAULT_BATTLE_POSITION_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPadding, echelonPosition = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPosition, metersPerPixel, smooth = DEFAULT_BATTLE_POSITION_OPTIONS.smooth, smoothResolution = DEFAULT_BATTLE_POSITION_OPTIONS.smoothResolution } = options;
|
|
2586
|
+
const { echelon = DEFAULT_BATTLE_POSITION_OPTIONS.echelon, echelonSize = DEFAULT_BATTLE_POSITION_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPadding, echelonPosition = DEFAULT_BATTLE_POSITION_OPTIONS.echelonPosition, echelonAnchor, metersPerPixel, smooth = DEFAULT_BATTLE_POSITION_OPTIONS.smooth, smoothResolution = DEFAULT_BATTLE_POSITION_OPTIONS.smoothResolution } = options;
|
|
2479
2587
|
const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
|
|
2480
2588
|
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION$6);
|
|
2481
|
-
const { strokes, fills, gaps } = placeEchelon(buildAreaPerimeter(verts), h, echelon, echelonPadding, echelonPosition);
|
|
2589
|
+
const { strokes, fills, gaps } = placeEchelon(buildAreaPerimeter(verts), h, echelon, echelonPadding, echelonPosition, echelonAnchor);
|
|
2482
2590
|
const boundaryCoords = buildGappedLine(verts, gaps);
|
|
2483
2591
|
const features = [];
|
|
2484
2592
|
if (boundaryCoords.length > 0) features.push({
|
|
@@ -2513,11 +2621,29 @@ function createBattlePosition(positions, options = {}) {
|
|
|
2513
2621
|
features
|
|
2514
2622
|
};
|
|
2515
2623
|
}
|
|
2624
|
+
/**
|
|
2625
|
+
* `freezeOrientation` hook (ADR-0023) shared by Battle Position and Strong
|
|
2626
|
+
* Point: pins the echelon anchor to its current bottom-edge fraction so a
|
|
2627
|
+
* subsequent rigid rotation of `controlPoints` (e.g. the transform box's
|
|
2628
|
+
* rotate grip) does not re-derive a different "bottom edge" against the
|
|
2629
|
+
* rotated geometry. Returns `undefined` — nothing to freeze — when:
|
|
2630
|
+
* - `echelonAnchor` is already set (previously frozen or user-authored), or
|
|
2631
|
+
* - the echelon resolves to none (nothing visible to pin), or
|
|
2632
|
+
* - the ring is degenerate (no segments, or a near-zero perimeter).
|
|
2633
|
+
*/
|
|
2634
|
+
function freezeEchelonAnchor(controlPoints, options) {
|
|
2635
|
+
if (options.echelonAnchor !== void 0) return void 0;
|
|
2636
|
+
if (!resolveEchelon(options.echelon ?? DEFAULT_BATTLE_POSITION_OPTIONS.echelon)) return void 0;
|
|
2637
|
+
const perimeter = buildAreaPerimeter(buildClosedRing(controlPoints, options.smooth ?? DEFAULT_BATTLE_POSITION_OPTIONS.smooth, options.smoothResolution ?? DEFAULT_BATTLE_POSITION_OPTIONS.smoothResolution, DEFAULT_SMOOTH_RESOLUTION$6));
|
|
2638
|
+
if (perimeter.segs.length === 0 || perimeter.totalLength < 1e-6) return void 0;
|
|
2639
|
+
return { echelonAnchor: perimeter.anchorTarget / perimeter.totalLength };
|
|
2640
|
+
}
|
|
2516
2641
|
const BATTLE_POSITION = defineControlMeasure({
|
|
2517
2642
|
metadata: BATTLE_POSITION_METADATA,
|
|
2518
2643
|
generator: createBattlePosition,
|
|
2519
2644
|
defaultOptions: DEFAULT_BATTLE_POSITION_OPTIONS,
|
|
2520
2645
|
rule: area1DrawRule,
|
|
2646
|
+
freezeOrientation: freezeEchelonAnchor,
|
|
2521
2647
|
previewSample: {
|
|
2522
2648
|
controlPoints: [
|
|
2523
2649
|
[-1, -.5],
|
|
@@ -4127,30 +4253,41 @@ const DEFAULT_GENERIC_RECTANGLE_OPTIONS = { filled: false };
|
|
|
4127
4253
|
const GENERIC_RECTANGLE_METADATA = {
|
|
4128
4254
|
id: "rectangle",
|
|
4129
4255
|
name: "Rectangle",
|
|
4130
|
-
description: "A non-doctrinal
|
|
4256
|
+
description: "A non-doctrinal oriented rectangle defined by three adjacent corners.",
|
|
4131
4257
|
entity: "Generic Graphics",
|
|
4132
4258
|
entityType: "Basic Shape",
|
|
4133
4259
|
entitySubtype: "Rectangle",
|
|
4134
4260
|
value: "990103",
|
|
4135
|
-
minCoordinates:
|
|
4136
|
-
maxCoordinates:
|
|
4261
|
+
minCoordinates: 3,
|
|
4262
|
+
maxCoordinates: 3,
|
|
4137
4263
|
geometry: "area",
|
|
4138
4264
|
geometryTypes: ["Polygon"],
|
|
4139
4265
|
drawRule: "Rectangle",
|
|
4140
4266
|
params: FILLED_AREA_PARAMS
|
|
4141
4267
|
};
|
|
4142
4268
|
function createGenericRectangle(coordinates, options = {}) {
|
|
4143
|
-
const
|
|
4144
|
-
|
|
4145
|
-
|
|
4269
|
+
const [c1, c2, c3] = rectangleDrawRule.derive(coordinates);
|
|
4270
|
+
if (!c1 || !c2 || !c3) return {
|
|
4271
|
+
type: "FeatureCollection",
|
|
4272
|
+
features: []
|
|
4273
|
+
};
|
|
4274
|
+
const p1 = project(c1[0], c1[1]);
|
|
4275
|
+
const p2 = project(c2[0], c2[1]);
|
|
4276
|
+
const p3 = project(c3[0], c3[1]);
|
|
4277
|
+
if (Math.hypot(p2[0] - p1[0], p2[1] - p1[1]) < 1e-6) return {
|
|
4278
|
+
type: "FeatureCollection",
|
|
4279
|
+
features: []
|
|
4280
|
+
};
|
|
4281
|
+
const fill = options.filled ?? DEFAULT_GENERIC_RECTANGLE_OPTIONS.filled;
|
|
4282
|
+
if (Math.hypot(p3[0] - p2[0], p3[1] - p2[1]) < 1e-6) return {
|
|
4146
4283
|
type: "FeatureCollection",
|
|
4147
4284
|
features: []
|
|
4148
4285
|
};
|
|
4149
4286
|
const ring = [
|
|
4150
4287
|
p1,
|
|
4151
|
-
[p2[0], p1[1]],
|
|
4152
4288
|
p2,
|
|
4153
|
-
|
|
4289
|
+
p3,
|
|
4290
|
+
[p1[0] + p3[0] - p2[0], p1[1] + p3[1] - p2[1]],
|
|
4154
4291
|
p1
|
|
4155
4292
|
].map((point) => unproject(point[0], point[1]));
|
|
4156
4293
|
return {
|
|
@@ -4159,7 +4296,7 @@ function createGenericRectangle(coordinates, options = {}) {
|
|
|
4159
4296
|
type: "Feature",
|
|
4160
4297
|
properties: {
|
|
4161
4298
|
part: "rectangle",
|
|
4162
|
-
fill
|
|
4299
|
+
fill
|
|
4163
4300
|
},
|
|
4164
4301
|
geometry: {
|
|
4165
4302
|
type: "Polygon",
|
|
@@ -4173,7 +4310,11 @@ const GENERIC_RECTANGLE = defineControlMeasure({
|
|
|
4173
4310
|
generator: createGenericRectangle,
|
|
4174
4311
|
defaultOptions: DEFAULT_GENERIC_RECTANGLE_OPTIONS,
|
|
4175
4312
|
rule: rectangleDrawRule,
|
|
4176
|
-
previewSample: { controlPoints: [
|
|
4313
|
+
previewSample: { controlPoints: [
|
|
4314
|
+
[-.9, -.55],
|
|
4315
|
+
[.9, -.55],
|
|
4316
|
+
[.9, .55]
|
|
4317
|
+
] }
|
|
4177
4318
|
});
|
|
4178
4319
|
//#endregion
|
|
4179
4320
|
//#region src/generators/cm34-mission-tasks/clear.ts
|
|
@@ -6519,11 +6660,11 @@ function buildStrongPointTics(perimeter, gaps, h) {
|
|
|
6519
6660
|
* @param options - {@link StrongPointOptions}.
|
|
6520
6661
|
*/
|
|
6521
6662
|
function createStrongPoint(positions, options = {}) {
|
|
6522
|
-
const { echelon = DEFAULT_STRONG_POINT_OPTIONS.echelon, echelonSize = DEFAULT_STRONG_POINT_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_STRONG_POINT_OPTIONS.echelonPadding, echelonPosition = DEFAULT_STRONG_POINT_OPTIONS.echelonPosition, metersPerPixel, smooth = DEFAULT_STRONG_POINT_OPTIONS.smooth, smoothResolution = DEFAULT_STRONG_POINT_OPTIONS.smoothResolution } = options;
|
|
6663
|
+
const { echelon = DEFAULT_STRONG_POINT_OPTIONS.echelon, echelonSize = DEFAULT_STRONG_POINT_OPTIONS.echelonSize, echelonSizePixels, echelonPadding = DEFAULT_STRONG_POINT_OPTIONS.echelonPadding, echelonPosition = DEFAULT_STRONG_POINT_OPTIONS.echelonPosition, echelonAnchor, metersPerPixel, smooth = DEFAULT_STRONG_POINT_OPTIONS.smooth, smoothResolution = DEFAULT_STRONG_POINT_OPTIONS.smoothResolution } = options;
|
|
6523
6664
|
const h = resolveEchelonHeight(echelonSize, echelonSizePixels, metersPerPixel);
|
|
6524
6665
|
const verts = buildClosedRing(positions, smooth, smoothResolution, DEFAULT_SMOOTH_RESOLUTION);
|
|
6525
6666
|
const perimeter = buildAreaPerimeter(verts);
|
|
6526
|
-
const { strokes, fills, gaps } = placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition);
|
|
6667
|
+
const { strokes, fills, gaps } = placeEchelon(perimeter, h, echelon, echelonPadding, echelonPosition, echelonAnchor);
|
|
6527
6668
|
const boundaryCoords = buildGappedLine(verts, gaps);
|
|
6528
6669
|
const tics = buildStrongPointTics(perimeter, gaps, h);
|
|
6529
6670
|
const features = [];
|
|
@@ -6572,6 +6713,7 @@ const STRONG_POINT = defineControlMeasure({
|
|
|
6572
6713
|
generator: createStrongPoint,
|
|
6573
6714
|
defaultOptions: DEFAULT_STRONG_POINT_OPTIONS,
|
|
6574
6715
|
rule: area1DrawRule,
|
|
6716
|
+
freezeOrientation: freezeEchelonAnchor,
|
|
6575
6717
|
previewSample: {
|
|
6576
6718
|
controlPoints: [
|
|
6577
6719
|
[-1, -.5],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orbat-mapper/control-measures",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.7",
|
|
4
4
|
"description": "Library for drawing tactical graphics and control measures according to MIL-STD-2525 and APP-6 standards.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Orbat Mapper",
|