Selaa lähdekoodia

Added support for "plot" checkboxes and "solid" checkbox for Gerber.

Juan Pablo Caram 12 vuotta sitten
vanhempi
commit
f8352e7188
4 muutettua tiedostoa jossa 141 lisäystä ja 96 poistoa
  1. 57 21
      FlatCAM.py
  2. 9 55
      FlatCAM.ui
  3. 74 19
      camlib.py
  4. 1 1
      defaults.json

+ 57 - 21
FlatCAM.py

@@ -266,9 +266,16 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
         self.options['bboxmargin'] *= factor
 
     def plot(self, figure):
+        """
+        Plots the object on to the specified figure.
+
+        :param figure: Matplotlib figure on which to plot.
+        """
+
         FlatCAMObj.plot(self, figure)
 
-        #self.create_geometry()
+        if not self.options["plot"]:
+            return
 
         if self.options["mergepolys"]:
             geometry = self.solid_geometry
@@ -282,12 +289,22 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
         else:
             linespec = 'k-'
 
-        for poly in geometry:
-            x, y = poly.exterior.xy
-            self.axes.plot(x, y, linespec)
-            for ints in poly.interiors:
-                x, y = ints.coords.xy
+        if self.options["solid"]:
+            for poly in geometry:
+                # TODO: Too many things hardcoded.
+                patch = PolygonPatch(poly,
+                                     facecolor="#BBF268",
+                                     edgecolor="#006E20",
+                                     alpha=0.75,
+                                     zorder=2)
+                self.axes.add_patch(patch)
+        else:
+            for poly in geometry:
+                x, y = poly.exterior.xy
                 self.axes.plot(x, y, linespec)
+                for ints in poly.interiors:
+                    x, y = ints.coords.xy
+                    self.axes.plot(x, y, linespec)
 
         self.app.canvas.queue_draw()
 
@@ -346,8 +363,9 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
 
     def plot(self, figure):
         FlatCAMObj.plot(self, figure)
-        #self.setup_axes(figure)
-        #self.create_geometry()
+
+        if not self.options["plot"]:
+            return
 
         # Plot excellon
         for geo in self.solid_geometry:
@@ -400,15 +418,11 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
 
         self.options.update({
             "plot": True,
-            "solid": False,
-            "multicolored": False,
             "tooldia": 0.4 / 25.4  # 0.4mm in inches
         })
 
         self.form_kinds.update({
             "plot": "cb",
-            "solid": "cb",
-            "multicolored": "cb",
             "tooldia": "entry_eval"
         })
 
@@ -418,8 +432,11 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         self.ser_attrs += ['options', 'kind']
 
     def plot(self, figure):
-        FlatCAMObj.plot(self, figure)
-        #self.setup_axes(figure)
+        FlatCAMObj.plot(self, figure)  # Only sets up axes
+
+        if not self.options["plot"]:
+            return
+
         self.plot2(self.axes, tooldia=self.options["tooldia"])
         self.app.on_zoom_fit(None)
         self.app.canvas.queue_draw()
@@ -494,7 +511,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
 
     def plot(self, figure):
         FlatCAMObj.plot(self, figure)
-        #self.setup_axes(figure)
+
+        if not self.options["plot"]:
+            return
 
         try:
             _ = iter(self.solid_geometry)
@@ -1262,6 +1281,11 @@ class App:
     ########################################
     ##         EVENT HANDLERS             ##
     ########################################
+    def on_cb_plot_toggled(self, widget):
+        self.get_current().read_form()
+        self.get_current().plot(self.figure)
+        self.on_zoom_fit(None)  # TODO: Does not update correctly otherwise.
+
     def on_about(self, widget):
         """
         Opens the 'About' dialog box.
@@ -1269,11 +1293,11 @@ class App:
         :param widget: Ignored.
         :return: None
         """
+
         about = self.builder.get_object("aboutdialog")
         response = about.run()
         about.destroy()
 
-
     def on_create_mirror(self, widget):
         """
         Creates a mirror image of a Gerber object to be used as a bottom
@@ -1322,6 +1346,7 @@ class App:
         :param widget: Ignored.
         :return: None
         """
