@oicl/openbridge-webcomponents 2.0.0-next.111 → 2.0.0-next.113
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/bundle/openbridge-webcomponents.bundle.js +185 -29
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +180 -21
- package/dist/building-blocks/readout-block/readout-block.d.ts +29 -5
- package/dist/building-blocks/readout-block/readout-block.d.ts.map +1 -1
- package/dist/building-blocks/readout-block/readout-block.js +21 -7
- package/dist/building-blocks/readout-block/readout-block.js.map +1 -1
- package/dist/navigation-instruments/compass/compass.d.ts +1 -1
- package/dist/navigation-instruments/compass/compass.d.ts.map +1 -1
- package/dist/navigation-instruments/compass/compass.js +1 -1
- package/dist/navigation-instruments/compass/compass.js.map +1 -1
- package/dist/navigation-instruments/current/current.d.ts +25 -0
- package/dist/navigation-instruments/current/current.d.ts.map +1 -1
- package/dist/navigation-instruments/current/current.js +25 -4
- package/dist/navigation-instruments/current/current.js.map +1 -1
- package/dist/navigation-instruments/heading/heading.js +1 -1
- package/dist/navigation-instruments/heading/heading.js.map +1 -1
- package/dist/navigation-instruments/pitch-roll-heave/pitch-roll-heave.d.ts +6 -0
- package/dist/navigation-instruments/pitch-roll-heave/pitch-roll-heave.d.ts.map +1 -1
- package/dist/navigation-instruments/pitch-roll-heave/pitch-roll-heave.js +8 -1
- package/dist/navigation-instruments/pitch-roll-heave/pitch-roll-heave.js.map +1 -1
- package/dist/navigation-instruments/rate-of-turn/rate-of-turn.d.ts +5 -2
- package/dist/navigation-instruments/rate-of-turn/rate-of-turn.d.ts.map +1 -1
- package/dist/navigation-instruments/rate-of-turn/rate-of-turn.js.map +1 -1
- package/dist/navigation-instruments/readout/readout-formatters.d.ts +31 -0
- package/dist/navigation-instruments/readout/readout-formatters.d.ts.map +1 -1
- package/dist/navigation-instruments/readout/readout-formatters.js +55 -1
- package/dist/navigation-instruments/readout/readout-formatters.js.map +1 -1
- package/dist/navigation-instruments/readout/readout.d.ts +26 -1
- package/dist/navigation-instruments/readout/readout.d.ts.map +1 -1
- package/dist/navigation-instruments/readout/readout.js +16 -3
- package/dist/navigation-instruments/readout/readout.js.map +1 -1
- package/dist/navigation-instruments/readout-list/readout-list.d.ts +6 -1
- package/dist/navigation-instruments/readout-list/readout-list.d.ts.map +1 -1
- package/dist/navigation-instruments/readout-list/readout-list.js +21 -4
- package/dist/navigation-instruments/readout-list/readout-list.js.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts +26 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.js +15 -3
- package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
- package/dist/navigation-instruments/top-view-propulsion/top-view-propulsion.d.ts +6 -0
- package/dist/navigation-instruments/top-view-propulsion/top-view-propulsion.d.ts.map +1 -1
- package/dist/navigation-instruments/top-view-propulsion/top-view-propulsion.js +19 -4
- package/dist/navigation-instruments/top-view-propulsion/top-view-propulsion.js.map +1 -1
- package/dist/navigation-instruments/watch/environment.d.ts +8 -0
- package/dist/navigation-instruments/watch/environment.d.ts.map +1 -1
- package/dist/navigation-instruments/watch/environment.js +3 -0
- package/dist/navigation-instruments/watch/environment.js.map +1 -1
- package/dist/navigation-instruments/watch/force-pattern.d.ts +20 -2
- package/dist/navigation-instruments/watch/force-pattern.d.ts.map +1 -1
- package/dist/navigation-instruments/watch/force-pattern.js +12 -5
- package/dist/navigation-instruments/watch/force-pattern.js.map +1 -1
- package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
- package/dist/navigation-instruments/watch/watch.js +6 -4
- package/dist/navigation-instruments/watch/watch.js.map +1 -1
- package/dist/navigation-instruments/wind/wind.d.ts +5 -2
- package/dist/navigation-instruments/wind/wind.d.ts.map +1 -1
- package/dist/navigation-instruments/wind/wind.js +5 -1
- package/dist/navigation-instruments/wind/wind.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.js","sources":["../../../src/navigation-instruments/watch/watch.ts"],"sourcesContent":["import {\n LitElement,\n PropertyValues,\n SVGTemplateResult,\n html,\n nothing,\n svg,\n unsafeCSS,\n} from 'lit';\nimport {property, state} from 'lit/decorators.js';\nimport {circle} from '../../svghelpers/index.js';\nimport {roundedArch} from '../../svghelpers/roundedArch.js';\nimport {\n cssSafeAngle,\n deriveRadialSetpointConfig,\n drawSetpointMarker,\n getSetpointAnimationDurationMs,\n getSetpointOutwardOffset,\n RADIAL_SETPOINT_RADIUS,\n SetpointVisualState,\n SETPOINT_ANIMATION_CSS_VAR,\n SETPOINT_ANIMATION_DURATION_DEFAULT,\n} from '../../svghelpers/setpoint.js';\nimport {InstrumentState, Priority} from '../types.js';\nimport compentStyle from './watch.css?inline';\nimport {ResizeController} from '@lit-labs/observers/resize-controller.js';\nimport {adviceMask, AngleAdviceRaw, renderAdvice} from './advice.js';\nimport {Tickmark, TickmarkStyle, tickmark} from './tickmark.js';\nexport {TickmarkStyle};\nimport {\n RotType,\n RotPosition,\n renderRotDots,\n renderRotBarStatic,\n renderRotBarDots,\n shortestAngularDeltaDeg,\n renderRotZeroPill,\n rotBarThresholdAngle,\n ROT_ZERO_DEADBAND_DEG,\n} from '../rate-of-turn/rot-renderer.js';\nexport {RotType, RotPosition};\nimport {\n RateOfTurnController,\n disposeRotController,\n} from '../rate-of-turn/rate-of-turn.controller.js';\nimport {\n renderLabels,\n renderNorthArrow,\n renderNorthMarker,\n getLabelPositions,\n LabelPosition,\n} from './label.js';\nimport {VesselImage, VesselImageSize, vesselImages} from './vessel.js';\nimport {\n renderCurrent,\n renderCurrentCentered,\n renderWind,\n} from './environment.js';\nimport {customElement} from '../../decorator.js';\nimport {type ZoomToFitArcFrame} from '../../svghelpers/arc-frame.js';\nimport {\n applyPinnedHostSize,\n computeRadialFrame,\n estimateLabelWidthPx,\n measureContainerPx,\n NSWE_LABEL_WIDTH_PX,\n observeInnerBox,\n type RadialFrame,\n} from '../../svghelpers/radial-frame.js';\nexport {VesselImage, VesselImageSize, vesselImages};\n\nexport enum WatchCircleType {\n single = 'single',\n double = 'double',\n doubleThin = 'doubleThin',\n triple = 'triple',\n}\n\nexport interface WatchArea {\n startAngle: number;\n endAngle: number;\n roundOutsideCut: boolean;\n roundInsideCut: boolean;\n}\n\nexport interface WatchBarArea {\n startAngle: number;\n endAngle: number;\n fillColor: string;\n /** Outer band radius override; defaults to the second ring (160). */\n outerRadius?: number;\n /** Inner band radius override; defaults to the third ring (112). */\n innerRadius?: number;\n}\n\nexport interface WatchNeedle {\n angle: number;\n fillColor: string;\n strokeColor: string;\n /** Radial length of the needle pill; defaults to 48 (the full band). */\n length?: number;\n}\n\nexport interface WatchVessel {\n size: VesselImageSize;\n transform: string;\n vesselImage: VesselImage;\n}\n\nexport const OUTER_RING_RADIUS = 368 / 2;\nconst RING2_RADIUS = 320 / 2;\nconst RING3_RADIUS = 224 / 2;\nconst RING3B_RADIUS = 272 / 2;\nconst RING4_RADIUS = 176 / 2;\n\nexport function innerRingRadiusFor(type: WatchCircleType): number {\n switch (type) {\n case WatchCircleType.single:\n return RING2_RADIUS;\n case WatchCircleType.double:\n return RING3_RADIUS;\n case WatchCircleType.doubleThin:\n return RING3B_RADIUS;\n case WatchCircleType.triple:\n return RING4_RADIUS;\n default:\n throw new Error(`Unknown WatchCircleType: ${type as string}`);\n }\n}\n\nconst RADIAL_SETPOINT_INWARD_ADJUST = 4;\n\n/**\n * `<obc-watch>` - Core SVG renderer for circular/radial watch-based instruments.\n *\n * This component renders all circular instrument elements including rings, tickmarks,\n * bar areas, needles, advices, setpoints, vessel images, and environmental indicators\n * (wind/current). It serves as the foundation for compass, heading, rudder, speed-gauge,\n * and other radial navigation instruments.\n *\n * ## Setpoint Behavior\n *\n * The setpoint marker visual state is derived from the combination of `atAngleSetpoint`,\n * `angleSetpoint`, and `angleSetpointAtZeroDeadband` properties:\n *\n * - **notEqual**: Value differs from setpoint (triangular marker, offset outward)\n * - **equal**: Value matches setpoint (line marker, sits on ring)\n * - **equalZero**: Value matches setpoint at zero angle (double-line marker, offset outward)\n * - **focus**: User is actively adjusting via `newAngleSetpoint` - shows focus visual state\n *\n * ## newAngleSetpoint Pattern\n *\n * When `newAngleSetpoint` is defined, TWO setpoint markers are rendered:\n * 1. Original marker at `angleSetpoint` - dimmed (0.75 opacity)\n * 2. New marker at `newAngleSetpoint` - focus visual state, full opacity\n *\n * This enables the \"adjustment preview\" UX where users can see both the current\n * and proposed setpoint positions simultaneously.\n *\n * The `RADIAL_SETPOINT_INWARD_ADJUST` constant (4px) fine-tunes radial setpoint positioning\n * to match Figma designs, applied on top of visual state offsets from setpoint.ts.\n *\n * The `colorMode` property allows overriding the derived color mode (enhanced for enhanced priority,\n * regular for other states).\n *\n * ## Setpoint Animation (`animateSetpoint`)\n *\n * When `animateSetpoint` is true and a confirm occurs (`newAngleSetpoint` → `undefined`):\n * - The original setpoint slides to the new position via CSS transition\n * - The departing new-setpoint marker fades out\n * - Angular transitions always take the shortest path via accumulated\n * CSS-safe angles (`cssSafeAngle()`), so even 350° → 10° animates +20°\n *\n * Duration: `var(--setpoint-animation-duration, 300ms)`\n *\n * Internally, `_departingNewAngleSetpoint` captures the departing angle during confirm\n * fade-out and `_animationTimer` auto-clears it after the animation duration.\n * `_setpointCssAngle` tracks the accumulated CSS angle to avoid long-way-around\n * transitions across the 0°/360° boundary.\n *\n * @experimental\n */\n@customElement('obc-watch')\nexport class ObcWatch extends LitElement {\n private _setpointId = `watch-setpoint-${Math.random().toString(36).slice(2, 9)}`;\n private _newSetpointId = `watch-new-setpoint-${Math.random().toString(36).slice(2, 9)}`;\n\n /** Instrument state (active, loading, off) */\n @property({type: String}) state: InstrumentState = InstrumentState.active;\n /** Color priority (enhanced = blue palette, regular = gray palette) */\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: String}) watchCircleType: WatchCircleType =\n WatchCircleType.single;\n /**\n * When `true`, the full watch-face circle is drawn behind the dial —\n * a frame-primary fill with a tertiary outline — so partial-sector\n * instruments still read as a complete circle. In the `off` state the fill\n * is omitted (only the outline remains), keeping the off skeleton hollow.\n * With sector `areas`, the per-area arch outline is dropped; the face\n * outline takes its place.\n */\n @property({type: Boolean}) hasBackgroundCircle: boolean = false;\n @property({type: Boolean}) northArrow: boolean = false;\n @property({type: Boolean}) northArrowInside: boolean | undefined;\n /**\n * Replace the north arrow with the compact heading-up north marker: a small\n * triangle on the outer ring with an upright \"N\" at the outside label\n * radius. Rotates with the card via `rotation`.\n * @availableWhen northArrow==true\n */\n @property({type: Boolean}) northMarker: boolean = false;\n /** Setpoint angle in degrees (0° = 12 o'clock) */\n @property({type: Number}) angleSetpoint: number | undefined;\n /** New setpoint being adjusted (focus mode) */\n @property({type: Number}) newAngleSetpoint: number | undefined;\n /** Whether value matches setpoint (within deadband) */\n @property({type: Boolean}) atAngleSetpoint: boolean = false;\n /** Deadband for zero detection (default 0.5°) */\n @property({type: Number}) angleSetpointAtZeroDeadband: number = 0.5;\n /** Override to derive setpoint color from priority regardless of state */\n @property({type: Boolean}) setpointOverride: boolean = false;\n @property({type: Boolean}) touching: boolean = false;\n\n @property({type: Boolean}) animateSetpoint: boolean = false;\n\n @state() private _departingNewAngleSetpoint: number | undefined;\n private _animationTimer?: ReturnType<typeof setTimeout>;\n\n /**\n * Accumulated CSS-safe angle for the original setpoint marker.\n * Ensures CSS rotate() transitions always take the shortest path,\n * even across the 0°/360° boundary.\n */\n private _setpointCssAngle: number = 0;\n\n /** Whether the setpoint CSS angle has been initialised (to skip transition on first render). */\n private _setpointCssAngleInit = false;\n /**\n * Explicit padding override in SVG units: the un-zoomed viewBox becomes\n * exactly `(176 + padding) * 2`. Setting it disables the automatic\n * width-aware label reserve (issue #1021) — the caller owns label room.\n */\n @property({type: Number}) padding: number | undefined;\n /**\n * Outer-ring diameter in CSS pixels. When set, the instrument renders at a\n * fixed intrinsic size derived from the ring, arc shape and label reserve —\n * so instruments sharing the same value have identical ring circumference\n * regardless of label width or arc extent (like obc-donut-chart's\n * fixedHeight). When unset (default), the instrument fills its container.\n */\n @property({type: Number, attribute: 'face-diameter'})\n faceDiameter: number | undefined;\n @property({type: Array, attribute: false}) areas: WatchArea[] = [];\n @property({type: Array, attribute: false}) barAreas: WatchBarArea[] = [];\n /**\n * Rounds the band track's sector end cuts at the band's own radii and clips\n * bars to that rounded track shape (the design's track-mask model), instead\n * of the joint silhouette cut shared with the scale ring. No effect without\n * `areas`.\n */\n @property({type: Boolean}) roundBandCuts: boolean = false;\n @property({type: Array, attribute: false}) needles: WatchNeedle[] = [];\n @property({type: Array, attribute: false}) tickmarks: Tickmark[] = [];\n @property({type: Boolean}) tickmarksInside: boolean = false;\n @property({type: String}) tickmarkStyle: TickmarkStyle =\n TickmarkStyle.regular;\n @property({type: Array, attribute: false}) advices: AngleAdviceRaw[] = [];\n @property({type: Boolean}) crosshairEnabled: boolean = false;\n /**\n * Cuts the crosshair out inside the inner ring, so center content (e.g.\n * center readouts) sits on a clean face.\n * @availableWhen crosshairEnabled==true\n */\n @property({type: Boolean}) crosshairCenterCutout: boolean = false;\n @property({type: Boolean}) showLabels: boolean = false;\n /**\n * With `tickmarksInside`, anchor the NSEW label boxes flush against the\n * inner ring (px-fixed) instead of the legacy gap that lets them drift\n * toward the centre as the instrument shrinks.\n * @availableWhen tickmarksInside==true\n */\n @property({type: Boolean}) insideLabelsFlush: boolean = false;\n @property({type: Array, attribute: false}) vessels: WatchVessel[] = [];\n @property({type: Number}) windKnots: number | null = null;\n @property({type: Number}) windFromDirectionDeg: number | null = null;\n @property({type: Number}) windSymbolRadius: number | null = null;\n @property({type: String}) windColor: string | undefined;\n @property({type: Number}) current: number | null = null;\n @property({type: Number}) currentFromDirectionDeg: number | null = null;\n @property({type: Number}) currentSymbolRadius: number | null = null;\n @property({type: String}) currentColor: string | undefined;\n /**\n * Render the current icon centered on the face (direction-type layout)\n * instead of at `currentSymbolRadius` on the periphery.\n * @availableWhen current!=null && currentFromDirectionDeg!=null\n */\n @property({type: Boolean}) currentIconCentered: boolean = false;\n /**\n * Scale factor for the centered current icon.\n * @availableWhen currentIconCentered==true\n */\n @property({type: Number}) scaleCurrentIcon: number = 1;\n @property({type: Boolean}) starboardPortIndicator: boolean = false;\n /** Top clip, % of height. Ignored when `zoomToFitArc` is true. */\n @property({type: Number}) clipTop: number = 0;\n /** Bottom clip, % of height. Ignored when `zoomToFitArc` is true. */\n @property({type: Number}) clipBottom: number = 0;\n /** Left clip, % of width — horizontal counterpart of clipTop/bottom (90° sectors). Ignored when `zoomToFitArc`. */\n @property({type: Number}) clipLeft: number = 0;\n /** Right clip, % of width — horizontal counterpart of clipTop/bottom (90° sectors). Ignored when `zoomToFitArc`. */\n @property({type: Number}) clipRight: number = 0;\n /**\n * Place the horizontal end labels (±90°, e.g. min/max) below the tick instead\n * of beside it. This is the \"Max-min\" label placement from the radial label\n * model (External / Internal / Max-min) — see PR #903 / design discussion.\n * Currently only used by the 180° sector of `obc-gauge-radial`.\n */\n @property({type: Boolean}) endLabelsMaxMin: boolean = false;\n @property({type: Number}) scaleWindIcon: number = 1;\n @property({type: Number}) rotation: number | undefined;\n @property({type: Boolean}) zoomToFitArc: boolean = false;\n /** Pre-computed zoom-to-fit arc frame. When set, the watch skips its own `computeZoomToFitArcFrame()` call and uses these values directly. Consumer instruments (e.g. rudder, instrument-radial) should compute the frame once and pass it here to avoid redundant computation. If you pass `arcFrame`, you own keeping it in sync with `areas` / `watchCircleType` — obc-watch will NOT recompute it, so a stale frame renders stale geometry. */\n @property({attribute: false}) arcFrame: ZoomToFitArcFrame | undefined;\n @property({type: Number}) tickFadeAngle: number = 0;\n\n /** ROT visualization type: `'dots'` (spinning dots) or `'bar'` (arc bar with clipped dots). Undefined hides the ROT layer. */\n @property({type: String}) rotType: RotType | undefined;\n /** Track on which ROT elements are placed: `'scale'` (on the outer ring) or `'innerCircle'` (default, inside the inner ring) */\n @property({type: String}) rotPosition: RotPosition = RotPosition.innerCircle;\n /** Start angle of the ROT bar arc in degrees (0° = 12 o'clock, clockwise). Only used when `rotType` is `'bar'`. */\n @property({type: Number}) rotStartAngle: number = 0;\n /** End angle of the ROT bar arc in degrees. The bar is hidden when the difference from `rotStartAngle` is less than 0.1°. */\n @property({type: Number}) rotEndAngle: number = 0;\n /** Override priority for ROT color derivation. When set, ROT colors use this instead of the main `priority`. Useful when the ROT element has independent priority (e.g. compass per-element priority). */\n @property({type: String}) rotPriority: Priority | undefined;\n @property({type: Boolean}) rotPortStarboard: boolean = false;\n @property({type: Number}) rotAtZeroDeadband: number = ROT_ZERO_DEADBAND_DEG;\n /** Measured rate of turn in degrees per minute (the maritime/AIS convention, see ES-TRIN 2025/1 Art. 3.02 and ITU-R M.1371). Sign controls direction (positive = starboard/clockwise). When defined, this drives both the dot animation (multiplied by `rotDotAnimationFactor`) and the port/starboard direction sign. */\n @property({type: Number}) rateOfTurnDegreesPerMinute: number | undefined;\n /** Visual amplification factor applied only to the spinning-dot animation (not to bar extent). Default `18` keeps the legacy visual feel (≈1 rpm at 20°/min). */\n @property({type: Number}) rotDotAnimationFactor: number = 18;\n /**\n * @deprecated Use `rateOfTurnDegreesPerMinute` (and optionally `rotDotAnimationFactor`) instead.\n * Kept as a backward-compatible alias; takes effect only when\n * `rateOfTurnDegreesPerMinute` is `undefined`.\n */\n @property({type: Number})\n set rotationsPerMinute(value: number) {\n this._legacyRotationsPerMinute = value;\n }\n /** Legacy spin speed of the ROT dot ring, in rotations per minute (sign controls direction; positive = clockwise). */\n get rotationsPerMinute() {\n return this._legacyRotationsPerMinute;\n }\n private _legacyRotationsPerMinute = 0;\n private _rotController?: RateOfTurnController;\n\n /**\n * Effective rotations-per-minute for the spinning dot animation and\n * port/starboard direction sign. Resolves to:\n * • `(rateOfTurnDegreesPerMinute / 360) * rotDotAnimationFactor` when the\n * new physical API is in use, OR\n * • the legacy `rotationsPerMinute` value otherwise.\n */\n private get _effectiveRpm(): number {\n if (this.rateOfTurnDegreesPerMinute != null) {\n return (\n (this.rateOfTurnDegreesPerMinute / 360) * this.rotDotAnimationFactor\n );\n }\n return this._legacyRotationsPerMinute;\n }\n\n private _resizeController = new ResizeController(this, {});\n\n override firstUpdated(changed: PropertyValues): void {\n super.firstUpdated(changed);\n observeInnerBox(this._resizeController, this.renderRoot);\n }\n\n override willUpdate(changed: PropertyValues): void {\n super.willUpdate(changed);\n\n // Push the resolved effective RPM into the live controller whenever any\n // of the inputs that compose it change.\n if (\n this._rotController &&\n (changed.has('rateOfTurnDegreesPerMinute') ||\n changed.has('rotDotAnimationFactor') ||\n changed.has('rotationsPerMinute'))\n ) {\n this._rotController.rotationsPerMinute = this._effectiveRpm;\n }\n\n // Detect confirm: newAngleSetpoint was defined, now undefined\n if (changed.has('newAngleSetpoint') && this.animateSetpoint) {\n const prev = changed.get('newAngleSetpoint') as number | undefined;\n if (prev !== undefined && this.newAngleSetpoint === undefined) {\n this._departingNewAngleSetpoint = prev;\n clearTimeout(this._animationTimer);\n const duration = getSetpointAnimationDurationMs(this);\n this._animationTimer = setTimeout(() => {\n this._departingNewAngleSetpoint = undefined;\n }, duration);\n }\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n clearTimeout(this._animationTimer);\n this._rotController = disposeRotController(this, this._rotController);\n }\n\n override updated(changed: PropertyValues): void {\n super.updated(changed);\n this._hostSizePinned = applyPinnedHostSize(\n this,\n this.arcFrame ? undefined : this._ownFrame,\n this._hostSizePinned\n );\n const el = this.rotType\n ? this.renderRoot.querySelector('#rot-spinner')\n : null;\n if (!el) {\n this._rotController = disposeRotController(this, this._rotController);\n return;\n }\n if (!this._rotController || this._rotController.el !== el) {\n this._rotController = disposeRotController(this, this._rotController);\n this._rotController = new RateOfTurnController(\n this,\n el,\n this._effectiveRpm\n );\n }\n }\n\n private get innerRingRadius(): number {\n return innerRingRadiusFor(this.watchCircleType);\n }\n\n private _rOff = 0;\n\n /**\n * Set by the frame when the label reserve exceeded its cap: tick label\n * texts are dropped instead of clipped (see radial-frame.ts).\n */\n private _labelsHidden = false;\n\n /** The frame of the last render, when computed internally (no arcFrame). */\n private _ownFrame: RadialFrame | undefined;\n\n /** Whether the host size styles were set by applyPinnedHostSize. */\n private _hostSizePinned = false;\n\n /**\n * Radius for a dial-band edge under zoom: additive (`base + _rOff`), keeping\n * band thickness constant in SVG units. INVARIANT: every band-edge radius\n * (rings, value arc, bars, needles, inside-label `textRadius`) must go through\n * this — mixing additive and multiplicative offsets misaligns ticks, labels,\n * advice masks and arcs. The proportional experiments were removed for this\n * reason (PR #903).\n */\n private _bandRadius(base: number): number {\n return base + this._rOff;\n }\n\n private watchCircle(): SVGTemplateResult | SVGTemplateResult[] {\n const rings = [];\n // Full-face circle behind everything, split in two: the fill goes under\n // the rings and the outline goes on top of them — the masked track band\n // reaches exactly the face radius, so an outline drawn underneath would\n // lose its inner half wherever the band overlaps it and look thinner\n // there than across the open sector gap. In the ring branch the existing\n // outer ring already outlines the face, so only the fill applies.\n const faceFill =\n this.hasBackgroundCircle && this.state !== InstrumentState.off\n ? svg`\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${this._bandRadius(OUTER_RING_RADIUS)}\"\n fill=\"var(--instrument-frame-primary-color)\"\n stroke=\"none\"\n />`\n : undefined;\n const faceOutline =\n this.hasBackgroundCircle && this.areas.length > 0\n ? svg`\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${this._bandRadius(OUTER_RING_RADIUS)}\"\n fill=\"none\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n />`\n : undefined;\n if (this.state !== InstrumentState.off) {\n rings.push(svg`\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${this._bandRadius(172)}\"\n stroke=\"var(--instrument-frame-primary-color)\"\n fill=\"none\"\n stroke-width=\"24\"\n />`);\n\n if (this.watchCircleType !== WatchCircleType.single) {\n const r1 = this._bandRadius(RING2_RADIUS);\n const r2 = this._bandRadius(\n this.watchCircleType === WatchCircleType.doubleThin\n ? RING3B_RADIUS\n : RING3_RADIUS\n );\n const r = (r1 + r2) / 2;\n const strokeWidth = r1 - r2;\n if (this.roundBandCuts && this.areas.length > 0) {\n rings.push(\n ...this.areas.map(\n (area) =>\n svg`<path d=${roundedArch({\n startAngle: area.startAngle,\n endAngle: area.endAngle,\n R: r1,\n r: r2,\n roundOutsideCut: area.roundOutsideCut,\n roundInsideCut: area.roundInsideCut,\n })} fill=\"var(--instrument-frame-secondary-color)\" stroke=\"var(--instrument-frame-secondary-color)\" stroke-width=\"1\" vector-effect=\"non-scaling-stroke\" />`\n )\n );\n } else {\n rings.push(\n svg`\n <circle cx=\"0\" cy=\"0\" r=${r} stroke=\"var(--instrument-frame-secondary-color)\" stroke-width=${strokeWidth} fill=\"none\" />\n <circle cx=\"0\" cy=\"0\" r=${r1} stroke=\"var(--instrument-frame-secondary-color)\" stroke-width=\"1\" fill=\"none\" vector-effect=\"non-scaling-stroke\" />\n <circle cx=\"0\" cy=\"0\" r=${r2} stroke=\"var(--instrument-frame-secondary-color)\" stroke-width=\"1\" fill=\"none\" vector-effect=\"non-scaling-stroke\" />\n `\n );\n }\n }\n if (this.watchCircleType === WatchCircleType.triple) {\n const r1 = this._bandRadius(RING3_RADIUS);\n const r2 = this._bandRadius(RING4_RADIUS);\n const r = (r1 + r2) / 2;\n const strokeWidth = r1 - r2;\n rings.push(\n svg`<circle cx=\"0\" cy=\"0\" r=${r} stroke=\"var(--instrument-frame-primary-color)\" stroke-width=${strokeWidth} fill=\"none\" />`\n );\n }\n }\n\n const maskSize = Math.max(200, OUTER_RING_RADIUS + this._rOff + 50);\n let result = rings;\n if (this.areas.length > 0) {\n const areas = this.areas.map((area) => {\n const svgPath = roundedArch({\n startAngle: area.startAngle,\n endAngle: area.endAngle,\n R: this._bandRadius(OUTER_RING_RADIUS),\n r: this._bandRadius(this.innerRingRadius),\n roundOutsideCut: area.roundOutsideCut,\n roundInsideCut: area.roundInsideCut,\n });\n return svgPath;\n });\n const mask = svg`<mask id=\"cutMask\">\n <rect x=\"${-maskSize}\" y=\"${-maskSize}\" width=\"${maskSize * 2}\" height=\"${maskSize * 2}\" fill=\"black\" />\n ${areas.map((area) => svg`<path d=${area} fill=\"white\" vector-effect=\"non-scaling-stroke\" stroke=\"white\" stroke-width=\"1\"/>`)}\n </mask>`;\n // clipPath for ROT uses r=0 so dots at any track radius stay visible\n const rotClip = svg`<clipPath id=\"rot-arc-clip\">${this.areas.map(\n (area) =>\n svg`<path d=${roundedArch({\n startAngle: area.startAngle,\n endAngle: area.endAngle,\n R: OUTER_RING_RADIUS + this._rOff + 20,\n r: 0,\n roundOutsideCut: area.roundOutsideCut,\n roundInsideCut: area.roundInsideCut,\n })} />`\n )}</clipPath>`;\n result = [\n mask,\n rotClip,\n ...(faceFill ? [faceFill] : []),\n svg`<g mask=\"url(#cutMask)\">${rings}</g>`,\n ...(faceOutline ? [faceOutline] : []),\n ];\n if (!this.hasBackgroundCircle) {\n areas.forEach((area) => {\n result.push(\n svg`<path d=${area} fill=\"none\" stroke=\"var(--instrument-frame-tertiary-color)\" vector-effect=\"non-scaling-stroke\"/>`\n );\n });\n }\n } else {\n if (faceFill) {\n result = [faceFill, ...rings];\n }\n if (this.state !== InstrumentState.off) {\n result.push(\n circle('outerRing', {\n radius: this._bandRadius(OUTER_RING_RADIUS),\n strokeWidth: 1,\n strokeColor: 'var(--instrument-frame-tertiary-color)',\n strokePosition: 'center',\n fillColor: 'none',\n })\n );\n\n result.push(svg`\n ${circle('innerRing', {\n radius: this._bandRadius(this.innerRingRadius),\n strokeWidth: 1,\n strokeColor: 'var(--instrument-frame-tertiary-color)',\n strokePosition: 'center',\n fillColor: 'none',\n })}\n `);\n } else {\n result.push(svg`\n ${circle('innerRing', {\n radius: this._bandRadius(OUTER_RING_RADIUS),\n strokeWidth: 1,\n strokeColor: 'var(--instrument-frame-tertiary-color)',\n strokePosition: 'center',\n fillColor: 'none',\n })}\n `);\n }\n }\n return result;\n }\n\n private _renderTickFadeDefs(): SVGTemplateResult | typeof nothing {\n if (this.tickFadeAngle <= 0 || this.areas.length === 0) return nothing;\n const area = this.areas[0];\n const arcSpan = area.endAngle - area.startAngle;\n const fade = Math.min(this.tickFadeAngle, arcSpan / 4);\n if (fade < 0.5) return nothing;\n\n const {startAngle, endAngle} = area;\n const R = OUTER_RING_RADIUS + this._rOff + 200;\n const toRad = (deg: number) => (deg * Math.PI) / 180;\n const px = (deg: number) => R * Math.sin(toRad(deg));\n const py = (deg: number) => -R * Math.cos(toRad(deg));\n\n const pieSlice = (a: number, b: number): string => {\n const x1 = px(a),\n y1 = py(a);\n const x2 = px(b),\n y2 = py(b);\n const largeArc = b - a > 180 ? 1 : 0;\n return `M 0 0 L ${x1} ${y1} A ${R} ${R} 0 ${largeArc} 1 ${x2} ${y2} Z`;\n };\n\n const Rm =\n (this._bandRadius(OUTER_RING_RADIUS) +\n this._bandRadius(this.innerRingRadius)) /\n 2;\n const gx = (deg: number) => Rm * Math.sin(toRad(deg));\n const gy = (deg: number) => -Rm * Math.cos(toRad(deg));\n\n return svg`\n <defs>\n <linearGradient id=\"tickFadeL\" gradientUnits=\"userSpaceOnUse\"\n x1=\"${gx(startAngle)}\" y1=\"${gy(startAngle)}\"\n x2=\"${gx(startAngle + fade)}\" y2=\"${gy(startAngle + fade)}\">\n <stop offset=\"0\" stop-color=\"black\" />\n <stop offset=\"1\" stop-color=\"white\" />\n </linearGradient>\n <linearGradient id=\"tickFadeR\" gradientUnits=\"userSpaceOnUse\"\n x1=\"${gx(endAngle - fade)}\" y1=\"${gy(endAngle - fade)}\"\n x2=\"${gx(endAngle)}\" y2=\"${gy(endAngle)}\">\n <stop offset=\"0\" stop-color=\"white\" />\n <stop offset=\"1\" stop-color=\"black\" />\n </linearGradient>\n <mask id=\"tickFadeMask\" maskUnits=\"userSpaceOnUse\"\n x=\"${-R}\" y=\"${-R}\" width=\"${R * 2}\" height=\"${R * 2}\">\n <path d=\"${pieSlice(startAngle + fade, endAngle - fade)}\" fill=\"white\" />\n <path d=\"${pieSlice(startAngle, startAngle + fade)}\" fill=\"url(#tickFadeL)\" />\n <path d=\"${pieSlice(endAngle - fade, endAngle)}\" fill=\"url(#tickFadeR)\" />\n </mask>\n </defs>\n `;\n }\n\n private renderCrosshair(\n radius: number,\n labelKnockouts?: {\n positions: LabelPosition[];\n rotation: number | undefined;\n scale: number;\n /** Inner ring radius – crosshair is hidden between labelRadius and this value. */\n innerRingRadius: number;\n },\n centerCutoutRadius?: number\n ): SVGTemplateResult {\n const hasLabelKnockouts =\n !!labelKnockouts && labelKnockouts.positions.length > 0;\n const hasMask = hasLabelKnockouts || centerCutoutRadius !== undefined;\n\n // Radius at which labels sit (distance from centre).\n // Any position is equally valid — they're all at the same radial distance.\n const labelRadius = hasLabelKnockouts\n ? Math.max(\n ...labelKnockouts!.positions.map((l) =>\n Math.abs(l.x !== 0 ? l.x : l.y)\n )\n )\n : 0;\n // Small extra padding so the crosshair doesn't start/end right at the\n // label edge — use the same visual pad as the letter knockouts.\n const ringGapPad = hasLabelKnockouts ? 3 / labelKnockouts!.scale : 0;\n\n return svg`\n ${\n hasMask\n ? svg`\n <defs>\n <mask\n id=\"crosshair-label-mask\"\n maskUnits=\"userSpaceOnUse\"\n x=\"-${radius}\" y=\"-${radius}\"\n width=\"${radius * 2}\" height=\"${radius * 2}\"\n >\n <rect x=\"-${radius}\" y=\"-${radius}\" width=\"${radius * 2}\" height=\"${radius * 2}\" fill=\"white\"/>\n ${\n hasLabelKnockouts\n ? svg`\n <!-- Annular ring knockout: hide crosshair between labels and inner ring -->\n <circle cx=\"0\" cy=\"0\" r=\"${labelKnockouts!.innerRingRadius}\" fill=\"black\"/>\n <circle cx=\"0\" cy=\"0\" r=\"${labelRadius - ringGapPad}\" fill=\"white\"/>\n <!-- Per-label rectangular knockouts -->\n ${labelKnockouts!.positions.map((l) => {\n const fontSize = 12 / labelKnockouts!.scale;\n const pad = 3 / labelKnockouts!.scale;\n const size = fontSize + pad * 2;\n return svg`\n <rect\n x=\"${l.x - size / 2}\" y=\"${l.y - size / 2}\"\n width=\"${size}\" height=\"${size}\"\n fill=\"black\"\n transform=\"rotate(${-(labelKnockouts!.rotation ?? 0)})\"\n transform-origin=\"${l.x} ${l.y}\"\n />\n `;\n })}`\n : nothing\n }\n ${\n centerCutoutRadius !== undefined\n ? svg`<circle cx=\"0\" cy=\"0\" r=\"${centerCutoutRadius}\" fill=\"black\"/>`\n : nothing\n }\n </mask>\n </defs>`\n : nothing\n }\n <g mask=${hasMask ? 'url(#crosshair-label-mask)' : nothing}>\n <line\n x1=\"-${radius}\"\n y1=\"0\"\n x2=\"${radius}\"\n y2=\"0\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n />\n <line\n x1=\"0\"\n y1=\"-${radius}\"\n x2=\"0\"\n y2=\"${radius}\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n />\n </g>\n `;\n }\n\n private renderBars(): SVGTemplateResult[] | typeof nothing {\n if (this.barAreas.length === 0) {\n return nothing;\n }\n return this.barAreas.map((bar, index) => {\n const startAngle = Math.min(bar.startAngle, bar.endAngle);\n const endAngle = Math.max(bar.startAngle, bar.endAngle);\n const arc = roundedArch({\n r: this._bandRadius(bar.innerRadius ?? RING3_RADIUS),\n R: this._bandRadius(bar.outerRadius ?? RING2_RADIUS),\n startAngle: startAngle,\n endAngle: endAngle,\n roundInsideCut: false,\n roundOutsideCut: false,\n });\n const barMaskR = (bar.outerRadius ?? RING2_RADIUS) + this._rOff + 40;\n // The mask is a sector to cut out the stroke on the start and end of the bar\n const mask = svg`<mask id=\"barMask-${index}\">\n <rect x=\"${-barMaskR}\" y=\"${-barMaskR}\" width=\"${barMaskR * 2}\" height=\"${barMaskR * 2}\" fill=\"black\" />\n <path d=${roundedArch({\n r: 1,\n R: barMaskR,\n startAngle: startAngle,\n endAngle: endAngle,\n roundInsideCut: false,\n roundOutsideCut: false,\n })} fill=\"white\" />\n </mask>`;\n // With roundBandCuts the bar is additionally clipped to the rounded\n // track shape, so its ends round off where they reach the sector cuts\n // (the design's track-mask model) while mid-track ends stay square.\n const trackMask =\n this.roundBandCuts && this.areas.length > 0\n ? svg`<mask id=\"barTrackMask-${index}\">\n <rect x=\"${-barMaskR}\" y=\"${-barMaskR}\" width=\"${barMaskR * 2}\" height=\"${barMaskR * 2}\" fill=\"black\" />\n ${this.areas.map(\n (area) =>\n svg`<path d=${roundedArch({\n startAngle: area.startAngle,\n endAngle: area.endAngle,\n R: this._bandRadius(bar.outerRadius ?? RING2_RADIUS),\n r: this._bandRadius(bar.innerRadius ?? RING3_RADIUS),\n roundOutsideCut: area.roundOutsideCut,\n roundInsideCut: area.roundInsideCut,\n })} fill=\"white\" stroke=\"white\" stroke-width=\"1\" vector-effect=\"non-scaling-stroke\" />`\n )}\n </mask>`\n : nothing;\n return svg`\n ${mask}\n ${trackMask}\n <g mask=${this.areas.length > 0 ? 'url(#cutMask)' : nothing}>\n <g mask=${trackMask !== nothing ? `url(#barTrackMask-${index})` : nothing}>\n <path\n d=${arc}\n fill=${bar.fillColor}\n stroke=${bar.fillColor}\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n mask=\"url(#barMask-${index})\"\n />\n </g>\n </g>\n `;\n });\n }\n\n private renderNeedles(): SVGTemplateResult[] | typeof nothing {\n if (this.needles.length === 0) {\n return nothing;\n }\n return this.needles.map((needle) => {\n return svg`\n <rect\n transform=\"rotate(${needle.angle})\"\n x=\"-4\" y=\"${-this._bandRadius(RING2_RADIUS)}\" width=\"8\" height=\"${needle.length ?? 48}\" rx=\"4\"\n fill=${needle.fillColor}\n stroke=${needle.strokeColor}\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n paint-order=\"stroke fill\"\n />\n `;\n });\n }\n\n private getScale({width, height}: {width: number; height: number}): number {\n const container = measureContainerPx(this);\n const scale = Math.min(container.width / width, container.height / height);\n // On first paint the element and its parent can both still be zero-sized, so\n // the scale is 0 (or non-finite). That value flows into `px / scale` label\n // math and yields ±Infinity coordinates the browser rejects. Fall back to a\n // 1:1 scale until a real size is available; the ResizeController re-renders\n // with the true scale once the element is laid out (issue #1032).\n if (!Number.isFinite(scale) || scale <= 0) {\n return 1;\n }\n return scale;\n }\n\n /**\n * Pixel width of the widest outside label, feeding the frame's label\n * reserve (issue #1021). Explicit `padding` is a hard geometry override\n * and disables the reserve, preserving legacy consumer output.\n */\n private getLabelWidthPx(): number {\n if (this.padding !== undefined || this.tickmarksInside) {\n return 0;\n }\n if (this.showLabels) {\n return NSWE_LABEL_WIDTH_PX;\n }\n return estimateLabelWidthPx(this.tickmarks.map((t) => t.text));\n }\n\n override render() {\n let width: number;\n let height: number;\n let viewBox: string;\n\n if (this.arcFrame) {\n this._rOff = this.arcFrame.radiusOffset;\n this._labelsHidden = false;\n this._ownFrame = undefined;\n width = this.arcFrame.width;\n height = this.arcFrame.height;\n viewBox = this.arcFrame.viewBox;\n } else {\n const frame = computeRadialFrame({\n basePadding: this.padding ?? 24,\n labelWidthPx: this.getLabelWidthPx(),\n clips: {\n top: this.clipTop,\n bottom: this.clipBottom,\n left: this.clipLeft,\n right: this.clipRight,\n },\n containerPx: measureContainerPx(this),\n faceDiameter: this.faceDiameter,\n zoomToFitArc: this.zoomToFitArc,\n areas: this.areas,\n innerRadius: this.innerRingRadius,\n });\n this._rOff = frame.radiusOffset;\n this._labelsHidden = frame.labelsHidden;\n this._ownFrame = frame;\n width = frame.width;\n height = frame.height;\n viewBox = frame.viewBox;\n }\n\n const rOff = this._rOff;\n const scale = this.getScale({width, height});\n const angleSetpoint = this.renderSetpoint();\n // Route through _bandRadius so inside labels track the (zoom-shifted) inner\n // band edge; the coupling is intentional (see _bandRadius INVARIANT).\n const textRadius = this.tickmarksInside\n ? this._bandRadius(this.innerRingRadius)\n : this._bandRadius(OUTER_RING_RADIUS);\n const maxDigits = Math.max(\n ...this.tickmarks.map((t) => t.text?.length ?? 0)\n );\n const tickmarks = this.tickmarks.map((t) =>\n tickmark(t.angle, {\n size: t.type,\n style: this.tickmarkStyle,\n scale,\n text: this.showLabels || this._labelsHidden ? undefined : t.text,\n inside: this.tickmarksInside,\n textRadius,\n rotation: this.rotation,\n maxDigits,\n color: t.color,\n radiusOffset: rOff,\n endLabelsMaxMin: this.endLabelsMaxMin,\n })\n );\n const advices = this.advices\n ? this.advices.map((a) => renderAdvice(a, rOff))\n : nothing;\n\n // Compute label positions once – used for both rendering and crosshair\n // knockout. NSWE labels and the north arrow are px-fixed outside decor,\n // so they follow the same labelsHidden degradation as tick label texts.\n const showNsweLabels = this.showLabels && !this._labelsHidden;\n const showNorthArrow = this.northArrow && !this._labelsHidden;\n const insideLabels = this.tickmarksInside && showNsweLabels;\n const includeNorth = !this.northArrow;\n const labelPositions = showNsweLabels\n ? getLabelPositions({\n scale,\n inside: this.tickmarksInside,\n innerRadius: this.innerRingRadius + rOff,\n includeNorth,\n insideFlush: this.insideLabelsFlush,\n })\n : undefined;\n\n const labels = labelPositions\n ? renderLabels({\n scale,\n rotation: this.rotation,\n inside: this.tickmarksInside,\n innerRadius: this.innerRingRadius + rOff,\n includeNorth,\n insideFlush: this.insideLabelsFlush,\n })\n : nothing;\n const northArrowEl = showNorthArrow\n ? this.northMarker\n ? renderNorthMarker({scale, rotation: this.rotation})\n : renderNorthArrow({\n scale,\n rotation: this.rotation,\n inside: this.northArrowInside ?? this.tickmarksInside,\n })\n : nothing;\n const wind =\n this.windKnots != null && this.windFromDirectionDeg != null\n ? svg`<g transform=\"scale(${this.scaleWindIcon})\">${renderWind({\n windKnots: this.windKnots,\n fromDirectionDeg: this.windFromDirectionDeg,\n radius: this.windSymbolRadius ?? 192,\n color: this.windColor,\n })}</g>`\n : nothing;\n const current =\n this.current != null && this.currentFromDirectionDeg != null\n ? this.currentIconCentered\n ? renderCurrentCentered({\n current: this.current,\n fromDirectionDeg: this.currentFromDirectionDeg,\n scale: this.scaleCurrentIcon,\n color: this.currentColor,\n })\n : renderCurrent({\n current: this.current,\n fromDirectionDeg: this.currentFromDirectionDeg,\n radius: this.currentSymbolRadius ?? 192,\n color: this.currentColor,\n })\n : nothing;\n return html`\n <svg\n width=\"100%\"\n height=\"100%\"\n viewBox=${viewBox}\n style=\"--scale: ${scale}\"\n transform=\"rotate(${this.rotation ?? 0})\"\n >\n ${this.watchCircle()} ${this.renderBars()}\n ${this.crosshairEnabled\n ? this.renderCrosshair(\n OUTER_RING_RADIUS + rOff,\n insideLabels && labelPositions\n ? {\n positions: labelPositions,\n rotation: this.rotation,\n scale,\n innerRingRadius: this.innerRingRadius + rOff,\n }\n : undefined,\n this.crosshairCenterCutout\n ? this.innerRingRadius + rOff\n : undefined\n )\n : nothing}\n ${northArrowEl} ${this.renderStarboardPortIndicator()} ${current}\n ${this._renderTickFadeDefs()} ${wind}\n ${this.tickFadeAngle > 0 && this.areas.length > 0\n ? svg`<g mask=\"url(#tickFadeMask)\">${tickmarks}</g>`\n : tickmarks}\n ${this.areas.length > 0\n ? svg`<g clip-path=\"url(#rot-arc-clip)\">${this.renderRot()}</g>`\n : this.renderRot()}\n ${advices} ${angleSetpoint}\n ${this.tickFadeAngle > 0 && this.areas.length > 0\n ? svg`<g mask=\"url(#tickFadeMask)\">${labels}</g>`\n : labels}\n ${this.renderVesselImage()} ${this.renderNeedles()}\n </svg>\n `;\n }\n\n private getRotColors(): {\n dotColor: string;\n barBgColor: string;\n } {\n const p = this.rotPriority ?? this.priority;\n const isEnhanced = p === Priority.enhanced;\n\n if (this.rotPortStarboard) {\n // For bar type, derive direction from bar angles (visual direction);\n // for dots, use spinner RPM.\n let direction: number;\n if (this.rotType === RotType.bar) {\n const cwSpan =\n (((this.rotEndAngle - this.rotStartAngle) % 360) + 360) % 360;\n direction = cwSpan <= 180 ? cwSpan : cwSpan - 360;\n } else {\n direction = this._effectiveRpm;\n }\n\n if (direction > 0) {\n return {\n dotColor: 'var(--instrument-starboard-secondary-color)',\n barBgColor: 'var(--instrument-starboard-primary-color)',\n };\n }\n if (direction < 0) {\n return {\n dotColor: 'var(--instrument-port-secondary-color)',\n barBgColor: 'var(--instrument-port-primary-color)',\n };\n }\n }\n\n return {\n dotColor: isEnhanced\n ? 'var(--instrument-enhanced-tertiary-color)'\n : 'var(--instrument-regular-tertiary-color)',\n barBgColor: isEnhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)',\n };\n }\n\n private renderRot(): SVGTemplateResult | typeof nothing {\n if (!this.rotType) return nothing;\n\n const {dotColor, barBgColor} = this.getRotColors();\n const rOff = this._rOff;\n\n if (this.rotType === RotType.bar) {\n const angularDelta = shortestAngularDeltaDeg(\n this.rotStartAngle,\n this.rotEndAngle\n );\n const threshold = rotBarThresholdAngle(this.rotPosition, rOff);\n const zeroDb = Number.isFinite(this.rotAtZeroDeadband)\n ? this.rotAtZeroDeadband\n : ROT_ZERO_DEADBAND_DEG;\n\n if (angularDelta < Math.max(zeroDb, threshold)) {\n return renderRotZeroPill(\n barBgColor,\n this.rotStartAngle,\n this.rotPosition,\n rOff\n );\n }\n\n return svg`\n ${renderRotBarStatic({\n startAngle: this.rotStartAngle,\n endAngle: this.rotEndAngle,\n barColor: barBgColor,\n position: this.rotPosition,\n maskId: 'rot-bar-mask',\n radiusOffset: rOff,\n })}\n ${svg`<g clip-path=\"url(#rot-bar-mask)\">\n <g id=\"rot-spinner\">\n ${renderRotBarDots(dotColor, this.rotPosition, rOff)}\n </g>\n </g>`}\n `;\n }\n\n const p = this.rotPriority ?? this.priority;\n const isEnhanced = p === Priority.enhanced;\n let dotsColor: string = isEnhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n if (this.rotPortStarboard) {\n if (this._effectiveRpm > 0) {\n dotsColor = 'var(--instrument-starboard-secondary-color)';\n } else if (this._effectiveRpm < 0) {\n dotsColor = 'var(--instrument-port-secondary-color)';\n }\n }\n return svg`\n <g id=\"rot-spinner\">\n ${renderRotDots(dotsColor, this.rotPosition, rOff)}\n </g>\n `;\n }\n\n private renderSetpoint(): SVGTemplateResult | typeof nothing {\n if (this.angleSetpoint === undefined) {\n return nothing;\n }\n\n const derived = deriveRadialSetpointConfig({\n state: this.state,\n priority: this.priority,\n atSetpoint: this.atAngleSetpoint,\n angleSetpoint: this.angleSetpoint,\n setpointAtZeroDeadband: this.angleSetpointAtZeroDeadband,\n newAngleSetpoint: this.newAngleSetpoint,\n touching: this.touching,\n setpointOverride: this.setpointOverride,\n });\n\n const {visualState, colorMode, disabled, hasNewSetpoint} = derived;\n\n const outwardOffset = getSetpointOutwardOffset(visualState);\n const radius =\n RADIAL_SETPOINT_RADIUS +\n this._rOff +\n outwardOffset -\n RADIAL_SETPOINT_INWARD_ADJUST;\n\n // Render original setpoint marker (dimmed when newAngleSetpoint is active)\n const opacity = hasNewSetpoint ? 0.75 : 1;\n const originalMarker = drawSetpointMarker({\n visualState,\n colorMode,\n disabled,\n id: this._setpointId,\n });\n\n const animate = this.animateSetpoint;\n const hasDeparting = this._departingNewAngleSetpoint !== undefined;\n\n // Compute CSS-safe accumulated angle so transitions always take the short path\n const rawAngle = this.angleSetpoint + 90;\n if (!this._setpointCssAngleInit) {\n // First render: set angle without transition\n this._setpointCssAngle = rawAngle;\n this._setpointCssAngleInit = true;\n } else {\n this._setpointCssAngle = cssSafeAngle(this._setpointCssAngle, rawAngle);\n }\n\n // Use CSS style transform when animating for smooth transition\n const originalSetpoint = animate\n ? svg`\n <g style=\"transform: rotate(${this._setpointCssAngle}deg) translateX(${-radius}px) rotate(270deg); opacity: ${opacity}; transition: transform var(${SETPOINT_ANIMATION_CSS_VAR}, ${SETPOINT_ANIMATION_DURATION_DEFAULT}) ease-out, opacity var(${SETPOINT_ANIMATION_CSS_VAR}, ${SETPOINT_ANIMATION_DURATION_DEFAULT}) ease-out;\">\n ${originalMarker}\n </g>\n `\n : svg`\n <g transform=\"rotate(${this.angleSetpoint + 90}) translate(${-radius}, 0) rotate(270)\" opacity=\"${opacity}\">\n ${originalMarker}\n </g>\n `;\n\n // Render newAngleSetpoint in focus state (always on top)\n // OR render departing newAngleSetpoint during confirm fade-out\n if (hasNewSetpoint || hasDeparting) {\n const isActive = hasNewSetpoint;\n const newAngle = isActive\n ? this.newAngleSetpoint!\n : this._departingNewAngleSetpoint!;\n const targetOpacity = isActive ? 1 : 0;\n\n const focusOutwardOffset = getSetpointOutwardOffset(\n SetpointVisualState.focus\n );\n const focusRadius =\n RADIAL_SETPOINT_RADIUS +\n this._rOff +\n focusOutwardOffset -\n RADIAL_SETPOINT_INWARD_ADJUST;\n\n const newMarker = drawSetpointMarker({\n visualState: SetpointVisualState.focus,\n colorMode,\n disabled: false, // newSetpoint is never disabled\n id: this._newSetpointId,\n });\n\n if (animate) {\n const duration = `var(${SETPOINT_ANIMATION_CSS_VAR}, ${SETPOINT_ANIMATION_DURATION_DEFAULT})`;\n return svg`\n ${originalSetpoint}\n <g style=\"transform: rotate(${newAngle + 90}deg) translateX(${-focusRadius}px) rotate(270deg); opacity: ${targetOpacity}; transition: opacity ${duration} ease-out;\">\n ${newMarker}\n </g>\n `;\n }\n\n return svg`\n ${originalSetpoint}\n <g transform=\"rotate(${newAngle + 90}) translate(${-focusRadius}, 0) rotate(270)\" opacity=\"${targetOpacity}\">\n ${newMarker}\n </g>\n `;\n }\n\n return originalSetpoint;\n }\n\n private renderVesselImage(): SVGTemplateResult[] | typeof nothing {\n if (this.vessels.length === 0) {\n return nothing;\n }\n\n return this.vessels.map((v) => {\n let size;\n switch (v.size) {\n case VesselImageSize.large:\n size = 224;\n break;\n case VesselImageSize.medium:\n size = 160;\n break;\n default:\n size = 100;\n }\n\n const scale = size / 160;\n return svg`<g style=\"transform: ${v.transform} scale(${scale}) translate(-80px, -80px) \">${vesselImages[v.vesselImage]}</g>`;\n });\n }\n\n private renderStarboardPortIndicator(): SVGTemplateResult[] | typeof nothing {\n if (!this.starboardPortIndicator) {\n return nothing;\n }\n return [\n adviceMask(\n 0,\n 180,\n 'var(--instrument-starboard-secondary-color)',\n 'var(--instrument-starboard-secondary-color)'\n ),\n adviceMask(\n 180,\n 360,\n 'var(--instrument-port-secondary-color)',\n 'var(--instrument-port-secondary-color)'\n ),\n ] as SVGTemplateResult[];\n }\n\n static override styles = unsafeCSS(compentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-watch': ObcWatch;\n }\n}\n"],"names":["WatchCircleType"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"watch.js","sources":["../../../src/navigation-instruments/watch/watch.ts"],"sourcesContent":["import {\n LitElement,\n PropertyValues,\n SVGTemplateResult,\n html,\n nothing,\n svg,\n unsafeCSS,\n} from 'lit';\nimport {property, state} from 'lit/decorators.js';\nimport {circle} from '../../svghelpers/index.js';\nimport {roundedArch} from '../../svghelpers/roundedArch.js';\nimport {\n cssSafeAngle,\n deriveRadialSetpointConfig,\n drawSetpointMarker,\n getSetpointAnimationDurationMs,\n getSetpointOutwardOffset,\n RADIAL_SETPOINT_RADIUS,\n SetpointVisualState,\n SETPOINT_ANIMATION_CSS_VAR,\n SETPOINT_ANIMATION_DURATION_DEFAULT,\n} from '../../svghelpers/setpoint.js';\nimport {InstrumentState, Priority} from '../types.js';\nimport compentStyle from './watch.css?inline';\nimport {ResizeController} from '@lit-labs/observers/resize-controller.js';\nimport {adviceMask, AngleAdviceRaw, renderAdvice} from './advice.js';\nimport {Tickmark, TickmarkStyle, tickmark} from './tickmark.js';\nexport {TickmarkStyle};\nimport {\n RotType,\n RotPosition,\n renderRotDots,\n renderRotBarStatic,\n renderRotBarDots,\n shortestAngularDeltaDeg,\n renderRotZeroPill,\n rotBarThresholdAngle,\n ROT_ZERO_DEADBAND_DEG,\n} from '../rate-of-turn/rot-renderer.js';\nexport {RotType, RotPosition};\nimport {\n RateOfTurnController,\n disposeRotController,\n} from '../rate-of-turn/rate-of-turn.controller.js';\nimport {\n renderLabels,\n renderNorthArrow,\n renderNorthMarker,\n getLabelPositions,\n LabelPosition,\n} from './label.js';\nimport {VesselImage, VesselImageSize, vesselImages} from './vessel.js';\nimport {\n renderCurrent,\n renderCurrentCentered,\n renderWind,\n WIND_ICON_TIP_TO_BOX_INNER,\n} from './environment.js';\nimport {customElement} from '../../decorator.js';\nimport {type ZoomToFitArcFrame} from '../../svghelpers/arc-frame.js';\nimport {\n applyPinnedHostSize,\n computeRadialFrame,\n estimateLabelWidthPx,\n measureContainerPx,\n NSWE_LABEL_WIDTH_PX,\n observeInnerBox,\n type RadialFrame,\n} from '../../svghelpers/radial-frame.js';\nexport {VesselImage, VesselImageSize, vesselImages};\n\nexport enum WatchCircleType {\n single = 'single',\n double = 'double',\n doubleThin = 'doubleThin',\n triple = 'triple',\n}\n\nexport interface WatchArea {\n startAngle: number;\n endAngle: number;\n roundOutsideCut: boolean;\n roundInsideCut: boolean;\n}\n\nexport interface WatchBarArea {\n startAngle: number;\n endAngle: number;\n fillColor: string;\n /** Outer band radius override; defaults to the second ring (160). */\n outerRadius?: number;\n /** Inner band radius override; defaults to the third ring (112). */\n innerRadius?: number;\n}\n\nexport interface WatchNeedle {\n angle: number;\n fillColor: string;\n strokeColor: string;\n /** Radial length of the needle pill; defaults to 48 (the full band). */\n length?: number;\n}\n\nexport interface WatchVessel {\n size: VesselImageSize;\n transform: string;\n vesselImage: VesselImage;\n}\n\nexport const OUTER_RING_RADIUS = 368 / 2;\nconst RING2_RADIUS = 320 / 2;\nconst RING3_RADIUS = 224 / 2;\nconst RING3B_RADIUS = 272 / 2;\nconst RING4_RADIUS = 176 / 2;\n\nexport function innerRingRadiusFor(type: WatchCircleType): number {\n switch (type) {\n case WatchCircleType.single:\n return RING2_RADIUS;\n case WatchCircleType.double:\n return RING3_RADIUS;\n case WatchCircleType.doubleThin:\n return RING3B_RADIUS;\n case WatchCircleType.triple:\n return RING4_RADIUS;\n default:\n throw new Error(`Unknown WatchCircleType: ${type as string}`);\n }\n}\n\nconst RADIAL_SETPOINT_INWARD_ADJUST = 4;\n\n/**\n * Default anchors for the wind/current icons outside the outer ring (the\n * compass layout): the 48-unit icon box sits 4 units off the ring, spanning\n * 188–236 (OpenBridge 6.1 compass, node 19846-157200). The current chevron\n * anchors by its tip at the box inner edge; the wind anchor adds the glyph's\n * inward overhang so its box starts at the same edge.\n */\nconst CURRENT_ICON_OUTSIDE_RADIUS = OUTER_RING_RADIUS + 4;\nconst WIND_ICON_OUTSIDE_RADIUS =\n CURRENT_ICON_OUTSIDE_RADIUS + WIND_ICON_TIP_TO_BOX_INNER;\n\n/**\n * `<obc-watch>` - Core SVG renderer for circular/radial watch-based instruments.\n *\n * This component renders all circular instrument elements including rings, tickmarks,\n * bar areas, needles, advices, setpoints, vessel images, and environmental indicators\n * (wind/current). It serves as the foundation for compass, heading, rudder, speed-gauge,\n * and other radial navigation instruments.\n *\n * ## Setpoint Behavior\n *\n * The setpoint marker visual state is derived from the combination of `atAngleSetpoint`,\n * `angleSetpoint`, and `angleSetpointAtZeroDeadband` properties:\n *\n * - **notEqual**: Value differs from setpoint (triangular marker, offset outward)\n * - **equal**: Value matches setpoint (line marker, sits on ring)\n * - **equalZero**: Value matches setpoint at zero angle (double-line marker, offset outward)\n * - **focus**: User is actively adjusting via `newAngleSetpoint` - shows focus visual state\n *\n * ## newAngleSetpoint Pattern\n *\n * When `newAngleSetpoint` is defined, TWO setpoint markers are rendered:\n * 1. Original marker at `angleSetpoint` - dimmed (0.75 opacity)\n * 2. New marker at `newAngleSetpoint` - focus visual state, full opacity\n *\n * This enables the \"adjustment preview\" UX where users can see both the current\n * and proposed setpoint positions simultaneously.\n *\n * The `RADIAL_SETPOINT_INWARD_ADJUST` constant (4px) fine-tunes radial setpoint positioning\n * to match Figma designs, applied on top of visual state offsets from setpoint.ts.\n *\n * The `colorMode` property allows overriding the derived color mode (enhanced for enhanced priority,\n * regular for other states).\n *\n * ## Setpoint Animation (`animateSetpoint`)\n *\n * When `animateSetpoint` is true and a confirm occurs (`newAngleSetpoint` → `undefined`):\n * - The original setpoint slides to the new position via CSS transition\n * - The departing new-setpoint marker fades out\n * - Angular transitions always take the shortest path via accumulated\n * CSS-safe angles (`cssSafeAngle()`), so even 350° → 10° animates +20°\n *\n * Duration: `var(--setpoint-animation-duration, 300ms)`\n *\n * Internally, `_departingNewAngleSetpoint` captures the departing angle during confirm\n * fade-out and `_animationTimer` auto-clears it after the animation duration.\n * `_setpointCssAngle` tracks the accumulated CSS angle to avoid long-way-around\n * transitions across the 0°/360° boundary.\n *\n * @experimental\n */\n@customElement('obc-watch')\nexport class ObcWatch extends LitElement {\n private _setpointId = `watch-setpoint-${Math.random().toString(36).slice(2, 9)}`;\n private _newSetpointId = `watch-new-setpoint-${Math.random().toString(36).slice(2, 9)}`;\n\n /** Instrument state (active, loading, off) */\n @property({type: String}) state: InstrumentState = InstrumentState.active;\n /** Color priority (enhanced = blue palette, regular = gray palette) */\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: String}) watchCircleType: WatchCircleType =\n WatchCircleType.single;\n /**\n * When `true`, the full watch-face circle is drawn behind the dial —\n * a frame-primary fill with a tertiary outline — so partial-sector\n * instruments still read as a complete circle. In the `off` state the fill\n * is omitted (only the outline remains), keeping the off skeleton hollow.\n * With sector `areas`, the per-area arch outline is dropped; the face\n * outline takes its place.\n */\n @property({type: Boolean}) hasBackgroundCircle: boolean = false;\n @property({type: Boolean}) northArrow: boolean = false;\n @property({type: Boolean}) northArrowInside: boolean | undefined;\n /**\n * Replace the north arrow with the compact heading-up north marker: a small\n * triangle on the outer ring with an upright \"N\" at the outside label\n * radius. Rotates with the card via `rotation`.\n * @availableWhen northArrow==true\n */\n @property({type: Boolean}) northMarker: boolean = false;\n /** Setpoint angle in degrees (0° = 12 o'clock) */\n @property({type: Number}) angleSetpoint: number | undefined;\n /** New setpoint being adjusted (focus mode) */\n @property({type: Number}) newAngleSetpoint: number | undefined;\n /** Whether value matches setpoint (within deadband) */\n @property({type: Boolean}) atAngleSetpoint: boolean = false;\n /** Deadband for zero detection (default 0.5°) */\n @property({type: Number}) angleSetpointAtZeroDeadband: number = 0.5;\n /** Override to derive setpoint color from priority regardless of state */\n @property({type: Boolean}) setpointOverride: boolean = false;\n @property({type: Boolean}) touching: boolean = false;\n\n @property({type: Boolean}) animateSetpoint: boolean = false;\n\n @state() private _departingNewAngleSetpoint: number | undefined;\n private _animationTimer?: ReturnType<typeof setTimeout>;\n\n /**\n * Accumulated CSS-safe angle for the original setpoint marker.\n * Ensures CSS rotate() transitions always take the shortest path,\n * even across the 0°/360° boundary.\n */\n private _setpointCssAngle: number = 0;\n\n /** Whether the setpoint CSS angle has been initialised (to skip transition on first render). */\n private _setpointCssAngleInit = false;\n /**\n * Explicit padding override in SVG units: the un-zoomed viewBox becomes\n * exactly `(176 + padding) * 2`. Setting it disables the automatic\n * width-aware label reserve (issue #1021) — the caller owns label room.\n */\n @property({type: Number}) padding: number | undefined;\n /**\n * Outer-ring diameter in CSS pixels. When set, the instrument renders at a\n * fixed intrinsic size derived from the ring, arc shape and label reserve —\n * so instruments sharing the same value have identical ring circumference\n * regardless of label width or arc extent (like obc-donut-chart's\n * fixedHeight). When unset (default), the instrument fills its container.\n */\n @property({type: Number, attribute: 'face-diameter'})\n faceDiameter: number | undefined;\n @property({type: Array, attribute: false}) areas: WatchArea[] = [];\n @property({type: Array, attribute: false}) barAreas: WatchBarArea[] = [];\n /**\n * Rounds the band track's sector end cuts at the band's own radii and clips\n * bars to that rounded track shape (the design's track-mask model), instead\n * of the joint silhouette cut shared with the scale ring. No effect without\n * `areas`.\n */\n @property({type: Boolean}) roundBandCuts: boolean = false;\n @property({type: Array, attribute: false}) needles: WatchNeedle[] = [];\n @property({type: Array, attribute: false}) tickmarks: Tickmark[] = [];\n @property({type: Boolean}) tickmarksInside: boolean = false;\n @property({type: String}) tickmarkStyle: TickmarkStyle =\n TickmarkStyle.regular;\n @property({type: Array, attribute: false}) advices: AngleAdviceRaw[] = [];\n @property({type: Boolean}) crosshairEnabled: boolean = false;\n /**\n * Cuts the crosshair out inside the inner ring, so center content (e.g.\n * center readouts) sits on a clean face.\n * @availableWhen crosshairEnabled==true\n */\n @property({type: Boolean}) crosshairCenterCutout: boolean = false;\n @property({type: Boolean}) showLabels: boolean = false;\n /**\n * With `tickmarksInside`, anchor the NSEW label boxes flush against the\n * inner ring (px-fixed) instead of the legacy gap that lets them drift\n * toward the centre as the instrument shrinks.\n * @availableWhen tickmarksInside==true\n */\n @property({type: Boolean}) insideLabelsFlush: boolean = false;\n @property({type: Array, attribute: false}) vessels: WatchVessel[] = [];\n @property({type: Number}) windKnots: number | null = null;\n @property({type: Number}) windFromDirectionDeg: number | null = null;\n @property({type: Number}) windSymbolRadius: number | null = null;\n @property({type: String}) windColor: string | undefined;\n @property({type: Number}) current: number | null = null;\n @property({type: Number}) currentFromDirectionDeg: number | null = null;\n @property({type: Number}) currentSymbolRadius: number | null = null;\n @property({type: String}) currentColor: string | undefined;\n /**\n * Render the current icon centered on the face (direction-type layout)\n * instead of at `currentSymbolRadius` on the periphery.\n * @availableWhen current!=null && currentFromDirectionDeg!=null\n */\n @property({type: Boolean}) currentIconCentered: boolean = false;\n /**\n * Scale factor for the centered current icon.\n * @availableWhen currentIconCentered==true\n */\n @property({type: Number}) scaleCurrentIcon: number = 1;\n @property({type: Boolean}) starboardPortIndicator: boolean = false;\n /** Top clip, % of height. Ignored when `zoomToFitArc` is true. */\n @property({type: Number}) clipTop: number = 0;\n /** Bottom clip, % of height. Ignored when `zoomToFitArc` is true. */\n @property({type: Number}) clipBottom: number = 0;\n /** Left clip, % of width — horizontal counterpart of clipTop/bottom (90° sectors). Ignored when `zoomToFitArc`. */\n @property({type: Number}) clipLeft: number = 0;\n /** Right clip, % of width — horizontal counterpart of clipTop/bottom (90° sectors). Ignored when `zoomToFitArc`. */\n @property({type: Number}) clipRight: number = 0;\n /**\n * Place the horizontal end labels (±90°, e.g. min/max) below the tick instead\n * of beside it. This is the \"Max-min\" label placement from the radial label\n * model (External / Internal / Max-min) — see PR #903 / design discussion.\n * Currently only used by the 180° sector of `obc-gauge-radial`.\n */\n @property({type: Boolean}) endLabelsMaxMin: boolean = false;\n @property({type: Number}) scaleWindIcon: number = 1;\n @property({type: Number}) rotation: number | undefined;\n @property({type: Boolean}) zoomToFitArc: boolean = false;\n /** Pre-computed zoom-to-fit arc frame. When set, the watch skips its own `computeZoomToFitArcFrame()` call and uses these values directly. Consumer instruments (e.g. rudder, instrument-radial) should compute the frame once and pass it here to avoid redundant computation. If you pass `arcFrame`, you own keeping it in sync with `areas` / `watchCircleType` — obc-watch will NOT recompute it, so a stale frame renders stale geometry. */\n @property({attribute: false}) arcFrame: ZoomToFitArcFrame | undefined;\n @property({type: Number}) tickFadeAngle: number = 0;\n\n /** ROT visualization type: `'dots'` (spinning dots) or `'bar'` (arc bar with clipped dots). Undefined hides the ROT layer. */\n @property({type: String}) rotType: RotType | undefined;\n /** Track on which ROT elements are placed: `'scale'` (on the outer ring) or `'innerCircle'` (default, inside the inner ring) */\n @property({type: String}) rotPosition: RotPosition = RotPosition.innerCircle;\n /** Start angle of the ROT bar arc in degrees (0° = 12 o'clock, clockwise). Only used when `rotType` is `'bar'`. */\n @property({type: Number}) rotStartAngle: number = 0;\n /** End angle of the ROT bar arc in degrees. The bar is hidden when the difference from `rotStartAngle` is less than 0.1°. */\n @property({type: Number}) rotEndAngle: number = 0;\n /** Override priority for ROT color derivation. When set, ROT colors use this instead of the main `priority`. Useful when the ROT element has independent priority (e.g. compass per-element priority). */\n @property({type: String}) rotPriority: Priority | undefined;\n @property({type: Boolean}) rotPortStarboard: boolean = false;\n @property({type: Number}) rotAtZeroDeadband: number = ROT_ZERO_DEADBAND_DEG;\n /** Measured rate of turn in degrees per minute (the maritime/AIS convention, see ES-TRIN 2025/1 Art. 3.02 and ITU-R M.1371). Sign controls direction (positive = starboard/clockwise). When defined, this drives both the dot animation (multiplied by `rotDotAnimationFactor`) and the port/starboard direction sign. */\n @property({type: Number}) rateOfTurnDegreesPerMinute: number | undefined;\n /** Visual amplification factor applied only to the spinning-dot animation (not to bar extent). Default `18` keeps the legacy visual feel (≈1 rpm at 20°/min). */\n @property({type: Number}) rotDotAnimationFactor: number = 18;\n /**\n * @deprecated Use `rateOfTurnDegreesPerMinute` (and optionally `rotDotAnimationFactor`) instead.\n * Kept as a backward-compatible alias; takes effect only when\n * `rateOfTurnDegreesPerMinute` is `undefined`.\n */\n @property({type: Number})\n set rotationsPerMinute(value: number) {\n this._legacyRotationsPerMinute = value;\n }\n /** Legacy spin speed of the ROT dot ring, in rotations per minute (sign controls direction; positive = clockwise). */\n get rotationsPerMinute() {\n return this._legacyRotationsPerMinute;\n }\n private _legacyRotationsPerMinute = 0;\n private _rotController?: RateOfTurnController;\n\n /**\n * Effective rotations-per-minute for the spinning dot animation and\n * port/starboard direction sign. Resolves to:\n * • `(rateOfTurnDegreesPerMinute / 360) * rotDotAnimationFactor` when the\n * new physical API is in use, OR\n * • the legacy `rotationsPerMinute` value otherwise.\n */\n private get _effectiveRpm(): number {\n if (this.rateOfTurnDegreesPerMinute != null) {\n return (\n (this.rateOfTurnDegreesPerMinute / 360) * this.rotDotAnimationFactor\n );\n }\n return this._legacyRotationsPerMinute;\n }\n\n private _resizeController = new ResizeController(this, {});\n\n override firstUpdated(changed: PropertyValues): void {\n super.firstUpdated(changed);\n observeInnerBox(this._resizeController, this.renderRoot);\n }\n\n override willUpdate(changed: PropertyValues): void {\n super.willUpdate(changed);\n\n // Push the resolved effective RPM into the live controller whenever any\n // of the inputs that compose it change.\n if (\n this._rotController &&\n (changed.has('rateOfTurnDegreesPerMinute') ||\n changed.has('rotDotAnimationFactor') ||\n changed.has('rotationsPerMinute'))\n ) {\n this._rotController.rotationsPerMinute = this._effectiveRpm;\n }\n\n // Detect confirm: newAngleSetpoint was defined, now undefined\n if (changed.has('newAngleSetpoint') && this.animateSetpoint) {\n const prev = changed.get('newAngleSetpoint') as number | undefined;\n if (prev !== undefined && this.newAngleSetpoint === undefined) {\n this._departingNewAngleSetpoint = prev;\n clearTimeout(this._animationTimer);\n const duration = getSetpointAnimationDurationMs(this);\n this._animationTimer = setTimeout(() => {\n this._departingNewAngleSetpoint = undefined;\n }, duration);\n }\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n clearTimeout(this._animationTimer);\n this._rotController = disposeRotController(this, this._rotController);\n }\n\n override updated(changed: PropertyValues): void {\n super.updated(changed);\n this._hostSizePinned = applyPinnedHostSize(\n this,\n this.arcFrame ? undefined : this._ownFrame,\n this._hostSizePinned\n );\n const el = this.rotType\n ? this.renderRoot.querySelector('#rot-spinner')\n : null;\n if (!el) {\n this._rotController = disposeRotController(this, this._rotController);\n return;\n }\n if (!this._rotController || this._rotController.el !== el) {\n this._rotController = disposeRotController(this, this._rotController);\n this._rotController = new RateOfTurnController(\n this,\n el,\n this._effectiveRpm\n );\n }\n }\n\n private get innerRingRadius(): number {\n return innerRingRadiusFor(this.watchCircleType);\n }\n\n private _rOff = 0;\n\n /**\n * Set by the frame when the label reserve exceeded its cap: tick label\n * texts are dropped instead of clipped (see radial-frame.ts).\n */\n private _labelsHidden = false;\n\n /** The frame of the last render, when computed internally (no arcFrame). */\n private _ownFrame: RadialFrame | undefined;\n\n /** Whether the host size styles were set by applyPinnedHostSize. */\n private _hostSizePinned = false;\n\n /**\n * Radius for a dial-band edge under zoom: additive (`base + _rOff`), keeping\n * band thickness constant in SVG units. INVARIANT: every band-edge radius\n * (rings, value arc, bars, needles, inside-label `textRadius`) must go through\n * this — mixing additive and multiplicative offsets misaligns ticks, labels,\n * advice masks and arcs. The proportional experiments were removed for this\n * reason (PR #903).\n */\n private _bandRadius(base: number): number {\n return base + this._rOff;\n }\n\n private watchCircle(): SVGTemplateResult | SVGTemplateResult[] {\n const rings = [];\n // Full-face circle behind everything, split in two: the fill goes under\n // the rings and the outline goes on top of them — the masked track band\n // reaches exactly the face radius, so an outline drawn underneath would\n // lose its inner half wherever the band overlaps it and look thinner\n // there than across the open sector gap. In the ring branch the existing\n // outer ring already outlines the face, so only the fill applies.\n const faceFill =\n this.hasBackgroundCircle && this.state !== InstrumentState.off\n ? svg`\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${this._bandRadius(OUTER_RING_RADIUS)}\"\n fill=\"var(--instrument-frame-primary-color)\"\n stroke=\"none\"\n />`\n : undefined;\n const faceOutline =\n this.hasBackgroundCircle && this.areas.length > 0\n ? svg`\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${this._bandRadius(OUTER_RING_RADIUS)}\"\n fill=\"none\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n />`\n : undefined;\n if (this.state !== InstrumentState.off) {\n rings.push(svg`\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${this._bandRadius(172)}\"\n stroke=\"var(--instrument-frame-primary-color)\"\n fill=\"none\"\n stroke-width=\"24\"\n />`);\n\n if (this.watchCircleType !== WatchCircleType.single) {\n const r1 = this._bandRadius(RING2_RADIUS);\n const r2 = this._bandRadius(\n this.watchCircleType === WatchCircleType.doubleThin\n ? RING3B_RADIUS\n : RING3_RADIUS\n );\n const r = (r1 + r2) / 2;\n const strokeWidth = r1 - r2;\n if (this.roundBandCuts && this.areas.length > 0) {\n rings.push(\n ...this.areas.map(\n (area) =>\n svg`<path d=${roundedArch({\n startAngle: area.startAngle,\n endAngle: area.endAngle,\n R: r1,\n r: r2,\n roundOutsideCut: area.roundOutsideCut,\n roundInsideCut: area.roundInsideCut,\n })} fill=\"var(--instrument-frame-secondary-color)\" stroke=\"var(--instrument-frame-secondary-color)\" stroke-width=\"1\" vector-effect=\"non-scaling-stroke\" />`\n )\n );\n } else {\n rings.push(\n svg`\n <circle cx=\"0\" cy=\"0\" r=${r} stroke=\"var(--instrument-frame-secondary-color)\" stroke-width=${strokeWidth} fill=\"none\" />\n <circle cx=\"0\" cy=\"0\" r=${r1} stroke=\"var(--instrument-frame-secondary-color)\" stroke-width=\"1\" fill=\"none\" vector-effect=\"non-scaling-stroke\" />\n <circle cx=\"0\" cy=\"0\" r=${r2} stroke=\"var(--instrument-frame-secondary-color)\" stroke-width=\"1\" fill=\"none\" vector-effect=\"non-scaling-stroke\" />\n `\n );\n }\n }\n if (this.watchCircleType === WatchCircleType.triple) {\n const r1 = this._bandRadius(RING3_RADIUS);\n const r2 = this._bandRadius(RING4_RADIUS);\n const r = (r1 + r2) / 2;\n const strokeWidth = r1 - r2;\n rings.push(\n svg`<circle cx=\"0\" cy=\"0\" r=${r} stroke=\"var(--instrument-frame-primary-color)\" stroke-width=${strokeWidth} fill=\"none\" />`\n );\n }\n }\n\n const maskSize = Math.max(200, OUTER_RING_RADIUS + this._rOff + 50);\n let result = rings;\n if (this.areas.length > 0) {\n const areas = this.areas.map((area) => {\n const svgPath = roundedArch({\n startAngle: area.startAngle,\n endAngle: area.endAngle,\n R: this._bandRadius(OUTER_RING_RADIUS),\n r: this._bandRadius(this.innerRingRadius),\n roundOutsideCut: area.roundOutsideCut,\n roundInsideCut: area.roundInsideCut,\n });\n return svgPath;\n });\n const mask = svg`<mask id=\"cutMask\">\n <rect x=\"${-maskSize}\" y=\"${-maskSize}\" width=\"${maskSize * 2}\" height=\"${maskSize * 2}\" fill=\"black\" />\n ${areas.map((area) => svg`<path d=${area} fill=\"white\" vector-effect=\"non-scaling-stroke\" stroke=\"white\" stroke-width=\"1\"/>`)}\n </mask>`;\n // clipPath for ROT uses r=0 so dots at any track radius stay visible\n const rotClip = svg`<clipPath id=\"rot-arc-clip\">${this.areas.map(\n (area) =>\n svg`<path d=${roundedArch({\n startAngle: area.startAngle,\n endAngle: area.endAngle,\n R: OUTER_RING_RADIUS + this._rOff + 20,\n r: 0,\n roundOutsideCut: area.roundOutsideCut,\n roundInsideCut: area.roundInsideCut,\n })} />`\n )}</clipPath>`;\n result = [\n mask,\n rotClip,\n ...(faceFill ? [faceFill] : []),\n svg`<g mask=\"url(#cutMask)\">${rings}</g>`,\n ...(faceOutline ? [faceOutline] : []),\n ];\n if (!this.hasBackgroundCircle) {\n areas.forEach((area) => {\n result.push(\n svg`<path d=${area} fill=\"none\" stroke=\"var(--instrument-frame-tertiary-color)\" vector-effect=\"non-scaling-stroke\"/>`\n );\n });\n }\n } else {\n if (faceFill) {\n result = [faceFill, ...rings];\n }\n if (this.state !== InstrumentState.off) {\n result.push(\n circle('outerRing', {\n radius: this._bandRadius(OUTER_RING_RADIUS),\n strokeWidth: 1,\n strokeColor: 'var(--instrument-frame-tertiary-color)',\n strokePosition: 'center',\n fillColor: 'none',\n })\n );\n\n result.push(svg`\n ${circle('innerRing', {\n radius: this._bandRadius(this.innerRingRadius),\n strokeWidth: 1,\n strokeColor: 'var(--instrument-frame-tertiary-color)',\n strokePosition: 'center',\n fillColor: 'none',\n })}\n `);\n } else {\n result.push(svg`\n ${circle('innerRing', {\n radius: this._bandRadius(OUTER_RING_RADIUS),\n strokeWidth: 1,\n strokeColor: 'var(--instrument-frame-tertiary-color)',\n strokePosition: 'center',\n fillColor: 'none',\n })}\n `);\n }\n }\n return result;\n }\n\n private _renderTickFadeDefs(): SVGTemplateResult | typeof nothing {\n if (this.tickFadeAngle <= 0 || this.areas.length === 0) return nothing;\n const area = this.areas[0];\n const arcSpan = area.endAngle - area.startAngle;\n const fade = Math.min(this.tickFadeAngle, arcSpan / 4);\n if (fade < 0.5) return nothing;\n\n const {startAngle, endAngle} = area;\n const R = OUTER_RING_RADIUS + this._rOff + 200;\n const toRad = (deg: number) => (deg * Math.PI) / 180;\n const px = (deg: number) => R * Math.sin(toRad(deg));\n const py = (deg: number) => -R * Math.cos(toRad(deg));\n\n const pieSlice = (a: number, b: number): string => {\n const x1 = px(a),\n y1 = py(a);\n const x2 = px(b),\n y2 = py(b);\n const largeArc = b - a > 180 ? 1 : 0;\n return `M 0 0 L ${x1} ${y1} A ${R} ${R} 0 ${largeArc} 1 ${x2} ${y2} Z`;\n };\n\n const Rm =\n (this._bandRadius(OUTER_RING_RADIUS) +\n this._bandRadius(this.innerRingRadius)) /\n 2;\n const gx = (deg: number) => Rm * Math.sin(toRad(deg));\n const gy = (deg: number) => -Rm * Math.cos(toRad(deg));\n\n return svg`\n <defs>\n <linearGradient id=\"tickFadeL\" gradientUnits=\"userSpaceOnUse\"\n x1=\"${gx(startAngle)}\" y1=\"${gy(startAngle)}\"\n x2=\"${gx(startAngle + fade)}\" y2=\"${gy(startAngle + fade)}\">\n <stop offset=\"0\" stop-color=\"black\" />\n <stop offset=\"1\" stop-color=\"white\" />\n </linearGradient>\n <linearGradient id=\"tickFadeR\" gradientUnits=\"userSpaceOnUse\"\n x1=\"${gx(endAngle - fade)}\" y1=\"${gy(endAngle - fade)}\"\n x2=\"${gx(endAngle)}\" y2=\"${gy(endAngle)}\">\n <stop offset=\"0\" stop-color=\"white\" />\n <stop offset=\"1\" stop-color=\"black\" />\n </linearGradient>\n <mask id=\"tickFadeMask\" maskUnits=\"userSpaceOnUse\"\n x=\"${-R}\" y=\"${-R}\" width=\"${R * 2}\" height=\"${R * 2}\">\n <path d=\"${pieSlice(startAngle + fade, endAngle - fade)}\" fill=\"white\" />\n <path d=\"${pieSlice(startAngle, startAngle + fade)}\" fill=\"url(#tickFadeL)\" />\n <path d=\"${pieSlice(endAngle - fade, endAngle)}\" fill=\"url(#tickFadeR)\" />\n </mask>\n </defs>\n `;\n }\n\n private renderCrosshair(\n radius: number,\n labelKnockouts?: {\n positions: LabelPosition[];\n rotation: number | undefined;\n scale: number;\n /** Inner ring radius – crosshair is hidden between labelRadius and this value. */\n innerRingRadius: number;\n },\n centerCutoutRadius?: number\n ): SVGTemplateResult {\n const hasLabelKnockouts =\n !!labelKnockouts && labelKnockouts.positions.length > 0;\n const hasMask = hasLabelKnockouts || centerCutoutRadius !== undefined;\n\n // Radius at which labels sit (distance from centre).\n // Any position is equally valid — they're all at the same radial distance.\n const labelRadius = hasLabelKnockouts\n ? Math.max(\n ...labelKnockouts!.positions.map((l) =>\n Math.abs(l.x !== 0 ? l.x : l.y)\n )\n )\n : 0;\n // Small extra padding so the crosshair doesn't start/end right at the\n // label edge — use the same visual pad as the letter knockouts.\n const ringGapPad = hasLabelKnockouts ? 3 / labelKnockouts!.scale : 0;\n\n return svg`\n ${\n hasMask\n ? svg`\n <defs>\n <mask\n id=\"crosshair-label-mask\"\n maskUnits=\"userSpaceOnUse\"\n x=\"-${radius}\" y=\"-${radius}\"\n width=\"${radius * 2}\" height=\"${radius * 2}\"\n >\n <rect x=\"-${radius}\" y=\"-${radius}\" width=\"${radius * 2}\" height=\"${radius * 2}\" fill=\"white\"/>\n ${\n hasLabelKnockouts\n ? svg`\n <!-- Annular ring knockout: hide crosshair between labels and inner ring -->\n <circle cx=\"0\" cy=\"0\" r=\"${labelKnockouts!.innerRingRadius}\" fill=\"black\"/>\n <circle cx=\"0\" cy=\"0\" r=\"${labelRadius - ringGapPad}\" fill=\"white\"/>\n <!-- Per-label rectangular knockouts -->\n ${labelKnockouts!.positions.map((l) => {\n const fontSize = 12 / labelKnockouts!.scale;\n const pad = 3 / labelKnockouts!.scale;\n const size = fontSize + pad * 2;\n return svg`\n <rect\n x=\"${l.x - size / 2}\" y=\"${l.y - size / 2}\"\n width=\"${size}\" height=\"${size}\"\n fill=\"black\"\n transform=\"rotate(${-(labelKnockouts!.rotation ?? 0)})\"\n transform-origin=\"${l.x} ${l.y}\"\n />\n `;\n })}`\n : nothing\n }\n ${\n centerCutoutRadius !== undefined\n ? svg`<circle cx=\"0\" cy=\"0\" r=\"${centerCutoutRadius}\" fill=\"black\"/>`\n : nothing\n }\n </mask>\n </defs>`\n : nothing\n }\n <g mask=${hasMask ? 'url(#crosshair-label-mask)' : nothing}>\n <line\n x1=\"-${radius}\"\n y1=\"0\"\n x2=\"${radius}\"\n y2=\"0\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n />\n <line\n x1=\"0\"\n y1=\"-${radius}\"\n x2=\"0\"\n y2=\"${radius}\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n />\n </g>\n `;\n }\n\n private renderBars(): SVGTemplateResult[] | typeof nothing {\n if (this.barAreas.length === 0) {\n return nothing;\n }\n return this.barAreas.map((bar, index) => {\n const startAngle = Math.min(bar.startAngle, bar.endAngle);\n const endAngle = Math.max(bar.startAngle, bar.endAngle);\n const arc = roundedArch({\n r: this._bandRadius(bar.innerRadius ?? RING3_RADIUS),\n R: this._bandRadius(bar.outerRadius ?? RING2_RADIUS),\n startAngle: startAngle,\n endAngle: endAngle,\n roundInsideCut: false,\n roundOutsideCut: false,\n });\n const barMaskR = (bar.outerRadius ?? RING2_RADIUS) + this._rOff + 40;\n // The mask is a sector to cut out the stroke on the start and end of the bar\n const mask = svg`<mask id=\"barMask-${index}\">\n <rect x=\"${-barMaskR}\" y=\"${-barMaskR}\" width=\"${barMaskR * 2}\" height=\"${barMaskR * 2}\" fill=\"black\" />\n <path d=${roundedArch({\n r: 1,\n R: barMaskR,\n startAngle: startAngle,\n endAngle: endAngle,\n roundInsideCut: false,\n roundOutsideCut: false,\n })} fill=\"white\" />\n </mask>`;\n // With roundBandCuts the bar is additionally clipped to the rounded\n // track shape, so its ends round off where they reach the sector cuts\n // (the design's track-mask model) while mid-track ends stay square.\n const trackMask =\n this.roundBandCuts && this.areas.length > 0\n ? svg`<mask id=\"barTrackMask-${index}\">\n <rect x=\"${-barMaskR}\" y=\"${-barMaskR}\" width=\"${barMaskR * 2}\" height=\"${barMaskR * 2}\" fill=\"black\" />\n ${this.areas.map(\n (area) =>\n svg`<path d=${roundedArch({\n startAngle: area.startAngle,\n endAngle: area.endAngle,\n R: this._bandRadius(bar.outerRadius ?? RING2_RADIUS),\n r: this._bandRadius(bar.innerRadius ?? RING3_RADIUS),\n roundOutsideCut: area.roundOutsideCut,\n roundInsideCut: area.roundInsideCut,\n })} fill=\"white\" stroke=\"white\" stroke-width=\"1\" vector-effect=\"non-scaling-stroke\" />`\n )}\n </mask>`\n : nothing;\n return svg`\n ${mask}\n ${trackMask}\n <g mask=${this.areas.length > 0 ? 'url(#cutMask)' : nothing}>\n <g mask=${trackMask !== nothing ? `url(#barTrackMask-${index})` : nothing}>\n <path\n d=${arc}\n fill=${bar.fillColor}\n stroke=${bar.fillColor}\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n mask=\"url(#barMask-${index})\"\n />\n </g>\n </g>\n `;\n });\n }\n\n private renderNeedles(): SVGTemplateResult[] | typeof nothing {\n if (this.needles.length === 0) {\n return nothing;\n }\n return this.needles.map((needle) => {\n return svg`\n <rect\n transform=\"rotate(${needle.angle})\"\n x=\"-4\" y=\"${-this._bandRadius(RING2_RADIUS)}\" width=\"8\" height=\"${needle.length ?? 48}\" rx=\"4\"\n fill=${needle.fillColor}\n stroke=${needle.strokeColor}\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n paint-order=\"stroke fill\"\n />\n `;\n });\n }\n\n private getScale({width, height}: {width: number; height: number}): number {\n const container = measureContainerPx(this);\n const scale = Math.min(container.width / width, container.height / height);\n // On first paint the element and its parent can both still be zero-sized, so\n // the scale is 0 (or non-finite). That value flows into `px / scale` label\n // math and yields ±Infinity coordinates the browser rejects. Fall back to a\n // 1:1 scale until a real size is available; the ResizeController re-renders\n // with the true scale once the element is laid out (issue #1032).\n if (!Number.isFinite(scale) || scale <= 0) {\n return 1;\n }\n return scale;\n }\n\n /**\n * Pixel width of the widest outside label, feeding the frame's label\n * reserve (issue #1021). Explicit `padding` is a hard geometry override\n * and disables the reserve, preserving legacy consumer output.\n */\n private getLabelWidthPx(): number {\n if (this.padding !== undefined || this.tickmarksInside) {\n return 0;\n }\n if (this.showLabels) {\n return NSWE_LABEL_WIDTH_PX;\n }\n return estimateLabelWidthPx(this.tickmarks.map((t) => t.text));\n }\n\n override render() {\n let width: number;\n let height: number;\n let viewBox: string;\n\n if (this.arcFrame) {\n this._rOff = this.arcFrame.radiusOffset;\n this._labelsHidden = false;\n this._ownFrame = undefined;\n width = this.arcFrame.width;\n height = this.arcFrame.height;\n viewBox = this.arcFrame.viewBox;\n } else {\n const frame = computeRadialFrame({\n basePadding: this.padding ?? 24,\n labelWidthPx: this.getLabelWidthPx(),\n clips: {\n top: this.clipTop,\n bottom: this.clipBottom,\n left: this.clipLeft,\n right: this.clipRight,\n },\n containerPx: measureContainerPx(this),\n faceDiameter: this.faceDiameter,\n zoomToFitArc: this.zoomToFitArc,\n areas: this.areas,\n innerRadius: this.innerRingRadius,\n });\n this._rOff = frame.radiusOffset;\n this._labelsHidden = frame.labelsHidden;\n this._ownFrame = frame;\n width = frame.width;\n height = frame.height;\n viewBox = frame.viewBox;\n }\n\n const rOff = this._rOff;\n const scale = this.getScale({width, height});\n const angleSetpoint = this.renderSetpoint();\n // Route through _bandRadius so inside labels track the (zoom-shifted) inner\n // band edge; the coupling is intentional (see _bandRadius INVARIANT).\n const textRadius = this.tickmarksInside\n ? this._bandRadius(this.innerRingRadius)\n : this._bandRadius(OUTER_RING_RADIUS);\n const maxDigits = Math.max(\n ...this.tickmarks.map((t) => t.text?.length ?? 0)\n );\n const tickmarks = this.tickmarks.map((t) =>\n tickmark(t.angle, {\n size: t.type,\n style: this.tickmarkStyle,\n scale,\n text: this.showLabels || this._labelsHidden ? undefined : t.text,\n inside: this.tickmarksInside,\n textRadius,\n rotation: this.rotation,\n maxDigits,\n color: t.color,\n radiusOffset: rOff,\n endLabelsMaxMin: this.endLabelsMaxMin,\n })\n );\n const advices = this.advices\n ? this.advices.map((a) => renderAdvice(a, rOff))\n : nothing;\n\n // Compute label positions once – used for both rendering and crosshair\n // knockout. NSWE labels are px-fixed outside decor, so they follow the\n // same labelsHidden degradation as tick label texts. The north arrow is\n // exempt: below the small-scale threshold it renders as a compact\n // triangle at the ring that scales with the face, so it stays visible\n // on the smallest faces without clipping.\n const showNsweLabels = this.showLabels && !this._labelsHidden;\n const showNorthArrow = this.northArrow;\n const insideLabels = this.tickmarksInside && showNsweLabels;\n const includeNorth = !this.northArrow;\n const labelPositions = showNsweLabels\n ? getLabelPositions({\n scale,\n inside: this.tickmarksInside,\n innerRadius: this.innerRingRadius + rOff,\n includeNorth,\n insideFlush: this.insideLabelsFlush,\n })\n : undefined;\n\n const labels = labelPositions\n ? renderLabels({\n scale,\n rotation: this.rotation,\n inside: this.tickmarksInside,\n innerRadius: this.innerRingRadius + rOff,\n includeNorth,\n insideFlush: this.insideLabelsFlush,\n })\n : nothing;\n const northArrowEl = showNorthArrow\n ? this.northMarker\n ? renderNorthMarker({scale, rotation: this.rotation})\n : renderNorthArrow({\n scale,\n rotation: this.rotation,\n inside: this.northArrowInside ?? this.tickmarksInside,\n })\n : nothing;\n const wind =\n this.windKnots != null && this.windFromDirectionDeg != null\n ? svg`<g transform=\"scale(${this.scaleWindIcon})\">${renderWind({\n windKnots: this.windKnots,\n fromDirectionDeg: this.windFromDirectionDeg,\n radius: this.windSymbolRadius ?? WIND_ICON_OUTSIDE_RADIUS,\n color: this.windColor,\n })}</g>`\n : nothing;\n const current =\n this.current != null && this.currentFromDirectionDeg != null\n ? this.currentIconCentered\n ? renderCurrentCentered({\n current: this.current,\n fromDirectionDeg: this.currentFromDirectionDeg,\n scale: this.scaleCurrentIcon,\n color: this.currentColor,\n })\n : renderCurrent({\n current: this.current,\n fromDirectionDeg: this.currentFromDirectionDeg,\n radius: this.currentSymbolRadius ?? CURRENT_ICON_OUTSIDE_RADIUS,\n color: this.currentColor,\n })\n : nothing;\n return html`\n <svg\n width=\"100%\"\n height=\"100%\"\n viewBox=${viewBox}\n style=\"--scale: ${scale}\"\n transform=\"rotate(${this.rotation ?? 0})\"\n >\n ${this.watchCircle()} ${this.renderBars()}\n ${this.crosshairEnabled\n ? this.renderCrosshair(\n OUTER_RING_RADIUS + rOff,\n insideLabels && labelPositions\n ? {\n positions: labelPositions,\n rotation: this.rotation,\n scale,\n innerRingRadius: this.innerRingRadius + rOff,\n }\n : undefined,\n this.crosshairCenterCutout\n ? this.innerRingRadius + rOff\n : undefined\n )\n : nothing}\n ${northArrowEl} ${this.renderStarboardPortIndicator()} ${current}\n ${this._renderTickFadeDefs()} ${wind}\n ${this.tickFadeAngle > 0 && this.areas.length > 0\n ? svg`<g mask=\"url(#tickFadeMask)\">${tickmarks}</g>`\n : tickmarks}\n ${this.areas.length > 0\n ? svg`<g clip-path=\"url(#rot-arc-clip)\">${this.renderRot()}</g>`\n : this.renderRot()}\n ${advices} ${angleSetpoint}\n ${this.tickFadeAngle > 0 && this.areas.length > 0\n ? svg`<g mask=\"url(#tickFadeMask)\">${labels}</g>`\n : labels}\n ${this.renderVesselImage()} ${this.renderNeedles()}\n </svg>\n `;\n }\n\n private getRotColors(): {\n dotColor: string;\n barBgColor: string;\n } {\n const p = this.rotPriority ?? this.priority;\n const isEnhanced = p === Priority.enhanced;\n\n if (this.rotPortStarboard) {\n // For bar type, derive direction from bar angles (visual direction);\n // for dots, use spinner RPM.\n let direction: number;\n if (this.rotType === RotType.bar) {\n const cwSpan =\n (((this.rotEndAngle - this.rotStartAngle) % 360) + 360) % 360;\n direction = cwSpan <= 180 ? cwSpan : cwSpan - 360;\n } else {\n direction = this._effectiveRpm;\n }\n\n if (direction > 0) {\n return {\n dotColor: 'var(--instrument-starboard-secondary-color)',\n barBgColor: 'var(--instrument-starboard-primary-color)',\n };\n }\n if (direction < 0) {\n return {\n dotColor: 'var(--instrument-port-secondary-color)',\n barBgColor: 'var(--instrument-port-primary-color)',\n };\n }\n }\n\n return {\n dotColor: isEnhanced\n ? 'var(--instrument-enhanced-tertiary-color)'\n : 'var(--instrument-regular-tertiary-color)',\n barBgColor: isEnhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)',\n };\n }\n\n private renderRot(): SVGTemplateResult | typeof nothing {\n if (!this.rotType) return nothing;\n\n const {dotColor, barBgColor} = this.getRotColors();\n const rOff = this._rOff;\n\n if (this.rotType === RotType.bar) {\n const angularDelta = shortestAngularDeltaDeg(\n this.rotStartAngle,\n this.rotEndAngle\n );\n const threshold = rotBarThresholdAngle(this.rotPosition, rOff);\n const zeroDb = Number.isFinite(this.rotAtZeroDeadband)\n ? this.rotAtZeroDeadband\n : ROT_ZERO_DEADBAND_DEG;\n\n if (angularDelta < Math.max(zeroDb, threshold)) {\n return renderRotZeroPill(\n barBgColor,\n this.rotStartAngle,\n this.rotPosition,\n rOff\n );\n }\n\n return svg`\n ${renderRotBarStatic({\n startAngle: this.rotStartAngle,\n endAngle: this.rotEndAngle,\n barColor: barBgColor,\n position: this.rotPosition,\n maskId: 'rot-bar-mask',\n radiusOffset: rOff,\n })}\n ${svg`<g clip-path=\"url(#rot-bar-mask)\">\n <g id=\"rot-spinner\">\n ${renderRotBarDots(dotColor, this.rotPosition, rOff)}\n </g>\n </g>`}\n `;\n }\n\n const p = this.rotPriority ?? this.priority;\n const isEnhanced = p === Priority.enhanced;\n let dotsColor: string = isEnhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n if (this.rotPortStarboard) {\n if (this._effectiveRpm > 0) {\n dotsColor = 'var(--instrument-starboard-secondary-color)';\n } else if (this._effectiveRpm < 0) {\n dotsColor = 'var(--instrument-port-secondary-color)';\n }\n }\n return svg`\n <g id=\"rot-spinner\">\n ${renderRotDots(dotsColor, this.rotPosition, rOff)}\n </g>\n `;\n }\n\n private renderSetpoint(): SVGTemplateResult | typeof nothing {\n if (this.angleSetpoint === undefined) {\n return nothing;\n }\n\n const derived = deriveRadialSetpointConfig({\n state: this.state,\n priority: this.priority,\n atSetpoint: this.atAngleSetpoint,\n angleSetpoint: this.angleSetpoint,\n setpointAtZeroDeadband: this.angleSetpointAtZeroDeadband,\n newAngleSetpoint: this.newAngleSetpoint,\n touching: this.touching,\n setpointOverride: this.setpointOverride,\n });\n\n const {visualState, colorMode, disabled, hasNewSetpoint} = derived;\n\n const outwardOffset = getSetpointOutwardOffset(visualState);\n const radius =\n RADIAL_SETPOINT_RADIUS +\n this._rOff +\n outwardOffset -\n RADIAL_SETPOINT_INWARD_ADJUST;\n\n // Render original setpoint marker (dimmed when newAngleSetpoint is active)\n const opacity = hasNewSetpoint ? 0.75 : 1;\n const originalMarker = drawSetpointMarker({\n visualState,\n colorMode,\n disabled,\n id: this._setpointId,\n });\n\n const animate = this.animateSetpoint;\n const hasDeparting = this._departingNewAngleSetpoint !== undefined;\n\n // Compute CSS-safe accumulated angle so transitions always take the short path\n const rawAngle = this.angleSetpoint + 90;\n if (!this._setpointCssAngleInit) {\n // First render: set angle without transition\n this._setpointCssAngle = rawAngle;\n this._setpointCssAngleInit = true;\n } else {\n this._setpointCssAngle = cssSafeAngle(this._setpointCssAngle, rawAngle);\n }\n\n // Use CSS style transform when animating for smooth transition\n const originalSetpoint = animate\n ? svg`\n <g style=\"transform: rotate(${this._setpointCssAngle}deg) translateX(${-radius}px) rotate(270deg); opacity: ${opacity}; transition: transform var(${SETPOINT_ANIMATION_CSS_VAR}, ${SETPOINT_ANIMATION_DURATION_DEFAULT}) ease-out, opacity var(${SETPOINT_ANIMATION_CSS_VAR}, ${SETPOINT_ANIMATION_DURATION_DEFAULT}) ease-out;\">\n ${originalMarker}\n </g>\n `\n : svg`\n <g transform=\"rotate(${this.angleSetpoint + 90}) translate(${-radius}, 0) rotate(270)\" opacity=\"${opacity}\">\n ${originalMarker}\n </g>\n `;\n\n // Render newAngleSetpoint in focus state (always on top)\n // OR render departing newAngleSetpoint during confirm fade-out\n if (hasNewSetpoint || hasDeparting) {\n const isActive = hasNewSetpoint;\n const newAngle = isActive\n ? this.newAngleSetpoint!\n : this._departingNewAngleSetpoint!;\n const targetOpacity = isActive ? 1 : 0;\n\n const focusOutwardOffset = getSetpointOutwardOffset(\n SetpointVisualState.focus\n );\n const focusRadius =\n RADIAL_SETPOINT_RADIUS +\n this._rOff +\n focusOutwardOffset -\n RADIAL_SETPOINT_INWARD_ADJUST;\n\n const newMarker = drawSetpointMarker({\n visualState: SetpointVisualState.focus,\n colorMode,\n disabled: false, // newSetpoint is never disabled\n id: this._newSetpointId,\n });\n\n if (animate) {\n const duration = `var(${SETPOINT_ANIMATION_CSS_VAR}, ${SETPOINT_ANIMATION_DURATION_DEFAULT})`;\n return svg`\n ${originalSetpoint}\n <g style=\"transform: rotate(${newAngle + 90}deg) translateX(${-focusRadius}px) rotate(270deg); opacity: ${targetOpacity}; transition: opacity ${duration} ease-out;\">\n ${newMarker}\n </g>\n `;\n }\n\n return svg`\n ${originalSetpoint}\n <g transform=\"rotate(${newAngle + 90}) translate(${-focusRadius}, 0) rotate(270)\" opacity=\"${targetOpacity}\">\n ${newMarker}\n </g>\n `;\n }\n\n return originalSetpoint;\n }\n\n private renderVesselImage(): SVGTemplateResult[] | typeof nothing {\n if (this.vessels.length === 0) {\n return nothing;\n }\n\n return this.vessels.map((v) => {\n let size;\n switch (v.size) {\n case VesselImageSize.large:\n size = 224;\n break;\n case VesselImageSize.medium:\n size = 160;\n break;\n default:\n size = 100;\n }\n\n const scale = size / 160;\n return svg`<g style=\"transform: ${v.transform} scale(${scale}) translate(-80px, -80px) \">${vesselImages[v.vesselImage]}</g>`;\n });\n }\n\n private renderStarboardPortIndicator(): SVGTemplateResult[] | typeof nothing {\n if (!this.starboardPortIndicator) {\n return nothing;\n }\n return [\n adviceMask(\n 0,\n 180,\n 'var(--instrument-starboard-secondary-color)',\n 'var(--instrument-starboard-secondary-color)'\n ),\n adviceMask(\n 180,\n 360,\n 'var(--instrument-port-secondary-color)',\n 'var(--instrument-port-secondary-color)'\n ),\n ] as SVGTemplateResult[];\n }\n\n static override styles = unsafeCSS(compentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-watch': ObcWatch;\n }\n}\n"],"names":["WatchCircleType"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEO,IAAK,oCAAAA,qBAAL;AACLA,mBAAA,QAAA,IAAS;AACTA,mBAAA,QAAA,IAAS;AACTA,mBAAA,YAAA,IAAa;AACbA,mBAAA,QAAA,IAAS;AAJC,SAAAA;AAAA,GAAA,mBAAA,CAAA,CAAA;AAsCL,MAAM,oBAAoB,MAAM;AACvC,MAAM,eAAe,MAAM;AAC3B,MAAM,eAAe,MAAM;AAC3B,MAAM,gBAAgB,MAAM;AAC5B,MAAM,eAAe,MAAM;AAEpB,SAAS,mBAAmB,MAA+B;AAChE,UAAQ,MAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,YAAM,IAAI,MAAM,4BAA4B,IAAc,EAAE;AAAA,EAAA;AAElE;AAEA,MAAM,gCAAgC;AAStC,MAAM,8BAA8B,oBAAoB;AACxD,MAAM,2BACJ,8BAA8B;AAqDzB,IAAM,WAAN,cAAuB,WAAW;AAAA,EAAlC,cAAA;AAAA,UAAA,GAAA,SAAA;AACL,SAAQ,cAAc,kBAAkB,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC9E,SAAQ,iBAAiB,sBAAsB,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAG3D,SAAA,QAAyB,gBAAgB;AAEzC,SAAA,WAAqB,SAAS;AAC9B,SAAA,kBACxB;AASyB,SAAA,sBAA+B;AAC/B,SAAA,aAAsB;AAQtB,SAAA,cAAuB;AAMvB,SAAA,kBAA2B;AAE5B,SAAA,8BAAsC;AAErC,SAAA,mBAA4B;AAC5B,SAAA,WAAoB;AAEpB,SAAA,kBAA2B;AAUtD,SAAQ,oBAA4B;AAGpC,SAAQ,wBAAwB;AAgBW,SAAA,QAAqB,CAAA;AACrB,SAAA,WAA2B,CAAA;AAO3C,SAAA,gBAAyB;AACT,SAAA,UAAyB,CAAA;AACzB,SAAA,YAAwB,CAAA;AACxC,SAAA,kBAA2B;AAC5B,SAAA,gBACxB,cAAc;AAC2B,SAAA,UAA4B,CAAA;AAC5C,SAAA,mBAA4B;AAM5B,SAAA,wBAAiC;AACjC,SAAA,aAAsB;AAOtB,SAAA,oBAA6B;AACb,SAAA,UAAyB,CAAA;AAC1C,SAAA,YAA2B;AAC3B,SAAA,uBAAsC;AACtC,SAAA,mBAAkC;AAElC,SAAA,UAAyB;AACzB,SAAA,0BAAyC;AACzC,SAAA,sBAAqC;AAOpC,SAAA,sBAA+B;AAKhC,SAAA,mBAA2B;AAC1B,SAAA,yBAAkC;AAEnC,SAAA,UAAkB;AAElB,SAAA,aAAqB;AAErB,SAAA,WAAmB;AAEnB,SAAA,YAAoB;AAOnB,SAAA,kBAA2B;AAC5B,SAAA,gBAAwB;AAEvB,SAAA,eAAwB;AAGzB,SAAA,gBAAwB;AAKxB,SAAA,cAA2B,YAAY;AAEvC,SAAA,gBAAwB;AAExB,SAAA,cAAsB;AAGrB,SAAA,mBAA4B;AAC7B,SAAA,oBAA4B;AAI5B,SAAA,wBAAgC;AAc1D,SAAQ,4BAA4B;AAmBpC,SAAQ,oBAAoB,IAAI,iBAAiB,MAAM,CAAA,CAAE;AAqEzD,SAAQ,QAAQ;AAMhB,SAAQ,gBAAgB;AAMxB,SAAQ,kBAAkB;AAAA,EAAA;AAAA,EA3G1B,IAAI,mBAAmB,OAAe;AACpC,SAAK,4BAA4B;AAAA,EACnC;AAAA;AAAA,EAEA,IAAI,qBAAqB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,IAAY,gBAAwB;AAClC,QAAI,KAAK,8BAA8B,MAAM;AAC3C,aACG,KAAK,6BAA6B,MAAO,KAAK;AAAA,IAEnD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAIS,aAAa,SAA+B;AACnD,UAAM,aAAa,OAAO;AAC1B,oBAAgB,KAAK,mBAAmB,KAAK,UAAU;AAAA,EACzD;AAAA,EAES,WAAW,SAA+B;AACjD,UAAM,WAAW,OAAO;AAIxB,QACE,KAAK,mBACJ,QAAQ,IAAI,4BAA4B,KACvC,QAAQ,IAAI,uBAAuB,KACnC,QAAQ,IAAI,oBAAoB,IAClC;AACA,WAAK,eAAe,qBAAqB,KAAK;AAAA,IAChD;AAGA,QAAI,QAAQ,IAAI,kBAAkB,KAAK,KAAK,iBAAiB;AAC3D,YAAM,OAAO,QAAQ,IAAI,kBAAkB;AAC3C,UAAI,SAAS,UAAa,KAAK,qBAAqB,QAAW;AAC7D,aAAK,6BAA6B;AAClC,qBAAa,KAAK,eAAe;AACjC,cAAM,WAAW,+BAA+B,IAAI;AACpD,aAAK,kBAAkB,WAAW,MAAM;AACtC,eAAK,6BAA6B;AAAA,QACpC,GAAG,QAAQ;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAAA,EAES,uBAA6B;AACpC,UAAM,qBAAA;AACN,iBAAa,KAAK,eAAe;AACjC,SAAK,iBAAiB,qBAAqB,MAAM,KAAK,cAAc;AAAA,EACtE;AAAA,EAES,QAAQ,SAA+B;AAC9C,UAAM,QAAQ,OAAO;AACrB,SAAK,kBAAkB;AAAA,MACrB;AAAA,MACA,KAAK,WAAW,SAAY,KAAK;AAAA,MACjC,KAAK;AAAA,IAAA;AAEP,UAAM,KAAK,KAAK,UACZ,KAAK,WAAW,cAAc,cAAc,IAC5C;AACJ,QAAI,CAAC,IAAI;AACP,WAAK,iBAAiB,qBAAqB,MAAM,KAAK,cAAc;AACpE;AAAA,IACF;AACA,QAAI,CAAC,KAAK,kBAAkB,KAAK,eAAe,OAAO,IAAI;AACzD,WAAK,iBAAiB,qBAAqB,MAAM,KAAK,cAAc;AACpE,WAAK,iBAAiB,IAAI;AAAA,QACxB;AAAA,QACA;AAAA,QACA,KAAK;AAAA,MAAA;AAAA,IAET;AAAA,EACF;AAAA,EAEA,IAAY,kBAA0B;AACpC,WAAO,mBAAmB,KAAK,eAAe;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBQ,YAAY,MAAsB;AACxC,WAAO,OAAO,KAAK;AAAA,EACrB;AAAA,EAEQ,cAAuD;AAC7D,UAAM,QAAQ,CAAA;AAOd,UAAM,WACJ,KAAK,uBAAuB,KAAK,UAAU,gBAAgB,MACvD;AAAA;AAAA;AAAA;AAAA,eAIK,KAAK,YAAY,iBAAiB,CAAC;AAAA;AAAA;AAAA,cAIxC;AACN,UAAM,cACJ,KAAK,uBAAuB,KAAK,MAAM,SAAS,IAC5C;AAAA;AAAA;AAAA;AAAA,eAIK,KAAK,YAAY,iBAAiB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,cAMxC;AACN,QAAI,KAAK,UAAU,gBAAgB,KAAK;AACtC,YAAM,KAAK;AAAA;AAAA;AAAA;AAAA,eAIF,KAAK,YAAY,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA,WAIzB;AAEL,UAAI,KAAK,oBAAoB,UAAwB;AACnD,cAAM,KAAK,KAAK,YAAY,YAAY;AACxC,cAAM,KAAK,KAAK;AAAA,UACd,KAAK,oBAAoB,eACrB,gBACA;AAAA,QAAA;AAEN,cAAM,KAAK,KAAK,MAAM;AACtB,cAAM,cAAc,KAAK;AACzB,YAAI,KAAK,iBAAiB,KAAK,MAAM,SAAS,GAAG;AAC/C,gBAAM;AAAA,YACJ,GAAG,KAAK,MAAM;AAAA,cACZ,CAAC,SACC,cAAc,YAAY;AAAA,gBACxB,YAAY,KAAK;AAAA,gBACjB,UAAU,KAAK;AAAA,gBACf,GAAG;AAAA,gBACH,GAAG;AAAA,gBACH,iBAAiB,KAAK;AAAA,gBACtB,gBAAgB,KAAK;AAAA,cAAA,CACtB,CAAC;AAAA,YAAA;AAAA,UACN;AAAA,QAEJ,OAAO;AACL,gBAAM;AAAA,YACJ;AAAA,sCAC0B,CAAC,kEAAkE,WAAW;AAAA,sCAC9E,EAAE;AAAA,sCACF,EAAE;AAAA;AAAA,UAAA;AAAA,QAGhC;AAAA,MACF;AACA,UAAI,KAAK,oBAAoB,UAAwB;AACnD,cAAM,KAAK,KAAK,YAAY,YAAY;AACxC,cAAM,KAAK,KAAK,YAAY,YAAY;AACxC,cAAM,KAAK,KAAK,MAAM;AACtB,cAAM,cAAc,KAAK;AACzB,cAAM;AAAA,UACJ,8BAA8B,CAAC,gEAAgE,WAAW;AAAA,QAAA;AAAA,MAE9G;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,IAAI,KAAK,oBAAoB,KAAK,QAAQ,EAAE;AAClE,QAAI,SAAS;AACb,QAAI,KAAK,MAAM,SAAS,GAAG;AACzB,YAAM,QAAQ,KAAK,MAAM,IAAI,CAAC,SAAS;AACrC,cAAM,UAAU,YAAY;AAAA,UAC1B,YAAY,KAAK;AAAA,UACjB,UAAU,KAAK;AAAA,UACf,GAAG,KAAK,YAAY,iBAAiB;AAAA,UACrC,GAAG,KAAK,YAAY,KAAK,eAAe;AAAA,UACxC,iBAAiB,KAAK;AAAA,UACtB,gBAAgB,KAAK;AAAA,QAAA,CACtB;AACD,eAAO;AAAA,MACT,CAAC;AACD,YAAM,OAAO;AAAA,mBACA,CAAC,QAAQ,QAAQ,CAAC,QAAQ,YAAY,WAAW,CAAC,aAAa,WAAW,CAAC;AAAA,UACpF,MAAM,IAAI,CAAC,SAAS,cAAc,IAAI,oFAAoF,CAAC;AAAA;AAG/H,YAAM,UAAU,kCAAkC,KAAK,MAAM;AAAA,QAC3D,CAAC,SACC,cAAc,YAAY;AAAA,UACxB,YAAY,KAAK;AAAA,UACjB,UAAU,KAAK;AAAA,UACf,GAAG,oBAAoB,KAAK,QAAQ;AAAA,UACpC,GAAG;AAAA,UACH,iBAAiB,KAAK;AAAA,UACtB,gBAAgB,KAAK;AAAA,QAAA,CACtB,CAAC;AAAA,MAAA,CACL;AACD,eAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA,GAAI,WAAW,CAAC,QAAQ,IAAI,CAAA;AAAA,QAC5B,8BAA8B,KAAK;AAAA,QACnC,GAAI,cAAc,CAAC,WAAW,IAAI,CAAA;AAAA,MAAC;AAErC,UAAI,CAAC,KAAK,qBAAqB;AAC7B,cAAM,QAAQ,CAAC,SAAS;AACtB,iBAAO;AAAA,YACL,cAAc,IAAI;AAAA,UAAA;AAAA,QAEtB,CAAC;AAAA,MACH;AAAA,IACF,OAAO;AACL,UAAI,UAAU;AACZ,iBAAS,CAAC,UAAU,GAAG,KAAK;AAAA,MAC9B;AACA,UAAI,KAAK,UAAU,gBAAgB,KAAK;AACtC,eAAO;AAAA,UACL,OAAO,aAAa;AAAA,YAClB,QAAQ,KAAK,YAAY,iBAAiB;AAAA,YAC1C,aAAa;AAAA,YACb,aAAa;AAAA,YACb,gBAAgB;AAAA,YAChB,WAAW;AAAA,UAAA,CACZ;AAAA,QAAA;AAGH,eAAO,KAAK;AAAA,YACR,OAAO,aAAa;AAAA,UACpB,QAAQ,KAAK,YAAY,KAAK,eAAe;AAAA,UAC7C,aAAa;AAAA,UACb,aAAa;AAAA,UACb,gBAAgB;AAAA,UAChB,WAAW;AAAA,QAAA,CACZ,CAAC;AAAA,SACH;AAAA,MACH,OAAO;AACL,eAAO,KAAK;AAAA,YACR,OAAO,aAAa;AAAA,UACpB,QAAQ,KAAK,YAAY,iBAAiB;AAAA,UAC1C,aAAa;AAAA,UACb,aAAa;AAAA,UACb,gBAAgB;AAAA,UAChB,WAAW;AAAA,QAAA,CACZ,CAAC;AAAA,SACH;AAAA,MACH;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,sBAA0D;AAChE,QAAI,KAAK,iBAAiB,KAAK,KAAK,MAAM,WAAW,EAAG,QAAO;AAC/D,UAAM,OAAO,KAAK,MAAM,CAAC;AACzB,UAAM,UAAU,KAAK,WAAW,KAAK;AACrC,UAAM,OAAO,KAAK,IAAI,KAAK,eAAe,UAAU,CAAC;AACrD,QAAI,OAAO,IAAK,QAAO;AAEvB,UAAM,EAAC,YAAY,SAAA,IAAY;AAC/B,UAAM,IAAI,oBAAoB,KAAK,QAAQ;AAC3C,UAAM,QAAQ,CAAC,QAAiB,MAAM,KAAK,KAAM;AACjD,UAAM,KAAK,CAAC,QAAgB,IAAI,KAAK,IAAI,MAAM,GAAG,CAAC;AACnD,UAAM,KAAK,CAAC,QAAgB,CAAC,IAAI,KAAK,IAAI,MAAM,GAAG,CAAC;AAEpD,UAAM,WAAW,CAAC,GAAW,MAAsB;AACjD,YAAM,KAAK,GAAG,CAAC,GACb,KAAK,GAAG,CAAC;AACX,YAAM,KAAK,GAAG,CAAC,GACb,KAAK,GAAG,CAAC;AACX,YAAM,WAAW,IAAI,IAAI,MAAM,IAAI;AACnC,aAAO,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,MAAM,EAAE,IAAI,EAAE;AAAA,IACpE;AAEA,UAAM,MACH,KAAK,YAAY,iBAAiB,IACjC,KAAK,YAAY,KAAK,eAAe,KACvC;AACF,UAAM,KAAK,CAAC,QAAgB,KAAK,KAAK,IAAI,MAAM,GAAG,CAAC;AACpD,UAAM,KAAK,CAAC,QAAgB,CAAC,KAAK,KAAK,IAAI,MAAM,GAAG,CAAC;AAErD,WAAO;AAAA;AAAA;AAAA,gBAGK,GAAG,UAAU,CAAC,SAAS,GAAG,UAAU,CAAC;AAAA,gBACrC,GAAG,aAAa,IAAI,CAAC,SAAS,GAAG,aAAa,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,gBAKnD,GAAG,WAAW,IAAI,CAAC,SAAS,GAAG,WAAW,IAAI,CAAC;AAAA,gBAC/C,GAAG,QAAQ,CAAC,SAAS,GAAG,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,eAKlC,CAAC,CAAC,QAAQ,CAAC,CAAC,YAAY,IAAI,CAAC,aAAa,IAAI,CAAC;AAAA,qBACzC,SAAS,aAAa,MAAM,WAAW,IAAI,CAAC;AAAA,qBAC5C,SAAS,YAAY,aAAa,IAAI,CAAC;AAAA,qBACvC,SAAS,WAAW,MAAM,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA,EAItD;AAAA,EAEQ,gBACN,QACA,gBAOA,oBACmB;AACnB,UAAM,oBACJ,CAAC,CAAC,kBAAkB,eAAe,UAAU,SAAS;AACxD,UAAM,UAAU,qBAAqB,uBAAuB;AAI5D,UAAM,cAAc,oBAChB,KAAK;AAAA,MACH,GAAG,eAAgB,UAAU;AAAA,QAAI,CAAC,MAChC,KAAK,IAAI,EAAE,MAAM,IAAI,EAAE,IAAI,EAAE,CAAC;AAAA,MAAA;AAAA,IAChC,IAEF;AAGJ,UAAM,aAAa,oBAAoB,IAAI,eAAgB,QAAQ;AAEnE,WAAO;AAAA,QAEH,UACI;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKM,MAAM,SAAS,MAAM;AAAA,qBAClB,SAAS,CAAC,aAAa,SAAS,CAAC;AAAA;AAAA,wBAE9B,MAAM,SAAS,MAAM,YAAY,SAAS,CAAC,aAAa,SAAS,CAAC;AAAA,cAE5E,oBACI;AAAA;AAAA,uCAEqB,eAAgB,eAAe;AAAA,uCAC/B,cAAc,UAAU;AAAA;AAAA,cAEjD,eAAgB,UAAU,IAAI,CAAC,MAAM;AACrC,YAAM,WAAW,KAAK,eAAgB;AACtC,YAAM,MAAM,IAAI,eAAgB;AAChC,YAAM,OAAO,WAAW,MAAM;AAC9B,aAAO;AAAA;AAAA,uBAEE,EAAE,IAAI,OAAO,CAAC,QAAQ,EAAE,IAAI,OAAO,CAAC;AAAA,2BAChC,IAAI,aAAa,IAAI;AAAA;AAAA,sCAEV,EAAE,eAAgB,YAAY,EAAE;AAAA,sCAChC,EAAE,CAAC,IAAI,EAAE,CAAC;AAAA;AAAA;AAAA,IAGpC,CAAC,CAAC,KACI,OACN;AAAA,cAEE,uBAAuB,SACnB,+BAA+B,kBAAkB,qBACjD,OACN;AAAA;AAAA,mBAGA,OACN;AAAA,gBACU,UAAU,+BAA+B,OAAO;AAAA;AAAA,iBAE/C,MAAM;AAAA;AAAA,gBAEP,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAQL,MAAM;AAAA;AAAA,gBAEP,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB;AAAA,EAEQ,aAAmD;AACzD,QAAI,KAAK,SAAS,WAAW,GAAG;AAC9B,aAAO;AAAA,IACT;AACA,WAAO,KAAK,SAAS,IAAI,CAAC,KAAK,UAAU;AACvC,YAAM,aAAa,KAAK,IAAI,IAAI,YAAY,IAAI,QAAQ;AACxD,YAAM,WAAW,KAAK,IAAI,IAAI,YAAY,IAAI,QAAQ;AACtD,YAAM,MAAM,YAAY;AAAA,QACtB,GAAG,KAAK,YAAY,IAAI,eAAe,YAAY;AAAA,QACnD,GAAG,KAAK,YAAY,IAAI,eAAe,YAAY;AAAA,QACnD;AAAA,QACA;AAAA,QACA,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,MAAA,CAClB;AACD,YAAM,YAAY,IAAI,eAAe,gBAAgB,KAAK,QAAQ;AAElE,YAAM,OAAO,wBAAwB,KAAK;AAAA,mBAC7B,CAAC,QAAQ,QAAQ,CAAC,QAAQ,YAAY,WAAW,CAAC,aAAa,WAAW,CAAC;AAAA,kBAC5E,YAAY;AAAA,QACpB,GAAG;AAAA,QACH,GAAG;AAAA,QACH;AAAA,QACA;AAAA,QACA,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,MAAA,CAClB,CAAC;AAAA;AAKJ,YAAM,YACJ,KAAK,iBAAiB,KAAK,MAAM,SAAS,IACtC,6BAA6B,KAAK;AAAA,yBACrB,CAAC,QAAQ,QAAQ,CAAC,QAAQ,YAAY,WAAW,CAAC,aAAa,WAAW,CAAC;AAAA,gBACpF,KAAK,MAAM;AAAA,QACX,CAAC,SACC,cAAc,YAAY;AAAA,UACxB,YAAY,KAAK;AAAA,UACjB,UAAU,KAAK;AAAA,UACf,GAAG,KAAK,YAAY,IAAI,eAAe,YAAY;AAAA,UACnD,GAAG,KAAK,YAAY,IAAI,eAAe,YAAY;AAAA,UACnD,iBAAiB,KAAK;AAAA,UACtB,gBAAgB,KAAK;AAAA,QAAA,CACtB,CAAC;AAAA,MAAA,CACL;AAAA,uBAEH;AACN,aAAO;AAAA,UACH,IAAI;AAAA,UACJ,SAAS;AAAA,kBACD,KAAK,MAAM,SAAS,IAAI,kBAAkB,OAAO;AAAA,kBACjD,cAAc,UAAU,qBAAqB,KAAK,MAAM,OAAO;AAAA;AAAA,cAEnE,GAAG;AAAA,iBACA,IAAI,SAAS;AAAA,mBACX,IAAI,SAAS;AAAA;AAAA;AAAA,+BAGD,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,IAKhC,CAAC;AAAA,EACH;AAAA,EAEQ,gBAAsD;AAC5D,QAAI,KAAK,QAAQ,WAAW,GAAG;AAC7B,aAAO;AAAA,IACT;AACA,WAAO,KAAK,QAAQ,IAAI,CAAC,WAAW;AAClC,aAAO;AAAA;AAAA,8BAEiB,OAAO,KAAK;AAAA,sBACpB,CAAC,KAAK,YAAY,YAAY,CAAC,uBAAuB,OAAO,UAAU,EAAE;AAAA,iBAC9E,OAAO,SAAS;AAAA,mBACd,OAAO,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMjC,CAAC;AAAA,EACH;AAAA,EAEQ,SAAS,EAAC,OAAO,UAAkD;AACzE,UAAM,YAAY,mBAAmB,IAAI;AACzC,UAAM,QAAQ,KAAK,IAAI,UAAU,QAAQ,OAAO,UAAU,SAAS,MAAM;AAMzE,QAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GAAG;AACzC,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,kBAA0B;AAChC,QAAI,KAAK,YAAY,UAAa,KAAK,iBAAiB;AACtD,aAAO;AAAA,IACT;AACA,QAAI,KAAK,YAAY;AACnB,aAAO;AAAA,IACT;AACA,WAAO,qBAAqB,KAAK,UAAU,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAAA,EAC/D;AAAA,EAES,SAAS;AAChB,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,QAAI,KAAK,UAAU;AACjB,WAAK,QAAQ,KAAK,SAAS;AAC3B,WAAK,gBAAgB;AACrB,WAAK,YAAY;AACjB,cAAQ,KAAK,SAAS;AACtB,eAAS,KAAK,SAAS;AACvB,gBAAU,KAAK,SAAS;AAAA,IAC1B,OAAO;AACL,YAAM,QAAQ,mBAAmB;AAAA,QAC/B,aAAa,KAAK,WAAW;AAAA,QAC7B,cAAc,KAAK,gBAAA;AAAA,QACnB,OAAO;AAAA,UACL,KAAK,KAAK;AAAA,UACV,QAAQ,KAAK;AAAA,UACb,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,QAAA;AAAA,QAEd,aAAa,mBAAmB,IAAI;AAAA,QACpC,cAAc,KAAK;AAAA,QACnB,cAAc,KAAK;AAAA,QACnB,OAAO,KAAK;AAAA,QACZ,aAAa,KAAK;AAAA,MAAA,CACnB;AACD,WAAK,QAAQ,MAAM;AACnB,WAAK,gBAAgB,MAAM;AAC3B,WAAK,YAAY;AACjB,cAAQ,MAAM;AACd,eAAS,MAAM;AACf,gBAAU,MAAM;AAAA,IAClB;AAEA,UAAM,OAAO,KAAK;AAClB,UAAM,QAAQ,KAAK,SAAS,EAAC,OAAO,QAAO;AAC3C,UAAM,gBAAgB,KAAK,eAAA;AAG3B,UAAM,aAAa,KAAK,kBACpB,KAAK,YAAY,KAAK,eAAe,IACrC,KAAK,YAAY,iBAAiB;AACtC,UAAM,YAAY,KAAK;AAAA,MACrB,GAAG,KAAK,UAAU,IAAI,CAAC,MAAM,EAAE,MAAM,UAAU,CAAC;AAAA,IAAA;AAElD,UAAM,YAAY,KAAK,UAAU;AAAA,MAAI,CAAC,MACpC,SAAS,EAAE,OAAO;AAAA,QAChB,MAAM,EAAE;AAAA,QACR,OAAO,KAAK;AAAA,QACZ;AAAA,QACA,MAAM,KAAK,cAAc,KAAK,gBAAgB,SAAY,EAAE;AAAA,QAC5D,QAAQ,KAAK;AAAA,QACb;AAAA,QACA,UAAU,KAAK;AAAA,QACf;AAAA,QACA,OAAO,EAAE;AAAA,QACT,cAAc;AAAA,QACd,iBAAiB,KAAK;AAAA,MAAA,CACvB;AAAA,IAAA;AAEH,UAAM,UAAU,KAAK,UACjB,KAAK,QAAQ,IAAI,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,IAC7C;AAQJ,UAAM,iBAAiB,KAAK,cAAc,CAAC,KAAK;AAChD,UAAM,iBAAiB,KAAK;AAC5B,UAAM,eAAe,KAAK,mBAAmB;AAC7C,UAAM,eAAe,CAAC,KAAK;AAC3B,UAAM,iBAAiB,iBACnB,kBAAkB;AAAA,MAChB;AAAA,MACA,QAAQ,KAAK;AAAA,MACb,aAAa,KAAK,kBAAkB;AAAA,MACpC;AAAA,MACA,aAAa,KAAK;AAAA,IAAA,CACnB,IACD;AAEJ,UAAM,SAAS,iBACX,aAAa;AAAA,MACX;AAAA,MACA,UAAU,KAAK;AAAA,MACf,QAAQ,KAAK;AAAA,MACb,aAAa,KAAK,kBAAkB;AAAA,MACpC;AAAA,MACA,aAAa,KAAK;AAAA,IAAA,CACnB,IACD;AACJ,UAAM,eAAe,iBACjB,KAAK,cACH,kBAAkB,EAAC,OAAO,UAAU,KAAK,SAAA,CAAS,IAClD,iBAAiB;AAAA,MACf;AAAA,MACA,UAAU,KAAK;AAAA,MACf,QAAQ,KAAK,oBAAoB,KAAK;AAAA,IAAA,CACvC,IACH;AACJ,UAAM,OACJ,KAAK,aAAa,QAAQ,KAAK,wBAAwB,OACnD,0BAA0B,KAAK,aAAa,MAAM,WAAW;AAAA,MAC3D,WAAW,KAAK;AAAA,MAChB,kBAAkB,KAAK;AAAA,MACvB,QAAQ,KAAK,oBAAoB;AAAA,MACjC,OAAO,KAAK;AAAA,IAAA,CACb,CAAC,SACF;AACN,UAAM,UACJ,KAAK,WAAW,QAAQ,KAAK,2BAA2B,OACpD,KAAK,sBACH,sBAAsB;AAAA,MACpB,SAAS,KAAK;AAAA,MACd,kBAAkB,KAAK;AAAA,MACvB,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,IAAA,CACb,IACD,cAAc;AAAA,MACZ,SAAS,KAAK;AAAA,MACd,kBAAkB,KAAK;AAAA,MACvB,QAAQ,KAAK,uBAAuB;AAAA,MACpC,OAAO,KAAK;AAAA,IAAA,CACb,IACH;AACN,WAAO;AAAA;AAAA;AAAA;AAAA,kBAIO,OAAO;AAAA,0BACC,KAAK;AAAA,4BACH,KAAK,YAAY,CAAC;AAAA;AAAA,UAEpC,KAAK,YAAA,CAAa,IAAI,KAAK,YAAY;AAAA,UACvC,KAAK,mBACH,KAAK;AAAA,MACH,oBAAoB;AAAA,MACpB,gBAAgB,iBACZ;AAAA,QACE,WAAW;AAAA,QACX,UAAU,KAAK;AAAA,QACf;AAAA,QACA,iBAAiB,KAAK,kBAAkB;AAAA,MAAA,IAE1C;AAAA,MACJ,KAAK,wBACD,KAAK,kBAAkB,OACvB;AAAA,IAAA,IAEN,OAAO;AAAA,UACT,YAAY,IAAI,KAAK,6BAAA,CAA8B,IAAI,OAAO;AAAA,UAC9D,KAAK,qBAAqB,IAAI,IAAI;AAAA,UAClC,KAAK,gBAAgB,KAAK,KAAK,MAAM,SAAS,IAC5C,mCAAmC,SAAS,SAC5C,SAAS;AAAA,UACX,KAAK,MAAM,SAAS,IAClB,wCAAwC,KAAK,UAAA,CAAW,SACxD,KAAK,UAAA,CAAW;AAAA,UAClB,OAAO,IAAI,aAAa;AAAA,UACxB,KAAK,gBAAgB,KAAK,KAAK,MAAM,SAAS,IAC5C,mCAAmC,MAAM,SACzC,MAAM;AAAA,UACR,KAAK,kBAAA,CAAmB,IAAI,KAAK,eAAe;AAAA;AAAA;AAAA,EAGxD;AAAA,EAEQ,eAGN;AACA,UAAM,IAAI,KAAK,eAAe,KAAK;AACnC,UAAM,aAAa,MAAM,SAAS;AAElC,QAAI,KAAK,kBAAkB;AAGzB,UAAI;AACJ,UAAI,KAAK,YAAY,QAAQ,KAAK;AAChC,cAAM,WACD,KAAK,cAAc,KAAK,iBAAiB,MAAO,OAAO;AAC5D,oBAAY,UAAU,MAAM,SAAS,SAAS;AAAA,MAChD,OAAO;AACL,oBAAY,KAAK;AAAA,MACnB;AAEA,UAAI,YAAY,GAAG;AACjB,eAAO;AAAA,UACL,UAAU;AAAA,UACV,YAAY;AAAA,QAAA;AAAA,MAEhB;AACA,UAAI,YAAY,GAAG;AACjB,eAAO;AAAA,UACL,UAAU;AAAA,UACV,YAAY;AAAA,QAAA;AAAA,MAEhB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,UAAU,aACN,8CACA;AAAA,MACJ,YAAY,aACR,+CACA;AAAA,IAAA;AAAA,EAER;AAAA,EAEQ,YAAgD;AACtD,QAAI,CAAC,KAAK,QAAS,QAAO;AAE1B,UAAM,EAAC,UAAU,eAAc,KAAK,aAAA;AACpC,UAAM,OAAO,KAAK;AAElB,QAAI,KAAK,YAAY,QAAQ,KAAK;AAChC,YAAM,eAAe;AAAA,QACnB,KAAK;AAAA,QACL,KAAK;AAAA,MAAA;AAEP,YAAM,YAAY,qBAAqB,KAAK,aAAa,IAAI;AAC7D,YAAM,SAAS,OAAO,SAAS,KAAK,iBAAiB,IACjD,KAAK,oBACL;AAEJ,UAAI,eAAe,KAAK,IAAI,QAAQ,SAAS,GAAG;AAC9C,eAAO;AAAA,UACL;AAAA,UACA,KAAK;AAAA,UACL,KAAK;AAAA,UACL;AAAA,QAAA;AAAA,MAEJ;AAEA,aAAO;AAAA,UACH,mBAAmB;AAAA,QACnB,YAAY,KAAK;AAAA,QACjB,UAAU,KAAK;AAAA,QACf,UAAU;AAAA,QACV,UAAU,KAAK;AAAA,QACf,QAAQ;AAAA,QACR,cAAc;AAAA,MAAA,CACf,CAAC;AAAA,UACA;AAAA;AAAA,gBAEM,iBAAiB,UAAU,KAAK,aAAa,IAAI,CAAC;AAAA;AAAA,eAEnD;AAAA;AAAA,IAEX;AAEA,UAAM,IAAI,KAAK,eAAe,KAAK;AACnC,UAAM,aAAa,MAAM,SAAS;AAClC,QAAI,YAAoB,aACpB,+CACA;AACJ,QAAI,KAAK,kBAAkB;AACzB,UAAI,KAAK,gBAAgB,GAAG;AAC1B,oBAAY;AAAA,MACd,WAAW,KAAK,gBAAgB,GAAG;AACjC,oBAAY;AAAA,MACd;AAAA,IACF;AACA,WAAO;AAAA;AAAA,UAED,cAAc,WAAW,KAAK,aAAa,IAAI,CAAC;AAAA;AAAA;AAAA,EAGxD;AAAA,EAEQ,iBAAqD;AAC3D,QAAI,KAAK,kBAAkB,QAAW;AACpC,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,2BAA2B;AAAA,MACzC,OAAO,KAAK;AAAA,MACZ,UAAU,KAAK;AAAA,MACf,YAAY,KAAK;AAAA,MACjB,eAAe,KAAK;AAAA,MACpB,wBAAwB,KAAK;AAAA,MAC7B,kBAAkB,KAAK;AAAA,MACvB,UAAU,KAAK;AAAA,MACf,kBAAkB,KAAK;AAAA,IAAA,CACxB;AAED,UAAM,EAAC,aAAa,WAAW,UAAU,mBAAkB;AAE3D,UAAM,gBAAgB,yBAAyB,WAAW;AAC1D,UAAM,SACJ,yBACA,KAAK,QACL,gBACA;AAGF,UAAM,UAAU,iBAAiB,OAAO;AACxC,UAAM,iBAAiB,mBAAmB;AAAA,MACxC;AAAA,MACA;AAAA,MACA;AAAA,MACA,IAAI,KAAK;AAAA,IAAA,CACV;AAED,UAAM,UAAU,KAAK;AACrB,UAAM,eAAe,KAAK,+BAA+B;AAGzD,UAAM,WAAW,KAAK,gBAAgB;AACtC,QAAI,CAAC,KAAK,uBAAuB;AAE/B,WAAK,oBAAoB;AACzB,WAAK,wBAAwB;AAAA,IAC/B,OAAO;AACL,WAAK,oBAAoB,aAAa,KAAK,mBAAmB,QAAQ;AAAA,IACxE;AAGA,UAAM,mBAAmB,UACrB;AAAA,sCAC8B,KAAK,iBAAiB,mBAAmB,CAAC,MAAM,gCAAgC,OAAO,+BAA+B,0BAA0B,KAAK,mCAAmC,2BAA2B,0BAA0B,KAAK,mCAAmC;AAAA,YAC/S,cAAc;AAAA;AAAA,UAGlB;AAAA,+BACuB,KAAK,gBAAgB,EAAE,eAAe,CAAC,MAAM,8BAA8B,OAAO;AAAA,YACrG,cAAc;AAAA;AAAA;AAMtB,QAAI,kBAAkB,cAAc;AAClC,YAAM,WAAW;AACjB,YAAM,WAAW,WACb,KAAK,mBACL,KAAK;AACT,YAAM,gBAAgB,WAAW,IAAI;AAErC,YAAM,qBAAqB;AAAA,QACzB,oBAAoB;AAAA,MAAA;AAEtB,YAAM,cACJ,yBACA,KAAK,QACL,qBACA;AAEF,YAAM,YAAY,mBAAmB;AAAA,QACnC,aAAa,oBAAoB;AAAA,QACjC;AAAA,QACA,UAAU;AAAA;AAAA,QACV,IAAI,KAAK;AAAA,MAAA,CACV;AAED,UAAI,SAAS;AACX,cAAM,WAAW,OAAO,0BAA0B,KAAK,mCAAmC;AAC1F,eAAO;AAAA,YACH,gBAAgB;AAAA,wCACY,WAAW,EAAE,mBAAmB,CAAC,WAAW,gCAAgC,aAAa,yBAAyB,QAAQ;AAAA,cACpJ,SAAS;AAAA;AAAA;AAAA,MAGjB;AAEA,aAAO;AAAA,UACH,gBAAgB;AAAA,+BACK,WAAW,EAAE,eAAe,CAAC,WAAW,8BAA8B,aAAa;AAAA,YACtG,SAAS;AAAA;AAAA;AAAA,IAGjB;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,oBAA0D;AAChE,QAAI,KAAK,QAAQ,WAAW,GAAG;AAC7B,aAAO;AAAA,IACT;AAEA,WAAO,KAAK,QAAQ,IAAI,CAAC,MAAM;AAC7B,UAAI;AACJ,cAAQ,EAAE,MAAA;AAAA,QACR,KAAK,gBAAgB;AACnB,iBAAO;AACP;AAAA,QACF,KAAK,gBAAgB;AACnB,iBAAO;AACP;AAAA,QACF;AACE,iBAAO;AAAA,MAAA;AAGX,YAAM,QAAQ,OAAO;AACrB,aAAO,2BAA2B,EAAE,SAAS,UAAU,KAAK,+BAA+B,aAAa,EAAE,WAAW,CAAC;AAAA,IACxH,CAAC;AAAA,EACH;AAAA,EAEQ,+BAAqE;AAC3E,QAAI,CAAC,KAAK,wBAAwB;AAChC,aAAO;AAAA,IACT;AACA,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAEF;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ;AAGF;AAxnCa,SAunCK,SAAS,UAAU,YAAY;AAlnCrB,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GALb,SAKe,WAAA,SAAA,CAAA;AAEA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAPb,SAOe,WAAA,YAAA,CAAA;AACA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GARb,SAQe,WAAA,mBAAA,CAAA;AAUC,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAlBd,SAkBgB,WAAA,uBAAA,CAAA;AACA,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAnBd,SAmBgB,WAAA,cAAA,CAAA;AACA,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GApBd,SAoBgB,WAAA,oBAAA,CAAA;AAOA,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GA3Bd,SA2BgB,WAAA,eAAA,CAAA;AAED,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA7Bb,SA6Be,WAAA,iBAAA,CAAA;AAEA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA/Bb,SA+Be,WAAA,oBAAA,CAAA;AAEC,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAjCd,SAiCgB,WAAA,mBAAA,CAAA;AAED,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAnCb,SAmCe,WAAA,+BAAA,CAAA;AAEC,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GArCd,SAqCgB,WAAA,oBAAA,CAAA;AACA,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAtCd,SAsCgB,WAAA,YAAA,CAAA;AAEA,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAxCd,SAwCgB,WAAA,mBAAA,CAAA;AAEV,gBAAA;AAAA,EAAhB,MAAA;AAAM,GA1CI,SA0CM,WAAA,8BAAA,CAAA;AAiBS,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA3Db,SA2De,WAAA,WAAA,CAAA;AAS1B,gBAAA;AAAA,EADC,SAAS,EAAC,MAAM,QAAQ,WAAW,iBAAgB;AAAA,GAnEzC,SAoEX,WAAA,gBAAA,CAAA;AAC2C,gBAAA;AAAA,EAA1C,SAAS,EAAC,MAAM,OAAO,WAAW,OAAM;AAAA,GArE9B,SAqEgC,WAAA,SAAA,CAAA;AACA,gBAAA;AAAA,EAA1C,SAAS,EAAC,MAAM,OAAO,WAAW,OAAM;AAAA,GAtE9B,SAsEgC,WAAA,YAAA,CAAA;AAOhB,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GA7Ed,SA6EgB,WAAA,iBAAA,CAAA;AACgB,gBAAA;AAAA,EAA1C,SAAS,EAAC,MAAM,OAAO,WAAW,OAAM;AAAA,GA9E9B,SA8EgC,WAAA,WAAA,CAAA;AACA,gBAAA;AAAA,EAA1C,SAAS,EAAC,MAAM,OAAO,WAAW,OAAM;AAAA,GA/E9B,SA+EgC,WAAA,aAAA,CAAA;AAChB,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAhFd,SAgFgB,WAAA,mBAAA,CAAA;AACD,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAjFb,SAiFe,WAAA,iBAAA,CAAA;AAEiB,gBAAA;AAAA,EAA1C,SAAS,EAAC,MAAM,OAAO,WAAW,OAAM;AAAA,GAnF9B,SAmFgC,WAAA,WAAA,CAAA;AAChB,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GApFd,SAoFgB,WAAA,oBAAA,CAAA;AAMA,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GA1Fd,SA0FgB,WAAA,yBAAA,CAAA;AACA,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GA3Fd,SA2FgB,WAAA,cAAA,CAAA;AAOA,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAlGd,SAkGgB,WAAA,qBAAA,CAAA;AACgB,gBAAA;AAAA,EAA1C,SAAS,EAAC,MAAM,OAAO,WAAW,OAAM;AAAA,GAnG9B,SAmGgC,WAAA,WAAA,CAAA;AACjB,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GApGb,SAoGe,WAAA,aAAA,CAAA;AACA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GArGb,SAqGe,WAAA,wBAAA,CAAA;AACA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAtGb,SAsGe,WAAA,oBAAA,CAAA;AACA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAvGb,SAuGe,WAAA,aAAA,CAAA;AACA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAxGb,SAwGe,WAAA,WAAA,CAAA;AACA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAzGb,SAyGe,WAAA,2BAAA,CAAA;AACA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA1Gb,SA0Ge,WAAA,uBAAA,CAAA;AACA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA3Gb,SA2Ge,WAAA,gBAAA,CAAA;AAMC,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAjHd,SAiHgB,WAAA,uBAAA,CAAA;AAKD,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAtHb,SAsHe,WAAA,oBAAA,CAAA;AACC,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAvHd,SAuHgB,WAAA,0BAAA,CAAA;AAED,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAzHb,SAyHe,WAAA,WAAA,CAAA;AAEA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA3Hb,SA2He,WAAA,cAAA,CAAA;AAEA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA7Hb,SA6He,WAAA,YAAA,CAAA;AAEA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA/Hb,SA+He,WAAA,aAAA,CAAA;AAOC,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAtId,SAsIgB,WAAA,mBAAA,CAAA;AACD,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAvIb,SAuIe,WAAA,iBAAA,CAAA;AACA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAxIb,SAwIe,WAAA,YAAA,CAAA;AACC,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAzId,SAyIgB,WAAA,gBAAA,CAAA;AAEG,gBAAA;AAAA,EAA7B,SAAS,EAAC,WAAW,MAAA,CAAM;AAAA,GA3IjB,SA2ImB,WAAA,YAAA,CAAA;AACJ,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA5Ib,SA4Ie,WAAA,iBAAA,CAAA;AAGA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA/Ib,SA+Ie,WAAA,WAAA,CAAA;AAEA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAjJb,SAiJe,WAAA,eAAA,CAAA;AAEA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAnJb,SAmJe,WAAA,iBAAA,CAAA;AAEA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GArJb,SAqJe,WAAA,eAAA,CAAA;AAEA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAvJb,SAuJe,WAAA,eAAA,CAAA;AACC,gBAAA;AAAA,EAA1B,SAAS,EAAC,MAAM,QAAA,CAAQ;AAAA,GAxJd,SAwJgB,WAAA,oBAAA,CAAA;AACD,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAzJb,SAyJe,WAAA,qBAAA,CAAA;AAEA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA3Jb,SA2Je,WAAA,8BAAA,CAAA;AAEA,gBAAA;AAAA,EAAzB,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GA7Jb,SA6Je,WAAA,yBAAA,CAAA;AAOtB,gBAAA;AAAA,EADH,SAAS,EAAC,MAAM,OAAA,CAAO;AAAA,GAnKb,SAoKP,WAAA,sBAAA,CAAA;AApKO,WAAN,gBAAA;AAAA,EADN,cAAc,WAAW;AAAA,GACb,QAAA;"}
|
|
@@ -57,8 +57,11 @@ export declare class ObcWind extends LitElement {
|
|
|
57
57
|
* `scaleWindIcon` scales the arrow glyph — and, because the scale group wraps
|
|
58
58
|
* the translated glyph, the effective tip radius is `radius × scale`.
|
|
59
59
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
60
|
+
* Large places the 48-unit icon box exactly inside the double-face track
|
|
61
|
+
* band (112–160), per the OpenBridge 6.1 design (the icon box spans the
|
|
62
|
+
* band, node 19663-140867); the tip anchor is the box inner edge plus the
|
|
63
|
+
* glyph's inward overhang. Medium ≈93 and small ≈8 (touching center) are
|
|
64
|
+
* measured from the Figma variants (node 6552-102891).
|
|
62
65
|
*/
|
|
63
66
|
export declare function resolveWindArrowPlacement(variant: ResolvedWindVariant): {
|
|
64
67
|
windSymbolRadius: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wind.d.ts","sourceRoot":"","sources":["../../../src/navigation-instruments/wind/wind.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAkB,MAAM,KAAK,CAAC;AAGhD,OAAO,EACL,WAAW,EAIZ,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"wind.d.ts","sourceRoot":"","sources":["../../../src/navigation-instruments/wind/wind.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAkB,MAAM,KAAK,CAAC;AAGhD,OAAO,EACL,WAAW,EAIZ,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAC;AAGrC,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,oBAAY,WAAW;IACrB,IAAI,SAAS;IACb,KAAK,UAAU;IACf,MAAM,WAAW;IACjB,KAAK,UAAU;CAChB;AAED,oBAAY,iBAAiB;IAC3B,SAAS,cAAc;IACvB,aAAa,kBAAkB;CAChC;AAED,KAAK,mBAAmB,GACpB,WAAW,CAAC,KAAK,GACjB,WAAW,CAAC,MAAM,GAClB,WAAW,CAAC,KAAK,CAAC;AAQtB;;GAEG;AACH,qBACa,OAAQ,SAAQ,UAAU;IACX,wBAAwB,EAAE,MAAM,CAAK;IACrC,qBAAqB,EAAE,MAAM,CAAM;IAE7D,iBAAiB,EAAE,iBAAiB,EAAE,CAAM;IAClB,WAAW,EAAE,WAAW,CAA0B;IAClD,gBAAgB,EAAE,MAAM,CAAK;IAC7B,OAAO,EAAE,WAAW,CAAoB;IACxC,iBAAiB,SAA6B;IAC9C,kBAAkB,SAA8B;IAC1E,yFAAyF;IAC/D,aAAa,EAAE,iBAAiB,CAC5B;IAC9B,wGAAwG;IAC9E,QAAQ,EAAE,QAAQ,CAAoB;IAEvD,OAAO,CAAC,YAAY,CAA2C;IAExE,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC,OAAO,CAAC,kBAAkB,CAAS;IAE1B,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAK5B,YAAY,IAAI,IAAI;cAOV,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAU/D,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,cAAc;IAWtB,OAAO,KAAK,gBAAgB,GAK3B;IAEQ,MAAM;IA0Cf,OAAO,KAAK,kBAAkB,GAI7B;IAED,OAAO,CAAC,wBAAwB;IAmBhC,OAAO,CAAC,mBAAmB;IAoF3B,OAAgB,MAAM,0BAA2B;CAClD;AAoBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,mBAAmB,GAAG;IACvE,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;CACvB,CAcA;AAYD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,UAAU,EAAE,OAAO,CAAC;KACrB;CACF"}
|
|
@@ -2,6 +2,7 @@ import { unsafeCSS, LitElement, html } from "lit";
|
|
|
2
2
|
import { property, state } from "lit/decorators.js";
|
|
3
3
|
import compentStyle from "./wind.css.js";
|
|
4
4
|
import { WatchCircleType, innerRingRadiusFor } from "../watch/watch.js";
|
|
5
|
+
import { WIND_ICON_TIP_TO_BOX_INNER } from "../watch/environment.js";
|
|
5
6
|
import { renderWindForcePattern } from "../watch/force-pattern.js";
|
|
6
7
|
import { Priority } from "../types.js";
|
|
7
8
|
import { customElement } from "../../decorator.js";
|
|
@@ -279,7 +280,10 @@ function resolveHistogramMaxRadius(variant) {
|
|
|
279
280
|
function resolveWindArrowPlacement(variant) {
|
|
280
281
|
switch (variant) {
|
|
281
282
|
case "large":
|
|
282
|
-
return {
|
|
283
|
+
return {
|
|
284
|
+
windSymbolRadius: innerRingRadiusFor(WatchCircleType.double) + WIND_ICON_TIP_TO_BOX_INNER,
|
|
285
|
+
scaleWindIcon: 1
|
|
286
|
+
};
|
|
283
287
|
case "medium":
|
|
284
288
|
return { windSymbolRadius: 62, scaleWindIcon: 1.5 };
|
|
285
289
|
case "small":
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wind.js","sources":["../../../src/navigation-instruments/wind/wind.ts"],"sourcesContent":["import {LitElement, html, unsafeCSS} from 'lit';\nimport {property, state} from 'lit/decorators.js';\nimport compentStyle from './wind.css?inline';\nimport {\n VesselImage,\n VesselImageSize,\n WatchCircleType,\n innerRingRadiusFor,\n} from '../watch/watch.js';\nimport {renderWindForcePattern} from '../watch/force-pattern.js';\nimport {Priority} from '../types.js';\nimport {customElement} from '../../decorator.js';\n\nexport interface WindHistogramData {\n direction: number;\n occurrences: number;\n}\n\nexport enum WindVariant {\n auto = 'auto',\n small = 'small',\n medium = 'medium',\n large = 'large',\n}\n\nexport enum WindVisualization {\n histogram = 'histogram',\n forceGraphics = 'forceGraphics',\n}\n\ntype ResolvedWindVariant =\n | WindVariant.small\n | WindVariant.medium\n | WindVariant.large;\n\nconst WIND_SMALL_MAX_PX_DEFAULT = 96;\nconst WIND_MEDIUM_MAX_PX_DEFAULT = 200;\nconst WIND_HISTOGRAM_MIN_RADIUS = 50;\nconst WIND_PATTERN_SCALE_DOUBLE = 0.875;\nconst WIND_PATTERN_SCALE_SINGLE = 1.28125;\n\n/**\n * @stable\n */\n@customElement('obc-wind')\nexport class ObcWind extends LitElement {\n @property({type: Number}) currentWindFromDirection: number = 0;\n @property({type: Number}) currentWindSpeedKnots: number = 25;\n @property({type: Array, attribute: false})\n windHistogramData: WindHistogramData[] = [];\n @property({type: String}) vesselImage: VesselImage = VesselImage.genericTop;\n @property({type: Number}) vesselHeadingDeg: number = 0;\n @property({type: String}) variant: WindVariant = WindVariant.auto;\n @property({type: Number}) smallVariantMaxPx = WIND_SMALL_MAX_PX_DEFAULT;\n @property({type: Number}) mediumVariantMaxPx = WIND_MEDIUM_MAX_PX_DEFAULT;\n /** Center visualization: wind histogram (default) or the force-graphics streak field. */\n @property({type: String}) visualization: WindVisualization =\n WindVisualization.histogram;\n /** Color priority: `Priority.enhanced` uses the blue/enhanced palette (default: `Priority.regular`). */\n @property({type: String}) priority: Priority = Priority.regular;\n\n @state() private _autoVariant: ResolvedWindVariant = WindVariant.medium;\n\n private _resizeObserver?: ResizeObserver;\n private _isObservingResize = false;\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.syncResizeObserver();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.stopResizeObserver();\n }\n\n override firstUpdated(): void {\n // Defer initial sizing to firstUpdated when element has been painted\n if (this.variant === WindVariant.auto) {\n this.setAutoVariant(this.clientWidth, this.clientHeight);\n }\n }\n\n protected override updated(changed: Map<string, unknown>): void {\n if (changed.has('variant')) {\n this.syncResizeObserver();\n }\n\n if (changed.has('smallVariantMaxPx') || changed.has('mediumVariantMaxPx')) {\n this.setAutoVariant(this.clientWidth, this.clientHeight);\n }\n }\n\n private syncResizeObserver(): void {\n if (\n this.variant !== WindVariant.auto ||\n typeof ResizeObserver === 'undefined'\n ) {\n this.stopResizeObserver();\n return;\n }\n\n if (!this._resizeObserver) {\n this._resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (!entry || this.variant !== WindVariant.auto) return;\n const {width, height} = entry.contentRect;\n this.setAutoVariant(width, height);\n });\n }\n\n if (!this._isObservingResize) {\n this._resizeObserver.observe(this);\n this._isObservingResize = true;\n }\n }\n\n private stopResizeObserver(): void {\n if (this._resizeObserver && this._isObservingResize) {\n this._resizeObserver.disconnect();\n this._isObservingResize = false;\n }\n }\n\n private setAutoVariant(width: number, height: number): void {\n const next = resolveAutoWindVariant(\n Math.min(width, height),\n this.smallVariantMaxPx,\n this.mediumVariantMaxPx\n );\n if (next !== this._autoVariant) {\n this._autoVariant = next;\n }\n }\n\n private get effectiveVariant(): ResolvedWindVariant {\n if (this.variant === WindVariant.auto) return this._autoVariant;\n return isResolvedWindVariant(this.variant)\n ? this.variant\n : this._autoVariant;\n }\n\n override render() {\n const variant = this.effectiveVariant;\n const watchCircleType =\n variant === WindVariant.large\n ? WatchCircleType.double\n : WatchCircleType.single;\n const vessels =\n variant === WindVariant.small\n ? []\n : [\n {\n size: VesselImageSize.medium,\n transform: `rotate(${this.vesselHeadingDeg}deg)`,\n vesselImage: this.vesselImage,\n },\n ];\n const {windSymbolRadius, scaleWindIcon} =\n resolveWindArrowPlacement(variant);\n return html`\n <div class=\"wrapper variant-${variant}\">\n ${this.renderVisualizationLayer(variant)}\n <obc-watch\n .watchCircleType=${watchCircleType}\n .priority=${this.priority}\n .windKnots=${this.currentWindSpeedKnots}\n .windFromDirectionDeg=${this.currentWindFromDirection}\n .windColor=${this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : undefined}\n .windSymbolRadius=${windSymbolRadius}\n .scaleWindIcon=${scaleWindIcon}\n crosshairEnabled\n northArrow\n tickmarksInside\n .showLabels=${true}\n .insideLabelsFlush=${true}\n .vessels=${vessels}\n ></obc-watch>\n </div>\n `;\n }\n\n private get visualizationColor(): string {\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-tertiary-color)'\n : 'var(--instrument-regular-tertiary-color)';\n }\n\n private renderVisualizationLayer(variant: ResolvedWindVariant) {\n if (this.visualization === WindVisualization.forceGraphics) {\n return html`\n <svg width=\"100%\" height=\"100%\" viewBox=\"-200 -200 400 400\">\n ${renderWindForcePattern({\n fromDirectionDeg: this.currentWindFromDirection,\n radius: resolveHistogramMaxRadius(variant),\n patternScale:\n variant === WindVariant.large\n ? WIND_PATTERN_SCALE_DOUBLE\n : WIND_PATTERN_SCALE_SINGLE,\n color: this.visualizationColor,\n })}\n </svg>\n `;\n }\n return this.renderWindHistogram(variant);\n }\n\n private renderWindHistogram(variant: ResolvedWindVariant) {\n const maxRadius = resolveHistogramMaxRadius(variant);\n const minRadius = WIND_HISTOGRAM_MIN_RADIUS;\n const center = {x: 0, y: 0};\n\n // Find the max occurrences for scaling\n const maxOccurrences = Math.max(\n ...this.windHistogramData.map((d) => d.occurrences)\n );\n const hasValidScale = Number.isFinite(maxOccurrences) && maxOccurrences > 0;\n\n // Interpolate occurrences for each degree (0-359)\n const interpolated: number[] = new Array(360).fill(0);\n if (this.windHistogramData.length > 1) {\n // Sort data by direction\n const sorted = [...this.windHistogramData].sort(\n (a, b) => a.direction - b.direction\n );\n for (let i = 0; i < sorted.length; i++) {\n const curr = sorted[i];\n const next = sorted[(i + 1) % sorted.length];\n const start = curr.direction;\n // Ensure wrap-around at 360\n const end =\n next.direction > start ? next.direction : next.direction + 360;\n for (let deg = start; deg < end; deg++) {\n const t = (deg - start) / (end - start);\n const occ =\n curr.occurrences + t * (next.occurrences - curr.occurrences);\n interpolated[deg % 360] = occ;\n }\n }\n } else if (this.windHistogramData.length === 1) {\n interpolated.fill(this.windHistogramData[0].occurrences);\n }\n\n // Create SVG path for each degree\n let outerPathPoints = '';\n for (let deg = 0; deg < 360; deg++) {\n const angle = ((deg - 90) * Math.PI) / 180;\n const occ = interpolated[deg];\n const radius = hasValidScale\n ? maxRadius - (occ / maxOccurrences) * (maxRadius - minRadius)\n : maxRadius;\n const x2 = center.x + radius * Math.cos(angle);\n const y2 = center.y + radius * Math.sin(angle);\n outerPathPoints += `L ${x2} ${y2} `;\n }\n\n return html`\n <svg width=\"100%\" height=\"100%\" viewBox=\"-200 -200 400 400\">\n <mask id=\"mask\">\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${maxRadius}\"\n stroke-width=\"1\"\n fill=\"white\"\n />\n <path d=\"M 0 ${-maxRadius} ${outerPathPoints}Z\" fill=\"black\" />\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${minRadius}\"\n vector-effect=\"non-scaling-stroke\"\n stroke=\"white\"\n stroke-width=\"1\"\n fill=\"transparent\"\n />\n </mask>\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${maxRadius}\"\n vector-effect=\"non-scaling-stroke\"\n stroke=\"${this.visualizationColor}\"\n stroke-width=\"1\"\n fill=\"${this.visualizationColor}\"\n mask=\"url(#mask)\"\n />\n </svg>\n `;\n }\n\n static override styles = unsafeCSS(compentStyle);\n}\n\nfunction resolveAutoWindVariant(\n sizePx: number,\n smallVariantMaxPx: number,\n mediumVariantMaxPx: number\n): ResolvedWindVariant {\n const minT = Math.min(smallVariantMaxPx, mediumVariantMaxPx);\n const maxT = Math.max(smallVariantMaxPx, mediumVariantMaxPx);\n if (sizePx < minT) return WindVariant.small;\n if (sizePx < maxT) return WindVariant.medium;\n return WindVariant.large;\n}\n\nfunction resolveHistogramMaxRadius(variant: ResolvedWindVariant): number {\n return variant === WindVariant.large\n ? innerRingRadiusFor(WatchCircleType.double)\n : innerRingRadiusFor(WatchCircleType.single);\n}\n\n/**\n * Per-variant placement of the wind arrow inside `<obc-watch>`.\n *\n * `windSymbolRadius` is the distance from the watch center where the arrow\n * tip is anchored (in watch SVG units, where the outer ring sits at 184);\n * `scaleWindIcon` scales the arrow glyph — and, because the scale group wraps\n * the translated glyph, the effective tip radius is `radius × scale`.\n *\n * Values measured from the OpenBridge 6.1 Figma (node 6552-102891):\n * large tip ≈119 (inner-ring edge), medium ≈93, small ≈8 (touching center).\n */\nexport function resolveWindArrowPlacement(variant: ResolvedWindVariant): {\n windSymbolRadius: number;\n scaleWindIcon: number;\n} {\n switch (variant) {\n case WindVariant.large:\n return {windSymbolRadius: 119, scaleWindIcon: 1.0};\n case WindVariant.medium:\n return {windSymbolRadius: 62, scaleWindIcon: 1.5};\n case WindVariant.small:\n return {windSymbolRadius: 3, scaleWindIcon: 2.667};\n }\n}\n\nfunction isResolvedWindVariant(\n variant: string\n): variant is ResolvedWindVariant {\n return (\n variant === WindVariant.small ||\n variant === WindVariant.medium ||\n variant === WindVariant.large\n );\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-wind': ObcWind;\n }\n}\n"],"names":["WindVariant","WindVisualization"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"wind.js","sources":["../../../src/navigation-instruments/wind/wind.ts"],"sourcesContent":["import {LitElement, html, unsafeCSS} from 'lit';\nimport {property, state} from 'lit/decorators.js';\nimport compentStyle from './wind.css?inline';\nimport {\n VesselImage,\n VesselImageSize,\n WatchCircleType,\n innerRingRadiusFor,\n} from '../watch/watch.js';\nimport {WIND_ICON_TIP_TO_BOX_INNER} from '../watch/environment.js';\nimport {renderWindForcePattern} from '../watch/force-pattern.js';\nimport {Priority} from '../types.js';\nimport {customElement} from '../../decorator.js';\n\nexport interface WindHistogramData {\n direction: number;\n occurrences: number;\n}\n\nexport enum WindVariant {\n auto = 'auto',\n small = 'small',\n medium = 'medium',\n large = 'large',\n}\n\nexport enum WindVisualization {\n histogram = 'histogram',\n forceGraphics = 'forceGraphics',\n}\n\ntype ResolvedWindVariant =\n | WindVariant.small\n | WindVariant.medium\n | WindVariant.large;\n\nconst WIND_SMALL_MAX_PX_DEFAULT = 96;\nconst WIND_MEDIUM_MAX_PX_DEFAULT = 200;\nconst WIND_HISTOGRAM_MIN_RADIUS = 50;\nconst WIND_PATTERN_SCALE_DOUBLE = 0.875;\nconst WIND_PATTERN_SCALE_SINGLE = 1.28125;\n\n/**\n * @stable\n */\n@customElement('obc-wind')\nexport class ObcWind extends LitElement {\n @property({type: Number}) currentWindFromDirection: number = 0;\n @property({type: Number}) currentWindSpeedKnots: number = 25;\n @property({type: Array, attribute: false})\n windHistogramData: WindHistogramData[] = [];\n @property({type: String}) vesselImage: VesselImage = VesselImage.genericTop;\n @property({type: Number}) vesselHeadingDeg: number = 0;\n @property({type: String}) variant: WindVariant = WindVariant.auto;\n @property({type: Number}) smallVariantMaxPx = WIND_SMALL_MAX_PX_DEFAULT;\n @property({type: Number}) mediumVariantMaxPx = WIND_MEDIUM_MAX_PX_DEFAULT;\n /** Center visualization: wind histogram (default) or the force-graphics streak field. */\n @property({type: String}) visualization: WindVisualization =\n WindVisualization.histogram;\n /** Color priority: `Priority.enhanced` uses the blue/enhanced palette (default: `Priority.regular`). */\n @property({type: String}) priority: Priority = Priority.regular;\n\n @state() private _autoVariant: ResolvedWindVariant = WindVariant.medium;\n\n private _resizeObserver?: ResizeObserver;\n private _isObservingResize = false;\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.syncResizeObserver();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.stopResizeObserver();\n }\n\n override firstUpdated(): void {\n // Defer initial sizing to firstUpdated when element has been painted\n if (this.variant === WindVariant.auto) {\n this.setAutoVariant(this.clientWidth, this.clientHeight);\n }\n }\n\n protected override updated(changed: Map<string, unknown>): void {\n if (changed.has('variant')) {\n this.syncResizeObserver();\n }\n\n if (changed.has('smallVariantMaxPx') || changed.has('mediumVariantMaxPx')) {\n this.setAutoVariant(this.clientWidth, this.clientHeight);\n }\n }\n\n private syncResizeObserver(): void {\n if (\n this.variant !== WindVariant.auto ||\n typeof ResizeObserver === 'undefined'\n ) {\n this.stopResizeObserver();\n return;\n }\n\n if (!this._resizeObserver) {\n this._resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (!entry || this.variant !== WindVariant.auto) return;\n const {width, height} = entry.contentRect;\n this.setAutoVariant(width, height);\n });\n }\n\n if (!this._isObservingResize) {\n this._resizeObserver.observe(this);\n this._isObservingResize = true;\n }\n }\n\n private stopResizeObserver(): void {\n if (this._resizeObserver && this._isObservingResize) {\n this._resizeObserver.disconnect();\n this._isObservingResize = false;\n }\n }\n\n private setAutoVariant(width: number, height: number): void {\n const next = resolveAutoWindVariant(\n Math.min(width, height),\n this.smallVariantMaxPx,\n this.mediumVariantMaxPx\n );\n if (next !== this._autoVariant) {\n this._autoVariant = next;\n }\n }\n\n private get effectiveVariant(): ResolvedWindVariant {\n if (this.variant === WindVariant.auto) return this._autoVariant;\n return isResolvedWindVariant(this.variant)\n ? this.variant\n : this._autoVariant;\n }\n\n override render() {\n const variant = this.effectiveVariant;\n const watchCircleType =\n variant === WindVariant.large\n ? WatchCircleType.double\n : WatchCircleType.single;\n const vessels =\n variant === WindVariant.small\n ? []\n : [\n {\n size: VesselImageSize.medium,\n transform: `rotate(${this.vesselHeadingDeg}deg)`,\n vesselImage: this.vesselImage,\n },\n ];\n const {windSymbolRadius, scaleWindIcon} =\n resolveWindArrowPlacement(variant);\n return html`\n <div class=\"wrapper variant-${variant}\">\n ${this.renderVisualizationLayer(variant)}\n <obc-watch\n .watchCircleType=${watchCircleType}\n .priority=${this.priority}\n .windKnots=${this.currentWindSpeedKnots}\n .windFromDirectionDeg=${this.currentWindFromDirection}\n .windColor=${this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : undefined}\n .windSymbolRadius=${windSymbolRadius}\n .scaleWindIcon=${scaleWindIcon}\n crosshairEnabled\n northArrow\n tickmarksInside\n .showLabels=${true}\n .insideLabelsFlush=${true}\n .vessels=${vessels}\n ></obc-watch>\n </div>\n `;\n }\n\n private get visualizationColor(): string {\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-tertiary-color)'\n : 'var(--instrument-regular-tertiary-color)';\n }\n\n private renderVisualizationLayer(variant: ResolvedWindVariant) {\n if (this.visualization === WindVisualization.forceGraphics) {\n return html`\n <svg width=\"100%\" height=\"100%\" viewBox=\"-200 -200 400 400\">\n ${renderWindForcePattern({\n fromDirectionDeg: this.currentWindFromDirection,\n radius: resolveHistogramMaxRadius(variant),\n patternScale:\n variant === WindVariant.large\n ? WIND_PATTERN_SCALE_DOUBLE\n : WIND_PATTERN_SCALE_SINGLE,\n color: this.visualizationColor,\n })}\n </svg>\n `;\n }\n return this.renderWindHistogram(variant);\n }\n\n private renderWindHistogram(variant: ResolvedWindVariant) {\n const maxRadius = resolveHistogramMaxRadius(variant);\n const minRadius = WIND_HISTOGRAM_MIN_RADIUS;\n const center = {x: 0, y: 0};\n\n // Find the max occurrences for scaling\n const maxOccurrences = Math.max(\n ...this.windHistogramData.map((d) => d.occurrences)\n );\n const hasValidScale = Number.isFinite(maxOccurrences) && maxOccurrences > 0;\n\n // Interpolate occurrences for each degree (0-359)\n const interpolated: number[] = new Array(360).fill(0);\n if (this.windHistogramData.length > 1) {\n // Sort data by direction\n const sorted = [...this.windHistogramData].sort(\n (a, b) => a.direction - b.direction\n );\n for (let i = 0; i < sorted.length; i++) {\n const curr = sorted[i];\n const next = sorted[(i + 1) % sorted.length];\n const start = curr.direction;\n // Ensure wrap-around at 360\n const end =\n next.direction > start ? next.direction : next.direction + 360;\n for (let deg = start; deg < end; deg++) {\n const t = (deg - start) / (end - start);\n const occ =\n curr.occurrences + t * (next.occurrences - curr.occurrences);\n interpolated[deg % 360] = occ;\n }\n }\n } else if (this.windHistogramData.length === 1) {\n interpolated.fill(this.windHistogramData[0].occurrences);\n }\n\n // Create SVG path for each degree\n let outerPathPoints = '';\n for (let deg = 0; deg < 360; deg++) {\n const angle = ((deg - 90) * Math.PI) / 180;\n const occ = interpolated[deg];\n const radius = hasValidScale\n ? maxRadius - (occ / maxOccurrences) * (maxRadius - minRadius)\n : maxRadius;\n const x2 = center.x + radius * Math.cos(angle);\n const y2 = center.y + radius * Math.sin(angle);\n outerPathPoints += `L ${x2} ${y2} `;\n }\n\n return html`\n <svg width=\"100%\" height=\"100%\" viewBox=\"-200 -200 400 400\">\n <mask id=\"mask\">\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${maxRadius}\"\n stroke-width=\"1\"\n fill=\"white\"\n />\n <path d=\"M 0 ${-maxRadius} ${outerPathPoints}Z\" fill=\"black\" />\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${minRadius}\"\n vector-effect=\"non-scaling-stroke\"\n stroke=\"white\"\n stroke-width=\"1\"\n fill=\"transparent\"\n />\n </mask>\n <circle\n cx=\"0\"\n cy=\"0\"\n r=\"${maxRadius}\"\n vector-effect=\"non-scaling-stroke\"\n stroke=\"${this.visualizationColor}\"\n stroke-width=\"1\"\n fill=\"${this.visualizationColor}\"\n mask=\"url(#mask)\"\n />\n </svg>\n `;\n }\n\n static override styles = unsafeCSS(compentStyle);\n}\n\nfunction resolveAutoWindVariant(\n sizePx: number,\n smallVariantMaxPx: number,\n mediumVariantMaxPx: number\n): ResolvedWindVariant {\n const minT = Math.min(smallVariantMaxPx, mediumVariantMaxPx);\n const maxT = Math.max(smallVariantMaxPx, mediumVariantMaxPx);\n if (sizePx < minT) return WindVariant.small;\n if (sizePx < maxT) return WindVariant.medium;\n return WindVariant.large;\n}\n\nfunction resolveHistogramMaxRadius(variant: ResolvedWindVariant): number {\n return variant === WindVariant.large\n ? innerRingRadiusFor(WatchCircleType.double)\n : innerRingRadiusFor(WatchCircleType.single);\n}\n\n/**\n * Per-variant placement of the wind arrow inside `<obc-watch>`.\n *\n * `windSymbolRadius` is the distance from the watch center where the arrow\n * tip is anchored (in watch SVG units, where the outer ring sits at 184);\n * `scaleWindIcon` scales the arrow glyph — and, because the scale group wraps\n * the translated glyph, the effective tip radius is `radius × scale`.\n *\n * Large places the 48-unit icon box exactly inside the double-face track\n * band (112–160), per the OpenBridge 6.1 design (the icon box spans the\n * band, node 19663-140867); the tip anchor is the box inner edge plus the\n * glyph's inward overhang. Medium ≈93 and small ≈8 (touching center) are\n * measured from the Figma variants (node 6552-102891).\n */\nexport function resolveWindArrowPlacement(variant: ResolvedWindVariant): {\n windSymbolRadius: number;\n scaleWindIcon: number;\n} {\n switch (variant) {\n case WindVariant.large:\n return {\n windSymbolRadius:\n innerRingRadiusFor(WatchCircleType.double) +\n WIND_ICON_TIP_TO_BOX_INNER,\n scaleWindIcon: 1.0,\n };\n case WindVariant.medium:\n return {windSymbolRadius: 62, scaleWindIcon: 1.5};\n case WindVariant.small:\n return {windSymbolRadius: 3, scaleWindIcon: 2.667};\n }\n}\n\nfunction isResolvedWindVariant(\n variant: string\n): variant is ResolvedWindVariant {\n return (\n variant === WindVariant.small ||\n variant === WindVariant.medium ||\n variant === WindVariant.large\n );\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-wind': ObcWind;\n }\n}\n"],"names":["WindVariant","WindVisualization"],"mappings":"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"}
|