- updated the isolate, cutout and geocutout Tcl commands and now they yield multigeo Geometry objects

This commit is contained in:
Marius Stanciu
2022-01-31 21:19:37 +02:00
committed by Marius
parent 7306e7ada1
commit 28372c1e08
5 changed files with 121 additions and 169 deletions

View File

@@ -21,6 +21,7 @@ CHANGELOG for FlatCAM beta
- another fix for Shapely 2.0 deprecation warning - another fix for Shapely 2.0 deprecation warning
- yet another fix for Shapely 2.0 deprecation warnings - yet another fix for Shapely 2.0 deprecation warnings
- updated the `join_geometry` and `join_excellon` Tcl commands format to follow the format of other commands - updated the `join_geometry` and `join_excellon` Tcl commands format to follow the format of other commands
- updated the `isolate`, `cutout` and `geocutout` Tcl commands and now they yield multigeo Geometry objects
30.01.2022 30.01.2022

View File

@@ -110,7 +110,7 @@ class GeometryObject(FlatCAMObj, Geometry):
tool_id of the tools and the value is another dict that will hold the data under the following form: tool_id of the tools and the value is another dict that will hold the data under the following form:
{tooluid: { {tooluid: {
'tooldia': 1, 'tooldia': 1,
'data': self.default_tool_data 'data': self.default_tool_data,
'solid_geometry': [] 'solid_geometry': []
} }
} }

View File

@@ -579,6 +579,7 @@ class GerberObject(FlatCAMObj, Gerber):
# Propagate options # Propagate options
geo_obj.options["tools_mill_tooldia"] = str(dia) geo_obj.options["tools_mill_tooldia"] = str(dia)
geo_obj.tool_type = self.app.defaults["tools_iso_tool_shape"] geo_obj.tool_type = self.app.defaults["tools_iso_tool_shape"]
geo_obj.multigeo = True
geo_obj.solid_geometry = [] geo_obj.solid_geometry = []
@@ -639,9 +640,6 @@ class GerberObject(FlatCAMObj, Gerber):
msg = '[success] %s: %s' % (_("Isolation geometry created"), geo_obj.options["name"]) msg = '[success] %s: %s' % (_("Isolation geometry created"), geo_obj.options["name"])
app_obj.inform.emit(msg) app_obj.inform.emit(msg)
# even if combine is checked, one pass is still single-geo
geo_obj.multigeo = True if passes > 1 else False
# ############################################################ # ############################################################
# ########## AREA SUBTRACTION ################################ # ########## AREA SUBTRACTION ################################
# ############################################################ # ############################################################
@@ -679,6 +677,7 @@ class GerberObject(FlatCAMObj, Gerber):
# Propagate options # Propagate options
geo_obj.options["tools_mill_tooldia"] = str(dia) geo_obj.options["tools_mill_tooldia"] = str(dia)
geo_obj.tool_type = self.app.defaults["tools_iso_tool_shape"] geo_obj.tool_type = self.app.defaults["tools_iso_tool_shape"]
geo_obj.multigeo = True
# 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
mill_dir = 1 if milling_type == 'cl' else 0 mill_dir = 1 if milling_type == 'cl' else 0
@@ -731,7 +730,6 @@ class GerberObject(FlatCAMObj, Gerber):
elif plot: elif plot:
msg = '[success] %s: %s' % (_("Isolation geometry created"), geo_obj.options["name"]) msg = '[success] %s: %s' % (_("Isolation geometry created"), geo_obj.options["name"])
app_obj.inform.emit(msg) app_obj.inform.emit(msg)
geo_obj.multigeo = False
# ############################################################ # ############################################################
# ########## AREA SUBTRACTION ################################ # ########## AREA SUBTRACTION ################################

View File

