block by nitaku 8949625

Quadratic Gosper curve (L-system)

Full Screen

A Quadratic Gosper space-filling curve drawn with an implementation of an L-system renderer. L-system definition is taken from this examples.

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 d, fractalize, height, qgosper, svg, svg_path, transition, tweenDash, 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;
  };

  qgosper = fractalize({
    axiom: '-YF',
    steps: 3,
    rules: {
      X: 'XFX-YF-YF+FX+FX-YF-YFFX+YF+FXFXYF-FX+YF+FXFX+YF-FXYF-YF-FX+FX+YFYF-',
      Y: '+FXFX-YF-YF+FX+FXYF+FX-YFYF-FX-YF+FXYFYF-FX-YFFX+FX+YF-YF-FX+FX+YFY'
    }
  });

  d = svg_path({
    fractal: qgosper,
    side: 3,
    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)');

  /* 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(30000).attrTween('stroke-dasharray', tweenDash);
  };

  svg.append('path').attr('class', 'curve').attr('d', d).attr('transform', 'translate(300,430)').call(transition);

}).call(this);

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Quadratic Gosper curve</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
    
qgosper = fractalize
    axiom: '-YF'
    steps: 3
    rules:
        X: 'XFX-YF-YF+FX+FX-YF-YFFX+YF+FXFXYF-FX+YF+FXFX+YF-FXYF-YF-FX+FX+YFYF-'
        Y: '+FXFX-YF-YF+FX+FXYF+FX-YFYF-FX-YF+FXYFYF-FX-YFFX+FX+YF-YF-FX+FX+YFY'
        
d = svg_path
    fractal: qgosper
    side: 3
    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)')
    
### 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(30000)
        .attrTween('stroke-dasharray', tweenDash)
        
        
svg.append('path')
    .attr('class', 'curve')
    .attr('d', d)
    .attr('transform', 'translate(300,430)')
    .call(transition)

index.css

.curve {
  fill: none;
  stroke: black;
  stroke-width: 1px;
  shape-rendering: crispEdges;
}

.shadow {
  opacity: 0.1;
}

index.sass

.curve
    fill: none
    stroke: black
    stroke-width: 1px
    shape-rendering: crispEdges
    
.shadow
    opacity: 0.1