@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
@@ -46,12 +46,22 @@ export declare class ObcInstrumentRadial extends ObcInstrumentRadial_base {
46
46
  advices: GaugeRadialAdvice[];
47
47
  clipTop: number;
48
48
  clipBottom: number;
49
+ clipLeft: number;
50
+ clipRight: number;
51
+ /**
52
+ * Place the horizontal end labels (±90°, e.g. min/max) below the tick instead
53
+ * of beside it — the "Max-min" placement from the radial label model
54
+ * (External / Internal / Max-min). See PR #903 / design discussion.
55
+ */
56
+ endLabelsMaxMin: boolean;
49
57
  zoomToFitArc: boolean;
50
58
  private _radiusOffset;
51
59
  private _arcFrame;
52
60
  private get clampedValue();
53
61
  private get minAngle();
54
62
  private get maxAngle();
63
+ private mapAngle;
64
+ private get safeClips();
55
65
  private get _derivedNeedleColor();
56
66
  private get _derivedBarColor();
57
67
  render(): import('lit-html').TemplateResult<1>;
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-radial.d.ts","sourceRoot":"","sources":["../../../src/building-blocks/instrument-radial/instrument-radial.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,UAAU,EAAqB,MAAM,KAAK,CAAC;AAGxD,OAAO,EAEL,UAAU,EAEX,MAAM,8CAA8C,CAAC;AAEtD,OAAO,EAAC,QAAQ,EAAC,MAAM,gDAAgD,CAAC;AAExE,OAAO,EAAC,aAAa,EAAC,MAAM,gDAAgD,CAAC;AAC7E,OAAO,EAAC,eAAe,EAAE,QAAQ,EAAC,MAAM,uCAAuC,CAAC;AAWhF,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;;AAuBD,qBACa,mBAAoB,SAAQ,wBAAyB;IAKtC,KAAK,EAAE,eAAe,CAA0B;IAChD,QAAQ,EAAE,QAAQ,CAAoB;IAEtC,KAAK,SAAK;IACV,QAAQ,SAAO;IACf,QAAQ,SAAK;IACT,QAAQ,EAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,CAAS;IACvD;;;OAGG;IACuB,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAM;IAC3E;;;OAGG;IACuB,yBAAyB,EAAE,MAAM,GAAG,SAAS,CAAM;IAC7E;;;OAGG;IACuB,wBAAwB,EAAE,MAAM,GAAG,SAAS,CAC1D;IACc,IAAI,EAAE,kBAAkB,CACtB;IACF,UAAU,EAAE,kBAAkB,CAC5B;IACD,eAAe,EAAE,OAAO,CAAS;IAClC,aAAa,EAAE,aAAa,CAC9B;IACmB,OAAO,EAAE,iBAAiB,EAAE,CAAM;IACnD,OAAO,EAAE,MAAM,CAAK;IACpB,UAAU,EAAE,MAAM,CAAK;IACtB,YAAY,EAAE,OAAO,CAAS;IAEzD,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,SAAS,CAAgC;IAEjD,OAAO,KAAK,YAAY,GAIvB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,KAAK,mBAAmB,GAU9B;IAED,OAAO,KAAK,gBAAgB,GAe3B;IAEQ,MAAM;IAwFf,OAAO,KAAK,OAAO,GAoBlB;IAED,IAAI,SAAS,IAAI,QAAQ,EAAE,CA6G1B;IAED,OAAO,KAAK,QAAQ,GAoBnB;IAED,OAAgB,MAAM,0BAmCpB;CACH;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,uBAAuB,EAAE,mBAAmB,CAAC;KAC9C;CACF"}
1
+ {"version":3,"file":"instrument-radial.d.ts","sourceRoot":"","sources":["../../../src/building-blocks/instrument-radial/instrument-radial.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,UAAU,EAAqB,MAAM,KAAK,CAAC;AAGxD,OAAO,EAEL,UAAU,EAEX,MAAM,8CAA8C,CAAC;AAEtD,OAAO,EAAC,QAAQ,EAAC,MAAM,gDAAgD,CAAC;AAExE,OAAO,EAAC,aAAa,EAAC,MAAM,gDAAgD,CAAC;AAC7E,OAAO,EAAC,eAAe,EAAE,QAAQ,EAAC,MAAM,uCAAuC,CAAC;AAehF,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;;AA8ED,qBACa,mBAAoB,SAAQ,wBAAyB;IAKtC,KAAK,EAAE,eAAe,CAA0B;IAChD,QAAQ,EAAE,QAAQ,CAAoB;IAEtC,KAAK,SAAK;IACV,QAAQ,SAAO;IACf,QAAQ,SAAK;IACT,QAAQ,EAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,CAAS;IACvD;;;OAGG;IACuB,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAM;IAC3E;;;OAGG;IACuB,yBAAyB,EAAE,MAAM,GAAG,SAAS,CAAM;IAC7E;;;OAGG;IACuB,wBAAwB,EAAE,MAAM,GAAG,SAAS,CAC1D;IACc,IAAI,EAAE,kBAAkB,CACtB;IACF,UAAU,EAAE,kBAAkB,CAC5B;IACD,eAAe,EAAE,OAAO,CAAS;IAClC,aAAa,EAAE,aAAa,CAC9B;IACmB,OAAO,EAAE,iBAAiB,EAAE,CAAM;IACnD,OAAO,EAAE,MAAM,CAAK;IACpB,UAAU,EAAE,MAAM,CAAK;IACvB,QAAQ,EAAE,MAAM,CAAK;IACrB,SAAS,EAAE,MAAM,CAAK;IAChD;;;;OAIG;IACwB,eAAe,EAAE,OAAO,CAAS;IACjC,YAAY,EAAE,OAAO,CAAS;IAEzD,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,SAAS,CAAgC;IAEjD,OAAO,KAAK,YAAY,GAIvB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAID,OAAO,CAAC,QAAQ;IAWhB,OAAO,KAAK,SAAS,GAOpB;IAED,OAAO,KAAK,mBAAmB,GAU9B;IAED,OAAO,KAAK,gBAAgB,GAe3B;IAEQ,MAAM;IA8Ff,OAAO,KAAK,OAAO,GAsBlB;IAED,IAAI,SAAS,IAAI,QAAQ,EAAE,CA6G1B;IAED,OAAO,KAAK,QAAQ,GAoBnB;IAED,OAAgB,MAAM,0BAmCpB;CACH;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,uBAAuB,EAAE,mBAAmB,CAAC;KAC9C;CACF"}
@@ -17,12 +17,17 @@ var __decorateClass = (decorators, target, key, kind) => {
17
17
  if (kind && result) __defProp(target, key, result);
18
18
  return result;
19
19
  };
20
+ const WATCH_DEFAULT_VIEWBOX = 448;
20
21
  var ObcGaugeRadialType = /* @__PURE__ */ ((ObcGaugeRadialType2) => {
21
22
  ObcGaugeRadialType2["filled"] = "filled";
22
23
  ObcGaugeRadialType2["bar"] = "bar";
23
24
  ObcGaugeRadialType2["needle"] = "needle";
24
25
  return ObcGaugeRadialType2;
25
26
  })(ObcGaugeRadialType || {});
27
+ const NEEDLE_TIP_RADIUS = 160;
28
+ const NEEDLE_TIP_GAP = 5;
29
+ const NEEDLE_WIDTH = 8;
30
+ const NEEDLE_HUB_RADIUS = 16;
26
31
  function rangeIncludesZero(minValue, maxValue) {
27
32
  return minValue <= 0 && maxValue >= 0;
28
33
  }
@@ -38,6 +43,31 @@ function strongerTickmarkType(existing, candidate) {
38
43
  };
39
44
  return priority[candidate] > priority[existing] ? candidate : existing;
40
45
  }
46
+ function clampClipPercent(n) {
47
+ return Number.isFinite(n) ? Math.min(Math.max(n, 0), 100) : 0;
48
+ }
49
+ function normalizeClips(clips) {
50
+ let top = clampClipPercent(clips.top);
51
+ let bottom = clampClipPercent(clips.bottom);
52
+ let left = clampClipPercent(clips.left);
53
+ let right = clampClipPercent(clips.right);
54
+ if (top + bottom >= 100) {
55
+ top = 0;
56
+ bottom = 0;
57
+ }
58
+ if (left + right >= 100) {
59
+ left = 0;
60
+ right = 0;
61
+ }
62
+ return { top, bottom, left, right };
63
+ }
64
+ function defaultGaugeAngle(value, minValue, maxValue) {
65
+ const span = maxValue - minValue;
66
+ if (!Number.isFinite(span) || span <= 0) {
67
+ return -135;
68
+ }
69
+ return (value - minValue) / span * 270 - 135;
70
+ }
41
71
  let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
42
72
  constructor() {
43
73
  super(...arguments);
@@ -57,6 +87,9 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
57
87
  this.advices = [];
58
88
  this.clipTop = 0;
59
89
  this.clipBottom = 0;
90
+ this.clipLeft = 0;
91
+ this.clipRight = 0;
92
+ this.endLabelsMaxMin = false;
60
93
  this.zoomToFitArc = false;
61
94
  this._radiusOffset = 0;
62
95
  }
@@ -66,10 +99,27 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
66
99
  return Math.max(lowerBound, Math.min(this.value, upperBound));
67
100
  }
