Quantcast
Channel: Blog - delimited
Viewing all articles
Browse latest Browse all 26

Fractals in D3: Dragon Curves

$
0
0
hokusai-fractals-wave.jpg

Authored by Steven Hall

This week I am continuing to experiment with rendering fractals in D3.  In this post we're looking at examples of generating some really cool fractals called dragon curves (also referred to as Heighway dragons).  This post is a continuation of the previous one on fractal ferns.  Take a look at that post if you want some basic info on fractals and some links I found useful.  Fractals are a world unto themselves, so there are plenty of interesting things to be investigated in this area.  We are just scratching the surface with these two posts.

Hokusai and Fractals

I've been a big fan of Japanese woodblock art for years and it turns out as luck would have it there is a connection between fractals and one of the most famous woodblock artists, Katsushika Hokusai.  In the 2008 NOVA documentary on fractals (YouTube), Mandelbrot, the creator of fractal geometry, mentions in passing that he believed Hokusai intuitively understood fractals and incorporated them into his art.  The image above is a composite of Hokusai's "The Great Wave" and a dragon curve generated in D3 (example 2 below).  I think putting these two images together helps see what Mandelbrot was talking about (with some imagination).  Cool stuff.  It's always nice when worlds collide like this.

If you are interested in Japanese woodblock art, John Resig (of JQuery fame) has a nice search engine.

Dragon Curves

Dragon_curve.png

A few words about how dragon curves are generated in general and then I'll move on to the code I came up with to make this happen in D3.  To get sense of what needs to be done in the JavaScript, It may be helpful to click on one the examples below to get a sense of what the goal is. 

Each dragon curve starts with a simple set of lines and gradually builds up the fractal pattern by iteratively replacing each line segment with two new segments.  The basic process is represented well in the image to the right here taken from the Wikipedia page on dragon curves.

Like all fractals, dragon curves have a self-similar, recursive pattern to them and can be built up by repeatedly applying a set of simple rules.  The curves being generated in this post are modeled on a variety of dragon curves called Heighway Dragons.  In each iteration we need to start from a base segment and replace each segment by 2 segments with a right angle and with a rotation of 45° alternatively to the right and to the left.  It took some experimentation to come up with an efficient way to do this in D3, but I am pretty pleased with the results.  Let's look at the code.

Code Examples in this Post

You can check out the code by clicking on the examples below and looking at them in the browser or you can download all the examples in one zip file and run it locally by going to this github repository.  The code is pretty straightforward just a few HTML files and the main JavaScript for all the examples in the dragons.js file.  The dragon.js file is doing all the heavy lifting in each of the examples and there are just a few lines of JavaScript changing in a script tag in each HTML file.

dragon-cruve-d3-s.png

Example 1 - Single Curve

Basic single dragon curve rendered in D3.  Starting from a two line V-shape, the example allows you to view each iteration as the curve is gradually built up.

You can view the example here.


fractal-dragon-curve-d3.png

Example 2 - Double Curve

Two dragon curves over-lapping each other.  This example starts with four lines arranged in V-shape and three different colors. The colors are inherited in each successive iteration from the parent line segment. Like the first example you can step through each iteration and see how the image emerges.

You can view the example here.


fractal-twin-dragon-d3.png

Example 3 - Twin Dragon

This is an example of a twin dragon.  By starting with two V-shapes back-to-back you can tile two dragon curves together.  The two curves perfectly interlock at every iteration.

You can view the example here.


fractal-twin-dragon-d3-colors.png

Example 4 - Twin Dragon w/ 4 Colors

This example is exactly the same as the previous one except it is initiated with four lines all with distinct colors.

You can view the example here.



Drawing the Curves in D3

The first thing to notice on the code is that the dragon.js file is returning an object I am simply calling  "VIZ".  The VIZ object has all the main methods needed to draw the dragon curves. I would take a look at that file first and then see how that object is used in each of the HTML files.

In the HTML for each of the examples the code begins by loading the initial lines that start the visualization:

// FROM dragons-single.html
VIZ.start = function () {
VIZ.clear();
VIZ.add({id: VIZ.id(), x1: 30, y1: 30, x2: 50, y2: 50, c: '#003366'});
VIZ.add({id: VIZ.id(), x1: 70, y1: 30, x2: 50, y2: 50, c: '#003366'});
VIZ.drawLines();
}

VIZ.start();

We put these lines in a VIZ.start function so they can be re-run when the user presses the "Back to Beginning" button.  Each time VIZ.start executes it clears all the lines in the "lines" array, inserts the initial starting line segments and draws those lines.  The two lines with VIZ.add are sending an object with the coordinates for the first two lines which, in this case, form a V on the screen.  In these examples, the SVG is 100px by 100px and gets resized dynamically when the browser window changes.  I just did some quick layout work and plotted the V to be centered a little above the middle of the SVG.

