Skip to content

Commit

Permalink
abstract all the coordinates
Browse files Browse the repository at this point in the history
  • Loading branch information
mcnuttandrew committed Jan 18, 2024
1 parent 13483ba commit bd5ad62
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 54 deletions.
52 changes: 30 additions & 22 deletions src/components/ColorScatterPlot.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,15 @@
$: xRange = config.xDomain;
$: domainXScale = scaleLinear().domain([0, 1]).range(xRange);
const makeDomain = (range: number[], extent: number[], scale: any) => [
// range[0] > range[1] ? scale(extent[1]) : scale(extent[0]),
// range[0] > range[1] ? scale(extent[0]) : scale(extent[1]),
scale(extent[0]),
scale(extent[1]),
];
$: xScale = scaleLinear()
.domain(makeDomain(xRange, extents.x, domainXScale))
.domain([domainXScale(extents.x[0]), domainXScale(extents.x[1])])
.range([0, plotWidth]);
$: yRange = config.yDomain;
$: domainYScale = scaleLinear().domain([0, 1]).range(yRange);
$: yScale = scaleLinear()
.domain(makeDomain(yRange, extents.y, domainYScale))
.domain([domainYScale(extents.y[0]), domainYScale(extents.y[1])])
.range([0, plotHeight]);
$: zRange = config.zDomain;
Expand All @@ -72,7 +67,12 @@
.domain([domainLScale(extents.z[0]), domainLScale(extents.z[1])])
.range([0, plotHeight]);
$: pos = makePosAndSizes(pickedColors, xScale, yScale, zScale);
$: miniConfig = {
xIdx: config.xChannelIndex,
yIdx: config.yChannelIndex,
zIdx: config.zChannelIndex,
};
$: pos = makePosAndSizes(pickedColors, xScale, yScale, zScale, miniConfig);
let dragging: false | { x: number; y: number } = false;
let dragBox: false | { x: number; y: number } = false;
Expand All @@ -92,25 +92,33 @@
x: x - dragging.x,
y: y - dragging.y,
};
const [l, a, b] = originalColor.toChannels();
const newX = xScale.invert(xScale(a) + screenPosDelta.x);
const newY = yScale.invert(yScale(b) + screenPosDelta.y);
return colorFromChannels(
[l, clampToRange(newX, xRange), clampToRange(newY, yRange)],
colorSpace
const { xIdx, yIdx } = miniConfig;
const coords = originalColor.toChannels();
coords[xIdx] = clampToRange(
xScale.invert(xScale(coords[xIdx]) + screenPosDelta.x),
xRange
);
coords[yIdx] = clampToRange(
yScale.invert(yScale(coords[yIdx]) + screenPosDelta.y),
yRange
);
return colorFromChannels(coords, colorSpace);
};
const eventToColorZ = (e: any, color: Color, originalColor: Color): Color => {
if (!dragging || dragging.x === undefined || dragging.y === undefined)
return color;
const screenPosDelta = toXY(e).y - dragging.y;
const [l, a, b] = originalColor.toChannels();
const newZ = zScale.invert(zScale(l) + screenPosDelta);
const { zIdx } = miniConfig;
const coords = originalColor.toChannels();
coords[zIdx] = clampToRange(
zScale.invert(zScale(coords[zIdx]) + screenPosDelta),
zRange
);
return colorFromChannels([clampToRange(newZ, zRange), a, b], colorSpace);
return colorFromChannels(coords, colorSpace);
};
function stopDrag() {
Expand Down Expand Up @@ -238,9 +246,9 @@
let hoveredPoint: Color | false = false;
let hoverPoint = (x: typeof hoveredPoint) => (hoveredPoint = x);
$: x = (point: Color) => xScale(point.toChannels()[1]);
$: y = (point: Color) => yScale(point.toChannels()[2]);
$: z = (point: Color) => zScale(point.toChannels()[0]);
$: x = (point: Color) => xScale(point.toChannels()[config.xChannelIndex]);
$: y = (point: Color) => yScale(point.toChannels()[config.yChannelIndex]);
$: z = (point: Color) => zScale(point.toChannels()[config.zChannelIndex]);
function clickResponse(e: any, i: number) {
if (e.metaKey || e.shiftKey) {
Expand Down
13 changes: 7 additions & 6 deletions src/components/KeyboardHooks.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,20 @@
if (!focusSet.has(idx)) {
return color;
}
const [l, a, b] = color.toChannels();
const channels = Object.keys(color.channels);
const channels = color.toChannels();
const xVal = channels[config.xChannelIndex];
const yVal = channels[config.yChannelIndex];
if (key === "arrowdown") {
color.setChannel(channels[2], b + verticalDir * step);
color.setChannel(config.yChannel, yVal + verticalDir * step);
}
if (key === "arrowup") {
color.setChannel(channels[2], b - verticalDir * step);
color.setChannel(config.yChannel, yVal - verticalDir * step);
}
if (key === "arrowleft") {
color.setChannel(channels[1], a - horizontalDir * step);
color.setChannel(config.xChannel, xVal - horizontalDir * step);
}
if (key === "arrowright") {
color.setChannel(channels[1], a + horizontalDir * step);
color.setChannel(config.xChannel, xVal + horizontalDir * step);
}
return color;
});
Expand Down
8 changes: 4 additions & 4 deletions src/content-modules/contextual-tools/AlignSelection.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@
on:click={() => {
const newCoordinate = op(...focusLabs.map((x) => x[pos]));
colorStore.setCurrentPalColors(
mapFocusedColors(([l, a, b]) => {
mapFocusedColors(([a, b, c]) => {
return colorFromChannels(
[
pos === 0 ? newCoordinate : l,
pos === 1 ? newCoordinate : a,
pos === 2 ? newCoordinate : b,
pos === 0 ? newCoordinate : a,
pos === 1 ? newCoordinate : b,
pos === 2 ? newCoordinate : c,
],
colorSpace
);
Expand Down
12 changes: 8 additions & 4 deletions src/content-modules/contextual-tools/DistributePoints.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { colorFromChannels } from "../../lib/Color";
import { colorFromChannels, colorPickerConfig } from "../../lib/Color";
import colorStore from "../../stores/color-store";
import focusStore from "../../stores/focus-store";
import Tooltip from "../../components/Tooltip.svelte";
Expand All @@ -8,6 +8,7 @@
$: colors = $colorStore.currentPal.colors;
$: focusedColors = $focusStore.focusedColors;
$: colorSpace = $colorStore.currentPal.colorSpace;
$: config = colorPickerConfig[colorSpace];
type Direction = "horizontal" | "vertical" | "in z space";
function distributePoints(direction: Direction) {
Expand All @@ -28,12 +29,15 @@
let newPoints = sortedIndexes.map((colorIdx, arrIdx) => {
const t = arrIdx / numPoints;
const newPoint = colors.at(colorIdx)!.toChannels() as Channels;
const xIdx = config.xChannelIndex;
const yIdx = config.yChannelIndex;
const zIdx = config.zChannelIndex;
if (direction === "horizontal") {
newPoint[1] = minPoint[1] * (1 - t) + maxPoint[1] * t;
newPoint[xIdx] = minPoint[xIdx] * (1 - t) + maxPoint[xIdx] * t;
} else if (direction === "in z space") {
newPoint[0] = minPoint[0] * (1 - t) + maxPoint[0] * t;
newPoint[zIdx] = minPoint[zIdx] * (1 - t) + maxPoint[zIdx] * t;
} else {
newPoint[2] = minPoint[2] * (1 - t) + maxPoint[2] * t;
newPoint[yIdx] = minPoint[yIdx] * (1 - t) + maxPoint[yIdx] * t;
}
return newPoint as Channels;
});
Expand Down
28 changes: 20 additions & 8 deletions src/lib/Color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class Color {
spaceName: keyof typeof colorDirectory = "rgb";
domains: Domain;
stepSize: Channels = [1, 1, 1];
channelNames: string[] = [];
dimensionToChannel: Record<"x" | "y" | "z", string> = { x: "", y: "", z: "" };
axisLabel: (num: number) => string = (x) => x.toFixed(1).toString();

Expand Down Expand Up @@ -147,12 +148,13 @@ export function stringToChannels(spaceName: string, str: string) {

export class CIELAB extends Color {
name = "CIELAB";
channelNames = ["L", "a", "b"];
channels = { L: 0, a: 0, b: 0 };
domains = { L: [100, 0], a: [-125, 125], b: [125, -125] } as Domain;
chromaBind = chroma.lab;
spaceName = "lab" as const;
stepSize: Channels = [1, 1, 1];
dimensionToChannel = { x: "a", y: "b", z: "L" };
dimensionToChannel = { x: "L", y: "b", z: "a" };
axisLabel = (num: number) => `${Math.round(num)}`;

toString(): string {
Expand All @@ -162,6 +164,7 @@ export class CIELAB extends Color {
}
export class HSV extends Color {
name = "HSV";
channelNames = ["h", "s", "v"];
channels = { h: 0, s: 0, v: 0 };
domains = { h: [0, 360], s: [0, 100], v: [0, 100] } as Domain;
chromaBind = chroma.hsv;
Expand All @@ -175,6 +178,7 @@ export class HSV extends Color {

export class RGB extends Color {
name = "RGB";
channelNames = ["r", "g", "b"];
channels = { r: 0, g: 0, b: 0 };
chromaBind = chroma.rgb;
spaceName = "srgb" as const;
Expand All @@ -190,6 +194,7 @@ export class RGB extends Color {

export class HSL extends Color {
name = "HSL";
channelNames = ["h", "s", "l"];
channels = { h: 0, s: 0, l: 0 };
chromaBind = chroma.hsl;
spaceName = "hsl" as const;
Expand All @@ -204,6 +209,7 @@ export class HSL extends Color {
}
export class LCH extends Color {
name = "LCH";
channelNames = ["l", "c", "h"];
channels = { l: 0, c: 0, h: 0 };
chromaBind = chroma.lch;
spaceName = "lch" as const;
Expand All @@ -215,6 +221,7 @@ export class LCH extends Color {

export class OKLAB extends Color {
name = "OKLAB";
channelNames = ["l", "a", "b"];
channels = { l: 0, a: 0, b: 0 };
chromaBind = chroma.oklab;
spaceName = "oklab" as const;
Expand All @@ -225,6 +232,7 @@ export class OKLAB extends Color {

export class OKLCH extends Color {
name = "OKLCH";
channelNames = ["l", "c", "h"];
channels = { l: 0, c: 0, h: 0 };
chromaBind = chroma.oklch;
spaceName = "oklch" as const;
Expand All @@ -235,6 +243,7 @@ export class OKLCH extends Color {

export class JZAZBZ extends Color {
name = "JZAZBZ";
channelNames = ["jz", "az", "bz"];
channels = { jz: 0, az: 0, bz: 0 };
spaceName = "jzazbz" as const;
domains = { jz: [1, 0], az: [-0.5, 0.5], bz: [0.5, -0.5] } as Domain;
Expand Down Expand Up @@ -312,17 +321,20 @@ type ColorSpace = keyof typeof colorDirectory | string;
export const colorPickerConfig = Object.fromEntries(
(Object.keys(colorDirectory) as ColorSpace[]).map((name: ColorSpace) => {
const exampleColor = new (colorDirectory as any)[name]() as Color;
const dimensionToChannel = exampleColor.dimensionToChannel;
const { x, y, z } = exampleColor.dimensionToChannel;
return [
name,
{
title: exampleColor.name,
xDomain: exampleColor.domains[dimensionToChannel.x],
yDomain: exampleColor.domains[dimensionToChannel.y],
zDomain: exampleColor.domains[dimensionToChannel.z],
xChannel: dimensionToChannel.x,
yChannel: dimensionToChannel.y,
zChannel: dimensionToChannel.z,
xDomain: exampleColor.domains[x],
yDomain: exampleColor.domains[y],
zDomain: exampleColor.domains[z],
xChannel: x,
xChannelIndex: exampleColor.channelNames.indexOf(x),
yChannel: y,
yChannelIndex: exampleColor.channelNames.indexOf(y),
zChannel: z,
zChannelIndex: exampleColor.channelNames.indexOf(z),
xStep: exampleColor.stepSize[1],
yStep: exampleColor.stepSize[2],
zStep: exampleColor.stepSize[0],
Expand Down
24 changes: 14 additions & 10 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,9 @@ export function avgColors(
}

export function opposingColor(color: Color): Color {
const c = color.toChroma().hsl();
const channels = c.map((x, i) => (i === 0 ? (x + 180) % 360 : x));
const chromaColor = chroma.hsl(channels[0], channels[1], channels[2]);
return colorFromChannels(chromaColor.lab(), "lab");
const [h, s, l] = color.toColorIO().to("hsl").coords;
const channels = [(h + 180) % 360, s, l] as [number, number, number];
return colorFromChannels(channels, "hsl").toColorSpace(color.spaceName);
}

export function deDup(arr: Color[]): Color[] {
Expand Down Expand Up @@ -130,11 +129,15 @@ export function draggable(node: any) {
}

const extent = (arr: number[]) => [Math.min(...arr), Math.max(...arr)];
export function makeExtents(arr: number[][]) {
function makeExtents(
arr: number[][],
config: { xIdx: number; yIdx: number; zIdx: number }
) {
const { xIdx, yIdx, zIdx } = config;
return {
x: extent(arr.map((x) => x[1])),
y: extent(arr.map((x) => x[2])),
z: extent(arr.map((x) => x[0])),
x: extent(arr.map((x) => x[xIdx])),
y: extent(arr.map((x) => x[yIdx])),
z: extent(arr.map((x) => x[zIdx])),
};
}

Expand Down Expand Up @@ -214,9 +217,10 @@ export function makePosAndSizes(
pickedColors: [number, number, number][],
xScale: any,
yScale: any,
zScale: any
zScale: any,
config: { xIdx: number; yIdx: number; zIdx: number }
) {
const selectionExtents = makeExtents(pickedColors);
const selectionExtents = makeExtents(pickedColors, config);
const makePos = (key: keyof typeof selectionExtents, scale: any) => {
const [a, b] = scale.domain();
return scale(selectionExtents[key][a > b ? 1 : 0]);
Expand Down

0 comments on commit bd5ad62

Please sign in to comment.