Skip to content

Commit 3706e76

Browse files
committed
Added offset_polygon() and offset_points() to utils. Both depend on PyEuclid and are relatively naive approaches to offset point sets; they can't handle curves and will make stupid mistakes with non-convex shapes.
1 parent 5b2338d commit 3706e76

File tree

3 files changed

+382
-52
lines changed

3 files changed

+382
-52
lines changed

setup.py

100644100755
File mode changed.

solid/screw_thread.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
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

Comments
 (0)