Skip to content

Instantly share code, notes, and snippets.

@vorg
Created January 5, 2024 13:34
Show Gist options
  • Save vorg/d50ae6099bf5d9a8615e1765e5454ebe to your computer and use it in GitHub Desktop.
Save vorg/d50ae6099bf5d9a8615e1765e5454ebe to your computer and use it in GitHub Desktop.
risograph-grid.js
//assumes A1 sized Canvas 2245x3179
import {
fromRGBBytes,
toHex,
fromHex,
create as createColor,
toHSLuv,
toRGBBytes,
} from "pex-color";
import {
trilerp,
converter,
formatCss,
formatHex,
easingSmoothstep,
} from "culori";
import { vec4 } from "pex-math";
import spectral from "spectral.js";
// data from https://api.color.pizza/v1/?list=risograph
/** @type {NodesFn} */
export default (node, graph) => {
const triggerIn = node.triggerIn("in");
const triggerOut = node.triggerOut("out");
const dataIn = node.in("dataIn", null);
const dataOut = node.out("data");
let nodeInitialised = false;
const scale = 2;
const rgb = converter("rgb");
const RYB_CUBE = [
{ mode: "rgb", r: 248 / 255, g: 237 / 255, b: 220 / 255 }, // white
{
mode: "rgb",
r: 0.8901960784313725,
g: 0.1411764705882353,
b: 0.12941176470588237,
}, // red
{
mode: "rgb",
r: 0.9529411764705882,
g: 0.9019607843137255,
b: 0,
}, // yellow
{
mode: "rgb",
r: 0.9411764705882353,
g: 0.5568627450980392,
b: 0.10980392156862745,
}, // orange
{
mode: "rgb",
r: 0.08627450980392157,
g: 0.6,
b: 0.8549019607843137,
}, // blue
{
mode: "rgb",
r: 0.47058823529411764,
g: 0.13333333333333333,
b: 0.6666666666666666,
}, // violet
{
mode: "rgb",
r: 0,
g: 0.5568627450980392,
b: 0.3568627450980392,
}, // green
{ mode: "rgb", r: 29 / 255, g: 28 / 255, b: 28 / 255 }, // black
];
function ryb2rgb(coords) {
//const biased_coords = coords.map(t => t * t * (3 - 2 * t));
const r = easingSmoothstep(coords[0]);
const y = easingSmoothstep(coords[1]);
const b = easingSmoothstep(coords[2]);
return {
mode: "rgb",
r: trilerp(...RYB_CUBE.map((it) => it.r), r, y, b),
g: trilerp(...RYB_CUBE.map((it) => it.g), r, y, b),
b: trilerp(...RYB_CUBE.map((it) => it.b), r, y, b),
};
}
triggerIn.onTrigger = (props) => {
const { canvas, ctx2d } = props;
const data = dataIn.value;
if (!data) return;
ctx2d.fillStyle = "#FFFFFF";
ctx2d.fillRect(0, 0, canvas.width, canvas.height);
ctx2d.save();
try {
ctx2d.scale(scale, scale);
// // data.sort((a, b) => {
// // const ha = getHue(a);
// // const hb = getHue(b);
// // return ha - hb;
// // });
// let dx = 10;
// let dy = 10;
// data.forEach((color) => {
// if (!color.color_measurement) return;
// // if (getLightness(color) < 0.5 || getLightness(color) > 0.9) return;
// const { r, g, b } = color.color_measurement.rgb;
// ctx2d.fillStyle = `rgb(${r}, ${g}, ${b})`;
// ctx2d.fillRect(dx, dy, 160, 30);
// ctx2d.fillStyle = `#FFFFFF`;
// ctx2d.font = "20px Arial";
// ctx2d.fillText(color.color_name, dx, dy + 55);
// dx += 180;
// if (dx + 180 > canvas.width / scale) {
// dx = 10;
// dy += 80;
// }
// });
const colors = [
"Yellow",
"Bright Red",
"Fluorescent Pink",
"Fluorescent Orange",
"Aqua",
"Green",
"Teal",
"Blue",
"Black",
];
const colorsSlurp = [
"#FDDF0D",
"#EA3220",
"#FB74B6",
"#F97B46",
"#57B4D7",
"#238B44",
"#295B6D",
"#0D48B0",
"#202121",
];
ctx2d.globalCompositeOperation = "multiply";
// ctx2d.globalAlpha = 0.9;
colors.forEach((colorName, i) => {
const color = data.colors.find((c) => c.name == colorName);
const ryb = ryb2rgb([
color.rgb.r / 255,
color.rgb.g / 255,
color.rgb.b / 255,
]);
// console.log("ryb", ryb);
colors
.slice()
.reverse()
.forEach((colorName2, j) => {
const color2 = data.colors.find((c) => c.name == colorName2);
const ryb2 = ryb2rgb([
color2.rgb.r / 255,
color2.rgb.g / 255,
color2.rgb.b / 255,
]);
ctx2d.fillStyle = color.hex;
ctx2d.fillStyle = colorsSlurp[i];
// ctx2d.fillStyle = formatHex(ryb);
ctx2d.beginPath();
ctx2d.arc(100 + 113 * j, 300 + 150 * i, 30, 0, Math.PI * 2);
ctx2d.fill();
ctx2d.fillStyle = color2.hex;
ctx2d.fillStyle = colorsSlurp[colorsSlurp.length - j - 1];
// ctx2d.fillStyle = formatHex(ryb2);
ctx2d.beginPath();
ctx2d.arc(
100 + 113 * j - 2,
300 + 150 * i - 30,
30,
0,
Math.PI * 2,
);
ctx2d.fill();
// ctx2d.save();
// ctx2d.scale(1, 0.6);
// ctx2d.fillStyle = spectral.mix(color.hex, color2.hex, 0.5);
// // ctx2d.fillStyle = formatHex(ryb2);
// ctx2d.beginPath();
// ctx2d.arc(
// 100 + 113 * j - 2,
// ((300 + 150 * i - 15) * 1) / 0.6,
// 25,
// 0,
// Math.PI * 2,
// );
// ctx2d.fill();
// ctx2d.restore();
});
ctx2d.save();
ctx2d.translate(35, 300 + 150 * i - 15);
ctx2d.rotate(-Math.PI / 2);
ctx2d.fillStyle = color.hex;
ctx2d.textAlign = "center";
ctx2d.font = "16px Arial";
ctx2d.fillText(colorName.split(" ")[0].toUpperCase(), 0, 0);
ctx2d.fillText(colorName.split(" ")[1]?.toUpperCase() || "", 0, 20);
ctx2d.fillStyle = "#000000";
ctx2d.fillText(
`${color.rgb.r}, ${color.rgb.g}, ${color.rgb.b}`,
0,
canvas.width / 2 - 70,
);
ctx2d.restore();
});
} finally {
ctx2d.restore();
}
ctx2d.fillStyle = "#000000";
ctx2d.textAlign = "center";
ctx2d.font = "bold 80px Arial";
ctx2d.fillText("color.pizza", canvas.width / 2, 150);
ctx2d.fillText(
"Risograph Color Chart".toUpperCase(),
canvas.width / 2,
300,
);
triggerOut.trigger(props);
};
dataIn.onChange = () => {
if (!dataIn.value) return;
let data = dataIn.value;
console.log(data);
dataOut.setValue(data);
};
node.onReady = () => {
if (!nodeInitialised) {
nodeInitialised = true;
}
};
node.onDestroy = () => {};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment