Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active December 25, 2015 20:59
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 nitaku/7039746 to your computer and use it in GitHub Desktop.
Save nitaku/7039746 to your computer and use it in GitHub Desktop.
Simplified Gosper regions

A test for Visvalingam simplification applied to Gosper regions. Mouseover to control the simplification.

The code uses the TopoJSON API as in this Mike Bostock's example to obtain a topology-preserving simplification (regions are simplified together). If regions were simplified one by one, artifacts (holes and overlaps) would have appeared along the common boundaries.

Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
global = {
area: 1
}
### whenever the mouse moves, compute a new area parameter for the simplification, then draw again the regions ###
mousemoved = () ->
global.area = global.mouse_scale.invert(d3.mouse(this)[0])
global.svg.selectAll('.region, .boundary').attr('d', global.path_generator)
window.main = () ->
width = 960
height = 500
### prepare a scale for the mouse input ###
global.mouse_scale = d3.scale.sqrt()
.domain([0, 1e4])
.range([0, width])
global.svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height)
.on('mousemove', mousemoved)
vis = global.svg.append('g')
.attr('transform','translate(640, 120)')
### custom projection to make hexagons appear regular (y axis is also flipped) ###
### it also applies the Visvalingam simplification ###
radius = 0.6
dx = radius * 2 * Math.sin(Math.PI / 3)
dy = radius * 1.5
global.path_generator = d3.geo.path()
.projection d3.geo.transform({
point: (x,y,z) ->
### draw only nodes that are "important" enough ###
if z >= global.area
this.stream.point(x * dx / 2, -(y - (2 - (y & 1)) / 3) * dy / 2)
})
### define a color scale (Colorbrewer's Set3) ###
colorify = d3.scale.ordinal()
.domain(['A','B','C','D'])
.range(["#8dd3c7","#ffffb3","#bebada","#fb8072"])
### load topoJSON data ###
d3.json 'readme.regions.topo.json', (error, data) ->
### presimplify the topology (compute the effective area (z) of each point) ###
topojson.presimplify(data)
### draw the cells ###
vis.selectAll('.region')
.data(topojson.feature(data, data.objects.regions).features)
.enter().append('path')
.attr('class', 'region')
.attr('d', global.path_generator)
.attr('fill', (d) -> colorify(d.properties['class']))
### draw the boundaries ###
vis.append('path')
.datum(topojson.mesh(data, data.objects.regions, (a, b) -> a is b))
.attr('d', global.path_generator)
.attr('class', 'outer boundary')
vis.append('path')
.datum(topojson.mesh(data, data.objects.regions, (a, b) -> a isnt b))
.attr('d', global.path_generator)
.attr('class', 'boundary')
.boundary {
stroke: #333333;
stroke-width: 1px;
fill: none;
}
.boundary.outer {
stroke-width: 1.5px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Simplified Gosper Regions</title>
<link type="text/css" href="index.css" rel="stylesheet"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="index.js"></script>
</head>
<body onload="main()"></body>
</html>
(function() {
var global, mousemoved;
global = {
area: 1
};
/* whenever the mouse moves, compute a new area parameter for the simplification, then draw again the regions
*/
mousemoved = function() {
global.area = global.mouse_scale.invert(d3.mouse(this)[0]);
return global.svg.selectAll('.region, .boundary').attr('d', global.path_generator);
};
window.main = function() {
var colorify, dx, dy, height, radius, vis, width;
width = 960;
height = 500;
/* prepare a scale for the mouse input
*/
global.mouse_scale = d3.scale.sqrt().domain([0, 1e4]).range([0, width]);
global.svg = d3.select('body').append('svg').attr('width', width).attr('height', height).on('mousemove', mousemoved);
vis = global.svg.append('g').attr('transform', 'translate(640, 120)');
/* custom projection to make hexagons appear regular (y axis is also flipped)
*/
/* it also applies the Visvalingam simplification
*/
radius = 0.6;
dx = radius * 2 * Math.sin(Math.PI / 3);
dy = radius * 1.5;
global.path_generator = d3.geo.path().projection(d3.geo.transform({
point: function(x, y, z) {
/* draw only nodes that are "important" enough
*/ if (z >= global.area) {
return this.stream.point(x * dx / 2, -(y - (2 - (y & 1)) / 3) * dy / 2);
}
}
}));
/* define a color scale (Colorbrewer's Set3)
*/
colorify = d3.scale.ordinal().domain(['A', 'B', 'C', 'D']).range(["#8dd3c7", "#ffffb3", "#bebada", "#fb8072"]);
/* load topoJSON data
*/
return d3.json('readme.regions.topo.json', function(error, data) {
/* presimplify the topology (compute the effective area (z) of each point)
*/ topojson.presimplify(data);
/* draw the cells
*/
vis.selectAll('.region').data(topojson.feature(data, data.objects.regions).features).enter().append('path').attr('class', 'region').attr('d', global.path_generator).attr('fill', function(d) {
return colorify(d.properties['class']);
});
/* draw the boundaries
*/
vis.append('path').datum(topojson.mesh(data, data.objects.regions, function(a, b) {
return a === b;
})).attr('d', global.path_generator).attr('class', 'outer boundary');
return vis.append('path').datum(topojson.mesh(data, data.objects.regions, function(a, b) {
return a !== b;
})).attr('d', global.path_generator).attr('class', 'boundary');
});
};
}).call(this);
.boundary
stroke: #333
stroke-width: 1px
fill: none
&.outer
stroke-width: 1.5px
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment