- In Gerber isolation changed the UI

- in Gerber isolation added the option to selectively isolate only certain polygons
This commit is contained in:
Marius Stanciu 2019-11-25 16:52:37 +02:00
parent aac4fd75ca
commit d5a9e0bb5a
6 changed files with 325 additions and 222 deletions

View File

@ -523,6 +523,7 @@ class App(QtCore.QObject):
"gerber_isooverlap": 0.00393701, "gerber_isooverlap": 0.00393701,
"gerber_milling_type": "cl", "gerber_milling_type": "cl",
"gerber_combine_passes": False, "gerber_combine_passes": False,
"gerber_iso_scope": 'all',
"gerber_noncoppermargin": 0.00393701, "gerber_noncoppermargin": 0.00393701,
"gerber_noncopperrounded": False, "gerber_noncopperrounded": False,
"gerber_bboxmargin": 0.00393701, "gerber_bboxmargin": 0.00393701,
@ -537,6 +538,7 @@ class App(QtCore.QObject):
"gerber_vtipdia": 0.1, "gerber_vtipdia": 0.1,
"gerber_vtipangle": 30, "gerber_vtipangle": 30,
"gerber_vcutz": -0.05, "gerber_vcutz": -0.05,
"gerber_iso_type": "full",
"gerber_buffering": "full", "gerber_buffering": "full",
"gerber_simplification": False, "gerber_simplification": False,
"gerber_simp_tolerance": 0.0005, "gerber_simp_tolerance": 0.0005,
@ -1077,6 +1079,7 @@ class App(QtCore.QObject):
"gerber_isopasses": self.ui.gerber_defaults_form.gerber_opt_group.iso_width_entry, "gerber_isopasses": self.ui.gerber_defaults_form.gerber_opt_group.iso_width_entry,
"gerber_isooverlap": self.ui.gerber_defaults_form.gerber_opt_group.iso_overlap_entry, "gerber_isooverlap": self.ui.gerber_defaults_form.gerber_opt_group.iso_overlap_entry,
"gerber_combine_passes": self.ui.gerber_defaults_form.gerber_opt_group.combine_passes_cb, "gerber_combine_passes": self.ui.gerber_defaults_form.gerber_opt_group.combine_passes_cb,
"gerber_iso_scope": self.ui.gerber_defaults_form.gerber_opt_group.iso_scope_radio,
"gerber_milling_type": self.ui.gerber_defaults_form.gerber_opt_group.milling_type_radio, "gerber_milling_type": self.ui.gerber_defaults_form.gerber_opt_group.milling_type_radio,
"gerber_noncoppermargin": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_margin_entry, "gerber_noncoppermargin": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_margin_entry,
"gerber_noncopperrounded": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_rounded_cb, "gerber_noncopperrounded": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_rounded_cb,
@ -1092,6 +1095,7 @@ class App(QtCore.QObject):
"gerber_vtipdia": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipdia_spinner, "gerber_vtipdia": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipdia_spinner,
"gerber_vtipangle": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipangle_spinner, "gerber_vtipangle": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipangle_spinner,
"gerber_vcutz": self.ui.gerber_defaults_form.gerber_adv_opt_group.cutz_spinner, "gerber_vcutz": self.ui.gerber_defaults_form.gerber_adv_opt_group.cutz_spinner,
"gerber_iso_type": self.ui.gerber_defaults_form.gerber_adv_opt_group.iso_type_radio,
"gerber_buffering": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffering_radio, "gerber_buffering": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffering_radio,
"gerber_simplification": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplify_cb, "gerber_simplification": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplify_cb,
@ -2424,6 +2428,9 @@ class App(QtCore.QObject):
# decide if we have a double click or single click # decide if we have a double click or single click
self.doubleclick = False self.doubleclick = False
# store here the is_dragging value
self.event_is_dragging = False
# variable to store if a command is active (then the var is not None) and which one it is # variable to store if a command is active (then the var is not None) and which one it is
self.command_active = None self.command_active = None
# variable to store the status of moving selection action # variable to store the status of moving selection action
@ -8337,7 +8344,7 @@ class App(QtCore.QObject):
pan_button = 2 pan_button = 2
else: else:
pan_button = 3 pan_button = 3
event_is_dragging = event.is_dragging self.event_is_dragging = event.is_dragging
else: else:
event_pos = (event.xdata, event.ydata) event_pos = (event.xdata, event.ydata)
# Matplotlib has the middle and right buttons mapped in reverse compared with VisPy # Matplotlib has the middle and right buttons mapped in reverse compared with VisPy
@ -8345,7 +8352,7 @@ class App(QtCore.QObject):
pan_button = 3 pan_button = 3
else: else:
pan_button = 2 pan_button = 2
event_is_dragging = self.plotcanvas.is_dragging self.event_is_dragging = self.plotcanvas.is_dragging
# So it can receive key presses # So it can receive key presses
self.plotcanvas.native.setFocus() self.plotcanvas.native.setFocus()
@ -8355,7 +8362,7 @@ class App(QtCore.QObject):
if not origin_click: if not origin_click:
# if the RMB is clicked and mouse is moving over plot then 'panning_action' is True # if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
if event.button == pan_button and event_is_dragging == 1: if event.button == pan_button and self.event_is_dragging == 1:
self.ui.popMenu.mouse_is_panning = True self.ui.popMenu.mouse_is_panning = True
return return
@ -8383,7 +8390,7 @@ class App(QtCore.QObject):
self.mouse = [pos[0], pos[1]] self.mouse = [pos[0], pos[1]]
# if the mouse is moved and the LMB is clicked then the action is a selection # if the mouse is moved and the LMB is clicked then the action is a selection
if event_is_dragging == 1 and event.button == 1: if self.event_is_dragging == 1 and event.button == 1:
self.delete_selection_shape() self.delete_selection_shape()
if dx < 0: if dx < 0:
self.draw_moving_selection_shape(self.pos, pos, color=self.defaults['global_alt_sel_line'], self.draw_moving_selection_shape(self.pos, pos, color=self.defaults['global_alt_sel_line'],

View File

@ -597,7 +597,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
"bboxmargin": 0.0, "bboxmargin": 0.0,
"bboxrounded": False, "bboxrounded": False,
"aperture_display": False, "aperture_display": False,
"follow": False "follow": False,
"iso_scope": 'all',
"iso_type": 'full'
}) })
# type of isolation: 0 = exteriors, 1 = interiors, 2 = complete isolation (both interiors and exteriors) # type of isolation: 0 = exteriors, 1 = interiors, 2 = complete isolation (both interiors and exteriors)
@ -618,6 +620,12 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
# Number of decimals to be used by tools in this class # Number of decimals to be used by tools in this class
self.decimals = 4 self.decimals = 4
# Mouse events
self.mr = self.app.mr
# list to store the polygons selected for isolation
self.poly_list = list()
# Attributes to be included in serialization # Attributes to be included in serialization
# Always append to it because it carries contents # Always append to it because it carries contents
# from predecessors. # from predecessors.
@ -662,7 +670,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
"bboxmargin": self.ui.bbmargin_entry, "bboxmargin": self.ui.bbmargin_entry,
"bboxrounded": self.ui.bbrounded_cb, "bboxrounded": self.ui.bbrounded_cb,
"aperture_display": self.ui.aperture_table_visibility_cb, "aperture_display": self.ui.aperture_table_visibility_cb,
"follow": self.ui.follow_cb "follow": self.ui.follow_cb,
"iso_scope": self.ui.iso_scope_radio,
"iso_type": self.ui.iso_type_radio
}) })
# Fill form fields only on object create # Fill form fields only on object create
@ -672,8 +682,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click) self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click) self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click)
self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click) self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click)
self.ui.generate_ext_iso_button.clicked.connect(self.on_ext_iso_button_click)
self.ui.generate_int_iso_button.clicked.connect(self.on_int_iso_button_click)
self.ui.generate_iso_button.clicked.connect(self.on_iso_button_click) self.ui.generate_iso_button.clicked.connect(self.on_iso_button_click)
self.ui.generate_ncc_button.clicked.connect(self.app.ncclear_tool.run) self.ui.generate_ncc_button.clicked.connect(self.app.ncclear_tool.run)
self.ui.generate_cutout_button.clicked.connect(self.app.cutout_tool.run) self.ui.generate_cutout_button.clicked.connect(self.app.cutout_tool.run)
@ -710,8 +718,8 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
self.ui.aperture_table_visibility_cb.hide() self.ui.aperture_table_visibility_cb.hide()
self.ui.milling_type_label.hide() self.ui.milling_type_label.hide()
self.ui.milling_type_radio.hide() self.ui.milling_type_radio.hide()
self.ui.generate_ext_iso_button.hide() self.ui.iso_type_radio.hide()
self.ui.generate_int_iso_button.hide()
self.ui.follow_cb.hide() self.ui.follow_cb.hide()
self.ui.except_cb.setChecked(False) self.ui.except_cb.setChecked(False)
self.ui.except_cb.hide() self.ui.except_cb.hide()
@ -978,42 +986,16 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
self.app.new_object("geometry", name, geo_init) self.app.new_object("geometry", name, geo_init)
def on_ext_iso_button_click(self, *args):
obj = self.app.collection.get_active()
def worker_task(obj, app_obj):
with self.app.proc_container.new(_("Isolating...")):
if self.ui.follow_cb.get_value() is True:
obj.follow_geo()
# in the end toggle the visibility of the origin object so we can see the generated Geometry
obj.ui.plot_cb.toggle()
else:
app_obj.report_usage("gerber_on_iso_button")
self.read_form()
self.isolate(iso_type=0)
self.app.worker_task.emit({'fcn': worker_task, 'params': [obj, self.app]})
def on_int_iso_button_click(self, *args):
obj = self.app.collection.get_active()
def worker_task(obj, app_obj):
with self.app.proc_container.new(_("Isolating...")):
if self.ui.follow_cb.get_value() is True:
obj.follow_geo()
# in the end toggle the visibility of the origin object so we can see the generated Geometry
obj.ui.plot_cb.toggle()
else:
app_obj.report_usage("gerber_on_iso_button")
self.read_form()
self.isolate(iso_type=1)
self.app.worker_task.emit({'fcn': worker_task, 'params': [obj, self.app]})
def on_iso_button_click(self, *args): def on_iso_button_click(self, *args):
obj = self.app.collection.get_active() obj = self.app.collection.get_active()
self.iso_type = 2
if self.ui.iso_type_radio.get_value() == 'ext':
self.iso_type = 0
if self.ui.iso_type_radio.get_value() == 'int':
self.iso_type = 1
def worker_task(obj, app_obj): def worker_task(obj, app_obj):
with self.app.proc_container.new(_("Isolating...")): with self.app.proc_container.new(_("Isolating...")):
if self.ui.follow_cb.get_value() is True: if self.ui.follow_cb.get_value() is True:
@ -1023,7 +1005,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
else: else:
app_obj.report_usage("gerber_on_iso_button") app_obj.report_usage("gerber_on_iso_button")
self.read_form() self.read_form()
self.isolate()
iso_scope = 'all' if self.ui.iso_scope_radio == 'all' else 'single'
self.isolate_handler(iso_type=self.iso_type, iso_scope=iso_scope)
self.app.worker_task.emit({'fcn': worker_task, 'params': [obj, self.app]}) self.app.worker_task.emit({'fcn': worker_task, 'params': [obj, self.app]})
@ -1053,18 +1037,96 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
except Exception as e: except Exception as e:
return "Operation failed: %s" % str(e) return "Operation failed: %s" % str(e)
def isolate(self, iso_type=None, dia=None, passes=None, overlap=None, outname=None, combine=None, def isolate_handler(self, iso_type, iso_scope):
if iso_scope == 'all':
self.isolate(iso_type=iso_type)
else:
# disengage the grid snapping since it will be hard to find the drills on grid
if self.app.ui.grid_snap_btn.isChecked():
self.grid_status_memory = True
self.app.ui.grid_snap_btn.trigger()
else:
self.grid_status_memory = False
self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_click_release)
if self.app.is_legacy is False:
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
else:
self.app.plotcanvas.graph_event_disconnect(self.app.mr)
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click on polygon to isolate it."))
def on_mouse_click_release(self, event):
if self.app.is_legacy is False:
event_pos = event.pos
event_is_dragging = event.is_dragging
right_button = 2
else:
event_pos = (event.xdata, event.ydata)
event_is_dragging = self.app.plotcanvas.is_dragging
right_button = 3
try:
x = float(event_pos[0])
y = float(event_pos[1])
except TypeError:
return
event_pos = (x, y)
curr_pos = self.app.plotcanvas.translate_coords(event_pos)
if event.button == 1:
clicked_poly = self.find_polygon(point=(curr_pos[0], curr_pos[1]))
if clicked_poly:
self.poly_list.append(clicked_poly)
self.app.inform.emit(
'%s: %d. %s' % (_("Added polygon"),
int(len(self.poly_list)),
_("Click to start adding next polygon or right click to start isolation."))
)
else:
self.app.inform.emit(_("No polygon detected under click position. Try again."))
elif event.button == right_button and self.app.event_is_dragging is False:
# restore the Grid snapping if it was active before
if self.grid_status_memory is True:
self.app.ui.grid_snap_btn.trigger()
if self.app.is_legacy is False:
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_click_release)
else:
self.app.plotcanvas.graph_event_disconnect(self.mr)
self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
self.app.on_mouse_click_release_over_plot)
self.isolate(iso_type=self.iso_type, geometry=self.poly_list)
def isolate(self, iso_type=None, geometry=None, dia=None, passes=None, overlap=None, outname=None, combine=None,
milling_type=None, follow=None, plot=True): milling_type=None, follow=None, plot=True):
""" """
Creates an isolation routing geometry object in the project. Creates an isolation routing geometry object in the project.
:param iso_type: type of isolation to be done: 0 = exteriors, 1 = interiors and 2 = both :param iso_type: type of isolation to be done: 0 = exteriors, 1 = interiors and 2 = both
:param iso_scope: whether to isolate all polygons or single polygpns: 'all' = all, 'single' = one by one, single
:param dia: Tool diameter :param dia: Tool diameter
:param passes: Number of tool widths to cut :param passes: Number of tool widths to cut
:param overlap: Overlap between passes in fraction of tool diameter :param overlap: Overlap between passes in fraction of tool diameter
:param outname: Base name of the output object :param outname: Base name of the output object
:return: None :return: None
""" """
if geometry is None:
if follow:
work_geo = self.follow_geometry
else:
work_geo = self.solid_geometry
else:
work_geo = geometry
if dia is None: if dia is None:
dia = float(self.options["isotooldia"]) dia = float(self.options["isotooldia"])
if passes is None: if passes is None:
@ -1075,28 +1137,33 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
combine = self.options["combine_passes"] combine = self.options["combine_passes"]
else: else:
combine = bool(combine) combine = bool(combine)
if milling_type is None: if milling_type is None:
milling_type = self.options["milling_type"] milling_type = self.options["milling_type"]
if iso_type is None: if iso_type is None:
self.iso_type = 2 iso_t = 2
else: else:
self.iso_type = iso_type iso_t = iso_type
base_name = self.options["name"] base_name = self.options["name"]
def generate_envelope(offset, invert, envelope_iso_type=2, follow=None, passes=0): def generate_envelope(offset, invert, geometry=None, env_iso_type=2, follow=None, nr_passes=0):
# isolation_geometry produces an envelope that is going on the left of the geometry # isolation_geometry produces an envelope that is going on the left of the geometry
# (the copper features). To leave the least amount of burrs on the features # (the copper features). To leave the least amount of burrs on the features
# the tool needs to travel on the right side of the features (this is called conventional milling) # the tool needs to travel on the right side of the features (this is called conventional milling)
# the first pass is the one cutting all of the features, so it needs to be reversed # the first pass is the one cutting all of the features, so it needs to be reversed
# the other passes overlap preceding ones and cut the left over copper. It is better for them # the other passes overlap preceding ones and cut the left over copper. It is better for them
# to cut on the right side of the left over copper i.e on the left side of the features. # to cut on the right side of the left over copper i.e on the left side of the features.
try:
geom = self.isolation_geometry(offset, iso_type=envelope_iso_type, follow=follow, passes=passes) if follow:
except Exception as e: geom = self.isolation_geometry(offset, geometry=geometry, follow=follow)
log.debug('FlatCAMGerber.isolate().generate_envelope() --> %s' % str(e)) else:
return 'fail' try:
geom = self.isolation_geometry(offset, geometry=geometry, iso_type=env_iso_type, passes=nr_passes)
except Exception as e:
log.debug('FlatCAMGerber.isolate().generate_envelope() --> %s' % str(e))
return 'fail'
if invert: if invert:
try: try:
@ -1121,24 +1188,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
return 'fail' return 'fail'
return geom return geom
# if invert:
# try:
# if type(geom) is MultiPolygon:
# pl = []
# for p in geom:
# if p is not None:
# pl.append(Polygon(p.exterior.coords[::-1], p.interiors))
# geom = MultiPolygon(pl)
# elif type(geom) is Polygon and geom is not None:
# geom = Polygon(geom.exterior.coords[::-1], geom.interiors)
# else:
# log.debug("FlatCAMGerber.isolate().generate_envelope() Error --> Unexpected Geometry %s" %
# type(geom))
# except Exception as e:
# log.debug("FlatCAMGerber.isolate().generate_envelope() Error --> %s" % str(e))
# return 'fail'
# return geom
# if float(self.options["isotooldia"]) < 0: # if float(self.options["isotooldia"]) < 0:
# self.options["isotooldia"] = -self.options["isotooldia"] # self.options["isotooldia"] = -self.options["isotooldia"]
@ -1210,30 +1259,26 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
iso_offset = dia * ((2 * i + 1) / 2.0) - (i * overlap * dia) iso_offset = dia * ((2 * i + 1) / 2.0) - (i * overlap * dia)
# if milling type is climb then the move is counter-clockwise around features # if milling type is climb then the move is counter-clockwise around features
if milling_type == 'cl': mill_t = 1 if milling_type == 'cl' else 0
# geom = generate_envelope (offset, i == 0) geom = generate_envelope(iso_offset, mill_t, geometry=work_geo, env_iso_type=iso_t, follow=follow,
geom = generate_envelope(iso_offset, 1, envelope_iso_type=self.iso_type, follow=follow, nr_passes=i)
passes=i)
else:
geom = generate_envelope(iso_offset, 0, envelope_iso_type=self.iso_type, follow=follow,
passes=i)
if geom == 'fail': if geom == 'fail':
app_obj.inform.emit('[ERROR_NOTCL] %s' % app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
_("Isolation geometry could not be generated."))
return 'fail' return 'fail'
geo_obj.solid_geometry.append(geom) geo_obj.solid_geometry.append(geom)
# transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI # transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI
if self.ui.tool_type_radio.get_value() == 'circular': if self.ui.tool_type_radio.get_value() == 'v':
new_cutz = self.app.defaults['geometry_cutz']
new_vtipdia = self.app.defaults['geometry_vtipdia']
new_vtipangle = self.app.defaults['geometry_vtipangle']
tool_type = 'C1'
else:
new_cutz = self.ui.cutz_spinner.get_value() new_cutz = self.ui.cutz_spinner.get_value()
new_vtipdia = self.ui.tipdia_spinner.get_value() new_vtipdia = self.ui.tipdia_spinner.get_value()
new_vtipangle = self.ui.tipangle_spinner.get_value() new_vtipangle = self.ui.tipangle_spinner.get_value()
tool_type = 'V' tool_type = 'V'
else:
new_cutz = self.app.defaults['geometry_cutz']
new_vtipdia = self.app.defaults['geometry_vtipdia']
new_vtipangle = self.app.defaults['geometry_vtipangle']
tool_type = 'C1'
# store here the default data for Geometry Data # store here the default data for Geometry Data
default_data = {} default_data = {}
@ -1280,7 +1325,8 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
# proceed with object creation, if there are empty and the number of them is the length # proceed with object creation, if there are empty and the number of them is the length
# of the list then we have an empty solid_geometry which should raise a Custom Exception # of the list then we have an empty solid_geometry which should raise a Custom Exception
empty_cnt = 0 empty_cnt = 0
if not isinstance(geo_obj.solid_geometry, list): if not isinstance(geo_obj.solid_geometry, list) and \
not isinstance(geo_obj.solid_geometry, MultiPolygon):
geo_obj.solid_geometry = [geo_obj.solid_geometry] geo_obj.solid_geometry = [geo_obj.solid_geometry]
for g in geo_obj.solid_geometry: for g in geo_obj.solid_geometry:
@ -1338,13 +1384,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
geo_obj.options["cnctooldia"] = str(self.options["isotooldia"]) geo_obj.options["cnctooldia"] = str(self.options["isotooldia"])
# if milling type is climb then the move is counter-clockwise around features # if milling type is climb then the move is counter-clockwise around features
if milling_type == 'cl': mill_t = 1 if milling_type == 'cl' else 0
# geo_obj.solid_geometry = generate_envelope(offset, i == 0) mill_t = 1 if milling_type == 'cl' else 0
geom = generate_envelope(offset, 1, envelope_iso_type=self.iso_type, follow=follow, geom = generate_envelope(offset, mill_t, geometry=work_geo, env_iso_type=iso_t, follow=follow,
passes=i) nr_passes=i)
else:
geom = generate_envelope(offset, 0, envelope_iso_type=self.iso_type, follow=follow,
passes=i)
if geom == 'fail': if geom == 'fail':
app_obj.inform.emit('[ERROR_NOTCL] %s' % app_obj.inform.emit('[ERROR_NOTCL] %s' %
_("Isolation geometry could not be generated.")) _("Isolation geometry could not be generated."))

