Public
Edited
Apr 11, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof colorWheelCIECAM02 = {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 400;

const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = Math.min(centerX, centerY) * 0.8;
const dotRadius = 10;
function hexToJCh(hex) {
const rgb = d3.rgb(hex);
const jch = d3cam.jch(rgb); // Using d3-cam02 for conversion
return jch;
}
hexColors.forEach((hex, index) => {
const jch = hexToJCh(hex);
const angle = (jch.h * Math.PI) / 180; // CIECAM02 hue to angle
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);

// Draw the color dot
context.beginPath();
context.arc(x, y, dotRadius, 0, 2 * Math.PI, false);
context.fillStyle = hex;
context.fill();
});
return canvas;
}

Insert cell
hexJCh = hexColors.map(hex => hexToJCh(hex))/* .map(jch => jch.rgb()) */
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3cam = require("https://unpkg.com/d3-cam02@0.1.5/build/d3-cam02.js")
Insert cell
Insert cell
Insert cell
Insert cell
selectedColorIndex = colorNameToIndexMap[selectedColor] - 5
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
render(() => {
return bf.Bluefish(
bf.StackV({spacing: 32},
bf.Text({ 'font-size': 48 }, 'Complementary Colors'),
bf.StackV({spacing: 24}, ForEach([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], (i, _) => {
return (
bf.Background({ padding: 20, background: bf.Rect( { fill: 'none', stroke: '#D4D4D4', 'stroke-width': 1, rx: 3})},
bf.StackV({spacing: 8},
ColorSwatch({ colors: groupedColors[i+5]}),
ColorSwatch({ colors: groupedColors[complementaryColors[i][1]+5]}),
)
)
)
}))
)
)
})
Insert cell
Insert cell
groupedColors[0]
Insert cell
ForEach = (list, callBack) => {
return h(For, { each: list }, callBack);
}
Insert cell
For = solid.For
Insert cell
solid = import("https://cdn.jsdelivr.net/npm/solid-js/+esm");
Insert cell
h = h_pkg.default
Insert cell
h_pkg = import("https://cdn.jsdelivr.net/npm/solid-js/h/+esm");
Insert cell
render = (component) => {
let root = htl.html`<div>`;
web.render(component, root);
return root;
}
Insert cell
web = import("https://cdn.jsdelivr.net/npm/solid-js/web/+esm");
Insert cell
bf = bluefish.Hyperscript
Insert cell
bluefish = import("https://cdn.jsdelivr.net/npm/@bluefish-js/solid@0.0.17/+esm")
Insert cell
JSON.stringify(colorsList)
Insert cell
Insert cell
Insert cell
groupedColors = {
// Assuming 'colors' is your initial array of color objects
const colors = colorsList;
const grouped = colors.reduce((acc, color) => {
// Extract the base color name (e.g., "SLATE" from "SLATE 50")
const baseName = color.name.split(' ')[0];
if (!acc[baseName]) {
acc[baseName] = []; // Initialize an array if it doesn't exist
}
acc[baseName].push(color); // Push the color object to the correct array
return acc;
}, {});

// Convert the grouped object into a 2D array
const result = Object.keys(grouped).map(baseName => grouped[baseName]);
return result;
}

Insert cell
function hexToJCh(hex) {
const rgb = d3.rgb(hex);
const jch = d3cam.jch(rgb); // Using d3-cam02 for conversion
return jch;
}
Insert cell
jchColors = hexColors.map((hex) => {
return hexToJCh(hex);
})
Insert cell
function findClosestColor(hue, colorListJCCh) {
let index = -1;
let closestColor = null;
let smallestHueDifference = Infinity;
jchColors.forEach((testColor, i) => {
let hueDifference = Math.abs(testColor.h - hue);
// Since hue is a circular metric, we need to take the shortest path around the circle
hueDifference = hueDifference > 180 ? 360 - hueDifference : hueDifference;
if (hueDifference < smallestHueDifference) {
smallestHueDifference = hueDifference;
closestColor = testColor;
index = i;
}
});
return [closestColor, index]
}
Insert cell
complementaryColors = jchColors.map((jch) => {
const complementaryHue = (jch.h + 180) % 360;
return findClosestColor(complementaryHue);
})
Insert cell
splitComplementaryColors = jchColors.map((jch) => {
const splitComplementaryHue1 = (jch.h + 180 - 30) % 360;
const splitComplementaryHue2 = (jch.h + 180 + 30) % 360;
return [findClosestColor(splitComplementaryHue1), findClosestColor(splitComplementaryHue2)];
})
Insert cell
triadicColors = jchColors.map((jch) => {
const triadicHue1 = (jch.h + 120) % 360;
const triadicHue2 = (jch.h + 240) % 360; // equivalent to subtracting 120 degrees
return [findClosestColor(triadicHue1), findClosestColor(triadicHue2)];
})
Insert cell
tetradicColors = jchColors.map((jch) => {
const tetradicHue1 = (jch.h + 180) % 360; // The complementary hue
const tetradicHue2 = (jch.h + 60) % 360; // Adjacent to the original hue
const tetradicHue3 = (jch.h + 240) % 360; // Adjacent to the complementary hue
return [findClosestColor(tetradicHue1), findClosestColor(tetradicHue2), findClosestColor(tetradicHue3)];
})
Insert cell
analogousColors = jchColors.map((jch) => {
const analogousHue1 = (jch.h + 15) % 360;
const analogousHue2 = (jch.h - 15) % 360;
return [findClosestColor(analogousHue1), findClosestColor(analogousHue2)];
})
Insert cell
// h/t Josh Horowitz
style = html`<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Alegreya+Sans:ital,wght@0,100;0,300;0,400;0,500;0,700;0,800;0,900;1,100;1,300;1,400;1,500;1,700;1,800;1,900&family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">`
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more