Commit 6ed95bf7 authored by sumpfralle's avatar sumpfralle

removed useless local cache for the process pusher

adjusted the ContourFollow strategy to use caching properly


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@747 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 198a3ab2
...@@ -45,30 +45,31 @@ log = pycam.Utils.log.get_logger() ...@@ -45,30 +45,31 @@ log = pycam.Utils.log.get_logger()
# We need to use a global function here - otherwise it does not work with # We need to use a global function here - otherwise it does not work with
# the multiprocessing Pool. # the multiprocessing Pool.
def _process_one_triangle(obj, triangle, z): def _process_one_triangle(model, cutter, up_vector, triangle, z):
result = [] result = []
if id(triangle) in obj._processed_triangles:
# skip triangles that are known to cause no collision
return result
# ignore triangles below the z level # ignore triangles below the z level
if triangle.maxz < z: if triangle.maxz < z:
# Case 1a # Case 1a
return result return result, None
# ignore triangles pointing upwards or downwards # ignore triangles pointing upwards or downwards
if triangle.normal.cross(obj._up_vector).norm == 0: if triangle.normal.cross(up_vector).norm == 0:
# Case 1b # Case 1b
return result return result, None
edge_collisions = obj.get_collision_waterline_of_triangle(triangle, z) edge_collisions = get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z)
if not edge_collisions: if edge_collisions is None:
return result # don't try to use this edge again
for cutter_location, edge in edge_collisions: return result, [id(triangle)]
shifted_edge = obj.get_shifted_waterline(edge, cutter_location) elif len(edge_collisions) == 0:
if not shifted_edge is None: return result, None
if _DEBUG_DISBALE_WATERLINE_SHIFT: else:
result.append((edge, edge)) for cutter_location, edge in edge_collisions:
else: shifted_edge = get_shifted_waterline(up_vector, edge, cutter_location)
result.append((edge, shifted_edge)) if not shifted_edge is None:
return result if _DEBUG_DISBALE_WATERLINE_SHIFT:
result.append((edge, edge))
else:
result.append((edge, shifted_edge))
return result, None
class CollisionPaths: class CollisionPaths:
...@@ -286,9 +287,13 @@ class ContourFollow: ...@@ -286,9 +287,13 @@ class ContourFollow:
projected_waterlines = [] projected_waterlines = []
triangles = self.model.triangles(minx=minx, miny=miny, maxx=maxx, triangles = self.model.triangles(minx=minx, miny=miny, maxx=maxx,
maxy=maxy) maxy=maxy)
results_iter = run_in_parallel(_process_one_triangle, args = [(self.model, self.cutter, self._up_vector, t, z)
[(self, t, z) for t in triangles], unordered=True) for t in triangles if not id(t) in self._processed_triangles]
for result in results_iter: results_iter = run_in_parallel(_process_one_triangle, args,
unordered=True)
for result, ignore_triangle_id_list in results_iter:
if ignore_triangle_id_list:
self._processed_triangles.extend(ignore_triangle_id_list)
for edge, shifted_edge in result: for edge, shifted_edge in result:
waterline_triangles.add(edge, shifted_edge) waterline_triangles.add(edge, shifted_edge)
if (not progress_counter is None) \ if (not progress_counter is None) \
...@@ -304,199 +309,197 @@ class ContourFollow: ...@@ -304,199 +309,197 @@ class ContourFollow:
result.append(cropped_line) result.append(cropped_line)
return result return result
def get_max_length(self):
if not hasattr(self, "_max_length_cache"): def get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z):
# update the cache # TODO: there are problems with "material allowance > 0"
x_dim = abs(self.model.maxx - self.model.minx) plane = Plane(Point(0, 0, z), up_vector)
y_dim = abs(self.model.maxy - self.model.miny) if triangle.minz >= z:
z_dim = abs(self.model.maxz - self.model.minz) # no point of the triangle is below z
self._max_length_cache = sqrt(x_dim ** 2 + y_dim ** 2 + z_dim ** 2) # try all edges
return self._max_length_cache # Case (4)
proj_points = []
def get_collision_waterline_of_triangle(self, triangle, z): for p in triangle.get_points():
# TODO: there are problems with "material allowance > 0" proj_p = plane.get_point_projection(p)
plane = Plane(Point(0, 0, z), self._up_vector) if not proj_p in proj_points:
if triangle.minz >= z: proj_points.append(proj_p)
# no point of the triangle is below z if len(proj_points) == 3:
# try all edges edges = []
# Case (4) for index in range(3):
proj_points = [] edge = Line(proj_points[index - 1], proj_points[index])
for p in triangle.get_points(): # the edge should be clockwise around the model
proj_p = plane.get_point_projection(p) if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
if not proj_p in proj_points: edge = Line(edge.p2, edge.p1)
proj_points.append(proj_p) edges.append((edge, proj_points[index - 2]))
if len(proj_points) == 3: outer_edges = []
edges = [] for edge, other_point in edges:
for index in range(3): # pick only edges, where the other point is on the right side
edge = Line(proj_points[index - 1], proj_points[index]) if other_point.sub(edge.p1).cross(edge.dir).dot(up_vector) > 0:
# the edge should be clockwise around the model outer_edges.append(edge)
if edge.dir.cross(triangle.normal).dot(self._up_vector) < 0: if len(outer_edges) == 0:
edge = Line(edge.p2, edge.p1) # the points seem to be an one line
edges.append((edge, proj_points[index - 2])) # pick the longest edge
long_edge = edges[0][0]
for edge, other_point in edges[1:]:
if edge.len > long_edge.len:
long_edge = edge
outer_edges = [long_edge]
else:
edge = Line(proj_points[0], proj_points[1])
if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
edge = Line(edge.p2, edge.p1)
outer_edges = [edge]
else:
# some parts of the triangle are above and some below the cutter level
# Cases (2a), (2b), (3a) and (3b)
points_above = [plane.get_point_projection(p) for p in triangle.get_points() if p.z > z]
waterline = plane.intersect_triangle(triangle)
if waterline is None:
if len(points_above) == 0:
# the highest point of the triangle is at z
outer_edges = [] outer_edges = []
for edge, other_point in edges:
# pick only edges, where the other point is on the right side
if other_point.sub(edge.p1).cross(edge.dir).dot(self._up_vector) > 0:
outer_edges.append(edge)
if len(outer_edges) == 0:
# the points seem to be an one line
# pick the longest edge
long_edge = edges[0][0]
for edge, other_point in edges[1:]:
if edge.len > long_edge.len:
long_edge = edge
outer_edges = [long_edge]
else: else:
edge = Line(proj_points[0], proj_points[1]) if abs(triangle.minz - z) < epsilon:
if edge.dir.cross(triangle.normal).dot(self._up_vector) < 0: # This is just an accuracy issue (see the
edge = Line(edge.p2, edge.p1) # "triangle.minz >= z" statement above).
outer_edges = [edge] outer_edges = []
elif not [p for p in triangle.get_points() if p.z > z + epsilon]:
# same as above: fix for inaccurate floating calculations
outer_edges = []
else:
# this should not happen
raise ValueError(("Could not find a waterline, but " \
+ "there are points above z level (%f): " \
+ "%s / %s") % (z, triangle, points_above))
else: else:
# some parts of the triangle are above and some below the cutter level # remove points that are not part of the waterline
# Cases (2a), (2b), (3a) and (3b) points_above = [p for p in points_above
points_above = [plane.get_point_projection(p) for p in triangle.get_points() if p.z > z] if (p != waterline.p1) and (p != waterline.p2)]
waterline = plane.intersect_triangle(triangle) potential_edges = []
if waterline is None: if len(points_above) == 0:
if len(points_above) == 0: # part of case (2a)
# the highest point of the triangle is at z outer_edges = [waterline]
elif len(points_above) == 1:
other_point = points_above[0]
dot = other_point.sub(waterline.p1).cross(waterline.dir).dot(up_vector)
if dot > 0:
# Case (2b)
outer_edges = [waterline]
elif dot < 0:
# Case (3b)
edges = []
edges.append(Line(waterline.p1, other_point))
edges.append(Line(waterline.p2, other_point))
outer_edges = [] outer_edges = []
for edge in edges:
if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
outer_edges.append(Line(edge.p2, edge.p1))
else:
outer_edges.append(edge)
else: else:
if abs(triangle.minz - z) < epsilon: # the three points are on one line
# This is just an accuracy issue (see the # part of case (2a)
# "triangle.minz >= z" statement above). edges = []
outer_edges = [] edges.append(waterline)
elif not [p for p in triangle.get_points() if p.z > z + epsilon]: edges.append(Line(waterline.p1, other_point))
# same as above: fix for inaccurate floating calculations edges.append(Line(waterline.p2, other_point))
outer_edges = [] edges.sort(key=lambda x: x.len)
edge = edges[-1]
if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
outer_edges = [Line(edge.p2, edge.p1)]
else: else:
# this should not happen outer_edges = [edge]
raise ValueError(("Could not find a waterline, but " \
+ "there are points above z level (%f): " \
+ "%s / %s") % (z, triangle, points_above))
else: else:
# remove points that are not part of the waterline # two points above
points_above = [p for p in points_above other_point = points_above[0]
if (p != waterline.p1) and (p != waterline.p2)] dot = other_point.sub(waterline.p1).cross(waterline.dir).dot(up_vector)
potential_edges = [] if dot > 0:
if len(points_above) == 0: # Case (2b)
# part of case (2a) # the other two points are on the right side
outer_edges = [waterline] outer_edges = [waterline]
elif len(points_above) == 1: elif dot < 0:
other_point = points_above[0] # Case (3a)
dot = other_point.sub(waterline.p1).cross(waterline.dir).dot(self._up_vector) edge = Line(points_above[0], points_above[1])
if dot > 0: if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
# Case (2b) outer_edges = [Line(edge.p2, edge.p1)]
outer_edges = [waterline]
elif dot < 0:
# Case (3b)
edges = []
edges.append(Line(waterline.p1, other_point))
edges.append(Line(waterline.p2, other_point))
outer_edges = []
for edge in edges:
if edge.dir.cross(triangle.normal).dot(self._up_vector) < 0:
outer_edges.append(Line(edge.p2, edge.p1))
else:
outer_edges.append(edge)
else: else:
# the three points are on one line outer_edges = [edge]
# part of case (2a)
edges = []
edges.append(waterline)
edges.append(Line(waterline.p1, other_point))
edges.append(Line(waterline.p2, other_point))
edges.sort(key=lambda x: x.len)
edge = edges[-1]
if edge.dir.cross(triangle.normal).dot(self._up_vector) < 0:
outer_edges = [Line(edge.p2, edge.p1)]
else:
outer_edges = [edge]
else: else:
# two points above edges = []
other_point = points_above[0] # pick the longest combination of two of these points
dot = other_point.sub(waterline.p1).cross(waterline.dir).dot(self._up_vector) # part of case (2a)
if dot > 0: # TODO: maybe we should use the waterline instead? (otherweise the line could be too long and thus connections to the adjacent waterlines are not discovered? Test this with an appropriate test model.)
# Case (2b) points = [waterline.p1, waterline.p2] + points_above
# the other two points are on the right side for p1 in points:
outer_edges = [waterline] for p2 in points:
elif dot < 0: if not p1 is p2:
# Case (3a) edges.append(Line(p1, p2))
edge = Line(points_above[0], points_above[1]) edges.sort(key=lambda x: x.len)
if edge.dir.cross(triangle.normal).dot(self._up_vector) < 0: edge = edges[-1]
outer_edges = [Line(edge.p2, edge.p1)] if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
else: outer_edges = [Line(edge.p2, edge.p1)]
outer_edges = [edge]
else: else:
edges = [] outer_edges = [edge]
# pick the longest combination of two of these points # calculate the maximum diagonal length within the model
# part of case (2a) x_dim = abs(model.maxx - model.minx)
# TODO: maybe we should use the waterline instead? (otherweise the line could be too long and thus connections to the adjacent waterlines are not discovered? Test this with an appropriate test model.) y_dim = abs(model.maxy - model.miny)
points = [waterline.p1, waterline.p2] + points_above z_dim = abs(model.maxz - model.minz)
for p1 in points: max_length = sqrt(x_dim ** 2 + y_dim ** 2 + z_dim ** 2)
for p2 in points: result = []
if not p1 is p2: for edge in outer_edges:
edges.append(Line(p1, p2)) direction = up_vector.cross(edge.dir).normalized()
edges.sort(key=lambda x: x.len) if direction is None:
edge = edges[-1] continue
if edge.dir.cross(triangle.normal).dot(self._up_vector) < 0: direction = direction.mul(max_length)
outer_edges = [Line(edge.p2, edge.p1)] edge_dir = edge.p2.sub(edge.p1)
else: # TODO: adapt the number of potential starting positions to the length of the line
outer_edges = [edge] # Don't use 0.0 and 1.0 - this could result in ambiguous collisions
result = [] # with triangles sharing these vertices.
for edge in outer_edges: for factor in (0.5, epsilon, 1.0 - epsilon, 0.25, 0.75):
direction = self._up_vector.cross(edge.dir).normalized() start = edge.p1.add(edge_dir.mul(factor))
if direction is None: # We need to use the triangle collision algorithm here - because we
continue # need the point of collision in the triangle.
direction = direction.mul(self.get_max_length()) collisions = get_free_paths_triangles(model, cutter, start,
edge_dir = edge.p2.sub(edge.p1) start.add(direction), return_triangles=True)
# TODO: adapt the number of potential starting positions to the length of the line for index, coll in enumerate(collisions):
# Don't use 0.0 and 1.0 - this could result in ambiguous collisions if (index % 2 == 0) and (not coll[1] is None) \
# with triangles sharing these vertices. and (not coll[2] is None) \
for factor in (0.5, epsilon, 1.0 - epsilon, 0.25, 0.75): and (coll[0].sub(start).dot(direction) > 0):
start = edge.p1.add(edge_dir.mul(factor)) cl, hit_t, cp = coll
# We need to use the triangle collision algorithm here - because we
# need the point of collision in the triangle.
collisions = get_free_paths_triangles(self.model, self.cutter, start,
start.add(direction), return_triangles=True)
for index, coll in enumerate(collisions):
if (index % 2 == 0) and (not coll[1] is None) \
and (not coll[2] is None) \
and (coll[0].sub(start).dot(direction) > 0):
cl, hit_t, cp = coll
break
else:
continue
log.info("Failed to detect any collision: " \
+ "%s / %s -> %s" % (edge, start, direction))
proj_cp = plane.get_point_projection(cp)
# e.g. the Spherical Cutter often does not collide exactly above
# the potential collision line.
# TODO: maybe an "is cp inside of the triangle" check would be good?
if (triangle is hit_t) or (edge.is_point_inside(proj_cp)):
result.append((cl, edge))
# continue with the next outer_edge
break break
# Don't check triangles again that are completely above the z level and else:
# did not return any collisions. continue
if (len(result) == 0) and (triangle.minz > z): log.info("Failed to detect any collision: " \
self._processed_triangles.append(id(triangle)) + "%s / %s -> %s" % (edge, start, direction))
return result proj_cp = plane.get_point_projection(cp)
# e.g. the Spherical Cutter often does not collide exactly above
# the potential collision line.
# TODO: maybe an "is cp inside of the triangle" check would be good?
if (triangle is hit_t) or (edge.is_point_inside(proj_cp)):
result.append((cl, edge))
# continue with the next outer_edge
break
# Don't check triangles again that are completely above the z level and
# did not return any collisions.
if (len(result) == 0) and (triangle.minz > z):
# a return value None indicates that the triangle needs no further evaluation
return None
return result
def get_shifted_waterline(self, waterline, cutter_location): def get_shifted_waterline(up_vector, waterline, cutter_location):
# Project the waterline and the cutter location down to the slice plane. # Project the waterline and the cutter location down to the slice plane.
# This is necessary for calculating the horizontal distance between the # This is necessary for calculating the horizontal distance between the
# cutter and the triangle waterline. # cutter and the triangle waterline.
plane = Plane(cutter_location, self._up_vector) plane = Plane(cutter_location, up_vector)
wl_proj = plane.get_line_projection(waterline) wl_proj = plane.get_line_projection(waterline)
if wl_proj.len < epsilon: if wl_proj.len < epsilon:
return None return None
offset = wl_proj.dist_to_point(cutter_location) offset = wl_proj.dist_to_point(cutter_location)
if offset < epsilon: if offset < epsilon:
return wl_proj return wl_proj
# shift both ends of the waterline towards the cutter location # shift both ends of the waterline towards the cutter location
shift = cutter_location.sub(wl_proj.closest_point(cutter_location)) shift = cutter_location.sub(wl_proj.closest_point(cutter_location))
# increase the shift width slightly to avoid "touch" collisions # increase the shift width slightly to avoid "touch" collisions
shift = shift.mul(1.0 + epsilon) shift = shift.mul(1.0 + epsilon)
shifted_waterline = Line(wl_proj.p1.add(shift), wl_proj.p2.add(shift)) shifted_waterline = Line(wl_proj.p1.add(shift), wl_proj.p2.add(shift))
return shifted_waterline return shifted_waterline
...@@ -254,7 +254,6 @@ def run_in_parallel_remote(func, args_list, unordered=False, ...@@ -254,7 +254,6 @@ def run_in_parallel_remote(func, args_list, unordered=False,
results_queue = __manager.results() results_queue = __manager.results()
remote_cache = __manager.cache() remote_cache = __manager.cache()
stats = __manager.statistics() stats = __manager.statistics()
local_cache = {}
for args in args_list: for args in args_list:
start_time = time.time() start_time = time.time()
result_args = [] result_args = []
...@@ -262,13 +261,9 @@ def run_in_parallel_remote(func, args_list, unordered=False, ...@@ -262,13 +261,9 @@ def run_in_parallel_remote(func, args_list, unordered=False,
# add the argument to the cache if possible # add the argument to the cache if possible
if hasattr(arg, "uuid"): if hasattr(arg, "uuid"):
data_uuid = ProcessDataCacheItemID(arg.uuid) data_uuid = ProcessDataCacheItemID(arg.uuid)
if not data_uuid in local_cache.keys(): if not remote_cache.contains(data_uuid):
local_cache[data_uuid] = arg log.debug("Adding cache item for job %s: %s - %s" % (job_id, arg.uuid, arg.__class__))
log.debug("Adding item to manager's local cache " \ remote_cache.add(data_uuid, arg)
+ "(job: %s): %s - %s" \
% (job_id, arg.uuid, arg.__class__))
if not remote_cache.contains(data_uuid):
remote_cache.add(data_uuid, arg)
result_args.append(data_uuid) result_args.append(data_uuid)
else: else:
result_args.append(arg) result_args.append(arg)
...@@ -404,6 +399,7 @@ class ProcessStatistics(object): ...@@ -404,6 +399,7 @@ class ProcessStatistics(object):
self.queues[name].transfer_time += amount self.queues[name].transfer_time += amount
# TODO: implement an expiry time for cache items
class ProcessDataCache(object): class ProcessDataCache(object):
def __init__(self): def __init__(self):
...@@ -417,7 +413,6 @@ class ProcessDataCache(object): ...@@ -417,7 +413,6 @@ class ProcessDataCache(object):
def add(self, name, value): def add(self, name, value):
if isinstance(name, ProcessDataCacheItemID): if isinstance(name, ProcessDataCacheItemID):
name = name.value name = name.value
log.debug("Added cache item: %s - %s" % (name, type(value)))
self.cache[name] = value self.cache[name] = value
def get(self, name): def get(self, name):
...@@ -428,7 +423,6 @@ class ProcessDataCache(object): ...@@ -428,7 +423,6 @@ class ProcessDataCache(object):
def remove(self, name): def remove(self, name):
if isinstance(name, ProcessDataCacheItemID): if isinstance(name, ProcessDataCacheItemID):
name = name.value name = name.value
log.debug("Removed cache item: %s - %s" % (name, type(value)))
if name in self.cache: if name in self.cache:
del self.cache[name] del self.cache[name]
......
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