Add 'always include this symbol' dropdown. (#115)

Fixes #36.
pull/118/head
Atul Varma 2021-05-12 21:55:18 -04:00 zatwierdzone przez GitHub
rodzic f49fa7170d
commit 8151663860
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
4 zmienionych plików z 84 dodań i 9 usunięć

Wyświetl plik

@ -1,6 +1,10 @@
import React, { useContext, useRef, useState } from "react";
import { SvgVocabulary } from "../svg-vocabulary";
import { noFillIfShowingSpecs, SvgSymbolData } from "../svg-symbol";
import React, { useContext, useMemo, useRef, useState } from "react";
import { SvgVocabulary, SvgVocabularyWithBlank } from "../svg-vocabulary";
import {
EMPTY_SVG_SYMBOL_DATA,
noFillIfShowingSpecs,
SvgSymbolData,
} from "../svg-symbol";
import {
AttachmentPointType,
ATTACHMENT_POINT_TYPES,
@ -27,6 +31,7 @@ import {
} from "../svg-composition-context";
import { Page } from "../page";
import { RandomizerWidget } from "../randomizer-widget";
import { VocabularyWidget } from "../vocabulary-widget";
/** Symbols that can be the "root" (i.e., main body) of a creature. */
const ROOT_SYMBOLS = SvgVocabulary.items.filter(
@ -174,6 +179,40 @@ function getDownloadBasename(randomSeed: number) {
return `mystic-symbolic-creature-${randomSeed}`;
}
function creatureHasSymbol(
creature: CreatureSymbol,
symbol: SvgSymbolData
): boolean {
if (creature.data === symbol) return true;
if (creature.attachments.some((a) => creatureHasSymbol(a, symbol)))
return true;
return creature.nests.some((n) => creatureHasSymbol(n, symbol));
}
function repeatUntilSymbolIsIncluded(
symbol: SvgSymbolData,
rng: Random,
createCreature: (rng: Random) => CreatureSymbol,
maxAttempts = 10_000
): CreatureSymbol {
if (symbol === EMPTY_SVG_SYMBOL_DATA) return createCreature(rng);
for (let i = 0; i < maxAttempts; i++) {
const creature = createCreature(rng);
if (creatureHasSymbol(creature, symbol)) return creature;
}
// We don't want to hold up the UI forever so just log a message and
// return *something*.
console.log(
`Tried to create a creature with the ${symbol.name} symbol ` +
`but gave up after ${maxAttempts} attempts.`
);
return createCreature(rng);
}
export const CreaturePage: React.FC<{}> = () => {
const svgRef = useRef<SVGSVGElement>(null);
const [randomSeed, setRandomSeed] = useState<number>(Date.now());
@ -186,10 +225,22 @@ export const CreaturePage: React.FC<{}> = () => {
...defaultCtx,
...compCtx,
});
const creature = COMPLEXITY_LEVEL_GENERATORS[complexity]({
rng: new Random(randomSeed),
randomlyInvert,
});
const [alwaysInclude, setAlwaysInclude] = useState<SvgSymbolData>(
EMPTY_SVG_SYMBOL_DATA
);
const creature = useMemo(
() =>
repeatUntilSymbolIsIncluded(
alwaysInclude,
new Random(randomSeed),
(rng) =>
COMPLEXITY_LEVEL_GENERATORS[complexity]({
rng,
randomlyInvert,
})
),
[alwaysInclude, complexity, randomSeed, randomlyInvert]
);
return (
<Page title="Creature!">
@ -218,7 +269,16 @@ export const CreaturePage: React.FC<{}> = () => {
<RandomizerWidget
onColorsChange={(colors) => setCompCtx({ ...compCtx, ...colors })}
onSymbolsChange={newRandomSeed}
/>
>
<div className="thingy">
<VocabularyWidget
label="Always include this symbol"
value={alwaysInclude}
onChange={setAlwaysInclude}
choices={SvgVocabularyWithBlank}
/>
</div>
</RandomizerWidget>
<div className="thingy">
<ExportWidget
basename={getDownloadBasename(randomSeed)}

Wyświetl plik

@ -65,6 +65,7 @@ export const RandomizerWidget: React.FC<RandomizerWidgetProps> = (props) => {
{randType !== "symbols" && (
<PaletteAlgorithmWidget value={paletteAlg} onChange={setPaletteAlg} />
)}
{props.children}
<button accessKey="r" onClick={randomize}>
<u>R</u>andomize!
</button>

Wyświetl plik

@ -16,6 +16,15 @@ export type SvgSymbolData = {
specs?: Specs;
};
export const EMPTY_SVG_SYMBOL_DATA: SvgSymbolData = {
name: "",
bbox: {
x: { min: 0, max: 0 },
y: { min: 0, max: 0 },
},
layers: [],
};
export type SvgSymbolElement = (
| {
tagName: "g";

Wyświetl plik

@ -1,5 +1,10 @@
import type { SvgSymbolData } from "./svg-symbol";
import { EMPTY_SVG_SYMBOL_DATA, SvgSymbolData } from "./svg-symbol";
import { Vocabulary } from "./vocabulary";
import _SvgVocabulary from "./_svg-vocabulary";
export const SvgVocabulary = new Vocabulary<SvgSymbolData>(_SvgVocabulary);
export const SvgVocabularyWithBlank = new Vocabulary<SvgSymbolData>([
EMPTY_SVG_SYMBOL_DATA,
..._SvgVocabulary,
]);