feature: color parameter sliders for Maneesh\'s algorithm
rodzic
1a4426472e
commit
a44a4bd255
|
@ -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) => (
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
Plik diff jest za duży
Load Diff
Ładowanie…
Reference in New Issue