Code

Extensions. New Gcode tools extension (Bug #654528).
authorJazzyNico <nicoduf@yahoo.fr>
Fri, 8 Oct 2010 10:44:19 +0000 (12:44 +0200)
committerJazzyNico <nicoduf@yahoo.fr>
Fri, 8 Oct 2010 10:44:19 +0000 (12:44 +0200)
13 files changed:
po/POTFILES.in
po/inkscape.pot
share/extensions/Makefile.am
share/extensions/gcodetools.py [new file with mode: 0644]
share/extensions/gcodetools_all_in_one.inx [new file with mode: 0644]
share/extensions/gcodetools_area.inx [new file with mode: 0644]
share/extensions/gcodetools_check_for_updates.inx [new file with mode: 0644]
share/extensions/gcodetools_dxf_points.inx [new file with mode: 0644]
share/extensions/gcodetools_engraving.inx [new file with mode: 0644]
share/extensions/gcodetools_lathe.inx [new file with mode: 0644]
share/extensions/gcodetools_orientation_points.inx [new file with mode: 0644]
share/extensions/gcodetools_path_to_gcode.inx [new file with mode: 0644]
share/extensions/gcodetools_tools_library.inx [new file with mode: 0644]

index 83da4321b0fe5f85b499ad81212d25375e9efa45..6bd2da7568b15b49dc78f5a9ee470fabc041bd69 100644 (file)
@@ -144,6 +144,7 @@ share/extensions/dxf_outlines.py
 share/extensions/embedimage.py
 share/extensions/export_gimp_palette.py
 share/extensions/extractimage.py
+share/extensions/gcodetools.py
 share/extensions/guides_creator.py
 share/extensions/inkex.py
 share/extensions/markers_strokepaint.py
@@ -508,6 +509,15 @@ share/extensions/web-transmit-att.py
 [type: gettext/xml] share/extensions/fractalize.inx
 [type: gettext/xml] share/extensions/funcplot.inx
 [type: gettext/xml] share/extensions/gears.inx
+[type: gettext/xml] share/extensions/gcodetools_all_in_one.inx
+[type: gettext/xml] share/extensions/gcodetools_area.inx
+[type: gettext/xml] share/extensions/gcodetools_check_for_updates.inx
+[type: gettext/xml] share/extensions/gcodetools_dxf_points.inx
+[type: gettext/xml] share/extensions/gcodetools_engraving.inx
+[type: gettext/xml] share/extensions/gcodetools_lathe.inx
+[type: gettext/xml] share/extensions/gcodetools_orientation_points.inx
+[type: gettext/xml] share/extensions/gcodetools_path_to_gcode.inx
+[type: gettext/xml] share/extensions/gcodetools_tools_library.inx
 [type: gettext/xml] share/extensions/gimp_xcf.inx
 [type: gettext/xml] share/extensions/grid_cartesian.inx
 [type: gettext/xml] share/extensions/grid_polar.inx
index a2de67bed3ec388f0eb5a885da16cd614f81a1fe..34a16356f87d35911eefda9f10ea9d908c160326 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: inkscape-devel@lists.sourceforge.net\n"
-"POT-Creation-Date: 2010-10-02 17:43+0200\n"
+"POT-Creation-Date: 2010-10-08 12:37+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -238,6 +238,15 @@ msgstr ""
 #: ../share/extensions/web-set-att.inx.h:4
 #: ../share/extensions/web-transmit-att.inx.h:4
 #: ../src/ui/dialog/extension-editor.cpp:81
+#: ../share/extensions/gcodetools_all_in_one.inx.h:25
+#: ../share/extensions/gcodetools_area.inx.h:17
+#: ../share/extensions/gcodetools_check_for_updates.inx.h:5
+#: ../share/extensions/gcodetools_dxf_points.inx.h:13
+#: ../share/extensions/gcodetools_engraving.inx.h:11
+#: ../share/extensions/gcodetools_lathe.inx.h:14
+#: ../share/extensions/gcodetools_orientation_points.inx.h:5
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:11
+#: ../share/extensions/gcodetools_tools_library.inx.h:3
 msgid "Help"
 msgstr ""
 
@@ -264,6 +273,11 @@ msgstr ""
 #: ../share/extensions/web-set-att.inx.h:6
 #: ../share/extensions/web-transmit-att.inx.h:6
 #: ../src/ui/dialog/tracedialog.cpp:617 ../src/ui/dialog/tracedialog.cpp:623
+#: ../share/extensions/gcodetools_all_in_one.inx.h:33
+#: ../share/extensions/gcodetools_area.inx.h:22
+#: ../share/extensions/gcodetools_engraving.inx.h:16
+#: ../share/extensions/gcodetools_lathe.inx.h:22
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:15
 msgid "Options"
 msgstr ""
 
@@ -2238,6 +2252,8 @@ msgid "Color Markers to Match Stroke"
 msgstr ""
 
 #: ../share/extensions/measure.inx.h:1
+#: ../share/extensions/gcodetools_all_in_one.inx.h:8
+#: ../share/extensions/gcodetools_area.inx.h:5
 msgid "Area"
 msgstr ""
 
@@ -4067,6 +4083,169 @@ msgstr ""
 msgid "Unable to find image data."
 msgstr ""
 
+#: ../share/extensions/gcodetools.py:3087
+msgid ""
+"Directory does not exist! Please specify existing directory at Preferences "
+"tab!"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3112
+#, python-format
+msgid ""
+"Can not write to specified file!\n"
+"%s"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3240
+#, python-format
+msgid ""
+"Orientation points for '%s' layer have not been found! Please add "
+"orientation points using Orientation tab!"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3246
+#, python-format
+msgid "There are more than one orientation point groups in '%s' layer"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3277
+#: ../share/extensions/gcodetools.py:3279
+msgid ""
+"Orientation points are wrong! (if there are two orientation points they "
+"sould not be the same. If there are three orientation points they should not "
+"be in a straight line.)"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3398
+#, python-format
+msgid ""
+"Warning! Found bad orientation points in '%s' layer. Resulting Gcode could "
+"be corrupt!"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3411
+msgid ""
+"This extension works with Paths and Dynamic Offsets and groups of them only! "
+"All other objects will be ignored!\n"
+"Solution 1: press Path->Object to path or Shift+Ctrl+C.\n"
+"Solution 2: Path->Dynamic offset or Ctrl+J.\n"
+"Solution 3: export all contours to PostScript level 2 (File->Save As->.ps) "
+"and File->Import this file."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3468
+#, python-format
+msgid ""
+"Warning! Tool's and default tool's parameter's (%s) types are not the same "
+"( type('%s') != type('%s') )."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3471
+#, python-format
+msgid "Warning! Tool has parameter that default tool has not ( '%s': '%s' )."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3485
+#, python-format
+msgid "Layer '%s' contains more than one tool!"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3488
+#, python-format
+msgid ""
+"Can not find tool for '%s' layer! Please add one with Tools library tab!"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3591
+#: ../share/extensions/gcodetools.py:3672
+msgid "No paths are selected! Trying to work on all available paths."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3610
+#: ../share/extensions/gcodetools.py:3681
+msgid ""
+"Warning: One or more paths dont have 'd' parameter, try to Ungroup (Ctrl"
+"+Shift+G) and Object to Path (Ctrl+Shift+C)!"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3640
+msgid ""
+"Noting is selected. Please select something to convert to drill point "
+"(dxfpoint) or clear point sign."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3713
+#: ../share/extensions/gcodetools.py:3807
+msgid "This extension requires at least one selected path."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3719
+#, python-format
+msgid "Tool diameter must be > 0 but tool's diameter on '%s' layer is not!"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:3730
+msgid "Warning: omitting non-path"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:4062
+#, python-format
+msgid "Tool '%s' has no shape!"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:4072
+msgid "No need to engrave sharp angles."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:4085
+msgid ""
+"Active layer already has orientation points! Remove them or select another "
+"layer!"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:4133
+msgid "Active layer already has a tool! Remove it or select another layer!"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:4257
+msgid "Selection is empty! Will compute whole drawing."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:4317
+msgid ""
+"Tutorials, manuals and support can be found at\n"
+"English support forum:\n"
+"\thttp://www.cnc-club.ru/gcodetools\n"
+"and Russian support forum:\n"
+"\thttp://www.cnc-club.ru/gcodetoolsru"
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:4362
+msgid "Lathe X and Z axis remap should be 'X', 'Y' or 'Z'. Exiting..."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:4365
+msgid "Lathe X and Z axis remap should be the same. Exiting..."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:4526
+msgid ""
+"Select one of the active tabs - Path to Gcode, Area, Engraving, DXF points, "
+"Orientation, Offset, Lathe or Tools library."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:4532
+msgid ""
+"Orientation points have not been defined! A default set of orientation "
+"points has been automatically added."
+msgstr ""
+
+#: ../share/extensions/gcodetools.py:4536
+msgid ""
+"Cutting tool has not been defined! A default tool has been automatically "
+"added."
+msgstr ""
+
 #: ../share/extensions/inkex.py:67
 #, python-format
 msgid ""
@@ -6319,7 +6498,7 @@ msgid "Defines the direction and magnitude of the extrusion"
 msgstr ""
 
 #: ../src/sp-flowtext.cpp:378 ../src/sp-text.cpp:427
-#: ../src/text-context.cpp:1604
+#: ../src/text-context.cpp:1628
 msgid " [truncated]"
 msgstr ""
 
@@ -7797,7 +7976,7 @@ msgstr ""
 msgid "Set as default"
 msgstr ""
 
-#: ../src/dialogs/text-edit.cpp:665 ../src/text-context.cpp:1500
+#: ../src/dialogs/text-edit.cpp:665 ../src/text-context.cpp:1524
 msgid "Set text style"
 msgstr ""
 
@@ -8484,7 +8663,7 @@ msgid ""
 "this extension."
 msgstr ""
 
-#: ../src/extension/implementation/script.cpp:985
+#: ../src/extension/implementation/script.cpp:989
 msgid ""
 "Inkscape has received additional data from the script executed.  The script "
 "did not return an error, but this may indicate the results will not be as "
@@ -10509,6 +10688,8 @@ msgstr ""
 
 #. Name
 #: ../src/libgdl/gdl-dock-item.c:287 ../src/widgets/toolbox.cpp:7616
+#: ../share/extensions/gcodetools_all_in_one.inx.h:34
+#: ../share/extensions/gcodetools_orientation_points.inx.h:6
 msgid "Orientation"
 msgstr ""
 
@@ -12757,7 +12938,7 @@ msgstr ""
 msgid "<b>Nothing</b> was deleted."
 msgstr ""
 
-#: ../src/selection-chemistry.cpp:330 ../src/text-context.cpp:1002
+#: ../src/selection-chemistry.cpp:330 ../src/text-context.cpp:1026
 #: ../src/ui/dialog/swatches.cpp:208 ../src/ui/dialog/swatches.cpp:271
 #: ../src/widgets/toolbox.cpp:1362 ../src/widgets/toolbox.cpp:6130
 msgid "Delete"
@@ -13333,12 +13514,12 @@ msgstr ""
 msgid "<b>Rotate</b>: %0.2f&#176;; with <b>Ctrl</b> to snap angle"
 msgstr ""
 
-#: ../src/seltrans.cpp:1367
+#: ../src/seltrans.cpp:1364
 #, c-format
 msgid "Move <b>center</b> to %s, %s"
 msgstr ""
 
-#: ../src/seltrans.cpp:1542
+#: ../src/seltrans.cpp:1539
 #, c-format
 msgid ""
 "<b>Move</b> by %s, %s; with <b>Ctrl</b> to restrict to horizontal/vertical; "
@@ -13878,120 +14059,120 @@ msgstr ""
 msgid "Unicode (<b>Enter</b> to finish): %s: %s"
 msgstr ""
 
-#: ../src/text-context.cpp:581 ../src/text-context.cpp:856
+#: ../src/text-context.cpp:581 ../src/text-context.cpp:880
 msgid "Unicode (<b>Enter</b> to finish): "
 msgstr ""
 
-#: ../src/text-context.cpp:656
+#: ../src/text-context.cpp:668
 #, c-format
 msgid "<b>Flowed text frame</b>: %s &#215; %s"
 msgstr ""
 
-#: ../src/text-context.cpp:688
+#: ../src/text-context.cpp:714
 msgid "Type text; <b>Enter</b> to start new line."
 msgstr ""
 
-#: ../src/text-context.cpp:701
+#: ../src/text-context.cpp:725
 msgid "Flowed text is created."
 msgstr ""
 
-#: ../src/text-context.cpp:703
+#: ../src/text-context.cpp:727
 msgid "Create flowed text"
 msgstr ""
 
-#: ../src/text-context.cpp:705
+#: ../src/text-context.cpp:729
 msgid ""
 "The frame is <b>too small</b> for the current font size. Flowed text not "
 "created."
 msgstr ""
 
-#: ../src/text-context.cpp:841
+#: ../src/text-context.cpp:865
 msgid "No-break space"
 msgstr ""
 
-#: ../src/text-context.cpp:843
+#: ../src/text-context.cpp:867
 msgid "Insert no-break space"
 msgstr ""
 
-#: ../src/text-context.cpp:880
+#: ../src/text-context.cpp:904
 msgid "Make bold"
 msgstr ""
 
-#: ../src/text-context.cpp:898
+#: ../src/text-context.cpp:922
 msgid "Make italic"
 msgstr ""
 
-#: ../src/text-context.cpp:937
+#: ../src/text-context.cpp:961
 msgid "New line"
 msgstr ""
 
-#: ../src/text-context.cpp:971
+#: ../src/text-context.cpp:995
 msgid "Backspace"
 msgstr ""
 
-#: ../src/text-context.cpp:1019
+#: ../src/text-context.cpp:1043
 msgid "Kern to the left"
 msgstr ""
 
-#: ../src/text-context.cpp:1044
+#: ../src/text-context.cpp:1068
 msgid "Kern to the right"
 msgstr ""
 
-#: ../src/text-context.cpp:1069
+#: ../src/text-context.cpp:1093
 msgid "Kern up"
 msgstr ""
 
-#: ../src/text-context.cpp:1095
+#: ../src/text-context.cpp:1119
 msgid "Kern down"
 msgstr ""
 
-#: ../src/text-context.cpp:1172
+#: ../src/text-context.cpp:1196
 msgid "Rotate counterclockwise"
 msgstr ""
 
-#: ../src/text-context.cpp:1193
+#: ../src/text-context.cpp:1217
 msgid "Rotate clockwise"
 msgstr ""
 
-#: ../src/text-context.cpp:1210
+#: ../src/text-context.cpp:1234
 msgid "Contract line spacing"
 msgstr ""
 
-#: ../src/text-context.cpp:1218
+#: ../src/text-context.cpp:1242
 msgid "Contract letter spacing"
 msgstr ""
 
-#: ../src/text-context.cpp:1237
+#: ../src/text-context.cpp:1261
 msgid "Expand line spacing"
 msgstr ""
 
-#: ../src/text-context.cpp:1245
+#: ../src/text-context.cpp:1269
 msgid "Expand letter spacing"
 msgstr ""
 
-#: ../src/text-context.cpp:1375
+#: ../src/text-context.cpp:1399
 msgid "Paste text"
 msgstr ""
 
-#: ../src/text-context.cpp:1621
+#: ../src/text-context.cpp:1645
 #, c-format
 msgid ""
 "Type or edit flowed text (%d characters%s); <b>Enter</b> to start new "
 "paragraph."
 msgstr ""
 
-#: ../src/text-context.cpp:1623
+#: ../src/text-context.cpp:1647
 #, c-format
 msgid "Type or edit text (%d characters%s); <b>Enter</b> to start new line."
 msgstr ""
 
-#: ../src/text-context.cpp:1631 ../src/tools-switch.cpp:197
+#: ../src/text-context.cpp:1655 ../src/tools-switch.cpp:197
 msgid ""
 "<b>Click</b> to select or create text, <b>drag</b> to create flowed text; "
 "then type."
 msgstr ""
 
-#: ../src/text-context.cpp:1741
+#: ../src/text-context.cpp:1765
 msgid "Type text"
 msgstr ""
 
@@ -25265,6 +25446,465 @@ msgstr ""
 #. End:
 #.
 #. vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+#: ../share/extensions/gcodetools_all_in_one.inx.h:1
+#: ../share/extensions/gcodetools_area.inx.h:1
+msgid ""
+"\"Create area offset\": creates several Inkscape path offsets to fill "
+"original path's area up to \"Area radius\" value. Outlines start from \"1/2 D"
+"\" up to \"Area width\" total width with \"D\" steps where D is taken from "
+"the nearest tool definition (\"Tool diameter\" value). Only one offset will "
+"be created if the \"Area width\" is equal to \"1/2 D\"."
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:2
+#: ../share/extensions/gcodetools_orientation_points.inx.h:1
+msgid "2-points mode (move and rotate, maintained aspect ratio X/Y)"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:3
+#: ../share/extensions/gcodetools_orientation_points.inx.h:2
+msgid "3-points mode (move, rotate and mirror, different X/Y scale)"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:4
+#: ../share/extensions/gcodetools_area.inx.h:2
+msgid "Action:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:5
+#: ../share/extensions/gcodetools_area.inx.h:3
+#: ../share/extensions/gcodetools_dxf_points.inx.h:1
+#: ../share/extensions/gcodetools_engraving.inx.h:1
+#: ../share/extensions/gcodetools_lathe.inx.h:1
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:1
+msgid "Add numeric suffix to filename"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:6
+#: ../share/extensions/gcodetools_area.inx.h:4
+#: ../share/extensions/gcodetools_dxf_points.inx.h:2
+#: ../share/extensions/gcodetools_engraving.inx.h:2
+#: ../share/extensions/gcodetools_lathe.inx.h:2
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:2
+msgid "Additional post-processor:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:7
+msgid "All in one"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:9
+#: ../share/extensions/gcodetools_area.inx.h:6
+msgid "Area artefacts"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:10
+#: ../share/extensions/gcodetools_area.inx.h:7
+msgid "Area width:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:11
+#: ../share/extensions/gcodetools_area.inx.h:8
+msgid "Artefact diameter:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:12
+#: ../share/extensions/gcodetools_area.inx.h:9
+#: ../share/extensions/gcodetools_lathe.inx.h:3
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:3
+msgid ""
+"Biarc interpolation tolerance is the maximum distance between path and its "
+"approximation. The segment will be split into two segments if the distance "
+"between path's segment and it's approximation exceeds biarc interpolation "
+"tolerance."
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:13
+#: ../share/extensions/gcodetools_area.inx.h:10
+#: ../share/extensions/gcodetools_lathe.inx.h:4
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:4
+msgid "Biarc interpolation tolerance:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:14
+#: ../share/extensions/gcodetools_dxf_points.inx.h:3
+msgid ""
+"Convert selected objects to drill points (as dxf_import plugin does). Also "
+"you can save original shape. Only the start point of each curve will be "
+"used. Also you can manually select object, open XML editor (Shift+Ctrl+X) "
+"and add or remove XML tag 'dxfpoint' with any value."
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:15
+#: ../share/extensions/gcodetools_dxf_points.inx.h:4
+msgid "Convert selection:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:16
+#: ../share/extensions/gcodetools_dxf_points.inx.h:6
+msgid "DXF points"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:17
+#: ../share/extensions/gcodetools_area.inx.h:11
+#: ../share/extensions/gcodetools_dxf_points.inx.h:7
+#: ../share/extensions/gcodetools_engraving.inx.h:3
+#: ../share/extensions/gcodetools_lathe.inx.h:6
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:5
+msgid "Directory:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:18
+msgid "Draw additional graphics to debug engraving path:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:19
+#: ../share/extensions/gcodetools_engraving.inx.h:5
+msgid "Engraving"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:20
+#: ../share/extensions/gcodetools_area.inx.h:12
+#: ../share/extensions/gcodetools_dxf_points.inx.h:8
+#: ../share/extensions/gcodetools_engraving.inx.h:6
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:6
+msgid "File:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:21
+#: ../share/extensions/gcodetools_area.inx.h:13
+#: ../share/extensions/gcodetools_dxf_points.inx.h:9
+#: ../share/extensions/gcodetools_engraving.inx.h:7
+#: ../share/extensions/gcodetools_lathe.inx.h:10
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:7
+msgid "Full path to log file:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:22
+#: ../share/extensions/gcodetools_area.inx.h:14
+#: ../share/extensions/gcodetools_check_for_updates.inx.h:3
+#: ../share/extensions/gcodetools_dxf_points.inx.h:10
+#: ../share/extensions/gcodetools_engraving.inx.h:8
+#: ../share/extensions/gcodetools_lathe.inx.h:11
+#: ../share/extensions/gcodetools_orientation_points.inx.h:3
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:8
+#: ../share/extensions/gcodetools_tools_library.inx.h:1
+msgid "Gcodetools"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:23
+#: ../share/extensions/gcodetools_area.inx.h:15
+#: ../share/extensions/gcodetools_check_for_updates.inx.h:4
+#: ../share/extensions/gcodetools_dxf_points.inx.h:11
+#: ../share/extensions/gcodetools_engraving.inx.h:9
+#: ../share/extensions/gcodetools_lathe.inx.h:12
+#: ../share/extensions/gcodetools_orientation_points.inx.h:4
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:9
+#: ../share/extensions/gcodetools_tools_library.inx.h:2
+msgid ""
+"Gcodetools plug-in: converts paths to Gcode (using circular interpolation), "
+"makes offset paths and engraves sharp corners using cone cutters. This plug-"
+"in calculates Gcode for paths using circular interpolation or linear motion "
+"when needed. Tutorials, manuals and support can be found at English support "
+"forum: http://www.cnc-club.ru/gcodetools and Russian support forum: http://"
+"www.cnc-club.ru/gcodetoolsru Credits: Nick Drobchenko, Vladimir Kalyaev, "
+"John Brooker, Henry Nicolas. Gcodetools ver. 1.6.01"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:24
+#: ../share/extensions/gcodetools_area.inx.h:16
+#: ../share/extensions/gcodetools_dxf_points.inx.h:12
+#: ../share/extensions/gcodetools_engraving.inx.h:10
+#: ../share/extensions/gcodetools_lathe.inx.h:13
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:10
+msgid "Generate log file"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:26
+#: ../share/extensions/gcodetools_tools_library.inx.h:4
+msgid "Just check tools"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:27
+#: ../share/extensions/gcodetools_area.inx.h:18
+msgid "Maximum area cutting curves:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:28
+#: ../share/extensions/gcodetools_engraving.inx.h:12
+msgid "Maximum distance for engraving:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:29
+#: ../share/extensions/gcodetools_area.inx.h:19
+#: ../share/extensions/gcodetools_lathe.inx.h:19
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:12
+msgid "Maximum splitting depth:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:30
+#: ../share/extensions/gcodetools_area.inx.h:20
+#: ../share/extensions/gcodetools_engraving.inx.h:13
+#: ../share/extensions/gcodetools_lathe.inx.h:20
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:13
+msgid "Minimum arc radius:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:31
+#: ../share/extensions/gcodetools_engraving.inx.h:14
+msgid "Number of sample points used to calculate distance:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:32
+#: ../share/extensions/gcodetools_area.inx.h:21
+#: ../share/extensions/gcodetools_engraving.inx.h:15
+#: ../share/extensions/gcodetools_lathe.inx.h:21
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:14
+msgid "Offset along Z axis:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:35
+#: ../share/extensions/gcodetools_orientation_points.inx.h:8
+msgid ""
+"Orientation points are used to calculate transformation (offset,scale,mirror,"
+"rotation in XY plane) of the path. 3-points mode only: do not put all three "
+"into one line (use 2-points mode instead). You can modify Z surface, Z depth "
+"values later using text tool (3rd coordinates). If there are no orientation "
+"points inside current layer they are taken from the upper layer. Do not "
+"ungroup orientation points! You can select them using double click to enter "
+"the group or by Ctrl+Click. Now press apply to create control points "
+"(independent set for each layer)."
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:36
+#: ../share/extensions/gcodetools_orientation_points.inx.h:9
+msgid "Orientation type:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:37
+#: ../share/extensions/gcodetools_area.inx.h:23
+#: ../share/extensions/gcodetools_lathe.inx.h:23
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:16
+msgid "Path to Gcode"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:38
+#: ../share/extensions/gcodetools_area.inx.h:24
+#: ../share/extensions/gcodetools_dxf_points.inx.h:14
+#: ../share/extensions/gcodetools_engraving.inx.h:17
+#: ../share/extensions/gcodetools_lathe.inx.h:24
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:17
+msgid "Post-processor:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:39
+#: ../share/extensions/gcodetools_area.inx.h:25
+#: ../share/extensions/gcodetools_dxf_points.inx.h:15
+#: ../share/extensions/gcodetools_engraving.inx.h:18
+#: ../share/extensions/gcodetools_lathe.inx.h:25
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:18
+msgid "Preferences"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:40
+#: ../share/extensions/gcodetools_area.inx.h:26
+#: ../share/extensions/gcodetools_engraving.inx.h:19
+#: ../share/extensions/gcodetools_lathe.inx.h:26
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:19
+msgid "Scale along Z axis:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:41
+#: ../share/extensions/gcodetools_area.inx.h:27
+#: ../share/extensions/gcodetools_engraving.inx.h:20
+#: ../share/extensions/gcodetools_lathe.inx.h:27
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:20
+msgid "Select all paths if nothing is selected"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:42
+#: ../share/extensions/gcodetools_tools_library.inx.h:5
+msgid ""
+"Selected tool type fills appropriate default values. You can change these "
+"values using the Text tool later on. The topmost (z order) tool in the "
+"active layer is used. If there is no tool inside the current layer it is "
+"taken from the upper layer. Press Apply to create new tool."
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:43
+#: ../share/extensions/gcodetools_engraving.inx.h:21
+msgid "Sharp angle tolerance:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:44
+#: ../share/extensions/gcodetools_engraving.inx.h:22
+msgid ""
+"This function creates path to engrave sharp angles. Cutter's shape function "
+"is defined by the tool. Some simple shapes: cone....(45 degrees)...........: "
+"w cone....(height/diameter=10/3).: 10/3 w sphere..(\"r\" diameter).........: "
+"math.sqrt(max(0,r**2-w**2)) ellipse.(R1=r and R2=r*4r).....: math.sqrt(max(0,"
+"r**2-w**2))*4"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:45
+#: ../share/extensions/gcodetools_tools_library.inx.h:6
+msgid "Tools library"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:46
+#: ../share/extensions/gcodetools_tools_library.inx.h:7
+msgid "Tools type:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:47
+#: ../share/extensions/gcodetools_area.inx.h:28
+#: ../share/extensions/gcodetools_dxf_points.inx.h:16
+#: ../share/extensions/gcodetools_engraving.inx.h:23
+#: ../share/extensions/gcodetools_lathe.inx.h:28
+#: ../share/extensions/gcodetools_orientation_points.inx.h:10
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:21
+msgid "Units (mm or in):"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:48
+#: ../share/extensions/gcodetools_area.inx.h:29
+msgid ""
+"Usage: 1. Select all Area Offsets (gray outlines) 2. Object/Ungroup (Shift"
+"+Ctrl+G) 3. Press Apply Suspected small objects will be marked out by "
+"colored arrows."
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:49
+#: ../share/extensions/gcodetools_orientation_points.inx.h:11
+msgid "Z depth:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:50
+#: ../share/extensions/gcodetools_area.inx.h:30
+#: ../share/extensions/gcodetools_dxf_points.inx.h:17
+#: ../share/extensions/gcodetools_engraving.inx.h:24
+#: ../share/extensions/gcodetools_lathe.inx.h:29
+#: ../share/extensions/gcodetools_path_to_gcode.inx.h:22
+msgid "Z safe height for G00 move over blank:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:51
+#: ../share/extensions/gcodetools_orientation_points.inx.h:12
+msgid "Z surface:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:52
+#: ../share/extensions/gcodetools_dxf_points.inx.h:18
+msgid "clear dxfpoint sign"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:53
+#: ../share/extensions/gcodetools_tools_library.inx.h:8
+msgid "cone"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:54
+#: ../share/extensions/gcodetools_tools_library.inx.h:9
+msgid "cylinder"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:55
+#: ../share/extensions/gcodetools_tools_library.inx.h:10
+msgid "default"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:56
+#: ../share/extensions/gcodetools_area.inx.h:31
+msgid "delete"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:57
+#: ../share/extensions/gcodetools_tools_library.inx.h:11
+msgid "lathe cutter"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:58
+#: ../share/extensions/gcodetools_area.inx.h:32
+msgid "mark with an arrow"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:59
+#: ../share/extensions/gcodetools_area.inx.h:33
+msgid "mark with style"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:60
+#: ../share/extensions/gcodetools_tools_library.inx.h:12
+msgid "plasma"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:61
+#: ../share/extensions/gcodetools_dxf_points.inx.h:19
+msgid "set as dxfpoint and draw arrow"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:62
+#: ../share/extensions/gcodetools_dxf_points.inx.h:20
+msgid "set as dxfpoint and save shape"
+msgstr ""
+
+#: ../share/extensions/gcodetools_all_in_one.inx.h:63
+#: ../share/extensions/gcodetools_tools_library.inx.h:13
+msgid "tangent knife"
+msgstr ""
+
+#: ../share/extensions/gcodetools_check_for_updates.inx.h:1
+msgid "Check for Gcodetools latest stable version and try to get the updates."
+msgstr ""
+
+#: ../share/extensions/gcodetools_check_for_updates.inx.h:2
+msgid "Check for updates"
+msgstr ""
+
+#: ../share/extensions/gcodetools_dxf_points.inx.h:5
+msgid "DXF Points"
+msgstr ""
+
+#: ../share/extensions/gcodetools_engraving.inx.h:4
+msgid "Draw additional graphics to debug engraving path"
+msgstr ""
+
+#: ../share/extensions/gcodetools_lathe.inx.h:5
+msgid "Create fine cut using:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_lathe.inx.h:7
+msgid "File"
+msgstr ""
+
+#: ../share/extensions/gcodetools_lathe.inx.h:8
+msgid "Fine cut count:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_lathe.inx.h:9
+msgid "Fine cut width:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_lathe.inx.h:15
+msgid "Lathe"
+msgstr ""
+
+#: ../share/extensions/gcodetools_lathe.inx.h:16
+msgid "Lathe X axis remap:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_lathe.inx.h:17
+msgid "Lathe Z axis remap:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_lathe.inx.h:18
+msgid "Lathe width:"
+msgstr ""
+
+#: ../share/extensions/gcodetools_orientation_points.inx.h:7
+msgid "Orientation points"
+msgstr ""
+
 #: ../share/extensions/render_barcode_datamatrix.inx.h:1
 msgid "Barcode - Datamatrix"
 msgstr ""
index f621837f7226c0e53369a4aca4661386b8765aa7..d77ac1fec79e651f416bcefd7b6e82fcc6c96104 100644 (file)
@@ -62,6 +62,7 @@ extensions = \
        fractalize.py \
        funcplot.py \
        gears.py\
+    gcodetools.py\
        generate_voronoi.py \
        gimp_xcf.py \
        grid_cartesian.py \
@@ -218,6 +219,15 @@ modules = \
        fractalize.inx \
        funcplot.inx \
        gears.inx\
+    gcodetools_all_in_one.inx\
+    gcodetools_area.inx\
+    gcodetools_check_for_updates.inx\
+    gcodetools_dxf_points.inx\
+    gcodetools_engraving.inx\
+    gcodetools_lathe.inx\
+    gcodetools_orientation_points.inx\
+    gcodetools_path_to_gcode.inx\
+    gcodetools_tools_library.inx\
        generate_voronoi.inx \
        gimp_xcf.inx \
        grid_cartesian.inx \
diff --git a/share/extensions/gcodetools.py b/share/extensions/gcodetools.py
new file mode 100644 (file)
index 0000000..abf6898
--- /dev/null
@@ -0,0 +1,4602 @@
+#!/usr/bin/env python 
+"""
+Copyright (C) 2009 Nick Drobchenko, nick@cnc-club.ru
+based on gcode.py (C) 2007 hugomatic... 
+based on addnodes.py (C) 2005,2007 Aaron Spike, aaron@ekips.org
+based on dots.py (C) 2005 Aaron Spike, aaron@ekips.org
+based on interp.py (C) 2005 Aaron Spike, aaron@ekips.org
+based on bezmisc.py (C) 2005 Aaron Spike, aaron@ekips.org
+based on cubicsuperpath.py (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+"""
+
+###
+###            Gcodetools v 1.6.03 
+###
+gcodetools_current_version = "1.6.03"
+
+import inkex, simplestyle, simplepath
+import cubicsuperpath, simpletransform, bezmisc
+
+import os
+import math
+import bezmisc
+import re
+import copy
+import sys
+import time
+import cmath
+import numpy
+import codecs
+import random
+import gettext
+_ = gettext.gettext
+
+### Check if inkex has errormsg (0.46 version doesnot have one.) Could be removed later.
+if "errormsg" not in dir(inkex):
+       inkex.errormsg = lambda msg: sys.stderr.write((unicode(msg) + "\n").encode("UTF-8"))
+
+
+def bezierslopeatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t):
+       ax,ay,bx,by,cx,cy,x0,y0=bezmisc.bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
+       dx=3*ax*(t**2)+2*bx*t+cx
+       dy=3*ay*(t**2)+2*by*t+cy
+       if dx==dy==0 : 
+               dx = 6*ax*t+2*bx
+               dy = 6*ay*t+2*by
+               if dx==dy==0 : 
+                       dx = 6*ax
+                       dy = 6*ay
+                       if dx==dy==0 : 
+                               print_("Slope error x = %s*t^3+%s*t^2+%s*t+%s, y = %s*t^3+%s*t^2+%s*t+%s,  t = %s, dx==dy==0" % (ax,bx,cx,dx,ay,by,cy,dy,t))
+                               print_(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
+                               dx, dy = 1, 1
+                               
+       return dx,dy
+bezmisc.bezierslopeatt = bezierslopeatt
+
+
+def ireplace(self,old,new,count=0):
+       pattern = re.compile(re.escape(old),re.I)
+       return re.sub(pattern,new,self,count) 
+
+################################################################################
+###
+###            Styles and additional parameters
+###
+################################################################################
+
+math.pi2 = math.pi*2
+straight_tolerance = 0.0001
+straight_distance_tolerance = 0.0001
+engraving_tolerance = 0.0001
+loft_lengths_tolerance = 0.0000001
+options = {}
+defaults = {
+'header': """%
+(Header)
+(Generated by gcodetools from Inkscape.)
+(Using default header. To add your own header create file "header" in the output dir.)
+M3
+(Header end.)
+""",
+'footer': """
+(Footer)
+M5
+G00 X0.0000 Y0.0000
+M2
+(Using default footer. To add your own footer create file "footer" in the output dir.)
+(end)
+%"""
+}
+
+intersection_recursion_depth = 10
+intersection_tolerance = 0.00001
+
+styles = {
+               "loft_style" : {
+                               'main curve':   simplestyle.formatStyle({ 'stroke': '#88f', 'fill': 'none', 'stroke-width':'1', 'marker-end':'url(#Arrow2Mend)' }),
+                       },
+               "biarc_style" : {
+                               'biarc0':       simplestyle.formatStyle({ 'stroke': '#88f', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'biarc1':       simplestyle.formatStyle({ 'stroke': '#8f8', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'line':         simplestyle.formatStyle({ 'stroke': '#f88', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'area':         simplestyle.formatStyle({ 'stroke': '#777', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.1' }),
+                       },
+               "biarc_style_dark" : {
+                               'biarc0':       simplestyle.formatStyle({ 'stroke': '#33a', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'biarc1':       simplestyle.formatStyle({ 'stroke': '#3a3', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'line':         simplestyle.formatStyle({ 'stroke': '#a33', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'area':         simplestyle.formatStyle({ 'stroke': '#222', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
+                       },
+               "biarc_style_dark_area" : {
+                               'biarc0':       simplestyle.formatStyle({ 'stroke': '#33a', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.1' }),
+                               'biarc1':       simplestyle.formatStyle({ 'stroke': '#3a3', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.1' }),
+                               'line':         simplestyle.formatStyle({ 'stroke': '#a33', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.1' }),
+                               'area':         simplestyle.formatStyle({ 'stroke': '#222', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
+                       },
+               "biarc_style_i"  : {
+                               'biarc0':       simplestyle.formatStyle({ 'stroke': '#880', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'biarc1':       simplestyle.formatStyle({ 'stroke': '#808', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'line':         simplestyle.formatStyle({ 'stroke': '#088', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'area':         simplestyle.formatStyle({ 'stroke': '#999', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
+                       },
+               "biarc_style_dark_i" : {
+                               'biarc0':       simplestyle.formatStyle({ 'stroke': '#dd5', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'biarc1':       simplestyle.formatStyle({ 'stroke': '#d5d', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'line':         simplestyle.formatStyle({ 'stroke': '#5dd', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'1' }),
+                               'area':         simplestyle.formatStyle({ 'stroke': '#aaa', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
+                       },
+               "biarc_style_lathe_feed" : {
+                               'biarc0':       simplestyle.formatStyle({ 'stroke': '#07f', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
+                               'biarc1':       simplestyle.formatStyle({ 'stroke': '#0f7', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
+                               'line':         simplestyle.formatStyle({ 'stroke': '#f44', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
+                               'area':         simplestyle.formatStyle({ 'stroke': '#aaa', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
+                       },
+               "biarc_style_lathe_passing feed" : {
+                               'biarc0':       simplestyle.formatStyle({ 'stroke': '#07f', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
+                               'biarc1':       simplestyle.formatStyle({ 'stroke': '#0f7', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
+                               'line':         simplestyle.formatStyle({ 'stroke': '#f44', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
+                               'area':         simplestyle.formatStyle({ 'stroke': '#aaa', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
+                       },
+               "biarc_style_lathe_fine feed" : {
+                               'biarc0':       simplestyle.formatStyle({ 'stroke': '#7f0', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
+                               'biarc1':       simplestyle.formatStyle({ 'stroke': '#f70', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
+                               'line':         simplestyle.formatStyle({ 'stroke': '#744', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'.4' }),
+                               'area':         simplestyle.formatStyle({ 'stroke': '#aaa', 'fill': 'none', "marker-end":"url(#DrawCurveMarker)", 'stroke-width':'0.3' }),
+                       },
+               "area artefact":                simplestyle.formatStyle({ 'stroke': '#ff0000', 'fill': '#ffff00', 'stroke-width':'1' }),
+               "area artefact arrow":  simplestyle.formatStyle({ 'stroke': '#ff0000', 'fill': '#ffff00', 'stroke-width':'1' }),
+               "dxf_points":                   simplestyle.formatStyle({ "stroke": "#ff0000", "fill": "#ff0000"}),
+               
+       }
+
+
+################################################################################
+###            Cubic Super Path additional functions
+################################################################################
+
+def csp_simple_bound(csp):
+       minx,miny,maxx,maxy = None,None,None,None
+       for subpath in csp:
+               for sp in subpath : 
+                       for p in sp:
+                               minx = min(minx,p[0]) if minx!=None else p[0]
+                               miny = min(miny,p[1]) if miny!=None else p[1]
+                               maxx = max(maxx,p[0]) if maxx!=None else p[0]
+                               maxy = max(maxy,p[1]) if maxy!=None else p[1]
+       return minx,miny,maxx,maxy              
+
+
+def csp_segment_to_bez(sp1,sp2) :
+       return sp1[1:]+sp2[:2]
+
+
+def bound_to_bound_distance(sp1,sp2,sp3,sp4) :
+       min_dist = 1e100
+       max_dist = 0
+       points1 = csp_segment_to_bez(sp1,sp2)
+       points2 = csp_segment_to_bez(sp3,sp4)
+       for i in range(4) :
+               for j in range(4) :
+                       min_, max_ = line_to_line_min_max_distance_2(points1[i-1], points1[i], points2[j-1], points2[j])
+                       min_dist = min(min_dist,min_)
+                       max_dist = max(max_dist,max_)
+                       print_("bound_to_bound", min_dist, max_dist)
+       return min_dist, max_dist
+       
+def csp_to_point_distance(csp, p, dist_bounds = [0,1e100], tolerance=.01) :
+       min_dist = [1e100,0,0,0]
+       for j in range(len(csp)) :
+               for i in range(1,len(csp[j])) :
+                       d = csp_seg_to_point_distance(csp[j][i-1],csp[j][i],p,sample_points = 5, tolerance = .01)
+                       if d[0] < dist_bounds[0] : 
+#                              draw_pointer( list(csp_at_t(subpath[dist[2]-1],subpath[dist[2]],dist[3]))
+#                                      +list(csp_at_t(csp[dist[4]][dist[5]-1],csp[dist[4]][dist[5]],dist[6])),"red","line", comment = math.sqrt(dist[0]))
+                               return [d[0],j,i,d[1]]
+                       else : 
+                               if d[0] < min_dist[0] : min_dist = [d[0],j,i,d[1]]
+       return min_dist
+                       
+def csp_seg_to_point_distance(sp1,sp2,p,sample_points = 5, tolerance = .01) :
+       ax,ay,bx,by,cx,cy,dx,dy = csp_parameterize(sp1,sp2)
+       dx, dy = dx-p[0], dy-p[1]
+       if sample_points < 2 : sample_points = 2
+       d = min( [(p[0]-sp1[1][0])**2 + (p[1]-sp1[1][1])**2,0.], [(p[0]-sp2[1][0])**2 + (p[1]-sp2[1][1])**2,1.] )       
+       for k in range(sample_points) :
+               t = float(k)/(sample_points-1)
+               i = 0
+               while i==0 or abs(f)>0.000001 and i<20 : 
+                       t2,t3 = t**2,t**3
+                       f = (ax*t3+bx*t2+cx*t+dx)*(3*ax*t2+2*bx*t+cx) + (ay*t3+by*t2+cy*t+dy)*(3*ay*t2+2*by*t+cy)
+                       df = (6*ax*t+2*bx)*(ax*t3+bx*t2+cx*t+dx) + (3*ax*t2+2*bx*t+cx)**2 + (6*ay*t+2*by)*(ay*t3+by*t2+cy*t+dy) + (3*ay*t2+2*by*t+cy)**2
+                       if df!=0 :
+                               t = t - f/df
+                       else :  
+                               break
+                       i += 1  
+               if 0<=t<=1 : 
+                       p1 = csp_at_t(sp1,sp2,t)
+                       d1 = (p1[0]-p[0])**2 + (p1[1]-p[1])**2
+                       if d1 < d[0] :
+                               d = [d1,t]
+       return d        
+
+
+def csp_seg_to_csp_seg_distance(sp1,sp2,sp3,sp4, dist_bounds = [0,1e100], sample_points = 5, tolerance=.01) : 
+       # check the ending points first
+       dist =  csp_seg_to_point_distance(sp1,sp2,sp3[1],sample_points, tolerance)
+       dist += [0.]
+       if dist[0] <= dist_bounds[0] : return dist
+       d = csp_seg_to_point_distance(sp1,sp2,sp4[1],sample_points, tolerance)
+       if d[0]<dist[0] :
+               dist = d+[1.]
+               if dist[0] <= dist_bounds[0] : return dist
+       d =     csp_seg_to_point_distance(sp3,sp4,sp1[1],sample_points, tolerance)
+       if d[0]<dist[0] :
+               dist = [d[0],0.,d[1]]
+               if dist[0] <= dist_bounds[0] : return dist
+       d =     csp_seg_to_point_distance(sp3,sp4,sp2[1],sample_points, tolerance)
+       if d[0]<dist[0] :
+               dist = [d[0],1.,d[1]]
+               if dist[0] <= dist_bounds[0] : return dist
+       sample_points -= 2
+       if sample_points < 1 : sample_points = 1
+       ax1,ay1,bx1,by1,cx1,cy1,dx1,dy1 = csp_parameterize(sp1,sp2)
+       ax2,ay2,bx2,by2,cx2,cy2,dx2,dy2 = csp_parameterize(sp3,sp4)
+       #       try to find closes points using Newtons method
+       for k in range(sample_points) :
+               for j in range(sample_points) : 
+                       t1,t2 = float(k+1)/(sample_points+1), float(j)/(sample_points+1)
+                       t12, t13, t22, t23 = t1*t1, t1*t1*t1, t2*t2, t2*t2*t2
+                       i = 0
+                       F1, F2, F = [0,0], [[0,0],[0,0]], 1e100
+                       x,y   = ax1*t13+bx1*t12+cx1*t1+dx1 - (ax2*t23+bx2*t22+cx2*t2+dx2), ay1*t13+by1*t12+cy1*t1+dy1 - (ay2*t23+by2*t22+cy2*t2+dy2)                    
+                       while i<2 or abs(F-Flast)>tolerance and i<30 :
+                               #draw_pointer(csp_at_t(sp1,sp2,t1))
+                               f1x = 3*ax1*t12+2*bx1*t1+cx1
+                               f1y = 3*ay1*t12+2*by1*t1+cy1
+                               f2x = 3*ax2*t22+2*bx2*t2+cx2
+                               f2y = 3*ay2*t22+2*by2*t2+cy2
+                               F1[0] = 2*f1x*x +  2*f1y*y
+                               F1[1] = -2*f2x*x -  2*f2y*y
+                               F2[0][0] =  2*(6*ax1*t1+2*bx1)*x + 2*f1x*f1x + 2*(6*ay1*t1+2*by1)*y +2*f1y*f1y
+                               F2[0][1] = -2*f1x*f2x - 2*f1y*f2y
+                               F2[1][0] = -2*f2x*f1x - 2*f2y*f1y 
+                               F2[1][1] = -2*(6*ax2*t2+2*bx2)*x + 2*f2x*f2x - 2*(6*ay2*t2+2*by2)*y + 2*f2y*f2y
+                               F2 = inv_2x2(F2)
+                               if F2!=None :
+                                       t1 -= ( F2[0][0]*F1[0] + F2[0][1]*F1[1] )
+                                       t2 -= ( F2[1][0]*F1[0] + F2[1][1]*F1[1] )
+                                       t12, t13, t22, t23 = t1*t1, t1*t1*t1, t2*t2, t2*t2*t2
+                                       x,y   = ax1*t13+bx1*t12+cx1*t1+dx1 - (ax2*t23+bx2*t22+cx2*t2+dx2), ay1*t13+by1*t12+cy1*t1+dy1 - (ay2*t23+by2*t22+cy2*t2+dy2)
+                                       Flast = F
+                                       F = x*x+y*y
+                               else : 
+                                       break
+                               i += 1
+                       if F < dist[0] and 0<=t1<=1 and 0<=t2<=1:
+                               dist = [F,t1,t2]
+                               if dist[0] <= dist_bounds[0] : 
+                                       return dist
+       return dist                     
+
+
+def csp_to_csp_distance(csp1,csp2, dist_bounds = [0,1e100], tolerance=.01) : 
+       dist = [1e100,0,0,0,0,0,0]
+       for i1 in range(len(csp1)) : 
+               for j1 in range(1,len(csp1[i1])) :
+                       for i2 in range(len(csp2)) :
+                               for j2 in range(1,len(csp2[i2])) :
+                                       d = csp_seg_bound_to_csp_seg_bound_max_min_distance(csp1[i1][j1-1],csp1[i1][j1],csp2[i2][j2-1],csp2[i2][j2])
+                                       if d[0] >= dist_bounds[1] : continue
+                                       if  d[1] < dist_bounds[0] : return [d[1],i1,j1,1,i2,j2,1]
+                                       d = csp_seg_to_csp_seg_distance(csp1[i1][j1-1],csp1[i1][j1],csp2[i2][j2-1],csp2[i2][j2], dist_bounds, tolerance=tolerance)
+                                       if d[0] < dist[0] :
+                                               dist = [d[0], i1,j1,d[1], i2,j2,d[2]]
+                                       if dist[0] <= dist_bounds[0] :  
+                                               return dist
+                       if dist[0] >= dist_bounds[1] :  
+                               return dist
+       return dist
+#      draw_pointer( list(csp_at_t(csp1[dist[1]][dist[2]-1],csp1[dist[1]][dist[2]],dist[3]))
+#                              + list(csp_at_t(csp2[dist[4]][dist[5]-1],csp2[dist[4]][dist[5]],dist[6])), "#507","line")
+                                       
+                                       
+def csp_split(sp1,sp2,t=.5) :
+       [x1,y1],[x2,y2],[x3,y3],[x4,y4] = sp1[1], sp1[2], sp2[0], sp2[1] 
+       x12 = x1+(x2-x1)*t
+       y12 = y1+(y2-y1)*t
+       x23 = x2+(x3-x2)*t
+       y23 = y2+(y3-y2)*t
+       x34 = x3+(x4-x3)*t
+       y34 = y3+(y4-y3)*t
+       x1223 = x12+(x23-x12)*t
+       y1223 = y12+(y23-y12)*t
+       x2334 = x23+(x34-x23)*t
+       y2334 = y23+(y34-y23)*t
+       x = x1223+(x2334-x1223)*t
+       y = y1223+(y2334-y1223)*t
+       return [sp1[0],sp1[1],[x12,y12]], [[x1223,y1223],[x,y],[x2334,y2334]], [[x34,y34],sp2[1],sp2[2]]
+       
+def csp_true_bounds(csp) :
+       # Finds minx,miny,maxx,maxy of the csp and return their (x,y,i,j,t) 
+       minx = [float("inf"), 0, 0, 0]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
+       maxx = [float("-inf"), 0, 0, 0]
+       miny = [float("inf"), 0, 0, 0]
+       maxy = [float("-inf"), 0, 0, 0]
+       for i in range(len(csp)):
+               for j in range(1,len(csp[i])):
+                       ax,ay,bx,by,cx,cy,x0,y0 = bezmisc.bezierparameterize((csp[i][j-1][1],csp[i][j-1][2],csp[i][j][0],csp[i][j][1]))
+                       roots = cubic_solver(0, 3*ax, 2*bx, cx)  + [0,1]
+                       for root in roots :
+                               if type(root) is complex and abs(root.imag)<1e-10:
+                                       root = root.real
+                               if type(root) is not complex and 0<=root<=1:
+                                       y = ay*(root**3)+by*(root**2)+cy*root+y0  
+                                       x = ax*(root**3)+bx*(root**2)+cx*root+x0  
+                                       maxx = max([x,y,i,j,root],maxx)
+                                       minx = min([x,y,i,j,root],minx)
+
+                       roots = cubic_solver(0, 3*ay, 2*by, cy)  + [0,1]
+                       for root in roots :
+                               if type(root) is complex and root.imag==0:
+                                       root = root.real
+                               if type(root) is not complex and 0<=root<=1:
+                                       y = ay*(root**3)+by*(root**2)+cy*root+y0  
+                                       x = ax*(root**3)+bx*(root**2)+cx*root+x0  
+                                       maxy = max([y,x,i,j,root],maxy)
+                                       miny = min([y,x,i,j,root],miny)
+       maxy[0],maxy[1] = maxy[1],maxy[0]
+       miny[0],miny[1] = miny[1],miny[0]
+
+       return minx,miny,maxx,maxy
+
+
+############################################################################
+### csp_segments_intersection(sp1,sp2,sp3,sp4)
+###
+### Returns array containig all intersections between two segmets of cubic 
+### super path. Results are [ta,tb], or [ta0, ta1, tb0, tb1, "Overlap"] 
+### where ta, tb are values of t for the intersection point.
+############################################################################
+def csp_segments_intersection(sp1,sp2,sp3,sp4) :
+       a, b = csp_segment_to_bez(sp1,sp2), csp_segment_to_bez(sp3,sp4)
+
+       def polish_intersection(a,b,ta,tb, tolerance = intersection_tolerance) :
+               ax,ay,bx,by,cx,cy,dx,dy                 = bezmisc.bezierparameterize(a)
+               ax1,ay1,bx1,by1,cx1,cy1,dx1,dy1 = bezmisc.bezierparameterize(b)
+               i = 0
+               F, F1 =  [.0,.0], [[.0,.0],[.0,.0]]
+               while i==0 or (abs(F[0])**2+abs(F[1])**2 > tolerance and i<10):
+                       ta3, ta2, tb3, tb2 = ta**3, ta**2, tb**3, tb**2
+                       F[0] = ax*ta3+bx*ta2+cx*ta+dx-ax1*tb3-bx1*tb2-cx1*tb-dx1
+                       F[1] = ay*ta3+by*ta2+cy*ta+dy-ay1*tb3-by1*tb2-cy1*tb-dy1
+                       F1[0][0] =  3*ax *ta2 + 2*bx *ta + cx
+                       F1[0][1] = -3*ax1*tb2 - 2*bx1*tb - cx1
+                       F1[1][0] =  3*ay *ta2 + 2*by *ta + cy
+                       F1[1][1] = -3*ay1*tb2 - 2*by1*tb - cy1  
+                       det = F1[0][0]*F1[1][1] - F1[0][1]*F1[1][0]
+                       if det!=0 :
+                               F1 = [  [ F1[1][1]/det, -F1[0][1]/det], [-F1[1][0]/det,  F1[0][0]/det] ]
+                               ta = ta - ( F1[0][0]*F[0] + F1[0][1]*F[1] )
+                               tb = tb - ( F1[1][0]*F[0] + F1[1][1]*F[1] )
+                       else: break     
+                       i += 1
+
+               return ta, tb                   
+
+
+       def recursion(a,b, ta0,ta1,tb0,tb1, depth_a,depth_b) :
+               global bezier_intersection_recursive_result
+               if a==b : 
+                       bezier_intersection_recursive_result += [[ta0,tb0,ta1,tb1,"Overlap"]]
+                       return 
+               tam, tbm = (ta0+ta1)/2, (tb0+tb1)/2
+               if depth_a>0 and depth_b>0 : 
+                       a1,a2 = bez_split(a,0.5)
+                       b1,b2 = bez_split(b,0.5)
+                       if bez_bounds_intersect(a1,b1) : recursion(a1,b1, ta0,tam,tb0,tbm, depth_a-1,depth_b-1) 
+                       if bez_bounds_intersect(a2,b1) : recursion(a2,b1, tam,ta1,tb0,tbm, depth_a-1,depth_b-1) 
+                       if bez_bounds_intersect(a1,b2) : recursion(a1,b2, ta0,tam,tbm,tb1, depth_a-1,depth_b-1) 
+                       if bez_bounds_intersect(a2,b2) : recursion(a2,b2, tam,ta1,tbm,tb1, depth_a-1,depth_b-1) 
+               elif depth_a>0  : 
+                       a1,a2 = bez_split(a,0.5)
+                       if bez_bounds_intersect(a1,b) : recursion(a1,b, ta0,tam,tb0,tb1, depth_a-1,depth_b) 
+                       if bez_bounds_intersect(a2,b) : recursion(a2,b, tam,ta1,tb0,tb1, depth_a-1,depth_b) 
+               elif depth_b>0  : 
+                       b1,b2 = bez_split(b,0.5)
+                       if bez_bounds_intersect(a,b1) : recursion(a,b1, ta0,ta1,tb0,tbm, depth_a,depth_b-1) 
+                       if bez_bounds_intersect(a,b2) : recursion(a,b2, ta0,ta1,tbm,tb1, depth_a,depth_b-1) 
+               else : # Both segments have been subdevided enougth. Let's get some intersections :).
+                       intersection, t1, t2 =  straight_segments_intersection([a[0]]+[a[3]],[b[0]]+[b[3]])
+                       if intersection :
+                               if intersection == "Overlap" :
+                                       t1 = ( max(0,min(1,t1[0]))+max(0,min(1,t1[1])) )/2
+                                       t2 = ( max(0,min(1,t2[0]))+max(0,min(1,t2[1])) )/2
+                               bezier_intersection_recursive_result += [[ta0+t1*(ta1-ta0),tb0+t2*(tb1-tb0)]]
+
+       global bezier_intersection_recursive_result
+       bezier_intersection_recursive_result = []
+       recursion(a,b,0.,1.,0.,1.,intersection_recursion_depth,intersection_recursion_depth)
+       intersections = bezier_intersection_recursive_result
+       for i in range(len(intersections)) :                    
+               if len(intersections[i])<5 or intersections[i][4] != "Overlap" :
+                       intersections[i] = polish_intersection(a,b,intersections[i][0],intersections[i][1])
+       return intersections
+
+
+def csp_segments_true_intersection(sp1,sp2,sp3,sp4) :
+       intersections = csp_segments_intersection(sp1,sp2,sp3,sp4)
+       res = []
+       for intersection in intersections :
+               if  (
+                               (len(intersection)==5 and intersection[4] == "Overlap" and (0<=intersection[0]<=1 or 0<=intersection[1]<=1) and (0<=intersection[2]<=1 or 0<=intersection[3]<=1) ) 
+                        or ( 0<=intersection[0]<=1 and 0<=intersection[1]<=1 )
+                       ) :
+                       res += [intersection]
+       return res
+
+
+def csp_get_t_at_curvature(sp1,sp2,c, sample_points = 16):
+       # returns a list containning [t1,t2,t3,...,tn],  0<=ti<=1...
+       if sample_points < 2 : sample_points = 2
+       tolerance = .0000000001
+       res = []
+       ax,ay,bx,by,cx,cy,dx,dy = csp_parameterize(sp1,sp2)
+       for k in range(sample_points) :
+               t = float(k)/(sample_points-1)
+               i, F = 0, 1e100
+               while i<2 or abs(F)>tolerance and i<17 :
+                       try : # some numerical calculation could exceed the limits 
+                               t2 = t*t
+                               #slopes...
+                               f1x = 3*ax*t2+2*bx*t+cx
+                               f1y = 3*ay*t2+2*by*t+cy
+                               f2x = 6*ax*t+2*bx
+                               f2y = 6*ay*t+2*by
+                               f3x = 6*ax
+                               f3y = 6*ay
+                               d = (f1x**2+f1y**2)**1.5
+                               F1 = (
+                                                (      (f1x*f3y-f3x*f1y)*d - (f1x*f2y-f2x*f1y)*3.*(f2x*f1x+f2y*f1y)*((f1x**2+f1y**2)**.5) )    / 
+                                                               ((f1x**2+f1y**2)**3)
+                                        )
+                               F = (f1x*f2y-f1y*f2x)/d - c
+                               t -= F/F1
+                       except:
+                               break
+                       i += 1
+               if 0<=t<=1 and F<=tolerance:
+                       if len(res) == 0 :
+                               res.append(t)
+                       for i in res : 
+                               if abs(t-i)<=0.001 :
+                                       break
+                       if not abs(t-i)<=0.001 :
+                               res.append(t)
+       return res      
+               
+       
+def csp_max_curvature(sp1,sp2):
+       ax,ay,bx,by,cx,cy,dx,dy = csp_parameterize(sp1,sp2)
+       tolerance = .0001
+       F = 0.
+       i = 0
+       while i<2 or F-Flast<tolerance and i<10 :
+               t = .5
+               f1x = 3*ax*t**2 + 2*bx*t + cx
+               f1y = 3*ay*t**2 + 2*by*t + cy
+               f2x = 6*ax*t + 2*bx
+               f2y = 6*ay*t + 2*by
+               f3x = 6*ax
+               f3y = 6*ay
+               d = pow(f1x**2+f1y**2,1.5)
+               if d != 0 :
+                       Flast = F
+                       F = (f1x*f2y-f1y*f2x)/d
+                       F1 =    (
+                                                (      d*(f1x*f3y-f3x*f1y) - (f1x*f2y-f2x*f1y)*3.*(f2x*f1x+f2y*f1y)*pow(f1x**2+f1y**2,.5) )    / 
+                                                               (f1x**2+f1y**2)**3
+                                       )
+                       i+=1    
+                       if F1!=0:
+                               t -= F/F1
+                       else:
+                               break
+               else: break
+       return t                        
+       
+
+def csp_curvature_at_t(sp1,sp2,t, depth = 3) :
+       ax,ay,bx,by,cx,cy,dx,dy = bezmisc.bezierparameterize(csp_segment_to_bez(sp1,sp2))
+       
+       #curvature = (x'y''-y'x'') / (x'^2+y'^2)^1.5 
+       
+       f1x = 3*ax*t**2 + 2*bx*t + cx
+       f1y = 3*ay*t**2 + 2*by*t + cy
+       f2x = 6*ax*t + 2*bx
+       f2y = 6*ay*t + 2*by
+       d = (f1x**2+f1y**2)**1.5
+       if d != 0 :
+               return (f1x*f2y-f1y*f2x)/d
+       else :
+               t1 = f1x*f2y-f1y*f2x
+               if t1 > 0 : return 1e100
+               if t1 < 0 : return -1e100
+               # Use the Lapitals rule to solve 0/0 problem for 2 times...
+               t1 = 2*(bx*ay-ax*by)*t+(ay*cx-ax*cy)
+               if t1 > 0 : return 1e100
+               if t1 < 0 : return -1e100
+               t1 = bx*ay-ax*by
+               if t1 > 0 : return 1e100
+               if t1 < 0 : return -1e100
+               if depth>0 :
+                       # little hack ;^) hope it wont influence anything...
+                       return csp_curvature_at_t(sp1,sp2,t*1.004, depth-1)
+               return 1e100
+
+               
+def csp_curvature_radius_at_t(sp1,sp2,t) :
+       c = csp_curvature_at_t(sp1,sp2,t)
+       if c == 0 : return 1e100 
+       else: return 1/c
+
+
+def csp_special_points(sp1,sp2) :
+       # special points = curvature == 0
+       ax,ay,bx,by,cx,cy,dx,dy = bezmisc.bezierparameterize((sp1[1],sp1[2],sp2[0],sp2[1]))     
+       a = 3*ax*by-3*ay*bx
+       b = 3*ax*cy-3*cx*ay
+       c = bx*cy-cx*by
+       roots = cubic_solver(0, a, b, c)        
+       res = []
+       for i in roots :
+               if type(i) is complex and i.imag==0:
+                       i = i.real
+               if type(i) is not complex and 0<=i<=1:
+                       res.append(i)
+       return res
+
+       
+def csp_subpath_ccw(subpath):
+       # Remove all zerro length segments
+       s = 0
+       #subpath = subpath[:]
+       if (P(subpath[-1][1])-P(subpath[0][1])).l2() > 1e-10 :
+               subpath[-1][2] = subpath[-1][1]
+               subpath[0][0] = subpath[0][1]
+               subpath += [ [subpath[0][1],subpath[0][1],subpath[0][1]]  ]
+       pl = subpath[-1][2]
+       for sp1 in subpath:
+               for p in sp1 :
+                       s += (p[0]-pl[0])*(p[1]+pl[1])
+                       pl = p
+       return s<0
+
+
+def csp_at_t(sp1,sp2,t):
+       ax,bx,cx,dx = sp1[1][0], sp1[2][0], sp2[0][0], sp2[1][0]
+       ay,by,cy,dy = sp1[1][1], sp1[2][1], sp2[0][1], sp2[1][1]
+
+       x1, y1 = ax+(bx-ax)*t, ay+(by-ay)*t     
+       x2, y2 = bx+(cx-bx)*t, by+(cy-by)*t     
+       x3, y3 = cx+(dx-cx)*t, cy+(dy-cy)*t     
+       
+       x4,y4 = x1+(x2-x1)*t, y1+(y2-y1)*t 
+       x5,y5 = x2+(x3-x2)*t, y2+(y3-y2)*t 
+       
+       x,y = x4+(x5-x4)*t, y4+(y5-y4)*t 
+       return [x,y]
+
+
+def csp_splitatlength(sp1, sp2, l = 0.5, tolerance = 0.01):
+       bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
+       t = bezmisc.beziertatlength(bez, l, tolerance)
+       return csp_split(sp1, sp2, t)   
+
+       
+def cspseglength(sp1,sp2, tolerance = 0.001):
+       bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
+       return bezmisc.bezierlength(bez, tolerance)     
+
+
+def csplength(csp):
+       total = 0
+       lengths = []
+       for sp in csp:
+               for i in xrange(1,len(sp)):
+                       l = cspseglength(sp[i-1],sp[i])
+                       lengths.append(l)
+                       total += l                      
+       return lengths, total
+
+
+def csp_segments(csp):
+       l, seg = 0, [0]
+       for sp in csp:
+               for i in xrange(1,len(sp)):
+                       l += cspseglength(sp[i-1],sp[i])
+                       seg += [ l ] 
+
+       if l>0 :
+               seg = [seg[i]/l for i in xrange(len(seg))]
+       return seg,l
+
+
+def rebuild_csp (csp, segs, s=None):
+       # rebuild_csp() adds to csp control points making it's segments looks like segs
+       if s==None : s, l = csp_segments(csp)
+       
+       if len(s)>len(segs) : return None
+       segs = segs[:]
+       segs.sort()
+       for i in xrange(len(s)):
+               d = None
+               for j in xrange(len(segs)):
+                       d = min( [abs(s[i]-segs[j]),j], d) if d!=None else [abs(s[i]-segs[j]),j]
+               del segs[d[1]]
+       for i in xrange(len(segs)):
+               for j in xrange(0,len(s)):
+                       if segs[i]<s[j] : break
+               if s[j]-s[j-1] != 0 :
+                       t = (segs[i] - s[j-1])/(s[j]-s[j-1])
+                       sp1,sp2,sp3 = csp_split(csp[j-1],csp[j], t)
+                       csp = csp[:j-1] + [sp1,sp2,sp3] + csp[j+1:]
+                       s = s[:j] + [ s[j-1]*(1-t)+s[j]*t   ] + s[j:]
+       return csp, s
+
+
+def csp_slope(sp1,sp2,t):
+       bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
+       return bezmisc.bezierslopeatt(bez,t)
+
+
+def csp_line_intersection(l1,l2,sp1,sp2):
+       dd=l1[0]
+       cc=l2[0]-l1[0]
+       bb=l1[1]
+       aa=l2[1]-l1[1]
+       if aa==cc==0 : return []
+       if aa:
+               coef1=cc/aa
+               coef2=1
+       else:
+               coef1=1
+               coef2=aa/cc
+       bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
+       ax,ay,bx,by,cx,cy,x0,y0=bezmisc.bezierparameterize(bez)
+       a=coef1*ay-coef2*ax
+       b=coef1*by-coef2*bx
+       c=coef1*cy-coef2*cx
+       d=coef1*(y0-bb)-coef2*(x0-dd)
+       roots = cubic_solver(a,b,c,d)
+       retval = []
+       for i in roots :
+               if type(i) is complex and abs(i.imag)<1e-7:
+                       i = i.real
+               if type(i) is not complex and -1e-10<=i<=1.+1e-10:
+                       retval.append(i)
+       return retval
+
+
+def csp_split_by_two_points(sp1,sp2,t1,t2) :
+       if t1>t2 : t1, t2 = t2, t1
+       if t1 == t2 : 
+               sp1,sp2,sp3 =  csp_split(sp1,sp2,t)
+               return [sp1,sp2,sp2,sp3]
+       elif t1 <= 1e-10 and t2 >= 1.-1e-10 :
+               return [sp1,sp1,sp2,sp2]
+       elif t1 <= 1e-10:       
+               sp1,sp2,sp3 = csp_split(sp1,sp2,t2)
+               return [sp1,sp1,sp2,sp3]
+       elif t2 >= 1.-1e-10 : 
+               sp1,sp2,sp3 = csp_split(sp1,sp2,t1)
+               return [sp1,sp2,sp3,sp3]
+       else:
+               sp1,sp2,sp3 = csp_split(sp1,sp2,t1)
+               sp2,sp3,sp4 = csp_split(sp2,sp3,(t2-t1)/(1-t1) )
+               return [sp1,sp2,sp3,sp4]
+
+
+def csp_subpath_split_by_points(subpath, points) :
+       # points are [[i,t]...] where i-segment's number
+       points.sort()
+       points = [[1,0.]] + points + [[len(subpath)-1,1.]]
+       parts = []
+       for int1,int2 in zip(points,points[1:]) : 
+               if int1==int2 :
+                       continue
+               if int1[1] == 1. :
+                       int1[0] += 1
+                       int1[1] = 0.
+               if int1==int2 :
+                       continue
+               if int2[1] == 0. :
+                       int2[0] -= 1
+                       int2[1] = 1.
+               if int1[0] == 0 and int2[0]==len(subpath)-1:# and small(int1[1]) and small(int2[1]-1) :
+                       continue
+               if int1[0]==int2[0] :   # same segment
+                       sp = csp_split_by_two_points(subpath[int1[0]-1],subpath[int1[0]],int1[1], int2[1])
+                       if sp[1]!=sp[2] :
+                               parts += [   [sp[1],sp[2]]     ]
+               else :
+                       sp5,sp1,sp2 = csp_split(subpath[int1[0]-1],subpath[int1[0]],int1[1])
+                       sp3,sp4,sp5 = csp_split(subpath[int2[0]-1],subpath[int2[0]],int2[1])
+                       if int1[0]==int2[0]-1 :
+                               parts += [      [sp1, [sp2[0],sp2[1],sp3[2]], sp4]  ]
+                       else :
+                               parts += [  [sp1,sp2]+subpath[int1[0]+1:int2[0]-1]+[sp3,sp4]  ]
+       return parts
+
+
+def csp_from_arc(start, end, center, r, slope_st) : 
+       # Creates csp that approximise specified arc
+       r = abs(r)
+       alpha = (atan2(end[0]-center[0],end[1]-center[1]) - atan2(start[0]-center[0],start[1]-center[1])) % math.pi2
+
+       sectors = int(abs(alpha)*2/math.pi)+1
+       alpha_start = atan2(start[0]-center[0],start[1]-center[1])
+       cos_,sin_ = math.cos(alpha_start), math.sin(alpha_start)
+       k = (4.*math.tan(alpha/sectors/4.)/3.)
+       if dot(slope_st , [- sin_*k*r, cos_*k*r]) < 0 :
+               if alpha>0 : alpha -= math.pi2
+               else: alpha += math.pi2
+       if abs(alpha*r)<0.001 : 
+               return []
+
+       sectors = int(abs(alpha)*2/math.pi)+1
+       k = (4.*math.tan(alpha/sectors/4.)/3.)
+       result = []
+       for i in range(sectors+1) :
+               cos_,sin_ = math.cos(alpha_start + alpha*i/sectors), math.sin(alpha_start + alpha*i/sectors)
+               sp = [ [], [center[0] + cos_*r, center[1] + sin_*r], [] ]
+               sp[0] = [sp[1][0] + sin_*k*r, sp[1][1] - cos_*k*r ]
+               sp[2] = [sp[1][0] - sin_*k*r, sp[1][1] + cos_*k*r ]
+               result += [sp]
+       result[0][0] = result[0][1][:]
+       result[-1][2] = result[-1][1]
+               
+       return result
+
+
+def point_to_arc_distance(p, arc):
+       ###             Distance calculattion from point to arc
+       P0,P2,c,a = arc
+       dist = None
+       p = P(p)
+       r = (P0-c).mag()
+       if r>0 :
+               i = c + (p-c).unit()*r
+               alpha = ((i-c).angle() - (P0-c).angle())
+               if a*alpha<0: 
+                       if alpha>0:     alpha = alpha-math.pi2
+                       else: alpha = math.pi2+alpha
+               if between(alpha,0,a) or min(abs(alpha),abs(alpha-a))<straight_tolerance : 
+                       return (p-i).mag(), [i.x, i.y]
+               else : 
+                       d1, d2 = (p-P0).mag(), (p-P2).mag()
+                       if d1<d2 : 
+                               return (d1, [P0.x,P0.y])
+                       else :
+                               return (d2, [P2.x,P2.y])
+
+
+def csp_to_arc_distance(sp1,sp2, arc1, arc2, tolerance = 0.01 ): # arc = [start,end,center,alpha]
+       n, i = 10, 0
+       d, d1, dl = (0,(0,0)), (0,(0,0)), 0
+       while i<1 or (abs(d1[0]-dl[0])>tolerance and i<4):
+               i += 1
+               dl = d1*1       
+               for j in range(n+1):
+                       t = float(j)/n
+                       p = csp_at_t(sp1,sp2,t) 
+                       d = min(point_to_arc_distance(p,arc1), point_to_arc_distance(p,arc2))
+                       d1 = max(d1,d)
+               n=n*2
+       return d1[0]
+
+
+def csp_simple_bound_to_point_distance(p, csp):
+       minx,miny,maxx,maxy = None,None,None,None
+       for subpath in csp:
+               for sp in subpath:
+                       for p_ in sp:
+                               minx = min(minx,p_[0]) if minx!=None else p_[0]
+                               miny = min(miny,p_[1]) if miny!=None else p_[1]
+                               maxx = max(maxx,p_[0]) if maxx!=None else p_[0]
+                               maxy = max(maxy,p_[1]) if maxy!=None else p_[1]
+       return math.sqrt(max(minx-p[0],p[0]-maxx,0)**2+max(miny-p[1],p[1]-maxy,0)**2)
+
+
+def csp_point_inside_bound(sp1, sp2, p):
+       bez = [sp1[1],sp1[2],sp2[0],sp2[1]]
+       x,y = p
+       c = 0
+       for i in range(4):
+               [x0,y0], [x1,y1] = bez[i-1], bez[i]
+               if x0-x1!=0 and (y-y0)*(x1-x0)>=(x-x0)*(y1-y0) and x>min(x0,x1) and x<=max(x0,x1) :
+                       c +=1
+       return c%2==0   
+
+
+def csp_bound_to_point_distance(sp1, sp2, p):
+       if csp_point_inside_bound(sp1, sp2, p) :
+               return 0.
+       bez = csp_segment_to_bez(sp1,sp2)
+       min_dist = 1e100
+       for i in range(0,4):
+               d = point_to_line_segment_distance_2(p, bez[i-1],bez[i])
+               if d <= min_dist : min_dist = d
+       return min_dist 
+
+
+def line_line_intersect(p1,p2,p3,p4) : # Return only true intersection. 
+       if (p1[0]==p2[0] and p1[1]==p2[1]) or (p3[0]==p4[0] and p3[1]==p4[1]) : return False
+       x = (p2[0]-p1[0])*(p4[1]-p3[1]) - (p2[1]-p1[1])*(p4[0]-p3[0])
+       if x==0 : # Lines are parallel
+               if (p3[0]-p1[0])*(p2[1]-p1[1]) == (p3[1]-p1[1])*(p2[0]-p1[0]) :
+                       if p3[0]!=p4[0] :
+                               t11 = (p1[0]-p3[0])/(p4[0]-p3[0]) 
+                               t12 = (p2[0]-p3[0])/(p4[0]-p3[0]) 
+                               t21 = (p3[0]-p1[0])/(p2[0]-p1[0])
+                               t22 = (p4[0]-p1[0])/(p2[0]-p1[0])
+                       else:
+                               t11 = (p1[1]-p3[1])/(p4[1]-p3[1]) 
+                               t12 = (p2[1]-p3[1])/(p4[1]-p3[1]) 
+                               t21 = (p3[1]-p1[1])/(p2[1]-p1[1])
+                               t22 = (p4[1]-p1[1])/(p2[1]-p1[1])
+                       return ("Overlap" if (0<=t11<=1 or 0<=t12<=1) and (0<=t21<=1 or  0<=t22<=1) else False)
+               else: return False      
+       else :
+               return (
+                                       0<=((p4[0]-p3[0])*(p1[1]-p3[1]) - (p4[1]-p3[1])*(p1[0]-p3[0]))/x<=1 and
+                                       0<=((p2[0]-p1[0])*(p1[1]-p3[1]) - (p2[1]-p1[1])*(p1[0]-p3[0]))/x<=1 )
+                                       
+                                       
+def line_line_intersection_points(p1,p2,p3,p4) : # Return only points [ (x,y) ] 
+       if (p1[0]==p2[0] and p1[1]==p2[1]) or (p3[0]==p4[0] and p3[1]==p4[1]) : return []
+       x = (p2[0]-p1[0])*(p4[1]-p3[1]) - (p2[1]-p1[1])*(p4[0]-p3[0])
+       if x==0 : # Lines are parallel
+               if (p3[0]-p1[0])*(p2[1]-p1[1]) == (p3[1]-p1[1])*(p2[0]-p1[0]) :
+                       if p3[0]!=p4[0] :
+                               t11 = (p1[0]-p3[0])/(p4[0]-p3[0]) 
+                               t12 = (p2[0]-p3[0])/(p4[0]-p3[0]) 
+                               t21 = (p3[0]-p1[0])/(p2[0]-p1[0])
+                               t22 = (p4[0]-p1[0])/(p2[0]-p1[0])
+                       else:
+                               t11 = (p1[1]-p3[1])/(p4[1]-p3[1]) 
+                               t12 = (p2[1]-p3[1])/(p4[1]-p3[1]) 
+                               t21 = (p3[1]-p1[1])/(p2[1]-p1[1])
+                               t22 = (p4[1]-p1[1])/(p2[1]-p1[1])
+                       res = [] 
+                       if (0<=t11<=1 or 0<=t12<=1) and (0<=t21<=1 or  0<=t22<=1) :
+                               if 0<=t11<=1 : res += [p1]      
+                               if 0<=t12<=1 : res += [p2]      
+                               if 0<=t21<=1 : res += [p3]      
+                               if 0<=t22<=1 : res += [p4]      
+                       return res
+               else: return []
+       else :
+               t1 = ((p4[0]-p3[0])*(p1[1]-p3[1]) - (p4[1]-p3[1])*(p1[0]-p3[0]))/x
+               t2 = ((p2[0]-p1[0])*(p1[1]-p3[1]) - (p2[1]-p1[1])*(p1[0]-p3[0]))/x
+               if 0<=t1<=1 and 0<=t2<=1 : return [ [p1[0]*(1-t1)+p2[0]*t1, p1[1]*(1-t1)+p2[1]*t1] ]
+               else : return []                                        
+
+
+def point_to_point_d2(a,b):
+       return (a[0]-b[0])**2 + (a[1]-b[1])**2
+
+
+def point_to_point_d(a,b):
+       return math.sqrt((a[0]-b[0])**2 + (a[1]-b[1])**2)
+
+
+def point_to_line_segment_distance_2(p1, p2,p3) :
+       # p1 - point, p2,p3 - line segment
+       #draw_pointer(p1)
+       w0 = [p1[0]-p2[0], p1[1]-p2[1]]
+       v = [p3[0]-p2[0], p3[1]-p2[1]]
+       c1 = w0[0]*v[0] + w0[1]*v[1]
+       if c1 <= 0 :
+               return w0[0]*w0[0]+w0[1]*w0[1]
+       c2 = v[0]*v[0] + v[1]*v[1]
+       if c2 <= c1 :
+               return  (p1[0]-p3[0])**2 + (p1[1]-p3[1])**2
+       return (p1[0]- p2[0]-v[0]*c1/c2)**2 + (p1[1]- p2[1]-v[1]*c1/c2)
+
+
+def line_to_line_distance_2(p1,p2,p3,p4):
+       if line_line_intersect(p1,p2,p3,p4) : return 0
+       return min(
+                       point_to_line_segment_distance_2(p1,p3,p4),
+                       point_to_line_segment_distance_2(p2,p3,p4),
+                       point_to_line_segment_distance_2(p3,p1,p2),
+                       point_to_line_segment_distance_2(p4,p1,p2))
+
+
+def csp_seg_bound_to_csp_seg_bound_max_min_distance(sp1,sp2,sp3,sp4) :
+       bez1 = csp_segment_to_bez(sp1,sp2)
+       bez2 = csp_segment_to_bez(sp3,sp4) 
+       min_dist = 1e100
+       max_dist = 0.
+       for i in range(4) :
+               if csp_point_inside_bound(sp1, sp2, bez2[i]) or csp_point_inside_bound(sp3, sp4, bez1[i]) :
+                       min_dist = 0.
+                       break   
+       for i in range(4) : 
+               for j in range(4) : 
+                       d = line_to_line_distance_2(bez1[i-1],bez1[i],bez2[j-1],bez2[j])
+                       if d < min_dist : min_dist = d
+                       d = (bez2[j][0]-bez1[i][0])**2 + (bez2[j][1]-bez1[i][1])**2 
+                       if max_dist < d  : max_dist = d
+       return min_dist, max_dist
+
+
+def csp_reverse(csp) :
+       for i in range(len(csp)) :
+               n = []
+               for j in csp[i] :
+                       n = [  [j[2][:],j[1][:],j[0][:]]  ] + n
+               csp[i] = n[:]
+       return csp                      
+
+
+def csp_normalized_slope(sp1,sp2,t) :
+       ax,ay,bx,by,cx,cy,dx,dy=bezmisc.bezierparameterize((sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:]))
+       if sp1[1]==sp2[1]==sp1[2]==sp2[0] : return [1.,0.]
+       f1x = 3*ax*t*t+2*bx*t+cx
+       f1y = 3*ay*t*t+2*by*t+cy
+       if abs(f1x*f1x+f1y*f1y) > 1e-20 :
+               l = math.sqrt(f1x*f1x+f1y*f1y)
+               return [f1x/l, f1y/l]
+
+       if t == 0 :
+               f1x = sp2[0][0]-sp1[1][0]
+               f1y = sp2[0][1]-sp1[1][1]
+               if abs(f1x*f1x+f1y*f1y) > 1e-20 :
+                       l = math.sqrt(f1x*f1x+f1y*f1y)
+                       return [f1x/l, f1y/l]
+               else :
+                       f1x = sp2[1][0]-sp1[1][0]
+                       f1y = sp2[1][1]-sp1[1][1]
+                       if f1x*f1x+f1y*f1y != 0 :
+                               l = math.sqrt(f1x*f1x+f1y*f1y)
+                               return [f1x/l, f1y/l]
+       elif t == 1 :
+               f1x = sp2[1][0]-sp1[2][0]
+               f1y = sp2[1][1]-sp1[2][1]
+               if abs(f1x*f1x+f1y*f1y) > 1e-20 :
+                       l = math.sqrt(f1x*f1x+f1y*f1y)
+                       return [f1x/l, f1y/l]
+               else :
+                       f1x = sp2[1][0]-sp1[1][0]
+                       f1y = sp2[1][1]-sp1[1][1]
+                       if f1x*f1x+f1y*f1y != 0 :
+                               l = math.sqrt(f1x*f1x+f1y*f1y)
+                               return [f1x/l, f1y/l]
+       else :
+               return [1.,0.]
+
+                               
+def csp_normalized_normal(sp1,sp2,t) :
+       nx,ny = csp_normalized_slope(sp1,sp2,t)
+       return [-ny, nx]
+
+
+def csp_parameterize(sp1,sp2):
+       return bezmisc.bezierparameterize(csp_segment_to_bez(sp1,sp2))
+
+
+def csp_concat_subpaths(*s):
+       
+       def concat(s1,s2) :
+               if s1 == [] : return s2
+               if s2 == [] : return s1
+               if (s1[-1][1][0]-s2[0][1][0])**2 + (s1[-1][1][1]-s2[0][1][1])**2 > 0.00001 :
+                       return s1[:-1]+[ [s1[-1][0],s1[-1][1],s1[-1][1]],  [s2[0][1],s2[0][1],s2[0][2]] ] + s2[1:]              
+               else :
+                       return s1[:-1]+[ [s1[-1][0],s2[0][1],s2[0][2]] ] + s2[1:]               
+                       
+       if len(s) == 0 : return []
+       if len(s) ==1 : return s[0]
+       result = s[0]
+       for s1 in s[1:]:
+               result = concat(result,s1)
+       return result
+
+
+def csp_draw(csp, color="#05f", group = None, style="fill:none;", width = .1, comment = "") :
+       if csp!=[] and csp!=[[]] :
+               if group == None : group = options.doc_root 
+               style += "stroke:"+color+";"+ "stroke-width:%0.4fpx;"%width
+               args = {"d": cubicsuperpath.formatPath(csp), "style":style}
+               if comment!="" : args["comment"] = str(comment)
+               inkex.etree.SubElement( group, inkex.addNS('path','svg'), args )                                                        
+
+       
+def csp_subpaths_end_to_start_distance2(s1,s2):
+       return (s1[-1][1][0]-s2[0][1][0])**2 + (s1[-1][1][1]-s2[0][1][1])**2
+
+
+def csp_clip_by_line(csp,l1,l2) :
+       result = []
+       for i in range(len(csp)):
+               s = csp[i]
+               intersections = []
+               for j in range(1,len(s)) :
+                       intersections += [  [j,int_] for int_ in csp_line_intersection(l1,l2,s[j-1],s[j])]
+               splitted_s = csp_subpath_split_by_points(s, intersections)
+               for s in splitted_s[:] :
+                       clip = False
+                       for p in csp_true_bounds([s]) :
+                               if (l1[1]-l2[1])*p[0] + (l2[0]-l1[0])*p[1] + (l1[0]*l2[1]-l2[0]*l1[1])<-0.01 : 
+                                       clip = True
+                                       break
+                       if clip :
+                               splitted_s.remove(s)
+               result += splitted_s
+       return result
+
+
+def csp_subpath_line_to(subpath, points) :
+       # Appends subpath with line or polyline.
+       if len(points)>0 :
+               if len(subpath)>0:
+                       subpath[-1][2] = subpath[-1][1][:]
+               if type(points[0]) == type([1,1]) :
+                       for p in points :
+                               subpath += [ [p[:],p[:],p[:]] ]
+               else: 
+                       subpath += [ [points,points,points] ]
+       return subpath
+
+       
+def csp_join_subpaths(csp) :
+       result = csp[:]
+       done_smf = True
+       joined_result = []
+       while done_smf :
+               done_smf = False
+               while len(result)>0:
+                       s1 = result[-1][:]
+                       del(result[-1])
+                       j = 0
+                       joined_smf = False
+                       while j<len(joined_result) :
+                               if csp_subpaths_end_to_start_distance2(joined_result[j],s1) <0.000001 :
+                                       joined_result[j] = csp_concat_subpaths(joined_result[j],s1)
+                                       done_smf = True
+                                       joined_smf = True
+                                       break                           
+                               if csp_subpaths_end_to_start_distance2(s1,joined_result[j]) <0.000001 :
+                                       joined_result[j] = csp_concat_subpaths(s1,joined_result[j])
+                                       done_smf = True
+                                       joined_smf = True
+                                       break                           
+                               j += 1
+                       if not joined_smf : joined_result += [s1[:]]
+               if done_smf : 
+                       result = joined_result[:]
+                       joined_result = []
+       return joined_result
+
+
+def triangle_cross(a,b,c):
+       return (a[0]-b[0])*(c[1]-b[1]) - (c[0]-b[0])*(a[1]-b[1])
+       
+
+def csp_segment_convex_hull(sp1,sp2):
+       a,b,c,d = sp1[1][:], sp1[2][:], sp2[0][:], sp2[1][:]
+       
+       abc = triangle_cross(a,b,c)
+       abd = triangle_cross(a,b,d)
+       bcd = triangle_cross(b,c,d)
+       cad = triangle_cross(c,a,d)
+       if abc == 0 and abd == 0 : return [min(a,b,c,d), max(a,b,c,d)]
+       if abc == 0 : return [d, min(a,b,c), max(a,b,c)]
+       if abd == 0 : return [c, min(a,b,d), max(a,b,d)]
+       if bcd == 0 : return [a, min(b,c,d), max(b,c,d)]
+       if cad == 0 : return [b, min(c,a,d), max(c,a,d)]
+       
+       m1, m2, m3  =  abc*abd>0, abc*bcd>0, abc*cad>0
+       if m1 and m2 and m3 : return [a,b,c]
+       if     m1 and     m2 and not m3 : return [a,b,c,d]
+       if     m1 and not m2 and     m3 : return [a,b,d,c]
+       if not m1 and     m2 and     m3 : return [a,d,b,c]
+       if m1 and not (m2 and m3) : return [a,b,d]
+       if not (m1 and m2) and m3 : return [c,a,d]
+       if not (m1 and m3) and m2 : return [b,c,d]
+       
+       raise ValueError, "csp_segment_convex_hull happend something that shouldnot happen!"    
+
+       
+################################################################################
+###            Bezier additional functions
+################################################################################
+
+def bez_bounds_intersect(bez1, bez2) :
+       return bounds_intersect(bez_bound(bez2), bez_bound(bez1))
+
+
+def bez_bound(bez) :
+       return [
+                               min(bez[0][0], bez[1][0], bez[2][0], bez[3][0]),
+                               min(bez[0][1], bez[1][1], bez[2][1], bez[3][1]),
+                               max(bez[0][0], bez[1][0], bez[2][0], bez[3][0]),
+                               max(bez[0][1], bez[1][1], bez[2][1], bez[3][1]),
+                       ]
+
+
+def bounds_intersect(a, b) :
+       return not ( (a[0]>b[2]) or (b[0]>a[2]) or (a[1]>b[3]) or (b[1]>a[3]) )
+
+
+def tpoint((x1,y1),(x2,y2),t):
+       return [x1+t*(x2-x1),y1+t*(y2-y1)]
+
+
+def bez_to_csp_segment(bez) :
+       return [bez[0],bez[0],bez[1]], [bez[2],bez[3],bez[3]]
+
+
+def bez_split(a,t=0.5) :
+        a1 = tpoint(a[0],a[1],t)
+        at = tpoint(a[1],a[2],t)
+        b2 = tpoint(a[2],a[3],t)
+        a2 = tpoint(a1,at,t)
+        b1 = tpoint(b2,at,t)
+        a3 = tpoint(a2,b1,t)
+        return [a[0],a1,a2,a3], [a3,b1,b2,a[3]]
+
+       
+def bez_at_t(bez,t) :
+       return csp_at_t([bez[0],bez[0],bez[1]],[bez[2],bez[3],bez[3]],t)
+
+
+def bez_to_point_distance(bez,p,needed_dist=[0.,1e100]):
+       # returns [d^2,t]
+       return csp_seg_to_point_distance(bez_to_csp_segment(bez),p,needed_dist)
+
+
+def bez_normalized_slope(bez,t):
+       return csp_normalized_slope([bez[0],bez[0],bez[1]], [bez[2],bez[3],bez[3]],t)   
+
+################################################################################
+###    Some vector functions
+################################################################################
+       
+def normalize((x,y)) :
+       l = math.sqrt(x**2+y**2)
+       if l == 0 : return [0.,0.]
+       else :          return [x/l, y/l]
+
+
+def cross(a,b) :
+       return a[1] * b[0] - a[0] * b[1]
+
+
+def dot(a,b) :
+       return a[0] * b[0] + a[1] * b[1]
+
+
+def rotate_ccw(d) :
+       return [-d[1],d[0]]
+
+
+def vectors_ccw(a,b):
+       return a[0]*b[1]-b[0]*a[1] < 0
+
+
+def vector_from_to_length(a,b):
+       return math.sqrt((a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]))
+
+################################################################################
+###    Common functions
+################################################################################
+
+def matrix_mul(a,b) :
+       return [ [ sum([a[i][k]*b[k][j] for k in range(len(a[0])) ])   for j in range(len(b[0]))]   for i in range(len(a))] 
+       try :
+               return [ [ sum([a[i][k]*b[k][j] for k in range(len(a[0])) ])   for j in range(len(b[0]))]   for i in range(len(a))] 
+       except :
+               return None
+
+
+def transpose(a) :
+       try :
+               return [ [ a[i][j] for i in range(len(a)) ] for j in range(len(a[0])) ]
+       except :
+               return None
+
+
+def det_3x3(a):
+       return  float(
+               a[0][0]*a[1][1]*a[2][2] + a[0][1]*a[1][2]*a[2][0] + a[1][0]*a[2][1]*a[0][2]
+               - a[0][2]*a[1][1]*a[2][0] - a[0][0]*a[2][1]*a[1][2] - a[0][1]*a[2][2]*a[1][0]
+               )
+
+
+def inv_3x3(a): # invert matrix 3x3
+       det = det_3x3(a)
+       if det==0: return None
+       return  [  
+               [  (a[1][1]*a[2][2] - a[2][1]*a[1][2])/det,  -(a[0][1]*a[2][2] - a[2][1]*a[0][2])/det,  (a[0][1]*a[1][2] - a[1][1]*a[0][2])/det ], 
+               [ -(a[1][0]*a[2][2] - a[2][0]*a[1][2])/det,   (a[0][0]*a[2][2] - a[2][0]*a[0][2])/det, -(a[0][0]*a[1][2] - a[1][0]*a[0][2])/det ], 
+               [  (a[1][0]*a[2][1] - a[2][0]*a[1][1])/det,  -(a[0][0]*a[2][1] - a[2][0]*a[0][1])/det,  (a[0][0]*a[1][1] - a[1][0]*a[0][1])/det ]
+       ]
+
+
+def inv_2x2(a): # invert matrix 2x2
+       det = a[0][0]*a[1][1] - a[1][0]*a[0][1]
+       if det==0: return None
+       return [
+                       [a[1][1]/det, -a[0][1]/det],
+                       [-a[1][0]/det, a[0][0]/det]
+                       ]
+
+
+def small(a) :
+       global small_tolerance
+       return abs(a)<small_tolerance
+
+                                       
+def atan2(*arg):       
+       if len(arg)==1 and ( type(arg[0]) == type([0.,0.]) or type(arg[0])==type((0.,0.)) ) :
+               return (math.pi/2 - math.atan2(arg[0][0], arg[0][1]) ) % math.pi2
+       elif len(arg)==2 :
+               
+               return (math.pi/2 - math.atan2(arg[0],arg[1]) ) % math.pi2
+       else :
+               raise ValueError, "Bad argumets for atan! (%s)" % arg  
+
+
+def draw_text(text,x,y,style = None, font_size = 20) :
+       if style == None : 
+               style = "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;"
+       style += "font-size:%fpx;"%font_size
+       t = inkex.etree.SubElement(     options.doc_root, inkex.addNS('text','svg'), {  
+                                                       'x':    str(x),
+                                                       inkex.addNS("space","xml"):"preserve",
+                                                       'y':    str(y)
+                                               })
+       text = str(text).split("\n")
+       for s in text :
+               span = inkex.etree.SubElement( t, inkex.addNS('tspan','svg'), 
+                                               {
+                                                       'x':    str(x),
+                                                       'y':    str(+y),
+                                                       inkex.addNS("role","sodipodi"):"line",
+                                               })                                      
+               y += font_size
+               span.text = s
+                       
+
+def draw_pointer(x,color = "#f00", figure = "cross", comment = "", width = .1) :
+       if figure ==  "line" :
+               s = ""
+               for i in range(1,len(x)/2) :
+                       s+= " %s, %s " %(x[i*2],x[i*2+1])
+               inkex.etree.SubElement( options.doc_root, inkex.addNS('path','svg'), {"d": "M %s,%s L %s"%(x[0],x[1],s), "style":"fill:none;stroke:%s;stroke-width:%f;"%(color,width),"comment":str(comment)} )
+       else :
+               inkex.etree.SubElement( options.doc_root, inkex.addNS('path','svg'), {"d": "m %s,%s l 10,10 -20,-20 10,10 -10,10, 20,-20"%(x[0],x[1]), "style":"fill:none;stroke:%s;stroke-width:%f;"%(color,width),"comment":str(comment)} )
+
+
+def straight_segments_intersection(a,b, true_intersection = True) : # (True intersection means check ta and tb are in [0,1])
+       ax,bx,cx,dx, ay,by,cy,dy = a[0][0],a[1][0],b[0][0],b[1][0], a[0][1],a[1][1],b[0][1],b[1][1] 
+       if (ax==bx and ay==by) or (cx==dx and cy==dy) : return False, 0, 0
+       if (bx-ax)*(dy-cy)-(by-ay)*(dx-cx)==0 : # Lines are parallel
+               ta = (ax-cx)/(dx-cx) if cx!=dx else (ay-cy)/(dy-cy)
+               tb = (bx-cx)/(dx-cx) if cx!=dx else (by-cy)/(dy-cy)
+               tc = (cx-ax)/(bx-ax) if ax!=bx else (cy-ay)/(by-ay)
+               td = (dx-ax)/(bx-ax) if ax!=bx else (dy-ay)/(by-ay)
+               return ("Overlap" if 0<=ta<=1 or 0<=tb<=1 or  0<=tc<=1 or  0<=td<=1 or not true_intersection else False), (ta,tb), (tc,td)
+       else :
+               ta = ( (ay-cy)*(dx-cx)-(ax-cx)*(dy-cy) ) / ( (bx-ax)*(dy-cy)-(by-ay)*(dx-cx) )
+               tb = ( ax-cx+ta*(bx-ax) ) / (dx-cx) if dx!=cx else ( ay-cy+ta*(by-ay) ) / (dy-cy)
+               return (0<=ta<=1 and 0<=tb<=1 or not true_intersection), ta, tb
+       
+       
+
+def isnan(x): return type(x) is float and x != x
+
+def isinf(x): inf = 1e5000; return x == inf or x == -inf
+
+def between(c,x,y):
+               return x-straight_tolerance<=c<=y+straight_tolerance or y-straight_tolerance<=c<=x+straight_tolerance
+
+
+def cubic_solver(a,b,c,d):     
+       if a!=0:
+               #       Monics formula see http://en.wikipedia.org/wiki/Cubic_function#Monic_formula_of_roots
+               a,b,c = (b/a, c/a, d/a)
+               m = 2*a**3 - 9*a*b + 27*c
+               k = a**2 - 3*b
+               n = m**2 - 4*k**3
+               w1 = -.5 + .5*cmath.sqrt(3)*1j
+               w2 = -.5 - .5*cmath.sqrt(3)*1j
+               if n>=0 :
+                       t = m+math.sqrt(n)
+                       m1 = pow(t/2,1./3) if t>=0 else -pow(-t/2,1./3)
+                       t = m-math.sqrt(n)
+                       n1 = pow(t/2,1./3) if t>=0 else -pow(-t/2,1./3)
+               else :
+                       m1 = pow(complex((m+cmath.sqrt(n))/2),1./3)
+                       n1 = pow(complex((m-cmath.sqrt(n))/2),1./3)
+               x1 = -1./3 * (a + m1 + n1)
+               x2 = -1./3 * (a + w1*m1 + w2*n1)
+               x3 = -1./3 * (a + w2*m1 + w1*n1)
+               return [x1,x2,x3]
+       elif b!=0:
+               det = c**2-4*b*d
+               if det>0 :
+                       return [(-c+math.sqrt(det))/(2*b),(-c-math.sqrt(det))/(2*b)]
+               elif d == 0 :
+                       return [-c/(b*b)]       
+               else :
+                       return [(-c+cmath.sqrt(det))/(2*b),(-c-cmath.sqrt(det))/(2*b)]
+       elif c!=0 :
+               return [-d/c]
+       else : return []
+
+
+################################################################################
+###            print_ prints any arguments into specified log file
+################################################################################
+
+def print_(*arg):
+       f = open(options.log_filename,"a")
+       for s in arg :
+               s = str(unicode(s).encode('unicode_escape'))+" "
+               f.write( s )
+       f.write("\n")
+       f.close()
+
+
+################################################################################
+###            Point (x,y) operations
+################################################################################
+class P:
+       def __init__(self, x, y=None):
+               if not y==None:
+                       self.x, self.y = float(x), float(y)
+               else:
+                       self.x, self.y = float(x[0]), float(x[1])
+       def __add__(self, other): return P(self.x + other.x, self.y + other.y)
+       def __sub__(self, other): return P(self.x - other.x, self.y - other.y)
+       def __neg__(self): return P(-self.x, -self.y)
+       def __mul__(self, other):
+               if isinstance(other, P):
+                       return self.x * other.x + self.y * other.y
+               return P(self.x * other, self.y * other)
+       __rmul__ = __mul__
+       def __div__(self, other): return P(self.x / other, self.y / other)
+       def mag(self): return math.hypot(self.x, self.y)
+       def unit(self):
+               h = self.mag()
+               if h: return self / h
+               else: return P(0,0)
+       def dot(self, other): return self.x * other.x + self.y * other.y
+       def rot(self, theta):
+               c = math.cos(theta)
+               s = math.sin(theta)
+               return P(self.x * c - self.y * s,  self.x * s + self.y * c)
+       def angle(self): return math.atan2(self.y, self.x)
+       def __repr__(self): return '%f,%f' % (self.x, self.y)
+       def pr(self): return "%.2f,%.2f" % (self.x, self.y)
+       def to_list(self): return [self.x, self.y]      
+       def ccw(self): return P(-self.y,self.x)
+       def l2(self): return self.x*self.x + self.y*self.y
+       
+################################################################################
+###
+### Offset function 
+###
+### This function offsets given cubic super path.  
+### It's based on src/livarot/PathOutline.cpp from Inkscape's source code.
+###
+###
+################################################################################
+def csp_offset(csp, r) :
+       offset_tolerance = 0.05
+       offset_subdivision_depth = 10
+       time_ = time.time()
+       time_start  = time_
+       print_("Offset start at %s"% time_)
+       print_("Offset radius %s"% r)
+       
+       
+       def csp_offset_segment(sp1,sp2,r) :
+               result = []
+               t = csp_get_t_at_curvature(sp1,sp2,1/r)
+               if len(t) == 0 : t =[0.,1.]
+               t.sort()
+               if t[0]>.00000001 : t = [0.]+t 
+               if t[-1]<.99999999 : t.append(1.) 
+               for st,end in zip(t,t[1:]) :    
+                       c = csp_curvature_at_t(sp1,sp2,(st+end)/2)
+                       sp = csp_split_by_two_points(sp1,sp2,st,end)
+                       if sp[1]!=sp[2]:
+                               if (c>1/r and r<0 or c<1/r and r>0) :
+                                       offset = offset_segment_recursion(sp[1],sp[2],r, offset_subdivision_depth, offset_tolerance) 
+                               else : # This part will be clipped for sure... TODO Optimize it...
+                                       offset = offset_segment_recursion(sp[1],sp[2],r, offset_subdivision_depth, offset_tolerance) 
+                                       
+                               if result==[] :
+                                       result = offset[:]
+                               else: 
+                                       if csp_subpaths_end_to_start_distance2(result,offset)<0.0001 :
+                                               result = csp_concat_subpaths(result,offset)
+                                       else:
+                                               
+                                               intersection = csp_get_subapths_last_first_intersection(result,offset)
+                                               if intersection != [] :
+                                                       i,t1,j,t2 = intersection
+                                                       sp1_,sp2_,sp3_ = csp_split(result[i-1],result[i],t1)
+                                                       result = result[:i-1] + [ sp1_, sp2_ ]
+                                                       sp1_,sp2_,sp3_ = csp_split(offset[j-1],offset[j],t2)
+                                                       result = csp_concat_subpaths( result, [sp2_,sp3_] + offset[j+1:] )
+                                               else : 
+                                                       pass # ???                                              
+                                                       #raise ValueError, "Offset curvature clipping error"
+               #csp_draw([result])                                                     
+               return result                                   
+       
+                                               
+       def create_offset_segment(sp1,sp2,r) :
+               # See   Gernot Hoffmann "Bezier Curves"  p.34 -> 7.1 Bezier Offset Curves
+               p0,p1,p2,p3 = P(sp1[1]),P(sp1[2]),P(sp2[0]),P(sp2[1])
+               s0,s1,s3 = p1-p0,p2-p1,p3-p2
+               n0 = s0.ccw().unit() if s0.l2()!=0 else P(csp_normalized_normal(sp1,sp2,0))
+               n3 = s3.ccw().unit() if s3.l2()!=0 else P(csp_normalized_normal(sp1,sp2,1))
+               n1 = s1.ccw().unit() if s1.l2()!=0 else (n0.unit()+n3.unit()).unit()
+
+               q0,q3 = p0+r*n0, p3+r*n3
+               c = csp_curvature_at_t(sp1,sp2,0)
+               q1 = q0 + (p1-p0)*(1- (r*c if abs(c)<100 else 0) )
+               c = csp_curvature_at_t(sp1,sp2,1)
+               q2 = q3 + (p2-p3)*(1- (r*c if abs(c)<100 else 0) ) 
+
+               
+               return [[q0.to_list(), q0.to_list(), q1.to_list()],[q2.to_list(), q3.to_list(), q3.to_list()]]
+       
+       
+       def csp_get_subapths_last_first_intersection(s1,s2):
+               _break = False
+               for i in range(1,len(s1)) :
+                       sp11, sp12 = s1[-i-1], s1[-i]
+                       for j in range(1,len(s2)) :
+                               sp21,sp22 = s2[j-1], s2[j] 
+                               intersection = csp_segments_true_intersection(sp11,sp12,sp21,sp22)
+                               if intersection != [] :
+                                       _break = True
+                                       break 
+                       if _break:break
+               if _break :
+                       intersection = max(intersection)
+                       return [len(s1)-i,intersection[0], j,intersection[1]]
+               else : 
+                       return []
+       
+               
+       def csp_join_offsets(prev,next,sp1,sp2,sp1_l,sp2_l,r):
+               if len(next)>1 :
+                       if (P(prev[-1][1])-P(next[0][1])).l2()<0.001 : 
+                               return prev,[],next
+                       intersection = csp_get_subapths_last_first_intersection(prev,next)
+                       if intersection != [] :
+                               i,t1,j,t2 = intersection
+                               sp1_,sp2_,sp3_ = csp_split(prev[i-1],prev[i],t1)
+                               sp3_,sp4_,sp5_ = csp_split(next[j-1], next[j],t2)
+                               return prev[:i-1] + [ sp1_, sp2_ ], [], [sp4_,sp5_] + next[j+1:] 
+                       
+               # Offsets do not intersect... will add an arc...
+               start = (P(csp_at_t(sp1_l,sp2_l,1.)) + r*P(csp_normalized_normal(sp1_l,sp2_l,1.))).to_list()
+               end   = (P(csp_at_t(sp1,sp2,0.)) + r*P(csp_normalized_normal(sp1,sp2,0.))).to_list()
+               arc = csp_from_arc(start, end, sp1[1], r, csp_normalized_slope(sp1_l,sp2_l,1.) )
+               if arc == [] :
+                       return prev,[],next
+               else:
+                       # Clip prev by arc
+                       if csp_subpaths_end_to_start_distance2(prev,arc)>0.00001 :
+                               intersection = csp_get_subapths_last_first_intersection(prev,arc)
+                               if intersection != [] :
+                                       i,t1,j,t2 = intersection
+                                       sp1_,sp2_,sp3_ = csp_split(prev[i-1],prev[i],t1)
+                                       sp3_,sp4_,sp5_ = csp_split(arc[j-1],arc[j],t2)
+                                       prev = prev[:i-1] + [ sp1_, sp2_ ]
+                                       arc = [sp4_,sp5_] + arc[j+1:] 
+                               #else : raise ValueError, "Offset curvature clipping error"
+                       # Clip next by arc
+                       if next == [] :
+                               return prev,[],arc
+                       if csp_subpaths_end_to_start_distance2(arc,next)>0.00001 :
+                               intersection = csp_get_subapths_last_first_intersection(arc,next)
+                               if intersection != [] :
+                                       i,t1,j,t2 = intersection
+                                       sp1_,sp2_,sp3_ = csp_split(arc[i-1],arc[i],t1)
+                                       sp3_,sp4_,sp5_ = csp_split(next[j-1],next[j],t2)
+                                       arc = arc[:i-1] + [ sp1_, sp2_ ]
+                                       next = [sp4_,sp5_] + next[j+1:] 
+                               #else : raise ValueError, "Offset curvature clipping error"
+
+                       return prev,arc,next
+               
+               
+       def offset_segment_recursion(sp1,sp2,r, depth, tolerance) :
+               sp1_r,sp2_r = create_offset_segment(sp1,sp2,r)
+               err = max(
+                               csp_seg_to_point_distance(sp1_r,sp2_r, (P(csp_at_t(sp1,sp2,.25)) + P(csp_normalized_normal(sp1,sp2,.25))*r).to_list())[0], 
+                               csp_seg_to_point_distance(sp1_r,sp2_r, (P(csp_at_t(sp1,sp2,.50)) + P(csp_normalized_normal(sp1,sp2,.50))*r).to_list())[0],
+                               csp_seg_to_point_distance(sp1_r,sp2_r, (P(csp_at_t(sp1,sp2,.75)) + P(csp_normalized_normal(sp1,sp2,.75))*r).to_list())[0],
+                               )
+
+               if  err>tolerance**2 and depth>0:
+                       #print_(csp_seg_to_point_distance(sp1_r,sp2_r, (P(csp_at_t(sp1,sp2,.25)) + P(csp_normalized_normal(sp1,sp2,.25))*r).to_list())[0], tolerance)
+                       if depth > offset_subdivision_depth-2 :
+                               t = csp_max_curvature(sp1,sp2)
+                               t = max(.1,min(.9 ,t))
+                       else :
+                               t = .5
+                       sp3,sp4,sp5 = csp_split(sp1,sp2,t)
+                       r1 = offset_segment_recursion(sp3,sp4,r, depth-1, tolerance)
+                       r2 = offset_segment_recursion(sp4,sp5,r, depth-1, tolerance)
+                       return r1[:-1]+ [[r1[-1][0],r1[-1][1],r2[0][2]]] + r2[1:]
+               else :
+                       #csp_draw([[sp1_r,sp2_r]])
+                       #draw_pointer(sp1[1]+sp1_r[1], "#057", "line")
+                       #draw_pointer(sp2[1]+sp2_r[1], "#705", "line")
+                       return [sp1_r,sp2_r]
+               
+
+       ############################################################################
+       # Some small definitions
+       ############################################################################
+       csp_len = len(csp)
+
+       ############################################################################
+       # Prepare the path
+       ############################################################################
+       # Remove all small segments (segment length < 0.001)
+
+       for i in xrange(len(csp)) :
+               for j in xrange(len(csp[i])) :
+                       sp = csp[i][j]
+                       if (P(sp[1])-P(sp[0])).mag() < 0.001 :
+                               csp[i][j][0] = sp[1]
+                       if (P(sp[2])-P(sp[0])).mag() < 0.001 :
+                               csp[i][j][2] = sp[1]
+       for i in xrange(len(csp)) :
+               for j in xrange(1,len(csp[i])) :
+                       if cspseglength(csp[i][j-1], csp[i][j])<0.001 : 
+                               csp[i] = csp[i][:j] + csp[i][j+1:]
+               if cspseglength(csp[i][-1],csp[i][0])>0.001 : 
+                       csp[i][-1][2] = csp[i][-1][1]
+                       csp[i]+= [ [csp[i][0][1],csp[i][0][1],csp[i][0][1]] ]
+       
+       # TODO Get rid of self intersections.
+
+       original_csp = csp[:]
+       # Clip segments which has curvature>1/r. Because their offset will be selfintersecting and very nasty.
+                                       
+       print_("Offset prepared the path in %s"%(time.time()-time_))
+       print_("Path length = %s"% sum([len(i)for i in csp] ) )
+       time_ = time.time()
+
+       ############################################################################
+       # Offset
+       ############################################################################
+       # Create offsets for all segments in the path. And join them together inside each subpath.              
+       unclipped_offset = [[] for i in xrange(csp_len)]
+       offsets_original = [[] for i in xrange(csp_len)]
+       join_points = [[] for i in xrange(csp_len)]
+       intersection = [[] for i in xrange(csp_len)]
+       for i in xrange(csp_len) :
+               subpath = csp[i]
+               subpath_offset = []
+               last_offset_len = 0
+               for sp1,sp2 in zip(subpath, subpath[1:]) : 
+                       segment_offset = csp_offset_segment(sp1,sp2,r)
+                       if subpath_offset == [] :
+                               subpath_offset = segment_offset
+                               
+                               prev_l = len(subpath_offset)
+                       else : 
+                               prev, arc, next = csp_join_offsets(subpath_offset[-prev_l:],segment_offset,sp1,sp2,sp1_l,sp2_l,r)
+                               #csp_draw([prev],"Blue")
+                               #csp_draw([arc],"Magenta")
+                               subpath_offset = csp_concat_subpaths(subpath_offset[:-prev_l+1],prev,arc,next)
+                               prev_l = len(next)                              
+                       sp1_l, sp2_l = sp1[:], sp2[:]
+                                               
+               # Join last and first offsets togother to close the curve
+               
+               prev, arc, next = csp_join_offsets(subpath_offset[-prev_l:], subpath_offset[:2], subpath[0], subpath[1], sp1_l,sp2_l, r)
+               subpath_offset[:2] = next[:]
+               subpath_offset = csp_concat_subpaths(subpath_offset[:-prev_l+1],prev,arc)
+               #csp_draw([prev],"Blue")
+               #csp_draw([arc],"Red")
+               #csp_draw([next],"Red")
+
+               # Collect subpath's offset and save it to unclipped offset list.        
+               unclipped_offset[i] = subpath_offset[:] 
+
+               #for k,t in intersection[i]:
+               #       draw_pointer(csp_at_t(subpath_offset[k-1], subpath_offset[k], t))
+                       
+       #inkex.etree.SubElement( options.doc_root, inkex.addNS('path','svg'), {"d": cubicsuperpath.formatPath(unclipped_offset), "style":"fill:none;stroke:#0f0;"} )    
+       print_("Offsetted path in %s"%(time.time()-time_))
+       time_ = time.time()
+       
+       #for i in range(len(unclipped_offset)):
+       #       csp_draw([unclipped_offset[i]], color = ["Green","Red","Blue"][i%3], width = .1)
+       #return []
+       ############################################################################
+       # Now to the clipping. 
+       ############################################################################
+       # First of all find all intersection's between all segments of all offseted subpaths, including self intersections.
+
+       #TODO define offset tolerance here
+       global small_tolerance
+       small_tolerance = 0.01
+       summ = 0
+       summ1 = 0        
+       for subpath_i in xrange(csp_len) :
+               for subpath_j in xrange(subpath_i,csp_len) :
+                       subpath = unclipped_offset[subpath_i]
+                       subpath1 = unclipped_offset[subpath_j]
+                       for i in xrange(1,len(subpath)) :
+                               # If subpath_i==subpath_j we are looking for self intersections, so 
+                               # we'll need search intersections only for xrange(i,len(subpath1))
+                               for j in ( xrange(i,len(subpath1)) if subpath_i==subpath_j else xrange(len(subpath1))) :
+                                       if subpath_i==subpath_j and j==i :
+                                               # Find self intersections of a segment
+                                               sp1,sp2,sp3 = csp_split(subpath[i-1],subpath[i],.5)
+                                               intersections = csp_segments_intersection(sp1,sp2,sp2,sp3)
+                                               summ +=1 
+                                               for t in intersections :
+                                                       summ1 += 1
+                                                       if not ( small(t[0]-1) and small(t[1]) ) and 0<=t[0]<=1 and 0<=t[1]<=1 :
+                                                               intersection[subpath_i] += [ [i,t[0]/2],[j,t[1]/2+.5] ]
+                                       else :
+                                               intersections = csp_segments_intersection(subpath[i-1],subpath[i],subpath1[j-1],subpath1[j])
+                                               summ +=1 
+                                               for t in intersections :
+                                                       summ1 += 1
+                                                       #TODO tolerance dependence to cpsp_length(t)
+                                                       if len(t) == 2 and 0<=t[0]<=1 and 0<=t[1]<=1 and not (
+                                                                       subpath_i==subpath_j and (
+                                                                       (j-i-1) % (len(subpath)-1) == 0 and small(t[0]-1) and small(t[1]) or 
+                                                                       (i-j-1) % (len(subpath)-1) == 0 and small(t[1]-1) and small(t[0]) )  ) :
+                                                               intersection[subpath_i] += [ [i,t[0]] ]
+                                                               intersection[subpath_j] += [ [j,t[1]] ] 
+                                                               #draw_pointer(csp_at_t(subpath[i-1],subpath[i],t[0]),"#f00")
+                                                               #print_(t)
+                                                               #print_(i,j)
+                                                       elif len(t)==5 and t[4]=="Overlap":     
+                                                               intersection[subpath_i] += [ [i,t[0]], [i,t[1]] ]
+                                                               intersection[subpath_j] += [ [j,t[1]], [j,t[3]] ]
+
+       print_("Intersections found in %s"%(time.time()-time_))
+       print_("Examined %s segments"%(summ))
+       print_("found %s intersections"%(summ1))
+       time_ = time.time()
+                                                               
+       ########################################################################
+       # Split unclipped offset by intersection points into splitted_offset
+       ########################################################################
+       splitted_offset = []
+       for i in xrange(csp_len) :
+               subpath = unclipped_offset[i]
+               if len(intersection[i]) > 0 :
+                       parts = csp_subpath_split_by_points(subpath, intersection[i])
+                       # Close parts list to close path (The first and the last parts are joined together)             
+                       if  [1,0.] not in intersection[i] : 
+                               parts[0][0][0] = parts[-1][-1][0]
+                               parts[0] = csp_concat_subpaths(parts[-1], parts[0])
+                               splitted_offset += parts[:-1]
+                       else: 
+                               splitted_offset += parts[:]
+               else :
+                       splitted_offset += [subpath[:]]
+       
+       #for i in range(len(splitted_offset)):
+       #       csp_draw([splitted_offset[i]], color = ["Green","Red","Blue"][i%3])
+       print_("Splitted in %s"%(time.time()-time_))
+       time_ = time.time()
+
+       
+       ########################################################################
+       # Clipping
+       ########################################################################                
+       result = []
+       for subpath_i in range(len(splitted_offset)):
+               clip = False
+               s1 = splitted_offset[subpath_i]
+               for subpath_j in range(len(splitted_offset)):
+                       s2 = splitted_offset[subpath_j]
+                       if (P(s1[0][1])-P(s2[-1][1])).l2()<0.0001 and ( (subpath_i+1) % len(splitted_offset) != subpath_j ): 
+                               if dot(csp_normalized_normal(s2[-2],s2[-1],1.),csp_normalized_slope(s1[0],s1[1],0.))*r<-0.0001 :
+                                       clip = True
+                                       break
+                       if (P(s2[0][1])-P(s1[-1][1])).l2()<0.0001 and ( (subpath_j+1) % len(splitted_offset) != subpath_i ):
+                               if dot(csp_normalized_normal(s2[0],s2[1],0.),csp_normalized_slope(s1[-2],s1[-1],1.))*r>0.0001 :
+                                       clip = True
+                                       break
+                       
+               if not clip :
+                       result += [s1[:]]
+               elif options.offset_draw_clippend_path :
+                       csp_draw([s1],color="Red",width=.1)
+                       draw_pointer( csp_at_t(s2[-2],s2[-1],1.)+
+                               (P(csp_at_t(s2[-2],s2[-1],1.))+ P(csp_normalized_normal(s2[-2],s2[-1],1.))*10).to_list(),"Green", "line"  )
+                       draw_pointer( csp_at_t(s1[0],s1[1],0.)+
+                               (P(csp_at_t(s1[0],s1[1],0.))+ P(csp_normalized_slope(s1[0],s1[1],0.))*10).to_list(),"Red", "line"  )
+                               
+       # Now join all together and check closure and orientation of result
+       joined_result = csp_join_subpaths(result)
+       # Check if each subpath from joined_result is closed
+       #csp_draw(joined_result,color="Green",width=1)
+
+       
+       for s in joined_result[:] :
+               if csp_subpaths_end_to_start_distance2(s,s) > 0.001 :
+                       # Remove open parts
+                       if options.offset_draw_clippend_path:
+                               csp_draw([s],color="Orange",width=1)
+                               draw_pointer(s[0][1], comment= csp_subpaths_end_to_start_distance2(s,s))
+                               draw_pointer(s[-1][1], comment = csp_subpaths_end_to_start_distance2(s,s))
+                       joined_result.remove(s)
+               else :                  
+                       # Remove small parts
+                       minx,miny,maxx,maxy = csp_true_bounds([s])
+                       if (minx[0]-maxx[0])**2 + (miny[1]-maxy[1])**2 < 0.1 :
+                               joined_result.remove(s)
+       print_("Clipped and joined path in %s"%(time.time()-time_))
+       time_ = time.time()
+                       
+       ########################################################################
+       # Now to the Dummy cliping: remove parts from splitted offset if their 
+       # centers are  closer to the original path than offset radius. 
+       ########################################################################                
+       
+       r1,r2 = ( (0.99*r)**2, (1.01*r)**2 ) if abs(r*.01)<1 else  ((abs(r)-1)**2, (abs(r)+1)**2)
+       for s in joined_result[:]:
+               dist = csp_to_point_distance(original_csp, s[int(len(s)/2)][1], dist_bounds = [r1,r2], tolerance = .000001)
+               if not r1 < dist[0] < r2 : 
+                       joined_result.remove(s)
+                       if options.offset_draw_clippend_path:
+                               csp_draw([s], comment = math.sqrt(dist[0]))
+                               draw_pointer(csp_at_t(csp[dist[1]][dist[2]-1],csp[dist[1]][dist[2]],dist[3])+s[int(len(s)/2)][1],"blue", "line", comment = [math.sqrt(dist[0]),i,j,sp]  )
+
+       print_("-----------------------------")
+       print_("Total offset time %s"%(time.time()-time_start))
+       print_()
+       return joined_result
+       
+       
+               
+
+
+################################################################################
+###
+###            Biarc function
+###
+###            Calculates biarc approximation of cubic super path segment
+###            splits segment if needed or approximates it with straight line
+###
+################################################################################
+def biarc(sp1, sp2, z1, z2, depth=0):
+       def biarc_split(sp1,sp2, z1, z2, depth): 
+               if depth<options.biarc_max_split_depth:
+                       sp1,sp2,sp3 = csp_split(sp1,sp2)
+                       l1, l2 = cspseglength(sp1,sp2), cspseglength(sp2,sp3)
+                       if l1+l2 == 0 : zm = z1
+                       else : zm = z1+(z2-z1)*l1/(l1+l2)
+                       return biarc(sp1,sp2,z1,zm,depth+1)+biarc(sp2,sp3,zm,z2,depth+1)
+               else: return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]
+
+       P0, P4 = P(sp1[1]), P(sp2[1])
+       TS, TE, v = (P(sp1[2])-P0), -(P(sp2[0])-P4), P0 - P4
+       tsa, tea, va = TS.angle(), TE.angle(), v.angle()
+       if TE.mag()<straight_distance_tolerance and TS.mag()<straight_distance_tolerance:       
+               # Both tangents are zerro - line straight
+               return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]
+       if TE.mag() < straight_distance_tolerance:
+               TE = -(TS+v).unit()
+               r = TS.mag()/v.mag()*2
+       elif TS.mag() < straight_distance_tolerance:
+               TS = -(TE+v).unit()
+               r = 1/( TE.mag()/v.mag()*2 )
+       else:   
+               r=TS.mag()/TE.mag()
+       TS, TE = TS.unit(), TE.unit()
+       tang_are_parallel = ((tsa-tea)%math.pi<straight_tolerance or math.pi-(tsa-tea)%math.pi<straight_tolerance )
+       if ( tang_are_parallel  and 
+                               ((v.mag()<straight_distance_tolerance or TE.mag()<straight_distance_tolerance or TS.mag()<straight_distance_tolerance) or
+                                       1-abs(TS*v/(TS.mag()*v.mag()))<straight_tolerance)      ):
+                               # Both tangents are parallel and start and end are the same - line straight
+                               # or one of tangents still smaller then tollerance
+
+                               # Both tangents and v are parallel - line straight
+               return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]
+
+       c,b,a = v*v, 2*v*(r*TS+TE), 2*r*(TS*TE-1)
+       if v.mag()==0:
+               return biarc_split(sp1, sp2, z1, z2, depth)
+       asmall, bsmall, csmall = abs(a)<10**-10,abs(b)<10**-10,abs(c)<10**-10 
+       if              asmall and b!=0:        beta = -c/b
+       elif    csmall and a!=0:        beta = -b/a 
+       elif not asmall:         
+               discr = b*b-4*a*c
+               if discr < 0:   raise ValueError, (a,b,c,discr)
+               disq = discr**.5
+               beta1 = (-b - disq) / 2 / a
+               beta2 = (-b + disq) / 2 / a
+               if beta1*beta2 > 0 :    raise ValueError, (a,b,c,disq,beta1,beta2)
+               beta = max(beta1, beta2)
+       elif    asmall and bsmall:      
+               return biarc_split(sp1, sp2, z1, z2, depth)
+       alpha = beta * r
+       ab = alpha + beta 
+       P1 = P0 + alpha * TS
+       P3 = P4 - beta * TE
+       P2 = (beta / ab)  * P1 + (alpha / ab) * P3
+
+
+       def calculate_arc_params(P0,P1,P2):
+               D = (P0+P2)/2
+               if (D-P1).mag()==0: return None, None
+               R = D - ( (D-P0).mag()**2/(D-P1).mag() )*(P1-D).unit()
+               p0a, p1a, p2a = (P0-R).angle()%(2*math.pi), (P1-R).angle()%(2*math.pi), (P2-R).angle()%(2*math.pi)
+               alpha =  (p2a - p0a) % (2*math.pi)                                      
+               if (p0a<p2a and  (p1a<p0a or p2a<p1a))  or      (p2a<p1a<p0a) : 
+                       alpha = -2*math.pi+alpha 
+               if abs(R.x)>1000000 or abs(R.y)>1000000  or (R-P0).mag<options.min_arc_radius :
+                       return None, None
+               else :  
+                       return  R, alpha
+       R1,a1 = calculate_arc_params(P0,P1,P2)
+       R2,a2 = calculate_arc_params(P2,P3,P4)
+       if R1==None or R2==None or (R1-P0).mag()<straight_tolerance or (R2-P2).mag()<straight_tolerance : return [ [sp1[1],'line', 0, 0, sp2[1], [z1,z2]] ]
+       
+       d = csp_to_arc_distance(sp1,sp2, [P0,P2,R1,a1],[P2,P4,R2,a2])
+       if d > options.biarc_tolerance and depth<options.biarc_max_split_depth   : return biarc_split(sp1, sp2, z1, z2, depth)
+       else:
+               if R2.mag()*a2 == 0 : zm = z2
+               else : zm  = z1 + (z2-z1)*(abs(R1.mag()*a1))/(abs(R2.mag()*a2)+abs(R1.mag()*a1)) 
+               return [        [ sp1[1], 'arc', [R1.x,R1.y], a1, [P2.x,P2.y], [z1,zm] ], [ [P2.x,P2.y], 'arc', [R2.x,R2.y], a2, [P4.x,P4.y], [zm,z2] ]         ]
+
+
+def biarc_curve_segment_length(seg):
+       if seg[1] == "arc" :
+               return math.sqrt((seg[0][0]-seg[2][0])**2+(seg[0][1]-seg[2][1])**2)*seg[3]
+       elif seg[1] == "line" : 
+               return math.sqrt((seg[0][0]-seg[4][0])**2+(seg[0][1]-seg[4][1])**2)
+       else: 
+               return 0        
+
+
+def biarc_curve_clip_at_l(curve, l, clip_type = "strict") :
+       # get first subcurve and ceck it's length  
+       subcurve, subcurve_l, moved = [], 0, False
+       for seg in curve:
+               if seg[1] == "move" and moved or seg[1] == "end" :      
+                       break
+               if seg[1] == "move" : moved = True 
+               subcurve_l += biarc_curve_segment_length(seg)
+               if seg[1] == "arc" or seg[1] == "line" : 
+                       subcurve += [seg]
+
+       if subcurve_l < l and clip_type == "strict" : return [] 
+       lc = 0
+       if (subcurve[-1][4][0]-subcurve[0][0][0])**2 + (subcurve[-1][4][1]-subcurve[0][0][1])**2 < 10**-7 : subcurve_closed = True
+       i = 0
+       reverse = False
+       while lc<l :
+               seg = subcurve[i]
+               if reverse :  
+                       if seg[1] == "line" :
+                               seg = [seg[4], "line", 0 , 0, seg[0], seg[5]] # Hmmm... Do we have to swap seg[5][0] and seg[5][1] (zstart and zend) or not?
+                       elif seg[1] == "arc" :
+                               seg = [seg[4], "arc", seg[2] , -seg[3], seg[0], seg[5]] # Hmmm... Do we have to swap seg[5][0] and seg[5][1] (zstart and zend) or not?
+               ls = biarc_curve_segment_length(seg)
+               if ls != 0 :
+                       if l-lc>ls :
+                               res += [seg]
+                       else :
+                               if seg[1] == "arc" :
+                                       r  = math.sqrt((seg[0][0]-seg[2][0])**2+(seg[0][1]-seg[2][1])**2)
+                                       x,y = seg[0][0]-seg[2][0], seg[0][1]-seg[2][1]
+                                       a = seg[3]/ls*(l-lc)
+                                       x,y = x*math.cos(a) - y*math.sin(a),  x*math.sin(a) + y*math.cos(a)
+                                       x,y = x+seg[2][0], y+seg[2][1]
+                                       res += [[ seg[0], "arc",  seg[2], a, [x,y], [seg[5][0],seg[5][1]/ls*(l-lc)]  ]]
+                               if seg[1] == "line" :
+                                       res += [[ seg[0], "line",  0, 0, [(seg[4][0]-seg[0][0])/ls*(l-lc),(seg[4][1]-seg[0][1])/ls*(l-lc)], [seg[5][0],seg[5][1]/ls*(l-lc)]  ]]
+               i += 1 
+               if i >= len(subcurve) and not subcurve_closed: 
+                       reverse = not reverse
+               i = i%len(subcurve)
+       return res      
+
+       
+       
+class Postprocessor():
+       def __init__(self, error_function_handler):     
+               self.error = error_function_handler 
+               self.functions = {
+                                       "remap"         : self.remap,
+                                       "remapi"        : self.remapi ,
+                                       "scale"         : self.scale,
+                                       "move"          : self.move,
+                                       "flip"          : self.flip_axis,
+                                       "flip_axis"     : self.flip_axis,
+                                       "round"         : self.round_coordinates,
+                                       "parameterize"          : self.parameterize,
+                                       }
+       
+                       
+       def process(self,command):
+               command = re.sub(r"\\\\",":#:#:slash:#:#:",command)
+               command = re.sub(r"\\;",":#:#:semicolon:#:#:",command)
+               command = command.split(";")
+               for s in command: 
+                       s = re.sub(":#:#:slash:#:#:","\\\\",s)
+                       s = re.sub(":#:#:semicolon:#:#:","\\;",s)
+                       s = s.strip()
+                       if s!="" :
+                               self.parse_command(s)           
+                       
+       
+       def parse_command(self,command):
+               r = re.match(r"([A-Za-z0-9_]+)\s*\(\s*(.*)\)",command)
+               if not r:
+                       self.error("Parse error while postprocessing.\n(Command: '%s')"%(command), "error")
+               function, parameters = r.group(1).lower(),r.group(2)
+               if function in self.functions :
+                       print_("Postprocessor: executing function %s(%s)"%(function,parameters))
+                       self.functions[function](parameters)
+               else : 
+                       self.error("Unrecognized function '%s' while postprocessing.\n(Command: '%s')"%(function,command), "error")
+       
+       
+       def re_sub_on_gcode_lines(self, pattern,replacemant):
+               gcode = self.gcode.split("\n")
+               self.gcode = ""
+               for i in range(len(gcode)) :
+                       self.gcode += re.sub(pattern,replacement,gcode[i])
+               
+       
+       def remapi(self,parameters):
+               self.remap(parameters, case_sensitive = True)
+       
+       
+       def remap(self,parameters, case_sensitive = False):
+               # remap parameters should be like "x->y,y->x"
+               parameters = parameters.replace("\,",":#:#:coma:#:#:")
+               parameters = parameters.split(",")
+               pattern, remap = [], []
+               for s in parameters:
+                       s = s.replace(":#:#:coma:#:#:","\,")
+                       r = re.match("""\s*(\'|\")(.*)\\1\s*->\s*(\'|\")(.*)\\3\s*""",s)
+                       if not r :
+                               self.error("Bad parameters for remap.\n(Parameters: '%s')"%(parameters), "error")
+                       pattern +=[r.group(2)]  
+                       remap +=[r.group(4)]    
+               
+               
+               
+               for i in range(len(pattern)) :
+                       if case_sensitive :
+                               self.gcode = ireplace(self.gcode, pattern[i], ":#:#:remap_pattern%s:#:#:"%i )
+                       else :
+                               self.gcode = self.gcode.replace(pattern[i], ":#:#:remap_pattern%s:#:#:"%i)
+                       
+               for i in range(len(remap)) :
+                       self.gcode = self.gcode.replace(":#:#:remap_pattern%s:#:#:"%i, remap[i])
+       
+       
+       def transform(self, move, scale):
+               axis = ["xi","yj","zk","a"]
+               flip = scale[0]*scale[1]*scale[2] < 0 
+               gcode = ""
+               warned = []
+               r_scale = scale[0]
+               plane = "g17"
+               for s in self.gcode.split("\n"):
+                       # get plane selection: 
+                       s_wo_comments = re.sub(r"\([^\)]*\)","",s)
+                       r = re.search(r"(?i)(G17|G18|G19)", s_wo_comments)
+                       if r :
+                               plane = r.group(1).lower()
+                               if plane == "g17" : r_scale = scale[0] # plane XY -> scale x
+                               if plane == "g18" : r_scale = scale[0] # plane XZ -> scale x
+                               if plane == "g19" : r_scale = scale[1] # plane YZ -> scale y
+                       # Raise warning if scale factors are not the game for G02 and G03       
+                       if plane not in warned:
+                               r = re.search(r"(?i)(G02|G03)", s_wo_comments)
+                               if r :
+                                       if plane == "g17" and scale[0]!=scale[1]: self.error("Post-processor: Scale factors for X and Y axis are not the same. G02 and G03 codes will be corrupted.","warning") 
+                                       if plane == "g18" and scale[0]!=scale[2]: self.error("Post-processor: Scale factors for X and Z axis are not the same. G02 and G03 codes will be corrupted.","warning") 
+                                       if plane == "g19" and scale[1]!=scale[2]: self.error("Post-processor: Scale factors for Y and Z axis are not the same. G02 and G03 codes will be corrupted.","warning") 
+                                       warned += [plane]
+                       # Transform             
+                       for i in range(len(axis)) :
+                               if move[i] != 0 or scale[i] != 1:
+                                       for a in axis[i] :
+                                               r = re.search(r"(?i)("+a+r")\s*(-?)\s*(\d*\.?\d*)", s)
+                                               if r and r.group(3)!="":
+                                                       s = re.sub(r"(?i)("+a+r")\s*(-?)\s*(\d*\.?\d*)", r"\1 %f"%(float(r.group(2)+r.group(3))*scale[i]+(move[i] if a not in ["i","j","k"] else 0) ), s)
+                       #scale radius R
+                       if r_scale != 1 :
+                               r = re.search(r"(?i)(r)\s*(-?\s*(\d*\.?\d*))", s)
+                               if r and r.group(3)!="":
+                                       try:
+                                               s = re.sub(r"(?i)(r)\s*(-?)\s*(\d*\.?\d*)", r"\1 %f"%( float(r.group(2)+r.group(3))*r_scale ), s)
+                                       except:
+                                               pass    
+
+                       gcode += s + "\n"
+                       
+               self.gcode = gcode
+               if flip : 
+                       self.remapi("'G02'->'G03', 'G03'->'G02'")
+
+
+       def parameterize(self,parameters) :
+               planes = []
+               feeds = {}
+               coords = []
+               gcode = ""
+               coords_def = {"x":"x","y":"y","z":"z","i":"x","j":"y","k":"z","a":"a"}
+               for s in self.gcode.split("\n"):
+                       s_wo_comments = re.sub(r"\([^\)]*\)","",s)
+                       # get Planes
+                       r = re.search(r"(?i)(G17|G18|G19)", s_wo_comments)
+                       if r :
+                               plane = r.group(1).lower()
+                               if plane not in planes : 
+                                       planes += [plane]
+                       # get Feeds
+                       r = re.search(r"(?i)(F)\s*(-?)\s*(\d*\.?\d*)", s_wo_comments)
+                       if r :
+                               feed  = float (r.group(2)+r.group(3))
+                               if feed not in feeds :
+                                       feeds[feed] = "#"+str(len(feeds)+20)
+                                       
+                       #Coordinates
+                       for c in "xyzijka" :
+                               r = re.search(r"(?i)("+c+r")\s*(-?)\s*(\d*\.?\d*)", s_wo_comments)
+                               if r :
+                                       c = coords_def[r.group(1).lower()]
+                                       if c not in coords :
+                                               coords += [c]
+               # Add offset parametrization
+               offset = {"x":"#6","y":"#7","z":"#8","a":"#9"}
+               for c in coords:
+                       gcode += "%s  = 0 (%s axis offset)\n" %  (offset[c],c.upper())
+                       
+               # Add scale parametrization
+               if planes == [] : planes = ["g17"]
+               if len(planes)>1 :  # have G02 and G03 in several planes scale_x = scale_y = scale_z required
+                       gcode += "#10 = 1 (Scale factor)\n"
+                       scale = {"x":"#10","i":"#10","y":"#10","j":"#10","z":"#10","k":"#10","r":"#10"}
+               else :
+                       gcode += "#10 = 1 (%s Scale factor)\n" % ({"g17":"XY","g18":"XZ","g19":"YZ"}[planes[0]])
+                       gcode += "#11 = 1 (%s Scale factor)\n" % ({"g17":"Z","g18":"Y","g19":"X"}[planes[0]])
+                       scale = {"x":"#10","i":"#10","y":"#10","j":"#10","z":"#10","k":"#10","r":"#10"}
+                       if "g17" in planes :
+                               scale["z"] = "#11"                              
+                               scale["k"] = "#11"                              
+                       if "g18" in planes :
+                               scale["y"] = "#11"                              
+                               scale["j"] = "#11"                              
+                       if "g19" in planes :
+                               scale["x"] = "#11"                              
+                               scale["i"] = "#11"                              
+               # Add a scale 
+               if "a" in coords:
+                       gcode += "#12  = 1 (A axis scale)\n" 
+                       scale["a"] = "#12"
+               
+               # Add feed parametrization 
+               for f in feeds :
+                       gcode += "%s = %f (Feed definition)\n" % (feeds[f],f)
+
+               # Parameterize Gcode            
+               for s in self.gcode.split("\n"):
+                       #feed replace :
+                       r = re.search(r"(?i)(F)\s*(-?)\s*(\d*\.?\d*)", s)
+                       if r and len(r.group(3))>0:
+                               s = re.sub(r"(?i)(F)\s*(-?)\s*(\d*\.?\d*)", "F [%s]"%feeds[float(r.group(2)+r.group(3))], s)
+                       #Coords XYZA replace
+                       for c in "xyza" :
+                               r = re.search(r"(?i)(("+c+r")\s*(-?)\s*(\d*\.?\d*))", s)
+                               if r and len(r.group(4))>0:
+                                       s = re.sub(r"(?i)("+c+r")\s*((-?)\s*(\d*\.?\d*))", r"\1[\2*%s+%s]"%(scale[c],offset[c]), s)
+
+                       #Coords IJKR replace
+                       for c in "ijkr" :
+                               r = re.search(r"(?i)(("+c+r")\s*(-?)\s*(\d*\.?\d*))", s)
+                               if r and len(r.group(4))>0:
+                                       s = re.sub(r"(?i)("+c+r")\s*((-?)\s*(\d*\.?\d*))", r"\1[\2*%s]"%scale[c], s)
+
+                       gcode += s + "\n"
+       
+               self.gcode = gcode      
+
+       
+       def round_coordinates(self,parameters) :
+               try: 
+                       round_ = int(parameters)
+               except :        
+                       self.error("Bad parameters for round. Round should be an integer! \n(Parameters: '%s')"%(parameters), "error")          
+               gcode = ""
+               for s in self.gcode.split("\n"):
+                       for a in "xyzijkaf" :
+                               r = re.search(r"(?i)("+a+r")\s*(-?\s*(\d*\.?\d*))", s)
+                               if r : 
+                                       
+                                       if r.group(2)!="":
+                                               s = re.sub(
+                                                                       r"(?i)("+a+r")\s*(-?)\s*(\d*\.?\d*)", 
+                                                                       (r"\1 %0."+str(round_)+"f" if round_>0 else r"\1 %d")%round(float(r.group(2)),round_),
+                                                                       s)
+                       gcode += s + "\n"
+               self.gcode = gcode
+       
+
+       def scale(self, parameters):
+               parameters = parameters.split(",")
+               scale = [1.,1.,1.,1.]
+               try :
+                       for i in range(len(parameters)) :
+                               if float(parameters[i])==0 : 
+                                       self.error("Bad parameters for scale. Scale should not be 0 at any axis! \n(Parameters: '%s')"%(parameters), "error")           
+                               scale[i] = float(parameters[i])
+               except :
+                       self.error("Bad parameters for scale.\n(Parameters: '%s')"%(parameters), "error")
+               self.transform([0,0,0,0],scale)         
+
+       
+       def move(self, parameters):
+               parameters = parameters.split(",")
+               move = [0.,0.,0.,0.]
+               try :
+                       for i in range(len(parameters)) :
+                               move[i] = float(parameters[i])
+               except :
+                       self.error("Bad parameters for move.\n(Parameters: '%s')"%(parameters), "error")
+               self.transform(move,[1.,1.,1.,1.])              
+
+
+       def flip_axis(self, parameters):
+               parameters = parameters.lower()
+               axis = {"x":1.,"y":1.,"z":1.,"a":1.}    
+               for p in parameters: 
+                       if p in [","," ","      ","\r","'",'"'] : continue
+                       if p not in ["x","y","z","a"] : 
+                               self.error("Bad parameters for flip_axis. Parameter should be string consists of 'xyza' \n(Parameters: '%s')"%(parameters), "error")
+                       axis[p] = -axis[p]
+               self.scale("%f,%f,%f,%f"%(axis["x"],axis["y"],axis["z"],axis["a"]))     
+
+       
+                       
+################################################################################
+###            Polygon class
+################################################################################
+class Polygon:
+       def __init__(self, polygon=None):
+               self.polygon = [] if polygon==None else polygon[:]
+       
+       
+       def move(self, x, y) :
+               for i in range(len(self.polygon)) :
+                       for j in range(len(self.polygon[i])) :
+                               self.polygon[i][j][0] += x
+                               self.polygon[i][j][1] += y
+       
+       
+       def bounds(self) : 
+               minx,miny,maxx,maxy = 1e400, 1e400, -1e400, -1e400
+               for poly in self.polygon :
+                       for p in poly :
+                               if minx > p[0] : minx = p[0]
+                               if miny > p[1] : miny = p[1]
+                               if maxx < p[0] : maxx = p[0]
+                               if maxy < p[1] : maxy = p[1]
+               return minx*1,miny*1,maxx*1,maxy*1              
+       
+       
+       def width(self):
+               b = self.bounds()
+               return b[2]-b[0]
+       
+       
+       def rotate_(self,sin,cos) :
+               for i in range(len(self.polygon)) :
+                       for j in range(len(self.polygon[i])) :
+                               x,y = self.polygon[i][j][0], self.polygon[i][j][1] 
+                               self.polygon[i][j][0] = x*cos - y*sin
+                               self.polygon[i][j][1] = x*sin + y*cos
+               
+       
+       def rotate(self, a):
+               cos, sin = math.cos(a), math.sin(a)
+               self.rotate_(sin,cos)
+       
+                       
+       def drop_into_direction(self, direction, surface) :
+               # Polygon is a list of simple polygons
+               # Surface is a polygon + line y = 0 
+               # Direction is [dx,dy]  
+               if len(self.polygon) == 0 or len(self.polygon[0])==0 : return
+               if direction[0]**2 + direction[1]**2 <1e-10 : return
+               direction = normalize(direction)
+               sin,cos = direction[0], -direction[1]
+               self.rotate_(-sin,cos)
+               surface.rotate_(-sin,cos)
+               self.drop_down(surface, zerro_plane = False)
+               self.rotate_(sin,cos)
+               surface.rotate_(sin,cos)
+               
+                       
+       def centroid(self):
+               centroids = []
+               sa = 0
+               for poly in self.polygon:
+                       cx,cy,a = 0,0,0
+                       for i in range(len(poly)):
+                               [x1,y1],[x2,y2] = poly[i-1],poly[i]
+                               cx += (x1+x2)*(x1*y2-x2*y1)
+                               cy += (y1+y2)*(x1*y2-x2*y1)
+                               a  += (x1*y2-x2*y1)
+                       a *= 3.
+                       if abs(a)>0 :
+                               cx /= a
+                               cy /= a
+                               sa += abs(a)
+                               centroids += [ [cx,cy,a] ]
+               if sa == 0 : return     [0.,0.]
+               cx,cy = 0.,0.
+               for c in centroids :
+                       cx += c[0]*c[2]
+                       cy += c[1]*c[2]
+               cx /= sa
+               cy /= sa
+               return [cx,cy]
+
+                       
+       def drop_down(self, surface, zerro_plane = True) :
+               # Polygon is a list of simple polygons
+               # Surface is a polygon + line y = 0 
+               # Down means min y (0,-1)  
+               if len(self.polygon) == 0 or len(self.polygon[0])==0 : return
+               # Get surface top point
+               top = surface.bounds()[3]
+               if zerro_plane : top = max(0, top)
+               # Get polygon bottom point
+               bottom = self.bounds()[1]
+               self.move(0, top - bottom + 10)         
+               # Now get shortest distance from surface to polygon in positive x=0 direction
+               # Such distance = min(distance(vertex, edge)...)  where edge from surface and 
+               # vertex from polygon and vice versa...
+               dist = 1e300
+               for poly in surface.polygon :
+                       for i in range(len(poly)) :
+                               for poly1 in self.polygon :
+                                       for i1 in range(len(poly1)) :
+                                               st,end = poly[i-1], poly[i] 
+                                               vertex = poly1[i1]
+                                               if st[0]<=vertex[0]<= end[0] or end[0]<=vertex[0]<=st[0] :
+                                                       if st[0]==end[0] : d = min(vertex[1]-st[1],vertex[1]-end[1])
+                                                       else : d = vertex[1] - st[1] - (end[1]-st[1])*(vertex[0]-st[0])/(end[0]-st[0]) 
+                                                       if dist > d  : dist = d
+                                               # and vice versa just change the sign because vertex now under the edge
+                                               st,end = poly1[i1-1], poly1[i1] 
+                                               vertex = poly[i]
+                                               if st[0]<=vertex[0]<=end[0] or end[0]<=vertex[0]<=st[0] :
+                                                       if st[0]==end[0] : d = min(- vertex[1]+st[1],-vertex[1]+end[1])
+                                                       else : d =  - vertex[1] + st[1] + (end[1]-st[1])*(vertex[0]-st[0])/(end[0]-st[0]) 
+                                                       if dist > d  : dist = d
+               
+               if zerro_plane and dist > 10 + top : dist = 10 + top 
+               #print_(dist, top, bottom)
+               #self.draw()
+               self.move(0, -dist)             
+               
+                                       
+       def draw(self,color="#075",width=.1) :
+               for poly in self.polygon :
+                       csp_draw( [csp_subpath_line_to([],poly+[poly[0]])], color=color,width=width )
+                       
+       
+       def add(self, add) :
+               if type(add) == type([]) :
+                       self.polygon += add[:]
+               else :  
+                       self.polygon += add.polygon[:]
+
+       
+       def point_inside(self,p) :
+               inside = False
+               for poly in self.polygon :
+                       for i in range(len(poly)):
+                               st,end = poly[i-1], poly[i]
+                               if p==st or p==end : return True # point is a vertex = point is on the edge
+                               if st[0]>end[0] : st, end = end, st # This will be needed to check that edge if open only at rigth end
+                               c = (p[1]-st[1])*(end[0]-st[0])-(end[1]-st[1])*(p[0]-st[0])
+                               #print_(c)
+                               if st[0]<=p[0]<end[0] : 
+                                       if c<0 : 
+                                               inside = not inside
+                                       elif c == 0 : return True # point is on the edge
+                               elif st[0]==end[0]==p[0] and (st[1]<=p[1]<=end[1] or end[1]<=p[1]<=st[1]) : # point is on the edge
+                                       return True
+               return inside                   
+
+       
+       def hull(self) :
+               # Add vertices at all self intersection points. 
+               hull = []
+               for i1 in range(len(self.polygon)):
+                       poly1 = self.polygon[i1]
+                       poly_ = []
+                       for j1 in range(len(poly1)):
+                               s, e = poly1[j1-1],poly1[j1]
+                               poly_ += [s]
+                               
+                               # Check self intersections 
+                               for j2 in range(j1+1,len(poly1)):
+                                       s1, e1 = poly1[j2-1],poly1[j2]
+                                       int_ = line_line_intersection_points(s,e,s1,e1)
+                                       for p in int_ :
+                                               if point_to_point_d2(p,s)>0.000001 and point_to_point_d2(p,e)>0.000001 : 
+                                                       poly_ += [p]
+                               # Check self intersections with other polys  
+                               for i2 in range(len(self.polygon)):
+                                       if i1==i2 : continue
+                                       poly2 = self.polygon[i2]
+                                       for j2 in range(len(poly2)):
+                                               s1, e1 = poly2[j2-1],poly2[j2]
+                                               int_ = line_line_intersection_points(s,e,s1,e1)
+                                               for p in int_ :
+                                                       if point_to_point_d2(p,s)>0.000001 and point_to_point_d2(p,e)>0.000001 : 
+                                                               poly_ += [p]
+                       hull += [poly_]
+               # Create the dictionary containing all edges in both directions
+               edges = {}
+               for poly in self.polygon :
+                       for i in range(len(poly)):
+                               s,e = tuple(poly[i-1]), tuple(poly[i])
+                               if (point_to_point_d2(e,s)<0.000001) : continue
+                               break_s, break_e = False, False
+                               for p in edges :
+                                       if point_to_point_d2(p,s)<0.000001 : 
+                                               break_s = True
+                                               s = p
+                                       if point_to_point_d2(p,e)<0.000001 : 
+                                               break_e = True
+                                               e = p
+                                       if break_s and break_e : break
+                               l = point_to_point_d(s,e)
+                               if not break_s and not break_e : 
+                                       edges[s] = [ [s,e,l] ]
+                                       edges[e] = [ [e,s,l] ]
+                                       #draw_pointer(s+e,"red","line")
+                                       #draw_pointer(s+e,"red","line")
+                               else : 
+                                       if e in edges : 
+                                               for edge in edges[e] :  
+                                                       if point_to_point_d2(edge[1],s)<0.000001 :
+                                                               break
+                                               if point_to_point_d2(edge[1],s)>0.000001 :
+                                                       edges[e] += [ [e,s,l] ]
+                                                       #draw_pointer(s+e,"red","line")
+                                                       
+                                       else : 
+                                               edges[e] = [ [e,s,l] ]
+                                               #draw_pointer(s+e,"green","line")
+                                       if s in edges : 
+                                               for edge in edges[s] :  
+                                                       if  point_to_point_d2(edge[1],e)<0.000001 :
+                                                               break
+                                               if point_to_point_d2(edge[1],e)>0.000001 :
+                                                       edges[s] += [ [s,e, l] ]
+                                                       #draw_pointer(s+e,"red","line")
+                                       else : 
+                                               edges[s] = [ [s,e,l] ]
+                                               #draw_pointer(s+e,"green","line")
+
+               
+               def angle_quadrant(sin,cos):
+                       # quadrants are (0,pi/2], (pi/2,pi], (pi,3*pi/2], (3*pi/2, 2*pi], i.e. 0 is in the 4-th quadrant
+                       if sin>0 and cos>=0 : return 1
+                       if sin>=0 and cos<0 : return 2
+                       if sin<0 and cos<=0 : return 3
+                       if sin<=0 and cos>0 : return 4
+                       
+               
+               def angle_is_less(sin,cos,sin1,cos1):
+                       # 0 = 2*pi is the largest angle
+                       if [sin1, cos1] == [0,1] : return True
+                       if [sin, cos] == [0,1] : return False
+                       if angle_quadrant(sin,cos)>angle_quadrant(sin1,cos1) : 
+                               return False
+                       if angle_quadrant(sin,cos)<angle_quadrant(sin1,cos1) : 
+                               return True
+                       if sin>=0 and cos>0 : return sin<sin1
+                       if sin>0 and cos<=0 : return sin>sin1
+                       if sin<=0 and cos<0 : return sin>sin1
+                       if sin<0 and cos>=0 : return sin<sin1
+
+                       
+               def get_closes_edge_by_angle(edges, last):
+                       # Last edge is normalized vector of the last edge.
+                       min_angle = [0,1]
+                       next = last
+                       last_edge = [(last[0][0]-last[1][0])/last[2], (last[0][1]-last[1][1])/last[2]]
+                       for p in edges:
+                               #draw_pointer(list(p[0])+[p[0][0]+last_edge[0]*40,p[0][1]+last_edge[1]*40], "Red", "line", width=1)
+                               #print_("len(edges)=",len(edges))
+                               cur = [(p[1][0]-p[0][0])/p[2],(p[1][1]-p[0][1])/p[2]]
+                               cos, sin = dot(cur,last_edge),  cross(cur,last_edge)
+                               #draw_pointer(list(p[0])+[p[0][0]+cur[0]*40,p[0][1]+cur[1]*40], "Orange", "line", width=1, comment = [sin,cos])
+                               #print_("cos, sin=",cos,sin)
+                               #print_("min_angle_before=",min_angle)
+
+                               if      angle_is_less(sin,cos,min_angle[0],min_angle[1]) : 
+                                       min_angle = [sin,cos]
+                                       next = p
+                               #print_("min_angle=",min_angle)
+
+                       return next             
+                       
+               # Join edges together into new polygon cutting the vertexes inside new polygon
+               self.polygon = []
+               len_edges = sum([len(edges[p]) for p in edges]) 
+               loops = 0 
+               
+               while len(edges)>0 :
+                       poly = []
+                       if loops > len_edges  : raise ValueError, "Hull error"
+                       loops+=1
+                       # Find left most vertex.
+                       start = (1e100,1)
+                       for edge in edges : 
+                               start = min(start, min(edges[edge])) 
+                       last = [(start[0][0]-1,start[0][1]),start[0],1]
+                       first_run = True
+                       loops1 = 0
+                       while (last[1]!=start[0] or first_run) :        
+                               first_run = False
+                               if loops1 > len_edges  : raise ValueError, "Hull error"
+                               loops1 += 1
+                               next = get_closes_edge_by_angle(edges[last[1]],last)
+                               #draw_pointer(next[0]+next[1],"Green","line", comment=i, width= 1)
+                               #print_(next[0],"-",next[1]) 
+
+                               last = next
+                               poly += [ list(last[0]) ]                               
+                       self.polygon += [ poly ]
+                       # Remove all edges that are intersects new poly (any vertex inside new poly)
+                       poly_ = Polygon([poly])
+                       for p in edges.keys()[:] : 
+                               if poly_.point_inside(list(p)) : del edges[p]
+               self.draw(color="Green", width=1)       
+
+
+class Arangement_Genetic:
+       # gene = [fittness, order, rotation, xposition]
+       # spieces = [gene]*shapes count
+       # population = [spieces]
+       def __init__(self, polygons, material_width):
+               self.population = []
+               self.genes_count = len(polygons)
+               self.polygons = polygons
+               self.width = material_width
+               self.mutation_factor = 0.1
+               self.order_mutate_factor = 1.
+               self.move_mutate_factor = 1.
+
+       
+       def add_random_species(self,count):
+               for i in range(count):
+                       specimen = []
+                       order = range(self.genes_count)
+                       random.shuffle(order)
+                       for j in order:
+                               specimen += [ [j, random.random(), random.random()] ]
+                       self.population += [ [None,specimen] ]
+
+       
+       def species_distance2(self,sp1,sp2) :
+               # retun distance, each component is normalized
+               s = 0
+               for j in range(self.genes_count) :
+                       s += ((sp1[j][0]-sp2[j][0])/self.genes_count)**2 + (( sp1[j][1]-sp2[j][1]))**2 + ((sp1[j][2]-sp2[j][2]))**2
+               return s
+
+       
+       def similarity(self,sp1,top) :
+               # Define similarity as a simple distance between two points in len(gene)*len(spiece) -th dimentions
+               # for sp2 in top_spieces sum(|sp1-sp2|)/top_count
+               sim = 0
+               for sp2 in top : 
+                       sim += math.sqrt(species_distance2(sp1,sp2[1]))
+               return sim/len(top)
+               
+       
+       def leave_top_species(self,count):
+               self.population.sort()
+               res = [  copy.deepcopy(self.population[0]) ]
+               del self.population[0]
+               for i in range(count-1) :
+                       t = []
+                       for j in range(20) : 
+                               i1 = random.randint(0,len(self.population)-1) 
+                               t += [ [self.population[i1][0],i1] ] 
+                       t.sort()
+                       res += [  copy.deepcopy(self.population[t[0][1]]) ]
+                       del self.population[t[0][1]]
+               self.population = res           
+               #del self.population[0]
+               #for c in range(count-1) :
+               #       rank = []
+               #       for i in range(len(self.population)) :  
+               #               sim = self.similarity(self.population[i][1],res)
+               #               rank += [ [self.population[i][0] / sim if sim>0 else 1e100,i] ]
+               #       rank.sort()
+               #       res += [  copy.deepcopy(self.population[rank[0][1]]) ]
+               #       print_(rank[0],self.population[rank[0][1]][0])
+               #       print_(res[-1])
+               #       del self.population[rank[0][1]]
+                       
+               self.population = res
+                       
+                       
+       def populate_species(self,count, parent_count):
+               self.population.sort()
+               self.inc = 0
+               for c in range(count):
+                       parent1 = random.randint(0,parent_count-1)
+                       parent2 = random.randint(0,parent_count-1)
+                       if parent1==parent2 : parent2 = (parent2+1) % parent_count
+                       parent1, parent2 = self.population[parent1][1], self.population[parent2][1]
+                       i1,i2 = 0, 0
+                       genes_order = []
+                       specimen = [ [0,0.,0.] for i in range(self.genes_count) ]
+                       
+                       self.incest_mutation_multiplyer = 1.
+                       self.incest_mutation_count_multiplyer = 1.
+
+                       if self.species_distance2(parent1, parent2) <= .01/self.genes_count :
+                               # OMG it's a incest :O!!!
+                               # Damn you bastards!
+                               self.inc +=1
+                               self.incest_mutation_multiplyer = 2. 
+                               self.incest_mutation_count_multiplyer = 2. 
+                       else :
+                               if random.random()<.01 : print_(self.species_distance2(parent1, parent2))       
+                       start_gene = random.randint(0,self.genes_count)
+                       end_gene = (max(1,random.randint(0,self.genes_count),int(self.genes_count/4))+start_gene) % self.genes_count
+                       if end_gene<start_gene : 
+                               end_gene, start_gene = start_gene, end_gene
+                               parent1, parent2 = parent2, parent1
+                       for i in range(start_gene,end_gene) : 
+                               #rotation_mutate_param = random.random()/100
+                               #xposition_mutate_param = random.random()/100
+                               tr = 1. #- rotation_mutate_param
+                               tp = 1. #- xposition_mutate_param
+                               specimen[i] = [parent1[i][0], parent1[i][1]*tr+parent2[i][1]*(1-tr),parent1[i][2]*tp+parent2[i][2]*(1-tp)]
+                               genes_order += [ parent1[i][0] ]
+
+                       for i in range(0,start_gene)+range(end_gene,self.genes_count) : 
+                               tr = 0. #rotation_mutate_param
+                               tp = 0. #xposition_mutate_param
+                               j = i 
+                               while parent2[j][0] in genes_order :
+                                       j = (j+1)%self.genes_count
+                               specimen[i] = [parent2[j][0], parent1[i][1]*tr+parent2[i][1]*(1-tr),parent1[i][2]*tp+parent2[i][2]*(1-tp)]
+                               genes_order += [ parent2[j][0] ]                                                
+                               
+
+                       for i in range(random.randint(self.mutation_genes_count[0],self.mutation_genes_count[0]*self.incest_mutation_count_multiplyer )) :
+                               if random.random() < self.order_mutate_factor * self.incest_mutation_multiplyer : 
+                                       i1,i2 = random.randint(0,self.genes_count-1),random.randint(0,self.genes_count-1)
+                                       specimen[i1][0], specimen[i2][0] = specimen[i2][0], specimen[i1][0]
+                               if random.random() < self.move_mutation_factor * self.incest_mutation_multiplyer: 
+                                       i1 = random.randint(0,self.genes_count-1)
+                                       specimen[i1][1] =  (specimen[i1][1]+random.random()*math.pi2*self.move_mutation_multiplier)%1.
+                                       specimen[i1][2] =  (specimen[i1][2]+random.random()*self.move_mutation_multiplier)%1.
+                       self.population += [ [None,specimen] ]  
+
+       
+       def test_spiece_drop_down(self,spiece) : 
+               surface = Polygon()
+               for p in spiece :
+                       time_ = time.time()
+                       poly = Polygon(copy.deepcopy(self.polygons[p[0]].polygon))
+                       poly.rotate(p[1]*math.pi2)
+                       w = poly.width()
+                       left = poly.bounds()[0]
+                       poly.move( -left + (self.width-w)*p[2],0)
+                       poly.drop_down(surface)
+                       surface.add(poly)
+               return surface
+
+       
+       def test(self,test_function): 
+               for i in range(len(self.population)) :
+                       if self.population[i][0] == None :
+                               surface = test_function(self.population[i][1])
+                               b = surface.bounds()
+                               self.population[i][0] = (b[3]-b[1])*(b[2]-b[0])
+               self.population.sort()                          
+
+
+       def test_spiece_centroid(self,spiece) : 
+               poly = Polygon(copy.deepcopy(self.polygons[spiece[0][0]].polygon))
+               poly.rotate(spiece[0][2]*math.pi2)
+               surface  = Polygon(poly.polygon)
+               i = 0
+               for p in spiece[1:] :
+                       i += 1
+                       poly = Polygon(copy.deepcopy(self.polygons[p[0]].polygon))
+                       poly.rotate(p[2]*math.pi2)
+                       c = surface.centroid()
+                       c1 = poly.centroid()
+                       direction = [math.cos(p[1]*math.pi2), -math.sin(p[1]*math.pi2)]
+                       poly.move(c[0]-c1[0]-direction[0]*100,c[1]-c1[1]-direction[1]*100)
+                       poly.drop_into_direction(direction,surface)
+                       surface.add(poly)
+               return surface
+               
+               
+               
+               #surface.draw()
+
+               
+################################################################################
+###
+###            Gcodetools class
+###
+################################################################################
+
+class Gcodetools(inkex.Effect):
+
+       def export_gcode(self,gcode) :
+               if self.options.postprocessor != ""  or self.options.postprocessor_custom != "" :
+                       postprocessor = Postprocessor(self.error)
+                       postprocessor.gcode = gcode
+                       if self.options.postprocessor != "" :
+                               postprocessor.process(self.options.postprocessor)
+                       if self.options.postprocessor_custom != "" :
+                               postprocessor.process(self.options.postprocessor_custom)
+               postprocessor.gcode = self.header + postprocessor.gcode + self.footer
+               f = open(self.options.directory+self.options.file, "w") 
+               f.write(postprocessor.gcode)
+               f.close()                                                       
+
+
+################################################################################
+###            Arrangement: arranges paths by givven params
+###            TODO move it to the bottom
+################################################################################
+       def arrangement(self) :
+               paths = self.selected_paths
+               surface = Polygon()
+               polygons = []
+               time_ = time.time()
+               print_("Arrangement start at %s"%(time_))
+               original_paths = []
+               for layer in self.layers :
+                       if layer in paths :
+                               for path in paths[layer] :
+                                       csp = cubicsuperpath.parsePath(path.get("d"))
+                                       polygon = Polygon()
+                                       for subpath in csp :
+                                               for sp1, sp2 in zip(subpath,subpath[1:]) :
+                                                       polygon.add([csp_segment_convex_hull(sp1,sp2)])
+                                       #print_("Redused edges count from", sum([len(poly) for poly in polygon.polygon ]) )
+                                       polygon.hull()
+                                       original_paths += [path]
+                                       polygons += [polygon]
+                                       
+               print_("Paths hull computed in %s sec."%(time.time()-time_))
+               print_("Got %s polygons having average %s edges each."% ( len(polygons), float(sum([ sum([len(poly) for poly in polygon.polygon]) for polygon in polygons ])) / len(polygons) ) )
+               time_ = time.time()
+               
+#              material_width = self.options.arrangement_material_width
+#              population = Arangement_Genetic(polygons, material_width)
+#              population.add_random_species(1)
+#              population.test_population_centroid()
+##             return
+               material_width = self.options.arrangement_material_width
+               population = Arangement_Genetic(polygons, material_width)
+               
+               
+               print_("Genetic alhorithm start at %s"%(time_))
+               time_ = time.time()
+               
+       
+
+               population.add_random_species(50)
+               population.test(population.test_spiece_centroid)
+               print_("Initial population done in %s"%(time.time()-time_))
+               time_ = time.time()
+               pop = copy.deepcopy(population)
+               population_count = self.options.arrangement_population_count
+               last_champ = []
+               champions_count = 0
+               
+               
+               
+               
+               for i in range(population_count):
+                       population.leave_top_species(20)
+                       population.move_mutation_multiplier = random.random()/2
+                       
+                       population.order_mutation_factor = .2
+                       population.move_mutation_factor = 1.
+                       population.mutation_genes_count = [1,2]
+                       population.populate_species(250, 20)
+                       """
+                       randomize = i%100 < 40
+                       if      i%100 < 40 : 
+                               population.add_random_species(250)
+                       if  40<= i%100 < 100 : 
+                               population.mutation_genes_count = [1,max(2,int(population.genes_count/4))]  #[1,max(2,int(population.genes_count/2))] if 40<=i%100<60 else [1,max(2,int(population.genes_count/10))]
+                               population.move_mutation_multiplier = 1. if 40<=i%100<80 else .1
+                               population.move_mutation_factor = (-(i%100)/30+10/3) if 50<=i%100<100 else .5
+                               population.order_mutation_factor = 1./(i%100-79) if 80<=i%100<100 else 1.
+                               population.populate_species(250, 10)
+                       """
+                       population.test(population.test_spiece_centroid)
+                       draw_new_champ = False
+                       print_()
+                       for x in population.population[:10]:
+                               print_(x[0])
+                       
+                       if population.population[0][0]!= last_champ : 
+                               draw_new_champ = True
+                               last_champ = population.population[0][0]*1
+                       
+
+                       k = ""                  
+                       #for j in range(10) :
+                       #       k += "%s   " % population.population[j][0]
+                       print_("Cicle %s done in %s"%(i,time.time()-time_))
+                       time_ = time.time()
+                       print_("%s incests been found"%population.inc)
+                       print_()
+                       #print_(k)
+                       #print_()
+                       if i == 0  or i == population_count-1 or draw_new_champ :
+                               colors = ["blue"]
+                               
+                               surface = population.test_spiece_centroid(population.population[0][1])
+                               b = surface.bounds()
+                               x,y = 400* (champions_count%10), 700*int(champions_count/10)
+                               surface.move(x-b[0],y-b[1])
+                               surface.draw(width=2, color=colors[0])
+                               draw_text("Step = %s\nSquare = %f"%(i,(b[2]-b[0])*(b[3]-b[1])),x,y-40)
+                               champions_count += 1
+
+                               spiece = population.population[0][1]
+                               poly = Polygon(copy.deepcopy(population.polygons[spiece[0][0]].polygon))
+                               poly.rotate(spiece[0][2]*math.pi2)
+                               surface  = Polygon(poly.polygon)
+                               poly.draw(width = 2, color= "Violet")
+                               for p in spiece[1:] :
+                                       poly = Polygon(copy.deepcopy(population.polygons[p[0]].polygon))
+                                       poly.rotate(p[2]*math.pi2)
+                                       direction = [math.cos(p[1]*math.pi2), -math.sin(p[1]*math.pi2)]
+                                       normalize(direction)
+                                       c = surface.centroid()
+                                       c1 = poly.centroid()
+                                       poly.move(c[0]-c1[0]-direction[0]*400,c[1]-c1[1]-direction[1]*400)
+                                       c = surface.centroid()
+                                       c1 = poly.centroid()
+                                       poly.draw(width = 5, color= "Violet")
+                                       draw_pointer(c+c1,"Green","line")
+                                       direction = normalize(direction)
+                                       
+                                       
+                                       sin,cos = direction[0], direction[1]
+                                       poly.rotate_(-sin,cos)
+                                       surface.rotate_(-sin,cos)
+#                                      poly.draw(color = "Violet",width=4)                                     
+                                       surface.draw(color = "Orange",width=4)                                  
+                                       poly.rotate_(sin,cos)
+                                       surface.rotate_(sin,cos)
+
+
+                                       poly.drop_into_direction(direction,surface)
+                                       surface.add(poly)
+                               
+                               
+               # Now we'll need apply transforms to original paths
+               
+               
+       def __init__(self):
+               inkex.Effect.__init__(self)
+               self.OptionParser.add_option("-d", "--directory",                                       action="store", type="string",          dest="directory", default="/home/",                                     help="Directory for gcode file")
+               self.OptionParser.add_option("-f", "--filename",                                        action="store", type="string",          dest="file", default="-1.0",                                            help="File name")                       
+               self.OptionParser.add_option("",   "--add-numeric-suffix-to-filename", action="store", type="inkbool",  dest="add_numeric_suffix_to_filename", default=True,help="Add numeric suffix to filename")                      
+               self.OptionParser.add_option("",   "--Zscale",                                          action="store", type="float",           dest="Zscale", default="1.0",                                           help="Scale factor Z")                          
+               self.OptionParser.add_option("",   "--Zoffset",                                         action="store", type="float",           dest="Zoffset", default="0.0",                                          help="Offset along Z")
+               self.OptionParser.add_option("-s", "--Zsafe",                                           action="store", type="float",           dest="Zsafe", default="0.5",                                            help="Z above all obstacles")
+               self.OptionParser.add_option("-z", "--Zsurface",                                        action="store", type="float",           dest="Zsurface", default="0.0",                                         help="Z of the surface")
+               self.OptionParser.add_option("-c", "--Zdepth",                                          action="store", type="float",           dest="Zdepth", default="-0.125",                                        help="Z depth of cut")
+               self.OptionParser.add_option("",   "--Zstep",                                           action="store", type="float",           dest="Zstep", default="-0.125",                                         help="Z step of cutting")               
+               self.OptionParser.add_option("-p", "--feed",                                            action="store", type="float",           dest="feed", default="4.0",                                                     help="Feed rate in unit/min")
+
+               self.OptionParser.add_option("",   "--biarc-tolerance",                         action="store", type="float",           dest="biarc_tolerance", default="1",                            help="Tolerance used when calculating biarc interpolation.")                            
+               self.OptionParser.add_option("",   "--biarc-max-split-depth",           action="store", type="int",             dest="biarc_max_split_depth", default="4",                      help="Defines maximum depth of splitting while approximating using biarcs.")                            
+
+               self.OptionParser.add_option("",   "--tool-diameter",                           action="store", type="float",           dest="tool_diameter", default="3",                                      help="Tool diameter used for area cutting")             
+               self.OptionParser.add_option("",   "--max-area-curves",                         action="store", type="int",             dest="max_area_curves", default="100",                          help="Maximum area curves for each area")
+               self.OptionParser.add_option("",   "--area-inkscape-radius",            action="store", type="float",           dest="area_inkscape_radius", default="-10",                     help="Radius for preparing curves using inkscape")
+               self.OptionParser.add_option("",   "--unit",                                            action="store", type="string",          dest="unit", default="G21 (All units in mm)",           help="Units")
+               self.OptionParser.add_option("",   "--active-tab",                                      action="store", type="string",          dest="active_tab", default="",                                          help="Defines which tab is active")
+
+               self.OptionParser.add_option("",   "--area-find-artefacts-diameter",action="store", type="float",               dest="area_find_artefacts_diameter", default="1",                                       help="artefacts seeking radius")
+               self.OptionParser.add_option("",   "--area-find-artefacts-action",      action="store", type="string",          dest="area_find_artefacts_action", default="mark with an arrow",        help="artefacts action type")
+
+               self.OptionParser.add_option("",   "--auto_select_paths",                       action="store", type="inkbool",         dest="auto_select_paths", default=True,                         help="Select all paths if nothing is selected.")                
+
+               self.OptionParser.add_option("",   "--loft-distances",                          action="store", type="string",          dest="loft_distances", default="10",                            help="Distances between paths.")
+               self.OptionParser.add_option("",   "--loft-direction",                          action="store", type="string",          dest="loft_direction", default="crosswise",                     help="Direction of loft's interpolation.")
+               self.OptionParser.add_option("",   "--loft-interpolation-degree",       action="store", type="float",           dest="loft_interpolation_degree", default="2",          help="Which interpolation use to loft the paths smooth interpolation or staright.")
+
+               self.OptionParser.add_option("",   "--min-arc-radius",                          action="store", type="float",           dest="min_arc_radius", default=".1",                            help="All arc having radius less than minimum will be considered as straight line")             
+
+               self.OptionParser.add_option("",   "--engraving-sharp-angle-tollerance",action="store", type="float",   dest="engraving_sharp_angle_tollerance", default="150",         help="All angles thar are less than engraving-sharp-angle-tollerance will be thought sharp")            
+               self.OptionParser.add_option("",   "--engraving-max-dist",                      action="store", type="float",           dest="engraving_max_dist", default="10",                                        help="Distanse from original path where engraving is not needed (usualy it's cutting tool diameter)")           
+               self.OptionParser.add_option("",   "--engraving-newton-iterations", action="store", type="int",                 dest="engraving_newton_iterations", default="4",                        help="Number of sample points used to calculate distance")              
+               self.OptionParser.add_option("",   "--engraving-draw-calculation-paths",action="store", type="inkbool", dest="engraving_draw_calculation_paths", default=False,         help="Draw additional graphics to debug engraving path")                
+               self.OptionParser.add_option("",   "--engraving-cutter-shape-function",action="store", type="string",   dest="engraving_cutter_shape_function", default="w",            help="Cutter shape function z(w). Ex. cone: w. ")
+
+               self.OptionParser.add_option("",   "--lathe-width",                                     action="store", type="float",           dest="lathe_width", default=10.,                                                        help="Lathe width")
+               self.OptionParser.add_option("",   "--lathe-fine-cut-width",            action="store", type="float",           dest="lathe_fine_cut_width", default=1.,                                        help="Fine cut width")
+               self.OptionParser.add_option("",   "--lathe-fine-cut-count",            action="store", type="int",             dest="lathe_fine_cut_count", default=1.,                                        help="Fine cut count")
+               self.OptionParser.add_option("",   "--lathe-create-fine-cut-using",     action="store", type="string",          dest="lathe_create_fine_cut_using", default="Move path",                        help="Create fine cut using")
+               self.OptionParser.add_option("",   "--lathe-x-axis-remap",                      action="store", type="string",          dest="lathe_x_axis_remap", default="X",                                         help="Lathe X axis remap")
+               self.OptionParser.add_option("",   "--lathe-z-axis-remap",                      action="store", type="string",          dest="lathe_z_axis_remap", default="Z",                                         help="Lathe Z axis remap")
+
+               self.OptionParser.add_option("",   "--create-log",                                      action="store", type="inkbool",         dest="log_create_log", default=False,                           help="Create log files")
+               self.OptionParser.add_option("",   "--log-filename",                            action="store", type="string",          dest="log_filename", default='',                                        help="Create log files")
+
+               self.OptionParser.add_option("",   "--orientation-points-count",        action="store", type="int",             dest="orientation_points_count", default='2',                   help="Orientation points count")
+               self.OptionParser.add_option("",   "--tools-library-type",                      action="store", type="string",          dest="tools_library_type", default='cylinder cutter',   help="Create tools defention")
+
+               self.OptionParser.add_option("",   "--dxfpoints-action",                        action="store", type="string",          dest="dxfpoints_action", default='replace',                     help="dxfpoint sign toggle")
+                                                                                                                                                                                                                 
+               self.OptionParser.add_option("",   "--help-language",                           action="store", type="string",          dest="help_language", default='http://www.cnc-club.ru/forum/viewtopic.php?f=33&t=35',   help="Open help page in webbrowser.")
+
+               self.OptionParser.add_option("",   "--offset-radius",                           action="store", type="float",           dest="offset_radius", default=10.,              help="Offset radius")
+               self.OptionParser.add_option("",   "--offset-step",                                     action="store", type="float",           dest="offset_step", default=10.,                help="Offset step")
+               self.OptionParser.add_option("",   "--offset-draw-clippend-path",       action="store", type="inkbool",         dest="offset_draw_clippend_path", default=False,                help="Draw clipped path")               
+               self.OptionParser.add_option("",   "--offset-just-get-distance",        action="store", type="inkbool",         dest="offset_just_get_distance", default=False,         help="Don't do offset just get distance")               
+       
+               self.OptionParser.add_option("",   "--arrangement-material-width",      action="store", type="float",           dest="arrangement_material_width", default=500,         help="Materials width for arrangement")         
+               self.OptionParser.add_option("",   "--arrangement-population-count",action="store", type="int",                 dest="arrangement_population_count", default=100,       help="Genetic algorithm populations count")             
+
+               self.OptionParser.add_option("",   "--postprocessor",                           action="store", type="string",          dest="postprocessor", default='',                       help="Postprocessor command.")
+               self.OptionParser.add_option("",   "--postprocessor-custom",            action="store", type="string",          dest="postprocessor_custom", default='',        help="Postprocessor custom command.")
+       
+               
+
+               self.default_tool = {
+                                       "name": "Default tool",
+                                       "id": "default tool",
+                                       "diameter":10.,
+                                       "shape": "10",
+                                       "penetration angle":90.,
+                                       "penetration feed":100.,
+                                       "depth step":1.,
+                                       "feed":400.,
+                                       "in trajectotry":"",
+                                       "out trajectotry":"",
+                                       "gcode before path":"",
+                                       "gcode after path":"",
+                                       "sog":"",
+                                       "spinlde rpm":"",
+                                       "CW or CCW":"",
+                                       "tool change gcode":" ",
+                                       "4th axis meaning": " ",
+                                       "4th axis scale": 1.,
+                                       "4th axis offset": 0.,
+                                       "passing feed":"800",                                   
+                                       "fine feed":"800",                                      
+                               }                       
+               self.tools_field_order = [
+                                       'name',
+                                       'id',
+                                       'diameter',
+                                       'feed',
+                                       'shape',
+                                       'penetration angle',
+                                       'penetration feed',
+                                       "passing feed",
+                                       'depth step',
+                                       "in trajectotry",
+                                       "out trajectotry",
+                                       "gcode before path",
+                                       "gcode after path",
+                                       "sog",
+                                       "spinlde rpm",
+                                       "CW or CCW",
+                                       "tool change gcode",
+                               ]
+
+
+       def parse_curve(self, p, layer, w = None, f = None):
+                       c = []
+                       if len(p)==0 : 
+                               return []
+                       p = self.transform_csp(p, layer)
+                       
+
+                       ### Sort to reduce Rapid distance       
+                       k = range(1,len(p))
+                       keys = [0]
+                       while len(k)>0:
+                               end = p[keys[-1]][-1][1]
+                               dist = None
+                               for i in range(len(k)):
+                                       start = p[k[i]][0][1]
+                                       dist = max(   ( -( ( end[0]-start[0])**2+(end[1]-start[1])**2 ) ,i)     ,   dist )
+                               keys += [k[dist[1]]]
+                               del k[dist[1]]
+                       for k in keys:
+                               subpath = p[k]
+                               c += [ [        [subpath[0][1][0],subpath[0][1][1]]   , 'move', 0, 0] ]
+                               for i in range(1,len(subpath)):
+                                       sp1 = [  [subpath[i-1][j][0], subpath[i-1][j][1]] for j in range(3)]
+                                       sp2 = [  [subpath[i  ][j][0], subpath[i  ][j][1]] for j in range(3)]
+                                       c += biarc(sp1,sp2,0,0) if w==None else biarc(sp1,sp2,-f(w[k][i-1]),-f(w[k][i]))
+#                                      l1 = biarc(sp1,sp2,0,0) if w==None else biarc(sp1,sp2,-f(w[k][i-1]),-f(w[k][i]))
+#                                      print_((-f(w[k][i-1]),-f(w[k][i]), [i1[5] for i1 in l1]) )
+                               c += [ [ [subpath[-1][1][0],subpath[-1][1][1]]  ,'end',0,0] ]
+                       return c
+
+
+       def draw_curve(self, curve, layer, group=None, style=styles["biarc_style"]):
+       
+               self.get_defs()
+               # Add marker to defs if it doesnot exists
+               if "DrawCurveMarker" not in self.defs : 
+                       defs = inkex.etree.SubElement( self.document.getroot(), inkex.addNS("defs","svg"))
+                       marker = inkex.etree.SubElement( defs, inkex.addNS("marker","svg"), {"id":"DrawCurveMarker","orient":"auto","refX":"-8","refY":"-2.41063","style":"overflow:visible"})
+                       inkex.etree.SubElement( marker, inkex.addNS("path","svg"), 
+                                       {       "d":"m -6.55552,-2.41063 0,0 L -13.11104,0 c 1.0473,-1.42323 1.04126,-3.37047 0,-4.82126",
+                                               "style": "fill:#000044; fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"       }
+                               )
+               if "DrawCurveMarker_r" not in self.defs : 
+                       defs = inkex.etree.SubElement( self.document.getroot(), inkex.addNS("defs","svg"))
+                       marker = inkex.etree.SubElement( defs, inkex.addNS("marker","svg"), {"id":"DrawCurveMarker_r","orient":"auto","refX":"8","refY":"-2.41063","style":"overflow:visible"})
+                       inkex.etree.SubElement( marker, inkex.addNS("path","svg"), 
+                                       {       "d":"m 6.55552,-2.41063 0,0 L 13.11104,0 c -1.0473,-1.42323 -1.04126,-3.37047 0,-4.82126",
+                                               "style": "fill:#000044; fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"       }
+                               )
+               for i in [0,1]:
+                       style['biarc%s_r'%i] = simplestyle.parseStyle(style['biarc%s'%i])
+                       style['biarc%s_r'%i]["marker-start"] = "url(#DrawCurveMarker_r)"
+                       del(style['biarc%s_r'%i]["marker-end"])
+                       style['biarc%s_r'%i] = simplestyle.formatStyle(style['biarc%s_r'%i])
+               
+               if group==None:
+                       group = inkex.etree.SubElement( self.layers[min(1,len(self.layers)-1)], inkex.addNS('g','svg'), {"gcodetools": "Preview group"} )
+               s, arcn = '', 0
+               
+               
+               a,b,c = [0.,0.], [1.,0.], [0.,1.]
+               k = (b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1])
+               a,b,c = self.transform(a, layer, True), self.transform(b, layer, True), self.transform(c, layer, True)
+               if ((b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1]))*k > 0 : reverse_angle = 1
+               else : reverse_angle = -1 
+               for sk in curve:
+                       si = sk[:]
+                       si[0], si[2] = self.transform(si[0], layer, True), (self.transform(si[2], layer, True) if type(si[2])==type([]) and len(si[2])==2 else si[2])
+                       
+                       if s!='':
+                               if s[1] == 'line':
+                                       inkex.etree.SubElement( group, inkex.addNS('path','svg'), 
+                                                       {
+                                                               'style': style['line'],
+                                                               'd':'M %s,%s L %s,%s' % (s[0][0], s[0][1], si[0][0], si[0][1]),
+                                                               "gcodetools": "Preview",
+                                                       }
+                                               )
+                               elif s[1] == 'arc':
+                                       arcn += 1
+                                       sp = s[0]
+                                       c = s[2]
+                                       s[3] = s[3]*reverse_angle
+                                               
+                                       a =  ( (P(si[0])-P(c)).angle() - (P(s[0])-P(c)).angle() )%math.pi2 #s[3]
+                                       if s[3]*a<0: 
+                                                       if a>0: a = a-math.pi2
+                                                       else: a = math.pi2+a
+                                       r = math.sqrt( (sp[0]-c[0])**2 + (sp[1]-c[1])**2 )
+                                       a_st = ( math.atan2(sp[0]-c[0],- (sp[1]-c[1])) - math.pi/2 ) % (math.pi*2)
+                                       st = style['biarc%s' % (arcn%2)][:]
+                                       if a>0:
+                                               a_end = a_st+a
+                                               st = style['biarc%s'%(arcn%2)]
+                                       else: 
+                                               a_end = a_st*1
+                                               a_st = a_st+a
+                                               st = style['biarc%s_r'%(arcn%2)]
+                                       inkex.etree.SubElement( group, inkex.addNS('path','svg'), 
+                                                {
+                                                       'style': st,
+                                                        inkex.addNS('cx','sodipodi'):          str(c[0]),
+                                                        inkex.addNS('cy','sodipodi'):          str(c[1]),
+                                                        inkex.addNS('rx','sodipodi'):          str(r),
+                                                        inkex.addNS('ry','sodipodi'):          str(r),
+                                                        inkex.addNS('start','sodipodi'):       str(a_st),
+                                                        inkex.addNS('end','sodipodi'):         str(a_end),
+                                                        inkex.addNS('open','sodipodi'):        'true',
+                                                        inkex.addNS('type','sodipodi'):        'arc',
+                                                       "gcodetools": "Preview",
+                                               })
+                       s = si
+       
+
+       def check_dir(self):
+               if self.options.directory[-1] not in ["/","\\"]:
+                       if "\\" in self.options.directory :
+                               self.options.directory += "\\"
+                       else :
+                               self.options.directory += "/"
+               print_("Checking direcrory: '%s'"%self.options.directory)
+               if (os.path.isdir(self.options.directory)):
+                       if (os.path.isfile(self.options.directory+'header')):
+                               f = open(self.options.directory+slash+'header', 'r')
+                               self.header = f.read()
+                               f.close()
+                       else:
+                               self.header = defaults['header']
+                       if (os.path.isfile(self.options.directory+'footer')):
+                               f = open(self.options.directory+'footer','r')
+                               self.footer = f.read()
+                               f.close()
+                       else:
+                               self.footer = defaults['footer']
+                       self.header += self.options.unit + "\n" 
+               else: 
+                       self.error(_("Directory does not exist! Please specify existing directory at Preferences tab!"),"error")
+                       return False
+
+               if self.options.add_numeric_suffix_to_filename :
+                       dir_list = os.listdir(self.options.directory)
+                       if "." in self.options.file : 
+                               r = re.match(r"^(.*)(\..*)$",self.options.file)
+                               ext = r.group(2)
+                               name = r.group(1)
+                       else:   
+                               ext = ""
+                               name = self.options.file
+                       max_n = 0
+                       for s in dir_list :
+                               r = re.match(r"^%s_0*(\d+)%s$"%(re.escape(name),re.escape(ext) ), s)
+                               if r :
+                                       max_n = max(max_n,int(r.group(1)))
+                       filename = name + "_" + ( "0"*(4-len(str(max_n+1))) + str(max_n+1) ) + ext
+                       self.options.file = filename
+               
+               print_("Testing writing rights on '%s'"%(self.options.directory+self.options.file))
+               try:    
+                       f = open(self.options.directory+self.options.file, "w") 
+                       f.close()                                                       
+               except:
+                       self.error(_("Can not write to specified file!\n%s"%(self.options.directory+self.options.file)),"error")
+                       return False
+               return True
+                       
+
+
+################################################################################
+###
+###            Generate Gcode
+###            Generates Gcode on given curve.
+###
+###            Crve defenitnion [start point, type = {'arc','line','move','end'}, arc center, arc angle, end point, [zstart, zend]]            
+###
+################################################################################
+       def generate_gcode(self, curve, layer, depth):
+               Zauto_scale = self.Zauto_scale[layer]
+               tool = self.tools[layer][0]
+
+               def c(c):
+                       c = [c[i] if i<len(c) else None for i in range(6)]
+                       if c[5] == 0 : c[5]=None
+                       s,s1 = [" X", " Y", " Z", " I", " J", " K"], ["","","","","",""]
+                       m,a = [1,1,self.options.Zscale*Zauto_scale,1,1,self.options.Zscale*Zauto_scale], [0,0,self.options.Zoffset,0,0,0]
+                       r = ''  
+                       for i in range(6):
+                               if c[i]!=None:
+                                       r += s[i] + ("%f" % (c[i]*m[i]+a[i])) + s1[i]
+                       return r
+
+               def calculate_angle(a, current_a):
+                       return  min(                                    
+                                               [abs(a-current_a%math.pi2+math.pi2), a+current_a-current_a%math.pi2+math.pi2],
+                                               [abs(a-current_a%math.pi2-math.pi2), a+current_a-current_a%math.pi2-math.pi2],
+                                               [abs(a-current_a%math.pi2),                      a+current_a-current_a%math.pi2])[1]
+               if len(curve)==0 : return ""    
+                               
+               try :
+                       self.last_used_tool == None
+               except :
+                       self.last_used_tool = None
+               print_("working on curve")
+               print_(curve)
+               g = tool['tool change gcode'] +"\n" if tool != self.last_used_tool else "\n"
+               
+               lg, zs, f =  'G00', self.options.Zsafe, " F%f"%tool['feed'] 
+               current_a = 0
+               go_to_safe_distance = "G00" + c([None,None,zs]) + "\n" 
+               penetration_feed = " F%s"%tool['penetration feed'] 
+               for i in range(1,len(curve)):
+               #       Creating Gcode for curve between s=curve[i-1] and si=curve[i] start at s[0] end at s[4]=si[0]
+                       s, si = curve[i-1], curve[i]
+                       feed = f if lg not in ['G01','G02','G03'] else ''
+                       if s[1] == 'move':
+                               g += go_to_safe_distance + "G00" + c(si[0]) + "\n" + tool['gcode before path'] + "\n"
+                               lg = 'G00'
+                       elif s[1] == 'end':
+                               g += go_to_safe_distance + tool['gcode after path'] + "\n"
+                               lg = 'G00'
+                       elif s[1] == 'line':
+                               if tool['4th axis meaning'] == "tangent knife" : 
+                                       a = atan2(si[0][0]-s[0][0],si[0][1]-s[0][1])
+                                       a = calculate_angle(a, current_a)
+                                       g+="G01 A%s\n" % (a*tool['4th axis scale']+tool['4th axis offset'])
+                                       current_a = a
+                               if lg=="G00": g += "G01" + c([None,None,s[5][0]+depth]) + penetration_feed +"\n"        
+                               g += "G01" +c(si[0]+[s[5][1]+depth]) + feed + "\n"
+                               lg = 'G01'
+                       elif s[1] == 'arc':
+                               r = [(s[2][0]-s[0][0]), (s[2][1]-s[0][1])]
+                               if tool['4th axis meaning'] == "tangent knife" : 
+                                       if s[3]<0 : # CW
+                                               a1 = atan2(s[2][1]-s[0][1],-s[2][0]+s[0][0]) + math.pi 
+                                       else: #CCW
+                                               a1 = atan2(-s[2][1]+s[0][1],s[2][0]-s[0][0]) + math.pi
+                                       a = calculate_angle(a1, current_a)
+                                       g+="G01 A%s\n" % (a*tool['4th axis scale']+tool['4th axis offset'])
+                                       current_a = a
+                                       axis4 = " A%s"%((current_a+s[3])*tool['4th axis scale']+tool['4th axis offset'])
+                                       current_a = current_a+s[3]
+                               else : axis4 = ""
+                               if lg=="G00": g += "G01" + c([None,None,s[5][0]+depth]) + penetration_feed + "\n"                               
+                               if (r[0]**2 + r[1]**2)>self.options.min_arc_radius:
+                                       r1, r2 = (P(s[0])-P(s[2])), (P(si[0])-P(s[2]))
+                                       if abs(r1.mag()-r2.mag()) < 0.001 :
+                                               g += ("G02" if s[3]<0 else "G03") + c(si[0]+[ s[5][1]+depth, (s[2][0]-s[0][0]),(s[2][1]-s[0][1])  ]) + feed + axis4 + "\n"
+                                       else:
+                                               r = (r1.mag()+r2.mag())/2
+                                               g += ("G02" if s[3]<0 else "G03") + c(si[0]+[s[5][1]+depth]) + " R%f" % (r) + feed  + axis4 + "\n"
+                                       lg = 'G02'
+                               else:
+                                       if tool['4th axis meaning'] == "tangent knife" : 
+                                               a = atan2(si[0][0]-s[0][0],si[0][1]-s[0][1]) + math.pi
+                                               a = calculate_angle(a, current_a)
+                                               g+="G01 A%s\n" % (a*tool['4th axis scale']+tool['4th axis offset'])
+                                               current_a = a
+                                       g += "G01" +c(si[0]+[s[5][1]+depth]) + feed + "\n"
+                                       lg = 'G01'
+               if si[1] == 'end':
+                       g += go_to_safe_distance + tool['gcode after path'] + "\n"
+               return g
+
+
+       def get_transforms(self,g):
+               root = self.document.getroot()
+               trans = []
+               while (g!=root):
+                       if 'transform' in g.keys():
+                               t = g.get('transform')
+                               t = simpletransform.parseTransform(t)
+                               trans = simpletransform.composeTransform(t,trans) if trans != [] else t
+                               print_(trans)
+                       g=g.getparent()
+               return trans 
+               
+
+       def apply_transforms(self,g,csp):
+               trans = self.get_transforms(g)
+               if trans != []:
+                       simpletransform.applyTransformToPath(trans, csp)
+               return csp
+
+
+       def transform(self,source_point, layer, reverse=False):
+               if layer not in self.transform_matrix:
+                       for i in range(self.layers.index(layer),-1,-1):
+                               if self.layers[i] in self.orientation_points : 
+                                       break
+                       if self.layers[i] not in self.orientation_points :
+                               self.error(_("Orientation points for '%s' layer have not been found! Please add orientation points using Orientation tab!") % layer.get(inkex.addNS('label','inkscape')),"no_orientation_points")
+                       elif self.layers[i] in self.transform_matrix :
+                               self.transform_matrix[layer] = self.transform_matrix[self.layers[i]]
+                       else :
+                               orientation_layer = self.layers[i]
+                               if len(self.orientation_points[orientation_layer])>1 : 
+                                       self.error(_("There are more than one orientation point groups in '%s' layer") % orientation_layer.get(inkex.addNS('label','inkscape')),"more_than_one_orientation_point_groups")
+                               points = self.orientation_points[orientation_layer][0]
+                               if len(points)==2:
+                                       points += [ [ [(points[1][0][1]-points[0][0][1])+points[0][0][0], -(points[1][0][0]-points[0][0][0])+points[0][0][1]], [-(points[1][1][1]-points[0][1][1])+points[0][1][0], points[1][1][0]-points[0][1][0]+points[0][1][1]] ] ]
+                               if len(points)==3:
+                                       print_("Layer '%s' Orientation points: " % orientation_layer.get(inkex.addNS('label','inkscape')))
+                                       for point in points:
+                                               print_(point)
+                                       #       Zcoordinates definition taken from Orientatnion point 1 and 2 
+                                       self.Zcoordinates[layer] = [max(points[0][1][2],points[1][1][2]), min(points[0][1][2],points[1][1][2])]
+                                       matrix = numpy.array([
+                                                               [points[0][0][0], points[0][0][1], 1, 0, 0, 0, 0, 0, 0],
+                                                               [0, 0, 0, points[0][0][0], points[0][0][1], 1, 0, 0, 0],
+                                                               [0, 0, 0, 0, 0, 0, points[0][0][0], points[0][0][1], 1],
+                                                               [points[1][0][0], points[1][0][1], 1, 0, 0, 0, 0, 0, 0],
+                                                               [0, 0, 0, points[1][0][0], points[1][0][1], 1, 0, 0, 0],
+                                                               [0, 0, 0, 0, 0, 0, points[1][0][0], points[1][0][1], 1],
+                                                               [points[2][0][0], points[2][0][1], 1, 0, 0, 0, 0, 0, 0],
+                                                               [0, 0, 0, points[2][0][0], points[2][0][1], 1, 0, 0, 0],
+                                                               [0, 0, 0, 0, 0, 0, points[2][0][0], points[2][0][1], 1]
+                                                       ])
+                                                               
+                                       if numpy.linalg.det(matrix)!=0 :
+                                               m = numpy.linalg.solve(matrix,
+                                                       numpy.array(
+                                                               [[points[0][1][0]], [points[0][1][1]], [1], [points[1][1][0]], [points[1][1][1]], [1], [points[2][1][0]], [points[2][1][1]], [1]]       
+                                                                               )
+                                                       ).tolist()
+                                               self.transform_matrix[layer] = [[m[j*3+i][0] for i in range(3)] for j in range(3)]
+                                       
+                                       else :
+                                               self.error(_("Orientation points are wrong! (if there are two orientation points they sould not be the same. If there are three orientation points they should not be in a straight line.)"),"wrong_orientation_points")
+                               else :
+                                       self.error(_("Orientation points are wrong! (if there are two orientation points they sould not be the same. If there are three orientation points they should not be in a straight line.)"),"wrong_orientation_points")
+
+                       self.transform_matrix_reverse[layer] = numpy.linalg.inv(self.transform_matrix[layer]).tolist()          
+                       print_("\n Layer '%s' transformation matrixes:" % layer.get(inkex.addNS('label','inkscape')) )
+                       print_(self.transform_matrix)
+                       print_(self.transform_matrix_reverse)
+
+                       ###self.Zauto_scale[layer]  = math.sqrt( (self.transform_matrix[layer][0][0]**2 + self.transform_matrix[layer][1][1]**2)/2 )
+                       ### Zautoscale is absolete
+                       self.Zauto_scale[layer] = 1
+                       print_("Z automatic scale = %s (computed according orientation points)" % self.Zauto_scale[layer])
+
+               x,y = source_point[0], source_point[1]
+               if not reverse :
+                       t = self.transform_matrix[layer]
+               else :
+                       t = self.transform_matrix_reverse[layer]
+               return [t[0][0]*x+t[0][1]*y+t[0][2], t[1][0]*x+t[1][1]*y+t[1][2]]
+
+
+       def transform_csp(self, csp_, layer, reverse = False):
+               csp = [  [ [csp_[i][j][0][:],csp_[i][j][1][:],csp_[i][j][2][:]]  for j in range(len(csp_[i])) ]   for i in range(len(csp_)) ]
+               for i in xrange(len(csp)):
+                       for j in xrange(len(csp[i])): 
+                               for k in xrange(len(csp[i][j])): 
+                                       csp[i][j][k] = self.transform(csp[i][j][k],layer, reverse)
+               return csp
+       
+               
+################################################################################
+###            Errors handling function, notes are just printed into Logfile, 
+###            warnings are printed into log file and warning message is displayed but
+###            extension continues working, errors causes log and execution is halted
+###            Notes, warnings adn errors could be assigned to space or comma or dot 
+###            sepparated strings (case is ignoreg).
+################################################################################
+       def error(self, s, type_= "Warning"):
+               notes = "Note "
+               warnings = """
+                                               Warning tools_warning
+                                               bad_orientation_points_in_some_layers
+                                               more_than_one_orientation_point_groups
+                                               more_than_one_tool
+                                               orientation_have_not_been_defined
+                                               tool_have_not_been_defined
+                                               selection_does_not_contain_paths
+                                               selection_does_not_contain_paths_will_take_all
+                                               selection_is_empty_will_comupe_drawing
+                                               selection_contains_objects_that_are_not_paths
+                                               """
+               errors = """
+                                               Error   
+                                               wrong_orientation_points        
+                                               area_tools_diameter_error
+                                               no_tool_error
+                                               active_layer_already_has_tool
+                                               active_layer_already_has_orientation_points
+                                       """
+               if type_.lower() in re.split("[\s\n,\.]+", errors.lower()) :
+                       print_(s)
+                       inkex.errormsg(s+"\n")          
+                       sys.exit()
+               elif type_.lower() in re.split("[\s\n,\.]+", warnings.lower()) :
+                       print_(s)
+                       inkex.errormsg(s+"\n")          
+               elif type_.lower() in re.split("[\s\n,\.]+", notes.lower()) :
+                       print_(s)
+               else :
+                       print_(s)
+                       inkex.errormsg(s)               
+                       sys.exit()
+       
+       
+################################################################################
+###            Get defs from svg
+################################################################################
+       def get_defs(self) :
+               self.defs = {}
+               def recursive(g) :
+                       for i in g:
+                               if i.tag == inkex.addNS("defs","svg") : 
+                                       for j in i: 
+                                               self.defs[j.get("id")] = i
+                               if i.tag ==inkex.addNS("g",'svg') :
+                                       recursive(i)
+               recursive(self.document.getroot())
+
+
+################################################################################
+###
+###            Get Gcodetools info from the svg
+###
+################################################################################
+       def get_info(self):
+               self.selected_paths = {}
+               self.paths = {}         
+               self.tools = {}
+               self.orientation_points = {}
+               self.layers = [self.document.getroot()]
+               self.Zcoordinates = {}
+               self.transform_matrix = {}
+               self.transform_matrix_reverse = {}
+               self.Zauto_scale = {}
+               
+               def recursive_search(g, layer, selected=False):
+                       items = g.getchildren()
+                       items.reverse()
+                       for i in items:
+                               if selected:
+                                       self.selected[i.get("id")] = i
+                               if i.tag == inkex.addNS("g",'svg') and i.get(inkex.addNS('groupmode','inkscape')) == 'layer':
+                                       self.layers += [i]
+                                       recursive_search(i,i)
+                               elif i.get('gcodetools') == "Gcodetools orientation group" :
+                                       points = self.get_orientation_points(i)
+                                       if points != None :
+                                               self.orientation_points[layer] = self.orientation_points[layer]+[points[:]] if layer in self.orientation_points else [points[:]]
+                                               print_("Found orientation points in '%s' layer: %s" % (layer.get(inkex.addNS('label','inkscape')), points))
+                                       else :
+                                               self.error(_("Warning! Found bad orientation points in '%s' layer. Resulting Gcode could be corrupt!") % layer.get(inkex.addNS('label','inkscape')), "bad_orientation_points_in_some_layers") 
+                               elif i.get("gcodetools") == "Gcodetools tool defenition" :
+                                       tool = self.get_tool(i)
+                                       self.tools[layer] = self.tools[layer] + [tool.copy()] if layer in self.tools else [tool.copy()]
+                                       print_("Found tool in '%s' layer: %s" % (layer.get(inkex.addNS('label','inkscape')), tool))
+                               elif i.tag == inkex.addNS('path','svg'):
+                                       if "gcodetools"  not in i.keys() :
+                                               self.paths[layer] = self.paths[layer] + [i] if layer in self.paths else [i]  
+                                               if i.get("id") in self.selected :
+                                                       self.selected_paths[layer] = self.selected_paths[layer] + [i] if layer in self.selected_paths else [i]  
+                               elif i.tag == inkex.addNS("g",'svg'):
+                                       recursive_search(i,layer, (i.get("id") in self.selected) )
+                               elif i.get("id") in self.selected :
+                                       self.error(_("This extension works with Paths and Dynamic Offsets and groups of them only! All other objects will be ignored!\nSolution 1: press Path->Object to path or Shift+Ctrl+C.\nSolution 2: Path->Dynamic offset or Ctrl+J.\nSolution 3: export all contours to PostScript level 2 (File->Save As->.ps) and File->Import this file."),"selection_contains_objects_that_are_not_paths")
+                               
+                                       
+               recursive_search(self.document.getroot(),self.document.getroot())
+
+
+       def get_orientation_points(self,g):
+               items = g.getchildren()
+               items.reverse()
+               p2, p3 = [], []
+               p = None
+               for i in items:
+                       if i.tag == inkex.addNS("g",'svg') and i.get("gcodetools") == "Gcodetools orientation point (2 points)":
+                               p2 += [i]
+                       if i.tag == inkex.addNS("g",'svg') and i.get("gcodetools") == "Gcodetools orientation point (3 points)":
+                               p3 += [i]
+               if len(p2)==2 : p=p2 
+               elif len(p3)==3 : p=p3 
+               if p==None : return None
+               points = []
+               for i in p :    
+                       point = [[],[]] 
+                       for  node in i :
+                               if node.get('gcodetools') == "Gcodetools orientation point arrow":
+                                       point[0] = self.apply_transforms(node,cubicsuperpath.parsePath(node.get("d")))[0][0][1]
+                               if node.get('gcodetools') == "Gcodetools orientation point text":
+                                       r = re.match(r'(?i)\s*\(\s*(-?\s*\d*(?:,|\.)*\d*)\s*;\s*(-?\s*\d*(?:,|\.)*\d*)\s*;\s*(-?\s*\d*(?:,|\.)*\d*)\s*\)\s*',node.text)
+                                       point[1] = [float(r.group(1)),float(r.group(2)),float(r.group(3))]
+                       if point[0]!=[] and point[1]!=[]:       points += [point]
+               if len(points)==len(p2)==2 or len(points)==len(p3)==3 : return points
+               else : return None
+
+
+       def get_tool(self, g):
+               tool = self.default_tool.copy()
+               tool["self_group"] = g 
+               for i in g:
+                       #       Get parameters
+                       if i.get("gcodetools") == "Gcodetools tool background" : 
+                               tool["style"] = simplestyle.parseStyle(i.get("style"))
+                       elif i.get("gcodetools") == "Gcodetools tool parameter" :
+                               key = None
+                               value = None
+                               for j in i:
+                                       if j.get("gcodetools") == "Gcodetools tool defention field name":
+                                               key = j.text
+                                       if j.get("gcodetools") == "Gcodetools tool defention field value":
+                                               for k in j :
+                                                       if k.tag == inkex.addNS('tspan','svg') and k.get("gcodetools") == "Gcodetools tool defention field value":
+                                                               if k.text!=None : value = value +"\n" + k.text if value != None else k.text
+                               if value == None or key == None: continue
+                               #print_("Found tool parameter '%s':'%s'" % (key,value))
+                               if key in self.default_tool.keys() :
+                                        try :
+                                               tool[key] = type(self.default_tool[key])(value)
+                                        except :
+                                               tool[key] = self.default_tool[key]
+                                               self.error(_("Warning! Tool's and default tool's parameter's (%s) types are not the same ( type('%s') != type('%s') ).") % (key, value, self.default_tool[key]), "tools_warning")
+                               else :
+                                       tool[key] = value
+                                       self.error(_("Warning! Tool has parameter that default tool has not ( '%s': '%s' ).") % (key, value), "tools_warning" )
+               return tool
+               
+               
+       def set_tool(self,layer):
+#              print_(("index(layer)=",self.layers.index(layer),"set_tool():layer=",layer,"self.tools=",self.tools))
+#              for l in self.layers:
+#                      print_(("l=",l))
+               for i in range(self.layers.index(layer),-1,-1):
+#                      print_(("processing layer",i))
+                       if self.layers[i] in self.tools : 
+                               break
+               if self.layers[i] in self.tools :
+                       if self.layers[i] != layer : self.tools[layer] = self.tools[self.layers[i]]
+                       if len(self.tools[layer])>1 : self.error(_("Layer '%s' contains more than one tool!") % self.layers[i].get(inkex.addNS('label','inkscape')), "more_than_one_tool")
+                       return self.tools[layer]
+               else :
+                       self.error(_("Can not find tool for '%s' layer! Please add one with Tools library tab!") % layer.get(inkex.addNS('label','inkscape')), "no_tool_error")
+
+
+################################################################################
+###
+###            Path to Gcode
+###
+################################################################################
+       def path_to_gcode(self) :
+
+               def get_boundaries(points):
+                       minx,miny,maxx,maxy=None,None,None,None
+                       out=[[],[],[],[]]
+                       for p in points:
+                               if minx==p[0]:
+                                       out[0]+=[p]
+                               if minx==None or p[0]<minx: 
+                                       minx=p[0]
+                                       out[0]=[p]
+
+                               if miny==p[1]:
+                                       out[1]+=[p]
+                               if miny==None or p[1]<miny: 
+                                       miny=p[1]
+                                       out[1]=[p]
+
+                               if maxx==p[0]:
+                                       out[2]+=[p]
+                               if maxx==None or p[0]>maxx: 
+                                       maxx=p[0]
+                                       out[2]=[p]
+
+                               if maxy==p[1]:
+                                       out[3]+=[p]
+                               if maxy==None or p[1]>maxy: 
+                                       maxy=p[1]
+                                       out[3]=[p]
+                       return out
+
+
+               def remove_duplicates(points):
+                       i=0             
+                       out=[]
+                       for p in points:
+                               for j in xrange(i,len(points)):
+                                       if p==points[j]: points[j]=[None,None]  
+                               if p!=[None,None]: out+=[p]
+                       i+=1
+                       return(out)
+       
+       
+               def get_way_len(points):
+                       l=0
+                       for i in xrange(1,len(points)):
+                               l+=math.sqrt((points[i][0]-points[i-1][0])**2 + (points[i][1]-points[i-1][1])**2)
+                       return l
+
+       
+               def sort_dxfpoints(points):
+                       points=remove_duplicates(points)
+#                      print_(get_boundaries(get_boundaries(points)[2])[1])
+                       ways=[
+                                                 # l=0, d=1, r=2, u=3
+                        [3,0], # ul
+                        [3,2], # ur
+                        [1,0], # dl
+                        [1,2], # dr
+                        [0,3], # lu
+                        [0,1], # ld
+                        [2,3], # ru
+                        [2,1], # rd
+                       ]
+#                      print_(("points=",points))
+                       minimal_way=[]
+                       minimal_len=None
+                       minimal_way_type=None
+                       for w in ways:
+                               tpoints=points[:]
+                               cw=[]
+#                              print_(("tpoints=",tpoints))
+                               for j in xrange(0,len(points)):
+                                       p=get_boundaries(get_boundaries(tpoints)[w[0]])[w[1]]
+#                                      print_(p)
+                                       tpoints.remove(p[0])
+                                       cw+=p
+                               curlen = get_way_len(cw)
+                               if minimal_len==None or curlen < minimal_len: 
+                                       minimal_len=curlen
+                                       minimal_way=cw
+                                       minimal_way_type=w
+                       
+                       return minimal_way
+
+               
+               def print_dxfpoints(points):
+                       gcode=""
+                       for point in points:
+                               gcode +="(drilling dxfpoint)\nG00 Z%f\nG00 X%f Y%f\nG01 Z%f F%f\nG04 P%f\nG00 Z%f\n" % (self.options.Zsafe,point[0],point[1],self.Zcoordinates[layer][1],self.tools[layer][0]["penetration feed"],0.2,self.options.Zsafe) 
+#                      print_(("got dxfpoints array=",points))
+                       return gcode
+                       
+               if self.selected_paths == {} and self.options.auto_select_paths:
+                       paths=self.paths
+                       self.error(_("No paths are selected! Trying to work on all available paths."),"warning")
+               else :
+                       paths = self.selected_paths
+               self.check_dir() 
+               gcode = ""
+
+               biarc_group = inkex.etree.SubElement( self.selected_paths.keys()[0] if len(self.selected_paths.keys())>0 else self.layers[0], inkex.addNS('g','svg') )
+               print_(("self.layers=",self.layers))
+               print_(("paths=",paths))
+               for layer in self.layers :
+#                      print_(("processing layer",layer," of layers:",self.layers))
+                       if layer in paths :
+#                              print_(("layer ",layer, " is in paths:",paths))
+                               print_(("layer",layer))
+                               self.set_tool(layer)
+                               p = []  
+                               dxfpoints = []
+                               for path in paths[layer] :
+                                       if "d" not in path.keys() : 
+                                               self.error(_("Warning: One or more paths dont have 'd' parameter, try to Ungroup (Ctrl+Shift+G) and Object to Path (Ctrl+Shift+C)!"),"selection_contains_objects_that_are_not_paths")
+                                               continue                                        
+                                       csp = cubicsuperpath.parsePath(path.get("d"))
+                                       csp = self.apply_transforms(path, csp)
+                                       if path.get("dxfpoint") == "1":
+                                               tmp_curve=self.transform_csp(csp, layer)
+                                               x=tmp_curve[0][0][0][0]
+                                               y=tmp_curve[0][0][0][1]
+                                               print_("got dxfpoint (scaled) at (%f,%f)" % (x,y))
+                                               dxfpoints += [[x,y]]
+                                       else:
+                                               p += csp
+                               dxfpoints=sort_dxfpoints(dxfpoints)
+                               gcode+=print_dxfpoints(dxfpoints)
+                               curve = self.parse_curve(p, layer)
+                               self.draw_curve(curve, layer, biarc_group)
+                               if self.tools[layer][0]["depth step"] == 0 : self.tools[layer][0]["depth step"] = 1
+                               for step in range( 0,  int(math.ceil( abs( (self.Zcoordinates[layer][1]-self.Zcoordinates[layer][0])/self.tools[layer][0]["depth step"] )) ) ):
+                                       Zpos = max(             self.Zcoordinates[layer][1],             self.Zcoordinates[layer][0] - abs(self.tools[layer][0]["depth step"]*(step+1)) )
+                                       gcode += self.generate_gcode(curve, layer, Zpos)
+                       
+               self.export_gcode(gcode)
+       
+################################################################################
+###
+###            dxfpoints
+###
+################################################################################
+       def dxfpoints(self):
+               if self.selected_paths == {}:
+                       self.error(_("Noting is selected. Please select something to convert to drill point (dxfpoint) or clear point sign."),"warning")
+               for layer in self.layers :
+                       if layer in self.selected_paths :
+                               for path in self.selected_paths[layer]:
+#                                      print_(("processing path",path.get('d')))
+                                       if self.options.dxfpoints_action == 'replace':
+#                                              print_("trying to set as dxfpoint")
+                                               
+                                               path.set("dxfpoint","1")
+                                               r = re.match("^\s*.\s*(\S+)",path.get("d"))
+                                               if r!=None:
+                                                       print_(("got path=",r.group(1)))
+                                                       path.set("d","m %s 2.9375,-6.343750000001 0.8125,1.90625 6.843748640396,-6.84374864039 0,0 0.6875,0.6875 -6.84375,6.84375 1.90625,0.812500000001 z" % r.group(1))
+                                                       path.set("style",styles["dxf_points"])
+
+                                       if self.options.dxfpoints_action == 'save':
+                                               path.set("dxfpoint","1")
+
+                                       if self.options.dxfpoints_action == 'clear' and path.get("dxfpoint") == "1":
+                                               path.set("dxfpoint","0")
+#                                              for id, node in self.selected.iteritems():
+#                                                      print_((id,node,node.attrib))
+
+
+################################################################################
+###
+###            Artefacts
+###
+################################################################################
+       def area_artefacts(self) :
+                       if self.selected_paths == {} and self.options.auto_select_paths:
+                               paths=self.paths
+                               self.error(_("No paths are selected! Trying to work on all available paths."),"warning")
+                       else :
+                               paths = self.selected_paths
+                       for layer in paths :
+#                              paths[layer].reverse() # Reverse list of paths to leave their order 
+                               for path in paths[layer] :
+                                       parent = path.getparent()
+                                       style = path.get("style") if "style" in path.keys() else ""
+                                       if "d" not in path.keys() : 
+                                               self.error(_("Warning: One or more paths dont have 'd' parameter, try to Ungroup (Ctrl+Shift+G) and Object to Path (Ctrl+Shift+C)!"),"selection_contains_objects_that_are_not_paths")
+                                               continue                
+                                       csp = cubicsuperpath.parsePath(path.get("d"))
+                                       csp = self.apply_transforms(path, csp)
+                                       for subpath in csp :
+                                               bounds = csp_simple_bound([subpath])
+                                               if  (bounds[2]-bounds[0])**2+(bounds[3]-bounds[1])**2 < self.options.area_find_artefacts_diameter**2:
+                                                       if self.options.area_find_artefacts_action == "mark with an arrow" :
+                                                               inkex.etree.SubElement(parent, inkex.addNS('path','svg'), 
+                                                                               {
+                                                                                       'd': 'm %s,%s 2.9375,-6.343750000001 0.8125,1.90625 6.843748640396,-6.84374864039 0,0 0.6875,0.6875 -6.84375,6.84375 1.90625,0.812500000001 z' % (subpath[0][1][0],subpath[0][1][1]),
+                                                                                       'style': styles["area artefact arrow"],
+                                                                                       'gcodetools': 'area artefact arrow',
+                                                                               })
+                                                               inkex.etree.SubElement(parent, inkex.addNS('path','svg'), {'d': cubicsuperpath.formatPath([subpath]), 'style': style, "gcodetools_parameter":"area artefact"})          
+                                                       elif self.options.area_find_artefacts_action == "mark with style" :
+                                                               inkex.etree.SubElement(parent, inkex.addNS('path','svg'), {'d': cubicsuperpath.formatPath([subpath]), 'style': styles["area artefact"]})
+                                                       elif self.options.area_find_artefacts_action == "delete" :
+                                                               print_("Deleted artifact %s" % subpath )
+                                               else :
+                                                       inkex.etree.SubElement(parent, inkex.addNS('path','svg'), {'d': cubicsuperpath.formatPath([subpath]), 'style': style})
+                                       parent.remove(path)
+                       return
+                       
+
+################################################################################
+###
+###            Calculate area curves
+###
+################################################################################
+       def area(self) :
+               if len(self.selected_paths)<=0:
+                       self.error(_("This extension requires at least one selected path."),"warning")
+                       return
+               for layer in self.layers :
+                       if layer in self.selected_paths :
+                               self.set_tool(layer)
+                               if self.tools[layer][0]['diameter']<=0 : 
+                                       self.error(_("Tool diameter must be > 0 but tool's diameter on '%s' layer is not!") % layer.get(inkex.addNS('label','inkscape')),"area_tools_diameter_error")
+       
+                               for path in self.selected_paths[layer]:
+                                       print_(("doing path",   path.get("style"), path.get("d")))
+
+                                       area_group = inkex.etree.SubElement( path.getparent(), inkex.addNS('g','svg') )
+                               
+                                       d = path.get('d')
+                                       print_(d)
+                                       if d==None:
+                                               print_("omitting non-path")
+                                               self.error(_("Warning: omitting non-path"),"selection_contains_objects_that_are_not_paths")
+                                               continue
+                                       csp = cubicsuperpath.parsePath(d)
+                               
+                                       if path.get(inkex.addNS('type','sodipodi'))!="inkscape:offset":
+                                               print_("Path %s is not an offset. Preparation started." % path.get("id"))
+                                               # Path is not offset. Preparation will be needed.
+                                               # Finding top most point in path (min y value)
+                                               
+                                               min_x,min_y,min_i,min_j,min_t = csp_true_bounds(csp)[1]
+                                               
+                                               # Reverse path if needed.
+                                               if min_y!=float("-inf") :
+                                                       # Move outline subpath to the begining of csp
+                                                       subp = csp[min_i]
+                                                       del csp[min_i]
+                                                       j = min_j
+                                                       # Split by the topmost point and join again
+                                                       if min_t in [0,1]:
+                                                               if min_t == 0: j=j-1
+                                                               subp[-1][2], subp[0][0] = subp[-1][1], subp[0][1]
+                                                               subp = [ [subp[j][1], subp[j][1], subp[j][2]] ] + subp[j+1:] + subp[:j] + [ [subp[j][0], subp[j][1], subp[j][1]] ]
+                                                       else:                                                   
+                                                               sp1,sp2,sp3 = csp_split(subp[j-1],subp[j],min_t)
+                                                               subp[-1][2], subp[0][0] = subp[-1][1], subp[0][1]
+                                                               subp = [ [ sp2[1], sp2[1],sp2[2] ] ] + [sp3] + subp[j+1:] + subp[:j-1] + [sp1] + [[ sp2[0], sp2[1],sp2[1] ]]                                              
+                                                       csp = [subp] + csp
+                                                       # reverse path if needed
+                                                       if csp_subpath_ccw(csp[0]) :
+                                                               for i in range(len(csp)):
+                                                                       n = []
+                                                                       for j in csp[i]:
+                                                                               n = [  [j[2][:],j[1][:],j[0][:]]  ] + n
+                                                                       csp[i] = n[:]
+
+                                                       
+                                               d = cubicsuperpath.formatPath(csp)
+                                               print_(("original  d=",d))
+                                               d = re.sub(r'(?i)(m[^mz]+)',r'\1 Z ',d)
+                                               d = re.sub(r'(?i)\s*z\s*z\s*',r' Z ',d)
+                                               d = re.sub(r'(?i)\s*([A-Za-z])\s*',r' \1 ',d)
+                                               print_(("formatted d=",d))
+                                       # scale = sqrt(Xscale**2 + Yscale**2) / sqrt(1**2 + 1**2)
+                                       p0 = self.transform([0,0],layer)
+                                       p1 = self.transform([0,1],layer)
+                                       scale = (P(p0)-P(p1)).mag()
+                                       if scale == 0 : scale = 1. 
+                                       else : scale = 1./scale
+                                       print_(scale)
+                                       tool_d = self.tools[layer][0]['diameter']*scale
+                                       r = self.options.area_inkscape_radius * scale
+                                       sign=1 if r>0 else -1
+                                       print_("Tool diameter = %s, r = %s" % (tool_d, r))
+
+                                       for i in range(self.options.max_area_curves):
+                                               radius = - tool_d * (i+0.5) * sign
+                                               if abs(radius)>abs(r): 
+                                                       radius = -r
+                                               
+                                               inkex.etree.SubElement( area_group, inkex.addNS('path','svg'), 
+                                                                               {
+                                                                                        inkex.addNS('type','sodipodi'):        'inkscape:offset',
+                                                                                        inkex.addNS('radius','inkscape'):      str(radius),
+                                                                                        inkex.addNS('original','inkscape'):    d,
+                                                                                       'style': styles["biarc_style_i"]['area']
+                                                                               })
+                                               print_(("adding curve",area_group,d,styles["biarc_style_i"]['area']))
+                                               if radius == -r : break 
+
+
+################################################################################
+###
+###            Engraving
+###
+################################################################################
+       def engraving(self) :
+               if len(self.selected_paths)<=0:
+                       self.error(_("This extension requires at least one selected path."),"warning")
+                       return
+               if not self.check_dir() : return
+               gcode = ''
+
+               def find_cutter_center((x1,y1),(nx1,ny1), sp1,sp2, tool, t3 = .5):
+                       ####################################################################
+                       ###             To find center of cutter a system of non linear equations 
+                       ###             will be solved using Newton's method 
+                       ####################################################################
+                       bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
+                       ax,ay,bx,by,cx,cy,dx,dy=bezmisc.bezierparameterize(bez)
+                       fx=ax*(t3*t3*t3)+bx*(t3*t3)+cx*t3+dx
+                       fy=ay*(t3*t3*t3)+by*(t3*t3)+cy*t3+dy
+               
+                       nx2,ny2 = csp_normalized_normal(sp1,sp2,t3)                     
+                       intersection, t1, t2 = straight_segments_intersection([[x1,y1],[x1+nx1,y1+ny1]],[[fx,fy],[fx+nx2,fy+ny2]], False)
+                       if not intersection  or intersection == "Overlap" :
+                               if nx1!=0 :
+                                       t1 = t2 = (x1-fx)/nx1
+                               else :
+                                       t1 = t2 = (y1-fy)/ny1
+                                       
+                       t = [ t1, t2, t3 ]                                      
+                       i = 0
+                       F = [0.,0.,0.]
+                       F1 = [[0.,0.,0.],[0.,0.,0.],[0.,0.,0.]]
+                       while i==0 or abs(F[0])+abs(F[1])+math.sqrt(abs(F[2])) >engraving_tolerance and i<10:
+                               t1,t2,t3 = t[0],t[1],t[2]
+                               fx=ax*(t3*t3*t3)+bx*(t3*t3)+cx*t3+dx
+                               fy=ay*(t3*t3*t3)+by*(t3*t3)+cy*t3+dy
+                               f1x=3*ax*(t3*t3)+2*bx*t3+cx
+                               f1y=3*ay*(t3*t3)+2*by*t3+cy
+                               i+=1
+                               
+                               tx = fx-x1-nx1*t1
+                               ty = fy-y1-ny1*t1
+                               
+                               F[0] = x1+nx1*t1-fx+t2*f1y
+                               F[1] = y1+ny1*t1-fy-t2*f1x
+                               F[2] = t1*t1 - tx*tx -ty*ty                                     
+                               
+                               F1[0][0] = nx1
+                               F1[0][1] = f1y
+                               F1[0][2] = -f1x+t2*(6*ay*t3+2*by)
+                               
+                               F1[1][0] = ny1
+                               F1[1][1] = -f1x
+                               F1[1][2] = -f1y-t2*(6*ax*t3+2*bx)
+                               
+                               F1[2][0] = 2*t1+2*nx1*tx +2*ny1*ty
+                               F1[2][1] = 0
+                               F1[2][2] = -2*f1x*tx -2*f1y*ty
+
+                               F1 = inv_3x3(F1)
+                       
+                               if (     isnan(F[0]) or isnan(F[1]) or isnan(F[2]) or 
+                                                isinf(F[0]) or isinf(F[1]) or  isinf(F[2]) ):
+                                       return t+[1e100,i]      
+                       
+                               if F1!= None :
+                                       t[0] -=  F1[0][0]*F[0] + F1[0][1]*F[1] + F1[0][2]*F[2]
+                                       t[1] -=  F1[1][0]*F[0] + F1[1][1]*F[1] + F1[1][2]*F[2]
+                                       t[2] -=  F1[2][0]*F[0] + F1[2][1]*F[1] + F1[2][2]*F[2]
+                               else: break     
+                               
+                       return t+[abs(F[0])+abs(F[1])+math.sqrt(abs(F[2])),i]
+               cspe =[]
+               we = []
+               for layer in self.layers :
+                       if layer in self.selected_paths : 
+                               self.set_tool(layer)
+                               engraving_group = inkex.etree.SubElement( self.selected_paths[layer][0].getparent(), inkex.addNS('g','svg') )
+                               for node in self.selected_paths[layer] :        
+                                       if node.tag == inkex.addNS('path','svg'):
+                                               cspi = cubicsuperpath.parsePath(node.get('d'))
+
+                                               for j in xrange(len(cspi)):
+                                                       # Remove zerro length segments
+                                                       i = 1
+                                                       while i<len(cspi[j]):
+                                                               if abs(cspi[j][i-1][1][0]-cspi[j][i][1][0])<engraving_tolerance and abs(cspi[j][i-1][1][1]-cspi[j][i][1][1])<engraving_tolerance:
+                                                                       cspi[j][i-1][2] = cspi[j][i][2]
+                                                                       del cspi[j][i]
+                                                               else:
+                                                                       i += 1
+                                               for csp in cspi:
+                                                       #       Create list containing normlas and points
+                                                       nl = []
+                                                       for i in range(1,len(csp)):
+                                                               n, n1 = [], []
+                                                               sp1, sp2 = csp[i-1], csp[i]
+                                                               for ti in [.0,.25,.75,1.]:
+                                                                       #       Is following string is nedded or not??? (It makes t depend on form of the curve) 
+                                                                       #ti = bezmisc.beziertatlength(bez,ti)   
+                                                                       x1,y1 = csp_at_t(sp1,sp2,ti)
+                                                                       nx,ny = csp_normalized_normal(sp1,sp2,ti)
+                                                                       n+=[ [ [x1,y1], [nx,ny], False, False, i] ] # [point coordinates, normal, is an inner corner, is an outer corner, csp's index]
+                                                                       if ti==1 and i<len(csp)-1:
+                                                                               nx2, ny2 = csp_normalized_slope(csp[i],csp[i+1],0)
+                                                                               nx2,ny2 = -ny2,nx2
+                                                                               ang = ny2*nx-ny*nx2
+                                                                               ang1 = 180-math.acos(max(-1,min(1,nx*nx2+ny*ny2)))*180/math.pi
+                                                                               if ang > 0  and ang1 < self.options.engraving_sharp_angle_tollerance :  # inner angle
+                                                                                       n[-1][2] = True
+                                                                               elif ang < 0 and ang1 < self.options.engraving_sharp_angle_tollerance :                                 # outer angle
+                                                                                       a = -math.acos(nx*nx2+ny*ny2)
+                                                                                       for t in [.0,.25,.75,1.]:
+                                                                                               n1 += [ [ [x1,y1], [nx*math.cos(a*t)-ny*math.sin(a*t),nx*math.sin(a*t)+ny*math.cos(a*t)], False, True, i ]  ]
+                                                               nl += [ n ] + ([ n1 ] if n1!=[] else [])
+                                                       # Modify first/last points if curve is closed
+                                                       if abs(csp[-1][1][0]-csp[0][1][0])<engraving_tolerance and abs(csp[-1][1][1]-csp[0][1][1])<engraving_tolerance :
+                                                               x1,y1 = csp_at_t(csp[-2],csp[-1],1)
+                                                               nx,ny = csp_normalized_slope(csp[-2],csp[-1],1)
+                                                               nx,ny = -ny,nx
+                                                               nx2,ny2 = csp_normalized_slope(csp[0],csp[1],0)
+                                                               nx2,ny2 = -ny2,nx2
+                                                               ang = ny2*nx-ny*nx2
+                                                               if ang > 0  and 180-math.acos(nx*nx2+ny*ny2)*180/math.pi < self.options.engraving_sharp_angle_tollerance :      # inner angle
+                                                                       nl[-1][-1][2] = True
+                                                               elif ang < 0 and 180-math.acos(nx*nx2+ny*ny2)*180/math.pi < self.options.engraving_sharp_angle_tollerance :                                     # outer angle
+                                                                       a = -math.acos(nx*nx2+ny*ny2)
+                                                                       n1 = []
+                                                                       for t in [.0,.25,.75,1.]:
+                                                                               n1 += [ [ [x1,y1], [nx*math.cos(a*t)-ny*math.sin(a*t),nx*math.sin(a*t)+ny*math.cos(a*t)], False, True, i ]  ]
+                                                                       nl += [ n1 ] 
+
+
+                                                       print_(("engraving_draw_calculation_paths=",self.options.engraving_draw_calculation_paths))
+                                                       if self.options.engraving_draw_calculation_paths==True:
+                                                               for i in nl:
+                                                                       for p in i:
+                                                                               inkex.etree.SubElement( engraving_group, inkex.addNS('path','svg'), 
+                                                                                       {
+                                                                                                "d":   "M %f,%f L %f,%f" %(p[0][0],p[0][1],p[0][0]+p[1][0]*10,p[0][1]+p[1][1]*10),
+                                                                                               'style':        "stroke:#0000ff; stroke-opacity:0.46; stroke-width:0.1; fill:none",
+                                                                                               "gcodetools": "Engraving calculation paths"
+                                                                                       })                              
+
+
+                                                       #       Calculate offset points 
+                                                       csp_points = [] 
+                                                       for ki in xrange(len(nl)):
+                                                               p = []
+                                                               for ti in xrange(3) if ki!=len(nl)-1 else xrange(4):
+                                                                       n = nl[ki][ti]
+                                                                       x1,y1 = n[0]
+                                                                       nx,ny = n[1]
+                                                                       d, r = 0, float("inf")
+                                                                       if ti==0 and nl[ki-1][-1][2] == True    or              ti==3 and nl[ki][ti][2] == True:
+                                                                               # Point is a sharp angle r=0p
+                                                                               r = 0
+                                                                       else :
+                                                                               for j in xrange(0,len(cspi)):
+                                                                                       for i in xrange(1,len(cspi[j])):
+                                                                                               d = csp_bound_to_point_distance(cspi[j][i-1], cspi[j][i], [x1,y1])
+                                                                                               if d >= self.options.engraving_max_dist*2 :
+                                                                                                       r = min(math.sqrt(d/2),r) 
+                                                                                                       continue
+                                                                                               for n1 in xrange(self.options.engraving_newton_iterations):
+                                                                                                       t = find_cutter_center((x1,y1),(nx,ny), cspi[j][i-1], cspi[j][i], self.tools[layer][0], float(n1)/(self.options.engraving_newton_iterations-1))
+                                                                                                       print_(t)
+                                                                                                       if t[0] > engraving_tolerance and 0<=t[2]<=1 and abs(t[3])<engraving_tolerance:
+                                                                                                               print_("!@#!@#!@#!@#!@",t)
+                                                                                                               t3 = t[2]
+                                                                                                               ax,ay,bx,by,cx,cy,dx,dy=bezmisc.bezierparameterize((cspi[j][i-1][1],cspi[j][i-1][2],cspi[j][i][0],cspi[j][i][1]))
+                                                                                                               x2=ax*(t3*t3*t3)+bx*(t3*t3)+cx*t3+dx
+                                                                                                               y2=ay*(t3*t3*t3)+by*(t3*t3)+cy*t3+dy
+                                                                                                               if abs(x2-x1)<engraving_tolerance and abs(y2-y1)<engraving_tolerance:
+                                                                                                                       f1x = 3*ax*(t3*t3)+2*bx*t3+cx
+                                                                                                                       f1y = 3*ay*(t3*t3)+2*by*t3+cy
+                                                                                                                       f2x = 6*ax*t3+2*bx
+                                                                                                                       f2y = 6*ay*t3+2*by
+                                                                                                                       d = f1x*f2y-f1y*f2x
+                                                                                                                       # d = curvature
+                                                                                                                       if d!=0 :
+                                                                                                                               d = math.sqrt((f1x*f1x+f1y*f1y)**3)/d
+                                                                                                                               if d>0:
+                                                                                                                                       r = min( d,r) if r!=None else d
+                                                                                                                               else :
+                                                                                                                                       r = min(r,self.options.engraving_max_dist) if r!=None else self.options.engraving_max_dist
+                                                                                                               else:                                           
+                                                                                                                       r = min(t[0],r) if r!=None else t[0]    
+                                                                               for j in xrange(0,len(cspi)):
+                                                                                       for i in xrange(0,len(cspi[j])):
+                                                                                               x2,y2 = cspi[j][i][1]
+                                                                                               if (abs(x1-x2)>engraving_tolerance or abs(y1-y2)>engraving_tolerance ) and (x2*nx - x1*nx + y2*ny - y1*ny) != 0:
+                                                                                                       t1 = .5 * ( (x1-x2)**2+(y1-y2)**2 ) /  (x2*nx - x1*nx + y2*ny - y1*ny)
+                                                                                                       if t1>0 : r = min(t1,r) if r!=None else t1
+                                                                       if self.options.engraving_draw_calculation_paths==True:
+                                                                               inkex.etree.SubElement( engraving_group, inkex.addNS('path','svg'), 
+                                                                                               {"gcodetools": "Engraving calculation paths", 'style':  "fill:#ff00ff; fill-opacity:0.46; stroke:#000000; stroke-width:0.1;", inkex.addNS('cx','sodipodi'):             str(x1+nx*r), inkex.addNS('cy','sodipodi'):             str(y1+ny*r), inkex.addNS('rx','sodipodi'):     str(1), inkex.addNS('ry','sodipodi'): str(1), inkex.addNS('type','sodipodi'):   'arc'}) 
+                                                                               inkex.etree.SubElement( engraving_group, inkex.addNS('path','svg'), 
+                                                                                               {"gcodetools": "Engraving calculation paths", 'style':  "fill:none; fill-opacity:0.46; stroke:#000000; stroke-width:0.1;", inkex.addNS('cx','sodipodi'):                str(x1+nx*r),  inkex.addNS('cy','sodipodi'):            str(y1+ny*r),inkex.addNS('rx','sodipodi'):              str(r), inkex.addNS('ry','sodipodi'):           str(r), inkex.addNS('type','sodipodi'): 'arc'})
+                                                                       r = min(r, self.options.engraving_max_dist)
+                                                                       w = min(r, self.tools[layer][0]['diameter'])
+                                                                       p += [ [x1+nx*w,y1+ny*w,r,w] ]
+                                                       
+                                               
+                                                       
+                                                               if len(csp_points)>0 : csp_points[-1] += [p[0]]                                                                 
+                                                               csp_points += [ p ]
+                                                       #       Splitting path to pieces each of them not further from path more than engraving_max_dist
+                                                       engraving_path = [ [] ]
+                                                       for p_ in csp_points :
+                                                               for p in p_:
+                                                                       if p[2]<self.options.engraving_max_dist : break
+                                                               if p[2]<self.options.engraving_max_dist: engraving_path[-1] += [p_]
+                                                               else : 
+                                                                       if engraving_path[-1] != [] : engraving_path += [ [] ]
+                                                       if engraving_path[-1] == [] : del engraving_path[-1] 
+                                       
+                                       
+                                                       for csp_points in engraving_path :
+                                                               #       Create Path that goes through this points 
+                                                               cspm = []
+                                                               w = []
+                                                               m = [[0.0, 0.0, 0.0, 1.0], [0.015625, 0.140625, 0.421875, 0.421875], [0.421875, 0.421875, 0.140625, 0.015625], [1.0, 0.0, 0.0, 0.0]]
+                                                               for p in csp_points:
+                                                                       m = numpy.array(m)
+                                                                       xi = numpy.array( [p[i][:2] for i in range(4)])
+                                                                       sp1,sp2 = [[0.,0.],[0.,0.],[0.,0.]], [[0.,0.],[0.,0.],[0.,0.]]
+                                                                       a,b,c,d = numpy.linalg.solve(m, xi).tolist()
+                                                                       sp1[1], sp1[0] = d, d
+                                                                       sp1[2] = c
+                                                                       sp2[0] = b
+                                                                       sp2[1], sp2[2] = a, a
+                                                                       sp3,sp4,sp5 = csp_split(sp1, sp2, .25)
+                                                                       l = cspseglength(sp3,sp4)
+                                                                       sp1,sp2,sp4 = csp_split(sp1, sp2, .75)
+                                                                       l1 = cspseglength(sp1,sp2)
+                                                                       if l1!=0:
+                                                                               sp1,sp2,sp3 = csp_splitatlength(sp1, sp2, l/l1)
+                                                                               if len(cspm)>0 :
+                                                                                       cspm[-1][2] = sp1[2]
+                                                                                       cspm += [sp2[:], sp3[:], sp4[:]]
+                                                                                       w +=  [p[i][3] for i in range(1,4)] 
+                                                                               else :
+                                                                                       cspm += [sp1[:], sp2[:], sp3[:], sp4[:]]        
+                                                                                       w += [p[i][3] for i in range(4)]
+                                                               if self.options.engraving_draw_calculation_paths==True:
+                                                                       node =  inkex.etree.SubElement( engraving_group, inkex.addNS('path','svg'),                                                                             {
+                                                                                                                        "d":    cubicsuperpath.formatPath([cspm]),
+                                                                                                                       'style':        styles["biarc_style_i"]['biarc1'],
+                                                                                                                       "gcodetools": "Engraving calculation paths",
+                                                                                                               })
+                                                                       for i in xrange(len(cspm)):
+                                                                               inkex.etree.SubElement( engraving_group, inkex.addNS('path','svg'), 
+                                                                                               {"gcodetools": "Engraving calculation paths", 'style':  "fill:none; fill-opacity:0.46; stroke:#000000; stroke-width:0.1;", inkex.addNS('cx','sodipodi'):                str(cspm[i][1][0]),  inkex.addNS('cy','sodipodi'):              str(cspm[i][1][1]),inkex.addNS('rx','sodipodi'):                str(w[i]), inkex.addNS('ry','sodipodi'):                str(w[i]), inkex.addNS('type','sodipodi'):      'arc'})
+                                                               cspe += [cspm]
+                                                               we   += [w]                             
+
+                               if self.tools[layer][0]['shape'] != "":
+                                       f = eval('lambda w: ' + self.tools[layer][0]['shape'].strip('"'))
+                               else: 
+                                       self.error(_("Tool '%s' has no shape!") % self.tools[layer][0]['name'],"engraving_tools_shape_error")
+                                       f = lambda w: w
+               
+                               if cspe!=[]:
+                                       curve = self.parse_curve(cspe, layer, we, f)
+                                       self.draw_curve(curve, layer, engraving_group)
+                                       gcode += self.generate_gcode(curve, layer, self.options.Zsurface)
+
+               if gcode!='' :
+                       self.export_gcode(gcode)
+               else :  self.error(_("No need to engrave sharp angles."),"warning")
+
+
+################################################################################
+###
+###            Orientation
+###
+################################################################################
+       def orientation(self, layer=None) :
+               print_("entering orientations")
+               if layer == None :
+                       layer = self.current_layer if self.current_layer is not None else self.document.getroot()
+               if layer in self.orientation_points:
+                       self.error(_("Active layer already has orientation points! Remove them or select another layer!"),"active_layer_already_has_orientation_points")
+               
+               orientation_group = inkex.etree.SubElement(layer, inkex.addNS('g','svg'), {"gcodetools":"Gcodetools orientation group"})
+               doc_height = inkex.unittouu(self.document.getroot().get('height'))
+               if self.document.getroot().get('height') == "100%" :
+                       doc_height = 1052.3622047
+                       print_("Overruding height from 100 percents to %s" % doc_height)
+               if self.options.unit == "G21 (All units in mm)" : 
+                       points = [[0.,0.,self.options.Zsurface],[100.,0.,self.options.Zdepth],[0.,100.,0.]]
+                       orientation_scale = 3.5433070660
+                       print_("orientation_scale < 0 ===> switching to mm units=%0.10f"%orientation_scale )
+               elif self.options.unit == "G20 (All units in inches)" :
+                       points = [[0.,0.,self.options.Zsurface],[5.,0.,self.options.Zdepth],[0.,5.,0.]]
+                       orientation_scale = 90
+                       print_("orientation_scale < 0 ===> switching to inches units=%0.10f"%orientation_scale )
+               if self.options.orientation_points_count == 2 :
+                       points = points[:2]
+               print_(("using orientation scale",orientation_scale,"i=",points))
+               for i in points :
+                       si = [i[0]*orientation_scale, i[1]*orientation_scale]
+                       g = inkex.etree.SubElement(orientation_group, inkex.addNS('g','svg'), {'gcodetools': "Gcodetools orientation point (%s points)" % self.options.orientation_points_count})
+                       inkex.etree.SubElement( g, inkex.addNS('path','svg'), 
+                               {
+                                       'style':        "stroke:none;fill:#000000;",    
+                                       'd':'m %s,%s 2.9375,-6.343750000001 0.8125,1.90625 6.843748640396,-6.84374864039 0,0 0.6875,0.6875 -6.84375,6.84375 1.90625,0.812500000001 z z' % (si[0], -si[1]+doc_height),
+                                       'gcodetools': "Gcodetools orientation point arrow"
+                               })
+                       t = inkex.etree.SubElement(     g, inkex.addNS('text','svg'), 
+                               {
+                                       'style':        "font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;",
+                                       inkex.addNS("space","xml"):"preserve",
+                                       'x':    str(si[0]+10),
+                                       'y':    str(-si[1]-10+doc_height),
+                                       'gcodetools': "Gcodetools orientation point text"
+                               })
+                       t.text = "(%s; %s; %s)" % (i[0],i[1],i[2])
+       
+               
+################################################################################
+###
+###            Tools library
+###
+################################################################################
+       def tools_library(self, layer=None) :
+               # Add a tool to the drawing
+               if layer == None :
+                       layer = self.current_layer if self.current_layer is not None else self.document.getroot()
+               if layer in self.tools:
+                       self.error(_("Active layer already has a tool! Remove it or select another layer!"),"active_layer_already_has_tool")
+
+               if self.options.tools_library_type == "cylinder cutter" :
+                       tool = {
+                                       "name": "Cylindrical cutter",
+                                       "id": "Cylindrical cutter 0001",
+                                       "diameter":10,
+                                       "penetration angle":90,
+                                       "feed":"400",
+                                       "penetration feed":"100",
+                                       "depth step":"1",
+                                       "tool change gcode":" "
+                       }
+               elif self.options.tools_library_type == "lathe cutter" :
+                       tool = {
+                                       "name": "Lathe cutter",
+                                       "id": "Lathe cutter 0001",
+                                       "diameter":10,
+                                       "penetration angle":90,
+                                       "feed":"400",
+                                       "passing feed":"800",
+                                       "fine feed":"100",
+                                       "penetration feed":"100",
+                                       "depth step":"1",
+                                       "tool change gcode":" "
+                       }
+               elif self.options.tools_library_type == "cone cutter":  
+                       tool = {
+                                       "name": "Cone cutter",
+                                       "id": "Cone cutter 0001",
+                                       "diameter":10,
+                                       "shape":"w",
+                                       "feed":"400",
+                                       "penetration feed":"100",
+                                       "depth step":"1",
+                                       "tool change gcode":" "
+                       }
+               elif self.options.tools_library_type == "tangent knife":        
+                       tool = {
+                                       "name": "Tangent knife",
+                                       "id": "Tangent knife 0001",
+                                       "feed":"400",
+                                       "penetration feed":"100",
+                                       "depth step":"100",
+                                       "4th axis meaning": "tangent knife",
+                                       "4th axis scale": 1.,
+                                       "4th axis offset": 0,
+                                       "tool change gcode":" "
+                       }
+                       
+               elif self.options.tools_library_type == "plasma cutter":        
+                       tool = {
+                               "name": "Plasma cutter",
+                               "id": "Plasma cutter 0001",
+                               "diameter":10,
+                               "penetration feed":100,
+                               "feed":400,
+                               "gcode before path":"""G31 Z-100 F500 (find metal)
+G92 Z0 (zerro z)
+G00 Z10 F500 (going up)
+M03 (turn on plasma)
+G04 P0.2 (pause)
+G01 Z1 (going to cutting z)\n""",
+                               "gcode after path":"M05 (turn off plasma)\n",
+                       }
+               else :
+                       tool = self.default_tool
+               
+               tool_num = sum([len(self.tools[i]) for i in self.tools])
+               colors = ["00ff00","0000ff","ff0000","fefe00","00fefe", "fe00fe", "fe7e00", "7efe00", "00fe7e", "007efe", "7e00fe", "fe007e"]
+               
+               tools_group = inkex.etree.SubElement(layer, inkex.addNS('g','svg'), {'gcodetools': "Gcodetools tool defenition"})
+               bg = inkex.etree.SubElement(    tools_group, inkex.addNS('path','svg'), 
+                                       {'style':       "fill:#%s;fill-opacity:0.5;stroke:#444444; stroke-width:1px;"%colors[tool_num%len(colors)], "gcodetools":"Gcodetools tool background"})
+
+               y = 0
+               keys = []
+               for key in self.tools_field_order:
+                       if key in tool: keys += [key]
+               for key in tool:
+                       if key not in keys: keys += [key]
+               for key in keys :
+                       g = inkex.etree.SubElement(tools_group, inkex.addNS('g','svg'), {'gcodetools': "Gcodetools tool parameter"})
+               
+                       t = inkex.etree.SubElement(     g, inkex.addNS('text','svg'), 
+                                       {
+                                               'style':        ("font-size:10px;" if key!="name" else "font-size:20px;") +     "font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;",
+                                               inkex.addNS("space","xml"):"preserve",                                                  
+                                               'x':    str(0),
+                                               'y':    str(y),
+                                               'gcodetools': "Gcodetools tool defention field name"
+                                       })
+                       t.text = str(key)
+                       v = str(tool[key]).split("\n")
+                       t = inkex.etree.SubElement(     g, inkex.addNS('text','svg'), 
+                                       {
+                                               'style':        ("font-size:10px;" if key!="name" else "font-size:20px;") + "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;",
+                                               'x':    str(150),
+                                               inkex.addNS("space","xml"):"preserve",
+                                               'y':    str(y),
+                                               'gcodetools': "Gcodetools tool defention field value"
+                                       })
+                       for s in v :
+                               span = inkex.etree.SubElement( t, inkex.addNS('tspan','svg'), 
+                                       {
+                                               'x':    str(150),
+                                               'y':    str(+y),
+                                               inkex.addNS("role","sodipodi"):"line",
+                                               'gcodetools': "Gcodetools tool defention field value"
+                                       })                                      
+                               y += 15 if key!='name' else 20
+                               span.text = s
+               bg.set('d',"m -20,-20 l 400,0 0,%f -400,0 z " % (y+50))
+               tool = []
+               tools_group.set("transform", simpletransform.formatTransform([ [1,0,self.view_center[0]-150 ], [0,1,self.view_center[1]] ] ))
+
+
+################################################################################
+###
+###            Check tools and OP asignment
+###
+################################################################################
+       def check_tools_and_op(self):
+               if len(self.selected)<=0 :
+                       self.error(_("Selection is empty! Will compute whole drawing."),"selection_is_empty_will_comupe_drawing")
+                       paths = self.paths
+               else :
+                       paths = self.selected_paths
+               #       Set group
+               group = inkex.etree.SubElement( self.selected_paths.keys()[0] if len(self.selected_paths.keys())>0 else self.layers[0], inkex.addNS('g','svg') )
+               trans_ = [[1,0.3,0],[0,0.5,0]]  
+               self.get_defs()
+               # Add marker to defs if it doesnot exists
+               if "CheckToolsAndOPMarker" not in self.defs : 
+                       defs = inkex.etree.SubElement( self.document.getroot(), inkex.addNS("defs","svg"))
+                       marker = inkex.etree.SubElement( defs, inkex.addNS("marker","svg"), {"id":"CheckToolsAndOPMarker","orient":"auto","refX":"-8","refY":"-2.41063","style":"overflow:visible"})
+                       inkex.etree.SubElement( marker, inkex.addNS("path","svg"), 
+                                       {       "d":"m -6.55552,-2.41063 0,0 L -13.11104,0 c 1.0473,-1.42323 1.04126,-3.37047 0,-4.82126",
+                                               "style": "fill:#000044; fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"       }
+                               )
+               bounds = [float('inf'),float('inf'),float('-inf'),float('-inf')]
+               tools_bounds = {}
+               for layer in self.layers :
+                       if layer in paths :
+                               self.set_tool(layer)
+                               tool = self.tools[layer][0]
+                               tools_bounds[layer] = tools_bounds[layer] if layer in tools_bounds else [float("inf"),float("-inf")]
+                               style = simplestyle.formatStyle(tool["style"])
+                               for path in paths[layer] :
+                                       style = "fill:%s; fill-opacity:%s; stroke:#000044; stroke-width:1; marker-mid:url(#CheckToolsAndOPMarker);" % (
+                                       tool["style"]["fill"] if "fill" in tool["style"] else "#00ff00", 
+                                       tool["style"]["fill-opacity"] if "fill-opacity" in tool["style"] else "0.5")
+                                       group.insert( 0, inkex.etree.Element(path.tag, path.attrib))
+                                       new = group.getchildren()[0]
+                                       new.set("style", style)
+                                       
+                                       trans = self.get_transforms(path)
+                                       trans = simpletransform.composeTransform( trans_, trans if trans != [] else [[1.,0.,0.],[0.,1.,0.]])
+                                       csp = cubicsuperpath.parsePath(path.get("d"))
+                                       simpletransform.applyTransformToPath(trans,csp)
+                                       path_bounds = csp_simple_bound(csp)
+                                       trans = simpletransform.formatTransform(trans)
+                                       bounds = [min(bounds[0],path_bounds[0]), min(bounds[1],path_bounds[1]), max(bounds[2],path_bounds[2]), max(bounds[3],path_bounds[3])]
+                                       tools_bounds[layer] = [min(tools_bounds[layer][0], path_bounds[1]), max(tools_bounds[layer][1], path_bounds[3])] 
+
+                                       new.set("transform", trans) 
+                                       trans_[1][2] += 20
+                               trans_[1][2] += 100
+
+               for layer in self.layers :
+                       if layer in self.tools :
+                               if layer in tools_bounds :
+                                       tool = self.tools[layer][0]
+                                       g = copy.deepcopy(tool["self_group"])
+                                       g.attrib["gcodetools"] = "Check tools and OP asignment"
+                                       trans = [[1,0.3,bounds[2]],[0,0.5,tools_bounds[layer][0]]]      
+                                       g.set("transform",simpletransform.formatTransform(trans))
+                                       group.insert( 0, g )
+
+
+################################################################################
+###            TODO Launch browser on help tab
+################################################################################
+       def help(self):
+               self.error(_("""Tutorials, manuals and support can be found at\nEnglish support forum:\n        http://www.cnc-club.ru/gcodetools\nand Russian support forum:\n http://www.cnc-club.ru/gcodetoolsru"""),"warning")
+               return
+
+
+################################################################################
+###            Lathe
+################################################################################
+       def generate_lathe_gcode(self, subpath, layer, feed_type) :
+               if len(subpath) <2 : return ""
+               feed = " F %f" % self.tool[feed_type] 
+               x,z = self.options.lathe_x_axis_remap, self.options.lathe_z_axis_remap
+               flip_angle = -1 if x.lower()+z.lower() in ["xz", "yx", "zy"] else 1
+               alias = {"X":"I", "Y":"J", "Z":"K", "x":"i", "y":"j", "z":"k"} 
+               i_, k_ = alias[x], alias[z]
+               c = [ [subpath[0][1], "move", 0, 0, 0] ]
+               #csp_draw(self.transform_csp([subpath],layer,True), color = "Orange", width = .1)
+               for sp1,sp2 in zip(subpath,subpath[1:]) :
+                       c += biarc(sp1,sp2,0,0)
+               for i in range(1,len(c)) : # Just in case check end point of each segment
+                       c[i-1][4] = c[i][0][:]
+               c += [ [subpath[-1][1], "end", 0, 0, 0] ]                       
+               self.draw_curve(c, layer, style = styles["biarc_style_lathe_%s" % feed_type])
+               
+               gcode = ("G01 %s %f %s %f" % (x, c[0][4][0], z, c[0][4][1]) ) + feed + "\n" # Just in case move to the start...
+               for s in c :
+                       if s[1] == 'line':
+                               gcode += ("G01 %s %f %s %f" % (x, s[4][0], z, s[4][1]) ) + feed + "\n"
+                       elif s[1] == 'arc':
+                               r = [(s[2][0]-s[0][0]), (s[2][1]-s[0][1])]
+                               if (r[0]**2 + r[1]**2)>self.options.min_arc_radius:
+                                       r1, r2 = (P(s[0])-P(s[2])), (P(s[4])-P(s[2]))
+                                       if abs(r1.mag()-r2.mag()) < 0.001 :
+                                               gcode += ("G02" if s[3]*flip_angle<0 else "G03") + (" %s %f %s %f %s %f %s %f" % (x,s[4][0],z,s[4][1],i_,(s[2][0]-s[0][0]), k_, (s[2][1]-s[0][1]) ) ) + feed + "\n"
+                                       else:
+                                               r = (r1.mag()+r2.mag())/2
+                                               gcode += ("G02" if s[3]*flip_angle<0 else "G03") + (" %s %f %s %f" % (x,s[4][0],z,y[4][1]) ) + " R%f"%r + feed + "\n"
+               return gcode
+
+
+       def lathe(self):
+               if not self.check_dir() : return
+               x,z = self.options.lathe_x_axis_remap, self.options.lathe_z_axis_remap
+               x = re.sub("^\s*([XYZxyz])\s*$",r"\1",x) 
+               z = re.sub("^\s*([XYZxyz])\s*$",r"\1",z)
+               if x not in ["X", "Y", "Z", "x", "y", "z"] or z not in ["X", "Y", "Z", "x", "y", "z"] :
+                       self.error(_("Lathe X and Z axis remap should be 'X', 'Y' or 'Z'. Exiting..."),"warning") 
+                       return
+               if x.lower() == z.lower() :
+                       self.error(_("Lathe X and Z axis remap should be the same. Exiting..."),"warning") 
+                       return
+               if      x.lower()+z.lower() in ["xy","yx"] : gcode_plane_selection = "G17 (Using XY plane)\n"
+               if      x.lower()+z.lower() in ["xz","zx"] : gcode_plane_selection = "G18 (Using XZ plane)\n"
+               if      x.lower()+z.lower() in ["zy","yz"] : gcode_plane_selection = "G19 (Using YZ plane)\n"
+               self.options.lathe_x_axis_remap, self.options.lathe_z_axis_remap = x, z
+       
+               paths = self.selected_paths
+               self.tool = []
+               gcode = ""
+               for layer in self.layers :
+                       if layer in paths :
+                               self.set_tool(layer)
+                               if self.tool != self.tools[layer][0] :
+                                       self.tool = self.tools[layer][0]
+                                       self.tool["passing feed"]       = float(self.tool["passing feed"] if "passing feed" in self.tool else self.tool["feed"])
+                                       self.tool["feed"]                       = float(self.tool["feed"])
+                                       self.tool["fine feed"]          = float(self.tool["fine feed"] if "fine feed" in self.tool else self.tool["feed"]) 
+                                       
+                                       gcode += ( "(Change tool to %s)\n" % re.sub("\"'\(\)\\\\"," ",self.tool["name"]) ) + self.tool["tool change gcode"] + "\n"
+                                       
+                               for path in paths[layer]:
+                                       csp = self.transform_csp(cubicsuperpath.parsePath(path.get("d")),layer)
+                                       
+                                       for subpath in csp :
+                                               # Offset the path if fine cut is defined.
+                                               fine_cut = subpath[:]
+                                               if self.options.lathe_fine_cut_width>0 :
+                                                       r = self.options.lathe_fine_cut_width
+                                                       # Close the path to make offset correct 
+                                                       bound = csp_simple_bound([subpath])
+                                                       minx,miny,maxx,maxy = csp_true_bounds([subpath])
+                                                       offsetted_subpath = csp_subpath_line_to(subpath[:], [ [subpath[-1][1][0], miny[1]-r*10 ], [subpath[0][1][0], miny[1]-r*10 ], [subpath[0][1][0], subpath[0][1][1] ]  ])
+                                                       left,right = subpath[-1][1][0], subpath[0][1][0]
+                                                       if left>right : left, right = right,left
+                                                       offsetted_subpath = csp_offset([offsetted_subpath], r if not csp_subpath_ccw(offsetted_subpath) else -r ) 
+                                                       offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [left,10], [left,0] )
+                                                       offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [right,0], [right,10] )
+                                                       offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [0, miny[1]-r], [10, miny[1]-r] )
+                                                       #csp_draw(self.transform_csp(offsetted_subpath,layer,True), color = "Green", width = 1)
+                                                       # Join offsetted_subpath together 
+                                                       # Hope there wont be any cicles
+                                                       subpath = csp_join_subpaths(offsetted_subpath)[0]
+                                                       
+                                               # Create solid object from path and lathe_width 
+                                               bound = csp_simple_bound([subpath])
+                                               top_start, top_end = [subpath[0][1][0], self.options.lathe_width+self.options.Zsafe+self.options.lathe_fine_cut_width], [subpath[-1][1][0], self.options.lathe_width+self.options.Zsafe+self.options.lathe_fine_cut_width]
+
+                                               gcode += ("G01 %s %f F %f \n" % (z, top_start[1], self.tool["passing feed"]) )
+                                               gcode += ("G01 %s %f %s %f F %f \n" % (x, top_start[0], z, top_start[1], self.tool["passing feed"]) )
+
+                                               subpath = csp_concat_subpaths(csp_subpath_line_to([],[top_start,subpath[0][1]]), subpath)
+                                               subpath = csp_subpath_line_to(subpath,[top_end,top_start])
+       
+                                               
+                                               width = max(0, self.options.lathe_width - max(0, bound[1]) )
+                                               step = self.tool['depth step']
+                                               steps = int(math.ceil(width/step))
+                                               for i in range(steps+1):
+                                                       current_width = self.options.lathe_width - step*i
+                                                       intersections = []
+                                                       for j in range(1,len(subpath)) :
+                                                               sp1,sp2 = subpath[j-1], subpath[j]
+                                                               intersections += [[j,k] for k in csp_line_intersection([bound[0]-10,current_width], [bound[2]+10,current_width], sp1, sp2)]
+                                                               intersections += [[j,k] for k in csp_line_intersection([bound[0]-10,current_width+step], [bound[2]+10,current_width+step], sp1, sp2)]
+                                                       parts = csp_subpath_split_by_points(subpath,intersections)
+                                                       for part in parts :
+                                                               minx,miny,maxx,maxy = csp_true_bounds([part])
+                                                               y = (maxy[1]+miny[1])/2
+                                                               if  y > current_width+step :
+                                                                       gcode += self.generate_lathe_gcode(part,layer,"passing feed")
+                                                               elif current_width <= y <= current_width+step :
+                                                                       gcode += self.generate_lathe_gcode(part,layer,"feed")
+                                                               else :
+                                                                       # full step cut
+                                                                       part = csp_subpath_line_to([], [part[0][1], part[-1][1]] )
+                                                                       gcode += self.generate_lathe_gcode(part,layer,"feed")
+                                                                       
+                                               top_start, top_end = [fine_cut[0][1][0], self.options.lathe_width+self.options.Zsafe+self.options.lathe_fine_cut_width], [fine_cut[-1][1][0], self.options.lathe_width+self.options.Zsafe+self.options.lathe_fine_cut_width]
+                                               gcode += "\n(Fine cutting start)\n(Calculating fine cut using %s)\n"%self.options.lathe_create_fine_cut_using
+                                               for i in range(self.options.lathe_fine_cut_count) :
+                                                       width = self.options.lathe_fine_cut_width*(1-float(i+1)/self.options.lathe_fine_cut_count ) 
+                                                       if width == 0 : 
+                                                               current_pass = fine_cut                                                 
+                                                       else :  
+                                                               if self.options.lathe_create_fine_cut_using == "Move path" :
+                                                                       current_pass = [ [  [i2[0],i2[1]+width]  for i2 in i1]  for i1 in fine_cut]
+                                                               else :
+                                                                       minx,miny,maxx,maxy = csp_true_bounds([fine_cut])
+                                                                       offsetted_subpath = csp_subpath_line_to(fine_cut[:], [ [fine_cut[-1][1][0], miny[1]-r*10 ], [fine_cut[0][1][0], miny[1]-r*10 ], [fine_cut[0][1][0], fine_cut[0][1][1] ]  ])
+                                                                       left,right = fine_cut[-1][1][0], fine_cut[0][1][0]
+                                                                       if left>right : left, right = right,left
+                                                                       offsetted_subpath = csp_offset([offsetted_subpath], width if not csp_subpath_ccw(offsetted_subpath) else -width ) 
+                                                                       offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [left,10], [left,0] )
+                                                                       offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [right,0], [right,10] )
+                                                                       offsetted_subpath = csp_clip_by_line(offsetted_subpath,  [0, miny[1]-r], [10, miny[1]-r] )
+                                                                       current_pass = csp_join_subpaths(offsetted_subpath)[0]
+
+
+                                                       gcode += "\n(Fine cut %i-th cicle start)\n"%(i+1)                                       
+                                                       gcode += ("G01 %s %f %s %f F %f \n" % (x, top_start[0], z, top_start[1], self.tool["passing feed"]) )
+                                                       gcode += ("G01 %s %f %s %f F %f \n" % (x, current_pass[0][1][0], z, current_pass[0][1][1]+self.options.lathe_fine_cut_width, self.tool["passing feed"]) )
+                                                       gcode += ("G01 %s %f %s %f F %f \n" % (x, current_pass[0][1][0], z, current_pass[0][1][1], self.tool["fine feed"]) )
+                                               
+                                                       gcode += self.generate_lathe_gcode(current_pass,layer,"fine feed")
+                                                       gcode += ("G01 %s %f F %f \n" % (z, top_start[1], self.tool["passing feed"]) )
+                                                       gcode += ("G01 %s %f %s %f F %f \n" % (x, top_start[0], z, top_start[1], self.tool["passing feed"]) )
+       
+               
+               
+               self.export_gcode(gcode)
+
+       
+       def update(self) :
+               try :
+                       import urllib
+                       f = urllib.urlopen("http://www.cnc-club.ru/gcodetools_latest_version", proxies = urllib.getproxies())
+                       a = f.read()
+                       for s in a.split("\n") :
+                               r = re.search(r"Gcodetools\s+latest\s+version\s*=\s*(.*)",s)
+                               if r : 
+                                       ver = r.group(1).strip()
+                                       if ver != gcodetools_current_version :
+                                               self.error("There is a newer version of Gcodetools you can get it at: \nhttp://www.cnc-club.ru/gcodetools (English version). \nhttp://www.cnc-club.ru/gcodetools_ru (Russian version). ","Warning")                                     
+                                       else :
+                                               self.error("You are currently using latest stable version of Gcodetools.","Warning")                                    
+                                       return 
+                       self.error("Can not check the latest version. You can check it manualy at \nhttp://www.cnc-club.ru/gcodetools (English version). \nhttp://www.cnc-club.ru/gcodetools_ru (Russian version). \nCurrent version is Gcodetools %s"%gcodetools_current_version,"Warning")                                    
+               except :
+                       self.error("Can not check the latest version. You can check it manualy at \nhttp://www.cnc-club.ru/gcodetools (English version). \nhttp://www.cnc-club.ru/gcodetools_ru (Russian version). \nCurrent version is Gcodetools %s"%gcodetools_current_version,"Warning")                                    
+                               
+                                                                               
+################################################################################
+###
+###            Effect
+###
+###            Main function of Gcodetools class
+###
+################################################################################
+       def effect(self) :
+               global options
+               options = self.options
+               options.self = self
+               options.doc_root = self.document.getroot()
+
+               # define print_ function 
+               global print_
+               if self.options.log_create_log :
+                       try :
+                               if os.path.isfile(self.options.log_filename) : os.remove(self.options.log_filename)
+                               f = open(self.options.log_filename,"a")
+                               f.write("Gcodetools log file.\nStarted at %s.\n%s\n" % (time.strftime("%d.%m.%Y %H:%M:%S"),options.log_filename))
+                               f.write("%s tab is active.\n" % self.options.active_tab)
+                               f.close()
+                       except :
+                               print_  = lambda *x : None 
+               else : print_  = lambda *x : None 
+               if self.options.active_tab == '"help"' :
+                       self.help()
+                       return
+               elif self.options.active_tab not in ['"dxfpoints"','"path-to-gcode"', '"area"', '"area_artefacts"', '"engraving"', '"orientation"', '"tools_library"', '"lathe"', '"offset"', '"arrangement"', '"update"']:
+                       self.error(_("Select one of the active tabs - Path to Gcode, Area, Engraving, DXF points, Orientation, Offset, Lathe or Tools library."),"error")
+               else:
+                       # Get all Gcodetools data from the scene.
+                       self.get_info()
+                       if self.options.active_tab in ['"dxfpoints"','"path-to-gcode"', '"area"', '"area_artefacts"', '"engraving"', '"lathe"']:
+                               if self.orientation_points == {} :
+                                       self.error(_("Orientation points have not been defined! A default set of orientation points has been automatically added."),"warning")
+                                       self.orientation( self.layers[min(1,len(self.layers)-1)] )              
+                                       self.get_info()
+                               if self.tools == {} :
+                                       self.error(_("Cutting tool has not been defined! A default tool has been automatically added."),"warning")
+                                       self.options.tools_library_type = "default"
+                                       self.tools_library( self.layers[min(1,len(self.layers)-1)] )
+                                       self.get_info()
+                       if self.options.active_tab == '"path-to-gcode"': 
+                               self.path_to_gcode()            
+                       elif self.options.active_tab == '"area"': 
+                               self.area()             
+                       elif self.options.active_tab == '"area_artefacts"': 
+                               self.area_artefacts()           
+                       elif self.options.active_tab == '"dxfpoints"': 
+                               self.dxfpoints()                
+                       elif self.options.active_tab == '"engraving"': 
+                               self.engraving()                
+                       elif self.options.active_tab == '"orientation"': 
+                               self.orientation()              
+                       elif self.options.active_tab == '"tools_library"': 
+                               if self.options.tools_library_type != "check":
+                                       self.tools_library()
+                               else :  
+                                       self.check_tools_and_op()
+                       elif self.options.active_tab == '"lathe"': 
+                               self.lathe()
+                       elif self.options.active_tab == '"update"': 
+                               self.update()
+                       elif self.options.active_tab == '"offset"': 
+                               if self.options.offset_just_get_distance :
+                                       for layer in self.selected_paths :
+                                               if len(self.selected_paths[layer]) == 2 :
+                                                       csp1, csp2 = cubicsuperpath.parsePath(self.selected_paths[layer][0].get("d")), cubicsuperpath.parsePath(self.selected_paths[layer][1].get("d"))
+                                                       dist = csp_to_csp_distance(csp1,csp2)
+                                                       print_(dist)
+                                                       draw_pointer( list(csp_at_t(csp1[dist[1]][dist[2]-1],csp1[dist[1]][dist[2]],dist[3]))
+                                                                               +list(csp_at_t(csp2[dist[4]][dist[5]-1],csp2[dist[4]][dist[5]],dist[6])),"red","line", comment = math.sqrt(dist[0]))
+                                       return  
+                               if self.options.offset_step == 0 : self.options.offset_step = self.options.offset_radius
+                               if self.options.offset_step*self.options.offset_radius <0 : self.options.offset_step *= -1
+                               time_ = time.time()
+                               offsets_count = 0
+                               for layer in self.selected_paths :
+                                       for path in self.selected_paths[layer] :
+                                                                               
+                                               offset = self.options.offset_step/2
+                                               while abs(offset) <= abs(self.options.offset_radius) :
+                                                       offset_ = csp_offset(cubicsuperpath.parsePath(path.get("d")), offset)                           
+                                                       offsets_count += 1
+                                                       if offset_ != [] : 
+                                                               for iii in offset_ :
+                                                                       csp_draw([iii], color="Green", width=1)         
+                                                                       #print_(offset_)
+                                                       else :
+                                                               print_("------------Reached empty offset at radius %s"% offset )
+                                                               break
+                                                       offset += self.options.offset_step
+                               print_()
+                               print_("-----------------------------------------------------------------------------------")                           
+                               print_("-----------------------------------------------------------------------------------")                           
+                               print_("-----------------------------------------------------------------------------------")                           
+                               print_()
+                               print_("Done in %s"%(time.time()-time_))                                
+                               print_("Total offsets count %s"%offsets_count)                          
+                       elif self.options.active_tab == '"arrangement"': 
+                               self.arrangement()
+#                                              
+e = Gcodetools()
+e.affect()                                     
+
diff --git a/share/extensions/gcodetools_all_in_one.inx b/share/extensions/gcodetools_all_in_one.inx
new file mode 100644 (file)
index 0000000..0f566fc
--- /dev/null
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+    <_name>All in one</_name>
+    <id>ru.cnc-club.filter.gcodetools_ptg_area_area_artefacts_engraving_dxfpoints_tools_library_orientation</id>
+       <dependency type="executable" location="extensions">gcodetools.py</dependency>
+       <dependency type="executable" location="extensions">inkex.py</dependency>
+       <param name='active-tab' type="notebook">
+
+               <page name='path-to-gcode' _gui-text='Path to Gcode'>   
+                               <param name="biarc-tolerance" type='float' precision="5" _gui-text='Biarc interpolation tolerance:'>1</param>
+                               <param name="biarc-max-split-depth" type="int" _gui-text="Maximum splitting depth:">4</param>
+                               <_param name="help" type="description">
+Biarc interpolation tolerance is the maximum distance between path and its approximation.
+The segment will be split into two segments if the distance between path's segment and it's approximation exceeds biarc interpolation tolerance.
+</_param>
+               </page>
+
+               <page name='area' _gui-text='Area'>     
+                       <param name="max-area-curves" type="int" min="0" max="1000" _gui-text="Maximum area cutting curves:">100</param>
+                       <param name="area-inkscape-radius" type="float" min="-1000" max="1000" _gui-text="Area width:">-10</param>
+
+                       <_param name="help" type="description">
+"Create area offset": creates several Inkscape path offsets to fill original path's area up to "Area radius" value. 
+
+Outlines start from "1/2 D" up to "Area width" total width with "D" steps where D is taken from the nearest tool definition ("Tool diameter" value).
+Only one offset will be created if the "Area width" is equal to "1/2 D".
+                       </_param>
+               </page>         
+
+               <page name='area_artefacts' _gui-text='Area artefacts'> 
+                       <param name="area-find-artefacts-diameter" type="float" min="0.01" max="1000" _gui-text="Artefact diameter:">5.0</param>
+                       <param name="area-find-artefacts-action" type="optiongroup" _gui-text="Action:">
+                               <_option value="mark with an arrow">mark with an arrow</_option>
+                               <_option value="mark with style">mark with style</_option>
+                               <_option value="delete">delete</_option>
+                       </param>        
+                       <_param name="help" type="description">
+Usage: 
+1. Select all Area Offsets (gray outlines)
+2. Object/Ungroup (Shift+Ctrl+G)
+3. Press Apply
+
+Suspected small objects will be marked out by colored arrows.
+                       </_param>
+               </page>         
+
+               <page name='engraving' _gui-text='Engraving'>   
+                       <param name="engraving-sharp-angle-tollerance" type="float"  precision="5" min="0" max="180" _gui-text="Sharp angle tolerance:">150</param>
+                       <param name="engraving-max-dist" type="float" precision="5" min="0" max="1000" _gui-text="Maximum distance for engraving:">10</param>
+                       <param name="engraving-newton-iterations" type="int" min="2" max="10" _gui-text="Number of sample points used to calculate distance:">4</param>
+                       <param name="engraving-draw-calculation-paths" type="boolean" _gui-text="Draw additional graphics to debug engraving path:">false</param>
+                       
+                       <_param name="help" type="description">
+This function creates path to engrave sharp angles.
+Cutter's shape function is defined by the tool. Some simple shapes:
+
+cone....(45 degrees)...........: w
+cone....(height/diameter=10/3).: 10/3 w
+sphere..("r" diameter).........: math.sqrt(max(0,r**2-w**2))
+ellipse.(R1=r and R2=r*4r).....: math.sqrt(max(0,r**2-w**2))*4</_param>
+               </page>
+
+               <page name='dxfpoints' _gui-text='DXF points'>
+                       <_param name="help" type="description">
+
+Convert selected objects to drill points (as dxf_import plugin does). Also you can save original shape. Only the start point of each curve will be used.
+
+Also you can manually select object, open XML editor (Shift+Ctrl+X) and add or remove XML tag 'dxfpoint' with any value.
+                 </_param>
+                       <param type='optiongroup' name='dxfpoints-action' _gui-text="Convert selection:">
+<_option value='save'>set as dxfpoint and save shape</_option>
+<_option value='replace'>set as dxfpoint and draw arrow</_option>
+<_option value='clear'>clear dxfpoint sign</_option>
+                       </param>
+
+               </page>
+
+               <page name='tools_library' _gui-text='Tools library'>   
+                       
+                       <param type='optiongroup' name='tools-library-type' _gui-text="Tools type:">
+<_option value='default tool'>default</_option>
+<_option value='cylinder cutter'>cylinder</_option>
+<_option value='cone cutter'>cone</_option>
+<_option value='plasma cutter'>plasma</_option>
+<_option value='tangent knife'>tangent knife</_option>
+<_option value='lathe cutter'>lathe cutter</_option>
+
+<_option value='check'>Just check tools</_option>
+
+                       </param>
+                       
+                       <_param name="help" type="description">
+Selected tool type fills appropriate default values. You can change these values using the Text tool later on.
+
+The topmost (z order) tool in the active layer is used. If there is no tool inside the current layer it is taken from the upper layer.
+
+Press Apply to create new tool.
+                       </_param>
+       </page>
+
+               <page name='orientation' _gui-text='Orientation'>       
+
+                       <param name="orientation-points-count" type="optiongroup" _gui-text="Orientation type:">
+<_option value="2">2-points mode
+(move and rotate,
+maintained aspect ratio X/Y)</_option>
+<_option value="3">3-points mode
+(move, rotate and mirror, 
+different X/Y scale)</_option>
+                       </param>
+                       <param name="Zsurface" type="float" precision="5" min="-1000" max="1000" _gui-text="Z surface:">0</param>
+                       <param name="Zdepth" type="float" precision="5" min="-1000" max="1000" _gui-text="Z depth:">-1</param>
+                       <param name="unit" type="enum" _gui-text="Units (mm or in):">
+                               <item value="G21 (All units in mm)">mm</item>
+                               <item value="G20 (All units in inches)">in</item>
+                       </param>
+
+                       <_param name="help" type="description">
+Orientation points are used to calculate transformation (offset,scale,mirror,rotation in XY plane) of the path.
+3-points mode only: do not put all three into one line (use 2-points mode instead).
+
+You can modify Z surface, Z depth values later using text tool (3rd coordinates).
+
+If there are no orientation points inside current layer they are taken from the upper layer.
+
+Do not ungroup orientation points! You can select them using double click to enter the group or by Ctrl+Click.
+
+Now press apply to create control points (independent set for each layer).
+                       </_param>
+               </page>
+
+               <page name='options' _gui-text='Options'>
+                       <param name="Zscale" type="float" precision="5" min="-100000" max="100000" _gui-text="Scale along Z axis:">1</param>                    
+                       <param name="Zoffset" type="float" precision="5" min="-100000" max="100000" _gui-text="Offset along Z axis:">0.0</param>
+                       <param name="auto_select_paths" type="boolean" _gui-text="Select all paths if nothing is selected">true</param>
+                       <param name="min-arc-radius" type="float" precision="5" min="-1000" max="1000"  _gui-text="Minimum arc radius:">0.05</param>
+               </page>
+
+               <page name='preferences' _gui-text='Preferences'>
+                       <param name="filename" type="string" _gui-text="File:">output.ngc</param>
+                       <param name="add-numeric-suffix-to-filename" type="boolean" _gui-text="Add numeric suffix to filename">true</param>
+                       
+                       <param name="directory" type="string" _gui-text="Directory:">/home</param>
+
+                       <param name="Zsafe" type="float" precision="5" min="-1000" max="1000" _gui-text="Z safe height for G00 move over blank:">5</param>
+                       <param name="unit" type="enum" _gui-text="Units (mm or in):">
+                               <item value="G21 (All units in mm)">mm</item>
+                               <item value="G20 (All units in inches)">in</item>
+                       </param>
+                       <param name="postprocessor" type="enum" _gui-text="Post-processor:">
+                               <item value=" ">None</item>
+                               <item value="parameterize();">Parameterize Gcode</item>
+                               <item value="flip(y);parameterize();">Flip y axis and parameterize Gcode</item>
+                               <item value="round(4);">Round all values to 4 digits</item>
+                       </param>
+                       <param name="postprocessor-custom" type="string" _gui-text="Additional post-processor:"></param>                        
+                       
+
+                       <param name="create-log" type="boolean" _gui-text="Generate log file">false</param>                     
+                       <param name="log-filename" type="string" _gui-text="Full path to log file:"></param>                    
+                       
+               </page>
+
+               <page name='help' _gui-text='Help'>
+                       <_param name="fullhelp" type="description">
+Gcodetools plug-in: converts paths to Gcode (using circular interpolation), makes offset paths and engraves sharp corners using cone cutters. 
+This plug-in calculates Gcode for paths using circular interpolation or linear motion when needed.
+
+Tutorials, manuals and support can be found at
+English support forum:
+       http://www.cnc-club.ru/gcodetools
+       
+and Russian support forum:
+       http://www.cnc-club.ru/gcodetoolsru
+
+Credits: Nick Drobchenko, Vladimir Kalyaev, John Brooker, Henry Nicolas.
+
+Gcodetools ver. 1.6.01
+</_param>      
+
+               </page>
+
+       </param>        
+    <effect>
+               <effects-menu>
+                       <submenu _name="Gcodetools"/>
+               </effects-menu>
+               <object-type>path</object-type>
+    </effect>
+    <script>
+        <command reldir="extensions" interpreter="python">gcodetools.py</command>
+    </script>
+    
+</inkscape-extension>
diff --git a/share/extensions/gcodetools_area.inx b/share/extensions/gcodetools_area.inx
new file mode 100644 (file)
index 0000000..718ae0d
--- /dev/null
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+    <_name>Area</_name>
+    <id>ru.cnc-club.filter.gcodetools_area_area_artefacts_ptg</id>
+       <dependency type="executable" location="extensions">gcodetools.py</dependency>
+       <dependency type="executable" location="extensions">inkex.py</dependency>
+       <param name='active-tab' type="notebook">
+
+               <page name='area' _gui-text='Area'>     
+                       <param name="max-area-curves" type="int" min="0" max="1000" _gui-text="Maximum area cutting curves:">100</param>
+                       <param name="area-inkscape-radius" type="float" min="-1000" max="1000" _gui-text="Area width:">-10</param>
+
+                       <_param name="help" type="description">
+"Create area offset": creates several Inkscape path offsets to fill original path's area up to "Area radius" value. 
+
+Outlines start from "1/2 D" up to "Area width" total width with "D" steps where D is taken from the nearest tool definition ("Tool diameter" value).
+Only one offset will be created if the "Area width" is equal to "1/2 D".
+                       </_param>
+               </page>         
+
+               <page name='area_artefacts' _gui-text='Area artefacts'> 
+                       <param name="area-find-artefacts-diameter" type="float" min="0.01" max="1000" _gui-text="Artefact diameter:">5.0</param>
+                       <param name="area-find-artefacts-action" type="optiongroup" _gui-text="Action:">
+                               <_option value="mark with an arrow">mark with an arrow</_option>
+                               <_option value="mark with style">mark with style</_option>
+                               <_option value="delete">delete</_option>
+                       </param>        
+                       <_param name="help" type="description">
+Usage: 
+1. Select all Area Offsets (gray outlines)
+2. Object/Ungroup (Shift+Ctrl+G)
+3. Press Apply
+
+Suspected small objects will be marked out by colored arrows.
+                       </_param>
+               </page>         
+
+               <page name='path-to-gcode' _gui-text='Path to Gcode'>   
+                               <param name="biarc-tolerance" type='float' precision="5" _gui-text='Biarc interpolation tolerance:'>1</param>
+                               <param name="biarc-max-split-depth" type="int" _gui-text="Maximum splitting depth:">4</param>
+                               <_param name="help" type="description">
+Biarc interpolation tolerance is the maximum distance between path and its approximation.
+The segment will be split into two segments if the distance between path's segment and it's approximation exceeds biarc interpolation tolerance.
+</_param>
+               </page>
+
+               <page name='options' _gui-text='Options'>
+                       <param name="Zscale" type="float" precision="5" min="-100000" max="100000" _gui-text="Scale along Z axis:">1</param>                    
+                       <param name="Zoffset" type="float" precision="5" min="-100000" max="100000" _gui-text="Offset along Z axis:">0.0</param>
+                       <param name="auto_select_paths" type="boolean" _gui-text="Select all paths if nothing is selected">true</param>
+                       <param name="min-arc-radius" type="float" precision="5" min="-1000" max="1000"  _gui-text="Minimum arc radius:">0.05</param>
+               </page>
+
+               <page name='preferences' _gui-text='Preferences'>
+                       <param name="filename" type="string" _gui-text="File:">output.ngc</param>
+                       <param name="add-numeric-suffix-to-filename" type="boolean" _gui-text="Add numeric suffix to filename">true</param>
+                       
+                       <param name="directory" type="string" _gui-text="Directory:">/home</param>
+
+                       <param name="Zsafe" type="float" precision="5" min="-1000" max="1000" _gui-text="Z safe height for G00 move over blank:">5</param>
+                       <param name="unit" type="enum" _gui-text="Units (mm or in):">
+                               <item value="G21 (All units in mm)">mm</item>
+                               <item value="G20 (All units in inches)">in</item>
+                       </param>
+                       <param name="postprocessor" type="enum" _gui-text="Post-processor:">
+                               <item value=" ">None</item>
+                               <item value="parameterize();">Parameterize Gcode</item>
+                               <item value="flip(y);parameterize();">Flip y axis and parameterize Gcode</item>
+                               <item value="round(4);">Round all values to 4 digits</item>
+                       </param>
+                       <param name="postprocessor-custom" type="string" _gui-text="Additional post-processor:"></param>                        
+                       
+
+                       <param name="create-log" type="boolean" _gui-text="Generate log file">false</param>                     
+                       <param name="log-filename" type="string" _gui-text="Full path to log file:"></param>                    
+                       
+               </page>
+
+               <page name='help' _gui-text='Help'>
+                       <_param name="fullhelp" type="description">
+Gcodetools plug-in: converts paths to Gcode (using circular interpolation), makes offset paths and engraves sharp corners using cone cutters. 
+This plug-in calculates Gcode for paths using circular interpolation or linear motion when needed.
+
+Tutorials, manuals and support can be found at
+English support forum:
+       http://www.cnc-club.ru/gcodetools
+       
+and Russian support forum:
+       http://www.cnc-club.ru/gcodetoolsru
+
+Credits: Nick Drobchenko, Vladimir Kalyaev, John Brooker, Henry Nicolas.
+
+Gcodetools ver. 1.6.01
+</_param>      
+
+               </page>
+
+       </param>        
+    <effect>
+               <effects-menu>
+                       <submenu _name="Gcodetools"/>
+               </effects-menu>
+               <object-type>path</object-type>
+    </effect>
+    <script>
+        <command reldir="extensions" interpreter="python">gcodetools.py</command>
+    </script>
+    
+</inkscape-extension>
diff --git a/share/extensions/gcodetools_check_for_updates.inx b/share/extensions/gcodetools_check_for_updates.inx
new file mode 100644 (file)
index 0000000..728f5d1
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+    <_name>Check for updates</_name>
+    <id>ru.cnc-club.filter.gcodetools_update_no_options_no_preferences</id>
+       <dependency type="executable" location="extensions">gcodetools.py</dependency>
+       <dependency type="executable" location="extensions">inkex.py</dependency>
+       <param name='active-tab' type="notebook">
+
+       <page name='update' _gui-text='Check for updates'>      
+               <_param name="help" type="description">Check for Gcodetools latest stable version and try to get the updates.</_param>
+       </page>
+
+               <page name='help' _gui-text='Help'>
+                       <_param name="fullhelp" type="description">
+Gcodetools plug-in: converts paths to Gcode (using circular interpolation), makes offset paths and engraves sharp corners using cone cutters. 
+This plug-in calculates Gcode for paths using circular interpolation or linear motion when needed.
+
+Tutorials, manuals and support can be found at
+English support forum:
+       http://www.cnc-club.ru/gcodetools
+       
+and Russian support forum:
+       http://www.cnc-club.ru/gcodetoolsru
+
+Credits: Nick Drobchenko, Vladimir Kalyaev, John Brooker, Henry Nicolas.
+
+Gcodetools ver. 1.6.01
+</_param>      
+
+               </page>
+
+       </param>        
+    <effect>
+               <effects-menu>
+                       <submenu _name="Gcodetools"/>
+               </effects-menu>
+               <object-type>path</object-type>
+    </effect>
+    <script>
+        <command reldir="extensions" interpreter="python">gcodetools.py</command>
+    </script>
+    
+</inkscape-extension>
diff --git a/share/extensions/gcodetools_dxf_points.inx b/share/extensions/gcodetools_dxf_points.inx
new file mode 100644 (file)
index 0000000..7283853
--- /dev/null
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+    <_name>DXF Points</_name>
+    <id>ru.cnc-club.filter.gcodetools_dxfpoints_no_options</id>
+       <dependency type="executable" location="extensions">gcodetools.py</dependency>
+       <dependency type="executable" location="extensions">inkex.py</dependency>
+       <param name='active-tab' type="notebook">
+
+               <page name='dxfpoints' _gui-text='DXF points'>
+                       <_param name="help" type="description">
+
+Convert selected objects to drill points (as dxf_import plugin does). Also you can save original shape. Only the start point of each curve will be used.
+
+Also you can manually select object, open XML editor (Shift+Ctrl+X) and add or remove XML tag 'dxfpoint' with any value.
+                 </_param>
+                       <param type='optiongroup' name='dxfpoints-action' _gui-text="Convert selection:">
+<_option value='save'>set as dxfpoint and save shape</_option>
+<_option value='replace'>set as dxfpoint and draw arrow</_option>
+<_option value='clear'>clear dxfpoint sign</_option>
+                       </param>
+
+               </page>
+
+               <page name='preferences' _gui-text='Preferences'>
+                       <param name="filename" type="string" _gui-text="File:">output.ngc</param>
+                       <param name="add-numeric-suffix-to-filename" type="boolean" _gui-text="Add numeric suffix to filename">true</param>
+                       
+                       <param name="directory" type="string" _gui-text="Directory:">/home</param>
+
+                       <param name="Zsafe" type="float" precision="5" min="-1000" max="1000" _gui-text="Z safe height for G00 move over blank:">5</param>
+                       <param name="unit" type="enum" _gui-text="Units (mm or in):">
+                               <item value="G21 (All units in mm)">mm</item>
+                               <item value="G20 (All units in inches)">in</item>
+                       </param>
+                       <param name="postprocessor" type="enum" _gui-text="Post-processor:">
+                               <item value=" ">None</item>
+                               <item value="parameterize();">Parameterize Gcode</item>
+                               <item value="flip(y);parameterize();">Flip y axis and parameterize Gcode</item>
+                               <item value="round(4);">Round all values to 4 digits</item>
+                       </param>
+                       <param name="postprocessor-custom" type="string" _gui-text="Additional post-processor:"></param>                        
+                       
+
+                       <param name="create-log" type="boolean" _gui-text="Generate log file">false</param>                     
+                       <param name="log-filename" type="string" _gui-text="Full path to log file:"></param>                    
+                       
+               </page>
+
+               <page name='help' _gui-text='Help'>
+                       <_param name="fullhelp" type="description">
+Gcodetools plug-in: converts paths to Gcode (using circular interpolation), makes offset paths and engraves sharp corners using cone cutters. 
+This plug-in calculates Gcode for paths using circular interpolation or linear motion when needed.
+
+Tutorials, manuals and support can be found at
+English support forum:
+       http://www.cnc-club.ru/gcodetools
+       
+and Russian support forum:
+       http://www.cnc-club.ru/gcodetoolsru
+
+Credits: Nick Drobchenko, Vladimir Kalyaev, John Brooker, Henry Nicolas.
+
+Gcodetools ver. 1.6.01
+</_param>      
+
+               </page>
+
+       </param>        
+    <effect>
+               <effects-menu>
+                       <submenu _name="Gcodetools"/>
+               </effects-menu>
+               <object-type>path</object-type>
+    </effect>
+    <script>
+        <command reldir="extensions" interpreter="python">gcodetools.py</command>
+    </script>
+    
+</inkscape-extension>
diff --git a/share/extensions/gcodetools_engraving.inx b/share/extensions/gcodetools_engraving.inx
new file mode 100644 (file)
index 0000000..8645faa
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+    <_name>Engraving</_name>
+    <id>ru.cnc-club.filter.gcodetools_engraving</id>
+       <dependency type="executable" location="extensions">gcodetools.py</dependency>
+       <dependency type="executable" location="extensions">inkex.py</dependency>
+       <param name='active-tab' type="notebook">
+
+               <page name='engraving' _gui-text='Engraving'>   
+                       <param name="engraving-sharp-angle-tollerance" type="float"  precision="5" min="0" max="180" _gui-text="Sharp angle tolerance:">150</param>
+                       <param name="engraving-max-dist" type="float" precision="5" min="0" max="1000" _gui-text="Maximum distance for engraving:">10</param>
+                       <param name="engraving-newton-iterations" type="int" min="2" max="10" _gui-text="Number of sample points used to calculate distance:">4</param>
+                       <param name="engraving-draw-calculation-paths" type="boolean" _gui-text="Draw additional graphics to debug engraving path">false</param>
+                       
+                       <_param name="help" type="description">
+This function creates path to engrave sharp angles.
+Cutter's shape function is defined by the tool. Some simple shapes:
+
+cone....(45 degrees)...........: w
+cone....(height/diameter=10/3).: 10/3 w
+sphere..("r" diameter).........: math.sqrt(max(0,r**2-w**2))
+ellipse.(R1=r and R2=r*4r).....: math.sqrt(max(0,r**2-w**2))*4</_param>
+               </page>
+
+               <page name='options' _gui-text='Options'>
+                       <param name="Zscale" type="float" precision="5" min="-100000" max="100000" _gui-text="Scale along Z axis:">1</param>                    
+                       <param name="Zoffset" type="float" precision="5" min="-100000" max="100000" _gui-text="Offset along Z axis:">0.0</param>
+                       <param name="auto_select_paths" type="boolean" _gui-text="Select all paths if nothing is selected">true</param>
+                       <param name="min-arc-radius" type="float" precision="5" min="-1000" max="1000"  _gui-text="Minimum arc radius:">0.05</param>
+               </page>
+
+               <page name='preferences' _gui-text='Preferences'>
+                       <param name="filename" type="string" _gui-text="File:">output.ngc</param>
+                       <param name="add-numeric-suffix-to-filename" type="boolean" _gui-text="Add numeric suffix to filename">true</param>
+                       
+                       <param name="directory" type="string" _gui-text="Directory:">/home</param>
+
+                       <param name="Zsafe" type="float" precision="5" min="-1000" max="1000" _gui-text="Z safe height for G00 move over blank:">5</param>
+                       <param name="unit" type="enum" _gui-text="Units (mm or in):">
+                               <item value="G21 (All units in mm)">mm</item>
+                               <item value="G20 (All units in inches)">in</item>
+                       </param>
+                       <param name="postprocessor" type="enum" _gui-text="Post-processor:">
+                               <item value=" ">None</item>
+                               <item value="parameterize();">Parameterize Gcode</item>
+                               <item value="flip(y);parameterize();">Flip y axis and parameterize Gcode</item>
+                               <item value="round(4);">Round all values to 4 digits</item>
+                       </param>
+                       <param name="postprocessor-custom" type="string" _gui-text="Additional post-processor:"></param>                        
+                       
+
+                       <param name="create-log" type="boolean" _gui-text="Generate log file">false</param>                     
+                       <param name="log-filename" type="string" _gui-text="Full path to log file:"></param>                    
+                       
+               </page>
+
+               <page name='help' _gui-text='Help'>
+                       <_param name="fullhelp" type="description">
+Gcodetools plug-in: converts paths to Gcode (using circular interpolation), makes offset paths and engraves sharp corners using cone cutters. 
+This plug-in calculates Gcode for paths using circular interpolation or linear motion when needed.
+
+Tutorials, manuals and support can be found at
+English support forum:
+       http://www.cnc-club.ru/gcodetools
+       
+and Russian support forum:
+       http://www.cnc-club.ru/gcodetoolsru
+
+Credits: Nick Drobchenko, Vladimir Kalyaev, John Brooker, Henry Nicolas.
+
+Gcodetools ver. 1.6.01
+</_param>      
+
+               </page>
+
+       </param>        
+    <effect>
+               <effects-menu>
+                       <submenu _name="Gcodetools"/>
+               </effects-menu>
+               <object-type>path</object-type>
+    </effect>
+    <script>
+        <command reldir="extensions" interpreter="python">gcodetools.py</command>
+    </script>
+    
+</inkscape-extension>
diff --git a/share/extensions/gcodetools_lathe.inx b/share/extensions/gcodetools_lathe.inx
new file mode 100644 (file)
index 0000000..9330736
--- /dev/null
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+    <_name>Lathe</_name>
+    <id>ru.cnc-club.filter.gcodetools_lathe_ptg</id>
+       <dependency type="executable" location="extensions">gcodetools.py</dependency>
+       <dependency type="executable" location="extensions">inkex.py</dependency>
+       <param name='active-tab' type="notebook">
+
+       <page name='lathe' _gui-text='Lathe'>   
+               <param name="lathe-width" type="float" precision="5" min="0" max="1000" _gui-text="Lathe width:">10</param>
+               <param name="lathe-fine-cut-width" type="float" precision="5" min="0" max="1000" _gui-text="Fine cut width:">1</param>
+               <param name="lathe-fine-cut-count" type="int" min="0" max="1000" _gui-text="Fine cut count:">1</param>
+               <param name="lathe-create-fine-cut-using"  _gui-text="Create fine cut using:" type="optiongroup" appearance="minimal">
+                       <option value="Move path">Move path</option>
+                       <option value="Offset path">Offset path</option>
+               </param>
+               <param name="lathe-x-axis-remap" type="string" _gui-text="Lathe X axis remap:">X</param>
+               <param name="lathe-z-axis-remap" type="string" _gui-text="Lathe Z axis remap:">Z</param>
+
+               
+       </page>
+
+               <page name='path-to-gcode' _gui-text='Path to Gcode'>   
+                               <param name="biarc-tolerance" type='float' precision="5" _gui-text='Biarc interpolation tolerance:'>1</param>
+                               <param name="biarc-max-split-depth" type="int" _gui-text="Maximum splitting depth:">4</param>
+                               <_param name="help" type="description">
+Biarc interpolation tolerance is the maximum distance between path and its approximation.
+The segment will be split into two segments if the distance between path's segment and it's approximation exceeds biarc interpolation tolerance.
+</_param>
+               </page>
+
+               <page name='options' _gui-text='Options'>
+                       <param name="Zscale" type="float" precision="5" min="-100000" max="100000" _gui-text="Scale along Z axis:">1</param>                    
+                       <param name="Zoffset" type="float" precision="5" min="-100000" max="100000" _gui-text="Offset along Z axis:">0.0</param>
+                       <param name="auto_select_paths" type="boolean" _gui-text="Select all paths if nothing is selected">true</param>
+                       <param name="min-arc-radius" type="float" precision="5" min="-1000" max="1000"  _gui-text="Minimum arc radius:">0.05</param>
+               </page>
+
+               <page name='preferences' _gui-text='Preferences'>
+                       <param name="filename" type="string" _gui-text="File">output.ngc</param>
+                       <param name="add-numeric-suffix-to-filename" type="boolean" _gui-text="Add numeric suffix to filename">true</param>
+                       
+                       <param name="directory" type="string" _gui-text="Directory:">/home</param>
+
+                       <param name="Zsafe" type="float" precision="5" min="-1000" max="1000" _gui-text="Z safe height for G00 move over blank:">5</param>
+                       <param name="unit" type="enum" _gui-text="Units (mm or in):">
+                               <item value="G21 (All units in mm)">mm</item>
+                               <item value="G20 (All units in inches)">in</item>
+                       </param>
+                       <param name="postprocessor" type="enum" _gui-text="Post-processor:">
+                               <item value=" ">None</item>
+                               <item value="parameterize();">Parameterize Gcode</item>
+                               <item value="flip(y);parameterize();">Flip y axis and parameterize Gcode</item>
+                               <item value="round(4);">Round all values to 4 digits</item>
+                       </param>
+                       <param name="postprocessor-custom" type="string" _gui-text="Additional post-processor:"></param>                        
+                       
+
+                       <param name="create-log" type="boolean" _gui-text="Generate log file">false</param>                     
+                       <param name="log-filename" type="string" _gui-text="Full path to log file:"></param>                    
+                       
+               </page>
+
+               <page name='help' _gui-text='Help'>
+                       <_param name="fullhelp" type="description">
+Gcodetools plug-in: converts paths to Gcode (using circular interpolation), makes offset paths and engraves sharp corners using cone cutters. 
+This plug-in calculates Gcode for paths using circular interpolation or linear motion when needed.
+
+Tutorials, manuals and support can be found at
+English support forum:
+       http://www.cnc-club.ru/gcodetools
+       
+and Russian support forum:
+       http://www.cnc-club.ru/gcodetoolsru
+
+Credits: Nick Drobchenko, Vladimir Kalyaev, John Brooker, Henry Nicolas.
+
+Gcodetools ver. 1.6.01
+</_param>      
+
+               </page>
+
+       </param>        
+    <effect>
+               <effects-menu>
+                       <submenu _name="Gcodetools"/>
+               </effects-menu>
+               <object-type>path</object-type>
+    </effect>
+    <script>
+        <command reldir="extensions" interpreter="python">gcodetools.py</command>
+    </script>
+    
+</inkscape-extension>
diff --git a/share/extensions/gcodetools_orientation_points.inx b/share/extensions/gcodetools_orientation_points.inx
new file mode 100644 (file)
index 0000000..997b24d
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+    <_name>Orientation points</_name>
+    <id>ru.cnc-club.filter.gcodetools_orientation_no_options_no_preferences</id>
+       <dependency type="executable" location="extensions">gcodetools.py</dependency>
+       <dependency type="executable" location="extensions">inkex.py</dependency>
+       <param name='active-tab' type="notebook">
+
+               <page name='orientation' _gui-text='Orientation'>       
+
+                       <param name="orientation-points-count" type="optiongroup" _gui-text="Orientation type:">
+<_option value="2">2-points mode
+(move and rotate,
+maintained aspect ratio X/Y)</_option>
+<_option value="3">3-points mode
+(move, rotate and mirror, 
+different X/Y scale)</_option>
+                       </param>
+                       <param name="Zsurface" type="float" precision="5" min="-1000" max="1000" _gui-text="Z surface:">0</param>
+                       <param name="Zdepth" type="float" precision="5" min="-1000" max="1000" _gui-text="Z depth:">-1</param>
+                       <param name="unit" type="enum" _gui-text="Units (mm or in):">
+                               <item value="G21 (All units in mm)">mm</item>
+                               <item value="G20 (All units in inches)">in</item>
+                       </param>
+
+                       <_param name="help" type="description">
+Orientation points are used to calculate transformation (offset,scale,mirror,rotation in XY plane) of the path.
+3-points mode only: do not put all three into one line (use 2-points mode instead).
+
+You can modify Z surface, Z depth values later using text tool (3rd coordinates).
+
+If there are no orientation points inside current layer they are taken from the upper layer.
+
+Do not ungroup orientation points! You can select them using double click to enter the group or by Ctrl+Click.
+
+Now press apply to create control points (independent set for each layer).
+                       </_param>
+               </page>
+
+               <page name='help' _gui-text='Help'>
+                       <_param name="fullhelp" type="description">
+Gcodetools plug-in: converts paths to Gcode (using circular interpolation), makes offset paths and engraves sharp corners using cone cutters. 
+This plug-in calculates Gcode for paths using circular interpolation or linear motion when needed.
+
+Tutorials, manuals and support can be found at
+English support forum:
+       http://www.cnc-club.ru/gcodetools
+       
+and Russian support forum:
+       http://www.cnc-club.ru/gcodetoolsru
+
+Credits: Nick Drobchenko, Vladimir Kalyaev, John Brooker, Henry Nicolas.
+
+Gcodetools ver. 1.6.01
+</_param>      
+
+               </page>
+
+       </param>        
+    <effect>
+               <effects-menu>
+                       <submenu _name="Gcodetools"/>
+               </effects-menu>
+               <object-type>path</object-type>
+    </effect>
+    <script>
+        <command reldir="extensions" interpreter="python">gcodetools.py</command>
+    </script>
+    
+</inkscape-extension>
diff --git a/share/extensions/gcodetools_path_to_gcode.inx b/share/extensions/gcodetools_path_to_gcode.inx
new file mode 100644 (file)
index 0000000..1be7583
--- /dev/null
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+    <_name>Path to Gcode</_name>
+    <id>ru.cnc-club.filter.gcodetools_ptg</id>
+       <dependency type="executable" location="extensions">gcodetools.py</dependency>
+       <dependency type="executable" location="extensions">inkex.py</dependency>
+       <param name='active-tab' type="notebook">
+
+               <page name='path-to-gcode' _gui-text='Path to Gcode'>   
+                               <param name="biarc-tolerance" type='float' precision="5" _gui-text='Biarc interpolation tolerance:'>1</param>
+                               <param name="biarc-max-split-depth" type="int" _gui-text="Maximum splitting depth:">4</param>
+                               <_param name="help" type="description">
+Biarc interpolation tolerance is the maximum distance between path and its approximation.
+The segment will be split into two segments if the distance between path's segment and it's approximation exceeds biarc interpolation tolerance.
+</_param>
+               </page>
+
+               <page name='options' _gui-text='Options'>
+                       <param name="Zscale" type="float" precision="5" min="-100000" max="100000" _gui-text="Scale along Z axis:">1</param>                    
+                       <param name="Zoffset" type="float" precision="5" min="-100000" max="100000" _gui-text="Offset along Z axis:">0.0</param>
+                       <param name="auto_select_paths" type="boolean" _gui-text="Select all paths if nothing is selected">true</param>
+                       <param name="min-arc-radius" type="float" precision="5" min="-1000" max="1000"  _gui-text="Minimum arc radius:">0.05</param>
+               </page>
+
+               <page name='preferences' _gui-text='Preferences'>
+                       <param name="filename" type="string" _gui-text="File:">output.ngc</param>
+                       <param name="add-numeric-suffix-to-filename" type="boolean" _gui-text="Add numeric suffix to filename">true</param>
+                       
+                       <param name="directory" type="string" _gui-text="Directory:">/home</param>
+
+                       <param name="Zsafe" type="float" precision="5" min="-1000" max="1000" _gui-text="Z safe height for G00 move over blank:">5</param>
+                       <param name="unit" type="enum" _gui-text="Units (mm or in):">
+                               <item value="G21 (All units in mm)">mm</item>
+                               <item value="G20 (All units in inches)">in</item>
+                       </param>
+                       <param name="postprocessor" type="enum" _gui-text="Post-processor:">
+                               <item value=" ">None</item>
+                               <item value="parameterize();">Parameterize Gcode</item>
+                               <item value="flip(y);parameterize();">Flip y axis and parameterize Gcode</item>
+                               <item value="round(4);">Round all values to 4 digits</item>
+                       </param>
+                       <param name="postprocessor-custom" type="string" _gui-text="Additional post-processor:"></param>                        
+                       
+
+                       <param name="create-log" type="boolean" _gui-text="Generate log file">false</param>                     
+                       <param name="log-filename" type="string" _gui-text="Full path to log file:"></param>                    
+                       
+               </page>
+
+               <page name='help' _gui-text='Help'>
+                       <_param name="fullhelp" type="description">
+Gcodetools plug-in: converts paths to Gcode (using circular interpolation), makes offset paths and engraves sharp corners using cone cutters. 
+This plug-in calculates Gcode for paths using circular interpolation or linear motion when needed.
+
+Tutorials, manuals and support can be found at
+English support forum:
+       http://www.cnc-club.ru/gcodetools
+       
+and Russian support forum:
+       http://www.cnc-club.ru/gcodetoolsru
+
+Credits: Nick Drobchenko, Vladimir Kalyaev, John Brooker, Henry Nicolas.
+
+Gcodetools ver. 1.6.01
+</_param>      
+
+               </page>
+
+       </param>        
+    <effect>
+               <effects-menu>
+                       <submenu _name="Gcodetools"/>
+               </effects-menu>
+               <object-type>path</object-type>
+    </effect>
+    <script>
+        <command reldir="extensions" interpreter="python">gcodetools.py</command>
+    </script>
+    
+</inkscape-extension>
diff --git a/share/extensions/gcodetools_tools_library.inx b/share/extensions/gcodetools_tools_library.inx
new file mode 100644 (file)
index 0000000..4d15dc5
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+       <_name>Tools library</_name>
+    <id>ru.cnc-club.filter.gcodetools_tools_library_no_options_no_preferences</id>
+       <dependency type="executable" location="extensions">gcodetools.py</dependency>
+       <dependency type="executable" location="extensions">inkex.py</dependency>
+       <param name='active-tab' type="notebook">
+
+               <page name='tools_library' _gui-text='Tools library'>   
+                       
+                       <param type='optiongroup' name='tools-library-type' _gui-text="Tools type:">
+<_option value='default tool'>default</_option>
+<_option value='cylinder cutter'>cylinder</_option>
+<_option value='cone cutter'>cone</_option>
+<_option value='plasma cutter'>plasma</_option>
+<_option value='tangent knife'>tangent knife</_option>
+<_option value='lathe cutter'>lathe cutter</_option>
+
+<_option value='check'>Just check tools</_option>
+
+                       </param>
+                       
+                       <_param name="help" type="description">
+Selected tool type fills appropriate default values. You can change these values using the Text tool later on.
+
+The topmost (z order) tool in the active layer is used. If there is no tool inside the current layer it is taken from the upper layer.
+
+Press Apply to create new tool.
+                       </_param>
+       </page>
+
+               <page name='help' _gui-text='Help'>
+                       <_param name="fullhelp" type="description">
+Gcodetools plug-in: converts paths to Gcode (using circular interpolation), makes offset paths and engraves sharp corners using cone cutters. 
+This plug-in calculates Gcode for paths using circular interpolation or linear motion when needed.
+
+Tutorials, manuals and support can be found at
+English support forum:
+       http://www.cnc-club.ru/gcodetools
+       
+and Russian support forum:
+       http://www.cnc-club.ru/gcodetoolsru
+
+Credits: Nick Drobchenko, Vladimir Kalyaev, John Brooker, Henry Nicolas.
+
+Gcodetools ver. 1.6.01
+</_param>      
+
+               </page>
+
+       </param>        
+    <effect>
+               <effects-menu>
+                       <submenu _name="Gcodetools"/>
+               </effects-menu>
+               <object-type>path</object-type>
+    </effect>
+    <script>
+        <command reldir="extensions" interpreter="python">gcodetools.py</command>
+    </script>
+    
+</inkscape-extension>