qmk-userspace/layers2svg.php

286 lines
7.5 KiB
PHP

<?php
const UNIT = 50;
const GAP = 3;
$infoFile = "../../keyboards/lily58/rev1/info.json";
$info = json_decode(file_get_contents($infoFile));
$positions = $info->layouts->LAYOUT->layout;
function determineTotalWidthAndHeight($positions): array
{
// Determine total width and height.
$width = $height = 0;
foreach ($positions as $i => $pos) {
$w = ($pos->w ?? 1) * UNIT;
$h = ($pos->h ?? 1) * UNIT;
$x = ($pos->x > 6 ? $pos->x - 2 : $pos->x) * UNIT;
$y = $pos->y * UNIT;
$newWidth = $x + $w + GAP;
$newHeight = $y + $h + GAP;
if ($newWidth > $width) {
$width = $newWidth;
}
if ($newHeight > $height) {
// Key 53/54 are the inner thumb keys, which are typically 1.5u.
$height = $newHeight;
}
}
return [$height, $width + UNIT - GAP];
}
[$height, $width] = determineTotalWidthAndHeight($positions);
$layersToRender = ["QUERTY", "COLEMAK", "NAVIGATION", "NUMBERS", "SYMBOLS", "FUNCTION", "MOUSE"];
$colors = [
"COLEMAK" => "darkorange",
"NAVIGATION" => "mediumaquamarine",
"NUMBERS" => "chocolate",
"SYMBOLS" => "thistle",
"FUNCTION" => "deepskyblue",
"MOUSE" => "navajowhite",
];
$layoutFile = "layout.h";
$file = file($layoutFile);
$currentDefine = null;
$continue = false;
$defineBuffer = [];
$defines = [];
$layerDefines = [];
$inEnum = false;
foreach ($file as $line) {
$line = trim($line);
if (str_starts_with($line, "enum ")) {
$inEnum = true;
continue;
}
if ($inEnum && $line === "};") {
$inEnum = false;
continue;
}
if ($inEnum) {
continue;
}
if (
$line === ""
|| str_starts_with($line, "//")
|| str_starts_with($line, "/*")
|| str_starts_with($line, "#pragma")
|| str_starts_with($line, "#include")
|| str_starts_with($line, "#ifndef")
|| str_starts_with($line, "#endif")
) {
continue;
}
if (!$continue && $currentDefine !== null) {
if (is_array($defineBuffer) && count($defineBuffer) < 2) {
$defineBuffer = implode("", $defineBuffer);
}
if (in_array($currentDefine, $layersToRender)) {
$layerDefines[$currentDefine] = $defineBuffer;
} else {
$defines[$currentDefine] = $defineBuffer;
}
$currentDefine = null;
$defineBuffer = [];
$continue = false;
}
if (str_starts_with($line, "#define")) {
[, $currentDefine, $line] = preg_split("/\s+/", $line, 3);
}
$continue = str_ends_with($line, "\\");
$line = trim(rtrim($line, "\\"));
if (!$line) {
continue;
}
$bits = preg_split("/\s+/", $line);
foreach ($bits as $bit) {
$defineBuffer[] = rtrim($bit, ",");
}
unset($bit);
}
unset($bit, $bits, $currentDefine, $continue, $defineBuffer, $line);
$layers = [];
foreach ($layersToRender as $layerIndex => $layerName) {
if (!array_key_exists($layerName, $layerDefines)) {
continue;
}
$layer =& $layerDefines[$layerName];
$elements = [];
foreach ($layer as $keyIndex => $key) {
$color = "";
if (array_key_exists($key, $defines)) {
$key = $defines[$key];
}
if ($key === "KC_NO") {
$key = [""];
} elseif ($key === "KC_TRANS") {
$key = ["TRNS"];
} elseif ($key === "CW_TOGG") {
$key = ["Caps", "word"];
} elseif (str_starts_with($key, "KC_")) {
$key = [str_replace("KC_", "", $key)];
} elseif (preg_match("/^LT\((\d+),KC_(\w+)\)$/", $key, $m)) {
$toLayer = $layersToRender[$m[1]];
$key = [
$m[2],
"" . substr($toLayer, 0, 3),
];
$color = $colors[$toLayer] ?? "";
} elseif (preg_match("/^TG\((\d+)\)$/", $key, $m)) {
$toLayer = $layersToRender[$m[1]];
$key = ["" . substr($toLayer, 0, 3)];
$color = $colors[$toLayer] ?? "";
} elseif (preg_match("/^DF\((\d+)\)$/", $key, $m)) {
$toLayer = $layersToRender[$m[1]];
$key = ["D→" . substr($toLayer, 0, 3)];
$color = $colors[$toLayer] ?? "";
} elseif (preg_match("/^[RL](GUI|ALT|CTL|SFT)_T\(KC_(\w+)\)$/", $key, $m)) {
$key = [
$m[2],
$m[1],
];
} elseif (str_contains($key, "(")) {
$key = explode("(", $key);
$key[0] .= "(";
} else {
$key = [$key];
}
$pos =& $positions[$keyIndex];
if ($pos === null) {
error_log("encountered null pos at keyIndex $keyIndex in layer $layerName.");
continue;
}
$w = ($pos->w ?? 1) * UNIT;
$h = ($pos->h ?? 1) * UNIT;
$x = ($pos->x > 6 ? $pos->x - 2 : $pos->x) * (UNIT + GAP);
$y = $pos->y * (UNIT + GAP);
$tx = $x + GAP;
$ty = $y + 3 * GAP;
$key = implode(
"\n",
array_map(static fn(string $line) => "<tspan x=\"{$tx}\" dy=\"10\">{$line}</tspan>", $key)
);
$class = $color ? " {$color}" : "";
$elements[] = <<<ELEMENT
<g id="layer_{$layerIndex}_key_{$keyIndex}">
<rect class="key{$class}" x="{$x}" y="{$y}" width="{$w}" height="{$h}" rx="3" />
<text class="key-label" x="{$tx}" y="{$ty}">
{$key}
</text>
</g>
ELEMENT;
}
$elements = implode("\n", $elements);
$layerWidth = $width + 10;
$layerHeigth = $height + UNIT;
$offset = $layerIndex * ($height + GAP + UNIT) + UNIT;
$layers[] = <<<LAYER
<g class="layer" id="layer-{$layerName}" transform="translate(0,{$offset})">
<rect class="layer-border" height="{$layerHeigth}" width="{$layerWidth}" rx="10" />
<text class="layer-header" transform="translate(10,20)">Layer {$layerName}</text>
<g class="layer-keys" transform="translate(10,30)">
$elements
</g>
</g>
LAYER;
}
$layers = implode("\n", $layers);
/*echo '<?xml version="1.0" standalone="no" ?>'*/
?>
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 <?= $width + 10 ?> <?= $offset + $layerHeigth ?>">
<style>
g.layer .layer-border {
stroke-width: 1.5pt;
stroke: aliceblue;
fill: white;
}
.key {
fill: #eeeeee;
fill-opacity: 0.05;
stroke: #444444;
stroke-width: 1.2pt;
}
.key.navajowhite {
stroke: navajowhite;
}
.key.mediumaquamarine {
stroke: mediumaquamarine;
}
.key.chocolate {
stroke: chocolate;
}
.key.thistle {
stroke: thistle;
}
.key.deepskyblue {
stroke: deepskyblue;
}
/*.key.darkorange {*/
/* stroke: darkorange;*/
/*}*/
.layer.layer-NAVIGATION .key {
fill: mediumaquamarine;
}
.layer.layer-NUMBERS .key {
fill: chocolate;
}
.layer.layer-SYMBOLS .key {
fill: thistle;
}
.layer.layer-FUNCTION .key {
fill: deepskyblue;
}
.layer.layer-MOUSE .key {
fill: navajowhite;
}
/*.layer.layer-COLEMAK .key {*/
/* fill: darkorange;*/
/*}*/
.layer-border {
fill: none;
stroke-width: 1.5pt;
}
.layer-header {
font-size: 10pt;
font-weight: bold;
}
text {
font-size: 7pt;
text-align: left;
}
</style>
<?= $layers ?>
</svg>