block by nitaku dcce9b645783d5239a04

FASS spiral (L-system)

Full Screen

Another take on mixing spirals with space-filling curves, this time with a different FASS curve (see also this experiment using a Hilbert curve).

Unlike the previous example, this layout tries to keep the origin point in the centroid of the figure. The chosen FASS curve covers a square with an odd number of tiles (9), so the center tile can be chosen to host the origin (the Hilbert curve covers instead a square with 4 tiles).

Unfortunately, the choice of a different curve makes the layout lose some locality: The worst aspect ratio of a jigsaw region for this layout is 1/6, while for the Hilbert one is 1/4 (to see that, just look at how many segments are drawn without turns in the central part of the layout - the behavior you can see is also repeated in a fractal fashion with bigger tiles).

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: 'FY',
    steps: 4,
    rules: {
      Y: 'Y+RFR+FLF+RFRFR+FLFLF',
      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 (L-system)" />
    <title>FASS spiral (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: 'FY'
    steps: 4
    rules:
        Y: 'Y+RFR+FLF+RFRFR+FLFLF'
        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;
}