Last active
December 10, 2015 00:09
-
-
Save ahwolf/4349166 to your computer and use it in GitHub Desktop.
Snowflakes with D3
This file contains 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
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> | |
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script> | |
<script type="text/javascript" src="https://raw.github.com/ahwolf/d3_tutorial/master/code/js/lib/jquery-1.8.0.min.js"></script> | |
<script type="text/javascript" src="https://raw.github.com/ahwolf/d3_tutorial/master/code/js/lib/underscore-1.3.3-min.js"></script> | |
<style type="text/css"> | |
svg{display:block;}p.instructions{font-style:italic}#animation{margin-top:10px;text-align:center;width:400px}#animation .button{color:black;font-size:14px;font-weight:bold;width:150px;border-width:1px;padding:10px;margin:2px}.snowflake.even{fill:#42cae9}.snowflake.odd{fill:#9ee4f3}#rss_chart{display:none} | |
</style> | |
</head> | |
<body> | |
<div id="main_container"> | |
<div id="animation"> | |
<button id="animation_button" class="button" | |
onclick="click_animation_button()">Create Snowflake</button> | |
<button id="reset_button" class="button" | |
onclick="click_reset_button()">New Paper</button> | |
</div> | |
</div> | |
<script type="text/javascript"> | |
// Some variables that will be helpful to define | |
var height = 400; | |
var width = 400; | |
var padding = 30; | |
var duration = 1000; | |
var triangle_vertices = [ | |
[width/2,padding], | |
[width-padding,padding], | |
[width/2,height/2], | |
]; | |
// create the points on the edge for the circles | |
var num_points_on_edge = 5; | |
var data_1 = create_points(triangle_vertices[triangle_vertices.length-1],triangle_vertices[0], num_points_on_edge); | |
// var data_1 = []; | |
for (var i = 0; i < triangle_vertices.length-1; i++){ | |
data_1 = data_1.concat(create_points(triangle_vertices[i],triangle_vertices[i+1], num_points_on_edge)) | |
} | |
// Initialize the two svg canvgases | |
var svg = d3.select("#main_container") | |
.append("svg") | |
.attr("width", width + padding) | |
.attr("height", height + padding); | |
var line = d3.svg.line() | |
.x(function(d) { | |
return d[0]}) | |
.y(function(d) { return d[1]}); | |
var first_flip = d3.svg.line() | |
.x(function(d) { | |
return x_scale(d[1])}) | |
.y(function(d) { return y_scale(d[0])}); | |
var second_flip = d3.svg.line() | |
.x(function(d) { | |
return x_scale(d[0])}) | |
.y(function(d) { return d[1]}); | |
var third_flip = d3.svg.line() | |
.x(function(d) { | |
return d[0]}) | |
.y(function(d) { return y_scale(d[1])}); | |
var x_scale = d3.scale.linear() | |
.domain([0,width]) | |
.range([width, 0]); | |
var y_scale = d3.scale.linear() | |
.domain([0,height]) | |
.range([height, 0]); | |
var gradient = svg.append("svg:defs") | |
.append("svg:linearGradient") | |
.attr("id", "gradient") | |
.attr("x1", "0%") | |
.attr("y1", "0%") | |
.attr("x2", "50%") | |
.attr("y2", "50%") | |
.attr("spreadMethod", "pad"); | |
gradient.append("svg:stop") | |
.attr("offset", "0%") | |
.attr("stop-color", "#42cae9") | |
.attr("stop-opacity", 1); | |
gradient.append("svg:stop") | |
.attr("offset", "100%") | |
.attr("stop-color", "#9ee4f3") | |
.attr("stop-opacity", 1); | |
var gradient_2 = svg.append("svg:defs") | |
.append("svg:linearGradient") | |
.attr("id", "gradient_2") | |
.attr("x1", "50%") | |
.attr("y1", "50%") | |
.attr("x2", "100%") | |
.attr("y2", "100%") | |
.attr("spreadMethod", "pad"); | |
gradient_2.append("svg:stop") | |
.attr("offset", "0%") | |
.attr("stop-color", "#9ee4f3") | |
.attr("stop-opacity", 1); | |
gradient_2.append("svg:stop") | |
.attr("offset", "100%") | |
.attr("stop-color", "#42cae9") | |
.attr("stop-opacity", 1); | |
var gradient_3 = svg.append("svg:defs") | |
.append("svg:linearGradient") | |
.attr("id", "gradient_3") | |
.attr("x1", "100%") | |
.attr("y1", "0%") | |
.attr("x2", "50%") | |
.attr("y2", "50%") | |
.attr("spreadMethod", "pad"); | |
gradient_3.append("svg:stop") | |
.attr("offset", "0%") | |
.attr("stop-color", "#42cae9") | |
.attr("stop-opacity", 1); | |
gradient_3.append("svg:stop") | |
.attr("offset", "100%") | |
.attr("stop-color", "#9ee4f3") | |
.attr("stop-opacity", 1); | |
var gradient_4 = svg.append("svg:defs") | |
.append("svg:linearGradient") | |
.attr("id", "gradient_4") | |
.attr("x1", "0%") | |
.attr("y1", "100%") | |
.attr("x2", "100%") | |
.attr("y2", "0%") | |
.attr("spreadMethod", "pad"); | |
gradient_4.append("svg:stop") | |
.attr("offset", "0%") | |
.attr("stop-color", "#42cae9") | |
.attr("stop-opacity", 1); | |
gradient_4.append("svg:stop") | |
.attr("offset", "50%") | |
.attr("stop-color", "#9ee4f3") | |
.attr("stop-opacity", 1); | |
initial_draw(); | |
var path_1; | |
function initial_draw(){ | |
path_1 = svg.selectAll(".path_1") | |
.data([data_1]); | |
path_1.enter().append("path") | |
.classed("snowflake",true) | |
.classed("odd",true); | |
path_1.transition().duration(duration) | |
.attr("d",function(d){return line(d)}); | |
svg.selectAll("circle").remove(); | |
var circles = svg.selectAll("circle") | |
.data(data_1) | |
.enter().append("circle") | |
.attr("cx",function(d){return d[0]}) | |
.attr("cy",function(d){return d[1]}) | |
.attr("r", 8) | |
.attr("stroke","none") | |
.attr("fill","#666") | |
.call(d3.behavior.drag() | |
.on("dragstart",function (d, i) { | |
this.__origin__ = d; | |
}) | |
.on("drag", function (d, i) { | |
if (point_in_triangle(triangle_vertices[0], | |
triangle_vertices[1], | |
triangle_vertices[2], | |
this)){ | |
d[0] = this.__origin__[0] + d3.event.dx; | |
d[1] = this.__origin__[1] + d3.event.dy | |
d3.select(this).attr("cx", d[0]); | |
d3.select(this).attr("cy", d[1]); | |
data_1[i] = [d[0],d[1]]; | |
path_1.transition().duration(0) | |
.attr("d",function(d){return line(d)}); | |
} | |
else{ | |
d = this.__origin__; | |
} | |
}) | |
.on("dragend", function (d) { | |
delete this.__origin__; | |
})); | |
} | |
function click_animation_button() { | |
var create = "Create Snowflake"; | |
var reset = "Reset Paper"; | |
if ($("#animation_button").html() == create){ | |
$("#animation_button").html(reset); | |
animate_snowflake(); | |
} | |
else { | |
$("#animation_button").html(create); | |
reset_snowflake(); | |
} | |
} | |
function click_reset_button() { | |
data_1 = create_points(triangle_vertices[triangle_vertices.length-1], | |
triangle_vertices[0], | |
num_points_on_edge); | |
for (var i = 0; i < triangle_vertices.length-1; i++){ | |
data_1 = data_1.concat(create_points(triangle_vertices[i],triangle_vertices[i+1], num_points_on_edge)) | |
} | |
$("#animation_button").html("Create Snowflake"); | |
reset_snowflake(); | |
} | |
function reset_snowflake(){ | |
svg.selectAll(".snowflake") | |
.remove(); | |
initial_draw(); | |
} | |
function animate_snowflake(){ | |
var circles = svg.selectAll("circle") | |
.remove(); | |
d3.select(".snowflake") | |
.transition().duration(0); | |
var path_2 = svg.selectAll(".path_2") | |
.data([data_1]); | |
path_2.enter().append("path") | |
.attr("class","path_2") | |
.classed("snowflake",true) | |
.classed("even",true) | |
.attr("d",function (d){return line(d);}) | |
.transition().duration(duration) | |
.attr("d",function(d){return first_flip(d)}); | |
setTimeout(function(){ | |
var data_2 = create_data(path_2); | |
var path_3 = svg.selectAll(".path_3") | |
.data([data_1]); | |
path_3.enter().append("path") | |
.attr("class","path_3") | |
.classed("snowflake",true) | |
.classed("odd",true) | |
.attr("d",function(d){return line(d)}); | |
path_3.transition().duration(duration) | |
.attr("d",function(d){return second_flip(d)}); | |
var path_4 = svg.selectAll(".path_4") | |
.data([data_2]); | |
path_4.enter().append("path") | |
.attr("class","path_4") | |
.classed("snowflake",true) | |
.classed("even",true) | |
.attr("d",function(d){return line(d)}); | |
path_4.transition().duration(duration) | |
.attr("d",function(d){return second_flip(d)}); | |
setTimeout(function(){ | |
var data_3 = create_data(path_3); | |
var data_4 = create_data(path_4); | |
var path_5 = svg.selectAll(".path_5") | |
.data([data_1]); | |
path_5.enter().append("path") | |
.attr("class","path_5") | |
.classed("snowflake",true) | |
.classed("odd",true) | |
.attr("d",function(d){return line(d)}); | |
path_5.transition().duration(duration) | |
.attr("d",function(d){return third_flip(d)}); | |
var path_8 = svg.selectAll(".path_8") | |
.data([data_4]); | |
path_8.enter().append("path") | |
.attr("class","path_8") | |
.classed("snowflake",true) | |
.classed("even",true) | |
.attr("d",function(d){return line(d)}); | |
path_8.transition().duration(duration) | |
.attr("d",function(d){return third_flip(d)}); | |
var path_7 = svg.selectAll(".path_7") | |
.data([data_3]); | |
path_7.enter().append("path") | |
.attr("class","path_7") | |
.classed("snowflake",true) | |
.classed("odd",true) | |
.attr("d",function(d){return line(d)}); | |
path_7.transition().duration(duration) | |
.attr("d",function(d){return third_flip(d)}); | |
var path_6 = svg.selectAll(".path_6") | |
.data([data_2]); | |
path_6.enter().append("path") | |
.attr("class","path_6") | |
.classed("snowflake",true) | |
.classed("even",true) | |
.attr("d",function(d){return line(d)}); | |
path_6.transition().duration(duration) | |
.attr("d", function(d){return third_flip(d)}); | |
// add some glitter and the gradients to make it all purty | |
setTimeout(function () { | |
// third flip gradient change using opacity | |
var gradient_5 = svg.selectAll(".gradient_5") | |
.data([data_1]); | |
gradient_5.enter().append("path") | |
.attr("d",function(d){return third_flip(d)}) | |
.attr("fill","url(#gradient_4)") | |
.attr("class","snowflake") | |
.attr("opacity",0); | |
gradient_5.transition().duration(duration) | |
.attr("opacity",1); | |
var gradient_8 = svg.selectAll(".gradient_8") | |
.data([data_4]); | |
gradient_8.enter().append("path") | |
.attr("d",function(d){return third_flip(d)}) | |
.attr("fill","url(#gradient)") | |
.attr("class","snowflake") | |
.attr("opacity",0); | |
gradient_8.transition().duration(duration) | |
.attr("opacity",1); | |
var gradient_7 = svg.selectAll(".gradient_7") | |
.data([data_3]); | |
gradient_7.enter().append("path") | |
.attr("d",function(d){return third_flip(d)}) | |
.attr("fill","url(#gradient_2)") | |
.attr("class","snowflake") | |
.attr("opacity",0); | |
gradient_7.transition().duration(duration) | |
.attr("opacity",1); | |
var gradient_6 = svg.selectAll(".gradient_6") | |
.data([data_2]); | |
gradient_6.enter().append("path") | |
.attr("d",function(d){return third_flip(d)}) | |
.attr("fill","url(#gradient_3)") | |
.attr("class","snowflake") | |
.attr("opacity",0); | |
gradient_6.transition().duration(duration) | |
.attr("opacity",1); | |
// second_flip gradient | |
var gradient_3 = svg.selectAll(".gradient_3") | |
.data([data_1]); | |
gradient_3.enter().append("path") | |
.attr("d",function(d){return second_flip(d)}) | |
.attr("fill","url(#gradient_3)") | |
.attr("class","snowflake") | |
.attr("opacity",0); | |
gradient_3.transition().duration(duration) | |
.attr("opacity",1); | |
var gradient_4 = svg.selectAll(".gradient_4") | |
.data([data_2]); | |
gradient_4.enter().append("path") | |
.attr("d",function(d){return second_flip(d)}) | |
.attr("fill","url(#gradient_4)") | |
.attr("class","snowflake") | |
.attr("opacity",0); | |
gradient_4.transition().duration(duration) | |
.attr("opacity",1); | |
// first_flip gradient | |
var gradient_2 = svg.selectAll(".gradient_2") | |
.data([data_1]); | |
gradient_2.enter().append("path") | |
.attr("d",function(d){return first_flip(d)}) | |
.attr("fill","url(#gradient_2)") | |
.attr("class","snowflake") | |
.attr("opacity",0); | |
gradient_2.transition().duration(duration) | |
.attr("opacity",1); | |
// original path gradient | |
var gradient_1 = svg.selectAll(".gradient_1") | |
.data([data_1]); | |
gradient_1.enter().append("path") | |
.attr("d",function(d){return line(d)}) | |
.attr("fill","url(#gradient)") | |
.attr("class","snowflake") | |
.attr("opacity",0); | |
gradient_1.transition().duration(duration) | |
.attr("opacity",1); | |
// No longer require the normal paths | |
path_1.transition().duration(duration).remove(); | |
path_2.transition().duration(duration).remove(); | |
path_3.transition().duration(duration).remove(); | |
path_4.transition().duration(duration).remove(); | |
path_5.transition().duration(duration).remove(); | |
path_6.transition().duration(duration).remove(); | |
path_7.transition().duration(duration).remove(); | |
path_8.transition().duration(duration).remove(); | |
setTimeout(function () { | |
var circle_data = []; | |
_.each(_.range(100), function () { | |
circle_data.push([Math.random()*(width-2*padding)+padding, | |
Math.random()*(height-2*padding)+padding]); | |
}); | |
var new_circle = []; | |
_.each(circle_data,function(d){ | |
new_circle.push([d[0],height-d[1]]); | |
}); | |
circle_data = circle_data.concat(new_circle); | |
svg.selectAll("circle") | |
.data(circle_data) | |
.enter().append("circle") | |
.attr("cx",function(d){return d[0]}) | |
.attr("cy",function(d){return d[1]}) | |
.attr("class","glitter") | |
.attr("fill","none") | |
.attr("r",2); | |
d3.timer(function(){ | |
var circle = svg.selectAll(".glitter") | |
.data(circle_data); | |
circle.transition().duration(10) | |
.attr("fill",function(d,i){ | |
var random_metric = Math.random(); | |
return "rgba(255,255,255,"+random_metric+")"; | |
}); | |
if ($("#animation_button").html() == "Create Snowflake"){ | |
svg.selectAll(".glitter").remove(); | |
return true; | |
} | |
}); | |
}, duration/2); | |
}, duration); | |
}, duration); | |
}, duration); | |
} | |
// http://www.blackpawn.com/texts/pointinpoly/default.html | |
function cross_product (u, v) { | |
return u[0]*v[1] - v[0]*u[1]; | |
} | |
function sign (u) { | |
return u>=0 | |
} | |
function same_side(p1, p2, a, b) { | |
var b_minus_a = [b[0]-a[0], b[1]-a[1]]; | |
var p1_minus_a = [p1[0]-a[0], p1[1]-a[1]]; | |
var p2_minus_a = [p2[0]-a[0], p2[1]-a[1]]; | |
var cp1 = cross_product(b_minus_a, p1_minus_a); | |
var cp2 = cross_product(b_minus_a, p2_minus_a); | |
if (sign(cp1) === sign(cp2)) { | |
return true; | |
} | |
return false; | |
} | |
function point_in_triangle(a, b, c, that) { | |
var cursor_pos = []; | |
cursor_pos[0] = that.__origin__[0] + d3.event.dx; | |
cursor_pos[1] = that.__origin__[1] + d3.event.dy | |
if (same_side(cursor_pos, a, b, c) && same_side(cursor_pos, b, a, c) && same_side(cursor_pos, c, a, b)) { | |
return true; | |
} | |
return false; | |
} | |
function create_points(point_1, point_2, num_points){ | |
var points = []; | |
if (point_2[0] === point_1[0]){ | |
// do something different | |
for (var i = 0; i<num_points;i++){ | |
var x = point_2[0] | |
var y = (point_2[1] - point_1[1]) * i/num_points + point_1[1]; | |
points.push([x,y]); | |
} | |
} | |
else{ | |
var slope = (point_2[1] - point_1[1]) / (point_2[0] - point_1[0]); | |
var y_intercept = -slope*point_1[0] + point_1[1]; | |
for (var i = 0; i<num_points;i++){ | |
var x = (point_2[0] - point_1[0]) * i/num_points + point_1[0]; | |
var y = slope*x + y_intercept; | |
points.push([x,y]); | |
} | |
} | |
return points; | |
} | |
function create_data(path) { | |
var d = path.attr("d").substring(1); | |
var coordinates = _.map(d.split('L'), function (s) { | |
return _.map(s.split(','), Number); | |
}); | |
return coordinates; | |
}; | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment