Public
Edited
May 25, 2022
1 fork
Importers
23 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
colorPopulation = quantizeImage(await select.blob())
Insert cell
Insert cell
function intToHex(colorInt) {
return "#" + ("000000" + colorInt.toString(16)).slice(-6);
}
Insert cell
selectedColors
Insert cell
viewof selectedColors = SwatchInput(colorPopulation, {
multiple: true
})
Insert cell
SwatchInput = async (colors, { value, label, multiple = false } = {}) => {
let indexes = [];
const node = html`<div style="padding-top: 0.5rem; padding-bottom: 0.5rem;">
${colors.map((color, idx) => {
const c = color.toString();
let onclick = () => set(node, idx);
let style = `position: relative; appearance: none; border: none; outline: none; display: inline-block; width: 40px; height: 40px; background-color: ${c}; border: 1px solid rgba(0,0,0,.12); border-radius: 4px; margin-right: 2px;`;
return html`<button onclick=${onclick} style="${style}">
<div style="color: ${isWhiteText(c) ? "white" : "black"}">${icon(
"checkmark"
)}</div>
</button>`;
})}
</div>`;
// Update the display whenever the value changes
Object.defineProperty(node, "value", {
get() {
return value;
},
set(v) {
const index = +v;
const c = colors[index];
if (multiple) {
if (value === undefined) {
value = [];
}
if (v !== undefined) {
if (indexes.includes(v)) {
indexes = indexes.filter((vv) => vv !== v);
} else {
indexes.push(v);
}
}
value = indexes.map((idx) => colors[idx]);
} else {
value = c;
indexes = [v];
}
restyle();
}
});

// Set the initial value
if (multiple) {
if (value === undefined) {
value = [];
indexes = [];
} else {
indexes = value.map((v) => colors.findIndex((c) => c.equals(v)));
}
set(node);
} else {
let initIndex = value ? colors.findIndex((c) => c.equals(value)) : 0;
set(node, initIndex);
}

function restyle(newIndex) {
const btns = node.querySelectorAll("button");
btns.forEach((btn, idx) => {
let checked = indexes.includes(idx);
let ch = btn.getElementsByTagName("div")[0];
if (checked) {
btn.style.boxShadow = "0 0 0 2px rgba(0,0,0,1)";
btn.style.zIndex = "10";
ch.style.display = "block";
} else {
btn.style.boxShadow = "";
btn.style.zIndex = "";
ch.style.display = "none";
}
});
}

restyle();
return node;
}
Insert cell
quantizeImage = async (urlOrBlob) => {
// create canvas
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const img = new Image();
if (urlOrBlob instanceof Blob) {
img.src = URL.createObjectURL(urlOrBlob);
} else if (typeof urlOrBlob === "string") {
img.src = urlOrBlob;
}
await img.decode();
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const pixels = [];
for (let i = 0; i < data.length; i += 4) {
const opaqueBlack = 0xff000000;
const color =
opaqueBlack | (data[i] << 16) | (data[i + 1] << 8) | data[i + 2];
pixels.push(color);
}
const result = colorUtil.QuantizerCelebi.quantize(pixels, colorCount);
const colorScores = colorUtil.Score.score(result);
const colorPop = Array.from(result.entries(), ([color, population]) => ({
color,
population
})).sort((a, b) => b.population - a.population);

return colorPop.map(({ color, population }, idx) => {
const colorI = ColorRGB.fromInt(color);
colorI.setMeta({
scoreIndex: colorScores.findIndex((c) => c === color),
population
});
return colorI;
});
}
Insert cell
colorUtil = import("https://cdn.skypack.dev/@material/material-color-utilities")
Insert cell
function set(input, value) {
input.value = value;
input.dispatchEvent(new Event("input", { bubbles: true })); // Native events bubble, so we should too
}
Insert cell
html = htl.html
Insert cell
import { ramp } from "@mbostock/color-ramp"
Insert cell
function axis(scale, val) {
return Object.assign(d3.axisBottom(scale.range([20, width - 20])), {
render() {
return d3
.create("svg")
.attr("viewBox", [0, -10, width, 33])
.call(this)
.node();
}
});
}
Insert cell
import { ColorRGB, isWhiteText } from "@mateh/palette"
Insert cell
import { icon } from "@mateh/icon"
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