Using FlatCamRTreeStorage in copper clearing algorithm.

This commit is contained in:
jpcaram
2015-01-01 14:59:45 -05:00
parent 709cb4bff1
commit 977b5b1f91
4 changed files with 159 additions and 101 deletions

View File

@@ -704,7 +704,6 @@ class FlatCAMDraw(QtCore.QObject):
def toolbar_tool_toggle(self, key): def toolbar_tool_toggle(self, key):
self.options[key] = self.sender().isChecked() self.options[key] = self.sender().isChecked()
print "grid_snap", self.options["grid_snap"]
def clear(self): def clear(self):
self.active_tool = None self.active_tool = None
@@ -721,24 +720,14 @@ class FlatCAMDraw(QtCore.QObject):
:param fcgeometry: FlatCAMGeometry :param fcgeometry: FlatCAMGeometry
:return: None :return: None
""" """
assert isinstance(fcgeometry, Geometry)
if fcgeometry.solid_geometry is None:
geometry = []
else:
try:
_ = iter(fcgeometry.solid_geometry)
geometry = fcgeometry.solid_geometry
except TypeError:
geometry = [fcgeometry.solid_geometry]
# Delete contents of editor.
#self.shape_buffer = []
self.clear() self.clear()
# Link shapes into editor. # Link shapes into editor.
for shape in geometry: for shape in fcgeometry.flatten():
#self.shape_buffer.append(DrawToolShape(geometry)) if shape is not None: # TODO: Make flatten never create a None
self.add_shape(DrawToolShape(shape.flatten())) self.add_shape(DrawToolShape(shape))
self.replot() self.replot()
self.drawing_toolbar.setDisabled(False) self.drawing_toolbar.setDisabled(False)
@@ -850,8 +839,6 @@ class FlatCAMDraw(QtCore.QObject):
if isinstance(geo, DrawToolShape) and geo.geo is not None: if isinstance(geo, DrawToolShape) and geo.geo is not None:
print geo.geo
# Remove any previous utility shape # Remove any previous utility shape
self.delete_utility_geometry() self.delete_utility_geometry()
@@ -1255,9 +1242,9 @@ class FlatCAMDraw(QtCore.QObject):
results = cascaded_union([t.geo for t in self.get_selected()]) results = cascaded_union([t.geo for t in self.get_selected()])
# Delete originals. # Delete originals.
for shape in self.get_selected(): for_deletion = [s for s in self.get_selected()]
#self.shape_buffer.remove(shape) for shape in for_deletion:
self.delete_shape(shape) # TODO: This will crash self.delete_shape(shape)
# Selected geometry is now gone! # Selected geometry is now gone!
self.selected = [] self.selected = []

194
camlib.py
View File

