Skip to content

Commit 21de1f3

Browse files
committed
feat(7segment): multi digit support, pin info
also add: - optional colon (clock mode) - ability to set off color and background color - `pins` attribute with three modes: top, extend or none
1 parent fb7ba24 commit 21de1f3

File tree

3 files changed

+246
-54
lines changed

3 files changed

+246
-54
lines changed

src/7segment-element.stories.ts

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,77 @@
1-
import { withKnobs } from '@storybook/addon-knobs';
2-
import { storiesOf } from '@storybook/web-components';
1+
import { withKnobs, select } from '@storybook/addon-knobs';
2+
import { storiesOf, forceReRender } from '@storybook/web-components';
33
import { html } from 'lit-html';
44
import './7segment-element';
5-
import { LitElement, customElement, property } from 'lit-element';
5+
6+
class SpinnerAnimation {
7+
private index = 0;
8+
values: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
9+
10+
step() {
11+
this.values = [0, 0, 0, 0, 0, 0, 0, 0];
12+
this.values[this.index++ % 6] = 1;
13+
}
14+
}
15+
16+
class BlinkAnimation {
17+
value = false;
18+
19+
step() {
20+
this.value = !this.value;
21+
}
22+
}
23+
24+
const spinnerAnimation = new SpinnerAnimation();
25+
const blinkAnimation = new BlinkAnimation();
26+
setInterval(() => {
27+
spinnerAnimation.step();
28+
blinkAnimation.step();
29+
forceReRender();
30+
}, 100);
631

732
storiesOf('7 Segment', module)
833
.addParameters({ component: 'wokwi-7segment' })
934
.addDecorator(withKnobs)
1035
.add(
1136
'Red 4.',
12-
() => html`<wokwi-7segment color="red" values="[0,1,1,0,0,1,1,1]"></wokwi-7segment>`
37+
() =>
38+
html`<wokwi-7segment
39+
color="red"
40+
values="[0,1,1,0,0,1,1,1]"
41+
pins=${select('pins', ['extend', 'top', 'none'], 'extend')}
42+
></wokwi-7segment>`
1343
)
1444
.add(
1545
'Green 5',
1646
() => html`<wokwi-7segment color="green" values="[1,0,1,1,0,1,1,0]"></wokwi-7segment>`
1747
)
1848
.add(
19-
'Blue spinner', //
20-
() => html`<wokwi-blue-spinner></wokwi-blue-spinner>`
49+
'2 yellow Digits',
50+
() =>
51+
html`<wokwi-7segment
52+
color="yellow"
53+
digits="2"
54+
values="[0,1,1,0,0,1,1,1,0,1,1,0,0,1,1,0]"
55+
></wokwi-7segment>`
56+
)
57+
.add(
58+
'Clock mode',
59+
() =>
60+
html`<wokwi-7segment
61+
color="red"
62+
digits="4"
63+
colon="true"
64+
.colonValue=${blinkAnimation.value}
65+
pins=${select('pins', ['extend', 'top', 'none'], 'top')}
66+
values="[0,1,1,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0]"
67+
></wokwi-7segment>`
68+
)
69+
.add(
70+
'Blue spinner',
71+
() =>
72+
html`<wokwi-7segment
73+
color="#8080ff"
74+
pins="none"
75+
.values="${spinnerAnimation.values}"
76+
></wokwi-7segment>`
2177
);
22-
23-
@customElement('wokwi-blue-spinner')
24-
export class BlueSpinnerElement extends LitElement {
25-
@property({ type: Array }) values: number[] = [];
26-
27-
private interval: number | null = null;
28-
29-
connectedCallback() {
30-
super.connectedCallback();
31-
let i = 0;
32-
this.interval = setInterval(() => {
33-
this.values = [0, 0, 0, 0, 0, 0, 0, 0];
34-
this.values[i++ % 6] = 1;
35-
}, 100);
36-
}
37-
38-
disconnectedCallback() {
39-
if (this.interval) {
40-
clearInterval(this.interval);
41-
this.interval = null;
42-
}
43-
}
44-
45-
render() {
46-
return html` <wokwi-7segment color="blue" .values="${this.values}"></wokwi-7segment> `;
47-
}
48-
}

