Commit 6b98aebd authored by sumpfralle's avatar sumpfralle

added support for "material allowance" to non-ODE calculations


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@294 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 5e3387e8
Version 0.2.3 - UNRELEASED
* GUI change: tool and process settings can be combined into tasks
* added "material allowance" support for non-ODE calculations
* added export of EMC tool definitions
* store configured settings to a file in the user's home directory
* fixed "overlap" calculation
......
......@@ -15,12 +15,28 @@ class BaseCutter:
BaseCutter.id += 1
self.radius = radius
self.radiussq = radius*radius
self.minx = location.x-radius
self.maxx = location.x+radius
self.miny = location.y-radius
self.maxy = location.y+radius
self.required_distance = 0
self.distance_radius = self.radius
self.distance_radiussq = self.distance_radius * self.distance_radius
# self.minx, self.maxx, self.miny and self.maxy are defined as properties below
self.shape = {}
def _get_minx(self):
return self.location.x - self.distance_radius
minx = property(_get_minx)
def _get_maxx(self):
return self.location.x + self.distance_radius
maxx = property(_get_maxx)
def _get_miny(self):
return self.location.y - self.distance_radius
miny = property(_get_miny)
def _get_maxy(self):
return self.location.y + self.distance_radius
maxy = property(_get_maxy)
def __repr__(self):
return "BaseCutter"
......@@ -36,12 +52,17 @@ class BaseCutter:
# just return a string comparison
return cmp(str(self), str(other))
def set_required_distance(self, value):
if value >= 0:
self.required_distance = value
self.distance_radius = self.radius + self.get_required_distance()
self.distance_radiussq = self.distance_radius * self.distance_radius
def get_required_distance(self):
return self.required_distance
def moveto(self, location):
self.location = location
self.minx = location.x-self.radius
self.maxx = location.x+self.radius
self.miny = location.y-self.radius
self.maxy = location.y+self.radius
for shape, set_pos_func in self.shape.values():
set_pos_func(location.x, location.y, location.z)
......@@ -61,13 +82,14 @@ class BaseCutter:
# check bounding circle collision
c = triangle.center()
if sqr(c.x-self.location.x)+sqr(c.y-self.location.y)>(self.radiussq+2*self.radius*triangle.radius()+triangle.radiussq()):
if sqr(c.x-self.location.x)+sqr(c.y-self.location.y)>(self.distance_radiussq+2*self.distance_radius*triangle.radius()+triangle.radiussq()):
return None
(cl,d)= self.intersect(BaseCutter.vertical, triangle)
return cl
def push(self, dx, dy, triangle):
""" TODO: this function is never used - remove it? """
# check bounding box collision
if dx == 0:
if self.miny > triangle.maxy():
......
......@@ -20,12 +20,12 @@ class CylindricalCutter(BaseCutter):
BaseCutter.__init__(self, location, radius)
self.height = height
self.axis = Point(0,0,1)
self.center = location
self.center = location.sub(Point(0, 0, self.get_required_distance()))
def __repr__(self):
return "CylindricalCutter<%s,%s>" % (self.location,self.radius)
def get_shape(self, format="ODE", additional_distance=0.0):
def get_shape(self, format="ODE"):
if format == "ODE":
import ode
""" We don't handle the the "additional_distance" perfectly, since
......@@ -36,7 +36,8 @@ class CylindricalCutter(BaseCutter):
\_/
This slight incorrectness should be neglectable and causes no harm.
"""
radius = self.radius + additional_distance
additional_distance = self.get_required_distance()
radius = self.distance_radius
height = self.height + additional_distance
center_height = 0.5 * height - additional_distance
geom = ode.GeomTransform(None)
......@@ -88,10 +89,10 @@ class CylindricalCutter(BaseCutter):
def moveto(self, location):
BaseCutter.moveto(self, location)
self.center = location
self.center = location.sub(Point(0, 0, self.get_required_distance()))
def intersect_circle_plane(self, direction, triangle):
(ccp,cp,d) = intersect_circle_plane(self.center, self.radius, direction, triangle)
(ccp,cp,d) = intersect_circle_plane(self.center, self.distance_radius, direction, triangle)
if ccp:
cl = cp.add(self.location.sub(ccp))
return (cl,ccp,cp,d)
......@@ -104,7 +105,7 @@ class CylindricalCutter(BaseCutter):
return (None,INFINITE)
def intersect_circle_point(self, direction, point):
(ccp,cp,l) = intersect_circle_point(self.center, self.axis, self.radius, self.radiussq, direction, point)
(ccp,cp,l) = intersect_circle_point(self.center, self.axis, self.distance_radius, self.distance_radiussq, direction, point)
if ccp:
cl = cp.add(self.location.sub(ccp))
return (cl,ccp,cp,l)
......@@ -115,7 +116,7 @@ class CylindricalCutter(BaseCutter):
return (cl,l)
def intersect_circle_line(self, direction, edge):
(ccp,cp,l) = intersect_circle_line(self.center, self.axis, self.radius, self.radiussq, direction, edge)
(ccp,cp,l) = intersect_circle_line(self.center, self.axis, self.distance_radius, self.distance_radiussq, direction, edge)
if ccp:
cl = cp.add(self.location.sub(ccp))
return (cl,ccp,cp,l)
......@@ -131,7 +132,7 @@ class CylindricalCutter(BaseCutter):
return (cl,l)
def intersect_cylinder_point(self, direction, point):
(ccp,cp,l) = intersect_cylinder_point(self.center, self.axis, self.radius, self.radiussq, direction, point)
(ccp,cp,l) = intersect_cylinder_point(self.center, self.axis, self.distance_radius, self.distance_radiussq, direction, point)
# offset intersection
if ccp:
cl = cp.add(self.location.sub(ccp))
......@@ -145,7 +146,7 @@ class CylindricalCutter(BaseCutter):
return (cl, l)
def intersect_cylinder_line(self, direction, edge):
(ccp,cp,l) = intersect_cylinder_line(self.center, self.axis, self.radius, self.radiussq, direction, edge)
(ccp,cp,l) = intersect_cylinder_line(self.center, self.axis, self.distance_radius, self.distance_radiussq, direction, edge)
# offset intersection
if ccp:
cl = cp.add(self.location.sub(ccp))
......
......@@ -25,9 +25,10 @@ class SphericalCutter(BaseCutter):
def __repr__(self):
return "SphericalCutter<%s,%s>" % (self.location,self.radius)
def get_shape(self, format="ODE", additional_distance=0.0):
def get_shape(self, format="ODE"):
if format == "ODE":
import ode
additional_distance = self.get_required_distance()
radius = self.radius + additional_distance
center_height = 0.5 * self.height + radius - additional_distance
geom = ode.GeomTransform(None)
......@@ -96,7 +97,7 @@ class SphericalCutter(BaseCutter):
self.center = Point(location.x, location.y, location.z+self.radius)
def intersect_sphere_plane(self, direction, triangle):
(ccp,cp,d) = intersect_sphere_plane(self.center, self.radius, direction, triangle)
(ccp,cp,d) = intersect_sphere_plane(self.center, self.distance_radius, direction, triangle)
# offset intersection
if ccp:
cl = cp.add(self.location.sub(ccp))
......@@ -110,7 +111,7 @@ class SphericalCutter(BaseCutter):
return (None,INFINITE)
def intersect_sphere_point(self, direction, point):
(ccp,cp,l) = intersect_sphere_point(self.center, self.radius, self.radiussq, direction, point)
(ccp,cp,l) = intersect_sphere_point(self.center, self.distance_radius, self.distance_radiussq, direction, point)
# offset intersection
cl = None
if cp:
......@@ -122,7 +123,7 @@ class SphericalCutter(BaseCutter):
return (cl,l)
def intersect_sphere_line(self, direction, edge):
(ccp,cp,l) = intersect_sphere_line(self.center, self.radius, self.radiussq, direction, edge)
(ccp,cp,l) = intersect_sphere_line(self.center, self.distance_radius, self.distance_radiussq, direction, edge)
# offset intersection
if ccp:
cl = cp.sub(ccp.sub(self.location))
......@@ -140,7 +141,7 @@ class SphericalCutter(BaseCutter):
return (cl,l)
def intersect_cylinder_point(self, direction, point):
(ccp,cp,l)=intersect_cylinder_point(self.center, self.axis, self.radius, self.radiussq, direction, point)
(ccp,cp,l)=intersect_cylinder_point(self.center, self.axis, self.distance_radius, self.distance_radiussq, direction, point)
# offset intersection
if ccp:
cl = cp.add(self.location.sub(ccp))
......@@ -154,7 +155,7 @@ class SphericalCutter(BaseCutter):
return (cl, l)
def intersect_cylinder_line(self, direction, edge):
(ccp,cp,l) = intersect_cylinder_line(self.center, self.axis, self.radius, self.radiussq, direction, edge)
(ccp,cp,l) = intersect_cylinder_line(self.center, self.axis, self.distance_radius, self.distance_radiussq, direction, edge)
# offset intersection
if ccp:
cl = self.location.add(cp.sub(ccp))
......@@ -240,6 +241,7 @@ class SphericalCutter(BaseCutter):
return (cl,d)
def drop_bis(self, triangle):
""" TODO: this function is never called - remove it? """
n = triangle.normal()
if abs(n.dot(self.axis))<epsilon:
d = triangle.p1.sub(self.center).dot(n)
......
......@@ -26,6 +26,19 @@ class ToroidalCutter(BaseCutter):
self.center = Point(location.x, location.y, location.z+minorradius)
self.majorradiussq = sqr(self.majorradius)
self.minorradiussq = sqr(self.minorradius)
self.distance_majorradius = self.majorradius + self.get_required_distance()
self.distance_minorradius = self.minorradius + self.get_required_distance()
self.distance_majorradiussq = sqr(self.distance_majorradius)
self.distance_minorradiussq = sqr(self.distance_minorradius)
def set_required_distance(self, value):
""" trigger the update of "self.distance_major/minorradius" """
BaseCutter.set_required_distance(self, value)
if value >= 0:
self.distance_majorradius = self.majorradius + self.get_required_distance()
self.distance_minorradius = self.minorradius + self.get_required_distance()
self.distance_majorradiussq = sqr(self.distance_majorradius)
self.distance_minorradiussq = sqr(self.distance_minorradius)
def __repr__(self):
return "ToroidalCutter<%s,%f,R=%f,r=%f>" % (self.location,self.radius,self.majorradius,self.minorradius)
......@@ -40,14 +53,15 @@ class ToroidalCutter(BaseCutter):
# just return a string comparison
return cmp(str(self), str(other))
def get_shape(self, format="ODE", additional_distance=0.0):
def get_shape(self, format="ODE"):
if format == "ODE":
import ode
from pycam.Cutters.CylindricalCutter import CylindricalCutter
# TODO: use an appromixated trimesh instead (ODE does not support toroidal shapes)
# for now: use the simple cylinder shape - this should not do any harm
self.shape[format] = CylindricalCutter(self.radius, self.location,
height=self.height).get_shape(format, additional_distance)
cylinder = CylindricalCutter(self.radius, self.location, height=self.height)
cylinder.set_required_distance(self.get_required_distance())
self.shape[format] = cylinder.get_shape(format)
return self.shape[format]
def to_OpenGL(self):
......@@ -72,7 +86,7 @@ class ToroidalCutter(BaseCutter):
self.center = Point(location.x, location.y, location.z+self.minorradius)
def intersect_torus_plane(self, direction, triangle):
(ccp,cp,l) = intersect_torus_plane(self.center, self.axis, self.majorradius, self.minorradius, direction, triangle)
(ccp,cp,l) = intersect_torus_plane(self.center, self.axis, self.distance_majorradius, self.distance_minorradius, direction, triangle)
if cp:
cl = cp.add(self.location.sub(ccp))
return (cl,ccp,cp,l)
......@@ -85,7 +99,7 @@ class ToroidalCutter(BaseCutter):
return (None,INFINITE)
def intersect_torus_point(self, direction, point):
(ccp,cp,l) = intersect_torus_point(self.center, self.axis, self.majorradius, self.minorradius, self.majorradiussq, self.minorradiussq, direction, point)
(ccp,cp,l) = intersect_torus_point(self.center, self.axis, self.distance_majorradius, self.distance_minorradius, self.distance_majorradiussq, self.distance_minorradiussq, direction, point)
if ccp:
cl = point.add(self.location.sub(ccp))
return (cl, ccp, point, l)
......@@ -100,7 +114,7 @@ class ToroidalCutter(BaseCutter):
min_m = 0
min_l = INFINITE
min_cl = None
scale = int(edge.len()/self.minorradius*2)
scale = int(edge.len()/self.distance_minorradius*2)
if scale<3:
scale = 3
for i in range(0,scale+1):
......@@ -130,7 +144,7 @@ class ToroidalCutter(BaseCutter):
return (min_cl, min_l)
def intersect_cylinder_point(self, direction, point):
(ccp,cp,l) = intersect_cylinder_point(self.center, self.axis, self.radius, self.radiussq, direction, point)
(ccp,cp,l) = intersect_cylinder_point(self.center, self.axis, self.distance_radius, self.distance_radiussq, direction, point)
# offset intersection
if ccp:
cl = self.location.add(direction.mul(l))
......@@ -144,7 +158,7 @@ class ToroidalCutter(BaseCutter):
return (cl, l)
def intersect_cylinder_line(self, direction, edge):
(ccp,cp,l) = intersect_cylinder_line(self.center, self.axis, self.radius, self.radiussq, direction, edge)
(ccp,cp,l) = intersect_cylinder_line(self.center, self.axis, self.distance_radius, self.distance_radiussq, direction, edge)
# offset intersection
if ccp:
cl = self.location.add(cp.sub(ccp))
......@@ -162,7 +176,7 @@ class ToroidalCutter(BaseCutter):
return (cl,l)
def intersect_circle_plane(self, direction, triangle):
(ccp,cp,l) = intersect_circle_plane(self.location, self.majorradius, direction, triangle)
(ccp,cp,l) = intersect_circle_plane(self.location, self.distance_majorradius, direction, triangle)
# offset intersection
if ccp:
cl = cp.sub(ccp.sub(self.location))
......@@ -176,7 +190,7 @@ class ToroidalCutter(BaseCutter):
return (None,INFINITE)
def intersect_circle_point(self, direction, point):
(ccp, cp, l) = intersect_circle_point(self.location, self.axis, self.majorradius, self.majorradiussq, direction, point)
(ccp, cp, l) = intersect_circle_point(self.location, self.axis, self.distance_majorradius, self.distance_majorradiussq, direction, point)
if ccp:
cl = cp.sub(ccp.sub(self.location))
return (cl,ccp,point,l)
......@@ -187,7 +201,7 @@ class ToroidalCutter(BaseCutter):
return (cl,l)
def intersect_circle_line(self, direction, edge):
(ccp,cp,l) = intersect_circle_line(self.location, self.axis, self.majorradius, self.majorradiussq, direction, edge)
(ccp,cp,l) = intersect_circle_line(self.location, self.axis, self.distance_majorradius, self.distance_majorradiussq, direction, edge)
if ccp:
cl = cp.sub(ccp.sub(self.location))
return (cl,ccp,cp,l)
......
......@@ -543,13 +543,11 @@ class ProjectGui:
# repaint the 3d view after a color change
obj.connect("color-set", self.update_view)
# set the availability of ODE
enable_ode_control = self.gui.get_object("SettingEnableODE")
if ode_objects.is_ode_available():
obj = self.gui.get_object("SettingEnableODE")
self.settings.add_item("enable_ode", obj.get_active, obj.set_active)
self.settings.add_item("enable_ode", enable_ode_control.get_active, enable_ode_control.set_active)
else:
self.gui.get_object("SettingEnableODE").set_sensitive(False)
# TODO: remove this as soon as non-ODE toolpath generation respects material allowance
self.gui.get_object("MaterialAllowanceControl").set_sensitive(False)
enable_ode_control.set_sensitive(False)
# bind dummy get/set functions to "enable_ode" (always return False)
self.settings.add_item("enable_ode", lambda: False, lambda state: None)
skip_obj = self.gui.get_object("DrillProgressFrameSkipControl")
......@@ -704,10 +702,10 @@ class ProjectGui:
self.view3d.glsetup()
self.view3d.paint()
def get_physics(self, cutter, material_allowance):
def get_physics(self, cutter):
if self.settings.get("enable_ode"):
self._physics_cache = ode_objects.generate_physics(self.model, cutter,
self._physics_cache, material_allowance)
self._physics_cache = ode_objects.generate_physics(self.model,
cutter, self._physics_cache)
else:
self._physics_cache = None
return self._physics_cache
......@@ -858,8 +856,6 @@ class ProjectGui:
self.gui.get_object(objname).set_sensitive(dropcutter_active)
# disable "step down" control, if PushCutter is not active
self.gui.get_object("MaxStepDownControl").set_sensitive(get_obj("PushCutter").get_active())
# "material allowance" requires ODE support
self.gui.get_object("MaterialAllowanceControl").set_sensitive(self.settings.get("enable_ode"))
def update_tool_controls(self, widget=None, data=None):
# disable the toroidal radius if the toroidal cutter is not enabled
......@@ -1655,7 +1651,8 @@ class ProjectGui:
def get_pathgenerator_instance(self, cutter, process_settings):
pathgenerator = process_settings["path_generator"]
pathprocessor = process_settings["path_postprocessor"]
physics = self.get_physics(cutter, process_settings["material_allowance"])
cutter.set_required_distance(process_settings["material_allowance"])
physics = self.get_physics(cutter)
if pathgenerator == "DropCutter":
if pathprocessor == "ZigZagCutter":
processor = pycam.PathProcessors.PathAccumulator(zigzag=True)
......
......@@ -10,12 +10,12 @@ ShapeCapsule = lambda radius, height: ode.GeomCapsule(None, radius, height - (2
_ode_override_state = None
def generate_physics(model, cutter, physics=None, material_allowance=0.0):
def generate_physics(model, cutter, physics=None):
if physics is None:
physics = PhysicalWorld()
physics.reset()
physics.add_mesh((0, 0, 0), model.triangles())
shape_info = cutter.get_shape("ODE", additional_distance=material_allowance)
shape_info = cutter.get_shape("ODE")
physics.set_drill(shape_info[0], (0.0, 0.0, 0.0))
return physics
......
......@@ -186,10 +186,10 @@ class DropCutter:
cut_max = None
triangle_max = None
self.cutter.moveto(p)
box_x_min = p.x - self.cutter.radius
box_x_max = p.x + self.cutter.radius
box_y_min = p.y - self.cutter.radius
box_y_max = p.y + self.cutter.radius
box_x_min = self.cutter.minx
box_x_max = self.cutter.maxx
box_y_min = self.cutter.miny
box_y_max = self.cutter.maxy
box_z_min = dim_height.end
box_z_max = self.safety_height
triangles = self.model.triangles(box_x_min, box_y_min, box_z_min, box_x_max, box_y_max, box_z_max)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment