@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.
- package/bundle/openbridge-webcomponents.bundle.js +987 -362
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +576 -10
- package/dist/building-blocks/instrument-radial/instrument-radial.d.ts +10 -0
- package/dist/building-blocks/instrument-radial/instrument-radial.d.ts.map +1 -1
- package/dist/building-blocks/instrument-radial/instrument-radial.js +86 -21
- package/dist/building-blocks/instrument-radial/instrument-radial.js.map +1 -1
- package/dist/navigation-instruments/compass-sector/compass-sector.css.js +12 -0
- package/dist/navigation-instruments/compass-sector/compass-sector.css.js.map +1 -1
- package/dist/navigation-instruments/compass-sector/compass-sector.d.ts +23 -0
- package/dist/navigation-instruments/compass-sector/compass-sector.d.ts.map +1 -1
- package/dist/navigation-instruments/compass-sector/compass-sector.js +47 -0
- package/dist/navigation-instruments/compass-sector/compass-sector.js.map +1 -1
- package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js +99 -0
- package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js.map +1 -0
- package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts +42 -7
- package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts.map +1 -1
- package/dist/navigation-instruments/gauge-radial/gauge-radial.js +178 -31
- package/dist/navigation-instruments/gauge-radial/gauge-radial.js.map +1 -1
- package/dist/navigation-instruments/pitch/pitch.d.ts +37 -0
- package/dist/navigation-instruments/pitch/pitch.d.ts.map +1 -1
- package/dist/navigation-instruments/pitch/pitch.js +130 -62
- package/dist/navigation-instruments/pitch/pitch.js.map +1 -1
- package/dist/navigation-instruments/pitch-roll/pitch-roll.d.ts +7 -0
- package/dist/navigation-instruments/pitch-roll/pitch-roll.d.ts.map +1 -1
- package/dist/navigation-instruments/pitch-roll/pitch-roll.js +58 -2
- package/dist/navigation-instruments/pitch-roll/pitch-roll.js.map +1 -1
- package/dist/navigation-instruments/readout/readout.css.js +4 -0
- package/dist/navigation-instruments/readout/readout.css.js.map +1 -1
- package/dist/navigation-instruments/roll/roll.d.ts +37 -0
- package/dist/navigation-instruments/roll/roll.d.ts.map +1 -1
- package/dist/navigation-instruments/roll/roll.js +119 -63
- package/dist/navigation-instruments/roll/roll.js.map +1 -1
- package/dist/navigation-instruments/rot-sector/rot-sector.d.ts +15 -0
- package/dist/navigation-instruments/rot-sector/rot-sector.d.ts.map +1 -1
- package/dist/navigation-instruments/rot-sector/rot-sector.js +53 -1
- package/dist/navigation-instruments/rot-sector/rot-sector.js.map +1 -1
- package/dist/navigation-instruments/watch/tickmark.d.ts +2 -1
- package/dist/navigation-instruments/watch/tickmark.d.ts.map +1 -1
- package/dist/navigation-instruments/watch/tickmark.js +24 -4
- package/dist/navigation-instruments/watch/tickmark.js.map +1 -1
- package/dist/navigation-instruments/watch/watch.d.ts +23 -1
- package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
- package/dist/navigation-instruments/watch/watch.js +48 -20
- package/dist/navigation-instruments/watch/watch.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pitch-roll.js","sources":["../../../src/navigation-instruments/pitch-roll/pitch-roll.ts"],"sourcesContent":["import {LitElement, css, html, nothing, svg} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport '../watch/watch.js';\nimport {\n VesselImage,\n VesselImageSize,\n WatchCircleType,\n type WatchArea,\n OUTER_RING_RADIUS,\n innerRingRadiusFor,\n vesselImages,\n} from '../watch/watch.js';\nimport {TickmarkType} from '../watch/tickmark.js';\nimport {AdviceState, AdviceType, AngleAdviceRaw} from '../watch/advice.js';\nimport {customElement} from '../../decorator.js';\nimport {Priority} from '../types.js';\nimport {\n computeZoomToFitArcFrame,\n normalizeArcAngle,\n shiftArcFrameToOuterEdge,\n} from '../../svghelpers/arc-frame.js';\n\nexport enum PitchRollPriorityElement {\n pitch = 'pitch',\n roll = 'roll',\n}\n\n/** Half-side of the centre overlay viewBox in SVG units. */\nconst CENTRE_HALF = 200;\n\n/**\n * Minimum diagonal Euclidean distance (in central-layer / display\n * pixels on the default 400 px container) required between adjacent\n * zoomed arc bands' nearest corners (inner-to-inner and outer-to-outer\n * checked, the smaller binds). When two adjacent requested arcs would\n * sit closer than this — or actually overlap — both axes' visible arcs\n * are shortened (ratio-preserving so `aP : aR = pitchReq : rollReq`)\n * just enough to reach this clearance. The frame itself stays\n * unchanged so band thickness, position and zoom level keep matching\n * the standalone `obc-pitch` / `obc-roll` instruments.\n */\nconst CORNER_GAP_PX = 32;\n\n/** Numerical safety floor when an axis arc has to collapse for clearance. */\nconst MIN_ARC_HALF_DEG = 2;\n\n@customElement('obc-pitch-roll')\nexport class ObcPitchRoll extends LitElement {\n @property({type: Number}) pitch = 0;\n @property({type: Number}) roll = 0;\n @property({type: Number}) minAvgPitch = 0;\n @property({type: Number}) maxAvgPitch = 0;\n @property({type: Number}) minAvgRoll = 0;\n @property({type: Number}) maxAvgRoll = 0;\n @property({type: String}) vesselImageFore: VesselImage = VesselImage.psvFore;\n @property({type: String}) vesselImageSide: VesselImage = VesselImage.psvSide;\n @property({type: Number}) scaleForeImage = 1;\n @property({type: Number}) maxPitchAdvice: number | undefined = undefined;\n @property({type: Number}) maxRollAdvice: number | undefined = undefined;\n @property({type: Boolean}) triggerPitchAdvice = false;\n @property({type: Boolean}) triggerRollAdvice = false;\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: Array, attribute: false})\n priorityElements: PitchRollPriorityElement[] = [\n PitchRollPriorityElement.pitch,\n PitchRollPriorityElement.roll,\n ];\n @property({type: Boolean}) zoomToFitArc: boolean = false;\n /**\n * Half-extent of each of the four watch arcs in degrees, measured from the\n * arc's natural center (0°/90°/180°/270°). Each arc spans\n * `center ± arcAngle`. Default `30` reproduces the historical 60°-wide\n * arcs; smaller values produce narrower arcs that, combined with\n * `zoomToFitArc`, reveal more detail in the relevant motion range.\n */\n @property({type: Number}) arcAngle: number = 30;\n /**\n * Optional per-axis override for the pitch arcs (top + bottom). Falls\n * back to {@link arcAngle} when undefined. Useful for rectangular layouts\n * where pitch and roll need different angular extents.\n */\n @property({type: Number}) pitchArcAngle?: number;\n /**\n * Optional per-axis override for the roll arcs (left + right). Falls\n * back to {@link arcAngle} when undefined.\n */\n @property({type: Number}) rollArcAngle?: number;\n\n private priorityFor(element: PitchRollPriorityElement): 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 needleColor(element: PitchRollPriorityElement): string {\n return this.priorityFor(element) === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n }\n\n private barColor(element: PitchRollPriorityElement): string {\n return this.priorityFor(element) === Priority.enhanced\n ? 'var(--instrument-enhanced-tertiary-color)'\n : 'var(--instrument-regular-tertiary-color)';\n }\n\n private get normalizedScaleForeImage(): number {\n if (!Number.isFinite(this.scaleForeImage)) {\n return 1;\n }\n return Math.max(0, Math.min(2, this.scaleForeImage));\n }\n\n /** Requested (clamped to a minimum) half-extent for each axis. */\n private get requestedPitchArcAngle(): number {\n return normalizeArcAngle(this.pitchArcAngle ?? this.arcAngle, 30);\n }\n private get requestedRollArcAngle(): number {\n return normalizeArcAngle(this.rollArcAngle ?? this.arcAngle, 30);\n }\n\n override render() {\n const pitchReq = this.requestedPitchArcAngle;\n const rollReq = this.requestedRollArcAngle;\n const areas = [\n {\n startAngle: 90 - pitchReq,\n endAngle: 90 + pitchReq,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n {\n startAngle: 270 - pitchReq,\n endAngle: 270 + pitchReq,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n {\n startAngle: 360 - rollReq,\n endAngle: rollReq,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n {\n startAngle: 180 - rollReq,\n endAngle: 180 + rollReq,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n ];\n\n const overlayViewBox = `-${CENTRE_HALF} -${CENTRE_HALF} ${CENTRE_HALF * 2} ${CENTRE_HALF * 2}`;\n const vesselScale = 224 / 160;\n\n return html`\n <div class=\"container\">\n <svg viewBox=\"${overlayViewBox}\">\n ${svg`\n <line\n x1=\"-150\"\n y1=\"0\"\n x2=\"150\"\n y2=\"0\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n />\n <g\n style=\"transform: rotate(${this.pitch}deg) scale(${vesselScale}) translate(-80px, -80px);\"\n >\n ${this.zoomToFitArc ? vesselImages[this.vesselImageSide] : nothing}\n </g>\n <g\n style=\"transform: rotate(${this.roll}deg) scale(${vesselScale * this.normalizedScaleForeImage}) translate(-80px, -80px);\"\n >\n ${this.zoomToFitArc ? vesselImages[this.vesselImageFore] : nothing}\n </g>\n `}\n </svg>\n ${this.zoomToFitArc\n ? this.renderZoomedArcs(pitchReq, rollReq)\n : this.renderFullWatch(areas)}\n </div>\n `;\n }\n\n /**\n * Zoomed-arc layer: four CSS-rotated `<obc-watch>` instances, each\n * containing a single arc rendered at the watch's natural top\n * (`0° ± arcAngle`). Each watch handles its own `zoomToFitArc` framing\n * so each visible arc spans almost the full container — exactly like the\n * pitch and roll narrow stories. The four are rotated 0 / 90 / 180 / 270\n * to land at top / right / bottom / left. Top + bottom carry pitch data,\n * left + right carry roll data.\n *\n * Each axis can request its own half-extent via `pitchArcAngle` /\n * `rollArcAngle`. The zoom-fit frame for each axis is computed with the\n * EXACT same math as `obc-pitch` / `obc-roll` at the requested\n * half-extent (same band thickness, same zoom level, same position) so\n * each cardinal sub-watch matches its standalone equivalent. With the\n * frames left untouched, two requested arcs may overlap at the\n * diagonals; to avoid that, both arcs are shortened (ratio-preserving:\n * `aP : aR = pitchReq : rollReq`) just enough that the diagonal\n * Euclidean distance between adjacent inner BBOX corners equals\n * {@link CORNER_GAP_PX}. The shortened half-extent is passed only to\n * the band's `areas` (and clamped advices) so the sub-watch renders a\n * shorter band with its native rounded end-caps; the frame itself is\n * unchanged.\n */\n private renderZoomedArcs(pitchReq: number, rollReq: number) {\n const tickmarks = [{angle: 0, type: TickmarkType.main}];\n\n // ---- Per-axis zoom-fit frames (requested half-extents) -------------\n const ext = 48;\n const targetSize = (176 + ext) * 2;\n const innerNat = innerRingRadiusFor(WatchCircleType.double);\n const buildFrame = (halfDeg: number) => {\n const areas: WatchArea[] = [\n {\n startAngle: -halfDeg,\n endAngle: halfDeg,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n ];\n const baseFrame = computeZoomToFitArcFrame({\n areas,\n outerRadius: OUTER_RING_RADIUS,\n innerRadius: innerNat,\n extension: ext,\n targetSize,\n });\n const subArcFrame = shiftArcFrameToOuterEdge(\n baseFrame,\n OUTER_RING_RADIUS + baseFrame.radiusOffset,\n OUTER_RING_RADIUS,\n CENTRE_HALF\n );\n // Display scale: how many container px per obc-watch SVG unit.\n const scale = (CENTRE_HALF * 2) / subArcFrame.width;\n // Outer- and inner-arc circle radii of the band, in central\n // (container) px. The watch renders the area between\n // (OUTER_RING_RADIUS + radiusOffset) and (innerRingRadius +\n // radiusOffset), so radiusOffset shifts BOTH radii (band\n // thickness stays constant). Both arcs are centred at the watch's\n // SVG origin; that origin sits at central-coords (0, outerR-OR)\n // along the band's cardinal direction (= outerR-OR below centre\n // for the top band).\n const outerR = (OUTER_RING_RADIUS + baseFrame.radiusOffset) * scale;\n const innerR = (innerNat + baseFrame.radiusOffset) * scale;\n return {subArcFrame, outerR, innerR};\n };\n // Frames are built at the REQUESTED half-extents — identical to\n // what `obc-pitch` / `obc-roll` produce when `zoomToFitArc=true`.\n // Each cardinal sub-watch therefore matches its standalone\n // equivalent (band thickness, length, position, zoom level).\n const pitchFrame = buildFrame(pitchReq);\n const rollFrame = buildFrame(rollReq);\n\n // ---- Method A: shorten visible arc to clear adjacent corners --------\n // The frames stay UNCHANGED; only the half-extent passed to the\n // band's `areas` shrinks. obc-watch then renders a shorter arc with\n // its native rounded end-caps — same band thickness, same position,\n // same zoom level.\n //\n // Geometry of one band (top, cardinal = -y in container coords):\n // outer-edge endpoint at angle θ from cardinal\n // P_out(θ) = (outerR·sin θ, (outerR − OR) − outerR·cos θ)\n // inner-edge endpoint at angle θ\n // P_in(θ) = (innerR·sin θ, (outerR − OR) − innerR·cos θ)\n // The right band is the same template rotated 90° CW\n // (CSS rotate(90deg)): (x, y) → (−y, x).\n //\n // For pitch–roll layout the binding constraint is the diagonal\n // gap between the top band's right corners and the right band's\n // top corners (and analogously for the other three diagonals). Two\n // pairs need to clear each other:\n // (a) inner-corner vs inner-corner (P1 = P_in_top(+aP),\n // Q1 = rotate(P_in_right(−aR)))\n // (b) outer-corner vs outer-corner (P2 = P_out_top(+aP),\n // Q2 = rotate(P_out_right(−aR)))\n // Cap-line endpoints lie on the same arcs so any other point on\n // the cap is at least as far away.\n //\n // Define a SIGNED diagonal gap: positive when the two corners are\n // diagonally clear (both Δx > 0 AND Δy > 0), negative when either\n // projection has crossed (i.e. the bands overlap). Its magnitude\n // is the Euclidean distance between the corner pair. We bisect on\n // a scalar s ∈ [0, 1] (aP = pitchReq·s, aR = rollReq·s) so the\n // requested pitch:roll RATIO is preserved, until the MIN of the\n // inner and outer signed gaps equals CORNER_GAP_PX.\n const OR = OUTER_RING_RADIUS;\n const aPreqRad = (pitchReq * Math.PI) / 180;\n const aRreqRad = (rollReq * Math.PI) / 180;\n const signedDist = (\n px: number,\n py: number,\n qx: number,\n qy: number\n ): number => {\n const dx = qx - px;\n const dy = qy - py;\n const mag = Math.hypot(dx, dy);\n return dx > 0 && dy > 0 ? mag : -mag;\n };\n const cornerGaps = (\n apRad: number,\n arRad: number\n ): {inner: number; outer: number} => {\n const cosP = Math.cos(apRad);\n const sinP = Math.sin(apRad);\n const cosR = Math.cos(arRad);\n const sinR = Math.sin(arRad);\n // Top band's right corners (container coords).\n const p1x = pitchFrame.innerR * sinP;\n const p1y = pitchFrame.outerR - OR - pitchFrame.innerR * cosP;\n const p2x = pitchFrame.outerR * sinP;\n const p2y = pitchFrame.outerR - OR - pitchFrame.outerR * cosP;\n // Right band's top corners = top template's left corners rotated\n // 90° CW: (x, y) → (−y, x).\n // Top template's −aR inner corner: (−innerR·sin aR,\n // (outerR−OR) − innerR·cos aR) → rotated:\n // (OR − outerR + innerR·cos aR, −innerR·sin aR)\n const q1x = OR - rollFrame.outerR + rollFrame.innerR * cosR;\n const q1y = -rollFrame.innerR * sinR;\n const q2x = OR - rollFrame.outerR + rollFrame.outerR * cosR;\n const q2y = -rollFrame.outerR * sinR;\n return {\n inner: signedDist(p1x, p1y, q1x, q1y),\n outer: signedDist(p2x, p2y, q2x, q2y),\n };\n };\n const minGap = (apRad: number, arRad: number): number => {\n const g = cornerGaps(apRad, arRad);\n return Math.min(g.inner, g.outer);\n };\n let aP = aPreqRad;\n let aR = aRreqRad;\n if (minGap(aPreqRad, aRreqRad) < CORNER_GAP_PX) {\n let lo = 0;\n let hi = 1;\n for (let i = 0; i < 40; i++) {\n const mid = (lo + hi) / 2;\n if (minGap(aPreqRad * mid, aRreqRad * mid) >= CORNER_GAP_PX) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n aP = aPreqRad * lo;\n aR = aRreqRad * lo;\n }\n const pitchClampedDeg = Math.max(MIN_ARC_HALF_DEG, (aP * 180) / Math.PI);\n const rollClampedDeg = Math.max(MIN_ARC_HALF_DEG, (aR * 180) / Math.PI);\n\n const subAreas = (halfDeg: number): WatchArea[] => [\n {\n startAngle: -halfDeg,\n endAngle: halfDeg,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n ];\n const pitchAreas = subAreas(pitchClampedDeg);\n const rollAreas = subAreas(rollClampedDeg);\n\n const pitchAdvices = this.subAdvices('pitch', pitchClampedDeg);\n const rollAdvices = this.subAdvices('roll', rollClampedDeg);\n\n // Clip each sub-watch to the angular sector actually covered by the\n // (possibly shortened) arc so the indicator pill and bar end-of-range\n // limit lines cannot leak past the visible band when the value falls\n // outside the clamped range. Advices are clamped to the band extent\n // in `subAdvices` so they fit naturally and are not affected by this\n // clip. The clip is a triangle in the element's CSS box, with one\n // vertex at the watch origin (SVG 0,0 mapped to CSS px) and two\n // vertices at the intersection of the sector edges with the top edge\n // of the box. Applied in unrotated local coords; CSS rotation then\n // carries it to the correct cardinal side.\n const sectorClip = (\n halfDeg: number,\n frame: typeof rollFrame.subArcFrame\n ): string => {\n // Watch origin (SVG 0,0) in CSS percentages of the element box.\n // Element fills the 100% × 100% .container; obc-watch fills it with\n // viewBox = frame.{x,y,width,height}, so SVG (0,0) sits at:\n // (-frame.x / frame.width, -frame.y / frame.height)\n const oxPct = (-frame.x / frame.width) * 100;\n const oyPct = (-frame.y / frame.height) * 100;\n // Sector half-angle, expressed as the horizontal offset (in pct)\n // a ray reaches when traveling from the origin up to the top edge.\n const dxPct = oyPct * Math.tan((halfDeg * Math.PI) / 180);\n // Clamp to box bounds so half-angles ≥ 45° still produce a polygon\n // that reaches the corners instead of going off-canvas.\n const lx = Math.max(0, oxPct - dxPct);\n const rx = Math.min(100, oxPct + dxPct);\n return `polygon(${oxPct}% ${oyPct}%, ${lx}% 0%, ${rx}% 0%)`;\n };\n const pitchClip = sectorClip(pitchClampedDeg, pitchFrame.subArcFrame);\n const rollClip = sectorClip(rollClampedDeg, rollFrame.subArcFrame);\n\n const rollNeedles = [\n {\n angle: this.roll,\n fillColor: this.needleColor(PitchRollPriorityElement.roll),\n strokeColor: 'var(--border-silhouette-color)',\n },\n ];\n const pitchNeedles = [\n {\n angle: this.pitch,\n fillColor: this.needleColor(PitchRollPriorityElement.pitch),\n strokeColor: 'var(--border-silhouette-color)',\n },\n ];\n const rollBars = [\n {\n startAngle: this.minAvgRoll,\n endAngle: this.maxAvgRoll,\n fillColor: this.barColor(PitchRollPriorityElement.roll),\n },\n ];\n const pitchBars = [\n {\n startAngle: this.minAvgPitch,\n endAngle: this.maxAvgPitch,\n fillColor: this.barColor(PitchRollPriorityElement.pitch),\n },\n ];\n\n const subWatch = (\n rotation: number,\n arcFrame: typeof rollFrame.subArcFrame,\n areas: WatchArea[],\n barAreas: typeof rollBars,\n needles: typeof rollNeedles,\n advices: AngleAdviceRaw[],\n clipPath: string\n ) => html`\n <obc-watch\n class=\"sub-watch\"\n style=\"transform: rotate(${rotation}deg); clip-path: ${clipPath};\"\n .watchCircleType=${WatchCircleType.double}\n .zoomToFitArc=${true}\n .arcFrame=${arcFrame}\n .areas=${areas}\n .barAreas=${barAreas}\n .needles=${needles}\n .vessels=${[]}\n .tickmarks=${tickmarks}\n .advices=${advices}\n ></obc-watch>\n `;\n\n return html`\n ${subWatch(\n 0,\n rollFrame.subArcFrame,\n rollAreas,\n rollBars,\n rollNeedles,\n rollAdvices,\n rollClip\n )}\n ${subWatch(\n 90,\n pitchFrame.subArcFrame,\n pitchAreas,\n pitchBars,\n pitchNeedles,\n pitchAdvices,\n pitchClip\n )}\n ${subWatch(\n 180,\n rollFrame.subArcFrame,\n rollAreas,\n rollBars,\n rollNeedles,\n rollAdvices,\n rollClip\n )}\n ${subWatch(\n 270,\n pitchFrame.subArcFrame,\n pitchAreas,\n pitchBars,\n pitchNeedles,\n pitchAdvices,\n pitchClip\n )}\n `;\n }\n\n /**\n * Caution advices for a single sub-watch axis, emitted at sub-watch local\n * angles (centred on 0°). The outer extent is clamped to the actually\n * rendered band half-extent (`halfDeg`) so advices fit naturally inside\n * the visible arc and are not visually cropped by the sub-watch's sector\n * clip-path. The clip-path itself remains in place to crop the needle.\n */\n private subAdvices(\n axis: 'pitch' | 'roll',\n halfDeg: number\n ): AngleAdviceRaw[] {\n const advices: AngleAdviceRaw[] = [];\n const max = axis === 'pitch' ? this.maxPitchAdvice : this.maxRollAdvice;\n if (max === undefined) return advices;\n const trigger =\n axis === 'pitch' ? this.triggerPitchAdvice : this.triggerRollAdvice;\n const cap = axis === 'pitch' ? 30 : 45;\n const outer = Math.min(halfDeg, cap);\n const inner = Math.min(max, outer);\n const state = trigger ? AdviceState.triggered : AdviceState.regular;\n advices.push({\n minAngle: -outer,\n maxAngle: -inner,\n type: AdviceType.caution,\n state,\n hideMinTickmark: true,\n });\n advices.push({\n minAngle: inner,\n maxAngle: outer,\n type: AdviceType.caution,\n state,\n hideMaxTickmark: true,\n });\n return advices;\n }\n\n /** Full unzoomed watch — original single-instance render. */\n private renderFullWatch(areas: WatchArea[]) {\n return html`\n <obc-watch\n .watchCircleType=${WatchCircleType.double}\n .zoomToFitArc=${false}\n .areas=${areas}\n .barAreas=${[\n {\n startAngle: this.minAvgRoll,\n endAngle: this.maxAvgRoll,\n fillColor: this.barColor(PitchRollPriorityElement.roll),\n },\n {\n startAngle: 180 + this.minAvgRoll,\n endAngle: 180 + this.maxAvgRoll,\n fillColor: this.barColor(PitchRollPriorityElement.roll),\n },\n {\n startAngle: 90 + this.minAvgPitch,\n endAngle: 90 + this.maxAvgPitch,\n fillColor: this.barColor(PitchRollPriorityElement.pitch),\n },\n {\n startAngle: 270 + this.minAvgPitch,\n endAngle: 270 + this.maxAvgPitch,\n fillColor: this.barColor(PitchRollPriorityElement.pitch),\n },\n ]}\n .needles=${[\n {\n angle: this.roll,\n fillColor: this.needleColor(PitchRollPriorityElement.roll),\n strokeColor: 'var(--border-silhouette-color)',\n },\n {\n angle: 180 + this.roll,\n fillColor: this.needleColor(PitchRollPriorityElement.roll),\n strokeColor: 'var(--border-silhouette-color)',\n },\n {\n angle: 90 + this.pitch,\n fillColor: this.needleColor(PitchRollPriorityElement.pitch),\n strokeColor: 'var(--border-silhouette-color)',\n },\n {\n angle: 270 + this.pitch,\n fillColor: this.needleColor(PitchRollPriorityElement.pitch),\n strokeColor: 'var(--border-silhouette-color)',\n },\n ]}\n .vessels=${[\n {\n size: VesselImageSize.large,\n vesselImage: this.vesselImageSide,\n transform: `rotate(${this.pitch}deg)`,\n },\n {\n size: VesselImageSize.large,\n vesselImage: this.vesselImageFore,\n transform: `rotate(${this.roll}deg) scale(${this.normalizedScaleForeImage})`,\n },\n ]}\n .tickmarks=${[\n {angle: 0, type: TickmarkType.main},\n {angle: 90, type: TickmarkType.main},\n {angle: 180, type: TickmarkType.main},\n {angle: 270, type: TickmarkType.main},\n ]}\n .advices=${this.advices}\n ></obc-watch>\n `;\n }\n\n private get advices(): AngleAdviceRaw[] {\n const pitchReq = this.requestedPitchArcAngle;\n const rollReq = this.requestedRollArcAngle;\n const advices = [];\n if (this.maxPitchAdvice !== undefined) {\n const outer = Math.min(pitchReq, 30);\n const inner = Math.min(this.maxPitchAdvice, outer);\n const state = this.triggerPitchAdvice\n ? AdviceState.triggered\n : AdviceState.regular;\n advices.push({\n minAngle: 90 - outer,\n maxAngle: 90 - inner,\n type: AdviceType.caution,\n state: state,\n hideMinTickmark: true,\n });\n advices.push({\n minAngle: 90 + inner,\n maxAngle: 90 + outer,\n type: AdviceType.caution,\n state: state,\n hideMaxTickmark: true,\n });\n advices.push({\n minAngle: 270 - outer,\n maxAngle: 270 - inner,\n type: AdviceType.caution,\n state: state,\n hideMinTickmark: true,\n });\n advices.push({\n minAngle: 270 + inner,\n maxAngle: 270 + outer,\n type: AdviceType.caution,\n state: state,\n hideMaxTickmark: true,\n });\n }\n if (this.maxRollAdvice !== undefined) {\n const outer = Math.min(rollReq, 45);\n const inner = Math.min(this.maxRollAdvice, outer);\n const state = this.triggerRollAdvice\n ? AdviceState.triggered\n : AdviceState.regular;\n advices.push({\n minAngle: -outer,\n maxAngle: -inner,\n type: AdviceType.caution,\n state: state,\n hideMinTickmark: true,\n });\n advices.push({\n minAngle: inner,\n maxAngle: outer,\n type: AdviceType.caution,\n state: state,\n hideMaxTickmark: true,\n });\n advices.push({\n minAngle: 180 - outer,\n maxAngle: 180 - inner,\n type: AdviceType.caution,\n state: state,\n hideMinTickmark: true,\n });\n advices.push({\n minAngle: 180 + inner,\n maxAngle: 180 + outer,\n type: AdviceType.caution,\n state: state,\n hideMaxTickmark: true,\n });\n }\n return advices;\n }\n\n static override styles = 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}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-pitch-roll': ObcPitchRoll;\n }\n}\n"],"names":["PitchRollPriorityElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pitch-roll.js","sources":["../../../src/navigation-instruments/pitch-roll/pitch-roll.ts"],"sourcesContent":["import {LitElement, css, html, nothing, svg} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport '../watch/watch.js';\nimport {\n VesselImage,\n VesselImageSize,\n WatchCircleType,\n type WatchArea,\n OUTER_RING_RADIUS,\n innerRingRadiusFor,\n vesselImages,\n} from '../watch/watch.js';\nimport {TickmarkType} from '../watch/tickmark.js';\nimport {AdviceState, AdviceType, AngleAdviceRaw} from '../watch/advice.js';\nimport {customElement} from '../../decorator.js';\nimport {Priority} from '../types.js';\nimport '../readout/readout.js';\nimport {ReadoutDirection, ReadoutVariant} from '../readout/readout.js';\nimport {\n computeZoomToFitArcFrame,\n normalizeArcAngle,\n shiftArcFrameToOuterEdge,\n} from '../../svghelpers/arc-frame.js';\n\nexport enum PitchRollPriorityElement {\n pitch = 'pitch',\n roll = 'roll',\n}\n\n/** Half-side of the centre overlay viewBox in SVG units. */\nconst CENTRE_HALF = 200;\n\n/**\n * Minimum diagonal Euclidean distance (in central-layer / display\n * pixels on the default 400 px container) required between adjacent\n * zoomed arc bands' nearest corners (inner-to-inner and outer-to-outer\n * checked, the smaller binds). When two adjacent requested arcs would\n * sit closer than this — or actually overlap — both axes' visible arcs\n * are shortened (ratio-preserving so `aP : aR = pitchReq : rollReq`)\n * just enough to reach this clearance. The frame itself stays\n * unchanged so band thickness, position and zoom level keep matching\n * the standalone `obc-pitch` / `obc-roll` instruments.\n */\nconst CORNER_GAP_PX = 32;\n\n/** Numerical safety floor when an axis arc has to collapse for clearance. */\nconst MIN_ARC_HALF_DEG = 2;\n\n@customElement('obc-pitch-roll')\nexport class ObcPitchRoll extends LitElement {\n @property({type: Number}) pitch = 0;\n @property({type: Number}) roll = 0;\n @property({type: Number}) minAvgPitch = 0;\n @property({type: Number}) maxAvgPitch = 0;\n @property({type: Number}) minAvgRoll = 0;\n @property({type: Number}) maxAvgRoll = 0;\n @property({type: String}) vesselImageFore: VesselImage = VesselImage.psvFore;\n @property({type: String}) vesselImageSide: VesselImage = VesselImage.psvSide;\n @property({type: Number}) scaleForeImage = 1;\n @property({type: Number}) maxPitchAdvice: number | undefined = undefined;\n @property({type: Number}) maxRollAdvice: number | undefined = undefined;\n @property({type: Boolean}) triggerPitchAdvice = false;\n @property({type: Boolean}) triggerRollAdvice = false;\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: Array, attribute: false})\n priorityElements: PitchRollPriorityElement[] = [\n PitchRollPriorityElement.pitch,\n PitchRollPriorityElement.roll,\n ];\n /**\n * When `true`, the centre shows two stacked `<obc-readout>`s (pitch above\n * roll) instead of the vessel images. Default `false`.\n */\n @property({type: Boolean}) hasReadout: boolean = false;\n @property({type: Boolean}) zoomToFitArc: boolean = false;\n /**\n * Half-extent of each of the four watch arcs in degrees, measured from the\n * arc's natural center (0°/90°/180°/270°). Each arc spans\n * `center ± arcAngle`. Default `30` reproduces the historical 60°-wide\n * arcs; smaller values produce narrower arcs that, combined with\n * `zoomToFitArc`, reveal more detail in the relevant motion range.\n */\n @property({type: Number}) arcAngle: number = 30;\n /**\n * Optional per-axis override for the pitch arcs (top + bottom). Falls\n * back to {@link arcAngle} when undefined. Useful for rectangular layouts\n * where pitch and roll need different angular extents.\n */\n @property({type: Number}) pitchArcAngle?: number;\n /**\n * Optional per-axis override for the roll arcs (left + right). Falls\n * back to {@link arcAngle} when undefined.\n */\n @property({type: Number}) rollArcAngle?: number;\n\n private priorityFor(element: PitchRollPriorityElement): 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 needleColor(element: PitchRollPriorityElement): string {\n return this.priorityFor(element) === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n }\n\n private barColor(element: PitchRollPriorityElement): string {\n return this.priorityFor(element) === Priority.enhanced\n ? 'var(--instrument-enhanced-tertiary-color)'\n : 'var(--instrument-regular-tertiary-color)';\n }\n\n private get normalizedScaleForeImage(): number {\n if (!Number.isFinite(this.scaleForeImage)) {\n return 1;\n }\n return Math.max(0, Math.min(2, this.scaleForeImage));\n }\n\n /** Requested (clamped to a minimum) half-extent for each axis. */\n private get requestedPitchArcAngle(): number {\n return normalizeArcAngle(this.pitchArcAngle ?? this.arcAngle, 30);\n }\n private get requestedRollArcAngle(): number {\n return normalizeArcAngle(this.rollArcAngle ?? this.arcAngle, 30);\n }\n\n override render() {\n const pitchReq = this.requestedPitchArcAngle;\n const rollReq = this.requestedRollArcAngle;\n const areas = [\n {\n startAngle: 90 - pitchReq,\n endAngle: 90 + pitchReq,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n {\n startAngle: 270 - pitchReq,\n endAngle: 270 + pitchReq,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n {\n startAngle: 360 - rollReq,\n endAngle: rollReq,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n {\n startAngle: 180 - rollReq,\n endAngle: 180 + rollReq,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n ];\n\n const overlayViewBox = `-${CENTRE_HALF} -${CENTRE_HALF} ${CENTRE_HALF * 2} ${CENTRE_HALF * 2}`;\n const vesselScale = 224 / 160;\n\n return html`\n <div class=\"container\">\n <svg viewBox=\"${overlayViewBox}\">\n ${this.hasReadout\n ? nothing\n : svg`\n <line\n x1=\"-150\"\n y1=\"0\"\n x2=\"150\"\n y2=\"0\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n />\n <g\n style=\"transform: rotate(${this.pitch}deg) scale(${vesselScale}) translate(-80px, -80px);\"\n >\n ${this.zoomToFitArc ? vesselImages[this.vesselImageSide] : nothing}\n </g>\n <g\n style=\"transform: rotate(${this.roll}deg) scale(${vesselScale * this.normalizedScaleForeImage}) translate(-80px, -80px);\"\n >\n ${this.zoomToFitArc ? vesselImages[this.vesselImageFore] : nothing}\n </g>\n `}\n </svg>\n ${this.zoomToFitArc\n ? this.renderZoomedArcs(pitchReq, rollReq)\n : this.renderFullWatch(areas)}\n ${this.hasReadout\n ? html`<div class=\"readout\">\n <div class=\"readout-group\">\n ${this.renderReadout(\n this.pitch,\n 'Pitch',\n PitchRollPriorityElement.pitch\n )}\n <div class=\"readout-divider\"></div>\n ${this.renderReadout(\n this.roll,\n 'Roll',\n PitchRollPriorityElement.roll\n )}\n </div>\n </div>`\n : nothing}\n </div>\n `;\n }\n\n private renderReadout(\n value: number,\n label: string,\n element: PitchRollPriorityElement\n ) {\n return html`\n <obc-readout\n .variant=${ReadoutVariant.enhanced}\n .direction=${ReadoutDirection.vertical}\n .hasSetpoint=${false}\n .hasAdvice=${false}\n .value=${value}\n .fractionDigits=${0}\n .valuePriority=${this.priorityFor(element)}\n label=${label}\n unit=\"DEG\"\n ></obc-readout>\n `;\n }\n\n /**\n * Zoomed-arc layer: four CSS-rotated `<obc-watch>` instances, each\n * containing a single arc rendered at the watch's natural top\n * (`0° ± arcAngle`). Each watch handles its own `zoomToFitArc` framing\n * so each visible arc spans almost the full container — exactly like the\n * pitch and roll narrow stories. The four are rotated 0 / 90 / 180 / 270\n * to land at top / right / bottom / left. Top + bottom carry pitch data,\n * left + right carry roll data.\n *\n * Each axis can request its own half-extent via `pitchArcAngle` /\n * `rollArcAngle`. The zoom-fit frame for each axis is computed with the\n * EXACT same math as `obc-pitch` / `obc-roll` at the requested\n * half-extent (same band thickness, same zoom level, same position) so\n * each cardinal sub-watch matches its standalone equivalent. With the\n * frames left untouched, two requested arcs may overlap at the\n * diagonals; to avoid that, both arcs are shortened (ratio-preserving:\n * `aP : aR = pitchReq : rollReq`) just enough that the diagonal\n * Euclidean distance between adjacent inner BBOX corners equals\n * {@link CORNER_GAP_PX}. The shortened half-extent is passed only to\n * the band's `areas` (and clamped advices) so the sub-watch renders a\n * shorter band with its native rounded end-caps; the frame itself is\n * unchanged.\n */\n private renderZoomedArcs(pitchReq: number, rollReq: number) {\n const tickmarks = [{angle: 0, type: TickmarkType.main}];\n\n // ---- Per-axis zoom-fit frames (requested half-extents) -------------\n const ext = 48;\n const targetSize = (176 + ext) * 2;\n const innerNat = innerRingRadiusFor(WatchCircleType.double);\n const buildFrame = (halfDeg: number) => {\n const areas: WatchArea[] = [\n {\n startAngle: -halfDeg,\n endAngle: halfDeg,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n ];\n const baseFrame = computeZoomToFitArcFrame({\n areas,\n outerRadius: OUTER_RING_RADIUS,\n innerRadius: innerNat,\n extension: ext,\n targetSize,\n });\n const subArcFrame = shiftArcFrameToOuterEdge(\n baseFrame,\n OUTER_RING_RADIUS + baseFrame.radiusOffset,\n OUTER_RING_RADIUS,\n CENTRE_HALF\n );\n // Display scale: how many container px per obc-watch SVG unit.\n const scale = (CENTRE_HALF * 2) / subArcFrame.width;\n // Outer- and inner-arc circle radii of the band, in central\n // (container) px. The watch renders the area between\n // (OUTER_RING_RADIUS + radiusOffset) and (innerRingRadius +\n // radiusOffset), so radiusOffset shifts BOTH radii (band\n // thickness stays constant). Both arcs are centred at the watch's\n // SVG origin; that origin sits at central-coords (0, outerR-OR)\n // along the band's cardinal direction (= outerR-OR below centre\n // for the top band).\n const outerR = (OUTER_RING_RADIUS + baseFrame.radiusOffset) * scale;\n const innerR = (innerNat + baseFrame.radiusOffset) * scale;\n return {subArcFrame, outerR, innerR};\n };\n // Frames are built at the REQUESTED half-extents — identical to\n // what `obc-pitch` / `obc-roll` produce when `zoomToFitArc=true`.\n // Each cardinal sub-watch therefore matches its standalone\n // equivalent (band thickness, length, position, zoom level).\n const pitchFrame = buildFrame(pitchReq);\n const rollFrame = buildFrame(rollReq);\n\n // ---- Method A: shorten visible arc to clear adjacent corners --------\n // The frames stay UNCHANGED; only the half-extent passed to the\n // band's `areas` shrinks. obc-watch then renders a shorter arc with\n // its native rounded end-caps — same band thickness, same position,\n // same zoom level.\n //\n // Geometry of one band (top, cardinal = -y in container coords):\n // outer-edge endpoint at angle θ from cardinal\n // P_out(θ) = (outerR·sin θ, (outerR − OR) − outerR·cos θ)\n // inner-edge endpoint at angle θ\n // P_in(θ) = (innerR·sin θ, (outerR − OR) − innerR·cos θ)\n // The right band is the same template rotated 90° CW\n // (CSS rotate(90deg)): (x, y) → (−y, x).\n //\n // For pitch–roll layout the binding constraint is the diagonal\n // gap between the top band's right corners and the right band's\n // top corners (and analogously for the other three diagonals). Two\n // pairs need to clear each other:\n // (a) inner-corner vs inner-corner (P1 = P_in_top(+aP),\n // Q1 = rotate(P_in_right(−aR)))\n // (b) outer-corner vs outer-corner (P2 = P_out_top(+aP),\n // Q2 = rotate(P_out_right(−aR)))\n // Cap-line endpoints lie on the same arcs so any other point on\n // the cap is at least as far away.\n //\n // Define a SIGNED diagonal gap: positive when the two corners are\n // diagonally clear (both Δx > 0 AND Δy > 0), negative when either\n // projection has crossed (i.e. the bands overlap). Its magnitude\n // is the Euclidean distance between the corner pair. We bisect on\n // a scalar s ∈ [0, 1] (aP = pitchReq·s, aR = rollReq·s) so the\n // requested pitch:roll RATIO is preserved, until the MIN of the\n // inner and outer signed gaps equals CORNER_GAP_PX.\n const OR = OUTER_RING_RADIUS;\n const aPreqRad = (pitchReq * Math.PI) / 180;\n const aRreqRad = (rollReq * Math.PI) / 180;\n const signedDist = (\n px: number,\n py: number,\n qx: number,\n qy: number\n ): number => {\n const dx = qx - px;\n const dy = qy - py;\n const mag = Math.hypot(dx, dy);\n return dx > 0 && dy > 0 ? mag : -mag;\n };\n const cornerGaps = (\n apRad: number,\n arRad: number\n ): {inner: number; outer: number} => {\n const cosP = Math.cos(apRad);\n const sinP = Math.sin(apRad);\n const cosR = Math.cos(arRad);\n const sinR = Math.sin(arRad);\n // Top band's right corners (container coords).\n const p1x = pitchFrame.innerR * sinP;\n const p1y = pitchFrame.outerR - OR - pitchFrame.innerR * cosP;\n const p2x = pitchFrame.outerR * sinP;\n const p2y = pitchFrame.outerR - OR - pitchFrame.outerR * cosP;\n // Right band's top corners = top template's left corners rotated\n // 90° CW: (x, y) → (−y, x).\n // Top template's −aR inner corner: (−innerR·sin aR,\n // (outerR−OR) − innerR·cos aR) → rotated:\n // (OR − outerR + innerR·cos aR, −innerR·sin aR)\n const q1x = OR - rollFrame.outerR + rollFrame.innerR * cosR;\n const q1y = -rollFrame.innerR * sinR;\n const q2x = OR - rollFrame.outerR + rollFrame.outerR * cosR;\n const q2y = -rollFrame.outerR * sinR;\n return {\n inner: signedDist(p1x, p1y, q1x, q1y),\n outer: signedDist(p2x, p2y, q2x, q2y),\n };\n };\n const minGap = (apRad: number, arRad: number): number => {\n const g = cornerGaps(apRad, arRad);\n return Math.min(g.inner, g.outer);\n };\n let aP = aPreqRad;\n let aR = aRreqRad;\n if (minGap(aPreqRad, aRreqRad) < CORNER_GAP_PX) {\n let lo = 0;\n let hi = 1;\n for (let i = 0; i < 40; i++) {\n const mid = (lo + hi) / 2;\n if (minGap(aPreqRad * mid, aRreqRad * mid) >= CORNER_GAP_PX) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n aP = aPreqRad * lo;\n aR = aRreqRad * lo;\n }\n const pitchClampedDeg = Math.max(MIN_ARC_HALF_DEG, (aP * 180) / Math.PI);\n const rollClampedDeg = Math.max(MIN_ARC_HALF_DEG, (aR * 180) / Math.PI);\n\n const subAreas = (halfDeg: number): WatchArea[] => [\n {\n startAngle: -halfDeg,\n endAngle: halfDeg,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n ];\n const pitchAreas = subAreas(pitchClampedDeg);\n const rollAreas = subAreas(rollClampedDeg);\n\n const pitchAdvices = this.subAdvices('pitch', pitchClampedDeg);\n const rollAdvices = this.subAdvices('roll', rollClampedDeg);\n\n // Clip each sub-watch to the angular sector actually covered by the\n // (possibly shortened) arc so the indicator pill and bar end-of-range\n // limit lines cannot leak past the visible band when the value falls\n // outside the clamped range. Advices are clamped to the band extent\n // in `subAdvices` so they fit naturally and are not affected by this\n // clip. The clip is a triangle in the element's CSS box, with one\n // vertex at the watch origin (SVG 0,0 mapped to CSS px) and two\n // vertices at the intersection of the sector edges with the top edge\n // of the box. Applied in unrotated local coords; CSS rotation then\n // carries it to the correct cardinal side.\n const sectorClip = (\n halfDeg: number,\n frame: typeof rollFrame.subArcFrame\n ): string => {\n // Watch origin (SVG 0,0) in CSS percentages of the element box.\n // Element fills the 100% × 100% .container; obc-watch fills it with\n // viewBox = frame.{x,y,width,height}, so SVG (0,0) sits at:\n // (-frame.x / frame.width, -frame.y / frame.height)\n const oxPct = (-frame.x / frame.width) * 100;\n const oyPct = (-frame.y / frame.height) * 100;\n // Sector half-angle, expressed as the horizontal offset (in pct)\n // a ray reaches when traveling from the origin up to the top edge.\n const dxPct = oyPct * Math.tan((halfDeg * Math.PI) / 180);\n // Clamp to box bounds so half-angles ≥ 45° still produce a polygon\n // that reaches the corners instead of going off-canvas.\n const lx = Math.max(0, oxPct - dxPct);\n const rx = Math.min(100, oxPct + dxPct);\n return `polygon(${oxPct}% ${oyPct}%, ${lx}% 0%, ${rx}% 0%)`;\n };\n const pitchClip = sectorClip(pitchClampedDeg, pitchFrame.subArcFrame);\n const rollClip = sectorClip(rollClampedDeg, rollFrame.subArcFrame);\n\n const rollNeedles = [\n {\n angle: this.roll,\n fillColor: this.needleColor(PitchRollPriorityElement.roll),\n strokeColor: 'var(--border-silhouette-color)',\n },\n ];\n const pitchNeedles = [\n {\n angle: this.pitch,\n fillColor: this.needleColor(PitchRollPriorityElement.pitch),\n strokeColor: 'var(--border-silhouette-color)',\n },\n ];\n const rollBars = [\n {\n startAngle: this.minAvgRoll,\n endAngle: this.maxAvgRoll,\n fillColor: this.barColor(PitchRollPriorityElement.roll),\n },\n ];\n const pitchBars = [\n {\n startAngle: this.minAvgPitch,\n endAngle: this.maxAvgPitch,\n fillColor: this.barColor(PitchRollPriorityElement.pitch),\n },\n ];\n\n const subWatch = (\n rotation: number,\n arcFrame: typeof rollFrame.subArcFrame,\n areas: WatchArea[],\n barAreas: typeof rollBars,\n needles: typeof rollNeedles,\n advices: AngleAdviceRaw[],\n clipPath: string\n ) => html`\n <obc-watch\n class=\"sub-watch\"\n style=\"transform: rotate(${rotation}deg); clip-path: ${clipPath};\"\n .watchCircleType=${WatchCircleType.double}\n .zoomToFitArc=${true}\n .arcFrame=${arcFrame}\n .areas=${areas}\n .barAreas=${barAreas}\n .needles=${needles}\n .vessels=${[]}\n .tickmarks=${tickmarks}\n .advices=${advices}\n ></obc-watch>\n `;\n\n return html`\n ${subWatch(\n 0,\n rollFrame.subArcFrame,\n rollAreas,\n rollBars,\n rollNeedles,\n rollAdvices,\n rollClip\n )}\n ${subWatch(\n 90,\n pitchFrame.subArcFrame,\n pitchAreas,\n pitchBars,\n pitchNeedles,\n pitchAdvices,\n pitchClip\n )}\n ${subWatch(\n 180,\n rollFrame.subArcFrame,\n rollAreas,\n rollBars,\n rollNeedles,\n rollAdvices,\n rollClip\n )}\n ${subWatch(\n 270,\n pitchFrame.subArcFrame,\n pitchAreas,\n pitchBars,\n pitchNeedles,\n pitchAdvices,\n pitchClip\n )}\n `;\n }\n\n /**\n * Caution advices for a single sub-watch axis, emitted at sub-watch local\n * angles (centred on 0°). The outer extent is clamped to the actually\n * rendered band half-extent (`halfDeg`) so advices fit naturally inside\n * the visible arc and are not visually cropped by the sub-watch's sector\n * clip-path. The clip-path itself remains in place to crop the needle.\n */\n private subAdvices(\n axis: 'pitch' | 'roll',\n halfDeg: number\n ): AngleAdviceRaw[] {\n const advices: AngleAdviceRaw[] = [];\n const max = axis === 'pitch' ? this.maxPitchAdvice : this.maxRollAdvice;\n if (max === undefined) return advices;\n const trigger =\n axis === 'pitch' ? this.triggerPitchAdvice : this.triggerRollAdvice;\n const cap = axis === 'pitch' ? 30 : 45;\n const outer = Math.min(halfDeg, cap);\n const inner = Math.min(max, outer);\n const state = trigger ? AdviceState.triggered : AdviceState.regular;\n advices.push({\n minAngle: -outer,\n maxAngle: -inner,\n type: AdviceType.caution,\n state,\n hideMinTickmark: true,\n });\n advices.push({\n minAngle: inner,\n maxAngle: outer,\n type: AdviceType.caution,\n state,\n hideMaxTickmark: true,\n });\n return advices;\n }\n\n /** Full unzoomed watch — original single-instance render. */\n private renderFullWatch(areas: WatchArea[]) {\n return html`\n <obc-watch\n .watchCircleType=${WatchCircleType.double}\n .zoomToFitArc=${false}\n .areas=${areas}\n .barAreas=${[\n {\n startAngle: this.minAvgRoll,\n endAngle: this.maxAvgRoll,\n fillColor: this.barColor(PitchRollPriorityElement.roll),\n },\n {\n startAngle: 180 + this.minAvgRoll,\n endAngle: 180 + this.maxAvgRoll,\n fillColor: this.barColor(PitchRollPriorityElement.roll),\n },\n {\n startAngle: 90 + this.minAvgPitch,\n endAngle: 90 + this.maxAvgPitch,\n fillColor: this.barColor(PitchRollPriorityElement.pitch),\n },\n {\n startAngle: 270 + this.minAvgPitch,\n endAngle: 270 + this.maxAvgPitch,\n fillColor: this.barColor(PitchRollPriorityElement.pitch),\n },\n ]}\n .needles=${[\n {\n angle: this.roll,\n fillColor: this.needleColor(PitchRollPriorityElement.roll),\n strokeColor: 'var(--border-silhouette-color)',\n },\n {\n angle: 180 + this.roll,\n fillColor: this.needleColor(PitchRollPriorityElement.roll),\n strokeColor: 'var(--border-silhouette-color)',\n },\n {\n angle: 90 + this.pitch,\n fillColor: this.needleColor(PitchRollPriorityElement.pitch),\n strokeColor: 'var(--border-silhouette-color)',\n },\n {\n angle: 270 + this.pitch,\n fillColor: this.needleColor(PitchRollPriorityElement.pitch),\n strokeColor: 'var(--border-silhouette-color)',\n },\n ]}\n .vessels=${this.hasReadout\n ? []\n : [\n {\n size: VesselImageSize.large,\n vesselImage: this.vesselImageSide,\n transform: `rotate(${this.pitch}deg)`,\n },\n {\n size: VesselImageSize.large,\n vesselImage: this.vesselImageFore,\n transform: `rotate(${this.roll}deg) scale(${this.normalizedScaleForeImage})`,\n },\n ]}\n .tickmarks=${[\n {angle: 0, type: TickmarkType.main},\n {angle: 90, type: TickmarkType.main},\n {angle: 180, type: TickmarkType.main},\n {angle: 270, type: TickmarkType.main},\n ]}\n .advices=${this.advices}\n ></obc-watch>\n `;\n }\n\n private get advices(): AngleAdviceRaw[] {\n const pitchReq = this.requestedPitchArcAngle;\n const rollReq = this.requestedRollArcAngle;\n const advices = [];\n if (this.maxPitchAdvice !== undefined) {\n const outer = Math.min(pitchReq, 30);\n const inner = Math.min(this.maxPitchAdvice, outer);\n const state = this.triggerPitchAdvice\n ? AdviceState.triggered\n : AdviceState.regular;\n advices.push({\n minAngle: 90 - outer,\n maxAngle: 90 - inner,\n type: AdviceType.caution,\n state: state,\n hideMinTickmark: true,\n });\n advices.push({\n minAngle: 90 + inner,\n maxAngle: 90 + outer,\n type: AdviceType.caution,\n state: state,\n hideMaxTickmark: true,\n });\n advices.push({\n minAngle: 270 - outer,\n maxAngle: 270 - inner,\n type: AdviceType.caution,\n state: state,\n hideMinTickmark: true,\n });\n advices.push({\n minAngle: 270 + inner,\n maxAngle: 270 + outer,\n type: AdviceType.caution,\n state: state,\n hideMaxTickmark: true,\n });\n }\n if (this.maxRollAdvice !== undefined) {\n const outer = Math.min(rollReq, 45);\n const inner = Math.min(this.maxRollAdvice, outer);\n const state = this.triggerRollAdvice\n ? AdviceState.triggered\n : AdviceState.regular;\n advices.push({\n minAngle: -outer,\n maxAngle: -inner,\n type: AdviceType.caution,\n state: state,\n hideMinTickmark: true,\n });\n advices.push({\n minAngle: inner,\n maxAngle: outer,\n type: AdviceType.caution,\n state: state,\n hideMaxTickmark: true,\n });\n advices.push({\n minAngle: 180 - outer,\n maxAngle: 180 - inner,\n type: AdviceType.caution,\n state: state,\n hideMinTickmark: true,\n });\n advices.push({\n minAngle: 180 + inner,\n maxAngle: 180 + outer,\n type: AdviceType.caution,\n state: state,\n hideMaxTickmark: true,\n });\n }\n return advices;\n }\n\n static override styles = 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 .readout {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .readout-group {\n display: flex;\n flex-direction: column;\n align-items: center;\n width: fit-content;\n }\n\n .readout-divider {\n align-self: stretch;\n height: 1px;\n background: var(--border-divider-color);\n }\n `;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-pitch-roll': ObcPitchRoll;\n }\n}\n"],"names":["PitchRollPriorityElement"],"mappings":"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"}
|
|
@@ -393,6 +393,10 @@ const componentStyle = css`
|
|
|
393
393
|
text-align: center;
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
+
.readout.stack.vertical.alignment-center .instrument-label-unit-container {
|
|
397
|
+
align-items: center;
|
|
398
|
+
}
|
|
399
|
+
|
|
396
400
|
.readout.stack.vertical.alignment-vertical {
|
|
397
401
|
justify-items: end;
|
|
398
402
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readout.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"readout.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
2
|
import { VesselImage } from '../watch/watch.js';
|
|
3
|
+
import { Priority } from '../types.js';
|
|
3
4
|
import '../watch/watch.js';
|
|
5
|
+
import '../readout/readout.js';
|
|
6
|
+
export declare enum ObcRollType {
|
|
7
|
+
/** Single arc scale at the bottom (default). */
|
|
8
|
+
singleScale = "single-scale",
|
|
9
|
+
/** Bottom scale duplicated to the top as well. */
|
|
10
|
+
dualScale = "dual-scale"
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* `<obc-roll>` — Roll (heel) indicator with a bottom arc scale.
|
|
14
|
+
*
|
|
15
|
+
* Shows `roll` against a watch arc centred at the bottom, with an average-roll
|
|
16
|
+
* band and a rotating indicator. Supports an optional top scale (`dual-scale`),
|
|
17
|
+
* a centre readout (`hasReadout`), and a `regular`/`enhanced` palette. See the
|
|
18
|
+
* individual properties for details.
|
|
19
|
+
*
|
|
20
|
+
* @element obc-roll
|
|
21
|
+
*/
|
|
4
22
|
export declare class ObcRoll extends LitElement {
|
|
5
23
|
roll: number;
|
|
6
24
|
minAvgRoll: number;
|
|
@@ -10,6 +28,22 @@ export declare class ObcRoll extends LitElement {
|
|
|
10
28
|
maxRollAdvice: number | undefined;
|
|
11
29
|
triggerRollAdvice: boolean;
|
|
12
30
|
zoomToFitArc: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* When `true`, the centre shows an `<obc-readout>` with the roll value
|
|
33
|
+
* (label `Roll`, unit `DEG`) instead of the horizon line, rotating indicator
|
|
34
|
+
* and vessel. Default `false`.
|
|
35
|
+
*/
|
|
36
|
+
hasReadout: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* `single-scale` shows one arc at the bottom (default); `dual-scale` also
|
|
39
|
+
* shows the scale on the top arc (the indicator's opposite end).
|
|
40
|
+
*/
|
|
41
|
+
type: ObcRollType;
|
|
42
|
+
/**
|
|
43
|
+
* Colour palette for the scale fill / indicator and the readout value:
|
|
44
|
+
* `regular` (default) or `enhanced`.
|
|
45
|
+
*/
|
|
46
|
+
priority: Priority;
|
|
13
47
|
/**
|
|
14
48
|
* Half-extent of the watch arc in degrees. The arc spans `180° ± arcAngle`
|
|
15
49
|
* and roll values are placed at their true position within it. Default
|
|
@@ -24,7 +58,10 @@ export declare class ObcRoll extends LitElement {
|
|
|
24
58
|
arcAngle: number;
|
|
25
59
|
private _arcFrame;
|
|
26
60
|
private get normalizedScaleForeImage();
|
|
61
|
+
private get scaleFillColor();
|
|
62
|
+
private get indicatorColor();
|
|
27
63
|
render(): import('lit-html').TemplateResult<1>;
|
|
64
|
+
private renderScale;
|
|
28
65
|
private get advices();
|
|
29
66
|
static styles: import('lit').CSSResult;
|
|
30
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roll.d.ts","sourceRoot":"","sources":["../../../src/navigation-instruments/roll/roll.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAA0B,MAAM,KAAK,CAAC;AAExD,OAAO,mBAAmB,CAAC;AAC3B,OAAO,EAEL,WAAW,
|
|
1
|
+
{"version":3,"file":"roll.d.ts","sourceRoot":"","sources":["../../../src/navigation-instruments/roll/roll.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAA0B,MAAM,KAAK,CAAC;AAExD,OAAO,mBAAmB,CAAC;AAC3B,OAAO,EAEL,WAAW,EAMZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,uBAAuB,CAAC;AAE/B,OAAO,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAC;AAerC,oBAAY,WAAW;IACrB,gDAAgD;IAChD,WAAW,iBAAiB;IAC5B,kDAAkD;IAClD,SAAS,eAAe;CACzB;AAED;;;;;;;;;GASG;AACH,qBACa,OAAQ,SAAQ,UAAU;IACX,IAAI,SAAK;IACT,UAAU,SAAK;IACf,UAAU,SAAK;IACf,eAAe,EAAE,WAAW,CAAuB;IACnD,cAAc,SAAK;IACnB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAa;IAC7C,iBAAiB,UAAS;IAC1B,YAAY,EAAE,OAAO,CAAS;IACzD;;;;OAIG;IACwB,UAAU,EAAE,OAAO,CAAS;IACvD;;;OAGG;IACuB,IAAI,EAAE,WAAW,CAA2B;IACtE;;;OAGG;IACuB,QAAQ,EAAE,QAAQ,CAAoB;IAChE;;;;;;;;;;OAUG;IACuB,QAAQ,EAAE,MAAM,CAAM;IAEhD,OAAO,CAAC,SAAS,CAAgC;IAEjD,OAAO,KAAK,wBAAwB,GAKnC;IAED,OAAO,KAAK,cAAc,GAIzB;IAED,OAAO,KAAK,cAAc,GAIzB;IAEQ,MAAM;IA+Gf,OAAO,CAAC,WAAW;IAuCnB,OAAO,KAAK,OAAO,GA4BlB;IAED,OAAgB,MAAM,0BA4BpB;CACH;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,UAAU,EAAE,OAAO,CAAC;KACrB;CACF"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { css, LitElement, nothing, svg, html } from "lit";
|
|
2
2
|
import { property } from "lit/decorators.js";
|
|
3
3
|
import { innerRingRadiusFor, WatchCircleType, OUTER_RING_RADIUS } from "../watch/watch.js";
|
|
4
|
+
import { ReadoutVariant, ReadoutDirection } from "../readout/readout.js";
|
|
5
|
+
import { Priority } from "../types.js";
|
|
4
6
|
import { TickmarkType } from "../watch/tickmark.js";
|
|
5
7
|
import { AdviceState, AdviceType } from "../watch/advice.js";
|
|
6
8
|
import { customElement } from "../../decorator.js";
|
|
7
9
|
import { normalizeArcAngle, computeZoomToFitArcFrame, shiftArcFrameToOuterEdge } from "../../svghelpers/arc-frame.js";
|
|
8
|
-
import { VesselImage,
|
|
10
|
+
import { VesselImage, vesselImages, VesselImageSize } from "../watch/vessel.js";
|
|
9
11
|
var __defProp = Object.defineProperty;
|
|
10
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
13
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
@@ -18,6 +20,11 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
18
20
|
};
|
|
19
21
|
const watchRadius = OUTER_RING_RADIUS;
|
|
20
22
|
const CENTRE_HALF = 200;
|
|
23
|
+
var ObcRollType = /* @__PURE__ */ ((ObcRollType2) => {
|
|
24
|
+
ObcRollType2["singleScale"] = "single-scale";
|
|
25
|
+
ObcRollType2["dualScale"] = "dual-scale";
|
|
26
|
+
return ObcRollType2;
|
|
27
|
+
})(ObcRollType || {});
|
|
21
28
|
let ObcRoll = class extends LitElement {
|
|
22
29
|
constructor() {
|
|
23
30
|
super(...arguments);
|
|
@@ -29,6 +36,9 @@ let ObcRoll = class extends LitElement {
|
|
|
29
36
|
this.maxRollAdvice = void 0;
|
|
30
37
|
this.triggerRollAdvice = false;
|
|
31
38
|
this.zoomToFitArc = false;
|
|
39
|
+
this.hasReadout = false;
|
|
40
|
+
this.type = "single-scale";
|
|
41
|
+
this.priority = Priority.regular;
|
|
32
42
|
this.arcAngle = 45;
|
|
33
43
|
}
|
|
34
44
|
get normalizedScaleForeImage() {
|
|
@@ -37,6 +47,12 @@ let ObcRoll = class extends LitElement {
|
|
|
37
47
|
}
|
|
38
48
|
return Math.max(0, Math.min(2, this.scaleForeImage));
|
|
39
49
|
}
|
|
50
|
+
get scaleFillColor() {
|
|
51
|
+
return this.priority === Priority.enhanced ? "var(--instrument-enhanced-tertiary-color)" : "var(--instrument-regular-tertiary-color)";
|
|
52
|
+
}
|
|
53
|
+
get indicatorColor() {
|
|
54
|
+
return this.priority === Priority.enhanced ? "var(--instrument-enhanced-secondary-color)" : "var(--instrument-regular-secondary-color)";
|
|
55
|
+
}
|
|
40
56
|
render() {
|
|
41
57
|
const arcAngle = normalizeArcAngle(this.arcAngle, 45);
|
|
42
58
|
const x = watchRadius * Math.sin(arcAngle * Math.PI / 180);
|
|
@@ -74,97 +90,117 @@ let ObcRoll = class extends LitElement {
|
|
|
74
90
|
return html`
|
|
75
91
|
<div class="container">
|
|
76
92
|
<svg viewBox="${centreViewBox}">
|
|
77
|
-
${svg`
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
93
|
+
${this.hasReadout ? nothing : svg`
|
|
94
|
+
<line
|
|
95
|
+
x1="-${watchRadius}"
|
|
96
|
+
y1="0"
|
|
97
|
+
x2="${watchRadius}"
|
|
98
|
+
y2="0"
|
|
99
|
+
stroke="var(--instrument-frame-tertiary-color)"
|
|
100
|
+
/>
|
|
101
|
+
<line
|
|
102
|
+
x1="0"
|
|
103
|
+
y1="0"
|
|
104
|
+
y2="${watchRadius - 10}"
|
|
105
|
+
x2="0"
|
|
106
|
+
stroke="${this.indicatorColor}"
|
|
107
|
+
transform="${needleTransform}"
|
|
108
|
+
/>
|
|
109
|
+
<g
|
|
110
|
+
style="transform: rotate(${this.roll}deg) scale(${vesselScale * this.normalizedScaleForeImage}) translate(-80px, -80px);"
|
|
111
|
+
>
|
|
112
|
+
${this.zoomToFitArc ? vesselImages[this.vesselImageFore] : nothing}
|
|
113
|
+
</g>
|
|
114
|
+
`}
|
|
115
|
+
${this.zoomToFitArc ? nothing : svg`
|
|
116
|
+
<path
|
|
117
|
+
d="M ${-x} ${y} A ${watchRadius} ${watchRadius} 0 1 1 ${x} ${y}"
|
|
118
|
+
fill="none"
|
|
119
|
+
stroke="var(--instrument-frame-tertiary-color)"
|
|
120
|
+
/>
|
|
121
|
+
`}
|
|
106
122
|
</svg>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
${this.renderScale(areas, false)}
|
|
124
|
+
${this.type === "dual-scale" ? this.renderScale(areas, true) : nothing}
|
|
125
|
+
${this.hasReadout ? html`<div class="readout">
|
|
126
|
+
<obc-readout
|
|
127
|
+
.variant=${ReadoutVariant.enhanced}
|
|
128
|
+
.direction=${ReadoutDirection.vertical}
|
|
129
|
+
.hasSetpoint=${false}
|
|
130
|
+
.hasAdvice=${false}
|
|
131
|
+
.value=${this.roll}
|
|
132
|
+
.fractionDigits=${0}
|
|
133
|
+
.valuePriority=${this.priority}
|
|
134
|
+
label="Roll"
|
|
135
|
+
unit="DEG"
|
|
136
|
+
></obc-readout>
|
|
137
|
+
</div>` : nothing}
|
|
138
|
+
</div>
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
// `top` rotates a second watch 180° onto the top arc for dual-scale — a
|
|
142
|
+
// rotation (opposite end of the indicator), not a mirror. A separate watch
|
|
143
|
+
// keeps the zoomed `arcFrame` correct.
|
|
144
|
+
renderScale(areas, top) {
|
|
145
|
+
return html`
|
|
146
|
+
<obc-watch
|
|
147
|
+
class=${top ? "scale-top" : nothing}
|
|
148
|
+
.priority=${this.priority}
|
|
149
|
+
.watchCircleType=${WatchCircleType.double}
|
|
150
|
+
.zoomToFitArc=${this.zoomToFitArc}
|
|
151
|
+
.arcFrame=${this._arcFrame}
|
|
152
|
+
tickmarksInside
|
|
153
|
+
.areas=${areas}
|
|
154
|
+
.barAreas=${[
|
|
114
155
|
{
|
|
115
156
|
startAngle: 180 + this.minAvgRoll,
|
|
116
157
|
endAngle: 180 + this.maxAvgRoll,
|
|
117
|
-
fillColor:
|
|
158
|
+
fillColor: this.scaleFillColor
|
|
118
159
|
}
|
|
119
160
|
]}
|
|
120
|
-
|
|
161
|
+
.needles=${[
|
|
121
162
|
{
|
|
122
163
|
angle: 180 + this.roll,
|
|
123
|
-
fillColor:
|
|
164
|
+
fillColor: this.indicatorColor,
|
|
124
165
|
strokeColor: "var(--border-silhouette-color)"
|
|
125
166
|
}
|
|
126
167
|
]}
|
|
127
|
-
|
|
168
|
+
.vessels=${top || this.zoomToFitArc || this.hasReadout ? [] : [
|
|
128
169
|
{
|
|
129
170
|
size: VesselImageSize.large,
|
|
130
171
|
vesselImage: this.vesselImageFore,
|
|
131
172
|
transform: `rotate(${this.roll}deg) scale(${this.normalizedScaleForeImage})`
|
|
132
173
|
}
|
|
133
174
|
]}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
type: TickmarkType.main
|
|
138
|
-
}
|
|
139
|
-
]}
|
|
140
|
-
.advices=${this.advices}
|
|
141
|
-
></obc-watch>
|
|
142
|
-
</div>
|
|
175
|
+
.tickmarks=${[{ angle: 180, type: TickmarkType.main }]}
|
|
176
|
+
.advices=${this.advices}
|
|
177
|
+
></obc-watch>
|
|
143
178
|
`;
|
|
144
179
|
}
|
|
145
180
|
get advices() {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
181
|
+
if (this.maxRollAdvice === void 0) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
const arcAngle = normalizeArcAngle(this.arcAngle, 45);
|
|
185
|
+
const outer = Math.min(arcAngle, 45);
|
|
186
|
+
const inner = Math.min(this.maxRollAdvice, outer);
|
|
187
|
+
const state = this.triggerRollAdvice ? AdviceState.triggered : AdviceState.regular;
|
|
188
|
+
return [
|
|
189
|
+
{
|
|
153
190
|
minAngle: 180 - outer,
|
|
154
191
|
maxAngle: 180 - inner,
|
|
155
192
|
type: AdviceType.caution,
|
|
156
193
|
state,
|
|
157
194
|
hideMinTickmark: true
|
|
158
|
-
}
|
|
159
|
-
|
|
195
|
+
},
|
|
196
|
+
{
|
|
160
197
|
minAngle: 180 + inner,
|
|
161
198
|
maxAngle: 180 + outer,
|
|
162
199
|
type: AdviceType.caution,
|
|
163
200
|
state,
|
|
164
201
|
hideMaxTickmark: true
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return advices;
|
|
202
|
+
}
|
|
203
|
+
];
|
|
168
204
|
}
|
|
169
205
|
};
|
|
170
206
|
ObcRoll.styles = css`
|
|
@@ -185,6 +221,16 @@ ObcRoll.styles = css`
|
|
|
185
221
|
width: 100%;
|
|
186
222
|
height: 100%;
|
|
187
223
|
}
|
|
224
|
+
|
|
225
|
+
.readout {
|
|
226
|
+
display: flex;
|
|
227
|
+
align-items: center;
|
|
228
|
+
justify-content: center;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.scale-top {
|
|
232
|
+
transform: rotate(180deg);
|
|
233
|
+
}
|
|
188
234
|
`;
|
|
189
235
|
__decorateClass([
|
|
190
236
|
property({ type: Number })
|
|
@@ -210,6 +256,15 @@ __decorateClass([
|
|
|
210
256
|
__decorateClass([
|
|
211
257
|
property({ type: Boolean })
|
|
212
258
|
], ObcRoll.prototype, "zoomToFitArc", 2);
|
|
259
|
+
__decorateClass([
|
|
260
|
+
property({ type: Boolean })
|
|
261
|
+
], ObcRoll.prototype, "hasReadout", 2);
|
|
262
|
+
__decorateClass([
|
|
263
|
+
property({ type: String })
|
|
264
|
+
], ObcRoll.prototype, "type", 2);
|
|
265
|
+
__decorateClass([
|
|
266
|
+
property({ type: String })
|
|
267
|
+
], ObcRoll.prototype, "priority", 2);
|
|
213
268
|
__decorateClass([
|
|
214
269
|
property({ type: Number })
|
|
215
270
|
], ObcRoll.prototype, "arcAngle", 2);
|
|
@@ -217,6 +272,7 @@ ObcRoll = __decorateClass([
|
|
|
217
272
|
customElement("obc-roll")
|
|
218
273
|
], ObcRoll);
|
|
219
274
|
export {
|
|
220
|
-
ObcRoll
|
|
275
|
+
ObcRoll,
|
|
276
|
+
ObcRollType
|
|
221
277
|
};
|
|
222
278
|
//# sourceMappingURL=roll.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roll.js","sources":["../../../src/navigation-instruments/roll/roll.ts"],"sourcesContent":["import {LitElement, css, html, nothing, svg} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport '../watch/watch.js';\nimport {\n OUTER_RING_RADIUS,\n VesselImage,\n VesselImageSize,\n WatchCircleType,\n innerRingRadiusFor,\n vesselImages,\n} from '../watch/watch.js';\nimport {TickmarkType} from '../watch/tickmark.js';\nimport {AdviceState, AdviceType, AngleAdviceRaw} from '../watch/advice.js';\nimport {customElement} from '../../decorator.js';\nimport {\n computeZoomToFitArcFrame,\n normalizeArcAngle,\n shiftArcFrameToOuterEdge,\n type ZoomToFitArcFrame,\n} from '../../svghelpers/arc-frame.js';\n\nconst watchRadius = OUTER_RING_RADIUS;\n/** Half-side of the centre overlay viewBox in SVG units. */\nconst CENTRE_HALF = 200;\n\n@customElement('obc-roll')\nexport class ObcRoll extends LitElement {\n @property({type: Number}) roll = 0;\n @property({type: Number}) minAvgRoll = 0;\n @property({type: Number}) maxAvgRoll = 0;\n @property({type: String}) vesselImageFore: VesselImage = VesselImage.psvFore;\n @property({type: Number}) scaleForeImage = 1;\n @property({type: Number}) maxRollAdvice: number | undefined = undefined;\n @property({type: Boolean}) triggerRollAdvice = false;\n @property({type: Boolean}) zoomToFitArc: boolean = false;\n /**\n * Half-extent of the watch arc in degrees. The arc spans `180° ± arcAngle`\n * and roll values are placed at their true position within it. Default\n * `45` reproduces the historical 90°-wide arc.\n *\n * Smaller values render a narrower arc. Combined with `zoomToFitArc`, the\n * narrower arc is enlarged (its radius grows) on its own layer, while the\n * vessel image and the rotating indicator line stay at their natural size\n * and position on a separate central layer. The two layers are\n * intentionally visually disconnected.\n */\n @property({type: Number}) arcAngle: number = 45;\n\n private _arcFrame: ZoomToFitArcFrame | undefined;\n\n private get normalizedScaleForeImage(): number {\n if (!Number.isFinite(this.scaleForeImage)) {\n return 1;\n }\n return Math.max(0, Math.min(2, this.scaleForeImage));\n }\n\n override render() {\n const arcAngle = normalizeArcAngle(this.arcAngle, 45);\n // Outer thin-ring complement endpoints. The arc band is centred at\n // watch angle 180° (bottom) and spans 180° ± arcAngle, so its edges\n // sit at SVG coords (±R·sin(arcAngle), R·cos(arcAngle)).\n const x = watchRadius * Math.sin((arcAngle * Math.PI) / 180);\n const y = watchRadius * Math.cos((arcAngle * Math.PI) / 180);\n\n const areas = [\n {\n startAngle: 180 - arcAngle,\n endAngle: 180 + arcAngle,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n ];\n\n if (this.zoomToFitArc) {\n const ext = 48;\n const targetSize = (176 + ext) * 2;\n // Pure arc-only fit (compass-sector style). The viewBox is centred on\n // the enlarged arc bbox, so the origin (centre of the instrument) is\n // typically OUTSIDE this viewBox. The vessel and central elements\n // therefore need their own normal-scale layer.\n const baseFrame = computeZoomToFitArcFrame({\n areas,\n outerRadius: OUTER_RING_RADIUS,\n innerRadius: innerRingRadiusFor(WatchCircleType.double),\n extension: ext,\n targetSize,\n });\n // Push the enlarged arc to the side so its outer edge aligns with the\n // central layer's outer ring. Direction is derived from the arc bbox\n // centre so left/right/top/bottom is handled automatically.\n this._arcFrame = shiftArcFrameToOuterEdge(\n baseFrame,\n OUTER_RING_RADIUS + baseFrame.radiusOffset,\n OUTER_RING_RADIUS,\n CENTRE_HALF\n );\n } else {\n this._arcFrame = undefined;\n }\n\n const needleTransform = `rotate(${this.roll} 0 0)`;\n const centreViewBox = `-${CENTRE_HALF} -${CENTRE_HALF} ${CENTRE_HALF * 2} ${CENTRE_HALF * 2}`;\n const vesselScale = 224 / 160;\n\n return html`\n <div class=\"container\">\n <svg viewBox=\"${centreViewBox}\">\n ${svg`\n <line\n x1=\"-${watchRadius}\"\n y1=\"0\"\n x2=\"${watchRadius}\"\n y2=\"0\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n />\n <line\n x1=\"0\"\n y1=\"0\"\n y2=\"${watchRadius - 10}\"\n x2=\"0\"\n stroke=\"var(--instrument-enhanced-secondary-color)\"\n transform=\"${needleTransform}\"\n />\n <g\n style=\"transform: rotate(${this.roll}deg) scale(${vesselScale * this.normalizedScaleForeImage}) translate(-80px, -80px);\"\n >\n ${this.zoomToFitArc ? vesselImages[this.vesselImageFore] : nothing}\n </g>\n ${\n this.zoomToFitArc\n ? nothing\n : svg`\n <path\n d=\"M ${-x} ${y} A ${watchRadius} ${watchRadius} 0 1 1 ${x} ${y}\"\n fill=\"none\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n />\n `\n }\n `}\n </svg>\n <obc-watch\n .watchCircleType=${WatchCircleType.double}\n .zoomToFitArc=${this.zoomToFitArc}\n .arcFrame=${this._arcFrame}\n tickmarksInside\n .areas=${areas}\n .barAreas=${[\n {\n startAngle: 180 + this.minAvgRoll,\n endAngle: 180 + this.maxAvgRoll,\n fillColor: 'var(--instrument-enhanced-tertiary-color)',\n },\n ]}\n .needles=${[\n {\n angle: 180 + this.roll,\n fillColor: 'var(--instrument-enhanced-secondary-color)',\n strokeColor: 'var(--border-silhouette-color)',\n },\n ]}\n .vessels=${this.zoomToFitArc\n ? []\n : [\n {\n size: VesselImageSize.large,\n vesselImage: this.vesselImageFore,\n transform: `rotate(${this.roll}deg) scale(${this.normalizedScaleForeImage})`,\n },\n ]}\n .tickmarks=${[\n {\n angle: 180,\n type: TickmarkType.main,\n },\n ]}\n .advices=${this.advices}\n ></obc-watch>\n </div>\n `;\n }\n\n private get advices(): AngleAdviceRaw[] {\n const advices = [];\n if (this.maxRollAdvice !== undefined) {\n const arcAngle = normalizeArcAngle(this.arcAngle, 45);\n // Caution band fills the remainder of the arc out to a default 45°\n // caution range (clamped to the arc edge).\n const outer = Math.min(arcAngle, 45);\n const inner = Math.min(this.maxRollAdvice, outer);\n const state = this.triggerRollAdvice\n ? AdviceState.triggered\n : AdviceState.regular;\n advices.push({\n minAngle: 180 - outer,\n maxAngle: 180 - inner,\n type: AdviceType.caution,\n state: state,\n hideMinTickmark: true,\n });\n advices.push({\n minAngle: 180 + inner,\n maxAngle: 180 + outer,\n type: AdviceType.caution,\n state: state,\n hideMaxTickmark: true,\n });\n }\n return advices;\n }\n\n static override styles = 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}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-roll': ObcRoll;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"roll.js","sources":["../../../src/navigation-instruments/roll/roll.ts"],"sourcesContent":["import {LitElement, css, html, nothing, svg} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport '../watch/watch.js';\nimport {\n OUTER_RING_RADIUS,\n VesselImage,\n VesselImageSize,\n WatchCircleType,\n innerRingRadiusFor,\n vesselImages,\n type WatchArea,\n} from '../watch/watch.js';\nimport '../readout/readout.js';\nimport {ReadoutDirection, ReadoutVariant} from '../readout/readout.js';\nimport {Priority} from '../types.js';\nimport {TickmarkType} from '../watch/tickmark.js';\nimport {AdviceState, AdviceType, AngleAdviceRaw} from '../watch/advice.js';\nimport {customElement} from '../../decorator.js';\nimport {\n computeZoomToFitArcFrame,\n normalizeArcAngle,\n shiftArcFrameToOuterEdge,\n type ZoomToFitArcFrame,\n} from '../../svghelpers/arc-frame.js';\n\nconst watchRadius = OUTER_RING_RADIUS;\n/** Half-side of the centre overlay viewBox in SVG units. */\nconst CENTRE_HALF = 200;\n\nexport enum ObcRollType {\n /** Single arc scale at the bottom (default). */\n singleScale = 'single-scale',\n /** Bottom scale duplicated to the top as well. */\n dualScale = 'dual-scale',\n}\n\n/**\n * `<obc-roll>` — Roll (heel) indicator with a bottom arc scale.\n *\n * Shows `roll` against a watch arc centred at the bottom, with an average-roll\n * band and a rotating indicator. Supports an optional top scale (`dual-scale`),\n * a centre readout (`hasReadout`), and a `regular`/`enhanced` palette. See the\n * individual properties for details.\n *\n * @element obc-roll\n */\n@customElement('obc-roll')\nexport class ObcRoll extends LitElement {\n @property({type: Number}) roll = 0;\n @property({type: Number}) minAvgRoll = 0;\n @property({type: Number}) maxAvgRoll = 0;\n @property({type: String}) vesselImageFore: VesselImage = VesselImage.psvFore;\n @property({type: Number}) scaleForeImage = 1;\n @property({type: Number}) maxRollAdvice: number | undefined = undefined;\n @property({type: Boolean}) triggerRollAdvice = false;\n @property({type: Boolean}) zoomToFitArc: boolean = false;\n /**\n * When `true`, the centre shows an `<obc-readout>` with the roll value\n * (label `Roll`, unit `DEG`) instead of the horizon line, rotating indicator\n * and vessel. Default `false`.\n */\n @property({type: Boolean}) hasReadout: boolean = false;\n /**\n * `single-scale` shows one arc at the bottom (default); `dual-scale` also\n * shows the scale on the top arc (the indicator's opposite end).\n */\n @property({type: String}) type: ObcRollType = ObcRollType.singleScale;\n /**\n * Colour palette for the scale fill / indicator and the readout value:\n * `regular` (default) or `enhanced`.\n */\n @property({type: String}) priority: Priority = Priority.regular;\n /**\n * Half-extent of the watch arc in degrees. The arc spans `180° ± arcAngle`\n * and roll values are placed at their true position within it. Default\n * `45` reproduces the historical 90°-wide arc.\n *\n * Smaller values render a narrower arc. Combined with `zoomToFitArc`, the\n * narrower arc is enlarged (its radius grows) on its own layer, while the\n * vessel image and the rotating indicator line stay at their natural size\n * and position on a separate central layer. The two layers are\n * intentionally visually disconnected.\n */\n @property({type: Number}) arcAngle: number = 45;\n\n private _arcFrame: ZoomToFitArcFrame | undefined;\n\n private get normalizedScaleForeImage(): number {\n if (!Number.isFinite(this.scaleForeImage)) {\n return 1;\n }\n return Math.max(0, Math.min(2, this.scaleForeImage));\n }\n\n private get scaleFillColor(): string {\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-tertiary-color)'\n : 'var(--instrument-regular-tertiary-color)';\n }\n\n private get indicatorColor(): string {\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n }\n\n override render() {\n const arcAngle = normalizeArcAngle(this.arcAngle, 45);\n // Outer thin-ring complement endpoints. The arc band is centred at\n // watch angle 180° (bottom) and spans 180° ± arcAngle, so its edges\n // sit at SVG coords (±R·sin(arcAngle), R·cos(arcAngle)).\n const x = watchRadius * Math.sin((arcAngle * Math.PI) / 180);\n const y = watchRadius * Math.cos((arcAngle * Math.PI) / 180);\n\n const areas: WatchArea[] = [\n {\n startAngle: 180 - arcAngle,\n endAngle: 180 + arcAngle,\n roundOutsideCut: true,\n roundInsideCut: true,\n },\n ];\n\n if (this.zoomToFitArc) {\n const ext = 48;\n const targetSize = (176 + ext) * 2;\n // Pure arc-only fit (compass-sector style). The viewBox is centred on\n // the enlarged arc bbox, so the origin (centre of the instrument) is\n // typically OUTSIDE this viewBox. The vessel and central elements\n // therefore need their own normal-scale layer.\n const baseFrame = computeZoomToFitArcFrame({\n areas,\n outerRadius: OUTER_RING_RADIUS,\n innerRadius: innerRingRadiusFor(WatchCircleType.double),\n extension: ext,\n targetSize,\n });\n // Push the enlarged arc to the side so its outer edge aligns with the\n // central layer's outer ring. Direction is derived from the arc bbox\n // centre so left/right/top/bottom is handled automatically.\n this._arcFrame = shiftArcFrameToOuterEdge(\n baseFrame,\n OUTER_RING_RADIUS + baseFrame.radiusOffset,\n OUTER_RING_RADIUS,\n CENTRE_HALF\n );\n } else {\n this._arcFrame = undefined;\n }\n\n const needleTransform = `rotate(${this.roll} 0 0)`;\n const centreViewBox = `-${CENTRE_HALF} -${CENTRE_HALF} ${CENTRE_HALF * 2} ${CENTRE_HALF * 2}`;\n const vesselScale = 224 / 160;\n\n return html`\n <div class=\"container\">\n <svg viewBox=\"${centreViewBox}\">\n ${this.hasReadout\n ? nothing\n : svg`\n <line\n x1=\"-${watchRadius}\"\n y1=\"0\"\n x2=\"${watchRadius}\"\n y2=\"0\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n />\n <line\n x1=\"0\"\n y1=\"0\"\n y2=\"${watchRadius - 10}\"\n x2=\"0\"\n stroke=\"${this.indicatorColor}\"\n transform=\"${needleTransform}\"\n />\n <g\n style=\"transform: rotate(${this.roll}deg) scale(${vesselScale * this.normalizedScaleForeImage}) translate(-80px, -80px);\"\n >\n ${this.zoomToFitArc ? vesselImages[this.vesselImageFore] : nothing}\n </g>\n `}\n ${this.zoomToFitArc\n ? nothing\n : svg`\n <path\n d=\"M ${-x} ${y} A ${watchRadius} ${watchRadius} 0 1 1 ${x} ${y}\"\n fill=\"none\"\n stroke=\"var(--instrument-frame-tertiary-color)\"\n />\n `}\n </svg>\n ${this.renderScale(areas, false)}\n ${this.type === ObcRollType.dualScale\n ? this.renderScale(areas, true)\n : nothing}\n ${this.hasReadout\n ? html`<div class=\"readout\">\n <obc-readout\n .variant=${ReadoutVariant.enhanced}\n .direction=${ReadoutDirection.vertical}\n .hasSetpoint=${false}\n .hasAdvice=${false}\n .value=${this.roll}\n .fractionDigits=${0}\n .valuePriority=${this.priority}\n label=\"Roll\"\n unit=\"DEG\"\n ></obc-readout>\n </div>`\n : nothing}\n </div>\n `;\n }\n\n // `top` rotates a second watch 180° onto the top arc for dual-scale — a\n // rotation (opposite end of the indicator), not a mirror. A separate watch\n // keeps the zoomed `arcFrame` correct.\n private renderScale(areas: WatchArea[], top: boolean) {\n return html`\n <obc-watch\n class=${top ? 'scale-top' : nothing}\n .priority=${this.priority}\n .watchCircleType=${WatchCircleType.double}\n .zoomToFitArc=${this.zoomToFitArc}\n .arcFrame=${this._arcFrame}\n tickmarksInside\n .areas=${areas}\n .barAreas=${[\n {\n startAngle: 180 + this.minAvgRoll,\n endAngle: 180 + this.maxAvgRoll,\n fillColor: this.scaleFillColor,\n },\n ]}\n .needles=${[\n {\n angle: 180 + this.roll,\n fillColor: this.indicatorColor,\n strokeColor: 'var(--border-silhouette-color)',\n },\n ]}\n .vessels=${top || this.zoomToFitArc || this.hasReadout\n ? []\n : [\n {\n size: VesselImageSize.large,\n vesselImage: this.vesselImageFore,\n transform: `rotate(${this.roll}deg) scale(${this.normalizedScaleForeImage})`,\n },\n ]}\n .tickmarks=${[{angle: 180, type: TickmarkType.main}]}\n .advices=${this.advices}\n ></obc-watch>\n `;\n }\n\n private get advices(): AngleAdviceRaw[] {\n if (this.maxRollAdvice === undefined) {\n return [];\n }\n const arcAngle = normalizeArcAngle(this.arcAngle, 45);\n // Caution band fills the remainder of the arc out to a default 45° caution\n // range (clamped to the arc edge).\n const outer = Math.min(arcAngle, 45);\n const inner = Math.min(this.maxRollAdvice, outer);\n const state = this.triggerRollAdvice\n ? AdviceState.triggered\n : AdviceState.regular;\n return [\n {\n minAngle: 180 - outer,\n maxAngle: 180 - inner,\n type: AdviceType.caution,\n state: state,\n hideMinTickmark: true,\n },\n {\n minAngle: 180 + inner,\n maxAngle: 180 + outer,\n type: AdviceType.caution,\n state: state,\n hideMaxTickmark: true,\n },\n ];\n }\n\n static override styles = 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 .readout {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .scale-top {\n transform: rotate(180deg);\n }\n `;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-roll': ObcRoll;\n }\n}\n"],"names":["ObcRollType"],"mappings":"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"}
|