function sevenSegmentDisplay(
digits = 10,
{ height, sign, lit, unlit, background, padding, skew } = {
height: 100,
sign: false,
lit: "#000",
unlit: "#eee",
background: "none",
padding: 0,
skew: 0
}
) {
function getWidth(height) {
const scaleFactor = height / 100;
return (digits + sign | 0) * (height / 2 + padding * scaleFactor) + 12 * scaleFactor;
};
function setupDisplay() {
return svg`
<svg width=${getWidth(height)} height=${height} viewBox="0 0 ${getWidth(100)} 100">
<defs>
<path id="a" d="M10,8L14,4L42,4L46,8L42,12L14,12L10,8z" />
<path id="b" d="M48,10L52,14L52,42L48,46L44,42L44,14L48,10z" />
<path id="c" d="M48,50L52,54L52,82L48,86L44,82L44,54L48,50z" />
<path id="d" d="M10,88L14,84L42,84L46,88L42,92L14,92L10,88z" />
<path id="e" d="M8,50L12,54L12,82L8,86L4,82L4,54L8,50z" />
<path id="f" d="M8,10L12,14L12,42L8,46L4,42L4,14L8,10z" />
<path id="g" d="M10,48L14,44L42,44L46,48L42,52L14,52L10,48z" />
<circle id="dot" cx="56" cy="88" r="4" />
</defs>
<rect width=${getWidth(100)} height="100" fill=${background} />
<g id="digits" />
</svg>`;
}
function digitTo7Segment(d) {
let A = (d & 0b1000) >> 3,
B = (d & 0b0100) >> 2,
C = (d & 0b0010) >> 1,
D = d & 0b0001;
let notB = B ^ 1, notC = C ^ 1, notD = D ^ 1;
return [
A | C | B & D | notB & notD,
C & D | notB | notC & notD,
B | D | notC,
A | notB & notD | notB & C | C & notD | B & notC & D,
C & notD | notB & notD,
A | B & notC | B & notD | notC & notD,
A | B & notC | notB & C | C & notD
]
}
function precision(f) {
if (!isFinite(f)) return 0;
let e = 1, p = 0;
while (Math.round(f * e) / e !== f) { e *= 10; p++; }
return p;
}
let display = setupDisplay(height);
const output = function(value) {
const prec = precision(value);
const length = (Math.log10(Math.abs(value)) | 0) + prec + 1;
let d = Math.abs(value) * 10 ** prec;
let id, x, fillSegment, fillDot;
let innerHTML = sign ? `<use xlink:href="#g" fill=${value < 0 ? lit : unlit} />`: "";
for (let i = 0; i < digits; ++i) {
x = (digits - i - (sign ? 0 : 1)) * (50 + padding);
fillDot = i === prec && prec > 0 ? lit : unlit;
innerHTML +=`
<g transform="translate(${x},0) skewX(${skew})" >
${digitTo7Segment(d % 10).map((isLit, j) => {
id = String.fromCharCode(0x61 + j);
fillSegment = isLit && isFinite(value) && i < length ? lit : unlit;
return `<use xlink:href="#${id}" fill=${fillSegment} />`;
})}
<use xlink:href="#dot" fill=${fillDot} />
</g>`;
d = Math.floor(d / 10);
}
display.querySelector("#digits").innerHTML = innerHTML;
return display;
}
output.digits = function(n) {
if (arguments.length) {
digits = n;
display = setupDisplay(height);
return output;
}
return digits;
}
output.height = function(i) {
if (arguments.length) {
height = i;
display = setupDisplay(height);
return output;
}
return height;
}
output.sign = function(bool) {
if (arguments.length) {
sign = bool;
display = setupDisplay(height);
return output;
}
return digits;
}
output.padding = function(p) {
if (arguments.length) {
padding = p;
display = setupDisplay(height);
return output;
}
return padding;
}
output.lit = function(l) {
return arguments.length ? (lit = l, output) : lit;
}
output.unlit = function(u) {
return arguments.length ? (unlit = u, output) : unlit;
}
output.background = function(b) {
if (arguments.length) {
background = b;
display = setupDisplay(height);
return output;
}
return background;
}
output.skew = function(s) {
return arguments.length ? (skew = s, output) : skew;
}
return output;
}