@oicl/openbridge-webcomponents 0.0.15-dev-20240916083108 → 0.0.15-dev-20240923184809

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. package/.storybook/main.ts +8 -0
  2. package/.storybook/preview.ts +2 -0
  3. package/__snapshots__/building-blocks-watch--advice.png +0 -0
  4. package/__snapshots__/building-blocks-watch-flat--primary.png +0 -0
  5. package/__snapshots__/navigation-instruments-azimuth-thruster--in-command-at-setpoint-disable-auto-setpoint.png +0 -0
  6. package/__snapshots__/navigation-instruments-azimuth-thruster--in-command-at-setpoint.png +0 -0
  7. package/__snapshots__/navigation-instruments-azimuth-thruster--in-command.png +0 -0
  8. package/__snapshots__/navigation-instruments-azimuth-thruster--pod.png +0 -0
  9. package/__snapshots__/navigation-instruments-azimuth-thruster--single-direction-with-propeller.png +0 -0
  10. package/__snapshots__/navigation-instruments-azimuth-thruster--single-direction.png +0 -0
  11. package/__snapshots__/navigation-instruments-azimuth-thruster-labeled--large.png +0 -0
  12. package/__snapshots__/navigation-instruments-azimuth-thruster-labeled--medium.png +0 -0
  13. package/__snapshots__/navigation-instruments-azimuth-thruster-labeled--no-command.png +0 -0
  14. package/__snapshots__/navigation-instruments-compass--primary.png +0 -0
  15. package/__snapshots__/navigation-instruments-compass-flat--primary.png +0 -0
  16. package/__snapshots__/navigation-instruments-compass-flat--with-fov-indicator.png +0 -0
  17. package/__snapshots__/navigation-instruments-main-engine--active.png +0 -0
  18. package/__snapshots__/navigation-instruments-main-engine--in-command.png +0 -0
  19. package/__snapshots__/navigation-instruments-main-engine--off.png +0 -0
  20. package/__snapshots__/navigation-instruments-thruster--tunnel.png +0 -0
  21. package/custom-elements.json +2184 -660
  22. package/dist/navigation-instruments/azimuth-thruster/azimuth-thruster.d.ts.map +1 -1
  23. package/dist/navigation-instruments/azimuth-thruster/azimuth-thruster.js +2 -1
  24. package/dist/navigation-instruments/azimuth-thruster/azimuth-thruster.js.map +1 -1
  25. package/dist/navigation-instruments/compass/arrow.d.ts +7 -0
  26. package/dist/navigation-instruments/compass/arrow.d.ts.map +1 -0
  27. package/dist/navigation-instruments/compass/arrow.js +59 -0
  28. package/dist/navigation-instruments/compass/arrow.js.map +1 -0
  29. package/dist/navigation-instruments/compass/compass.d.ts +23 -0
  30. package/dist/navigation-instruments/compass/compass.d.ts.map +1 -0
  31. package/dist/navigation-instruments/compass/compass.js +139 -0
  32. package/dist/navigation-instruments/compass/compass.js.map +1 -0
  33. package/dist/navigation-instruments/compass/radial-tickmark.d.ts +4 -0
  34. package/dist/navigation-instruments/compass/radial-tickmark.d.ts.map +1 -0
  35. package/dist/navigation-instruments/compass/radial-tickmark.js +69 -0
  36. package/dist/navigation-instruments/compass/radial-tickmark.js.map +1 -0
  37. package/dist/navigation-instruments/compass-flat/compass-flat.css.js +29 -0
  38. package/dist/navigation-instruments/compass-flat/compass-flat.css.js.map +1 -0
  39. package/dist/navigation-instruments/compass-flat/compass-flat.d.ts +45 -0
  40. package/dist/navigation-instruments/compass-flat/compass-flat.d.ts.map +1 -0
  41. package/dist/navigation-instruments/compass-flat/compass-flat.js +223 -0
  42. package/dist/navigation-instruments/compass-flat/compass-flat.js.map +1 -0
  43. package/dist/navigation-instruments/main-engine/main-engine.css.js +22 -0
  44. package/dist/navigation-instruments/main-engine/main-engine.css.js.map +1 -0
  45. package/dist/navigation-instruments/main-engine/main-engine.d.ts +29 -0
  46. package/dist/navigation-instruments/main-engine/main-engine.d.ts.map +1 -0
  47. package/dist/navigation-instruments/main-engine/main-engine.js +196 -0
  48. package/dist/navigation-instruments/main-engine/main-engine.js.map +1 -0
  49. package/dist/navigation-instruments/thruster/advice.d.ts.map +1 -1
  50. package/dist/navigation-instruments/thruster/advice.js +9 -5
  51. package/dist/navigation-instruments/thruster/advice.js.map +1 -1
  52. package/dist/navigation-instruments/thruster/thruster.d.ts +54 -1
  53. package/dist/navigation-instruments/thruster/thruster.d.ts.map +1 -1
  54. package/dist/navigation-instruments/thruster/thruster.js +163 -99
  55. package/dist/navigation-instruments/thruster/thruster.js.map +1 -1
  56. package/dist/navigation-instruments/watch/advice.js +1 -1
  57. package/dist/navigation-instruments/watch/advice.js.map +1 -1
  58. package/dist/navigation-instruments/watch/label.d.ts +3 -0
  59. package/dist/navigation-instruments/watch/label.d.ts.map +1 -0
  60. package/dist/navigation-instruments/watch/label.js +68 -0
  61. package/dist/navigation-instruments/watch/label.js.map +1 -0
  62. package/dist/navigation-instruments/watch/watch.css.js +15 -14
  63. package/dist/navigation-instruments/watch/watch.css.js.map +1 -1
  64. package/dist/navigation-instruments/watch/watch.d.ts +3 -0
  65. package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
  66. package/dist/navigation-instruments/watch/watch.js +34 -1
  67. package/dist/navigation-instruments/watch/watch.js.map +1 -1
  68. package/dist/navigation-instruments/watch-flat/tickmark-flat.d.ts +20 -0
  69. package/dist/navigation-instruments/watch-flat/tickmark-flat.d.ts.map +1 -0
  70. package/dist/navigation-instruments/watch-flat/tickmark-flat.js +53 -0
  71. package/dist/navigation-instruments/watch-flat/tickmark-flat.js.map +1 -0
  72. package/dist/navigation-instruments/watch-flat/watch-flat.css.js +32 -0
  73. package/dist/navigation-instruments/watch-flat/watch-flat.css.js.map +1 -0
  74. package/dist/navigation-instruments/watch-flat/watch-flat.d.ts +29 -0
  75. package/dist/navigation-instruments/watch-flat/watch-flat.d.ts.map +1 -0
  76. package/dist/navigation-instruments/watch-flat/watch-flat.js +184 -0
  77. package/dist/navigation-instruments/watch-flat/watch-flat.js.map +1 -0
  78. package/dist/svghelpers/rectangular.d.ts +1 -0
  79. package/dist/svghelpers/rectangular.d.ts.map +1 -1
  80. package/dist/svghelpers/rectangular.js +3 -2
  81. package/dist/svghelpers/rectangular.js.map +1 -1
  82. package/package.json +16 -11
  83. package/src/navigation-instruments/azimuth-thruster/azimuth-thruster.ts +1 -0
  84. package/src/navigation-instruments/compass/arrow.ts +61 -0
  85. package/src/navigation-instruments/compass/compass.stories.ts +37 -0
  86. package/src/navigation-instruments/compass/compass.ts +132 -0
  87. package/src/navigation-instruments/compass/radial-tickmark.ts +77 -0
  88. package/src/navigation-instruments/compass-flat/compass-flat.css +23 -0
  89. package/src/navigation-instruments/compass-flat/compass-flat.stories.ts +35 -0
  90. package/src/navigation-instruments/compass-flat/compass-flat.ts +221 -0
  91. package/src/navigation-instruments/main-engine/main-engine.css +17 -0
  92. package/src/navigation-instruments/main-engine/main-engine.stories.ts +54 -0
  93. package/src/navigation-instruments/main-engine/main-engine.ts +160 -0
  94. package/src/navigation-instruments/thruster/advice.ts +9 -5
  95. package/src/navigation-instruments/thruster/thruster.stories.ts +1 -0
  96. package/src/navigation-instruments/thruster/thruster.ts +205 -113
  97. package/src/navigation-instruments/watch/advice.ts +1 -1
  98. package/src/navigation-instruments/watch/label.ts +69 -0
  99. package/src/navigation-instruments/watch/watch.css +7 -7
  100. package/src/navigation-instruments/watch/watch.ts +30 -1
  101. package/src/navigation-instruments/watch-flat/tickmark-flat.ts +62 -0
  102. package/src/navigation-instruments/watch-flat/watch-flat.css +19 -0
  103. package/src/navigation-instruments/watch-flat/watch-flat.stories.ts +17 -0
  104. package/src/navigation-instruments/watch-flat/watch-flat.ts +148 -0
  105. package/src/palettes/variables.css +1343 -1343
  106. package/src/svghelpers/rectangular.ts +6 -3
