- remade the SolderPaste geometry generation function in ToolSoderPaste to work in certain scenarios where the Gerber pads in the SolderPaste mask Gerber may be just pads outlines

This commit is contained in:
Marius Stanciu
2019-02-23 13:48:18 +02:00
committed by Marius S
parent 92c02bc6a1
commit adcafb7cac
2 changed files with 101 additions and 66 deletions

View File

@@ -11,6 +11,7 @@ CAD program, and create G-Code for Isolation routing.
23.02.2019
- remade the SolderPaste geometry generation function in ToolSoderPaste to work in certain scenarios where the Gerber pads in the SolderPaste mask Gerber may be just pads outlines
-
22.02.2019

View File

@@ -391,6 +391,9 @@ class SolderPaste(FlatCAMTool):
# this will be used in the combobox context menu, for delete entry
self.obj_to_be_deleted_name = ''
# stpre here the flattened geometry
self.flat_geometry = []
# action to be added in the combobox context menu
self.combo_context_del_action = QtWidgets.QAction(QtGui.QIcon('share/trash16.png'), "Delete Object")
@@ -910,44 +913,6 @@ class SolderPaste(FlatCAMTool):
# self.save_gcode_frame.setDisabled(True)
pass
@staticmethod
def solder_line(p, offset, units):
x_min, y_min, x_max, y_max = p.bounds
diag_1_intersect = LineString([(x_min, y_min), (x_max, y_max)]).intersection(p)
diag_2_intersect = LineString([(x_min, y_max), (x_max, y_min)]).intersection(p)
if units == 'MM':
round_diag_1 = round(diag_1_intersect.length, 1)
round_diag_2 = round(diag_2_intersect.length, 1)
else:
round_diag_1 = round(diag_1_intersect.length, 2)
round_diag_2 = round(diag_2_intersect.length, 2)
if round_diag_1 == round_diag_2:
l = distance((x_min, y_min), (x_max, y_min))
h = distance((x_min, y_min), (x_min, y_max))
if offset >= l / 2 or offset >= h / 2:
return "fail"
if l > h:
h_half = h / 2
start = [x_min, (y_min + h_half)]
stop = [(x_min + l), (y_min + h_half)]
else:
l_half = l / 2
start = [(x_min + l_half), y_min]
stop = [(x_min + l_half), (y_min + h)]
geo = LineString([start, stop])
elif round_diag_1 > round_diag_2:
geo = round_diag_1
else:
geo = round_diag_2
offseted_poly = p.buffer(-offset)
geo = geo.intersection(offseted_poly)
return geo
def on_create_geo_click(self, signal):
"""
Will create a solderpaste dispensing geometry.
@@ -989,6 +954,39 @@ class SolderPaste(FlatCAMTool):
self.app.inform.emit("[WARNING_NOTCL] No Nozzle tools in the tool table.")
return 'fail'
def flatten(geometry=None, reset=True, pathonly=False):
"""
Creates a list of non-iterable linear geometry objects.
Polygons are expanded into its exterior pathonly param if specified.
Results are placed in flat_geometry
:param geometry: Shapely type or list or list of list of such.
:param reset: Clears the contents of self.flat_geometry.
:param pathonly: Expands polygons into linear elements from the exterior attribute.
"""
if reset:
self.flat_geometry = []
## If iterable, expand recursively.
try:
for geo in geometry:
if geo is not None:
flatten(geometry=geo,
reset=False,
pathonly=pathonly)
## Not iterable, do the actual indexing and add.
except TypeError:
if pathonly and type(geometry) == Polygon:
self.flat_geometry.append(geometry.exterior)
else:
self.flat_geometry.append(geometry)
return self.flat_geometry
# TODO when/if the Gerber files will have solid_geometry in the self.apertures I will have to take care here
flatten(geometry=obj.solid_geometry, pathonly=True)
def geo_init(geo_obj, app_obj):
geo_obj.options.update(self.options)
geo_obj.solid_geometry = []
@@ -998,12 +996,8 @@ class SolderPaste(FlatCAMTool):
geo_obj.multitool = True
geo_obj.special_group = 'solder_paste_tool'
work_geo = obj.solid_geometry
try:
_ = iter(work_geo)
except TypeError:
work_geo = [work_geo]
geo = LineString()
work_geo = []
rest_geo = []
tooluid = 1
@@ -1023,37 +1017,75 @@ class SolderPaste(FlatCAMTool):
geo_obj.tools[tooluid]['type'] = 'SolderPaste'
geo_obj.tools[tooluid]['tool_type'] = 'DN'
for g in work_geo:
if type(g) == MultiPolygon:
for poly in g:
geom = self.solder_line(poly, offset=offset, units=self.units)
if geom != 'fail':
try:
geo_obj.tools[tooluid]['solid_geometry'].append(geom)
except Exception as e:
log.debug('ToolSoderPaste.on_create_geo() --> %s' % str(e))
else:
rest_geo.append(poly)
elif type(g) == Polygon:
geom = self.solder_line(g, offset=offset, units=self.units)
if geom != 'fail':
try:
geo_obj.tools[tooluid]['solid_geometry'].append(geom)
except Exception as e:
log.debug('ToolSoderPaste.on_create_geo() --> %s' % str(e))
# self.flat_geometry is a list of LinearRings produced by flatten() from the exteriors of the Polygons
# We get possible issues if we try to directly use the Polygons, due of possible the interiors,
# so we do a hack: get first the exterior in a form of LinearRings and then convert back to Polygon
# because intersection does not work on LinearRings
for g in self.flat_geometry:
# for whatever reason intersection on LinearRings does not work so we convert back to Polygons
poly = Polygon(g)
x_min, y_min, x_max, y_max = poly.bounds
diag_1_intersect = LineString([(x_min, y_min), (x_max, y_max)]).intersection(poly)
diag_2_intersect = LineString([(x_min, y_max), (x_max, y_min)]).intersection(poly)
if self.units == 'MM':
round_diag_1 = round(diag_1_intersect.length, 1)
round_diag_2 = round(diag_2_intersect.length, 1)
else:
round_diag_1 = round(diag_1_intersect.length, 2)
round_diag_2 = round(diag_2_intersect.length, 2)
if round_diag_1 == round_diag_2:
l = distance((x_min, y_min), (x_max, y_min))
h = distance((x_min, y_min), (x_min, y_max))
if offset >= l / 2 or offset >= h / 2:
pass
else:
rest_geo.append(g)
if l > h:
h_half = h / 2
start = [x_min, (y_min + h_half)]
stop = [(x_min + l), (y_min + h_half)]
geo = LineString([start, stop])
else:
l_half = l / 2
start = [(x_min + l_half), y_min]
stop = [(x_min + l_half), (y_min + h)]
geo = LineString([start, stop])
elif round_diag_1 > round_diag_2:
geo = diag_1_intersect
else:
geo = diag_2_intersect
offseted_poly = poly.buffer(-offset)
geo = geo.intersection(offseted_poly)
if not geo.is_empty:
try:
geo_obj.tools[tooluid]['solid_geometry'].append(geo)
except Exception as e:
log.debug('ToolSolderPaste.on_create_geo() --> %s' % str(e))
else:
rest_geo.append(g)
work_geo = deepcopy(rest_geo)
rest_geo[:] = []
if not work_geo:
a = 0
for tooluid_key in geo_obj.tools:
if not geo_obj.tools[tooluid_key]['solid_geometry']:
a += 1
if a == len(geo_obj.tools):
self.app.inform.emit('[ERROR_NOTCL]Cancelled. Empty file, it has no geometry...')
return 'fail'
app_obj.inform.emit("[success] Solder Paste geometry generated successfully...")
return
# if we still have geometry not processed at the end of the tools then we failed
# some or all the pads are not covered with solder paste
if rest_geo:
if work_geo:
app_obj.inform.emit("[WARNING_NOTCL] Some or all pads have no solder "
"due of inadequate nozzle diameters...")
return 'fail'
@@ -1087,6 +1119,10 @@ class SolderPaste(FlatCAMTool):
name = self.geo_obj_combo.currentText()
obj = self.app.collection.get_by_name(name)
if name == '':
self.app.inform.emit("[WARNING_NOTCL]There is no Geometry object available.")
return 'fail'
if obj.special_group != 'solder_paste_tool':
self.app.inform.emit("[WARNING_NOTCL]This Geometry can't be processed. NOT a solder_paste_tool geometry.")
return 'fail'
@@ -1114,8 +1150,6 @@ class SolderPaste(FlatCAMTool):
:param use_thread: True if threaded execution is desired
:return:
"""
obj = workobject
try: