diff --git a/lib/pages/creature-page.tsx b/lib/pages/creature-page.tsx index 48a3f7a..b5985c8 100644 --- a/lib/pages/creature-page.tsx +++ b/lib/pages/creature-page.tsx @@ -28,6 +28,7 @@ import { ColorWidget } from "../color-widget"; import { NumericSlider } from "../numeric-slider"; import { DEFAULT_BG_COLOR } from "../colors"; import { Checkbox } from "../checkbox"; +import { createRandomColorPalette } from "../random-colors"; /** Symbols that can be the "root" (i.e., main body) of a creature. */ const ROOT_SYMBOLS = SvgVocabulary.items.filter( @@ -192,6 +193,11 @@ export const CreaturePage: React.FC<{}> = () => { rng: new Random(randomSeed), randomlyInvert, }); + const randomizeColors = () => { + const [bgColor, stroke, fill] = createRandomColorPalette(3); + setBgColor(bgColor); + setSymbolCtx({ ...symbolCtx, stroke, fill }); + }; return ( <> @@ -199,6 +205,11 @@ export const CreaturePage: React.FC<{}> = () => { {" "} +
+ +
= () => { const circle2SymbolCtx = invertCircle2 ? swapColors(symbolCtx) : symbolCtx; + const randomizeColors = () => { + const [bgColor, stroke, fill] = createRandomColorPalette(3); + setBgColor(bgColor); + setBaseSymbolCtx({ ...baseSymbolCtx, stroke, fill }); + }; + const circles = [ , ]; @@ -293,6 +300,11 @@ export const MandalaPage: React.FC<{}> = () => { onChange={setBgColor} />{" "} +
+ +
First circle { + it("clamps values over 255 to 255", () => { + expect(clampedByteToHex(500)).toBe("ff"); + }); + + it("clamps values under 0 to 0", () => { + expect(clampedByteToHex(-50)).toBe("00"); + }); + + it("zero-pads values", () => { + expect(clampedByteToHex(10)).toBe("0a"); + }); + + it("works with numbers that don't need zero-padding", () => { + expect(clampedByteToHex(22)).toBe("16"); + }); +}); + +test("createRandomColorPalette() works", () => { + const palette = createRandomColorPalette(3); + expect(palette).toHaveLength(3); + for (let color of palette) { + expect(color).toMatch(/^\#[0-9a-f]{6}$/); + } +}); diff --git a/lib/random-colors.ts b/lib/random-colors.ts new file mode 100644 index 0000000..6005123 --- /dev/null +++ b/lib/random-colors.ts @@ -0,0 +1,38 @@ +import { Random } from "./random"; +import { range } from "./util"; + +/** + * Clamp the given number to be between 0 and 255, then + * convert it to hexadecimal. + */ +export function clampedByteToHex(value: number): string { + if (value < 0) { + value = 0; + } else if (value > 255) { + value = 255; + } + let hex = value.toString(16); + if (hex.length === 1) { + hex = "0" + hex; + } + return hex; +} + +function createRandomColor(rng: Random): string { + const rgb = range(3).map(() => rng.inRange({ min: 0, max: 255, step: 1 })); + return "#" + rgb.map(clampedByteToHex).join(""); +} + +/** + * Create a random color palette with the given number of + * entries, optionally using the given random number generator. + * + * 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() +): string[] { + return range(numEntries).map(() => createRandomColor(rng)); +}