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:
if (s.get("support_grid_thickness") > 0) \
and (s.get("support_grid_height") > 0) \
and (s.get("support_grid_average_distance") > 0) \
and (s.get("support_grid_minimum_bridges") > 0) \
and (s.get("support_grid_length") > 0):
support_grid = pycam.Toolpath.SupportGrid.get_distributed_support_bridges(
and (s.get("support_grid_minimum_bridges") > 0):
support_grid = pycam.Toolpath.SupportGrid.get_support_distributed(
s.get("model"), s.get("minz"),
s.get("support_grid_average_distance"),
s.get("support_grid_minimum_bridges"),
......@@ -816,6 +815,7 @@ class ProjectGui:
elif grid_type == GRID_TYPES["none"]:
pass
s.set("support_grid", support_grid)
self.update_view()
def switch_support_grid_manual_selector(self, widget=None):
old_axis_was_x = self.grid_adjustment_axis_x_last
......@@ -2604,7 +2604,7 @@ class ProjectGui:
adjustments_x=self.grid_adjustments_x,
adjustments_y=self.grid_adjustments_y)
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_minimum_bridges"),
self.settings.get("support_grid_thickness"),
......
......@@ -563,6 +563,7 @@ class ToolpathSettings:
"feedrate": float,
},
"SupportGrid": {
"type": str,
"distance_x": float,
"distance_y": float,
"thickness": float,
......@@ -571,6 +572,9 @@ class ToolpathSettings:
"offset_y": float,
"adjustments_x": "list_of_float",
"adjustments_y": "list_of_float",
"average_distance": float,
"minimum_bridges": int,
"length": float,
},
"Program": {
"unit": str,
......@@ -637,6 +641,7 @@ class ToolpathSettings:
adjustments_x = []
if adjustments_y is None:
adjustments_y = []
self.support_grid["type"] = "grid"
self.support_grid["distance_x"] = distance_x
self.support_grid["distance_y"] = distance_y
self.support_grid["offset_x"] = offset_x
......@@ -646,14 +651,28 @@ class ToolpathSettings:
self.support_grid["adjustments_x"] = adjustments_x
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):
result = {}
if self.support_grid:
return self.support_grid
options = self.support_grid
else:
result = {}
for key in self.SECTIONS["SupportGrid"].keys():
options = {}
# add all keys from the default list
for key in self.SECTIONS["SupportGrid"].keys():
if options.has_key(key):
result[key] = options[key]
else:
result[key] = None
return result
return result
def set_calculation_backend(self, backend=None):
self.program["enable_ode"] = (backend.upper() == "ODE")
......
......@@ -50,20 +50,23 @@ def generate_toolpath_from_settings(model, tp_settings, callback=None):
process["generator"], process["postprocessor"],
process["material_allowance"], process["safety_height"],
process["overlap"], process["step_down"], process["engrave_offset"],
grid["distance_x"], grid["distance_y"], grid["thickness"],
grid["height"], grid["offset_x"], grid["offset_y"],
grid["adjustments_x"], grid["adjustments_y"], backend,
callback)
grid["type"], grid["distance_x"], grid["distance_y"],
grid["thickness"], grid["height"], grid["offset_x"],
grid["offset_y"], grid["adjustments_x"], grid["adjustments_y"],
grid["average_distance"], grid["minimum_bridges"], grid["length"],
backend, callback)
def generate_toolpath(model, tool_settings=None,
bounds_low=None, bounds_high=None, direction="x",
path_generator="DropCutter", path_postprocessor="ZigZagCutter",
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_height=None, support_grid_offset_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
@type model: pycam.Geometry.Model.Model
......@@ -143,7 +146,8 @@ def generate_toolpath(model, tool_settings=None,
# material allowance is ignored for engraving
material_allowance = 0
# 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)) \
and (not support_grid_thickness is None)):
# grid height defaults to the thickness
......@@ -168,6 +172,35 @@ def generate_toolpath(model, tool_settings=None,
adjustments_x=support_grid_adjustments_x,
adjustments_y=support_grid_adjustments_y)
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
# considered to be part of the material_allowance.
if (not contour_model is None) and (engrave_offset != 0):
......
......@@ -27,23 +27,33 @@ from pycam.Geometry.Model import Model
from pycam.Geometry.utils import number
def _add_pyramid_to_model(model, start, direction, height, width):
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 _get_triangles_for_face(pts):
t1 = Triangle(pts[0], pts[1], pts[2], Line(pts[0], pts[1]),
Line(pts[1], pts[2]), Line(pts[2], pts[0]))
t2 = Triangle(pts[2], pts[3], pts[0], Line(pts[2], pts[3]),
Line(pts[3], pts[0]), Line(pts[0], pts[2]))
return (t1, t2)
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]),
Line(pts[1], pts[2]), Line(pts[2], pts[0]))
t2 = Triangle(pts[2], pts[3], pts[0], Line(pts[2], pts[3]),
Line(pts[3], pts[0]), Line(pts[0], pts[2]))
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 = (
Point(minx, miny, minz),
Point(maxx, miny, minz),
......@@ -55,22 +65,22 @@ def _add_cuboid_to_model(minx, maxx, miny, maxy, minz, maxz):
Point(minx, maxy, maxz))
triangles = []
# lower face
triangles.extend(get_triangles_for_face(
triangles.extend(_get_triangles_for_face(
(points[0], points[1], points[2], points[3])))
# upper face
triangles.extend(get_triangles_for_face(
triangles.extend(_get_triangles_for_face(
(points[4], points[5], points[6], points[7])))
# front face
triangles.extend(get_triangles_for_face(
triangles.extend(_get_triangles_for_face(
(points[0], points[1], points[5], points[4])))
# back face
triangles.extend(get_triangles_for_face(
triangles.extend(_get_triangles_for_face(
(points[2], points[3], points[7], points[6])))
# right face
triangles.extend(get_triangles_for_face(
triangles.extend(_get_triangles_for_face(
(points[1], points[2], points[6], points[5])))
# left face
triangles.extend(get_triangles_for_face(
triangles.extend(_get_triangles_for_face(
(points[3], points[0], points[4], points[7])))
# add all triangles to the model
model = Model()
......@@ -131,19 +141,27 @@ def get_support_grid(minx, maxx, miny, maxy, z_plane, dist_x, dist_y, thickness,
length_extension = max(thickness, height)
for line_x in lines_x:
# 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,
maxy + length_extension, z_plane, z_plane + height)
for line_y in lines_y:
# 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,
line_y + thick_half, z_plane, z_plane + height)
return grid_model
def get_distributed_support_bridges(model, z_plane, average_distance,
min_bridges_per_polygon, thickness, height):
def get_support_distributed(model, z_plane, average_distance,
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()
bridge_positions = []
# minimum required distance between two bridge start points
avoid_distance = 1.5 * (abs(length) + thickness)
for polygon in model.get_polygons():
if not polygon.is_outer():
continue
......@@ -160,7 +178,9 @@ def get_distributed_support_bridges(model, z_plane, average_distance,
while len(positions) < num_of_bridges:
current_line_index += 1
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]
current_line_index += 1
current_line_index %= len(poly_lengths)
......@@ -168,11 +188,27 @@ def get_distributed_support_bridges(model, z_plane, average_distance,
distance_processed += poly_lengths[current_line_index]
distance_processed %= real_average_distance
for line_index in positions:
bridge_dir = lines[line_index].dir.cross(polygon.plane.n)
# TODO: should we ask for a length?
length = average_distance / 4
_add_pyramid_to_model(result,
polygon.get_middle_of_line(line_index), bridge_dir,
height, thickness)
position = polygon.get_middle_of_line(line_index)
# skip bridges that are close to another existing bridge
if is_near_list(bridge_positions, position, avoid_distance):
line = polygon.get_lines()[line_index]
# calculate two alternative points on the same line
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
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