@oicl/openbridge-webcomponents 2.1.0-next.8 → 2.1.0-next.9

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.
Files changed (31) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +25 -16
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +8 -8
  4. package/dist/components/menu-button/menu-button.css.js +1 -1
  5. package/dist/components/menu-button/menu-button.css.js.map +1 -1
  6. package/dist/icons/icon-log-open-google.d.ts +16 -0
  7. package/dist/icons/icon-log-open-google.d.ts.map +1 -0
  8. package/dist/icons/icon-log-open-google.js +10 -0
  9. package/dist/icons/icon-log-open-google.js.map +1 -0
  10. package/dist/icons/icon-timer-google.d.ts +16 -0
  11. package/dist/icons/icon-timer-google.d.ts.map +1 -0
  12. package/dist/icons/icon-timer-google.js +10 -0
  13. package/dist/icons/icon-timer-google.js.map +1 -0
  14. package/dist/icons/index.d.ts +2 -0
  15. package/dist/icons/index.d.ts.map +1 -1
  16. package/dist/icons/index.js +2 -0
  17. package/dist/navigation-instruments/compass/compass.js +1 -1
  18. package/dist/navigation-instruments/compass/compass.js.map +1 -1
  19. package/dist/navigation-instruments/compass-flat/compass-flat.js +1 -1
  20. package/dist/navigation-instruments/compass-flat/compass-flat.js.map +1 -1
  21. package/dist/navigation-instruments/compass-sector/compass-sector.js +1 -1
  22. package/dist/navigation-instruments/compass-sector/compass-sector.js.map +1 -1
  23. package/dist/navigation-instruments/rate-of-turn/rate-of-turn.controller.js +2 -2
  24. package/dist/navigation-instruments/rate-of-turn/rate-of-turn.controller.js.map +1 -1
  25. package/dist/navigation-instruments/rate-of-turn/rate-of-turn.js +1 -1
  26. package/dist/navigation-instruments/rate-of-turn/rate-of-turn.js.map +1 -1
  27. package/dist/palettes/blinking.d.ts +5 -4
  28. package/dist/palettes/blinking.d.ts.map +1 -1
  29. package/dist/palettes/blinking.js +10 -9
  30. package/dist/palettes/blinking.js.map +1 -1
  31. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"compass-flat.js","names":[],"sources":["../../../src/navigation-instruments/compass-flat/compass-flat.ts"],"sourcesContent":["import {LitElement, html, svg, SVGTemplateResult, unsafeCSS} from 'lit';\nimport componentStyle from './compass-flat.css?inline';\nimport instrumentReadoutStyle from '../readout/instrument-readout.css?inline';\nimport {\n centerReadoutStyles,\n renderCenterReadouts,\n} from '../readout/center-readout.js';\nimport {ReadoutSize} from '../readout/readout.js';\nimport {property, state} from 'lit/decorators.js';\nimport {Tickmark, TickmarkType} from '../watch-flat/tickmark-flat.js';\nimport '../watch-flat/watch-flat.js';\nimport {customElement} from '../../decorator.js';\nimport {Priority} from '../types.js';\nimport {\n RotType,\n LINEAR_DOT_ANGLE_SPACING,\n ROT_ZERO_DEADBAND_DEG,\n} from '../rate-of-turn/rot-renderer.js';\n\nexport {RotType};\n\nexport enum CompassFlatPriorityElement {\n hdg = 'hdg',\n cog = 'cog',\n rot = 'rot',\n}\n\nexport enum LabelPosition {\n top = -45,\n bottom = 50,\n}\n\nexport enum LabelStyle {\n regular = 'var(--instrument-tick-mark-secondary-color)',\n}\n\nexport interface Label {\n x: number;\n y: LabelPosition;\n text: string;\n}\n\n/**\n * `<obc-compass-flat>` — Horizontal strip compass with HDG/COG arrows and optional rate-of-turn indicator.\n *\n * Renders a flat (non-circular) compass strip that scrolls horizontally to\n * display the current heading. A filled HDG arrow sits at the center while a\n * hollow COG arrow is positioned at the angular difference. The strip\n * auto-scales its field of view so both arrows remain visible. Cardinal\n * labels (N, S, E, W) and tickmarks are generated at densities responsive to\n * the strip width.\n *\n * ## Features\n *\n * - **Auto-scaling FOV**: The visible compass range widens to keep both HDG\n * and COG arrows in view, bounded by `minFOV` / `maxFOV`.\n * - **HDG / COG arrows**: Filled (HDG) and hollow (COG) arrow SVGs positioned\n * along the strip.\n * - **FOV indicator**: Optional numeric labels showing the port/starboard\n * bearing range and current heading.\n * - **Rate of turn**: Animated linear ROT indicator (spinning dots or\n * horizontal bar) via `rotType`. When enabled, a bottom bar is added to\n * the strip to house the indicator.\n * - **Readout**: `hasReadout` shows a centered heading readout below the\n * strip (label `HDG`, unit `DEG` by default).\n * - **Color priority**: Per-element priority for HDG, COG, and ROT via\n * `priorityElements`.\n *\n *\n * @ignition-base-height: 170px\n * @ignition-base-width: 512px\n *\n * @property heading - Current heading in degrees.\n * @property courseOverGround - Current COG in degrees.\n * @property rotType - ROT display mode: `'dots'`, `'bar'`, or `undefined` (hidden).\n * @property rateOfTurnDegreesPerMinute - Measured rate of turn in degrees per minute (positive = starboard).\n * When `undefined`, falls back to the deprecated `rotationsPerMinute`.\n * @availableWhen rateOfTurnDegreesPerMinute rotType!=undefined\n * @property rotDotAnimationFactor - Visual amplification applied only to the spinning dot animation.\n * @availableWhen rotDotAnimationFactor rotType!=undefined\n * @property rotMaxValue - Bar-extent reference value in **degrees per minute**. Default `60`\n * per ES-TRIN 2025/1 Art. 3.02.\n * @availableWhen rotMaxValue rotType!=undefined\n * @property rotArcExtent - Degrees of bar arc per max-value ROT (default 60).\n * @availableWhen rotArcExtent rotType!=undefined\n * @availableWhen rotPortStarboard rotType!=undefined\n * @availableWhen rotAtZeroDeadband rotType!=undefined\n * @property hasReadout - When `true`, shows a centered `<obc-readout>` below the strip displaying\n * the heading (label `HDG`, unit `DEG`). The value color follows the HDG\n * entry in `priorityElements`.\n * @property label - Readout label. Default `HDG`.\n * @availableWhen label hasReadout==true\n * @property unit - Readout unit. Default `DEG`.\n * @availableWhen unit hasReadout==true\n * @property fractionDigits - Number of fraction digits shown in the readout. Default `0`.\n * @availableWhen fractionDigits hasReadout==true\n * @stable\n */\n@customElement('obc-compass-flat')\nexport class ObcCompassFlat extends LitElement {\n @property({type: Boolean}) FOVIndicator: boolean = false;\n @property({type: Number}) heading = 0;\n @property({type: Number}) courseOverGround = 0;\n @property({type: Number}) tickInterval = 5;\n @property({type: Number}) FOV = 45;\n @property({type: Number}) minFOV = 45;\n @property({type: Number}) maxFOV = 180;\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: Array, attribute: false})\n priorityElements: CompassFlatPriorityElement[] = [\n CompassFlatPriorityElement.hdg,\n ];\n @property({type: String}) rotType: RotType | undefined;\n @property({type: Number}) rateOfTurnDegreesPerMinute: number | undefined;\n @property({type: Number}) rotDotAnimationFactor: number = 18;\n /**\n * **Deprecated.** Use `rateOfTurnDegreesPerMinute` instead.\n * @deprecated Use `rateOfTurnDegreesPerMinute` instead.\n * @availableWhen rotType!=undefined\n */\n @property({type: Number}) rotationsPerMinute: number = 1;\n @property({type: Number}) rotMaxValue: number = 60;\n @property({type: Number}) rotArcExtent: number = 60;\n @property({type: Boolean}) rotPortStarboard: boolean = false;\n @property({type: Number}) rotAtZeroDeadband: number = ROT_ZERO_DEADBAND_DEG;\n @property({type: Boolean}) hasReadout: boolean = false;\n @property({type: String}) label = 'HDG';\n @property({type: String}) unit = 'DEG';\n @property({type: Number}) fractionDigits = 0;\n\n @state() private hostWidth = 0;\n @state() private hostHeight = 0;\n\n private resizeObserver: ResizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n this.hostWidth = entry.contentRect.width;\n this.hostHeight = entry.contentRect.height;\n }\n });\n\n /** Vertical space reserved for the readout block below the strip. */\n private static readonly READOUT_RESERVE_PX = 56;\n\n private get stripHeight(): number {\n return (\n this.hostHeight -\n (this.hasReadout ? ObcCompassFlat.READOUT_RESERVE_PX : 0)\n );\n }\n\n // Made by chatGPT so that the text is inside the wrapper\n private get maxContainerWidth(): number {\n return -125.36 + 3.79 * this.stripHeight;\n }\n\n private get containerWidth(): number {\n return Math.min(this.hostWidth, this.maxContainerWidth);\n }\n\n override connectedCallback() {\n super.connectedCallback();\n this.resizeObserver.observe(this);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.resizeObserver.unobserve(this);\n }\n\n generateLabels() {\n if (this.containerWidth < 192) {\n return [];\n } else if (this.containerWidth <= 300) {\n return [\n {x: -180, y: LabelPosition.top, text: 'S'},\n {x: -90, y: LabelPosition.top, text: 'W'},\n {x: 0, y: LabelPosition.top, text: 'N'},\n {x: 90, y: LabelPosition.top, text: 'E'},\n {x: 180, y: LabelPosition.top, text: 'S'},\n {x: 270, y: LabelPosition.top, text: 'W'},\n {x: 360, y: LabelPosition.top, text: 'N'},\n {x: 450, y: LabelPosition.top, text: 'E'},\n {x: 540, y: LabelPosition.top, text: 'S'},\n ];\n } else {\n return [\n {x: -180, y: LabelPosition.top, text: 'S'},\n {x: -135, y: LabelPosition.top, text: 'SW'},\n {x: -90, y: LabelPosition.top, text: 'W'},\n {x: -45, y: LabelPosition.top, text: 'NW'},\n {x: 0, y: LabelPosition.top, text: 'N'},\n {x: 45, y: LabelPosition.top, text: 'NE'},\n {x: 90, y: LabelPosition.top, text: 'E'},\n {x: 135, y: LabelPosition.top, text: 'SE'},\n {x: 180, y: LabelPosition.top, text: 'S'},\n {x: 225, y: LabelPosition.top, text: 'SW'},\n {x: 270, y: LabelPosition.top, text: 'W'},\n {x: 315, y: LabelPosition.top, text: 'NW'},\n {x: 360, y: LabelPosition.top, text: 'N'},\n {x: 405, y: LabelPosition.top, text: 'NE'},\n {x: 450, y: LabelPosition.top, text: 'E'},\n {x: 495, y: LabelPosition.top, text: 'SE'},\n {x: 540, y: LabelPosition.top, text: 'S'},\n ];\n }\n }\n\n private generateIntervalTickmarks(scale: number): Tickmark[] {\n const tickmarks: Tickmark[] = [];\n let cardinalInterval = 90;\n\n if (this.containerWidth > 300) {\n cardinalInterval = 45;\n } else if (this.containerWidth < 192) {\n cardinalInterval = 0;\n }\n\n // Guard against zero/negative/non-finite interval to prevent infinite loops\n if (\n !this.tickInterval ||\n this.tickInterval <= 0 ||\n !Number.isFinite(this.tickInterval)\n ) {\n return tickmarks;\n }\n\n for (\n let angle = -180;\n angle < this.maxFOV * 3;\n angle += this.tickInterval\n ) {\n if (cardinalInterval !== 0 && angle % cardinalInterval === 0) {\n continue;\n }\n tickmarks.push({angle: angle * scale, type: TickmarkType.secondary});\n }\n\n return tickmarks;\n }\n\n private generateCardinalTickmarks(\n scale: number,\n labels: Label[]\n ): Tickmark[] {\n const tickmarks: Tickmark[] = [];\n\n for (const label of labels) {\n tickmarks.push({angle: label.x * scale, type: TickmarkType.main});\n }\n\n return tickmarks;\n }\n\n private generateTickmarks(scale: number, labels: Label[]): Tickmark[] {\n return [\n ...this.generateCardinalTickmarks(scale, labels),\n ...this.generateIntervalTickmarks(scale),\n ];\n }\n\n private renderFOVIndicator(): SVGTemplateResult[] {\n const indicators: SVGTemplateResult[] = [];\n\n const maxAdjustment = 10;\n const minContainerWidth = 300;\n const maxContainerWidth = 512;\n\n let yAdjustment = 0;\n if (this.containerWidth < maxContainerWidth) {\n const widthRange = maxContainerWidth - minContainerWidth;\n const scaleFactor =\n (maxContainerWidth - this.containerWidth) / widthRange;\n yAdjustment = scaleFactor * maxAdjustment;\n }\n\n const y = LabelPosition.bottom + yAdjustment + (this.rotType ? 12 : 0);\n\n indicators.push(svg`\n <text x=\"-175\" y=${y} class=\"label left\" fill=${LabelStyle.regular}>\n ${-this.FOV}\\u00B0\n </text>`);\n\n indicators.push(svg`\n <text x=\"0\" y=${y} class=\"label\" fill=${LabelStyle.regular}>\n ${this.heading}\\u00B0\n </text>`);\n\n indicators.push(svg`\n <text x=\"175\" y=${y} class=\"label right\" fill=${LabelStyle.regular}>\n ${this.FOV}\\u00B0\n </text>`);\n\n return indicators;\n }\n\n private priorityFor(element: CompassFlatPriorityElement): Priority {\n const selected = Array.isArray(this.priorityElements)\n ? this.priorityElements\n : [];\n return selected.includes(element) ? this.priority : Priority.regular;\n }\n\n private get _effectiveRotDegPerMin(): number {\n return this.rateOfTurnDegreesPerMinute ?? this.rotationsPerMinute;\n }\n\n private arrowColorFor(element: CompassFlatPriorityElement): string {\n return this.priorityFor(element) === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n }\n\n private get HDGSvg(): SVGTemplateResult {\n return svg`<g transform=\"translate(-24, -74)\">\n <path d=\"M36.7011 44.1445L36.6898 44.1379L36.6781 44.1318L24.2301 37.6823L24.0001 37.5631L23.7701 37.6823L11.3221 44.1318L11.3104 44.1379L11.2991 44.1445C9.25497 45.3438 6.78661 43.308 7.68828 41.0919L22.6036 4.43285C23.1096 3.18905 24.8906 3.18905 25.3967 4.43284L40.3119 41.0919C41.2136 43.308 38.7452 45.3438 36.7011 44.1445Z\" fill=\"${this.arrowColorFor(CompassFlatPriorityElement.hdg)}\" stroke=\"var(--border-silhouette-color)\"/>\n </g>`;\n }\n\n private COGSvg(translation: number): SVGTemplateResult {\n return svg`\n <g transform=\"translate(${-24 + translation}, -74)\">\n <path d=\"M31.9025 36.0262L33.1068 36.6502L32.5956 35.3938L24.4632 15.406L24.0001 14.2677L23.537 15.406L15.4046 35.3938L14.8935 36.6502L16.0978 36.0262L24.0001 31.9319L31.9025 36.0262ZM36.7011 44.1445L36.6898 44.1379L36.6781 44.1318L24.2301 37.6823L24.0001 37.5631L23.7701 37.6823L11.3221 44.1318L11.3104 44.1379L11.2991 44.1445C9.25497 45.3438 6.78661 43.308 7.68828 41.0919L22.6036 4.43285C23.1096 3.18905 24.8906 3.18905 25.3967 4.43284L40.3119 41.0919C41.2136 43.308 38.7452 45.3438 36.7011 44.1445Z\" fill=\"${this.arrowColorFor(CompassFlatPriorityElement.cog)}\" stroke=\"var(--border-silhouette-color)\"/>\n </g>\n `;\n }\n\n override render() {\n let angleDiff = this.courseOverGround - this.heading;\n\n if (angleDiff > this.maxFOV) {\n angleDiff -= 360;\n } else if (angleDiff < -this.maxFOV) {\n angleDiff += 360;\n }\n\n this.FOV = Math.max(this.minFOV, Math.abs(angleDiff));\n\n const baseOffset = 5;\n const translationScale = (baseOffset * 35) / this.FOV;\n\n const translation = angleDiff * translationScale;\n const labels = this.generateLabels();\n const tickmarks = this.generateTickmarks(translationScale, labels);\n const scaledLabels = labels.map((l) => ({\n ...l,\n x: l.x * translationScale,\n }));\n\n const hasBottomBar = !!this.rotType;\n const arrowViewBoxY = hasBottomBar\n ? -128 + Math.round((12 * 384) / 352)\n : -128;\n const viewBox = `-192 ${arrowViewBoxY} 384 128`;\n\n const strip = html`\n <div class=\"container\" style=\"max-width:${this.maxContainerWidth}px\">\n <obc-watch-flat\n .FOVIndicator=${this.FOVIndicator ? this.renderFOVIndicator() : []}\n .labels=${scaledLabels}\n .rotation=${this.heading}\n .tickmarks=${tickmarks}\n .tickmarkSpacing=${translationScale}\n .bottomBar=${!!this.rotType}\n .rotType=${this.rotType}\n .rotStartX=${0}\n .rotEndX=${\n (this._effectiveRotDegPerMin / (this.rotMaxValue || 1)) *\n this.rotArcExtent *\n translationScale\n }\n .rotDotSpacing=${LINEAR_DOT_ANGLE_SPACING * translationScale}\n .rateOfTurnDegreesPerMinute=${this.rateOfTurnDegreesPerMinute}\n .rotDotAnimationFactor=${this.rotDotAnimationFactor}\n .rotationsPerMinute=${this.rotationsPerMinute}\n .rotPriority=${this.priorityFor(CompassFlatPriorityElement.rot)}\n .rotPortStarboard=${this.rotPortStarboard}\n .rotAtZeroDeadband=${this.rotAtZeroDeadband * translationScale}\n ></obc-watch-flat>\n <svg viewBox=${viewBox} xmlns=\"http://www.w3.org/2000/svg\">\n ${this.HDGSvg}${this.COGSvg(translation)}\n </svg>\n </div>\n `;\n\n if (!this.hasReadout) {\n return strip;\n }\n return html`\n <div class=\"with-readout\">\n ${strip}\n <div class=\"flat-readout\">\n ${renderCenterReadouts([\n {\n value: this.heading,\n label: this.label,\n unit: this.unit,\n fractionDigits: this.fractionDigits,\n size: ReadoutSize.large,\n priority: this.priorityFor(CompassFlatPriorityElement.hdg),\n centerValue: true,\n centerMeta: true,\n },\n ])}\n </div>\n </div>\n `;\n }\n\n static override styles = [\n unsafeCSS(instrumentReadoutStyle),\n centerReadoutStyles,\n unsafeCSS(componentStyle),\n ];\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-compass-flat': ObcCompassFlat;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"compass-flat.js","names":[],"sources":["../../../src/navigation-instruments/compass-flat/compass-flat.ts"],"sourcesContent":["import {LitElement, html, svg, SVGTemplateResult, unsafeCSS} from 'lit';\nimport componentStyle from './compass-flat.css?inline';\nimport instrumentReadoutStyle from '../readout/instrument-readout.css?inline';\nimport {\n centerReadoutStyles,\n renderCenterReadouts,\n} from '../readout/center-readout.js';\nimport {ReadoutSize} from '../readout/readout.js';\nimport {property, state} from 'lit/decorators.js';\nimport {Tickmark, TickmarkType} from '../watch-flat/tickmark-flat.js';\nimport '../watch-flat/watch-flat.js';\nimport {customElement} from '../../decorator.js';\nimport {Priority} from '../types.js';\nimport {\n RotType,\n LINEAR_DOT_ANGLE_SPACING,\n ROT_ZERO_DEADBAND_DEG,\n} from '../rate-of-turn/rot-renderer.js';\n\nexport {RotType};\n\nexport enum CompassFlatPriorityElement {\n hdg = 'hdg',\n cog = 'cog',\n rot = 'rot',\n}\n\nexport enum LabelPosition {\n top = -45,\n bottom = 50,\n}\n\nexport enum LabelStyle {\n regular = 'var(--instrument-tick-mark-secondary-color)',\n}\n\nexport interface Label {\n x: number;\n y: LabelPosition;\n text: string;\n}\n\n/**\n * `<obc-compass-flat>` — Horizontal strip compass with HDG/COG arrows and optional rate-of-turn indicator.\n *\n * Renders a flat (non-circular) compass strip that scrolls horizontally to\n * display the current heading. A filled HDG arrow sits at the center while a\n * hollow COG arrow is positioned at the angular difference. The strip\n * auto-scales its field of view so both arrows remain visible. Cardinal\n * labels (N, S, E, W) and tickmarks are generated at densities responsive to\n * the strip width.\n *\n * ## Features\n *\n * - **Auto-scaling FOV**: The visible compass range widens to keep both HDG\n * and COG arrows in view, bounded by `minFOV` / `maxFOV`.\n * - **HDG / COG arrows**: Filled (HDG) and hollow (COG) arrow SVGs positioned\n * along the strip.\n * - **FOV indicator**: Optional numeric labels showing the port/starboard\n * bearing range and current heading.\n * - **Rate of turn**: Animated linear ROT indicator (spinning dots or\n * horizontal bar) via `rotType`. When enabled, a bottom bar is added to\n * the strip to house the indicator.\n * - **Readout**: `hasReadout` shows a centered heading readout below the\n * strip (label `HDG`, unit `DEG` by default).\n * - **Color priority**: Per-element priority for HDG, COG, and ROT via\n * `priorityElements`.\n *\n *\n * @ignition-base-height: 170px\n * @ignition-base-width: 512px\n *\n * @property heading - Current heading in degrees.\n * @property courseOverGround - Current COG in degrees.\n * @property rotType - ROT display mode: `'dots'`, `'bar'`, or `undefined` (hidden).\n * @property rateOfTurnDegreesPerMinute - Measured rate of turn in degrees per minute (positive = starboard).\n * When `undefined`, falls back to the deprecated `rotationsPerMinute`.\n * @availableWhen rateOfTurnDegreesPerMinute rotType!=undefined\n * @property rotDotAnimationFactor - Visual amplification applied only to the spinning dot animation.\n * @availableWhen rotDotAnimationFactor rotType!=undefined\n * @property rotMaxValue - Bar-extent reference value in **degrees per minute**. Default `60`\n * per ES-TRIN 2025/1 Art. 3.02.\n * @availableWhen rotMaxValue rotType!=undefined\n * @property rotArcExtent - Degrees of bar arc per max-value ROT (default 60).\n * @availableWhen rotArcExtent rotType!=undefined\n * @availableWhen rotPortStarboard rotType!=undefined\n * @availableWhen rotAtZeroDeadband rotType!=undefined\n * @property hasReadout - When `true`, shows a centered `<obc-readout>` below the strip displaying\n * the heading (label `HDG`, unit `DEG`). The value color follows the HDG\n * entry in `priorityElements`.\n * @property label - Readout label. Default `HDG`.\n * @availableWhen label hasReadout==true\n * @property unit - Readout unit. Default `DEG`.\n * @availableWhen unit hasReadout==true\n * @property fractionDigits - Number of fraction digits shown in the readout. Default `0`.\n * @availableWhen fractionDigits hasReadout==true\n * @stable\n */\n@customElement('obc-compass-flat')\nexport class ObcCompassFlat extends LitElement {\n @property({type: Boolean}) FOVIndicator: boolean = false;\n @property({type: Number}) heading = 0;\n @property({type: Number}) courseOverGround = 0;\n @property({type: Number}) tickInterval = 5;\n @property({type: Number}) FOV = 45;\n @property({type: Number}) minFOV = 45;\n @property({type: Number}) maxFOV = 180;\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: Array, attribute: false})\n priorityElements: CompassFlatPriorityElement[] = [\n CompassFlatPriorityElement.hdg,\n ];\n @property({type: String}) rotType: RotType | undefined;\n @property({type: Number}) rateOfTurnDegreesPerMinute: number | undefined;\n @property({type: Number}) rotDotAnimationFactor: number = 18;\n /**\n * **Deprecated.** Use `rateOfTurnDegreesPerMinute` instead.\n * @deprecated Use `rateOfTurnDegreesPerMinute` instead.\n * @availableWhen rotType!=undefined\n */\n @property({type: Number}) rotationsPerMinute: number = 0;\n @property({type: Number}) rotMaxValue: number = 60;\n @property({type: Number}) rotArcExtent: number = 60;\n @property({type: Boolean}) rotPortStarboard: boolean = false;\n @property({type: Number}) rotAtZeroDeadband: number = ROT_ZERO_DEADBAND_DEG;\n @property({type: Boolean}) hasReadout: boolean = false;\n @property({type: String}) label = 'HDG';\n @property({type: String}) unit = 'DEG';\n @property({type: Number}) fractionDigits = 0;\n\n @state() private hostWidth = 0;\n @state() private hostHeight = 0;\n\n private resizeObserver: ResizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n this.hostWidth = entry.contentRect.width;\n this.hostHeight = entry.contentRect.height;\n }\n });\n\n /** Vertical space reserved for the readout block below the strip. */\n private static readonly READOUT_RESERVE_PX = 56;\n\n private get stripHeight(): number {\n return (\n this.hostHeight -\n (this.hasReadout ? ObcCompassFlat.READOUT_RESERVE_PX : 0)\n );\n }\n\n // Made by chatGPT so that the text is inside the wrapper\n private get maxContainerWidth(): number {\n return -125.36 + 3.79 * this.stripHeight;\n }\n\n private get containerWidth(): number {\n return Math.min(this.hostWidth, this.maxContainerWidth);\n }\n\n override connectedCallback() {\n super.connectedCallback();\n this.resizeObserver.observe(this);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.resizeObserver.unobserve(this);\n }\n\n generateLabels() {\n if (this.containerWidth < 192) {\n return [];\n } else if (this.containerWidth <= 300) {\n return [\n {x: -180, y: LabelPosition.top, text: 'S'},\n {x: -90, y: LabelPosition.top, text: 'W'},\n {x: 0, y: LabelPosition.top, text: 'N'},\n {x: 90, y: LabelPosition.top, text: 'E'},\n {x: 180, y: LabelPosition.top, text: 'S'},\n {x: 270, y: LabelPosition.top, text: 'W'},\n {x: 360, y: LabelPosition.top, text: 'N'},\n {x: 450, y: LabelPosition.top, text: 'E'},\n {x: 540, y: LabelPosition.top, text: 'S'},\n ];\n } else {\n return [\n {x: -180, y: LabelPosition.top, text: 'S'},\n {x: -135, y: LabelPosition.top, text: 'SW'},\n {x: -90, y: LabelPosition.top, text: 'W'},\n {x: -45, y: LabelPosition.top, text: 'NW'},\n {x: 0, y: LabelPosition.top, text: 'N'},\n {x: 45, y: LabelPosition.top, text: 'NE'},\n {x: 90, y: LabelPosition.top, text: 'E'},\n {x: 135, y: LabelPosition.top, text: 'SE'},\n {x: 180, y: LabelPosition.top, text: 'S'},\n {x: 225, y: LabelPosition.top, text: 'SW'},\n {x: 270, y: LabelPosition.top, text: 'W'},\n {x: 315, y: LabelPosition.top, text: 'NW'},\n {x: 360, y: LabelPosition.top, text: 'N'},\n {x: 405, y: LabelPosition.top, text: 'NE'},\n {x: 450, y: LabelPosition.top, text: 'E'},\n {x: 495, y: LabelPosition.top, text: 'SE'},\n {x: 540, y: LabelPosition.top, text: 'S'},\n ];\n }\n }\n\n private generateIntervalTickmarks(scale: number): Tickmark[] {\n const tickmarks: Tickmark[] = [];\n let cardinalInterval = 90;\n\n if (this.containerWidth > 300) {\n cardinalInterval = 45;\n } else if (this.containerWidth < 192) {\n cardinalInterval = 0;\n }\n\n // Guard against zero/negative/non-finite interval to prevent infinite loops\n if (\n !this.tickInterval ||\n this.tickInterval <= 0 ||\n !Number.isFinite(this.tickInterval)\n ) {\n return tickmarks;\n }\n\n for (\n let angle = -180;\n angle < this.maxFOV * 3;\n angle += this.tickInterval\n ) {\n if (cardinalInterval !== 0 && angle % cardinalInterval === 0) {\n continue;\n }\n tickmarks.push({angle: angle * scale, type: TickmarkType.secondary});\n }\n\n return tickmarks;\n }\n\n private generateCardinalTickmarks(\n scale: number,\n labels: Label[]\n ): Tickmark[] {\n const tickmarks: Tickmark[] = [];\n\n for (const label of labels) {\n tickmarks.push({angle: label.x * scale, type: TickmarkType.main});\n }\n\n return tickmarks;\n }\n\n private generateTickmarks(scale: number, labels: Label[]): Tickmark[] {\n return [\n ...this.generateCardinalTickmarks(scale, labels),\n ...this.generateIntervalTickmarks(scale),\n ];\n }\n\n private renderFOVIndicator(): SVGTemplateResult[] {\n const indicators: SVGTemplateResult[] = [];\n\n const maxAdjustment = 10;\n const minContainerWidth = 300;\n const maxContainerWidth = 512;\n\n let yAdjustment = 0;\n if (this.containerWidth < maxContainerWidth) {\n const widthRange = maxContainerWidth - minContainerWidth;\n const scaleFactor =\n (maxContainerWidth - this.containerWidth) / widthRange;\n yAdjustment = scaleFactor * maxAdjustment;\n }\n\n const y = LabelPosition.bottom + yAdjustment + (this.rotType ? 12 : 0);\n\n indicators.push(svg`\n <text x=\"-175\" y=${y} class=\"label left\" fill=${LabelStyle.regular}>\n ${-this.FOV}\\u00B0\n </text>`);\n\n indicators.push(svg`\n <text x=\"0\" y=${y} class=\"label\" fill=${LabelStyle.regular}>\n ${this.heading}\\u00B0\n </text>`);\n\n indicators.push(svg`\n <text x=\"175\" y=${y} class=\"label right\" fill=${LabelStyle.regular}>\n ${this.FOV}\\u00B0\n </text>`);\n\n return indicators;\n }\n\n private priorityFor(element: CompassFlatPriorityElement): Priority {\n const selected = Array.isArray(this.priorityElements)\n ? this.priorityElements\n : [];\n return selected.includes(element) ? this.priority : Priority.regular;\n }\n\n private get _effectiveRotDegPerMin(): number {\n return this.rateOfTurnDegreesPerMinute ?? this.rotationsPerMinute;\n }\n\n private arrowColorFor(element: CompassFlatPriorityElement): string {\n return this.priorityFor(element) === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n }\n\n private get HDGSvg(): SVGTemplateResult {\n return svg`<g transform=\"translate(-24, -74)\">\n <path d=\"M36.7011 44.1445L36.6898 44.1379L36.6781 44.1318L24.2301 37.6823L24.0001 37.5631L23.7701 37.6823L11.3221 44.1318L11.3104 44.1379L11.2991 44.1445C9.25497 45.3438 6.78661 43.308 7.68828 41.0919L22.6036 4.43285C23.1096 3.18905 24.8906 3.18905 25.3967 4.43284L40.3119 41.0919C41.2136 43.308 38.7452 45.3438 36.7011 44.1445Z\" fill=\"${this.arrowColorFor(CompassFlatPriorityElement.hdg)}\" stroke=\"var(--border-silhouette-color)\"/>\n </g>`;\n }\n\n private COGSvg(translation: number): SVGTemplateResult {\n return svg`\n <g transform=\"translate(${-24 + translation}, -74)\">\n <path d=\"M31.9025 36.0262L33.1068 36.6502L32.5956 35.3938L24.4632 15.406L24.0001 14.2677L23.537 15.406L15.4046 35.3938L14.8935 36.6502L16.0978 36.0262L24.0001 31.9319L31.9025 36.0262ZM36.7011 44.1445L36.6898 44.1379L36.6781 44.1318L24.2301 37.6823L24.0001 37.5631L23.7701 37.6823L11.3221 44.1318L11.3104 44.1379L11.2991 44.1445C9.25497 45.3438 6.78661 43.308 7.68828 41.0919L22.6036 4.43285C23.1096 3.18905 24.8906 3.18905 25.3967 4.43284L40.3119 41.0919C41.2136 43.308 38.7452 45.3438 36.7011 44.1445Z\" fill=\"${this.arrowColorFor(CompassFlatPriorityElement.cog)}\" stroke=\"var(--border-silhouette-color)\"/>\n </g>\n `;\n }\n\n override render() {\n let angleDiff = this.courseOverGround - this.heading;\n\n if (angleDiff > this.maxFOV) {\n angleDiff -= 360;\n } else if (angleDiff < -this.maxFOV) {\n angleDiff += 360;\n }\n\n this.FOV = Math.max(this.minFOV, Math.abs(angleDiff));\n\n const baseOffset = 5;\n const translationScale = (baseOffset * 35) / this.FOV;\n\n const translation = angleDiff * translationScale;\n const labels = this.generateLabels();\n const tickmarks = this.generateTickmarks(translationScale, labels);\n const scaledLabels = labels.map((l) => ({\n ...l,\n x: l.x * translationScale,\n }));\n\n const hasBottomBar = !!this.rotType;\n const arrowViewBoxY = hasBottomBar\n ? -128 + Math.round((12 * 384) / 352)\n : -128;\n const viewBox = `-192 ${arrowViewBoxY} 384 128`;\n\n const strip = html`\n <div class=\"container\" style=\"max-width:${this.maxContainerWidth}px\">\n <obc-watch-flat\n .FOVIndicator=${this.FOVIndicator ? this.renderFOVIndicator() : []}\n .labels=${scaledLabels}\n .rotation=${this.heading}\n .tickmarks=${tickmarks}\n .tickmarkSpacing=${translationScale}\n .bottomBar=${!!this.rotType}\n .rotType=${this.rotType}\n .rotStartX=${0}\n .rotEndX=${\n (this._effectiveRotDegPerMin / (this.rotMaxValue || 1)) *\n this.rotArcExtent *\n translationScale\n }\n .rotDotSpacing=${LINEAR_DOT_ANGLE_SPACING * translationScale}\n .rateOfTurnDegreesPerMinute=${this.rateOfTurnDegreesPerMinute}\n .rotDotAnimationFactor=${this.rotDotAnimationFactor}\n .rotationsPerMinute=${this.rotationsPerMinute}\n .rotPriority=${this.priorityFor(CompassFlatPriorityElement.rot)}\n .rotPortStarboard=${this.rotPortStarboard}\n .rotAtZeroDeadband=${this.rotAtZeroDeadband * translationScale}\n ></obc-watch-flat>\n <svg viewBox=${viewBox} xmlns=\"http://www.w3.org/2000/svg\">\n ${this.HDGSvg}${this.COGSvg(translation)}\n </svg>\n </div>\n `;\n\n if (!this.hasReadout) {\n return strip;\n }\n return html`\n <div class=\"with-readout\">\n ${strip}\n <div class=\"flat-readout\">\n ${renderCenterReadouts([\n {\n value: this.heading,\n label: this.label,\n unit: this.unit,\n fractionDigits: this.fractionDigits,\n size: ReadoutSize.large,\n priority: this.priorityFor(CompassFlatPriorityElement.hdg),\n centerValue: true,\n centerMeta: true,\n },\n ])}\n </div>\n </div>\n `;\n }\n\n static override styles = [\n unsafeCSS(instrumentReadoutStyle),\n centerReadoutStyles,\n unsafeCSS(componentStyle),\n ];\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-compass-flat': ObcCompassFlat;\n }\n}\n"],"mappings":"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"}
