Просмотр исходного кода

Merged jpcgt/flatcam into master

Kamil Sopko 10 лет назад
Родитель
Сommit
83c24ec61d
4 измененных файлов с 157 добавлено и 93 удалено
  1. 84 64
      FlatCAMApp.py
  2. 47 24
      FlatCAMObj.py
  3. 21 0
      ObjectCollection.py
  4. 5 5
      camlib.py

+ 84 - 64
FlatCAMApp.py

@@ -2137,7 +2137,7 @@ class App(QtCore.QObject):
 
         def geocutout(name, *args):
             """
-                cut gaps in current geometry
+                subtract gaps from geometry, this will not create new object
 
             :param name:
             :param args:
@@ -2148,6 +2148,14 @@ class App(QtCore.QObject):
                      'gapsize': float,
                      'gaps': str}
 
+            #way gaps wil be rendered:
+            # lr    - left + right
+            # tb    - top + bottom
+            # 4     - left + right +top + bottom
+            # 2lr   - 2*left + 2*right
+            # 2tb   - 2*top + 2*bottom
+            # 8     - 2*left + 2*right +2*top + 2*bottom
+
             for key in kwa:
                 if key not in types:
                     return 'Unknown parameter: %s' % key
@@ -2159,15 +2167,23 @@ class App(QtCore.QObject):
                 return "Could not retrieve object: %s" % name
 
 
-
+            #get min and max data for each object as we just cut rectangles across X or Y
             xmin, ymin, xmax, ymax = obj.bounds()
             px = 0.5 * (xmin + xmax)
             py = 0.5 * (ymin + ymax)
+            lenghtx = (xmax - xmin)
+            lenghty = (ymax - ymin)
             gapsize = kwa['gapsize']+kwa['dia']/2
+            if kwa['gaps'] == '8' or kwa['gaps']=='2lr':
+                subtract_rectangle(name,xmin-gapsize,py-gapsize+lenghty/4,xmax+gapsize,py+gapsize+lenghty/4)
+                subtract_rectangle(name,xmin-gapsize,py-gapsize-lenghty/4,xmax+gapsize,py+gapsize-lenghty/4)
+            if kwa['gaps'] == '8' or kwa['gaps']=='2tb':
+                subtract_rectangle(name,px-gapsize+lenghtx/4,ymin-gapsize,px+gapsize+lenghtx/4,ymax+gapsize)
+                subtract_rectangle(name,px-gapsize-lenghtx/4,ymin-gapsize,px+gapsize-lenghtx/4,ymax+gapsize)
             if kwa['gaps'] == '4' or kwa['gaps']=='lr':
-                del_rectangle(name,xmin-gapsize,py-gapsize,xmax+gapsize,py+gapsize)
+                subtract_rectangle(name,xmin-gapsize,py-gapsize,xmax+gapsize,py+gapsize)
             if kwa['gaps'] == '4' or kwa['gaps']=='tb':
-                del_rectangle(name,px-gapsize,ymin-gapsize,px+gapsize,ymax+gapsize)
+                subtract_rectangle(name,px-gapsize,ymin-gapsize,px+gapsize,ymax+gapsize)
             return 'Ok'
 
         def mirror(name, *args):
@@ -2299,6 +2315,7 @@ class App(QtCore.QObject):
                      'axis': str,
                      'holes': str,
                      'grid': float,
+                     'minoffset': float,
                      'gridoffset': float,
                      'axisoffset': float,
                      'dia': float,
@@ -2359,25 +2376,22 @@ class App(QtCore.QObject):
                     else:
                         axisoffset=0
 
-
+                    #this  will align hole  to given aligngridoffset and minimal offset from pcb, based on selected axis
                     if axis == "X":
-                        firstpoint=-kwa['gridoffset']+xmin
-                        #-5
-                        minlenght=(xmax-xmin+2*kwa['gridoffset'])
-                        #57+10=67
-                        gridstripped=(minlenght//kwa['grid'])*kwa['grid']
-                        #67//10=60
-                        if (minlenght-gridstripped) >kwa['gridoffset']:
-                            gridstripped=gridstripped+kwa['grid']
-                        lastpoint=(firstpoint+gridstripped)
+                        firstpoint=kwa['gridoffset']
+                        while (xmin-kwa['minoffset'])<firstpoint:
+                            firstpoint=firstpoint-kwa['grid']
+                        lastpoint=kwa['gridoffset']
+                        while (xmax+kwa['minoffset'])>lastpoint:
+                            lastpoint=lastpoint+kwa['grid']
                         localHoles=(firstpoint,axisoffset),(lastpoint,axisoffset)
                     else:
-                        firstpoint=-kwa['gridoffset']+ymin
-                        minlenght=(ymax-ymin+2*kwa['gridoffset'])
-                        gridstripped=minlenght//kwa['grid']*kwa['grid']
-                        if (minlenght-gridstripped) >kwa['gridoffset']:
-                            gridstripped=gridstripped+kwa['grid']
-                        lastpoint=(firstpoint+gridstripped)
+                        firstpoint=kwa['gridoffset']
+                        while (ymin-kwa['minoffset'])<firstpoint:
+                            firstpoint=firstpoint-kwa['grid']
+                        lastpoint=kwa['gridoffset']
+                        while (ymax+kwa['minoffset'])>lastpoint:
+                            lastpoint=lastpoint+kwa['grid']
                         localHoles=(axisoffset,firstpoint),(axisoffset,lastpoint)
 
                     for hole in localHoles:
@@ -2456,26 +2470,26 @@ class App(QtCore.QObject):
                 return "ERROR: Only Excellon objects can be drilled."
 
             try:
-              
+
                 # Get the tools from the list
                 job_name = kwa["outname"]
-        
+
                 # Object initialization function for app.new_object()
                 def job_init(job_obj, app_obj):
                     assert isinstance(job_obj, FlatCAMCNCjob), \
                         "Initializer expected FlatCAMCNCjob, got %s" % type(job_obj)
-                    
+
                     job_obj.z_cut = kwa["drillz"]
                     job_obj.z_move = kwa["travelz"]
                     job_obj.feedrate = kwa["feedrate"]
                     job_obj.spindlespeed = kwa["spindlespeed"] if "spindlespeed" in kwa else None
                     toolchange = True if "toolchange" in kwa and kwa["toolchange"] == 1 else False
                     job_obj.generate_from_excellon_by_tool(obj, kwa["tools"], toolchange)
-                    
+
                     job_obj.gcode_parse()
-                    
+
                     job_obj.create_geometry()
-        
+
                 obj.app.new_object("cncjob", job_name, job_init)
 
             except Exception, e:
@@ -2600,7 +2614,7 @@ class App(QtCore.QObject):
             types = {'dia': float,
                      'passes': int,
                      'overlap': float,
-                     'outname': str, 
+                     'outname': str,
                      'combine': int}
 
             for key in kwa:
@@ -2634,7 +2648,9 @@ class App(QtCore.QObject):
                      'feedrate': float,
                      'tooldia': float,
                      'outname': str,
-                     'spindlespeed': int
+                     'spindlespeed': int,
+                     'multidepth' : bool,
+                     'depthperpass' : float
                      }
 
             for key in kwa:
@@ -2718,7 +2734,7 @@ class App(QtCore.QObject):
             return add_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y,
                             topright_x, topright_y, topright_x, botleft_y)
 
-        def del_poly(obj_name, *args):
+        def subtract_poly(obj_name, *args):
             if len(args) % 2 != 0:
                 return "Incomplete coordinate."
 
@@ -2731,19 +2747,13 @@ class App(QtCore.QObject):
             if obj is None:
                 return "Object not found: %s" % obj_name
 
-            def init_obj_me(init_obj, app):
-                assert isinstance(init_obj, FlatCAMGeometry)
-                init_obj.solid_geometry=cascaded_union(diff)
+            obj.subtract_polygon(points)
+            obj.plot()
 
-            diff= obj.del_polygon(points)
-            try:
-                delete(obj_name)
-                obj.app.new_object("geometry", obj_name, init_obj_me)
-            except Exception as e:
-                return "Failed: %s" % str(e)
+            return "OK."
 
-        def del_rectangle(obj_name, botleft_x, botleft_y, topright_x, topright_y):
-            return del_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y,
+        def subtract_rectangle(obj_name, botleft_x, botleft_y, topright_x, topright_y):
+            return subtract_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y,
                             topright_x, topright_y, topright_x, botleft_y)
 
         def add_circle(obj_name, center_x, center_y, radius):
@@ -2764,6 +2774,8 @@ class App(QtCore.QObject):
 
         def delete(obj_name):
             try:
+                #deselect all  to avoid  delete selected object when run  delete  from  shell
+                self.collection.set_all_inactive()
                 self.collection.set_active(str(obj_name))
                 self.on_delete()
             except Exception, e:
@@ -2805,7 +2817,7 @@ class App(QtCore.QObject):
                     objs.append(obj)
 
             def initialize(obj, app):
-                FlatCAMExcellon.merge(objs, obj,True)
+                FlatCAMExcellon.merge(objs, obj)
 
             if objs is not None:
                 self.new_object("excellon", obj_name, initialize)
@@ -2870,14 +2882,14 @@ class App(QtCore.QObject):
                 obj_init.offset([float(currentx), float(currenty)]),
 
             def initialize_local_excellon(obj_init, app):
-                FlatCAMExcellon.merge(obj, obj_init,True)
+                FlatCAMExcellon.merge(obj, obj_init)
                 obj_init.offset([float(currentx), float(currenty)]),
 
             def initialize_geometry(obj_init, app):
                 FlatCAMGeometry.merge(objs, obj_init)
 
             def initialize_excellon(obj_init, app):
-                FlatCAMExcellon.merge(objs, obj_init,True)
+                FlatCAMExcellon.merge(objs, obj_init)
 
             objs=[]
             if obj is not None:
@@ -2899,7 +2911,8 @@ class App(QtCore.QObject):
                 else:
                     self.new_object("geometry", outname, initialize_geometry)
 
-
+                #deselect all  to avoid  delete selected object when run  delete  from  shell
+                self.collection.set_all_inactive()
                 for delobj in objs:
                     self.collection.set_active(delobj.options['name'])
                     self.on_delete()
@@ -3091,8 +3104,8 @@ class App(QtCore.QObject):
             },
             'geocutout': {
                 'fcn': geocutout,
-                'help': "Cut holding gaps closed geometry.\n" +
-                        "> geocutout <name> [-dia <3.0 (float)>] [-margin <0.0 (float)>] [-gapsize <0.5 (float)>] [-gaps <lr (4|tb|lr)>]\n" +
+                'help': "Cut holding gaps from geometry.\n" +
+                        "> geocutout <name> [-dia <3.0 (float)>] [-margin <0.0 (float)>] [-gapsize <0.5 (float)>] [-gaps <lr (8|4|tb|lr|2tb|2lr)>]\n" +
                         "   name: Name of the geometry object\n" +
                         "   dia: Tool diameter\n" +
                         "   margin: Margin over bounds\n" +
@@ -3138,12 +3151,13 @@ class App(QtCore.QObject):
             'aligndrill': {
                 'fcn': aligndrill,
                 'help': "Create excellon with drills for aligment.\n" +
-                        "> aligndrill <name> [-dia <3.0 (float)>] -axis <X|Y> [-box <nameOfBox> [-grid <10 (float)> -gridoffset <5 (float)> [-axisoffset <0 (float)>]] | -dist <number>]\n" +
+                        "> aligndrill <name> [-dia <3.0 (float)>] -axis <X|Y> [-box <nameOfBox> -minoffset <float> [-grid <10 (float)> -gridoffset <5 (float)> [-axisoffset <0 (float)>]] | -dist <number>]\n" +
                         "   name: Name of the object (Gerber or Excellon) to mirror.\n" +
                         "   dia: Tool diameter\n" +
                         "   box: Name of object which act as box (cutout for example.)\n" +
                         "   grid: aligning  to grid, for thouse, who have aligning pins inside table in grid (-5,0),(5,0),(15,0)..." +
-                        "   gridoffset: offset from pcb from 0 position and   minimal offset to grid on max" +
+                        "   gridoffset: offset of grid from 0 position" +
+                        "   minoffset: min and max distance between align hole and pcb" +
                         "   axisoffset: offset on second axis before aligment holes" +
                         "   axis: Mirror axis parallel to the X or Y axis.\n" +
                         "   dist: Distance of the mirror axis to the X or Y axis."
@@ -3207,13 +3221,15 @@ class App(QtCore.QObject):
             'cncjob': {
                 'fcn': cncjob,
                 'help': 'Generates a CNC Job from a Geometry Object.\n' +
-                        '> cncjob <name> [-z_cut <c>] [-z_move <m>] [-feedrate <f>] [-tooldia <t>] [-spindlespeed (int)] [-outname <n>]\n' +
+                        '> cncjob <name> [-z_cut <c>] [-z_move <float>] [-feedrate <float>] [-tooldia <float>] [-spindlespeed <int>] [-multidepth <bool>] [-depthperpass <float>] [-outname <str>]\n' +
                         '   name: Name of the source object\n' +
                         '   z_cut: Z-axis cutting position\n' +
                         '   z_move: Z-axis moving position\n' +
                         '   feedrate: Moving speed when cutting\n' +
                         '   tooldia: Tool diameter to show on screen\n' +
                         '   spindlespeed: Speed of the spindle in rpm (example: 4000)\n' +
+                        '   multidepth: Use or not multidepth cnccut\n'+
+                        '   depthperpass: Height of one layer for multidepth\n'+
                         '   outname: Name of the output object'
             },
             'write_gcode': {
@@ -3245,11 +3261,11 @@ class App(QtCore.QObject):
                         '   name: Name of the geometry object to which to append the polygon.\n' +
                         '   xi, yi: Coordinates of points in the polygon.'
             },
-            'del_poly': {
-                'fcn': del_poly,
-                'help': ' - Remove a polygon from the given Geometry object.\n' +
-                        '> del_poly <name> <x0> <y0> <x1> <y1> <x2> <y2> [x3 y3 [...]]\n' +
-                        '   name: Name of the geometry object to which to remove the polygon.\n' +
+            'subtract_poly': {
+                'fcn': subtract_poly,
+                'help': 'Subtract polygon from the given Geometry object.\n' +
+                        '> subtract_poly <name> <x0> <y0> <x1> <y1> <x2> <y2> [x3 y3 [...]]\n' +
+                        '   name: Name of the geometry object, which will be  sutracted.\n' +
                         '   xi, yi: Coordinates of points in the polygon.'
             },
             'delete': {
@@ -3295,11 +3311,11 @@ class App(QtCore.QObject):
                         "   rows: number of rows\n"+
                         "   outname: Name of the new geometry object."
             },
-            'del_rect': {
-                'fcn': del_rectangle,
-                'help': 'Delete a rectange from the given Geometry object.\n' +
-                        '> del_rect <name> <botleft_x> <botleft_y> <topright_x> <topright_y>\n' +
-                        '   name: Name of the geometry object to which to remove the rectangle.\n' +
+            'subtract_rect': {
+                'fcn': subtract_rectangle,
+                'help': 'Subtract rectange from the given Geometry object.\n' +
+                        '> subtract_rect <name> <botleft_x> <botleft_y> <topright_x> <topright_y>\n' +
+                        '   name: Name of the geometry object, which will be subtracted.\n' +
                         '   botleft_x, botleft_y: Coordinates of the bottom left corner.\n' +
                         '   topright_x, topright_y Coordinates of the top right corner.'
             },
@@ -3412,13 +3428,17 @@ class App(QtCore.QObject):
         for recent in self.recent:
             filename = recent['filename'].split('/')[-1].split('\\')[-1]
 
-            action = QtGui.QAction(QtGui.QIcon(icons[recent["kind"]]), filename, self)
+            try:
+                action = QtGui.QAction(QtGui.QIcon(icons[recent["kind"]]), filename, self)
+
+                # Attach callback
+                o = make_callback(openers[recent["kind"]], recent['filename'])
+                action.triggered.connect(o)
 
-            # Attach callback
-            o = make_callback(openers[recent["kind"]], recent['filename'])
-            action.triggered.connect(o)
+                self.ui.recent.addAction(action)
 
-            self.ui.recent.addAction(action)
+            except KeyError:
+                App.log.error("Unsupported file type: %s" % recent["kind"])
 
         # self.builder.get_object('open_recent').set_submenu(recent_menu)
         # self.ui.menufilerecent.set_submenu(recent_menu)

+ 47 - 24
FlatCAMObj.py

@@ -123,8 +123,12 @@ class FlatCAMObj(QtCore.QObject):
 
         :return: None
         """
