三角形填充和分形练习

三角形填充和分形练习_第1张图片

画出一个六角星,以六角星的每一个小三角形两条边上的三等分点作一个新的等边三角形,然后递归的做下去,并且图形的内部必须填充颜色。

[0]:首先考虑如何画出一个六角星,基本方法是:先画一个三角形,然后对每一条边的两个三等分点求出第三个顶点,这样以新的三个点作一个三角形和原来的三角形合成在一起就得到了六角星。

[1]:接下来考虑如何对每一个小的三角形进行分形:我们以一个三角形的三个顶点作为参数,我们只需要其中的两条边(每条边由两个点组成),然后求出三等分点,根据两个三等分点(x1,y1),(x2,y2),根据一定的公式显然可以计算出第三个点,关键是第三个点有两个方向,我们可以这样做:计算两个可能的顶点和三角形另一个点的距离,显然距离较大的就是我们要找的顶点。这样就可以成功确定一个新的三角形。

[2]:三角形填充

我们不用相应的API去画三角形,而是自己实现。给定任意一个三角形,我们将三个顶点按照y坐标进行排序,然后再按照x坐标进行排序,考虑利用直线进行填充的基本单位,我们从顶点开始,往下不断用直线进行填充,具体的算法可以参考:https://zhuanlan.zhihu.com/p/20148016

注意要处理一些可能的corner case,比如y坐标可能相等,x坐标可能相等,这样就可能出现0除错误

[3]:最终我们确定整体框架,传入一个三角形的三个顶点,返回另外三个顶点的坐标,然后对六个小三角形开始进行分型处理,每生成一个新的三角形,就利用相应的填充算法进行填充。

code:

三角形填充的实现,代码丑陋,还没想好怎么优雅的处理一些corner case.直觉上应该处理为画一条直线的函数和计算直线坐标的函数,这样可以使得框架清晰(to do)

points = defaultdict(lambda:' ')
def triangle(p1,p2,p3):
    p1,p2,p3 = sorted((p1,p2,p3),key = lambda p:p[1])#sort by y 
    sign = 1 if p2[0]0] else -1
    d = abs((p1[1]-p2[1])/(p1[1]-p3[1]))
    M = ((1-d)*p1[0]+d*p3[0],p2[1])
    _y = int(p2[1] - p1[1])

    for y in range(int(p1[1]),int(p2[1]+1)):
        if _y==0:
            for x in range(int(p1[0]),int(p2[0]+1)):
                points[(x,p1[1])] = '*'
            break

        dx = p1[0]-p2[0]
        dy = y-p1[1]
        dxx = dy*dx/_y
        dx2 = M[0] - p1[0]
        dxx2 = dy*dx2/_y
        for x in range(int(p1[0]-dxx),int(p1[0]+dxx2+sign),sign):
            points[(x,y)] = '*'
    _y = int(p3[1] - p2[1])
    for y in range(int(p2[1]),int(p3[1]+1)):
        if _y==0:
            for x in range(int(p2[0]),int(p3[0])+1):
                points[(x,p3[1])] = '*'
            break

        dx = p3[0] - M[0]
        dy = p3[1]-y
        dxx = dx*dy/_y
        dx2 = p3[0]-p2[0]
        dxx2 = dx2*dy/_y
        for x in range(int(p3[0]-dxx2),int(p3[0]-dxx+sign),sign):
            points[(x,y)] = '*'

分形:由于这个图形是我自己瞎编的,参考了koch雪花,所以函数命名叫koch.这里考虑两种情况,1:要对三条边进行计算获得 2:只对两条边进行计算。这里应该可以处理为对每一条边进行计算,然后把3条和两条拆开成两个函数更加清晰。(to do)

def koch(p1,p2,p3,n,flag = True):
    triangle(p1,p2,p3)
    p = {(p1,p2),(p2,p3),(p1,p3)} if flag else {(p1,p3),(p2,p3)}
    d = 1/3
    all = {p1,p2,p3}
    other = []
    if n>1:
        for (_p1,_p2) in p:
            (x1,y1),(x2,y2) = _p1,_p2
            x4 = (1-d)*x1+d*x2
            y4 = (1-d)*y1+d*y2
            x5 = d*x1+(1-d)*x2
            y5 = d*y1+(1-d)*y2
            (x3,y3) = tuple(all-{_p1,_p2})[0]

            x6,y6 = get_point(x4,y4,x5,y5,x3,y3)
            other.append((x6,y6))
            koch((x4,y4),(x5,y5),(x6,y6),n-1,False)
        x7 = (1-d)*p3[0]+d*p1[0]
        y7 = (1-d)*p3[1]+d*p1[1]
        x8 = (1-d)*p3[0]+d*p2[0]
        y8 = (1-d)*p3[1]+d*p2[1]
        koch((x7,y7),(x8,y8),p3,n-1,False)
    if flag:
        return other

def get_point(x1,y1,x2,y2,x,y):
    mx,my = (x1+x2)/2,(y1+y2)/2
    d1 = ((my-y1)**2+(mx-x1)**2)**(0.5)
    k = (y2-y1)/(x2-x1)
    dis,x3,y3 = 0,0,0
    for sign in (-1,1):
        b = my+sign*(3**0.5)*d1/(1+k**2)**(0.5)
        a = mx-(b-my)*k
        dis,x3,y3 = max((dis,x3,y3),(((x-a)**2+(y-b)**2)**0.5,a,b))
    return x3,y3

填充:

from PIL import Image,ImageDraw
image = Image.new('RGB', (width, height), (255, 255, 255))
draw = ImageDraw.Draw(image)
for x in range(width):
    for y in range(height):
        if (x,y) in points:
            draw.point((x, y), fill=(0,0,0))
image.save('code.jpg', 'jpeg')

结果展示:

三角形填充和分形练习_第2张图片

你可能感兴趣的:(常用算法,Python)