Allow user to choose from multiple palette algorithms. (#97)

Fixes #95.
pull/98/head
Atul Varma 2021-04-17 07:33:27 -04:00 zatwierdzone przez GitHub
rodzic 9de487fac0
commit 949d8d72dd
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
5 zmienionych plików z 123 dodań i 17 usunięć

Wyświetl plik

@ -4,8 +4,13 @@ import { CreatureContext, CreatureContextType } from "../creature-symbol";
import { createCreatureSymbolFactory } from "../creature-symbol-factory";
import { HoverDebugHelper } from "../hover-debug-helper";
import { Page } from "../page";
import { PaletteAlgorithmWidget } from "../palette-algorithm-widget";
import { Random } from "../random";
import { createRandomColorPalette } from "../random-colors";
import {
createRandomColorPalette,
DEFAULT_RANDOM_PALETTE_ALGORITHM,
RandomPaletteAlgorithm,
} from "../random-colors";
import { createSvgSymbolContext } from "../svg-symbol";
import { svgScale, SvgTransform } from "../svg-transform";
import { SvgVocabulary } from "../svg-vocabulary";
@ -55,17 +60,22 @@ const EYE_CREATURE = (
);
const RandomColorSampling: React.FC<{}> = () => {
const [paletteAlg, setPaletteAlg] = useState<RandomPaletteAlgorithm>(
DEFAULT_RANDOM_PALETTE_ALGORITHM
);
const [seed, setSeed] = useState(Date.now());
const NUM_COLORS = 100;
const rng = new Random(seed);
const palette = createRandomColorPalette(NUM_COLORS, rng);
const palette = createRandomColorPalette(NUM_COLORS, rng, paletteAlg);
return (
<>
<PaletteAlgorithmWidget value={paletteAlg} onChange={setPaletteAlg} />
<div className="thingy">
<div style={{ fontSize: 0 }}>
{range(NUM_COLORS).map((i) => (
<div
key={i}
style={{
backgroundColor: palette[i],
width: "1rem",

Wyświetl plik

@ -0,0 +1,34 @@
import React from "react";
import {
RandomPaletteAlgorithm,
RANDOM_PALETTE_ALGORITHMS,
} from "./random-colors";
export type PaletteAlgorithmWidgetProps = {
value: RandomPaletteAlgorithm;
onChange: (value: RandomPaletteAlgorithm) => void;
};
export const PaletteAlgorithmWidget: React.FC<PaletteAlgorithmWidgetProps> = ({
value,
onChange,
}) => {
const id = "algorithm";
return (
<div className="flex-widget thingy">
<label htmlFor={id}>Palette algorithm: </label>
<select
id={id}
onChange={(e) => onChange(e.target.value as RandomPaletteAlgorithm)}
value={value}
>
{RANDOM_PALETTE_ALGORITHMS.map((choice) => (
<option key={choice} value={choice}>
{choice}
</option>
))}
</select>
</div>
);
};

Wyświetl plik

@ -1,4 +1,8 @@
import { clampedByteToHex, createRandomColorPalette } from "./random-colors";
import {
clampedByteToHex,
createRandomColorPalette,
RANDOM_PALETTE_ALGORITHMS,
} from "./random-colors";
describe("clampedByteToHex", () => {
it("clamps values over 255 to 255", () => {
@ -18,10 +22,14 @@ describe("clampedByteToHex", () => {
});
});
test("createRandomColorPalette() works", () => {
const palette = createRandomColorPalette(3);
expect(palette).toHaveLength(3);
for (let color of palette) {
expect(color).toMatch(/^\#[0-9a-f]{6}$/);
describe("createRandomColorPalette()", () => {
for (let alg of RANDOM_PALETTE_ALGORITHMS) {
it(`works using the '${alg}' algorithm`, () => {
const palette = createRandomColorPalette(3, undefined, alg);
expect(palette).toHaveLength(3);
for (let color of palette) {
expect(color).toMatch(/^\#[0-9a-f]{6}$/);
}
});
}
});

Wyświetl plik

@ -2,6 +2,13 @@ import { Random } from "./random";
import { range } from "./util";
import * as colorspaces from "colorspaces";
type RandomPaletteGenerator = (numEntries: number, rng: Random) => string[];
export type RandomPaletteAlgorithm = "RGB" | "CIELUV";
export const DEFAULT_RANDOM_PALETTE_ALGORITHM: RandomPaletteAlgorithm =
"CIELUV";
/**
* Clamp the given number to be between 0 and 255, then
* convert it to hexadecimal.
@ -19,7 +26,12 @@ export function clampedByteToHex(value: number): string {
return hex;
}
function createRandomColor(rng: Random): string {
function createRandomRGBColor(rng: Random): string {
const rgb = range(3).map(() => rng.inRange({ min: 0, max: 255, step: 1 }));
return "#" + rgb.map(clampedByteToHex).join("");
}
function createRandomCIELUVColor(rng: Random): string {
const max_luv_samples = 100;
let luv_sample_failed = true;
let rand_color_hex: string = "#000000";
@ -56,15 +68,40 @@ function createRandomColor(rng: Random): string {
}
/**
* Create a random color palette with the given number of
* entries, optionally using the given random number generator.
* Factory function to take a function that generates a random color
* and return a palette generator that just calls it once for every
* color in the palette.
*/
function createSimplePaletteGenerator(
createColor: (rng: Random) => string
): RandomPaletteGenerator {
return (numEntries: number, rng: Random) =>
range(numEntries).map(() => createColor(rng));
}
const PALETTE_GENERATORS: {
[key in RandomPaletteAlgorithm]: RandomPaletteGenerator;
} = {
RGB: createSimplePaletteGenerator(createRandomRGBColor),
CIELUV: createSimplePaletteGenerator(createRandomCIELUVColor),
};
export const RANDOM_PALETTE_ALGORITHMS = Object.keys(
PALETTE_GENERATORS
) as RandomPaletteAlgorithm[];
/**
* Create a random color palette with the given number of entries,
* optionally using the given random number generator and the
* given algorithm.
*
* The return value is an Array of strings, where each string is
* a color hex hash (e.g. `#ff0000`).
*/
export function createRandomColorPalette(
numEntries: number,
rng: Random = new Random()
rng: Random = new Random(),
algorithm: RandomPaletteAlgorithm = DEFAULT_RANDOM_PALETTE_ALGORITHM
): string[] {
return range(numEntries).map(() => createRandomColor(rng));
return PALETTE_GENERATORS[algorithm](numEntries, rng);
}

Wyświetl plik

@ -1,6 +1,11 @@
import React, { useState } from "react";
import { PaletteAlgorithmWidget } from "./palette-algorithm-widget";
import { Random } from "./random";
import { createRandomColorPalette } from "./random-colors";
import {
createRandomColorPalette,
DEFAULT_RANDOM_PALETTE_ALGORITHM,
RandomPaletteAlgorithm,
} from "./random-colors";
import { SvgCompositionContext } from "./svg-composition-context";
type SvgCompositionColors = Pick<
@ -8,8 +13,14 @@ type SvgCompositionColors = Pick<
"background" | "fill" | "stroke"
>;
function createRandomCompositionColors(): SvgCompositionColors {
const [background, stroke, fill] = createRandomColorPalette(3);
function createRandomCompositionColors(
alg: RandomPaletteAlgorithm
): SvgCompositionColors {
const [background, stroke, fill] = createRandomColorPalette(
3,
undefined,
alg
);
return { background, stroke, fill };
}
@ -20,10 +31,13 @@ export type RandomizerWidgetProps = {
export const RandomizerWidget: React.FC<RandomizerWidgetProps> = (props) => {
type RandType = "colors" | "symbols" | "colors and symbols";
const [paletteAlg, setPaletteAlg] = useState<RandomPaletteAlgorithm>(
DEFAULT_RANDOM_PALETTE_ALGORITHM
);
const [randType, setRandType] = useState<RandType>("colors and symbols");
const randomize = () => {
if (randType === "colors" || randType === "colors and symbols") {
props.onColorsChange(createRandomCompositionColors());
props.onColorsChange(createRandomCompositionColors(paletteAlg));
}
if (randType === "symbols" || randType === "colors and symbols") {
props.onSymbolsChange(new Random(Date.now()));
@ -48,6 +62,9 @@ export const RandomizerWidget: React.FC<RandomizerWidgetProps> = (props) => {
{makeRadio("colors")}
{makeRadio("symbols")}
{makeRadio("colors and symbols")}
{randType !== "symbols" && (
<PaletteAlgorithmWidget value={paletteAlg} onChange={setPaletteAlg} />
)}
<button accessKey="r" onClick={randomize}>
<u>R</u>andomize!
</button>