+        FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> FlatCAMObj.to_form()")
         for option in self.options:
-            self.set_form_item(option)
+            try:
+                self.set_form_item(option)
+            except:
+                self.app.log.warning("Unexpected error:", sys.exc_info())
 
     def read_form(self):
         """
@@ -135,7 +139,10 @@ class FlatCAMObj(QtCore.QObject):
         """
         FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> FlatCAMObj.read_form()")
         for option in self.options:
-            self.read_form_item(option)
+            try:
+                self.read_form_item(option)
+            except:
+                self.app.log.warning("Unexpected error:", sys.exc_info())
 
     def build_ui(self):
         """
@@ -197,6 +204,17 @@ class FlatCAMObj(QtCore.QObject):
         except KeyError:
             self.app.log.warning("Failed to read option from field: %s" % option)
 
+        # #try read field only when option have equivalent in form_fields
+        # if option in self.form_fields:
+        #     option_type=type(self.options[option])
+        #     try:
+        #         value=self.form_fields[option].get_value()
+        #     #catch per option as it was ignored anyway, also when syntax error (probably uninitialized field),don't read either.
+        #     except (KeyError,SyntaxError):
+        #         self.app.log.warning("Failed to read option from field: %s" % option)
+        # else:
+        #     self.app.log.warning("Form fied does not exists: %s" % option)
+
     def plot(self):
         """
         Plot this object (Extend this method to implement the actual plotting).
