Merge branch 'new_aperture_storage' into beta_8.916
# Conflicts: # README.md
This commit is contained in:
commit
5287cbd8de
|
@ -1155,10 +1155,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
self.app.progress.emit(30)
|
||||
try:
|
||||
if aperture_to_plot_mark in self.apertures:
|
||||
if type(self.apertures[aperture_to_plot_mark]['solid_geometry']) is not list:
|
||||
self.apertures[aperture_to_plot_mark]['solid_geometry'] = \
|
||||
[self.apertures[aperture_to_plot_mark]['solid_geometry']]
|
||||
for geo in self.apertures[aperture_to_plot_mark]['solid_geometry']:
|
||||
for elem in self.apertures[aperture_to_plot_mark]['geometry']:
|
||||
if 'solid' in elem:
|
||||
geo = elem['solid']
|
||||
if type(geo) == Polygon or type(geo) == LineString:
|
||||
self.add_mark_shape(apid=aperture_to_plot_mark, shape=geo, color=color,
|
||||
face_color=color, visible=visibility)
|
||||
|
|
|
@ -11,9 +11,14 @@ CAD program, and create G-Code for Isolation routing.
|
|||
|
||||
10.05.2019
|
||||
|
||||
- Gerber Editor - working in conversion to the new data format
|
||||
- made sure that only units toggle done in Edit -> Preferences will toggle the data in Preferences. THe menu entry Edit -> Toggle Units and the shortcut key 'Q' will change only the display units in the app
|
||||
- optimized Transform tool
|
||||
|
||||
9.05.2019
|
||||
|
||||
- rework the Gerber parser
|
||||
|
||||
8.05.2019
|
||||
|
||||
- added zoom fit for Set Origin command
|
||||
|
|
647
camlib.py
647
camlib.py
|
@ -1920,14 +1920,19 @@ class Gerber (Geometry):
|
|||
'''
|
||||
apertures = {
|
||||
'id':{
|
||||
'type':chr,
|
||||
'type':string,
|
||||
'size':float,
|
||||
'width':float,
|
||||
'height':float,
|
||||
'solid_geometry': [],
|
||||
'follow_geometry': [],
|
||||
'geometry': [],
|
||||
}
|
||||
}
|
||||
apertures['geometry'] list elements are dicts
|
||||
dict = {
|
||||
'solid': [],
|
||||
'follow': [],
|
||||
'clear': []
|
||||
}
|
||||
'''
|
||||
|
||||
# aperture storage
|
||||
|
@ -2181,8 +2186,8 @@ class Gerber (Geometry):
|
|||
# store here the follow geometry
|
||||
follow_buffer = []
|
||||
|
||||
last_path_aperture = None
|
||||
current_aperture = None
|
||||
last_path_aperture = '0'
|
||||
current_aperture = '0'
|
||||
|
||||
# 1,2 or 3 from "G01", "G02" or "G03"
|
||||
current_interpolation_mode = None
|
||||
|
@ -2227,7 +2232,7 @@ class Gerber (Geometry):
|
|||
|
||||
### Cleanup
|
||||
gline = gline.strip(' \r\n')
|
||||
# log.debug("Line=%3s %s" % (line_num, gline))
|
||||
log.debug("Line=%3s %s" % (line_num, gline))
|
||||
|
||||
#### Ignored lines
|
||||
## Comments
|
||||
|
@ -2244,37 +2249,30 @@ class Gerber (Geometry):
|
|||
new_polarity = match.group(1)
|
||||
# log.info("Polarity CHANGE, LPC = %s, poly_buff = %s" % (self.is_lpc, poly_buffer))
|
||||
self.is_lpc = True if new_polarity == 'C' else False
|
||||
|
||||
if len(path) > 1 and current_polarity != new_polarity:
|
||||
|
||||
# finish the current path and add it to the storage
|
||||
# --- Buffered ----
|
||||
width = self.apertures[last_path_aperture]["size"]
|
||||
|
||||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['follow_geometry'] = []
|
||||
self.apertures[last_path_aperture]['follow_geometry'].append(geo)
|
||||
if path:
|
||||
geo_f = LineString(path)
|
||||
geo_s = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
follow_buffer.append(geo_f)
|
||||
poly_buffer.append(geo_s)
|
||||
|
||||
geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
|
||||
if not geo.is_empty:
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
geo_dict = dict()
|
||||
geo_dict['follow'] = geo_f
|
||||
if self.is_lpc:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
geo_dict['solid'] = geo_s
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[last_path_aperture]['geometry'] = []
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
|
||||
path = [path[-1]]
|
||||
|
||||
|
@ -2437,28 +2435,25 @@ class Gerber (Geometry):
|
|||
flash = Gerber.create_flash_geometry(
|
||||
Point(current_x, current_y), self.apertures[current_aperture],
|
||||
int(self.steps_per_circle))
|
||||
if not flash.is_empty:
|
||||
poly_buffer.append(flash)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[current_aperture]['clear_geometry'].append(flash)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['clear_geometry'] = []
|
||||
self.apertures[current_aperture]['clear_geometry'].append(flash)
|
||||
else:
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(Point(
|
||||
current_x, current_y))
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(Point(
|
||||
current_x, current_y))
|
||||
|
||||
if not flash.is_empty:
|
||||
geo_f = Point(current_x, current_y)
|
||||
geo_s = flash
|
||||
# follow_buffer.append(geo_f)
|
||||
poly_buffer.append(geo_s)
|
||||
|
||||
geo_dict = dict()
|
||||
geo_dict['follow'] = geo_f
|
||||
if self.is_lpc:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
geo_dict['solid'] = geo_s
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
self.apertures[current_aperture]['geometry'].append(geo_dict)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
self.apertures[current_aperture]['geometry'] = []
|
||||
self.apertures[current_aperture]['geometry'].append(geo_dict)
|
||||
|
||||
except IndexError:
|
||||
log.warning("Line %d: %s -> Nothing there to flash!" % (line_num, gline))
|
||||
|
||||
|
@ -2482,37 +2477,25 @@ class Gerber (Geometry):
|
|||
|
||||
# Take care of the current path with the previous tool
|
||||
if len(path) > 1:
|
||||
if self.apertures[last_path_aperture]["type"] == 'R':
|
||||
# do nothing because 'R' type moving aperture is none at once
|
||||
pass
|
||||
else:
|
||||
|
||||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['follow_geometry'] = []
|
||||
self.apertures[last_path_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
# --- Buffered ----
|
||||
if self.apertures[last_path_aperture]["type"] != 'R':
|
||||
width = self.apertures[last_path_aperture]["size"]
|
||||
geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
if not geo.is_empty:
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
|
||||
geo_f = LineString(path)
|
||||
geo_s = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
follow_buffer.append(geo_f)
|
||||
poly_buffer.append(geo_s)
|
||||
|
||||
geo_dict = dict()
|
||||
geo_dict['follow'] = geo_f
|
||||
if self.is_lpc:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
geo_dict['solid'] = geo_s
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[last_path_aperture]['geometry'] = []
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
|
||||
path = [path[-1]]
|
||||
|
||||
|
@ -2522,34 +2505,24 @@ class Gerber (Geometry):
|
|||
if self.regionon_re.search(gline):
|
||||
if len(path) > 1:
|
||||
# Take care of what is left in the path
|
||||
|
||||
## --- Buffered ---
|
||||
width = self.apertures[last_path_aperture]["size"]
|
||||
|
||||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
geo_f = LineString(path)
|
||||
geo_s = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
follow_buffer.append(geo_f)
|
||||
poly_buffer.append(geo_s)
|
||||
|
||||
geo = LineString(path).buffer(width/1.999, int(self.steps_per_circle / 4))
|
||||
if not geo.is_empty:
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
geo_dict = dict()
|
||||
geo_dict['follow'] = geo_f
|
||||
if self.is_lpc:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
geo_dict['solid'] = geo_s
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[last_path_aperture]['geometry'] = []
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
|
||||
path = [path[-1]]
|
||||
|
||||
|
@ -2564,94 +2537,61 @@ class Gerber (Geometry):
|
|||
self.apertures['0'] = {}
|
||||
self.apertures['0']['type'] = 'REG'
|
||||
self.apertures['0']['size'] = 0.0
|
||||
self.apertures['0']['solid_geometry'] = []
|
||||
self.apertures['0']['geometry'] = []
|
||||
|
||||
# if D02 happened before G37 we now have a path with 1 element only so we have to add the current
|
||||
# geo to the poly_buffer otherwise we loose it
|
||||
if current_operation_code == 2:
|
||||
if geo:
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures['0']['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures['0']['follow_geometry'] = []
|
||||
self.apertures['0']['follow_geometry'].append(geo)
|
||||
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures['0']['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures['0']['clear_geometry'] = []
|
||||
self.apertures['0']['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures['0']['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures['0']['solid_geometry'] = []
|
||||
self.apertures['0']['solid_geometry'].append(geo)
|
||||
continue
|
||||
# if current_operation_code == 2:
|
||||
# if geo:
|
||||
# if not geo.is_empty:
|
||||
# follow_buffer.append(geo)
|
||||
# try:
|
||||
# self.apertures['0']['follow_geometry'].append(geo)
|
||||
# except KeyError:
|
||||
# self.apertures['0']['follow_geometry'] = []
|
||||
# self.apertures['0']['follow_geometry'].append(geo)
|
||||
#
|
||||
# poly_buffer.append(geo)
|
||||
# if self.is_lpc is True:
|
||||
# try:
|
||||
# self.apertures['0']['clear_geometry'].append(geo)
|
||||
# except KeyError:
|
||||
# self.apertures['0']['clear_geometry'] = []
|
||||
# self.apertures['0']['clear_geometry'].append(geo)
|
||||
# else:
|
||||
# try:
|
||||
# self.apertures['0']['solid_geometry'].append(geo)
|
||||
# except KeyError:
|
||||
# self.apertures['0']['solid_geometry'] = []
|
||||
# self.apertures['0']['solid_geometry'].append(geo)
|
||||
# continue
|
||||
|
||||
# Only one path defines region?
|
||||
# This can happen if D02 happened before G37 and
|
||||
# is not and error.
|
||||
if len(path) < 3:
|
||||
# print "ERROR: Path contains less than 3 points:"
|
||||
# print path
|
||||
# print "Line (%d): " % line_num, gline
|
||||
# path = []
|
||||
#path = [[current_x, current_y]]
|
||||
continue
|
||||
|
||||
# For regions we may ignore an aperture that is None
|
||||
# self.regions.append({"polygon": Polygon(path),
|
||||
# "aperture": last_path_aperture})
|
||||
geo_f = LineString(path)
|
||||
geo_s = Polygon(path)
|
||||
if not geo_s.is_valid:
|
||||
geo_s = geo_s.buffer(0, int(self.steps_per_circle / 4))
|
||||
follow_buffer.append(geo_f)
|
||||
if not geo_s.is_empty:
|
||||
poly_buffer.append(geo_s)
|
||||
|
||||
# --- Buffered ---
|
||||
|
||||
region = Polygon()
|
||||
if not region.is_empty:
|
||||
follow_buffer.append(region)
|
||||
try:
|
||||
self.apertures['0']['follow_geometry'].append(region)
|
||||
except KeyError:
|
||||
self.apertures['0']['follow_geometry'] = []
|
||||
self.apertures['0']['follow_geometry'].append(region)
|
||||
|
||||
region = Polygon(path)
|
||||
if not region.is_valid:
|
||||
region = region.buffer(0, int(self.steps_per_circle / 4))
|
||||
|
||||
if not region.is_empty:
|
||||
poly_buffer.append(region)
|
||||
|
||||
# we do this for the case that a region is done without having defined any aperture
|
||||
# Allegro does that
|
||||
# if current_aperture:
|
||||
# used_aperture = current_aperture
|
||||
# elif last_path_aperture:
|
||||
# used_aperture = last_path_aperture
|
||||
# else:
|
||||
# if '0' not in self.apertures:
|
||||
# self.apertures['0'] = {}
|
||||
# self.apertures['0']['size'] = 0.0
|
||||
# self.apertures['0']['type'] = 'REG'
|
||||
# self.apertures['0']['solid_geometry'] = []
|
||||
# used_aperture = '0'
|
||||
used_aperture = '0'
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[used_aperture]['clear_geometry'].append(region)
|
||||
except KeyError:
|
||||
self.apertures[used_aperture]['clear_geometry'] = []
|
||||
self.apertures[used_aperture]['clear_geometry'].append(region)
|
||||
geo_dict = dict()
|
||||
geo_dict['follow'] = geo_f
|
||||
if not geo_s.is_empty:
|
||||
if self.is_lpc:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
geo_dict['solid'] = geo_s
|
||||
try:
|
||||
self.apertures[used_aperture]['solid_geometry'].append(region)
|
||||
self.apertures['0']['geometry'].append(geo_dict)
|
||||
except KeyError:
|
||||
self.apertures[used_aperture]['solid_geometry'] = []
|
||||
self.apertures[used_aperture]['solid_geometry'].append(region)
|
||||
self.apertures['0']['geometry'] = []
|
||||
self.apertures['0']['geometry'].append(geo_dict)
|
||||
|
||||
path = [[current_x, current_y]] # Start new path
|
||||
continue
|
||||
|
@ -2680,6 +2620,14 @@ class Gerber (Geometry):
|
|||
# NOTE: Letting it continue allows it to react to the
|
||||
# operation code.
|
||||
|
||||
if current_aperture is None:
|
||||
if '0' not in self.apertures:
|
||||
self.apertures['0'] = {}
|
||||
self.apertures['0']['type'] = 'REG'
|
||||
self.apertures['0']['size'] = 0.0
|
||||
self.apertures['0']['geometry'] = []
|
||||
current_aperture = '0'
|
||||
|
||||
# Parse coordinates
|
||||
if match.group(2) is not None:
|
||||
linear_x = parse_gerber_number(match.group(2),
|
||||
|
@ -2705,6 +2653,7 @@ class Gerber (Geometry):
|
|||
# only add the point if it's a new one otherwise skip it (harder to process)
|
||||
if path[-1] != [current_x, current_y]:
|
||||
path.append([current_x, current_y])
|
||||
|
||||
if making_region is False:
|
||||
# if the aperture is rectangle then add a rectangular shape having as parameters the
|
||||
# coordinates of the start and end point and also the width and height
|
||||
|
@ -2717,136 +2666,79 @@ class Gerber (Geometry):
|
|||
maxx = max(path[0][0], path[1][0]) + width / 2
|
||||
miny = min(path[0][1], path[1][1]) - height / 2
|
||||
maxy = max(path[0][1], path[1][1]) + height / 2
|
||||
log.debug("Coords: %s - %s - %s - %s" % (minx, miny, maxx, maxy))
|
||||
# log.debug("Coords: %s - %s - %s - %s" % (minx, miny, maxx, maxy))
|
||||
|
||||
geo = shply_box(minx, miny, maxx, maxy)
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[current_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['clear_geometry'] = []
|
||||
self.apertures[current_aperture]['clear_geometry'].append(geo)
|
||||
r_x = maxx - minx
|
||||
r_y = maxy - miny
|
||||
geo_f = Point(r_x, r_y)
|
||||
geo_s = shply_box(minx, miny, maxx, maxy)
|
||||
follow_buffer.append(geo_f)
|
||||
poly_buffer.append(geo_s)
|
||||
|
||||
geo_dict = dict()
|
||||
geo_dict['follow'] = geo_f
|
||||
if self.is_lpc:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
geo_dict['solid'] = geo_s
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[current_aperture]['geometry'].append(geo_dict)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[current_aperture]['geometry'] = []
|
||||
self.apertures[current_aperture]['geometry'].append(geo_dict)
|
||||
except:
|
||||
pass
|
||||
last_path_aperture = current_aperture
|
||||
# we do this for the case that a region is done without having defined any aperture
|
||||
# Allegro does that
|
||||
if last_path_aperture is None:
|
||||
if '0' not in self.apertures:
|
||||
self.apertures['0'] = {}
|
||||
self.apertures['0']['type'] = 'REG'
|
||||
self.apertures['0']['size'] = 0.0
|
||||
self.apertures['0']['solid_geometry'] = []
|
||||
last_path_aperture = '0'
|
||||
|
||||
else:
|
||||
self.app.inform.emit(_("[WARNING] Coordinates missing, line ignored: %s") % str(gline))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] GERBER file might be CORRUPT. Check the file !!!"))
|
||||
|
||||
elif current_operation_code == 2:
|
||||
# finish current path
|
||||
if len(path) > 1:
|
||||
geo = None
|
||||
|
||||
# --- BUFFERED ---
|
||||
# this treats the case when we are storing geometry as paths only
|
||||
if making_region:
|
||||
# we do this for the case that a region is done without having defined any aperture
|
||||
# Allegro does that
|
||||
if last_path_aperture is None:
|
||||
if '0' not in self.apertures:
|
||||
self.apertures['0'] = {}
|
||||
self.apertures['0']['type'] = 'REG'
|
||||
self.apertures['0']['size'] = 0.0
|
||||
self.apertures['0']['solid_geometry'] = []
|
||||
self.apertures['0']['geometry'] = []
|
||||
last_path_aperture = '0'
|
||||
geo = Polygon()
|
||||
width = 0
|
||||
else:
|
||||
geo = LineString(path)
|
||||
width = self.apertures[last_path_aperture]["size"]
|
||||
|
||||
try:
|
||||
if self.apertures[last_path_aperture]["type"] != 'R':
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except Exception as e:
|
||||
log.debug("camlib.Gerber.parse_lines() --> %s" % str(e))
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
if path and self.apertures[last_path_aperture]["type"] != 'R':
|
||||
geo_f = LineString(path)
|
||||
geo_s = None
|
||||
|
||||
# this treats the case when we are storing geometry as solids
|
||||
if making_region:
|
||||
# we do this for the case that a region is done without having defined any aperture
|
||||
# Allegro does that
|
||||
if last_path_aperture is None:
|
||||
if '0' not in self.apertures:
|
||||
self.apertures['0'] = {}
|
||||
self.apertures['0']['type'] = 'REG'
|
||||
self.apertures['0']['size'] = 0.0
|
||||
self.apertures['0']['solid_geometry'] = []
|
||||
last_path_aperture = '0'
|
||||
# elem = [current_x, current_y]
|
||||
# if elem != path[-1]:
|
||||
# path.append([current_x, current_y])
|
||||
|
||||
try:
|
||||
geo = Polygon(path)
|
||||
geo_s = Polygon(path)
|
||||
poly_buffer.append(geo_s)
|
||||
except ValueError:
|
||||
log.warning("Problem %s %s" % (gline, line_num))
|
||||
self.app.inform.emit(_("[ERROR] Region does not have enough points. "
|
||||
"File will be processed but there are parser errors. "
|
||||
"Line number: %s") % str(line_num))
|
||||
else:
|
||||
if last_path_aperture is None:
|
||||
log.warning("No aperture defined for curent path. (%d)" % line_num)
|
||||
width = self.apertures[last_path_aperture]["size"] # TODO: WARNING this should fail!
|
||||
geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
geo_s = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
poly_buffer.append(geo_s)
|
||||
follow_buffer.append(geo_f)
|
||||
|
||||
try:
|
||||
if self.apertures[last_path_aperture]["type"] != 'R':
|
||||
if not geo.is_empty:
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
geo_dict = dict()
|
||||
geo_dict['follow'] = geo_f
|
||||
if geo_s:
|
||||
if self.is_lpc:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
geo_dict['solid'] = geo_s
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except Exception as e:
|
||||
log.debug("camlib.Gerber.parse_lines() --> %s" % str(e))
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[last_path_aperture]['geometry'] = []
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
|
||||
# if linear_x or linear_y are None, ignore those
|
||||
if linear_x is not None and linear_y is not None:
|
||||
|
@ -2859,98 +2751,53 @@ class Gerber (Geometry):
|
|||
# Not allowed in region mode.
|
||||
elif current_operation_code == 3:
|
||||
|
||||
# Create path draw so far.
|
||||
if len(path) > 1:
|
||||
# --- Buffered ----
|
||||
|
||||
# this treats the case when we are storing geometry as paths
|
||||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
try:
|
||||
if self.apertures[last_path_aperture]["type"] != 'R':
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['follow_geometry'] = []
|
||||
self.apertures[last_path_aperture]['follow_geometry'].append(geo)
|
||||
except Exception as e:
|
||||
log.debug("camlib.Gerber.parse_lines() --> G01 match D03 --> %s" % str(e))
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['follow_geometry'] = []
|
||||
self.apertures[last_path_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
# this treats the case when we are storing geometry as solids
|
||||
width = self.apertures[last_path_aperture]["size"]
|
||||
geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
if not geo.is_empty:
|
||||
try:
|
||||
if self.apertures[last_path_aperture]["type"] != 'R':
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except:
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
# finish the path draw until now
|
||||
if len(path) > 1 and self.apertures[last_path_aperture]["type"] != 'R':
|
||||
|
||||
geo_f = LineString(path)
|
||||
geo_s = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
follow_buffer.append(geo_f)
|
||||
poly_buffer.append(geo_s)
|
||||
|
||||
geo_dict = dict()
|
||||
geo_dict['follow'] = geo_f
|
||||
if self.is_lpc:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
geo_dict['solid'] = geo_s
|
||||
try:
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['geometry'] = []
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
# Reset path starting point
|
||||
path = [[linear_x, linear_y]]
|
||||
|
||||
# --- BUFFERED ---
|
||||
# Draw the flash
|
||||
# this treats the case when we are storing geometry as paths
|
||||
geo_flash = Point([linear_x, linear_y])
|
||||
follow_buffer.append(geo_flash)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo_flash)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo_flash)
|
||||
|
||||
# this treats the case when we are storing geometry as solids
|
||||
flash = Gerber.create_flash_geometry(
|
||||
Point( [linear_x, linear_y]),
|
||||
# Draw the flash
|
||||
geo_f = Point(linear_x, linear_y)
|
||||
geo_s = Gerber.create_flash_geometry(
|
||||
Point([linear_x, linear_y]),
|
||||
self.apertures[current_aperture],
|
||||
int(self.steps_per_circle)
|
||||
)
|
||||
if not flash.is_empty:
|
||||
poly_buffer.append(flash)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[current_aperture]['clear_geometry'].append(flash)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['clear_geometry'] = []
|
||||
self.apertures[current_aperture]['clear_geometry'].append(flash)
|
||||
follow_buffer.append(geo_f)
|
||||
if not geo_s.is_empty:
|
||||
poly_buffer.append(geo_s)
|
||||
|
||||
geo_dict = dict()
|
||||
geo_dict['follow'] = geo_f
|
||||
if not geo_s.is_empty:
|
||||
if self.is_lpc:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
geo_dict['solid'] = geo_s
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
self.apertures[current_aperture]['geometry'].append(geo_dict)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
self.apertures[current_aperture]['geometry'] = []
|
||||
self.apertures[current_aperture]['geometry'].append(geo_dict)
|
||||
|
||||
# maybe those lines are not exactly needed but it is easier to read the program as those coordinates
|
||||
# are used in case that circular interpolation is encountered within the Gerber file
|
||||
|
@ -3024,6 +2871,8 @@ class Gerber (Geometry):
|
|||
# Nothing created! Pen Up.
|
||||
if current_operation_code == 2:
|
||||
log.warning("Arc with D2. (%d)" % line_num)
|
||||
|
||||
# if we have something drawn until this moment, add it
|
||||
if len(path) > 1:
|
||||
if last_path_aperture is None:
|
||||
log.warning("No aperture defined for curent path. (%d)" % line_num)
|
||||
|
@ -3031,32 +2880,24 @@ class Gerber (Geometry):
|
|||
# --- BUFFERED ---
|
||||
width = self.apertures[last_path_aperture]["size"]
|
||||
|
||||
# this treats the case when we are storing geometry as paths
|
||||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
geo_f = LineString(path)
|
||||
geo_s = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
if not geo_s.is_empty:
|
||||
follow_buffer.append(geo_f)
|
||||
poly_buffer.append(geo_s)
|
||||
|
||||
# this treats the case when we are storing geometry as solids
|
||||
buffered = LineString(path).buffer(width / 1.999, int(self.steps_per_circle))
|
||||
if not buffered.is_empty:
|
||||
poly_buffer.append(buffered)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(buffered)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(buffered)
|
||||
geo_dict = dict()
|
||||
geo_dict['follow'] = geo_f
|
||||
if not geo_s.is_empty:
|
||||
if self.is_lpc:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
geo_dict['solid'] = geo_s
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(buffered)
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(buffered)
|
||||
self.apertures[last_path_aperture]['geometry'] = []
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
|
||||
current_x = circular_x
|
||||
current_y = circular_y
|
||||
|
@ -3168,39 +3009,28 @@ class Gerber (Geometry):
|
|||
# In case that G01 (moving) aperture is rectangular, there is no need to still create
|
||||
# another geo since we already created a shapely box using the start and end coordinates found in
|
||||
# path variable. We do it only for other apertures than 'R' type
|
||||
if self.apertures[last_path_aperture]["type"] == 'R':
|
||||
pass
|
||||
else:
|
||||
if self.apertures[last_path_aperture]["type"] != 'R':
|
||||
# EOF, create shapely LineString if something still in path
|
||||
## --- Buffered ---
|
||||
|
||||
# this treats the case when we are storing geometry as paths
|
||||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
# this treats the case when we are storing geometry as solids
|
||||
width = self.apertures[last_path_aperture]["size"]
|
||||
geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
if not geo.is_empty:
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
|
||||
geo_f = LineString(path)
|
||||
geo_s = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
follow_buffer.append(geo_f)
|
||||
if not geo_s.is_empty:
|
||||
poly_buffer.append(geo_s)
|
||||
|
||||
geo_dict = dict()
|
||||
geo_dict['follow'] = geo_f
|
||||
if not geo_s.is_empty:
|
||||
if self.is_lpc:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
geo_dict['solid'] = geo_s
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[last_path_aperture]['geometry'] = []
|
||||
self.apertures[last_path_aperture]['geometry'].append(geo_dict)
|
||||
|
||||
# TODO: make sure to keep track of units changes because right now it seems to happen in a weird way
|
||||
# find out the conversion factor used to convert inside the self.apertures keys: size, width, height
|
||||
|
@ -3215,31 +3045,23 @@ class Gerber (Geometry):
|
|||
global_clear_geo = []
|
||||
|
||||
for apid in self.apertures:
|
||||
# first check if we have any clear_geometry (LPC) and if yes added it to the global_clear_geo
|
||||
if 'clear_geometry' in self.apertures[apid]:
|
||||
for pol in self.apertures[apid]['clear_geometry']:
|
||||
global_clear_geo.append(pol)
|
||||
self.apertures[apid].pop('clear_geometry', None)
|
||||
if 'geometry' in self.apertures[apid]:
|
||||
for elem in self.apertures[apid]['geometry']:
|
||||
if 'clear' in elem:
|
||||
global_clear_geo.append(elem['clear'])
|
||||
log.warning("Found %d clear polygons." % len(global_clear_geo))
|
||||
|
||||
temp_geo = []
|
||||
for apid in self.apertures:
|
||||
if 'solid_geometry' in self.apertures[apid]:
|
||||
for solid_geo in self.apertures[apid]['solid_geometry']:
|
||||
if 'geometry' in self.apertures[apid]:
|
||||
for elem in self.apertures[apid]['geometry']:
|
||||
if 'solid' in elem:
|
||||
for clear_geo in global_clear_geo:
|
||||
# Make sure that the clear_geo is within the solid_geo otherwise we loose
|
||||
# the solid_geometry. We want for clear_geometry just to cut into solid_geometry not to
|
||||
# delete it
|
||||
if clear_geo.within(solid_geo):
|
||||
solid_geo = solid_geo.difference(clear_geo)
|
||||
try:
|
||||
for poly in solid_geo:
|
||||
temp_geo.append(poly)
|
||||
except TypeError:
|
||||
temp_geo.append(solid_geo)
|
||||
if clear_geo.within(elem['solid']):
|
||||
elem['solid'] = elem['solid'].difference(clear_geo)
|
||||
|
||||
self.apertures[apid]['solid_geometry'] = deepcopy(temp_geo)
|
||||
temp_geo = []
|
||||
log.warning("Polygon difference done for %d apertures." % len(self.apertures))
|
||||
|
||||
for apid in self.apertures:
|
||||
|
@ -3249,6 +3071,9 @@ class Gerber (Geometry):
|
|||
self.apertures[apid][k] = v * conversion_factor
|
||||
# -------------------------------------------------------------
|
||||
|
||||
# for t in self.apertures:
|
||||
# print(t, self.apertures[t])
|
||||
|
||||
# --- Apply buffer ---
|
||||
# this treats the case when we are storing geometry as paths
|
||||
self.follow_geometry = follow_buffer
|
||||
|
|
|
@ -14,7 +14,6 @@ from copy import copy, deepcopy
|
|||
from camlib import *
|
||||
from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, LengthEntry, RadioSet, \
|
||||
SpinBoxDelegate, EvalEntry, EvalEntry2, FCInputDialog, FCButton, OptionalInputSection, FCCheckBox
|
||||
from flatcamEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor
|
||||
from FlatCAMObj import FlatCAMGerber
|
||||
from FlatCAMTool import FlatCAMTool
|
||||
|
||||
|
@ -32,6 +31,148 @@ if '_' not in builtins.__dict__:
|
|||
_ = gettext.gettext
|
||||
|
||||
|
||||
class DrawToolShape(object):
|
||||
"""
|
||||
Encapsulates "shapes" under a common class.
|
||||
"""
|
||||
|
||||
tolerance = None
|
||||
|
||||
@staticmethod
|
||||
def get_pts(o):
|
||||
"""
|
||||
Returns a list of all points in the object, where
|
||||
the object can be a Polygon, Not a polygon, or a list
|
||||
of such. Search is done recursively.
|
||||
|
||||
:param: geometric object
|
||||
:return: List of points
|
||||
:rtype: list
|
||||
"""
|
||||
pts = []
|
||||
|
||||
## Iterable: descend into each item.
|
||||
try:
|
||||
for subo in o:
|
||||
pts += DrawToolShape.get_pts(subo)
|
||||
|
||||
## Non-iterable
|
||||
except TypeError:
|
||||
if o is not None:
|
||||
## DrawToolShape: descend into .geo.
|
||||
if isinstance(o, DrawToolShape):
|
||||
pts += DrawToolShape.get_pts(o.geo)
|
||||
|
||||
## Descend into .exerior and .interiors
|
||||
elif type(o) == Polygon:
|
||||
pts += DrawToolShape.get_pts(o.exterior)
|
||||
for i in o.interiors:
|
||||
pts += DrawToolShape.get_pts(i)
|
||||
elif type(o) == MultiLineString:
|
||||
for line in o:
|
||||
pts += DrawToolShape.get_pts(line)
|
||||
## Has .coords: list them.
|
||||
else:
|
||||
if DrawToolShape.tolerance is not None:
|
||||
pts += list(o.simplify(DrawToolShape.tolerance).coords)
|
||||
else:
|
||||
pts += list(o.coords)
|
||||
else:
|
||||
return
|
||||
return pts
|
||||
|
||||
def __init__(self, geo={}):
|
||||
|
||||
# Shapely type or list of such
|
||||
self.geo = geo
|
||||
self.utility = False
|
||||
|
||||
class DrawToolUtilityShape(DrawToolShape):
|
||||
"""
|
||||
Utility shapes are temporary geometry in the editor
|
||||
to assist in the creation of shapes. For example it
|
||||
will show the outline of a rectangle from the first
|
||||
point to the current mouse pointer before the second
|
||||
point is clicked and the final geometry is created.
|
||||
"""
|
||||
|
||||
def __init__(self, geo={}):
|
||||
super(DrawToolUtilityShape, self).__init__(geo=geo)
|
||||
self.utility = True
|
||||
|
||||
|
||||
class DrawTool(object):
|
||||
"""
|
||||
Abstract Class representing a tool in the drawing
|
||||
program. Can generate geometry, including temporary
|
||||
utility geometry that is updated on user clicks
|
||||
and mouse motion.
|
||||
"""
|
||||
|
||||
def __init__(self, draw_app):
|
||||
self.draw_app = draw_app
|
||||
self.complete = False
|
||||
self.points = []
|
||||
self.geometry = None # DrawToolShape or None
|
||||
|
||||
def click(self, point):
|
||||
"""
|
||||
:param point: [x, y] Coordinate pair.
|
||||
"""
|
||||
return ""
|
||||
|
||||
def click_release(self, point):
|
||||
"""
|
||||
:param point: [x, y] Coordinate pair.
|
||||
"""
|
||||
return ""
|
||||
|
||||
def on_key(self, key):
|
||||
return None
|
||||
|
||||
def utility_geometry(self, data=None):
|
||||
return None
|
||||
|
||||
def bounds(self, obj):
|
||||
def bounds_rec(o):
|
||||
if type(o) is list:
|
||||
minx = Inf
|
||||
miny = Inf
|
||||
maxx = -Inf
|
||||
maxy = -Inf
|
||||
|
||||
for k in o:
|
||||
try:
|
||||
minx_, miny_, maxx_, maxy_ = bounds_rec(k)
|
||||
except Exception as e:
|
||||
log.debug("camlib.Gerber.bounds() --> %s" % str(e))
|
||||
return
|
||||
|
||||
minx = min(minx, minx_)
|
||||
miny = min(miny, miny_)
|
||||
maxx = max(maxx, maxx_)
|
||||
maxy = max(maxy, maxy_)
|
||||
return minx, miny, maxx, maxy
|
||||
else:
|
||||
# it's a Shapely object, return it's bounds
|
||||
return o.geo.bounds
|
||||
|
||||
bounds_coords = bounds_rec(obj)
|
||||
return bounds_coords
|
||||
|
||||
|
||||
class FCShapeTool(DrawTool):
|
||||
"""
|
||||
Abstract class for tools that create a shape.
|
||||
"""
|
||||
|
||||
def __init__(self, draw_app):
|
||||
DrawTool.__init__(self, draw_app)
|
||||
|
||||
def make(self):
|
||||
pass
|
||||
|
||||
|
||||
class FCPad(FCShapeTool):
|
||||
"""
|
||||
Resulting type: Polygon
|
||||
|
@ -2159,9 +2300,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
# this var will store the state of the toolbar before starting the editor
|
||||
self.toolbar_old_state = False
|
||||
|
||||
# holds flattened geometry
|
||||
self.flat_geometry = []
|
||||
|
||||
# Init GUI
|
||||
self.apdim_lbl.hide()
|
||||
self.apdim_entry.hide()
|
||||
|
@ -2180,7 +2318,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.shapes.enabled = False
|
||||
self.tool_shape.enabled = False
|
||||
|
||||
## List of selected shapes.
|
||||
## List of selected geometric elements.
|
||||
self.selected = []
|
||||
|
||||
self.key = None # Currently pressed key
|
||||
|
@ -2493,8 +2631,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.apsize_entry.set_value(size_val)
|
||||
self.storage_dict[ap_id]['size'] = size_val
|
||||
|
||||
self.storage_dict[ap_id]['solid_geometry'] = []
|
||||
self.storage_dict[ap_id]['follow_geometry'] = []
|
||||
self.storage_dict[ap_id]['geometry'] = []
|
||||
|
||||
# self.olddia_newdia dict keeps the evidence on current aperture codes as keys and gets updated on values
|
||||
# each time a aperture code is edited or added
|
||||
|
@ -2535,8 +2672,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
return
|
||||
self.storage_dict[ap_id]['size'] = size_val
|
||||
|
||||
self.storage_dict[ap_id]['solid_geometry'] = []
|
||||
self.storage_dict[ap_id]['follow_geometry'] = []
|
||||
self.storage_dict[ap_id]['geometry'] = []
|
||||
|
||||
# self.olddia_newdia dict keeps the evidence on current aperture codes as keys and gets updated on values
|
||||
# each time a aperture code is edited or added
|
||||
|
@ -2662,11 +2798,19 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
else:
|
||||
# aperture code is already in use so we move the pads from the prior tool to the new tool
|
||||
factor = current_table_dia_edited / dia_changed
|
||||
for shape in self.storage_dict[dia_changed].get_objects():
|
||||
geometry.append(DrawToolShape(
|
||||
MultiLineString([affinity.scale(subgeo, xfact=factor, yfact=factor) for subgeo in shape.geo])))
|
||||
geometry = []
|
||||
for geo_el in self.storage_dict[dia_changed]:
|
||||
geometric_data = geo_el.geo
|
||||
new_geo_el = dict()
|
||||
if 'solid' in geometric_data:
|
||||
new_geo_el['solid'] = deepcopy(geometric_data['solid'])
|
||||
if 'follow' in geometric_data:
|
||||
new_geo_el['follow'] = deepcopy(geometric_data['follow'])
|
||||
if 'clear' in geometric_data:
|
||||
new_geo_el['clear'] = deepcopy(geometric_data['clear'])
|
||||
# geometry.append(DrawToolShape(
|
||||
# MultiLineString([affinity.scale(subgeo, xfact=factor, yfact=factor) for subgeo in shape.geo])))
|
||||
|
||||
self.points_edit[current_table_dia_edited].append((0, 0))
|
||||
self.add_gerber_shape(geometry, self.storage_dict[current_table_dia_edited])
|
||||
|
||||
self.on_aperture_delete(apid=dia_changed)
|
||||
|
@ -2916,37 +3060,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.shapes.clear(update=True)
|
||||
self.tool_shape.clear(update=True)
|
||||
|
||||
def flatten(self, geometry=None, reset=True, pathonly=False):
|
||||
"""
|
||||
Creates a list of non-iterable linear geometry objects.
|
||||
Polygons are expanded into its exterior pathonly param if specified.
|
||||
|
||||
Results are placed in flat_geometry
|
||||
|
||||
:param geometry: Shapely type or list or list of list of such.
|
||||
:param reset: Clears the contents of self.flat_geometry.
|
||||
:param pathonly: Expands polygons into linear elements from the exterior attribute.
|
||||
"""
|
||||
|
||||
if reset:
|
||||
self.flat_geometry = []
|
||||
## If iterable, expand recursively.
|
||||
try:
|
||||
for geo in geometry:
|
||||
if geo is not None:
|
||||
self.flatten(geometry=geo, reset=False, pathonly=pathonly)
|
||||
|
||||
## Not iterable, do the actual indexing and add.
|
||||
except TypeError:
|
||||
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)
|
||||
return self.flat_geometry
|
||||
|
||||
def edit_fcgerber(self, orig_grb_obj):
|
||||
"""
|
||||
Imports the geometry found in self.apertures from the given FlatCAM Gerber object
|
||||
|
@ -2978,28 +3091,19 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
|
||||
def job_thread(self, apid):
|
||||
with self.app.proc_container.new(_("Adding aperture: %s geo ...") % str(apid)):
|
||||
solid_storage_elem = []
|
||||
follow_storage_elem = []
|
||||
|
||||
storage_elem = []
|
||||
self.storage_dict[apid] = {}
|
||||
|
||||
# add the Gerber geometry to editor storage
|
||||
for k, v in self.gerber_obj.apertures[apid].items():
|
||||
try:
|
||||
if k == 'solid_geometry':
|
||||
for geo in v:
|
||||
if geo:
|
||||
self.add_gerber_shape(DrawToolShape(geo), solid_storage_elem)
|
||||
self.storage_dict[apid][k] = solid_storage_elem
|
||||
elif k == 'follow_geometry':
|
||||
for geo in v:
|
||||
if geo is not None:
|
||||
self.add_gerber_shape(DrawToolShape(geo), follow_storage_elem)
|
||||
self.storage_dict[apid][k] = follow_storage_elem
|
||||
elif k == 'clear_geometry':
|
||||
continue
|
||||
if k == 'geometry':
|
||||
for geo_el in v:
|
||||
if geo_el:
|
||||
self.add_gerber_shape(DrawToolShape(geo_el), storage_elem)
|
||||
self.storage_dict[apid][k] = storage_elem
|
||||
else:
|
||||
self.storage_dict[apid][k] = v
|
||||
self.storage_dict[apid][k] = self.gerber_obj.apertures[apid][k]
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGrbEditor.edit_fcgerber().job_thread() --> %s" % str(e))
|
||||
# Check promises and clear if exists
|
||||
|
@ -3104,26 +3208,27 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
grb_obj.apertures[storage_apid] = {}
|
||||
|
||||
for k, v in storage_val.items():
|
||||
if k == 'solid_geometry':
|
||||
if k == 'geometry':
|
||||
grb_obj.apertures[storage_apid][k] = []
|
||||
for geo in v:
|
||||
new_geo = deepcopy(geo.geo)
|
||||
grb_obj.apertures[storage_apid][k].append(new_geo)
|
||||
poly_buffer.append(new_geo)
|
||||
|
||||
elif k == 'follow_geometry':
|
||||
grb_obj.apertures[storage_apid][k] = []
|
||||
for geo_f in v:
|
||||
if isinstance(geo_f.geo, Polygon):
|
||||
for geo_el in v:
|
||||
new_geo = dict()
|
||||
geometric_data = geo_el.geo
|
||||
for key in geometric_data:
|
||||
if key == 'solid':
|
||||
new_geo[key] = geometric_data['solid']
|
||||
poly_buffer.append(deepcopy(new_geo['solid']))
|
||||
if key == 'follow':
|
||||
if isinstance(geometric_data[key], Polygon):
|
||||
buff_val = -(int(storage_apid) / 2)
|
||||
geo_f = geo_f.geo.buffer(buff_val).exterior
|
||||
new_geo = deepcopy(geo_f)
|
||||
geo_f = geo_el.geo.buffer(buff_val).exterior
|
||||
new_geo[key] = geo_f
|
||||
else:
|
||||
new_geo = deepcopy(geo_f.geo)
|
||||
grb_obj.apertures[storage_apid][k].append(new_geo)
|
||||
follow_buffer.append(new_geo)
|
||||
else:
|
||||
grb_obj.apertures[storage_apid][k] = deepcopy(v)
|
||||
new_geo[key] = geometric_data[key]
|
||||
follow_buffer.append(deepcopy(new_geo['follow']))
|
||||
if key == 'clear':
|
||||
new_geo[key] = geometric_data['clear']
|
||||
|
||||
grb_obj.apertures[storage_apid][k].append(deepcopy(new_geo))
|
||||
|
||||
grb_obj.aperture_macros = deepcopy(self.gerber_obj.aperture_macros)
|
||||
|
||||
|
@ -3221,7 +3326,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
selected_apid = self.apertures_table.item(row, 1).text()
|
||||
self.last_aperture_selected = copy(selected_apid)
|
||||
|
||||
for obj in self.storage_dict[selected_apid]['solid_geometry']:
|
||||
for obj in self.storage_dict[selected_apid]['geometry']:
|
||||
self.selected.append(obj)
|
||||
except Exception as e:
|
||||
self.app.log.debug(str(e))
|
||||
|
@ -3233,7 +3338,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
return self.options[key]
|
||||
|
||||
def on_grb_shape_complete(self, storage=None, specific_shape=None, noplot=False):
|
||||
self.app.log.debug("on_shape_complete()")
|
||||
self.app.log.debug("on_grb_shape_complete()")
|
||||
|
||||
if specific_shape:
|
||||
geo = specific_shape
|
||||
|
@ -3246,7 +3351,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
# Add shape
|
||||
self.add_gerber_shape(geo, storage)
|
||||
else:
|
||||
stora = self.storage_dict[self.last_aperture_selected]['solid_geometry']
|
||||
stora = self.storage_dict[self.last_aperture_selected]['geometry']
|
||||
self.add_gerber_shape(geo, storage=stora)
|
||||
|
||||
# Remove any utility shapes
|
||||
|
@ -3257,35 +3362,36 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
# Replot and reset tool.
|
||||
self.plot_all()
|
||||
|
||||
def add_gerber_shape(self, shape, storage):
|
||||
def add_gerber_shape(self, shape_element, storage):
|
||||
"""
|
||||
Adds a shape to the shape storage.
|
||||
|
||||
:param shape: Shape to be added.
|
||||
:type shape: DrawToolShape
|
||||
:param shape_element: Shape to be added.
|
||||
:type shape_element: DrawToolShape or DrawToolUtilityShape Geometry is stored as a dict with keys: solid,
|
||||
follow, clear, each value being a list of Shapely objects. The dict can have at least one of the mentioned keys
|
||||
:return: None
|
||||
"""
|
||||
# List of DrawToolShape?
|
||||
|
||||
if isinstance(shape, list):
|
||||
for subshape in shape:
|
||||
if isinstance(shape_element, list):
|
||||
for subshape in shape_element:
|
||||
self.add_gerber_shape(subshape, storage)
|
||||
return
|
||||
|
||||
assert isinstance(shape, DrawToolShape), \
|
||||
"Expected a DrawToolShape, got %s" % str(type(shape))
|
||||
assert isinstance(shape_element, DrawToolShape), \
|
||||
"Expected a DrawToolShape, got %s" % str(type(shape_element))
|
||||
|
||||
assert shape.geo is not None, \
|
||||
assert shape_element.geo is not None, \
|
||||
"Shape object has empty geometry (None)"
|
||||
|
||||
assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or \
|
||||
not isinstance(shape.geo, list), \
|
||||
assert (isinstance(shape_element.geo, list) and len(shape_element.geo) > 0) or \
|
||||
not isinstance(shape_element.geo, list), \
|
||||
"Shape objects has empty geometry ([])"
|
||||
|
||||
if isinstance(shape, DrawToolUtilityShape):
|
||||
self.utility.append(shape)
|
||||
if isinstance(shape_element, DrawToolUtilityShape):
|
||||
self.utility.append(shape_element)
|
||||
else:
|
||||
storage.append(shape) # TODO: Check performance
|
||||
storage.append(shape_element)
|
||||
|
||||
def on_canvas_click(self, event):
|
||||
"""
|
||||
|
@ -3440,9 +3546,10 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.app.delete_selection_shape()
|
||||
for storage in self.storage_dict:
|
||||
try:
|
||||
for obj in self.storage_dict[storage]['solid_geometry']:
|
||||
if (sel_type is True and poly_selection.contains(obj.geo)) or \
|
||||
(sel_type is False and poly_selection.intersects(obj.geo)):
|
||||
for obj in self.storage_dict[storage]['geometry']:
|
||||
geometric_data = obj.geo['solid']
|
||||
if (sel_type is True and poly_selection.contains(geometric_data)) or \
|
||||
(sel_type is False and poly_selection.intersects(geometric_data)):
|
||||
if self.key == self.app.defaults["global_mselect_key"]:
|
||||
if obj in self.selected:
|
||||
self.selected.remove(obj)
|
||||
|
@ -3562,15 +3669,17 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
def draw_utility_geometry(self, geo):
|
||||
if type(geo.geo) == list:
|
||||
for el in geo.geo:
|
||||
geometric_data = el['solid']
|
||||
# Add the new utility shape
|
||||
self.tool_shape.add(
|
||||
shape=el, color=(self.app.defaults["global_draw_color"] + '80'),
|
||||
shape=geometric_data, color=(self.app.defaults["global_draw_color"] + '80'),
|
||||
# face_color=self.app.defaults['global_alt_sel_fill'],
|
||||
update=False, layer=0, tolerance=None)
|
||||
else:
|
||||
geometric_data = geo.geo['solid']
|
||||
# Add the new utility shape
|
||||
self.tool_shape.add(
|
||||
shape=geo.geo,
|
||||
shape=geometric_data,
|
||||
color=(self.app.defaults["global_draw_color"] + '80'),
|
||||
# face_color=self.app.defaults['global_alt_sel_fill'],
|
||||
update=False, layer=0, tolerance=None)
|
||||
|
@ -3590,32 +3699,34 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
|
||||
for storage in self.storage_dict:
|
||||
try:
|
||||
for shape in self.storage_dict[storage]['solid_geometry']:
|
||||
if shape.geo is None:
|
||||
for elem in self.storage_dict[storage]['geometry']:
|
||||
geometric_data = elem.geo['solid']
|
||||
if geometric_data is None:
|
||||
continue
|
||||
|
||||
if shape in self.selected:
|
||||
self.plot_shape(geometry=shape.geo, color=self.app.defaults['global_sel_draw_color'],
|
||||
linewidth=2)
|
||||
if elem in self.selected:
|
||||
self.plot_shape(geometry=geometric_data,
|
||||
color=self.app.defaults['global_sel_draw_color'])
|
||||
continue
|
||||
self.plot_shape(geometry=shape.geo, color=self.app.defaults['global_draw_color'])
|
||||
self.plot_shape(geometry=geometric_data,
|
||||
color=self.app.defaults['global_draw_color'])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
for shape in self.utility:
|
||||
self.plot_shape(geometry=shape.geo, linewidth=1)
|
||||
for elem in self.utility:
|
||||
geometric_data = elem.geo['solid']
|
||||
self.plot_shape(geometry=geometric_data)
|
||||
continue
|
||||
|
||||
self.shapes.redraw()
|
||||
|
||||
def plot_shape(self, geometry=None, color='black', linewidth=1):
|
||||
def plot_shape(self, geometry=None, color='black'):
|
||||
"""
|
||||
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)
|
||||
:param color: Shape color
|
||||
:param linewidth: Width of lines in # of pixels.
|
||||
:return: List of plotted elements.
|
||||
"""
|
||||
# plot_elements = []
|
||||
|
@ -3671,20 +3782,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
def on_shape_complete(self):
|
||||
self.app.log.debug("on_shape_complete()")
|
||||
|
||||
# Add shape
|
||||
self.add_gerber_shape(self.active_tool.geometry)
|
||||
|
||||
# Remove any utility shapes
|
||||
self.delete_utility_geometry()
|
||||
self.tool_shape.clear(update=True)
|
||||
|
||||
# Replot and reset tool.
|
||||
self.plot_all()
|
||||
# self.active_tool = type(self.active_tool)(self)
|
||||
|
||||
def get_selected(self):
|
||||
"""
|
||||
Returns list of shapes that are selected in the editor.
|
||||
|
@ -3708,28 +3805,28 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.build_ui()
|
||||
self.app.inform.emit(_("[success] Done. Apertures geometry deleted."))
|
||||
|
||||
def delete_shape(self, shape):
|
||||
def delete_shape(self, geo_el):
|
||||
self.is_modified = True
|
||||
|
||||
if shape in self.utility:
|
||||
self.utility.remove(shape)
|
||||
if geo_el in self.utility:
|
||||
self.utility.remove(geo_el)
|
||||
return
|
||||
|
||||
for storage in self.storage_dict:
|
||||
try:
|
||||
if shape in self.storage_dict[storage]['solid_geometry']:
|
||||
self.storage_dict[storage]['solid_geometry'].remove(shape)
|
||||
if geo_el in self.storage_dict[storage]['geometry']:
|
||||
self.storage_dict[storage]['geometry'].remove(geo_el)
|
||||
except KeyError:
|
||||
pass
|
||||
if shape in self.selected:
|
||||
self.selected.remove(shape) # TODO: Check performance
|
||||
if geo_el in self.selected:
|
||||
self.selected.remove(geo_el) # TODO: Check performance
|
||||
|
||||
def delete_utility_geometry(self):
|
||||
# for_deletion = [shape for shape in self.shape_buffer if shape.utility]
|
||||
# for_deletion = [shape for shape in self.storage.get_objects() if shape.utility]
|
||||
for_deletion = [shape for shape in self.utility]
|
||||
for shape in for_deletion:
|
||||
self.delete_shape(shape)
|
||||
for_deletion = [geo_el for geo_el in self.utility]
|
||||
for geo_el in for_deletion:
|
||||
self.delete_shape(geo_el)
|
||||
|
||||
self.tool_shape.clear(update=True)
|
||||
self.tool_shape.redraw()
|
||||
|
@ -3748,17 +3845,17 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.tools_gerber[toolname]["button"].setChecked(True)
|
||||
self.on_tool_select(toolname)
|
||||
|
||||
def set_selected(self, shape):
|
||||
def set_selected(self, geo_el):
|
||||
|
||||
# Remove and add to the end.
|
||||
if shape in self.selected:
|
||||
self.selected.remove(shape)
|
||||
if geo_el in self.selected:
|
||||
self.selected.remove(geo_el)
|
||||
|
||||
self.selected.append(shape)
|
||||
self.selected.append(geo_el)
|
||||
|
||||
def set_unselected(self, shape):
|
||||
if shape in self.selected:
|
||||
self.selected.remove(shape)
|
||||
def set_unselected(self, geo_el):
|
||||
if geo_el in self.selected:
|
||||
self.selected.remove(geo_el)
|
||||
|
||||
def on_array_type_combo(self):
|
||||
if self.array_type_combo.currentIndex() == 0:
|
||||
|
@ -3827,17 +3924,28 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
# I populated the combobox such that the index coincide with the join styles value (whcih is really an INT)
|
||||
join_style = self.buffer_corner_cb.currentIndex() + 1
|
||||
|
||||
def buffer_recursion(geom, selection):
|
||||
if type(geom) == list or type(geom) is MultiPolygon:
|
||||
def buffer_recursion(geom_el, selection):
|
||||
if type(geom_el) == list:
|
||||
geoms = list()
|
||||
for local_geom in geom:
|
||||
for local_geom in geom_el:
|
||||
geoms.append(buffer_recursion(local_geom, selection=selection))
|
||||
return geoms
|
||||
else:
|
||||
if geom in selection:
|
||||
return DrawToolShape(geom.geo.buffer(buff_value, join_style=join_style))
|
||||
if geom_el in selection:
|
||||
geometric_data = geom_el.geo
|
||||
buffered_geom_el = dict()
|
||||
if 'solid' in geom_el:
|
||||
buffered_geom_el['solid'] = DrawToolShape(geometric_data['solid'].buffer(buff_value,
|
||||
join_style=join_style))
|
||||
if 'follow' in geom_el:
|
||||
buffered_geom_el['follow'] = DrawToolShape(geometric_data['follow'].buffer(buff_value,
|
||||
join_style=join_style))
|
||||
if 'clear' in geom_el:
|
||||
buffered_geom_el['clear'] = DrawToolShape(geometric_data['clear'].buffer(buff_value,
|
||||
join_style=join_style))
|
||||
return buffered_geom_el
|
||||
else:
|
||||
return geom
|
||||
return geom_el
|
||||
|
||||
if not self.apertures_table.selectedItems():
|
||||
self.app.inform.emit(_(
|
||||
|
@ -3849,9 +3957,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
try:
|
||||
apid = self.apertures_table.item(x.row(), 1).text()
|
||||
|
||||
temp_storage = deepcopy(buffer_recursion(self.storage_dict[apid]['solid_geometry'], self.selected))
|
||||
self.storage_dict[apid]['solid_geometry'] = []
|
||||
self.storage_dict[apid]['solid_geometry'] = temp_storage
|
||||
temp_storage = deepcopy(buffer_recursion(self.storage_dict[apid]['geometry'], self.selected))
|
||||
self.storage_dict[apid]['geometry'] = []
|
||||
self.storage_dict[apid]['geometry'] = temp_storage
|
||||
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGrbEditor.buffer() --> %s" % str(e))
|
||||
|
@ -3874,17 +3982,29 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
"Add it and retry."))
|
||||
return
|
||||
|
||||
def scale_recursion(geom, selection):
|
||||
if type(geom) == list or type(geom) is MultiPolygon:
|
||||
def scale_recursion(geom_el, selection):
|
||||
if type(geom_el) == list:
|
||||
geoms = list()
|
||||
for local_geom in geom:
|
||||
for local_geom in geom_el:
|
||||
geoms.append(scale_recursion(local_geom, selection=selection))
|
||||
return geoms
|
||||
else:
|
||||
if geom in selection:
|
||||
return DrawToolShape(affinity.scale(geom.geo, scale_factor, scale_factor, origin='center'))
|
||||
if geom_el in selection:
|
||||
geometric_data = geom_el.geo
|
||||
scaled_geom_el = dict()
|
||||
if 'solid' in geom_el:
|
||||
scaled_geom_el['solid'] = DrawToolShape(
|
||||
affinity.scale(geometric_data['solid'], scale_factor, scale_factor, origin='center'))
|
||||
if 'follow' in geom_el:
|
||||
scaled_geom_el['follow'] = DrawToolShape(
|
||||
affinity.scale(geometric_data['follow'], scale_factor, scale_factor, origin='center'))
|
||||
if 'clear' in geom_el:
|
||||
scaled_geom_el['clear'] = DrawToolShape(
|
||||
affinity.scale(geometric_data['clear'], scale_factor, scale_factor, origin='center'))
|
||||
|
||||
return scaled_geom_el
|
||||
else:
|
||||
return geom
|
||||
return geom_el
|
||||
|
||||
if not self.apertures_table.selectedItems():
|
||||
self.app.inform.emit(_(
|
||||
|
@ -3896,9 +4016,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
try:
|
||||
apid = self.apertures_table.item(x.row(), 1).text()
|
||||
|
||||
temp_storage = deepcopy(scale_recursion(self.storage_dict[apid]['solid_geometry'], self.selected))
|
||||
self.storage_dict[apid]['solid_geometry'] = []
|
||||
self.storage_dict[apid]['solid_geometry'] = temp_storage
|
||||
temp_storage = deepcopy(scale_recursion(self.storage_dict[apid]['geometry'], self.selected))
|
||||
self.storage_dict[apid]['geometry'] = []
|
||||
self.storage_dict[apid]['geometry'] = temp_storage
|
||||
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGrbEditor.on_scale() --> %s" % str(e))
|
||||
|
@ -4581,21 +4701,23 @@ class TransformEditorTool(FlatCAMTool):
|
|||
return
|
||||
|
||||
def on_rotate_action(self, num):
|
||||
shape_list = self.draw_app.selected
|
||||
elem_list = self.draw_app.selected
|
||||
xminlist = []
|
||||
yminlist = []
|
||||
xmaxlist = []
|
||||
ymaxlist = []
|
||||
|
||||
if not shape_list:
|
||||
if not elem_list:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to rotate!"))
|
||||
return
|
||||
else:
|
||||
|
||||
with self.app.proc_container.new(_("Appying Rotate")):
|
||||
try:
|
||||
# first get a bounding box to fit all
|
||||
for sha in shape_list:
|
||||
xmin, ymin, xmax, ymax = sha.bounds()
|
||||
# first get a bounding box to fit all; we use only the 'solids' as those should provide the biggest
|
||||
# bounding box
|
||||
for el in elem_list:
|
||||
if 'solid' in el:
|
||||
xmin, ymin, xmax, ymax = el['solid'].bounds()
|
||||
xminlist.append(xmin)
|
||||
yminlist.append(ymin)
|
||||
xmaxlist.append(xmax)
|
||||
|
@ -4608,36 +4730,35 @@ class TransformEditorTool(FlatCAMTool):
|
|||
ymaximal = max(ymaxlist)
|
||||
|
||||
self.app.progress.emit(20)
|
||||
|
||||
for sel_sha in shape_list:
|
||||
px = 0.5 * (xminimal + xmaximal)
|
||||
py = 0.5 * (yminimal + ymaximal)
|
||||
|
||||
sel_sha.rotate(-num, point=(px, py))
|
||||
for sel_el in elem_list:
|
||||
if 'solid' in sel_el:
|
||||
sel_el['solid'].rotate(-num, point=(px, py))
|
||||
if 'follow' in sel_el:
|
||||
sel_el['follow'].rotate(-num, point=(px, py))
|
||||
if 'clear' in sel_el:
|
||||
sel_el['clear'].rotate(-num, point=(px, py))
|
||||
self.draw_app.plot_all()
|
||||
# self.draw_app.add_shape(DrawToolShape(sel_sha.geo))
|
||||
|
||||
# self.draw_app.transform_complete.emit()
|
||||
|
||||
self.app.inform.emit(_("[success] Done. Rotate completed."))
|
||||
|
||||
self.app.progress.emit(100)
|
||||
|
||||
except Exception as e:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, rotation movement was not executed.") % str(e))
|
||||
return
|
||||
|
||||
def on_flip(self, axis):
|
||||
shape_list = self.draw_app.selected
|
||||
elem_list = self.draw_app.selected
|
||||
xminlist = []
|
||||
yminlist = []
|
||||
xmaxlist = []
|
||||
ymaxlist = []
|
||||
|
||||
if not shape_list:
|
||||
if not elem_list:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to flip!"))
|
||||
return
|
||||
else:
|
||||
|
||||
with self.app.proc_container.new(_("Applying Flip")):
|
||||
try:
|
||||
# get mirroring coords from the point entry
|
||||
|
@ -4645,9 +4766,11 @@ class TransformEditorTool(FlatCAMTool):
|
|||
px, py = eval('{}'.format(self.flip_ref_entry.text()))
|
||||
# get mirroing coords from the center of an all-enclosing bounding box
|
||||
else:
|
||||
# first get a bounding box to fit all
|
||||
for sha in shape_list:
|
||||
xmin, ymin, xmax, ymax = sha.bounds()
|
||||
# first get a bounding box to fit all; we use only the 'solids' as those should provide the biggest
|
||||
# bounding box
|
||||
for el in elem_list:
|
||||
if 'solid' in el:
|
||||
xmin, ymin, xmax, ymax = el['solid'].bounds()
|
||||
xminlist.append(xmin)
|
||||
yminlist.append(ymin)
|
||||
xmaxlist.append(xmax)
|
||||
|
@ -4665,19 +4788,24 @@ class TransformEditorTool(FlatCAMTool):
|
|||
self.app.progress.emit(20)
|
||||
|
||||
# execute mirroring
|
||||
for sha in shape_list:
|
||||
for sel_el in elem_list:
|
||||
if axis is 'X':
|
||||
sha.mirror('X', (px, py))
|
||||
if 'solid' in sel_el:
|
||||
sel_el['solid'].mirror('X', (px, py))
|
||||
if 'follow' in sel_el:
|
||||
sel_el['follow'].mirror('X', (px, py))
|
||||
if 'clear' in sel_el:
|
||||
sel_el['clear'].mirror('X', (px, py))
|
||||
self.app.inform.emit(_('[success] Flip on the Y axis done ...'))
|
||||
elif axis is 'Y':
|
||||
sha.mirror('Y', (px, py))
|
||||
if 'solid' in sel_el:
|
||||
sel_el['solid'].mirror('Y', (px, py))
|
||||
if 'follow' in sel_el:
|
||||
sel_el['follow'].mirror('Y', (px, py))
|
||||
if 'clear' in sel_el:
|
||||
sel_el['clear'].mirror('Y', (px, py))
|
||||
self.app.inform.emit(_('[success] Flip on the X axis done ...'))
|
||||
self.draw_app.plot_all()
|
||||
|
||||
# self.draw_app.add_shape(DrawToolShape(sha.geo))
|
||||
#
|
||||
# self.draw_app.transform_complete.emit()
|
||||
|
||||
self.app.progress.emit(100)
|
||||
|
||||
except Exception as e:
|
||||
|
@ -4685,19 +4813,21 @@ class TransformEditorTool(FlatCAMTool):
|
|||
return
|
||||
|
||||
def on_skew(self, axis, num):
|
||||
shape_list = self.draw_app.selected
|
||||
elem_list = self.draw_app.selected
|
||||
xminlist = []
|
||||
yminlist = []
|
||||
|
||||
if not shape_list:
|
||||
if not elem_list:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to shear/skew!"))
|
||||
return
|
||||
else:
|
||||
with self.app.proc_container.new(_("Applying Skew")):
|
||||
try:
|
||||
# first get a bounding box to fit all
|
||||
for sha in shape_list:
|
||||
xmin, ymin, xmax, ymax = sha.bounds()
|
||||
# first get a bounding box to fit all; we use only the 'solids' as those should provide the biggest
|
||||
# bounding box
|
||||
for el in elem_list:
|
||||
if 'solid' in el:
|
||||
xmin, ymin, xmax, ymax = el['solid'].bounds()
|
||||
xminlist.append(xmin)
|
||||
yminlist.append(ymin)
|
||||
|
||||
|
@ -4707,17 +4837,23 @@ class TransformEditorTool(FlatCAMTool):
|
|||
|
||||
self.app.progress.emit(20)
|
||||
|
||||
for sha in shape_list:
|
||||
for sel_el in elem_list:
|
||||
if axis is 'X':
|
||||
sha.skew(num, 0, point=(xminimal, yminimal))
|
||||
if 'solid' in sel_el:
|
||||
sel_el['solid'].skew(num, 0, point=(xminimal, yminimal))
|
||||
if 'follow' in sel_el:
|
||||
sel_el['follow'].skew(num, 0, point=(xminimal, yminimal))
|
||||
if 'clear' in sel_el:
|
||||
sel_el['clear'].skew(num, 0, point=(xminimal, yminimal))
|
||||
elif axis is 'Y':
|
||||
sha.skew(0, num, point=(xminimal, yminimal))
|
||||
if 'solid' in sel_el:
|
||||
sel_el['solid'].skew(0, num, point=(xminimal, yminimal))
|
||||
if 'follow' in sel_el:
|
||||
sel_el['follow'].skew(0, num, point=(xminimal, yminimal))
|
||||
if 'clear' in sel_el:
|
||||
sel_el['clear'].skew(0, num, point=(xminimal, yminimal))
|
||||
self.draw_app.plot_all()
|
||||
|
||||
# self.draw_app.add_shape(DrawToolShape(sha.geo))
|
||||
#
|
||||
# self.draw_app.transform_complete.emit()
|
||||
|
||||
self.app.inform.emit(_('[success] Skew on the %s axis done ...') % str(axis))
|
||||
self.app.progress.emit(100)
|
||||
|
||||
|
@ -4726,21 +4862,23 @@ class TransformEditorTool(FlatCAMTool):
|
|||
return
|
||||
|
||||
def on_scale(self, axis, xfactor, yfactor, point=None):
|
||||
shape_list = self.draw_app.selected
|
||||
elem_list = self.draw_app.selected
|
||||
xminlist = []
|
||||
yminlist = []
|
||||
xmaxlist = []
|
||||
ymaxlist = []
|
||||
|
||||
if not shape_list:
|
||||
if not elem_list:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to scale!"))
|
||||
return
|
||||
else:
|
||||
with self.app.proc_container.new(_("Applying Scale")):
|
||||
try:
|
||||
# first get a bounding box to fit all
|
||||
for sha in shape_list:
|
||||
xmin, ymin, xmax, ymax = sha.bounds()
|
||||
# first get a bounding box to fit all; we use only the 'solids' as those should provide the biggest
|
||||
# bounding box
|
||||
for el in elem_list:
|
||||
if 'solid' in el:
|
||||
xmin, ymin, xmax, ymax = el['solid'].bounds()
|
||||
xminlist.append(xmin)
|
||||
yminlist.append(ymin)
|
||||
xmaxlist.append(xmax)
|
||||
|
@ -4761,14 +4899,15 @@ class TransformEditorTool(FlatCAMTool):
|
|||
px = 0
|
||||
py = 0
|
||||
|
||||
for sha in shape_list:
|
||||
sha.scale(xfactor, yfactor, point=(px, py))
|
||||
for sel_el in elem_list:
|
||||
if 'solid' in sel_el:
|
||||
sel_el['solid'].scale(xfactor, yfactor, point=(px, py))
|
||||
if 'follow' in sel_el:
|
||||
sel_el['follow'].scale(xfactor, yfactor, point=(px, py))
|
||||
if 'clear' in sel_el:
|
||||
sel_el['clear'].scale(xfactor, yfactor, point=(px, py))
|
||||
self.draw_app.plot_all()
|
||||
|
||||
# self.draw_app.add_shape(DrawToolShape(sha.geo))
|
||||
#
|
||||
# self.draw_app.transform_complete.emit()
|
||||
|
||||
self.app.inform.emit(_('[success] Scale on the %s axis done ...') % str(axis))
|
||||
self.app.progress.emit(100)
|
||||
except Exception as e:
|
||||
|
@ -4776,38 +4915,33 @@ class TransformEditorTool(FlatCAMTool):
|
|||
return
|
||||
|
||||
def on_offset(self, axis, num):
|
||||
shape_list = self.draw_app.selected
|
||||
xminlist = []
|
||||
yminlist = []
|
||||
elem_list = self.draw_app.selected
|
||||
|
||||
if not shape_list:
|
||||
if not elem_list:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to offset!"))
|
||||
return
|
||||
else:
|
||||
with self.app.proc_container.new(_("Applying Offset")):
|
||||
try:
|
||||
# first get a bounding box to fit all
|
||||
for sha in shape_list:
|
||||
xmin, ymin, xmax, ymax = sha.bounds()
|
||||
xminlist.append(xmin)
|
||||
yminlist.append(ymin)
|
||||
|
||||
# get the minimum x,y and maximum x,y for all objects selected
|
||||
xminimal = min(xminlist)
|
||||
yminimal = min(yminlist)
|
||||
self.app.progress.emit(20)
|
||||
|
||||
for sha in shape_list:
|
||||
for sel_el in elem_list:
|
||||
if axis is 'X':
|
||||
sha.offset((num, 0))
|
||||
if 'solid' in sel_el:
|
||||
sel_el['solid'].offset((num, 0))
|
||||
if 'follow' in sel_el:
|
||||
sel_el['follow'].offset((num, 0))
|
||||
if 'clear' in sel_el:
|
||||
sel_el['clear'].offset((num, 0))
|
||||
elif axis is 'Y':
|
||||
sha.offset((0, num))
|
||||
if 'solid' in sel_el:
|
||||
sel_el['solid'].offset((0, num))
|
||||
if 'follow' in sel_el:
|
||||
sel_el['follow'].offset((0, num))
|
||||
if 'clear' in sel_el:
|
||||
sel_el['clear'].offset((0, num))
|
||||
self.draw_app.plot_all()
|
||||
|
||||
# self.draw_app.add_shape(DrawToolShape(sha.geo))
|
||||
#
|
||||
# self.draw_app.transform_complete.emit()
|
||||
|
||||
self.app.inform.emit(_('[success] Offset on the %s axis done ...') % str(axis))
|
||||
self.app.progress.emit(100)
|
||||
|
||||
|
|
Loading…
Reference in New Issue