├── .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
/.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/gaearon/react-fractals/533cad46ea817a272a497aeac93ba8ad6ce6e8c7/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": "^15.4.0",
13 | "react-dom": "^15.4.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/gaearon/react-fractals/533cad46ea817a272a497aeac93ba8ad6ce6e8c7/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/gaearon/react-fractals/533cad46ea817a272a497aeac93ba8ad6ce6e8c7/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 |
10 | // borrowed from Vue fork https://github.com/yyx990803/vue-fractal/blob/master/src/App.vue
11 | function throttleWithRAF (fn) {
12 | let running = false
13 | return function () {
14 | if (running) return
15 | running = true
16 | window.requestAnimationFrame(() => {
17 | fn.apply(this, arguments)
18 | running = false
19 | })
20 | }
21 | }
22 |
23 | class App extends Component {
24 | svg = {
25 | width: 1280,
26 | height: 600
27 | };
28 | state = {
29 | currentMax: 0,
30 | baseW: 80,
31 | heightFactor: 0,
32 | lean: 0
33 | };
34 | running = false;
35 | realMax = 11;
36 |
37 | componentDidMount() {
38 | d3select(this.refs.svg).on("mousemove", this.onMouseMove.bind(this));
39 |
40 | this.next();
41 | }
42 |
43 | next() {
44 | const { currentMax } = this.state;
45 |
46 | if (currentMax < this.realMax) {
47 | this.setState({currentMax: currentMax + 1});
48 | setTimeout(this.next.bind(this), 500);
49 | }
50 | }
51 |
52 | // Throttling approach borrowed from Vue fork
53 | // https://github.com/yyx990803/vue-fractal/blob/master/src/App.vue
54 | // rAF makes it slower than just throttling on React update
55 | onMouseMove(event) {
56 | if (this.running) return;
57 | this.running = true;
58 |
59 | const [x, y] = d3mouse(this.refs.svg),
60 |
61 | scaleFactor = scaleLinear().domain([this.svg.height, 0])
62 | .range([0, .8]),
63 |
64 | scaleLean = scaleLinear().domain([0, this.svg.width/2, this.svg.width])
65 | .range([.5, 0, -.5]);
66 |
67 | this.setState({
68 | heightFactor: scaleFactor(y),
69 | lean: scaleLean(x)
70 | });
71 | this.running = false;
72 | }
73 |
74 | render() {
75 | return (
76 |
77 |
78 |

79 |
This is a dancing Pythagoras tree
80 |
81 |
82 |
95 |
96 |
97 | );
98 | }
99 | }
100 |
101 | export default App;
102 |
--------------------------------------------------------------------------------
/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 |
2 | import React from 'react';
3 | import { interpolateViridis } from 'd3-scale';
4 |
5 | Math.deg = function(radians) {
6 | return radians * (180 / Math.PI);
7 | };
8 |
9 | const memoizedCalc = function () {
10 | const memo = {};
11 |
12 | const key = ({ w, heightFactor, lean }) => [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 * (.5+lean))**2),
26 | nextLeft: Math.sqrt(trigH**2 + (w * (.5-lean))**2),
27 | A: Math.deg(Math.atan(trigH / ((.5-lean) * w))),
28 | B: Math.deg(Math.atan(trigH / ((.5+lean) * w)))
29 | };
30 |
31 | memo[memoKey] = result;
32 | return result;
33 | }
34 | }
35 | }();
36 |
37 | const Pythagoras = ({ w,x, y, heightFactor, lean, left, right, lvl, maxlvl }) => {
38 | if (lvl >= maxlvl || w < 1) {
39 | return null;
40 | }
41 |
42 | const { nextRight, nextLeft, A, B } = memoizedCalc({
43 | w: w,
44 | heightFactor: heightFactor,
45 | lean: lean
46 | });
47 |
48 | let rotate = '';
49 |
50 | if (left) {
51 | rotate = `rotate(${-A} 0 ${w})`;
52 | }else if (right) {
53 | rotate = `rotate(${B} ${w} ${w})`;
54 | }
55 |
56 | return (
57 |
58 |
61 |
62 |
68 |
69 |
75 |
76 |
77 | );
78 | };
79 |
80 | export default Pythagoras;
81 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------