Skip to content

Instantly share code, notes, and snippets.

@peterlozano
Last active August 29, 2015 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peterlozano/480f90947c6e08be6b95 to your computer and use it in GitHub Desktop.
Save peterlozano/480f90947c6e08be6b95 to your computer and use it in GitHub Desktop.
Cycloid Optical Illusion.

Recently an optical illusion video became popular and I've tried to implement the same animation using d3.js.

Basically it seems that the circles are moving in a circle but they are all moving in straight lines.

I'm starting to learn d3.js to I'm open to any comments about how to improve the implementation.

Articles

<!DOCTYPE html>
<html>
<head>
<title>Cycloid Opticall Illusion</title>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<label for="circle-count">Circle Count</label>
<select name="circle_count" id="circle-count">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="8" selected>8</option>
<option value="16">16</option>
<option value="32">32</option>
</select>
<label for="radius">Size</label>
<select name="radius" id="radius">
<option value="5">5</option>
<option value="10">10</option>
<option value="15">15</option>
<option value="20" selected>20</option>
</select>
<label for="show-lines">Show lines</label>
<input type="checkbox" id="show-lines" checked="checked"/>
</body>
<script>
var width = 450;
var height = 450;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var center_x = (width / 2);
var center_y = (height / 2);
// Calculate position of the circles, given a d=position and angle=current angle.
var xpos_gen = function(d, angle) {
return center_x + (((center_y - radius) * Math.sin(d)) * Math.sin(angle + d));
};
var ypos_gen = function(d, angle) {
return center_y + (((center_y - radius) * Math.cos(d)) * Math.sin(angle + d));
};
// Defaults.
var circle_count = 8;
var radius = 20;
// For short.
var pi = Math.PI;
// Calculate line start and end coordinates.
var line_x1 = function(d) {
return center_x - Math.sin(d) * (width / 2);
};
var line_x2 = function(d) {
return center_x + Math.sin(d) * (width / 2);
};
var line_y1 = function(d) {
return center_y - Math.cos(d) * (height / 2);
};
var line_y2 = function(d) {
return center_y + Math.cos(d) * (height / 2);
};
// The data sets the initial position of the circles as well as their
// relative positions to each other.
var data = [];
// Update the drawing with new parameters.
var generate = function(circle_count) {
// Refill the data.
data = [];
for (i = 0; i < circle_count; i++) {
data.push((pi / circle_count) * i);
}
// Draw the lines
var lines = svg.selectAll('line').data(data);
var line_draw = function(l) {
l.attr('x1', line_x1)
.attr('y1', line_y1)
.attr('x2', line_x2)
.attr('y2', line_y2);
};
lines.enter()
.append('line')
.call(line_draw);
lines.call(line_draw);
lines.exit().remove();
};
generate(8);
var update = function(angle) {
// Generate new position functions in each call that depend on the current angle.
var xpos = function(d) {
return xpos_gen(d, angle);
};
var ypos = function(d) {
return ypos_gen(d, angle);
};
var circles = svg.selectAll('circle').data(data);
var circle_draw = function(circle) {
circle.attr('r', radius)
.attr('cx', xpos)
.attr('cy', ypos);
};
circles.enter()
.append('circle')
.call(circle_draw);
circles.call(circle_draw);
circles.exit().remove();
};
// Base angle to make calculations
// Ee increase this 1 degree each timer call.
var angle = 0;
d3.timer(function() {
// Increase base angle.
angle = angle + (Math.PI / 90);
// Avoid angle growing too much.
if (angle >= Math.PI * 2) {
angle = 0;
}
// Update visuals.
update(angle);
});
d3.select('#circle-count').on('change', function() {
generate(this.value);
});
d3.select('#radius').on('change', function() {
radius = this.value;
});
d3.select('#show-lines').on('change', function() {
if (this.checked) {
d3.selectAll('line').style('display', null);
}
else {
d3.selectAll('line').style('display', 'none');
}
});
</script>
</html>
line {
stroke:rgb(255,0,0);
stroke-width:2
}
body {
margin: 10px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment