From a0d6d1a6a975c6e62c8b7e8ed5f2cee7a3ffcf3c Mon Sep 17 00:00:00 2001 From: jpcaram Date: Sat, 27 Dec 2014 15:12:49 -0500 Subject: [PATCH] Added OS-X installation instructions to manual. --- FlatCAMApp.py | 16 +++--- FlatCAMDraw.py | 15 +++--- camlib.py | 108 ++++++++++++++++++++++++++-------------- doc/source/planning.rst | 3 +- manual/installation.rst | 60 +++++++++++++++++++--- 5 files changed, 141 insertions(+), 61 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 41383a0f..d0b5902a 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2419,16 +2419,18 @@ class App(QtCore.QObject): try: f = open(filename, 'w') except IOError: - App.log.error("[error] Failed to open file for saving:", filename) + App.log.error("[error] Failed to open file for saving: %s", filename) return # Write - try: - json.dump(d, f, default=to_dict) - except: - App.log.error("[error] File open but failed to write:", filename) - f.close() - return + json.dump(d, f, default=to_dict) + # try: + # json.dump(d, f, default=to_dict) + # except Exception, e: + # print str(e) + # App.log.error("[error] File open but failed to write: %s", filename) + # f.close() + # return f.close() diff --git a/FlatCAMDraw.py b/FlatCAMDraw.py index 61d7c7af..9918ed97 100644 --- a/FlatCAMDraw.py +++ b/FlatCAMDraw.py @@ -800,7 +800,6 @@ class FlatCAMDraw(QtCore.QObject): self.shape_buffer.remove(shape) # Add the new utility shape - #self.shape_buffer.append(geo) self.add_shape(geo) # Efficient plotting for fast animation @@ -811,13 +810,12 @@ class FlatCAMDraw(QtCore.QObject): self.axes.draw_artist(el) #self.canvas.canvas.blit(self.axes.bbox) - #self.replot() + # Pointer (snapped) + elements = self.axes.plot(x, y, 'bo', animated=True) + for el in elements: + self.axes.draw_artist(el) - elements = self.axes.plot(x, y, 'bo', animated=True) - for el in elements: - self.axes.draw_artist(el) - - self.canvas.canvas.blit(self.axes.bbox) + self.canvas.canvas.blit(self.axes.bbox) def on_canvas_key(self, event): """ @@ -905,7 +903,7 @@ class FlatCAMDraw(QtCore.QObject): def plot_shape(self, geometry=None, linespec='b-', linewidth=1, animated=False): """ - Plots a geometric object or list of objects without rendeting. Plotted objects + Plots a geometric object or list of objects without rendering. Plotted objects are returned as a list. This allows for efficient/animated rendering. :param geometry: Geometry to be plotted (Any Shapely.geom kind or list of such) @@ -971,7 +969,6 @@ class FlatCAMDraw(QtCore.QObject): :type shape: DrawToolShape :return: None """ - print "add_shape()" # List of DrawToolShape? if isinstance(shape, list): diff --git a/camlib.py b/camlib.py index 3da969a0..d49a6165 100644 --- a/camlib.py +++ b/camlib.py @@ -154,26 +154,31 @@ class Geometry(object): if reset: self.flat_geometry = [] + ## If iterable, expand recursively. try: for geo in geometry: self.flatten_to_paths(geometry=geo, reset=False) + + ## Not iterable, do the actual indexing and add. except TypeError: if type(geometry) == Polygon: g = geometry.exterior self.flat_geometry.append(g) - self.flat_geometry_rtree.insert(len(self.flat_geometry)-1, g.coords[0]) - self.flat_geometry_rtree.insert(len(self.flat_geometry)-1, g.coords[-1]) + + ## Add first and last points of the path to the index. + self.flat_geometry_rtree.insert(len(self.flat_geometry) - 1, g.coords[0]) + self.flat_geometry_rtree.insert(len(self.flat_geometry) - 1, g.coords[-1]) for interior in geometry.interiors: g = interior self.flat_geometry.append(g) - self.flat_geometry_rtree.insert(len(self.flat_geometry)-1, g.coords[0]) - self.flat_geometry_rtree.insert(len(self.flat_geometry)-1, g.coords[-1]) + self.flat_geometry_rtree.insert(len(self.flat_geometry) - 1, g.coords[0]) + self.flat_geometry_rtree.insert(len(self.flat_geometry) - 1, g.coords[-1]) else: g = geometry self.flat_geometry.append(g) - self.flat_geometry_rtree.insert(len(self.flat_geometry)-1, g.coords[0]) - self.flat_geometry_rtree.insert(len(self.flat_geometry)-1, g.coords[-1]) + self.flat_geometry_rtree.insert(len(self.flat_geometry) - 1, g.coords[0]) + self.flat_geometry_rtree.insert(len(self.flat_geometry) - 1, g.coords[-1]) return self.flat_geometry, self.flat_geometry_rtree @@ -258,17 +263,18 @@ class Geometry(object): :return: """ + # Estimate good seedpoint if not provided. if seedpoint is None: seedpoint = polygon.representative_point() # Current buffer radius - radius = tooldia/2*(1-overlap) + radius = tooldia / 2 * (1 - overlap) # The toolpaths - geoms = [Point(seedpoint).buffer(radius).exterior] + geoms = [] # Path margin - path_margin = polygon.buffer(-tooldia/2) + path_margin = polygon.buffer(-tooldia / 2) # Grow from seed until outside the box. while 1: @@ -281,12 +287,12 @@ class Geometry(object): else: geoms.append(path) - radius += tooldia*(1-overlap) + radius += tooldia * (1 - overlap) # Clean edges - outer_edges = [x.exterior for x in autolist(polygon.buffer(-tooldia/2))] + outer_edges = [x.exterior for x in autolist(polygon.buffer(-tooldia / 2))] inner_edges = [] - for x in autolist(polygon.buffer(-tooldia/2)): # Over resulting polygons + for x in autolist(polygon.buffer(-tooldia / 2)): # Over resulting polygons for y in x.interiors: # Over interiors of each polygon inner_edges.append(y) geoms += outer_edges + inner_edges @@ -2198,6 +2204,11 @@ class CNCjob(Geometry): """ Generates G-Code from a Geometry object. Stores in ``self.gcode``. + Algorithm description: + ---------------------- + Follow geometry paths in the order they are being read. No attempt + to optimize. + :param geometry: Geometry defining the toolpath :type geometry: Geometry :param append: Wether to append to self.gcode or re-write it. @@ -2259,14 +2270,21 @@ class CNCjob(Geometry): """ Second algorithm to generate from Geometry. + ALgorithm description: + ---------------------- + Uses RTree to find the nearest path to follow. + :param geometry: :param append: :param tooldia: :param tolerance: - :return: + :return: None """ assert isinstance(geometry, Geometry) - flat_geometry, rtindex = geometry.flatten_to_paths() + + ## Flatten the geometry and get rtree index + flat_geometry, rti = geometry.flatten_to_paths() + log.debug("%d paths" % len(flat_geometry)) if tooldia is not None: self.tooldia = tooldia @@ -2285,24 +2303,28 @@ class CNCjob(Geometry): self.gcode += "M03\n" # Spindle start self.gcode += self.pausecode + "\n" - # Iterate over geometry and run individual methods - # depending on type - # for geo in flat_geometry: - # - # if type(geo) == LineString or type(geo) == LinearRing: - # self.gcode += self.linear2gcode(geo, tolerance=tolerance) - # continue - # - # if type(geo) == Point: - # self.gcode += self.point2gcode(geo) - # continue - # - # log.warning("G-code generation not implemented for %s" % (str(type(geo)))) - - hits = list(rtindex.nearest((0, 0), 1)) + ## Iterate over geometry paths getting the nearest each time. + path_count = 0 + current_pt = (0, 0) + hits = list(rti.nearest(current_pt, 1)) while len(hits) > 0: + path_count += 1 + print "Current: ", "(%.3f, %.3f)" % current_pt geo = flat_geometry[hits[0]] + # Determine which end of the path is closest. + distance2start = distance(current_pt, geo.coords[0]) + distance2stop = distance(current_pt, geo.coords[-1]) + print " Path index =", hits[0] + print " Start: ", "(%.3f, %.3f)" % geo.coords[0], " D(Start): %.3f" % distance2start + print " Stop : ", "(%.3f, %.3f)" % geo.coords[-1], " D(Stop): %.3f" % distance2stop + + # Reverse if end is closest. + if distance2start > distance2stop: + print " Reversing!" + geo.coords = list(geo.coords)[::-1] + + # G-code if type(geo) == LineString or type(geo) == LinearRing: self.gcode += self.linear2gcode(geo, tolerance=tolerance) elif type(geo) == Point: @@ -2310,12 +2332,13 @@ class CNCjob(Geometry): else: log.warning("G-code generation not implemented for %s" % (str(type(geo)))) - start_pt = geo.coords[0] - stop_pt = geo.coords[-1] - rtindex.delete(hits[0], start_pt) - rtindex.delete(hits[0], stop_pt) - hits = list(rtindex.nearest(stop_pt, 1)) + # Delete from index, update current location and continue. + rti.delete(hits[0], geo.coords[0]) + rti.delete(hits[0], geo.coords[-1]) + current_pt = geo.coords[-1] + hits = list(rti.nearest(current_pt, 1)) + log.debug("%s paths traced." % path_count) # Finish self.gcode += "G00 Z%.4f\n" % self.z_move # Stop cutting @@ -2518,6 +2541,8 @@ class CNCjob(Geometry): :param tool_tolerance: Tolerance when drawing the toolshape. :return: None """ + path_num = 0 + if tooldia is None: tooldia = self.tooldia @@ -2531,7 +2556,11 @@ class CNCjob(Geometry): axes.plot(x, y, linespec, color=linecolor) else: for geo in self.gcode_parsed: - poly = geo['geom'].buffer(tooldia/2.0).simplify(tool_tolerance) + path_num += 1 + axes.annotate(str(path_num), xy=geo['geom'].coords[0], + xycoords='data') + + poly = geo['geom'].buffer(tooldia / 2.0).simplify(tool_tolerance) patch = PolygonPatch(poly, facecolor=color[geo['kind'][0]][0], edgecolor=color[geo['kind'][0]][1], alpha=alpha[geo['kind'][0]], zorder=2) @@ -2812,7 +2841,10 @@ def find_polygon(poly_set, point): def to_dict(obj): """ - Makes a Shapely geometry object into serializeable form. + Makes the following types into serializable form: + + * ApertureMacro + * BaseGeometry :param obj: Shapely geometry. :type obj: BaseGeometry @@ -3149,3 +3181,7 @@ def three_point_circle(p1, p2, p3): radius = norm(center - p1) return center, radius, T[0] + + +def distance(pt1, pt2): + return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) \ No newline at end of file diff --git a/doc/source/planning.rst b/doc/source/planning.rst index d69a194f..7a3a87f4 100644 --- a/doc/source/planning.rst +++ b/doc/source/planning.rst @@ -7,7 +7,8 @@ Drawing * [DONE] Arcs * [DONE] Subtract Shapes * [DONE] Selected objects must be kept onlist to preserve order. -* Polygon to outline +* [DONE] Cut Path + * Polygon to outline * Force perpendicular * Un-group (Union creates group) * Group (But not union) diff --git a/manual/installation.rst b/manual/installation.rst index b4ffcde3..e57cd215 100644 --- a/manual/installation.rst +++ b/manual/installation.rst @@ -4,16 +4,27 @@ Installation Windows Installer ----------------- -Download the installer from the repository_ and run it in your machine. It includes everything you need. +Download the installer from the repository_ and run it in your machine. +It includes everything you need. .. _repository: https://bitbucket.org/jpcgt/flatcam/downloads Ubuntu ------ -FlatCAM should work on most Linux distributions but Ubuntu has been chosen as the test platform. +FlatCAM should work on most Linux distributions but Ubuntu has been +chosen as the test platform. + +There are several dependencies required to run FlatCAM. These are +listed in the following section. Before attempting a manual installation, +try running the provided setup script ``setup_ubuntu.sh`` that will +download and install required packages. + +OS-X +---- + +See manual instructions below. -There are several dependencies required to run FlatCAM. These are listed in the following section. Before attempting a manual installation, try running the provided setup script ``setup_ubuntu.sh`` that will download and install required packages. Manual Installation ------------------- @@ -26,6 +37,9 @@ Requirements * Matplotlib 1.3.1 * Numpy 1.8 * `Shapely 1.3`_ + * GEOS +* RTree + * SpatialIndex .. _Shapely 1.3: https://pypi.python.org/pypi/Shapely @@ -34,9 +48,14 @@ These packages might have their own dependencies. Linux ~~~~~ -Under Linux, most modern package installers like **yum** or **apt-get** will attempt to locate and install the whole tree of dependencies for a specified package automatically. Refer to the provided setup script ``setup_ubuntu.sh`` for the names and installation order. +Under Linux, most modern package installers like **yum** or **apt-get** +will attempt to locate and install the whole tree of dependencies for a +specified package automatically. Refer to the provided setup script +``setup_ubuntu.sh`` for the names and installation order. -Once the dependencies are installed, download the latest .zip release (or the latest source, although it is not garanteed to work), unpack it, change into the created folder and run:: +Once the dependencies are installed, download the latest .zip release +(or the latest source, although it is not garanteed to work), unpack it, +change into the created folder and run:: Python FlatCAM.py @@ -44,11 +63,36 @@ Once the dependencies are installed, download the latest .zip release (or the la Windows ~~~~~~~ -An easy way to get the requirements in your system is to install WinPython_. This is a standalone distribution of Python which includes all of FlatCAM's dependencies, except for Shapely. +An easy way to get the requirements in your system is to install WinPython_. +This is a standalone distribution of Python which includes all of FlatCAM's +dependencies, except for Shapely. .. _WinPython: http://winpython.sourceforge.net/ -Once the dependencies are installed, download the latest .zip release (or the latest source, although it is not garanteed to work), unpack it, change into the created folder and run:: +Once the dependencies are installed, download the latest .zip +release (or the latest source, although it is not garanteed to work), +unpack it, change into the created folder and run:: - Python FlatCAM.py + python FlatCAM.py + + +OS-X +~~~~ + +Start by installing binary packages: pyqt, geos, spatialindex. +One way to do this is using Homebrew_:: + + brew install name_of_package + +.. _Homebrew: http://brew.sh + +Now you can install all Python packages (numpy, matplotlib, rtree, scipy, +shapely, simplejson) using pip:: + + pip install name_of_package + +Finally, download the latest FlatCAM .zip package or source code. Change into +its directory and launch it by running:: + + python FlatCAM.py