- Gerber Editor - working in conversion to the new data format
This commit is contained in:
@@ -9,6 +9,10 @@ CAD program, and create G-Code for Isolation routing.
|
||||
|
||||
=================================================
|
||||
|
||||
10.05.2019
|
||||
|
||||
- Gerber Editor - working in conversion to the new data format
|
||||
|
||||
9.05.2019
|
||||
|
||||
- rework the Gerber parser
|
||||
|
||||
@@ -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()
|
||||
@@ -2660,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)
|
||||
@@ -2914,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
|
||||
@@ -2983,17 +3098,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
||||
for k, v in self.gerber_obj.apertures[apid].items():
|
||||
try:
|
||||
if k == 'geometry':
|
||||
for el_dict in v:
|
||||
if el_dict:
|
||||
new_el_dict = dict()
|
||||
if 'solid' in el_dict:
|
||||
new_el_dict['solid'] =DrawToolShape(deepcopy(el_dict['solid']))
|
||||
if 'follow' in el_dict:
|
||||
new_el_dict['follow'] =DrawToolShape(deepcopy(el_dict['follow']))
|
||||
if 'clear' in el_dict:
|
||||
new_el_dict['clear'] =DrawToolShape(deepcopy(el_dict['clear']))
|
||||
storage_elem.append(new_el_dict)
|
||||
self.add_gerber_shape(DrawToolShape(new_el_dict['solid']))
|
||||
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] = self.gerber_obj.apertures[apid][k]
|
||||
@@ -3101,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)
|
||||
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_el.geo.buffer(buff_val).exterior
|
||||
new_geo[key] = geo_f
|
||||
else:
|
||||
new_geo[key] = geometric_data[key]
|
||||
follow_buffer.append(deepcopy(new_geo['follow']))
|
||||
if key == 'clear':
|
||||
new_geo[key] = geometric_data['clear']
|
||||
|
||||
elif k == 'follow_geometry':
|
||||
grb_obj.apertures[storage_apid][k] = []
|
||||
for geo_f in v:
|
||||
if isinstance(geo_f.geo, Polygon):
|
||||
buff_val = -(int(storage_apid) / 2)
|
||||
geo_f = geo_f.geo.buffer(buff_val).exterior
|
||||
new_geo = deepcopy(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)
|
||||
grb_obj.apertures[storage_apid][k].append(deepcopy(new_geo))
|
||||
|
||||
grb_obj.aperture_macros = deepcopy(self.gerber_obj.aperture_macros)
|
||||
|
||||
@@ -3218,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))
|
||||
@@ -3230,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
|
||||
@@ -3243,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
|
||||
@@ -3254,33 +3362,36 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
||||
# Replot and reset tool.
|
||||
self.plot_all()
|
||||
|
||||
def add_gerber_shape(self, shape):
|
||||
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:
|
||||
self.add_gerber_shape(subshape)
|
||||
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_element)
|
||||
|
||||
def on_canvas_click(self, event):
|
||||
"""
|
||||
@@ -3435,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)
|
||||
@@ -3557,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)
|
||||
@@ -3586,32 +3700,33 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
||||
for storage in self.storage_dict:
|
||||
try:
|
||||
for elem in self.storage_dict[storage]['geometry']:
|
||||
if elem['solid'].geo is None:
|
||||
geometric_data = elem.geo['solid']
|
||||
if geometric_data is None:
|
||||
continue
|
||||
|
||||
if elem['solid'] in self.selected:
|
||||
self.plot_shape(geometry=elem['solid'].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=elem['solid'].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 elem in self.utility:
|
||||
self.plot_shape(geometry=elem['solid'].geo, linewidth=1)
|
||||
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 = []
|
||||
@@ -3667,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.
|
||||
@@ -3831,15 +3932,16 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
||||
return geoms
|
||||
else:
|
||||
if geom_el in selection:
|
||||
geometric_data = geom_el.geo
|
||||
buffered_geom_el = dict()
|
||||
if 'solid' in geom_el:
|
||||
buffered_geom_el['solid'] = DrawToolShape(geom_el['solid'].geo.buffer(buff_value,
|
||||
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(geom_el['follow'].geo.buffer(buff_value,
|
||||
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(geom_el['clear'].geo.buffer(buff_value,
|
||||
buffered_geom_el['clear'] = DrawToolShape(geometric_data['clear'].buffer(buff_value,
|
||||
join_style=join_style))
|
||||
return buffered_geom_el
|
||||
else:
|
||||
@@ -3888,16 +3990,17 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
||||
return geoms
|
||||
else:
|
||||
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(geom_el['solid'].geo, scale_factor, scale_factor, origin='center'))
|
||||
affinity.scale(geometric_data['solid'], scale_factor, scale_factor, origin='center'))
|
||||
if 'follow' in geom_el:
|
||||
scaled_geom_el['follow'] = DrawToolShape(
|
||||
affinity.scale(geom_el['follow'].geo, scale_factor, scale_factor, origin='center'))
|
||||
affinity.scale(geometric_data['follow'], scale_factor, scale_factor, origin='center'))
|
||||
if 'clear' in geom_el:
|
||||
scaled_geom_el['clear'] = DrawToolShape(
|
||||
affinity.scale(geom_el['clear'].geo, scale_factor, scale_factor, origin='center'))
|
||||
affinity.scale(geometric_data['clear'], scale_factor, scale_factor, origin='center'))
|
||||
|
||||
return scaled_geom_el
|
||||
else:
|
||||
@@ -4607,117 +4710,126 @@ class TransformEditorTool(FlatCAMTool):
|
||||
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; we use only the 'solids' as those should provide the biggest
|
||||
# bounding box
|
||||
for geo_el in elem_list:
|
||||
xmin, ymin, xmax, ymax = geo_el..bounds()
|
||||
|
||||
with self.app.proc_container.new(_("Appying Rotate")):
|
||||
try:
|
||||
# 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)
|
||||
ymaxlist.append(ymax)
|
||||
|
||||
# get the minimum x,y and maximum x,y for all objects selected
|
||||
xminimal = min(xminlist)
|
||||
yminimal = min(yminlist)
|
||||
xmaximal = max(xmaxlist)
|
||||
ymaximal = max(ymaxlist)
|
||||
|
||||
self.app.progress.emit(20)
|
||||
px = 0.5 * (xminimal + xmaximal)
|
||||
py = 0.5 * (yminimal + ymaximal)
|
||||
|
||||
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.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):
|
||||
elem_list = self.draw_app.selected
|
||||
xminlist = []
|
||||
yminlist = []
|
||||
xmaxlist = []
|
||||
ymaxlist = []
|
||||
|
||||
if not elem_list:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to flip!"))
|
||||
return
|
||||
|
||||
with self.app.proc_container.new(_("Applying Flip")):
|
||||
try:
|
||||
# get mirroring coords from the point entry
|
||||
if self.flip_ref_cb.isChecked():
|
||||
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; 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)
|
||||
ymaxlist.append(ymax)
|
||||
|
||||
# get the minimum x,y and maximum x,y for all objects selected
|
||||
xminimal = min(xminlist)
|
||||
yminimal = min(yminlist)
|
||||
xmaximal = max(xmaxlist)
|
||||
ymaximal = max(ymaxlist)
|
||||
|
||||
self.app.progress.emit(20)
|
||||
px = 0.5 * (xminimal + xmaximal)
|
||||
py = 0.5 * (yminimal + ymaximal)
|
||||
|
||||
for sel_sha in shape_list:
|
||||
px = 0.5 * (xminimal + xmaximal)
|
||||
py = 0.5 * (yminimal + ymaximal)
|
||||
self.app.progress.emit(20)
|
||||
|
||||
sel_sha.rotate(-num, point=(px, py))
|
||||
self.draw_app.plot_all()
|
||||
# self.draw_app.add_shape(DrawToolShape(sel_sha.geo))
|
||||
# execute mirroring
|
||||
for sel_el in elem_list:
|
||||
if axis is 'X':
|
||||
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':
|
||||
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.app.progress.emit(100)
|
||||
|
||||
# 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
|
||||
xminlist = []
|
||||
yminlist = []
|
||||
xmaxlist = []
|
||||
ymaxlist = []
|
||||
|
||||
if not shape_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
|
||||
if self.flip_ref_cb.isChecked():
|
||||
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()
|
||||
xminlist.append(xmin)
|
||||
yminlist.append(ymin)
|
||||
xmaxlist.append(xmax)
|
||||
ymaxlist.append(ymax)
|
||||
|
||||
# get the minimum x,y and maximum x,y for all objects selected
|
||||
xminimal = min(xminlist)
|
||||
yminimal = min(yminlist)
|
||||
xmaximal = max(xmaxlist)
|
||||
ymaximal = max(ymaxlist)
|
||||
|
||||
px = 0.5 * (xminimal + xmaximal)
|
||||
py = 0.5 * (yminimal + ymaximal)
|
||||
|
||||
self.app.progress.emit(20)
|
||||
|
||||
# execute mirroring
|
||||
for sha in shape_list:
|
||||
if axis is 'X':
|
||||
sha.mirror('X', (px, py))
|
||||
self.app.inform.emit(_('[success] Flip on the Y axis done ...'))
|
||||
elif axis is 'Y':
|
||||
sha.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:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Flip action was not executed.") % str(e))
|
||||
return
|
||||
except Exception as e:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Flip action was not executed.") % str(e))
|
||||
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()
|
||||
xminlist.append(xmin)
|
||||
yminlist.append(ymin)
|
||||
# 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)
|
||||
|
||||
# get the minimum x,y and maximum x,y for all objects selected
|
||||
xminimal = min(xminlist)
|
||||
@@ -4725,16 +4837,22 @@ 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))
|
||||
self.draw_app.plot_all()
|
||||
|
||||
# self.draw_app.add_shape(DrawToolShape(sha.geo))
|
||||
#
|
||||
# self.draw_app.transform_complete.emit()
|
||||
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.app.inform.emit(_('[success] Skew on the %s axis done ...') % str(axis))
|
||||
self.app.progress.emit(100)
|
||||
@@ -4744,25 +4862,27 @@ 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()
|
||||
xminlist.append(xmin)
|
||||
yminlist.append(ymin)
|
||||
xmaxlist.append(xmax)
|
||||
ymaxlist.append(ymax)
|
||||
# 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)
|
||||
ymaxlist.append(ymax)
|
||||
|
||||
# get the minimum x,y and maximum x,y for all objects selected
|
||||
xminimal = min(xminlist)
|
||||
@@ -4779,13 +4899,14 @@ class TransformEditorTool(FlatCAMTool):
|
||||
px = 0
|
||||
py = 0
|
||||
|
||||
for sha in shape_list:
|
||||
sha.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()
|
||||
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.app.inform.emit(_('[success] Scale on the %s axis done ...') % str(axis))
|
||||
self.app.progress.emit(100)
|
||||
@@ -4794,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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user