Last active
August 29, 2015 14:03
-
-
Save nitaku/1ea254444b4a3fb73dd3 to your computer and use it in GitHub Desktop.
Archimedean spiral with a given angular length
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
WIDTH = 960 | |
HEIGHT = 500 | |
svg = d3.select('body').append('svg') | |
.attr | |
width: WIDTH | |
height: HEIGHT | |
.append('g') | |
.attr | |
transform: "translate(#{WIDTH/2},80)" | |
# WARNING the following assumes a=0 | |
a = 0 | |
# angle samples per turn (not good for large spirals!) | |
SAMPLES = 80 | |
# draw axes | |
svg.append('line') | |
.attr | |
class: 'my_axis' | |
x1: -WIDTH | |
x2: WIDTH | |
svg.append('line') | |
.attr | |
class: 'my_axis' | |
y1: -HEIGHT | |
y2: HEIGHT | |
spiral = svg.append('path') | |
.attr | |
class: 'spiral' | |
# draw the controls | |
theta_max_scale = d3.scale.linear() | |
.domain([0, 360 * 12]) | |
.range([-WIDTH/2+50, WIDTH/2-50]) | |
.clamp(true) | |
theta_max_brush = d3.svg.brush() | |
.x(theta_max_scale) | |
.extent([0, 0]) | |
THETA_SLIDER_Y = -50 | |
svg.append('g') | |
.attr('class', 'x axis') | |
.attr('transform', "translate(0,#{THETA_SLIDER_Y})") | |
.call(d3.svg.axis() | |
.scale(theta_max_scale) | |
.orient('bottom') | |
.tickFormat((d) -> d + '°') | |
.tickSize(0) | |
.tickPadding(12)) | |
.select('.domain') | |
.select(() -> this.parentNode.appendChild(this.cloneNode(true)) ) | |
.attr('class', 'halo') | |
slider = svg.append('g') | |
.attr('class', 'slider') | |
.call(theta_max_brush) | |
slider.selectAll('.extent,.resize') | |
.remove() | |
slider.select('.background') | |
.attr('transform', "translate(0,#{THETA_SLIDER_Y-11})") | |
.attr('height', 22) | |
handle = slider.append('circle') | |
.attr('class', 'handle') | |
.attr('transform', "translate(0,#{THETA_SLIDER_Y})") | |
.attr('r', 9) | |
# initial animation | |
slider | |
.call(theta_max_brush.event) | |
.transition() | |
.duration(750) | |
.call(theta_max_brush.extent([2*360, 2*360])) | |
.call(theta_max_brush.event) | |
brushed = () -> | |
value = theta_max_brush.extent()[0] | |
if d3.event.sourceEvent # not a programmatic event | |
value = theta_max_scale.invert(d3.mouse(this)[0]) | |
theta_max_brush.extent([value, value]) | |
handle.attr('cx', theta_max_scale(value)) | |
# redraw the spiral | |
redraw(2*Math.PI * value/360, 16) | |
theta_max_brush | |
.on('brush', brushed) | |
redraw = (theta_max, dist) -> | |
b = dist/(2*Math.PI) | |
# compute the radius | |
radius = a + b * theta_max | |
number_of_points = Math.ceil( theta_max/(2*Math.PI) * SAMPLES ) | |
points = d3.range(0, number_of_points).map (i) -> | |
theta = i*2*Math.PI/SAMPLES | |
# PI/2 is subtracted from angles to have the spiral end at the top | |
return {theta: theta-Math.PI/2-theta_max, r: a + b*theta} | |
points.push {theta: -Math.PI/2, r: radius} | |
# draw the spiral | |
line_generator = d3.svg.line() | |
.x((d) -> d.r * Math.cos(d.theta)) | |
.y((d) -> radius + d.r * Math.sin(d.theta)) # translate by radius to keep the final point in the same position | |
.interpolate('cardinal') | |
spiral | |
.datum(points) | |
.attr | |
d: line_generator | |
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
svg { | |
background-color: white; | |
} | |
.spiral { | |
fill: none; | |
stroke: black; | |
stroke-width: 2px; | |
} | |
.my_axis { | |
fill: none; | |
stroke: lightgray; | |
stroke-dasharray: 24 6 2 6; | |
shape-rendering: crispEdges; | |
} | |
.axis { | |
font: 10px sans-serif; | |
-webkit-user-select: none; | |
-moz-user-select: none; | |
user-select: none; | |
} | |
.axis .domain { | |
fill: none; | |
stroke: #000; | |
stroke-opacity: .3; | |
stroke-width: 10px; | |
stroke-linecap: round; | |
} | |
.axis .halo { | |
fill: none; | |
stroke: #ddd; | |
stroke-width: 8px; | |
stroke-linecap: round; | |
} | |
.slider .handle { | |
fill: #fff; | |
stroke: #000; | |
stroke-opacity: .5; | |
stroke-width: 1.25px; | |
pointer-events: none; | |
} |
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 charset="utf-8"> | |
<meta name="description" content="Archimedean spiral with a given angular length" /> | |
<title>Archimedean spiral with a given angular length</title> | |
<link rel="stylesheet" href="index.css"> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
</head> | |
<body> | |
<script src="index.js"></script> | |
</body> | |
</html> |
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
(function() { | |
var HEIGHT, SAMPLES, THETA_SLIDER_Y, WIDTH, a, brushed, handle, redraw, slider, spiral, svg, theta_max_brush, theta_max_scale; | |
WIDTH = 960; | |
HEIGHT = 500; | |
svg = d3.select('body').append('svg').attr({ | |
width: WIDTH, | |
height: HEIGHT | |
}).append('g').attr({ | |
transform: "translate(" + (WIDTH / 2) + ",80)" | |
}); | |
a = 0; | |
SAMPLES = 80; | |
svg.append('line').attr({ | |
"class": 'my_axis', | |
x1: -WIDTH, | |
x2: WIDTH | |
}); | |
svg.append('line').attr({ | |
"class": 'my_axis', | |
y1: -HEIGHT, | |
y2: HEIGHT | |
}); | |
spiral = svg.append('path').attr({ | |
"class": 'spiral' | |
}); | |
theta_max_scale = d3.scale.linear().domain([0, 360 * 12]).range([-WIDTH / 2 + 50, WIDTH / 2 - 50]).clamp(true); | |
theta_max_brush = d3.svg.brush().x(theta_max_scale).extent([0, 0]); | |
THETA_SLIDER_Y = -50; | |
svg.append('g').attr('class', 'x axis').attr('transform', "translate(0," + THETA_SLIDER_Y + ")").call(d3.svg.axis().scale(theta_max_scale).orient('bottom').tickFormat(function(d) { | |
return d + '°'; | |
}).tickSize(0).tickPadding(12)).select('.domain').select(function() { | |
return this.parentNode.appendChild(this.cloneNode(true)); | |
}).attr('class', 'halo'); | |
slider = svg.append('g').attr('class', 'slider').call(theta_max_brush); | |
slider.selectAll('.extent,.resize').remove(); | |
slider.select('.background').attr('transform', "translate(0," + (THETA_SLIDER_Y - 11) + ")").attr('height', 22); | |
handle = slider.append('circle').attr('class', 'handle').attr('transform', "translate(0," + THETA_SLIDER_Y + ")").attr('r', 9); | |
slider.call(theta_max_brush.event).transition().duration(750).call(theta_max_brush.extent([2 * 360, 2 * 360])).call(theta_max_brush.event); | |
brushed = function() { | |
var value; | |
value = theta_max_brush.extent()[0]; | |
if (d3.event.sourceEvent) { | |
value = theta_max_scale.invert(d3.mouse(this)[0]); | |
theta_max_brush.extent([value, value]); | |
} | |
handle.attr('cx', theta_max_scale(value)); | |
return redraw(2 * Math.PI * value / 360, 16); | |
}; | |
theta_max_brush.on('brush', brushed); | |
redraw = function(theta_max, dist) { | |
var b, line_generator, number_of_points, points, radius; | |
b = dist / (2 * Math.PI); | |
radius = a + b * theta_max; | |
number_of_points = Math.ceil(theta_max / (2 * Math.PI) * SAMPLES); | |
points = d3.range(0, number_of_points).map(function(i) { | |
var theta; | |
theta = i * 2 * Math.PI / SAMPLES; | |
return { | |
theta: theta - Math.PI / 2 - theta_max, | |
r: a + b * theta | |
}; | |
}); | |
points.push({ | |
theta: -Math.PI / 2, | |
r: radius | |
}); | |
line_generator = d3.svg.line().x(function(d) { | |
return d.r * Math.cos(d.theta); | |
}).y(function(d) { | |
return radius + d.r * Math.sin(d.theta); | |
}).interpolate('cardinal'); | |
return spiral.datum(points).attr({ | |
d: line_generator | |
}); | |
}; | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment