- refactored the imports in the Plugins, moved almost all imports in the AppTool file
- fixed a number of issues, mostly leftovers from moving the UI of a Plugin in its own class - fixed some bugs in the Punch Gerber plugin - fixed some bugs where the 'pool' parameter was not passed when creating shapes collections (in 3D graphic mode); I wonder how it worked until now - added a new feature in the Isolation Plugin: now for all the isolation Geometry objects this plugin can do a supplementary simplification of the geometry using the tolerance parameter defined in the General Parameters. This should lead to a reduced number of tool lifts when doing corners
This commit is contained in:
committed by
Marius Stanciu
parent
e1824a09f7
commit
ada48269a9
@@ -5,31 +5,10 @@
|
||||
# License: MIT Licence #
|
||||
# ##########################################################
|
||||
|
||||
from PyQt6 import QtWidgets, QtCore, QtGui
|
||||
|
||||
from appTool import AppTool
|
||||
from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCButton, \
|
||||
FCComboBox, OptionalInputSection, FCSpinner, FCLabel, FCInputDialogSpinnerButton, FCComboBox2, \
|
||||
VerticalScrollArea, FCGridLayout, FCFrame
|
||||
from appTool import *
|
||||
from appParsers.ParseGerber import Gerber
|
||||
from camlib import grace
|
||||
|
||||
from copy import deepcopy
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
import simplejson as json
|
||||
import sys
|
||||
|
||||
from shapely.ops import unary_union, nearest_points
|
||||
from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing, Point
|
||||
|
||||
from matplotlib.backend_bases import KeyEvent as mpl_key_event
|
||||
|
||||
import logging
|
||||
import gettext
|
||||
import appTranslation as fcTranslate
|
||||
import builtins
|
||||
from camlib import grace
|
||||
|
||||
fcTranslate.apply_language('strings')
|
||||
if '_' not in builtins.__dict__:
|
||||
@@ -347,6 +326,9 @@ class ToolIsolation(AppTool, Gerber):
|
||||
self.ui.milling_type_radio.set_value(self.app.options["tools_iso_milling_type"])
|
||||
self.ui.combine_passes_cb.set_value(self.app.options["tools_iso_combine_passes"])
|
||||
self.ui.valid_cb.set_value(self.app.options["tools_iso_check_valid"])
|
||||
self.ui.simplify_cb.set_value(self.app.options["tools_iso_simplification"])
|
||||
self.ui.sim_tol_entry.set_value(self.app.options["tools_iso_simplification_tol"])
|
||||
|
||||
self.ui.area_shape_radio.set_value(self.app.options["tools_iso_area_shape"])
|
||||
self.ui.poly_int_cb.set_value(self.app.options["tools_iso_poly_ints"])
|
||||
self.ui.forced_rest_iso_cb.set_value(self.app.options["tools_iso_force"])
|
||||
@@ -1756,6 +1738,11 @@ class ToolIsolation(AppTool, Gerber):
|
||||
selection_type = args['sel_type'] if 'sel_type' in args else self.ui.select_combo.get_value()
|
||||
# tool_tip_shape: ["C1", "C2", "C3", "C4", "B", "V", "L"]
|
||||
tool_tip_shape = args['tip_shape'] if 'tip_shape' in args else self.ui.tool_shape_combo.get_value()
|
||||
# use simplification: True/False with simplificaiton_tol: Float
|
||||
use_simplification = args['iso_simplification'] if 'iso_simplification' in args else \
|
||||
self.ui.simplify_cb.get_value()
|
||||
simplification_tol = args['simplification_tol'] if 'simplification_tol' in args else \
|
||||
self.ui.sim_tol_entry.get_value()
|
||||
|
||||
# update the Common Parameters values in the self.iso_tools
|
||||
for tool_iso in tools_storage:
|
||||
@@ -1763,6 +1750,8 @@ class ToolIsolation(AppTool, Gerber):
|
||||
if key == 'data':
|
||||
tools_storage[tool_iso][key]["tools_iso_rest"] = use_rest
|
||||
tools_storage[tool_iso][key]["tools_iso_combine_passes"] = use_combine
|
||||
tools_storage[tool_iso][key]["tools_iso_simplification"] = use_simplification
|
||||
tools_storage[tool_iso][key]["tools_iso_simplification_tol"] = simplification_tol
|
||||
tools_storage[tool_iso][key]["tools_iso_isoexcept"] = use_iso_except
|
||||
tools_storage[tool_iso][key]["tools_iso_selection"] = selection_type
|
||||
tools_storage[tool_iso][key]["tools_iso_area_shape"] = sel_area_shape
|
||||
@@ -1772,11 +1761,13 @@ class ToolIsolation(AppTool, Gerber):
|
||||
if use_combine:
|
||||
if use_rest:
|
||||
self.combined_rest(iso_obj=isolated_obj, iso2geo=geometry, tools_storage=tools_storage,
|
||||
lim_area=limited_area, negative_dia=negative_dia, plot=plot)
|
||||
lim_area=limited_area, negative_dia=negative_dia,
|
||||
simp_en=use_simplification, simp_tol=simplification_tol, plot=plot)
|
||||
else:
|
||||
self.combined_normal(iso_obj=isolated_obj, iso2geo=geometry, tools_storage=tools_storage,
|
||||
sel_tools=sel_tools, iso_except=use_iso_except, lim_area=limited_area,
|
||||
negative_dia=negative_dia, extra_passes=use_extra_passes, plot=plot)
|
||||
negative_dia=negative_dia, extra_passes=use_extra_passes,
|
||||
simp_en=use_simplification, simp_tol=simplification_tol, plot=plot)
|
||||
|
||||
else:
|
||||
prog_plot = self.app.options["tools_iso_plotting"]
|
||||
@@ -1876,7 +1867,10 @@ class ToolIsolation(AppTool, Gerber):
|
||||
iso_geo = self.area_intersection(iso_geo, intersection_geo=limited_area)
|
||||
|
||||
# make sure that no empty geometry element is in the solid_geometry
|
||||
new_solid_geo = [geo for geo in iso_geo if not geo.is_empty]
|
||||
new_solid_geo = self.flatten_list(iso_geo)
|
||||
if use_simplification:
|
||||
new_solid_geo = [
|
||||
g.simplify(tolerance=simplification_tol) for g in new_solid_geo if not g.is_empty]
|
||||
|
||||
tool_data.update({
|
||||
"name": iso_name,
|
||||
@@ -1940,7 +1934,8 @@ class ToolIsolation(AppTool, Gerber):
|
||||
# Switch notebook to Properties page
|
||||
# self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
|
||||
|
||||
def combined_rest(self, iso_obj, iso2geo, tools_storage, lim_area, negative_dia=None, plot=True):
|
||||
def combined_rest(self, iso_obj, iso2geo, tools_storage, lim_area, negative_dia=None,
|
||||
simp_en=False, simp_tol=0.001, plot=True):
|
||||
"""
|
||||
Isolate the provided Gerber object using "rest machining" strategy
|
||||
|
||||
@@ -1954,6 +1949,10 @@ class ToolIsolation(AppTool, Gerber):
|
||||
:type lim_area: Shapely Polygon or a list of them
|
||||
:param negative_dia: isolate the geometry with a negative value for the tool diameter
|
||||
:type negative_dia: bool
|
||||
:param simp_en: enable simplification of geometry
|
||||
:type simp_en: bool
|
||||
:param simp_tol: simplification tolerance
|
||||
:type simp_tol: float
|
||||
:param plot: if to plot the resulting geometry object
|
||||
:type plot: bool
|
||||
:return: Isolated solid geometry
|
||||
@@ -2071,6 +2070,12 @@ class ToolIsolation(AppTool, Gerber):
|
||||
if not work_geo:
|
||||
break
|
||||
|
||||
total_solid_geometry = self.flatten_list(total_solid_geometry)
|
||||
total_solid_geometry = [g for g in total_solid_geometry if not g.is_empty]
|
||||
if simp_en:
|
||||
total_solid_geometry = [
|
||||
g.simplify(tolerance=simp_tol) for g in total_solid_geometry if not g.is_empty]
|
||||
|
||||
# clean the progressive plotted shapes if it was used
|
||||
if plot and self.app.options["tools_iso_plotting"] == 'progressive':
|
||||
self.temp_shapes.clear(update=True)
|
||||
@@ -2109,6 +2114,12 @@ class ToolIsolation(AppTool, Gerber):
|
||||
not isinstance(geo_obj.solid_geometry, MultiPolygon):
|
||||
geo_obj.solid_geometry = [geo_obj.solid_geometry]
|
||||
|
||||
# simplify geometry if enabled
|
||||
if simp_en:
|
||||
for t in geo_obj.tools:
|
||||
geo_obj.tools[t]['solid_geometry'] = [
|
||||
g.simplify(tolerance=simp_tol) for g in geo_obj.tools[t]['solid_geometry']]
|
||||
|
||||
for g in geo_obj.solid_geometry:
|
||||
if g:
|
||||
break
|
||||
@@ -2138,7 +2149,7 @@ class ToolIsolation(AppTool, Gerber):
|
||||
self.app.inform_shell.emit(msg=msg)
|
||||
|
||||
def combined_normal(self, iso_obj, iso2geo, tools_storage, lim_area, sel_tools, iso_except, extra_passes=None,
|
||||
negative_dia=None, plot=True, prog_plot=None):
|
||||
negative_dia=None, simp_en=False, simp_tol=0.001, plot=True, prog_plot=None):
|
||||
"""
|
||||
|
||||
:param iso_obj: the isolated Gerber object
|
||||
@@ -2157,6 +2168,10 @@ class ToolIsolation(AppTool, Gerber):
|
||||
:type extra_passes: int
|
||||
:param negative_dia: isolate the geometry with a negative value for the tool diameter
|
||||
:type negative_dia: bool
|
||||
:param simp_en: enable simplification of geometry
|
||||
:type simp_en: bool
|
||||
:param simp_tol: simplification tolerance
|
||||
:type simp_tol: float
|
||||
:param plot: if to plot the resulting geometry object
|
||||
:type plot: bool
|
||||
:param prog_plot: If True a progressive plot is used
|
||||
@@ -2291,6 +2306,12 @@ class ToolIsolation(AppTool, Gerber):
|
||||
if not tool_dict['solid_geometry']:
|
||||
tools_storage.pop(tool, None)
|
||||
|
||||
total_solid_geometry = self.flatten_list(total_solid_geometry)
|
||||
total_solid_geometry = [g for g in total_solid_geometry if not g.is_empty]
|
||||
if simp_en:
|
||||
total_solid_geometry = [
|
||||
g.simplify(tolerance=simp_tol) for g in total_solid_geometry if not g.is_empty]
|
||||
|
||||
def iso_init(geo_obj, app_obj):
|
||||
geo_obj.obj_options["tools_mill_tooldia"] = str(tool_dia)
|
||||
|
||||
@@ -2317,6 +2338,12 @@ class ToolIsolation(AppTool, Gerber):
|
||||
not isinstance(geo_obj.solid_geometry, MultiPolygon):
|
||||
geo_obj.solid_geometry = [geo_obj.solid_geometry]
|
||||
|
||||
# simplify geometry if enabled
|
||||
if simp_en:
|
||||
for t in geo_obj.tools:
|
||||
geo_obj.tools[t]['solid_geometry'] = [
|
||||
g.simplify(tolerance=simp_tol) for g in geo_obj.tools[t]['solid_geometry']]
|
||||
|
||||
for g in geo_obj.solid_geometry:
|
||||
if g:
|
||||
break
|
||||
@@ -3230,8 +3257,8 @@ class ToolIsolation(AppTool, Gerber):
|
||||
elif isinstance(work_geo_shp, LinearRing) and work_geo_shp is not None:
|
||||
work_geo_shp = [Polygon(work_geo_shp.coords[::-1])]
|
||||
else:
|
||||
self.app.log.debug("ToolIsolation.generate_rest_geometry() Error --> Unexpected Geometry %s" %
|
||||
type(work_geo))
|
||||
self.app.log.debug(
|
||||
"ToolIsolation.generate_rest_geometry() Error --> Unexpected Geometry %s" % type(work_geo))
|
||||
except Exception as e:
|
||||
self.app.log.error("ToolIsolation.generate_rest_geometry() Error --> %s" % str(e))
|
||||
return 'fail', 'fail'
|
||||
@@ -3294,6 +3321,13 @@ class ToolIsolation(AppTool, Gerber):
|
||||
|
||||
return None
|
||||
|
||||
def flatten_list(self, obj_list):
|
||||
for item in obj_list:
|
||||
if isinstance(item, Iterable) and not isinstance(item, (str, bytes)):
|
||||
yield from self.flatten_list(item)
|
||||
else:
|
||||
yield item
|
||||
|
||||
|
||||
class IsoUI:
|
||||
|
||||
@@ -3780,13 +3814,29 @@ class IsoUI:
|
||||
|
||||
gen_grid.addWidget(self.valid_cb, 4, 0, 1, 2)
|
||||
|
||||
# Simplification Tolerance
|
||||
self.simplify_cb = FCCheckBox('%s' % _("Simplify"))
|
||||
self.simplify_cb.setToolTip(
|
||||
_("All points in the simplified object will be\n"
|
||||
"within the tolerance distance of the original geometry.")
|
||||
)
|
||||
self.sim_tol_entry = FCDoubleSpinner()
|
||||
self.sim_tol_entry.set_precision(self.decimals)
|
||||
self.sim_tol_entry.setSingleStep(10 ** -self.decimals)
|
||||
self.sim_tol_entry.set_range(0.0000, 10000.0000)
|
||||
|
||||
self.sois = OptionalInputSection(self.simplify_cb, [self.sim_tol_entry])
|
||||
|
||||
gen_grid.addWidget(self.simplify_cb, 6, 0)
|
||||
gen_grid.addWidget(self.sim_tol_entry, 6, 1)
|
||||
|
||||
# Exception Areas
|
||||
self.except_cb = FCCheckBox(label=_('Except'))
|
||||
self.except_cb.setToolTip(_("When the isolation geometry is generated,\n"
|
||||
"by checking this, the area of the object below\n"
|
||||
"will be subtracted from the isolation geometry."))
|
||||
self.except_cb.setObjectName("i_except")
|
||||
gen_grid.addWidget(self.except_cb, 6, 0)
|
||||
gen_grid.addWidget(self.except_cb, 8, 0)
|
||||
|
||||
# Type of object to be excepted
|
||||
self.type_excobj_radio = RadioSet([{'label': _("Geometry"), 'value': 'geometry'},
|
||||
@@ -3798,7 +3848,7 @@ class IsoUI:
|
||||
"of objects that will populate the 'Object' combobox.")
|
||||
)
|
||||
|
||||
gen_grid.addWidget(self.type_excobj_radio, 6, 1)
|
||||
gen_grid.addWidget(self.type_excobj_radio, 8, 1)
|
||||
|
||||
# The object to be excepted
|
||||
self.exc_obj_combo = FCComboBox()
|
||||
@@ -3810,7 +3860,7 @@ class IsoUI:
|
||||
self.exc_obj_combo.is_last = True
|
||||
self.exc_obj_combo.obj_type = "gerber"
|
||||
|
||||
gen_grid.addWidget(self.exc_obj_combo, 8, 0, 1, 2)
|
||||
gen_grid.addWidget(self.exc_obj_combo, 10, 0, 1, 2)
|
||||
|
||||
self.e_ois = OptionalInputSection(self.except_cb,
|
||||
[
|
||||
@@ -3833,8 +3883,8 @@ class IsoUI:
|
||||
)
|
||||
self.select_combo.setObjectName("i_selection")
|
||||
|
||||
gen_grid.addWidget(self.select_label, 10, 0)
|
||||
gen_grid.addWidget(self.select_combo, 10, 1)
|
||||
gen_grid.addWidget(self.select_label, 12, 0)
|
||||
gen_grid.addWidget(self.select_combo, 12, 1)
|
||||
|
||||
# Reference Type
|
||||
self.reference_combo_type_label = FCLabel('%s:' % _("Type"))
|
||||
@@ -3842,15 +3892,15 @@ class IsoUI:
|
||||
self.reference_combo_type = FCComboBox2()
|
||||
self.reference_combo_type.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
|
||||
|
||||
gen_grid.addWidget(self.reference_combo_type_label, 12, 0)
|
||||
gen_grid.addWidget(self.reference_combo_type, 12, 1)
|
||||
gen_grid.addWidget(self.reference_combo_type_label, 14, 0)
|
||||
gen_grid.addWidget(self.reference_combo_type, 14, 1)
|
||||
|
||||
self.reference_combo = FCComboBox()
|
||||
self.reference_combo.setModel(self.app.collection)
|
||||
self.reference_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
||||
self.reference_combo.is_last = True
|
||||
|
||||
gen_grid.addWidget(self.reference_combo, 14, 0, 1, 2)
|
||||
gen_grid.addWidget(self.reference_combo, 16, 0, 1, 2)
|
||||
|
||||
self.reference_combo.hide()
|
||||
self.reference_combo_type.hide()
|
||||
@@ -3863,7 +3913,7 @@ class IsoUI:
|
||||
"(holes in the polygon).")
|
||||
)
|
||||
|
||||
gen_grid.addWidget(self.poly_int_cb, 16, 0)
|
||||
gen_grid.addWidget(self.poly_int_cb, 18, 0)
|
||||
|
||||
self.poly_int_cb.hide()
|
||||
|
||||
@@ -3887,7 +3937,7 @@ class IsoUI:
|
||||
|
||||
sel_hlay.addWidget(self.sel_all_btn)
|
||||
sel_hlay.addWidget(self.clear_all_btn)
|
||||
gen_grid.addLayout(sel_hlay, 18, 0, 1, 2)
|
||||
gen_grid.addLayout(sel_hlay, 20, 0, 1, 2)
|
||||
|
||||
# Area Selection shape
|
||||
self.area_shape_label = FCLabel('%s:' % _("Shape"))
|
||||
@@ -3898,8 +3948,8 @@ class IsoUI:
|
||||
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
|
||||
{'label': _("Polygon"), 'value': 'polygon'}])
|
||||
|
||||
gen_grid.addWidget(self.area_shape_label, 20, 0)
|
||||
gen_grid.addWidget(self.area_shape_radio, 20, 1)
|
||||
gen_grid.addWidget(self.area_shape_label, 22, 0)
|
||||
gen_grid.addWidget(self.area_shape_radio, 22, 1)
|
||||
|
||||
self.area_shape_label.hide()
|
||||
self.area_shape_radio.hide()
|
||||
|
||||
Reference in New Issue
Block a user