From fba66ce0b0a1f5751215f9946b90e46348e740bb Mon Sep 17 00:00:00 2001 From: Dan Kreft Date: Sat, 5 Nov 2022 10:11:39 -0700 Subject: [PATCH] Added fractal code, got it running (still dysfunctional) --- src/App.css | 41 --- src/App.jsx | 34 --- src/components/App/App.jsx | 15 + src/components/App/Fractal/Fractal.jsx | 29 ++ src/components/App/Fractal/index.js | 3 + src/components/App/Fractal/lib/fractal.js | 256 ++++++++++++++++++ .../App/Fractal/lib/getDirection.js | 23 ++ .../App/Fractal/lib/getDirection.spec.js | 11 + src/components/App/Fractal/styles.module.sass | 3 + src/components/App/index.js | 3 + src/components/App/styles.module.sass | 9 + src/index.css | 47 +--- src/main.jsx | 5 +- 13 files changed, 363 insertions(+), 116 deletions(-) delete mode 100644 src/App.css delete mode 100644 src/App.jsx create mode 100644 src/components/App/App.jsx create mode 100644 src/components/App/Fractal/Fractal.jsx create mode 100644 src/components/App/Fractal/index.js create mode 100644 src/components/App/Fractal/lib/fractal.js create mode 100644 src/components/App/Fractal/lib/getDirection.js create mode 100644 src/components/App/Fractal/lib/getDirection.spec.js create mode 100644 src/components/App/Fractal/styles.module.sass create mode 100644 src/components/App/index.js create mode 100644 src/components/App/styles.module.sass diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 2c5e2ef..0000000 --- a/src/App.css +++ /dev/null @@ -1,41 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/App.jsx b/src/App.jsx deleted file mode 100644 index ef0adc0..0000000 --- a/src/App.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import './App.css' - -function App() { - const [count, setCount] = useState(0) - - return ( -
-
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

-
- ) -} - -export default App diff --git a/src/components/App/App.jsx b/src/components/App/App.jsx new file mode 100644 index 0000000..68f0676 --- /dev/null +++ b/src/components/App/App.jsx @@ -0,0 +1,15 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Fractal from './Fractal' + +import Styles from './styles.module.sass' + + +export default function App() { + return ( +
+ +
+ ) +} diff --git a/src/components/App/Fractal/Fractal.jsx b/src/components/App/Fractal/Fractal.jsx new file mode 100644 index 0000000..f4737da --- /dev/null +++ b/src/components/App/Fractal/Fractal.jsx @@ -0,0 +1,29 @@ +import React, { + useLayoutEffect, + useRef, +} from 'react' + +import PropTypes from 'prop-types' + +import drawFractal from './lib/fractal' + +import Styles from './styles.module.sass' + + +export default function Fractal({}) { + const ref = useRef() + + useLayoutEffect(() => { + drawFractal(ref.current) + }, []) + + return ( + + ) +} + +Fractal.propTypes = { +} diff --git a/src/components/App/Fractal/index.js b/src/components/App/Fractal/index.js new file mode 100644 index 0000000..9eceb05 --- /dev/null +++ b/src/components/App/Fractal/index.js @@ -0,0 +1,3 @@ +import Fractal from './Fractal' + +export default Fractal diff --git a/src/components/App/Fractal/lib/fractal.js b/src/components/App/Fractal/lib/fractal.js new file mode 100644 index 0000000..c7437a0 --- /dev/null +++ b/src/components/App/Fractal/lib/fractal.js @@ -0,0 +1,256 @@ +import getDirection from './getDirection' + +const COS60 = 0.5 +const SIN60 = Math.sqrt(3) / 2 +const D2R = Math.PI / 180 + +const COLORS = ['#fff', '#f00', '#0f0', '#00f', 'pink', 'cyan', 'violet'] +const LIMIT = 4 //Math.pow(2, 5) + +const LENGTH = 400 + + +export default function drawFractal(canvas) { + const DIRECTION = 110 + + //const canvas = document.getElementById('canvas') + canvas.width = canvas.offsetWidth + canvas.height = canvas.offsetHeight + + let pointNum = 0 + + const colors = colorGenerator() + + const ctx = canvas.getContext('2d') + ctx.strokeStyle = '#000' + ctx.font = '14px monospace' + ctx.translate(canvas.width * 0.3, + canvas.height * 0.6) + + const root = buildShape({ + bounds: { + x: [-canvas.width * 0.5, canvas.width * 0.5], + y: [-canvas.width * 0.5, canvas.height * 0.5], + }, + startX: 0, + startY: 0, + length: LENGTH, + direction: DIRECTION, + }) + + console.log('root: %o', root) + + const shapesToDraw = [root] + + let i = 0 + + do { + ++i + + ctx.strokeStyle = colors.next().value + + const shape = shapesToDraw.shift() + + console.group('SHAPE (%o): %o', ctx.strokeStyle, shape) + + drawPolygon(shape) + + const length = shape.length / 2 + if ( length < 1 ) { + console.warn('new Length too small to render') + break + } + + const { + bounds, + points, + } = shape + + for ( let j = 0; j < points.length; ++j ) { + const p1 = points[j] + const p2 = points[j + 1] || points[0] + + const direction = getDirection(p2, p1) + + const child = buildShape({ + bounds, + direction, + length, + startX: p1.x, + startY: p1.y, + }) + + shapesToDraw.push(child) + } + + console.groupEnd() + } while ( i < LIMIT && i <= shapesToDraw.length ) + + + // + // Functions + // + + function buildShape({ bounds, direction, length, startX, startY }) { + console.group('buildShape(%o)', { bounds }) + + console.log('DIRECTION: %o', direction) + const p1 = { + x: startX, + y: startY, + } + + // const p2 = getNextPoint({ + // angle: direction, + // length, + // x: startX, + // y: startY, + // }) + + // const p3 = getNextPoint({ + // angle: direction - 60, + // length, + // x: startX, + // y: startY, + // }) + + const p2 = getPointWithinBounds({ + angle: direction, + bounds, + length, + startX, + startY, + }) + + const p3 = getPointWithinBounds({ + angle: direction - 60, + bounds, + length, + startX, + startY, + }) + + const points = [ + p1, + p2, + p3, + ] + console.log('points: %o', points) + const shape = { + length, + direction, + bounds: getBounds(points), + points, + } + + console.log('shape: %o', shape) + console.groupEnd() + return shape + } + + function getNextPoint({ angle, length, x, y }) { + return { + angle, + x: x + length * cos(angle), + y: y + length * sin(angle), + } + } + + function getPointWithinBounds({ angle, bounds, length, startX, startY }) { + const x = getCoordValue({ + bounds: bounds.x, + length: length * cos(angle), + start: startX, + }) + + const y = getCoordValue({ + bounds: bounds.y, + length: length * sin(angle), + start: startY, + }) + + return { x, y } + } + + function getCoordValue({ bounds, length, start }) { + const finalValue = start + length + + console.log('getCoordValue: %o < %o || %o > %o', + finalValue, + bounds[0], + finalValue, + bounds[1]) + + if ( finalValue < bounds[0] || finalValue > bounds[1] ) { + console.log('subtracting %o - %o', start, length) + return start - length + } + + return finalValue + } + + function getBounds(points) { + const xValues = points.map(({ x }) => x) + const yValues = points.map(({ y }) => y) + + return { + x: [ + Math.min(...xValues), + Math.max(...xValues), + ], + y: [ + Math.min(...yValues), + Math.max(...yValues), + ], + } + } + + function drawPolygon({ direction, length, points }) { + console.group('drawPolygon(%o)', { direction, length, points }) + const [start, ...others] = points + + ctx.beginPath() + + ctx.moveTo(start.x, start.y) + + // ctx.strokeText(`(${ start.x }, ${ start.y })`, start.x + 20, start.y + 10) + + others.forEach(({ x, y }, i) => { + ctx.lineTo(x, y) + //ctx.strokeText(`(${ x }, ${ y })`, x + 20, y + 10) + ctx.stroke() + }) + + ctx.lineTo(start.x, start.y) + + ctx.stroke() + ctx.closePath() + + console.groupEnd() + } + + function cos(deg) { + return Math.cos(d2r(deg)) + } + + function sin(deg) { + return Math.sin(d2r(deg)) + } + + function d2r(degrees) { + return degrees * D2R + } + + function* colorGenerator() { + let idx = 0 + while ( 1 ) { + yield COLORS[idx] + + ++idx + + if ( idx > COLORS.length - 1 ) { + idx = 0 + } + } + } +} diff --git a/src/components/App/Fractal/lib/getDirection.js b/src/components/App/Fractal/lib/getDirection.js new file mode 100644 index 0000000..ca39c26 --- /dev/null +++ b/src/components/App/Fractal/lib/getDirection.js @@ -0,0 +1,23 @@ +const R2D = 180 / Math.PI + + +export default function getDirection(from, to) { + if ( !from || !to ) { + return + } + + const dY = to.y - from.y + const dX = to.x - from .x + + const slope = dY / dX + + return atan(slope) +} + +function atan(slope) { + return r2d(Math.atan(slope)) +} + +function r2d(rad) { + return rad * R2D +} diff --git a/src/components/App/Fractal/lib/getDirection.spec.js b/src/components/App/Fractal/lib/getDirection.spec.js new file mode 100644 index 0000000..0fe60e1 --- /dev/null +++ b/src/components/App/Fractal/lib/getDirection.spec.js @@ -0,0 +1,11 @@ +import getDirection from './getDirection' + + +describe('getDirection()', () => { + def('from', () => void 0) + def('to', () => void 0) + + subject(() => getDirection($from, $to)) + + it(() => is.expected.toBeUndefined()) +}) diff --git a/src/components/App/Fractal/styles.module.sass b/src/components/App/Fractal/styles.module.sass new file mode 100644 index 0000000..3fa473f --- /dev/null +++ b/src/components/App/Fractal/styles.module.sass @@ -0,0 +1,3 @@ +.root + height: 100% + width: 100% diff --git a/src/components/App/index.js b/src/components/App/index.js new file mode 100644 index 0000000..f22feb8 --- /dev/null +++ b/src/components/App/index.js @@ -0,0 +1,3 @@ +import App from './App' + +export default App diff --git a/src/components/App/styles.module.sass b/src/components/App/styles.module.sass new file mode 100644 index 0000000..cdc9c55 --- /dev/null +++ b/src/components/App/styles.module.sass @@ -0,0 +1,9 @@ +.root + height: 100% + width: 100% + border: 1px solid pink + bottom: 0 + top: 0 + left: 0 + right: 0 + position: absolute diff --git a/src/index.css b/src/index.css index 917888c..212195a 100644 --- a/src/index.css +++ b/src/index.css @@ -3,6 +3,10 @@ font-size: 16px; line-height: 24px; font-weight: 400; + height: 100%; + width: 100%; + margin: 0; + padding: 0; color-scheme: light dark; color: rgba(255, 255, 255, 0.87); @@ -26,45 +30,8 @@ a:hover { body { margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; + lace-items: center; + height: 100%; + width: 100%; } -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/src/main.jsx b/src/main.jsx index 9af0bb6..74254e8 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,8 +1,11 @@ import React from 'react' import ReactDOM from 'react-dom/client' -import App from './App' + +import App from './components/App' + import './index.css' + ReactDOM.createRoot(document.getElementById('root')).render(