diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0546f4e3..3abc6ad5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,12 @@ CHANGELOG for FlatCAM Evo beta
=================================================
+5.06.2023
+
+- updated the FCButton and FCLabel custom widgets
+- Paint Plugin: fixed issues caused by the latest changes in the Shapely module
+- NCC Plugin: some changes in the method used by the Tcl Command
+
31.05.2023
- Pdf Import plugin: fixed an issue with assigning a wrong property to a Qtimer
diff --git a/appGUI/GUIElements.py b/appGUI/GUIElements.py
index 518033d8..d660af0e 100644
--- a/appGUI/GUIElements.py
+++ b/appGUI/GUIElements.py
@@ -3005,16 +3005,37 @@ class FCInputDialogSpinnerButton(QtWidgets.QDialog):
class FCButton(QtWidgets.QPushButton):
- def __init__(self, text=None, checkable=None, click_callback=None, bold=False, color=None, parent=None):
+ def __init__(self, text=None, checkable=None, click_callback=None, bold=False, font_size=None,
+ color=None, back_color=None, border_color=None, parent=None):
super(FCButton, self).__init__(text, parent)
- self._bold = False
- self._color = None
+ self._bold = bold
+ self._color = color
+ self._background_color = back_color
+ self._border_color = border_color
self.bold = True if bold else False
+ font = QtGui.QFont()
+ if font_size:
+ font.setPointSize(font_size)
+ font.setBold(True) if bold else font.setBold(False)
+ self.setFont(font)
if color:
self.color = self.patching_text_color(color)
+ self.setStyleSheet(".CustomButton{color: %s;}" % color)
+
+ if back_color:
+ stylesheet_content = "background-color:%s;" % back_color
+ self.setStyleSheet(".FCButton{%s}" % stylesheet_content)
+
+ if border_color:
+ if self.styleSheet() != "":
+ self.setStyleSheet(
+ self.styleSheet().replace('}', "border-radius:3px; border:1px solid %s;}" % border_color))
+ else:
+ stylesheet_content = "border-radius:3px; border:1px solid %s;" % border_color
+ self.setStyleSheet(".FCButton{%s}" % stylesheet_content)
if checkable is not None:
self.setCheckable(checkable)
@@ -3022,6 +3043,26 @@ class FCButton(QtWidgets.QPushButton):
if click_callback is not None:
self.clicked.connect(click_callback)
+ self.default_stylesheet = self.styleSheet()
+
+ def restore_stylesheet(self):
+ self.setStyleSheet(self.default_stylesheet)
+
+ def set_stylesheet(self):
+ self.default_stylesheet = self.styleSheet()
+
+ def setDisabled(self, arg__1):
+ if not self.isEnabled() and arg__1 is True:
+ return
+ if self.isEnabled() and arg__1 is False:
+ return
+ if arg__1 is False:
+ self.restore_stylesheet()
+ else:
+ self.set_stylesheet()
+ self.setStyleSheet("")
+ super().setDisabled(arg__1)
+
@property
def bold(self):
return self._bold
@@ -3040,7 +3081,38 @@ class FCButton(QtWidgets.QPushButton):
@color.setter
def color(self, color):
self._color = color
- self.setStyleSheet(".FCButton{color: %s;}" % color)
+ if self.styleSheet() != "":
+ self.setStyleSheet(self.styleSheet().replace('}', "color: %s;}" % color))
+ else:
+ self.setStyleSheet('.FCButton{"color: %s;}' % color)
+ self.set_stylesheet()
+
+ @property
+ def b_color(self):
+ return self._background_color
+
+ @b_color.setter
+ def b_color(self, color):
+ self._background_color = color
+ if self.styleSheet() != "":
+ self.setStyleSheet(self.styleSheet().replace('}', "background-color:%s;}" % color))
+ else:
+ self.setStyleSheet('.FCButton{"background-color:%s;}' % color)
+ self.set_stylesheet()
+
+ @property
+ def border_color(self):
+ return self._border_color
+
+ @border_color.setter
+ def border_color(self, color):
+ self._border_color = color
+ if self.styleSheet() != "":
+ self.setStyleSheet(self.styleSheet().replace('}', "border-radius:3px; border:1px solid %s;}" % color))
+ else:
+ stylesheet_content = "border-radius:3px; border:1px solid %s;" % color
+ self.setStyleSheet(".FCButton{%s}" % stylesheet_content)
+ self.set_stylesheet()
def get_value(self):
return self.isChecked()
@@ -3053,17 +3125,19 @@ class FCButton(QtWidgets.QPushButton):
class FCLabel(QtWidgets.QLabel):
- clicked = QtCore.pyqtSignal(bool)
- right_clicked = QtCore.pyqtSignal(bool)
- middle_clicked = QtCore.pyqtSignal(bool)
+ clicked = QtCore.Signal(bool)
+ right_clicked = QtCore.Signal(bool)
+ middle_clicked = QtCore.Signal(bool)
- def __init__(self, title=None, color=None, bold=None, size=None, parent=None):
+ def __init__(self, title=None, color=None, b_color=None, bold=None, size=None, parent=None):
"""
:param title: the label's text
:type title: str
:param color: text color
:type color: str
+ :param b_color: Label color
+ :type b_color: str
:param bold: the text weight
:type bold: bool
:param size: Font Size in points
@@ -3074,23 +3148,18 @@ class FCLabel(QtWidgets.QLabel):
super(FCLabel, self).__init__(parent)
- self._color = None
- self._bold = False
+ self._color = color
+ self._background_color = b_color
+ self._bold = bold
self._title = title
- self._font_size = None
+ self._font_size = size
+
+ self.original_color = self.palette().color(QtGui.QPalette.ColorRole.WindowText)
if color:
color = self.patching_text_color(color)
if isinstance(title, str):
- # if color and not bold:
- # self.setText('%s' % (str(color), title))
- # elif not color and bold:
- # self.setText('%s' % title)
- # elif color and bold:
- # self.setText('%s' % (str(color), title))
- # else:
- # self.setText(title)
if color:
self.setText('%s' % (str(color), title))
else:
@@ -3100,14 +3169,22 @@ class FCLabel(QtWidgets.QLabel):
font.setBold(True) if bold else font.setBold(False)
if size:
font.setPointSize(size)
- # "%s"
self.setFont(font)
+ if b_color:
+ stylesheet_content = "background-color:%s;" % b_color
+ self.setStyleSheet(".CustomLabel{%s}" % stylesheet_content)
+
# for the usage of this label as a clickable label, to know that current state
self.clicked_state = False
self.middle_clicked_state = False
self.right_clicked_state = False
+ self.default_stylesheet = self.styleSheet()
+
+ def restore_stylesheet(self):
+ self.setStyleSheet(self.default_stylesheet)
+
@property
def color(self):
return self._color
@@ -3118,11 +3195,20 @@ class FCLabel(QtWidgets.QLabel):
self.setText('%s' % (str(color), self._title))
@property
- def bold(self):
+ def b_color(self):
+ return self._background_color
+
+ @b_color.setter
+ def b_color(self, color):
+ self._background_color = color
+ self.setStyleSheet(self.styleSheet().replace('}', "background-color:%s;}" % color))
+
+ @property
+ def bold(self) -> bool:
return self._bold
@bold.setter
- def bold(self, bold):
+ def bold(self, bold: bool):
self._bold = bold
font = QtGui.QFont()
font.setBold(True) if bold else font.setBold(False)
diff --git a/appPlugins/ToolNCC.py b/appPlugins/ToolNCC.py
index 9ffb3c01..5419fbb5 100644
--- a/appPlugins/ToolNCC.py
+++ b/appPlugins/ToolNCC.py
@@ -2935,18 +2935,14 @@ class NonCopperClear(AppTool, Gerber):
bounding_box = None
if ncc_select == 0: # itself
- geo_n = ncc_sel_obj.solid_geometry
+ geo_n = flatten_shapely_geometry(ncc_sel_obj.solid_geometry)
try:
- if isinstance(geo_n, MultiPolygon):
- env_obj = geo_n.convex_hull
- elif (isinstance(geo_n, MultiPolygon) and len(geo_n.geoms) == 1) or \
- (isinstance(geo_n, list) and len(geo_n) == 1) and isinstance(geo_n[0], Polygon):
+ if len(geo_n) == 1:
env_obj = unary_union(geo_n)
else:
env_obj = unary_union(geo_n)
env_obj = env_obj.convex_hull
-
bounding_box = env_obj.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre)
except Exception as e:
self.app.log.error("NonCopperClear.clear_copper() 'itself' --> %s" % str(e))
@@ -3224,128 +3220,93 @@ class NonCopperClear(AppTool, Gerber):
except Exception:
continue
- # Transform area to MultiPolygon
- if type(area) is Polygon:
- area = MultiPolygon([area])
+ area = flatten_shapely_geometry(area)
# variables to display the percentage of work done
- geo_len = len(area.geoms)
+ geo_len = len(area)
old_disp_number = 0
self.app.log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
if area.geoms:
- if len(area.geoms) > 0:
- pol_nr = 0
- for p in area.geoms:
- # provide the app with a way to process the GUI events when in a blocking loop
- QtWidgets.QApplication.processEvents()
+ pol_nr = 0
+ for p in area:
+ # provide the app with a way to process the GUI events when in a blocking loop
+ QtWidgets.QApplication.processEvents()
- if self.app.abort_flag:
- # graceful abort requested by the user
- raise grace
+ if self.app.abort_flag:
+ # graceful abort requested by the user
+ raise grace
- # clean the polygon
- p = p.buffer(0)
+ # clean the polygon
+ p = p.buffer(0)
- if p is not None and p.is_valid:
- poly_processed = []
- try:
- for pol in p:
- if pol is not None and isinstance(pol, Polygon):
- if ncc_method == 0: # standard
- cp = self.clear_polygon(pol, tool,
- self.circle_steps,
- overlap=overlap, contour=contour,
- connect=connect,
- prog_plot=False)
- elif ncc_method == 1: # seed
- cp = self.clear_polygon2(pol, tool,
- self.circle_steps,
- overlap=overlap, contour=contour,
- connect=connect,
- prog_plot=False)
- else:
- cp = self.clear_polygon3(pol, tool,
- self.circle_steps,
- overlap=overlap, contour=contour,
- connect=connect,
- prog_plot=False)
- if cp:
- cleared_geo += list(cp.get_objects())
- poly_processed.append(True)
- else:
- poly_processed.append(False)
- self.app.log.warning("Polygon in MultiPolygon can not be cleared.")
- else:
- self.app.log.warning(
- "Geo in Iterable can not be cleared because it is not Polygon. "
- "It is: %s" % str(type(pol)))
- except TypeError:
- if isinstance(p, Polygon):
- if ncc_method == 0: # standard
- cp = self.clear_polygon(p, tool, self.circle_steps,
- overlap=overlap, contour=contour, connect=connect,
- prog_plot=False)
- elif ncc_method == 1: # seed
- cp = self.clear_polygon2(p, tool, self.circle_steps,
- overlap=overlap, contour=contour, connect=connect,
- prog_plot=False)
- else:
- cp = self.clear_polygon3(p, tool, self.circle_steps,
- overlap=overlap, contour=contour, connect=connect,
- prog_plot=False)
- if cp:
- cleared_geo += list(cp.get_objects())
- poly_processed.append(True)
- else:
- poly_processed.append(False)
- self.app.log.warning("Polygon can not be cleared.")
- else:
- self.app.log.warning("Geo can not be cleared because it is: %s" % str(type(p)))
+ if p and p.is_valid:
+ poly_processed = []
+ if isinstance(p, Polygon):
+ if ncc_method == 0: # standard
+ cp = self.clear_polygon(p, tool, self.circle_steps,
+ overlap=overlap, contour=contour, connect=connect,
+ prog_plot=False)
+ elif ncc_method == 1: # seed
+ cp = self.clear_polygon2(p, tool, self.circle_steps,
+ overlap=overlap, contour=contour, connect=connect,
+ prog_plot=False)
+ else:
+ cp = self.clear_polygon3(p, tool, self.circle_steps,
+ overlap=overlap, contour=contour, connect=connect,
+ prog_plot=False)
+ if cp:
+ cleared_geo += list(cp.get_objects())
+ poly_processed.append(True)
+ else:
+ poly_processed.append(False)
+ self.app.log.warning("Polygon can not be cleared.")
+ else:
+ self.app.log.warning("Geo can not be cleared because it is: %s" % str(type(p)))
- p_cleared = poly_processed.count(True)
- p_not_cleared = poly_processed.count(False)
+ p_cleared = poly_processed.count(True)
+ p_not_cleared = poly_processed.count(False)
- if p_not_cleared:
- app_obj.poly_not_cleared = True
+ if p_not_cleared:
+ app_obj.poly_not_cleared = True
- if p_cleared == 0:
- continue
+ if p_cleared == 0:
+ continue
- pol_nr += 1
- disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
- # log.debug("Polygons cleared: %d" % pol_nr)
+ pol_nr += 1
+ disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
+ # log.debug("Polygons cleared: %d" % pol_nr)
- if old_disp_number < disp_number <= 100:
- self.app.proc_container.update_view_text(' %d%%' % disp_number)
- old_disp_number = disp_number
- # log.debug("Polygons cleared: %d. Percentage done: %d%%" % (pol_nr, disp_number))
+ if old_disp_number < disp_number <= 100:
+ self.app.proc_container.update_view_text(' %d%%' % disp_number)
+ old_disp_number = disp_number
+ # log.debug("Polygons cleared: %d. Percentage done: %d%%" % (pol_nr, disp_number))
- # check if there is a geometry at all in the cleared geometry
- if cleared_geo:
- # Overall cleared area
- cleared = empty.buffer(-offset_a * (1 + overlap)).buffer(-tool / 1.999999).buffer(
- tool / 1.999999)
+ # check if there is a geometry at all in the cleared geometry
+ if cleared_geo:
+ # Overall cleared area
+ cleared = empty.buffer(-offset_a * (1 + overlap)).buffer(-tool / 1.999999).buffer(
+ tool / 1.999999)
- # clean-up cleared geo
- cleared = cleared.buffer(0)
+ # clean-up cleared geo
+ cleared = cleared.buffer(0)
- # find the tooluid associated with the current tool_dia so we know where to add the tool
- # solid_geometry
- for k, v in tools_storage.items():
- if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals,
- tool)):
- current_uid = int(k)
+ # find the tooluid associated with the current tool_dia so we know where to add the tool
+ # solid_geometry
+ for k, v in tools_storage.items():
+ if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals,
+ tool)):
+ current_uid = int(k)
- # add the solid_geometry to the current too in self.paint_tools dictionary
- # and then reset the temporary list that stored that solid_geometry
- v['solid_geometry'] = deepcopy(cleared_geo)
- v['data']['name'] = name
- break
- geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
- else:
- app_obj.log.debug("There are no geometries in the cleared polygon.")
+ # add the solid_geometry to the current too in self.paint_tools dictionary
+ # and then reset the temporary list that stored that solid_geometry
+ v['solid_geometry'] = flatten_shapely_geometry(cleared_geo)
+ v['data']['name'] = name
+ break
+ geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
+ else:
+ app_obj.log.debug("There are no geometries in the cleared polygon.")
# delete tools with empty geometry
# look for keys in the tools_storage dict that have 'solid_geometry' values empty
@@ -3747,7 +3708,7 @@ class NonCopperClear(AppTool, Gerber):
# add the solid_geometry to the current too in self.paint_tools dictionary
# and then reset the temporary list that stored that solid_geometry
- v['solid_geometry'] = deepcopy(cleared_area)
+ v['solid_geometry'] = flatten_shapely_geometry(cleared_area)
v['data']['name'] = name
cleared_area[:] = []
break
diff --git a/appPlugins/ToolPaint.py b/appPlugins/ToolPaint.py
index bb214bfc..b9689f55 100644
--- a/appPlugins/ToolPaint.py
+++ b/appPlugins/ToolPaint.py
@@ -31,7 +31,7 @@ import appTranslation as fcTranslate
import builtins
from appParsers.ParseGerber import Gerber
-from camlib import Geometry, AppRTreeStorage, grace
+from camlib import Geometry, AppRTreeStorage, grace, flatten_shapely_geometry
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
@@ -1938,7 +1938,7 @@ class ToolPaint(AppTool, Gerber):
paint_offset = float(tools_storage[current_uid]['data']['tools_paint_offset'])
poly_buf = []
- for pol in geometry:
+ for pol in flatten_shapely_geometry(geometry):
buffered_pol = pol.buffer(-paint_offset)
if buffered_pol and not buffered_pol.is_empty:
poly_buf.append(buffered_pol)
@@ -1960,37 +1960,24 @@ class ToolPaint(AppTool, Gerber):
# -----------------------------
try:
cp = []
- try:
- for pp in poly_buf:
- # provide the app with a way to process the GUI events when in a blocking loop
- QtWidgets.QApplication.processEvents()
- if self.app.abort_flag:
- # graceful abort requested by the user
- raise grace
- geo_res = self.paint_polygon_worker(pp, tooldiameter=tool_dia, over=over, conn=conn,
- cont=cont, paint_method=paint_method, obj=obj,
- prog_plot=prog_plot)
- if geo_res:
- cp.append(geo_res)
- pol_nr += 1
- disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
- # log.debug("Polygons cleared: %d" % pol_nr)
-
- if old_disp_number < disp_number <= 100:
- self.app.proc_container.update_view_text(' %d%%' % disp_number)
- old_disp_number = disp_number
- except TypeError:
+ for pp in poly_buf:
# provide the app with a way to process the GUI events when in a blocking loop
QtWidgets.QApplication.processEvents()
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
-
- geo_res = self.paint_polygon_worker(poly_buf, tooldiameter=tool_dia, over=over, conn=conn,
+ geo_res = self.paint_polygon_worker(pp, tooldiameter=tool_dia, over=over, conn=conn,
cont=cont, paint_method=paint_method, obj=obj,
prog_plot=prog_plot)
if geo_res:
cp.append(geo_res)
+ pol_nr += 1
+ disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
+ # log.debug("Polygons cleared: %d" % pol_nr)
+
+ if old_disp_number < disp_number <= 100:
+ self.app.proc_container.update_view_text(' %d%%' % disp_number)
+ old_disp_number = disp_number
total_geometry = []
if cp:
@@ -2035,7 +2022,7 @@ class ToolPaint(AppTool, Gerber):
geo_obj.tools.clear()
geo_obj.tools = dict(tools_storage)
- geo_obj.solid_geometry = unary_union(final_solid_geometry)
+ geo_obj.solid_geometry = flatten_shapely_geometry(unary_union(final_solid_geometry))
try:
if isinstance(geo_obj.solid_geometry, list):
@@ -2090,6 +2077,7 @@ class ToolPaint(AppTool, Gerber):
poly_buf.append(buffered_pol)
poly_buf = unary_union(poly_buf)
+ poly_buf = flatten_shapely_geometry(poly_buf)
if not poly_buf:
self.app.inform.emit(
@@ -2136,47 +2124,7 @@ class ToolPaint(AppTool, Gerber):
# -----------------------------
try:
cleared_geo = []
- try:
- for pp in poly_buf:
- # provide the app with a way to process the GUI events when in a blocking loop
- QtWidgets.QApplication.processEvents()
- if self.app.abort_flag:
- # graceful abort requested by the user
- raise grace
-
- # speedup the clearing by not trying to clear polygons that is clear they can't be
- # cleared with the current tool. this tremendously reduce the clearing time
- check_dist = -tool_dia / 2.0
- check_buff = pp.buffer(check_dist)
- if not check_buff or check_buff.is_empty:
- continue
-
- geo_res = self.paint_polygon_worker(pp, tooldiameter=tool_dia, over=over, conn=conn,
- cont=cont, paint_method=paint_method, obj=obj,
- prog_plot=prog_plot)
- geo_elems = list(geo_res.get_objects())
- # See if the polygon was completely cleared
- pp_cleared = unary_union(geo_elems).buffer(tool_dia / 2.0)
- rest_geo = pp.difference(pp_cleared)
- if rest_geo and not rest_geo.is_empty:
- try:
- for r in rest_geo:
- if r.is_valid:
- rest_list.append(r)
- except TypeError:
- if rest_geo.is_valid:
- rest_list.append(rest_geo)
-
- if geo_res:
- cleared_geo += geo_elems
- pol_nr += 1
- disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
- # log.debug("Polygons cleared: %d" % pol_nr)
-
- if old_disp_number < disp_number <= 100:
- self.app.proc_container.update_view_text(' %d%%' % disp_number)
- old_disp_number = disp_number
- except TypeError:
+ for pp in poly_buf:
# provide the app with a way to process the GUI events when in a blocking loop
QtWidgets.QApplication.processEvents()
if self.app.abort_flag:
@@ -2186,30 +2134,32 @@ class ToolPaint(AppTool, Gerber):
# speedup the clearing by not trying to clear polygons that is clear they can't be
# cleared with the current tool. this tremendously reduce the clearing time
check_dist = -tool_dia / 2.0
- check_buff = poly_buf.buffer(check_dist)
+ check_buff = pp.buffer(check_dist)
if not check_buff or check_buff.is_empty:
continue
- geo_res = self.paint_polygon_worker(poly_buf, tooldiameter=tool_dia, over=over, conn=conn,
+ geo_res = self.paint_polygon_worker(pp, tooldiameter=tool_dia, over=over, conn=conn,
cont=cont, paint_method=paint_method, obj=obj,
prog_plot=prog_plot)
geo_elems = list(geo_res.get_objects())
-
# See if the polygon was completely cleared
pp_cleared = unary_union(geo_elems).buffer(tool_dia / 2.0)
- rest_geo = poly_buf.difference(pp_cleared)
- if rest_geo and not rest_geo.is_empty:
- try:
- for r in rest_geo:
- if r.is_valid:
- rest_list.append(r)
- except TypeError:
- if rest_geo.is_valid:
- rest_list.append(rest_geo)
+ rest_geo = pp.difference(pp_cleared)
+ if rest_geo:
+ rest_geo = flatten_shapely_geometry(rest_geo)
+ for r in rest_geo:
+ if r.is_valid and not r.is_empty:
+ rest_list.append(r)
if geo_res:
cleared_geo += geo_elems
+ pol_nr += 1
+ disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
+ # log.debug("Polygons cleared: %d" % pol_nr)
+ if old_disp_number < disp_number <= 100:
+ self.app.proc_container.update_view_text(' %d%%' % disp_number)
+ old_disp_number = disp_number
except grace:
return "fail"
except Exception as e:
@@ -2235,26 +2185,23 @@ class ToolPaint(AppTool, Gerber):
buffered_cleared = unary_union(cleared_geo)
buffered_cleared = buffered_cleared.buffer(tool_dia / 2.0)
- poly_buf = poly_buf.difference(buffered_cleared)
+ poly_buf = MultiPolygon(poly_buf).difference(buffered_cleared)
+ poly_buf = flatten_shapely_geometry(poly_buf)
tmp = []
- try:
- for p in poly_buf:
- if p.is_valid:
- tmp.append(p)
- except TypeError:
- if poly_buf.is_valid:
- tmp.append(poly_buf)
-
+ for p in poly_buf:
+ if p.is_valid:
+ tmp.append(p)
tmp += rest_list
+ print(tmp)
poly_buf = MultiPolygon(tmp)
if not poly_buf.is_valid:
poly_buf = unary_union(tmp)
-
if not poly_buf or poly_buf.is_empty or not poly_buf.is_valid:
app_obj.log.debug("Rest geometry empty. Breaking.")
break
+ poly_buf = flatten_shapely_geometry(poly_buf)
geo_obj.multigeo = True
geo_obj.obj_options["tools_mill_tooldia"] = '0.0'
@@ -2290,7 +2237,7 @@ class ToolPaint(AppTool, Gerber):
"Change the painting parameters and try again.")
)
return "fail"
- geo_obj.solid_geometry = unary_union(final_solid_geometry)
+ geo_obj.solid_geometry = flatten_shapely_geometry(unary_union(final_solid_geometry))
else:
return 'fail'
try: