- fixed the Tool Isolation when using the 'follow' parameter
- in Isolation Tool when the Rest machining is checked the combine parameter is set True automatically because the rest machining concept make sense only when all tools are used together
This commit is contained in:
parent
8d16bebf44
commit
52dbb1aa6d
|
@ -205,17 +205,9 @@ class GerberObjectUI(ObjectUI):
|
|||
self.multicolored_cb.setMinimumWidth(55)
|
||||
grid0.addWidget(self.multicolored_cb, 0, 2)
|
||||
|
||||
# Plot CB
|
||||
self.plot_lbl = FCLabel('%s' % _("Plot"))
|
||||
self.plot_lbl.setToolTip(_("Plot (show) this object."))
|
||||
self.plot_cb = FCCheckBox()
|
||||
|
||||
grid0.addWidget(self.plot_lbl, 1, 0)
|
||||
grid0.addWidget(self.plot_cb, 1, 1)
|
||||
|
||||
# ## Object name
|
||||
self.name_hlay = QtWidgets.QHBoxLayout()
|
||||
self.custom_box.addLayout(self.name_hlay)
|
||||
grid0.addLayout(self.name_hlay, 1, 0, 1, 3)
|
||||
|
||||
name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
|
||||
self.name_entry = FCEntry()
|
||||
|
@ -223,6 +215,28 @@ class GerberObjectUI(ObjectUI):
|
|||
self.name_hlay.addWidget(name_label)
|
||||
self.name_hlay.addWidget(self.name_entry)
|
||||
|
||||
# Plot CB
|
||||
self.plot_lbl = FCLabel('%s' % _("Plot"))
|
||||
self.plot_lbl.setToolTip(_("Plot (show) this object."))
|
||||
self.plot_cb = FCCheckBox()
|
||||
|
||||
grid0.addWidget(self.plot_lbl, 2, 0)
|
||||
grid0.addWidget(self.plot_cb, 2, 1)
|
||||
|
||||
# generate follow
|
||||
self.follow_lbl = FCLabel('%s:' % _("Follow"))
|
||||
self.follow_lbl.setToolTip(_("Generate a 'Follow' geometry.\n"
|
||||
"This means that it will cut through\n"
|
||||
"the middle of the trace."))
|
||||
self.follow_lbl.setMinimumWidth(90)
|
||||
self.follow_cb = FCCheckBox()
|
||||
|
||||
hf_lay = QtWidgets.QHBoxLayout()
|
||||
self.custom_box.addLayout(hf_lay)
|
||||
hf_lay.addWidget(self.follow_lbl)
|
||||
hf_lay.addWidget(self.follow_cb)
|
||||
hf_lay.addStretch()
|
||||
|
||||
hlay_plot = QtWidgets.QHBoxLayout()
|
||||
self.custom_box.addLayout(hlay_plot)
|
||||
|
||||
|
@ -284,20 +298,6 @@ class GerberObjectUI(ObjectUI):
|
|||
# start with apertures table hidden
|
||||
self.apertures_table.setVisible(False)
|
||||
|
||||
# generate follow
|
||||
self.follow_lbl = FCLabel('%s:' % _("Follow"))
|
||||
self.follow_lbl.setToolTip(_("Generate a 'Follow' geometry.\n"
|
||||
"This means that it will cut through\n"
|
||||
"the middle of the trace."))
|
||||
self.follow_lbl.setMinimumWidth(90)
|
||||
self.follow_cb = FCCheckBox()
|
||||
|
||||
hf_lay = QtWidgets.QHBoxLayout()
|
||||
self.custom_box.addLayout(hf_lay)
|
||||
hf_lay.addWidget(self.follow_lbl)
|
||||
hf_lay.addWidget(self.follow_cb)
|
||||
hf_lay.addStretch()
|
||||
|
||||
# Buffer Geometry
|
||||
self.create_buffer_button = QtWidgets.QPushButton(_('Buffer Solid Geometry'))
|
||||
self.create_buffer_button.setToolTip(
|
||||
|
|
|
@ -636,31 +636,19 @@ class ToolIsolation(AppTool, Gerber):
|
|||
|
||||
self.sel_rect = []
|
||||
|
||||
self.bound_obj_name = ""
|
||||
self.bound_obj = None
|
||||
|
||||
self.ncc_dia_list = []
|
||||
self.iso_dia_list = []
|
||||
self.has_offset = None
|
||||
self.o_name = None
|
||||
self.overlap = None
|
||||
self.connect = None
|
||||
self.contour = None
|
||||
self.rest = None
|
||||
|
||||
self.first_click = False
|
||||
self.cursor_pos = None
|
||||
self.mouse_is_dragging = False
|
||||
|
||||
# store here the points for the "Polygon" area selection shape
|
||||
self.points = []
|
||||
|
||||
# set this as True when in middle of drawing a "Polygon" area selection shape
|
||||
# it is made False by first click to signify that the shape is complete
|
||||
self.poly_drawn = False
|
||||
|
||||
self.mm = None
|
||||
self.mr = None
|
||||
|
||||
self.kp = None
|
||||
|
||||
# store geometry from Polygon selection
|
||||
|
@ -668,16 +656,23 @@ class ToolIsolation(AppTool, Gerber):
|
|||
|
||||
self.grid_status_memory = self.app.ui.grid_snap_btn.isChecked()
|
||||
|
||||
# store here the state of the combine_cb GUI element
|
||||
# used when the rest machining is toggled
|
||||
self.old_combine_state = None
|
||||
|
||||
# store here solid_geometry when there are tool with isolation job
|
||||
self.solid_geometry = []
|
||||
|
||||
self.select_method = None
|
||||
self.tool_type_item_options = []
|
||||
|
||||
self.grb_circle_steps = int(self.app.defaults["gerber_circle_steps"])
|
||||
|
||||
self.tooldia = None
|
||||
|
||||
# multiprocessing
|
||||
self.pool = self.app.pool
|
||||
self.results = []
|
||||
|
||||
self.form_fields = {
|
||||
"tools_iso_passes": self.passes_entry,
|
||||
"tools_iso_overlap": self.iso_overlap_entry,
|
||||
|
@ -890,9 +885,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||
|
||||
# reset those objects on a new run
|
||||
self.grb_obj = None
|
||||
self.bound_obj = None
|
||||
self.obj_name = ''
|
||||
self.bound_obj_name = ''
|
||||
|
||||
self.build_ui()
|
||||
self.app.ui.notebook.setTabText(2, _("Isolation Tool"))
|
||||
|
@ -1081,8 +1074,6 @@ class ToolIsolation(AppTool, Gerber):
|
|||
|
||||
self.obj_name = ""
|
||||
self.grb_obj = None
|
||||
self.bound_obj_name = ""
|
||||
self.bound_obj = None
|
||||
|
||||
self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
|
||||
self.units = self.app.defaults['units'].upper()
|
||||
|
@ -1338,10 +1329,17 @@ class ToolIsolation(AppTool, Gerber):
|
|||
self.order_radio.set_value('rev')
|
||||
self.ncc_order_label.setDisabled(True)
|
||||
self.order_radio.setDisabled(True)
|
||||
|
||||
self.old_combine_state = self.combine_passes_cb.get_value()
|
||||
self.combine_passes_cb.set_value(True)
|
||||
self.combine_passes_cb.setDisabled(True)
|
||||
else:
|
||||
self.ncc_order_label.setDisabled(False)
|
||||
self.order_radio.setDisabled(False)
|
||||
|
||||
self.combine_passes_cb.set_value(self.old_combine_state)
|
||||
self.combine_passes_cb.setDisabled(False)
|
||||
|
||||
def on_tooltable_cellwidget_change(self):
|
||||
cw = self.sender()
|
||||
assert isinstance(cw, QtWidgets.QComboBox), \
|
||||
|
@ -1600,7 +1598,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||
|
||||
self.app.worker_task.emit({'fcn': buffer_task, 'params': []})
|
||||
|
||||
def on_iso_button_click(self, *args):
|
||||
def on_iso_button_click(self):
|
||||
|
||||
self.obj_name = self.object_combo.currentText()
|
||||
|
||||
|
@ -1666,8 +1664,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||
selection = self.select_combo.get_value()
|
||||
|
||||
if selection == _("All"):
|
||||
full_geo = isolated_obj.solid_geometry
|
||||
self.isolate(isolated_obj=isolated_obj, geometry=full_geo)
|
||||
self.isolate(isolated_obj=isolated_obj)
|
||||
elif selection == _("Area Selection"):
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
|
||||
|
||||
|
@ -1716,149 +1713,23 @@ class ToolIsolation(AppTool, Gerber):
|
|||
:type isolated_obj: AppObjects.FlatCAMGerber.GerberObject
|
||||
:param geometry: specific geometry to isolate
|
||||
:type geometry: List of Shapely polygon
|
||||
:param limited_area: if not None clear only this area
|
||||
:param limited_area: if not None isolate only this area
|
||||
:type limited_area: Shapely Polygon or a list of them
|
||||
:param plot: if to plot the resulting geometry object
|
||||
:type plot: bool
|
||||
:return: None
|
||||
"""
|
||||
|
||||
iso_name = isolated_obj.options["name"]
|
||||
combine = self.combine_passes_cb.get_value()
|
||||
tools_storage = self.iso_tools
|
||||
|
||||
if combine:
|
||||
total_solid_geometry = []
|
||||
|
||||
for tool in tools_storage:
|
||||
tool_dia = tools_storage[tool]['tooldia']
|
||||
tool_type = tools_storage[tool]['tool_type']
|
||||
tool_data = tools_storage[tool]['data']
|
||||
|
||||
to_follow = tool_data['tools_iso_follow']
|
||||
|
||||
work_geo = geometry
|
||||
if work_geo is None:
|
||||
work_geo = isolated_obj.follow_geometry if to_follow else isolated_obj.solid_geometry
|
||||
|
||||
iso_t = {
|
||||
'ext': 0,
|
||||
'int': 1,
|
||||
'full': 2
|
||||
}[tool_data['tools_iso_isotype']]
|
||||
|
||||
passes = tool_data['tools_iso_passes']
|
||||
overlap = tool_data['tools_iso_overlap']
|
||||
overlap /= 100.0
|
||||
|
||||
milling_type = tool_data['tools_iso_milling_type']
|
||||
|
||||
iso_except = tool_data['tools_iso_isoexcept']
|
||||
|
||||
outname = "%s_%.*f" % (isolated_obj.options["name"], self.decimals, float(tool_dia))
|
||||
|
||||
iso_name = outname + "_iso"
|
||||
if iso_t == 0:
|
||||
iso_name = outname + "_ext_iso"
|
||||
elif iso_t == 1:
|
||||
iso_name = outname + "_int_iso"
|
||||
|
||||
# transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI
|
||||
if tool_type.lower() == 'v':
|
||||
new_cutz = self.ui.cutz_spinner.get_value()
|
||||
new_vtipdia = self.ui.tipdia_spinner.get_value()
|
||||
new_vtipangle = self.ui.tipangle_spinner.get_value()
|
||||
tool_type = 'V'
|
||||
tool_data.update({
|
||||
"name": iso_name,
|
||||
"cutz": new_cutz,
|
||||
"vtipdia": new_vtipdia,
|
||||
"vtipangle": new_vtipangle,
|
||||
})
|
||||
else:
|
||||
tool_data.update({
|
||||
"name": iso_name,
|
||||
})
|
||||
tool_type = 'C1'
|
||||
|
||||
solid_geo = []
|
||||
for nr_pass in range(passes):
|
||||
iso_offset = tool_dia * ((2 * nr_pass + 1) / 2.0000001) - (nr_pass * overlap * tool_dia)
|
||||
|
||||
# if milling type is climb then the move is counter-clockwise around features
|
||||
mill_dir = 1 if milling_type == 'cl' else 0
|
||||
|
||||
iso_geo = self.generate_envelope(iso_offset, mill_dir, geometry=work_geo, env_iso_type=iso_t,
|
||||
follow=to_follow, nr_passes=nr_pass)
|
||||
if iso_geo == 'fail':
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
|
||||
continue
|
||||
try:
|
||||
for geo in iso_geo:
|
||||
solid_geo.append(geo)
|
||||
except TypeError:
|
||||
solid_geo.append(iso_geo)
|
||||
|
||||
# ############################################################
|
||||
# ########## AREA SUBTRACTION ################################
|
||||
# ############################################################
|
||||
if iso_except:
|
||||
self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo"))
|
||||
solid_geo = self.area_subtraction(solid_geo)
|
||||
|
||||
if limited_area:
|
||||
self.app.proc_container.update_view_text(' %s' % _("Intersecting Geo"))
|
||||
solid_geo = self.area_intersection(solid_geo, intersection_geo=limited_area)
|
||||
|
||||
tools_storage.update({
|
||||
tool: {
|
||||
'tooldia': float(tool_dia),
|
||||
'offset': 'Path',
|
||||
'offset_value': 0.0,
|
||||
'type': _('Rough'),
|
||||
'tool_type': tool_type,
|
||||
'data': tool_data,
|
||||
'solid_geometry': deepcopy(solid_geo)
|
||||
}
|
||||
})
|
||||
|
||||
total_solid_geometry += solid_geo
|
||||
|
||||
def iso_init(geo_obj, app_obj):
|
||||
geo_obj.options["cnctooldia"] = str(tool_dia)
|
||||
|
||||
geo_obj.tools = dict(tools_storage)
|
||||
geo_obj.solid_geometry = total_solid_geometry
|
||||
# even if combine is checked, one pass is still single-geo
|
||||
|
||||
if len(self.iso_tools) > 1:
|
||||
geo_obj.multigeo = True
|
||||
else:
|
||||
passes_no = float(self.iso_tools[0]['data']['tools_iso_passes'])
|
||||
geo_obj.multigeo = True if passes_no > 1 else False
|
||||
|
||||
# detect if solid_geometry is empty and this require list flattening which is "heavy"
|
||||
# or just looking in the lists (they are one level depth) and if any is not empty
|
||||
# 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
|
||||
empty_cnt = 0
|
||||
if not isinstance(geo_obj.solid_geometry, list) and \
|
||||
not isinstance(geo_obj.solid_geometry, MultiPolygon):
|
||||
geo_obj.solid_geometry = [geo_obj.solid_geometry]
|
||||
|
||||
for g in geo_obj.solid_geometry:
|
||||
if g:
|
||||
break
|
||||
else:
|
||||
empty_cnt += 1
|
||||
|
||||
if empty_cnt == len(geo_obj.solid_geometry):
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Empty Geometry in"), geo_obj.options["name"]))
|
||||
return 'fail'
|
||||
else:
|
||||
app_obj.inform.emit('[success] %s: %s' % (_("Isolation geometry created"), geo_obj.options["name"]))
|
||||
|
||||
self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot)
|
||||
if self.rest_cb.get_value():
|
||||
self.combined_rest(iso_obj=isolated_obj, iso2geo=geometry, tools_storage=tools_storage,
|
||||
lim_area=limited_area, plot=plot)
|
||||
else:
|
||||
self.combined_normal(iso_obj=isolated_obj, iso2geo=geometry, tools_storage=tools_storage,
|
||||
lim_area=limited_area, plot=plot)
|
||||
|
||||
else:
|
||||
for tool in tools_storage:
|
||||
|
@ -1992,9 +1863,321 @@ class ToolIsolation(AppTool, Gerber):
|
|||
(_("Isolation geometry created"), geo_obj.options["name"]))
|
||||
geo_obj.multigeo = False
|
||||
|
||||
# TODO: Do something if this is None. Offer changing name?
|
||||
self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot)
|
||||
|
||||
def combined_rest(self, iso_obj, iso2geo, tools_storage, lim_area, plot=True):
|
||||
"""
|
||||
|
||||
:param iso_obj: the isolated Gerber object
|
||||
:type iso_obj: AppObjects.FlatCAMGerber.GerberObject
|
||||
:param iso2geo: specific geometry to isolate
|
||||
:type iso2geo: list of Shapely Polygon
|
||||
:param tools_storage: a dictionary that holds the tools and geometry
|
||||
:type tools_storage: dict
|
||||
:param lim_area: if not None restrict isolation to this area
|
||||
:type lim_area: Shapely Polygon or a list of them
|
||||
:param plot: if to plot the resulting geometry object
|
||||
:type plot: bool
|
||||
:return: Isolated solid geometry
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
log.debug("ToolIsolation.combine_rest()")
|
||||
|
||||
total_solid_geometry = []
|
||||
|
||||
iso_name = iso_obj.options["name"]
|
||||
geometry = iso2geo
|
||||
|
||||
for tool in tools_storage:
|
||||
tool_dia = tools_storage[tool]['tooldia']
|
||||
tool_type = tools_storage[tool]['tool_type']
|
||||
tool_data = tools_storage[tool]['data']
|
||||
|
||||
to_follow = tool_data['tools_iso_follow']
|
||||
|
||||
work_geo = geometry
|
||||
if work_geo is None:
|
||||
work_geo = iso_obj.follow_geometry if to_follow else iso_obj.solid_geometry
|
||||
|
||||
iso_t = {
|
||||
'ext': 0,
|
||||
'int': 1,
|
||||
'full': 2
|
||||
}[tool_data['tools_iso_isotype']]
|
||||
|
||||
passes = tool_data['tools_iso_passes']
|
||||
overlap = tool_data['tools_iso_overlap']
|
||||
overlap /= 100.0
|
||||
|
||||
milling_type = tool_data['tools_iso_milling_type']
|
||||
|
||||
iso_except = tool_data['tools_iso_isoexcept']
|
||||
|
||||
outname = "%s_%.*f" % (iso_obj.options["name"], self.decimals, float(tool_dia))
|
||||
|
||||
iso_name = outname + "_iso"
|
||||
if iso_t == 0:
|
||||
iso_name = outname + "_ext_iso"
|
||||
elif iso_t == 1:
|
||||
iso_name = outname + "_int_iso"
|
||||
|
||||
# transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI
|
||||
if tool_type.lower() == 'v':
|
||||
new_cutz = self.ui.cutz_spinner.get_value()
|
||||
new_vtipdia = self.ui.tipdia_spinner.get_value()
|
||||
new_vtipangle = self.ui.tipangle_spinner.get_value()
|
||||
tool_type = 'V'
|
||||
tool_data.update({
|
||||
"name": iso_name,
|
||||
"cutz": new_cutz,
|
||||
"vtipdia": new_vtipdia,
|
||||
"vtipangle": new_vtipangle,
|
||||
})
|
||||
else:
|
||||
tool_data.update({
|
||||
"name": iso_name,
|
||||
})
|
||||
tool_type = 'C1'
|
||||
|
||||
solid_geo = []
|
||||
for nr_pass in range(passes):
|
||||
iso_offset = tool_dia * ((2 * nr_pass + 1) / 2.0000001) - (nr_pass * overlap * tool_dia)
|
||||
|
||||
# if milling type is climb then the move is counter-clockwise around features
|
||||
mill_dir = 1 if milling_type == 'cl' else 0
|
||||
|
||||
iso_geo = self.generate_envelope(iso_offset, mill_dir, geometry=work_geo, env_iso_type=iso_t,
|
||||
follow=to_follow, nr_passes=nr_pass)
|
||||
if iso_geo == 'fail':
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
|
||||
continue
|
||||
try:
|
||||
for geo in iso_geo:
|
||||
solid_geo.append(geo)
|
||||
except TypeError:
|
||||
solid_geo.append(iso_geo)
|
||||
|
||||
# ############################################################
|
||||
# ########## AREA SUBTRACTION ################################
|
||||
# ############################################################
|
||||
if iso_except:
|
||||
self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo"))
|
||||
solid_geo = self.area_subtraction(solid_geo)
|
||||
|
||||
if lim_area:
|
||||
self.app.proc_container.update_view_text(' %s' % _("Intersecting Geo"))
|
||||
solid_geo = self.area_intersection(solid_geo, intersection_geo=lim_area)
|
||||
|
||||
tools_storage.update({
|
||||
tool: {
|
||||
'tooldia': float(tool_dia),
|
||||
'offset': 'Path',
|
||||
'offset_value': 0.0,
|
||||
'type': _('Rough'),
|
||||
'tool_type': tool_type,
|
||||
'data': tool_data,
|
||||
'solid_geometry': deepcopy(solid_geo)
|
||||
}
|
||||
})
|
||||
|
||||
total_solid_geometry += solid_geo
|
||||
|
||||
def iso_init(geo_obj, app_obj):
|
||||
geo_obj.options["cnctooldia"] = str(tool_dia)
|
||||
|
||||
geo_obj.tools = dict(tools_storage)
|
||||
geo_obj.solid_geometry = total_solid_geometry
|
||||
# even if combine is checked, one pass is still single-geo
|
||||
|
||||
if len(tools_storage) > 1:
|
||||
geo_obj.multigeo = True
|
||||
else:
|
||||
if to_follow:
|
||||
passes_no = 1
|
||||
else:
|
||||
passes_no = float(tools_storage[0]['data']['tools_iso_passes'])
|
||||
geo_obj.multigeo = True if passes_no > 1 else False
|
||||
|
||||
# detect if solid_geometry is empty and this require list flattening which is "heavy"
|
||||
# or just looking in the lists (they are one level depth) and if any is not empty
|
||||
# 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
|
||||
empty_cnt = 0
|
||||
if not isinstance(geo_obj.solid_geometry, list) and \
|
||||
not isinstance(geo_obj.solid_geometry, MultiPolygon):
|
||||
geo_obj.solid_geometry = [geo_obj.solid_geometry]
|
||||
|
||||
for g in geo_obj.solid_geometry:
|
||||
if g:
|
||||
break
|
||||
else:
|
||||
empty_cnt += 1
|
||||
|
||||
if empty_cnt == len(geo_obj.solid_geometry):
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Empty Geometry in"), geo_obj.options["name"]))
|
||||
return 'fail'
|
||||
else:
|
||||
app_obj.inform.emit('[success] %s: %s' % (_("Isolation geometry created"), geo_obj.options["name"]))
|
||||
|
||||
self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot)
|
||||
|
||||
def combined_normal(self, iso_obj, iso2geo, tools_storage, lim_area, plot=True):
|
||||
"""
|
||||
|
||||
:param iso_obj: the isolated Gerber object
|
||||
:type iso_obj: AppObjects.FlatCAMGerber.GerberObject
|
||||
:param iso2geo: specific geometry to isolate
|
||||
:type iso2geo: list of Shapely Polygon
|
||||
:param tools_storage: a dictionary that holds the tools and geometry
|
||||
:type tools_storage: dict
|
||||
:param lim_area: if not None restrict isolation to this area
|
||||
:type lim_area: Shapely Polygon or a list of them
|
||||
:param plot: if to plot the resulting geometry object
|
||||
:type plot: bool
|
||||
:return: Isolated solid geometry
|
||||
:rtype:
|
||||
"""
|
||||
log.debug("ToolIsolation.combined_normal()")
|
||||
|
||||
total_solid_geometry = []
|
||||
|
||||
iso_name = iso_obj.options["name"]
|
||||
geometry = iso2geo
|
||||
|
||||
for tool in tools_storage:
|
||||
tool_dia = tools_storage[tool]['tooldia']
|
||||
tool_type = tools_storage[tool]['tool_type']
|
||||
tool_data = tools_storage[tool]['data']
|
||||
|
||||
to_follow = tool_data['tools_iso_follow']
|
||||
|
||||
work_geo = geometry
|
||||
if work_geo is None:
|
||||
work_geo = iso_obj.follow_geometry if to_follow else iso_obj.solid_geometry
|
||||
|
||||
iso_t = {
|
||||
'ext': 0,
|
||||
'int': 1,
|
||||
'full': 2
|
||||
}[tool_data['tools_iso_isotype']]
|
||||
|
||||
passes = tool_data['tools_iso_passes']
|
||||
overlap = tool_data['tools_iso_overlap']
|
||||
overlap /= 100.0
|
||||
|
||||
milling_type = tool_data['tools_iso_milling_type']
|
||||
|
||||
iso_except = tool_data['tools_iso_isoexcept']
|
||||
|
||||
outname = "%s_%.*f" % (iso_obj.options["name"], self.decimals, float(tool_dia))
|
||||
|
||||
iso_name = outname + "_iso"
|
||||
if iso_t == 0:
|
||||
iso_name = outname + "_ext_iso"
|
||||
elif iso_t == 1:
|
||||
iso_name = outname + "_int_iso"
|
||||
|
||||
# transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI
|
||||
if tool_type.lower() == 'v':
|
||||
new_cutz = self.ui.cutz_spinner.get_value()
|
||||
new_vtipdia = self.ui.tipdia_spinner.get_value()
|
||||
new_vtipangle = self.ui.tipangle_spinner.get_value()
|
||||
tool_type = 'V'
|
||||
tool_data.update({
|
||||
"name": iso_name,
|
||||
"cutz": new_cutz,
|
||||
"vtipdia": new_vtipdia,
|
||||
"vtipangle": new_vtipangle,
|
||||
})
|
||||
else:
|
||||
tool_data.update({
|
||||
"name": iso_name,
|
||||
})
|
||||
tool_type = 'C1'
|
||||
|
||||
solid_geo = []
|
||||
for nr_pass in range(passes):
|
||||
iso_offset = tool_dia * ((2 * nr_pass + 1) / 2.0000001) - (nr_pass * overlap * tool_dia)
|
||||
|
||||
# if milling type is climb then the move is counter-clockwise around features
|
||||
mill_dir = 1 if milling_type == 'cl' else 0
|
||||
|
||||
iso_geo = self.generate_envelope(iso_offset, mill_dir, geometry=work_geo, env_iso_type=iso_t,
|
||||
follow=to_follow, nr_passes=nr_pass)
|
||||
if iso_geo == 'fail':
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
|
||||
continue
|
||||
try:
|
||||
for geo in iso_geo:
|
||||
solid_geo.append(geo)
|
||||
except TypeError:
|
||||
solid_geo.append(iso_geo)
|
||||
|
||||
# ############################################################
|
||||
# ########## AREA SUBTRACTION ################################
|
||||
# ############################################################
|
||||
if iso_except:
|
||||
self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo"))
|
||||
solid_geo = self.area_subtraction(solid_geo)
|
||||
|
||||
if lim_area:
|
||||
self.app.proc_container.update_view_text(' %s' % _("Intersecting Geo"))
|
||||
solid_geo = self.area_intersection(solid_geo, intersection_geo=lim_area)
|
||||
|
||||
tools_storage.update({
|
||||
tool: {
|
||||
'tooldia': float(tool_dia),
|
||||
'offset': 'Path',
|
||||
'offset_value': 0.0,
|
||||
'type': _('Rough'),
|
||||
'tool_type': tool_type,
|
||||
'data': tool_data,
|
||||
'solid_geometry': deepcopy(solid_geo)
|
||||
}
|
||||
})
|
||||
|
||||
total_solid_geometry += solid_geo
|
||||
|
||||
def iso_init(geo_obj, app_obj):
|
||||
geo_obj.options["cnctooldia"] = str(tool_dia)
|
||||
|
||||
geo_obj.tools = dict(tools_storage)
|
||||
geo_obj.solid_geometry = total_solid_geometry
|
||||
# even if combine is checked, one pass is still single-geo
|
||||
|
||||
if len(tools_storage) > 1:
|
||||
geo_obj.multigeo = True
|
||||
else:
|
||||
if to_follow:
|
||||
passes_no = 1
|
||||
else:
|
||||
passes_no = float(tools_storage[0]['data']['tools_iso_passes'])
|
||||
geo_obj.multigeo = True if passes_no > 1 else False
|
||||
|
||||
# detect if solid_geometry is empty and this require list flattening which is "heavy"
|
||||
# or just looking in the lists (they are one level depth) and if any is not empty
|
||||
# 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
|
||||
empty_cnt = 0
|
||||
if not isinstance(geo_obj.solid_geometry, list) and \
|
||||
not isinstance(geo_obj.solid_geometry, MultiPolygon):
|
||||
geo_obj.solid_geometry = [geo_obj.solid_geometry]
|
||||
|
||||
for g in geo_obj.solid_geometry:
|
||||
if g:
|
||||
break
|
||||
else:
|
||||
empty_cnt += 1
|
||||
|
||||
if empty_cnt == len(geo_obj.solid_geometry):
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Empty Geometry in"), geo_obj.options["name"]))
|
||||
return 'fail'
|
||||
else:
|
||||
app_obj.inform.emit('[success] %s: %s' % (_("Isolation geometry created"), geo_obj.options["name"]))
|
||||
|
||||
self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot)
|
||||
|
||||
def area_subtraction(self, geo, subtractor_geo):
|
||||
"""
|
||||
Subtracts the subtractor_geo (if present else self.solid_geometry) from the geo
|
||||
|
@ -2521,12 +2704,13 @@ class ToolIsolation(AppTool, Gerber):
|
|||
|
||||
if follow:
|
||||
geom = self.grb_obj.isolation_geometry(offset, geometry=geometry, follow=follow)
|
||||
return geom
|
||||
else:
|
||||
try:
|
||||
geom = self.grb_obj.isolation_geometry(offset, geometry=geometry, iso_type=env_iso_type,
|
||||
passes=nr_passes)
|
||||
except Exception as e:
|
||||
log.debug('ToolIsolation.isolate().generate_envelope() --> %s' % str(e))
|
||||
log.debug('ToolIsolation.generate_envelope() --> %s' % str(e))
|
||||
return 'fail'
|
||||
|
||||
if invert:
|
||||
|
@ -2661,7 +2845,6 @@ class ToolIsolation(AppTool, Gerber):
|
|||
def reset_usage(self):
|
||||
self.obj_name = ""
|
||||
self.grb_obj = None
|
||||
self.bound_obj = None
|
||||
|
||||
self.first_click = False
|
||||
self.cursor_pos = None
|
||||
|
|
|
@ -7,6 +7,11 @@ CHANGELOG for FlatCAM beta
|
|||
|
||||
=================================================
|
||||
|
||||
29.05.2020
|
||||
|
||||
- fixed the Tool Isolation when using the 'follow' parameter
|
||||
- in Isolation Tool when the Rest machining is checked the combine parameter is set True automatically because the rest machining concept make sense only when all tools are used together
|
||||
|
||||
28.05.2020
|
||||
|
||||
- made the visibility change (when using the Spacebar key in Project Tab) to be not threaded and to use the enabled property of the ShapesCollection which should be faster
|
||||
|
|
Loading…
Reference in New Issue