1+ #! /usr/bin/python
2+ # -*- coding: UTF-8 -*-
3+ import os , sys , re
4+
5+ sys .path .append (os .path .join ( os .getenv ('HOME' ), 'Desktop' , 'SolidPython' ))
6+
7+ from pyopenscad import *
8+ from sp_utils import *
9+
10+ sys .path .append ( os .path .join ( os .getenv ('HOME' ), 'Desktop' ,'pyeuclid' ))
11+ from euclid import *
12+
13+ ONE_THIRD = 1 / 3.0
14+
15+ def kochify_3d ( a , b , c ,
16+ ab_weight = 0.5 , bc_weight = 0.5 , ca_weight = 0.5 ,
17+ pyr_a_weight = ONE_THIRD , pyr_b_weight = ONE_THIRD , pyr_c_weight = ONE_THIRD ,
18+ pyr_height_weight = ONE_THIRD
19+ ):
20+ '''
21+ Point3s a, b, and c must be coplanar and define a face
22+ ab_weight, etc define the subdivision of the original face
23+ pyr_a_weight, etc define where the point of the new pyramid face will go
24+ pyr_height determines how far from the face the new pyramid's point will be
25+ '''
26+ triangles = []
27+ new_a = a .affine_combination ( b , ab_weight )
28+ new_b = b .affine_combination ( c , bc_weight )
29+ new_c = c .affine_combination ( a , ca_weight )
30+
31+ triangles .extend ( [[a , new_a , new_c ], [b , new_b , new_a ], [c , new_c , new_b ]])
32+
33+ avg_pt_x = a .x * pyr_a_weight + b .x * pyr_b_weight + c .x * pyr_c_weight
34+ avg_pt_y = a .y * pyr_a_weight + b .y * pyr_b_weight + c .y * pyr_c_weight
35+ avg_pt_z = a .z * pyr_a_weight + b .z * pyr_b_weight + c .z * pyr_c_weight
36+
37+ center_pt = Point3 ( avg_pt_x , avg_pt_y , avg_pt_z )
38+
39+ # The top of the pyramid will be on a normal
40+ ab_vec = b - a
41+ bc_vec = c - b
42+ ca_vec = a - c
43+ normal = ab_vec .cross ( bc_vec ).normalized ()
44+ avg_side_length = (abs (ab_vec ) + abs (bc_vec ) + abs (ca_vec ))/ 3
45+ pyr_h = avg_side_length * pyr_height_weight
46+ pyr_pt = LineSegment3 ( center_pt , normal , pyr_h ).p2
47+
48+
49+ triangles .extend ([[new_a , pyr_pt , new_c ], [new_b , pyr_pt , new_a ], [new_c , pyr_pt , new_b ]])
50+
51+ return triangles
52+
53+
54+ def kochify ( seg , height_ratio = 0.33 , left_loc = 0.33 , midpoint_loc = 0.5 , right_loc = 0.66 ):
55+ a , b = seg .p1 , seg .p2
56+ l = a .affine_combination ( b , left_loc )
57+ c = a .affine_combination ( b , midpoint_loc )
58+ r = a .affine_combination ( b , right_loc )
59+ # The point of the new triangle will be height_ratio * abs(seg) long,
60+ # and run perpendicular to seg, through c.
61+ perp = seg .v .cross ().normalized ()
62+
63+ c_height = height_ratio * abs (seg )
64+ perp_pt = LineSegment2 ( c , perp , - c_height ).p2
65+
66+ # For the moment, assume perp_pt is on the right side of seg.
67+ # Will confirm this later if needed
68+ return [ LineSegment2 ( a , l ),
69+ LineSegment2 ( l , perp_pt ),
70+ LineSegment2 ( perp_pt , r ),
71+ LineSegment2 ( r , b )]
72+
73+ def main_3d ():
74+ gens = 4
75+
76+ # Parameters
77+ ab_weight = 0.5
78+ bc_weight = 0.5
79+ ca_weight = 0.5
80+ pyr_a_weight = ONE_THIRD
81+ pyr_b_weight = ONE_THIRD
82+ pyr_c_weight = ONE_THIRD
83+ pyr_height_weight = ONE_THIRD
84+ pyr_height_weight = ONE_THIRD
85+ # pyr_height_weight = .25
86+
87+ all_polys = union ()
88+
89+ # setup
90+ ax , ay , az = 100 , - 100 , 100
91+ bx , by , bz = 100 , 100 ,- 100
92+ cx , cy , cz = - 100 , 100 , 100
93+ dx , dy , dz = - 100 , - 100 , - 100
94+ generations = [ [[ Point3 ( ax , ay , az ), Point3 ( bx , by , bz ), Point3 ( cx , cy , cz )],
95+ [ Point3 ( bx , by , bz ), Point3 ( ax , ay , az ), Point3 ( dx , dy , dz )],
96+ [ Point3 ( ax , ay , az ), Point3 ( cx , cy , cz ), Point3 ( dx , dy , dz )],
97+ [ Point3 ( cx , cy , cz ), Point3 ( bx , by , bz ), Point3 ( dx , dy , dz )],
98+ ]
99+ ]
100+
101+ # Recursively generate snowflake segments
102+ for g in range (1 , gens ):
103+ generations .append ([])
104+ for a , b , c in generations [g - 1 ]:
105+ new_tris = kochify_3d ( a , b , c ,
106+ ab_weight , bc_weight , ca_weight ,
107+ pyr_a_weight , pyr_b_weight ,pyr_c_weight ,
108+ pyr_height_weight )
109+ # new_tris = kochify_3d( a, b, c)
110+ generations [g ].extend ( new_tris )
111+
112+ # Put all generations into SCAD
113+ orig_length = abs ( generations [0 ][0 ][1 ] - generations [0 ][0 ][0 ])
114+ for g , a_gen in enumerate (generations ):
115+ # Move each generation up in y so it doesn't overlap the others
116+ h = orig_length * 1.5 * g
117+
118+ # Build the points and triangles arrays that SCAD needs
119+ tris = []
120+ points = []
121+ for a ,b ,c in a_gen :
122+ points .extend ([ [a .x , a .y , a .z ], [b .x , b .y , b .z ], [c .x , c .y , c .z ]])
123+ t = len (points )
124+ tris .append ([t - 3 , t - 2 , t - 1 ])
125+
126+ # Do the SCAD
127+ edges = [range (len (points ))]
128+ all_polys .add ( up ( h )(
129+ polyhedron ( points , tris )
130+ )
131+ )
132+
133+ scad_render_to_file ( all_polys , os .path .join ( os .getenv ('HOME' ), 'Desktop' , 'koch_3d.scad' ))
134+
135+ def main ():
136+ # Parameters
137+ midpoint_weight = 0.5
138+ height_ratio = 0.25
139+ left_loc = ONE_THIRD
140+ midpoint_loc = 0.5
141+ right_loc = 2 * ONE_THIRD
142+ gens = 5
143+
144+ # Results
145+ all_polys = union ()
146+
147+ # setup
148+ ax , ay = 0 , 0
149+ bx , by = 100 , 0
150+ cx , cy = 50 , 86.6
151+ base_seg1 = LineSegment2 ( Point2 ( ax , ay ), Point2 ( cx , cy ))
152+ base_seg2 = LineSegment2 ( Point2 ( cx , cy ), Point2 ( bx , by ))
153+ base_seg3 = LineSegment2 ( Point2 ( bx , by ), Point2 ( ax , ay ))
154+ generations = [[base_seg1 , base_seg2 , base_seg3 ]]
155+
156+
157+ # Recursively generate snowflake segments
158+ for g in range (1 , gens ):
159+ generations .append ([])
160+ for seg in generations [g - 1 ]:
161+ generations [g ].extend ( kochify ( seg , height_ratio , left_loc , midpoint_loc , right_loc ))
162+ # generations[g].extend( kochify( seg))
163+
164+ # # Put all generations into SCAD
165+ orig_length = abs ( generations [0 ][0 ])
166+ for g , a_gen in enumerate (generations ):
167+ points = [s .p1 for s in a_gen ]
168+ # points.append( a_gen[-1].p2) # add the last point
169+
170+ rect_offset = 10
171+
172+ # Just use arrays for points so SCAD understands
173+ points = [[p .x , p .y ] for p in points ]
174+
175+ # Move each generation up in y so it doesn't overlap the others
176+ h = orig_length * 1.5 * g
177+
178+ # Do the SCAD
179+ edges = [range (len (points ))]
180+ all_polys .add ( forward ( h )( polygon (points = points , paths = edges )))
181+
182+ scad_render_to_file ( all_polys , os .path .join ( os .getenv ('HOME' ), 'Desktop' , 'koch.scad' ))
183+
184+ if __name__ == '__main__' :
185+ main_3d ()
186+ main ()
0 commit comments