src/7segment-element.ts

Lines changed: 178 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,106 @@
1-
import { css, customElement, html, LitElement, property } from 'lit-element';
1+
import { css, customElement, html, LitElement, property, svg } from 'lit-element';
2+
import { ElementPin } from './pin';
23

34
@customElement('wokwi-7segment')
45
export class SevenSegmentElement extends LitElement {
6+
/** The color of a lit segment */
57
@property() color = 'red';
8+
9+
/** The colon of an unlit segment */
10+
@property() offColor = '#444';
11+
12+
/** The background color of the 7-segment display */
13+
@property() background = 'black';
14+
15+
/** Number of digits to display (1, 2 or 4 are common values) */
16+
@property({ type: Number }) digits = 1;
17+
18+
/** Whether to show a colon (clock mode) */
19+
@property({ type: Boolean }) colon = false;
20+
21+
/** Whether the colon is lit or not */
22+
@property({ type: Boolean }) colonValue = false;
23+
24+
/**
25+
* Pin format. `top` to draw pins on top of the segment display panel,
26+
* `extend` to extend them to the sides of the panel, and `none` to
27+
* disable drawing the pins.
28+
*/
29+
@property() pins: 'top' | 'extend' | 'none' = 'top';
30+
31+
/**
32+
* The values for the individual segments. Each digits has 8
33+
* segment values in the following order: A, B, C, D, E, F, G, DP
34+
*
35+
* The values are 1 for a lit segment, and 0 for an unlit segment.
36+
*/
637
@property({ type: Array }) values: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
738

39+
get pinInfo(): ElementPin[] {
40+
const pinXY = (n: number) => {
41+
const { startX, cols, bottomY } = this.pinPositions;
42+
const col = (n - 1) % cols;
43+
const row = Math.floor((n - 1) / cols);
44+
const xOffset = 1.27;
45+
const mmToPix = 3.78;
46+
const x = startX + xOffset + col * 2.54;
47+
const y = this.pins === 'top' ? (row ? bottomY + 1 : 1) : row ? bottomY + 2 : 0;
48+
return { number: n, x: x * mmToPix, y: y * mmToPix };
49+
};
50+
51+
switch (this.digits) {
52+
case 4:
53+
// Pinout based on KW4-56NALB model
54+
return [
55+
{ name: 'A', ...pinXY(13), signals: [], description: 'Segment A' },
56+
{ name: 'B', ...pinXY(9), signals: [], description: 'Segment B' },
57+
{ name: 'C', ...pinXY(4), signals: [], description: 'Segment C' },
58+
{ name: 'D', ...pinXY(2), signals: [], description: 'Segment D' },
59+
{ name: 'E', ...pinXY(1), signals: [], description: 'Segment E' },
60+
{ name: 'F', ...pinXY(12), signals: [], description: 'Segment F' },
61+
{ name: 'G', ...pinXY(5), signals: [], description: 'Segment G' },
62+
{ name: 'DP', ...pinXY(3), signals: [], description: 'Decimal Point' },
63+
{ name: 'DIG1', ...pinXY(14), signals: [], description: 'Digit 1 Common' },
64+
{ name: 'DIG2', ...pinXY(11), signals: [], description: 'Digit 2 Common' },
65+
{ name: 'DIG3', ...pinXY(10), signals: [], description: 'Digit 3 Common' },
66+
{ name: 'DIG4', ...pinXY(6), signals: [], description: 'Digit 4 Common' },
67+
{ name: 'COM', ...pinXY(7), signals: [], description: 'Common pin' },
68+
{ name: 'CLN', ...pinXY(8), signals: [], description: 'Colon' },
69+
];
70+
71+
case 2:
72+
// Pinout based on CL5621C model
73+
return [
74+
{ name: 'DIG1', ...pinXY(8), signals: [], description: 'Digit 1 Common' },
75+
{ name: 'DIG2', ...pinXY(7), signals: [], description: 'Digit 2 Common' },
76+
{ name: 'A', ...pinXY(10), signals: [], description: 'Segment A' },
77+
{ name: 'B', ...pinXY(9), signals: [], description: 'Segment B' },
78+
{ name: 'C', ...pinXY(1), signals: [], description: 'Segment C' },
79+
{ name: 'D', ...pinXY(4), signals: [], description: 'Segment D' },
80+
{ name: 'E', ...pinXY(3), signals: [], description: 'Segment E' },
81+
{ name: 'F', ...pinXY(6), signals: [], description: 'Segment F' },
82+
{ name: 'G', ...pinXY(5), signals: [], description: 'Segment G' },
83+
{ name: 'DP', ...pinXY(2), signals: [], description: 'Decimal Point' },
84+
];
85+
86+
case 1:
87+
default:
88+
// Pinout based on 5611BH model
89+
return [
90+
{ name: 'COM.1', ...pinXY(3), signals: [], description: 'Common' },
91+
{ name: 'COM.2', ...pinXY(8), signals: [], description: 'Common' },
92+
{ name: 'A', ...pinXY(7), signals: [], description: 'Segment A' },
93+
{ name: 'B', ...pinXY(6), signals: [], description: 'Segment B' },
94+
{ name: 'C', ...pinXY(4), signals: [], description: 'Segment C' },
95+
{ name: 'D', ...pinXY(2), signals: [], description: 'Segment D' },
96+
{ name: 'E', ...pinXY(1), signals: [], description: 'Segment E' },
97+
{ name: 'F', ...pinXY(9), signals: [], description: 'Segment F' },
98+
{ name: 'G', ...pinXY(10), signals: [], description: 'Segment G' },
99+
{ name: 'DP', ...pinXY(5), signals: [], description: 'Decimal Point' },
100+
];
101+
}
102+
}
103+
8104
static get styles() {
9105
return css`
10106
polygon {
@@ -15,28 +111,89 @@ export class SevenSegmentElement extends LitElement {
15111
`;
16112
}
17113

114+
get pinPositions() {
115+
const { digits } = this;
116+
const numPins = digits === 4 ? 14 : 10;
117+
const cols = Math.ceil(numPins / 2);
118+
return {
119+
startX: (12.55 * digits - cols * 2.54) / 2,
120+
bottomY: this.pins === 'extend' ? 21 : 17,
121+
cols,
122+
};
123+
}
124+
125+
private get yOffset() {
126+
return this.pins === 'extend' ? 2 : 0;
127+
}
128+
129+
renderDigit(x: number, startIndex: number) {
130+
const fill = (index: number) => (this.values[startIndex + index] ? this.color : this.offColor);
131+
return svg`
132+
<g transform="skewX(-8) translate(${x}, ${this.yOffset + 2.4}) scale(0.81)">
133+
<polygon points="2 0 8 0 9 1 8 2 2 2 1 1" fill="${fill(0)}" />
134+
<polygon points="10 2 10 8 9 9 8 8 8 2 9 1" fill="${fill(1)}" />
135+
<polygon points="10 10 10 16 9 17 8 16 8 10 9 9" fill="${fill(2)}" />
136+
<polygon points="8 18 2 18 1 17 2 16 8 16 9 17" fill="${fill(3)}" />
137+
<polygon points="0 16 0 10 1 9 2 10 2 16 1 17" fill="${fill(4)}" />
138+
<polygon points="0 8 0 2 1 1 2 2 2 8 1 9" fill=${fill(5)} />
139+
<polygon points="2 8 8 8 9 9 8 10 2 10 1 9" fill=${fill(6)} />
140+
</g>
141+
<circle cx="${x + 7.4}" cy="${this.yOffset + 16}" r="0.89" fill="${fill(7)}" />
142+
`;
143+
}
144+
145+
renderColon() {
146+
const { yOffset } = this;
147+
const colonPosition = 1.5 + 12.7 * Math.round(this.digits / 2);
148+
const colonFill = this.colonValue ? this.color : this.offColor;
149+
return svg`
150+
<g transform="skewX(-8)" fill="${colonFill}">
151+
<circle cx="${colonPosition}" cy="${yOffset + 5.75}" r="0.89" />
152+
<circle cx="${colonPosition}" cy="${yOffset + 13.25}" r="0.89" />
153+
</g>
154+
`;
155+
}
156+
157+
renderPins() {
158+
const { cols, bottomY, startX: x } = this.pinPositions;
159+
return svg`
160+
<g fill="url(#pin-pattern)" transform="translate(${x}, 0)">
161+
<rect height="2" width=${cols * 2.54} />
162+
<rect height="2" width=${cols * 2.54} transform="translate(0, ${bottomY})" />
163+
</g>
164+
`;
165+
}
166+
18167
render() {
19-
const { color, values } = this;
20-
const fill = (index: number) => (values[index] ? color : '#ddd');
168+
const { digits, colon, pins, yOffset } = this;
169+
const width = 12.55 * digits;
170+
const height = pins === 'extend' ? 23 : 19;
171+
const digitShapes = [];
172+
for (let i = 0; i < digits; i++) {
173+
digitShapes.push(this.renderDigit(3.5 + i * 12.7, i * 8));
174+
}
21175
return html`
22-
<svg
23-
width="12mm"
24-
height="18.5mm"
25-
version="1.1"
26-
viewBox="0 0 12 18.5"
27-
xmlns="http://www.w3.org/2000/svg"
28-
>
29-
<g transform="skewX(-8) translate(2, 0)">
30-
<polygon points="2 0 8 0 9 1 8 2 2 2 1 1" fill="${fill(0)}" />
31-
<polygon points="10 2 10 8 9 9 8 8 8 2 9 1" fill="${fill(1)}" />
32-
<polygon points="10 10 10 16 9 17 8 16 8 10 9 9" fill="${fill(2)}" />
33-
<polygon points="8 18 2 18 1 17 2 16 8 16 9 17" fill="${fill(3)}" />
34-
<polygon points="0 16 0 10 1 9 2 10 2 16 1 17" fill="${fill(4)}" />
35-
<polygon points="0 8 0 2 1 1 2 2 2 8 1 9" fill=${fill(5)} />
36-
<polygon points="2 8 8 8 9 9 8 10 2 10 1 9" fill=${fill(6)} />
37-
</g>
38-
<circle cx="11" cy="17" r="1.1" fill="${fill(7)}" />
39-
</svg>
176+
<div style="position:relative">
177+
<svg
178+
width="${width}mm"
179+
height="${height}mm"
180+
version="1.1"
181+
viewBox="0 0 ${width} ${height}"
182+
xmlns="http://www.w3.org/2000/svg"
183+
>
184+
<defs>
185+
<pattern id="pin-pattern" height="2" width="2.54" patternUnits="userSpaceOnUse">
186+
${pins === 'extend'
187+
? svg`<rect x="1.02" y="0" height="2" width="0.5" fill="#aaa" />`
188+
: svg`<circle cx="1.27" cy="1" r=0.5 fill="#aaa" />`}
189+
</pattern>
190+
</defs>
191+
<rect x="0" y="${yOffset}" width="${width}" height="19" />
192+
${digitShapes}<!-- -->
193+
${colon ? this.renderColon() : null}<!-- -->
194+
${pins !== 'none' ? this.renderPins() : null}
195+
</svg>
196+
</div>
40197
`;
41198
}
42199
}

src/pin.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ export interface ElementPin {
4444
/** The signals for this pin. Leave empty for generic pins without a designated signals. **/
4545
signals: PinSignalInfo[];
4646

47+
/**
48+
* Pin number. Only relevant for components with numbered pins
49+
* (e.g. chips in DIP package), and always starts with 1.
50+
*/
51+
number?: number;
52+
4753
/**
4854
* Optional pin description
4955
*/

0 commit comments

Comments
 (0)