Using FlatCamRTreeStorage in copper clearing algorithm.
This commit is contained in:
@@ -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
194
camlib.py
@@ -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):
|
||||||
|
|||||||
@@ -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
37
tests/test_fcrts.py
Normal 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)
|
||||||
Reference in New Issue
Block a user