68
101
  get minAngle() {
69
- return this.getAngle(this.minValue);
102
+ return this.mapAngle(this.minValue);
70
103
  }
71
104
  get maxAngle() {
72
- return this.getAngle(this.maxValue);
105
+ return this.mapAngle(this.maxValue);
106
+ }
107
+ // Map a value to an angle via the consumer's `getAngle`, guarding a missing
108
+ // or non-finite mapping so a misconfigured consumer can't emit NaN geometry.
109
+ mapAngle(value) {
110
+ const fn = this.getAngle;
111
+ const angle = typeof fn === "function" ? fn(value) : defaultGaugeAngle(value, this.minValue, this.maxValue);
112
+ return Number.isFinite(angle) ? angle : 0;
113
+ }
114
+ // Clamped clips, reused for the overlay viewBox and the clips forwarded to
115
+ // obc-watch.
116
+ get safeClips() {
117
+ return normalizeClips({
118
+ top: this.clipTop,
119
+ bottom: this.clipBottom,
120
+ left: this.clipLeft,
121
+ right: this.clipRight
122
+ });
73
123
  }
74
124
  get _derivedNeedleColor() {
75
125
  if (this.state === InstrumentState.loading || this.state === InstrumentState.off) {
@@ -90,12 +140,12 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
90
140
  const barColor = this.barColor ?? this._derivedBarColor;
91
141
  const barStartValue = Math.max(this.minValue, Math.min(0, this.maxValue));
92
142
  const value = this.clampedValue;
93
- const setpointAngle = this.setpoint !== void 0 ? this.getAngle(this.setpoint) : void 0;
94
- const newSetpointAngle = this.newSetpoint !== void 0 ? this.getAngle(this.newSetpoint) : void 0;
143
+ const setpointAngle = this.setpoint !== void 0 ? this.mapAngle(this.setpoint) : void 0;
144
+ const newSetpointAngle = this.newSetpoint !== void 0 ? this.mapAngle(this.newSetpoint) : void 0;
95
145
  const barAreas = this.type === "needle" ? [] : [
96
146
  {
97
- startAngle: this.getAngle(barStartValue),
98
- endAngle: this.getAngle(value),
147
+ startAngle: this.mapAngle(barStartValue),
148
+ endAngle: this.mapAngle(value),
99
149
  fillColor: barColor
100
150
  }
101
151
  ];
@@ -108,6 +158,7 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
108
158
  }
109
159
  ];
110
160
  const watchCircleType = this.type === "needle" ? WatchCircleType.single : WatchCircleType.double;
161
+ const clips = this.safeClips;
111
162
  let viewBox;
112
163
  if (this.zoomToFitArc) {
113
164
  const ext = 48;
@@ -119,16 +170,18 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
119
170
  extension: ext,
120
171
  targetSize
121
172
  });
122
- viewBox = frame.viewBox;
123
173
  this._radiusOffset = frame.radiusOffset;
174
+ viewBox = frame.viewBox;
124
175
  this._arcFrame = frame;
125
176
  } else {
126
177
  this._radiusOffset = 0;
127
178
  this._arcFrame = void 0;
128
- const width = 448;
129
- const height = width * (1 - this.clipTop / 100 - this.clipBottom / 100);
130
- const top = -width / 2 + width * this.clipTop / 100;
131
- viewBox = `${-width / 2} ${top} ${width} ${height}`;
179
+ const full = WATCH_DEFAULT_VIEWBOX;
180
+ const w = full * (1 - clips.left / 100 - clips.right / 100);
181
+ const h = full * (1 - clips.top / 100 - clips.bottom / 100);
182
+ const left = -full / 2 + full * clips.left / 100;
183
+ const top = -full / 2 + full * clips.top / 100;
184
+ viewBox = `${left} ${top} ${w} ${h}`;
132
185
  }
133
186
  return html`
134
187
  <div class="container">
@@ -149,8 +202,11 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
149
202
  .areas=${areas}
150
203
  .watchCircleType=${watchCircleType}
151
204
  .barAreas=${barAreas}
152
- .clipTop=${this.zoomToFitArc ? 0 : this.clipTop}
153
- .clipBottom=${this.zoomToFitArc ? 0 : this.clipBottom}
205
+ .clipTop=${this.zoomToFitArc ? 0 : clips.top}
206
+ .clipBottom=${this.zoomToFitArc ? 0 : clips.bottom}
207
+ .clipLeft=${this.zoomToFitArc ? 0 : clips.left}
208
+ .clipRight=${this.zoomToFitArc ? 0 : clips.right}
209
+ .endLabelsMaxMin=${this.endLabelsMaxMin}
154
210
  .zoomToFitArc=${this.zoomToFitArc}
155
211
  .arcFrame=${this._arcFrame}
156
212
  ></obc-watch>
@@ -166,14 +222,14 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
166
222
  const rOff = this._radiusOffset;
167
223
  const value = this.clampedValue;
168
224
  if (this.type === "needle") {
169
- return svg`<g transform="rotate(${this.getAngle(value)}) translate(-256, -256)">
170
- <circle cx="256" cy="256" r="14" fill=${needleColor}/>
171
- <rect x="250" y="${96 - rOff}" width="12" height="${192 + rOff}" rx="6" fill=${needleColor}/>
172
- <rect x="252" y="${98 - rOff}" width="8" height="${188 + rOff}" rx="4" stroke=${needleColor} fill=${needleColor} stroke-width="4"/>
225
+ const tipY = 256 - (NEEDLE_TIP_RADIUS - NEEDLE_TIP_GAP) - rOff;
226
+ return svg`<g transform="rotate(${this.mapAngle(value)}) translate(-256, -256)">
227
+ <rect x="${256 - NEEDLE_WIDTH / 2}" y="${tipY}" width="${NEEDLE_WIDTH}" height="${256 - tipY}" rx="${NEEDLE_WIDTH / 2}" fill=${needleColor} stroke=${needleColor}/>
228
+ <circle cx="256" cy="256" r="${NEEDLE_HUB_RADIUS}" fill=${needleColor}/>
173
229
  </g>
174
230
  `;
