@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.105 → 2.0.0-next.106

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.
@@ -30,6 +30,20 @@ export enum PitchRollPriorityElement {
30
30
  roll = 'roll',
31
31
  }
32
32
 
33
+ export enum PitchRollType {
34
+ /**
35
+ * Pitch and roll arcs on both opposing sides (default). Unlike
36
+ * `obc-pitch`/`obc-roll` this is the default, because it is the historical
37
+ * behaviour of `obc-pitch-roll`.
38
+ */
39
+ dualScale = 'dual-scale',
40
+ /**
41
+ * One pitch arc on the right and one roll arc at the bottom, with a thin
42
+ * ring completing the circle and rotating indicator lines in the centre.
43
+ */
44
+ singleScale = 'single-scale',
45
+ }
46
+
33
47
  /** Half-side of the centre overlay viewBox in SVG units. */
34
48
  const CENTRE_HALF = 200;
35
49
 
@@ -54,6 +68,12 @@ const MIN_ARC_HALF_DEG = 2;
54
68
  */
55
69
  @customElement('obc-pitch-roll')
56
70
  export class ObcPitchRoll extends LitElement {
71
+ /**
72
+ * `dual-scale` (default) shows pitch and roll arcs on both opposing sides;
73
+ * `single-scale` shows one pitch arc on the right and one roll arc at the
74
+ * bottom, completed by a thin ring.
75
+ */
76
+ @property({type: String}) type: PitchRollType = PitchRollType.dualScale;
57
77
  @property({type: Number}) pitch = 0;
58
78
  @property({type: Number}) roll = 0;
59
79
  @property({type: Number}) minAvgPitch = 0;
@@ -155,28 +175,20 @@ export class ObcPitchRoll extends LitElement {
155
175
  return normalizeArcAngle(this.rollArcAngle ?? this.arcAngle, 30);
156
176
  }
157
177
 
178
+ private get isSingleScale(): boolean {
179
+ return this.type === PitchRollType.singleScale;
180
+ }
181
+
158
182
  override render() {
159
183
  const pitchReq = this.requestedPitchArcAngle;
160
184
  const rollReq = this.requestedRollArcAngle;
161
- const areas = [
185
+ const areas: WatchArea[] = [
162
186
  {
163
187
  startAngle: 90 - pitchReq,
164
188
  endAngle: 90 + pitchReq,
165
189
  roundOutsideCut: true,
166
190
  roundInsideCut: true,
167
191
  },
168
- {
169
- startAngle: 270 - pitchReq,
170
- endAngle: 270 + pitchReq,
171
- roundOutsideCut: true,
172
- roundInsideCut: true,
173
- },
174
- {
175
- startAngle: 360 - rollReq,
176
- endAngle: rollReq,
177
- roundOutsideCut: true,
178
- roundInsideCut: true,
179
- },
180
192
  {
181
193
  startAngle: 180 - rollReq,
182
194
  endAngle: 180 + rollReq,
@@ -184,6 +196,22 @@ export class ObcPitchRoll extends LitElement {
184
196
  roundInsideCut: true,
185
197
  },
186
198
  ];
199
+ if (!this.isSingleScale) {
200
+ areas.push(
201
+ {
202
+ startAngle: 270 - pitchReq,
203
+ endAngle: 270 + pitchReq,
204
+ roundOutsideCut: true,
205
+ roundInsideCut: true,
206
+ },
207
+ {
208
+ startAngle: 360 - rollReq,
209
+ endAngle: rollReq,
210
+ roundOutsideCut: true,
211
+ roundInsideCut: true,
212
+ }
213
+ );
214
+ }
187
215
 
188
216
  const overlayViewBox = `-${CENTRE_HALF} -${CENTRE_HALF} ${CENTRE_HALF * 2} ${CENTRE_HALF * 2}`;
189
217
  const vesselScale = 224 / 160;
@@ -191,16 +219,56 @@ export class ObcPitchRoll extends LitElement {
191
219
  return html`
192
220
  <div class="container">
193
221
  <svg viewBox="${overlayViewBox}">
222
+ ${this.isSingleScale && !this.zoomToFitArc
223
+ ? this.renderRingComplement(pitchReq, rollReq)
224
+ : nothing}
194
225
  ${this.hasReadout
195
226
  ? nothing
196
227
  : svg`
197
- <line
198
- x1="-150"
199
- y1="0"
200
- x2="150"
201
- y2="0"
202
- stroke="var(--instrument-frame-tertiary-color)"
203
- />
228
+ ${
229
+ this.isSingleScale
230
+ ? svg`
231
+ <line
232
+ x1=${-OUTER_RING_RADIUS}
233
+ y1="0"
234
+ x2=${OUTER_RING_RADIUS}
235
+ y2="0"
236
+ stroke="var(--instrument-frame-tertiary-color)"
237
+ />
238
+ <line
239
+ x1="0"
240
+ y1=${-OUTER_RING_RADIUS}
241
+ x2="0"
242
+ y2=${OUTER_RING_RADIUS}
243
+ stroke="var(--instrument-frame-tertiary-color)"
244
+ />
245
+ <line
246
+ x1="0"
247
+ y1="0"
248
+ x2=${OUTER_RING_RADIUS - 10}
249
+ y2="0"
250
+ stroke="${this.needleColor(PitchRollPriorityElement.pitch)}"
251
+ transform="rotate(${this.pitch} 0 0)"
252
+ />
253
+ <line
254
+ x1="0"
255
+ y1="0"
256
+ x2="0"
257
+ y2=${OUTER_RING_RADIUS - 10}
258
+ stroke="${this.needleColor(PitchRollPriorityElement.roll)}"
259
+ transform="rotate(${this.roll} 0 0)"
260
+ />
261
+ `
262
+ : svg`
263
+ <line
264
+ x1="-150"
265
+ y1="0"
266
+ x2="150"
267
+ y2="0"
268
+ stroke="var(--instrument-frame-tertiary-color)"
269
+ />
270
+ `
271
+ }
204
272
  <g
205
273
  style="transform: rotate(${this.pitch}deg) scale(${vesselScale}) translate(-80px, -80px);"
206
274
  >
@@ -242,6 +310,38 @@ export class ObcPitchRoll extends LitElement {
242
310
  `;
243
311
  }
244
312
 
313
+ /**
314
+ * Thin ring segments completing the circle between the single-scale arcs:
315
+ * one short segment between the pitch (right) and roll (bottom) arcs, and
316
+ * one long segment the other way around (left and top).
317
+ */
318
+ private renderRingComplement(pitchArc: number, rollArc: number) {
319
+ const r = OUTER_RING_RADIUS;
320
+ const pt = (deg: number): [number, number] => {
321
+ const rad = ((deg - 90) * Math.PI) / 180;
322
+ return [r * Math.cos(rad), r * Math.sin(rad)];
323
+ };
324
+ const segment = (from: number, to: number) => {
325
+ if (to - from <= 0) {
326
+ return nothing;
327
+ }
328
+ const [x1, y1] = pt(from);
329
+ const [x2, y2] = pt(to);
330
+ const large = to - from > 180 ? 1 : 0;
331
+ return svg`
332
+ <path
333
+ d="M ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2}"
334
+ fill="none"
335
+ stroke="var(--instrument-frame-tertiary-color)"
336
+ />
337
+ `;
338
+ };
339
+ return svg`
340
+ ${segment(90 + pitchArc, 180 - rollArc)}
341
+ ${segment(180 + rollArc, 450 - pitchArc)}
342
+ `;
343
+ }
344
+
245
345
  /**
246
346
  * Zoomed-arc layer: four CSS-rotated `<obc-watch>` instances, each
247
347
  * containing a single arc rendered at the watch's natural top
@@ -521,16 +621,18 @@ export class ObcPitchRoll extends LitElement {
521
621
  `;
522
622
 
523
623
  return html`
524
- ${subWatch(
525
- 0,
526
- rollFrame.subArcFrame,
527
- rollAreas,
528
- rollBars,
529
- rollNeedles,
530
- rollAdvices,
531
- rollClip,
532
- rollTickmarks
533
- )}
624
+ ${this.isSingleScale
625
+ ? nothing
626
+ : subWatch(
627
+ 0,
628
+ rollFrame.subArcFrame,
629
+ rollAreas,
630
+ rollBars,
631
+ rollNeedles,
632
+ rollAdvices,
633
+ rollClip,
634
+ rollTickmarks
635
+ )}
534
636
  ${subWatch(
535
637
  90,
536
638
  pitchFrame.subArcFrame,
@@ -551,16 +653,18 @@ export class ObcPitchRoll extends LitElement {
551
653
  rollClip,
552
654
  rollTickmarks
553
655
  )}
554
- ${subWatch(
555
- 270,
556
- pitchFrame.subArcFrame,
557
- pitchAreas,
558
- pitchBars,
559
- pitchNeedles,
560
- pitchAdvices,
561
- pitchClip,
562
- pitchTickmarks
563
- )}
656
+ ${this.isSingleScale
657
+ ? nothing
658
+ : subWatch(
659
+ 270,
660
+ pitchFrame.subArcFrame,
661
+ pitchAreas,
662
+ pitchBars,
663
+ pitchNeedles,
664
+ pitchAdvices,
665
+ pitchClip,
666
+ pitchTickmarks
667
+ )}
564
668
  `;
565
669
  }
566
670
 
@@ -603,55 +707,76 @@ export class ObcPitchRoll extends LitElement {
603
707
 
604
708
  /** Full unzoomed watch — original single-instance render. */
605
709
  private renderFullWatch(areas: WatchArea[]) {
710
+ const barAreas = [
711
+ {
712
+ startAngle: 180 + this.minAvgRoll,
713
+ endAngle: 180 + this.maxAvgRoll,
714
+ fillColor: this.barColor(PitchRollPriorityElement.roll),
715
+ },
716
+ {
717
+ startAngle: 90 + this.minAvgPitch,
718
+ endAngle: 90 + this.maxAvgPitch,
719
+ fillColor: this.barColor(PitchRollPriorityElement.pitch),
720
+ },
721
+ ];
722
+ const needles = [
723
+ {
724
+ angle: 180 + this.roll,
725
+ fillColor: this.needleColor(PitchRollPriorityElement.roll),
726
+ strokeColor: 'var(--border-silhouette-color)',
727
+ },
728
+ {
729
+ angle: 90 + this.pitch,
730
+ fillColor: this.needleColor(PitchRollPriorityElement.pitch),
731
+ strokeColor: 'var(--border-silhouette-color)',
732
+ },
733
+ ];
734
+ const tickmarks = [
735
+ {angle: 90, type: TickmarkType.main},
736
+ {angle: 180, type: TickmarkType.main},
737
+ ...arcTickmarks(90, this.requestedPitchArcAngle),
738
+ ...arcTickmarks(180, this.requestedRollArcAngle),
739
+ ];
740
+ if (!this.isSingleScale) {
741
+ barAreas.push(
742
+ {
743
+ startAngle: this.minAvgRoll,
744
+ endAngle: this.maxAvgRoll,
745
+ fillColor: this.barColor(PitchRollPriorityElement.roll),
746
+ },
747
+ {
748
+ startAngle: 270 + this.minAvgPitch,
749
+ endAngle: 270 + this.maxAvgPitch,
750
+ fillColor: this.barColor(PitchRollPriorityElement.pitch),
751
+ }
752
+ );
753
+ needles.push(
754
+ {
755
+ angle: this.roll,
756
+ fillColor: this.needleColor(PitchRollPriorityElement.roll),
757
+ strokeColor: 'var(--border-silhouette-color)',
758
+ },
759
+ {
760
+ angle: 270 + this.pitch,
761
+ fillColor: this.needleColor(PitchRollPriorityElement.pitch),
762
+ strokeColor: 'var(--border-silhouette-color)',
763
+ }
764
+ );
765
+ tickmarks.push(
766
+ {angle: 0, type: TickmarkType.main},
767
+ {angle: 270, type: TickmarkType.main},
768
+ ...arcTickmarks(0, this.requestedRollArcAngle),
769
+ ...arcTickmarks(270, this.requestedPitchArcAngle)
770
+ );
771
+ }
772
+
606
773
  return html`
607
774
  <obc-watch
608
775
  .watchCircleType=${WatchCircleType.double}
609
776
  .zoomToFitArc=${false}
610
777
  .areas=${areas}
611
- .barAreas=${[
612
- {
613
- startAngle: this.minAvgRoll,
614
- endAngle: this.maxAvgRoll,
615
- fillColor: this.barColor(PitchRollPriorityElement.roll),
616
- },
617
- {
618
- startAngle: 180 + this.minAvgRoll,
619
- endAngle: 180 + this.maxAvgRoll,
620
- fillColor: this.barColor(PitchRollPriorityElement.roll),
621
- },
622
- {
623
- startAngle: 90 + this.minAvgPitch,
624
- endAngle: 90 + this.maxAvgPitch,
625
- fillColor: this.barColor(PitchRollPriorityElement.pitch),
626
- },
627
- {
628
- startAngle: 270 + this.minAvgPitch,
629
- endAngle: 270 + this.maxAvgPitch,
630
- fillColor: this.barColor(PitchRollPriorityElement.pitch),
631
- },
632
- ]}
633
- .needles=${[
634
- {
635
- angle: this.roll,
636
- fillColor: this.needleColor(PitchRollPriorityElement.roll),
637
- strokeColor: 'var(--border-silhouette-color)',
638
- },
639
- {
640
- angle: 180 + this.roll,
641
- fillColor: this.needleColor(PitchRollPriorityElement.roll),
642
- strokeColor: 'var(--border-silhouette-color)',
643
- },
644
- {
645
- angle: 90 + this.pitch,
646
- fillColor: this.needleColor(PitchRollPriorityElement.pitch),
647
- strokeColor: 'var(--border-silhouette-color)',
648
- },
649
- {
650
- angle: 270 + this.pitch,
651
- fillColor: this.needleColor(PitchRollPriorityElement.pitch),
652
- strokeColor: 'var(--border-silhouette-color)',
653
- },
654
- ]}
778
+ .barAreas=${barAreas}
779
+ .needles=${needles}
655
780
  .vessels=${this.hasReadout
656
781
  ? []
657
782
  : [
@@ -666,16 +791,7 @@ export class ObcPitchRoll extends LitElement {
666
791
  transform: `rotate(${this.roll}deg) scale(${this.normalizedScaleForeImage})`,
667
792
  },
668
793
  ]}
669
- .tickmarks=${[
670
- {angle: 0, type: TickmarkType.main},
671
- {angle: 90, type: TickmarkType.main},
672
- {angle: 180, type: TickmarkType.main},
673
- {angle: 270, type: TickmarkType.main},
674
- ...arcTickmarks(0, this.requestedRollArcAngle),
675
- ...arcTickmarks(180, this.requestedRollArcAngle),
676
- ...arcTickmarks(90, this.requestedPitchArcAngle),
677
- ...arcTickmarks(270, this.requestedPitchArcAngle),
678
- ]}
794
+ .tickmarks=${tickmarks}
679
795
  .advices=${this.advices}
680
796
  ></obc-watch>
681
797
  `;
@@ -705,20 +821,22 @@ export class ObcPitchRoll extends LitElement {
705
821
  state: state,
706
822
  hideMaxTickmark: true,
707
823
  });
708
- advices.push({
709
- minAngle: 270 - outer,
710
- maxAngle: 270 - inner,
711
- type: AdviceType.caution,
712
- state: state,
713
- hideMinTickmark: true,
714
- });
715
- advices.push({
716
- minAngle: 270 + inner,
717
- maxAngle: 270 + outer,
718
- type: AdviceType.caution,
719
- state: state,
720
- hideMaxTickmark: true,
721
- });
824
+ if (!this.isSingleScale) {
825
+ advices.push({
826
+ minAngle: 270 - outer,
827
+ maxAngle: 270 - inner,
828
+ type: AdviceType.caution,
829
+ state: state,
830
+ hideMinTickmark: true,
831
+ });
832
+ advices.push({
833
+ minAngle: 270 + inner,
834
+ maxAngle: 270 + outer,
835
+ type: AdviceType.caution,
836
+ state: state,
837
+ hideMaxTickmark: true,
838
+ });
839
+ }
722
840
  }
723
841
  if (this.maxRollAdvice !== undefined) {
724
842
  const outer = Math.min(rollReq, 45);
@@ -726,20 +844,6 @@ export class ObcPitchRoll extends LitElement {
726
844
  const state = this.triggerRollAdvice
727
845
  ? AdviceState.triggered
728
846
  : AdviceState.regular;
729
- advices.push({
730
- minAngle: -outer,
731
- maxAngle: -inner,
732
- type: AdviceType.caution,
733
- state: state,
734
- hideMinTickmark: true,
735
- });
736
- advices.push({
737
- minAngle: inner,
738
- maxAngle: outer,
739
- type: AdviceType.caution,
740
- state: state,
741
- hideMaxTickmark: true,
742
- });
743
847
  advices.push({
744
848
  minAngle: 180 - outer,
745
849
  maxAngle: 180 - inner,
@@ -754,6 +858,22 @@ export class ObcPitchRoll extends LitElement {
754
858
  state: state,
755
859
  hideMaxTickmark: true,
756
860
  });
861
+ if (!this.isSingleScale) {
862
+ advices.push({
863
+ minAngle: -outer,
864
+ maxAngle: -inner,
865
+ type: AdviceType.caution,
866
+ state: state,
867
+ hideMinTickmark: true,
868
+ });
869
+ advices.push({
870
+ minAngle: inner,
871
+ maxAngle: outer,
872
+ type: AdviceType.caution,
873
+ state: state,
874
+ hideMaxTickmark: true,
875
+ });
876
+ }
757
877
  }
758
878
  return advices;
759
879
  }