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