Skip to content

Instantly share code, notes, and snippets.

@mootari
Created November 19, 2016 14:37
Show Gist options
  • Save mootari/56039eed3e606fc97562034ad5f71637 to your computer and use it in GitHub Desktop.
Save mootari/56039eed3e606fc97562034ad5f71637 to your computer and use it in GitHub Desktop.
Squaring the circle, part 1
license: mit
height: 500
scrolling: yes

Experiment in creating regular grids from circular paths, step 1: General concept and reference grids.

<!DOCTYPE html>
<html>
<head>
<style>
html, body, div, svg {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body, #app {
width: 100%;
height: 100%;
position: relative;
}
body {
overflow: hidden;
}
.line {
fill: none;
}
@media print {
.dg { display: none }
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
<script src="script.js"></script>
</body>
</html>
(function() {
var config = {
sides: 4,
increase: 2,
radius: 30,
steps: 10,
size: 15,
opacity: .2,
drawLines: true
};
var app = d3.select('#app');
var bbox = app.node().getBoundingClientRect();
var w = bbox.width;
var h = bbox.height;
var stage = app.append('svg').append('g');
resize(w, h);
gui();
update();
d3.select(window).on('resize', function() {
bbox = app.node().getBoundingClientRect();
w = bbox.width;
h = bbox.height;
resize(w, h);
});
function resize(width, height) {
d3.select(stage.node().parentNode)
.attr('width', width)
.attr('height', height);
stage.attr('transform', 'translate(' + (width / 2) + ' ' + (height / 2) + ')');
}
function gui() {
var delayUpdate = delayCallback(update, 20, 3);
var ui = new dat.GUI();
ui.add(config, 'sides').min(3).max(10).step(1).onChange(delayUpdate);
ui.add(config, 'increase').min(0).max(3).step(1).onChange(delayUpdate);
ui.add(config, 'steps').min(1).max(30).step(1).onChange(delayUpdate);
ui.add(config, 'radius').min(1).max(300).step(1).onChange(delayUpdate);
ui.add(config, 'size').min(1).max(300).step(1).onChange(delayUpdate);
ui.add(config, 'opacity').min(0).max(1).step(.001).onChange(delayUpdate);
ui.add(config, 'drawLines').onChange(delayUpdate);
}
function delayCallback(callback, delay, skipMax) {
function run() {
skipped = toId = 0;
callback();
}
var toId = skipped = 0;
return function() {
if(!toId || skipped < skipMax) {
skipped++;
clearTimeout(toId);
toId = setTimeout(run, delay);
}
};
}
function getPoints(sides, segments, radius, angleOffset) {
function segmentLine(p1, p2, segments) {
var points = [];
var dx = (p2[0] - p1[0]) / segments;
var dy = (p2[1] - p1[1]) / segments;
for(var i = 1; i < segments; i++) {
points.push([
p1[0] + dx * i,
p1[1] + dy * i
]);
}
return points;
}
var points = [], corners = [];
var aSegment = Math.PI * 2 / sides;
var i, a;
for(i = 0; i < sides; i++) {
a = aSegment * i + angleOffset;
corners.push([
Math.cos(a) * radius,
Math.sin(a) * radius
]);
}
var p1, p2;
for(i = 0; i < corners.length; i++) {
p1 = corners[i];
p2 = corners[(i + 1) % corners.length];
points.push(p1);
points = points.concat(segmentLine(p1, p2, segments));
}
return points;
}
function points(sides, steps, increase, radius) {
var offset = Math.PI / 2 - Math.PI / sides;
return d3.range(steps).map(function(v, i) {
return getPoints(sides, (i + 1) * increase, (i + 1) * radius, offset);
});
}
function renderCenter(parent, size, opacity) {
var center = parent.selectAll('.center').data([null]);
center.enter()
.append('circle')
.attr('class', 'center')
.attr('cx', 0)
.attr('cy', 0)
.merge(center)
.attr('r', size / 2)
.attr('fill', 'rgba(0,0,0,' + opacity + ')');
}
function renderPoints(points, parent, size, dotOpacity, drawLines) {
function renderLine(d) {
var p = d3.select(this).selectAll('.line').data(drawLines ? [d] : []);
p.exit().remove();
p.enter()
.append('path')
.attr('class', 'line')
.merge(p)
.attr('stroke', 'black')
.attr('d', function(d) {
return 'M' + d.map(function(d) {
return d.join(',');
}).join('L') + 'Z';
});
}
var group = parent.selectAll('.step').data(points);
group.exit().remove();
group = group.enter()
.append('g')
.attr('class', 'step')
.merge(group)
.each(renderLine);
var dot = group.selectAll('.dot')
.data(function(d) { return d; });
dot.exit().remove();
dot
.enter()
.append('circle')
.attr('class', 'dot')
.merge(dot)
.attr('r', size / 2)
.attr('cx', function(d) { return d[0]; })
.attr('cy', function(d) { return d[1]; })
.attr('fill', 'rgba(0,0,0,' + dotOpacity + ')');
}
function update() {
renderCenter(stage, config.size, config.opacity);
var p = points(config.sides, config.steps, config.increase, config.radius);
renderPoints(p, stage, config.size, config.opacity, config.drawLines);
}
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment