Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active September 18, 2017 02:14
Show Gist options
  • Save nitaku/8955159 to your computer and use it in GitHub Desktop.
Save nitaku/8955159 to your computer and use it in GitHub Desktop.
Stability of Peano curve

This example shows the stability of the Peano space-filling curve: each order of the curve can be overlapped to each other (click the canvas to see it).

By stability, we indicate the property of the curve to yield stable layouts, suitable for treemaps that follow our data cartography methodology. According to it, a slight change in input data should be reflected only by a slight change in the map. An unstable curve (like the classical Hilbert curve, see this example) could cause a map to flip even if a single cell is added.

### compute a Lindenmayer system given an axiom, a number of steps and rules ###
fractalize = (config) ->
input = config.axiom
for i in [0...config.steps]
output = ''
for char in input
if char of config.rules
output += config.rules[char]
else
output += char
input = output
return output
### convert a Lindenmayer string into an SVG path string ###
svg_path = (config) ->
angle = 0.0
path = 'M0 0'
for char in config.fractal
if char == '+'
angle += config.angle
else if char == '-'
angle -= config.angle
else if char == 'F'
path += "l#{config.side * Math.cos(angle)} #{config.side * Math.sin(angle)}"
return path
side = 6
curves = []
for steps in [1..4]
fractal = fractalize
axiom: 'L'
steps: steps
rules:
L: 'LFRFL-F-RFLFR+F+LFRFL'
R: 'RFLFR+F+LFRFL-F-RFLFR'
curves.push svg_path
fractal: fractal
side: side
angle: Math.PI/2
width = 960
height = 500
svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height)
svg.selectAll('.curve')
.data(curves)
.enter().append('path')
.attr('class', 'curve')
.attr('d', (d)->d)
.attr('transform', (d,i)->"translate(#{100 + (Math.pow(3,i+1)/2+i)*side},490)")
.attr('opacity', 1)
collapse = false
svg.on 'click', () ->
collapse = not collapse
if collapse
svg.selectAll('.curve').transition().duration(1000)
.attr('transform', (d,i)->'translate(240,490)')
.attr('opacity', 0.4)
else
svg.selectAll('.curve').transition().duration(1000)
.attr('transform', (d,i)->"translate(#{100 + (Math.pow(3,i+1)/2+i)*side},490)")
.attr('opacity', 1)
.curve {
fill: none;
stroke: black;
stroke-width: 1.5px;
}
svg {
cursor: pointer;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Peano curve stability</title>
<link type="text/css" href="index.css" rel="stylesheet"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body></body>
<script src="index.js"></script>
</html>
/* compute a Lindenmayer system given an axiom, a number of steps and rules
*/
(function() {
var collapse, curves, fractal, fractalize, height, side, steps, svg, svg_path, width;
fractalize = function(config) {
var char, i, input, output, _i, _len, _ref;
input = config.axiom;
for (i = 0, _ref = config.steps; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) {
output = '';
for (_i = 0, _len = input.length; _i < _len; _i++) {
char = input[_i];
if (char in config.rules) {
output += config.rules[char];
} else {
output += char;
}
}
input = output;
}
return output;
};
/* convert a Lindenmayer string into an SVG path string
*/
svg_path = function(config) {
var angle, char, path, _i, _len, _ref;
angle = 0.0;
path = 'M0 0';
_ref = config.fractal;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
char = _ref[_i];
if (char === '+') {
angle += config.angle;
} else if (char === '-') {
angle -= config.angle;
} else if (char === 'F') {
path += "l" + (config.side * Math.cos(angle)) + " " + (config.side * Math.sin(angle));
}
}
return path;
};
side = 6;
curves = [];
for (steps = 1; steps <= 4; steps++) {
fractal = fractalize({
axiom: 'L',
steps: steps,
rules: {
L: 'LFRFL-F-RFLFR+F+LFRFL',
R: 'RFLFR+F+LFRFL-F-RFLFR'
}
});
curves.push(svg_path({
fractal: fractal,
side: side,
angle: Math.PI / 2
}));
}
width = 960;
height = 500;
svg = d3.select('body').append('svg').attr('width', width).attr('height', height);
svg.selectAll('.curve').data(curves).enter().append('path').attr('class', 'curve').attr('d', function(d) {
return d;
}).attr('transform', function(d, i) {
return "translate(" + (100 + (Math.pow(3, i + 1) / 2 + i) * side) + ",490)";
}).attr('opacity', 1);
collapse = false;
svg.on('click', function() {
collapse = !collapse;
if (collapse) {
return svg.selectAll('.curve').transition().duration(1000).attr('transform', function(d, i) {
return 'translate(240,490)';
}).attr('opacity', 0.4);
} else {
return svg.selectAll('.curve').transition().duration(1000).attr('transform', function(d, i) {
return "translate(" + (100 + (Math.pow(3, i + 1) / 2 + i) * side) + ",490)";
}).attr('opacity', 1);
}
});
}).call(this);
.curve
fill: none
stroke: black
stroke-width: 1.5px
svg
cursor: pointer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment