From 9d0bcf477a18b04fad786649c550095a05441992 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 19 Feb 2019 14:53:55 +0200 Subject: [PATCH] - removed the Open Gerber with 'follow' menu entry and also the open_gerber Tcl Command attribute 'follow'. This is no longer required because now the follow_geometry is stored by default in a Gerber object attribute gerber_obj.follow_geometry - added a new parameter for the Tcl CommandIsolate, named: 'follow'. When follow = 1 (True) the resulting geometry will follow the Gerber paths. --- FlatCAMApp.py | 46 +------ FlatCAMGUI.py | 10 -- FlatCAMObj.py | 50 ++++++-- ObjectUI.py | 7 +- README.md | 7 +- camlib.py | 191 +++++++++++++++++----------- tclCommands/TclCommandGeoCutout.py | 2 +- tclCommands/TclCommandIsolate.py | 10 +- tclCommands/TclCommandOpenGerber.py | 7 +- 9 files changed, 176 insertions(+), 154 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 557d5f9d..8d0aee50 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -1101,7 +1101,6 @@ class App(QtCore.QObject): self.ui.menufilenewexc.triggered.connect(self.new_excellon_object) self.ui.menufileopengerber.triggered.connect(self.on_fileopengerber) - self.ui.menufileopengerber_follow.triggered.connect(self.on_fileopengerber_follow) self.ui.menufileopenexcellon.triggered.connect(self.on_fileopenexcellon) self.ui.menufileopengcode.triggered.connect(self.on_fileopengcode) self.ui.menufileopenproject.triggered.connect(self.on_file_openproject) @@ -4864,42 +4863,6 @@ class App(QtCore.QObject): self.worker_task.emit({'fcn': self.open_gerber, 'params': [filename]}) - def on_fileopengerber_follow(self): - """ - File menu callback for opening a Gerber. - - :return: None - """ - - self.report_usage("on_fileopengerber_follow") - App.log.debug("on_fileopengerber_follow()") - _filter_ = "Gerber Files (*.gbr *.ger *.gtl *.gbl *.gts *.gbs *.gtp *.gbp *.gto *.gbo *.gm1 *.gml *.gm3 *.gko " \ - "*.cmp *.sol *.stc *.sts *.plc *.pls *.crc *.crs *.tsm *.bsm *.ly2 *.ly15 *.dim *.mil *.grb" \ - "*.top *.bot *.smt *.smb *.sst *.ssb *.spt *.spb *.pho *.gdo *.art *.gbd);;" \ - "Protel Files (*.gtl *.gbl *.gts *.gbs *.gto *.gbo *.gtp *.gbp *.gml *.gm1 *.gm3 *.gko);;" \ - "Eagle Files (*.cmp *.sol *.stc *.sts *.plc *.pls *.crc *.crs *.tsm *.bsm *.ly2 *.ly15 *.dim *.mil);;" \ - "OrCAD Files (*.top *.bot *.smt *.smb *.sst *.ssb *.spt *.spb);;" \ - "Allegro Files (*.art);;" \ - "Mentor Files (*.pho *.gdo);;" \ - "All Files (*.*)" - try: - filename, _ = QtWidgets.QFileDialog.getOpenFileName(caption="Open Gerber with Follow", - directory=self.get_last_folder(), filter=_filter_) - except TypeError: - filename, _ = QtWidgets.QFileDialog.getOpenFileName(caption="Open Gerber with Follow", filter=_filter_) - - # The Qt methods above will return a QString which can cause problems later. - # So far json.dump() will fail to serialize it. - # TODO: Improve the serialization methods and remove this fix. - filename = str(filename) - follow = True - - if filename == "": - self.inform.emit("[WARNING_NOTCL]Open Gerber-Follow cancelled.") - else: - self.worker_task.emit({'fcn': self.open_gerber, - 'params': [filename, follow]}) - def on_fileopenexcellon(self): """ File menu callback for opening an Excellon file. @@ -5996,7 +5959,7 @@ class App(QtCore.QObject): self.inform.emit("[success] Opened: " + filename) self.progress.emit(100) - def open_gerber(self, filename, follow=False, outname=None): + def open_gerber(self, filename, outname=None): """ Opens a Gerber file, parses it and creates a new object for it in the program. Thread-safe. @@ -6020,7 +5983,7 @@ class App(QtCore.QObject): # Opening the file happens here self.progress.emit(30) try: - gerber_obj.parse_file(filename, follow=follow) + gerber_obj.parse_file(filename) except IOError: app_obj.inform.emit("[ERROR_NOTCL] Failed to open file: " + filename) app_obj.progress.emit(0) @@ -6048,10 +6011,7 @@ class App(QtCore.QObject): # Further parsing self.progress.emit(70) # TODO: Note the mixture of self and app_obj used here - if follow is False: - App.log.debug("open_gerber()") - else: - App.log.debug("open_gerber() with 'follow' attribute") + App.log.debug("open_gerber()") with self.proc_container.new("Opening Gerber") as proc: diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index e18320e1..995e7413 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -67,16 +67,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow): 'Open &Gerber ...\tCTRL+G', self) self.menufile_open.addAction(self.menufileopengerber) - # Open gerber with follow... - self.menufileopengerber_follow = QtWidgets.QAction(QtGui.QIcon('share/flatcam_icon24.png'), - 'Open &Gerber (w/ Follow) ...', self) - self.menufileopengerber_follow.setToolTip( - "Will open a Gerber file with the 'follow' attribute.\n" - "This will actually 'trace' the features of a Gerber file and\n" - "the resulting Gerber geometry will have no volume, it will be\n" - "made out of lines." - ) - self.menufile_open.addAction(self.menufileopengerber_follow) self.menufile_open.addSeparator() # Open Excellon ... diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 6cd57213..a2791e10 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -371,9 +371,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber): if grb_final.solid_geometry is None: grb_final.solid_geometry = [] + grb_final.follow_geometry = [] if type(grb_final.solid_geometry) is not list: grb_final.solid_geometry = [grb_final.solid_geometry] + grb_final.follow_geometry = [grb_final.follow_geometry] for grb in grb_list: for option in grb.options: @@ -389,8 +391,10 @@ class FlatCAMGerber(FlatCAMObj, Gerber): else: # If not list, just append for geos in grb.solid_geometry: grb_final.solid_geometry.append(geos) + grb_final.follow_geometry.append(geos) grb_final.solid_geometry = MultiPolygon(grb_final.solid_geometry) + grb_final.follow_geometry = MultiPolygon(grb_final.follow_geometry) def __init__(self, name): Gerber.__init__(self, steps_per_circle=int(self.app.defaults["gerber_circle_steps"])) @@ -420,6 +424,8 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.multigeo = False + self.follow = False + self.apertures_row = 0 # store the source file here @@ -482,6 +488,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.ui.generate_bb_button.clicked.connect(self.on_generatebb_button_click) self.ui.generate_noncopper_button.clicked.connect(self.on_generatenoncopper_button_click) self.ui.aperture_table_visibility_cb.stateChanged.connect(self.on_aperture_table_visibility_change) + self.ui.follow_cb.stateChanged.connect(self.on_follow_cb_click) # Show/Hide Advanced Options if self.app.defaults["global_advanced"] is False: @@ -675,7 +682,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): def on_int_iso_button_click(self, *args): - if self.ui.follow_cb.get_value() == True: + if self.ui.follow_cb.get_value() is True: obj = self.app.collection.get_active() obj.follow() # in the end toggle the visibility of the origin object so we can see the generated Geometry @@ -687,9 +694,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber): def on_iso_button_click(self, *args): - if self.ui.follow_cb.get_value() == True: + if self.ui.follow_cb.get_value() is True: obj = self.app.collection.get_active() - obj.follow() + 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: @@ -697,7 +704,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.read_form() self.isolate() - def follow(self, outname=None): + def follow_geo(self, outname=None): """ Creates a geometry object "following" the gerber paths. @@ -715,7 +722,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): def follow_init(follow_obj, app): # Propagate options follow_obj.options["cnctooldia"] = float(self.options["isotooldia"]) - follow_obj.solid_geometry = self.solid_geometry + follow_obj.solid_geometry = self.follow_geometry # TODO: Do something if this is None. Offer changing name? try: @@ -724,7 +731,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): return "Operation failed: %s" % str(e) def isolate(self, iso_type=None, dia=None, passes=None, overlap=None, - outname=None, combine=None, milling_type=None): + outname=None, combine=None, milling_type=None, follow=None): """ Creates an isolation routing geometry object in the project. @@ -735,6 +742,8 @@ class FlatCAMGerber(FlatCAMObj, Gerber): :param outname: Base name of the output object :return: None """ + + if dia is None: dia = float(self.options["isotooldia"]) if passes is None: @@ -755,7 +764,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): base_name = self.options["name"] + "_iso" base_name = outname or base_name - def generate_envelope(offset, invert, envelope_iso_type=2): + def generate_envelope(offset, invert, envelope_iso_type=2, follow=None): # 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 tool needs to travel on the right side of the features (this is called conventional milling) @@ -763,7 +772,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): # 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. try: - geom = self.isolation_geometry(offset, iso_type=envelope_iso_type) + geom = self.isolation_geometry(offset, iso_type=envelope_iso_type, follow=follow) except Exception as e: log.debug(str(e)) return 'fail' @@ -803,9 +812,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber): # if milling type is climb then the move is counter-clockwise around features if milling_type == 'cl': # geom = generate_envelope (offset, i == 0) - geom = generate_envelope(iso_offset, 1, envelope_iso_type=self.iso_type) + geom = generate_envelope(iso_offset, 1, envelope_iso_type=self.iso_type, follow=follow) else: - geom = generate_envelope(iso_offset, 0, envelope_iso_type=self.iso_type) + geom = generate_envelope(iso_offset, 0, envelope_iso_type=self.iso_type, follow=follow) geo_obj.solid_geometry.append(geom) # detect if solid_geometry is empty and this require list flattening which is "heavy" @@ -855,9 +864,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber): # if milling type is climb then the move is counter-clockwise around features if milling_type == 'cl': # geo_obj.solid_geometry = generate_envelope(offset, i == 0) - geo_obj.solid_geometry = generate_envelope(offset, 1, envelope_iso_type=self.iso_type) + geo_obj.solid_geometry = generate_envelope(offset, 1, envelope_iso_type=self.iso_type, + follow=follow) else: - geo_obj.solid_geometry = generate_envelope(offset, 0, envelope_iso_type=self.iso_type) + geo_obj.solid_geometry = generate_envelope(offset, 0, envelope_iso_type=self.iso_type, + follow=follow) # 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 @@ -897,6 +908,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.read_form_item('multicolored') self.plot() + def on_follow_cb_click(self): + if self.muted_ui: + return + self.plot() + def on_aperture_table_visibility_change(self): if self.ui.aperture_table_visibility_cb.isChecked(): self.ui.apertures_table.setVisible(True) @@ -942,7 +958,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber): else: face_color = self.app.defaults['global_plot_fill'] - geometry = self.solid_geometry + # if the Follow Geometry checkbox is checked then plot only the follow geometry + if self.ui.follow_cb.get_value(): + geometry = self.follow_geometry + else: + geometry = self.solid_geometry # Make sure geometry is iterable. try: @@ -962,6 +982,8 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.add_shape(shape=g, color=color, face_color=random_color() if self.options['multicolored'] else face_color, visible=self.options['plot']) + elif type(g) == Point: + pass else: for el in g: self.add_shape(shape=el, color=color, @@ -972,6 +994,8 @@ class FlatCAMGerber(FlatCAMObj, Gerber): if type(g) == Polygon or type(g) == LineString: self.add_shape(shape=g, color=random_color() if self.options['multicolored'] else 'black', visible=self.options['plot']) + elif type(g) == Point: + pass else: for el in g: self.add_shape(shape=el, color=random_color() if self.options['multicolored'] else 'black', diff --git a/ObjectUI.py b/ObjectUI.py index 6a6cf45a..225af137 100644 --- a/ObjectUI.py +++ b/ObjectUI.py @@ -266,13 +266,12 @@ class GerberObjectUI(ObjectUI): grid1.addWidget(self.combine_passes_cb, 4, 0) # generate follow - self.follow_cb = FCCheckBox(label='"Follow" Geo') + self.follow_cb = FCCheckBox(label='"Follow"') self.follow_cb.setToolTip( "Generate a 'Follow' geometry.\n" "This means that it will cut through\n" - "the middle of the trace.\n" - "Requires that the Gerber file to be\n" - "loaded with 'follow' parameter." + "the middle of the trace." + ) grid1.addWidget(self.follow_cb, 4, 1) diff --git a/README.md b/README.md index 59a059e3..20a49ca0 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ CAD program, and create G-Code for Isolation routing. - made FlatCAM able to load old type (uncompressed) FlatCAM projects - fixed issue with not loading old projects that do not have certain information's required by the new versions of FlatCAM - compacted a bit more the GUI for Gerber Object - +- removed the Open Gerber with 'follow' menu entry and also the open_gerber Tcl Command attribute 'follow'. This is no longer required because now the follow_geometry is stored by default in a Gerber object attribute gerber_obj.follow_geometry +- added a new parameter for the Tcl CommandIsolate, named: 'follow'. When follow = 1 (True) the resulting geometry will follow the Gerber paths. 18.02.2019 @@ -23,14 +24,14 @@ CAD program, and create G-Code for Isolation routing. - the Paint Tool in Geometry Editor will load the default values from Tool Paint in Preferences - when the Tools in Geometry Editor are activated, the notebook with the Tool Tab will be unhidden. After execution the notebook will hide again for the Buffer Tool. - changed the font in Tool names -- added in Geometry Editor a new Tool: Transformation Tool. It still has some bugs, though ... +- added in Geometry Editor a new Tool: Transformation Tool. - in Geometry Editor by selecting a shape with a selection shape, that object was added multiple times (one per each selection) to the selected list, which is not intended. Bug fixed. - finished adding Transform Tool in Geometry Editor - everything is working as intended - fixed a bug in Tool Transform that made the user to not be able to capture the click coordinates with SHIFT + LMB click combo - added the ability to choose an App QStyle out of the offered choices (different for each OS) to be applied at the next app start (Preferences -> General -> Gui Pref -> Style Combobox) - added support for FlatCAM usage with High DPI monitors (4k). It is applied on the next app startup after change in Preferences -> General -> Gui Settings -> HDPI Support Checkbox - made the app not remember the window size if the app is maximized and remember in QSettings if it was maximized. This way we can restore the maximized state but restore the windows size unmaximized -- added a button to clear de GUI preferences in Preferences -> General -> Gui Settings -> Clear GUI Settings +- added a button to clear the GUI preferences in Preferences -> General -> Gui Settings -> Clear GUI Settings - added key shortcuts for the shape transformations within Geometry Editor: X, Y keys for Flip(mirror), SHIFT+X, SHIFT+Y combo keys for Skew and ALT+X, ALT+Y combo keys for Offset - adjusted the plotcanvas.zomm_fit() function so the objects are better fit into view (with a border around) - modified the GUI in Objects Selected Tab to accommodate 2 different modes: basic and Advanced. In Basic mode, some of the functionality's are hidden from the user. diff --git a/camlib.py b/camlib.py index f54e0f69..f11ed041 100644 --- a/camlib.py +++ b/camlib.py @@ -92,8 +92,11 @@ class Geometry(object): # Final geometry: MultiPolygon or list (of geometry constructs) self.solid_geometry = None + # Final geometry: MultiLineString or list (of LineString or Points) + self.follow_geometry = None + # Attributes to be included in serialization - self.ser_attrs = ["units", 'solid_geometry'] + self.ser_attrs = ["units", 'solid_geometry', 'follow_geometry'] # Flattened geometry (list of paths only) self.flat_geometry = [] @@ -500,7 +503,7 @@ class Geometry(object): # # return self.flat_geometry, self.flat_geometry_rtree - def isolation_geometry(self, offset, iso_type=2, corner=None): + def isolation_geometry(self, offset, iso_type=2, corner=None, follow=None): """ Creates contours around geometry at a given offset distance. @@ -542,16 +545,24 @@ class Geometry(object): # the previously commented block is replaced with this block - regression - to solve the bug with multiple # isolation passes cutting from the copper features if offset == 0: - geo_iso = self.solid_geometry - else: - if corner is None: - geo_iso = self.solid_geometry.buffer(offset, int(int(self.geo_steps_per_circle) / 4)) + if follow: + geo_iso = self.follow_geometry else: - geo_iso = self.solid_geometry.buffer(offset, int(int(self.geo_steps_per_circle) / 4), join_style=corner) + geo_iso = self.solid_geometry + else: + if follow: + geo_iso = self.follow_geometry + else: + if corner is None: + geo_iso = self.solid_geometry.buffer(offset, int(int(self.geo_steps_per_circle) / 4)) + else: + geo_iso = self.solid_geometry.buffer(offset, int(int(self.geo_steps_per_circle) / 4), + join_style=corner) # end of replaced block - - if iso_type == 2: + if follow: + return geo_iso + elif iso_type == 2: return geo_iso elif iso_type == 0: return self.get_exteriors(geo_iso) @@ -1889,8 +1900,12 @@ class Gerber (Geometry): # Initialize parent Geometry.__init__(self, geo_steps_per_circle=int(steps_per_circle)) + # will store the Gerber geometry's as solids self.solid_geometry = Polygon() + # will store the Gerber geometry's as paths + self.follow_geometry = [] + # Number format self.int_digits = 3 """Number of integer digits in Gerber numbers. Used during parsing.""" @@ -2113,10 +2128,10 @@ class Gerber (Geometry): yield line break - self.parse_lines(line_generator(), follow=follow) + self.parse_lines(line_generator()) #@profile - def parse_lines(self, glines, follow=False): + def parse_lines(self, glines): """ Main Gerber parser. Reads Gerber and populates ``self.paths``, ``self.apertures``, ``self.flashes``, ``self.regions`` and ``self.units``. @@ -2143,6 +2158,9 @@ class Gerber (Geometry): # applying a union for every new polygon. poly_buffer = [] + # store here the follow geometry + follow_buffer = [] + last_path_aperture = None current_aperture = None @@ -2208,10 +2226,11 @@ class Gerber (Geometry): # --- Buffered ---- width = self.apertures[last_path_aperture]["size"] - if follow: - geo = LineString(path) - else: - geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4)) + geo = LineString(path) + if not geo.is_empty: + follow_buffer.append(geo) + + geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4)) if not geo.is_empty: poly_buffer.append(geo) @@ -2222,9 +2241,14 @@ class Gerber (Geometry): # TODO: Remove when bug fixed if len(poly_buffer) > 0: if current_polarity == 'D': + self.follow_geometry = self.solid_geometry.union(cascaded_union(follow_buffer)) self.solid_geometry = self.solid_geometry.union(cascaded_union(poly_buffer)) + else: - self.solid_geometry = self.solid_geometry.difference(cascaded_union(poly_buffer)) + self.follow_geometry = self.solid_geometry.difference(cascaded_union(follow_buffer)) + self.solid_geometry = self.solid_geometry.union(cascaded_union(poly_buffer)) + + follow_buffer = [] poly_buffer = [] current_polarity = match.group(1) @@ -2405,10 +2429,11 @@ class Gerber (Geometry): # --- Buffered ---- width = self.apertures[last_path_aperture]["size"] - if follow: - geo = LineString(path) - else: - geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4)) + geo = LineString(path) + if not geo.is_empty: + follow_buffer.append(geo) + + geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4)) if not geo.is_empty: poly_buffer.append(geo) @@ -2424,10 +2449,11 @@ class Gerber (Geometry): ## --- Buffered --- width = self.apertures[last_path_aperture]["size"] - if follow: - geo = LineString(path) - else: - geo = LineString(path).buffer(width/1.999, int(self.steps_per_circle / 4)) + geo = LineString(path) + if not geo.is_empty: + follow_buffer.append(geo) + + geo = LineString(path).buffer(width/1.999, int(self.steps_per_circle / 4)) if not geo.is_empty: poly_buffer.append(geo) @@ -2445,6 +2471,7 @@ class Gerber (Geometry): if current_operation_code == 2: if geo: if not geo.is_empty: + follow_buffer.append(geo) poly_buffer.append(geo) continue @@ -2464,15 +2491,14 @@ class Gerber (Geometry): # "aperture": last_path_aperture}) # --- Buffered --- - if follow: - region = Polygon() - else: - region = Polygon(path) + region = Polygon() + if not region.is_empty: + follow_buffer.append(region) + + region = Polygon(path) if not region.is_valid: - if not follow: - region = region.buffer(0, int(self.steps_per_circle / 4)) - + region = region.buffer(0, int(self.steps_per_circle / 4)) if not region.is_empty: poly_buffer.append(region) @@ -2529,7 +2555,7 @@ class Gerber (Geometry): if path[-1] != [linear_x, linear_y]: path.append([linear_x, linear_y]) - if follow == 0 and making_region is False: + if making_region is False: # if the aperture is rectangle then add a rectangular shape having as parameters the # coordinates of the start and end point and also the width and height # of the 'R' aperture @@ -2555,29 +2581,35 @@ class Gerber (Geometry): geo = None ## --- BUFFERED --- + # this treats the case when we are storing geometry as paths only if making_region: - if follow: - geo = Polygon() - else: - elem = [linear_x, linear_y] - if elem != path[-1]: - path.append([linear_x, linear_y]) - try: - geo = Polygon(path) - except ValueError: - log.warning("Problem %s %s" % (gline, line_num)) - self.app.inform.emit("[ERROR] Region does not have enough points. " - "File will be processed but there are parser errors. " - "Line number: %s" % str(line_num)) + geo = Polygon() + else: + geo = LineString(path) + try: + if self.apertures[last_path_aperture]["type"] != 'R': + if not geo.is_empty: + follow_buffer.append(geo) + except: + follow_buffer.append(geo) + + # this treats the case when we are storing geometry as solids + if making_region: + elem = [linear_x, linear_y] + if elem != path[-1]: + path.append([linear_x, linear_y]) + try: + geo = Polygon(path) + except ValueError: + log.warning("Problem %s %s" % (gline, line_num)) + self.app.inform.emit("[ERROR] Region does not have enough points. " + "File will be processed but there are parser errors. " + "Line number: %s" % str(line_num)) else: if last_path_aperture is None: log.warning("No aperture defined for curent path. (%d)" % line_num) width = self.apertures[last_path_aperture]["size"] # TODO: WARNING this should fail! - #log.debug("Line %d: Setting aperture to %s before buffering." % (line_num, last_path_aperture)) - if follow: - geo = LineString(path) - else: - geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4)) + geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4)) try: if self.apertures[last_path_aperture]["type"] != 'R': @@ -2600,19 +2632,23 @@ class Gerber (Geometry): # Create path draw so far. if len(path) > 1: # --- Buffered ---- + + # this treats the case when we are storing geometry as paths + geo = LineString(path) + if not geo.is_empty: + try: + if self.apertures[current_aperture]["type"] != 'R': + follow_buffer.append(geo) + except: + follow_buffer.append(geo) + + # this treats the case when we are storing geometry as solids width = self.apertures[last_path_aperture]["size"] - - if follow: - geo = LineString(path) - else: - geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4)) - + geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4)) if not geo.is_empty: try: if self.apertures[current_aperture]["type"] != 'R': poly_buffer.append(geo) - else: - pass except: poly_buffer.append(geo) @@ -2621,11 +2657,12 @@ class Gerber (Geometry): # --- BUFFERED --- # Draw the flash - if follow: - continue + # this treats the case when we are storing geometry as paths + follow_buffer.append(Point([linear_x, linear_y])) + + # this treats the case when we are storing geometry as solids flash = Gerber.create_flash_geometry( - Point( - [linear_x, linear_y]), + Point( [linear_x, linear_y]), self.apertures[current_aperture], int(self.steps_per_circle) ) @@ -2711,10 +2748,13 @@ class Gerber (Geometry): # --- BUFFERED --- width = self.apertures[last_path_aperture]["size"] - if follow: - buffered = LineString(path) - else: - buffered = LineString(path).buffer(width / 1.999, int(self.steps_per_circle)) + # this treats the case when we are storing geometry as paths + geo = LineString(path) + if not geo.is_empty: + follow_buffer.append(geo) + + # this treats the case when we are storing geometry as solids + buffered = LineString(path).buffer(width / 1.999, int(self.steps_per_circle)) if not buffered.is_empty: poly_buffer.append(buffered) @@ -2833,19 +2873,24 @@ class Gerber (Geometry): else: # EOF, create shapely LineString if something still in path ## --- Buffered --- + + # this treats the case when we are storing geometry as paths + geo = LineString(path) + if not geo.is_empty: + follow_buffer.append(geo) + + # this treats the case when we are storing geometry as solids width = self.apertures[last_path_aperture]["size"] - if follow: - geo = LineString(path) - else: - geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4)) + geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4)) if not geo.is_empty: poly_buffer.append(geo) # --- Apply buffer --- - if follow: - self.solid_geometry = poly_buffer - return + # this treats the case when we are storing geometry as paths + self.follow_geometry = follow_buffer + + # this treats the case when we are storing geometry as solids log.warning("Joining %d polygons." % len(poly_buffer)) if len(poly_buffer) == 0: diff --git a/tclCommands/TclCommandGeoCutout.py b/tclCommands/TclCommandGeoCutout.py index db49be02..749bce35 100644 --- a/tclCommands/TclCommandGeoCutout.py +++ b/tclCommands/TclCommandGeoCutout.py @@ -225,7 +225,7 @@ class TclCommandGeoCutout(TclCommandSignaled): def geo_init(geo_obj, app_obj): try: - geo = cutout_obj.isolation_geometry((dia / 2), iso_type=0, corner=2) + geo = cutout_obj.isolation_geometry((dia / 2), iso_type=0, corner=2, follow=None) except Exception as e: log.debug("TclCommandGeoCutout.execute() --> %s" % str(e)) return 'fail' diff --git a/tclCommands/TclCommandIsolate.py b/tclCommands/TclCommandIsolate.py index 93ad41d0..7293f506 100644 --- a/tclCommands/TclCommandIsolate.py +++ b/tclCommands/TclCommandIsolate.py @@ -28,7 +28,9 @@ class TclCommandIsolate(TclCommandSignaled): ('passes', int), ('overlap', float), ('combine', int), - ('outname', str) + ('outname', str), + ('follow', str) + ]) # array of mandatory options for current Tcl command: required = {'name','outname'} @@ -43,7 +45,8 @@ class TclCommandIsolate(TclCommandSignaled): ('passes', 'Passes of tool width.'), ('overlap', 'Fraction of tool diameter to overlap passes.'), ('combine', 'Combine all passes into one geometry.'), - ('outname', 'Name of the resulting Geometry object.') + ('outname', 'Name of the resulting Geometry object.'), + ('follow', 'Create a Geometry that follows the Gerber path.') ]), 'examples': [] } @@ -68,6 +71,9 @@ class TclCommandIsolate(TclCommandSignaled): else: timeout = 10000 + if 'follow' not in args: + args['follow'] = None + obj = self.app.collection.get_by_name(name) if obj is None: self.raise_tcl_error("Object not found: %s" % name) diff --git a/tclCommands/TclCommandOpenGerber.py b/tclCommands/TclCommandOpenGerber.py index 9472aa3e..2d4b5b48 100644 --- a/tclCommands/TclCommandOpenGerber.py +++ b/tclCommands/TclCommandOpenGerber.py @@ -17,7 +17,6 @@ class TclCommandOpenGerber(TclCommandSignaled): # dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value option_types = collections.OrderedDict([ - ('follow', str), ('outname', str) ]) @@ -29,7 +28,6 @@ class TclCommandOpenGerber(TclCommandSignaled): 'main': "Opens a Gerber file.", 'args': collections.OrderedDict([ ('filename', 'Path to file to open.'), - ('follow', 'N If 1, does not create polygons, just follows the gerber path.'), ('outname', 'Name of the resulting Gerber object.') ]), 'examples': [] @@ -54,7 +52,7 @@ class TclCommandOpenGerber(TclCommandSignaled): # Opening the file happens here self.app.progress.emit(30) try: - gerber_obj.parse_file(filename, follow=follow) + gerber_obj.parse_file(filename) except IOError: app_obj.inform.emit("[ERROR_NOTCL] Failed to open file: %s " % filename) @@ -77,9 +75,8 @@ class TclCommandOpenGerber(TclCommandSignaled): else: outname = filename.split('/')[-1].split('\\')[-1] - follow = None if 'follow' in args: - follow = args['follow'] + self.raise_tcl_error("The 'follow' parameter is obsolete. To create 'follow' geometry use the 'follow' parameter for the Tcl Command isolate()") with self.app.proc_container.new("Opening Gerber"):