+
         # Mirror axis. Same as in on_create_mirror.
         axis = self.get_radio_value({"rb_mirror_x": "X",
                                      "rb_mirror_y": "Y"})
@@ -1445,6 +1470,7 @@ class App:
         :param widget: Ignored.
         :return: None
         """
+
         if self.toggle_units_ignore:
             return
 
@@ -1522,6 +1548,7 @@ class App:
         :param param: Ignored.
         :return: None
         """
+
         def on_success(app_obj, filename):
             app_obj.open_project(filename)
 
@@ -1536,6 +1563,7 @@ class App:
         :param param: Ignored.
         :return: None
         """
+
         if self.project_filename is None:
             self.on_file_saveprojectas(None)
         else:
@@ -1551,6 +1579,7 @@ class App:
         :param param: Ignored.
         :return: None
         """
+
         def on_success(app_obj, filename):
             assert isinstance(app_obj, App)
             app_obj.save_project(filename)
@@ -1569,6 +1598,7 @@ class App:
         :param param: Ignore.
         :return: None
         """
+
         def on_success(app_obj, filename):
             assert isinstance(app_obj, App)
             app_obj.save_project(filename)
@@ -2016,7 +2046,8 @@ class App:
         # Object initialization function for app.new_object()
         def job_init(job_obj, app_obj):
             assert isinstance(job_obj, FlatCAMCNCjob)
-            geometry = app_obj.stuff[app_obj.selected_item_name]
+            #geometry = app_obj.stuff[app_obj.selected_item_name]
+            geometry = app_obj.get_current()
             assert isinstance(geometry, FlatCAMGeometry)
             geometry.read_form()
 
@@ -2027,13 +2058,15 @@ class App:
             job_obj.options["tooldia"] = geometry.options["cnctooldia"]
 
             GLib.idle_add(lambda: app_obj.set_progress_bar(0.4, "Analyzing Geometry..."))
-            job_obj.generate_from_geometry(geometry)
+            # TODO: The tolerance should not be hard coded. Just for testing.
+            job_obj.generate_from_geometry(geometry, tolerance=0.001)
 
             GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Parsing G-Code..."))
             job_obj.gcode_parse()
 
-            GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Creating New Geometry..."))
-            job_obj.create_geometry()
+            # TODO: job_obj.create_geometry creates stuff that is not used.
+            #GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Creating New Geometry..."))
+            #job_obj.create_geometry()
 
             GLib.idle_add(lambda: app_obj.set_progress_bar(0.8, "Plotting..."))
 
@@ -2119,13 +2152,16 @@ class App:
         # Update UI
         self.build_list()  # Update the items list
 
-    def on_replot(self, widget):
+    def on_toolbar_replot(self, widget):
         """
         Callback for toolbar button. Re-plots all objects.
 
         :param widget: The widget from which this was called.
         :return: None
         """
+
+        self.get_current().read_form()
+
         self.plot_all()
 
     def on_clear_plots(self, widget):

+ 9 - 55
FlatCAM.ui

@@ -567,6 +567,7 @@ THE SOFTWARE.</property>
                     <property name="xalign">0</property>
                     <property name="active">True</property>
                     <property name="draw_indicator">True</property>
+                    <signal name="toggled" handler="on_cb_plot_toggled" swapped="no"/>
                   </object>
                   <packing>
                     <property name="expand">False</property>
@@ -575,34 +576,10 @@ THE SOFTWARE.</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkCheckButton" id="cb_cncjob_solid">
-                    <property name="label" translatable="yes">Solid</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="xalign">0</property>
-                    <property name="draw_indicator">True</property>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">True</property>
-                    <property name="position">4</property>
-                  </packing>
+                  <placeholder/>
                 </child>
                 <child>
-                  <object class="GtkCheckButton" id="cb_cncjob_multicolored">
-                    <property name="label" translatable="yes">Multi-colored</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="xalign">0</property>
-                    <property name="draw_indicator">True</property>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">True</property>
-                    <property name="position">5</property>
-                  </packing>
+                  <placeholder/>
                 </child>
                 <child>
                   <object class="GtkBox" id="box9">
@@ -874,6 +851,7 @@ THE SOFTWARE.</property>
                     <property name="xalign">0</property>
                     <property name="active">True</property>
                     <property name="draw_indicator">True</property>
+                    <signal name="toggled" handler="on_cb_plot_toggled" swapped="no"/>
                   </object>
                   <packing>
                     <property name="expand">False</property>
@@ -1301,6 +1279,7 @@ THE SOFTWARE.</property>
                     <property name="xalign">0</property>
                     <property name="active">True</property>
                     <property name="draw_indicator">True</property>
+                    <signal name="toggled" handler="on_cb_plot_toggled" swapped="no"/>
                   </object>
                   <packing>
                     <property name="expand">False</property>
@@ -1836,6 +1815,7 @@ THE SOFTWARE.</property>
                     <property name="xalign">0</property>
                     <property name="active">True</property>
                     <property name="draw_indicator">True</property>
+                    <signal name="toggled" handler="on_cb_plot_toggled" swapped="no"/>
                   </object>
                   <packing>
                     <property name="expand">False</property>
@@ -2813,7 +2793,7 @@ to application defaults.</property>
                 <property name="label" translatable="yes">Re-plot</property>
                 <property name="use_underline">True</property>
                 <property name="stock_id">gtk-redo</property>
-                <signal name="clicked" handler="on_replot" swapped="no"/>
+                <signal name="clicked" handler="on_toolbar_replot" swapped="no"/>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -4078,36 +4058,10 @@ to application defaults.</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <object class="GtkCheckButton" id="cb_app_cncjob_solid">
-                                    <property name="label" translatable="yes">Solid</property>
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="receives_default">False</property>
-                                    <property name="xalign">0</property>
-                                    <property name="draw_indicator">True</property>
-                                    <signal name="toggled" handler="on_options_update" swapped="no"/>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">37</property>
-                                  </packing>
+                                  <placeholder/>
                                 </child>
                                 <child>
-                                  <object class="GtkCheckButton" id="cb_app_cncjob_multicolored">
-                                    <property name="label" translatable="yes">Multi-colored</property>
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="receives_default">False</property>
-                                    <property name="xalign">0</property>
-                                    <property name="draw_indicator">True</property>
-                                    <signal name="toggled" handler="on_options_update" swapped="no"/>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">38</property>
-                                  </packing>
+                                  <placeholder/>
                                 </child>
                                 <child>
                                   <object class="GtkBox" id="box17">

+ 74 - 19
camlib.py

@@ -925,11 +925,13 @@ class CNCjob(Geometry):
         """
         Creates gcode for this object from an Excellon object
         for the specified tools.