Other than the x1, y1, x2, y2 coordinates there are two other properties in the object passed to VIZ.add: an id and a color (c).  The id calls to a simple function (VIZ.id) that just returns a unique number.  We need that id because the D3 portion of the code uses the id to know whether a line segment is new or has been removed and needs to "exit" (there is a transition added to the elements).  The color (c) can be any hex number and will be inherited by all the future line segments that are descendents of that line.

After the initial lines are inserted and drawn to the screen, we can iterate the visualization on an interval.  The code for that follows immediately after calling VIZ.start() in each of the HTML files.  The interval code looks like this:

VIZ.interval = setInterval(function () {
VIZ.iterate();
VIZ.drawLines();
VIZ.count++;
if (VIZ.count >= 13) {
clearInterval(VIZ.interval);
}
}, 1000);

By making the interval a variable we can call clearInterval(VIZ.interval) at anytime to stop the process.  You can see this is used in the JQuery code for the buttons in each of the HTML files.  In this case we want to step through 13 iterations and then stop. 

Each iteration doubles the number of lines in the array and so you have to be careful because at some point the browser will blow up because there are too many SVG elements to handle (Chrome seems to be the best in this regard).  I chose the number 13 because that number of iterations seemed to run smoothly in the browsers on my machine (Chrome, FireFox, and Safari). The user can choose to do more iterations by pressing the "Iterate Once" button as much as they want, but eventually the browser will freeze.

Iterating the Dragon Curve

Cycling over the array of lines

Cycling over the array of lines

This is where things get interesting.  The basic process is shown in the image at the left.  The idea is that each time the VIZ.iterate function is called the "lines" array is cycled over and each existing line in the drawing is processed.  This is done by using the "pop" method to (1) pull each line segment off the end of the array, (2) derive the two new line segments, (3) insert those into the beginning of the array using the "unshift" method and then (4) transition out the original line segment. 

The majority of the work is happening in these three functions from the dragon.js file:

function rotatePoint(point, center, radians) {
var cos = Math.cos(radians), sin = Math.sin(radians);
return {
x: center.x + cos * (point.x - center.x) - sin * (point.y - center.y),
y: center.y + sin * (point.x - center.x) + cos * (point.y - center.y)
};
}

function findIntersection(p1, p2, p3, p4) {
var t1, t2, t3, t4, t5, t6, nx, ny;
t1 = (p1.x * p2.y - p1.y * p2.x);
t2 = (p3.x * p4.y - p3.y * p4.x);
t3 = (p1.x - p2.x);
t4 = (p3.y - p4.y);
t5 = (p1.y - p2.y);
t6 = (p3.x - p4.x);
nx = (t1 * t6 - t3 * t2)/(t3 * t4 - t5 * t6);
ny = (t1 * t4 - t5 * t2)/(t3 * t4 - t5 * t6);
return {x: nx, y: ny};
}

VIZ.iterate = function () {
var len = lines.length, line, p1, p2, mp, r1, r2, cp;
for (var i = 0; i < len; i++){
line = lines.pop();
p1 = {x: line.x1, y: line.y1};
p2 = {x: line.x2, y: line.y2};
mp = {x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2};
r1 = rotatePoint(mp, {x: p1.x, y: p1.y}, 0.785398163);
r2 = rotatePoint(mp, {x: p2.x, y: p2.y}, -0.785398163);
cp = findIntersection(p1, r1, p2, r2);
lines.unshift({id: VIZ.id(), x1: p1.x, y1: p1.y, x2: cp.x, y2: cp.y, c: line.c});
lines.unshift({id: VIZ.id(), x1: p2.x, y1: p2.y, x2: cp.x, y2: cp.y, c: line.c});
}
}

The process of figuring out the coordinates of the two new lines is a little tricky but the graphic below shows what is happening in the VIZ.iterate function in each loop (each line segment):

dragon-curve-iteration.jpg

That covers the bulk of the code for this tutorial.  If you want to understand how the SVG resizes with the browser, look at the previous post on fractal ferns.  There is discussion of how that works at the end of the article. The only thing left to show for this example is what is happening when D3 renders the SVG.  Here is the VIZ.drawLines function:

  VIZ.drawLines = function () {
svg.selectAll(".lineSegment")
.data(lines, function (d) { return d.id; })
.enter()
.append("line")
.attr("class", "lineSegment")
.attr("x1", function (d) { return d.x1; })
.attr("y1", function (d) { return d.y1; })
.attr("x2", function (d) { return d.x2; })
.attr("y2", function (d) { return d.y2; })
.style("stroke", function (d) { return d.c; });

svg.selectAll(".lineSegment")
.data(lines, function (d) { return d.id; })
.exit()
.style("stroke", "#3399cc")
.transition()
.delay(500)
.duration(500)
.style("opacity", 0)
.remove();
}

Anytime VIZ.drawLines() is called D3 renders the current content of the "lines" array.  It uses the id of each line segment to know whether the segment is entering or exiting.

That's it.  You've got yourself a dragon curve.


Viewing all articles
Browse latest Browse all 26

Latest Images

Trending Articles





Latest Images