@@ -631,13 +649,16 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         self.ser_attrs += ['options', 'kind']
 
     @staticmethod
-    def merge(exc_list, exc_final, copy_options):
+    def merge(exc_list, exc_final):
         """
-        Merges(copy if used on one) the excellon of objects in exc_list into
-        options  have same like exc_final
-        the geometry of geo_final.
+        Merge excellons in exc_list into exc_final.
+        Options are allways copied from source .
+
+        Tools are also merged, if  name for  tool is same and   size differs, then as name is used next available  number from both lists
+
+        if only one object is  specified in exc_list then this acts  as copy only
 
-        :param exc_list: List of FlatCAMExcellon Objects to join.
+        :param exc_list: List or one object of FlatCAMExcellon Objects to join.
         :param exc_final: Destination FlatCAMExcellon object.
         :return: None
         """
@@ -648,26 +669,27 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         else:
             exc_list_real=exc_list
 
-
         for exc in exc_list_real:
             # Expand lists
             if type(exc) is list:
-                FlatCAMExcellon.merge(exc, exc_final, copy_options)
-
-            # If not list, just append
+                FlatCAMExcellon.merge(exc, exc_final)
+            # If not list, merge excellons
             else:
-                if copy_options is True:
-                    exc_final.options["plot"]=exc.options["plot"]
-                    exc_final.options["solid"]=exc.options["solid"]
-                    exc_final.options["drillz"]=exc.options["drillz"]
-                    exc_final.options["travelz"]=exc.options["travelz"]
-                    exc_final.options["feedrate"]=exc.options["feedrate"]
-                    exc_final.options["tooldia"]=exc.options["tooldia"]
-                    exc_final.options["toolchange"]=exc.options["toolchange"]
-                    exc_final.options["toolchangez"]=exc.options["toolchangez"]
-                    exc_final.options["spindlespeed"]=exc.options["spindlespeed"]
-
 