@@ -2,6 +2,7 @@ from tclCommands.TclCommand import TclCommand
import collections import collections
import logging import logging
from copy import deepcopy
from shapely.ops import unary_union from shapely.ops import unary_union
from shapely.geometry import LineString from shapely.geometry import LineString
@@ -107,7 +108,7 @@ class TclCommandCutout(TclCommand):
return "fail" return "fail"
def geo_init_me(geo_obj, app_obj): def geo_init_me(geo_obj, app_obj):
geo_obj.multigeo = False geo_obj.multigeo = True
margin = margin_par + dia_par / 2 margin = margin_par + dia_par / 2
gap_size = dia_par + gapsize_par gap_size = dia_par + gapsize_par
@@ -152,8 +153,30 @@ class TclCommandCutout(TclCommand):
cuts = cases[gaps_par] cuts = cases[gaps_par]
geo_obj.solid_geometry = unary_union([LineString(segment) for segment in cuts]) geo_obj.solid_geometry = unary_union([LineString(segment) for segment in cuts])
if not geo_obj.solid_geometry:
app_obj.log("TclCommandCutout.execute(). No geometry after cutout.")
return "fail"
default_tool_data = self.app.options.copy()
geo_obj.tools = {
1: {
'tooldia': dia_par,
'data': default_tool_data,
'solid_geometry': deepcopy(geo_obj.solid_geometry)
}
}
geo_obj.tools[1]['data']['tools_cutout_tooldia'] = dia_par
geo_obj.tools[1]['data']['tools_cutout_gaps_ff'] = gaps_par
geo_obj.tools[1]['data']['tools_cutout_margin'] = margin_par
geo_obj.tools[1]['data']['tools_cutout_gapsize'] = gapsize_par
try: try:
self.app.app_obj.new_object("geometry", outname, geo_init_me, plot=False) ret = self.app.app_obj.new_object("geometry", outname, geo_init_me, plot=False)
if ret == 'fail':
self.app.log.error("Could not create a cutout Geometry object." )
return "fail"
self.app.inform.emit("[success] Rectangular-form Cutout operation finished.") self.app.inform.emit("[success] Rectangular-form Cutout operation finished.")
except Exception as e: except Exception as e:
return "Operation failed: %s" % str(e) self.app.log.error("Cutout operation failed: %s" % str(e))
return "fail"

View File

