block by nitaku e0f1b570161875b27fc9

FASS spiral II (L-system)

Full Screen

An improvement upon the previous experiment. Two alternating concatenation rules (X and Y) are used to make the spiral grow alternatively clockwise and counterclockwise. Due to the fact that less segments are aligned, the worst aspect ratio of this layout is 1/4 like for the Hilbert spiral layout (the former experiment gave 1/6).

index.js

(function() {
  // noprotect;
  /* compute a Lindenmayer system given an axiom, a number of steps and rules
  */

  var curve, d, fractalize, height, svg_path, transition, tweenDash, vis, width;

  fractalize = function(config) {
    var char, i, input, output, _i, _j, _len, _ref;

    input = config.axiom;
    for (i = _i = 0, _ref = config.steps; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
      output = '';
      for (_j = 0, _len = input.length; _j < _len; _j++) {
        char = input[_j];
        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;
  };

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

  curve = fractalize({
    axiom: 'FX',
    steps: 4,
    rules: {
      X: 'Y-LFL-FRF-LFLFL-FRFR+F',
      Y: 'X+RFR+FLF+RFRFR+FLFL-F',
      L: 'LF+RFR+FL-F-LFLFL-FRFR+',
      R: '-LFLF+RFRFR+F+RF-LFL-FR'
    }
  });

  d = svg_path({
    fractal: curve,
    side: 5,
    angle: Math.PI / 2
  });

  width = 960;

  height = 500;

  vis = d3.select('body').append('svg').attr({
    width: width,
    height: height
  }).append('g').attr({
    transform: "translate(" + (width / 2) + "," + (height / 2) + ")"
  });

  vis.append('path').attr('class', 'curve shadow').attr('d', d);

  vis.append('path').attr('class', 'curve').attr('d', d).call(transition);

  vis.append('circle').attr({
    r: 2,
    fill: 'red'
  });

}).call(this);

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="description" content="FASS spiral II (L-system)" />
    <title>FASS spiral II (L-system)</title>
    <link rel="stylesheet" href="index.css">
    <script src="//d3js.org/d3.v3.min.js"></script>
</head>
<body>
  <script src="index.js"></script>
</body>
</html>

index.coffee

`// noprotect`

### 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
    
### 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)
        
        
curve = fractalize
    axiom: 'FX'
    steps: 4
    rules:
        X: 'Y-LFL-FRF-LFLFL-FRFR+F'
        Y: 'X+RFR+FLF+RFRFR+FLFL-F'
        L: 'LF+RFR+FL-F-LFLFL-FRFR+'
        R: '-LFLF+RFRFR+F+RF-LFL-FR'
        
d = svg_path
    fractal: curve
    side: 5
    angle: Math.PI/2
    
width = 960
height = 500

vis = d3.select('body').append('svg')
    .attr
      width: width
      height: height
  .append('g')
    .attr
      transform: "translate(#{width/2},#{height/2})"
    
vis.append('path')
    .attr('class', 'curve shadow')
    .attr('d', d)
    
vis.append('path')
    .attr('class', 'curve')
    .attr('d', d)
    .call(transition)
    
vis.append('circle')
  .attr
    r: 2
    fill: 'red'
    

index.css

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

.shadow {
  opacity: 0.1;
}