+                #    TODO: I realize forms does not save values into options , when  object is deselected
+                #    leave this  here for future use
+                #    this  reinitialize options based on forms, all steps may not be necessary
+                #    exc.app.collection.set_active(exc.options['name'])
+                #    exc.to_form()
+                #    exc.read_form()
+                for option in exc.options:
+                    if option is not 'name':
+                        try:
+                            exc_final.options[option] = exc.options[option]
+                        except:
+                            exc.app.log.warning("Failed to copy option.",option)
+
+                #deep copy of all drills,to avoid any references
                 for drill in exc.drills:
                     point = Point(drill['point'].x,drill['point'].y)
                     exc_final.drills.append({"point": point, "tool": drill['tool']})
@@ -679,7 +701,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                         max_numeric_tool=numeric_tool
                     toolsrework[exc.tools[toolname]['C']]=toolname
 
-                #final as last  becouse  names from final tools  will be used
+                #exc_final as last because names from final tools will be used
                 for toolname in exc_final.tools.iterkeys():
                     numeric_tool=int(toolname)
                     if numeric_tool>max_numeric_tool:
@@ -692,9 +714,10 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                             exc_final.tools[str(max_numeric_tool+1)]={"C": toolvalues}
                     else:
                         exc_final.tools[toolsrework[toolvalues]]={"C": toolvalues}
