/** * * Run `yarn global add canvas` first */ const { createCanvas } = require("canvas"); const fs = require("fs"); const path = require("path"); const outputFolder = path.join(__dirname, "..", "wires", "sets"); const dimensions = 192; const lineSize = 14; const lowerLineSize = 32; const variants = { first: "#61ef6f", second: "#f0bd65", third: "#5fb2f1", conflict: "#f74c4c", }; function hexToRGB(h) { let r = 0, g = 0, b = 0; // 3 digits if (h.length == 4) { r = "0x" + h[1] + h[1]; g = "0x" + h[2] + h[2]; b = "0x" + h[3] + h[3]; // 6 digits } else if (h.length == 7) { r = "0x" + h[1] + h[2]; g = "0x" + h[3] + h[4]; b = "0x" + h[5] + h[6]; } return [+r, +g, +b]; } function RGBToHSL(r, g, b) { // Make r, g, and b fractions of 1 r /= 255; g /= 255; b /= 255; // Find greatest and smallest channel values let cmin = Math.min(r, g, b), cmax = Math.max(r, g, b), delta = cmax - cmin, h = 0, s = 0, l = 0; // Calculate hue // No difference if (delta == 0) h = 0; // Red is max else if (cmax == r) h = ((g - b) / delta) % 6; // Green is max else if (cmax == g) h = (b - r) / delta + 2; // Blue is max else h = (r - g) / delta + 4; h = Math.round(h * 60); // Make negative hues positive behind 360° if (h < 0) h += 360; // Calculate lightness l = (cmax + cmin) / 2; // Calculate saturation s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); // Multiply l and s by 100 s = +(s * 100).toFixed(1); l = +(l * 100).toFixed(1); return [h, s, l]; } function HSLToRGB(h, s, l) { // Must be fractions of 1 s /= 100; l /= 100; let c = (1 - Math.abs(2 * l - 1)) * s, x = c * (1 - Math.abs(((h / 60) % 2) - 1)), m = l - c / 2, r = 0, g = 0, b = 0; if (0 <= h && h < 60) { r = c; g = x; b = 0; } else if (60 <= h && h < 120) { r = x; g = c; b = 0; } else if (120 <= h && h < 180) { r = 0; g = c; b = x; } else if (180 <= h && h < 240) { r = 0; g = x; b = c; } else if (240 <= h && h < 300) { r = x; g = 0; b = c; } else if (300 <= h && h < 360) { r = c; g = 0; b = x; } r = Math.round((r + m) * 255); g = Math.round((g + m) * 255); b = Math.round((b + m) * 255); return [r, g, b]; } async function run() { console.log("Running"); const promises = []; for (const variantId in variants) { const variantColor = variants[variantId]; const variantHSL = RGBToHSL(...hexToRGB(variantColor)); const darkenedColor = HSLToRGB(variantHSL[0], variantHSL[1] - 15, variantHSL[2] - 20); const hexDarkenedColor = "rgb(" + darkenedColor.join(",") + ")"; console.log(variantColor, "->", hexToRGB(variantColor), variantHSL, "->", darkenedColor); const parts = { forward: [[0.5, 0, 0.5, 1]], turn: [ [0.5, 0.5, 0.5, 1], [0.5, 0.5, 1, 0.5], ], split: [ [0.5, 0.5, 0.5, 1], [0, 0.5, 1, 0.5], ], cross: [ [0, 0.5, 1, 0.5], [0.5, 0, 0.5, 1], ], }; for (const partId in parts) { const partLines = parts[partId]; const canvas = createCanvas(dimensions, dimensions); const context = canvas.getContext("2d"); context.quality = "best"; context.clearRect(0, 0, dimensions, dimensions); const lineCanvas = createCanvas(dimensions, dimensions); const lineContext = lineCanvas.getContext("2d"); lineContext.quality = "best"; lineContext.clearRect(0, 0, dimensions, dimensions); lineContext.strokeStyle = hexDarkenedColor; lineContext.lineWidth = lowerLineSize; lineContext.lineCap = "square"; lineContext.imageSmoothingEnabled = false; // Draw lower lines partLines.forEach(([x1, y1, x2, y2]) => { lineContext.beginPath(); lineContext.moveTo(x1 * dimensions, y1 * dimensions); lineContext.lineTo(x2 * dimensions, y2 * dimensions); lineContext.stroke(); }); context.globalAlpha = 0.4; context.drawImage(lineCanvas, 0, 0, dimensions, dimensions); context.globalAlpha = 1; context.imageSmoothingEnabled = false; context.lineCap = "square"; context.strokeStyle = variantColor; context.lineWidth = lineSize; // Draw upper lines partLines.forEach(([x1, y1, x2, y2]) => { context.beginPath(); context.moveTo(x1 * dimensions, y1 * dimensions); context.lineTo(x2 * dimensions, y2 * dimensions); context.stroke(); }); const out = fs.createWriteStream(path.join(outputFolder, variantId + "_" + partId + ".png")); const stream = canvas.createPNGStream(); stream.pipe(out); promises.push(new Promise(resolve => stream.on("end", resolve))); } } console.log("Waiting for completion"); await Promise.all(promises); console.log("Done!"); } run();