@@ -138,7 +138,7 @@ class Geometry(object):
else: else:
return self.solid_geometry.bounds return self.solid_geometry.bounds
def flatten(self, geometry=None, reset=True): def flatten(self, geometry=None, reset=True, pathonly=False):
if geometry is None: if geometry is None:
geometry = self.solid_geometry geometry = self.solid_geometry
@@ -148,12 +148,21 @@ class Geometry(object):
## If iterable, expand recursively. ## If iterable, expand recursively.
try: try:
for geo in geometry: for geo in geometry:
self.flatten(geometry=geo, reset=False) self.flatten(geometry=geo,
reset=False,
pathonly=pathonly)
## Not iterable, do the actual indexing and add. ## Not iterable, do the actual indexing and add.
except TypeError: except TypeError:
if type(geometry) == Polygon: if pathonly and type(geometry) == Polygon:
self.flat_geometry.append(geometry.exterior)
self.flatten(geometry=geometry.interiors,
reset=False,
pathonly=True)
else:
self.flat_geometry.append(geometry) self.flat_geometry.append(geometry)
# if type(geometry) == Polygon:
# self.flat_geometry.append(geometry)
return self.flat_geometry return self.flat_geometry
@@ -178,50 +187,49 @@ class Geometry(object):
idx.insert(shape) idx.insert(shape)
return idx return idx
# def flatten_to_paths(self, geometry=None, reset=True):
def flatten_to_paths(self, geometry=None, reset=True): # """
""" # Creates a list of non-iterable linear geometry elements and
Creates a list of non-iterable linear geometry elements and # indexes them in rtree.
indexes them in rtree. #
# :param geometry: Iterable geometry
:param geometry: Iterable geometry # :param reset: Wether to clear (True) or append (False) to self.flat_geometry
:param reset: Wether to clear (True) or append (False) to self.flat_geometry # :return: self.flat_geometry, self.flat_geometry_rtree
:return: self.flat_geometry, self.flat_geometry_rtree # """
""" #
# if geometry is None:
if geometry is None: # geometry = self.solid_geometry
geometry = self.solid_geometry #
# if reset:
if reset: # self.flat_geometry = []
self.flat_geometry = [] #
# ## If iterable, expand recursively.
## 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.
## 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) #
# ## Add first and last points of the path to the index.
## 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[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]) #
# 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
def isolation_geometry(self, offset): def isolation_geometry(self, offset):
""" """
@@ -2323,10 +2331,21 @@ class CNCjob(Geometry):
""" """
assert isinstance(geometry, Geometry) assert isinstance(geometry, Geometry)
## Flatten the geometry and get rtree index ## Flatten the geometry
flat_geometry, rti = geometry.flatten_to_paths() flat_geometry = geometry.flatten(pathonly=True)
log.debug("%d paths" % len(flat_geometry)) log.debug("%d paths" % len(flat_geometry))
## Index first and last points in paths
def get_pts(o):
return [o.coords[0], o.coords[-1]]
storage = FlatCAMRTreeStorage()
storage.get_points = get_pts
for shape in flat_geometry:
if shape is not None: # TODO: This shouldn't have happened.
storage.insert(shape)
if tooldia is not None: if tooldia is not None:
self.tooldia = tooldia self.tooldia = tooldia
@@ -2347,37 +2366,44 @@ class CNCjob(Geometry):
## Iterate over geometry paths getting the nearest each time. ## Iterate over geometry paths getting the nearest each time.
path_count = 0 path_count = 0
current_pt = (0, 0) current_pt = (0, 0)
hits = list(rti.nearest(current_pt, 1)) pt, geo = storage.nearest(current_pt)
while len(hits) > 0: try:
path_count += 1 while True:
print "Current: ", "(%.3f, %.3f)" % current_pt path_count += 1
geo = flat_geometry[hits[0]] print "Current: ", "(%.3f, %.3f)" % current_pt
# Determine which end of the path is closest. # TODO: There shoudn't be any None in geometry.flatten()
distance2start = distance(current_pt, geo.coords[0]) # if geo is None:
distance2stop = distance(current_pt, geo.coords[-1]) # storage.remove(geo)
print " Path index =", hits[0] # continue
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. # Remove before modifying, otherwise
if distance2start > distance2stop: # deletion will fail.
print " Reversing!" storage.remove(geo)
geo.coords = list(geo.coords)[::-1]
# G-code if list(pt) == list(geo.coords[-1]):
if type(geo) == LineString or type(geo) == LinearRing: print "Reversing"
self.gcode += self.linear2gcode(geo, tolerance=tolerance) geo.coords = list(geo.coords)[::-1]
elif type(geo) == Point:
self.gcode += self.point2gcode(geo)
else:
log.warning("G-code generation not implemented for %s" % (str(type(geo))))
# Delete from index, update current location and continue. # G-code
rti.delete(hits[0], geo.coords[0]) if type(geo) == LineString or type(geo) == LinearRing:
rti.delete(hits[0], geo.coords[-1]) self.gcode += self.linear2gcode(geo, tolerance=tolerance)
current_pt = geo.coords[-1] elif type(geo) == Point:
hits = list(rti.nearest(current_pt, 1)) self.gcode += self.point2gcode(geo)
else:
log.warning("G-code generation not implemented for %s" % (str(type(geo))))
# 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]
# Next
pt, geo = storage.nearest(current_pt)
except StopIteration: # Nothing found in storage.
pass
log.debug("%s paths traced." % path_count) log.debug("%s paths traced." % path_count)
@@ -3231,8 +3257,15 @@ def distance(pt1, pt2):
class FlatCAMRTree(object): class FlatCAMRTree(object):
def __init__(self): def __init__(self):
# Python RTree Index
self.rti = rtindex.Index() self.rti = rtindex.Index()
## Track object-point relationship
# Each is list of points in object.
self.obj2points = [] self.obj2points = []
# Index is index in rtree, value is index of
# object in obj2points.
self.points2obj = [] self.points2obj = []
self.get_points = lambda go: go.coords self.get_points = lambda go: go.coords
@@ -3251,7 +3284,6 @@ class FlatCAMRTree(object):
self.grow_obj2points(objid) self.grow_obj2points(objid)
self.obj2points[objid] = [] self.obj2points[objid] = []
#for pt in obj.coords:
for pt in self.get_points(obj): for pt in self.get_points(obj):
self.rti.insert(len(self.points2obj), (pt[0], pt[1], pt[0], pt[1]), obj=objid) self.rti.insert(len(self.points2obj), (pt[0], pt[1], pt[0], pt[1]), obj=objid)
self.obj2points[objid].append(len(self.points2obj)) self.obj2points[objid].append(len(self.points2obj))
@@ -3259,10 +3291,7 @@ class FlatCAMRTree(object):
def remove_obj(self, objid, obj): def remove_obj(self, objid, obj):
# Use all ptids to delete from index # Use all ptids to delete from index
#for i in range(len(self.obj2points[objid])):
for i, pt in enumerate(self.get_points(obj)): for i, pt in enumerate(self.get_points(obj)):
#pt = obj.coords[i]
#pt = self.get_points(obj)[i]
self.rti.delete(self.obj2points[objid][i], (pt[0], pt[1], pt[0], pt[1])) self.rti.delete(self.obj2points[objid][i], (pt[0], pt[1], pt[0], pt[1]))
def nearest(self, pt): def nearest(self, pt):
@@ -3280,8 +3309,13 @@ class FlatCAMRTreeStorage(FlatCAMRTree):
super(FlatCAMRTreeStorage, self).insert(len(self.objects) - 1, obj) super(FlatCAMRTreeStorage, self).insert(len(self.objects) - 1, obj)
def remove(self, obj): def remove(self, obj):
# Get index in list
objidx = self.objects.index(obj) objidx = self.objects.index(obj)
# Remove from list
self.objects[objidx] = None self.objects[objidx] = None
# Remove from index
self.remove_obj(objidx, obj) self.remove_obj(objidx, obj)
def get_objects(self): def get_objects(self):

View File

@@ -12,7 +12,7 @@ Drawing
* Force perpendicular * Force perpendicular
* Un-group (Union creates group) * Un-group (Union creates group)
* Group (But not union) * Group (But not union)
* Remove from index (rebuild index or make deleted instances * [DONE] Remove from index (rebuild index or make deleted instances
equal to None in the list). equal to None in the list).
* Better handling/abstraction of geometry types and lists of such. * Better handling/abstraction of geometry types and lists of such.

37
tests/test_fcrts.py Normal file
View File

@@ -0,0 +1,37 @@
from camlib import *
from shapely.geometry import LineString, LinearRing
s = FlatCAMRTreeStorage()
geoms = [
LinearRing(((0.5699056603773586, 0.7216037735849057),
(0.9885849056603774, 0.7216037735849057),
(0.9885849056603774, 0.6689622641509434),
(0.5699056603773586, 0.6689622641509434),
(0.5699056603773586, 0.7216037735849057))),
LineString(((0.8684952830188680, 0.6952830188679245),
(0.8680655198743615, 0.6865349890935113),
(0.8667803692948564, 0.6778712076279851),
(0.8646522079829676, 0.6693751114229638),
(0.8645044888670096, 0.6689622641509434))),
LineString(((0.9874952830188680, 0.6952830188679245),
(0.9864925023483531, 0.6748709493942936),
(0.9856160316877274, 0.6689622641509434))),
]
for geo in geoms:
s.insert(geo)
current_pt = (0, 0)
pt, geo = s.nearest(current_pt)
while geo is not None:
print pt, geo
print "OBJECTS BEFORE:", s.objects
#geo.coords = list(geo.coords[::-1])
s.remove(geo)
print "OBJECTS AFTER:", s.objects
current_pt = geo.coords[-1]
pt, geo = s.nearest(current_pt)