@oicl/openbridge-webcomponents 2.0.0-next.56 → 2.0.0-next.58

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 (46) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +987 -362
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +576 -10
  4. package/dist/building-blocks/instrument-radial/instrument-radial.d.ts +10 -0
  5. package/dist/building-blocks/instrument-radial/instrument-radial.d.ts.map +1 -1
  6. package/dist/building-blocks/instrument-radial/instrument-radial.js +86 -21
  7. package/dist/building-blocks/instrument-radial/instrument-radial.js.map +1 -1
  8. package/dist/navigation-instruments/compass-sector/compass-sector.css.js +12 -0
  9. package/dist/navigation-instruments/compass-sector/compass-sector.css.js.map +1 -1
  10. package/dist/navigation-instruments/compass-sector/compass-sector.d.ts +23 -0
  11. package/dist/navigation-instruments/compass-sector/compass-sector.d.ts.map +1 -1
  12. package/dist/navigation-instruments/compass-sector/compass-sector.js +47 -0
  13. package/dist/navigation-instruments/compass-sector/compass-sector.js.map +1 -1
  14. package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js +99 -0
  15. package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js.map +1 -0
  16. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts +42 -7
  17. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts.map +1 -1
  18. package/dist/navigation-instruments/gauge-radial/gauge-radial.js +178 -31
  19. package/dist/navigation-instruments/gauge-radial/gauge-radial.js.map +1 -1
  20. package/dist/navigation-instruments/pitch/pitch.d.ts +37 -0
  21. package/dist/navigation-instruments/pitch/pitch.d.ts.map +1 -1
  22. package/dist/navigation-instruments/pitch/pitch.js +130 -62
  23. package/dist/navigation-instruments/pitch/pitch.js.map +1 -1
  24. package/dist/navigation-instruments/pitch-roll/pitch-roll.d.ts +7 -0
  25. package/dist/navigation-instruments/pitch-roll/pitch-roll.d.ts.map +1 -1
  26. package/dist/navigation-instruments/pitch-roll/pitch-roll.js +58 -2
  27. package/dist/navigation-instruments/pitch-roll/pitch-roll.js.map +1 -1
  28. package/dist/navigation-instruments/readout/readout.css.js +4 -0
  29. package/dist/navigation-instruments/readout/readout.css.js.map +1 -1
  30. package/dist/navigation-instruments/roll/roll.d.ts +37 -0
  31. package/dist/navigation-instruments/roll/roll.d.ts.map +1 -1
  32. package/dist/navigation-instruments/roll/roll.js +119 -63
  33. package/dist/navigation-instruments/roll/roll.js.map +1 -1
  34. package/dist/navigation-instruments/rot-sector/rot-sector.d.ts +15 -0
  35. package/dist/navigation-instruments/rot-sector/rot-sector.d.ts.map +1 -1
  36. package/dist/navigation-instruments/rot-sector/rot-sector.js +53 -1
  37. package/dist/navigation-instruments/rot-sector/rot-sector.js.map +1 -1
  38. package/dist/navigation-instruments/watch/tickmark.d.ts +2 -1
  39. package/dist/navigation-instruments/watch/tickmark.d.ts.map +1 -1
  40. package/dist/navigation-instruments/watch/tickmark.js +24 -4
  41. package/dist/navigation-instruments/watch/tickmark.js.map +1 -1
  42. package/dist/navigation-instruments/watch/watch.d.ts +23 -1
  43. package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
  44. package/dist/navigation-instruments/watch/watch.js +48 -20
  45. package/dist/navigation-instruments/watch/watch.js.map +1 -1
  46. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"compass-sector.js","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 componentStyle from './compass-sector.css?inline';\nimport '../watch/watch.js';\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 computeZoomToFitArcFrame,\n type ZoomToFitArcFrame,\n} from '../../svghelpers/arc-frame.js';\nimport {ROT_ZERO_DEADBAND_DEG} from '../rate-of-turn/rot-renderer.js';\nimport {customElement} from '../../decorator.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\nconst PADDING = 72;\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\nfunction normalizeAngle(a: number): number {\n return ((a % 360) + 360) % 360;\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 * @fires None\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 /**\n * 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 */\n @property({type: Number}) rateOfTurnDegreesPerMinute: number | undefined;\n /**\n * 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 */\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 */\n @property({type: Number}) rotationsPerMinute: number = 1;\n /**\n * Bar-extent reference value in **degrees per minute**. The bar fills the\n * full ±`ARC_HALF_EXTENT` arc when the measured ROT equals\n * ±`rotMaxValue`. Default `60` aligns with ES-TRIN 2025/1 Art. 3.02.\n *\n * Note: the unit changed from rotations per minute to degrees per minute\n * with the introduction of `rateOfTurnDegreesPerMinute`.\n */\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\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 _radiusOffset = 0;\n private _cachedViewBox = '';\n private _cachedArcFrame: ZoomToFitArcFrame | undefined;\n private _cachedAreas: WatchArea[] = [];\n private _cachedTickmarks: Tickmark[] = [];\n private _cachedAdvices: AngleAdviceRaw[] = [];\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._computeViewBox();\n this._cachedTickmarks = this._buildTickmarks();\n }\n\n if (arcInputsChanged || changed.has('headingAdvices')) {\n this._cachedAdvices = this._buildAdvices();\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 private _computeViewBox(): void {\n if (this.zoomToFitArc) {\n const targetSize = (176 + PADDING) * 2;\n const frame = computeZoomToFitArcFrame({\n areas: this._cachedAreas,\n outerRadius: OUTER_RING_RADIUS,\n innerRadius: INNER_RADIUS,\n extension: PADDING,\n targetSize,\n });\n this._radiusOffset = frame.radiusOffset;\n this._cachedViewBox = frame.viewBox;\n this._cachedArcFrame = frame;\n } else {\n this._radiusOffset = 0;\n const width = (176 + PADDING) * 2;\n this._cachedViewBox = `-${width / 2} -${width / 2} ${width} ${width}`;\n this._cachedArcFrame = undefined;\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 // Render\n // ---------------------------------------------------------------------------\n\n override render() {\n const rotation = -this.heading;\n const viewBox = this._cachedViewBox;\n const rOff = this._radiusOffset;\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 .padding=${PADDING}\n .advices=${this._cachedAdvices}\n .tickmarks=${this._cachedTickmarks}\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=${this._cachedArcFrame}\n .zoomToFitArc=${this.zoomToFitArc}\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 viewBox=\"${viewBox}\" transform=\"rotate(${rotation})\">\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 </div>\n `;\n }\n\n static override styles = unsafeCSS(componentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-compass-sector': ObcCompassSector;\n }\n}\n"],"names":["CompassSectorPriorityElement"],"mappings":"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"}
