algorithmsvgdrawingfractals

How do I draw this shape in SVG?


I've been trying to think of an elegant algorithm to draw this with SVG and it eludes me.

Drawing just the wire-frame is pretty easy. It's just lines from each corner to the edge on the right side from it, where the target points are equally spaced. But filling them is trickier - I need the actual polygon coordinates to draw the shapes filled... don't I?

One way would be to solve all the segment intersections with a bit of math. That would give me the coordinates of all intersections, but then how do I know how to groups of four coordinates, and also keep track of which ones to fill?


Solution

  • Your picture can be divided in 4 equal parts, which are point-symetric, except for a swap of black and white tiles. To calculate the bottom quadrant, for example, you iterate over all the lines starting in the left-bottom corner (x1, y1) and go towards the right edge (x2, y2), then iterate over all lines that can go from the top-left corner (x3, y3) towards the bottom edge (x4, y4), calculate the intersection points and save those in matrices Px and Py. I was too lazy to do the math, so I just typed over the formula for line intersections. Finally, iterate over the matrix and plot a patch between neighboring points, if the sum of the indices ix and iy is odd.

    Example using Python/matplotlib:

    from __future__ import division
    import matplotlib.pyplot as plt
    import numpy as np
    
    def intersect(x1, y1, x2, y2, x3, y3, x4, y4):
        det = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4)
        px = ((x1*y2-y1*x2)*(x3-x4) - (x1-x2)*(x3*y4-y3*x4)) / det
        py = ((x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4)) / det
        return px, py
    
    n = 10
    Px = np.zeros((n+1, n+1))
    Py = np.zeros((n+1, n+1))
    
    x1, y1 = 0, 0
    x2 = 1
    x3, y3 = 0, 1
    y4 = 0
    for ix in range(n+1): # index left to right along bottom
        x4 = ix / n
        for iy in range(n+1): # index bottom to top along right side
            y2 = iy / n
            px, py = intersect(x1, y1, x2, y2, x3, y3, x4, y4)
            plt.plot([x1,x2], [y1,y2], 'k')
            plt.plot([x3,x4], [y3,y4], 'k')
            plt.plot(px, py, '.r', markersize=10, zorder=3)
            Px[ix, iy] = px
            Py[ix, iy] = py
    
    for ix in range(n):
        for iy in range(n):
            if (ix + iy) % 2: # only plot if sum is odd
                xy = [[Px[ix, iy], Py[ix, iy]], # rectangle of neighboring points
                      [Px[ix, iy+1], Py[ix, iy+1]],
                      [Px[ix+1, iy+1], Py[ix+1, iy+1]],
                      [Px[ix+1, iy], Py[ix+1, iy]]]
                poly = plt.Polygon(xy,facecolor='gray',edgecolor='none')
                plt.gca().add_patch(poly)
    plt.show()   
    

    This code might be optimized a bit more, but like this it should be reasonably clear what it does.

    Result: pattern Extending this to all 4 quadrants and writing this as a SVG-file is left as an exercise for the reader :).