-        @param exobj: Excellon object to process
-        @type exobj: Excellon
-        @param tools: Comma separated tool names
-        @type: tools: str
-        @return: None
+
+        :param exobj: Excellon object to process
+        :type exobj: Excellon
+        :param tools: Comma separated tool names
+        :type: tools: str
+        :return: None
+        :rtype: None
         """
         print "Creating CNC Job from Excellon..."
         if tools == "all":
@@ -970,9 +972,21 @@ class CNCjob(Geometry):
 
         self.gcode = gcode
 
-    def generate_from_geometry(self, geometry, append=True, tooldia=None):
+    def generate_from_geometry(self, geometry, append=True, tooldia=None, tolerance=0):
         """
-        Generates G-Code from a Geometry object.
+        Generates G-Code from a Geometry object. Stores in ``self.gcode``.
+
+        :param geometry: Geometry defining the toolpath
+        :type geometry: Geometry
+        :param append: Wether to append to self.gcode or re-write it.
+        :type append: bool
+        :param tooldia: If given, sets the tooldia property but does
+        not affect the process in any other way.
+        :type tooldia: bool
+        :param tolerance: All points in the simplified object will be within the
+        tolerance distance of the original geometry.
+        :return: None
+        :rtype: None
         """
         if tooldia is not None:
             self.tooldia = tooldia
@@ -993,11 +1007,11 @@ class CNCjob(Geometry):
         for geo in geometry.solid_geometry:
             
             if type(geo) == Polygon:
-                self.gcode += self.polygon2gcode(geo)
+                self.gcode += self.polygon2gcode(geo, tolerance=tolerance)
                 continue
             
             if type(geo) == LineString or type(geo) == LinearRing:
-                self.gcode += self.linear2gcode(geo)
+                self.gcode += self.linear2gcode(geo, tolerance=tolerance)
                 continue
             
             if type(geo) == Point:
@@ -1006,7 +1020,7 @@ class CNCjob(Geometry):
 
             if type(geo) == MultiPolygon:
                 for poly in geo:
-                    self.gcode += self.polygon2gcode(poly)
+                    self.gcode += self.polygon2gcode(poly, tolerance=tolerance)
                 continue
 
             print "WARNING: G-code generation not implemented for %s" % (str(type(geo)))
@@ -1017,7 +1031,10 @@ class CNCjob(Geometry):
 
     def pre_parse(self, gtext):
         """
