三角形填充和分形練習
阿新 • • 發佈:2018-12-31
畫出一個六角星,以六角星的每一個小三角形兩條邊上的三等分點作一個新的等邊三角形,然後遞迴的做下去,並且圖形的內部必須填充顏色。
[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]<p1[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')