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.
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.
### 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) |
.curve { | |
fill: none; | |
stroke: black; | |
stroke-width: 1px; | |
shape-rendering: crispEdges; | |
} | |
.shadow { | |
opacity: 0.1; | |
} |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Quadratic Gosper curve</title> | |
<link type="text/css" href="index.css" rel="stylesheet"/> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
</head> | |
<body></body> | |
<script src="index.js"></script> | |
</html> |
/* 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); |
.curve | |
fill: none | |
stroke: black | |
stroke-width: 1px | |
shape-rendering: crispEdges | |
.shadow | |
opacity: 0.1 | |