summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/colors.js143
-rw-r--r--tools/gencolor.js27
-rw-r--r--tools/gentextures.sh4
-rw-r--r--tools/parse-layout.js167
-rw-r--r--tools/rock_overlay@2x.pngbin0 -> 160515 bytes
5 files changed, 341 insertions, 0 deletions
diff --git a/tools/colors.js b/tools/colors.js
new file mode 100644
index 0000000..16378f7
--- /dev/null
+++ b/tools/colors.js
@@ -0,0 +1,143 @@
+"use strict"
+
+function rgb_from_any(color) {
+ if (typeof color === "string")
+ color = parse_hex(color)
+ switch (color.mode) {
+ case "rgb": return color
+ case "lrgb": return rgb_from_lrgb(color)
+ case "oklab": return rgb_from_oklab(color)
+ }
+}
+
+function lrgb_from_any(color) {
+ switch (color.mode) {
+ case "rgb": return lrgb_from_rgb(color)
+ case "lrgb": return color
+ case "oklab": return lrgb_from_oklab(color)
+ }
+}
+
+function oklab_from_any(color) {
+ switch (color.mode) {
+ case "rgb": return oklab_from_rgb(color)
+ case "lrgb": return oklab_from_lrgb(color)
+ case "oklab": return color
+ }
+}
+
+function format_hex(color) {
+ let { r, g, b } = rgb_from_any(color)
+ let adj = 1
+ r = Math.round(Math.max(0, Math.min(1, r)) * 255)
+ g = Math.round(Math.max(0, Math.min(1, g)) * 255)
+ b = Math.round(Math.max(0, Math.min(1, b)) * 255)
+ let x = (r << 16) | (g << 8) | b
+ return "#" + x.toString(16).padStart(6, "0")
+}
+
+function parse_hex(str) {
+ let x = parseInt(str.substring(1), 16)
+ return {
+ mode: "rgb",
+ r: ((x >> 16) & 255) / 255.0,
+ g: ((x >> 8) & 255) / 255.0,
+ b: ((x) & 255) / 255.0
+ }
+}
+
+function lrgb_from_rgb({ r, g, b }) {
+ function to_linear(c) {
+ let ac = Math.abs(c)
+ if (ac < 0.04045)
+ return c / 12.92
+ return (Math.sign(c) || 1) * Math.pow((ac + 0.055) / 1.055, 2.4)
+ }
+ return {
+ mode: "lrgb",
+ r: to_linear(r),
+ g: to_linear(g),
+ b: to_linear(b)
+ }
+}
+
+function rgb_from_lrgb({ r, g, b }) {
+ function from_linear(c) {
+ let ac = Math.abs(c)
+ if (ac > 0.0031308)
+ return (Math.sign(c) || 1) * (1.055 * Math.pow(ac, 1 / 2.4) - 0.055)
+ return c * 12.92
+ }
+ return {
+ mode: "rgb",
+ r: from_linear(r),
+ g: from_linear(g),
+ b: from_linear(b)
+ }
+}
+
+function oklab_from_lrgb({ r, g, b }) {
+ let L = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b)
+ let M = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b)
+ let S = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b)
+ return {
+ mode: "oklab",
+ l: 0.2104542553 * L + 0.793617785 * M - 0.0040720468 * S,
+ a: 1.9779984951 * L - 2.428592205 * M + 0.4505937099 * S,
+ b: 0.0259040371 * L + 0.7827717662 * M - 0.808675766 * S
+ }
+}
+
+function lrgb_from_oklab({ l, a, b }) {
+ let L = Math.pow(l + 0.3963377774 * a + 0.2158037573 * b, 3)
+ let M = Math.pow(l - 0.1055613458 * a - 0.0638541728 * b, 3)
+ let S = Math.pow(l - 0.0894841775 * a - 1.291485548 * b, 3)
+ return {
+ mode: "lrgb",
+ r: +4.0767416621 * L - 3.3077115913 * M + 0.2309699292 * S,
+ g: -1.2684380046 * L + 2.6097574011 * M - 0.3413193965 * S,
+ b: -0.0041960863 * L - 0.7034186147 * M + 1.707614701 * S
+ }
+}
+
+function oklab_from_rgb(rgb) {
+ return oklab_from_lrgb(lrgb_from_rgb(rgb))
+}
+
+function rgb_from_oklab(oklab) {
+ return rgb_from_lrgb(lrgb_from_oklab(oklab))
+}
+
+function format_hsl(rgb) {
+ let { r, g, b } = rgb_from_any(color)
+ let cmin = Math.min(r, g, b)
+ let cmax = Math.max(r, g, b)
+ let delta = cmax - cmin
+ let h = 0, s = 0, l = 0
+
+ if (delta == 0)
+ h = 0
+ else if (cmax == r)
+ h = ((g - b) / delta) % 6
+ else if (cmax == g)
+ h = (b - r) / delta + 2
+ else
+ h = (r - g) / delta + 4
+
+ h = Math.round(h * 60)
+
+ if (h < 0)
+ h += 360
+
+ l = (cmax + cmin) / 2
+
+ s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1))
+
+ s = Math.round(s * 100)
+ l = Math.round(l * 100)
+
+ return "hsl(" + h + "," + s + "%," + l + "%)"
+}
+
+if (typeof module === "object")
+ module.exports = { format_hex, parse_hex, rgb_from_any, lrgb_from_any, oklab_from_any, format_hsl }
diff --git a/tools/gencolor.js b/tools/gencolor.js
new file mode 100644
index 0000000..ea804e6
--- /dev/null
+++ b/tools/gencolor.js
@@ -0,0 +1,27 @@
+"use strict"
+
+const colors = require("./colors.js")
+
+function lerp(v0, v1, t) {
+ return v0 + t * (v1 - v0)
+}
+
+function lerp_rgb(rgb, x=0, t=0) {
+ let lab = colors.oklab_from_any(colors.parse_hex(rgb))
+ lab.l = lerp(lab.l, x, t)
+ return colors.format_hex(lab)
+}
+
+function make_colors(rgb, sel) {
+ let bg = lerp_rgb(rgb, 1.0, 0.0)
+ let hi = lerp_rgb(rgb, 1.0, 0.4)
+ let sh = lerp_rgb(rgb, 0.2, 0.4)
+ let bd = lerp_rgb(rgb, 0.2, 0.8)
+ console.log(sel + ` { background-color: ${bg}; border-color: ${hi} ${sh} ${sh} ${hi}; box-shadow: 0 0 0 1px ${bd}, 0px 1px 4px #0008; }`)
+}
+
+make_colors("#bbbbbb", ".token.white")
+make_colors("#ef0500", ".token.red")
+make_colors("#da997b", ".token.pink")
+make_colors("#bbb079", ".token.brown")
+make_colors("#a6a6a8", ".token.gray")
diff --git a/tools/gentextures.sh b/tools/gentextures.sh
new file mode 100644
index 0000000..b9eabb9
--- /dev/null
+++ b/tools/gentextures.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+pngtopnm tools/rock_overlay@2x.png | pnmnorm | pgmtoppm "#000000-#111111" | ppmquant 2 | pnmtopng > tools/texture_mask.png
+convert tools/texture_mask.png -alpha copy -fx '#fff' images/texture.png
+zopflipng -m -y images/texture.png images/texture.png
diff --git a/tools/parse-layout.js b/tools/parse-layout.js
new file mode 100644
index 0000000..5dcf4f8
--- /dev/null
+++ b/tools/parse-layout.js
@@ -0,0 +1,167 @@
+const fs = require("fs")
+
+const { round, floor, ceil } = Math
+
+let boxes = {}
+let nodes = []
+let edges = []
+let mode, name, x, y, w, h, cx, cy, rx, ry, x2, y2
+let scale = 1
+
+function flush() {
+ if (mode === 'path') {
+ edges.push({ x1: x, y1: y, x2, y2 })
+ }
+ if (mode === 'rect') {
+ boxes[name] = [ x * scale |0, y * scale |0, w * scale |0, h * scale |0 ]
+ nodes.push({ name, x: x + w/2, y: y + h/2 })
+ }
+ if (mode === 'circle') {
+ x = cx - rx
+ y = cy - ry
+ w = rx * 2
+ h = ry * 2
+ boxes[name] = [ x * scale |0, y * scale |0, w * scale |0, h * scale |0 ]
+ nodes.push({ name, x: cx, y: cy })
+ }
+ x = y = x2 = y2 = w = h = cx = cy = rx = ry = 0
+ name = null
+}
+
+function parse_path_data(path) {
+ let cx = 0
+ let cy = 0
+ let abs = 0
+ for (let i = 0; i < path.length;) {
+ switch (path[i]) {
+ case 'M':
+ x = cx = Number(path[i+1])
+ y = cy = Number(path[i+2])
+ i += 3
+ abs = true
+ break
+ case 'm':
+ x = cx = cx + Number(path[i+1])
+ y = cy = cy + Number(path[i+2])
+ i += 3
+ abs = false
+ break
+ case 'C':
+ x2 = cx = Number(path[i+5])
+ y2 = cy = Number(path[i+6])
+ i += 7
+ abs = true
+ break
+ case 'L':
+ i += 1
+ abs = true
+ break
+ case 'H':
+ x2 = cx = Number(path[i+1])
+ i += 2
+ abs = true
+ break
+ case 'V':
+ y2 = cy = Number(path[i+1])
+ i += 2
+ abs = true
+ break
+ case 'c':
+ x2 = cx = cx + Number(path[i+5])
+ y2 = cy = cy + Number(path[i+6])
+ i += 7
+ break
+ case 'l':
+ i += 1
+ abs = false
+ break
+ case 'h':
+ x2 = cx = cx + Number(path[i+1])
+ i += 2
+ abs = false
+ break
+ case 'v':
+ y2 = cy = cy + Number(path[i+1])
+ i += 2
+ abs = false
+ break
+ default:
+ if (abs) {
+ x2 = cx = Number(path[i+0])
+ y2 = cy = Number(path[i+1])
+ } else {
+ x2 = cx = cx + Number(path[i+0])
+ y2 = cy = cy + Number(path[i+1])
+ }
+ i += 2
+ break
+ }
+ }
+}
+
+for (let line of fs.readFileSync("tools/layout.svg", "utf-8").split("\n")) {
+ line = line.trim()
+ if (line.startsWith("<rect")) {
+ flush()
+ mode = "rect"
+ x = y = w = h = 0
+ } else if (line.startsWith("<ellipse") || line.startsWith("<circle")) {
+ flush()
+ mode = "circle"
+ cx = cy = rx = ry = 0
+ } else if (line.startsWith("<path")) {
+ flush()
+ mode = "path"
+ } else if (line.startsWith('x="'))
+ x = round(Number(line.split('"')[1]))
+ else if (line.startsWith('y="'))
+ y = round(Number(line.split('"')[1]))
+ else if (line.startsWith('width="'))
+ w = round(Number(line.split('"')[1]))
+ else if (line.startsWith('height="'))
+ h = round(Number(line.split('"')[1]))
+ else if (line.startsWith('cx="'))
+ cx = round(Number(line.split('"')[1]))
+ else if (line.startsWith('cy="'))
+ cy = round(Number(line.split('"')[1]))
+ else if (line.startsWith('r="'))
+ rx = ry = round(Number(line.split('"')[1]))
+ else if (line.startsWith('rx="'))
+ rx = round(Number(line.split('"')[1]))
+ else if (line.startsWith('ry="'))
+ ry = round(Number(line.split('"')[1]))
+ else if (line.startsWith('inkscape:label="'))
+ name = line.split('"')[1]
+ else if (line.startsWith('d="'))
+ parse_path_data(line.split('"')[1].split(/[ ,]/))
+}
+
+flush()
+
+console.log("const boxes = {")
+for (let key in boxes)
+ console.log("\t\"" + key + "\": " + JSON.stringify(boxes[key]) + ",")
+console.log("}")
+
+function find_closest_node(x, y) {
+ let nd = Infinity, nn = null
+
+ for (let n of nodes) {
+ let d = Math.hypot(n.x - x, n.y - y)
+ if (d < nd) {
+ nd = d
+ nn = n.name
+ }
+ }
+
+ if (!nn)
+ console.log("NOT FOUND", x, y)
+
+ return nn
+}
+
+for (let e of edges) {
+ let n1 = find_closest_node(e.x1, e.y1)
+ let n2 = find_closest_node(e.x2, e.y2)
+ console.log(`edge("${n1}", "${n2}", ${e.x1|0}, ${e.y1|0}, ${e.x2|0}, ${e.y2|0})`)
+}
diff --git a/tools/rock_overlay@2x.png b/tools/rock_overlay@2x.png
new file mode 100644
index 0000000..8710ab6
--- /dev/null
+++ b/tools/rock_overlay@2x.png
Binary files differ