Browse Source

implement basic set of tests for tcl_shell, need to be completed

Kamil Sopko 10 years ago
parent
commit
fd1c8afef9

+ 218 - 186
FlatCAMApp.py

@@ -675,8 +675,19 @@ class App(QtCore.QObject):
         """
         Handles input from the shell. See FlatCAMApp.setup_shell for shell commands.
 
+        :param text:
+        :return: output if there was any
+        """
+
+        return self.exec_command_test(self, text, False)
+
+    def exec_command_test(self, text, reraise=True):
+        """
+        Handles input from the shell. See FlatCAMApp.setup_shell for shell commands.
+
         :param text: Input command
-        :return: None
+        :param reraise: raise exception and not hide it, used mainly in unittests
+        :return: output if there was any
         """
 
         self.report_usage('exec_command')
@@ -691,8 +702,10 @@ class App(QtCore.QObject):
             result = self.tcl.eval("set errorInfo")
             self.log.error("Exec command Exception: %s" % (result + '\n'))
             self.shell.append_error('ERROR: ' + result + '\n')
-            #show error in console and just return
-        return
+            #show error in console and just return or in test raise exception
+            if reraise:
+                raise e
+        return result
 
         """
         Code below is unsused. Saved for later.
@@ -2167,85 +2180,96 @@ class App(QtCore.QObject):
             return 'Ok'
 
 
-        def geocutout(name, *args):
-            """
+        def geocutout(name=None, *args):
+            '''
+            TCL shell command - see help section
+
             Subtract gaps from geometry, this will not create new object
 
-            :param name:
-            :param args:
-            :return:
-            """
-            a, kwa = h(*args)
-            types = {'dia': float,
-                     'gapsize': float,
-                     'gaps': str}
+            :param name: name of object
+            :param args: array of arguments
+            :return: "Ok" if completed without errors
+            '''
 
-            # How 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
+            try:
+                a, kwa = h(*args)
+                types = {'dia': float,
+                         'gapsize': float,
+                         'gaps': str}
+
+                # How 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
-                kwa[key] = types[key](kwa[key])
+                if name is None:
+                    self.raiseTclError('Argument name is missing.')
 
-            try:
-                obj = self.collection.get_by_name(str(name))
-            except:
-                return "Could not retrieve object: %s" % name
+                for key in kwa:
+                    if key not in types:
+                        self.raiseTclError('Unknown parameter: %s' % key)
+                    try:
+                        kwa[key] = types[key](kwa[key])
+                    except Exception, e:
+                        self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, str(types[key])))
 