1
+ {"version":3,"file":"compass-sector.js","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 componentStyle from './compass-sector.css?inline';\nimport '../watch/watch.js';\nimport '../readout/readout.js';\nimport {ReadoutDirection, ReadoutVariant} from '../readout/readout.js';\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 computeZoomToFitArcFrame,\n type ZoomToFitArcFrame,\n} from '../../svghelpers/arc-frame.js';\nimport {ROT_ZERO_DEADBAND_DEG} from '../rate-of-turn/rot-renderer.js';\nimport {customElement} from '../../decorator.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\nconst PADDING = 72;\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\nfunction normalizeAngle(a: number): number {\n return ((a % 360) + 360) % 360;\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 * @fires None\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 /**\n * 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 */\n @property({type: Number}) rateOfTurnDegreesPerMinute: number | undefined;\n /**\n * 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 */\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 */\n @property({type: Number}) rotationsPerMinute: number = 1;\n /**\n * Bar-extent reference value in **degrees per minute**. The bar fills the\n * full ±`ARC_HALF_EXTENT` arc when the measured ROT equals\n * ±`rotMaxValue`. Default `60` aligns with ES-TRIN 2025/1 Art. 3.02.\n *\n * Note: the unit changed from rotations per minute to degrees per minute\n * with the introduction of `rateOfTurnDegreesPerMinute`.\n */\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 /**\n * 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 */\n @property({type: Boolean}) hasReadout: boolean = false;\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 _radiusOffset = 0;\n private _cachedViewBox = '';\n private _cachedArcFrame: ZoomToFitArcFrame | undefined;\n private _cachedAreas: WatchArea[] = [];\n private _cachedTickmarks: Tickmark[] = [];\n private _cachedAdvices: AngleAdviceRaw[] = [];\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._computeViewBox();\n this._cachedTickmarks = this._buildTickmarks();\n }\n\n if (arcInputsChanged || changed.has('headingAdvices')) {\n this._cachedAdvices = this._buildAdvices();\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 private _computeViewBox(): void {\n if (this.zoomToFitArc) {\n const targetSize = (176 + PADDING) * 2;\n const frame = computeZoomToFitArcFrame({\n areas: this._cachedAreas,\n outerRadius: OUTER_RING_RADIUS,\n innerRadius: INNER_RADIUS,\n extension: PADDING,\n targetSize,\n });\n this._radiusOffset = frame.radiusOffset;\n this._cachedViewBox = frame.viewBox;\n this._cachedArcFrame = frame;\n } else {\n this._radiusOffset = 0;\n const width = (176 + PADDING) * 2;\n this._cachedViewBox = `-${width / 2} -${width / 2} ${width} ${width}`;\n this._cachedArcFrame = undefined;\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 host height.\n *\n * With `zoomToFitArc` the arc is reframed by `computeZoomToFitArcFrame`, whose\n * `radiusOffset`/`viewBox` depend on the arc's *absolute* orientation (which\n * cardinal axes its bounding box crosses), not just its bend. The arc itself\n * is always rotated back to the top, so it stays put on screen — but a\n * frame-derived offset would swing wildly as `heading` rotates the bbox around\n * the circle. So in zoom we use a fixed offset, which keeps the readout steady\n * under the (stationary) arc regardless of heading.\n *\n * Without zoom the viewBox is the fixed, origin-symmetric 120° framing, so the\n * geometry is orientation-independent: place the readout halfway down the inner\n * radius from the watch center toward the arc's inner edge.\n */\n private get _readoutTopPercent(): number {\n if (this.zoomToFitArc) {\n return 70;\n }\n const [, vy, , vh] = this._cachedViewBox.split(' ').map(Number);\n if (!vh || Number.isNaN(vy)) {\n return 50;\n }\n const anchorY = -INNER_RADIUS * 0.5;\n return Math.round(((anchorY - vy) / vh) * 1000) / 10;\n }\n\n // ---------------------------------------------------------------------------\n // Render\n // ---------------------------------------------------------------------------\n\n override render() {\n const rotation = -this.heading;\n const viewBox = this._cachedViewBox;\n const rOff = this._radiusOffset;\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 .padding=${PADDING}\n .advices=${this._cachedAdvices}\n .tickmarks=${this._cachedTickmarks}\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=${this._cachedArcFrame}\n .zoomToFitArc=${this.zoomToFitArc}\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 viewBox=\"${viewBox}\" transform=\"rotate(${rotation})\">\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 ${this.hasReadout\n ? html`<div class=\"readout\" style=\"top: ${this._readoutTopPercent}%\">\n <obc-readout\n .variant=${ReadoutVariant.enhanced}\n .direction=${ReadoutDirection.vertical}\n .hasSetpoint=${false}\n .hasAdvice=${false}\n .value=${this.heading}\n .fractionDigits=${0}\n .valuePriority=${this.priorityFor(\n CompassSectorPriorityElement.hdg\n )}\n label=\"HDG\"\n unit=\"DEG\"\n ></obc-readout>\n </div>`\n : nothing}\n </div>\n `;\n }\n\n static override styles = unsafeCSS(componentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-compass-sector': ObcCompassSector;\n }\n}\n"],"names":["CompassSectorPriorityElement"],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ import { css } from "lit";
2
+ const componentStyle = css`
3
+ * {
4
+ -webkit-tap-highlight-color: transparent;
5
+ }
6
+
7
+ :host {
8
+ display: block;
9
+ width: 100%;
10
+ height: 100%;
11
+ }
12
+
13
+ /* Host takes each sector's crop aspect (see \`sectorClips\`): 270/90 square, 180
14
+ wide — so the dial is the same size at any width. */
15
+
16
+ :host([sector="270"]),
17
+ :host([sector="90-left"]),
18
+ :host([sector="90-right"]) {
19
+ height: auto;
20
+ aspect-ratio: 1;
21
+ }
22
+
23
+ /* 448 / 251 = the 180° crop's aspect (448 wide × 448·(1 − 44%) tall); keep in
24
+ sync with \`sectorClips\` bottom=44 in gauge-radial.ts. */
25
+
26
+ :host([sector="180"]) {
27
+ height: auto;
28
+ aspect-ratio: 448 / 251;
29
+ }
30
+
31
+ .gauge-radial-root {
32
+ /* Readout vertical placement per layout (% of the cropped host), tuned to the
33
+ watch geometry — re-check if the ring radii or the 448 box change. Which
34
+ layout uses which is in renderReadouts(). */
35
+ --readout-meta-top: 72%;
36
+ --readout-meta-top-needle: 63%;
37
+ --readout-meta-top-180: 60%;
38
+
39
+ position: relative;
40
+ width: 100%;
41
+ height: 100%;
42
+ }
43
+
44
+ .gauge-radial-root .gauge-readout-value {
45
+ position: absolute;
46
+ left: 50%;
47
+ top: 50%;
48
+ transform: translate(-50%, -50%);
49
+ z-index: 1;
50
+ }
51
+
52
+ .gauge-radial-root .gauge-readout-meta {
53
+ position: absolute;
54
+ left: 50%;
55
+ top: var(--readout-meta-top);
56
+ transform: translateX(-50%);
57
+ z-index: 1;
58
+ }
59
+
60
+ .gauge-radial-root.type-needle .gauge-readout-meta {
61
+ top: var(--readout-meta-top-needle);
62
+ }
63
+
64
+ .gauge-radial-root.sector-180 .gauge-readout-meta {
65
+ top: var(--readout-meta-top-180);
66
+ }
67
+
68
+ /* Nudge the corner readout inward (up + toward center) so it sits inside the
69
+ open quadrant rather than hugging the box corner. Symmetric for both
70
+ orientations — only the horizontal edge differs. */
71
+
72
+ .gauge-radial-root.sector-90-left .gauge-readout-meta,.gauge-radial-root.sector-90-right .gauge-readout-meta {
73
+ --readout-90-inset: 6%;
74
+ bottom: var(--readout-90-inset);
75
+ top: auto;
76
+ transform: none;
77
+ }
78
+
79
+ .gauge-radial-root.sector-90-left .gauge-readout-meta {
80
+ left: auto;
81
+ right: var(--readout-90-inset);
82
+ }
83
+
84
+ .gauge-radial-root.sector-90-right .gauge-readout-meta {
85
+ left: var(--readout-90-inset);
86
+ right: auto;
87
+ }
88
+
89
+ /* 90-right readout sits in the bottom-left corner, so left-align the value
90
+ under the label (enhanced readouts right-align the value by default). */
91
+
92
+ .gauge-radial-root.sector-90-right .gauge-readout-meta::part(value-wrapper) {
93
+ justify-self: start;
94
+ }
95
+ `;
96
+ export {
97
+ componentStyle as default
98
+ };
99
+ //# sourceMappingURL=gauge-radial.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gauge-radial.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,13 +1,20 @@
1
- import { LitElement } from 'lit';
1
+ import { LitElement, TemplateResult } from 'lit';
2
2
  import { AdviceType } from '../watch/advice.js';
