문제

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?

도움이 되었습니까?

해결책

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 :).

다른 팁

Possible solution:

We can look at each black-filled cell as an intersection of two triangles. The triangles fan out from each corner. So if we have a function to calculate a polygon for the intersection of two triangles, all we need to do is iterate over all the intersections of all triangles (that actually intersect). The decision to color a triangle black or not is then basically a parity check. So if we have four sides: A, B, C, D and N triangles fanning out from each side, then the intersection of Aj and Bk should be black if j*k is an odd number.

You don't have to calculate the intersection points. You can use the SVG feature fill-rule="evenodd" which creates holes when a shape overlaps itself.

Here is an example graphic similar to your question:

<svg width="400" height="400" viewBox="0 0 10 10">
    <path d="M 0,0 L 10,10 9,10 0,0 8,10 7,10 0,0 6,10 5,10 0,0 4,10 3,10 0,0 2,10 1,10 0,0
    0,10 10,0 10,1 0,10 10,2 10,3 0,10 10,4 10,5 0,10 10,6 10,7 0,10 10,8 10,9 0,10
    10,10 0,0 1,0 10,10 2,0 3,0 10,10 4,0 5,0 10,10 6,0 7,0 10,10 8,0 9,0 10,10
    10,0 0,10 0,9 10,0 0,8 0,7, 10,0 0,6 0,5 10,0 0,4 0,3 10,0 0,2 0,1 10,0 0,0" fill-rule="evenodd" fill="black"/>
</svg>

Of course you could also fill in the coordinates of the points on the edges programatically.

Here is how it will look:

SVG with fill-rule evenodd

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top