block by nitaku 8955159

Stability of Peano curve

Full Screen

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.

index.js

/* 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);

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Peano curve stability</title>
        <link type="text/css" href="index.css" rel="stylesheet"/>
        <script src="//d3js.org/d3.v3.min.js"></script>
    </head>
    <body></body>
    <script src="index.js"></script>
</html>

index.coffee

### 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)
            

index.css

.curve {
  fill: none;
  stroke: black;
  stroke-width: 1.5px;
}

svg {
  cursor: pointer;
}

index.sass

.curve
    fill: none
    stroke: black
    stroke-width: 1.5px
    
svg
    cursor: pointer