Commit 2fb2f1a1 authored by sumpfralle's avatar sumpfralle

finished implementation of automatically distributed support grid


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@624 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent fdb7758b
...@@ -804,9 +804,8 @@ class ProjectGui: ...@@ -804,9 +804,8 @@ class ProjectGui:
if (s.get("support_grid_thickness") > 0) \ if (s.get("support_grid_thickness") > 0) \
and (s.get("support_grid_height") > 0) \ and (s.get("support_grid_height") > 0) \
and (s.get("support_grid_average_distance") > 0) \ and (s.get("support_grid_average_distance") > 0) \
and (s.get("support_grid_minimum_bridges") > 0) \ and (s.get("support_grid_minimum_bridges") > 0):
and (s.get("support_grid_length") > 0): support_grid = pycam.Toolpath.SupportGrid.get_support_distributed(
support_grid = pycam.Toolpath.SupportGrid.get_distributed_support_bridges(
s.get("model"), s.get("minz"), s.get("model"), s.get("minz"),
s.get("support_grid_average_distance"), s.get("support_grid_average_distance"),
s.get("support_grid_minimum_bridges"), s.get("support_grid_minimum_bridges"),
...@@ -816,6 +815,7 @@ class ProjectGui: ...@@ -816,6 +815,7 @@ class ProjectGui:
elif grid_type == GRID_TYPES["none"]: elif grid_type == GRID_TYPES["none"]:
pass pass
s.set("support_grid", support_grid) s.set("support_grid", support_grid)
self.update_view()
def switch_support_grid_manual_selector(self, widget=None): def switch_support_grid_manual_selector(self, widget=None):
old_axis_was_x = self.grid_adjustment_axis_x_last old_axis_was_x = self.grid_adjustment_axis_x_last
...@@ -2604,7 +2604,7 @@ class ProjectGui: ...@@ -2604,7 +2604,7 @@ class ProjectGui:
adjustments_x=self.grid_adjustments_x, adjustments_x=self.grid_adjustments_x,
adjustments_y=self.grid_adjustments_y) adjustments_y=self.grid_adjustments_y)
elif grid_type == GRID_TYPES["automatic"]: elif grid_type == GRID_TYPES["automatic"]:
toolpath_settings.set_support_automatic( toolpath_settings.set_support_distributed(
self.settings.get("support_grid_average_distance"), self.settings.get("support_grid_average_distance"),
self.settings.get("support_grid_minimum_bridges"), self.settings.get("support_grid_minimum_bridges"),
self.settings.get("support_grid_thickness"), self.settings.get("support_grid_thickness"),
......
...@@ -563,6 +563,7 @@ class ToolpathSettings: ...@@ -563,6 +563,7 @@ class ToolpathSettings:
"feedrate": float, "feedrate": float,
}, },
"SupportGrid": { "SupportGrid": {
"type": str,
"distance_x": float, "distance_x": float,
"distance_y": float, "distance_y": float,
"thickness": float, "thickness": float,
...@@ -571,6 +572,9 @@ class ToolpathSettings: ...@@ -571,6 +572,9 @@ class ToolpathSettings:
"offset_y": float, "offset_y": float,
"adjustments_x": "list_of_float", "adjustments_x": "list_of_float",
"adjustments_y": "list_of_float", "adjustments_y": "list_of_float",
"average_distance": float,
"minimum_bridges": int,
"length": float,
}, },
"Program": { "Program": {
"unit": str, "unit": str,
...@@ -637,6 +641,7 @@ class ToolpathSettings: ...@@ -637,6 +641,7 @@ class ToolpathSettings:
adjustments_x = [] adjustments_x = []
if adjustments_y is None: if adjustments_y is None:
adjustments_y = [] adjustments_y = []
self.support_grid["type"] = "grid"
self.support_grid["distance_x"] = distance_x self.support_grid["distance_x"] = distance_x
self.support_grid["distance_y"] = distance_y self.support_grid["distance_y"] = distance_y
self.support_grid["offset_x"] = offset_x self.support_grid["offset_x"] = offset_x
...@@ -646,12 +651,26 @@ class ToolpathSettings: ...@@ -646,12 +651,26 @@ class ToolpathSettings:
self.support_grid["adjustments_x"] = adjustments_x self.support_grid["adjustments_x"] = adjustments_x
self.support_grid["adjustments_y"] = adjustments_y self.support_grid["adjustments_y"] = adjustments_y
def set_support_distributed(self, average_distance, minimum_bridges,
thickness, height, length):
self.support_grid["type"] = "distributed"
self.support_grid["average_distance"] = average_distance
self.support_grid["minimum_bridges"] = minimum_bridges
self.support_grid["thickness"] = thickness
self.support_grid["height"] = height
self.support_grid["length"] = length
def get_support_grid(self): def get_support_grid(self):
result = {}
if self.support_grid: if self.support_grid:
return self.support_grid options = self.support_grid
else: else:
result = {} options = {}
# add all keys from the default list
for key in self.SECTIONS["SupportGrid"].keys(): for key in self.SECTIONS["SupportGrid"].keys():
if options.has_key(key):
result[key] = options[key]
else:
result[key] = None result[key] = None
return result return result
......
...@@ -50,20 +50,23 @@ def generate_toolpath_from_settings(model, tp_settings, callback=None): ...@@ -50,20 +50,23 @@ def generate_toolpath_from_settings(model, tp_settings, callback=None):
process["generator"], process["postprocessor"], process["generator"], process["postprocessor"],
process["material_allowance"], process["safety_height"], process["material_allowance"], process["safety_height"],
process["overlap"], process["step_down"], process["engrave_offset"], process["overlap"], process["step_down"], process["engrave_offset"],
grid["distance_x"], grid["distance_y"], grid["thickness"], grid["type"], grid["distance_x"], grid["distance_y"],
grid["height"], grid["offset_x"], grid["offset_y"], grid["thickness"], grid["height"], grid["offset_x"],
grid["adjustments_x"], grid["adjustments_y"], backend, grid["offset_y"], grid["adjustments_x"], grid["adjustments_y"],
callback) grid["average_distance"], grid["minimum_bridges"], grid["length"],
backend, callback)
def generate_toolpath(model, tool_settings=None, def generate_toolpath(model, tool_settings=None,
bounds_low=None, bounds_high=None, direction="x", bounds_low=None, bounds_high=None, direction="x",
path_generator="DropCutter", path_postprocessor="ZigZagCutter", path_generator="DropCutter", path_postprocessor="ZigZagCutter",
material_allowance=0, safety_height=None, overlap=0, step_down=0, material_allowance=0, safety_height=None, overlap=0, step_down=0,
engrave_offset=0, support_grid_distance_x=None, engrave_offset=0, support_grid_type=None, support_grid_distance_x=None,
support_grid_distance_y=None, support_grid_thickness=None, support_grid_distance_y=None, support_grid_thickness=None,
support_grid_height=None, support_grid_offset_x=None, support_grid_height=None, support_grid_offset_x=None,
support_grid_offset_y=None, support_grid_adjustments_x=None, support_grid_offset_y=None, support_grid_adjustments_x=None,
support_grid_adjustments_y=None, calculation_backend=None, callback=None): support_grid_adjustments_y=None, support_grid_average_distance=None,
support_grid_minimum_bridges=None, support_grid_length=None,
calculation_backend=None, callback=None):
""" abstract interface for generating a toolpath """ abstract interface for generating a toolpath
@type model: pycam.Geometry.Model.Model @type model: pycam.Geometry.Model.Model
...@@ -143,7 +146,8 @@ def generate_toolpath(model, tool_settings=None, ...@@ -143,7 +146,8 @@ def generate_toolpath(model, tool_settings=None,
# material allowance is ignored for engraving # material allowance is ignored for engraving
material_allowance = 0 material_allowance = 0
# create the grid model if requested # create the grid model if requested
if (((not support_grid_distance_x is None) \ if (support_grid_type == "grid") \
and (((not support_grid_distance_x is None) \
or (not support_grid_distance_y is None)) \ or (not support_grid_distance_y is None)) \
and (not support_grid_thickness is None)): and (not support_grid_thickness is None)):
# grid height defaults to the thickness # grid height defaults to the thickness
...@@ -168,6 +172,35 @@ def generate_toolpath(model, tool_settings=None, ...@@ -168,6 +172,35 @@ def generate_toolpath(model, tool_settings=None,
adjustments_x=support_grid_adjustments_x, adjustments_x=support_grid_adjustments_x,
adjustments_y=support_grid_adjustments_y) adjustments_y=support_grid_adjustments_y)
trimesh_model += support_grid_model trimesh_model += support_grid_model
elif (support_grid_type == "distributed") \
and (not support_grid_average_distance is None) \
and (not support_grid_thickness is None) \
and (not support_grid_length is None):
if support_grid_height is None:
support_grid_height = support_grid_thickness
if support_grid_minimum_bridges is None:
support_grid_minimum_bridges = 2
if support_grid_average_distance <= 0:
return "The average support grid distance must be a positive value"
if support_grid_minimum_bridges <= 0:
return "The minimum number of bridged per polygon must be a " \
+ "positive value"
if support_grid_thickness <= 0:
return "The thickness of the support grid must be a positive value"
if support_grid_height <= 0:
return "The height of the support grid must be a positive value"
if not callback is None:
callback(text="Preparing support grid model ...")
# check which model to choose
if not contour_model is None:
model = contour_model
else:
model = trimesh_model
support_grid_model = pycam.Toolpath.SupportGrid.get_support_distributed(
model, minz, support_grid_average_distance,
support_grid_minimum_bridges, support_grid_thickness,
support_grid_height, support_grid_length)
trimesh_model += support_grid_model
# Adapt the contour_model to the engraving offset. This offset is # Adapt the contour_model to the engraving offset. This offset is
# considered to be part of the material_allowance. # considered to be part of the material_allowance.
if (not contour_model is None) and (engrave_offset != 0): if (not contour_model is None) and (engrave_offset != 0):
......
...@@ -27,23 +27,33 @@ from pycam.Geometry.Model import Model ...@@ -27,23 +27,33 @@ from pycam.Geometry.Model import Model
from pycam.Geometry.utils import number from pycam.Geometry.utils import number
def _add_pyramid_to_model(model, start, direction, height, width): def _get_triangles_for_face(pts):
up = Vector(0, 0, 1)
top = start.add(direction).add(up.mul(height))
middle_end = start.add(direction)
end_right = middle_end.add(direction.cross(up).normalized().mul(width / 2))
end_left = middle_end.add(direction.cross(up).normalized().mul(-width / 2))
for points in ((start, top, end_right), (start, end_right, top),
(start, end_right, end_left), (top, end_left, end_right)):
model.append(Triangle(points[0], points[1], points[2]))
def _add_cuboid_to_model(minx, maxx, miny, maxy, minz, maxz):
def get_triangles_for_face(pts):
t1 = Triangle(pts[0], pts[1], pts[2], Line(pts[0], pts[1]), t1 = Triangle(pts[0], pts[1], pts[2], Line(pts[0], pts[1]),
Line(pts[1], pts[2]), Line(pts[2], pts[0])) Line(pts[1], pts[2]), Line(pts[2], pts[0]))
t2 = Triangle(pts[2], pts[3], pts[0], Line(pts[2], pts[3]), t2 = Triangle(pts[2], pts[3], pts[0], Line(pts[2], pts[3]),
Line(pts[3], pts[0]), Line(pts[0], pts[2])) Line(pts[3], pts[0]), Line(pts[0], pts[2]))
return (t1, t2) return (t1, t2)
def _add_cuboid_to_model(model, start, direction, height, width):
up = Vector(0, 0, 1).mul(height)
ortho_dir = direction.cross(up).normalized()
start1 = start.add(ortho_dir.mul(width/2))
start2 = start1.add(up)
start3 = start2.add(ortho_dir.mul(-width))
start4 = start3.sub(up)
end1 = start1.add(direction)
end2 = start2.add(direction)
end3 = start3.add(direction)
end4 = start4.add(direction)
faces = ((start1, start2, start3, start4), (start1, end1, end2, start2),
(start2, end2, end3, start3), (start3, end3, end4, start4),
(start4, end4, end1, start1), (end4, end3, end2, end1))
for face in faces:
t1, t2 = _get_triangles_for_face(face)
model.append(t1)
model.append(t2)
def _add_aligned_cuboid_to_model(minx, maxx, miny, maxy, minz, maxz):
points = ( points = (
Point(minx, miny, minz), Point(minx, miny, minz),
Point(maxx, miny, minz), Point(maxx, miny, minz),
...@@ -55,22 +65,22 @@ def _add_cuboid_to_model(minx, maxx, miny, maxy, minz, maxz): ...@@ -55,22 +65,22 @@ def _add_cuboid_to_model(minx, maxx, miny, maxy, minz, maxz):
Point(minx, maxy, maxz)) Point(minx, maxy, maxz))
triangles = [] triangles = []
# lower face # lower face
triangles.extend(get_triangles_for_face( triangles.extend(_get_triangles_for_face(
(points[0], points[1], points[2], points[3]))) (points[0], points[1], points[2], points[3])))
# upper face # upper face
triangles.extend(get_triangles_for_face( triangles.extend(_get_triangles_for_face(
(points[4], points[5], points[6], points[7]))) (points[4], points[5], points[6], points[7])))
# front face # front face
triangles.extend(get_triangles_for_face( triangles.extend(_get_triangles_for_face(
(points[0], points[1], points[5], points[4]))) (points[0], points[1], points[5], points[4])))
# back face # back face
triangles.extend(get_triangles_for_face( triangles.extend(_get_triangles_for_face(
(points[2], points[3], points[7], points[6]))) (points[2], points[3], points[7], points[6])))
# right face # right face
triangles.extend(get_triangles_for_face( triangles.extend(_get_triangles_for_face(
(points[1], points[2], points[6], points[5]))) (points[1], points[2], points[6], points[5])))
# left face # left face
triangles.extend(get_triangles_for_face( triangles.extend(_get_triangles_for_face(
(points[3], points[0], points[4], points[7]))) (points[3], points[0], points[4], points[7])))
# add all triangles to the model # add all triangles to the model
model = Model() model = Model()
...@@ -131,19 +141,27 @@ def get_support_grid(minx, maxx, miny, maxy, z_plane, dist_x, dist_y, thickness, ...@@ -131,19 +141,27 @@ def get_support_grid(minx, maxx, miny, maxy, z_plane, dist_x, dist_y, thickness,
length_extension = max(thickness, height) length_extension = max(thickness, height)
for line_x in lines_x: for line_x in lines_x:
# we make the grid slightly longer (by thickness) than necessary # we make the grid slightly longer (by thickness) than necessary
grid_model += _add_cuboid_to_model(line_x - thick_half, grid_model += _add_aligned_cuboid_to_model(line_x - thick_half,
line_x + thick_half, miny - length_extension, line_x + thick_half, miny - length_extension,
maxy + length_extension, z_plane, z_plane + height) maxy + length_extension, z_plane, z_plane + height)
for line_y in lines_y: for line_y in lines_y:
# we make the grid slightly longer (by thickness) than necessary # we make the grid slightly longer (by thickness) than necessary
grid_model += _add_cuboid_to_model(minx - length_extension, grid_model += _add_aligned_cuboid_to_model(minx - length_extension,
maxx + length_extension, line_y - thick_half, maxx + length_extension, line_y - thick_half,
line_y + thick_half, z_plane, z_plane + height) line_y + thick_half, z_plane, z_plane + height)
return grid_model return grid_model
def get_distributed_support_bridges(model, z_plane, average_distance, def get_support_distributed(model, z_plane, average_distance,
min_bridges_per_polygon, thickness, height): min_bridges_per_polygon, thickness, height, length):
def is_near_list(point_list, point, distance):
for p in point_list:
if p.sub(point).norm <= distance:
return True
return False
result = Model() result = Model()
bridge_positions = []
# minimum required distance between two bridge start points
avoid_distance = 1.5 * (abs(length) + thickness)
for polygon in model.get_polygons(): for polygon in model.get_polygons():
if not polygon.is_outer(): if not polygon.is_outer():
continue continue
...@@ -160,7 +178,9 @@ def get_distributed_support_bridges(model, z_plane, average_distance, ...@@ -160,7 +178,9 @@ def get_distributed_support_bridges(model, z_plane, average_distance,
while len(positions) < num_of_bridges: while len(positions) < num_of_bridges:
current_line_index += 1 current_line_index += 1
current_line_index %= len(poly_lengths) current_line_index %= len(poly_lengths)
while distance_processed + poly_lengths[current_line_index] < real_average_distance: # skip lines that are not at least twice as long as the grid width
while (distance_processed + poly_lengths[current_line_index] \
< real_average_distance):
distance_processed += poly_lengths[current_line_index] distance_processed += poly_lengths[current_line_index]
current_line_index += 1 current_line_index += 1
current_line_index %= len(poly_lengths) current_line_index %= len(poly_lengths)
...@@ -168,11 +188,27 @@ def get_distributed_support_bridges(model, z_plane, average_distance, ...@@ -168,11 +188,27 @@ def get_distributed_support_bridges(model, z_plane, average_distance,
distance_processed += poly_lengths[current_line_index] distance_processed += poly_lengths[current_line_index]
distance_processed %= real_average_distance distance_processed %= real_average_distance
for line_index in positions: for line_index in positions:
bridge_dir = lines[line_index].dir.cross(polygon.plane.n) position = polygon.get_middle_of_line(line_index)
# TODO: should we ask for a length? # skip bridges that are close to another existing bridge
length = average_distance / 4 if is_near_list(bridge_positions, position, avoid_distance):
_add_pyramid_to_model(result, line = polygon.get_lines()[line_index]
polygon.get_middle_of_line(line_index), bridge_dir, # calculate two alternative points on the same line
height, thickness) position1 = position.add(line.p1).div(2)
position2 = position.add(line.p2).div(2)
if is_near_list(bridge_positions, position1, avoid_distance):
if is_near_list(bridge_positions, position2,
avoid_distance):
# no valid alternative - we skip this bridge
continue
else:
# position2 is OK
position = position2
else:
# position1 is OK
position = position1
bridge_positions.append(position)
bridge_dir = lines[line_index].dir.cross(
polygon.plane.n).normalized().mul(length)
_add_cuboid_to_model(result, position, bridge_dir, height, thickness)
return result return result
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