Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f37e762

Browse files
Add files via upload
1 parent be2570c commit f37e762

File tree

1 file changed

+196
-0
lines changed

1 file changed

+196
-0
lines changed

‎pythagorean tree.py‎

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
2+
# coding: utf-8
3+
4+
# In[1]:
5+
6+
7+
#fractal is one of the interesting topics in geometry
8+
#it is usually described by a recursive function
9+
#voila,here we are!
10+
import matplotlib.pyplot as plt
11+
12+
13+
# In[2]:
14+
15+
16+
#compute euclidean distance
17+
def euclidean_distance(point1,point2):
18+
return ((point1[0]-point2[0])**2+(point1[1]-point2[1])**2)**0.5
19+
20+
21+
# In[3]:
22+
23+
24+
#simple solution to get coefficients of the equation
25+
def get_line_params(x1,y1,x2,y2):
26+
27+
slope=(y1-y2)/(x1-x2)
28+
intercept=y1-slope*x1
29+
30+
return slope,intercept
31+
32+
33+
# In[4]:
34+
35+
36+
#standard solution to quadratic equation
37+
def solve_quadratic_equation(A,B,C):
38+
x1=(-B+(B**2-4*A*C)**0.5)/(2*A)
39+
x2=(-B-(B**2-4*A*C)**0.5)/(2*A)
40+
return [x1,x2]
41+
42+
43+
# In[5]:
44+
45+
46+
#analytic geometry to compute target datapoints
47+
def get_datapoint(pivot,measure,length,direction='inner'):
48+
49+
#for undefined slope
50+
if pivot[0]==measure[0]:
51+
y1=pivot[1]+length
52+
y2=pivot[1]-length
53+
x1=pivot[0]
54+
x2=pivot[0]
55+
56+
#for general cases
57+
else:
58+
59+
#get line equation
60+
slope,intercept=get_line_params(pivot[0],pivot[1],
61+
measure[0],measure[1],)
62+
63+
#solve quadratic equation
64+
A=1
65+
B=-2*pivot[0]
66+
C=pivot[0]**2-length**2/(slope**2+1)
67+
x1,x2=solve_quadratic_equation(A,B,C)
68+
69+
#get y from line equation
70+
y1=slope*x1+intercept
71+
y2=slope*x2+intercept
72+
73+
if direction=='inner':
74+
75+
#take the one between pivot and measure points
76+
datapoint=min([(x1,y1),(x2,y2)],
77+
key=lambda x:euclidean_distance(x,measure))
78+
else:
79+
80+
#take the one farther away from measure points
81+
datapoint=max([(x1,y1),(x2,y2)],
82+
key=lambda x:euclidean_distance(x,measure))
83+
84+
return datapoint
85+
86+
87+
# In[6]:
88+
89+
90+
#recursively plot symmetric pythagorean tree at 45 degree
91+
# https://larryriddle.agnesscott.org/ifs/pythagorean/pythTree.htm
92+
def pythagorean_tree(ax,top_left,top_right,bottom_left,
93+
bottom_right,current_angle,line_len,n):
94+
95+
#plot square
96+
ax.add_patch(plt.Rectangle(xy=bottom_left,width=line_len,height=line_len,
97+
angle=current_angle,))
98+
99+
if n==0:
100+
return
101+
else:
102+
103+
#find mid point
104+
#midpoint has to satisfy two conditions
105+
#it has to be on the same line as bottom_left and bottom_right
106+
#assume this line follows y=kx+b
107+
#the midpoint is (x,kx+b)
108+
#bottom_left is (α,kα+b),bottom_right is (δ,kδ+b)
109+
#the euclidean distance between midpoint and bottom_left should be
110+
#half of the euclidean distance between bottom_left and bottom_right
111+
#(x-α)**2+(kx+b-kα-b)**2=((α-δ)**2+(kα+b-kδ-b)**2)/4
112+
#apart from x,everything else in the equation is constant
113+
#this forms a simple quadratic solution to get two roots
114+
#one root would be between bottom_left and bottom_right which yields midpoint
115+
#and the other would be farther away from bottom_right
116+
#this function solves the equation via (-B+(B**2-4*A*C)**0.5)/(2*A)
117+
#alternatively,you can use scipy.optimize.root
118+
#the caveat is it does not offer both roots
119+
#a wrong initial guess could take you to the wrong root
120+
bottom_mid=get_datapoint(bottom_left,bottom_right,line_len/2)
121+
top_mid=get_datapoint(top_left,top_right,line_len/2)
122+
123+
#compute the top point of a triangle
124+
#the computation is similar to midpoint
125+
#the euclidean distance between triangle_top and top_mid should be
126+
#half of the distance between top_mid and bottom_mid
127+
triangle_top=get_datapoint(top_mid,bottom_mid,
128+
line_len/2,direction='outer')
129+
130+
#get top left for right square
131+
#the computation is similar to midpoint
132+
#the euclidean distance between triangle_top and rightsq_topleft
133+
#should be the same as the distance between triangle_top and top_left
134+
rightsq_topleft=get_datapoint(triangle_top,top_left,
135+
line_len/(2**0.5),direction='outer')
136+
137+
#get midpoint of the diagonal between rightsq_topleft and top_right
138+
#the computation is similar to midpoint
139+
#the euclidean distance between rightsq_diag_mid and rightsq_topleft
140+
#should be half of the distance between rightsq_topleft and top_right
141+
rightsq_diag_mid=get_datapoint(top_right,rightsq_topleft,line_len/2)
142+
rightsq_topright=get_datapoint(rightsq_diag_mid,triangle_top,
143+
line_len/2,direction='outer')
144+
145+
#get top left and right for left square similar to right square
146+
leftsq_topleft=get_datapoint(triangle_top,rightsq_topright,
147+
line_len,direction='outer')
148+
leftsq_topright=get_datapoint(triangle_top,top_right,
149+
line_len/(2**0.5),direction='outer')
150+
151+
#recursive do the same for left square
152+
pythagorean_tree(ax,leftsq_topleft,leftsq_topright,
153+
top_left,triangle_top,current_angle+45,
154+
line_len/(2**0.5),n-1)
155+
156+
#recursive do the same for right square
157+
pythagorean_tree(ax,rightsq_topleft,rightsq_topright,
158+
triangle_top,top_right,current_angle-45,
159+
line_len/(2**0.5),n-1)
160+
161+
162+
# In[7]:
163+
164+
165+
#initialize
166+
top_left=(0,0)
167+
top_right=(1,0)
168+
bottom_left=(0,-1)
169+
bottom_right=(1,-1)
170+
n=5
171+
current_angle=0
172+
line_len=euclidean_distance(top_left,top_right)
173+
174+
175+
# In[8]:
176+
177+
178+
#viz
179+
ax=plt.figure(figsize=(10,8)).add_subplot(111)
180+
pythagorean_tree(ax,top_left,top_right,bottom_left,
181+
bottom_right,current_angle,line_len,n)
182+
183+
#limit figure dimension for better viz
184+
plt.ylim(min([bottom_left[1],bottom_right[1]]),
185+
max([top_left[1],top_right[1]])+euclidean_distance(
186+
top_left,top_right)*(n-2)
187+
)
188+
plt.xlim(min([bottom_left[0],top_left[0]])-euclidean_distance(
189+
top_left,top_right)*(n-1)/2,
190+
max([bottom_right[0],top_right[0]])+euclidean_distance(
191+
top_left,top_right)*(n-1)/2,
192+
)
193+
194+
plt.axis('off')
195+
plt.show()
196+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /