1+ #! /usr/bin/python
2+ # -*- coding: utf-8 -*-
3+ import os , sys , re
4+
5+ from solid import *
6+ from solid .utils import *
7+ from euclid import *
8+
9+ def thread ( outline_pts , inner_rad , pitch , length , segments_per_rot = 32 ,
10+ neck_in_degrees = 0 , neck_out_degrees = 0 ):
11+ '''
12+ Sweeps outline_pts (an array of points describing a closed polygon in XY)
13+ through a spiral.
14+
15+ This is done by creating and returning one huge polyhedron, with potentially
16+ thousands of faces. An alternate approach would make one single polyhedron,
17+ then repeat it over and over in the spiral shape, unioning them all together.
18+ This would create a similar number of SCAD objects and operations, but still
19+ require a lot of transforms and unions to be done in the SCAD code rather than
20+ in the python, as here. Also would take some doing to make the neck-in work
21+ as well. Not sure how the two approaches compare in terms of render-time.
22+ -ETJ 16 Mar 2011
23+
24+ outline_pts: a list of points (NOT an OpenSCAD polygon) that define the cross
25+ section of the thread
26+
27+ inner_rad:
28+ length:
29+ segments_per_rot:
30+ neck_in_degrees:
31+ neck_out_degrees:
32+ '''
33+ a = union ()
34+ rotations = float (length )/ pitch
35+
36+ total_angle = 360.0 * rotations
37+ up_step = float (length ) / (rotations * segments_per_rot )
38+ total_steps = int (ceil ( rotations * segments_per_rot ))
39+ step_angle = total_angle / total_steps
40+
41+ all_points = []
42+ all_tris = []
43+ euc_up = Vector3 ( * UP_VEC )
44+ poly_sides = len ( outline_pts )
45+
46+ # Figure out how wide the tooth profile is
47+ min_bb , max_bb = bounding_box ( outline_pts )
48+ outline_w = max_bb [0 ] - min_bb [0 ]
49+
50+ min_rad = max ( 0 , inner_rad - outline_w - EPSILON )
51+
52+ # outline_pts, since they were created in 2D , are in the XY plane.
53+ # But spirals move a profile in XZ around the Z-axis. So swap Y and Z
54+ # co-ords... and hope users know about this
55+ # Also add inner_rad to the profile
56+ euc_points = []
57+ for p in outline_pts :
58+ # If p is in [x, y] format, make it [x, y, 0]
59+ if len ( p ) == 2 :
60+ p .append ( 0 )
61+ # [x, y, z] => [ x+inner_rad, z, y]
62+ s = Point3 ( p [0 ], p [2 ], p [1 ]) # adding inner_rad, swapping Y & Z
63+ euc_points .append ( s )
64+
65+ for i in range ( total_steps ):
66+ angle = i * step_angle
67+ elevation = i * up_step
68+ if angle > total_angle :
69+ angle = total_angle
70+ elevation = length
71+
72+ rad = inner_rad
73+ if angle < neck_in_degrees :
74+ rad = min_rad + angle / neck_in_degrees * outline_w
75+ elif angle > total_angle - neck_out_degrees :
76+ rad = min_rad + (total_angle - angle )/ neck_out_degrees * outline_w
77+
78+ elev_vec = Vector3 ( rad , 0 , elevation )
79+
80+ for p in euc_points :
81+ pt = (p + elev_vec ).rotate_around ( axis = euc_up , theta = radians ( angle ))
82+ all_points .append ( pt .as_arr ())
83+
84+ # Add the connectivity information
85+ if i < total_steps - 1 :
86+ ind = i * poly_sides
87+ for j in range ( ind , ind + poly_sides - 1 ):
88+ all_tris .append ( [ j , j + poly_sides , j + 1 ])
89+ all_tris .append ( [ j + 1 , j + poly_sides , j + poly_sides + 1 ])
90+ all_tris .append ( [ ind + poly_sides - 1 , ind + poly_sides - 1 + poly_sides , ind ])
91+ all_tris .append ( [ ind , ind + poly_sides - 1 + poly_sides , ind + poly_sides ])
92+
93+ # End triangle fans for beginning and end
94+ last_loop = len (all_points ) - poly_sides
95+ for i in range ( poly_sides - 2 ):
96+ all_tris .append ( [ 0 , i + 1 , i + 2 ])
97+ all_tris .append ( [ last_loop , last_loop + i + 2 , last_loop + i + 1 ])
98+
99+
100+ # Make the polyhedron
101+ a = polyhedron ( points = all_points , triangles = all_tris )
102+
103+ # Subtract the center, to remove the neck-in pieces
104+ # subtract above and below to make sure the entire screw fits within height 'length'
105+ cube_side = 2 * (inner_rad + EPSILON + outline_w )
106+ subs = union ()(
107+ cylinder ( inner_rad , length ),
108+ down ( cube_side / 2 )( cube ( cube_side , center = True )),
109+ up ( cube_side / 2 + length )( cube ( cube_side , center = True ))
110+ )
111+ return render ()(a - subs )
112+
113+ def default_thread_section ( tooth_height , tooth_depth ):
114+ return [ [ 0 , - tooth_height / 2 ],
115+ [ tooth_depth , 0 ],
116+ [ 0 , tooth_height / 2 ]
117+ ]
118+
119+
120+ def assembly ():
121+ # Scad code here
122+ a = union ()
123+
124+ rad = 5
125+ pts = [ [ 0 , - 1 , 0 ],
126+ [ 1 , 0 , 0 ],
127+ [ 0 , 1 , 0 ],
128+ [ - 1 , 0 , 0 ],
129+ [ - 1 , - 1 , 0 ] ]
130+
131+ a = thread ( pts , inner_rad = 10 , pitch = 6 , length = 2 , segments_per_rot = 31 ,
132+ neck_in_degrees = 30 , neck_out_degrees = 30 )
133+
134+ return a + cylinder ( 10 + EPSILON , 2 )
135+
136+ if __name__ == '__main__' :
137+ a = assembly ()
138+ scad_render_to_file ( a )
0 commit comments