@orbat-mapper/control-measures 0.2.0-alpha.27 → 0.2.0-alpha.29
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-B5pIINgW.d.mts → index-J_5z80SV.d.mts} +18 -5
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/preview/index.d.mts +6 -3
- package/dist/preview/index.mjs +20 -5
- package/dist/{renderControlMeasure-Chc1sdHN.mjs → renderControlMeasure-BdbObD8B.mjs} +159 -30
- package/package.json +1 -1
|
@@ -310,7 +310,7 @@ interface ControlMeasureMetadata {
|
|
|
310
310
|
* control would be inert.
|
|
311
311
|
* - `"fixed"` — a filled part exists, but the generator pins its
|
|
312
312
|
* `fillPattern` (a solid accent like an echelon marker, or a doctrinal
|
|
313
|
-
* hatch like Limited Access Area's back-
|
|
313
|
+
* hatch like Limited Access Area's back-hatch) — `cm.style.fillPattern`
|
|
314
314
|
* never reaches it. `fillColor` is *not* pinned, so a fill color override
|
|
315
315
|
* still reaches the part; only the pattern control is inert.
|
|
316
316
|
* - `"user"` — a filled part exists whose pattern the generator leaves
|
|
@@ -749,7 +749,7 @@ interface ControlMeasureStyle {
|
|
|
749
749
|
fillColor?: string;
|
|
750
750
|
/**
|
|
751
751
|
* How a filled part's interior is painted. `"solid"` is explicit so a
|
|
752
|
-
* measure can override a patterned layer default (e.g. a
|
|
752
|
+
* measure can override a patterned layer default (e.g. a hatch fill set
|
|
753
753
|
* at the façade). Pattern lines are drawn in the resolved fill color —
|
|
754
754
|
* alpha included, there is no separate opacity field. Renderers that
|
|
755
755
|
* cannot draw a pattern degrade to solid.
|
|
@@ -767,11 +767,26 @@ interface ControlMeasureStyle {
|
|
|
767
767
|
* Out-of-range values clamp to `[0, 1]`. See ADR-0040.
|
|
768
768
|
*/
|
|
769
769
|
opacity?: number;
|
|
770
|
+
/**
|
|
771
|
+
* Render a contrast-aware halo behind label glyphs so text stays legible on
|
|
772
|
+
* busy backgrounds. Cascades like any other hint (graphicsStyle → measure →
|
|
773
|
+
* generator), letting a host switch halos on for every labeled measure
|
|
774
|
+
* rather than only the generic Text graphic.
|
|
775
|
+
*
|
|
776
|
+
* Text-only: the renderer applies it to features that carry `text` and drops
|
|
777
|
+
* it everywhere else, so a layer-level `true` never leaks onto plain stroked
|
|
778
|
+
* or filled parts. The halo *color* is not authored here — it is derived per
|
|
779
|
+
* feature from the resolved text color, so each label gets a halo that
|
|
780
|
+
* actually contrasts with it. Unlike the other hints this one is consumed by
|
|
781
|
+
* the renderer and lifted to a top-level `textHalo` feature property; it is
|
|
782
|
+
* never emitted on `properties.style`. See ADR-0042.
|
|
783
|
+
*/
|
|
784
|
+
textHalo?: boolean;
|
|
770
785
|
}
|
|
771
786
|
/**
|
|
772
787
|
* How a filled part's interior is painted. See `ControlMeasureStyle.fillPattern`.
|
|
773
788
|
*/
|
|
774
|
-
type FillPattern = "solid" | "
|
|
789
|
+
type FillPattern = "solid" | "hatch" | "reverse-hatch" | "cross-hatch" | "horizontal" | "vertical" | "dots";
|
|
775
790
|
//#endregion
|
|
776
791
|
//#region src/internal/area-labels.d.ts
|
|
777
792
|
/** Shared placement options for the centered-label area measures. */
|
|
@@ -1706,8 +1721,6 @@ interface GenericTextOptions {
|
|
|
1706
1721
|
* @default 200
|
|
1707
1722
|
*/
|
|
1708
1723
|
maxSizePixels?: number;
|
|
1709
|
-
/** Contrasting halo behind the glyphs so the text stays legible on busy backgrounds. @default false */
|
|
1710
|
-
halo?: boolean;
|
|
1711
1724
|
}
|
|
1712
1725
|
declare const DEFAULT_GENERIC_TEXT_OPTIONS: GenericTextOptions;
|
|
1713
1726
|
declare function createGenericText(coordinates: Position[], options?: GenericTextOptions): FeatureCollection<Point, Record<string, unknown>>;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, $n as DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS, $t as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, A as roundToFixed, An as PickupZoneOptions, Ar as CanonicalTextAmplifiers, At as DEFAULT_COVER_OPTIONS, B as cloneControlMeasure, Bn as EncirclementOptions, Br as AnchorTransformEvent, Bt as DEFAULT_BREACH_OPTIONS, C as BaselineFrame, Cn as DEFAULT_MAIN_ATTACK_OPTIONS, Cr as ControlMeasureGeometryType, Ct as DEFAULT_SCREEN_OPTIONS, D as createBaselineFrame, Dn as AreaOfOperationsOptions, Dr as TextAmplifierDescriptor, Dt as DEFAULT_DELAY_OPTIONS, E as BaselineFrameOrigin, En as computeInitialWidthPoint, Er as ParamDescriptor, Et as GuardOptions, F as applyBoxTransformOptions, Fn as AssemblyAreaOptions, Fr as TextAmplifierKey, Ft as CanalizeOptions, G as ControlMeasureKind, Gn as StyleHints, Gt as DEFAULT_HANDOVER_LINE_OPTIONS, H as CONTROL_MEASURE_IDS, Hn as FinalProtectiveFireOptions, Ht as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, I as freezeOrientationOptions, In as DEFAULT_ASSEMBLY_AREA_OPTIONS, Ir as TextAmplifiers, It as DEFAULT_CANALIZE_OPTIONS, J as getControlMeasureMetadataByValue, Jn as PrincipalDirectionOfFireOptions, Jt as PhaseLineOptions, K as OptionsByKind, Kn as controlMeasureIdFromFeature, Kt as HandoverLineOptions, L as RenderOptions, Ln as AreaDefenseOptions, Lr as canonicalTextAmplifierKey, Lt as BypassOptions, M as SimpleStyleRender, Mn as LandingZoneOptions, Mr as LabelPlacementOverride, Mt as TacticalArrowOptions, N as toSimpleStyle, Nn as DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS, Nr as TEXT_AMPLIFIER_FIELDS, Nt as ClearOptions, O as EPSILON, On as DEFAULT_AREA_OF_OPERATIONS_OPTIONS, Or as AmplifierPlacement, Ot as DelayOptions, P as resolveStyleHints, Pn as JointTacticalActionAreaOptions, Pr as TextAmplifierField, Pt as DEFAULT_CLEAR_OPTIONS, Q as ObstacleBypassImpossibleOptions, Qn as DirectionOfMainAttackOptions, Qt as DEFAULT_ATTACK_BY_FIRE_OPTIONS, R as renderControlMeasure, Rn as DEFAULT_AREA_DEFENSE_OPTIONS, Rr as normalizeTextAmplifiers, Rt as DEFAULT_BYPASS_OPTIONS, S as snapToMidpointPerpendicular, Sn as SupportingAttackOptions, Sr as ControlMeasureGeometry, St as DEFAULT_ANTITANK_DITCH_OPTIONS, T as BaselineFrameOptions, Tn as calculateMetrics, Tr as ControlMeasurePaintCapability, Tt as DEFAULT_GUARD_OPTIONS, U as CONTROL_MEASURE_METADATA, Un as ControlMeasureRender, Ut as BattleHandoverLineOptions, V as isKind, Vn as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, Vr as ControlMeasureDrawRule, Vt as BlockMissionTaskOptions, W as ControlMeasureId, Wn as FeaturePartProps, Wt as DEFAULT_BATTLE_HANDOVER_LINE_OPTIONS, X as listControlMeasureMetadata, Xn as DirectionOfSupportingAttackOptions, Xt as FLOTOptions, Y as getDefaultOptions, Yn as DEFAULT_DIRECTION_OF_SUPPORTING_ATTACK_OPTIONS, Yt as DEFAULT_FLOT_OPTIONS, Z as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, Zn as DEFAULT_DIRECTION_OF_MAIN_ATTACK_OPTIONS, Zt as AttackByFireOptions, _ as MidpointPerpendicularDrawRuleOptions, _n as DEFAULT_BLOCK_ARROW_OPTIONS, _r as unproject, _t as DEFAULT_FORTIFIED_LINE_OPTIONS, a as supportByFireDrawRule, an as DEFAULT_GENERIC_TEXT_OPTIONS, ar as ControlMeasureStyle, at as DEFAULT_FIX_OPTIONS, b as getMidpointPerpendicularSignedDistance, bn as DEFAULT_CLASSIC_ARROW_OPTIONS, br as BoxTransformDelta, bt as DEFAULT_ANTITANK_WALL_OPTIONS, c as line23DrawRule, cn as GenericCircleOptions, cr as GenericC2LineOptions, ct as DisruptOptions, d as ambushDrawRule, dn as DEFAULT_GENERIC_POLYGON_OPTIONS, dr as DEFAULT_LIGHT_LINE_OPTIONS, dt as DEFAULT_TURNING_MOVEMENT_OPTIONS, en as SupportByFireOptions, er as DirectionOfAttackAviationOptions, et as ObstacleBypassDifficultOptions, f as attackByFireDrawRule, fn as GenericPolygonOptions, fr as LightLineOptions, ft as TurningMovementOptions, g as blockDrawRule, gn as BlockArrowOptions, gr as project, gt as FortifiedAreaOptions, h as disruptDrawRule, hn as BlockArrowHeadStyle, hr as haversineDistance, ht as DEFAULT_FORTIFIED_AREA_OPTIONS, i as axis1DrawRule, in as DEFAULT_AIRBORNE_ATTACK_OPTIONS, ir as DEFAULT_BATTLE_POSITION_OPTIONS, it as TurnOptions, j as SimpleStyleProps, jn as DEFAULT_LANDING_ZONE_OPTIONS, jr as GeneratedLabelKey, jt as DEFAULT_TACTICAL_ARROW_OPTIONS, k as getMetersPerPixel, kn as DEFAULT_PICKUP_ZONE_OPTIONS, kr as AmplifierPlacements, kt as CoverOptions, l as turnDrawRule, ln as DEFAULT_GENERIC_RECTANGLE_OPTIONS, lr as DEFAULT_ENGINEER_WORK_LINE_OPTIONS, lt as BlockOptions, m as centerRadiusDrawRule, mn as GenericLineOptions, mr as Point2D, mt as FrontalAttackOptions, n as DEFAULT_AMBUSH_OPTIONS, nn as DEFAULT_ATTACK_HELICOPTER_OPTIONS, nr as StrongPointOptions, nt as ObstacleBypassEasyOptions, o as line26DrawRule, on as GenericTextOptions, or as FillPattern, ot as FixOptions, p as point12DrawRule, pn as DEFAULT_GENERIC_LINE_OPTIONS, pr as LabelSizeOptions, pt as DEFAULT_FRONTAL_ATTACK_OPTIONS, q as getControlMeasureMetadata, qn as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, qt as DEFAULT_PHASE_LINE_OPTIONS, r as rectangleDrawRule, rn as AirborneAttackOptions, rr as BattlePositionOptions, rt as DEFAULT_TURN_OPTIONS, s as line24DrawRule, sn as DEFAULT_GENERIC_CIRCLE_OPTIONS, sr as DEFAULT_GENERIC_C2_LINE_OPTIONS, st as DEFAULT_DISRUPT_OPTIONS, t as AmbushOptions, tn as AttackHelicopterOptions, tr as DEFAULT_STRONG_POINT_OPTIONS, tt as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, u as line1DrawRule, un as GenericRectangleOptions, ur as EngineerWorkLineOptions, ut as DEFAULT_BLOCK_OPTIONS, v as computeDefaultMidpointPerpendicularPoint, vn as ClassicArrowHeadStyle, vr as BoundaryOptions, vt as FortifiedLineOptions, w as BaselineFrameNormal, wn as MainAttackOptions, wr as ControlMeasureMetadata, wt as ScreenOptions, x as pointOnMidpointPerpendicularAxis, xn as DEFAULT_SUPPORTING_ATTACK_OPTIONS, xr as ControlMeasureFillCapability, xt as AntitankDitchOptions, y as createMidpointPerpendicularDrawRule, yn as ClassicArrowOptions, yr as DEFAULT_BOUNDARY_OPTIONS, yt as AntitankWallOptions, z as ControlMeasure, zn as DEFAULT_ENCIRCLEMENT_OPTIONS, zr as resolveAmplifierPlacement, zt as BreachOptions } from "./index-
|
|
1
|
+
import { $ as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, $n as DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS, $t as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, A as roundToFixed, An as PickupZoneOptions, Ar as CanonicalTextAmplifiers, At as DEFAULT_COVER_OPTIONS, B as cloneControlMeasure, Bn as EncirclementOptions, Br as AnchorTransformEvent, Bt as DEFAULT_BREACH_OPTIONS, C as BaselineFrame, Cn as DEFAULT_MAIN_ATTACK_OPTIONS, Cr as ControlMeasureGeometryType, Ct as DEFAULT_SCREEN_OPTIONS, D as createBaselineFrame, Dn as AreaOfOperationsOptions, Dr as TextAmplifierDescriptor, Dt as DEFAULT_DELAY_OPTIONS, E as BaselineFrameOrigin, En as computeInitialWidthPoint, Er as ParamDescriptor, Et as GuardOptions, F as applyBoxTransformOptions, Fn as AssemblyAreaOptions, Fr as TextAmplifierKey, Ft as CanalizeOptions, G as ControlMeasureKind, Gn as StyleHints, Gt as DEFAULT_HANDOVER_LINE_OPTIONS, H as CONTROL_MEASURE_IDS, Hn as FinalProtectiveFireOptions, Ht as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, I as freezeOrientationOptions, In as DEFAULT_ASSEMBLY_AREA_OPTIONS, Ir as TextAmplifiers, It as DEFAULT_CANALIZE_OPTIONS, J as getControlMeasureMetadataByValue, Jn as PrincipalDirectionOfFireOptions, Jt as PhaseLineOptions, K as OptionsByKind, Kn as controlMeasureIdFromFeature, Kt as HandoverLineOptions, L as RenderOptions, Ln as AreaDefenseOptions, Lr as canonicalTextAmplifierKey, Lt as BypassOptions, M as SimpleStyleRender, Mn as LandingZoneOptions, Mr as LabelPlacementOverride, Mt as TacticalArrowOptions, N as toSimpleStyle, Nn as DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS, Nr as TEXT_AMPLIFIER_FIELDS, Nt as ClearOptions, O as EPSILON, On as DEFAULT_AREA_OF_OPERATIONS_OPTIONS, Or as AmplifierPlacement, Ot as DelayOptions, P as resolveStyleHints, Pn as JointTacticalActionAreaOptions, Pr as TextAmplifierField, Pt as DEFAULT_CLEAR_OPTIONS, Q as ObstacleBypassImpossibleOptions, Qn as DirectionOfMainAttackOptions, Qt as DEFAULT_ATTACK_BY_FIRE_OPTIONS, R as renderControlMeasure, Rn as DEFAULT_AREA_DEFENSE_OPTIONS, Rr as normalizeTextAmplifiers, Rt as DEFAULT_BYPASS_OPTIONS, S as snapToMidpointPerpendicular, Sn as SupportingAttackOptions, Sr as ControlMeasureGeometry, St as DEFAULT_ANTITANK_DITCH_OPTIONS, T as BaselineFrameOptions, Tn as calculateMetrics, Tr as ControlMeasurePaintCapability, Tt as DEFAULT_GUARD_OPTIONS, U as CONTROL_MEASURE_METADATA, Un as ControlMeasureRender, Ut as BattleHandoverLineOptions, V as isKind, Vn as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, Vr as ControlMeasureDrawRule, Vt as BlockMissionTaskOptions, W as ControlMeasureId, Wn as FeaturePartProps, Wt as DEFAULT_BATTLE_HANDOVER_LINE_OPTIONS, X as listControlMeasureMetadata, Xn as DirectionOfSupportingAttackOptions, Xt as FLOTOptions, Y as getDefaultOptions, Yn as DEFAULT_DIRECTION_OF_SUPPORTING_ATTACK_OPTIONS, Yt as DEFAULT_FLOT_OPTIONS, Z as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, Zn as DEFAULT_DIRECTION_OF_MAIN_ATTACK_OPTIONS, Zt as AttackByFireOptions, _ as MidpointPerpendicularDrawRuleOptions, _n as DEFAULT_BLOCK_ARROW_OPTIONS, _r as unproject, _t as DEFAULT_FORTIFIED_LINE_OPTIONS, a as supportByFireDrawRule, an as DEFAULT_GENERIC_TEXT_OPTIONS, ar as ControlMeasureStyle, at as DEFAULT_FIX_OPTIONS, b as getMidpointPerpendicularSignedDistance, bn as DEFAULT_CLASSIC_ARROW_OPTIONS, br as BoxTransformDelta, bt as DEFAULT_ANTITANK_WALL_OPTIONS, c as line23DrawRule, cn as GenericCircleOptions, cr as GenericC2LineOptions, ct as DisruptOptions, d as ambushDrawRule, dn as DEFAULT_GENERIC_POLYGON_OPTIONS, dr as DEFAULT_LIGHT_LINE_OPTIONS, dt as DEFAULT_TURNING_MOVEMENT_OPTIONS, en as SupportByFireOptions, er as DirectionOfAttackAviationOptions, et as ObstacleBypassDifficultOptions, f as attackByFireDrawRule, fn as GenericPolygonOptions, fr as LightLineOptions, ft as TurningMovementOptions, g as blockDrawRule, gn as BlockArrowOptions, gr as project, gt as FortifiedAreaOptions, h as disruptDrawRule, hn as BlockArrowHeadStyle, hr as haversineDistance, ht as DEFAULT_FORTIFIED_AREA_OPTIONS, i as axis1DrawRule, in as DEFAULT_AIRBORNE_ATTACK_OPTIONS, ir as DEFAULT_BATTLE_POSITION_OPTIONS, it as TurnOptions, j as SimpleStyleProps, jn as DEFAULT_LANDING_ZONE_OPTIONS, jr as GeneratedLabelKey, jt as DEFAULT_TACTICAL_ARROW_OPTIONS, k as getMetersPerPixel, kn as DEFAULT_PICKUP_ZONE_OPTIONS, kr as AmplifierPlacements, kt as CoverOptions, l as turnDrawRule, ln as DEFAULT_GENERIC_RECTANGLE_OPTIONS, lr as DEFAULT_ENGINEER_WORK_LINE_OPTIONS, lt as BlockOptions, m as centerRadiusDrawRule, mn as GenericLineOptions, mr as Point2D, mt as FrontalAttackOptions, n as DEFAULT_AMBUSH_OPTIONS, nn as DEFAULT_ATTACK_HELICOPTER_OPTIONS, nr as StrongPointOptions, nt as ObstacleBypassEasyOptions, o as line26DrawRule, on as GenericTextOptions, or as FillPattern, ot as FixOptions, p as point12DrawRule, pn as DEFAULT_GENERIC_LINE_OPTIONS, pr as LabelSizeOptions, pt as DEFAULT_FRONTAL_ATTACK_OPTIONS, q as getControlMeasureMetadata, qn as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, qt as DEFAULT_PHASE_LINE_OPTIONS, r as rectangleDrawRule, rn as AirborneAttackOptions, rr as BattlePositionOptions, rt as DEFAULT_TURN_OPTIONS, s as line24DrawRule, sn as DEFAULT_GENERIC_CIRCLE_OPTIONS, sr as DEFAULT_GENERIC_C2_LINE_OPTIONS, st as DEFAULT_DISRUPT_OPTIONS, t as AmbushOptions, tn as AttackHelicopterOptions, tr as DEFAULT_STRONG_POINT_OPTIONS, tt as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, u as line1DrawRule, un as GenericRectangleOptions, ur as EngineerWorkLineOptions, ut as DEFAULT_BLOCK_OPTIONS, v as computeDefaultMidpointPerpendicularPoint, vn as ClassicArrowHeadStyle, vr as BoundaryOptions, vt as FortifiedLineOptions, w as BaselineFrameNormal, wn as MainAttackOptions, wr as ControlMeasureMetadata, wt as ScreenOptions, x as pointOnMidpointPerpendicularAxis, xn as DEFAULT_SUPPORTING_ATTACK_OPTIONS, xr as ControlMeasureFillCapability, xt as AntitankDitchOptions, y as createMidpointPerpendicularDrawRule, yn as ClassicArrowOptions, yr as DEFAULT_BOUNDARY_OPTIONS, yt as AntitankWallOptions, z as ControlMeasure, zn as DEFAULT_ENCIRCLEMENT_OPTIONS, zr as resolveAmplifierPlacement, zt as BreachOptions } from "./index-J_5z80SV.mjs";
|
|
2
2
|
export { type AirborneAttackOptions, type AmbushOptions, type AmplifierPlacement, type AmplifierPlacements, type AnchorTransformEvent, type AntitankDitchOptions, type AntitankWallOptions, type AreaDefenseOptions, type AreaOfOperationsOptions, type AssemblyAreaOptions, type AttackByFireOptions, type AttackHelicopterOptions, type BaselineFrame, type BaselineFrameNormal, type BaselineFrameOptions, type BaselineFrameOrigin, type BattleHandoverLineOptions, type BattlePositionOptions, type BlockArrowHeadStyle, type BlockArrowOptions, type BlockMissionTaskOptions, type BlockOptions, type BoundaryOptions, type BoxTransformDelta, type BreachOptions, type BypassOptions, CONTROL_MEASURE_IDS, CONTROL_MEASURE_METADATA, type CanalizeOptions, type CanonicalTextAmplifiers, type ClassicArrowHeadStyle, type ClassicArrowOptions, type ClearOptions, type ControlMeasure, type ControlMeasureDrawRule, type ControlMeasureFillCapability, type ControlMeasureGeometry, type ControlMeasureGeometryType, type ControlMeasureId, type ControlMeasureKind, type ControlMeasureMetadata, type ControlMeasurePaintCapability, type ControlMeasureRender, type ControlMeasureStyle, type CoverOptions, DEFAULT_AIRBORNE_ATTACK_OPTIONS, DEFAULT_AMBUSH_OPTIONS, DEFAULT_ANTITANK_DITCH_OPTIONS, DEFAULT_ANTITANK_WALL_OPTIONS, DEFAULT_AREA_DEFENSE_OPTIONS, DEFAULT_AREA_OF_OPERATIONS_OPTIONS, DEFAULT_ASSEMBLY_AREA_OPTIONS, DEFAULT_ATTACK_BY_FIRE_OPTIONS, DEFAULT_ATTACK_HELICOPTER_OPTIONS, DEFAULT_BATTLE_HANDOVER_LINE_OPTIONS, DEFAULT_BATTLE_POSITION_OPTIONS, DEFAULT_BLOCK_ARROW_OPTIONS, DEFAULT_BLOCK_MISSION_TASK_OPTIONS, DEFAULT_BLOCK_OPTIONS, DEFAULT_BOUNDARY_OPTIONS, DEFAULT_BREACH_OPTIONS, DEFAULT_BYPASS_OPTIONS, DEFAULT_CANALIZE_OPTIONS, DEFAULT_CLASSIC_ARROW_OPTIONS, DEFAULT_CLEAR_OPTIONS, DEFAULT_COVER_OPTIONS, DEFAULT_DELAY_OPTIONS, DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS, DEFAULT_DIRECTION_OF_MAIN_ATTACK_OPTIONS, DEFAULT_DIRECTION_OF_SUPPORTING_ATTACK_OPTIONS, DEFAULT_DISRUPT_OPTIONS, DEFAULT_ENCIRCLEMENT_OPTIONS, DEFAULT_ENGINEER_WORK_LINE_OPTIONS, DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, DEFAULT_FIX_OPTIONS, DEFAULT_FLOT_OPTIONS, DEFAULT_FORTIFIED_AREA_OPTIONS, DEFAULT_FORTIFIED_LINE_OPTIONS, DEFAULT_FRONTAL_ATTACK_OPTIONS, DEFAULT_GENERIC_C2_LINE_OPTIONS, DEFAULT_GENERIC_CIRCLE_OPTIONS, DEFAULT_GENERIC_LINE_OPTIONS, DEFAULT_GENERIC_POLYGON_OPTIONS, DEFAULT_GENERIC_RECTANGLE_OPTIONS, DEFAULT_GENERIC_TEXT_OPTIONS, DEFAULT_GUARD_OPTIONS, DEFAULT_HANDOVER_LINE_OPTIONS, DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS, DEFAULT_LANDING_ZONE_OPTIONS, DEFAULT_LIGHT_LINE_OPTIONS, DEFAULT_MAIN_ATTACK_OPTIONS, DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, DEFAULT_PHASE_LINE_OPTIONS, DEFAULT_PICKUP_ZONE_OPTIONS, DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, DEFAULT_SCREEN_OPTIONS, DEFAULT_STRONG_POINT_OPTIONS, DEFAULT_SUPPORTING_ATTACK_OPTIONS, DEFAULT_SUPPORT_BY_FIRE_OPTIONS, DEFAULT_TACTICAL_ARROW_OPTIONS, DEFAULT_TURNING_MOVEMENT_OPTIONS, DEFAULT_TURN_OPTIONS, type DelayOptions, type DirectionOfAttackAviationOptions, type DirectionOfMainAttackOptions, type DirectionOfSupportingAttackOptions, type DisruptOptions, EPSILON, type EncirclementOptions, type EngineerWorkLineOptions, type FLOTOptions, type FeaturePartProps, type FillPattern, type FinalProtectiveFireOptions, type FixOptions, type FortifiedAreaOptions, type FortifiedLineOptions, type FrontalAttackOptions, type GeneratedLabelKey, type GenericC2LineOptions, type GenericCircleOptions, type GenericLineOptions, type GenericPolygonOptions, type GenericRectangleOptions, type GenericTextOptions, type GuardOptions, type HandoverLineOptions, type JointTacticalActionAreaOptions, type LabelPlacementOverride, type LabelSizeOptions, type LandingZoneOptions, type LightLineOptions, type MainAttackOptions, type MidpointPerpendicularDrawRuleOptions, type ObstacleBypassDifficultOptions, type ObstacleBypassEasyOptions, type ObstacleBypassImpossibleOptions, type OptionsByKind, type ParamDescriptor, type PhaseLineOptions, type PickupZoneOptions, type Point2D, type PrincipalDirectionOfFireOptions, type RenderOptions, type ScreenOptions, type SimpleStyleProps, type SimpleStyleRender, type StrongPointOptions, type StyleHints, type SupportByFireOptions, type SupportingAttackOptions, TEXT_AMPLIFIER_FIELDS, type TacticalArrowOptions, type TextAmplifierDescriptor, type TextAmplifierField, type TextAmplifierKey, type TextAmplifiers, type TurnOptions, type TurningMovementOptions, ambushDrawRule, applyBoxTransformOptions, attackByFireDrawRule, axis1DrawRule, blockDrawRule, calculateMetrics, canonicalTextAmplifierKey, centerRadiusDrawRule, cloneControlMeasure, computeDefaultMidpointPerpendicularPoint, computeInitialWidthPoint, controlMeasureIdFromFeature, createBaselineFrame, createMidpointPerpendicularDrawRule, disruptDrawRule, freezeOrientationOptions, getControlMeasureMetadata, getControlMeasureMetadataByValue, getDefaultOptions, getMetersPerPixel, getMidpointPerpendicularSignedDistance, haversineDistance, isKind, line1DrawRule, line23DrawRule, line24DrawRule, line26DrawRule, listControlMeasureMetadata, normalizeTextAmplifiers, point12DrawRule, pointOnMidpointPerpendicularAxis, project, rectangleDrawRule, renderControlMeasure, resolveAmplifierPlacement, resolveStyleHints, roundToFixed, snapToMidpointPerpendicular, supportByFireDrawRule, toSimpleStyle, turnDrawRule, unproject };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as DEFAULT_BOUNDARY_OPTIONS, A as DEFAULT_FLOT_OPTIONS, At as centerRadiusDrawRule, B as DEFAULT_GENERIC_TEXT_OPTIONS, Bt as computeInitialWidthPoint, C as DEFAULT_SCREEN_OPTIONS, Ct as line24DrawRule, D as DEFAULT_BATTLE_HANDOVER_LINE_OPTIONS, Dt as ambushDrawRule, E as DEFAULT_FORTIFIED_LINE_OPTIONS, Et as line1DrawRule, F as DEFAULT_GUARD_OPTIONS, Ft as getMidpointPerpendicularSignedDistance, G as DEFAULT_CLASSIC_ARROW_OPTIONS, Gt as getMetersPerPixel, H as DEFAULT_GENERIC_POLYGON_OPTIONS, Ht as project, I as DEFAULT_DELAY_OPTIONS, It as pointOnMidpointPerpendicularAxis, J as DEFAULT_BREACH_OPTIONS, K as DEFAULT_CANALIZE_OPTIONS, Kt as roundToFixed, L as DEFAULT_COVER_OPTIONS, Lt as snapToMidpointPerpendicular, M as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, Mt as blockDrawRule, N as DEFAULT_ENCIRCLEMENT_OPTIONS, Nt as computeDefaultMidpointPerpendicularPoint, O as DEFAULT_HANDOVER_LINE_OPTIONS, Ot as attackByFireDrawRule, P as DEFAULT_DISRUPT_OPTIONS, Pt as createMidpointPerpendicularDrawRule, Q as DEFAULT_LIGHT_LINE_OPTIONS, R as DEFAULT_TACTICAL_ARROW_OPTIONS, Rt as createBaselineFrame, S as DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS, St as line26DrawRule, T as DEFAULT_FORTIFIED_AREA_OPTIONS, Tt as turnDrawRule, U as DEFAULT_GENERIC_LINE_OPTIONS, Ut as unproject, V as DEFAULT_GENERIC_RECTANGLE_OPTIONS, Vt as haversineDistance, W as DEFAULT_GENERIC_CIRCLE_OPTIONS, Wt as EPSILON, X as DEFAULT_GENERIC_C2_LINE_OPTIONS, Y as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, Z as DEFAULT_ENGINEER_WORK_LINE_OPTIONS, _ as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, _t as DEFAULT_AMBUSH_OPTIONS, a as DEFINITIONS, at as DEFAULT_ANTITANK_WALL_OPTIONS, b as DEFAULT_MAIN_ATTACK_OPTIONS, bt as axis1DrawRule, c as getDefaultOptions, ct as DEFAULT_AREA_OF_OPERATIONS_OPTIONS, d as DEFAULT_TURN_OPTIONS, dt as canonicalTextAmplifierKey, et as DEFAULT_BLOCK_ARROW_OPTIONS, f as DEFAULT_SUPPORTING_ATTACK_OPTIONS, ft as normalizeTextAmplifiers, g as DEFAULT_PICKUP_ZONE_OPTIONS, gt as DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS, h as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, ht as DEFAULT_DIRECTION_OF_MAIN_ATTACK_OPTIONS, i as CONTROL_MEASURE_METADATA, it as DEFAULT_ATTACK_BY_FIRE_OPTIONS, j as DEFAULT_FIX_OPTIONS, jt as disruptDrawRule, k as DEFAULT_PHASE_LINE_OPTIONS, kt as point12DrawRule, l as listControlMeasureMetadata, lt as DEFAULT_AREA_DEFENSE_OPTIONS, m as DEFAULT_STRONG_POINT_OPTIONS, mt as DEFAULT_DIRECTION_OF_SUPPORTING_ATTACK_OPTIONS, n as resolveStyleHints, nt as DEFAULT_BATTLE_POSITION_OPTIONS, o as getControlMeasureMetadata, ot as DEFAULT_ANTITANK_DITCH_OPTIONS, p as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, pt as resolveAmplifierPlacement, q as DEFAULT_BYPASS_OPTIONS, r as CONTROL_MEASURE_IDS, rt as DEFAULT_ATTACK_HELICOPTER_OPTIONS, s as getControlMeasureMetadataByValue, st as DEFAULT_ASSEMBLY_AREA_OPTIONS, t as renderControlMeasure, tt as DEFAULT_BLOCK_OPTIONS, u as DEFAULT_TURNING_MOVEMENT_OPTIONS, ut as TEXT_AMPLIFIER_FIELDS, v as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, vt as DEFAULT_AIRBORNE_ATTACK_OPTIONS, w as DEFAULT_FRONTAL_ATTACK_OPTIONS, wt as line23DrawRule, x as DEFAULT_LANDING_ZONE_OPTIONS, xt as supportByFireDrawRule, y as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, yt as rectangleDrawRule, z as DEFAULT_CLEAR_OPTIONS, zt as calculateMetrics } from "./renderControlMeasure-
|
|
1
|
+
import { $ as DEFAULT_BOUNDARY_OPTIONS, A as DEFAULT_FLOT_OPTIONS, At as centerRadiusDrawRule, B as DEFAULT_GENERIC_TEXT_OPTIONS, Bt as computeInitialWidthPoint, C as DEFAULT_SCREEN_OPTIONS, Ct as line24DrawRule, D as DEFAULT_BATTLE_HANDOVER_LINE_OPTIONS, Dt as ambushDrawRule, E as DEFAULT_FORTIFIED_LINE_OPTIONS, Et as line1DrawRule, F as DEFAULT_GUARD_OPTIONS, Ft as getMidpointPerpendicularSignedDistance, G as DEFAULT_CLASSIC_ARROW_OPTIONS, Gt as getMetersPerPixel, H as DEFAULT_GENERIC_POLYGON_OPTIONS, Ht as project, I as DEFAULT_DELAY_OPTIONS, It as pointOnMidpointPerpendicularAxis, J as DEFAULT_BREACH_OPTIONS, K as DEFAULT_CANALIZE_OPTIONS, Kt as roundToFixed, L as DEFAULT_COVER_OPTIONS, Lt as snapToMidpointPerpendicular, M as DEFAULT_FINAL_PROTECTIVE_FIRE_OPTIONS, Mt as blockDrawRule, N as DEFAULT_ENCIRCLEMENT_OPTIONS, Nt as computeDefaultMidpointPerpendicularPoint, O as DEFAULT_HANDOVER_LINE_OPTIONS, Ot as attackByFireDrawRule, P as DEFAULT_DISRUPT_OPTIONS, Pt as createMidpointPerpendicularDrawRule, Q as DEFAULT_LIGHT_LINE_OPTIONS, R as DEFAULT_TACTICAL_ARROW_OPTIONS, Rt as createBaselineFrame, S as DEFAULT_JOINT_TACTICAL_ACTION_AREA_OPTIONS, St as line26DrawRule, T as DEFAULT_FORTIFIED_AREA_OPTIONS, Tt as turnDrawRule, U as DEFAULT_GENERIC_LINE_OPTIONS, Ut as unproject, V as DEFAULT_GENERIC_RECTANGLE_OPTIONS, Vt as haversineDistance, W as DEFAULT_GENERIC_CIRCLE_OPTIONS, Wt as EPSILON, X as DEFAULT_GENERIC_C2_LINE_OPTIONS, Y as DEFAULT_BLOCK_MISSION_TASK_OPTIONS, Z as DEFAULT_ENGINEER_WORK_LINE_OPTIONS, _ as DEFAULT_OBSTACLE_BYPASS_IMPOSSIBLE_OPTIONS, _t as DEFAULT_AMBUSH_OPTIONS, a as DEFINITIONS, at as DEFAULT_ANTITANK_WALL_OPTIONS, b as DEFAULT_MAIN_ATTACK_OPTIONS, bt as axis1DrawRule, c as getDefaultOptions, ct as DEFAULT_AREA_OF_OPERATIONS_OPTIONS, d as DEFAULT_TURN_OPTIONS, dt as canonicalTextAmplifierKey, et as DEFAULT_BLOCK_ARROW_OPTIONS, f as DEFAULT_SUPPORTING_ATTACK_OPTIONS, ft as normalizeTextAmplifiers, g as DEFAULT_PICKUP_ZONE_OPTIONS, gt as DEFAULT_DIRECTION_OF_ATTACK_AVIATION_OPTIONS, h as DEFAULT_PRINCIPAL_DIRECTION_OF_FIRE_OPTIONS, ht as DEFAULT_DIRECTION_OF_MAIN_ATTACK_OPTIONS, i as CONTROL_MEASURE_METADATA, it as DEFAULT_ATTACK_BY_FIRE_OPTIONS, j as DEFAULT_FIX_OPTIONS, jt as disruptDrawRule, k as DEFAULT_PHASE_LINE_OPTIONS, kt as point12DrawRule, l as listControlMeasureMetadata, lt as DEFAULT_AREA_DEFENSE_OPTIONS, m as DEFAULT_STRONG_POINT_OPTIONS, mt as DEFAULT_DIRECTION_OF_SUPPORTING_ATTACK_OPTIONS, n as resolveStyleHints, nt as DEFAULT_BATTLE_POSITION_OPTIONS, o as getControlMeasureMetadata, ot as DEFAULT_ANTITANK_DITCH_OPTIONS, p as DEFAULT_SUPPORT_BY_FIRE_OPTIONS, pt as resolveAmplifierPlacement, q as DEFAULT_BYPASS_OPTIONS, r as CONTROL_MEASURE_IDS, rt as DEFAULT_ATTACK_HELICOPTER_OPTIONS, s as getControlMeasureMetadataByValue, st as DEFAULT_ASSEMBLY_AREA_OPTIONS, t as renderControlMeasure, tt as DEFAULT_BLOCK_OPTIONS, u as DEFAULT_TURNING_MOVEMENT_OPTIONS, ut as TEXT_AMPLIFIER_FIELDS, v as DEFAULT_OBSTACLE_BYPASS_EASY_OPTIONS, vt as DEFAULT_AIRBORNE_ATTACK_OPTIONS, w as DEFAULT_FRONTAL_ATTACK_OPTIONS, wt as line23DrawRule, x as DEFAULT_LANDING_ZONE_OPTIONS, xt as supportByFireDrawRule, y as DEFAULT_OBSTACLE_BYPASS_DIFFICULT_OPTIONS, yt as rectangleDrawRule, z as DEFAULT_CLEAR_OPTIONS, zt as calculateMetrics } from "./renderControlMeasure-BdbObD8B.mjs";
|
|
2
2
|
//#region src/freeze-orientation.ts
|
|
3
3
|
/**
|
|
4
4
|
* Dispatches to a measure kind's `freezeOrientation` hook (see
|
package/dist/preview/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ir as TextAmplifiers, W as ControlMeasureId, or as FillPattern } from "../index-
|
|
1
|
+
import { Ir as TextAmplifiers, W as ControlMeasureId, or as FillPattern } from "../index-J_5z80SV.mjs";
|
|
2
2
|
import { FeatureCollection, Geometry } from "geojson";
|
|
3
3
|
|
|
4
4
|
//#region src/preview/index.d.ts
|
|
@@ -16,8 +16,11 @@ declare const PREVIEW_PATTERN_TILE = 8;
|
|
|
16
16
|
*/
|
|
17
17
|
declare const PREVIEW_FILL_PATTERNS: readonly {
|
|
18
18
|
readonly id: PreviewFillPattern;
|
|
19
|
-
readonly paths: readonly string[];
|
|
19
|
+
readonly paths: readonly string[]; /** Filled dot centers (tile units); the `"dots"` family has no stroked path. */
|
|
20
|
+
readonly dots?: readonly (readonly [number, number])[];
|
|
20
21
|
}[];
|
|
22
|
+
/** Radius (SVG units) of a {@link PREVIEW_FILL_PATTERNS} dot. */
|
|
23
|
+
declare const PREVIEW_PATTERN_DOT_RADIUS = 1.25;
|
|
21
24
|
/**
|
|
22
25
|
* Renders a measure's representative preview to GeoJSON. Pure — no map engine,
|
|
23
26
|
* so it runs identically at build time (docs catalog, README generator) and in
|
|
@@ -146,4 +149,4 @@ declare function viewBoxString(shapes: PreviewShape[], dims: {
|
|
|
146
149
|
*/
|
|
147
150
|
declare function svgTextTransform(s: PreviewShape): string | undefined;
|
|
148
151
|
//#endregion
|
|
149
|
-
export { PREVIEW_FILL_PATTERNS, PREVIEW_PATTERN_TILE, PreviewFillPattern, PreviewShape, ProjectDimensions, previewLabelBackgroundWidth, projectRenderToShapes, renderRepresentative, svgTextTransform, viewBoxString, viewBoxWithLabels };
|
|
152
|
+
export { PREVIEW_FILL_PATTERNS, PREVIEW_PATTERN_DOT_RADIUS, PREVIEW_PATTERN_TILE, PreviewFillPattern, PreviewShape, ProjectDimensions, previewLabelBackgroundWidth, projectRenderToShapes, renderRepresentative, svgTextTransform, viewBoxString, viewBoxWithLabels };
|
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-BdbObD8B.mjs";
|
|
2
2
|
//#region src/preview/index.ts
|
|
3
3
|
/** Side length (SVG units) of the repeating hatch tile in {@link PREVIEW_FILL_PATTERNS}. */
|
|
4
4
|
const PREVIEW_PATTERN_TILE = 8;
|
|
@@ -11,18 +11,33 @@ const PREVIEW_PATTERN_TILE = 8;
|
|
|
11
11
|
*/
|
|
12
12
|
const PREVIEW_FILL_PATTERNS = [
|
|
13
13
|
{
|
|
14
|
-
id: "
|
|
14
|
+
id: "hatch",
|
|
15
15
|
paths: ["M-1,-1 L9,9 M7,-1 L9,1 M-1,7 L1,9"]
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
|
-
id: "
|
|
18
|
+
id: "reverse-hatch",
|
|
19
19
|
paths: ["M9,-1 L-1,9 M1,-1 L-1,1 M9,7 L7,9"]
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
|
-
id: "cross",
|
|
22
|
+
id: "cross-hatch",
|
|
23
23
|
paths: ["M-1,-1 L9,9 M7,-1 L9,1 M-1,7 L1,9", "M9,-1 L-1,9 M1,-1 L-1,1 M9,7 L7,9"]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "horizontal",
|
|
27
|
+
paths: ["M-1,4 L9,4"]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "vertical",
|
|
31
|
+
paths: ["M4,-1 L4,9"]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "dots",
|
|
35
|
+
paths: [],
|
|
36
|
+
dots: [[4, 4]]
|
|
24
37
|
}
|
|
25
38
|
];
|
|
39
|
+
/** Radius (SVG units) of a {@link PREVIEW_FILL_PATTERNS} dot. */
|
|
40
|
+
const PREVIEW_PATTERN_DOT_RADIUS = 1.25;
|
|
26
41
|
/**
|
|
27
42
|
* Unitless `previewSample` points (~`[-1, 1]`) are scaled by this degree offset
|
|
28
43
|
* before reaching a generator. Most generators operate in projected meters, so
|
|
@@ -407,4 +422,4 @@ function collectShapes(g, tx, out, bboxArea, textLimits, props) {
|
|
|
407
422
|
}
|
|
408
423
|
}
|
|
409
424
|
//#endregion
|
|
410
|
-
export { PREVIEW_FILL_PATTERNS, PREVIEW_PATTERN_TILE, previewLabelBackgroundWidth, projectRenderToShapes, renderRepresentative, svgTextTransform, viewBoxString, viewBoxWithLabels };
|
|
425
|
+
export { PREVIEW_FILL_PATTERNS, PREVIEW_PATTERN_DOT_RADIUS, PREVIEW_PATTERN_TILE, previewLabelBackgroundWidth, projectRenderToShapes, renderRepresentative, svgTextTransform, viewBoxString, viewBoxWithLabels };
|
|
@@ -3228,6 +3228,20 @@ const ROW_PITCH_RATIO = 1.2;
|
|
|
3228
3228
|
* ENY marker's own half-width/half-height when carving its boundary
|
|
3229
3229
|
* knockout gap (see {@link pushAreaLabels}). */
|
|
3230
3230
|
const ENY_CLEARANCE_RATIO = .3;
|
|
3231
|
+
/** Where within each vertex-latitude band {@link ringLabelAnchor} probes for
|
|
3232
|
+
* interior chords — offset samples flank the midpoint so a self-intersection
|
|
3233
|
+
* pinch sitting exactly mid-band doesn't hide the band's real width. */
|
|
3234
|
+
const BAND_SAMPLE_FRACTIONS = [
|
|
3235
|
+
.25,
|
|
3236
|
+
.5,
|
|
3237
|
+
.75
|
|
3238
|
+
];
|
|
3239
|
+
/** Cap on how many latitude bands {@link ringLabelAnchor} probes. Each probe
|
|
3240
|
+
* rescans the whole ring, so probing every band of a smoothed or imported
|
|
3241
|
+
* outline would make the fallback quadratic (~190ms at 2k vertices, on every
|
|
3242
|
+
* rerender); striding holds it to a fixed multiple of one ring scan. Rings
|
|
3243
|
+
* with no more bands than this are still probed exhaustively. */
|
|
3244
|
+
const MAX_SAMPLED_BANDS = 48;
|
|
3231
3245
|
const DEFAULT_SMOOTH_RESOLUTION$9 = 16;
|
|
3232
3246
|
/**
|
|
3233
3247
|
* Composes an area's main label text from a `prefix` (e.g. `"AO"`) and a
|
|
@@ -3262,11 +3276,10 @@ function composeAreaLabelTexts(textAmplifiers, prefix = "", separator = " ") {
|
|
|
3262
3276
|
* Polygon centroid (area-weighted, shoelace-derived) of a closed ring in
|
|
3263
3277
|
* projected meters, falling back to the plain vertex average when the ring's
|
|
3264
3278
|
* signed area is ~0 (collinear/degenerate — the area-weighted formula would
|
|
3265
|
-
* divide by zero). For a **concave** ring
|
|
3266
|
-
* the shape
|
|
3267
|
-
*
|
|
3268
|
-
*
|
|
3269
|
-
* center a non-label glyph on the area (e.g. Airfield Zone's runway glyph).
|
|
3279
|
+
* divide by zero). For a **concave** or self-intersecting ring this point can
|
|
3280
|
+
* fall outside the shape, so anything that must sit *within* the area should
|
|
3281
|
+
* anchor on {@link ringLabelAnchor} instead; this raw centroid remains exported
|
|
3282
|
+
* for callers that want the plain moment-derived center.
|
|
3270
3283
|
*/
|
|
3271
3284
|
function ringCentroid(verts) {
|
|
3272
3285
|
const n = verts.length - 1;
|
|
@@ -3327,6 +3340,125 @@ function ringCrossingsAtY(verts, target) {
|
|
|
3327
3340
|
totalArc: dist
|
|
3328
3341
|
};
|
|
3329
3342
|
}
|
|
3343
|
+
/**
|
|
3344
|
+
* Ascending x of the ring's crossings of the horizontal line `y = target`. The
|
|
3345
|
+
* ring's **interior spans** at that latitude are the consecutive pairs
|
|
3346
|
+
* `[xs[0], xs[1]]`, `[xs[2], xs[3]]`, … — the even-odd fill rule — so a
|
|
3347
|
+
* self-intersecting ring (a bowtie) yields one span per lobe. The half-open
|
|
3348
|
+
* per-edge test (`min(ay,by) <= target < max(ay,by)`, matching
|
|
3349
|
+
* {@link ringCrossingsAtY}) counts a vertex sitting exactly on `target` once, so
|
|
3350
|
+
* the count is even for any non-degenerate ring; an odd count leaves the last
|
|
3351
|
+
* crossing unpaired and callers ignore it.
|
|
3352
|
+
*
|
|
3353
|
+
* Deliberately does not go through {@link ringCrossingsAtY}: that builds a
|
|
3354
|
+
* `RingCrossing` per crossing with an arc length and a normalized tangent, and
|
|
3355
|
+
* {@link ringLabelAnchor} probes up to `3 * MAX_SAMPLED_BANDS` latitudes per
|
|
3356
|
+
* render and reads nothing but x. Scalar arithmetic here keeps those probes free
|
|
3357
|
+
* of per-edge vector allocation and square roots.
|
|
3358
|
+
*/
|
|
3359
|
+
function ringCrossingXsAtY(verts, target) {
|
|
3360
|
+
const xs = [];
|
|
3361
|
+
for (let i = 0; i < verts.length - 1; i++) {
|
|
3362
|
+
const a = verts[i];
|
|
3363
|
+
const b = verts[i + 1];
|
|
3364
|
+
const ay = a[1];
|
|
3365
|
+
const by = b[1];
|
|
3366
|
+
if (Math.min(ay, by) <= target && target < Math.max(ay, by)) xs.push(a[0] + (target - ay) / (by - ay) * (b[0] - a[0]));
|
|
3367
|
+
}
|
|
3368
|
+
return xs.sort((left, right) => left - right);
|
|
3369
|
+
}
|
|
3370
|
+
/** Whether `x` falls within one of the interior spans paired off `xs` (see
|
|
3371
|
+
* {@link ringCrossingXsAtY}), ignoring spans pinched to no width. */
|
|
3372
|
+
function spansContainX(xs, x) {
|
|
3373
|
+
for (let i = 0; i + 1 < xs.length; i += 2) {
|
|
3374
|
+
const x0 = xs[i];
|
|
3375
|
+
const x1 = xs[i + 1];
|
|
3376
|
+
if (x1 - x0 > 1e-6 && x0 <= x && x <= x1) return true;
|
|
3377
|
+
}
|
|
3378
|
+
return false;
|
|
3379
|
+
}
|
|
3380
|
+
/** The latitudes {@link ringLabelAnchor} probes for interior chords: each
|
|
3381
|
+
* {@link BAND_SAMPLE_FRACTIONS} offset into every band between consecutive
|
|
3382
|
+
* distinct vertex latitudes, strided to at most {@link MAX_SAMPLED_BANDS}
|
|
3383
|
+
* bands. */
|
|
3384
|
+
function chordSampleLatitudes(verts) {
|
|
3385
|
+
const bandEdges = [...new Set(verts.map((vert) => vert[1]))].sort((a, b) => a - b);
|
|
3386
|
+
const bandCount = bandEdges.length - 1;
|
|
3387
|
+
const stride = Math.max(1, Math.ceil(bandCount / MAX_SAMPLED_BANDS));
|
|
3388
|
+
const latitudes = [];
|
|
3389
|
+
for (let i = 0; i < bandCount; i += stride) {
|
|
3390
|
+
const low = bandEdges[i];
|
|
3391
|
+
const height = bandEdges[i + 1] - low;
|
|
3392
|
+
for (const fraction of BAND_SAMPLE_FRACTIONS) latitudes.push(low + fraction * height);
|
|
3393
|
+
}
|
|
3394
|
+
return latitudes;
|
|
3395
|
+
}
|
|
3396
|
+
/**
|
|
3397
|
+
* Default anchor for an area's centered text — the ring's {@link ringCentroid}
|
|
3398
|
+
* whenever that point actually lies inside the ring, so every convex area (and
|
|
3399
|
+
* every concave one whose centroid still falls within it) keeps the doctrinal
|
|
3400
|
+
* template's plain "center of the area" placement unchanged.
|
|
3401
|
+
*
|
|
3402
|
+
* A strongly concave or self-intersecting ring can put the area-weighted
|
|
3403
|
+
* centroid outside the shape — in a bowtie the two lobes wind oppositely, so
|
|
3404
|
+
* their shoelace contributions partly cancel and the centroid drifts far off,
|
|
3405
|
+
* stranding the label on open ground. For those rings the anchor falls back to
|
|
3406
|
+
* the midpoint of the ring's **widest interior chord**: the interior spans
|
|
3407
|
+
* ({@link ringCrossingXsAtY}) at each probed latitude
|
|
3408
|
+
* ({@link chordSampleLatitudes}) are compared, and the widest wins. Horizontal
|
|
3409
|
+
* chord width is the right thing to maximize because area rows are horizontal
|
|
3410
|
+
* text
|
|
3411
|
+
* ({@link HORIZONTAL_LABEL_ROTATION}), and the result is inside the ring by
|
|
3412
|
+
* construction, which also guarantees {@link pushAreaLabels}'s Field N "ENY"
|
|
3413
|
+
* markers find their two edge crossings.
|
|
3414
|
+
*
|
|
3415
|
+
* Three samples per band rather than one: a band's crossing *count* is constant
|
|
3416
|
+
* within the band, but a self-intersection inside it (which is not a vertex, so
|
|
3417
|
+
* it opens no band of its own) pinches a span to zero width at one latitude —
|
|
3418
|
+
* exactly the midpoint on a symmetric bowtie. Offset samples step around that.
|
|
3419
|
+
* The same zero-width degeneracy is why the centroid must land in a span of
|
|
3420
|
+
* real width to be kept: a bowtie's centroid can sit precisely on the pinch,
|
|
3421
|
+
* which is technically "inside" but the worst spot on the shape. Equal-width
|
|
3422
|
+
* candidates resolve to the one nearest the centroid's latitude, then to the
|
|
3423
|
+
* southernmost, keeping the anchor deterministic.
|
|
3424
|
+
*
|
|
3425
|
+
* Each probe rescans the ring, so bands are strided to at most
|
|
3426
|
+
* {@link MAX_SAMPLED_BANDS} of them, keeping the fallback linear in vertex count
|
|
3427
|
+
* rather than quadratic — a smoothed or imported outline can carry thousands of
|
|
3428
|
+
* vertices and this runs on every rerender. Rings within the cap are probed
|
|
3429
|
+
* exhaustively, so ordinary drawn areas are unaffected. Striding samples bands
|
|
3430
|
+
* rather than uniform latitudes because a probe must land *within* a band to
|
|
3431
|
+
* dodge the pinch above.
|
|
3432
|
+
*
|
|
3433
|
+
* This is not a pole of inaccessibility: the chord midpoint is interior but not
|
|
3434
|
+
* maximally distant from the boundary, so a row wider than the chord can still
|
|
3435
|
+
* overhang. Nor is the chord guaranteed widest once striding kicks in — a wide
|
|
3436
|
+
* chord confined to a single skipped band is missed — but the result is still
|
|
3437
|
+
* interior, which is what correctness rests on. Rings with no interior span of
|
|
3438
|
+
* real width (fully degenerate) return the centroid.
|
|
3439
|
+
*/
|
|
3440
|
+
function ringLabelAnchor(verts) {
|
|
3441
|
+
const centroid = ringCentroid(verts);
|
|
3442
|
+
if (spansContainX(ringCrossingXsAtY(verts, centroid[1]), centroid[0])) return centroid;
|
|
3443
|
+
let anchor;
|
|
3444
|
+
let widestSeen = 0;
|
|
3445
|
+
let anchorOffset = Infinity;
|
|
3446
|
+
for (const y of chordSampleLatitudes(verts)) {
|
|
3447
|
+
const offset = Math.abs(y - centroid[1]);
|
|
3448
|
+
const xs = ringCrossingXsAtY(verts, y);
|
|
3449
|
+
for (let i = 0; i + 1 < xs.length; i += 2) {
|
|
3450
|
+
const x0 = xs[i];
|
|
3451
|
+
const x1 = xs[i + 1];
|
|
3452
|
+
const width = x1 - x0;
|
|
3453
|
+
if (width < 1e-6) continue;
|
|
3454
|
+
if (width > widestSeen + 1e-6) widestSeen = width;
|
|
3455
|
+
else if (width < widestSeen - 1e-6 || offset >= anchorOffset) continue;
|
|
3456
|
+
anchorOffset = offset;
|
|
3457
|
+
anchor = [.5 * (x0 + x1), y];
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
return anchor ?? centroid;
|
|
3461
|
+
}
|
|
3330
3462
|
/** Nearest contact point on a closed ring for a dragged marker anchor. */
|
|
3331
3463
|
function nearestPointOnRing(verts, target) {
|
|
3332
3464
|
let nearest;
|
|
@@ -3366,7 +3498,7 @@ function labelHalfExtent(direction, textWidth, textHeight, rotation) {
|
|
|
3366
3498
|
* empty unless `texts.hostile` was placed.
|
|
3367
3499
|
*
|
|
3368
3500
|
* Rows are `[name (T), additionalInformation (H), dtgStart (W), dtgEnd (W1)]`,
|
|
3369
|
-
* filtered to non-empty text, stacked around the ring's {@link
|
|
3501
|
+
* filtered to non-empty text, stacked around the ring's {@link ringLabelAnchor}
|
|
3370
3502
|
* at `pitch = resolveLabelOffsetMeters(options, 1.2 + labelPadding)` (the 50m
|
|
3371
3503
|
* no-size fallback applies until a live label size is captured — see
|
|
3372
3504
|
* `capturesLabelSize` — so a transient row overlap is possible right after
|
|
@@ -3374,7 +3506,7 @@ function labelHalfExtent(direction, textWidth, textHeight, rotation) {
|
|
|
3374
3506
|
* row (the name) reads topmost.
|
|
3375
3507
|
*
|
|
3376
3508
|
* When `texts.hostile` is set, the ring's west/east crossings of the
|
|
3377
|
-
* horizontal line through the
|
|
3509
|
+
* horizontal line through the anchor are found ({@link ringCrossingsAtY});
|
|
3378
3510
|
* the minimum/maximum-x crossings are the default anchors for the two "ENY"
|
|
3379
3511
|
* labels. A persisted `label:N:west` / `label:N:east` placement replaces that
|
|
3380
3512
|
* anchor. Each marker carves a masking {@link ArcGap} at the nearest point on
|
|
@@ -3388,10 +3520,12 @@ function labelHalfExtent(direction, textWidth, textHeight, rotation) {
|
|
|
3388
3520
|
* `textHeight = resolveLabelOffsetMeters(options, 1)`,
|
|
3389
3521
|
* `textWidth = text.length * LABEL_CHAR_ASPECT_RATIO * textHeight`, and
|
|
3390
3522
|
* `clearance = (ENY_CLEARANCE_RATIO + labelPadding) * textHeight` — mirroring
|
|
3391
|
-
* {@link placeEchelon}'s glyph-width masking gap (ADR-0023).
|
|
3392
|
-
*
|
|
3393
|
-
*
|
|
3394
|
-
*
|
|
3523
|
+
* {@link placeEchelon}'s glyph-width masking gap (ADR-0023). Because the anchor
|
|
3524
|
+
* is interior by construction there are normally two crossings; fewer (only a
|
|
3525
|
+
* degenerate ring) skips the markers silently rather than guessing their default
|
|
3526
|
+
* placements. On a self-intersecting ring the extreme crossings can belong to
|
|
3527
|
+
* different lobes than the anchor's, which still reads as the area's west and
|
|
3528
|
+
* east edges.
|
|
3395
3529
|
*
|
|
3396
3530
|
* A ring with fewer than 2 vertices is degenerate — nothing is placed.
|
|
3397
3531
|
*
|
|
@@ -3405,7 +3539,7 @@ function pushAreaLabels(out, ringVerts, texts, options = {}, amplifierPlacements
|
|
|
3405
3539
|
const sizeProps = labelSizeProps(options);
|
|
3406
3540
|
const padding = Math.max(0, options.labelPadding ?? 0);
|
|
3407
3541
|
const maskRingVerts = maskBoundary?.ringVerts ?? ringVerts;
|
|
3408
|
-
const [cx, cy] =
|
|
3542
|
+
const [cx, cy] = ringLabelAnchor(ringVerts);
|
|
3409
3543
|
const rows = [
|
|
3410
3544
|
[texts.name, "T"],
|
|
3411
3545
|
[texts.additionalInformation, "H"],
|
|
@@ -3600,7 +3734,7 @@ const AREA = defineControlMeasure({
|
|
|
3600
3734
|
/**
|
|
3601
3735
|
* Per-feature style hint pinning a filled part to a solid interior. Attach to
|
|
3602
3736
|
* a generator's intrinsic doctrinal accents — arrowheads, teeth, echelon
|
|
3603
|
-
* glyphs, barbs, blades — so a `
|
|
3737
|
+
* glyphs, barbs, blades — so a patterned (`hatch`, `dots`, …) fill set at the graphicsStyle
|
|
3604
3738
|
* or measure layer never bleeds into a small silhouette and ruins its
|
|
3605
3739
|
* legibility. The renderer only reads style hints, so one shared instance is
|
|
3606
3740
|
* safe to reuse across every accent feature.
|
|
@@ -4073,7 +4207,7 @@ function createAirfieldZone(positions, options = {}, textAmplifiers = {}, contex
|
|
|
4073
4207
|
if (x > maxX) maxX = x;
|
|
4074
4208
|
}
|
|
4075
4209
|
const bboxWidth = maxX - minX;
|
|
4076
|
-
const [cx, cy] =
|
|
4210
|
+
const [cx, cy] = ringLabelAnchor(verts);
|
|
4077
4211
|
if (bboxWidth >= 1e-6) {
|
|
4078
4212
|
const longHalf = .5 * glyphSizeRatio * bboxWidth;
|
|
4079
4213
|
const shortHalf = longHalf * SHORT_LINE_RATIO;
|
|
@@ -7386,8 +7520,7 @@ const DEFAULT_GENERIC_TEXT_OPTIONS = {
|
|
|
7386
7520
|
textStyle: "regular",
|
|
7387
7521
|
rotation: 0,
|
|
7388
7522
|
sizePixels: 24,
|
|
7389
|
-
maxSizePixels: 200
|
|
7390
|
-
halo: false
|
|
7523
|
+
maxSizePixels: 200
|
|
7391
7524
|
};
|
|
7392
7525
|
const GENERIC_TEXT_METADATA = {
|
|
7393
7526
|
id: "text",
|
|
@@ -7460,12 +7593,6 @@ const GENERIC_TEXT_METADATA = {
|
|
|
7460
7593
|
}
|
|
7461
7594
|
]
|
|
7462
7595
|
},
|
|
7463
|
-
{
|
|
7464
|
-
key: "halo",
|
|
7465
|
-
label: "Halo",
|
|
7466
|
-
description: "Render a contrasting halo behind the text for legibility on busy backgrounds.",
|
|
7467
|
-
type: "boolean"
|
|
7468
|
-
},
|
|
7469
7596
|
{
|
|
7470
7597
|
key: "rotation",
|
|
7471
7598
|
label: "Rotation",
|
|
@@ -7526,7 +7653,6 @@ function createGenericText(coordinates, options = {}) {
|
|
|
7526
7653
|
...textAnchor !== void 0 ? { textAnchor } : {},
|
|
7527
7654
|
textStyle: merged.textStyle ?? "regular",
|
|
7528
7655
|
...merged.maxSizePixels !== void 0 ? { textMaxSizePixels: merged.maxSizePixels } : {},
|
|
7529
|
-
...merged.halo === true ? { textHalo: true } : {},
|
|
7530
7656
|
...sizeProps
|
|
7531
7657
|
},
|
|
7532
7658
|
geometry: {
|
|
@@ -10295,7 +10421,7 @@ const DEFAULT_LIMITED_ACCESS_AREA_OPTIONS = {
|
|
|
10295
10421
|
const LIMITED_ACCESS_AREA_METADATA = {
|
|
10296
10422
|
id: "limited-access-area",
|
|
10297
10423
|
name: "Limited Access Area",
|
|
10298
|
-
description: "An area with restricted access, identified by a back-
|
|
10424
|
+
description: "An area with restricted access, identified by a back-hatch fill.",
|
|
10299
10425
|
entity: "Maneuver Areas",
|
|
10300
10426
|
entityType: "Limited Access Area",
|
|
10301
10427
|
value: "151100",
|
|
@@ -10320,7 +10446,7 @@ const LIMITED_ACCESS_AREA_METADATA = {
|
|
|
10320
10446
|
function createLimitedAccessArea(positions, options = {}, textAmplifiers = {}, context = {}) {
|
|
10321
10447
|
const { smooth = false, smoothResolution = 16 } = options;
|
|
10322
10448
|
const verts = buildClosedRing(positions, smooth, smoothResolution, 16);
|
|
10323
|
-
const center =
|
|
10449
|
+
const center = ringLabelAnchor(verts);
|
|
10324
10450
|
const labelFeatures = [{
|
|
10325
10451
|
type: "Feature",
|
|
10326
10452
|
properties: {
|
|
@@ -10338,7 +10464,7 @@ function createLimitedAccessArea(positions, options = {}, textAmplifiers = {}, c
|
|
|
10338
10464
|
}];
|
|
10339
10465
|
return {
|
|
10340
10466
|
type: "FeatureCollection",
|
|
10341
|
-
features: [...patternedAreaFeatures(verts, pushAreaLabels(labelFeatures, verts, { hostile: textAmplifiers.N }, options, context.amplifierPlacements), "
|
|
10467
|
+
features: [...patternedAreaFeatures(verts, pushAreaLabels(labelFeatures, verts, { hostile: textAmplifiers.N }, options, context.amplifierPlacements), "reverse-hatch"), ...labelFeatures]
|
|
10342
10468
|
};
|
|
10343
10469
|
}
|
|
10344
10470
|
const LIMITED_ACCESS_AREA = defineControlMeasure({
|
|
@@ -10422,7 +10548,7 @@ function makeLabel(out, point, text, options, properties = {}) {
|
|
|
10422
10548
|
function createNoFireAreaIrregular(positions, options = {}, textAmplifiers = {}, context = {}) {
|
|
10423
10549
|
const { smooth = false, smoothResolution = 16 } = options;
|
|
10424
10550
|
const verts = buildClosedRing(positions, smooth, smoothResolution, 16);
|
|
10425
|
-
const [cx, cy] =
|
|
10551
|
+
const [cx, cy] = ringLabelAnchor(verts);
|
|
10426
10552
|
const hasWindow = Boolean(textAmplifiers.W || textAmplifiers.W1);
|
|
10427
10553
|
const rowCount = 1 + (textAmplifiers.T ? 1 : 0) + (hasWindow ? 1 : 0);
|
|
10428
10554
|
const pitch = resolveLabelOffsetMeters(options, 1.2 + Math.max(0, options.labelPadding ?? .7));
|
|
@@ -10449,7 +10575,7 @@ function createNoFireAreaIrregular(positions, options = {}, textAmplifiers = {},
|
|
|
10449
10575
|
}
|
|
10450
10576
|
return {
|
|
10451
10577
|
type: "FeatureCollection",
|
|
10452
|
-
features: [...patternedAreaFeatures(verts, pushAreaLabels(labels, verts, { hostile: textAmplifiers.N }, options, context.amplifierPlacements), "
|
|
10578
|
+
features: [...patternedAreaFeatures(verts, pushAreaLabels(labels, verts, { hostile: textAmplifiers.N }, options, context.amplifierPlacements), "reverse-hatch"), ...labels]
|
|
10453
10579
|
};
|
|
10454
10580
|
}
|
|
10455
10581
|
const NO_FIRE_AREA_IRREGULAR = defineControlMeasure({
|
|
@@ -12182,6 +12308,7 @@ function resolveStyleHints(graphicsStyle, measureStyle, generatorStyle) {
|
|
|
12182
12308
|
*/
|
|
12183
12309
|
function resolveSymbolColor(merged, filled) {
|
|
12184
12310
|
const { color, strokeColor, fillColor, fillPattern, opacity, ...rest } = merged ?? {};
|
|
12311
|
+
delete rest.textHalo;
|
|
12185
12312
|
const rawStroke = firstColor(strokeColor, color);
|
|
12186
12313
|
const resolvedStroke = foldGraphicOpacity(rawStroke, opacity);
|
|
12187
12314
|
const resolved = {
|
|
@@ -12294,14 +12421,16 @@ function normalizeFeature(id, feature, index, generatedLabelOrdinal, measureStyl
|
|
|
12294
12421
|
const part = typeof sourceProps.part === "string" && sourceProps.part.length > 0 ? sourceProps.part : "main";
|
|
12295
12422
|
const generatorStyle = typeof sourceProps.style === "object" && sourceProps.style !== null ? sourceProps.style : void 0;
|
|
12296
12423
|
const filled = sourceProps.fill === true;
|
|
12297
|
-
const
|
|
12424
|
+
const merged = resolveStyleHints(graphicsStyle, measureStyle, generatorStyle);
|
|
12425
|
+
const style = resolveSymbolColor(merged, filled);
|
|
12298
12426
|
const text = typeof sourceProps.text === "string" ? sourceProps.text : void 0;
|
|
12299
12427
|
const rotation = typeof sourceProps.rotation === "number" ? sourceProps.rotation : void 0;
|
|
12300
12428
|
const textSizePixels = typeof sourceProps.textSizePixels === "number" ? sourceProps.textSizePixels : void 0;
|
|
12301
12429
|
const textSizeMeters = typeof sourceProps.textSizeMeters === "number" ? sourceProps.textSizeMeters : void 0;
|
|
12302
12430
|
const textAnchor = sourceProps.textAnchor === "start" || sourceProps.textAnchor === "end" ? sourceProps.textAnchor : void 0;
|
|
12303
12431
|
const textBackground = sourceProps.textBackground === true ? true : void 0;
|
|
12304
|
-
const
|
|
12432
|
+
const requestedHalo = typeof sourceProps.textHalo === "boolean" ? sourceProps.textHalo : merged?.textHalo === true;
|
|
12433
|
+
const textHalo = text !== void 0 && requestedHalo ? true : void 0;
|
|
12305
12434
|
const textHaloColor = textHalo ? contrastingHaloColor(style.strokeColor ?? "#000") : void 0;
|
|
12306
12435
|
const textJustify = sourceProps.textJustify === "left" || sourceProps.textJustify === "center" || sourceProps.textJustify === "right" ? sourceProps.textJustify : void 0;
|
|
12307
12436
|
const textStyle = sourceProps.textStyle === "regular" || sourceProps.textStyle === "italic" || sourceProps.textStyle === "light" || sourceProps.textStyle === "caps" ? sourceProps.textStyle : void 0;
|
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.29",
|
|
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",
|