@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.110 → 2.0.0-next.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/openbridge-webcomponents.bundle.js +2370 -1522
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +641 -7
- package/dist/automation/indicator-battery/indicator-battery.css.js +52 -0
- package/dist/automation/indicator-battery/indicator-battery.css.js.map +1 -0
- package/dist/automation/indicator-battery/indicator-battery.d.ts +65 -0
- package/dist/automation/indicator-battery/indicator-battery.d.ts.map +1 -0
- package/dist/automation/indicator-battery/indicator-battery.js +119 -0
- package/dist/automation/indicator-battery/indicator-battery.js.map +1 -0
- package/dist/automation/indicator-engine/indicator-engine.css.js +45 -0
- package/dist/automation/indicator-engine/indicator-engine.css.js.map +1 -0
- package/dist/automation/indicator-engine/indicator-engine.d.ts +34 -0
- package/dist/automation/indicator-engine/indicator-engine.d.ts.map +1 -0
- package/dist/automation/indicator-engine/indicator-engine.js +73 -0
- package/dist/automation/indicator-engine/indicator-engine.js.map +1 -0
- package/dist/automation/indicator-generator/indicator-generator-geometry.d.ts +30 -0
- package/dist/automation/indicator-generator/indicator-generator-geometry.d.ts.map +1 -0
- package/dist/automation/indicator-generator/indicator-generator-geometry.js +61 -0
- package/dist/automation/indicator-generator/indicator-generator-geometry.js.map +1 -0
- package/dist/automation/indicator-generator/indicator-generator.css.js +121 -0
- package/dist/automation/indicator-generator/indicator-generator.css.js.map +1 -0
- package/dist/automation/indicator-generator/indicator-generator.d.ts +57 -0
- package/dist/automation/indicator-generator/indicator-generator.d.ts.map +1 -0
- package/dist/automation/indicator-generator/indicator-generator.js +136 -0
- package/dist/automation/indicator-generator/indicator-generator.js.map +1 -0
- package/dist/automation/indicator-shared/linear-indicator.d.ts +46 -0
- package/dist/automation/indicator-shared/linear-indicator.d.ts.map +1 -0
- package/dist/automation/indicator-shared/linear-indicator.js +175 -0
- package/dist/automation/indicator-shared/linear-indicator.js.map +1 -0
- package/dist/automation/indicator-tank-atmospheric/indicator-tank-atmospheric.css.js +48 -0
- package/dist/automation/indicator-tank-atmospheric/indicator-tank-atmospheric.css.js.map +1 -0
- package/dist/automation/indicator-tank-atmospheric/indicator-tank-atmospheric.d.ts +65 -0
- package/dist/automation/indicator-tank-atmospheric/indicator-tank-atmospheric.d.ts.map +1 -0
- package/dist/automation/indicator-tank-atmospheric/indicator-tank-atmospheric.js +105 -0
- package/dist/automation/indicator-tank-atmospheric/indicator-tank-atmospheric.js.map +1 -0
- package/dist/navigation-instruments/compass/compass.d.ts +1 -1
- package/dist/navigation-instruments/compass/compass.d.ts.map +1 -1
- package/dist/navigation-instruments/compass/compass.js +1 -1
- package/dist/navigation-instruments/compass/compass.js.map +1 -1
- package/dist/navigation-instruments/current/current.d.ts +25 -0
- package/dist/navigation-instruments/current/current.d.ts.map +1 -1
- package/dist/navigation-instruments/current/current.js +25 -4
- package/dist/navigation-instruments/current/current.js.map +1 -1
- package/dist/navigation-instruments/heading/heading.js +1 -1
- package/dist/navigation-instruments/heading/heading.js.map +1 -1
- package/dist/navigation-instruments/pitch-roll-heave/pitch-roll-heave.d.ts +6 -0
- package/dist/navigation-instruments/pitch-roll-heave/pitch-roll-heave.d.ts.map +1 -1
- package/dist/navigation-instruments/pitch-roll-heave/pitch-roll-heave.js +8 -1
- package/dist/navigation-instruments/pitch-roll-heave/pitch-roll-heave.js.map +1 -1
- package/dist/navigation-instruments/rate-of-turn/rate-of-turn.d.ts +5 -2
- package/dist/navigation-instruments/rate-of-turn/rate-of-turn.d.ts.map +1 -1
- package/dist/navigation-instruments/rate-of-turn/rate-of-turn.js.map +1 -1
- package/dist/navigation-instruments/top-view-propulsion/top-view-propulsion.d.ts +6 -0
- package/dist/navigation-instruments/top-view-propulsion/top-view-propulsion.d.ts.map +1 -1
- package/dist/navigation-instruments/top-view-propulsion/top-view-propulsion.js +19 -4
- package/dist/navigation-instruments/top-view-propulsion/top-view-propulsion.js.map +1 -1
- package/dist/navigation-instruments/watch/environment.d.ts +8 -0
- package/dist/navigation-instruments/watch/environment.d.ts.map +1 -1
- package/dist/navigation-instruments/watch/environment.js +3 -0
- package/dist/navigation-instruments/watch/environment.js.map +1 -1
- package/dist/navigation-instruments/watch/force-pattern.d.ts +20 -2
- package/dist/navigation-instruments/watch/force-pattern.d.ts.map +1 -1
- package/dist/navigation-instruments/watch/force-pattern.js +12 -5
- package/dist/navigation-instruments/watch/force-pattern.js.map +1 -1
- package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
- package/dist/navigation-instruments/watch/watch.js +6 -4
- package/dist/navigation-instruments/watch/watch.js.map +1 -1
- package/dist/navigation-instruments/wind/wind.d.ts +5 -2
- package/dist/navigation-instruments/wind/wind.d.ts.map +1 -1
- package/dist/navigation-instruments/wind/wind.js +5 -1
- package/dist/navigation-instruments/wind/wind.js.map +1 -1
- package/package.json +1 -1
- package/src/automation/indicator-battery/indicator-battery.css +42 -0
- package/src/automation/indicator-battery/indicator-battery.stories.ts +94 -0
- package/src/automation/indicator-battery/indicator-battery.ts +149 -0
- package/src/automation/indicator-engine/indicator-engine.css +35 -0
- package/src/automation/indicator-engine/indicator-engine.stories.ts +48 -0
- package/src/automation/indicator-engine/indicator-engine.ts +78 -0
- package/src/automation/indicator-generator/indicator-generator-geometry.spec.ts +62 -0
- package/src/automation/indicator-generator/indicator-generator-geometry.ts +63 -0
- package/src/automation/indicator-generator/indicator-generator.css +111 -0
- package/src/automation/indicator-generator/indicator-generator.stories.ts +103 -0
- package/src/automation/indicator-generator/indicator-generator.ts +184 -0
- package/src/automation/indicator-shared/linear-indicator.spec.ts +90 -0
- package/src/automation/indicator-shared/linear-indicator.ts +230 -0
- package/src/automation/indicator-tank-atmospheric/indicator-tank-atmospheric.css +38 -0
- package/src/automation/indicator-tank-atmospheric/indicator-tank-atmospheric.stories.ts +94 -0
- package/src/automation/indicator-tank-atmospheric/indicator-tank-atmospheric.ts +138 -0
- package/src/navigation-instruments/compass/compass.ts +2 -2
- package/src/navigation-instruments/current/current.stories.ts +33 -0
- package/src/navigation-instruments/current/current.ts +37 -3
- package/src/navigation-instruments/heading/heading.stories.ts +6 -4
- package/src/navigation-instruments/heading/heading.ts +1 -1
- package/src/navigation-instruments/pitch-roll-heave/pitch-roll-heave.ts +8 -1
- package/src/navigation-instruments/rate-of-turn/rate-of-turn.stories.ts +3 -3
- package/src/navigation-instruments/rate-of-turn/rate-of-turn.ts +5 -2
- package/src/navigation-instruments/top-view-propulsion/top-view-propulsion.ts +20 -2
- package/src/navigation-instruments/watch/environment.ts +10 -0
- package/src/navigation-instruments/watch/force-pattern.spec.ts +42 -0
- package/src/navigation-instruments/watch/force-pattern.ts +44 -7
- package/src/navigation-instruments/watch/watch.ts +20 -5
- package/src/navigation-instruments/wind/wind.spec.ts +3 -1
- package/src/navigation-instruments/wind/wind.ts +12 -3
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type {Meta, StoryObj} from '@storybook/web-components-vite';
|
|
2
|
+
import {html} from 'lit';
|
|
3
|
+
import {
|
|
4
|
+
ObcIndicatorTankAtmospheric,
|
|
5
|
+
IndicatorTankAtmosphericVariant,
|
|
6
|
+
IndicatorTankAtmosphericValue,
|
|
7
|
+
} from './indicator-tank-atmospheric.js';
|
|
8
|
+
import './indicator-tank-atmospheric.js';
|
|
9
|
+
import {IndicatorDirection} from '../indicator-shared/linear-indicator.js';
|
|
10
|
+
|
|
11
|
+
const meta = {
|
|
12
|
+
title: 'Automation/Indicators/Indicator Tank Atmospheric',
|
|
13
|
+
component: 'obc-indicator-tank-atmospheric',
|
|
14
|
+
tags: ['autodocs', '6.0'],
|
|
15
|
+
args: {
|
|
16
|
+
direction: IndicatorDirection.vertical,
|
|
17
|
+
variant: IndicatorTankAtmosphericVariant.bar,
|
|
18
|
+
value: IndicatorTankAtmosphericValue.regular,
|
|
19
|
+
level: 40,
|
|
20
|
+
data: [60, 52, 58, 45, 50, 42, 44, 40],
|
|
21
|
+
hasIcon: false,
|
|
22
|
+
},
|
|
23
|
+
argTypes: {
|
|
24
|
+
direction: {
|
|
25
|
+
control: 'radio',
|
|
26
|
+
options: Object.values(IndicatorDirection),
|
|
27
|
+
},
|
|
28
|
+
variant: {
|
|
29
|
+
control: 'radio',
|
|
30
|
+
options: Object.values(IndicatorTankAtmosphericVariant),
|
|
31
|
+
},
|
|
32
|
+
value: {
|
|
33
|
+
control: 'radio',
|
|
34
|
+
options: Object.values(IndicatorTankAtmosphericValue),
|
|
35
|
+
},
|
|
36
|
+
level: {control: {type: 'range', min: 0, max: 100}},
|
|
37
|
+
},
|
|
38
|
+
render: (args) => html`
|
|
39
|
+
<obc-indicator-tank-atmospheric
|
|
40
|
+
.direction=${args.direction}
|
|
41
|
+
.variant=${args.variant}
|
|
42
|
+
.value=${args.value}
|
|
43
|
+
.level=${args.level}
|
|
44
|
+
.data=${args.data}
|
|
45
|
+
.hasIcon=${args.hasIcon}
|
|
46
|
+
></obc-indicator-tank-atmospheric>
|
|
47
|
+
`,
|
|
48
|
+
} satisfies Meta<Partial<ObcIndicatorTankAtmospheric>>;
|
|
49
|
+
|
|
50
|
+
export default meta;
|
|
51
|
+
type Story = StoryObj<Partial<ObcIndicatorTankAtmospheric>>;
|
|
52
|
+
|
|
53
|
+
export const Default: Story = {};
|
|
54
|
+
|
|
55
|
+
export const BarHorizontal: Story = {
|
|
56
|
+
args: {direction: IndicatorDirection.horizontal},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const TrendVertical: Story = {
|
|
60
|
+
args: {variant: IndicatorTankAtmosphericVariant.trend},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const TrendHorizontal: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
variant: IndicatorTankAtmosphericVariant.trend,
|
|
66
|
+
direction: IndicatorDirection.horizontal,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const Enhanced: Story = {
|
|
71
|
+
args: {value: IndicatorTankAtmosphericValue.enhanced},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const Medium: Story = {
|
|
75
|
+
args: {value: IndicatorTankAtmosphericValue.medium},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const TrendEnhanced: Story = {
|
|
79
|
+
args: {
|
|
80
|
+
variant: IndicatorTankAtmosphericVariant.trend,
|
|
81
|
+
value: IndicatorTankAtmosphericValue.enhanced,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const TrendMedium: Story = {
|
|
86
|
+
args: {
|
|
87
|
+
variant: IndicatorTankAtmosphericVariant.trend,
|
|
88
|
+
value: IndicatorTankAtmosphericValue.medium,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const WithIcon: Story = {
|
|
93
|
+
args: {hasIcon: true},
|
|
94
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {LitElement, html, unsafeCSS, nothing} from 'lit';
|
|
2
|
+
import {property} from 'lit/decorators.js';
|
|
3
|
+
import componentStyle from './indicator-tank-atmospheric.css?inline';
|
|
4
|
+
import {customElement} from '../../decorator.js';
|
|
5
|
+
import '../../icons/icon-tank.js';
|
|
6
|
+
import {
|
|
7
|
+
IndicatorDirection,
|
|
8
|
+
LinearIndicatorScheme,
|
|
9
|
+
Rect,
|
|
10
|
+
renderLinearBar,
|
|
11
|
+
renderTrendGraph,
|
|
12
|
+
} from '../indicator-shared/linear-indicator.js';
|
|
13
|
+
|
|
14
|
+
export enum IndicatorTankAtmosphericVariant {
|
|
15
|
+
bar = 'bar',
|
|
16
|
+
trend = 'trend',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export enum IndicatorTankAtmosphericValue {
|
|
20
|
+
regular = 'regular',
|
|
21
|
+
enhanced = 'enhanced',
|
|
22
|
+
medium = 'medium',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const HORIZONTAL_BAR: Rect = {x: 8, y: 11, width: 32, height: 26};
|
|
26
|
+
const VERTICAL_BAR: Rect = {x: 11, y: 8, width: 26, height: 32};
|
|
27
|
+
const HORIZONTAL_TREND: Rect = {x: 8, y: 10, width: 32, height: 28};
|
|
28
|
+
const VERTICAL_TREND: Rect = {x: 10, y: 8, width: 28, height: 32};
|
|
29
|
+
|
|
30
|
+
const FRAME_PATH =
|
|
31
|
+
'M20.5 32.5L4.21259 32.5C3.70663 32.5 3.45364 32.5 3.09178 32.3531C2.84817 32.2542 2.44102 31.9492 2.27758 31.7433C2.0348 31.4373 1.98325 31.2615 1.88017 30.9098C1.29286 28.906 0.5 24.6852 0.5 16.5C0.5 8.31487 1.29286 4.09402 1.88016 2.09025C1.98325 1.73854 2.0348 1.56268 2.27758 1.25676C2.44102 1.0508 2.84817 0.745859 3.09178 0.646944C3.45364 0.500011 3.70662 0.500011 4.21259 0.500011L20.5 0.50001L36.7874 0.500011C37.2934 0.500011 37.5464 0.500011 37.9082 0.646943C38.1518 0.745859 38.559 1.0508 38.7224 1.25675C38.9652 1.56268 39.0167 1.73854 39.1198 2.09025C39.7071 4.09402 40.5 8.31487 40.5 16.5C40.5 24.6851 39.7071 28.906 39.1198 30.9098C39.0167 31.2615 38.9652 31.4373 38.7224 31.7433C38.559 31.9492 38.1518 32.2542 37.9082 32.3531C37.5464 32.5 37.2934 32.5 36.7874 32.5H20.5Z';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Compact 48×48 atmospheric tank level indicator.
|
|
35
|
+
*
|
|
36
|
+
* ## Features / Variants
|
|
37
|
+
* - `variant`: `bar` (bottom-up percentage fill) or `trend` (history graph
|
|
38
|
+
* with a current-value micro-bar)
|
|
39
|
+
* - `direction`: `vertical` or `horizontal` tank outline
|
|
40
|
+
* - `value`: color scheme — `regular`, `enhanced` or `medium`
|
|
41
|
+
* - `level`: current tank level 0–100 (%), drives the fill and micro-bar
|
|
42
|
+
* - `data`: history samples 0–100 (%) drawn as the trend graph
|
|
43
|
+
* - `hasIcon`: centered icon overlay (bar variant only)
|
|
44
|
+
*
|
|
45
|
+
* ## Usage Guidelines
|
|
46
|
+
* Use as an at-a-glance level indicator in dashboards and lists. For a
|
|
47
|
+
* full-featured tank cell with readouts, badges and setpoints, use
|
|
48
|
+
* `obc-automation-tank` instead.
|
|
49
|
+
*
|
|
50
|
+
* ## Best Practices / Constraints
|
|
51
|
+
* - `level` and `data` values are clamped to 0–100; values outside that
|
|
52
|
+
* range are pinned to the nearest bound.
|
|
53
|
+
* - The `trend` variant needs at least two `data` samples to draw a line;
|
|
54
|
+
* with fewer it shows only the current-value micro-bar.
|
|
55
|
+
* - The `icon` slot renders only in the `bar` variant; it is ignored in
|
|
56
|
+
* `trend`.
|
|
57
|
+
* - Display-only: the root SVG is `aria-hidden`; convey tank level to
|
|
58
|
+
* assistive tech through adjacent text if it is not otherwise available.
|
|
59
|
+
*
|
|
60
|
+
* ## Slots
|
|
61
|
+
* | Slot | Condition | Purpose |
|
|
62
|
+
* | ------ | ----------------------------- | ------------------------------ |
|
|
63
|
+
* | `icon` | `hasIcon` and `variant='bar'` | Icon overlay (`obi-*` element) |
|
|
64
|
+
*
|
|
65
|
+
* @slot icon - Centered icon overlay, defaults to `obi-tank`
|
|
66
|
+
*/
|
|
67
|
+
@customElement('obc-indicator-tank-atmospheric')
|
|
68
|
+
export class ObcIndicatorTankAtmospheric extends LitElement {
|
|
69
|
+
@property({type: String, reflect: true}) direction: IndicatorDirection =
|
|
70
|
+
IndicatorDirection.vertical;
|
|
71
|
+
@property({type: String, reflect: true})
|
|
72
|
+
variant: IndicatorTankAtmosphericVariant =
|
|
73
|
+
IndicatorTankAtmosphericVariant.bar;
|
|
74
|
+
@property({type: String, reflect: true})
|
|
75
|
+
value: IndicatorTankAtmosphericValue = IndicatorTankAtmosphericValue.regular;
|
|
76
|
+
/** Current level, 0-100 (%) */
|
|
77
|
+
@property({type: Number}) level = 0;
|
|
78
|
+
/** History samples, 0-100 (%) each, oldest first */
|
|
79
|
+
@property({type: Array}) data: number[] = [];
|
|
80
|
+
@property({type: Boolean}) hasIcon = false;
|
|
81
|
+
|
|
82
|
+
private get scheme(): LinearIndicatorScheme {
|
|
83
|
+
switch (this.value) {
|
|
84
|
+
case IndicatorTankAtmosphericValue.enhanced:
|
|
85
|
+
return LinearIndicatorScheme.enhanced;
|
|
86
|
+
case IndicatorTankAtmosphericValue.medium:
|
|
87
|
+
return LinearIndicatorScheme.categorical;
|
|
88
|
+
default:
|
|
89
|
+
return LinearIndicatorScheme.regular;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
override render() {
|
|
94
|
+
const horizontal = this.direction === IndicatorDirection.horizontal;
|
|
95
|
+
const trend = this.variant === IndicatorTankAtmosphericVariant.trend;
|
|
96
|
+
const barRect = horizontal
|
|
97
|
+
? trend
|
|
98
|
+
? HORIZONTAL_TREND
|
|
99
|
+
: HORIZONTAL_BAR
|
|
100
|
+
: trend
|
|
101
|
+
? VERTICAL_TREND
|
|
102
|
+
: VERTICAL_BAR;
|
|
103
|
+
const frameTransform = horizontal
|
|
104
|
+
? 'translate(3.5 7.5)'
|
|
105
|
+
: 'rotate(90 24 24) translate(3.5 7.5)';
|
|
106
|
+
const showIcon =
|
|
107
|
+
this.hasIcon && this.variant === IndicatorTankAtmosphericVariant.bar;
|
|
108
|
+
return html`
|
|
109
|
+
<svg viewBox="0 0 48 48" aria-hidden="true">
|
|
110
|
+
<g transform=${frameTransform}>
|
|
111
|
+
<path class="frame" d=${FRAME_PATH} />
|
|
112
|
+
</g>
|
|
113
|
+
${this.variant === IndicatorTankAtmosphericVariant.trend
|
|
114
|
+
? renderTrendGraph(
|
|
115
|
+
barRect,
|
|
116
|
+
this.data,
|
|
117
|
+
this.level,
|
|
118
|
+
this.scheme,
|
|
119
|
+
'tank'
|
|
120
|
+
)
|
|
121
|
+
: renderLinearBar(barRect, this.level, this.scheme, 'tank')}
|
|
122
|
+
</svg>
|
|
123
|
+
${showIcon
|
|
124
|
+
? html`<div class="icon-wrapper">
|
|
125
|
+
<slot name="icon"><obi-tank usecsscolor></obi-tank></slot>
|
|
126
|
+
</div>`
|
|
127
|
+
: nothing}
|
|
128
|
+
`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static override styles = unsafeCSS(componentStyle);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
declare global {
|
|
135
|
+
interface HTMLElementTagNameMap {
|
|
136
|
+
'obc-indicator-tank-atmospheric': ObcIndicatorTankAtmospheric;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -262,7 +262,7 @@ export class ObcCompass extends LitElement {
|
|
|
262
262
|
/** @availableWhen priority==enhanced */
|
|
263
263
|
@property({type: Array, attribute: false})
|
|
264
264
|
priorityElements: CompassPriorityElement[] = [CompassPriorityElement.hdg];
|
|
265
|
-
/** Show compass NSEW labels
|
|
265
|
+
/** Show compass NSEW labels. The north arrow is always shown, independent of this flag. */
|
|
266
266
|
@property({type: Boolean}) showLabels: boolean = false;
|
|
267
267
|
/** When true, labels and north arrow are placed inside the outer ring. */
|
|
268
268
|
@property({type: Boolean}) tickmarksInside: boolean = false;
|
|
@@ -424,7 +424,7 @@ export class ObcCompass extends LitElement {
|
|
|
424
424
|
.tickmarksInside=${this.tickmarksInside}
|
|
425
425
|
.crosshairEnabled=${true}
|
|
426
426
|
.crosshairCenterCutout=${this.hasCenterReadouts}
|
|
427
|
-
.northArrow=${
|
|
427
|
+
.northArrow=${true}
|
|
428
428
|
.angleSetpoint=${this.headingSetpoint ?? undefined}
|
|
429
429
|
.newAngleSetpoint=${this.newHeadingSetpoint}
|
|
430
430
|
.atAngleSetpoint=${this._headingSp.computeAtSetpoint(this.heading)}
|
|
@@ -38,6 +38,21 @@ const meta: Meta<typeof ObcCurrent> = {
|
|
|
38
38
|
control: 'select',
|
|
39
39
|
options: topVessels,
|
|
40
40
|
},
|
|
41
|
+
hasPattern: {control: 'boolean'},
|
|
42
|
+
waveLength: {
|
|
43
|
+
control: {type: 'range', min: 0.25, max: 3, step: 0.05},
|
|
44
|
+
description:
|
|
45
|
+
'Wavelength multiplier on the design band spacing (1 = design).',
|
|
46
|
+
},
|
|
47
|
+
waveHeight: {
|
|
48
|
+
control: {type: 'range', min: 0, max: 1, step: 0.05},
|
|
49
|
+
description: 'Peak opacity the pattern bands fade up to.',
|
|
50
|
+
},
|
|
51
|
+
waveSpeed: {
|
|
52
|
+
control: {type: 'range', min: -2, max: 2, step: 0.1},
|
|
53
|
+
description:
|
|
54
|
+
'Pattern drift in wavelengths per second, with the flow (0 = static).',
|
|
55
|
+
},
|
|
41
56
|
},
|
|
42
57
|
} satisfies Meta<ObcCurrent>;
|
|
43
58
|
|
|
@@ -59,3 +74,21 @@ export const VesselEnhanced: Story = {
|
|
|
59
74
|
export const DirectionEnhanced: Story = {
|
|
60
75
|
args: {type: CurrentType.direction, priority: Priority.enhanced},
|
|
61
76
|
};
|
|
77
|
+
|
|
78
|
+
export const WithoutPattern: Story = {
|
|
79
|
+
args: {hasPattern: false},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** Denser, softer waves: half the design wavelength at reduced intensity. */
|
|
83
|
+
export const WaveTuned: Story = {
|
|
84
|
+
args: {waveLength: 0.5, waveHeight: 0.6},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The pattern drifts with the flow at `waveSpeed` wavelengths per second
|
|
89
|
+
* (excluded from visual tests because it animates continuously).
|
|
90
|
+
*/
|
|
91
|
+
export const AnimatedWaves: Story = {
|
|
92
|
+
tags: ['skip-test'],
|
|
93
|
+
args: {waveSpeed: 0.4},
|
|
94
|
+
};
|
|
@@ -18,8 +18,14 @@ export enum CurrentType {
|
|
|
18
18
|
|
|
19
19
|
const CURRENT_PATTERN_SCALE_VESSEL = 1.39;
|
|
20
20
|
const CURRENT_PATTERN_SCALE_DIRECTION = 2.0;
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Chevron-tip anchor radius for the peripheral icon (vessel type): the tip
|
|
23
|
+
* sits on the track band's inner edge (112), so the 48-unit glyph box fills
|
|
24
|
+
* the band (112–160) exactly, per the OpenBridge 6.1 design.
|
|
25
|
+
*/
|
|
26
|
+
const CURRENT_ICON_PERIPHERY_RADIUS = innerRingRadiusFor(
|
|
27
|
+
WatchCircleType.double
|
|
28
|
+
);
|
|
23
29
|
/** Scale of the centered 24-unit chevron glyph (direction type), from Figma (168/24). */
|
|
24
30
|
const CURRENT_DIRECTION_ICON_SCALE = 7;
|
|
25
31
|
|
|
@@ -56,6 +62,9 @@ export function clampCurrentSpeed(value: number | null): number | null {
|
|
|
56
62
|
* the enhanced (blue) palette.
|
|
57
63
|
* - **Vessel image**: configurable silhouette rotating with
|
|
58
64
|
* `vesselHeadingDeg` (vessel type).
|
|
65
|
+
* - **Pattern toggle & wave tuning**: `hasPattern` switches the band pattern
|
|
66
|
+
* off; `waveLength`, `waveHeight` and `waveSpeed` tune the band spacing,
|
|
67
|
+
* intensity and drift motion.
|
|
59
68
|
*
|
|
60
69
|
* ## Usage Guidelines
|
|
61
70
|
*
|
|
@@ -89,6 +98,28 @@ export class ObcCurrent extends LitElement {
|
|
|
89
98
|
@property({type: Number}) currentFromDirection: number | null = null;
|
|
90
99
|
/** Color priority: `Priority.enhanced` uses the blue/enhanced palette (default: `Priority.regular`). */
|
|
91
100
|
@property({type: String}) priority: Priority = Priority.regular;
|
|
101
|
+
/**
|
|
102
|
+
* Show the force-graphics band pattern behind the watch face.
|
|
103
|
+
* @availableWhen currentFromDirection!=null
|
|
104
|
+
*/
|
|
105
|
+
@property({type: Boolean, attribute: false}) hasPattern = true;
|
|
106
|
+
/**
|
|
107
|
+
* Wavelength of the pattern bands as a multiplier on the design spacing
|
|
108
|
+
* (1 = design, 0.5 = twice as dense).
|
|
109
|
+
* @availableWhen hasPattern==true && currentFromDirection!=null
|
|
110
|
+
*/
|
|
111
|
+
@property({type: Number}) waveLength = 1;
|
|
112
|
+
/**
|
|
113
|
+
* Wave intensity: the peak opacity the pattern bands fade up to (0–1).
|
|
114
|
+
* @availableWhen hasPattern==true && currentFromDirection!=null
|
|
115
|
+
*/
|
|
116
|
+
@property({type: Number}) waveHeight = 1;
|
|
117
|
+
/**
|
|
118
|
+
* Drift speed of the pattern in wavelengths per second, moving with the
|
|
119
|
+
* flow (negative drifts against it); 0 keeps the pattern static.
|
|
120
|
+
* @availableWhen hasPattern==true && currentFromDirection!=null
|
|
121
|
+
*/
|
|
122
|
+
@property({type: Number}) waveSpeed = 0;
|
|
92
123
|
/**
|
|
93
124
|
* The image of the vessel.
|
|
94
125
|
* @availableWhen type==vessel
|
|
@@ -148,7 +179,7 @@ export class ObcCurrent extends LitElement {
|
|
|
148
179
|
}
|
|
149
180
|
|
|
150
181
|
private renderForcePattern(watchCircleType: WatchCircleType) {
|
|
151
|
-
if (this.currentFromDirection == null) {
|
|
182
|
+
if (!this.hasPattern || this.currentFromDirection == null) {
|
|
152
183
|
return nothing;
|
|
153
184
|
}
|
|
154
185
|
return html`
|
|
@@ -162,6 +193,9 @@ export class ObcCurrent extends LitElement {
|
|
|
162
193
|
color: this.enhanced
|
|
163
194
|
? 'var(--instrument-enhanced-tertiary-color)'
|
|
164
195
|
: undefined,
|
|
196
|
+
waveLength: this.waveLength,
|
|
197
|
+
waveHeight: this.waveHeight,
|
|
198
|
+
waveSpeed: this.waveSpeed,
|
|
165
199
|
})}
|
|
166
200
|
</svg>
|
|
167
201
|
`;
|
|
@@ -128,12 +128,14 @@ export const WithVectorArrows: Story = {
|
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
130
|
* Below the label-reserve cap (~200px for the NSWE + north-arrow decor) the
|
|
131
|
-
* frame reports `labelsHidden` and the instrument drops the labels
|
|
132
|
-
*
|
|
133
|
-
*
|
|
131
|
+
* frame reports `labelsHidden` and the instrument drops the labels instead
|
|
132
|
+
* of letting them clip — the same graceful degradation the tick-label
|
|
133
|
+
* instruments use (issue #1021). The north arrow stays visible on even the
|
|
134
|
+
* smallest face: below the small-scale threshold it renders as a compact
|
|
135
|
+
* triangle at the ring that scales with the face, so it never clips.
|
|
134
136
|
*/
|
|
135
137
|
export const SmallContainerDegraded: Story = {
|
|
136
|
-
name: 'Small Container (160px, Labels
|
|
138
|
+
name: 'Small Container (160px, Labels Hidden Past Cap)',
|
|
137
139
|
args: {
|
|
138
140
|
width: 160,
|
|
139
141
|
},
|
|
@@ -309,7 +309,7 @@ export class ObcHeading extends LitElement {
|
|
|
309
309
|
.tickmarksInside=${this.tickmarksInside}
|
|
310
310
|
.crosshairEnabled=${true}
|
|
311
311
|
.crosshairCenterCutout=${this.hasCenterReadouts}
|
|
312
|
-
.northArrow=${
|
|
312
|
+
.northArrow=${true}
|
|
313
313
|
.angleSetpoint=${this.headingSetpoint ?? undefined}
|
|
314
314
|
.newAngleSetpoint=${this.newHeadingSetpoint}
|
|
315
315
|
.atAngleSetpoint=${this._headingSp.computeAtSetpoint(this.heading)}
|
|
@@ -351,9 +351,16 @@ export class ObcPitchRollHeave extends LitElement {
|
|
|
351
351
|
* `obc-watch` renders its crosshair after the bands, i.e. on top of them.
|
|
352
352
|
* Drawing it here puts it under the watch layer, so the bands and the heave
|
|
353
353
|
* column mask it naturally.
|
|
354
|
+
*
|
|
355
|
+
* In `single-scale` nothing frames the segment above the vessel (the other
|
|
356
|
+
* three arms end under the pitch band, roll band and heave column), so the
|
|
357
|
+
* vertical arm stops at the centre instead of continuing upward. In
|
|
358
|
+
* `dual-scale` the top roll band gives it a natural endpoint, so the full
|
|
359
|
+
* arm is kept.
|
|
354
360
|
*/
|
|
355
361
|
private renderCrosshair(): SVGTemplateResult {
|
|
356
362
|
const r = OUTER_RING_RADIUS;
|
|
363
|
+
const yTop = this.isDualScale ? -r : 0;
|
|
357
364
|
return svg`
|
|
358
365
|
<line
|
|
359
366
|
x1=${-r} y1="0" x2=${r} y2="0"
|
|
@@ -361,7 +368,7 @@ export class ObcPitchRollHeave extends LitElement {
|
|
|
361
368
|
vector-effect="non-scaling-stroke"
|
|
362
369
|
/>
|
|
363
370
|
<line
|
|
364
|
-
x1="0" y1=${
|
|
371
|
+
x1="0" y1=${yTop} x2="0" y2=${r}
|
|
365
372
|
stroke="var(--instrument-frame-tertiary-color)"
|
|
366
373
|
vector-effect="non-scaling-stroke"
|
|
367
374
|
/>
|
|
@@ -131,7 +131,7 @@ export const WithTrackBar: Story = {
|
|
|
131
131
|
hasReadout: true,
|
|
132
132
|
rateOfTurnDegreesPerMinute: 45,
|
|
133
133
|
rotPosition: RotPosition.innerCircle,
|
|
134
|
-
watchCircleType: WatchCircleType.
|
|
134
|
+
watchCircleType: WatchCircleType.triple,
|
|
135
135
|
},
|
|
136
136
|
};
|
|
137
137
|
|
|
@@ -142,7 +142,7 @@ export const WithTrackBarEnhanced: Story = {
|
|
|
142
142
|
hasReadout: true,
|
|
143
143
|
rateOfTurnDegreesPerMinute: 45,
|
|
144
144
|
rotPosition: RotPosition.innerCircle,
|
|
145
|
-
watchCircleType: WatchCircleType.
|
|
145
|
+
watchCircleType: WatchCircleType.triple,
|
|
146
146
|
priority: Priority.enhanced,
|
|
147
147
|
},
|
|
148
148
|
};
|
|
@@ -154,7 +154,7 @@ export const WithTrackBarPortStarboard: Story = {
|
|
|
154
154
|
hasReadout: true,
|
|
155
155
|
rateOfTurnDegreesPerMinute: 45,
|
|
156
156
|
rotPosition: RotPosition.innerCircle,
|
|
157
|
-
watchCircleType: WatchCircleType.
|
|
157
|
+
watchCircleType: WatchCircleType.triple,
|
|
158
158
|
rotPortStarboard: true,
|
|
159
159
|
},
|
|
160
160
|
};
|
|
@@ -35,7 +35,8 @@ export {RotType, RotPosition};
|
|
|
35
35
|
* - **Track bar** (`hasTrackBar`): A bar in the ring band growing from the
|
|
36
36
|
* twelve o'clock position to the measured rate of turn, with a needle
|
|
37
37
|
* marker at its end and sector tickmarks (pairs with
|
|
38
|
-
* `watchCircleType="double"` for the banded face
|
|
38
|
+
* `watchCircleType="double"` for the banded face, or `"triple"` when the
|
|
39
|
+
* ROT dots sit on the inner circle).
|
|
39
40
|
* - **Center readout** (`hasReadout`): A centered readout showing the
|
|
40
41
|
* measured rate of turn (label `ROT`, unit `DEG/min` by default).
|
|
41
42
|
* - **Color priority**: Uses `priority` to select regular or enhanced color
|
|
@@ -91,7 +92,9 @@ export class ObcRateOfTurn extends LitElement {
|
|
|
91
92
|
* When `true`, shows a bar in the ring band from the twelve o'clock
|
|
92
93
|
* position to the measured rate of turn, with a needle marker at its end
|
|
93
94
|
* and sector tickmarks. Pairs with `watchCircleType="double"` for the
|
|
94
|
-
* banded face
|
|
95
|
+
* banded face — or `"triple"` when `rotPosition` is `innerCircle`, so the
|
|
96
|
+
* dots get their inner track too. Driven by `rateOfTurnDegreesPerMinute`
|
|
97
|
+
* only.
|
|
95
98
|
*/
|
|
96
99
|
@property({type: Boolean}) hasTrackBar: boolean = false;
|
|
97
100
|
/**
|
|
@@ -15,10 +15,12 @@ import {SetpointBundle} from '../../svghelpers/setpoint-bundle.js';
|
|
|
15
15
|
import '../watch/watch.js';
|
|
16
16
|
import {
|
|
17
17
|
OUTER_RING_RADIUS,
|
|
18
|
+
WatchCircleType,
|
|
18
19
|
type WatchBarArea,
|
|
19
20
|
type WatchNeedle,
|
|
20
21
|
} from '../watch/watch.js';
|
|
21
22
|
import {
|
|
23
|
+
BAND_INNER_RADIUS,
|
|
22
24
|
LANE_DIVIDER_RADIUS,
|
|
23
25
|
LANE_DIVIDER_WIDTH,
|
|
24
26
|
PRIMARY_SUBBAND_INNER_RADIUS,
|
|
@@ -411,6 +413,21 @@ export class ObcTopViewPropulsion extends LitElement {
|
|
|
411
413
|
></path>`;
|
|
412
414
|
}
|
|
413
415
|
|
|
416
|
+
/**
|
|
417
|
+
* Thin outline on the track band's inner edge, always shown per the
|
|
418
|
+
* design's track face. Drawn on the overlay because the watch paints its
|
|
419
|
+
* band bars over the ring circles, which would swallow a watch-side line.
|
|
420
|
+
*/
|
|
421
|
+
private renderInnerRingLine() {
|
|
422
|
+
return svg`<circle
|
|
423
|
+
r=${BAND_INNER_RADIUS}
|
|
424
|
+
fill="none"
|
|
425
|
+
stroke="var(--instrument-frame-tertiary-color)"
|
|
426
|
+
stroke-width="1"
|
|
427
|
+
vector-effect="non-scaling-stroke"
|
|
428
|
+
></circle>`;
|
|
429
|
+
}
|
|
430
|
+
|
|
414
431
|
private renderPropeller() {
|
|
415
432
|
return svg`<g transform="scale(${PROPELLER_SCALE}) translate(-80 -80)">${
|
|
416
433
|
propellerImages[this.propeller]
|
|
@@ -448,6 +465,7 @@ export class ObcTopViewPropulsion extends LitElement {
|
|
|
448
465
|
<obc-watch
|
|
449
466
|
.touching=${this.touching}
|
|
450
467
|
.arcFrame=${frame}
|
|
468
|
+
.watchCircleType=${WatchCircleType.double}
|
|
451
469
|
.tickmarks=${shownTickmarks}
|
|
452
470
|
.state=${this.state}
|
|
453
471
|
.priority=${this.priority}
|
|
@@ -465,8 +483,8 @@ export class ObcTopViewPropulsion extends LitElement {
|
|
|
465
483
|
.tickmarkStyle=${this.tickmarkStyle}
|
|
466
484
|
></obc-watch>
|
|
467
485
|
<svg viewBox=${frame.viewBox} xmlns="http://www.w3.org/2000/svg">
|
|
468
|
-
${this.
|
|
469
|
-
${this.renderPropeller()}
|
|
486
|
+
${this.renderInnerRingLine()} ${this.renderLoadingArc()}
|
|
487
|
+
${this.renderSecondaryArc()} ${this.renderPropeller()}
|
|
470
488
|
</svg>
|
|
471
489
|
</div>
|
|
472
490
|
`;
|
|
@@ -70,6 +70,16 @@ const WIND_ICON_SCALE = 2;
|
|
|
70
70
|
const WIND_ICON_TIP_X = 12;
|
|
71
71
|
const WIND_ICON_TIP_Y = 22.4453;
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Radial overhang of the rendered (2×, 48-unit) wind icon box past the tip
|
|
75
|
+
* anchor toward the watch center: the glyph tip sits at y=22.4453 of the
|
|
76
|
+
* 24-unit box, so the scaled box extends `(24 − 22.4453) × 2 ≈ 3.11` units
|
|
77
|
+
* inside the anchor radius. Add it to a desired box inner-edge radius to get
|
|
78
|
+
* the `windSymbolRadius` that makes the box start exactly there.
|
|
79
|
+
*/
|
|
80
|
+
export const WIND_ICON_TIP_TO_BOX_INNER =
|
|
81
|
+
(24 - WIND_ICON_TIP_Y) * WIND_ICON_SCALE;
|
|
82
|
+
|
|
73
83
|
const windIconCache = new Map<string, SVGTemplateResult>();
|
|
74
84
|
|
|
75
85
|
function getWindIconSvg(bucket: number): SVGTemplateResult | null {
|
|
@@ -96,4 +96,46 @@ describe('renderCurrentForcePattern', () => {
|
|
|
96
96
|
);
|
|
97
97
|
expect(flat).toContain(String(-(256 * 1.39) / 2 + 104 * 1.39));
|
|
98
98
|
});
|
|
99
|
+
|
|
100
|
+
it('honors wave tuning: length scales the tile, height caps opacity, speed drifts', () => {
|
|
101
|
+
const flat = flatten(
|
|
102
|
+
renderCurrentForcePattern({
|
|
103
|
+
fromDirectionDeg: 0,
|
|
104
|
+
radius: 112,
|
|
105
|
+
patternScale: 2.0,
|
|
106
|
+
waveLength: 0.75,
|
|
107
|
+
waveHeight: 0.6,
|
|
108
|
+
waveSpeed: 0.4,
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
expect(flat).toContain(`width=${48 * 2.0 * 0.75}`);
|
|
112
|
+
expect(flat).toContain('stop-opacity=0.6');
|
|
113
|
+
expect(flat).toContain('animateTransform');
|
|
114
|
+
expect(flat).toContain('dur="2.5s"');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('is static at full intensity without wave options', () => {
|
|
118
|
+
const flat = flatten(
|
|
119
|
+
renderCurrentForcePattern({
|
|
120
|
+
fromDirectionDeg: 0,
|
|
121
|
+
radius: 112,
|
|
122
|
+
patternScale: 1.39,
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
expect(flat).not.toContain('animateTransform');
|
|
126
|
+
expect(flat).toContain('stop-opacity=1');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('falls back to full intensity for a non-finite waveHeight', () => {
|
|
130
|
+
const flat = flatten(
|
|
131
|
+
renderCurrentForcePattern({
|
|
132
|
+
fromDirectionDeg: 0,
|
|
133
|
+
radius: 112,
|
|
134
|
+
patternScale: 1.39,
|
|
135
|
+
waveHeight: Number.NaN,
|
|
136
|
+
})
|
|
137
|
+
);
|
|
138
|
+
expect(flat).toContain('stop-opacity=1');
|
|
139
|
+
expect(flat).not.toContain('NaN');
|
|
140
|
+
});
|
|
99
141
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {svg, SVGTemplateResult} from 'lit';
|
|
1
|
+
import {nothing, svg, SVGTemplateResult} from 'lit';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Options for the force-graphics pattern underlays (wind streaks / current
|
|
@@ -19,6 +19,22 @@ export interface ForcePatternOptions {
|
|
|
19
19
|
color?: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/** Wave tuning for the current band pattern, on top of the shared options. */
|
|
23
|
+
export interface CurrentForcePatternOptions extends ForcePatternOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Wavelength as a multiplier on the design band spacing (1 = design,
|
|
26
|
+
* 0.5 = twice as dense). Non-finite or non-positive values fall back to 1.
|
|
27
|
+
*/
|
|
28
|
+
waveLength?: number;
|
|
29
|
+
/** Wave intensity: peak opacity the band gradient fades up to (0–1). */
|
|
30
|
+
waveHeight?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Drift speed in wavelengths per second, moving with the flow (negative
|
|
33
|
+
* drifts against it). 0 / undefined keeps the pattern static.
|
|
34
|
+
*/
|
|
35
|
+
waveSpeed?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
22
38
|
const DESIGN_PATTERN_SQUARE = 256;
|
|
23
39
|
const WIND_TILE = 24;
|
|
24
40
|
/**
|
|
@@ -64,21 +80,39 @@ export function renderWindForcePattern(
|
|
|
64
80
|
/**
|
|
65
81
|
* Current force-graphics: soft-edged gradient bands (48-unit sawtooth,
|
|
66
82
|
* transparent → opaque), perpendicular to the flow, clipped to a circle and
|
|
67
|
-
* rotated by the from-direction.
|
|
83
|
+
* rotated by the from-direction. `waveLength` stretches the band spacing,
|
|
84
|
+
* `waveHeight` scales the gradient's peak opacity, and a non-zero `waveSpeed`
|
|
85
|
+
* drifts the field with the flow (one wavelength per `1/waveSpeed` seconds)
|
|
86
|
+
* via a declarative SMIL loop.
|
|
68
87
|
*/
|
|
69
88
|
export function renderCurrentForcePattern(
|
|
70
|
-
options:
|
|
89
|
+
options: CurrentForcePatternOptions
|
|
71
90
|
): SVGTemplateResult {
|
|
72
91
|
const {fromDirectionDeg, radius, patternScale} = options;
|
|
73
92
|
const color = options.color ?? DEFAULT_COLOR;
|
|
74
|
-
const
|
|
93
|
+
const lengthScale =
|
|
94
|
+
Number.isFinite(options.waveLength) && options.waveLength! > 0
|
|
95
|
+
? options.waveLength!
|
|
96
|
+
: 1;
|
|
97
|
+
const intensity = Number.isFinite(options.waveHeight)
|
|
98
|
+
? Math.max(0, Math.min(1, options.waveHeight!))
|
|
99
|
+
: 1;
|
|
100
|
+
const speed = Number.isFinite(options.waveSpeed) ? options.waveSpeed! : 0;
|
|
101
|
+
const tile = CURRENT_TILE * patternScale * lengthScale;
|
|
75
102
|
const side = DESIGN_PATTERN_SQUARE * patternScale;
|
|
76
|
-
const phase = CURRENT_TILE_PHASE_OFFSET * patternScale;
|
|
103
|
+
const phase = CURRENT_TILE_PHASE_OFFSET * patternScale * lengthScale;
|
|
104
|
+
// The pattern is anchored in the rect's user space, so translating the
|
|
105
|
+
// wrapper group drifts the whole band field; the rect is one tile taller on
|
|
106
|
+
// each end to stay covering the clip disc across the loop.
|
|
107
|
+
const drift =
|
|
108
|
+
speed !== 0
|
|
109
|
+
? svg`<animateTransform attributeName="transform" attributeType="XML" type="translate" from="0 0" to="0 ${tile * Math.sign(speed)}" dur="${1 / Math.abs(speed)}s" repeatCount="indefinite"/>`
|
|
110
|
+
: nothing;
|
|
77
111
|
return svg`
|
|
78
112
|
<defs>
|
|
79
113
|
<linearGradient id="current-force-band-gradient" x1="24" y1="0" x2="24" y2="48" gradientUnits="userSpaceOnUse">
|
|
80
114
|
<stop stop-color=${color} stop-opacity="0"/>
|
|
81
|
-
<stop offset="1" stop-color=${color}/>
|
|
115
|
+
<stop offset="1" stop-color=${color} stop-opacity=${intensity}/>
|
|
82
116
|
</linearGradient>
|
|
83
117
|
<pattern id="current-force-pattern" patternUnits="userSpaceOnUse" x=${-side / 2 + phase} y=${-side / 2 + phase} width=${tile} height=${tile} viewBox="0 0 48 48" preserveAspectRatio="none">
|
|
84
118
|
<rect width="48" height="48" fill="url(#current-force-band-gradient)"/>
|
|
@@ -86,7 +120,10 @@ export function renderCurrentForcePattern(
|
|
|
86
120
|
<clipPath id="current-force-clip"><circle cx="0" cy="0" r=${radius}/></clipPath>
|
|
87
121
|
</defs>
|
|
88
122
|
<g clip-path="url(#current-force-clip)" transform="rotate(${fromDirectionDeg})">
|
|
89
|
-
<
|
|
123
|
+
<g>
|
|
124
|
+
${drift}
|
|
125
|
+
<rect x=${-side / 2} y=${-side / 2 - tile} width=${side} height=${side + 2 * tile} fill="url(#current-force-pattern)"/>
|
|
126
|
+
</g>
|
|
90
127
|
</g>
|
|
91
128
|
`;
|
|
92
129
|
}
|