Created
July 15, 2019 06:45
-
-
Save jasonszhao/84aebccf4b49f89c8f0b003f567f42ea to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<head> | |
<meta charset="utf-8"> | |
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> | |
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> | |
<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/ramda.min.js"></script> | |
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> | |
<link rel="stylesheet" href="https://unpkg.com/[email protected]/css/tachyons.min.css" /> | |
<style> | |
html, | |
body { | |
font-family: sans-serif; | |
} | |
</style> | |
</head> | |
<div class="f5 normal ma3 black-60 absolute z-max top-0"> | |
<a href="../">..</a> / | |
<h1 class="f5 normal di">Sierpiński triangle</h1> | |
</div> | |
<div id="app" class="flex justify-center ma4"></div> | |
<script type="text/babel"> | |
const triangle_area = (x1, y1, x2, y2, x3, y3) => | |
0.5 * ( x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2) ) | |
const avg = (a, b) => (a + b) / 2 | |
class Triangle extends React.Component { | |
constructor(props) { | |
super(props) | |
for (const [k, v] of Object.entries(props)) { | |
this[k] = v | |
} | |
} | |
shouldComponentUpdate() { | |
return false | |
} | |
render() { | |
const { x1, y1, x2, y2, x3, y3, min_area} = this | |
// return <polygon | |
// key={`polygon-${x1},${y1} ${x2},${y2} ${x3},${y3}`} | |
// points={`${x1},${y1} ${x2},${y2} ${x3},${y3}`} | |
// fill="black" | |
// stroke="green" | |
// /> | |
if (triangle_area(x1, y1, x2, y2, x3, y3) < min_area) { | |
return <polygon | |
key={`polygon-${x1},${y1} ${x2},${y2} ${x3},${y3}`} | |
points={`${x1},${y1} ${x2},${y2} ${x3},${y3}`} | |
fill="black" /> | |
} else { | |
return [ | |
<polygon | |
fill="none" | |
key={`polygon-${x1},${y1} ${x2},${y2} ${x3},${y3}`} points={`${x1},${y1} ${x2},${y2} ${x3},${y3}`} />, | |
<Triangle | |
key={`Triangle1-${x1},${y1} ${x2},${y2} ${x3},${y3}`} | |
x1={x1} y1={y1} | |
x2={avg(x1, x2)} y2={avg(y1, y2)} | |
x3={avg(x1, x3)} y3={avg(y1, y3)} | |
min_area={min_area} />, | |
<Triangle | |
key={`Triangle2-${x1},${y1} ${x2},${y2} ${x3},${y3}`} | |
x1={avg(x1, x2)} y1={avg(y1, y2)} | |
x2={x2} y2={y2} | |
x3={avg(x2, x3)} y3={avg(y2, y3)} | |
min_area={min_area} />, | |
<Triangle | |
key={`Triangle3-${x1},${y1} ${x2},${y2} ${x3},${y3}`} | |
x1={avg(x1, x3)} y1={avg(y1, y3)} | |
x2={avg(x2, x3)} y2={avg(y2, y3)} | |
x3={x3} y3={y3} | |
min_area={min_area} />, | |
] | |
} | |
} | |
} | |
class App extends React.Component { | |
width = 400 | |
constructor() { | |
super() | |
this.state = { | |
t: 0, | |
bodies: [] | |
} | |
this.handleClick = this.handleClick.bind(this) | |
} | |
update(time_ms) { | |
this.setState(state => { | |
const time = time_ms / 1000 | |
const dt = time - this.state.t | |
if (dt <= 0) { | |
return | |
} | |
const newState = { | |
t: time, | |
bodies: [], | |
} | |
for (const b of state.bodies) { | |
if (!(b.centerx + b.r >= 0 | |
&& b.centerx - b.r <= 100 | |
&& b.centery + b.r >= 0 | |
&& b.centery - b.r <= 100 | |
&& b.r > 0)) { | |
continue | |
} | |
newState.bodies.push({ | |
...b, | |
centerx : b.centerx + dt * b.centerx_v, | |
centery : b.centery + dt * b.centery_v, | |
rot : b.rot + dt * b.rot_v, | |
r : b.r + dt * b.r_v, | |
}) | |
} | |
return newState | |
}) | |
this.loop() | |
} | |
loop() { | |
window.requestAnimationFrame(this.update.bind(this)) | |
} | |
componentDidMount() { | |
this.loop() | |
} | |
handleClick(e) { | |
const new_body = { | |
r_i: 30*Math.random() + 12, | |
centerx: (e.clientX - e.target.getBoundingClientRect().x) / this.width * 100, | |
centery: (e.clientY - e.target.getBoundingClientRect().y) / this.width * 100, | |
rot_i: Math.random() * 2 * Math.PI, | |
r_v: -(Math.random() * 10 + 5), | |
centerx_v: (Math.random() - 0.5) * 16, | |
centery_v: (Math.random() - 0.5) * 16, | |
rot_v: (Math.random() - 0.5) * 20 * Math.PI, | |
key: Math.random().toString(), | |
keyName: Math.random().toString(), | |
} | |
new_body.r = new_body.r_i | |
new_body.rot = new_body.rot_i | |
this.setState(s => | |
R.over( | |
R.lensProp('bodies'), | |
R.append(new_body), | |
s) | |
) | |
} | |
render() { | |
return ( | |
<svg viewBox="0 0 100 100" width={this.width} className="ba" onClick={this.handleClick}> | |
{this.state.bodies.map(b => { | |
const {r_i, rot_i} = b | |
return <g | |
transform={ | |
`rotate(${b.rot-b.rot_i},${b.centerx},${b.centery}) | |
translate(${b.centerx},${b.centery}) | |
scale(${b.r / b.r_i})`} | |
key={`Body-${b.keyName}`}> | |
<Triangle | |
x1={r_i*Math.cos(rot_i)} y1={r_i*Math.sin(rot_i)} | |
x2={r_i*Math.cos(rot_i + 2/3*Math.PI)} y2={r_i*Math.sin(rot_i + 2/3*Math.PI)} | |
x3={r_i*Math.cos(rot_i + 4/3*Math.PI)} y3={r_i*Math.sin(rot_i+ 4/3*Math.PI)} | |
min_area={1}/> | |
</g> | |
})} | |
</svg> | |
) | |
} | |
} | |
ReactDOM.render(<App/>, document.getElementById('app')) | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment