import React, { useRef, useState } from "react"; import { AutoSizingSvg } from "../auto-sizing-svg"; import { getBoundingBoxCenter } from "../bounding-box"; import { ColorWidget } from "../color-widget"; import { DEFAULT_BG_COLOR } from "../colors"; import { ExportSvgButton } from "../export-svg"; import { HoverDebugHelper } from "../hover-debug-helper"; import { NumericSlider } from "../numeric-slider"; import { createSvgSymbolContext, safeGetAttachmentPoint, SvgSymbolContent, SvgSymbolContext, SvgSymbolData, swapColors, } from "../svg-symbol"; import { VocabularyWidget } from "../vocabulary-widget"; import { svgRotate, svgScale, SvgTransform, svgTranslate, } from "../svg-transform"; import { SvgVocabulary } from "../svg-vocabulary"; import { SymbolContextWidget } from "../symbol-context-widget"; import { NumericRange, range } from "../util"; import { Random } from "../random"; import { PointWithNormal } from "../specs"; import { getAttachmentTransforms } from "../attach"; import { Checkbox } from "../checkbox"; const CIRCLE_1_DEFAULTS: MandalaCircleParams = { data: SvgVocabulary.get("eye_vertical"), radius: 50, numSymbols: 6, }; const CIRCLE_2_DEFAULTS: MandalaCircleParams = { data: SvgVocabulary.get("leg"), radius: 0, numSymbols: 3, }; const CIRCLE_2_DEFAULT_SCALE = 0.5; const RADIUS: NumericRange = { min: 0, max: 1000, step: 1, }; const NUM_SYMBOLS: NumericRange = { min: 1, max: 30, step: 1, }; const SCALE: NumericRange = { min: 0.1, max: 2, step: 0.1, }; /** * Returns the anchor point of the given symbol; if it doesn't have * an anchor point, return a reasonable default one by taking the * center of the symbol and having the normal point along the positive * x-axis. */ function getAnchorOrCenter(symbol: SvgSymbolData): PointWithNormal { return ( safeGetAttachmentPoint(symbol, "anchor") || { point: getBoundingBoxCenter(symbol.bbox), normal: { x: 1, y: 0 }, } ); } type MandalaCircleParams = { data: SvgSymbolData; radius: number; numSymbols: number; }; const MandalaCircle: React.FC = ( props ) => { const degreesPerItem = 360 / props.numSymbols; const { translation, rotation } = getAttachmentTransforms( { point: { x: 0, y: 0 }, normal: { x: 1, y: 0 }, }, getAnchorOrCenter(props.data) ); const symbol = ( ); const symbols = range(props.numSymbols).map((i) => ( )); return <>{symbols}; }; const MandalaCircleParamsWidget: React.FC<{ idPrefix: string; value: MandalaCircleParams; onChange: (value: MandalaCircleParams) => void; }> = ({ idPrefix, value, onChange }) => { return (
onChange({ ...value, data })} choices={SvgVocabulary} /> onChange({ ...value, radius })} {...RADIUS} /> onChange({ ...value, numSymbols })} {...NUM_SYMBOLS} />
); }; function getRandomCircleParams(rng: Random): MandalaCircleParams { return { data: rng.choice(SvgVocabulary.items), radius: rng.inRange(RADIUS), numSymbols: rng.inRange(NUM_SYMBOLS), }; } export const MandalaPage: React.FC<{}> = () => { const svgRef = useRef(null); const [bgColor, setBgColor] = useState(DEFAULT_BG_COLOR); const [circle1, setCircle1] = useState( CIRCLE_1_DEFAULTS ); const [circle2, setCircle2] = useState( CIRCLE_2_DEFAULTS ); const [symbolCtx, setSymbolCtx] = useState(createSvgSymbolContext()); const [useTwoCircles, setUseTwoCircles] = useState(false); const [invertCircle2, setInvertCircle2] = useState(true); const [circle2Scale, setCircle2Scale] = useState(CIRCLE_2_DEFAULT_SCALE); const randomize = () => { const rng = new Random(Date.now()); setCircle1(getRandomCircleParams(rng)); setCircle2(getRandomCircleParams(rng)); setCircle2Scale(rng.inRange(SCALE)); }; const circle2SymbolCtx = invertCircle2 ? swapColors(symbolCtx) : symbolCtx; return ( <>

Mandala!

{" "}
First circle
{useTwoCircles && (
Second circle
)}
{" "}
{useTwoCircles && ( )} ); };