Commit dfbffd2b authored by sumpfralle's avatar sumpfralle

more style fixes

disabled "namedtuple" for now (it was never in effect)


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@975 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent f84adff7
......@@ -3,7 +3,7 @@
$Id$
Copyright 2008-2010 Lode Leroy
Copyright 2010 Lars Kruse <devel@sumpfralle.de>
Copyright 2010-2011 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
......@@ -22,11 +22,10 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
import pycam.Utils.threading
from pycam.Geometry.Point import Point
from pycam.Geometry.utils import number, INFINITE, epsilon
from pycam.Geometry.intersection import intersect_circle_point, \
intersect_cylinder_point, intersect_cylinder_line
from pycam.Geometry.intersection import intersect_cylinder_point, \
intersect_cylinder_line
import uuid
......@@ -49,7 +48,8 @@ class BaseCutter(object):
self.distance_radius = self.radius
self.distance_radiussq = self.distance_radius ** 2
self.shape = {}
self.moveto(location)
self.location = location
self.moveto(self.location)
self.uuid = None
self.update_uuid()
......@@ -131,8 +131,7 @@ class BaseCutter(object):
* triangle.radius + triangle.radiussq) + epsilon:
return None
(cl, d, cp) = self.intersect(BaseCutter.vertical, triangle, start=start)
return cl
return self.intersect(BaseCutter.vertical, triangle, start=start)[0]
def intersect_circle_triangle(self, direction, triangle, start=None):
(cl, ccp, cp, d) = self.intersect_circle_plane(direction, triangle,
......
......@@ -131,7 +131,8 @@ class CylindricalCutter(BaseCutter):
def moveto(self, location, **kwargs):
BaseCutter.moveto(self, location, **kwargs)
self.center = Point(location.x, location.y, location.z - self.get_required_distance())
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:
......@@ -166,10 +167,6 @@ class CylindricalCutter(BaseCutter):
return (cl, ccp, cp, l)
return (None, None, None, INFINITE)
def intersect_plane(self, direction, triangle, start=None):
# TODO: are "intersect_plane" and "self.intersect_circle_plane" obsolete?
return self.intersect_circle_plane(direction, triangle, start=start)
def intersect(self, direction, triangle, start=None):
(cl_t, d_t, cp_t) = self.intersect_circle_triangle(direction, triangle,
start=start)
......
......@@ -128,7 +128,8 @@ class ToroidalCutter(BaseCutter):
return (None, None, None, INFINITE)
def intersect_torus_triangle(self, direction, triangle, start=None):
(cl, ccp, cp, d) = self.intersect_torus_plane(direction, triangle, start=start)
(cl, ccp, cp, d) = self.intersect_torus_plane(direction, triangle,
start=start)
if cp and triangle.is_point_inside(cp):
return (cl, d, cp)
return (None, INFINITE, None)
......
......@@ -27,11 +27,12 @@ import os
class STLExporter:
def __init__(self, model, name="model", created_by="pycam", linesep=None, comment=None):
def __init__(self, model, name="model", created_by="pycam", linesep=None,
comment=None):
# sadly STL does not seem to support comments
self.model = model
self.name = name
self.created_by = created_by
self.comment = comment
if linesep is None:
self.linesep = os.linesep
else:
......@@ -49,20 +50,14 @@ class STLExporter:
date = datetime.date.today().isoformat()
yield """solid "%s"; Produced by %s (v%s), %s""" \
% (self.name, self.created_by, VERSION, date)
# sadly STL does not seem to support comments
"""
if self.comment:
for line in self.comment.split(self.linesep):
yield(";%s" % line)
"""
for tr in self.model.triangles():
norm = tr.normal.normalized()
for triangle in self.model.triangles():
norm = triangle.normal.normalized()
yield "facet normal %f %f %f" % (norm.x, norm.y, norm.z)
yield " outer loop"
# Triangle vertices are stored in clockwise order - thus we need
# to reverse the order (STL expects counter-clockwise orientation).
for p in (tr.p3, tr.p2, tr.p1):
yield " vertex %f %f %f" % (p.x, p.y, p.z)
for point in (triangle.p3, triangle.p2, triangle.p1):
yield " vertex %f %f %f" % (point.x, point.y, point.z)
yield " endloop"
yield "endfacet"
yield "endsolid"
......
......@@ -29,7 +29,7 @@ class SVGExporter:
def __init__(self, output, unit="mm"):
if isinstance(output, basestring):
# a filename was given
self.output = file(filename,"w")
self.output = file(output,"w")
else:
# a stream was given
self.output = output
......
......@@ -44,15 +44,22 @@ class gcode:
"G04 P3 T%d M6" % self.tool_id,
"G00 Z%.4f" % self.safetyheight)
def end(self):
return "M2"
def exactpath(self):
return "G61"
def continuous(self):
return "G64"
def rapid(self, x = None, y = None, z = None, a = None, gcode = "G00",
feed=None):
gcodestring = feedstring = xstring = ystring = zstring = astring = ""
def rapid(self, x=None, y=None, z=None, a=None, code="G00", feed=None):
gcodestring = ""
feedstring = ""
xstring = ""
ystring = ""
zstring = ""
astring = ""
if x == None:
x = self.lastx
if y == None:
......@@ -61,9 +68,9 @@ class gcode:
z = self.lastz
if a == None:
a = self.lasta
if gcode != self.lastgcode:
gcodestring = gcode
self.lastgcode = gcode
if code != self.lastgcode:
gcodestring = code
self.lastgcode = code
if x != self.lastx:
xstring = " X%.4f" % (x)
self.lastx = x
......@@ -76,7 +83,7 @@ class gcode:
if a != self.lasta:
astring = " A%.4f" % (a)
self.lasta = a
if (gcode == "G01") and feed and (feed != self.lastfeed):
if (code == "G01") and feed and (feed != self.lastfeed):
feedstring = " F%.4f" % (feed)
self.lastfeed = feed
positionstring = xstring + ystring + zstring + astring
......@@ -90,7 +97,7 @@ class gcode:
if y == None: y = self.lasty
if z == None: z = self.lastz
if a == None: a = self.lasta
return self.rapid(x, y, z, a, gcode="G01", feed=feed)
return self.rapid(x, y, z, a, code="G01", feed=feed)
def safety(self):
return self.rapid(z=self.safetyheight)
......
......@@ -27,12 +27,10 @@ from pycam.Geometry.Plane import Plane
from pycam.Geometry.utils import epsilon, sqrt
# OpenGLTools will be imported later, if necessary
#import pycam.Gui.OpenGLTools
import math
try:
import OpenGL.GL as GL
import OpenGL.GLUT as GLUT
GL_enabled = True
except ImportError:
GL_enabled = False
......@@ -42,6 +40,7 @@ class Line(TransformableContainer):
id = 0
def __init__(self, p1, p2):
super(Line, self).__init__()
self.id = Line.id
Line.id += 1
self.p1 = p1
......
......@@ -27,7 +27,7 @@ from pycam.Geometry.Triangle import Triangle
from pycam.Geometry.Line import Line
from pycam.Geometry.Plane import Plane
from pycam.Geometry.Polygon import Polygon
from pycam.Geometry.Point import Point, Vector
from pycam.Geometry.Point import Point
from pycam.Geometry.TriangleKdtree import TriangleKdtree
from pycam.Geometry.Matrix import TRANSFORMATIONS
from pycam.Toolpath import Bounds
......@@ -110,7 +110,8 @@ class BaseModel(TransformableContainer):
+ "support the 'export' function.") % str(type(self)))
def _update_limits(self, item):
# ignore items without limit attributes (e.g. the normal of a ContourModel)
# Ignore items without limit attributes (e.g. the normal of a
# ContourModel).
if hasattr(item, "minx"):
if self.minx is None:
self.minx = item.minx
......@@ -313,7 +314,8 @@ class ContourModel(BaseModel):
self._plane_groups = [self._plane]
self._item_groups.append(self._plane_groups)
self._cached_offset_models = {}
self._export_function = pycam.Exporters.SVGExporter.SVGExporterContourModel
self._export_function = \
pycam.Exporters.SVGExporter.SVGExporterContourModel
def reset_cache(self):
super(ContourModel, self).reset_cache()
......@@ -409,8 +411,10 @@ class ContourModel(BaseModel):
new_queue = processed
while len(self._line_groups) > 0:
self._line_groups.pop()
print "Processed polygons: %s" % str([len(p.get_lines()) for p in processed_polygons])
print "New queue: %s" % str([len(p.get_lines()) for p in new_queue])
print "Processed polygons: %s" % str([len(p.get_lines())
for p in processed_polygons])
print "New queue: %s" % str([len(p.get_lines())
for p in new_queue])
for processed_polygon in processed_polygons + new_queue:
self._line_groups.append(processed_polygon)
# TODO: this is quite expensive - can we do it differently?
......
......@@ -29,8 +29,10 @@ This reduces the memory consumption of a toolpath down to 1/3.
try:
# this works for python 2.6 or above (saves memory)
import collections.namedtuple
tuple_point = collections.namedtuple("TuplePoint", "x y z")
# TODO: disabled for now - check if we could enable it later ...
import INVALID_IMPORT
from collections import namedtuple
tuple_point = namedtuple("TuplePoint", "x y z")
get_point_object = lambda point: tuple_point(point.x, point.y, point.z)
except ImportError:
# dummy for python < v2.6 (consumes more memory)
......@@ -48,22 +50,22 @@ class Path:
self.points = []
def __repr__(self):
s = ""
s += "path %d: " % self.id
text = ""
text += "path %d: " % self.id
first = True
for p in self.points:
for point in self.points:
if first:
first = False
else:
s += "-"
s += "%d(%g,%g,%g)" % (p.id, p.x, p.y, p.z)
return s
text += "-"
text += "%d(%g,%g,%g)" % (point.id, point.x, point.y, point.z)
return text
def insert(self, index, p):
self.points.insert(index, get_point_object(p))
def insert(self, index, point):
self.points.insert(index, get_point_object(point))
def append(self, p):
self.points.append(get_point_object(p))
def append(self, point):
self.points.append(get_point_object(point))
def reverse(self):
self.points.reverse()
......@@ -22,18 +22,19 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
from pycam.Geometry import TransformableContainer
from pycam.Geometry.utils import INFINITE, epsilon
from pycam.Geometry.Point import Point, Vector
from pycam.Geometry.Point import Vector
# "Line" is imported later to avoid circular imports
#from pycam.Geometry.Line import Line
class Plane(TransformableContainer):
id = 0
def __init__(self, p, n):
def __init__(self, point, normal):
super(Plane, self).__init__()
self.id = Plane.id
Plane.id += 1
self.p = p
self.n = n
self.p = point
self.n = normal
if not isinstance(self.n, Vector):
self.n = self.n.get_vector()
......@@ -123,8 +124,7 @@ class Plane(TransformableContainer):
return None
def get_point_projection(self, point):
p, dist = self.intersect_point(self.n, point)
return p
return self.intersect_point(self.n, point)[0]
def get_line_projection(self, line):
# don't import Line in the header -> circular import
......
......@@ -25,9 +25,7 @@ from pycam.Geometry.Point import Point, Vector
from pycam.Geometry.Plane import Plane
from pycam.Geometry import TransformableContainer, get_bisector
from pycam.Geometry.utils import number, epsilon
import pycam.Geometry.Matrix as Matrix
import pycam.Utils.log
import math
# import later to avoid circular imports
#from pycam.Geometry.Model import ContourModel
......@@ -69,7 +67,8 @@ class Polygon(TransformableContainer):
self._update_limits(line.p2)
elif self._points[-1] == line.p1:
# the new Line can be added to the end of the polygon
if line.dir == self._points[-1].sub(self._points[-2]).normalized():
if line.dir == self._points[-1].sub(
self._points[-2]).normalized():
# Remove the last point, if the previous point combination
# is in line with the new Line. This avoids unnecessary
# points on straight lines.
......@@ -84,7 +83,8 @@ class Polygon(TransformableContainer):
else:
# the new Line can be added to the beginning of the polygon
if (len(self._points) > 1) \
and (line.dir == self._points[1].sub(self._points[0]).normalized()):
and (line.dir == self._points[1].sub(
self._points[0]).normalized()):
# Avoid points on straight lines - see above.
self._points.pop(0)
if line.p1 != self._points[-1]:
......@@ -142,7 +142,6 @@ class Polygon(TransformableContainer):
Currently this works only for line groups in an xy-plane.
Returns zero for empty line groups or for open line groups.
Returns negative values for inner hole.
TODO: "get_area" is wrong by some factor - check the result!
"""
if not self._points:
return 0
......@@ -227,7 +226,8 @@ class Polygon(TransformableContainer):
or (not self.is_closed and index == len(self._points) - 1):
return None
else:
return self._points[index].add(self._points[(index + 1) % len(self._points)]).div(2)
return self._points[index].add(self._points[(index + 1) % \
len(self._points)]).div(2)
def get_lengths(self):
result = []
......@@ -324,7 +324,8 @@ class Polygon(TransformableContainer):
lines = []
for index in range(len(self._points) - 1):
lines.append(Line(self._points[index], self._points[index + 1]))
# connect the last point with the first only if the polygon is closed
# Connect the last point with the first only if the polygon is
# closed.
if self.is_closed:
lines.append(Line(self._points[-1], self._points[0]))
self._lines_cache = lines
......@@ -333,16 +334,6 @@ class Polygon(TransformableContainer):
def to_OpenGL(self, **kwords):
for line in self.get_lines():
line.to_OpenGL(**kwords)
return
offset_polygons = self.get_offset_polygons(0.2)
for polygon in offset_polygons:
for line in polygon.get_lines():
line.to_OpenGL(**kwords)
"""
for index, point in enumerate(self._points):
line = Line(point, point.add(self.get_bisector(index)))
line.get_length_line(1).to_OpenGL()
"""
def _update_limits(self, point):
if self.minx is None:
......@@ -452,9 +443,12 @@ class Polygon(TransformableContainer):
shifted_lines[prev_index] = (False, Line(prev_line.p1, cp))
shifted_lines[next_index] = (False, Line(cp, next_line.p2))
else:
cp, dist = prev_line.get_intersection(next_line, infinite_lines=True)
raise BaseException("Expected intersection not found: " \
+ "%s - %s - %s(%d) / %s(%d)" % (cp, shifted_lines[prev_index+1:next_index], prev_line, prev_index, next_line, next_index))
cp, dist = prev_line.get_intersection(next_line,
infinite_lines=True)
raise BaseException("Expected intersection not found: " + \
"%s - %s - %s(%d) / %s(%d)" % \
(cp, shifted_lines[prev_index+1:next_index],
prev_line, prev_index, next_line, next_index))
if index > next_index:
# we wrapped around the end of the list
break
......@@ -603,7 +597,8 @@ class Polygon(TransformableContainer):
for cached_offset in self._cached_offset_polygons:
if is_better_offset(best_offset, cached_offset):
best_offset = cached_offset
best_offset_polygons = self._cached_offset_polygons[cached_offset]
best_offset_polygons = \
self._cached_offset_polygons[cached_offset]
remaining_offset = offset - best_offset
result_polygons = []
for poly in best_offset_polygons:
......@@ -780,14 +775,16 @@ class Polygon(TransformableContainer):
# We ignore groups that changed the direction. These
# parts of the original group are flipped due to the
# offset.
log.debug("Ignoring reversed polygon: %s / %s" % (self.get_area(), group.get_area()))
log.debug("Ignoring reversed polygon: %s / %s" % \
(self.get_area(), group.get_area()))
continue
# Remove polygons that should be inside the original,
# but due to float inaccuracies they are not.
if ((self.is_outer() and (offset < 0)) \
or (not self.is_outer() and (offset > 0))) \
and (not self.is_polygon_inside(group)):
log.debug("Ignoring inaccurate polygon: %s / %s" % (self.get_area(), group.get_area()))
log.debug("Ignoring inaccurate polygon: %s / %s" \
% (self.get_area(), group.get_area()))
continue
groups.append(group)
if not groups:
......
......@@ -39,11 +39,14 @@ else:
ceil = lambda value: int(math.ceil(value))
# return "0" for "-epsilon < value < 0" (to work around floating inaccuracies)
# otherwise: return the sqrt function of the current type (could even raise exceptions)
# otherwise: return the sqrt function of the current type (could even raise
# exceptions)
if _use_precision:
sqrt = lambda value: (((value < -epsilon) or (value > 0)) and value.sqrt()) or 0
sqrt = lambda value: (((value < -epsilon) or (value > 0)) and \
value.sqrt()) or 0
else:
sqrt = lambda value: (((value < -epsilon) or (value > 0)) and math.sqrt(value)) or 0
sqrt = lambda value: (((value < -epsilon) or (value > 0)) and \
math.sqrt(value)) or 0
if _use_precision:
number = lambda value: decimal.Decimal(str(value))
......
......@@ -2,7 +2,7 @@
"""
$Id$
Copyright 2010 Lars Kruse <devel@sumpfralle.de>
Copyright 2010-2011 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
......@@ -32,13 +32,11 @@ except (ImportError, RuntimeError):
from pycam.Geometry.Point import Point
import pycam.Geometry.Matrix as Matrix
from pycam.Geometry.utils import sqrt, number, epsilon
from pycam.Geometry.utils import sqrt, number
import pycam.Utils.log
import gtk
import gobject
import pango
import math
import time
# buttons for rotating, moving and zooming the model view window
BUTTON_ROTATE = gtk.gdk.BUTTON1_MASK
......@@ -237,12 +235,13 @@ class Camera:
# This distance calculation is completely based on trial-and-error.
distance = math.sqrt(sum([d ** 2 for d in v["distance"]]))
distance *= math.log(math.sqrt(width * height)) / math.log(10)
left = v["center"][0] - math.sin(v["fovy"] / 360.0 * math.pi) * distance
right = v["center"][0] + math.sin(v["fovy"] / 360.0 * math.pi) * distance
top = v["center"][1] + math.sin(v["fovy"] / 360.0 * math.pi) * distance
bottom = v["center"][1] - math.sin(v["fovy"] / 360.0 * math.pi) * distance
near = v["center"][2] - 2 * math.sin(v["fovy"] / 360.0 * math.pi) * distance
far = v["center"][2] + 2 * math.sin(v["fovy"] / 360.0 * math.pi) * distance
sin_factor = math.sin(v["fovy"] / 360.0 * math.pi) * distance
left = v["center"][0] - sin_factor
right = v["center"][0] + sin_factor
top = v["center"][1] + sin_factor
bottom = v["center"][1] - sin_factor
near = v["center"][2] - 2 * sin_factor
far = v["center"][2] + 2 * sin_factor
GL.glOrtho(left, right, bottom, top, near, far)
GLU.gluLookAt(camera_position[0], camera_position[1],
camera_position[2], v["center"][0], v["center"][1],
......@@ -383,9 +382,10 @@ class ModelViewWindowGL:
for action in context_menu_actions:
action_group.add_action(action)
uimanager = gtk.UIManager()
# the "pos" parameter is optional since 2.12 - we can remove it later
# The "pos" parameter is optional since gtk 2.12 - we can
# remove it later.
uimanager.insert_action_group(action_group, pos=-1)
uimanager_template = """<ui><popup name="context">%s</popup></ui>"""
uimanager_template = '<ui><popup name="context">%s</popup></ui>'
uimanager_item_template = """<menuitem action="%s" />"""
uimanager_text = uimanager_template % "".join(
[uimanager_item_template % action.get_name()
......@@ -442,12 +442,6 @@ class ModelViewWindowGL:
ord("K"): (0, 1),
ord("L"): (-1, 0),
}
def get_char(value):
# avoid exceptions
if 0 <= value <= 255:
return chr(value)
else:
return None
if key_string and (key_string in '1234567'):
names = ["reset", "front", "back", "left", "right", "top", "bottom"]
index = '1234567'.index(key_string)
......@@ -603,7 +597,8 @@ class ModelViewWindowGL:
and (abs(event.y - self.mouse["pressed_pos"][1]) < 3):
# A quick press/release cycle with the right mouse button
# -> open the context menu.
self.context_menu.popup(None, None, None, event.button, int(event.get_time()))
self.context_menu.popup(None, None, None, event.button,
int(event.get_time()))
@check_busy
@gtkgl_functionwrapper
......@@ -907,11 +902,11 @@ def draw_complete_model_view(settings):
# draw the model
if settings.get("show_model") \
and not (settings.get("show_simulation") \
and settings.get("simulation_toolpath_moves")):
and settings.get("simulation_toolpath_moves")):
GL.glColor4f(*settings.get("color_model"))
model = settings.get("model")
min_area = abs(model.maxx - model.minx) * abs(model.maxy - model.miny) / 100
"""
min_area = abs(model.maxx - model.minx) * abs(model.maxy - model.miny) / 100
# example for coloring specific triangles
groups = model.get_flat_areas(min_area)
all_flat_ids = []
......@@ -967,9 +962,9 @@ def draw_complete_model_view(settings):
for point in path.points:
moves.append((point, False))
if not toolpath_in_progress is None:
draw_toolpath(moves, settings.get("color_toolpath_cut"),
settings.get("color_toolpath_return"),
show_directions=settings.get("show_directions"))
draw_toolpath(moves, settings.get("color_toolpath_cut"),
settings.get("color_toolpath_return"),
show_directions=settings.get("show_directions"))
@keep_gl_mode
@keep_matrix
......
......@@ -60,6 +60,7 @@ import logging
import datetime
import traceback
import random
import math
import re
import os
import sys
......@@ -569,22 +570,19 @@ class ProjectGui:
support_grid_type_control = self.gui.get_object(
"SupportGridTypesControl")
support_grid_type_control.connect("changed",
self.update_support_grid_controls)
self.update_support_controls)
self.settings.add_item("support_grid_type",
support_grid_type_control.get_active,
support_grid_type_control.set_active)
self.settings.set("support_grid_type", GRID_TYPES["none"])
grid_distance_x = self.gui.get_object("SupportGridDistanceX")
grid_distance_x.connect("value-changed",
self.update_support_grid_controls)
grid_distance_x.connect("value-changed", self.update_support_controls)
self.settings.add_item("support_grid_distance_x",
grid_distance_x.get_value, grid_distance_x.set_value)
grid_distance_square = self.gui.get_object("SupportGridDistanceSquare")
grid_distance_square.connect("clicked",
self.update_support_grid_controls)
grid_distance_square.connect("clicked", self.update_support_controls)
grid_distance_y = self.gui.get_object("SupportGridDistanceY")
grid_distance_y.connect("value-changed",
self.update_support_grid_controls)
grid_distance_y.connect("value-changed", self.update_support_controls)
def get_support_grid_distance_y():
if grid_distance_square.get_active():
return self.settings.get("support_grid_distance_x")
......@@ -593,36 +591,33 @@ class ProjectGui:
self.settings.add_item("support_grid_distance_y",
get_support_grid_distance_y, grid_distance_y.set_value)
grid_thickness = self.gui.get_object("SupportGridThickness")
grid_thickness.connect("value-changed", self.update_support_grid_model)
grid_thickness.connect("value-changed", self.update_support_model)
self.settings.add_item("support_grid_thickness",
grid_thickness.get_value, grid_thickness.set_value)
grid_height = self.gui.get_object("SupportGridHeight")
grid_height.connect("value-changed", self.update_support_grid_model)
grid_height.connect("value-changed", self.update_support_model)
self.settings.add_item("support_grid_height",
grid_height.get_value, grid_height.set_value)
grid_length = self.gui.get_object("SupportGridLength")
grid_length.connect("value-changed", self.update_support_grid_model)
grid_length.connect("value-changed", self.update_support_model)
self.settings.add_item("support_grid_length",
grid_length.get_value, grid_length.set_value)
grid_offset_x = self.gui.get_object("SupportGridOffsetX")
grid_offset_x.connect("value-changed",
self.update_support_grid_model)
grid_offset_x.connect("value-changed", self.update_support_model)
self.settings.add_item("support_grid_offset_x",
grid_offset_x.get_value, grid_offset_x.set_value)
grid_offset_y = self.gui.get_object("SupportGridOffsetY")
grid_offset_y.connect("value-changed",
self.update_support_grid_model)
grid_offset_y.connect("value-changed", self.update_support_model)
self.settings.add_item("support_grid_offset_y",
grid_offset_y.get_value, grid_offset_y.set_value)
grid_average_distance = self.gui.get_object("GridAverageDistance")
grid_average_distance.connect("value-changed",
self.update_support_grid_model)
self.update_support_model)
self.settings.add_item("support_grid_average_distance",
grid_average_distance.get_value,
grid_average_distance.set_value)
grid_minimum_bridges = self.gui.get_object("GridMinBridgesPerPolygon")
grid_minimum_bridges.connect("value-changed",
self.update_support_grid_model)
grid_minimum_bridges.connect("value-changed", self.update_support_model)
self.settings.add_item("support_grid_minimum_bridges",
grid_minimum_bridges.get_value, grid_minimum_bridges.set_value)
# manual grid adjustments
......@@ -1069,7 +1064,7 @@ class ProjectGui:
self.update_tasklist_table()
self.update_save_actions()
self.update_unit_labels()
self.update_support_grid_controls()
self.update_support_controls()
self.update_scale_controls()
self.update_gcode_controls()
self.update_ode_settings()
......@@ -1117,8 +1112,9 @@ class ProjectGui:
self.gui_is_active = True
try:
result = func(self, *args, **kwargs)
except:
# catch possible exceptions and report them
except Exception:
# Catch possible exceptions (except system-exit ones) and
# report them.
report_exception()
result = None
self.gui_is_active = False
......@@ -1191,7 +1187,7 @@ class ProjectGui:
self.gui.get_object("SaveModel").set_sensitive(save_possible)
@gui_activity_guard
def update_support_grid_controls(self, widget=None):
def update_support_controls(self, widget=None):
controls = {"GridProfileExpander": ("grid", "automatic_edge",
"automatic_corner"),
"GridPatternExpander": ("grid", ),
......@@ -1226,10 +1222,10 @@ class ProjectGui:
obj.show()
else:
obj.hide()
self.update_support_grid_model()
self.update_support_model()
self.update_view()
def update_support_grid_model(self, widget=None):
def update_support_model(self, widget=None):
grid_type = self.settings.get("support_grid_type")
s = self.settings
support_grid = None
......@@ -1316,7 +1312,7 @@ class ProjectGui:
if not tree_iter is None:
value_string = "(%+.1f)" % new_value
self.grid_adjustment_model.set(tree_iter, 1, value_string)
self.update_support_grid_model()
self.update_support_model()
self.update_view()
def reset_support_grid_manual(self, widget=None, reset_all=False):
......@@ -1327,7 +1323,7 @@ class ProjectGui:
self.settings.set("support_grid_adjustment_value", 0)
self.update_support_grid_manual_model()
self.switch_support_grid_manual_selector()
self.update_support_grid_model()
self.update_support_model()
self.update_view()
def update_support_grid_manual_model(self):
......@@ -1545,7 +1541,7 @@ class ProjectGui:
# update the values in the manual support grid adjustment list
self.update_support_grid_manual_model()
# the support grid depends on the boundary
self.update_support_grid_model()
self.update_support_model()
self.update_view()
def update_tasklist_controls(self):
......@@ -1989,7 +1985,7 @@ class ProjectGui:
except UnicodeDecodeError:
# remove all non-ascii characters
clean_char = lambda c: (32 <= ord(c) < 128) and c or " "
message = "".join(map(clean_char, message))
message = "".join([clean_char(char) for char in message])
self.log_model.append((timestamp, title, message))
# update the status bar (if the GTK interface is still active)
if not self.status_bar.window is None:
......@@ -2608,7 +2604,7 @@ class ProjectGui:
self.disable_progress_cancel_button()
self.model.shift(shift_x, shift_y, shift_z,
callback=self.update_progress_bar)
self.update_support_grid_model()
self.update_support_model()
self.update_view()
def _get_model_center(self):
......@@ -2664,7 +2660,7 @@ class ProjectGui:
self.model.scale(factor, callback=self.update_progress_bar)
self._set_model_center(old_center)
self.append_to_queue(self.update_scale_controls)
self.append_to_queue(self.update_support_grid_model)
self.append_to_queue(self.update_support_model)
self.append_to_queue(self.update_view)
@gui_activity_guard
......@@ -2697,7 +2693,7 @@ class ProjectGui:
len(self.model.get_polygons()),
self.update_progress_bar).increment
self.model.reverse_directions(callback=progress_callback)
self.update_support_grid_model()
self.update_support_model()
@progress_activity_guard
@gui_activity_guard
......@@ -2729,7 +2725,7 @@ class ProjectGui:
callback=self.update_progress_bar)
# move the model to its previous center
self._set_model_center(old_center)
self.update_support_grid_model()
self.update_support_model()
self.update_view()
def destroy(self, widget=None, data=None):
......@@ -2848,7 +2844,7 @@ class ProjectGui:
def _update_all_model_attributes(self):
self.append_to_queue(self.update_scale_controls)
self.append_to_queue(self.update_model_type_related_controls)
self.append_to_queue(self.update_support_grid_controls)
self.append_to_queue(self.update_support_controls)
self.append_to_queue(self.toggle_3d_view, value=True)
self.append_to_queue(self.update_view)
......@@ -3199,7 +3195,7 @@ class ProjectGui:
self.update_view()
@progress_activity_guard
def crop_toolpath(self, tp):
def crop_toolpath(self, toolpath):
if hasattr(self.model, "get_polygons"):
contour = self.model
elif hasattr(self.model, "get_waterline_contour"):
......@@ -3208,13 +3204,13 @@ class ProjectGui:
contour = self.model.get_waterline_contour(plane)
self.update_progress_bar("Applying the tool diameter offset")
contour = contour.get_offset_model(
2 * tp.get_tool_settings()["tool_radius"])
2 * toolpath.get_tool_settings()["tool_radius"])
else:
log.warn(("The current model (%s) does not support " \
+ "projections") % str(type(self.model)))
return
self.update_progress_bar("Cropping the toolpath")
tp.crop(contour.get_polygons(), callback=self.update_progress_bar)
toolpath.crop(contour.get_polygons(), callback=self.update_progress_bar)
def update_toolpath_table(self, new_index=None, skip_model_update=False):
def get_time_string(minutes):
......@@ -3548,7 +3544,8 @@ class ProjectGui:
try:
toolpath = pycam.Toolpath.Generator.generate_toolpath_from_settings(
self.model, toolpath_settings, callback=draw_callback)
except:
except Exception:
# catch all non-system-exiting exceptions
report_exception()
return False
......@@ -3706,7 +3703,7 @@ class ProjectGui:
default_filename += os.path.extsep + filename_extension
elif type_filter:
for one_type in type_filter:
label, extension = one_type
extension = one_type[1]
if isinstance(extension, (list, tuple, set)):
extension = extension[0]
# use only the extension of the type filter string
......@@ -3815,12 +3812,12 @@ class ProjectGui:
else:
export_toolpaths = self.toolpath
destination = open(filename, "w")
safety_height=self.settings.get("gcode_safety_height")
safety_height = self.settings.get("gcode_safety_height")
meta_data = self.get_meta_data()
machine_time = 0
# calculate the machine time and store it in the GCode header
for tp in export_toolpaths:
machine_time += tp.get_machine_time(safety_height)
for toolpath in export_toolpaths:
machine_time += toolpath.get_machine_time(safety_height)
all_info = meta_data + os.linesep \
+ "Estimated machine time: %g minutes" % machine_time
generator = GCodeGenerator(destination,
......@@ -3845,13 +3842,12 @@ class ProjectGui:
generator.set_path_mode(PATH_MODES["continuous"],
self.settings.get("gcode_motion_tolerance"),
naive_tolerance)
for tp in export_toolpaths:
settings = tp.get_toolpath_settings()
process = settings.get_process_settings()
for toolpath in export_toolpaths:
settings = toolpath.get_toolpath_settings()
tool = settings.get_tool_settings()
generator.set_speed(tool["feedrate"], tool["speed"])
generator.add_moves(tp.get_moves(safety_height),
tool_id=tool["id"], comment=tp.get_meta_data())
generator.add_moves(toolpath.get_moves(safety_height),
tool_id=tool["id"], comment=toolpath.get_meta_data())
generator.finish()
destination.close()
log.info("GCode file successfully written: %s" % str(filename))
......
......@@ -359,9 +359,9 @@ process: 3
tasks=None):
text = self.get_config_text(tools, processes, bounds, tasks)
try:
fi = open(filename, "w")
fi.write(text)
fi.close()
handle = open(filename, "w")
handle.write(text)
handle.close()
except IOError, err_msg:
log.error("Settings: Failed to write configuration to file " \
+ "(%s): %s" % (filename, err_msg))
......@@ -417,7 +417,8 @@ process: 3
try:
try:
value_raw = self.config.get(
prefix + self.DEFAULT_SUFFIX, key, raw=raw)
prefix + self.DEFAULT_SUFFIX, key,
raw=raw)
except (ConfigParser.NoSectionError,
ConfigParser.NoOptionError):
value_raw = None
......@@ -474,13 +475,13 @@ process: 3
def get_config_text(self, tools=None, processes=None, bounds=None,
tasks=None):
def get_dictionary_of_bounds(b):
def get_dictionary_of_bounds(boundary):
""" this function should be the inverse operation of
'_get_bounds_instance_from_dict'
"""
result = {}
result["name"] = b.get_name()
bounds_type_num = b.get_type()
result["name"] = boundary.get_name()
bounds_type_num = boundary.get_type()
if bounds_type_num == Bounds.TYPE_RELATIVE_MARGIN:
bounds_type_name = "relative_margin"
elif bounds_type_num == Bounds.TYPE_FIXED_MARGIN:
......@@ -488,12 +489,12 @@ process: 3
else:
bounds_type_name = "custom"
result["type"] = bounds_type_name
low, high = b.get_bounds()
low, high = boundary.get_bounds()
for index, axis in enumerate("xyz"):
result["%s_low" % axis] = low[index]
result["%s_high" % axis] = high[index]
# special handler to allow tasks to track this new object
result[self.REFERENCE_TAG] = b
result[self.REFERENCE_TAG] = boundary
return result
result = []
if tools is None:
......
......@@ -44,7 +44,7 @@ DEPENDENCY_DESCRIPTION = {
+ "'glxgears' to locate this problem."),
}
REQUIREMENTS_LINK = "http://sourceforge.net/apps/mediawiki/pycam/index.php?title=Requirements"
REQUIREMENTS_LINK = "http://sf.net/apps/mediawiki/pycam/?title=Requirements"
# Usually the windows registry "HKEY_LOCAL_MACHINE/SOFTWARE/Gtk+/Path" contains
# something like: C:\Programs\Common files\GTK
......@@ -80,8 +80,8 @@ def import_gtk_carefully():
except NameError:
# GTK is probably not installed - the next import will fail
pass
except WindowsError:
# this happens with pyinstaller binaries - just ignore it
except OSError:
# "WindowsError" - this happens with pyinstaller binaries
pass
else:
# add the new path to the PATH environment variable
......
......@@ -183,8 +183,9 @@ class DXFParser(object):
for index in range(len(p_array)):
if p_array[index] is None:
if (index == 0) or (index == 1):
log.debug("DXFImporter: weird LWPOLYLINE input data " \
+ "in line %d: %s" % (self.line_number, p_array))
log.debug("DXFImporter: weird LWPOLYLINE input " + \
"date in line %d: %s" % \
(self.line_number, p_array))
p_array[index] = 0
points.append(Point(p_array[0], p_array[1], p_array[2]))
start_line = self.line_number
......@@ -323,8 +324,8 @@ class DXFParser(object):
center = Point(center[0], center[1], center[2])
xy_point_coords = pycam.Geometry.get_points_of_arc(center, radius,
angle_start, angle_end)
# Somehow the order of points seems to be the opposite of the what
# is expected.
# Somehow the order of points seems to be the opposite of what is
# expected.
xy_point_coords.reverse()
if len(xy_point_coords) > 1:
for index in range(len(xy_point_coords) - 1):
......@@ -334,9 +335,9 @@ class DXFParser(object):
p2 = Point(p2[0], p2[1], center.z)
self.lines.append(Line(p1, p2))
else:
log.warn("DXFImporter: Ignoring zero-length LINE (between " \
+ "input line %d and %d): %s" % (start_line, end_line,
line))
log.warn("DXFImporter: Ignoring tiny ARC (between input " + \
"line %d and %d): %s / %s (%s - %s)" % (start_line,
end_line, center, radius, angle_start, angle_end))
def check_header(self):
# TODO: this function is not used?
......@@ -367,8 +368,8 @@ def import_model(filename, program_locations=None, unit=None,
if lines:
model = pycam.Geometry.Model.ContourModel()
for index, l in enumerate(lines):
model.append(l)
for index, line in enumerate(lines):
model.append(line)
# keep the GUI smooth
if callback and (index % 50 == 0):
callback()
......@@ -390,7 +391,7 @@ def import_model(filename, program_locations=None, unit=None,
% (len(lines), len(model.get_polygons())))
return model
else:
link = "http://sourceforge.net/apps/mediawiki/pycam/index.php?title=SupportedFormats"
link = "http://sf.net/apps/mediawiki/pycam/?title=SupportedFormats"
log.error('DXFImporter: No supported elements found in DXF file!\n' \
+ '<a href="%s">Read PyCAM\'s modelling hints.</a>' % link)
return None
......
......@@ -23,7 +23,6 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
from pycam.Importers.SVGImporter import convert_eps2dxf
import pycam.Importers.DXFImporter
import tempfile
import subprocess
import os
log = pycam.Utils.log.get_logger()
......
......@@ -145,9 +145,9 @@ def ImportModel(filename, use_kdtree=True, program_locations=None, unit=None,
t = Triangle(p1, p3, p2)
elif dotcross < 0:
if not normal_conflict_warning_seen:
log.warn(("Inconsistent normal/vertices found in facet " \
+ "definition %d of '%s'. Please validate the STL " \
+ "file!") % (i, filename))
log.warn(("Inconsistent normal/vertices found in facet " + \
"definition %d of '%s'. Please validate the " + \
"STL file!") % (i, filename))
normal_conflict_warning_seen = True
t = Triangle(p1, p2, p3)
else:
......@@ -245,9 +245,9 @@ def ImportModel(filename, use_kdtree=True, program_locations=None, unit=None,
t = Triangle(p1, p3, p2, n)
elif dotcross < 0:
if not normal_conflict_warning_seen:
log.warn(("Inconsistent normal/vertices found in line " \
+ "%d of '%s'. Please validate the STL file!") \
% (current_line, filename))
log.warn(("Inconsistent normal/vertices found in " + \
"line %d of '%s'. Please validate the STL " + \
"file!") % (current_line, filename))
normal_conflict_warning_seen = True
t = Triangle(p1, p2, p3, n)
else:
......
......@@ -68,10 +68,11 @@ def convert_eps2dxf(eps_filename, dxf_filename, location=None, unit="mm"):
if returncode == 0:
return True
elif returncode == -11:
log.warn(("SVGImporter: maybe there was a problem with the " \
+ "conversion from EPS (%s) to DXF\n Users of Ubuntu 'lucid' should install " \
+ "the package 'libpstoedit0c2a' from the 'maverick' " \
+ "repository to avoid this warning.") % str(eps_filename))
log.warn(("SVGImporter: maybe there was a problem with the " + \
"conversion from EPS (%s) to DXF\n Users of Ubuntu 'lucid' " + \
"should install the package 'libpstoedit0c2a' from the " + \
"'maverick' repository to avoid this warning.") % \
str(eps_filename))
return True
else:
log.warn(("SVGImporter: failed to convert EPS file (%s) to DXF file " \
......@@ -96,7 +97,8 @@ def import_model(filename, program_locations=None, unit="mm", callback=None):
# the "right" way would be:
# inkscape --print='| pstoedit -dt -f dxf:-polyaslines - -' input.svg
# Sadly a bug in v0.47 breaks this: https://bugs.launchpad.net/inkscape/+bug/511361
# Sadly a bug in v0.47 breaks this:
# https://bugs.launchpad.net/inkscape/+bug/511361
# convert svg to eps via inkscape
eps_file_handle, eps_file_name = tempfile.mkstemp(suffix=".eps")
......
......@@ -28,11 +28,9 @@ from pycam.Geometry.Line import Line
from pycam.Geometry.Plane import Plane
from pycam.PathGenerators import get_free_paths_ode, get_free_paths_triangles
from pycam.Geometry.utils import epsilon, ceil, sqrt
from pycam.Geometry import get_bisector, get_angle_pi
from pycam.Utils import ProgressCounter
from pycam.Utils.threading import run_in_parallel
import pycam.Utils.log
import random
import math
_DEBUG_DISABLE_COLLISION_CHECK = False
......@@ -55,7 +53,8 @@ def _process_one_triangle((model, cutter, up_vector, triangle, z)):
if triangle.normal.cross(up_vector).norm == 0:
# Case 1b
return result, None
edge_collisions = get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z)
edge_collisions = get_collision_waterline_of_triangle(model, cutter,
up_vector, triangle, z)
if edge_collisions is None:
# don't try to use this edge again
return result, [id(triangle)]
......@@ -63,7 +62,8 @@ def _process_one_triangle((model, cutter, up_vector, triangle, z)):
return result, None
else:
for cutter_location, edge in edge_collisions:
shifted_edge = get_shifted_waterline(up_vector, edge, cutter_location)
shifted_edge = get_shifted_waterline(up_vector, edge,
cutter_location)
if not shifted_edge is None:
if _DEBUG_DISBALE_WATERLINE_SHIFT:
result.append((edge, edge))
......@@ -115,11 +115,12 @@ class CollisionPaths:
groups = [current_group]
while queue:
for index in queue:
if self.waterlines[index].p2 == self.waterlines[current_group[0]].p1:
index_wl = self.waterlines[index]
if index_wl.p2 == self.waterlines[current_group[0]].p1:
current_group.insert(0, index)
queue.remove(index)
break
elif self.waterlines[index].p1 == self.waterlines[current_group[-1]].p2:
elif index_wl.p1 == self.waterlines[current_group[-1]].p2:
current_group.append(index)
queue.remove(index)
break
......@@ -133,7 +134,9 @@ class CollisionPaths:
return groups
def extend_shifted_lines(self):
# TODO: improve the code below to handle "holes" properly (neighbours that disappear due to a negative collision distance - use the example "SampleScene.stl" as a reference)
# TODO: improve the code below to handle "holes" properly (neighbours
# that disappear due to a negative collision distance - use the example
# "SampleScene.stl" as a reference)
def get_right_neighbour(group, ref):
group_len = len(group)
# limit the search for a neighbour for non-closed groups
......@@ -164,9 +167,12 @@ class CollisionPaths:
if current_shifted.p2 == neighbour_shifted.p1:
index += 1
continue
cp, dist = current_shifted.get_intersection(neighbour_shifted, infinite_lines=True)
cp2, dist2 = neighbour_shifted.get_intersection(current_shifted, infinite_lines=True)
# TODO: add an arc (composed of lines) for a soft corner (not required, but nicer)
cp, dist = current_shifted.get_intersection(neighbour_shifted,
infinite_lines=True)
cp2, dist2 = neighbour_shifted.get_intersection(current_shifted,
infinite_lines=True)
# TODO: add an arc (composed of lines) for a soft corner (not
# required, but nicer)
if dist < epsilon:
self.shifted_lines[current] = None
index -= 1
......@@ -204,7 +210,8 @@ class ContourFollow:
maxy = max([m.maxy for m in self.models])
miny = max([m.miny for m in self.models])
model_dim = max(abs(maxx - minx), abs(maxy - miny))
depth = math.log(accuracy * model_dim / self.cutter.radius) / math.log(2)
depth = math.log(accuracy * model_dim / self.cutter.radius) / \
math.log(2)
self._physics_maxdepth = min(max_depth, max(ceil(depth), 4))
def _get_free_paths(self, p1, p2):
......@@ -229,7 +236,8 @@ class ContourFollow:
z_step = diff_z / max(1, (num_of_layers - 1))
# only the first model is used for the contour-follow algorithm
num_of_triangles = len(self.models[0].triangles(minx=minx, miny=miny, maxx=maxx, maxy=maxy))
num_of_triangles = len(self.models[0].triangles(minx=minx, miny=miny,
maxx=maxx, maxy=maxy))
progress_counter = ProgressCounter(2 * num_of_layers * num_of_triangles,
draw_callback)
......@@ -240,10 +248,11 @@ class ContourFollow:
# collision handling function
for z in z_steps:
# update the progress bar and check, if we should cancel the process
if draw_callback and draw_callback(text="ContourFollow: processing" \
+ " layer %d/%d" % (current_layer + 1, num_of_layers)):
# cancel immediately
break
if draw_callback:
if draw_callback(text="ContourFollow: processing layer %d/%d" \
% (current_layer + 1, num_of_layers)):
# cancel immediately
break
self.pa.new_direction(0)
self.GenerateToolPathSlice(minx, maxx, miny, maxy, z,
draw_callback, progress_counter, num_of_triangles)
......@@ -271,15 +280,17 @@ class ContourFollow:
self.pa.new_scanline()
for p in points:
self.pa.append(p)
last_position = points[-1]
if draw_callback:
draw_callback(tool_position=p, toolpath=self.pa.paths)
last_position = p
draw_callback(tool_position=last_position,
toolpath=self.pa.paths)
# update the progress counter
if not progress_counter is None:
if progress_counter.increment():
# quit requested
break
# the progress counter jumps up by the number of non directly processed triangles
# The progress counter jumps up by the number of non directly processed
# triangles.
if not progress_counter is None:
progress_counter.increment(num_of_triangles - len(shifted_lines))
self.pa.end_scanline()
......@@ -289,10 +300,7 @@ class ContourFollow:
progress_counter=None):
# use only the first model for the contour
follow_model = self.models[0]
plane = Plane(Point(0, 0, z), self._up_vector)
lines = []
waterline_triangles = CollisionPaths()
projected_waterlines = []
triangles = follow_model.triangles(minx=minx, miny=miny, maxx=maxx,
maxy=maxy)
args = [(follow_model, self.cutter, self._up_vector, t, z)
......@@ -359,7 +367,8 @@ def get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z):
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]
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:
......@@ -370,7 +379,8 @@ def get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z):
# This is just an accuracy issue (see the
# "triangle.minz >= z" statement above).
outer_edges = []
elif not [p for p in triangle.get_points() if p.z > z + epsilon]:
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:
......@@ -382,13 +392,13 @@ def get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z):
# remove points that are not part of the waterline
points_above = [p for p in points_above
if (p != waterline.p1) and (p != waterline.p2)]
potential_edges = []
if len(points_above) == 0:
# part of case (2a)
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)
dot = other_point.sub(waterline.p1).cross(waterline.dir).dot(
up_vector)
if dot > 0:
# Case (2b)
outer_edges = [waterline]
......@@ -419,7 +429,8 @@ def get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z):
else:
# two points above
other_point = points_above[0]
dot = other_point.sub(waterline.p1).cross(waterline.dir).dot(up_vector)
dot = other_point.sub(waterline.p1).cross(waterline.dir).dot(
up_vector)
if dot > 0:
# Case (2b)
# the other two points are on the right side
......@@ -435,7 +446,10 @@ def get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z):
edges = []
# pick the longest combination of two of these points
# part of case (2a)
# 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.)
# 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.)
points = [waterline.p1, waterline.p2] + points_above
for p1 in points:
for p2 in points:
......@@ -459,9 +473,9 @@ def get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z):
continue
direction = direction.mul(max_length)
edge_dir = edge.p2.sub(edge.p1)
# TODO: adapt the number of potential starting positions to the length of the line
# Don't use 0.0 and 1.0 - this could result in ambiguous collisions
# with triangles sharing these vertices.
# TODO: Adapt the number of potential starting positions to the length
# of the line. Don't use 0.0 and 1.0 - this could result in ambiguous
# collisions with triangles sharing these vertices.
for factor in (0.5, epsilon, 1.0 - epsilon, 0.25, 0.75):
start = edge.p1.add(edge_dir.mul(factor))
# We need to use the triangle collision algorithm here - because we
......@@ -475,9 +489,9 @@ def get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z):
cl, hit_t, cp = coll
break
else:
continue
log.info("Failed to detect any collision: " \
log.debug("Failed to detect any collision: " \
+ "%s / %s -> %s" % (edge, start, direction))
continue
proj_cp = plane.get_point_projection(cp)
# e.g. the Spherical Cutter often does not collide exactly above
# the potential collision line.
......@@ -489,7 +503,7 @@ def get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z):
# 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
# None indicates that the triangle needs no further evaluation
return None
return result
......
......@@ -2,7 +2,7 @@
"""
$Id$
Copyright 2010 Lars Kruse <devel@sumpfralle.de>
Copyright 2010-2011 Lars Kruse <devel@sumpfralle.de>
Copyright 2008-2009 Lode Leroy
This file is part of PyCAM.
......@@ -21,8 +21,6 @@ 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.Point import Point
from pycam.Geometry.utils import INFINITE, ceil
from pycam.PathGenerators import get_max_height_dynamic
from pycam.Utils import ProgressCounter
from pycam.Utils.threading import run_in_parallel
......@@ -117,17 +115,17 @@ class DropCutter:
# cancel requested
quit_requested = True
break
for p in points:
if p is None:
for point in points:
if point is None:
# exceeded maxz - the cutter has to skip this point
self.pa.end_scanline()
self.pa.new_scanline()
continue
self.pa.append(p)
self.pa.append(point)
# "draw_callback" returns true, if the user requested to quit
# via the GUI.
# The progress counter may return True, if cancel was requested.
if draw_callback and draw_callback(tool_position=p,
if draw_callback and draw_callback(tool_position=point,
toolpath=self.pa.paths):
quit_requested = True
break
......
......@@ -22,9 +22,10 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
import pycam.PathProcessors.PathAccumulator
from pycam.Geometry.Point import Point
from pycam.Geometry.Point import Point, Vector
from pycam.Geometry.Line import Line
from pycam.Geometry.Plane import Plane
from pycam.Geometry.utils import ceil
from pycam.PathGenerators import get_max_height_dynamic
from pycam.PathGenerators import get_max_height_dynamic, get_free_paths_ode, \
get_free_paths_triangles
from pycam.Utils import ProgressCounter
......@@ -178,19 +179,19 @@ class EngraveCutter:
# for both point pairs.
factor = (z - line.p1.z) / (line.p2.z - line.p1.z)
plane_point = line.p1.add(line.vector.mul(factor))
GenerateToolPathLinePush(pa, Line(line.p1, plane_point), z,
self.GenerateToolPathLinePush(pa, Line(line.p1, plane_point), z,
previous_z, draw_callback=draw_callback)
GenerateToolPathLinePush(pa, Line(plane_point, line.p2), z,
self.GenerateToolPathLinePush(pa, Line(plane_point, line.p2), z,
previous_z, draw_callback=draw_callback)
elif line.minz < previous_z < line.maxz:
plane = Plane(Point(0, 0, previous_z), Vector(0, 0, 1))
cp, d = plane.intersect_point(line.dir, line.p1)
cp = plane.intersect_point(line.dir, line.p1)[0]
# we can be sure that there is an intersection
if line.p1.z > previous_z:
p1, p2 = cp, line.p2
else:
p1, p2 = line.p1, cp
GenerateToolPathLinePush(pa, Line(p1, p2), z, previous_z,
self.GenerateToolPathLinePush(pa, Line(p1, p2), z, previous_z,
draw_callback=draw_callback)
else:
if line.maxz <= z:
......@@ -212,12 +213,13 @@ class EngraveCutter:
elif self.physics:
points = get_free_paths_ode(self.physics, p1, p2)
else:
points = get_free_paths_triangles(relevant_models, self.cutter, p1, p2)
points = get_free_paths_triangles(relevant_models, self.cutter,
p1, p2)
if points:
for p in points:
pa.append(p)
for point in points:
pa.append(point)
if draw_callback:
draw_callback(tool_position=p, toolpath=pa.paths)
draw_callback(tool_position=points[-1], toolpath=pa.paths)
def GenerateToolPathLineDrop(self, pa, line, minz, maxz, horiz_step,
......@@ -245,18 +247,19 @@ class EngraveCutter:
x_steps = [(p1.x + i * x_step) for i in range(num_of_steps)]
y_steps = [(p1.y + i * y_step) for i in range(num_of_steps)]
step_coords = zip(x_steps, y_steps)
# TODO: this "min(..)" is not correct for inclided lines. This should be fixed in "get_max_height".
# TODO: this "min(..)" is not correct for inclided lines. This
# should be fixed in "get_max_height".
points = get_max_height_dynamic(self.combined_model, self.cutter,
step_coords, min(p1.z, p2.z), maxz, self.physics)
for p in points:
if p is None:
for point in points:
if point is None:
# exceeded maxz - the cutter has to skip this point
pa.end_scanline()
pa.new_scanline()
continue
pa.append(p)
pa.append(point)
if draw_callback and points:
draw_callback(tool_position=points[-1], toolpath=pa.paths)
draw_callback(tool_position=points[-1], toolpath=pa.paths)
pa.end_scanline()
pa.end_direction()
......@@ -21,10 +21,9 @@ 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.Point import Point
from pycam.PathGenerators import get_free_paths_ode, get_free_paths_triangles
import pycam.PathProcessors
from pycam.Geometry.utils import epsilon, ceil
from pycam.Geometry.utils import ceil
from pycam.Utils.threading import run_in_parallel
from pycam.Utils import ProgressCounter
import pycam.Utils.log
......@@ -86,7 +85,8 @@ class PushCutter:
break
self.pa.new_direction(0)
self.GenerateToolPathSlice(layer_grid, draw_callback, progress_counter)
self.GenerateToolPathSlice(layer_grid, draw_callback,
progress_counter)
self.pa.end_direction()
self.pa.finish()
......@@ -106,8 +106,8 @@ class PushCutter:
for p1, p2 in pairs:
free_points = get_free_paths_triangles(other_models,
self.cutter, p1, p2)
for p in free_points:
final_pa.append(p)
for point in free_points:
final_pa.append(point)
final_pa.end_scanline()
final_pa.finish()
return final_pa.paths
......@@ -144,10 +144,10 @@ class PushCutter:
callback=progress_counter.update):
if points:
self.pa.new_scanline()
for p in points:
self.pa.append(p)
for point in points:
self.pa.append(point)
if draw_callback:
draw_callback(tool_position=p, toolpath=self.pa.paths)
draw_callback(tool_position=points[-1], toolpath=self.pa.paths)
self.pa.end_scanline()
# update the progress counter
if progress_counter and progress_counter.increment():
......
......@@ -129,7 +129,7 @@ def get_free_paths_triangles(models, cutter, p1, p2, return_triangles=False):
return points
else:
# return only the cutter locations (without triangles)
return [cl for (cl, t, cp) in points]
return [cut_info[0] for cut_info in points]
def get_free_paths_ode(physics, p1, p2, depth=8):
......@@ -281,9 +281,10 @@ def get_max_height_dynamic(model, cutter, positions, minz, maxz, physics=None):
p1 = result[index]
p2 = result[index + 1]
p3 = result[index + 2]
if (not p1 is None) and (not p2 is None) and (not p3 is None) \
and not _check_deviance_of_adjacent_points(p1, p2, p3, min_distance) \
and (depth_count < max_depth):
if (not p1 is None) and (not p2 is None) and (not p3 is None) and \
not _check_deviance_of_adjacent_points(p1, p2, p3,
min_distance) and \
(depth_count < max_depth):
# distribute the new point two before the middle and one after
if depth_count % 3 != 2:
# insert between the 1st and 2nd point
......
......@@ -23,6 +23,7 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
from pycam.Geometry.utils import sqrt
from pycam.Geometry.Point import Point
import ctypes
import math
try:
......
......@@ -251,7 +251,6 @@ def generate_toolpath(model, tool_settings=None,
if (not contour_model is None) and (pocketing_type != "none"):
if not callback is None:
callback(text="Generating pocketing polygons ...")
new_polygons = []
pocketing_offset = cutter.radius * 1.8
# TODO: this is an arbitrary limit to avoid infinite loops
pocketing_limit = 1000
......@@ -273,8 +272,8 @@ def generate_toolpath(model, tool_settings=None,
else:
other_polygons.append(poly)
else:
return "Unknown pocketing type given (not one of 'none', 'holes', " \
+ "'enclosed'): %s" % str(pocketing_type)
return "Unknown pocketing type given (not one of 'none', " + \
"'holes', 'enclosed'): %s" % str(pocketing_type)
# For now we use only the polygons that do not surround eny other
# polygons. Sorry - the pocketing is currently very simple ...
base_filtered_polygons = []
......@@ -402,8 +401,9 @@ def _get_pathgenerator_instance(trimesh_models, contour_model, cutter,
elif pathprocessor == "ContourCutter":
processor = ContourCutter.ContourCutter()
else:
return ("Invalid postprocessor (%s) for 'PushCutter' - it should " \
+ "be one of these: %s") % (pathprocessor, PATH_POSTPROCESSORS)
return ("Invalid postprocessor (%s) for 'PushCutter' - it " + \
"should be one of these: %s") % \
(pathprocessor, PATH_POSTPROCESSORS)
return PushCutter.PushCutter(cutter, trimesh_models, processor,
physics=physics)
elif pathgenerator == "EngraveCutter":
......
......@@ -110,11 +110,13 @@ def get_fixed_grid_layer(minx, maxx, miny, maxy, z, line_distance,
if milling_style == MILLING_STYLE_IGNORE:
# just move forward - milling style is not important
pass
elif (milling_style == MILLING_STYLE_CLIMB) == (grid_direction == GRID_DIRECTION_X):
elif (milling_style == MILLING_STYLE_CLIMB) == \
(grid_direction == GRID_DIRECTION_X):
if bool(start_position & START_X) == bool(start_position & START_Y):
# we can't start from here - choose an alternative
start_position = get_alternative_start_position(start_position)
elif (milling_style == MILLING_STYLE_CONVENTIONAL) == (grid_direction == GRID_DIRECTION_X):
elif (milling_style == MILLING_STYLE_CONVENTIONAL) == \
(grid_direction == GRID_DIRECTION_X):
if bool(start_position & START_X) != bool(start_position & START_Y):
# we can't start from here - choose an alternative
start_position = get_alternative_start_position(start_position)
......
......@@ -21,7 +21,6 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
from pycam.Geometry.Point import Point, Vector
from pycam.Geometry.Line import Line
from pycam.Geometry.Triangle import Triangle
from pycam.Geometry.Plane import Plane
from pycam.Geometry.Model import Model
......@@ -129,8 +128,8 @@ def get_support_grid_locations(minx, maxx, miny, maxy, dist_x, dist_y,
def get_support_grid(minx, maxx, miny, maxy, z_plane, dist_x, dist_y, thickness,
height, offset_x=0.0, offset_y=0.0, adjustments_x=None,
adjustments_y=None):
lines_x, lines_y = get_support_grid_locations(minx, maxx, miny, maxy, dist_x,
dist_y, offset_x, offset_y, adjustments_x, adjustments_y)
lines_x, lines_y = get_support_grid_locations(minx, maxx, miny, maxy,
dist_x, dist_y, offset_x, offset_y, adjustments_x, adjustments_y)
# create all x grid lines
grid_model = Model()
# convert all inputs to "number"
......@@ -214,7 +213,8 @@ class _BridgeCorner(object):
return "%s (%s) - %s" % (self.position, self.location, self.priority)
def _get_corner_bridges(polygon, z_plane, min_bridges, average_distance, avoid_distance):
def _get_corner_bridges(polygon, z_plane, min_bridges, average_distance,
avoid_distance):
""" try to place support bridges at corners of a polygon
Priorities:
- bigger corner angles are preferred
......@@ -264,7 +264,8 @@ def _get_corner_bridges(polygon, z_plane, min_bridges, average_distance, avoid_d
corners.remove(suitable_corners[0])
return [(c.position, c.direction) for c in bridge_corners]
def _get_edge_bridges(polygon, z_plane, min_bridges, average_distance, avoid_distance):
def _get_edge_bridges(polygon, z_plane, min_bridges, average_distance,
avoid_distance):
def is_near_list(point_list, point, distance):
for p in point_list:
if p.sub(point).norm <= distance:
......
......@@ -116,14 +116,15 @@ class Toolpath(object):
self.append = self.append_with_movement_limit
def append_with_movement_limit(self, new_position, rapid):
if self.last_pos is None:
# first movement with unknown start position - thus we ignore it
# first move with unknown start position - ignore it
self.moves.append((new_position, rapid))
self.last_pos = new_position
return True
else:
distance = new_position.sub(self.last_pos).norm
if self.moved_distance + distance > self.max_movement:
partial = (self.max_movement - self.moved_distance) / distance
partial = (self.max_movement - self.moved_distance) / \
distance
partial_dest = self.last_pos.add(new_position.sub(
self.last_pos).mul(partial))
self.moves.append((partial_dest, rapid))
......@@ -213,7 +214,8 @@ class Toolpath(object):
for path in self.toolpath:
if path:
for index in range(len(path.points) - 1):
open_lines.append(Line(path.points[index], path.points[index + 1]))
open_lines.append(Line(path.points[index],
path.points[index + 1]))
# go through all polygons and add "inner" lines (or parts thereof) to
# the final list of remaining lines
inner_lines = []
......
......@@ -99,9 +99,9 @@ def get_external_program_location(key):
location = win32api.FindExecutable(key)[1]
if location:
return location
except:
# Wildcard exeception to match "ImportError" and "pywintypes.error"
# (for "not found").
except Exception:
# Wildcard (non-system exiting) exeception to match "ImportError" and
# "pywintypes.error" (for "not found").
pass
# go through the PATH environment variable
if "PATH" in os.environ:
......
......@@ -45,7 +45,7 @@ try:
# prevent connection errors to trigger exceptions
try:
SyncManager._run_server(*args)
except socket.error, err_msg:
except socket.error:
pass
except ImportError:
pass
......@@ -151,7 +151,7 @@ class ManagerInfo(object):
def init_threading(number_of_processes=None, enable_server=False, remote=None,
run_server=False, server_credentials="", local_port=DEFAULT_PORT):
global __multiprocessing, __num_of_processes, __manager, __closing,
global __multiprocessing, __num_of_processes, __manager, __closing, \
__task_source_uuid
if __multiprocessing:
# kill the manager and clean everything up for a re-initialization
......@@ -465,8 +465,8 @@ def _handle_tasks(tasks, results, stats, cache, pending_tasks, closing):
def run_in_parallel_remote(func, args_list, unordered=False,
disable_multiprocessing=False, callback=None):
global __multiprocessing, __num_of_processes, __manager, __task_source_uuid,
__finished_jobs
global __multiprocessing, __num_of_processes, __manager, \
__task_source_uuid, __finished_jobs
if __multiprocessing is None:
# threading was not configured before
init_threading()
......
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