rodzic
9de487fac0
commit
949d8d72dd
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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}$/);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Ładowanie…
Reference in New Issue