Added OS-X installation instructions to manual.
This commit is contained in:
@@ -2419,16 +2419,18 @@ class App(QtCore.QObject):
|
|||||||
try:
|
try:
|
||||||
f = open(filename, 'w')
|
f = open(filename, 'w')
|
||||||
except IOError:
|
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
|
return
|
||||||
|
|
||||||
# Write
|
# Write
|
||||||
try:
|
|
||||||
json.dump(d, f, default=to_dict)
|
json.dump(d, f, default=to_dict)
|
||||||
except:
|
# try:
|
||||||
App.log.error("[error] File open but failed to write:", filename)
|
# json.dump(d, f, default=to_dict)
|
||||||
f.close()
|
# except Exception, e:
|
||||||
return
|
# print str(e)
|
||||||
|
# App.log.error("[error] File open but failed to write: %s", filename)
|
||||||
|
# f.close()
|
||||||
|
# return
|
||||||
|
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|||||||
@@ -800,7 +800,6 @@ class FlatCAMDraw(QtCore.QObject):
|
|||||||
self.shape_buffer.remove(shape)
|
self.shape_buffer.remove(shape)
|
||||||
|
|
||||||
# Add the new utility shape
|
# Add the new utility shape
|
||||||
#self.shape_buffer.append(geo)
|
|
||||||
self.add_shape(geo)
|
self.add_shape(geo)
|
||||||
|
|
||||||
# Efficient plotting for fast animation
|
# Efficient plotting for fast animation
|
||||||
@@ -811,8 +810,7 @@ class FlatCAMDraw(QtCore.QObject):
|
|||||||
self.axes.draw_artist(el)
|
self.axes.draw_artist(el)
|
||||||
#self.canvas.canvas.blit(self.axes.bbox)
|
#self.canvas.canvas.blit(self.axes.bbox)
|
||||||
|
|
||||||
#self.replot()
|
# Pointer (snapped)
|
||||||
|
|
||||||
elements = self.axes.plot(x, y, 'bo', animated=True)
|
elements = self.axes.plot(x, y, 'bo', animated=True)
|
||||||
for el in elements:
|
for el in elements:
|
||||||
self.axes.draw_artist(el)
|
self.axes.draw_artist(el)
|
||||||
@@ -905,7 +903,7 @@ class FlatCAMDraw(QtCore.QObject):
|
|||||||
|
|
||||||
def plot_shape(self, geometry=None, linespec='b-', linewidth=1, animated=False):
|
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.
|
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)
|
: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
|
:type shape: DrawToolShape
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
print "add_shape()"
|
|
||||||
|
|
||||||
# List of DrawToolShape?
|
# List of DrawToolShape?
|
||||||
if isinstance(shape, list):
|
if isinstance(shape, list):
|
||||||
|
|||||||
108
camlib.py
108
camlib.py
@@ -154,26 +154,31 @@ class Geometry(object):
|
|||||||
if reset:
|
if reset:
|
||||||
self.flat_geometry = []
|
self.flat_geometry = []
|
||||||
|
|
||||||
|
## If iterable, expand recursively.
|
||||||
try:
|
try:
|
||||||
for geo in geometry:
|
for geo in geometry:
|
||||||
self.flatten_to_paths(geometry=geo, reset=False)
|
self.flatten_to_paths(geometry=geo, reset=False)
|
||||||
|
|
||||||
|
## Not iterable, do the actual indexing and add.
|
||||||
except TypeError:
|
except TypeError:
|
||||||
if type(geometry) == Polygon:
|
if type(geometry) == Polygon:
|
||||||
g = geometry.exterior
|
g = geometry.exterior
|
||||||
self.flat_geometry.append(g)
|
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:
|
for interior in geometry.interiors:
|
||||||
g = interior
|
g = interior
|
||||||
self.flat_geometry.append(g)
|
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[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[-1])
|
||||||
else:
|
else:
|
||||||
g = geometry
|
g = geometry
|
||||||
self.flat_geometry.append(g)
|
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[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[-1])
|
||||||
|
|
||||||
return self.flat_geometry, self.flat_geometry_rtree
|
return self.flat_geometry, self.flat_geometry_rtree
|
||||||
|
|
||||||
@@ -258,17 +263,18 @@ class Geometry(object):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Estimate good seedpoint if not provided.
|
||||||
if seedpoint is None:
|
if seedpoint is None:
|
||||||
seedpoint = polygon.representative_point()
|
seedpoint = polygon.representative_point()
|
||||||
|
|
||||||
# Current buffer radius
|
# Current buffer radius
|
||||||
radius = tooldia/2*(1-overlap)
|
radius = tooldia / 2 * (1 - overlap)
|
||||||
|
|
||||||
# The toolpaths
|
# The toolpaths
|
||||||
geoms = [Point(seedpoint).buffer(radius).exterior]
|
geoms = []
|
||||||
|
|
||||||
# Path margin
|
# Path margin
|
||||||
path_margin = polygon.buffer(-tooldia/2)
|
path_margin = polygon.buffer(-tooldia / 2)
|
||||||
|
|
||||||
# Grow from seed until outside the box.
|
# Grow from seed until outside the box.
|
||||||
while 1:
|
while 1:
|
||||||
@@ -281,12 +287,12 @@ class Geometry(object):
|
|||||||
else:
|
else:
|
||||||
geoms.append(path)
|
geoms.append(path)
|
||||||
|
|
||||||
radius += tooldia*(1-overlap)
|
radius += tooldia * (1 - overlap)
|
||||||
|
|
||||||
# Clean edges
|
# 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 = []
|
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
|
for y in x.interiors: # Over interiors of each polygon
|
||||||
inner_edges.append(y)
|
inner_edges.append(y)
|
||||||
geoms += outer_edges + inner_edges
|
geoms += outer_edges + inner_edges
|
||||||
@@ -2198,6 +2204,11 @@ class CNCjob(Geometry):
|
|||||||
"""
|
"""
|
||||||
Generates G-Code from a Geometry object. Stores in ``self.gcode``.
|
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
|
:param geometry: Geometry defining the toolpath
|
||||||
:type geometry: Geometry
|
:type geometry: Geometry
|
||||||
:param append: Wether to append to self.gcode or re-write it.
|
: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.
|
Second algorithm to generate from Geometry.
|
||||||
|
|
||||||
|
ALgorithm description:
|
||||||
|
----------------------
|
||||||
|
Uses RTree to find the nearest path to follow.
|
||||||
|
|
||||||
:param geometry:
|
:param geometry:
|
||||||
:param append:
|
:param append:
|
||||||
:param tooldia:
|
:param tooldia:
|
||||||
:param tolerance:
|
:param tolerance:
|
||||||
:return:
|
:return: None
|
||||||
"""
|
"""
|
||||||
assert isinstance(geometry, Geometry)
|
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:
|
if tooldia is not None:
|
||||||
self.tooldia = tooldia
|
self.tooldia = tooldia
|
||||||
@@ -2285,24 +2303,28 @@ class CNCjob(Geometry):
|
|||||||
self.gcode += "M03\n" # Spindle start
|
self.gcode += "M03\n" # Spindle start
|
||||||
self.gcode += self.pausecode + "\n"
|
self.gcode += self.pausecode + "\n"
|
||||||
|
|
||||||
# Iterate over geometry and run individual methods
|
## Iterate over geometry paths getting the nearest each time.
|
||||||
# depending on type
|
path_count = 0
|
||||||
# for geo in flat_geometry:
|
current_pt = (0, 0)
|
||||||
#
|
hits = list(rti.nearest(current_pt, 1))
|
||||||
# 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))
|
|
||||||
while len(hits) > 0:
|
while len(hits) > 0:
|
||||||
|
path_count += 1
|
||||||
|
print "Current: ", "(%.3f, %.3f)" % current_pt
|
||||||
geo = flat_geometry[hits[0]]
|
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:
|
if type(geo) == LineString or type(geo) == LinearRing:
|
||||||
self.gcode += self.linear2gcode(geo, tolerance=tolerance)
|
self.gcode += self.linear2gcode(geo, tolerance=tolerance)
|
||||||
elif type(geo) == Point:
|
elif type(geo) == Point:
|
||||||
@@ -2310,12 +2332,13 @@ class CNCjob(Geometry):
|
|||||||
else:
|
else:
|
||||||
log.warning("G-code generation not implemented for %s" % (str(type(geo))))
|
log.warning("G-code generation not implemented for %s" % (str(type(geo))))
|
||||||
|
|
||||||
start_pt = geo.coords[0]
|
# Delete from index, update current location and continue.
|
||||||
stop_pt = geo.coords[-1]
|
rti.delete(hits[0], geo.coords[0])
|
||||||
rtindex.delete(hits[0], start_pt)
|
rti.delete(hits[0], geo.coords[-1])
|
||||||
rtindex.delete(hits[0], stop_pt)
|
current_pt = geo.coords[-1]
|
||||||
hits = list(rtindex.nearest(stop_pt, 1))
|
hits = list(rti.nearest(current_pt, 1))
|
||||||
|
|
||||||
|
log.debug("%s paths traced." % path_count)
|
||||||
|
|
||||||
# Finish
|
# Finish
|
||||||
self.gcode += "G00 Z%.4f\n" % self.z_move # Stop cutting
|
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.
|
:param tool_tolerance: Tolerance when drawing the toolshape.
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
path_num = 0
|
||||||
|
|
||||||
if tooldia is None:
|
if tooldia is None:
|
||||||
tooldia = self.tooldia
|
tooldia = self.tooldia
|
||||||
|
|
||||||
@@ -2531,7 +2556,11 @@ class CNCjob(Geometry):
|
|||||||
axes.plot(x, y, linespec, color=linecolor)
|
axes.plot(x, y, linespec, color=linecolor)
|
||||||
else:
|
else:
|
||||||
for geo in self.gcode_parsed:
|
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],
|
patch = PolygonPatch(poly, facecolor=color[geo['kind'][0]][0],
|
||||||
edgecolor=color[geo['kind'][0]][1],
|
edgecolor=color[geo['kind'][0]][1],
|
||||||
alpha=alpha[geo['kind'][0]], zorder=2)
|
alpha=alpha[geo['kind'][0]], zorder=2)
|
||||||
@@ -2812,7 +2841,10 @@ def find_polygon(poly_set, point):
|
|||||||
|
|
||||||
def to_dict(obj):
|
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.
|
:param obj: Shapely geometry.
|
||||||
:type obj: BaseGeometry
|
:type obj: BaseGeometry
|
||||||
@@ -3149,3 +3181,7 @@ def three_point_circle(p1, p2, p3):
|
|||||||
radius = norm(center - p1)
|
radius = norm(center - p1)
|
||||||
|
|
||||||
return center, radius, T[0]
|
return center, radius, T[0]
|
||||||
|
|
||||||
|
|
||||||
|
def distance(pt1, pt2):
|
||||||
|
return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)
|
||||||
@@ -7,7 +7,8 @@ Drawing
|
|||||||
* [DONE] Arcs
|
* [DONE] Arcs
|
||||||
* [DONE] Subtract Shapes
|
* [DONE] Subtract Shapes
|
||||||
* [DONE] Selected objects must be kept onlist to preserve order.
|
* [DONE] Selected objects must be kept onlist to preserve order.
|
||||||
* Polygon to outline
|
* [DONE] Cut Path
|
||||||
|
* Polygon to outline
|
||||||
* Force perpendicular
|
* Force perpendicular
|
||||||
* Un-group (Union creates group)
|
* Un-group (Union creates group)
|
||||||
* Group (But not union)
|
* Group (But not union)
|
||||||
|
|||||||
@@ -4,16 +4,27 @@ Installation
|
|||||||
Windows Installer
|
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
|
.. _repository: https://bitbucket.org/jpcgt/flatcam/downloads
|
||||||
|
|
||||||
Ubuntu
|
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
|
Manual Installation
|
||||||
-------------------
|
-------------------
|
||||||
@@ -26,6 +37,9 @@ Requirements
|
|||||||
* Matplotlib 1.3.1
|
* Matplotlib 1.3.1
|
||||||
* Numpy 1.8
|
* Numpy 1.8
|
||||||
* `Shapely 1.3`_
|
* `Shapely 1.3`_
|
||||||
|
* GEOS
|
||||||
|
* RTree
|
||||||
|
* SpatialIndex
|
||||||
|
|
||||||
.. _Shapely 1.3: https://pypi.python.org/pypi/Shapely
|
.. _Shapely 1.3: https://pypi.python.org/pypi/Shapely
|
||||||
|
|
||||||
@@ -34,9 +48,14 @@ These packages might have their own dependencies.
|
|||||||
Linux
|
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
|
Python FlatCAM.py
|
||||||
|
|
||||||
@@ -44,11 +63,36 @@ Once the dependencies are installed, download the latest .zip release (or the la
|
|||||||
Windows
|
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/
|
.. _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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user