diff --git a/src/App.tsx b/src/App.tsx index 578dff7..b812cf6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { Grid, IconButton } from '@material-ui/core'; +import { Grid } from '@material-ui/core'; import React, { RefObject, useCallback, @@ -40,7 +40,6 @@ import FirstTimeInfo from './components/info/FirstTimeInfo'; import InfoDialog from './components/info/InfoDialog'; import AnimationFinalCard, { AnimationStatus, - MISIUREWICZ_POINTS, } from './components/tans_theorem/AnimationFinalCard'; import JuliaRenderer from './components/render/JuliaRenderer'; // import 'typeface-roboto'; @@ -55,16 +54,18 @@ import { PreperiodicPoint, similarPoints, } from './components/tans_theorem/tansTheoremUtils'; -import SimilarityMenu from './components/tans_theorem/SimilarityMenu'; import SimilarityAnimationCard from './components/tans_theorem/SimilarityAnimationCard'; -import MisiurewiczDomainsMenu from './components/tans_theorem/MisiurewiczDomainsMenu'; -import PointsInfoCard from './components/tans_theorem/MisiurewiczPointsMenu'; -import ArrowBackwardIcon from '@material-ui/icons/ArrowBack'; -import CloseIcon from '@material-ui/icons/Close'; +import PointsMenuJulia from './components/tans_theorem/PointsMenuJulia'; +import PointsMenuMandelbrot from './components/tans_theorem/PointsMenuMandelbrot'; import MapMarkerManager from './components/tans_theorem/MapMarkerManager'; import ZoomMenu from './components/tans_theorem/ZoomMenu'; -import PlayCard from './components/tans_theorem/PlayCard'; -import IntroCard from './components/tans_theorem/IntroCard'; +import SelfSimilaritySlider from './components/tans_theorem/SelfSimilaritySlider'; +import IntroCard from './components/tans_theorem/IntroDialog'; +import { misiurewiczPairs } from './components/tans_theorem/MPoints'; + +const MISIUREWICZ_POINTS: PreperiodicPoint[] = misiurewiczPairs + .slice(0, 200) + .map((p) => new PreperiodicPoint(p, p, false)); const defaultP: XYType = [-0.10109636384562218, +0.9562865108091414]; const defaultMisiurewiczPoint = new PreperiodicPoint(defaultP, defaultP, false); @@ -191,7 +192,7 @@ function App({ settings }: { settings: settingsDefinitionsType }): JSX.Element { } }, ); - // explicitly not adding mandelbrotControls to the deps list + // ficitly not adding mandelbrotControls to the deps list // eslint-disable-next-line react-hooks/exhaustive-deps }, [precision, updateM]); @@ -244,6 +245,9 @@ function App({ settings }: { settings: settingsDefinitionsType }): JSX.Element { const [showTan, setShowTan] = useState(false); const toggleTan = () => { setShowTan(true); + if (animationState !== AnimationStatus.INTRO) + setAnimationState(AnimationStatus.SELECT_MANDELBROT_POINT); + warpToPoint(mandelbrotControls, { xy: [0, 0], z: 0.5, @@ -267,14 +271,17 @@ function App({ settings }: { settings: settingsDefinitionsType }): JSX.Element { const [animationState, setAnimationState] = React.useState(AnimationStatus.INTRO); const [magnification, setMagnification] = React.useState(1); - const [focusedPointMandelbrot, setFocusedPointMandelbrot] = useState( - defaultMisiurewiczPoint, + const [mandelbrotPoints] = useState( + MISIUREWICZ_POINTS.sort((a, b) => a.factorMagnitude - b.factorMagnitude), ); const [juliaPoints, setJuliaPoints] = useState( similarPoints(defaultMisiurewiczPoint, 4).sort( (a, b) => a.factorMagnitude - b.factorMagnitude, ), ); + const [focusedPointMandelbrot, setFocusedPointMandelbrot] = useState( + defaultMisiurewiczPoint, + ); const [focusedPointJulia, setFocusedPointJulia] = useState(juliaPoints[0]); const [aspectRatio, setAspectRatio] = useState(1); @@ -291,6 +298,7 @@ function App({ settings }: { settings: settingsDefinitionsType }): JSX.Element { settings.rotateWhileZooming, ); }; + const alignM = (z: number) => { const newMag = normaliseZoom(z, focusedPointJulia); align(newMag); @@ -301,23 +309,20 @@ function App({ settings }: { settings: settingsDefinitionsType }): JSX.Element { align(newMag); }; - const handleMisiurewiczPointSelection = useCallback( - (pointM: PreperiodicPoint): void => { - const similars = similarPoints(pointM, 4).sort( - (a, b) => a.factorMagnitude - b.factorMagnitude, - ); - if (similars.length > 0) { - setFocusedPointMandelbrot(pointM); - setFocusedPointJulia(similars[0]); - setJuliaPoints(similars); - } - }, - [], - ); + const handleMisiurewiczPointSelection = useCallback((point: PreperiodicPoint): void => { + const similars = similarPoints(point, 4).sort( + (a, b) => a.factorMagnitude - b.factorMagnitude, + ); + if (similars.length > 0) { + setFocusedPointMandelbrot(point); + setFocusedPointJulia(similars[0]); + setJuliaPoints(similars); + } + }, []); const handleSimilarPointSelection = useCallback( - (pointJ: PreperiodicPoint): void => { - setFocusedPointJulia(pointJ); + (point: PreperiodicPoint): void => { + setFocusedPointJulia(point); }, [setFocusedPointJulia], ); @@ -343,17 +348,6 @@ function App({ settings }: { settings: settingsDefinitionsType }): JSX.Element { }; useInterval(updateAspectRatio, 1000); - const BackButton = () => ( - - - - ); - const QuitButton = () => ( - - - - ); - const handleNearest = (xy: XYType) => { const mPoint = findNearestMisiurewiczPoint(xy, 10000); if (mPoint[0] !== 0 && mPoint[1] !== 0) { @@ -362,6 +356,20 @@ function App({ settings }: { settings: settingsDefinitionsType }): JSX.Element { } }; + const handleMisiurewiczGo = () => { + setAnimationState(AnimationStatus.SELECT_JULIA_POINT); + warpToPoint(mandelbrotControls, { + xy: focusedPointMandelbrot.point, + z: 1, + theta: 0, + }); + warpToPoint(juliaControls, { + xy: [0, 0], + z: 0.5, + theta: 0, + }); + }; + return ( <> @@ -382,120 +390,119 @@ function App({ settings }: { settings: settingsDefinitionsType }): JSX.Element { precision={precision} precisionFormatter={precisionFormatter} /> -
- {showTan ? ( -
+ { + setAnimationState(AnimationStatus.SELECT_MANDELBROT_POINT); }} - > - {animationState === AnimationStatus.INTRO ? ( - - ) : null} - {animationState === AnimationStatus.SELECT_MANDELBROT_POINT ? ( - settings.shadeMisiurewiczDomains ? ( - - ) : ( - - ) - ) : null} - - {animationState === AnimationStatus.SELECT_JULIA_POINT ? ( - - ) : null} - {[ - AnimationStatus.ZOOM_M, - AnimationStatus.ZOOM_J, - AnimationStatus.ROTATE_M, - AnimationStatus.ROTATE_J, - ].includes(animationState) ? ( - - ) : null} -
- ) : null} - {showTan && - settings.rotateWhileZooming && - animationState === AnimationStatus.PLAY ? ( - - ) : null} -
- + ].includes(animationState) ? ( + { + handleMisiurewiczPointSelection(c); + warpToPoint(mandelbrotControls, { + xy: c.point, + z: c.factorMagnitude, + theta: 0, + }); + }} + handlePointSelectionJulia={handleSimilarPointSelection} + /> + ) : null} + {animationState === AnimationStatus.SELECT_MANDELBROT_POINT ? ( + + ) : null} + {animationState === AnimationStatus.SELECT_JULIA_POINT ? ( + { + setAnimationState(AnimationStatus.ZOOM_M); + warpToPoint(juliaControls, { + xy: focusedPointJulia.point, + z: 1, + theta: 0, + }); + }} + /> + ) : null} + {[ + AnimationStatus.ZOOM_M, + AnimationStatus.ZOOM_J, + AnimationStatus.ROTATE_M, + AnimationStatus.ROTATE_J, + ].includes(animationState) ? ( + { + return; + }} + handleQuit={handleReset} + show={true} + mandelbrotControls={mandelbrotControls} + juliaControls={juliaControls} + animationState={animationState} + setAnimationState={setAnimationState} + focusedPointMandelbrot={focusedPointMandelbrot} + focusedPointJulia={focusedPointJulia} + /> + ) : null} + { + return; + }} + /> + {settings.rotateWhileZooming && animationState === AnimationStatus.PLAY ? ( + + ) : null} + + ) : null} {showTan && @@ -547,7 +554,7 @@ function App({ settings }: { settings: settingsDefinitionsType }): JSX.Element { )} - + diff --git a/src/common/tans.ts b/src/common/tans.ts index 428e601..ffa1a30 100644 --- a/src/common/tans.ts +++ b/src/common/tans.ts @@ -2,11 +2,6 @@ import { AnimationStatus } from '../components/tans_theorem/AnimationFinalCard'; import { PreperiodicPoint } from '../components/tans_theorem/tansTheoremUtils'; import { ViewerControlSprings } from './types'; -export interface AnimationFinalCardProps { - animationState: AnimationStatus; - handleReset: () => void; -} - export interface MarkerManagerProps { show: boolean; aspectRatio: number; @@ -16,16 +11,25 @@ export interface MarkerManagerProps { points: PreperiodicPoint[]; } -export interface InfoCardProps { +export interface ComplexNumberMarkerProps { + aspectRatio: number; + m: PreperiodicPoint; + viewerControl: ViewerControlSprings; + onClick: () => void; + isFocused: boolean; +} + +export interface SelectMenuProps { + show: boolean; + handleQuit: () => void; + handleGo: () => void; +} + +export interface TansDialogsProps { show: boolean; - mandelbrot: ViewerControlSprings; - julia: ViewerControlSprings; animationState: AnimationStatus; setAnimationState: React.Dispatch>; - focusedPointMandelbrot: PreperiodicPoint; - focusedPointJulia: PreperiodicPoint; - handleMandelbrotSelection: (focusedPointMandelbrot: PreperiodicPoint) => void; - quitButton: () => JSX.Element; + handleQuit: () => void; } export interface PointsListProps { @@ -35,25 +39,13 @@ export interface PointsListProps { handleSelection: (point: PreperiodicPoint) => void; } -export interface MisiurewiczDomainsMenuProps { - show: boolean; - mandelbrot: ViewerControlSprings; - julia: ViewerControlSprings; - setAnimationState: React.Dispatch>; - focusedPointMandelbrot: PreperiodicPoint; - focusedPointJulia: PreperiodicPoint; - quitButton: () => JSX.Element; -} - -export interface ZoomCardProps { - show: boolean; - mandelbrot: ViewerControlSprings; - julia: ViewerControlSprings; +export interface ZoomCardProps extends SelectMenuProps { + mandelbrotControls: ViewerControlSprings; + juliaControls: ViewerControlSprings; animationState: AnimationStatus; setAnimationState: React.Dispatch>; focusedPointMandelbrot: PreperiodicPoint; focusedPointJulia: PreperiodicPoint; - backButton: () => JSX.Element; } export interface PlayCardProps { @@ -61,40 +53,13 @@ export interface PlayCardProps { magnification: number; } -export interface ComplexNumberMarkerProps { - aspectRatio: number; - m: PreperiodicPoint; - viewerControl: ViewerControlSprings; - onClick: () => void; - isFocused: boolean; -} - export interface SimilarityAnimationProps { show: boolean; animationState: AnimationStatus; - focusedPoint: PreperiodicPoint; - focusedPointJulia: PreperiodicPoint; -} - -export interface SimilarityMenuProps { - show: boolean; - julia: ViewerControlSprings; - setAnimationState: React.Dispatch>; - focusedPointMandelbrot: PreperiodicPoint; - focusedPointJulia: PreperiodicPoint; - handleSimilarPointSelection: (focusedPointJulia: PreperiodicPoint) => void; - similarPointsJulia: PreperiodicPoint[]; - backButton: () => JSX.Element; -} - -export interface IntroCardProps { - show: boolean; - mandelbrot: ViewerControlSprings; - julia: ViewerControlSprings; - animationState: AnimationStatus; - setAnimationState: React.Dispatch>; focusedPointMandelbrot: PreperiodicPoint; focusedPointJulia: PreperiodicPoint; - handleMandelbrotSelection: (focusedPointMandelbrot: PreperiodicPoint) => void; - quitButton: () => JSX.Element; + pointsMandelbrot: PreperiodicPoint[]; + pointsJulia: PreperiodicPoint[]; + handlePointSelectionMandelbrot: (focusedPoint: PreperiodicPoint) => void; + handlePointSelectionJulia: (focusedPoint: PreperiodicPoint) => void; } diff --git a/src/components/tans_theorem/AnimationFinalCard.tsx b/src/components/tans_theorem/AnimationFinalCard.tsx index 2fb4d2b..9b315e4 100644 --- a/src/components/tans_theorem/AnimationFinalCard.tsx +++ b/src/components/tans_theorem/AnimationFinalCard.tsx @@ -1,9 +1,7 @@ import React from 'react'; -import { AnimationFinalCardProps } from '../../common/tans'; -import { Card, Grid, IconButton, Typography, Box } from '@material-ui/core'; -import { PreperiodicPoint } from './tansTheoremUtils'; -import { misiurewiczPairs } from './MPoints'; -import ArrowBackwardIcon from '@material-ui/icons/ArrowBack'; +import { SelectMenuProps } from '../../common/tans'; +import { Card, Button, Typography, Box } from '@material-ui/core'; +import { KeyboardArrowLeft } from '@material-ui/icons'; export enum AnimationStatus { INTRO = -1, @@ -16,47 +14,31 @@ export enum AnimationStatus { PLAY = 6, } -export const MISIUREWICZ_POINTS: PreperiodicPoint[] = misiurewiczPairs - .slice(0, 200) - .map((p) => new PreperiodicPoint(p, p, false)) - .sort((a, b) => a.factorMagnitude - b.factorMagnitude); - -const AnimationFinalCard = (props: AnimationFinalCardProps): JSX.Element => { - const BackButton = () => { - return ( - // eslint-disable-next-line react/prop-types - - - - ); - }; - +const AnimationFinalCard = (props: SelectMenuProps): JSX.Element => { return ( <> - {props.animationState === AnimationStatus.PLAY ? ( + {props.show ? ( - - {BackButton()} - - - You are now free to continue magnifying. - - - - higher magnification → stronger similarity - - - - + +
+ You are now free to continue magnifying. + + + higher magnification → stronger similarity + + +
) : null} diff --git a/src/components/tans_theorem/ComplexNumberMarker.tsx b/src/components/tans_theorem/ComplexNumberMarker.tsx index 4069820..d7c9e49 100644 --- a/src/components/tans_theorem/ComplexNumberMarker.tsx +++ b/src/components/tans_theorem/ComplexNumberMarker.tsx @@ -45,7 +45,7 @@ const ComplexNumberMarker = (props: ComplexNumberMarkerProps): JSX.Element => { return (
{ - const goButton = ( - setAnimationState: React.Dispatch>, - ) => { - return ( - - ); - }; - - return ( - - - - {props.quitButton()} - - - Welcome to the Tans theorem explorer! - - - - - - Tan's theorem states that, at particular points, the Mandelbrot set and - Julia sets are almost{' '} - - indistinguishable - - . - - - This mode will take you through the process of selecting a point on each set, - then magnifying and rotating them to best show the similarity. - - - Simply press START, then follow the steps provided. - - - - {goButton(props.setAnimationState)} - - ); -}; - -export default IntroCard; diff --git a/src/components/tans_theorem/IntroDialog.tsx b/src/components/tans_theorem/IntroDialog.tsx new file mode 100644 index 0000000..1550828 --- /dev/null +++ b/src/components/tans_theorem/IntroDialog.tsx @@ -0,0 +1,63 @@ +import { Typography, Box, Dialog } from '@material-ui/core'; +import React from 'react'; +import { SelectMenuProps } from '../../common/tans'; +import { DialogContent, DialogTitle } from '../custom/DialogComponents'; + +const IntroCard = (props: SelectMenuProps): JSX.Element => { + const [open, setOpen] = React.useState(true); + + if (props.show) { + props.handleGo(); + } + + const onClose = () => { + setOpen(false); + }; + + return ( + + Welcome to the Tans theorem explorer! + +
+ + Tan's theorem states that, at particular points, the Mandelbrot set and + the corresponding Julia set are almost{' '} + + indistinguishable + + . + + + This feature will take you through the process of selecting a point on each + set, then magnifying and rotating them to best show the similarity. + + + To start, simply exit this popup then follow the steps provided. + +
+
+
+ ); +}; + +export default IntroCard; diff --git a/src/components/tans_theorem/MapMarkerManager.tsx b/src/components/tans_theorem/MapMarkerManager.tsx index 1e91050..fdd5029 100644 --- a/src/components/tans_theorem/MapMarkerManager.tsx +++ b/src/components/tans_theorem/MapMarkerManager.tsx @@ -4,7 +4,7 @@ import { XYType, ViewerControlSprings } from '../../common/types'; import ComplexNumberMarker from './ComplexNumberMarker'; import { PreperiodicPoint } from './tansTheoremUtils'; -const MAX_MARKERS = 5; +const MAX_MARKERS = 8; /** * Check if a point is within a given "bounding" box. diff --git a/src/components/tans_theorem/MisiurewiczDomainsMenu.tsx b/src/components/tans_theorem/MisiurewiczDomainsMenu.tsx deleted file mode 100644 index 44a1df3..0000000 --- a/src/components/tans_theorem/MisiurewiczDomainsMenu.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { Button, Card, Grow, Typography, Grid } from '@material-ui/core'; -import React from 'react'; -import { MisiurewiczDomainsMenuProps } from '../../common/tans'; -import { AnimationStatus } from './AnimationFinalCard'; -import { warpToPoint } from '../../common/utils'; - -const MisiurewiczDomainsMenu = (props: MisiurewiczDomainsMenuProps): JSX.Element => { - const goButton = ( - setAnimationState: React.Dispatch>, - ) => { - return ( - - ); - }; - - return ( - - - - {props.quitButton()} - - - Pick a point in the Mandelbrot set! - - - - - {goButton(props.setAnimationState)} - - - ); -}; - -export default MisiurewiczDomainsMenu; diff --git a/src/components/tans_theorem/MisiurewiczPointsMenu.tsx b/src/components/tans_theorem/MisiurewiczPointsMenu.tsx deleted file mode 100644 index ffce15b..0000000 --- a/src/components/tans_theorem/MisiurewiczPointsMenu.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { Button, Card, Grow, Typography, Grid } from '@material-ui/core'; -import React from 'react'; -import { InfoCardProps } from '../../common/tans'; -import { AnimationStatus, MISIUREWICZ_POINTS } from './AnimationFinalCard'; -import PointsList from './PointsList'; -import { warpToPoint } from '../../common/utils'; -import { PreperiodicPoint, formatComplexNumber } from './tansTheoremUtils'; - -const PointsInfoCard = (props: InfoCardProps): JSX.Element => { - const goButton = ( - setAnimationState: React.Dispatch>, - ) => { - return ( - - ); - }; - - const handleSelection = (c: PreperiodicPoint) => { - props.handleMandelbrotSelection(c); - warpToPoint(props.mandelbrot, { - xy: c.point, - z: c.factorMagnitude, - theta: 0, - }); - }; - - return ( - - - - {props.quitButton()} - - - Pick a point in the Mandelbrot set! - - - - `${c.toString()} = ${formatComplexNumber(c.point)}`} - handleSelection={handleSelection} - /> - {goButton(props.setAnimationState)} - - - ); -}; - -export default PointsInfoCard; diff --git a/src/components/tans_theorem/PlayCard.tsx b/src/components/tans_theorem/PlayCard.tsx deleted file mode 100644 index c4cdb1a..0000000 --- a/src/components/tans_theorem/PlayCard.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Slider } from '@material-ui/core'; -import React from 'react'; -import { PlayCardProps } from '../../common/tans'; - -const gen = (x: number) => { - const y = Math.abs(x) % 1; - return x < 0 ? 1 - y : y; -}; -const PlayCard = (props: PlayCardProps): JSX.Element => { - const x = - Math.log(props.magnification) / - Math.log(props.focusedPointMandelbrot.selfSimilarityFactorMagnitude); - const progress = gen(x); - - return ( - - ); -}; - -export default PlayCard; diff --git a/src/components/tans_theorem/PointsList.tsx b/src/components/tans_theorem/PointsList.tsx index 93ea6d9..c06ab84 100644 --- a/src/components/tans_theorem/PointsList.tsx +++ b/src/components/tans_theorem/PointsList.tsx @@ -6,9 +6,7 @@ const PointsList = (props: PointsListProps): JSX.Element => { const handleSimilarPointSelection = (event: React.ChangeEvent<{ value: unknown }>) => { const identifier = event.target.value as number; - const chosen = props.points[identifier]; - - props.handleSelection(chosen); + props.handleSelection(props.points[identifier]); }; return ( diff --git a/src/components/tans_theorem/PointsMenuJulia.tsx b/src/components/tans_theorem/PointsMenuJulia.tsx new file mode 100644 index 0000000..a18d11c --- /dev/null +++ b/src/components/tans_theorem/PointsMenuJulia.tsx @@ -0,0 +1,34 @@ +import { Card, Grow, Button } from '@material-ui/core'; +import React from 'react'; +import { SelectMenuProps } from '../../common/tans'; +import { KeyboardArrowRight } from '@material-ui/icons'; +import { KeyboardArrowLeft } from '@material-ui/icons'; + +const PointsInfoCard = (props: SelectMenuProps): JSX.Element => { + return ( + + + +
+ +
+
+
+ ); +}; + +export default PointsInfoCard; diff --git a/src/components/tans_theorem/PointsMenuMandelbrot.tsx b/src/components/tans_theorem/PointsMenuMandelbrot.tsx new file mode 100644 index 0000000..1f29885 --- /dev/null +++ b/src/components/tans_theorem/PointsMenuMandelbrot.tsx @@ -0,0 +1,32 @@ +import { Card, Grow, Button } from '@material-ui/core'; +import React from 'react'; +import { SelectMenuProps } from '../../common/tans'; +import { KeyboardArrowRight } from '@material-ui/icons'; + +const PointsInfoCard = (props: SelectMenuProps): JSX.Element => { + return ( + + + +
+ +
+
+
+ ); +}; + +export default PointsInfoCard; diff --git a/src/components/tans_theorem/SelfSimilaritySlider.tsx b/src/components/tans_theorem/SelfSimilaritySlider.tsx new file mode 100644 index 0000000..1b2d713 --- /dev/null +++ b/src/components/tans_theorem/SelfSimilaritySlider.tsx @@ -0,0 +1,29 @@ +import { Card, Slider } from '@material-ui/core'; +import React from 'react'; +import { PlayCardProps } from '../../common/tans'; + +const gen = (x: number) => { + const y = Math.abs(x) % 1; + return x < 0 ? 1 - y : y; +}; +const SelfSimilaritySlider = (props: PlayCardProps): JSX.Element => { + const x = + Math.log(props.magnification) / + Math.log(props.focusedPointMandelbrot.selfSimilarityFactorMagnitude); + const progress = gen(x); + + return ( + + + + ); +}; + +export default SelfSimilaritySlider; diff --git a/src/components/tans_theorem/SimilarityAnimationCard.tsx b/src/components/tans_theorem/SimilarityAnimationCard.tsx index 387741c..15180da 100644 --- a/src/components/tans_theorem/SimilarityAnimationCard.tsx +++ b/src/components/tans_theorem/SimilarityAnimationCard.tsx @@ -1,5 +1,15 @@ -import { Card, Stepper, StepLabel, Step, Grow } from '@material-ui/core'; -import React from 'react'; +import { + Button, + Card, + Stepper, + StepLabel, + Step, + Grow, + IconButton, + Dialog, + Typography, +} from '@material-ui/core'; +import React, { useState } from 'react'; import { formatAngle, formatComplexNumber, @@ -8,75 +18,298 @@ import { } from './tansTheoremUtils'; import { SimilarityAnimationProps } from '../../common/tans'; import { AnimationStatus } from './AnimationFinalCard'; +import KeyboardArrowLeftIcon from '@material-ui/icons/KeyboardArrowLeft'; +import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight'; +import { DialogContent, DialogDivider, DialogTitle } from '../custom/DialogComponents'; +import ZoomInIcon from '@material-ui/icons/ZoomIn'; +import RotateRightIcon from '@material-ui/icons/RotateRight'; +import PointsList from './PointsList'; -function getSteps( - c: PreperiodicPoint, - cj: PreperiodicPoint, - animationState: AnimationStatus, -) { - if ( - animationState === AnimationStatus.SELECT_MANDELBROT_POINT || - animationState === AnimationStatus.INTRO - ) { - return [ - ['Select point in M', `?`], - ['Select point in J', `?`], - ['Magnify M', `?x`], - ['Magnify J', `?x`], - ['Rotate M', `?°`], - ['Rotate J', `?°`], - ]; - } else if (animationState === AnimationStatus.SELECT_JULIA_POINT) { - return [ - ['Select point in M', `${formatComplexNumber(c.point)}`], - ['Select point in J', `?`], - ['Magnify M', `${round(c.factorMagnitude, 1)}x`], - ['Magnify J', `?x`], - ['Rotate M', `${formatAngle(c.factorAngle)}`], - ['Rotate J', `?°`], - ]; - } else { - return [ - ['Select point in M', `${formatComplexNumber(c.point)}`], - ['Select point in J', `${formatComplexNumber(cj.point)}`], - ['Magnify M', `${round(c.factorMagnitude, 1)}x`], - ['Magnify J', `${round(cj.factorMagnitude, 1)}x`], - ['Rotate M', `${formatAngle(c.factorAngle)}`], - ['Rotate J', `${formatAngle(cj.factorAngle)}`], - ]; - } +interface SimpleDialogProps { + animationState: AnimationStatus; + open: boolean; + onClose: (value: string) => void; } const SimilarityAnimationCard = (props: SimilarityAnimationProps): JSX.Element => { + function getSteps( + c: PreperiodicPoint, + cj: PreperiodicPoint, + animationState: AnimationStatus, + ): [string, JSX.Element | string][] { + if ( + animationState === AnimationStatus.SELECT_MANDELBROT_POINT || + animationState === AnimationStatus.INTRO + ) { + return [ + [ + 'Select point in Mandelbrot set', + // eslint-disable-next-line react/jsx-key + `${formatComplexNumber(c.point)} `} + handleSelection={props.handlePointSelectionMandelbrot} + />, + ], + ['Select point in Julia set', `?`], + ['Magnify Mandelbrot', `${round(c.factorMagnitude, 1)}x`], + ['Magnify Julia', `?x`], + ['Rotate Mandelbrot', `${formatAngle(c.factorAngle)}`], + ['Rotate Julia', `?°`], + ]; + } else if (animationState === AnimationStatus.SELECT_JULIA_POINT) { + return [ + ['Select point in Mandelbrot set', `${formatComplexNumber(c.point)}`], + [ + 'Select point in Julia set', + // eslint-disable-next-line react/jsx-key + `${formatComplexNumber(c.point)} `} + handleSelection={props.handlePointSelectionJulia} + />, + ], + ['Magnify Mandelbrot', `${round(c.factorMagnitude, 1)}x`], + ['Magnify Julia', `${round(cj.factorMagnitude, 1)}x`], + ['Rotate Mandelbrot', `${formatAngle(c.factorAngle)}`], + ['Rotate Julia', `${formatAngle(cj.factorAngle)}`], + ]; + } else { + return [ + ['Select point in Mandelbrot set', `${formatComplexNumber(c.point)}`], + ['Select point in Julia set', `${formatComplexNumber(cj.point)}`], + ['Magnify Mandelbrot', `${round(c.factorMagnitude, 1)}x`], + ['Magnify Julia', `${round(cj.factorMagnitude, 1)}x`], + ['Rotate Mandelbrot', `${formatAngle(c.factorAngle)}`], + ['Rotate Julia', `${formatAngle(cj.factorAngle)}`], + ]; + } + } + const icons = { + '-1': 'null', + 0: 'null', + 1: 'null', + 2: , + 3: , + 4: , + 5: , + 6: 'null', + }; + + const factorTextExpanded = { + '-1': 'null', + 0: 'null', + 1: 'null', + 2: `|u'(c)| = |${formatComplexNumber(props.focusedPointMandelbrot.factor, 2)}| = ${ + Math.round(props.focusedPointMandelbrot.factorMagnitude * 100) / 100 + }`, + 3: `|a| = |${formatComplexNumber(props.focusedPointJulia.factor, 2)}| = ${ + Math.round(props.focusedPointJulia.factorMagnitude * 100) / 100 + }`, + 4: `arg(u'(c)) = arg(${formatComplexNumber( + props.focusedPointMandelbrot.factor, + 2, + )}) = ${formatAngle(props.focusedPointMandelbrot.factorAngle)}`, + 5: `arg(a) = arg(${formatComplexNumber( + props.focusedPointJulia.factor, + 2, + )}) = ${formatAngle(props.focusedPointJulia.factorAngle)}`, + 6: 'null', + }; + + const paragraph1 = { + '-1': 'null', + 0: 'null', + 1: 'null', + 2: `The complex number, u'(c), used to "align" the Mandelbrot set is the following:`, + 3: `The complex number, a, used to "align" the Julia set is the following:`, + 4: `The complex number, u'(c), used to "align" the Mandelbrot set is the following:`, + 5: `The complex number, a, used to "align" the Julia set is the following:`, + 6: 'null', + }; + + const paragraph2 = { + '-1': 'null', + 0: 'null', + 1: 'null', + 2: `To magnify, we take the magnitude of u'(c), so the magnification factor is:`, + 3: `To magnify, we take the magnitude of a, so the magnification factor is:`, + 4: `To rotate, we take the argument of u'(c), so the rotation factor is:`, + 5: `To rotate, we take the argument of a, so the rotation factor is:`, + 6: 'null', + }; + + const numberText = { + '-1': 'null', + 0: 'null', + 1: 'null', + 2: `u'(c) = ${formatComplexNumber(props.focusedPointMandelbrot.factor, 2)}`, + 3: `a = ${formatComplexNumber(props.focusedPointJulia.factor, 2)}`, + 4: `u'(c) = ${formatComplexNumber(props.focusedPointMandelbrot.factor, 2)}`, + 5: `a = ${formatComplexNumber(props.focusedPointJulia.factor, 2)}`, + 6: 'null', + }; + + const dialogText = { + '-1': 'null', + 0: 'null', + 1: 'null', + 2: `The Mandelbrot set magnification factor`, + 3: `The Julia set magnification factor`, + 4: `The Mandelbrot set rotation factor`, + 5: `The Julia set rotation factor`, + 6: 'null', + }; + + function SimpleDialog(props: SimpleDialogProps) { + const { onClose, open } = props; + + const handleClose = () => { + onClose('null'); + }; + + return ( + + + {icons[props.animationState]} + {dialogText[props.animationState]} + + +
+ + To show the similarity between the Mandelbrot and Julia set, we multiply + every point in each set by a particular complex number. Geometrically, + multiplying two complex numbers is equivalent to multiplying their + magnitudes and adding their angles. That's why we magnify and rotate in + this animation! + + + {paragraph1[props.animationState]} + {numberText[props.animationState]} + {paragraph2[props.animationState]} + {factorTextExpanded[props.animationState]} + + + For the full details on calculating the magnification and rotation factors + for each set, read " Lei Tan, Similarity Between the Mandelbrot Set and + Julia Sets", 1990, page 609. + +
+
+
+ ); + } + const steps = getSteps( - props.focusedPoint, + props.focusedPointMandelbrot, props.focusedPointJulia, props.animationState, ); + const [expanded, setExpanded] = useState(false); + + const [open, setOpen] = React.useState(false); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + return ( - - {steps.map((label) => { - const stepProps: { completed?: boolean } = {}; - const labelProps: { optional?: React.ReactNode } = {}; - labelProps.optional = label[1]; - return ( - - {label[0]} - - ); - })} - + setExpanded(!expanded)} + > + {expanded ? : } + + {expanded ? ( + + {steps.map((label) => { + const stepProps: { completed?: boolean } = {}; + const labelProps: { optional?: React.ReactNode } = {}; + labelProps.optional = ( +
+ {label[1]} + {label[0][0] !== 'S' ? ( + + ) : null} +
+ ); + return ( + + {label[0]} + + ); + })} +
+ ) : ( + + {steps.map((label) => { + const stepProps: { completed?: boolean } = {}; + const labelProps: { optional?: React.ReactNode } = {}; + labelProps.optional = ( +
+ {label[1]} + {label[0][0] !== 'S' ? ( + + ) : null} +
+ ); + return steps[props.animationState] === label ? ( + + {label[0]} + + ) : ( + + + + ); + })} +
+ )} +
); diff --git a/src/components/tans_theorem/SimilarityMenu.tsx b/src/components/tans_theorem/SimilarityMenu.tsx deleted file mode 100644 index 4d5c9a1..0000000 --- a/src/components/tans_theorem/SimilarityMenu.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Button, Card, Grid, Typography } from '@material-ui/core'; -import React from 'react'; -import { SimilarityMenuProps } from '../../common/tans'; -import { AnimationStatus } from './AnimationFinalCard'; -import PointsList from './PointsList'; -import { warpToPoint } from '../../common/utils'; -import { formatComplexNumber } from './tansTheoremUtils'; - -const SimilarityMenu = (props: SimilarityMenuProps): JSX.Element => { - const goButton = ( - setAnimationState: React.Dispatch>, - ) => { - return ( - - ); - }; - - return ( - - - {props.backButton()} - - - Pick a point in the Julia set! - - - - `${formatComplexNumber(c.point)}`} - /> - {goButton(props.setAnimationState)} - - ); -}; - -export default SimilarityMenu; diff --git a/src/components/tans_theorem/ZoomMenu.tsx b/src/components/tans_theorem/ZoomMenu.tsx index 60851be..1e09030 100644 --- a/src/components/tans_theorem/ZoomMenu.tsx +++ b/src/components/tans_theorem/ZoomMenu.tsx @@ -1,195 +1,47 @@ -import { Button, Typography, Card, Grid } from '@material-ui/core'; +import { Card, Button } from '@material-ui/core'; import React from 'react'; import { ZoomCardProps } from '../../common/tans'; import { AnimationStatus } from './AnimationFinalCard'; +import { KeyboardArrowLeft } from '@material-ui/icons'; +import { KeyboardArrowRight } from '@material-ui/icons'; +import { ZoomType, ThetaType } from '../../common/types'; import { warpToPoint } from '../../common/utils'; -import ZoomInIcon from '@material-ui/icons/ZoomIn'; -import RotateRightIcon from '@material-ui/icons/RotateRight'; -import { ThetaType, ZoomType } from '../../common/types'; -import { formatAngle, formatComplexNumber } from './tansTheoremUtils'; -import Dialog from '@material-ui/core/Dialog'; -import { DialogContent, DialogDivider, DialogTitle } from '../custom/DialogComponents'; const INITIAL_ZOOM = 1; -export interface SimpleDialogProps { - animationState: AnimationStatus; - open: boolean; - onClose: (value: string) => void; -} - const ZoomMenu = (props: ZoomCardProps): JSX.Element => { + const ActionText = { + '-1': 'null', + 0: 'null', + 1: 'null', + 2: 'MAGNIFY', + 3: 'MAGNIFY', + 4: 'ROTATE', + 5: 'ROTATE', + 6: 'null', + }; + const zoomMandelbrot = () => { props.setAnimationState(AnimationStatus.ZOOM_J); const zoomM: ZoomType = props.focusedPointMandelbrot.factorMagnitude * INITIAL_ZOOM; - warpToPoint(props.mandelbrot, { + warpToPoint(props.mandelbrotControls, { xy: props.focusedPointMandelbrot.point, z: zoomM, theta: 0, }); }; - const icons = { - '-1': 'null', - 0: 'null', - 1: 'null', - 2: , - 3: , - 4: , - 5: , - 6: 'null', - }; - - const titleStrings = { - '-1': 'null', - 0: 'null', - 1: 'null', - 2: 'Magnify M by', - 3: 'Magnify J by', - 4: 'Rotate M by', - 5: 'Rotate J by', - 6: 'null', - }; - - const factorText = { - '-1': 'null', - 0: 'null', - 1: 'null', - 2: `|${formatComplexNumber(props.focusedPointMandelbrot.factor, 1)}| = ${Math.round( - props.focusedPointMandelbrot.factorMagnitude, - )}x`, - 3: `|${formatComplexNumber(props.focusedPointJulia.factor, 1)}| = ${Math.round( - props.focusedPointJulia.factorMagnitude, - )}x`, - 4: `arg(${formatComplexNumber( - props.focusedPointMandelbrot.factor, - 1, - )}) = ${formatAngle(props.focusedPointMandelbrot.factorAngle)}`, - 5: `arg(${formatComplexNumber(props.focusedPointJulia.factor, 1)}) = ${formatAngle( - props.focusedPointJulia.factorAngle, - )}`, - 6: 'null', - }; - - const factorTextExpanded = { - '-1': 'null', - 0: 'null', - 1: 'null', - 2: `|u'(c)| = |${formatComplexNumber(props.focusedPointMandelbrot.factor, 2)}| = ${ - Math.round(props.focusedPointMandelbrot.factorMagnitude * 100) / 100 - }`, - 3: `|a| = |${formatComplexNumber(props.focusedPointJulia.factor, 2)}| = ${ - Math.round(props.focusedPointJulia.factorMagnitude * 100) / 100 - }`, - 4: `arg(u'(c)) = arg(${formatComplexNumber( - props.focusedPointMandelbrot.factor, - 2, - )}) = ${formatAngle(props.focusedPointMandelbrot.factorAngle)}`, - 5: `arg(a) = arg(${formatComplexNumber( - props.focusedPointJulia.factor, - 2, - )}) = ${formatAngle(props.focusedPointJulia.factorAngle)}`, - 6: 'null', - }; - - const paragraph1 = { - '-1': 'null', - 0: 'null', - 1: 'null', - 2: `The complex number, u'(c), used to "align" the Mandelbrot set is the following:`, - 3: `The complex number, a, used to "align" the Julia set is the following:`, - 4: `The complex number, u'(c), used to "align" the Mandelbrot set is the following:`, - 5: `The complex number, a, used to "align" the Julia set is the following:`, - 6: 'null', - }; - - const paragraph2 = { - '-1': 'null', - 0: 'null', - 1: 'null', - 2: `To magnify, we take the magnitude of u'(c), so the magnification factor is:`, - 3: `To magnify, we take the magnitude of a, so the magnification factor is:`, - 4: `To rotate, we take the argument of u'(c), so the rotation factor is:`, - 5: `To rotate, we take the argument of a, so the rotation factor is:`, - 6: 'null', - }; - - const numberText = { - '-1': 'null', - 0: 'null', - 1: 'null', - 2: `u'(c) = ${formatComplexNumber(props.focusedPointMandelbrot.factor, 2)}`, - 3: `a = ${formatComplexNumber(props.focusedPointJulia.factor, 2)}`, - 4: `u'(c) = ${formatComplexNumber(props.focusedPointMandelbrot.factor, 2)}`, - 5: `a = ${formatComplexNumber(props.focusedPointJulia.factor, 2)}`, - 6: 'null', - }; - - const dialogText = { - '-1': 'null', - 0: 'null', - 1: 'null', - 2: `The Mandelbrot set magnification factor`, - 3: `The Julia set magnification factor`, - 4: `The Mandelbrot set rotation factor`, - 5: `The Julia set rotation factor`, - 6: 'null', - }; - - function SimpleDialog(props: SimpleDialogProps) { - const { onClose, open } = props; - - const handleClose = () => { - onClose('null'); - }; - - return ( - - - {icons[props.animationState]} - {dialogText[props.animationState]} - - -
- - To show the similarity between the Mandelbrot and Julia set, we multiply - every point in each set by a particular complex number. Geometrically, - multiplying two complex numbers is equivalent to multiplying their - magnitudes and adding their angles. That's why we magnify and rotate in - this animation! - - - {paragraph1[props.animationState]} - {numberText[props.animationState]} - {paragraph2[props.animationState]} - {factorTextExpanded[props.animationState]} - - - For the full details on calculating the magnification and rotation factors - for each set, read " Lei Tan, Similarity Between the Mandelbrot Set and - Julia Sets", 1990, page 609. - -
-
-
- ); - } const zoomJulia = () => { props.setAnimationState(AnimationStatus.ROTATE_M); const zoomJ: ZoomType = props.focusedPointJulia.factorMagnitude * INITIAL_ZOOM; - warpToPoint(props.julia, { xy: props.focusedPointJulia.point, z: zoomJ, theta: 0 }); + warpToPoint(props.juliaControls, { + xy: props.focusedPointJulia.point, + z: zoomJ, + theta: 0, + }); }; const rotateMandelbrot = () => { @@ -198,7 +50,7 @@ const ZoomMenu = (props: ZoomCardProps): JSX.Element => { const zoomM: ZoomType = props.focusedPointMandelbrot.factorMagnitude * INITIAL_ZOOM; const thetaM: ThetaType = -props.focusedPointMandelbrot.factorAngle; - warpToPoint(props.mandelbrot, { + warpToPoint(props.mandelbrotControls, { xy: props.focusedPointMandelbrot.point, z: zoomM, theta: thetaM, @@ -211,100 +63,53 @@ const ZoomMenu = (props: ZoomCardProps): JSX.Element => { const zoomJ: ZoomType = props.focusedPointJulia.factorMagnitude * INITIAL_ZOOM; const thetaJ: ThetaType = -props.focusedPointJulia.factorAngle; - warpToPoint(props.julia, { + warpToPoint(props.juliaControls, { xy: props.focusedPointJulia.point, z: zoomJ, theta: thetaJ, }); }; - const [open, setOpen] = React.useState(false); - - const handleClickOpen = () => { - setOpen(true); - }; - - const handleClose = () => { - setOpen(false); - }; - return ( - - {props.backButton()} - - - {titleStrings[props.animationState]} - - - - - - - {factorText[props.animationState]} - - - - - - - {' '} - + +
+ +
); };