2023-10-22 16:10:53 +02:00
|
|
|
import { tag, px } from "./svg";
|
2024-02-21 05:01:46 +01:00
|
|
|
import * as L from "./layout";
|
|
|
|
import * as V from "./vec2";
|
2023-10-22 16:10:53 +02:00
|
|
|
import {
|
|
|
|
KeyboardKey,
|
|
|
|
KeySymbol,
|
|
|
|
Layout,
|
|
|
|
LayoutColorscheme,
|
2024-02-21 05:01:46 +01:00
|
|
|
LayoutMeasurements,
|
2023-10-22 16:10:53 +02:00
|
|
|
SpecialSymbols,
|
|
|
|
VisualKey,
|
|
|
|
} from "./types";
|
|
|
|
|
|
|
|
function textContents(input: KeySymbol): string {
|
|
|
|
if (input === SpecialSymbols.TL || input === SpecialSymbols.TR) return "■";
|
|
|
|
|
|
|
|
return input;
|
|
|
|
}
|
|
|
|
|
|
|
|
function renderKey(
|
|
|
|
visual: VisualKey,
|
|
|
|
key: KeyboardKey,
|
|
|
|
colorscheme: LayoutColorscheme,
|
2024-02-21 05:01:46 +01:00
|
|
|
measurements: LayoutMeasurements,
|
2023-10-22 16:10:53 +02:00
|
|
|
) {
|
2024-02-21 05:01:46 +01:00
|
|
|
const withPadding = {
|
|
|
|
position: V.add(visual.position, measurements.keyPadding),
|
|
|
|
size: V.add(visual.size, -2 * measurements.keyPadding),
|
|
|
|
};
|
|
|
|
|
2023-10-22 16:10:53 +02:00
|
|
|
const centerX = visual.position[0] + visual.size[0] / 2;
|
|
|
|
const centerY = visual.position[1] + visual.size[1] / 2;
|
|
|
|
const textAttribs = {
|
|
|
|
"text-anchor": "middle",
|
2024-02-21 05:01:46 +01:00
|
|
|
"dominant-baseline": "central",
|
2023-10-22 16:10:53 +02:00
|
|
|
"font-family": "Helvetica",
|
|
|
|
};
|
|
|
|
|
|
|
|
const textColor = (input: KeySymbol, _default: string): string => {
|
|
|
|
if (input === SpecialSymbols.TL) return colorscheme.tlLayerColor;
|
|
|
|
if (input === SpecialSymbols.TR) return colorscheme.trLayerColor;
|
|
|
|
return _default;
|
|
|
|
};
|
|
|
|
|
|
|
|
return tag(
|
|
|
|
"g",
|
|
|
|
{
|
|
|
|
transform:
|
|
|
|
visual.angle && visual.angle !== 0
|
|
|
|
? `rotate(${visual.angle}, ${centerX}, ${centerY})`
|
2024-02-21 05:01:46 +01:00
|
|
|
: "",
|
2023-10-22 16:10:53 +02:00
|
|
|
},
|
|
|
|
[
|
|
|
|
tag("rect", {
|
2024-02-21 05:01:46 +01:00
|
|
|
width: px(withPadding.size[0]),
|
|
|
|
height: px(withPadding.size[1]),
|
|
|
|
x: withPadding.position[0],
|
|
|
|
y: withPadding.position[1],
|
|
|
|
rx: measurements.keyCornerRadius,
|
2023-10-22 16:10:53 +02:00
|
|
|
fill: colorscheme.keyFill,
|
|
|
|
stroke: colorscheme.keyStroke,
|
2024-02-21 05:01:46 +01:00
|
|
|
"stroke-width": px(measurements.keyStrokeWidth),
|
2023-10-22 16:10:53 +02:00
|
|
|
}),
|
|
|
|
tag(
|
|
|
|
"text",
|
|
|
|
{
|
|
|
|
x: centerX,
|
|
|
|
y: centerY,
|
2024-02-21 05:01:46 +01:00
|
|
|
textLength: px(withPadding.size[1] / 2),
|
2023-10-22 16:10:53 +02:00
|
|
|
fill: textColor(key.main, colorscheme.mainLayerColor),
|
|
|
|
...textAttribs,
|
|
|
|
},
|
|
|
|
textContents(key.main),
|
|
|
|
),
|
|
|
|
tag(
|
|
|
|
"text",
|
|
|
|
{
|
2024-02-21 05:01:46 +01:00
|
|
|
x: withPadding.position[0] + withPadding.size[0] / 6,
|
|
|
|
y: withPadding.position[1] + withPadding.size[1] / 6,
|
2023-10-22 16:10:53 +02:00
|
|
|
fill: textColor(key.tlLayer, colorscheme.tlLayerColor),
|
|
|
|
"font-size": "66%",
|
|
|
|
...textAttribs,
|
2024-02-21 05:01:46 +01:00
|
|
|
"text-anchor": "start",
|
2023-10-22 16:10:53 +02:00
|
|
|
},
|
|
|
|
textContents(key.tlLayer),
|
|
|
|
),
|
|
|
|
tag(
|
|
|
|
"text",
|
|
|
|
{
|
2024-02-21 05:01:46 +01:00
|
|
|
x: withPadding.position[0] + (9 * withPadding.size[0]) / 10,
|
|
|
|
y: withPadding.position[1] + withPadding.size[1] / 6,
|
2023-10-22 16:10:53 +02:00
|
|
|
fill: textColor(key.trLayer, colorscheme.trLayerColor),
|
|
|
|
"font-size": "66%",
|
|
|
|
...textAttribs,
|
|
|
|
"text-anchor": "end",
|
|
|
|
},
|
|
|
|
textContents(key.trLayer),
|
|
|
|
),
|
|
|
|
tag(
|
|
|
|
"text",
|
|
|
|
{
|
2024-02-21 05:01:46 +01:00
|
|
|
x: withPadding.position[0] + withPadding.size[0] / 10,
|
|
|
|
y: withPadding.position[1] + (5 * withPadding.size[1]) / 6,
|
2023-10-22 16:10:53 +02:00
|
|
|
fill: textColor(key.blLayer, colorscheme.blLayerColor),
|
|
|
|
"font-size": "66%",
|
|
|
|
...textAttribs,
|
|
|
|
"text-anchor": "start",
|
|
|
|
},
|
|
|
|
textContents(key.blLayer),
|
|
|
|
),
|
|
|
|
].join("\n"),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function renderLayout(layout: Layout) {
|
|
|
|
return tag(
|
|
|
|
"svg",
|
|
|
|
{
|
|
|
|
viewBox: [
|
2024-02-21 05:01:46 +01:00
|
|
|
-layout.measurements.imagePadding,
|
|
|
|
-layout.measurements.imagePadding,
|
|
|
|
2 * layout.measurements.imagePadding +
|
|
|
|
layout.size[0] * layout.measurements.keySize,
|
|
|
|
2 * layout.measurements.imagePadding +
|
|
|
|
layout.size[1] * layout.measurements.keySize,
|
2023-10-22 16:10:53 +02:00
|
|
|
].join(" "),
|
|
|
|
xmlns: "http://www.w3.org/2000/svg",
|
|
|
|
"xmlns:xlink": "http://www.w3.org/1999/xlink",
|
|
|
|
},
|
|
|
|
layout.visual
|
|
|
|
.map((key, index) =>
|
2024-02-21 05:01:46 +01:00
|
|
|
renderKey(
|
|
|
|
L.scaleVisual(key, layout.measurements.keySize),
|
|
|
|
layout.keys[index],
|
|
|
|
layout.colorscheme,
|
|
|
|
layout.measurements,
|
|
|
|
),
|
2023-10-22 16:10:53 +02:00
|
|
|
)
|
|
|
|
.join("\n"),
|
|
|
|
);
|
|
|
|
}
|