├── .gitignore
├── README.md
├── growing-pythagoras-tree.gif
├── package.json
├── public
├── favicon.ico
└── index.html
├── react-tree.gif
├── src
├── App.css
├── App.js
├── App.test.js
├── Pythagoras.js
├── index.css
├── index.js
└── logo.svg
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # testing
7 | coverage
8 |
9 | # production
10 | build
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | npm-debug.log
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).
2 |
3 | # React Fractals
4 |
5 | An experiment in fractalization and component recursion.
6 |
7 | 
8 |
--------------------------------------------------------------------------------
/growing-pythagoras-tree.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Swizec/react-fractals/628a8a16da8a1836aad0e3c8c55f44a456ab0906/growing-pythagoras-tree.gif
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-fractals",
3 | "version": "0.0.1",
4 | "private": true,
5 | "devDependencies": {
6 | "gh-pages": "^0.12.0",
7 | "react-scripts": "0.7.0"
8 | },
9 | "dependencies": {
10 | "d3-scale": "^1.0.4",
11 | "d3-selection": "^1.0.3",
12 | "react": "^16.3.0-rc.0",
13 | "react-dom": "^16.3.0-rc.0"
14 | },
15 | "homepage": "https://swizec.github.io/react-fractals",
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test --env=jsdom",
20 | "eject": "react-scripts eject",
21 | "deploy": "gh-pages -d build"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Swizec/react-fractals/628a8a16da8a1836aad0e3c8c55f44a456ab0906/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
16 | React App
17 |
18 |
19 |
20 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/react-tree.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Swizec/react-fractals/628a8a16da8a1836aad0e3c8c55f44a456ab0906/react-tree.gif
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 80px;
8 | }
9 |
10 | .App-header {
11 | background-color: #222;
12 | height: 150px;
13 | padding: 20px;
14 | color: white;
15 | }
16 |
17 | .App-intro {
18 | font-size: large;
19 | }
20 |
21 | @keyframes App-logo-spin {
22 | from { transform: rotate(0deg); }
23 | to { transform: rotate(360deg); }
24 | }
25 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import logo from "./logo.svg";
3 | import "./App.css";
4 | import { select as d3select, mouse as d3mouse } from "d3-selection";
5 | import { scaleLinear } from "d3-scale";
6 |
7 | import Pythagoras from "./Pythagoras";
8 |
9 | class App extends Component {
10 | svg = {
11 | width: 1280,
12 | height: 600
13 | };
14 | state = {
15 | currentMax: 0,
16 | baseW: 80,
17 | heightFactor: 0,
18 | lean: 0
19 | };
20 | running = false;
21 | realMax = 11;
22 |
23 | componentDidMount() {
24 | d3select(this.refs.svg).on("mousemove", this.onMouseMove.bind(this));
25 |
26 | this.next();
27 | }
28 |
29 | next() {
30 | const { currentMax } = this.state;
31 |
32 | if (currentMax < this.realMax) {
33 | this.setState({ currentMax: currentMax + 1 });
34 | setTimeout(this.next.bind(this), 500);
35 | }
36 | }
37 |
38 | // Throttling approach borrowed from Vue fork
39 | // https://github.com/yyx990803/vue-fractal/blob/master/src/App.vue
40 | // rAF makes it slower than just throttling on React update
41 | onMouseMove(event) {
42 | if (this.running) return;
43 | this.running = true;
44 |
45 | const [x, y] = d3mouse(this.refs.svg),
46 | scaleFactor = scaleLinear()
47 | .domain([this.svg.height, 0])
48 | .range([0, 0.8]),
49 | scaleLean = scaleLinear()
50 | .domain([0, this.svg.width / 2, this.svg.width])
51 | .range([0.5, 0, -0.5]);
52 |
53 | this.setState({
54 | heightFactor: scaleFactor(y),
55 | lean: scaleLean(x)
56 | });
57 | this.running = false;
58 | }
59 |
60 | render() {
61 | return (
62 |
63 |
64 |

65 |
This is a dancing Pythagoras tree
66 |
67 |
68 |
85 |
86 |
87 | );
88 | }
89 | }
90 |
91 | export default App;
92 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | });
9 |
--------------------------------------------------------------------------------
/src/Pythagoras.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { interpolateViridis } from "d3-scale";
3 |
4 | Math.deg = function(radians) {
5 | return radians * (180 / Math.PI);
6 | };
7 |
8 | const memoizedCalc = (function() {
9 | const memo = {};
10 |
11 | const key = ({ w, heightFactor, lean }) =>
12 | [w, heightFactor, lean].join("-");
13 |
14 | return args => {
15 | const memoKey = key(args);
16 |
17 | if (memo[memoKey]) {
18 | return memo[memoKey];
19 | } else {
20 | const { w, heightFactor, lean } = args;
21 |
22 | const trigH = heightFactor * w;
23 |
24 | const result = {
25 | nextRight: Math.sqrt(trigH ** 2 + (w * (0.5 - lean)) ** 2),
26 | nextLeft: Math.sqrt(trigH ** 2 + (w * (0.5 + lean)) ** 2),
27 | A: Math.deg(Math.atan(trigH / ((0.5 + lean) * w))),
28 | B: Math.deg(Math.atan(trigH / ((0.5 - lean) * w)))
29 | };
30 |
31 | memo[memoKey] = result;
32 | return result;
33 | }
34 | };
35 | })();
36 |
37 | const Pythagoras = ({
38 | w,
39 | x,
40 | y,
41 | heightFactor,
42 | lean,
43 | left,
44 | right,
45 | lvl,
46 | maxlvl
47 | }) => {
48 | if (lvl >= maxlvl || w < 1) {
49 | return null;
50 | }
51 |
52 | const { nextRight, nextLeft, A, B } = memoizedCalc({
53 | w: w,
54 | heightFactor: heightFactor,
55 | lean: lean
56 | });
57 |
58 | let rotate = "";
59 |
60 | if (left) {
61 | rotate = `rotate(${-A} 0 ${w})`;
62 | } else if (right) {
63 | rotate = `rotate(${B} ${w} ${w})`;
64 | }
65 |
66 | return (
67 |
68 |
75 |
76 |
86 |
87 |
97 |
98 | );
99 | };
100 |
101 | export default Pythagoras;
102 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import './index.css';
5 |
6 | ReactDOM.render(
7 | ,
8 | document.getElementById('root')
9 | );
10 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------