View File

@ -9,6 +9,11 @@ CAD program, and create G-Code for Isolation routing.
================================================= =================================================
25.11.2019
- In Gerber isolation changed the UI
- in Gerber isolation added the option to selectively isolate only certain polygons
23.11.2019 23.11.2019
- in Tool Fiducials added a new fiducial type: chess pattern - in Tool Fiducials added a new fiducial type: chess pattern

109
camlib.py
View File

@ -897,7 +897,7 @@ class Geometry(object):
# #
# return self.flat_geometry, self.flat_geometry_rtree # return self.flat_geometry, self.flat_geometry_rtree
def isolation_geometry(self, offset, iso_type=2, corner=None, follow=None, passes=0): def isolation_geometry(self, offset, geometry=None, iso_type=2, corner=None, follow=None, passes=0):
""" """
Creates contours around geometry at a given Creates contours around geometry at a given
offset distance. offset distance.
@ -916,73 +916,58 @@ class Geometry(object):
# graceful abort requested by the user # graceful abort requested by the user
raise FlatCAMApp.GracefulException raise FlatCAMApp.GracefulException
geo_iso = [] geo_iso = list()
if offset == 0:
if follow: if follow:
geo_iso = self.follow_geometry return geometry
else:
geo_iso = self.solid_geometry if geometry:
working_geo = geometry
else: else:
if follow: working_geo = self.solid_geometry
geo_iso = self.follow_geometry
try:
geo_len = len(working_geo)
except TypeError:
geo_len = 1
old_disp_number = 0
pol_nr = 0
# yet, it can be done by issuing an unary_union in the end, thus getting rid of the overlapping geo
try:
for pol in working_geo:
if self.app.abort_flag:
# graceful abort requested by the user
raise FlatCAMApp.GracefulException
if offset == 0:
geo_iso.append(pol)
else:
corner_type = 1 if corner is None else corner
geo_iso.append(pol.buffer(offset, int(int(self.geo_steps_per_circle) / 4), join_style=corner_type))
pol_nr += 1
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
if old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %s %d: %d%%' %
(_("Pass"), int(passes + 1), int(disp_number)))
old_disp_number = disp_number
except TypeError:
# taking care of the case when the self.solid_geometry is just a single Polygon, not a list or a
# MultiPolygon (not an iterable)
if offset == 0:
geo_iso.append(working_geo)
else: else:
# if isinstance(self.solid_geometry, list): corner_type = 1 if corner is None else corner
# temp_geo = cascaded_union(self.solid_geometry) geo_iso.append(working_geo.buffer(offset, int(int(self.geo_steps_per_circle) / 4),
# else: join_style=corner_type))
# temp_geo = self.solid_geometry
# Remember: do not make a buffer for each element in the solid_geometry because it will cut into self.app.proc_container.update_view_text(' %s' % _("Buffering"))
# other copper features geo_iso = unary_union(geo_iso)
# if corner is None:
# geo_iso = temp_geo.buffer(offset, int(int(self.geo_steps_per_circle) / 4))
# else:
# geo_iso = temp_geo.buffer(offset, int(int(self.geo_steps_per_circle) / 4),
# join_style=corner)
# variables to display the percentage of work done
geo_len = 0
try:
for pol in self.solid_geometry:
geo_len += 1
except TypeError:
geo_len = 1
disp_number = 0
old_disp_number = 0
pol_nr = 0
# yet, it can be done by issuing an unary_union in the end, thus getting rid of the overlapping geo
try:
for pol in self.solid_geometry:
if self.app.abort_flag:
# graceful abort requested by the user
raise FlatCAMApp.GracefulException
if corner is None:
geo_iso.append(pol.buffer(offset, int(int(self.geo_steps_per_circle) / 4)))
else:
geo_iso.append(pol.buffer(offset, int(int(self.geo_steps_per_circle) / 4)),
join_style=corner)
pol_nr += 1
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
if old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %s %d: %d%%' %
(_("Pass"), int(passes + 1), int(disp_number)))
old_disp_number = disp_number
except TypeError:
# taking care of the case when the self.solid_geometry is just a single Polygon, not a list or a
# MultiPolygon (not an iterable)
if corner is None:
geo_iso.append(self.solid_geometry.buffer(offset, int(int(self.geo_steps_per_circle) / 4)))
else:
geo_iso.append(self.solid_geometry.buffer(offset, int(int(self.geo_steps_per_circle) / 4)),
join_style=corner)
self.app.proc_container.update_view_text(' %s' % _("Buffering"))
geo_iso = unary_union(geo_iso)
self.app.proc_container.update_view_text('') self.app.proc_container.update_view_text('')
# end of replaced block # end of replaced block
if follow:
return geo_iso if iso_type == 2:
elif iso_type == 2:
return geo_iso return geo_iso
elif iso_type == 0: elif iso_type == 0:
self.app.proc_container.update_view_text(' %s' % _("Get Exteriors")) self.app.proc_container.update_view_text(' %s' % _("Get Exteriors"))

