Skip to content

Instantly share code, notes, and snippets.

@beemyfriend
Last active March 25, 2017 21:12
Show Gist options
  • Select an option

  • Save beemyfriend/c1a649e8c157bb9b5f042cdc797f9330 to your computer and use it in GitHub Desktop.

Select an option

Save beemyfriend/c1a649e8c157bb9b5f042cdc797f9330 to your computer and use it in GitHub Desktop.
Fractals With Canvas and D3: Fractal Creater
<body>
<div style = 'position: absolute; top:0px; left:-500px'><canvas id = 'myCanvas' width = '500' height = '500' ></canvas></div>
<div style = 'position: absolute; top:70px; left:10px'><canvas id = 'otherCanvas' width = '500' height = '500'></canvas></div>
<form name = 'createRule' style = 'position: absolute; top:60px; left:520px'>
<br>
<br>
<h3>Create Fractal Rules</h3>
<label id = 'xslabel'>X-Scale: 0.5</label>
<input id = 'xscale' type = 'range' min = '-1' max = '1' step = '.05' value = '.5' onchange="document.getElementById('xslabel').innerHTML = 'X-Scale: ' + this.value;"/>
<br>
<label id = 'yslabel'>Y-Scale: 0.5</label>
<input id = 'yscale' type = 'range' min = '-1' max = '1' step = '.05' value = '.5' onchange="document.getElementById('yslabel').innerHTML = 'Y-Scale: ' + this.value;"/>
<br>
<label id = 'anglelabel'>Angle: 0 degrees</label>
<input id = 'angle' type = 'range' min = 0 max = '360' step = '5' value = '0' onchange="document.getElementById('anglelabel').innerHTML = 'Angle: ' + this.value + ' degrees';"/>
<br>
<label id = 'xclabel'>X-Coordinate: 0</label>
<input id = 'x' type = 'range' min = '-1' max = '1' step = '.05' value = '0' onchange="document.getElementById('xclabel').innerHTML = 'X-Coordinate: ' + this.value;"/>
<br>
<label id = 'yclabel'>Y-Coordinate: 0</label>
<input id = 'y' type= 'range' min = '-1' max = '1' step = '.05' value = '0' onchange="document.getElementById('yclabel').innerHTML = 'Y-Coordinate: ' + this.value;"/>
<br><br>
<button onclick = 'createNewRule(); update(); return false;'>Add Rule</button>
<br>
<button onclick="draw(myRules, 10); return false;">Build the Fractal!</button>
</form>
<div id = 'showrules' style = 'position:absolute; top: 370; left: 520'></div>
<script src ="https://d3js.org/d3.v4.js"></script>
<script>
var myCanvas = document.getElementById('myCanvas'),
myCtx = myCanvas.getContext('2d'),
otherCtx = document.getElementById('otherCanvas').getContext('2d'),
height = myCanvas.height,
width = myCanvas.width;
//myRules will be populated with rules each time the user pushes the "Add Rule" button
var myRules = [];
var showRules = d3.select('#showrules');
//Display the rules as the user adds them
//Should be able to delete the rules by pressing a 'delete' button
//Correct rule is deleted, but the <p> object isn't correctly updated
function update(){
var text = showRules.selectAll('p')
.data(myRules);
text.enter().append('p')
.attr('class', 'enter')
.attr('id', function(d,i){return 'rule'+(i+1);})
// I think the below .html() cannot be changed without deleting th <p>
.html(function(d, i){return '<b>Fractal Rule #' + (i+1) +'</b><br><b>X-Scale:</b> ' + d[0] + ' <b>Y-Scale:</b> ' + d[1] + ' <b>Angle:</b> ' + d[2] + ' <b>X-Coordinate:</b> ' + d[3] + ' <b>Y-Coordinate:</b> ' + d[4];});
text.exit().remove();
var buttons = showRules.selectAll('input')
.data(myRules);
function handleClick(i){
myRules.splice(i, 1);
update();
};
buttons.enter().append('input')
.attr('type', 'button')
.attr('value', function(d, i){return 'delete rule '+ (i+1)})
.on('click', function(d,i){return handleClick(i)})
buttons.exit().remove();
}
//takes user input and pushes the an array of instructions to myRules
function createNewRule(){
var xscale = document.getElementById('xscale').value;
var yscale = document.getElementById('yscale').value;
var angle = document.getElementById('angle').value;
var x = document.getElementById('x').value;
var y = document.getElementById('y').value;
myRules[myRules.length] = [xscale, yscale, angle, x, y];
}
//init() creates a rectangle with corners colored different colors
//different colors are meant to act as an aid to identify transformations
function init(){
console.log('ran init')
myCtx.fillStyle = 'purple';
myCtx.fillRect(0, 0, width, height);
myCtx.fillStyle = 'yellow';
myCtx.fillRect(0, 0, width/10 , height/10 );
myCtx.fillStyle = 'pink';
myCtx.fillRect(width * 9 / 10, height * 9 / 10, width/10, height/10);
myCtx.fillStyle = 'blue';
myCtx.fillRect(0, height * 9 /10, width/10, height/10);
myCtx.fillStyle = 'brown';
myCtx.fillRect(width * 9 / 10, 0, width/10, height/10);
myCtx.strokeStyle = 'red';
myCtx.rect(0, 0, width, height);
myCtx.stroke();
}
//Canvas rotations require Radians, not degrees;
function toRads(degree){
return Math.PI / 180 * degree;
}
//drawFractalRules() draws the rules in an attempt to imitate
//Iterated Fuction Systems (IFS)
//learn more at http://users.math.yale.edu/public_html/People/frame/Fractals/
function drawFractalRule(scale_x, scale_y, angle, x, y){
if(scale_x > 0){
angle = -angle
}
angle = toRads(angle);
var dw = width/scale_x;
var dh = height/scale_y;
var dx = (dw * x)
var dy = dh - (dh * y) - (dh * scale_y);
console.log('dy: ' + dy);
console.log('dh: ' + dh);
otherCtx.save();
otherCtx.scale(scale_x, scale_y);
otherCtx.translate(0, height)
otherCtx.translate(dx, dy)
otherCtx.rotate(angle);
otherCtx.drawImage(myCanvas, 0, -height);
otherCtx.restore();
}
function draw(rules, iterations){
var now = 0;
var animate = setInterval(function(){now < 1 ? start() : update()}, 1000);
function start(){
console.log('ran start')
init();
otherCtx.drawImage(myCanvas, 0, 0);
now++
}
function update(){
console.log('called update')
if(now >= iterations){
clearInterval(animate);
} else {
otherCtx.clearRect(0, 0, width, height)
for(var i = 0; i < rules.length; i++){
drawFractalRules(Number(rules[i][0]), Number(rules[i][1]), Number(rules[i][2]), Number(rules[i][3]), Number(rules[i][4]))
console.log(i + ': ' + rules[i])
}
myCtx.clearRect(0, 0, width, height);
myCtx.drawImage(otherCanvas, 0, 0);
now++;
}
}
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment