block by nitaku 6514960

Hilbert curve (L-system)

Full Screen

A Hilbert space-filling curve drawn with an implementation of an L-system renderer.

The drawing of the line is animated using Mike Bostock’s stroke dash interpolation.

index.js


/* compute a Lindenmayer system given an axiom, a number of steps and rules
*/

(function() {
  var fractalize, svg_path, transition, tweenDash;

  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;
  };

  window.main = function() {
    var d, height, hilbert, svg, width;
    hilbert = fractalize({
      axiom: 'A',
      steps: 6,
      rules: {
        A: '-BF+AFA+FB-',
        B: '+AF-BFB-FA+'
      }
    });
    d = svg_path({
      fractal: hilbert,
      side: 6,
      angle: Math.PI / 2
    });
    width = 960;
    height = 500;
    svg = d3.select('body').append('svg').attr('width', width).attr('height', height);
    svg.append('path').attr('class', 'curve shadow').attr('d', d).attr('transform', 'translate(300,430)');
    return svg.append('path').attr('class', 'curve').attr('d', d).attr('transform', 'translate(300,430)').call(transition);
  };

  /* animate the path
  */

  /* from Mike Bostock's stroke dash interpolation example http://bl.ocks.org/mbostock/5649592
  */

  tweenDash = function() {
    var i, l;
    l = this.getTotalLength();
    i = d3.interpolateString('0,' + l, l + ',' + l);
    return function(t) {
      return i(t);
    };
  };

  transition = function(path) {
    return path.transition().duration(20000).attrTween('stroke-dasharray', tweenDash);
  };

}).call(this);

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hilbert curve</title>
        <link type="text/css" href="index.css" rel="stylesheet"/>
        <script src="//d3js.org/d3.v3.min.js"></script>
        <script src="index.js"></script>
    </head>
    <body onload="main()"></body>
</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
    
window.main = () ->
    hilbert = fractalize
        axiom: 'A'
        steps: 6
        rules:
            A: '-BF+AFA+FB-'
            B: '+AF-BFB-FA+'
            
    d = svg_path
        fractal: hilbert
        side: 6
        angle: Math.PI/2
        
    width = 960
    height = 500
    
    svg = d3.select('body').append('svg')
        .attr('width', width)
        .attr('height', height)
        
    svg.append('path')
        .attr('class', 'curve shadow')
        .attr('d', d)
        .attr('transform', 'translate(300,430)')
        
    svg.append('path')
        .attr('class', 'curve')
        .attr('d', d)
        .attr('transform', 'translate(300,430)')
        .call(transition)
        
### animate the path ###
### from Mike Bostock's stroke dash interpolation example http://bl.ocks.org/mbostock/5649592 ###
tweenDash = () ->
    l = this.getTotalLength()
    i = d3.interpolateString('0,' + l, l + ',' + l)
    return (t) -> i(t)
    
transition = (path) ->
    path.transition()
        .duration(20000)
        .attrTween('stroke-dasharray', tweenDash)
        

index.css

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

.shadow {
  opacity: 0.1;
}

index.sass

.curve
    fill: none
    stroke: black
    stroke-width: 1.5px
    
.shadow
    opacity: 0.1