3
3
  import { InstrumentState, Priority } from '../types.js';
4
4
  import { TickmarkStyle } from '../watch/tickmark.js';
5
5
  import '../../building-blocks/instrument-radial/instrument-radial.js';
6
+ import '../readout/readout.js';
6
7
  export declare enum ObcGaugeRadialType {
7
8
  filled = "filled",
8
9
  bar = "bar",
9
10
  needle = "needle"
10
11
  }
12
+ export declare enum GaugeRadialSector {
13
+ deg270 = "270",
14
+ deg180 = "180",
15
+ deg90Left = "90-left",
16
+ deg90Right = "90-right"
17
+ }
11
18
  export interface GaugeRadialAdvice {
12
19
  minValue: number;
13
20
  maxValue: number;
@@ -29,14 +36,17 @@ declare const ObcGaugeRadial_base: (new (...args: any[]) => import('../../svghel
29
36
  *
30
37
  * - **Three display types**: `filled` (solid arc), `bar` (thinner arc), and
31
38
  * `needle` (pointer indicator) via the `type` property.
32
- * - **Full-range mapping**: The configured `minValue..maxValue` always spans
33
- * the full 270° sweep. Symmetric ranges still place `0` at 12 o'clock.
39
+ * - **Sector sweep**: `sector` selects the arc span (`270`, `180`, `90-left`, or `90-right`).
40
+ * The configured `minValue..maxValue` always spans the full sector. For the
41
+ * centered sectors (`270`, `180`) a symmetric range places `0` at 12 o'clock;
42
+ * for `90-left`/`90-right` the range midpoint sits at the middle of the quadrant.
34
43
  * - **Setpoint via mixin**: `setpoint`, `newSetpoint`, `touching`,
35
44
  * `autoAtSetpointDeadband`, `setpointOverride`, and all other setpoint
36
45
  * properties are provided by `SetpointMixin` and forwarded to the inner
37
46
  * `<obc-instrument-radial>`.
38
47
  * - **Advice zones**: Pass an array of {@link GaugeRadialAdvice} objects to
39
- * render caution/alert arcs on the gauge.
48
+ * render caution/alert arcs on the gauge. Not shown on the `90-left` /
49
+ * `90-right` sectors.
40
50
  *
41
51
  * ## Usage Guidelines
42
52
  *
@@ -45,6 +55,12 @@ declare const ObcGaugeRadial_base: (new (...args: any[]) => import('../../svghel
45
55
  * - Provide `primaryTickmarkInterval` and `secondaryTickmarkInterval` to
46
56
  * control tickmark density.
47
57
  * - Enable `showLabels` to show numeric labels at primary tickmarks.
58
+ * - Enable `showReadout` with optional `label` and `unit`. Layout depends on `sector`
59
+ * and `type`: **270** filled/bar — value at center + a label-only `meta` row in
60
+ * the bottom gap (two separate readouts); **180** filled/bar — a single centered
61
+ * stack (value + label/unit) at the bottom; **270** needle — bottom stack;
62
+ * **180** needle — no readout; **90-left** / **90-right** filled/bar — corner
63
+ * readout in a square host; **90** needle — no readout.
48
64
  *
49
65
  * ## Best Practices
50
66
  *
@@ -61,7 +77,7 @@ declare const ObcGaugeRadial_base: (new (...args: any[]) => import('../../svghel
61
77
  * minValue="0"
62
78
  * maxValue="100"
63
79
  * type="filled"
64
- * enhanced
80
+ * priority="enhanced"
65
81
  * showLabels
66
82
  * primaryTickmarkInterval="25"
67
83
  * secondaryTickmarkInterval="5"
@@ -89,9 +105,28 @@ export declare class ObcGaugeRadial extends ObcGaugeRadial_base {
89
105
  type: ObcGaugeRadialType;
90
106
  tickmarksInside: boolean;
91
107
  tickmarkStyle: TickmarkStyle;
108
+ /** Caution/alert arcs. Ignored on `sector: 90-left` / `90-right`. */
92
109
  advices: GaugeRadialAdvice[];
93
- getAngle(v: number): number;
94
- render(): import('lit-html').TemplateResult<1>;
110
+ sector: GaugeRadialSector;
111
+ showReadout: boolean;
112
+ label: string;
113
+ unit: string;
114
+ fractionDigits: number;
115
+ private get sectorAngles();
116
+ /**
117
+ * Per-edge crop (%) of the shared, origin-centered 448 SVG box each sector
118
+ * shows. All sectors render at the same natural scale, so the dial stays the
119
+ * same size; the sector only windows a different part (270 whole, 180 wide,
120
+ * 90 a quadrant).
121
+ */
122
+ private get sectorClips();
123
+ private get isSector90();
124
+ getAngle: (v: number) => number;
125
+ /** Renders one gauge readout; `withMeta`/`labelOnly` pick parts. */
126
+ private renderReadout;
127
+ private renderReadouts;
128
+ render(): TemplateResult<1>;
129
+ static styles: import('lit').CSSResult;
95
130
  }
96
131
  declare global {
97
132
  interface HTMLElementTagNameMap {
@@ -1 +1 @@
1
- {"version":3,"file":"gauge-radial.d.ts","sourceRoot":"","sources":["../../../src/navigation-instruments/gauge-radial/gauge-radial.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAO,MAAM,KAAK,CAAC;AAGrC,OAAO,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAC,eAAe,EAAE,QAAQ,EAAC,MAAM,aAAa,CAAC;AAEtD,OAAO,8DAA8D,CAAC;AACtE,OAAO,EAAC,aAAa,EAAC,MAAM,sBAAsB,CAAC;AAEnD,oBAAY,kBAAkB;IAC5B,MAAM,WAAW;IACjB,GAAG,QAAQ;IACX,MAAM,WAAW;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACjB;;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,qBACa,cAAe,SAAQ,mBAAyB;IACjC,KAAK,SAAK;IACV,QAAQ,SAAO;IACf,QAAQ,SAAK;IACZ,UAAU,EAAE,OAAO,CAAS;IAC7B,uBAAuB,SAAM;IAC7B,yBAAyB,SAAM;IACzD;;;OAGG;IACuB,wBAAwB,EAAE,MAAM,GAAG,SAAS,CAC1D;IACc,KAAK,EAAE,eAAe,CAA0B;IAChD,QAAQ,EAAE,QAAQ,CAAoB;IACtC,IAAI,EAAE,kBAAkB,CACtB;IACD,eAAe,EAAE,OAAO,CAAS;IAClC,aAAa,EAAE,aAAa,CAC9B;IACmB,OAAO,EAAE,iBAAiB,EAAE,CAAM;IAE7E,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM;IASlB,MAAM;CA8BhB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,cAAc,CAAC;KACpC;CACF"}
1
+ {"version":3,"file":"gauge-radial.d.ts","sourceRoot":"","sources":["../../../src/navigation-instruments/gauge-radial/gauge-radial.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAA4B,KAAK,cAAc,EAAC,MAAM,KAAK,CAAC;AAK9E,OAAO,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAC,eAAe,EAAE,QAAQ,EAAC,MAAM,aAAa,CAAC;AAEtD,OAAO,8DAA8D,CAAC;AACtE,OAAO,EAAC,aAAa,EAAC,MAAM,sBAAsB,CAAC;AACnD,OAAO,uBAAuB,CAAC;AAM/B,oBAAY,kBAAkB;IAC5B,MAAM,WAAW;IACjB,GAAG,QAAQ;IACX,MAAM,WAAW;CAClB;AAED,oBAAY,iBAAiB;IAC3B,MAAM,QAAQ;IACd,MAAM,QAAQ;IACd,SAAS,YAAY;IACrB,UAAU,aAAa;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACjB;;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,qBACa,cAAe,SAAQ,mBAAyB;IACjC,KAAK,SAAK;IACV,QAAQ,SAAO;IACf,QAAQ,SAAK;IACZ,UAAU,EAAE,OAAO,CAAS;IAC7B,uBAAuB,SAAM;IAC7B,yBAAyB,SAAM;IACzD;;;OAGG;IACuB,wBAAwB,EAAE,MAAM,GAAG,SAAS,CAC1D;IACc,KAAK,EAAE,eAAe,CAA0B;IAChD,QAAQ,EAAE,QAAQ,CAAoB;IACtC,IAAI,EAAE,kBAAkB,CACtB;IACD,eAAe,EAAE,OAAO,CAAS;IAClC,aAAa,EAAE,aAAa,CAC9B;IACxB,qEAAqE;IAC1B,OAAO,EAAE,iBAAiB,EAAE,CAAM;IACpC,MAAM,EAAE,iBAAiB,CACvC;IACA,WAAW,UAAS;IACrB,KAAK,SAAM;IACX,IAAI,SAAM;IACV,cAAc,SAAK;IAE7C,OAAO,KAAK,YAAY,GAYvB;IAED;;;;;OAKG;IACH,OAAO,KAAK,WAAW,GAiBtB;IAED,OAAO,KAAK,UAAU,GAKrB;IAID,QAAQ,GAAI,GAAG,MAAM,KAAG,MAAM,CAQ5B;IAEF,oEAAoE;IACpE,OAAO,CAAC,aAAa;IA+BrB,OAAO,CAAC,cAAc;IAiDb,MAAM;IAgDf,OAAgB,MAAM,0BAA6B;CACpD;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,cAAc,CAAC;KACpC;CACF"}
@@ -1,10 +1,13 @@
1
- import { LitElement, html } from "lit";
1
+ import { LitElement, html, nothing, unsafeCSS } from "lit";
2
+ import { classMap } from "lit/directives/class-map.js";
3
+ import componentStyle from "./gauge-radial.css.js";
2
4
  import { customElement } from "../../decorator.js";
3
5
  import { property } from "lit/decorators.js";
4
6
  import { InstrumentState, Priority } from "../types.js";
5
7
  import { SetpointMixin } from "../../svghelpers/setpoint-mixin.js";
6
8
  import "../../building-blocks/instrument-radial/instrument-radial.js";
7
9
  import { TickmarkStyle } from "../watch/tickmark.js";
10
+ import { ReadoutStackVerticalAlignment, ReadoutVariant } from "../readout/readout.js";
8
11
  var __defProp = Object.defineProperty;
9
12
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
13
  var __decorateClass = (decorators, target, key, kind) => {
@@ -21,6 +24,13 @@ var ObcGaugeRadialType = /* @__PURE__ */ ((ObcGaugeRadialType2) => {
21
24
  ObcGaugeRadialType2["needle"] = "needle";
22
25
  return ObcGaugeRadialType2;
23
26
  })(ObcGaugeRadialType || {});
27
+ var GaugeRadialSector = /* @__PURE__ */ ((GaugeRadialSector2) => {
28
+ GaugeRadialSector2["deg270"] = "270";
29
+ GaugeRadialSector2["deg180"] = "180";
30
+ GaugeRadialSector2["deg90Left"] = "90-left";
31
+ GaugeRadialSector2["deg90Right"] = "90-right";
32
+ return GaugeRadialSector2;
33
+ })(GaugeRadialSector || {});
24
34
  let ObcGaugeRadial = class extends SetpointMixin(LitElement) {
25
35
  constructor() {
26
36
  super(...arguments);
@@ -37,45 +47,166 @@ let ObcGaugeRadial = class extends SetpointMixin(LitElement) {
37
47
  this.tickmarksInside = false;
38
48
  this.tickmarkStyle = TickmarkStyle.regular;
39
49
  this.advices = [];
50
+ this.sector = "270";
51
+ this.showReadout = false;
52
+ this.label = "";
53
+ this.unit = "";
54
+ this.fractionDigits = 0;
55
+ this.getAngle = (v) => {
56
+ const { sweep, start } = this.sectorAngles;
57
+ const span = this.maxValue - this.minValue;
58
+ if (!Number.isFinite(span) || span <= 0) {
59
+ return start;
60
+ }
61
+ return (v - this.minValue) / span * sweep + start;
62
+ };
40
63
  }
41
- getAngle(v) {
42
- const span = this.maxValue - this.minValue;
43
- if (!Number.isFinite(span) || span <= 0) {
44
- return -135;
64
+ get sectorAngles() {
65
+ switch (this.sector) {
66
+ case "180":
67
+ return { sweep: 180, start: -90 };
68
+ case "90-left":
69
+ return { sweep: 90, start: -90 };
70
+ case "90-right":
71
+ return { sweep: 90, start: 0 };
72
+ case "270":
73
+ default:
74
+ return { sweep: 270, start: -135 };
45
75
  }
46
- return (v - this.minValue) / span * 270 - 135;
76
+ }
77
+ /**
78
+ * Per-edge crop (%) of the shared, origin-centered 448 SVG box each sector
79
+ * shows. All sectors render at the same natural scale, so the dial stays the
80
+ * same size; the sector only windows a different part (270 whole, 180 wide,
81
+ * 90 a quadrant).
82
+ */
83
+ get sectorClips() {
84
+ switch (this.sector) {
85
+ case "180":
86
+ return { top: 0, bottom: 44, left: 0, right: 0 };
87
+ case "90-left":
88
+ return { top: 0, bottom: 45, left: 0, right: 45 };
89
+ case "90-right":
90
+ return { top: 0, bottom: 45, left: 45, right: 0 };
91
+ case "270":
92
+ default:
93
+ return { top: 0, bottom: 0, left: 0, right: 0 };
94
+ }
95
+ }
96
+ get isSector90() {
97
+ return this.sector === "90-left" || this.sector === "90-right";
98
+ }
99
+ /** Renders one gauge readout; `withMeta`/`labelOnly` pick parts. */
100
+ renderReadout({
101
+ className,
102
+ variant,
103
+ alignment = ReadoutStackVerticalAlignment.vertical,
104
+ withMeta = true,
105
+ labelOnly = false
106
+ }) {
107
+ const withValue = !labelOnly;
108
+ return html`
109
+ <obc-readout
110
+ class=${className}
111
+ direction="vertical"
112
+ ?labelOnly=${labelOnly}
113
+ .variant=${variant}
114
+ .alignment=${alignment}
115
+ .valuePriority=${withValue ? this.priority : void 0}
116
+ .value=${withValue ? this.value : void 0}
117
+ .fractionDigits=${this.fractionDigits}
118
+ .label=${withMeta ? this.label : ""}
119
+ .unit=${withMeta ? this.unit : ""}
120
+ ></obc-readout>
121
+ `;
122
+ }
123
+ renderReadouts() {
124
+ if (!this.showReadout) {
125
+ return nothing;
126
+ }
127
+ const isNeedle = this.type === "needle";
128
+ const is90 = this.isSector90;
129
+ const is180 = this.sector === "180";
130
+ if (isNeedle && (is180 || is90)) {
131
+ return nothing;
132
+ }
133
+ if (is90) {
134
+ return this.renderReadout({
135
+ className: "gauge-readout-meta",
136
+ variant: ReadoutVariant.enhanced
137
+ });
138
+ }
139
+ if (isNeedle || is180) {
140
+ return this.renderReadout({
141
+ className: "gauge-readout-meta",
142
+ variant: ReadoutVariant.stack,
143
+ alignment: ReadoutStackVerticalAlignment.center
144
+ });
145
+ }
146
+ return html`
147
+ ${this.renderReadout({
148
+ className: "gauge-readout-value",
149
+ variant: ReadoutVariant.enhanced,
150
+ withMeta: false
151
+ })}
152
+ ${this.label || this.unit ? this.renderReadout({
153
+ className: "gauge-readout-meta",
154
+ variant: ReadoutVariant.stack,
155
+ alignment: ReadoutStackVerticalAlignment.center,
156
+ labelOnly: true
157
+ }) : nothing}
158
+ `;
47
159
  }
48
160
  render() {
161
+ const clips = this.sectorClips;
49
162
  return html`
50
- <obc-instrument-radial
51
- .value=${this.value}
52
- .state=${this.state}
53
- .priority=${this.priority}
54
- .setpoint=${this.setpoint}
55
- .newSetpoint=${this.newSetpoint}
56
- .setpointAtZeroDeadband=${this.setpointAtZeroDeadband}
57
- .setpointOverride=${this.setpointOverride}
58
- .touching=${this.touching}
59
- .autoAtSetpoint=${this.autoAtSetpoint}
60
- .autoAtSetpointDeadband=${this.autoAtSetpointDeadband}
61
- .animateSetpoint=${this.animateSetpoint}
62
- .maxValue=${this.maxValue}
63
- .minValue=${this.minValue}
64
- .getAngle=${this.getAngle}
65
- .showLabels=${this.showLabels}
66
- .primaryTickmarkInterval=${this.primaryTickmarkInterval}
67
- .secondaryTickmarkInterval=${this.secondaryTickmarkInterval}
68
- .tertiaryTickmarkInterval=${this.tertiaryTickmarkInterval}
69
- .type=${this.type}
70
- .needleType=${this.type}
71
- .tickmarksInside=${this.tickmarksInside}
72
- .tickmarkStyle=${this.tickmarkStyle}
73
- .advices=${this.advices}
163
+ <div
164
+ class=${classMap({
165
+ "gauge-radial-root": true,
166
+ "type-needle": this.type === "needle",
167
+ "sector-180": this.sector === "180",
168
+ "sector-90-left": this.sector === "90-left",
169
+ "sector-90-right": this.sector === "90-right"
170
+ /* deg90Right */
171
+ })}
74
172
  >
75
- </obc-instrument-radial>
173
+ <obc-instrument-radial
174
+ .value=${this.value}
175
+ .state=${this.state}
176
+ .priority=${this.priority}
177
+ .setpoint=${this.setpoint}
178
+ .newSetpoint=${this.newSetpoint}
179
+ .setpointAtZeroDeadband=${this.setpointAtZeroDeadband}
180
+ .setpointOverride=${this.setpointOverride}
181
+ .touching=${this.touching}
182
+ .autoAtSetpoint=${this.autoAtSetpoint}
183
+ .autoAtSetpointDeadband=${this.autoAtSetpointDeadband}
184
+ .animateSetpoint=${this.animateSetpoint}
185
+ .maxValue=${this.maxValue}
186
+ .minValue=${this.minValue}
187
+ .getAngle=${this.getAngle}
188
+ .showLabels=${this.showLabels}
189
+ .primaryTickmarkInterval=${this.primaryTickmarkInterval}
190
+ .secondaryTickmarkInterval=${this.secondaryTickmarkInterval}
191
+ .tertiaryTickmarkInterval=${this.tertiaryTickmarkInterval}
192
+ .type=${this.type}
193
+ .needleType=${this.type}
194
+ .tickmarksInside=${this.tickmarksInside}
195
+ .tickmarkStyle=${this.tickmarkStyle}
196
+ .advices=${this.isSector90 ? [] : this.advices}
197
+ .clipTop=${clips.top}
198
+ .clipBottom=${clips.bottom}
199
+ .clipLeft=${clips.left}
200
+ .clipRight=${clips.right}
201
+ .endLabelsMaxMin=${this.sector === "180"}
202
+ >
203
+ </obc-instrument-radial>
204
+ ${this.renderReadouts()}
205
+ </div>
76
206
  `;
77
207
  }
78
208
  };
209
+ ObcGaugeRadial.styles = unsafeCSS(componentStyle);
79
210
  __decorateClass([
80
211
  property({ type: Number })
81
212
  ], ObcGaugeRadial.prototype, "value", 2);
@@ -115,10 +246,26 @@ __decorateClass([
115
246
  __decorateClass([
116
247
  property({ type: Array, attribute: false })
117
248
  ], ObcGaugeRadial.prototype, "advices", 2);
249
+ __decorateClass([
250
+ property({ type: String, reflect: true })
251
+ ], ObcGaugeRadial.prototype, "sector", 2);
252
+ __decorateClass([
253
+ property({ type: Boolean })
254
+ ], ObcGaugeRadial.prototype, "showReadout", 2);
255
+ __decorateClass([
256
+ property({ type: String })
257
+ ], ObcGaugeRadial.prototype, "label", 2);
258
+ __decorateClass([
259
+ property({ type: String })
260
+ ], ObcGaugeRadial.prototype, "unit", 2);
261
+ __decorateClass([
262
+ property({ type: Number })
263
+ ], ObcGaugeRadial.prototype, "fractionDigits", 2);
118
264
  ObcGaugeRadial = __decorateClass([
119
265
  customElement("obc-gauge-radial")
120
266
  ], ObcGaugeRadial);
121
267
  export {
268
+ GaugeRadialSector,
122
269
  ObcGaugeRadial,
123
270
  ObcGaugeRadialType
124
271
  };