-        gtext is a single string with g-code
+        Separates parts of the G-Code text into a list of dictionaries.
+        Used by ``self.gcode_parse()``.
+
+        :param gtext: A single string with g-code
         """
 
         # Units: G20-inches, G21-mm
@@ -1177,9 +1194,18 @@ class CNCjob(Geometry):
         
     def plot2(self, axes, tooldia=None, dpi=75, margin=0.1,
              color={"T": ["#F0E24D", "#B5AB3A"], "C": ["#5E6CFF", "#4650BD"]},
-             alpha={"T": 0.3, "C": 1.0}):
+             alpha={"T": 0.3, "C": 1.0}, tool_tolerance=0.001):
         """
         Plots the G-code job onto the given axes.
+
+        :param axes: Matplotlib axes on which to plot.
+        :param tooldia: Tool diameter.
+        :param dpi: Not used!
+        :param margin: Not used!
+        :param color: Color specification.
+        :param alpha: Transparency specification.
+        :param tool_tolerance: Tolerance when drawing the toolshape.
+        :return: None
         """
         if tooldia is None:
             tooldia = self.tooldia
@@ -1194,32 +1220,44 @@ class CNCjob(Geometry):
                 axes.plot(x, y, linespec, color=linecolor)
         else:
             for geo in self.gcode_parsed:
-                poly = geo['geom'].buffer(tooldia/2.0)
+                poly = geo['geom'].buffer(tooldia/2.0).simplify(tool_tolerance)
                 patch = PolygonPatch(poly, facecolor=color[geo['kind'][0]][0],
                                      edgecolor=color[geo['kind'][0]][1],
                                      alpha=alpha[geo['kind'][0]], zorder=2)
                 axes.add_patch(patch)
         
     def create_geometry(self):
+        # TODO: This takes forever. Too much data?
         self.solid_geometry = cascaded_union([geo['geom'] for geo in self.gcode_parsed])
 
-    def polygon2gcode(self, polygon):
+    def polygon2gcode(self, polygon, tolerance=0):
         """
         Creates G-Code for the exterior and all interior paths
         of a polygon.
 
         :param polygon: A Shapely.Polygon
         :type polygon: Shapely.Polygon
