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

Merged jpcgt/flatcam into master

Kamil Sopko 10 лет назад
Родитель
Сommit
a0dd997536
88 измененных файлов с 13707 добавлено и 3038 удалено
  1. 3 1
      .gitignore
  2. 190 0
      DblSidedTool.py
  3. 377 157
      FlatCAMApp.py
  4. 202 22
      FlatCAMDraw.py
  5. 99 6
      FlatCAMGUI.py
  6. 269 119
      FlatCAMObj.py
  7. 148 0
      FlatCAMProcess.py
  8. 1 218
      FlatCAMTool.py
  9. 9 4
      FlatCAMWorker.py
  10. 78 25
      GUIElements.py
  11. 74 0
      MeasurementTool.py
  12. 64 7
      ObjectCollection.py
  13. 137 23
      ObjectUI.py
  14. 211 16
      PlotCanvas.py
  15. 8 8
      README.md
  16. 573 96
      camlib.py
  17. 49 0
      make_win32.py
  18. 0 17
      manual/_theme/__init__.py
  19. 0 19
      manual/_theme/breadcrumbs.html
  20. 0 32
      manual/_theme/footer.html
  21. 0 149
      manual/_theme/layout.html
  22. 0 205
      manual/_theme/layout_old.html
  23. 0 50
      manual/_theme/search.html
  24. 0 7
      manual/_theme/searchbox.html
  25. 0 17
      manual/_theme/sphinx_rtd_theme/__init__.py
  26. 0 19
      manual/_theme/sphinx_rtd_theme/breadcrumbs.html
  27. 0 32
      manual/_theme/sphinx_rtd_theme/footer.html
  28. 0 170
      manual/_theme/sphinx_rtd_theme/layout.html
  29. 0 205
      manual/_theme/sphinx_rtd_theme/layout_old.html
  30. 0 50
      manual/_theme/sphinx_rtd_theme/search.html
  31. 0 7
      manual/_theme/sphinx_rtd_theme/searchbox.html
  32. 0 0
      manual/_theme/sphinx_rtd_theme/static/css/badge_only.css
  33. 0 0
      manual/_theme/sphinx_rtd_theme/static/css/theme.css
  34. BIN
      manual/_theme/sphinx_rtd_theme/static/font/fontawesome_webfont.eot
  35. 0 195
      manual/_theme/sphinx_rtd_theme/static/font/fontawesome_webfont.svg
  36. BIN
      manual/_theme/sphinx_rtd_theme/static/font/fontawesome_webfont.ttf
  37. BIN
      manual/_theme/sphinx_rtd_theme/static/font/fontawesome_webfont.woff
  38. 0 47
      manual/_theme/sphinx_rtd_theme/static/js/theme.js
  39. 0 8
      manual/_theme/sphinx_rtd_theme/theme.conf
  40. 0 37
      manual/_theme/sphinx_rtd_theme/versions.html
  41. 0 0
      manual/_theme/static/css/badge_only.css
  42. 0 0
      manual/_theme/static/css/theme.css
  43. BIN
      manual/_theme/static/font/fontawesome_webfont.eot
  44. 0 195
      manual/_theme/static/font/fontawesome_webfont.svg
  45. BIN
      manual/_theme/static/font/fontawesome_webfont.ttf
  46. BIN
      manual/_theme/static/font/fontawesome_webfont.woff
  47. 0 47
      manual/_theme/static/js/theme.js
  48. 0 8
      manual/_theme/theme.conf
  49. 0 37
      manual/_theme/versions.html
  50. 0 46
      manual/basics.rst
  51. 0 241
      manual/cmdreference.rst
  52. 0 134
      manual/editor.rst
  53. 0 54
      manual/flatcamshell.rst
  54. 0 100
      manual/installation.rst
  55. 0 4
      manual/introduction.rst
  56. 0 201
      manual/procedures.rst
  57. 9 0
      requirements.txt
  58. 34 0
      sandbox/diagnose.py
  59. 30 0
      sandbox/gerber_find.py
  60. 20 0
      sandbox/process_widget.py
  61. 2 2
      setup_ubuntu.sh
  62. BIN
      share/active.gif
  63. BIN
      share/intersection16.png
  64. BIN
      share/intersection24.png
  65. BIN
      share/intersection32.png
  66. 506 0
      svgparse.py
  67. 0 0
      tests/__init__.py
  68. 95 0
      tests/canvas/performance.py
  69. 6 0
      tests/canvas/prof.sh
  70. 6358 0
      tests/gerber_files/STM32F4-spindle.cmp
  71. 54 0
      tests/gerber_files/simple1.gbr
  72. 3045 0
      tests/gerber_parsing_profiling/gerber1.gbr
  73. 13 0
      tests/gerber_parsing_profiling/gerber_parsing_line_profile_1.py
  74. 17 0
      tests/gerber_parsing_profiling/gerber_parsing_profile_1.py
  75. 34 0
      tests/other/destructor_test.py
  76. 0 0
      tests/other/profile_gerber_parser.py
  77. 0 0
      tests/other/test_excellon_1.py
  78. 0 0
      tests/other/test_fcrts.py
  79. 47 0
      tests/other/test_plotg.py
  80. 0 1
      tests/other/test_rt.py
  81. 126 0
      tests/svg/drawing.svg
  82. 330 0
      tests/test_excellon.py
  83. 34 0
      tests/test_gerber_buffer.py
  84. 136 0
      tests/test_gerber_flow.py
  85. 212 0
      tests/test_paint.py
  86. 88 0
      tests/test_pathconnect.py
  87. 8 0
      tests/toolpath_optimization_profiling/toollift_minimization_line_profile1.py
  88. 11 0
      tests/toolpath_optimization_profiling/toollift_minimization_profile1.py

+ 3 - 1
.gitignore

@@ -1 +1,3 @@
-*.pyc
+*.pyc
+.idea/
+tests/tmp/

+ 190 - 0
DblSidedTool.py

@@ -0,0 +1,190 @@
+from PyQt4 import QtGui
+from GUIElements import RadioSet, EvalEntry, LengthEntry
+from FlatCAMTool import FlatCAMTool
+#from FlatCAMObj import FlatCAMGerber, FlatCAMExcellon
+from FlatCAMObj import *
+from shapely.geometry import Point
+from shapely import affinity
+
+
+class DblSidedTool(FlatCAMTool):
+
+    toolName = "Double-Sided PCB Tool"
+
+    def __init__(self, app):
+        FlatCAMTool.__init__(self, app)
+
+        ## Title
+        title_label = QtGui.QLabel("<font size=4><b>%s</b></font>" % self.toolName)
+        self.layout.addWidget(title_label)
+
+        ## Form Layout
+        form_layout = QtGui.QFormLayout()
+        self.layout.addLayout(form_layout)
+
+        ## Layer to mirror
+        self.object_combo = QtGui.QComboBox()
+        self.object_combo.setModel(self.app.collection)
+        self.botlay_label = QtGui.QLabel("Bottom Layer:")
+        self.botlay_label.setToolTip(
+            "Layer to be mirrorer."
+        )
+        # form_layout.addRow("Bottom Layer:", self.object_combo)
+        form_layout.addRow(self.botlay_label, self.object_combo)
+
+        ## Axis
+        self.mirror_axis = RadioSet([{'label': 'X', 'value': 'X'},
+                                     {'label': 'Y', 'value': 'Y'}])
+        self.mirax_label = QtGui.QLabel("Mirror Axis:")
+        self.mirax_label.setToolTip(
+            "Mirror vertically (X) or horizontally (Y)."
+        )
+        # form_layout.addRow("Mirror Axis:", self.mirror_axis)
+        form_layout.addRow(self.mirax_label, self.mirror_axis)
+
+        ## Axis Location
+        self.axis_location = RadioSet([{'label': 'Point', 'value': 'point'},
+                                       {'label': 'Box', 'value': 'box'}])
+        self.axloc_label = QtGui.QLabel("Axis Location:")
+        self.axloc_label.setToolTip(
+            "The axis should pass through a <b>point</b> or cut "
+            "a specified <b>box</b> (in a Geometry object) in "
+            "the middle."
+        )
+        # form_layout.addRow("Axis Location:", self.axis_location)
+        form_layout.addRow(self.axloc_label, self.axis_location)
+
+        ## Point/Box
+        self.point_box_container = QtGui.QVBoxLayout()
+        self.pb_label = QtGui.QLabel("Point/Box:")
+        self.pb_label.setToolTip(
+            "Specify the point (x, y) through which the mirror axis "
+            "passes or the Geometry object containing a rectangle "
+            "that the mirror axis cuts in half."
+        )
+        # form_layout.addRow("Point/Box:", self.point_box_container)
+        form_layout.addRow(self.pb_label, self.point_box_container)
+
+        self.point = EvalEntry()
+        self.point_box_container.addWidget(self.point)
+        self.box_combo = QtGui.QComboBox()
+        self.box_combo.setModel(self.app.collection)
+        self.point_box_container.addWidget(self.box_combo)
+        self.box_combo.hide()
+
+        ## Alignment holes
+        self.alignment_holes = EvalEntry()
+        self.ah_label = QtGui.QLabel("Alignment Holes:")
+        self.ah_label.setToolTip(
+            "Alignment holes (x1, y1), (x2, y2), ... "
+            "on one side of the mirror axis."
+        )
+        form_layout.addRow(self.ah_label, self.alignment_holes)
+
+        ## Drill diameter for alignment holes
+        self.drill_dia = LengthEntry()
+        self.dd_label = QtGui.QLabel("Drill diam.:")
+        self.dd_label.setToolTip(
+            "Diameter of the drill for the "
+            "alignment holes."
+        )
+        form_layout.addRow(self.dd_label, self.drill_dia)
+
+        ## Buttons
+        hlay = QtGui.QHBoxLayout()
+        self.layout.addLayout(hlay)
+        hlay.addStretch()
+        self.create_alignment_hole_button = QtGui.QPushButton("Create Alignment Drill")
+        self.create_alignment_hole_button.setToolTip(
+            "Creates an Excellon Object containing the "
+            "specified alignment holes and their mirror "
+            "images."
+        )
+        self.mirror_object_button = QtGui.QPushButton("Mirror Object")
+        self.mirror_object_button.setToolTip(
+            "Mirrors (flips) the specified object around "
+            "the specified axis. Does not create a new "
+            "object, but modifies it."
+        )
+        hlay.addWidget(self.create_alignment_hole_button)
+        hlay.addWidget(self.mirror_object_button)
+
+        self.layout.addStretch()
+
+        ## Signals
+        self.create_alignment_hole_button.clicked.connect(self.on_create_alignment_holes)
+        self.mirror_object_button.clicked.connect(self.on_mirror)
+
+        self.axis_location.group_toggle_fn = self.on_toggle_pointbox
+
+        ## Initialize form
+        self.mirror_axis.set_value('X')
+        self.axis_location.set_value('point')
+
+    def on_create_alignment_holes(self):
+        axis = self.mirror_axis.get_value()
+        mode = self.axis_location.get_value()
+
+        if mode == "point":
+            px, py = self.point.get_value()
+        else:
+            selection_index = self.box_combo.currentIndex()
+            bb_obj = self.app.collection.object_list[selection_index]  # TODO: Direct access??
+            xmin, ymin, xmax, ymax = bb_obj.bounds()
+            px = 0.5 * (xmin + xmax)
+            py = 0.5 * (ymin + ymax)
+
+        xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
+
+        dia = self.drill_dia.get_value()
+        tools = {"1": {"C": dia}}
+
+        # holes = self.alignment_holes.get_value()
+        holes = eval('[{}]'.format(self.alignment_holes.text()))
+        drills = []
+
+        for hole in holes:
+            point = Point(hole)
+            point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
+            drills.append({"point": point, "tool": "1"})
+            drills.append({"point": point_mirror, "tool": "1"})
+
+        def obj_init(obj_inst, app_inst):
+            obj_inst.tools = tools
+            obj_inst.drills = drills
+            obj_inst.create_geometry()
+
+        self.app.new_object("excellon", "Alignment Drills", obj_init)
+
+    def on_mirror(self):
+        selection_index = self.object_combo.currentIndex()
+        fcobj = self.app.collection.object_list[selection_index]
+
+        # For now, lets limit to Gerbers and Excellons.
+        # assert isinstance(gerb, FlatCAMGerber)
+        if not isinstance(fcobj, FlatCAMGerber) and not isinstance(fcobj, FlatCAMExcellon):
+            self.info("ERROR: Only Gerber and Excellon objects can be mirrored.")
+            return
+
+        axis = self.mirror_axis.get_value()
+        mode = self.axis_location.get_value()
+
+        if mode == "point":
+            px, py = self.point.get_value()
+        else:
+            selection_index = self.box_combo.currentIndex()
+            bb_obj = self.app.collection.object_list[selection_index]  # TODO: Direct access??
+            xmin, ymin, xmax, ymax = bb_obj.bounds()
+            px = 0.5 * (xmin + xmax)
+            py = 0.5 * (ymin + ymax)
+
+        fcobj.mirror(axis, [px, py])
+        fcobj.plot()
+
+    def on_toggle_pointbox(self):
+        if self.axis_location.get_value() == "point":
+            self.point.show()
+            self.box_combo.hide()
+        else:
+            self.point.hide()
+            self.box_combo.show()

Разница между файлами не показана из-за своего большого размера
+ 377 - 157
FlatCAMApp.py


+ 202 - 22
FlatCAMDraw.py

@@ -1,6 +1,8 @@
 from PyQt4 import QtGui, QtCore, Qt
 from PyQt4 import QtGui, QtCore, Qt
 import FlatCAMApp
 import FlatCAMApp
 from camlib import *
 from camlib import *
+from FlatCAMTool import FlatCAMTool
+from ObjectUI import LengthEntry
 
 
 from shapely.geometry import Polygon, LineString, Point, LinearRing
 from shapely.geometry import Polygon, LineString, Point, LinearRing
 from shapely.geometry import MultiPoint, MultiPolygon
 from shapely.geometry import MultiPoint, MultiPolygon
@@ -14,11 +16,52 @@ from shapely.geometry.base import BaseGeometry
 from numpy import arctan2, Inf, array, sqrt, pi, ceil, sin, cos, sign, dot
 from numpy import arctan2, Inf, array, sqrt, pi, ceil, sin, cos, sign, dot
 from numpy.linalg import solve
 from numpy.linalg import solve
 
 
-from mpl_toolkits.axes_grid.anchored_artists import AnchoredDrawingArea
+#from mpl_toolkits.axes_grid.anchored_artists import AnchoredDrawingArea
 
 
 from rtree import index as rtindex
 from rtree import index as rtindex
 
 
 
 
+class BufferSelectionTool(FlatCAMTool):
+    """
+    Simple input for buffer distance.
+    """
+
+    toolName = "Buffer Selection"
+
+    def __init__(self, app, fcdraw):
+        FlatCAMTool.__init__(self, app)
+
+        self.fcdraw = fcdraw
+
+        ## Title
+        title_label = QtGui.QLabel("<font size=4><b>%s</b></font>" % self.toolName)
+        self.layout.addWidget(title_label)
+
+        ## Form Layout
+        form_layout = QtGui.QFormLayout()
+        self.layout.addLayout(form_layout)
+
+        ## Buffer distance
+        self.buffer_distance_entry = LengthEntry()
+        form_layout.addRow("Buffer distance:", self.buffer_distance_entry)
+
+        ## Buttons
+        hlay = QtGui.QHBoxLayout()
+        self.layout.addLayout(hlay)
+        hlay.addStretch()
+        self.buffer_button = QtGui.QPushButton("Buffer")
+        hlay.addWidget(self.buffer_button)
+
+        self.layout.addStretch()
+
+        ## Signals
+        self.buffer_button.clicked.connect(self.on_buffer)
+
+    def on_buffer(self):
+        buffer_distance = self.buffer_distance_entry.get_value()
+        self.fcdraw.buffer(buffer_distance)
+
+
 class DrawToolShape(object):
 class DrawToolShape(object):
     """
     """
     Encapsulates "shapes" under a common class.
     Encapsulates "shapes" under a common class.
@@ -291,7 +334,7 @@ class FCArc(FCShapeTool):
             startangle = arctan2(p1[1] - center[1], p1[0] - center[0])
             startangle = arctan2(p1[1] - center[1], p1[0] - center[0])
             stopangle = arctan2(p2[1] - center[1], p2[0] - center[0])
             stopangle = arctan2(p2[1] - center[1], p2[0] - center[0])
             self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle,
             self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle,
-                                           self.direction, self.steps_per_circ)))
+                                          self.direction, self.steps_per_circ)))
 
 
         elif self.mode == '132':
         elif self.mode == '132':
             p1 = array(self.points[0])
             p1 = array(self.points[0])
@@ -305,7 +348,7 @@ class FCArc(FCShapeTool):
             stopangle = arctan2(p3[1] - center[1], p3[0] - center[0])
             stopangle = arctan2(p3[1] - center[1], p3[0] - center[0])
 
 
             self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle,
             self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle,
-                                           direction, self.steps_per_circ)))
+                                          direction, self.steps_per_circ)))
 
 
         else:  # self.mode == '12c'
         else:  # self.mode == '12c'
             p1 = array(self.points[0])
             p1 = array(self.points[0])
@@ -414,6 +457,11 @@ class FCPolygon(FCShapeTool):
         self.geometry = DrawToolShape(Polygon(self.points))
         self.geometry = DrawToolShape(Polygon(self.points))
         self.complete = True
         self.complete = True
 
 
+    def on_key(self, key):
+        if key == 'backspace':
+            if len(self.points) > 0:
+                self.points = self.points[0:-1]
+
 
 
 class FCPath(FCPolygon):
 class FCPath(FCPolygon):
     """
     """
@@ -425,13 +473,18 @@ class FCPath(FCPolygon):
         self.complete = True
         self.complete = True
 
 
     def utility_geometry(self, data=None):
     def utility_geometry(self, data=None):
-        if len(self.points) > 1:
+        if len(self.points) > 0:
             temp_points = [x for x in self.points]
             temp_points = [x for x in self.points]
             temp_points.append(data)
             temp_points.append(data)
             return DrawToolUtilityShape(LineString(temp_points))
             return DrawToolUtilityShape(LineString(temp_points))
 
 
         return None
         return None
 
 
+    def on_key(self, key):
+        if key == 'backspace':
+            if len(self.points) > 0:
+                self.points = self.points[0:-1]
+
 
 
 class FCSelect(DrawTool):
 class FCSelect(DrawTool):
     def __init__(self, draw_app):
     def __init__(self, draw_app):
@@ -442,7 +495,10 @@ class FCSelect(DrawTool):
         self.start_msg = "Click on geometry to select"
         self.start_msg = "Click on geometry to select"
 
 
     def click(self, point):
     def click(self, point):
-        _, closest_shape = self.storage.nearest(point)
+        try:
+            _, closest_shape = self.storage.nearest(point)
+        except StopIteration:
+            return ""
 
 
         if self.draw_app.key != 'control':
         if self.draw_app.key != 'control':
             self.draw_app.selected = []
             self.draw_app.selected = []
@@ -481,7 +537,7 @@ class FCMove(FCShapeTool):
         dx = self.destination[0] - self.origin[0]
         dx = self.destination[0] - self.origin[0]
         dy = self.destination[1] - self.origin[1]
         dy = self.destination[1] - self.origin[1]
         self.geometry = [DrawToolShape(affinity.translate(geom.geo, xoff=dx, yoff=dy))
         self.geometry = [DrawToolShape(affinity.translate(geom.geo, xoff=dx, yoff=dy))
-                                       for geom in self.draw_app.get_selected()]
+                         for geom in self.draw_app.get_selected()]
 
 
         # Delete old
         # Delete old
         self.draw_app.delete_selected()
         self.draw_app.delete_selected()
@@ -528,7 +584,9 @@ class FCCopy(FCMove):
 ########################
 ########################
 class FlatCAMDraw(QtCore.QObject):
 class FlatCAMDraw(QtCore.QObject):
     def __init__(self, app, disabled=False):
     def __init__(self, app, disabled=False):
-        assert isinstance(app, FlatCAMApp.App)
+        assert isinstance(app, FlatCAMApp.App), \
+            "Expected the app to be a FlatCAMApp.App, got %s" % type(app)
+
         super(FlatCAMDraw, self).__init__()
         super(FlatCAMDraw, self).__init__()
 
 
         self.app = app
         self.app = app
@@ -546,6 +604,7 @@ class FlatCAMDraw(QtCore.QObject):
         self.add_polygon_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/polygon32.png'), 'Add Polygon')
         self.add_polygon_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/polygon32.png'), 'Add Polygon')
         self.add_path_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/path32.png'), 'Add Path')
         self.add_path_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/path32.png'), 'Add Path')
         self.union_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/union32.png'), 'Polygon Union')
         self.union_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/union32.png'), 'Polygon Union')
+        self.intersection_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/intersection32.png'), 'Polygon Intersection')
         self.subtract_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/subtract32.png'), 'Polygon Subtraction')
         self.subtract_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/subtract32.png'), 'Polygon Subtraction')
         self.cutpath_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/cutpath32.png'), 'Cut Path')
         self.cutpath_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/cutpath32.png'), 'Cut Path')
         self.move_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/move32.png'), "Move Objects 'm'")
         self.move_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/move32.png'), "Move Objects 'm'")
@@ -573,14 +632,43 @@ class FlatCAMDraw(QtCore.QObject):
         self.snap_toolbar.setDisabled(disabled)
         self.snap_toolbar.setDisabled(disabled)
         self.app.ui.addToolBar(self.snap_toolbar)
         self.app.ui.addToolBar(self.snap_toolbar)
 
 
+        ### Application menu ###
+        self.menu = QtGui.QMenu("Drawing")
+        self.app.ui.menu.insertMenu(self.app.ui.menutoolaction, self.menu)
+        # self.select_menuitem = self.menu.addAction(QtGui.QIcon('share/pointer16.png'), "Select 'Esc'")
+        # self.add_circle_menuitem = self.menu.addAction(QtGui.QIcon('share/circle16.png'), 'Add Circle')
+        # self.add_arc_menuitem = self.menu.addAction(QtGui.QIcon('share/arc16.png'), 'Add Arc')
+        # self.add_rectangle_menuitem = self.menu.addAction(QtGui.QIcon('share/rectangle16.png'), 'Add Rectangle')
+        # self.add_polygon_menuitem = self.menu.addAction(QtGui.QIcon('share/polygon16.png'), 'Add Polygon')
+        # self.add_path_menuitem = self.menu.addAction(QtGui.QIcon('share/path16.png'), 'Add Path')
+        self.union_menuitem = self.menu.addAction(QtGui.QIcon('share/union16.png'), 'Polygon Union')
+        self.intersection_menuitem = self.menu.addAction(QtGui.QIcon('share/intersection16.png'), 'Polygon Intersection')
+        # self.subtract_menuitem = self.menu.addAction(QtGui.QIcon('share/subtract16.png'), 'Polygon Subtraction')
+        self.cutpath_menuitem = self.menu.addAction(QtGui.QIcon('share/cutpath16.png'), 'Cut Path')
+        # self.move_menuitem = self.menu.addAction(QtGui.QIcon('share/move16.png'), "Move Objects 'm'")
+        # self.copy_menuitem = self.menu.addAction(QtGui.QIcon('share/copy16.png'), "Copy Objects 'c'")
+        self.delete_menuitem = self.menu.addAction(QtGui.QIcon('share/deleteshape16.png'), "Delete Shape '-'")
+        self.buffer_menuitem = self.menu.addAction(QtGui.QIcon('share/buffer16.png'), "Buffer selection 'b'")
+        self.menu.addSeparator()
+
+        self.buffer_menuitem.triggered.connect(self.on_buffer_tool)
+        self.delete_menuitem.triggered.connect(self.on_delete_btn)
+        self.union_menuitem.triggered.connect(self.union)
+        self.intersection_menuitem.triggered.connect(self.intersection)
+        self.cutpath_menuitem.triggered.connect(self.cutpath)
+
         ### Event handlers ###
         ### Event handlers ###
-        ## Canvas events
-        self.canvas.mpl_connect('button_press_event', self.on_canvas_click)
-        self.canvas.mpl_connect('motion_notify_event', self.on_canvas_move)
-        self.canvas.mpl_connect('key_press_event', self.on_canvas_key)
-        self.canvas.mpl_connect('key_release_event', self.on_canvas_key_release)
+        # Connection ids for Matplotlib
+        self.cid_canvas_click = None
+        self.cid_canvas_move = None
+        self.cid_canvas_key = None
+        self.cid_canvas_key_release = None
+
+        # Connect the canvas
+        #self.connect_canvas_event_handlers()
 
 
         self.union_btn.triggered.connect(self.union)
         self.union_btn.triggered.connect(self.union)
+        self.intersection_btn.triggered.connect(self.intersection)
         self.subtract_btn.triggered.connect(self.subtract)
         self.subtract_btn.triggered.connect(self.subtract)
         self.cutpath_btn.triggered.connect(self.cutpath)
         self.cutpath_btn.triggered.connect(self.cutpath)
         self.delete_btn.triggered.connect(self.on_delete_btn)
         self.delete_btn.triggered.connect(self.on_delete_btn)
@@ -663,6 +751,19 @@ class FlatCAMDraw(QtCore.QObject):
     def activate(self):
     def activate(self):
         pass
         pass
 
 
+    def connect_canvas_event_handlers(self):
+        ## Canvas events
+        self.cid_canvas_click = self.canvas.mpl_connect('button_press_event', self.on_canvas_click)
+        self.cid_canvas_move = self.canvas.mpl_connect('motion_notify_event', self.on_canvas_move)
+        self.cid_canvas_key = self.canvas.mpl_connect('key_press_event', self.on_canvas_key)
+        self.cid_canvas_key_release = self.canvas.mpl_connect('key_release_event', self.on_canvas_key_release)
+
+    def disconnect_canvas_event_handlers(self):
+        self.canvas.mpl_disconnect(self.cid_canvas_click)
+        self.canvas.mpl_disconnect(self.cid_canvas_move)
+        self.canvas.mpl_disconnect(self.cid_canvas_key)
+        self.canvas.mpl_disconnect(self.cid_canvas_key_release)
+
     def add_shape(self, shape):
     def add_shape(self, shape):
         """
         """
         Adds a shape to the shape storage.
         Adds a shape to the shape storage.
@@ -678,9 +779,15 @@ class FlatCAMDraw(QtCore.QObject):
                 self.add_shape(subshape)
                 self.add_shape(subshape)
             return
             return
 
 
-        assert isinstance(shape, DrawToolShape)
-        assert shape.geo is not None
-        assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or not isinstance(shape.geo, list)
+        assert isinstance(shape, DrawToolShape), \
+            "Expected a DrawToolShape, got %s" % type(shape)
+
+        assert shape.geo is not None, \
+            "Shape object has empty geometry (None)"
+
+        assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or \
+               not isinstance(shape.geo, list), \
+            "Shape objects has empty geometry ([])"
 
 
         if isinstance(shape, DrawToolUtilityShape):
         if isinstance(shape, DrawToolUtilityShape):
             self.utility.append(shape)
             self.utility.append(shape)
@@ -688,6 +795,7 @@ class FlatCAMDraw(QtCore.QObject):
             self.storage.insert(shape)
             self.storage.insert(shape)
 
 
     def deactivate(self):
     def deactivate(self):
+        self.disconnect_canvas_event_handlers()
         self.clear()
         self.clear()
         self.drawing_toolbar.setDisabled(True)
         self.drawing_toolbar.setDisabled(True)
         self.snap_toolbar.setDisabled(True)  # TODO: Combine and move into tool
         self.snap_toolbar.setDisabled(True)  # TODO: Combine and move into tool
@@ -735,9 +843,13 @@ class FlatCAMDraw(QtCore.QObject):
         :param fcgeometry: FlatCAMGeometry
         :param fcgeometry: FlatCAMGeometry
         :return: None
         :return: None
         """
         """
-        assert isinstance(fcgeometry, Geometry)
+        assert isinstance(fcgeometry, Geometry), \
+            "Expected a Geometry, got %s" % type(fcgeometry)
 
 
-        self.clear()
+        self.deactivate()
+
+        self.connect_canvas_event_handlers()
+        self.select_tool("select")
 
 
         # Link shapes into editor.
         # Link shapes into editor.
         for shape in fcgeometry.flatten():
         for shape in fcgeometry.flatten():
@@ -748,6 +860,10 @@ class FlatCAMDraw(QtCore.QObject):
         self.drawing_toolbar.setDisabled(False)
         self.drawing_toolbar.setDisabled(False)
         self.snap_toolbar.setDisabled(False)
         self.snap_toolbar.setDisabled(False)
 
 
+    def on_buffer_tool(self):
+        buff_tool = BufferSelectionTool(self.app, self)
+        buff_tool.run()
+
     def on_tool_select(self, tool):
     def on_tool_select(self, tool):
         """
         """
         Behavior of the toolbar. Tool initialization.
         Behavior of the toolbar. Tool initialization.
@@ -780,7 +896,8 @@ class FlatCAMDraw(QtCore.QObject):
         :param event: Event object dispatched by Matplotlib
         :param event: Event object dispatched by Matplotlib
         :return: None
         :return: None
         """
         """
-        if self.active_tool is not None:
+        # Selection with left mouse button
+        if self.active_tool is not None and event.button is 1:
             # Dispatch event to active_tool
             # Dispatch event to active_tool
             msg = self.active_tool.click(self.snap(event.xdata, event.ydata))
             msg = self.active_tool.click(self.snap(event.xdata, event.ydata))
             self.app.info(msg)
             self.app.info(msg)
@@ -895,19 +1012,21 @@ class FlatCAMDraw(QtCore.QObject):
                 self.active_tool.make()
                 self.active_tool.make()
                 if self.active_tool.complete:
                 if self.active_tool.complete:
                     self.on_shape_complete()
                     self.on_shape_complete()
+                    self.app.info("Done.")
             return
             return
 
 
         ### Abort the current action
         ### Abort the current action
         if event.key == 'escape':
         if event.key == 'escape':
             # TODO: ...?
             # TODO: ...?
-            self.on_tool_select("select")
+            #self.on_tool_select("select")
             self.app.info("Cancelled.")
             self.app.info("Cancelled.")
 
 
             self.delete_utility_geometry()
             self.delete_utility_geometry()
 
 
             self.replot()
             self.replot()
-            self.select_btn.setChecked(True)
-            self.on_tool_select('select')
+            # self.select_btn.setChecked(True)
+            # self.on_tool_select('select')
+            self.select_tool('select')
             return
             return
 
 
         ### Delete selected object
         ### Delete selected object
@@ -920,12 +1039,14 @@ class FlatCAMDraw(QtCore.QObject):
             self.move_btn.setChecked(True)
             self.move_btn.setChecked(True)
             self.on_tool_select('move')
             self.on_tool_select('move')
             self.active_tool.set_origin(self.snap(event.xdata, event.ydata))
             self.active_tool.set_origin(self.snap(event.xdata, event.ydata))
+            self.app.info("Click on target point.")
 
 
         ### Copy
         ### Copy
         if event.key == 'c':
         if event.key == 'c':
             self.copy_btn.setChecked(True)
             self.copy_btn.setChecked(True)
             self.on_tool_select('copy')
             self.on_tool_select('copy')
             self.active_tool.set_origin(self.snap(event.xdata, event.ydata))
             self.active_tool.set_origin(self.snap(event.xdata, event.ydata))
+            self.app.info("Click on target point.")
 
 
         ### Snap
         ### Snap
         if event.key == 'g':
         if event.key == 'g':
@@ -933,8 +1054,14 @@ class FlatCAMDraw(QtCore.QObject):
         if event.key == 'k':
         if event.key == 'k':
             self.corner_snap_btn.trigger()
             self.corner_snap_btn.trigger()
 
 
+        ### Buffer
+        if event.key == 'b':
+            self.on_buffer_tool()
+
         ### Propagate to tool
         ### Propagate to tool
-        response = self.active_tool.on_key(event.key)
+        response = None
+        if self.active_tool is not None:
+            response = self.active_tool.on_key(event.key)
         if response is not None:
         if response is not None:
             self.app.info(response)
             self.app.info(response)
 
 
@@ -1081,6 +1208,16 @@ class FlatCAMDraw(QtCore.QObject):
 
 
         return storage
         return storage
 
 
+    def select_tool(self, toolname):
+        """
+        Selects a drawing tool. Impacts the object and GUI.
+
+        :param toolname: Name of the tool.
+        :return: None
+        """
+        self.tools[toolname]["button"].setChecked(True)
+        self.on_tool_select(toolname)
+
     def set_selected(self, shape):
     def set_selected(self, shape):
 
 
         # Remove and add to the end.
         # Remove and add to the end.
@@ -1171,6 +1308,32 @@ class FlatCAMDraw(QtCore.QObject):
 
 
         self.replot()
         self.replot()
 
 
+    def intersection(self):
+        """
+        Makes intersectino of selected polygons. Original polygons are deleted.
+
+        :return: None
+        """
+
+        shapes = self.get_selected()
+
+        results = shapes[0].geo
+
+        for shape in shapes[1:]:
+            results = results.intersection(shape.geo)
+
+        # Delete originals.
+        for_deletion = [s for s in self.get_selected()]
+        for shape in for_deletion:
+            self.delete_shape(shape)
+
+        # Selected geometry is now gone!
+        self.selected = []
+
+        self.add_shape(DrawToolShape(results))
+
+        self.replot()
+
     def subtract(self):
     def subtract(self):
         selected = self.get_selected()
         selected = self.get_selected()
         tools = selected[1:]
         tools = selected[1:]
@@ -1182,6 +1345,23 @@ class FlatCAMDraw(QtCore.QObject):
 
 
         self.replot()
         self.replot()
 
 
+    def buffer(self, buf_distance):
+        selected = self.get_selected()
+
+        if len(selected) == 0:
+            self.app.inform.emit("[warning] Nothing selected for buffering.")
+            return
+
+        if not isinstance(buf_distance, float):
+            self.app.inform.emit("[warning] Invalid distance for buffering.")
+            return
+
+        pre_buffer = cascaded_union([t.geo for t in selected])
+        results = pre_buffer.buffer(buf_distance)
+        self.add_shape(DrawToolShape(results))
+
+        self.replot()
+
 
 
 def distance(pt1, pt2):
 def distance(pt1, pt2):
     return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)
     return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)

+ 99 - 6
FlatCAMGUI.py

@@ -4,6 +4,9 @@ from GUIElements import *
 
 
 class FlatCAMGUI(QtGui.QMainWindow):
 class FlatCAMGUI(QtGui.QMainWindow):
 
 
+    # Emitted when persistent window geometry needs to be retained
+    geom_update = QtCore.pyqtSignal(int, int, int, int, name='geomUpdate')
+
     def __init__(self, version):
     def __init__(self, version):
         super(FlatCAMGUI, self).__init__()
         super(FlatCAMGUI, self).__init__()
 
 
@@ -25,7 +28,7 @@ class FlatCAMGUI(QtGui.QMainWindow):
         # Recent
         # Recent
         self.recent = self.menufile.addMenu(QtGui.QIcon('share/folder16.png'), "Open recent ...")
         self.recent = self.menufile.addMenu(QtGui.QIcon('share/folder16.png'), "Open recent ...")
 
 
-        # Open gerber
+        # Open gerber ...
         self.menufileopengerber = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open &Gerber ...', self)
         self.menufileopengerber = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open &Gerber ...', self)
         self.menufile.addAction(self.menufileopengerber)
         self.menufile.addAction(self.menufileopengerber)
 
 
@@ -41,6 +44,10 @@ class FlatCAMGUI(QtGui.QMainWindow):
         self.menufileopenproject = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open &Project ...', self)
         self.menufileopenproject = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open &Project ...', self)
         self.menufile.addAction(self.menufileopenproject)
         self.menufile.addAction(self.menufileopenproject)
 
 
+        # Import SVG ...
+        self.menufileimportsvg = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Import &SVG ...', self)
+        self.menufile.addAction(self.menufileimportsvg)
+
         # Save Project
         # Save Project
         self.menufilesaveproject = QtGui.QAction(QtGui.QIcon('share/floppy16.png'), '&Save Project', self)
         self.menufilesaveproject = QtGui.QAction(QtGui.QIcon('share/floppy16.png'), '&Save Project', self)
         self.menufile.addAction(self.menufilesaveproject)
         self.menufile.addAction(self.menufilesaveproject)
@@ -70,6 +77,7 @@ class FlatCAMGUI(QtGui.QMainWindow):
         self.menueditnew = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), 'New Geometry')
         self.menueditnew = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), 'New Geometry')
         self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share/edit16.png'), 'Edit Geometry')
         self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share/edit16.png'), 'Edit Geometry')
         self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), 'Update Geometry')
         self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), 'Update Geometry')
+        #self.menueditok.
         #self.menueditcancel = self.menuedit.addAction(QtGui.QIcon('share/cancel_edit16.png'), "Cancel Edit")
         #self.menueditcancel = self.menuedit.addAction(QtGui.QIcon('share/cancel_edit16.png'), "Cancel Edit")
         self.menueditjoin = self.menuedit.addAction(QtGui.QIcon('share/join16.png'), 'Join Geometry')
         self.menueditjoin = self.menuedit.addAction(QtGui.QIcon('share/join16.png'), 'Join Geometry')
         self.menueditdelete = self.menuedit.addAction(QtGui.QIcon('share/trash16.png'), 'Delete')
         self.menueditdelete = self.menuedit.addAction(QtGui.QIcon('share/trash16.png'), 'Delete')
@@ -92,7 +100,9 @@ class FlatCAMGUI(QtGui.QMainWindow):
         self.menuviewenable = self.menuview.addAction(QtGui.QIcon('share/replot16.png'), 'Enable all plots')
         self.menuviewenable = self.menuview.addAction(QtGui.QIcon('share/replot16.png'), 'Enable all plots')
 
 
         ### Tool ###
         ### Tool ###
-        self.menutool = self.menu.addMenu('&Tool')
+        #self.menutool = self.menu.addMenu('&Tool')
+        self.menutool = QtGui.QMenu('&Tool')
+        self.menutoolaction = self.menu.addMenu(self.menutool)
         self.menutoolshell = self.menutool.addAction(QtGui.QIcon('share/shell16.png'), '&Command Line')
         self.menutoolshell = self.menutool.addAction(QtGui.QIcon('share/shell16.png'), '&Command Line')
 
 
         ### Help ###
         ### Help ###
@@ -115,6 +125,7 @@ class FlatCAMGUI(QtGui.QMainWindow):
         self.newgeo_btn = self.toolbar.addAction(QtGui.QIcon('share/new_geo32.png'), "New Blank Geometry")
         self.newgeo_btn = self.toolbar.addAction(QtGui.QIcon('share/new_geo32.png'), "New Blank Geometry")
         self.editgeo_btn = self.toolbar.addAction(QtGui.QIcon('share/edit32.png'), "Edit Geometry")
         self.editgeo_btn = self.toolbar.addAction(QtGui.QIcon('share/edit32.png'), "Edit Geometry")
         self.updategeo_btn = self.toolbar.addAction(QtGui.QIcon('share/edit_ok32.png'), "Update Geometry")
         self.updategeo_btn = self.toolbar.addAction(QtGui.QIcon('share/edit_ok32.png'), "Update Geometry")
+        self.updategeo_btn.setEnabled(False)
         #self.canceledit_btn = self.toolbar.addAction(QtGui.QIcon('share/cancel_edit32.png'), "Cancel Edit")
         #self.canceledit_btn = self.toolbar.addAction(QtGui.QIcon('share/cancel_edit32.png'), "Cancel Edit")
         self.delete_btn = self.toolbar.addAction(QtGui.QIcon('share/delete32.png'), "&Delete")
         self.delete_btn = self.toolbar.addAction(QtGui.QIcon('share/delete32.png'), "&Delete")
         self.shell_btn = self.toolbar.addAction(QtGui.QIcon('share/shell32.png'), "&Command Line")
         self.shell_btn = self.toolbar.addAction(QtGui.QIcon('share/shell32.png'), "&Command Line")
@@ -191,7 +202,6 @@ class FlatCAMGUI(QtGui.QMainWindow):
         # self.right_layout.setContentsMargins(0, 0, 0, 0)
         # self.right_layout.setContentsMargins(0, 0, 0, 0)
         right_widget.setLayout(self.right_layout)
         right_widget.setLayout(self.right_layout)
 
 
-
         ################
         ################
         ### Info bar ###
         ### Info bar ###
         ################
         ################
@@ -216,7 +226,10 @@ class FlatCAMGUI(QtGui.QMainWindow):
         self.progress_bar = QtGui.QProgressBar()
         self.progress_bar = QtGui.QProgressBar()
         self.progress_bar.setMinimum(0)
         self.progress_bar.setMinimum(0)
         self.progress_bar.setMaximum(100)
         self.progress_bar.setMaximum(100)
-        infobar.addWidget(self.progress_bar)
+        #infobar.addWidget(self.progress_bar)
+
+        self.activity_view = FlatCAMActivityView()
+        infobar.addWidget(self.activity_view)
 
 
         #############
         #############
         ### Icons ###
         ### Icons ###
@@ -231,13 +244,48 @@ class FlatCAMGUI(QtGui.QMainWindow):
         self.setWindowIcon(self.app_icon)
         self.setWindowIcon(self.app_icon)
 
 
         self.setGeometry(100, 100, 1024, 650)
         self.setGeometry(100, 100, 1024, 650)
-        self.setWindowTitle('FlatCAM %s' % version)
+        self.setWindowTitle('FlatCAM %s - Development Version' % version)
         self.show()
         self.show()
 
 
     def closeEvent(self, event):
     def closeEvent(self, event):
+        grect = self.geometry()
+        self.geom_update.emit(grect.x(), grect.y(), grect.width(), grect.height())
         QtGui.qApp.quit()
         QtGui.qApp.quit()
 
 
 
 
+class FlatCAMActivityView(QtGui.QWidget):
+
+    def __init__(self, parent=None):
+        super(FlatCAMActivityView, self).__init__(parent=parent)
+
+        self.setMinimumWidth(200)
+
+        self.icon = QtGui.QLabel(self)
+        self.icon.setGeometry(0, 0, 12, 12)
+        self.movie = QtGui.QMovie("share/active.gif")
+        self.icon.setMovie(self.movie)
+        #self.movie.start()
+
+        layout = QtGui.QHBoxLayout()
+        layout.setContentsMargins(5, 0, 5, 0)
+        layout.setAlignment(QtCore.Qt.AlignLeft)
+        self.setLayout(layout)
+
+        layout.addWidget(self.icon)
+        self.text = QtGui.QLabel(self)
+        self.text.setText("Idle.")
+
+        layout.addWidget(self.text)
+
+    def set_idle(self):
+        self.movie.stop()
+        self.text.setText("Idle.")
+
+    def set_busy(self, msg):
+        self.movie.start()
+        self.text.setText(msg)
+
+
 class FlatCAMInfoBar(QtGui.QWidget):
 class FlatCAMInfoBar(QtGui.QWidget):
 
 
     def __init__(self, parent=None):
     def __init__(self, parent=None):
@@ -256,6 +304,7 @@ class FlatCAMInfoBar(QtGui.QWidget):
 
 
         self.text = QtGui.QLabel(self)
         self.text = QtGui.QLabel(self)
         self.text.setText("Hello!")
         self.text.setText("Hello!")
+        self.text.setToolTip("Hello!")
 
 
         layout.addWidget(self.text)
         layout.addWidget(self.text)
 
 
@@ -263,6 +312,7 @@ class FlatCAMInfoBar(QtGui.QWidget):
 
 
     def set_text_(self, text):
     def set_text_(self, text):
         self.text.setText(text)
         self.text.setText(text)
+        self.text.setToolTip(text)
 
 
     def set_status(self, text, level="info"):
     def set_status(self, text, level="info"):
         level = str(level)
         level = str(level)
@@ -361,6 +411,12 @@ class GerberOptionsGroupUI(OptionsGroupUI):
         grid1.addWidget(overlabel, 2, 0)
         grid1.addWidget(overlabel, 2, 0)
         self.iso_overlap_entry = FloatEntry()
         self.iso_overlap_entry = FloatEntry()
         grid1.addWidget(self.iso_overlap_entry, 2, 1)
         grid1.addWidget(self.iso_overlap_entry, 2, 1)
+        
+        self.combine_passes_cb = FCCheckBox(label='Combine Passes')
+        self.combine_passes_cb.setToolTip(
+            "Combine all passes into one object"
+        )
+        grid1.addWidget(self.combine_passes_cb, 3, 0)
 
 
         ## Board cuttout
         ## Board cuttout
         self.board_cutout_label = QtGui.QLabel("<b>Board cutout:</b>")
         self.board_cutout_label = QtGui.QLabel("<b>Board cutout:</b>")
@@ -530,6 +586,23 @@ class ExcellonOptionsGroupUI(OptionsGroupUI):
         self.feedrate_entry = LengthEntry()
         self.feedrate_entry = LengthEntry()
         grid1.addWidget(self.feedrate_entry, 2, 1)
         grid1.addWidget(self.feedrate_entry, 2, 1)
 
 
+        toolchangezlabel = QtGui.QLabel('Toolchange Z:')
+        toolchangezlabel.setToolTip(
+            "Tool Z where user can change drill bit\n"
+        )
+        grid1.addWidget(toolchangezlabel, 3, 0)
+        self.toolchangez_entry = LengthEntry()
+        grid1.addWidget(self.toolchangez_entry, 3, 1)
+
+        spdlabel = QtGui.QLabel('Spindle speed:')
+        spdlabel.setToolTip(
+            "Speed of the spindle\n"
+            "in RPM (optional)"
+        )
+        grid1.addWidget(spdlabel, 4, 0)
+        self.spindlespeed_entry = IntEntry(allow_empty=True)
+        grid1.addWidget(self.spindlespeed_entry, 4, 1)
+
 
 
 class GeometryOptionsGroupUI(OptionsGroupUI):
 class GeometryOptionsGroupUI(OptionsGroupUI):
     def __init__(self, parent=None):
     def __init__(self, parent=None):
@@ -597,6 +670,15 @@ class GeometryOptionsGroupUI(OptionsGroupUI):
         self.cnctooldia_entry = LengthEntry()
         self.cnctooldia_entry = LengthEntry()
         grid1.addWidget(self.cnctooldia_entry, 3, 1)
         grid1.addWidget(self.cnctooldia_entry, 3, 1)
 
 
+        spdlabel = QtGui.QLabel('Spindle speed:')
+        spdlabel.setToolTip(
+            "Speed of the spindle\n"
+            "in RPM (optional)"
+        )
+        grid1.addWidget(spdlabel, 4, 0)
+        self.cncspindlespeed_entry = IntEntry(allow_empty=True)
+        grid1.addWidget(self.cncspindlespeed_entry, 4, 1)
+
         ## Paint area
         ## Paint area
         self.paint_label = QtGui.QLabel('<b>Paint Area:</b>')
         self.paint_label = QtGui.QLabel('<b>Paint Area:</b>')
         self.paint_label.setToolTip(
         self.paint_label.setToolTip(
@@ -680,7 +762,18 @@ class CNCJobOptionsGroupUI(OptionsGroupUI):
         )
         )
         self.layout.addWidget(self.export_gcode_label)
         self.layout.addWidget(self.export_gcode_label)
 
 
-        # Append text to Gerber
+        # Prepend to G-Code
+        prependlabel = QtGui.QLabel('Prepend to G-Code:')
+        prependlabel.setToolTip(
+            "Type here any G-Code commands you would\n"
+            "like to add at the beginning of the G-Code file."
+        )
+        self.layout.addWidget(prependlabel)
+
+        self.prepend_text = FCTextArea()
+        self.layout.addWidget(self.prepend_text)
+
+        # Append text to G-Code
         appendlabel = QtGui.QLabel('Append to G-Code:')
         appendlabel = QtGui.QLabel('Append to G-Code:')
         appendlabel.setToolTip(
         appendlabel.setToolTip(
             "Type here any G-Code commands you would\n"
             "Type here any G-Code commands you would\n"

+ 269 - 119
FlatCAMObj.py

@@ -1,4 +1,5 @@
 from PyQt4 import QtCore
 from PyQt4 import QtCore
+from copy import copy
 from ObjectUI import *
 from ObjectUI import *
 import FlatCAMApp
 import FlatCAMApp
 import inspect  # TODO: For debugging only.
 import inspect  # TODO: For debugging only.
@@ -25,8 +26,6 @@ class FlatCAMObj(QtCore.QObject):
         """
         """
 
 
         :param name: Name of the object given by the user.
         :param name: Name of the object given by the user.
-        :param ui: User interface to interact with the object.
-        :type ui: ObjectUI
         :return: FlatCAMObj
         :return: FlatCAMObj
         """
         """
         QtCore.QObject.__init__(self)
         QtCore.QObject.__init__(self)
@@ -140,7 +139,8 @@ class FlatCAMObj(QtCore.QObject):
 
 
     def build_ui(self):
     def build_ui(self):
         """
         """
-        Sets up the UI/form for this object.
+        Sets up the UI/form for this object. Show the UI
+        in the App.
 
 
         :return: None
         :return: None
         :rtype: None
         :rtype: None
@@ -219,7 +219,6 @@ class FlatCAMObj(QtCore.QObject):
 
 
         # Clear axes or we will plot on top of them.
         # Clear axes or we will plot on top of them.
         self.axes.cla()  # TODO: Thread safe?
         self.axes.cla()  # TODO: Thread safe?
-        # GLib.idle_add(self.axes.cla)
         return True
         return True
 
 
     def serialize(self):
     def serialize(self):
@@ -265,6 +264,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
             "isotooldia": 0.016,
             "isotooldia": 0.016,
             "isopasses": 1,
             "isopasses": 1,
             "isooverlap": 0.15,
             "isooverlap": 0.15,
+            "combine_passes": True,
             "cutouttooldia": 0.07,
             "cutouttooldia": 0.07,
             "cutoutmargin": 0.2,
             "cutoutmargin": 0.2,
             "cutoutgapsize": 0.15,
             "cutoutgapsize": 0.15,
@@ -290,6 +290,14 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
         # self.ui.generate_noncopper_button.clicked.connect(self.on_generatenoncopper_button_click)
         # self.ui.generate_noncopper_button.clicked.connect(self.on_generatenoncopper_button_click)
 
 
     def set_ui(self, ui):
     def set_ui(self, ui):
+        """
+        Maps options with GUI inputs.
+        Connects GUI events to methods.
+
+        :param ui: GUI object.
+        :type ui: GerberObjectUI
+        :return: None
+        """
         FlatCAMObj.set_ui(self, ui)
         FlatCAMObj.set_ui(self, ui)
 
 
         FlatCAMApp.App.log.debug("FlatCAMGerber.set_ui()")
         FlatCAMApp.App.log.debug("FlatCAMGerber.set_ui()")
@@ -301,6 +309,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
             "isotooldia": self.ui.iso_tool_dia_entry,
             "isotooldia": self.ui.iso_tool_dia_entry,
             "isopasses": self.ui.iso_width_entry,
             "isopasses": self.ui.iso_width_entry,
             "isooverlap": self.ui.iso_overlap_entry,
             "isooverlap": self.ui.iso_overlap_entry,
+            "combine_passes":self.ui.combine_passes_cb,
             "cutouttooldia": self.ui.cutout_tooldia_entry,
             "cutouttooldia": self.ui.cutout_tooldia_entry,
             "cutoutmargin": self.ui.cutout_margin_entry,
             "cutoutmargin": self.ui.cutout_margin_entry,
             "cutoutgapsize": self.ui.cutout_gap_entry,
             "cutoutgapsize": self.ui.cutout_gap_entry,
@@ -418,7 +427,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
         # TODO: Do something if this is None. Offer changing name?
         # TODO: Do something if this is None. Offer changing name?
         self.app.new_object("geometry", follow_name, follow_init)
         self.app.new_object("geometry", follow_name, follow_init)
 
 
-    def isolate(self, dia=None, passes=None, overlap=None, outname=None):
+    def isolate(self, dia=None, passes=None, overlap=None, outname=None, combine=None):
         """
         """
         Creates an isolation routing geometry object in the project.
         Creates an isolation routing geometry object in the project.
 
 
@@ -428,36 +437,76 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
         :param outname: Base name of the output object
         :param outname: Base name of the output object
         :return: None
         :return: None
         """
         """
-
         if dia is None:
         if dia is None:
             dia = self.options["isotooldia"]
             dia = self.options["isotooldia"]
         if passes is None:
         if passes is None:
             passes = int(self.options["isopasses"])
             passes = int(self.options["isopasses"])
         if overlap is None:
         if overlap is None:
-            overlap = self.options["isooverlap"] * dia
+            overlap = self.options["isooverlap"]
+        if combine is None:
+            combine = self.options["combine_passes"]
+        else:
+            combine = bool(combine)
 
 
         base_name = self.options["name"] + "_iso"
         base_name = self.options["name"] + "_iso"
         base_name = outname or base_name
         base_name = outname or base_name
 
 
-        for i in range(passes):
-
-            offset = (2*i + 1)/2.0 * dia - i*overlap
-            if passes > 1:
-                iso_name = base_name + str(i+1)
-            else:
-                iso_name = base_name
+        def generate_envelope(offset, invert):
+            # isolation_geometry produces an envelope that is going on the left of the geometry
+            # (the copper features). To leave the least amount of burrs on the features
+            # the tool needs to travel on the right side of the features (this is called conventional milling)
+            # the first pass is the one cutting all of the features, so it needs to be reversed
+            # the other passes overlap preceding ones and cut the left over copper. It is better for them
+            # to cut on the right side of the left over copper i.e on the left side of the features. 
+            geom = self.isolation_geometry(offset)
+            if invert:
+                if type(geom) is MultiPolygon:
+                    pl = []
+                    for p in geom:
+                        pl.append(Polygon(p.exterior.coords[::-1], p.interiors))
+                    geom = MultiPolygon(pl)
+                elif type(geom) is Polygon:
+                    geom = Polygon(geom.exterior.coords[::-1], geom.interiors)
+                else:
+                    raise "Unexpected Geometry"
+            return geom
+
+        if combine:
+            iso_name = base_name
 
 
             # TODO: This is ugly. Create way to pass data into init function.
             # TODO: This is ugly. Create way to pass data into init function.
             def iso_init(geo_obj, app_obj):
             def iso_init(geo_obj, app_obj):
                 # Propagate options
                 # Propagate options
                 geo_obj.options["cnctooldia"] = self.options["isotooldia"]
                 geo_obj.options["cnctooldia"] = self.options["isotooldia"]
-
-                geo_obj.solid_geometry = self.isolation_geometry(offset)
+                geo_obj.solid_geometry = []
+                for i in range(passes):
+                    offset = (2 * i + 1) / 2.0 * dia - i * overlap * dia
+                    geom = generate_envelope (offset, i == 0)
+                    geo_obj.solid_geometry.append(geom)
                 app_obj.info("Isolation geometry created: %s" % geo_obj.options["name"])
                 app_obj.info("Isolation geometry created: %s" % geo_obj.options["name"])
 
 
             # TODO: Do something if this is None. Offer changing name?
             # TODO: Do something if this is None. Offer changing name?
             self.app.new_object("geometry", iso_name, iso_init)
             self.app.new_object("geometry", iso_name, iso_init)
 
 
+        else:
+            for i in range(passes):
+
+                offset = (2 * i + 1) / 2.0 * dia - i * overlap  * dia
+                if passes > 1:
+                    iso_name = base_name + str(i + 1)
+                else:
+                    iso_name = base_name
+
+                # TODO: This is ugly. Create way to pass data into init function.
+                def iso_init(geo_obj, app_obj):
+                    # Propagate options
+                    geo_obj.options["cnctooldia"] = self.options["isotooldia"]
+                    geo_obj.solid_geometry = generate_envelope (offset, i == 0)
+                    app_obj.info("Isolation geometry created: %s" % geo_obj.options["name"])
+
+                # TODO: Do something if this is None. Offer changing name?
+                self.app.new_object("geometry", iso_name, iso_init)
+
     def on_plot_cb_click(self, *args):
     def on_plot_cb_click(self, *args):
         if self.muted_ui:
         if self.muted_ui:
             return
             return
@@ -567,7 +616,10 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             "travelz": 0.1,
             "travelz": 0.1,
             "feedrate": 5.0,
             "feedrate": 5.0,
             # "toolselection": ""
             # "toolselection": ""
-            "tooldia": 0.1
+            "tooldia": 0.1,
+            "toolchange": False,
+            "toolchangez": 1.0,
+            "spindlespeed": None
         })
         })
 
 
         # TODO: Document this.
         # TODO: Document this.
@@ -596,6 +648,12 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             dia.setFlags(QtCore.Qt.ItemIsEnabled)
             dia.setFlags(QtCore.Qt.ItemIsEnabled)
             self.ui.tools_table.setItem(i, 1, dia)  # Diameter
             self.ui.tools_table.setItem(i, 1, dia)  # Diameter
             i += 1
             i += 1
+        
+        # sort the tool diameter column
+        self.ui.tools_table.sortItems(1)
+        # all the tools are selected by default
+        self.ui.tools_table.selectColumn(0)
+        
         self.ui.tools_table.resizeColumnsToContents()
         self.ui.tools_table.resizeColumnsToContents()
         self.ui.tools_table.resizeRowsToContents()
         self.ui.tools_table.resizeRowsToContents()
         self.ui.tools_table.horizontalHeader().setStretchLastSection(True)
         self.ui.tools_table.horizontalHeader().setStretchLastSection(True)
@@ -603,6 +661,14 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         self.ui.tools_table.setSortingEnabled(True)
         self.ui.tools_table.setSortingEnabled(True)
 
 
     def set_ui(self, ui):
     def set_ui(self, ui):
+        """
+        Configures the user interface for this object.
+        Connects options to form fields.
+
+        :param ui: User interface object.
+        :type ui: ExcellonObjectUI
+        :return: None
+        """
         FlatCAMObj.set_ui(self, ui)
         FlatCAMObj.set_ui(self, ui)
 
 
         FlatCAMApp.App.log.debug("FlatCAMExcellon.set_ui()")
         FlatCAMApp.App.log.debug("FlatCAMExcellon.set_ui()")
@@ -613,14 +679,16 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             "drillz": self.ui.cutz_entry,
             "drillz": self.ui.cutz_entry,
             "travelz": self.ui.travelz_entry,
             "travelz": self.ui.travelz_entry,
             "feedrate": self.ui.feedrate_entry,
             "feedrate": self.ui.feedrate_entry,
-            # "toolselection": self.ui.tools_entry
-            "tooldia": self.ui.tooldia_entry
+            "tooldia": self.ui.tooldia_entry,
+            "toolchange": self.ui.toolchange_cb,
+            "toolchangez": self.ui.toolchangez_entry,
+            "spindlespeed": self.ui.spindlespeed_entry
         })
         })
 
 
-        assert isinstance(self.ui, ExcellonObjectUI)
+        assert isinstance(self.ui, ExcellonObjectUI), \
+            "Expected a ExcellonObjectUI, got %s" % type(self.ui)
         self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
         self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
         self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click)
         self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click)
-        # self.ui.choose_tools_button.clicked.connect(self.show_tool_chooser)
         self.ui.generate_cnc_button.clicked.connect(self.on_create_cncjob_button_click)
         self.ui.generate_cnc_button.clicked.connect(self.on_create_cncjob_button_click)
         self.ui.generate_milling_button.clicked.connect(self.on_generate_milling_button_click)
         self.ui.generate_milling_button.clicked.connect(self.on_generate_milling_button_click)
 
 
@@ -628,30 +696,46 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         """
         """
         Returns the keys to the self.tools dictionary corresponding
         Returns the keys to the self.tools dictionary corresponding
         to the selections on the tool list in the GUI.
         to the selections on the tool list in the GUI.
+
+        :return: List of tools.
+        :rtype: list
         """
         """
         return [str(x.text()) for x in self.ui.tools_table.selectedItems()]
         return [str(x.text()) for x in self.ui.tools_table.selectedItems()]
 
 
-    def on_generate_milling_button_click(self, *args):
-        self.app.report_usage("excellon_on_create_milling_button")
-        self.read_form()
+    def generate_milling(self, tools=None, outname=None, tooldia=None):
+        """
+        Note: This method is a good template for generic operations as
+        it takes it's options from parameters or otherwise from the
+        object's options and returns a success, msg tuple as feedback
+        for shell operations.
+
+        :return: Success/failure condition tuple (bool, str).
+        :rtype: tuple
+        """
 
 
         # Get the tools from the list. These are keys
         # Get the tools from the list. These are keys
         # to self.tools
         # to self.tools
-        tools = self.get_selected_tools_list()
+        if tools is None:
+            tools = self.get_selected_tools_list()
+
+        if outname is None:
+            outname = self.options["name"] + "_mill"
+
+        if tooldia is None:
+            tooldia = self.options["tooldia"]
 
 
         if len(tools) == 0:
         if len(tools) == 0:
             self.app.inform.emit("Please select one or more tools from the list and try again.")
             self.app.inform.emit("Please select one or more tools from the list and try again.")
-            return
+            return False, "Error: No tools."
 
 
         for tool in tools:
         for tool in tools:
-            if self.tools[tool]["C"] < self.options["tooldia"]:
+            if self.tools[tool]["C"] < tooldia:
                 self.app.inform.emit("[warning] Milling tool is larger than hole size. Cancelled.")
                 self.app.inform.emit("[warning] Milling tool is larger than hole size. Cancelled.")
-                return
-
-        geo_name = self.options["name"] + "_mill"
+                return False, "Error: Milling tool is larger than hole."
 
 
         def geo_init(geo_obj, app_obj):
         def geo_init(geo_obj, app_obj):
-            assert isinstance(geo_obj, FlatCAMGeometry)
+            assert isinstance(geo_obj, FlatCAMGeometry), \
+                "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
             app_obj.progress.emit(20)
             app_obj.progress.emit(20)
 
 
             geo_obj.solid_geometry = []
             geo_obj.solid_geometry = []
@@ -660,17 +744,27 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                 if hole['tool'] in tools:
                 if hole['tool'] in tools:
                     geo_obj.solid_geometry.append(
                     geo_obj.solid_geometry.append(
                         Point(hole['point']).buffer(self.tools[hole['tool']]["C"] / 2 -
                         Point(hole['point']).buffer(self.tools[hole['tool']]["C"] / 2 -
-                                                    self.options["tooldia"] / 2).exterior
+                                                    tooldia / 2).exterior
                     )
                     )
 
 
         def geo_thread(app_obj):
         def geo_thread(app_obj):
-            app_obj.new_object("geometry", geo_name, geo_init)
+            app_obj.new_object("geometry", outname, geo_init)
             app_obj.progress.emit(100)
             app_obj.progress.emit(100)
 
 
+        # Create a promise with the new name
+        self.app.collection.promise(outname)
+
         # Send to worker
         # Send to worker
-        # self.app.worker.add_task(job_thread, [self.app])
         self.app.worker_task.emit({'fcn': geo_thread, 'params': [self.app]})
         self.app.worker_task.emit({'fcn': geo_thread, 'params': [self.app]})
 
 
+        return True, ""
+
+    def on_generate_milling_button_click(self, *args):
+        self.app.report_usage("excellon_on_create_milling_button")
+        self.read_form()
+
+        self.generate_milling()
+
     def on_create_cncjob_button_click(self, *args):
     def on_create_cncjob_button_click(self, *args):
         self.app.report_usage("excellon_on_create_cncjob_button")
         self.app.report_usage("excellon_on_create_cncjob_button")
         self.read_form()
         self.read_form()
@@ -686,38 +780,38 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
 
 
         # Object initialization function for app.new_object()
         # Object initialization function for app.new_object()
         def job_init(job_obj, app_obj):
         def job_init(job_obj, app_obj):
-            assert isinstance(job_obj, FlatCAMCNCjob)
+            assert isinstance(job_obj, FlatCAMCNCjob), \
+                "Initializer expected a FlatCAMCNCjob, got %s" % type(job_obj)
 
 
-            # GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Creating CNC Job..."))
             app_obj.progress.emit(20)
             app_obj.progress.emit(20)
             job_obj.z_cut = self.options["drillz"]
             job_obj.z_cut = self.options["drillz"]
             job_obj.z_move = self.options["travelz"]
             job_obj.z_move = self.options["travelz"]
             job_obj.feedrate = self.options["feedrate"]
             job_obj.feedrate = self.options["feedrate"]
+            job_obj.spindlespeed = self.options["spindlespeed"]
             # There could be more than one drill size...
             # There could be more than one drill size...
             # job_obj.tooldia =   # TODO: duplicate variable!
             # job_obj.tooldia =   # TODO: duplicate variable!
             # job_obj.options["tooldia"] =
             # job_obj.options["tooldia"] =
 
 
             tools_csv = ','.join(tools)
             tools_csv = ','.join(tools)
-            # job_obj.generate_from_excellon_by_tool(self, self.options["toolselection"])
-            job_obj.generate_from_excellon_by_tool(self, tools_csv)
+            job_obj.generate_from_excellon_by_tool(self, tools_csv,
+                                                   toolchange=self.options["toolchange"],
+                                                   toolchangez=self.options["toolchangez"])
 
 
-            # GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Parsing G-Code..."))
             app_obj.progress.emit(50)
             app_obj.progress.emit(50)
             job_obj.gcode_parse()
             job_obj.gcode_parse()
 
 
-            # GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Creating New Geometry..."))
             app_obj.progress.emit(60)
             app_obj.progress.emit(60)
             job_obj.create_geometry()
             job_obj.create_geometry()
 
 
-            # GLib.idle_add(lambda: app_obj.set_progress_bar(0.8, "Plotting..."))
             app_obj.progress.emit(80)
             app_obj.progress.emit(80)
 
 
         # To be run in separate thread
         # To be run in separate thread
         def job_thread(app_obj):
         def job_thread(app_obj):
             app_obj.new_object("cncjob", job_name, job_init)
             app_obj.new_object("cncjob", job_name, job_init)
-            # GLib.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
             app_obj.progress.emit(100)
             app_obj.progress.emit(100)
-            # GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, ""))
+
+        # Create promise for the new name.
+        self.app.collection.promise(job_name)
 
 
         # Send to worker
         # Send to worker
         # self.app.worker.add_task(job_thread, [self.app])
         # self.app.worker.add_task(job_thread, [self.app])
@@ -772,33 +866,6 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                     self.axes.plot(x, y, 'g-')
                     self.axes.plot(x, y, 'g-')
 
 
         self.app.plotcanvas.auto_adjust_axes()
         self.app.plotcanvas.auto_adjust_axes()
-        # GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
-        # self.emit(QtCore.SIGNAL("plotChanged"), self)
-
-    def show_tool_chooser(self):
-        # win = Gtk.Window()
-        # box = Gtk.Box(spacing=2)
-        # box.set_orientation(Gtk.Orientation(1))
-        # win.add(box)
-        # for tool in self.tools:
-        #     self.tool_cbs[tool] = Gtk.CheckButton(label=tool + ": " + str(self.tools[tool]))
-        #     box.pack_start(self.tool_cbs[tool], False, False, 1)
-        # button = Gtk.Button(label="Accept")
-        # box.pack_start(button, False, False, 1)
-        # win.show_all()
-        #
-        # def on_accept(widget):
-        #     win.destroy()
-        #     tool_list = []
-        #     for toolx in self.tool_cbs:
-        #         if self.tool_cbs[toolx].get_active():
-        #             tool_list.append(toolx)
-        #     self.options["toolselection"] = ", ".join(tool_list)
-        #     self.to_form()
-        #
-        # button.connect("activate", on_accept)
-        # button.connect("clicked", on_accept)
-        return
 
 
 
 
 class FlatCAMCNCjob(FlatCAMObj, CNCjob):
 class FlatCAMCNCjob(FlatCAMObj, CNCjob):
@@ -809,10 +876,15 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
     ui_type = CNCObjectUI
     ui_type = CNCObjectUI
 
 
     def __init__(self, name, units="in", kind="generic", z_move=0.1,
     def __init__(self, name, units="in", kind="generic", z_move=0.1,
-                 feedrate=3.0, z_cut=-0.002, tooldia=0.0):
+                 feedrate=3.0, z_cut=-0.002, tooldia=0.0,
+                 spindlespeed=None):
+
         FlatCAMApp.App.log.debug("Creating CNCJob object...")
         FlatCAMApp.App.log.debug("Creating CNCJob object...")
+
         CNCjob.__init__(self, units=units, kind=kind, z_move=z_move,
         CNCjob.__init__(self, units=units, kind=kind, z_move=z_move,
-                        feedrate=feedrate, z_cut=z_cut, tooldia=tooldia)
+                        feedrate=feedrate, z_cut=z_cut, tooldia=tooldia,
+                        spindlespeed=spindlespeed)
+
         FlatCAMObj.__init__(self, name)
         FlatCAMObj.__init__(self, name)
 
 
         self.kind = "cncjob"
         self.kind = "cncjob"
@@ -820,7 +892,8 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         self.options.update({
         self.options.update({
             "plot": True,
             "plot": True,
             "tooldia": 0.4 / 25.4,  # 0.4mm in inches
             "tooldia": 0.4 / 25.4,  # 0.4mm in inches
-            "append": ""
+            "append": "",
+            "prepend": ""
         })
         })
 
 
         # Attributes to be included in serialization
         # Attributes to be included in serialization
@@ -833,12 +906,14 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
 
 
         FlatCAMApp.App.log.debug("FlatCAMCNCJob.set_ui()")
         FlatCAMApp.App.log.debug("FlatCAMCNCJob.set_ui()")
 
 
-        assert isinstance(self.ui, CNCObjectUI)
+        assert isinstance(self.ui, CNCObjectUI), \
+            "Expected a CNCObjectUI, got %s" % type(self.ui)
 
 
         self.form_fields.update({
         self.form_fields.update({
             "plot": self.ui.plot_cb,
             "plot": self.ui.plot_cb,
             "tooldia": self.ui.tooldia_entry,
             "tooldia": self.ui.tooldia_entry,
-            "append": self.ui.append_text
+            "append": self.ui.append_text,
+            "prepend": self.ui.prepend_text
         })
         })
 
 
         self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
         self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
@@ -862,16 +937,19 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         except TypeError:
         except TypeError:
             filename = QtGui.QFileDialog.getSaveFileName(caption="Export G-Code ...")
             filename = QtGui.QFileDialog.getSaveFileName(caption="Export G-Code ...")
 
 
+        preamble = str(self.ui.prepend_text.get_value())
         postamble = str(self.ui.append_text.get_value())
         postamble = str(self.ui.append_text.get_value())
 
 
-        self.export_gcode(filename, preamble='', postamble=postamble)
+        self.export_gcode(filename, preamble=preamble, postamble=postamble)
 
 
     def export_gcode(self, filename, preamble='', postamble=''):
     def export_gcode(self, filename, preamble='', postamble=''):
         f = open(filename, 'w')
         f = open(filename, 'w')
         f.write(preamble + '\n' + self.gcode + "\n" + postamble)
         f.write(preamble + '\n' + self.gcode + "\n" + postamble)
         f.close()
         f.close()
 
 
+        # Just for adding it to the recent files list.
         self.app.file_opened.emit("cncjob", filename)
         self.app.file_opened.emit("cncjob", filename)
+
         self.app.inform.emit("Saved to: " + filename)
         self.app.inform.emit("Saved to: " + filename)
 
 
     def on_plot_cb_click(self, *args):
     def on_plot_cb_click(self, *args):
@@ -915,14 +993,28 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         :param geo_final: Destination FlatCAMGeometry object.
         :param geo_final: Destination FlatCAMGeometry object.
         :return: None
         :return: None
         """
         """
-        geo_final.solid_geometry = []
+
+        if geo_final.solid_geometry is None:
+            geo_final.solid_geometry = []
+        if type(geo_final.solid_geometry) is not list:
+            geo_final.solid_geometry = [geo_final.solid_geometry]
 
 
         for geo in geo_list:
         for geo in geo_list:
-            try:
-                for shape in geo.solid_geometry:
-                    geo_final.solid_geometry.append(shape)
-            except:
-                geo_final.solid_geometry.append(geo)
+
+            # Expand lists
+            if type(geo) is list:
+                FlatCAMGeometry.merge(geo, geo_final)
+
+            # If not list, just append
+            else:
+                geo_final.solid_geometry.append(geo.solid_geometry)
+
+            # try:  # Iterable
+            #     for shape in geo.solid_geometry:
+            #         geo_final.solid_geometry.append(shape)
+            #
+            # except TypeError:  # Non-iterable
+            #     geo_final.solid_geometry.append(geo.solid_geometry)
 
 
     def __init__(self, name):
     def __init__(self, name):
         FlatCAMObj.__init__(self, name)
         FlatCAMObj.__init__(self, name)
@@ -932,16 +1024,17 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
 
 
         self.options.update({
         self.options.update({
             "plot": True,
             "plot": True,
-            # "solid": False,
-            # "multicolored": False,
             "cutz": -0.002,
             "cutz": -0.002,
             "travelz": 0.1,
             "travelz": 0.1,
             "feedrate": 5.0,
             "feedrate": 5.0,
+            "spindlespeed": None,
             "cnctooldia": 0.4 / 25.4,
             "cnctooldia": 0.4 / 25.4,
             "painttooldia": 0.0625,
             "painttooldia": 0.0625,
             "paintoverlap": 0.15,
             "paintoverlap": 0.15,
             "paintmargin": 0.01,
             "paintmargin": 0.01,
-            "paintmethod": "standard"
+            "paintmethod": "standard",
+            "multidepth": False,
+            "depthperpass": 0.002
         })
         })
 
 
         # Attributes to be included in serialization
         # Attributes to be included in serialization
@@ -957,20 +1050,22 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
 
 
         FlatCAMApp.App.log.debug("FlatCAMGeometry.set_ui()")
         FlatCAMApp.App.log.debug("FlatCAMGeometry.set_ui()")
 
 
-        assert isinstance(self.ui, GeometryObjectUI)
+        assert isinstance(self.ui, GeometryObjectUI), \
+            "Expected a GeometryObjectUI, got %s" % type(self.ui)
 
 
         self.form_fields.update({
         self.form_fields.update({
             "plot": self.ui.plot_cb,
             "plot": self.ui.plot_cb,
-            # "solid": self.ui.sol,
-            # "multicolored": self.ui.,
             "cutz": self.ui.cutz_entry,
             "cutz": self.ui.cutz_entry,
             "travelz": self.ui.travelz_entry,
             "travelz": self.ui.travelz_entry,
             "feedrate": self.ui.cncfeedrate_entry,
             "feedrate": self.ui.cncfeedrate_entry,
+            "spindlespeed": self.ui.cncspindlespeed_entry,
             "cnctooldia": self.ui.cnctooldia_entry,
             "cnctooldia": self.ui.cnctooldia_entry,
             "painttooldia": self.ui.painttooldia_entry,
             "painttooldia": self.ui.painttooldia_entry,
             "paintoverlap": self.ui.paintoverlap_entry,
             "paintoverlap": self.ui.paintoverlap_entry,
             "paintmargin": self.ui.paintmargin_entry,
             "paintmargin": self.ui.paintmargin_entry,
-            "paintmethod": self.ui.paintmethod_combo
+            "paintmethod": self.ui.paintmethod_combo,
+            "multidepth": self.ui.mpass_cb,
+            "depthperpass": self.ui.maxdepth_entry
         })
         })
 
 
         self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
         self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
@@ -990,6 +1085,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
 
 
         # To be called after clicking on the plot.
         # To be called after clicking on the plot.
         def doit(event):
         def doit(event):
+            self.app.info("Painting polygon...")
             self.app.plotcanvas.mpl_disconnect(subscription)
             self.app.plotcanvas.mpl_disconnect(subscription)
             point = [event.xdata, event.ydata]
             point = [event.xdata, event.ydata]
             self.paint_poly(point, tooldia, overlap)
             self.paint_poly(point, tooldia, overlap)
@@ -999,80 +1095,136 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
     def paint_poly(self, inside_pt, tooldia, overlap):
     def paint_poly(self, inside_pt, tooldia, overlap):
 
 
         # Which polygon.
         # Which polygon.
-        poly = find_polygon(self.solid_geometry, inside_pt)
+        #poly = find_polygon(self.solid_geometry, inside_pt)
+        poly = self.find_polygon(inside_pt)
 
 
         # No polygon?
         # No polygon?
         if poly is None:
         if poly is None:
             self.app.log.warning('No polygon found.')
             self.app.log.warning('No polygon found.')
-            self.app.inform('[warning] No polygon found.')
+            self.app.inform.emit('[warning] No polygon found.')
             return
             return
 
 
+        proc = self.app.proc_container.new("Painting polygon.")
+
+        name = self.options["name"] + "_paint"
+
         # Initializes the new geometry object
         # Initializes the new geometry object
         def gen_paintarea(geo_obj, app_obj):
         def gen_paintarea(geo_obj, app_obj):
-            assert isinstance(geo_obj, FlatCAMGeometry)
+            assert isinstance(geo_obj, FlatCAMGeometry), \
+                "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
             #assert isinstance(app_obj, App)
             #assert isinstance(app_obj, App)
 
 
-            cp = self.clear_polygon(poly.buffer(-self.options["paintmargin"]), tooldia, overlap=overlap)
             if self.options["paintmethod"] == "seed":
             if self.options["paintmethod"] == "seed":
-                cp = self.clear_polygon2(poly.buffer(-self.options["paintmargin"]), tooldia, overlap=overlap)
-            geo_obj.solid_geometry = cp
+                cp = self.clear_polygon2(poly.buffer(-self.options["paintmargin"]),
+                                         tooldia, overlap=overlap)
+
+            else:
+                cp = self.clear_polygon(poly.buffer(-self.options["paintmargin"]),
+                                        tooldia, overlap=overlap)
+
+            geo_obj.solid_geometry = list(cp.get_objects())
             geo_obj.options["cnctooldia"] = tooldia
             geo_obj.options["cnctooldia"] = tooldia
+            self.app.inform.emit("Done.")
 
 
-        name = self.options["name"] + "_paint"
-        self.app.new_object("geometry", name, gen_paintarea)
+        def job_thread(app_obj):
+            try:
+                app_obj.new_object("geometry", name, gen_paintarea)
+            except Exception as e:
+                proc.done()
+                raise e
+            proc.done()
+
+        self.app.inform.emit("Polygon Paint started ...")
+
+        # Promise object with the new name
+        self.app.collection.promise(name)
+
+        # Background
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
 
 
     def on_generatecnc_button_click(self, *args):
     def on_generatecnc_button_click(self, *args):
         self.app.report_usage("geometry_on_generatecnc_button")
         self.app.report_usage("geometry_on_generatecnc_button")
         self.read_form()
         self.read_form()
         self.generatecncjob()
         self.generatecncjob()
 
 
-    def generatecncjob(self, z_cut=None, z_move=None,
-                       feedrate=None, tooldia=None, outname=None):
+    def generatecncjob(self,
+                       z_cut=None,
+                       z_move=None,
+                       feedrate=None,
+                       tooldia=None,
+                       outname=None,
+                       spindlespeed=None,
+                       multidepth=None,
+                       depthperpass=None):
+        """
+        Creates a CNCJob out of this Geometry object. The actual
+        work is done by the target FlatCAMCNCjob object's
+        `generate_from_geometry_2()` method.
+
+        :param z_cut: Cut depth (negative)
+        :param z_move: Hight of the tool when travelling (not cutting)
+        :param feedrate: Feed rate while cutting
+        :param tooldia: Tool diameter
+        :param outname: Name of the new object
+        :param spindlespeed: Spindle speed (RPM)
+        :return: None
+        """
 
 
         outname = outname if outname is not None else self.options["name"] + "_cnc"
         outname = outname if outname is not None else self.options["name"] + "_cnc"
         z_cut = z_cut if z_cut is not None else self.options["cutz"]
         z_cut = z_cut if z_cut is not None else self.options["cutz"]
         z_move = z_move if z_move is not None else self.options["travelz"]
         z_move = z_move if z_move is not None else self.options["travelz"]
         feedrate = feedrate if feedrate is not None else self.options["feedrate"]
         feedrate = feedrate if feedrate is not None else self.options["feedrate"]
         tooldia = tooldia if tooldia is not None else self.options["cnctooldia"]
         tooldia = tooldia if tooldia is not None else self.options["cnctooldia"]
+        multidepth = multidepth if multidepth is not None else self.options["multidepth"]
+        depthperpass = depthperpass if depthperpass is not None else self.options["depthperpass"]
+
+        # To allow default value to be "" (optional in gui) and translate to None
+        # if not isinstance(spindlespeed, int):
+        #     if isinstance(self.options["spindlespeed"], int) or \
+        #             isinstance(self.options["spindlespeed"], float):
+        #         spindlespeed = int(self.options["spindlespeed"])
+        #     else:
+        #         spindlespeed = None
+
+        if spindlespeed is None:
+            # int or None.
+            spindlespeed = self.options['spindlespeed']
 
 
         # Object initialization function for app.new_object()
         # Object initialization function for app.new_object()
         # RUNNING ON SEPARATE THREAD!
         # RUNNING ON SEPARATE THREAD!
         def job_init(job_obj, app_obj):
         def job_init(job_obj, app_obj):
-            assert isinstance(job_obj, FlatCAMCNCjob)
+            assert isinstance(job_obj, FlatCAMCNCjob), \
+                "Initializer expected a FlatCAMCNCjob, got %s" % type(job_obj)
+
             # Propagate options
             # Propagate options
             job_obj.options["tooldia"] = tooldia
             job_obj.options["tooldia"] = tooldia
 
 
-            # GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Creating CNC Job..."))
             app_obj.progress.emit(20)
             app_obj.progress.emit(20)
             job_obj.z_cut = z_cut
             job_obj.z_cut = z_cut
             job_obj.z_move = z_move
             job_obj.z_move = z_move
             job_obj.feedrate = feedrate
             job_obj.feedrate = feedrate
-
-            # GLib.idle_add(lambda: app_obj.set_progress_bar(0.4, "Analyzing Geometry..."))
+            job_obj.spindlespeed = spindlespeed
             app_obj.progress.emit(40)
             app_obj.progress.emit(40)
             # TODO: The tolerance should not be hard coded. Just for testing.
             # TODO: The tolerance should not be hard coded. Just for testing.
-            #job_obj.generate_from_geometry(self, tolerance=0.0005)
-            job_obj.generate_from_geometry_2(self, tolerance=0.0005)
+            job_obj.generate_from_geometry_2(self,
+                                             multidepth=multidepth,
+                                             depthpercut=depthperpass,
+                                             tolerance=0.0005)
 
 
-            # GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Parsing G-Code..."))
             app_obj.progress.emit(50)
             app_obj.progress.emit(50)
             job_obj.gcode_parse()
             job_obj.gcode_parse()
 
 
-            # 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..."))
             app_obj.progress.emit(80)
             app_obj.progress.emit(80)
 
 
         # To be run in separate thread
         # To be run in separate thread
         def job_thread(app_obj):
         def job_thread(app_obj):
-            app_obj.new_object("cncjob", outname, job_init)
-            # GLib.idle_add(lambda: app_obj.info("CNCjob created: %s" % job_name))
-            # GLib.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
-            # GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, "Idle"))
-            app_obj.inform.emit("CNCjob created: %s" % outname)
-            app_obj.progress.emit(100)
+            with self.app.proc_container.new("Generating CNC Job."):
+                app_obj.new_object("cncjob", outname, job_init)
+                app_obj.inform.emit("CNCjob created: %s" % outname)
+                app_obj.progress.emit(100)
+
+        # Create a promise with the name
+        self.app.collection.promise(outname)
 
 
         # Send to worker
         # Send to worker
         self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
         self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
@@ -1204,5 +1356,3 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         self.plot_element(self.solid_geometry)
         self.plot_element(self.solid_geometry)
 
 
         self.app.plotcanvas.auto_adjust_axes()
         self.app.plotcanvas.auto_adjust_axes()
-        # GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
-        # self.emit(QtCore.SIGNAL("plotChanged"), self)

+ 148 - 0
FlatCAMProcess.py

@@ -0,0 +1,148 @@
+from FlatCAMGUI import FlatCAMActivityView
+from PyQt4 import QtCore
+import weakref
+
+
+# import logging
+
+# log = logging.getLogger('base2')
+# #log.setLevel(logging.DEBUG)
+# log.setLevel(logging.WARNING)
+# #log.setLevel(logging.INFO)
+# formatter = logging.Formatter('[%(levelname)s] %(message)s')
+# handler = logging.StreamHandler()
+# handler.setFormatter(formatter)
+# log.addHandler(handler)
+
+
+class FCProcess(object):
+
+    app = None
+
+    def __init__(self, descr):
+        self.callbacks = {
+            "done": []
+        }
+        self.descr = descr
+        self.status = "Active"
+
+    def __del__(self):
+        self.done()
+
+    def __enter__(self):
+        pass
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        if exc_type is not None:
+            self.app.log.error("Abnormal termination of process!")
+            self.app.log.error(exc_type)
+            self.app.log.error(exc_val)
+            self.app.log.error(exc_tb)
+
+        self.done()
+
+    def done(self):
+        for fcn in self.callbacks["done"]:
+            fcn(self)
+
+    def connect(self, callback, event="done"):
+        if callback not in self.callbacks[event]:
+            self.callbacks[event].append(callback)
+
+    def disconnect(self, callback, event="done"):
+        try:
+            self.callbacks[event].remove(callback)
+        except ValueError:
+            pass
+
+    def set_status(self, status_string):
+        self.status = status_string
+
+    def status_msg(self):
+        return self.descr
+
+
+class FCProcessContainer(object):
+    """
+    This is the process container, or controller (as in MVC)
+    of the Process/Activity tracking.
+
+    FCProcessContainer keeps weak references to the FCProcess'es
+    such that their __del__ method is called when the user
+    looses track of their reference.
+    """
+
+    app = None
+
+    def __init__(self):
+
+        self.procs = []
+
+    def add(self, proc):
+
+        self.procs.append(weakref.ref(proc))
+
+    def new(self, descr):
+        proc = FCProcess(descr)
+
+        proc.connect(self.on_done, event="done")
+
+        self.add(proc)
+
+        self.on_change(proc)
+
+        return proc
+
+    def on_change(self, proc):
+        pass
+
+    def on_done(self, proc):
+        self.remove(proc)
+
+    def remove(self, proc):
+
+        to_be_removed = []
+
+        for pref in self.procs:
+            if pref() == proc or pref() is None:
+                to_be_removed.append(pref)
+
+        for pref in to_be_removed:
+            self.procs.remove(pref)
+
+
+class FCVisibleProcessContainer(QtCore.QObject, FCProcessContainer):
+    something_changed = QtCore.pyqtSignal()
+
+    def __init__(self, view):
+        assert isinstance(view, FlatCAMActivityView), \
+            "Expected a FlatCAMActivityView, got %s" % type(view)
+
+        FCProcessContainer.__init__(self)
+        QtCore.QObject.__init__(self)
+
+        self.view = view
+
+        self.something_changed.connect(self.update_view)
+
+    def on_done(self, proc):
+        self.app.log.debug("FCVisibleProcessContainer.on_done()")
+        super(FCVisibleProcessContainer, self).on_done(proc)
+
+        self.something_changed.emit()
+
+    def on_change(self, proc):
+        self.app.log.debug("FCVisibleProcessContainer.on_change()")
+        super(FCVisibleProcessContainer, self).on_change(proc)
+
+        self.something_changed.emit()
+
+    def update_view(self):
+        if len(self.procs) == 0:
+            self.view.set_idle()
+
+        elif len(self.procs) == 1:
+            self.view.set_busy(self.procs[0]().status_msg())
+
+        else:
+            self.view.set_busy("%d processes running." % len(self.procs))

+ 1 - 218
FlatCAMTool.py

@@ -1,11 +1,4 @@
-from PyQt4 import QtGui, QtCore
-from shapely.geometry import Point
-from shapely import affinity
-from math import sqrt
-
-import FlatCAMApp
-from GUIElements import *
-from FlatCAMObj import FlatCAMGerber, FlatCAMExcellon
+from PyQt4 import QtGui
 
 
 
 
 class FlatCAMTool(QtGui.QWidget):
 class FlatCAMTool(QtGui.QWidget):
@@ -46,213 +39,3 @@ class FlatCAMTool(QtGui.QWidget):
         self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
         self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
 
 
         self.show()
         self.show()
-
-
-class DblSidedTool(FlatCAMTool):
-
-    toolName = "Double-Sided PCB Tool"
-
-    def __init__(self, app):
-        FlatCAMTool.__init__(self, app)
-
-        ## Title
-        title_label = QtGui.QLabel("<font size=4><b>%s</b></font>" % self.toolName)
-        self.layout.addWidget(title_label)
-
-        ## Form Layout
-        form_layout = QtGui.QFormLayout()
-        self.layout.addLayout(form_layout)
-
-        ## Layer to mirror
-        self.object_combo = QtGui.QComboBox()
-        self.object_combo.setModel(self.app.collection)
-        form_layout.addRow("Bottom Layer:", self.object_combo)
-
-        ## Axis
-        self.mirror_axis = RadioSet([{'label': 'X', 'value': 'X'},
-                                     {'label': 'Y', 'value': 'Y'}])
-        form_layout.addRow("Mirror Axis:", self.mirror_axis)
-
-        ## Axis Location
-        self.axis_location = RadioSet([{'label': 'Point', 'value': 'point'},
-                                       {'label': 'Box', 'value': 'box'}])
-        form_layout.addRow("Axis Location:", self.axis_location)
-
-        ## Point/Box
-        self.point_box_container = QtGui.QVBoxLayout()
-        form_layout.addRow("Point/Box:", self.point_box_container)
-        self.point = EvalEntry()
-        self.point_box_container.addWidget(self.point)
-        self.box_combo = QtGui.QComboBox()
-        self.box_combo.setModel(self.app.collection)
-        self.point_box_container.addWidget(self.box_combo)
-        self.box_combo.hide()
-
-        ## Alignment holes
-        self.alignment_holes = EvalEntry()
-        form_layout.addRow("Alignment Holes:", self.alignment_holes)
-
-        ## Drill diameter for alignment holes
-        self.drill_dia = LengthEntry()
-        form_layout.addRow("Drill diam.:", self.drill_dia)
-
-        ## Buttons
-        hlay = QtGui.QHBoxLayout()
-        self.layout.addLayout(hlay)
-        hlay.addStretch()
-        self.create_alignment_hole_button = QtGui.QPushButton("Create Alignment Drill")
-        self.mirror_object_button = QtGui.QPushButton("Mirror Object")
-        hlay.addWidget(self.create_alignment_hole_button)
-        hlay.addWidget(self.mirror_object_button)
-
-        self.layout.addStretch()
-
-        ## Signals
-        self.create_alignment_hole_button.clicked.connect(self.on_create_alignment_holes)
-        self.mirror_object_button.clicked.connect(self.on_mirror)
-
-        self.axis_location.group_toggle_fn = self.on_toggle_pointbox
-
-        ## Initialize form
-        self.mirror_axis.set_value('X')
-        self.axis_location.set_value('point')
-
-    def on_create_alignment_holes(self):
-        axis = self.mirror_axis.get_value()
-        mode = self.axis_location.get_value()
-
-        if mode == "point":
-            px, py = self.point.get_value()
-        else:
-            selection_index = self.box_combo.currentIndex()
-            bb_obj = self.app.collection.object_list[selection_index]  # TODO: Direct access??
-            xmin, ymin, xmax, ymax = bb_obj.bounds()
-            px = 0.5*(xmin+xmax)
-            py = 0.5*(ymin+ymax)
-
-        xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
-
-        dia = self.drill_dia.get_value()
-        tools = {"1": {"C": dia}}
-
-        holes = self.alignment_holes.get_value()
-        drills = []
-
-        for hole in holes:
-            point = Point(hole)
-            point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
-            drills.append({"point": point, "tool": "1"})
-            drills.append({"point": point_mirror, "tool": "1"})
-
-        def obj_init(obj_inst, app_inst):
-            obj_inst.tools = tools
-            obj_inst.drills = drills
-            obj_inst.create_geometry()
-
-        self.app.new_object("excellon", "Alignment Drills", obj_init)
-
-    def on_mirror(self):
-        selection_index = self.object_combo.currentIndex()
-        fcobj = self.app.collection.object_list[selection_index]
-
-        # For now, lets limit to Gerbers and Excellons.
-        # assert isinstance(gerb, FlatCAMGerber)
-        if not isinstance(fcobj, FlatCAMGerber) and not isinstance(fcobj, FlatCAMExcellon):
-            self.info("ERROR: Only Gerber and Excellon objects can be mirrored.")
-            return
-
-        axis = self.mirror_axis.get_value()
-        mode = self.axis_location.get_value()
-
-        if mode == "point":
-            px, py = self.point.get_value()
-        else:
-            selection_index = self.box_combo.currentIndex()
-            bb_obj = self.app.collection.object_list[selection_index]  # TODO: Direct access??
-            xmin, ymin, xmax, ymax = bb_obj.bounds()
-            px = 0.5*(xmin+xmax)
-            py = 0.5*(ymin+ymax)
-
-        fcobj.mirror(axis, [px, py])
-        fcobj.plot()
-
-    def on_toggle_pointbox(self):
-        if self.axis_location.get_value() == "point":
-            self.point.show()
-            self.box_combo.hide()
-        else:
-            self.point.hide()
-            self.box_combo.show()
-
-
-class Measurement(FlatCAMTool):
-
-    toolName = "Measurement Tool"
-
-    def __init__(self, app):
-        FlatCAMTool.__init__(self, app)
-
-        # self.setContentsMargins(0, 0, 0, 0)
-        self.layout.setMargin(0)
-        self.layout.setContentsMargins(0, 0, 3, 0)
-
-        self.setSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Maximum)
-
-        self.point1 = None
-        self.point2 = None
-        self.label = QtGui.QLabel("Click on a reference point ...")
-        self.label.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Plain)
-        self.label.setMargin(3)
-        self.layout.addWidget(self.label)
-        # self.layout.setMargin(0)
-        self.setVisible(False)
-
-        self.click_subscription = None
-        self.move_subscription = None
-
-    def install(self):
-        FlatCAMTool.install(self)
-        self.app.ui.right_layout.addWidget(self)
-        self.app.plotcanvas.mpl_connect('key_press_event', self.on_key_press)
-
-    def run(self):
-        self.toggle()
-
-    def on_click(self, event):
-        if self.point1 is None:
-            self.point1 = (event.xdata, event.ydata)
-        else:
-            self.point2 = copy(self.point1)
-            self.point1 = (event.xdata, event.ydata)
-        self.on_move(event)
-
-    def on_key_press(self, event):
-        if event.key == 'r':
-            self.toggle()
-
-    def toggle(self):
-        if self.isVisible():
-            self.setVisible(False)
-            self.app.plotcanvas.mpl_disconnect(self.move_subscription)
-            self.app.plotcanvas.mpl_disconnect(self.click_subscription)
-        else:
-            self.setVisible(True)
-            self.move_subscription = self.app.plotcanvas.mpl_connect('motion_notify_event', self.on_move)
-            self.click_subscription = self.app.plotcanvas.mpl_connect('button_press_event', self.on_click)
-
-    def on_move(self, event):
-        if self.point1 is None:
-            self.label.setText("Click on a reference point...")
-        else:
-            try:
-                dx = event.xdata - self.point1[0]
-                dy = event.ydata - self.point1[1]
-                d = sqrt(dx**2 + dy**2)
-                self.label.setText("D = %.4f  D(x) = %.4f  D(y) = %.4f" % (d, dx, dy))
-            except TypeError:
-                pass
-        if self.update is not None:
-            self.update()
-
-
-

+ 9 - 4
FlatCAMWorker.py

@@ -1,5 +1,5 @@
 from PyQt4 import QtCore
 from PyQt4 import QtCore
-import FlatCAMApp
+#import FlatCAMApp
 
 
 
 
 class Worker(QtCore.QObject):
 class Worker(QtCore.QObject):
@@ -14,13 +14,15 @@ class Worker(QtCore.QObject):
         self.name = name
         self.name = name
 
 
     def run(self):
     def run(self):
-        FlatCAMApp.App.log.debug("Worker Started!")
+        # FlatCAMApp.App.log.debug("Worker Started!")
+        self.app.log.debug("Worker Started!")
 
 
         # Tasks are queued in the event listener.
         # Tasks are queued in the event listener.
         self.app.worker_task.connect(self.do_worker_task)
         self.app.worker_task.connect(self.do_worker_task)
 
 
     def do_worker_task(self, task):
     def do_worker_task(self, task):
-        FlatCAMApp.App.log.debug("Running task: %s" % str(task))
+        # FlatCAMApp.App.log.debug("Running task: %s" % str(task))
+        self.app.log.debug("Running task: %s" % str(task))
 
 
         # 'worker_name' property of task allows to target
         # 'worker_name' property of task allows to target
         # specific worker.
         # specific worker.
@@ -30,4 +32,7 @@ class Worker(QtCore.QObject):
 
 
         if 'worker_name' not in task and self.name is None:
         if 'worker_name' not in task and self.name is None:
             task['fcn'](*task['params'])
             task['fcn'](*task['params'])
-            return
+            return
+
+        # FlatCAMApp.App.log.debug("Task ignored.")
+        self.app.log.debug("Task ignored.")

+ 78 - 25
GUIElements.py

@@ -1,7 +1,10 @@
 from PyQt4 import QtGui, QtCore
 from PyQt4 import QtGui, QtCore
 from copy import copy
 from copy import copy
-import FlatCAMApp
+#import FlatCAMApp
 import re
 import re
+import logging
+
+log = logging.getLogger('base')
 
 
 
 
 class RadioSet(QtGui.QWidget):
 class RadioSet(QtGui.QWidget):
@@ -37,7 +40,7 @@ class RadioSet(QtGui.QWidget):
         self.group_toggle_fn = lambda: None
         self.group_toggle_fn = lambda: None
 
 
     def on_toggle(self):
     def on_toggle(self):
-        FlatCAMApp.App.log.debug("Radio toggled")
+        log.debug("Radio toggled")
         radio = self.sender()
         radio = self.sender()
         if radio.isChecked():
         if radio.isChecked():
             self.group_toggle_fn()
             self.group_toggle_fn()
@@ -47,7 +50,7 @@ class RadioSet(QtGui.QWidget):
         for choice in self.choices:
         for choice in self.choices:
             if choice['radio'].isChecked():
             if choice['radio'].isChecked():
                 return choice['value']
                 return choice['value']
-        FlatCAMApp.App.log.error("No button was toggled in RadioSet.")
+        log.error("No button was toggled in RadioSet.")
         return None
         return None
 
 
     def set_value(self, val):
     def set_value(self, val):
@@ -55,7 +58,7 @@ class RadioSet(QtGui.QWidget):
             if choice['value'] == val:
             if choice['value'] == val:
                 choice['radio'].setChecked(True)
                 choice['radio'].setChecked(True)
                 return
                 return
-        FlatCAMApp.App.log.error("Value given is not part of this RadioSet: %s" % str(val))
+        log.error("Value given is not part of this RadioSet: %s" % str(val))
 
 
 
 
 class LengthEntry(QtGui.QLineEdit):
 class LengthEntry(QtGui.QLineEdit):
@@ -78,21 +81,25 @@ class LengthEntry(QtGui.QLineEdit):
         if val is not None:
         if val is not None:
             self.set_text(QtCore.QString(str(val)))
             self.set_text(QtCore.QString(str(val)))
         else:
         else:
-            FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.get_text())
+            log.warning("Could not interpret entry: %s" % self.get_text())
 
 
     def get_value(self):
     def get_value(self):
         raw = str(self.text()).strip(' ')
         raw = str(self.text()).strip(' ')
-        match = self.format_re.search(raw)
+        # match = self.format_re.search(raw)
 
 
-        if not match:
-            return None
         try:
         try:
-            if match.group(2) is not None and match.group(2).upper() in self.scales:
-                return float(eval(match.group(1)))*float(self.scales[self.output_units][match.group(2).upper()])
-            else:
-                return float(eval(match.group(1)))
+            units = raw[-2:]
+            units = self.scales[self.output_units][units.upper()]
+            value = raw[:-2]
+            return float(eval(value))*units
+        except IndexError:
+            value = raw
+            return float(eval(value))
+        except KeyError:
+            value = raw
+            return float(eval(value))
         except:
         except:
-            FlatCAMApp.App.log.warning("Could not parse value in entry: %s" % str(raw))
+            log.warning("Could not parse value in entry: %s" % str(raw))
             return None
             return None
 
 
     def set_value(self, val):
     def set_value(self, val):
@@ -108,30 +115,43 @@ class FloatEntry(QtGui.QLineEdit):
         if val is not None:
         if val is not None:
             self.set_text(QtCore.QString(str(val)))
             self.set_text(QtCore.QString(str(val)))
         else:
         else:
-            FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.text())
+            log.warning("Could not interpret entry: %s" % self.text())
 
 
     def get_value(self):
     def get_value(self):
         raw = str(self.text()).strip(' ')
         raw = str(self.text()).strip(' ')
         try:
         try:
             evaled = eval(raw)
             evaled = eval(raw)
         except:
         except:
-            FlatCAMApp.App.log.error("Could not evaluate: %s" % str(raw))
+            log.error("Could not evaluate: %s" % str(raw))
             return None
             return None
 
 
         return float(evaled)
         return float(evaled)
 
 
     def set_value(self, val):
     def set_value(self, val):
-        self.setText("%.6f"%val)
+        self.setText("%.6f" % val)
 
 
 
 
 class IntEntry(QtGui.QLineEdit):
 class IntEntry(QtGui.QLineEdit):
-    def __init__(self, parent=None):
+
+    def __init__(self, parent=None, allow_empty=False, empty_val=None):
         super(IntEntry, self).__init__(parent)
         super(IntEntry, self).__init__(parent)
+        self.allow_empty = allow_empty
+        self.empty_val = empty_val
 
 
     def get_value(self):
     def get_value(self):
+
+        if self.allow_empty:
+            if str(self.text()) == "":
+                return self.empty_val
+
         return int(self.text())
         return int(self.text())
 
 
     def set_value(self, val):
     def set_value(self, val):
+
+        if val == self.empty_val and self.allow_empty:
+            self.setText(QtCore.QString(""))
+            return
+
         self.setText(QtCore.QString(str(val)))
         self.setText(QtCore.QString(str(val)))
 
 
 
 
@@ -155,14 +175,14 @@ class EvalEntry(QtGui.QLineEdit):
         if val is not None:
         if val is not None:
             self.setText(QtCore.QString(str(val)))
             self.setText(QtCore.QString(str(val)))
         else:
         else:
-            FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.get_text())
+            log.warning("Could not interpret entry: %s" % self.get_text())
 
 
     def get_value(self):
     def get_value(self):
         raw = str(self.text()).strip(' ')
         raw = str(self.text()).strip(' ')
         try:
         try:
             return eval(raw)
             return eval(raw)
         except:
         except:
-            FlatCAMApp.App.log.error("Could not evaluate: %s" % str(raw))
+            log.error("Could not evaluate: %s" % str(raw))
             return None
             return None
 
 
     def set_value(self, val):
     def set_value(self, val):
@@ -213,18 +233,51 @@ class VerticalScrollArea(QtGui.QScrollArea):
         :return:
         :return:
         """
         """
         if event.type() == QtCore.QEvent.Resize and source == self.widget():
         if event.type() == QtCore.QEvent.Resize and source == self.widget():
-            # FlatCAMApp.App.log.debug("VerticalScrollArea: Widget resized:")
-            # FlatCAMApp.App.log.debug(" minimumSizeHint().width() = %d" % self.widget().minimumSizeHint().width())
-            # FlatCAMApp.App.log.debug(" verticalScrollBar().width() = %d" % self.verticalScrollBar().width())
+            # log.debug("VerticalScrollArea: Widget resized:")
+            # log.debug(" minimumSizeHint().width() = %d" % self.widget().minimumSizeHint().width())
+            # log.debug(" verticalScrollBar().width() = %d" % self.verticalScrollBar().width())
 
 
             self.setMinimumWidth(self.widget().sizeHint().width() +
             self.setMinimumWidth(self.widget().sizeHint().width() +
                                  self.verticalScrollBar().sizeHint().width())
                                  self.verticalScrollBar().sizeHint().width())
 
 
             # if self.verticalScrollBar().isVisible():
             # if self.verticalScrollBar().isVisible():
-            #     FlatCAMApp.App.log.debug(" Scroll bar visible")
+            #     log.debug(" Scroll bar visible")
             #     self.setMinimumWidth(self.widget().minimumSizeHint().width() +
             #     self.setMinimumWidth(self.widget().minimumSizeHint().width() +
             #                          self.verticalScrollBar().width())
             #                          self.verticalScrollBar().width())
             # else:
             # else:
-            #     FlatCAMApp.App.log.debug(" Scroll bar hidden")
+            #     log.debug(" Scroll bar hidden")
             #     self.setMinimumWidth(self.widget().minimumSizeHint().width())
             #     self.setMinimumWidth(self.widget().minimumSizeHint().width())
-        return QtGui.QWidget.eventFilter(self, source, event)
+        return QtGui.QWidget.eventFilter(self, source, event)
+
+
+class OptionalInputSection:
+
+    def __init__(self, cb, optinputs):
+        """
+        Associates the a checkbox with a set of inputs.
+
+        :param cb: Checkbox that enables the optional inputs.
+        :param optinputs: List of widgets that are optional.
+        :return:
+        """
+        assert isinstance(cb, FCCheckBox), \
+            "Expected an FCCheckBox, got %s" % type(cb)
+
+        self.cb = cb
+        self.optinputs = optinputs
+
+        self.on_cb_change()
+        self.cb.stateChanged.connect(self.on_cb_change)
+
+    def on_cb_change(self):
+
+        if self.cb.checkState():
+
+            for widget in self.optinputs:
+                widget.setEnabled(True)
+
+        else:
+
+            for widget in self.optinputs:
+                widget.setEnabled(False)
+

+ 74 - 0
MeasurementTool.py

@@ -0,0 +1,74 @@
+from PyQt4 import QtGui
+from FlatCAMTool import FlatCAMTool
+from copy import copy
+from math import sqrt
+
+
+class Measurement(FlatCAMTool):
+
+    toolName = "Measurement Tool"
+
+    def __init__(self, app):
+        FlatCAMTool.__init__(self, app)
+
+        # self.setContentsMargins(0, 0, 0, 0)
+        self.layout.setMargin(0)
+        self.layout.setContentsMargins(0, 0, 3, 0)
+
+        self.setSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Maximum)
+
+        self.point1 = None
+        self.point2 = None
+        self.label = QtGui.QLabel("Click on a reference point ...")
+        self.label.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Plain)
+        self.label.setMargin(3)
+        self.layout.addWidget(self.label)
+        # self.layout.setMargin(0)
+        self.setVisible(False)
+
+        self.click_subscription = None
+        self.move_subscription = None
+
+    def install(self):
+        FlatCAMTool.install(self)
+        self.app.ui.right_layout.addWidget(self)
+        self.app.plotcanvas.mpl_connect('key_press_event', self.on_key_press)
+
+    def run(self):
+        self.toggle()
+
+    def on_click(self, event):
+        if self.point1 is None:
+            self.point1 = (event.xdata, event.ydata)
+        else:
+            self.point2 = copy(self.point1)
+            self.point1 = (event.xdata, event.ydata)
+        self.on_move(event)
+
+    def on_key_press(self, event):
+        if event.key == 'r':
+            self.toggle()
+
+    def toggle(self):
+        if self.isVisible():
+            self.setVisible(False)
+            self.app.plotcanvas.mpl_disconnect(self.move_subscription)
+            self.app.plotcanvas.mpl_disconnect(self.click_subscription)
+        else:
+            self.setVisible(True)
+            self.move_subscription = self.app.plotcanvas.mpl_connect('motion_notify_event', self.on_move)
+            self.click_subscription = self.app.plotcanvas.mpl_connect('button_press_event', self.on_click)
+
+    def on_move(self, event):
+        if self.point1 is None:
+            self.label.setText("Click on a reference point...")
+        else:
+            try:
+                dx = event.xdata - self.point1[0]
+                dy = event.ydata - self.point1[1]
+                d = sqrt(dx**2 + dy**2)
+                self.label.setText("D = %.4f  D(x) = %.4f  D(y) = %.4f" % (d, dx, dy))
+            except TypeError:
+                pass
+        if self.update is not None:
+            self.update()

+ 64 - 7
ObjectCollection.py

@@ -1,9 +1,22 @@
-from PyQt4.QtCore import QModelIndex
+#from PyQt4.QtCore import QModelIndex
 from FlatCAMObj import *
 from FlatCAMObj import *
 import inspect  # TODO: Remove
 import inspect  # TODO: Remove
 import FlatCAMApp
 import FlatCAMApp
 from PyQt4 import Qt, QtGui, QtCore
 from PyQt4 import Qt, QtGui, QtCore
 
 
+
+class KeySensitiveListView(QtGui.QListView):
+    """
+    QtGui.QListView extended to emit a signal on key press.
+    """
+
+    keyPressed = QtCore.pyqtSignal(int)
+
+    def keyPressEvent(self, event):
+        super(KeySensitiveListView, self).keyPressEvent(event)
+        self.keyPressed.emit(event.key())
+
+
 class ObjectCollection(QtCore.QAbstractListModel):
 class ObjectCollection(QtCore.QAbstractListModel):
     """
     """
     Object storage and management.
     Object storage and management.
@@ -34,8 +47,16 @@ class ObjectCollection(QtCore.QAbstractListModel):
         self.object_list = []
         self.object_list = []
         self.checked_indexes = []
         self.checked_indexes = []
 
 
+        # Names of objects that are expected to become available.
+        # For example, when the creation of a new object will run
+        # in the background and will complete some time in the
+        # future. This is a way to reserve the name and to let other
+        # tasks know that they have to wait until available.
+        self.promises = set()
+
         ### View
         ### View
-        self.view = QtGui.QListView()
+        #self.view = QtGui.QListView()
+        self.view = KeySensitiveListView()
         self.view.setSelectionMode(Qt.QAbstractItemView.ExtendedSelection)
         self.view.setSelectionMode(Qt.QAbstractItemView.ExtendedSelection)
         self.view.setModel(self)
         self.view.setModel(self)
 
 
@@ -44,12 +65,26 @@ class ObjectCollection(QtCore.QAbstractListModel):
         ## GUI Events
         ## GUI Events
         self.view.selectionModel().selectionChanged.connect(self.on_list_selection_change)
         self.view.selectionModel().selectionChanged.connect(self.on_list_selection_change)
         self.view.activated.connect(self.on_item_activated)
         self.view.activated.connect(self.on_item_activated)
+        self.view.keyPressed.connect(self.on_key)
+        self.view.clicked.connect(self.on_mouse_down)
+
+    def promise(self, obj_name):
+        FlatCAMApp.App.log.debug("Object %s has been promised." % obj_name)
+        self.promises.add(obj_name)
 
 
-    def on_key(self, event):
-        print event
+    def has_promises(self):
+        return len(self.promises) > 0
+
+    def on_key(self, key):
+
+        # Delete
+        if key == QtCore.Qt.Key_Delete:
+            # Delete via the application to
+            # ensure cleanup of the GUI
+            self.get_active().app.on_delete()
 
 
     def on_mouse_down(self, event):
     def on_mouse_down(self, event):
-        print "Mouse button pressed on list"
+        FlatCAMApp.App.log.debug("Mouse button pressed on list")
 
 
     def rowCount(self, parent=QtCore.QModelIndex(), *args, **kwargs):
     def rowCount(self, parent=QtCore.QModelIndex(), *args, **kwargs):
         return len(self.object_list)
         return len(self.object_list)
@@ -78,15 +113,37 @@ class ObjectCollection(QtCore.QAbstractListModel):
     def append(self, obj, active=False):
     def append(self, obj, active=False):
         FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> OC.append()")
         FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> OC.append()")
 
 
+        name = obj.options["name"]
+
+        # Check promises and clear if exists
+        if name in self.promises:
+            self.promises.remove(name)
+            FlatCAMApp.App.log.debug("Promised object %s became available." % name)
+            FlatCAMApp.App.log.debug("%d promised objects remaining." % len(self.promises))
+
+        # Prevent same name
+        while name in self.get_names():
+            ## Create a new name
+            # Ends with number?
+            FlatCAMApp.App.log.debug("new_object(): Object name (%s) exists, changing." % name)
+            match = re.search(r'(.*[^\d])?(\d+)$', name)
+            if match:  # Yes: Increment the number!
+                base = match.group(1) or ''
+                num = int(match.group(2))
+                name = base + str(num + 1)
+            else:  # No: add a number!
+                name += "_1"
+        obj.options["name"] = name
+
         obj.set_ui(obj.ui_type())
         obj.set_ui(obj.ui_type())
 
 
-        # Required before appending
+        # Required before appending (Qt MVC)
         self.beginInsertRows(QtCore.QModelIndex(), len(self.object_list), len(self.object_list))
         self.beginInsertRows(QtCore.QModelIndex(), len(self.object_list), len(self.object_list))
 
 
         # Simply append to the python list
         # Simply append to the python list
         self.object_list.append(obj)
         self.object_list.append(obj)
 
 
-        # Required after appending
+        # Required after appending (Qt MVC)
         self.endInsertRows()
         self.endInsertRows()
 
 
     def get_names(self):
     def get_names(self):

+ 137 - 23
ObjectUI.py

@@ -1,6 +1,8 @@
 import sys
 import sys
 from PyQt4 import QtGui, QtCore
 from PyQt4 import QtGui, QtCore
-from GUIElements import *
+#from GUIElements import *
+from GUIElements import FCEntry, FloatEntry, EvalEntry, FCCheckBox, \
+    LengthEntry, FCTextArea, IntEntry, RadioSet, OptionalInputSection
 
 
 
 
 class ObjectUI(QtGui.QWidget):
 class ObjectUI(QtGui.QWidget):
@@ -39,19 +41,23 @@ class ObjectUI(QtGui.QWidget):
         self.name_box.addWidget(self.name_entry)
         self.name_box.addWidget(self.name_entry)
 
 
         ## Box box for custom widgets
         ## Box box for custom widgets
+        # This gets populated in offspring implementations.
         self.custom_box = QtGui.QVBoxLayout()
         self.custom_box = QtGui.QVBoxLayout()
         layout.addLayout(self.custom_box)
         layout.addLayout(self.custom_box)
 
 
-        ## Common to all objects
-        ## Scale
+        ###########################
+        ## Common to all objects ##
+        ###########################
+
+        #### Scale ####
         self.scale_label = QtGui.QLabel('<b>Scale:</b>')
         self.scale_label = QtGui.QLabel('<b>Scale:</b>')
         self.scale_label.setToolTip(
         self.scale_label.setToolTip(
             "Change the size of the object."
             "Change the size of the object."
         )
         )
         layout.addWidget(self.scale_label)
         layout.addWidget(self.scale_label)
 
 
-        grid1 = QtGui.QGridLayout()
-        layout.addLayout(grid1)
+        self.scale_grid = QtGui.QGridLayout()
+        layout.addLayout(self.scale_grid)
 
 
         # Factor
         # Factor
         faclabel = QtGui.QLabel('Factor:')
         faclabel = QtGui.QLabel('Factor:')
@@ -59,10 +65,10 @@ class ObjectUI(QtGui.QWidget):
             "Factor by which to multiply\n"
             "Factor by which to multiply\n"
             "geometric features of this object."
             "geometric features of this object."
         )
         )
-        grid1.addWidget(faclabel, 0, 0)
+        self.scale_grid.addWidget(faclabel, 0, 0)
         self.scale_entry = FloatEntry()
         self.scale_entry = FloatEntry()
         self.scale_entry.set_value(1.0)
         self.scale_entry.set_value(1.0)
-        grid1.addWidget(self.scale_entry, 0, 1)
+        self.scale_grid.addWidget(self.scale_entry, 0, 1)
 
 
         # GO Button
         # GO Button
         self.scale_button = QtGui.QPushButton('Scale')
         self.scale_button = QtGui.QPushButton('Scale')
@@ -71,25 +77,25 @@ class ObjectUI(QtGui.QWidget):
         )
         )
         layout.addWidget(self.scale_button)
         layout.addWidget(self.scale_button)
 
 
-        ## Offset
+        #### Offset ####
         self.offset_label = QtGui.QLabel('<b>Offset:</b>')
         self.offset_label = QtGui.QLabel('<b>Offset:</b>')
         self.offset_label.setToolTip(
         self.offset_label.setToolTip(
             "Change the position of this object."
             "Change the position of this object."
         )
         )
         layout.addWidget(self.offset_label)
         layout.addWidget(self.offset_label)
 
 
-        grid2 = QtGui.QGridLayout()
-        layout.addLayout(grid2)
+        self.offset_grid = QtGui.QGridLayout()
+        layout.addLayout(self.offset_grid)
 
 
-        self.offset_label = QtGui.QLabel('Vector:')
-        self.offset_label.setToolTip(
+        self.offset_vectorlabel = QtGui.QLabel('Vector:')
+        self.offset_vectorlabel.setToolTip(
             "Amount by which to move the object\n"
             "Amount by which to move the object\n"
             "in the x and y axes in (x, y) format."
             "in the x and y axes in (x, y) format."
         )
         )
-        grid2.addWidget(self.offset_label, 0, 0)
+        self.offset_grid.addWidget(self.offset_vectorlabel, 0, 0)
         self.offsetvector_entry = EvalEntry()
         self.offsetvector_entry = EvalEntry()
         self.offsetvector_entry.setText("(0.0, 0.0)")
         self.offsetvector_entry.setText("(0.0, 0.0)")
-        grid2.addWidget(self.offsetvector_entry, 0, 1)
+        self.offset_grid.addWidget(self.offsetvector_entry, 0, 1)
 
 
         self.offset_button = QtGui.QPushButton('Offset')
         self.offset_button = QtGui.QPushButton('Offset')
         self.offset_button.setToolTip(
         self.offset_button.setToolTip(
@@ -106,8 +112,25 @@ class CNCObjectUI(ObjectUI):
     """
     """
 
 
     def __init__(self, parent=None):
     def __init__(self, parent=None):
+        """
+        Creates the user interface for CNCJob objects. GUI elements should
+        be placed in ``self.custom_box`` to preserve the layout.
+        """
+
         ObjectUI.__init__(self, title='CNC Job Object', icon_file='share/cnc32.png', parent=parent)
         ObjectUI.__init__(self, title='CNC Job Object', icon_file='share/cnc32.png', parent=parent)
 
 
+        # Scale and offset are not available for CNCJob objects.
+        # Hiding from the GUI.
+        for i in range(0, self.scale_grid.count()):
+            self.scale_grid.itemAt(i).widget().hide()
+        self.scale_label.hide()
+        self.scale_button.hide()
+
+        for i in range(0, self.offset_grid.count()):
+            self.offset_grid.itemAt(i).widget().hide()
+        self.offset_label.hide()
+        self.offset_button.hide()
+
         ## Plot options
         ## Plot options
         self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
         self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
         self.custom_box.addWidget(self.plot_options_label)
         self.custom_box.addWidget(self.plot_options_label)
@@ -148,6 +171,17 @@ class CNCObjectUI(ObjectUI):
         )
         )
         self.custom_box.addWidget(self.export_gcode_label)
         self.custom_box.addWidget(self.export_gcode_label)
 
 
+        # Prepend text to Gerber
+        prependlabel = QtGui.QLabel('Prepend to G-Code:')
+        prependlabel.setToolTip(
+            "Type here any G-Code commands you would\n"
+            "like to add to the beginning of the generated file."
+        )
+        self.custom_box.addWidget(prependlabel)
+
+        self.prepend_text = FCTextArea()
+        self.custom_box.addWidget(self.prepend_text)
+
         # Append text to Gerber
         # Append text to Gerber
         appendlabel = QtGui.QLabel('Append to G-Code:')
         appendlabel = QtGui.QLabel('Append to G-Code:')
         appendlabel.setToolTip(
         appendlabel.setToolTip(
@@ -188,7 +222,9 @@ class GeometryObjectUI(ObjectUI):
         )
         )
         self.custom_box.addWidget(self.plot_cb)
         self.custom_box.addWidget(self.plot_cb)
 
 
-        ## Create CNC Job
+        #-----------------------------------
+        # Create CNC Job
+        #-----------------------------------
         self.cncjob_label = QtGui.QLabel('<b>Create CNC Job:</b>')
         self.cncjob_label = QtGui.QLabel('<b>Create CNC Job:</b>')
         self.cncjob_label.setToolTip(
         self.cncjob_label.setToolTip(
             "Create a CNC Job object\n"
             "Create a CNC Job object\n"
@@ -239,15 +275,48 @@ class GeometryObjectUI(ObjectUI):
         self.cnctooldia_entry = LengthEntry()
         self.cnctooldia_entry = LengthEntry()
         grid1.addWidget(self.cnctooldia_entry, 3, 1)
         grid1.addWidget(self.cnctooldia_entry, 3, 1)
 
 
+        # Spindlespeed
+        spdlabel = QtGui.QLabel('Spindle speed:')
+        spdlabel.setToolTip(
+            "Speed of the spindle\n"
+            "in RPM (optional)"
+        )
+        grid1.addWidget(spdlabel, 4, 0)
+        self.cncspindlespeed_entry = IntEntry(allow_empty=True)
+        grid1.addWidget(self.cncspindlespeed_entry, 4, 1)
+
+        # Multi-pass
+        mpasslabel = QtGui.QLabel('Multi-Depth:')
+        mpasslabel.setToolTip(
+            "Use multiple passes to limit\n"
+            "the cut depth in each pass. Will\n"
+            "cut multiple times until Cut Z is\n"
+            "reached."
+        )
+        grid1.addWidget(mpasslabel, 5, 0)
+        self.mpass_cb = FCCheckBox()
+        grid1.addWidget(self.mpass_cb, 5, 1)
+
+        maxdepthlabel = QtGui.QLabel('Depth/pass:')
+        maxdepthlabel.setToolTip(
+            "Depth of each pass (positive)."
+        )
+        grid1.addWidget(maxdepthlabel, 6, 0)
+        self.maxdepth_entry = LengthEntry()
+        grid1.addWidget(self.maxdepth_entry, 6, 1)
+
+        self.ois_mpass = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry])
+
+        # Button
         self.generate_cnc_button = QtGui.QPushButton('Generate')
         self.generate_cnc_button = QtGui.QPushButton('Generate')
         self.generate_cnc_button.setToolTip(
         self.generate_cnc_button.setToolTip(
             "Generate the CNC Job object."
             "Generate the CNC Job object."
         )
         )
         self.custom_box.addWidget(self.generate_cnc_button)
         self.custom_box.addWidget(self.generate_cnc_button)
 
 
-        ################
-        ## Paint area ##
-        ################
+        #------------------------------
+        # Paint area
+        #------------------------------
         self.paint_label = QtGui.QLabel('<b>Paint Area:</b>')
         self.paint_label = QtGui.QLabel('<b>Paint Area:</b>')
         self.paint_label.setToolTip(
         self.paint_label.setToolTip(
             "Creates tool paths to cover the\n"
             "Creates tool paths to cover the\n"
@@ -323,9 +392,12 @@ class ExcellonObjectUI(ObjectUI):
     """
     """
 
 
     def __init__(self, parent=None):
     def __init__(self, parent=None):
-        ObjectUI.__init__(self, title='Excellon Object', icon_file='share/drill32.png', parent=parent)
+        ObjectUI.__init__(self, title='Excellon Object',
+                          icon_file='share/drill32.png',
+                          parent=parent)
+
+        #### Plot options ####
 
 
-        ## Plot options
         self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
         self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
         self.custom_box.addWidget(self.plot_options_label)
         self.custom_box.addWidget(self.plot_options_label)
 
 
@@ -342,7 +414,8 @@ class ExcellonObjectUI(ObjectUI):
         )
         )
         grid0.addWidget(self.solid_cb, 0, 1)
         grid0.addWidget(self.solid_cb, 0, 1)
 
 
-        ## Tools
+        #### Tools ####
+
         self.tools_table_label = QtGui.QLabel('<b>Tools</b>')
         self.tools_table_label = QtGui.QLabel('<b>Tools</b>')
         self.tools_table_label.setToolTip(
         self.tools_table_label.setToolTip(
             "Tools in this Excellon object."
             "Tools in this Excellon object."
@@ -352,7 +425,8 @@ class ExcellonObjectUI(ObjectUI):
         self.tools_table.setFixedHeight(100)
         self.tools_table.setFixedHeight(100)
         self.custom_box.addWidget(self.tools_table)
         self.custom_box.addWidget(self.tools_table)
 
 
-        ## Create CNC Job
+        #### Create CNC Job ####
+
         self.cncjob_label = QtGui.QLabel('<b>Create CNC Job</b>')
         self.cncjob_label = QtGui.QLabel('<b>Create CNC Job</b>')
         self.cncjob_label.setToolTip(
         self.cncjob_label.setToolTip(
             "Create a CNC Job object\n"
             "Create a CNC Job object\n"
@@ -390,6 +464,37 @@ class ExcellonObjectUI(ObjectUI):
         self.feedrate_entry = LengthEntry()
         self.feedrate_entry = LengthEntry()
         grid1.addWidget(self.feedrate_entry, 2, 1)
         grid1.addWidget(self.feedrate_entry, 2, 1)
 
 
+        # Tool change:
+        toolchlabel = QtGui.QLabel("Tool change:")
+        toolchlabel.setToolTip(
+            "Include tool-change sequence\n"
+            "in G-Code (Pause for tool change)."
+        )
+        self.toolchange_cb = FCCheckBox()
+        grid1.addWidget(toolchlabel, 3, 0)
+        grid1.addWidget(self.toolchange_cb, 3, 1)
+
+        # Tool change Z:
+        toolchzlabel = QtGui.QLabel("Tool change Z:")
+        toolchzlabel.setToolTip(
+            "Z-axis position (height) for\n"
+            "tool change."
+        )
+        grid1.addWidget(toolchzlabel, 4, 0)
+        self.toolchangez_entry = LengthEntry()
+        grid1.addWidget(self.toolchangez_entry, 4, 1)
+        self.ois_tcz = OptionalInputSection(self.toolchange_cb, [self.toolchangez_entry])
+
+        # Spindlespeed
+        spdlabel = QtGui.QLabel('Spindle speed:')
+        spdlabel.setToolTip(
+            "Speed of the spindle\n"
+            "in RPM (optional)"
+        )
+        grid1.addWidget(spdlabel, 5, 0)
+        self.spindlespeed_entry = IntEntry(allow_empty=True)
+        grid1.addWidget(self.spindlespeed_entry, 5, 1)
+
         choose_tools_label = QtGui.QLabel(
         choose_tools_label = QtGui.QLabel(
             "Select from the tools section above\n"
             "Select from the tools section above\n"
             "the tools you want to include."
             "the tools you want to include."
@@ -402,7 +507,7 @@ class ExcellonObjectUI(ObjectUI):
         )
         )
         self.custom_box.addWidget(self.generate_cnc_button)
         self.custom_box.addWidget(self.generate_cnc_button)
 
 
-        ## Milling Holes
+        #### Milling Holes ####
         self.mill_hole_label = QtGui.QLabel('<b>Mill Holes</b>')
         self.mill_hole_label = QtGui.QLabel('<b>Mill Holes</b>')
         self.mill_hole_label.setToolTip(
         self.mill_hole_label.setToolTip(
             "Create Geometry for milling holes."
             "Create Geometry for milling holes."
@@ -432,6 +537,7 @@ class ExcellonObjectUI(ObjectUI):
         )
         )
         self.custom_box.addWidget(self.generate_milling_button)
         self.custom_box.addWidget(self.generate_milling_button)
 
 
+
 class GerberObjectUI(ObjectUI):
 class GerberObjectUI(ObjectUI):
     """
     """
     User interface for Gerber objects.
     User interface for Gerber objects.
@@ -503,6 +609,14 @@ class GerberObjectUI(ObjectUI):
         self.iso_overlap_entry = FloatEntry()
         self.iso_overlap_entry = FloatEntry()
         grid1.addWidget(self.iso_overlap_entry, 2, 1)
         grid1.addWidget(self.iso_overlap_entry, 2, 1)
 
 
+        # combine all passes CB
+        self.combine_passes_cb = FCCheckBox(label='Combine Passes')
+        self.combine_passes_cb.setToolTip(
+            "Combine all passes into one object"
+        )
+        grid1.addWidget(self.combine_passes_cb, 3, 0)
+
+
         self.generate_iso_button = QtGui.QPushButton('Generate Geometry')
         self.generate_iso_button = QtGui.QPushButton('Generate Geometry')
         self.generate_iso_button.setToolTip(
         self.generate_iso_button.setToolTip(
             "Create the Geometry Object\n"
             "Create the Geometry Object\n"

+ 211 - 16
PlotCanvas.py

@@ -7,17 +7,108 @@
 ############################################################
 ############################################################
 
 
 from PyQt4 import QtGui, QtCore
 from PyQt4 import QtGui, QtCore
+
+# Prevent conflict with Qt5 and above.
+from matplotlib import use as mpl_use
+mpl_use("Qt4Agg")
+
 from matplotlib.figure import Figure
 from matplotlib.figure import Figure
 from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
 from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
+from matplotlib.backends.backend_agg import FigureCanvasAgg
 import FlatCAMApp
 import FlatCAMApp
+import logging
+
+log = logging.getLogger('base')
+
+
+class CanvasCache(QtCore.QObject):
+    """
+
+    Case story #1:
+
+    1) No objects in the project.
+    2) Object is created (new_object() emits object_created(obj)).
+       on_object_created() adds (i) object to collection and emits
+       (ii) new_object_available() then calls (iii) object.plot()
+    3) object.plot() creates axes if necessary on
+       app.collection.figure. Then plots on it.
+    4) Plots on a cache-size canvas (in background).
+    5) Plot completes. Bitmap is generated.
+    6) Visible canvas is painted.
+
+    """
+
+    # Signals:
+    # A bitmap is ready to be displayed.
+    new_screen = QtCore.pyqtSignal()
+
+    def __init__(self, plotcanvas, app, dpi=50):
+
+        super(CanvasCache, self).__init__()
+
+        self.app = app
+
+        self.plotcanvas = plotcanvas
+        self.dpi = dpi
+
+        self.figure = Figure(dpi=dpi)
+
+        self.axes = self.figure.add_axes([0.0, 0.0, 1.0, 1.0], alpha=1.0)
+        self.axes.set_frame_on(False)
+        self.axes.set_xticks([])
+        self.axes.set_yticks([])
+
+        self.canvas = FigureCanvasAgg(self.figure)
+
+        self.cache = None
+
+    def run(self):
 
 
+        log.debug("CanvasCache Thread Started!")
 
 
-class PlotCanvas:
+        self.plotcanvas.update_screen_request.connect(self.on_update_req)
+
+        self.app.new_object_available.connect(self.on_new_object_available)
+
+    def on_update_req(self, extents):
+        """
+        Event handler for an updated display request.
+
+        :param extents: [xmin, xmax, ymin, ymax, zoom(optional)]
+        """
+
+        log.debug("Canvas update requested: %s" % str(extents))
+
+        # Note: This information below might be out of date. Establish
+        # a protocol regarding when to change the canvas in the main
+        # thread and when to check these values here in the background,
+        # or pass this data in the signal (safer).
+        log.debug("Size: %s [px]" % str(self.plotcanvas.get_axes_pixelsize()))
+        log.debug("Density: %s [units/px]" % str(self.plotcanvas.get_density()))
+
+        # Move the requested screen portion to the main thread
+        # and inform about the update:
+
+        self.new_screen.emit()
+
+        # Continue to update the cache.
+
+    def on_new_object_available(self):
+
+        log.debug("A new object is available. Should plot it!")
+
+
+class PlotCanvas(QtCore.QObject):
     """
     """
     Class handling the plotting area in the application.
     Class handling the plotting area in the application.
     """
     """
 
 
-    def __init__(self, container):
+    # Signals:
+    # Request for new bitmap to display. The parameter
+    # is a list with [xmin, xmax, ymin, ymax, zoom(optional)]
+    update_screen_request = QtCore.pyqtSignal(list)
+
+    def __init__(self, container, app):
         """
         """
         The constructor configures the Matplotlib figure that
         The constructor configures the Matplotlib figure that
         will contain all plots, creates the base axes and connects
         will contain all plots, creates the base axes and connects
@@ -26,6 +117,11 @@ class PlotCanvas:
         :param container: The parent container in which to draw plots.
         :param container: The parent container in which to draw plots.
         :rtype: PlotCanvas
         :rtype: PlotCanvas
         """
         """
+
+        super(PlotCanvas, self).__init__()
+
+        self.app = app
+
         # Options
         # Options
         self.x_margin = 15  # pixels
         self.x_margin = 15  # pixels
         self.y_margin = 25  # Pixels
         self.y_margin = 25  # Pixels
@@ -43,8 +139,11 @@ class PlotCanvas:
         self.axes.set_aspect(1)
         self.axes.set_aspect(1)
         self.axes.grid(True)
         self.axes.grid(True)
 
 
-        # The canvas is the top level container (Gtk.DrawingArea)
+        # The canvas is the top level container (FigureCanvasQTAgg)
         self.canvas = FigureCanvas(self.figure)
         self.canvas = FigureCanvas(self.figure)
+        # self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
+        # self.canvas.setFocus()
+
         #self.canvas.set_hexpand(1)
         #self.canvas.set_hexpand(1)
         #self.canvas.set_vexpand(1)
         #self.canvas.set_vexpand(1)
         #self.canvas.set_can_focus(True)  # For key press
         #self.canvas.set_can_focus(True)  # For key press
@@ -57,7 +156,18 @@ class PlotCanvas:
         # Update every time the canvas is re-drawn.
         # Update every time the canvas is re-drawn.
         self.background = self.canvas.copy_from_bbox(self.axes.bbox)
         self.background = self.canvas.copy_from_bbox(self.axes.bbox)
 
 
+        ### Bitmap Cache
+        self.cache = CanvasCache(self, self.app)
+        self.cache_thread = QtCore.QThread()
+        self.cache.moveToThread(self.cache_thread)
+        super(PlotCanvas, self).connect(self.cache_thread, QtCore.SIGNAL("started()"), self.cache.run)
+        # self.connect()
+        self.cache_thread.start()
+        self.cache.new_screen.connect(self.on_new_screen)
+
         # Events
         # Events
+        self.canvas.mpl_connect('button_press_event', self.on_mouse_press)
+        self.canvas.mpl_connect('button_release_event', self.on_mouse_release)
         self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
         self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
         #self.canvas.connect('configure-event', self.auto_adjust_axes)
         #self.canvas.connect('configure-event', self.auto_adjust_axes)
         self.canvas.mpl_connect('resize_event', self.auto_adjust_axes)
         self.canvas.mpl_connect('resize_event', self.auto_adjust_axes)
@@ -66,10 +176,18 @@ class PlotCanvas:
         self.canvas.mpl_connect('scroll_event', self.on_scroll)
         self.canvas.mpl_connect('scroll_event', self.on_scroll)
         self.canvas.mpl_connect('key_press_event', self.on_key_down)
         self.canvas.mpl_connect('key_press_event', self.on_key_down)
         self.canvas.mpl_connect('key_release_event', self.on_key_up)
         self.canvas.mpl_connect('key_release_event', self.on_key_up)
+        self.canvas.mpl_connect('draw_event', self.on_draw)
 
 
         self.mouse = [0, 0]
         self.mouse = [0, 0]
         self.key = None
         self.key = None
 
 
+        self.pan_axes = []
+        self.panning = False
+
+    def on_new_screen(self):
+
+        log.debug("Cache updated the screen!")
+
     def on_key_down(self, event):
     def on_key_down(self, event):
         """
         """
 
 
@@ -110,7 +228,7 @@ class PlotCanvas:
 
 
     def connect(self, event_name, callback):
     def connect(self, event_name, callback):
         """
         """
-        Attach an event handler to the canvas through the native GTK interface.
+        Attach an event handler to the canvas through the native Qt interface.
 
 
         :param event_name: Name of the event
         :param event_name: Name of the event
         :type event_name: str
         :type event_name: str
@@ -140,7 +258,7 @@ class PlotCanvas:
         self.axes.grid(True)
         self.axes.grid(True)
 
 
         # Re-draw
         # Re-draw
-        self.canvas.draw()
+        self.canvas.draw_idle()
 
 
     def adjust_axes(self, xmin, ymin, xmax, ymax):
     def adjust_axes(self, xmin, ymin, xmax, ymax):
         """
         """
@@ -196,9 +314,11 @@ class PlotCanvas:
             ax.set_ylim((ymin, ymax))
             ax.set_ylim((ymin, ymax))
             ax.set_position([x_ratio, y_ratio, 1 - 2 * x_ratio, 1 - 2 * y_ratio])
             ax.set_position([x_ratio, y_ratio, 1 - 2 * x_ratio, 1 - 2 * y_ratio])
 
 
-        # Re-draw
+        # Sync re-draw to proper paint on form resize
         self.canvas.draw()
         self.canvas.draw()
-        self.background = self.canvas.copy_from_bbox(self.axes.bbox)
+
+        ##### Temporary place-holder for cached update #####
+        self.update_screen_request.emit([0, 0, 0, 0, 0])
 
 
     def auto_adjust_axes(self, *args):
     def auto_adjust_axes(self, *args):
         """
         """
@@ -249,9 +369,11 @@ class PlotCanvas:
             ax.set_xlim((xmin, xmax))
             ax.set_xlim((xmin, xmax))
             ax.set_ylim((ymin, ymax))
             ax.set_ylim((ymin, ymax))
 
 
-        # Re-draw
-        self.canvas.draw()
-        self.background = self.canvas.copy_from_bbox(self.axes.bbox)
+        # Async re-draw
+        self.canvas.draw_idle()
+
+        ##### Temporary place-holder for cached update #####
+        self.update_screen_request.emit([0, 0, 0, 0, 0])
 
 
     def pan(self, x, y):
     def pan(self, x, y):
         xmin, xmax = self.axes.get_xlim()
         xmin, xmax = self.axes.get_xlim()
@@ -261,12 +383,14 @@ class PlotCanvas:
 
 
         # Adjust axes
         # Adjust axes
         for ax in self.figure.get_axes():
         for ax in self.figure.get_axes():
-            ax.set_xlim((xmin + x*width, xmax + x*width))
-            ax.set_ylim((ymin + y*height, ymax + y*height))
+            ax.set_xlim((xmin + x * width, xmax + x * width))
+            ax.set_ylim((ymin + y * height, ymax + y * height))
 
 
         # Re-draw
         # Re-draw
-        self.canvas.draw()
-        self.background = self.canvas.copy_from_bbox(self.axes.bbox)
+        self.canvas.draw_idle()
+
+        ##### Temporary place-holder for cached update #####
+        self.update_screen_request.emit([0, 0, 0, 0, 0])
 
 
     def new_axes(self, name):
     def new_axes(self, name):
         """
         """
@@ -299,7 +423,7 @@ class PlotCanvas:
             if event.button == 'up':
             if event.button == 'up':
                 self.zoom(1.5, self.mouse)
                 self.zoom(1.5, self.mouse)
             else:
             else:
-                self.zoom(1/1.5, self.mouse)
+                self.zoom(1 / 1.5, self.mouse)
             return
             return
 
 
         if self.key == 'shift':
         if self.key == 'shift':
@@ -318,12 +442,83 @@ class PlotCanvas:
                 self.pan(0, -0.3)
                 self.pan(0, -0.3)
             return
             return
 
 
+    def on_mouse_press(self, event):
+
+        # Check for middle mouse button press
+        if event.button == 2:
+
+            # Prepare axes for pan (using 'matplotlib' pan function)
+            self.pan_axes = []
+            for a in self.figure.get_axes():
+                if (event.x is not None and event.y is not None and a.in_axes(event) and
+                        a.get_navigate() and a.can_pan()):
+                    a.start_pan(event.x, event.y, 1)
+                    self.pan_axes.append(a)
+
+            # Set pan view flag
+            if len(self.pan_axes) > 0: self.panning = True;
+
+    def on_mouse_release(self, event):
+
+        # Check for middle mouse button release to complete pan procedure
+        if event.button == 2:
+            for a in self.pan_axes:
+                a.end_pan()
+
+            # Clear pan flag
+            self.panning = False
+
     def on_mouse_move(self, event):
     def on_mouse_move(self, event):
         """
         """
-        Mouse movement event hadler. Stores the coordinates.
+        Mouse movement event hadler. Stores the coordinates. Updates view on pan.
 
 
         :param event: Contains information about the event.
         :param event: Contains information about the event.
         :return: None
         :return: None
         """
         """
         self.mouse = [event.xdata, event.ydata]
         self.mouse = [event.xdata, event.ydata]
 
 
+        # Update pan view on mouse move
+        if self.panning is True:
+            for a in self.pan_axes:
+                a.drag_pan(1, event.key, event.x, event.y)
+
+            # Async re-draw (redraws only on thread idle state, uses timer on backend)
+            self.canvas.draw_idle()
+
+            ##### Temporary place-holder for cached update #####
+            self.update_screen_request.emit([0, 0, 0, 0, 0])
+
+    def on_draw(self, renderer):
+
+        # Store background on canvas redraw
+        self.background = self.canvas.copy_from_bbox(self.axes.bbox)
+
+    def get_axes_pixelsize(self):
+        """
+        Axes size in pixels.
+
+        :return: Pixel width and height
+        :rtype: tuple
+        """
+        bbox = self.axes.get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
+        width, height = bbox.width, bbox.height
+        width *= self.figure.dpi
+        height *= self.figure.dpi
+        return width, height
+
+    def get_density(self):
+        """
+        Returns unit length per pixel on horizontal
+        and vertical axes.
+
+        :return: X and Y density
+        :rtype: tuple
+        """
+        xpx, ypx = self.get_axes_pixelsize()
+
+        xmin, xmax = self.axes.get_xlim()
+        ymin, ymax = self.axes.get_ylim()
+        width = xmax - xmin
+        height = ymax - ymin
+
+        return width / xpx, height / ypx

+ 8 - 8
README.md

@@ -1,8 +1,8 @@
-FlatCAM: 2D Post-processing for Manufacturing
-=============================================
-
-(c) 2014 Juan Pablo Caram
-
-FlatCAM is a program for preparing CNC jobs for making PCBs on a CNC router.
-Among other things, it can take a Gerber file generated by your favorite PCB
-CAD program, and create G-Code for Isolation routing.
+FlatCAM: 2D Computer-Aided PCB Manufacturing
+============================================
+
+(c) 2014-2015 Juan Pablo Caram
+
+FlatCAM is a program for preparing CNC jobs for making PCBs on a CNC router.
+Among other things, it can take a Gerber file generated by your favorite PCB
+CAD program, and create G-Code for Isolation routing.

Разница между файлами не показана из-за своего большого размера
+ 573 - 96
camlib.py


+ 49 - 0
make_win32.py

@@ -0,0 +1,49 @@
+# Files not needed: Qt, tk.dll, tcl.dll, tk/, tcl/, vtk/,
+#   scipy.lib.lapack.flapack.pyd, scipy.lib.blas.fblas.pyd,
+#   numpy.core._dotblas.pyd, scipy.sparse.sparsetools._bsr.pyd,
+#   scipy.sparse.sparsetools._csr.pyd, scipy.sparse.sparsetools._csc.pyd,
+#   scipy.sparse.sparsetools._coo.pyd
+
+import os, site, sys
+from cx_Freeze import setup, Executable
+
+## Get the site-package folder, not everybody will install
+## Python into C:\PythonXX
+site_dir = site.getsitepackages()[1]
+
+include_files = []
+include_files.append((os.path.join(site_dir, "shapely"), "shapely"))
+include_files.append((os.path.join(site_dir, "matplotlib"), "matplotlib"))
+include_files.append(("share", "share"))
+include_files.append((os.path.join(site_dir, "rtree"), "rtree"))
+include_files.append(("README.md", "README.md"))
+include_files.append(("LICENSE", "LICENSE"))
+
+base = None
+
+## Lets not open the console while running the app
+if sys.platform == "win32":
+    base = "Win32GUI"
+
+buildOptions = dict(
+    compressed=False,
+    include_files=include_files,
+    icon='share/flatcam_icon48.ico',
+    # excludes=['PyQt4', 'tk', 'tcl']
+    excludes=['scipy.lib.lapack.flapack.pyd',
+              'scipy.lib.blas.fblas.pyd',
+              'QtOpenGL4.dll']
+)
+
+print "INCLUDE_FILES", include_files
+
+execfile('clean.py')
+
+setup(
+    name="FlatCAM",
+    author="Juan Pablo Caram",
+    version="8.4",
+    description="FlatCAM: 2D Computer Aided PCB Manufacturing",
+    options=dict(build_exe=buildOptions),
+    executables=[Executable("FlatCAM.py", base=base)]
+)

+ 0 - 17
manual/_theme/__init__.py

@@ -1,17 +0,0 @@
-"""Sphinx ReadTheDocs theme.
-
-From https://github.com/ryan-roemer/sphinx-bootstrap-theme.
-
-"""
-import os
-
-VERSION = (0, 1, 5)
-
-__version__ = ".".join(str(v) for v in VERSION)
-__version_full__ = __version__
-
-
-def get_html_theme_path():
-    """Return list of HTML theme paths."""
-    cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
-    return cur_dir

+ 0 - 19
manual/_theme/breadcrumbs.html

@@ -1,19 +0,0 @@
-<div role="navigation" aria-label="breadcrumbs navigation">
-  <ul class="wy-breadcrumbs">
-    <li><a href="{{ pathto(master_doc) }}">Docs</a> &raquo;</li>
-      {% for doc in parents %}
-          <li><a href="{{ doc.link|e }}">{{ doc.title }}</a> &raquo;</li>
-      {% endfor %}
-    <li>{{ title }}</li>
-      <li class="wy-breadcrumbs-aside">
-        {% if display_github %}
-          <a href="https://github.com/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}.rst" class="icon icon-github"> Edit on GitHub</a>
-        {% elif display_bitbucket %}
-          <a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}.rst" class="icon icon-bitbucket"> Edit on Bitbucket</a>
-        {% elif show_source and has_source and sourcename %}
-          <a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
-        {% endif %}
-      </li>
-  </ul>
-  <hr/>
-</div>

+ 0 - 32
manual/_theme/footer.html

@@ -1,32 +0,0 @@
-<footer>
-  {% if next or prev %}
-    <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
-      {% if next %}
-        <a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}"/>Next <span class="icon icon-circle-arrow-right"></span></a>
-      {% endif %}
-      {% if prev %}
-        <a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}"><span class="icon icon-circle-arrow-left"></span> Previous</a>
-      {% endif %}
-    </div>
-  {% endif %}
-
-  <hr/>
-
-  <div role="contentinfo">
-    <p>
-    {%- if show_copyright %}
-      {%- if hasdoc('copyright') %}
-        {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
-      {%- else %}
-        {% trans copyright=copyright|e %}&copy; Copyright {{ copyright }}.{% endtrans %}
-      {%- endif %}
-    {%- endif %}
-
-    {%- if last_updated %}
-      {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
-    {%- endif %}
-    </p>
-  </div>
-
-  {% trans %}<a href="https://github.com/snide/sphinx_rtd_theme">Sphinx theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>{% endtrans %}
-</footer>

+ 0 - 149
manual/_theme/layout.html

@@ -1,149 +0,0 @@
-{# TEMPLATE VAR SETTINGS #}
-{%- set url_root = pathto('', 1) %}
-{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
-{%- if not embedded and docstitle %}
-  {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
-{%- else %}
-  {%- set titlesuffix = "" %}
-{%- endif %}
-
-<!DOCTYPE html>
-<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
-<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
-<head>
-  <meta charset="utf-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  {% block htmltitle %}
-  <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
-  {% endblock %}
-
-  {# FAVICON #}
-  {% if favicon %}
-    <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
-  {% endif %}
-
-  {# CSS #}
-  <link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
-
-  {# JS #}
-  {% if not embedded %}
-
-    <script type="text/javascript">
-      var DOCUMENTATION_OPTIONS = {
-        URL_ROOT:'{{ url_root }}',
-        VERSION:'{{ release|e }}',
-        COLLAPSE_INDEX:false,
-        FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',
-        HAS_SOURCE:  {{ has_source|lower }}
-      };
-    </script>
-    {%- for scriptfile in script_files %}
-      <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
-    {%- endfor %}
-
-    {% if use_opensearch %}
-      <link rel="search" type="application/opensearchdescription+xml" title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}" href="{{ pathto('_static/opensearch.xml', 1) }}"/>
-    {% endif %}
-
-  {% endif %}
-
-  {# RTD hosts these file themselves, so just load on non RTD builds #}
-  {% if not READTHEDOCS %}
-    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
-    <script type="text/javascript" src="{{ pathto('_static/js/theme.js', 1) }}"></script>
-  {% endif %}
-
-  {# STICKY NAVIGATION #}
-  {% if theme_sticky_navigation %}
-    <script type="text/javascript">
-        jQuery(function () {
-            SphinxRtdTheme.StickyNav.enable();
-        });
-    </script>
-  {% endif %}
-
-  {% for cssfile in css_files %}
-    <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
-  {% endfor %}
-
-  {%- block linktags %}
-    {%- if hasdoc('about') %}
-        <link rel="author" title="{{ _('About these documents') }}"
-              href="{{ pathto('about') }}"/>
-    {%- endif %}
-    {%- if hasdoc('genindex') %}
-        <link rel="index" title="{{ _('Index') }}"
-              href="{{ pathto('genindex') }}"/>
-    {%- endif %}
-    {%- if hasdoc('search') %}
-        <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}"/>
-    {%- endif %}
-    {%- if hasdoc('copyright') %}
-        <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}"/>
-    {%- endif %}
-    <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}"/>
-    {%- if parents %}
-        <link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}"/>
-    {%- endif %}
-    {%- if next %}
-        <link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}"/>
-    {%- endif %}
-    {%- if prev %}
-        <link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}"/>
-    {%- endif %}
-  {%- endblock %}
-  {%- block extrahead %} {% endblock %}
-
-  <script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>
-
-</head>
-
-<body class="wy-body-for-nav" role="document">
-
-  <div class="wy-grid-for-nav">
-
-    {# SIDE NAV, TOGGLES ON MOBILE #}
-    <nav data-toggle="wy-nav-shift" class="wy-nav-side">
-      <div class="wy-side-nav-search">
-        <a href="{{ pathto(master_doc) }}" class="icon icon-home"> {{ project }}</a>
-        {% include "searchbox.html" %}
-      </div>
-
-      <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
-        {% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}
-        {% if toctree %}
-            {{ toctree }}
-        {% else %}
-            <!-- Local TOC -->
-            <div class="local-toc">{{ toc }}</div>
-        {% endif %}
-      </div>
-      &nbsp;
-    </nav>
-
-    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
-
-      {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #}
-      <nav class="wy-nav-top" role="navigation" aria-label="top navigation">
-        <i data-toggle="wy-nav-top" class="icon icon-reorder"></i>
-        <a href="{{ pathto(master_doc) }}">{{ project }}</a>
-      </nav>
-
-
-      {# PAGE CONTENT #}
-      <div class="wy-nav-content">
-        <div class="rst-content">
-          {% include "breadcrumbs.html" %}
-          <div role="main">
-            {% block body %}{% endblock %}
-          </div>
-          {% include "footer.html" %}
-        </div>
-      </div>
-
-    </section>
-
-  </div>
-  {% include "versions.html" %}
-</body>
-</html>

+ 0 - 205
manual/_theme/layout_old.html

@@ -1,205 +0,0 @@
-{#
-    basic/layout.html
-    ~~~~~~~~~~~~~~~~~
-
-    Master layout template for Sphinx themes.
-
-    :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-#}
-{%- block doctype -%}
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-{%- endblock %}
-{%- set reldelim1 = reldelim1 is not defined and ' &raquo;' or reldelim1 %}
-{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
-{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and
-                         (sidebars != []) %}
-{%- set url_root = pathto('', 1) %}
-{# XXX necessary? #}
-{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
-{%- if not embedded and docstitle %}
-  {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
-{%- else %}
-  {%- set titlesuffix = "" %}
-{%- endif %}
-
-{%- macro relbar() %}
-    <div class="related">
-      <h3>{{ _('Navigation') }}</h3>
-      <ul>
-        {%- for rellink in rellinks %}
-        <li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}>
-          <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags|e }}"
-             {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
-          {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li>
-        {%- endfor %}
-        {%- block rootrellink %}
-        <li><a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a>{{ reldelim1 }}</li>
-        {%- endblock %}
-        {%- for parent in parents %}
-          <li><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>
-        {%- endfor %}
-        {%- block relbaritems %} {% endblock %}
-      </ul>
-    </div>
-{%- endmacro %}
-
-{%- macro sidebar() %}
-      {%- if render_sidebar %}
-      <div class="sphinxsidebar">
-        <div class="sphinxsidebarwrapper">
-          {%- block sidebarlogo %}
-          {%- if logo %}
-            <p class="logo"><a href="{{ pathto(master_doc) }}">
-              <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
-            </a></p>
-          {%- endif %}
-          {%- endblock %}
-          {%- if sidebars != None %}
-            {#- new style sidebar: explicitly include/exclude templates #}
-            {%- for sidebartemplate in sidebars %}
-            {%- include sidebartemplate %}
-            {%- endfor %}
-          {%- else %}
-            {#- old style sidebars: using blocks -- should be deprecated #}
-            {%- block sidebartoc %}
-            {%- include "localtoc.html" %}
-            {%- endblock %}
-            {%- block sidebarrel %}
-            {%- include "relations.html" %}
-            {%- endblock %}
-            {%- block sidebarsourcelink %}
-            {%- include "sourcelink.html" %}
-            {%- endblock %}
-            {%- if customsidebar %}
-            {%- include customsidebar %}
-            {%- endif %}
-            {%- block sidebarsearch %}
-            {%- include "searchbox.html" %}
-            {%- endblock %}
-          {%- endif %}
-        </div>
-      </div>
-      {%- endif %}
-{%- endmacro %}
-
-{%- macro script() %}
-    <script type="text/javascript">
-      var DOCUMENTATION_OPTIONS = {
-        URL_ROOT:    '{{ url_root }}',
-        VERSION:     '{{ release|e }}',
-        COLLAPSE_INDEX: false,
-        FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
-        HAS_SOURCE:  {{ has_source|lower }}
-      };
-    </script>
-    {%- for scriptfile in script_files %}
-    <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
-    {%- endfor %}
-{%- endmacro %}
-
-{%- macro css() %}
-    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
-    <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
-    {%- for cssfile in css_files %}
-    <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
-    {%- endfor %}
-{%- endmacro %}
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" />
-    {{ metatags }}
-    {%- block htmltitle %}
-    <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
-    {%- endblock %}
-    {{ css() }}
-    {%- if not embedded %}
-    {{ script() }}
-    {%- if use_opensearch %}
-    <link rel="search" type="application/opensearchdescription+xml"
-          title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
-          href="{{ pathto('_static/opensearch.xml', 1) }}"/>
-    {%- endif %}
-    {%- if favicon %}
-    <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
-    {%- endif %}
-    {%- endif %}
-{%- block linktags %}
-    {%- if hasdoc('about') %}
-    <link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" />
-    {%- endif %}
-    {%- if hasdoc('genindex') %}
-    <link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" />
-    {%- endif %}
-    {%- if hasdoc('search') %}
-    <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" />
-    {%- endif %}
-    {%- if hasdoc('copyright') %}
-    <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" />
-    {%- endif %}
-    <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" />
-    {%- if parents %}
-    <link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}" />
-    {%- endif %}
-    {%- if next %}
-    <link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}" />
-    {%- endif %}
-    {%- if prev %}
-    <link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}" />
-    {%- endif %}
-{%- endblock %}
-{%- block extrahead %} {% endblock %}
-  </head>
-  <body>
-{%- block header %}{% endblock %}
-
-{%- block relbar1 %}{{ relbar() }}{% endblock %}
-
-{%- block content %}
-  {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %}
-
-    <div class="document">
-  {%- block document %}
-      <div class="documentwrapper">
-      {%- if render_sidebar %}
-        <div class="bodywrapper">
-      {%- endif %}
-          <div class="body">
-            {% block body %} {% endblock %}
-          </div>
-      {%- if render_sidebar %}
-        </div>
-      {%- endif %}
-      </div>
-  {%- endblock %}
-
-  {%- block sidebar2 %}{{ sidebar() }}{% endblock %}
-      <div class="clearer"></div>
-    </div>
-{%- endblock %}
-
-{%- block relbar2 %}{{ relbar() }}{% endblock %}
-
-{%- block footer %}
-    <div class="footer">
-    {%- if show_copyright %}
-      {%- if hasdoc('copyright') %}
-        {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
-      {%- else %}
-        {% trans copyright=copyright|e %}&copy; Copyright {{ copyright }}.{% endtrans %}
-      {%- endif %}
-    {%- endif %}
-    {%- if last_updated %}
-      {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
-    {%- endif %}
-    {%- if show_sphinx %}
-      {% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx-doc.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %}
-    {%- endif %}
-    </div>
-    <p>asdf asdf asdf asdf 22</p>
-{%- endblock %}
-  </body>
-</html>
-

+ 0 - 50
manual/_theme/search.html

@@ -1,50 +0,0 @@
-{#
-    basic/search.html
-    ~~~~~~~~~~~~~~~~~
-
-    Template for the search page.
-
-    :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-#}
-{%- extends "layout.html" %}
-{% set title = _('Search') %}
-{% set script_files = script_files + ['_static/searchtools.js'] %}
-{% block extrahead %}
-  <script type="text/javascript">
-    jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); });
-  </script>
-  {# this is used when loading the search index using $.ajax fails,
-     such as on Chrome for documents on localhost #}
-  <script type="text/javascript" id="searchindexloader"></script>
-  {{ super() }}
-{% endblock %}
-{% block body %}
-  <noscript>
-  <div id="fallback" class="admonition warning">
-    <p class="last">
-      {% trans %}Please activate JavaScript to enable the search
-      functionality.{% endtrans %}
-    </p>
-  </div>
-  </noscript>
-
-  {% if search_performed %}
-    <h2>{{ _('Search Results') }}</h2>
-    {% if not search_results %}
-      <p>{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}</p>
-    {% endif %}
-  {% endif %}
-  <div id="search-results">
-  {% if search_results %}
-    <ul>
-    {% for href, caption, context in search_results %}
-      <li>
-        <a href="{{ pathto(item.href) }}">{{ caption }}</a>
-        <p class="context">{{ context|e }}</p>
-      </li>
-    {% endfor %}
-    </ul>
-  {% endif %}
-  </div>
-{% endblock %}

+ 0 - 7
manual/_theme/searchbox.html

@@ -1,7 +0,0 @@
-<div role="search">
-  <form id ="rtd-search-form" class="wy-form" action="{{ pathto('search') }}" method="get">
-    <input type="text" name="q" placeholder="Search docs" />
-    <input type="hidden" name="check_keywords" value="yes" />
-    <input type="hidden" name="area" value="default" />
-  </form>
-</div>

+ 0 - 17
manual/_theme/sphinx_rtd_theme/__init__.py

@@ -1,17 +0,0 @@
-"""Sphinx ReadTheDocs theme.
-
-From https://github.com/ryan-roemer/sphinx-bootstrap-theme.
-
-"""
-import os
-
-VERSION = (0, 1, 5)
-
-__version__ = ".".join(str(v) for v in VERSION)
-__version_full__ = __version__
-
-
-def get_html_theme_path():
-    """Return list of HTML theme paths."""
-    cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
-    return cur_dir

+ 0 - 19
manual/_theme/sphinx_rtd_theme/breadcrumbs.html

@@ -1,19 +0,0 @@
-<div role="navigation" aria-label="breadcrumbs navigation">
-  <ul class="wy-breadcrumbs">
-    <li><a href="{{ pathto(master_doc) }}">Docs</a> &raquo;</li>
-      {% for doc in parents %}
-          <li><a href="{{ doc.link|e }}">{{ doc.title }}</a> &raquo;</li>
-      {% endfor %}
-    <li>{{ title }}</li>
-      <li class="wy-breadcrumbs-aside">
-        {% if display_github %}
-          <a href="https://github.com/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}.rst" class="icon icon-github"> Edit on GitHub</a>
-        {% elif display_bitbucket %}
-          <a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}.rst" class="icon icon-bitbucket"> Edit on Bitbucket</a>
-        {% elif show_source and has_source and sourcename %}
-          <a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
-        {% endif %}
-      </li>
-  </ul>
-  <hr/>
-</div>

+ 0 - 32
manual/_theme/sphinx_rtd_theme/footer.html

@@ -1,32 +0,0 @@
-<footer>
-  {% if next or prev %}
-    <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
-      {% if next %}
-        <a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}"/>Next <span class="icon icon-circle-arrow-right"></span></a>
-      {% endif %}
-      {% if prev %}
-        <a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}"><span class="icon icon-circle-arrow-left"></span> Previous</a>
-      {% endif %}
-    </div>
-  {% endif %}
-
-  <hr/>
-
-  <div role="contentinfo">
-    <p>
-    {%- if show_copyright %}
-      {%- if hasdoc('copyright') %}
-        {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
-      {%- else %}
-        {% trans copyright=copyright|e %}&copy; Copyright {{ copyright }}.{% endtrans %}
-      {%- endif %}
-    {%- endif %}
-
-    {%- if last_updated %}
-      {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
-    {%- endif %}
-    </p>
-  </div>
-
-  {% trans %}<a href="https://github.com/snide/sphinx_rtd_theme">Sphinx theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>{% endtrans %}
-</footer>

+ 0 - 170
manual/_theme/sphinx_rtd_theme/layout.html

@@ -1,170 +0,0 @@
-{# TEMPLATE VAR SETTINGS #}
-{%- set url_root = pathto('', 1) %}
-{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
-{%- if not embedded and docstitle %}
-  {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
-{%- else %}
-  {%- set titlesuffix = "" %}
-{%- endif %}
-
-<!DOCTYPE html>
-<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
-<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
-<head>
-  <meta charset="utf-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  {% block htmltitle %}
-  <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
-  {% endblock %}
-
-  {# FAVICON #}
-  {% if favicon %}
-    <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
-  {% endif %}
-
-  {# CSS #}
-  <link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
-
-  {# JS #}
-  {% if not embedded %}
-
-    <script type="text/javascript">
-      var DOCUMENTATION_OPTIONS = {
-        URL_ROOT:'{{ url_root }}',
-        VERSION:'{{ release|e }}',
-        COLLAPSE_INDEX:false,
-        FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',
-        HAS_SOURCE:  {{ has_source|lower }}
-      };
-    </script>
-    {%- for scriptfile in script_files %}
-      <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
-    {%- endfor %}
-
-    {% if use_opensearch %}
-      <link rel="search" type="application/opensearchdescription+xml" title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}" href="{{ pathto('_static/opensearch.xml', 1) }}"/>
-    {% endif %}
-
-  {% endif %}
-
-  {# RTD hosts these file themselves, so just load on non RTD builds #}
-  {% if not READTHEDOCS %}
-    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
-    <script type="text/javascript" src="{{ pathto('_static/js/theme.js', 1) }}"></script>
-  {% endif %}
-
-  {# STICKY NAVIGATION #}
-  {% if theme_sticky_navigation %}
-    <script type="text/javascript">
-        jQuery(function () {
-            SphinxRtdTheme.StickyNav.enable();
-        });
-    </script>
-  {% endif %}
-
-  {% for cssfile in css_files %}
-    <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
-  {% endfor %}
-
-  {%- block linktags %}
-    {%- if hasdoc('about') %}
-        <link rel="author" title="{{ _('About these documents') }}"
-              href="{{ pathto('about') }}"/>
-    {%- endif %}
-    {%- if hasdoc('genindex') %}
-        <link rel="index" title="{{ _('Index') }}"
-              href="{{ pathto('genindex') }}"/>
-    {%- endif %}
-    {%- if hasdoc('search') %}
-        <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}"/>
-    {%- endif %}
-    {%- if hasdoc('copyright') %}
-        <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}"/>
-    {%- endif %}
-    <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}"/>
-    {%- if parents %}
-        <link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}"/>
-    {%- endif %}
-    {%- if next %}
-        <link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}"/>
-    {%- endif %}
-    {%- if prev %}
-        <link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}"/>
-    {%- endif %}
-  {%- endblock %}
-  {%- block extrahead %} {% endblock %}
-
-  <script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>
-
-
-
-<script>
-  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
-  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
-  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
-  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
-
-  ga('create', 'UA-55008703-1', 'auto');
-  ga('send', 'pageview');
-
-</script>
-</head>
-
-<body class="wy-body-for-nav" role="document">
-
-  <div class="wy-grid-for-nav">
-
-    {# SIDE NAV, TOGGLES ON MOBILE #}
-    <nav data-toggle="wy-nav-shift" class="wy-nav-side">
-      <div class="wy-side-nav-search" style="background-color: #3d3d3d;">
-        <!--<a href="{{ pathto(master_doc) }}" class="icon icon-home"> {{ project }}</a>-->
-        <!--<a href="http://flatcam.org" class="icon icon-home"> {{ project }}</a>-->
-        <a href="http://flatcam.org">
-            <img src="http://flatcam.org/static/images/fcweblogo1.png"
-                    style="height: auto;
-                    width: auto;
-                    border-radius: 0px;
-                    background-color: #3d3d3d;
-                    margin: auto">
-        </a>
-        {% include "searchbox.html" %}
-      </div>
-
-      <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
-        {% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}
-        {% if toctree %}
-            {{ toctree }}
-        {% else %}
-            <!-- Local TOC -->
-            <div class="local-toc">{{ toc }}</div>
-        {% endif %}
-      </div>
-      &nbsp;
-    </nav>
-
-    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
-
-      {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #}
-      <nav class="wy-nav-top" role="navigation" aria-label="top navigation">
-        <i data-toggle="wy-nav-top" class="icon icon-reorder"></i>
-        <a href="{{ pathto(master_doc) }}">{{ project }}</a>
-      </nav>
-
-
-      {# PAGE CONTENT #}
-      <div class="wy-nav-content">
-        <div class="rst-content">
-          {% include "breadcrumbs.html" %}
-          <div role="main">
-            {% block body %}{% endblock %}
-          </div>
-          {% include "footer.html" %}
-        </div>
-      </div>
-
-    </section>
-
-  </div>
-  {% include "versions.html" %}
-</body>
-</html>

+ 0 - 205
manual/_theme/sphinx_rtd_theme/layout_old.html

@@ -1,205 +0,0 @@
-{#
-    basic/layout.html
-    ~~~~~~~~~~~~~~~~~
-
-    Master layout template for Sphinx themes.
-
-    :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-#}
-{%- block doctype -%}
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-{%- endblock %}
-{%- set reldelim1 = reldelim1 is not defined and ' &raquo;' or reldelim1 %}
-{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
-{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and
-                         (sidebars != []) %}
-{%- set url_root = pathto('', 1) %}
-{# XXX necessary? #}
-{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
-{%- if not embedded and docstitle %}
-  {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
-{%- else %}
-  {%- set titlesuffix = "" %}
-{%- endif %}
-
-{%- macro relbar() %}
-    <div class="related">
-      <h3>{{ _('Navigation') }}</h3>
-      <ul>
-        {%- for rellink in rellinks %}
-        <li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}>
-          <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags|e }}"
-             {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
-          {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li>
-        {%- endfor %}
-        {%- block rootrellink %}
-        <li><a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a>{{ reldelim1 }}</li>
-        {%- endblock %}
-        {%- for parent in parents %}
-          <li><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>
-        {%- endfor %}
-        {%- block relbaritems %} {% endblock %}
-      </ul>
-    </div>
-{%- endmacro %}
-
-{%- macro sidebar() %}
-      {%- if render_sidebar %}
-      <div class="sphinxsidebar">
-        <div class="sphinxsidebarwrapper">
-          {%- block sidebarlogo %}
-          {%- if logo %}
-            <p class="logo"><a href="{{ pathto(master_doc) }}">
-              <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
-            </a></p>
-          {%- endif %}
-          {%- endblock %}
-          {%- if sidebars != None %}
-            {#- new style sidebar: explicitly include/exclude templates #}
-            {%- for sidebartemplate in sidebars %}
-            {%- include sidebartemplate %}
-            {%- endfor %}
-          {%- else %}
-            {#- old style sidebars: using blocks -- should be deprecated #}
-            {%- block sidebartoc %}
-            {%- include "localtoc.html" %}
-            {%- endblock %}
-            {%- block sidebarrel %}
-            {%- include "relations.html" %}
-            {%- endblock %}
-            {%- block sidebarsourcelink %}
-            {%- include "sourcelink.html" %}
-            {%- endblock %}
-            {%- if customsidebar %}
-            {%- include customsidebar %}
-            {%- endif %}
-            {%- block sidebarsearch %}
-            {%- include "searchbox.html" %}
-            {%- endblock %}
-          {%- endif %}
-        </div>
-      </div>
-      {%- endif %}
-{%- endmacro %}
-
-{%- macro script() %}
-    <script type="text/javascript">
-      var DOCUMENTATION_OPTIONS = {
-        URL_ROOT:    '{{ url_root }}',
-        VERSION:     '{{ release|e }}',
-        COLLAPSE_INDEX: false,
-        FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
-        HAS_SOURCE:  {{ has_source|lower }}
-      };
-    </script>
-    {%- for scriptfile in script_files %}
-    <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
-    {%- endfor %}
-{%- endmacro %}
-
-{%- macro css() %}
-    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
-    <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
-    {%- for cssfile in css_files %}
-    <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
-    {%- endfor %}
-{%- endmacro %}
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" />
-    {{ metatags }}
-    {%- block htmltitle %}
-    <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
-    {%- endblock %}
-    {{ css() }}
-    {%- if not embedded %}
-    {{ script() }}
-    {%- if use_opensearch %}
-    <link rel="search" type="application/opensearchdescription+xml"
-          title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
-          href="{{ pathto('_static/opensearch.xml', 1) }}"/>
-    {%- endif %}
-    {%- if favicon %}
-    <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
-    {%- endif %}
-    {%- endif %}
-{%- block linktags %}
-    {%- if hasdoc('about') %}
-    <link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" />
-    {%- endif %}
-    {%- if hasdoc('genindex') %}
-    <link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" />
-    {%- endif %}
-    {%- if hasdoc('search') %}
-    <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" />
-    {%- endif %}
-    {%- if hasdoc('copyright') %}
-    <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" />
-    {%- endif %}
-    <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" />
-    {%- if parents %}
-    <link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}" />
-    {%- endif %}
-    {%- if next %}
-    <link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}" />
-    {%- endif %}
-    {%- if prev %}
-    <link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}" />
-    {%- endif %}
-{%- endblock %}
-{%- block extrahead %} {% endblock %}
-  </head>
-  <body>
-{%- block header %}{% endblock %}
-
-{%- block relbar1 %}{{ relbar() }}{% endblock %}
-
-{%- block content %}
-  {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %}
-
-    <div class="document">
-  {%- block document %}
-      <div class="documentwrapper">
-      {%- if render_sidebar %}
-        <div class="bodywrapper">
-      {%- endif %}
-          <div class="body">
-            {% block body %} {% endblock %}
-          </div>
-      {%- if render_sidebar %}
-        </div>
-      {%- endif %}
-      </div>
-  {%- endblock %}
-
-  {%- block sidebar2 %}{{ sidebar() }}{% endblock %}
-      <div class="clearer"></div>
-    </div>
-{%- endblock %}
-
-{%- block relbar2 %}{{ relbar() }}{% endblock %}
-
-{%- block footer %}
-    <div class="footer">
-    {%- if show_copyright %}
-      {%- if hasdoc('copyright') %}
-        {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
-      {%- else %}
-        {% trans copyright=copyright|e %}&copy; Copyright {{ copyright }}.{% endtrans %}
-      {%- endif %}
-    {%- endif %}
-    {%- if last_updated %}
-      {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
-    {%- endif %}
-    {%- if show_sphinx %}
-      {% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx-doc.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %}
-    {%- endif %}
-    </div>
-    <p>asdf asdf asdf asdf 22</p>
-{%- endblock %}
-  </body>
-</html>
-

+ 0 - 50
manual/_theme/sphinx_rtd_theme/search.html

@@ -1,50 +0,0 @@
-{#
-    basic/search.html
-    ~~~~~~~~~~~~~~~~~
-
-    Template for the search page.
-
-    :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-#}
-{%- extends "layout.html" %}
-{% set title = _('Search') %}
-{% set script_files = script_files + ['_static/searchtools.js'] %}
-{% block extrahead %}
-  <script type="text/javascript">
-    jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); });
-  </script>
-  {# this is used when loading the search index using $.ajax fails,
-     such as on Chrome for documents on localhost #}
-  <script type="text/javascript" id="searchindexloader"></script>
-  {{ super() }}
-{% endblock %}
-{% block body %}
-  <noscript>
-  <div id="fallback" class="admonition warning">
-    <p class="last">
-      {% trans %}Please activate JavaScript to enable the search
-      functionality.{% endtrans %}
-    </p>
-  </div>
-  </noscript>
-
-  {% if search_performed %}
-    <h2>{{ _('Search Results') }}</h2>
-    {% if not search_results %}
-      <p>{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}</p>
-    {% endif %}
-  {% endif %}
-  <div id="search-results">
-  {% if search_results %}
-    <ul>
-    {% for href, caption, context in search_results %}
-      <li>
-        <a href="{{ pathto(item.href) }}">{{ caption }}</a>
-        <p class="context">{{ context|e }}</p>
-      </li>
-    {% endfor %}
-    </ul>
-  {% endif %}
-  </div>
-{% endblock %}

+ 0 - 7
manual/_theme/sphinx_rtd_theme/searchbox.html

@@ -1,7 +0,0 @@
-<div role="search">
-  <form id ="rtd-search-form" class="wy-form" action="{{ pathto('search') }}" method="get">
-    <input type="text" name="q" placeholder="Search docs" />
-    <input type="hidden" name="check_keywords" value="yes" />
-    <input type="hidden" name="area" value="default" />
-  </form>
-</div>

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
manual/_theme/sphinx_rtd_theme/static/css/badge_only.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
manual/_theme/sphinx_rtd_theme/static/css/theme.css


BIN
manual/_theme/sphinx_rtd_theme/static/font/fontawesome_webfont.eot


Разница между файлами не показана из-за своего большого размера
+ 0 - 195
manual/_theme/sphinx_rtd_theme/static/font/fontawesome_webfont.svg


BIN
manual/_theme/sphinx_rtd_theme/static/font/fontawesome_webfont.ttf


BIN
manual/_theme/sphinx_rtd_theme/static/font/fontawesome_webfont.woff


+ 0 - 47
manual/_theme/sphinx_rtd_theme/static/js/theme.js

@@ -1,47 +0,0 @@
-$( document ).ready(function() {
-    // Shift nav in mobile when clicking the menu.
-    $(document).on('click', "[data-toggle='wy-nav-top']", function() {
-      $("[data-toggle='wy-nav-shift']").toggleClass("shift");
-      $("[data-toggle='rst-versions']").toggleClass("shift");
-    });
-    // Close menu when you click a link.
-    $(document).on('click', ".wy-menu-vertical .current ul li a", function() {
-      $("[data-toggle='wy-nav-shift']").removeClass("shift");
-      $("[data-toggle='rst-versions']").toggleClass("shift");
-    });
-    $(document).on('click', "[data-toggle='rst-current-version']", function() {
-      $("[data-toggle='rst-versions']").toggleClass("shift-up");
-    });  
-    // Make tables responsive
-    $("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>");
-});
-
-window.SphinxRtdTheme = (function (jquery) {
-    var stickyNav = (function () {
-        var navBar,
-            win,
-            stickyNavCssClass = 'stickynav',
-            applyStickNav = function () {
-                if (navBar.height() <= win.height()) {
-                    navBar.addClass(stickyNavCssClass);
-                } else {
-                    navBar.removeClass(stickyNavCssClass);
-                }
-            },
-            enable = function () {
-                applyStickNav();
-                win.on('resize', applyStickNav);
-            },
-            init = function () {
-                navBar = jquery('nav.wy-nav-side:first');
-                win    = jquery(window);
-            };
-        jquery(init);
-        return {
-            enable : enable
-        };
-    }());
-    return {
-        StickyNav : stickyNav
-    };
-}($));

+ 0 - 8
manual/_theme/sphinx_rtd_theme/theme.conf

@@ -1,8 +0,0 @@
-[theme]
-inherit = basic
-stylesheet = css/theme.css
-
-[options]
-typekit_id = hiw1hhg
-analytics_id = 
-sticky_navigation = False

+ 0 - 37
manual/_theme/sphinx_rtd_theme/versions.html

@@ -1,37 +0,0 @@
-{% if READTHEDOCS %}
-{# Add rst-badge after rst-versions for small badge style. #}
-  <div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="versions">
-    <span class="rst-current-version" data-toggle="rst-current-version">
-      <span class="icon icon-book"> Read the Docs</span>
-      v: {{ current_version }} 
-      <span class="icon icon-caret-down"></span>
-    </span>
-    <div class="rst-other-versions">
-      <dl>
-        <dt>Versions</dt>
-        {% for slug, url in versions %}
-          <dd><a href="{{ url }}">{{ slug }}</a></dd>
-        {% endfor %}
-      </dl>
-      <dl>
-        <dt>Downloads</dt>
-        {% for type, url in downloads %}
-          <dd><a href="{{ url }}">{{ type }}</a></dd>
-        {% endfor %}
-      </dl>
-      <dl>
-        <dt>On Read the Docs</dt>
-          <dd>
-            <a href="//{{ PRODUCTION_DOMAIN }}/projects/{{ slug }}/?fromdocs={{ slug }}">Project Home</a>
-          </dd>
-          <dd>
-            <a href="//{{ PRODUCTION_DOMAIN }}/builds/{{ slug }}/?fromdocs={{ slug }}">Builds</a>
-          </dd>
-      </dl>
-      <hr/>
-      Free document hosting provided by <a href="http://www.readthedocs.org">Read the Docs</a>.
-
-    </div>
-  </div>
-{% endif %}
-

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
manual/_theme/static/css/badge_only.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
manual/_theme/static/css/theme.css


BIN
manual/_theme/static/font/fontawesome_webfont.eot


Разница между файлами не показана из-за своего большого размера
+ 0 - 195
manual/_theme/static/font/fontawesome_webfont.svg


BIN
manual/_theme/static/font/fontawesome_webfont.ttf


BIN
manual/_theme/static/font/fontawesome_webfont.woff


+ 0 - 47
manual/_theme/static/js/theme.js

@@ -1,47 +0,0 @@
-$( document ).ready(function() {
-    // Shift nav in mobile when clicking the menu.
-    $(document).on('click', "[data-toggle='wy-nav-top']", function() {
-      $("[data-toggle='wy-nav-shift']").toggleClass("shift");
-      $("[data-toggle='rst-versions']").toggleClass("shift");
-    });
-    // Close menu when you click a link.
-    $(document).on('click', ".wy-menu-vertical .current ul li a", function() {
-      $("[data-toggle='wy-nav-shift']").removeClass("shift");
-      $("[data-toggle='rst-versions']").toggleClass("shift");
-    });
-    $(document).on('click', "[data-toggle='rst-current-version']", function() {
-      $("[data-toggle='rst-versions']").toggleClass("shift-up");
-    });  
-    // Make tables responsive
-    $("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>");
-});
-
-window.SphinxRtdTheme = (function (jquery) {
-    var stickyNav = (function () {
-        var navBar,
-            win,
-            stickyNavCssClass = 'stickynav',
-            applyStickNav = function () {
-                if (navBar.height() <= win.height()) {
-                    navBar.addClass(stickyNavCssClass);
-                } else {
-                    navBar.removeClass(stickyNavCssClass);
-                }
-            },
-            enable = function () {
-                applyStickNav();
-                win.on('resize', applyStickNav);
-            },
-            init = function () {
-                navBar = jquery('nav.wy-nav-side:first');
-                win    = jquery(window);
-            };
-        jquery(init);
-        return {
-            enable : enable
-        };
-    }());
-    return {
-        StickyNav : stickyNav
-    };
-}($));

+ 0 - 8
manual/_theme/theme.conf

@@ -1,8 +0,0 @@
-[theme]
-inherit = basic
-stylesheet = css/theme.css
-
-[options]
-typekit_id = hiw1hhg
-analytics_id = 
-sticky_navigation = False

+ 0 - 37
manual/_theme/versions.html

@@ -1,37 +0,0 @@
-{% if READTHEDOCS %}
-{# Add rst-badge after rst-versions for small badge style. #}
-  <div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="versions">
-    <span class="rst-current-version" data-toggle="rst-current-version">
-      <span class="icon icon-book"> Read the Docs</span>
-      v: {{ current_version }} 
-      <span class="icon icon-caret-down"></span>
-    </span>
-    <div class="rst-other-versions">
-      <dl>
-        <dt>Versions</dt>
-        {% for slug, url in versions %}
-          <dd><a href="{{ url }}">{{ slug }}</a></dd>
-        {% endfor %}
-      </dl>
-      <dl>
-        <dt>Downloads</dt>
-        {% for type, url in downloads %}
-          <dd><a href="{{ url }}">{{ type }}</a></dd>
-        {% endfor %}
-      </dl>
-      <dl>
-        <dt>On Read the Docs</dt>
-          <dd>
-            <a href="//{{ PRODUCTION_DOMAIN }}/projects/{{ slug }}/?fromdocs={{ slug }}">Project Home</a>
-          </dd>
-          <dd>
-            <a href="//{{ PRODUCTION_DOMAIN }}/builds/{{ slug }}/?fromdocs={{ slug }}">Builds</a>
-          </dd>
-      </dl>
-      <hr/>
-      Free document hosting provided by <a href="http://www.readthedocs.org">Read the Docs</a>.
-
-    </div>
-  </div>
-{% endif %}
-

+ 0 - 46
manual/basics.rst

@@ -1,46 +0,0 @@
-Basics
-======
-
-Source Files
-------------
-
-Supported source files are:
-
-* **Gerber**: Typically define copper layers in a circuit board.
-* **Excellon**: (drill file): Contain drill specifications, size and coordinates.
-* **G-Code**: CNC machine instructions for cutting and/or drilling.
-
-These source files can be loaded by selecting File→Open Gerber…, File→Open Excellon… or File→Open G-Code… respectively. The objects created from source files are automatically added to the current project when loaded.
-
-
-Objects and Tasks
------------------
-
-Data in FlatCAM is in the form of 4 different kinds of objects: Gerber, Excellon, Geometry and CNC Job. Gerber, Excellon and CNC Jos objects are directly created by reading files in Gerber, Excellon and G-Code formats. Geometry objects are an intermediate step available to manipulate data. The diagram bellow illustrates the relationship between files and objects. The arrows connecting objects represent a sub-set of the tasks that can be performed in FlatCAM.
-
-.. image:: objects_flow.png
-    :align: center
-
-
-Creating, Saving and Loading Projects
--------------------------------------
-
-A project is everything that you have loaded, created and set inside the program. A new project is created every time you load the program or run File→New.
-
-By running File→Save Project, File→Save Project As… or File→Save a Project Copy… you are saving everything currently in the environment including project options. File→Open Project… lets you load a saved project.
-
-
-Navigating Plots
-----------------
-
-Plots for open objects (Gerber, drills, g-code, etc…) are automatically shown on screen. A plot for a given can be updated by clicking “Update Plot” in the “Selected” tab, in case any parameters that would have changed the plot have been modified.
-
-Zooming plots in and out is accomplished by clicking on the plot and using the mouse **scroll wheel** or hitting one of the following keys:
-
-* ``1``: Fits all graphics to the available plotting area.
-* ``2``: Zooms out
-* ``3``: Zooms in
-
-When zooming in or out, the point under the cursor stays at the same location.
-
-To scroll left-right or up-down, hold the ``shift`` or ``control`` key respectively while turning the mouse **scroll wheel**.

+ 0 - 241
manual/cmdreference.rst

@@ -1,241 +0,0 @@
-.. _cmdreference:
-
-Shell Command Reference
-=======================
-
-.. warning::
-    The FlatCAM Shell is under development and its behavior might change in the future. This includes available commands and their syntax.
-
-.. _add_circle:
-
-add_circle
-~~~~~~~~~~
-Creates a circle in the given Geometry object.
-
-    > add_circle <name> <center_x> <center_y> <radius>
-       name: Name of the geometry object to which to append the circle.
-
-       center_x, center_y: Coordinates of the center of the circle.
-
-       radius: Radius of the circle.
-
-.. _add_poly:
-
-add_poly
-~~~~~~~~
-Creates a polygon in the given Geometry object.
-
-    > create_poly <name> <x0> <y0> <x1> <y1> <x2> <y2> [x3 y3 [...]]
-       name: Name of the geometry object to which to append the polygon.
-
-       xi, yi: Coordinates of points in the polygon.
-
-.. _add_rect:
-
-add_rect
-~~~~~~~~
-Creates a rectange in the given Geometry object.
-
-    > add_rect <name> <botleft_x> <botleft_y> <topright_x> <topright_y>
-       name: Name of the geometry object to which to append the rectangle.
-
-       botleft_x, botleft_y: Coordinates of the bottom left corner.
-
-       topright_x, topright_y Coordinates of the top right corner.
-
-cncjob
-~~~~~~
-Generates a CNC Job from a Geometry Object.
-
-    > cncjob <name> [-z_cut <c>] [-z_move <m>] [-feedrate <f>] [-tooldia <t>] [-outname <n>]
-       name: Name of the source object
-
-       z_cut: Z-axis cutting position
-
-       z_move: Z-axis moving position
-
-       feedrate: Moving speed when cutting
-
-       tooldia: Tool diameter to show on screen
-
-       outname: Name of the output object
-
-delete
-~~~~~~
-Deletes the give object.
-
-    > delete <name>
-       name: Name of the object to delete.
-
-follow
-~~~~~~
-Creates a geometry object following gerber paths.
-
-    > follow <name> [-outname <oname>]
-       name: Name of the gerber object.
-
-       outname: Name of the output geometry object.
-
-.. _geo_union:
-
-geo_union
-~~~~~~~~~
-Runs a union operation (addition) on the components of the geometry object. For example, if it contains 2 intersecting polygons, this opperation adds them intoa single larger polygon.
-
-    > geo_union <name>
-       name: Name of the geometry object.
-
-get_names
-~~~~~~~~~
-Lists the names of objects in the project.
-
-
-    > get_names
-       No parameters.
-
-help
-~~~~
-Shows list of commands.
-
-isolate
-~~~~~~~
-Creates isolation routing geometry for the given Gerber.
-
-    > isolate <name> [-dia <d>] [-passes <p>] [-overlap <o>]
-       name: Name of the object
-
-       dia: Tool diameter
-
-       passes: # of tool width
-
-       overlap: Fraction of tool diameter to overlap passes
-
-make_docs
-~~~~~~~~~
-Prints command rererence in reStructuredText format.
-
-new
-~~~
-Starts a new project. Clears objects from memory.
-
-
-    > new
-       No parameters.
-
-.. _new_geometry:
-
-new_geometry
-~~~~~~~~~~~~
-Creates a new empty geometry object.
-
-    > new_geometry <name>
-       name: New object name
-
-.. _offset:
-
-offset
-~~~~~~
-Changes the position of the object.
-
-    > offset <name> <x> <y>
-       name: Name of the object
-
-       x: X-axis distance
-
-       y: Y-axis distance
-
-open_excellon
-~~~~~~~~~~~~~
-Opens an Excellon file.
-
-    > open_excellon <filename> [-outname <o>]
-       filename: Path to file to open.
-
-       outname: Name of the created excellon object.
-
-open_gcode
-~~~~~~~~~~
-Opens an G-Code file.
-
-    > open_gcode <filename> [-outname <o>]
-       filename: Path to file to open.
-
-       outname: Name of the created CNC Job object.
-
-open_gerber
-~~~~~~~~~~~
-Opens a Gerber file.
-
-    > open_gerber <filename> [-follow <0|1>] [-outname <o>]
-       filename: Path to file to open.
-
-       follow: If 1, does not create polygons, just follows the gerber path.
-
-       outname: Name of the created gerber object.
-
-open_project
-~~~~~~~~~~~~
-Opens a FlatCAM project.
-
-    > open_project <filename>
-       filename: Path to file to open.
-
-options
-~~~~~~~
-Shows the settings for an object.
-
-
-    > options <name>
-       name: Object name.
-
-paint_poly
-~~~~~~~~~~
-Creates a geometry object with toolpath to cover the inside of a polygon.
-
-    > paint_poly <name> <inside_pt_x> <inside_pt_y> <tooldia> <overlap>
-       name: Name of the sourge geometry object.
-
-       inside_pt_x, inside_pt_y: Coordinates of a point inside the polygon.
-
-       tooldia: Diameter of the tool to be used.
-
-       overlap: Fraction of the tool diameter to overlap cuts.
-
-plot
-~~~~
-Updates the plot on the user interface
-
-save_project
-~~~~~~~~~~~~
-Saves the FlatCAM project to file.
-
-    > save_project <filename>
-       filename: Path to file to save.
-
-.. _scale:
-
-scale
-~~~~~
-Resizes the object by a factor.
-
-    > scale <name> <factor>
-       name: Name of the object
-
-       factor: Fraction by which to scale
-
-set_active
-~~~~~~~~~~
-Sets a FlatCAM object as active.
-
-
-    > set_active <name>
-       name: Name of the object.
-
-write_gcode
-~~~~~~~~~~~
-Saves G-code of a CNC Job object to file.
-
-    > write_gcode <name> <filename>
-       name: Source CNC Job object
-
-       filename: Output filename

+ 0 - 134
manual/editor.rst

@@ -1,134 +0,0 @@
-Geometry Editor
-===============
-
-Introduction
-------------
-
-The Geometry Editor is a drawing CAD that allows you to edit
-FlatCAM Geometry Objects or create new ones from scratch. This
-provides the ultimate flexibility by letting you specify precisely
-and arbitrarily what you want your CNC router to do.
-
-Creating New Geometry Objects
------------------------------
-
-To create a blank Geometry Object, simply click on the menu item
-**Edit→New Geometry Object** or click the **New Blank Geometry** button on
-the toolbar. A Geometry object with the name "New Geometry" will
-be added to your project list.
-
-.. image:: editor1.png
-   :align: center
-
-.. seealso::
-
-   FlatCAM Shell command :ref:`new_geometry`
-
-
-Editing Existing Geometry Objects
----------------------------------
-
-To edit a Geometry Object, select it from the project list and
-click on the menu item **Edit→Edit Geometry** or on the **Edit Geometry**
-toolbar button.
-
-This will make a copy of the selected object in the editor and
-the editor toolbar buttons will become active.
-
-Changes made to the geometry in the editor will not affect the
-Geometry Object until the **Edit->Update Geometry** button or
-**Update Geometry** toolbar button is clicked.
-This replaces the geometry in the currently selected Geometry
-Object (which can be different from which the editor copied its
-contents originally) with the geometry in the editor.
-
-Selecting Shapes
-~~~~~~~~~~~~~~~~
-
-When the **Selection Tool** is active in the toolbar (Hit ``Esc``), clicking on the
-plot will select the nearest shape. If one shape is inside the other,
-you might need to move the outer one to get to the inner one. This
-behavior might be improved in the future.
-
-Holding the ``Control`` key while clicking will add the nearest shape
-to the set of selected objects.
-
-Creating Shapes
-~~~~~~~~~~~~~~~
-
-The shape creation tools in the editor are:
-
-* Circle
-* Arc
-* Rectangle
-* Polygon
-* Path
-
-.. image:: editor2.png
-   :align: center
-
-After clicking on the respective toolbar button, follow the instructions
-on the status bar.
-
-Shapes that do not require a fixed number of clicks to complete, like
-polygons and paths, are complete by hitting the ``Space`` key.
-
-Certain shape tools can have different options or modes. By hitting
-`o` and/or `p` the tool will cycle through its options and/or modes.
-
-.. seealso::
-
-   The FlatCAM Shell commands :ref:`add_circle`, :ref:`add_poly` and :ref:`add_rect`,
-   create shapes directly on a given Geometry Object.
-
-Union
-~~~~~
-
-Clicking on the **Union** tool after selecting two or more shapes
-will create a union. For closed shapes, their union is a polygon covering
-the area that all the selected shapes encompassed. Unions of disjoint shapes
-can still be created and is equivalent to grouping shapes.
-
-.. image:: editor_union.png
-   :align: center
-
-.. seealso::
-
-   The FlatCAM Shell command :ref:`geo_union` executes a union of
-   all geometry in a Geometry object.
-
-Moving and Copying
-~~~~~~~~~~~~~~~~~~
-
-The **Move** and **Copy** tools work on selected objects. As soon as the tool
-is selected (On the toolbar or the ``m`` and ``c`` keys) the reference point
-is set at the mouse pointer location. Clicking on the plot sets the target
-location and finalizes the operation. An outline of the shapes is shown
-while moving the mouse.
-
-.. seealso::
-
-   The FlatCAM Shell command :ref:`offset` will move (offset) all
-   the geometry in a Geometry Object. This can also be done in
-   the **Selected** panel for selected FlatCAM object.
-
-Cancelling an operation
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Hitting the ``Esc`` key cancels whatever tool/operation is active and
-selects the **Selection Tool**.
-
-Deleting selected shapes
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Selections are deleted by hitting the ``-`` sign key.
-
-Other
-~~~~~
-
-.. seealso::
-
-   The FlatCAM Shell command :ref:`scale` changes the size of the
-   geometry in a Geometry Object.
-
-

+ 0 - 54
manual/flatcamshell.rst

@@ -1,54 +0,0 @@
-Shell Command Line Interface
-============================
-
-.. warning::
-    The FlatCAM Shell is under development and its behavior might change in the future. This includes available commands and their syntax.
-
-The FlatCAM Shell provides a command line interface to FlatCAM's functionalies and to the TCL language. It serves the following purposes:
-
-* An alternative the GUI for issuing commands and visualizing data output.
-* Scripting interface for automating large sequences of commands.
-* A mechanism for the user to implement new functionality.
-* A mechanism to provide detailed information to the user.
-* Keep a record of commands and responses.
-
-The Shell Window
-----------------
-
-.. image:: shell.png
-    :align: center
-
-The FlatCAM Shell window is shown at startup when FlatCAM loads. It can be closed and re-opened at any time without loss of data by clicking on the close button on the top right edge of the window and selecting **Tool→Command Line** from the main menu respectively.
-
-It is divided into 2 sections, an output section on the top and an input section on the bottom. A record of previously issued commands is shown in the output section along with their results and other information that FlatCAM might provide. Distinction between types of data in the output section is done by color coding.
-
-To issue a command, type it in the input section and hit ``Enter``. If the command consists of multiple lines, use ``Shift-Enter`` to insert a new line without issuing the command.
-
-Shell Language
---------------
-
-The Shell uses the TCL_ language. TCL provides the simples posible syntax and requires no learning at all for isuing the basic FlatCAM commands. Nonetheless, TCL is a powerfull language that enables the users to create their own complex functionality if desired.
-
-.. _TCL: https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html
-
-Issuing a command is as simple as typing its name and hitting the ``Enter`` key. For example::
-
-    new
-
-This the same as clicking on **File→New** in the main window menu.
-
-If a command requires additional information, it is given immediately after the command name and a space. For example, to open the gerber file ``mygerber.gbr``::
-
-    open_gerber mygerber.gbr
-
-Sometimes a command will have optional parameters, this is, if you provide it it will use it, otherwise it will use some default value. For example::
-
-    isolate mygerber.gbr -tooldia 0.04
-
-This would create isolation routing geometry for the ``mygerber.gbr`` gerber object with a tool diameter of 0.04 inches (or mm, depending on the project settings). Otherwise, if you run::
-
-    isolate mygerber.gbr
-
-The same action will be performed but the tool diameter will be taken from the default value set for that object (If it is a new object, its default values will be set from the project options.)
-
-For complete reference of FlatCAM Shell commands, see :ref:`cmdreference`.

+ 0 - 100
manual/installation.rst

@@ -1,100 +0,0 @@
-Installation
-============
-
-Windows Installer
------------------
-
-Download the installer from the repository_ and run it in your machine.
-It includes everything you need.
-
-.. _repository: https://bitbucket.org/jpcgt/flatcam/downloads
-
-Ubuntu
-------
-
-FlatCAM should work on most Linux distributions but Ubuntu has been
-chosen as the test platform.
-
-There are several dependencies required to run FlatCAM. These are
-listed in the following section. Before attempting a manual installation,
-try running the provided setup script ``setup_ubuntu.sh`` that will
-download and install required packages.
-
-OS-X
-----
-
-See manual instructions below.
-
-
-Manual Installation
--------------------
-
-Requirements
-~~~~~~~~~~~~
-
-* Python 2.7 32-bit
-* PyQt 4
-* Matplotlib 1.3.1
-* Numpy 1.8
-* `Shapely 1.3`_
-  * GEOS
-* RTree
-  * SpatialIndex
-
-.. _Shapely 1.3: https://pypi.python.org/pypi/Shapely
-
-These packages might have their own dependencies.
-
-Linux
-~~~~~
-
-Under Linux, most modern package installers like **yum** or **apt-get**
-will attempt to locate and install the whole tree of dependencies for a
-specified package automatically. Refer to the provided setup script
-``setup_ubuntu.sh`` for the names and installation order.
-
-Once the dependencies are installed, download the latest .zip release
-(or the latest source, although it is not garanteed to work), unpack it,
-change into the created folder and run::
-
-    Python FlatCAM.py
-
-
-Windows
-~~~~~~~
-
-An easy way to get the requirements in your system is to install WinPython_.
-This is a standalone distribution of Python which includes all of FlatCAM's
-dependencies, except for Shapely and RTree. These can be found here:
-`Unofficial Windows Binaries for Python Extension Packages`_.
-
-.. _WinPython: http://winpython.sourceforge.net/
-.. _Unofficial Windows Binaries for Python Extension Packages: http://www.lfd.uci.edu/~gohlke/pythonlibs/
-
-Once the dependencies are installed, download the latest .zip
-release (or the latest source, although it is not garanteed to work),
-unpack it, change into the created folder and run::
-
-    python FlatCAM.py
-
-
-OS-X
-~~~~
-
-Start by installing binary packages: pyqt, geos, spatialindex.
-One way to do this is using Homebrew_::
-
-    brew install name_of_package
-
-.. _Homebrew: http://brew.sh
-
-Now you can install all Python packages (numpy, matplotlib, rtree, scipy,
-shapely, simplejson) using pip::
-
-    pip install name_of_package
-
-Finally, download the latest FlatCAM .zip package or source code. Change into
-its directory and launch it by running::
-
-    python FlatCAM.py
-

+ 0 - 4
manual/introduction.rst

@@ -1,4 +0,0 @@
-Introduction
-============
-
-FlatCAM is a program for preparing CNC jobs for making PCBs on a CNC router. Among other things, it can take a Gerber file generated by your favorite PCB CAD program, and create G-Code for Isolation routing.

+ 0 - 201
manual/procedures.rst

@@ -1,201 +0,0 @@
-Common Procedures
-=================
-
-Isolation Routing
------------------
-
-Isolation routing is the operation of cutting copper around traces for electrical isolation.
-
-1) Open a Gerber file: File→Open Gerber…
-
-The file is automatically analyzed, processed and plotted.
-
-2) Enter the diameter of the tool you will use for isolation routing and hit “Generate Geometry”. The units are determined by the project setting and are shown on the bottom right of the screen. If you want to work using different units, go to Options, Project Options (Combo box), Units. This will change the units for the whole project.
-
-.. image:: open_gerber_ready.png
-    :align: center
-
-This creates a new geometry object listed under “Project” with the same name as the  Gerber object with an “_iso” postfix, and its options are shown in “Selected”. Zoom into the plot (click over the plot and use the ‘2’ and ‘3’ keys to zoom in and out) to inspect the results.
-
-.. image:: iso_routing_ready.png
-    :align: center
-
-3) Create a CNC job from the new geometry by indicating the desired parameters as shown in the figure above and explained below:
-
-* **Cut Z**: The depth of the tool while cutting. -2 mils or -0.05 mm are a typical value for isolation routing.
-* **Travel Z**: The height above the board at which the cutting tool will travel  when not cutting copper.
-* **Feedrate**: The speed of the cutting tool while cutting in inches/minute of mm/minute depending on the project settings.
-* **Tool diam.**: The cutting tool diameter. Use the same value as when creating the isolation routing geometry in step 2.
-
-A CNC Job object has been added to your project and its options are shown in the “Selected” tab. Tool paths are shown on the plot. Blue are copper cuts, while yellow are travelling (no cutting) motions.
-
-.. image:: iso_routing_cnc_ready.png
-    :align: center
-
-Click on the “Export” button under “Export G-Code”. This will open a dialog box for you to save to a file. This is the file that you will supply to your CNC router.
-
-Wide Isolation Routing
-----------------------
-
-1) Go to the “Project” tab and double-click on the isolation geometry object that you created for the 1st pass. In the “Selected” tab, change its name and hit Enter.
-
-2) Go back to “Project” and double-click on the Gerber object. This takes you back to step 1 of tutorial 4.1. We will generate geometry for a second pass in the same way but with a larger offset. If “Tool diam” reads 0.016, enter 0.016*2.5 instead (0.016*3 would put the path 3 times as far which provides no overlap with the previous pass. By setting it to 0.016*2.5 you ensure that there is no copper left on the board and a cleaner finish). Click on “Generate Geometry” just like in tutorial 4.1 and you should get something like the figure below.
-
-.. image:: Double_iso.png
-    :align: center
-
-3) Generate a CNC job like you did in part 3 of tutorial 4.1. You will end up with 2 G-Code file, one for each pass.
-
-CNC Jobs for Drill Files
-------------------------
-
-1) Open a drill (Excellon) file: File→Open Excellon. The drill file will be drawn onto the plot and its options form should show up.
-
-2) A drill file will usually contain different tools (drill diameters). You can choose to create a CNC job for each individual tool or bundle some of the tools together in the same job (in case you did not intend to use drill bits of different diameters). Click on “Choose” under “Create CNC Job” to open a selection window with the list of tools. Each has the format “id”: “diameter”, where the diameter is in the project’s units. Check the boxes by the tools you want to  include in the job. The comma-separated list of tools should appear in the “Tools” entry box (Note: If you know the ids of the tools you can type the list yourself).
-
-.. image:: drill_tool_select.png
-    :align: center
-
-3) Adjust “Drill Z” (Drilling depth), “Travel Z” (Height for X-Y movement) and “Feed rate” (Z-axis speed in project units per minute) to your desired values, and click on “Generate”.
-
-.. image:: drill_cnc_job.png
-    :align: center
-
-A CNC job will be created and the tool-path will be shown on the screen. Click on “Export G-Code” as explained in tutorial 4.1 to save the generated CNC job to G-Code.
-
-2-side PCB
-----------
-
-The main idea behind this process is to ensure that when you turn your board around to mill the bottom side of your PCB it will be perfectly aligned with the top side.
-
-The mechanical alignment is accomplished by using alignment holes/pins. These are holes on your board and on the milling machine bed (typically a board of wood, known as “sacrificial” layer). Pins are used to align the holes on the board to the holes on the sacrificial layer. The holes are always pairs of holes that are symmetrical about a mirror axis. This ensures that when you turn your board around, the board can be aligned using the same alignment holes as before.
-
-.. image:: double1.png
-    :align: center
-
-The bottom layer must be flipped (mirrored) around the same axis that was used for the alignment holes.
-
-The placement of the mirror axis can be very arbitrary. You just need to make sure that after flipping the board around, it will still fit on the milling machine bed. Same for the alignment holes. It doesn’t matter much where you put them. Perhaps if you have a large bare copper board and you plan on reusing the alignment holes for different projects that will be made out of this same bare board, you might want to define the location of the axis and holes, and record them for later use.
-
-FlatCAM takes care of the rest. To ensure the symmetry of the alignment holes, FlatCAM asks you to specify the holes on only one side of the mirror axis and it will create the mirror copies for you. It will also mirror the bottom (Gerber) layer around the same axis.
-
-.. image:: double2.png
-    :align: center
-
-The process of making the double-sided board consists of creating the CNC Job objects for the top layer, the mirrored bottom layer and the alignment holes using FlatCAM. Then you can run the alignment holes job (you must drill through the board and into the sacrificial layer) and the top layer job. You must then turn the board around, align it using the alignment holes (fit some kind of pin in the holes) and run the bottom layer job.
-
-Here is how to use FlatCAM to generate the required files:
-
-1) Open the Gerber files for the top and bottom layers of your PCB.
-
-2) Start the Double-sided PCB tool by selecting Tools→Double-sided PCB tool.
-
-.. image:: dbl_sided1_arrows.png
-    :align: center
-
-The tool has the following options:
-
-* **Bottom Layer**: Indicates which layer is the bottom layer, which needs to be flipped.
-
-* **Mirror axis**: Around which axis, X or Y, we want to mirror the layer.
-
-* **Axis location**: How we want to specify the location of the axis.
-
-* **Point/Box**: Specifies the location of the axis. It depends on the Axis location setting:
-
-    * Point: A single point specifies where the axis is, and you input the coordinates of the point in the format (x, y). For example, if Mirror Axis is set to X and Point is set to (1.0, -1.0) the axis will be horizontal at y=-1.0 (The x coordinate is ignored).
-
-    * Box: If you have already defined the boundary of your PCB you may want the axis to be exactly in the middle of that rectangle. The boundary must be some geometry object defined in the project and can be selected in the combo box appearing in the Point/Box field.
-
-* Alignment holes: These can aid in placing the board at the exact position after turning it over for the bottom side job. These holes must be symmetrical to the mirror axis. You only need to specify the location of the holes on one side of the mirror axis and the program will create the mirror copy. Input the coordinates of the holes in the following format: (x1, y1), (x2, y2), etc.
-
-* Drill diameter: The diameter of the drill for the alignment holes.
-
-.. note::
-    You don’t have to manually type coordinates of points. Clicking on the plot automatically copies the coordinates to the clipboard in (x, y) format and these can be pasted into the input boxes.
-
-3) Create the mirror image of the bottom layer by clicking on “Create Mirror”. This will create a new Gerber object for the project. You can work with this object, create isolation routing and a CNC job as it has been show in the previous tutorials. You may want to hide or remove the layer for the top side to ease the visualization.
-
-4) Create the alignment hole drill object by going back to the Double-sided PCB tool and clicking “Create alignment drill”. This will create a drill job as specified and a CNC job can be created for it as has been shown for the previous examples.
-
-.. image:: bottom.png
-    :align: center
-
-Copper Area Clear
------------------
-
-Removing large areas of copper is necessary when trying to avoid shorts due to dust, rust, etc, or in RF circuits, where the remaining unused copper is just unwanted parasitics. This tutorial shows how to eliminate all copper that is not specified in the Gerber source, but the user can still selectively choose what to clear.
-
-.. image:: coppercleardemo1.png
-    :align: center
-
-1) Open a Gerber file as explained in previous tutorials.
-
-2) In the “Select” tab for the Gerber object, under “Non-copper regions”, provide “Boundary Margin” and click “Generate Geometry”. This creates a bounding box around the Gerber object, with the given margin. Then subtracts the Gerber object from the bounding box, resulting in a Geometry object with polygons covering the areas without copper.
-
-.. image:: coppercleardemo2.png
-    :align: center
-
-3) Now we can choose which polygon we want to “paint”, this is, draw a toolpath inside it to cover all its surface. In the “Selected” tab for the newly created geometry, under “Paint Area”, provide the following:
-
-* **Tool diam.**: The diameter of the tool that will be used to cut the area.
-
-* **Overlap**: Fraction of the tool diameter by which to overlap each passing cut. The default value of 0.15 is the minimum to ensure no copper is left in 90 degree turns of the toolpath.
-
-* **Margin**: Distance for the tool to stay away from the polygon boundary. This can be used to ensure that a large tool does not touch copper edges that have or will be cut by a smaller more precise tool.
-
-4) Click on “Generate” and then click on the plot inside the polygon to be painted. This will create a new Geometry object with the desired toolpaths.
-
-.. image:: coppercleardemo3.png
-    :align: center
-
-Board Cutout
-------------
-
-To cut the PCB to the desired shape and remove it from a larger blank PCB, a toolpath that traces the board edge can be created. Gaps to hold the board until the job is complete can be placed along the edge.
-
-This tutorial describes how to create rectangular cutouts with 2 or 4 gaps.
-
-1) Open a Gerber file and find the **Board Cutout** section in the **Selected tab**.
-
-.. image:: cutout.png
-    :align: center
-
-2) Specify a **Margin**. This will create a rectangular cutout at the given distance from any element in the Gerber. Specify a **Gap Size**. 2 times the diameter of the tool you will use for cutting is a good size. Specify how many and where you want the **Gaps** along the edge, 2 (top and bottom), 2 (left and right) or 4, one on each side. Click on **Generate Geometry**. The figure below shows an example of the results.
-
-.. image:: cutout2.png
-    :align: center
-
-3) Create a CNC job for the newly created geometry as explained in earlier tutorials.
-
-.. image:: cutout3.png
-    :align: center
-
-Bed Flattening
---------------
-
-Most often a sacrificial layer (e.g. wood) is used between the machine bed and the PCB so when drilling and cutting the machine is not hit by the tool. In order to have a completely flat surface for the PCB, the sacrificial layer is flattened by the machine. This tutorial shows how to create a toolpath for this purpose.
-
-1) Open a Gerber file and locate the **Bounding Box** section in the **Selected tab**.
-
-2) Specify a **Margin** (distance of the bounding box from any element in the Gerber) and whether you want **rounded corners**. Click **Generate Bounding Box**.
-
-.. image:: bedflatten0.png
-    :align: center
-
-Make sure your blank PCB will fit in the bounding box.
-
-.. image:: bedflatten.png
-    :align: center
-
-3) In the **Selected** tab for the newly created geometry locate the **Paint Area** section. Specify the **diameter** of the tool you will use, how much (fraction of the tool width) each pass will **overlap** each other, and a **Margin** (although typically not needed and set to 0.0 in this case.)
-
-.. image:: bedflatten1.png
-    :align: center
-
-4) Click on **Generate**, and you will be asked to **click** inside the polygon inside which to create the tool path. Click inside the boundary we just created.
-
-.. image:: bedflatten2.png
-    :align: center
-
-5) Create a CNC job for the newly created geometry as explained in earlier tutorials.

+ 9 - 0
requirements.txt

@@ -0,0 +1,9 @@
+# This file contains python only requirements to be installed with pip
+# Python pacakges that cannot be installed with pip (e.g. PyQT4) are not included.
+# Usage: pip install -r requirements.txt
+numpy>=1.8
+matplotlib>=1.3.1
+shapely>=1.3
+simplejson
+rtree
+scipy

+ 34 - 0
sandbox/diagnose.py

@@ -0,0 +1,34 @@
+#import sys
+import platform
+
+print "Platform", platform.system(), platform.release()
+print "Distro", platform.dist()
+print "Python", platform.python_version()
+
+
+import rtree
+
+print "rtree", rtree.__version__
+
+
+import shapely
+import shapely.geos
+
+print "shapely", shapely.__version__
+print "GEOS library", shapely.geos.geos_version
+
+
+from PyQt4 import Qt
+
+print "Qt", Qt.qVersion()
+
+
+import numpy
+
+print "Numpy", numpy.__version__
+
+
+import matplotlib
+
+print "MatPlotLib", matplotlib.__version__
+print "MPL Numpy", matplotlib.__version__numpy__

+ 30 - 0
sandbox/gerber_find.py

@@ -0,0 +1,30 @@
+from camlib import *
+
+
+def gerber_find(filename, coords, frac_digits=5, tol=0.1):
+    g = Gerber()
+    f = open(filename)
+    current_x = None
+    current_y = None
+    line_num = 0
+    for line in f:
+        line_num += 1
+        try:
+            match = g.lin_re.search(line)
+            if match:
+                # Parse coordinates
+                if match.group(2) is not None:
+                    current_x = parse_gerber_number(match.group(2), frac_digits)
+                if match.group(3) is not None:
+                    current_y = parse_gerber_number(match.group(3), frac_digits)
+
+                if distance(coords, (current_x, current_y)) <= tol:
+                    print line_num, ":", line.strip('\n\r')
+        except Exception as e:
+            print str(e)
+            print line_num, ":", line.strip('\n\r')
+
+
+if __name__ == "__main__":
+    filename = "/home/jpcaram/flatcam_test_files/ExtraTrace_cleanup.gbr"
+    gerber_find(filename, (1.2, 1.1))

+ 20 - 0
sandbox/process_widget.py

@@ -0,0 +1,20 @@
+import sys
+from PyQt4.QtGui import *
+
+app = QApplication(sys.argv)
+
+top = QWidget()
+halign = QHBoxLayout()
+top.setLayout(halign)
+busy_anim = QMovie("../share/busy16.gif")
+busy_anim.start()
+busy_anim_label = QLabel()
+busy_anim_label.setMovie(busy_anim)
+halign.addWidget(busy_anim_label)
+
+message_label = QLabel("Processing...")
+halign.addWidget(message_label)
+
+top.show()
+
+sys.exit(app.exec_())

+ 2 - 2
setup_ubuntu.sh

@@ -2,8 +2,7 @@
 apt-get install libpng-dev
 apt-get install libpng-dev
 apt-get install libfreetype6 libfreetype6-dev
 apt-get install libfreetype6 libfreetype6-dev
 apt-get install python-dev
 apt-get install python-dev
-#apt-get install python-gi
-#apt-get install libgtk-3-devel
+apt-get install python-simplejson
 apt-get install python-qt4
 apt-get install python-qt4
 apt-get install python-numpy python-scipy python-matplotlib
 apt-get install python-numpy python-scipy python-matplotlib
 apt-get install libgeos-dev
 apt-get install libgeos-dev
@@ -14,3 +13,4 @@ pip install --upgrade matplotlib
 pip install --upgrade Shapely
 pip install --upgrade Shapely
 apt-get install libspatialindex-dev
 apt-get install libspatialindex-dev
 pip install rtree
 pip install rtree
+pip install svg.path

BIN
share/active.gif


BIN
share/intersection16.png


BIN
share/intersection24.png


BIN
share/intersection32.png


+ 506 - 0
svgparse.py

@@ -0,0 +1,506 @@
+############################################################
+# FlatCAM: 2D Post-processing for Manufacturing            #
+# http://flatcam.org                                       #
+# Author: Juan Pablo Caram (c)                             #
+# Date: 12/18/2015                                         #
+# MIT Licence                                              #
+#                                                          #
+# SVG Features supported:                                  #
+#  * Groups                                                #
+#  * Rectangles (w/ rounded corners)                       #
+#  * Circles                                               #
+#  * Ellipses                                              #
+#  * Polygons                                              #
+#  * Polylines                                             #
+#  * Lines                                                 #
+#  * Paths                                                 #
+#  * All transformations                                   #
+#                                                          #
+#  Reference: www.w3.org/TR/SVG/Overview.html              #
+############################################################
+
+import xml.etree.ElementTree as ET
+import re
+import itertools
+from svg.path import Path, Line, Arc, CubicBezier, QuadraticBezier, parse_path
+from shapely.geometry import LinearRing, LineString, Point
+from shapely.affinity import translate, rotate, scale, skew, affine_transform
+import numpy as np
+import logging
+
+log = logging.getLogger('base2')
+
+
+def svgparselength(lengthstr):
+    """
+    Parse an SVG length string into a float and a units
+    string, if any.
+
+    :param lengthstr: SVG length string.
+    :return: Number and units pair.
+    :rtype: tuple(float, str|None)
+    """
+
+    integer_re_str = r'[+-]?[0-9]+'
+    number_re_str = r'(?:[+-]?[0-9]*\.[0-9]+(?:[Ee]' + integer_re_str + ')?' + r')|' + \
+                    r'(?:' + integer_re_str + r'(?:[Ee]' + integer_re_str + r')?)'
+    length_re_str = r'(' + number_re_str + r')(em|ex|px|in|cm|mm|pt|pc|%)?'
+
+    match = re.search(length_re_str, lengthstr)
+    if match:
+        return float(match.group(1)), match.group(2)
+
+    raise Exception('Cannot parse SVG length: %s' % lengthstr)
+
+
+def path2shapely(path, res=1.0):
+    """
+    Converts an svg.path.Path into a Shapely
+    LinearRing or LinearString.
+
+    :rtype : LinearRing
+    :rtype : LineString
+    :param path: svg.path.Path instance
+    :param res: Resolution (minimum step along path)
+    :return: Shapely geometry object
+    """
+    points = []
+
+    for component in path:
+
+        # Line
+        if isinstance(component, Line):
+            start = component.start
+            x, y = start.real, start.imag
+            if len(points) == 0 or points[-1] != (x, y):
+                points.append((x, y))
+            end = component.end
+            points.append((end.real, end.imag))
+            continue
+
+        # Arc, CubicBezier or QuadraticBezier
+        if isinstance(component, Arc) or \
+           isinstance(component, CubicBezier) or \
+           isinstance(component, QuadraticBezier):
+
+            # How many points to use in the dicrete representation.
+            length = component.length(res / 10.0)
+            steps = int(length / res + 0.5)
+            frac = 1.0 / steps
+
+            # print length, steps, frac
+            for i in range(steps):
+                point = component.point(i * frac)
+                x, y = point.real, point.imag
+                if len(points) == 0 or points[-1] != (x, y):
+                    points.append((x, y))
+            end = component.point(1.0)
+            points.append((end.real, end.imag))
+            continue
+
+        log.warning("I don't know what this is:", component)
+        continue
+
+    if path.closed:
+        return LinearRing(points)
+    else:
+        return LineString(points)
+
+
+def svgrect2shapely(rect, n_points=32):
+    """
+    Converts an SVG rect into Shapely geometry.
+
+    :param rect: Rect Element
+    :type rect: xml.etree.ElementTree.Element
+    :return: shapely.geometry.polygon.LinearRing
+    """
+    w = svgparselength(rect.get('width'))[0]
+    h = svgparselength(rect.get('height'))[0]
+    x = svgparselength(rect.get('x'))[0]
+    y = svgparselength(rect.get('y'))[0]
+
+    rxstr = rect.get('rx')
+    rystr = rect.get('ry')
+
+    if rxstr is None and rystr is None:  # Sharp corners
+        pts = [
+            (x, y), (x + w, y), (x + w, y + h), (x, y + h), (x, y)
+        ]
+
+    else:  # Rounded corners
+        rx = 0.0 if rxstr is None else svgparselength(rxstr)[0]
+        ry = 0.0 if rystr is None else svgparselength(rystr)[0]
+
+        n_points = int(n_points / 4 + 0.5)
+        t = np.arange(n_points, dtype=float) / n_points / 4
+
+        x_ = (x + w - rx) + rx * np.cos(2 * np.pi * (t + 0.75))
+        y_ = (y + ry) + ry * np.sin(2 * np.pi * (t + 0.75))
+
+        lower_right = [(x_[i], y_[i]) for i in range(n_points)]
+
+        x_ = (x + w - rx) + rx * np.cos(2 * np.pi * t)
+        y_ = (y + h - ry) + ry * np.sin(2 * np.pi * t)
+
+        upper_right = [(x_[i], y_[i]) for i in range(n_points)]
+
+        x_ = (x + rx) + rx * np.cos(2 * np.pi * (t + 0.25))
+        y_ = (y + h - ry) + ry * np.sin(2 * np.pi * (t + 0.25))
+
+        upper_left = [(x_[i], y_[i]) for i in range(n_points)]
+
+        x_ = (x + rx) + rx * np.cos(2 * np.pi * (t + 0.5))
+        y_ = (y + ry) + ry * np.sin(2 * np.pi * (t + 0.5))
+
+        lower_left = [(x_[i], y_[i]) for i in range(n_points)]
+
+        pts = [(x + rx, y), (x - rx + w, y)] + \
+            lower_right + \
+            [(x + w, y + ry), (x + w, y + h - ry)] + \
+            upper_right + \
+            [(x + w - rx, y + h), (x + rx, y + h)] + \
+            upper_left + \
+            [(x, y + h - ry), (x, y + ry)] + \
+            lower_left
+
+    return LinearRing(pts)
+
+
+def svgcircle2shapely(circle):
+    """
+    Converts an SVG circle into Shapely geometry.
+
+    :param circle: Circle Element
+    :type circle: xml.etree.ElementTree.Element
+    :return: Shapely representation of the circle.
+    :rtype: shapely.geometry.polygon.LinearRing
+    """
+    # cx = float(circle.get('cx'))
+    # cy = float(circle.get('cy'))
+    # r = float(circle.get('r'))
+    cx = svgparselength(circle.get('cx'))[0]  # TODO: No units support yet
+    cy = svgparselength(circle.get('cy'))[0]  # TODO: No units support yet
+    r = svgparselength(circle.get('r'))[0]  # TODO: No units support yet
+
+    # TODO: No resolution specified.
+    return Point(cx, cy).buffer(r)
+
+
+def svgellipse2shapely(ellipse, n_points=64):
+    """
+    Converts an SVG ellipse into Shapely geometry
+
+    :param ellipse: Ellipse Element
+    :type ellipse: xml.etree.ElementTree.Element
+    :param n_points: Number of discrete points in output.
+    :return: Shapely representation of the ellipse.
+    :rtype: shapely.geometry.polygon.LinearRing
+    """
+
+    cx = svgparselength(ellipse.get('cx'))[0]  # TODO: No units support yet
+    cy = svgparselength(ellipse.get('cy'))[0]  # TODO: No units support yet
+
+    rx = svgparselength(ellipse.get('rx'))[0]  # TODO: No units support yet
+    ry = svgparselength(ellipse.get('ry'))[0]  # TODO: No units support yet
+
+    t = np.arange(n_points, dtype=float) / n_points
+    x = cx + rx * np.cos(2 * np.pi * t)
+    y = cy + ry * np.sin(2 * np.pi * t)
+    pts = [(x[i], y[i]) for i in range(n_points)]
+
+    return LinearRing(pts)
+
+
+def svgline2shapely(line):
+    """
+
+    :param line: Line element
+    :type line: xml.etree.ElementTree.Element
+    :return: Shapely representation on the line.
+    :rtype: shapely.geometry.polygon.LinearRing
+    """
+
+    x1 = svgparselength(line.get('x1'))[0]
+    y1 = svgparselength(line.get('y1'))[0]
+    x2 = svgparselength(line.get('x2'))[0]
+    y2 = svgparselength(line.get('y2'))[0]
+
+    return LineString([(x1, y1), (x2, y2)])
+
+
+def svgpolyline2shapely(polyline):
+
+    ptliststr = polyline.get('points')
+    points = parse_svg_point_list(ptliststr)
+
+    return LineString(points)
+
+
+def svgpolygon2shapely(polygon):
+
+    ptliststr = polygon.get('points')
+    points = parse_svg_point_list(ptliststr)
+
+    return LinearRing(points)
+
+
+def getsvggeo(node):
+    """
+    Extracts and flattens all geometry from an SVG node
+    into a list of Shapely geometry.
+
+    :param node: xml.etree.ElementTree.Element
+    :return: List of Shapely geometry
+    :rtype: list
+    """
+    kind = re.search('(?:\{.*\})?(.*)$', node.tag).group(1)
+    geo = []
+
+    # Recurse
+    if len(node) > 0:
+        for child in node:
+            subgeo = getsvggeo(child)
+            if subgeo is not None:
+                geo += subgeo
+
+    # Parse
+    elif kind == 'path':
+        log.debug("***PATH***")
+        P = parse_path(node.get('d'))
+        P = path2shapely(P)
+        geo = [P]
+
+    elif kind == 'rect':
+        log.debug("***RECT***")
+        R = svgrect2shapely(node)
+        geo = [R]
+
+    elif kind == 'circle':
+        log.debug("***CIRCLE***")
+        C = svgcircle2shapely(node)
+        geo = [C]
+
+    elif kind == 'ellipse':
+        log.debug("***ELLIPSE***")
+        E = svgellipse2shapely(node)
+        geo = [E]
+
+    elif kind == 'polygon':
+        log.debug("***POLYGON***")
+        poly = svgpolygon2shapely(node)
+        geo = [poly]
+
+    elif kind == 'line':
+        log.debug("***LINE***")
+        line = svgline2shapely(node)
+        geo = [line]
+
+    elif kind == 'polyline':
+        log.debug("***POLYLINE***")
+        pline = svgpolyline2shapely(node)
+        geo = [pline]
+
+    else:
+        log.warning("Unknown kind: " + kind)
+        geo = None
+
+    # Transformations
+    if 'transform' in node.attrib:
+        trstr = node.get('transform')
+        trlist = parse_svg_transform(trstr)
+        #log.debug(trlist)
+
+        # Transformations are applied in reverse order
+        for tr in trlist[::-1]:
+            if tr[0] == 'translate':
+                geo = [translate(geoi, tr[1], tr[2]) for geoi in geo]
+            elif tr[0] == 'scale':
+                geo = [scale(geoi, tr[0], tr[1], origin=(0, 0))
+                       for geoi in geo]
+            elif tr[0] == 'rotate':
+                geo = [rotate(geoi, tr[1], origin=(tr[2], tr[3]))
+                       for geoi in geo]
+            elif tr[0] == 'skew':
+                geo = [skew(geoi, tr[1], tr[2], origin=(0, 0))
+                       for geoi in geo]
+            elif tr[0] == 'matrix':
+                geo = [affine_transform(geoi, tr[1:]) for geoi in geo]
+            else:
+                raise Exception('Unknown transformation: %s', tr)
+
+    return geo
+
+
+def parse_svg_point_list(ptliststr):
+    """
+    Returns a list of coordinate pairs extracted from the "points"
+    attribute in SVG polygons and polylines.
+
+    :param ptliststr: "points" attribute string in polygon or polyline.
+    :return: List of tuples with coordinates.
+    """
+
+    pairs = []
+    last = None
+    pos = 0
+    i = 0
+
+    for match in re.finditer(r'(\s*,\s*)|(\s+)', ptliststr):
+
+        val = float(ptliststr[pos:match.start()])
+
+        if i % 2 == 1:
+            pairs.append((last, val))
+        else:
+            last = val
+
+        pos = match.end()
+        i += 1
+
+    # Check for last element
+    val = float(ptliststr[pos:])
+    if i % 2 == 1:
+        pairs.append((last, val))
+    else:
+        log.warning("Incomplete coordinates.")
+
+    return pairs
+
+
+def parse_svg_transform(trstr):
+    """
+    Parses an SVG transform string into a list
+    of transform names and their parameters.
+
+    Possible transformations are:
+
+    * Translate: translate(<tx> [<ty>]), which specifies
+      a translation by tx and ty. If <ty> is not provided,
+      it is assumed to be zero. Result is
+      ['translate', tx, ty]
+
+    * Scale: scale(<sx> [<sy>]), which specifies a scale operation
+      by sx and sy. If <sy> is not provided, it is assumed to be
+      equal to <sx>. Result is: ['scale', sx, sy]
+
+    * Rotate: rotate(<rotate-angle> [<cx> <cy>]), which specifies
+      a rotation by <rotate-angle> degrees about a given point.
+      If optional parameters <cx> and <cy> are not supplied,
+      the rotate is about the origin of the current user coordinate
+      system. Result is: ['rotate', rotate-angle, cx, cy]
+
+    * Skew: skewX(<skew-angle>), which specifies a skew
+      transformation along the x-axis. skewY(<skew-angle>), which
+      specifies a skew transformation along the y-axis.
+      Result is ['skew', angle-x, angle-y]
+
+    * Matrix: matrix(<a> <b> <c> <d> <e> <f>), which specifies a
+      transformation in the form of a transformation matrix of six
+      values. matrix(a,b,c,d,e,f) is equivalent to applying the
+      transformation matrix [a b c d e f]. Result is
+      ['matrix', a, b, c, d, e, f]
+
+    Note: All parameters to the transformations are "numbers",
+    i.e. no units present.
+
+    :param trstr: SVG transform string.
+    :type trstr: str
+    :return: List of transforms.
+    :rtype: list
+    """
+    trlist = []
+
+    assert isinstance(trstr, str)
+    trstr = trstr.strip(' ')
+
+    integer_re_str = r'[+-]?[0-9]+'
+    number_re_str = r'(?:[+-]?[0-9]*\.[0-9]+(?:[Ee]' + integer_re_str + ')?' + r')|' + \
+                    r'(?:' + integer_re_str + r'(?:[Ee]' + integer_re_str + r')?)'
+
+    # num_re_str = r'[\+\-]?[0-9\.e]+'  # TODO: Negative exponents missing
+    comma_or_space_re_str = r'(?:(?:\s+)|(?:\s*,\s*))'
+    translate_re_str = r'translate\s*\(\s*(' + \
+                       number_re_str + r')(?:' + \
+                       comma_or_space_re_str + \
+                       r'(' + number_re_str + r'))?\s*\)'
+    scale_re_str = r'scale\s*\(\s*(' + \
+                   number_re_str + r')' + \
+                   r'(?:' + comma_or_space_re_str + \
+                   r'(' + number_re_str + r'))?\s*\)'
+    skew_re_str = r'skew([XY])\s*\(\s*(' + \
+                  number_re_str + r')\s*\)'
+    rotate_re_str = r'rotate\s*\(\s*(' + \
+                    number_re_str + r')' + \
+                    r'(?:' + comma_or_space_re_str + \
+                    r'(' + number_re_str + r')' + \
+                    comma_or_space_re_str + \
+                    r'(' + number_re_str + r'))?\*\)'
+    matrix_re_str = r'matrix\s*\(\s*' + \
+                    r'(' + number_re_str + r')' + comma_or_space_re_str + \
+                    r'(' + number_re_str + r')' + comma_or_space_re_str + \
+                    r'(' + number_re_str + r')' + comma_or_space_re_str + \
+                    r'(' + number_re_str + r')' + comma_or_space_re_str + \
+                    r'(' + number_re_str + r')' + comma_or_space_re_str + \
+                    r'(' + number_re_str + r')\s*\)'
+
+    while len(trstr) > 0:
+        match = re.search(r'^' + translate_re_str, trstr)
+        if match:
+            trlist.append([
+                'translate',
+                float(match.group(1)),
+                float(match.group(2)) if match.group else 0.0
+            ])
+            trstr = trstr[len(match.group(0)):].strip(' ')
+            continue
+
+        match = re.search(r'^' + scale_re_str, trstr)
+        if match:
+            trlist.append([
+                'translate',
+                float(match.group(1)),
+                float(match.group(2)) if match.group else float(match.group(1))
+            ])
+            trstr = trstr[len(match.group(0)):].strip(' ')
+            continue
+
+        match = re.search(r'^' + skew_re_str, trstr)
+        if match:
+            trlist.append([
+                'skew',
+                float(match.group(2)) if match.group(1) == 'X' else 0.0,
+                float(match.group(2)) if match.group(1) == 'Y' else 0.0
+            ])
+            trstr = trstr[len(match.group(0)):].strip(' ')
+            continue
+
+        match = re.search(r'^' + rotate_re_str, trstr)
+        if match:
+            trlist.append([
+                'rotate',
+                float(match.group(1)),
+                float(match.group(2)) if match.group(2) else 0.0,
+                float(match.group(3)) if match.group(3) else 0.0
+            ])
+            trstr = trstr[len(match.group(0)):].strip(' ')
+            continue
+
+        match = re.search(r'^' + matrix_re_str, trstr)
+        if match:
+            trlist.append(['matrix'] + [float(x) for x in match.groups()])
+            trstr = trstr[len(match.group(0)):].strip(' ')
+            continue
+
+        raise Exception("Don't know how to parse: %s" % trstr)
+
+    return trlist
+
+
+if __name__ == "__main__":
+    tree = ET.parse('tests/svg/drawing.svg')
+    root = tree.getroot()
+    ns = re.search(r'\{(.*)\}', root.tag).group(1)
+    print ns
+    for geo in getsvggeo(root):
+        print geo

+ 0 - 0
tests/__init__.py


+ 95 - 0
tests/canvas/performance.py

@@ -0,0 +1,95 @@
+from __future__ import division
+import matplotlib
+matplotlib.use('Agg')
+import matplotlib.pyplot as plt
+import numpy as np
+import cStringIO
+from matplotlib.backends.backend_agg import FigureCanvasAgg
+from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
+from matplotlib.figure import Figure
+import cProfile
+import sys
+
+
+def gen_data():
+    N = 100000
+    x = np.random.rand(N) * 10
+    y = np.random.rand(N) * 10
+    colors = np.random.rand(N)
+    area = np.pi * (15 * np.random.rand(N))**2  # 0 to 15 point radiuses
+    data = x, y, area, colors
+    return data
+
+
+# @profile
+def large_plot(data):
+    x, y, area, colors = data
+
+    fig = Figure(figsize=(10, 10), dpi=80)
+    axes = fig.add_axes([0.0, 0.0, 1.0, 1.0], alpha=1.0)
+    axes.set_frame_on(False)
+    axes.set_xticks([])
+    axes.set_yticks([])
+    # axes.set_xlim(0, 10)
+    # axes.set_ylim(0, 10)
+
+    axes.scatter(x, y, s=area, c=colors, alpha=0.5)
+
+    axes.set_xlim(0, 10)
+    axes.set_ylim(0, 10)
+
+    canvas = FigureCanvasAgg(fig)
+    canvas.draw()
+    # canvas = FigureCanvasQTAgg(fig)
+    # buf = canvas.tostring_rgb()
+    buf = fig.canvas.tostring_rgb()
+
+    ncols, nrows = fig.canvas.get_width_height()
+    img = np.fromstring(buf, dtype=np.uint8).reshape(nrows, ncols, 3)
+
+    return img
+
+
+def small_plot(data):
+    x, y, area, colors = data
+
+    fig = Figure(figsize=(3, 3), dpi=80)
+    axes = fig.add_axes([0.0, 0.0, 1.0, 1.0], alpha=1.0)
+    axes.set_frame_on(False)
+    axes.set_xticks([])
+    axes.set_yticks([])
+    # axes.set_xlim(5, 6)
+    # axes.set_ylim(5, 6)
+
+    axes.scatter(x, y, s=area, c=colors, alpha=0.5)
+
+    axes.set_xlim(4, 7)
+    axes.set_ylim(4, 7)
+
+    canvas = FigureCanvasAgg(fig)
+    canvas.draw()
+    # canvas = FigureCanvasQTAgg(fig)
+    # buf = canvas.tostring_rgb()
+    buf = fig.canvas.tostring_rgb()
+
+    ncols, nrows = fig.canvas.get_width_height()
+    img = np.fromstring(buf, dtype=np.uint8).reshape(nrows, ncols, 3)
+
+    return img
+
+def doit():
+    d = gen_data()
+    img = large_plot(d)
+    return img
+
+
+if __name__ == "__main__":
+
+    d = gen_data()
+
+    if sys.argv[1] == 'large':
+        cProfile.runctx('large_plot(d)', None, locals())
+    else:
+        cProfile.runctx('small_plot(d)', None, locals())
+
+

+ 6 - 0
tests/canvas/prof.sh

@@ -0,0 +1,6 @@
+#!/bin/sh
+
+echo "*** LARGE ***"
+python performance.py large | egrep "(\(scatter\))|(\(draw\))|(tostring_rgb)|(fromstring)"
+echo "*** SMALL ***"
+python performance.py small | egrep "(\(scatter\))|(\(draw\))|(tostring_rgb)|(fromstring)"

+ 6358 - 0
tests/gerber_files/STM32F4-spindle.cmp

@@ -0,0 +1,6358 @@
+G75*
+%MOIN*%
+%OFA0B0*%
+%FSLAX25Y25*%
+%IPPOS*%
+%LPD*%
+%AMOC8*
+5,1,8,0,0,1.08239X$1,22.5*
+%
+%ADD10R,0.30000X0.30000*%
+%ADD11R,0.44685X0.17500*%
+%ADD12R,0.04724X0.01181*%
+%ADD13R,0.01181X0.04724*%
+%ADD14R,0.05512X0.04724*%
+%ADD15R,0.05118X0.06299*%
+%ADD16R,0.06299X0.05118*%
+%ADD17R,0.09843X0.01969*%
+%ADD18R,0.09843X0.07874*%
+%ADD19R,0.05118X0.05906*%
+%ADD20R,0.03150X0.05512*%
+%ADD21R,0.03150X0.07480*%
+%ADD22C,0.06000*%
+%ADD23R,0.05906X0.07874*%
+%ADD24OC8,0.06400*%
+%ADD25R,0.05512X0.05512*%
+%ADD26R,0.12598X0.04331*%
+%ADD27C,0.07400*%
+%ADD28R,0.06299X0.07098*%
+%ADD29R,0.03937X0.04331*%
+%ADD30R,0.05906X0.05118*%
+%ADD31C,0.01200*%
+%ADD32C,0.02400*%
+%ADD33C,0.01600*%
+%ADD34C,0.03181*%
+%ADD35C,0.04000*%
+D10*
+X0102500Y0095000D03*
+D11*
+X0212657Y0076250D03*
+D12*
+X0124941Y0080236D03*
+X0124941Y0082205D03*
+X0124941Y0084173D03*
+X0124941Y0086142D03*
+X0124941Y0088110D03*
+X0124941Y0090079D03*
+X0124941Y0092047D03*
+X0124941Y0094016D03*
+X0124941Y0095984D03*
+X0124941Y0097953D03*
+X0124941Y0099921D03*
+X0124941Y0101890D03*
+X0124941Y0103858D03*
+X0124941Y0105827D03*
+X0124941Y0107795D03*
+X0124941Y0109764D03*
+X0080059Y0109764D03*
+X0080059Y0107795D03*
+X0080059Y0105827D03*
+X0080059Y0103858D03*
+X0080059Y0101890D03*
+X0080059Y0099921D03*
+X0080059Y0097953D03*
+X0080059Y0095984D03*
+X0080059Y0094016D03*
+X0080059Y0092047D03*
+X0080059Y0090079D03*
+X0080059Y0088110D03*
+X0080059Y0086142D03*
+X0080059Y0084173D03*
+X0080059Y0082205D03*
+X0080059Y0080236D03*
+D13*
+X0087736Y0072559D03*
+X0089705Y0072559D03*
+X0091673Y0072559D03*
+X0093642Y0072559D03*
+X0095610Y0072559D03*
+X0097579Y0072559D03*
+X0099547Y0072559D03*
+X0101516Y0072559D03*
+X0103484Y0072559D03*
+X0105453Y0072559D03*
+X0107421Y0072559D03*
+X0109390Y0072559D03*
+X0111358Y0072559D03*
+X0113327Y0072559D03*
+X0115295Y0072559D03*
+X0117264Y0072559D03*
+X0117264Y0117441D03*
+X0115295Y0117441D03*
+X0113327Y0117441D03*
+X0111358Y0117441D03*
+X0109390Y0117441D03*
+X0107421Y0117441D03*
+X0105453Y0117441D03*
+X0103484Y0117441D03*
+X0101516Y0117441D03*
+X0099547Y0117441D03*
+X0097579Y0117441D03*
+X0095610Y0117441D03*
+X0093642Y0117441D03*
+X0091673Y0117441D03*
+X0089705Y0117441D03*
+X0087736Y0117441D03*
+D14*
+X0059331Y0113346D03*
+X0059331Y0106654D03*
+X0050669Y0106654D03*
+X0050669Y0113346D03*
+D15*
+X0051063Y0125000D03*
+X0058937Y0125000D03*
+X0056063Y0072500D03*
+X0063937Y0072500D03*
+X0078563Y0060000D03*
+X0086437Y0060000D03*
+X0121063Y0060000D03*
+X0128937Y0060000D03*
+X0136063Y0067500D03*
+X0143937Y0067500D03*
+X0141437Y0115000D03*
+X0133563Y0115000D03*
+X0133563Y0130000D03*
+X0141437Y0130000D03*
+D16*
+X0082500Y0136063D03*
+X0082500Y0143937D03*
+X0072500Y0123937D03*
+X0072500Y0116063D03*
+X0040000Y0113937D03*
+X0040000Y0106063D03*
+X0027500Y0141063D03*
+X0027500Y0148937D03*
+X0275000Y0208563D03*
+X0275000Y0216437D03*
+D17*
+X0255157Y0111299D03*
+X0255157Y0108150D03*
+X0255157Y0105000D03*
+X0255157Y0101850D03*
+X0255157Y0098701D03*
+D18*
+X0255157Y0087283D03*
+X0276811Y0087283D03*
+X0276811Y0122717D03*
+X0255157Y0122717D03*
+D19*
+X0223740Y0107500D03*
+X0216260Y0107500D03*
+X0216260Y0097500D03*
+X0223740Y0097500D03*
+X0267510Y0072500D03*
+X0274990Y0072500D03*
+X0161240Y0122500D03*
+X0153760Y0122500D03*
+X0082490Y0176250D03*
+X0075010Y0176250D03*
+X0103760Y0217500D03*
+X0111240Y0217500D03*
+X0128760Y0217500D03*
+X0136240Y0217500D03*
+X0153760Y0217500D03*
+X0161240Y0217500D03*
+X0126240Y0047500D03*
+X0118760Y0047500D03*
+D20*
+X0216598Y0062201D03*
+X0228402Y0062201D03*
+D21*
+X0222500Y0063201D03*
+D22*
+X0223500Y0069516D02*
+X0221500Y0069516D01*
+X0221500Y0077886D01*
+X0223500Y0077886D01*
+X0223500Y0069516D01*
+X0223500Y0075515D02*
+X0221500Y0075515D01*
+D23*
+X0244094Y0072500D03*
+X0255906Y0072500D03*
+X0255906Y0060000D03*
+X0244094Y0060000D03*
+D24*
+X0225000Y0147500D03*
+X0215000Y0147500D03*
+X0215000Y0157500D03*
+X0225000Y0157500D03*
+X0225000Y0167500D03*
+X0215000Y0167500D03*
+X0215000Y0177500D03*
+X0225000Y0177500D03*
+X0195000Y0207500D03*
+X0185000Y0207500D03*
+X0185000Y0217500D03*
+X0195000Y0217500D03*
+X0062500Y0217500D03*
+X0052500Y0217500D03*
+X0052500Y0207500D03*
+X0062500Y0207500D03*
+X0042500Y0207500D03*
+X0032500Y0207500D03*
+X0022500Y0207500D03*
+X0022500Y0217500D03*
+X0032500Y0217500D03*
+X0042500Y0217500D03*
+X0082500Y0025000D03*
+X0092500Y0025000D03*
+X0092500Y0015000D03*
+X0082500Y0015000D03*
+X0102500Y0015000D03*
+X0112500Y0015000D03*
+X0112500Y0025000D03*
+X0102500Y0025000D03*
+X0122500Y0025000D03*
+X0122500Y0015000D03*
+D25*
+X0240000Y0081270D03*
+X0240000Y0096230D03*
+X0246250Y0140020D03*
+X0257500Y0140020D03*
+X0257500Y0154980D03*
+X0246250Y0154980D03*
+D26*
+X0234114Y0202126D03*
+X0234114Y0217874D03*
+X0260886Y0217874D03*
+X0260886Y0202126D03*
+D27*
+X0273800Y0162500D02*
+X0281200Y0162500D01*
+X0281200Y0152500D02*
+X0273800Y0152500D01*
+X0273800Y0047500D02*
+X0281200Y0047500D01*
+X0271200Y0047500D02*
+X0263800Y0047500D01*
+X0263800Y0037500D02*
+X0271200Y0037500D01*
+X0273800Y0037500D02*
+X0281200Y0037500D01*
+X0281200Y0027500D02*
+X0273800Y0027500D01*
+X0271200Y0027500D02*
+X0263800Y0027500D01*
+X0263800Y0017500D02*
+X0271200Y0017500D01*
+X0273800Y0017500D02*
+X0281200Y0017500D01*
+X0096250Y0177550D02*
+X0096250Y0184950D01*
+D28*
+X0101902Y0205000D03*
+X0113098Y0205000D03*
+X0126902Y0205000D03*
+X0138098Y0205000D03*
+X0151902Y0205000D03*
+X0163098Y0205000D03*
+X0265652Y0060000D03*
+X0276848Y0060000D03*
+D29*
+X0063750Y0058346D03*
+X0063750Y0051654D03*
+D30*
+X0027500Y0071260D03*
+X0027500Y0078740D03*
+D31*
+X0002568Y0002568D02*
+X0002568Y0227392D01*
+X0014348Y0227392D01*
+X0010705Y0223749D01*
+X0009814Y0222858D01*
+X0009331Y0221693D01*
+X0009331Y0091870D01*
+X0009814Y0090705D01*
+X0014814Y0085705D01*
+X0015705Y0084814D01*
+X0016870Y0084331D01*
+X0024331Y0084331D01*
+X0024331Y0083868D01*
+X0024036Y0083868D01*
+X0023092Y0083477D01*
+X0022370Y0082754D01*
+X0021979Y0081810D01*
+X0021979Y0075670D01*
+X0022256Y0075000D01*
+X0021979Y0074330D01*
+X0021979Y0068190D01*
+X0022370Y0067246D01*
+X0023092Y0066523D01*
+X0024036Y0066132D01*
+X0024331Y0066132D01*
+X0024331Y0061653D01*
+X0024814Y0060489D01*
+X0028751Y0056552D01*
+X0029642Y0055660D01*
+X0030807Y0055178D01*
+X0059417Y0055178D01*
+X0059491Y0055000D01*
+X0059213Y0054330D01*
+X0059213Y0048977D01*
+X0059604Y0048033D01*
+X0060327Y0047311D01*
+X0061271Y0046920D01*
+X0066229Y0046920D01*
+X0067173Y0047311D01*
+X0067896Y0048033D01*
+X0068083Y0048485D01*
+X0083130Y0048485D01*
+X0084295Y0048967D01*
+X0088232Y0052904D01*
+X0089123Y0053796D01*
+X0089324Y0054282D01*
+X0089507Y0054282D01*
+X0090451Y0054673D01*
+X0091174Y0055395D01*
+X0091565Y0056339D01*
+X0091565Y0059347D01*
+X0092442Y0060225D01*
+X0092442Y0052749D01*
+X0090045Y0050353D01*
+X0089642Y0050186D01*
+X0088751Y0049295D01*
+X0084814Y0045358D01*
+X0084331Y0044193D01*
+X0084331Y0030100D01*
+X0082900Y0030100D01*
+X0082900Y0025400D01*
+X0082100Y0025400D01*
+X0082100Y0030100D01*
+X0080388Y0030100D01*
+X0077400Y0027112D01*
+X0077400Y0025400D01*
+X0082100Y0025400D01*
+X0082100Y0024600D01*
+X0077400Y0024600D01*
+X0077400Y0022888D01*
+X0079815Y0020473D01*
+X0076731Y0017389D01*
+X0076731Y0012611D01*
+X0080111Y0009231D01*
+X0084889Y0009231D01*
+X0087500Y0011842D01*
+X0090111Y0009231D01*
+X0094889Y0009231D01*
+X0097500Y0011842D01*
+X0100111Y0009231D01*
+X0104889Y0009231D01*
+X0107500Y0011842D01*
+X0110111Y0009231D01*
+X0114889Y0009231D01*
+X0117500Y0011842D01*
+X0120111Y0009231D01*
+X0124889Y0009231D01*
+X0128268Y0012611D01*
+X0128268Y0016288D01*
+X0131795Y0019814D01*
+X0132686Y0020705D01*
+X0133168Y0021870D01*
+X0133168Y0034193D01*
+X0132686Y0035358D01*
+X0128749Y0039295D01*
+X0127858Y0040186D01*
+X0126693Y0040668D01*
+X0112671Y0040668D01*
+X0110590Y0042749D01*
+X0110590Y0045756D01*
+X0111532Y0044814D01*
+X0112697Y0044331D01*
+X0113632Y0044331D01*
+X0113632Y0044036D01*
+X0114023Y0043092D01*
+X0114746Y0042370D01*
+X0115690Y0041979D01*
+X0121830Y0041979D01*
+X0122774Y0042370D01*
+X0123132Y0042727D01*
+X0123431Y0042647D01*
+X0125640Y0042647D01*
+X0125640Y0046900D01*
+X0126840Y0046900D01*
+X0126840Y0042647D01*
+X0129049Y0042647D01*
+X0129533Y0042777D01*
+X0129966Y0043027D01*
+X0130320Y0043381D01*
+X0130570Y0043814D01*
+X0130699Y0044297D01*
+X0130699Y0046900D01*
+X0126840Y0046900D01*
+X0126840Y0048100D01*
+X0125640Y0048100D01*
+X0125640Y0052353D01*
+X0123431Y0052353D01*
+X0123132Y0052273D01*
+X0122774Y0052630D01*
+X0121830Y0053021D01*
+X0115690Y0053021D01*
+X0114746Y0052630D01*
+X0114023Y0051908D01*
+X0113841Y0051467D01*
+X0112558Y0052749D01*
+X0112558Y0058328D01*
+X0112609Y0058205D01*
+X0113500Y0057314D01*
+X0114665Y0056831D01*
+X0115935Y0056831D01*
+X0115935Y0056339D01*
+X0116326Y0055395D01*
+X0117049Y0054673D01*
+X0117993Y0054282D01*
+X0124133Y0054282D01*
+X0125077Y0054673D01*
+X0125543Y0055139D01*
+X0125645Y0055080D01*
+X0126128Y0054950D01*
+X0128337Y0054950D01*
+X0128337Y0059400D01*
+X0129537Y0059400D01*
+X0129537Y0060600D01*
+X0133396Y0060600D01*
+X0133396Y0061782D01*
+X0139133Y0061782D01*
+X0140077Y0062173D01*
+X0140543Y0062639D01*
+X0140645Y0062580D01*
+X0141128Y0062450D01*
+X0143337Y0062450D01*
+X0143337Y0066900D01*
+X0144537Y0066900D01*
+X0144537Y0068100D01*
+X0148396Y0068100D01*
+X0148396Y0070900D01*
+X0148307Y0071231D01*
+X0163731Y0071231D01*
+X0163731Y0049250D01*
+X0164305Y0047865D01*
+X0165365Y0046805D01*
+X0166750Y0046231D01*
+X0257540Y0046231D01*
+X0258486Y0043949D01*
+X0259935Y0042500D01*
+X0258486Y0041051D01*
+X0257531Y0038747D01*
+X0257531Y0036253D01*
+X0258486Y0033949D01*
+X0260249Y0032186D01*
+X0260550Y0032061D01*
+X0260152Y0031771D01*
+X0259529Y0031148D01*
+X0259010Y0030435D01*
+X0258610Y0029650D01*
+X0258338Y0028811D01*
+X0258209Y0028000D01*
+X0267000Y0028000D01*
+X0267000Y0027000D01*
+X0268000Y0027000D01*
+X0268000Y0028000D01*
+X0277000Y0028000D01*
+X0277000Y0027000D01*
+X0276791Y0027000D01*
+X0268000Y0027000D01*
+X0268000Y0021900D01*
+X0268000Y0018000D01*
+X0277000Y0018000D01*
+X0277000Y0027000D01*
+X0278000Y0027000D01*
+X0278000Y0021900D01*
+X0278000Y0018000D01*
+X0277000Y0018000D01*
+X0277000Y0017000D01*
+X0276791Y0017000D01*
+X0268000Y0017000D01*
+X0268000Y0018000D01*
+X0267000Y0018000D01*
+X0267000Y0027000D01*
+X0258209Y0027000D01*
+X0258338Y0026189D01*
+X0258610Y0025350D01*
+X0259010Y0024565D01*
+X0259529Y0023852D01*
+X0260152Y0023229D01*
+X0260865Y0022710D01*
+X0261278Y0022500D01*
+X0260865Y0022290D01*
+X0260152Y0021771D01*
+X0259529Y0021148D01*
+X0259010Y0020435D01*
+X0258610Y0019650D01*
+X0258338Y0018811D01*
+X0258209Y0018000D01*
+X0267000Y0018000D01*
+X0267000Y0017000D01*
+X0268000Y0017000D01*
+X0268000Y0011900D01*
+X0271641Y0011900D01*
+X0272500Y0012036D01*
+X0273359Y0011900D01*
+X0277000Y0011900D01*
+X0277000Y0017000D01*
+X0278000Y0017000D01*
+X0278000Y0011900D01*
+X0281641Y0011900D01*
+X0282511Y0012038D01*
+X0283350Y0012310D01*
+X0284135Y0012710D01*
+X0284848Y0013229D01*
+X0285471Y0013852D01*
+X0285990Y0014565D01*
+X0286132Y0014845D01*
+X0286132Y0002568D01*
+X0002568Y0002568D01*
+X0002568Y0003596D02*
+X0286132Y0003596D01*
+X0286132Y0004794D02*
+X0002568Y0004794D01*
+X0002568Y0005993D02*
+X0286132Y0005993D01*
+X0286132Y0007191D02*
+X0002568Y0007191D01*
+X0002568Y0008390D02*
+X0286132Y0008390D01*
+X0286132Y0009588D02*
+X0125246Y0009588D01*
+X0126444Y0010787D02*
+X0286132Y0010787D01*
+X0286132Y0011985D02*
+X0282178Y0011985D01*
+X0284786Y0013184D02*
+X0286132Y0013184D01*
+X0286132Y0014382D02*
+X0285857Y0014382D01*
+X0278000Y0014382D02*
+X0277000Y0014382D01*
+X0277000Y0013184D02*
+X0278000Y0013184D01*
+X0278000Y0011985D02*
+X0277000Y0011985D01*
+X0272822Y0011985D02*
+X0272178Y0011985D01*
+X0268000Y0011985D02*
+X0267000Y0011985D01*
+X0267000Y0011900D02*
+X0267000Y0017000D01*
+X0258209Y0017000D01*
+X0258338Y0016189D01*
+X0258610Y0015350D01*
+X0259010Y0014565D01*
+X0259529Y0013852D01*
+X0260152Y0013229D01*
+X0260865Y0012710D01*
+X0261650Y0012310D01*
+X0262489Y0012038D01*
+X0263359Y0011900D01*
+X0267000Y0011900D01*
+X0267000Y0013184D02*
+X0268000Y0013184D01*
+X0268000Y0014382D02*
+X0267000Y0014382D01*
+X0267000Y0015581D02*
+X0268000Y0015581D01*
+X0268000Y0016779D02*
+X0267000Y0016779D01*
+X0267000Y0017978D02*
+X0129959Y0017978D01*
+X0131157Y0019176D02*
+X0258456Y0019176D01*
+X0258980Y0020375D02*
+X0132356Y0020375D01*
+X0133046Y0021573D02*
+X0259954Y0021573D01*
+X0260781Y0022772D02*
+X0133168Y0022772D01*
+X0133168Y0023970D02*
+X0259443Y0023970D01*
+X0258703Y0025169D02*
+X0133168Y0025169D01*
+X0133168Y0026367D02*
+X0258310Y0026367D01*
+X0258330Y0028764D02*
+X0133168Y0028764D01*
+X0133168Y0027566D02*
+X0267000Y0027566D01*
+X0268000Y0027566D02*
+X0277000Y0027566D01*
+X0277000Y0026367D02*
+X0278000Y0026367D01*
+X0278000Y0025169D02*
+X0277000Y0025169D01*
+X0277000Y0023970D02*
+X0278000Y0023970D01*
+X0278000Y0022772D02*
+X0277000Y0022772D01*
+X0277000Y0021573D02*
+X0278000Y0021573D01*
+X0278000Y0020375D02*
+X0277000Y0020375D01*
+X0277000Y0019176D02*
+X0278000Y0019176D01*
+X0277000Y0017978D02*
+X0268000Y0017978D01*
+X0268000Y0019176D02*
+X0267000Y0019176D01*
+X0267000Y0020375D02*
+X0268000Y0020375D01*
+X0268000Y0021573D02*
+X0267000Y0021573D01*
+X0267000Y0022772D02*
+X0268000Y0022772D01*
+X0268000Y0023970D02*
+X0267000Y0023970D01*
+X0267000Y0025169D02*
+X0268000Y0025169D01*
+X0268000Y0026367D02*
+X0267000Y0026367D01*
+X0259542Y0031161D02*
+X0258224Y0031161D01*
+X0258093Y0030847D02*
+X0258356Y0031482D01*
+X0258491Y0032156D01*
+X0258491Y0032500D01*
+X0258491Y0032844D01*
+X0258356Y0033518D01*
+X0258093Y0034153D01*
+X0257711Y0034725D01*
+X0257225Y0035211D01*
+X0256653Y0035593D01*
+X0256018Y0035856D01*
+X0255344Y0035991D01*
+X0255000Y0035991D01*
+X0254656Y0035991D01*
+X0253982Y0035856D01*
+X0253347Y0035593D01*
+X0252775Y0035211D01*
+X0252289Y0034725D01*
+X0251907Y0034153D01*
+X0251644Y0033518D01*
+X0251509Y0032844D01*
+X0251509Y0032500D01*
+X0251509Y0032156D01*
+X0251644Y0031482D01*
+X0251907Y0030847D01*
+X0252289Y0030275D01*
+X0252775Y0029789D01*
+X0253347Y0029407D01*
+X0253982Y0029144D01*
+X0254656Y0029009D01*
+X0255000Y0029009D01*
+X0255344Y0029009D01*
+X0256018Y0029144D01*
+X0256653Y0029407D01*
+X0257225Y0029789D01*
+X0257711Y0030275D01*
+X0258093Y0030847D01*
+X0258770Y0029963D02*
+X0257399Y0029963D01*
+X0255000Y0029963D02*
+X0255000Y0029963D01*
+X0255000Y0029009D02*
+X0255000Y0032500D01*
+X0258491Y0032500D01*
+X0255000Y0032500D01*
+X0255000Y0032500D01*
+X0255000Y0032500D01*
+X0255000Y0035991D01*
+X0255000Y0032500D01*
+X0255000Y0032500D01*
+X0255000Y0032500D01*
+X0251509Y0032500D01*
+X0255000Y0032500D01*
+X0255000Y0029009D01*
+X0255000Y0031161D02*
+X0255000Y0031161D01*
+X0255000Y0032360D02*
+X0255000Y0032360D01*
+X0255000Y0033558D02*
+X0255000Y0033558D01*
+X0255000Y0034757D02*
+X0255000Y0034757D01*
+X0255000Y0035955D02*
+X0255000Y0035955D01*
+X0255521Y0035955D02*
+X0257655Y0035955D01*
+X0257680Y0034757D02*
+X0258151Y0034757D01*
+X0258340Y0033558D02*
+X0258877Y0033558D01*
+X0258491Y0032360D02*
+X0260075Y0032360D01*
+X0257531Y0037154D02*
+X0130890Y0037154D01*
+X0129692Y0038352D02*
+X0257531Y0038352D01*
+X0257865Y0039551D02*
+X0128493Y0039551D01*
+X0126063Y0037500D02*
+X0111358Y0037500D01*
+X0107421Y0041437D01*
+X0107421Y0072559D01*
+X0105453Y0072559D02*
+X0105453Y0038937D01*
+X0109390Y0035000D01*
+X0113563Y0035000D01*
+X0117500Y0031063D01*
+X0117500Y0020000D01*
+X0112500Y0015000D01*
+X0116444Y0010787D02*
+X0118555Y0010787D01*
+X0119754Y0009588D02*
+X0115246Y0009588D01*
+X0109754Y0009588D02*
+X0105246Y0009588D01*
+X0106444Y0010787D02*
+X0108555Y0010787D01*
+X0102500Y0015000D02*
+X0107500Y0020000D01*
+X0107500Y0028563D01*
+X0103563Y0032500D01*
+X0103484Y0032500D01*
+X0099547Y0036437D01*
+X0099547Y0072559D01*
+X0097579Y0072559D02*
+X0097579Y0020079D01*
+X0092500Y0015000D01*
+X0096444Y0010787D02*
+X0098555Y0010787D01*
+X0099754Y0009588D02*
+X0095246Y0009588D01*
+X0089754Y0009588D02*
+X0085246Y0009588D01*
+X0086444Y0010787D02*
+X0088555Y0010787D01*
+X0082500Y0015000D02*
+X0087500Y0020000D01*
+X0087500Y0043563D01*
+X0091437Y0047500D01*
+X0091673Y0047500D01*
+X0095610Y0051437D01*
+X0095610Y0072559D01*
+X0091673Y0072559D02*
+X0091673Y0086142D01*
+X0100000Y0086142D01*
+X0100000Y0109764D01*
+X0080059Y0109764D01*
+X0124941Y0109764D01*
+X0128750Y0109764D01*
+X0128750Y0122500D01*
+X0153760Y0122500D01*
+X0155000Y0122500D01*
+X0155000Y0130000D01*
+X0190000Y0130000D01*
+X0186708Y0133168D02*
+X0187412Y0133873D01*
+X0189091Y0134568D01*
+X0190909Y0134568D01*
+X0192588Y0133873D01*
+X0193873Y0132588D01*
+X0194331Y0131481D01*
+X0194331Y0144208D01*
+X0193627Y0144912D01*
+X0193168Y0146019D01*
+X0193168Y0140807D01*
+X0192686Y0139642D01*
+X0189999Y0136955D01*
+X0189999Y0136955D01*
+X0189108Y0136064D01*
+X0187943Y0135581D01*
+X0136963Y0135581D01*
+X0137577Y0135327D01*
+X0138043Y0134861D01*
+X0138145Y0134920D01*
+X0138628Y0135050D01*
+X0140837Y0135050D01*
+X0140837Y0130600D01*
+X0142037Y0130600D01*
+X0142037Y0135050D01*
+X0144246Y0135050D01*
+X0144729Y0134920D01*
+X0145163Y0134670D01*
+X0145516Y0134316D01*
+X0145767Y0133883D01*
+X0145896Y0133400D01*
+X0145896Y0130600D01*
+X0142037Y0130600D01*
+X0142037Y0129400D01*
+X0145896Y0129400D01*
+X0145896Y0126600D01*
+X0145767Y0126117D01*
+X0145516Y0125684D01*
+X0145501Y0125668D01*
+X0148632Y0125668D01*
+X0148632Y0125964D01*
+X0149023Y0126908D01*
+X0149746Y0127630D01*
+X0150058Y0127759D01*
+X0150000Y0127759D01*
+X0150000Y0131250D01*
+X0150000Y0131250D01*
+X0150000Y0134741D01*
+X0149656Y0134741D01*
+X0148982Y0134606D01*
+X0148347Y0134343D01*
+X0147775Y0133961D01*
+X0147289Y0133475D01*
+X0146907Y0132903D01*
+X0146644Y0132268D01*
+X0146509Y0131594D01*
+X0146509Y0131250D01*
+X0146509Y0130906D01*
+X0146644Y0130232D01*
+X0146907Y0129597D01*
+X0147289Y0129025D01*
+X0147775Y0128539D01*
+X0148347Y0128157D01*
+X0148982Y0127894D01*
+X0149656Y0127759D01*
+X0150000Y0127759D01*
+X0150000Y0131250D01*
+X0150000Y0131250D01*
+X0150000Y0131250D01*
+X0146509Y0131250D01*
+X0150000Y0131250D01*
+X0150000Y0134741D01*
+X0150344Y0134741D01*
+X0151018Y0134606D01*
+X0151653Y0134343D01*
+X0152225Y0133961D01*
+X0152711Y0133475D01*
+X0153093Y0132903D01*
+X0153190Y0132671D01*
+X0153205Y0132686D01*
+X0154370Y0133168D01*
+X0186708Y0133168D01*
+X0188282Y0134233D02*
+X0151818Y0134233D01*
+X0153005Y0133035D02*
+X0154047Y0133035D01*
+X0150000Y0133035D02*
+X0150000Y0133035D01*
+X0150000Y0134233D02*
+X0150000Y0134233D01*
+X0148182Y0134233D02*
+X0145564Y0134233D01*
+X0145896Y0133035D02*
+X0146995Y0133035D01*
+X0146558Y0131836D02*
+X0145896Y0131836D01*
+X0145896Y0130638D02*
+X0146563Y0130638D01*
+X0147012Y0129439D02*
+X0142037Y0129439D01*
+X0142037Y0130638D02*
+X0140837Y0130638D01*
+X0140837Y0131836D02*
+X0142037Y0131836D01*
+X0142037Y0133035D02*
+X0140837Y0133035D01*
+X0140837Y0134233D02*
+X0142037Y0134233D01*
+X0137324Y0135432D02*
+X0194331Y0135432D01*
+X0194331Y0136630D02*
+X0189674Y0136630D01*
+X0190873Y0137829D02*
+X0194331Y0137829D01*
+X0194331Y0139027D02*
+X0192071Y0139027D01*
+X0192928Y0140226D02*
+X0194331Y0140226D01*
+X0194331Y0141424D02*
+X0193168Y0141424D01*
+X0193168Y0142623D02*
+X0194331Y0142623D01*
+X0194331Y0143821D02*
+X0193168Y0143821D01*
+X0193168Y0145020D02*
+X0193582Y0145020D01*
+X0197500Y0147500D02*
+X0197500Y0123937D01*
+X0196063Y0122500D01*
+X0161240Y0122500D01*
+X0160856Y0122500D01*
+X0160856Y0109764D01*
+X0156919Y0105827D01*
+X0124941Y0105827D01*
+X0124941Y0107795D02*
+X0133563Y0107795D01*
+X0133563Y0115000D01*
+X0136731Y0109323D02*
+X0137577Y0109673D01*
+X0138043Y0110139D01*
+X0138145Y0110080D01*
+X0138628Y0109950D01*
+X0140837Y0109950D01*
+X0140837Y0114400D01*
+X0142037Y0114400D01*
+X0142037Y0115600D01*
+X0145896Y0115600D01*
+X0145896Y0118400D01*
+X0145767Y0118883D01*
+X0145516Y0119316D01*
+X0145501Y0119331D01*
+X0148632Y0119331D01*
+X0148632Y0119036D01*
+X0149023Y0118092D01*
+X0149746Y0117370D01*
+X0150690Y0116979D01*
+X0156830Y0116979D01*
+X0157500Y0117256D01*
+X0157688Y0117179D01*
+X0157688Y0111076D01*
+X0155607Y0108995D01*
+X0136731Y0108995D01*
+X0136731Y0109323D01*
+X0136731Y0109065D02*
+X0150629Y0109065D01*
+X0150906Y0109009D02*
+X0151250Y0109009D01*
+X0151594Y0109009D01*
+X0152268Y0109144D01*
+X0152903Y0109407D01*
+X0153475Y0109789D01*
+X0153961Y0110275D01*
+X0154343Y0110847D01*
+X0154606Y0111482D01*
+X0154741Y0112156D01*
+X0154741Y0112500D01*
+X0154741Y0112844D01*
+X0154606Y0113518D01*
+X0154343Y0114153D01*
+X0153961Y0114725D01*
+X0153475Y0115211D01*
+X0152903Y0115593D01*
+X0152268Y0115856D01*
+X0151594Y0115991D01*
+X0151250Y0115991D01*
+X0150906Y0115991D01*
+X0150232Y0115856D01*
+X0149597Y0115593D01*
+X0149025Y0115211D01*
+X0148539Y0114725D01*
+X0148157Y0114153D01*
+X0147894Y0113518D01*
+X0147759Y0112844D01*
+X0147759Y0112500D01*
+X0147759Y0112156D01*
+X0147894Y0111482D01*
+X0148157Y0110847D01*
+X0148539Y0110275D01*
+X0149025Y0109789D01*
+X0149597Y0109407D01*
+X0150232Y0109144D01*
+X0150906Y0109009D01*
+X0151250Y0109009D02*
+X0151250Y0112500D01*
+X0154741Y0112500D01*
+X0151250Y0112500D01*
+X0151250Y0112500D01*
+X0151250Y0112500D01*
+X0151250Y0115991D01*
+X0151250Y0112500D01*
+X0151250Y0109009D01*
+X0151250Y0109065D02*
+X0151250Y0109065D01*
+X0151871Y0109065D02*
+X0155676Y0109065D01*
+X0156875Y0110263D02*
+X0153949Y0110263D01*
+X0154598Y0111462D02*
+X0157688Y0111462D01*
+X0157688Y0112660D02*
+X0154741Y0112660D01*
+X0154465Y0113859D02*
+X0157688Y0113859D01*
+X0157688Y0115057D02*
+X0153629Y0115057D01*
+X0151250Y0115057D02*
+X0151250Y0115057D01*
+X0151250Y0113859D02*
+X0151250Y0113859D01*
+X0151250Y0112660D02*
+X0151250Y0112660D01*
+X0151250Y0112500D02*
+X0151250Y0112500D01*
+X0151250Y0112500D01*
+X0147759Y0112500D01*
+X0151250Y0112500D01*
+X0151250Y0111462D02*
+X0151250Y0111462D01*
+X0151250Y0110263D02*
+X0151250Y0110263D01*
+X0148551Y0110263D02*
+X0145047Y0110263D01*
+X0145163Y0110330D02*
+X0145516Y0110684D01*
+X0145767Y0111117D01*
+X0145896Y0111600D01*
+X0145896Y0114400D01*
+X0142037Y0114400D01*
+X0142037Y0109950D01*
+X0144246Y0109950D01*
+X0144729Y0110080D01*
+X0145163Y0110330D01*
+X0145859Y0111462D02*
+X0147902Y0111462D01*
+X0147759Y0112660D02*
+X0145896Y0112660D01*
+X0145896Y0113859D02*
+X0148035Y0113859D01*
+X0148871Y0115057D02*
+X0142037Y0115057D01*
+X0142037Y0113859D02*
+X0140837Y0113859D01*
+X0140837Y0112660D02*
+X0142037Y0112660D01*
+X0142037Y0111462D02*
+X0140837Y0111462D01*
+X0140837Y0110263D02*
+X0142037Y0110263D01*
+X0145896Y0116256D02*
+X0157688Y0116256D01*
+X0164025Y0116256D02*
+X0249618Y0116256D01*
+X0249725Y0116211D02*
+X0251181Y0116211D01*
+X0251181Y0114183D01*
+X0249986Y0114183D01*
+X0249503Y0114054D01*
+X0249070Y0113804D01*
+X0248716Y0113450D01*
+X0248466Y0113017D01*
+X0248336Y0112534D01*
+X0248336Y0111299D01*
+X0248336Y0110866D01*
+X0248059Y0110589D01*
+X0247668Y0109645D01*
+X0247668Y0108168D01*
+X0236312Y0108168D01*
+X0235186Y0109295D01*
+X0234295Y0110186D01*
+X0233130Y0110668D01*
+X0228868Y0110668D01*
+X0228868Y0110964D01*
+X0228477Y0111908D01*
+X0227754Y0112630D01*
+X0226810Y0113021D01*
+X0220670Y0113021D01*
+X0220000Y0112744D01*
+X0219330Y0113021D01*
+X0213190Y0113021D01*
+X0212246Y0112630D01*
+X0211523Y0111908D01*
+X0211132Y0110964D01*
+X0211132Y0110668D01*
+X0206870Y0110668D01*
+X0205705Y0110186D01*
+X0204814Y0109295D01*
+X0202546Y0107027D01*
+X0162600Y0107027D01*
+X0162651Y0107078D01*
+X0163542Y0107969D01*
+X0164025Y0109134D01*
+X0164025Y0116979D01*
+X0164310Y0116979D01*
+X0165254Y0117370D01*
+X0165977Y0118092D01*
+X0166368Y0119036D01*
+X0166368Y0119331D01*
+X0196693Y0119331D01*
+X0197858Y0119814D01*
+X0198749Y0120705D01*
+X0200186Y0122142D01*
+X0200668Y0123307D01*
+X0200668Y0144208D01*
+X0201373Y0144912D01*
+X0202068Y0146591D01*
+X0202068Y0148409D01*
+X0201373Y0150088D01*
+X0200088Y0151373D01*
+X0198409Y0152068D01*
+X0196591Y0152068D01*
+X0194912Y0151373D01*
+X0193627Y0150088D01*
+X0193168Y0148981D01*
+X0193168Y0152251D01*
+X0194729Y0153811D01*
+X0194912Y0153627D01*
+X0196591Y0152931D01*
+X0198409Y0152931D01*
+X0200088Y0153627D01*
+X0201373Y0154912D01*
+X0202068Y0156591D01*
+X0202068Y0158409D01*
+X0201373Y0160088D01*
+X0200088Y0161373D01*
+X0198409Y0162068D01*
+X0196591Y0162068D01*
+X0194912Y0161373D01*
+X0194208Y0160668D01*
+X0193307Y0160668D01*
+X0193168Y0160611D01*
+X0193168Y0202400D01*
+X0194600Y0202400D01*
+X0194600Y0207100D01*
+X0195400Y0207100D01*
+X0195400Y0207900D01*
+X0200100Y0207900D01*
+X0200100Y0209612D01*
+X0197685Y0212027D01*
+X0200768Y0215111D01*
+X0200768Y0219331D01*
+X0225246Y0219331D01*
+X0225246Y0215198D01*
+X0225637Y0214254D01*
+X0226360Y0213531D01*
+X0227304Y0213140D01*
+X0240924Y0213140D01*
+X0241868Y0213531D01*
+X0242591Y0214254D01*
+X0242778Y0214706D01*
+X0252222Y0214706D01*
+X0252409Y0214254D01*
+X0253132Y0213531D01*
+X0254076Y0213140D01*
+X0267696Y0213140D01*
+X0268640Y0213531D01*
+X0269282Y0214173D01*
+X0269282Y0213367D01*
+X0269673Y0212423D01*
+X0270139Y0211957D01*
+X0270080Y0211855D01*
+X0269950Y0211372D01*
+X0269950Y0209163D01*
+X0274400Y0209163D01*
+X0274400Y0207963D01*
+X0269950Y0207963D01*
+X0269950Y0205754D01*
+X0270080Y0205271D01*
+X0270330Y0204837D01*
+X0270684Y0204484D01*
+X0271117Y0204233D01*
+X0271600Y0204104D01*
+X0274400Y0204104D01*
+X0274400Y0207963D01*
+X0275600Y0207963D01*
+X0275600Y0209163D01*
+X0280050Y0209163D01*
+X0280050Y0211372D01*
+X0279920Y0211855D01*
+X0279861Y0211957D01*
+X0280327Y0212423D01*
+X0280718Y0213367D01*
+X0280718Y0219507D01*
+X0280327Y0220451D01*
+X0279605Y0221174D01*
+X0278661Y0221565D01*
+X0271339Y0221565D01*
+X0270395Y0221174D01*
+X0270264Y0221043D01*
+X0269550Y0221043D01*
+X0269363Y0221494D01*
+X0268640Y0222217D01*
+X0267696Y0222608D01*
+X0254076Y0222608D01*
+X0253132Y0222217D01*
+X0252409Y0221494D01*
+X0252222Y0221043D01*
+X0242778Y0221043D01*
+X0242591Y0221494D01*
+X0241868Y0222217D01*
+X0240924Y0222608D01*
+X0237283Y0222608D01*
+X0237283Y0223130D01*
+X0236800Y0224295D01*
+X0235909Y0225186D01*
+X0234744Y0225668D01*
+X0189370Y0225668D01*
+X0188205Y0225186D01*
+X0187314Y0224295D01*
+X0186288Y0223268D01*
+X0182611Y0223268D01*
+X0179231Y0219889D01*
+X0179231Y0216212D01*
+X0178205Y0215186D01*
+X0177314Y0214295D01*
+X0176831Y0213130D01*
+X0176831Y0193812D01*
+X0173688Y0190668D01*
+X0149551Y0190668D01*
+X0153696Y0194814D01*
+X0154588Y0195705D01*
+X0155070Y0196870D01*
+X0155070Y0198882D01*
+X0155562Y0198882D01*
+X0156506Y0199273D01*
+X0157229Y0199996D01*
+X0157500Y0200651D01*
+X0157771Y0199996D01*
+X0158494Y0199273D01*
+X0159438Y0198882D01*
+X0166759Y0198882D01*
+X0167703Y0199273D01*
+X0168425Y0199996D01*
+X0168817Y0200940D01*
+X0168817Y0209060D01*
+X0168425Y0210004D01*
+X0167703Y0210727D01*
+X0166759Y0211118D01*
+X0166267Y0211118D01*
+X0166267Y0213793D01*
+X0166368Y0214036D01*
+X0166368Y0220964D01*
+X0165977Y0221908D01*
+X0165254Y0222630D01*
+X0164310Y0223021D01*
+X0158170Y0223021D01*
+X0157226Y0222630D01*
+X0156868Y0222273D01*
+X0156569Y0222353D01*
+X0154360Y0222353D01*
+X0154360Y0218100D01*
+X0153160Y0218100D01*
+X0153160Y0222353D01*
+X0150951Y0222353D01*
+X0150467Y0222223D01*
+X0150034Y0221973D01*
+X0149680Y0221619D01*
+X0149430Y0221186D01*
+X0149301Y0220703D01*
+X0149301Y0218100D01*
+X0153160Y0218100D01*
+X0153160Y0216900D01*
+X0154360Y0216900D01*
+X0154360Y0212647D01*
+X0156569Y0212647D01*
+X0156868Y0212727D01*
+X0157226Y0212370D01*
+X0158170Y0211979D01*
+X0159930Y0211979D01*
+X0159930Y0211118D01*
+X0159438Y0211118D01*
+X0158494Y0210727D01*
+X0157771Y0210004D01*
+X0157500Y0209349D01*
+X0157229Y0210004D01*
+X0156506Y0210727D01*
+X0155562Y0211118D01*
+X0148241Y0211118D01*
+X0147297Y0210727D01*
+X0146574Y0210004D01*
+X0146183Y0209060D01*
+X0146183Y0200940D01*
+X0146574Y0199996D01*
+X0147297Y0199273D01*
+X0148241Y0198882D01*
+X0148733Y0198882D01*
+X0148733Y0198812D01*
+X0145589Y0195668D01*
+X0129551Y0195668D01*
+X0129588Y0195705D01*
+X0130070Y0196870D01*
+X0130070Y0198882D01*
+X0130562Y0198882D01*
+X0131506Y0199273D01*
+X0132229Y0199996D01*
+X0132500Y0200651D01*
+X0132771Y0199996D01*
+X0133494Y0199273D01*
+X0134438Y0198882D01*
+X0141759Y0198882D01*
+X0142703Y0199273D01*
+X0143425Y0199996D01*
+X0143817Y0200940D01*
+X0143817Y0209060D01*
+X0143425Y0210004D01*
+X0142703Y0210727D01*
+X0141759Y0211118D01*
+X0141267Y0211118D01*
+X0141267Y0213793D01*
+X0141368Y0214036D01*
+X0141368Y0220964D01*
+X0140977Y0221908D01*
+X0140254Y0222630D01*
+X0139310Y0223021D01*
+X0133170Y0223021D01*
+X0132226Y0222630D01*
+X0131868Y0222273D01*
+X0131569Y0222353D01*
+X0129360Y0222353D01*
+X0129360Y0218100D01*
+X0128160Y0218100D01*
+X0128160Y0222353D01*
+X0125951Y0222353D01*
+X0125467Y0222223D01*
+X0125034Y0221973D01*
+X0124680Y0221619D01*
+X0124430Y0221186D01*
+X0124301Y0220703D01*
+X0124301Y0218100D01*
+X0128160Y0218100D01*
+X0128160Y0216900D01*
+X0129360Y0216900D01*
+X0129360Y0212647D01*
+X0131569Y0212647D01*
+X0131868Y0212727D01*
+X0132226Y0212370D01*
+X0133170Y0211979D01*
+X0134930Y0211979D01*
+X0134930Y0211118D01*
+X0134438Y0211118D01*
+X0133494Y0210727D01*
+X0132771Y0210004D01*
+X0132500Y0209349D01*
+X0132229Y0210004D01*
+X0131506Y0210727D01*
+X0130562Y0211118D01*
+X0123241Y0211118D01*
+X0122297Y0210727D01*
+X0121574Y0210004D01*
+X0121183Y0209060D01*
+X0121183Y0200940D01*
+X0121296Y0200668D01*
+X0118771Y0200668D01*
+X0118690Y0200635D01*
+X0118817Y0200940D01*
+X0118817Y0209060D01*
+X0118425Y0210004D01*
+X0117703Y0210727D01*
+X0116759Y0211118D01*
+X0116267Y0211118D01*
+X0116267Y0213793D01*
+X0116368Y0214036D01*
+X0116368Y0220964D01*
+X0115977Y0221908D01*
+X0115254Y0222630D01*
+X0114310Y0223021D01*
+X0108170Y0223021D01*
+X0107226Y0222630D01*
+X0106868Y0222273D01*
+X0106569Y0222353D01*
+X0104360Y0222353D01*
+X0104360Y0218100D01*
+X0103160Y0218100D01*
+X0103160Y0222353D01*
+X0100951Y0222353D01*
+X0100467Y0222223D01*
+X0100034Y0221973D01*
+X0099680Y0221619D01*
+X0099430Y0221186D01*
+X0099301Y0220703D01*
+X0099301Y0218100D01*
+X0103160Y0218100D01*
+X0103160Y0216900D01*
+X0104360Y0216900D01*
+X0104360Y0212647D01*
+X0106569Y0212647D01*
+X0106868Y0212727D01*
+X0107226Y0212370D01*
+X0108170Y0211979D01*
+X0109930Y0211979D01*
+X0109930Y0211118D01*
+X0109438Y0211118D01*
+X0108494Y0210727D01*
+X0107771Y0210004D01*
+X0107500Y0209349D01*
+X0107229Y0210004D01*
+X0106506Y0210727D01*
+X0105562Y0211118D01*
+X0098241Y0211118D01*
+X0097297Y0210727D01*
+X0096574Y0210004D01*
+X0096183Y0209060D01*
+X0096183Y0208168D01*
+X0085653Y0208168D01*
+X0085186Y0209295D01*
+X0084295Y0210186D01*
+X0079295Y0215186D01*
+X0078130Y0215668D01*
+X0068268Y0215668D01*
+X0068268Y0219889D01*
+X0064889Y0223268D01*
+X0061212Y0223268D01*
+X0057686Y0226795D01*
+X0057089Y0227392D01*
+X0286132Y0227392D01*
+X0286132Y0166433D01*
+X0284751Y0167814D01*
+X0282447Y0168768D01*
+X0272553Y0168768D01*
+X0270249Y0167814D01*
+X0268704Y0166268D01*
+X0261750Y0166268D01*
+X0260365Y0165695D01*
+X0255365Y0160695D01*
+X0254975Y0160305D01*
+X0254233Y0160305D01*
+X0253289Y0159914D01*
+X0252567Y0159191D01*
+X0252176Y0158247D01*
+X0252176Y0151713D01*
+X0252567Y0150769D01*
+X0253289Y0150047D01*
+X0254233Y0149656D01*
+X0260767Y0149656D01*
+X0261711Y0150047D01*
+X0262433Y0150769D01*
+X0262824Y0151713D01*
+X0262824Y0157495D01*
+X0264061Y0158731D01*
+X0268704Y0158731D01*
+X0270249Y0157186D01*
+X0270550Y0157061D01*
+X0270152Y0156771D01*
+X0269529Y0156148D01*
+X0269010Y0155435D01*
+X0268610Y0154650D01*
+X0268338Y0153811D01*
+X0268209Y0153000D01*
+X0277000Y0153000D01*
+X0277000Y0152000D01*
+X0278000Y0152000D01*
+X0278000Y0146900D01*
+X0281641Y0146900D01*
+X0282511Y0147038D01*
+X0283350Y0147310D01*
+X0284135Y0147710D01*
+X0284848Y0148229D01*
+X0285471Y0148852D01*
+X0285990Y0149565D01*
+X0286132Y0149845D01*
+X0286132Y0051433D01*
+X0284751Y0052814D01*
+X0282447Y0053768D01*
+X0272553Y0053768D01*
+X0272500Y0053746D01*
+X0272447Y0053768D01*
+X0269418Y0053768D01*
+X0269418Y0053926D01*
+X0270256Y0054273D01*
+X0270979Y0054996D01*
+X0271250Y0055651D01*
+X0271521Y0054996D01*
+X0272244Y0054273D01*
+X0273188Y0053882D01*
+X0280509Y0053882D01*
+X0281453Y0054273D01*
+X0282175Y0054996D01*
+X0282567Y0055940D01*
+X0282567Y0064060D01*
+X0282175Y0065004D01*
+X0281453Y0065727D01*
+X0280509Y0066118D01*
+X0280017Y0066118D01*
+X0280017Y0068793D01*
+X0280118Y0069036D01*
+X0280118Y0075964D01*
+X0279727Y0076908D01*
+X0279004Y0077630D01*
+X0278060Y0078021D01*
+X0277876Y0078021D01*
+X0278093Y0078347D01*
+X0278356Y0078982D01*
+X0278491Y0079656D01*
+X0278491Y0080000D01*
+X0278491Y0080344D01*
+X0278404Y0080778D01*
+X0282243Y0080778D01*
+X0283187Y0081169D01*
+X0283910Y0081892D01*
+X0284301Y0082836D01*
+X0284301Y0091731D01*
+X0283910Y0092675D01*
+X0283187Y0093398D01*
+X0282243Y0093789D01*
+X0281318Y0093789D01*
+X0281318Y0116211D01*
+X0282243Y0116211D01*
+X0283187Y0116602D01*
+X0283910Y0117325D01*
+X0284301Y0118269D01*
+X0284301Y0127164D01*
+X0283910Y0128108D01*
+X0283187Y0128831D01*
+X0282243Y0129222D01*
+X0271379Y0129222D01*
+X0271268Y0129176D01*
+X0271268Y0135750D01*
+X0270695Y0137135D01*
+X0269635Y0138195D01*
+X0264635Y0143195D01*
+X0263250Y0143768D01*
+X0262625Y0143768D01*
+X0262433Y0144231D01*
+X0261711Y0144953D01*
+X0260767Y0145344D01*
+X0254233Y0145344D01*
+X0253289Y0144953D01*
+X0252567Y0144231D01*
+X0252375Y0143768D01*
+X0251375Y0143768D01*
+X0251183Y0144231D01*
+X0250461Y0144953D01*
+X0249517Y0145344D01*
+X0242983Y0145344D01*
+X0242039Y0144953D01*
+X0241317Y0144231D01*
+X0240926Y0143286D01*
+X0240926Y0136753D01*
+X0241317Y0135809D01*
+X0242039Y0135086D01*
+X0242983Y0134695D01*
+X0249517Y0134695D01*
+X0250461Y0135086D01*
+X0251183Y0135809D01*
+X0251358Y0136231D01*
+X0252392Y0136231D01*
+X0252567Y0135809D01*
+X0253289Y0135086D01*
+X0254233Y0134695D01*
+X0260767Y0134695D01*
+X0261711Y0135086D01*
+X0261898Y0135273D01*
+X0263731Y0133439D01*
+X0263731Y0126318D01*
+X0262647Y0126318D01*
+X0262647Y0127164D01*
+X0262256Y0128108D01*
+X0261534Y0128831D01*
+X0260590Y0129222D01*
+X0249725Y0129222D01*
+X0248781Y0128831D01*
+X0248059Y0128108D01*
+X0247668Y0127164D01*
+X0247668Y0118269D01*
+X0248059Y0117325D01*
+X0248781Y0116602D01*
+X0249725Y0116211D01*
+X0251181Y0115057D02*
+X0164025Y0115057D01*
+X0164025Y0113859D02*
+X0249164Y0113859D01*
+X0248370Y0112660D02*
+X0227682Y0112660D01*
+X0228661Y0111462D02*
+X0248336Y0111462D01*
+X0248336Y0111299D02*
+X0248769Y0111299D01*
+X0248336Y0111299D01*
+X0248769Y0111299D02*
+X0248769Y0111299D01*
+X0247924Y0110263D02*
+X0234109Y0110263D01*
+X0235416Y0109065D02*
+X0247668Y0109065D01*
+X0240000Y0111299D02*
+X0240000Y0122500D01*
+X0243491Y0122500D01*
+X0243491Y0122844D01*
+X0243356Y0123518D01*
+X0243093Y0124153D01*
+X0242711Y0124725D01*
+X0242225Y0125211D01*
+X0241653Y0125593D01*
+X0241018Y0125856D01*
+X0240344Y0125991D01*
+X0240000Y0125991D01*
+X0239656Y0125991D01*
+X0238982Y0125856D01*
+X0238347Y0125593D01*
+X0237775Y0125211D01*
+X0237289Y0124725D01*
+X0236907Y0124153D01*
+X0236644Y0123518D01*
+X0236509Y0122844D01*
+X0236509Y0122500D01*
+X0236509Y0122156D01*
+X0236644Y0121482D01*
+X0236907Y0120847D01*
+X0237289Y0120275D01*
+X0237775Y0119789D01*
+X0238347Y0119407D01*
+X0238982Y0119144D01*
+X0239656Y0119009D01*
+X0240000Y0119009D01*
+X0240344Y0119009D01*
+X0241018Y0119144D01*
+X0241653Y0119407D01*
+X0242225Y0119789D01*
+X0242711Y0120275D01*
+X0243093Y0120847D01*
+X0243356Y0121482D01*
+X0243491Y0122156D01*
+X0243491Y0122500D01*
+X0240000Y0122500D01*
+X0240000Y0122500D01*
+X0240000Y0122500D01*
+X0240000Y0125991D01*
+X0240000Y0122500D01*
+X0240000Y0119009D01*
+X0240000Y0122500D01*
+X0240000Y0122500D01*
+X0240000Y0122500D01*
+X0236509Y0122500D01*
+X0240000Y0122500D01*
+X0240000Y0122248D02*
+X0240000Y0122248D01*
+X0240000Y0121050D02*
+X0240000Y0121050D01*
+X0240000Y0119851D02*
+X0240000Y0119851D01*
+X0242288Y0119851D02*
+X0247668Y0119851D01*
+X0247668Y0118653D02*
+X0166209Y0118653D01*
+X0165338Y0117454D02*
+X0248005Y0117454D01*
+X0247668Y0121050D02*
+X0243177Y0121050D01*
+X0243491Y0122248D02*
+X0247668Y0122248D01*
+X0247668Y0123447D02*
+X0243371Y0123447D01*
+X0242765Y0124645D02*
+X0247668Y0124645D01*
+X0247668Y0125844D02*
+X0241049Y0125844D01*
+X0240000Y0125844D02*
+X0240000Y0125844D01*
+X0238951Y0125844D02*
+X0200668Y0125844D01*
+X0200668Y0127042D02*
+X0247668Y0127042D01*
+X0248191Y0128241D02*
+X0200668Y0128241D01*
+X0200668Y0129439D02*
+X0263731Y0129439D01*
+X0263731Y0128241D02*
+X0262124Y0128241D01*
+X0262647Y0127042D02*
+X0263731Y0127042D01*
+X0263731Y0130638D02*
+X0200668Y0130638D01*
+X0200668Y0131836D02*
+X0223517Y0131836D01*
+X0223347Y0131907D02*
+X0223982Y0131644D01*
+X0224656Y0131509D01*
+X0225000Y0131509D01*
+X0225344Y0131509D01*
+X0226018Y0131644D01*
+X0226653Y0131907D01*
+X0227225Y0132289D01*
+X0227711Y0132775D01*
+X0228093Y0133347D01*
+X0228356Y0133982D01*
+X0228491Y0134656D01*
+X0228491Y0135000D01*
+X0228491Y0135344D01*
+X0228356Y0136018D01*
+X0228093Y0136653D01*
+X0227711Y0137225D01*
+X0227225Y0137711D01*
+X0226653Y0138093D01*
+X0226018Y0138356D01*
+X0225344Y0138491D01*
+X0225000Y0138491D01*
+X0224656Y0138491D01*
+X0223982Y0138356D01*
+X0223347Y0138093D01*
+X0222775Y0137711D01*
+X0222289Y0137225D01*
+X0221907Y0136653D01*
+X0221644Y0136018D01*
+X0221509Y0135344D01*
+X0221509Y0135000D01*
+X0221509Y0134656D01*
+X0221644Y0133982D01*
+X0221907Y0133347D01*
+X0222289Y0132775D01*
+X0222775Y0132289D01*
+X0223347Y0131907D01*
+X0225000Y0131836D02*
+X0225000Y0131836D01*
+X0225000Y0131509D02*
+X0225000Y0135000D01*
+X0228491Y0135000D01*
+X0225000Y0135000D01*
+X0225000Y0135000D01*
+X0225000Y0135000D01*
+X0225000Y0138491D01*
+X0225000Y0135000D01*
+X0225000Y0135000D01*
+X0225000Y0135000D01*
+X0221509Y0135000D01*
+X0225000Y0135000D01*
+X0225000Y0131509D01*
+X0226483Y0131836D02*
+X0263731Y0131836D01*
+X0263731Y0133035D02*
+X0227885Y0133035D01*
+X0228406Y0134233D02*
+X0262937Y0134233D01*
+X0268802Y0139027D02*
+X0274355Y0139027D01*
+X0274407Y0139153D02*
+X0274144Y0138518D01*
+X0274009Y0137844D01*
+X0274009Y0137500D01*
+X0274009Y0137156D01*
+X0274144Y0136482D01*
+X0274407Y0135847D01*
+X0274789Y0135275D01*
+X0275275Y0134789D01*
+X0275847Y0134407D01*
+X0276482Y0134144D01*
+X0277156Y0134009D01*
+X0277500Y0134009D01*
+X0277844Y0134009D01*
+X0278518Y0134144D01*
+X0279153Y0134407D01*
+X0279725Y0134789D01*
+X0280211Y0135275D01*
+X0280593Y0135847D01*
+X0280856Y0136482D01*
+X0280991Y0137156D01*
+X0280991Y0137500D01*
+X0280991Y0137844D01*
+X0280856Y0138518D01*
+X0280593Y0139153D01*
+X0280211Y0139725D01*
+X0279725Y0140211D01*
+X0279153Y0140593D01*
+X0278518Y0140856D01*
+X0277844Y0140991D01*
+X0277500Y0140991D01*
+X0277156Y0140991D01*
+X0276482Y0140856D01*
+X0275847Y0140593D01*
+X0275275Y0140211D01*
+X0274789Y0139725D01*
+X0274407Y0139153D01*
+X0274009Y0137829D02*
+X0270001Y0137829D01*
+X0270904Y0136630D02*
+X0274114Y0136630D01*
+X0274009Y0137500D02*
+X0277500Y0137500D01*
+X0280991Y0137500D01*
+X0277500Y0137500D01*
+X0277500Y0137500D01*
+X0277500Y0137500D01*
+X0277500Y0140991D01*
+X0277500Y0137500D01*
+X0277500Y0134009D01*
+X0277500Y0137500D01*
+X0277500Y0137500D01*
+X0277500Y0137500D01*
+X0274009Y0137500D01*
+X0274684Y0135432D02*
+X0271268Y0135432D01*
+X0271268Y0134233D02*
+X0276265Y0134233D01*
+X0277500Y0134233D02*
+X0277500Y0134233D01*
+X0277500Y0135432D02*
+X0277500Y0135432D01*
+X0277500Y0136630D02*
+X0277500Y0136630D01*
+X0277500Y0137829D02*
+X0277500Y0137829D01*
+X0277500Y0139027D02*
+X0277500Y0139027D01*
+X0277500Y0140226D02*
+X0277500Y0140226D01*
+X0275297Y0140226D02*
+X0267604Y0140226D01*
+X0266405Y0141424D02*
+X0286132Y0141424D01*
+X0286132Y0140226D02*
+X0279703Y0140226D01*
+X0280645Y0139027D02*
+X0286132Y0139027D01*
+X0286132Y0137829D02*
+X0280991Y0137829D01*
+X0280886Y0136630D02*
+X0286132Y0136630D01*
+X0286132Y0135432D02*
+X0280316Y0135432D01*
+X0278735Y0134233D02*
+X0286132Y0134233D01*
+X0286132Y0133035D02*
+X0271268Y0133035D01*
+X0271268Y0131836D02*
+X0286132Y0131836D01*
+X0286132Y0130638D02*
+X0271268Y0130638D01*
+X0271268Y0129439D02*
+X0286132Y0129439D01*
+X0286132Y0128241D02*
+X0283778Y0128241D01*
+X0284301Y0127042D02*
+X0286132Y0127042D01*
+X0286132Y0125844D02*
+X0284301Y0125844D01*
+X0284301Y0124645D02*
+X0286132Y0124645D01*
+X0286132Y0123447D02*
+X0284301Y0123447D01*
+X0284301Y0122248D02*
+X0286132Y0122248D01*
+X0286132Y0121050D02*
+X0284301Y0121050D01*
+X0284301Y0119851D02*
+X0286132Y0119851D01*
+X0286132Y0118653D02*
+X0284301Y0118653D01*
+X0283963Y0117454D02*
+X0286132Y0117454D01*
+X0286132Y0116256D02*
+X0282351Y0116256D01*
+X0281318Y0115057D02*
+X0286132Y0115057D01*
+X0286132Y0113859D02*
+X0281318Y0113859D01*
+X0281318Y0112660D02*
+X0286132Y0112660D01*
+X0286132Y0111462D02*
+X0281318Y0111462D01*
+X0281318Y0110263D02*
+X0286132Y0110263D01*
+X0286132Y0109065D02*
+X0281318Y0109065D01*
+X0281318Y0107866D02*
+X0286132Y0107866D01*
+X0286132Y0106668D02*
+X0281318Y0106668D01*
+X0281318Y0105469D02*
+X0286132Y0105469D01*
+X0286132Y0104270D02*
+X0281318Y0104270D01*
+X0281318Y0103072D02*
+X0286132Y0103072D01*
+X0286132Y0101873D02*
+X0281318Y0101873D01*
+X0281318Y0100675D02*
+X0286132Y0100675D01*
+X0286132Y0099476D02*
+X0281318Y0099476D01*
+X0281318Y0098278D02*
+X0286132Y0098278D01*
+X0286132Y0097079D02*
+X0281318Y0097079D01*
+X0281318Y0095881D02*
+X0286132Y0095881D01*
+X0286132Y0094682D02*
+X0281318Y0094682D01*
+X0282980Y0093484D02*
+X0286132Y0093484D01*
+X0286132Y0092285D02*
+X0284071Y0092285D01*
+X0284301Y0091087D02*
+X0286132Y0091087D01*
+X0286132Y0089888D02*
+X0284301Y0089888D01*
+X0284301Y0088690D02*
+X0286132Y0088690D01*
+X0286132Y0087491D02*
+X0284301Y0087491D01*
+X0284301Y0086293D02*
+X0286132Y0086293D01*
+X0286132Y0085094D02*
+X0284301Y0085094D01*
+X0284301Y0083896D02*
+X0286132Y0083896D01*
+X0286132Y0082697D02*
+X0284244Y0082697D01*
+X0283517Y0081499D02*
+X0286132Y0081499D01*
+X0286132Y0080300D02*
+X0278491Y0080300D01*
+X0278491Y0080000D02*
+X0275000Y0080000D01*
+X0275000Y0080000D01*
+X0275000Y0080000D01*
+X0271509Y0080000D01*
+X0271509Y0079656D01*
+X0271644Y0078982D01*
+X0271907Y0078347D01*
+X0272124Y0078021D01*
+X0271920Y0078021D01*
+X0270976Y0077630D01*
+X0270618Y0077273D01*
+X0270319Y0077353D01*
+X0268110Y0077353D01*
+X0268110Y0073100D01*
+X0266910Y0073100D01*
+X0266910Y0077353D01*
+X0265182Y0077353D01*
+X0265695Y0077865D01*
+X0269717Y0081887D01*
+X0270435Y0081169D01*
+X0271379Y0080778D01*
+X0271596Y0080778D01*
+X0271509Y0080344D01*
+X0271509Y0080000D01*
+X0275000Y0080000D01*
+X0278491Y0080000D01*
+X0278380Y0079102D02*
+X0286132Y0079102D01*
+X0286132Y0077903D02*
+X0278345Y0077903D01*
+X0279811Y0076705D02*
+X0286132Y0076705D01*
+X0286132Y0075506D02*
+X0280118Y0075506D01*
+X0280118Y0074308D02*
+X0286132Y0074308D01*
+X0286132Y0073109D02*
+X0280118Y0073109D01*
+X0280118Y0071911D02*
+X0286132Y0071911D01*
+X0286132Y0070712D02*
+X0280118Y0070712D01*
+X0280118Y0069514D02*
+X0286132Y0069514D01*
+X0286132Y0068315D02*
+X0280017Y0068315D01*
+X0280017Y0067117D02*
+X0286132Y0067117D01*
+X0286132Y0065918D02*
+X0280991Y0065918D01*
+X0282293Y0064720D02*
+X0286132Y0064720D01*
+X0286132Y0063521D02*
+X0282567Y0063521D01*
+X0282567Y0062323D02*
+X0286132Y0062323D01*
+X0286132Y0061124D02*
+X0282567Y0061124D01*
+X0282567Y0059926D02*
+X0286132Y0059926D01*
+X0286132Y0058727D02*
+X0282567Y0058727D01*
+X0282567Y0057529D02*
+X0286132Y0057529D01*
+X0286132Y0056330D02*
+X0282567Y0056330D01*
+X0282232Y0055132D02*
+X0286132Y0055132D01*
+X0286132Y0053933D02*
+X0280631Y0053933D01*
+X0284830Y0052734D02*
+X0286132Y0052734D01*
+X0286132Y0051536D02*
+X0286029Y0051536D01*
+X0276848Y0060000D02*
+X0276848Y0072500D01*
+X0274990Y0072500D01*
+X0273680Y0066979D02*
+X0271920Y0066979D01*
+X0270976Y0067370D01*
+X0270618Y0067727D01*
+X0270319Y0067647D01*
+X0268110Y0067647D01*
+X0268110Y0071900D01*
+X0266910Y0071900D01*
+X0266910Y0067647D01*
+X0264701Y0067647D01*
+X0264217Y0067777D01*
+X0263784Y0068027D01*
+X0263430Y0068381D01*
+X0263180Y0068814D01*
+X0263051Y0069297D01*
+X0263051Y0071900D01*
+X0266910Y0071900D01*
+X0266910Y0073100D01*
+X0263051Y0073100D01*
+X0263051Y0075703D01*
+X0263180Y0076186D01*
+X0263206Y0076231D01*
+X0260758Y0076231D01*
+X0260758Y0073100D01*
+X0256506Y0073100D01*
+X0256506Y0071900D01*
+X0260758Y0071900D01*
+X0260758Y0068313D01*
+X0260629Y0067830D01*
+X0260379Y0067396D01*
+X0260025Y0067043D01*
+X0259592Y0066792D01*
+X0259108Y0066663D01*
+X0256505Y0066663D01*
+X0256505Y0071900D01*
+X0255306Y0071900D01*
+X0255306Y0066663D01*
+X0255068Y0066663D01*
+X0255068Y0065837D01*
+X0255306Y0065837D01*
+X0255306Y0060600D01*
+X0256505Y0060600D01*
+X0256505Y0065837D01*
+X0259108Y0065837D01*
+X0259592Y0065708D01*
+X0260025Y0065457D01*
+X0260379Y0065104D01*
+X0260395Y0065075D01*
+X0261047Y0065727D01*
+X0261991Y0066118D01*
+X0269312Y0066118D01*
+X0270256Y0065727D01*
+X0270979Y0065004D01*
+X0271250Y0064349D01*
+X0271521Y0065004D01*
+X0272244Y0065727D01*
+X0273188Y0066118D01*
+X0273680Y0066118D01*
+X0273680Y0066979D01*
+X0272706Y0065918D02*
+X0269794Y0065918D01*
+X0271097Y0064720D02*
+X0271403Y0064720D01*
+X0271587Y0067117D02*
+X0260099Y0067117D01*
+X0260758Y0068315D02*
+X0263496Y0068315D01*
+X0263051Y0069514D02*
+X0260758Y0069514D01*
+X0260758Y0070712D02*
+X0263051Y0070712D01*
+X0263051Y0073109D02*
+X0260758Y0073109D01*
+X0260758Y0074308D02*
+X0263051Y0074308D01*
+X0263051Y0075506D02*
+X0260758Y0075506D01*
+X0265733Y0077903D02*
+X0271635Y0077903D01*
+X0271620Y0079102D02*
+X0266931Y0079102D01*
+X0268130Y0080300D02*
+X0271509Y0080300D01*
+X0270105Y0081499D02*
+X0269328Y0081499D01*
+X0268110Y0076705D02*
+X0266910Y0076705D01*
+X0266910Y0075506D02*
+X0268110Y0075506D01*
+X0268110Y0074308D02*
+X0266910Y0074308D01*
+X0266910Y0073109D02*
+X0268110Y0073109D01*
+X0266910Y0071911D02*
+X0256506Y0071911D01*
+X0256505Y0070712D02*
+X0255306Y0070712D01*
+X0255306Y0069514D02*
+X0256505Y0069514D01*
+X0256505Y0068315D02*
+X0255306Y0068315D01*
+X0255306Y0067117D02*
+X0256505Y0067117D01*
+X0255068Y0065918D02*
+X0261509Y0065918D01*
+X0256505Y0064720D02*
+X0255306Y0064720D01*
+X0255306Y0063521D02*
+X0256505Y0063521D01*
+X0256505Y0062323D02*
+X0255306Y0062323D01*
+X0255306Y0061124D02*
+X0256505Y0061124D01*
+X0256505Y0059400D02*
+X0256505Y0054163D01*
+X0259108Y0054163D01*
+X0259592Y0054292D01*
+X0260025Y0054543D01*
+X0260379Y0054896D01*
+X0260395Y0054925D01*
+X0261047Y0054273D01*
+X0261991Y0053882D01*
+X0263081Y0053882D01*
+X0263081Y0053768D01*
+X0262553Y0053768D01*
+X0254770Y0053768D01*
+X0254933Y0054163D01*
+X0255306Y0054163D01*
+X0255306Y0059400D01*
+X0256505Y0059400D01*
+X0256505Y0058727D02*
+X0255306Y0058727D01*
+X0255306Y0057529D02*
+X0256505Y0057529D01*
+X0256505Y0056330D02*
+X0255306Y0056330D01*
+X0255306Y0055132D02*
+X0256505Y0055132D01*
+X0254838Y0053933D02*
+X0261869Y0053933D01*
+X0266250Y0050000D02*
+X0266250Y0060000D01*
+X0265652Y0060000D01*
+X0271035Y0055132D02*
+X0271465Y0055132D01*
+X0273065Y0053933D02*
+X0269435Y0053933D01*
+X0257825Y0045543D02*
+X0137891Y0045543D01*
+X0137711Y0045275D02*
+X0138093Y0045847D01*
+X0138356Y0046482D01*
+X0138491Y0047156D01*
+X0138491Y0047500D01*
+X0138491Y0047844D01*
+X0138356Y0048518D01*
+X0138093Y0049153D01*
+X0137711Y0049725D01*
+X0137225Y0050211D01*
+X0136653Y0050593D01*
+X0136018Y0050856D01*
+X0135344Y0050991D01*
+X0135000Y0050991D01*
+X0134656Y0050991D01*
+X0133982Y0050856D01*
+X0133347Y0050593D01*
+X0132775Y0050211D01*
+X0132289Y0049725D01*
+X0131907Y0049153D01*
+X0131644Y0048518D01*
+X0131509Y0047844D01*
+X0131509Y0047500D01*
+X0131509Y0047156D01*
+X0131644Y0046482D01*
+X0131907Y0045847D01*
+X0132289Y0045275D01*
+X0132775Y0044789D01*
+X0133347Y0044407D01*
+X0133982Y0044144D01*
+X0134656Y0044009D01*
+X0135000Y0044009D01*
+X0135344Y0044009D01*
+X0136018Y0044144D01*
+X0136653Y0044407D01*
+X0137225Y0044789D01*
+X0137711Y0045275D01*
+X0136504Y0044345D02*
+X0258322Y0044345D01*
+X0259289Y0043146D02*
+X0130085Y0043146D01*
+X0130699Y0044345D02*
+X0133496Y0044345D01*
+X0135000Y0044345D02*
+X0135000Y0044345D01*
+X0135000Y0044009D02*
+X0135000Y0047500D01*
+X0138491Y0047500D01*
+X0135000Y0047500D01*
+X0135000Y0047500D01*
+X0135000Y0047500D01*
+X0135000Y0050991D01*
+X0135000Y0047500D01*
+X0135000Y0047500D01*
+X0135000Y0047500D01*
+X0131509Y0047500D01*
+X0135000Y0047500D01*
+X0135000Y0044009D01*
+X0135000Y0045543D02*
+X0135000Y0045543D01*
+X0135000Y0046742D02*
+X0135000Y0046742D01*
+X0135000Y0047940D02*
+X0135000Y0047940D01*
+X0135000Y0049139D02*
+X0135000Y0049139D01*
+X0135000Y0050337D02*
+X0135000Y0050337D01*
+X0132964Y0050337D02*
+X0130699Y0050337D01*
+X0130699Y0050703D02*
+X0130570Y0051186D01*
+X0130320Y0051619D01*
+X0129966Y0051973D01*
+X0129533Y0052223D01*
+X0129049Y0052353D01*
+X0126840Y0052353D01*
+X0126840Y0048100D01*
+X0130699Y0048100D01*
+X0130699Y0050703D01*
+X0130368Y0051536D02*
+X0163731Y0051536D01*
+X0163731Y0052734D02*
+X0122522Y0052734D01*
+X0125640Y0051536D02*
+X0126840Y0051536D01*
+X0126840Y0050337D02*
+X0125640Y0050337D01*
+X0125640Y0049139D02*
+X0126840Y0049139D01*
+X0126840Y0047940D02*
+X0131529Y0047940D01*
+X0131592Y0046742D02*
+X0130699Y0046742D01*
+X0130699Y0045543D02*
+X0132109Y0045543D01*
+X0131901Y0049139D02*
+X0130699Y0049139D01*
+X0126840Y0046742D02*
+X0125640Y0046742D01*
+X0125640Y0045543D02*
+X0126840Y0045543D01*
+X0126840Y0044345D02*
+X0125640Y0044345D01*
+X0125640Y0043146D02*
+X0126840Y0043146D01*
+X0126063Y0037500D02*
+X0130000Y0033563D01*
+X0130000Y0022500D01*
+X0122500Y0015000D01*
+X0128268Y0015581D02*
+X0258535Y0015581D01*
+X0258244Y0016779D02*
+X0128760Y0016779D01*
+X0128268Y0014382D02*
+X0259143Y0014382D01*
+X0260214Y0013184D02*
+X0128268Y0013184D01*
+X0127643Y0011985D02*
+X0262822Y0011985D01*
+X0277000Y0015581D02*
+X0278000Y0015581D01*
+X0278000Y0016779D02*
+X0277000Y0016779D01*
+X0252601Y0029963D02*
+X0133168Y0029963D01*
+X0133168Y0031161D02*
+X0251776Y0031161D01*
+X0251509Y0032360D02*
+X0133168Y0032360D01*
+X0133168Y0033558D02*
+X0251660Y0033558D01*
+X0252320Y0034757D02*
+X0132935Y0034757D01*
+X0132089Y0035955D02*
+X0254479Y0035955D01*
+X0258361Y0040749D02*
+X0112590Y0040749D01*
+X0111391Y0041948D02*
+X0259383Y0041948D01*
+X0239969Y0053768D02*
+X0171268Y0053768D01*
+X0171268Y0075750D01*
+X0170695Y0077135D01*
+X0169635Y0078195D01*
+X0168250Y0078768D01*
+X0129720Y0078768D01*
+X0129872Y0079135D01*
+X0129872Y0094784D01*
+X0188688Y0094784D01*
+X0195267Y0088205D01*
+X0195903Y0087568D01*
+X0189804Y0087568D01*
+X0188860Y0087177D01*
+X0188137Y0086455D01*
+X0187746Y0085511D01*
+X0187746Y0066989D01*
+X0188137Y0066045D01*
+X0188860Y0065323D01*
+X0189804Y0064931D01*
+X0213124Y0064931D01*
+X0213124Y0062388D01*
+X0216411Y0062388D01*
+X0216411Y0062013D01*
+X0216786Y0062013D01*
+X0216786Y0057545D01*
+X0218423Y0057545D01*
+X0218907Y0057674D01*
+X0219016Y0057737D01*
+X0219470Y0057283D01*
+X0220414Y0056892D01*
+X0224586Y0056892D01*
+X0225432Y0057243D01*
+X0226111Y0056961D01*
+X0226267Y0056805D01*
+X0227652Y0056231D01*
+X0238573Y0056231D01*
+X0238573Y0055552D01*
+X0238964Y0054608D01*
+X0239687Y0053886D01*
+X0239969Y0053768D01*
+X0239639Y0053933D02*
+X0171268Y0053933D01*
+X0171268Y0055132D02*
+X0204932Y0055132D01*
+X0204789Y0055275D02*
+X0205275Y0054789D01*
+X0205847Y0054407D01*
+X0206482Y0054144D01*
+X0207156Y0054009D01*
+X0207500Y0054009D01*
+X0207844Y0054009D01*
+X0208518Y0054144D01*
+X0209153Y0054407D01*
+X0209725Y0054789D01*
+X0210211Y0055275D01*
+X0210593Y0055847D01*
+X0210856Y0056482D01*
+X0210991Y0057156D01*
+X0210991Y0057500D01*
+X0210991Y0057844D01*
+X0210856Y0058518D01*
+X0210593Y0059153D01*
+X0210211Y0059725D01*
+X0209725Y0060211D01*
+X0209153Y0060593D01*
+X0208518Y0060856D01*
+X0207844Y0060991D01*
+X0207500Y0060991D01*
+X0207156Y0060991D01*
+X0206482Y0060856D01*
+X0205847Y0060593D01*
+X0205275Y0060211D01*
+X0204789Y0059725D01*
+X0204407Y0059153D01*
+X0204144Y0058518D01*
+X0204009Y0057844D01*
+X0204009Y0057500D01*
+X0204009Y0057156D01*
+X0204144Y0056482D01*
+X0204407Y0055847D01*
+X0204789Y0055275D01*
+X0204206Y0056330D02*
+X0171268Y0056330D01*
+X0171268Y0057529D02*
+X0204009Y0057529D01*
+X0204009Y0057500D02*
+X0207500Y0057500D01*
+X0210991Y0057500D01*
+X0207500Y0057500D01*
+X0207500Y0057500D01*
+X0207500Y0057500D01*
+X0207500Y0060991D01*
+X0207500Y0057500D01*
+X0207500Y0054009D01*
+X0207500Y0057500D01*
+X0207500Y0057500D01*
+X0207500Y0057500D01*
+X0204009Y0057500D01*
+X0204230Y0058727D02*
+X0171268Y0058727D01*
+X0171268Y0059926D02*
+X0204989Y0059926D01*
+X0207500Y0059926D02*
+X0207500Y0059926D01*
+X0207500Y0058727D02*
+X0207500Y0058727D01*
+X0207500Y0057529D02*
+X0207500Y0057529D01*
+X0207500Y0056330D02*
+X0207500Y0056330D01*
+X0207500Y0055132D02*
+X0207500Y0055132D01*
+X0210068Y0055132D02*
+X0238747Y0055132D01*
+X0227414Y0056330D02*
+X0210794Y0056330D01*
+X0210991Y0057529D02*
+X0219225Y0057529D01*
+X0216786Y0058727D02*
+X0216411Y0058727D01*
+X0216411Y0057545D02*
+X0216411Y0062013D01*
+X0213124Y0062013D01*
+X0213124Y0059195D01*
+X0213253Y0058712D01*
+X0213503Y0058278D01*
+X0213857Y0057924D01*
+X0214290Y0057674D01*
+X0214773Y0057545D01*
+X0216411Y0057545D01*
+X0216411Y0059926D02*
+X0216786Y0059926D01*
+X0216786Y0061124D02*
+X0216411Y0061124D01*
+X0216411Y0062323D02*
+X0171268Y0062323D01*
+X0171268Y0063521D02*
+X0213124Y0063521D01*
+X0213124Y0064720D02*
+X0171268Y0064720D01*
+X0171268Y0065918D02*
+X0188264Y0065918D01*
+X0187746Y0067117D02*
+X0171268Y0067117D01*
+X0171268Y0068315D02*
+X0187746Y0068315D01*
+X0187746Y0069514D02*
+X0171268Y0069514D01*
+X0171268Y0070712D02*
+X0187746Y0070712D01*
+X0187746Y0071911D02*
+X0171268Y0071911D01*
+X0171268Y0073109D02*
+X0187746Y0073109D01*
+X0187746Y0074308D02*
+X0171268Y0074308D01*
+X0171268Y0075506D02*
+X0187746Y0075506D01*
+X0187746Y0076705D02*
+X0170873Y0076705D01*
+X0169926Y0077903D02*
+X0187746Y0077903D01*
+X0187746Y0079102D02*
+X0129858Y0079102D01*
+X0129872Y0080300D02*
+X0187746Y0080300D01*
+X0187746Y0081499D02*
+X0129872Y0081499D01*
+X0129872Y0082697D02*
+X0187746Y0082697D01*
+X0187746Y0083896D02*
+X0129872Y0083896D01*
+X0129872Y0085094D02*
+X0187746Y0085094D01*
+X0188070Y0086293D02*
+X0129872Y0086293D01*
+X0129872Y0087491D02*
+X0189618Y0087491D01*
+X0192385Y0091087D02*
+X0129872Y0091087D01*
+X0129872Y0092285D02*
+X0191186Y0092285D01*
+X0189988Y0093484D02*
+X0129872Y0093484D01*
+X0129872Y0094682D02*
+X0188789Y0094682D01*
+X0190000Y0097953D02*
+X0197953Y0090000D01*
+X0232500Y0090000D01*
+X0237500Y0095000D01*
+X0237500Y0098701D01*
+X0236437Y0101850D02*
+X0232500Y0097913D01*
+X0232500Y0097500D01*
+X0223740Y0097500D01*
+X0216260Y0097500D02*
+X0208937Y0097500D01*
+X0207500Y0099136D01*
+X0204746Y0101890D01*
+X0124941Y0101890D01*
+X0124941Y0103858D02*
+X0203858Y0103858D01*
+X0207500Y0107500D01*
+X0216260Y0107500D01*
+X0211339Y0111462D02*
+X0164025Y0111462D01*
+X0164025Y0112660D02*
+X0212318Y0112660D01*
+X0205891Y0110263D02*
+X0164025Y0110263D01*
+X0163996Y0109065D02*
+X0204584Y0109065D01*
+X0203385Y0107866D02*
+X0163439Y0107866D01*
+X0149662Y0117454D02*
+X0145896Y0117454D01*
+X0145828Y0118653D02*
+X0148791Y0118653D01*
+X0148632Y0125844D02*
+X0145609Y0125844D01*
+X0145896Y0127042D02*
+X0149158Y0127042D01*
+X0150000Y0128241D02*
+X0150000Y0128241D01*
+X0150000Y0129439D02*
+X0150000Y0129439D01*
+X0150000Y0130638D02*
+X0150000Y0130638D01*
+X0150000Y0131836D02*
+X0150000Y0131836D01*
+X0148221Y0128241D02*
+X0145896Y0128241D01*
+X0133563Y0130000D02*
+X0128750Y0130000D01*
+X0128750Y0122500D01*
+X0117264Y0117441D02*
+X0117264Y0133563D01*
+X0122451Y0138750D01*
+X0187313Y0138750D01*
+X0190000Y0141437D01*
+X0190000Y0153563D01*
+X0193937Y0157500D01*
+X0197500Y0157500D01*
+X0201657Y0159402D02*
+X0209231Y0159402D01*
+X0209231Y0159889D02*
+X0209231Y0155111D01*
+X0211842Y0152500D01*
+X0209231Y0149889D01*
+X0209231Y0145111D01*
+X0212611Y0141731D01*
+X0217389Y0141731D01*
+X0220000Y0144342D01*
+X0222611Y0141731D01*
+X0227389Y0141731D01*
+X0230768Y0145111D01*
+X0230768Y0149889D01*
+X0227685Y0152973D01*
+X0230100Y0155388D01*
+X0230100Y0157100D01*
+X0225400Y0157100D01*
+X0225400Y0157900D01*
+X0230100Y0157900D01*
+X0230100Y0159612D01*
+X0227212Y0162500D01*
+X0230100Y0165388D01*
+X0230100Y0167100D01*
+X0225400Y0167100D01*
+X0225400Y0167900D01*
+X0230100Y0167900D01*
+X0230100Y0169612D01*
+X0227685Y0172027D01*
+X0230768Y0175111D01*
+X0230768Y0179889D01*
+X0227389Y0183268D01*
+X0222611Y0183268D01*
+X0220000Y0180658D01*
+X0217389Y0183268D01*
+X0212611Y0183268D01*
+X0209231Y0179889D01*
+X0209231Y0175111D01*
+X0211842Y0172500D01*
+X0209231Y0169889D01*
+X0209231Y0165111D01*
+X0211842Y0162500D01*
+X0209231Y0159889D01*
+X0209943Y0160601D02*
+X0200860Y0160601D01*
+X0199059Y0161799D02*
+X0211141Y0161799D01*
+X0211345Y0162998D02*
+X0193168Y0162998D01*
+X0193168Y0164196D02*
+X0210146Y0164196D01*
+X0209231Y0165395D02*
+X0193168Y0165395D01*
+X0193168Y0166593D02*
+X0209231Y0166593D01*
+X0209231Y0167792D02*
+X0193168Y0167792D01*
+X0193168Y0168990D02*
+X0209231Y0168990D01*
+X0209531Y0170189D02*
+X0193168Y0170189D01*
+X0193168Y0171387D02*
+X0210729Y0171387D01*
+X0211756Y0172586D02*
+X0193168Y0172586D01*
+X0193168Y0173784D02*
+X0210558Y0173784D01*
+X0209359Y0174983D02*
+X0193168Y0174983D01*
+X0193168Y0176181D02*
+X0209231Y0176181D01*
+X0209231Y0177380D02*
+X0193168Y0177380D01*
+X0193168Y0178578D02*
+X0209231Y0178578D01*
+X0209231Y0179777D02*
+X0193168Y0179777D01*
+X0193168Y0180975D02*
+X0210317Y0180975D01*
+X0211516Y0182174D02*
+X0193168Y0182174D01*
+X0193168Y0183372D02*
+X0286132Y0183372D01*
+X0286132Y0182174D02*
+X0228484Y0182174D01*
+X0229683Y0180975D02*
+X0243264Y0180975D01*
+X0243662Y0181373D02*
+X0242377Y0180088D01*
+X0241681Y0178409D01*
+X0241681Y0176591D01*
+X0242377Y0174912D01*
+X0242481Y0174808D01*
+X0242481Y0160097D01*
+X0242039Y0159914D01*
+X0241317Y0159191D01*
+X0240926Y0158247D01*
+X0240926Y0151713D01*
+X0241317Y0150769D01*
+X0242039Y0150047D01*
+X0242983Y0149656D01*
+X0249517Y0149656D01*
+X0250461Y0150047D01*
+X0251183Y0150769D01*
+X0251574Y0151713D01*
+X0251574Y0158247D01*
+X0251183Y0159191D01*
+X0250461Y0159914D01*
+X0250018Y0160097D01*
+X0250018Y0174808D01*
+X0250123Y0174912D01*
+X0250818Y0176591D01*
+X0250818Y0178409D01*
+X0250123Y0180088D01*
+X0248838Y0181373D01*
+X0247159Y0182068D01*
+X0245341Y0182068D01*
+X0243662Y0181373D01*
+X0242248Y0179777D02*
+X0230768Y0179777D01*
+X0230768Y0178578D02*
+X0241752Y0178578D01*
+X0241681Y0177380D02*
+X0230768Y0177380D01*
+X0230768Y0176181D02*
+X0241851Y0176181D01*
+X0242348Y0174983D02*
+X0230641Y0174983D01*
+X0229442Y0173784D02*
+X0242481Y0173784D01*
+X0242481Y0172586D02*
+X0228244Y0172586D01*
+X0228325Y0171387D02*
+X0242481Y0171387D01*
+X0242481Y0170189D02*
+X0229524Y0170189D01*
+X0230100Y0168990D02*
+X0242481Y0168990D01*
+X0242481Y0167792D02*
+X0225400Y0167792D01*
+X0225400Y0167100D02*
+X0225400Y0162400D01*
+X0225400Y0157900D01*
+X0224600Y0157900D01*
+X0224600Y0167100D01*
+X0225400Y0167100D01*
+X0225400Y0166593D02*
+X0224600Y0166593D01*
+X0224600Y0165395D02*
+X0225400Y0165395D01*
+X0225400Y0164196D02*
+X0224600Y0164196D01*
+X0224600Y0162998D02*
+X0225400Y0162998D01*
+X0225400Y0161799D02*
+X0224600Y0161799D01*
+X0224600Y0160601D02*
+X0225400Y0160601D01*
+X0225400Y0159402D02*
+X0224600Y0159402D01*
+X0224600Y0158203D02*
+X0225400Y0158203D01*
+X0227913Y0161799D02*
+X0234080Y0161799D01*
+X0234144Y0161482D02*
+X0234407Y0160847D01*
+X0234789Y0160275D01*
+X0235275Y0159789D01*
+X0235847Y0159407D01*
+X0236482Y0159144D01*
+X0237156Y0159009D01*
+X0237500Y0159009D01*
+X0237844Y0159009D01*
+X0238518Y0159144D01*
+X0239153Y0159407D01*
+X0239725Y0159789D01*
+X0240211Y0160275D01*
+X0240593Y0160847D01*
+X0240856Y0161482D01*
+X0240991Y0162156D01*
+X0240991Y0162500D01*
+X0240991Y0162844D01*
+X0240856Y0163518D01*
+X0240593Y0164153D01*
+X0240211Y0164725D01*
+X0239725Y0165211D01*
+X0239153Y0165593D01*
+X0238518Y0165856D01*
+X0237844Y0165991D01*
+X0237500Y0165991D01*
+X0237156Y0165991D01*
+X0236482Y0165856D01*
+X0235847Y0165593D01*
+X0235275Y0165211D01*
+X0234789Y0164725D01*
+X0234407Y0164153D01*
+X0234144Y0163518D01*
+X0234009Y0162844D01*
+X0234009Y0162500D01*
+X0234009Y0162156D01*
+X0234144Y0161482D01*
+X0234571Y0160601D02*
+X0229112Y0160601D01*
+X0230100Y0159402D02*
+X0235858Y0159402D01*
+X0237500Y0159402D02*
+X0237500Y0159402D01*
+X0237500Y0159009D02*
+X0237500Y0162500D01*
+X0240991Y0162500D01*
+X0237500Y0162500D01*
+X0237500Y0162500D01*
+X0237500Y0162500D01*
+X0237500Y0165991D01*
+X0237500Y0162500D01*
+X0237500Y0162500D01*
+X0237500Y0162500D01*
+X0234009Y0162500D01*
+X0237500Y0162500D01*
+X0237500Y0159009D01*
+X0237500Y0160601D02*
+X0237500Y0160601D01*
+X0237500Y0161799D02*
+X0237500Y0161799D01*
+X0237500Y0162998D02*
+X0237500Y0162998D01*
+X0237500Y0164196D02*
+X0237500Y0164196D01*
+X0237500Y0165395D02*
+X0237500Y0165395D01*
+X0239451Y0165395D02*
+X0242481Y0165395D01*
+X0242481Y0166593D02*
+X0230100Y0166593D01*
+X0230100Y0165395D02*
+X0235549Y0165395D01*
+X0234435Y0164196D02*
+X0228909Y0164196D01*
+X0227710Y0162998D02*
+X0234040Y0162998D01*
+X0230100Y0158203D02*
+X0240926Y0158203D01*
+X0240926Y0157005D02*
+X0230100Y0157005D01*
+X0230100Y0155806D02*
+X0240926Y0155806D01*
+X0240926Y0154608D02*
+X0229320Y0154608D01*
+X0228122Y0153409D02*
+X0240926Y0153409D01*
+X0240926Y0152211D02*
+X0228447Y0152211D01*
+X0229645Y0151012D02*
+X0241216Y0151012D01*
+X0242602Y0149814D02*
+X0230768Y0149814D01*
+X0230768Y0148615D02*
+X0269765Y0148615D01*
+X0269529Y0148852D02*
+X0270152Y0148229D01*
+X0270865Y0147710D01*
+X0271650Y0147310D01*
+X0272489Y0147038D01*
+X0273359Y0146900D01*
+X0277000Y0146900D01*
+X0277000Y0152000D01*
+X0268209Y0152000D01*
+X0268338Y0151189D01*
+X0268610Y0150350D01*
+X0269010Y0149565D01*
+X0269529Y0148852D01*
+X0268884Y0149814D02*
+X0261148Y0149814D01*
+X0262534Y0151012D02*
+X0268395Y0151012D01*
+X0268274Y0153409D02*
+X0262824Y0153409D01*
+X0262824Y0152211D02*
+X0277000Y0152211D01*
+X0277000Y0151012D02*
+X0278000Y0151012D01*
+X0278000Y0149814D02*
+X0277000Y0149814D01*
+X0277000Y0148615D02*
+X0278000Y0148615D01*
+X0278000Y0147417D02*
+X0277000Y0147417D01*
+X0271441Y0147417D02*
+X0230768Y0147417D01*
+X0230768Y0146218D02*
+X0286132Y0146218D01*
+X0286132Y0145020D02*
+X0261550Y0145020D01*
+X0262603Y0143821D02*
+X0286132Y0143821D01*
+X0286132Y0142623D02*
+X0265207Y0142623D01*
+X0257500Y0140020D02*
+X0257500Y0140000D01*
+X0252397Y0143821D02*
+X0251353Y0143821D01*
+X0250300Y0145020D02*
+X0253450Y0145020D01*
+X0253852Y0149814D02*
+X0249898Y0149814D01*
+X0251284Y0151012D02*
+X0252466Y0151012D01*
+X0252176Y0152211D02*
+X0251574Y0152211D01*
+X0251574Y0153409D02*
+X0252176Y0153409D01*
+X0252176Y0154608D02*
+X0251574Y0154608D01*
+X0251574Y0155806D02*
+X0252176Y0155806D01*
+X0252176Y0157005D02*
+X0251574Y0157005D01*
+X0251574Y0158203D02*
+X0252176Y0158203D01*
+X0252777Y0159402D02*
+X0250973Y0159402D01*
+X0250018Y0160601D02*
+X0255271Y0160601D01*
+X0256470Y0161799D02*
+X0250018Y0161799D01*
+X0250018Y0162998D02*
+X0257668Y0162998D01*
+X0258867Y0164196D02*
+X0250018Y0164196D01*
+X0250018Y0165395D02*
+X0260065Y0165395D01*
+X0263533Y0158203D02*
+X0269231Y0158203D01*
+X0270473Y0157005D02*
+X0262824Y0157005D01*
+X0262824Y0155806D02*
+X0269280Y0155806D01*
+X0268597Y0154608D02*
+X0262824Y0154608D01*
+X0269028Y0166593D02*
+X0250018Y0166593D01*
+X0250018Y0167792D02*
+X0270227Y0167792D01*
+X0284773Y0167792D02*
+X0286132Y0167792D01*
+X0286132Y0168990D02*
+X0250018Y0168990D01*
+X0250018Y0170189D02*
+X0286132Y0170189D01*
+X0286132Y0171387D02*
+X0250018Y0171387D01*
+X0250018Y0172586D02*
+X0286132Y0172586D01*
+X0286132Y0173784D02*
+X0250018Y0173784D01*
+X0250152Y0174983D02*
+X0286132Y0174983D01*
+X0286132Y0176181D02*
+X0250649Y0176181D01*
+X0250818Y0177380D02*
+X0286132Y0177380D01*
+X0286132Y0178578D02*
+X0250748Y0178578D01*
+X0250252Y0179777D02*
+X0286132Y0179777D01*
+X0286132Y0180975D02*
+X0249236Y0180975D01*
+X0253853Y0198190D02*
+X0254336Y0198061D01*
+X0260403Y0198061D01*
+X0260403Y0201643D01*
+X0261369Y0201643D01*
+X0261369Y0202609D01*
+X0269085Y0202609D01*
+X0269085Y0204541D01*
+X0268956Y0205025D01*
+X0268705Y0205458D01*
+X0268352Y0205812D01*
+X0267918Y0206062D01*
+X0267435Y0206191D01*
+X0261369Y0206191D01*
+X0261369Y0202609D01*
+X0260403Y0202609D01*
+X0260403Y0206191D01*
+X0254336Y0206191D01*
+X0253853Y0206062D01*
+X0253420Y0205812D01*
+X0253066Y0205458D01*
+X0252816Y0205025D01*
+X0252687Y0204541D01*
+X0252687Y0202609D01*
+X0260403Y0202609D01*
+X0260403Y0201643D01*
+X0252687Y0201643D01*
+X0252687Y0199710D01*
+X0252816Y0199227D01*
+X0253066Y0198794D01*
+X0253420Y0198440D01*
+X0253853Y0198190D01*
+X0252974Y0198953D02*
+X0242025Y0198953D01*
+X0241934Y0198794D02*
+X0242184Y0199227D01*
+X0242313Y0199710D01*
+X0242313Y0201643D01*
+X0234597Y0201643D01*
+X0234597Y0198061D01*
+X0240664Y0198061D01*
+X0241147Y0198190D01*
+X0241580Y0198440D01*
+X0241934Y0198794D01*
+X0242313Y0200151D02*
+X0252687Y0200151D01*
+X0252687Y0201350D02*
+X0242313Y0201350D01*
+X0242313Y0202609D02*
+X0242313Y0204541D01*
+X0242184Y0205025D01*
+X0241934Y0205458D01*
+X0241580Y0205812D01*
+X0241147Y0206062D01*
+X0240664Y0206191D01*
+X0234597Y0206191D01*
+X0234597Y0202609D01*
+X0233631Y0202609D01*
+X0233631Y0201643D01*
+X0225915Y0201643D01*
+X0225915Y0199710D01*
+X0226044Y0199227D01*
+X0226295Y0198794D01*
+X0226648Y0198440D01*
+X0227082Y0198190D01*
+X0227565Y0198061D01*
+X0233631Y0198061D01*
+X0233631Y0201643D01*
+X0234597Y0201643D01*
+X0234597Y0202609D01*
+X0242313Y0202609D01*
+X0242313Y0203747D02*
+X0252687Y0203747D01*
+X0252795Y0204945D02*
+X0242205Y0204945D01*
+X0240840Y0206144D02*
+X0254160Y0206144D01*
+X0260403Y0206144D02*
+X0261369Y0206144D01*
+X0261369Y0204945D02*
+X0260403Y0204945D01*
+X0260403Y0203747D02*
+X0261369Y0203747D01*
+X0261369Y0202548D02*
+X0286132Y0202548D01*
+X0286132Y0201350D02*
+X0269085Y0201350D01*
+X0269085Y0201643D02*
+X0269085Y0199710D01*
+X0268956Y0199227D01*
+X0268705Y0198794D01*
+X0268352Y0198440D01*
+X0267918Y0198190D01*
+X0267435Y0198061D01*
+X0261369Y0198061D01*
+X0261369Y0201643D01*
+X0269085Y0201643D01*
+X0270000Y0202126D02*
+X0275000Y0207126D01*
+X0275000Y0207500D01*
+X0274400Y0207342D02*
+X0275600Y0207342D01*
+X0275600Y0207963D02*
+X0275600Y0204104D01*
+X0278400Y0204104D01*
+X0278883Y0204233D01*
+X0279316Y0204484D01*
+X0279670Y0204837D01*
+X0279920Y0205271D01*
+X0280050Y0205754D01*
+X0280050Y0207963D01*
+X0275600Y0207963D01*
+X0275600Y0208541D02*
+X0286132Y0208541D01*
+X0286132Y0209739D02*
+X0280050Y0209739D01*
+X0280050Y0210938D02*
+X0286132Y0210938D01*
+X0286132Y0212137D02*
+X0280041Y0212137D01*
+X0280705Y0213335D02*
+X0286132Y0213335D01*
+X0286132Y0214534D02*
+X0280718Y0214534D01*
+X0280718Y0215732D02*
+X0286132Y0215732D01*
+X0286132Y0216931D02*
+X0280718Y0216931D01*
+X0280718Y0218129D02*
+X0286132Y0218129D01*
+X0286132Y0219328D02*
+X0280718Y0219328D01*
+X0280252Y0220526D02*
+X0286132Y0220526D01*
+X0286132Y0221725D02*
+X0269132Y0221725D01*
+X0275000Y0217874D02*
+X0260886Y0217874D01*
+X0234114Y0217874D01*
+X0234114Y0222500D01*
+X0190000Y0222500D01*
+X0185000Y0217500D01*
+X0180000Y0212500D01*
+X0180000Y0192500D01*
+X0175000Y0187500D01*
+X0127500Y0187500D01*
+X0122500Y0182500D01*
+X0122500Y0157500D01*
+X0117500Y0152500D01*
+X0107421Y0152500D01*
+X0103484Y0148563D01*
+X0103484Y0117441D01*
+X0101516Y0117441D02*
+X0101516Y0151063D01*
+X0105453Y0155000D01*
+X0113750Y0155000D01*
+X0118750Y0160000D01*
+X0118750Y0187500D01*
+X0123750Y0192500D01*
+X0146902Y0192500D01*
+X0151902Y0197500D01*
+X0151902Y0205000D01*
+X0146183Y0204945D02*
+X0143817Y0204945D01*
+X0143817Y0203747D02*
+X0146183Y0203747D01*
+X0146183Y0202548D02*
+X0143817Y0202548D01*
+X0143817Y0201350D02*
+X0146183Y0201350D01*
+X0146510Y0200151D02*
+X0143490Y0200151D01*
+X0141929Y0198953D02*
+X0148071Y0198953D01*
+X0147675Y0197754D02*
+X0130070Y0197754D01*
+X0129940Y0196556D02*
+X0146476Y0196556D01*
+X0150644Y0191762D02*
+X0168696Y0191762D01*
+X0168982Y0191644D02*
+X0169656Y0191509D01*
+X0170000Y0191509D01*
+X0170344Y0191509D01*
+X0171018Y0191644D01*
+X0171653Y0191907D01*
+X0172225Y0192289D01*
+X0172711Y0192775D01*
+X0173093Y0193347D01*
+X0173356Y0193982D01*
+X0173491Y0194656D01*
+X0173491Y0195000D01*
+X0173491Y0195344D01*
+X0173356Y0196018D01*
+X0173093Y0196653D01*
+X0172711Y0197225D01*
+X0172225Y0197711D01*
+X0171653Y0198093D01*
+X0171018Y0198356D01*
+X0170344Y0198491D01*
+X0170000Y0198491D01*
+X0169656Y0198491D01*
+X0168982Y0198356D01*
+X0168347Y0198093D01*
+X0167775Y0197711D01*
+X0167289Y0197225D01*
+X0166907Y0196653D01*
+X0166644Y0196018D01*
+X0166509Y0195344D01*
+X0166509Y0195000D01*
+X0166509Y0194656D01*
+X0166644Y0193982D01*
+X0166907Y0193347D01*
+X0167289Y0192775D01*
+X0167775Y0192289D01*
+X0168347Y0191907D01*
+X0168982Y0191644D01*
+X0170000Y0191762D02*
+X0170000Y0191762D01*
+X0170000Y0191509D02*
+X0170000Y0195000D01*
+X0173491Y0195000D01*
+X0170000Y0195000D01*
+X0170000Y0195000D01*
+X0170000Y0195000D01*
+X0170000Y0198491D01*
+X0170000Y0195000D01*
+X0170000Y0195000D01*
+X0170000Y0195000D01*
+X0166509Y0195000D01*
+X0170000Y0195000D01*
+X0170000Y0191509D01*
+X0171304Y0191762D02*
+X0174781Y0191762D01*
+X0175979Y0192960D02*
+X0172835Y0192960D01*
+X0173392Y0194159D02*
+X0176831Y0194159D01*
+X0176831Y0195357D02*
+X0173488Y0195357D01*
+X0173134Y0196556D02*
+X0176831Y0196556D01*
+X0176831Y0197754D02*
+X0172161Y0197754D01*
+X0170000Y0197754D02*
+X0170000Y0197754D01*
+X0170000Y0196556D02*
+X0170000Y0196556D01*
+X0170000Y0195357D02*
+X0170000Y0195357D01*
+X0170000Y0194159D02*
+X0170000Y0194159D01*
+X0170000Y0192960D02*
+X0170000Y0192960D01*
+X0167165Y0192960D02*
+X0151843Y0192960D01*
+X0153041Y0194159D02*
+X0166608Y0194159D01*
+X0166512Y0195357D02*
+X0154240Y0195357D01*
+X0154940Y0196556D02*
+X0166866Y0196556D01*
+X0167839Y0197754D02*
+X0155070Y0197754D01*
+X0155733Y0198953D02*
+X0159267Y0198953D01*
+X0157707Y0200151D02*
+X0157293Y0200151D01*
+X0163098Y0205000D02*
+X0163098Y0217500D01*
+X0161240Y0217500D01*
+X0166267Y0213335D02*
+X0176916Y0213335D01*
+X0176831Y0212137D02*
+X0166267Y0212137D01*
+X0167193Y0210938D02*
+X0176831Y0210938D01*
+X0176831Y0209739D02*
+X0168535Y0209739D01*
+X0168817Y0208541D02*
+X0176831Y0208541D01*
+X0176831Y0207342D02*
+X0168817Y0207342D01*
+X0168817Y0206144D02*
+X0176831Y0206144D01*
+X0176831Y0204945D02*
+X0168817Y0204945D01*
+X0168817Y0203747D02*
+X0176831Y0203747D01*
+X0176831Y0202548D02*
+X0168817Y0202548D01*
+X0168817Y0201350D02*
+X0176831Y0201350D01*
+X0176831Y0200151D02*
+X0168490Y0200151D01*
+X0166929Y0198953D02*
+X0176831Y0198953D01*
+X0183168Y0198953D02*
+X0186831Y0198953D01*
+X0186831Y0200151D02*
+X0183168Y0200151D01*
+X0183168Y0201350D02*
+X0186831Y0201350D01*
+X0186831Y0202400D02*
+X0186831Y0161312D01*
+X0184455Y0158936D01*
+X0184455Y0158936D01*
+X0183564Y0158045D01*
+X0183081Y0156880D01*
+X0183081Y0147562D01*
+X0182039Y0146520D01*
+X0182068Y0146591D01*
+X0182068Y0148409D01*
+X0181373Y0150088D01*
+X0180088Y0151373D01*
+X0178409Y0152068D01*
+X0176591Y0152068D01*
+X0174912Y0151373D01*
+X0174208Y0150668D01*
+X0120149Y0150668D01*
+X0120186Y0150705D01*
+X0125186Y0155705D01*
+X0125668Y0156870D01*
+X0125668Y0181188D01*
+X0128812Y0184331D01*
+X0175630Y0184331D01*
+X0176795Y0184814D01*
+X0177686Y0185705D01*
+X0182686Y0190705D01*
+X0183168Y0191870D01*
+X0183168Y0202400D01*
+X0184600Y0202400D01*
+X0184600Y0207100D01*
+X0185400Y0207100D01*
+X0185400Y0202400D01*
+X0186831Y0202400D01*
+X0185400Y0202548D02*
+X0184600Y0202548D01*
+X0184600Y0203747D02*
+X0185400Y0203747D01*
+X0185400Y0204945D02*
+X0184600Y0204945D01*
+X0184600Y0206144D02*
+X0185400Y0206144D01*
+X0190000Y0212500D02*
+X0190000Y0160000D01*
+X0186250Y0156250D01*
+X0186250Y0146250D01*
+X0182500Y0142500D01*
+X0115295Y0142500D01*
+X0111358Y0138563D01*
+X0111358Y0117441D01*
+X0105453Y0117441D02*
+X0105453Y0143563D01*
+X0109390Y0147500D01*
+X0177500Y0147500D01*
+X0182068Y0147417D02*
+X0182936Y0147417D01*
+X0183081Y0148615D02*
+X0181983Y0148615D01*
+X0181486Y0149814D02*
+X0183081Y0149814D01*
+X0183081Y0151012D02*
+X0180448Y0151012D01*
+X0183081Y0152211D02*
+X0121692Y0152211D01*
+X0122890Y0153409D02*
+X0183081Y0153409D01*
+X0183081Y0154608D02*
+X0124089Y0154608D01*
+X0125228Y0155806D02*
+X0183081Y0155806D01*
+X0183133Y0157005D02*
+X0125668Y0157005D01*
+X0125668Y0158203D02*
+X0183723Y0158203D01*
+X0184921Y0159402D02*
+X0125668Y0159402D01*
+X0125668Y0160601D02*
+X0186120Y0160601D01*
+X0186831Y0161799D02*
+X0125668Y0161799D01*
+X0125668Y0162998D02*
+X0186831Y0162998D01*
+X0186831Y0164196D02*
+X0125668Y0164196D01*
+X0125668Y0165395D02*
+X0186831Y0165395D01*
+X0186831Y0166593D02*
+X0125668Y0166593D01*
+X0125668Y0167792D02*
+X0186831Y0167792D01*
+X0186831Y0168990D02*
+X0125668Y0168990D01*
+X0125668Y0170189D02*
+X0186831Y0170189D01*
+X0186831Y0171387D02*
+X0125668Y0171387D01*
+X0125668Y0172586D02*
+X0149978Y0172586D01*
+X0149789Y0172775D02*
+X0150275Y0172289D01*
+X0150847Y0171907D01*
+X0151482Y0171644D01*
+X0152156Y0171509D01*
+X0152500Y0171509D01*
+X0152844Y0171509D01*
+X0153518Y0171644D01*
+X0154153Y0171907D01*
+X0154725Y0172289D01*
+X0155211Y0172775D01*
+X0155593Y0173347D01*
+X0155856Y0173982D01*
+X0155991Y0174656D01*
+X0155991Y0175000D01*
+X0155991Y0175344D01*
+X0155856Y0176018D01*
+X0155593Y0176653D01*
+X0155211Y0177225D01*
+X0154725Y0177711D01*
+X0154153Y0178093D01*
+X0153518Y0178356D01*
+X0152844Y0178491D01*
+X0152500Y0178491D01*
+X0152156Y0178491D01*
+X0151482Y0178356D01*
+X0150847Y0178093D01*
+X0150275Y0177711D01*
+X0149789Y0177225D01*
+X0149407Y0176653D01*
+X0149144Y0176018D01*
+X0149009Y0175344D01*
+X0149009Y0175000D01*
+X0149009Y0174656D01*
+X0149144Y0173982D01*
+X0149407Y0173347D01*
+X0149789Y0172775D01*
+X0149225Y0173784D02*
+X0125668Y0173784D01*
+X0125668Y0174983D02*
+X0149009Y0174983D01*
+X0149009Y0175000D02*
+X0152500Y0175000D01*
+X0155991Y0175000D01*
+X0152500Y0175000D01*
+X0152500Y0175000D01*
+X0152500Y0175000D01*
+X0152500Y0178491D01*
+X0152500Y0175000D01*
+X0152500Y0171509D01*
+X0152500Y0175000D01*
+X0152500Y0175000D01*
+X0152500Y0175000D01*
+X0149009Y0175000D01*
+X0149211Y0176181D02*
+X0125668Y0176181D01*
+X0125668Y0177380D02*
+X0149943Y0177380D01*
+X0152500Y0177380D02*
+X0152500Y0177380D01*
+X0152500Y0176181D02*
+X0152500Y0176181D01*
+X0152500Y0174983D02*
+X0152500Y0174983D01*
+X0152500Y0173784D02*
+X0152500Y0173784D01*
+X0152500Y0172586D02*
+X0152500Y0172586D01*
+X0155022Y0172586D02*
+X0186831Y0172586D01*
+X0186831Y0173784D02*
+X0155775Y0173784D01*
+X0155991Y0174983D02*
+X0186831Y0174983D01*
+X0186831Y0176181D02*
+X0155789Y0176181D01*
+X0155057Y0177380D02*
+X0186831Y0177380D01*
+X0186831Y0178578D02*
+X0125668Y0178578D01*
+X0125668Y0179777D02*
+X0186831Y0179777D01*
+X0186831Y0180975D02*
+X0125668Y0180975D01*
+X0126655Y0182174D02*
+X0186831Y0182174D01*
+X0186831Y0183372D02*
+X0127853Y0183372D01*
+X0114402Y0192500D02*
+X0119402Y0197500D01*
+X0126902Y0197500D01*
+X0126902Y0205000D01*
+X0121183Y0204945D02*
+X0118817Y0204945D01*
+X0118817Y0203747D02*
+X0121183Y0203747D01*
+X0121183Y0202548D02*
+X0118817Y0202548D01*
+X0118817Y0201350D02*
+X0121183Y0201350D01*
+X0121183Y0206144D02*
+X0118817Y0206144D01*
+X0118817Y0207342D02*
+X0121183Y0207342D01*
+X0121183Y0208541D02*
+X0118817Y0208541D01*
+X0118535Y0209739D02*
+X0121465Y0209739D01*
+X0122807Y0210938D02*
+X0117193Y0210938D01*
+X0116267Y0212137D02*
+X0132789Y0212137D01*
+X0134004Y0210938D02*
+X0130996Y0210938D01*
+X0132338Y0209739D02*
+X0132662Y0209739D01*
+X0128160Y0212647D02*
+X0128160Y0216900D01*
+X0124301Y0216900D01*
+X0124301Y0214297D01*
+X0124430Y0213814D01*
+X0124680Y0213381D01*
+X0125034Y0213027D01*
+X0125467Y0212777D01*
+X0125951Y0212647D01*
+X0128160Y0212647D01*
+X0128160Y0213335D02*
+X0129360Y0213335D01*
+X0129360Y0214534D02*
+X0128160Y0214534D01*
+X0128160Y0215732D02*
+X0129360Y0215732D01*
+X0128160Y0216931D02*
+X0116368Y0216931D01*
+X0116368Y0218129D02*
+X0124301Y0218129D01*
+X0124301Y0219328D02*
+X0116368Y0219328D01*
+X0116368Y0220526D02*
+X0124301Y0220526D01*
+X0124786Y0221725D02*
+X0116053Y0221725D01*
+X0114547Y0222923D02*
+X0132933Y0222923D01*
+X0129360Y0221725D02*
+X0128160Y0221725D01*
+X0128160Y0220526D02*
+X0129360Y0220526D01*
+X0129360Y0219328D02*
+X0128160Y0219328D01*
+X0128160Y0218129D02*
+X0129360Y0218129D01*
+X0124301Y0215732D02*
+X0116368Y0215732D01*
+X0116368Y0214534D02*
+X0124301Y0214534D01*
+X0124726Y0213335D02*
+X0116267Y0213335D01*
+X0113098Y0217500D02*
+X0111240Y0217500D01*
+X0113098Y0217500D02*
+X0113098Y0205000D01*
+X0107662Y0209739D02*
+X0107338Y0209739D01*
+X0105996Y0210938D02*
+X0109004Y0210938D01*
+X0107789Y0212137D02*
+X0082344Y0212137D01*
+X0081146Y0213335D02*
+X0099726Y0213335D01*
+X0099680Y0213381D02*
+X0100034Y0213027D01*
+X0100467Y0212777D01*
+X0100951Y0212647D01*
+X0103160Y0212647D01*
+X0103160Y0216900D01*
+X0099301Y0216900D01*
+X0099301Y0214297D01*
+X0099430Y0213814D01*
+X0099680Y0213381D01*
+X0099301Y0214534D02*
+X0079947Y0214534D01*
+X0077500Y0212500D02*
+X0082500Y0207500D01*
+X0082500Y0205000D01*
+X0101902Y0205000D01*
+X0096183Y0201831D02*
+X0096183Y0200940D01*
+X0096296Y0200668D01*
+X0053812Y0200668D01*
+X0052081Y0202400D01*
+X0052100Y0202400D01*
+X0052100Y0207100D01*
+X0052900Y0207100D01*
+X0052900Y0207900D01*
+X0057600Y0207900D01*
+X0062100Y0207900D01*
+X0062100Y0207100D01*
+X0052900Y0207100D01*
+X0052900Y0202400D01*
+X0054612Y0202400D01*
+X0057500Y0205288D01*
+X0060388Y0202400D01*
+X0062100Y0202400D01*
+X0062100Y0207100D01*
+X0062900Y0207100D01*
+X0062900Y0207900D01*
+X0067600Y0207900D01*
+X0067600Y0209331D01*
+X0076188Y0209331D01*
+X0079331Y0206188D01*
+X0079331Y0204370D01*
+X0079814Y0203205D01*
+X0080705Y0202314D01*
+X0081870Y0201831D01*
+X0096183Y0201831D01*
+X0096183Y0201350D02*
+X0053131Y0201350D01*
+X0052900Y0202548D02*
+X0052100Y0202548D01*
+X0052100Y0203747D02*
+X0052900Y0203747D01*
+X0052900Y0204945D02*
+X0052100Y0204945D01*
+X0052100Y0206144D02*
+X0052900Y0206144D01*
+X0052900Y0207342D02*
+X0062100Y0207342D01*
+X0062900Y0207342D02*
+X0078177Y0207342D01*
+X0079331Y0206144D02*
+X0067600Y0206144D01*
+X0067600Y0205388D02*
+X0067600Y0207100D01*
+X0062900Y0207100D01*
+X0062900Y0202400D01*
+X0064612Y0202400D01*
+X0067600Y0205388D01*
+X0067158Y0204945D02*
+X0079331Y0204945D01*
+X0079589Y0203747D02*
+X0065959Y0203747D01*
+X0064761Y0202548D02*
+X0080471Y0202548D01*
+X0076978Y0208541D02*
+X0067600Y0208541D01*
+X0062900Y0206144D02*
+X0062100Y0206144D01*
+X0062100Y0204945D02*
+X0062900Y0204945D01*
+X0062900Y0203747D02*
+X0062100Y0203747D01*
+X0062100Y0202548D02*
+X0062900Y0202548D01*
+X0060239Y0202548D02*
+X0054761Y0202548D01*
+X0055959Y0203747D02*
+X0059041Y0203747D01*
+X0057842Y0204945D02*
+X0057158Y0204945D01*
+X0057500Y0212500D02*
+X0077500Y0212500D01*
+X0083543Y0210938D02*
+X0097807Y0210938D01*
+X0096465Y0209739D02*
+X0084741Y0209739D01*
+X0085498Y0208541D02*
+X0096183Y0208541D01*
+X0103160Y0213335D02*
+X0104360Y0213335D01*
+X0104360Y0214534D02*
+X0103160Y0214534D01*
+X0103160Y0215732D02*
+X0104360Y0215732D01*
+X0103160Y0216931D02*
+X0068268Y0216931D01*
+X0068268Y0218129D02*
+X0099301Y0218129D01*
+X0099301Y0219328D02*
+X0068268Y0219328D01*
+X0067632Y0220526D02*
+X0099301Y0220526D01*
+X0099786Y0221725D02*
+X0066433Y0221725D01*
+X0065235Y0222923D02*
+X0107933Y0222923D01*
+X0104360Y0221725D02*
+X0103160Y0221725D01*
+X0103160Y0220526D02*
+X0104360Y0220526D01*
+X0104360Y0219328D02*
+X0103160Y0219328D01*
+X0103160Y0218129D02*
+X0104360Y0218129D01*
+X0099301Y0215732D02*
+X0068268Y0215732D01*
+X0062500Y0217500D02*
+X0055000Y0225000D01*
+X0016437Y0225000D01*
+X0012500Y0221063D01*
+X0012500Y0092500D01*
+X0017500Y0087500D01*
+X0027500Y0087500D01*
+X0027500Y0078740D01*
+X0021979Y0079102D02*
+X0002568Y0079102D01*
+X0002568Y0080300D02*
+X0021979Y0080300D01*
+X0021979Y0081499D02*
+X0002568Y0081499D01*
+X0002568Y0082697D02*
+X0022346Y0082697D01*
+X0024331Y0083896D02*
+X0002568Y0083896D01*
+X0002568Y0085094D02*
+X0015425Y0085094D01*
+X0014226Y0086293D02*
+X0002568Y0086293D01*
+X0002568Y0087491D02*
+X0013028Y0087491D01*
+X0011829Y0088690D02*
+X0002568Y0088690D01*
+X0002568Y0089888D02*
+X0010631Y0089888D01*
+X0009656Y0091087D02*
+X0002568Y0091087D01*
+X0002568Y0092285D02*
+X0009331Y0092285D01*
+X0009331Y0093484D02*
+X0002568Y0093484D01*
+X0002568Y0094682D02*
+X0009331Y0094682D01*
+X0009331Y0095881D02*
+X0002568Y0095881D01*
+X0002568Y0097079D02*
+X0009331Y0097079D01*
+X0009331Y0098278D02*
+X0002568Y0098278D01*
+X0002568Y0099476D02*
+X0009331Y0099476D01*
+X0009331Y0100675D02*
+X0002568Y0100675D01*
+X0002568Y0101873D02*
+X0009331Y0101873D01*
+X0009331Y0103072D02*
+X0002568Y0103072D01*
+X0002568Y0104270D02*
+X0009331Y0104270D01*
+X0009331Y0105469D02*
+X0002568Y0105469D01*
+X0002568Y0106668D02*
+X0009331Y0106668D01*
+X0009331Y0107866D02*
+X0002568Y0107866D01*
+X0002568Y0109065D02*
+X0009331Y0109065D01*
+X0009331Y0110263D02*
+X0002568Y0110263D01*
+X0002568Y0111462D02*
+X0009331Y0111462D01*
+X0009331Y0112660D02*
+X0002568Y0112660D01*
+X0002568Y0113859D02*
+X0009331Y0113859D01*
+X0009331Y0115057D02*
+X0002568Y0115057D01*
+X0002568Y0116256D02*
+X0009331Y0116256D01*
+X0009331Y0117454D02*
+X0002568Y0117454D01*
+X0002568Y0118653D02*
+X0009331Y0118653D01*
+X0009331Y0119851D02*
+X0002568Y0119851D01*
+X0002568Y0121050D02*
+X0009331Y0121050D01*
+X0009331Y0122248D02*
+X0002568Y0122248D01*
+X0002568Y0123447D02*
+X0009331Y0123447D01*
+X0009331Y0124645D02*
+X0002568Y0124645D01*
+X0002568Y0125844D02*
+X0009331Y0125844D01*
+X0009331Y0127042D02*
+X0002568Y0127042D01*
+X0002568Y0128241D02*
+X0009331Y0128241D01*
+X0009331Y0129439D02*
+X0002568Y0129439D01*
+X0002568Y0130638D02*
+X0009331Y0130638D01*
+X0009331Y0131836D02*
+X0002568Y0131836D01*
+X0002568Y0133035D02*
+X0009331Y0133035D01*
+X0009331Y0134233D02*
+X0002568Y0134233D01*
+X0002568Y0135432D02*
+X0009331Y0135432D01*
+X0009331Y0136630D02*
+X0002568Y0136630D01*
+X0002568Y0137829D02*
+X0009331Y0137829D01*
+X0009331Y0139027D02*
+X0002568Y0139027D01*
+X0002568Y0140226D02*
+X0009331Y0140226D01*
+X0009331Y0141424D02*
+X0002568Y0141424D01*
+X0002568Y0142623D02*
+X0009331Y0142623D01*
+X0009331Y0143821D02*
+X0002568Y0143821D01*
+X0002568Y0145020D02*
+X0009331Y0145020D01*
+X0009331Y0146218D02*
+X0002568Y0146218D01*
+X0002568Y0147417D02*
+X0009331Y0147417D01*
+X0009331Y0148615D02*
+X0002568Y0148615D01*
+X0002568Y0149814D02*
+X0009331Y0149814D01*
+X0009331Y0151012D02*
+X0002568Y0151012D01*
+X0002568Y0152211D02*
+X0009331Y0152211D01*
+X0009331Y0153409D02*
+X0002568Y0153409D01*
+X0002568Y0154608D02*
+X0009331Y0154608D01*
+X0009331Y0155806D02*
+X0002568Y0155806D01*
+X0002568Y0157005D02*
+X0009331Y0157005D01*
+X0009331Y0158203D02*
+X0002568Y0158203D01*
+X0002568Y0159402D02*
+X0009331Y0159402D01*
+X0009331Y0160601D02*
+X0002568Y0160601D01*
+X0002568Y0161799D02*
+X0009331Y0161799D01*
+X0009331Y0162998D02*
+X0002568Y0162998D01*
+X0002568Y0164196D02*
+X0009331Y0164196D01*
+X0009331Y0165395D02*
+X0002568Y0165395D01*
+X0002568Y0166593D02*
+X0009331Y0166593D01*
+X0009331Y0167792D02*
+X0002568Y0167792D01*
+X0002568Y0168990D02*
+X0009331Y0168990D01*
+X0009331Y0170189D02*
+X0002568Y0170189D01*
+X0002568Y0171387D02*
+X0009331Y0171387D01*
+X0009331Y0172586D02*
+X0002568Y0172586D01*
+X0002568Y0173784D02*
+X0009331Y0173784D01*
+X0009331Y0174983D02*
+X0002568Y0174983D01*
+X0002568Y0176181D02*
+X0009331Y0176181D01*
+X0009331Y0177380D02*
+X0002568Y0177380D01*
+X0002568Y0178578D02*
+X0009331Y0178578D01*
+X0009331Y0179777D02*
+X0002568Y0179777D01*
+X0002568Y0180975D02*
+X0009331Y0180975D01*
+X0009331Y0182174D02*
+X0002568Y0182174D01*
+X0002568Y0183372D02*
+X0009331Y0183372D01*
+X0009331Y0184571D02*
+X0002568Y0184571D01*
+X0002568Y0185769D02*
+X0009331Y0185769D01*
+X0009331Y0186968D02*
+X0002568Y0186968D01*
+X0002568Y0188166D02*
+X0009331Y0188166D01*
+X0009331Y0189365D02*
+X0002568Y0189365D01*
+X0002568Y0190563D02*
+X0009331Y0190563D01*
+X0009331Y0191762D02*
+X0002568Y0191762D01*
+X0002568Y0192960D02*
+X0009331Y0192960D01*
+X0009331Y0194159D02*
+X0002568Y0194159D01*
+X0002568Y0195357D02*
+X0009331Y0195357D01*
+X0009331Y0196556D02*
+X0002568Y0196556D01*
+X0002568Y0197754D02*
+X0009331Y0197754D01*
+X0009331Y0198953D02*
+X0002568Y0198953D01*
+X0002568Y0200151D02*
+X0009331Y0200151D01*
+X0009331Y0201350D02*
+X0002568Y0201350D01*
+X0002568Y0202548D02*
+X0009331Y0202548D01*
+X0009331Y0203747D02*
+X0002568Y0203747D01*
+X0002568Y0204945D02*
+X0009331Y0204945D01*
+X0009331Y0206144D02*
+X0002568Y0206144D01*
+X0002568Y0207342D02*
+X0009331Y0207342D01*
+X0009331Y0208541D02*
+X0002568Y0208541D01*
+X0002568Y0209739D02*
+X0009331Y0209739D01*
+X0009331Y0210938D02*
+X0002568Y0210938D01*
+X0002568Y0212137D02*
+X0009331Y0212137D01*
+X0009331Y0213335D02*
+X0002568Y0213335D01*
+X0002568Y0214534D02*
+X0009331Y0214534D01*
+X0009331Y0215732D02*
+X0002568Y0215732D01*
+X0002568Y0216931D02*
+X0009331Y0216931D01*
+X0009331Y0218129D02*
+X0002568Y0218129D01*
+X0002568Y0219328D02*
+X0009331Y0219328D01*
+X0009331Y0220526D02*
+X0002568Y0220526D01*
+X0002568Y0221725D02*
+X0009344Y0221725D01*
+X0009879Y0222923D02*
+X0002568Y0222923D01*
+X0002568Y0224122D02*
+X0011078Y0224122D01*
+X0012276Y0225320D02*
+X0002568Y0225320D01*
+X0002568Y0226519D02*
+X0013475Y0226519D01*
+X0018937Y0222500D02*
+X0015000Y0218563D01*
+X0015000Y0095000D01*
+X0020000Y0090000D01*
+X0068563Y0090000D01*
+X0070610Y0092047D01*
+X0080059Y0092047D01*
+X0080059Y0090079D02*
+X0072579Y0090079D01*
+X0070000Y0087500D01*
+X0027500Y0087500D01*
+X0030668Y0084331D02*
+X0057079Y0084331D01*
+X0056525Y0083961D01*
+X0056039Y0083475D01*
+X0055657Y0082903D01*
+X0055394Y0082268D01*
+X0055259Y0081594D01*
+X0055259Y0081250D01*
+X0055259Y0080906D01*
+X0055394Y0080232D01*
+X0055657Y0079597D01*
+X0056039Y0079025D01*
+X0056525Y0078539D01*
+X0057097Y0078157D01*
+X0057732Y0077894D01*
+X0058406Y0077759D01*
+X0058750Y0077759D01*
+X0059094Y0077759D01*
+X0059768Y0077894D01*
+X0060403Y0078157D01*
+X0060975Y0078539D01*
+X0061461Y0079025D01*
+X0061843Y0079597D01*
+X0062106Y0080232D01*
+X0062241Y0080906D01*
+X0062241Y0081250D01*
+X0062241Y0081594D01*
+X0062106Y0082268D01*
+X0061843Y0082903D01*
+X0061461Y0083475D01*
+X0060975Y0083961D01*
+X0060421Y0084331D01*
+X0069919Y0084331D01*
+X0063747Y0078218D01*
+X0060867Y0078218D01*
+X0059923Y0077827D01*
+X0059457Y0077361D01*
+X0059355Y0077420D01*
+X0058872Y0077550D01*
+X0056663Y0077550D01*
+X0056663Y0073100D01*
+X0055463Y0073100D01*
+X0055463Y0077550D01*
+X0053254Y0077550D01*
+X0052771Y0077420D01*
+X0052337Y0077170D01*
+X0051984Y0076816D01*
+X0051733Y0076383D01*
+X0051604Y0075900D01*
+X0051604Y0073100D01*
+X0055463Y0073100D01*
+X0055463Y0071900D01*
+X0056663Y0071900D01*
+X0056663Y0067450D01*
+X0058872Y0067450D01*
+X0059355Y0067580D01*
+X0059457Y0067639D01*
+X0059923Y0067173D01*
+X0060769Y0066823D01*
+X0060769Y0062872D01*
+X0060327Y0062689D01*
+X0059604Y0061967D01*
+X0059417Y0061515D01*
+X0032749Y0061515D01*
+X0030668Y0063596D01*
+X0030668Y0066132D01*
+X0030964Y0066132D01*
+X0031908Y0066523D01*
+X0032630Y0067246D01*
+X0033021Y0068190D01*
+X0033021Y0074330D01*
+X0032744Y0075000D01*
+X0033021Y0075670D01*
+X0033021Y0081810D01*
+X0032630Y0082754D01*
+X0031908Y0083477D01*
+X0030964Y0083868D01*
+X0030668Y0083868D01*
+X0030668Y0084331D01*
+X0030668Y0083896D02*
+X0056459Y0083896D01*
+X0055571Y0082697D02*
+X0032654Y0082697D01*
+X0033021Y0081499D02*
+X0055259Y0081499D01*
+X0055259Y0081250D02*
+X0058750Y0081250D01*
+X0062241Y0081250D01*
+X0058750Y0081250D01*
+X0058750Y0081250D01*
+X0058750Y0077759D01*
+X0058750Y0081250D01*
+X0058750Y0081250D01*
+X0058750Y0081250D01*
+X0055259Y0081250D01*
+X0055380Y0080300D02*
+X0033021Y0080300D01*
+X0033021Y0079102D02*
+X0055987Y0079102D01*
+X0057709Y0077903D02*
+X0033021Y0077903D01*
+X0033021Y0076705D02*
+X0051919Y0076705D01*
+X0051604Y0075506D02*
+X0032953Y0075506D01*
+X0033021Y0074308D02*
+X0051604Y0074308D01*
+X0051604Y0073109D02*
+X0033021Y0073109D01*
+X0033021Y0071911D02*
+X0055463Y0071911D01*
+X0055463Y0071900D02*
+X0051604Y0071900D01*
+X0051604Y0069100D01*
+X0051733Y0068617D01*
+X0051984Y0068184D01*
+X0052337Y0067830D01*
+X0052771Y0067580D01*
+X0053254Y0067450D01*
+X0055463Y0067450D01*
+X0055463Y0071900D01*
+X0055463Y0070712D02*
+X0056663Y0070712D01*
+X0056663Y0069514D02*
+X0055463Y0069514D01*
+X0055463Y0068315D02*
+X0056663Y0068315D01*
+X0060059Y0067117D02*
+X0032501Y0067117D01*
+X0033021Y0068315D02*
+X0051908Y0068315D01*
+X0051604Y0069514D02*
+X0033021Y0069514D01*
+X0033021Y0070712D02*
+X0051604Y0070712D01*
+X0055463Y0073109D02*
+X0056663Y0073109D01*
+X0056663Y0074308D02*
+X0055463Y0074308D01*
+X0055463Y0075506D02*
+X0056663Y0075506D01*
+X0056663Y0076705D02*
+X0055463Y0076705D01*
+X0058750Y0077903D02*
+X0058750Y0077903D01*
+X0059791Y0077903D02*
+X0060107Y0077903D01*
+X0058750Y0079102D02*
+X0058750Y0079102D01*
+X0058750Y0080300D02*
+X0058750Y0080300D01*
+X0061513Y0079102D02*
+X0064639Y0079102D01*
+X0065849Y0080300D02*
+X0062120Y0080300D01*
+X0062241Y0081499D02*
+X0067059Y0081499D01*
+X0068269Y0082697D02*
+X0061929Y0082697D01*
+X0062500Y0083750D02*
+X0070000Y0083750D01*
+X0074360Y0088110D01*
+X0080059Y0088110D01*
+X0080059Y0086142D02*
+X0076250Y0086142D01*
+X0065000Y0075000D01*
+X0063937Y0072500D01*
+X0063937Y0060846D01*
+X0063750Y0058346D01*
+X0031437Y0058346D01*
+X0027500Y0062283D01*
+X0027500Y0071260D01*
+X0021979Y0070712D02*
+X0002568Y0070712D01*
+X0002568Y0069514D02*
+X0021979Y0069514D01*
+X0021979Y0068315D02*
+X0002568Y0068315D01*
+X0002568Y0067117D02*
+X0022499Y0067117D01*
+X0024331Y0065918D02*
+X0002568Y0065918D01*
+X0002568Y0064720D02*
+X0024331Y0064720D01*
+X0024331Y0063521D02*
+X0002568Y0063521D01*
+X0002568Y0062323D02*
+X0024331Y0062323D01*
+X0024551Y0061124D02*
+X0002568Y0061124D01*
+X0002568Y0059926D02*
+X0025377Y0059926D01*
+X0026575Y0058727D02*
+X0002568Y0058727D01*
+X0002568Y0057529D02*
+X0027774Y0057529D01*
+X0028972Y0056330D02*
+X0002568Y0056330D01*
+X0002568Y0055132D02*
+X0059436Y0055132D01*
+X0059213Y0053933D02*
+X0002568Y0053933D01*
+X0002568Y0052734D02*
+X0059213Y0052734D01*
+X0059213Y0051536D02*
+X0002568Y0051536D01*
+X0002568Y0050337D02*
+X0059213Y0050337D01*
+X0059213Y0049139D02*
+X0002568Y0049139D01*
+X0002568Y0047940D02*
+X0059697Y0047940D01*
+X0063750Y0051654D02*
+X0082500Y0051654D01*
+X0086437Y0055591D01*
+X0086437Y0060000D01*
+X0087736Y0060000D01*
+X0091673Y0063937D01*
+X0091673Y0072559D01*
+X0089705Y0072559D02*
+X0089705Y0065000D01*
+X0077500Y0065000D01*
+X0077500Y0060000D01*
+X0077963Y0059926D02*
+X0068287Y0059926D01*
+X0068287Y0061023D02*
+X0067896Y0061967D01*
+X0067173Y0062689D01*
+X0067105Y0062717D01*
+X0067105Y0066823D01*
+X0067951Y0067173D01*
+X0068674Y0067895D01*
+X0069065Y0068839D01*
+X0069065Y0074566D01*
+X0073681Y0079139D01*
+X0073681Y0076989D01*
+X0074073Y0076045D01*
+X0074795Y0075323D01*
+X0075739Y0074931D01*
+X0076181Y0074931D01*
+X0076181Y0070739D01*
+X0076573Y0069795D01*
+X0077295Y0069073D01*
+X0078239Y0068681D01*
+X0085029Y0068681D01*
+X0085691Y0068019D01*
+X0086635Y0067628D01*
+X0088505Y0067628D01*
+X0088505Y0065718D01*
+X0083367Y0065718D01*
+X0082423Y0065327D01*
+X0081957Y0064861D01*
+X0081855Y0064920D01*
+X0081372Y0065050D01*
+X0079163Y0065050D01*
+X0079163Y0060600D01*
+X0077963Y0060600D01*
+X0077963Y0065050D01*
+X0075754Y0065050D01*
+X0075271Y0064920D01*
+X0074837Y0064670D01*
+X0074484Y0064316D01*
+X0074233Y0063883D01*
+X0074104Y0063400D01*
+X0074104Y0060600D01*
+X0077963Y0060600D01*
+X0077963Y0059400D01*
+X0079163Y0059400D01*
+X0079163Y0054950D01*
+X0081316Y0054950D01*
+X0081188Y0054822D01*
+X0068083Y0054822D01*
+X0068009Y0055000D01*
+X0068287Y0055670D01*
+X0068287Y0061023D01*
+X0068245Y0061124D02*
+X0074104Y0061124D01*
+X0074104Y0062323D02*
+X0067540Y0062323D01*
+X0067105Y0063521D02*
+X0074136Y0063521D01*
+X0073518Y0064144D02*
+X0074153Y0064407D01*
+X0074725Y0064789D01*
+X0075211Y0065275D01*
+X0075593Y0065847D01*
+X0075856Y0066482D01*
+X0075991Y0067156D01*
+X0075991Y0067500D01*
+X0075991Y0067844D01*
+X0075856Y0068518D01*
+X0075593Y0069153D01*
+X0075211Y0069725D01*
+X0074725Y0070211D01*
+X0074153Y0070593D01*
+X0073518Y0070856D01*
+X0072844Y0070991D01*
+X0072500Y0070991D01*
+X0072156Y0070991D01*
+X0071482Y0070856D01*
+X0070847Y0070593D01*
+X0070275Y0070211D01*
+X0069789Y0069725D01*
+X0069407Y0069153D01*
+X0069144Y0068518D01*
+X0069009Y0067844D01*
+X0069009Y0067500D01*
+X0069009Y0067156D01*
+X0069144Y0066482D01*
+X0069407Y0065847D01*
+X0069789Y0065275D01*
+X0070275Y0064789D01*
+X0070847Y0064407D01*
+X0071482Y0064144D01*
+X0072156Y0064009D01*
+X0072500Y0064009D01*
+X0072844Y0064009D01*
+X0073518Y0064144D01*
+X0072500Y0064009D02*
+X0072500Y0067500D01*
+X0075991Y0067500D01*
+X0072500Y0067500D01*
+X0072500Y0067500D01*
+X0072500Y0067500D01*
+X0072500Y0070991D01*
+X0072500Y0067500D01*
+X0072500Y0067500D01*
+X0072500Y0067500D01*
+X0069009Y0067500D01*
+X0072500Y0067500D01*
+X0072500Y0064009D01*
+X0072500Y0064720D02*
+X0072500Y0064720D01*
+X0072500Y0065918D02*
+X0072500Y0065918D01*
+X0072500Y0067117D02*
+X0072500Y0067117D01*
+X0072500Y0068315D02*
+X0072500Y0068315D01*
+X0072500Y0069514D02*
+X0072500Y0069514D01*
+X0072500Y0070712D02*
+X0072500Y0070712D01*
+X0073866Y0070712D02*
+X0076193Y0070712D01*
+X0076181Y0071911D02*
+X0069065Y0071911D01*
+X0069065Y0073109D02*
+X0076181Y0073109D01*
+X0076181Y0074308D02*
+X0069065Y0074308D01*
+X0070014Y0075506D02*
+X0074611Y0075506D01*
+X0073799Y0076705D02*
+X0071224Y0076705D01*
+X0072434Y0077903D02*
+X0073681Y0077903D01*
+X0073644Y0079102D02*
+X0073681Y0079102D01*
+X0069479Y0083896D02*
+X0061041Y0083896D01*
+X0066063Y0092500D02*
+X0021437Y0092500D01*
+X0017500Y0096437D01*
+X0017500Y0151063D01*
+X0021437Y0155000D01*
+X0027500Y0155000D01*
+X0027500Y0148937D01*
+X0033218Y0148615D02*
+X0088505Y0148615D01*
+X0088505Y0147417D02*
+X0087312Y0147417D01*
+X0087420Y0147229D02*
+X0087170Y0147663D01*
+X0086816Y0148016D01*
+X0086383Y0148267D01*
+X0085900Y0148396D01*
+X0083100Y0148396D01*
+X0083100Y0144537D01*
+X0087550Y0144537D01*
+X0087550Y0146746D01*
+X0087420Y0147229D01*
+X0087550Y0146218D02*
+X0088505Y0146218D01*
+X0088505Y0145020D02*
+X0087550Y0145020D01*
+X0088505Y0143821D02*
+X0083100Y0143821D01*
+X0083100Y0143337D02*
+X0083100Y0144537D01*
+X0081900Y0144537D01*
+X0081900Y0143337D01*
+X0077450Y0143337D01*
+X0077450Y0141128D01*
+X0077580Y0140645D01*
+X0077639Y0140543D01*
+X0077173Y0140077D01*
+X0076782Y0139133D01*
+X0076782Y0132993D01*
+X0077173Y0132049D01*
+X0077895Y0131326D01*
+X0078839Y0130935D01*
+X0079331Y0130935D01*
+X0079331Y0126870D01*
+X0079814Y0125705D01*
+X0080705Y0124814D01*
+X0081870Y0124331D01*
+X0084568Y0124331D01*
+X0084568Y0121318D01*
+X0078239Y0121318D01*
+X0077521Y0121021D01*
+X0077550Y0121128D01*
+X0077550Y0123337D01*
+X0073100Y0123337D01*
+X0073100Y0124537D01*
+X0077550Y0124537D01*
+X0077550Y0126746D01*
+X0077420Y0127229D01*
+X0077170Y0127663D01*
+X0076816Y0128016D01*
+X0076383Y0128267D01*
+X0075900Y0128396D01*
+X0073100Y0128396D01*
+X0073100Y0124537D01*
+X0071900Y0124537D01*
+X0071900Y0123337D01*
+X0067450Y0123337D01*
+X0067450Y0121128D01*
+X0067580Y0120645D01*
+X0067639Y0120543D01*
+X0067173Y0120077D01*
+X0066782Y0119133D01*
+X0066782Y0115012D01*
+X0066249Y0115545D01*
+X0065358Y0116436D01*
+X0064401Y0116832D01*
+X0064264Y0117164D01*
+X0063542Y0117886D01*
+X0063168Y0118041D01*
+X0063168Y0119890D01*
+X0063674Y0120395D01*
+X0064065Y0121339D01*
+X0064065Y0128661D01*
+X0063674Y0129605D01*
+X0062951Y0130327D01*
+X0062007Y0130718D01*
+X0055867Y0130718D01*
+X0054923Y0130327D01*
+X0054457Y0129861D01*
+X0054355Y0129920D01*
+X0053872Y0130050D01*
+X0051663Y0130050D01*
+X0051663Y0127741D01*
+X0050630Y0128168D01*
+X0050463Y0128168D01*
+X0050463Y0130050D01*
+X0048254Y0130050D01*
+X0047771Y0129920D01*
+X0047337Y0129670D01*
+X0046984Y0129316D01*
+X0046733Y0128883D01*
+X0046604Y0128400D01*
+X0046604Y0125600D01*
+X0046831Y0125600D01*
+X0046831Y0124400D01*
+X0046604Y0124400D01*
+X0046604Y0121600D01*
+X0046733Y0121117D01*
+X0046831Y0120947D01*
+X0046831Y0117278D01*
+X0046747Y0117229D01*
+X0046393Y0116875D01*
+X0046143Y0116442D01*
+X0046013Y0115959D01*
+X0046013Y0113928D01*
+X0046831Y0113928D01*
+X0046831Y0112765D01*
+X0046013Y0112765D01*
+X0046013Y0110748D01*
+X0045736Y0110471D01*
+X0045467Y0109822D01*
+X0045433Y0109822D01*
+X0045327Y0110077D01*
+X0044861Y0110543D01*
+X0044920Y0110645D01*
+X0045050Y0111128D01*
+X0045050Y0113337D01*
+X0040600Y0113337D01*
+X0040600Y0114537D01*
+X0045050Y0114537D01*
+X0045050Y0116746D01*
+X0044920Y0117229D01*
+X0044670Y0117663D01*
+X0044316Y0118016D01*
+X0043883Y0118267D01*
+X0043400Y0118396D01*
+X0040600Y0118396D01*
+X0040600Y0114537D01*
+X0039400Y0114537D01*
+X0039400Y0113337D01*
+X0034950Y0113337D01*
+X0034950Y0111128D01*
+X0035080Y0110645D01*
+X0035139Y0110543D01*
+X0034673Y0110077D01*
+X0034282Y0109133D01*
+X0034282Y0102993D01*
+X0034673Y0102049D01*
+X0035395Y0101326D01*
+X0036339Y0100935D01*
+X0043661Y0100935D01*
+X0044605Y0101326D01*
+X0045327Y0102049D01*
+X0045695Y0102936D01*
+X0045736Y0102836D01*
+X0046458Y0102114D01*
+X0047402Y0101723D01*
+X0047501Y0101723D01*
+X0047501Y0099291D01*
+X0047983Y0098126D01*
+X0048874Y0097235D01*
+X0050039Y0096753D01*
+X0067365Y0096753D01*
+X0065286Y0095668D01*
+X0022749Y0095668D01*
+X0020668Y0097749D01*
+X0020668Y0149751D01*
+X0021782Y0150864D01*
+X0021782Y0145867D01*
+X0022173Y0144923D01*
+X0022639Y0144457D01*
+X0022580Y0144355D01*
+X0022450Y0143872D01*
+X0022450Y0141663D01*
+X0026900Y0141663D01*
+X0026900Y0140463D01*
+X0022450Y0140463D01*
+X0022450Y0138254D01*
+X0022580Y0137771D01*
+X0022830Y0137337D01*
+X0023184Y0136984D01*
+X0023617Y0136733D01*
+X0024100Y0136604D01*
+X0026900Y0136604D01*
+X0026900Y0140463D01*
+X0028100Y0140463D01*
+X0028100Y0141663D01*
+X0032550Y0141663D01*
+X0032550Y0143872D01*
+X0032420Y0144355D01*
+X0032361Y0144457D01*
+X0032827Y0144923D01*
+X0033218Y0145867D01*
+X0033218Y0151831D01*
+X0079208Y0151831D01*
+X0079912Y0151127D01*
+X0081591Y0150431D01*
+X0083409Y0150431D01*
+X0085088Y0151127D01*
+X0086373Y0152412D01*
+X0087068Y0154091D01*
+X0087068Y0155909D01*
+X0086872Y0156383D01*
+X0088505Y0154751D01*
+X0088505Y0130611D01*
+X0088366Y0130668D01*
+X0085668Y0130668D01*
+X0085668Y0130935D01*
+X0086161Y0130935D01*
+X0087105Y0131326D01*
+X0087827Y0132049D01*
+X0088218Y0132993D01*
+X0088218Y0139133D01*
+X0087827Y0140077D01*
+X0087361Y0140543D01*
+X0087420Y0140645D01*
+X0087550Y0141128D01*
+X0087550Y0143337D01*
+X0083100Y0143337D01*
+X0081900Y0143821D02*
+X0032550Y0143821D01*
+X0032550Y0142623D02*
+X0077450Y0142623D01*
+X0077450Y0141424D02*
+X0028100Y0141424D01*
+X0028100Y0140463D02*
+X0032550Y0140463D01*
+X0032550Y0138254D01*
+X0032420Y0137771D01*
+X0032170Y0137337D01*
+X0031816Y0136984D01*
+X0031383Y0136733D01*
+X0030900Y0136604D01*
+X0028100Y0136604D01*
+X0028100Y0140463D01*
+X0028100Y0140226D02*
+X0026900Y0140226D01*
+X0026900Y0141424D02*
+X0020668Y0141424D01*
+X0020668Y0140226D02*
+X0022450Y0140226D01*
+X0022450Y0139027D02*
+X0020668Y0139027D01*
+X0020668Y0137829D02*
+X0022564Y0137829D01*
+X0024002Y0136630D02*
+X0020668Y0136630D01*
+X0020668Y0135432D02*
+X0069684Y0135432D01*
+X0069789Y0135275D02*
+X0070275Y0134789D01*
+X0070847Y0134407D01*
+X0071482Y0134144D01*
+X0072156Y0134009D01*
+X0072500Y0134009D01*
+X0072844Y0134009D01*
+X0073518Y0134144D01*
+X0074153Y0134407D01*
+X0074725Y0134789D01*
+X0075211Y0135275D01*
+X0075593Y0135847D01*
+X0075856Y0136482D01*
+X0075991Y0137156D01*
+X0075991Y0137500D01*
+X0075991Y0137844D01*
+X0075856Y0138518D01*
+X0075593Y0139153D01*
+X0075211Y0139725D01*
+X0074725Y0140211D01*
+X0074153Y0140593D01*
+X0073518Y0140856D01*
+X0072844Y0140991D01*
+X0072500Y0140991D01*
+X0072156Y0140991D01*
+X0071482Y0140856D01*
+X0070847Y0140593D01*
+X0070275Y0140211D01*
+X0069789Y0139725D01*
+X0069407Y0139153D01*
+X0069144Y0138518D01*
+X0069009Y0137844D01*
+X0069009Y0137500D01*
+X0069009Y0137156D01*
+X0069144Y0136482D01*
+X0069407Y0135847D01*
+X0069789Y0135275D01*
+X0069114Y0136630D02*
+X0030998Y0136630D01*
+X0032436Y0137829D02*
+X0069009Y0137829D01*
+X0069009Y0137500D02*
+X0072500Y0137500D01*
+X0075991Y0137500D01*
+X0072500Y0137500D01*
+X0072500Y0137500D01*
+X0072500Y0137500D01*
+X0072500Y0140991D01*
+X0072500Y0137500D01*
+X0072500Y0134009D01*
+X0072500Y0137500D01*
+X0072500Y0137500D01*
+X0072500Y0137500D01*
+X0069009Y0137500D01*
+X0069355Y0139027D02*
+X0032550Y0139027D01*
+X0032550Y0140226D02*
+X0070297Y0140226D01*
+X0072500Y0140226D02*
+X0072500Y0140226D01*
+X0072500Y0139027D02*
+X0072500Y0139027D01*
+X0072500Y0137829D02*
+X0072500Y0137829D01*
+X0072500Y0136630D02*
+X0072500Y0136630D01*
+X0072500Y0135432D02*
+X0072500Y0135432D01*
+X0072500Y0134233D02*
+X0072500Y0134233D01*
+X0073735Y0134233D02*
+X0076782Y0134233D01*
+X0076782Y0133035D02*
+X0020668Y0133035D01*
+X0020668Y0134233D02*
+X0071265Y0134233D01*
+X0075316Y0135432D02*
+X0076782Y0135432D01*
+X0076782Y0136630D02*
+X0075886Y0136630D01*
+X0075991Y0137829D02*
+X0076782Y0137829D01*
+X0076782Y0139027D02*
+X0075645Y0139027D01*
+X0074703Y0140226D02*
+X0077322Y0140226D01*
+X0077450Y0144537D02*
+X0081900Y0144537D01*
+X0081900Y0148396D01*
+X0079100Y0148396D01*
+X0078617Y0148267D01*
+X0078184Y0148016D01*
+X0077830Y0147663D01*
+X0077580Y0147229D01*
+X0077450Y0146746D01*
+X0077450Y0144537D01*
+X0077450Y0145020D02*
+X0032867Y0145020D01*
+X0033218Y0146218D02*
+X0077450Y0146218D01*
+X0077688Y0147417D02*
+X0033218Y0147417D01*
+X0033218Y0149814D02*
+X0088505Y0149814D01*
+X0088505Y0151012D02*
+X0084811Y0151012D01*
+X0086172Y0152211D02*
+X0088505Y0152211D01*
+X0088505Y0153409D02*
+X0086786Y0153409D01*
+X0087068Y0154608D02*
+X0088505Y0154608D01*
+X0087449Y0155806D02*
+X0087068Y0155806D01*
+X0091673Y0156063D02*
+X0091673Y0117441D01*
+X0089705Y0117441D02*
+X0089705Y0145000D01*
+X0082500Y0145000D01*
+X0081900Y0145020D02*
+X0083100Y0145020D01*
+X0083100Y0146218D02*
+X0081900Y0146218D01*
+X0081900Y0147417D02*
+X0083100Y0147417D01*
+X0080189Y0151012D02*
+X0033218Y0151012D01*
+X0027500Y0155000D02*
+X0082500Y0155000D01*
+X0086673Y0161063D02*
+X0091673Y0156063D01*
+X0093642Y0160000D02*
+X0093642Y0117441D01*
+X0095610Y0117441D02*
+X0095610Y0180000D01*
+X0096250Y0181250D01*
+X0082500Y0181250D01*
+X0082500Y0177500D01*
+X0082490Y0176250D01*
+X0087618Y0176181D02*
+X0090032Y0176181D01*
+X0089981Y0176303D02*
+X0090936Y0173999D01*
+X0092442Y0172493D01*
+X0092442Y0165681D01*
+X0090437Y0167686D01*
+X0089272Y0168168D01*
+X0043812Y0168168D01*
+X0040668Y0171312D01*
+X0040668Y0202400D01*
+X0042100Y0202400D01*
+X0042100Y0207100D01*
+X0042900Y0207100D01*
+X0042900Y0202400D01*
+X0044331Y0202400D01*
+X0044331Y0201870D01*
+X0044814Y0200705D01*
+X0049814Y0195705D01*
+X0050705Y0194814D01*
+X0051870Y0194331D01*
+X0103688Y0194331D01*
+X0106831Y0191188D01*
+X0106831Y0166312D01*
+X0103688Y0163168D01*
+X0100885Y0163168D01*
+X0099721Y0162686D01*
+X0098830Y0161795D01*
+X0098779Y0161744D01*
+X0098779Y0171812D01*
+X0099801Y0172236D01*
+X0101564Y0173999D01*
+X0102518Y0176303D01*
+X0102518Y0186197D01*
+X0101564Y0188501D01*
+X0099801Y0190264D01*
+X0097497Y0191218D01*
+X0095003Y0191218D01*
+X0092699Y0190264D01*
+X0090936Y0188501D01*
+X0089981Y0186197D01*
+X0089981Y0184418D01*
+X0081870Y0184418D01*
+X0080705Y0183936D01*
+X0079814Y0183045D01*
+X0079331Y0181880D01*
+X0079331Y0181735D01*
+X0078476Y0181380D01*
+X0078118Y0181023D01*
+X0077819Y0181103D01*
+X0075610Y0181103D01*
+X0075610Y0176850D01*
+X0074410Y0176850D01*
+X0074410Y0181103D01*
+X0072201Y0181103D01*
+X0071717Y0180973D01*
+X0071284Y0180723D01*
+X0070930Y0180369D01*
+X0070680Y0179936D01*
+X0070551Y0179453D01*
+X0070551Y0176850D01*
+X0074410Y0176850D01*
+X0074410Y0175650D01*
+X0075610Y0175650D01*
+X0075610Y0171397D01*
+X0077819Y0171397D01*
+X0078118Y0171477D01*
+X0078476Y0171120D01*
+X0079420Y0170729D01*
+X0085560Y0170729D01*
+X0086504Y0171120D01*
+X0087227Y0171842D01*
+X0087618Y0172786D01*
+X0087618Y0178081D01*
+X0089981Y0178081D01*
+X0089981Y0176303D01*
+X0089981Y0177380D02*
+X0087618Y0177380D01*
+X0087618Y0174983D02*
+X0090528Y0174983D01*
+X0091151Y0173784D02*
+X0087618Y0173784D01*
+X0087535Y0172586D02*
+X0092349Y0172586D01*
+X0092442Y0171387D02*
+X0086771Y0171387D01*
+X0090182Y0167792D02*
+X0092442Y0167792D01*
+X0092442Y0168990D02*
+X0042991Y0168990D01*
+X0041792Y0170189D02*
+X0092442Y0170189D01*
+X0092442Y0166593D02*
+X0091530Y0166593D01*
+X0088642Y0165000D02*
+X0093642Y0160000D01*
+X0098779Y0161799D02*
+X0098834Y0161799D01*
+X0098779Y0162998D02*
+X0100473Y0162998D01*
+X0098779Y0164196D02*
+X0104715Y0164196D01*
+X0105914Y0165395D02*
+X0098779Y0165395D01*
+X0098779Y0166593D02*
+X0106831Y0166593D01*
+X0106831Y0167792D02*
+X0098779Y0167792D01*
+X0098779Y0168990D02*
+X0106831Y0168990D01*
+X0106831Y0170189D02*
+X0098779Y0170189D01*
+X0098779Y0171387D02*
+X0106831Y0171387D01*
+X0106831Y0172586D02*
+X0100151Y0172586D01*
+X0101349Y0173784D02*
+X0106831Y0173784D01*
+X0106831Y0174983D02*
+X0101972Y0174983D01*
+X0102468Y0176181D02*
+X0106831Y0176181D01*
+X0106831Y0177380D02*
+X0102518Y0177380D01*
+X0102518Y0178578D02*
+X0106831Y0178578D01*
+X0106831Y0179777D02*
+X0102518Y0179777D01*
+X0102518Y0180975D02*
+X0106831Y0180975D01*
+X0106831Y0182174D02*
+X0102518Y0182174D01*
+X0102518Y0183372D02*
+X0106831Y0183372D01*
+X0106831Y0184571D02*
+X0102518Y0184571D01*
+X0102518Y0185769D02*
+X0106831Y0185769D01*
+X0106831Y0186968D02*
+X0102199Y0186968D01*
+X0101703Y0188166D02*
+X0106831Y0188166D01*
+X0106831Y0189365D02*
+X0100700Y0189365D01*
+X0099079Y0190563D02*
+X0106831Y0190563D01*
+X0106257Y0191762D02*
+X0081775Y0191762D01*
+X0081653Y0191843D02*
+X0081018Y0192106D01*
+X0080344Y0192241D01*
+X0080000Y0192241D01*
+X0079656Y0192241D01*
+X0078982Y0192106D01*
+X0078347Y0191843D01*
+X0077775Y0191461D01*
+X0077289Y0190975D01*
+X0076907Y0190403D01*
+X0076644Y0189768D01*
+X0076509Y0189094D01*
+X0076509Y0188750D01*
+X0076509Y0188406D01*
+X0076644Y0187732D01*
+X0076907Y0187097D01*
+X0077289Y0186525D01*
+X0077775Y0186039D01*
+X0078347Y0185657D01*
+X0078982Y0185394D01*
+X0079656Y0185259D01*
+X0080000Y0185259D01*
+X0080344Y0185259D01*
+X0081018Y0185394D01*
+X0081653Y0185657D01*
+X0082225Y0186039D01*
+X0082711Y0186525D01*
+X0083093Y0187097D01*
+X0083356Y0187732D01*
+X0083491Y0188406D01*
+X0083491Y0188750D01*
+X0083491Y0189094D01*
+X0083356Y0189768D01*
+X0083093Y0190403D01*
+X0082711Y0190975D01*
+X0082225Y0191461D01*
+X0081653Y0191843D01*
+X0080000Y0191762D02*
+X0080000Y0191762D01*
+X0080000Y0192241D02*
+X0080000Y0188750D01*
+X0083491Y0188750D01*
+X0080000Y0188750D01*
+X0080000Y0188750D01*
+X0080000Y0188750D01*
+X0080000Y0192241D01*
+X0080000Y0190563D02*
+X0080000Y0190563D01*
+X0080000Y0189365D02*
+X0080000Y0189365D01*
+X0080000Y0188750D02*
+X0080000Y0188750D01*
+X0080000Y0185259D01*
+X0080000Y0188750D01*
+X0080000Y0188750D01*
+X0076509Y0188750D01*
+X0080000Y0188750D01*
+X0080000Y0188166D02*
+X0080000Y0188166D01*
+X0080000Y0186968D02*
+X0080000Y0186968D01*
+X0080000Y0185769D02*
+X0080000Y0185769D01*
+X0081822Y0185769D02*
+X0089981Y0185769D01*
+X0089981Y0184571D02*
+X0040668Y0184571D01*
+X0040668Y0185769D02*
+X0078178Y0185769D01*
+X0076993Y0186968D02*
+X0040668Y0186968D01*
+X0040668Y0188166D02*
+X0076557Y0188166D01*
+X0076563Y0189365D02*
+X0040668Y0189365D01*
+X0040668Y0190563D02*
+X0077014Y0190563D01*
+X0078225Y0191762D02*
+X0040668Y0191762D01*
+X0040668Y0192960D02*
+X0105059Y0192960D01*
+X0103860Y0194159D02*
+X0040668Y0194159D01*
+X0040668Y0195357D02*
+X0050162Y0195357D01*
+X0048963Y0196556D02*
+X0040668Y0196556D01*
+X0040668Y0197754D02*
+X0047765Y0197754D01*
+X0046566Y0198953D02*
+X0040668Y0198953D01*
+X0040668Y0200151D02*
+X0045368Y0200151D01*
+X0044547Y0201350D02*
+X0040668Y0201350D01*
+X0042100Y0202548D02*
+X0042900Y0202548D01*
+X0042900Y0203747D02*
+X0042100Y0203747D01*
+X0042100Y0204945D02*
+X0042900Y0204945D01*
+X0042900Y0206144D02*
+X0042100Y0206144D01*
+X0047500Y0202500D02*
+X0052500Y0197500D01*
+X0105000Y0197500D01*
+X0110000Y0192500D01*
+X0110000Y0165000D01*
+X0105000Y0160000D01*
+X0101516Y0160000D01*
+X0097579Y0156063D01*
+X0097579Y0117441D01*
+X0099547Y0117441D02*
+X0099547Y0153563D01*
+X0103484Y0157500D01*
+X0109402Y0157500D01*
+X0114402Y0162500D01*
+X0114402Y0192500D01*
+X0130733Y0198953D02*
+X0134267Y0198953D01*
+X0132707Y0200151D02*
+X0132293Y0200151D01*
+X0138098Y0205000D02*
+X0138098Y0217500D01*
+X0136240Y0217500D01*
+X0141368Y0216931D02*
+X0153160Y0216931D01*
+X0153160Y0216900D02*
+X0149301Y0216900D01*
+X0149301Y0214297D01*
+X0149430Y0213814D01*
+X0149680Y0213381D01*
+X0150034Y0213027D01*
+X0150467Y0212777D01*
+X0150951Y0212647D01*
+X0153160Y0212647D01*
+X0153160Y0216900D01*
+X0153160Y0215732D02*
+X0154360Y0215732D01*
+X0154360Y0214534D02*
+X0153160Y0214534D01*
+X0153160Y0213335D02*
+X0154360Y0213335D01*
+X0149726Y0213335D02*
+X0141267Y0213335D01*
+X0141267Y0212137D02*
+X0157789Y0212137D01*
+X0159004Y0210938D02*
+X0155996Y0210938D01*
+X0157338Y0209739D02*
+X0157662Y0209739D01*
+X0149301Y0214534D02*
+X0141368Y0214534D01*
+X0141368Y0215732D02*
+X0149301Y0215732D01*
+X0149301Y0218129D02*
+X0141368Y0218129D01*
+X0141368Y0219328D02*
+X0149301Y0219328D01*
+X0149301Y0220526D02*
+X0141368Y0220526D01*
+X0141053Y0221725D02*
+X0149786Y0221725D01*
+X0153160Y0221725D02*
+X0154360Y0221725D01*
+X0154360Y0220526D02*
+X0153160Y0220526D01*
+X0153160Y0219328D02*
+X0154360Y0219328D01*
+X0154360Y0218129D02*
+X0153160Y0218129D01*
+X0157933Y0222923D02*
+X0139547Y0222923D01*
+X0142193Y0210938D02*
+X0147807Y0210938D01*
+X0146465Y0209739D02*
+X0143535Y0209739D01*
+X0143817Y0208541D02*
+X0146183Y0208541D01*
+X0146183Y0207342D02*
+X0143817Y0207342D01*
+X0143817Y0206144D02*
+X0146183Y0206144D01*
+X0166368Y0214534D02*
+X0177553Y0214534D01*
+X0178751Y0215732D02*
+X0166368Y0215732D01*
+X0166368Y0216931D02*
+X0179231Y0216931D01*
+X0179231Y0218129D02*
+X0166368Y0218129D01*
+X0166368Y0219328D02*
+X0179231Y0219328D01*
+X0179868Y0220526D02*
+X0166368Y0220526D01*
+X0166053Y0221725D02*
+X0181067Y0221725D01*
+X0182265Y0222923D02*
+X0164547Y0222923D01*
+X0187141Y0224122D02*
+X0060359Y0224122D01*
+X0059161Y0225320D02*
+X0188529Y0225320D01*
+X0195000Y0217500D02*
+X0190000Y0212500D01*
+X0195400Y0207342D02*
+X0209009Y0207342D01*
+X0209009Y0207500D02*
+X0209009Y0207156D01*
+X0209144Y0206482D01*
+X0209407Y0205847D01*
+X0209789Y0205275D01*
+X0210275Y0204789D01*
+X0210847Y0204407D01*
+X0211482Y0204144D01*
+X0212156Y0204009D01*
+X0212500Y0204009D01*
+X0212844Y0204009D01*
+X0213518Y0204144D01*
+X0214153Y0204407D01*
+X0214725Y0204789D01*
+X0215211Y0205275D01*
+X0215593Y0205847D01*
+X0215856Y0206482D01*
+X0215991Y0207156D01*
+X0215991Y0207500D01*
+X0215991Y0207844D01*
+X0215856Y0208518D01*
+X0215593Y0209153D01*
+X0215211Y0209725D01*
+X0214725Y0210211D01*
+X0214153Y0210593D01*
+X0213518Y0210856D01*
+X0212844Y0210991D01*
+X0212500Y0210991D01*
+X0212156Y0210991D01*
+X0211482Y0210856D01*
+X0210847Y0210593D01*
+X0210275Y0210211D01*
+X0209789Y0209725D01*
+X0209407Y0209153D01*
+X0209144Y0208518D01*
+X0209009Y0207844D01*
+X0209009Y0207500D01*
+X0212500Y0207500D01*
+X0215991Y0207500D01*
+X0212500Y0207500D01*
+X0212500Y0207500D01*
+X0212500Y0207500D01*
+X0212500Y0210991D01*
+X0212500Y0207500D01*
+X0212500Y0204009D01*
+X0212500Y0207500D01*
+X0212500Y0207500D01*
+X0212500Y0207500D01*
+X0209009Y0207500D01*
+X0209153Y0208541D02*
+X0200100Y0208541D01*
+X0199973Y0209739D02*
+X0209803Y0209739D01*
+X0211892Y0210938D02*
+X0198774Y0210938D01*
+X0197794Y0212137D02*
+X0269959Y0212137D01*
+X0269950Y0210938D02*
+X0213108Y0210938D01*
+X0212500Y0210938D02*
+X0212500Y0210938D01*
+X0212500Y0209739D02*
+X0212500Y0209739D01*
+X0212500Y0208541D02*
+X0212500Y0208541D01*
+X0212500Y0207342D02*
+X0212500Y0207342D01*
+X0212500Y0206144D02*
+X0212500Y0206144D01*
+X0212500Y0204945D02*
+X0212500Y0204945D01*
+X0214882Y0204945D02*
+X0226023Y0204945D01*
+X0226044Y0205025D02*
+X0225915Y0204541D01*
+X0225915Y0202609D01*
+X0233631Y0202609D01*
+X0233631Y0206191D01*
+X0227565Y0206191D01*
+X0227082Y0206062D01*
+X0226648Y0205812D01*
+X0226295Y0205458D01*
+X0226044Y0205025D01*
+X0225915Y0203747D02*
+X0198459Y0203747D01*
+X0197261Y0202548D02*
+X0233631Y0202548D01*
+X0234597Y0202548D02*
+X0260403Y0202548D01*
+X0260886Y0202126D02*
+X0270000Y0202126D01*
+X0269085Y0203747D02*
+X0286132Y0203747D01*
+X0286132Y0204945D02*
+X0279732Y0204945D01*
+X0280050Y0206144D02*
+X0286132Y0206144D01*
+X0286132Y0207342D02*
+X0280050Y0207342D01*
+X0275600Y0206144D02*
+X0274400Y0206144D01*
+X0274400Y0204945D02*
+X0275600Y0204945D01*
+X0274400Y0208541D02*
+X0215847Y0208541D01*
+X0215991Y0207342D02*
+X0269950Y0207342D01*
+X0269950Y0206144D02*
+X0267612Y0206144D01*
+X0268977Y0204945D02*
+X0270268Y0204945D01*
+X0269950Y0209739D02*
+X0215197Y0209739D01*
+X0215716Y0206144D02*
+X0227388Y0206144D01*
+X0225915Y0201350D02*
+X0193168Y0201350D01*
+X0193168Y0200151D02*
+X0225915Y0200151D01*
+X0226203Y0198953D02*
+X0193168Y0198953D01*
+X0193168Y0197754D02*
+X0286132Y0197754D01*
+X0286132Y0196556D02*
+X0193168Y0196556D01*
+X0193168Y0195357D02*
+X0286132Y0195357D01*
+X0286132Y0194159D02*
+X0193168Y0194159D01*
+X0193168Y0192960D02*
+X0286132Y0192960D01*
+X0286132Y0191762D02*
+X0193168Y0191762D01*
+X0193168Y0190563D02*
+X0286132Y0190563D01*
+X0286132Y0189365D02*
+X0193168Y0189365D01*
+X0193168Y0188166D02*
+X0286132Y0188166D01*
+X0286132Y0186968D02*
+X0193168Y0186968D01*
+X0193168Y0185769D02*
+X0286132Y0185769D01*
+X0286132Y0184571D02*
+X0193168Y0184571D01*
+X0186831Y0184571D02*
+X0176208Y0184571D01*
+X0177750Y0185769D02*
+X0186831Y0185769D01*
+X0186831Y0186968D02*
+X0178949Y0186968D01*
+X0180147Y0188166D02*
+X0186831Y0188166D01*
+X0186831Y0189365D02*
+X0181346Y0189365D01*
+X0182544Y0190563D02*
+X0186831Y0190563D01*
+X0186831Y0191762D02*
+X0183124Y0191762D01*
+X0183168Y0192960D02*
+X0186831Y0192960D01*
+X0186831Y0194159D02*
+X0183168Y0194159D01*
+X0183168Y0195357D02*
+X0186831Y0195357D01*
+X0186831Y0196556D02*
+X0183168Y0196556D01*
+X0183168Y0197754D02*
+X0186831Y0197754D01*
+X0194600Y0202548D02*
+X0195400Y0202548D01*
+X0195400Y0202400D02*
+X0197112Y0202400D01*
+X0200100Y0205388D01*
+X0200100Y0207100D01*
+X0195400Y0207100D01*
+X0195400Y0202400D01*
+X0195400Y0203747D02*
+X0194600Y0203747D01*
+X0194600Y0204945D02*
+X0195400Y0204945D01*
+X0195400Y0206144D02*
+X0194600Y0206144D01*
+X0199658Y0204945D02*
+X0210118Y0204945D01*
+X0209284Y0206144D02*
+X0200100Y0206144D01*
+X0198993Y0213335D02*
+X0226834Y0213335D01*
+X0225522Y0214534D02*
+X0200191Y0214534D01*
+X0200768Y0215732D02*
+X0225246Y0215732D01*
+X0225246Y0216931D02*
+X0200768Y0216931D01*
+X0200768Y0218129D02*
+X0225246Y0218129D01*
+X0225246Y0219328D02*
+X0200768Y0219328D01*
+X0233631Y0206144D02*
+X0234597Y0206144D01*
+X0234597Y0204945D02*
+X0233631Y0204945D01*
+X0233631Y0203747D02*
+X0234597Y0203747D01*
+X0234597Y0201350D02*
+X0233631Y0201350D01*
+X0233631Y0200151D02*
+X0234597Y0200151D01*
+X0234597Y0198953D02*
+X0233631Y0198953D01*
+X0241395Y0213335D02*
+X0253605Y0213335D01*
+X0252293Y0214534D02*
+X0242707Y0214534D01*
+X0242361Y0221725D02*
+X0252639Y0221725D01*
+X0237283Y0222923D02*
+X0286132Y0222923D01*
+X0286132Y0224122D02*
+X0236872Y0224122D01*
+X0235585Y0225320D02*
+X0286132Y0225320D01*
+X0286132Y0226519D02*
+X0057962Y0226519D01*
+X0057686Y0226795D02*
+X0057686Y0226795D01*
+X0052500Y0217500D02*
+X0057500Y0212500D01*
+X0052500Y0217500D02*
+X0047500Y0222500D01*
+X0018937Y0222500D01*
+X0022500Y0217500D02*
+X0027500Y0212500D01*
+X0027500Y0166063D01*
+X0032500Y0161063D01*
+X0086673Y0161063D01*
+X0088642Y0165000D02*
+X0042500Y0165000D01*
+X0037500Y0170000D01*
+X0037500Y0212500D01*
+X0032500Y0217500D01*
+X0042500Y0217500D02*
+X0047500Y0212500D01*
+X0047500Y0202500D01*
+X0034331Y0202400D02*
+X0034331Y0169370D01*
+X0034814Y0168205D01*
+X0038788Y0164231D01*
+X0033812Y0164231D01*
+X0030668Y0167375D01*
+X0030668Y0202400D01*
+X0032100Y0202400D01*
+X0032100Y0207100D01*
+X0032900Y0207100D01*
+X0032900Y0202400D01*
+X0034331Y0202400D01*
+X0034331Y0201350D02*
+X0030668Y0201350D01*
+X0030668Y0200151D02*
+X0034331Y0200151D01*
+X0034331Y0198953D02*
+X0030668Y0198953D01*
+X0030668Y0197754D02*
+X0034331Y0197754D01*
+X0034331Y0196556D02*
+X0030668Y0196556D01*
+X0030668Y0195357D02*
+X0034331Y0195357D01*
+X0034331Y0194159D02*
+X0030668Y0194159D01*
+X0030668Y0192960D02*
+X0034331Y0192960D01*
+X0034331Y0191762D02*
+X0030668Y0191762D01*
+X0030668Y0190563D02*
+X0034331Y0190563D01*
+X0034331Y0189365D02*
+X0030668Y0189365D01*
+X0030668Y0188166D02*
+X0034331Y0188166D01*
+X0034331Y0186968D02*
+X0030668Y0186968D01*
+X0030668Y0185769D02*
+X0034331Y0185769D01*
+X0034331Y0184571D02*
+X0030668Y0184571D01*
+X0030668Y0183372D02*
+X0034331Y0183372D01*
+X0034331Y0182174D02*
+X0030668Y0182174D01*
+X0030668Y0180975D02*
+X0034331Y0180975D01*
+X0034331Y0179777D02*
+X0030668Y0179777D01*
+X0030668Y0178578D02*
+X0034331Y0178578D01*
+X0034331Y0177380D02*
+X0030668Y0177380D01*
+X0030668Y0176181D02*
+X0034331Y0176181D01*
+X0034331Y0174983D02*
+X0030668Y0174983D01*
+X0030668Y0173784D02*
+X0034331Y0173784D01*
+X0034331Y0172586D02*
+X0030668Y0172586D01*
+X0030668Y0171387D02*
+X0034331Y0171387D01*
+X0034331Y0170189D02*
+X0030668Y0170189D01*
+X0030668Y0168990D02*
+X0034489Y0168990D01*
+X0035227Y0167792D02*
+X0030668Y0167792D01*
+X0031451Y0166593D02*
+X0036426Y0166593D01*
+X0037624Y0165395D02*
+X0032649Y0165395D01*
+X0028482Y0160601D02*
+X0018168Y0160601D01*
+X0018168Y0161799D02*
+X0027283Y0161799D01*
+X0026084Y0162998D02*
+X0018168Y0162998D01*
+X0018168Y0164196D02*
+X0024886Y0164196D01*
+X0024814Y0164268D02*
+X0029814Y0159268D01*
+X0030705Y0158377D01*
+X0031208Y0158168D01*
+X0028130Y0158168D01*
+X0020807Y0158168D01*
+X0019642Y0157686D01*
+X0018751Y0156795D01*
+X0018168Y0156212D01*
+X0018168Y0204619D01*
+X0020388Y0202400D01*
+X0022100Y0202400D01*
+X0022100Y0207100D01*
+X0022900Y0207100D01*
+X0022900Y0202400D01*
+X0024331Y0202400D01*
+X0024331Y0165433D01*
+X0024814Y0164268D01*
+X0024347Y0165395D02*
+X0018168Y0165395D01*
+X0018168Y0166593D02*
+X0024331Y0166593D01*
+X0024331Y0167792D02*
+X0018168Y0167792D01*
+X0018168Y0168990D02*
+X0024331Y0168990D01*
+X0024331Y0170189D02*
+X0018168Y0170189D01*
+X0018168Y0171387D02*
+X0024331Y0171387D01*
+X0024331Y0172586D02*
+X0018168Y0172586D01*
+X0018168Y0173784D02*
+X0024331Y0173784D01*
+X0024331Y0174983D02*
+X0018168Y0174983D01*
+X0018168Y0176181D02*
+X0024331Y0176181D01*
+X0024331Y0177380D02*
+X0018168Y0177380D01*
+X0018168Y0178578D02*
+X0024331Y0178578D01*
+X0024331Y0179777D02*
+X0018168Y0179777D01*
+X0018168Y0180975D02*
+X0024331Y0180975D01*
+X0024331Y0182174D02*
+X0018168Y0182174D01*
+X0018168Y0183372D02*
+X0024331Y0183372D01*
+X0024331Y0184571D02*
+X0018168Y0184571D01*
+X0018168Y0185769D02*
+X0024331Y0185769D01*
+X0024331Y0186968D02*
+X0018168Y0186968D01*
+X0018168Y0188166D02*
+X0024331Y0188166D01*
+X0024331Y0189365D02*
+X0018168Y0189365D01*
+X0018168Y0190563D02*
+X0024331Y0190563D01*
+X0024331Y0191762D02*
+X0018168Y0191762D01*
+X0018168Y0192960D02*
+X0024331Y0192960D01*
+X0024331Y0194159D02*
+X0018168Y0194159D01*
+X0018168Y0195357D02*
+X0024331Y0195357D01*
+X0024331Y0196556D02*
+X0018168Y0196556D01*
+X0018168Y0197754D02*
+X0024331Y0197754D01*
+X0024331Y0198953D02*
+X0018168Y0198953D01*
+X0018168Y0200151D02*
+X0024331Y0200151D01*
+X0024331Y0201350D02*
+X0018168Y0201350D01*
+X0018168Y0202548D02*
+X0020239Y0202548D01*
+X0019041Y0203747D02*
+X0018168Y0203747D01*
+X0022100Y0203747D02*
+X0022900Y0203747D01*
+X0022900Y0204945D02*
+X0022100Y0204945D01*
+X0022100Y0206144D02*
+X0022900Y0206144D01*
+X0022900Y0202548D02*
+X0022100Y0202548D01*
+X0032100Y0202548D02*
+X0032900Y0202548D01*
+X0032900Y0203747D02*
+X0032100Y0203747D01*
+X0032100Y0204945D02*
+X0032900Y0204945D01*
+X0032900Y0206144D02*
+X0032100Y0206144D01*
+X0040668Y0183372D02*
+X0080141Y0183372D01*
+X0079453Y0182174D02*
+X0040668Y0182174D01*
+X0040668Y0180975D02*
+X0071725Y0180975D01*
+X0070638Y0179777D02*
+X0040668Y0179777D01*
+X0040668Y0178578D02*
+X0070551Y0178578D01*
+X0070551Y0177380D02*
+X0040668Y0177380D01*
+X0040668Y0176181D02*
+X0074410Y0176181D01*
+X0074410Y0175650D02*
+X0070551Y0175650D01*
+X0070551Y0173047D01*
+X0070680Y0172564D01*
+X0070930Y0172131D01*
+X0071284Y0171777D01*
+X0071717Y0171527D01*
+X0072201Y0171397D01*
+X0074410Y0171397D01*
+X0074410Y0175650D01*
+X0074410Y0174983D02*
+X0075610Y0174983D01*
+X0075610Y0173784D02*
+X0074410Y0173784D01*
+X0074410Y0172586D02*
+X0075610Y0172586D01*
+X0078209Y0171387D02*
+X0040668Y0171387D01*
+X0040668Y0172586D02*
+X0070674Y0172586D01*
+X0070551Y0173784D02*
+X0040668Y0173784D01*
+X0040668Y0174983D02*
+X0070551Y0174983D01*
+X0074410Y0177380D02*
+X0075610Y0177380D01*
+X0075610Y0178578D02*
+X0074410Y0178578D01*
+X0074410Y0179777D02*
+X0075610Y0179777D01*
+X0075610Y0180975D02*
+X0074410Y0180975D01*
+X0083007Y0186968D02*
+X0090301Y0186968D01*
+X0090797Y0188166D02*
+X0083443Y0188166D01*
+X0083437Y0189365D02*
+X0091800Y0189365D01*
+X0093421Y0190563D02*
+X0082986Y0190563D01*
+X0120493Y0151012D02*
+X0174552Y0151012D01*
+X0193168Y0151012D02*
+X0194552Y0151012D01*
+X0193514Y0149814D02*
+X0193168Y0149814D01*
+X0193168Y0152211D02*
+X0211553Y0152211D01*
+X0210933Y0153409D02*
+X0199563Y0153409D01*
+X0201069Y0154608D02*
+X0209734Y0154608D01*
+X0209231Y0155806D02*
+X0201743Y0155806D01*
+X0202068Y0157005D02*
+X0209231Y0157005D01*
+X0209231Y0158203D02*
+X0202068Y0158203D01*
+X0195941Y0161799D02*
+X0193168Y0161799D01*
+X0194327Y0153409D02*
+X0195437Y0153409D01*
+X0200448Y0151012D02*
+X0210355Y0151012D01*
+X0209231Y0149814D02*
+X0201486Y0149814D01*
+X0201983Y0148615D02*
+X0209231Y0148615D01*
+X0209231Y0147417D02*
+X0202068Y0147417D01*
+X0201914Y0146218D02*
+X0209231Y0146218D01*
+X0209322Y0145020D02*
+X0201418Y0145020D01*
+X0200668Y0143821D02*
+X0210521Y0143821D01*
+X0211719Y0142623D02*
+X0200668Y0142623D01*
+X0200668Y0141424D02*
+X0240926Y0141424D01*
+X0240926Y0140226D02*
+X0200668Y0140226D01*
+X0200668Y0139027D02*
+X0240926Y0139027D01*
+X0240926Y0137829D02*
+X0227049Y0137829D01*
+X0228103Y0136630D02*
+X0240976Y0136630D01*
+X0241694Y0135432D02*
+X0228473Y0135432D01*
+X0225000Y0135432D02*
+X0225000Y0135432D01*
+X0225000Y0136630D02*
+X0225000Y0136630D01*
+X0225000Y0137829D02*
+X0225000Y0137829D01*
+X0222951Y0137829D02*
+X0200668Y0137829D01*
+X0200668Y0136630D02*
+X0221897Y0136630D01*
+X0221527Y0135432D02*
+X0200668Y0135432D01*
+X0200668Y0134233D02*
+X0221594Y0134233D01*
+X0222115Y0133035D02*
+X0200668Y0133035D01*
+X0194331Y0133035D02*
+X0193426Y0133035D01*
+X0194184Y0131836D02*
+X0194331Y0131836D01*
+X0194331Y0134233D02*
+X0191718Y0134233D01*
+X0200668Y0124645D02*
+X0237235Y0124645D01*
+X0236629Y0123447D02*
+X0200668Y0123447D01*
+X0200230Y0122248D02*
+X0236509Y0122248D01*
+X0236823Y0121050D02*
+X0199094Y0121050D01*
+X0197895Y0119851D02*
+X0237712Y0119851D01*
+X0240000Y0123447D02*
+X0240000Y0123447D01*
+X0240000Y0124645D02*
+X0240000Y0124645D01*
+X0250806Y0135432D02*
+X0252944Y0135432D01*
+X0247500Y0140000D02*
+X0246250Y0140020D01*
+X0240926Y0142623D02*
+X0228281Y0142623D01*
+X0229479Y0143821D02*
+X0241147Y0143821D01*
+X0242200Y0145020D02*
+X0230678Y0145020D01*
+X0221719Y0142623D02*
+X0218281Y0142623D01*
+X0219479Y0143821D02*
+X0220521Y0143821D01*
+X0225000Y0134233D02*
+X0225000Y0134233D01*
+X0225000Y0133035D02*
+X0225000Y0133035D01*
+X0223740Y0107500D02*
+X0232500Y0107500D01*
+X0235000Y0105000D01*
+X0255157Y0105000D01*
+X0255157Y0101850D02*
+X0236437Y0101850D01*
+X0266910Y0070712D02*
+X0268110Y0070712D01*
+X0268110Y0069514D02*
+X0266910Y0069514D01*
+X0266910Y0068315D02*
+X0268110Y0068315D01*
+X0213124Y0061124D02*
+X0171268Y0061124D01*
+X0163731Y0061124D02*
+X0133396Y0061124D01*
+X0133396Y0059400D02*
+X0129537Y0059400D01*
+X0129537Y0054950D01*
+X0131746Y0054950D01*
+X0132229Y0055080D01*
+X0132663Y0055330D01*
+X0133016Y0055684D01*
+X0133267Y0056117D01*
+X0133396Y0056600D01*
+X0133396Y0059400D01*
+X0133396Y0058727D02*
+X0163731Y0058727D01*
+X0163731Y0057529D02*
+X0133396Y0057529D01*
+X0133324Y0056330D02*
+X0163731Y0056330D01*
+X0163731Y0055132D02*
+X0132319Y0055132D01*
+X0129537Y0055132D02*
+X0128337Y0055132D01*
+X0128337Y0056330D02*
+X0129537Y0056330D01*
+X0129537Y0057529D02*
+X0128337Y0057529D01*
+X0128337Y0058727D02*
+X0129537Y0058727D01*
+X0129537Y0059926D02*
+X0163731Y0059926D01*
+X0163731Y0062323D02*
+X0140227Y0062323D01*
+X0143337Y0063521D02*
+X0144537Y0063521D01*
+X0144537Y0062450D02*
+X0146746Y0062450D01*
+X0147229Y0062580D01*
+X0147663Y0062830D01*
+X0148016Y0063184D01*
+X0148267Y0063617D01*
+X0148396Y0064100D01*
+X0148396Y0066900D01*
+X0144537Y0066900D01*
+X0144537Y0062450D01*
+X0144537Y0064720D02*
+X0143337Y0064720D01*
+X0143337Y0065918D02*
+X0144537Y0065918D01*
+X0144537Y0067117D02*
+X0163731Y0067117D01*
+X0163731Y0068315D02*
+X0148396Y0068315D01*
+X0148396Y0069514D02*
+X0163731Y0069514D01*
+X0163731Y0070712D02*
+X0148396Y0070712D01*
+X0148396Y0065918D02*
+X0163731Y0065918D01*
+X0163731Y0064720D02*
+X0148396Y0064720D01*
+X0148211Y0063521D02*
+X0163731Y0063521D01*
+X0163731Y0053933D02*
+X0112558Y0053933D01*
+X0112558Y0055132D02*
+X0116590Y0055132D01*
+X0115939Y0056330D02*
+X0112558Y0056330D01*
+X0112558Y0057529D02*
+X0113286Y0057529D01*
+X0115295Y0060000D02*
+X0121063Y0060000D01*
+X0122500Y0060000D01*
+X0125536Y0055132D02*
+X0125555Y0055132D01*
+X0118760Y0047500D02*
+X0113327Y0047500D01*
+X0109390Y0051437D01*
+X0109390Y0072559D01*
+X0115295Y0072559D02*
+X0115295Y0060000D01*
+X0114998Y0052734D02*
+X0112573Y0052734D01*
+X0113772Y0051536D02*
+X0113869Y0051536D01*
+X0110802Y0045543D02*
+X0110590Y0045543D01*
+X0110590Y0044345D02*
+X0112664Y0044345D01*
+X0114001Y0043146D02*
+X0110590Y0043146D01*
+X0118712Y0034331D02*
+X0124751Y0034331D01*
+X0126831Y0032251D01*
+X0126831Y0027881D01*
+X0124612Y0030100D01*
+X0122900Y0030100D01*
+X0122900Y0025400D01*
+X0122100Y0025400D01*
+X0122100Y0030100D01*
+X0120668Y0030100D01*
+X0120668Y0031693D01*
+X0120186Y0032858D01*
+X0118712Y0034331D01*
+X0119486Y0033558D02*
+X0125524Y0033558D01*
+X0126722Y0032360D02*
+X0120392Y0032360D01*
+X0120668Y0031161D02*
+X0126831Y0031161D01*
+X0126831Y0029963D02*
+X0124750Y0029963D01*
+X0125948Y0028764D02*
+X0126831Y0028764D01*
+X0122900Y0028764D02*
+X0122100Y0028764D01*
+X0122100Y0027566D02*
+X0122900Y0027566D01*
+X0122900Y0026367D02*
+X0122100Y0026367D01*
+X0122100Y0029963D02*
+X0122900Y0029963D01*
+X0113982Y0030100D02*
+X0112251Y0031831D01*
+X0108759Y0031831D01*
+X0108679Y0031865D01*
+X0110186Y0030358D01*
+X0110321Y0030033D01*
+X0110388Y0030100D01*
+X0112100Y0030100D01*
+X0112100Y0025400D01*
+X0112900Y0025400D01*
+X0112900Y0030100D01*
+X0113982Y0030100D01*
+X0112900Y0029963D02*
+X0112100Y0029963D01*
+X0112100Y0028764D02*
+X0112900Y0028764D01*
+X0112900Y0027566D02*
+X0112100Y0027566D01*
+X0112100Y0026367D02*
+X0112900Y0026367D01*
+X0112921Y0031161D02*
+X0109383Y0031161D01*
+X0094410Y0031161D02*
+X0090668Y0031161D01*
+X0090668Y0030100D02*
+X0090668Y0042251D01*
+X0093065Y0044647D01*
+X0093468Y0044814D01*
+X0094410Y0045756D01*
+X0094410Y0030100D01*
+X0092900Y0030100D01*
+X0092900Y0025400D01*
+X0092100Y0025400D01*
+X0092100Y0030100D01*
+X0090668Y0030100D01*
+X0092100Y0029963D02*
+X0092900Y0029963D01*
+X0092900Y0028764D02*
+X0092100Y0028764D01*
+X0092100Y0027566D02*
+X0092900Y0027566D01*
+X0092900Y0026367D02*
+X0092100Y0026367D01*
+X0090668Y0032360D02*
+X0094410Y0032360D01*
+X0094410Y0033558D02*
+X0090668Y0033558D01*
+X0090668Y0034757D02*
+X0094410Y0034757D01*
+X0094410Y0035955D02*
+X0090668Y0035955D01*
+X0090668Y0037154D02*
+X0094410Y0037154D01*
+X0094410Y0038352D02*
+X0090668Y0038352D01*
+X0090668Y0039551D02*
+X0094410Y0039551D01*
+X0094410Y0040749D02*
+X0090668Y0040749D01*
+X0090668Y0041948D02*
+X0094410Y0041948D01*
+X0094410Y0043146D02*
+X0091564Y0043146D01*
+X0092763Y0044345D02*
+X0094410Y0044345D01*
+X0094410Y0045543D02*
+X0094198Y0045543D01*
+X0090008Y0050337D02*
+X0085665Y0050337D01*
+X0084466Y0049139D02*
+X0088595Y0049139D01*
+X0087397Y0047940D02*
+X0067803Y0047940D01*
+X0068064Y0055132D02*
+X0075181Y0055132D01*
+X0075271Y0055080D02*
+X0075754Y0054950D01*
+X0077963Y0054950D01*
+X0077963Y0059400D01*
+X0074104Y0059400D01*
+X0074104Y0056600D01*
+X0074233Y0056117D01*
+X0074484Y0055684D01*
+X0074837Y0055330D01*
+X0075271Y0055080D01*
+X0074176Y0056330D02*
+X0068287Y0056330D01*
+X0068287Y0057529D02*
+X0074104Y0057529D01*
+X0074104Y0058727D02*
+X0068287Y0058727D01*
+X0060769Y0063521D02*
+X0030743Y0063521D01*
+X0030668Y0064720D02*
+X0060769Y0064720D01*
+X0060769Y0065918D02*
+X0030668Y0065918D01*
+X0031942Y0062323D02*
+X0059960Y0062323D01*
+X0067105Y0064720D02*
+X0070378Y0064720D01*
+X0069377Y0065918D02*
+X0067105Y0065918D01*
+X0067815Y0067117D02*
+X0069017Y0067117D01*
+X0069103Y0068315D02*
+X0068847Y0068315D01*
+X0069065Y0069514D02*
+X0069647Y0069514D01*
+X0069065Y0070712D02*
+X0071134Y0070712D01*
+X0075353Y0069514D02*
+X0076854Y0069514D01*
+X0075897Y0068315D02*
+X0085395Y0068315D01*
+X0088505Y0067117D02*
+X0075983Y0067117D01*
+X0075623Y0065918D02*
+X0088505Y0065918D01*
+X0092143Y0059926D02*
+X0092442Y0059926D01*
+X0092442Y0058727D02*
+X0091565Y0058727D01*
+X0091565Y0057529D02*
+X0092442Y0057529D01*
+X0092442Y0056330D02*
+X0091561Y0056330D01*
+X0090910Y0055132D02*
+X0092442Y0055132D01*
+X0092442Y0053933D02*
+X0089180Y0053933D01*
+X0088062Y0052734D02*
+X0092427Y0052734D01*
+X0091228Y0051536D02*
+X0086863Y0051536D01*
+X0086198Y0046742D02*
+X0002568Y0046742D01*
+X0002568Y0045543D02*
+X0084999Y0045543D01*
+X0084394Y0044345D02*
+X0002568Y0044345D01*
+X0002568Y0043146D02*
+X0084331Y0043146D01*
+X0084331Y0041948D02*
+X0002568Y0041948D01*
+X0002568Y0040749D02*
+X0084331Y0040749D01*
+X0084331Y0039551D02*
+X0002568Y0039551D01*
+X0002568Y0038352D02*
+X0084331Y0038352D01*
+X0084331Y0037154D02*
+X0002568Y0037154D01*
+X0002568Y0035955D02*
+X0084331Y0035955D01*
+X0084331Y0034757D02*
+X0002568Y0034757D01*
+X0002568Y0033558D02*
+X0084331Y0033558D01*
+X0084331Y0032360D02*
+X0002568Y0032360D01*
+X0002568Y0031161D02*
+X0084331Y0031161D01*
+X0082900Y0029963D02*
+X0082100Y0029963D01*
+X0082100Y0028764D02*
+X0082900Y0028764D01*
+X0082900Y0027566D02*
+X0082100Y0027566D01*
+X0082100Y0026367D02*
+X0082900Y0026367D01*
+X0082100Y0025169D02*
+X0002568Y0025169D01*
+X0002568Y0026367D02*
+X0077400Y0026367D01*
+X0077853Y0027566D02*
+X0002568Y0027566D01*
+X0002568Y0028764D02*
+X0079052Y0028764D01*
+X0080250Y0029963D02*
+X0002568Y0029963D01*
+X0002568Y0023970D02*
+X0077400Y0023970D01*
+X0077516Y0022772D02*
+X0002568Y0022772D01*
+X0002568Y0021573D02*
+X0078714Y0021573D01*
+X0079717Y0020375D02*
+X0002568Y0020375D01*
+X0002568Y0019176D02*
+X0078518Y0019176D01*
+X0077320Y0017978D02*
+X0002568Y0017978D01*
+X0002568Y0016779D02*
+X0076731Y0016779D01*
+X0076731Y0015581D02*
+X0002568Y0015581D01*
+X0002568Y0014382D02*
+X0076731Y0014382D01*
+X0076731Y0013184D02*
+X0002568Y0013184D01*
+X0002568Y0011985D02*
+X0077357Y0011985D01*
+X0078555Y0010787D02*
+X0002568Y0010787D01*
+X0002568Y0009588D02*
+X0079754Y0009588D01*
+X0079163Y0055132D02*
+X0077963Y0055132D01*
+X0077963Y0056330D02*
+X0079163Y0056330D01*
+X0079163Y0057529D02*
+X0077963Y0057529D01*
+X0077963Y0058727D02*
+X0079163Y0058727D01*
+X0079163Y0061124D02*
+X0077963Y0061124D01*
+X0077963Y0062323D02*
+X0079163Y0062323D01*
+X0079163Y0063521D02*
+X0077963Y0063521D01*
+X0077963Y0064720D02*
+X0079163Y0064720D01*
+X0074923Y0064720D02*
+X0074622Y0064720D01*
+X0100000Y0086142D02*
+X0117264Y0086142D01*
+X0117264Y0072559D01*
+X0117264Y0067500D01*
+X0127500Y0067500D01*
+X0136063Y0067500D01*
+X0137036Y0050337D02*
+X0163731Y0050337D01*
+X0163778Y0049139D02*
+X0138099Y0049139D01*
+X0138471Y0047940D02*
+X0164274Y0047940D01*
+X0165518Y0046742D02*
+X0138408Y0046742D01*
+X0129872Y0088690D02*
+X0194782Y0088690D01*
+X0193583Y0089888D02*
+X0129872Y0089888D01*
+X0124941Y0097953D02*
+X0190000Y0097953D01*
+X0162500Y0122500D02*
+X0161240Y0122500D01*
+X0088505Y0130638D02*
+X0088441Y0130638D01*
+X0088505Y0131836D02*
+X0087614Y0131836D01*
+X0088218Y0133035D02*
+X0088505Y0133035D01*
+X0088505Y0134233D02*
+X0088218Y0134233D01*
+X0088218Y0135432D02*
+X0088505Y0135432D01*
+X0088505Y0136630D02*
+X0088218Y0136630D01*
+X0088218Y0137829D02*
+X0088505Y0137829D01*
+X0088505Y0139027D02*
+X0088218Y0139027D01*
+X0088505Y0140226D02*
+X0087678Y0140226D01*
+X0087550Y0141424D02*
+X0088505Y0141424D01*
+X0088505Y0142623D02*
+X0087550Y0142623D01*
+X0082500Y0136063D02*
+X0082500Y0127500D01*
+X0087736Y0127500D01*
+X0087736Y0117441D01*
+X0087500Y0117500D02*
+X0087500Y0110000D01*
+X0080059Y0109764D02*
+X0075000Y0109764D01*
+X0072500Y0112264D01*
+X0072500Y0116063D01*
+X0066782Y0116256D02*
+X0065538Y0116256D01*
+X0066737Y0115057D02*
+X0066782Y0115057D01*
+X0066782Y0117454D02*
+X0063974Y0117454D01*
+X0063168Y0118653D02*
+X0066782Y0118653D01*
+X0067079Y0119851D02*
+X0063168Y0119851D01*
+X0063944Y0121050D02*
+X0067471Y0121050D01*
+X0067450Y0122248D02*
+X0064065Y0122248D01*
+X0064065Y0123447D02*
+X0071900Y0123447D01*
+X0071900Y0124537D02*
+X0067450Y0124537D01*
+X0067450Y0126746D01*
+X0067580Y0127229D01*
+X0067830Y0127663D01*
+X0068184Y0128016D01*
+X0068617Y0128267D01*
+X0069100Y0128396D01*
+X0071900Y0128396D01*
+X0071900Y0124537D01*
+X0071900Y0124645D02*
+X0073100Y0124645D01*
+X0073100Y0123447D02*
+X0084568Y0123447D01*
+X0084568Y0122248D02*
+X0077550Y0122248D01*
+X0077529Y0121050D02*
+X0077590Y0121050D01*
+X0077550Y0124645D02*
+X0081112Y0124645D01*
+X0079756Y0125844D02*
+X0077550Y0125844D01*
+X0077470Y0127042D02*
+X0079331Y0127042D01*
+X0079331Y0128241D02*
+X0076428Y0128241D01*
+X0079331Y0129439D02*
+X0063742Y0129439D01*
+X0064065Y0128241D02*
+X0068572Y0128241D01*
+X0067530Y0127042D02*
+X0064065Y0127042D01*
+X0064065Y0125844D02*
+X0067450Y0125844D01*
+X0067450Y0124645D02*
+X0064065Y0124645D01*
+X0060000Y0125000D02*
+X0060000Y0113750D01*
+X0060000Y0112500D01*
+X0060000Y0113750D02*
+X0063563Y0113750D01*
+X0075423Y0101890D01*
+X0080059Y0101890D01*
+X0080059Y0099921D02*
+X0050669Y0099921D01*
+X0050669Y0106654D01*
+X0040000Y0106654D01*
+X0040000Y0106063D01*
+X0045141Y0110263D02*
+X0045650Y0110263D01*
+X0046013Y0111462D02*
+X0045050Y0111462D01*
+X0045050Y0112660D02*
+X0046013Y0112660D01*
+X0046831Y0113859D02*
+X0040600Y0113859D01*
+X0040000Y0113937D02*
+X0040000Y0113346D01*
+X0050669Y0113346D01*
+X0050000Y0112500D02*
+X0050000Y0125000D01*
+X0046831Y0124645D02*
+X0043488Y0124645D01*
+X0043491Y0124656D02*
+X0043491Y0125000D01*
+X0043491Y0125344D01*
+X0043356Y0126018D01*
+X0043093Y0126653D01*
+X0042711Y0127225D01*
+X0042225Y0127711D01*
+X0041653Y0128093D01*
+X0041018Y0128356D01*
+X0040344Y0128491D01*
+X0040000Y0128491D01*
+X0039656Y0128491D01*
+X0038982Y0128356D01*
+X0038347Y0128093D01*
+X0037775Y0127711D01*
+X0037289Y0127225D01*
+X0036907Y0126653D01*
+X0036644Y0126018D01*
+X0036509Y0125344D01*
+X0036509Y0125000D01*
+X0036509Y0124656D01*
+X0036644Y0123982D01*
+X0036907Y0123347D01*
+X0037289Y0122775D01*
+X0037775Y0122289D01*
+X0038347Y0121907D01*
+X0038982Y0121644D01*
+X0039656Y0121509D01*
+X0040000Y0121509D01*
+X0040344Y0121509D01*
+X0041018Y0121644D01*
+X0041653Y0121907D01*
+X0042225Y0122289D01*
+X0042711Y0122775D01*
+X0043093Y0123347D01*
+X0043356Y0123982D01*
+X0043491Y0124656D01*
+X0043491Y0125000D02*
+X0040000Y0125000D01*
+X0043491Y0125000D01*
+X0043391Y0125844D02*
+X0046604Y0125844D01*
+X0046604Y0127042D02*
+X0042833Y0127042D01*
+X0041297Y0128241D02*
+X0046604Y0128241D01*
+X0047107Y0129439D02*
+X0020668Y0129439D01*
+X0020668Y0128241D02*
+X0038703Y0128241D01*
+X0040000Y0128241D02*
+X0040000Y0128241D01*
+X0040000Y0128491D02*
+X0040000Y0125000D01*
+X0040000Y0125000D01*
+X0040000Y0125000D01*
+X0040000Y0128491D01*
+X0040000Y0127042D02*
+X0040000Y0127042D01*
+X0040000Y0125844D02*
+X0040000Y0125844D01*
+X0040000Y0125000D02*
+X0040000Y0125000D01*
+X0040000Y0121509D01*
+X0040000Y0125000D01*
+X0040000Y0125000D01*
+X0036509Y0125000D01*
+X0040000Y0125000D01*
+X0040000Y0124645D02*
+X0040000Y0124645D01*
+X0040000Y0123447D02*
+X0040000Y0123447D01*
+X0040000Y0122248D02*
+X0040000Y0122248D01*
+X0042164Y0122248D02*
+X0046604Y0122248D01*
+X0046604Y0123447D02*
+X0043135Y0123447D01*
+X0046772Y0121050D02*
+X0020668Y0121050D01*
+X0020668Y0122248D02*
+X0037836Y0122248D01*
+X0036865Y0123447D02*
+X0020668Y0123447D01*
+X0020668Y0124645D02*
+X0036512Y0124645D01*
+X0036609Y0125844D02*
+X0020668Y0125844D01*
+X0020668Y0127042D02*
+X0037167Y0127042D01*
+X0036600Y0118396D02*
+X0036117Y0118267D01*
+X0035684Y0118016D01*
+X0035330Y0117663D01*
+X0035080Y0117229D01*
+X0034950Y0116746D01*
+X0034950Y0114537D01*
+X0039400Y0114537D01*
+X0039400Y0118396D01*
+X0036600Y0118396D01*
+X0035210Y0117454D02*
+X0020668Y0117454D01*
+X0020668Y0116256D02*
+X0034950Y0116256D01*
+X0034950Y0115057D02*
+X0020668Y0115057D01*
+X0020668Y0113859D02*
+X0039400Y0113859D01*
+X0039400Y0115057D02*
+X0040600Y0115057D01*
+X0040600Y0116256D02*
+X0039400Y0116256D01*
+X0039400Y0117454D02*
+X0040600Y0117454D01*
+X0044790Y0117454D02*
+X0046831Y0117454D01*
+X0046831Y0118653D02*
+X0020668Y0118653D01*
+X0020668Y0119851D02*
+X0046831Y0119851D01*
+X0046093Y0116256D02*
+X0045050Y0116256D01*
+X0045050Y0115057D02*
+X0046013Y0115057D01*
+X0034950Y0112660D02*
+X0020668Y0112660D01*
+X0020668Y0111462D02*
+X0034950Y0111462D01*
+X0034859Y0110263D02*
+X0020668Y0110263D01*
+X0020668Y0109065D02*
+X0034282Y0109065D01*
+X0034282Y0107866D02*
+X0020668Y0107866D01*
+X0020668Y0106668D02*
+X0034282Y0106668D01*
+X0034282Y0105469D02*
+X0020668Y0105469D01*
+X0020668Y0104270D02*
+X0034282Y0104270D01*
+X0034282Y0103072D02*
+X0020668Y0103072D01*
+X0020668Y0101873D02*
+X0034848Y0101873D01*
+X0045152Y0101873D02*
+X0047039Y0101873D01*
+X0047501Y0100675D02*
+X0020668Y0100675D01*
+X0020668Y0099476D02*
+X0047501Y0099476D01*
+X0047920Y0098278D02*
+X0020668Y0098278D01*
+X0021339Y0097079D02*
+X0049250Y0097079D01*
+X0060000Y0105000D02*
+X0066250Y0105000D01*
+X0060000Y0105000D02*
+X0060000Y0107500D01*
+X0065693Y0095881D02*
+X0022537Y0095881D01*
+X0021979Y0077903D02*
+X0002568Y0077903D01*
+X0002568Y0076705D02*
+X0021979Y0076705D01*
+X0022047Y0075506D02*
+X0002568Y0075506D01*
+X0002568Y0074308D02*
+X0021979Y0074308D01*
+X0021979Y0073109D02*
+X0002568Y0073109D01*
+X0002568Y0071911D02*
+X0021979Y0071911D01*
+X0066063Y0092500D02*
+X0076516Y0097953D01*
+X0080059Y0097953D01*
+X0073100Y0125844D02*
+X0071900Y0125844D01*
+X0071900Y0127042D02*
+X0073100Y0127042D01*
+X0073100Y0128241D02*
+X0071900Y0128241D01*
+X0077386Y0131836D02*
+X0020668Y0131836D01*
+X0020668Y0130638D02*
+X0055673Y0130638D01*
+X0051663Y0129439D02*
+X0050463Y0129439D01*
+X0050463Y0128241D02*
+X0051663Y0128241D01*
+X0062201Y0130638D02*
+X0079331Y0130638D01*
+X0031124Y0158203D02*
+X0018168Y0158203D01*
+X0018168Y0157005D02*
+X0018961Y0157005D01*
+X0018168Y0159402D02*
+X0029680Y0159402D01*
+X0021782Y0149814D02*
+X0020732Y0149814D01*
+X0020668Y0148615D02*
+X0021782Y0148615D01*
+X0021782Y0147417D02*
+X0020668Y0147417D01*
+X0020668Y0146218D02*
+X0021782Y0146218D01*
+X0022133Y0145020D02*
+X0020668Y0145020D01*
+X0020668Y0143821D02*
+X0022450Y0143821D01*
+X0022450Y0142623D02*
+X0020668Y0142623D01*
+X0026900Y0139027D02*
+X0028100Y0139027D01*
+X0028100Y0137829D02*
+X0026900Y0137829D01*
+X0026900Y0136630D02*
+X0028100Y0136630D01*
+X0210011Y0059926D02*
+X0213124Y0059926D01*
+X0213249Y0058727D02*
+X0210770Y0058727D01*
+X0283559Y0147417D02*
+X0286132Y0147417D01*
+X0286132Y0148615D02*
+X0285235Y0148615D01*
+X0286116Y0149814D02*
+X0286132Y0149814D01*
+X0285972Y0166593D02*
+X0286132Y0166593D01*
+X0286132Y0198953D02*
+X0268797Y0198953D01*
+X0269085Y0200151D02*
+X0286132Y0200151D01*
+X0269295Y0213335D02*
+X0268166Y0213335D01*
+X0275000Y0216437D02*
+X0275000Y0217874D01*
+X0261369Y0201350D02*
+X0260403Y0201350D01*
+X0260403Y0200151D02*
+X0261369Y0200151D01*
+X0261369Y0198953D02*
+X0260403Y0198953D01*
+X0221516Y0182174D02*
+X0218484Y0182174D01*
+X0219683Y0180975D02*
+X0220317Y0180975D01*
+X0240565Y0164196D02*
+X0242481Y0164196D01*
+X0242481Y0162998D02*
+X0240960Y0162998D01*
+X0240919Y0161799D02*
+X0242481Y0161799D01*
+X0242481Y0160601D02*
+X0240429Y0160601D01*
+X0241527Y0159402D02*
+X0239142Y0159402D01*
+D32*
+X0246250Y0154980D02*
+X0246250Y0177500D01*
+X0262500Y0162500D02*
+X0257500Y0157500D01*
+X0257500Y0154980D01*
+X0262500Y0162500D02*
+X0277500Y0162500D01*
+X0262500Y0140000D02*
+X0257500Y0140000D01*
+X0247500Y0140000D01*
+X0262500Y0140000D02*
+X0267500Y0135000D01*
+X0267500Y0085000D01*
+X0262500Y0080000D01*
+X0240000Y0080000D01*
+X0237500Y0080000D01*
+X0237500Y0077500D01*
+X0242500Y0077500D01*
+X0242500Y0072500D01*
+X0237500Y0077500D02*
+X0222500Y0077500D01*
+X0222500Y0073701D01*
+X0228402Y0062201D02*
+X0228402Y0060000D01*
+X0244094Y0060000D01*
+X0245000Y0060000D01*
+X0245000Y0050000D01*
+X0167500Y0050000D01*
+X0167500Y0075000D01*
+X0127500Y0075000D01*
+X0245000Y0050000D02*
+X0266250Y0050000D01*
+X0267500Y0047500D01*
+X0255906Y0060000D02*
+X0255906Y0072500D01*
+X0255000Y0072500D02*
+X0267500Y0072500D01*
+D33*
+X0267500Y0075000D01*
+X0272500Y0080000D01*
+X0275000Y0080000D01*
+X0267510Y0072500D02*
+X0267500Y0072500D01*
+X0244094Y0072500D02*
+X0242500Y0072500D01*
+X0240000Y0080000D02*
+X0240000Y0081270D01*
+X0237500Y0094980D02*
+X0237500Y0095000D01*
+X0237500Y0094980D02*
+X0240000Y0096230D01*
+X0237500Y0098701D02*
+X0255157Y0098701D01*
+X0255157Y0111299D02*
+X0240000Y0111299D01*
+X0222500Y0073701D02*
+X0222500Y0063201D01*
+X0267500Y0047500D02*
+X0267500Y0037500D01*
+X0277500Y0037500D01*
+X0277500Y0027500D02*
+X0267500Y0027500D01*
+X0267500Y0017500D01*
+X0277500Y0017500D01*
+X0277500Y0047500D02*
+X0267500Y0047500D01*
+X0127500Y0067500D02*
+X0127500Y0075000D01*
+D34*
+X0135000Y0047500D03*
+X0072500Y0067500D03*
+X0058750Y0081250D03*
+X0040000Y0125000D03*
+X0072500Y0137500D03*
+X0080000Y0188750D03*
+X0152500Y0175000D03*
+X0170000Y0195000D03*
+X0212500Y0207500D03*
+X0237500Y0162500D03*
+X0225000Y0135000D03*
+X0240000Y0122500D03*
+X0277500Y0137500D03*
+X0275000Y0080000D03*
+X0255000Y0032500D03*
+X0207500Y0057500D03*
+X0151250Y0112500D03*
+X0150000Y0131250D03*
+D35*
+X0177500Y0147500D03*
+X0197500Y0147500D03*
+X0197500Y0157500D03*
+X0190000Y0130000D03*
+X0246250Y0177500D03*
+X0082500Y0155000D03*
+X0066250Y0105000D03*
+M02*

+ 54 - 0
tests/gerber_files/simple1.gbr

@@ -0,0 +1,54 @@
+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,0.047244X0.078740*%
+%ADD11C,0.024000*%
+%LNCOPPER1*%
+G90*
+G70*
+G54D10*
+X2940Y1051D03*
+X2940Y941D03*
+G54D11*
+X2438Y839D02*
+X2440Y1023D01*
+D02*
+X2940Y907D02*
+X2941Y839D01*
+D02*
+X2941Y839D02*
+X2438Y839D01*
+D02*
+X2941Y1239D02*
+X2940Y1085D01*
+D02*
+X2438Y1239D02*
+X2941Y1239D01*
+D02*
+X2440Y1126D02*
+X2438Y1239D01*
+G36*
+X2418Y1064D02*
+X2461Y1064D01*
+X2461Y1017D01*
+X2418Y1017D01*
+X2418Y1064D01*
+G37*
+D02*
+G36*
+X2418Y1131D02*
+X2461Y1131D01*
+X2461Y1084D01*
+X2418Y1084D01*
+X2418Y1131D01*
+G37*
+D02*
+G04 End of Copper1*
+M02*

+ 3045 - 0
tests/gerber_parsing_profiling/gerber1.gbr

@@ -0,0 +1,3045 @@
+G04 Generated by Ultiboard 12.0 *
+%FSLAX25Y25*%
+%MOMM*%
+
+%ADD10C,0.00001*%
+%ADD11C,0.25400*%
+%ADD12C,0.50800*%
+%ADD13C,1.90500*%
+%ADD14C,0.12700*%
+%ADD15C,2.00000*%
+%ADD16R,1.80000X1.15000*%
+%ADD17C,1.60884*%
+%ADD18R,0.67742X0.67742*%
+%ADD19C,0.84658*%
+%ADD20R,0.76200X0.76200*%
+%ADD21C,0.50800*%
+%ADD22R,1.27000X1.27000*%
+%ADD23R,2.70000X1.15000*%
+%ADD24R,1.15000X1.45000*%
+%ADD25R,1.15000X2.70000*%
+%ADD26R,0.52908X0.52908*%
+%ADD27C,0.99492*%
+%ADD28C,1.80000*%
+%ADD29R,1.80000X1.80000*%
+%ADD30R,2.57000X2.80000*%
+%ADD31C,1.98984*%
+
+
+G04 ColorRGB 002000 for the following layer *
+%LNCopper Top*%
+%LPD*%
+G54D10*
+G36*
+X1590549Y63807D02*
+X1590549Y63807D01*
+X3936193Y63807D01*
+X3936193Y6436193D01*
+X63807Y6436193D01*
+X63807Y63807D01*
+X1254251Y63807D01*
+G75*
+D01*
+G02X1230093Y113800I39649J49993*
+G01*
+X1230093Y113800D01*
+X1230093Y185606D01*
+X1354206Y185606D01*
+X1354206Y63807D01*
+X1490594Y63807D01*
+X1490594Y185606D01*
+X1614707Y185606D01*
+X1614707Y113800D01*
+G75*
+D01*
+G02X1590549Y63807I-63807J0*
+G01*
+D02*
+G37*
+%LPC*%
+G36*
+X2821307Y4787900D02*
+X2821307Y4787900D01*
+X2821307Y4717860D01*
+G75*
+D01*
+G02X2855900Y4724830I34593J-82359*
+G01*
+X2855900Y4724830D01*
+X2932100Y4724830D01*
+G74*
+D01*
+G02X3021430Y4635500I0J89330*
+G01*
+X3021430Y4635500D01*
+X3021430Y4559300D01*
+G75*
+D01*
+G02X2940855Y4470400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021430Y4381500I8755J88900*
+G01*
+X3021430Y4381500D01*
+X3021430Y4379662D01*
+X3024710Y4382942D01*
+X3024710Y5027758D01*
+X2858598Y5193870D01*
+X1490507Y5193870D01*
+X1490507Y5148200D01*
+G75*
+D01*
+G02X1426700Y5084393I-63807J0*
+G01*
+X1426700Y5084393D01*
+X1311700Y5084393D01*
+G75*
+D01*
+G02X1247893Y5148200I0J63807*
+G01*
+X1247893Y5148200D01*
+X1247893Y5418200D01*
+G75*
+D01*
+G02X1311700Y5482007I63807J0*
+G01*
+X1311700Y5482007D01*
+X1426700Y5482007D01*
+G74*
+D01*
+G02X1490507Y5418200I0J63807*
+G01*
+X1490507Y5418200D01*
+X1490507Y5372530D01*
+X2022842Y5372530D01*
+G75*
+D01*
+G02X2244358Y5372530I110758J120269*
+G01*
+X2244358Y5372530D01*
+X2530842Y5372530D01*
+G75*
+D01*
+G02X2752358Y5372530I110758J120269*
+G01*
+X2752358Y5372530D01*
+X2895490Y5372530D01*
+G75*
+D01*
+G02X2928856Y5366109I110J-89330*
+G01*
+G74*
+D01*
+G02X2958845Y5346286I33257J82908*
+G01*
+X2958845Y5346286D01*
+X3177126Y5128005D01*
+G74*
+D01*
+G02X3203370Y5064760I63085J63245*
+G01*
+X3203370Y5064760D01*
+X3203370Y4345940D01*
+G75*
+D01*
+G02X3177126Y4282695I-89330J1*
+G01*
+X3177126Y4282695D01*
+X3047745Y4153314D01*
+G74*
+D01*
+G02X3021121Y4134921I63246J63084*
+G01*
+G74*
+D01*
+G02X3021430Y4127500I89020J7424*
+G01*
+X3021430Y4127500D01*
+X3021430Y4051300D01*
+G75*
+D01*
+G02X2940855Y3962400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021148Y3880588I8755J88900*
+G01*
+X3021148Y3880588D01*
+X2939188Y3880588D01*
+X2939188Y3962252D01*
+G74*
+D01*
+G02X2932100Y3961970I7092J89047*
+G01*
+X2932100Y3961970D01*
+X2855900Y3961970D01*
+G75*
+D01*
+G02X2848812Y3962252I4J89330*
+G01*
+X2848812Y3962252D01*
+X2848812Y3880588D01*
+X2810004Y3880588D01*
+G74*
+D01*
+G02X2740855Y3835400I77904J43711*
+G01*
+G74*
+D01*
+G02X2810004Y3790212I8756J88900*
+G01*
+X2810004Y3790212D01*
+X2848812Y3790212D01*
+X2848812Y3708548D01*
+G75*
+D01*
+G02X2855900Y3708830I7092J-89047*
+G01*
+X2855900Y3708830D01*
+X2932100Y3708830D01*
+G74*
+D01*
+G02X2939188Y3708548I4J89330*
+G01*
+X2939188Y3708548D01*
+X2939188Y3790212D01*
+X3021148Y3790212D01*
+G74*
+D01*
+G02X2940855Y3708400I89047J7087*
+G01*
+G74*
+D01*
+G02X3021430Y3619500I8755J88900*
+G01*
+X3021430Y3619500D01*
+X3021430Y3543300D01*
+G75*
+D01*
+G02X2940855Y3454400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021430Y3365500I8755J88900*
+G01*
+X3021430Y3365500D01*
+X3021430Y3289300D01*
+G75*
+D01*
+G02X3001389Y3232918I-89330J0*
+G01*
+X3001389Y3232918D01*
+X3318326Y3232918D01*
+G75*
+D01*
+G02X3364423Y3252607I46097J-44117*
+G01*
+X3364423Y3252607D01*
+X3412122Y3252607D01*
+G75*
+D01*
+G02X3496724Y3252607I42301J-153806*
+G01*
+X3496724Y3252607D01*
+X3544423Y3252607D01*
+G74*
+D01*
+G02X3608231Y3188800I1J63807*
+G01*
+X3608231Y3188800D01*
+X3608231Y3141101D01*
+G75*
+D01*
+G02X3608231Y3056499I-153807J-42301*
+G01*
+X3608231Y3056499D01*
+X3608231Y3008800D01*
+G75*
+D01*
+G02X3544423Y2944993I-63808J1*
+G01*
+X3544423Y2944993D01*
+X3523668Y2944993D01*
+G74*
+D01*
+G02X3429023Y2913882I94645J128407*
+G01*
+X3429023Y2913882D01*
+X3001389Y2913882D01*
+G74*
+D01*
+G02X3021430Y2857500I69289J56382*
+G01*
+X3021430Y2857500D01*
+X3021430Y2781300D01*
+G75*
+D01*
+G02X2940855Y2692400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021148Y2610588I8755J88900*
+G01*
+X3021148Y2610588D01*
+X2939188Y2610588D01*
+X2939188Y2692252D01*
+G74*
+D01*
+G02X2932100Y2691970I7092J89047*
+G01*
+X2932100Y2691970D01*
+X2855900Y2691970D01*
+G75*
+D01*
+G02X2848812Y2692252I4J89330*
+G01*
+X2848812Y2692252D01*
+X2848812Y2610588D01*
+X2810004Y2610588D01*
+G74*
+D01*
+G02X2740855Y2565400I77904J43711*
+G01*
+G74*
+D01*
+G02X2810004Y2520212I8756J88900*
+G01*
+X2810004Y2520212D01*
+X2848812Y2520212D01*
+X2848812Y2438548D01*
+G75*
+D01*
+G02X2855900Y2438830I7092J-89047*
+G01*
+X2855900Y2438830D01*
+X2932100Y2438830D01*
+G74*
+D01*
+G02X2939188Y2438548I4J89330*
+G01*
+X2939188Y2438548D01*
+X2939188Y2520212D01*
+X3021148Y2520212D01*
+G74*
+D01*
+G02X2940855Y2438400I89047J7087*
+G01*
+G74*
+D01*
+G02X3021430Y2349500I8755J88900*
+G01*
+X3021430Y2349500D01*
+X3021430Y2273300D01*
+G75*
+D01*
+G02X2940855Y2184400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021430Y2095500I8755J88900*
+G01*
+X3021430Y2095500D01*
+X3021430Y2019300D01*
+G75*
+D01*
+G02X2940855Y1930400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021430Y1841500I8755J88900*
+G01*
+X3021430Y1841500D01*
+X3021430Y1765300D01*
+G75*
+D01*
+G02X2932100Y1675970I-89330J0*
+G01*
+X2932100Y1675970D01*
+X2855900Y1675970D01*
+G75*
+D01*
+G02X2821430Y1682888I-1J89330*
+G01*
+X2821430Y1682888D01*
+X2821430Y1638300D01*
+G75*
+D01*
+G02X2732100Y1548970I-89330J0*
+G01*
+X2732100Y1548970D01*
+X2655900Y1548970D01*
+G75*
+D01*
+G02X2566570Y1638300I0J89330*
+G01*
+X2566570Y1638300D01*
+X2566570Y1714500D01*
+G74*
+D01*
+G02X2647145Y1803400I89330J0*
+G01*
+G75*
+D01*
+G02X2566570Y1892300I8755J88900*
+G01*
+X2566570Y1892300D01*
+X2566570Y1968500D01*
+G74*
+D01*
+G02X2647145Y2057400I89330J0*
+G01*
+G74*
+D01*
+G02X2582720Y2095070I8755J88900*
+G01*
+X2582720Y2095070D01*
+X2045877Y2095070D01*
+G75*
+D01*
+G02X1982664Y2121234I-47J89330*
+G01*
+X1982664Y2121234D01*
+X1562434Y2541464D01*
+G75*
+D01*
+G02X1536270Y2604716I63166J63166*
+G01*
+X1536270Y2604716D01*
+X1536270Y2878293D01*
+X1490600Y2878293D01*
+G75*
+D01*
+G02X1427407Y2933270I0J63807*
+G01*
+X1427407Y2933270D01*
+X725170Y2933270D01*
+G75*
+D01*
+G02X701417Y2936486I0J89330*
+G01*
+G74*
+D01*
+G02X662004Y2959434I23752J86113*
+G01*
+X662004Y2959434D01*
+X597330Y3024108D01*
+X597330Y2563005D01*
+G74*
+D01*
+G02X648212Y2472271I55458J90734*
+G01*
+X648212Y2472271D01*
+X648212Y2404529D01*
+G75*
+D01*
+G02X597330Y2313795I-106341J0*
+G01*
+X597330Y2313795D01*
+X597330Y2297269D01*
+G75*
+D01*
+G02X597330Y2071531I-89329J-112869*
+G01*
+X597330Y2071531D01*
+X597330Y2043269D01*
+G75*
+D01*
+G02X597330Y1817531I-89329J-112869*
+G01*
+X597330Y1817531D01*
+X597330Y1789269D01*
+G75*
+D01*
+G02X597330Y1563531I-89329J-112869*
+G01*
+X597330Y1563531D01*
+X597330Y1486330D01*
+X1473200Y1486330D01*
+G74*
+D01*
+G02X1536445Y1460086I1J89330*
+G01*
+X1536445Y1460086D01*
+X1815686Y1180845D01*
+G74*
+D01*
+G02X1827393Y1166446I63083J63248*
+G01*
+X1827393Y1166446D01*
+X1827393Y1172700D01*
+G75*
+D01*
+G02X1891200Y1236507I63807J0*
+G01*
+X1891200Y1236507D01*
+X1891870Y1236507D01*
+X1891870Y1239969D01*
+G75*
+D01*
+G02X1918034Y1303266I89330J131*
+G01*
+X1918034Y1303266D01*
+X2032334Y1417566D01*
+G74*
+D01*
+G02X2052310Y1432595I63165J63167*
+G01*
+G75*
+D01*
+G02X2309229Y1443730I132090J-78194*
+G01*
+X2309229Y1443730D01*
+X2362157Y1443730D01*
+G75*
+D01*
+G02X2370371Y1443356I48J-89330*
+G01*
+G74*
+D01*
+G02X2425445Y1417486I8172J88955*
+G01*
+X2425445Y1417486D01*
+X2603086Y1239845D01*
+G74*
+D01*
+G02X2629330Y1176600I63085J63245*
+G01*
+X2629330Y1176600D01*
+X2629330Y644332D01*
+G74*
+D01*
+G02X2702712Y517549I89329J136332*
+G01*
+X2702712Y517549D01*
+X2712261Y508000D01*
+X2702712Y498451D01*
+G75*
+D01*
+G02X2413000Y405837I-162711J9547*
+G01*
+G75*
+D01*
+G02X2196670Y644332I-126997J102166*
+G01*
+X2196670Y644332D01*
+X2196670Y693893D01*
+X2196000Y693893D01*
+G75*
+D01*
+G02X2140699Y725870I0J63807*
+G01*
+X2140699Y725870D01*
+X2126501Y725870D01*
+G74*
+D01*
+G02X2071200Y693893I55301J31830*
+G01*
+X2071200Y693893D01*
+X1891200Y693893D01*
+G75*
+D01*
+G02X1835899Y725870I0J63807*
+G01*
+X1835899Y725870D01*
+X1826450Y725870D01*
+G75*
+D01*
+G02X1763234Y752034I-50J89330*
+G01*
+X1763234Y752034D01*
+X1689434Y825834D01*
+G75*
+D01*
+G02X1663270Y889088I63166J63166*
+G01*
+X1663270Y889088D01*
+X1663270Y1080598D01*
+X1625968Y1117900D01*
+G74*
+D01*
+G02X1625969Y1117600I76567J405*
+G01*
+X1625969Y1117600D01*
+X1625969Y736600D01*
+G75*
+D01*
+G02X1567930Y662307I-76569J0*
+G01*
+G74*
+D01*
+G02X1550900Y659993I17028J61493*
+G01*
+X1550900Y659993D01*
+X1293900Y659993D01*
+G75*
+D01*
+G02X1276871Y662307I-1J63807*
+G01*
+G74*
+D01*
+G02X1266963Y665508I18531J74292*
+G01*
+X1266963Y665508D01*
+X758963Y868708D01*
+G75*
+D01*
+G02X710831Y939939I28436J71092*
+G01*
+X710831Y939939D01*
+X710831Y1117600D01*
+G75*
+D01*
+G02X787400Y1194169I76569J0*
+G01*
+X787400Y1194169D01*
+X1549400Y1194169D01*
+G74*
+D01*
+G02X1549700Y1194168I105J76569*
+G01*
+X1549700Y1194168D01*
+X1436198Y1307670D01*
+X508000Y1307670D01*
+G75*
+D01*
+G02X418670Y1397000I0J89330*
+G01*
+X418670Y1397000D01*
+X418670Y1563531D01*
+G75*
+D01*
+G02X418670Y1789269I89329J112869*
+G01*
+X418670Y1789269D01*
+X418670Y1817531D01*
+G75*
+D01*
+G02X418670Y2043269I89329J112869*
+G01*
+X418670Y2043269D01*
+X418670Y2071531D01*
+G75*
+D01*
+G02X418670Y2297269I89329J112869*
+G01*
+X418670Y2297269D01*
+X418670Y2313795D01*
+G75*
+D01*
+G02X367788Y2404529I55458J90734*
+G01*
+X367788Y2404529D01*
+X367788Y2472271D01*
+G74*
+D01*
+G02X418670Y2563005I106341J0*
+G01*
+X418670Y2563005D01*
+X418670Y4408331D01*
+G75*
+D01*
+G02X418670Y4634069I89329J112869*
+G01*
+X418670Y4634069D01*
+X418670Y4650595D01*
+G75*
+D01*
+G02X367788Y4741329I55458J90734*
+G01*
+X367788Y4741329D01*
+X367788Y4809071D01*
+G74*
+D01*
+G02X418670Y4899805I106341J0*
+G01*
+X418670Y4899805D01*
+X418670Y5029034D01*
+G75*
+D01*
+G02X444834Y5092366I89330J166*
+G01*
+X444834Y5092366D01*
+X698834Y5346366D01*
+G74*
+D01*
+G02X749888Y5371705I63166J63166*
+G01*
+G75*
+D01*
+G02X762000Y5372530I12112J-88504*
+G01*
+X762000Y5372530D01*
+X947893Y5372530D01*
+X947893Y5418200D01*
+G75*
+D01*
+G02X1011700Y5482007I63807J0*
+G01*
+X1011700Y5482007D01*
+X1126700Y5482007D01*
+G74*
+D01*
+G02X1190507Y5418200I0J63807*
+G01*
+X1190507Y5418200D01*
+X1190507Y5148200D01*
+G75*
+D01*
+G02X1126700Y5084393I-63807J0*
+G01*
+X1126700Y5084393D01*
+X1011700Y5084393D01*
+G75*
+D01*
+G02X947893Y5148200I0J63807*
+G01*
+X947893Y5148200D01*
+X947893Y5193870D01*
+X799002Y5193870D01*
+X597330Y4992198D01*
+X597330Y4899805D01*
+G74*
+D01*
+G02X648212Y4809071I55458J90734*
+G01*
+X648212Y4809071D01*
+X648212Y4741329D01*
+G75*
+D01*
+G02X597330Y4650595I-106341J0*
+G01*
+X597330Y4650595D01*
+X597330Y4634069D01*
+G75*
+D01*
+G02X597330Y4408331I-89329J-112869*
+G01*
+X597330Y4408331D01*
+X597330Y4379992D01*
+X646904Y4429566D01*
+G74*
+D01*
+G02X677608Y4449623I63166J63166*
+G01*
+G75*
+D01*
+G02X710070Y4455730I32462J-83223*
+G01*
+X710070Y4455730D01*
+X1141809Y4455730D01*
+G75*
+D01*
+G02X1202249Y4648200I128190J65470*
+G01*
+G75*
+D01*
+G02X1382869Y4864530I67754J126998*
+G01*
+X1382869Y4864530D01*
+X2640232Y4864530D01*
+G75*
+D01*
+G02X2669697Y4859565I98J-89330*
+G01*
+G74*
+D01*
+G02X2686444Y4851707I29368J84364*
+G01*
+X2686444Y4851707D01*
+X2757500Y4851707D01*
+G74*
+D01*
+G02X2821307Y4787900I0J63807*
+G01*
+D02*
+G37*
+G36*
+X1202249Y2311400D02*
+G75*
+D01*
+G02X1337751Y2311400I67751J126999*
+G01*
+G75*
+D01*
+G02X1337751Y2057400I-67750J-127000*
+G01*
+G75*
+D01*
+G02X1337751Y1803400I-67750J-127000*
+G01*
+G75*
+D01*
+G02X1202249Y1803400I-67751J-126999*
+G01*
+G75*
+D01*
+G02X1202249Y2057400I67750J127000*
+G01*
+G75*
+D01*
+G02X1202249Y2311400I67750J127000*
+G01*
+D02*
+G37*
+G36*
+X2986100Y5492800D02*
+G75*
+D01*
+G02X2986100Y5492800I163500J0*
+G01*
+D02*
+G37*
+G36*
+X3494100Y5492800D02*
+G75*
+D01*
+G02X3494100Y5492800I163500J0*
+G01*
+D02*
+G37*
+G36*
+X2864000Y2565400D02*
+G75*
+D01*
+G02X2864000Y2565400I30000J0*
+G01*
+D02*
+G37*
+G36*
+X2864000Y3835400D02*
+G75*
+D01*
+G02X2864000Y3835400I30000J0*
+G01*
+D02*
+G37*
+G36*
+X2860893Y359367D02*
+G75*
+D01*
+G02X2727107Y359367I-66893J148633*
+G01*
+X2727107Y359367D01*
+X2794000Y426261D01*
+X2860893Y359367D01*
+D02*
+G37*
+G36*
+X3300593Y351300D02*
+X3300593Y351300D01*
+X3300593Y365783D01*
+X3411383Y365783D01*
+X3411383Y287493D01*
+X3364400Y287493D01*
+G75*
+D01*
+G02X3300593Y351300I0J63807*
+G01*
+D02*
+G37*
+G36*
+X3608207Y351300D02*
+G75*
+D01*
+G02X3544400Y287493I-63807J0*
+G01*
+X3544400Y287493D01*
+X3497417Y287493D01*
+X3497417Y365783D01*
+X3608207Y365783D01*
+X3608207Y351300D01*
+D02*
+G37*
+G36*
+X2727107Y656633D02*
+G75*
+D01*
+G02X2860893Y656633I66893J-148633*
+G01*
+X2860893Y656633D01*
+X2794000Y589739D01*
+X2727107Y656633D01*
+D02*
+G37*
+G36*
+X2734729Y508000D02*
+G75*
+D01*
+G02X2734729Y508000I59271J0*
+G01*
+D02*
+G37*
+G36*
+X2942633Y574893D02*
+G75*
+D01*
+G02X2942633Y441107I-148633J-66893*
+G01*
+X2942633Y441107D01*
+X2875739Y508000D01*
+X2942633Y574893D01*
+D02*
+G37*
+G36*
+X3364400Y530107D02*
+X3364400Y530107D01*
+X3411383Y530107D01*
+X3411383Y451817D01*
+X3300593Y451817D01*
+X3300593Y466300D01*
+G75*
+D01*
+G02X3364400Y530107I63807J0*
+G01*
+D02*
+G37*
+G36*
+X3543730Y873971D02*
+X3543730Y873971D01*
+X3543730Y830107D01*
+X3544400Y830107D01*
+G74*
+D01*
+G02X3608207Y766300I0J63807*
+G01*
+X3608207Y766300D01*
+X3608207Y651300D01*
+G75*
+D01*
+G02X3544400Y587493I-63807J0*
+G01*
+X3544400Y587493D01*
+X3364400Y587493D01*
+G75*
+D01*
+G02X3300593Y651300I0J63807*
+G01*
+X3300593Y651300D01*
+X3300593Y766300D01*
+G75*
+D01*
+G02X3364400Y830107I63807J0*
+G01*
+X3364400Y830107D01*
+X3365070Y830107D01*
+X3365070Y873971D01*
+G75*
+D01*
+G02X3543730Y873971I89330J124828*
+G01*
+D02*
+G37*
+G36*
+X3608207Y466300D02*
+X3608207Y466300D01*
+X3608207Y451817D01*
+X3497417Y451817D01*
+X3497417Y530107D01*
+X3544400Y530107D01*
+G74*
+D01*
+G02X3608207Y466300I0J63807*
+G01*
+D02*
+G37*
+G36*
+X1230093Y393800D02*
+G75*
+D01*
+G02X1293900Y457607I63807J0*
+G01*
+X1293900Y457607D01*
+X1354206Y457607D01*
+X1354206Y321994D01*
+X1230093Y321994D01*
+X1230093Y393800D01*
+D02*
+G37*
+G36*
+X1614707Y393800D02*
+X1614707Y393800D01*
+X1614707Y321994D01*
+X1490594Y321994D01*
+X1490594Y457607D01*
+X1550900Y457607D01*
+G74*
+D01*
+G02X1614707Y393800I0J63807*
+G01*
+D02*
+G37*
+G36*
+X242900Y1009600D02*
+G75*
+D01*
+G02X242900Y1009600I163500J0*
+G01*
+D02*
+G37*
+%LPD*%
+G36*
+X1760600Y2878293D02*
+X1760600Y2878293D01*
+X1714930Y2878293D01*
+X1714930Y2641632D01*
+X2020369Y2336193D01*
+X2082832Y2273730D01*
+X2582720Y2273730D01*
+G74*
+D01*
+G02X2647145Y2311400I73180J51229*
+G01*
+G75*
+D01*
+G02X2566570Y2400300I8755J88900*
+G01*
+X2566570Y2400300D01*
+X2566570Y2476500D01*
+G74*
+D01*
+G02X2647145Y2565400I89330J0*
+G01*
+G75*
+D01*
+G02X2566570Y2654300I8755J88900*
+G01*
+X2566570Y2654300D01*
+X2566570Y2730500D01*
+G74*
+D01*
+G02X2647145Y2819400I89330J0*
+G01*
+G75*
+D01*
+G02X2566570Y2908300I8755J88900*
+G01*
+X2566570Y2908300D01*
+X2566570Y2984500D01*
+G74*
+D01*
+G02X2647145Y3073400I89330J0*
+G01*
+G75*
+D01*
+G02X2566570Y3162300I8755J88900*
+G01*
+X2566570Y3162300D01*
+X2566570Y3238500D01*
+G74*
+D01*
+G02X2586611Y3294882I89330J0*
+G01*
+X2586611Y3294882D01*
+X2184423Y3294882D01*
+G75*
+D01*
+G02X2142122Y3300593I0J159518*
+G01*
+X2142122Y3300593D01*
+X2094423Y3300593D01*
+G75*
+D01*
+G02X2030616Y3364400I0J63807*
+G01*
+X2030616Y3364400D01*
+X2030616Y3412099D01*
+G75*
+D01*
+G02X2030616Y3496701I153806J42301*
+G01*
+X2030616Y3496701D01*
+X2030616Y3544400D01*
+G75*
+D01*
+G02X2094423Y3608207I63807J0*
+G01*
+X2094423Y3608207D01*
+X2142122Y3608207D01*
+G75*
+D01*
+G02X2184423Y3613918I42301J-153806*
+G01*
+X2184423Y3613918D01*
+X2586611Y3613918D01*
+G75*
+D01*
+G02X2566570Y3670300I69289J56382*
+G01*
+X2566570Y3670300D01*
+X2566570Y3746500D01*
+G74*
+D01*
+G02X2647145Y3835400I89330J0*
+G01*
+G74*
+D01*
+G02X2582720Y3873070I8755J88900*
+G01*
+X2582720Y3873070D01*
+X2495048Y3873070D01*
+G74*
+D01*
+G02X2433500Y3826093I61547J16830*
+G01*
+X2433500Y3826093D01*
+X2318500Y3826093D01*
+G75*
+D01*
+G02X2286000Y3834990I0J63807*
+G01*
+G74*
+D01*
+G02X2253500Y3826093I32500J54910*
+G01*
+X2253500Y3826093D01*
+X2138500Y3826093D01*
+G75*
+D01*
+G02X2093648Y3844516I-1J63807*
+G01*
+X2093648Y3844516D01*
+X1714930Y3465798D01*
+X1714930Y3420907D01*
+X1760600Y3420907D01*
+G74*
+D01*
+G02X1824407Y3357100I0J63807*
+G01*
+X1824407Y3357100D01*
+X1824407Y3242100D01*
+G75*
+D01*
+G02X1760600Y3178293I-63807J0*
+G01*
+X1760600Y3178293D01*
+X1490600Y3178293D01*
+G75*
+D01*
+G02X1433985Y3212670I0J63807*
+G01*
+X1433985Y3212670D01*
+X1252630Y3212670D01*
+G75*
+D01*
+G02X1201009Y3212670I-25810J85519*
+G01*
+X1201009Y3212670D01*
+X925669Y3212670D01*
+G75*
+D01*
+G02X702740Y3209231I-112869J89329*
+G01*
+X702740Y3209231D01*
+X702740Y3171362D01*
+X762172Y3111930D01*
+X1457965Y3111930D01*
+G75*
+D01*
+G02X1490600Y3120907I32634J-54830*
+G01*
+X1490600Y3120907D01*
+X1760600Y3120907D01*
+G74*
+D01*
+G02X1824407Y3057100I0J63807*
+G01*
+X1824407Y3057100D01*
+X1824407Y2942100D01*
+G75*
+D01*
+G02X1760600Y2878293I-63807J0*
+G01*
+D02*
+G37*
+G36*
+X699445Y3644713D02*
+G74*
+D01*
+G03X646764Y3619166I10485J88713*
+G01*
+X646764Y3619166D01*
+X597330Y3569732D01*
+X597330Y4127328D01*
+X747072Y4277070D01*
+X1435299Y4277070D01*
+G75*
+D01*
+G03X1490600Y4245093I55301J31830*
+G01*
+X1490600Y4245093D01*
+X1760600Y4245093D01*
+G75*
+D01*
+G03X1824407Y4308900I0J63807*
+G01*
+X1824407Y4308900D01*
+X1824407Y4423900D01*
+G74*
+D01*
+G03X1760600Y4487707I63807J0*
+G01*
+X1760600Y4487707D01*
+X1490600Y4487707D01*
+G75*
+D01*
+G03X1435299Y4455730I0J-63807*
+G01*
+X1435299Y4455730D01*
+X1398191Y4455730D01*
+G74*
+D01*
+G03X1413342Y4508070I128190J65470*
+G01*
+X1413342Y4508070D01*
+X2056368Y4508070D01*
+X1736005Y4187707D01*
+X1490600Y4187707D01*
+G75*
+D01*
+G03X1433985Y4153330I0J-63807*
+G01*
+X1433985Y4153330D01*
+X934099Y4153330D01*
+G74*
+D01*
+G03X839254Y4204248I94845J62875*
+G01*
+X839254Y4204248D01*
+X786346Y4204248D01*
+G75*
+D01*
+G03X672552Y4090454I0J-113794*
+G01*
+X672552Y4090454D01*
+X672552Y4037546D01*
+G75*
+D01*
+G03X739463Y3933859I113794J0*
+G01*
+G75*
+D01*
+G03X745049Y3683000I73337J-123859*
+G01*
+G74*
+D01*
+G03X699445Y3644713I67749J127000*
+G01*
+D02*
+G37*
+G36*
+X2376000Y993893D02*
+X2376000Y993893D01*
+X2196000Y993893D01*
+G75*
+D01*
+G02X2140699Y1025870I0J63807*
+G01*
+X2140699Y1025870D01*
+X2126501Y1025870D01*
+G74*
+D01*
+G02X2071200Y993893I55301J31830*
+G01*
+X2071200Y993893D01*
+X1891200Y993893D01*
+G75*
+D01*
+G02X1841930Y1017156I0J63807*
+G01*
+X1841930Y1017156D01*
+X1841930Y926002D01*
+X1848142Y919789D01*
+G75*
+D01*
+G02X1891200Y936507I43058J-47089*
+G01*
+X1891200Y936507D01*
+X2071200Y936507D01*
+G74*
+D01*
+G02X2126501Y904530I0J63807*
+G01*
+X2126501Y904530D01*
+X2140699Y904530D01*
+G75*
+D01*
+G02X2196000Y936507I55301J-31830*
+G01*
+X2196000Y936507D01*
+X2376000Y936507D01*
+G74*
+D01*
+G02X2439807Y872700I0J63807*
+G01*
+X2439807Y872700D01*
+X2439807Y757700D01*
+G75*
+D01*
+G02X2376000Y693893I-63807J0*
+G01*
+X2376000Y693893D01*
+X2375330Y693893D01*
+X2375330Y644332D01*
+G74*
+D01*
+G02X2413000Y610163I89329J136332*
+G01*
+G74*
+D01*
+G02X2450670Y644332I126999J102163*
+G01*
+X2450670Y644332D01*
+X2450670Y1139598D01*
+X2439807Y1150461D01*
+X2439807Y1057700D01*
+G75*
+D01*
+G02X2376000Y993893I-63807J0*
+G01*
+D02*
+G37*
+G36*
+X787400Y1117600D02*
+X787400Y1117600D01*
+X1549400Y1117600D01*
+X1549400Y736600D01*
+X1295400Y736600D01*
+X787400Y939800D01*
+X787400Y1117600D01*
+D02*
+G37*
+G36*
+X925669Y3391330D02*
+G74*
+D01*
+G03X880551Y3429000I112868J89330*
+G01*
+G75*
+D01*
+G03X880551Y3683000I-67750J127000*
+G01*
+G74*
+D01*
+G03X925669Y3720670I67750J127000*
+G01*
+X925669Y3720670D01*
+X1129185Y3720670D01*
+G75*
+D01*
+G03X1185800Y3686293I56615J29430*
+G01*
+X1185800Y3686293D01*
+X1455800Y3686293D01*
+G74*
+D01*
+G03X1512415Y3720670I0J63807*
+G01*
+X1512415Y3720670D01*
+X1715696Y3720670D01*
+G75*
+D01*
+G03X1717149Y3720681I50J89330*
+G01*
+X1717149Y3720681D01*
+X1562434Y3565966D01*
+G75*
+D01*
+G03X1536270Y3502656I63166J-63166*
+G01*
+X1536270Y3502656D01*
+X1536270Y3420907D01*
+X1512537Y3420907D01*
+G75*
+D01*
+G03X1519607Y3450100I-56736J29193*
+G01*
+X1519607Y3450100D01*
+X1519607Y3565100D01*
+G74*
+D01*
+G03X1455800Y3628907I63807J0*
+G01*
+X1455800Y3628907D01*
+X1185800Y3628907D01*
+G75*
+D01*
+G03X1121993Y3565100I0J-63807*
+G01*
+X1121993Y3565100D01*
+X1121993Y3450100D01*
+G75*
+D01*
+G03X1160951Y3391330I63807J0*
+G01*
+X1160951Y3391330D01*
+X925669Y3391330D01*
+D02*
+G37*
+G36*
+X2566570Y4178300D02*
+X2566570Y4178300D01*
+X2566570Y4254070D01*
+X2286172Y4254070D01*
+X2130277Y4098175D01*
+G75*
+D01*
+G02X2138500Y4098707I8222J-63275*
+G01*
+X2138500Y4098707D01*
+X2253500Y4098707D01*
+G74*
+D01*
+G02X2286000Y4089810I0J63807*
+G01*
+G75*
+D01*
+G02X2318500Y4098707I32500J-54910*
+G01*
+X2318500Y4098707D01*
+X2433500Y4098707D01*
+G74*
+D01*
+G02X2495048Y4051730I1J63807*
+G01*
+X2495048Y4051730D01*
+X2582720Y4051730D01*
+G74*
+D01*
+G02X2647145Y4089400I73180J51229*
+G01*
+G75*
+D01*
+G02X2566570Y4178300I8755J88900*
+G01*
+D02*
+G37*
+G36*
+X925669Y3899330D02*
+G74*
+D01*
+G03X886137Y3933859I112869J89329*
+G01*
+G74*
+D01*
+G03X934099Y3974670I46883J103686*
+G01*
+X934099Y3974670D01*
+X1436752Y3974670D01*
+G75*
+D01*
+G03X1490600Y3945093I53848J34229*
+G01*
+X1490600Y3945093D01*
+X1724531Y3945093D01*
+X1678768Y3899330D01*
+X1509648Y3899330D01*
+G74*
+D01*
+G03X1455800Y3928907I53848J34229*
+G01*
+X1455800Y3928907D01*
+X1185800Y3928907D01*
+G75*
+D01*
+G03X1131952Y3899330I0J-63807*
+G01*
+X1131952Y3899330D01*
+X925669Y3899330D01*
+D02*
+G37*
+G36*
+X2309229Y1265070D02*
+X2309229Y1265070D01*
+X2325198Y1265070D01*
+X2353761Y1236507D01*
+X2282703Y1236507D01*
+G74*
+D01*
+G03X2309229Y1265070I98303J117891*
+G01*
+D02*
+G37*
+G54D11*
+X787400Y1117600D02*
+X1549400Y1117600D01*
+X1549400Y736600D01*
+X1295400Y736600D01*
+X787400Y939800D01*
+X787400Y1117600D01*
+X2821307Y4787900D02*
+X2821307Y4717860D01*
+G75*
+D01*
+G02X2855900Y4724830I34593J-82359*
+G01*
+X2932100Y4724830D01*
+G74*
+D01*
+G02X3021430Y4635500I0J89330*
+G01*
+X3021430Y4559300D01*
+G75*
+D01*
+G02X2940855Y4470400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021430Y4381500I8755J88900*
+G01*
+X3021430Y4379662D01*
+X3024710Y4382942D01*
+X3024710Y5027758D01*
+X2858598Y5193870D01*
+X1490507Y5193870D01*
+X1490507Y5148200D01*
+G75*
+D01*
+G02X1426700Y5084393I-63807J0*
+G01*
+X1311700Y5084393D01*
+G75*
+D01*
+G02X1247893Y5148200I0J63807*
+G01*
+X1247893Y5418200D01*
+G75*
+D01*
+G02X1311700Y5482007I63807J0*
+G01*
+X1426700Y5482007D01*
+G74*
+D01*
+G02X1490507Y5418200I0J63807*
+G01*
+X1490507Y5372530D01*
+X2022842Y5372530D01*
+G75*
+D01*
+G02X2244358Y5372530I110758J120269*
+G01*
+X2530842Y5372530D01*
+G75*
+D01*
+G02X2752358Y5372530I110758J120269*
+G01*
+X2895490Y5372530D01*
+G75*
+D01*
+G02X2928856Y5366109I110J-89330*
+G01*
+G74*
+D01*
+G02X2958845Y5346286I33257J82908*
+G01*
+X3177126Y5128005D01*
+G74*
+D01*
+G02X3203370Y5064760I63085J63245*
+G01*
+X3203370Y4345940D01*
+G75*
+D01*
+G02X3177126Y4282695I-89330J1*
+G01*
+X3047745Y4153314D01*
+G74*
+D01*
+G02X3021121Y4134921I63246J63084*
+G01*
+G74*
+D01*
+G02X3021430Y4127500I89020J7424*
+G01*
+X3021430Y4051300D01*
+G75*
+D01*
+G02X2940855Y3962400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021148Y3880588I8755J88900*
+G01*
+X2939188Y3880588D01*
+X2939188Y3962252D01*
+G74*
+D01*
+G02X2932100Y3961970I7092J89047*
+G01*
+X2855900Y3961970D01*
+G75*
+D01*
+G02X2848812Y3962252I4J89330*
+G01*
+X2848812Y3880588D01*
+X2810004Y3880588D01*
+G74*
+D01*
+G02X2740855Y3835400I77904J43711*
+G01*
+G74*
+D01*
+G02X2810004Y3790212I8756J88900*
+G01*
+X2848812Y3790212D01*
+X2848812Y3708548D01*
+G75*
+D01*
+G02X2855900Y3708830I7092J-89047*
+G01*
+X2932100Y3708830D01*
+G74*
+D01*
+G02X2939188Y3708548I4J89330*
+G01*
+X2939188Y3790212D01*
+X3021148Y3790212D01*
+G74*
+D01*
+G02X2940855Y3708400I89047J7087*
+G01*
+G74*
+D01*
+G02X3021430Y3619500I8755J88900*
+G01*
+X3021430Y3543300D01*
+G75*
+D01*
+G02X2940855Y3454400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021430Y3365500I8755J88900*
+G01*
+X3021430Y3289300D01*
+G75*
+D01*
+G02X3001389Y3232918I-89330J0*
+G01*
+X3318326Y3232918D01*
+G75*
+D01*
+G02X3364423Y3252607I46097J-44117*
+G01*
+X3412122Y3252607D01*
+G75*
+D01*
+G02X3496724Y3252607I42301J-153806*
+G01*
+X3544423Y3252607D01*
+G74*
+D01*
+G02X3608231Y3188800I1J63807*
+G01*
+X3608231Y3141101D01*
+G75*
+D01*
+G02X3608231Y3056499I-153807J-42301*
+G01*
+X3608231Y3008800D01*
+G75*
+D01*
+G02X3544423Y2944993I-63808J1*
+G01*
+X3523668Y2944993D01*
+G74*
+D01*
+G02X3429023Y2913882I94645J128407*
+G01*
+X3001389Y2913882D01*
+G74*
+D01*
+G02X3021430Y2857500I69289J56382*
+G01*
+X3021430Y2781300D01*
+G75*
+D01*
+G02X2940855Y2692400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021148Y2610588I8755J88900*
+G01*
+X2939188Y2610588D01*
+X2939188Y2692252D01*
+G74*
+D01*
+G02X2932100Y2691970I7092J89047*
+G01*
+X2855900Y2691970D01*
+G75*
+D01*
+G02X2848812Y2692252I4J89330*
+G01*
+X2848812Y2610588D01*
+X2810004Y2610588D01*
+G74*
+D01*
+G02X2740855Y2565400I77904J43711*
+G01*
+G74*
+D01*
+G02X2810004Y2520212I8756J88900*
+G01*
+X2848812Y2520212D01*
+X2848812Y2438548D01*
+G75*
+D01*
+G02X2855900Y2438830I7092J-89047*
+G01*
+X2932100Y2438830D01*
+G74*
+D01*
+G02X2939188Y2438548I4J89330*
+G01*
+X2939188Y2520212D01*
+X3021148Y2520212D01*
+G74*
+D01*
+G02X2940855Y2438400I89047J7087*
+G01*
+G74*
+D01*
+G02X3021430Y2349500I8755J88900*
+G01*
+X3021430Y2273300D01*
+G75*
+D01*
+G02X2940855Y2184400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021430Y2095500I8755J88900*
+G01*
+X3021430Y2019300D01*
+G75*
+D01*
+G02X2940855Y1930400I-89330J0*
+G01*
+G74*
+D01*
+G02X3021430Y1841500I8755J88900*
+G01*
+X3021430Y1765300D01*
+G75*
+D01*
+G02X2932100Y1675970I-89330J0*
+G01*
+X2855900Y1675970D01*
+G75*
+D01*
+G02X2821430Y1682888I-1J89330*
+G01*
+X2821430Y1638300D01*
+G75*
+D01*
+G02X2732100Y1548970I-89330J0*
+G01*
+X2655900Y1548970D01*
+G75*
+D01*
+G02X2566570Y1638300I0J89330*
+G01*
+X2566570Y1714500D01*
+G74*
+D01*
+G02X2647145Y1803400I89330J0*
+G01*
+G75*
+D01*
+G02X2566570Y1892300I8755J88900*
+G01*
+X2566570Y1968500D01*
+G74*
+D01*
+G02X2647145Y2057400I89330J0*
+G01*
+G74*
+D01*
+G02X2582720Y2095070I8755J88900*
+G01*
+X2045877Y2095070D01*
+G75*
+D01*
+G02X1982664Y2121234I-47J89330*
+G01*
+X1562434Y2541464D01*
+G75*
+D01*
+G02X1536270Y2604716I63166J63166*
+G01*
+X1536270Y2878293D01*
+X1490600Y2878293D01*
+G75*
+D01*
+G02X1427407Y2933270I0J63807*
+G01*
+X725170Y2933270D01*
+G75*
+D01*
+G02X701417Y2936486I0J89330*
+G01*
+G74*
+D01*
+G02X662004Y2959434I23752J86113*
+G01*
+X597330Y3024108D01*
+X597330Y2563005D01*
+G74*
+D01*
+G02X648212Y2472271I55458J90734*
+G01*
+X648212Y2404529D01*
+G75*
+D01*
+G02X597330Y2313795I-106341J0*
+G01*
+X597330Y2297269D01*
+G75*
+D01*
+G02X597330Y2071531I-89329J-112869*
+G01*
+X597330Y2043269D01*
+G75*
+D01*
+G02X597330Y1817531I-89329J-112869*
+G01*
+X597330Y1789269D01*
+G75*
+D01*
+G02X597330Y1563531I-89329J-112869*
+G01*
+X597330Y1486330D01*
+X1473200Y1486330D01*
+G74*
+D01*
+G02X1536445Y1460086I1J89330*
+G01*
+X1815686Y1180845D01*
+G74*
+D01*
+G02X1827393Y1166446I63083J63248*
+G01*
+X1827393Y1172700D01*
+G75*
+D01*
+G02X1891200Y1236507I63807J0*
+G01*
+X1891870Y1236507D01*
+X1891870Y1239969D01*
+G75*
+D01*
+G02X1918034Y1303266I89330J131*
+G01*
+X2032334Y1417566D01*
+G74*
+D01*
+G02X2052310Y1432595I63165J63167*
+G01*
+G75*
+D01*
+G02X2309229Y1443730I132090J-78194*
+G01*
+X2362157Y1443730D01*
+G75*
+D01*
+G02X2370371Y1443356I48J-89330*
+G01*
+G74*
+D01*
+G02X2425445Y1417486I8172J88955*
+G01*
+X2603086Y1239845D01*
+G74*
+D01*
+G02X2629330Y1176600I63085J63245*
+G01*
+X2629330Y644332D01*
+G74*
+D01*
+G02X2702712Y517549I89329J136332*
+G01*
+X2712261Y508000D01*
+X2702712Y498451D01*
+G75*
+D01*
+G02X2413000Y405837I-162711J9547*
+G01*
+G75*
+D01*
+G02X2196670Y644332I-126997J102166*
+G01*
+X2196670Y693893D01*
+X2196000Y693893D01*
+G75*
+D01*
+G02X2140699Y725870I0J63807*
+G01*
+X2126501Y725870D01*
+G74*
+D01*
+G02X2071200Y693893I55301J31830*
+G01*
+X1891200Y693893D01*
+G75*
+D01*
+G02X1835899Y725870I0J63807*
+G01*
+X1826450Y725870D01*
+G75*
+D01*
+G02X1763234Y752034I-50J89330*
+G01*
+X1689434Y825834D01*
+G75*
+D01*
+G02X1663270Y889088I63166J63166*
+G01*
+X1663270Y1080598D01*
+X1625968Y1117900D01*
+G74*
+D01*
+G02X1625969Y1117600I76567J405*
+G01*
+X1625969Y736600D01*
+G75*
+D01*
+G02X1567930Y662307I-76569J0*
+G01*
+G74*
+D01*
+G02X1550900Y659993I17028J61493*
+G01*
+X1293900Y659993D01*
+G75*
+D01*
+G02X1276871Y662307I-1J63807*
+G01*
+G74*
+D01*
+G02X1266963Y665508I18531J74292*
+G01*
+X758963Y868708D01*
+G75*
+D01*
+G02X710831Y939939I28436J71092*
+G01*
+X710831Y1117600D01*
+G75*
+D01*
+G02X787400Y1194169I76569J0*
+G01*
+X1549400Y1194169D01*
+G74*
+D01*
+G02X1549700Y1194168I105J76569*
+G01*
+X1436198Y1307670D01*
+X508000Y1307670D01*
+G75*
+D01*
+G02X418670Y1397000I0J89330*
+G01*
+X418670Y1563531D01*
+G75*
+D01*
+G02X418670Y1789269I89329J112869*
+G01*
+X418670Y1817531D01*
+G75*
+D01*
+G02X418670Y2043269I89329J112869*
+G01*
+X418670Y2071531D01*
+G75*
+D01*
+G02X418670Y2297269I89329J112869*
+G01*
+X418670Y2313795D01*
+G75*
+D01*
+G02X367788Y2404529I55458J90734*
+G01*
+X367788Y2472271D01*
+G74*
+D01*
+G02X418670Y2563005I106341J0*
+G01*
+X418670Y4408331D01*
+G75*
+D01*
+G02X418670Y4634069I89329J112869*
+G01*
+X418670Y4650595D01*
+G75*
+D01*
+G02X367788Y4741329I55458J90734*
+G01*
+X367788Y4809071D01*
+G74*
+D01*
+G02X418670Y4899805I106341J0*
+G01*
+X418670Y5029034D01*
+G75*
+D01*
+G02X444834Y5092366I89330J166*
+G01*
+X698834Y5346366D01*
+G74*
+D01*
+G02X749888Y5371705I63166J63166*
+G01*
+G75*
+D01*
+G02X762000Y5372530I12112J-88504*
+G01*
+X947893Y5372530D01*
+X947893Y5418200D01*
+G75*
+D01*
+G02X1011700Y5482007I63807J0*
+G01*
+X1126700Y5482007D01*
+G74*
+D01*
+G02X1190507Y5418200I0J63807*
+G01*
+X1190507Y5148200D01*
+G75*
+D01*
+G02X1126700Y5084393I-63807J0*
+G01*
+X1011700Y5084393D01*
+G75*
+D01*
+G02X947893Y5148200I0J63807*
+G01*
+X947893Y5193870D01*
+X799002Y5193870D01*
+X597330Y4992198D01*
+X597330Y4899805D01*
+G74*
+D01*
+G02X648212Y4809071I55458J90734*
+G01*
+X648212Y4741329D01*
+G75*
+D01*
+G02X597330Y4650595I-106341J0*
+G01*
+X597330Y4634069D01*
+G75*
+D01*
+G02X597330Y4408331I-89329J-112869*
+G01*
+X597330Y4379992D01*
+X646904Y4429566D01*
+G74*
+D01*
+G02X677608Y4449623I63166J63166*
+G01*
+G75*
+D01*
+G02X710070Y4455730I32462J-83223*
+G01*
+X1141809Y4455730D01*
+G75*
+D01*
+G02X1202249Y4648200I128190J65470*
+G01*
+G75*
+D01*
+G02X1382869Y4864530I67754J126998*
+G01*
+X2640232Y4864530D01*
+G75*
+D01*
+G02X2669697Y4859565I98J-89330*
+G01*
+G74*
+D01*
+G02X2686444Y4851707I29368J84364*
+G01*
+X2757500Y4851707D01*
+G74*
+D01*
+G02X2821307Y4787900I0J63807*
+G01*
+X1202249Y2311400D02*
+G75*
+D01*
+G02X1337751Y2311400I67751J126999*
+G01*
+G75*
+D01*
+G02X1337751Y2057400I-67750J-127000*
+G01*
+G75*
+D01*
+G02X1337751Y1803400I-67750J-127000*
+G01*
+G75*
+D01*
+G02X1202249Y1803400I-67751J-126999*
+G01*
+G75*
+D01*
+G02X1202249Y2057400I67750J127000*
+G01*
+G75*
+D01*
+G02X1202249Y2311400I67750J127000*
+G01*
+X2986100Y5492800D02*
+G75*
+D01*
+G02X2986100Y5492800I163500J0*
+G01*
+X3494100Y5492800D02*
+G75*
+D01*
+G02X3494100Y5492800I163500J0*
+G01*
+X2864000Y2565400D02*
+G75*
+D01*
+G02X2864000Y2565400I30000J0*
+G01*
+X2864000Y3835400D02*
+G75*
+D01*
+G02X2864000Y3835400I30000J0*
+G01*
+X2860893Y359367D02*
+G75*
+D01*
+G02X2727107Y359367I-66893J148633*
+G01*
+X2794000Y426261D01*
+X2860893Y359367D01*
+X3300593Y351300D02*
+X3300593Y365783D01*
+X3411383Y365783D01*
+X3411383Y287493D01*
+X3364400Y287493D01*
+G75*
+D01*
+G02X3300593Y351300I0J63807*
+G01*
+X3608207Y351300D02*
+G75*
+D01*
+G02X3544400Y287493I-63807J0*
+G01*
+X3497417Y287493D01*
+X3497417Y365783D01*
+X3608207Y365783D01*
+X3608207Y351300D01*
+X2727107Y656633D02*
+G75*
+D01*
+G02X2860893Y656633I66893J-148633*
+G01*
+X2794000Y589739D01*
+X2727107Y656633D01*
+X2734729Y508000D02*
+G75*
+D01*
+G02X2734729Y508000I59271J0*
+G01*
+X2942633Y574893D02*
+G75*
+D01*
+G02X2942633Y441107I-148633J-66893*
+G01*
+X2875739Y508000D01*
+X2942633Y574893D01*
+X3364400Y530107D02*
+X3411383Y530107D01*
+X3411383Y451817D01*
+X3300593Y451817D01*
+X3300593Y466300D01*
+G75*
+D01*
+G02X3364400Y530107I63807J0*
+G01*
+X3543730Y873971D02*
+X3543730Y830107D01*
+X3544400Y830107D01*
+G74*
+D01*
+G02X3608207Y766300I0J63807*
+G01*
+X3608207Y651300D01*
+G75*
+D01*
+G02X3544400Y587493I-63807J0*
+G01*
+X3364400Y587493D01*
+G75*
+D01*
+G02X3300593Y651300I0J63807*
+G01*
+X3300593Y766300D01*
+G75*
+D01*
+G02X3364400Y830107I63807J0*
+G01*
+X3365070Y830107D01*
+X3365070Y873971D01*
+G75*
+D01*
+G02X3543730Y873971I89330J124828*
+G01*
+X3608207Y466300D02*
+X3608207Y451817D01*
+X3497417Y451817D01*
+X3497417Y530107D01*
+X3544400Y530107D01*
+G74*
+D01*
+G02X3608207Y466300I0J63807*
+G01*
+X1230093Y393800D02*
+G75*
+D01*
+G02X1293900Y457607I63807J0*
+G01*
+X1354206Y457607D01*
+X1354206Y321994D01*
+X1230093Y321994D01*
+X1230093Y393800D01*
+X1614707Y393800D02*
+X1614707Y321994D01*
+X1490594Y321994D01*
+X1490594Y457607D01*
+X1550900Y457607D01*
+G74*
+D01*
+G02X1614707Y393800I0J63807*
+G01*
+X242900Y1009600D02*
+G75*
+D01*
+G02X242900Y1009600I163500J0*
+G01*
+X1590549Y63807D02*
+X3936193Y63807D01*
+X3936193Y6436193D01*
+X63807Y6436193D01*
+X63807Y63807D01*
+X1254251Y63807D01*
+G75*
+D01*
+G02X1230093Y113800I39649J49993*
+G01*
+X1230093Y185606D01*
+X1354206Y185606D01*
+X1354206Y63807D01*
+X1490594Y63807D01*
+X1490594Y185606D01*
+X1614707Y185606D01*
+X1614707Y113800D01*
+G75*
+D01*
+G02X1590549Y63807I-63807J0*
+G01*
+X2376000Y993893D02*
+X2196000Y993893D01*
+G75*
+D01*
+G02X2140699Y1025870I0J63807*
+G01*
+X2126501Y1025870D01*
+G74*
+D01*
+G02X2071200Y993893I55301J31830*
+G01*
+X1891200Y993893D01*
+G75*
+D01*
+G02X1841930Y1017156I0J63807*
+G01*
+X1841930Y926002D01*
+X1848142Y919789D01*
+G75*
+D01*
+G02X1891200Y936507I43058J-47089*
+G01*
+X2071200Y936507D01*
+G74*
+D01*
+G02X2126501Y904530I0J63807*
+G01*
+X2140699Y904530D01*
+G75*
+D01*
+G02X2196000Y936507I55301J-31830*
+G01*
+X2376000Y936507D01*
+G74*
+D01*
+G02X2439807Y872700I0J63807*
+G01*
+X2439807Y757700D01*
+G75*
+D01*
+G02X2376000Y693893I-63807J0*
+G01*
+X2375330Y693893D01*
+X2375330Y644332D01*
+G74*
+D01*
+G02X2413000Y610163I89329J136332*
+G01*
+G74*
+D01*
+G02X2450670Y644332I126999J102163*
+G01*
+X2450670Y1139598D01*
+X2439807Y1150461D01*
+X2439807Y1057700D01*
+G75*
+D01*
+G02X2376000Y993893I-63807J0*
+G01*
+X2309229Y1265070D02*
+X2325198Y1265070D01*
+X2353761Y1236507D01*
+X2282703Y1236507D01*
+G74*
+D01*
+G03X2309229Y1265070I98303J117891*
+G01*
+X1760600Y2878293D02*
+X1714930Y2878293D01*
+X1714930Y2641632D01*
+X2020369Y2336193D01*
+X2082832Y2273730D01*
+X2582720Y2273730D01*
+G74*
+D01*
+G02X2647145Y2311400I73180J51229*
+G01*
+G75*
+D01*
+G02X2566570Y2400300I8755J88900*
+G01*
+X2566570Y2476500D01*
+G74*
+D01*
+G02X2647145Y2565400I89330J0*
+G01*
+G75*
+D01*
+G02X2566570Y2654300I8755J88900*
+G01*
+X2566570Y2730500D01*
+G74*
+D01*
+G02X2647145Y2819400I89330J0*
+G01*
+G75*
+D01*
+G02X2566570Y2908300I8755J88900*
+G01*
+X2566570Y2984500D01*
+G74*
+D01*
+G02X2647145Y3073400I89330J0*
+G01*
+G75*
+D01*
+G02X2566570Y3162300I8755J88900*
+G01*
+X2566570Y3238500D01*
+G74*
+D01*
+G02X2586611Y3294882I89330J0*
+G01*
+X2184423Y3294882D01*
+G75*
+D01*
+G02X2142122Y3300593I0J159518*
+G01*
+X2094423Y3300593D01*
+G75*
+D01*
+G02X2030616Y3364400I0J63807*
+G01*
+X2030616Y3412099D01*
+G75*
+D01*
+G02X2030616Y3496701I153806J42301*
+G01*
+X2030616Y3544400D01*
+G75*
+D01*
+G02X2094423Y3608207I63807J0*
+G01*
+X2142122Y3608207D01*
+G75*
+D01*
+G02X2184423Y3613918I42301J-153806*
+G01*
+X2586611Y3613918D01*
+G75*
+D01*
+G02X2566570Y3670300I69289J56382*
+G01*
+X2566570Y3746500D01*
+G74*
+D01*
+G02X2647145Y3835400I89330J0*
+G01*
+G74*
+D01*
+G02X2582720Y3873070I8755J88900*
+G01*
+X2495048Y3873070D01*
+G74*
+D01*
+G02X2433500Y3826093I61547J16830*
+G01*
+X2318500Y3826093D01*
+G75*
+D01*
+G02X2286000Y3834990I0J63807*
+G01*
+G74*
+D01*
+G02X2253500Y3826093I32500J54910*
+G01*
+X2138500Y3826093D01*
+G75*
+D01*
+G02X2093648Y3844516I-1J63807*
+G01*
+X1714930Y3465798D01*
+X1714930Y3420907D01*
+X1760600Y3420907D01*
+G74*
+D01*
+G02X1824407Y3357100I0J63807*
+G01*
+X1824407Y3242100D01*
+G75*
+D01*
+G02X1760600Y3178293I-63807J0*
+G01*
+X1490600Y3178293D01*
+G75*
+D01*
+G02X1433985Y3212670I0J63807*
+G01*
+X1252630Y3212670D01*
+G75*
+D01*
+G02X1201009Y3212670I-25810J85519*
+G01*
+X925669Y3212670D01*
+G75*
+D01*
+G02X702740Y3209231I-112869J89329*
+G01*
+X702740Y3171362D01*
+X762172Y3111930D01*
+X1457965Y3111930D01*
+G75*
+D01*
+G02X1490600Y3120907I32634J-54830*
+G01*
+X1760600Y3120907D01*
+G74*
+D01*
+G02X1824407Y3057100I0J63807*
+G01*
+X1824407Y2942100D01*
+G75*
+D01*
+G02X1760600Y2878293I-63807J0*
+G01*
+X925669Y3391330D02*
+G74*
+D01*
+G03X880551Y3429000I112868J89330*
+G01*
+G75*
+D01*
+G03X880551Y3683000I-67750J127000*
+G01*
+G74*
+D01*
+G03X925669Y3720670I67750J127000*
+G01*
+X1129185Y3720670D01*
+G75*
+D01*
+G03X1185800Y3686293I56615J29430*
+G01*
+X1455800Y3686293D01*
+G74*
+D01*
+G03X1512415Y3720670I0J63807*
+G01*
+X1715696Y3720670D01*
+G75*
+D01*
+G03X1717149Y3720681I50J89330*
+G01*
+X1562434Y3565966D01*
+G75*
+D01*
+G03X1536270Y3502656I63166J-63166*
+G01*
+X1536270Y3420907D01*
+X1512537Y3420907D01*
+G75*
+D01*
+G03X1519607Y3450100I-56736J29193*
+G01*
+X1519607Y3565100D01*
+G74*
+D01*
+G03X1455800Y3628907I63807J0*
+G01*
+X1185800Y3628907D01*
+G75*
+D01*
+G03X1121993Y3565100I0J-63807*
+G01*
+X1121993Y3450100D01*
+G75*
+D01*
+G03X1160951Y3391330I63807J0*
+G01*
+X925669Y3391330D01*
+X699445Y3644713D02*
+G74*
+D01*
+G03X646764Y3619166I10485J88713*
+G01*
+X597330Y3569732D01*
+X597330Y4127328D01*
+X747072Y4277070D01*
+X1435299Y4277070D01*
+G75*
+D01*
+G03X1490600Y4245093I55301J31830*
+G01*
+X1760600Y4245093D01*
+G75*
+D01*
+G03X1824407Y4308900I0J63807*
+G01*
+X1824407Y4423900D01*
+G74*
+D01*
+G03X1760600Y4487707I63807J0*
+G01*
+X1490600Y4487707D01*
+G75*
+D01*
+G03X1435299Y4455730I0J-63807*
+G01*
+X1398191Y4455730D01*
+G74*
+D01*
+G03X1413342Y4508070I128190J65470*
+G01*
+X2056368Y4508070D01*
+X1736005Y4187707D01*
+X1490600Y4187707D01*
+G75*
+D01*
+G03X1433985Y4153330I0J-63807*
+G01*
+X934099Y4153330D01*
+G74*
+D01*
+G03X839254Y4204248I94845J62875*
+G01*
+X786346Y4204248D01*
+G75*
+D01*
+G03X672552Y4090454I0J-113794*
+G01*
+X672552Y4037546D01*
+G75*
+D01*
+G03X739463Y3933859I113794J0*
+G01*
+G75*
+D01*
+G03X745049Y3683000I73337J-123859*
+G01*
+G74*
+D01*
+G03X699445Y3644713I67749J127000*
+G01*
+X925669Y3899330D02*
+G74*
+D01*
+G03X886137Y3933859I112869J89329*
+G01*
+G74*
+D01*
+G03X934099Y3974670I46883J103686*
+G01*
+X1436752Y3974670D01*
+G75*
+D01*
+G03X1490600Y3945093I53848J34229*
+G01*
+X1724531Y3945093D01*
+X1678768Y3899330D01*
+X1509648Y3899330D01*
+G74*
+D01*
+G03X1455800Y3928907I53848J34229*
+G01*
+X1185800Y3928907D01*
+G75*
+D01*
+G03X1131952Y3899330I0J-63807*
+G01*
+X925669Y3899330D01*
+X2566570Y4178300D02*
+X2566570Y4254070D01*
+X2286172Y4254070D01*
+X2130277Y4098175D01*
+G75*
+D01*
+G02X2138500Y4098707I8222J-63275*
+G01*
+X2253500Y4098707D01*
+G74*
+D01*
+G02X2286000Y4089810I0J63807*
+G01*
+G75*
+D01*
+G02X2318500Y4098707I32500J-54910*
+G01*
+X2433500Y4098707D01*
+G74*
+D01*
+G02X2495048Y4051730I1J63807*
+G01*
+X2582720Y4051730D01*
+G74*
+D01*
+G02X2647145Y4089400I73180J51229*
+G01*
+G75*
+D01*
+G02X2566570Y4178300I8755J88900*
+G01*
+G54D12*
+X3454400Y304600D02*
+X3302000Y152200D01*
+X3454400Y408800D02*
+X3454400Y304600D01*
+X2286000Y815200D02*
+X2286000Y508000D01*
+X3454400Y408800D02*
+X2893200Y408800D01*
+X2794000Y508000D01*
+X3454400Y708800D02*
+X3454400Y998800D01*
+X1752600Y1117600D02*
+X1752600Y889000D01*
+X1826400Y815200D01*
+X2286000Y815200D01*
+X1473200Y1397000D02*
+X1752600Y1117600D01*
+X508000Y5029200D02*
+X508000Y1397000D01*
+X1473200Y1397000D01*
+X1459230Y3302000D02*
+X812800Y3302000D01*
+X1625600Y3299600D02*
+X1461630Y3299600D01*
+X1715770Y3810000D02*
+X812800Y3810000D01*
+X2249170Y4343400D02*
+X1715770Y3810000D01*
+X2694000Y3962400D02*
+X2376000Y3962400D01*
+X2894000Y4343400D02*
+X2249170Y4343400D01*
+X2694000Y4216400D02*
+X2984500Y4216400D01*
+X3114040Y4345940D01*
+X3114040Y5064760D01*
+X1346200Y4597400D02*
+X1270000Y4521200D01*
+X2894000Y4597400D02*
+X1346200Y4597400D01*
+X762000Y5283200D02*
+X508000Y5029200D01*
+X3114040Y5064760D02*
+X2895600Y5283200D01*
+X1069200Y5283200D02*
+X762000Y5283200D01*
+X2895600Y5283200D02*
+X1369200Y5283200D01*
+X1981200Y1240100D02*
+X2095500Y1354400D01*
+X1981200Y1115200D02*
+X1981200Y1240100D01*
+X710070Y4366400D02*
+X511810Y4168140D01*
+X1625600Y4366400D02*
+X710070Y4366400D01*
+X613410Y3459480D02*
+X709930Y3556000D01*
+X812800Y3556000D01*
+X725170Y3022600D02*
+X613410Y3134360D01*
+X725170Y3022600D02*
+X1602600Y3022600D01*
+X613410Y3134360D02*
+X613410Y3459480D01*
+X1602600Y3022600D02*
+X1625600Y2999600D01*
+X1625600Y2604630D02*
+X2045830Y2184400D01*
+X1625600Y2604630D02*
+X1625600Y2999600D01*
+X2045830Y2184400D02*
+X2694000Y2184400D01*
+X2085200Y3962400D02*
+X1625600Y3502800D01*
+X2085200Y3962400D02*
+X2196000Y3962400D01*
+X1625600Y3502800D02*
+X1625600Y3299600D01*
+X1320800Y3392170D02*
+X1226820Y3298190D01*
+X1320800Y3507600D02*
+X1320800Y3392170D01*
+X2691130Y4724400D02*
+X2640330Y4775200D01*
+X2694000Y4724400D02*
+X2691130Y4724400D01*
+X2640330Y4775200D02*
+X1270000Y4775200D01*
+X1738630Y4064000D02*
+X2145030Y4470400D01*
+X2694000Y4470400D01*
+X812800Y4064000D02*
+X1738630Y4064000D01*
+X1981200Y1115200D02*
+X2286000Y1115200D01*
+X2540000Y508000D02*
+X2540000Y1176600D01*
+X2362200Y1354400D01*
+X2095500Y1354400D02*
+X2362200Y1354400D01*
+G54D13*
+X3694230Y152200D02*
+X3840480Y298450D01*
+X3840480Y298450D02*
+X3840480Y3542030D01*
+X3562350Y2565400D02*
+X3835400Y2292350D01*
+X2894000Y2565400D02*
+X3562350Y2565400D01*
+X2894000Y3073400D02*
+X3429023Y3073400D01*
+X3454423Y3098800D01*
+X2694000Y3454400D02*
+X2184423Y3454400D01*
+X3840480Y3542030D02*
+X3547110Y3835400D01*
+X2894000Y3835400D01*
+X3694230Y152200D02*
+X1524000Y152200D01*
+X1422400Y253800D01*
+G54D14*
+X-63500Y-63500D02*
+X-63500Y599200D01*
+X-63500Y-63500D02*
+X349200Y-63500D01*
+X4063500Y-63500D02*
+X3650800Y-63500D01*
+X4063500Y-63500D02*
+X4063500Y599200D01*
+X4063500Y6563500D02*
+X4063500Y5900800D01*
+X4063500Y6563500D02*
+X3650800Y6563500D01*
+X-63500Y6563500D02*
+X349200Y6563500D01*
+X-63500Y6563500D02*
+X-63500Y5900800D01*
+X-563500Y-63500D02*
+X-1563500Y-63500D01*
+X-1063500Y-563500D02*
+X-1063500Y436500D01*
+X-1438500Y-63500D02*
+G75*
+D01*
+G02X-1438500Y-63500I375000J0*
+G01*
+X4563500Y-63500D02*
+X5563500Y-63500D01*
+X5063500Y-563500D02*
+X5063500Y436500D01*
+X4688500Y-63500D02*
+G75*
+D01*
+G02X4688500Y-63500I375000J0*
+G01*
+X4938500Y-63500D02*
+G75*
+D01*
+G02X4938500Y-63500I125000J0*
+G01*
+X-563500Y6563500D02*
+X-1563500Y6563500D01*
+X-1063500Y6063500D02*
+X-1063500Y7063500D01*
+X-1438500Y6563500D02*
+G75*
+D01*
+G02X-1438500Y6563500I375000J0*
+G01*
+X-1313500Y6563500D02*
+G75*
+D01*
+G02X-1313500Y6563500I250000J0*
+G01*
+X-1188500Y6563500D02*
+G75*
+D01*
+G02X-1188500Y6563500I125000J0*
+G01*
+G54D15*
+X406400Y1009600D03*
+X914400Y1009600D03*
+X3657600Y5492800D03*
+X3149600Y5492800D03*
+X2641600Y5492800D03*
+X2133600Y5492800D03*
+G54D16*
+X3454400Y408800D03*
+X3454400Y708800D03*
+X1981200Y815200D03*
+X1981200Y1115200D03*
+X2286000Y815200D03*
+X2286000Y1115200D03*
+G54D17*
+X1270000Y2438400D03*
+X508000Y1676400D03*
+X1270000Y1676400D03*
+X508000Y1930400D03*
+X508000Y2184400D03*
+X1270000Y1930400D03*
+X1270000Y2184400D03*
+X508000Y4521200D03*
+X1270000Y4521200D03*
+X1270000Y4775200D03*
+X812800Y3556000D03*
+X812800Y3810000D03*
+X812800Y3302000D03*
+G54D18*
+X508000Y2438400D03*
+X508000Y4775200D03*
+G54D19*
+X474129Y2404529D02*
+X541871Y2404529D01*
+X541871Y2472271D01*
+X474129Y2472271D01*
+X474129Y2404529D01*D02*
+X474129Y4741329D02*
+X541871Y4741329D01*
+X541871Y4809071D01*
+X474129Y4809071D01*
+X474129Y4741329D01*D02*
+G54D20*
+X2694000Y4470400D03*
+X2694000Y4216400D03*
+X2694000Y3962400D03*
+X2694000Y3708400D03*
+X2694000Y3454400D03*
+X2694000Y3200400D03*
+X2694000Y2946400D03*
+X2694000Y2692400D03*
+X2694000Y2438400D03*
+X2694000Y2184400D03*
+X2694000Y1930400D03*
+X2694000Y1676400D03*
+X2894000Y4597400D03*
+X2894000Y4343400D03*
+X2894000Y4089400D03*
+X2894000Y3835400D03*
+X2894000Y3581400D03*
+X2894000Y3327400D03*
+X2894000Y3073400D03*
+X2894000Y2819400D03*
+X2894000Y2565400D03*
+X2894000Y2311400D03*
+X2894000Y2057400D03*
+X2894000Y1803400D03*
+G54D21*
+X2655900Y4432300D02*
+X2732100Y4432300D01*
+X2732100Y4508500D01*
+X2655900Y4508500D01*
+X2655900Y4432300D01*D02*
+X2655900Y4178300D02*
+X2732100Y4178300D01*
+X2732100Y4254500D01*
+X2655900Y4254500D01*
+X2655900Y4178300D01*D02*
+X2655900Y3924300D02*
+X2732100Y3924300D01*
+X2732100Y4000500D01*
+X2655900Y4000500D01*
+X2655900Y3924300D01*D02*
+X2655900Y3670300D02*
+X2732100Y3670300D01*
+X2732100Y3746500D01*
+X2655900Y3746500D01*
+X2655900Y3670300D01*D02*
+X2655900Y3416300D02*
+X2732100Y3416300D01*
+X2732100Y3492500D01*
+X2655900Y3492500D01*
+X2655900Y3416300D01*D02*
+X2655900Y3162300D02*
+X2732100Y3162300D01*
+X2732100Y3238500D01*
+X2655900Y3238500D01*
+X2655900Y3162300D01*D02*
+X2655900Y2908300D02*
+X2732100Y2908300D01*
+X2732100Y2984500D01*
+X2655900Y2984500D01*
+X2655900Y2908300D01*D02*
+X2655900Y2654300D02*
+X2732100Y2654300D01*
+X2732100Y2730500D01*
+X2655900Y2730500D01*
+X2655900Y2654300D01*D02*
+X2655900Y2400300D02*
+X2732100Y2400300D01*
+X2732100Y2476500D01*
+X2655900Y2476500D01*
+X2655900Y2400300D01*D02*
+X2655900Y2146300D02*
+X2732100Y2146300D01*
+X2732100Y2222500D01*
+X2655900Y2222500D01*
+X2655900Y2146300D01*D02*
+X2655900Y1892300D02*
+X2732100Y1892300D01*
+X2732100Y1968500D01*
+X2655900Y1968500D01*
+X2655900Y1892300D01*D02*
+X2655900Y1638300D02*
+X2732100Y1638300D01*
+X2732100Y1714500D01*
+X2655900Y1714500D01*
+X2655900Y1638300D01*D02*
+X2855900Y4559300D02*
+X2932100Y4559300D01*
+X2932100Y4635500D01*
+X2855900Y4635500D01*
+X2855900Y4559300D01*D02*
+X2855900Y4305300D02*
+X2932100Y4305300D01*
+X2932100Y4381500D01*
+X2855900Y4381500D01*
+X2855900Y4305300D01*D02*
+X2855900Y4051300D02*
+X2932100Y4051300D01*
+X2932100Y4127500D01*
+X2855900Y4127500D01*
+X2855900Y4051300D01*D02*
+X2855900Y3797300D02*
+X2932100Y3797300D01*
+X2932100Y3873500D01*
+X2855900Y3873500D01*
+X2855900Y3797300D01*D02*
+X2855900Y3543300D02*
+X2932100Y3543300D01*
+X2932100Y3619500D01*
+X2855900Y3619500D01*
+X2855900Y3543300D01*D02*
+X2855900Y3289300D02*
+X2932100Y3289300D01*
+X2932100Y3365500D01*
+X2855900Y3365500D01*
+X2855900Y3289300D01*D02*
+X2855900Y3035300D02*
+X2932100Y3035300D01*
+X2932100Y3111500D01*
+X2855900Y3111500D01*
+X2855900Y3035300D01*D02*
+X2855900Y2781300D02*
+X2932100Y2781300D01*
+X2932100Y2857500D01*
+X2855900Y2857500D01*
+X2855900Y2781300D01*D02*
+X2855900Y2527300D02*
+X2932100Y2527300D01*
+X2932100Y2603500D01*
+X2855900Y2603500D01*
+X2855900Y2527300D01*D02*
+X2855900Y2273300D02*
+X2932100Y2273300D01*
+X2932100Y2349500D01*
+X2855900Y2349500D01*
+X2855900Y2273300D01*D02*
+X2855900Y2019300D02*
+X2932100Y2019300D01*
+X2932100Y2095500D01*
+X2855900Y2095500D01*
+X2855900Y2019300D01*D02*
+X2855900Y1765300D02*
+X2932100Y1765300D01*
+X2932100Y1841500D01*
+X2855900Y1841500D01*
+X2855900Y1765300D01*D02*
+G54D22*
+X2694000Y4724400D03*
+G54D23*
+X1625600Y3299600D03*
+X1625600Y2999600D03*
+X1625600Y4366400D03*
+X1625600Y4066400D03*
+X1320800Y3507600D03*
+X1320800Y3807600D03*
+G54D24*
+X2196000Y3962400D03*
+X2376000Y3962400D03*
+G54D25*
+X1069200Y5283200D03*
+X1369200Y5283200D03*
+G54D26*
+X812800Y4064000D03*
+G54D27*
+X786346Y4037546D02*
+X839254Y4037546D01*
+X839254Y4090454D01*
+X786346Y4090454D01*
+X786346Y4037546D01*D02*
+G54D28*
+X3454400Y998800D03*
+X2184400Y1354400D03*
+G54D29*
+X3454423Y3098800D03*
+X2184423Y3454400D03*
+G54D30*
+X1422400Y863800D03*
+X1422400Y253800D03*
+G54D31*
+X2286000Y508000D03*
+X2540000Y508000D03*
+X2794000Y508000D03*
+
+M00*

+ 13 - 0
tests/gerber_parsing_profiling/gerber_parsing_line_profile_1.py

@@ -0,0 +1,13 @@
+# This script is for profiling Gerber.parse_lines() line by line.
+# Run kernprof -l -v gerber_parsing_line_profile_1.py
+
+import sys
+sys.path.append('../../')
+
+from camlib import *
+
+log = logging.getLogger('base2')
+log.setLevel(logging.WARNING)
+
+g = Gerber()
+g.parse_file("gerber1.gbr")

+ 17 - 0
tests/gerber_parsing_profiling/gerber_parsing_profile_1.py

@@ -0,0 +1,17 @@
+import cProfile
+import pstats
+import sys
+sys.path.append('../../')
+
+from camlib import *
+
+log = logging.getLogger('base2')
+log.setLevel(logging.WARNING)
+
+g = Gerber()
+
+#cProfile.run('g.parse_file("gerber1.gbr")', 'gerber1_profile', sort='cumtime')
+cProfile.run('g.parse_file("/home/jpcaram/flatcam_test_files/Gerbers/AVR_Transistor_Tester_silkscreen_top.GTO")',
+             'gerber1_profile', sort='cumtime')
+p = pstats.Stats('gerber1_profile')
+p.strip_dirs().sort_stats('cumulative').print_stats(.1)

+ 34 - 0
tests/other/destructor_test.py

@@ -0,0 +1,34 @@
+import sys
+from PyQt4 import QtCore, QtGui
+
+
+class MyObj():
+
+    def __init__(self):
+        pass
+
+    def __del__(self):
+        print "##### Destroyed ######"
+
+
+def parse():
+    o = MyObj()
+    raise Exception("Intentional Exception")
+
+
+class Example(QtGui.QWidget):
+
+    def __init__(self):
+        super(Example, self).__init__()
+
+        qbtn = QtGui.QPushButton('Raise', self)
+        qbtn.clicked.connect(parse)
+
+        self.setWindowTitle('Quit button')
+        self.show()
+
+
+if __name__ == '__main__':
+    app = QtGui.QApplication(sys.argv)
+    ex = Example()
+    sys.exit(app.exec_())

+ 0 - 0
tests/profile_gerber_parser.py → tests/other/profile_gerber_parser.py


+ 0 - 0
tests/test_excellon_1.py → tests/other/test_excellon_1.py


+ 0 - 0
tests/test_fcrts.py → tests/other/test_fcrts.py


+ 47 - 0
tests/other/test_plotg.py

@@ -0,0 +1,47 @@
+from shapely.geometry import LineString, Polygon
+from shapely.ops import cascaded_union, unary_union
+from matplotlib.pyplot import plot, subplot, show
+from camlib import *
+
+
+def plotg2(geo, solid_poly=False, color="black", linestyle='solid'):
+
+    try:
+        for sub_geo in geo:
+            plotg2(sub_geo, solid_poly=solid_poly, color=color, linestyle=linestyle)
+    except TypeError:
+        if type(geo) == Polygon:
+            if solid_poly:
+                patch = PolygonPatch(geo,
+                                     #facecolor="#BBF268",
+                                     facecolor=color,
+                                     edgecolor="#006E20",
+                                     alpha=0.5,
+                                     zorder=2)
+                ax = subplot(111)
+                ax.add_patch(patch)
+            else:
+                x, y = geo.exterior.coords.xy
+                plot(x, y, color=color, linestyle=linestyle)
+                for ints in geo.interiors:
+                    x, y = ints.coords.xy
+                    plot(x, y, color=color, linestyle=linestyle)
+
+        if type(geo) == LineString or type(geo) == LinearRing:
+            x, y = geo.coords.xy
+            plot(x, y, color=color, linestyle=linestyle)
+
+        if type(geo) == Point:
+            x, y = geo.coords.xy
+            plot(x, y, 'o')
+
+
+if __name__ == "__main__":
+    p = Polygon([[0, 0], [0, 5], [5, 5], [5, 0]])
+    paths = [
+        LineString([[0.5, 2], [2, 4.5]]),
+        LineString([[2, 0.5], [4.5, 2]])
+    ]
+    plotg2(p, solid_poly=True)
+    plotg2(paths, linestyle="dashed")
+    show()

+ 0 - 1
tests/test_rt.py → tests/other/test_rt.py

@@ -1,6 +1,5 @@
 from rtree import index as rtindex
 from rtree import index as rtindex
 
 
-
 def pt2rect(pt):
 def pt2rect(pt):
     return pt[0], pt[1], pt[0], pt[1]
     return pt[0], pt[1], pt[0], pt[1]
 
 

+ 126 - 0
tests/svg/drawing.svg

@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="drawing.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4"
+     inkscape:cx="436.65332"
+     inkscape:cy="798.58794"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="968"
+     inkscape:window-height="759"
+     inkscape:window-x="1949"
+     inkscape:window-y="142"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       sodipodi:type="arc"
+       style="fill:none;stroke:#999999;stroke-width:0.69999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       id="path2985"
+       sodipodi:cx="210.11172"
+       sodipodi:cy="201.81374"
+       sodipodi:rx="70.710678"
+       sodipodi:ry="70.710678"
+       d="m 280.8224,201.81374 a 70.710678,70.710678 0 1 1 -141.42135,0 70.710678,70.710678 0 1 1 141.42135,0 z" />
+    <rect
+       style="fill:none;stroke:#999999;stroke-width:0.69999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       id="rect2987"
+       width="82.832512"
+       height="72.73098"
+       x="343.45187"
+       y="127.06245" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 261.70701,300.10548 137.14286,-68.57143 -13.57143,104.28572 105.71429,2.85714 -232.14286,29.28572 z"
+       id="path2991"
+       inkscape:connector-curvature="0" />
+    <g
+       id="g3018"
+       transform="translate(-37.142857,-103.57143)">
+      <rect
+         y="222.01678"
+         x="508.10672"
+         height="72.73098"
+         width="82.832512"
+         id="rect2987-8"
+         style="fill:none;stroke:#999999;stroke-width:0.69999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+      <rect
+         y="177.86595"
+         x="534.49329"
+         height="72.73098"
+         width="82.832512"
+         id="rect2987-4"
+         style="fill:none;stroke:#999999;stroke-width:0.69999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+    </g>
+    <path
+       style="fill:none;stroke:#999999;stroke-width:0.69999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       d="M 550.71875 258.84375 L 550.71875 286 L 513.59375 286 L 513.59375 358.71875 L 596.40625 358.71875 L 596.40625 331.59375 L 633.5625 331.59375 L 633.5625 258.84375 L 550.71875 258.84375 z "
+       id="rect2987-5" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 276.42857,98.076465 430.71429,83.076464"
+       id="path3037"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 164.28571,391.64789 c 12.85715,-54.28571 55.00001,21.42858 84.28572,22.85715 29.28571,1.42857 30.71429,-14.28572 30.71429,-14.28572"
+       id="path3039"
+       inkscape:connector-curvature="0" />
+    <g
+       id="g3018-3"
+       transform="matrix(0.54511991,0,0,0.54511991,308.96645,74.66094)">
+      <rect
+         y="222.01678"
+         x="508.10672"
+         height="72.73098"
+         width="82.832512"
+         id="rect2987-8-5"
+         style="fill:none;stroke:#999999;stroke-width:1.28412116;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+      <rect
+         y="177.86595"
+         x="534.49329"
+         height="72.73098"
+         width="82.832512"
+         id="rect2987-4-6"
+         style="fill:none;stroke:#999999;stroke-width:1.28412116;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+    </g>
+  </g>
+</svg>

+ 330 - 0
tests/test_excellon.py

@@ -0,0 +1,330 @@
+import unittest
+import camlib
+
+
+class ExcellonNumberParseTestInch(unittest.TestCase):
+    # Inch base format: 00.0000
+
+    # LEADING ZEROS
+    # With leading zeros, when you type in a coordinate,
+    # the leading zeros must always be included.  Trailing zeros
+    # are unneeded and may be left off. The CNC-7 will automatically add them.
+
+    # TRAILING ZEROS
+    # You must show all zeros to the right of the number and can omit
+    # all zeros to the left of the number. The CNC-7 will count the number
+    # of digits you typed and automatically fill in the missing zeros.
+
+    def test_inch_leading_6digit(self):
+        excellon = camlib.Excellon()
+        self.assertEqual(excellon.zeros, "L")
+        self.assertEqual(excellon.parse_number("123456"), 12.3456)
+
+    def test_inch_leading_5digit(self):
+        excellon = camlib.Excellon()
+        self.assertEqual(excellon.parse_number("12345"), 12.345)
+
+    def test_inch_leading_15digit(self):
+        excellon = camlib.Excellon()
+        self.assertEqual(excellon.parse_number("012345"), 1.2345)
+
+    def test_inch_leading_51digit(self):
+        excellon = camlib.Excellon()
+        self.assertEqual(excellon.parse_number("123450"), 12.345)
+
+    def test_inch_trailing_6digit(self):
+        excellon = camlib.Excellon()
+        excellon.zeros = "T"
+        self.assertEqual(excellon.parse_number("123456"), 12.3456)
+
+    def test_inch_trailing_5digit(self):
+        excellon = camlib.Excellon()
+        excellon.zeros = "T"
+        self.assertEqual(excellon.parse_number("12345"), 1.2345)
+
+    def test_inch_trailing_15digit(self):
+        excellon = camlib.Excellon()
+        excellon.zeros = "T"
+        self.assertEqual(excellon.parse_number("012345"), 1.2345)
+
+    def test_inch_trailing_51digit(self):
+        excellon = camlib.Excellon()
+        excellon.zeros = "T"
+        self.assertEqual(excellon.parse_number("123450"), 12.345)
+
+
+class ExcellonNumberParseTestMetric(unittest.TestCase):
+    # Metric base format: 000.000
+
+    # LEADING ZEROS
+    # With leading zeros, when you type in a coordinate,
+    # the leading zeros must always be included.  Trailing zeros
+    # are unneeded and may be left off. The CNC-7 will automatically add them.
+
+    # TRAILING ZEROS
+    # You must show all zeros to the right of the number and can omit
+    # all zeros to the left of the number. The CNC-7 will count the number
+    # of digits you typed and automatically fill in the missing zeros.
+
+    def test_inch_leading_6digit(self):
+        excellon = camlib.Excellon()
+        excellon.units = "mm"
+        self.assertEqual(excellon.parse_number("123456"), 123.456)
+
+    def test_inch_leading_5digit(self):
+        excellon = camlib.Excellon()
+        excellon.units = "mm"
+        self.assertEqual(excellon.parse_number("12345"), 123.45)
+
+    def test_inch_leading_15digit(self):
+        excellon = camlib.Excellon()
+        excellon.units = "mm"
+        self.assertEqual(excellon.parse_number("012345"), 12.345)
+
+    def test_inch_leading_51digit(self):
+        excellon = camlib.Excellon()
+        excellon.units = "mm"
+        self.assertEqual(excellon.parse_number("123450"), 123.45)
+
+    def test_inch_trailing_6digit(self):
+        excellon = camlib.Excellon()
+        excellon.units = "mm"
+        excellon.zeros = "T"
+        self.assertEqual(excellon.parse_number("123456"), 123.456)
+
+    def test_inch_trailing_5digit(self):
+        excellon = camlib.Excellon()
+        excellon.units = "mm"
+        excellon.zeros = "T"
+        self.assertEqual(excellon.parse_number("12345"), 12.345)
+
+    def test_inch_trailing_15digit(self):
+        excellon = camlib.Excellon()
+        excellon.units = "mm"
+        excellon.zeros = "T"
+        self.assertEqual(excellon.parse_number("012345"), 12.345)
+
+    def test_inch_trailing_51digit(self):
+        excellon = camlib.Excellon()
+        excellon.units = "mm"
+        excellon.zeros = "T"
+        self.assertEqual(excellon.parse_number("123450"), 123.45)
+
+
+class ExcellonFormatM72Test(unittest.TestCase):
+
+    def setUp(self):
+        self.excellon = camlib.Excellon()
+        code = """
+        M48
+        M72
+        T1C.02362F197S550
+        T2C.03543F197S550
+        M95
+        T1
+        X9000Y11750
+        X30250Y10500
+        """
+        code = code.split('\n')
+        self.excellon.parse_lines(code)
+
+    def test_format(self):
+        self.assertEqual(self.excellon.units.lower(), "in")
+        self.assertEqual(self.excellon.zeros, "L")
+
+    def test_coords(self):
+        # For X9000 add the missing 00 on the right. Then divide by 10000.
+        self.assertEqual(self.excellon.drills[0]["point"].coords[0], (90.0, 11.75))
+        self.assertEqual(self.excellon.drills[1]["point"].coords[0], (30.25, 10.5))
+
+
+class ExcellonFormatM71Test(unittest.TestCase):
+
+    def setUp(self):
+        self.excellon = camlib.Excellon()
+        code = """
+        M48
+        M71
+        T1C.02362F197S550
+        T2C.03543F197S550
+        M95
+        T1
+        X9000Y11750
+        X30250Y10500
+        """
+        code = code.split('\n')
+        self.excellon.parse_lines(code)
+
+    def test_format(self):
+        self.assertEqual(self.excellon.units.lower(), "mm")
+        self.assertEqual(self.excellon.zeros, "L")
+
+    def test_coords(self):
+        # For X9000 add the missing 00 on the right. Then divide by 10000.
+        self.assertEqual(self.excellon.drills[0]["point"].coords[0], (900.0, 117.5))
+        self.assertEqual(self.excellon.drills[1]["point"].coords[0], (302.5, 105.0))
+
+
+class ExcellonFormatINCHLZTest(unittest.TestCase):
+
+    def setUp(self):
+        self.excellon = camlib.Excellon()
+        code = """
+        M48
+        INCH,LZ
+        T1C.02362F197S550
+        T2C.03543F197S550
+        M95
+        T1
+        X9000Y11750
+        X30250Y10500
+        """
+        code = code.split('\n')
+        self.excellon.parse_lines(code)
+
+    def test_format(self):
+        self.assertEqual(self.excellon.units.lower(), "in")
+        self.assertEqual(self.excellon.zeros, "L")
+
+    def test_coords(self):
+        # For X9000 add the missing 00 on the right. Then divide by 10000.
+        self.assertEqual(self.excellon.drills[0]["point"].coords[0], (90.0, 11.75))
+        self.assertEqual(self.excellon.drills[1]["point"].coords[0], (30.25, 10.5))
+
+
+class ExcellonFormatINCHTest(unittest.TestCase):
+
+    def setUp(self):
+        self.excellon = camlib.Excellon()
+        code = """
+        M48
+        INCH,LZ
+        T1C.02362F197S550
+        T2C.03543F197S550
+        M95
+        T1
+        X9000Y11750
+        X30250Y10500
+        """
+        code = code.split('\n')
+        self.excellon.parse_lines(code)
+
+    def test_format(self):
+        self.assertEqual(self.excellon.units.lower(), "in")
+        self.assertEqual(self.excellon.zeros, "L")
+
+    def test_coords(self):
+        # For X9000 add the missing 00 on the right. Then divide by 10000.
+        self.assertEqual(self.excellon.drills[0]["point"].coords[0], (90.0, 11.75))
+        self.assertEqual(self.excellon.drills[1]["point"].coords[0], (30.25, 10.5))
+
+
+class ExcellonFormatINCHTZTest(unittest.TestCase):
+
+    def setUp(self):
+        self.excellon = camlib.Excellon()
+        code = """
+        M48
+        INCH,TZ
+        T1C.02362F197S550
+        T2C.03543F197S550
+        M95
+        T1
+        X9000Y11750
+        X30250Y10500
+        """
+        code = code.split('\n')
+        self.excellon.parse_lines(code)
+
+    def test_format(self):
+        self.assertEqual(self.excellon.units.lower(), "in")
+        self.assertEqual(self.excellon.zeros, "T")
+
+    def test_coords(self):
+        # For X9000 add the missing 00 on the right. Then divide by 10000.
+        self.assertEqual(self.excellon.drills[0]["point"].coords[0], (0.9, 1.175))
+        self.assertEqual(self.excellon.drills[1]["point"].coords[0], (3.025, 1.05))
+
+
+class ExcellonFormatMETRICLZTest(unittest.TestCase):
+
+    def setUp(self):
+        self.excellon = camlib.Excellon()
+        code = """
+        M48
+        METRIC,LZ
+        T1C.02362F197S550
+        T2C.03543F197S550
+        M95
+        T1
+        X9000Y11750
+        X30250Y10500
+        """
+        code = code.split('\n')
+        self.excellon.parse_lines(code)
+
+    def test_format(self):
+        self.assertEqual(self.excellon.units.lower(), "mm")
+        self.assertEqual(self.excellon.zeros, "L")
+
+    def test_coords(self):
+        # For X9000 add the missing 00 on the right. Then divide by 10000.
+        self.assertEqual(self.excellon.drills[0]["point"].coords[0], (900.0, 117.5))
+        self.assertEqual(self.excellon.drills[1]["point"].coords[0], (302.5, 105.0))
+
+
+class ExcellonFormatMETRICTest(unittest.TestCase):
+
+    def setUp(self):
+        self.excellon = camlib.Excellon()
+        code = """
+        M48
+        METRIC,LZ
+        T1C.02362F197S550
+        T2C.03543F197S550
+        M95
+        T1
+        X9000Y11750
+        X30250Y10500
+        """
+        code = code.split('\n')
+        self.excellon.parse_lines(code)
+
+    def test_format(self):
+        self.assertEqual(self.excellon.units.lower(), "mm")
+        self.assertEqual(self.excellon.zeros, "L")
+
+    def test_coords(self):
+        # For X9000 add the missing 00 on the right. Then divide by 10000.
+        self.assertEqual(self.excellon.drills[0]["point"].coords[0], (900.0, 117.5))
+        self.assertEqual(self.excellon.drills[1]["point"].coords[0], (302.5, 105.0))
+
+
+class ExcellonFormatMETRICTZTest(unittest.TestCase):
+
+    def setUp(self):
+        self.excellon = camlib.Excellon()
+        code = """
+        M48
+        METRIC,TZ
+        T1C.02362F197S550
+        T2C.03543F197S550
+        M95
+        T1
+        X9000Y11750
+        X30250Y10500
+        """
+        code = code.split('\n')
+        self.excellon.parse_lines(code)
+
+    def test_format(self):
+        self.assertEqual(self.excellon.units.lower(), "mm")
+        self.assertEqual(self.excellon.zeros, "T")
+
+    def test_coords(self):
+        # For X9000 add the missing 00 on the right. Then divide by 10000.
+        self.assertEqual(self.excellon.drills[0]["point"].coords[0], (9.0, 11.75))
+        self.assertEqual(self.excellon.drills[1]["point"].coords[0], (30.25, 10.5))
+
+if __name__ == '__main__':
+    unittest.main()

+ 34 - 0
tests/test_gerber_buffer.py

@@ -0,0 +1,34 @@
+import unittest
+import camlib
+
+
+class GerberBuffer(unittest.TestCase):
+    def setUp(self):
+        self.gerber1 = camlib.Gerber()
+        self.gerber1.use_buffer_for_union = True
+        self.gerber1.parse_file("tests/gerber_files/STM32F4-spindle.cmp")
+        geometry1 = self.gerber1.solid_geometry
+        self.geometry1_area = self.compute_area(geometry1)
+        self.gerber2 = camlib.Gerber()
+        self.gerber2.use_buffer_for_union = False
+        self.gerber2.parse_file("tests/gerber_files/STM32F4-spindle.cmp")
+        geometry2 = self.gerber2.solid_geometry
+        self.geometry2_area = self.compute_area (geometry2)
+
+    def compute_area(self, geometry):
+        area = 0
+        try:
+            for geo in geometry:
+                area += geo.area
+
+        ## Not iterable, do the actual indexing and add.
+        except TypeError:
+            area = geometry.area
+        return area
+
+    def test_buffer(self):
+        self.assertLessEqual(abs(self.geometry2_area - self.geometry1_area), 0.000001)
+
+
+if __name__ == '__main__':
+    unittest.main()

+ 136 - 0
tests/test_gerber_flow.py

@@ -0,0 +1,136 @@
+import sys
+import unittest
+from PyQt4 import QtGui
+from FlatCAMApp import App
+from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob
+from ObjectUI import GerberObjectUI, GeometryObjectUI
+from time import sleep
+import os
+
+
+class GerberFlowTestCase(unittest.TestCase):
+
+    filename = 'simple1.gbr'
+
+    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)
+
+        self.fc.open_gerber('tests/gerber_files/' + self.filename)
+
+    def tearDown(self):
+        del self.fc
+        del self.app
+
+    def test_flow(self):
+        # Names of available objects.
+        names = self.fc.collection.get_names()
+        print names
+
+        #--------------------------------------
+        # Total of 1 objects.
+        #--------------------------------------
+        self.assertEquals(len(names), 1,
+                          "Expected 1 object, found %d" % len(names))
+
+        #--------------------------------------
+        # Object's name matches the file name.
+        #--------------------------------------
+        self.assertEquals(names[0], self.filename,
+                          "Expected name == %s, got %s" % (self.filename, names[0]))
+
+        #---------------------------------------
+        # Get object by that name, make sure it's a FlatCAMGerber.
+        #---------------------------------------
+        gerber_name = names[0]
+        gerber_obj = self.fc.collection.get_by_name(gerber_name)
+        self.assertTrue(isinstance(gerber_obj, FlatCAMGerber),
+                        "Expected FlatCAMGerber, instead, %s is %s" %
+                        (gerber_name, type(gerber_obj)))
+
+        #--------------------------------------------------
+        # Create isolation routing using default values
+        # and by clicking on the button.
+        #--------------------------------------------------
+        # Get the object's GUI and click on "Generate Geometry" under
+        # "Isolation Routing"
+        assert isinstance(gerber_obj, FlatCAMGerber)  # Just for the IDE
+        gerber_obj.build_ui()  # Open the object's UI.
+        ui = gerber_obj.ui
+        assert isinstance(ui, GerberObjectUI)
+        ui.generate_iso_button.click()  # Click
+
+        #---------------------------------------------
+        # Check that only 1 object has been created.
+        #---------------------------------------------
+        names = self.fc.collection.get_names()
+        self.assertEqual(len(names), 2,
+                         "Expected 2 objects, found %d" % len(names))
+
+        #-------------------------------------------------------
+        # Make sure the Geometry Object has the correct name
+        #-------------------------------------------------------
+        geo_name = gerber_name + "_iso"
+        self.assertTrue(geo_name in names,
+                        "Object named %s not found." % geo_name)
+
+        #-------------------------------------------------------
+        # Get the object make sure it's a geometry object
+        #-------------------------------------------------------
+        geo_obj = self.fc.collection.get_by_name(geo_name)
+        self.assertTrue(isinstance(geo_obj, FlatCAMGeometry),
+                        "Expected a FlatCAMGeometry, got %s" % type(geo_obj))
+
+        #------------------------------------
+        # Open the UI, make CNCObject
+        #------------------------------------
+        geo_obj.build_ui()
+        ui = geo_obj.ui
+        assert isinstance(ui, GeometryObjectUI)  # Just for the IDE
+        ui.generate_cnc_button.click()  # Click
+
+        # Work is done in a separate thread and results are
+        # passed via events to the main event loop which is
+        # not running. Run only for pending events.
+        #
+        # I'm not sure why, but running it only once does
+        # not catch the new object. Might be a timing issue.
+        # http://pyqt.sourceforge.net/Docs/PyQt4/qeventloop.html#details
+        for _ in range(2):
+            sleep(0.1)
+            self.app.processEvents()
+
+        #---------------------------------------------
+        # Check that only 1 object has been created.
+        #---------------------------------------------
+        names = self.fc.collection.get_names()
+        self.assertEqual(len(names), 3,
+                         "Expected 3 objects, found %d" % len(names))
+
+        #-------------------------------------------------------
+        # Make sure the CNC Job Object has the correct name
+        #-------------------------------------------------------
+        cnc_name = geo_name + "_cnc"
+        self.assertTrue(cnc_name in names,
+                        "Object named %s not found." % geo_name)
+
+        #-------------------------------------------------------
+        # Get the object make sure it's a CNC Job object
+        #-------------------------------------------------------
+        cnc_obj = self.fc.collection.get_by_name(cnc_name)
+        self.assertTrue(isinstance(cnc_obj, FlatCAMCNCjob),
+                        "Expected a FlatCAMCNCJob, got %s" % type(geo_obj))
+
+        #-----------------------------------------
+        # Export G-Code, check output
+        #-----------------------------------------
+        assert isinstance(cnc_obj, FlatCAMCNCjob)
+        output_filename = "tests/tmp/" + cnc_name + ".gcode"
+        cnc_obj.export_gcode(output_filename)
+        self.assertTrue(os.path.isfile(output_filename))
+        os.remove(output_filename)
+
+        print names

+ 212 - 0
tests/test_paint.py

@@ -0,0 +1,212 @@
+import unittest
+
+from shapely.geometry import LineString, Polygon
+from shapely.ops import cascaded_union, unary_union
+from matplotlib.pyplot import plot, subplot, show, cla, clf, xlim, ylim, title
+from camlib import *
+from copy import deepcopy
+
+
+def mkstorage(paths):
+    def get_pts(o):
+        return [o.coords[0], o.coords[-1]]
+    storage = FlatCAMRTreeStorage()
+    storage.get_points = get_pts
+    for p in paths:
+        storage.insert(p)
+    return storage
+
+
+def plotg2(geo, solid_poly=False, color="black", linestyle='solid'):
+
+    try:
+        for sub_geo in geo:
+            plotg2(sub_geo, solid_poly=solid_poly, color=color, linestyle=linestyle)
+    except TypeError:
+        if type(geo) == Polygon:
+            if solid_poly:
+                patch = PolygonPatch(geo,
+                                     #facecolor="#BBF268",
+                                     facecolor=color,
+                                     edgecolor="#006E20",
+                                     alpha=0.5,
+                                     zorder=2)
+                ax = subplot(111)
+                ax.add_patch(patch)
+            else:
+                x, y = geo.exterior.coords.xy
+                plot(x, y, color=color, linestyle=linestyle)
+                for ints in geo.interiors:
+                    x, y = ints.coords.xy
+                    plot(x, y, color=color, linestyle=linestyle)
+
+        if type(geo) == LineString or type(geo) == LinearRing:
+            x, y = geo.coords.xy
+            plot(x, y, color=color, linestyle=linestyle)
+
+        if type(geo) == Point:
+            x, y = geo.coords.xy
+            plot(x, y, 'o')
+
+
+class PaintTestCase(unittest.TestCase):
+    # def __init__(self):
+    #     super(PaintTestCase, self).__init__()
+    #     self.boundary = None
+    #     self.descr = None
+
+    def plot_summary_A(self, paths, tooldia, result, msg):
+        plotg2(self.boundary, solid_poly=True, color="green")
+        plotg2(paths, color="red")
+        plotg2([r.buffer(tooldia / 2) for r in result], solid_poly=True, color="blue")
+        plotg2(result, color="black", linestyle='dashed')
+        title(msg)
+        xlim(0, 5)
+        ylim(0, 5)
+        show()
+
+
+class PaintConnectTest(PaintTestCase):
+    """
+    Simple rectangular boundary and paths inside.
+    """
+
+    def setUp(self):
+        self.boundary = Polygon([[0, 0], [0, 5], [5, 5], [5, 0]])
+
+    def test_jump(self):
+        print "Test: WALK Expected"
+        paths = [
+            LineString([[0.5, 2], [2, 4.5]]),
+            LineString([[2, 0.5], [4.5, 2]])
+        ]
+        for p in paths:
+            print p
+
+        tooldia = 1.0
+
+        print "--"
+        result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)
+
+        result = list(result.get_objects())
+        for r in result:
+            print r
+
+        self.assertEqual(len(result), 1)
+
+        # self.plot_summary_A(paths, tooldia, result, "WALK expected.")
+
+    def test_no_jump1(self):
+        print "Test: FLY Expected"
+        paths = [
+            LineString([[0, 2], [2, 5]]),
+            LineString([[2, 0], [5, 2]])
+        ]
+        for p in paths:
+            print p
+
+        tooldia = 1.0
+
+        print "--"
+        result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)
+
+        result = list(result.get_objects())
+        for r in result:
+            print r
+
+        self.assertEqual(len(result), len(paths))
+
+        # self.plot_summary_A(paths, tooldia, result, "FLY Expected")
+
+    def test_no_jump2(self):
+        print "Test: FLY Expected"
+        paths = [
+            LineString([[0.5, 2], [2, 4.5]]),
+            LineString([[2, 0.5], [4.5, 2]])
+        ]
+        for p in paths:
+            print p
+
+        tooldia = 1.1
+
+        print "--"
+        result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)
+
+        result = list(result.get_objects())
+        for r in result:
+            print r
+
+        self.assertEqual(len(result), len(paths))
+
+        # self.plot_summary_A(paths, tooldia, result, "FLY Expected")
+
+
+class PaintConnectTest2(PaintTestCase):
+    """
+    Boundary with an internal cutout.
+    """
+
+    def setUp(self):
+        self.boundary = Polygon([[0, 0], [0, 5], [5, 5], [5, 0]])
+        self.boundary = self.boundary.difference(
+            Polygon([[2, 1], [3, 1], [3, 4], [2, 4]])
+        )
+
+    def test_no_jump3(self):
+        print "TEST: No jump expected"
+        paths = [
+            LineString([[0.5, 1], [1.5, 3]]),
+            LineString([[4, 1], [4, 4]])
+        ]
+        for p in paths:
+            print p
+
+        tooldia = 1.0
+
+        print "--"
+        result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)
+
+        result = list(result.get_objects())
+        for r in result:
+            print r
+
+        self.assertEqual(len(result), len(paths))
+
+        # self.plot_summary_A(paths, tooldia, result, "FLY Expected")
+
+
+class PaintConnectTest3(PaintTestCase):
+    """
+    Tests with linerings among elements.
+    """
+
+    def setUp(self):
+        self.boundary = Polygon([[0, 0], [0, 5], [5, 5], [5, 0]])
+        print "TEST w/ LinearRings"
+
+    def test_jump2(self):
+        print "Test: WALK Expected"
+        paths = [
+            LineString([[0.5, 2], [2, 4.5]]),
+            LineString([[2, 0.5], [4.5, 2]]),
+            self.boundary.buffer(-0.5).exterior
+        ]
+        for p in paths:
+            print p
+
+        tooldia = 1.0
+
+        print "--"
+        result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)
+
+        result = list(result.get_objects())
+        for r in result:
+            print r
+
+        self.assertEqual(len(result), 1)
+
+        # self.plot_summary_A(paths, tooldia, result, "WALK Expected")
+
+
+if __name__ == '__main__':
+    unittest.main()

+ 88 - 0
tests/test_pathconnect.py

@@ -0,0 +1,88 @@
+import unittest
+
+from shapely.geometry import LineString, Polygon
+from shapely.ops import cascaded_union, unary_union
+from matplotlib.pyplot import plot, subplot, show, cla, clf, xlim, ylim, title
+from camlib import *
+from random import random
+
+
+def mkstorage(paths):
+    def get_pts(o):
+        return [o.coords[0], o.coords[-1]]
+    storage = FlatCAMRTreeStorage()
+    storage.get_points = get_pts
+    for p in paths:
+        storage.insert(p)
+    return storage
+
+
+class PathConnectTest1(unittest.TestCase):
+
+    def setUp(self):
+        print "PathConnectTest1.setUp()"
+        pass
+
+    def test_simple_connect(self):
+        paths = [
+            LineString([[0, 0], [1, 1]]),
+            LineString([[1, 1], [2, 1]])
+        ]
+
+        result = Geometry.path_connect(mkstorage(paths))
+
+        result = list(result.get_objects())
+        self.assertEqual(len(result), 1)
+        self.assertTrue(result[0].equals(LineString([[0, 0], [1, 1], [2, 1]])))
+
+    def test_interfere_connect(self):
+        paths = [
+            LineString([[0, 0], [1, 1]]),
+            LineString([[1, 1], [2, 1]]),
+            LineString([[-0.5, 0.5], [0.5, 0]])
+        ]
+
+        result = Geometry.path_connect(mkstorage(paths))
+
+        result = list(result.get_objects())
+        self.assertEqual(len(result), 2)
+        matches = [p for p in result if p.equals(LineString([[0, 0], [1, 1], [2, 1]]))]
+        self.assertEqual(len(matches), 1)
+
+    def test_simple_connect_offset1(self):
+        for i in range(20):
+            offset_x = random()
+            offset_y = random()
+
+            paths = [
+                LineString([[0 + offset_x, 0 + offset_y], [1 + offset_x, 1 + offset_y]]),
+                LineString([[1 + offset_x, 1 + offset_y], [2 + offset_x, 1 + offset_y]])
+            ]
+
+            result = Geometry.path_connect(mkstorage(paths))
+
+            result = list(result.get_objects())
+            self.assertEqual(len(result), 1)
+            self.assertTrue(result[0].equals(LineString([[0 + offset_x, 0 + offset_y],
+                                                         [1 + offset_x, 1 + offset_y],
+                                                         [2 + offset_x, 1 + offset_y]])))
+
+    def test_ring_interfere_connect(self):
+        print
+        print "TEST STARTING ..."
+
+        paths = [
+            LineString([[0, 0], [1, 1]]),
+            LineString([[1, 1], [2, 1]]),
+            LinearRing([[1, 1], [2, 2], [1, 3], [0, 2]])
+        ]
+
+        result = Geometry.path_connect(mkstorage(paths))
+
+        result = list(result.get_objects())
+        self.assertEqual(len(result), 2)
+        matches = [p for p in result if p.equals(LineString([[0, 0], [1, 1], [2, 1]]))]
+        self.assertEqual(len(matches), 1)
+
+if __name__ == "__main__":
+    unittest.main()

+ 8 - 0
tests/toolpath_optimization_profiling/toollift_minimization_line_profile1.py

@@ -0,0 +1,8 @@
+# Run kernprof -l -v gerber_parsing_line_profile_1.py
+import sys
+sys.path.append('../../')
+from camlib import *
+from shapely.geometry import Polygon
+
+poly = Polygon([(0.0, 0.0), (1.0, 0.0), (1.0, 0.5), (0.0, 0.5)])
+result = Geometry.clear_polygon2(poly, 0.01)

+ 11 - 0
tests/toolpath_optimization_profiling/toollift_minimization_profile1.py

@@ -0,0 +1,11 @@
+import cProfile
+import pstats
+from camlib import *
+from shapely.geometry import Polygon
+
+poly = Polygon([(0.0, 0.0), (1.0, 0.0), (1.0, 0.5), (0.0, 0.5)])
+
+cProfile.run('result = Geometry.clear_polygon2(poly, 0.01)',
+             'toollist_minimization_profile', sort='cumtime')
+p = pstats.Stats('toollist_minimization_profile')
+p.sort_stats('cumulative').print_stats(.1)

Некоторые файлы не были показаны из-за большого количества измененных файлов