Public
Edited
Jul 28, 2023
Insert cell
Insert cell
viewof dark = inputMode({ value: false })
Insert cell
Insert cell
Insert cell
Insert cell
surface.raster
Insert cell
{
const maxChroma100 = surface.space.scale.maxChroma100;
return Plot.plot({
width: (200 * maxChroma100) / 30,
aspectRatio: 1,
grid: true,
x: { label: "chroma", ticks: 5 },
y: { label: "luminance" },
marks: [
Plot.raster({
fill: surface.raster,
x1: 0,
x2: maxChroma100,
y1: 0,
y2: 100
})
]
});
}
Insert cell
points = [
{ luminance: 0, chroma: 0 },
{ luminance: 0.5, chroma: 0.5 },
{ luminance: 1, chroma: 0 }
]
Insert cell
makePointSpline = function (points) {
const lumSpline = d3.interpolateBasis(points.map((x) => x.luminance));
const chrSpline = d3.interpolateBasis(points.map((x) => x.chroma));

return (x) => ({ luminance: lumSpline(x), chroma: chrSpline(x) });
}
Insert cell
pointSpline = makePointSpline(points)
Insert cell
pointSpline(0.5)
Insert cell
math.cumsum([1, 2, 3])
Insert cell
arr = [
[1, 2],
[3, 5],
[6, 7]
]
Insert cell
// integrate along the length
[
0,
...math.dotPow(
math.dotPow(math.diff(arr), 2).map((x) => math.sum(x)),
0.5
)
]
Insert cell
// [normalized distance], [index]
spline = NaturalCubicSpline([0, 0.25, 1], [0, 0.5, 1])
Insert cell
spline(0.25)
Insert cell
surfaceHue = function (hue50, { dHue = 0, space = colorSpaces.munsell } = {}) {
const fnSurface = function (luminance) {
return hue50 + dHue * (luminance - 50);
};

return new ColorSurface(space, fnSurface);
}
Insert cell
class ColorSurface {
#fnMaxChroma100;

constructor(fnSurface, space) {
this.fnSurface = fnSurface;
this.space = space;

this.#fnMaxChroma100 = _.memoize(
// Number -> Number[3]
function (luminance) {
const hue = this.fnSurface(luminance);
return [0.4, 0.2, 0.6];
}
);
}

get raster() {
const fnSurface = this.fnSurface;
const toSrgb = _.flow([
colorUtils.fromPolar,
this.space.from100,
this.space.toSRgb
]);

return function (chroma, luminance) {
try {
const hue = fnSurface(luminance);
const sRgb = toSrgb([luminance, chroma, hue]);

const hex = colorUtils.inRgbGamut(sRgb)
? colorUtils.hexFromSRgb(sRgb)
: undefined;

return hex;
} catch (error) {
throw `${error} for {luminance: ${luminance}, chroma: ${chroma}}`;
}
};
}

// Number -> Number[3]
sRgbMaxChroma100(luminance) {
return this.#fnMaxChroma100(luminance);
}

static fromHue = function (
hue,
{ lum = 50, dHue = 0, space = colorSpaces.munsell } = {}
) {
const fnSurface = function (luminance) {
return hue + dHue * (luminance - lum);
};

return new ColorSurface(fnSurface, space);
};

static fromHex = function (
hex,
{ dHue = 0, space = colorSpaces.munsell } = {}
) {
const [lum, _, hue] = _.flow(
colorUtils.hexToSRgb,
space.fromSRgb,
space.to100,
colorUtils.toPolar
)(hex);

return this.fromHue(hue, { lum: lum, dHue: 0, space: space });
};

static fromHexHex = function (
hex1,
hex2,
{ short = true, space = colorSpaces.munsell } = {}
) {
const toPolar = _.flow(
colorUtils.hexToSRgb,
space.fromSRgb,
space.to100,
colorUtils.toPolar
);

const [lum1, _1, hue1] = toPolar(hex1);
const [lum2, _2, hue2] = toPolar(hex2);

const dHue = path(hue1, hue2, { short: short });
return this.fromHue(hue1, { lum: lum1, dHue: dHue, space: space });
};

static fromHexArray(arrHex, { space = colorSpaces.munsell }) {
// idea: spline hue = f(luminance)
// need to assure monotonicity
throw "not implemented";
}
}
Insert cell
path = function (h1, h2, { short = true } = {}) {
const difference = h2 - h1;
const isDiffShort = math.abs(difference) < 180;

const complement = difference > 0 ? difference - 360 : 360 + difference;

return short == isDiffShort ? difference : complement;
}
Insert cell
path(45, 315, { short: true })
Insert cell
// ColorScale
//
d3.interpolateBrBG(0.5)
Insert cell
class ColorTrajectory {}
Insert cell
class ColorScale {}
Insert cell
Insert cell
math = import("https://cdn.skypack.dev/mathjs@11.8.2?")
Insert cell
import { NaturalCubicSpline } from "@jrus/cubic-spline"
Insert cell
import { colorSpaces, colorUtils } from "337ad46fff07887e"
Insert cell
import { inputMode, invokeMode, styleDark } from "@ijlyttle/dark-mode-input"
Insert cell
invokeMode(dark)
Insert cell
styleDark()
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