+                #this value  was not co
+                exc_final.zeros=exc.zeros
                 exc_final.create_geometry()
 
-
     def build_ui(self):
         FlatCAMObj.build_ui(self)
 

+ 21 - 0
ObjectCollection.py

@@ -244,6 +244,27 @@ class ObjectCollection(QtCore.QAbstractListModel):
         iobj = self.createIndex(self.get_names().index(name), 0)  # Column 0
         self.view.selectionModel().select(iobj, QtGui.QItemSelectionModel.Select)
 
+    def set_inactive(self, name):
+        """
+        Unselect object by name from the project list. This triggers the
+        list_selection_changed event and call on_list_selection_changed.
+
+        :param name: Name of the FlatCAM Object
+        :return: None
+        """
+        iobj = self.createIndex(self.get_names().index(name), 0)  # Column 0
+        self.view.selectionModel().select(iobj, QtGui.QItemSelectionModel.Deselect)
+
+    def set_all_inactive(self):
+        """
+        Unselect all objects from the project list. This triggers the
+        list_selection_changed event and call on_list_selection_changed.
+
+        :return: None
+        """
+        for name in self.get_names():
+            self.set_inactive(name)
+
     def on_list_selection_change(self, current, previous):
         FlatCAMApp.App.log.debug("on_list_selection_change()")
         FlatCAMApp.App.log.debug("Current: %s, Previous %s" % (str(current), str(previous)))

+ 5 - 5
camlib.py

@@ -136,17 +136,17 @@ class Geometry(object):
             log.error("Failed to run union on polygons.")
             raise
 
-    def del_polygon(self, points):
+    def subtract_polygon(self, points):
         """
-        Delete a polygon from the object
+        Subtract polygon from the given object. This only operates on the paths in the original geometry, i.e. it converts polygons into paths.
 
         :param points: The vertices of the polygon.
-        :return: None
+        :return: none
         """
         if self.solid_geometry is None:
             self.solid_geometry = []
 
-
+        #pathonly should be allways True, otherwise polygons are not subtracted
         flat_geometry = self.flatten(pathonly=True)
         log.debug("%d paths" % len(flat_geometry))
         polygon=Polygon(points)
@@ -157,7 +157,7 @@ class Geometry(object):
                 diffs.append(target.difference(toolgeo))
             else:
                 log.warning("Not implemented.")
-        return cascaded_union(diffs)
+        self.solid_geometry=cascaded_union(diffs)
 
     def bounds(self):
         """