feature: color parameter sliders for Maneesh\'s algorithm

corinna000-feature/color-parameter-sliders^2
Corinna Cohn 2021-05-25 21:14:08 -04:00
rodzic 1a4426472e
commit a44a4bd255
5 zmienionych plików z 119 dodań i 12823 usunięć

Wyświetl plik

@ -60,17 +60,18 @@ const EYE_CREATURE = (
); );
const RandomColorSampling: React.FC<{}> = () => { const RandomColorSampling: React.FC<{}> = () => {
const [paletteConfig, setPaletteConfig] = useState({})
const [paletteAlg, setPaletteAlg] = useState<RandomPaletteAlgorithm>( const [paletteAlg, setPaletteAlg] = useState<RandomPaletteAlgorithm>(
DEFAULT_RANDOM_PALETTE_ALGORITHM DEFAULT_RANDOM_PALETTE_ALGORITHM
); );
const [seed, setSeed] = useState(Date.now()); const [seed, setSeed] = useState(Date.now());
const NUM_COLORS = 100; const NUM_COLORS = 100;
const rng = new Random(seed); const rng = new Random(seed);
const palette = createRandomColorPalette(NUM_COLORS, rng, paletteAlg); const palette = createRandomColorPalette(NUM_COLORS, rng, paletteAlg, paletteConfig);
return ( return (
<> <>
<PaletteAlgorithmWidget value={paletteAlg} onChange={setPaletteAlg} /> <PaletteAlgorithmWidget value={paletteAlg} onChange={setPaletteAlg} onPaletteConfigChange={setPaletteConfig} />
<div className="thingy"> <div className="thingy">
<div style={{ fontSize: 0 }}> <div style={{ fontSize: 0 }}>
{range(NUM_COLORS).map((i) => ( {range(NUM_COLORS).map((i) => (

Wyświetl plik

@ -1,22 +1,55 @@
import React from "react"; import React, {useEffect, useState} from "react";
import { import {
RandomPaletteAlgorithm, RandomPaletteAlgorithm,
RANDOM_PALETTE_ALGORITHMS, RANDOM_PALETTE_ALGORITHMS, PaletteAlgorithmConfig,
} from "./random-colors"; } from "./random-colors";
export type PaletteAlgorithmWidgetProps = { export type PaletteAlgorithmWidgetProps = {
value: RandomPaletteAlgorithm; value: RandomPaletteAlgorithm;
onChange: (value: RandomPaletteAlgorithm) => void; onChange: (value: RandomPaletteAlgorithm) => void;
onPaletteConfigChange: (value: PaletteAlgorithmConfig) => void;
}; };
export const PaletteAlgorithmWidget: React.FC<PaletteAlgorithmWidgetProps> = ({ export const PaletteAlgorithmWidget: React.FC<PaletteAlgorithmWidgetProps> = ({
value, value,
onChange, onChange,
onPaletteConfigChange = () => {}
}) => { }) => {
const id = "algorithm"; const id = "algorithm";
const [paletteConfig, setPaletteConfig] = useState<PaletteAlgorithmConfig>({
hue: 120,
hueInterval: 15,
saturation: 50,
valueMin: 20,
valueMax: 80,
});
useEffect(() => {
onPaletteConfigChange(paletteConfig);
}, [paletteConfig])
return ( return (
<div className="flex-widget thingy"> <div className="flex-widget thingy">
{value === "threevals" && (
<div className="flex-widget thingy">
<label>Hue {paletteConfig.hue}</label>
<input type="range" min="0" max="360" value={paletteConfig.hue}
onChange={(e) => setPaletteConfig({...paletteConfig, hue: Number(e.target.value) }) } />
<label>Hue Interval {paletteConfig.hueInterval}</label>
<input type="range" min="0" max="120" value={paletteConfig.hueInterval}
onChange={(e) => setPaletteConfig({...paletteConfig, hueInterval: Number(e.target.value) }) }
/>
<label>Value min {paletteConfig.valueMin}</label>
<input type="range" min="0" max="100" value={paletteConfig.valueMin}
onChange={(e) => setPaletteConfig({...paletteConfig, valueMin: Number(e.target.value) }) }
/>
<label>Value Max {paletteConfig.valueMax}</label>
<input type="range" min="0" max="100" value={paletteConfig.valueMax}
onChange={(e) => setPaletteConfig({...paletteConfig, valueMax: Number(e.target.value) }) }
/>
<label>Saturation {paletteConfig.saturation}</label>
<input type="range" min="0" max="100" value={paletteConfig.saturation}
onChange={(e) => setPaletteConfig({...paletteConfig, saturation: Number(e.target.value) }) }
/></div>)}
<label htmlFor={id}>Palette algorithm: </label> <label htmlFor={id}>Palette algorithm: </label>
<select <select
id={id} id={id}

Wyświetl plik

@ -3,8 +3,18 @@ import { range, clamp } from "./util";
import * as colorspaces from "colorspaces"; import * as colorspaces from "colorspaces";
import { ColorTuple, hsluvToHex } from "hsluv"; import { ColorTuple, hsluvToHex } from "hsluv";
type RandomPaletteGenerator = (numEntries: number, rng: Random) => string[]; export interface PaletteAlgorithmConfig {
//type ColorFunction = (rng: Random) => string[]; valueMin?: number,
valueMax?: number,
hue?: number,
hueInterval?: number
saturation?: number
}
type RandomPaletteGenerator = (numEntries: number, rng: Random, config: PaletteAlgorithmConfig) => string[];
// type ColorFunction = (rng: Random) => string[];
// type ColorFunctionConfig = () => string[];
export type RandomPaletteAlgorithm = "RGB" | "CIELUV" | "threevals"; export type RandomPaletteAlgorithm = "RGB" | "CIELUV" | "threevals";
// | "randgrey" // | "randgrey"
@ -96,23 +106,21 @@ function createRandGrey(rng: Random): string[] {
} }
*/ */
/* function create3Vconfig() {
function create3V180(angle1: number): ColorFunction { return (rng: Random, config: PaletteAlgorithmConfig): string[] => {
return (rng: Random): string[] => { const LMin = config.valueMin ? config.valueMin : 25;
let Ls = [25, 50, 75]; const LMax = config.valueMax? config.valueMax: 75;
let Ls = [LMin, 50, LMax];
//Now we have 3 lightness values, pick a random hue and sat //Now we have 3 lightness values, pick a random hue and sat
let h1 = rng.inInterval({ min: 0, max: 360 }), let h1 = config.hue ? config.hue : rng.inInterval({ min: 0, max: 360 }),
h2 = 360 * (((h1 + angle1) / 360) % 1), h2 = 360 * (((h1 + (config.hueInterval ? config.hueInterval : 120)) / 360) % 1),
h3 = 360 * (((h1 + 180) / 360) % 1); h3 = 360 * (((h2 + (config.hueInterval ? config.hueInterval : 240)) / 360) % 1);
let Hs = [h1, h2, h3]; let Hs = [h1, h2, h3];
let Ss = [ const sat = config.saturation ? config.saturation : rng.fromGaussian({ mean: 100, stddev: 40 });
rng.fromGaussian({ mean: 100, stddev: 40 }), let Ss = [ sat, sat, sat ];
rng.fromGaussian({ mean: 100, stddev: 40 }),
rng.fromGaussian({ mean: 100, stddev: 40 }),
];
Ss = Ss.map((x) => clamp(x, 0, 100)); Ss = Ss.map((x) => clamp(x, 0, 100));
//zip //zip
@ -124,41 +132,40 @@ function create3V180(angle1: number): ColorFunction {
return hexcolors; return hexcolors;
}; };
} }
*/
function threeVColor(rng: Random): string[] { // function threeVColor(rng: Random): string[] {
let L1 = rng.inInterval({ min: 10, max: 25 }); // let L1 = rng.inInterval({ min: 10, max: 25 });
let L2 = rng.inInterval({ min: L1 + 25, max: 60 }); // let L2 = rng.inInterval({ min: L1 + 25, max: 60 });
let L3 = rng.inInterval({ min: L2 + 25, max: 85 }); // let L3 = rng.inInterval({ min: L2 + 25, max: 85 });
//
let Ls = [L1, L2, L3]; // let Ls = [L1, L2, L3];
//
let angleI = rng.inInterval({ min: 0, max: 120 }); // let angleI = rng.inInterval({ min: 0, max: 120 });
//
//Now we have 3 lightness values, pick a random hue and sat // //Now we have 3 lightness values, pick a random hue and sat
let h1 = rng.inInterval({ min: 0, max: 360 }), // let h1 = rng.inInterval({ min: 0, max: 360 }),
h2 = h1 + angleI, // h2 = h1 + angleI,
h3 = 360 * ((((h1 + h2) / 2 + 180) / 360) % 1); // h3 = 360 * ((((h1 + h2) / 2 + 180) / 360) % 1);
//
h2 = 360 * ((h2 / 360) % 1); // h2 = 360 * ((h2 / 360) % 1);
//
let Hs = [h1, h2, h3]; // let Hs = [h1, h2, h3];
//
let Ss = [ // let Ss = [
rng.fromGaussian({ mean: 100, stddev: 40 }), // rng.fromGaussian({ mean: 100, stddev: 40 }),
rng.fromGaussian({ mean: 100, stddev: 40 }), // rng.fromGaussian({ mean: 100, stddev: 40 }),
rng.fromGaussian({ mean: 100, stddev: 40 }), // rng.fromGaussian({ mean: 100, stddev: 40 }),
]; // ];
Ss = Ss.map((x) => clamp(x, 0, 100)); // Ss = Ss.map((x) => clamp(x, 0, 100));
//
//zip // //zip
let hsls = Ls.map((k, i) => [Hs[i], Ss[i], k]); // let hsls = Ls.map((k, i) => [Hs[i], Ss[i], k]);
let hexcolors = hsls.map((x) => hsluvToHex(x as ColorTuple)); // let hexcolors = hsls.map((x) => hsluvToHex(x as ColorTuple));
//
//scramble order // //scramble order
hexcolors = rng.uniqueChoices(hexcolors, hexcolors.length); // hexcolors = rng.uniqueChoices(hexcolors, hexcolors.length);
return hexcolors; // return hexcolors;
} // }
/* /*
function threeVColor(rng: Random): string[] { function threeVColor(rng: Random): string[] {
@ -219,16 +226,16 @@ function createSimplePaletteGenerator(
*/ */
function createTriadPaletteGenerator( function createTriadPaletteGenerator(
createTriad: (rng: Random) => string[] createTriad: (rng: Random, config: PaletteAlgorithmConfig) => string[]
): RandomPaletteGenerator { ): RandomPaletteGenerator {
return (numEntries: number, rng: Random): string[] => { return (numEntries: number, rng: Random, config?): string[] => {
let colors: string[] = []; let colors: string[] = [];
let n = Math.floor(numEntries / 3) + 1; let n = Math.floor(numEntries / 3) + 1;
if (numEntries == 3) { if (numEntries == 3) {
colors = colors.concat(createTriad(rng)); colors = colors.concat(createTriad(rng, config));
} else { } else {
for (let i = 0; i < n; i++) colors = colors.concat(createTriad(rng)); for (let i = 0; i < n; i++) colors = colors.concat(createTriad(rng, config));
colors = colors.slice(0, numEntries); colors = colors.slice(0, numEntries);
} }
@ -241,7 +248,8 @@ const PALETTE_GENERATORS: {
} = { } = {
RGB: createSimplePaletteGenerator(createRandomRGBColor), RGB: createSimplePaletteGenerator(createRandomRGBColor),
CIELUV: createSimplePaletteGenerator(createRandomCIELUVColor), CIELUV: createSimplePaletteGenerator(createRandomCIELUVColor),
threevals: createTriadPaletteGenerator(threeVColor), threevals: createTriadPaletteGenerator(create3Vconfig()),
// threevals: createTriadPaletteGenerator(threeVColor),
//randgrey: createTriadPaletteGenerator(createRandGrey), //randgrey: createTriadPaletteGenerator(createRandGrey),
//threev15: createTriadPaletteGenerator(create3V180(15)), //threev15: createTriadPaletteGenerator(create3V180(15)),
//threev30: createTriadPaletteGenerator(create3V180(15)), //threev30: createTriadPaletteGenerator(create3V180(15)),
@ -266,7 +274,8 @@ export const RANDOM_PALETTE_ALGORITHMS = Object.keys(
export function createRandomColorPalette( export function createRandomColorPalette(
numEntries: number, numEntries: number,
rng: Random = new Random(), rng: Random = new Random(),
algorithm: RandomPaletteAlgorithm = DEFAULT_RANDOM_PALETTE_ALGORITHM algorithm: RandomPaletteAlgorithm = DEFAULT_RANDOM_PALETTE_ALGORITHM,
config: PaletteAlgorithmConfig = {},
): string[] { ): string[] {
return PALETTE_GENERATORS[algorithm](numEntries, rng); return PALETTE_GENERATORS[algorithm](numEntries, rng, config);
} }

Wyświetl plik

@ -3,7 +3,7 @@ import { PaletteAlgorithmWidget } from "./palette-algorithm-widget";
import { Random } from "./random"; import { Random } from "./random";
import { import {
createRandomColorPalette, createRandomColorPalette,
DEFAULT_RANDOM_PALETTE_ALGORITHM, DEFAULT_RANDOM_PALETTE_ALGORITHM, PaletteAlgorithmConfig,
RandomPaletteAlgorithm, RandomPaletteAlgorithm,
} from "./random-colors"; } from "./random-colors";
import { SvgCompositionContext } from "./svg-composition-context"; import { SvgCompositionContext } from "./svg-composition-context";
@ -14,12 +14,14 @@ type SvgCompositionColors = Pick<
>; >;
function createRandomCompositionColors( function createRandomCompositionColors(
alg: RandomPaletteAlgorithm alg: RandomPaletteAlgorithm,
config?: PaletteAlgorithmConfig
): SvgCompositionColors { ): SvgCompositionColors {
const [background, stroke, fill] = createRandomColorPalette( const [background, stroke, fill] = createRandomColorPalette(
3, 3,
undefined, undefined,
alg alg,
config
); );
return { background, stroke, fill }; return { background, stroke, fill };
} }
@ -34,10 +36,11 @@ export const RandomizerWidget: React.FC<RandomizerWidgetProps> = (props) => {
const [paletteAlg, setPaletteAlg] = useState<RandomPaletteAlgorithm>( const [paletteAlg, setPaletteAlg] = useState<RandomPaletteAlgorithm>(
DEFAULT_RANDOM_PALETTE_ALGORITHM DEFAULT_RANDOM_PALETTE_ALGORITHM
); );
const [paletteConfig, setPaletteConfig] = useState({})
const [randType, setRandType] = useState<RandType>("colors and symbols"); const [randType, setRandType] = useState<RandType>("colors and symbols");
const randomize = () => { const randomize = () => {
if (randType === "colors" || randType === "colors and symbols") { if (randType === "colors" || randType === "colors and symbols") {
props.onColorsChange(createRandomCompositionColors(paletteAlg)); props.onColorsChange(createRandomCompositionColors(paletteAlg, paletteConfig));
} }
if (randType === "symbols" || randType === "colors and symbols") { if (randType === "symbols" || randType === "colors and symbols") {
props.onSymbolsChange(new Random(Date.now())); props.onSymbolsChange(new Random(Date.now()));
@ -63,7 +66,7 @@ export const RandomizerWidget: React.FC<RandomizerWidgetProps> = (props) => {
{makeRadio("symbols")} {makeRadio("symbols")}
{makeRadio("colors and symbols")} {makeRadio("colors and symbols")}
{randType !== "symbols" && ( {randType !== "symbols" && (
<PaletteAlgorithmWidget value={paletteAlg} onChange={setPaletteAlg} /> <PaletteAlgorithmWidget value={paletteAlg} onChange={setPaletteAlg} onPaletteConfigChange={setPaletteConfig} />
)} )}
{props.children} {props.children}
<button accessKey="r" onClick={randomize}> <button accessKey="r" onClick={randomize}>

12768
package-lock.json wygenerowano

Plik diff jest za duży Load Diff