View File

@ -281,6 +281,7 @@ class GerberObjectUI(ObjectUI):
self.custom_box.addLayout(grid1) self.custom_box.addLayout(grid1)
grid1.setColumnStretch(0, 0) grid1.setColumnStretch(0, 0)
grid1.setColumnStretch(1, 1) grid1.setColumnStretch(1, 1)
grid1.setColumnStretch(2, 1)
# Tool Type # Tool Type
self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type')) self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type'))
@ -290,8 +291,8 @@ class GerberObjectUI(ObjectUI):
"When the 'V-shape' is selected then the tool\n" "When the 'V-shape' is selected then the tool\n"
"diameter will depend on the chosen cut depth.") "diameter will depend on the chosen cut depth.")
) )
self.tool_type_radio = RadioSet([{'label': 'Circular', 'value': 'circular'}, self.tool_type_radio = RadioSet([{'label': _('Circular'), 'value': 'circular'},
{'label': 'V-Shape', 'value': 'v'}]) {'label': _('V-Shape'), 'value': 'v'}])
grid1.addWidget(self.tool_type_label, 0, 0) grid1.addWidget(self.tool_type_label, 0, 0)
grid1.addWidget(self.tool_type_radio, 0, 1, 1, 2) grid1.addWidget(self.tool_type_radio, 0, 1, 1, 2)
@ -396,7 +397,7 @@ class GerberObjectUI(ObjectUI):
grid1.addWidget(self.milling_type_radio, 7, 1, 1, 2) grid1.addWidget(self.milling_type_radio, 7, 1, 1, 2)
# combine all passes CB # combine all passes CB
self.combine_passes_cb = FCCheckBox(label=_('Combine Passes')) self.combine_passes_cb = FCCheckBox(label=_('Combine'))
self.combine_passes_cb.setToolTip( self.combine_passes_cb.setToolTip(
_("Combine all passes into one object") _("Combine all passes into one object")
) )
@ -406,15 +407,15 @@ class GerberObjectUI(ObjectUI):
self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n" self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
"This means that it will cut through\n" "This means that it will cut through\n"
"the middle of the trace.")) "the middle of the trace."))
grid1.addWidget(self.combine_passes_cb, 8, 0)
# avoid an area from isolation # avoid an area from isolation
self.except_cb = FCCheckBox(label=_('Except')) self.except_cb = FCCheckBox(label=_('Except'))
grid1.addWidget(self.follow_cb, 8, 1)
self.except_cb.setToolTip(_("When the isolation geometry is generated,\n" self.except_cb.setToolTip(_("When the isolation geometry is generated,\n"
"by checking this, the area of the object bellow\n" "by checking this, the area of the object bellow\n"
"will be subtracted from the isolation geometry.")) "will be subtracted from the isolation geometry."))
grid1.addWidget(self.combine_passes_cb, 8, 0)
grid1.addWidget(self.follow_cb, 8, 1)
grid1.addWidget(self.except_cb, 8, 2) grid1.addWidget(self.except_cb, 8, 2)
# ## Form Layout # ## Form Layout
@ -454,8 +455,50 @@ class GerberObjectUI(ObjectUI):
form_layout.addRow(self.obj_label, self.obj_combo) form_layout.addRow(self.obj_label, self.obj_combo)
self.gen_iso_label = QtWidgets.QLabel("<b>%s</b>" % _("Generate Isolation Geometry")) # ---------------------------------------------- #
self.gen_iso_label.setToolTip( # --------- Isolation scope -------------------- #
# ---------------------------------------------- #
self.iso_scope_label = QtWidgets.QLabel('%s:' % _('Scope'))
self.iso_scope_label.setToolTip(
_("Isolation scope. Choose what to isolate:\n"
"- 'All' -> Isolate all the polygons in the object\n"
"- 'Single' -> Isolate a single polygon.")
)
self.iso_scope_radio = RadioSet([{'label': _('All'), 'value': 'all'},
{'label': _('Single'), 'value': 'single'}])
grid1.addWidget(self.iso_scope_label, 10, 0)
grid1.addWidget(self.iso_scope_radio, 10, 1, 1, 2)
# ---------------------------------------------- #
# --------- Isolation type -------------------- #
# ---------------------------------------------- #
self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
self.iso_type_label.setToolTip(
_("Choose how the isolation will be executed:\n"
"- 'Full' -> complete isolation of polygons\n"
"- 'Ext' -> will isolate only on the outside\n"
"- 'Int' -> will isolate only on the inside\n"
"'Exterior' isolation is almost always possible\n"
"(with the right tool) but 'Interior'\n"
"isolation can be done only when there is an opening\n"
"inside of the polygon (e.g polygon is a 'doughnut' shape).")
)
self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'},
{'label': _('Ext'), 'value': 'ext'},
{'label': _('Int'), 'value': 'int'}])
grid1.addWidget(self.iso_type_label, 11, 0)
grid1.addWidget(self.iso_type_radio, 11, 1, 1, 2)
self.generate_iso_button = QtWidgets.QPushButton("%s" % _("Generate Isolation Geometry"))
self.generate_iso_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.generate_iso_button.setToolTip(
_("Create a Geometry object with toolpaths to cut \n" _("Create a Geometry object with toolpaths to cut \n"
"isolation outside, inside or on both sides of the\n" "isolation outside, inside or on both sides of the\n"
"object. For a Gerber object outside means outside\n" "object. For a Gerber object outside means outside\n"
@ -466,7 +509,7 @@ class GerberObjectUI(ObjectUI):
"inside the actual Gerber feature, use a negative tool\n" "inside the actual Gerber feature, use a negative tool\n"
"diameter above.") "diameter above.")
) )
grid1.addWidget(self.gen_iso_label, 10, 0, 1, 3) grid1.addWidget(self.generate_iso_button, 12, 0, 1, 3)
self.create_buffer_button = QtWidgets.QPushButton(_('Buffer Solid Geometry')) self.create_buffer_button = QtWidgets.QPushButton(_('Buffer Solid Geometry'))
self.create_buffer_button.setToolTip( self.create_buffer_button.setToolTip(
@ -475,48 +518,15 @@ class GerberObjectUI(ObjectUI):
"Clicking this will create the buffered geometry\n" "Clicking this will create the buffered geometry\n"
"required for isolation.") "required for isolation.")
) )
grid1.addWidget(self.create_buffer_button, 11, 0, 1, 3) grid1.addWidget(self.create_buffer_button, 13, 0, 1, 2)
self.generate_iso_button = QtWidgets.QPushButton(_('FULL Geo'))
self.generate_iso_button.setToolTip(
_("Create the Geometry Object\n"
"for isolation routing. It contains both\n"
"the interiors and exteriors geometry.")
)
grid1.addWidget(self.generate_iso_button, 12, 0)
hlay_1 = QtWidgets.QHBoxLayout()
grid1.addLayout(hlay_1, 12, 1, 1, 2)
self.generate_ext_iso_button = QtWidgets.QPushButton(_('Ext Geo'))
self.generate_ext_iso_button.setToolTip(
_("Create the Geometry Object\n"
"for isolation routing containing\n"
"only the exteriors geometry.")
)
# self.generate_ext_iso_button.setMinimumWidth(100)
hlay_1.addWidget(self.generate_ext_iso_button)
self.generate_int_iso_button = QtWidgets.QPushButton(_('Int Geo'))
self.generate_int_iso_button.setToolTip(
_("Create the Geometry Object\n"
"for isolation routing containing\n"
"only the interiors geometry.")
)
# self.generate_ext_iso_button.setMinimumWidth(90)
hlay_1.addWidget(self.generate_int_iso_button)
self.ohis_iso = OptionalHideInputSection( self.ohis_iso = OptionalHideInputSection(
self.except_cb, self.except_cb,
[self.type_obj_combo, self.type_obj_combo_label, self.obj_combo, self.obj_label], [self.type_obj_combo, self.type_obj_combo_label, self.obj_combo, self.obj_label],
logic=True logic=True
) )
# when the follow checkbox is checked then the exteriors and interiors isolation generation buttons
# are disabled as is doesn't make sense to have them enabled due of the nature of "follow"
self.ois_iso = OptionalInputSection(self.follow_cb,
[self.generate_int_iso_button, self.generate_ext_iso_button], logic=False)
grid1.addWidget(QtWidgets.QLabel(''), 13, 0) grid1.addWidget(QtWidgets.QLabel(''), 14, 0)
# ########################################### # ###########################################
# ########## NEW GRID ####################### # ########## NEW GRID #######################
@ -562,6 +572,11 @@ class GerberObjectUI(ObjectUI):
grid2.addWidget(self.board_cutout_label, 2, 0) grid2.addWidget(self.board_cutout_label, 2, 0)
grid2.addWidget(self.generate_cutout_button, 2, 1) grid2.addWidget(self.generate_cutout_button, 2, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid2.addWidget(separator_line, 3, 0, 1, 2)
# ## Non-copper regions # ## Non-copper regions
self.noncopper_label = QtWidgets.QLabel("<b>%s</b>" % _("Non-copper regions")) self.noncopper_label = QtWidgets.QLabel("<b>%s</b>" % _("Non-copper regions"))
self.noncopper_label.setToolTip( self.noncopper_label.setToolTip(
@ -572,7 +587,7 @@ class GerberObjectUI(ObjectUI):
"copper from a specified region.") "copper from a specified region.")
) )
grid2.addWidget(self.noncopper_label, 3, 0, 1, 2) grid2.addWidget(self.noncopper_label, 4, 0, 1, 2)
# Margin # Margin
bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin')) bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
@ -588,8 +603,8 @@ class GerberObjectUI(ObjectUI):
self.noncopper_margin_entry.set_precision(self.decimals) self.noncopper_margin_entry.set_precision(self.decimals)
self.noncopper_margin_entry.setSingleStep(0.1) self.noncopper_margin_entry.setSingleStep(0.1)
grid2.addWidget(bmlabel, 4, 0) grid2.addWidget(bmlabel, 5, 0)
grid2.addWidget(self.noncopper_margin_entry, 4, 1) grid2.addWidget(self.noncopper_margin_entry, 5, 1)
# Rounded corners # Rounded corners
self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo")) self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo"))
@ -599,8 +614,13 @@ class GerberObjectUI(ObjectUI):
self.noncopper_rounded_cb.setMinimumWidth(90) self.noncopper_rounded_cb.setMinimumWidth(90)
self.generate_noncopper_button = QtWidgets.QPushButton(_('Generate Geo')) self.generate_noncopper_button = QtWidgets.QPushButton(_('Generate Geo'))
grid2.addWidget(self.noncopper_rounded_cb, 5, 0) grid2.addWidget(self.noncopper_rounded_cb, 6, 0)
grid2.addWidget(self.generate_noncopper_button, 5, 1) grid2.addWidget(self.generate_noncopper_button, 6, 1)
separator_line1 = QtWidgets.QFrame()
separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken)
grid2.addWidget(separator_line1, 7, 0, 1, 2)
# ## Bounding box # ## Bounding box
self.boundingbox_label = QtWidgets.QLabel('<b>%s</b>' % _('Bounding Box')) self.boundingbox_label = QtWidgets.QLabel('<b>%s</b>' % _('Bounding Box'))
@ -609,7 +629,7 @@ class GerberObjectUI(ObjectUI):
"Square shape.") "Square shape.")
) )
grid2.addWidget(self.boundingbox_label, 6, 0, 1, 2) grid2.addWidget(self.boundingbox_label, 8, 0, 1, 2)
bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin')) bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
bbmargin.setToolTip( bbmargin.setToolTip(
@ -622,8 +642,8 @@ class GerberObjectUI(ObjectUI):
self.bbmargin_entry.set_precision(self.decimals) self.bbmargin_entry.set_precision(self.decimals)
self.bbmargin_entry.setSingleStep(0.1) self.bbmargin_entry.setSingleStep(0.1)
grid2.addWidget(bbmargin, 7, 0) grid2.addWidget(bbmargin, 9, 0)
grid2.addWidget(self.bbmargin_entry, 7, 1) grid2.addWidget(self.bbmargin_entry, 9, 1)
self.bbrounded_cb = FCCheckBox(label=_("Rounded Geo")) self.bbrounded_cb = FCCheckBox(label=_("Rounded Geo"))
self.bbrounded_cb.setToolTip( self.bbrounded_cb.setToolTip(
@ -638,9 +658,13 @@ class GerberObjectUI(ObjectUI):
self.generate_bb_button.setToolTip( self.generate_bb_button.setToolTip(
_("Generate the Geometry object.") _("Generate the Geometry object.")
) )
grid2.addWidget(self.bbrounded_cb, 8, 0) grid2.addWidget(self.bbrounded_cb, 10, 0)
grid2.addWidget(self.generate_bb_button, 8, 1) grid2.addWidget(self.generate_bb_button, 10, 1)
separator_line2 = QtWidgets.QFrame()
separator_line2.setFrameShape(QtWidgets.QFrame.HLine)
separator_line2.setFrameShadow(QtWidgets.QFrame.Sunken)
grid2.addWidget(separator_line2, 11, 0, 1, 2)
class ExcellonObjectUI(ObjectUI): class ExcellonObjectUI(ObjectUI):
""" """

View File

@ -1344,9 +1344,10 @@ class GerberOptPrefGroupUI(OptionsGroupUI):
_("Width of the isolation gap in\n" _("Width of the isolation gap in\n"
"number (integer) of tool widths.") "number (integer) of tool widths.")
) )
grid0.addWidget(passlabel, 1, 0)
self.iso_width_entry = FCSpinner() self.iso_width_entry = FCSpinner()
self.iso_width_entry.setRange(1, 999) self.iso_width_entry.setRange(1, 999)
grid0.addWidget(passlabel, 1, 0)
grid0.addWidget(self.iso_width_entry, 1, 1) grid0.addWidget(self.iso_width_entry, 1, 1)
# Pass overlap # Pass overlap
@ -1356,14 +1357,28 @@ class GerberOptPrefGroupUI(OptionsGroupUI):
"Example:\n" "Example:\n"
"A value here of 0.25 means an overlap of 25%% from the tool diameter found above.") "A value here of 0.25 means an overlap of 25%% from the tool diameter found above.")
) )
grid0.addWidget(overlabel, 2, 0)
self.iso_overlap_entry = FCDoubleSpinner() self.iso_overlap_entry = FCDoubleSpinner()
self.iso_overlap_entry.set_precision(3) self.iso_overlap_entry.set_precision(3)
self.iso_overlap_entry.setWrapping(True) self.iso_overlap_entry.setWrapping(True)
self.iso_overlap_entry.setRange(0.000, 0.999) self.iso_overlap_entry.setRange(0.000, 0.999)
self.iso_overlap_entry.setSingleStep(0.1) self.iso_overlap_entry.setSingleStep(0.1)
grid0.addWidget(overlabel, 2, 0)
grid0.addWidget(self.iso_overlap_entry, 2, 1) grid0.addWidget(self.iso_overlap_entry, 2, 1)
# Isolation Scope
self.iso_scope_label = QtWidgets.QLabel('%s:' % _('Scope'))
self.iso_scope_label.setToolTip(
_("Isolation scope. Choose what to isolate:\n"
"- 'All' -> Isolate all the polygons in the object\n"
"- 'Single' -> Isolate a single polygon.")
)
self.iso_scope_radio = RadioSet([{'label': _('All'), 'value': 'all'},
{'label': _('Single'), 'value': 'single'}])
grid0.addWidget(self.iso_scope_label, 3, 0)
grid0.addWidget(self.iso_scope_radio, 3, 1, 1, 2)
# Milling Type # Milling Type
milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type')) milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
milling_type_label.setToolTip( milling_type_label.setToolTip(
@ -1371,17 +1386,17 @@ class GerberOptPrefGroupUI(OptionsGroupUI):
"- climb / best for precision milling and to reduce tool usage\n" "- climb / best for precision milling and to reduce tool usage\n"
"- conventional / useful when there is no backlash compensation") "- conventional / useful when there is no backlash compensation")
) )
grid0.addWidget(milling_type_label, 3, 0) grid0.addWidget(milling_type_label, 4, 0)
self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'}, self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
{'label': _('Conv.'), 'value': 'cv'}]) {'label': _('Conv.'), 'value': 'cv'}])
grid0.addWidget(self.milling_type_radio, 3, 1) grid0.addWidget(self.milling_type_radio, 4, 1)
# Combine passes # Combine passes
self.combine_passes_cb = FCCheckBox(label=_('Combine Passes')) self.combine_passes_cb = FCCheckBox(label=_('Combine Passes'))
self.combine_passes_cb.setToolTip( self.combine_passes_cb.setToolTip(
_("Combine all passes into one object") _("Combine all passes into one object")
) )
grid0.addWidget(self.combine_passes_cb, 4, 0, 1, 2) grid0.addWidget(self.combine_passes_cb, 5, 0, 1, 2)
# ## Clear non-copper regions # ## Clear non-copper regions
self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Non-copper regions")) self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Non-copper regions"))
@ -1537,9 +1552,29 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
self.cutz_spinner.set_range(-99.9999, -0.0001) self.cutz_spinner.set_range(-99.9999, -0.0001)
self.cutz_spinner.setSingleStep(0.1) self.cutz_spinner.setSingleStep(0.1)
self.cutz_spinner.setWrapping(True) self.cutz_spinner.setWrapping(True)
grid0.addWidget(self.cutzlabel, 5, 0) grid0.addWidget(self.cutzlabel, 5, 0)
grid0.addWidget(self.cutz_spinner, 5, 1, 1, 2) grid0.addWidget(self.cutz_spinner, 5, 1, 1, 2)
# Isolation Type
self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
self.iso_type_label.setToolTip(
_("Choose how the isolation will be executed:\n"
"- 'Full' -> complete isolation of polygons\n"
"- 'Ext' -> will isolate only on the outside\n"
"- 'Int' -> will isolate only on the inside\n"
"'Exterior' isolation is almost always possible\n"
"(with the right tool) but 'Interior'\n"
"isolation can be done only when there is an opening\n"
"inside of the polygon (e.g polygon is a 'doughnut' shape).")
)
self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'},
{'label': _('Exterior'), 'value': 'ext'},
{'label': _('Interior'), 'value': 'int'}])
grid0.addWidget(self.iso_type_label, 6, 0,)
grid0.addWidget(self.iso_type_radio, 6, 1, 1, 2)
# Buffering Type # Buffering Type
buffering_label = QtWidgets.QLabel('%s:' % _('Buffering')) buffering_label = QtWidgets.QLabel('%s:' % _('Buffering'))
buffering_label.setToolTip( buffering_label.setToolTip(
@ -1550,8 +1585,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
) )
self.buffering_radio = RadioSet([{'label': _('None'), 'value': 'no'}, self.buffering_radio = RadioSet([{'label': _('None'), 'value': 'no'},
{'label': _('Full'), 'value': 'full'}]) {'label': _('Full'), 'value': 'full'}])
grid0.addWidget(buffering_label, 6, 0) grid0.addWidget(buffering_label, 7, 0)
grid0.addWidget(self.buffering_radio, 6, 1) grid0.addWidget(self.buffering_radio, 7, 1)
# Simplification # Simplification
self.simplify_cb = FCCheckBox(label=_('Simplify')) self.simplify_cb = FCCheckBox(label=_('Simplify'))
@ -1560,7 +1595,7 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
"loaded with simplification having a set tolerance.\n" "loaded with simplification having a set tolerance.\n"
"<<WARNING>>: Don't change this unless you know what you are doing !!!") "<<WARNING>>: Don't change this unless you know what you are doing !!!")
) )
grid0.addWidget(self.simplify_cb, 7, 0, 1, 2) grid0.addWidget(self.simplify_cb, 8, 0, 1, 2)
# Simplification tolerance # Simplification tolerance
self.simplification_tol_label = QtWidgets.QLabel(_('Tolerance')) self.simplification_tol_label = QtWidgets.QLabel(_('Tolerance'))
@ -1572,11 +1607,14 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
self.simplification_tol_spinner.setRange(0.00000, 0.01000) self.simplification_tol_spinner.setRange(0.00000, 0.01000)
self.simplification_tol_spinner.setSingleStep(0.0001) self.simplification_tol_spinner.setSingleStep(0.0001)
grid0.addWidget(self.simplification_tol_label, 8, 0) grid0.addWidget(self.simplification_tol_label, 9, 0)
grid0.addWidget(self.simplification_tol_spinner, 8, 1) grid0.addWidget(self.simplification_tol_spinner, 9, 1)
self.ois_simplif = OptionalInputSection(self.simplify_cb, self.ois_simplif = OptionalInputSection(
[self.simplification_tol_label, self.simplification_tol_spinner], self.simplify_cb,
logic=True) [
self.simplification_tol_label, self.simplification_tol_spinner
],
logic=True)
self.layout.addStretch() self.layout.addStretch()