175
231
  } else {
176
- return svg`<g transform="rotate(${this.getAngle(value)}) translate(-256, -256)">
232
+ return svg`<g transform="rotate(${this.mapAngle(value)}) translate(-256, -256)">
177
233
  <rect x="252" y="${96 - rOff}" width="8" height="48" rx="4" fill=${needleColor} stroke="var(--border-silhouette-color)"/>
178
234
  </g>
179
235
  `;
@@ -196,7 +252,7 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
196
252
  return;
197
253
  }
198
254
  tickmarksByValue.set(normalizedValue, {
199
- angle: this.getAngle(normalizedValue),
255
+ angle: this.mapAngle(normalizedValue),
200
256
  type,
201
257
  text
202
258
  });
@@ -256,8 +312,8 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
256
312
  get _advices() {
257
313
  const value = this.clampedValue;
258
314
  return this.advices.map((advice) => {
259
- const minAngle = this.getAngle(advice.minValue);
260
- const maxAngle = this.getAngle(advice.maxValue);
315
+ const minAngle = this.mapAngle(advice.minValue);
316
+ const maxAngle = this.mapAngle(advice.maxValue);
261
317
  let state = advice.hinted ? AdviceState.hinted : AdviceState.regular;
262
318
  if (value >= advice.minValue && value <= advice.maxValue) {
263
319
  state = AdviceState.triggered;
@@ -366,6 +422,15 @@ __decorateClass([
366
422
  __decorateClass([
367
423
  property({ type: Number })
368
424
  ], ObcInstrumentRadial.prototype, "clipBottom", 2);
425
+ __decorateClass([
426
+ property({ type: Number })
427
+ ], ObcInstrumentRadial.prototype, "clipLeft", 2);
428
+ __decorateClass([
429
+ property({ type: Number })
430
+ ], ObcInstrumentRadial.prototype, "clipRight", 2);
431
+ __decorateClass([
432
+ property({ type: Boolean })
433
+ ], ObcInstrumentRadial.prototype, "endLabelsMaxMin", 2);
369
434
  __decorateClass([
370
435
  property({ type: Boolean })
371
436
  ], ObcInstrumentRadial.prototype, "zoomToFitArc", 2);
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-radial.js","sources":["../../../src/building-blocks/instrument-radial/instrument-radial.ts"],"sourcesContent":["import {css, LitElement, html, svg, nothing} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport {customElement} from '../../decorator.js';\nimport {\n AdviceState,\n AdviceType,\n AngleAdviceRaw,\n} from '../../navigation-instruments/watch/advice.js';\nimport {WatchCircleType} from '../../navigation-instruments/watch/watch.js';\nimport {Tickmark} from '../../navigation-instruments/watch/tickmark.js';\nimport {TickmarkType} from '../../navigation-instruments/watch/tickmark.js';\nimport {TickmarkStyle} from '../../navigation-instruments/watch/tickmark.js';\nimport {InstrumentState, Priority} from '../../navigation-instruments/types.js';\nimport {SetpointMixin} from '../../svghelpers/setpoint-mixin.js';\nimport {\n OUTER_RING_RADIUS,\n innerRingRadiusFor,\n} from '../../navigation-instruments/watch/watch.js';\nimport {\n computeZoomToFitArcFrame,\n type ZoomToFitArcFrame,\n} from '../../svghelpers/arc-frame.js';\n\nexport enum ObcGaugeRadialType {\n filled = 'filled',\n bar = 'bar',\n needle = 'needle',\n}\n\nexport interface GaugeRadialAdvice {\n minValue: number;\n maxValue: number;\n type: AdviceType;\n hinted: boolean;\n}\n\nfunction rangeIncludesZero(minValue: number, maxValue: number): boolean {\n return minValue <= 0 && maxValue >= 0;\n}\n\nfunction strongerTickmarkType(\n existing: TickmarkType,\n candidate: TickmarkType\n): TickmarkType {\n const priority: Record<TickmarkType, number> = {\n [TickmarkType.zeroLineThick]: 6,\n [TickmarkType.zeroLine]: 5,\n [TickmarkType.main]: 4,\n [TickmarkType.primary]: 3,\n [TickmarkType.secondary]: 2,\n [TickmarkType.tertiary]: 1,\n [TickmarkType.textOnly]: 0,\n };\n\n return priority[candidate] > priority[existing] ? candidate : existing;\n}\n\n@customElement('obc-instrument-radial')\nexport class ObcInstrumentRadial extends SetpointMixin(LitElement) {\n // setpoint, newSetpoint, atSetpoint, touching, autoAtSetpoint,\n // autoAtSetpointDeadband, setpointAtZeroDeadband, setpointOverride\n // — all inherited from SetpointMixin\n\n @property({type: String}) state: InstrumentState = InstrumentState.active;\n @property({type: String}) priority: Priority = Priority.regular;\n\n @property({type: Number}) value = 0;\n @property({type: Number}) maxValue = 100;\n @property({type: Number}) minValue = 0;\n @property({attribute: false}) getAngle!: (v: number) => number;\n @property({type: String}) needleColor: string | undefined;\n @property({type: String}) barColor: string | undefined;\n @property({type: Boolean}) showLabels: boolean = false;\n /**\n * Interval for primary tickmarks in value units.\n * When undefined or <= 0, no primary tickmarks are shown.\n */\n @property({type: Number}) primaryTickmarkInterval: number | undefined = 50;\n /**\n * Interval for secondary tickmarks in value units.\n * When undefined or <= 0, no secondary tickmarks are shown.\n */\n @property({type: Number}) secondaryTickmarkInterval: number | undefined = 10;\n /**\n * Interval for tertiary tickmarks in value units.\n * When undefined or <= 0, no tertiary tickmarks are shown.\n */\n @property({type: Number}) tertiaryTickmarkInterval: number | undefined =\n undefined;\n @property({type: String}) type: ObcGaugeRadialType =\n ObcGaugeRadialType.filled;\n @property({type: String}) needleType: ObcGaugeRadialType =\n ObcGaugeRadialType.filled;\n @property({type: Boolean}) tickmarksInside: boolean = false;\n @property({type: String}) tickmarkStyle: TickmarkStyle =\n TickmarkStyle.regular;\n @property({type: Array, attribute: false}) advices: GaugeRadialAdvice[] = [];\n @property({type: Number}) clipTop: number = 0; // in percent of height\n @property({type: Number}) clipBottom: number = 0; // in percent of height\n @property({type: Boolean}) zoomToFitArc: boolean = false;\n\n private _radiusOffset = 0;\n private _arcFrame: ZoomToFitArcFrame | undefined;\n\n private get clampedValue(): number {\n const lowerBound = Math.min(this.minValue, this.maxValue);\n const upperBound = Math.max(this.minValue, this.maxValue);\n return Math.max(lowerBound, Math.min(this.value, upperBound));\n }\n\n private get minAngle(): number {\n return this.getAngle(this.minValue);\n }\n\n private get maxAngle(): number {\n return this.getAngle(this.maxValue);\n }\n\n private get _derivedNeedleColor(): string {\n if (\n this.state === InstrumentState.loading ||\n this.state === InstrumentState.off\n ) {\n return 'transparent';\n }\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n }\n\n private get _derivedBarColor(): string {\n if (\n this.state === InstrumentState.loading ||\n this.state === InstrumentState.off\n ) {\n return 'transparent';\n }\n if (this.type === ObcGaugeRadialType.filled) {\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n }\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-tertiary-color)'\n : 'var(--instrument-regular-tertiary-color)';\n }\n\n override render() {\n const barColor = this.barColor ?? this._derivedBarColor;\n const barStartValue = Math.max(this.minValue, Math.min(0, this.maxValue));\n const value = this.clampedValue;\n const setpointAngle =\n this.setpoint !== undefined ? this.getAngle(this.setpoint) : undefined;\n const newSetpointAngle =\n this.newSetpoint !== undefined\n ? this.getAngle(this.newSetpoint)\n : undefined;\n\n const barAreas =\n this.type === ObcGaugeRadialType.needle\n ? []\n : [\n {\n startAngle: this.getAngle(barStartValue),\n endAngle: this.getAngle(value),\n fillColor: barColor,\n },\n ];\n\n const areas = [\n {\n startAngle: this.minAngle,\n endAngle: this.maxAngle,\n roundInsideCut: true,\n roundOutsideCut: true,\n },\n ];\n\n const watchCircleType =\n this.type === ObcGaugeRadialType.needle\n ? WatchCircleType.single\n : WatchCircleType.double;\n\n let viewBox: string;\n if (this.zoomToFitArc) {\n const ext = 48;\n const targetSize = (176 + ext) * 2;\n const frame = computeZoomToFitArcFrame({\n areas,\n outerRadius: OUTER_RING_RADIUS,\n innerRadius: innerRingRadiusFor(watchCircleType),\n extension: ext,\n targetSize,\n });\n viewBox = frame.viewBox;\n this._radiusOffset = frame.radiusOffset;\n this._arcFrame = frame;\n } else {\n this._radiusOffset = 0;\n this._arcFrame = undefined;\n const width = 448;\n const height = width * (1 - this.clipTop / 100 - this.clipBottom / 100);\n const top = -width / 2 + (width * this.clipTop) / 100;\n viewBox = `${-width / 2} ${top} ${width} ${height}`;\n }\n\n return html`\n <div class=\"container\">\n <obc-watch\n .state=${this.state}\n .priority=${this.priority}\n .angleSetpoint=${setpointAngle}\n .newAngleSetpoint=${newSetpointAngle}\n .atAngleSetpoint=${this.computeAtSetpoint(value)}\n .angleSetpointAtZeroDeadband=${this.setpointAtZeroDeadband}\n .setpointOverride=${this.setpointOverride}\n .animateSetpoint=${this.animateSetpoint}\n .padding=${48}\n .tickmarks=${this.tickmarks}\n .tickmarksInside=${this.tickmarksInside}\n .tickmarkStyle=${this.tickmarkStyle}\n .advices=${this._advices}\n .areas=${areas}\n .watchCircleType=${watchCircleType}\n .barAreas=${barAreas}\n .clipTop=${this.zoomToFitArc ? 0 : this.clipTop}\n .clipBottom=${this.zoomToFitArc ? 0 : this.clipBottom}\n .zoomToFitArc=${this.zoomToFitArc}\n .arcFrame=${this._arcFrame}\n ></obc-watch>\n <svg class=\"gauge-radial\" viewBox=${viewBox}>${this._needle}</svg>\n </div>\n `;\n }\n\n private get _needle() {\n if (this.type === ObcGaugeRadialType.filled) {\n return nothing;\n }\n const needleColor = this.needleColor ?? this._derivedNeedleColor;\n const rOff = this._radiusOffset;\n const value = this.clampedValue;\n if (this.type === ObcGaugeRadialType.needle) {\n return svg`<g transform=\"rotate(${this.getAngle(value)}) translate(-256, -256)\">\n <circle cx=\"256\" cy=\"256\" r=\"14\" fill=${needleColor}/>\n <rect x=\"250\" y=\"${96 - rOff}\" width=\"12\" height=\"${192 + rOff}\" rx=\"6\" fill=${needleColor}/>\n <rect x=\"252\" y=\"${98 - rOff}\" width=\"8\" height=\"${188 + rOff}\" rx=\"4\" stroke=${needleColor} fill=${needleColor} stroke-width=\"4\"/>\n </g>\n`;\n } else {\n return svg`<g transform=\"rotate(${this.getAngle(value)}) translate(-256, -256)\">\n<rect x=\"252\" y=\"${96 - rOff}\" width=\"8\" height=\"48\" rx=\"4\" fill=${needleColor} stroke=\"var(--border-silhouette-color)\"/>\n</g>\n `;\n }\n }\n\n get tickmarks(): Tickmark[] {\n const tickmarksByValue = new Map<number, Tickmark>();\n const normalizeValue = (value: number) =>\n Math.abs(value) < 1e-9 ? 0 : Number(value.toFixed(6));\n\n const upsertTickmark = (\n value: number,\n type: TickmarkType,\n text?: string\n ) => {\n if (\n !Number.isFinite(value) ||\n value < this.minValue ||\n value > this.maxValue\n ) {\n return;\n }\n\n const normalizedValue = normalizeValue(value);\n const existing = tickmarksByValue.get(normalizedValue);\n if (existing) {\n existing.type = strongerTickmarkType(existing.type, type);\n if (text !== undefined) {\n existing.text = text;\n }\n return;\n }\n\n tickmarksByValue.set(normalizedValue, {\n angle: this.getAngle(normalizedValue),\n type,\n text,\n });\n };\n\n const addTickmarksAtInterval = (\n interval: number | undefined,\n type: TickmarkType,\n withLabels = false\n ) => {\n if (\n interval === undefined ||\n interval <= 0 ||\n !Number.isFinite(interval)\n ) {\n return;\n }\n\n const epsilon = Math.abs(interval) * 1e-6;\n const startValue =\n Math.ceil((this.minValue - epsilon) / interval) * interval;\n\n for (\n let value = startValue;\n value < this.maxValue - epsilon;\n value += interval\n ) {\n const normalizedValue = normalizeValue(value);\n if (\n normalizedValue <= this.minValue + epsilon ||\n normalizedValue >= this.maxValue - epsilon\n ) {\n continue;\n }\n\n upsertTickmark(\n normalizedValue,\n type,\n withLabels && this.showLabels ? normalizedValue.toString() : undefined\n );\n }\n };\n\n addTickmarksAtInterval(\n this.primaryTickmarkInterval,\n TickmarkType.primary,\n true\n );\n addTickmarksAtInterval(\n this.secondaryTickmarkInterval,\n TickmarkType.secondary\n );\n addTickmarksAtInterval(\n this.tertiaryTickmarkInterval,\n TickmarkType.tertiary\n );\n\n if (rangeIncludesZero(this.minValue, this.maxValue)) {\n upsertTickmark(\n 0,\n this.minValue < 0 ? TickmarkType.main : TickmarkType.textOnly,\n this.showLabels ? '0' : undefined\n );\n }\n\n if (this.showLabels) {\n upsertTickmark(\n this.minValue,\n TickmarkType.textOnly,\n this.minValue.toString()\n );\n upsertTickmark(\n this.maxValue,\n TickmarkType.textOnly,\n this.maxValue.toString()\n );\n }\n\n return [...tickmarksByValue.values()].sort((a, b) => a.angle - b.angle);\n }\n\n private get _advices(): AngleAdviceRaw[] {\n const value = this.clampedValue;\n\n return this.advices.map((advice) => {\n const minAngle = this.getAngle(advice.minValue);\n const maxAngle = this.getAngle(advice.maxValue);\n let state = advice.hinted ? AdviceState.hinted : AdviceState.regular;\n if (value >= advice.minValue && value <= advice.maxValue) {\n state = AdviceState.triggered;\n }\n\n return {\n minAngle,\n maxAngle,\n type: advice.type,\n state,\n hideMinTickmark: advice.minValue === this.minValue,\n hideMaxTickmark: advice.maxValue === this.maxValue,\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 obc-watch {\n anchor-name: --watch;\n }\n\n .speed-gauge-value {\n position: absolute;\n top: clamp(\n 70%,\n calc(80% - (anchor-size(--watch height) - 200px) * 0.2),\n 80%\n );\n left: 50%;\n transform: translateX(-50%);\n width: fit-content;\n height: fit-content;\n }\n `;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-instrument-radial': ObcInstrumentRadial;\n }\n}\n"],"names":["ObcGaugeRadialType"],"mappings":"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"}
1
+ {"version":3,"file":"instrument-radial.js","sources":["../../../src/building-blocks/instrument-radial/instrument-radial.ts"],"sourcesContent":["import {css, LitElement, html, svg, nothing} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport {customElement} from '../../decorator.js';\nimport {\n AdviceState,\n AdviceType,\n AngleAdviceRaw,\n} from '../../navigation-instruments/watch/advice.js';\nimport {WatchCircleType} from '../../navigation-instruments/watch/watch.js';\nimport {Tickmark} from '../../navigation-instruments/watch/tickmark.js';\nimport {TickmarkType} from '../../navigation-instruments/watch/tickmark.js';\nimport {TickmarkStyle} from '../../navigation-instruments/watch/tickmark.js';\nimport {InstrumentState, Priority} from '../../navigation-instruments/types.js';\nimport {SetpointMixin} from '../../svghelpers/setpoint-mixin.js';\nimport {\n OUTER_RING_RADIUS,\n innerRingRadiusFor,\n} from '../../navigation-instruments/watch/watch.js';\nimport {\n computeZoomToFitArcFrame,\n type ZoomToFitArcFrame,\n} from '../../svghelpers/arc-frame.js';\n\n// Must match obc-watch's un-zoomed viewBox `(176 + getPadding()) * 2`\n// (default padding 48 → 448); instrument-radial overlays obc-watch 1:1.\nconst WATCH_DEFAULT_VIEWBOX = 448;\n\nexport enum ObcGaugeRadialType {\n filled = 'filled',\n bar = 'bar',\n needle = 'needle',\n}\n\nexport interface GaugeRadialAdvice {\n minValue: number;\n maxValue: number;\n type: AdviceType;\n hinted: boolean;\n}\n\nconst NEEDLE_TIP_RADIUS = 160;\nconst NEEDLE_TIP_GAP = 5; // tip stops this far short of the scale\nconst NEEDLE_WIDTH = 8;\nconst NEEDLE_HUB_RADIUS = 16;\n\nfunction rangeIncludesZero(minValue: number, maxValue: number): boolean {\n return minValue <= 0 && maxValue >= 0;\n}\n\nfunction strongerTickmarkType(\n existing: TickmarkType,\n candidate: TickmarkType\n): TickmarkType {\n const priority: Record<TickmarkType, number> = {\n [TickmarkType.zeroLineThick]: 6,\n [TickmarkType.zeroLine]: 5,\n [TickmarkType.main]: 4,\n [TickmarkType.primary]: 3,\n [TickmarkType.secondary]: 2,\n [TickmarkType.tertiary]: 1,\n [TickmarkType.textOnly]: 0,\n };\n\n return priority[candidate] > priority[existing] ? candidate : existing;\n}\n\ninterface Clips {\n top: number;\n bottom: number;\n left: number;\n right: number;\n}\n\n/** Clamp a clip percentage to [0, 100]; non-finite returns 0. */\nfunction clampClipPercent(n: number): number {\n return Number.isFinite(n) ? Math.min(Math.max(n, 0), 100) : 0;\n}\n\n/**\n * Clamp the four clips to [0, 100] and drop any opposite pair that would\n * collapse the viewBox (sum >= 100), so a bad clip can't produce a zero- or\n * negative-size box. Valid clips pass through unchanged.\n */\nfunction normalizeClips(clips: Clips): Clips {\n let top = clampClipPercent(clips.top);\n let bottom = clampClipPercent(clips.bottom);\n let left = clampClipPercent(clips.left);\n let right = clampClipPercent(clips.right);\n if (top + bottom >= 100) {\n top = 0;\n bottom = 0;\n }\n if (left + right >= 100) {\n left = 0;\n right = 0;\n }\n return {top, bottom, left, right};\n}\n\n/**\n * Fallback value-to-angle mapping when no `getAngle` is supplied: linear over\n * the historical 270° sweep (-135 to 135). Returns -135 for a non-positive or\n * non-finite span.\n */\nfunction defaultGaugeAngle(\n value: number,\n minValue: number,\n maxValue: number\n): number {\n const span = maxValue - minValue;\n if (!Number.isFinite(span) || span <= 0) {\n return -135;\n }\n return ((value - minValue) / span) * 270 - 135;\n}\n\n@customElement('obc-instrument-radial')\nexport class ObcInstrumentRadial extends SetpointMixin(LitElement) {\n // setpoint, newSetpoint, atSetpoint, touching, autoAtSetpoint,\n // autoAtSetpointDeadband, setpointAtZeroDeadband, setpointOverride\n // — all inherited from SetpointMixin\n\n @property({type: String}) state: InstrumentState = InstrumentState.active;\n @property({type: String}) priority: Priority = Priority.regular;\n\n @property({type: Number}) value = 0;\n @property({type: Number}) maxValue = 100;\n @property({type: Number}) minValue = 0;\n @property({attribute: false}) getAngle!: (v: number) => number;\n @property({type: String}) needleColor: string | undefined;\n @property({type: String}) barColor: string | undefined;\n @property({type: Boolean}) showLabels: boolean = false;\n /**\n * Interval for primary tickmarks in value units.\n * When undefined or <= 0, no primary tickmarks are shown.\n */\n @property({type: Number}) primaryTickmarkInterval: number | undefined = 50;\n /**\n * Interval for secondary tickmarks in value units.\n * When undefined or <= 0, no secondary tickmarks are shown.\n */\n @property({type: Number}) secondaryTickmarkInterval: number | undefined = 10;\n /**\n * Interval for tertiary tickmarks in value units.\n * When undefined or <= 0, no tertiary tickmarks are shown.\n */\n @property({type: Number}) tertiaryTickmarkInterval: number | undefined =\n undefined;\n @property({type: String}) type: ObcGaugeRadialType =\n ObcGaugeRadialType.filled;\n @property({type: String}) needleType: ObcGaugeRadialType =\n ObcGaugeRadialType.filled;\n @property({type: Boolean}) tickmarksInside: boolean = false;\n @property({type: String}) tickmarkStyle: TickmarkStyle =\n TickmarkStyle.regular;\n @property({type: Array, attribute: false}) advices: GaugeRadialAdvice[] = [];\n @property({type: Number}) clipTop: number = 0; // in percent of height\n @property({type: Number}) clipBottom: number = 0; // in percent of height\n @property({type: Number}) clipLeft: number = 0; // in percent of width\n @property({type: Number}) clipRight: number = 0; // in percent of width\n /**\n * Place the horizontal end labels (±90°, e.g. min/max) below the tick instead\n * of beside it — the \"Max-min\" placement from the radial label model\n * (External / Internal / Max-min). See PR #903 / design discussion.\n */\n @property({type: Boolean}) endLabelsMaxMin: boolean = false;\n @property({type: Boolean}) zoomToFitArc: boolean = false;\n\n private _radiusOffset = 0;\n private _arcFrame: ZoomToFitArcFrame | undefined;\n\n private get clampedValue(): number {\n const lowerBound = Math.min(this.minValue, this.maxValue);\n const upperBound = Math.max(this.minValue, this.maxValue);\n return Math.max(lowerBound, Math.min(this.value, upperBound));\n }\n\n private get minAngle(): number {\n return this.mapAngle(this.minValue);\n }\n\n private get maxAngle(): number {\n return this.mapAngle(this.maxValue);\n }\n\n // Map a value to an angle via the consumer's `getAngle`, guarding a missing\n // or non-finite mapping so a misconfigured consumer can't emit NaN geometry.\n private mapAngle(value: number): number {\n const fn = this.getAngle;\n const angle =\n typeof fn === 'function'\n ? fn(value)\n : defaultGaugeAngle(value, this.minValue, this.maxValue);\n return Number.isFinite(angle) ? angle : 0;\n }\n\n // Clamped clips, reused for the overlay viewBox and the clips forwarded to\n // obc-watch.\n private get safeClips(): Clips {\n return normalizeClips({\n top: this.clipTop,\n bottom: this.clipBottom,\n left: this.clipLeft,\n right: this.clipRight,\n });\n }\n\n private get _derivedNeedleColor(): string {\n if (\n this.state === InstrumentState.loading ||\n this.state === InstrumentState.off\n ) {\n return 'transparent';\n }\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n }\n\n private get _derivedBarColor(): string {\n if (\n this.state === InstrumentState.loading ||\n this.state === InstrumentState.off\n ) {\n return 'transparent';\n }\n if (this.type === ObcGaugeRadialType.filled) {\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-secondary-color)'\n : 'var(--instrument-regular-secondary-color)';\n }\n return this.priority === Priority.enhanced\n ? 'var(--instrument-enhanced-tertiary-color)'\n : 'var(--instrument-regular-tertiary-color)';\n }\n\n override render() {\n const barColor = this.barColor ?? this._derivedBarColor;\n const barStartValue = Math.max(this.minValue, Math.min(0, this.maxValue));\n const value = this.clampedValue;\n const setpointAngle =\n this.setpoint !== undefined ? this.mapAngle(this.setpoint) : undefined;\n const newSetpointAngle =\n this.newSetpoint !== undefined\n ? this.mapAngle(this.newSetpoint)\n : undefined;\n\n const barAreas =\n this.type === ObcGaugeRadialType.needle\n ? []\n : [\n {\n startAngle: this.mapAngle(barStartValue),\n endAngle: this.mapAngle(value),\n fillColor: barColor,\n },\n ];\n\n const areas = [\n {\n startAngle: this.minAngle,\n endAngle: this.maxAngle,\n roundInsideCut: true,\n roundOutsideCut: true,\n },\n ];\n\n const watchCircleType =\n this.type === ObcGaugeRadialType.needle\n ? WatchCircleType.single\n : WatchCircleType.double;\n\n const clips = this.safeClips;\n let viewBox: string;\n if (this.zoomToFitArc) {\n const ext = 48;\n const targetSize = (176 + ext) * 2;\n const frame = computeZoomToFitArcFrame({\n areas,\n outerRadius: OUTER_RING_RADIUS,\n innerRadius: innerRingRadiusFor(watchCircleType),\n extension: ext,\n targetSize,\n });\n this._radiusOffset = frame.radiusOffset;\n viewBox = frame.viewBox;\n this._arcFrame = frame;\n } else {\n this._radiusOffset = 0;\n this._arcFrame = undefined;\n const full = WATCH_DEFAULT_VIEWBOX;\n const w = full * (1 - clips.left / 100 - clips.right / 100);\n const h = full * (1 - clips.top / 100 - clips.bottom / 100);\n const left = -full / 2 + (full * clips.left) / 100;\n const top = -full / 2 + (full * clips.top) / 100;\n viewBox = `${left} ${top} ${w} ${h}`;\n }\n\n return html`\n <div class=\"container\">\n <obc-watch\n .state=${this.state}\n .priority=${this.priority}\n .angleSetpoint=${setpointAngle}\n .newAngleSetpoint=${newSetpointAngle}\n .atAngleSetpoint=${this.computeAtSetpoint(value)}\n .angleSetpointAtZeroDeadband=${this.setpointAtZeroDeadband}\n .setpointOverride=${this.setpointOverride}\n .animateSetpoint=${this.animateSetpoint}\n .padding=${48}\n .tickmarks=${this.tickmarks}\n .tickmarksInside=${this.tickmarksInside}\n .tickmarkStyle=${this.tickmarkStyle}\n .advices=${this._advices}\n .areas=${areas}\n .watchCircleType=${watchCircleType}\n .barAreas=${barAreas}\n .clipTop=${this.zoomToFitArc ? 0 : clips.top}\n .clipBottom=${this.zoomToFitArc ? 0 : clips.bottom}\n .clipLeft=${this.zoomToFitArc ? 0 : clips.left}\n .clipRight=${this.zoomToFitArc ? 0 : clips.right}\n .endLabelsMaxMin=${this.endLabelsMaxMin}\n .zoomToFitArc=${this.zoomToFitArc}\n .arcFrame=${this._arcFrame}\n ></obc-watch>\n <svg class=\"gauge-radial\" viewBox=${viewBox}>${this._needle}</svg>\n </div>\n `;\n }\n\n private get _needle() {\n if (this.type === ObcGaugeRadialType.filled) {\n return nothing;\n }\n const needleColor = this.needleColor ?? this._derivedNeedleColor;\n const rOff = this._radiusOffset;\n const value = this.clampedValue;\n if (this.type === ObcGaugeRadialType.needle) {\n // Rod runs from the value tip down to the center hub. Width is constant;\n // the tip shifts outward additively under zoom.\n const tipY = 256 - (NEEDLE_TIP_RADIUS - NEEDLE_TIP_GAP) - rOff;\n return svg`<g transform=\"rotate(${this.mapAngle(value)}) translate(-256, -256)\">\n <rect x=\"${256 - NEEDLE_WIDTH / 2}\" y=\"${tipY}\" width=\"${NEEDLE_WIDTH}\" height=\"${256 - tipY}\" rx=\"${NEEDLE_WIDTH / 2}\" fill=${needleColor} stroke=${needleColor}/>\n <circle cx=\"256\" cy=\"256\" r=\"${NEEDLE_HUB_RADIUS}\" fill=${needleColor}/>\n </g>\n`;\n } else {\n return svg`<g transform=\"rotate(${this.mapAngle(value)}) translate(-256, -256)\">\n<rect x=\"252\" y=\"${96 - rOff}\" width=\"8\" height=\"48\" rx=\"4\" fill=${needleColor} stroke=\"var(--border-silhouette-color)\"/>\n</g>\n `;\n }\n }\n\n get tickmarks(): Tickmark[] {\n const tickmarksByValue = new Map<number, Tickmark>();\n const normalizeValue = (value: number) =>\n Math.abs(value) < 1e-9 ? 0 : Number(value.toFixed(6));\n\n const upsertTickmark = (\n value: number,\n type: TickmarkType,\n text?: string\n ) => {\n if (\n !Number.isFinite(value) ||\n value < this.minValue ||\n value > this.maxValue\n ) {\n return;\n }\n\n const normalizedValue = normalizeValue(value);\n const existing = tickmarksByValue.get(normalizedValue);\n if (existing) {\n existing.type = strongerTickmarkType(existing.type, type);\n if (text !== undefined) {\n existing.text = text;\n }\n return;\n }\n\n tickmarksByValue.set(normalizedValue, {\n angle: this.mapAngle(normalizedValue),\n type,\n text,\n });\n };\n\n const addTickmarksAtInterval = (\n interval: number | undefined,\n type: TickmarkType,\n withLabels = false\n ) => {\n if (\n interval === undefined ||\n interval <= 0 ||\n !Number.isFinite(interval)\n ) {\n return;\n }\n\n const epsilon = Math.abs(interval) * 1e-6;\n const startValue =\n Math.ceil((this.minValue - epsilon) / interval) * interval;\n\n for (\n let value = startValue;\n value < this.maxValue - epsilon;\n value += interval\n ) {\n const normalizedValue = normalizeValue(value);\n if (\n normalizedValue <= this.minValue + epsilon ||\n normalizedValue >= this.maxValue - epsilon\n ) {\n continue;\n }\n\n upsertTickmark(\n normalizedValue,\n type,\n withLabels && this.showLabels ? normalizedValue.toString() : undefined\n );\n }\n };\n\n addTickmarksAtInterval(\n this.primaryTickmarkInterval,\n TickmarkType.primary,\n true\n );\n addTickmarksAtInterval(\n this.secondaryTickmarkInterval,\n TickmarkType.secondary\n );\n addTickmarksAtInterval(\n this.tertiaryTickmarkInterval,\n TickmarkType.tertiary\n );\n\n if (rangeIncludesZero(this.minValue, this.maxValue)) {\n upsertTickmark(\n 0,\n this.minValue < 0 ? TickmarkType.main : TickmarkType.textOnly,\n this.showLabels ? '0' : undefined\n );\n }\n\n if (this.showLabels) {\n upsertTickmark(\n this.minValue,\n TickmarkType.textOnly,\n this.minValue.toString()\n );\n upsertTickmark(\n this.maxValue,\n TickmarkType.textOnly,\n this.maxValue.toString()\n );\n }\n\n return [...tickmarksByValue.values()].sort((a, b) => a.angle - b.angle);\n }\n\n private get _advices(): AngleAdviceRaw[] {\n const value = this.clampedValue;\n\n return this.advices.map((advice) => {\n const minAngle = this.mapAngle(advice.minValue);\n const maxAngle = this.mapAngle(advice.maxValue);\n let state = advice.hinted ? AdviceState.hinted : AdviceState.regular;\n if (value >= advice.minValue && value <= advice.maxValue) {\n state = AdviceState.triggered;\n }\n\n return {\n minAngle,\n maxAngle,\n type: advice.type,\n state,\n hideMinTickmark: advice.minValue === this.minValue,\n hideMaxTickmark: advice.maxValue === this.maxValue,\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 obc-watch {\n anchor-name: --watch;\n }\n\n .speed-gauge-value {\n position: absolute;\n top: clamp(\n 70%,\n calc(80% - (anchor-size(--watch height) - 200px) * 0.2),\n 80%\n );\n left: 50%;\n transform: translateX(-50%);\n width: fit-content;\n height: fit-content;\n }\n `;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-instrument-radial': ObcInstrumentRadial;\n }\n}\n"],"names":["ObcGaugeRadialType"],"mappings":"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"}
@@ -22,6 +22,18 @@ const componentStyle = css`
22
22
  height: 100%;
23
23
  }
24
24
 
25
+ .container > .readout {
26
+ left: 50%;
27
+ width: auto;
28
+ height: auto;
29
+ transform: translate(-50%, -50%);
30
+ }
31
+
32
+ obc-readout::part(value-wrapper),
33
+ obc-readout::part(meta-wrapper) {
34
+ justify-self: center;
35
+ }
36
+
25
37
  :host {
26
38
  display: block;
27
39
  width: 100%;
@@ -1 +1 @@
1
- {"version":3,"file":"compass-sector.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"compass-sector.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -3,6 +3,7 @@ import { AngleAdvice } from '../watch/advice.js';
3
3
  import { RotType, RotPosition } from '../watch/watch.js';
4
4
  import { InstrumentState, Priority } from '../types.js';
5
5
  import '../watch/watch.js';
6
+ import '../readout/readout.js';
6
7
  export { RotType, RotPosition };
7
8
  export declare enum CompassSectorPriorityElement {
8
9
  hdg = "hdg",
@@ -98,6 +99,12 @@ export declare class ObcCompassSector extends LitElement {
98
99
  priorityElements: CompassSectorPriorityElement[];
99
100
  tickmarksInside: boolean;
100
101
  zoomToFitArc: boolean;
102
+ /**
103
+ * When `true`, shows a centered `<obc-readout>` under the arc displaying the
104
+ * heading (label `HDG`, unit `DEG`). The value color follows the HDG entry in
105
+ * `priorityElements`, matching the HDG arrow.
106
+ */
107
+ hasReadout: boolean;
101
108
  private _headingSp;
102
109
  private _halfFOV;
103
110
  private _arcHalfExtent;
@@ -119,6 +126,22 @@ export declare class ObcCompassSector extends LitElement {
119
126
  private get _effectiveRotDegPerMin();
120
127
  private get _rotEndAngle();
121
128
  private priorityFor;
129
+ /**
130
+ * Vertical placement of the readout as a percentage of the host height.
131
+ *
132
+ * With `zoomToFitArc` the arc is reframed by `computeZoomToFitArcFrame`, whose
133
+ * `radiusOffset`/`viewBox` depend on the arc's *absolute* orientation (which
134
+ * cardinal axes its bounding box crosses), not just its bend. The arc itself
135
+ * is always rotated back to the top, so it stays put on screen — but a
136
+ * frame-derived offset would swing wildly as `heading` rotates the bbox around
137
+ * the circle. So in zoom we use a fixed offset, which keeps the readout steady
138
+ * under the (stationary) arc regardless of heading.
139
+ *
140
+ * Without zoom the viewBox is the fixed, origin-symmetric 120° framing, so the
141
+ * geometry is orientation-independent: place the readout halfway down the inner
142
+ * radius from the watch center toward the arc's inner edge.
143
+ */
144
+ private get _readoutTopPercent();
122
145
  render(): import('lit-html').TemplateResult<1>;
123
146
  static styles: import('lit').CSSResult;
124
147
  }
@@ -1 +1 @@
1
- {"version":3,"file":"compass-sector.d.ts","sourceRoot":"","sources":["../../../src/navigation-instruments/compass-sector/compass-sector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,cAAc,EAAgC,MAAM,KAAK,CAAC;AAG9E,OAAO,mBAAmB,CAAC;AAG3B,OAAO,EAAc,WAAW,EAAiB,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAKL,OAAO,EACP,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAQ3B,OAAO,EAAC,eAAe,EAAE,QAAQ,EAAC,MAAM,aAAa,CAAC;AACtD,OAAO,EAAC,OAAO,EAAE,WAAW,EAAC,CAAC;AAE9B,oBAAY,4BAA4B;IACtC,GAAG,QAAQ;IACX,GAAG,QAAQ;IACX,GAAG,QAAQ;CACZ;AA+BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBACa,gBAAiB,SAAQ,UAAU;IACpB,OAAO,SAAK;IACZ,gBAAgB,SAAK;IAErB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IACtC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,iBAAiB,EAAE,OAAO,CAAS;IACpC,6BAA6B,EAAE,MAAM,CAAO;IAC3C,uBAAuB,EAAE,OAAO,CAAS;IACvB,qBAAqB,UAAQ;IAChD,6BAA6B,EAAE,MAAM,CAAK;IACzC,eAAe,EAAE,OAAO,CAAS;IACjC,QAAQ,EAAE,OAAO,CAAS;IACV,cAAc,EAAE,WAAW,EAAE,CAAM;IAEpD,MAAM,EAAE,MAAM,CAAM;IAEpB,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,WAAW,CAA2B;IAC7E;;;;;OAKG;IACuB,0BAA0B,EAAE,MAAM,GAAG,SAAS,CAAC;IACzE;;;;OAIG;IACuB,qBAAqB,EAAE,MAAM,CAAM;IAC7D;;;;OAIG;IACuB,kBAAkB,EAAE,MAAM,CAAK;IACzD;;;;;;;OAOG;IACuB,WAAW,EAAE,MAAM,CAAM;IACxB,gBAAgB,EAAE,OAAO,CAAS;IACnC,iBAAiB,EAAE,MAAM,CAAyB;IAElD,KAAK,EAAE,eAAe,CAA0B;IAChD,QAAQ,EAAE,QAAQ,CAAoB;IAEhE,gBAAgB,EAAE,4BAA4B,EAAE,CAE9C;IACyB,eAAe,EAAE,OAAO,CAAS;IACjC,YAAY,EAAE,OAAO,CAAS;IAEzD,OAAO,CAAC,UAAU,CAGf;IAGH,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,cAAc,CAAmB;IACzC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAM;IAC5B,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,gBAAgB,CAAkB;IAC1C,OAAO,CAAC,cAAc,CAAwB;IAErC,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IA8DzC,oBAAoB,IAAI,IAAI;IASrC,OAAO,CAAC,SAAS;IAejB,OAAO,CAAC,eAAe;IAmDvB,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;IAgBrB,OAAO,KAAK,sBAAsB,GAEjC;IAED,OAAO,KAAK,YAAY,GAKvB;IAED,OAAO,CAAC,WAAW;IAWV,MAAM;IAiFf,OAAgB,MAAM,0BAA6B;CACpD;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,oBAAoB,EAAE,gBAAgB,CAAC;KACxC;CACF"}
1
+ {"version":3,"file":"compass-sector.d.ts","sourceRoot":"","sources":["../../../src/navigation-instruments/compass-sector/compass-sector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,cAAc,EAAgC,MAAM,KAAK,CAAC;AAG9E,OAAO,mBAAmB,CAAC;AAC3B,OAAO,uBAAuB,CAAC;AAI/B,OAAO,EAAc,WAAW,EAAiB,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAKL,OAAO,EACP,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAQ3B,OAAO,EAAC,eAAe,EAAE,QAAQ,EAAC,MAAM,aAAa,CAAC;AACtD,OAAO,EAAC,OAAO,EAAE,WAAW,EAAC,CAAC;AAE9B,oBAAY,4BAA4B;IACtC,GAAG,QAAQ;IACX,GAAG,QAAQ;IACX,GAAG,QAAQ;CACZ;AA+BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBACa,gBAAiB,SAAQ,UAAU;IACpB,OAAO,SAAK;IACZ,gBAAgB,SAAK;IAErB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IACtC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,iBAAiB,EAAE,OAAO,CAAS;IACpC,6BAA6B,EAAE,MAAM,CAAO;IAC3C,uBAAuB,EAAE,OAAO,CAAS;IACvB,qBAAqB,UAAQ;IAChD,6BAA6B,EAAE,MAAM,CAAK;IACzC,eAAe,EAAE,OAAO,CAAS;IACjC,QAAQ,EAAE,OAAO,CAAS;IACV,cAAc,EAAE,WAAW,EAAE,CAAM;IAEpD,MAAM,EAAE,MAAM,CAAM;IAEpB,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,WAAW,CAA2B;IAC7E;;;;;OAKG;IACuB,0BAA0B,EAAE,MAAM,GAAG,SAAS,CAAC;IACzE;;;;OAIG;IACuB,qBAAqB,EAAE,MAAM,CAAM;IAC7D;;;;OAIG;IACuB,kBAAkB,EAAE,MAAM,CAAK;IACzD;;;;;;;OAOG;IACuB,WAAW,EAAE,MAAM,CAAM;IACxB,gBAAgB,EAAE,OAAO,CAAS;IACnC,iBAAiB,EAAE,MAAM,CAAyB;IAElD,KAAK,EAAE,eAAe,CAA0B;IAChD,QAAQ,EAAE,QAAQ,CAAoB;IAEhE,gBAAgB,EAAE,4BAA4B,EAAE,CAE9C;IACyB,eAAe,EAAE,OAAO,CAAS;IACjC,YAAY,EAAE,OAAO,CAAS;IACzD;;;;OAIG;IACwB,UAAU,EAAE,OAAO,CAAS;IAEvD,OAAO,CAAC,UAAU,CAGf;IAGH,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,cAAc,CAAmB;IACzC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAM;IAC5B,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,gBAAgB,CAAkB;IAC1C,OAAO,CAAC,cAAc,CAAwB;IAErC,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IA8DzC,oBAAoB,IAAI,IAAI;IASrC,OAAO,CAAC,SAAS;IAejB,OAAO,CAAC,eAAe;IAmDvB,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;IAgBrB,OAAO,KAAK,sBAAsB,GAEjC;IAED,OAAO,KAAK,YAAY,GAKvB;IAED,OAAO,CAAC,WAAW;IAOnB;;;;;;;;;;;;;;OAcG;IACH,OAAO,KAAK,kBAAkB,GAU7B;IAMQ,MAAM;IAkGf,OAAgB,MAAM,0BAA6B;CACpD;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,oBAAoB,EAAE,gBAAgB,CAAC;KACxC;CACF"}
@@ -2,6 +2,7 @@ import { unsafeCSS, LitElement, nothing, svg, html } from "lit";
2
2
  import { property } from "lit/decorators.js";
3
3
  import componentStyle from "./compass-sector.css.js";
4
4
  import { WatchCircleType, innerRingRadiusFor, OUTER_RING_RADIUS } from "../watch/watch.js";
5
+ import { ReadoutVariant, ReadoutDirection } from "../readout/readout.js";
5
6
  import { TickmarkType, TickmarkStyle } from "../watch/tickmark.js";
6
7
  import { arrow, ArrowStyle } from "../compass/arrow.js";
7
8
  import { AdviceState } from "../watch/advice.js";
@@ -75,6 +76,7 @@ let ObcCompassSector = class extends LitElement {
75
76
  ];
76
77
  this.tickmarksInside = false;
77
78
  this.zoomToFitArc = false;
79
+ this.hasReadout = false;
78
80
  this._headingSp = new SetpointBundle({
79
81
  angularWraparound: true,
80
82
  onAnimationEnd: () => this.requestUpdate()
@@ -262,6 +264,32 @@ let ObcCompassSector = class extends LitElement {
262
264
  const selected = Array.isArray(this.priorityElements) ? this.priorityElements : [];
263
265
  return selected.includes(element) ? this.priority : Priority.regular;
264
266
  }
267
+ /**
268
+ * Vertical placement of the readout as a percentage of the host height.
269
+ *
270
+ * With `zoomToFitArc` the arc is reframed by `computeZoomToFitArcFrame`, whose
271
+ * `radiusOffset`/`viewBox` depend on the arc's *absolute* orientation (which
272
+ * cardinal axes its bounding box crosses), not just its bend. The arc itself
273
+ * is always rotated back to the top, so it stays put on screen — but a
274
+ * frame-derived offset would swing wildly as `heading` rotates the bbox around
275
+ * the circle. So in zoom we use a fixed offset, which keeps the readout steady
276
+ * under the (stationary) arc regardless of heading.
277
+ *
278
+ * Without zoom the viewBox is the fixed, origin-symmetric 120° framing, so the
279
+ * geometry is orientation-independent: place the readout halfway down the inner
280
+ * radius from the watch center toward the arc's inner edge.
281
+ */
282
+ get _readoutTopPercent() {
283
+ if (this.zoomToFitArc) {
284
+ return 70;
285
+ }
286
+ const [, vy, , vh] = this._cachedViewBox.split(" ").map(Number);
287
+ if (!vh || Number.isNaN(vy)) {
288
+ return 50;
289
+ }
290
+ const anchorY = -INNER_RADIUS * 0.5;
291
+ return Math.round((anchorY - vy) / vh * 1e3) / 10;
292
+ }
265
293
  // ---------------------------------------------------------------------------
266
294
  // Render
267
295
  // ---------------------------------------------------------------------------
@@ -343,6 +371,22 @@ let ObcCompassSector = class extends LitElement {
343
371
  rOff
344
372
  )}
345
373
  </svg>
374
+ ${this.hasReadout ? html`<div class="readout" style="top: ${this._readoutTopPercent}%">
375
+ <obc-readout
376
+ .variant=${ReadoutVariant.enhanced}
377
+ .direction=${ReadoutDirection.vertical}
378
+ .hasSetpoint=${false}
379
+ .hasAdvice=${false}
380
+ .value=${this.heading}
381
+ .fractionDigits=${0}
382
+ .valuePriority=${this.priorityFor(
383
+ "hdg"
384
+ /* hdg */
385
+ )}
386
+ label="HDG"
387
+ unit="DEG"
388
+ ></obc-readout>
389
+ </div>` : nothing}
346
390
  </div>
347
391
  `;
348
392
  }
@@ -426,6 +470,9 @@ __decorateClass([
426
470
  __decorateClass([
427
471
  property({ type: Boolean })
428
472
  ], ObcCompassSector.prototype, "zoomToFitArc", 2);
473
+ __decorateClass([
474
+ property({ type: Boolean })
475
+ ], ObcCompassSector.prototype, "hasReadout", 2);
429
476
  ObcCompassSector = __decorateClass([
430
477
  customElement("obc-compass-sector")
431
478
  ], ObcCompassSector);