@@ -0,0 +1,221 @@
1
+ import {LitElement, svg, SVGTemplateResult, unsafeCSS} from 'lit';
2
+ import componentStyle from './compass-flat.css?inline';
3
+ import {customElement, property} from 'lit/decorators.js';
4
+ import {Tickmark, TickmarkType} from '../watch-flat/tickmark-flat';
5
+ import '../watch-flat/watch-flat';
6
+
7
+ export enum LabelPosition {
8
+ top = -45,
9
+ bottom = 50,
10
+ }
11
+
12
+ export enum LabelStyle {
13
+ regular = 'var(--instrument-tick-mark-secondary-color)',
14
+ }
15
+
16
+ export interface Label {
17
+ x: number;
18
+ y: LabelPosition;
19
+ text: string;
20
+ }
21
+
22
+ @customElement('obc-compass-flat')
23
+ export class ObcCompassFlat extends LitElement {
24
+ @property({type: Boolean}) noPadding: boolean = true;
25
+ @property({type: Boolean}) FOVIndicator: boolean = false;
26
+ @property({type: Number}) padding: number = 16;
27
+ @property({type: Number}) heading = 0;
28
+ @property({type: Number}) courseOverGround = 0;
29
+ @property({type: Number}) tickInterval = 5;
30
+ @property({type: Number}) FOV = 45;
31
+ @property({type: Number}) minFOV = 45;
32
+ @property({type: Number}) maxFOV = 180;
33
+ labels: Label[] = [];
34
+
35
+ @property({type: Number}) containerWidth = 0;
36
+
37
+ private resizeObserver: ResizeObserver = new ResizeObserver((entries) => {
38
+ for (const entry of entries) {
39
+ this.containerWidth = entry.contentRect.width;
40
+ this.updateLabels();
41
+ }
42
+ });
43
+
44
+ override connectedCallback() {
45
+ super.connectedCallback();
46
+ this.resizeObserver.observe(this);
47
+ }
48
+
49
+ override disconnectedCallback() {
50
+ super.disconnectedCallback();
51
+ this.resizeObserver.unobserve(this);
52
+ }
53
+
54
+ private updateLabels() {
55
+ if (this.containerWidth < 192) {
56
+ this.labels = [];
57
+ } else if (this.containerWidth <= 300) {
58
+ this.labels = [
59
+ {x: -180, y: LabelPosition.top, text: 'S'},
60
+ {x: -90, y: LabelPosition.top, text: 'W'},
61
+ {x: 0, y: LabelPosition.top, text: 'N'},
62
+ {x: 90, y: LabelPosition.top, text: 'E'},
63
+ {x: 180, y: LabelPosition.top, text: 'S'},
64
+ {x: 270, y: LabelPosition.top, text: 'W'},
65
+ {x: 360, y: LabelPosition.top, text: 'N'},
66
+ {x: 450, y: LabelPosition.top, text: 'E'},
67
+ {x: 540, y: LabelPosition.top, text: 'S'},
68
+ ];
69
+ } else {
70
+ this.labels = [
71
+ {x: -180, y: LabelPosition.top, text: 'S'},
72
+ {x: -135, y: LabelPosition.top, text: 'SW'},
73
+ {x: -90, y: LabelPosition.top, text: 'W'},
74
+ {x: -45, y: LabelPosition.top, text: 'NW'},
75
+ {x: 0, y: LabelPosition.top, text: 'N'},
76
+ {x: 45, y: LabelPosition.top, text: 'NE'},
77
+ {x: 90, y: LabelPosition.top, text: 'E'},
78
+ {x: 135, y: LabelPosition.top, text: 'SE'},
79
+ {x: 180, y: LabelPosition.top, text: 'S'},
80
+ {x: 225, y: LabelPosition.top, text: 'SW'},
81
+ {x: 270, y: LabelPosition.top, text: 'W'},
82
+ {x: 315, y: LabelPosition.top, text: 'NW'},
83
+ {x: 360, y: LabelPosition.top, text: 'N'},
84
+ {x: 405, y: LabelPosition.top, text: 'NE'},
85
+ {x: 450, y: LabelPosition.top, text: 'E'},
86
+ {x: 495, y: LabelPosition.top, text: 'SE'},
87
+ {x: 540, y: LabelPosition.top, text: 'S'},
88
+ ];
89
+ }
90
+ }
91
+
92
+ private generateIntervalTickmarks(scale: number): Tickmark[] {
93
+ const tickmarks: Tickmark[] = [];
94
+ let cardinalInterval = 90;
95
+
96
+ if (this.containerWidth > 300) {
97
+ cardinalInterval = 45;
98
+ } else if (this.containerWidth < 192) {
99
+ cardinalInterval = 0;
100
+ }
101
+
102
+ for (
103
+ let angle = -180;
104
+ angle < this.maxFOV * 3;
105
+ angle += this.tickInterval
106
+ ) {
107
+ if (cardinalInterval !== 0 && angle % cardinalInterval === 0) {
108
+ continue;
109
+ }
110
+ tickmarks.push({angle: angle * scale, type: TickmarkType.secondary});
111
+ }
112
+
113
+ return tickmarks;
114
+ }
115
+
116
+ private generateCardinalTickmarks(scale: number): Tickmark[] {
117
+ const tickmarks: Tickmark[] = [];
118
+
119
+ for (const label of this.labels) {
120
+ tickmarks.push({angle: label.x * scale, type: TickmarkType.main});
121
+ }
122
+
123
+ return tickmarks;
124
+ }
125
+
126
+ private generateTickmarks(scale: number): Tickmark[] {
127
+ return [
128
+ ...this.generateCardinalTickmarks(scale),
129
+ ...this.generateIntervalTickmarks(scale),
130
+ ];
131
+ }
132
+
133
+ private renderFOVIndicator(): SVGTemplateResult[] {
134
+ const indicators: SVGTemplateResult[] = [];
135
+
136
+ const maxAdjustment = 10;
137
+ const minContainerWidth = 300;
138
+ const maxContainerWidth = 512;
139
+
140
+ let yAdjustment = 0;
141
+ if (this.containerWidth < maxContainerWidth) {
142
+ const widthRange = maxContainerWidth - minContainerWidth;
143
+ const scaleFactor =
144
+ (maxContainerWidth - this.containerWidth) / widthRange;
145
+ yAdjustment = scaleFactor * maxAdjustment;
146
+ }
147
+
148
+ const y = LabelPosition.bottom + yAdjustment;
149
+
150
+ indicators.push(svg`
151
+ <text x="-175" y=${y} class="label left" fill=${LabelStyle.regular}>
152
+ ${-this.FOV}\u00B0
153
+ </text>`);
154
+
155
+ indicators.push(svg`
156
+ <text x="0" y=${y} class="label" fill=${LabelStyle.regular}>
157
+ ${this.heading}\u00B0
158
+ </text>`);
159
+
160
+ indicators.push(svg`
161
+ <text x="175" y=${y} class="label right" fill=${LabelStyle.regular}>
162
+ ${this.FOV}\u00B0
163
+ </text>`);
164
+
165
+ return indicators;
166
+ }
167
+
168
+ private get HDGSvg(): SVGTemplateResult {
169
+ return svg`<g transform="translate(-24, -74)">
170
+ <path d="M36.7011 44.1445L36.6898 44.1379L36.6781 44.1318L24.2301 37.6823L24.0001 37.5631L23.7701 37.6823L11.3221 44.1318L11.3104 44.1379L11.2991 44.1445C9.25497 45.3438 6.78661 43.308 7.68828 41.0919L22.6036 4.43285C23.1096 3.18905 24.8906 3.18905 25.3967 4.43284L40.3119 41.0919C41.2136 43.308 38.7452 45.3438 36.7011 44.1445Z" fill="var(--instrument-enhanced-secondary-color)" stroke="var(--border-silhouette-color)"/>
171
+ </g>`;
172
+ }
173
+
174
+ private COGSvg(translation: number): SVGTemplateResult {
175
+ return svg`
176
+ <g transform="translate(${-24 + translation}, -74)">
177
+ <path d="M31.9025 36.0262L33.1068 36.6502L32.5956 35.3938L24.4632 15.406L24.0001 14.2677L23.537 15.406L15.4046 35.3938L14.8935 36.6502L16.0978 36.0262L24.0001 31.9319L31.9025 36.0262ZM36.7011 44.1445L36.6898 44.1379L36.6781 44.1318L24.2301 37.6823L24.0001 37.5631L23.7701 37.6823L11.3221 44.1318L11.3104 44.1379L11.2991 44.1445C9.25497 45.3438 6.78661 43.308 7.68828 41.0919L22.6036 4.43285C23.1096 3.18905 24.8906 3.18905 25.3967 4.43284L40.3119 41.0919C41.2136 43.308 38.7452 45.3438 36.7011 44.1445Z" fill="var(--instrument-enhanced-secondary-color)" stroke="var(--border-silhouette-color)"/>
178
+ </g>
179
+ `;
180
+ }
181
+
182
+ override render() {
183
+ let angleDiff = this.courseOverGround - this.heading;
184
+
185
+ if (angleDiff > this.maxFOV) {
186
+ angleDiff -= 360;
187
+ } else if (angleDiff < -this.maxFOV) {
188
+ angleDiff += 360;
189
+ }
190
+
191
+ this.FOV = Math.max(this.minFOV, Math.abs(angleDiff));
192
+
193
+ const baseOffset = 5;
194
+ const translationScale = (baseOffset * 35) / this.FOV;
195
+
196
+ const translation = angleDiff * translationScale;
197
+
198
+ const tickmarks = this.generateTickmarks(translationScale);
199
+ this.labels.map((l) => {
200
+ l.x = l.x * translationScale;
201
+ });
202
+
203
+ const viewBox = this.noPadding ? '-192 -128 384 128' : '-200 -144 400 144';
204
+
205
+ return svg`
206
+ <div class="container">
207
+ <obc-watch-flat .noPadding=${this.noPadding} .FOVIndicator=${this.FOVIndicator ? this.renderFOVIndicator() : []} .labels=${this.labels} .rotation=${this.heading} .tickmarks=${tickmarks} .tickmarkSpacing=${translationScale}></obc-watch-flat>
208
+ <svg viewBox=${viewBox} xmlns="http://www.w3.org/2000/svg">
209
+ ${this.HDGSvg}${this.COGSvg(translation)}
210
+ </div>
211
+ `;
212
+ }
213
+
214
+ static override styles = unsafeCSS(componentStyle);
215
+ }
216
+
217
+ declare global {
218
+ interface HTMLElementTagNameMap {
219
+ 'obc-compass-flat': ObcCompassFlat;
220
+ }
221
+ }
@@ -0,0 +1,17 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ }
4
+
5
+ .container {
6
+ position: relative;
7
+ width: 100%;
8
+ height: 100%;
9
+ }
10
+
11
+ .container>* {
12
+ position: absolute;
13
+ top: 0;
14
+ left: 0;
15
+ width: 100%;
16
+ height: 100%;
17
+ }
@@ -0,0 +1,54 @@
1
+ import type {Meta, StoryObj} from '@storybook/web-components';
2
+ import {ObcMainEngine} from './main-engine';
3
+ import './main-engine';
4
+ import {beta6Decorator, widthDecorator} from '../../storybook-util';
5
+ import {InstrumentState} from '../types';
6
+
7
+ const meta: Meta<typeof ObcMainEngine> = {
8
+ title: 'Navigation instruments/Main Engine',
9
+ tags: ['autodocs'],
10
+ component: 'obc-main-engine',
11
+ args: {
12
+ width: 352,
13
+ thrust: 50,
14
+ thrustSetpoint: 30,
15
+ speed: 50,
16
+ speedSetpoint: 30,
17
+ },
18
+ argTypes: {
19
+ width: {control: {type: 'range', min: 32, max: 1028, step: 1}},
20
+ thrust: {control: {type: 'range', min: -100, max: 100, step: 1}},
21
+ thrustSetpoint: {control: {type: 'range', min: -100, max: 100, step: 1}},
22
+ speed: {control: {type: 'range', min: 0, max: 100, step: 1}},
23
+ speedSetpoint: {control: {type: 'range', min: 0, max: 100, step: 1}},
24
+ state: {
25
+ options: Object.values(InstrumentState),
26
+ },
27
+ },
28
+ decorators: [widthDecorator, beta6Decorator],
29
+ } satisfies Meta<ObcMainEngine>;
30
+
31
+ export default meta;
32
+ type Story = StoryObj<ObcMainEngine>;
33
+
34
+ export const InCommand: Story = {
35
+ args: {
36
+ state: InstrumentState.inCommand,
37
+ },
38
+ };
39
+
40
+ export const Active: Story = {
41
+ args: {
42
+ state: InstrumentState.active,
43
+ },
44
+ };
45
+
46
+ export const Off: Story = {
47
+ args: {
48
+ state: InstrumentState.off,
49
+ speed: 0,
50
+ thrust: 0,
51
+ speedSetpoint: 0,
52
+ thrustSetpoint: 0,
53
+ },
54
+ };
@@ -0,0 +1,160 @@
1
+ import {LitElement, html, unsafeCSS, svg, nothing} from 'lit';
2
+ import {customElement, property} from 'lit/decorators.js';
3
+ import compentStyle from './main-engine.css?inline';
4
+ import {InstrumentState} from '../types';
5
+ import {
6
+ atSetpoint,
7
+ convertThrustAdvices,
8
+ thrusterColors,
9
+ thrusterTopSingleSided,
10
+ setpointSvg,
11
+ } from '../thruster/thruster';
12
+ import {LinearAdvice} from '../thruster/advice';
13
+
14
+ @customElement('obc-main-engine')
15
+ export class ObcMainEngine extends LitElement {
16
+ @property({type: Number}) thrust: number = 0;
17
+ @property({type: Number}) thrustSetpoint: number | undefined;
18
+ @property({type: Boolean}) thrustTouching: boolean = false;
19
+ @property({type: Boolean}) atThrustSetpoint: boolean = false;
20
+ @property({type: Number}) speed: number = 0;
21
+ @property({type: Number}) speedSetpoint: number | undefined;
22
+ @property({type: Boolean}) speedTouching: boolean = false;
23
+ @property({type: Boolean}) atSpeedSetpoint: boolean = false;
24
+ @property({type: Boolean}) disableAutoAtThrustSetpoint: boolean = false;
25
+ @property({type: Boolean}) disableAutoAtSpeedSetpoint: boolean = false;
26
+ @property({type: Number}) autoAtThrustSetpointDeadband: number = 1;
27
+ @property({type: Number}) autoAtSpeedSetpointDeadband: number = 1;
28
+ @property({type: Number}) thrustSetpointAtZeroDeadband: number = 0.5;
29
+ @property({type: Number}) speedSetpointAtZeroDeadband: number = 0.5;
30
+ @property({type: String}) state: InstrumentState = InstrumentState.inCommand;
31
+ @property({type: Array}) thrustAdvices: LinearAdvice[] = [];
32
+
33
+ override render() {
34
+ const thrustSetpointAtZero =
35
+ Math.abs(this.thrustSetpoint || 0) < this.thrustSetpointAtZeroDeadband;
36
+ const speedSetpointAtZero =
37
+ Math.abs(this.speedSetpoint || 0) < this.speedSetpointAtZeroDeadband;
38
+ const thrustAtSetpoint = atSetpoint(this.thrust, this.thrustSetpoint, {
39
+ atSetpoint: this.atThrustSetpoint,
40
+ autoAtSetpoint: !this.disableAutoAtThrustSetpoint,
41
+ autoSetpointDeadband: this.autoAtThrustSetpointDeadband,
42
+ touching: this.thrustTouching,
43
+ });
44
+ const cThrust = thrusterColors(
45
+ {
46
+ atSetpoint: thrustAtSetpoint,
47
+ touching: this.thrustTouching,
48
+ },
49
+ this.state
50
+ );
51
+ const speedAtSetpoint = atSetpoint(this.speed, this.speedSetpoint, {
52
+ atSetpoint: this.atSpeedSetpoint,
53
+ autoAtSetpoint: !this.disableAutoAtSpeedSetpoint,
54
+ autoSetpointDeadband: this.autoAtSpeedSetpointDeadband,
55
+ touching: this.speedTouching,
56
+ });
57
+ const cSpeed = thrusterColors(
58
+ {
59
+ atSetpoint: speedAtSetpoint,
60
+ touching: this.speedTouching,
61
+ },
62
+ this.state
63
+ );
64
+ const container = svg`<rect x="-80" y="-176" width="160" height="352" fill="var(--instrument-frame-primary-color)" stroke="var(--instrument-frame-tertiary-color)" rx="8"/>`;
65
+ const border = svg`<rect x="-80" y="-176" width="160" height="352" fill="none" stroke="var(--instrument-frame-tertiary-color)" rx="8" vector-effect="non-scaling-stroke"/>`;
66
+ const frameLeft = svg`<rect x="-56" y="-176" width="48" height="352" fill="var(--instrument-frame-secondary-color)" vector-effect="non-scaling-stroke" stroke="var(--instrument-frame-secondary-color)"/>`;
67
+ const frameRight = svg`<rect x="8" y="-176" width="48" height="352" fill="var(--instrument-frame-secondary-color)" vector-effect="non-scaling-stroke" stroke="var(--instrument-frame-secondary-color)"/>`;
68
+ const thrustCenter = svg`<rect x="8" y="-2" height="4" width="72" fill="${cThrust.zeroLineColor}" stroke=${cThrust.zeroLineColor} vector-effect="non-scaling-stroke"/>`;
69
+ const {topAdvices: topThrustAdvice, bottomAdvices: bottomThrustAdvice} =
70
+ convertThrustAdvices(this.thrustAdvices, this.thrust);
71
+ const thrustTop = svg`<g transform="translate(44, 0)">
72
+ ${thrusterTopSingleSided(
73
+ 174,
74
+ Math.max(this.thrust, 0),
75
+ {box: cThrust.boxColor, container: ''},
76
+ {
77
+ hideContainer: true,
78
+ hideTicks: cThrust.hideTicks,
79
+ flipAdicePattern: false,
80
+ narrow: false,
81
+ },
82
+ topThrustAdvice
83
+ )}</g>`;
84
+ const thrusterBottom = svg`<g transform="rotate(180) scale(-1,1) translate(44)">
85
+ ${thrusterTopSingleSided(
86
+ 174,
87
+ Math.max(-this.thrust, 0),
88
+ {box: cThrust.boxColor, container: ''},
89
+ {
90
+ hideContainer: true,
91
+ hideTicks: cThrust.hideTicks,
92
+ flipAdicePattern: false,
93
+ narrow: false,
94
+ },
95
+ bottomThrustAdvice
96
+ )}</g>`;
97
+ const thrustSetpoint =
98
+ this.thrustSetpoint !== undefined
99
+ ? svg`<g transform="translate(44, 0)">${setpointSvg(
100
+ 174,
101
+ this.thrustSetpoint,
102
+ thrustSetpointAtZero,
103
+ {
104
+ fill: cThrust.setPointColor,
105
+ stroke: 'var(--border-silhouette-color)',
106
+ },
107
+ {
108
+ inCommand: this.state === InstrumentState.inCommand,
109
+ singleSided: true,
110
+ narrow: false,
111
+ }
112
+ )}</g>`
113
+ : nothing;
114
+
115
+ const speedHeight = 352 * (this.speed / 100) + 2;
116
+ const speedY = 176 - speedHeight;
117
+ const speedBoxColor =
118
+ this.state === InstrumentState.inCommand
119
+ ? 'var(--instrument-enhanced-tertiary-color)'
120
+ : 'var(--instrument-regular-tertiary-color)';
121
+ const speedBox = svg`<rect x="-56" y=${speedY} width="48" height=${speedHeight} fill=${speedBoxColor} stroke=${speedBoxColor} vector-effect="non-scaling-stroke">`;
122
+ const speedLine = svg`<rect x="-56" y=${speedY - 2} width="48" height="4" rx="2" fill=${cSpeed.boxColor} stroke=${cSpeed.boxColor}/>
123
+ `;
124
+ const speedSetpoint =
125
+ this.speedSetpoint !== undefined
126
+ ? svg`<g transform="scale(-1 1) translate(44, 176)">${setpointSvg(
127
+ 350,
128
+ this.speedSetpoint,
129
+ speedSetpointAtZero,
130
+ {
131
+ fill: cSpeed.setPointColor,
132
+ stroke: 'var(--border-silhouette-color)',
133
+ },
134
+ {
135
+ inCommand: this.state === InstrumentState.inCommand,
136
+ singleSided: true,
137
+ narrow: false,
138
+ }
139
+ )}</g>`
140
+ : nothing;
141
+
142
+ return html`
143
+ <div class="container">
144
+ <svg viewbox="-100 -200 200 400">
145
+ ${container} ${frameLeft} ${frameRight} ${thrustCenter} ${thrustTop}
146
+ ${thrusterBottom} ${speedBox} ${speedLine} ${border} ${thrustSetpoint}
147
+ ${speedSetpoint}
148
+ </svg>
149
+ </div>
150
+ `;
151
+ }
152
+
153
+ static override styles = unsafeCSS(compentStyle);
154
+ }
155
+
156
+ declare global {
157
+ interface HTMLElementTagNameMap {
158
+ 'obc-main-engine': ObcMainEngine;
159
+ }
160
+ }
@@ -84,20 +84,24 @@ export function renderAdvice(
84
84
  ${singleSidedTickmark(height, advice.max, tickmarkStyle)}
85
85
  `;
86
86
  } else {
87
- let mainColor;
87
+ let strokeColor;
88
88
  let tickmarkStyle;
89
+ let fillColor: string;
89
90
  if (advice.state === AdviceState.hinted) {
90
- mainColor = 'var(--instrument-frame-tertiary-color)';
91
+ strokeColor = 'var(--instrument-frame-tertiary-color)';
92
+ fillColor = 'none';
91
93
  tickmarkStyle = TickmarkStyle.hinted;
92
94
  } else if (advice.state === AdviceState.regular) {
93
- mainColor = 'var(--instrument-regular-secondary-color)';
95
+ strokeColor = 'var(--instrument-regular-secondary-color)';
96
+ fillColor = 'none';
94
97
  tickmarkStyle = TickmarkStyle.regular;
95
98
  } else {
96
- mainColor = 'var(--instrument-enhanced-secondary-color)';
99
+ strokeColor = 'var(--instrument-enhanced-secondary-color)';
100
+ fillColor = strokeColor;
97
101
  tickmarkStyle = TickmarkStyle.regular;
98
102
  }
99
103
  return svg`
100
- ${adviceMask(height, advice.min, advice.max, mainColor, mainColor)}
104
+ ${adviceMask(height, advice.min, advice.max, fillColor, strokeColor)}
101
105
  ${singleSidedTickmark(height, advice.min, tickmarkStyle)}
102
106
  ${singleSidedTickmark(height, advice.max, tickmarkStyle)}
103
107
  `;
@@ -12,6 +12,7 @@ const meta: Meta<typeof ObcThruster> = {
12
12
  component: 'obc-thruster',
13
13
  args: {width: 320},
14
14
  argTypes: {
15
+ width: {control: {type: 'range', min: 32, max: 1028, step: 1}},
15
16
  thrust: {control: {type: 'range', min: -100, max: 100, step: 1}},
16
17
  setpoint: {control: {type: 'range', min: -100, max: 100, step: 1}},
17
18
  state: {