1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# -*- coding: utf-8 -*-
"""
$Id$
Copyright 2008-2010 Lode Leroy
Copyright 2010 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PyCAM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
from pycam.Geometry.utils import INFINITE, sqrt
from pycam.Geometry.Point import Point, Vector
from pycam.Geometry.intersection import intersect_circle_plane, \
intersect_circle_point, intersect_circle_line
from pycam.Cutters.BaseCutter import BaseCutter
try:
import OpenGL.GL as GL
import OpenGL.GLU as GLU
GL_enabled = True
except ImportError:
GL_enabled = False
class CylindricalCutter(BaseCutter):
def __init__(self, radius, **kwargs):
BaseCutter.__init__(self, radius, **kwargs)
self.axis = Vector(0, 0, 1)
def __repr__(self):
return "CylindricalCutter<%s,%s>" % (self.location, self.radius)
def get_shape(self, engine="ODE"):
if engine == "ODE":
import ode
import pycam.Physics.ode_physics as ode_physics
""" We don't handle the the "additional_distance" perfectly, since
the "right" shape would be a cylinder with a small flat cap that
grows to the full expanded radius through a partial sphere. The
following ascii art shows the idea:
| |
\_/
This slight incorrectness should be neglectable and causes no harm.
"""
additional_distance = self.get_required_distance()
radius = self.distance_radius
height = self.height + additional_distance
center_height = height / 2 - additional_distance
geom = ode.GeomTransform(None)
geom_drill = ode.GeomCylinder(None, radius, height)
geom_drill.setPosition((0, 0, center_height))
geom.setGeom(geom_drill)
geom.children = []
def reset_shape():
geom.children = []
def set_position(x, y, z):
geom.setPosition((x, y, z))
def extend_shape(diff_x, diff_y, diff_z):
reset_shape()
# see http://mathworld.wolfram.com/RotationMatrix.html
hypotenuse = sqrt(diff_x * diff_x + diff_y * diff_y)
# Some paths contain two identical points (e.g. a "touch" of
# the PushCutter) We don't need any extension for these.
if hypotenuse == 0:
return
cosinus = diff_x / hypotenuse
sinus = diff_y / hypotenuse
# create the cyclinder at the other end
geom_end_transform = ode.GeomTransform(geom.space)
geom_end_transform.setBody(geom.getBody())
geom_end = ode.GeomCylinder(None, radius, height)
geom_end.setPosition((diff_x, diff_y, diff_z + center_height))
geom_end_transform.setGeom(geom_end)
# create the block that connects to two cylinders at the end
rot_matrix_box = (cosinus, sinus, 0.0, -sinus, cosinus, 0.0,
0.0, 0.0, 1.0)
geom_connect_transform = ode.GeomTransform(geom.space)
geom_connect_transform.setBody(geom.getBody())
geom_connect = ode_physics.get_parallelepiped_geom(
(Point(-hypotenuse / 2, radius, -diff_z / 2),
Point(hypotenuse / 2, radius, diff_z / 2),
Point(hypotenuse / 2, -radius, diff_z / 2),
Point(-hypotenuse / 2, -radius, -diff_z / 2)),
(Point(-hypotenuse / 2, radius,
self.height - diff_z / 2),
Point(hypotenuse / 2,
radius, self.height + diff_z / 2),
Point(hypotenuse / 2, -radius,
self.height + diff_z / 2),
Point(-hypotenuse / 2, -radius,
self.height - diff_z / 2)))
geom_connect.setRotation(rot_matrix_box)
geom_connect.setPosition((hypotenuse / 2, 0, radius))
geom_connect_transform.setGeom(geom_connect)
# sort the geoms in order of collision probability
geom.children.extend([geom_connect_transform,
geom_end_transform])
geom.extend_shape = extend_shape
geom.reset_shape = reset_shape
self.shape[engine] = (geom, set_position)
return self.shape[engine]
def to_OpenGL(self):
if not GL_enabled:
return
GL.glPushMatrix()
GL.glTranslate(self.center.x, self.center.y, self.center.z)
if not hasattr(self, "_cylinder"):
self._cylinder = GLU.gluNewQuadric()
GLU.gluCylinder(self._cylinder, self.radius, self.radius, self.height,
10, 10)
if not hasattr(self, "_disk"):
self._disk = GLU.gluNewQuadric()
GLU.gluDisk(self._disk, 0, self.radius, 10, 10)
GL.glPopMatrix()
def moveto(self, location, **kwargs):
BaseCutter.moveto(self, location, **kwargs)
self.center = Point(location.x, location.y,
location.z - self.get_required_distance())
def intersect_circle_plane(self, direction, triangle, start=None):
if start is None:
start = self.location
(ccp, cp, d) = intersect_circle_plane(
start.sub(self.location).add(self.center), self.distance_radius,
direction, triangle)
if ccp and cp:
cl = cp.add(start.sub(ccp))
return (cl, ccp, cp, d)
return (None, None, None, INFINITE)
def intersect_circle_point(self, direction, point, start=None):
if start is None:
start = self.location
(ccp, cp, l) = intersect_circle_point(
start.sub(self.location).add(self.center), self.axis,
self.distance_radius, self.distance_radiussq, direction, point)
if ccp:
cl = cp.add(start.sub(ccp))
return (cl, ccp, cp, l)
return (None, None, None, INFINITE)
def intersect_circle_line(self, direction, edge, start=None):
if start is None:
start = self.location
(ccp, cp, l) = intersect_circle_line(
start.sub(self.location).add(self.center), self.axis,
self.distance_radius, self.distance_radiussq, direction, edge)
if ccp:
cl = cp.add(start.sub(ccp))
return (cl, ccp, cp, l)
return (None, None, None, INFINITE)
def intersect(self, direction, triangle, start=None):
(cl_t, d_t, cp_t) = self.intersect_circle_triangle(direction, triangle,
start=start)
d = INFINITE
cl = None
cp = None
if d_t < d:
d = d_t
cl = cl_t
cp = cp_t
if cl and (direction.x == 0) and (direction.y == 0):
#print 'circle_triangle:'
#print 'cl is:', cl, 'd is:', d, 'cp is:', cp
return (cl, d, cp)
(cl_e1, d_e1, cp_e1) = self.intersect_circle_edge(direction,
triangle.e1, start=start)
(cl_e2, d_e2, cp_e2) = self.intersect_circle_edge(direction,
triangle.e2, start=start)
(cl_e3, d_e3, cp_e3) = self.intersect_circle_edge(direction,
triangle.e3, start=start)
if d_e1 < d:
d = d_e1
cl = cl_e1
cp = cp_e1
#print 'circle_edge e1:'
if d_e2 < d:
d = d_e2
cl = cl_e2
cp = cp_e2
#print 'circle_edge e2:'
if d_e3 < d:
d = d_e3
cl = cl_e3
cp = cp_e3
#print 'circle_edge e3:'
if cl and (direction.x == 0) and (direction.y == 0):
#print 'circle_edge:'
#print 'cl is:', cl, 'd is:', d, 'cp is:', cp
return (cl, d, cp)
(cl_p1, d_p1, cp_p1) = self.intersect_circle_vertex(direction,
triangle.p1, start=start)
(cl_p2, d_p2, cp_p2) = self.intersect_circle_vertex(direction,
triangle.p2, start=start)
(cl_p3, d_p3, cp_p3) = self.intersect_circle_vertex(direction,
triangle.p3, start=start)
if d_p1 < d:
d = d_p1
cl = cl_p1
cp = cp_p1
#print 'circle vertex p1:'
if d_p2 < d:
d = d_p2
cl = cl_p2
cp = cp_p2
#print 'circle vertex p2:'
if d_p3 < d:
d = d_p3
cl = cl_p3
cp = cp_p3
#print 'circle vertex p3:'
if cl and (direction.x == 0) and (direction.y == 0):
#print 'circle vertex:'
#print 'cl is:', cl, 'd is:', d, 'cp is:', cp
return (cl, d, cp)
if (direction.x != 0) or (direction.y != 0):
(cl_p1, d_p1, cp_p1) = self.intersect_cylinder_vertex(direction,
triangle.p1, start=start)
(cl_p2, d_p2, cp_p2) = self.intersect_cylinder_vertex(direction,
triangle.p2, start=start)
(cl_p3, d_p3, cp_p3) = self.intersect_cylinder_vertex(direction,
triangle.p3, start=start)
if d_p1 < d:
d = d_p1
cl = cl_p1
cp = cp_p1
#print 'cyl vertex p1:'
if d_p2 < d:
d = d_p2
cl = cl_p2
cp = cp_p2
#print 'cyl vertex p2:'
if d_p3 < d:
d = d_p3
cl = cl_p3
cp = cp_p3
#print 'cyl vertex p3:'
(cl_e1, d_e1, cp_e1) = self.intersect_cylinder_edge(direction,
triangle.e1, start=start)
(cl_e2, d_e2, cp_e2) = self.intersect_cylinder_edge(direction,
triangle.e2, start=start)
(cl_e3, d_e3, cp_e3) = self.intersect_cylinder_edge(direction,
triangle.e3, start=start)
if d_e1 < d:
d = d_e1
cl = cl_e1
cp = cp_e1
#print 'cyl edge e1:'
if d_e2 < d:
d = d_e2
cl = cl_e2
cp = cp_e2
#print 'cyl edge e2:'
if d_e3 < d:
d = d_e3
cl = cl_e3
cp = cp_e3
#print 'cyl edge e3:'
#print 'cyl:'
#print 'cl is:', cl, 'd is:', d, 'cp is:', cp
return (cl, d, cp)