+        :param tolerance: All points in the simplified object will be within the
+        tolerance distance of the original geometry.
+        :type tolerance: float
+        :return: G-code to cut along polygon.
+        :rtype: str
         """
+
+        if tolerance > 0:
+            target_polygon = polygon.simplify(tolerance)
+        else:
+            target_polygon = polygon
+
         gcode = ""
         t = "G0%d X%.4fY%.4f\n"
-        path = list(polygon.exterior.coords)             # Polygon exterior
+        path = list(target_polygon.exterior.coords)             # Polygon exterior
         gcode += t % (0, path[0][0], path[0][1])  # Move to first point
         gcode += "G01 Z%.4f\n" % self.z_cut       # Start cutting
         for pt in path[1:]:
             gcode += t % (1, pt[0], pt[1])    # Linear motion to point
         gcode += "G00 Z%.4f\n" % self.z_move  # Stop cutting
-        for ints in polygon.interiors:               # Polygon interiors
+        for ints in target_polygon.interiors:               # Polygon interiors
             path = list(ints.coords)
             gcode += t % (0, path[0][0], path[0][1])  # Move to first point
             gcode += "G01 Z%.4f\n" % self.z_cut       # Start cutting
@@ -1228,11 +1266,28 @@ class CNCjob(Geometry):
             gcode += "G00 Z%.4f\n" % self.z_move  # Stop cutting
         return gcode
 
-    def linear2gcode(self, linear):
+    def linear2gcode(self, linear, tolerance=0):
+        """
+        Generates G-code to cut along the linear feature.
+
+        :param linear: The path to cut along.
+        :type: Shapely.LinearRing or Shapely.Linear String
+        :param tolerance: All points in the simplified object will be within the
+        tolerance distance of the original geometry.
+        :type tolerance: float
+        :return: G-code to cut alon the linear feature.
+        :rtype: str
+        """
+
+        if tolerance > 0:
+            target_linear = linear.simplify(tolerance)
+        else:
+            target_linear = linear
+
         gcode = ""
         t = "G0%d X%.4fY%.4f\n"
-        path = list(linear.coords)
-        gcode += t%(0, path[0][0], path[0][1])  # Move to first point
+        path = list(target_linear.coords)
+        gcode += t % (0, path[0][0], path[0][1])  # Move to first point
         gcode += "G01 Z%.4f\n" % self.z_cut       # Start cutting
         for pt in path[1:]:
             gcode += t % (1, pt[0], pt[1])    # Linear motion to point

+ 1 - 1
defaults.json

@@ -1 +1 @@
-{"cncjob_multicolored": false, "geometry_paintoverlap": 0.15, "geometry_plot": true, "cncjob_solid": false, "gerber_isotooldia": 0.016, "gerber_plot": true, "gerber_mergepolys": true, "gerber_cutoutgapsize": 0.15, "geometry_feedrate": 5.0, "units": "IN", "excellon_travelz": 0.1, "gerber_multicolored": false, "gerber_solid": false, "excellon_plot": true, "excellon_feedrate": 5.0, "cncjob_tooldia": 0.016, "geometry_travelz": 0.1, "gerber_cutoutmargin": 0.2, "excellon_solid": false, "geometry_paintmargin": 0.01, "geometry_cutz": -0.002, "gerber_noncoppermargin": 0.0, "gerber_gaps": "4", "excellon_multicolored": false, "geometry_painttooldia": 0.0625, "cncjob_plot": true, "excellon_drillz": -0.1, "gerber_bboxrounded": false, "geometry_multicolored": false, "geometry_cnctooldia": 0.016, "geometry_solid": false, "gerber_bboxmargin": 0.0}
+{"geometry_paintoverlap": 0.15, "geometry_plot": true, "excellon_feedrate": 5.0, "gerber_plot": true, "gerber_mergepolys": true, "excellon_drillz": -0.1, "geometry_feedrate": 5.0, "units": "IN", "excellon_travelz": 0.1, "gerber_multicolored": false, "gerber_solid": true, "excellon_plot": true, "gerber_isotooldia": 0.016, "gerber_bboxmargin": 0.0, "cncjob_tooldia": 0.016, "geometry_travelz": 0.1, "gerber_cutoutmargin": 0.2, "excellon_solid": false, "geometry_paintmargin": 0.01, "geometry_cutz": -0.002, "geometry_cnctooldia": 0.016, "gerber_gaps": "4", "excellon_multicolored": false, "geometry_painttooldia": 0.0625, "cncjob_plot": true, "gerber_cutoutgapsize": 0.15, "gerber_bboxrounded": false, "geometry_multicolored": false, "gerber_noncoppermargin": 0.0, "geometry_solid": false}