@@ -88,7 +88,7 @@ var ObcCompassSector = class ObcCompassSector extends LitElement {
88
88
  this.minFOV = 30;
89
89
  this.rotPosition = RotPosition.innerCircle;
90
90
  this.rotDotAnimationFactor = 18;
91
- this.rotationsPerMinute = 1;
91
+ this.rotationsPerMinute = 0;
92
92
  this.rotMaxValue = 60;
93
93
  this.rotPortStarboard = false;
94
94
  this.rotAtZeroDeadband = ROT_ZERO_DEADBAND_DEG;
@@ -1 +1 @@
1
- {"version":3,"file":"compass-sector.js","names":[],"sources":["../../../src/navigation-instruments/compass-sector/compass-sector.ts"],"sourcesContent":["import {LitElement, PropertyValues, html, svg, unsafeCSS, nothing} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport {ResizeController} from '@lit-labs/observers/resize-controller.js';\nimport componentStyle from './compass-sector.css?inline';\nimport '../watch/watch.js';\nimport {\n centerReadoutStyles,\n renderCenterReadouts,\n} from '../readout/center-readout.js';\nimport {ReadoutSize} from '../readout/readout.js';\nimport instrumentReadoutStyle from '../readout/instrument-readout.css?inline';\nimport {Tickmark, TickmarkType, TickmarkStyle} from '../watch/tickmark.js';\nimport {arrow, ArrowStyle} from '../compass/arrow.js';\nimport {AdviceState, AngleAdvice, AngleAdviceRaw} from '../watch/advice.js';\nimport {\n WatchCircleType,\n WatchArea,\n OUTER_RING_RADIUS,\n innerRingRadiusFor,\n RotType,\n RotPosition,\n} from '../watch/watch.js';\nimport {SetpointBundle} from '../../svghelpers/setpoint-bundle.js';\nimport {\n computeRadialFrame,\n estimateLabelWidthPx,\n measureContainerPx,\n observeInnerBox,\n type RadialFrame,\n} from '../../svghelpers/radial-frame.js';\nimport {ROT_ZERO_DEADBAND_DEG} from '../rate-of-turn/rot-renderer.js';\nimport {customElement} from '../../decorator.js';\nimport {degToRad, normalizeAngle} from '../../svghelpers/math.js';\nimport {InstrumentState, Priority} from '../types.js';\nexport {RotType, RotPosition};\n\nexport enum CompassSectorPriorityElement {\n hdg = 'hdg',\n cog = 'cog',\n rot = 'rot',\n}\n\n/** The sector instruments' shared box: 40 units around the outer ring. */\nconst BASE_PADDING = 48;\n/**\n * Bottom crops, % of the 448 box, one per feature that hangs below the band.\n * Each leaves its lowest paint the same 40-unit margin the ring gets above —\n * `100 · (184 − y) / 448` — and `_clipBottom()` takes the deepest in play.\n * The band's own floor is the filleted inner corner at ±60°, y ≈ −47.\n */\nconst SECTOR_CLIP_BOTTOM = 51.5;\n/** ROT dots and bar ride the inner-circle track, ~5 units below the band. */\nconst SECTOR_CLIP_BOTTOM_ROT = 50.4;\n/** Inside labels sit under the inner ring, far below the band's floor. */\nconst SECTOR_CLIP_BOTTOM_INSIDE_LABELS = 47.3;\n/** The readout is px-sized, so it needs the room obc-gauge-radial's 180° sector reserves. */\nconst SECTOR_CLIP_BOTTOM_READOUT = 44;\n/**\n * Clearance the zoom flattens the arc out to. Zero puts the arc ends on the\n * side edges, which is the point of zooming; the un-zoomed 120° arc stops\n * well short of them because the box keeps the whole circle's width.\n */\nconst SECTOR_SIDE_MARGIN = 0;\nconst WATCH_TYPE = WatchCircleType.triple;\nconst INNER_RADIUS = innerRingRadiusFor(WATCH_TYPE);\n/** Half of the fixed 120° arc on the watch face. */\nconst ARC_HALF_EXTENT = 60;\nconst SCALE_INNER_RADIUS = innerRingRadiusFor(WatchCircleType.single);\n\ninterface TickDensity {\n mainInterval: number;\n primaryInterval: number;\n secondaryInterval: number | undefined;\n}\n\nfunction tickDensityForFOV(fov: number): TickDensity {\n if (fov <= 30) {\n return {mainInterval: 10, primaryInterval: 5, secondaryInterval: 1};\n } else if (fov <= 60) {\n return {mainInterval: 10, primaryInterval: 5, secondaryInterval: undefined};\n } else if (fov <= 120) {\n return {mainInterval: 30, primaryInterval: 10, secondaryInterval: 5};\n } else {\n return {mainInterval: 90, primaryInterval: 30, secondaryInterval: 10};\n }\n}\n\n/**\n * `<obc-compass-sector>` — Curved compass strip that auto‑scales to keep HDG and COG visible.\n *\n * Renders a fixed 120° arc of a triple‑ring compass face. The visible\n * compass range (field of view) adjusts automatically so that both the\n * heading (HDG) and course‑over‑ground (COG) arrows are always in view.\n * This is the radial equivalent of `<obc-compass-flat>`: the arc shape\n * never changes — only the scale (compass‑degrees per arc‑degree) changes.\n *\n * ## Features\n *\n * - **Fixed 120° arc**: The watch face is always a 120° sector (±60° from\n * center), identical in shape to `<obc-rot-sector>`.\n * - **FOV auto‑scaling**: The field of view widens when the HDG–COG\n * angular difference grows, compressing more compass degrees into the\n * fixed arc. Tickmark density adjusts automatically.\n * - **HDG / COG arrows**: Solid (HDG) arrow is always at the arc center.\n * Hollow (COG) arrow is positioned proportionally within the arc.\n * - **North arrow**: A gray triangle in the outer scale band indicates\n * north when it falls within the visible FOV.\n * - **Heading setpoint**: Optional setpoint marker with auto at‑setpoint\n * detection and confirm animation, positioned at the mapped arc angle.\n * - **Advice zones**: Pass `headingAdvices` to render caution/alert arcs,\n * mapped into the scaled arc.\n * - **Rate of turn**: Animated ROT indicator (dots or bar) spanning from\n * HDG to the mapped COG position, clipped to the arc.\n * - **Zoom to fit**: When `zoomToFitArc` is `true`, the fixed 120° arc is\n * enlarged to fill the available space.\n *\n * ## Usage Guidelines\n *\n * - Set `heading` and `courseOverGround` to sensor values in degrees.\n * - Adjust `minFOV` to control the minimum zoom level (default 30°).\n * - Enable `zoomToFitArc` to enlarge the arc to fill the viewport.\n * - For a full‑circle compass, use `<obc-compass>` instead.\n *\n * @availableWhen newHeadingSetpoint headingSetpoint!=null\n * @availableWhen atHeadingSetpoint headingSetpoint!=null && autoAtHeadingSetpoint==false\n * @availableWhen headingSetpointAtZeroDeadband headingSetpoint!=null\n * @availableWhen headingSetpointOverride headingSetpoint!=null\n * @availableWhen autoAtHeadingSetpoint headingSetpoint!=null\n * @availableWhen autoAtHeadingSetpointDeadband headingSetpoint!=null && autoAtHeadingSetpoint==true\n * @availableWhen animateSetpoint headingSetpoint!=null\n * @availableWhen touching headingSetpoint!=null\n * @availableWhen rotPosition rotType!=undefined\n * @property rateOfTurnDegreesPerMinute - Measured rate of turn in degrees per minute (positive = starboard).\n * Drives both the bar extent and (after multiplication by\n * `rotDotAnimationFactor`) the spinning dot animation. When `undefined`,\n * falls back to the deprecated `rotationsPerMinute`.\n * @availableWhen rateOfTurnDegreesPerMinute rotType!=undefined\n * @property rotDotAnimationFactor - Visual amplification applied only to the spinning dot animation\n * (not to the bar extent). Default `18` keeps the legacy visual feel\n * (≈1 rpm at 20°/min).\n * @availableWhen rotDotAnimationFactor rotType!=undefined\n * @availableWhen rotPortStarboard rotType!=undefined\n * @availableWhen rotAtZeroDeadband rotType==bar\n * @availableWhen priorityElements priority==enhanced\n * @property hasReadout - When `true`, shows a centered `<obc-readout>` under the arc displaying the\n * heading (label `HDG`, unit `DEG`). The value color follows the HDG entry in\n * `priorityElements`, matching the HDG arrow.\n * @property label - Readout label. Default `HDG`.\n * @availableWhen label hasReadout==true\n * @property unit - Readout unit. Default `DEG`.\n * @availableWhen unit hasReadout==true\n * @property fractionDigits - Number of fraction digits shown in the readout. Default `0`.\n * @availableWhen fractionDigits hasReadout==true\n * @property rotMaxValue - Bar-extent reference value in degrees per minute: the bar fills the full\n * ±`ARC_HALF_EXTENT` arc when the measured rate of turn reaches\n * ±`rotMaxValue`. The default `60` aligns with ES-TRIN 2025/1 Art. 3.02.\n * @availableWhen rotMaxValue rotType==bar\n * @stable\n */\n@customElement('obc-compass-sector')\nexport class ObcCompassSector extends LitElement {\n @property({type: Number}) heading = 0;\n @property({type: Number}) courseOverGround = 0;\n\n @property({type: Number}) headingSetpoint: number | null = null;\n @property({type: Number}) newHeadingSetpoint: number | undefined;\n @property({type: Boolean}) atHeadingSetpoint: boolean = false;\n @property({type: Number}) headingSetpointAtZeroDeadband: number = 0.5;\n @property({type: Boolean}) headingSetpointOverride: boolean = false;\n @property({type: Boolean, attribute: false}) autoAtHeadingSetpoint = true;\n @property({type: Number}) autoAtHeadingSetpointDeadband: number = 2;\n @property({type: Boolean}) animateSetpoint: boolean = false;\n @property({type: Boolean}) touching: boolean = false;\n @property({type: Array, attribute: false}) headingAdvices: AngleAdvice[] = [];\n\n @property({type: Number}) minFOV: number = 30;\n\n @property({type: String}) rotType: RotType | undefined;\n @property({type: String}) rotPosition: RotPosition = RotPosition.innerCircle;\n @property({type: Number}) rateOfTurnDegreesPerMinute: number | undefined;\n @property({type: Number}) rotDotAnimationFactor: number = 18;\n /**\n * @deprecated Use `rateOfTurnDegreesPerMinute` (and optionally\n * `rotDotAnimationFactor`) instead. Takes effect only when\n * `rateOfTurnDegreesPerMinute` is `undefined`.\n * @availableWhen rotType!=undefined && rateOfTurnDegreesPerMinute==undefined\n */\n @property({type: Number}) rotationsPerMinute: number = 1;\n @property({type: Number}) rotMaxValue: number = 60;\n @property({type: Boolean}) rotPortStarboard: boolean = false;\n @property({type: Number}) rotAtZeroDeadband: number = ROT_ZERO_DEADBAND_DEG;\n\n @property({type: String}) state: InstrumentState = InstrumentState.active;\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: Array, attribute: false})\n priorityElements: CompassSectorPriorityElement[] = [\n CompassSectorPriorityElement.hdg,\n ];\n @property({type: Boolean}) tickmarksInside: boolean = false;\n @property({type: Boolean}) zoomToFitArc: boolean = false;\n @property({type: Boolean}) hasReadout: boolean = false;\n @property({type: String}) label = 'HDG';\n @property({type: String}) unit = 'DEG';\n @property({type: Number}) fractionDigits = 0;\n\n private _headingSp = new SetpointBundle({\n angularWraparound: true,\n onAnimationEnd: () => this.requestUpdate(),\n });\n\n // Cached computed values — updated in willUpdate()\n private _halfFOV = 30;\n private _arcHalfExtent = ARC_HALF_EXTENT;\n private _scale = 1;\n private _cachedAreas: WatchArea[] = [];\n private _cachedTickmarks: Tickmark[] = [];\n private _cachedAdvices: AngleAdviceRaw[] = [];\n\n // The frame follows the host box, which no property change announces.\n private _resizeController = new ResizeController(this, {});\n\n private _aspectWidth = 448;\n private _aspectHeight = 217.28;\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 this._headingSp.sync({\n setpoint: this.headingSetpoint ?? undefined,\n newSetpoint: this.newHeadingSetpoint,\n atSetpoint: this.atHeadingSetpoint,\n touching: this.touching,\n autoAtSetpoint: this.autoAtHeadingSetpoint,\n autoAtSetpointDeadband: this.autoAtHeadingSetpointDeadband,\n setpointAtZeroDeadband: this.headingSetpointAtZeroDeadband,\n setpointOverride: this.headingSetpointOverride,\n animateSetpoint: this.animateSetpoint,\n });\n\n const arcInputsChanged =\n changed.has('heading') ||\n changed.has('courseOverGround') ||\n changed.has('minFOV') ||\n changed.has('zoomToFitArc');\n\n if (arcInputsChanged) {\n let diff = this.courseOverGround - this.heading;\n if (diff > 180) diff -= 360;\n else if (diff < -180) diff += 360;\n const minFov = Math.max(1, this.minFOV);\n const MARGIN = 15;\n\n if (this.zoomToFitArc) {\n const needed = Math.max(minFov, Math.abs(diff) + MARGIN);\n if (needed <= ARC_HALF_EXTENT) {\n this._halfFOV = needed;\n this._arcHalfExtent = needed;\n this._scale = 1;\n } else {\n this._halfFOV = needed;\n this._arcHalfExtent = ARC_HALF_EXTENT;\n this._scale = ARC_HALF_EXTENT / needed;\n }\n } else {\n this._halfFOV = Math.max(minFov, Math.abs(diff));\n this._arcHalfExtent = ARC_HALF_EXTENT;\n this._scale = ARC_HALF_EXTENT / this._halfFOV;\n }\n\n this._cachedAreas = [\n {\n startAngle: this.heading - this._arcHalfExtent,\n endAngle: this.heading + this._arcHalfExtent,\n roundInsideCut: true,\n roundOutsideCut: true,\n },\n ];\n\n this._cachedTickmarks = this._buildTickmarks();\n }\n\n if (arcInputsChanged || changed.has('headingAdvices')) {\n this._cachedAdvices = this._buildAdvices();\n }\n }\n\n override updated(changed: PropertyValues): void {\n super.updated(changed);\n // The host IS the canvas, so its box follows the frame the render used.\n this.style.setProperty(\n '--obc-compass-sector-aspect',\n `${this._aspectWidth} / ${this._aspectHeight}`\n );\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._headingSp.dispose();\n }\n\n // ---------------------------------------------------------------------------\n // Angle mapping — maps compass degrees to arc positions\n // ---------------------------------------------------------------------------\n\n private _mapAngle(compassDeg: number): number {\n let diff = compassDeg - this.heading;\n if (diff > 180) diff -= 360;\n else if (diff < -180) diff += 360;\n return this.heading + diff * this._scale;\n }\n\n // ---------------------------------------------------------------------------\n // Areas — fixed 120° arc centered on heading (built in willUpdate)\n // ---------------------------------------------------------------------------\n\n // ---------------------------------------------------------------------------\n // Tickmarks — compass-degree labels at mapped arc positions\n // ---------------------------------------------------------------------------\n\n private _buildTickmarks(): Tickmark[] {\n const fov = this._halfFOV * 2;\n const {mainInterval, primaryInterval, secondaryInterval} =\n tickDensityForFOV(fov);\n const halfFov = this._halfFOV;\n const compassStart = this.heading - halfFov;\n const compassEnd = this.heading + halfFov;\n\n const tickmarks: Tickmark[] = [];\n const added = new Set<number>();\n\n const addTick = (\n arcAngle: number,\n type: TickmarkType,\n text?: string\n ): void => {\n const key = Math.round(arcAngle * 1000);\n if (added.has(key)) return;\n added.add(key);\n tickmarks.push({angle: arcAngle, type, text});\n };\n\n const step = secondaryInterval ?? primaryInterval;\n const firstTick = Math.ceil(compassStart / step) * step;\n\n for (\n let compassDeg = firstTick;\n compassDeg <= compassEnd;\n compassDeg += step\n ) {\n const norm = normalizeAngle(compassDeg);\n const arcAngle = this._mapAngle(compassDeg);\n const isMain = norm % mainInterval === 0;\n const isPrimary = norm % primaryInterval === 0;\n\n if (isMain) {\n addTick(arcAngle, TickmarkType.main, Math.round(norm).toString());\n } else if (isPrimary) {\n addTick(arcAngle, TickmarkType.primary);\n } else {\n addTick(arcAngle, TickmarkType.secondary);\n }\n }\n\n return tickmarks;\n }\n\n // ---------------------------------------------------------------------------\n // ViewBox\n // ---------------------------------------------------------------------------\n\n /** Deepest crop the active features need, so none of them is shaved. */\n private _clipBottom(): number {\n const clips = [SECTOR_CLIP_BOTTOM];\n if (\n this.rotType !== undefined &&\n this.rotPosition === RotPosition.innerCircle\n ) {\n clips.push(SECTOR_CLIP_BOTTOM_ROT);\n }\n if (this.tickmarksInside) {\n clips.push(SECTOR_CLIP_BOTTOM_INSIDE_LABELS);\n }\n if (this.hasReadout) {\n clips.push(SECTOR_CLIP_BOTTOM_READOUT);\n }\n return Math.min(...clips);\n }\n\n /**\n * One frame per render, feeding both layers: the shared sector box cropped\n * to the arc. Zoom enlarges the arc rather than the window, so the window\n * only slides outward by the radius offset and the canvas holds still.\n */\n private _buildFrame(): RadialFrame {\n const frame = computeRadialFrame({\n basePadding: BASE_PADDING,\n labelWidthPx: this.tickmarksInside\n ? 0\n : estimateLabelWidthPx(this._cachedTickmarks.map((t) => t.text)),\n clips: {top: 0, bottom: this._clipBottom(), left: 0, right: 0},\n containerPx: measureContainerPx(this),\n });\n\n if (!this.zoomToFitArc) {\n return frame;\n }\n\n // Flatten the arc until its ends reach the same margin the 120° arc keeps,\n // measured off the window rather than the live heading — a frame fitted\n // where the arc currently points swings its box around the circle and the\n // arc drifts with it (#1152).\n const halfWidth = frame.width / 2 - SECTOR_SIDE_MARGIN;\n const sin = Math.sin(degToRad(this._arcHalfExtent));\n const radiusOffset = Math.max(0, halfWidth / sin - OUTER_RING_RADIUS);\n const y = frame.y - radiusOffset;\n return {\n ...frame,\n y,\n radiusOffset,\n viewBox: `${frame.x} ${y} ${frame.width} ${frame.height}`,\n };\n }\n\n // ---------------------------------------------------------------------------\n // North arrow — rendered in overlay at mapped 0° position\n // ---------------------------------------------------------------------------\n\n private _renderNorthArrow(rOff: number) {\n let northOffset = -this.heading;\n if (northOffset > 180) northOffset -= 360;\n else if (northOffset < -180) northOffset += 360;\n if (Math.abs(northOffset) > this._halfFOV) return nothing;\n\n const northArcAngle = this._mapAngle(0);\n const radius = OUTER_RING_RADIUS + rOff;\n return svg`\n <g transform=\"rotate(${northArcAngle}) translate(0, ${-radius})\">\n <path fill-rule=\"evenodd\" clip-rule=\"evenodd\"\n d=\"M-17.8457 24.984 0 0 17.8458 24.984C11.9868 24.3338 6.0324 24 0 24-6.0323 24-11.9867 24.3338-17.8457 24.984Z\"\n fill=\"var(--instrument-frame-tertiary-color)\"/>\n </g>\n `;\n }\n\n // ---------------------------------------------------------------------------\n // Priority & advice helpers\n // ---------------------------------------------------------------------------\n\n private _angleInRange(value: number, min: number, max: number): boolean {\n const v = normalizeAngle(value);\n const start = normalizeAngle(min);\n const end = normalizeAngle(max);\n return start <= end ? v >= start && v <= end : v >= start || v <= end;\n }\n\n private _buildAdvices(): AngleAdviceRaw[] {\n return this.headingAdvices.map(({minAngle, maxAngle, hinted, type}) => {\n const state = this._angleInRange(this.heading, minAngle, maxAngle)\n ? AdviceState.triggered\n : hinted\n ? AdviceState.hinted\n : AdviceState.regular;\n return {\n minAngle: this._mapAngle(minAngle),\n maxAngle: this._mapAngle(maxAngle),\n type,\n state,\n };\n });\n }\n\n private get _effectiveRotDegPerMin(): number {\n return this.rateOfTurnDegreesPerMinute ?? this.rotationsPerMinute;\n }\n\n private get _rotEndAngle(): number {\n const maxVal = this.rotMaxValue || 1;\n const barCompassDeg =\n (this._effectiveRotDegPerMin / maxVal) * ARC_HALF_EXTENT;\n return this._mapAngle(this.heading + barCompassDeg);\n }\n\n private priorityFor(element: CompassSectorPriorityElement): Priority {\n const selected = Array.isArray(this.priorityElements)\n ? this.priorityElements\n : [];\n return selected.includes(element) ? this.priority : Priority.regular;\n }\n\n /**\n * Vertical placement of the readout as a percentage of the frame height.\n *\n * The anchor sits halfway down the inner radius, below the arc's inner edge.\n * Zoom pushes that edge out by the radius offset, so the anchor follows it\n * and the gap under the arc reads the same in both modes.\n */\n private _readoutTopPercent(frame: RadialFrame): number {\n if (!frame.height) {\n return 50;\n }\n const anchorY = -INNER_RADIUS * 0.5 - frame.radiusOffset;\n return Math.round(((anchorY - frame.y) / frame.height) * 1000) / 10;\n }\n\n // ---------------------------------------------------------------------------\n // Render\n // ---------------------------------------------------------------------------\n\n override render() {\n const rotation = -this.heading;\n const frame = this._buildFrame();\n const viewBox = frame.viewBox;\n const rOff = frame.radiusOffset;\n // The overlay rotates its element box like obc-watch does, so it needs the\n // same pivot: the cropped frame's centre is not the compass centre.\n const pivotX = (-frame.x / frame.width) * 100;\n const pivotY = (-frame.y / frame.height) * 100;\n this._aspectWidth = frame.width;\n this._aspectHeight = frame.height;\n const tickmarks = frame.labelsHidden\n ? this._cachedTickmarks.map((t) => ({...t, text: undefined}))\n : this._cachedTickmarks;\n\n const mappedCOG = this._mapAngle(this.courseOverGround);\n const mappedSetpoint =\n this.headingSetpoint != null\n ? this._mapAngle(this.headingSetpoint)\n : undefined;\n const mappedNewSetpoint =\n this.newHeadingSetpoint != null\n ? this._mapAngle(this.newHeadingSetpoint)\n : undefined;\n\n return html`\n <div class=\"container\">\n <obc-watch\n .touching=${this.touching}\n .advices=${this._cachedAdvices}\n .tickmarks=${tickmarks}\n .tickmarkStyle=${TickmarkStyle.regular}\n .tickmarksInside=${this.tickmarksInside}\n .state=${this.state}\n .watchCircleType=${WATCH_TYPE}\n .northArrow=${false}\n .areas=${this._cachedAreas}\n .arcFrame=${frame}\n .tickFadeAngle=${this._arcHalfExtent * 0.2}\n .rotation=${rotation}\n .angleSetpoint=${mappedSetpoint}\n .newAngleSetpoint=${mappedNewSetpoint}\n .atAngleSetpoint=${this._headingSp.computeAtSetpoint(this.heading)}\n .angleSetpointAtZeroDeadband=${this.headingSetpointAtZeroDeadband}\n .setpointOverride=${this.headingSetpointOverride}\n .priority=${this.priority}\n .animateSetpoint=${this.animateSetpoint}\n .rotType=${this.rotType}\n .rotPosition=${this.rotPosition}\n .rotStartAngle=${this.heading}\n .rotEndAngle=${this._rotEndAngle}\n .rotPriority=${this.priorityFor(CompassSectorPriorityElement.rot)}\n .rotPortStarboard=${this.rotPortStarboard}\n .rotAtZeroDeadband=${this.rotAtZeroDeadband}\n .rateOfTurnDegreesPerMinute=${this.rateOfTurnDegreesPerMinute}\n .rotDotAnimationFactor=${this.rotDotAnimationFactor}\n .rotationsPerMinute=${this.rotationsPerMinute}\n >\n </obc-watch>\n <svg\n viewBox=\"${viewBox}\"\n transform=\"rotate(${rotation})\"\n style=\"transform-origin: ${pivotX}% ${pivotY}%\"\n >\n <g transform=\"rotate(${this.heading})\">\n <line\n x1=\"0\"\n y1=\"${-(SCALE_INNER_RADIUS + rOff)}\"\n x2=\"0\"\n y2=\"${-(INNER_RADIUS + rOff)}\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n />\n </g>\n ${this._renderNorthArrow(rOff)}\n ${arrow(\n ArrowStyle.HDG,\n this.heading,\n this.priorityFor(CompassSectorPriorityElement.hdg),\n rOff\n )}\n ${arrow(\n ArrowStyle.COG,\n mappedCOG,\n this.priorityFor(CompassSectorPriorityElement.cog),\n rOff\n )}\n </svg>\n ${\n this.hasReadout\n ? html`<div\n class=\"readout\"\n style=\"top: ${this._readoutTopPercent(frame)}%\"\n >\n ${renderCenterReadouts([\n {\n value: this.heading,\n label: this.label,\n unit: this.unit,\n fractionDigits: this.fractionDigits,\n size: ReadoutSize.large,\n priority: this.priorityFor(\n CompassSectorPriorityElement.hdg\n ),\n centerValue: true,\n centerMeta: true,\n },\n ])}\n </div>`\n : nothing\n }\n </div>\n `;\n }\n\n static override styles = [\n unsafeCSS(instrumentReadoutStyle),\n centerReadoutStyles,\n unsafeCSS(componentStyle),\n ];\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-compass-sector': ObcCompassSector;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"compass-sector.js","names":[],"sources":["../../../src/navigation-instruments/compass-sector/compass-sector.ts"],"sourcesContent":["import {LitElement, PropertyValues, html, svg, unsafeCSS, nothing} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport {ResizeController} from '@lit-labs/observers/resize-controller.js';\nimport componentStyle from './compass-sector.css?inline';\nimport '../watch/watch.js';\nimport {\n centerReadoutStyles,\n renderCenterReadouts,\n} from '../readout/center-readout.js';\nimport {ReadoutSize} from '../readout/readout.js';\nimport instrumentReadoutStyle from '../readout/instrument-readout.css?inline';\nimport {Tickmark, TickmarkType, TickmarkStyle} from '../watch/tickmark.js';\nimport {arrow, ArrowStyle} from '../compass/arrow.js';\nimport {AdviceState, AngleAdvice, AngleAdviceRaw} from '../watch/advice.js';\nimport {\n WatchCircleType,\n WatchArea,\n OUTER_RING_RADIUS,\n innerRingRadiusFor,\n RotType,\n RotPosition,\n} from '../watch/watch.js';\nimport {SetpointBundle} from '../../svghelpers/setpoint-bundle.js';\nimport {\n computeRadialFrame,\n estimateLabelWidthPx,\n measureContainerPx,\n observeInnerBox,\n type RadialFrame,\n} from '../../svghelpers/radial-frame.js';\nimport {ROT_ZERO_DEADBAND_DEG} from '../rate-of-turn/rot-renderer.js';\nimport {customElement} from '../../decorator.js';\nimport {degToRad, normalizeAngle} from '../../svghelpers/math.js';\nimport {InstrumentState, Priority} from '../types.js';\nexport {RotType, RotPosition};\n\nexport enum CompassSectorPriorityElement {\n hdg = 'hdg',\n cog = 'cog',\n rot = 'rot',\n}\n\n/** The sector instruments' shared box: 40 units around the outer ring. */\nconst BASE_PADDING = 48;\n/**\n * Bottom crops, % of the 448 box, one per feature that hangs below the band.\n * Each leaves its lowest paint the same 40-unit margin the ring gets above —\n * `100 · (184 − y) / 448` — and `_clipBottom()` takes the deepest in play.\n * The band's own floor is the filleted inner corner at ±60°, y ≈ −47.\n */\nconst SECTOR_CLIP_BOTTOM = 51.5;\n/** ROT dots and bar ride the inner-circle track, ~5 units below the band. */\nconst SECTOR_CLIP_BOTTOM_ROT = 50.4;\n/** Inside labels sit under the inner ring, far below the band's floor. */\nconst SECTOR_CLIP_BOTTOM_INSIDE_LABELS = 47.3;\n/** The readout is px-sized, so it needs the room obc-gauge-radial's 180° sector reserves. */\nconst SECTOR_CLIP_BOTTOM_READOUT = 44;\n/**\n * Clearance the zoom flattens the arc out to. Zero puts the arc ends on the\n * side edges, which is the point of zooming; the un-zoomed 120° arc stops\n * well short of them because the box keeps the whole circle's width.\n */\nconst SECTOR_SIDE_MARGIN = 0;\nconst WATCH_TYPE = WatchCircleType.triple;\nconst INNER_RADIUS = innerRingRadiusFor(WATCH_TYPE);\n/** Half of the fixed 120° arc on the watch face. */\nconst ARC_HALF_EXTENT = 60;\nconst SCALE_INNER_RADIUS = innerRingRadiusFor(WatchCircleType.single);\n\ninterface TickDensity {\n mainInterval: number;\n primaryInterval: number;\n secondaryInterval: number | undefined;\n}\n\nfunction tickDensityForFOV(fov: number): TickDensity {\n if (fov <= 30) {\n return {mainInterval: 10, primaryInterval: 5, secondaryInterval: 1};\n } else if (fov <= 60) {\n return {mainInterval: 10, primaryInterval: 5, secondaryInterval: undefined};\n } else if (fov <= 120) {\n return {mainInterval: 30, primaryInterval: 10, secondaryInterval: 5};\n } else {\n return {mainInterval: 90, primaryInterval: 30, secondaryInterval: 10};\n }\n}\n\n/**\n * `<obc-compass-sector>` — Curved compass strip that auto‑scales to keep HDG and COG visible.\n *\n * Renders a fixed 120° arc of a triple‑ring compass face. The visible\n * compass range (field of view) adjusts automatically so that both the\n * heading (HDG) and course‑over‑ground (COG) arrows are always in view.\n * This is the radial equivalent of `<obc-compass-flat>`: the arc shape\n * never changes — only the scale (compass‑degrees per arc‑degree) changes.\n *\n * ## Features\n *\n * - **Fixed 120° arc**: The watch face is always a 120° sector (±60° from\n * center), identical in shape to `<obc-rot-sector>`.\n * - **FOV auto‑scaling**: The field of view widens when the HDG–COG\n * angular difference grows, compressing more compass degrees into the\n * fixed arc. Tickmark density adjusts automatically.\n * - **HDG / COG arrows**: Solid (HDG) arrow is always at the arc center.\n * Hollow (COG) arrow is positioned proportionally within the arc.\n * - **North arrow**: A gray triangle in the outer scale band indicates\n * north when it falls within the visible FOV.\n * - **Heading setpoint**: Optional setpoint marker with auto at‑setpoint\n * detection and confirm animation, positioned at the mapped arc angle.\n * - **Advice zones**: Pass `headingAdvices` to render caution/alert arcs,\n * mapped into the scaled arc.\n * - **Rate of turn**: Animated ROT indicator (dots or bar) spanning from\n * HDG to the mapped COG position, clipped to the arc.\n * - **Zoom to fit**: When `zoomToFitArc` is `true`, the fixed 120° arc is\n * enlarged to fill the available space.\n *\n * ## Usage Guidelines\n *\n * - Set `heading` and `courseOverGround` to sensor values in degrees.\n * - Adjust `minFOV` to control the minimum zoom level (default 30°).\n * - Enable `zoomToFitArc` to enlarge the arc to fill the viewport.\n * - For a full‑circle compass, use `<obc-compass>` instead.\n *\n * @availableWhen newHeadingSetpoint headingSetpoint!=null\n * @availableWhen atHeadingSetpoint headingSetpoint!=null && autoAtHeadingSetpoint==false\n * @availableWhen headingSetpointAtZeroDeadband headingSetpoint!=null\n * @availableWhen headingSetpointOverride headingSetpoint!=null\n * @availableWhen autoAtHeadingSetpoint headingSetpoint!=null\n * @availableWhen autoAtHeadingSetpointDeadband headingSetpoint!=null && autoAtHeadingSetpoint==true\n * @availableWhen animateSetpoint headingSetpoint!=null\n * @availableWhen touching headingSetpoint!=null\n * @availableWhen rotPosition rotType!=undefined\n * @property rateOfTurnDegreesPerMinute - Measured rate of turn in degrees per minute (positive = starboard).\n * Drives both the bar extent and (after multiplication by\n * `rotDotAnimationFactor`) the spinning dot animation. When `undefined`,\n * falls back to the deprecated `rotationsPerMinute`.\n * @availableWhen rateOfTurnDegreesPerMinute rotType!=undefined\n * @property rotDotAnimationFactor - Visual amplification applied only to the spinning dot animation\n * (not to the bar extent). Default `18` keeps the legacy visual feel\n * (≈1 rpm at 20°/min).\n * @availableWhen rotDotAnimationFactor rotType!=undefined\n * @availableWhen rotPortStarboard rotType!=undefined\n * @availableWhen rotAtZeroDeadband rotType==bar\n * @availableWhen priorityElements priority==enhanced\n * @property hasReadout - When `true`, shows a centered `<obc-readout>` under the arc displaying the\n * heading (label `HDG`, unit `DEG`). The value color follows the HDG entry in\n * `priorityElements`, matching the HDG arrow.\n * @property label - Readout label. Default `HDG`.\n * @availableWhen label hasReadout==true\n * @property unit - Readout unit. Default `DEG`.\n * @availableWhen unit hasReadout==true\n * @property fractionDigits - Number of fraction digits shown in the readout. Default `0`.\n * @availableWhen fractionDigits hasReadout==true\n * @property rotMaxValue - Bar-extent reference value in degrees per minute: the bar fills the full\n * ±`ARC_HALF_EXTENT` arc when the measured rate of turn reaches\n * ±`rotMaxValue`. The default `60` aligns with ES-TRIN 2025/1 Art. 3.02.\n * @availableWhen rotMaxValue rotType==bar\n * @stable\n */\n@customElement('obc-compass-sector')\nexport class ObcCompassSector extends LitElement {\n @property({type: Number}) heading = 0;\n @property({type: Number}) courseOverGround = 0;\n\n @property({type: Number}) headingSetpoint: number | null = null;\n @property({type: Number}) newHeadingSetpoint: number | undefined;\n @property({type: Boolean}) atHeadingSetpoint: boolean = false;\n @property({type: Number}) headingSetpointAtZeroDeadband: number = 0.5;\n @property({type: Boolean}) headingSetpointOverride: boolean = false;\n @property({type: Boolean, attribute: false}) autoAtHeadingSetpoint = true;\n @property({type: Number}) autoAtHeadingSetpointDeadband: number = 2;\n @property({type: Boolean}) animateSetpoint: boolean = false;\n @property({type: Boolean}) touching: boolean = false;\n @property({type: Array, attribute: false}) headingAdvices: AngleAdvice[] = [];\n\n @property({type: Number}) minFOV: number = 30;\n\n @property({type: String}) rotType: RotType | undefined;\n @property({type: String}) rotPosition: RotPosition = RotPosition.innerCircle;\n @property({type: Number}) rateOfTurnDegreesPerMinute: number | undefined;\n @property({type: Number}) rotDotAnimationFactor: number = 18;\n /**\n * @deprecated Use `rateOfTurnDegreesPerMinute` (and optionally\n * `rotDotAnimationFactor`) instead. Takes effect only when\n * `rateOfTurnDegreesPerMinute` is `undefined`.\n * @availableWhen rotType!=undefined && rateOfTurnDegreesPerMinute==undefined\n */\n @property({type: Number}) rotationsPerMinute: number = 0;\n @property({type: Number}) rotMaxValue: number = 60;\n @property({type: Boolean}) rotPortStarboard: boolean = false;\n @property({type: Number}) rotAtZeroDeadband: number = ROT_ZERO_DEADBAND_DEG;\n\n @property({type: String}) state: InstrumentState = InstrumentState.active;\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: Array, attribute: false})\n priorityElements: CompassSectorPriorityElement[] = [\n CompassSectorPriorityElement.hdg,\n ];\n @property({type: Boolean}) tickmarksInside: boolean = false;\n @property({type: Boolean}) zoomToFitArc: boolean = false;\n @property({type: Boolean}) hasReadout: boolean = false;\n @property({type: String}) label = 'HDG';\n @property({type: String}) unit = 'DEG';\n @property({type: Number}) fractionDigits = 0;\n\n private _headingSp = new SetpointBundle({\n angularWraparound: true,\n onAnimationEnd: () => this.requestUpdate(),\n });\n\n // Cached computed values — updated in willUpdate()\n private _halfFOV = 30;\n private _arcHalfExtent = ARC_HALF_EXTENT;\n private _scale = 1;\n private _cachedAreas: WatchArea[] = [];\n private _cachedTickmarks: Tickmark[] = [];\n private _cachedAdvices: AngleAdviceRaw[] = [];\n\n // The frame follows the host box, which no property change announces.\n private _resizeController = new ResizeController(this, {});\n\n private _aspectWidth = 448;\n private _aspectHeight = 217.28;\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 this._headingSp.sync({\n setpoint: this.headingSetpoint ?? undefined,\n newSetpoint: this.newHeadingSetpoint,\n atSetpoint: this.atHeadingSetpoint,\n touching: this.touching,\n autoAtSetpoint: this.autoAtHeadingSetpoint,\n autoAtSetpointDeadband: this.autoAtHeadingSetpointDeadband,\n setpointAtZeroDeadband: this.headingSetpointAtZeroDeadband,\n setpointOverride: this.headingSetpointOverride,\n animateSetpoint: this.animateSetpoint,\n });\n\n const arcInputsChanged =\n changed.has('heading') ||\n changed.has('courseOverGround') ||\n changed.has('minFOV') ||\n changed.has('zoomToFitArc');\n\n if (arcInputsChanged) {\n let diff = this.courseOverGround - this.heading;\n if (diff > 180) diff -= 360;\n else if (diff < -180) diff += 360;\n const minFov = Math.max(1, this.minFOV);\n const MARGIN = 15;\n\n if (this.zoomToFitArc) {\n const needed = Math.max(minFov, Math.abs(diff) + MARGIN);\n if (needed <= ARC_HALF_EXTENT) {\n this._halfFOV = needed;\n this._arcHalfExtent = needed;\n this._scale = 1;\n } else {\n this._halfFOV = needed;\n this._arcHalfExtent = ARC_HALF_EXTENT;\n this._scale = ARC_HALF_EXTENT / needed;\n }\n } else {\n this._halfFOV = Math.max(minFov, Math.abs(diff));\n this._arcHalfExtent = ARC_HALF_EXTENT;\n this._scale = ARC_HALF_EXTENT / this._halfFOV;\n }\n\n this._cachedAreas = [\n {\n startAngle: this.heading - this._arcHalfExtent,\n endAngle: this.heading + this._arcHalfExtent,\n roundInsideCut: true,\n roundOutsideCut: true,\n },\n ];\n\n this._cachedTickmarks = this._buildTickmarks();\n }\n\n if (arcInputsChanged || changed.has('headingAdvices')) {\n this._cachedAdvices = this._buildAdvices();\n }\n }\n\n override updated(changed: PropertyValues): void {\n super.updated(changed);\n // The host IS the canvas, so its box follows the frame the render used.\n this.style.setProperty(\n '--obc-compass-sector-aspect',\n `${this._aspectWidth} / ${this._aspectHeight}`\n );\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._headingSp.dispose();\n }\n\n // ---------------------------------------------------------------------------\n // Angle mapping — maps compass degrees to arc positions\n // ---------------------------------------------------------------------------\n\n private _mapAngle(compassDeg: number): number {\n let diff = compassDeg - this.heading;\n if (diff > 180) diff -= 360;\n else if (diff < -180) diff += 360;\n return this.heading + diff * this._scale;\n }\n\n // ---------------------------------------------------------------------------\n // Areas — fixed 120° arc centered on heading (built in willUpdate)\n // ---------------------------------------------------------------------------\n\n // ---------------------------------------------------------------------------\n // Tickmarks — compass-degree labels at mapped arc positions\n // ---------------------------------------------------------------------------\n\n private _buildTickmarks(): Tickmark[] {\n const fov = this._halfFOV * 2;\n const {mainInterval, primaryInterval, secondaryInterval} =\n tickDensityForFOV(fov);\n const halfFov = this._halfFOV;\n const compassStart = this.heading - halfFov;\n const compassEnd = this.heading + halfFov;\n\n const tickmarks: Tickmark[] = [];\n const added = new Set<number>();\n\n const addTick = (\n arcAngle: number,\n type: TickmarkType,\n text?: string\n ): void => {\n const key = Math.round(arcAngle * 1000);\n if (added.has(key)) return;\n added.add(key);\n tickmarks.push({angle: arcAngle, type, text});\n };\n\n const step = secondaryInterval ?? primaryInterval;\n const firstTick = Math.ceil(compassStart / step) * step;\n\n for (\n let compassDeg = firstTick;\n compassDeg <= compassEnd;\n compassDeg += step\n ) {\n const norm = normalizeAngle(compassDeg);\n const arcAngle = this._mapAngle(compassDeg);\n const isMain = norm % mainInterval === 0;\n const isPrimary = norm % primaryInterval === 0;\n\n if (isMain) {\n addTick(arcAngle, TickmarkType.main, Math.round(norm).toString());\n } else if (isPrimary) {\n addTick(arcAngle, TickmarkType.primary);\n } else {\n addTick(arcAngle, TickmarkType.secondary);\n }\n }\n\n return tickmarks;\n }\n\n // ---------------------------------------------------------------------------\n // ViewBox\n // ---------------------------------------------------------------------------\n\n /** Deepest crop the active features need, so none of them is shaved. */\n private _clipBottom(): number {\n const clips = [SECTOR_CLIP_BOTTOM];\n if (\n this.rotType !== undefined &&\n this.rotPosition === RotPosition.innerCircle\n ) {\n clips.push(SECTOR_CLIP_BOTTOM_ROT);\n }\n if (this.tickmarksInside) {\n clips.push(SECTOR_CLIP_BOTTOM_INSIDE_LABELS);\n }\n if (this.hasReadout) {\n clips.push(SECTOR_CLIP_BOTTOM_READOUT);\n }\n return Math.min(...clips);\n }\n\n /**\n * One frame per render, feeding both layers: the shared sector box cropped\n * to the arc. Zoom enlarges the arc rather than the window, so the window\n * only slides outward by the radius offset and the canvas holds still.\n */\n private _buildFrame(): RadialFrame {\n const frame = computeRadialFrame({\n basePadding: BASE_PADDING,\n labelWidthPx: this.tickmarksInside\n ? 0\n : estimateLabelWidthPx(this._cachedTickmarks.map((t) => t.text)),\n clips: {top: 0, bottom: this._clipBottom(), left: 0, right: 0},\n containerPx: measureContainerPx(this),\n });\n\n if (!this.zoomToFitArc) {\n return frame;\n }\n\n // Flatten the arc until its ends reach the same margin the 120° arc keeps,\n // measured off the window rather than the live heading — a frame fitted\n // where the arc currently points swings its box around the circle and the\n // arc drifts with it (#1152).\n const halfWidth = frame.width / 2 - SECTOR_SIDE_MARGIN;\n const sin = Math.sin(degToRad(this._arcHalfExtent));\n const radiusOffset = Math.max(0, halfWidth / sin - OUTER_RING_RADIUS);\n const y = frame.y - radiusOffset;\n return {\n ...frame,\n y,\n radiusOffset,\n viewBox: `${frame.x} ${y} ${frame.width} ${frame.height}`,\n };\n }\n\n // ---------------------------------------------------------------------------\n // North arrow — rendered in overlay at mapped 0° position\n // ---------------------------------------------------------------------------\n\n private _renderNorthArrow(rOff: number) {\n let northOffset = -this.heading;\n if (northOffset > 180) northOffset -= 360;\n else if (northOffset < -180) northOffset += 360;\n if (Math.abs(northOffset) > this._halfFOV) return nothing;\n\n const northArcAngle = this._mapAngle(0);\n const radius = OUTER_RING_RADIUS + rOff;\n return svg`\n <g transform=\"rotate(${northArcAngle}) translate(0, ${-radius})\">\n <path fill-rule=\"evenodd\" clip-rule=\"evenodd\"\n d=\"M-17.8457 24.984 0 0 17.8458 24.984C11.9868 24.3338 6.0324 24 0 24-6.0323 24-11.9867 24.3338-17.8457 24.984Z\"\n fill=\"var(--instrument-frame-tertiary-color)\"/>\n </g>\n `;\n }\n\n // ---------------------------------------------------------------------------\n // Priority & advice helpers\n // ---------------------------------------------------------------------------\n\n private _angleInRange(value: number, min: number, max: number): boolean {\n const v = normalizeAngle(value);\n const start = normalizeAngle(min);\n const end = normalizeAngle(max);\n return start <= end ? v >= start && v <= end : v >= start || v <= end;\n }\n\n private _buildAdvices(): AngleAdviceRaw[] {\n return this.headingAdvices.map(({minAngle, maxAngle, hinted, type}) => {\n const state = this._angleInRange(this.heading, minAngle, maxAngle)\n ? AdviceState.triggered\n : hinted\n ? AdviceState.hinted\n : AdviceState.regular;\n return {\n minAngle: this._mapAngle(minAngle),\n maxAngle: this._mapAngle(maxAngle),\n type,\n state,\n };\n });\n }\n\n private get _effectiveRotDegPerMin(): number {\n return this.rateOfTurnDegreesPerMinute ?? this.rotationsPerMinute;\n }\n\n private get _rotEndAngle(): number {\n const maxVal = this.rotMaxValue || 1;\n const barCompassDeg =\n (this._effectiveRotDegPerMin / maxVal) * ARC_HALF_EXTENT;\n return this._mapAngle(this.heading + barCompassDeg);\n }\n\n private priorityFor(element: CompassSectorPriorityElement): Priority {\n const selected = Array.isArray(this.priorityElements)\n ? this.priorityElements\n : [];\n return selected.includes(element) ? this.priority : Priority.regular;\n }\n\n /**\n * Vertical placement of the readout as a percentage of the frame height.\n *\n * The anchor sits halfway down the inner radius, below the arc's inner edge.\n * Zoom pushes that edge out by the radius offset, so the anchor follows it\n * and the gap under the arc reads the same in both modes.\n */\n private _readoutTopPercent(frame: RadialFrame): number {\n if (!frame.height) {\n return 50;\n }\n const anchorY = -INNER_RADIUS * 0.5 - frame.radiusOffset;\n return Math.round(((anchorY - frame.y) / frame.height) * 1000) / 10;\n }\n\n // ---------------------------------------------------------------------------\n // Render\n // ---------------------------------------------------------------------------\n\n override render() {\n const rotation = -this.heading;\n const frame = this._buildFrame();\n const viewBox = frame.viewBox;\n const rOff = frame.radiusOffset;\n // The overlay rotates its element box like obc-watch does, so it needs the\n // same pivot: the cropped frame's centre is not the compass centre.\n const pivotX = (-frame.x / frame.width) * 100;\n const pivotY = (-frame.y / frame.height) * 100;\n this._aspectWidth = frame.width;\n this._aspectHeight = frame.height;\n const tickmarks = frame.labelsHidden\n ? this._cachedTickmarks.map((t) => ({...t, text: undefined}))\n : this._cachedTickmarks;\n\n const mappedCOG = this._mapAngle(this.courseOverGround);\n const mappedSetpoint =\n this.headingSetpoint != null\n ? this._mapAngle(this.headingSetpoint)\n : undefined;\n const mappedNewSetpoint =\n this.newHeadingSetpoint != null\n ? this._mapAngle(this.newHeadingSetpoint)\n : undefined;\n\n return html`\n <div class=\"container\">\n <obc-watch\n .touching=${this.touching}\n .advices=${this._cachedAdvices}\n .tickmarks=${tickmarks}\n .tickmarkStyle=${TickmarkStyle.regular}\n .tickmarksInside=${this.tickmarksInside}\n .state=${this.state}\n .watchCircleType=${WATCH_TYPE}\n .northArrow=${false}\n .areas=${this._cachedAreas}\n .arcFrame=${frame}\n .tickFadeAngle=${this._arcHalfExtent * 0.2}\n .rotation=${rotation}\n .angleSetpoint=${mappedSetpoint}\n .newAngleSetpoint=${mappedNewSetpoint}\n .atAngleSetpoint=${this._headingSp.computeAtSetpoint(this.heading)}\n .angleSetpointAtZeroDeadband=${this.headingSetpointAtZeroDeadband}\n .setpointOverride=${this.headingSetpointOverride}\n .priority=${this.priority}\n .animateSetpoint=${this.animateSetpoint}\n .rotType=${this.rotType}\n .rotPosition=${this.rotPosition}\n .rotStartAngle=${this.heading}\n .rotEndAngle=${this._rotEndAngle}\n .rotPriority=${this.priorityFor(CompassSectorPriorityElement.rot)}\n .rotPortStarboard=${this.rotPortStarboard}\n .rotAtZeroDeadband=${this.rotAtZeroDeadband}\n .rateOfTurnDegreesPerMinute=${this.rateOfTurnDegreesPerMinute}\n .rotDotAnimationFactor=${this.rotDotAnimationFactor}\n .rotationsPerMinute=${this.rotationsPerMinute}\n >\n </obc-watch>\n <svg\n viewBox=\"${viewBox}\"\n transform=\"rotate(${rotation})\"\n style=\"transform-origin: ${pivotX}% ${pivotY}%\"\n >\n <g transform=\"rotate(${this.heading})\">\n <line\n x1=\"0\"\n y1=\"${-(SCALE_INNER_RADIUS + rOff)}\"\n x2=\"0\"\n y2=\"${-(INNER_RADIUS + rOff)}\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n stroke-width=\"1\"\n vector-effect=\"non-scaling-stroke\"\n />\n </g>\n ${this._renderNorthArrow(rOff)}\n ${arrow(\n ArrowStyle.HDG,\n this.heading,\n this.priorityFor(CompassSectorPriorityElement.hdg),\n rOff\n )}\n ${arrow(\n ArrowStyle.COG,\n mappedCOG,\n this.priorityFor(CompassSectorPriorityElement.cog),\n rOff\n )}\n </svg>\n ${\n this.hasReadout\n ? html`<div\n class=\"readout\"\n style=\"top: ${this._readoutTopPercent(frame)}%\"\n >\n ${renderCenterReadouts([\n {\n value: this.heading,\n label: this.label,\n unit: this.unit,\n fractionDigits: this.fractionDigits,\n size: ReadoutSize.large,\n priority: this.priorityFor(\n CompassSectorPriorityElement.hdg\n ),\n centerValue: true,\n centerMeta: true,\n },\n ])}\n </div>`\n : nothing\n }\n </div>\n `;\n }\n\n static override styles = [\n unsafeCSS(instrumentReadoutStyle),\n centerReadoutStyles,\n unsafeCSS(componentStyle),\n ];\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-compass-sector': ObcCompassSector;\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  //#region src/navigation-instruments/rate-of-turn/rate-of-turn.controller.ts
2
2
  var RateOfTurnController = class {
3
- constructor(host, el, initialRotationsPerMinute = 1, cyclePx = 0) {
4
- this._rotationsPerMinute = 1;
3
+ constructor(host, el, initialRotationsPerMinute = 0, cyclePx = 0) {
4
+ this._rotationsPerMinute = 0;
5
5
  this._cyclePx = 0;
6
6
  this.host = host;
7
7
  this.el = el;
@@ -1 +1 @@
1
- {"version":3,"file":"rate-of-turn.controller.js","names":[],"sources":["../../../src/navigation-instruments/rate-of-turn/rate-of-turn.controller.ts"],"sourcesContent":["// spinner-controller.ts\nimport {ReactiveController, ReactiveControllerHost} from 'lit';\n\nexport class RateOfTurnController implements ReactiveController {\n private host: ReactiveControllerHost;\n readonly el: Element;\n private animation?: Animation;\n private _rotationsPerMinute = 1;\n private _cyclePx = 0;\n\n constructor(\n host: ReactiveControllerHost,\n el: Element,\n initialRotationsPerMinute = 1,\n cyclePx = 0\n ) {\n this.host = host;\n this.el = el;\n this._rotationsPerMinute = initialRotationsPerMinute;\n this._cyclePx = cyclePx;\n this.host.addController(this);\n }\n\n set rotationsPerMinute(value: number) {\n if (this._rotationsPerMinute !== value) {\n this._rotationsPerMinute = value;\n this.updateAnimation();\n }\n }\n\n get rotationsPerMinute() {\n return this._rotationsPerMinute;\n }\n\n set cyclePx(value: number) {\n if (this._cyclePx !== value) {\n this._cyclePx = value;\n this.updateAnimation();\n }\n }\n\n get cyclePx() {\n return this._cyclePx;\n }\n\n private get isTranslateMode() {\n return this._cyclePx > 0;\n }\n\n private getKeyframes(): Keyframe[] {\n if (this.isTranslateMode) {\n return [\n {transform: 'translateX(0px)'},\n {transform: `translateX(${this._cyclePx}px)`},\n ];\n }\n return [{transform: 'rotate(0deg)'}, {transform: 'rotate(360deg)'}];\n }\n\n hostConnected() {\n this.startAnimation();\n }\n\n private startAnimation() {\n const absSpeed = Math.abs(this._rotationsPerMinute);\n const duration = absSpeed === 0 ? 1 : (1000 * 60) / absSpeed;\n\n this.animation = this.el.animate(this.getKeyframes(), {\n duration,\n iterations: Infinity,\n direction: this._rotationsPerMinute >= 0 ? 'normal' : 'reverse',\n });\n\n if (this._rotationsPerMinute === 0) {\n this.animation.pause();\n }\n }\n\n private updateAnimation() {\n if (!this.animation) return;\n\n const currentTiming = this.animation.effect!.getComputedTiming();\n const oldDuration = currentTiming.duration as number;\n const currentTime = (this.animation.currentTime as number) ?? 0;\n const oldDirection = currentTiming.direction;\n const progress = (currentTime % oldDuration) / oldDuration;\n\n this.animation.cancel();\n\n const absSpeed = Math.abs(this._rotationsPerMinute);\n const newDuration = absSpeed === 0 ? 1 : (1000 * 60) / absSpeed;\n const newDirection = this._rotationsPerMinute >= 0 ? 'normal' : 'reverse';\n\n const correctedProgress =\n oldDirection !== newDirection ? 1 - progress : progress;\n\n this.animation = this.el.animate(this.getKeyframes(), {\n duration: newDuration,\n iterations: Infinity,\n direction: newDirection,\n });\n\n this.animation.currentTime = correctedProgress * newDuration;\n\n if (this._rotationsPerMinute === 0) {\n this.animation.pause();\n }\n }\n\n destroy() {\n this.animation?.cancel();\n this.animation = undefined;\n }\n\n hostDisconnected() {\n this.destroy();\n }\n}\n\n/**\n * Dispose a `RateOfTurnController` reference: destroy the animation,\n * remove the controller from its host, and return `undefined` to clear\n * the caller's field.\n */\nexport function disposeRotController(\n host: ReactiveControllerHost,\n controller: RateOfTurnController | undefined\n): undefined {\n if (controller) {\n controller.destroy();\n host.removeController(controller);\n }\n return undefined;\n}\n"],"mappings":";AAGA,IAAa,uBAAb,MAAgE;CAO9D,YACE,MACA,IACA,4BAA4B,GAC5B,UAAU,GACV;EAR4B,KAAA,sBAAA;EACX,KAAA,WAAA;EAQjB,KAAK,OAAO;EACZ,KAAK,KAAK;EACV,KAAK,sBAAsB;EAC3B,KAAK,WAAW;EAChB,KAAK,KAAK,cAAc,IAAI;CAC9B;CAEA,IAAI,mBAAmB,OAAe;EACpC,IAAI,KAAK,wBAAwB,OAAO;GACtC,KAAK,sBAAsB;GAC3B,KAAK,gBAAgB;EACvB;CACF;CAEA,IAAI,qBAAqB;EACvB,OAAO,KAAK;CACd;CAEA,IAAI,QAAQ,OAAe;EACzB,IAAI,KAAK,aAAa,OAAO;GAC3B,KAAK,WAAW;GAChB,KAAK,gBAAgB;EACvB;CACF;CAEA,IAAI,UAAU;EACZ,OAAO,KAAK;CACd;CAEA,IAAY,kBAAkB;EAC5B,OAAO,KAAK,WAAW;CACzB;CAEA,eAAmC;EACjC,IAAI,KAAK,iBACP,OAAO,CACL,EAAC,WAAW,kBAAiB,GAC7B,EAAC,WAAW,cAAc,KAAK,SAAS,KAAI,CAC9C;EAEF,OAAO,CAAC,EAAC,WAAW,eAAc,GAAG,EAAC,WAAW,iBAAgB,CAAC;CACpE;CAEA,gBAAgB;EACd,KAAK,eAAe;CACtB;CAEA,iBAAyB;EACvB,MAAM,WAAW,KAAK,IAAI,KAAK,mBAAmB;EAClD,MAAM,WAAW,aAAa,IAAI,IAAK,MAAa;EAEpD,KAAK,YAAY,KAAK,GAAG,QAAQ,KAAK,aAAa,GAAG;GACpD;GACA,YAAY;GACZ,WAAW,KAAK,uBAAuB,IAAI,WAAW;EACxD,CAAC;EAED,IAAI,KAAK,wBAAwB,GAC/B,KAAK,UAAU,MAAM;CAEzB;CAEA,kBAA0B;EACxB,IAAI,CAAC,KAAK,WAAW;EAErB,MAAM,gBAAgB,KAAK,UAAU,OAAQ,kBAAkB;EAC/D,MAAM,cAAc,cAAc;EAClC,MAAM,cAAe,KAAK,UAAU,eAA0B;EAC9D,MAAM,eAAe,cAAc;EACnC,MAAM,WAAY,cAAc,cAAe;EAE/C,KAAK,UAAU,OAAO;EAEtB,MAAM,WAAW,KAAK,IAAI,KAAK,mBAAmB;EAClD,MAAM,cAAc,aAAa,IAAI,IAAK,MAAa;EACvD,MAAM,eAAe,KAAK,uBAAuB,IAAI,WAAW;EAEhE,MAAM,oBACJ,iBAAiB,eAAe,IAAI,WAAW;EAEjD,KAAK,YAAY,KAAK,GAAG,QAAQ,KAAK,aAAa,GAAG;GACpD,UAAU;GACV,YAAY;GACZ,WAAW;EACb,CAAC;EAED,KAAK,UAAU,cAAc,oBAAoB;EAEjD,IAAI,KAAK,wBAAwB,GAC/B,KAAK,UAAU,MAAM;CAEzB;CAEA,UAAU;EACR,KAAK,WAAW,OAAO;EACvB,KAAK,YAAY,KAAA;CACnB;CAEA,mBAAmB;EACjB,KAAK,QAAQ;CACf;AACF;;;;;;AAOA,SAAgB,qBACd,MACA,YACW;CACX,IAAI,YAAY;EACd,WAAW,QAAQ;EACnB,KAAK,iBAAiB,UAAU;CAClC;AAEF"}
1
+ {"version":3,"file":"rate-of-turn.controller.js","names":[],"sources":["../../../src/navigation-instruments/rate-of-turn/rate-of-turn.controller.ts"],"sourcesContent":["// spinner-controller.ts\nimport {ReactiveController, ReactiveControllerHost} from 'lit';\n\nexport class RateOfTurnController implements ReactiveController {\n private host: ReactiveControllerHost;\n readonly el: Element;\n private animation?: Animation;\n private _rotationsPerMinute = 0;\n private _cyclePx = 0;\n\n constructor(\n host: ReactiveControllerHost,\n el: Element,\n initialRotationsPerMinute = 0,\n cyclePx = 0\n ) {\n this.host = host;\n this.el = el;\n this._rotationsPerMinute = initialRotationsPerMinute;\n this._cyclePx = cyclePx;\n this.host.addController(this);\n }\n\n set rotationsPerMinute(value: number) {\n if (this._rotationsPerMinute !== value) {\n this._rotationsPerMinute = value;\n this.updateAnimation();\n }\n }\n\n get rotationsPerMinute() {\n return this._rotationsPerMinute;\n }\n\n set cyclePx(value: number) {\n if (this._cyclePx !== value) {\n this._cyclePx = value;\n this.updateAnimation();\n }\n }\n\n get cyclePx() {\n return this._cyclePx;\n }\n\n private get isTranslateMode() {\n return this._cyclePx > 0;\n }\n\n private getKeyframes(): Keyframe[] {\n if (this.isTranslateMode) {\n return [\n {transform: 'translateX(0px)'},\n {transform: `translateX(${this._cyclePx}px)`},\n ];\n }\n return [{transform: 'rotate(0deg)'}, {transform: 'rotate(360deg)'}];\n }\n\n hostConnected() {\n this.startAnimation();\n }\n\n private startAnimation() {\n const absSpeed = Math.abs(this._rotationsPerMinute);\n const duration = absSpeed === 0 ? 1 : (1000 * 60) / absSpeed;\n\n this.animation = this.el.animate(this.getKeyframes(), {\n duration,\n iterations: Infinity,\n direction: this._rotationsPerMinute >= 0 ? 'normal' : 'reverse',\n });\n\n if (this._rotationsPerMinute === 0) {\n this.animation.pause();\n }\n }\n\n private updateAnimation() {\n if (!this.animation) return;\n\n const currentTiming = this.animation.effect!.getComputedTiming();\n const oldDuration = currentTiming.duration as number;\n const currentTime = (this.animation.currentTime as number) ?? 0;\n const oldDirection = currentTiming.direction;\n const progress = (currentTime % oldDuration) / oldDuration;\n\n this.animation.cancel();\n\n const absSpeed = Math.abs(this._rotationsPerMinute);\n const newDuration = absSpeed === 0 ? 1 : (1000 * 60) / absSpeed;\n const newDirection = this._rotationsPerMinute >= 0 ? 'normal' : 'reverse';\n\n const correctedProgress =\n oldDirection !== newDirection ? 1 - progress : progress;\n\n this.animation = this.el.animate(this.getKeyframes(), {\n duration: newDuration,\n iterations: Infinity,\n direction: newDirection,\n });\n\n this.animation.currentTime = correctedProgress * newDuration;\n\n if (this._rotationsPerMinute === 0) {\n this.animation.pause();\n }\n }\n\n destroy() {\n this.animation?.cancel();\n this.animation = undefined;\n }\n\n hostDisconnected() {\n this.destroy();\n }\n}\n\n/**\n * Dispose a `RateOfTurnController` reference: destroy the animation,\n * remove the controller from its host, and return `undefined` to clear\n * the caller's field.\n */\nexport function disposeRotController(\n host: ReactiveControllerHost,\n controller: RateOfTurnController | undefined\n): undefined {\n if (controller) {\n controller.destroy();\n host.removeController(controller);\n }\n return undefined;\n}\n"],"mappings":";AAGA,IAAa,uBAAb,MAAgE;CAO9D,YACE,MACA,IACA,4BAA4B,GAC5B,UAAU,GACV;EAR4B,KAAA,sBAAA;EACX,KAAA,WAAA;EAQjB,KAAK,OAAO;EACZ,KAAK,KAAK;EACV,KAAK,sBAAsB;EAC3B,KAAK,WAAW;EAChB,KAAK,KAAK,cAAc,IAAI;CAC9B;CAEA,IAAI,mBAAmB,OAAe;EACpC,IAAI,KAAK,wBAAwB,OAAO;GACtC,KAAK,sBAAsB;GAC3B,KAAK,gBAAgB;EACvB;CACF;CAEA,IAAI,qBAAqB;EACvB,OAAO,KAAK;CACd;CAEA,IAAI,QAAQ,OAAe;EACzB,IAAI,KAAK,aAAa,OAAO;GAC3B,KAAK,WAAW;GAChB,KAAK,gBAAgB;EACvB;CACF;CAEA,IAAI,UAAU;EACZ,OAAO,KAAK;CACd;CAEA,IAAY,kBAAkB;EAC5B,OAAO,KAAK,WAAW;CACzB;CAEA,eAAmC;EACjC,IAAI,KAAK,iBACP,OAAO,CACL,EAAC,WAAW,kBAAiB,GAC7B,EAAC,WAAW,cAAc,KAAK,SAAS,KAAI,CAC9C;EAEF,OAAO,CAAC,EAAC,WAAW,eAAc,GAAG,EAAC,WAAW,iBAAgB,CAAC;CACpE;CAEA,gBAAgB;EACd,KAAK,eAAe;CACtB;CAEA,iBAAyB;EACvB,MAAM,WAAW,KAAK,IAAI,KAAK,mBAAmB;EAClD,MAAM,WAAW,aAAa,IAAI,IAAK,MAAa;EAEpD,KAAK,YAAY,KAAK,GAAG,QAAQ,KAAK,aAAa,GAAG;GACpD;GACA,YAAY;GACZ,WAAW,KAAK,uBAAuB,IAAI,WAAW;EACxD,CAAC;EAED,IAAI,KAAK,wBAAwB,GAC/B,KAAK,UAAU,MAAM;CAEzB;CAEA,kBAA0B;EACxB,IAAI,CAAC,KAAK,WAAW;EAErB,MAAM,gBAAgB,KAAK,UAAU,OAAQ,kBAAkB;EAC/D,MAAM,cAAc,cAAc;EAClC,MAAM,cAAe,KAAK,UAAU,eAA0B;EAC9D,MAAM,eAAe,cAAc;EACnC,MAAM,WAAY,cAAc,cAAe;EAE/C,KAAK,UAAU,OAAO;EAEtB,MAAM,WAAW,KAAK,IAAI,KAAK,mBAAmB;EAClD,MAAM,cAAc,aAAa,IAAI,IAAK,MAAa;EACvD,MAAM,eAAe,KAAK,uBAAuB,IAAI,WAAW;EAEhE,MAAM,oBACJ,iBAAiB,eAAe,IAAI,WAAW;EAEjD,KAAK,YAAY,KAAK,GAAG,QAAQ,KAAK,aAAa,GAAG;GACpD,UAAU;GACV,YAAY;GACZ,WAAW;EACb,CAAC;EAED,KAAK,UAAU,cAAc,oBAAoB;EAEjD,IAAI,KAAK,wBAAwB,GAC/B,KAAK,UAAU,MAAM;CAEzB;CAEA,UAAU;EACR,KAAK,WAAW,OAAO;EACvB,KAAK,YAAY,KAAA;CACnB;CAEA,mBAAmB;EACjB,KAAK,QAAQ;CACf;AACF;;;;;;AAOA,SAAgB,qBACd,MACA,YACW;CACX,IAAI,YAAY;EACd,WAAW,QAAQ;EACnB,KAAK,iBAAiB,UAAU;CAClC;AAEF"}
@@ -15,7 +15,7 @@ var ObcRateOfTurn = class ObcRateOfTurn extends LitElement {
15
15
  constructor(..._args) {
16
16
  super(..._args);
17
17
  this.rotDotAnimationFactor = 18;
18
- this.rotationsPerMinute = 1;
18
+ this.rotationsPerMinute = 0;
19
19
  this.rotType = RotType.dots;
20
20
  this.rotPosition = RotPosition.scale;
21
21
  this.priority = Priority.regular;
@@ -1 +1 @@
1
- {"version":3,"file":"rate-of-turn.js","names":[],"sources":["../../../src/navigation-instruments/rate-of-turn/rate-of-turn.ts"],"sourcesContent":["import {LitElement, css, html, nothing, unsafeCSS} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport '../watch/watch.js';\nimport {WatchCircleType, RotType, RotPosition} from '../watch/watch.js';\nimport {Tickmark, TickmarkType} from '../watch/tickmark.js';\nimport {\n centerReadoutStyles,\n renderCenterReadouts,\n} from '../readout/center-readout.js';\nimport {ReadoutSize} from '../readout/readout.js';\nimport instrumentReadoutStyle from '../readout/instrument-readout.css?inline';\nimport {ROT_ZERO_DEADBAND_DEG} from './rot-renderer.js';\nimport {customElement} from '../../decorator.js';\nimport {Priority} from '../types.js';\nimport {clamp} from '../../svghelpers/math.js';\n\nexport {RotType, RotPosition};\n\n/**\n * `<obc-rate-of-turn>` — Standalone rate-of-turn indicator rendered on a circular watch face.\n *\n * Wraps `<obc-watch>` to display a spinning-dot or arc-bar ROT visualization\n * without any additional instrument overlays. Useful for isolating the ROT\n * indicator in layouts where heading/compass elements are handled separately.\n *\n * ## Features\n *\n * - **Dot mode** (`rotType=\"dots\"`): Five evenly-spaced dots spin at the\n * resolved rotations-per-minute (derived from\n * `rateOfTurnDegreesPerMinute × rotDotAnimationFactor / 360`).\n * - **Bar mode** (`rotType=\"bar\"`): A banana-shaped arc from `barStartAngle`\n * to `barEndAngle` with clipped spinning dots inside.\n * - **Track position**: Place the indicator on the outer scale ring\n * (`rotPosition=\"scale\"`) or the inner circle\n * (`rotPosition=\"innerCircle\"`).\n * - **Track bar** (`hasTrackBar`): A bar in the ring band growing from the\n * twelve o'clock position to the measured rate of turn, with a needle\n * marker at its end and sector tickmarks (pairs with\n * `watchCircleType=\"double\"` for the banded face, or `\"triple\"` when the\n * ROT dots sit on the inner circle).\n * - **Center readout** (`hasReadout`): A centered readout showing the\n * measured rate of turn (label `ROT`, unit `DEG/min` by default).\n * - **Color priority**: Uses `priority` to select regular or enhanced color\n * palette; `rotPortStarboard` colors the track bar and needle by turn\n * direction.\n *\n * ## Usage Guidelines\n *\n * - Set `rateOfTurnDegreesPerMinute` to the current sensor value in degrees\n * per minute (the maritime/AIS convention). Sign controls direction\n * (positive = starboard / clockwise).\n * - Tune `rotDotAnimationFactor` to amplify the dot animation independently\n * of the physical value (default `18` ≈ 1 rpm at 20°/min).\n * - In bar mode, `barStartAngle` and `barEndAngle` define the static arc\n * span (0° = 12 o'clock, clockwise).\n * - Change `watchCircleType` to match the surrounding instrument ring style\n * (e.g. `triple` for compass contexts).\n *\n * @element obc-rate-of-turn\n *\n * @property rateOfTurnDegreesPerMinute - Measured rate of turn in degrees per minute (positive = starboard).\n * When `undefined`, the legacy `rotationsPerMinute` value is used.\n * @property rotDotAnimationFactor - Visual amplification applied to the spinning dot animation. Default `18`\n * keeps the legacy visual feel (≈1 rpm at 20°/min).\n * @property hasTrackBar - When `true`, shows a bar in the ring band from the twelve o'clock\n * position to the measured rate of turn, with a needle marker at its end\n * and sector tickmarks. Pairs with `watchCircleType=\"double\"` for the\n * banded face — or `\"triple\"` when `rotPosition` is `innerCircle`, so the\n * dots get their inner track too. Driven by `rateOfTurnDegreesPerMinute`\n * only.\n * @property rotMaxValue - Bar-extent reference value in **degrees per minute**: the track bar\n * reaches ±`rotArcExtent` when the measured ROT equals ±`rotMaxValue`.\n * Default `60` aligns with ES-TRIN 2025/1 Art. 3.02.\n * @availableWhen rotMaxValue hasTrackBar==true\n * @property rotArcExtent - Arc extent of the track bar in degrees per direction. Default `60`.\n * @availableWhen rotArcExtent hasTrackBar==true\n * @property hasReadout - When `true`, shows a centered `<obc-readout>` with the measured rate of\n * turn. Shows a dash while `rateOfTurnDegreesPerMinute` is unset.\n * @property label - Readout label. Default `ROT`.\n * @availableWhen label hasReadout==true\n * @property unit - Readout unit. Default `DEG/min`.\n * @availableWhen unit hasReadout==true\n * @property fractionDigits - Number of fraction digits shown in the readout. Default `0`.\n * @availableWhen fractionDigits hasReadout==true\n * @stable\n */\n@customElement('obc-rate-of-turn')\nexport class ObcRateOfTurn extends LitElement {\n @property({type: Number}) rateOfTurnDegreesPerMinute: number | undefined;\n\n @property({type: Number}) rotDotAnimationFactor: number = 18;\n\n /**\n * @deprecated Use `rateOfTurnDegreesPerMinute` (and optionally\n * `rotDotAnimationFactor`) instead. Takes effect only when\n * `rateOfTurnDegreesPerMinute` is `undefined`.\n */\n @property({type: Number}) rotationsPerMinute: number = 1;\n\n @property({type: String}) rotType: RotType = RotType.dots;\n @property({type: String}) rotPosition: RotPosition = RotPosition.scale;\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: Number}) barStartAngle: number = 0;\n @property({type: Number}) barEndAngle: number = 30;\n @property({type: String}) watchCircleType: WatchCircleType =\n WatchCircleType.single;\n @property({type: Boolean}) rotPortStarboard: boolean = false;\n @property({type: Number}) rotAtZeroDeadband: number = ROT_ZERO_DEADBAND_DEG;\n @property({type: Boolean}) hasTrackBar: boolean = false;\n @property({type: Number}) rotMaxValue: number = 60;\n @property({type: Number}) rotArcExtent: number = 60;\n @property({type: Boolean}) hasReadout: boolean = false;\n @property({type: String}) label = 'ROT';\n @property({type: String}) unit = 'DEG/min';\n @property({type: Number}) fractionDigits = 0;\n\n private get trackBarAngle(): number {\n const max = this.rotMaxValue || 1;\n const rot = this.rateOfTurnDegreesPerMinute ?? 0;\n const ratio = clamp(rot / max, -1, 1);\n return ratio * this.rotArcExtent;\n }\n\n private get trackBarColor(): string {\n if (this.rotPortStarboard) {\n const rot = this.rateOfTurnDegreesPerMinute ?? 0;\n if (rot > 0) {\n return 'var(--instrument-starboard-secondary-color)';\n }\n if (rot < 0) {\n return 'var(--instrument-port-secondary-color)';\n }\n }\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-tertiary-color)'\n : 'var(--instrument-regular-tertiary-color)';\n }\n\n private get trackNeedleColor(): string {\n if (this.rotPortStarboard) {\n const rot = this.rateOfTurnDegreesPerMinute ?? 0;\n if (rot > 0) {\n return 'var(--instrument-starboard-primary-color)';\n }\n if (rot < 0) {\n return 'var(--instrument-port-primary-color)';\n }\n return 'var(--instrument-regular-secondary-color)';\n }\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n }\n\n private get trackTickmarks(): Tickmark[] {\n const ticks: Tickmark[] = [{angle: 0, type: TickmarkType.main}];\n for (let angle = 30; angle <= this.rotArcExtent; angle += 30) {\n ticks.push({angle, type: TickmarkType.primary});\n ticks.push({angle: -angle, type: TickmarkType.primary});\n }\n return ticks;\n }\n\n static override styles = [\n unsafeCSS(instrumentReadoutStyle),\n centerReadoutStyles,\n css`\n * {\n box-sizing: border-box;\n }\n\n .container {\n position: relative;\n width: 100%;\n height: 100%;\n }\n\n .container > * {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n }\n\n .center-readout-overlay {\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n }\n `,\n ];\n\n override render() {\n return html`<div class=\"container\">\n <obc-watch\n .watchCircleType=${this.watchCircleType}\n .priority=${this.priority}\n .tickmarks=${this.hasTrackBar ? this.trackTickmarks : []}\n .barAreas=${\n this.hasTrackBar\n ? [\n {\n startAngle: 0,\n endAngle: this.trackBarAngle,\n fillColor: this.trackBarColor,\n },\n ]\n : []\n }\n .needles=${\n this.hasTrackBar\n ? [\n {\n angle: this.trackBarAngle,\n fillColor: this.trackNeedleColor,\n strokeColor: 'var(--border-silhouette-color)',\n },\n ]\n : []\n }\n .rotType=${this.rotType}\n .rotPosition=${this.rotPosition}\n .rotStartAngle=${this.barStartAngle}\n .rotEndAngle=${this.barEndAngle}\n .rateOfTurnDegreesPerMinute=${this.rateOfTurnDegreesPerMinute}\n .rotDotAnimationFactor=${this.rotDotAnimationFactor}\n .rotationsPerMinute=${this.rotationsPerMinute}\n .rotPortStarboard=${this.rotPortStarboard}\n .rotAtZeroDeadband=${this.rotAtZeroDeadband}\n ></obc-watch>\n ${\n this.hasReadout\n ? html`<div class=\"center-readout-overlay\">\n ${renderCenterReadouts([\n {\n value: this.rateOfTurnDegreesPerMinute ?? null,\n label: this.label,\n unit: this.unit,\n fractionDigits: this.fractionDigits,\n size: ReadoutSize.large,\n priority: this.priority,\n centerValue: true,\n centerMeta: true,\n },\n ])}\n </div>`\n : nothing\n }\n </div>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-rate-of-turn': ObcRateOfTurn;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rate-of-turn.js","names":[],"sources":["../../../src/navigation-instruments/rate-of-turn/rate-of-turn.ts"],"sourcesContent":["import {LitElement, css, html, nothing, unsafeCSS} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport '../watch/watch.js';\nimport {WatchCircleType, RotType, RotPosition} from '../watch/watch.js';\nimport {Tickmark, TickmarkType} from '../watch/tickmark.js';\nimport {\n centerReadoutStyles,\n renderCenterReadouts,\n} from '../readout/center-readout.js';\nimport {ReadoutSize} from '../readout/readout.js';\nimport instrumentReadoutStyle from '../readout/instrument-readout.css?inline';\nimport {ROT_ZERO_DEADBAND_DEG} from './rot-renderer.js';\nimport {customElement} from '../../decorator.js';\nimport {Priority} from '../types.js';\nimport {clamp} from '../../svghelpers/math.js';\n\nexport {RotType, RotPosition};\n\n/**\n * `<obc-rate-of-turn>` — Standalone rate-of-turn indicator rendered on a circular watch face.\n *\n * Wraps `<obc-watch>` to display a spinning-dot or arc-bar ROT visualization\n * without any additional instrument overlays. Useful for isolating the ROT\n * indicator in layouts where heading/compass elements are handled separately.\n *\n * ## Features\n *\n * - **Dot mode** (`rotType=\"dots\"`): Five evenly-spaced dots spin at the\n * resolved rotations-per-minute (derived from\n * `rateOfTurnDegreesPerMinute × rotDotAnimationFactor / 360`).\n * - **Bar mode** (`rotType=\"bar\"`): A banana-shaped arc from `barStartAngle`\n * to `barEndAngle` with clipped spinning dots inside.\n * - **Track position**: Place the indicator on the outer scale ring\n * (`rotPosition=\"scale\"`) or the inner circle\n * (`rotPosition=\"innerCircle\"`).\n * - **Track bar** (`hasTrackBar`): A bar in the ring band growing from the\n * twelve o'clock position to the measured rate of turn, with a needle\n * marker at its end and sector tickmarks (pairs with\n * `watchCircleType=\"double\"` for the banded face, or `\"triple\"` when the\n * ROT dots sit on the inner circle).\n * - **Center readout** (`hasReadout`): A centered readout showing the\n * measured rate of turn (label `ROT`, unit `DEG/min` by default).\n * - **Color priority**: Uses `priority` to select regular or enhanced color\n * palette; `rotPortStarboard` colors the track bar and needle by turn\n * direction.\n *\n * ## Usage Guidelines\n *\n * - Set `rateOfTurnDegreesPerMinute` to the current sensor value in degrees\n * per minute (the maritime/AIS convention). Sign controls direction\n * (positive = starboard / clockwise).\n * - Tune `rotDotAnimationFactor` to amplify the dot animation independently\n * of the physical value (default `18` ≈ 1 rpm at 20°/min).\n * - In bar mode, `barStartAngle` and `barEndAngle` define the static arc\n * span (0° = 12 o'clock, clockwise).\n * - Change `watchCircleType` to match the surrounding instrument ring style\n * (e.g. `triple` for compass contexts).\n *\n * @element obc-rate-of-turn\n *\n * @property rateOfTurnDegreesPerMinute - Measured rate of turn in degrees per minute (positive = starboard).\n * When `undefined`, the legacy `rotationsPerMinute` value is used.\n * @property rotDotAnimationFactor - Visual amplification applied to the spinning dot animation. Default `18`\n * keeps the legacy visual feel (≈1 rpm at 20°/min).\n * @property hasTrackBar - When `true`, shows a bar in the ring band from the twelve o'clock\n * position to the measured rate of turn, with a needle marker at its end\n * and sector tickmarks. Pairs with `watchCircleType=\"double\"` for the\n * banded face — or `\"triple\"` when `rotPosition` is `innerCircle`, so the\n * dots get their inner track too. Driven by `rateOfTurnDegreesPerMinute`\n * only.\n * @property rotMaxValue - Bar-extent reference value in **degrees per minute**: the track bar\n * reaches ±`rotArcExtent` when the measured ROT equals ±`rotMaxValue`.\n * Default `60` aligns with ES-TRIN 2025/1 Art. 3.02.\n * @availableWhen rotMaxValue hasTrackBar==true\n * @property rotArcExtent - Arc extent of the track bar in degrees per direction. Default `60`.\n * @availableWhen rotArcExtent hasTrackBar==true\n * @property hasReadout - When `true`, shows a centered `<obc-readout>` with the measured rate of\n * turn. Shows a dash while `rateOfTurnDegreesPerMinute` is unset.\n * @property label - Readout label. Default `ROT`.\n * @availableWhen label hasReadout==true\n * @property unit - Readout unit. Default `DEG/min`.\n * @availableWhen unit hasReadout==true\n * @property fractionDigits - Number of fraction digits shown in the readout. Default `0`.\n * @availableWhen fractionDigits hasReadout==true\n * @stable\n */\n@customElement('obc-rate-of-turn')\nexport class ObcRateOfTurn extends LitElement {\n @property({type: Number}) rateOfTurnDegreesPerMinute: number | undefined;\n\n @property({type: Number}) rotDotAnimationFactor: number = 18;\n\n /**\n * @deprecated Use `rateOfTurnDegreesPerMinute` (and optionally\n * `rotDotAnimationFactor`) instead. Takes effect only when\n * `rateOfTurnDegreesPerMinute` is `undefined`.\n */\n @property({type: Number}) rotationsPerMinute: number = 0;\n\n @property({type: String}) rotType: RotType = RotType.dots;\n @property({type: String}) rotPosition: RotPosition = RotPosition.scale;\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: Number}) barStartAngle: number = 0;\n @property({type: Number}) barEndAngle: number = 30;\n @property({type: String}) watchCircleType: WatchCircleType =\n WatchCircleType.single;\n @property({type: Boolean}) rotPortStarboard: boolean = false;\n @property({type: Number}) rotAtZeroDeadband: number = ROT_ZERO_DEADBAND_DEG;\n @property({type: Boolean}) hasTrackBar: boolean = false;\n @property({type: Number}) rotMaxValue: number = 60;\n @property({type: Number}) rotArcExtent: number = 60;\n @property({type: Boolean}) hasReadout: boolean = false;\n @property({type: String}) label = 'ROT';\n @property({type: String}) unit = 'DEG/min';\n @property({type: Number}) fractionDigits = 0;\n\n private get trackBarAngle(): number {\n const max = this.rotMaxValue || 1;\n const rot = this.rateOfTurnDegreesPerMinute ?? 0;\n const ratio = clamp(rot / max, -1, 1);\n return ratio * this.rotArcExtent;\n }\n\n private get trackBarColor(): string {\n if (this.rotPortStarboard) {\n const rot = this.rateOfTurnDegreesPerMinute ?? 0;\n if (rot > 0) {\n return 'var(--instrument-starboard-secondary-color)';\n }\n if (rot < 0) {\n return 'var(--instrument-port-secondary-color)';\n }\n }\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-tertiary-color)'\n : 'var(--instrument-regular-tertiary-color)';\n }\n\n private get trackNeedleColor(): string {\n if (this.rotPortStarboard) {\n const rot = this.rateOfTurnDegreesPerMinute ?? 0;\n if (rot > 0) {\n return 'var(--instrument-starboard-primary-color)';\n }\n if (rot < 0) {\n return 'var(--instrument-port-primary-color)';\n }\n return 'var(--instrument-regular-secondary-color)';\n }\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n }\n\n private get trackTickmarks(): Tickmark[] {\n const ticks: Tickmark[] = [{angle: 0, type: TickmarkType.main}];\n for (let angle = 30; angle <= this.rotArcExtent; angle += 30) {\n ticks.push({angle, type: TickmarkType.primary});\n ticks.push({angle: -angle, type: TickmarkType.primary});\n }\n return ticks;\n }\n\n static override styles = [\n unsafeCSS(instrumentReadoutStyle),\n centerReadoutStyles,\n css`\n * {\n box-sizing: border-box;\n }\n\n .container {\n position: relative;\n width: 100%;\n height: 100%;\n }\n\n .container > * {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n }\n\n .center-readout-overlay {\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n }\n `,\n ];\n\n override render() {\n return html`<div class=\"container\">\n <obc-watch\n .watchCircleType=${this.watchCircleType}\n .priority=${this.priority}\n .tickmarks=${this.hasTrackBar ? this.trackTickmarks : []}\n .barAreas=${\n this.hasTrackBar\n ? [\n {\n startAngle: 0,\n endAngle: this.trackBarAngle,\n fillColor: this.trackBarColor,\n },\n ]\n : []\n }\n .needles=${\n this.hasTrackBar\n ? [\n {\n angle: this.trackBarAngle,\n fillColor: this.trackNeedleColor,\n strokeColor: 'var(--border-silhouette-color)',\n },\n ]\n : []\n }\n .rotType=${this.rotType}\n .rotPosition=${this.rotPosition}\n .rotStartAngle=${this.barStartAngle}\n .rotEndAngle=${this.barEndAngle}\n .rateOfTurnDegreesPerMinute=${this.rateOfTurnDegreesPerMinute}\n .rotDotAnimationFactor=${this.rotDotAnimationFactor}\n .rotationsPerMinute=${this.rotationsPerMinute}\n .rotPortStarboard=${this.rotPortStarboard}\n .rotAtZeroDeadband=${this.rotAtZeroDeadband}\n ></obc-watch>\n ${\n this.hasReadout\n ? html`<div class=\"center-readout-overlay\">\n ${renderCenterReadouts([\n {\n value: this.rateOfTurnDegreesPerMinute ?? null,\n label: this.label,\n unit: this.unit,\n fractionDigits: this.fractionDigits,\n size: ReadoutSize.large,\n priority: this.priority,\n centerValue: true,\n centerMeta: true,\n },\n ])}\n </div>`\n : nothing\n }\n </div>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-rate-of-turn': ObcRateOfTurn;\n }\n}\n"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  import { FlashTempo } from '../types.js';
2
- /** Shared on phase: every tempo lights up for the first 400 ms of its cycle. */
3
- export declare const FLASH_ON_MS: Record<FlashTempo, number>;
4
2
  /**
5
- * The off phase carries the tempo. The cycles (800/1600/3200 ms) nest, so
6
- * every tempo lights up together at the start of the longest one (#1224).
3
+ * The on phase carries the tempo, so a flashing alert stays coloured for most
4
+ * of its cycle. The cycles (800/1600/3200 ms) nest, so every tempo lights up
5
+ * together at the start of the longest one and goes off with it (#1367).
7
6
  */
7
+ export declare const FLASH_ON_MS: Record<FlashTempo, number>;
8
+ /** Shared off phase: every tempo goes off for the last 400 ms of its cycle. */
8
9
  export declare const FLASH_OFF_MS: Record<FlashTempo, number>;
9
10
  export declare const FLASH_TEMPOS: readonly FlashTempo[];
10
11
  export declare function flashPeriodMs(tempo: FlashTempo): number;
@@ -1 +1 @@
1
- {"version":3,"file":"blinking.d.ts","sourceRoot":"","sources":["../../src/palettes/blinking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,UAAU,EAAC,MAAM,aAAa,CAAC;AAE3D,gFAAgF;AAChF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAIlD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAInD,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAI7C,CAAC;AAEF,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEvD;AAED,qGAAqG;AACrG,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,GAAG,KAAK,GAAG,MAAM,CAE5E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,WAAW,EACf,KAAK,EAAE,UAAU,GAChB,MAAM,IAAI,CAkBZ;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,EAAE,EAAE,WAAW,GAAG,MAAM,IAAI,CAGvD"}
1
+ {"version":3,"file":"blinking.d.ts","sourceRoot":"","sources":["../../src/palettes/blinking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,UAAU,EAAC,MAAM,aAAa,CAAC;AAE3D;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAIlD,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAInD,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAI7C,CAAC;AAEF,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEvD;AAED,qGAAqG;AACrG,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,GAAG,KAAK,GAAG,MAAM,CAE5E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,WAAW,EACf,KAAK,EAAE,UAAU,GAChB,MAAM,IAAI,CAkBZ;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,EAAE,EAAE,WAAW,GAAG,MAAM,IAAI,CAGvD"}
@@ -1,20 +1,21 @@
1
1
  import { FlashingSpeed } from "../types.js";
2
2
  //#region src/palettes/blinking.ts
3
- /** Shared on phase: every tempo lights up for the first 400 ms of its cycle. */
4
- var FLASH_ON_MS = {
5
- [FlashingSpeed.Fast]: 400,
6
- [FlashingSpeed.Slow]: 400,
7
- [FlashingSpeed.VerySlow]: 400
8
- };
9
3
  /**
10
- * The off phase carries the tempo. The cycles (800/1600/3200 ms) nest, so
11
- * every tempo lights up together at the start of the longest one (#1224).
4
+ * The on phase carries the tempo, so a flashing alert stays coloured for most
5
+ * of its cycle. The cycles (800/1600/3200 ms) nest, so every tempo lights up
6
+ * together at the start of the longest one and goes off with it (#1367).
12
7
  */
13
- var FLASH_OFF_MS = {
8
+ var FLASH_ON_MS = {
14
9
  [FlashingSpeed.Fast]: 400,
15
10
  [FlashingSpeed.Slow]: 1200,
16
11
  [FlashingSpeed.VerySlow]: 2800
17
12
  };
13
+ /** Shared off phase: every tempo goes off for the last 400 ms of its cycle. */
14
+ var FLASH_OFF_MS = {
15
+ [FlashingSpeed.Fast]: 400,
16
+ [FlashingSpeed.Slow]: 400,
17
+ [FlashingSpeed.VerySlow]: 400
18
+ };
18
19
  var FLASH_TEMPOS = [
19
20
  FlashingSpeed.Fast,
20
21
  FlashingSpeed.Slow,
@@ -1 +1 @@
1
- {"version":3,"file":"blinking.js","names":[],"sources":["../../src/palettes/blinking.ts"],"sourcesContent":["import {FlashingSpeed, type FlashTempo} from '../types.js';\n\n/** Shared on phase: every tempo lights up for the first 400 ms of its cycle. */\nexport const FLASH_ON_MS: Record<FlashTempo, number> = {\n [FlashingSpeed.Fast]: 400,\n [FlashingSpeed.Slow]: 400,\n [FlashingSpeed.VerySlow]: 400,\n};\n\n/**\n * The off phase carries the tempo. The cycles (800/1600/3200 ms) nest, so\n * every tempo lights up together at the start of the longest one (#1224).\n */\nexport const FLASH_OFF_MS: Record<FlashTempo, number> = {\n [FlashingSpeed.Fast]: 400,\n [FlashingSpeed.Slow]: 1200,\n [FlashingSpeed.VerySlow]: 2800,\n};\n\nexport const FLASH_TEMPOS: readonly FlashTempo[] = [\n FlashingSpeed.Fast,\n FlashingSpeed.Slow,\n FlashingSpeed.VerySlow,\n];\n\nexport function flashPeriodMs(tempo: FlashTempo): number {\n return FLASH_ON_MS[tempo] + FLASH_OFF_MS[tempo];\n}\n\n/** Animated custom property name; `on` reads 1 during the on phase, `off` 1 during the off phase. */\nexport function flashVariable(tempo: FlashTempo, phase: 'on' | 'off'): string {\n return `--flash-${tempo}-${phase}`;\n}\n\n/**\n * Animates `--flash-<tempo>-on` / `-off` on `el` for one tempo. Returns the\n * cancel function.\n */\nexport function installFlashing(\n el: HTMLElement,\n tempo: FlashTempo\n): () => void {\n const on = flashVariable(tempo, 'on');\n const off = flashVariable(tempo, 'off');\n const period = flashPeriodMs(tempo);\n const frames: Keyframe[] = [\n {[on]: 1, [off]: 0, easing: 'step-end'},\n {\n [on]: 0,\n [off]: 1,\n offset: FLASH_ON_MS[tempo] / period,\n easing: 'step-end',\n },\n {[on]: 0, [off]: 1},\n ];\n const anim = el.animate(frames, {duration: period, iterations: Infinity});\n // Document-timeline origin, so every flashing element is in the same phase.\n anim.startTime = 0;\n return () => anim.cancel();\n}\n\n/** Installs every tempo on `el`, for CSS that reads the variables directly. */\nexport function blinkingAll(el: HTMLElement): () => void {\n const cancels = FLASH_TEMPOS.map((tempo) => installFlashing(el, tempo));\n return () => cancels.forEach((cancel) => cancel());\n}\n"],"mappings":";;;AAGA,IAAa,cAA0C;EACpD,cAAc,OAAO;EACrB,cAAc,OAAO;EACrB,cAAc,WAAW;AAC5B;;;;;AAMA,IAAa,eAA2C;EACrD,cAAc,OAAO;EACrB,cAAc,OAAO;EACrB,cAAc,WAAW;AAC5B;AAEA,IAAa,eAAsC;CACjD,cAAc;CACd,cAAc;CACd,cAAc;AAChB;AAEA,SAAgB,cAAc,OAA2B;CACvD,OAAO,YAAY,SAAS,aAAa;AAC3C;;AAGA,SAAgB,cAAc,OAAmB,OAA6B;CAC5E,OAAO,WAAW,MAAM,GAAG;AAC7B;;;;;AAMA,SAAgB,gBACd,IACA,OACY;CACZ,MAAM,KAAK,cAAc,OAAO,IAAI;CACpC,MAAM,MAAM,cAAc,OAAO,KAAK;CACtC,MAAM,SAAS,cAAc,KAAK;CAClC,MAAM,SAAqB;EACzB;IAAE,KAAK;IAAI,MAAM;GAAG,QAAQ;EAAU;EACtC;IACG,KAAK;IACL,MAAM;GACP,QAAQ,YAAY,SAAS;GAC7B,QAAQ;EACV;EACA;IAAE,KAAK;IAAI,MAAM;EAAC;CACpB;CACA,MAAM,OAAO,GAAG,QAAQ,QAAQ;EAAC,UAAU;EAAQ,YAAY;CAAQ,CAAC;CAExE,KAAK,YAAY;CACjB,aAAa,KAAK,OAAO;AAC3B;;AAGA,SAAgB,YAAY,IAA6B;CACvD,MAAM,UAAU,aAAa,KAAK,UAAU,gBAAgB,IAAI,KAAK,CAAC;CACtE,aAAa,QAAQ,SAAS,WAAW,OAAO,CAAC;AACnD"}
1
+ {"version":3,"file":"blinking.js","names":[],"sources":["../../src/palettes/blinking.ts"],"sourcesContent":["import {FlashingSpeed, type FlashTempo} from '../types.js';\n\n/**\n * The on phase carries the tempo, so a flashing alert stays coloured for most\n * of its cycle. The cycles (800/1600/3200 ms) nest, so every tempo lights up\n * together at the start of the longest one and goes off with it (#1367).\n */\nexport const FLASH_ON_MS: Record<FlashTempo, number> = {\n [FlashingSpeed.Fast]: 400,\n [FlashingSpeed.Slow]: 1200,\n [FlashingSpeed.VerySlow]: 2800,\n};\n\n/** Shared off phase: every tempo goes off for the last 400 ms of its cycle. */\nexport const FLASH_OFF_MS: Record<FlashTempo, number> = {\n [FlashingSpeed.Fast]: 400,\n [FlashingSpeed.Slow]: 400,\n [FlashingSpeed.VerySlow]: 400,\n};\n\nexport const FLASH_TEMPOS: readonly FlashTempo[] = [\n FlashingSpeed.Fast,\n FlashingSpeed.Slow,\n FlashingSpeed.VerySlow,\n];\n\nexport function flashPeriodMs(tempo: FlashTempo): number {\n return FLASH_ON_MS[tempo] + FLASH_OFF_MS[tempo];\n}\n\n/** Animated custom property name; `on` reads 1 during the on phase, `off` 1 during the off phase. */\nexport function flashVariable(tempo: FlashTempo, phase: 'on' | 'off'): string {\n return `--flash-${tempo}-${phase}`;\n}\n\n/**\n * Animates `--flash-<tempo>-on` / `-off` on `el` for one tempo. Returns the\n * cancel function.\n */\nexport function installFlashing(\n el: HTMLElement,\n tempo: FlashTempo\n): () => void {\n const on = flashVariable(tempo, 'on');\n const off = flashVariable(tempo, 'off');\n const period = flashPeriodMs(tempo);\n const frames: Keyframe[] = [\n {[on]: 1, [off]: 0, easing: 'step-end'},\n {\n [on]: 0,\n [off]: 1,\n offset: FLASH_ON_MS[tempo] / period,\n easing: 'step-end',\n },\n {[on]: 0, [off]: 1},\n ];\n const anim = el.animate(frames, {duration: period, iterations: Infinity});\n // Document-timeline origin, so every flashing element is in the same phase.\n anim.startTime = 0;\n return () => anim.cancel();\n}\n\n/** Installs every tempo on `el`, for CSS that reads the variables directly. */\nexport function blinkingAll(el: HTMLElement): () => void {\n const cancels = FLASH_TEMPOS.map((tempo) => installFlashing(el, tempo));\n return () => cancels.forEach((cancel) => cancel());\n}\n"],"mappings":";;;;;;;AAOA,IAAa,cAA0C;EACpD,cAAc,OAAO;EACrB,cAAc,OAAO;EACrB,cAAc,WAAW;AAC5B;;AAGA,IAAa,eAA2C;EACrD,cAAc,OAAO;EACrB,cAAc,OAAO;EACrB,cAAc,WAAW;AAC5B;AAEA,IAAa,eAAsC;CACjD,cAAc;CACd,cAAc;CACd,cAAc;AAChB;AAEA,SAAgB,cAAc,OAA2B;CACvD,OAAO,YAAY,SAAS,aAAa;AAC3C;;AAGA,SAAgB,cAAc,OAAmB,OAA6B;CAC5E,OAAO,WAAW,MAAM,GAAG;AAC7B;;;;;AAMA,SAAgB,gBACd,IACA,OACY;CACZ,MAAM,KAAK,cAAc,OAAO,IAAI;CACpC,MAAM,MAAM,cAAc,OAAO,KAAK;CACtC,MAAM,SAAS,cAAc,KAAK;CAClC,MAAM,SAAqB;EACzB;IAAE,KAAK;IAAI,MAAM;GAAG,QAAQ;EAAU;EACtC;IACG,KAAK;IACL,MAAM;GACP,QAAQ,YAAY,SAAS;GAC7B,QAAQ;EACV;EACA;IAAE,KAAK;IAAI,MAAM;EAAC;CACpB;CACA,MAAM,OAAO,GAAG,QAAQ,QAAQ;EAAC,UAAU;EAAQ,YAAY;CAAQ,CAAC;CAExE,KAAK,YAAY;CACjB,aAAa,KAAK,OAAO;AAC3B;;AAGA,SAAgB,YAAY,IAA6B;CACvD,MAAM,UAAU,aAAa,KAAK,UAAU,gBAAgB,IAAI,KAAK,CAAC;CACtE,aAAa,QAAQ,SAAS,WAAW,OAAO,CAAC;AACnD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oicl/openbridge-webcomponents",
3
- "version": "2.1.0-next.8",
3
+ "version": "2.1.0-next.9",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",