@@ -4,7 +4,7 @@ import logging
import collections import collections
from copy import deepcopy from copy import deepcopy
from shapely.ops import unary_union from shapely.ops import unary_union
from shapely.geometry import Polygon, LineString, LinearRing from shapely.geometry import Polygon, LineString, LinearRing, MultiPolygon, MultiLineString
import gettext import gettext
import appTranslation as fcTranslate import appTranslation as fcTranslate
@@ -110,19 +110,15 @@ class TclCommandGeoCutout(TclCommandSignaled):
# If iterable, expand recursively. # If iterable, expand recursively.
try: try:
for geo_el in geometry: w_geo = geometry.geoms if isinstance(geometry, (MultiPolygon, MultiLineString)) else geometry
for geo_el in w_geo:
if geo_el is not None: if geo_el is not None:
flatten(geometry=geo_el, flatten(geometry=geo_el, reset=False, pathonly=pathonly)
reset=False,
pathonly=pathonly)
# Not iterable, do the actual indexing and add.
except TypeError: except TypeError:
# Not iterable, do the actual indexing and add.
if pathonly and type(geometry) == Polygon: if pathonly and type(geometry) == Polygon:
self.flat_geometry.append(geometry.exterior) self.flat_geometry.append(geometry.exterior)
flatten(geometry=geometry.interiors, flatten(geometry=geometry.interiors, reset=False, pathonly=True)
reset=False,
pathonly=True)
else: else:
self.flat_geometry.append(geometry) self.flat_geometry.append(geometry)
@@ -137,14 +133,14 @@ class TclCommandGeoCutout(TclCommandSignaled):
if type(target) == LineString or type(target) == LinearRing: if type(target) == LineString or type(target) == LinearRing:
diffs.append(target.difference(toolgeo)) diffs.append(target.difference(toolgeo))
else: else:
self.app.log.warning("Not implemented.") self.app.log.warning("TclCommandGeoCutout.execute(). Not implemented.")
return unary_union(diffs) return unary_union(diffs)
if 'name' in args: if 'name' in args:
name = args['name'] name = args['name']
else: else:
self.app.inform.emit( msg = "[WARNING] %s" % _("The name of the object for which cutout is done is missing. Add it and retry.")
"[WARNING] %s" % _("The name of the object for which cutout is done is missing. Add it and retry.")) self.app.inform.emit(msg)
return "fail" return "fail"
if 'margin' in args: if 'margin' in args:
@@ -210,155 +206,89 @@ class TclCommandGeoCutout(TclCommandSignaled):
gaps_u = gaps gaps_u = gaps
if cutout_obj.kind == 'geometry': if cutout_obj.kind == 'geometry':
# rename the obj name so it can be identified as cutout geo_to_cutout = deepcopy(cutout_obj.solid_geometry)
# cutout_obj.options["name"] += "_cutout"
# if gaps_u == 8 or gaps_u == '2lr':
# subtract_rectangle(cutout_obj,
# xmin - gapsize, # botleft_x
# py - gapsize + lenghty / 4, # botleft_y
# xmax + gapsize, # topright_x
# py + gapsize + lenghty / 4) # topright_y
# subtract_rectangle(cutout_obj,
# xmin - gapsize,
# py - gapsize - lenghty / 4,
# xmax + gapsize,
# py + gapsize - lenghty / 4)
#
# if gaps_u == 8 or gaps_u == '2tb':
# subtract_rectangle(cutout_obj,
# px - gapsize + lenghtx / 4,
# ymin - gapsize,
# px + gapsize + lenghtx / 4,
# ymax + gapsize)
# subtract_rectangle(cutout_obj,
# px - gapsize - lenghtx / 4,
# ymin - gapsize,
# px + gapsize - lenghtx / 4,
# ymax + gapsize)
#
# if gaps_u == 4 or gaps_u == 'lr':
# subtract_rectangle(cutout_obj,
# xmin - gapsize,
# py - gapsize,
# xmax + gapsize,
# py + gapsize)
#
# if gaps_u == 4 or gaps_u == 'tb':
# subtract_rectangle(cutout_obj,
# px - gapsize,
# ymin - gapsize,
# px + gapsize,
# ymax + gapsize)
def geo_init(geo_obj, app_obj):
geo = deepcopy(cutout_obj.solid_geometry)
if gaps_u == 8 or gaps_u == '2lr':
geo = substract_rectangle_geo(geo,
xmin - gapsize, # botleft_x
py - gapsize + lenghty / 4, # botleft_y
xmax + gapsize, # topright_x
py + gapsize + lenghty / 4) # topright_y
geo = substract_rectangle_geo(geo,
xmin - gapsize,
py - gapsize - lenghty / 4,
xmax + gapsize,
py + gapsize - lenghty / 4)
if gaps_u == 8 or gaps_u == '2tb':
geo = substract_rectangle_geo(geo,
px - gapsize + lenghtx / 4,
ymin - gapsize,
px + gapsize + lenghtx / 4,
ymax + gapsize)
geo = substract_rectangle_geo(geo,
px - gapsize - lenghtx / 4,
ymin - gapsize,
px + gapsize - lenghtx / 4,
ymax + gapsize)
if gaps_u == 4 or gaps_u == 'lr':
geo = substract_rectangle_geo(geo,
xmin - gapsize,
py - gapsize,
xmax + gapsize,
py + gapsize)
if gaps_u == 4 or gaps_u == 'tb':
geo = substract_rectangle_geo(geo,
px - gapsize,
ymin - gapsize,
px + gapsize,
ymax + gapsize)
geo_obj.solid_geometry = deepcopy(geo)
geo_obj.options['xmin'] = cutout_obj.options['xmin']
geo_obj.options['ymin'] = cutout_obj.options['ymin']
geo_obj.options['xmax'] = cutout_obj.options['xmax']
geo_obj.options['ymax'] = cutout_obj.options['ymax']
app_obj.disable_plots(objects=[cutout_obj])
app_obj.inform.emit("[success] %s" % _("Any-form Cutout operation finished."))
self.app.app_obj.new_object('geometry', outname, geo_init, plot=False)
elif cutout_obj.kind == 'gerber': elif cutout_obj.kind == 'gerber':
try:
def geo_init(geo_obj, app_obj): geo_to_cutout = cutout_obj.isolation_geometry((dia / 2), iso_type=0, corner=2)
try: except Exception as exc:
geo = cutout_obj.isolation_geometry((dia / 2), iso_type=0, corner=2) self.app.log.error("TclCommandGeoCutout.execute() --> %s" % str(exc))
except Exception as exc: return 'fail'
self.app.log.error("TclCommandGeoCutout.execute() --> %s" % str(exc))
return 'fail'
if gaps_u == 8 or gaps_u == '2lr':
geo = substract_rectangle_geo(geo,
xmin - gapsize, # botleft_x
py - gapsize + lenghty / 4, # botleft_y
xmax + gapsize, # topright_x
py + gapsize + lenghty / 4) # topright_y
geo = substract_rectangle_geo(geo,
xmin - gapsize,
py - gapsize - lenghty / 4,
xmax + gapsize,
py + gapsize - lenghty / 4)
if gaps_u == 8 or gaps_u == '2tb':
geo = substract_rectangle_geo(geo,
px - gapsize + lenghtx / 4,
ymin - gapsize,
px + gapsize + lenghtx / 4,
ymax + gapsize)
geo = substract_rectangle_geo(geo,
px - gapsize - lenghtx / 4,
ymin - gapsize,
px + gapsize - lenghtx / 4,
ymax + gapsize)
if gaps_u == 4 or gaps_u == 'lr':
geo = substract_rectangle_geo(geo,
xmin - gapsize,
py - gapsize,
xmax + gapsize,
py + gapsize)
if gaps_u == 4 or gaps_u == 'tb':
geo = substract_rectangle_geo(geo,
px - gapsize,
ymin - gapsize,
px + gapsize,
ymax + gapsize)
geo_obj.solid_geometry = deepcopy(geo)
geo_obj.options['xmin'] = cutout_obj.options['xmin']
geo_obj.options['ymin'] = cutout_obj.options['ymin']
geo_obj.options['xmax'] = cutout_obj.options['xmax']
geo_obj.options['ymax'] = cutout_obj.options['ymax']
app_obj.inform.emit("[success] %s" % _("Any-form Cutout operation finished."))
self.app.app_obj.new_object('geometry', outname, geo_init, plot=False)
cutout_obj = self.app.collection.get_by_name(outname)
else: else:
self.app.inform.emit("[ERROR] %s" % _("Cancelled. Object type is not supported.")) self.app.inform.emit("[ERROR] %s" % _("Cancelled. Object type is not supported."))
return "fail" return "fail"
def geo_init(geo_obj, app_obj):
geo_obj.multigeo = True
geo = geo_to_cutout
if gaps_u == 8 or gaps_u == '2lr':
geo = substract_rectangle_geo(geo,
xmin - gapsize, # botleft_x
py - gapsize + lenghty / 4, # botleft_y
xmax + gapsize, # topright_x
py + gapsize + lenghty / 4) # topright_y
geo = substract_rectangle_geo(geo,
xmin - gapsize,
py - gapsize - lenghty / 4,
xmax + gapsize,
py + gapsize - lenghty / 4)
if gaps_u == 8 or gaps_u == '2tb':
geo = substract_rectangle_geo(geo,
px - gapsize + lenghtx / 4,
ymin - gapsize,
px + gapsize + lenghtx / 4,
ymax + gapsize)
geo = substract_rectangle_geo(geo,
px - gapsize - lenghtx / 4,
ymin - gapsize,
px + gapsize - lenghtx / 4,
ymax + gapsize)
if gaps_u == 4 or gaps_u == 'lr':
geo = substract_rectangle_geo(geo,
xmin - gapsize,
py - gapsize,
xmax + gapsize,
py + gapsize)
if gaps_u == 4 or gaps_u == 'tb':
geo = substract_rectangle_geo(geo,
px - gapsize,
ymin - gapsize,
px + gapsize,
ymax + gapsize)
geo_obj.solid_geometry = deepcopy(geo)
geo_obj.options['xmin'] = cutout_obj.options['xmin']
geo_obj.options['ymin'] = cutout_obj.options['ymin']
geo_obj.options['xmax'] = cutout_obj.options['xmax']
geo_obj.options['ymax'] = cutout_obj.options['ymax']
if not geo_obj.solid_geometry:
app_obj.log("TclCommandGeoCutout.execute(). No geometry after geo-cutout.")
return "fail"
default_tool_data = self.app.options.copy()
geo_obj.tools = {
1: {
'tooldia': dia,
'data': default_tool_data,
'solid_geometry': deepcopy(geo_obj.solid_geometry)
}
}
geo_obj.tools[1]['data']['tools_cutout_tooldia'] = dia
geo_obj.tools[1]['data']['tools_cutout_gaps_ff'] = gaps
geo_obj.tools[1]['data']['tools_cutout_margin'] = margin
geo_obj.tools[1]['data']['tools_cutout_gapsize'] = gapsize
app_obj.disable_plots(objects=[cutout_obj])
ret = self.app.app_obj.new_object('geometry', outname, geo_init, plot=False)
if ret == 'fail':
msg = "Could not create a geo-cutout Geometry object from a %s object." % cutout_obj.kind.capialize()
self.app.log.error(msg)
return "fail"
else:
self.app.inform.emit("[success] %s" % _("Any-form Cutout operation finished."))