import React, { useRef, useState } from "react"; import { AutoSizingSvg } from "../auto-sizing-svg"; import { ExportWidget } from "../export-svg"; import { HoverDebugHelper } from "../hover-debug-helper"; import { NumericSlider } from "../numeric-slider"; import { noFillIfShowingSpecs, SvgSymbolContext, swapColors, } from "../svg-symbol"; import { VocabularyWidget } from "../vocabulary-widget"; import { svgRotate, svgScale, SvgTransform } from "../svg-transform"; import { SvgVocabulary } from "../svg-vocabulary"; import { isEvenNumber, NumericRange } from "../util"; import { Random } from "../random"; import { Checkbox } from "../checkbox"; import { CompositionContextWidget, createSvgCompositionContext, } from "../svg-composition-context"; import { Page } from "../page"; import { MandalaCircle, MandalaCircleParams } from "../mandala-circle"; import { useAnimationPct } from "../animation"; type ExtendedMandalaCircleParams = MandalaCircleParams & { scaling: number; rotation: number; symbolScaling: number; symbolRotation: number; animateSymbolRotation: boolean; }; const CIRCLE_1_DEFAULTS: ExtendedMandalaCircleParams = { data: SvgVocabulary.get("eye"), radius: 300, numSymbols: 5, scaling: 1, rotation: 0, symbolScaling: 1, symbolRotation: 0, invertEveryOtherSymbol: false, animateSymbolRotation: false, }; const CIRCLE_2_DEFAULTS: ExtendedMandalaCircleParams = { data: SvgVocabulary.get("leg"), radius: 0, numSymbols: 3, scaling: 0.5, rotation: 0, symbolScaling: 1, symbolRotation: 0, invertEveryOtherSymbol: false, animateSymbolRotation: false, }; const RADIUS: NumericRange = { min: -500, max: 500, step: 1, }; const NUM_SYMBOLS: NumericRange = { min: 1, max: 20, step: 1, }; const SCALING: NumericRange = { min: 0.1, max: 1, step: 0.05, }; const ROTATION: NumericRange = { min: 0, max: 359, step: 1, }; const DURATION_SECS: NumericRange = { min: 0.5, max: 10, step: 0.1, }; const DEFAULT_DURATION_SECS = 3; const ExtendedMandalaCircle: React.FC< ExtendedMandalaCircleParams & SvgSymbolContext > = ({ scaling, rotation, symbolScaling, symbolRotation, ...props }) => { props = { ...props, symbolTransforms: [svgScale(symbolScaling), svgRotate(symbolRotation)], }; return ( ); }; function animateMandalaCircleParams( value: ExtendedMandalaCircleParams, animPct: number ): ExtendedMandalaCircleParams { if (value.animateSymbolRotation) { value = { ...value, symbolRotation: animPct * ROTATION.max, }; } return value; } function isAnyMandalaCircleAnimated( values: ExtendedMandalaCircleParams[] ): boolean { return values.some((value) => value.animateSymbolRotation); } const ExtendedMandalaCircleParamsWidget: React.FC<{ idPrefix: string; value: ExtendedMandalaCircleParams; onChange: (value: ExtendedMandalaCircleParams) => void; }> = ({ idPrefix, value, onChange }) => { return (
onChange({ ...value, data })} choices={SvgVocabulary} /> onChange({ ...value, radius })} {...RADIUS} /> onChange({ ...value, numSymbols })} {...NUM_SYMBOLS} /> onChange({ ...value, scaling })} {...SCALING} /> onChange({ ...value, rotation })} {...ROTATION} /> onChange({ ...value, symbolScaling })} {...SCALING} /> onChange({ ...value, symbolRotation })} {...ROTATION} /> onChange({ ...value, animateSymbolRotation }) } /> onChange({ ...value, invertEveryOtherSymbol }) } />
); }; function getRandomCircleParams(rng: Random): MandalaCircleParams { return { data: rng.choice(SvgVocabulary.items), radius: rng.inRange(RADIUS), numSymbols: rng.inRange(NUM_SYMBOLS), invertEveryOtherSymbol: rng.bool(), }; } export const MandalaPage: React.FC<{}> = () => { const svgRef = useRef(null); const canvasRef = useRef(null); const [circle1, setCircle1] = useState(CIRCLE_1_DEFAULTS); const [circle2, setCircle2] = useState(CIRCLE_2_DEFAULTS); const [durationSecs, setDurationSecs] = useState(DEFAULT_DURATION_SECS); const [baseCompCtx, setBaseCompCtx] = useState(createSvgCompositionContext()); const [useTwoCircles, setUseTwoCircles] = useState(false); const [invertCircle2, setInvertCircle2] = useState(true); const [firstBehindSecond, setFirstBehindSecond] = useState(false); const randomize = () => { const rng = new Random(Date.now()); setCircle1({ ...circle1, ...getRandomCircleParams(rng) }); setCircle2({ ...circle2, ...getRandomCircleParams(rng) }); }; const isAnimated = isAnyMandalaCircleAnimated([circle1, circle2]); const animPct = useAnimationPct(isAnimated ? durationSecs * 1000 : 0); const symbolCtx = noFillIfShowingSpecs(baseCompCtx); const circle2SymbolCtx = invertCircle2 ? swapColors(symbolCtx) : symbolCtx; const circles = [ , ]; if (useTwoCircles) { circles.push( ); if (firstBehindSecond) { circles.reverse(); } } return (
First circle
{useTwoCircles && (
Second circle {" "}
)} {isAnimated && ( setDurationSecs(duration)} {...DURATION_SECS} /> )}
{" "}
{circles}
); };