-            # 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':
-                subtract_rectangle(name,
-                                   xmin - gapsize,
-                                   py - gapsize,
-                                   xmax + gapsize,
-                                   py + gapsize)
-                
-            if kwa['gaps'] == '4' or kwa['gaps']=='tb':
-                subtract_rectangle(name,
-                                   px - gapsize,
-                                   ymin - gapsize,
-                                   px + gapsize,
-                                   ymax + gapsize)
-                
-            return 'Ok'
+                try:
+                    obj = self.collection.get_by_name(str(name))
+                except:
+                    self.raiseTclError("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':
+                    subtract_rectangle(name,
+                                       xmin - gapsize,
+                                       py - gapsize,
+                                       xmax + gapsize,
+                                       py + gapsize)
+
+                if kwa['gaps'] == '4' or kwa['gaps']=='tb':
+                    subtract_rectangle(name,
+                                       px - gapsize,
+                                       ymin - gapsize,
+                                       px + gapsize,
+                                       ymax + gapsize)
+
+            except Exception as unknown:
+                self.raiseTclUnknownError(unknown)
 
         def mirror(name, *args):
             a, kwa = h(*args)
@@ -2519,59 +2543,63 @@ class App(QtCore.QObject):
             :param args: array of arguments
             :return: "Ok" if completed without errors
             '''
-            a, kwa = h(*args)
-            types = {'tools': str,
-                     'outname': str,
-                     'drillz': float,
-                     'travelz': float,
-                     'feedrate': float,
-                     'spindlespeed': int,
-                     'toolchange': int
-                     }
 
-            if name is None:
-                self.raiseTclError('Argument name is missing.')
+            try:
+                a, kwa = h(*args)
+                types = {'tools': str,
+                         'outname': str,
+                         'drillz': float,
+                         'travelz': float,
+                         'feedrate': float,
+                         'spindlespeed': int,
+                         'toolchange': int
+                         }
+
+                if name is None:
+                    self.raiseTclError('Argument name is missing.')
+
+                for key in kwa:
+                    if key not in types:
+                        self.raiseTclError('Unknown parameter: %s' % key)
+                    try:
+                        kwa[key] = types[key](kwa[key])
+                    except Exception, e:
+                        self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, str(types[key])))
 
-            for key in kwa:
-                if key not in types:
-                    self.raiseTclError('Unknown parameter: %s' % key)
                 try:
-                    kwa[key] = types[key](kwa[key])
-                except Exception, e:
-                    self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, str(types[key])))
+                    obj = self.collection.get_by_name(str(name))
+                except:
+                    self.raiseTclError("Could not retrieve object: %s" % name)
 
-            try:
-                obj = self.collection.get_by_name(str(name))
-            except:
-                self.raiseTclError("Could not retrieve object: %s" % name)
+                if obj is None:
+                    self.raiseTclError('Object not found: %s' % name)
 
-            if obj is None:
-                self.raiseTclError('Object not found: %s' % name)
+                if not isinstance(obj, FlatCAMExcellon):
+                    self.raiseTclError('Only Excellon objects can be drilled, got %s %s.' % (name,  type(obj)))
 
-            if not isinstance(obj, FlatCAMExcellon):
-                self.raiseTclError('Only Excellon objects can be drilled, got %s %s.' % (name,  type(obj)))
+                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):
+                        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)
 
-            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):
-                    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:
+                    self.raiseTclError("Operation failed: %s" % str(e))
 
-            except Exception, e:
-                self.raiseTclError("Operation failed: %s" % str(e))
+            except Exception as unknown:
+                self.raiseTclUnknownError(unknown)
 
-            return 'Ok'
 
         def millholes(name=None, *args):
             '''
@@ -2580,48 +2608,51 @@ class App(QtCore.QObject):
             :param args: array of arguments
             :return: "Ok" if completed without errors
             '''
-            a, kwa = h(*args)
-            types = {'tooldia': float,
-                     'tools': str,
-                     'outname': str}
 
-            if name is None:
-                self.raiseTclError('Argument name is missing.')
+            try:
+                a, kwa = h(*args)
+                types = {'tooldia': float,
+                         'tools': str,
+                         'outname': str}
 
-            for key in kwa:
-                if key not in types:
-                    self.raiseTclError('Unknown parameter: %s' % key)
-                try:
-                    kwa[key] = types[key](kwa[key])
-                except Exception, e:
-                    self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, types[key]))
+                if name is None:
+                    self.raiseTclError('Argument name is missing.')
 
-            try:
-                if 'tools' in kwa:
-                    kwa['tools'] = [x.strip() for x in kwa['tools'].split(",")]
-            except Exception as e:
-                self.raiseTclError("Bad tools: %s" % str(e))
+                for key in kwa:
+                    if key not in types:
+                        self.raiseTclError('Unknown parameter: %s' % key)
+                    try:
+                        kwa[key] = types[key](kwa[key])
+                    except Exception, e:
+                        self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, types[key]))
 
-            try:
-                obj = self.collection.get_by_name(str(name))
-            except:
-                self.raiseTclError("Could not retrieve object: %s" % name)
+                try:
+                    if 'tools' in kwa:
+                        kwa['tools'] = [x.strip() for x in kwa['tools'].split(",")]
+                except Exception as e:
+                    self.raiseTclError("Bad tools: %s" % str(e))
 
-            if obj is None:
-                self.raiseTclError("Object not found: %s" % name)
+                try:
+                    obj = self.collection.get_by_name(str(name))
+                except:
+                    self.raiseTclError("Could not retrieve object: %s" % name)
 
-            if not isinstance(obj, FlatCAMExcellon):
-                self.raiseTclError('Only Excellon objects can be mill drilled, got %s %s.' % (name,  type(obj)))
+                if obj is None:
+                    self.raiseTclError("Object not found: %s" % name)
 
-            try:
-                success, msg = obj.generate_milling(**kwa)
-            except Exception as e:
-                self.raiseTclError("Operation failed: %s" % str(e))
+                if not isinstance(obj, FlatCAMExcellon):
+                    self.raiseTclError('Only Excellon objects can be mill drilled, got %s %s.' % (name,  type(obj)))
 
-            if not success:
-                self.raiseTclError(msg)
+                try:
+                    success, msg = obj.generate_milling(**kwa)
+                except Exception as e:
+                    self.raiseTclError("Operation failed: %s" % str(e))
 
-            return 'Ok'
+                if not success:
+                    self.raiseTclError(msg)
+
+            except Exception as unknown:
+                self.raiseTclUnknownError(unknown)
 
         def exteriors(name=None, *args):
             '''
@@ -2630,46 +2661,49 @@ class App(QtCore.QObject):
             :param args: array of arguments
             :return: "Ok" if completed without errors
             '''
-            a, kwa = h(*args)
-            types = {'outname': str}
 
-            if name is None:
-                self.raiseTclError('Argument name is missing.')
+            try:
+                a, kwa = h(*args)
+                types = {'outname': str}
 
-            for key in kwa:
-                if key not in types:
-                    self.raiseTclError('Unknown parameter: %s' % key)
-                try:
-                    kwa[key] = types[key](kwa[key])
-                except Exception, e:
-                    self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, types[key]))
+                if name is None:
+                    self.raiseTclError('Argument name is missing.')
 
-            try:
-                obj = self.collection.get_by_name(str(name))
-            except:
-                self.raiseTclError("Could not retrieve object: %s" % name)
+                for key in kwa:
+                    if key not in types:
+                        self.raiseTclError('Unknown parameter: %s' % key)
+                    try:
+                        kwa[key] = types[key](kwa[key])
+                    except Exception, e:
+                        self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, types[key]))
 
-            if obj is None:
-                self.raiseTclError("Object not found: %s" % name)
+                try:
+                    obj = self.collection.get_by_name(str(name))
+                except:
+                    self.raiseTclError("Could not retrieve object: %s" % name)
 
-            if not isinstance(obj, Geometry):
-                self.raiseTclError('Expected Geometry, got %s %s.' % (name,  type(obj)))
+                if obj is None:
+                    self.raiseTclError("Object not found: %s" % name)
 
-            def geo_init(geo_obj, app_obj):
-                geo_obj.solid_geometry = obj_exteriors
+                if not isinstance(obj, Geometry):
+                    self.raiseTclError('Expected Geometry, got %s %s.' % (name,  type(obj)))
 
-            if 'outname' in kwa:
-                outname = kwa['outname']
-            else:
-                outname = name + ".exteriors"
+                def geo_init(geo_obj, app_obj):
+                    geo_obj.solid_geometry = obj_exteriors
 
-            try:
-                obj_exteriors = obj.get_exteriors()
-                self.new_object('geometry', outname, geo_init)
-            except Exception as e:
-                self.raiseTclError("Failed: %s" % str(e))
+                if 'outname' in kwa:
+                    outname = kwa['outname']
+                else:
+                    outname = name + ".exteriors"
 
-            return 'Ok'
+                try:
+                    obj_exteriors = obj.get_exteriors()
+                    self.new_object('geometry', outname, geo_init)
+                except Exception as e:
+                    self.raiseTclError("Failed: %s" % str(e))
+
+            except Exception as unknown:
+                self.raiseTclUnknownError(unknown)
 
         def interiors(name=None, *args):
             '''
@@ -2722,8 +2756,6 @@ class App(QtCore.QObject):
             except Exception as unknown:
                 self.raiseTclUnknownError(unknown)
 
-            return 'Ok'
-
         def isolate(name=None, *args):
             '''
             TCL shell command - see help section

+ 26 - 0
tests/gerber_files/detector_contour.gbr

@@ -0,0 +1,26 @@
+G04 MADE WITH FRITZING*
+G04 WWW.FRITZING.ORG*
+G04 DOUBLE SIDED*
+G04 HOLES PLATED*
+G04 CONTOUR ON CENTER OF CONTOUR VECTOR*
+%ASAXBY*%
+%FSLAX23Y23*%
+%MOIN*%
+%OFA0B0*%
+%SFA1.0B1.0*%
+%ADD10R,1.771650X1.181100*%
+%ADD11C,0.008000*%
+%ADD10C,0.008*%
+%LNCONTOUR*%
+G90*
+G70*
+G54D10*
+G54D11*
+X4Y1177D02*
+X1768Y1177D01*
+X1768Y4D01*
+X4Y4D01*
+X4Y1177D01*
+D02*
+G04 End of contour*
+M02*

+ 2146 - 0
tests/gerber_files/detector_copper_bottom.gbr

@@ -0,0 +1,2146 @@
+G04 MADE WITH FRITZING*
+G04 WWW.FRITZING.ORG*
+G04 DOUBLE SIDED*
+G04 HOLES PLATED*
+G04 CONTOUR ON CENTER OF CONTOUR VECTOR*
+%ASAXBY*%
+%FSLAX23Y23*%
+%MOIN*%
+%OFA0B0*%
+%SFA1.0B1.0*%
+%ADD10C,0.075000*%
+%ADD11C,0.099055*%
+%ADD12C,0.078740*%
+%ADD13R,0.075000X0.075000*%
+%ADD14C,0.048000*%
+%ADD15C,0.020000*%
+%ADD16R,0.001000X0.001000*%
+%LNCOPPER0*%
+G90*
+G70*
+G54D10*
+X1149Y872D03*
+X1349Y872D03*
+X749Y722D03*
+X749Y522D03*
+X1149Y522D03*
+X1449Y522D03*
+X1149Y422D03*
+X1449Y422D03*
+X1149Y322D03*
+X1449Y322D03*
+X1149Y222D03*
+X1449Y222D03*
+X949Y472D03*
+X949Y72D03*
+G54D11*
+X749Y972D03*
+X599Y972D03*
+X349Y322D03*
+X349Y472D03*
+X349Y672D03*
+X349Y822D03*
+G54D10*
+X699Y122D03*
+X699Y322D03*
+G54D12*
+X699Y222D03*
+X949Y972D03*
+X749Y622D03*
+X1049Y222D03*
+X1249Y872D03*
+G54D13*
+X1149Y872D03*
+X1149Y522D03*
+G54D14*
+X949Y373D02*
+X949Y433D01*
+D02*
+X999Y323D02*
+X949Y373D01*
+D02*
+X1109Y322D02*
+X999Y323D01*
+D02*
+X499Y873D02*
+X1109Y872D01*
+D02*
+X1299Y73D02*
+X989Y72D01*
+D02*
+X1399Y322D02*
+X1349Y272D01*
+D02*
+X1349Y272D02*
+X1349Y122D01*
+D02*
+X1349Y122D02*
+X1299Y73D01*
+D02*
+X1409Y322D02*
+X1399Y322D01*
+D02*
+X909Y72D02*
+X749Y73D01*
+D02*
+X749Y73D02*
+X727Y94D01*
+D02*
+X649Y522D02*
+X709Y522D01*
+D02*
+X599Y473D02*
+X649Y522D01*
+D02*
+X401Y472D02*
+X599Y473D01*
+D02*
+X789Y522D02*
+X899Y522D01*
+D02*
+X709Y722D02*
+X599Y722D01*
+D02*
+X599Y722D02*
+X549Y673D01*
+D02*
+X549Y673D02*
+X401Y672D01*
+D02*
+X1149Y562D02*
+X1149Y833D01*
+D02*
+X499Y972D02*
+X499Y873D01*
+D02*
+X547Y972D02*
+X499Y972D01*
+D02*
+X699Y283D02*
+X699Y260D01*
+D02*
+X749Y562D02*
+X749Y584D01*
+D02*
+X499Y873D02*
+X499Y972D01*
+D02*
+X499Y972D02*
+X547Y972D01*
+D02*
+X401Y823D02*
+X449Y823D01*
+D02*
+X899Y522D02*
+X921Y500D01*
+D02*
+X1309Y872D02*
+X1287Y872D01*
+D02*
+X449Y823D02*
+X499Y873D01*
+D02*
+X1349Y422D02*
+X1349Y833D01*
+D02*
+X1189Y422D02*
+X1349Y422D01*
+D02*
+X1399Y322D02*
+X1409Y322D01*
+D02*
+X1349Y372D02*
+X1399Y322D01*
+D02*
+X1349Y422D02*
+X1349Y372D01*
+D02*
+X1189Y422D02*
+X1349Y422D01*
+D02*
+X801Y972D02*
+X911Y972D01*
+D02*
+X1109Y222D02*
+X1087Y222D01*
+D02*
+X401Y322D02*
+X659Y322D01*
+D02*
+X1399Y972D02*
+X987Y972D01*
+D02*
+X1449Y923D02*
+X1399Y972D01*
+D02*
+X1449Y562D02*
+X1449Y923D01*
+G54D15*
+X776Y695D02*
+X721Y695D01*
+X721Y750D01*
+X776Y750D01*
+X776Y695D01*
+D02*
+X671Y150D02*
+X726Y150D01*
+X726Y95D01*
+X671Y95D01*
+X671Y150D01*
+D02*
+G54D16*
+X766Y1112D02*
+X769Y1112D01*
+X764Y1111D02*
+X771Y1111D01*
+X763Y1110D02*
+X772Y1110D01*
+X762Y1109D02*
+X772Y1109D01*
+X762Y1108D02*
+X773Y1108D01*
+X762Y1107D02*
+X773Y1107D01*
+X762Y1106D02*
+X773Y1106D01*
+X762Y1105D02*
+X773Y1105D01*
+X762Y1104D02*
+X773Y1104D01*
+X762Y1103D02*
+X773Y1103D01*
+X762Y1102D02*
+X773Y1102D01*
+X762Y1101D02*
+X773Y1101D01*
+X762Y1100D02*
+X773Y1100D01*
+X762Y1099D02*
+X773Y1099D01*
+X762Y1098D02*
+X773Y1098D01*
+X762Y1097D02*
+X773Y1097D01*
+X762Y1096D02*
+X773Y1096D01*
+X762Y1095D02*
+X773Y1095D01*
+X762Y1094D02*
+X773Y1094D01*
+X762Y1093D02*
+X773Y1093D01*
+X762Y1092D02*
+X773Y1092D01*
+X762Y1091D02*
+X773Y1091D01*
+X762Y1090D02*
+X773Y1090D01*
+X762Y1089D02*
+X773Y1089D01*
+X566Y1088D02*
+X618Y1088D01*
+X741Y1088D02*
+X793Y1088D01*
+X565Y1087D02*
+X620Y1087D01*
+X740Y1087D02*
+X795Y1087D01*
+X564Y1086D02*
+X621Y1086D01*
+X739Y1086D02*
+X796Y1086D01*
+X563Y1085D02*
+X621Y1085D01*
+X738Y1085D02*
+X796Y1085D01*
+X563Y1084D02*
+X622Y1084D01*
+X738Y1084D02*
+X796Y1084D01*
+X563Y1083D02*
+X622Y1083D01*
+X738Y1083D02*
+X796Y1083D01*
+X563Y1082D02*
+X622Y1082D01*
+X738Y1082D02*
+X796Y1082D01*
+X563Y1081D02*
+X622Y1081D01*
+X738Y1081D02*
+X796Y1081D01*
+X563Y1080D02*
+X622Y1080D01*
+X738Y1080D02*
+X796Y1080D01*
+X563Y1079D02*
+X622Y1079D01*
+X739Y1079D02*
+X795Y1079D01*
+X563Y1078D02*
+X622Y1078D01*
+X739Y1078D02*
+X795Y1078D01*
+X563Y1077D02*
+X622Y1077D01*
+X741Y1077D02*
+X794Y1077D01*
+X563Y1076D02*
+X622Y1076D01*
+X762Y1076D02*
+X773Y1076D01*
+X563Y1075D02*
+X621Y1075D01*
+X762Y1075D02*
+X773Y1075D01*
+X563Y1074D02*
+X621Y1074D01*
+X762Y1074D02*
+X773Y1074D01*
+X564Y1073D02*
+X620Y1073D01*
+X762Y1073D02*
+X773Y1073D01*
+X565Y1072D02*
+X619Y1072D01*
+X762Y1072D02*
+X773Y1072D01*
+X569Y1071D02*
+X615Y1071D01*
+X762Y1071D02*
+X773Y1071D01*
+X762Y1070D02*
+X773Y1070D01*
+X762Y1069D02*
+X773Y1069D01*
+X762Y1068D02*
+X773Y1068D01*
+X762Y1067D02*
+X773Y1067D01*
+X762Y1066D02*
+X773Y1066D01*
+X762Y1065D02*
+X773Y1065D01*
+X762Y1064D02*
+X773Y1064D01*
+X762Y1063D02*
+X773Y1063D01*
+X762Y1062D02*
+X773Y1062D01*
+X762Y1061D02*
+X773Y1061D01*
+X762Y1060D02*
+X773Y1060D01*
+X762Y1059D02*
+X773Y1059D01*
+X762Y1058D02*
+X773Y1058D01*
+X762Y1057D02*
+X773Y1057D01*
+X762Y1056D02*
+X773Y1056D01*
+X763Y1055D02*
+X772Y1055D01*
+X763Y1054D02*
+X771Y1054D01*
+X765Y1053D02*
+X770Y1053D01*
+X1661Y878D02*
+X1697Y878D01*
+X1658Y877D02*
+X1698Y877D01*
+X1656Y876D02*
+X1700Y876D01*
+X1653Y875D02*
+X1701Y875D01*
+X1651Y874D02*
+X1701Y874D01*
+X1648Y873D02*
+X1702Y873D01*
+X1645Y872D02*
+X1702Y872D01*
+X1643Y871D02*
+X1702Y871D01*
+X1640Y870D02*
+X1702Y870D01*
+X1638Y869D02*
+X1703Y869D01*
+X1635Y868D02*
+X1702Y868D01*
+X1633Y867D02*
+X1702Y867D01*
+X1630Y866D02*
+X1702Y866D01*
+X1627Y865D02*
+X1701Y865D01*
+X1625Y864D02*
+X1701Y864D01*
+X1622Y863D02*
+X1700Y863D01*
+X1620Y862D02*
+X1699Y862D01*
+X1617Y861D02*
+X1697Y861D01*
+X1615Y860D02*
+X1664Y860D01*
+X1612Y859D02*
+X1661Y859D01*
+X1609Y858D02*
+X1659Y858D01*
+X1607Y857D02*
+X1656Y857D01*
+X1604Y856D02*
+X1653Y856D01*
+X1602Y855D02*
+X1651Y855D01*
+X1599Y854D02*
+X1648Y854D01*
+X1597Y853D02*
+X1646Y853D01*
+X1594Y852D02*
+X1643Y852D01*
+X1592Y851D02*
+X1641Y851D01*
+X1589Y850D02*
+X1638Y850D01*
+X1586Y849D02*
+X1635Y849D01*
+X1584Y848D02*
+X1633Y848D01*
+X1581Y847D02*
+X1630Y847D01*
+X1579Y846D02*
+X1628Y846D01*
+X1576Y845D02*
+X1625Y845D01*
+X1574Y844D02*
+X1623Y844D01*
+X1571Y843D02*
+X1620Y843D01*
+X1569Y842D02*
+X1618Y842D01*
+X1567Y841D02*
+X1615Y841D01*
+X1566Y840D02*
+X1612Y840D01*
+X1565Y839D02*
+X1610Y839D01*
+X1564Y838D02*
+X1607Y838D01*
+X1564Y837D02*
+X1605Y837D01*
+X1563Y836D02*
+X1602Y836D01*
+X1563Y835D02*
+X1600Y835D01*
+X1563Y834D02*
+X1597Y834D01*
+X1563Y833D02*
+X1599Y833D01*
+X1563Y832D02*
+X1601Y832D01*
+X1564Y831D02*
+X1604Y831D01*
+X1564Y830D02*
+X1606Y830D01*
+X1564Y829D02*
+X1609Y829D01*
+X1565Y828D02*
+X1611Y828D01*
+X1566Y827D02*
+X1614Y827D01*
+X1567Y826D02*
+X1616Y826D01*
+X1569Y825D02*
+X1619Y825D01*
+X1572Y824D02*
+X1622Y824D01*
+X1574Y823D02*
+X1624Y823D01*
+X1577Y822D02*
+X1627Y822D01*
+X1580Y821D02*
+X1629Y821D01*
+X1582Y820D02*
+X1632Y820D01*
+X1585Y819D02*
+X1634Y819D01*
+X1587Y818D02*
+X1637Y818D01*
+X1590Y817D02*
+X1639Y817D01*
+X1592Y816D02*
+X1642Y816D01*
+X1595Y815D02*
+X1645Y815D01*
+X1598Y814D02*
+X1647Y814D01*
+X1600Y813D02*
+X1650Y813D01*
+X1603Y812D02*
+X1652Y812D01*
+X1605Y811D02*
+X1655Y811D01*
+X1608Y810D02*
+X1657Y810D01*
+X1610Y809D02*
+X1660Y809D01*
+X1613Y808D02*
+X1662Y808D01*
+X1616Y807D02*
+X1695Y807D01*
+X1618Y806D02*
+X1698Y806D01*
+X1621Y805D02*
+X1699Y805D01*
+X1623Y804D02*
+X1700Y804D01*
+X1626Y803D02*
+X1701Y803D01*
+X1628Y802D02*
+X1702Y802D01*
+X1631Y801D02*
+X1702Y801D01*
+X1634Y800D02*
+X1702Y800D01*
+X1636Y799D02*
+X1702Y799D01*
+X1639Y798D02*
+X1703Y798D01*
+X1641Y797D02*
+X1702Y797D01*
+X1644Y796D02*
+X1702Y796D01*
+X1646Y795D02*
+X1702Y795D01*
+X1649Y794D02*
+X1702Y794D01*
+X1652Y793D02*
+X1701Y793D01*
+X1654Y792D02*
+X1700Y792D01*
+X1657Y791D02*
+X1699Y791D01*
+X1659Y790D02*
+X1698Y790D01*
+X1662Y789D02*
+X1694Y789D01*
+X191Y786D02*
+X194Y786D01*
+X106Y785D02*
+X117Y785D01*
+X189Y785D02*
+X198Y785D01*
+X104Y784D02*
+X119Y784D01*
+X187Y784D02*
+X200Y784D01*
+X102Y783D02*
+X121Y783D01*
+X186Y783D02*
+X202Y783D01*
+X101Y782D02*
+X122Y782D01*
+X186Y782D02*
+X204Y782D01*
+X100Y781D02*
+X123Y781D01*
+X185Y781D02*
+X205Y781D01*
+X99Y780D02*
+X125Y780D01*
+X185Y780D02*
+X206Y780D01*
+X98Y779D02*
+X126Y779D01*
+X185Y779D02*
+X207Y779D01*
+X97Y778D02*
+X127Y778D01*
+X185Y778D02*
+X208Y778D01*
+X97Y777D02*
+X128Y777D01*
+X185Y777D02*
+X208Y777D01*
+X96Y776D02*
+X130Y776D01*
+X185Y776D02*
+X209Y776D01*
+X96Y775D02*
+X131Y775D01*
+X186Y775D02*
+X210Y775D01*
+X96Y774D02*
+X132Y774D01*
+X186Y774D02*
+X210Y774D01*
+X95Y773D02*
+X134Y773D01*
+X187Y773D02*
+X211Y773D01*
+X95Y772D02*
+X135Y772D01*
+X188Y772D02*
+X211Y772D01*
+X95Y771D02*
+X136Y771D01*
+X191Y771D02*
+X211Y771D01*
+X95Y770D02*
+X109Y770D01*
+X113Y770D02*
+X137Y770D01*
+X195Y770D02*
+X211Y770D01*
+X95Y769D02*
+X109Y769D01*
+X114Y769D02*
+X139Y769D01*
+X196Y769D02*
+X212Y769D01*
+X95Y768D02*
+X109Y768D01*
+X116Y768D02*
+X140Y768D01*
+X197Y768D02*
+X212Y768D01*
+X95Y767D02*
+X109Y767D01*
+X117Y767D02*
+X141Y767D01*
+X197Y767D02*
+X212Y767D01*
+X95Y766D02*
+X109Y766D01*
+X118Y766D02*
+X143Y766D01*
+X198Y766D02*
+X212Y766D01*
+X95Y765D02*
+X109Y765D01*
+X120Y765D02*
+X144Y765D01*
+X198Y765D02*
+X212Y765D01*
+X95Y764D02*
+X109Y764D01*
+X121Y764D02*
+X145Y764D01*
+X198Y764D02*
+X212Y764D01*
+X95Y763D02*
+X109Y763D01*
+X122Y763D02*
+X146Y763D01*
+X198Y763D02*
+X212Y763D01*
+X95Y762D02*
+X109Y762D01*
+X123Y762D02*
+X148Y762D01*
+X198Y762D02*
+X212Y762D01*
+X95Y761D02*
+X109Y761D01*
+X125Y761D02*
+X149Y761D01*
+X198Y761D02*
+X212Y761D01*
+X95Y760D02*
+X109Y760D01*
+X126Y760D02*
+X150Y760D01*
+X198Y760D02*
+X212Y760D01*
+X95Y759D02*
+X109Y759D01*
+X127Y759D02*
+X152Y759D01*
+X198Y759D02*
+X212Y759D01*
+X95Y758D02*
+X109Y758D01*
+X129Y758D02*
+X153Y758D01*
+X198Y758D02*
+X212Y758D01*
+X95Y757D02*
+X109Y757D01*
+X130Y757D02*
+X154Y757D01*
+X198Y757D02*
+X212Y757D01*
+X95Y756D02*
+X109Y756D01*
+X131Y756D02*
+X155Y756D01*
+X198Y756D02*
+X212Y756D01*
+X95Y755D02*
+X109Y755D01*
+X132Y755D02*
+X157Y755D01*
+X198Y755D02*
+X212Y755D01*
+X95Y754D02*
+X109Y754D01*
+X134Y754D02*
+X158Y754D01*
+X198Y754D02*
+X212Y754D01*
+X95Y753D02*
+X109Y753D01*
+X135Y753D02*
+X159Y753D01*
+X198Y753D02*
+X212Y753D01*
+X95Y752D02*
+X109Y752D01*
+X136Y752D02*
+X161Y752D01*
+X198Y752D02*
+X212Y752D01*
+X95Y751D02*
+X109Y751D01*
+X138Y751D02*
+X162Y751D01*
+X198Y751D02*
+X212Y751D01*
+X95Y750D02*
+X109Y750D01*
+X139Y750D02*
+X163Y750D01*
+X198Y750D02*
+X212Y750D01*
+X95Y749D02*
+X109Y749D01*
+X140Y749D02*
+X164Y749D01*
+X198Y749D02*
+X212Y749D01*
+X95Y748D02*
+X109Y748D01*
+X141Y748D02*
+X166Y748D01*
+X198Y748D02*
+X212Y748D01*
+X1569Y748D02*
+X1620Y748D01*
+X95Y747D02*
+X109Y747D01*
+X143Y747D02*
+X167Y747D01*
+X198Y747D02*
+X212Y747D01*
+X1567Y747D02*
+X1622Y747D01*
+X95Y746D02*
+X109Y746D01*
+X144Y746D02*
+X168Y746D01*
+X198Y746D02*
+X212Y746D01*
+X1566Y746D02*
+X1623Y746D01*
+X95Y745D02*
+X109Y745D01*
+X145Y745D02*
+X170Y745D01*
+X198Y745D02*
+X212Y745D01*
+X1565Y745D02*
+X1624Y745D01*
+X95Y744D02*
+X109Y744D01*
+X147Y744D02*
+X171Y744D01*
+X198Y744D02*
+X212Y744D01*
+X1565Y744D02*
+X1625Y744D01*
+X95Y743D02*
+X109Y743D01*
+X148Y743D02*
+X172Y743D01*
+X198Y743D02*
+X212Y743D01*
+X1564Y743D02*
+X1626Y743D01*
+X95Y742D02*
+X109Y742D01*
+X149Y742D02*
+X173Y742D01*
+X198Y742D02*
+X212Y742D01*
+X1564Y742D02*
+X1626Y742D01*
+X95Y741D02*
+X109Y741D01*
+X151Y741D02*
+X175Y741D01*
+X198Y741D02*
+X212Y741D01*
+X1563Y741D02*
+X1626Y741D01*
+X95Y740D02*
+X109Y740D01*
+X152Y740D02*
+X176Y740D01*
+X198Y740D02*
+X212Y740D01*
+X1563Y740D02*
+X1626Y740D01*
+X95Y739D02*
+X109Y739D01*
+X153Y739D02*
+X177Y739D01*
+X198Y739D02*
+X212Y739D01*
+X1563Y739D02*
+X1626Y739D01*
+X95Y738D02*
+X109Y738D01*
+X154Y738D02*
+X179Y738D01*
+X198Y738D02*
+X212Y738D01*
+X1563Y738D02*
+X1626Y738D01*
+X95Y737D02*
+X109Y737D01*
+X156Y737D02*
+X180Y737D01*
+X198Y737D02*
+X212Y737D01*
+X1563Y737D02*
+X1626Y737D01*
+X95Y736D02*
+X109Y736D01*
+X157Y736D02*
+X181Y736D01*
+X198Y736D02*
+X212Y736D01*
+X1563Y736D02*
+X1626Y736D01*
+X95Y735D02*
+X109Y735D01*
+X158Y735D02*
+X182Y735D01*
+X198Y735D02*
+X212Y735D01*
+X1563Y735D02*
+X1626Y735D01*
+X95Y734D02*
+X109Y734D01*
+X160Y734D02*
+X184Y734D01*
+X198Y734D02*
+X212Y734D01*
+X1563Y734D02*
+X1626Y734D01*
+X95Y733D02*
+X109Y733D01*
+X161Y733D02*
+X185Y733D01*
+X198Y733D02*
+X212Y733D01*
+X1563Y733D02*
+X1626Y733D01*
+X95Y732D02*
+X109Y732D01*
+X162Y732D02*
+X186Y732D01*
+X198Y732D02*
+X212Y732D01*
+X1563Y732D02*
+X1626Y732D01*
+X95Y731D02*
+X109Y731D01*
+X163Y731D02*
+X188Y731D01*
+X198Y731D02*
+X212Y731D01*
+X1563Y731D02*
+X1626Y731D01*
+X95Y730D02*
+X109Y730D01*
+X165Y730D02*
+X189Y730D01*
+X198Y730D02*
+X212Y730D01*
+X1563Y730D02*
+X1581Y730D01*
+X1609Y730D02*
+X1626Y730D01*
+X95Y729D02*
+X110Y729D01*
+X166Y729D02*
+X190Y729D01*
+X198Y729D02*
+X212Y729D01*
+X1563Y729D02*
+X1580Y729D01*
+X1609Y729D02*
+X1626Y729D01*
+X95Y728D02*
+X110Y728D01*
+X167Y728D02*
+X191Y728D01*
+X198Y728D02*
+X212Y728D01*
+X1563Y728D02*
+X1580Y728D01*
+X1609Y728D02*
+X1626Y728D01*
+X95Y727D02*
+X111Y727D01*
+X169Y727D02*
+X193Y727D01*
+X198Y727D02*
+X212Y727D01*
+X1563Y727D02*
+X1580Y727D01*
+X1609Y727D02*
+X1626Y727D01*
+X96Y726D02*
+X114Y726D01*
+X170Y726D02*
+X194Y726D01*
+X196Y726D02*
+X212Y726D01*
+X1563Y726D02*
+X1580Y726D01*
+X1609Y726D02*
+X1626Y726D01*
+X96Y725D02*
+X118Y725D01*
+X171Y725D02*
+X212Y725D01*
+X1563Y725D02*
+X1580Y725D01*
+X1609Y725D02*
+X1626Y725D01*
+X96Y724D02*
+X119Y724D01*
+X172Y724D02*
+X212Y724D01*
+X1563Y724D02*
+X1580Y724D01*
+X1609Y724D02*
+X1626Y724D01*
+X97Y723D02*
+X120Y723D01*
+X174Y723D02*
+X211Y723D01*
+X1563Y723D02*
+X1580Y723D01*
+X1609Y723D02*
+X1626Y723D01*
+X97Y722D02*
+X121Y722D01*
+X175Y722D02*
+X211Y722D01*
+X1563Y722D02*
+X1580Y722D01*
+X1609Y722D02*
+X1626Y722D01*
+X98Y721D02*
+X122Y721D01*
+X176Y721D02*
+X211Y721D01*
+X1563Y721D02*
+X1580Y721D01*
+X1609Y721D02*
+X1626Y721D01*
+X98Y720D02*
+X122Y720D01*
+X178Y720D02*
+X210Y720D01*
+X1563Y720D02*
+X1580Y720D01*
+X1609Y720D02*
+X1626Y720D01*
+X99Y719D02*
+X122Y719D01*
+X179Y719D02*
+X210Y719D01*
+X1563Y719D02*
+X1580Y719D01*
+X1609Y719D02*
+X1626Y719D01*
+X100Y718D02*
+X122Y718D01*
+X180Y718D02*
+X209Y718D01*
+X1563Y718D02*
+X1580Y718D01*
+X1609Y718D02*
+X1626Y718D01*
+X101Y717D02*
+X122Y717D01*
+X181Y717D02*
+X208Y717D01*
+X1563Y717D02*
+X1580Y717D01*
+X1609Y717D02*
+X1626Y717D01*
+X102Y716D02*
+X122Y716D01*
+X183Y716D02*
+X207Y716D01*
+X1563Y716D02*
+X1580Y716D01*
+X1609Y716D02*
+X1626Y716D01*
+X103Y715D02*
+X121Y715D01*
+X184Y715D02*
+X206Y715D01*
+X1563Y715D02*
+X1580Y715D01*
+X1609Y715D02*
+X1626Y715D01*
+X104Y714D02*
+X121Y714D01*
+X185Y714D02*
+X205Y714D01*
+X1563Y714D02*
+X1580Y714D01*
+X1609Y714D02*
+X1626Y714D01*
+X106Y713D02*
+X120Y713D01*
+X187Y713D02*
+X204Y713D01*
+X1563Y713D02*
+X1580Y713D01*
+X1609Y713D02*
+X1626Y713D01*
+X108Y712D02*
+X119Y712D01*
+X189Y712D02*
+X202Y712D01*
+X1563Y712D02*
+X1580Y712D01*
+X1609Y712D02*
+X1626Y712D01*
+X112Y711D02*
+X117Y711D01*
+X192Y711D02*
+X198Y711D01*
+X1563Y711D02*
+X1580Y711D01*
+X1609Y711D02*
+X1626Y711D01*
+X1563Y710D02*
+X1580Y710D01*
+X1609Y710D02*
+X1626Y710D01*
+X1563Y709D02*
+X1580Y709D01*
+X1609Y709D02*
+X1626Y709D01*
+X1563Y708D02*
+X1580Y708D01*
+X1609Y708D02*
+X1626Y708D01*
+X1563Y707D02*
+X1580Y707D01*
+X1609Y707D02*
+X1626Y707D01*
+X1563Y706D02*
+X1580Y706D01*
+X1609Y706D02*
+X1626Y706D01*
+X1563Y705D02*
+X1580Y705D01*
+X1609Y705D02*
+X1626Y705D01*
+X1563Y704D02*
+X1580Y704D01*
+X1609Y704D02*
+X1626Y704D01*
+X1563Y703D02*
+X1580Y703D01*
+X1609Y703D02*
+X1626Y703D01*
+X1563Y702D02*
+X1580Y702D01*
+X1609Y702D02*
+X1626Y702D01*
+X1563Y701D02*
+X1580Y701D01*
+X1609Y701D02*
+X1626Y701D01*
+X1563Y700D02*
+X1580Y700D01*
+X1609Y700D02*
+X1626Y700D01*
+X1563Y699D02*
+X1580Y699D01*
+X1609Y699D02*
+X1626Y699D01*
+X1563Y698D02*
+X1580Y698D01*
+X1609Y698D02*
+X1626Y698D01*
+X1563Y697D02*
+X1580Y697D01*
+X1609Y697D02*
+X1626Y697D01*
+X1563Y696D02*
+X1580Y696D01*
+X1609Y696D02*
+X1626Y696D01*
+X1563Y695D02*
+X1580Y695D01*
+X1609Y695D02*
+X1626Y695D01*
+X1563Y694D02*
+X1580Y694D01*
+X1609Y694D02*
+X1626Y694D01*
+X1563Y693D02*
+X1580Y693D01*
+X1609Y693D02*
+X1626Y693D01*
+X1563Y692D02*
+X1580Y692D01*
+X1609Y692D02*
+X1626Y692D01*
+X1563Y691D02*
+X1580Y691D01*
+X1609Y691D02*
+X1626Y691D01*
+X1563Y690D02*
+X1580Y690D01*
+X1609Y690D02*
+X1626Y690D01*
+X1563Y689D02*
+X1580Y689D01*
+X1609Y689D02*
+X1626Y689D01*
+X1563Y688D02*
+X1580Y688D01*
+X1609Y688D02*
+X1626Y688D01*
+X1563Y687D02*
+X1580Y687D01*
+X1609Y687D02*
+X1626Y687D01*
+X1563Y686D02*
+X1580Y686D01*
+X1609Y686D02*
+X1626Y686D01*
+X1563Y685D02*
+X1580Y685D01*
+X1609Y685D02*
+X1626Y685D01*
+X1690Y685D02*
+X1698Y685D01*
+X1563Y684D02*
+X1580Y684D01*
+X1609Y684D02*
+X1626Y684D01*
+X1689Y684D02*
+X1699Y684D01*
+X1563Y683D02*
+X1580Y683D01*
+X1609Y683D02*
+X1626Y683D01*
+X1688Y683D02*
+X1700Y683D01*
+X1563Y682D02*
+X1580Y682D01*
+X1609Y682D02*
+X1626Y682D01*
+X1687Y682D02*
+X1701Y682D01*
+X1563Y681D02*
+X1580Y681D01*
+X1609Y681D02*
+X1626Y681D01*
+X1686Y681D02*
+X1702Y681D01*
+X1563Y680D02*
+X1580Y680D01*
+X1609Y680D02*
+X1626Y680D01*
+X1686Y680D02*
+X1702Y680D01*
+X1563Y679D02*
+X1580Y679D01*
+X1609Y679D02*
+X1626Y679D01*
+X1686Y679D02*
+X1702Y679D01*
+X1563Y678D02*
+X1580Y678D01*
+X1609Y678D02*
+X1626Y678D01*
+X1685Y678D02*
+X1702Y678D01*
+X1563Y677D02*
+X1581Y677D01*
+X1609Y677D02*
+X1627Y677D01*
+X1685Y677D02*
+X1703Y677D01*
+X1563Y676D02*
+X1703Y676D01*
+X1563Y675D02*
+X1703Y675D01*
+X1563Y674D02*
+X1703Y674D01*
+X1563Y673D02*
+X1703Y673D01*
+X1563Y672D02*
+X1703Y672D01*
+X1563Y671D02*
+X1703Y671D01*
+X1563Y670D02*
+X1703Y670D01*
+X1563Y669D02*
+X1703Y669D01*
+X1563Y668D02*
+X1703Y668D01*
+X1563Y667D02*
+X1702Y667D01*
+X1563Y666D02*
+X1702Y666D01*
+X1564Y665D02*
+X1702Y665D01*
+X1564Y664D02*
+X1702Y664D01*
+X1565Y663D02*
+X1701Y663D01*
+X1566Y662D02*
+X1700Y662D01*
+X1567Y661D02*
+X1699Y661D01*
+X1568Y660D02*
+X1698Y660D01*
+X1572Y659D02*
+X1694Y659D01*
+X1623Y618D02*
+X1635Y618D01*
+X1621Y617D02*
+X1637Y617D01*
+X1620Y616D02*
+X1639Y616D01*
+X1619Y615D02*
+X1640Y615D01*
+X1618Y614D02*
+X1640Y614D01*
+X1617Y613D02*
+X1641Y613D01*
+X1617Y612D02*
+X1641Y612D01*
+X1617Y611D02*
+X1641Y611D01*
+X1617Y610D02*
+X1642Y610D01*
+X1617Y609D02*
+X1642Y609D01*
+X1617Y608D02*
+X1642Y608D01*
+X1617Y607D02*
+X1642Y607D01*
+X1617Y606D02*
+X1642Y606D01*
+X1617Y605D02*
+X1642Y605D01*
+X1617Y604D02*
+X1642Y604D01*
+X1617Y603D02*
+X1642Y603D01*
+X1617Y602D02*
+X1642Y602D01*
+X1617Y601D02*
+X1642Y601D01*
+X1617Y600D02*
+X1642Y600D01*
+X1617Y599D02*
+X1642Y599D01*
+X1617Y598D02*
+X1642Y598D01*
+X1617Y597D02*
+X1642Y597D01*
+X1617Y596D02*
+X1642Y596D01*
+X1617Y595D02*
+X1642Y595D01*
+X1617Y594D02*
+X1642Y594D01*
+X1617Y593D02*
+X1642Y593D01*
+X1617Y592D02*
+X1642Y592D01*
+X1617Y591D02*
+X1642Y591D01*
+X1617Y590D02*
+X1642Y590D01*
+X1617Y589D02*
+X1642Y589D01*
+X1617Y588D02*
+X1642Y588D01*
+X1617Y587D02*
+X1642Y587D01*
+X1617Y586D02*
+X1642Y586D01*
+X1617Y585D02*
+X1642Y585D01*
+X1617Y584D02*
+X1642Y584D01*
+X1617Y583D02*
+X1642Y583D01*
+X1617Y582D02*
+X1642Y582D01*
+X1617Y581D02*
+X1642Y581D01*
+X1617Y580D02*
+X1642Y580D01*
+X1617Y579D02*
+X1642Y579D01*
+X1617Y578D02*
+X1642Y578D01*
+X1617Y577D02*
+X1642Y577D01*
+X1617Y576D02*
+X1642Y576D01*
+X1617Y575D02*
+X1642Y575D01*
+X1617Y574D02*
+X1642Y574D01*
+X1617Y573D02*
+X1642Y573D01*
+X1617Y572D02*
+X1642Y572D01*
+X1617Y571D02*
+X1642Y571D01*
+X1617Y570D02*
+X1642Y570D01*
+X1617Y569D02*
+X1642Y569D01*
+X1617Y568D02*
+X1642Y568D01*
+X1617Y567D02*
+X1642Y567D01*
+X1617Y566D02*
+X1642Y566D01*
+X1617Y565D02*
+X1642Y565D01*
+X1617Y564D02*
+X1642Y564D01*
+X1617Y563D02*
+X1642Y563D01*
+X1617Y562D02*
+X1642Y562D01*
+X1617Y561D02*
+X1642Y561D01*
+X1617Y560D02*
+X1642Y560D01*
+X1617Y559D02*
+X1642Y559D01*
+X1617Y558D02*
+X1642Y558D01*
+X1617Y557D02*
+X1642Y557D01*
+X1617Y556D02*
+X1642Y556D01*
+X1617Y555D02*
+X1642Y555D01*
+X1617Y554D02*
+X1642Y554D01*
+X1617Y553D02*
+X1642Y553D01*
+X1617Y552D02*
+X1642Y552D01*
+X1617Y551D02*
+X1642Y551D01*
+X1617Y550D02*
+X1642Y550D01*
+X1617Y549D02*
+X1642Y549D01*
+X1617Y548D02*
+X1642Y548D01*
+X1617Y547D02*
+X1642Y547D01*
+X1617Y546D02*
+X1642Y546D01*
+X1617Y545D02*
+X1642Y545D01*
+X1617Y544D02*
+X1642Y544D01*
+X1617Y543D02*
+X1642Y543D01*
+X1617Y542D02*
+X1642Y542D01*
+X1617Y541D02*
+X1642Y541D01*
+X1617Y540D02*
+X1642Y540D01*
+X1617Y539D02*
+X1642Y539D01*
+X1617Y538D02*
+X1642Y538D01*
+X1617Y537D02*
+X1642Y537D01*
+X1617Y536D02*
+X1641Y536D01*
+X1617Y535D02*
+X1641Y535D01*
+X1618Y534D02*
+X1641Y534D01*
+X1618Y533D02*
+X1640Y533D01*
+X1619Y532D02*
+X1639Y532D01*
+X1620Y531D02*
+X1638Y531D01*
+X1621Y530D02*
+X1637Y530D01*
+X1625Y529D02*
+X1633Y529D01*
+X1627Y488D02*
+X1638Y488D01*
+X1623Y487D02*
+X1643Y487D01*
+X1620Y486D02*
+X1646Y486D01*
+X1617Y485D02*
+X1649Y485D01*
+X1615Y484D02*
+X1651Y484D01*
+X1613Y483D02*
+X1653Y483D01*
+X1611Y482D02*
+X1655Y482D01*
+X1609Y481D02*
+X1657Y481D01*
+X1607Y480D02*
+X1659Y480D01*
+X1605Y479D02*
+X1661Y479D01*
+X1603Y478D02*
+X1663Y478D01*
+X1601Y477D02*
+X1665Y477D01*
+X1599Y476D02*
+X1667Y476D01*
+X1597Y475D02*
+X1669Y475D01*
+X1595Y474D02*
+X1671Y474D01*
+X1593Y473D02*
+X1673Y473D01*
+X1591Y472D02*
+X1675Y472D01*
+X1589Y471D02*
+X1677Y471D01*
+X1587Y470D02*
+X1629Y470D01*
+X1637Y470D02*
+X1679Y470D01*
+X1585Y469D02*
+X1625Y469D01*
+X1641Y469D02*
+X1681Y469D01*
+X1583Y468D02*
+X1622Y468D01*
+X1643Y468D02*
+X1683Y468D01*
+X1581Y467D02*
+X1620Y467D01*
+X1645Y467D02*
+X1685Y467D01*
+X1579Y466D02*
+X1618Y466D01*
+X1647Y466D02*
+X1687Y466D01*
+X1577Y465D02*
+X1616Y465D01*
+X1649Y465D02*
+X1689Y465D01*
+X1575Y464D02*
+X1614Y464D01*
+X1651Y464D02*
+X1690Y464D01*
+X1573Y463D02*
+X1612Y463D01*
+X1653Y463D02*
+X1692Y463D01*
+X1572Y462D02*
+X1611Y462D01*
+X1655Y462D02*
+X1693Y462D01*
+X1571Y461D02*
+X1609Y461D01*
+X1657Y461D02*
+X1694Y461D01*
+X1570Y460D02*
+X1607Y460D01*
+X1659Y460D02*
+X1695Y460D01*
+X1569Y459D02*
+X1605Y459D01*
+X1661Y459D02*
+X1696Y459D01*
+X1569Y458D02*
+X1603Y458D01*
+X1663Y458D02*
+X1697Y458D01*
+X1568Y457D02*
+X1601Y457D01*
+X1665Y457D02*
+X1697Y457D01*
+X1567Y456D02*
+X1599Y456D01*
+X1667Y456D02*
+X1698Y456D01*
+X1567Y455D02*
+X1597Y455D01*
+X1669Y455D02*
+X1699Y455D01*
+X1566Y454D02*
+X1595Y454D01*
+X1671Y454D02*
+X1699Y454D01*
+X1566Y453D02*
+X1593Y453D01*
+X1673Y453D02*
+X1700Y453D01*
+X1565Y452D02*
+X1591Y452D01*
+X1675Y452D02*
+X1700Y452D01*
+X1565Y451D02*
+X1589Y451D01*
+X1677Y451D02*
+X1701Y451D01*
+X1565Y450D02*
+X1587Y450D01*
+X1679Y450D02*
+X1701Y450D01*
+X1564Y449D02*
+X1585Y449D01*
+X1681Y449D02*
+X1701Y449D01*
+X1564Y448D02*
+X1583Y448D01*
+X1682Y448D02*
+X1702Y448D01*
+X1564Y447D02*
+X1582Y447D01*
+X1683Y447D02*
+X1702Y447D01*
+X1564Y446D02*
+X1582Y446D01*
+X1684Y446D02*
+X1702Y446D01*
+X1563Y445D02*
+X1581Y445D01*
+X1685Y445D02*
+X1702Y445D01*
+X1563Y444D02*
+X1581Y444D01*
+X1685Y444D02*
+X1702Y444D01*
+X1563Y443D02*
+X1581Y443D01*
+X1685Y443D02*
+X1702Y443D01*
+X1563Y442D02*
+X1580Y442D01*
+X1685Y442D02*
+X1703Y442D01*
+X1563Y441D02*
+X1580Y441D01*
+X1685Y441D02*
+X1703Y441D01*
+X1563Y440D02*
+X1580Y440D01*
+X1685Y440D02*
+X1703Y440D01*
+X1563Y439D02*
+X1580Y439D01*
+X1685Y439D02*
+X1703Y439D01*
+X1563Y438D02*
+X1580Y438D01*
+X1685Y438D02*
+X1703Y438D01*
+X1563Y437D02*
+X1580Y437D01*
+X1685Y437D02*
+X1703Y437D01*
+X1563Y436D02*
+X1580Y436D01*
+X1685Y436D02*
+X1703Y436D01*
+X1563Y435D02*
+X1581Y435D01*
+X1685Y435D02*
+X1703Y435D01*
+X1563Y434D02*
+X1703Y434D01*
+X99Y433D02*
+X105Y433D01*
+X202Y433D02*
+X208Y433D01*
+X1563Y433D02*
+X1703Y433D01*
+X98Y432D02*
+X106Y432D01*
+X200Y432D02*
+X209Y432D01*
+X1563Y432D02*
+X1703Y432D01*
+X97Y431D02*
+X107Y431D01*
+X199Y431D02*
+X210Y431D01*
+X1563Y431D02*
+X1703Y431D01*
+X96Y430D02*
+X108Y430D01*
+X199Y430D02*
+X211Y430D01*
+X1563Y430D02*
+X1703Y430D01*
+X95Y429D02*
+X109Y429D01*
+X198Y429D02*
+X211Y429D01*
+X1563Y429D02*
+X1703Y429D01*
+X95Y428D02*
+X109Y428D01*
+X198Y428D02*
+X212Y428D01*
+X1563Y428D02*
+X1703Y428D01*
+X95Y427D02*
+X109Y427D01*
+X198Y427D02*
+X212Y427D01*
+X1563Y427D02*
+X1703Y427D01*
+X95Y426D02*
+X109Y426D01*
+X198Y426D02*
+X212Y426D01*
+X1563Y426D02*
+X1703Y426D01*
+X95Y425D02*
+X109Y425D01*
+X198Y425D02*
+X212Y425D01*
+X1563Y425D02*
+X1703Y425D01*
+X95Y424D02*
+X109Y424D01*
+X198Y424D02*
+X212Y424D01*
+X1563Y424D02*
+X1703Y424D01*
+X95Y423D02*
+X109Y423D01*
+X198Y423D02*
+X212Y423D01*
+X1563Y423D02*
+X1703Y423D01*
+X95Y422D02*
+X109Y422D01*
+X198Y422D02*
+X212Y422D01*
+X1563Y422D02*
+X1703Y422D01*
+X95Y421D02*
+X109Y421D01*
+X198Y421D02*
+X212Y421D01*
+X1563Y421D02*
+X1703Y421D01*
+X95Y420D02*
+X109Y420D01*
+X198Y420D02*
+X212Y420D01*
+X1563Y420D02*
+X1703Y420D01*
+X95Y419D02*
+X109Y419D01*
+X198Y419D02*
+X212Y419D01*
+X1563Y419D02*
+X1703Y419D01*
+X95Y418D02*
+X109Y418D01*
+X198Y418D02*
+X212Y418D01*
+X1563Y418D02*
+X1703Y418D01*
+X95Y417D02*
+X109Y417D01*
+X198Y417D02*
+X212Y417D01*
+X1563Y417D02*
+X1703Y417D01*
+X95Y416D02*
+X109Y416D01*
+X198Y416D02*
+X212Y416D01*
+X1563Y416D02*
+X1580Y416D01*
+X1685Y416D02*
+X1703Y416D01*
+X95Y415D02*
+X109Y415D01*
+X198Y415D02*
+X212Y415D01*
+X1563Y415D02*
+X1580Y415D01*
+X1685Y415D02*
+X1703Y415D01*
+X95Y414D02*
+X109Y414D01*
+X198Y414D02*
+X212Y414D01*
+X1563Y414D02*
+X1580Y414D01*
+X1685Y414D02*
+X1703Y414D01*
+X95Y413D02*
+X109Y413D01*
+X198Y413D02*
+X212Y413D01*
+X1563Y413D02*
+X1580Y413D01*
+X1685Y413D02*
+X1703Y413D01*
+X95Y412D02*
+X109Y412D01*
+X198Y412D02*
+X212Y412D01*
+X1563Y412D02*
+X1580Y412D01*
+X1685Y412D02*
+X1703Y412D01*
+X95Y411D02*
+X109Y411D01*
+X198Y411D02*
+X212Y411D01*
+X1563Y411D02*
+X1580Y411D01*
+X1685Y411D02*
+X1703Y411D01*
+X95Y410D02*
+X109Y410D01*
+X198Y410D02*
+X212Y410D01*
+X1563Y410D02*
+X1580Y410D01*
+X1685Y410D02*
+X1703Y410D01*
+X95Y409D02*
+X109Y409D01*
+X198Y409D02*
+X212Y409D01*
+X1563Y409D02*
+X1580Y409D01*
+X1685Y409D02*
+X1703Y409D01*
+X95Y408D02*
+X109Y408D01*
+X198Y408D02*
+X212Y408D01*
+X1563Y408D02*
+X1580Y408D01*
+X1685Y408D02*
+X1703Y408D01*
+X95Y407D02*
+X109Y407D01*
+X198Y407D02*
+X212Y407D01*
+X1563Y407D02*
+X1580Y407D01*
+X1685Y407D02*
+X1702Y407D01*
+X95Y406D02*
+X109Y406D01*
+X198Y406D02*
+X212Y406D01*
+X1563Y406D02*
+X1580Y406D01*
+X1686Y406D02*
+X1702Y406D01*
+X95Y405D02*
+X109Y405D01*
+X198Y405D02*
+X212Y405D01*
+X1564Y405D02*
+X1580Y405D01*
+X1686Y405D02*
+X1702Y405D01*
+X95Y404D02*
+X109Y404D01*
+X198Y404D02*
+X212Y404D01*
+X1564Y404D02*
+X1580Y404D01*
+X1686Y404D02*
+X1702Y404D01*
+X95Y403D02*
+X109Y403D01*
+X198Y403D02*
+X212Y403D01*
+X1565Y403D02*
+X1579Y403D01*
+X1687Y403D02*
+X1701Y403D01*
+X95Y402D02*
+X109Y402D01*
+X198Y402D02*
+X212Y402D01*
+X1565Y402D02*
+X1578Y402D01*
+X1688Y402D02*
+X1700Y402D01*
+X95Y401D02*
+X109Y401D01*
+X198Y401D02*
+X212Y401D01*
+X1567Y401D02*
+X1577Y401D01*
+X1689Y401D02*
+X1699Y401D01*
+X95Y400D02*
+X109Y400D01*
+X198Y400D02*
+X212Y400D01*
+X1568Y400D02*
+X1576Y400D01*
+X1690Y400D02*
+X1698Y400D01*
+X95Y399D02*
+X109Y399D01*
+X198Y399D02*
+X212Y399D01*
+X1571Y399D02*
+X1573Y399D01*
+X1693Y399D02*
+X1695Y399D01*
+X95Y398D02*
+X109Y398D01*
+X198Y398D02*
+X212Y398D01*
+X95Y397D02*
+X109Y397D01*
+X198Y397D02*
+X212Y397D01*
+X95Y396D02*
+X109Y396D01*
+X197Y396D02*
+X212Y396D01*
+X95Y395D02*
+X110Y395D01*
+X197Y395D02*
+X212Y395D01*
+X95Y394D02*
+X110Y394D01*
+X197Y394D02*
+X212Y394D01*
+X95Y393D02*
+X111Y393D01*
+X196Y393D02*
+X211Y393D01*
+X96Y392D02*
+X112Y392D01*
+X195Y392D02*
+X211Y392D01*
+X96Y391D02*
+X114Y391D01*
+X193Y391D02*
+X211Y391D01*
+X96Y390D02*
+X116Y390D01*
+X191Y390D02*
+X211Y390D01*
+X97Y389D02*
+X118Y389D01*
+X189Y389D02*
+X210Y389D01*
+X97Y388D02*
+X120Y388D01*
+X187Y388D02*
+X210Y388D01*
+X98Y387D02*
+X122Y387D01*
+X185Y387D02*
+X209Y387D01*
+X98Y386D02*
+X124Y386D01*
+X183Y386D02*
+X209Y386D01*
+X99Y385D02*
+X126Y385D01*
+X181Y385D02*
+X208Y385D01*
+X100Y384D02*
+X128Y384D01*
+X179Y384D02*
+X207Y384D01*
+X101Y383D02*
+X130Y383D01*
+X177Y383D02*
+X207Y383D01*
+X101Y382D02*
+X132Y382D01*
+X175Y382D02*
+X206Y382D01*
+X102Y381D02*
+X134Y381D01*
+X173Y381D02*
+X205Y381D01*
+X104Y380D02*
+X136Y380D01*
+X171Y380D02*
+X204Y380D01*
+X105Y379D02*
+X138Y379D01*
+X169Y379D02*
+X202Y379D01*
+X107Y378D02*
+X140Y378D01*
+X167Y378D02*
+X201Y378D01*
+X108Y377D02*
+X141Y377D01*
+X165Y377D02*
+X199Y377D01*
+X110Y376D02*
+X143Y376D01*
+X163Y376D02*
+X197Y376D01*
+X112Y375D02*
+X146Y375D01*
+X161Y375D02*
+X195Y375D01*
+X114Y374D02*
+X149Y374D01*
+X157Y374D02*
+X193Y374D01*
+X116Y373D02*
+X191Y373D01*
+X118Y372D02*
+X189Y372D01*
+X120Y371D02*
+X187Y371D01*
+X122Y370D02*
+X185Y370D01*
+X124Y369D02*
+X183Y369D01*
+X126Y368D02*
+X181Y368D01*
+X128Y367D02*
+X179Y367D01*
+X130Y366D02*
+X177Y366D01*
+X132Y365D02*
+X174Y365D01*
+X134Y364D02*
+X172Y364D01*
+X136Y363D02*
+X170Y363D01*
+X138Y362D02*
+X168Y362D01*
+X141Y361D02*
+X166Y361D01*
+X144Y360D02*
+X163Y360D01*
+X148Y359D02*
+X159Y359D01*
+X1569Y358D02*
+X1702Y358D01*
+X1567Y357D02*
+X1703Y357D01*
+X1566Y356D02*
+X1703Y356D01*
+X1565Y355D02*
+X1703Y355D01*
+X1565Y354D02*
+X1703Y354D01*
+X1564Y353D02*
+X1703Y353D01*
+X1564Y352D02*
+X1703Y352D01*
+X1563Y351D02*
+X1703Y351D01*
+X1563Y350D02*
+X1703Y350D01*
+X1563Y349D02*
+X1703Y349D01*
+X1563Y348D02*
+X1703Y348D01*
+X1564Y347D02*
+X1703Y347D01*
+X1564Y346D02*
+X1703Y346D01*
+X1564Y345D02*
+X1703Y345D01*
+X1565Y344D02*
+X1703Y344D01*
+X1566Y343D02*
+X1703Y343D01*
+X1567Y342D02*
+X1703Y342D01*
+X1569Y341D02*
+X1703Y341D01*
+X1678Y340D02*
+X1703Y340D01*
+X1677Y339D02*
+X1703Y339D01*
+X1675Y338D02*
+X1703Y338D01*
+X1674Y337D02*
+X1703Y337D01*
+X1672Y336D02*
+X1703Y336D01*
+X1671Y335D02*
+X1702Y335D01*
+X1670Y334D02*
+X1700Y334D01*
+X1668Y333D02*
+X1699Y333D01*
+X1667Y332D02*
+X1697Y332D01*
+X1665Y331D02*
+X1696Y331D01*
+X1664Y330D02*
+X1694Y330D01*
+X1662Y329D02*
+X1693Y329D01*
+X1661Y328D02*
+X1692Y328D01*
+X1660Y327D02*
+X1690Y327D01*
+X1658Y326D02*
+X1689Y326D01*
+X1657Y325D02*
+X1687Y325D01*
+X1655Y324D02*
+X1686Y324D01*
+X1654Y323D02*
+X1684Y323D01*
+X1645Y322D02*
+X1683Y322D01*
+X1643Y321D02*
+X1682Y321D01*
+X1642Y320D02*
+X1680Y320D01*
+X1641Y319D02*
+X1679Y319D01*
+X1641Y318D02*
+X1677Y318D01*
+X1640Y317D02*
+X1676Y317D01*
+X1640Y316D02*
+X1674Y316D01*
+X1640Y315D02*
+X1673Y315D01*
+X1640Y314D02*
+X1672Y314D01*
+X1640Y313D02*
+X1672Y313D01*
+X1640Y312D02*
+X1673Y312D01*
+X1640Y311D02*
+X1675Y311D01*
+X1640Y310D02*
+X1676Y310D01*
+X1641Y309D02*
+X1678Y309D01*
+X1641Y308D02*
+X1679Y308D01*
+X1642Y307D02*
+X1681Y307D01*
+X1644Y306D02*
+X1682Y306D01*
+X1646Y305D02*
+X1683Y305D01*
+X1654Y304D02*
+X1685Y304D01*
+X1655Y303D02*
+X1686Y303D01*
+X1657Y302D02*
+X1688Y302D01*
+X1658Y301D02*
+X1689Y301D01*
+X1660Y300D02*
+X1691Y300D01*
+X1661Y299D02*
+X1692Y299D01*
+X1663Y298D02*
+X1693Y298D01*
+X1664Y297D02*
+X1695Y297D01*
+X1665Y296D02*
+X1696Y296D01*
+X1667Y295D02*
+X1698Y295D01*
+X1668Y294D02*
+X1699Y294D01*
+X1670Y293D02*
+X1700Y293D01*
+X1671Y292D02*
+X1702Y292D01*
+X1673Y291D02*
+X1703Y291D01*
+X1674Y290D02*
+X1703Y290D01*
+X1675Y289D02*
+X1703Y289D01*
+X1677Y288D02*
+X1703Y288D01*
+X1571Y287D02*
+X1703Y287D01*
+X1568Y286D02*
+X1703Y286D01*
+X1567Y285D02*
+X1703Y285D01*
+X1566Y284D02*
+X1703Y284D01*
+X1565Y283D02*
+X1703Y283D01*
+X1564Y282D02*
+X1703Y282D01*
+X1564Y281D02*
+X1703Y281D01*
+X1563Y280D02*
+X1703Y280D01*
+X1563Y279D02*
+X1703Y279D01*
+X1563Y278D02*
+X1703Y278D01*
+X1563Y277D02*
+X1703Y277D01*
+X1563Y276D02*
+X1703Y276D01*
+X1564Y275D02*
+X1703Y275D01*
+X1564Y274D02*
+X1703Y274D01*
+X1565Y273D02*
+X1703Y273D01*
+X1565Y272D02*
+X1703Y272D01*
+X1567Y271D02*
+X1703Y271D01*
+X1568Y270D02*
+X1703Y270D01*
+X1571Y269D02*
+X1702Y269D01*
+D02*
+G04 End of Copper0*
+M02*

+ 71 - 0
tests/gerber_files/detector_copper_top.gbr

@@ -0,0 +1,71 @@
+G04 MADE WITH FRITZING*
+G04 WWW.FRITZING.ORG*
+G04 DOUBLE SIDED*
+G04 HOLES PLATED*
+G04 CONTOUR ON CENTER OF CONTOUR VECTOR*
+%ASAXBY*%
+%FSLAX23Y23*%
+%MOIN*%
+%OFA0B0*%
+%SFA1.0B1.0*%
+%ADD10C,0.075000*%
+%ADD11C,0.099055*%
+%ADD12C,0.078740*%
+%ADD13R,0.075000X0.075000*%
+%ADD14C,0.024000*%
+%ADD15C,0.020000*%
+%LNCOPPER1*%
+G90*
+G70*
+G54D10*
+X1149Y872D03*
+X1349Y872D03*
+X749Y722D03*
+X749Y522D03*
+X1149Y522D03*
+X1449Y522D03*
+X1149Y422D03*
+X1449Y422D03*
+X1149Y322D03*
+X1449Y322D03*
+X1149Y222D03*
+X1449Y222D03*
+X949Y472D03*
+X949Y72D03*
+G54D11*
+X749Y972D03*
+X599Y972D03*
+X349Y322D03*
+X349Y472D03*
+X349Y672D03*
+X349Y822D03*
+G54D10*
+X699Y122D03*
+X699Y322D03*
+G54D12*
+X699Y222D03*
+X949Y972D03*
+X749Y622D03*
+X1049Y222D03*
+X1249Y872D03*
+G54D13*
+X1149Y872D03*
+X1149Y522D03*
+G54D14*
+X952Y946D02*
+X1045Y249D01*
+G54D15*
+X776Y695D02*
+X721Y695D01*
+X721Y750D01*
+X776Y750D01*
+X776Y695D01*
+D02*
+X671Y150D02*
+X726Y150D01*
+X726Y95D01*
+X671Y95D01*
+X671Y150D01*
+D02*
+G04 End of Copper1*
+M02*

+ 46 - 0
tests/gerber_files/detector_drill.txt

@@ -0,0 +1,46 @@
+; NON-PLATED HOLES START AT T1
+; THROUGH (PLATED) HOLES START AT T100
+M48
+INCH
+T1C0.125984
+T100C0.031496
+T101C0.035000
+T102C0.059055
+%
+T1
+X001488Y010223
+X001488Y001223
+X016488Y001223
+X016488Y010223
+T100
+X009488Y009723
+X007488Y006223
+X012488Y008723
+X010488Y002223
+X006988Y002223
+T101
+X014488Y004223
+X006988Y003223
+X013488Y008723
+X011488Y008723
+X007488Y005223
+X014488Y003223
+X014488Y002223
+X011488Y005223
+X009488Y000723
+X011488Y004223
+X006988Y001223
+X009488Y004723
+X007488Y007223
+X011488Y003223
+X014488Y005223
+X011488Y002223
+T102
+X003488Y008223
+X003488Y004723
+X007488Y009723
+X003488Y006723
+X005988Y009723
+X003488Y003223
+T00
+M30

+ 155 - 0
tests/test_tcl_shell.py

@@ -0,0 +1,155 @@
+import sys
+import unittest
+from PyQt4 import QtGui
+from FlatCAMApp import App
+from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon
+from ObjectUI import GerberObjectUI, GeometryObjectUI
+from time import sleep
+import os
+import tempfile
+
+class TclShellCommandTest(unittest.TestCase):
+
+    gerber_files = 'tests/gerber_files'
+    copper_bottom_filename = 'detector_copper_bottom.gbr'
+    copper_top_filename = 'detector_copper_top.gbr'
+    cutout_filename = 'detector_contour.gbr'
+    excellon_filename = 'detector_drill.txt'
+    excellon_name = "excellon"
+    gerber_top_name = "top"
+    gerber_bottom_name = "bottom"
+    gerber_cutout_name = "cutout"
+    engraver_diameter = 0.3
+    cutout_diameter = 3
+    drill_diameter = 0.8
+
+    def setUp(self):
+        self.app = QtGui.QApplication(sys.argv)
+
+        # Create App, keep app defaults (do not load
+        # user-defined defaults).
+        self.fc = App(user_defaults=False)
+
+    def tearDown(self):
+        del self.fc
+        del self.app
+
+    def test_set_get_units(self):
+
+        self.fc.exec_command_test('set_sys units IN')
+        self.fc.exec_command_test('new')
+        units=self.fc.exec_command_test('get_sys units')
+        self.assertEquals(units, "IN")
+
+        self.fc.exec_command_test('set_sys units MM')
+        self.fc.exec_command_test('new')
+        units=self.fc.exec_command_test('get_sys units')
+        self.assertEquals(units, "MM")
+
+    def test_gerber_flow(self):
+
+        # open  gerber files top, bottom and cutout
+
+        self.fc.exec_command_test('set_sys units MM')
+        self.fc.exec_command_test('new')
+
+        self.fc.exec_command_test('open_gerber %s/%s -outname %s' % (self.gerber_files, self.copper_top_filename, self.gerber_top_name))
+        gerber_top_obj = self.fc.collection.get_by_name(self.gerber_top_name)
+        self.assertTrue(isinstance(gerber_top_obj, FlatCAMGerber),
+                        "Expected FlatCAMGerber, instead, %s is %s" %
+                        (self.gerber_top_name, type(gerber_top_obj)))
+
+        self.fc.exec_command_test('open_gerber %s/%s -outname %s' % (self.gerber_files, self.copper_bottom_filename, self.gerber_bottom_name))
+        gerber_bottom_obj = self.fc.collection.get_by_name(self.gerber_bottom_name)
+        self.assertTrue(isinstance(gerber_bottom_obj, FlatCAMGerber),
+                        "Expected FlatCAMGerber, instead, %s is %s" %
+                        (self.gerber_bottom_name, type(gerber_bottom_obj)))
+
+        self.fc.exec_command_test('open_gerber %s/%s -outname %s' % (self.gerber_files, self.cutout_filename, self.gerber_cutout_name))
+        gerber_cutout_obj = self.fc.collection.get_by_name(self.gerber_cutout_name)
+        self.assertTrue(isinstance(gerber_cutout_obj, FlatCAMGerber),
+                        "Expected FlatCAMGerber, instead, %s is %s" %
+                        (self.gerber_cutout_name, type(gerber_cutout_obj)))
+
+        # exteriors delete and join geometries for top layer
+        self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_cutout_name, self.engraver_diameter))
+        self.fc.exec_command_test('exteriors %s -outname %s' % (self.gerber_cutout_name + '_iso', self.gerber_cutout_name + '_iso_exterior'))
+        self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_iso'))
+        obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_iso_exterior')
+        self.assertTrue(isinstance(obj, FlatCAMGeometry),
+                        "Expected FlatCAMGeometry, instead, %s is %s" %
+                        (self.gerber_cutout_name + '_iso_exterior', type(obj)))
+
+        # mirror bottom gerbers
+        self.fc.exec_command_test('mirror %s -box %s -axis X' % (self.gerber_bottom_name, self.gerber_cutout_name))
+        self.fc.exec_command_test('mirror %s -box %s -axis X' % (self.gerber_cutout_name, self.gerber_cutout_name))
+
+        # exteriors delete and join geometries for bottom layer
+        self.fc.exec_command_test('isolate %s -dia %f -outname %s' % (self.gerber_cutout_name, self.engraver_diameter, self.gerber_cutout_name + '_bottom_iso'))
+        self.fc.exec_command_test('exteriors %s -outname %s' % (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior'))
+        self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso'))
+        obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior')
+        self.assertTrue(isinstance(obj, FlatCAMGeometry),
+                        "Expected FlatCAMGeometry, instead, %s is %s" %
+                        (self.gerber_cutout_name + '_bottom_iso_exterior', type(obj)))
+
+        # at this stage we should have 5 objects
+        names = self.fc.collection.get_names()
+        self.assertEqual(len(names), 5,
+                         "Expected 5 objects, found %d" % len(names))
+
+        # isolate traces
+        self.fc.exec_command_test('isolate %s -dia %f' %  (self.gerber_top_name, self.engraver_diameter))
+        self.fc.exec_command_test('isolate %s -dia %f' %  (self.gerber_bottom_name, self.engraver_diameter))
+
+        # join isolated geometries for top and  bottom
+        self.fc.exec_command_test('join_geometries %s %s %s' %  (self.gerber_top_name + '_join_iso', self.gerber_top_name + '_iso', self.gerber_cutout_name + '_iso_exterior'))
+        self.fc.exec_command_test('join_geometries %s %s %s' %  (self.gerber_bottom_name + '_join_iso', self.gerber_bottom_name + '_iso', self.gerber_cutout_name + '_bottom_iso_exterior'))
+
+        # at this stage we should have 9 objects
+        names = self.fc.collection.get_names()
+        self.assertEqual(len(names), 9,
+                         "Expected 9 objects, found %d" % len(names))
+
+        # clean unused isolations
+        self.fc.exec_command_test('delete %s' % (self.gerber_bottom_name + '_iso'))
+        self.fc.exec_command_test('delete %s' % (self.gerber_top_name + '_iso'))
+        self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_iso_exterior'))
+        self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso_exterior'))
+
+        # at this stage we should have 5 objects again
+        names = self.fc.collection.get_names()
+        self.assertEqual(len(names), 5,
+                         "Expected 5 objects, found %d" % len(names))
+
+        # geocutout bottom test (it cuts  to same object)
+        self.fc.exec_command_test('isolate %s -dia %f -outname %s' % (self.gerber_cutout_name, self.cutout_diameter, self.gerber_cutout_name + '_bottom_iso'))
+        self.fc.exec_command_test('exteriors %s -outname %s' % (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior'))
+        self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso'))
+        obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior')
+        self.assertTrue(isinstance(obj, FlatCAMGeometry),
+                        "Expected FlatCAMGeometry, instead, %s is %s" %
+                        (self.gerber_cutout_name + '_bottom_iso_exterior', type(obj)))
+        self.fc.exec_command_test('geocutout %s -dia %f -gapsize 0.3 -gaps 4' % (self.gerber_cutout_name + '_bottom_iso_exterior', self.cutout_diameter))
+
+        # at this stage we should have 6 objects
+        names = self.fc.collection.get_names()
+        self.assertEqual(len(names), 6,
+                         "Expected 6 objects, found %d" % len(names))
+
+        # TODO: tests for tcl
+
+    def test_excellon_flow(self):
+
+
+        self.fc.exec_command_test('set_sys units MM')
+        self.fc.exec_command_test('open_excellon %s/%s -outname %s' % (self.gerber_files, self.excellon_filename, self.excellon_name))
+        excellon_obj = self.fc.collection.get_by_name(self.excellon_name)
+        self.assertTrue(isinstance(excellon_obj, FlatCAMExcellon),
+                        "Expected FlatCAMExcellon, instead, %s is %s" %
+                        (self.excellon_name, type(excellon_obj)))
+
+        # mirror bottom excellon
+        self.fc.exec_command_test('mirror %s -box %s -axis X' % (self.excellon_name, self.gerber_cutout_name))
+
+        # TODO: tests for tcl