Code

Commit LivePathEffect branch to trunk!
authorjohanengelen <johanengelen@users.sourceforge.net>
Tue, 14 Aug 2007 20:54:48 +0000 (20:54 +0000)
committerjohanengelen <johanengelen@users.sourceforge.net>
Tue, 14 Aug 2007 20:54:48 +0000 (20:54 +0000)
(disabled extension/internal/bitmap/*.* in build.xml to fix compilation)

137 files changed:
build.xml
configure.ac
share/examples/live-path-effects-gears.svg [new file with mode: 0644]
share/examples/live-path-effects-skeletal.svg [new file with mode: 0644]
share/examples/live-path-effects-slant.svg [new file with mode: 0644]
share/examples/live-path-effects-uri.svg [new file with mode: 0644]
share/keys/default.xml
share/keys/inkscape.xml
src/2geom/Makefile_insert [new file with mode: 0644]
src/2geom/basic-intersection.cpp [new file with mode: 0644]
src/2geom/basic-intersection.h [new file with mode: 0644]
src/2geom/bezier-to-sbasis.h [new file with mode: 0644]
src/2geom/bezier-utils.cpp [new file with mode: 0644]
src/2geom/bezier-utils.h [new file with mode: 0644]
src/2geom/bezier.h [new file with mode: 0644]
src/2geom/choose.h [new file with mode: 0644]
src/2geom/circle-circle.cpp [new file with mode: 0644]
src/2geom/circulator.h [new file with mode: 0644]
src/2geom/concepts.h [new file with mode: 0644]
src/2geom/conjugate_gradient.cpp [new file with mode: 0644]
src/2geom/conjugate_gradient.h [new file with mode: 0644]
src/2geom/convex-cover.cpp [new file with mode: 0644]
src/2geom/convex-cover.h [new file with mode: 0644]
src/2geom/coord.h [new file with mode: 0644]
src/2geom/d2.cpp [new file with mode: 0644]
src/2geom/d2.h [new file with mode: 0644]
src/2geom/geom.cpp [new file with mode: 0644]
src/2geom/geom.h [new file with mode: 0644]
src/2geom/interval.h [new file with mode: 0644]
src/2geom/isnan.h [new file with mode: 0644]
src/2geom/linear.h [new file with mode: 0644]
src/2geom/makefile.in [new file with mode: 0644]
src/2geom/matrix.cpp [new file with mode: 0644]
src/2geom/matrix.h [new file with mode: 0644]
src/2geom/path.cpp [new file with mode: 0644]
src/2geom/path.h [new file with mode: 0644]
src/2geom/piecewise.cpp [new file with mode: 0644]
src/2geom/piecewise.h [new file with mode: 0644]
src/2geom/point-l.h [new file with mode: 0644]
src/2geom/point-ops.h [new file with mode: 0644]
src/2geom/point.cpp [new file with mode: 0644]
src/2geom/point.h [new file with mode: 0644]
src/2geom/poly-dk-solve.cpp [new file with mode: 0644]
src/2geom/poly-dk-solve.h [new file with mode: 0644]
src/2geom/poly-laguerre-solve.cpp [new file with mode: 0644]
src/2geom/poly-laguerre-solve.h [new file with mode: 0644]
src/2geom/poly.cpp [new file with mode: 0644]
src/2geom/poly.h [new file with mode: 0644]
src/2geom/quadtree.cpp [new file with mode: 0644]
src/2geom/quadtree.h [new file with mode: 0644]
src/2geom/rect.h [new file with mode: 0644]
src/2geom/sbasis-2d.cpp [new file with mode: 0644]
src/2geom/sbasis-2d.h [new file with mode: 0644]
src/2geom/sbasis-geometric.cpp [new file with mode: 0644]
src/2geom/sbasis-geometric.h [new file with mode: 0644]
src/2geom/sbasis-math.cpp [new file with mode: 0644]
src/2geom/sbasis-math.h [new file with mode: 0644]
src/2geom/sbasis-poly.cpp [new file with mode: 0644]
src/2geom/sbasis-poly.h [new file with mode: 0644]
src/2geom/sbasis-roots.cpp [new file with mode: 0644]
src/2geom/sbasis-to-bezier.cpp [new file with mode: 0644]
src/2geom/sbasis-to-bezier.h [new file with mode: 0644]
src/2geom/sbasis.cpp [new file with mode: 0644]
src/2geom/sbasis.h [new file with mode: 0644]
src/2geom/solve-bezier-one-d.cpp [new file with mode: 0644]
src/2geom/solve-bezier-parametric.cpp [new file with mode: 0644]
src/2geom/solver.h [new file with mode: 0644]
src/2geom/sturm.h [new file with mode: 0644]
src/2geom/svg-path-parser.cpp [new file with mode: 0644]
src/2geom/svg-path-parser.h [new file with mode: 0644]
src/2geom/svg-path.cpp [new file with mode: 0644]
src/2geom/svg-path.h [new file with mode: 0644]
src/2geom/transforms.cpp [new file with mode: 0644]
src/2geom/transforms.h [new file with mode: 0644]
src/2geom/utils.h [new file with mode: 0644]
src/Makefile.am
src/Makefile_insert
src/attributes.cpp
src/attributes.h
src/live_effects/Makefile_insert [new file with mode: 0644]
src/live_effects/effect.cpp [new file with mode: 0644]
src/live_effects/effect.h [new file with mode: 0644]
src/live_effects/lpe-gears.cpp [new file with mode: 0644]
src/live_effects/lpe-gears.h [new file with mode: 0644]
src/live_effects/lpe-skeletalstrokes.cpp [new file with mode: 0644]
src/live_effects/lpe-skeletalstrokes.h [new file with mode: 0644]
src/live_effects/lpe-slant.cpp [new file with mode: 0644]
src/live_effects/lpe-slant.h [new file with mode: 0644]
src/live_effects/lpe-test-doEffect-stack.cpp [new file with mode: 0644]
src/live_effects/lpe-test-doEffect-stack.h [new file with mode: 0644]
src/live_effects/lpeobject-reference.cpp [new file with mode: 0644]
src/live_effects/lpeobject-reference.h [new file with mode: 0644]
src/live_effects/lpeobject.cpp [new file with mode: 0644]
src/live_effects/lpeobject.h [new file with mode: 0644]
src/live_effects/makefile.in [new file with mode: 0644]
src/live_effects/n-art-bpath-2geom.cpp [new file with mode: 0644]
src/live_effects/n-art-bpath-2geom.h [new file with mode: 0644]
src/live_effects/parameter/Makefile_insert [new file with mode: 0644]
src/live_effects/parameter/enum.h [new file with mode: 0644]
src/live_effects/parameter/makefile.in [new file with mode: 0644]
src/live_effects/parameter/parameter.cpp [new file with mode: 0644]
src/live_effects/parameter/parameter.h [new file with mode: 0644]
src/live_effects/parameter/path.cpp [new file with mode: 0644]
src/live_effects/parameter/path.h [new file with mode: 0644]
src/live_effects/parameter/point.cpp [new file with mode: 0644]
src/live_effects/parameter/point.h [new file with mode: 0644]
src/live_effects/todo.txt [new file with mode: 0644]
src/menus-skeleton.h
src/node-context.cpp
src/nodepath.cpp
src/nodepath.h
src/selection-chemistry.cpp
src/selection-chemistry.h
src/shape-editor.cpp
src/shape-editor.h
src/sp-ellipse.cpp
src/sp-object-repr.cpp
src/sp-object.cpp
src/sp-path.cpp
src/sp-path.h
src/sp-shape.cpp
src/sp-shape.h
src/sp-spiral.cpp
src/sp-star.cpp
src/ui/dialog/Makefile_insert
src/ui/dialog/dialog-manager.cpp
src/ui/dialog/livepatheffect-editor.cpp [new file with mode: 0644]
src/ui/dialog/livepatheffect-editor.h [new file with mode: 0644]
src/ui/widget/Makefile_insert
src/ui/widget/combo-enums.h
src/ui/widget/point.cpp [new file with mode: 0644]
src/ui/widget/point.h [new file with mode: 0644]
src/ui/widget/registered-enums.h [new file with mode: 0644]
src/ui/widget/registered-widget.cpp
src/ui/widget/registered-widget.h
src/verbs.cpp
src/verbs.h

index bd798ea685fe236e26bf0e33c0acbc027a0bd620..7e152429d8c9e2c1bc24b8fada9da0ea08239bf8 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -50,8 +50,7 @@
   <property name="lib"         location="lib"/>
   <property name="build"       location="build"/>
   <property name="dist"        location="inkscape"/>
-
-
+  <property name="boost"         location="c:/boost"/>
 
 
 
             <exclude name="removeoverlap/test.cpp"/>
             <exclude name="removeoverlap/remove_rectangle_overlap-test.cpp"/>
             <exclude name="removeoverlap/remove_rectangle_overlap-test.h"/>
+            <exclude name="extension/internal/bitmap/.*"/>
         </fileset>
         <flags>
             -Wall -O3
             -Wno-comment -I${gtk}/perl/lib/CORE
             <!-- PYTHON -->
             -I${gtk}/python/include
+            <!-- 2geom -->
+            <!-- -I${gtk}/include/2geom -->
+            -I${boost}
         </includes>
     </cc>
   </target>
            -llcms.dll
            -lssl -lcrypto
            -lpng -ljpeg.dll -lpopt ${gtk}/lib/zdll.lib
-           -lgc -mwindows -lws2_32 -lintl -lm
+           -lgc -mconsole -lws2_32 -lintl -lgdi32 -lcomdlg32 -lm
+           <!-- 2geom -->
+           <!-- -l2geom -->
        </libs>
     </link>
   </target>
index b776663dcd2bf5fac77091f6c776a57aaa0adeb0..8e063e0ece77e29fdabb7f34532f7a0da3ecf2a0 100644 (file)
@@ -589,6 +589,9 @@ else
 fi
 PKG_CHECK_MODULES(INKSCAPE, gdkmm-2.4  glibmm-2.4  gtkmm-2.4  gtk+-2.0 >= 2.8.0  libxml-2.0 >= 2.6.11  libxslt >= 1.0.15  cairo  sigc++-2.0 >= $min_sigc_version  $ink_spell_pkg  gthread-2.0 >= 2.0 libpng >= 1.2)
 
+# Check for some boost header files
+AC_CHECK_HEADERS([boost/concept_check.hpp], [], AC_MSG_ERROR([You need the boost package (e.g. libboost-dev)]))
+
 PKG_CHECK_MODULES(CAIRO_PDF, cairo-pdf, cairo_pdf=yes, cairo_pdf=no)
 if test "x$cairo_pdf" = "xyes"; then
   AC_DEFINE(HAVE_CAIRO_PDF, 1, [Whether the Cairo PDF backend is available])
@@ -877,6 +880,8 @@ src/libnr/makefile
 src/libnrtype/makefile
 src/libavoid/makefile
 src/livarot/makefile
+src/live_effects/makefile
+src/live_effects/parameter/makefile
 src/pedro/makefile
 src/jabber_whiteboard/makefile
 src/removeoverlap/makefile
@@ -892,6 +897,7 @@ src/utest/makefile
 src/util/makefile
 src/widgets/makefile
 src/xml/makefile
+src/2geom/makefile
 doc/Makefile
 po/Makefile.in
 share/Makefile
diff --git a/share/examples/live-path-effects-gears.svg b/share/examples/live-path-effects-gears.svg
new file mode 100644 (file)
index 0000000..e85e000
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46dev+devel"
+   sodipodi:docname="live-path-effects-gears.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <inkscape:path-effect
+       effect="gears"
+       id="path-effect2210"
+       phi="5"
+       teeth="10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="736.68915"
+     inkscape:cy="212.1628"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="673"
+     inkscape:window-height="613"
+     inkscape:window-x="69"
+     inkscape:window-y="47" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M -17.8461,434.418 C -17.8461,434.418 -19.671,432.462 -21.7934,427.709 C -23.9158,422.956 -26.3108,415.396 -27.1858,405.021 C -20.4921,399.657 -13.384,394.811 -5.94569,390.539 C 3.39235,395.143 9.55485,400.135 13.2045,403.847 C 16.8541,407.559 18.008,409.972 18.008,409.972 L 29.6542,434.639 C 40.1721,429.673 51.4735,426.371 63.0105,424.893 L 59.5447,397.836 C 59.5447,397.836 59.2177,395.182 60.2947,390.088 C 61.3717,384.995 63.8776,377.471 69.2679,368.564 C 77.8359,368.159 86.4352,368.416 94.9637,369.332 C 99.812,378.546 101.864,386.206 102.634,391.355 C 103.405,396.503 102.92,399.134 102.92,399.134 L 97.8434,425.935 C 109.271,428.1 120.355,432.071 130.558,437.657 L 143.657,413.73 C 143.657,413.73 144.953,411.39 148.818,407.903 C 152.683,404.415 159.133,399.801 168.73,395.763 C 175.899,400.472 182.705,405.735 189.066,411.489 C 187.573,421.793 184.73,429.196 182.327,433.814 C 179.925,438.432 177.986,440.275 177.986,440.275 L 158.126,458.974 C 166.099,467.442 172.732,477.17 177.702,487.686 L 202.364,476.029 C 202.364,476.029 204.788,474.897 209.964,474.348 C 215.141,473.798 223.071,473.857 233.208,476.23 C 236.241,484.254 238.654,492.512 240.418,500.906 C 233.153,508.364 226.502,512.683 221.844,515.007 C 217.185,517.331 214.534,517.682 214.534,517.682 L 187.475,521.136 C 188.948,532.674 188.596,544.442 186.436,555.871 L 213.24,560.936 C 213.24,560.936 215.866,561.446 220.377,564.044 C 224.888,566.642 231.269,571.35 238.075,579.229 C 235.813,587.503 232.911,595.602 229.404,603.43 C 219.143,605.194 211.223,604.778 206.089,603.92 C 200.954,603.062 198.602,601.788 198.602,601.788 L 174.682,588.677 C 169.091,598.877 161.889,608.191 153.424,616.168 L 172.132,636.021 C 172.132,636.021 173.957,637.976 176.079,642.73 C 178.201,647.483 180.597,655.043 181.472,665.418 C 174.778,670.781 167.67,675.628 160.231,679.9 C 150.893,675.295 144.731,670.304 141.081,666.592 C 137.432,662.88 136.278,660.466 136.278,660.466 L 124.631,635.8 C 114.114,640.766 102.812,644.068 91.2752,645.546 L 94.7411,672.602 C 94.7411,672.602 95.068,675.257 93.991,680.35 C 92.914,685.443 90.4081,692.967 85.0179,701.875 C 76.4498,702.28 67.8505,702.023 59.322,701.106 C 54.4738,691.893 52.4219,684.232 51.6513,679.084 C 50.8806,673.936 51.3656,671.305 51.3656,671.305 L 56.4423,644.504 C 45.0142,642.339 33.9303,638.368 23.7281,632.782 L 10.6284,656.708 C 10.6284,656.708 9.33244,659.048 5.46747,662.536 C 1.60251,666.023 -4.84727,670.637 -14.4438,674.675 C -21.6135,669.967 -28.4193,664.704 -34.7805,658.95 C -33.287,648.646 -30.4444,641.243 -28.0418,636.625 C -25.6391,632.007 -23.7005,630.163 -23.7005,630.163 L -3.84002,611.465 C -11.8131,602.996 -18.4458,593.268 -23.4165,582.753 L -48.078,594.41 C -48.078,594.41 -50.5018,595.541 -55.6785,596.091 C -60.8552,596.64 -68.7852,596.582 -78.9225,594.208 C -81.9552,586.185 -84.3679,577.927 -86.132,569.533 C -78.8673,562.074 -72.2161,557.756 -67.5579,555.432 C -62.8996,553.108 -60.2479,552.756 -60.2479,552.756 L -33.1896,549.303 C -34.6623,537.765 -34.3103,525.996 -32.1507,514.567 L -58.9542,509.503 C -58.9542,509.503 -61.5802,508.993 -66.0912,506.395 C -70.6023,503.797 -76.9835,499.089 -83.7895,491.21 C -81.5269,482.936 -78.6249,474.837 -75.1181,467.009 C -64.8571,465.245 -56.9378,465.661 -51.8032,466.519 C -46.6686,467.377 -44.3167,468.651 -44.3167,468.651 L -20.3959,481.761 C -14.8057,471.561 -7.60352,462.247 0.861445,454.27 L -17.8461,434.418 M 205.039,648.02 C 205.039,648.02 202.329,648.46 197.143,647.348 C 191.956,646.237 184.304,643.538 175.489,637.635 C 175.016,629.567 175.249,621.457 176.184,613.429 C 185.323,608.041 193.117,605.785 198.359,604.973 C 203.601,604.161 206.281,604.756 206.281,604.756 L 232.973,610.872 C 235.409,600.24 239.956,590.097 246.271,581.204 L 223.945,565.348 C 223.945,565.348 221.718,563.743 218.836,559.29 C 215.954,554.836 212.452,547.517 210.393,537.11 C 215.764,531.07 221.663,525.5 228,520.485 C 238.272,523.137 245.379,527.053 249.66,530.186 C 253.94,533.318 255.415,535.634 255.415,535.634 L 269.964,558.832 C 279.205,553.037 289.592,549.08 300.346,547.257 L 295.771,520.258 C 295.771,520.258 295.331,517.549 296.442,512.362 C 297.554,507.175 300.253,499.523 306.156,490.708 C 314.224,490.236 322.334,490.468 330.362,491.403 C 335.75,500.542 338.005,508.336 338.818,513.578 C 339.63,518.82 339.035,521.5 339.035,521.5 L 332.919,548.192 C 343.55,550.628 353.694,555.175 362.587,561.49 L 378.443,539.164 C 378.443,539.164 380.048,536.937 384.501,534.055 C 388.954,531.174 396.274,527.671 406.681,525.612 C 412.72,530.983 418.29,536.882 423.306,543.22 C 420.654,553.492 416.737,560.598 413.605,564.879 C 410.473,569.16 408.157,570.634 408.157,570.634 L 384.958,585.183 C 390.753,594.424 394.711,604.812 396.534,615.566 L 423.532,610.99 C 423.532,610.99 426.242,610.55 431.429,611.662 C 436.615,612.773 444.267,615.472 453.082,621.375 C 453.555,629.443 453.322,637.553 452.388,645.581 C 443.249,650.969 435.454,653.225 430.213,654.037 C 424.971,654.849 422.291,654.254 422.291,654.254 L 395.599,648.138 C 393.163,658.77 388.616,668.914 382.3,677.807 L 404.627,693.662 C 404.627,693.662 406.854,695.267 409.736,699.72 C 412.617,704.174 416.119,711.493 418.179,721.9 C 412.808,727.94 406.909,733.51 400.571,738.526 C 390.299,735.873 383.193,731.957 378.912,728.824 C 374.631,725.692 373.156,723.377 373.156,723.377 L 358.607,700.178 C 349.367,705.973 338.979,709.93 328.225,711.753 L 332.801,738.752 C 332.801,738.752 333.241,741.461 332.129,746.648 C 331.018,751.835 328.319,759.487 322.416,768.302 C 314.347,768.774 306.238,768.542 298.209,767.607 C 292.822,758.468 290.566,750.674 289.754,745.432 C 288.942,740.19 289.536,737.51 289.536,737.51 L 295.653,710.818 C 285.021,708.382 274.877,703.835 265.984,697.52 L 250.129,719.846 C 250.129,719.846 248.524,722.073 244.07,724.955 C 239.617,727.837 232.298,731.339 221.891,733.398 C 215.851,728.027 210.281,722.128 205.265,715.79 C 207.918,705.518 211.834,698.412 214.966,694.131 C 218.099,689.85 220.414,688.376 220.414,688.376 L 243.613,673.827 C 237.818,664.586 233.86,654.198 232.038,643.444 L 205.039,648.02 M 393.246,529.095 C 393.246,529.095 390.738,529.893 385.592,529.931 C 380.447,529.968 372.664,529.224 362.825,526.207 C 360.611,517.533 358.997,508.705 357.997,499.809 C 366.132,493.505 373.148,490.055 377.974,488.269 C 382.8,486.482 385.428,486.342 385.428,486.342 L 412.566,484.991 C 411.964,472.893 412.951,460.717 415.495,448.875 L 388.929,443.168 C 388.929,443.168 386.358,442.606 381.883,440.065 C 377.408,437.525 371.04,432.99 364.027,425.457 C 366.448,416.838 369.463,408.386 373.046,400.182 C 383.243,398.79 391.044,399.31 396.116,400.176 C 401.189,401.042 403.535,402.234 403.535,402.234 L 427.713,414.633 C 433.24,403.855 440.183,393.804 448.307,384.821 L 428.154,366.595 C 428.154,366.595 426.209,364.823 423.603,360.385 C 420.998,355.948 417.751,348.836 415.444,338.806 C 421.85,332.552 428.687,326.74 435.892,321.426 C 445.419,325.319 451.914,329.67 455.874,332.956 C 459.834,336.242 461.27,338.448 461.27,338.448 L 476.009,361.275 C 486.185,354.705 497.223,349.472 508.751,345.754 L 500.41,319.894 C 500.41,319.894 499.612,317.386 499.574,312.24 C 499.537,307.095 500.281,299.312 503.298,289.473 C 511.972,287.259 520.8,285.645 529.696,284.645 C 536,292.78 539.45,299.796 541.236,304.622 C 543.023,309.447 543.163,312.076 543.163,312.076 L 544.514,339.214 C 556.612,338.612 568.788,339.599 580.63,342.143 L 586.337,315.577 C 586.337,315.577 586.899,313.006 589.44,308.531 C 591.98,304.056 596.515,297.688 604.048,290.675 C 612.667,293.095 621.119,296.111 629.323,299.694 C 630.715,309.891 630.195,317.691 629.329,322.764 C 628.463,327.836 627.271,330.183 627.271,330.183 L 614.872,354.361 C 625.65,359.888 635.701,366.831 644.684,374.955 L 662.91,354.802 C 662.91,354.802 664.682,352.856 669.12,350.251 C 673.558,347.646 680.669,344.399 690.699,342.092 C 696.953,348.498 702.765,355.335 708.079,362.54 C 704.186,372.067 699.835,378.562 696.549,382.522 C 693.263,386.482 691.057,387.918 691.057,387.918 L 668.23,402.657 C 674.8,412.833 680.033,423.871 683.751,435.399 L 709.611,427.058 C 709.611,427.058 712.119,426.259 717.265,426.222 C 722.41,426.185 730.193,426.929 740.033,429.946 C 742.246,438.62 743.86,447.447 744.86,456.344 C 736.725,462.648 729.709,466.098 724.883,467.884 C 720.058,469.67 717.43,469.811 717.43,469.811 L 690.291,471.162 C 690.893,483.26 689.906,495.435 687.362,507.278 L 713.928,512.985 C 713.928,512.985 716.499,513.547 720.974,516.087 C 725.449,518.628 731.817,523.163 738.83,530.696 C 736.41,539.315 733.394,547.767 729.811,555.971 C 719.614,557.363 711.814,556.843 706.741,555.977 C 701.669,555.111 699.322,553.919 699.322,553.919 L 675.144,541.52 C 669.617,552.298 662.674,562.348 654.55,571.332 L 674.703,589.558 C 674.703,589.558 676.649,591.33 679.254,595.768 C 681.859,600.205 685.106,607.317 687.413,617.347 C 681.007,623.601 674.17,629.413 666.965,634.727 C 657.438,630.834 650.943,626.483 646.983,623.197 C 643.023,619.91 641.587,617.705 641.587,617.705 L 626.848,594.878 C 616.672,601.448 605.634,606.681 594.106,610.399 L 602.447,636.259 C 602.447,636.259 603.246,638.767 603.283,643.913 C 603.32,649.058 602.576,656.841 599.559,666.68 C 590.885,668.894 582.058,670.508 573.161,671.508 C 566.857,663.373 563.407,656.357 561.621,651.531 C 559.835,646.705 559.694,644.077 559.694,644.077 L 558.343,616.939 C 546.245,617.541 534.07,616.554 522.227,614.01 L 516.52,640.576 C 516.52,640.576 515.958,643.147 513.418,647.622 C 510.877,652.097 506.342,658.465 498.809,665.478 C 490.19,663.057 481.738,660.042 473.534,656.459 C 472.142,646.262 472.662,638.461 473.528,633.389 C 474.394,628.316 475.586,625.97 475.586,625.97 L 487.985,601.792 C 477.207,596.265 467.157,589.322 458.173,581.198 L 439.948,601.351 C 439.948,601.351 438.175,603.296 433.737,605.902 C 429.3,608.507 422.188,611.754 412.158,614.061 C 405.904,607.655 400.092,600.818 394.778,593.613 C 398.671,584.086 403.022,577.591 406.308,573.631 C 409.595,569.671 411.8,568.235 411.8,568.235 L 434.627,553.496 C 428.057,543.32 422.824,532.282 419.106,520.754 L 393.246,529.095 M 740.79,533.59 C 740.79,533.59 739.564,531.279 738.551,526.255 C 737.538,521.232 736.756,513.493 737.71,503.288 C 745.969,499.444 754.479,496.142 763.169,493.41 C 770.757,500.301 775.398,506.542 778.039,510.935 C 780.679,515.327 781.332,517.861 781.332,517.861 L 788.017,544.143 C 799.935,541.112 812.219,539.525 824.515,539.429 L 824.303,512.311 C 824.303,512.311 824.291,509.694 825.729,504.775 C 827.167,499.856 830.07,492.64 835.658,484.048 C 844.757,484.482 853.827,485.513 862.791,487.133 C 866.307,496.76 867.516,504.444 867.813,509.56 C 868.11,514.676 867.511,517.223 867.511,517.223 L 861.216,543.602 C 873.177,546.456 884.792,550.76 895.724,556.389 L 908.139,532.278 C 908.139,532.278 909.344,529.956 912.903,526.269 C 916.462,522.581 922.387,517.541 931.328,512.53 C 939.182,517.143 946.734,522.271 953.919,527.871 C 952.558,538.03 950.058,545.395 947.943,550.063 C 945.829,554.732 944.114,556.708 944.114,556.708 L 926.282,577.14 C 935.546,585.226 943.83,594.434 950.895,604.5 L 973.092,588.92 C 973.092,588.92 975.239,587.424 980.104,585.812 C 984.969,584.201 992.557,582.492 1002.8,582.209 C 1007.61,589.944 1011.92,597.995 1015.68,606.292 C 1009.75,614.655 1004.11,620.015 1000.07,623.166 C 996.03,626.316 993.594,627.27 993.594,627.27 L 968.309,637.074 C 972.754,648.539 975.81,660.543 977.388,672.738 L 1004.28,669.258 C 1004.28,669.258 1006.88,668.931 1011.94,669.765 C 1016.99,670.6 1024.51,672.612 1033.71,677.124 C 1034.37,686.209 1034.44,695.337 1033.92,704.431 C 1024.78,709.082 1017.3,711.209 1012.26,712.12 C 1007.21,713.031 1004.61,712.743 1004.61,712.743 L 977.669,709.674 C 976.277,721.892 973.405,733.94 969.135,745.472 L 994.566,754.89 C 994.566,754.89 997.017,755.806 1001.11,758.895 C 1005.2,761.984 1010.91,767.257 1016.97,775.529 C 1013.33,783.882 1009.15,791.998 1004.46,799.805 C 994.211,799.678 986.598,798.085 981.709,796.548 C 976.82,795.011 974.651,793.548 974.651,793.548 L 952.219,778.308 C 945.308,788.48 937.165,797.813 928.025,806.039 L 946.167,826.197 C 946.167,826.197 947.911,828.147 950.097,832.783 C 952.282,837.418 954.894,844.745 956.41,854.882 C 949.311,860.59 941.838,865.833 934.055,870.565 C 925.038,865.69 919.038,860.741 915.423,857.108 C 911.808,853.475 910.568,851.172 910.568,851.172 L 897.787,827.253 C 886.942,833.048 875.394,837.528 863.478,840.565 L 870.174,866.844 C 870.174,866.844 870.812,869.382 870.593,874.502 C 870.374,879.622 869.282,887.323 865.913,897.003 C 856.974,898.759 847.921,899.928 838.83,900.501 C 833.112,891.995 830.099,884.824 828.586,879.927 C 827.073,875.031 827.046,872.414 827.046,872.414 L 826.845,845.296 C 814.548,845.387 802.241,843.988 790.279,841.138 L 783.995,867.52 C 783.995,867.52 783.381,870.063 780.808,874.495 C 778.235,878.927 773.688,885.238 766.207,892.244 C 757.476,889.645 748.916,886.473 740.601,882.755 C 739.491,872.566 740.155,864.816 741.091,859.777 C 742.028,854.739 743.219,852.409 743.219,852.409 L 755.644,828.304 C 744.713,822.67 734.466,815.711 725.198,807.629 L 707.374,828.069 C 707.374,828.069 705.648,830.035 701.31,832.764 C 696.972,835.492 690.014,838.968 680.133,841.694 C 673.611,835.335 667.505,828.549 661.87,821.393 C 665.622,811.854 669.812,805.301 672.983,801.275 C 676.153,797.248 678.291,795.739 678.291,795.739 L 700.495,780.169 C 693.434,770.101 687.595,759.177 683.144,747.714 L 657.864,757.529 C 657.864,757.529 655.422,758.468 650.312,758.868 C 645.203,759.268 637.426,759.112 627.411,756.934 C 624.59,748.273 622.338,739.426 620.674,730.471 C 628.429,723.769 635.184,719.913 639.863,717.821 C 644.542,715.73 647.135,715.387 647.135,715.387 L 674.032,711.919 C 672.459,699.723 672.365,687.337 673.752,675.118 L 646.805,672.06 C 646.805,672.06 644.207,671.757 639.497,669.737 C 634.787,667.716 627.974,663.964 620.117,657.381 C 621.645,648.401 623.762,639.522 626.45,630.818 C 636.432,628.488 644.205,628.214 649.32,628.536 C 654.434,628.858 656.89,629.76 656.89,629.76 L 682.318,639.188 C 686.593,627.659 692.266,616.647 699.172,606.473 L 676.733,591.243 C 676.733,591.243 674.573,589.766 671.341,585.789 C 668.11,581.811 663.821,575.322 659.923,565.842 C 665.449,558.601 671.451,551.722 677.875,545.265 C 687.796,547.841 694.807,551.21 699.186,553.872 C 703.565,556.534 705.321,558.474 705.321,558.474 L 723.454,578.639 C 732.598,570.417 742.738,563.303 753.581,557.504 L 740.79,533.59 M 782.757,874.714 C 782.757,874.714 782.966,872.071 784.949,867.294 C 786.932,862.517 790.711,855.616 797.475,847.79 C 806.16,849.054 814.753,850.953 823.162,853.465 C 825.999,863.413 826.519,871.263 826.305,876.431 C 826.091,881.599 825.167,884.084 825.167,884.084 L 815.564,909.559 C 826.694,913.754 837.22,919.546 846.72,926.703 L 863.101,904.957 C 863.101,904.957 864.705,902.847 868.956,899.901 C 873.207,896.954 880.117,893.192 890.038,890.264 C 896.661,896.024 902.863,902.267 908.58,908.927 C 905.588,918.829 901.781,925.714 898.807,929.946 C 895.833,934.178 893.712,935.768 893.712,935.768 L 871.861,952.008 C 878.955,961.554 884.679,972.117 888.802,983.274 L 914.339,973.837 C 914.339,973.837 916.829,972.929 921.999,972.749 C 927.168,972.568 935.015,973.139 944.944,976.04 C 947.401,984.466 949.244,993.071 950.452,1001.76 C 942.582,1008.48 935.657,1012.21 930.867,1014.16 C 926.077,1016.12 923.433,1016.31 923.433,1016.31 L 896.271,1018.15 C 897.078,1030.02 896.182,1042 893.619,1053.62 L 920.204,1059.48 C 920.204,1059.48 922.79,1060.07 927.236,1062.71 C 931.682,1065.35 937.975,1070.07 944.76,1077.88 C 942.272,1086.3 939.17,1094.54 935.486,1102.5 C 925.236,1103.89 917.391,1103.29 912.307,1102.34 C 907.222,1101.4 904.894,1100.13 904.894,1100.13 L 881.045,1087 C 875.308,1097.42 868.077,1107.01 859.641,1115.4 L 878.834,1134.71 C 878.834,1134.71 880.695,1136.59 883.006,1141.22 C 885.317,1145.85 888.058,1153.22 889.544,1163.46 C 882.9,1169.2 875.839,1174.45 868.433,1179.16 C 859.057,1174.79 852.784,1170.04 849.018,1166.49 C 845.253,1162.95 843.981,1160.62 843.981,1160.62 L 831.016,1136.68 C 820.557,1142.34 809.287,1146.51 797.657,1149 L 803.364,1175.62 C 803.364,1175.62 803.908,1178.21 803.351,1183.36 C 802.794,1188.5 801.112,1196.18 796.827,1205.6 C 788.138,1206.83 779.358,1207.43 770.582,1207.39 C 765.057,1198.65 762.347,1191.26 761.096,1186.24 C 759.845,1181.22 760.032,1178.58 760.032,1178.58 L 762.069,1151.43 C 750.208,1150.54 738.477,1147.95 727.345,1143.76 L 717.754,1169.24 C 717.754,1169.24 716.81,1171.71 713.561,1175.74 C 710.312,1179.76 704.742,1185.32 696.047,1190.92 C 688.07,1187.26 680.36,1183.02 672.999,1178.24 C 673.08,1167.9 674.793,1160.22 676.454,1155.32 C 678.115,1150.42 679.702,1148.3 679.702,1148.3 L 696.093,1126.56 C 686.596,1119.4 678.128,1110.88 671.03,1101.33 L 649.186,1117.58 C 649.186,1117.58 647.052,1119.16 642.143,1120.78 C 637.234,1122.41 629.544,1124.08 619.2,1124.09 C 614.468,1116.7 610.276,1108.96 606.667,1100.96 C 612.328,1092.3 617.92,1086.77 621.966,1083.55 C 626.012,1080.32 628.495,1079.4 628.495,1079.4 L 654.036,1069.97 C 649.918,1058.81 647.402,1047.06 646.59,1035.2 L 619.429,1037.06 C 619.429,1037.06 616.783,1037.23 611.773,1035.94 C 606.762,1034.66 599.394,1031.9 590.684,1026.32 C 590.7,1017.54 591.356,1008.77 592.646,1000.09 C 602.089,995.863 609.785,994.232 614.931,993.708 C 620.077,993.184 622.668,993.746 622.668,993.746 L 649.25,999.625 C 651.819,988.012 656.054,976.769 661.786,966.347 L 637.93,953.227 C 637.93,953.227 635.613,951.94 632.092,948.151 C 628.571,944.363 623.863,938.059 619.553,928.655 C 624.312,921.28 629.608,914.252 635.387,907.646 C 645.614,909.198 652.971,911.987 657.583,914.328 C 662.195,916.67 664.071,918.543 664.071,918.543 L 683.255,937.86 C 691.695,929.479 701.336,922.311 711.792,916.642 L 698.817,892.708 C 698.817,892.708 697.563,890.372 696.649,885.281 C 695.735,880.19 695.183,872.342 696.642,862.101 C 704.632,858.469 712.887,855.421 721.32,852.988 C 729.085,859.823 733.766,866.146 736.38,870.609 C 738.994,875.072 739.56,877.662 739.56,877.662 L 745.255,904.285 C 756.885,901.797 768.872,900.979 780.733,901.863 L 782.757,874.714"
+       id="path2208"
+       inkscape:path-effect="#path-effect2210"
+       inkscape:original-d="M -94.285714,283.79075 L 77.142857,535.21933 L 214.28571,512.36218 L 314.28571,629.50504 L 551.42857,478.07647 L 825.71429,692.36218 L 771.42857,1026.6479" />
+  </g>
+</svg>
diff --git a/share/examples/live-path-effects-skeletal.svg b/share/examples/live-path-effects-skeletal.svg
new file mode 100644 (file)
index 0000000..2404430
--- /dev/null
@@ -0,0 +1,448 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448"
+   height="1052.3622"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46dev+devel"
+   sodipodi:docbase="C:\Documents and Settings\jf\Mes documents"
+   sodipodi:docname="live-path-effects-skeletal.svg"
+   version="1.0"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2461"
+       origin="0,0"
+       pattern="M 119.92529,-0.41956101 C 119.92529,1.659909 120.58678,2.431279 121.32538,3.765079 C 120.76714,6.123869 120.41028,8.500889 122.37545,11.088229 C 121.671,13.006199 122.5692,14.924159 123.95054,16.842119 C 123.71907,18.682279 124.92357,20.284009 126.05068,21.898579 C 126.25318,23.990909 127.36543,26.083239 129.20088,28.175559 C 112.57483,22.944749 111.46952,10.168439 108.8996,7.775379 C 107.5348,6.504509 106.05778,6.654809 105.00417,5.064329 C 103.95056,3.473849 103.32038,0.14257899 102.24918,-0.41974101 C 103.32038,-0.98206101 103.95056,-4.313331 105.00417,-5.903821 C 106.05778,-7.494301 107.5348,-7.343991 108.8996,-8.614861 C 111.46952,-11.007921 112.57483,-23.784231 129.20088,-29.015041 C 127.36543,-26.922721 126.25318,-24.830391 126.05068,-22.738061 C 124.92357,-21.123491 123.71907,-19.521771 123.95054,-17.681611 C 122.5692,-15.763641 121.671,-13.845681 122.37545,-11.927711 C 120.41028,-9.340371 120.76714,-6.963351 121.32538,-4.604571 C 120.58678,-3.270761 119.92529,-2.499391 119.92529,-0.41992101 M 102.24917,-9.399491 C 101.73918,-8.349411 101.40752,-7.043061 101.33869,-5.702881 M 97.931762,-12.373781 C 97.196772,-10.747321 96.683652,-9.149741 96.517362,-7.611401 M 40.796126,-31.951641 C 36.393294,-26.573831 34.489541,-20.703561 33.158143,-15.440541 M 92.314832,-9.032131 C 92.607472,-11.125331 93.016652,-13.311561 94.188102,-15.314231 M 69.466596,-14.799961 C 70.526116,-19.749901 72.232996,-25.173971 76.035906,-29.701501 M 76.247336,-13.550161 C 77.232456,-17.658201 78.546166,-22.280971 81.496132,-26.588961 M 81.688812,-12.160461 C 82.459442,-15.577031 83.798922,-19.288461 86.595282,-22.724941 M 87.619382,-10.586331 C 88.269962,-13.778381 89.079762,-17.038011 90.529502,-19.434311 M 39.372996,-16.390721 C 40.663096,-22.091891 42.448976,-28.160771 46.526886,-32.647581 M 44.719896,-16.786451 C 46.020076,-21.938221 48.120906,-27.667231 52.729216,-33.084921 M 51.186366,-16.832791 C 52.460916,-21.977761 54.401586,-27.698631 58.580646,-33.111011 M 57.755366,-16.454151 C 59.035266,-21.645811 60.894386,-27.451691 64.905486,-32.894161 M 70.743036,-31.820641 C 68.781736,-29.846411 65.654026,-23.126311 64.075116,-15.756401 M 32.943271,13.696999 C 31.02686,5.738139 30.600382,-0.45243101 26.863458,-0.37462101 C 30.736989,-0.29396101 30.793459,-6.970991 33.158143,-15.440541 C 32.058705,-10.280411 31.563422,-4.759601 33.107444,-0.37462101 C 31.648043,3.659839 31.768408,8.716679 32.943271,13.696999 z M 101.2948,4.002579 C 101.17471,1.399589 101.32632,-0.45999101 98.464912,-0.37462101 C 101.67522,-0.27884101 100.95543,-2.624431 101.33869,-5.702881 C 101.31622,-3.566861 101.64612,-1.440751 102.07417,-0.37462101 L 102.07417,-0.37461101 C 101.70843,0.53633899 101.30509,2.215759 101.2948,4.002579 z M 96.421272,5.945939 C 95.961022,1.675379 97.120182,-0.47020101 92.570952,-0.37462101 C 97.432252,-0.27249101 95.639582,-2.793851 96.517362,-7.611401 C 96.251492,-4.242461 97.074992,-1.421851 98.464912,-0.37462101 C 97.205982,0.57391899 96.293052,3.033689 96.421272,5.945939 z M 92.199792,7.369879 C 91.682072,3.112599 91.561712,-0.45910101 87.418402,-0.37462101 C 91.855932,-0.28415101 91.596802,-4.354961 92.314832,-9.032131 C 91.936652,-5.782151 91.961722,-2.262891 92.570952,-0.37462101 C 92.017822,1.339789 91.874662,4.370259 92.199792,7.369879 z M 69.316276,13.139069 C 67.797866,5.660429 67.642976,-0.45234101 63.912806,-0.37462101 C 67.774536,-0.29415101 67.758886,-6.940511 69.466596,-14.799961 C 68.459376,-9.690321 68.383356,-4.346991 70.151986,-0.37462101 C 68.463476,3.297119 68.413876,8.256599 69.316276,13.139069 z M 76.078836,11.926069 C 74.460446,4.871139 74.036686,-0.45624101 70.151986,-0.37462101 C 74.171726,-0.29017101 74.285226,-6.166821 76.247336,-13.550161 C 75.280136,-8.280741 74.927876,-2.963871 76.515186,-0.37462101 C 75.005646,1.958549 75.063016,6.925809 76.078836,11.926069 z M 81.510842,10.533629 C 80.232116,4.381079 80.563616,-0.45735101 76.515186,-0.37462101 C 80.723636,-0.28862101 80.257296,-5.632921 81.688812,-12.160461 C 80.480436,-7.730861 80.156206,-3.060491 81.828332,-0.37462101 C 80.253876,2.021669 80.503246,6.293799 81.510842,10.533629 z M 87.231632,9.011219 C 86.277232,3.962159 85.596432,-0.45378101 81.828332,-0.37462101 C 85.721262,-0.29283101 86.645572,-5.247951 87.619382,-10.586331 C 86.643642,-6.535971 86.369042,-2.355891 87.418402,-0.37462101 C 86.435402,1.337139 86.488032,5.129359 87.231632,9.011219 z M 39.188486,14.663929 C 37.426416,6.563309 36.714489,-0.45040101 33.107444,-0.37462101 C 36.841626,-0.29617101 37.280464,-7.861881 39.372996,-16.390721 C 38.458246,-11.472261 38.047626,-5.894711 39.116846,-0.37462101 C 38.108106,4.729069 38.236876,9.909499 39.188486,14.663929 z M 44.509636,15.084679 C 42.430096,6.289929 42.889186,-0.45620101 39.116846,-0.37462101 C 43.005546,-0.29052101 42.225876,-7.554121 44.719896,-16.786451 C 43.333526,-11.095791 42.850856,-4.991741 45.126236,-0.37462101 C 42.961866,3.900459 43.131256,9.595039 44.509636,15.084679 z M 50.990786,15.148389 C 48.912506,6.307419 49.021366,-0.45645101 45.126236,-0.37462101 C 49.135236,-0.29039101 48.729696,-7.576121 51.186366,-16.832791 C 49.925426,-11.117121 49.450086,-4.984491 51.399926,-0.37462101 C 49.545316,3.893529 49.721436,9.637169 50.990786,15.148389 z M 57.565346,14.782989 C 55.531406,6.152689 55.300196,-0.45545101 51.399926,-0.37462101 C 55.418466,-0.29134101 55.350966,-7.413051 57.755366,-16.454151 C 56.576046,-10.870761 56.066836,-4.924771 57.673626,-0.37462101 C 56.143836,3.842269 56.352086,9.409209 57.565346,14.782989 z M 63.897286,14.082869 C 61.995916,5.914449 61.453026,-0.45402101 57.673626,-0.37462101 C 61.576106,-0.29263101 61.875306,-7.194241 64.075116,-15.756401 C 63.069866,-10.454371 62.668056,-4.819531 63.912806,-0.37462101 C 62.730386,3.735449 62.890676,9.007129 63.897286,14.082869 z M 101.2948,4.002579 C 101.2854,5.635799 101.62236,7.359649 102.24917,8.650249 M 96.421272,5.945939 C 96.500192,7.738359 97.047242,9.667199 97.931762,11.624549 M 94.188102,14.564989 C 92.844012,12.267189 92.486012,9.723619 92.199792,7.369879 M 90.529502,18.685079 C 88.532242,15.921769 87.858722,12.328759 87.231632,9.011219 M 86.595282,21.975709 C 83.596552,18.186189 82.259812,14.137229 81.510842,10.533629 M 76.078836,11.926069 C 77.283916,17.857939 79.761566,23.856219 81.496132,25.839729 M 76.035906,28.952269 C 72.027436,24.064879 70.360566,18.282469 69.316276,13.139069 M 63.897286,14.082869 C 65.423906,21.780689 68.711106,29.026089 70.743036,31.071409 M 64.905486,32.144929 C 60.689146,26.315529 58.832476,20.159629 57.565346,14.782989 M 58.580646,32.361779 C 54.188676,26.567379 52.244036,20.479709 50.990786,15.148389 M 52.729216,32.335679 C 47.874606,26.521609 45.773926,20.431609 44.509636,15.084679 M 46.526886,31.898339 C 42.242406,27.064939 40.476646,20.585919 39.188486,14.663929 M 32.943271,13.696999 C 34.257898,19.156649 36.145459,25.406929 40.796126,31.202409 M 120.27531,-19.250851 C 122.72547,-21.866261 126.05068,-22.738061 126.05068,-22.738061 M 116.60008,-13.845671 C 119.57527,-16.461081 123.95054,-17.681611 123.95054,-17.681611 M 113.7999,-7.045611 C 117.82516,-11.578991 122.37545,-11.927711 122.37545,-11.927711 M 111.69977,-2.512241 C 116.7751,-4.604571 121.32538,-4.604571 121.32538,-4.604571 M 120.27531,18.411369 C 122.72547,21.026779 126.05068,21.898579 126.05068,21.898579 M 116.60008,13.006189 C 119.57527,15.621599 123.95054,16.842119 123.95054,16.842119 M 113.7999,6.206129 C 117.82516,10.739509 122.37545,11.088229 122.37545,11.088229 M 111.69977,1.672759 C 116.7751,3.765079 121.32538,3.765079 121.32538,3.765079 M 19.293941,-10.620001 C 19.293941,-10.186891 18.98032,-9.835381 18.593898,-9.835381 C 18.207472,-9.835381 17.893856,-10.186891 17.893856,-10.620001 C 17.893856,-11.053121 18.207472,-11.404621 18.593898,-11.404621 C 18.98032,-11.404621 19.293941,-11.053121 19.293941,-10.620001 z M 25.856859,27.564939 C 30.582156,27.477759 25.856859,6.205779 25.856859,-0.59428101 C 25.856859,-7.394341 27.795183,-22.180491 33.032312,-28.927861 C 38.358126,-35.789491 -1.283572,-12.994371 1.617831,4.810899 C 4.855537,6.641689 9.755844,7.600669 13.693591,9.082729 C 10.01836,9.344269 5.730588,8.210929 2.667892,7.426299 C 4.418006,11.087879 10.543393,21.200789 25.856859,27.564939 z M 21.219074,-11.796931 C 21.219074,-10.473531 20.297813,-9.399471 19.162695,-9.399471 C 18.027572,-9.399471 17.106311,-10.473531 17.106311,-11.796931 C 17.106311,-13.120321 18.027572,-14.194381 19.162695,-14.194381 C 20.297813,-14.194381 21.219074,-13.120321 21.219074,-11.796931 z M 19.293941,-10.620001 C 19.293941,-10.186891 18.98032,-9.835381 18.593898,-9.835381 C 18.207472,-9.835381 17.893856,-10.186891 17.893856,-10.620001 C 17.893856,-11.053121 18.207472,-11.404621 18.593898,-11.404621 C 18.98032,-11.404621 19.293941,-11.053121 19.293941,-10.620001 z" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2455"
+       origin="0.1,200"
+       pattern="M 139.18065,483.47597 C 139.18065,485.55544 139.84214,486.32681 140.58074,487.66061 C 140.0225,490.0194 139.66564,492.39642 141.63081,494.98376 C 140.92636,496.90173 141.82456,498.81969 143.2059,500.73765 C 142.97443,502.57781 144.17893,504.17954 145.30604,505.79411 C 145.50854,507.88644 146.62079,509.97877 148.45624,512.07109 C 131.83019,506.84028 130.72488,494.06397 128.15496,491.67091 C 126.79016,490.40004 125.31314,490.55034 124.25953,488.95986 C 123.20592,487.36938 122.57574,484.03811 121.50454,483.47579 C 122.57574,482.91347 123.20592,479.5822 124.25953,477.99171 C 125.31314,476.40123 126.79016,476.55154 128.15496,475.28067 C 130.72488,472.88761 131.83019,460.1113 148.45624,454.88049 C 146.62079,456.97281 145.50854,459.06514 145.30604,461.15747 C 144.17893,462.77204 142.97443,464.37376 143.2059,466.21392 C 141.82456,468.13189 140.92636,470.04985 141.63081,471.96782 C 139.66564,474.55516 140.0225,476.93218 140.58074,479.29096 C 139.84214,480.62477 139.18065,481.39614 139.18065,483.47561 M 121.50453,474.49604 C 120.99454,475.54612 120.66288,476.85247 120.59405,478.19265 M 117.18712,471.52175 C 116.45213,473.14821 115.93901,474.74579 115.77272,476.28413 M 60.051484,451.94389 C 55.648652,457.3217 53.744899,463.19197 52.413501,468.45499 M 111.57019,474.8634 C 111.86283,472.7702 112.27201,470.58397 113.44346,468.5813 M 88.721954,469.09557 C 89.781474,464.14563 91.488354,458.72156 95.291264,454.19403 M 95.502694,470.34537 C 96.487814,466.23733 97.801524,461.61456 100.75149,457.30657 M 100.94417,471.73507 C 101.7148,468.3185 103.05428,464.60707 105.85064,461.17059 M 106.87474,473.3092 C 107.52532,470.11715 108.33512,466.85752 109.78486,464.46122 M 58.628354,467.50481 C 59.918454,461.80364 61.704334,455.73476 65.782244,451.24795 M 63.975254,467.10908 C 65.275434,461.95731 67.376264,456.2283 71.984574,450.81061 M 70.441724,467.06274 C 71.716274,461.91777 73.656944,456.1969 77.836004,450.78452 M 77.010724,467.44138 C 78.290624,462.24972 80.149744,456.44384 84.160844,451.00137 M 89.998394,452.07489 C 88.037094,454.04912 84.909384,460.76922 83.330474,468.13913 M 52.198629,497.59253 C 50.282218,489.63367 49.85574,483.4431 46.118816,483.52091 C 49.992347,483.60157 50.048817,476.92454 52.413501,468.45499 C 51.314063,473.61512 50.81878,479.13593 52.362802,483.52091 C 50.903401,487.55537 51.023766,492.61221 52.198629,497.59253 z M 120.55016,487.89811 C 120.43007,485.29512 120.58168,483.43554 117.72027,483.52091 C 120.93058,483.61669 120.21079,481.2711 120.59405,478.19265 C 120.57158,480.32867 120.90148,482.45478 121.32953,483.52091 L 121.32953,483.52092 C 120.96379,484.43187 120.56045,486.11129 120.55016,487.89811 z M 115.67663,489.84147 C 115.21638,485.57091 116.37554,483.42533 111.82631,483.52091 C 116.68761,483.62304 114.89494,481.10168 115.77272,476.28413 C 115.50685,479.65307 116.33035,482.47368 117.72027,483.52091 C 116.46134,484.46945 115.54841,486.92922 115.67663,489.84147 z M 111.45515,491.26541 C 110.93743,487.00813 110.81707,483.43643 106.67376,483.52091 C 111.11129,483.61138 110.85216,479.54057 111.57019,474.8634 C 111.19201,478.11338 111.21708,481.63264 111.82631,483.52091 C 111.27318,485.23532 111.13002,488.26579 111.45515,491.26541 z M 88.571634,497.0346 C 87.053224,489.55596 86.898334,483.44319 83.168164,483.52091 C 87.029894,483.60138 87.014244,476.95502 88.721954,469.09557 C 87.714734,474.20521 87.638714,479.54854 89.407344,483.52091 C 87.718834,487.19265 87.669234,492.15213 88.571634,497.0346 z M 95.334194,495.8216 C 93.715804,488.76667 93.292044,483.43929 89.407344,483.52091 C 93.427084,483.60536 93.540584,477.72871 95.502694,470.34537 C 94.535494,475.61479 94.183234,480.93166 95.770544,483.52091 C 94.261004,485.85408 94.318374,490.82134 95.334194,495.8216 z M 100.7662,494.42916 C 99.487474,488.27661 99.818974,483.43818 95.770544,483.52091 C 99.978994,483.60691 99.512654,478.26261 100.94417,471.73507 C 99.735794,476.16467 99.411564,480.83504 101.08369,483.52091 C 99.509234,485.9172 99.758604,490.18933 100.7662,494.42916 z M 106.48699,492.90675 C 105.53259,487.85769 104.85179,483.44175 101.08369,483.52091 C 104.97662,483.6027 105.90093,478.64758 106.87474,473.3092 C 105.899,477.35956 105.6244,481.53964 106.67376,483.52091 C 105.69076,485.23267 105.74339,489.02489 106.48699,492.90675 z M 58.443844,498.55946 C 56.681774,490.45884 55.969847,483.44513 52.362802,483.52091 C 56.096984,483.59936 56.535822,476.03365 58.628354,467.50481 C 57.713604,472.42327 57.302984,478.00082 58.372204,483.52091 C 57.363464,488.6246 57.492234,493.80503 58.443844,498.55946 z M 63.764994,498.98021 C 61.685454,490.18546 62.144544,483.43933 58.372204,483.52091 C 62.260904,483.60501 61.481234,476.34141 63.975254,467.10908 C 62.588884,472.79974 62.106214,478.90379 64.381594,483.52091 C 62.217224,487.79599 62.386614,493.49057 63.764994,498.98021 z M 70.246144,499.04392 C 68.167864,490.20295 68.276724,483.43908 64.381594,483.52091 C 68.390594,483.60514 67.985054,476.31941 70.441724,467.06274 C 69.180784,472.77841 68.705444,478.91104 70.655284,483.52091 C 68.800674,487.78906 68.976794,493.5327 70.246144,499.04392 z M 76.820704,498.67852 C 74.786764,490.04822 74.555554,483.44008 70.655284,483.52091 C 74.673824,483.60419 74.606324,476.48248 77.010724,467.44138 C 75.831404,473.02477 75.322194,478.97076 76.928984,483.52091 C 75.399194,487.7378 75.607444,493.30474 76.820704,498.67852 z M 83.152644,497.9784 C 81.251274,489.80998 80.708384,483.44151 76.928984,483.52091 C 80.831464,483.6029 81.130664,476.70129 83.330474,468.13913 C 82.325224,473.44116 81.923414,479.076 83.168164,483.52091 C 81.985744,487.63098 82.146034,492.90266 83.152644,497.9784 z M 120.55016,487.89811 C 120.54076,489.53133 120.87772,491.25518 121.50453,492.54578 M 115.67663,489.84147 C 115.75555,491.63389 116.3026,493.56273 117.18712,495.52008 M 113.44346,498.46052 C 112.09937,496.16272 111.74137,493.61915 111.45515,491.26541 M 109.78486,502.58061 C 107.7876,499.8173 107.11408,496.22429 106.48699,492.90675 M 105.85064,505.87124 C 102.85191,502.08172 101.51517,498.03276 100.7662,494.42916 M 95.334194,495.8216 C 96.539274,501.75347 99.016924,507.75175 100.75149,509.73526 M 95.291264,512.8478 C 91.282794,507.96041 89.615924,502.178 88.571634,497.0346 M 83.152644,497.9784 C 84.679264,505.67622 87.966464,512.92162 89.998394,514.96694 M 84.160844,516.04046 C 79.944504,510.21106 78.087834,504.05516 76.820704,498.67852 M 77.836004,516.25731 C 73.444034,510.46291 71.499394,504.37524 70.246144,499.04392 M 71.984574,516.23121 C 67.129964,510.41714 65.029284,504.32714 63.764994,498.98021 M 65.782244,515.79387 C 61.497764,510.96047 59.732004,504.48145 58.443844,498.55946 M 52.198629,497.59253 C 53.513256,503.05218 55.400817,509.30246 60.051484,515.09794 M 139.53067,464.64468 C 141.98083,462.02927 145.30604,461.15747 145.30604,461.15747 M 135.85544,470.04986 C 138.83063,467.43445 143.2059,466.21392 143.2059,466.21392 M 133.05526,476.84992 C 137.08052,472.31654 141.63081,471.96782 141.63081,471.96782 M 130.95513,481.38329 C 136.03046,479.29096 140.58074,479.29096 140.58074,479.29096 M 139.53067,502.3069 C 141.98083,504.92231 145.30604,505.79411 145.30604,505.79411 M 135.85544,496.90172 C 138.83063,499.51713 143.2059,500.73765 143.2059,500.73765 M 133.05526,490.10166 C 137.08052,494.63504 141.63081,494.98376 141.63081,494.98376 M 130.95513,485.56829 C 136.03046,487.66061 140.58074,487.66061 140.58074,487.66061 M 38.549299,473.27553 C 38.549299,473.70864 38.235678,474.06015 37.849256,474.06015 C 37.46283,474.06015 37.149214,473.70864 37.149214,473.27553 C 37.149214,472.84241 37.46283,472.49091 37.849256,472.49091 C 38.235678,472.49091 38.549299,472.84241 38.549299,473.27553 z M 45.112217,511.46047 C 49.837514,511.37329 45.112217,490.10131 45.112217,483.30125 C 45.112217,476.50119 47.050541,461.71504 52.28767,454.96767 C 57.613484,448.10604 17.971786,470.90116 20.873189,488.70643 C 24.110895,490.53722 29.011202,491.4962 32.948949,492.97826 C 29.273718,493.2398 24.985946,492.10646 21.92325,491.32183 C 23.673364,494.98341 29.798751,505.09632 45.112217,511.46047 z M 40.474432,472.0986 C 40.474432,473.422 39.553171,474.49606 38.418053,474.49606 C 37.28293,474.49606 36.361669,473.422 36.361669,472.0986 C 36.361669,470.77521 37.28293,469.70115 38.418053,469.70115 C 39.553171,469.70115 40.474432,470.77521 40.474432,472.0986 z M 38.549299,473.27553 C 38.549299,473.70864 38.235678,474.06015 37.849256,474.06015 C 37.46283,474.06015 37.149214,473.70864 37.149214,473.27553 C 37.149214,472.84241 37.46283,472.49091 37.849256,472.49091 C 38.235678,472.49091 38.549299,472.84241 38.549299,473.27553 z" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2403"
+       origin="0.1,350" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2399"
+       origin="0.1,0.2"
+       pattern="M 377.47385,-87.380536 C 377.47385,-87.380536 376.80416,-86.692757 375.64606,-85.503375 C 375.12878,-84.97213 374.51407,-84.340813 373.81807,-83.626014 C 373.25847,-83.051296 372.64632,-82.42261 371.99001,-81.748579 C 371.41292,-81.155896 370.80168,-80.528154 370.16202,-79.871215 C 369.57515,-79.268489 368.96434,-78.641185 368.33402,-77.993832 C 367.74084,-77.384634 367.13038,-76.75768 366.5063,-76.116744 C 365.90835,-75.502649 365.29791,-74.875718 364.6782,-74.23927 C 364.07666,-73.621479 363.46638,-72.99472 362.85033,-72.362029 C 362.24567,-71.741039 361.63545,-71.114335 361.02246,-70.484786 C 360.41493,-69.860844 359.80467,-69.234108 359.19442,-68.607373 C 358.58415,-67.980622 357.97389,-67.353873 357.36634,-66.729917 C 356.75337,-66.100386 356.14317,-65.473701 355.53853,-64.85273 C 354.92252,-64.220081 354.31228,-63.593363 353.71078,-62.975612 C 353.09118,-62.339275 352.48084,-61.712454 351.883,-61.098466 C 351.25887,-60.457481 350.64836,-59.83048 350.05514,-59.221236 C 349.42485,-58.573925 348.81409,-57.946659 348.22725,-57.343968 C 347.58763,-56.687079 346.97645,-56.059383 346.39939,-55.466743 C 345.74316,-54.792784 345.13107,-54.164157 344.57151,-53.589485 C 343.87555,-52.874728 343.26086,-52.243435 342.74359,-51.712195 C 341.58531,-50.522626 340.9155,-49.83473 340.9155,-49.83473 C 340.32792,-49.104443 339.81526,-48.412938 339.37269,-47.757699 C 338.83536,-46.962174 338.40133,-46.22011 338.06195,-45.527008 C 337.62634,-44.637396 337.34665,-43.828447 337.20456,-43.090652 C 337.01793,-42.121635 337.06867,-41.275362 337.31526,-40.530283 C 337.5843,-39.717379 338.08647,-39.024935 338.76789,-38.424964 C 339.36613,-37.898228 340.10251,-37.442773 340.94058,-37.039659 C 341.67165,-36.688011 342.48009,-36.376193 343.3417,-36.09164 C 344.1298,-35.83136 344.96239,-35.593887 345.82092,-35.369604 C 346.63895,-35.155907 347.48053,-34.954183 348.32965,-34.756113 C 349.16607,-34.561007 350.00979,-34.369447 350.84551,-34.17348 C 351.69483,-33.974321 352.53589,-33.770615 353.35259,-33.554006 C 354.21099,-33.326341 355.04248,-33.084425 355.82841,-32.81857 C 356.68848,-32.527635 357.49398,-32.208027 358.22044,-31.847047 C 358.94005,-31.108817 358.71342,-30.414172 357.96718,-29.74343 C 357.46922,-29.295841 356.73987,-28.858898 355.90592,-28.426749 C 355.21747,-28.069994 354.45772,-27.716506 353.698,-27.362998 C 352.93824,-27.009469 352.1785,-26.655918 351.49014,-26.299054 C 350.65625,-25.866746 349.92708,-25.429576 349.42945,-24.981695 C 348.68422,-24.310967 348.45826,-23.616212 349.17746,-22.877786 C 349.62543,-22.418229 350.3278,-21.963408 351.2132,-21.511987 C 351.87081,-21.176705 352.62938,-20.843301 353.45966,-20.511231 C 354.17991,-20.223169 354.95413,-19.936112 355.76321,-19.649702 C 356.51396,-19.383942 357.29472,-19.118743 358.09026,-18.853819 C 358.85992,-18.597509 359.64341,-18.341454 360.42691,-18.085401 C 361.21039,-17.829354 361.99388,-17.573304 362.76357,-17.317 C 363.55917,-17.052063 364.34003,-16.786852 365.09088,-16.521082 C 365.90009,-16.23465 366.67446,-15.947574 367.39487,-15.659489 C 368.22507,-15.327507 368.98362,-14.994188 369.6413,-14.658991 C 370.5265,-14.20784 371.22895,-13.753287 371.67742,-13.294005 C 372.38115,-12.572067 372.45703,-11.840436 372.18189,-11.103392 C 371.93986,-10.455024 371.42619,-9.8024664 370.82934,-9.1486226 C 370.30243,-8.5714026 369.71069,-7.9931803 369.18377,-7.4159603 C 368.58687,-6.7620727 368.07316,-6.1094716 367.83113,-5.4610629 C 367.55601,-4.7240322 367.63191,-3.9924229 368.33562,-3.2705012 C 368.9359,-2.6546948 369.68437,-2.2644063 370.51328,-1.9863237 C 371.28865,-1.7262015 372.13439,-1.5642605 372.99504,-1.4077518 C 373.82944,-1.2560145 374.67786,-1.1093846 375.48974,-0.88333954 C 376.31941,-0.65234284 377.11092,-0.33841052 377.81032,0.14865531 C 378.46473,0.60438334 379.03849,1.211682 379.48743,2.044444 C 379.84025,2.6989242 380.11598,3.4926628 380.29315,4.461526 C 380.47028,5.4329128 381.01387,6.1300297 381.82143,6.5943802 C 382.51346,6.9922996 383.39934,7.2192869 384.41457,7.3014633 C 385.24149,7.3683968 386.15422,7.3392549 387.11792,7.2281579 C 387.97389,7.1294769 388.87007,6.9661375 389.78205,6.7480266 C 390.64171,6.5424273 391.51539,6.2881608 392.38266,5.9935134 C 393.23718,5.7031923 394.08547,5.3736621 394.90796,5.0128542 C 395.75171,4.6427138 396.5683,4.239657 397.33661,3.8122324 C 398.16333,3.3523147 398.93415,2.8641837 399.62275,2.358502 C 400.44747,1.5117371 400.43842,0.90434284 400.02642,0.41268314 C 399.64124,-0.046966894 398.90389,-0.40546694 398.16639,-0.76384408 C 397.429,-1.1221666 396.69147,-1.4803717 396.30567,-1.9394345 C 395.89256,-2.4309931 395.88274,-3.0381935 396.70821,-3.8850212 C 397.31651,-4.5093353 397.92159,-5.0553541 398.5216,-5.5350811 C 399.20107,-6.0783359 399.87404,-6.5365849 400.53781,-6.9272584 C 401.28264,-7.3656406 402.01589,-7.7189405 402.73374,-8.0117934 C 403.52769,-8.3356891 404.30282,-8.5856458 405.05395,-8.7949896 C 405.87792,-9.0246343 406.67303,-9.2054097 407.43246,-9.3813181 C 408.26836,-9.5749384 409.06105,-9.7626619 409.80144,-10.003158 C 410.60314,-10.263572 411.34353,-10.585865 412.01107,-11.044526 C 412.64733,-11.481693 413.21742,-12.04275 413.71135,-12.792194 C 414.11005,-13.397141 414.45913,-14.124834 414.75335,-15.009199 C 415.01489,-15.79561 414.73037,-16.511749 414.12644,-17.179391 C 413.6614,-17.693488 413.00697,-18.178827 412.26665,-18.645355 C 411.62276,-19.051104 410.9139,-19.442622 410.20815,-19.826446 C 409.48978,-20.217127 408.77463,-20.59984 408.13448,-20.981476 C 407.34439,-21.452507 406.66857,-21.921904 406.24198,-22.402631 C 405.65033,-23.069374 405.53814,-23.757918 406.26552,-24.50285 C 406.93191,-24.893431 407.71705,-25.274119 408.59447,-25.650069 C 409.339,-25.969081 410.14996,-26.284682 411.01117,-26.600018 C 411.79091,-26.885522 412.61184,-27.170812 413.46194,-27.458221 C 414.2609,-27.72834 415.08563,-28.000332 415.92615,-28.276137 C 416.73681,-28.542142 417.56216,-28.811692 418.39326,-29.086533 C 419.21082,-29.3569 420.03395,-29.632382 420.85412,-29.914638 C 421.67542,-30.197285 422.49376,-30.486721 423.30057,-30.784613 C 424.12267,-31.088153 424.93281,-31.400468 425.72194,-31.723326 C 426.54097,-32.058414 427.33737,-32.404856 428.10102,-32.764618 C 428.91097,-33.146197 429.68408,-33.542759 430.40829,-33.956656 C 431.19687,-34.407345 431.92747,-34.878589 432.58448,-35.373414 C 433.32089,-35.928033 433.96486,-36.512281 434.49445,-37.130419 C 435.09095,-37.826654 435.54235,-38.565889 435.81729,-39.354222 C 436.09732,-40.157163 436.19429,-41.011039 436.07508,-41.922296 C 435.97322,-42.700833 435.71356,-43.521255 435.27544,-44.387581 C 434.92324,-45.084004 434.45572,-45.810088 433.86213,-46.567925 C 433.37295,-47.192455 432.79817,-47.838545 432.13176,-48.507367 C 432.13176,-48.507367 431.43759,-49.219462 430.23717,-50.450891 C 429.70099,-51.00092 429.06381,-51.654553 428.34237,-52.394624 C 427.76231,-52.989659 427.12779,-53.640573 426.4475,-54.338432 C 425.84931,-54.952067 425.21574,-55.602003 424.5527,-56.282167 C 423.94437,-56.9062 423.31124,-57.555682 422.65788,-58.225921 C 422.04302,-58.856657 421.41024,-59.505775 420.76335,-60.16937 C 420.14356,-60.805174 419.5108,-61.454271 418.86844,-62.113219 C 418.24492,-62.75285 417.61233,-63.401767 416.97377,-64.056826 C 416.34701,-64.699769 415.71448,-65.348629 415.07909,-66.000435 C 414.44935,-66.646435 413.81679,-67.29533 413.18424,-67.944221 C 412.55174,-68.593055 411.91924,-69.241889 411.28956,-69.88783 C 410.65418,-70.539623 410.02167,-71.18847 409.39492,-71.8314 C 408.75639,-72.486423 408.12384,-73.135304 407.50035,-73.774901 C 406.8581,-74.43374 406.22545,-75.082729 405.60575,-75.718432 C 404.9588,-76.382086 404.32597,-77.031261 403.71106,-77.662049 C 403.05773,-78.332255 402.42463,-78.981705 401.81633,-79.605711 C 401.15333,-80.285831 400.5198,-80.935726 399.92165,-81.549327 C 399.24142,-82.247124 398.60695,-82.897982 398.02693,-83.492978 C 397.30552,-84.233017 396.66836,-84.886638 396.13217,-85.436668 C 394.93154,-86.66831 394.23724,-87.380536 394.23724,-87.380536 C 393.59189,-88.002366 392.88643,-88.538559 392.13826,-88.988716 C 391.37657,-89.447007 390.5706,-89.816126 389.73869,-90.09565 C 388.89909,-90.377762 388.03307,-90.568614 387.1595,-90.667773 C 386.28026,-90.767577 385.39336,-90.774493 384.51804,-90.688081 C 383.63826,-90.601229 382.77017,-90.420093 381.9333,-90.144226 C 381.09673,-89.868458 380.29136,-89.498026 379.5367,-89.032485 C 378.79373,-88.574161 378.09991,-88.023654 377.47385,-87.380536 C 377.47385,-87.380536 377.47385,-87.380536 377.47385,-87.380536 M 419.46669,-15.009199 C 419.46669,-14.550636 420.29548,-14.130971 421.54498,-13.811415 C 422.25697,-13.629323 423.10557,-13.479738 424.01527,-13.37398 C 424.81931,-13.28051 425.67107,-13.221276 426.51845,-13.204102 C 427.37673,-13.186705 428.2305,-13.212457 429.0256,-13.28948 C 429.95272,-13.379295 430.80007,-13.538825 431.48175,-13.780951 C 432.49433,-14.140614 433.14138,-14.682531 433.14138,-15.448922 C 432.86041,-16.264931 432.40976,-16.955326 431.83935,-17.522225 C 431.26182,-18.096198 430.56153,-18.543574 429.79029,-18.866557 C 429.04638,-19.1781 428.23646,-19.373905 427.40705,-19.455947 C 426.59298,-19.536474 425.76013,-19.507409 424.95248,-19.370619 C 424.13147,-19.231568 423.3365,-18.981197 422.61376,-18.62147 C 421.86467,-18.248626 421.19318,-17.758307 420.65073,-17.152694 C 420.11197,-16.55121 419.70049,-15.835996 419.46669,-15.009199 C 419.46669,-15.009199 419.46669,-15.009199 419.46669,-15.009199 M 357.86667,-5.11133 C 358.55134,-4.5171383 359.31485,-4.2054565 360.10741,-4.1204506 C 360.90652,-4.0347429 361.73517,-4.1794691 362.54235,-4.4974081 C 363.28373,-4.7894253 364.007,-5.2275617 364.67263,-5.767479 C 365.31082,-6.2851315 365.89603,-6.896344 366.39343,-7.5620358 C 366.90188,-8.242517 367.31857,-8.9799246 367.60631,-9.7325204 C 367.07793,-10.437124 366.30547,-10.90016 365.41067,-11.161662 C 364.60115,-11.398241 363.6915,-11.469864 362.77187,-11.406172 C 361.88696,-11.344887 360.9928,-11.158305 360.16973,-10.872837 C 359.27131,-10.561235 358.45758,-10.131811 357.83303,-9.6189079 C 357.04999,-8.9758496 356.5643,-8.2015681 356.58187,-7.3637588 C 356.59693,-6.6454314 356.98194,-5.8804004 357.86667,-5.11133 C 357.86667,-5.11133 357.86667,-5.11133 357.86667,-5.11133 M 417.75225,-11.182788 C 417.07502,-10.573102 416.62984,-9.9619767 416.37681,-9.3762927 C 416.02487,-8.561655 416.0447,-7.7962389 416.3289,-7.1523776 C 416.62465,-6.4823787 417.20668,-5.9440073 417.95401,-5.6187651 C 418.60149,-5.336979 419.37304,-5.215173 420.18999,-5.3063541 C 420.8882,-5.3842807 421.61957,-5.6177791 422.33497,-6.039938 C 422.77975,-6.4929485 422.78734,-7.4965033 422.15722,-8.5165874 C 421.77046,-9.1427096 421.14344,-9.7750588 420.22979,-10.29015 C 419.56275,-10.666217 418.74292,-10.979783 417.75225,-11.182788 C 417.75225,-11.182788 417.75225,-11.182788 417.75225,-11.182788 M 368.72477,-29.815034 C 368.93929,-29.681301 369.81913,-29.431691 371.10001,-29.117177 C 371.79941,-28.945445 372.61837,-28.754362 373.51388,-28.552225 C 374.27437,-28.380564 375.09007,-28.200933 375.93461,-28.018412 C 376.72492,-27.847614 377.5405,-27.674287 378.35975,-27.502593 C 379.17232,-27.332299 379.98851,-27.163612 380.78723,-27.000601 C 381.62463,-26.829693 382.44284,-26.665024 383.21757,-26.511273 C 384.09643,-26.336856 384.91935,-26.176491 385.65087,-26.037014 C 386.67569,-25.841613 387.52114,-25.68721 388.08976,-25.592599 C 388.88299,-25.424723 388.81735,-24.843403 388.22382,-24.217644 C 387.8807,-23.855883 387.36113,-23.47927 386.72909,-23.1591 C 386.14872,-23.004314 385.23507,-23.043745 384.11626,-23.2229 C 383.34341,-23.346654 382.47268,-23.537084 381.54628,-23.776222 C 380.73687,-23.985165 379.88497,-24.231292 379.01873,-24.502629 C 378.19223,-24.761516 377.35269,-25.043357 376.52456,-25.337737 C 375.68354,-25.636705 374.8543,-25.948608 374.06246,-26.262551 C 373.19865,-26.605027 372.37936,-26.949929 371.63784,-27.283111 C 370.70868,-27.700602 369.90165,-28.09969 369.28218,-28.452537 C 367.87745,-29.252674 367.43725,-29.815034 368.72477,-29.815034 C 368.72477,-29.815034 368.72477,-29.815034 368.72477,-29.815034 M 392.02752,-84.74766 C 392.02752,-84.74766 392.7344,-84.027144 393.83561,-82.904681 C 394.36005,-82.370118 394.97392,-81.744392 395.64348,-81.061913 C 396.2128,-80.481607 396.82237,-79.860268 397.45145,-79.21905 C 398.04259,-78.616501 398.65095,-77.996398 399.25931,-77.376294 C 399.86769,-76.756182 400.47606,-76.136069 401.06721,-75.533508 C 401.69634,-74.892231 402.30597,-74.270835 402.87534,-73.690478 C 403.54497,-73.007923 404.15892,-72.382133 404.6834,-71.847527 C 405.78456,-70.725117 406.49139,-70.004642 406.49139,-70.004642 C 407.10333,-69.377078 407.43845,-68.486913 407.55379,-67.623781 C 407.69697,-66.552367 407.5015,-65.522606 407.07648,-65.088482 C 407.07648,-65.088482 406.31158,-65.702557 405.28105,-66.529897 C 404.73277,-66.970071 404.10929,-67.470613 403.48581,-67.97116 C 402.86227,-68.471752 402.23873,-68.972349 401.69039,-69.412571 C 400.65982,-70.23994 399.89489,-70.854045 399.89489,-70.854045 C 399.89489,-70.854045 399.74446,-69.944712 399.54179,-68.719577 C 399.43396,-68.067759 399.31134,-67.32655 399.18873,-66.585333 C 399.0661,-65.844049 398.94347,-65.102758 398.83563,-64.45087 C 398.63295,-63.225692 398.48252,-62.316314 398.48252,-62.316314 C 398.48252,-62.316314 397.584,-62.792357 396.48234,-63.37602 C 395.83873,-63.717007 395.12579,-64.094729 394.48218,-64.435717 C 393.38051,-65.019385 392.48198,-65.495431 392.48198,-65.495431 C 392.48198,-65.495431 391.68973,-64.993043 390.56019,-64.276775 C 389.98665,-63.913077 389.32614,-63.494237 388.63825,-63.058026 C 388.00531,-62.656665 387.34919,-62.2406 386.71626,-61.83925 C 386.02838,-61.403044 385.3679,-60.98422 384.79439,-60.620545 C 383.66505,-59.904405 382.87297,-59.402124 382.87297,-59.402124 C 382.87297,-59.402124 382.61068,-60.461171 382.23672,-61.971077 C 382.04684,-62.73776 381.82817,-63.620686 381.60043,-64.540232 C 381.39091,-65.386196 381.17372,-66.263155 380.96421,-67.109111 C 380.7365,-68.028522 380.51786,-68.911313 380.328,-69.677897 C 379.95404,-71.187859 379.69173,-72.246957 379.69173,-72.246957 C 379.69173,-72.246957 379.1419,-71.292245 378.40112,-70.00597 C 378.007,-69.321624 377.55882,-68.543425 377.11064,-67.76522 C 376.66242,-66.986942 376.2142,-66.208656 375.82004,-65.524239 C 375.07923,-64.237918 374.52937,-63.283159 374.52937,-63.283159 C 374.52937,-63.283159 373.34764,-63.283159 371.89875,-63.283159 C 371.05228,-63.283159 370.11462,-63.283159 369.26815,-63.283159 C 367.81925,-63.283159 366.63751,-63.283159 366.63751,-63.283159 C 365.73009,-63.283159 365.04856,-63.609193 364.60329,-64.143167 C 364.1505,-64.68616 363.94204,-65.444172 363.98881,-66.293026 C 364.02685,-66.983511 364.23379,-67.734106 364.61549,-68.477967 C 364.93883,-69.10811 365.38759,-69.733421 365.96534,-70.313268 C 366.533,-70.928228 367.10997,-71.544506 367.69155,-72.158695 C 368.27081,-72.770441 368.85465,-73.380116 369.4384,-73.984353 C 370.02775,-74.594398 370.61702,-75.198901 371.2014,-75.7944 C 371.8,-76.404377 372.39347,-77.004907 372.97666,-77.592267 C 373.58434,-78.204291 374.18087,-78.802016 374.76041,-79.381232 C 375.37838,-79.998849 375.97704,-80.59542 376.54931,-81.165842 C 377.18047,-81.794948 377.77954,-82.392246 378.33704,-82.95089 C 378.99162,-83.606793 379.58889,-84.20941 380.11354,-84.74766 C 380.74309,-85.39346 381.46457,-85.908627 382.24134,-86.295283 C 383.03374,-86.689717 383.88367,-86.950416 384.75223,-87.079631 C 385.6235,-87.209248 386.51352,-87.206561 387.38301,-87.073843 C 388.25343,-86.940983 389.10329,-86.677815 389.8932,-86.286619 C 390.67638,-85.898751 391.40062,-85.385024 392.02752,-84.74766 C 392.02752,-84.74766 392.02752,-84.74766 392.02752,-84.74766" />
+    <marker
+       inkscape:stockid="Arrow1Mend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Mend"
+       style="overflow:visible;">
+      <path
+         id="path5779"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+         transform="scale(0.4) rotate(180) translate(10,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Mstart"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Mstart"
+       style="overflow:visible">
+      <path
+         id="path5782"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+         transform="scale(0.4) translate(10,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lend"
+       style="overflow:visible;">
+      <path
+         id="path17355"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+         transform="scale(0.8) rotate(180) translate(12.5,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.84461975"
+     inkscape:cx="472.3569"
+     inkscape:cy="830.30012"
+     inkscape:document-units="mm"
+     inkscape:current-layer="svg2"
+     inkscape:window-width="1152"
+     inkscape:window-height="808"
+     inkscape:window-x="-4"
+     inkscape:window-y="-4"
+     inkscape:grid-points="false"
+     inkscape:grid-bbox="false"
+     gridtolerance="50"
+     width="210mm"
+     height="297mm"
+     units="mm"
+     showgrid="true"
+     gridspacingx="1mm"
+     gridspacingy="1mm"
+     grid_units="mm"
+     inkscape:object-paths="false"
+     inkscape:object-nodes="false"
+     inkscape:object-points="false"
+     objecttolerance="12.4"
+     inkscape:guide-bbox="false" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="g3195"
+     transform="translate(516.20863,200.09004)">
+    <path
+       sodipodi:type="arc"
+       style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#0000ff;stroke-width:0.53125;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3197"
+       sodipodi:cx="-44.990662"
+       sodipodi:cy="199.31558"
+       sodipodi:rx="37.886871"
+       sodipodi:ry="34.92696"
+       d="M -7.1037903,199.31558 A 37.886871,34.92696 0 1 1 -82.877533,199.31558 A 37.886871,34.92696 0 1 1 -7.1037903,199.31558 z"
+       transform="translate(58.014272,-17.759472)" />
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-weight:normal;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="-82.877533"
+       y="346.71921"
+       id="text3199"
+       transform="translate(84.061496,-150.36352)"><tspan
+         sodipodi:role="line"
+         id="tspan3201"
+         x="-82.877533"
+         y="346.71921"
+         style="font-size:40px;font-weight:bold;writing-mode:lr-tb;fill:#0000ff">3</tspan></text>
+    <path
+       transform="translate(58.014272,-17.759472)"
+       d="M -7.1037903,199.31558 A 37.886871,34.92696 0 1 1 -82.877533,199.31558 A 37.886871,34.92696 0 1 1 -7.1037903,199.31558 z"
+       sodipodi:ry="34.92696"
+       sodipodi:rx="37.886871"
+       sodipodi:cy="199.31558"
+       sodipodi:cx="-44.990662"
+       id="path3203"
+       style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#0000ff;stroke-width:0.53125;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       sodipodi:type="arc" />
+  </g>
+  <g
+     id="g3205"
+     transform="translate(644.07682,-17.759471)">
+    <path
+       sodipodi:type="arc"
+       style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#0000ff;stroke-width:0.53125;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3207"
+       sodipodi:cx="-44.990662"
+       sodipodi:cy="199.31558"
+       sodipodi:rx="37.886871"
+       sodipodi:ry="34.92696"
+       d="M -7.1037903,199.31558 A 37.886871,34.92696 0 1 1 -82.877533,199.31558 A 37.886871,34.92696 0 1 1 -7.1037903,199.31558 z"
+       transform="translate(58.014272,-17.759472)" />
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-weight:normal;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="-82.877533"
+       y="346.71921"
+       id="text3209"
+       transform="translate(84.061496,-150.36352)"><tspan
+         sodipodi:role="line"
+         id="tspan3211"
+         x="-82.877533"
+         y="346.71921"
+         style="font-size:40px;font-weight:bold;writing-mode:lr-tb;fill:#0000ff">4</tspan></text>
+  </g>
+  <g
+     inkscape:label="Calque 1"
+     id="layer1"
+     inkscape:groupmode="layer" />
+  <text
+     xml:space="preserve"
+     style="font-size:32px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+     id="text10546"
+     y="265.19595"
+     x="194.35773"
+     transform="translate(-31.967048,-168.123)"><tspan
+       sodipodi:role="line"
+       id="tspan10548"
+       y="265.19595"
+       x="194.35773">+</tspan></text>
+  <path
+     style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000048px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+     d="M 247.1188,269.88102 C 297.86188,208.2082 345.42582,221.1401 391.46382,269.88102"
+     sodipodi:nodetypes="cc"
+     id="path8133"
+     transform="translate(-31.967048,-168.123)" />
+  <g
+     style="stroke:#000000;stroke-width:0.53125;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     id="g10352"
+     transform="translate(-52.598418,165.2633)">
+    <path
+       style="fill:#b3b3b3;fill-rule:evenodd;stroke:#000000;stroke-width:0.53125;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 182.41614,-84.323393 C 182.41614,-82.243924 183.07763,-81.472553 183.81623,-80.138746 C 183.25799,-77.779963 182.90113,-75.402938 184.8663,-72.815601 C 184.16185,-70.897634 185.06005,-68.979672 186.44139,-67.061705 C 186.20992,-65.221545 187.41442,-63.619821 188.54153,-62.00525 C 188.74403,-59.912924 189.85628,-57.820593 191.69173,-55.728267 C 175.06568,-60.959082 173.96037,-73.73539 171.39045,-76.128454 C 170.02565,-77.399318 168.54863,-77.249016 167.49502,-78.839496 C 166.44141,-80.429981 165.81123,-83.761248 164.74003,-84.323571 C 165.81123,-84.885893 166.44141,-88.217161 167.49502,-89.807646 C 168.54863,-91.398126 170.02565,-91.247824 171.39045,-92.518688 C 173.96037,-94.911751 175.06568,-107.68806 191.69173,-112.91887 C 189.85628,-110.82655 188.74403,-108.73422 188.54153,-106.64189 C 187.41442,-105.02732 186.20992,-103.4256 186.44139,-101.58544 C 185.06005,-99.667468 184.16185,-97.749506 184.8663,-95.831539 C 182.90113,-93.244204 183.25799,-90.867178 183.81623,-88.508395 C 183.07763,-87.174588 182.41614,-86.403217 182.41614,-84.323748"
+       sodipodi:nodetypes="ccccccsscsscccccc"
+       id="path8125" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.53125;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 164.74002,-93.303315 C 164.23003,-92.253241 163.89837,-90.946889 163.82954,-89.606705 M 160.42261,-96.277612 C 159.68762,-94.651147 159.1745,-93.053566 159.00821,-91.515233 M 103.28697,-115.85547 C 98.884138,-110.47766 96.980385,-104.60739 95.648987,-99.34437 M 154.80568,-92.93596 C 155.09832,-95.029159 155.5075,-97.215387 156.67895,-99.218055 M 131.95744,-98.703792 C 133.01696,-103.65373 134.72384,-109.0778 138.52675,-113.60533 M 138.73818,-97.453986 C 139.7233,-101.56203 141.03701,-106.1848 143.98698,-110.49279 M 144.17966,-96.064288 C 144.95029,-99.480863 146.28977,-103.19229 149.08613,-106.62877 M 150.11023,-94.490163 C 150.76081,-97.682213 151.57061,-100.94184 153.02035,-103.33814 M 101.86384,-100.29455 C 103.15394,-105.99572 104.93982,-112.0646 109.01773,-116.55141 M 107.21074,-100.69028 C 108.51092,-105.84205 110.61175,-111.57106 115.22006,-116.98875 M 113.67721,-100.73662 C 114.95176,-105.88159 116.89243,-111.60246 121.07149,-117.01484 M 120.24621,-100.35798 C 121.52611,-105.54964 123.38523,-111.35552 127.39633,-116.79799 M 133.23388,-115.72447 C 131.27258,-113.75024 128.14487,-107.03014 126.56596,-99.660228"
+       id="path8246" />
+    <path
+       style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:0.53125;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 95.434115,-70.206827 C 93.517704,-78.165688 93.091226,-84.356263 89.354302,-84.278446 C 93.227833,-84.197794 93.284303,-90.874822 95.648987,-99.34437 C 94.549549,-94.184237 94.054266,-88.663431 95.598288,-84.278446 C 94.138887,-80.243993 94.259252,-75.187153 95.434115,-70.206827 z M 163.78565,-79.901248 C 163.66556,-82.504242 163.81717,-84.363823 160.95576,-84.278446 C 164.16607,-84.182668 163.44628,-86.528264 163.82954,-89.606705 C 163.80707,-87.470695 164.13697,-85.344582 164.56502,-84.278446 L 164.56502,-84.278436 C 164.19928,-83.367485 163.79594,-81.688072 163.78565,-79.901248 z M 158.91212,-77.957892 C 158.45187,-82.228449 159.61103,-84.374027 155.0618,-84.278446 C 159.9231,-84.176316 158.13043,-86.697682 159.00821,-91.515233 C 158.74234,-88.146293 159.56584,-85.325684 160.95576,-84.278446 C 159.69683,-83.329905 158.7839,-80.870141 158.91212,-77.957892 z M 154.69064,-76.533945 C 154.17292,-80.791225 154.05256,-84.362925 149.90925,-84.278446 C 154.34678,-84.187975 154.08765,-88.258789 154.80568,-92.93596 C 154.4275,-89.685982 154.45257,-86.166715 155.0618,-84.278446 C 154.50867,-82.564043 154.36551,-79.533568 154.69064,-76.533945 z M 131.80712,-70.764756 C 130.28871,-78.243401 130.13382,-84.356174 126.40365,-84.278446 C 130.26538,-84.197981 130.24973,-90.844345 131.95744,-98.703792 C 130.95022,-93.594145 130.8742,-88.250819 132.64283,-84.278446 C 130.95432,-80.606707 130.90472,-75.647227 131.80712,-70.764756 z M 138.56968,-71.977762 C 136.95129,-79.032694 136.52753,-84.360065 132.64283,-84.278446 C 136.66257,-84.193996 136.77607,-90.070651 138.73818,-97.453986 C 137.77098,-92.184573 137.41872,-86.867701 139.00603,-84.278446 C 137.49649,-81.945278 137.55386,-76.978021 138.56968,-71.977762 z M 144.00169,-73.370196 C 142.72296,-79.522753 143.05446,-84.361175 139.00603,-84.278446 C 143.21448,-84.192453 142.74814,-89.53675 144.17966,-96.064288 C 142.97128,-91.634688 142.64705,-86.964322 144.31918,-84.278446 C 142.74472,-81.882157 142.99409,-77.610028 144.00169,-73.370196 z M 149.72248,-74.892609 C 148.76808,-79.941667 148.08728,-84.357614 144.31918,-84.278446 C 148.21211,-84.196659 149.13642,-89.151779 150.11023,-94.490163 C 149.13449,-90.439801 148.85989,-86.259716 149.90925,-84.278446 C 148.92625,-82.566687 148.97888,-78.774472 149.72248,-74.892609 z M 101.67933,-69.239896 C 99.91726,-77.340523 99.205333,-84.354231 95.598288,-84.278446 C 99.33247,-84.199998 99.771308,-91.765707 101.86384,-100.29455 C 100.94909,-95.376091 100.53847,-89.798542 101.60769,-84.278446 C 100.59895,-79.174755 100.72772,-73.994332 101.67933,-69.239896 z M 107.00048,-68.819152 C 104.92094,-77.613899 105.38003,-84.36003 101.60769,-84.278446 C 105.49639,-84.194351 104.71672,-91.457947 107.21074,-100.69028 C 105.82437,-94.999618 105.3417,-88.895565 107.61708,-84.278446 C 105.45271,-80.003367 105.6221,-74.308794 107.00048,-68.819152 z M 113.48163,-68.75544 C 111.40335,-77.596407 111.51221,-84.360282 107.61708,-84.278446 C 111.62608,-84.194223 111.22054,-91.479952 113.67721,-100.73662 C 112.41627,-95.020953 111.94093,-88.88832 113.89077,-84.278446 C 112.03616,-80.010296 112.21228,-74.266663 113.48163,-68.75544 z M 120.05619,-69.120836 C 118.02225,-77.751138 117.79104,-84.359276 113.89077,-84.278446 C 117.90931,-84.19517 117.84181,-91.316877 120.24621,-100.35798 C 119.06689,-94.774587 118.55768,-88.828598 120.16447,-84.278446 C 118.63468,-80.061561 118.84293,-74.494624 120.05619,-69.120836 z M 126.38813,-69.820964 C 124.48676,-77.989381 123.94387,-84.357851 120.16447,-84.278446 C 124.06695,-84.196462 124.36615,-91.098069 126.56596,-99.660228 C 125.56071,-94.358197 125.1589,-88.723356 126.40365,-84.278446 C 125.22123,-80.168381 125.38152,-74.896703 126.38813,-69.820964 z"
+       sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
+       id="path8248" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.53125;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 163.78565,-79.901248 C 163.77625,-78.26803 164.11321,-76.544183 164.74002,-75.253577 M 158.91212,-77.957892 C 158.99104,-76.165471 159.53809,-74.23663 160.42261,-72.279278 M 156.67895,-69.338835 C 155.33486,-71.636639 154.97686,-74.180211 154.69064,-76.533945 M 153.02035,-65.218749 C 151.02309,-67.982062 150.34957,-71.575067 149.72248,-74.892609 M 149.08613,-61.928118 C 146.0874,-65.717641 144.75066,-69.766598 144.00169,-73.370196 M 138.56968,-71.977762 C 139.77476,-66.045887 142.25241,-60.047612 143.98698,-58.064104 M 138.52675,-54.95156 C 134.51828,-59.838949 132.85141,-65.621355 131.80712,-70.764756 M 126.38813,-69.820964 C 127.91475,-62.123141 131.20195,-54.877738 133.23388,-52.832416 M 127.39633,-51.758902 C 123.17999,-57.588297 121.32332,-63.744202 120.05619,-69.120836 M 121.07149,-51.542047 C 116.67952,-57.336447 114.73488,-63.424118 113.48163,-68.75544 M 115.22006,-51.568146 C 110.36545,-57.382218 108.26477,-63.472221 107.00048,-68.819152 M 109.01773,-52.005485 C 104.73325,-56.838892 102.96749,-63.317905 101.67933,-69.239896 M 95.434115,-70.206827 C 96.748742,-64.747184 98.636303,-58.496896 103.28697,-52.701417"
+       sodipodi:nodetypes="cccccccccccccccccccccccccc"
+       id="path8242" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.53125;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 182.76616,-103.15468 C 185.21632,-105.77009 188.54153,-106.64189 188.54153,-106.64189 M 179.09093,-97.749501 C 182.06612,-100.36491 186.44139,-101.58544 186.44139,-101.58544 M 176.29075,-90.949443 C 180.31601,-95.482818 184.8663,-95.831539 184.8663,-95.831539 M 174.19062,-86.416069 C 179.26595,-88.508395 183.81623,-88.508395 183.81623,-88.508395"
+       sodipodi:nodetypes="cccccccc"
+       id="path8129" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.53125;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path8131"
+       sodipodi:nodetypes="cccccccc"
+       d="M 182.76616,-65.492461 C 185.21632,-62.877054 188.54153,-62.00525 188.54153,-62.00525 M 179.09093,-70.897639 C 182.06612,-68.282232 186.44139,-67.061705 186.44139,-67.061705 M 176.29075,-77.697698 C 180.31601,-73.164324 184.8663,-72.815601 184.8663,-72.815601 M 174.19062,-82.231072 C 179.26595,-80.138746 183.81623,-80.138746 183.81623,-80.138746" />
+    <path
+       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.53125;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 81.784785,-94.523832 C 81.784785,-94.090718 81.471164,-93.739209 81.084742,-93.739209 C 80.698316,-93.739209 80.3847,-94.090718 80.3847,-94.523832 C 80.3847,-94.956945 80.698316,-95.308454 81.084742,-95.308454 C 81.471164,-95.308454 81.784785,-94.956945 81.784785,-94.523832 z"
+       id="path8135" />
+    <path
+       style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:0.53125;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 88.347703,-56.338886 C 93.073,-56.426068 88.347703,-77.698048 88.347703,-84.498107 C 88.347703,-91.298166 90.286027,-106.08432 95.523156,-112.83169 C 100.84897,-119.69332 61.207272,-96.898202 64.108675,-79.092934 C 67.346381,-77.262144 72.246688,-76.303163 76.184435,-74.821095 C 72.509204,-74.55956 68.221432,-75.692904 65.158736,-76.477526 C 66.90885,-72.815951 73.034237,-62.703045 88.347703,-56.338886 z M 83.709918,-95.700756 C 83.709918,-94.377361 82.788657,-93.3033 81.653539,-93.3033 C 80.518416,-93.3033 79.597155,-94.377361 79.597155,-95.700756 C 79.597155,-97.024152 80.518416,-98.098213 81.653539,-98.098213 C 82.788657,-98.098213 83.709918,-97.024152 83.709918,-95.700756 z"
+       id="path8123" />
+    <path
+       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.53125;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       id="path9876"
+       d="M 81.784785,-94.523832 C 81.784785,-94.090718 81.471164,-93.739209 81.084742,-93.739209 C 80.698316,-93.739209 80.3847,-94.090718 80.3847,-94.523832 C 80.3847,-94.956945 80.698316,-95.308454 81.084742,-95.308454 C 81.471164,-95.308454 81.784785,-94.956945 81.784785,-94.523832 z" />
+  </g>
+  <path
+     style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+     id="path10580"
+     sodipodi:nodetypes="cc"
+     d="M 413.47727,254.19401 L 510.07072,254.19401"
+     transform="translate(-31.967048,-168.123)" />
+  <path
+     style="fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.53125000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 324.102,482.915 C 324.297,484.985 325.024,485.691 325.858,486.958 C 325.516,489.355 325.426,491.74 327.464,494.173 C 326.96,496.137 327.96,497.977 329.337,499.803 C 329.258,501.653 330.445,503.175 331.521,504.73 C 331.819,506.808 332.899,508.838 334.516,510.869 C 330.991,509.699 328.163,508.146 325.837,506.391 C 323.511,504.637 321.692,502.684 320.229,500.784 C 317.303,496.984 315.838,493.384 314.439,492.397 C 312.953,491.349 311.581,491.741 310.288,490.362 C 308.996,488.984 307.747,485.833 306.584,485.494 C 307.525,484.731 307.517,481.341 308.301,479.58 C 309.085,477.819 310.629,477.701 311.844,476.219 C 312.988,474.824 313.34,470.919 315.733,466.423 C 316.929,464.175 318.645,461.781 321.271,459.544 C 323.897,457.307 327.439,455.221 332.241,453.719 C 330.181,455.896 329.035,458.065 328.928,460.167 C 327.747,461.854 326.525,463.547 326.909,465.365 C 325.524,467.386 324.73,469.379 325.634,471.231 C 323.744,473.978 324.368,476.319 325.142,478.62 C 324.5,480.014 323.907,480.845 324.102,482.915 M 304.811,476.694 C 304.491,477.83 304.417,479.179 304.619,480.505 M 299.69,474.754 C 299.283,476.515 299.125,478.195 299.311,479.731 M 232.957,481.865 C 232.206,485.521 232.249,488.871 232.694,491.944 C 233.138,495.017 233.985,497.816 234.895,500.39 M 294.684,479.428 C 294.447,477.326 294.308,475.099 295.059,472.845 M 269.659,481.923 C 268.797,476.92 268.451,471.157 271.059,465.337 M 276.953,480.357 C 276.511,476.14 276.257,471.287 278.131,466.1 M 282.945,479.694 C 282.659,476.405 282.826,472.678 284.555,468.733 C 284.555,468.733 284.555,468.733 284.555,468.733 M 284.555,468.733 C 284.666,468.479 284.784,468.224 284.909,467.969 M 289.472,479.274 C 289.231,476.017 289.168,472.637 290.093,469.896 M 239.986,495.84 C 238.076,490.287 236.49,484.057 238.373,477.748 M 244.734,492.414 C 244.011,489.842 243.425,487.023 243.284,483.921 C 243.144,480.82 243.449,477.433 244.566,473.75 M 250.84,488.83 C 250.203,486.244 249.678,483.434 249.557,480.384 C 249.435,477.334 249.715,474.042 250.733,470.513 M 257.374,485.798 C 256.827,483.169 256.377,480.321 256.316,477.262 C 256.255,474.202 256.582,470.928 257.637,467.464 M 264.447,465.677 C 263.745,467.035 263.201,469.623 263.049,472.794 C 262.897,475.966 263.141,479.718 263.898,483.421 M 251.375,524.436 C 248.371,521.678 245.862,519.118 243.738,517.523 C 241.613,515.927 239.859,515.303 238.382,516.456 C 239.961,515.325 239.786,513.423 238.864,510.738 C 237.943,508.053 236.26,504.6 234.895,500.39 C 236.862,505.309 239.682,510.077 243.459,512.793 C 244.563,516.942 247.634,520.971 251.375,524.436 L 251.375,524.436 M 306.545,490.014 C 305.902,487.49 305.663,485.616 302.885,486.329 C 304.464,486.022 304.958,485.35 305.028,484.356 C 305.097,483.361 304.737,482.052 304.619,480.505 C 305.029,482.601 305.789,484.613 306.421,485.573 L 306.421,485.573 C 306.243,486.538 306.192,488.263 306.545,490.014 L 306.545,490.014 M 302.355,492.937 C 301.645,490.912 301.437,489.313 300.865,488.363 C 300.293,487.412 299.34,487.103 297.149,487.729 C 299.517,487.159 300.122,486.406 300.102,485.157 C 300.082,483.908 299.421,482.18 299.311,479.731 C 299.819,483.072 301.297,485.614 302.885,486.329 C 301.863,487.531 301.56,490.134 302.355,492.937 L 302.355,492.937 M 298.777,495.307 C 297.992,493.314 297.337,491.463 296.381,490.257 C 295.425,489.052 294.155,488.49 292.177,489.122 C 294.321,488.532 295.063,487.307 295.215,485.617 C 295.367,483.927 294.917,481.785 294.684,479.428 C 295.13,482.671 296.078,486.059 297.149,487.729 C 297.049,489.528 297.707,492.489 298.777,495.307 L 298.777,495.307 M 280.388,507.681 C 278.288,504.514 276.575,501.557 274.972,499.584 C 273.369,497.61 271.858,496.624 270.18,497.442 C 271.951,496.668 272.162,494.79 271.762,492.095 C 271.361,489.4 270.331,485.9 269.659,481.923 C 270.635,487.05 272.735,491.95 275.897,494.929 C 275.75,498.964 277.722,503.514 280.388,507.681 L 280.388,507.681 M 285.734,504.244 C 283.793,501.208 282.224,498.498 280.698,496.731 C 279.173,494.964 277.675,494.142 275.897,494.929 C 277.769,494.193 278.19,492.478 278.065,489.973 C 277.94,487.468 277.253,484.184 276.953,480.357 C 277.819,485.654 279.428,490.72 281.821,492.589 C 281.219,495.3 283.096,499.898 285.734,504.244 L 285.734,504.244 M 290.025,501.241 C 288.475,498.519 287.363,495.998 286.176,494.328 C 284.988,492.659 283.706,491.842 281.821,492.589 C 283.81,491.893 284.254,490.355 284.12,488.136 C 283.986,485.917 283.257,483.031 282.945,479.694 C 283.127,484.305 284.394,488.801 286.836,490.812 C 286.103,493.584 287.777,497.527 290.025,501.241 L 290.025,501.241 M 294.682,498.169 C 293.821,496.459 293.009,494.828 292.143,493.516 M 292.143,493.516 C 290.689,491.314 289.063,490.012 286.836,490.812 C 288.918,490.161 289.647,488.339 289.809,485.893 M 289.809,485.893 C 289.936,483.966 289.701,481.659 289.472,479.274 C 289.625,483.452 290.604,487.523 292.177,489.122 C 291.72,491.044 292.899,494.65 294.682,498.169 L 294.682,498.169 M 256.691,522.023 C 253.811,519.063 251.239,516.231 249.02,514.396 C 246.801,512.561 244.921,511.73 243.459,512.793 C 245.017,511.757 244.831,509.631 243.915,506.699 C 242.999,503.767 241.34,500.041 239.986,495.84 C 241.815,500.513 244.558,505.384 248.459,509.435 C 250.385,514.269 253.374,518.507 256.691,522.023 L 256.691,522.023 M 261.046,519.786 C 257.955,516.519 255.578,513.423 253.601,511.384 C 251.624,509.345 250.023,508.374 248.459,509.435 C 250.117,508.411 249.82,506.489 248.784,503.649 C 247.748,500.809 245.952,497.068 244.734,492.414 C 246.377,498.066 249.235,503.465 253.567,506.247 C 253.91,511.027 257.124,515.746 261.046,519.786 L 261.046,519.786 M 266.196,516.86 C 263.219,513.462 260.879,510.308 258.884,508.243 C 256.889,506.177 255.218,505.207 253.567,506.247 C 255.31,505.249 255.182,503.252 254.337,500.317 C 253.492,497.383 251.907,493.526 250.84,488.83 C 252.403,494.494 255.07,500.022 259.009,503.107 C 259.45,507.738 262.505,512.612 266.196,516.86 L 266.196,516.86 M 271.32,513.714 C 268.54,510.292 266.302,507.165 264.347,505.122 C 262.392,503.079 260.699,502.126 259.009,503.107 C 260.79,502.169 260.83,500.164 260.197,497.224 C 259.565,494.283 258.24,490.42 257.374,485.798 C 258.742,491.356 261.069,496.838 264.559,500.164 C 265.099,504.614 267.902,509.43 271.32,513.714 L 271.32,513.714 M 276.222,510.556 C 273.721,507.218 271.64,504.187 269.771,502.2 C 267.902,500.212 266.229,499.273 264.559,500.164 C 266.32,499.317 266.512,497.337 266.086,494.457 C 265.659,491.576 264.597,487.807 263.898,483.421 C 265.117,488.69 267.187,493.934 270.18,497.442 C 270.827,501.665 273.26,506.341 276.222,510.556 L 276.222,510.556 M 306.545,490.014 C 306.867,491.614 307.537,493.234 308.374,494.382 M 302.355,492.937 C 302.844,494.663 303.8,496.416 305.038,498.14 M 302.376,501.804 C 300.627,499.878 299.645,497.511 298.777,495.307 M 300.254,506.628 C 297.813,504.438 296.199,501.177 294.682,498.169 M 297.865,510.749 C 297.662,510.587 297.464,510.423 297.268,510.257 M 297.268,510.257 C 297.268,510.257 297.268,510.257 297.268,510.257 C 294.014,507.478 291.738,504.25 290.025,501.241 M 285.734,504.244 C 287.299,506.821 289.108,509.329 290.759,511.381 C 292.41,513.433 293.904,515.028 294.911,515.736 M 291.684,520.177 C 286.88,516.76 283.275,512.037 280.388,507.681 M 276.222,510.556 C 278.469,513.752 280.977,516.71 283.18,519.047 C 285.383,521.383 287.287,523.097 288.439,523.726 M 284.53,526.592 C 281.774,524.648 279.331,522.505 277.15,520.305 C 274.969,518.104 273.052,515.846 271.32,513.714 M 280.007,528.999 C 277.122,527.21 274.56,525.211 272.277,523.135 C 269.994,521.06 267.991,518.909 266.196,516.86 M 275.788,531.183 C 272.667,529.578 269.915,527.735 267.479,525.783 C 265.044,523.831 262.925,521.772 261.046,519.786 M 271.172,533.311 C 265.698,530.943 260.902,526.351 256.691,522.023 M 251.375,524.436 C 253.436,526.328 255.698,528.323 258.245,530.185 C 260.792,532.047 263.622,533.776 266.794,535.19 M 322.724,464.133 C 325.199,461.277 328.928,460.167 328.928,460.167 M 319.267,469.923 C 320.717,468.441 322.624,467.287 324.174,466.519 C 325.723,465.752 326.909,465.365 326.909,465.365 M 317.143,477.032 C 318.927,474.513 321.067,473.048 322.744,472.23 C 324.422,471.411 325.634,471.231 325.634,471.231 M 315.619,481.816 C 318.019,480.415 320.413,479.608 322.194,479.162 C 323.974,478.715 325.142,478.62 325.142,478.62 M 326.179,501.632 C 328.596,504.037 331.521,504.73 331.521,504.73 M 322.307,496.597 C 323.82,497.741 325.579,498.531 326.957,499.047 C 328.335,499.564 329.337,499.803 329.337,499.803 M 318.86,490.169 C 321.076,492.165 323.209,493.159 324.805,493.662 C 326.402,494.166 327.464,494.173 327.464,494.173 M 316.213,485.957 C 318.848,486.636 321.246,486.883 322.999,486.966 C 324.752,487.048 325.858,486.958 325.858,486.958 M 225.927,513.168 C 226.201,513.504 226.166,513.985 225.852,514.243 C 225.537,514.501 225.058,514.44 224.782,514.106 C 224.506,513.772 224.539,513.288 224.856,513.028 C 225.174,512.768 225.654,512.831 225.927,513.168 L 225.927,513.168 M 254.438,539.366 C 255.227,538.741 254.828,537.362 253.726,535.547 C 252.624,533.733 250.813,531.488 248.755,529.165 C 244.64,524.519 239.494,519.601 237.442,516.887 C 235.39,514.173 232.511,509.575 230.285,504.511 C 228.059,499.447 226.507,493.896 227.071,489.395 C 227.214,488.251 226.957,487.949 226.414,488.328 C 225.87,488.707 225.042,489.768 224.093,491.346 C 222.194,494.501 219.841,499.739 218.264,505.615 C 217.476,508.553 216.876,511.647 216.562,514.708 C 216.249,517.768 216.222,520.794 216.54,523.604 C 217.177,529.225 219.199,533.95 222.822,536.632 C 226.358,535.729 230.47,533.222 234.279,531.894 C 231.8,534.38 228.037,536.308 225.377,537.794 C 227.256,538.522 231.004,539.673 236.027,540.23 C 241.051,540.787 247.319,540.763 254.438,539.366 L 254.438,539.366 M 226.78,510.97 C 227.605,512.006 227.516,513.449 226.591,514.202 C 225.666,514.956 224.235,514.75 223.387,513.732 C 222.54,512.714 222.613,511.253 223.561,510.481 C 224.509,509.709 225.955,509.933 226.78,510.97 L 226.78,510.97 M 225.927,513.168 C 226.201,513.504 226.166,513.985 225.852,514.243 C 225.537,514.501 225.058,514.44 224.782,514.106 C 224.506,513.772 224.539,513.288 224.856,513.028 C 225.174,512.768 225.654,512.831 225.927,513.168 L 225.927,513.168 M 444.363,517.228 C 443.063,518.85 443.093,519.862 442.802,521.348 C 441.845,522.088 440.93,522.87 440.228,523.861 C 439.526,524.853 439.039,526.053 438.901,527.621 C 437.179,528.682 436.598,530.686 436.263,532.922 C 434.915,534.19 434.661,536.078 434.288,537.903 C 433.028,539.582 432.325,541.746 431.93,544.242 C 430.488,541.101 429.664,538.118 429.287,535.304 C 428.909,532.489 428.978,529.85 429.251,527.484 C 429.524,525.117 430,523.026 430.377,521.318 C 430.755,519.611 431.031,518.288 430.863,517.451 C 430.508,515.674 429.244,515.026 429.244,513.137 C 429.244,511.248 430.481,508.09 429.86,507.049 C 431.07,507.132 433.377,504.646 435.158,503.896 C 436.938,503.147 438.154,504.122 440.065,503.882 C 440.964,503.769 442.152,503.071 443.721,502.196 C 445.291,501.321 447.243,500.266 449.684,499.481 C 452.125,498.697 455.06,498.18 458.579,498.464 C 462.099,498.748 466.214,499.831 470.88,502.37 C 467.788,502.358 465.403,503.055 463.839,504.464 C 461.771,504.792 459.678,505.112 458.684,506.682 C 456.242,507.118 454.268,507.979 453.632,509.945 C 451.979,510.251 450.863,510.947 450.026,511.829 C 449.19,512.71 448.631,513.781 448.107,514.877 C 446.67,515.423 445.664,515.606 444.364,517.228 M 434.563,499.397 C 433.551,500.008 432.576,500.946 431.82,502.055 M 432.115,494.481 C 430.616,495.494 429.356,496.618 428.448,497.871 M 378.46,454.199 C 375.408,456.363 373.149,458.845 371.373,461.396 C 369.597,463.948 368.304,466.572 367.212,469.074 M 425.255,494.503 C 426.51,492.8 427.921,491.072 430.007,489.929 M 405.253,479.293 C 406.644,477.167 408.26,474.998 410.258,473.042 C 412.257,471.085 414.64,469.34 417.569,468.097 M 411.648,483.123 C 414.197,479.731 417.314,476.003 422.2,473.478 M 416.478,486.72 C 418.635,483.946 421.504,481.156 425.869,479.461 M 421.535,490.85 C 423.571,488.297 425.821,485.775 428.363,484.393 M 374.048,469.216 C 375.248,466.534 376.624,463.792 378.41,461.295 C 380.196,458.798 382.394,456.543 385.24,454.876 M 379.865,469.948 C 381.095,467.574 382.593,465.111 384.611,462.745 C 386.63,460.379 389.169,458.108 392.502,456.174 M 386.78,471.495 C 388.082,469.168 389.62,466.753 391.616,464.438 C 393.611,462.122 396.066,459.903 399.219,458.014 M 393.629,473.739 C 395.026,471.441 396.643,469.049 398.687,466.767 C 400.732,464.485 403.206,462.312 406.338,460.494 M 412.521,463.835 C 411.084,464.35 408.924,465.875 406.651,468.094 C 404.378,470.313 401.996,473.228 400.021,476.457 M 362.852,497.886 C 362.796,497.156 362.747,496.443 362.701,495.747 M 362.701,495.747 C 362.473,492.22 362.345,489.164 361.854,486.946 C 361.364,484.728 360.499,483.345 358.8,483.198 C 360.516,483.419 361.629,482.244 362.671,480.151 C 363.714,478.058 364.673,475.048 366.087,471.638 M 366.087,471.638 C 366.432,470.804 366.804,469.947 367.212,469.074 C 366.724,470.33 366.281,471.617 365.908,472.912 M 365.908,472.912 C 365.908,472.912 365.908,472.912 365.908,472.912 C 365.079,475.786 364.601,478.702 364.713,481.423 C 364.713,481.423 364.713,481.423 364.713,481.423 M 364.713,481.423 C 364.748,482.298 364.845,483.152 365.009,483.979 C 364.637,484.674 364.317,485.412 364.044,486.184 M 364.044,486.184 C 364.044,486.184 364.044,486.184 364.044,486.184 C 363.07,488.942 362.7,492.14 362.745,495.44 C 362.745,495.44 362.745,495.44 362.745,495.44 M 362.745,495.44 C 362.756,496.25 362.792,497.067 362.852,497.886 L 362.852,497.886 M 426.75,510.332 C 427.774,508.458 428.699,507.099 427.612,505.905 C 427.612,505.905 427.612,505.905 427.612,505.905 M 427.612,505.905 C 427.372,505.642 427.035,505.387 426.58,505.141 C 427.024,505.417 427.409,505.587 427.752,505.667 M 427.752,505.667 C 427.752,505.667 427.752,505.667 427.752,505.667 C 429.525,506.083 430.198,504.088 431.82,502.055 C 430.693,503.869 429.878,505.862 429.687,506.995 L 429.687,506.995 C 428.898,507.58 427.685,508.809 426.75,510.332 L 426.75,510.332 M 421.696,509.625 C 422.553,507.657 423.489,506.343 423.716,505.257 C 423.942,504.171 423.454,503.296 421.421,502.266 C 423.546,503.457 424.502,503.318 425.336,502.388 C 426.171,501.458 426.862,499.742 428.448,497.871 C 426.548,500.666 425.902,503.535 426.58,505.141 C 425.013,505.326 423.02,507.029 421.696,509.625 L 421.696,509.625 M 417.465,508.931 C 418.244,506.935 419.022,505.132 419.14,503.599 C 419.258,502.065 418.708,500.79 416.828,499.91 C 418.802,500.933 420.178,500.539 421.438,499.403 C 422.698,498.267 423.823,496.39 425.255,494.503 C 423.378,497.185 421.771,500.313 421.421,502.266 C 420.125,503.517 418.596,506.136 417.465,508.931 L 417.465,508.931 M 395.525,505.485 C 396.148,501.732 396.912,498.396 397.086,495.856 C 397.259,493.316 396.827,491.562 395.039,491.014 C 396.865,491.657 398.302,490.427 399.849,488.179 C 401.396,485.931 403.031,482.664 405.253,479.293 C 402.47,483.715 400.66,488.737 400.942,493.078 C 398.08,495.933 396.416,500.613 395.525,505.485 L 395.525,505.485 M 401.801,506.613 C 402.446,503.066 403.144,500.01 403.231,497.675 C 403.318,495.34 402.782,493.716 400.942,493.078 C 402.816,493.817 404.295,492.848 405.911,490.927 C 407.527,489.006 409.262,486.131 411.648,483.123 C 408.675,487.595 406.399,492.404 406.879,495.404 C 404.589,496.981 402.828,501.631 401.801,506.613 L 401.801,506.613 M 407.005,507.333 C 407.719,504.283 408.62,501.678 408.886,499.646 C 409.152,497.614 408.768,496.142 406.879,495.404 C 408.81,496.25 410.183,495.425 411.595,493.708 C 413.007,491.99 414.437,489.378 416.478,486.72 C 413.476,490.223 411.343,494.382 411.764,497.518 C 409.341,499.052 407.881,503.081 407.005,507.333 L 407.005,507.333 M 412.52,508.245 C 413.229,505.782 413.909,503.482 413.979,501.603 C 414.049,499.724 413.501,498.259 411.764,497.518 C 413.525,498.358 415.125,497.775 416.719,496.439 C 418.313,495.103 419.888,493.012 421.535,490.85 C 418.809,494.018 416.761,497.668 416.828,499.91 C 415.188,501.008 413.602,504.454 412.52,508.245 L 412.52,508.245 M 368.374,499.759 C 368.292,495.628 368.347,491.802 367.978,488.946 C 367.61,486.09 366.803,484.199 365.009,483.979 C 366.855,484.285 368.17,482.605 369.503,479.837 C 370.835,477.068 372.167,473.212 374.048,469.216 C 372.192,473.881 370.871,479.314 370.955,484.938 C 369.062,489.786 368.352,494.923 368.374,499.759 L 368.374,499.759 M 373.081,501.106 C 373.054,496.604 373.433,492.717 373.381,489.875 C 373.329,487.034 372.824,485.231 370.955,484.938 C 372.866,485.322 373.962,483.714 375.146,480.93 C 376.33,478.147 377.573,474.185 379.865,469.948 C 378.533,472.577 377.458,475.369 376.884,478.126 C 376.31,480.882 376.234,483.601 376.867,486.098 C 373.85,489.827 372.976,495.471 373.081,501.106 L 373.081,501.106 M 378.845,502.494 C 378.991,497.973 379.436,494.066 379.39,491.192 C 379.344,488.318 378.785,486.466 376.867,486.098 C 378.823,486.559 380.095,485.012 381.484,482.288 C 382.872,479.564 384.35,475.661 386.78,471.495 C 384.052,476.708 382.222,482.573 382.993,487.522 C 380.149,491.21 379.05,496.863 378.845,502.494 L 378.845,502.494 M 384.748,503.701 C 385.053,499.296 385.553,495.478 385.519,492.647 C 385.486,489.815 384.899,487.96 382.993,487.522 C 384.937,488.054 386.336,486.615 387.884,484.031 C 389.432,481.447 391.104,477.715 393.629,473.739 C 390.832,478.743 388.785,484.344 389.065,489.164 C 386.417,492.789 385.174,498.229 384.748,503.701 L 384.748,503.701 M 390.502,504.744 C 390.95,500.591 391.498,496.949 391.488,494.217 C 391.478,491.485 390.895,489.654 389.065,489.164 C 390.932,489.748 392.426,488.431 394.083,486.031 C 395.739,483.631 397.538,480.145 400.021,476.457 C 397.315,481.147 395.245,486.401 395.039,491.014 C 392.628,494.547 391.212,499.633 390.502,504.744 L 390.502,504.744 M 426.75,510.332 C 425.896,511.725 425.282,513.368 425.11,514.777 M 421.696,509.625 C 420.882,511.223 420.389,513.159 420.121,515.264 M 415.686,516.142 C 415.716,513.54 416.604,511.137 417.465,508.931 M 410.857,518.237 C 410.558,514.972 411.588,511.483 412.52,508.245 M 406.306,519.632 C 405.631,515.09 406.168,510.906 407.005,507.333 M 401.801,506.613 C 401.192,509.569 400.812,512.64 400.629,515.267 C 400.445,517.895 400.46,520.08 400.722,521.284 M 395.3,522.345 C 394.086,516.569 394.668,510.646 395.525,505.485 M 390.502,504.744 C 389.964,508.619 389.782,512.499 389.803,515.715 C 389.825,518.932 390.055,521.486 390.474,522.734 M 385.629,522.161 C 384.932,518.854 384.605,515.614 384.512,512.512 C 384.418,509.409 384.558,506.446 384.748,503.701 M 380.656,520.826 C 379.763,517.542 379.254,514.325 379.003,511.245 C 378.751,508.164 378.756,505.22 378.845,502.494 M 376.067,519.53 C 374.88,516.218 374.129,512.986 373.683,509.891 C 373.237,506.797 373.097,503.843 373.081,501.106 M 371.237,517.918 C 368.857,512.435 368.493,505.8 368.374,499.759 M 362.852,497.886 C 363.066,500.675 363.356,503.677 363.944,506.778 C 364.531,509.88 365.414,513.08 366.762,516.286 M 456.455,502.792 C 458.369,502.657 460.221,503.065 461.589,503.519 C 462.957,503.973 463.839,504.464 463.839,504.464 M 449.871,504.495 C 451.967,504.449 454.179,504.976 455.858,505.537 C 457.537,506.099 458.684,506.682 458.684,506.682 M 443.391,508.143 C 446.436,507.559 449.022,508.008 450.82,508.608 C 452.619,509.207 453.632,509.945 453.632,509.945 M 438.982,510.557 C 441.695,511.194 443.995,512.277 445.596,513.199 C 447.197,514.122 448.107,514.877 448.107,514.877 M 432.819,532.104 C 432.818,535.46 434.288,537.903 434.288,537.903 M 433.6,525.853 C 433.878,527.707 434.559,529.467 435.152,530.771 C 435.746,532.074 436.263,532.922 436.263,532.922 M 435.577,518.847 C 435.794,521.811 436.618,523.983 437.393,525.437 C 438.167,526.89 438.901,527.621 438.901,527.621 M 436.565,513.974 C 438.009,516.277 439.561,518.106 440.758,519.375 C 441.955,520.645 442.802,521.348 442.802,521.348 M 351.891,472.325 C 351.863,472.757 351.51,473.087 351.103,473.062 C 350.695,473.038 350.383,472.669 350.407,472.236 C 350.431,471.804 350.785,471.471 351.196,471.495 C 351.607,471.52 351.918,471.892 351.891,472.325 L 351.891,472.325 M 354.97,510.893 C 355.961,510.971 356.602,509.691 357.029,507.613 C 357.456,505.534 357.662,502.659 357.739,499.559 C 357.817,496.458 357.764,493.133 357.721,490.16 C 357.677,487.187 357.647,484.566 357.819,482.874 C 358.15,479.617 359.108,474.561 360.806,469.53 C 362.505,464.499 364.97,459.493 368.227,456.392 M 368.227,456.392 C 368.479,456.151 368.735,455.921 368.995,455.704 C 370.143,454.747 369.832,454.362 368.497,454.474 C 368.497,454.474 368.497,454.474 368.497,454.474 M 368.497,454.474 C 366.757,454.614 363.294,455.603 359.245,457.4 C 355.195,459.197 350.578,461.815 346.454,465.016 C 342.33,468.217 338.709,471.992 336.377,475.895 C 334.046,479.798 332.998,483.809 333.734,487.515 C 335.342,488.369 337.321,489.015 339.36,489.658 C 341.398,490.302 343.498,490.942 345.34,491.741 C 341.851,491.894 337.795,490.782 334.84,490.093 C 335.737,491.896 337.727,495.265 341.023,499.065 C 344.32,502.864 348.886,507.079 354.97,510.893 L 354.97,510.893 M 354.017,471.292 C 353.919,472.612 352.87,473.609 351.675,473.534 C 350.481,473.459 349.565,472.338 349.632,471.016 C 349.699,469.694 350.75,468.671 351.976,468.748 C 353.202,468.825 354.116,469.972 354.017,471.292 L 354.017,471.292 M 351.891,472.325 C 351.863,472.757 351.51,473.087 351.103,473.062 C 350.695,473.038 350.383,472.669 350.407,472.236 C 350.431,471.804 350.785,471.471 351.196,471.495 C 351.607,471.52 351.918,471.892 351.891,472.325 L 351.891,472.325"
+     id="path2459"
+     inkscape:path-effect="#path-effect2461"
+     inkscape:original-d="M 219.34365,533.3047 C 308.63155,445.53625 433.48392,486.37703 468.82612,545.02535"
+     sodipodi:nodetypes="cc"
+     transform="translate(293.62325,-417.93955)" />
+  <text
+     xml:space="preserve"
+     style="font-size:22px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+     x="101.82097"
+     y="192.80379"
+     id="text3070"
+     transform="translate(-61.566164,-16.575507)"><tspan
+       sodipodi:role="line"
+       id="tspan3072"
+       x="101.82097"
+       y="192.80379">Transform shape </tspan><tspan
+       sodipodi:role="line"
+       x="101.82097"
+       y="220.30379"
+       id="tspan3137">into single path</tspan></text>
+  <path
+     id="path3076"
+     style="fill:#b3b3b3;fill-rule:evenodd;stroke:#000000;stroke-width:0.53125;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+     d="M 222.16697,282.21392 C 222.16697,284.29339 222.82846,285.06476 223.56706,286.39856 C 223.00882,288.75735 222.65196,291.13437 224.61713,293.72171 C 223.91268,295.63968 224.81088,297.55764 226.19222,299.4756 C 225.96075,301.31577 227.16525,302.91749 228.29236,304.53206 C 228.49486,306.62439 229.60711,308.71672 231.44256,310.80904 C 214.81651,305.57823 213.7112,292.80192 211.14128,290.40886 C 209.77648,289.13799 208.29946,289.28829 207.24585,287.69781 C 206.19224,286.10733 205.56206,282.77606 204.49086,282.21374 C 205.56206,281.65142 206.19224,278.32015 207.24585,276.72966 C 208.29946,275.13918 209.77648,275.28949 211.14128,274.01862 C 213.7112,271.62556 214.81651,258.84925 231.44256,253.61844 C 229.60711,255.71076 228.49486,257.80309 228.29236,259.89542 C 227.16525,261.50999 225.96075,263.11171 226.19222,264.95187 C 224.81088,266.86984 223.91268,268.7878 224.61713,270.70577 C 222.65196,273.29311 223.00882,275.67013 223.56706,278.02891 C 222.82846,279.36272 222.16697,280.13409 222.16697,282.21356M 204.49085,273.23399 C 203.98086,274.28407 203.6492,275.59042 203.58037,276.93061 M 200.17344,270.2597 C 199.43845,271.88616 198.92533,273.48374 198.75904,275.02208 M 143.0378,250.68184 C 138.63497,256.05965 136.73122,261.92992 135.39982,267.19294 M 194.55651,273.60135 C 194.84915,271.50815 195.25833,269.32192 196.42978,267.31926 M 171.70827,267.83352 C 172.76779,262.88358 174.47467,257.45951 178.27758,252.93198 M 178.48901,269.08332 C 179.47413,264.97528 180.78784,260.35251 183.73781,256.04452 M 183.93049,270.47302 C 184.70112,267.05645 186.0406,263.34502 188.83696,259.90854 M 189.86106,272.04715 C 190.51164,268.8551 191.32144,265.59547 192.77118,263.19917 M 141.61467,266.24276 C 142.90477,260.54159 144.69065,254.47271 148.76856,249.9859 M 146.96157,265.84703 C 148.26175,260.69526 150.36258,254.96625 154.97089,249.54856 M 153.42804,265.80069 C 154.70259,260.65572 156.64326,254.93485 160.82232,249.52247 M 159.99704,266.17933 C 161.27694,260.98767 163.13606,255.18179 167.14716,249.73932 M 172.98471,250.81284 C 171.02341,252.78707 167.8957,259.50717 166.31679,266.87708M 135.18495,296.33048 C 133.26853,288.37162 132.84206,282.18105 129.10513,282.25886 C 132.97866,282.33952 133.03513,275.66249 135.39982,267.19294 C 134.30038,272.35307 133.8051,277.87388 135.34912,282.25886 C 133.88972,286.29332 134.01008,291.35016 135.18495,296.33048 z M 203.53648,286.63606 C 203.41639,284.03307 203.568,282.17349 200.70659,282.25886 C 203.9169,282.35464 203.19711,280.00905 203.58037,276.93061 C 203.5579,279.06661 203.8878,281.19273 204.31585,282.25886 L 204.31585,282.25887 C 203.95011,283.16983 203.54677,284.84924 203.53648,286.63606 z M 198.66295,288.57942 C 198.2027,284.30886 199.36186,282.16328 194.81263,282.25886 C 199.67393,282.36099 197.88126,279.83963 198.75904,275.02208 C 198.49317,278.39102 199.31667,281.21163 200.70659,282.25886 C 199.44766,283.2074 198.53473,285.66717 198.66295,288.57942 z M 194.44147,290.00336 C 193.92375,285.74608 193.80339,282.17438 189.66008,282.25886 C 194.09761,282.34934 193.83848,278.27852 194.55651,273.60135 C 194.17833,276.85133 194.2034,280.37059 194.81263,282.25886 C 194.2595,283.97327 194.11634,287.00374 194.44147,290.00336 z M 171.55795,295.77255 C 170.03954,288.29391 169.88465,282.18114 166.15448,282.25886 C 170.01621,282.33933 170.00056,275.69296 171.70827,267.83352 C 170.70105,272.94317 170.62503,278.28649 172.39366,282.25886 C 170.70515,285.9306 170.65555,290.89008 171.55795,295.77255 z M 178.32051,294.55955 C 176.70212,287.50462 176.27836,282.17724 172.39366,282.25886 C 176.4134,282.34331 176.5269,276.46666 178.48901,269.08332 C 177.52181,274.35274 177.16955,279.66961 178.75686,282.25886 C 177.24732,284.59203 177.30469,289.55929 178.32051,294.55955 z M 183.75252,293.16711 C 182.47379,287.01456 182.80529,282.17613 178.75686,282.25886 C 182.96531,282.34486 182.49897,277.00056 183.93049,270.47302 C 182.72211,274.90262 182.39788,279.57299 184.07001,282.25886 C 182.49555,284.65515 182.74492,288.92728 183.75252,293.16711 z M 189.47331,291.6447 C 188.51891,286.59564 187.83811,282.1797 184.07001,282.25886 C 187.96294,282.34065 188.88725,277.38553 189.86106,272.04715 C 188.88532,276.09751 188.61072,280.27759 189.66008,282.25886 C 188.67708,283.97062 188.72971,287.76284 189.47331,291.6447 z M 141.43016,297.29741 C 139.66809,289.19679 138.95616,282.18308 135.34912,282.25886 C 139.0833,282.33731 139.52214,274.7716 141.61467,266.24276 C 140.69992,271.16122 140.2893,276.73877 141.35852,282.25886 C 140.34978,287.36255 140.47855,292.54298 141.43016,297.29741 z M 146.75131,297.71816 C 144.67177,288.92341 145.13086,282.17728 141.35852,282.25886 C 145.24722,282.34296 144.46755,275.07936 146.96157,265.84703 C 145.5752,271.53769 145.09253,277.64175 147.36791,282.25886 C 145.20354,286.53394 145.37293,292.22852 146.75131,297.71816 z M 153.23246,297.78187 C 151.15418,288.9409 151.26304,282.17703 147.36791,282.25886 C 151.37691,282.34309 150.97137,275.05736 153.42804,265.80069 C 152.1671,271.51636 151.69176,277.64899 153.6416,282.25886 C 151.78699,286.52701 151.96311,292.27065 153.23246,297.78187 z M 159.80702,297.41647 C 157.77308,288.78617 157.54187,282.17803 153.6416,282.25886 C 157.66014,282.34214 157.59264,275.22043 159.99704,266.17933 C 158.81772,271.76272 158.30851,277.70871 159.9153,282.25886 C 158.38551,286.47575 158.59376,292.04269 159.80702,297.41647 z M 166.13896,296.71635 C 164.23759,288.54793 163.6947,282.17946 159.9153,282.25886 C 163.81778,282.34085 164.11698,275.43924 166.31679,266.87708 C 165.31154,272.17911 164.90973,277.81395 166.15448,282.25886 C 164.97206,286.36893 165.13235,291.64061 166.13896,296.71635 zM 203.53648,286.63606 C 203.52708,288.26928 203.86404,289.99313 204.49085,291.28373 M 198.66295,288.57942 C 198.74187,290.37184 199.28892,292.30068 200.17344,294.25803 M 196.42978,297.19847 C 195.08569,294.90067 194.72769,292.3571 194.44147,290.00336 M 192.77118,301.31856 C 190.77392,298.55525 190.1004,294.96224 189.47331,291.6447 M 188.83696,304.60919 C 185.83823,300.81967 184.50149,296.77071 183.75252,293.16711 M 178.32051,294.55955 C 179.52559,300.49142 182.00324,306.4897 183.73781,308.47321 M 178.27758,311.58575 C 174.26911,306.69836 172.60224,300.91596 171.55795,295.77255 M 166.13896,296.71635 C 167.66558,304.41417 170.95278,311.65957 172.98471,313.70489 M 167.14716,314.77841 C 162.93082,308.94901 161.07415,302.79311 159.80702,297.41647 M 160.82232,314.99526 C 156.43035,309.20086 154.48571,303.11319 153.23246,297.78187 M 154.97089,314.96916 C 150.11628,309.15509 148.0156,303.06509 146.75131,297.71816 M 148.76856,314.53182 C 144.48408,309.69842 142.71832,303.2194 141.43016,297.29741 M 135.18495,296.33048 C 136.49957,301.79013 138.38713,308.04041 143.0378,313.83589M 222.51699,263.38263 C 224.96715,260.76722 228.29236,259.89542 228.29236,259.89542 M 218.84176,268.78781 C 221.81695,266.1724 226.19222,264.95187 226.19222,264.95187 M 216.04158,275.58787 C 220.06684,271.05449 224.61713,270.70577 224.61713,270.70577 M 213.94145,280.12124 C 219.01678,278.02891 223.56706,278.02891 223.56706,278.02891M 222.51699,301.04485 C 224.96715,303.66026 228.29236,304.53206 228.29236,304.53206 M 218.84176,295.63967 C 221.81695,298.25508 226.19222,299.4756 226.19222,299.4756 M 216.04158,288.83961 C 220.06684,293.37299 224.61713,293.72171 224.61713,293.72171 M 213.94145,284.30624 C 219.01678,286.39856 223.56706,286.39856 223.56706,286.39856M 121.53562,272.01348 C 121.53562,272.44659 121.22199,272.7981 120.83557,272.7981 C 120.44915,272.7981 120.13553,272.44659 120.13553,272.01348 C 120.13553,271.58036 120.44915,271.22886 120.83557,271.22886 C 121.22199,271.22886 121.53562,271.58036 121.53562,272.01348 zM 128.09853,310.19842 C 132.82383,310.11124 128.09853,288.83926 128.09853,282.0392 C 128.09853,275.23914 130.03686,260.45299 135.27399,253.70562 C 140.5998,246.84399 100.9581,269.63911 103.85951,287.44438 C 107.09721,289.27517 111.99752,290.23415 115.93527,291.71621 C 112.26003,291.97775 107.97226,290.84441 104.90957,290.05978 C 106.65968,293.72136 112.78507,303.83426 128.09853,310.19842 z M 123.46075,270.83655 C 123.46075,272.15995 122.53949,273.23401 121.40437,273.23401 C 120.26925,273.23401 119.34799,272.15995 119.34799,270.83655 C 119.34799,269.51316 120.26925,268.4391 121.40437,268.4391 C 122.53949,268.4391 123.46075,269.51316 123.46075,270.83655 zM 121.53562,272.01348 C 121.53562,272.44659 121.22199,272.7981 120.83557,272.7981 C 120.44915,272.7981 120.13553,272.44659 120.13553,272.01348 C 120.13553,271.58036 120.44915,271.22886 120.83557,271.22886 C 121.22199,271.22886 121.53562,271.58036 121.53562,272.01348 z" />
+  <text
+     xml:space="preserve"
+     style="font-size:22px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+     x="214.29762"
+     y="353.823"
+     id="text3121"
+     transform="translate(-58.014272,18.943436)"><tspan
+       sodipodi:role="line"
+       id="tspan3123"
+       x="214.29762"
+       y="353.823">Copy to clipboard</tspan><tspan
+       sodipodi:role="line"
+       x="214.29762"
+       y="372.96726"
+       id="tspan3143"
+       style="font-size:12px">(it is easiest later on to move the nose</tspan><tspan
+       sodipodi:role="line"
+       x="214.29762"
+       y="387.96726"
+       id="tspan3145"
+       style="font-size:12px">of the fish to the top-left corner of the </tspan><tspan
+       sodipodi:role="line"
+       x="214.29762"
+       y="402.96726"
+       style="font-size:12px"
+       id="tspan3153">document (0,0) before copying to the </tspan><tspan
+       sodipodi:role="line"
+       x="214.29762"
+       y="417.96726"
+       style="font-size:12px"
+       id="tspan3163">clipboard, so you don't have to manually </tspan><tspan
+       sodipodi:role="line"
+       x="214.29762"
+       y="432.96726"
+       style="font-size:12px"
+       id="tspan3169">tweak the origin parameter)</tspan></text>
+  <path
+     style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+     d="M 439.25092,345.53523 C 504.36898,288.70493 570.671,289.88889 619.21356,351.45506"
+     id="path3125"
+     transform="translate(2.3679295,-35.518942)" />
+  <text
+     xml:space="preserve"
+     style="font-size:22px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+     x="460.56229"
+     y="364.47867"
+     id="text3127"
+     transform="translate(-26.047224,0)"><tspan
+       sodipodi:role="line"
+       id="tspan3129"
+       x="460.56229"
+       y="364.47867">Draw the skeleton path</tspan></text>
+  <text
+     xml:space="preserve"
+     style="font-size:22px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+     x="632.23718"
+     y="202.2755"
+     id="text3131"
+     transform="translate(-266.39207,-30.783083)"><tspan
+       sodipodi:role="line"
+       id="tspan3133"
+       x="632.23718"
+       y="202.2755">Press Ctrl-Shift-7 and apply </tspan><tspan
+       sodipodi:role="line"
+       x="632.23718"
+       y="229.7755"
+       id="tspan3171">Skeletal Strokes to the skeleton path.</tspan><tspan
+       sodipodi:role="line"
+       x="632.23718"
+       y="257.2755"
+       id="tspan3135">Press the paste button next to &quot;pattern&quot;</tspan></text>
+  <g
+     id="g3179">
+    <path
+       transform="translate(58.014272,-17.759472)"
+       d="M -7.1037903,199.31558 A 37.886871,34.92696 0 1 1 -82.877533,199.31558 A 37.886871,34.92696 0 1 1 -7.1037903,199.31558 z"
+       sodipodi:ry="34.92696"
+       sodipodi:rx="37.886871"
+       sodipodi:cy="199.31558"
+       sodipodi:cx="-44.990662"
+       id="path3177"
+       style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#0000ff;stroke-width:0.53125;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       sodipodi:type="arc" />
+    <text
+       transform="translate(84.061496,-150.36352)"
+       id="text3173"
+       y="346.71921"
+       x="-82.877533"
+       style="font-size:22px;font-style:normal;font-weight:normal;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       xml:space="preserve"><tspan
+         style="font-size:40px;font-weight:bold;writing-mode:lr-tb;fill:#0000ff"
+         y="346.71921"
+         x="-82.877533"
+         id="tspan3175"
+         sodipodi:role="line">1</tspan></text>
+  </g>
+  <path
+     transform="translate(185.88246,192.98625)"
+     d="M -7.1037903,199.31558 A 37.886871,34.92696 0 1 1 -82.877533,199.31558 A 37.886871,34.92696 0 1 1 -7.1037903,199.31558 z"
+     sodipodi:ry="34.92696"
+     sodipodi:rx="37.886871"
+     sodipodi:cy="199.31558"
+     sodipodi:cx="-44.990662"
+     id="path3186"
+     style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#0000ff;stroke-width:0.53125;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     sodipodi:type="arc" />
+  <text
+     transform="translate(211.92969,60.3822)"
+     id="text3188"
+     y="346.71921"
+     x="-82.877533"
+     style="font-size:22px;font-style:normal;font-weight:normal;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+     xml:space="preserve"><tspan
+       style="font-size:40px;font-weight:bold;writing-mode:lr-tb;fill:#0000ff"
+       y="346.71921"
+       x="-82.877533"
+       id="tspan3190"
+       sodipodi:role="line">2</tspan></text>
+  <text
+     xml:space="preserve"
+     style="font-size:22px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+     x="466.48212"
+     y="-32.149517"
+     id="text3213"
+     transform="translate(-14.207577,-11.839647)"><tspan
+       sodipodi:role="line"
+       id="tspan3215"
+       x="466.48212"
+       y="-32.149517">Press F2 and notice that the </tspan><tspan
+       sodipodi:role="line"
+       x="466.48212"
+       y="-4.6495171"
+       id="tspan3217">red skeleton path is live </tspan><tspan
+       sodipodi:role="line"
+       x="466.48212"
+       y="22.850483"
+       id="tspan3219">edittable on-canvas</tspan></text>
+  <g
+     id="g3221"
+     transform="translate(426.22731,-182.33057)">
+    <path
+       sodipodi:type="arc"
+       style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#0000ff;stroke-width:0.53125;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3223"
+       sodipodi:cx="-44.990662"
+       sodipodi:cy="199.31558"
+       sodipodi:rx="37.886871"
+       sodipodi:ry="34.92696"
+       d="M -7.1037903,199.31558 A 37.886871,34.92696 0 1 1 -82.877533,199.31558 A 37.886871,34.92696 0 1 1 -7.1037903,199.31558 z"
+       transform="translate(58.014272,-17.759472)" />
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-weight:normal;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="-82.877533"
+       y="346.71921"
+       id="text3225"
+       transform="translate(84.061496,-150.36352)"><tspan
+         sodipodi:role="line"
+         id="tspan3227"
+         x="-82.877533"
+         y="346.71921"
+         style="font-size:40px;font-weight:bold;writing-mode:lr-tb;fill:#0000ff">5</tspan></text>
+  </g>
+</svg>
diff --git a/share/examples/live-path-effects-slant.svg b/share/examples/live-path-effects-slant.svg
new file mode 100644 (file)
index 0000000..7b547bd
--- /dev/null
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46dev+devel"
+   sodipodi:docname="live-path-effects-slant.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <inkscape:path-effect
+       id="path-effect2184"
+       effect="slant"
+       factor="2" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.313708"
+     inkscape:cx="6.3191828"
+     inkscape:cy="1044.8634"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="573"
+     inkscape:window-height="613"
+     inkscape:window-x="276"
+     inkscape:window-y="110"
+     factor="2">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2183" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     style="display:inline">
+    <path
+       inkscape:path-effect="#path-effect2184"
+       inkscape:original-d="M 0,-0.63781738 C 4,-0.63781738 4.8367975,-0.64368656 6,-0.63781738 C 6.288508,-0.63636166 4.7826693,4.8807011 6,5.3621826 C 7.1743099,5.8266485 2.1209191,7.4831017 1,6.3621826 C -0.18260145,5.1795812 3.505791,2.5232159 7,1.3621826 C 8.1643369,0.97530425 10.081095,1.3804093 11,2.3621826 C 11.398913,2.7883882 13.058582,5.6741739 13,6.3621826 C 12.949526,6.9549646 12.275257,5.4894846 11.723372,4.5675324 C 11.47268,4.148738 10.024417,3.8420955 9.0099252,3.8914702 C 8.6338297,3.9097746 8.7458842,5.2626113 8.8681478,6.2792291 C 8.932653,6.8155879 19,5.2583593 19,5.3621826"
+       sodipodi:nodetypes="cssssssssss"
+       id="path2332"
+       d="M 0,-0.63781738 C 4,7.3621826 4.8367975,9.0299084 6,11.362183 C 6.288508,11.940654 4.7826693,14.44604 6,17.362183 C 7.1743099,20.175268 2.1209191,11.72494 1,8.3621826 C -0.18260145,4.8143783 3.505791,9.5347979 7,15.362183 C 8.1643369,17.303978 10.081095,21.542599 11,24.362183 C 11.398913,25.586214 13.058582,31.791338 13,32.362183 C 12.949526,32.854017 12.275257,30.039999 11.723372,28.014276 C 11.47268,27.094098 10.024417,23.890929 9.0099252,21.911321 C 8.6338297,21.177434 8.7458842,22.75438 8.8681478,24.015525 C 8.932653,24.680894 19,43.258359 19,43.362183"
+       style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+  </g>
+</svg>
diff --git a/share/examples/live-path-effects-uri.svg b/share/examples/live-path-effects-uri.svg
new file mode 100644 (file)
index 0000000..a6ecb3e
--- /dev/null
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46dev+devel"
+   sodipodi:docname="live-path-effects-uri.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <inkscape:path-effect
+       id="path-effect2184"
+       effect="slant"
+       factor="2" />
+    <inkscape:path-effect
+       id="path-effect2185"
+       effect="skeletal"
+       skeleton="M 0,0 L 1,1" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.313708"
+     inkscape:cx="6.3191828"
+     inkscape:cy="1044.8634"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="573"
+     inkscape:window-height="613"
+     inkscape:window-x="276"
+     inkscape:window-y="110"
+     factor="2">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2183" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     style="display:inline">
+    <path
+       inkscape:path-effect="#path-effect2184"
+       inkscape:original-d="M 0,-0.63781738 C 4,-0.63781738 4.8367975,-0.64368656 6,-0.63781738 C 6.288508,-0.63636166 4.7826693,4.8807011 6,5.3621826 C 7.1743099,5.8266485 2.1209191,7.4831017 1,6.3621826 C -0.18260145,5.1795812 3.505791,2.5232159 7,1.3621826 C 8.1643369,0.97530425 10.081095,1.3804093 11,2.3621826 C 11.398913,2.7883882 13.058582,5.6741739 13,6.3621826 C 12.949526,6.9549646 12.275257,5.4894846 11.723372,4.5675324 C 11.47268,4.148738 10.024417,3.8420955 9.0099252,3.8914702 C 8.6338297,3.9097746 8.7458842,5.2626113 8.8681478,6.2792291 C 8.932653,6.8155879 19,5.2583593 19,5.3621826"
+       sodipodi:nodetypes="cssssssssss"
+       id="path2332"
+       d="M 0,-0.63781738 C 4,7.3621826 4.8367975,9.0299084 6,11.362183 C 6.288508,11.940654 4.7826693,14.44604 6,17.362183 C 7.1743099,20.175268 2.1209191,11.72494 1,8.3621826 C -0.18260145,4.8143783 3.505791,9.5347979 7,15.362183 C 8.1643369,17.303978 10.081095,21.542599 11,24.362183 C 11.398913,25.586214 13.058582,31.791338 13,32.362183 C 12.949526,32.854017 12.275257,30.039999 11.723372,28.014276 C 11.47268,27.094098 10.024417,23.890929 9.0099252,21.911321 C 8.6338297,21.177434 8.7458842,22.75438 8.8681478,24.015525 C 8.932653,24.680894 19,43.258359 19,43.362183"
+       style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+  </g>
+</svg>
index 24f4dd3ae99daada3d65dff16941982c14b11390..16f8f2e2948d49b027941594bb47fd038c38730e 100644 (file)
@@ -510,6 +510,11 @@ override) the bindings in the main default.xml.
   <bind key="w" modifiers="Ctrl,Shift" action="DialogSwatches" display="true"/>
   <bind key="W" modifiers="Ctrl,Shift" action="DialogSwatches" />
 
+  <bind key="ampersand" modifiers="Ctrl" action="DialogLivePathEffect" />  <!-- & -->
+  <bind key="ampersand" modifiers="Ctrl,Shift" action="DialogLivePathEffect" />
+  <bind key="7" modifiers="Ctrl,Shift" action="DialogLivePathEffect" display="true" />
+
+
   <bind action="DialogMetadata" />
   <bind action="DialogClonetiler" />
   <bind action="DialogGridArrange" />
index 24f4dd3ae99daada3d65dff16941982c14b11390..e42da28f154fb66e03edcf6dff328de4fa735d77 100644 (file)
@@ -510,6 +510,10 @@ override) the bindings in the main default.xml.
   <bind key="w" modifiers="Ctrl,Shift" action="DialogSwatches" display="true"/>
   <bind key="W" modifiers="Ctrl,Shift" action="DialogSwatches" />
 
+  <bind key="ampersand" modifiers="Ctrl" action="DialogLivePathEffect" />  <!-- & -->
+  <bind key="ampersand" modifiers="Ctrl,Shift" action="DialogLivePathEffect" />
+  <bind key="7" modifiers="Ctrl,Shift" action="DialogLivePathEffect" display="true" />
+
   <bind action="DialogMetadata" />
   <bind action="DialogClonetiler" />
   <bind action="DialogGridArrange" />
diff --git a/src/2geom/Makefile_insert b/src/2geom/Makefile_insert
new file mode 100644 (file)
index 0000000..a100c41
--- /dev/null
@@ -0,0 +1,77 @@
+## Makefile.am fragment sourced by src/Makefile.am.\r
+\r
+2geom/all: 2geom/lib2geom.a\r
+\r
+2geom/clean:\r
+       rm -f 2geom/lib2geom.a $(2geom_lib2geom_a_OBJECTS)\r
+\r
+2geom_lib2geom_a_SOURCES = \\r
+       2geom/bezier-to-sbasis.h        \\r
+       2geom/bezier-utils.cpp  \\r
+       2geom/bezier-utils.h    \\r
+       2geom/choose.h  \\r
+       2geom/circle-circle.cpp \\r
+       2geom/circulator.h      \\r
+       2geom/concepts.h        \\r
+       2geom/conjugate_gradient.cpp    \\r
+       2geom/conjugate_gradient.h      \\r
+       2geom/convex-cover.cpp  \\r
+       2geom/convex-cover.h    \\r
+       2geom/coord.h   \\r
+       2geom/d2.cpp    \\r
+       2geom/d2.h      \\r
+       2geom/geom.cpp  \\r
+       2geom/geom.h    \\r
+       2geom/interval.h        \\r
+       2geom/isnan.h   \\r
+       2geom/linear.h  \\r
+       2geom/matrix.cpp        \\r
+       2geom/matrix.h  \\r
+       2geom/path.cpp  \\r
+       2geom/path.h    \\r
+       2geom/piecewise.cpp     \\r
+       2geom/piecewise.h       \\r
+       2geom/point.cpp \\r
+       2geom/point.h   \\r
+       2geom/point-l.h \\r
+       2geom/point-ops.h       \\r
+       2geom/poly.cpp  \\r
+       2geom/poly.h    \\r
+       2geom/poly-dk-solve.cpp \\r
+       2geom/poly-dk-solve.h   \\r
+       2geom/poly-laguerre-solve.cpp   \\r
+       2geom/poly-laguerre-solve.h     \\r
+       2geom/quadtree.cpp      \\r
+       2geom/quadtree.h        \\r
+       2geom/rect.h    \\r
+       2geom/sbasis-2d.cpp     \\r
+       2geom/sbasis-2d.h       \\r
+       2geom/sbasis.cpp        \\r
+       2geom/sbasis.h  \\r
+       2geom/sbasis-geometric.cpp      \\r
+       2geom/sbasis-geometric.h        \\r
+       2geom/sbasis-math.cpp   \\r
+       2geom/sbasis-math.h     \\r
+       2geom/sbasis-poly.cpp   \\r
+       2geom/sbasis-poly.h     \\r
+       2geom/sbasis-roots.cpp  \\r
+       2geom/sbasis-to-bezier.cpp      \\r
+       2geom/sbasis-to-bezier.h        \\r
+       2geom/solve-bezier-one-d.cpp    \\r
+       2geom/solve-bezier-parametric.cpp       \\r
+       2geom/solver.h  \\r
+       2geom/sturm.h   \\r
+       2geom/svg-path.cpp      \\r
+       2geom/svg-path.h        \\r
+       2geom/svg-path-parser.cpp       \\r
+       2geom/svg-path-parser.h \\r
+       2geom/transforms.cpp    \\r
+       2geom/transforms.h      \\r
+       2geom/utils.h   \\r
+       2geom/basic-intersection.cpp    \\r
+       2geom/basic-intersection.h      \\r
+       2geom/bezier.h\r
+\r
+\r
+\r
+\r
diff --git a/src/2geom/basic-intersection.cpp b/src/2geom/basic-intersection.cpp
new file mode 100644 (file)
index 0000000..a5b8270
--- /dev/null
@@ -0,0 +1,359 @@
+#include "basic-intersection.h"
+
+using std::vector;
+namespace Geom {
+
+class OldBezier {
+public:
+    std::vector<Geom::Point> p;
+    OldBezier() {
+    }
+    void split(double t, OldBezier &a, OldBezier &b) const;
+    
+    ~OldBezier() {}
+
+    void bounds(double &minax, double &maxax, 
+                double &minay, double &maxay) {
+        // Compute bounding box for a
+        minax = p[0][X];        // These are the most likely to be extremal
+        maxax = p.back()[X];
+        if( minax > maxax )
+            std::swap(minax, maxax);
+        for(unsigned i = 1; i < p.size()-1; i++) {
+            if( p[i][X] < minax )
+                minax = p[i][X];
+            else if( p[i][X] > maxax )
+                maxax = p[i][X];
+        }
+
+        minay = p[0][Y];        // These are the most likely to be extremal
+        maxay = p.back()[Y];
+        if( minay > maxay )
+            std::swap(minay, maxay);
+        for(unsigned i = 1; i < p.size()-1; i++) {
+            if( p[i][Y] < minay )
+                minay = p[i][Y];
+            else if( p[i][Y] > maxay )
+                maxay = p[i][Y];
+        }
+
+    }
+    
+};
+
+static std::vector<std::pair<double, double> >
+find_intersections( OldBezier a, OldBezier b);
+
+static std::vector<std::pair<double, double> > 
+find_self_intersections(OldBezier const &Sb, D2<SBasis> const & A);
+
+std::vector<std::pair<double, double> >
+find_intersections( vector<Geom::Point> const & A, 
+                    vector<Geom::Point> const & B) {
+    OldBezier a, b;
+    a.p = A;
+    b.p = B;
+    return find_intersections(a,b);
+}
+
+std::vector<std::pair<double, double> > 
+find_self_intersections(OldBezier const &Sb) {
+    throw NotImplemented();
+}
+
+std::vector<std::pair<double, double> > 
+find_self_intersections(D2<SBasis> const & A) {
+    OldBezier Sb;
+    Sb.p = sbasis_to_bezier(A);
+    return find_self_intersections(Sb, A);
+}
+
+
+static std::vector<std::pair<double, double> > 
+find_self_intersections(OldBezier const &Sb, D2<SBasis> const & A) {
+
+    
+    vector<double> dr = roots(derivative(A[X]));
+    {
+        vector<double> dyr = roots(derivative(A[Y]));
+        dr.insert(dr.begin(), dyr.begin(), dyr.end());
+    }
+    dr.push_back(0);
+    dr.push_back(1);
+    // We want to be sure that we have no empty segments
+    sort(dr.begin(), dr.end());
+    unique(dr.begin(), dr.end());
+    
+    std::vector<std::pair<double, double> > all_si;
+    
+    vector<OldBezier> pieces;
+    {
+        OldBezier in = Sb, l, r;
+        for(unsigned i = 0; i < dr.size()-1; i++) {
+            in.split((dr[i+1]-dr[i]) / (1 - dr[i]), l, r);
+            pieces.push_back(l);
+            in = r;
+        }
+    }
+    for(unsigned i = 0; i < dr.size()-1; i++) {
+        for(unsigned j = i+1; j < dr.size()-1; j++) {
+            std::vector<std::pair<double, double> > section = 
+                find_intersections( pieces[i], pieces[j]);
+            for(unsigned k = 0; k < section.size(); k++) {
+                double l = section[k].first;
+                double r = section[k].second;
+// XXX: This condition will prune out false positives, but it might create some false negatives.  Todo: Confirm it is correct.
+                if(j == i+1)
+                    if((l == 1) && (r == 0))
+                        continue;
+                all_si.push_back(std::make_pair((1-l)*dr[i] + l*dr[i+1],
+                                                (1-r)*dr[j] + r*dr[j+1]));
+            }
+        }
+    }
+    
+    // Because i is in order, all_si should be roughly already in order?
+    //sort(all_si.begin(), all_si.end());
+    //unique(all_si.begin(), all_si.end());
+    
+    return all_si;
+}
+
+/* The value of 1.0 / (1L<<14) is enough for most applications */
+const double INV_EPS = (1L<<14);
+    
+/*
+ * split the curve at the midpoint, returning an array with the two parts
+ * Temporary storage is minimized by using part of the storage for the result
+ * to hold an intermediate value until it is no longer needed.
+ */
+void OldBezier::split(double t, OldBezier &left, OldBezier &right) const {
+    const unsigned sz = p.size();
+    Geom::Point Vtemp[sz][sz];
+
+    /* Copy control points     */
+    std::copy(p.begin(), p.end(), Vtemp[0]);
+
+    /* Triangle computation    */
+    for (unsigned i = 1; i < sz; i++) {        
+        for (unsigned j = 0; j < sz - i; j++) {
+            Vtemp[i][j] = lerp(t, Vtemp[i-1][j], Vtemp[i-1][j+1]);
+        }
+    }
+    
+    left.p.resize(sz);
+    right.p.resize(sz);
+    for (unsigned j = 0; j < sz; j++)
+        left.p[j]  = Vtemp[j][0];
+    for (unsigned j = 0; j < sz; j++)
+        right.p[j] = Vtemp[sz-1-j][j];
+}
+
+    
+/*
+ * Test the bounding boxes of two OldBezier curves for interference.
+ * Several observations:
+ *     First, it is cheaper to compute the bounding box of the second curve
+ *     and test its bounding box for interference than to use a more direct
+ *     approach of comparing all control points of the second curve with 
+ *     the various edges of the bounding box of the first curve to test
+ *     for interference.
+ *     Second, after a few subdivisions it is highly probable that two corners
+ *     of the bounding box of a given OldBezier curve are the first and last 
+ *     control point.  Once this happens once, it happens for all subsequent
+ *     subcurves.  It might be worth putting in a test and then short-circuit
+ *     code for further subdivision levels.
+ *     Third, in the final comparison (the interference test) the comparisons
+ *     should both permit equality.  We want to find intersections even if they
+ *     occur at the ends of segments.
+ *     Finally, there are tighter bounding boxes that can be derived. It isn't
+ *     clear whether the higher probability of rejection (and hence fewer
+ *     subdivisions and tests) is worth the extra work.
+ */
+
+bool intersect_BB( OldBezier a, OldBezier b ) {
+    double minax, maxax, minay, maxay;
+    a.bounds(minax, maxax, minay, maxay);
+    double minbx, maxbx, minby, maxby;
+    b.bounds(minbx, maxbx, minby, maxby);
+    // Test bounding box of b against bounding box of a
+    // Not >= : need boundary case
+    return not( ( minax > maxbx ) || ( minay > maxby )
+                || ( minbx > maxax ) || ( minby > maxay ) );
+}
+       
+/* 
+ * Recursively intersect two curves keeping track of their real parameters 
+ * and depths of intersection.
+ * The results are returned in a 2-D array of doubles indicating the parameters
+ * for which intersections are found.  The parameters are in the order the
+ * intersections were found, which is probably not in sorted order.
+ * When an intersection is found, the parameter value for each of the two 
+ * is stored in the index elements array, and the index is incremented.
+ * 
+ * If either of the curves has subdivisions left before it is straight
+ *     (depth > 0)
+ * that curve (possibly both) is (are) subdivided at its (their) midpoint(s).
+ * the depth(s) is (are) decremented, and the parameter value(s) corresponding
+ * to the midpoints(s) is (are) computed.
+ * Then each of the subcurves of one curve is intersected with each of the 
+ * subcurves of the other curve, first by testing the bounding boxes for
+ * interference.  If there is any bounding box interference, the corresponding
+ * subcurves are recursively intersected.
+ * 
+ * If neither curve has subdivisions left, the line segments from the first
+ * to last control point of each segment are intersected.  (Actually the 
+ * only the parameter value corresponding to the intersection point is found).
+ *
+ * The apriori flatness test is probably more efficient than testing at each
+ * level of recursion, although a test after three or four levels would
+ * probably be worthwhile, since many curves become flat faster than their 
+ * asymptotic rate for the first few levels of recursion.
+ *
+ * The bounding box test fails much more frequently than it succeeds, providing
+ * substantial pruning of the search space.
+ *
+ * Each (sub)curve is subdivided only once, hence it is not possible that for 
+ * one final line intersection test the subdivision was at one level, while
+ * for another final line intersection test the subdivision (of the same curve)
+ * was at another.  Since the line segments share endpoints, the intersection
+ * is robust: a near-tangential intersection will yield zero or two
+ * intersections.
+ */
+void recursively_intersect( OldBezier a, double t0, double t1, int deptha,
+                          OldBezier b, double u0, double u1, int depthb,
+                          std::vector<std::pair<double, double> > &parameters)
+{
+    if( deptha > 0 )
+    {
+        OldBezier A[2];
+        a.split(0.5, A[0], A[1]);
+       double tmid = (t0+t1)*0.5;
+       deptha--;
+       if( depthb > 0 )
+        {
+           OldBezier B[2];
+            b.split(0.5, B[0], B[1]);
+           double umid = (u0+u1)*0.5;
+           depthb--;
+           if( intersect_BB( A[0], B[0] ) )
+               recursively_intersect( A[0], t0, tmid, deptha,
+                                     B[0], u0, umid, depthb,
+                                     parameters );
+           if( intersect_BB( A[1], B[0] ) )
+               recursively_intersect( A[1], tmid, t1, deptha,
+                                     B[0], u0, umid, depthb,
+                                     parameters );
+           if( intersect_BB( A[0], B[1] ) )
+               recursively_intersect( A[0], t0, tmid, deptha,
+                                     B[1], umid, u1, depthb,
+                                     parameters );
+           if( intersect_BB( A[1], B[1] ) )
+               recursively_intersect( A[1], tmid, t1, deptha,
+                                     B[1], umid, u1, depthb,
+                                     parameters );
+        }
+       else
+        {
+           if( intersect_BB( A[0], b ) )
+               recursively_intersect( A[0], t0, tmid, deptha,
+                                     b, u0, u1, depthb,
+                                     parameters );
+           if( intersect_BB( A[1], b ) )
+               recursively_intersect( A[1], tmid, t1, deptha,
+                                     b, u0, u1, depthb,
+                                     parameters );
+        }
+    }
+    else
+       if( depthb > 0 )
+        {
+           OldBezier B[2];
+            b.split(0.5, B[0], B[1]);
+           double umid = (u0 + u1)*0.5;
+           depthb--;
+           if( intersect_BB( a, B[0] ) )
+               recursively_intersect( a, t0, t1, deptha,
+                                     B[0], u0, umid, depthb,
+                                     parameters );
+           if( intersect_BB( a, B[1] ) )
+               recursively_intersect( a, t0, t1, deptha,
+                                     B[0], umid, u1, depthb,
+                                     parameters );
+        }
+       else // Both segments are fully subdivided; now do line segments
+        {
+           double xlk = a.p.back()[X] - a.p[0][X];
+           double ylk = a.p.back()[Y] - a.p[0][Y];
+           double xnm = b.p.back()[X] - b.p[0][X];
+           double ynm = b.p.back()[Y] - b.p[0][Y];
+           double xmk = b.p[0][X] - a.p[0][X];
+           double ymk = b.p[0][Y] - a.p[0][Y];
+           double det = xnm * ylk - ynm * xlk;
+           if( 1.0 + det == 1.0 )
+               return;
+           else
+            {
+               double detinv = 1.0 / det;
+               double s = ( xnm * ymk - ynm *xmk ) * detinv;
+               double t = ( xlk * ymk - ylk * xmk ) * detinv;
+               if( ( s < 0.0 ) || ( s > 1.0 ) || ( t < 0.0 ) || ( t > 1.0 ) )
+                   return;
+               parameters.push_back(std::pair<double, double>(t0 + s * ( t1 - t0 ),
+                                                         u0 + t * ( u1 - u0 )));
+            }
+        }
+}
+
+inline double log4( double x ) { return log(x)/log(4.); }
+    
+/*
+ * Wang's theorem is used to estimate the level of subdivision required,
+ * but only if the bounding boxes interfere at the top level.
+ * Assuming there is a possible intersection, recursively_intersect is
+ * used to find all the parameters corresponding to intersection points.
+ * these are then sorted and returned in an array.
+ */
+
+double Lmax(Point p) {
+    return std::max(fabs(p[X]), fabs(p[Y]));
+}
+
+unsigned wangs_theorem(OldBezier a) {
+    return 12; // seems a good approximation!
+    double la1 = Lmax( ( a.p[2] - a.p[1] ) - (a.p[1] - a.p[0]) );
+    double la2 = Lmax( ( a.p[3] - a.p[2] ) - (a.p[2] - a.p[1]) );
+    double l0 = std::max(la1, la2);
+    unsigned ra;
+    if( l0 * 0.75 * M_SQRT2 + 1.0 == 1.0 ) 
+        ra = 0;
+    else
+        ra = (unsigned)ceil( log4( M_SQRT2 * 6.0 / 8.0 * INV_EPS * l0 ) );
+    std::cout << ra << std::endl;
+    return ra;
+}
+
+std::vector<std::pair<double, double> > find_intersections( OldBezier a, OldBezier b)
+{
+    std::vector<std::pair<double, double> > parameters;
+    if( intersect_BB( a, b ) )
+    {
+       recursively_intersect( a, 0., 1., wangs_theorem(a), 
+                               b, 0., 1., wangs_theorem(b), 
+                               parameters);
+    }
+    std::sort(parameters.begin(), parameters.end());
+    return parameters;
+}
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/basic-intersection.h b/src/2geom/basic-intersection.h
new file mode 100644 (file)
index 0000000..76abcce
--- /dev/null
@@ -0,0 +1,34 @@
+#include "sbasis.h"
+#include "bezier-to-sbasis.h"
+#include "sbasis-to-bezier.h"
+#include "d2.h"
+
+namespace Geom {
+
+std::vector<std::pair<double, double> > 
+find_intersections( D2<SBasis> const & A, 
+                    D2<SBasis> const & B);
+
+std::vector<std::pair<double, double> > 
+find_self_intersections(D2<SBasis> const & A);
+
+// Bezier form
+std::vector<std::pair<double, double> > 
+find_intersections( std::vector<Point> const & A, 
+                    std::vector<Point> const & B);
+
+std::vector<std::pair<double, double> > 
+find_self_intersections(std::vector<Point> const & A);
+
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/bezier-to-sbasis.h b/src/2geom/bezier-to-sbasis.h
new file mode 100644 (file)
index 0000000..03c99a9
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * bezier-to-sbasis.h
+ *
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef _BEZIER_TO_SBASIS
+#define _BEZIER_TO_SBASIS
+
+#include "coord.h"
+
+#include "d2.h"
+#include "point.h"
+
+namespace Geom{
+
+template <unsigned order>
+struct bezier_to_sbasis_impl {
+    static inline SBasis compute(Coord const *handles) {
+        return multiply(Linear(1, 0), bezier_to_sbasis_impl<order-1>::compute(handles)) +
+               multiply(Linear(0, 1), bezier_to_sbasis_impl<order-1>::compute(handles+1));
+    }
+};
+
+template <>
+struct bezier_to_sbasis_impl<1> {
+    static inline SBasis compute(Coord const *handles) {
+        return Linear(handles[0], handles[1]);
+    }
+};
+
+template <>
+struct bezier_to_sbasis_impl<0> {
+    static inline SBasis compute(Coord const *handles) {
+        return Linear(handles[0], handles[0]);
+    }
+};
+
+template <unsigned order>
+inline SBasis bezier_to_sbasis(Coord const *handles) {
+    return bezier_to_sbasis_impl<order>::compute(handles);
+}
+
+template <unsigned order, typename T>
+inline D2<SBasis> handles_to_sbasis(T const &handles) {
+    double v[2][order+1];
+    for(unsigned i = 0; i <= order; i++)
+        for(unsigned j = 0; j < 2; j++)
+             v[j][i] = handles[i][j];
+    return D2<SBasis>(bezier_to_sbasis<order>(v[0]),
+                      bezier_to_sbasis<order>(v[1]));
+}
+
+};
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/bezier-utils.cpp b/src/2geom/bezier-utils.cpp
new file mode 100644 (file)
index 0000000..76c90a9
--- /dev/null
@@ -0,0 +1,1004 @@
+#define __SP_BEZIER_UTILS_C__
+
+/** \file
+ * Bezier interpolation for inkscape drawing code.
+ */
+/*
+ * Original code published in:
+ *   An Algorithm for Automatically Fitting Digitized Curves
+ *   by Philip J. Schneider
+ *  "Graphics Gems", Academic Press, 1990
+ *
+ * Authors:
+ *   Philip J. Schneider
+ *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   Peter Moulder <pmoulder@mail.csse.monash.edu.au>
+ *
+ * Copyright (C) 1990 Philip J. Schneider
+ * Copyright (C) 2001 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ * Copyright (C) 2003,2004 Monash University
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#define SP_HUGE 1e5
+#define noBEZIER_DEBUG
+
+#ifdef HAVE_IEEEFP_H
+# include <ieefp.h>
+#endif
+
+#include "bezier-utils.h"
+
+#include "isnan.h"
+#include <assert.h>
+
+namespace Geom{
+
+typedef Point BezierCurve[];
+
+/* Forward declarations */
+static void generate_bezier(Point b[], Point const d[], double const u[], unsigned len,
+                            Point const &tHat1, Point const &tHat2, double tolerance_sq);
+static void estimate_lengths(Point bezier[],
+                             Point const data[], double const u[], unsigned len,
+                             Point const &tHat1, Point const &tHat2);
+static void estimate_bi(Point b[4], unsigned ei,
+                        Point const data[], double const u[], unsigned len);
+static void reparameterize(Point const d[], unsigned len, double u[], BezierCurve const bezCurve);
+static double NewtonRaphsonRootFind(BezierCurve const Q, Point const &P, double u);
+static Point darray_center_tangent(Point const d[], unsigned center, unsigned length);
+static Point darray_right_tangent(Point const d[], unsigned const len);
+static unsigned copy_without_nans_or_adjacent_duplicates(Point const src[], unsigned src_len, Point dest[]);
+static void chord_length_parameterize(Point const d[], double u[], unsigned len);
+static double compute_max_error_ratio(Point const d[], double const u[], unsigned len,
+                                      BezierCurve const bezCurve, double tolerance,
+                                      unsigned *splitPoint);
+static double compute_hook(Point const &a, Point const &b, double const u, BezierCurve const bezCurve,
+                           double const tolerance);
+
+
+static Point const unconstrained_tangent(0, 0);
+
+
+/*
+ *  B0, B1, B2, B3 : Bezier multipliers
+ */
+
+#define B0(u) ( ( 1.0 - u )  *  ( 1.0 - u )  *  ( 1.0 - u ) )
+#define B1(u) ( 3 * u  *  ( 1.0 - u )  *  ( 1.0 - u ) )
+#define B2(u) ( 3 * u * u  *  ( 1.0 - u ) )
+#define B3(u) ( u * u * u )
+
+#ifdef BEZIER_DEBUG
+# define DOUBLE_ASSERT(x) assert( ( (x) > -SP_HUGE ) && ( (x) < SP_HUGE ) )
+# define BEZIER_ASSERT(b) do { \
+           DOUBLE_ASSERT((b)[0][X]); DOUBLE_ASSERT((b)[0][Y]);  \
+           DOUBLE_ASSERT((b)[1][X]); DOUBLE_ASSERT((b)[1][Y]);  \
+           DOUBLE_ASSERT((b)[2][X]); DOUBLE_ASSERT((b)[2][Y]);  \
+           DOUBLE_ASSERT((b)[3][X]); DOUBLE_ASSERT((b)[3][Y]);  \
+         } while(0)
+#else
+# define DOUBLE_ASSERT(x) do { } while(0)
+# define BEZIER_ASSERT(b) do { } while(0)
+#endif
+
+
+/**
+ * Fit a single-segment Bezier curve to a set of digitized points.
+ *
+ * \return Number of segments generated, or -1 on error.
+ */
+int
+bezier_fit_cubic(Point *bezier, Point const *data, int len, double error)
+{
+    return bezier_fit_cubic_r(bezier, data, len, error, 1);
+}
+
+/**
+ * Fit a multi-segment Bezier curve to a set of digitized points, with
+ * possible weedout of identical points and NaNs.
+ *
+ * \param max_beziers Maximum number of generated segments
+ * \param Result array, must be large enough for n. segments * 4 elements.
+ *
+ * \return Number of segments generated, or -1 on error.
+ */
+int
+bezier_fit_cubic_r(Point bezier[], Point const data[], int const len, double const error, unsigned const max_beziers)
+{
+    if(bezier == NULL || 
+       data == NULL || 
+       len <= 0 || 
+       max_beziers >= (1ul << (31 - 2 - 1 - 3))) 
+        return -1;
+    
+    Point *uniqued_data = new Point[len];
+    unsigned uniqued_len = copy_without_nans_or_adjacent_duplicates(data, len, uniqued_data);
+
+    if ( uniqued_len < 2 ) {
+        delete[] uniqued_data;
+        return 0;
+    }
+
+    /* Call fit-cubic function with recursion. */
+    int const ret = bezier_fit_cubic_full(bezier, NULL, uniqued_data, uniqued_len,
+                                          unconstrained_tangent, unconstrained_tangent,
+                                          error, max_beziers);
+    delete[] uniqued_data;
+    return ret;
+}
+
+/** 
+ * Copy points from src to dest, filter out points containing NaN and
+ * adjacent points with equal x and y.
+ * \return length of dest
+ */
+static unsigned
+copy_without_nans_or_adjacent_duplicates(Point const src[], unsigned src_len, Point dest[])
+{
+    unsigned si = 0;
+    for (;;) {
+        if ( si == src_len ) {
+            return 0;
+        }
+        if (!is_nan(src[si][X]) &&
+            !is_nan(src[si][Y])) {
+            dest[0] = Point(src[si]);
+            ++si;
+            break;
+        }
+    }
+    unsigned di = 0;
+    for (; si < src_len; ++si) {
+        Point const src_pt = Point(src[si]);
+        if ( src_pt != dest[di]
+             && !is_nan(src_pt[X])
+             && !is_nan(src_pt[Y])) {
+            dest[++di] = src_pt;
+        }
+    }
+    unsigned dest_len = di + 1;
+    assert( dest_len <= src_len );
+    return dest_len;
+}
+
+/**
+ * Fit a multi-segment Bezier curve to a set of digitized points, without
+ * possible weedout of identical points and NaNs.
+ * 
+ * \pre data is uniqued, i.e. not exist i: data[i] == data[i + 1].
+ * \param max_beziers Maximum number of generated segments
+ * \param Result array, must be large enough for n. segments * 4 elements.
+ */
+int
+bezier_fit_cubic_full(Point bezier[], int split_points[],
+                      Point const data[], int const len,
+                      Point const &tHat1, Point const &tHat2,
+                      double const error, unsigned const max_beziers)
+{
+    int const maxIterations = 4;   /* std::max times to try iterating */
+    
+    if(!(bezier != NULL) ||
+       !(data != NULL) ||
+       !(len > 0) ||
+       !(max_beziers >= 1) ||
+       !(error >= 0.0))
+        return -1;
+
+    if ( len < 2 ) return 0;
+
+    if ( len == 2 ) {
+        /* We have 2 points, which can be fitted trivially. */
+        bezier[0] = data[0];
+        bezier[3] = data[len - 1];
+        double const dist = distance(bezier[0], bezier[3]) / 3.0;
+        if (is_nan(dist)) {
+            /* Numerical problem, fall back to straight line segment. */
+            bezier[1] = bezier[0];
+            bezier[2] = bezier[3];
+        } else {
+            bezier[1] = ( is_zero(tHat1)
+                          ? ( 2 * bezier[0] + bezier[3] ) / 3.
+                          : bezier[0] + dist * tHat1 );
+            bezier[2] = ( is_zero(tHat2)
+                          ? ( bezier[0] + 2 * bezier[3] ) / 3.
+                          : bezier[3] + dist * tHat2 );
+        }
+        BEZIER_ASSERT(bezier);
+        return 1;
+    }
+
+    /*  Parameterize points, and attempt to fit curve */
+    unsigned splitPoint;   /* Point to split point set at. */
+    bool is_corner;
+    {
+        double *u = new double[len];
+        chord_length_parameterize(data, u, len);
+        if ( u[len - 1] == 0.0 ) {
+            /* Zero-length path: every point in data[] is the same.
+             *
+             * (Clients aren't allowed to pass such data; handling the case is defensive
+             * programming.)
+             */
+            delete[] u;
+            return 0;
+        }
+
+        generate_bezier(bezier, data, u, len, tHat1, tHat2, error);
+        reparameterize(data, len, u, bezier);
+
+        /* Find max deviation of points to fitted curve. */
+        double const tolerance = sqrt(error + 1e-9);
+        double maxErrorRatio = compute_max_error_ratio(data, u, len, bezier, tolerance, &splitPoint);
+
+        if ( fabs(maxErrorRatio) <= 1.0 ) {
+            BEZIER_ASSERT(bezier);
+            delete[] u;
+            return 1;
+        }
+
+        /* If error not too large, then try some reparameterization and iteration. */
+        if ( 0.0 <= maxErrorRatio && maxErrorRatio <= 3.0 ) {
+            for (int i = 0; i < maxIterations; i++) {
+                generate_bezier(bezier, data, u, len, tHat1, tHat2, error);
+                reparameterize(data, len, u, bezier);
+                maxErrorRatio = compute_max_error_ratio(data, u, len, bezier, tolerance, &splitPoint);
+                if ( fabs(maxErrorRatio) <= 1.0 ) {
+                    BEZIER_ASSERT(bezier);
+                    delete[] u;
+                    return 1;
+                }
+            }
+        }
+        delete[] u;
+        is_corner = (maxErrorRatio < 0);
+    }
+
+    if (is_corner) {
+        assert(splitPoint < unsigned(len));
+        if (splitPoint == 0) {
+            if (is_zero(tHat1)) {
+                /* Got spike even with unconstrained initial tangent. */
+                ++splitPoint;
+            } else {
+                return bezier_fit_cubic_full(bezier, split_points, data, len, unconstrained_tangent, tHat2,
+                                                error, max_beziers);
+            }
+        } else if (splitPoint == unsigned(len - 1)) {
+            if (is_zero(tHat2)) {
+                /* Got spike even with unconstrained final tangent. */
+                --splitPoint;
+            } else {
+                return bezier_fit_cubic_full(bezier, split_points, data, len, tHat1, unconstrained_tangent,
+                                                error, max_beziers);
+            }
+        }
+    }
+
+    if ( 1 < max_beziers ) {
+        /*
+         *  Fitting failed -- split at max error point and fit recursively
+         */
+        unsigned const rec_max_beziers1 = max_beziers - 1;
+
+        Point recTHat2, recTHat1;
+        if (is_corner) {
+            if(!(0 < splitPoint && splitPoint < unsigned(len - 1)))
+               return -1;
+            recTHat1 = recTHat2 = unconstrained_tangent;
+        } else {
+            /* Unit tangent vector at splitPoint. */
+            recTHat2 = darray_center_tangent(data, splitPoint, len);
+            recTHat1 = -recTHat2;
+        }
+        int const nsegs1 = bezier_fit_cubic_full(bezier, split_points, data, splitPoint + 1,
+                                                     tHat1, recTHat2, error, rec_max_beziers1);
+        if ( nsegs1 < 0 ) {
+#ifdef BEZIER_DEBUG
+            g_print("fit_cubic[1]: recursive call failed\n");
+#endif
+            return -1;
+        }
+        assert( nsegs1 != 0 );
+        if (split_points != NULL) {
+            split_points[nsegs1 - 1] = splitPoint;
+        }
+        unsigned const rec_max_beziers2 = max_beziers - nsegs1;
+        int const nsegs2 = bezier_fit_cubic_full(bezier + nsegs1*4,
+                                                     ( split_points == NULL
+                                                       ? NULL
+                                                       : split_points + nsegs1 ),
+                                                     data + splitPoint, len - splitPoint,
+                                                     recTHat1, tHat2, error, rec_max_beziers2);
+        if ( nsegs2 < 0 ) {
+#ifdef BEZIER_DEBUG
+            g_print("fit_cubic[2]: recursive call failed\n");
+#endif
+            return -1;
+        }
+
+#ifdef BEZIER_DEBUG
+        g_print("fit_cubic: success[nsegs: %d+%d=%d] on max_beziers:%u\n",
+                nsegs1, nsegs2, nsegs1 + nsegs2, max_beziers);
+#endif
+        return nsegs1 + nsegs2;
+    } else {
+        return -1;
+    }
+}
+
+
+/**
+ * Fill in \a bezier[] based on the given data and tangent requirements, using
+ * a least-squares fit.
+ *
+ * Each of tHat1 and tHat2 should be either a zero vector or a unit vector.
+ * If it is zero, then bezier[1 or 2] is estimated without constraint; otherwise,
+ * it bezier[1 or 2] is placed in the specified direction from bezier[0 or 3].
+ *
+ * \param tolerance_sq Used only for an initial guess as to tangent directions
+ *   when \a tHat1 or \a tHat2 is zero.
+ */
+static void
+generate_bezier(Point bezier[],
+                Point const data[], double const u[], unsigned const len,
+                Point const &tHat1, Point const &tHat2,
+                double const tolerance_sq)
+{
+    bool const est1 = is_zero(tHat1);
+    bool const est2 = is_zero(tHat2);
+    Point est_tHat1( est1
+                         ? darray_left_tangent(data, len, tolerance_sq)
+                         : tHat1 );
+    Point est_tHat2( est2
+                         ? darray_right_tangent(data, len, tolerance_sq)
+                         : tHat2 );
+    estimate_lengths(bezier, data, u, len, est_tHat1, est_tHat2);
+    /* We find that darray_right_tangent tends to produce better results
+       for our current freehand tool than full estimation. */
+    if (est1) {
+        estimate_bi(bezier, 1, data, u, len);
+        if (bezier[1] != bezier[0]) {
+            est_tHat1 = unit_vector(bezier[1] - bezier[0]);
+        }
+        estimate_lengths(bezier, data, u, len, est_tHat1, est_tHat2);
+    }
+}
+
+
+static void
+estimate_lengths(Point bezier[],
+                 Point const data[], double const uPrime[], unsigned const len,
+                 Point const &tHat1, Point const &tHat2)
+{
+    double C[2][2];   /* Matrix C. */
+    double X[2];      /* Matrix X. */
+
+    /* Create the C and X matrices. */
+    C[0][0] = 0.0;
+    C[0][1] = 0.0;
+    C[1][0] = 0.0;
+    C[1][1] = 0.0;
+    X[0]    = 0.0;
+    X[1]    = 0.0;
+
+    /* First and last control points of the Bezier curve are positioned exactly at the first and
+       last data points. */
+    bezier[0] = data[0];
+    bezier[3] = data[len - 1];
+
+    for (unsigned i = 0; i < len; i++) {
+        /* Bezier control point coefficients. */
+        double const b0 = B0(uPrime[i]);
+        double const b1 = B1(uPrime[i]);
+        double const b2 = B2(uPrime[i]);
+        double const b3 = B3(uPrime[i]);
+
+        /* rhs for eqn */
+        Point const a1 = b1 * tHat1;
+        Point const a2 = b2 * tHat2;
+
+        C[0][0] += dot(a1, a1);
+        C[0][1] += dot(a1, a2);
+        C[1][0] = C[0][1];
+        C[1][1] += dot(a2, a2);
+
+        /* Additional offset to the data point from the predicted point if we were to set bezier[1]
+           to bezier[0] and bezier[2] to bezier[3]. */
+        Point const shortfall
+            = ( data[i]
+                - ( ( b0 + b1 ) * bezier[0] )
+                - ( ( b2 + b3 ) * bezier[3] ) );
+        X[0] += dot(a1, shortfall);
+        X[1] += dot(a2, shortfall);
+    }
+
+    /* We've constructed a pair of equations in the form of a matrix product C * alpha = X.
+       Now solve for alpha. */
+    double alpha_l, alpha_r;
+
+    /* Compute the determinants of C and X. */
+    double const det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
+    if ( det_C0_C1 != 0 ) {
+        /* Apparently Kramer's rule. */
+        double const det_C0_X  = C[0][0] * X[1]    - C[0][1] * X[0];
+        double const det_X_C1  = X[0]    * C[1][1] - X[1]    * C[0][1];
+        alpha_l = det_X_C1 / det_C0_C1;
+        alpha_r = det_C0_X / det_C0_C1;
+    } else {
+        /* The matrix is under-determined.  Try requiring alpha_l == alpha_r.
+         *
+         * One way of implementing the constraint alpha_l == alpha_r is to treat them as the same
+         * variable in the equations.  We can do this by adding the columns of C to form a single
+         * column, to be multiplied by alpha to give the column vector X.
+         *
+         * We try each row in turn.
+         */
+        double const c0 = C[0][0] + C[0][1];
+        if (c0 != 0) {
+            alpha_l = alpha_r = X[0] / c0;
+        } else {
+            double const c1 = C[1][0] + C[1][1];
+            if (c1 != 0) {
+                alpha_l = alpha_r = X[1] / c1;
+            } else {
+                /* Let the below code handle this. */
+                alpha_l = alpha_r = 0.;
+            }
+        }
+    }
+
+    /* If alpha negative, use the Wu/Barsky heuristic (see text).  (If alpha is 0, you get
+       coincident control points that lead to divide by zero in any subsequent
+       NewtonRaphsonRootFind() call.) */
+    /// \todo Check whether this special-casing is necessary now that 
+    /// NewtonRaphsonRootFind handles non-positive denominator.
+    if ( alpha_l < 1.0e-6 ||
+         alpha_r < 1.0e-6   )
+    {
+        alpha_l = alpha_r = distance(data[0], data[len-1]) / 3.0;
+    }
+
+    /* Control points 1 and 2 are positioned an alpha distance out on the tangent vectors, left and
+       right, respectively. */
+    bezier[1] = alpha_l * tHat1 + bezier[0];
+    bezier[2] = alpha_r * tHat2 + bezier[3];
+
+    return;
+}
+
+static double lensq(Point const p) {
+    return dot(p, p);
+}
+
+static void
+estimate_bi(Point bezier[4], unsigned const ei,
+            Point const data[], double const u[], unsigned const len)
+{
+    if(!(1 <= ei && ei <= 2))
+        return;
+    unsigned const oi = 3 - ei;
+    double num[2] = {0., 0.};
+    double den = 0.;
+    for (unsigned i = 0; i < len; ++i) {
+        double const ui = u[i];
+        double const b[4] = {
+            B0(ui),
+            B1(ui),
+            B2(ui),
+            B3(ui)
+        };
+
+        for (unsigned d = 0; d < 2; ++d) {
+            num[d] += b[ei] * (b[0]  * bezier[0][d] +
+                               b[oi] * bezier[oi][d] +
+                               b[3]  * bezier[3][d] +
+                               - data[i][d]);
+        }
+        den -= b[ei] * b[ei];
+    }
+
+    if (den != 0.) {
+        for (unsigned d = 0; d < 2; ++d) {
+            bezier[ei][d] = num[d] / den;
+        }
+    } else {
+        bezier[ei] = ( oi * bezier[0] + ei * bezier[3] ) / 3.;
+    }
+}
+
+/**
+ * Given set of points and their parameterization, try to find a better assignment of parameter
+ * values for the points.
+ *
+ *  \param d  Array of digitized points.
+ *  \param u  Current parameter values.
+ *  \param bezCurve  Current fitted curve.
+ *  \param len  Number of values in both d and u arrays.
+ *              Also the size of the array that is allocated for return.
+ */
+static void
+reparameterize(Point const d[],
+               unsigned const len,
+               double u[],
+               BezierCurve const bezCurve)
+{
+    assert( 2 <= len );
+
+    unsigned const last = len - 1;
+    assert( bezCurve[0] == d[0] );
+    assert( bezCurve[3] == d[last] );
+    assert( u[0] == 0.0 );
+    assert( u[last] == 1.0 );
+    /* Otherwise, consider including 0 and last in the below loop. */
+
+    for (unsigned i = 1; i < last; i++) {
+        u[i] = NewtonRaphsonRootFind(bezCurve, d[i], u[i]);
+    }
+}
+
+/**
+ *  Use Newton-Raphson iteration to find better root.
+ *  
+ *  \param Q  Current fitted curve
+ *  \param P  Digitized point
+ *  \param u  Parameter value for "P"
+ *  
+ *  \return Improved u
+ */
+static double
+NewtonRaphsonRootFind(BezierCurve const Q, Point const &P, double const u)
+{
+    assert( 0.0 <= u );
+    assert( u <= 1.0 );
+
+    /* Generate control vertices for Q'. */
+    Point Q1[3];
+    for (unsigned i = 0; i < 3; i++) {
+        Q1[i] = 3.0 * ( Q[i+1] - Q[i] );
+    }
+
+    /* Generate control vertices for Q''. */
+    Point Q2[2];
+    for (unsigned i = 0; i < 2; i++) {
+        Q2[i] = 2.0 * ( Q1[i+1] - Q1[i] );
+    }
+
+    /* Compute Q(u), Q'(u) and Q''(u). */
+    Point const Q_u  = bezier_pt(3, Q, u);
+    Point const Q1_u = bezier_pt(2, Q1, u);
+    Point const Q2_u = bezier_pt(1, Q2, u);
+
+    /* Compute f(u)/f'(u), where f is the derivative wrt u of distsq(u) = 0.5 * the square of the
+       distance from P to Q(u).  Here we're using Newton-Raphson to find a stationary point in the
+       distsq(u), hopefully corresponding to a local minimum in distsq (and hence a local minimum
+       distance from P to Q(u)). */
+    Point const diff = Q_u - P;
+    double numerator = dot(diff, Q1_u);
+    double denominator = dot(Q1_u, Q1_u) + dot(diff, Q2_u);
+
+    double improved_u;
+    if ( denominator > 0. ) {
+        /* One iteration of Newton-Raphson:
+           improved_u = u - f(u)/f'(u) */
+        improved_u = u - ( numerator / denominator );
+    } else {
+        /* Using Newton-Raphson would move in the wrong direction (towards a local maximum rather
+           than local minimum), so we move an arbitrary amount in the right direction. */
+        if ( numerator > 0. ) {
+            improved_u = u * .98 - .01;
+        } else if ( numerator < 0. ) {
+            /* Deliberately asymmetrical, to reduce the chance of cycling. */
+            improved_u = .031 + u * .98;
+        } else {
+            improved_u = u;
+        }
+    }
+
+    if (!is_finite(improved_u)) {
+        improved_u = u;
+    } else if ( improved_u < 0.0 ) {
+        improved_u = 0.0;
+    } else if ( improved_u > 1.0 ) {
+        improved_u = 1.0;
+    }
+
+    /* Ensure that improved_u isn't actually worse. */
+    {
+        double const diff_lensq = lensq(diff);
+        for (double proportion = .125; ; proportion += .125) {
+            if ( lensq( bezier_pt(3, Q, improved_u) - P ) > diff_lensq ) {
+                if ( proportion > 1.0 ) {
+                    //g_warning("found proportion %g", proportion);
+                    improved_u = u;
+                    break;
+                }
+                improved_u = ( ( 1 - proportion ) * improved_u  +
+                               proportion         * u            );
+            } else {
+                break;
+            }
+        }
+    }
+
+    DOUBLE_ASSERT(improved_u);
+    return improved_u;
+}
+
+/** 
+ * Evaluate a Bezier curve at parameter value \a t.
+ * 
+ * \param degree The degree of the Bezier curve: 3 for cubic, 2 for quadratic etc. Must be less
+ *    than 4.
+ * \param V The control points for the Bezier curve.  Must have (\a degree+1)
+ *    elements.
+ * \param t The "parameter" value, specifying whereabouts along the curve to
+ *    evaluate.  Typically in the range [0.0, 1.0].
+ *
+ * Let s = 1 - t.
+ * BezierII(1, V) gives (s, t) * V, i.e. t of the way
+ * from V[0] to V[1].
+ * BezierII(2, V) gives (s**2, 2*s*t, t**2) * V.
+ * BezierII(3, V) gives (s**3, 3 s**2 t, 3s t**2, t**3) * V.
+ *
+ * The derivative of BezierII(i, V) with respect to t
+ * is i * BezierII(i-1, V'), where for all j, V'[j] =
+ * V[j + 1] - V[j].
+ */
+Point
+bezier_pt(unsigned const degree, Point const V[], double const t)
+{
+    /** Pascal's triangle. */
+    static int const pascal[4][4] = {{1},
+                                     {1, 1},
+                                     {1, 2, 1},
+                                     {1, 3, 3, 1}};
+    assert( degree < 4);
+    double const s = 1.0 - t;
+
+    /* Calculate powers of t and s. */
+    double spow[4];
+    double tpow[4];
+    spow[0] = 1.0; spow[1] = s;
+    tpow[0] = 1.0; tpow[1] = t;
+    for (unsigned i = 1; i < degree; ++i) {
+        spow[i + 1] = spow[i] * s;
+        tpow[i + 1] = tpow[i] * t;
+    }
+
+    Point ret = spow[degree] * V[0];
+    for (unsigned i = 1; i <= degree; ++i) {
+        ret += pascal[degree][i] * spow[degree - i] * tpow[i] * V[i];
+    }
+    return ret;
+}
+
+/*
+ * ComputeLeftTangent, ComputeRightTangent, ComputeCenterTangent :
+ * Approximate unit tangents at endpoints and "center" of digitized curve
+ */
+
+/** 
+ * Estimate the (forward) tangent at point d[first + 0.5].
+ *
+ * Unlike the center and right versions, this calculates the tangent in 
+ * the way one might expect, i.e., wrt increasing index into d.
+ * \pre (2 \<= len) and (d[0] != d[1]).
+ **/
+Point
+darray_left_tangent(Point const d[], unsigned const len)
+{
+    assert( len >= 2 );
+    assert( d[0] != d[1] );
+    return unit_vector( d[1] - d[0] );
+}
+
+/** 
+ * Estimates the (backward) tangent at d[last - 0.5].
+ *
+ * \note The tangent is "backwards", i.e. it is with respect to 
+ * decreasing index rather than increasing index.
+ *
+ * \pre 2 \<= len.
+ * \pre d[len - 1] != d[len - 2].
+ * \pre all[p in d] in_svg_plane(p).
+ */
+static Point
+darray_right_tangent(Point const d[], unsigned const len)
+{
+    assert( 2 <= len );
+    unsigned const last = len - 1;
+    unsigned const prev = last - 1;
+    assert( d[last] != d[prev] );
+    return unit_vector( d[prev] - d[last] );
+}
+
+/** 
+ * Estimate the (forward) tangent at point d[0].
+ *
+ * Unlike the center and right versions, this calculates the tangent in 
+ * the way one might expect, i.e., wrt increasing index into d.
+ *
+ * \pre 2 \<= len.
+ * \pre d[0] != d[1].
+ * \pre all[p in d] in_svg_plane(p).
+ * \post is_unit_vector(ret).
+ **/
+Point
+darray_left_tangent(Point const d[], unsigned const len, double const tolerance_sq)
+{
+    assert( 2 <= len );
+    assert( 0 <= tolerance_sq );
+    for (unsigned i = 1;;) {
+        Point const pi(d[i]);
+        Point const t(pi - d[0]);
+        double const distsq = dot(t, t);
+        if ( tolerance_sq < distsq ) {
+            return unit_vector(t);
+        }
+        ++i;
+        if (i == len) {
+            return ( distsq == 0
+                     ? darray_left_tangent(d, len)
+                     : unit_vector(t) );
+        }
+    }
+}
+
+/** 
+ * Estimates the (backward) tangent at d[last].
+ *
+ * \note The tangent is "backwards", i.e. it is with respect to 
+ * decreasing index rather than increasing index.
+ *
+ * \pre 2 \<= len.
+ * \pre d[len - 1] != d[len - 2].
+ * \pre all[p in d] in_svg_plane(p).
+ */
+Point
+darray_right_tangent(Point const d[], unsigned const len, double const tolerance_sq)
+{
+    assert( 2 <= len );
+    assert( 0 <= tolerance_sq );
+    unsigned const last = len - 1;
+    for (unsigned i = last - 1;; i--) {
+        Point const pi(d[i]);
+        Point const t(pi - d[last]);
+        double const distsq = dot(t, t);
+        if ( tolerance_sq < distsq ) {
+            return unit_vector(t);
+        }
+        if (i == 0) {
+            return ( distsq == 0
+                     ? darray_right_tangent(d, len)
+                     : unit_vector(t) );
+        }
+    }
+}
+
+/** 
+ * Estimates the (backward) tangent at d[center], by averaging the two 
+ * segments connected to d[center] (and then normalizing the result).
+ *
+ * \note The tangent is "backwards", i.e. it is with respect to 
+ * decreasing index rather than increasing index.
+ *
+ * \pre (0 \< center \< len - 1) and d is uniqued (at least in 
+ * the immediate vicinity of \a center).
+ */
+static Point
+darray_center_tangent(Point const d[],
+                         unsigned const center,
+                         unsigned const len)
+{
+    assert( center != 0 );
+    assert( center < len - 1 );
+
+    Point ret;
+    if ( d[center + 1] == d[center - 1] ) {
+        /* Rotate 90 degrees in an arbitrary direction. */
+        Point const diff = d[center] - d[center - 1];
+        ret = rot90(diff);
+    } else {
+        ret = d[center - 1] - d[center + 1];
+    }
+    ret.normalize();
+    return ret;
+}
+
+
+/**
+ *  Assign parameter values to digitized points using relative distances between points.
+ *
+ *  \pre Parameter array u must have space for \a len items.
+ */
+static void
+chord_length_parameterize(Point const d[], double u[], unsigned const len)
+{
+    if(!( 2 <= len ))
+        return;
+
+    /* First let u[i] equal the distance travelled along the path from d[0] to d[i]. */
+    u[0] = 0.0;
+    for (unsigned i = 1; i < len; i++) {
+        double const dist = distance(d[i], d[i-1]);
+        u[i] = u[i-1] + dist;
+    }
+
+    /* Then scale to [0.0 .. 1.0]. */
+    double tot_len = u[len - 1];
+    if(!( tot_len != 0 ))
+        return;
+    if (is_finite(tot_len)) {
+        for (unsigned i = 1; i < len; ++i) {
+            u[i] /= tot_len;
+        }
+    } else {
+        /* We could do better, but this probably never happens anyway. */
+        for (unsigned i = 1; i < len; ++i) {
+            u[i] = i / (double) ( len - 1 );
+        }
+    }
+
+    /** \todo
+     * It's been reported that u[len - 1] can differ from 1.0 on some 
+     * systems (amd64), despite it having been calculated as x / x where x 
+     * is isFinite and non-zero.
+     */
+    if (u[len - 1] != 1) {
+        double const diff = u[len - 1] - 1;
+        if (fabs(diff) > 1e-13) {
+            assert(0); // No warnings in 2geom
+            //g_warning("u[len - 1] = %19g (= 1 + %19g), expecting exactly 1",
+            //          u[len - 1], diff);
+        }
+        u[len - 1] = 1;
+    }
+
+#ifdef BEZIER_DEBUG
+    assert( u[0] == 0.0 && u[len - 1] == 1.0 );
+    for (unsigned i = 1; i < len; i++) {
+        assert( u[i] >= u[i-1] );
+    }
+#endif
+}
+
+
+
+
+/**
+ * Find the maximum squared distance of digitized points to fitted curve, and (if this maximum
+ * error is non-zero) set \a *splitPoint to the corresponding index.
+ *
+ * \pre 2 \<= len.
+ * \pre u[0] == 0.
+ * \pre u[len - 1] == 1.0.
+ * \post ((ret == 0.0)
+ *        || ((*splitPoint \< len - 1)
+ *            \&\& (*splitPoint != 0 || ret \< 0.0))).
+ */
+static double
+compute_max_error_ratio(Point const d[], double const u[], unsigned const len,
+                        BezierCurve const bezCurve, double const tolerance,
+                        unsigned *const splitPoint)
+{
+    assert( 2 <= len );
+    unsigned const last = len - 1;
+    assert( bezCurve[0] == d[0] );
+    assert( bezCurve[3] == d[last] );
+    assert( u[0] == 0.0 );
+    assert( u[last] == 1.0 );
+    /* I.e. assert that the error for the first & last points is zero.
+     * Otherwise we should include those points in the below loop.
+     * The assertion is also necessary to ensure 0 < splitPoint < last.
+     */
+
+    double maxDistsq = 0.0; /* Maximum error */
+    double max_hook_ratio = 0.0;
+    unsigned snap_end = 0;
+    Point prev = bezCurve[0];
+    for (unsigned i = 1; i <= last; i++) {
+        Point const curr = bezier_pt(3, bezCurve, u[i]);
+        double const distsq = lensq( curr - d[i] );
+        if ( distsq > maxDistsq ) {
+            maxDistsq = distsq;
+            *splitPoint = i;
+        }
+        double const hook_ratio = compute_hook(prev, curr, .5 * (u[i - 1] + u[i]), bezCurve, tolerance);
+        if (max_hook_ratio < hook_ratio) {
+            max_hook_ratio = hook_ratio;
+            snap_end = i;
+        }
+        prev = curr;
+    }
+
+    double const dist_ratio = sqrt(maxDistsq) / tolerance;
+    double ret;
+    if (max_hook_ratio <= dist_ratio) {
+        ret = dist_ratio;
+    } else {
+        assert(0 < snap_end);
+        ret = -max_hook_ratio;
+        *splitPoint = snap_end - 1;
+    }
+    assert( ret == 0.0
+              || ( ( *splitPoint < last )
+                   && ( *splitPoint != 0 || ret < 0. ) ) );
+    return ret;
+}
+
+/** 
+ * Whereas compute_max_error_ratio() checks for itself that each data point 
+ * is near some point on the curve, this function checks that each point on 
+ * the curve is near some data point (or near some point on the polyline 
+ * defined by the data points, or something like that: we allow for a
+ * "reasonable curviness" from such a polyline).  "Reasonable curviness" 
+ * means we draw a circle centred at the midpoint of a..b, of radius 
+ * proportional to the length |a - b|, and require that each point on the 
+ * segment of bezCurve between the parameters of a and b be within that circle.
+ * If any point P on the bezCurve segment is outside of that allowable 
+ * region (circle), then we return some metric that increases with the 
+ * distance from P to the circle.
+ *
+ *  Given that this is a fairly arbitrary criterion for finding appropriate 
+ *  places for sharp corners, we test only one point on bezCurve, namely 
+ *  the point on bezCurve with parameter halfway between our estimated 
+ *  parameters for a and b.  (Alternatives are taking the farthest of a
+ *  few parameters between those of a and b, or even using a variant of 
+ *  NewtonRaphsonFindRoot() for finding the maximum rather than minimum 
+ *  distance.)
+ */
+static double
+compute_hook(Point const &a, Point const &b, double const u, BezierCurve const bezCurve,
+             double const tolerance)
+{
+    Point const P = bezier_pt(3, bezCurve, u);
+    double const dist = distance((a+b)*.5, P);
+    if (dist < tolerance) {
+        return 0;
+    }
+    double const allowed = distance(a, b) + tolerance;
+    return dist / allowed;
+    /** \todo 
+     * effic: Hooks are very rare.  We could start by comparing 
+     * distsq, only resorting to the more expensive L2 in cases of 
+     * uncertainty.
+     */
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/bezier-utils.h b/src/2geom/bezier-utils.h
new file mode 100644 (file)
index 0000000..fba4333
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef __SP_BEZIER_UTILS_H__
+#define __SP_BEZIER_UTILS_H__
+
+/*
+ * An Algorithm for Automatically Fitting Digitized Curves
+ * by Philip J. Schneider
+ * from "Graphics Gems", Academic Press, 1990
+ *
+ * Authors:
+ *   Philip J. Schneider
+ *   Lauris Kaplinski <lauris@ximian.com>
+ *
+ * Copyright (C) 1990 Philip J. Schneider
+ * Copyright (C) 2001 Lauris Kaplinski and Ximian, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "point.h"
+
+namespace Geom{
+
+/* Bezier approximation utils */
+Point bezier_pt(unsigned degree, Point const V[], double t);
+
+int bezier_fit_cubic(Point bezier[], Point const data[], int len, double error);
+
+int bezier_fit_cubic_r(Point bezier[], Point const data[], int len, double error,
+                           unsigned max_beziers);
+
+int bezier_fit_cubic_full(Point bezier[], int split_points[], Point const data[], int len,
+                              Point const &tHat1, Point const &tHat2,
+                              double error, unsigned max_beziers);
+
+Point darray_left_tangent(Point const d[], unsigned const len);
+Point darray_left_tangent(Point const d[], unsigned const len, double const tolerance_sq);
+Point darray_right_tangent(Point const d[], unsigned const length, double const tolerance_sq);
+
+template <typename iterator>
+static void
+cubic_bezier_poly_coeff(iterator b, Point *pc) {
+       double c[10] = {1, 
+                       -3, 3, 
+                       3, -6, 3,
+                       -1, 3, -3, 1};
+
+       int cp = 0;
+
+       for(int i = 0; i < 4; i++) {
+               pc[i] = Point(0,0);
+               ++b;
+       }
+       for(int i = 0; i < 4; i++) {
+               --b;
+               for(int j = 0; j <= i; j++) {
+                       pc[3 - j] += c[cp]*(*b);
+                       cp++;
+               }
+       }
+}
+
+}
+#endif /* __SP_BEZIER_UTILS_H__ */
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/bezier.h b/src/2geom/bezier.h
new file mode 100644 (file)
index 0000000..7949e00
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * bezier.h
+ *
+ * Copyright 2007  MenTaLguY <mental@rydia.net>
+ * Copyright 2007  Michael Sloan <mgsloan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef SEEN_BEZIER_H
+#define SEEN_BEZIER_H
+
+#include "coord.h"
+#include "isnan.h"
+#include "bezier-to-sbasis.h"
+
+namespace Geom {
+
+template <unsigned order>
+class Bezier {
+private:
+  Coord c_[order+1];
+
+protected:
+  Bezier(Coord c[]) {
+    std::copy(c, c+order+1, c_);
+  }
+
+public:
+  template <unsigned required_order>
+  static void assert_order(Bezier<required_order> const *) {}
+
+  Bezier() {}
+
+  //Construct an order-0 bezier (constant Bézier)
+  explicit Bezier(Coord c0) {
+    assert_order<0>(this);
+    c_[0] = c0;
+  }
+
+  //Construct an order-1 bezier (linear Bézier)
+  Bezier(Coord c0, Coord c1) {
+    assert_order<1>(this);
+    c_[0] = c0; c_[1] = c1;
+  }
+
+  //Construct an order-2 bezier (quadratic Bézier)
+  Bezier(Coord c0, Coord c1, Coord c2) {
+    assert_order<2>(this);
+    c_[0] = c0; c_[1] = c1; c_[2] = c2;
+  }
+
+  //Construct an order-3 bezier (cubic Bézier)
+  Bezier(Coord c0, Coord c1, Coord c2, Coord c3) {
+    assert_order<3>(this);
+    c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3;
+  }
+
+  inline unsigned degree() const { return order; }
+
+  //IMPL: FragmentConcept
+  typedef Coord output_type;
+  inline bool isZero() const { 
+     for(int i = 0; i <= order; i++) {
+       if(c_[i] != 0) return false;
+     }
+     return true;
+  }
+  inline bool isFinite() const {
+    for(int i = 0; i <= order; i++) {
+      if(!is_finite(c_[i])) return false;
+    }
+    return true;
+  }
+  inline Coord at0() const { return c_[0]; }
+  inline Coord at1() const { return c_[order]; }
+
+  inline SBasis toSBasis() const { return bezier_to_sbasis<order>(c_); }
+
+  inline Interval bounds_fast() const { return Interval::fromArray(c_, order+1); }
+  //TODO: better bounds exact
+  inline Interval bounds_exact() const { return toSBasis().bounds_exact(); }
+  inline Interval bounds_local(double u, double v) const { return toSBasis().bounds_local(u, v); }
+
+  //Only mutator
+  inline Coord &operator[](int index) { return c_[index]; }
+  inline Coord const &operator[](int index) const { return c_[index]; }
+
+  Maybe<int> winding(Point p) const {
+    return sbasis_winding(toSBasis(), p);
+  }
+  
+  Point pointAndDerivativesAt(Coord t, unsigned n_derivs, Point *derivs) const {
+    //TODO
+    return Point(0,0);
+  }
+};
+
+template<unsigned order>
+Bezier<order> reverse(const Bezier<order> & a) {
+  Bezier<order> result;
+  for(int i = 0; i <= order; i++)
+    result[i] = a[order - i];
+  return result;
+}
+
+template<unsigned order>
+vector<Point> bezier_points(const D2<Bezier<order> > & a) {
+  vector<Point> result;
+  for(int i = 0; i <= order; i++) {
+    Point p;
+    for(unsigned d = 0; d < 2; d++) p[d] = a[d][i];
+    result[i] = p;
+  }
+  return result;
+}
+
+}
+#endif //SEEN_BEZIER_H
diff --git a/src/2geom/choose.h b/src/2geom/choose.h
new file mode 100644 (file)
index 0000000..169d77c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * choose.h
+ *
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef _CHOOSE_H
+#define _CHOOSE_H
+
+// XXX: Can we keep only the left terms easily? 
+// this would more than halve the array
+// row index becomes n2 = n/2, row2 = n2*(n2+1)/2, row = row2*2+(n&1)?n2:0
+// we could also leave off the ones
+
+template <typename T>
+T choose(unsigned n, unsigned k) {
+    static std::vector<T> pascals_triangle;
+    static unsigned rows_done = 0;
+    // indexing is (0,0,), (1,0), (1,1), (2, 0)...
+    // to get (i, j) i*(i+1)/2 + j
+    if(k < 0 || k > n) return 0;
+    if(rows_done <= n) {// we haven't got there yet
+        if(rows_done == 0) {
+            pascals_triangle.push_back(1);
+            rows_done = 1;
+        }
+        while(rows_done <= n) {
+            unsigned p = pascals_triangle.size() - rows_done;
+            pascals_triangle.push_back(1);
+            for(unsigned i = 0; i < rows_done-1; i++) {
+                pascals_triangle.push_back(pascals_triangle[p] 
+                                           + pascals_triangle[p+1]);
+               p++;
+            }
+            pascals_triangle.push_back(1);
+            rows_done ++;
+        }
+    }
+    unsigned row = (n*(n+1))/2;
+    return pascals_triangle[row+k];
+}
+
+#endif
diff --git a/src/2geom/circle-circle.cpp b/src/2geom/circle-circle.cpp
new file mode 100644 (file)
index 0000000..262f887
--- /dev/null
@@ -0,0 +1,131 @@
+/* circle_circle_intersection() *
+ * Determine the points where 2 circles in a common plane intersect.
+ *
+ * int circle_circle_intersection(
+ *                                // center and radius of 1st circle
+ *                                double x0, double y0, double r0,
+ *                                // center and radius of 2nd circle
+ *                                double x1, double y1, double r1,
+ *                                // 1st intersection point
+ *                                double *xi, double *yi,              
+ *                                // 2nd intersection point
+ *                                double *xi_prime, double *yi_prime)
+ *
+ * This is a public domain work. 3/26/2005 Tim Voght
+ * Ported to lib2geom, 2006 Nathan Hurst
+ *
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ * 
+ */
+#include <stdio.h>
+#include <math.h>
+#include "point.h"
+
+namespace Geom{
+
+int circle_circle_intersection(Point X0, double r0,
+                               Point X1, double r1,
+                               Point & p0, Point & p1)
+{
+  /* dx and dy are the vertical and horizontal distances between
+   * the circle centers.
+   */
+  Point D = X1 - X0;
+
+  /* Determine the straight-line distance between the centers. */
+  double d = L2(D);
+
+  /* Check for solvability. */
+  if (d > (r0 + r1))
+  {
+    /* no solution. circles do not intersect. */
+    return 0;
+  }
+  if (d <= fabs(r0 - r1))
+  {
+    /* no solution. one circle is contained in the other */
+    return 1;
+  }
+  
+  /* 'point 2' is the point where the line through the circle
+   * intersection points crosses the line between the circle
+   * centers.  
+   */
+
+  /* Determine the distance from point 0 to point 2. */
+  double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ;
+
+  /* Determine the coordinates of point 2. */
+  Point p2 = X0 + D * (a/d);
+
+  /* Determine the distance from point 2 to either of the
+   * intersection points.
+   */
+  double h = sqrt((r0*r0) - (a*a));
+
+  /* Now determine the offsets of the intersection points from
+   * point 2.
+   */
+  Point r = (h/d)*rot90(D);
+
+  /* Determine the absolute intersection points. */
+  p0 = p2 + r;
+  p1 = p2 - r;
+
+  return 2;
+}
+
+};
+
+
+#ifdef TEST
+
+void run_test(double x0, double y0, double r0,
+              double x1, double y1, double r1)
+{
+  double x3, y3, x3_prime, y3_prime;
+
+  printf("x0=%F, y0=%F, r0=%F, x1=%F, y1=%F, r1=%F :\n",
+          x0, y0, r0, x1, y1, r1);
+  Geom::Point p0, p1;
+  Geom::circle_circle_intersection(Geom::Point(x0, y0), r0, 
+                                  Geom::Point(x1, y1), r1,
+                                  p0, p1);
+  printf("  x3=%F, y3=%F, x3_prime=%F, y3_prime=%F\n",
+            p0[0], p0[1], p1[0], p1[1]);
+}
+
+int main(void)
+{
+  /* Add more! */    
+  run_test(-1.0, -1.0, 1.5, 1.0, 1.0, 2.0);
+  run_test(1.0, -1.0, 1.5, -1.0, 1.0, 2.0);
+  run_test(-1.0, 1.0, 1.5, 1.0, -1.0, 2.0);
+  run_test(1.0, 1.0, 1.5, -1.0, -1.0, 2.0);
+  exit(0);
+}
+#endif
+
diff --git a/src/2geom/circulator.h b/src/2geom/circulator.h
new file mode 100644 (file)
index 0000000..4c7f83b
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * ciculator.h
+ *
+ * Copyright 2006 MenTaLguY <mental@rydia.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef SEEN_Circulator_H
+#define SEEN_Circulator_H
+
+#include <iterator>
+
+namespace Geom {
+
+template <typename Iterator>
+class Circulator {
+public:
+    typedef std::random_access_iterator_tag std::iterator_category;
+    typedef std::iterator_traits<Iterator>::value_type value_type;
+    typedef std::iterator_traits<Iterator>::difference_type difference_type;
+    typedef std::iterator_traits<Iterator>::pointer pointer;
+    typedef std::iterator_traits<Iterator>::reference reference;
+
+    Circulator(Iterator const &first,
+               Iterator const &last,
+               Iterator const &pos)
+    : _first(first), _last(last), _pos(pos)
+    {
+        match_random_access(std::iterator_category(first));
+    }
+
+    reference operator*() const {
+        return *_pos;
+    }
+    pointer operator->() const {
+        return &*_pos;
+    }
+    
+    Circulator &operator++() {
+        if ( _first == _last ) return *this;
+        ++_pos;
+        if ( _pos == _last ) _pos = _first;
+        return *this;
+    }
+    Circulator operator++(int) {
+        Circulator saved=*this;
+        ++(*this);
+        return saved;
+    }
+
+    Circulator &operator--() {
+        if ( _pos == _first ) _pos = _last;
+        --_pos;
+        return *this;
+    }
+    Circulator operator--(int) {
+        Circulator saved=*this;
+        --(*this);
+        return saved;
+    }
+
+    Circulator &operator+=(int n) {
+        _pos = _offset(n);
+        return *this;
+    }
+    Circulator operator+(int n) const {
+        return Circulator(_first, _last, _offset(n));
+    }
+    Circulator &operator-=(int n) {
+        _pos = _offset(-n);
+        return *this;
+    }
+    Circulator operator-(int n) const {
+        return Circulator(_first, _last, _offset(-n));
+    }
+
+    difference_type operator-(Circulator const &other) {
+        return _pos - other._pos;
+    }
+
+    reference operator[n] const {
+        return *_offset(n);
+    }
+
+private:
+    void match_random_access(random_access_iterator_tag) {}
+
+    Iterator _offset(int n) {
+        difference_type range=( _last - _first );
+        difference_type offset=( _pos - _first + n );
+
+        if ( offset < 0 ) {
+            // modulus not well-defined for negative numbers in C++
+            offset += ( ( -offset / range ) + 1 ) * range;
+        } else if ( offset >= range ) {
+            offset %= range;
+        }
+        return _first + offset;
+    }
+
+    Iterator _first;
+    Iterator _last;
+    Iterator _pos;
+};
+
+}
+
+template <typename T>
+Geom::Circulator<T> operator+(int n, Geom::Circulator<T> const &c) {
+    return c + n;
+}
+
+#endif // SEEN_Circulator_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(substatement-open . 0))
+  indent-tabs-mode:nil
+  c-brace-offset:0
+  fill-column:99
+  End:
+  vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
+*/
+
diff --git a/src/2geom/concepts.h b/src/2geom/concepts.h
new file mode 100644 (file)
index 0000000..42192c4
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * concepts.h - Declares various mathematical concepts, for restriction of template parameters
+ *
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef SEEN_CONCEPTS_H
+#define SEEN_CONCEPTS_H
+
+#include "sbasis.h"
+#include "interval.h"
+#include "point.h"
+
+#include <boost/concept_check.hpp>
+
+namespace Geom {
+
+//forward decls
+template <typename T> class D2;
+
+template <typename T> struct ResultTraits;
+
+template <> struct ResultTraits<double> {
+  typedef Interval bounds_type;
+  typedef SBasis sb_type;
+};
+
+template <> struct ResultTraits<Point > {
+  typedef D2<Interval> bounds_type;
+  typedef D2<SBasis> sb_type;
+};
+
+//A concept for one-dimensional functions defined on [0,1]
+template <typename T>
+struct FragmentConcept {
+    typedef typename T::output_type                        OutputType;
+    typedef typename ResultTraits<OutputType>::bounds_type BoundsType;
+    typedef typename ResultTraits<OutputType>::sb_type     SbType;
+    T t;
+    double d;
+    OutputType o;
+    bool b;
+    BoundsType i;
+    Interval dom;
+    void constraints() {
+        t = T(o);
+        b = t.isZero();
+        b = t.isFinite();
+        o = t.at0();
+        o = t.at1();
+        o = t.valueAt(d);
+        o = t(d);
+        SbType sb = t.toSBasis();
+        t = reverse(t);
+        i = bounds_fast(t);
+        i = bounds_exact(t);
+        i = bounds_local(t, dom);
+        /*With portion, Interval makes some sense, but instead I'm opting for
+          doubles, for the following reasons:
+          A) This way a reversed portion may be specified
+          B) Performance might be a bit better for piecewise and such
+          C) Interval version provided below
+         */
+        t = portion(t, d, d);
+    }
+};
+
+template <typename T>
+inline T portion(const T& t, const Interval& i) { return portion(t, i.min(), i.max()); }
+
+template <typename T>
+struct NearConcept {
+    T a, b;
+    double tol;
+    bool res;
+    void constraints() {
+        res = near(a, b, tol);
+    }
+};
+
+template <typename T>
+struct OffsetableConcept {
+    T t;
+    typename T::output_type d;
+    void constraints() {
+        t = t + d; t += d;
+        t = t - d; t -= d;
+    }
+};
+
+template <typename T>
+struct ScalableConcept {
+    T t;
+    typename T::output_type d;
+    void constraints() {
+        t = -t;
+        t = t * d; t *= d;
+        t = t / d; t /= d;
+    }
+};
+
+template <class T>
+struct AddableConcept {
+    T i, j;
+    void constraints() {
+        i += j; i = i + j;
+        i -= j; i = i - j;
+    }
+};
+
+template <class T>
+struct MultiplicableConcept {
+    T i, j;
+    void constraints() {
+        i *= j; i = i * j;
+    }
+};
+
+};
+
+#endif //SEEN_CONCEPTS_H
diff --git a/src/2geom/conjugate_gradient.cpp b/src/2geom/conjugate_gradient.cpp
new file mode 100644 (file)
index 0000000..220cd06
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * conjugate_gradient.cpp
+ *
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <valarray>
+#include <cassert>
+#include "conjugate_gradient.h"
+
+/* lifted wholely from wikipedia. */
+
+using std::valarray;
+
+static void 
+matrix_times_vector(valarray<double> const &matrix, /* m * n */
+                   valarray<double> const &vec,  /* n */
+                   valarray<double> &result) /* m */
+{
+    unsigned n = vec.size();
+    unsigned m = result.size();
+    assert(m*n == matrix.size());
+    const double* mp = &matrix[0];
+    for (unsigned i = 0; i < m; i++) {
+        double res = 0;
+        for (unsigned j = 0; j < n; j++)
+            res += *mp++ * vec[j];
+        result[i] = res;
+    }
+}
+
+static double Linfty(valarray<double> const &vec) {
+    return std::max(vec.max(), -vec.min());
+}
+
+double
+inner(valarray<double> const &x, 
+      valarray<double> const &y) {
+    double total = 0;
+    for(unsigned i = 0; i < x.size(); i++)
+        total += x[i]*y[i];
+    return total;// (x*y).sum(); <- this is more concise, but ineff
+}
+
+void 
+conjugate_gradient(double **A, 
+                  double *x, 
+                  double *b, 
+                  unsigned n, 
+                  double tol,
+                   int max_iterations, 
+                  bool ortho1) {
+    valarray<double> vA(n*n);
+    valarray<double> vx(n);
+    valarray<double> vb(n);
+    for(unsigned i=0;i<n;i++) {
+        vx[i]=x[i];
+        vb[i]=b[i];
+        for(unsigned j=0;j<n;j++) {
+            vA[i*n+j]=A[i][j];
+        }
+    }
+    conjugate_gradient(vA,vx,vb,n,tol,max_iterations,ortho1);
+    for(unsigned i=0;i<n;i++) {
+        x[i]=vx[i];
+    }
+}
+void 
+conjugate_gradient(valarray<double> const &A, 
+                  valarray<double> &x, 
+                  valarray<double> const &b, 
+                  unsigned n, double tol,
+                  unsigned max_iterations, bool ortho1) {
+    valarray<double> Ap(n), p(n), r(n);
+    matrix_times_vector(A,x,Ap);
+    r=b-Ap; 
+    double r_r = inner(r,r);
+    unsigned k = 0;
+    tol *= tol;
+    while(k < max_iterations && r_r > tol) {
+        k++;
+        double r_r_new = r_r;
+        if(k == 1)
+            p = r;
+        else {
+            r_r_new = inner(r,r);
+            p = r + (r_r_new/r_r)*p;
+        }
+        matrix_times_vector(A, p, Ap);
+        double alpha_k = r_r_new / inner(p, Ap);
+        x += alpha_k*p;
+        r -= alpha_k*Ap;
+        r_r = r_r_new;
+    }
+    //printf("njh: %d iters, Linfty = %g L2 = %g\n", k, 
+    //std::max(-r.min(), r.max()), sqrt(r_r));
+    // x is solution
+}
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
diff --git a/src/2geom/conjugate_gradient.h b/src/2geom/conjugate_gradient.h
new file mode 100644 (file)
index 0000000..2090aec
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * conjugate_gradient.h
+ *
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef _CONJUGATE_GRADIENT_H
+#define _CONJUGATE_GRADIENT_H
+
+#include <valarray>
+
+double
+inner(std::valarray<double> const &x, 
+      std::valarray<double> const &y);
+
+void 
+conjugate_gradient(std::valarray<double> const &A, 
+                  std::valarray<double> &x, 
+                  std::valarray<double> const &b, 
+                  unsigned n, double tol,
+                  unsigned max_iterations, bool ortho1);
+#endif // _CONJUGATE_GRADIENT_H
diff --git a/src/2geom/convex-cover.cpp b/src/2geom/convex-cover.cpp
new file mode 100644 (file)
index 0000000..1c704e0
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * convex-cover.cpp
+ *
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "convex-cover.h"
+#include <algorithm>
+#include <map>
+/** Todo:
+    + modify graham scan to work top to bottom, rather than around angles
+    + intersection
+    + minimum distance between convex hulls
+    + maximum distance between convex hulls
+    + hausdorf metric?
+    + check all degenerate cases carefully
+    + check all algorithms meet all invariants
+    + generalise rotating caliper algorithm (iterator/circulator?)
+*/
+
+using std::vector;
+using std::map;
+using std::pair;
+
+namespace Geom{
+
+/*** SignedTriangleArea
+ * returns the area of the triangle defined by p0, p1, p2.  A clockwise triangle has positive area.
+ */
+double
+SignedTriangleArea(Point p0, Point p1, Point p2) {
+    return cross((p1 - p0), (p2 - p0));
+}
+
+class angle_cmp{
+public:
+    Point o;
+    angle_cmp(Point o) : o(o) {}
+    
+    bool
+    operator()(Point a, Point b) {
+        Point da = a - o;
+        Point db = b - o;
+        
+#if 1
+        double aa = da[0];
+        double ab = db[0];
+        if((da[1] == 0) && (db[1] == 0))
+            return da[0] < db[0];
+        if(da[1] == 0)
+            return true; // infinite tangent
+        if(db[1] == 0)
+            return false; // infinite tangent
+        aa = da[0] / da[1];
+        ab = db[0] / db[1];
+        if(aa > ab)
+            return true;
+#else
+        //assert((ata > atb) == (aa < ab));
+        double aa = atan2(da);
+        double ab = atan2(db);
+        if(aa < ab)
+            return true;
+#endif
+        if(aa == ab)
+            return L2sq(da) < L2sq(db);
+        return false;
+    }
+};
+
+void
+ConvexHull::find_pivot() {
+    // Find pivot P;
+    unsigned pivot = 0;
+    for(unsigned i = 1; i < boundary.size(); i++)
+        if(boundary[i] <= boundary[pivot])
+            pivot = i;
+    
+    std::swap(boundary[0], boundary[pivot]);
+}
+
+void
+ConvexHull::angle_sort() {
+// sort points by angle (resolve ties in favor of point farther from P);
+// we leave the first one in place as our pivot
+    std::sort(boundary.begin()+1, boundary.end(), angle_cmp(boundary[0]));
+}
+
+void
+ConvexHull::graham_scan() {
+    unsigned stac = 2;
+    for(int i = 2; i < boundary.size(); i++) {
+        double o = SignedTriangleArea(boundary[stac-2], 
+                                      boundary[stac-1], 
+                                      boundary[i]);
+        if(o == 0) { // colinear - dangerous...
+            stac--;
+        } else if(o < 0) { // anticlockwise
+        } else { // remove concavity
+            while(o >= 0 && stac > 2) {
+                stac--;
+                o = SignedTriangleArea(boundary[stac-2], 
+                                       boundary[stac-1], 
+                                       boundary[i]);
+            }
+        }
+        boundary[stac++] = boundary[i];
+    }
+    boundary.resize(stac);
+}
+
+void
+ConvexHull::graham() {
+    find_pivot();
+    angle_sort();
+    graham_scan();
+}
+
+//Mathematically incorrect mod, but more useful.
+int mod(int i, int l) {
+    return i >= 0 ? 
+           i % l : (i % l) + l;
+}
+//OPT: usages can often be replaced by conditions
+
+/*** ConvexHull::left
+ * Tests if a point is left (outside) of a particular segment, n. */
+bool
+ConvexHull::is_left(Point p, int n) {
+    return SignedTriangleArea((*this)[n], (*this)[n+1], p) > 0;
+}
+
+/*** ConvexHull::find_positive
+ * May return any number n where the segment n -> n + 1 (possibly looped around) in the hull such
+ * that the point is on the wrong side to be within the hull.  Returns -1 if it is within the hull.*/
+int
+ConvexHull::find_left(Point p) {
+    int l = boundary.size(); //Who knows if C++ is smart enough to optimize this?
+    for(int i = 0; i < l; i++) {
+        if(is_left(p, i)) return i;
+    }
+    return -1;
+}
+//OPT: do a spread iteration - quasi-random with no repeats and full coverage. 
+
+/*** ConvexHull::contains_point
+ * In order to test whether a point is inside a convex hull we can travel once around the outside making
+ * sure that each triangle made from an edge and the point has positive area. */
+bool
+ConvexHull::contains_point(Point p) {
+    return find_left(p) == -1;
+}
+
+/*** ConvexHull::add_point
+ * to add a point we need to find whether the new point extends the boundary, and if so, what it
+ * obscures.  Tarjan?  Jarvis?*/
+void
+ConvexHull::merge(Point p) {
+    std::vector<Point> out;
+
+    int l = boundary.size();
+
+    if(l < 2) {
+        boundary.push_back(p);
+        return;
+    }
+
+    bool pushed = false;
+
+    bool pre = is_left(p, -1);
+    for(int i = 0; i < l; i++) {
+        bool cur = is_left(p, i);
+        if(pre) {
+            if(cur) {
+                if(!pushed) {
+                    out.push_back(p);
+                    pushed = true;
+                }
+                continue;
+            }
+            else if(!pushed) {
+                out.push_back(p);
+                pushed = true;
+            }
+        }
+        out.push_back(boundary[i]);
+        pre = cur;
+    }
+    
+    boundary = out;
+}
+//OPT: quickly find an obscured point and find the bounds by extending from there.  then push all points not within the bounds in order.
+  //OPT: use binary searches to find the actual starts/ends, use known rights as boundaries.  may require cooperation of find_left algo.
+
+/*** ConvexHull::is_clockwise
+ * We require that successive pairs of edges always turn right.
+ * proposed algorithm: walk successive edges and require triangle area is positive.
+ */
+bool
+ConvexHull::is_clockwise() const {
+    if(is_degenerate())
+        return true;
+    Point first = boundary[0];
+    Point second = boundary[1];
+    for(std::vector<Point>::const_iterator it(boundary.begin()+2), e(boundary.end());
+        it != e;) {
+        if(SignedTriangleArea(first, second, *it) > 0)
+            return false;
+        first = second;
+        second = *it;
+        ++it;
+    }
+    return true;
+}
+
+/*** ConvexHull::top_point_first
+ * We require that the first point in the convex hull has the least y coord, and that off all such points on the hull, it has the least x coord.
+ * proposed algorithm: track lexicographic minimum while walking the list.
+ */
+bool
+ConvexHull::top_point_first() const {
+    std::vector<Point>::const_iterator pivot = boundary.begin();
+    for(std::vector<Point>::const_iterator it(boundary.begin()+1), 
+            e(boundary.end());
+        it != e; it++) {
+        if((*it)[1] < (*pivot)[1])
+            pivot = it;
+        else if(((*it)[1] == (*pivot)[1]) && 
+                ((*it)[0] < (*pivot)[0]))
+            pivot = it;
+    }
+    return pivot == boundary.begin();
+}
+//OPT: since the Y values are orderly there should be something like a binary search to do this.
+
+/*** ConvexHull::no_colinear_points
+ * We require that no three vertices are colinear.
+proposed algorithm:  We must be very careful about rounding here.
+*/
+bool
+ConvexHull::no_colinear_points() const {
+
+}
+
+bool
+ConvexHull::meets_invariants() const {
+    return is_clockwise() && top_point_first() && no_colinear_points();
+}
+
+/*** ConvexHull::is_degenerate
+ * We allow three degenerate cases: empty, 1 point and 2 points.  In many cases these should be handled explicitly.
+ */
+bool
+ConvexHull::is_degenerate() const {
+    return boundary.size() < 3;
+}
+
+
+/* Here we really need a rotating calipers implementation.  This implementation is slow and incorrect.
+   This incorrectness is a problem because it throws off the algorithms.  Perhaps I will come up with
+   something better tomorrow.  The incorrectness is in the order of the bridges - they must be in the
+   order of traversal around.  Since the a->b and b->a bridges are seperated, they don't need to be merge
+   order, just the order of the traversal of the host hull.  Currently some situations make a n->0 bridge
+   first.*/
+pair< map<int, int>, map<int, int> >
+bridges(ConvexHull a, ConvexHull b) {
+    map<int, int> abridges;
+    map<int, int> bbridges;
+
+    for(int ia = 0; ia < a.boundary.size(); ia++) {
+        for(int ib = 0; ib < b.boundary.size(); ib++) {
+            Point d = b[ib] - a[ia];
+            Geom::Coord e = cross(d, a[ia - 1] - a[ia]), f = cross(d, a[ia + 1] - a[ia]);
+            Geom::Coord g = cross(d, b[ib - 1] - a[ia]), h = cross(d, b[ib + 1] - a[ia]);
+            if     (e > 0 && f > 0 && g > 0 && h > 0) abridges[ia] = ib;
+            else if(e < 0 && f < 0 && g < 0 && h < 0) bbridges[ib] = ia;
+        }
+    }
+       
+    return make_pair(abridges, bbridges);
+}
+
+std::vector<Point> bridge_points(ConvexHull a, ConvexHull b) {
+    vector<Point> ret;
+    pair< map<int, int>, map<int, int> > indices = bridges(a, b);
+    for(map<int, int>::iterator it = indices.first.begin(); it != indices.first.end(); it++) {
+      ret.push_back(a[it->first]);
+      ret.push_back(b[it->second]);
+    }
+    for(map<int, int>::iterator it = indices.second.begin(); it != indices.second.end(); it++) {
+      ret.push_back(b[it->first]);
+      ret.push_back(a[it->second]);
+    }
+    return ret;
+}
+
+unsigned find_bottom_right(ConvexHull const &a) {
+    unsigned it = 1;
+    while(it < a.boundary.size() && 
+          a.boundary[it][Y] > a.boundary[it-1][Y])
+        it++;
+    return it-1;
+}
+
+/*** ConvexHull sweepline_intersection(ConvexHull a, ConvexHull b);
+ * find the intersection between two convex hulls.  The intersection is also a convex hull.
+ * (Proof: take any two points both in a and in b.  Any point between them is in a by convexity,
+ * and in b by convexity, thus in both.  Need to prove still finite bounds.)
+ * This algorithm works by sweeping a line down both convex hulls in parallel, working out the left and right edges of the new hull.
+ */
+ConvexHull sweepline_intersection(ConvexHull const &a, ConvexHull const &b) {
+    ConvexHull ret;
+    
+    int al = 0;
+    int bl = 0;
+    
+    while(al+1 < a.boundary.size() &&
+          (a.boundary[al+1][Y] > b.boundary[bl][Y])) {
+        al++;
+    }
+    while(bl+1 < b.boundary.size() &&
+          (b.boundary[bl+1][Y] > a.boundary[al][Y])) {
+        bl++;
+    }
+    // al and bl now point to the top of the first pair of edges that overlap in y value
+    double sweep_y = std::min(a.boundary[al][Y],
+                              b.boundary[bl][Y]);
+}
+
+/*** ConvexHull intersection(ConvexHull a, ConvexHull b);
+ * find the intersection between two convex hulls.  The intersection is also a convex hull.
+ * (Proof: take any two points both in a and in b.  Any point between them is in a by convexity,
+ * and in b by convexity, thus in both.  Need to prove still finite bounds.)
+ */
+ConvexHull intersection(ConvexHull a, ConvexHull b) {
+    ConvexHull ret;
+    int ai = 0, bi = 0;
+    int aj = a.boundary.size() - 1;
+    int bj = b.boundary.size() - 1;
+    
+    /*while (true) {
+        if(a[ai]
+    }*/
+    return ret;
+}
+
+/*** ConvexHull merge(ConvexHull a, ConvexHull b);
+ * find the smallest convex hull that surrounds a and b.
+ */
+ConvexHull merge(ConvexHull a, ConvexHull b) {
+    ConvexHull ret;
+
+    pair< map<int, int>, map<int, int> > bpair = bridges(a, b);
+    map<int, int> ab = bpair.first;
+    map<int, int> bb = bpair.second;
+
+    ab[-1] = 0;
+    bb[-1] = 0;
+
+    int i = -1;
+
+    if(a.boundary[0][1] > b.boundary[0][1]) goto start_b;
+    while(true) {
+        for(; ab.count(i) == 0; i++) {
+            ret.boundary.push_back(a[i]);
+            if(i >= a.boundary.size()) return ret;
+        }
+        if(ab[i] == 0 && i != -1) break;
+        i = ab[i];
+        start_b:
+        
+        for(; bb.count(i) == 0; i++) {
+            ret.boundary.push_back(b[i]);
+            if(i >= b.boundary.size()) return ret;
+        }
+        if(bb[i] == 0 && i != -1) break;
+        i = bb[i];
+    }
+    return ret;
+}
+
+ConvexHull graham_merge(ConvexHull a, ConvexHull b) {
+    ConvexHull result;
+    
+    // we can avoid the find pivot step because of top_point_first
+    if(b.boundary[0] <= a.boundary[0])
+        std::swap(a, b);
+    
+    result.boundary = a.boundary;
+    result.boundary.insert(result.boundary.end(), 
+                           b.boundary.begin(), b.boundary.end());
+    
+/** if we modified graham scan to work top to bottom as proposed in lect754.pdf we could replace the
+ angle sort with a simple merge sort type algorithm. furthermore, we could do the graham scan
+ online, avoiding a bunch of memory copies.  That would probably be linear. -- njh*/
+    result.angle_sort();
+    result.graham_scan();
+    
+    return result;
+}
+//TODO: reinstate
+/*ConvexCover::ConvexCover(Path const &sp) : path(&sp) {
+    cc.reserve(sp.size());
+    for(Geom::Path::const_iterator it(sp.begin()), end(sp.end()); it != end; ++it) {
+        cc.push_back(ConvexHull((*it).begin(), (*it).end()));
+    }
+}*/
+
+
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(substatement-open . 0))
+  indent-tabs-mode:nil
+  c-brace-offset:0
+  fill-column:99
+  End:
+  vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
+*/
+
+
diff --git a/src/2geom/convex-cover.h b/src/2geom/convex-cover.h
new file mode 100644 (file)
index 0000000..5c1f33f
--- /dev/null
@@ -0,0 +1,174 @@
+#ifndef GEOM_CONVEX_COVER_H
+#define GEOM_CONVEX_COVER_H
+
+/*
+ * convex-cover.h
+ *
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+/** A convex cover is a sequence of convex polygons that completely cover the path.  For now a
+ * convex hull class is included here (the convex-hull header is wrong)
+ */
+
+#include "point.h"
+#include <vector>
+
+namespace Geom{
+
+/** ConvexHull
+ * A convexhull is a convex region - every point between two points in the convex hull is also in
+ * the convex hull.  It is defined by a set of points travelling in a clockwise direction.  We require the first point to be top most, and of the topmost, leftmost.
+
+ * An empty hull has no points, we allow a single point or two points degenerate cases.
+
+ * We could provide the centroid as a member for efficient direction determination.  We can update the
+ * centroid with all operations with the same time complexity as the operation.
+ */
+
+class ConvexHull{
+public: // XXX: should be private :)
+    // extracts the convex hull of boundary. internal use only
+    void find_pivot();
+    void angle_sort();
+    void graham_scan();
+    void graham();
+public:
+    std::vector<Point> boundary;
+    //Point centroid;
+    
+    void merge(Point p);
+    bool contains_point(Point p);
+    
+    inline Point operator[](int i) const {
+        int l = boundary.size();
+        if(l == 0) return Point();
+        return boundary[i >= 0 ? i % l : (i % l) + l];
+    }
+
+    /*inline Point &operator[](unsigned i) {
+        int l = boundary.size();
+        if(l == 0) return Point();
+        return boundary[i >= 0 ? i % l : i % l + l];
+    }*/
+
+public:
+    ConvexHull() {}
+    ConvexHull(std::vector<Point> const & points) {
+        boundary = points;
+        graham();
+    }
+
+    template <typename T>
+    ConvexHull(T b, T e) :boundary(b,e) {}
+    
+public:
+    /** Is the convex hull clockwise?  We use the definition of clockwise from point.h
+    **/
+    bool is_clockwise() const;
+    bool no_colinear_points() const;
+    bool top_point_first() const;
+    bool meets_invariants() const;
+    
+    // contains no points
+    bool empty() const { return boundary.empty();}
+    
+    // contains exactly one point
+    bool singular() const { return boundary.size() == 1;}
+
+    //  all points are on a line
+    bool linear() const { return boundary.size() == 2;}
+    bool is_degenerate() const;
+    
+    // area of the convex hull
+    double area() const;
+    
+    // furthest point in a direction (lg time) 
+    Point const * furthest(Point direction) const;
+
+    bool is_left(Point p, int n);
+    int find_left(Point p);
+};
+
+// do two convex hulls intersect?
+bool intersectp(ConvexHull a, ConvexHull b);
+
+std::vector<Point> bridge_points(ConvexHull a, ConvexHull b);
+
+// find the convex hull intersection
+ConvexHull intersection(ConvexHull a, ConvexHull b);
+ConvexHull sweepline_intersection(ConvexHull const &a, ConvexHull const &b);
+
+// find the convex hull of a set of convex hulls
+ConvexHull merge(ConvexHull a, ConvexHull b);
+
+// naive approach
+ConvexHull graham_merge(ConvexHull a, ConvexHull b);
+
+unsigned find_bottom_right(ConvexHull const &a);
+
+/*** Arbitrary transform operator.
+ * Take a convex hull and apply an arbitrary convexity preserving transform.
+ *  we should be concerned about singular tranforms here.
+ */
+template <class T> ConvexHull operator*(ConvexHull const &p, T const &m) {
+    ConvexHull pr;
+    
+    pr.boundary.reserve(p.boundary.size());
+    
+    for(unsigned i = 0; i < p.boundary.size(); i++) {
+        pr.boundary.push_back(p.boundary[i]*m);
+    }
+    return pr;
+}
+
+//TODO: reinstate
+/*class ConvexCover{
+public:
+    Path const* path;
+    std::vector<ConvexHull> cc;
+    
+    ConvexCover(Path const &sp);
+};*/
+
+};
+
+#endif //2GEOM_CONVEX_COVER_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(substatement-open . 0))
+  indent-tabs-mode:nil
+  c-brace-offset:0
+  fill-column:99
+  End:
+  vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
+*/
+
diff --git a/src/2geom/coord.h b/src/2geom/coord.h
new file mode 100644 (file)
index 0000000..6636b4a
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * coord.h
+ *
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef SEEN_Geom_COORD_H
+#define SEEN_Geom_COORD_H
+
+#include <cmath>
+
+namespace Geom {
+
+/**
+ * A "real" type with sufficient precision for coordinates.
+ *
+ * You may safely assume that double (or even float) provides enough precision for storing
+ * on-canvas points, and hence that double provides enough precision for dot products of
+ * differences of on-canvas points.
+ */
+typedef double Coord;
+
+const Coord EPSILON = 1e-5; //1e-18;
+
+//IMPL: NearConcept
+inline bool near(Coord a, Coord b, double eps=EPSILON) { return fabs(a-b) <= eps; }
+
+} /* namespace Geom */
+
+
+#endif /* !SEEN_Geom_COORD_H */
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/d2.cpp b/src/2geom/d2.cpp
new file mode 100644 (file)
index 0000000..8653806
--- /dev/null
@@ -0,0 +1,177 @@
+/*\r
+ * d2.cpp - Lifts one dimensional objects into 2d \r
+ *\r
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it either under the terms of the GNU Lesser General Public\r
+ * License version 2.1 as published by the Free Software Foundation\r
+ * (the "LGPL") or, at your option, under the terms of the Mozilla\r
+ * Public License Version 1.1 (the "MPL"). If you do not alter this\r
+ * notice, a recipient may use your version of this file under either\r
+ * the MPL or the LGPL.\r
+ *\r
+ * You should have received a copy of the LGPL along with this library\r
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ * You should have received a copy of the MPL along with this library\r
+ * in the file COPYING-MPL-1.1\r
+ *\r
+ * The contents of this file are subject to the Mozilla Public License\r
+ * Version 1.1 (the "License"); you may not use this file except in\r
+ * compliance with the License. You may obtain a copy of the License at\r
+ * http://www.mozilla.org/MPL/\r
+ *\r
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY\r
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for\r
+ * the specific language governing rights and limitations.\r
+ *\r
+ */\r
+\r
+#include "d2.h"\r
+\r
+namespace Geom {\r
+\r
+SBasis L2(D2<SBasis> const & a, unsigned k) { return sqrt(dot(a, a), k); }\r
+double L2(D2<double> const & a) { return hypot(a[0], a[1]); }\r
+\r
+D2<SBasis> multiply(Linear const & a, D2<SBasis> const & b) {\r
+    return D2<SBasis>(multiply(a, b[X]), multiply(a, b[Y]));\r
+}\r
+\r
+D2<SBasis> multiply(SBasis const & a, D2<SBasis> const & b) {\r
+    return D2<SBasis>(multiply(a, b[X]), multiply(a, b[Y]));\r
+}\r
+\r
+D2<SBasis> truncate(D2<SBasis> const & a, unsigned terms) {\r
+    return D2<SBasis>(truncate(a[X], terms), truncate(a[Y], terms));\r
+}\r
+\r
+unsigned sbasis_size(D2<SBasis> const & a) {\r
+    return std::max((unsigned) a[0].size(), (unsigned) a[1].size());\r
+}\r
+\r
+//TODO: Is this sensical? shouldn't it be like pythagorean or something?\r
+double tail_error(D2<SBasis> const & a, unsigned tail) {\r
+    return std::max(a[0].tailError(tail), a[1].tailError(tail));\r
+}\r
+\r
+Piecewise<D2<SBasis> > sectionize(D2<Piecewise<SBasis> > const &a) {\r
+    Piecewise<SBasis> x = partition(a[0], a[1].cuts), y = partition(a[1], a[0].cuts);\r
+    assert(x.size() == y.size());\r
+    Piecewise<D2<SBasis> > ret;\r
+    for(unsigned i = 0; i < x.size(); i++)\r
+        ret.push_seg(D2<SBasis>(x[i], y[i]));\r
+    ret.cuts.insert(ret.cuts.end(), x.cuts.begin(), x.cuts.end());\r
+    return ret;\r
+}\r
+\r
+D2<Piecewise<SBasis> > make_cuts_independant(Piecewise<D2<SBasis> > const &a) {\r
+    D2<Piecewise<SBasis> > ret;\r
+    for(unsigned d = 0; d < 2; d++) {\r
+        for(unsigned i = 0; i < a.size(); i++)\r
+            ret[d].push_seg(a[i][d]);\r
+        ret[d].cuts.insert(ret[d].cuts.end(), a.cuts.begin(), a.cuts.end());\r
+    }\r
+    return ret;\r
+}\r
+\r
+Piecewise<D2<SBasis> > rot90(Piecewise<D2<SBasis> > const &M){\r
+  Piecewise<D2<SBasis> > result;\r
+  if (M.empty()) return M;\r
+  result.push_cut(M.cuts[0]);\r
+  for (unsigned i=0; i<M.size(); i++){\r
+    result.push(rot90(M[i]),M.cuts[i+1]);\r
+  }\r
+  return result;\r
+}\r
+\r
+Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, \r
+                     Piecewise<D2<SBasis> > const &b){\r
+  Piecewise<SBasis > result;\r
+  if (a.empty() || b.empty()) return result;\r
+  Piecewise<D2<SBasis> > aa = partition(a,b.cuts);\r
+  Piecewise<D2<SBasis> > bb = partition(b,a.cuts);\r
+\r
+  result.push_cut(aa.cuts.front());\r
+  for (unsigned i=0; i<aa.size(); i++){\r
+    result.push(dot(aa.segs[i],bb.segs[i]),aa.cuts[i+1]);\r
+  }\r
+  return result;\r
+}\r
+\r
+Piecewise<SBasis> cross(Piecewise<D2<SBasis> > const &a, \r
+                       Piecewise<D2<SBasis> > const &b){\r
+  Piecewise<SBasis > result;\r
+  if (a.empty() || b.empty()) return result;\r
+  Piecewise<D2<SBasis> > aa = partition(a,b.cuts);\r
+  Piecewise<D2<SBasis> > bb = partition(b,a.cuts);\r
+\r
+  result.push_cut(aa.cuts.front());\r
+  for (unsigned i=0; i<a.size(); i++){\r
+    result.push(cross(aa.segs[i],bb.segs[i]),aa.cuts[i+1]);\r
+  }\r
+  return result;\r
+}\r
+\r
+/* Replaced by remove_short_cuts in piecewise.h
+//this recursively removes the shortest cut interval until none is shorter than tol.\r
+//TODO: code this in a more efficient way!\r
+Piecewise<D2<SBasis> > remove_short_cuts(Piecewise<D2<SBasis> > const &f, double tol){\r
+    double min = tol;\r
+    unsigned idx = f.size();\r
+    for(unsigned i=0; i<f.size(); i++){\r
+        if (min > f.cuts[i+1]-f.cuts[i]){\r
+            min = f.cuts[i+1]-f.cuts[i];\r
+            idx = int(i);\r
+        }\r
+    }\r
+    if (idx==f.size()){\r
+        return f;\r
+    }\r
+    if (f.size()==1) {\r
+        //removing this seg would result in an empty pw<d2<sb>>...\r
+        return f;\r
+    }\r
+    Piecewise<D2<SBasis> > new_f=f;\r
+    for (int dim=0; dim<2; dim++){\r
+        double v = Hat(f.segs.at(idx)[dim][0]);\r
+        //TODO: what about closed curves?\r
+        if (idx>0 && f.segs.at(idx-1).at1()==f.segs.at(idx).at0()) \r
+            new_f.segs.at(idx-1)[dim][0][1] = v;\r
+        if (idx<f.size() && f.segs.at(idx+1).at0()==f.segs.at(idx).at1()) \r
+            new_f.segs.at(idx+1)[dim][0][0] = v;\r
+    }\r
+    double t = (f.cuts.at(idx)+f.cuts.at(idx+1))/2;\r
+    new_f.cuts.at(idx+1) = t;    \r
+    \r
+    new_f.segs.erase(new_f.segs.begin()+idx);\r
+    new_f.cuts.erase(new_f.cuts.begin()+idx);        \r
+    return remove_short_cuts(new_f, tol);\r
+}\r
+*/
+\r
+//if tol>0, only force continuity where the jump is smaller than tol.\r
+Piecewise<D2<SBasis> > force_continuity(Piecewise<D2<SBasis> > const &f, \r
+                                        double tol,\r
+                                        bool closed){\r
+    if (f.size()==0) return f;\r
+    Piecewise<D2<SBasis> > result=f;\r
+    unsigned cur   = (closed)? 0:1;\r
+    unsigned prev  = (closed)? f.size()-1:0;\r
+    while(cur<f.size()){\r
+        Point pt0 = f.segs[prev].at1();\r
+        Point pt1 = f.segs[cur ].at0();\r
+        if (tol<=0 || L2sq(pt0-pt1)<tol*tol){\r
+            pt0 = (pt0+pt1)/2;\r
+            for (unsigned dim=0; dim<2; dim++){\r
+                result.segs[prev][dim][0][1]=pt0[dim];\r
+                result.segs[cur ][dim][0][0]=pt0[dim];\r
+            }\r
+        }\r
+        prev = cur++;\r
+    }\r
+    return result;\r
+}\r
+  \r
+};\r
diff --git a/src/2geom/d2.h b/src/2geom/d2.h
new file mode 100644 (file)
index 0000000..8207051
--- /dev/null
@@ -0,0 +1,475 @@
+/*\r
+ * d2.h - Lifts one dimensional objects into 2d \r
+ *\r
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it either under the terms of the GNU Lesser General Public\r
+ * License version 2.1 as published by the Free Software Foundation\r
+ * (the "LGPL") or, at your option, under the terms of the Mozilla\r
+ * Public License Version 1.1 (the "MPL"). If you do not alter this\r
+ * notice, a recipient may use your version of this file under either\r
+ * the MPL or the LGPL.\r
+ *\r
+ * You should have received a copy of the LGPL along with this library\r
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ * You should have received a copy of the MPL along with this library\r
+ * in the file COPYING-MPL-1.1\r
+ *\r
+ * The contents of this file are subject to the Mozilla Public License\r
+ * Version 1.1 (the "License"); you may not use this file except in\r
+ * compliance with the License. You may obtain a copy of the License at\r
+ * http://www.mozilla.org/MPL/\r
+ *\r
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY\r
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for\r
+ * the specific language governing rights and limitations.\r
+ *\r
+ */\r
+\r
+#ifndef _2GEOM_D2  //If this is change, change the guard in rect.h as well.\r
+#define _2GEOM_D2\r
+\r
+#include "point.h"\r
+#include "interval.h"\r
+#include "matrix.h"\r
+\r
+#include <boost/concept_check.hpp>\r
+#include "concepts.h"\r
+\r
+namespace Geom{\r
+\r
+template <class T>\r
+class D2{\r
+    //BOOST_CLASS_REQUIRE(T, boost, AssignableConcept);\r
+  private:\r
+    T f[2];\r
+\r
+  public:\r
+    D2() {f[X] = f[Y] = T();}\r
+    explicit D2(Point const &a) {\r
+        f[X] = T(a[X]); f[Y] = T(a[Y]);\r
+    }\r
+\r
+    D2(T const &a, T const &b) {\r
+        f[X] = a;\r
+        f[Y] = b;\r
+    }\r
+\r
+    //TODO: ask mental about operator= as seen in Point\r
+\r
+    T& operator[](unsigned i)              { return f[i]; }\r
+    T const & operator[](unsigned i) const { return f[i]; }\r
+\r
+    //IMPL: FragmentConcept\r
+    typedef Point output_type;\r
+    bool isZero() const {\r
+        boost::function_requires<FragmentConcept<T> >();\r
+        return f[X].isZero() && f[Y].isZero();\r
+    }\r
+    bool isFinite() const {\r
+        boost::function_requires<FragmentConcept<T> >();\r
+        return f[X].isFinite() && f[Y].isFinite();\r
+    }\r
+    Point at0() const { \r
+        boost::function_requires<FragmentConcept<T> >();\r
+        return Point(f[X].at0(), f[Y].at0());\r
+    }\r
+    Point at1() const {\r
+        boost::function_requires<FragmentConcept<T> >();\r
+        return Point(f[X].at1(), f[Y].at1());\r
+    }\r
+    Point valueAt(double t) const {\r
+        boost::function_requires<FragmentConcept<T> >();\r
+        return (*this)(t);\r
+    }\r
+    D2<SBasis> toSBasis() const {\r
+        boost::function_requires<FragmentConcept<T> >();\r
+        return D2<SBasis>(f[X].toSBasis(), f[Y].toSBasis());\r
+    }\r
+\r
+    Point operator()(double t) const;\r
+    Point operator()(double x, double y) const;\r
+};\r
+\r
+template <typename T>\r
+D2<T> reverse(const D2<T> &a) {\r
+    boost::function_requires<FragmentConcept<T> >();\r
+    return D2<T>(reverse(a[X]), reverse(a[Y]));\r
+}\r
+\r
+//IMPL: boost::EqualityComparableConcept\r
+template <typename T>\r
+inline bool\r
+operator==(D2<T> const &a, D2<T> const &b) {\r
+    boost::function_requires<boost::EqualityComparableConcept<T> >();\r
+    return a[0]==b[0] && a[1]==b[1];\r
+}\r
+template <typename T>\r
+inline bool\r
+operator!=(D2<T> const &a, D2<T> const &b) {\r
+    boost::function_requires<boost::EqualityComparableConcept<T> >();\r
+    return a[0]!=b[0] || a[1]!=b[1];\r
+}\r
+\r
+//IMPL: NearConcept\r
+template <typename T>\r
+inline bool\r
+near(D2<T> const &a, D2<T> const &b, double tol) {\r
+    boost::function_requires<NearConcept<T> >();\r
+    return near(a[0], b[0]) && near(a[1], b[1]);\r
+}\r
+\r
+//IMPL: AddableConcept\r
+template <typename T>\r
+inline D2<T>\r
+operator+(D2<T> const &a, D2<T> const &b) {\r
+    boost::function_requires<AddableConcept<T> >();\r
+\r
+    D2<T> r;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        r[i] = a[i] + b[i];\r
+    return r;\r
+}\r
+template <typename T>\r
+inline D2<T>\r
+operator-(D2<T> const &a, D2<T> const &b) {\r
+    boost::function_requires<AddableConcept<T> >();\r
+\r
+    D2<T> r;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        r[i] = a[i] - b[i];\r
+    return r;\r
+}\r
+template <typename T>\r
+inline D2<T>\r
+operator+=(D2<T> &a, D2<T> const &b) {\r
+    boost::function_requires<AddableConcept<T> >();\r
+\r
+    for(unsigned i = 0; i < 2; i++)\r
+        a[i] += b[i];\r
+    return a;\r
+}\r
+template <typename T>\r
+inline D2<T>\r
+operator-=(D2<T> &a, D2<T> const & b) {\r
+    boost::function_requires<AddableConcept<T> >();\r
+\r
+    for(unsigned i = 0; i < 2; i++)\r
+        a[i] -= b[i];\r
+    return a;\r
+}\r
+\r
+//IMPL: ScalableConcept\r
+template <typename T>\r
+inline D2<T>\r
+operator-(D2<T> const & a) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+    D2<T> r;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        r[i] = -a[i];\r
+    return r;\r
+}\r
+template <typename T>\r
+inline D2<T>\r
+operator*(D2<T> const & a, Point const & b) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+\r
+    D2<T> r;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        r[i] = a[i] * b[i];\r
+    return r;\r
+}\r
+template <typename T>\r
+inline D2<T>\r
+operator/(D2<T> const & a, Point const & b) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+    //TODO: b==0?\r
+    D2<T> r;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        r[i] = a[i] / b[i];\r
+    return r;\r
+}\r
+template <typename T>\r
+inline D2<T>\r
+operator*=(D2<T> &a, Point const & b) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+\r
+    for(unsigned i = 0; i < 2; i++)\r
+        a[i] *= b[i];\r
+    return a;\r
+}\r
+template <typename T>\r
+inline D2<T>\r
+operator/=(D2<T> &a, Point const & b) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+    //TODO: b==0?\r
+    for(unsigned i = 0; i < 2; i++)\r
+        a[i] /= b[i];\r
+    return a;\r
+}\r
+\r
+template <typename T>\r
+inline D2<T> operator*(D2<T> const & a, double b) { return D2<T>(a[0]*b, a[1]*b); }\r
+template <typename T> \r
+inline D2<T> operator*=(D2<T> & a, double b) { a[0] *= b; a[1] *= b; return a; }\r
+template <typename T>\r
+inline D2<T> operator/(D2<T> const & a, double b) { return D2<T>(a[0]/b, a[1]/b); }\r
+template <typename T> \r
+inline D2<T> operator/=(D2<T> & a, double b) { a[0] /= b; a[1] /= b; return a; }\r
+\r
+template<typename T>\r
+D2<T> operator*(D2<T> const &v, Matrix const &m) {\r
+    boost::function_requires<AddableConcept<T> >();\r
+    boost::function_requires<ScalableConcept<T> >();\r
+    D2<T> ret;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        ret[i] = v[X] * m[i] + v[Y] * m[i + 2] + m[i + 4];\r
+    return ret;\r
+}\r
+\r
+//IMPL: OffsetableConcept\r
+template <typename T>\r
+inline D2<T>\r
+operator+(D2<T> const & a, Point b) {\r
+    boost::function_requires<OffsetableConcept<T> >();\r
+    D2<T> r;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        r[i] = a[i] + b[i];\r
+    return r;\r
+}\r
+template <typename T>\r
+inline D2<T>\r
+operator-(D2<T> const & a, Point b) {\r
+    boost::function_requires<OffsetableConcept<T> >();\r
+    D2<T> r;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        r[i] = a[i] - b[i];\r
+    return r;\r
+}\r
+template <typename T>\r
+inline D2<T>\r
+operator+=(D2<T> & a, Point b) {\r
+    boost::function_requires<OffsetableConcept<T> >();\r
+    for(unsigned i = 0; i < 2; i++)\r
+        a[i] += b[i];\r
+    return a;\r
+}\r
+template <typename T>\r
+inline D2<T>\r
+operator-=(D2<T> & a, Point b) {\r
+    boost::function_requires<OffsetableConcept<T> >();\r
+    for(unsigned i = 0; i < 2; i++)\r
+        a[i] -= b[i];\r
+    return a;\r
+}\r
+\r
+template <typename T>\r
+inline T\r
+dot(D2<T> const & a, D2<T> const & b) {\r
+    boost::function_requires<AddableConcept<T> >();\r
+    boost::function_requires<MultiplicableConcept<T> >();\r
+\r
+    T r;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        r += a[i] * b[i];\r
+    return r;\r
+}\r
+\r
+template <typename T>\r
+inline T\r
+cross(D2<T> const & a, D2<T> const & b) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+    boost::function_requires<MultiplicableConcept<T> >();\r
+\r
+    return a[1] * b[0] - a[0] * b[1];\r
+}\r
+\r
+\r
+//equivalent to cw/ccw, for use in situations where rotation direction doesn't matter.\r
+template <typename T>\r
+inline D2<T>\r
+rot90(D2<T> const & a) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+    return D2<T>(-a[Y], a[X]);\r
+}\r
+\r
+//TODO: concepterize the following functions\r
+template <typename T>\r
+inline D2<T>\r
+compose(D2<T> const & a, T const & b) {\r
+    D2<T> r;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        r[i] = compose(a[i],b);\r
+    return r;\r
+}\r
+\r
+template <typename T>\r
+inline D2<T>\r
+compose_each(D2<T> const & a, D2<T> const & b) {\r
+    D2<T> r;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        r[i] = compose(a[i],b[i]);\r
+    return r;\r
+}\r
+\r
+template <typename T>\r
+inline D2<T>\r
+compose_each(T const & a, D2<T> const & b) {\r
+    D2<T> r;\r
+    for(unsigned i = 0; i < 2; i++)\r
+        r[i] = compose(a,b[i]);\r
+    return r;\r
+}\r
+\r
+\r
+template<typename T>\r
+inline Point\r
+D2<T>::operator()(double t) const {\r
+    Point p;\r
+    for(unsigned i = 0; i < 2; i++)\r
+       p[i] = (*this)[i](t);\r
+    return p;\r
+}\r
+\r
+//TODO: we might want to have this take a Point as the parameter.\r
+template<typename T>\r
+inline Point\r
+D2<T>::operator()(double x, double y) const {\r
+    Point p;\r
+    for(unsigned i = 0; i < 2; i++)\r
+       p[i] = (*this)[i](x, y);\r
+    return p;\r
+}\r
+\r
+\r
+template<typename T>\r
+D2<T> derivative(D2<T> const & a) {\r
+    return D2<T>(derivative(a[X]), derivative(a[Y]));\r
+}\r
+\r
+template<typename T>\r
+D2<T> integral(D2<T> const & a) {\r
+    return D2<T>(integral(a[X]), integral(a[Y]));\r
+}\r
+\r
+} //end namespace Geom\r
+\r
+\r
+\r
+//TODO: implement intersect\r
+\r
+\r
+#include "rect.h"\r
+#include "sbasis.h"\r
+#include "sbasis-2d.h"\r
+#include "piecewise.h"\r
+\r
+namespace Geom{\r
+\r
+//Some D2 Fragment implementation which requires rect:\r
+template <typename T>\r
+Rect bounds_fast(const D2<T> &a) {\r
+    boost::function_requires<FragmentConcept<T> >();        \r
+    return Rect(bounds_fast(a[X]), bounds_fast(a[Y]));\r
+}\r
+template <typename T>\r
+Rect bounds_exact(const D2<T> &a) {\r
+    boost::function_requires<FragmentConcept<T> >();        \r
+    return Rect(bounds_exact(a[X]), bounds_exact(a[Y]));\r
+}\r
+template <typename T>\r
+Rect bounds_local(const D2<T> &a, const Interval &t) {\r
+    boost::function_requires<FragmentConcept<T> >();        \r
+    return Rect(bounds_local(a[X], t), bounds_local(a[Y], t));\r
+}\r
+\r
+//D2<SBasis> specific decls:\r
+\r
+inline D2<SBasis> compose(D2<SBasis> const & a, SBasis const & b) {\r
+    return D2<SBasis>(compose(a[X], b), compose(a[Y], b));\r
+}\r
+\r
+SBasis L2(D2<SBasis> const & a, unsigned k);\r
+double L2(D2<double> const & a);\r
+\r
+inline D2<SBasis> portion(D2<SBasis> const &M, double t0, double t1){\r
+    return D2<SBasis>(portion(M[0],t0,t1),portion(M[1],t0,t1));\r
+}\r
+\r
+D2<SBasis> multiply(Linear const & a, D2<SBasis> const & b);\r
+inline D2<SBasis> operator*(Linear const & a, D2<SBasis> const & b) { return multiply(a, b); }\r
+D2<SBasis> multiply(SBasis const & a, D2<SBasis> const & b);\r
+inline D2<SBasis> operator*(SBasis const & a, D2<SBasis> const & b) { return multiply(a, b); }\r
+D2<SBasis> truncate(D2<SBasis> const & a, unsigned terms);\r
+\r
+unsigned sbasis_size(D2<SBasis> const & a);\r
+double tail_error(D2<SBasis> const & a, unsigned tail);\r
+\r
+//Piecewise<D2<SBasis> > specific decls:\r
+\r
+Piecewise<D2<SBasis> > sectionize(D2<Piecewise<SBasis> > const &a);\r
+D2<Piecewise<SBasis> > make_cuts_independant(Piecewise<D2<SBasis> > const &a);\r
+Piecewise<D2<SBasis> > rot90(Piecewise<D2<SBasis> > const &a);\r
+Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b);\r
+Piecewise<SBasis> cross(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b);\r
+\r
+Piecewise<D2<SBasis> > force_continuity(Piecewise<D2<SBasis> > const &f, \r
+                                        double tol=0,\r
+                                        bool closed=false);\r
+\r
+class CoordIterator\r
+: public std::iterator<std::input_iterator_tag, SBasis const>\r
+{\r
+public:\r
+  CoordIterator(std::vector<D2<SBasis> >::const_iterator const &iter, unsigned d) : impl_(iter), ix_(d) {}\r
+\r
+  inline bool operator==(CoordIterator const &other) { return other.impl_ == impl_; }\r
+  inline bool operator!=(CoordIterator const &other) { return other.impl_ != impl_; }\r
+\r
+  inline SBasis operator*() const {\r
+        return (*impl_)[ix_];\r
+  }\r
+\r
+  inline CoordIterator &operator++() {\r
+    ++impl_;\r
+    return *this;\r
+  }\r
+  inline CoordIterator operator++(int) {\r
+    CoordIterator old=*this;\r
+    ++(*this);\r
+    return old;\r
+  }\r
+\r
+private:\r
+  std::vector<D2<SBasis> >::const_iterator impl_;\r
+  unsigned ix_;\r
+};\r
+\r
+inline CoordIterator iterateCoord(Piecewise<D2<SBasis> > const &a, unsigned d) {\r
+    return CoordIterator(a.segs.begin(), d);\r
+}\r
+\r
+//bounds specializations with order\r
+inline Rect bounds_fast(D2<SBasis> const & s, unsigned order=0) {\r
+    return Rect(bounds_fast(s[X], order),\r
+                bounds_fast(s[Y], order));\r
+}\r
+inline Rect bounds_local(D2<SBasis> const & s, Interval i, unsigned order=0) {\r
+    return Rect(bounds_local(s[X], i, order),\r
+                bounds_local(s[Y], i, order));\r
+}\r
+};\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
+#endif\r
diff --git a/src/2geom/geom.cpp b/src/2geom/geom.cpp
new file mode 100644 (file)
index 0000000..d2f2ef2
--- /dev/null
@@ -0,0 +1,218 @@
+/**
+ *  \file src/geom.cpp
+ *  \brief Various geometrical calculations.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "geom.h"
+#include "point.h"
+
+/**
+ * Finds the intersection of the two (infinite) lines
+ * defined by the points p such that dot(n0, p) == d0 and dot(n1, p) == d1.
+ *
+ * If the two lines intersect, then \a result becomes their point of
+ * intersection; otherwise, \a result remains unchanged.
+ *
+ * This function finds the intersection of the two lines (infinite)
+ * defined by n0.X = d0 and x1.X = d1.  The algorithm is as follows:
+ * To compute the intersection point use kramer's rule:
+ * \verbatim
+ * convert lines to form
+ * ax + by = c
+ * dx + ey = f
+ *
+ * (
+ *  e.g. a = (x2 - x1), b = (y2 - y1), c = (x2 - x1)*x1 + (y2 - y1)*y1
+ * )
+ *
+ * In our case we use:
+ *   a = n0.x     d = n1.x
+ *   b = n0.y     e = n1.y
+ *   c = d0        f = d1
+ *
+ * so:
+ *
+ * adx + bdy = cd
+ * adx + aey = af
+ *
+ * bdy - aey = cd - af
+ * (bd - ae)y = cd - af
+ *
+ * y = (cd - af)/(bd - ae)
+ *
+ * repeat for x and you get:
+ *
+ * x = (fb - ce)/(bd - ae)                \endverbatim
+ *
+ * If the denominator (bd-ae) is 0 then the lines are parallel, if the
+ * numerators are then 0 then the lines coincide.
+ *
+ * \todo Why not use existing but outcommented code below
+ * (HAVE_NEW_INTERSECTOR_CODE)?
+ */
+IntersectorKind 
+line_intersection(Geom::Point const &n0, double const d0,
+                  Geom::Point const &n1, double const d1,
+                  Geom::Point &result)
+{
+    double denominator = dot(Geom::rot90(n0), n1);
+    double X = n1[Geom::Y] * d0 -
+        n0[Geom::Y] * d1;
+    /* X = (-d1, d0) dot (n0[Y], n1[Y]) */
+
+    if (denominator == 0) {
+        if ( X == 0 ) {
+            return coincident;
+        } else {
+            return parallel;
+        }
+    }
+
+    double Y = n0[Geom::X] * d1 -
+        n1[Geom::X] * d0;
+
+    result = Geom::Point(X, Y) / denominator;
+
+    return intersects;
+}
+
+
+
+
+/* ccw exists as a building block */
+int
+intersector_ccw(const Geom::Point& p0, const Geom::Point& p1,
+        const Geom::Point& p2)
+/* Determine which way a set of three points winds. */
+{
+    Geom::Point d1 = p1 - p0;
+    Geom::Point d2 = p2 - p0;
+    /* compare slopes but avoid division operation */
+    double c = dot(Geom::rot90(d1), d2);
+    if(c > 0)
+        return +1; // ccw - do these match def'n in header?
+    if(c < 0)
+        return -1; // cw
+
+    /* Colinear [or NaN].  Decide the order. */
+    if ( ( d1[0] * d2[0] < 0 )  ||
+         ( d1[1] * d2[1] < 0 ) ) {
+        return -1; // p2  <  p0 < p1
+    } else if ( dot(d1,d1) < dot(d2,d2) ) {
+        return +1; // p0 <= p1  <  p2
+    } else {
+        return 0; // p0 <= p2 <= p1
+    }
+}
+
+/** Determine whether two line segments intersect.  This doesn't find
+    the point of intersection, use the line_intersect function above,
+    or the segment_intersection interface below.
+
+    \pre neither segment is zero-length; i.e. p00 != p01 and p10 != p11.
+*/
+static bool
+segment_intersectp(Geom::Point const &p00, Geom::Point const &p01,
+                               Geom::Point const &p10, Geom::Point const &p11)
+{
+    if(p00 == p01) return false;
+    if(p10 == p11) return false;
+
+    /* true iff (    (the p1 segment straddles the p0 infinite line)
+     *           and (the p0 segment straddles the p1 infinite line) ). */
+    return ((intersector_ccw(p00,p01, p10)
+             *intersector_ccw(p00, p01, p11)) <=0 )
+        &&
+        ((intersector_ccw(p10,p11, p00)
+          *intersector_ccw(p10, p11, p01)) <=0 );
+}
+
+
+/** Determine whether \& where two line segments intersect.
+
+If the two segments don't intersect, then \a result remains unchanged.
+
+\pre neither segment is zero-length; i.e. p00 != p01 and p10 != p11.
+**/
+IntersectorKind
+segment_intersect(Geom::Point const &p00, Geom::Point const &p01,
+                              Geom::Point const &p10, Geom::Point const &p11,
+                              Geom::Point &result)
+{
+    if(segment_intersectp(p00, p01, p10, p11)) {
+        Geom::Point n0 = (p01 - p00).ccw();
+        double d0 = dot(n0,p00);
+
+        Geom::Point n1 = (p11 - p10).ccw();
+        double d1 = dot(n1,p10);
+        return line_intersection(n0, d0, n1, d1, result);
+    } else {
+        return no_intersection;
+    }
+}
+
+/** Determine whether \& where two line segments intersect.
+
+If the two segments don't intersect, then \a result remains unchanged.
+
+\pre neither segment is zero-length; i.e. p00 != p01 and p10 != p11.
+**/
+IntersectorKind
+line_twopoint_intersect(Geom::Point const &p00, Geom::Point const &p01,
+                        Geom::Point const &p10, Geom::Point const &p11,
+                        Geom::Point &result)
+{
+    Geom::Point n0 = (p01 - p00).ccw();
+    double d0 = dot(n0,p00);
+    
+    Geom::Point n1 = (p11 - p10).ccw();
+    double d1 = dot(n1,p10);
+    return line_intersection(n0, d0, n1, d1, result);
+}
+
+/**
+ * polyCentroid: Calculates the centroid (xCentroid, yCentroid) and area of a polygon, given its
+ * vertices (x[0], y[0]) ... (x[n-1], y[n-1]). It is assumed that the contour is closed, i.e., that
+ * the vertex following (x[n-1], y[n-1]) is (x[0], y[0]).  The algebraic sign of the area is
+ * positive for counterclockwise ordering of vertices in x-y plane; otherwise negative.
+
+ * Returned values: 
+    0 for normal execution; 
+    1 if the polygon is degenerate (number of vertices < 3);
+    2 if area = 0 (and the centroid is undefined).
+
+    * for now we require the path to be a polyline and assume it is closed.
+**/
+
+int centroid(std::vector<Geom::Point> p, Geom::Point& centroid, double &area) {
+    const unsigned n = p.size();
+    if (n < 3)
+        return 1;
+    Geom::Point centroid_tmp(0,0);
+    double atmp = 0;
+    for (unsigned i = n-1, j = 0; j < n; i = j, j++) {
+        const double ai = -cross(p[j], p[i]);
+        atmp += ai;
+        centroid_tmp += (p[j] + p[i])*ai; // first moment.
+    }
+    area = atmp / 2;
+    if (atmp != 0) {
+        centroid = centroid_tmp / (3 * atmp);
+        return 0;
+    }
+    return 2;
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/geom.h b/src/2geom/geom.h
new file mode 100644 (file)
index 0000000..5386edb
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ *  \file geom.h
+ *  \brief Various geometrical calculations
+ *
+ *  Authors:
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * Copyright (C) 1999-2002 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+//TODO: move somewhere else
+
+#include <vector>
+#include "point.h"
+
+enum IntersectorKind {
+    intersects = 0,
+    parallel,
+    coincident,
+    no_intersection
+};
+
+int
+intersector_ccw(const Geom::Point& p0, const Geom::Point& p1,
+               const Geom::Point& p2);
+
+/* intersectors */
+
+IntersectorKind
+line_intersection(Geom::Point const &n0, double const d0,
+                 Geom::Point const &n1, double const d1,
+                 Geom::Point &result);
+
+IntersectorKind
+segment_intersect(Geom::Point const &p00, Geom::Point const &p01,
+                 Geom::Point const &p10, Geom::Point const &p11,
+                 Geom::Point &result);
+
+IntersectorKind
+line_twopoint_intersect(Geom::Point const &p00, Geom::Point const &p01,
+                       Geom::Point const &p10, Geom::Point const &p11,
+                       Geom::Point &result);
+
+int centroid(std::vector<Geom::Point> p, Geom::Point& centroid, double &area);
diff --git a/src/2geom/interval.h b/src/2geom/interval.h
new file mode 100644 (file)
index 0000000..459f2cd
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * interval.h - Simple closed interval class
+ *
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com>
+ *
+ * Original Rect/Range code by:
+ *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   bulia byak <buliabyak@users.sf.net>
+ *   MenTaLguY <mental@rydia.net>
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+#ifndef SEEN_INTERVAL_H
+#define SEEN_INTERVAL_H
+
+#include <assert.h>
+#include "coord.h"
+
+#include <boost/optional/optional.hpp>
+
+namespace Geom {
+
+//
+class Interval {
+private:
+    Coord _b[2];
+
+public:
+    //TODO: I just know this'll pop up somewhere, starting off someone's interval at 0...  I can't see how to avoid this.
+    explicit Interval() { _b[0] = _b[1] = 0; }    
+    explicit Interval(Coord u) { _b[0] = _b[1] = u; }
+    Interval(Coord u, Coord v) {
+        if(u < v) {
+            _b[0] = u; _b[1] = v;
+        } else {
+            _b[0] = v; _b[1] = u;
+        }
+    }
+
+    double operator[](unsigned i) const {
+        assert(i < 2);
+        return _b[i];
+    }
+    double& operator[](unsigned i) { return _b[i]; }  //Trust the user...
+
+    Coord min() const { return _b[0]; }
+    Coord max() const { return _b[1]; }
+    Coord extent() const { return _b[1] - _b[0]; }
+    Coord middle() const { return (_b[1] + _b[0]) * 0.5; }
+
+    bool isEmpty() const { return _b[0] == _b[1]; }
+    bool contains(Coord val) const { return _b[0] <= val && val <= _b[1]; }
+    bool contains(const Interval & val) const { return _b[0] <= val._b[0] && val._b[1] <= _b[1]; }
+    bool intersects(const Interval & val) const {
+        return contains(val._b[0]) || contains(val._b[1]) || val.contains(*this);
+    }
+
+    static Interval fromArray(const Coord* c, int n) {
+        assert(n > 0);
+        Interval result(c[0]);
+        for(int i = 1; i < n; i++) result.extendTo(c[i]);
+        return result;
+    }
+
+    bool operator==(Interval other) { return _b[0] == other._b[0] && _b[1] == other._b[1]; }
+    bool operator!=(Interval other) { return _b[0] != other._b[0] || _b[1] != other._b[1]; }
+
+    //IMPL: OffsetableConcept
+    //TODO: rename output_type to something else in the concept
+    typedef Coord output_type;
+    Interval operator+(Coord amnt) {
+        return Interval(_b[0] + amnt, _b[1] + amnt);
+    }
+    Interval operator-(Coord amnt) {
+        return Interval(_b[0] - amnt, _b[1] - amnt);
+    }
+    Interval operator+=(Coord amnt) {
+        _b[0] += amnt; _b[1] += amnt;
+        return *this;
+    }
+    Interval operator-=(Coord amnt) {
+        _b[0] -= amnt; _b[1] -= amnt;
+        return *this;
+    }
+
+    //IMPL: ScalableConcept
+    Interval operator-() const { return Interval(*this); }
+    Interval operator*(Coord s) const { return Interval(_b[0]*s, _b[1]*s); }
+    Interval operator/(Coord s) const { return Interval(_b[0]/s, _b[1]/s); }
+    Interval operator*=(Coord s) {
+        if(s < 0) {
+            Coord temp = _b[0];
+            _b[0] = _b[1]*s;
+            _b[1] = temp*s;
+        } else {
+            _b[0] *= s;
+            _b[1] *= s;
+        }
+        return *this;
+    }
+    Interval operator/=(Coord s) {
+        //TODO: what about s=0?
+        if(s < 0) {
+            Coord temp = _b[0];
+            _b[0] = _b[1]/s;
+            _b[1] = temp/s;
+        } else {
+            _b[0] /= s;
+            _b[1] /= s;
+        }
+        return *this;
+    }
+
+    //TODO: NaN handleage for the next two?
+    //TODO: Evaluate if wrap behaviour is proper.
+    //If val > max, then rather than becoming a min==max range, it 'wraps' over
+    void setMin(Coord val) {
+        if(val > _b[1]) {
+            _b[0] = _b[1];
+            _b[1] = val;
+        } else {
+            _b[0] = val;
+        }
+    }
+    //If val < min, then rather than becoming a min==max range, it 'wraps' over
+    void setMax(Coord val) {
+        if(val < _b[0]) {
+            _b[1] = _b[0];
+            _b[0] = val;
+        } else {
+            _b[1] = val;
+        }
+    }
+
+    void extendTo(Coord val) {
+       if(val < _b[0]) _b[0] = val;
+       if(val > _b[1]) _b[1] = val;  //no else, as we want to handle NaN
+    }
+
+    void expandBy(double amnt) {
+        _b[0] -= amnt;
+        _b[1] += amnt;
+    }
+
+    void unionWith(const Interval & a) {
+        if(a._b[0] < _b[0]) _b[0] = a._b[0];
+        if(a._b[1] > _b[1]) _b[1] = a._b[1];
+    }
+};
+
+//IMPL: AddableConcept
+inline Interval operator+(const Interval & a, const Interval & b) {
+    return Interval(a.min() + b.min(), a.max() + b.max());
+}
+inline Interval operator-(const Interval & a, const Interval & b) {
+    return Interval(a.min() - b.max(), a.max() - b.min());
+}
+inline Interval operator+=(Interval & a, const Interval & b) { a = a + b; return a; }
+inline Interval operator-=(Interval & a, const Interval & b) { a = a - b; return a; }
+
+//There might be impls of this based off sign checks
+inline Interval operator*(const Interval & a, const Interval & b) {
+    Interval res(a.min() * b.min());
+    res.extendTo(a.min() * b.max());
+    res.extendTo(a.max() * b.min());
+    res.extendTo(a.max() * b.max());
+    return res;
+}
+inline Interval operator*=(Interval & a, const Interval & b) { a = a * b; return a; }
+
+/* reinstate if useful (doesn't do the proper thing for 0 inclusion)
+inline Interval operator/(const Interval & a, const Interval & b) {
+    Interval res(a.min() / b.min());
+    res.extendTo(a.min() / b.max());
+    res.extendTo(a.max() / b.min());
+    res.extendTo(a.max() / b.max());
+    return res;
+}
+inline Interval operator/=(Interval & a, const Interval & b) { a = a / b; return a; }
+*/
+
+// 'union' conflicts with C keyword
+inline Interval unify(const Interval & a, const Interval & b) {
+    return Interval(std::min(a.min(), b.min()),
+                    std::max(a.max(), b.max()));
+}
+inline boost::optional<Interval> intersect(const Interval & a, const Interval & b) {
+    Coord u = std::max(a.min(), b.min()),
+          v = std::min(a.max(), b.max());
+    //technically >= might be incorrect, but singulars suck
+    return u >= v ? boost::optional<Interval>()
+                  : boost::optional<Interval>(Interval(u, v));
+}
+
+}
+#endif //SEEN_INTERVAL_H
diff --git a/src/2geom/isnan.h b/src/2geom/isnan.h
new file mode 100644 (file)
index 0000000..8b9ffae
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef __ISNAN_H__
+#define __ISNAN_H__
+
+/*
+ * Temporary fix for various misdefinitions of isnan().
+ * isnan() is becoming undef'd in some .h files. 
+ * #include this last in your .cpp file to get it right.
+ *
+ * The problem is that isnan and isfinite are part of C99 but aren't part of
+ * the C++ standard (which predates C99).
+ *
+ * Authors:
+ *   Inkscape groupies and obsessive-compulsives
+ *
+ * Copyright (C) 2004 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ *
+ * 2005 modification hereby placed in public domain.  Probably supercedes the 2004 copyright
+ * for the code itself.
+ */
+
+#include <cmath>
+/* You might try changing the above to <cmath> if you have problems.
+ * Whether you use math.h or cmath, you may need to edit the .cpp file
+ * and/or other .h files to use the same header file.
+ */
+
+#if defined(__isnan)
+# define is_nan(_a) (__isnan(_a))      /* MacOSX/Darwin definition < 10.4 */
+#elif defined(WIN32) || defined(_isnan)
+# define is_nan(_a) (_isnan(_a))       /* Win32 definition */
+#elif defined(isnan) || defined(__FreeBSD__)
+# define is_nan(_a) (isnan(_a))                /* GNU definition */
+#else
+# define is_nan(_a) (std::isnan(_a))
+#endif
+/* If the above doesn't work, then try (a != a).
+ * Also, please report a bug as per http://www.inkscape.org/report_bugs.php,
+ * giving information about what platform and compiler version you're using.
+ */
+
+
+#if defined(__isfinite)
+# define is_finite(_a) (__isfinite(_a))        /* MacOSX/Darwin definition < 10.4 */
+#elif defined(isfinite)
+# define is_finite(_a) (isfinite(_a))
+#else
+# define is_finite(_a) (std::isfinite(_a))
+#endif
+/* If the above doesn't work, then try (finite(_a) && !isNaN(_a)) or (!isNaN((_a) - (_a))).
+ * Also, please report a bug as per http://www.inkscape.org/report_bugs.php,
+ * giving information about what platform and compiler version you're using.
+ */
+
+
+#endif /* __ISNAN_H__ */
diff --git a/src/2geom/linear.h b/src/2geom/linear.h
new file mode 100644 (file)
index 0000000..2b34646
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ *  linear.h - Linear fragment function class
+ *
+ *  Authors:
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 2006-2007 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_LINEAR_H
+#define SEEN_LINEAR_H
+#include "isnan.h"
+#include "interval.h"
+
+namespace Geom{
+
+inline double lerp(double t, double a, double b) { return a*(1-t) + b*t; }
+
+class SBasis;
+
+class Hat{
+public:
+    Hat () {}
+    Hat(double d) :d(d) {}
+    operator double() const { return d; }
+    double d;
+};
+
+class Tri{
+public:
+    Tri () {}
+    Tri(double d) :d(d) {}
+    operator double() const { return d; }
+    double d;
+};
+
+class Linear{
+public:
+    double a[2];
+    Linear() {}
+    Linear(double aa, double b) {a[0] = aa; a[1] = b;}
+    Linear(Hat h, Tri t) {
+        a[0] = double(h) - double(t)/2; 
+        a[1] = double(h) + double(t)/2;
+    }
+
+    Linear(Hat h) {
+        a[0] = double(h); 
+        a[1] = double(h);
+    }
+
+    double operator[](const int i) const {
+        assert(i >= 0);
+        assert(i < 2);
+        return a[i];
+    }
+    double& operator[](const int i) {
+        assert(i >= 0);
+        assert(i < 2);
+        return a[i];
+    }
+
+    //IMPL: FragmentConcept
+    typedef double output_type;
+    inline bool isZero() const { return a[0] == 0 && a[1] == 0; }
+    inline bool isFinite() const { return is_finite(a[0]) && is_finite(a[1]); }
+
+    inline double at0() const { return a[0]; }
+    inline double at1() const { return a[1]; }
+
+    inline double valueAt(double t) const { return lerp(t, a[0], a[1]); }
+    inline double operator()(double t) const { return valueAt(t); }
+
+    //defined in sbasis.h
+    inline SBasis toSBasis() const;
+
+    inline Interval bounds_exact() const { return Interval(a[0], a[1]); }
+    inline Interval bounds_fast() const { return bounds_exact(); }
+    inline Interval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
+
+    operator Tri() const {
+        return a[1] - a[0];
+    }
+    operator Hat() const {
+        return (a[1] + a[0])/2;
+    }
+};
+
+inline Linear reverse(Linear const &a) { return Linear(a[1], a[0]); }
+
+//IMPL: AddableConcept
+inline Linear operator+(Linear const & a, Linear const & b) {
+    return Linear(a[0] + b[0], a[1] + b[1]);
+}
+inline Linear operator-(Linear const & a, Linear const & b) {
+    return Linear(a[0] - b[0], a[1] - b[1]);
+}
+inline Linear& operator+=(Linear & a, Linear const & b) {
+    a[0] += b[0]; a[1] += b[1];
+    return a;
+}
+inline Linear& operator-=(Linear & a, Linear const & b) {
+    a[0] -= b[0]; a[1] -= b[1];
+    return a;
+}
+//IMPL: OffsetableConcept
+inline Linear operator+(Linear const & a, double b) {
+    return Linear(a[0] + b, a[1] + b);
+}
+inline Linear operator-(Linear const & a, double b) {
+    return Linear(a[0] - b, a[1] - b);
+}
+inline Linear& operator+=(Linear & a, double b) {
+    a[0] += b; a[1] += b;
+    return a;
+}
+inline Linear& operator-=(Linear & a, double b) {
+    a[0] -= b; a[1] -= b;
+    return a;
+}
+//IMPL: boost::EqualityComparableConcept
+inline bool operator==(Linear const & a, Linear const & b) {
+    return a[0] == b[0] && a[1] == b[1];
+}
+inline bool operator!=(Linear const & a, Linear const & b) {
+    return a[0] != b[0] || a[1] != b[1];
+}
+//IMPL: ScalableConcept
+inline Linear operator-(Linear const &a) {
+    return Linear(-a[0], -a[1]);
+}
+inline Linear operator*(Linear const & a, double b) {
+    return Linear(a[0]*b, a[1]*b);
+}
+inline Linear operator/(Linear const & a, double b) {
+    return Linear(a[0]/b, a[1]/b);
+}
+inline Linear operator*=(Linear & a, double b) {
+    a[0] *= b; a[1] *= b;
+    return a;
+}
+inline Linear operator/=(Linear & a, double b) {
+    a[0] /= b; a[1] /= b;
+    return a;
+}
+};
+
+#endif //SEEN_LINEAR_H
diff --git a/src/2geom/makefile.in b/src/2geom/makefile.in
new file mode 100644 (file)
index 0000000..15dab3f
--- /dev/null
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.\r
+\r
+@SET_MAKE@\r
+\r
+# Explicit so that it's the default rule.\r
+all:\r
+       cd .. && $(MAKE) 2geom/all\r
+\r
+clean %.a %.o:\r
+       cd .. && $(MAKE) 2geom/$@\r
+\r
+.PHONY: all clean\r
+\r
+OBJEXT = @OBJEXT@\r
+\r
+.SUFFIXES:\r
+.SUFFIXES: .a .$(OBJEXT)\r
diff --git a/src/2geom/matrix.cpp b/src/2geom/matrix.cpp
new file mode 100644 (file)
index 0000000..f537959
--- /dev/null
@@ -0,0 +1,243 @@
+#define __Geom_MATRIX_C__
+
+/** \file
+ * Various matrix routines.  Currently includes some Geom::Rotate etc. routines too.
+ */
+
+/*
+ * Authors:
+ *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   Michael G. Sloan <mgsloan@gmail.com>
+ *
+ * This code is in public domain
+ */
+
+#include "utils.h"
+#include "matrix.h"
+#include "point.h"
+
+namespace Geom {
+
+/** Creates a Matrix given an axis and origin point.
+ *  The axis is represented as two vectors, which represent skew, rotation, and scaling in two dimensions.
+ *  from_basis(Point(1, 0), Point(0, 1), Point(0, 0)) would return the identity matrix.
+
+ \param x_basis the vector for the x-axis.
+ \param y_basis the vector for the y-axis.
+ \param offset the translation applied by the matrix.
+ \return The new Matrix.
+ */
+//NOTE: Inkscape's version is broken, so when including this version, you'll have to search for code with this func
+//TODO: move to Matrix::from_basis
+Matrix from_basis(Point const x_basis, Point const y_basis, Point const offset) {
+    return Matrix(x_basis[X], x_basis[Y],
+                  y_basis[X], y_basis[Y],
+                  offset [X], offset [Y]);
+}
+
+Point Matrix::xAxis() const {
+    return Point(_c[0], _c[1]);
+}
+
+Point Matrix::yAxis() const {
+    return Point(_c[2], _c[3]);
+}
+
+/** Gets the translation imparted by the Matrix.
+ */
+Point Matrix::translation() const {
+    return Point(_c[4], _c[5]);
+}
+
+void Matrix::setXAxis(Point const &vec) {
+    for(int i = 0; i < 2; i++)
+        _c[i] = vec[i];
+}
+
+void Matrix::setYAxis(Point const &vec) {
+    for(int i = 0; i < 2; i++)
+        _c[i + 2] = vec[i];
+}
+
+/** Sets the translation imparted by the Matrix.
+ */
+void Matrix::setTranslation(Point const &loc) {
+    for(int i = 0; i < 2; i++)
+        _c[i + 4] = loc[i];
+}
+
+/** Calculates the amount of x-scaling imparted by the Matrix.  This is the scaling applied to
+ *  the original x-axis region.  It is \emph{not} the overall x-scaling of the transformation.
+ *  Equivalent to L2(m.xAxis())
+ */
+double Matrix::expansionX() const {
+    return sqrt(_c[0] * _c[0] + _c[1] * _c[1]);
+}
+
+/** Calculates the amount of y-scaling imparted by the Matrix.  This is the scaling applied before
+ *  the other transformations.  It is \emph{not} the overall y-scaling of the transformation. 
+ *  Equivalent to L2(m.yAxis())
+ */
+double Matrix::expansionY() const {
+    return sqrt(_c[2] * _c[2] + _c[3] * _c[3]);
+}
+
+void Matrix::setExpansionX(double val) {
+    double exp_x = expansionX();
+    if(!near(exp_x, 0.0)) {  //TODO: best way to deal with it is to skip op?
+        double coef = val / expansionX();
+        for(unsigned i=0;i<2;i++) _c[i] *= coef;
+    }
+}
+
+void Matrix::setExpansionY(double val) {
+    double exp_y = expansionY();
+    if(!near(exp_y, 0.0)) {  //TODO: best way to deal with it is to skip op?
+        double coef = val / expansionY();
+        for(unsigned i=2;i<4;i++) _c[i] *= coef;
+    }
+}
+
+/** Sets this matrix to be the Identity Matrix. */
+void Matrix::setIdentity() {
+    _c[0] = 1.0; _c[1] = 0.0;
+    _c[2] = 0.0; _c[3] = 1.0;
+    _c[4] = 0.0; _c[5] = 0.0;
+}
+
+bool Matrix::isIdentity(Coord const eps) const {
+    return near(_c[0], 1.0) && near(_c[1], 0.0) &&
+           near(_c[2], 0.0) && near(_c[3], 1.0) &&
+           near(_c[4], 0.0) && near(_c[5], 0.0);
+}
+
+/** Answers the question "Does this matrix perform a translation, and \em{only} a translation?"
+ \param eps an epsilon value defaulting to EPSILON
+ \return A bool representing yes/no.
+ */
+bool Matrix::isTranslation(Coord const eps) const {
+    return near(_c[0], 1.0) && near(_c[1], 0.0) &&
+           near(_c[2], 0.0) && near(_c[3], 1.0) &&
+           !near(_c[4], 0.0) && !near(_c[5], 0.0);
+}
+
+/** Answers the question "Does this matrix perform a scale, and \em{only} a Scale?"
+ \param eps an epsilon value defaulting to EPSILON
+ \return A bool representing yes/no.
+ */
+bool Matrix::isScale(Coord const eps) const {
+    return !near(_c[0], 1.0) || !near(_c[3], 1.0) &&  //NOTE: these are the diags, and the next line opposite diags
+           near(_c[1], 0.0) && near(_c[2], 0.0) && 
+           near(_c[4], 0.0) && near(_c[5], 0.0);
+}
+
+/** Answers the question "Does this matrix perform a uniform scale, and \em{only} a uniform scale?"
+ \param eps an epsilon value defaulting to EPSILON
+ \return A bool representing yes/no.
+ */
+bool Matrix::isUniformScale(Coord const eps) const {
+    return !near(_c[0], 1.0) && near(_c[0], _c[3]) &&
+           near(_c[1], 0.0) && near(_c[2], 0.0) &&  
+           near(_c[4], 0.0) && near(_c[5], 0.0);
+}
+
+/** Answers the question "Does this matrix perform a rotation, and \em{only} a rotation?"
+ \param eps an epsilon value defaulting to EPSILON
+ \return A bool representing yes/no.
+ */
+bool Matrix::isRotation(Coord const eps) const {
+    return !near(_c[0], _c[3]) && near(_c[1], -_c[2]) &&
+           near(_c[4], 0.0) && near(_c[5], 0.0) &&
+           near(_c[0]*_c[0] + _c[1]*_c[1], 1.0);
+}
+
+/** Returns the Scale/Rotate/skew part of the matrix without the translation part. */
+Matrix Matrix::without_translation() const {
+    return Matrix(_c[0], _c[1], _c[2], _c[3], 0, 0);
+}
+
+/** Attempts to calculate the inverse of a matrix.
+ *  This is a Matrix such that m * m.inverse() is very near (hopefully < epsilon difference) the identity Matrix.
+ *  \textbf{The Identity Matrix is returned if the matrix has no inverse.}
+ \return The inverse of the Matrix if defined, otherwise the Identity Matrix.
+ */
+Matrix Matrix::inverse() const {
+    Matrix d;
+
+    Geom::Coord const determ = det();
+    if (!near(determ, 0.0)) {
+        Geom::Coord const ideterm = 1.0 / determ;
+
+        d._c[0] =  _c[3] * ideterm;
+        d._c[1] = -_c[1] * ideterm;
+        d._c[2] = -_c[2] * ideterm;
+        d._c[3] =  _c[0] * ideterm;
+        d._c[4] = -_c[4] * d._c[0] - _c[5] * d._c[2];
+        d._c[5] = -_c[4] * d._c[1] - _c[5] * d._c[3];
+    } else {
+        d.setIdentity();
+    }
+
+    return d;
+}
+
+/** Calculates the determinant of a Matrix. */
+Geom::Coord Matrix::det() const {
+    return _c[0] * _c[3] - _c[1] * _c[2];
+}
+
+/** Calculates the scalar of the descriminant of the Matrix.
+ *  This is simply the absolute value of the determinant.
+ */
+Geom::Coord Matrix::descrim2() const {
+    return fabs(det());
+}
+
+/** Calculates the descriminant of the Matrix. */
+Geom::Coord Matrix::descrim() const {
+    return sqrt(descrim2());
+}
+
+Matrix operator*(Matrix const &m0, Matrix const &m1) {
+    Matrix ret;
+    for(int a = 0; a < 5; a += 2) {
+        for(int b = 0; b < 2; b++) {
+            ret[a + b] = m0[a] * m1[b] + m0[a + 1] * m1[b + 2];
+        }
+    }
+    ret[4] += m1[4];
+    ret[5] += m1[5];
+    return ret;
+}
+
+//TODO: What's this!?!
+Matrix elliptic_quadratic_form(Matrix const &m) {
+    double const od = m[0] * m[1]  +  m[2] * m[3];
+    return Matrix(m[0]*m[0] + m[1]*m[1], od,
+                  od, m[2]*m[2] + m[3]*m[3],
+                  0, 0);
+}
+
+Eigen::Eigen(Matrix const &m) {
+    double const B = -m[0] - m[3];
+    double const C = m[0]*m[3] - m[1]*m[2];
+    double const center = -B/2.0;
+    double const delta = sqrt(B*B-4*C)/2.0;
+    values[0] = center + delta; values[1] = center - delta;
+    for (int i = 0; i < 2; i++) {
+        vectors[i] = unit_vector(rot90(Point(m[0]-values[i], m[1])));
+    }
+}
+
+}  //namespace Geom
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/matrix.h b/src/2geom/matrix.h
new file mode 100644 (file)
index 0000000..6a45b50
--- /dev/null
@@ -0,0 +1,158 @@
+#ifndef __Geom_MATRIX_H__
+#define __Geom_MATRIX_H__
+
+/** \file
+ * Definition of Geom::Matrix types.
+ *
+ * Main authors:
+ *   Lauris Kaplinski <lauris@kaplinski.com>:
+ *     Original NRMatrix definition and related macros.
+ *
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>:
+ *     Geom::Matrix class version of the above.
+ *
+ *   Michael G. Sloan <mgsloan@gmail.com>:
+ *     Reorganization and additions.
+ *
+ * This code is in public domain.
+ */
+
+//#include <glib/gmessages.h>
+
+#include "point.h"
+
+namespace Geom {
+
+/**
+ * The Matrix class.
+ * 
+ * For purposes of multiplication, points should be thought of as row vectors
+ *
+ * \f$(p_X p_Y 1)\f$
+ *
+ * to be right-multiplied by transformation matrices of the form
+ * \f[
+   \left[
+   \begin{array}{ccc}
+    c_0&c_1&0 \\
+    c_2&c_3&0 \\
+    c_4&c_5&1
+   \end{array}
+   \right]
+   \f]
+ * (so the columns of the matrix correspond to the columns (elements) of the result,
+ * and the rows of the matrix correspond to columns (elements) of the "input").
+ */
+class Matrix {
+  private:
+    Coord _c[6];
+  public:
+    Matrix() {}
+
+    Matrix(Matrix const &m) {
+        for(int i = 0; i < 6; i++) {
+            _c[i] = m[i];
+        }
+    }
+
+    Matrix(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5) {
+        _c[0] = c0; _c[1] = c1;
+        _c[2] = c2; _c[3] = c3;
+        _c[4] = c4; _c[5] = c5;
+    }
+
+    Matrix &operator=(Matrix const &m) {
+        for(int i = 0; i < 6; i++)
+            _c[i] = m._c[i];
+        return *this;
+    }
+
+    inline Coord operator[](unsigned const i) const { return _c[i]; }
+    inline Coord &operator[](unsigned const i) { return _c[i]; }
+
+
+    Point xAxis() const;
+    Point yAxis() const;
+    Point translation() const;
+    void setXAxis(Point const &vec);
+    void setYAxis(Point const &vec);
+    void setTranslation(Point const &loc);
+
+    double expansionX() const;
+    double expansionY() const;
+    void setExpansionX(double val);
+    void setExpansionY(double val);
+
+    void setIdentity();
+
+    bool isIdentity(Coord eps = EPSILON) const;
+    bool isTranslation(Coord eps = EPSILON) const;
+    bool isRotation(double eps = EPSILON) const;
+    bool isScale(double eps = EPSILON) const;
+    bool isUniformScale(double eps = EPSILON) const;
+
+    Matrix without_translation() const;
+
+    Matrix inverse() const;
+
+    Coord det() const;
+    Coord descrim2() const;
+    Coord descrim() const;
+};
+
+Matrix operator*(Matrix const &a, Matrix const &b);
+
+/** A function to print out the Matrix (for debugging) */
+inline std::ostream &operator<< (std::ostream &out_file, const Geom::Matrix &m) {
+    out_file << "A: " << m[0] << "  C: " << m[2] << "  E: " << m[4] << "\n";
+    out_file << "B: " << m[1] << "  D: " << m[3] << "  F: " << m[5] << "\n";
+    return out_file;
+}
+
+/** Given a matrix m such that unit_circle = m*x, this returns the
+ * quadratic form x*A*x = 1. */
+Matrix elliptic_quadratic_form(Matrix const &m);
+
+/** Given a matrix (ignoring the translation) this returns the eigen
+ * values and vectors. */
+class Eigen{
+public:
+    Point vectors[2];
+    double values[2];
+    Eigen(Matrix const &m);
+};
+
+// Matrix factories
+Matrix from_basis(const Point x_basis, const Point y_basis, const Point offset=Point(0,0));
+
+/** Returns the Identity Matrix. */
+inline Matrix identity() {
+    return Matrix(1.0, 0.0,
+                  0.0, 1.0,
+                  0.0, 0.0);
+}
+
+inline bool operator==(Matrix const &a, Matrix const &b) {
+    for(unsigned i = 0; i < 6; ++i) {
+        if ( a[i] != b[i] ) return false;
+    }
+    return true;
+}
+inline bool operator!=(Matrix const &a, Matrix const &b) { return !( a == b ); }
+
+
+
+} /* namespace Geom */
+
+#endif /* !__Geom_MATRIX_H__ */
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/path.cpp b/src/2geom/path.cpp
new file mode 100644 (file)
index 0000000..91868eb
--- /dev/null
@@ -0,0 +1,367 @@
+/*\r
+ * Path - Series of continuous curves\r
+ *   \r
+ * Copyright 2007  MenTaLguY <mental@rydia.net>\r
+ *     \r
+ * This library is free software; you can redistribute it and/or \r
+ * modify it either under the terms of the GNU Lesser General Public\r
+ * License version 2.1 as published by the Free Software Foundation\r
+ * (the "LGPL") or, at your option, under the terms of the Mozilla\r
+ * Public License Version 1.1 (the "MPL"). If you do not alter this \r
+ * notice, a recipient may use your version of this file under either\r
+ * the MPL or the LGPL.\r
+ *            \r
+ * You should have received a copy of the LGPL along with this library\r
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software \r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ * You should have received a copy of the MPL along with this library \r
+ * in the file COPYING-MPL-1.1\r
+ *                \r
+ * The contents of this file are subject to the Mozilla Public License\r
+ * Version 1.1 (the "License"); you may not use this file except in\r
+ * compliance with the License. You may obtain a copy of the License at\r
+ * http://www.mozilla.org/MPL/\r
+ *                   \r
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY\r
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for\r
+ * the specific language governing rights and limitations.\r
+ */\r
+\r
+#include "path.h"\r
+\r
+namespace Geom {\r
+\r
+namespace {\r
+\r
+enum Cmp {\r
+  LESS_THAN=-1,\r
+  GREATER_THAN=1,\r
+  EQUAL_TO=0\r
+};\r
+\r
+template <typename T1, typename T2>\r
+inline Cmp cmp(T1 const &a, T2 const &b) {\r
+  if ( a < b ) {\r
+    return LESS_THAN;\r
+  } else if ( b < a ) {\r
+    return GREATER_THAN;\r
+  } else {\r
+    return EQUAL_TO;\r
+  }\r
+}\r
+\r
+}\r
+\r
+boost::optional<int> CurveHelpers::sbasis_winding(D2<SBasis> const &sb, Point p) {\r
+  Interval ix = bounds_fast(sb[X]);\r
+\r
+  if ( p[X] > ix.max() ) { /* ray does not intersect bbox */\r
+    return 0;\r
+  }\r
+\r
+  SBasis fy = sb[Y];\r
+  fy -= p[Y];\r
+\r
+  if (fy.empty()) { /* coincident horizontal segment */\r
+    return boost::optional<int>();\r
+  }\r
+\r
+  if ( p[X] < ix.min() ) { /* ray does not originate in bbox */\r
+    double y = p[Y];\r
+    /* winding determined by position of endpoints */\r
+    Cmp initial_to_ray = cmp(fy[0][0], y);\r
+    Cmp final_to_ray = cmp(fy[0][1], y);\r
+    switch (cmp(final_to_ray, initial_to_ray)) {\r
+    case GREATER_THAN:\r
+      /* exclude final endpoint */\r
+      return ( final_to_ray != EQUAL_TO );\r
+    case LESS_THAN:\r
+      /* exclude final endpoint */\r
+      return -( final_to_ray != EQUAL_TO );\r
+    default:\r
+      /* any intersections cancel out */\r
+      return 0;\r
+    }\r
+  } else { /* ray originates in bbox */\r
+    std::vector<double> ts = roots(fy);\r
+\r
+    static const unsigned MAX_DERIVATIVES=8;\r
+    boost::optional<SBasis> ds[MAX_DERIVATIVES];\r
+    ds[0] = derivative(fy);\r
+\r
+    /* winding determined by summing signs of derivatives at intersections */\r
+    int winding=0;\r
+    for ( std::vector<double>::iterator ti = ts.begin()\r
+        ; ti != ts.end()\r
+        ; ++ti )\r
+    { \r
+      double t = *ti;\r
+      if ( sb[X](t) >= p[X] ) { /* root is ray intersection */\r
+        for ( boost::optional<SBasis> *di = ds\r
+            ; di != ( ds + MAX_DERIVATIVES )\r
+            ; ++di )\r
+        {\r
+          if (!*di) {\r
+            *di = derivative(**(di-1));\r
+          }\r
+          switch (cmp((**di)(t), 0)) {\r
+          case GREATER_THAN:\r
+            if ( t < 1 ) { /* exclude final endpoint */\r
+              winding += 1;\r
+            }\r
+            goto next_root;\r
+          case LESS_THAN:\r
+            if ( t < 1 ) { /* exclude final endpoint */\r
+              winding -= 1;\r
+            }\r
+            goto next_root;\r
+          default: (void)0;\r
+            /* give up */\r
+          };\r
+        }\r
+      } \r
+next_root: (void)0;\r
+    }\r
+    \r
+    return winding;\r
+  }\r
+}\r
+\r
+Rect BezierHelpers::bounds(unsigned degree, Point const *points) {\r
+  Point min=points[0];\r
+  Point max=points[0];\r
+  for ( unsigned i = 1 ; i <= degree ; ++i ) {\r
+    for ( unsigned axis = 0 ; axis < 2 ; ++axis ) {\r
+      min[axis] = std::min(min[axis], points[i][axis]);\r
+      max[axis] = std::max(max[axis], points[i][axis]);\r
+    }\r
+  }\r
+  return Rect(min, max);\r
+}\r
+\r
+Point BezierHelpers::point_and_derivatives_at(Coord t,\r
+                                              unsigned degree,\r
+                                              Point const *points,\r
+                                              unsigned n_derivs,\r
+                                              Point *derivs)\r
+{\r
+  return Point(0,0); // TODO\r
+}\r
+\r
+Geom::Point\r
+BezierHelpers::subdivideArr(Coord t,              // Parameter value\r
+                            unsigned degree,      // Degree of bezier curve\r
+                            Geom::Point const *V, // Control pts\r
+                            Geom::Point *Left,    // RETURN left half ctl pts\r
+                            Geom::Point *Right)   // RETURN right half ctl pts\r
+{\r
+    Geom::Point Vtemp[degree+1][degree+1];\r
+\r
+    /* Copy control points     */\r
+    std::copy(V, V+degree+1, Vtemp[0]);\r
+\r
+    /* Triangle computation    */\r
+    for (unsigned i = 1; i <= degree; i++) {   \r
+        for (unsigned j = 0; j <= degree - i; j++) {\r
+            Vtemp[i][j] = lerp(t, Vtemp[i-1][j], Vtemp[i-1][j+1]);\r
+        }\r
+    }\r
+    \r
+    for (unsigned j = 0; j <= degree; j++)\r
+        Left[j]  = Vtemp[j][0];\r
+    for (unsigned j = 0; j <= degree; j++)\r
+        Right[j] = Vtemp[degree-j][j];\r
+\r
+    return (Vtemp[degree][0]);\r
+}\r
+\r
+void Path::swap(Path &other) {\r
+  std::swap(curves_, other.curves_);\r
+  std::swap(closed_, other.closed_);\r
+  std::swap(*final_, *other.final_);\r
+  curves_[curves_.size()-1] = final_;\r
+  other.curves_[other.curves_.size()-1] = other.final_;\r
+}\r
+\r
+Rect Path::bounds_fast() const {\r
+  Rect bounds=front().bounds_fast();\r
+  for ( const_iterator iter=++begin(); iter != end() ; ++iter ) {\r
+    bounds.unionWith(iter->bounds_fast());\r
+  }\r
+  return bounds;\r
+}\r
+\r
+Rect Path::bounds_exact() const {\r
+  Rect bounds=front().bounds_exact();\r
+  for ( const_iterator iter=++begin(); iter != end() ; ++iter ) {\r
+    bounds.unionWith(iter->bounds_exact());\r
+  }\r
+  return bounds;\r
+}\r
+\r
+int Path::winding(Point p) const {\r
+  int winding = 0;\r
+  boost::optional<Cmp> ignore = boost::optional<Cmp>();\r
+  for ( const_iterator iter = begin()\r
+      ; iter != end_closed()\r
+      ; ++iter )\r
+  {\r
+    boost::optional<int> w = iter->winding(p);\r
+    if (w) {\r
+      winding += *w;\r
+      ignore = boost::optional<Cmp>();\r
+    } else {\r
+      Point initial = iter->initialPoint();\r
+      Point final = iter->finalPoint();\r
+      switch (cmp(initial[X], final[X])) {\r
+      case GREATER_THAN:\r
+        if ( !ignore || *ignore != GREATER_THAN ) { /* ignore repeated */\r
+          winding += 1;\r
+          ignore = GREATER_THAN;\r
+        }\r
+        break;\r
+      case LESS_THAN:\r
+        if ( !ignore || *ignore != LESS_THAN ) { /* ignore repeated */\r
+          if ( p[X] < final[X] ) { /* ignore final point */\r
+            winding -= 1;\r
+            ignore = LESS_THAN;\r
+          }\r
+        }\r
+        break;\r
+      case EQUAL_TO:\r
+        /* always ignore null segments */\r
+        break;\r
+      }\r
+    }\r
+  }\r
+  return winding;\r
+}\r
+\r
+void Path::append(Curve const &curve) {\r
+  if ( curves_.front() != final_ && curve.initialPoint() != (*final_)[0] ) {\r
+    throw ContinuityError();\r
+  }\r
+  do_append(curve.duplicate());\r
+}\r
+\r
+void Path::append(D2<SBasis> const &curve) {\r
+  if ( curves_.front() != final_ ) {\r
+    for ( int i = 0 ; i < 2 ; ++i ) {\r
+      if ( curve[i][0][0] != (*final_)[0][i] ) {\r
+        throw ContinuityError();\r
+      }\r
+    }\r
+  }\r
+  do_append(new SBasisCurve(curve));\r
+}\r
+\r
+void Path::do_update(Sequence::iterator first_replaced,\r
+                     Sequence::iterator last_replaced,\r
+                     Sequence::iterator first,\r
+                    Sequence::iterator last)\r
+{\r
+  // note: modifies the contents of [first,last)\r
+\r
+  check_continuity(first_replaced, last_replaced, first, last);\r
+  delete_range(first_replaced, last_replaced);\r
+  if ( ( last - first ) == ( last_replaced - first_replaced ) ) {\r
+    std::copy(first, last, first_replaced);\r
+  } else {\r
+    // this approach depends on std::vector's behavior WRT iterator stability\r
+    curves_.erase(first_replaced, last_replaced);\r
+    curves_.insert(first_replaced, first, last);\r
+  }\r
+\r
+  if ( curves_.front() != final_ ) {\r
+    (*final_)[0] = back().finalPoint();\r
+    (*final_)[1] = front().initialPoint();\r
+  }\r
+}\r
+\r
+void Path::do_append(Curve *curve) {\r
+  if ( curves_.front() == final_ ) {\r
+    (*final_)[1] = curve->initialPoint();\r
+  }\r
+  curves_.insert(curves_.end()-1, curve);\r
+  (*final_)[0] = curve->finalPoint();\r
+}\r
+\r
+void Path::delete_range(Sequence::iterator first, Sequence::iterator last) {\r
+  for ( Sequence::iterator iter=first ; iter != last ; ++iter ) {\r
+    delete *iter;\r
+  }\r
+}\r
+\r
+void Path::check_continuity(Sequence::iterator first_replaced,\r
+                            Sequence::iterator last_replaced,\r
+                            Sequence::iterator first,\r
+                            Sequence::iterator last)\r
+{\r
+  if ( first != last ) {\r
+    if ( first_replaced != curves_.begin() ) {\r
+      if ( (*first_replaced)->initialPoint() != (*first)->initialPoint() ) {\r
+        throw ContinuityError();\r
+      }\r
+    }\r
+    if ( last_replaced != (curves_.end()-1) ) {\r
+      if ( (*(last_replaced-1))->finalPoint() != (*(last-1))->finalPoint() ) {\r
+        throw ContinuityError();\r
+      }\r
+    }\r
+  } else if ( first_replaced != last_replaced && first_replaced != curves_.begin() && last_replaced != curves_.end()-1) {\r
+    if ( (*first_replaced)->initialPoint() !=\r
+         (*(last_replaced-1))->finalPoint() )\r
+    {\r
+      throw ContinuityError();\r
+    }\r
+  }\r
+}\r
+\r
+Rect SBasisCurve::bounds_fast() const {\r
+  throw NotImplemented();\r
+  return Rect(Point(0,0), Point(0,0));\r
+}\r
+\r
+Rect SBasisCurve::bounds_exact() const {\r
+  throw NotImplemented();\r
+  return Rect(Point(0,0), Point(0,0));\r
+}\r
+\r
+Point SBasisCurve::pointAndDerivativesAt(Coord t, unsigned n_derivs, Point *derivs) const {\r
+  throw NotImplemented();\r
+  return Point(0,0);\r
+}\r
+\r
+Path const &SBasisCurve::subdivide(Coord t, Path &out) const {\r
+  throw NotImplemented();\r
+}\r
+\r
+Rect SVGEllipticalArc::bounds_fast() const {\r
+    throw NotImplemented();\r
+}\r
+Rect SVGEllipticalArc::bounds_exact() const {\r
+    throw NotImplemented();\r
+}\r
+\r
+Point SVGEllipticalArc::pointAndDerivativesAt(Coord t, unsigned n_derivs, Point *derivs) const {\r
+    throw NotImplemented();\r
+}\r
+\r
+D2<SBasis> SVGEllipticalArc::sbasis() const {\r
+    throw NotImplemented();\r
+}\r
+\r
+}\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(substatement-open . 0))\r
+  indent-tabs-mode:nil\r
+  c-brace-offset:0\r
+  fill-column:99\r
+  End:\r
+  vim: filetype=cpp:expandtab:shiftwidth=2:tabstop=8:softtabstop=2 :\r
+*/\r
+\r
diff --git a/src/2geom/path.h b/src/2geom/path.h
new file mode 100644 (file)
index 0000000..31a7173
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ * Path - Series of continuous curves
+ *
+ * Copyright 2007  MenTaLguY <mental@rydia.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_GEOM_PATH_H
+#define SEEN_GEOM_PATH_H
+
+#include "point.h"
+#include <iterator>
+#include <algorithm>
+#include <exception>
+#include <stdexcept>
+#include <boost/optional/optional.hpp>
+#include "d2.h"
+#include "bezier-to-sbasis.h"
+
+namespace Geom {
+
+class Path;
+
+class Curve {
+public:
+  virtual ~Curve() {}
+
+  virtual Point initialPoint() const = 0;
+  virtual Point finalPoint() const = 0;
+
+  virtual Curve *duplicate() const = 0;
+
+  virtual Rect bounds_fast() const = 0;
+  virtual Rect bounds_exact() const = 0;
+
+  virtual boost::optional<int> winding(Point p) const = 0;
+
+  virtual Path const &subdivide(Coord t, Path &out) const = 0;
+
+  Point pointAt(Coord t) const { return pointAndDerivativesAt(t, 0, NULL); }
+  virtual Point pointAndDerivativesAt(Coord t, unsigned n, Point *ds) const = 0;
+  virtual D2<SBasis> sbasis() const = 0;
+};
+
+struct CurveHelpers {
+protected:
+  static boost::optional<int> sbasis_winding(D2<SBasis> const &sbasis, Point p);
+};
+
+struct BezierHelpers {
+protected:
+  static Rect bounds(unsigned degree, Point const *points);
+  static Point point_and_derivatives_at(Coord t,
+                                        unsigned degree, Point const *points,
+                                        unsigned n_derivs, Point *derivs);
+  static Point subdivideArr(Coord t, unsigned degree, Point const *V,
+                         Point *Left, Point *Right);
+
+};
+
+template <unsigned bezier_degree>
+class Bezier : public Curve, private CurveHelpers, private BezierHelpers {
+public:
+  template <unsigned required_degree>
+  static void assert_degree(Bezier<required_degree> const *) {}
+
+  Bezier() {}
+
+  // default copy
+  // default assign
+
+  Bezier(Point c0, Point c1) {
+    assert_degree<1>(this);
+    c_[0] = c0;
+    c_[1] = c1;
+  }
+
+  Bezier(Point c0, Point c1, Point c2) {
+    assert_degree<2>(this);
+    c_[0] = c0;
+    c_[1] = c1;
+    c_[2] = c2;
+  }
+
+  Bezier(Point c0, Point c1, Point c2, Point c3) {
+    assert_degree<3>(this);
+    c_[0] = c0;
+    c_[1] = c1;
+    c_[2] = c2;
+    c_[3] = c3;
+  }
+
+  unsigned degree() const { return bezier_degree; }
+
+  Curve *duplicate() const { return new Bezier(*this); }
+
+  Point initialPoint() const { return c_[0]; }
+  Point finalPoint() const { return c_[bezier_degree]; }
+
+  Point &operator[](int index) { return c_[index]; }
+  Point const &operator[](int index) const { return c_[index]; }
+
+  Rect bounds_fast() const { return bounds(bezier_degree, c_); }
+  Rect bounds_exact() const { return bounds(bezier_degree, c_); }
+
+  boost::optional<int> winding(Point p) const {
+    return sbasis_winding(sbasis(), p);
+  }
+
+  Path const &subdivide(Coord t, Path &out) const;
+  
+  Point pointAndDerivativesAt(Coord t, unsigned n_derivs, Point *derivs)
+  const
+  {
+    return point_and_derivatives_at(t, bezier_degree, c_, n_derivs, derivs);
+  }
+
+  D2<SBasis> sbasis() const {
+    return handles_to_sbasis<bezier_degree>(c_);
+  }
+
+protected:
+  Bezier(Point c[]) {
+    std::copy(c, c+bezier_degree+1, c_);
+  }
+
+private:
+  Point c_[bezier_degree+1];
+};
+
+// Bezier<0> is meaningless; specialize it out
+template<> class Bezier<0> { Bezier(); };
+
+typedef Bezier<1> LineSegment;
+typedef Bezier<2> QuadraticBezier;
+typedef Bezier<3> CubicBezier;
+
+class SVGEllipticalArc : public Curve, private CurveHelpers {
+public:
+  SVGEllipticalArc() {}
+
+  SVGEllipticalArc(Point initial, double rx, double ry,
+                   double x_axis_rotation, bool large_arc,
+                   bool sweep, Point final)
+  : initial_(initial), rx_(rx), ry_(ry), x_axis_rotation_(x_axis_rotation),
+    large_arc_(large_arc), sweep_(sweep), final_(final)
+  {}
+
+  Curve *duplicate() const { return new SVGEllipticalArc(*this); }
+
+  Point initialPoint() const { return initial_; }
+  Point finalPoint() const { return final_; }
+
+  Rect bounds_fast() const;
+  Rect bounds_exact() const;
+
+  boost::optional<int> winding(Point p) const {
+    return sbasis_winding(sbasis(), p);
+  }
+
+  Path const &subdivide(Coord t, Path &out) const;
+  
+  Point pointAndDerivativesAt(Coord t, unsigned n_derivs, Point *derivs) const;
+
+  D2<SBasis> sbasis() const;
+
+private:
+  Point initial_;
+  double rx_;
+  double ry_;
+  double x_axis_rotation_;
+  bool large_arc_;
+  bool sweep_;
+  Point final_;
+};
+
+class SBasisCurve : public Curve, private CurveHelpers {
+private:
+  SBasisCurve();
+public:
+  explicit SBasisCurve(D2<SBasis> const &coeffs)
+  : coeffs_(coeffs) {}
+
+  Point initialPoint() const {
+    return Point(coeffs_[X][0][0], coeffs_[Y][0][0]);
+  }
+  Point finalPoint() const {
+    return Point(coeffs_[X][0][1], coeffs_[Y][0][1]);
+  }
+
+  Curve *duplicate() const { return new SBasisCurve(*this); }
+
+  Rect bounds_fast() const;
+  Rect bounds_exact() const;
+
+  boost::optional<int> winding(Point p) const {
+    return sbasis_winding(coeffs_, p);
+  }
+
+  Path const &subdivide(Coord t, Path &out) const;
+
+  Point pointAndDerivativesAt(Coord t, unsigned n_derivs, Point *derivs) const;
+
+  D2<SBasis> sbasis() const { return coeffs_; }
+  
+private:
+  D2<SBasis> coeffs_;
+};
+
+template <typename IteratorImpl>
+class BaseIterator
+: public std::iterator<std::forward_iterator_tag, Curve const>
+{
+public:
+  BaseIterator() {}
+
+  // default construct
+  // default copy
+
+  bool operator==(BaseIterator const &other) {
+    return other.impl_ == impl_;
+  }
+  bool operator!=(BaseIterator const &other) {
+    return other.impl_ != impl_;
+  }
+
+  Curve const &operator*() const { return **impl_; }
+  Curve const *operator->() const { return *impl_; }
+
+  BaseIterator &operator++() {
+    ++impl_;
+    return *this;
+  }
+  BaseIterator operator++(int) {
+    BaseIterator old=*this;
+    ++(*this);
+    return old;
+  }
+
+private:
+  BaseIterator(IteratorImpl const &pos) : impl_(pos) {}
+
+  IteratorImpl impl_;
+  friend class Path;
+};
+
+template <typename Iterator>
+class DuplicatingIterator
+: public std::iterator<std::input_iterator_tag, Curve *>
+{
+public:
+  DuplicatingIterator() {}
+  DuplicatingIterator(Iterator const &iter) : impl_(iter) {}
+
+  bool operator==(DuplicatingIterator const &other) {
+    return other.impl_ == impl_;
+  }
+  bool operator!=(DuplicatingIterator const &other) {
+    return other.impl_ != impl_;
+  }
+
+  Curve *operator*() const { return (*impl_)->duplicate(); }
+
+  DuplicatingIterator &operator++() {
+    ++impl_;
+    return *this;
+  }
+  DuplicatingIterator operator++(int) {
+    DuplicatingIterator old=*this;
+    ++(*this);
+    return old;
+  }
+
+private:
+  Iterator impl_;
+};
+
+class ContinuityError : public std::runtime_error {
+public:
+  ContinuityError() : runtime_error("non-contiguous path") {}
+  ContinuityError(std::string const &message) : runtime_error(message) {}
+};
+
+class Path {
+private:
+  typedef std::vector<Curve *> Sequence;
+
+public:
+  typedef BaseIterator<Sequence::iterator> iterator;
+  typedef BaseIterator<Sequence::const_iterator> const_iterator;
+  typedef Sequence::size_type size_type;
+  typedef Sequence::difference_type difference_type;
+
+  Path()
+  : final_(new LineSegment()), closed_(false)
+  {
+    curves_.push_back(final_);
+  }
+
+  Path(Path const &other)
+  : final_(new LineSegment()), closed_(other.closed_)
+  {
+    curves_.push_back(final_);
+    insert(begin(), other.begin(), other.end());
+  }
+
+  explicit Path(Point p)
+  : final_(new LineSegment(p, p)), closed_(false)
+  {
+    curves_.push_back(final_);
+  }
+
+  template <typename Impl>
+  Path(BaseIterator<Impl> first, BaseIterator<Impl> last, bool closed=false)
+  : closed_(closed), final_(new LineSegment())
+  {
+    curves_.push_back(final_);
+    insert(begin(), first, last);
+  }
+
+  ~Path() {
+      delete_range(curves_.begin(), curves_.end()-1);
+      delete final_;
+  }
+
+  Path &operator=(Path const &other) {
+    clear();
+    insert(begin(), other.begin(), other.end());
+    close(other.closed_);
+    return *this;
+  }
+
+  void swap(Path &other);
+
+  Curve const &operator[](unsigned i) const { return *curves_[i]; }
+
+  iterator begin() { return curves_.begin(); }
+  iterator end() { return curves_.end()-1; }
+
+  Curve const &front() const { return *curves_[0]; }
+  Curve const &back() const { return *curves_[curves_.size()-2]; }
+
+  const_iterator begin() const { return curves_.begin(); }
+  const_iterator end() const { return curves_.end()-1; }
+
+  const_iterator end_open() const { return curves_.end()-1; }
+  const_iterator end_closed() const { return curves_.end(); }
+  const_iterator end_default() const {
+    return ( closed_ ? end_closed() : end_open() );
+  }
+
+  size_type size() const { return curves_.size()-1; }
+  size_type max_size() const { return curves_.max_size()-1; }
+
+  bool empty() const { return curves_.size() == 1; }
+  bool closed() const { return closed_; }
+  void close(bool closed=true) { closed_ = closed; }
+
+  int winding(Point p) const;
+
+  Rect bounds_fast() const;
+  Rect bounds_exact() const;
+
+  Piecewise<D2<SBasis> > toPwSb() const {
+    Piecewise<D2<SBasis> > ret;
+    ret.push_cut(0);
+    for(unsigned i = 0; i < size() + (closed_ ? 1 : 0); i++) {
+      ret.push(curves_[i]->sbasis(), i+1);
+    }
+    return ret;
+  }
+
+  void insert(iterator pos, Curve const &curve) {
+    Sequence source(1, curve.duplicate());
+    try {
+      do_update(pos.impl_, pos.impl_, source.begin(), source.end());
+    } catch (...) {
+      delete_range(source.begin(), source.end());
+      throw;
+    }
+  }
+
+  template <typename Impl>
+  void insert(iterator pos, BaseIterator<Impl> first, BaseIterator<Impl> last)
+  {
+    Sequence source(DuplicatingIterator<Impl>(first.impl_),
+                    DuplicatingIterator<Impl>(last.impl_));
+    try {
+      do_update(pos.impl_, pos.impl_, source.begin(), source.end());
+    } catch (...) {
+      delete_range(source.begin(), source.end());
+      throw;
+    }
+  }
+
+  void clear() {
+    do_update(curves_.begin(), curves_.end()-1,
+              curves_.begin(), curves_.begin());
+  }
+
+  void erase(iterator pos) {
+    do_update(pos.impl_, pos.impl_+1, curves_.begin(), curves_.begin());
+  }
+
+  void erase(iterator first, iterator last) {
+    do_update(first.impl_, last.impl_, curves_.begin(), curves_.begin());
+  }
+
+  void replace(iterator replaced, Curve const &curve) {
+    Sequence source(1, curve.duplicate());
+    try {
+      do_update(replaced.impl_, replaced.impl_+1, source.begin(), source.end());
+    } catch (...) {
+      delete_range(source.begin(), source.end());
+      throw;
+    }
+  }
+
+  void replace(iterator first_replaced, iterator last_replaced,
+               Curve const &curve)
+  {
+    Sequence source(1, curve.duplicate());
+    try {
+      do_update(first_replaced.impl_, last_replaced.impl_,
+                source.begin(), source.end());
+    } catch (...) {
+      delete_range(source.begin(), source.end());
+      throw;
+    }
+  }
+
+  template <typename Impl>
+  void replace(iterator replaced,
+               BaseIterator<Impl> first, BaseIterator<Impl> last)
+  {
+    Sequence source(DuplicatingIterator<Impl>(first.impl_),
+                    DuplicatingIterator<Impl>(last.impl_));
+    try {
+      do_update(replaced.impl_, replaced.impl_+1, source.begin(), source.end());
+    } catch (...) {
+      delete_range(source.begin(), source.end());
+      throw;
+    }
+  }
+
+  template <typename Impl>
+  void replace(iterator first_replaced, iterator last_replaced,
+               BaseIterator<Impl> first, BaseIterator<Impl> last)
+  {
+    Sequence source(first.impl_, last.impl_);
+    try {
+      do_update(first_replaced.impl_, last_replaced.impl_,
+                source.begin(), source.end());
+    } catch (...) {
+      delete_range(source.begin(), source.end());
+      throw;
+    }
+  }
+
+  void start(Point p) {
+    clear();
+    (*final_)[0] = (*final_)[1] = p;
+  }
+
+  Point initialPoint() const { return (*final_)[1]; }
+  Point finalPoint() const { return (*final_)[0]; }
+
+  void append(Curve const &curve);
+
+  void append(D2<SBasis> const &curve);
+
+  template <typename CurveType, typename A>
+  void appendNew(A a) {
+    do_append(new CurveType((*final_)[0], a));
+  }
+
+  template <typename CurveType, typename A, typename B>
+  void appendNew(A a, B b) {
+    do_append(new CurveType((*final_)[0], a, b));
+  }
+
+  template <typename CurveType, typename A, typename B, typename C>
+  void appendNew(A a, B b, C c) {
+    do_append(new CurveType((*final_)[0], a, b, c));
+  }
+
+  template <typename CurveType, typename A, typename B, typename C,
+                                typename D>
+  void appendNew(A a, B b, C c, D d) {
+    do_append(new CurveType((*final_)[0], a, b, c, d));
+  }
+
+  template <typename CurveType, typename A, typename B, typename C,
+                                typename D, typename E>
+  void appendNew(A a, B b, C c, D d, E e) {
+    do_append(new CurveType((*final_)[0], a, b, c, d, e));
+  }
+
+  template <typename CurveType, typename A, typename B, typename C,
+                                typename D, typename E, typename F>
+  void appendNew(A a, B b, C c, D d, E e, F f) {
+    do_append(new CurveType((*final_)[0], a, b, c, d, e, f));
+  }
+
+  template <typename CurveType, typename A, typename B, typename C,
+                                typename D, typename E, typename F,
+                                typename G>
+  void appendNew(A a, B b, C c, D d, E e, F f, G g) {
+    do_append(new CurveType((*final_)[0], a, b, c, d, e, f, g));
+  }
+
+  template <typename CurveType, typename A, typename B, typename C,
+                                typename D, typename E, typename F,
+                                typename G, typename H>
+  void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) {
+    do_append(new CurveType((*final_)[0], a, b, c, d, e, f, g, h));
+  }
+
+  template <typename CurveType, typename A, typename B, typename C,
+                                typename D, typename E, typename F,
+                                typename G, typename H, typename I>
+  void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
+    do_append(new CurveType((*final_)[0], a, b, c, d, e, f, g, h, i));
+  }
+
+private:
+  void do_update(Sequence::iterator first_replaced,
+                 Sequence::iterator last_replaced,
+                 Sequence::iterator first,
+                 Sequence::iterator last);
+
+  void do_append(Curve *curve);
+
+  void delete_range(Sequence::iterator first, Sequence::iterator last);
+
+  void check_continuity(Sequence::iterator first_replaced,
+                        Sequence::iterator last_replaced,
+                        Sequence::iterator first,
+                        Sequence::iterator last);
+
+  Sequence curves_;
+  LineSegment *final_;
+  bool closed_;
+};
+
+inline static Piecewise<D2<SBasis> > paths_to_pw(std::vector<Path> paths) {
+    Piecewise<D2<SBasis> > ret = paths[0].toPwSb();
+    for(unsigned i = 1; i < paths.size(); i++) {
+        ret.concat(paths[i].toPwSb());
+    }
+    return ret;
+}
+
+template <unsigned bezier_degree>
+inline Path const &Bezier<bezier_degree>::subdivide(Coord t, Path &out) const {
+  Bezier a, b;
+  subdivideArr(t, bezier_degree, c_, a.c_, b.c_);
+  out.clear();
+  out.close(false);
+  out.append(a);
+  out.append(b);
+  return out;
+}
+
+inline Path const &SVGEllipticalArc::subdivide(Coord t, Path &out) const {
+  SVGEllipticalArc a;
+  SVGEllipticalArc b;
+  a.rx_ = b.rx_ = rx_;
+  a.ry_ = b.ry_ = ry_;
+  a.x_axis_rotation_ = b.x_axis_rotation_ = x_axis_rotation_;
+  a.initial_ = initial_;
+  a.final_ = b.initial_ = pointAt(t);
+  b.final_ = final_;
+  out.clear();
+  out.close(false);
+  out.append(a);
+  out.append(b);
+  return out;
+}
+
+class Set {
+public:
+private:
+};
+
+}
+
+namespace std {
+
+template <>
+inline void swap<Geom::Path>(Geom::Path &a, Geom::Path &b)
+{
+  a.swap(b);
+}
+
+}
+
+#endif // SEEN_GEOM_PATH_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(substatement-open . 0))
+  indent-tabs-mode:nil
+  c-brace-offset:0
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=2:tabstop=8:softtabstop=2 :
diff --git a/src/2geom/piecewise.cpp b/src/2geom/piecewise.cpp
new file mode 100644 (file)
index 0000000..dc91ab4
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * piecewise.cpp - Piecewise function class
+ *
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com>
+ * Copyright 2007 JF Barraud
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "piecewise.h"
+#include <iterator>
+#include <map>
+
+namespace Geom {
+
+Piecewise<SBasis> divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, unsigned k) {
+    Piecewise<SBasis> pa = partition(a, b.cuts), pb = partition(b, a.cuts);
+    Piecewise<SBasis> ret = Piecewise<SBasis>();
+    assert(pa.size() == pb.size());
+    ret.cuts = pa.cuts;
+    for (unsigned i = 0; i < pa.size(); i++)
+        ret.push_seg(divide(pa[i], pb[i], k));
+    return ret;
+}
+
+Piecewise<SBasis> 
+divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero) {
+    Piecewise<SBasis> pa = partition(a, b.cuts), pb = partition(b, a.cuts);
+    Piecewise<SBasis> ret = Piecewise<SBasis>();
+    assert(pa.size() == pb.size());
+    for (unsigned i = 0; i < pa.size(); i++){
+        Piecewise<SBasis> divi = divide(pa[i], pb[i], tol, k, zero);
+        divi.setDomain(Interval(pa.cuts[i],pa.cuts[i+1]));
+        ret.concat(divi);
+    }
+    return ret;
+}
+Piecewise<SBasis> divide(Piecewise<SBasis> const &a, SBasis const &b, double tol, unsigned k, double zero){
+    return divide(a,Piecewise<SBasis>(b),tol,k,zero);
+}
+Piecewise<SBasis> divide(SBasis const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero){
+    return divide(Piecewise<SBasis>(a),b,tol,k,zero);
+}
+Piecewise<SBasis> divide(SBasis const &a, SBasis const &b, double tol, unsigned k, double zero) {
+    if (b.tailError(0)<2*zero){
+        //TODO: have a better look at sgn(b).
+        double sgn= (b(.5)<0.)?-1.:1;
+        return Piecewise<SBasis>(Linear(sgn/zero)*a);
+    }
+
+    if (fabs(b.at0())>zero && fabs(b.at1())>zero ){
+        SBasis c,r=a;
+        //TODO: what is a good relative tol? atm, c=a/b +/- (tol/a)%...
+        
+        k+=1;
+        r.resize(k, Linear(0,0));
+        c.resize(k, Linear(0,0));
+        
+        //assert(b.at0()!=0 && b.at1()!=0);
+        for (unsigned i=0; i<k; i++){
+            Linear ci = Linear(r[i][0]/b[0][0],r[i][1]/b[0][1]);
+            c[i]=ci;
+            r-=shift(ci*b,i);
+        }
+        
+        if (r.tailError(k)<tol) return Piecewise<SBasis>(c);
+    }
+    
+    Piecewise<SBasis> c0,c1;
+    c0 = divide(compose(a,Linear(0.,.5)),compose(b,Linear(0.,.5)),tol,k);
+    c1 = divide(compose(a,Linear(.5,1.)),compose(b,Linear(.5,1.)),tol,k);
+    c0.setDomain(Interval(0.,.5));
+    c1.setDomain(Interval(.5,1.));
+    c0.concat(c1);
+    return c0;
+}
+
+
+//-- compose(pw<T>,SBasis) ---------------
+/* 
+   the purpose of the following functions is only to reduce the code in piecewise.h
+   TODO: use a vector<pairs<double,unsigned> > instead of a map<double,unsigned>.
+ */
+
+std::map<double,unsigned> compose_pullback(std::vector<double> const &values, SBasis const &g){
+   std::map<double,unsigned> result;
+
+   std::vector<std::vector<double> > roots = multi_roots(g, values);
+   for(unsigned i=0; i<roots.size(); i++){
+       for(unsigned j=0; j<roots[i].size();j++){
+           result[roots[i][j]]=i;
+       }
+   }
+  // Also map 0 and 1 to the first value above(or =) g(0) and g(1).
+  if(result.count(0.)==0){
+      unsigned i=0;
+      while (i<values.size()&&(g.at0()>values[i])) i++;
+      result[0.]=i;
+  }
+  if(result.count(1.)==0){
+      unsigned i=0;
+      while (i<values.size()&&(g.at1()>values[i])) i++;
+      result[1.]=i;
+  }
+  return(result);
+}
+
+int compose_findSegIdx(std::map<double,unsigned>::iterator  const &cut,
+                       std::map<double,unsigned>::iterator  const &next,
+                       std::vector<double>  const &levels,
+                       SBasis const &g){
+    double     t0=(*cut).first;
+    unsigned idx0=(*cut).second;
+    double     t1=(*next).first;
+    unsigned idx1=(*next).second;
+    assert(t0<t1);
+    int  idx; //idx of the relevant f.segs
+    if (std::max(idx0,idx1)==levels.size()){ //g([t0,t1]) is above the top level,
+      idx=levels.size()-1;
+    } else if (idx0 != idx1){                //g([t0,t1]) crosses from level idx0 to idx1,
+      idx=std::min(idx0,idx1);
+    } else if(g((t0+t1)/2) < levels[idx0]) { //g([t0,t1]) is a 'U' under level idx0,
+      idx=idx0-1;
+    } else if(g((t0+t1)/2) > levels[idx0]) { //g([t0,t1]) is a 'bump' over level idx0,
+      idx=idx0;
+    } else {                                 //g([t0,t1]) is contained in level idx0!...
+      idx = (idx0==levels.size())? idx0-1:idx0;
+    }
+
+    //move idx back from levels f.cuts 
+    idx+=1;
+    return idx;
+}
+
+std::vector<double> roots(Piecewise<SBasis> const &f){
+    std::vector<double> result;
+    for (unsigned i=0; i<f.size(); i++){
+        std::vector<double> rts=roots(f.segs[i]);
+        rts=roots(f.segs[i]);
+
+        for (unsigned r=0; r<rts.size(); r++){
+            result.push_back(f.mapToDomain(rts[r], i));
+        }
+    }
+    return result;
+}
+
+}
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/piecewise.h b/src/2geom/piecewise.h
new file mode 100644 (file)
index 0000000..8823c21
--- /dev/null
@@ -0,0 +1,690 @@
+/*\r
+ * piecewise.h - Piecewise function class\r
+ *\r
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it either under the terms of the GNU Lesser General Public\r
+ * License version 2.1 as published by the Free Software Foundation\r
+ * (the "LGPL") or, at your option, under the terms of the Mozilla\r
+ * Public License Version 1.1 (the "MPL"). If you do not alter this\r
+ * notice, a recipient may use your version of this file under either\r
+ * the MPL or the LGPL.\r
+ *\r
+ * You should have received a copy of the LGPL along with this library\r
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ * You should have received a copy of the MPL along with this library\r
+ * in the file COPYING-MPL-1.1\r
+ *\r
+ * The contents of this file are subject to the Mozilla Public License\r
+ * Version 1.1 (the "License"); you may not use this file except in\r
+ * compliance with the License. You may obtain a copy of the License at\r
+ * http://www.mozilla.org/MPL/\r
+ *\r
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY\r
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for\r
+ * the specific language governing rights and limitations.\r
+ *\r
+ */\r
+\r
+#ifndef SEEN_GEOM_PW_SB_H\r
+#define SEEN_GEOM_PW_SB_H\r
+\r
+#include "sbasis.h"\r
+#include <vector>\r
+#include <map>\r
+\r
+#include "concepts.h"\r
+#include "isnan.h"\r
+#include <boost/concept_check.hpp>\r
+\r
+namespace Geom {\r
+\r
+template <typename T>\r
+class Piecewise {\r
+  BOOST_CLASS_REQUIRE(T, Geom, FragmentConcept);\r
+\r
+  public:\r
+    std::vector<double> cuts;\r
+    std::vector<T> segs;\r
+    //segs[i] stretches from cuts[i] to cuts[i+1].\r
+\r
+    Piecewise() {}\r
+\r
+    explicit Piecewise(const T &s) {\r
+        push_cut(0.);\r
+        push_seg(s);\r
+        push_cut(1.);\r
+    }\r
+\r
+    typedef typename T::output_type output_type;\r
+\r
+    explicit Piecewise(const output_type & v) {\r
+        push_cut(0.);\r
+        push_seg(T(v));\r
+        push_cut(1.);\r
+    }\r
+\r
+    inline T operator[](unsigned i) const { return segs[i]; }\r
+    inline T &operator[](unsigned i) { return segs[i]; }\r
+    inline output_type operator()(double t) const { return valueAt(t); }\r
+    inline output_type valueAt(double t) const {\r
+        unsigned n = segN(t);\r
+        return segs[n](segT(t, n));\r
+    }\r
+    //TODO: maybe it is not a good idea to have this?\r
+    Piecewise<T> operator()(SBasis f);\r
+    Piecewise<T> operator()(Piecewise<SBasis>f);\r
+\r
+    inline unsigned size() const { return segs.size(); }\r
+    inline bool empty() const { return segs.empty(); }\r
+\r
+    /**Convenience/implementation hiding function to add segment/cut pairs.\r
+     * Asserts that basic size and order invariants are correct\r
+     */\r
+    inline void push(const T &s, double to) {\r
+        assert(cuts.size() - segs.size() == 1);\r
+        push_seg(s);\r
+        push_cut(to);\r
+    }\r
+    //Convenience/implementation hiding function to add cuts.\r
+    inline void push_cut(double c) {\r
+        assert(cuts.empty() || c > cuts.back()); \r
+        cuts.push_back(c);\r
+    }\r
+    //Convenience/implementation hiding function to add segments.\r
+    inline void push_seg(const T &s) { segs.push_back(s); }\r
+\r
+    /**Returns the segment index which corresponds to a 'global' piecewise time.\r
+     * Also takes optional low/high parameters to expedite the search for the segment.\r
+     */\r
+    inline unsigned segN(double t, int low = 0, int high = -1) const {\r
+        high = (high == -1) ? size() : high;\r
+        if(t < cuts[0]) return 0;\r
+        if(t >= cuts[size()]) return size() - 1;\r
+        while(low < high) {\r
+            int mid = (high + low) / 2; //Lets not plan on having huge (> INT_MAX / 2) cut sequences\r
+            double mv = cuts[mid];\r
+            if(mv < t) {\r
+                if(t < cuts[mid + 1]) return mid; else low = mid + 1;\r
+            } else if(t < mv) {\r
+                if(cuts[mid - 1] < t) return mid - 1; else high = mid - 1;\r
+            } else {\r
+                return mid;\r
+            }\r
+        }\r
+        return low;\r
+    }\r
+\r
+    /**Returns the time within a segment, given the 'global' piecewise time.\r
+     * Also takes an optional index parameter which may be used for efficiency or to find the time on a\r
+     * segment outside its range.  If it is left to its default, -1, it will call segN to find the index.\r
+     */\r
+    inline double segT(double t, int i = -1) const {\r
+        if(i == -1) i = segN(t);\r
+        assert(i >= 0);\r
+        return (t - cuts[i]) / (cuts[i+1] - cuts[i]);\r
+    }\r
+\r
+    inline double mapToDomain(double t, unsigned i) const {\r
+        return (1-t)*cuts[i] + t*cuts[i+1]; //same as: t * (cuts[i+1] - cuts[i]) + cuts[i]\r
+    }    \r
+\r
+    //Offsets the piecewise domain\r
+    inline void offsetDomain(double o) {\r
+        assert(is_finite(o));\r
+        if(o != 0)\r
+            for(unsigned i = 0; i <= size(); i++)\r
+                cuts[i] += o;\r
+    }\r
+\r
+    //Scales the domain of the function by a value. 0 will result in an empty Piecewise.\r
+    inline void scaleDomain(double s) {\r
+        assert(s > 0);\r
+        if(s == 0) {\r
+            cuts.clear(); segs.clear();\r
+            return;\r
+        }\r
+        for(unsigned i = 0; i <= size(); i++)\r
+            cuts[i] *= s;\r
+    }\r
+\r
+    //Retrieves the domain in interval form\r
+    inline Interval domain() const { return Interval(cuts.front(), cuts.back()); }\r
+\r
+    //Transforms the domain into another interval\r
+    inline void setDomain(Interval dom) {\r
+        if(empty()) return;\r
+        if(dom.isEmpty()) {\r
+            cuts.clear(); segs.clear();\r
+            return;\r
+        }\r
+        double cf = cuts.front();\r
+        double o = dom.min() - cf, s = dom.extent() / (cuts.back() - cf);\r
+        for(unsigned i = 0; i <= size(); i++)\r
+            cuts[i] = (cuts[i] - cf) * s + o;\r
+    }\r
+\r
+    //Concatenates this Piecewise function with another, offseting time of the other to match the end.\r
+    inline void concat(const Piecewise<T> &other) {\r
+        if(other.empty()) return;\r
+\r
+        if(empty()) { \r
+            cuts = other.cuts; segs = other.segs;\r
+            return;\r
+        }\r
+\r
+        segs.insert(segs.end(), other.segs.begin(), other.segs.end());\r
+        double t = cuts.back() - other.cuts.front();        \r
+        for(unsigned i = 0; i < other.size(); i++)\r
+            push_cut(other.cuts[i + 1] + t);\r
+    }\r
+\r
+    //Like concat, but ensures continuity.\r
+    inline void continuousConcat(const Piecewise<T> &other) {\r
+        boost::function_requires<AddableConcept<typename T::output_type> >();\r
+        if(other.empty()) return;\r
+        typename T::output_type y = segs.back().at1() - other.segs.front().at0();\r
+\r
+        if(empty()) {\r
+            for(unsigned i = 0; i < other.size(); i++)\r
+                push_seg(other[i] + y);\r
+            cuts = other.cuts;\r
+            return;\r
+        }\r
+\r
+        double t = cuts.back() - other.cuts.front();\r
+        for(unsigned i = 0; i < other.size(); i++)\r
+            push(other[i] + y, other.cuts[i + 1] + t);\r
+    }\r
+\r
+    //returns true if the Piecewise<T> meets some basic invariants.\r
+    inline bool invariants() const {\r
+        // segs between cuts\r
+        if(!(segs.size() + 1 == cuts.size() || (segs.empty() && cuts.empty())))\r
+            return false;\r
+        // cuts in order\r
+        for(unsigned i = 0; i < segs.size(); i++)\r
+            if(cuts[i] >= cuts[i+1])\r
+                return false;\r
+        return true;\r
+    }\r
+\r
+};\r
+\r
+template<typename T>\r
+inline Interval bounds_fast(const Piecewise<T> &f) {\r
+    boost::function_requires<FragmentConcept<T> >();\r
+\r
+    if(f.empty()) return Interval(0);\r
+    Interval ret(bounds_fast(f[0]));\r
+    for(unsigned i = 1; i < f.size(); i++)\r
+        ret.unionWith(bounds_fast(f[i])); \r
+    return ret;\r
+}\r
+\r
+template<typename T>\r
+inline Interval bounds_exact(const Piecewise<T> &f) {\r
+    boost::function_requires<FragmentConcept<T> >();\r
+\r
+    if(f.empty()) return Interval(0);\r
+    Interval ret(bounds_exact(f[0]));\r
+    for(unsigned i = 1; i < f.size(); i++)\r
+        ret.unionWith(bounds_exact(f[i])); \r
+    return ret;\r
+}\r
+\r
+template<typename T>\r
+inline Interval bounds_local(const Piecewise<T> &f, const Interval &m) {\r
+    boost::function_requires<FragmentConcept<T> >();\r
+\r
+    if(f.empty()) return Interval(0);\r
+    if(m.isEmpty()) return Interval(f(m.min()));\r
+\r
+    unsigned fi = f.segN(m.min()), ti = f.segN(m.max());\r
+    double ft = f.segT(m.min(), fi), tt = f.segT(m.max(), ti);\r
+\r
+    if(fi == ti) return bounds_local(f[fi], Interval(ft, tt));\r
+\r
+    Interval ret(bounds_local(f[fi], Interval(ft, 1.)));\r
+    for(unsigned i = fi + 1; i < ti; i++)\r
+        ret.unionWith(bounds_exact(f[i]));\r
+    if(tt != 0.) ret.unionWith(bounds_local(f[ti], Interval(0., tt)));\r
+\r
+    return ret;\r
+}\r
+\r
+//returns a portion of a piece of a Piecewise<T>, given the piece's index and a to/from time.\r
+template<typename T>\r
+T elem_portion(const Piecewise<T> &a, unsigned i, double from, double to) {\r
+    assert(i < a.size());\r
+    double rwidth = 1 / (a.cuts[i+1] - a.cuts[i]);\r
+    return portion( a[i], (from - a.cuts[i]) * rwidth, (to - a.cuts[i]) * rwidth );\r
+}\r
+\r
+/**Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c);\r
+ * Further subdivides the Piecewise<T> such that there is a cut at every value in c.\r
+ * Precondition: c sorted lower to higher.\r
+ * \r
+ * //Given Piecewise<T> a and b:\r
+ * Piecewise<T> ac = a.partition(b.cuts);\r
+ * Piecewise<T> bc = b.partition(a.cuts);\r
+ * //ac.cuts should be equivalent to bc.cuts\r
+ */\r
+template<typename T>\r
+Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c) {\r
+    assert(pw.invariants());\r
+    if(c.empty()) return Piecewise<T>(pw);\r
+\r
+    Piecewise<T> ret = Piecewise<T>();\r
+    ret.cuts.reserve(c.size() + pw.cuts.size());\r
+    ret.segs.reserve(c.size() + pw.cuts.size() - 1);\r
+\r
+    if(pw.empty()) {\r
+        ret.cuts = c;\r
+        for(unsigned i = 0; i < c.size() - 1; i++)\r
+            ret.push_seg(T());\r
+        return ret;\r
+    }\r
+\r
+    unsigned si = 0, ci = 0;     //Segment index, Cut index\r
+\r
+    //if the cuts have something earlier than the Piecewise<T>, add portions of the first segment\r
+    while(c[ci] < pw.cuts.front() && ci < c.size()) {\r
+        bool isLast = (ci == c.size()-1 || c[ci + 1] >= pw.cuts.front());\r
+        ret.push_cut(c[ci]);\r
+        ret.push_seg( elem_portion(pw, 0, c[ci], isLast ? pw.cuts.front() : c[ci + 1]) );\r
+        ci++;\r
+    }\r
+\r
+    ret.push_cut(pw.cuts[0]);\r
+    double prev = pw.cuts[0];    //previous cut\r
+    //Loop which handles cuts within the Piecewise<T> domain\r
+    //Should have the cuts = segs + 1 invariant\r
+    while(si < pw.size() && ci <= c.size()) {\r
+        if(ci == c.size() && prev <= pw.cuts[si]) { //cuts exhausted, straight copy the rest\r
+            ret.segs.insert(ret.segs.end(), pw.segs.begin() + si, pw.segs.end());\r
+            ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + si + 1, pw.cuts.end());\r
+            return ret;\r
+        }else if(ci == c.size() || c[ci] >= pw.cuts[si + 1]) {  //no more cuts within this segment, finalize\r
+            if(prev > pw.cuts[si]) {      //segment already has cuts, so portion is required\r
+                ret.push_seg(portion(pw[si], pw.segT(prev, si), 1.0));\r
+            } else {                     //plain copy is fine\r
+                ret.push_seg(pw[si]);\r
+            }\r
+            ret.push_cut(pw.cuts[si + 1]);\r
+            prev = pw.cuts[si + 1];\r
+            si++;\r
+        } else if(c[ci] == pw.cuts[si]){                  //coincident\r
+            //Already finalized the seg with the code immediately above\r
+            ci++;\r
+        } else {                                         //plain old subdivision\r
+            ret.push(elem_portion(pw, si, prev, c[ci]), c[ci]);\r
+            prev = c[ci];\r
+            ci++;\r
+        }\r
+    }\r
+    \r
+    //input cuts extend further than this Piecewise<T>, extend the last segment.\r
+    while(ci < c.size()) {\r
+        if(c[ci] > prev) {\r
+            ret.push(elem_portion(pw, pw.size() - 1, prev, c[ci]), c[ci]);\r
+            prev = c[ci];\r
+        }\r
+        ci++;\r
+    }\r
+    return ret;\r
+}\r
+\r
+/**Piecewise<T> portion(const Piecewise<T> &pw, double from, double to);\r
+ * Returns a Piecewise<T> with a defined domain of [min(from, to), max(from, to)].\r
+ */\r
+template<typename T>\r
+Piecewise<T> portion(const Piecewise<T> &pw, double from, double to) {\r
+    if(pw.empty() || from == to) return Piecewise<T>();\r
+\r
+    Piecewise<T> ret;\r
+\r
+    double temp = from;\r
+    from = std::min(from, to);\r
+    to = std::max(temp, to);\r
+    \r
+    unsigned i = pw.segN(from);\r
+    ret.push_cut(from);\r
+    if(i == pw.size() - 1 || to < pw.cuts[i + 1]) {    //to/from inhabit the same segment\r
+        ret.push(elem_portion(pw, i, from, to), to);\r
+        return ret;\r
+    }\r
+    ret.push_seg(portion( pw[i], pw.segT(from, i), 1.0 ));\r
+    i++;\r
+    unsigned fi = pw.segN(to, i);\r
+\r
+    ret.segs.insert(ret.segs.end(), pw.segs.begin() + i, pw.segs.begin() + fi);  //copy segs\r
+    ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + i, pw.cuts.begin() + fi + 1);  //and their cuts\r
+\r
+    ret.push_seg( portion(pw[fi], 0.0, pw.segT(to, fi)));\r
+    if(to != ret.cuts.back()) ret.push_cut(to);\r
+    ret.invariants();\r
+    return ret;\r
+}\r
+\r
+template<typename T>\r
+Piecewise<T> remove_short_cuts(Piecewise<T> const &f, double tol) {\r
+    if(f.empty()) return f;\r
+    Piecewise<T> ret;\r
+    ret.push_cut(f.cuts[0]);\r
+    for(unsigned i=0; i<f.size(); i++){\r
+        if (f.cuts[i+1]-f.cuts[i] >= tol || i==f.size()-1) {\r
+            ret.push(f[i], f.cuts[i+1]);\r
+        }\r
+    }\r
+    return ret;\r
+}\r
+\r
+template<typename T>\r
+Piecewise<T> remove_short_cuts_extending(Piecewise<T> const &f, double tol) {\r
+    if(f.empty()) return f;\r
+    Piecewise<T> ret;\r
+    ret.push_cut(f.cuts[0]);\r
+    double last = f.cuts[0]; // last cut included\r
+    for(unsigned i=0; i<f.size(); i++){\r
+        if (f.cuts[i+1]-f.cuts[i] >= tol) {\r
+            ret.push(elem_portion(f, i, last, f.cuts[i+1]), f.cuts[i+1]);\r
+            last = f.cuts[i+1];\r
+        }\r
+    }\r
+    return ret;\r
+}\r
+\r
+template<typename T>\r
+std::vector<double> roots(const Piecewise<T> &pw) {\r
+    std::vector<double> ret;\r
+    for(unsigned i = 0; i < pw.size(); i++) {\r
+        std::vector<double> sr = roots(pw[i]);\r
+        for (unsigned j = 0; j < sr.size(); j++) ret.push_back(sr[j] * (pw.cuts[i + 1] - pw.cuts[i]) + pw.cuts[i]);\r
+\r
+    }\r
+    return ret;\r
+}\r
+\r
+//IMPL: OffsetableConcept\r
+template<typename T>\r
+Piecewise<T> operator+(Piecewise<T> const &a, typename T::output_type b) {\r
+    boost::function_requires<OffsetableConcept<T> >();\r
+//TODO:empty\r
+    Piecewise<T> ret = Piecewise<T>();\r
+    ret.cuts = a.cuts;\r
+    for(unsigned i = 0; i < a.size();i++)\r
+        ret.push_seg(a[i] + b);\r
+    return ret;\r
+}\r
+template<typename T>\r
+Piecewise<T> operator-(Piecewise<T> const &a, typename T::output_type b) {\r
+    boost::function_requires<OffsetableConcept<T> >();\r
+//TODO: empty\r
+    Piecewise<T> ret = Piecewise<T>();\r
+    ret.cuts = a.cuts;\r
+    for(unsigned i = 0; i < a.size();i++)\r
+        ret.push_seg(a[i] - b);\r
+    return ret;\r
+}\r
+template<typename T>\r
+Piecewise<T> operator+=(Piecewise<T>& a, typename T::output_type b) {\r
+    boost::function_requires<OffsetableConcept<T> >();\r
+\r
+    if(a.empty()) { a.push_cut(0.); a.push(T(b), 1.); return a; }\r
+\r
+    for(unsigned i = 0; i < a.size();i++)\r
+        a[i] += b;\r
+    return a;\r
+}\r
+template<typename T>\r
+Piecewise<T> operator-=(Piecewise<T>& a, typename T::output_type b) {\r
+    boost::function_requires<OffsetableConcept<T> >();\r
+\r
+    if(a.empty()) { a.push_cut(0.); a.push(T(b), 1.); return a; }\r
+\r
+    for(unsigned i = 0;i < a.size();i++)\r
+        a[i] -= b;\r
+    return a;\r
+}\r
+\r
+//IMPL: ScalableConcept\r
+template<typename T>\r
+Piecewise<T> operator-(Piecewise<T> const &a) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+\r
+    Piecewise<T> ret;\r
+    ret.cuts = a.cuts;\r
+    for(unsigned i = 0; i < a.size();i++)\r
+        ret.push_seg(- a[i]);\r
+    return ret;\r
+}\r
+template<typename T>\r
+Piecewise<T> operator*(Piecewise<T> const &a, double b) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+\r
+    if(a.empty()) return Piecewise<T>();\r
+\r
+    Piecewise<T> ret;\r
+    ret.cuts = a.cuts;\r
+    for(unsigned i = 0; i < a.size();i++)\r
+        ret.push_seg(a[i] * b);\r
+    return ret;\r
+}\r
+template<typename T>\r
+Piecewise<T> operator/(Piecewise<T> const &a, double b) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+\r
+    //FIXME: b == 0?\r
+    if(a.empty()) return Piecewise<T>();\r
+\r
+    Piecewise<T> ret;\r
+    ret.cuts = a.cuts;\r
+    for(unsigned i = 0; i < a.size();i++)\r
+        ret.push_seg(a[i] / b);\r
+    return ret;\r
+}\r
+template<typename T>\r
+Piecewise<T> operator*=(Piecewise<T>& a, double b) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+\r
+    if(a.empty()) return Piecewise<T>();\r
+\r
+    for(unsigned i = 0; i < a.size();i++)\r
+        a[i] *= b;\r
+    return a;\r
+}\r
+template<typename T>\r
+Piecewise<T> operator/=(Piecewise<T>& a, double b) {\r
+    boost::function_requires<ScalableConcept<T> >();\r
+\r
+    //FIXME: b == 0?\r
+    if(a.empty()) return Piecewise<T>();\r
+\r
+    for(unsigned i = 0; i < a.size();i++)\r
+        a[i] /= b;\r
+    return a;\r
+}\r
+\r
+//IMPL: AddableConcept\r
+template<typename T>\r
+Piecewise<T> operator+(Piecewise<T> const &a, Piecewise<T> const &b) {\r
+    boost::function_requires<AddableConcept<T> >();\r
+\r
+    Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts);\r
+    Piecewise<T> ret = Piecewise<T>();\r
+    assert(pa.size() == pb.size());\r
+    ret.cuts = pa.cuts;\r
+    for (unsigned i = 0; i < pa.size(); i++)\r
+        ret.push_seg(pa[i] + pb[i]);\r
+    return ret;\r
+}\r
+template<typename T>\r
+Piecewise<T> operator-(Piecewise<T> const &a, Piecewise<T> const &b) {\r
+    boost::function_requires<AddableConcept<T> >();\r
+\r
+    Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts);\r
+    Piecewise<T> ret = Piecewise<T>();\r
+    assert(pa.size() == pb.size());\r
+    ret.cuts = pa.cuts;\r
+    for (unsigned i = 0; i < pa.size(); i++)\r
+        ret.push_seg(pa[i] - pb[i]);\r
+    return ret;\r
+}\r
+template<typename T>\r
+inline Piecewise<T> operator+=(Piecewise<T> &a, Piecewise<T> const &b) {\r
+    a = a+b;\r
+    return a;\r
+}\r
+template<typename T>\r
+inline Piecewise<T> operator-=(Piecewise<T> &a, Piecewise<T> const &b) {\r
+    a = a-b;\r
+    return a;\r
+}\r
+\r
+template<typename T1,typename T2>\r
+Piecewise<T2> operator*(Piecewise<T1> const &a, Piecewise<T2> const &b) {\r
+    //function_requires<MultiplicableConcept<T1> >();\r
+    //function_requires<MultiplicableConcept<T2> >();\r
+\r
+    Piecewise<T1> pa = partition(a, b.cuts);\r
+    Piecewise<T2> pb = partition(b, a.cuts);\r
+    Piecewise<T2> ret = Piecewise<T2>();\r
+    assert(pa.size() == pb.size());\r
+    ret.cuts = pa.cuts;\r
+    for (unsigned i = 0; i < pa.size(); i++)\r
+        ret.push_seg(pa[i] * pb[i]);\r
+    return ret;\r
+}\r
+\r
+template<typename T>\r
+inline Piecewise<T> operator*=(Piecewise<T> &a, Piecewise<T> const &b) {\r
+    a = a * b;\r
+    return a;\r
+}\r
+\r
+Piecewise<SBasis> divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, unsigned k);\r
+//TODO: replace divide(a,b,k) by divide(a,b,tol,k)?\r
+//TODO: atm, relative error is ~(tol/a)%. Find a way to make it independant of a.\r
+//Nota: the result is 'truncated' where b is smaller than 'zero': ~ a/max(b,zero). \r
+Piecewise<SBasis> \r
+divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3);\r
+Piecewise<SBasis> \r
+divide(SBasis const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3);\r
+Piecewise<SBasis> \r
+divide(Piecewise<SBasis> const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3);\r
+Piecewise<SBasis> \r
+divide(SBasis const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3);\r
+\r
+//Composition: functions called compose_* are pieces of compose that are factored out in pw.cpp.\r
+std::map<double,unsigned> compose_pullback(std::vector<double> const &cuts, SBasis const &g);\r
+int compose_findSegIdx(std::map<double,unsigned>::iterator  const &cut,\r
+                       std::map<double,unsigned>::iterator  const &next,\r
+                       std::vector<double>  const &levels,\r
+                       SBasis const &g);\r
+\r
+//TODO: add concept check\r
+template<typename T>\r
+Piecewise<T> compose(Piecewise<T> const &f, SBasis const &g){\r
+    Piecewise<T> result;\r
+    if (f.empty()) return result;\r
+    if (g.isZero()) return Piecewise<T>(f(0));\r
+    if (f.size()==1){\r
+        double t0 = f.cuts[0], width = f.cuts[1] - t0;\r
+        return (Piecewise<T>) compose(f.segs[0],compose(Linear(-t0 / width, (1-t0) / width), g));\r
+    }\r
+    \r
+    //first check bounds...\r
+    Interval bs = bounds_fast(g);\r
+    if (f.cuts.front() > bs.max()  || bs.min() > f.cuts.back()){\r
+        int idx = (bs.max() < f.cuts[1]) ? 0 : f.cuts.size()-2;\r
+        double t0 = f.cuts[idx], width = f.cuts[idx+1] - t0;\r
+        return (Piecewise<T>) compose(f.segs[idx],compose(Linear(-t0 / width, (1-t0) / width), g));  \r
+    }\r
+    \r
+    std::vector<double> levels;//we can forget first and last cuts...\r
+    levels.insert(levels.begin(),f.cuts.begin()+1,f.cuts.end()-1);\r
+    //TODO: use a std::vector<pairs<double,unsigned> > instead of a map<double,unsigned>.\r
+    std::map<double,unsigned> cuts_pb = compose_pullback(levels,g);\r
+    \r
+    //-- Compose each piece of g with the relevant seg of f.\r
+    result.cuts.push_back(0.);\r
+    std::map<double,unsigned>::iterator cut=cuts_pb.begin();\r
+    std::map<double,unsigned>::iterator next=cut; next++;\r
+    while(next!=cuts_pb.end()){\r
+        //assert(std::abs(int((*cut).second-(*next).second))<1);\r
+        //TODO: find a way to recover from this error? the root finder missed some root;\r
+        //  the levels/variations of f might be too close/fast...\r
+        int idx = compose_findSegIdx(cut,next,levels,g);\r
+        double t0=(*cut).first;\r
+        double t1=(*next).first;\r
+        \r
+        SBasis sub_g=compose(g, Linear(t0,t1));\r
+        sub_g=compose(Linear(-f.cuts[idx]/(f.cuts[idx+1]-f.cuts[idx]),\r
+                             (1-f.cuts[idx])/(f.cuts[idx+1]-f.cuts[idx])),sub_g);\r
+        result.push(compose(f[idx],sub_g),t1);\r
+        cut++;\r
+        next++;\r
+    }\r
+    return(result);\r
+} \r
+\r
+//TODO: add concept check for following composition functions\r
+template<typename T>\r
+Piecewise<T> compose(Piecewise<T> const &f, Piecewise<SBasis> const &g){\r
+  Piecewise<T> result;\r
+  for(unsigned i = 0; i < g.segs.size(); i++){\r
+      Piecewise<T> fgi=compose(f, g.segs[i]);\r
+      fgi.setDomain(Interval(g.cuts[i], g.cuts[i+1]));\r
+      result.concat(fgi);\r
+  }\r
+  return result;\r
+}\r
+\r
+template <typename T>\r
+Piecewise<T> Piecewise<T>::operator()(SBasis f){return compose((*this),f);}\r
+template <typename T>\r
+Piecewise<T> Piecewise<T>::operator()(Piecewise<SBasis>f){return compose((*this),f);}\r
+\r
+template<typename T>\r
+Piecewise<T> integral(Piecewise<T> const &a) {\r
+    Piecewise<T> result;\r
+    result.segs.resize(a.segs.size());\r
+    result.cuts = a.cuts;\r
+    typename T::output_type c = a.segs[0].at0();\r
+    for(unsigned i = 0; i < a.segs.size(); i++){\r
+        result.segs[i] = integral(a.segs[i])*(a.cuts[i+1]-a.cuts[i]);\r
+        result.segs[i]+= c-result.segs[i].at0();\r
+        c = result.segs[i].at1();\r
+    }\r
+    return result;\r
+}\r
+\r
+template<typename T>\r
+Piecewise<T> derivative(Piecewise<T> const &a) {\r
+    Piecewise<T> result;\r
+    result.segs.resize(a.segs.size());\r
+    result.cuts = a.cuts;\r
+    for(unsigned i = 0; i < a.segs.size(); i++){\r
+        result.segs[i] = derivative(a.segs[i])/(a.cuts[i+1]-a.cuts[i]);\r
+    }\r
+    return result;\r
+}\r
+\r
+std::vector<double> roots(Piecewise<SBasis> const &f);\r
+\r
+}\r
+\r
+#endif //SEEN_GEOM_PW_SB_H\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
diff --git a/src/2geom/point-l.h b/src/2geom/point-l.h
new file mode 100644 (file)
index 0000000..cd4768d
--- /dev/null
@@ -0,0 +1,86 @@
+#ifndef SEEN_Geom_POINT_L_H
+#define SEEN_Geom_POINT_L_H
+
+#include <stdexcept>
+#include "point.h"
+
+namespace Geom {
+
+typedef long ICoord;
+
+class IPoint {
+    ICoord _pt[2];
+
+  public:
+    IPoint() { }
+
+    IPoint(ICoord x, ICoord y) {
+        _pt[X] = x;
+        _pt[Y] = y;
+    }
+
+    IPoint(NRPointL const &p) {
+        _pt[X] = p.x;
+        _pt[Y] = p.y;
+    }
+
+    IPoint(IPoint const &p) {
+        for (unsigned i = 0; i < 2; ++i) {
+            _pt[i] = p._pt[i];
+        }
+    }
+
+    IPoint &operator=(IPoint const &p) {
+        for (unsigned i = 0; i < 2; ++i) {
+            _pt[i] = p._pt[i];
+        }
+        return *this;
+    }
+
+    operator Point() {
+        return Point(_pt[X], _pt[Y]);
+    }
+
+    ICoord operator[](unsigned i) const throw(std::out_of_range) {
+        if ( i > Y ) throw std::out_of_range("index out of range");
+        return _pt[i];
+    }
+
+    ICoord &operator[](unsigned i) throw(std::out_of_range) {
+        if ( i > Y ) throw std::out_of_range("index out of range");
+        return _pt[i];
+    }
+
+    ICoord operator[](Dim2 d) const throw() { return _pt[d]; }
+    ICoord &operator[](Dim2 d) throw() { return _pt[d]; }
+
+    IPoint &operator+=(IPoint const &o) {
+        for ( unsigned i = 0 ; i < 2 ; ++i ) {
+            _pt[i] += o._pt[i];
+        }
+        return *this;
+    }
+  
+    IPoint &operator-=(IPoint const &o) {
+        for ( unsigned i = 0 ; i < 2 ; ++i ) {
+            _pt[i] -= o._pt[i];
+        }
+        return *this;
+    }
+};
+
+
+}  // namespace Geom
+
+#endif /* !SEEN_Geom_POINT_L_H */
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/point-ops.h b/src/2geom/point-ops.h
new file mode 100644 (file)
index 0000000..6f5eab5
--- /dev/null
@@ -0,0 +1,25 @@
+//[[[cog
+import operators
+
+setContext("Point", "Matrix", "Point")
+make({'*':'*='}, {'/':'/='})
+apsnd({'*':'/'}, "b.inverse()")
+
+setContext("Point", "double", "Point")
+make({'*=':'*'}, {'/=':'/'}, {'*':'*'}, {'*':'/'})
+
+setContext("Point", "Point", "bool")
+make({'==':'!='})
+
+setContext("Point", "Point", "Point")
+make({'+=':'+', '-=':'-'})
+]]]
+
+**************
+GENERATED CODE
+**************
+If you wish to modify, move function out of generation region and remove the
+cause of its generation.
+*/
+
+//[[[end]]]
diff --git a/src/2geom/point.cpp b/src/2geom/point.cpp
new file mode 100644 (file)
index 0000000..1e2a346
--- /dev/null
@@ -0,0 +1,164 @@
+#include "point.h"
+#include <assert.h>
+#include "coord.h"
+#include "isnan.h" //temporary fix for isnan()
+#include "matrix.h"
+
+namespace Geom {
+
+/** Scales this vector to make it a unit vector (within rounding error).
+ *
+ *  The current version tries to handle infinite coordinates gracefully,
+ *  but it's not clear that any callers need that.
+ *
+ *  \pre \f$this \neq (0, 0)\f$
+ *  \pre Neither component is NaN.
+ *  \post \f$-\epsilon<\left|this\right|-1<\epsilon\f$
+ */
+void Point::normalize() {
+    double len = hypot(_pt[0], _pt[1]);
+    if(len == 0) return;
+    if(is_nan(len)) return;
+    static double const inf = 1e400;
+    if(len != inf) {
+        *this /= len;
+    } else {
+        unsigned n_inf_coords = 0;
+        /* Delay updating pt in case neither coord is infinite. */
+        Point tmp;
+        for ( unsigned i = 0 ; i < 2 ; ++i ) {
+            if ( _pt[i] == inf ) {
+                ++n_inf_coords;
+                tmp[i] = 1.0;
+            } else if ( _pt[i] == -inf ) {
+                ++n_inf_coords;
+                tmp[i] = -1.0;
+            } else {
+                tmp[i] = 0.0;
+            }
+        }
+        switch (n_inf_coords) {
+        case 0: {
+            /* Can happen if both coords are near +/-DBL_MAX. */
+            *this /= 4.0;
+            len = hypot(_pt[0], _pt[1]);
+            assert(len != inf);
+            *this /= len;
+            break;
+        }
+        case 1: {
+            *this = tmp;
+            break;
+        }
+        case 2: {
+            *this = tmp * sqrt(0.5);
+            break;
+        }
+       }
+    }
+}
+
+/** Compute the L1 norm, or manhattan distance, of \a p. */
+Coord L1(Point const &p) {
+    Coord d = 0;
+    for ( int i = 0 ; i < 2 ; i++ ) {
+        d += fabs(p[i]);
+    }
+    return d;
+}
+
+/** Compute the L infinity, or maximum, norm of \a p. */
+Coord LInfty(Point const &p) {
+    Coord const a(fabs(p[0]));
+    Coord const b(fabs(p[1]));
+    return ( a < b || is_nan(b)
+             ? b
+             : a );
+}
+
+/** Returns true iff p is a zero vector, i.e.\ Point(0, 0).
+ *
+ *  (NaN is considered non-zero.)
+ */
+bool
+is_zero(Point const &p)
+{
+    return ( p[0] == 0 &&
+             p[1] == 0   );
+}
+
+bool
+is_unit_vector(Point const &p)
+{
+    return fabs(1.0 - L2(p)) <= 1e-4;
+    /* The tolerance of 1e-4 is somewhat arbitrary.  Point::normalize is believed to return
+       points well within this tolerance.  I'm not aware of any callers that want a small
+       tolerance; most callers would be ok with a tolerance of 0.25. */
+}
+
+Coord atan2(Point const p) {
+    return std::atan2(p[Y], p[X]);
+}
+
+/** compute the angle turning from a to b.  This should give \f$\pi/2\f$ for angle_between(a, rot90(a));
+ * This works by projecting b onto the basis defined by a, rot90(a)
+ */
+Coord angle_between(Point const a, Point const b) {
+    return std::atan2(cross(b,a), dot(b,a));
+}
+
+
+
+/** Returns a version of \a a scaled to be a unit vector (within rounding error).
+ *
+ *  The current version tries to handle infinite coordinates gracefully,
+ *  but it's not clear that any callers need that.
+ *
+ *  \pre a != Point(0, 0).
+ *  \pre Neither coordinate is NaN.
+ *  \post L2(ret) very near 1.0.
+ */
+Point unit_vector(Point const &a)
+{
+    Point ret(a);
+    ret.normalize();
+    return ret;
+}
+
+Point abs(Point const &b)
+{
+    Point ret;
+    for ( int i = 0 ; i < 2 ; i++ ) {
+        ret[i] = fabs(b[i]);
+    }
+    return ret;
+}
+
+Point operator*(Point const &v, Matrix const &m) {
+    Point ret;
+    for(int i = 0; i < 2; i++) {
+        ret[i] = v[X] * m[i] + v[Y] * m[i + 2] + m[i + 4];
+    }
+    return ret;
+}
+
+Point operator/(Point const &p, Matrix const &m) { return p * m.inverse(); }
+
+Point &Point::operator*=(Matrix const &m)
+{
+    *this = *this * m;
+    return *this;
+}
+
+}  //Namespace Geom
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/point.h b/src/2geom/point.h
new file mode 100644 (file)
index 0000000..0307769
--- /dev/null
@@ -0,0 +1,229 @@
+#ifndef SEEN_Geom_POINT_H
+#define SEEN_Geom_POINT_H
+
+/** \file
+ * Cartesian point class.
+ */
+
+#include <iostream>
+
+#include "coord.h"
+#include "utils.h"
+
+namespace Geom {
+
+enum Dim2 { X=0, Y=1 };
+
+class Matrix;
+
+/// Cartesian point.
+class Point {
+    Coord _pt[2];
+
+  public:
+    inline Point()
+    { _pt[X] = _pt[Y] = 0; }
+
+    inline Point(Coord x, Coord y) {
+        _pt[X] = x; _pt[Y] = y;
+    }
+
+    inline Point(Point const &p) {
+        for (unsigned i = 0; i < 2; ++i)
+            _pt[i] = p._pt[i];
+    }
+
+    inline Point &operator=(Point const &p) {
+        for (unsigned i = 0; i < 2; ++i)
+            _pt[i] = p._pt[i];
+        return *this;
+    }
+
+    inline Coord operator[](unsigned i) const { return _pt[i]; }
+    inline Coord &operator[](unsigned i) { return _pt[i]; }
+
+    Coord operator[](Dim2 d) const throw() { return _pt[d]; }
+    Coord &operator[](Dim2 d) throw() { return _pt[d]; }
+
+    static inline Point polar(Coord angle, Coord radius) {
+        return Point(radius * std::cos(angle), radius * std::sin(angle));
+    }
+
+    inline Coord length() const { return hypot(_pt[0], _pt[1]); }
+
+    /** Return a point like this point but rotated -90 degrees.
+        (If the y axis grows downwards and the x axis grows to the
+        right, then this is 90 degrees counter-clockwise.)
+    **/
+    Point ccw() const {
+        return Point(_pt[Y], -_pt[X]);
+    }
+
+    /** Return a point like this point but rotated +90 degrees.
+        (If the y axis grows downwards and the x axis grows to the
+        right, then this is 90 degrees clockwise.)
+    **/
+    Point cw() const {
+        return Point(-_pt[Y], _pt[X]);
+    }
+
+    /**
+        \brief A function to lower the precision of the point
+        \param  places  The number of decimal places that should be in
+                        the final number.
+    */
+    inline void round (int places = 0) {
+        _pt[X] = (Coord)(decimal_round((double)_pt[X], places));
+        _pt[Y] = (Coord)(decimal_round((double)_pt[Y], places));
+        return;
+    }
+
+    void normalize();
+
+    inline Point operator+(Point const &o) const {
+        return Point(_pt[X] + o._pt[X], _pt[Y] + o._pt[Y]);
+    }
+    inline Point operator-(Point const &o) const {
+        return Point(_pt[X] - o._pt[X], _pt[Y] - o._pt[Y]);
+    }
+    inline Point &operator+=(Point const &o) {
+        for ( unsigned i = 0 ; i < 2 ; ++i ) {
+            _pt[i] += o._pt[i];
+        }
+        return *this;
+    }  
+    inline Point &operator-=(Point const &o) {
+        for ( unsigned i = 0 ; i < 2 ; ++i ) {
+            _pt[i] -= o._pt[i];
+        }
+        return *this;
+    }
+
+    inline Point operator-() const {
+        return Point(-_pt[X], -_pt[Y]);
+    }
+    inline Point operator*(double const s) const {
+        return Point(_pt[X] * s, _pt[Y] * s);
+    }
+    inline Point operator/(double const s) const {
+        //TODO: s == 0?
+        return Point(_pt[X] / s, _pt[Y] / s);
+    }
+    inline Point &operator*=(double const s) {
+        for ( unsigned i = 0 ; i < 2 ; ++i ) _pt[i] *= s;
+        return *this;
+    }
+    inline Point &operator/=(double const s) {
+        //TODO: s == 0?
+        for ( unsigned i = 0 ; i < 2 ; ++i ) _pt[i] /= s;
+        return *this;
+    }
+
+    Point &operator*=(Matrix const &m);
+
+    inline int operator == (const Point &in_pnt) {
+        return ((_pt[X] == in_pnt[X]) && (_pt[Y] == in_pnt[Y]));
+    }
+
+    friend inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_pnt);
+};
+
+inline Point operator*(double const s, Point const &p) { return p * s; }
+
+/** A function to print out the Point.  It just prints out the coords
+    on the given output stream */
+inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_pnt) {
+    out_file << "X: " << in_pnt[X] << "  Y: " << in_pnt[Y];
+    return out_file;
+}
+
+/** This is a rotation (sort of). */
+inline Point operator^(Point const &a, Point const &b) {
+    Point const ret(a[0] * b[0] - a[1] * b[1],
+                    a[1] * b[0] + a[0] * b[1]);
+    return ret;
+}
+
+//IMPL: boost::EqualityComparableConcept
+inline bool operator==(Point const &a, Point const &b) {
+    return (a[X] == b[X]) && (a[Y] == b[Y]);
+}
+inline bool operator!=(Point const &a, Point const &b) {
+    return (a[X] != b[X]) || (a[Y] != b[Y]);
+}
+
+/** This is a lexicographical ordering for points.  It is remarkably useful for sweepline algorithms*/
+inline bool operator<=(Point const &a, Point const &b) {
+    return ( ( a[Y] < b[Y] ) ||
+             (( a[Y] == b[Y] ) && ( a[X] < b[X] )));
+}
+
+Coord L1(Point const &p);
+
+/** Compute the L2, or euclidean, norm of \a p. */
+inline Coord L2(Point const &p) { return p.length(); }
+
+/** Compute the square of L2 norm of \a p. Warning: this can overflow where L2 won't.*/
+inline Coord L2sq(Point const &p) { return p[0]*p[0] + p[1]*p[1]; }
+
+double LInfty(Point const &p);
+bool is_zero(Point const &p);
+bool is_unit_vector(Point const &p);
+
+extern double atan2(Point const p);
+/** compute the angle turning from a to b (signed). */
+extern double angle_between(Point const a, Point const b);
+
+//IMPL: NearConcept
+inline bool near(Point const &a, Point const &b, double const eps=EPSILON) {
+    return ( near(a[X],b[X],eps) && near(a[Y],b[Y],eps) );
+}
+
+/** Returns p * Geom::rotate_degrees(90), but more efficient.
+ *
+ * Angle direction in Inkscape code: If you use the traditional mathematics convention that y
+ * increases upwards, then positive angles are anticlockwise as per the mathematics convention.  If
+ * you take the common non-mathematical convention that y increases downwards, then positive angles
+ * are clockwise, as is common outside of mathematics.
+ *
+ * There is no rot_neg90 function: use -rot90(p) instead.
+ */
+inline Point rot90(Point const &p) { return Point(-p[Y], p[X]); }
+
+/** Given two points and a parameter t \in [0, 1], return a point
+ * proportionally from a to b by t.  Akin to 1 degree bezier.*/
+inline Point lerp(double const t, Point const a, Point const b) { return (a * (1 - t) + b * t); }
+
+Point unit_vector(Point const &a);
+
+/** compute the dot product (inner product) between the vectors a and b. */
+inline Coord dot(Point const &a, Point const &b) { return a[0] * b[0] + a[1] * b[1]; }
+/** Defined as dot(a, b.cw()). */
+inline Coord cross(Point const &a, Point const &b) { return dot(a, b.cw()); }
+
+/** compute the euclidean distance between points a and b.  TODO: hypot safer/faster? */
+inline Coord distance (Point const &a, Point const &b) { return L2(a - b); }
+
+/** compute the square of the distance between points a and b. */
+inline Coord distanceSq (Point const &a, Point const &b) { return L2sq(a - b); }
+
+Point abs(Point const &b);
+
+Point operator*(Point const &v, Matrix const &m);
+
+Point operator/(Point const &p, Matrix const &m);
+
+} /* namespace Geom */
+
+#endif /* !SEEN_Geom_POINT_H */
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/poly-dk-solve.cpp b/src/2geom/poly-dk-solve.cpp
new file mode 100644 (file)
index 0000000..dde230d
--- /dev/null
@@ -0,0 +1,64 @@
+#include "poly-dk-solve.h"
+#include <iterator>
+
+/*** implementation of the Durand-Kerner method.  seems buggy*/
+
+std::complex<double> evalu(Poly const & p, std::complex<double> x) {
+    std::complex<double> result = 0;
+    std::complex<double> xx = 1;
+    
+    for(unsigned i = 0; i < p.size(); i++) {
+        result += p[i]*xx;
+        xx *= x;
+    }
+    return result;
+}
+
+std::vector<std::complex<double> > DK(Poly const & ply, const double tol) {
+    std::vector<std::complex<double> > roots;
+    const int N = ply.degree();
+    
+    std::complex<double> b(0.4, 0.9);
+    std::complex<double> p = 1;
+    for(int i = 0; i < N; i++) {
+        roots.push_back(p);
+        p *= b;
+    }
+    assert(roots.size() == ply.degree());
+    
+    double error = 0;
+    int i;
+    for( i = 0; i < 30; i++) {
+        error = 0;
+        for(int r_i = 0; r_i < N; r_i++) {
+            std::complex<double> denom = 1;
+            std::complex<double> R = roots[r_i];
+            for(int d_i = 0; d_i < N; d_i++) {
+                if(r_i != d_i)
+                    denom *= R-roots[d_i];
+            }
+            assert(norm(denom) != 0);
+           std::complex<double> dr = evalu(ply, R)/denom;
+            error += norm(dr);
+            roots[r_i] = R - dr;
+        }
+        /*std::copy(roots.begin(), roots.end(), std::ostream_iterator<std::complex<double> >(std::cout, ",\t"));
+          std::cout << std::endl;*/
+        if(error < tol)
+            break;
+    }
+    //std::cout << error << ", " << i<< std::endl;
+    return roots;
+}
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/poly-dk-solve.h b/src/2geom/poly-dk-solve.h
new file mode 100644 (file)
index 0000000..9a4dd36
--- /dev/null
@@ -0,0 +1,5 @@
+#include "poly.h"
+#include <complex>
+
+std::vector<std::complex<double> > 
+DK(Poly const & ply, const double tol=1e-10);
diff --git a/src/2geom/poly-laguerre-solve.cpp b/src/2geom/poly-laguerre-solve.cpp
new file mode 100644 (file)
index 0000000..83f0492
--- /dev/null
@@ -0,0 +1,147 @@
+#include "poly-laguerre-solve.h"
+#include <iterator>
+
+typedef std::complex<double> cdouble;
+
+cdouble laguerre_internal_complex(Poly const & p,
+                                  double x0,
+                                  double tol,
+                                  bool & quad_root) {
+    cdouble a = 2*tol;
+    cdouble xk = x0;
+    double n = p.degree();
+    quad_root = false;
+    const unsigned shuffle_rate = 10;
+    static double shuffle[] = {0, 0.5, 0.25, 0.75, 0.125, 0.375, 0.625, 0.875, 1.0};
+    unsigned shuffle_counter = 0;
+    while(std::norm(a) > (tol*tol)) {
+        //std::cout << "xk = " << xk << std::endl;
+        cdouble b = p.back();
+        cdouble d = 0, f = 0;
+        double err = abs(b);
+        double abx = abs(xk);
+        for(int j = p.size()-2; j >= 0; j--) {
+            f = xk*f + d;
+            d = xk*d + b;
+            b = xk*b + p[j];
+            err = abs(b) + abx*err;
+        }
+        
+        err *= 1e-7; // magic epsilon for convergence, should be computed from tol
+        
+        cdouble px = b;
+        if(abs(b) < err)
+            return xk;
+        //if(std::norm(px) < tol*tol)
+        //    return xk;
+        cdouble G = d / px;
+        cdouble H = G*G - f / px;
+        
+        //std::cout << "G = " << G << "H = " << H;
+        cdouble radicand = (n - 1)*(n*H-G*G);
+        //assert(radicand.real() > 0);
+        if(radicand.real() < 0)
+            quad_root = true;
+        //std::cout << "radicand = " << radicand << std::endl;
+        if(G.real() < 0) // here we try to maximise the denominator avoiding cancellation
+            a = - sqrt(radicand);
+        else
+            a = sqrt(radicand);
+        //std::cout << "a = " << a << std::endl;
+        a = n / (a + G);
+        //std::cout << "a = " << a << std::endl;
+        if(shuffle_counter % shuffle_rate == 0)
+            ;//a *= shuffle[shuffle_counter / shuffle_rate];
+        xk -= a;
+        shuffle_counter++;
+        if(shuffle_counter >= 90)
+            break;
+    }
+    //std::cout << "xk = " << xk << std::endl;
+    return xk;
+}
+
+double laguerre_internal(Poly const & p,
+                         Poly const & pp,
+                         Poly const & ppp,
+                         double x0,
+                         double tol,
+                         bool & quad_root) {
+    double a = 2*tol;
+    double xk = x0;
+    double n = p.degree();
+    quad_root = false;
+    while(a*a > (tol*tol)) {
+        //std::cout << "xk = " << xk << std::endl;
+        double px = p(xk);
+        if(px*px < tol*tol)
+            return xk;
+        double G = pp(xk) / px;
+        double H = G*G - ppp(xk) / px;
+        
+        //std::cout << "G = " << G << "H = " << H;
+        double radicand = (n - 1)*(n*H-G*G);
+        assert(radicand > 0);
+        //std::cout << "radicand = " << radicand << std::endl;
+        if(G < 0) // here we try to maximise the denominator avoiding cancellation
+            a = - sqrt(radicand);
+        else
+            a = sqrt(radicand);
+        //std::cout << "a = " << a << std::endl;
+        a = n / (a + G);
+        //std::cout << "a = " << a << std::endl;
+        xk -= a;
+    }
+    //std::cout << "xk = " << xk << std::endl;
+    return xk;
+}
+
+
+std::vector<cdouble > 
+laguerre(Poly p, const double tol) {
+    std::vector<cdouble > solutions;
+    //std::cout << "p = " << p << " = ";
+    while(p.size() > 1)
+    {
+        double x0 = 0;
+        bool quad_root = false;
+        cdouble sol = laguerre_internal_complex(p, x0, tol, quad_root);
+        //if(abs(sol) > 1) break;
+        Poly dvs;
+        if(quad_root) {
+            dvs.push_back((sol*conj(sol)).real());
+            dvs.push_back(-(sol + conj(sol)).real());
+            dvs.push_back(1.0);
+            //std::cout << "(" <<  dvs << ")";
+           //solutions.push_back(sol);
+           //solutions.push_back(conj(sol));
+        } else {
+            //std::cout << sol << std::endl;
+            dvs.push_back(-sol.real());
+            dvs.push_back(1.0);
+           solutions.push_back(sol);
+            //std::cout << "(" <<  dvs << ")";
+        }
+        Poly r;
+        p = divide(p, dvs, r);
+        //std::cout << r << std::endl;
+    }
+    return solutions;
+}
+
+std::vector<double> 
+laguerre_real_interval(Poly const & ply, 
+                      const double lo, const double hi,
+                      const double tol) {
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/poly-laguerre-solve.h b/src/2geom/poly-laguerre-solve.h
new file mode 100644 (file)
index 0000000..86d1009
--- /dev/null
@@ -0,0 +1,10 @@
+#include "poly.h"
+#include <complex>
+
+std::vector<std::complex<double> > 
+laguerre(Poly ply, const double tol=1e-10);
+
+std::vector<double> 
+laguerre_real_interval(Poly  ply, 
+                      const double lo, const double hi,
+                      const double tol=1e-10);
diff --git a/src/2geom/poly.cpp b/src/2geom/poly.cpp
new file mode 100644 (file)
index 0000000..5c3a300
--- /dev/null
@@ -0,0 +1,197 @@
+#include "poly.h"
+
+Poly Poly::operator*(const Poly& p) const {
+    Poly result; 
+    result.resize(degree() +  p.degree()+1);
+    
+    for(unsigned i = 0; i < size(); i++) {
+        for(unsigned j = 0; j < p.size(); j++) {
+            result[i+j] += (*this)[i] * p[j];
+        }
+    }
+    return result;
+}
+#ifdef HAVE_GSL
+#include <gsl/gsl_poly.h>
+#endif
+
+/*double Poly::eval(double x) const {
+    return gsl_poly_eval(&coeff[0], size(), x);
+    }*/
+
+void Poly::normalize() {
+    while(back() == 0)
+        pop_back();
+}
+
+void Poly::monicify() {
+    normalize();
+    
+    double scale = 1./back(); // unitize
+    
+    for(unsigned i = 0; i < size(); i++) {
+        (*this)[i] *= scale;
+    }
+}
+
+
+#ifdef HAVE_GSL
+std::vector<std::complex<double> > solve(Poly const & pp) {
+    Poly p(pp);
+    p.normalize();
+    gsl_poly_complex_workspace * w 
+        = gsl_poly_complex_workspace_alloc (p.size());
+       
+    gsl_complex_packed_ptr z = new double[p.degree()*2];
+    double* a = new double[p.size()];
+    for(int i = 0; i < p.size(); i++)
+        a[i] = p[i];
+    std::vector<std::complex<double> > roots;
+    //roots.resize(p.degree());
+    
+    gsl_poly_complex_solve (a, p.size(), w, z);
+    delete[]a;
+     
+    gsl_poly_complex_workspace_free (w);
+     
+    for (int i = 0; i < p.degree(); i++) {
+        roots.push_back(std::complex<double> (z[2*i] ,z[2*i+1]));
+        //printf ("z%d = %+.18f %+.18f\n", i, z[2*i], z[2*i+1]);
+    }    
+    delete[] z;
+    return roots;
+}
+
+std::vector<double > solve_reals(Poly const & p) {
+    std::vector<std::complex<double> > roots = solve(p);
+    std::vector<double> real_roots;
+    
+    for(int i = 0; i < roots.size(); i++) {
+        if(roots[i].imag() == 0) // should be more lenient perhaps
+            real_roots.push_back(roots[i].real());
+    }
+    return real_roots;
+}
+#endif
+
+double polish_root(Poly const & p, double guess, double tol) {
+    Poly dp = derivative(p);
+    
+    double fn = p(guess);
+    while(fabs(fn) > tol) {
+        guess -= fn/dp(guess);
+        fn = p(guess);
+    }
+    return guess;
+}
+
+Poly integral(Poly const & p) {
+    Poly result;
+    
+    result.reserve(p.size()+1);
+    result.push_back(0); // arbitrary const
+    for(unsigned i = 0; i < p.size(); i++) {
+        result.push_back(p[i]/(i+1));
+    }
+    return result;
+
+}
+
+Poly derivative(Poly const & p) {
+    Poly result;
+    
+    if(p.size() <= 1)
+        return Poly(0);
+    result.reserve(p.size()-1);
+    for(unsigned i = 1; i < p.size(); i++) {
+        result.push_back(i*p[i]);
+    }
+    return result;
+}
+
+Poly compose(Poly const & a, Poly const & b) {
+    Poly result;
+    
+    for(unsigned i = a.size()-1; i >=0; i--) {
+        result = Poly(a[i]) + result * b;
+    }
+    return result;
+    
+}
+
+/* This version is backwards - dividing taylor terms
+Poly divide(Poly const &a, Poly const &b, Poly &r) {
+    Poly c;
+    r = a; // remainder
+    
+    const unsigned k = a.size();
+    r.resize(k, 0);
+    c.resize(k, 0);
+
+    for(unsigned i = 0; i < k; i++) {
+        double ci = r[i]/b[0];
+        c[i] += ci;
+        Poly bb = ci*b;
+        std::cout << ci <<"*" << b << ", r= " << r << std::endl;
+        r -= bb.shifted(i);
+    }
+    
+    return c;
+}
+*/
+
+Poly divide(Poly const &a, Poly const &b, Poly &r) {
+    Poly c;
+    r = a; // remainder
+    assert(b.size() > 0);
+    
+    const unsigned k = a.degree();
+    const unsigned l = b.degree();
+    c.resize(k, 0.);
+    
+    for(unsigned i = k; i >= l; i--) {
+        assert(i >= 0);
+        double ci = r.back()/b.back();
+        c[i-l] += ci;
+        Poly bb = ci*b;
+        //std::cout << ci <<"*(" << b.shifted(i-l) << ") = " 
+        //          << bb.shifted(i-l) << "     r= " << r << std::endl;
+        r -= bb.shifted(i-l);
+        r.pop_back();
+    }
+    //std::cout << "r= " << r << std::endl;
+    r.normalize();
+    c.normalize();
+    
+    return c;
+}
+
+Poly gcd(Poly const &a, Poly const &b, const double tol) {
+    if(a.size() < b.size())
+        return gcd(b, a);
+    if(b.size() <= 0)
+        return a;
+    if(b.size() == 1)
+        return a;
+    Poly r;
+    divide(a, b, r);
+    return gcd(b, r);
+}
+
+
+
+/*Poly divide_out_root(Poly const & p, double x) {
+    assert(1);
+    }*/
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/poly.h b/src/2geom/poly.h
new file mode 100644 (file)
index 0000000..4fc6809
--- /dev/null
@@ -0,0 +1,219 @@
+#ifndef SEEN_POLY_H
+#define SEEN_POLY_H
+#include <assert.h>
+#include <vector>
+#include <iostream>
+#include <algorithm>
+#include <complex>
+#include "utils.h"
+
+class Poly : public std::vector<double>{
+public:
+    // coeff; // sum x^i*coeff[i]
+    
+    //unsigned size() const { return coeff.size();}
+    unsigned degree() const { return size()-1;}
+
+    //double operator[](const int i) const { return (*this)[i];}
+    //double& operator[](const int i) { return (*this)[i];}
+    
+    Poly operator+(const Poly& p) const {
+        Poly result;
+        const unsigned out_size = std::max(size(), p.size());
+        const unsigned min_size = std::min(size(), p.size());
+        //result.reserve(out_size);
+        
+        for(unsigned i = 0; i < min_size; i++) {
+            result.push_back((*this)[i] + p[i]);
+        }
+        for(unsigned i = min_size; i < size(); i++)
+            result.push_back((*this)[i]);
+        for(unsigned i = min_size; i < p.size(); i++)
+            result.push_back(p[i]);
+        assert(result.size() == out_size);
+        return result;
+    }
+    Poly operator-(const Poly& p) const {
+        Poly result;
+        const unsigned out_size = std::max(size(), p.size());
+        const unsigned min_size = std::min(size(), p.size());
+        result.reserve(out_size);
+        
+        for(unsigned i = 0; i < min_size; i++) {
+            result.push_back((*this)[i] - p[i]);
+        }
+        for(unsigned i = min_size; i < size(); i++)
+            result.push_back((*this)[i]);
+        for(unsigned i = min_size; i < p.size(); i++)
+            result.push_back(-p[i]);
+        assert(result.size() == out_size);
+        return result;
+    }
+    Poly operator-=(const Poly& p) {
+        const unsigned out_size = std::max(size(), p.size());
+        const unsigned min_size = std::min(size(), p.size());
+        resize(out_size);
+        
+        for(unsigned i = 0; i < min_size; i++) {
+            (*this)[i] -= p[i];
+        }
+        for(unsigned i = min_size; i < out_size; i++)
+            (*this)[i] = -p[i];
+        return *this;
+    }
+    Poly operator-(const double k) const {
+        Poly result;
+        const unsigned out_size = size();
+        result.reserve(out_size);
+        
+        for(unsigned i = 0; i < out_size; i++) {
+            result.push_back((*this)[i]);
+        }
+        result[0] -= k;
+        return result;
+    }
+    Poly operator-() const {
+        Poly result;
+        result.resize(size());
+        
+        for(unsigned i = 0; i < size(); i++) {
+            result[i] = -(*this)[i];
+        }
+        return result;
+    }
+    Poly operator*(const double p) const {
+        Poly result;
+        const unsigned out_size = size();
+        result.reserve(out_size);
+        
+        for(unsigned i = 0; i < out_size; i++) {
+            result.push_back((*this)[i]*p);
+        }
+        assert(result.size() == out_size);
+        return result;
+    }
+// equivalent to multiply by x^terms, discard negative terms
+    Poly shifted(unsigned terms) const { 
+        Poly result;
+        const unsigned out_size = std::max(unsigned(0), size()+terms);
+        result.reserve(out_size);
+        
+        if(terms < 0) {
+            for(unsigned i = 0; i < out_size; i++) {
+                result.push_back((*this)[i-terms]);
+            }
+        } else {
+            for(unsigned i = 0; i < terms; i++) {
+                result.push_back(0.0);
+            }
+            for(unsigned i = 0; i < size(); i++) {
+                result.push_back((*this)[i]);
+            }
+        }
+        
+        assert(result.size() == out_size);
+        return result;
+    }
+    Poly operator*(const Poly& p) const;
+    
+    template <typename T>
+    T eval(T x) const {
+        T r = 0;
+        for(int k = size()-1; k >= 0; k--) {
+            r = r*x + T((*this)[k]);
+        }
+        return r;
+    }
+    
+    template <typename T>
+    T operator()(T t) const { return (T)eval(t);}
+    
+    void normalize();
+    
+    void monicify();
+    Poly() {}
+    Poly(const Poly& p) : std::vector<double>(p) {}
+    Poly(const double a) {push_back(a);}
+    
+public:
+    template <class T, class U>
+    void val_and_deriv(T x, U &pd) const {
+        pd[0] = back();
+        int nc = size() - 1;
+        int nd = pd.size() - 1;
+        for(unsigned j = 1; j < pd.size(); j++)
+            pd[j] = 0.0;
+        for(int i = nc -1; i >= 0; i--) {
+            int nnd = std::min(nd, nc-i);
+            for(int j = nnd; j >= 1; j--)
+                pd[j] = pd[j]*x + operator[](i);
+            pd[0] = pd[0]*x + operator[](i);
+        }
+        double cnst = 1;
+        for(int i = 2; i <= nd; i++) {
+            cnst *= i;
+            pd[i] *= cnst;
+        }
+    }
+    
+    static Poly linear(double ax, double b) {
+        Poly p;
+        p.push_back(b);
+        p.push_back(ax);
+        return p;
+    }
+};
+
+inline Poly operator*(double a, Poly const & b) { return b * a;}
+
+Poly integral(Poly const & p);
+Poly derivative(Poly const & p);
+Poly divide_out_root(Poly const & p, double x);
+Poly compose(Poly const & a, Poly const & b);
+Poly divide(Poly const &a, Poly const &b, Poly &r);
+Poly gcd(Poly const &a, Poly const &b, const double tol=1e-10);
+
+/*** solve(Poly p)
+ * find all p.degree() roots of p.
+ * This function can take a long time with suitably crafted polynomials, but in practice it should be fast.  Should we provide special forms for degree() <= 4?
+ */
+std::vector<std::complex<double> > solve(const Poly & p);
+
+/*** solve_reals(Poly p)
+ * find all real solutions to Poly p.
+ * currently we just use solve and pick out the suitably real looking values, there may be a better algorithm.
+ */
+std::vector<double> solve_reals(const Poly & p);
+double polish_root(Poly const & p, double guess, double tol);
+
+inline std::ostream &operator<< (std::ostream &out_file, const Poly &in_poly) {
+    if(in_poly.size() == 0)
+        out_file << "0";
+    else {
+        for(int i = (int)in_poly.size()-1; i >= 0; --i) {
+            if(i == 1) {
+                out_file << "" << in_poly[i] << "*x";
+                out_file << " + ";
+            } else if(i) {
+                out_file << "" << in_poly[i] << "*x^" << i;
+                out_file << " + ";
+            } else
+                out_file << in_poly[i];
+            
+        }
+    }
+    return out_file;
+}
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+#endif
diff --git a/src/2geom/quadtree.cpp b/src/2geom/quadtree.cpp
new file mode 100644 (file)
index 0000000..c033b76
--- /dev/null
@@ -0,0 +1,131 @@
+#include "quadtree.h"
+
+Quad* QuadTree::search(double x0, double y0, double x1, double y1) {
+    Quad *q = root;
+        
+    double bxx0 = bx1, bxx1 = bx1;
+    double byy0 = by1, byy1 = by1;
+    while(q) {
+        double cx = (bxx0 + bxx1)/2;
+        double cy = (byy0 + byy1)/2;
+        unsigned i = 0;
+        if(x0 >= cx) {
+            i += 1;
+            bxx0 = cx; // zoom in a quad
+        } else if(x1 <= cx) {
+            bxx1 = cx;
+        } else
+            break;
+        if(y0 >= cy) {
+            i += 2;
+            byy0 = cy;
+        } else if(y1 <= cy) {
+            byy1 = cy;
+        } else
+            break;
+        
+        assert(i < 4);
+        Quad *qq = q->children[i];
+        if(qq == 0) break; // last non-null
+        q = qq;
+    }
+    return q;
+}
+    
+void QuadTree::insert(double x0, double y0, double x1, double y1, int shape) {
+    // loop until a quad would break the box.
+    if(root == 0) {
+        root = new Quad;
+            
+        bx0 = 0;
+        bx1 = 1;
+        by0 = 0;
+        by1 = 1;
+    }
+    Quad *q = root;
+    
+    double bxx0 = bx0, bxx1 = bx1;
+    double byy0 = by0, byy1 = by1;
+    while((bxx0 > x0) ||
+          (bxx1 < x1) ||
+          (byy0 > y0) ||
+          (byy1 < y1)) { // too small initial size - double
+        unsigned i = 0;
+        if(bxx0 > x0) {
+            bxx0 = 2*bxx0 - bxx1;
+            i += 1;
+        } else {
+            bxx1 = 2*bxx1 - bxx0;
+        }
+        if(byy0 > y0) {
+            byy0 = 2*byy0 - byy1;
+            i += 2;
+        } else {
+            byy1 = 2*byy1 - byy0;
+        }
+        q = new Quad;
+        q->children[i] = root;
+        root = q;
+        bx0 = bxx0;
+        bx1 = bxx1;
+        by0 = byy0;
+        by1 = byy1;
+    }
+    
+    while(q) {
+        double cx = (bxx0 + bxx1)/2;
+        double cy = (byy0 + byy1)/2;
+        unsigned i = 0;
+        assert(x0 >= bxx0);
+        assert(x1 <= bxx1);
+        assert(y0 >= byy0);
+        assert(y1 <= byy1);
+        if(x0 >= cx) {
+            i += 1;
+            bxx0 = cx; // zoom in a quad
+        } else if(x1 <= cx) {
+            bxx1 = cx;
+        } else
+            break;
+        if(y0 >= cy) {
+            i += 2;
+            byy0 = cy;
+        } else if(y1 <= cy) {
+            byy1 = cy;
+        } else
+            break;
+            
+        assert(i < 4);
+        Quad *qq = q->children[i];
+        if(qq == 0) {
+            qq = new Quad;
+            q->children[i] = qq;
+        }
+        q = qq;
+    }
+    q->data.push_back(shape);
+}
+void QuadTree::erase(Quad *q, int shape) {
+    for(Quad::iterator i = q->data.begin();  i != q->data.end(); i++) {
+        if(*i == shape) {
+            q->data.erase(i);
+            if(q->data.empty()) {
+
+            }
+        }
+    }
+    return;
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(substatement-open . 0))
+  indent-tabs-mode:nil
+  c-brace-offset:0
+  fill-column:99
+  End:
+  vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
+*/
+
diff --git a/src/2geom/quadtree.h b/src/2geom/quadtree.h
new file mode 100644 (file)
index 0000000..ef583c0
--- /dev/null
@@ -0,0 +1,41 @@
+#include <vector>\r
+#include <cassert>
+
+class Quad{
+public:
+    Quad* children[4];
+    std::vector<int> data;
+    Quad() {
+        for(int i = 0; i < 4; i++)
+            children[i] = 0;
+    }
+    typedef std::vector<int>::iterator iterator;
+};
+
+class QuadTree{
+public:
+    Quad* root;
+    double scale;
+    double bx0, bx1;
+    double by0, by1;
+
+    QuadTree() : root(0), scale(1) {}
+    
+    Quad* search(double x0, double y0, double x1, double y1);
+    void insert(double x0, double y0, double x1, double y1, int shape);
+    void erase(Quad *q, int shape);
+};
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(substatement-open . 0))
+  indent-tabs-mode:nil
+  c-brace-offset:0
+  fill-column:99
+  End:
+  vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
+*/
+
diff --git a/src/2geom/rect.h b/src/2geom/rect.h
new file mode 100644 (file)
index 0000000..d968020
--- /dev/null
@@ -0,0 +1,97 @@
+//D2<Interval> specialization to Rect:\r
+\r
+ /* Authors of original rect class:\r
+ *   Lauris Kaplinski <lauris@kaplinski.com>\r
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>\r
+ *   bulia byak <buliabyak@users.sf.net>\r
+ *   MenTaLguY <mental@rydia.net>\r
+ */\r
+
+#ifdef _2GEOM_D2  /*This is intentional: we don't actually want anyone to
+                    include this, other than D2.h */
+#ifndef _2GEOM_RECT
+#define _2GEOM_RECT
+\r
+#include "matrix.h"\r
+#include <boost/optional/optional.hpp>
+\r
+namespace Geom {\r
+\r
+typedef D2<Interval> Rect;\r
+
+Rect unify(const Rect &, const Rect &);
+\r
+template<>\r
+class D2<Interval> {\r
+  private:\r
+    Interval f[2];\r
+    D2<Interval>();// { f[X] = f[Y] = Interval(0, 0); }\r
+\r
+  public:\r
+    D2<Interval>(Interval const &a, Interval const &b) {\r
+        f[X] = a;\r
+        f[Y] = b;\r
+    }\r
+\r
+    D2<Interval>(Point const & a, Point const & b) {\r
+        f[X] = Interval(a[X], b[X]);\r
+        f[Y] = Interval(a[Y], b[Y]);\r
+    }\r
+\r
+    inline Interval& operator[](unsigned i)              { return f[i]; }\r
+    inline Interval const & operator[](unsigned i) const { return f[i]; }\r
+\r
+    inline Point min() const { return Point(f[X].min(), f[Y].min()); }\r
+    inline Point max() const { return Point(f[X].max(), f[Y].max()); }\r
+\r
+    /** returns the four corners of the rectangle in order\r
+     *  (clockwise if +Y is up, anticlockwise if +Y is down) */\r
+    Point corner(unsigned i) const {\r
+      switch(i % 4) {\r
+        case 0: return Point(f[X].min(), f[Y].min());\r
+        case 1: return Point(f[X].max(), f[Y].min());\r
+        case 2: return Point(f[X].max(), f[Y].max());\r        case 3: return Point(f[X].min(), f[Y].max());\r
+      }\r
+    }\r
+
+    inline double width() const { return f[X].extent(); }
+    inline double height() const { return f[Y].extent(); }
+\r
+    /** returns a vector from min to max. */\r
+    inline Point dimensions() const { return Point(f[X].extent(), f[Y].extent()); }\r
+    inline Point midpoint() const { return Point(f[X].middle(), f[Y].middle()); }\r
+\r
+    inline double area() const { return f[X].extent() * f[Y].extent(); }\r
+    inline double maxExtent() const { return std::max(f[X].extent(), f[Y].extent()); }\r
+\r
+    inline bool isEmpty()                 const { return f[X].isEmpty()        && f[Y].isEmpty(); }\r
+    inline bool intersects(Rect const &r) const { return f[X].intersects(r[X]) && f[Y].intersects(r[Y]); }\r
+    inline bool contains(Rect const &r)   const { return f[X].contains(r[X])   && f[Y].contains(r[Y]); }\r
+    inline bool contains(Point const &p)  const { return f[X].contains(p[X])   && f[Y].contains(p[Y]); }\r
+\r
+    inline void expandTo(Point p)        { f[X].extendTo(p[X]);  f[Y].extendTo(p[Y]); }\r
+    inline void unionWith(Rect const &b) { f[X].unionWith(b[X]); f[Y].unionWith(b[Y]); }\r
+\r
+    inline void expandBy(double amnt)    { f[X].expandBy(amnt);  f[Y].expandBy(amnt); }\r
+    inline void expandBy(Point const p)  { f[X].expandBy(p[X]);  f[Y].expandBy(p[Y]); }\r
+\r
+    /** Transforms the rect by m. Note that it gives correct results only for scales and translates,
+        in the case of rotations, the area of the rect will grow as it cannot rotate. */\r
+    inline Rect operator*(Matrix const m) const { return unify(Rect(corner(0) * m, corner(2) * m),
+                                                               Rect(corner(1) * m, corner(3) * m)); }\r
+};
+
+inline Rect unify(const Rect & a, const Rect & b) {
+    return Rect(unify(a[X], b[X]), unify(a[Y], b[Y]));
+}
+
+inline boost::optional<Rect> intersect(const Rect & a, const Rect & b) {
+    boost::optional<Interval> x = intersect(a[X], b[X]);
+    boost::optional<Interval> y = intersect(a[Y], b[Y]);
+    return x && y ? boost::optional<Rect>(Rect(*x, *y)) : boost::optional<Rect>();
+}
+
+}
+
+#endif //_2GEOM_RECT
+#endif //_2GEOM_D2
diff --git a/src/2geom/sbasis-2d.cpp b/src/2geom/sbasis-2d.cpp
new file mode 100644 (file)
index 0000000..e271fed
--- /dev/null
@@ -0,0 +1,72 @@
+#include "sbasis-2d.h"
+
+namespace Geom{
+
+SBasis extract_u(SBasis2d const &a, double u) {
+    SBasis sb;
+    double s = u*(1-u);
+    
+    for(unsigned vi = 0; vi < a.vs; vi++) {
+        double sk = 1;
+        Linear bo(0,0);
+        for(unsigned ui = 0; ui < a.us; ui++) {
+            bo += (extract_u(a.index(ui, vi), u))*sk;
+            sk *= s;
+        }
+        sb.push_back(bo);
+    }
+    
+    return sb;
+}
+
+SBasis extract_v(SBasis2d const &a, double v) {
+    SBasis sb;
+    double s = v*(1-v);
+    
+    for(unsigned ui = 0; ui < a.us; ui++) {
+        double sk = 1;
+        Linear bo(0,0);
+        for(unsigned vi = 0; vi < a.vs; vi++) {
+            bo += (extract_v(a.index(ui, vi), v))*sk;
+            sk *= s;
+        }
+        sb.push_back(bo);
+    }
+    
+    return sb;
+}
+
+SBasis compose(Linear2d const &a, D2<SBasis> const &p) {
+    D2<SBasis> omp(-p[X] + 1, -p[Y] + 1);
+    return multiply(omp[0], omp[1])*a[0] +
+           multiply(p[0], omp[1])*a[1] +
+           multiply(omp[0], p[1])*a[2] +
+           multiply(p[0], p[1])*a[3];
+}
+
+SBasis 
+compose(SBasis2d const &fg, D2<SBasis> const &p) {
+    SBasis B;
+    SBasis s[2];
+    SBasis ss[2];
+    for(unsigned dim = 0; dim < 2; dim++) 
+        s[dim] = p[dim]*(Linear(1) - p[dim]);
+    ss[1] = Linear(1);
+    for(unsigned vi = 0; vi < fg.vs; vi++) {
+        ss[0] = ss[1];
+        for(unsigned ui = 0; ui < fg.us; ui++) {
+            unsigned i = ui + vi*fg.us;
+            B += ss[0]*compose(fg[i], p);
+            ss[0] *= s[0];
+        }
+        ss[1] *= s[1];
+    }
+    return B;
+}
+
+D2<SBasis>
+compose_each(D2<SBasis2d> const &fg, D2<SBasis> const &p) {
+    return D2<SBasis>(compose(fg[X], p), compose(fg[Y], p));
+}
+
+};
diff --git a/src/2geom/sbasis-2d.h b/src/2geom/sbasis-2d.h
new file mode 100644 (file)
index 0000000..921a740
--- /dev/null
@@ -0,0 +1,325 @@
+#ifndef SEEN_SBASIS_2D_H
+#define SEEN_SBASIS_2D_H
+#include <vector>
+#include <cassert>
+#include <algorithm>
+#include "d2.h"
+#include "sbasis.h"
+#include <iostream>
+
+namespace Geom{
+
+class Linear2d{
+public:
+    /*  
+        u 0,1
+        v 0,2
+    */
+    double a[4];
+    Linear2d() {}
+    Linear2d(double aa) {
+        for(unsigned i = 0 ; i < 4; i ++) 
+            a[i] = aa;
+    }
+    Linear2d(double a00, double a01, double a10, double a11) 
+    {
+        a[0] = a00; 
+        a[1] = a01;
+        a[2] = a10; 
+        a[3] = a11; 
+    }
+
+    double operator[](const int i) const {
+        assert(i >= 0);
+        assert(i < 4);
+        return a[i];
+    }
+    double& operator[](const int i) {
+        assert(i >= 0);
+        assert(i < 4);
+        return a[i];
+    }
+    double apply(double u, double v) {
+        return (a[0]*(1-u)*(1-v) +
+                a[1]*u*(1-v) +
+                a[2]*(1-u)*v +
+                a[3]*u*v);
+    }
+};
+
+inline Linear extract_u(Linear2d const &a, double u) {
+    return Linear(a[0]*(1-u) +
+                  a[1]*u,
+                  a[2]*(1-u) +
+                  a[3]*u);
+}
+inline Linear extract_v(Linear2d const &a, double v) {
+    return Linear(a[0]*(1-v) +
+                  a[2]*v,
+                  a[1]*(1-v) +
+                  a[3]*v);
+}
+inline Linear2d operator-(Linear2d const &a) {
+    return Linear2d(-a.a[0], -a.a[1],
+                    -a.a[2], -a.a[3]);
+}
+inline Linear2d operator+(Linear2d const & a, Linear2d const & b) {
+    return Linear2d(a[0] + b[0], 
+                  a[1] + b[1],
+                  a[2] + b[2], 
+                  a[3] + b[3]);
+}
+inline Linear2d operator-(Linear2d const & a, Linear2d const & b) {
+    return Linear2d(a[0] - b[0],
+                  a[1] - b[1],
+                  a[2] - b[2],
+                  a[3] - b[3]);
+}
+inline Linear2d& operator+=(Linear2d & a, Linear2d const & b) {
+    for(unsigned i = 0; i < 4; i++)
+        a[i] += b[i];
+    return a;
+}
+inline Linear2d& operator-=(Linear2d & a, Linear2d const & b) {
+    for(unsigned i = 0; i < 4; i++)
+        a[i] -= b[i];
+    return a;
+}
+inline Linear2d& operator*=(Linear2d & a, double b) {
+    for(unsigned i = 0; i < 4; i++)
+        a[i] *= b;
+    return a;
+}
+
+inline bool operator==(Linear2d const & a, Linear2d const & b) {
+    for(unsigned i = 0; i < 4; i++)
+        if(a[i] != b[i])
+            return false;
+    return true;
+}
+inline bool operator!=(Linear2d const & a, Linear2d const & b) {
+    for(unsigned i = 0; i < 4; i++)
+        if(a[i] == b[i])
+            return false;
+    return true;
+}
+inline Linear2d operator*(double const a, Linear2d const & b) {
+    return Linear2d(a*b[0], a*b[1],
+                  a*b[2], a*b[3]);
+}
+
+class SBasis2d : public std::vector<Linear2d>{
+public:
+    // vector in u,v
+    unsigned us, vs; // number of u terms, v terms
+    SBasis2d() {}
+    SBasis2d(Linear2d const & bo) 
+        : us(1), vs(1) {
+        push_back(bo);
+    }
+    SBasis2d(SBasis2d const & a) 
+        : std::vector<Linear2d>(a), us(a.us), vs(a.vs) {}
+    
+    Linear2d& index(unsigned ui, unsigned vi) {
+        assert(ui < us);
+        assert(vi < vs);
+        return (*this)[ui + vi*us];        
+    }
+    
+    Linear2d index(unsigned ui, unsigned vi) const {
+        if(ui >= us) 
+            return Linear2d(0);
+        if(vi >= vs)
+            return Linear2d(0);
+        return (*this)[ui + vi*us];        
+    }
+    
+    double apply(double u, double v) const {
+        double s = u*(1-u);
+        double t = v*(1-v);
+        Linear2d p;
+        double tk = 1;
+// XXX rewrite as horner
+        for(unsigned vi = 0; vi < vs; vi++) {
+            double sk = 1;
+            for(unsigned ui = 0; ui < us; ui++) {
+                p += (sk*tk)*index(ui, vi);
+                sk *= s;
+            }
+            tk *= t;
+        }
+        return p.apply(u,v);
+    }
+
+    void clear() {
+        fill(begin(), end(), Linear2d(0));
+    }
+    
+    void normalize(); // remove extra zeros
+
+    double tail_error(unsigned tail) const;
+    
+    void truncate(unsigned k);
+};
+
+inline SBasis2d operator-(const SBasis2d& p) {
+    SBasis2d result;
+    result.reserve(p.size());
+        
+    for(unsigned i = 0; i < p.size(); i++) {
+        result.push_back(-p[i]);
+    }
+    return result;
+}
+
+inline SBasis2d operator+(const SBasis2d& a, const SBasis2d& b) {
+    SBasis2d result;
+    result.us = std::max(a.us, b.us);
+    result.vs = std::max(a.vs, b.vs);
+    const unsigned out_size = result.us*result.vs;
+    result.resize(out_size);
+        
+    for(unsigned vi = 0; vi < result.vs; vi++) {
+        for(unsigned ui = 0; ui < result.us; ui++) {
+            Linear2d bo;
+            if(ui < a.us && vi < a.vs)
+                bo += a.index(ui, vi);
+            if(ui < b.us && vi < b.vs)
+                bo += b.index(ui, vi);
+            result.index(ui, vi) = bo;
+        }
+    }
+    return result;
+}
+
+inline SBasis2d operator-(const SBasis2d& a, const SBasis2d& b) {
+    SBasis2d result;
+    result.us = std::max(a.us, b.us);
+    result.vs = std::max(a.vs, b.vs);
+    const unsigned out_size = result.us*result.vs;
+    result.resize(out_size);
+        
+    for(unsigned vi = 0; vi < result.vs; vi++) {
+        for(unsigned ui = 0; ui < result.us; ui++) {
+            Linear2d bo;
+            if(ui < a.us && vi < a.vs)
+                bo += a.index(ui, vi);
+            if(ui < b.us && vi < b.vs)
+                bo -= b.index(ui, vi);
+            result.index(ui, vi) = bo;
+        }
+    }
+    return result;
+}
+
+
+inline SBasis2d& operator+=(SBasis2d& a, const Linear2d& b) {
+    if(a.size() < 1)
+        a.push_back(b);
+    else
+        a[0] += b;
+    return a;
+}
+
+inline SBasis2d& operator-=(SBasis2d& a, const Linear2d& b) {
+    if(a.size() < 1)
+        a.push_back(-b);
+    else
+        a[0] -= b;
+    return a;
+}
+
+inline SBasis2d& operator+=(SBasis2d& a, double b) {
+    if(a.size() < 1)
+        a.push_back(Linear2d(b));
+    else {
+        for(unsigned i = 0; i < 4; i++)
+            a[0] += double(b);
+    }
+    return a;
+}
+
+inline SBasis2d& operator-=(SBasis2d& a, double b) {
+    if(a.size() < 1)
+        a.push_back(Linear2d(-b));
+    else {
+        a[0] -= b;
+    }
+    return a;
+}
+
+inline SBasis2d& operator*=(SBasis2d& a, double b) {
+    for(unsigned i = 0; i < a.size(); i++)
+        a[i] *= b;
+    return a;
+}
+
+inline SBasis2d& operator/=(SBasis2d& a, double b) {
+    for(unsigned i = 0; i < a.size(); i++)
+        a[i] *= (1./b);
+    return a;
+}
+
+SBasis2d operator*(double k, SBasis2d const &a);
+SBasis2d operator*(SBasis2d const &a, SBasis2d const &b);
+
+SBasis2d shift(SBasis2d const &a, int sh);
+
+SBasis2d shift(Linear2d const &a, int sh);
+
+SBasis2d truncate(SBasis2d const &a, unsigned terms);
+
+SBasis2d multiply(SBasis2d const &a, SBasis2d const &b);
+
+SBasis2d integral(SBasis2d const &c);
+
+SBasis2d derivative(SBasis2d const &a);
+
+SBasis2d sqrt(SBasis2d const &a, int k);
+
+// return a kth order approx to 1/a)
+SBasis2d reciprocal(Linear2d const &a, int k);
+
+SBasis2d divide(SBasis2d const &a, SBasis2d const &b, int k);
+
+// a(b(t))
+SBasis2d compose(SBasis2d const &a, SBasis2d const &b);
+SBasis2d compose(SBasis2d const &a, SBasis2d const &b, unsigned k);
+SBasis2d inverse(SBasis2d const &a, int k);
+
+// these two should probably be replaced with compose
+SBasis extract_u(SBasis2d const &a, double u);
+SBasis extract_v(SBasis2d const &a, double v);
+
+SBasis compose(Linear2d const &a, D2<SBasis> const &p);
+
+SBasis compose(SBasis2d const &fg, D2<SBasis> const &p);
+
+D2<SBasis> compose_each(D2<SBasis2d> const &fg, D2<SBasis> const &p);
+
+inline std::ostream &operator<< (std::ostream &out_file, const Linear2d &bo) {
+    out_file << "{" << bo[0] << ", " << bo[1] << "}, ";
+    out_file << "{" << bo[2] << ", " << bo[3] << "}";
+    return out_file;
+}
+
+inline std::ostream &operator<< (std::ostream &out_file, const SBasis2d & p) {
+    for(unsigned i = 0; i < p.size(); i++) {
+        out_file << p[i] << "s^" << i << " + ";
+    }
+    return out_file;
+}
+
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+#endif
diff --git a/src/2geom/sbasis-geometric.cpp b/src/2geom/sbasis-geometric.cpp
new file mode 100644 (file)
index 0000000..170323c
--- /dev/null
@@ -0,0 +1,378 @@
+#include "sbasis-geometric.h"
+#include "sbasis.h"
+#include "sbasis-math.h"
+//#include "solver.h"
+#include "sbasis-geometric.h"
+
+/** Geometric operators on D2<SBasis> (1D->2D).
+ * Copyright 2007 JF Barraud
+ * Copyright 2007 N Hurst
+ *
+ * The functions defined in this header related to 2d geometric operations such as arc length,
+ * unit_vector, curvature, and centroid.  Most are built on top of unit_vector, which takes an
+ * arbitrary D2 and returns an D2 with unit length with the same direction.
+ *
+ * Todo/think about:
+ *  arclength D2 -> sbasis (giving arclength function)
+ *  does uniform_speed return natural parameterisation?
+ *  integrate sb2d code from normal-bundle
+ *  angle(md<2>) -> sbasis (gives angle from vector - discontinuous?)
+ *  osculating circle center?
+ *  
+ **/
+
+//namespace Geom{
+using namespace Geom;
+using namespace std;
+
+//Some utils first.
+//TODO: remove this!! 
+static vector<double> 
+vect_intersect(vector<double> const &a, vector<double> const &b, double tol=0.){
+    vector<double> inter;
+    unsigned i=0,j=0;
+    while ( i<a.size() && j<b.size() ){
+        if (fabs(a[i]-b[j])<tol){
+            inter.push_back(a[i]);
+            i+=1;
+            j+=1;
+        }else if (a[i]<b[j]){
+            i+=1;
+        }else if (a[i]>b[j]){
+            j+=1;
+        }
+    }
+    return inter;
+}
+
+static SBasis divide_by_sk(SBasis const &a, int k) {
+    assert( k<(int)a.size());
+    if(k < 0) return shift(a,-k);
+    SBasis c;
+    c.insert(c.begin(), a.begin()+k, a.end());
+    return c;
+}
+
+static SBasis divide_by_t0k(SBasis const &a, int k) {
+    if(k < 0) {
+        SBasis c = Linear(0,1);
+        for (int i=2; i<=-k; i++){
+            c*=c;
+        }
+        c*=a;
+        return(c);
+    }else{
+        SBasis c = Linear(1,0);
+        for (int i=2; i<=k; i++){
+            c*=c;
+        }
+        c*=a;
+        return(divide_by_sk(c,k));
+    }
+}
+
+static SBasis divide_by_t1k(SBasis const &a, int k) {
+    if(k < 0) {
+        SBasis c = Linear(1,0);
+        for (int i=2; i<=-k; i++){
+            c*=c;
+        }
+        c*=a;
+        return(c);
+    }else{
+        SBasis c = Linear(0,1);
+        for (int i=2; i<=k; i++){
+            c*=c;
+        }
+        c*=a;
+        return(divide_by_sk(c,k));
+    }
+}
+
+static D2<SBasis> RescaleForNonVanishingEnds(D2<SBasis> const &MM, double ZERO=1.e-4){
+    D2<SBasis> M = MM;
+    //TODO: divide by all the s at once!!!
+    while (fabs(M[0].at0())<ZERO && 
+           fabs(M[1].at0())<ZERO &&
+           fabs(M[0].at1())<ZERO && 
+           fabs(M[1].at1())<ZERO){
+        M[0] = divide_by_sk(M[0],1);
+        M[1] = divide_by_sk(M[1],1);
+    }
+    while (fabs(M[0].at0())<ZERO && fabs(M[1].at0())<ZERO){
+        M[0] = divide_by_t0k(M[0],1);
+        M[1] = divide_by_t0k(M[1],1);
+    }
+    while (fabs(M[0].at1())<ZERO && fabs(M[1].at1())<ZERO){
+        M[0] = divide_by_t1k(M[0],1);
+        M[1] = divide_by_t1k(M[1],1);
+    }
+    return M;
+}
+
+//=================================================================
+//TODO: what's this for?!?!
+Piecewise<D2<SBasis> > 
+Geom::cutAtRoots(Piecewise<D2<SBasis> > const &M, double ZERO){
+    vector<double> rts;
+    for (unsigned i=0; i<M.size(); i++){
+        vector<double> seg_rts = roots((M.segs[i])[0]);
+        seg_rts = vect_intersect(seg_rts, roots((M.segs[i])[1]), ZERO);
+        Linear mapToDom = Linear(M.cuts[i],M.cuts[i+1]);
+        for (unsigned r=0; r<seg_rts.size(); r++){
+            seg_rts[r]= mapToDom(seg_rts[r]);
+        }
+        rts.insert(rts.end(),seg_rts.begin(),seg_rts.end());
+    }
+    return partition(M,rts);
+}
+
+Piecewise<SBasis>
+Geom::atan2(Piecewise<D2<SBasis> > const &vect, double tol, unsigned order){
+    Piecewise<SBasis> result;
+    Piecewise<D2<SBasis> > v = cutAtRoots(vect);
+    result.cuts.push_back(v.cuts.front());
+    for (unsigned i=0; i<v.size(); i++){
+
+        D2<SBasis> vi = RescaleForNonVanishingEnds(v.segs[i]);
+        SBasis x=vi[0], y=vi[1];
+        Piecewise<SBasis> angle;
+        angle = divide (x*derivative(y)-y*derivative(x), x*x+y*y, tol, order);
+
+        //TODO: I don't understand this - sign.
+        angle = integral(-angle);
+        Point vi0 = vi.at0(); 
+        angle += -std::atan2(vi0[1],vi0[0]) - angle[0].at0();
+        //TODO: deal with 2*pi jumps form one seg to the other...
+        //TODO: not exact at t=1 because of the integral.
+        //TODO: force continuity?
+
+        angle.setDomain(Interval(v.cuts[i],v.cuts[i+1]));
+        result.concat(angle);   
+    }
+    return result;
+}
+Piecewise<SBasis>
+Geom::atan2(D2<SBasis> const &vect, double tol, unsigned order){
+    return atan2(Piecewise<D2<SBasis> >(vect),tol,order);
+}
+
+//unitVector(x,y) is computed as (b,-a) where a and b are solutions of:
+//     ax+by=0 (eqn1)   and   a^2+b^2=1 (eqn2)
+Piecewise<D2<SBasis> >
+Geom::unitVector(D2<SBasis> const &V_in, double tol, unsigned order){
+    D2<SBasis> V = RescaleForNonVanishingEnds(V_in);
+    if (V[0].empty() && V[1].empty())
+        return Piecewise<D2<SBasis> >(D2<SBasis>(Linear(1),SBasis()));
+    SBasis x = V[0], y = V[1], a, b;
+    SBasis r_eqn1, r_eqn2;
+
+    Point v0 = unit_vector(V.at0());
+    Point v1 = unit_vector(V.at1());
+    a.push_back(Linear(-v0[1],-v1[1]));
+    b.push_back(Linear( v0[0], v1[0]));
+
+    r_eqn1 = -(a*x+b*y);
+    r_eqn2 = Linear(1.)-(a*a+b*b);
+
+    for (unsigned k=1; k<=order; k++){
+        double r0  = (k<r_eqn1.size())? r_eqn1.at(k).at0() : 0;
+        double r1  = (k<r_eqn1.size())? r_eqn1.at(k).at1() : 0;
+        double rr0 = (k<r_eqn2.size())? r_eqn2.at(k).at0() : 0;
+        double rr1 = (k<r_eqn2.size())? r_eqn2.at(k).at1() : 0;
+        double a0,a1,b0,b1;// coeffs in a[k] and b[k]
+
+        //the equations to solve at this point are:
+        // a0*x(0)+b0*y(0)=r0 & 2*a0*a(0)+2*b0*b(0)=rr0
+        //and
+        // a1*x(1)+b1*y(1)=r1 & 2*a1*a(1)+2*b1*b(1)=rr1
+        a0 = r0/dot(v0,V(0))*v0[0]-rr0/2*v0[1];
+        b0 = r0/dot(v0,V(0))*v0[1]+rr0/2*v0[0];
+        a1 = r1/dot(v1,V(1))*v1[0]-rr1/2*v1[1];
+        b1 = r1/dot(v1,V(1))*v1[1]+rr1/2*v1[0];
+
+        a.push_back(Linear(a0,a1));        
+        b.push_back(Linear(b0,b1));
+        //TODO: use "incremental" rather than explicit formulas.
+        r_eqn1 = -(a*x+b*y);
+        r_eqn2 = Linear(1)-(a*a+b*b);
+    }
+    
+    //our candidat is:
+    D2<SBasis> unitV;
+    unitV[0] =  b;
+    unitV[1] = -a;
+
+    //is it good?
+    double rel_tol = max(1.,max(V_in[0].tailError(0),V_in[1].tailError(0)))*tol;
+
+    if (r_eqn1.tailError(order)>rel_tol || r_eqn2.tailError(order)>tol){
+        //if not: subdivide and concat results.
+        Piecewise<D2<SBasis> > unitV0, unitV1;
+        unitV0 = unitVector(compose(V,Linear(0,.5)),tol,order);
+        unitV1 = unitVector(compose(V,Linear(.5,1)),tol,order);
+        unitV0.setDomain(Interval(0.,.5));
+        unitV1.setDomain(Interval(.5,1.));
+        unitV0.concat(unitV1);
+        return(unitV0);
+    }else{
+        //if yes: return it as pw.
+        Piecewise<D2<SBasis> > result;
+        result=(Piecewise<D2<SBasis> >)unitV;
+        return result;
+    }
+}
+
+Piecewise<D2<SBasis> >
+Geom::unitVector(Piecewise<D2<SBasis> > const &V, double tol, unsigned order){
+    Piecewise<D2<SBasis> > result;
+    Piecewise<D2<SBasis> > VV = cutAtRoots(V);
+    result.cuts.push_back(VV.cuts.front());
+    for (unsigned i=0; i<VV.size(); i++){
+        Piecewise<D2<SBasis> > unit_seg;
+        unit_seg = unitVector(VV.segs[i],tol, order);
+        unit_seg.setDomain(Interval(VV.cuts[i],VV.cuts[i+1]));
+        result.concat(unit_seg);   
+    }
+    return result;
+}
+
+Piecewise<SBasis> 
+Geom::arcLengthSb(Piecewise<D2<SBasis> > const &M, double tol){
+    Piecewise<D2<SBasis> > dM = derivative(M);
+    Piecewise<SBasis> dMlength = sqrt(dot(dM,dM),tol,3);
+    Piecewise<SBasis> length = integral(dMlength);
+    length-=length.segs.front().at0();
+    return length;
+}
+Piecewise<SBasis> 
+Geom::arcLengthSb(D2<SBasis> const &M, double tol){
+    return arcLengthSb(Piecewise<D2<SBasis> >(M), tol);
+}
+
+double
+Geom::length(D2<SBasis> const &M,
+                 double tol){
+    Piecewise<SBasis> length = arcLengthSb(M, tol);
+    return length.segs.back().at1();
+}
+double
+Geom::length(Piecewise<D2<SBasis> > const &M,
+                 double tol){
+    Piecewise<SBasis> length = arcLengthSb(M, tol);
+    return length.segs.back().at1();
+}
+
+
+// incomplete.
+Piecewise<SBasis>
+Geom::curvature(D2<SBasis> const &M, double tol) {
+    D2<SBasis> dM=derivative(M);
+    Piecewise<SBasis> result;
+    Piecewise<D2<SBasis> > unitv = unitVector(dM,tol);
+    Piecewise<SBasis> dMlength = dot(Piecewise<D2<SBasis> >(dM),unitv);
+    Piecewise<SBasis> k = cross(derivative(unitv),unitv);
+    k = divide(k,dMlength,tol,3);
+    return(k);
+}
+
+Piecewise<SBasis> 
+Geom::curvature(Piecewise<D2<SBasis> > const &V, double tol){
+    Piecewise<SBasis> result;
+    Piecewise<D2<SBasis> > VV = cutAtRoots(V);
+    result.cuts.push_back(VV.cuts.front());
+    for (unsigned i=0; i<VV.size(); i++){
+        Piecewise<SBasis> curv_seg;
+        curv_seg = curvature(VV.segs[i],tol);
+        curv_seg.setDomain(Interval(VV.cuts[i],VV.cuts[i+1]));
+        result.concat(curv_seg);
+    }
+    return result;
+}
+
+//=================================================================
+
+Piecewise<D2<SBasis> >
+Geom::arc_length_parametrization(D2<SBasis> const &M,
+                           unsigned order,
+                           double tol){
+    Piecewise<D2<SBasis> > u;
+    u.push_cut(0);
+
+    Piecewise<SBasis> s = arcLengthSb(Piecewise<D2<SBasis> >(M),tol);
+    for (unsigned i=0; i < s.size();i++){
+        double t0=s.cuts[i],t1=s.cuts[i+1];
+        D2<SBasis> sub_M = compose(M,Linear(t0,t1));
+        D2<SBasis> sub_u;
+        for (unsigned dim=0;dim<2;dim++){
+            SBasis sub_s = s.segs[i];
+            sub_s-=sub_s.at0();
+            sub_s/=sub_s.at1();
+            sub_u[dim]=compose_inverse(sub_M[dim],sub_s, order, tol);
+        }
+        u.push(sub_u,s(t1));
+    }
+    return u;
+}
+
+Piecewise<D2<SBasis> >
+Geom::arc_length_parametrization(Piecewise<D2<SBasis> > const &M,
+                                 unsigned order,
+                                 double tol){
+    Piecewise<D2<SBasis> > result;
+    for (unsigned i=0; i<M.size(); i++ ){
+        Piecewise<D2<SBasis> > uniform_seg=arc_length_parametrization(M[i],order,tol);
+        result.concat(uniform_seg);
+    }
+    return(result);
+}
+
+/** centroid using sbasis integration.
+ * This approach uses green's theorem to compute the area and centroid using integrals.  For curved
+ * shapes this is much faster than converting to polyline.
+
+ * Returned values: 
+    0 for normal execution;
+    2 if area is zero, meaning centroid is meaningless.
+
+ * Copyright Nathan Hurst 2006
+ */
+
+unsigned Geom::centroid(Piecewise<D2<SBasis> > const &p, Point& centroid, double &area) {
+    Point centroid_tmp(0,0);
+    double atmp = 0;
+    for(unsigned i = 0; i < p.size(); i++) {
+        SBasis curl = dot(p[i], rot90(derivative(p[i])));
+        SBasis A = integral(curl);
+        D2<SBasis> C = integral(multiply(curl, p[i]));
+        atmp += A.at1() - A.at0();
+        centroid_tmp += C.at1()- C.at0(); // first moment.
+    }
+// join ends
+    centroid_tmp *= 2;
+    Point final = p[p.size()].at1(), initial = p[0].at0();
+    const double ai = cross(final, initial);
+    atmp += ai;
+    centroid_tmp += (final + initial)*ai; // first moment.
+    
+    area = atmp / 2;
+    if (atmp != 0) {
+        centroid = centroid_tmp / (3 * atmp);
+        return 0;
+    }
+    return 2;
+}
+
+//}; // namespace
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/sbasis-geometric.h b/src/2geom/sbasis-geometric.h
new file mode 100644 (file)
index 0000000..49f3715
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef _SBASIS_GEOMETRIC
+#define _SBASIS_GEOMETRIC
+#include "d2.h"
+#include "piecewise.h"
+#include <vector>
+
+/** two-dimensional geometric operators.  
+ * Copyright 2007, JFBarraud
+ * Copyright 2007, njh
+ * 
+ * These operators are built on a more 'polynomially robust'
+ * transformation to map a function that takes a [0,1] parameter to a
+ * 2d vector into a function that takes the same [0,1] parameter to a
+ * unit vector with the same direction.
+ *
+ * Rather that using (X/sqrt(X))(t) which involves two unstable
+ * operations, sqrt and divide, this approach forms a curve directly
+ * from the various tangent directions at each end (angular jet).  As
+ * a result, the final path has a convergence behaviour derived from
+ * that of the sin and cos series. -- njh
+ */
+
+namespace Geom{
+
+Piecewise<D2<SBasis> > 
+cutAtRoots(Piecewise<D2<SBasis> > const &M, double tol=1e-4);
+
+Piecewise<SBasis>
+atan2(D2<SBasis> const &vect, 
+           double tol=.01, unsigned order=3);
+
+Piecewise<SBasis>
+atan2(Piecewise<D2<SBasis> >const &vect, 
+           double tol=.01, unsigned order=3);
+
+Piecewise<D2<SBasis> >
+unitVector(D2<SBasis> const &vect, 
+           double tol=.01, unsigned order=3);
+Piecewise<D2<SBasis> >
+unitVector(Piecewise<D2<SBasis> > const &vect, 
+           double tol=.01, unsigned order=3);
+
+// Piecewise<D2<SBasis> >
+// uniform_speed(D2<SBasis> const M, 
+//               double tol=.1);
+
+Piecewise<SBasis> curvature(          D2<SBasis>   const &M, double tol=.01);
+Piecewise<SBasis> curvature(Piecewise<D2<SBasis> > const &M, double tol=.01);
+
+Piecewise<SBasis> arcLengthSb(          D2<SBasis>   const &M, double tol=.01);
+Piecewise<SBasis> arcLengthSb(Piecewise<D2<SBasis> > const &M, double tol=.01);
+
+double length(          D2<SBasis>   const &M, double tol=.01);
+double length(Piecewise<D2<SBasis> > const &M, double tol=.01);
+
+Piecewise<D2<SBasis> >
+arc_length_parametrization(D2<SBasis> const &M, 
+                           unsigned order=3, 
+                           double tol=.01);
+Piecewise<D2<SBasis> >
+arc_length_parametrization(Piecewise<D2<SBasis> > const &M,
+                           unsigned order=3,
+                           double tol=.01);
+
+
+unsigned centroid(Piecewise<D2<SBasis> > const &p, Point& centroid, double &area);
+
+};
+
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+
+                
diff --git a/src/2geom/sbasis-math.cpp b/src/2geom/sbasis-math.cpp
new file mode 100644 (file)
index 0000000..d88c268
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ *  sbasis-math.cpp - some std functions to work with (pw)s-basis
+ *
+ *  Authors:
+ *   Jean-Francois Barraud
+ *
+ * Copyright (C) 2006-2007 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+//this a first try to define sqrt, cos, sin, etc...
+//TODO: define a truncated compose(sb,sb, order) and extend it to pw<sb>.
+//TODO: in all these functions, compute 'order' according to 'tol'.
+
+#include "sbasis-math.h"
+//#define ZERO 1e-3
+
+
+namespace Geom {
+
+
+#include <stdio.h>
+#include <math.h>
+
+//-|x|-----------------------------------------------------------------------
+Piecewise<SBasis> abs(SBasis const &f){
+    return abs(Piecewise<SBasis>(f));
+}
+Piecewise<SBasis> abs(Piecewise<SBasis> const &f){
+    Piecewise<SBasis> absf=partition(f,roots(f));
+    for (unsigned i=0; i<absf.size(); i++){
+        if (absf.segs[i](.5)<0) absf.segs[i]*=-1;
+    }
+    return absf;
+}
+
+//-maxSb(x,y), minSb(x,y)--------------------------------------------------------
+Piecewise<SBasis> maxSb(          SBasis  const &f,           SBasis  const &g){
+    return maxSb(Piecewise<SBasis>(f),Piecewise<SBasis>(g));
+}
+Piecewise<SBasis> maxSb(Piecewise<SBasis> const &f,           SBasis  const &g){
+    return maxSb(f,Piecewise<SBasis>(g));
+}
+Piecewise<SBasis> maxSb(          SBasis  const &f, Piecewise<SBasis> const &g){
+    return maxSb(Piecewise<SBasis>(f),g);
+}
+Piecewise<SBasis> maxSb(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g){
+    Piecewise<SBasis> maxSb=partition(f,roots(f-g));
+    Piecewise<SBasis> gg =partition(g,maxSb.cuts);
+    maxSb = partition(maxSb,gg.cuts);
+    for (unsigned i=0; i<maxSb.size(); i++){
+        if (maxSb.segs[i](.5)<gg.segs[i](.5)) maxSb.segs[i]=gg.segs[i];
+    }
+    return maxSb;
+}
+
+Piecewise<SBasis> 
+minSb(          SBasis  const &f,           SBasis  const &g){ return -maxSb(-f,-g); }
+Piecewise<SBasis> 
+minSb(Piecewise<SBasis> const &f,           SBasis  const &g){ return -maxSb(-f,-g); }
+Piecewise<SBasis> 
+minSb(          SBasis  const &f, Piecewise<SBasis> const &g){ return -maxSb(-f,-g); }
+Piecewise<SBasis> 
+minSb(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g){ return -maxSb(-f,-g); }
+
+
+//-sign(x)---------------------------------------------------------------
+Piecewise<SBasis> signSb(SBasis const &f){
+    return signSb(Piecewise<SBasis>(f));
+}
+Piecewise<SBasis> signSb(Piecewise<SBasis> const &f){
+    Piecewise<SBasis> sign=partition(f,roots(f));
+    for (unsigned i=0; i<sign.size(); i++){
+        sign.segs[i] = (sign.segs[i](.5)<0)? Linear(-1.):Linear(1.);
+    }
+    return sign;
+}
+
+//-Sqrt----------------------------------------------------------
+static Piecewise<SBasis> sqrt_internal(SBasis const &f, 
+                                    double tol, 
+                                    int order){
+    SBasis sqrtf;
+    if(f.isZero() || order == 0){
+        return Piecewise<SBasis>(sqrtf);
+    }
+    if (f.at0()<-tol*tol && f.at1()<-tol*tol){
+        return sqrt_internal(-f,tol,order);
+    }else if (f.at0()>tol*tol && f.at1()>tol*tol){
+        sqrtf.resize(order+1, Linear(0,0));
+        sqrtf[0] = Linear(std::sqrt(f[0][0]), std::sqrt(f[0][1]));
+        SBasis r = f - multiply(sqrtf, sqrtf); // remainder    
+        for(unsigned i = 1; int(i) <= order and i<r.size(); i++) {
+            Linear ci(r[i][0]/(2*sqrtf[0][0]), r[i][1]/(2*sqrtf[0][1]));
+            SBasis cisi = shift(ci, i);
+            r -= multiply(shift((sqrtf*2 + cisi), i), SBasis(ci));
+            r.truncate(order+1);
+            sqrtf[i] = ci;
+            if(r.tailError(i) == 0) // if exact
+                break;
+        }
+    }else{
+        sqrtf = Linear(std::sqrt(fabs(f.at0())), std::sqrt(fabs(f.at1())));
+    }
+
+    double err = (f - multiply(sqrtf, sqrtf)).tailError(0);
+    if (err<tol){
+        return Piecewise<SBasis>(sqrtf);
+    }
+
+    Piecewise<SBasis> sqrtf0,sqrtf1;
+    sqrtf0 = sqrt_internal(compose(f,Linear(0.,.5)),tol,order);
+    sqrtf1 = sqrt_internal(compose(f,Linear(.5,1.)),tol,order);
+    sqrtf0.setDomain(Interval(0.,.5));
+    sqrtf1.setDomain(Interval(.5,1.));
+    sqrtf0.concat(sqrtf1);
+    return sqrtf0;
+}
+
+Piecewise<SBasis> sqrt(SBasis const &f, double tol, int order){
+    return sqrt(maxSb(f,Linear(tol*tol)),tol,order);
+}
+
+Piecewise<SBasis> sqrt(Piecewise<SBasis> const &f, double tol, int order){
+    Piecewise<SBasis> result;
+    Piecewise<SBasis> ff=maxSb(f,Linear(tol*tol));
+
+    for (unsigned i=0; i<ff.size(); i++){
+        Piecewise<SBasis> sqrtfi = sqrt_internal(ff.segs[i],tol,order);
+        sqrtfi.setDomain(Interval(ff.cuts[i],ff.cuts[i+1]));
+        result.concat(sqrtfi);
+    }
+    return result;
+}
+
+//-Yet another sin/cos--------------------------------------------------------------
+
+Piecewise<SBasis> sin(          SBasis  const &f, double tol, int order){return(cos(-f+M_PI/2,tol,order));}
+Piecewise<SBasis> sin(Piecewise<SBasis> const &f, double tol, int order){return(cos(-f+M_PI/2,tol,order));}
+
+Piecewise<SBasis> cos(Piecewise<SBasis> const &f, double tol, int order){
+    Piecewise<SBasis> result;
+    for (unsigned i=0; i<f.size(); i++){
+        Piecewise<SBasis> cosfi = cos(f.segs[i],tol,order);
+        cosfi.setDomain(Interval(f.cuts[i],f.cuts[i+1]));
+        result.concat(cosfi);
+    }
+    return result;
+}
+
+Piecewise<SBasis> cos(          SBasis  const &f, double tol, int order){
+    double alpha = (f.at0()+f.at1())/2.;
+    SBasis x = f-alpha;
+    double d = x.tailError(0),err=1;
+    //estimate cos(x)-sum_0^order (-1)^k x^2k/2k! by the first neglicted term
+    for (int i=1; i<=2*order; i++) err*=d/i;
+    
+    if (err<tol){
+        SBasis xk=Linear(1), c=Linear(1), s=Linear(0);
+        for (int k=1; k<=2*order; k+=2){
+            xk*=x/k;
+            //take also truncature errors into account...
+            err+=xk.tailError(order);
+            xk.truncate(order);
+            s+=xk;
+            xk*=-x/(k+1);
+            //take also truncature errors into account...
+            err+=xk.tailError(order);
+            xk.truncate(order);
+            c+=xk;
+        }
+        if (err<tol){
+            return Piecewise<SBasis>(std::cos(alpha)*c-std::sin(alpha)*s);
+        }
+    }
+    Piecewise<SBasis> c0,c1;
+    c0 = cos(compose(f,Linear(0.,.5)),tol,order);
+    c1 = cos(compose(f,Linear(.5,1.)),tol,order);
+    c0.setDomain(Interval(0.,.5));
+    c1.setDomain(Interval(.5,1.));
+    c0.concat(c1);
+    return c0;
+}
+
+//--1/x------------------------------------------------------------
+//TODO: this implementation is just wrong. Remove or redo!
+
+void truncateResult(Piecewise<SBasis> &f, int order){
+    if (order>=0){
+        for (unsigned k=0; k<f.segs.size(); k++){
+            f.segs[k].truncate(order);
+        }
+    }
+}
+
+Piecewise<SBasis> reciprocalOnDomain(Interval range, double tol){
+    Piecewise<SBasis> reciprocal_fn;
+    //TODO: deduce R from tol...
+    double R=2.;
+    SBasis reciprocal1_R=reciprocal(Linear(1,R),3);
+    double a=range.min(), b=range.max();
+    if (a*b<0){
+        b=std::max(fabs(a),fabs(b));
+        a=0;
+    }else if (b<0){
+        a=-range.max();
+        b=-range.min();
+    }
+
+    if (a<=tol){
+        reciprocal_fn.push_cut(0);
+        int i0=(int) floor(log(tol)/log(R));
+        a=pow(R,i0);
+        reciprocal_fn.push(Linear(1/a),a);
+    }else{
+        int i0=(int) floor(log(a)/log(R));
+        a=pow(R,i0);
+        reciprocal_fn.cuts.push_back(a);
+    }  
+
+    while (a<b){
+        reciprocal_fn.push(reciprocal1_R/a,R*a);
+        a*=R;
+    }
+    if (range.min()<0 || range.max()<0){
+        Piecewise<SBasis>reciprocal_fn_neg;
+        //TODO: define reverse(pw<sb>);
+        reciprocal_fn_neg.cuts.push_back(-reciprocal_fn.cuts.back());
+        for (unsigned i=0; i<reciprocal_fn.size(); i++){
+            int idx=reciprocal_fn.segs.size()-1-i;
+            reciprocal_fn_neg.push_seg(-reverse(reciprocal_fn.segs.at(idx)));
+            reciprocal_fn_neg.push_cut(-reciprocal_fn.cuts.at(idx));
+        }
+        if (range.max()>0){
+            reciprocal_fn_neg.concat(reciprocal_fn);
+        }
+        reciprocal_fn=reciprocal_fn_neg;
+    }
+
+    return(reciprocal_fn);
+}
+
+Piecewise<SBasis> reciprocal(SBasis const &f, double tol, int order){
+    Piecewise<SBasis> reciprocal_fn=reciprocalOnDomain(bounds_fast(f), tol);
+    Piecewise<SBasis> result=compose(reciprocal_fn,f);
+    truncateResult(result,order);
+    return(result);
+}
+Piecewise<SBasis> reciprocal(Piecewise<SBasis> const &f, double tol, int order){
+    Piecewise<SBasis> reciprocal_fn=reciprocalOnDomain(bounds_fast(f), tol);
+    Piecewise<SBasis> result=compose(reciprocal_fn,f);
+    truncateResult(result,order);
+    return(result);
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype = cpp:expandtab:shiftwidth = 4:tabstop = 8:softtabstop = 4:encoding = utf-8:textwidth = 99 :
diff --git a/src/2geom/sbasis-math.h b/src/2geom/sbasis-math.h
new file mode 100644 (file)
index 0000000..5296410
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *  sbasis-math.h - some std functions to work with (pw)s-basis
+ *
+ *  Authors:
+ *   Jean-Francois Barraud
+ *
+ * Copyright (C) 2006-2007 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+//this a first try to define sqrt, cos, sin, etc...
+//TODO: define a truncated compose(sb,sb, order) and extend it to pw<sb>.
+//TODO: in all these functions, compute 'order' according to 'tol'.
+//TODO: use template to define the pw version automatically from the sb version?
+
+#ifndef SEEN_GEOM_SB_CALCULS_H
+#define SEEN_GEOM_SB_CALCULS_H
+
+
+#include "sbasis.h"
+#include "piecewise.h"
+
+namespace Geom{
+//-|x|---------------------------------------------------------------
+Piecewise<SBasis> abs(          SBasis const &f);
+Piecewise<SBasis> abs(Piecewise<SBasis>const &f);
+
+//- max(f,g), min(f,g) ----------------------------------------------
+Piecewise<SBasis> maxSb(          SBasis  const &f,           SBasis  const &g);
+Piecewise<SBasis> maxSb(Piecewise<SBasis> const &f,           SBasis  const &g);
+Piecewise<SBasis> maxSb(          SBasis  const &f, Piecewise<SBasis> const &g);
+Piecewise<SBasis> maxSb(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g);
+Piecewise<SBasis> minSb(          SBasis  const &f,           SBasis  const &g);
+Piecewise<SBasis> minSb(Piecewise<SBasis> const &f,           SBasis  const &g);
+Piecewise<SBasis> minSb(          SBasis  const &f, Piecewise<SBasis> const &g);
+Piecewise<SBasis> minSb(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g);
+
+//-sign(x)---------------------------------------------------------------
+Piecewise<SBasis> signSb(          SBasis const &f);
+Piecewise<SBasis> signSb(Piecewise<SBasis>const &f);
+
+//-Sqrt---------------------------------------------------------------
+Piecewise<SBasis> sqrt(          SBasis const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> sqrt(Piecewise<SBasis>const &f, double tol=1e-3, int order=3);
+
+//-sin/cos--------------------------------------------------------------
+Piecewise<SBasis> cos(          SBasis  const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> cos(Piecewise<SBasis> const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> sin(          SBasis  const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> sin(Piecewise<SBasis> const &f, double tol=1e-3, int order=3);
+
+//--1/x------------------------------------------------------------
+//TODO: change this...
+Piecewise<SBasis> reciprocalOnDomain(Interval range, double tol=1e-3);
+Piecewise<SBasis> reciprocal(          SBasis const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> reciprocal(Piecewise<SBasis>const &f, double tol=1e-3, int order=3);
+
+}
+
+#endif //SEEN_GEOM_PW_SB_CALCULUS_H
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype = cpp:expandtab:shiftwidth = 4:tabstop = 8:softtabstop = 4:encoding = utf-8:textwidth = 99 :
diff --git a/src/2geom/sbasis-poly.cpp b/src/2geom/sbasis-poly.cpp
new file mode 100644 (file)
index 0000000..e0fa828
--- /dev/null
@@ -0,0 +1,45 @@
+#include "sbasis-poly.h"
+
+namespace Geom{
+
+SBasis poly_to_sbasis(Poly const & p) {
+    SBasis x = Linear(0, 1);
+    SBasis r;
+    
+    for(int i = p.size()-1; i >= 0; i--) {
+        r = SBasis(Linear(p[i], p[i])) + multiply(x, r);
+    }
+    r.normalize();
+    return r;
+       
+}
+
+Poly sbasis_to_poly(SBasis const & sb) {
+    Poly S; // (1-x)x = -1*x^2 + 1*x + 0
+    Poly A, B;
+    B.push_back(0);
+    B.push_back(1);
+    A.push_back(1);
+    A.push_back(-1);
+    S = A*B;
+    Poly r;
+    
+    for(int i = sb.size()-1; i >= 0; i--) {
+        r = S*r + sb[i][0]*A + sb[i][1]*B;
+    }
+    r.normalize();
+    return r;
+}
+
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/sbasis-poly.h b/src/2geom/sbasis-poly.h
new file mode 100644 (file)
index 0000000..4a8aa1c
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _SBASIS_TO_POLY
+#define _SBASIS_TO_POLY
+
+#include "poly.h"
+#include "sbasis.h"
+
+/*** Conversion between SBasis and Poly.  Not recommended for general
+ * use due to instability.
+ */
+
+namespace Geom{
+
+SBasis poly_to_sbasis(Poly const & p);
+Poly sbasis_to_poly(SBasis const & s);
+
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+
+#endif
diff --git a/src/2geom/sbasis-roots.cpp b/src/2geom/sbasis-roots.cpp
new file mode 100644 (file)
index 0000000..ad2dfbd
--- /dev/null
@@ -0,0 +1,352 @@
+/** root finding for sbasis functions.
+ * Copyright 2006 N Hurst
+ * Copyright 2007 JF Barraud
+ *
+ * It is more efficient to find roots of f(t) = c_0, c_1, ... all at once, rather than iterating.
+ *
+ * Todo/think about:
+ *  multi-roots using bernstein method, one approach would be:
+    sort c
+    take median and find roots of that
+    whenever a segment lies entirely on one side of the median, 
+    find the median of the half and recurse.
+
+    in essence we are implementing quicksort on a continuous function
+
+ *  the gsl poly roots finder is faster than bernstein too, but we don't use it for 3 reasons:
+
+ a) it requires convertion to poly, which is numerically unstable
+
+ b) it requires gsl (which is currently not a dependency, and would bring in a whole slew of unrelated stuff)
+
+ c) it finds all roots, even complex ones.  We don't want to accidently treat a nearly real root as a real root
+
+From memory gsl poly roots was about 10 times faster than bernstein in the case where all the roots
+are in [0,1] for polys of order 5.  I spent some time working out whether eigenvalue root finding
+could be done directly in sbasis space, but the maths was too hard for me. -- njh
+
+jfbarraud: eigenvalue root finding could be done directly in sbasis space ?
+
+njh: I don't know, I think it should.  You would make a matrix whose characteristic polynomial was
+correct, but do it by putting the sbasis terms in the right spots in the matrix.  normal eigenvalue
+root finding makes a matrix that is a diagonal + a row along the top.  This matrix has the property
+that its characteristic poly is just the poly whose coefficients are along the top row.
+
+Now an sbasis function is a linear combination of the poly coeffs.  So it seems to me that you
+should be able to put the sbasis coeffs directly into a matrix in the right spots so that the
+characteristic poly is the sbasis.  You'll still have problems b) and c).
+
+We might be able to lift an eigenvalue solver and include that directly into 2geom.  Eigenvalues
+also allow you to find intersections of multiple curves but require solving n*m x n*m matrices.
+
+ **/
+
+#include <cmath>
+#include <map>
+
+#include "sbasis.h"
+#include "sbasis-to-bezier.h"
+#include "solver.h"
+
+using namespace std;
+
+namespace Geom{
+
+Interval bounds_exact(SBasis const &a) {
+    Interval result = Interval(a.at0(), a.at1());
+    SBasis df = derivative(a);
+    vector<double>extrema = roots(df);
+    for (unsigned i=0; i<extrema.size(); i++){
+        result.extendTo(a(extrema[i]));
+    }
+    return result;
+}
+
+Interval bounds_fast(const SBasis &sb, int order) {
+    Interval res;
+    for(int j = sb.size()-1; j>=order; j--) {
+        double a=sb[j][0];
+        double b=sb[j][1];
+
+        double t, v;
+        v = res[0];
+        if (v<0) t = ((b-a)/v+1)*0.5;
+        if (v>=0 || t<0 || t>1) {
+            res[0] = std::min(a,b);
+        }else{
+            res[0]=lerp(t, a+v*t, b);
+        }
+
+        v = res[1];
+        if (v>0) t = ((b-a)/v+1)*0.5;
+        if (v<=0 || t<0 || t>1) {
+            res[1] = std::max(a,b);
+        }else{
+            res[1]=lerp(t, a+v*t, b);
+        }
+    }
+    if (order>0) res*=pow(.25,order);
+    return res;
+}
+
+Interval bounds_local(const SBasis &sb, const Interval &i, int order) {
+    double t0=i.min(), t1=i.max(), lo=0., hi=0.;
+    for(int j = sb.size()-1; j>=order; j--) {
+        double a=sb[j][0];
+        double b=sb[j][1];
+
+        double t;
+        if (lo<0) t = ((b-a)/lo+1)*0.5;
+        if (lo>=0 || t<t0 || t>t1) {
+            lo = std::min(a*(1-t0)+b*t0+lo*t0*(1-t0),a*(1-t1)+b*t1+lo*t1*(1-t1));
+        }else{
+            lo = lerp(t, a+lo*t, b);
+        }
+
+        if (hi>0) t = ((b-a)/hi+1)*0.5;
+        if (hi<=0 || t<t0 || t>t1) {
+            hi = std::max(a*(1-t0)+b*t0+hi*t0*(1-t0),a*(1-t1)+b*t1+hi*t1*(1-t1));
+        }else{
+            hi = lerp(t, a+hi*t, b);
+        }
+    }
+    Interval res = Interval(lo,hi);
+    if (order>0) res*=pow(.25,order);
+    return res;
+}
+
+//-- multi_roots ------------------------------------
+// goal: solve f(t)=c for several c at once.
+/* algo: -compute f at both ends of the given segment [a,b].
+         -compute bounds m<df(t)<M for df on the segment.
+         let c and C be the levels below and above f(a):
+         going from f(a) down to c with slope m takes at least time (f(a)-c)/m
+         going from f(a)  up  to C with slope M takes at least time (C-f(a))/M
+         From this we conclude there are no roots before a'=a+min((f(a)-c)/m,(C-f(a))/M).
+         Do the same for b: compute some b' such that there are no roots in (b',b].
+         -if [a',b'] is not empty, repeat the process with [a',(a'+b')/2] and [(a'+b')/2,b']. 
+  unfortunately, extra care is needed about rounding errors, and also to avoid the repetition of roots,
+  making things tricky and unpleasant...
+*/
+//TODO: Make sure the code is "rounding-errors proof" and take care about repetition of roots!
+
+
+static int upper_level(vector<double> const &levels,double x,double tol=0.){
+    return(upper_bound(levels.begin(),levels.end(),x-tol)-levels.begin());
+}
+
+static void multi_roots_internal(SBasis const &f,
+                                SBasis const &df,
+                                std::vector<double> const &levels,
+                                std::vector<std::vector<double> > &roots,
+                                double htol,
+                                double vtol,
+                                double a,
+                                double fa,
+                                double b,
+                                double fb){
+    
+    if (f.size()==0){
+        int idx;
+        idx=upper_level(levels,0,vtol);
+        if (idx<(int)levels.size()&&fabs(levels.at(idx))<=vtol){
+            roots[idx].push_back(a);
+            roots[idx].push_back(b);
+        }
+        return;
+    }
+////usefull? 
+//     if (f.size()==1){
+//         int idxa=upper_level(levels,fa);
+//         int idxb=upper_level(levels,fb);
+//         if (fa==fb){
+//             if (fa==levels[idxa]){
+//                 roots[a]=idxa;
+//                 roots[b]=idxa;
+//             }
+//             return;
+//         }
+//         int idx_min=std::min(idxa,idxb);
+//         int idx_max=std::max(idxa,idxb);
+//         if (idx_max==levels.size()) idx_max-=1;
+//         for(int i=idx_min;i<=idx_max; i++){
+//             double t=a+(b-a)*(levels[i]-fa)/(fb-fa);
+//             if(a<t&&t<b) roots[t]=i;
+//         }
+//         return;
+//     }
+    if ((b-a)<htol){
+        //TODO: use different tol for t and f ?
+        //TODO: unsigned idx ? (remove int casts when fixed)
+        int idx=std::min(upper_level(levels,fa,vtol),upper_level(levels,fb,vtol));
+        if (idx==(int)levels.size()) idx-=1;
+        double c=levels.at(idx);
+        if((fa-c)*(fb-c)<=0||fabs(fa-c)<vtol||fabs(fb-c)<vtol){
+            roots[idx].push_back((a+b)/2);
+        }
+        return;
+    }
+    
+    int idxa=upper_level(levels,fa,vtol);
+    int idxb=upper_level(levels,fb,vtol);
+
+    Interval bs = bounds_local(df,Interval(a,b));
+
+    //first times when a level (higher or lower) can be reached from a or b.
+    double ta_hi,tb_hi,ta_lo,tb_lo;
+    ta_hi=ta_lo=b+1;//default values => no root there.
+    tb_hi=tb_lo=a-1;//default values => no root there.
+
+    if (idxa<(int)levels.size() && fabs(fa-levels.at(idxa))<vtol){//a can be considered a root.
+        //ta_hi=ta_lo=a;
+        roots[idxa].push_back(a);
+        ta_hi=ta_lo=a+htol;
+    }else{
+        if (bs.max()>0 && idxa<(int)levels.size())
+            ta_hi=a+(levels.at(idxa  )-fa)/bs.max();
+        if (bs.min()<0 && idxa>0)
+            ta_lo=a+(levels.at(idxa-1)-fa)/bs.min();
+    }
+    if (idxb<(int)levels.size() && fabs(fb-levels.at(idxb))<vtol){//b can be considered a root.
+        //tb_hi=tb_lo=b;
+        roots[idxb].push_back(b);
+        tb_hi=tb_lo=b-htol;
+    }else{
+        if (bs.min()<0 && idxb<(int)levels.size())
+            tb_hi=b+(levels.at(idxb  )-fb)/bs.min();
+        if (bs.max()>0 && idxb>0)
+            tb_lo=b+(levels.at(idxb-1)-fb)/bs.max();
+    }
+    
+    double t0,t1;
+    t0=std::min(ta_hi,ta_lo);    
+    t1=std::max(tb_hi,tb_lo);
+    //hum, rounding errors frighten me! so I add this +tol...
+    if (t0>t1+htol) return;//no root here.
+
+    if (fabs(t1-t0)<htol){
+        multi_roots_internal(f,df,levels,roots,htol,vtol,t0,f(t0),t1,f(t1));
+    }else{
+        double t,t_left,t_right,ft,ft_left,ft_right;
+        t_left =t_right =t =(t0+t1)/2;
+        ft_left=ft_right=ft=f(t);
+        int idx=upper_level(levels,ft,vtol);
+        if (idx<(int)levels.size() && fabs(ft-levels.at(idx))<vtol){//t can be considered a root.
+            roots[idx].push_back(t);
+            //we do not want to count it twice (from the left and from the right)
+            t_left =t-htol/2;
+            t_right=t+htol/2;
+            ft_left =f(t_left);
+            ft_right=f(t_right);
+        }
+        multi_roots_internal(f,df,levels,roots,htol,vtol,t0     ,f(t0)   ,t_left,ft_left);
+        multi_roots_internal(f,df,levels,roots,htol,vtol,t_right,ft_right,t1    ,f(t1)  );
+    }
+}
+
+std::vector<std::vector<double> > multi_roots(SBasis const &f,
+                                      std::vector<double> const &levels,
+                                      double htol,
+                                      double vtol,
+                                      double a,
+                                      double b){
+
+    std::vector<std::vector<double> > roots(levels.size(), std::vector<double>());
+
+    SBasis df=derivative(f);
+    multi_roots_internal(f,df,levels,roots,htol,vtol,a,f(a),b,f(b));  
+
+    return(roots);
+}
+//-------------------------------------
+
+#if 0
+double Laguerre_internal(SBasis const & p,
+                         double x0,
+                         double tol,
+                         bool & quad_root) {
+    double a = 2*tol;
+    double xk = x0;
+    double n = p.size();
+    quad_root = false;
+    while(a > tol) {
+        //std::cout << "xk = " << xk << std::endl;
+        Linear b = p.back();
+        Linear d(0), f(0);
+        double err = fabs(b);
+        double abx = fabs(xk);
+        for(int j = p.size()-2; j >= 0; j--) {
+            f = xk*f + d;
+            d = xk*d + b;
+            b = xk*b + p[j];
+            err = fabs(b) + abx*err;
+        }
+        
+        err *= 1e-7; // magic epsilon for convergence, should be computed from tol
+        
+        double px = b;
+        if(fabs(b) < err)
+            return xk;
+        //if(std::norm(px) < tol*tol)
+        //    return xk;
+        double G = d / px;
+        double H = G*G - f / px;
+        
+        //std::cout << "G = " << G << "H = " << H;
+        double radicand = (n - 1)*(n*H-G*G);
+        //assert(radicand.real() > 0);
+        if(radicand < 0)
+            quad_root = true;
+        //std::cout << "radicand = " << radicand << std::endl;
+        if(G.real() < 0) // here we try to maximise the denominator avoiding cancellation
+            a = - std::sqrt(radicand);
+        else
+            a = std::sqrt(radicand);
+        //std::cout << "a = " << a << std::endl;
+        a = n / (a + G);
+        //std::cout << "a = " << a << std::endl;
+        xk -= a;
+    }
+    //std::cout << "xk = " << xk << std::endl;
+    return xk;
+}
+#endif
+
+void subdiv_sbasis(SBasis const & s,
+                   std::vector<double> & roots, 
+                   double left, double right) {
+    Interval bs = bounds_fast(s);
+    if(bs.min() > 0 || bs.max() < 0)
+        return; // no roots here
+    if(s.tailError(1) < 1e-7) {
+        double t = s[0][0] / (s[0][0] - s[0][1]);
+        roots.push_back(left*(1-t) + t*right);
+        return;
+    }
+    double middle = (left + right)/2;
+    subdiv_sbasis(compose(s, Linear(0, 0.5)), roots, left, middle);
+    subdiv_sbasis(compose(s, Linear(0.5, 1.)), roots, middle, right);
+}
+
+// It is faster to use the bernstein root finder for small degree polynomials (<100?.
+
+std::vector<double> roots(SBasis const & s) {
+    if(s.size() == 0) return std::vector<double>();
+    std::vector<double> b = sbasis_to_bezier(s), r;
+    
+    find_bernstein_roots(&b[0], b.size()-1, r, 0, 0., 1.);
+    return r;
+}
+
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/sbasis-to-bezier.cpp b/src/2geom/sbasis-to-bezier.cpp
new file mode 100644 (file)
index 0000000..39efc51
--- /dev/null
@@ -0,0 +1,214 @@
+/* From Sanchez-Reyes 1997
+   W_{j,k} = W_{n0j, n-k} = choose(n-2k-1, j-k)choose(2k+1,k)/choose(n,j)
+     for k=0,...,q-1; j = k, ...,n-k-1
+   W_{q,q} = 1 (n even)
+
+This is wrong, it should read
+   W_{j,k} = W_{n0j, n-k} = choose(n-2k-1, j-k)/choose(n,j)
+     for k=0,...,q-1; j = k, ...,n-k-1
+   W_{q,q} = 1 (n even)
+
+*/
+#include "sbasis-to-bezier.h"
+#include "choose.h"
+#include <iostream>
+
+namespace Geom{
+
+double W(unsigned n, unsigned j, unsigned k) {
+    unsigned q = (n+1)/2;
+    if((n & 1) == 0 && j == q && k == q)
+        return 1;
+    if(k > n-k) return W(n, n-j, n-k);
+    assert(!(k < 0));
+    if(k < 0) return 0;
+    assert((k <= q));
+    if(k >= q) return 0;
+    //assert(!(j >= n-k));
+    if(j >= n-k) return 0;
+    //assert(!(j < k));
+    if(j < k) return 0;
+    return choose<double>(n-2*k-1, j-k) /
+        choose<double>(n,j);
+}
+
+// this produces a degree 2q bezier from a degree k sbasis
+std::vector<double>
+sbasis_to_bezier(SBasis const &B, unsigned q) {
+    std::vector<double> result;
+    if(q == 0) {
+        q = B.size();
+        /*if(B.back()[0] == B.back()[1]) {
+            n--;
+            }*/
+    }
+    unsigned n = q*2;
+    result.resize(n, 0);
+    if(q > B.size())
+        q = B.size();
+    n--;
+    for(unsigned k = 0; k < q; k++) {
+        for(unsigned j = 0; j <= n-k; j++) {
+            result[j] += (W(n, j, k)*B[k][0] +
+                          W(n, n-j, k)*B[k][1]);
+        }
+    }
+    return result;
+}
+
+// this produces a 2q point bezier from a degree q sbasis
+std::vector<Geom::Point>
+sbasis_to_bezier(D2<SBasis> const &B, unsigned qq) {
+    std::vector<Geom::Point> result;
+    if(qq == 0) {
+        qq = sbasis_size(B);
+    }
+    unsigned n = qq * 2;
+    result.resize(n, Geom::Point(0,0));
+    n--;
+    for(unsigned dim = 0; dim < 2; dim++) {
+        unsigned q = qq;
+        if(q > B[dim].size())
+            q = B[dim].size();
+        for(unsigned k = 0; k < q; k++) {
+            for(unsigned j = 0; j <= n-k; j++) {
+                result[j][dim] += (W(n, j, k)*B[dim][k][0] +
+                             W(n, n-j, k)*B[dim][k][1]);
+                }
+        }
+    }
+    return result;
+}
+/*
+template <unsigned order>
+D2<Bezier<order> > sbasis_to_bezier(D2<SBasis> const &B) {
+    return D2<Bezier<order> >(sbasis_to_bezier<order>(B[0]), sbasis_to_bezier<order>(B[1]));
+}
+*/
+
+#if 0 // using old path
+//std::vector<Geom::Point>
+// mutating
+void
+subpath_from_sbasis(Geom::OldPathSetBuilder &pb, D2<SBasis> const &B, double tol, bool initial) {
+    assert(B.is_finite());
+    if(B.tail_error(2) < tol || B.size() == 2) { // nearly cubic enough
+        if(B.size() == 1) {
+            if (initial) {
+                pb.start_subpath(Geom::Point(B[0][0][0], B[1][0][0]));
+            }
+            pb.push_line(Geom::Point(B[0][0][1], B[1][0][1]));
+        } else {
+            std::vector<Geom::Point> bez = sbasis_to_bezier(B, 2);
+            if (initial) {
+                pb.start_subpath(bez[0]);
+            }
+            pb.push_cubic(bez[1], bez[2], bez[3]);
+        }
+    } else {
+        subpath_from_sbasis(pb, compose(B, Linear(0, 0.5)), tol, initial);
+        subpath_from_sbasis(pb, compose(B, Linear(0.5, 1)), tol, false);
+    }
+}
+
+/*
+* This version works by inverting a reasonable upper bound on the error term after subdividing the
+* curve at $a$.  We keep biting off pieces until there is no more curve left.
+* 
+* Derivation: The tail of the power series is $a_ks^k + a_{k+1}s^{k+1} + \ldots = e$.  A
+* subdivision at $a$ results in a tail error of $e*A^k, A = (1-a)a$.  Let this be the desired
+* tolerance tol $= e*A^k$ and invert getting $A = e^{1/k}$ and $a = 1/2 - \sqrt{1/4 - A}$
+*/
+void
+subpath_from_sbasis_incremental(Geom::OldPathSetBuilder &pb, D2<SBasis> B, double tol, bool initial) {
+    const unsigned k = 2; // cubic bezier
+    double te = B.tail_error(k);
+    assert(B[0].is_finite());
+    assert(B[1].is_finite());
+    
+    //std::cout << "tol = " << tol << std::endl;
+    while(1) {
+        double A = std::sqrt(tol/te); // pow(te, 1./k)
+        double a = A;
+        if(A < 1) {
+            A = std::min(A, 0.25);
+            a = 0.5 - std::sqrt(0.25 - A); // quadratic formula
+            if(a > 1) a = 1; // clamp to the end of the segment
+        } else
+            a = 1;
+        assert(a > 0);
+        //std::cout << "te = " << te << std::endl;
+        //std::cout << "A = " << A << "; a=" << a << std::endl;
+        D2<SBasis> Bs = compose(B, Linear(0, a));
+        assert(Bs.tail_error(k));
+        std::vector<Geom::Point> bez = sbasis_to_bezier(Bs, 2);
+        reverse(bez.begin(), bez.end());
+        if (initial) {
+          pb.start_subpath(bez[0]);
+          initial = false;
+        }
+        pb.push_cubic(bez[1], bez[2], bez[3]);
+        
+// move to next piece of curve
+        if(a >= 1) break;
+        B = compose(B, Linear(a, 1)); 
+        te = B.tail_error(k);
+    }
+}
+
+#endif
+
+void
+path_from_sbasis(Geom::Path &pb, D2<SBasis> const &B, double tol) {
+    assert(B.isFinite());
+    if(tail_error(B, 2) < tol || sbasis_size(B) == 2) { // nearly cubic enough
+        if(B[0].size() == 0 && B[1].size() != 0) {
+            pb.append(Geom::LineSegment(Geom::Point(0, B[1][0][0]), Geom::Point(0, B[1][0][1])));
+        } else if(B[0].size() != 0 && B[1].size() == 0) {
+            pb.append(Geom::LineSegment(Geom::Point(B[0][0][0], 0), Geom::Point(B[0][0][1], 0)));
+        } else if(sbasis_size(B) == 1) {
+            pb.append(Geom::LineSegment(Geom::Point(B[0][0][0], B[1][0][0]),Geom::Point(B[0][0][1], B[1][0][1])));
+        } else {
+            std::vector<Geom::Point> bez = sbasis_to_bezier(B, 2);
+            pb.append(Geom::CubicBezier(bez[0], bez[1], bez[2], bez[3]));
+        }
+    } else {
+        path_from_sbasis(pb, compose(B, Linear(0, 0.5)), tol);
+        path_from_sbasis(pb, compose(B, Linear(0.5, 1)), tol);
+    }
+}
+
+std::vector<Geom::Path>
+path_from_piecewise(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &B, double tol) {
+    std::vector<Geom::Path> ret;
+    if(B.size() == 0) return ret;
+    Geom::Path *cur = new Geom::Path();
+    unsigned i = 0;
+    while(true) {
+        path_from_sbasis(*cur, B[i], tol);
+        if(i >= B.size()-1) {
+            ret.push_back(*cur);
+            delete cur;
+            return ret;
+        }
+        i++;
+        if(B[i].at0() != B[i-1].at1()) {
+            ret.push_back(*cur);
+            delete cur;
+            cur = new Geom::Path();
+        }
+    }
+}
+
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/sbasis-to-bezier.h b/src/2geom/sbasis-to-bezier.h
new file mode 100644 (file)
index 0000000..142a250
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef _SBASIS_TO_BEZIER
+#define _SBASIS_TO_BEZIER
+
+#include "d2.h"
+#include "path.h"
+
+namespace Geom{
+// this produces a degree k bezier from a degree k sbasis
+std::vector<double>
+sbasis_to_bezier(SBasis const &B, unsigned q = 0);
+
+std::vector<Geom::Point>
+sbasis_to_bezier(D2<SBasis> const &B, unsigned q = 0);
+
+std::vector<Path> path_from_piecewise(Piecewise<D2<SBasis> > const &B, double tol);
+
+void path_from_sbasis(Path &pb, D2<SBasis> const &B, double tol);
+
+};
+#endif
diff --git a/src/2geom/sbasis.cpp b/src/2geom/sbasis.cpp
new file mode 100644 (file)
index 0000000..5bf0d28
--- /dev/null
@@ -0,0 +1,490 @@
+/*\r
+ *  sbasis.cpp - S-power basis function class + supporting classes\r
+ *\r
+ *  Authors:\r
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>\r
+ *   Michael Sloan <mgsloan@gmail.com>\r
+ *\r
+ * Copyright (C) 2006-2007 authors\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it either under the terms of the GNU Lesser General Public\r
+ * License version 2.1 as published by the Free Software Foundation\r
+ * (the "LGPL") or, at your option, under the terms of the Mozilla\r
+ * Public License Version 1.1 (the "MPL"). If you do not alter this\r
+ * notice, a recipient may use your version of this file under either\r
+ * the MPL or the LGPL.\r
+ *\r
+ * You should have received a copy of the LGPL along with this library\r
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ * You should have received a copy of the MPL along with this library\r
+ * in the file COPYING-MPL-1.1\r
+ *\r
+ * The contents of this file are subject to the Mozilla Public License\r
+ * Version 1.1 (the "License"); you may not use this file except in\r
+ * compliance with the License. You may obtain a copy of the License at\r
+ * http://www.mozilla.org/MPL/\r
+ *\r
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY\r
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for\r
+ * the specific language governing rights and limitations.\r
+ */\r
+\r
+#include <cmath>\r
+\r
+#include "sbasis.h"\r
+#include "isnan.h"\r
+\r
+namespace Geom{\r
+\r
+/*** At some point we should work on tighter bounds for the error.  It is clear that the error is\r
+ * bounded by the L1 norm over the tail of the series, but this is very loose, leading to far too\r
+ * many cubic beziers.  I've changed this to be \sum _i=tail ^\infty |hat a_i| 2^-i but I have no\r
+ * evidence that this is correct.\r
+ */\r
+\r
+/*\r
+double SBasis::tail_error(unsigned tail) const {\r
+    double err = 0, s = 1./(1<<(2*tail)); // rough\r
+    for(unsigned i = tail; i < size(); i++) {\r
+        err += (fabs((*this)[i][0]) + fabs((*this)[i][1]))*s;\r
+        s /= 4;\r
+    }\r
+    return err;\r
+}\r
+*/
+\r
+double SBasis::tailError(unsigned tail) const {\r
+  Interval bs = bounds_fast(*this, tail);\r
+  return std::max(fabs(bs.min()),fabs(bs.max()));\r
+}\r
+\r
+bool SBasis::isFinite() const {\r
+    for(unsigned i = 0; i < size(); i++) {\r
+        if(!(*this)[i].isFinite())\r
+            return false;\r
+    }\r
+    return true;\r
+}\r
+\r
+SBasis operator+(const SBasis& a, const SBasis& b) {\r
+    SBasis result;\r
+    const unsigned out_size = std::max(a.size(), b.size());\r
+    const unsigned min_size = std::min(a.size(), b.size());\r
+    result.reserve(out_size);\r
+    \r
+    for(unsigned i = 0; i < min_size; i++) {\r
+        result.push_back(a[i] + b[i]);\r
+    }\r
+    for(unsigned i = min_size; i < a.size(); i++)\r
+        result.push_back(a[i]);\r
+    for(unsigned i = min_size; i < b.size(); i++)\r
+        result.push_back(b[i]);\r
+\r
+    assert(result.size() == out_size);\r
+    return result;\r
+}\r
+\r
+SBasis operator-(const SBasis& a, const SBasis& b) {\r
+    SBasis result;\r
+    const unsigned out_size = std::max(a.size(), b.size());\r
+    const unsigned min_size = std::min(a.size(), b.size());\r
+    result.reserve(out_size);\r
+    \r
+    for(unsigned i = 0; i < min_size; i++) {\r
+        result.push_back(a[i] - b[i]);\r
+    }\r
+    for(unsigned i = min_size; i < a.size(); i++)\r
+        result.push_back(a[i]);\r
+    for(unsigned i = min_size; i < b.size(); i++)\r
+        result.push_back(-b[i]);\r
+\r
+    assert(result.size() == out_size);\r
+    return result;\r
+}\r
+\r
+SBasis& operator+=(SBasis& a, const SBasis& b) {\r
+    const unsigned out_size = std::max(a.size(), b.size());\r
+    const unsigned min_size = std::min(a.size(), b.size());\r
+    a.reserve(out_size);\r
+        \r
+    for(unsigned i = 0; i < min_size; i++)\r
+        a[i] += b[i];\r
+    for(unsigned i = min_size; i < b.size(); i++)\r
+        a.push_back(b[i]);\r
+    \r
+    assert(a.size() == out_size);\r
+    return a;\r
+}\r
+\r
+SBasis& operator-=(SBasis& a, const SBasis& b) {\r
+    const unsigned out_size = std::max(a.size(), b.size());\r
+    const unsigned min_size = std::min(a.size(), b.size());\r
+    a.reserve(out_size);\r
+        \r
+    for(unsigned i = 0; i < min_size; i++)\r
+        a[i] -= b[i];\r
+    for(unsigned i = min_size; i < b.size(); i++)\r
+        a.push_back(-b[i]);\r
+    \r
+    assert(a.size() == out_size);\r
+    return a;\r
+}\r
+\r
+SBasis operator*(SBasis const &a, double k) {\r
+    SBasis c;\r
+    c.reserve(a.size());\r
+    for(unsigned i = 0; i < a.size(); i++)\r
+        c.push_back(a[i] * k);\r
+    return c;\r
+}\r
+\r
+SBasis& operator*=(SBasis& a, double b) {\r
+    if (a.isZero()) return a;\r
+    if (b == 0)\r
+        a.clear();\r
+    else\r
+        for(unsigned i = 0; i < a.size(); i++)\r
+            a[i] *= b;\r
+    return a;\r
+}\r
+\r
+SBasis shift(SBasis const &a, int sh) {\r
+    SBasis c = a;\r
+    if(sh > 0) {\r
+        c.insert(c.begin(), sh, Linear(0,0));\r
+    } else {\r
+        //TODO: truncate\r
+    }\r
+    return c;\r
+}\r
+\r
+SBasis shift(Linear const &a, int sh) {\r
+    SBasis c;\r
+    if(sh > 0) {\r
+        c.insert(c.begin(), sh, Linear(0,0));\r
+        c.push_back(a);\r
+    }\r
+    return c;\r
+}\r
+\r
+SBasis multiply(SBasis const &a, SBasis const &b) {\r
+    // c = {a0*b0 - shift(1, a.Tri*b.Tri), a1*b1 - shift(1, a.Tri*b.Tri)}\r
+    \r
+    // shift(1, a.Tri*b.Tri)\r
+    SBasis c;\r
+    if(a.isZero() || b.isZero())\r
+        return c;\r
+    c.resize(a.size() + b.size(), Linear(0,0));\r
+    c[0] = Linear(0,0);\r
+    for(unsigned j = 0; j < b.size(); j++) {\r
+        for(unsigned i = j; i < a.size()+j; i++) {\r
+            double tri = Tri(b[j])*Tri(a[i-j]);\r
+            c[i+1/*shift*/] += Linear(Hat(-tri));\r
+        }\r
+    }\r
+    for(unsigned j = 0; j < b.size(); j++) {\r
+        for(unsigned i = j; i < a.size()+j; i++) {\r
+            for(unsigned dim = 0; dim < 2; dim++)\r
+                c[i][dim] += b[j][dim]*a[i-j][dim];\r
+        }\r
+    }\r
+    c.normalize();\r
+    //assert(!(0 == c.back()[0] && 0 == c.back()[1]));\r
+    return c;\r
+}\r
+\r
+SBasis integral(SBasis const &c) {\r
+    SBasis a;\r
+    a.resize(c.size() + 1, Linear(0,0));\r
+    a[0] = Linear(0,0);\r
+    \r
+    for(unsigned k = 1; k < c.size() + 1; k++) {\r
+        double ahat = -Tri(c[k-1])/(2*k);\r
+        a[k] = Hat(ahat);\r
+    }\r
+    double aTri = 0;\r
+    for(int k = c.size()-1; k >= 0; k--) {\r
+        aTri = (Hat(c[k]).d + (k+1)*aTri/2)/(2*k+1);\r
+        a[k][0] -= aTri/2;\r
+        a[k][1] += aTri/2;\r
+    }\r
+    a.normalize();\r
+    return a;\r
+}\r
+\r
+SBasis derivative(SBasis const &a) {\r
+    SBasis c;\r
+    c.resize(a.size(), Linear(0,0));\r
+    \r
+    for(unsigned k = 0; k < a.size(); k++) {\r
+        double d = (2*k+1)*Tri(a[k]);\r
+        \r
+        for(unsigned dim = 0; dim < 2; dim++) {\r
+            c[k][dim] = d;\r
+            if(k+1 < a.size()) {\r
+                if(dim)\r
+                    c[k][dim] = d - (k+1)*a[k+1][dim];\r
+                else\r
+                    c[k][dim] = d + (k+1)*a[k+1][dim];\r
+            }\r
+        }\r
+    }\r
+    \r
+    return c;\r
+}\r
+\r
+//TODO: convert int k to unsigned k, and remove cast\r
+SBasis sqrt(SBasis const &a, int k) {\r
+    SBasis c;\r
+    if(a.isZero() || k == 0)\r
+        return c;\r
+    c.resize(k, Linear(0,0));\r
+    c[0] = Linear(std::sqrt(a[0][0]), std::sqrt(a[0][1]));\r
+    SBasis r = a - multiply(c, c); // remainder\r
+    \r
+    for(unsigned i = 1; i <= (unsigned)k and i<r.size(); i++) {\r
+        Linear ci(r[i][0]/(2*c[0][0]), r[i][1]/(2*c[0][1]));\r
+        SBasis cisi = shift(ci, i);\r
+        r -= multiply(shift((c*2 + cisi), i), SBasis(ci));\r
+        r.truncate(k+1);\r
+        c += cisi;\r
+        if(r.tailError(i) == 0) // if exact\r
+            break;\r
+    }\r
+    \r
+    return c;\r
+}\r
+\r
+// return a kth order approx to 1/a)\r
+SBasis reciprocal(Linear const &a, int k) {\r
+    SBasis c;\r
+    assert(!a.isZero());\r
+    c.resize(k, Linear(0,0));\r
+    double r_s0 = (Tri(a)*Tri(a))/(-a[0]*a[1]);\r
+    double r_s0k = 1;\r
+    for(unsigned i = 0; i < (unsigned)k; i++) {\r
+        c[i] = Linear(r_s0k/a[0], r_s0k/a[1]);\r
+        r_s0k *= r_s0;\r
+    }\r
+    return c;\r
+}\r
+\r
+SBasis divide(SBasis const &a, SBasis const &b, int k) {\r
+    SBasis c;\r
+    assert(!a.isZero());\r
+    SBasis r = a; // remainder\r
+    \r
+    k++;\r
+    r.resize(k, Linear(0,0));\r
+    c.resize(k, Linear(0,0));\r
+\r
+    for(unsigned i = 0; i < (unsigned)k; i++) {\r
+        Linear ci(r[i][0]/b[0][0], r[i][1]/b[0][1]); //H0\r
+        c[i] += ci;\r
+        r -= shift(multiply(ci,b), i);\r
+        r.truncate(k+1);\r
+        if(r.tailError(i) == 0) // if exact\r
+            break;\r
+    }\r
+    \r
+    return c;\r
+}\r
+\r
+// a(b)\r
+// return a0 + s(a1 + s(a2 +...  where s = (1-u)u; ak =(1 - u)a^0_k + ua^1_k\r
+SBasis compose(SBasis const &a, SBasis const &b) {\r
+    SBasis s = multiply((SBasis(Linear(1,1))-b), b);\r
+    SBasis r;\r
+    \r
+    for(int i = a.size()-1; i >= 0; i--) {\r
+        r = SBasis(Linear(Hat(a[i][0]))) - b*a[i][0] + b*a[i][1] + multiply(r,s);\r
+    }\r
+    return r;\r
+}\r
+\r
+// a(b)\r
+// return a0 + s(a1 + s(a2 +...  where s = (1-u)u; ak =(1 - u)a^0_k + ua^1_k\r
+SBasis compose(SBasis const &a, SBasis const &b, unsigned k) {\r
+    SBasis s = multiply((SBasis(Linear(1,1))-b), b);\r
+    SBasis r;\r
+    \r
+    for(int i = a.size()-1; i >= 0; i--) {\r
+        r = SBasis(Linear(Hat(a[i][0]))) - b*a[i][0] + b*a[i][1] + multiply(r,s);\r
+    }\r
+    r.truncate(k);\r
+    return r;\r
+}\r
+\r
+/*\r
+Inversion algorithm. The notation is certainly very misleading. The\r
+pseudocode should say:\r
+\r
+c(v) := 0\r
+r(u) := r_0(u) := u\r
+for i:=0 to k do\r
+  c_i(v) := H_0(r_i(u)/(t_1)^i; u)\r
+  c(v) := c(v) + c_i(v)*t^i\r
+  r(u) := r(u) ? c_i(u)*(t(u))^i\r
+endfor\r
+*/\r
+\r
+//#define DEBUG_INVERSION 1 \r
+\r
+SBasis inverse(SBasis a, int k) {\r
+    assert(a.size() > 0);\r
+// the function should have 'unit range'("a00 = 0 and a01 = 1") and be monotonic.\r
+    double a0 = a[0][0];\r
+    if(a0 != 0) {\r
+        a -= a0;\r
+    }\r
+    double a1 = a[0][1];\r
+    assert(a1 != 0); // not invertable.\r
+    \r
+    if(a1 != 1) {\r
+        a /= a1;\r
+    }\r
+    SBasis c;                           // c(v) := 0\r
+    if(a.size() >= 2 && k == 2) {\r
+        c.push_back(Linear(0,1));\r
+        Linear t1(1+a[1][0], 1-a[1][1]);    // t_1\r
+        c.push_back(Linear(-a[1][0]/t1[0], -a[1][1]/t1[1]));\r
+    } else if(a.size() >= 2) {                      // non linear\r
+        SBasis r = Linear(0,1);             // r(u) := r_0(u) := u\r
+        Linear t1(1./(1+a[1][0]), 1./(1-a[1][1]));    // 1./t_1\r
+        Linear one(1,1);\r
+        Linear t1i = one;                   // t_1^0\r
+        SBasis one_minus_a = SBasis(one) - a;\r
+        SBasis t = multiply(one_minus_a, a); // t(u)\r
+        SBasis ti(one);                     // t(u)^0\r
+#ifdef DEBUG_INVERSION\r
+        std::cout << "a=" << a << std::endl;\r
+        std::cout << "1-a=" << one_minus_a << std::endl;\r
+        std::cout << "t1=" << t1 << std::endl;\r
+        //assert(t1 == t[1]);\r
+#endif\r
+    \r
+        c.resize(k+1, Linear(0,0));\r
+        for(unsigned i = 0; i < (unsigned)k; i++) {   // for i:=0 to k do\r
+#ifdef DEBUG_INVERSION\r
+            std::cout << "-------" << i << ": ---------" <<std::endl;\r
+            std::cout << "r=" << r << std::endl\r
+                      << "c=" << c << std::endl\r
+                      << "ti=" << ti << std::endl\r
+                      << std::endl;\r
+#endif\r
+            if(r.size() <= i)                // ensure enough space in the remainder, probably not needed\r
+                r.resize(i+1, Linear(0,0));\r
+            Linear ci(r[i][0]*t1i[0], r[i][1]*t1i[1]); // c_i(v) := H_0(r_i(u)/(t_1)^i; u)\r
+#ifdef DEBUG_INVERSION\r
+            std::cout << "t1i=" << t1i << std::endl;\r
+            std::cout << "ci=" << ci << std::endl;\r
+#endif\r
+            for(int dim = 0; dim < 2; dim++) // t1^-i *= 1./t1\r
+                t1i[dim] *= t1[dim];\r
+            c[i] = ci; // c(v) := c(v) + c_i(v)*t^i\r
+            // change from v to u parameterisation\r
+            SBasis civ = one_minus_a*ci[0] + a*ci[1]; \r
+            // r(u) := r(u) - c_i(u)*(t(u))^i\r
+            // We can truncate this to the number of final terms, as no following terms can\r
+            // contribute to the result.\r
+            r -= multiply(civ,ti);\r
+            r.truncate(k);\r
+            if(r.tailError(i) == 0)\r
+                break; // yay!\r
+            ti = multiply(ti,t);\r
+        }\r
+#ifdef DEBUG_INVERSION\r
+        std::cout << "##########################" << std::endl;\r
+#endif\r
+    } else\r
+        c = Linear(0,1); // linear\r
+    c -= a0; // invert the offset\r
+    c /= a1; // invert the slope\r
+    return c;\r
+}\r
+\r
+SBasis sin(Linear b, int k) {\r
+    SBasis s = Linear(std::sin(b[0]), std::sin(b[1]));\r
+    Tri tr(s[0]);\r
+    double t2 = Tri(b);\r
+    s.push_back(Linear(std::cos(b[0])*t2 - tr, -std::cos(b[1])*t2 + tr));\r
+    \r
+    t2 *= t2;\r
+    for(int i = 0; i < k; i++) {\r
+        Linear bo(4*(i+1)*s[i+1][0] - 2*s[i+1][1],\r
+                  -2*s[i+1][0] + 4*(i+1)*s[i+1][1]);\r
+        bo -= s[i]*(t2/(i+1));\r
+        \r
+        \r
+        s.push_back(bo/double(i+2));\r
+    }\r
+    \r
+    return s;\r
+}\r
+\r
+SBasis cos(Linear bo, int k) {\r
+    return sin(Linear(bo[0] + M_PI/2,\r
+                      bo[1] + M_PI/2),\r
+               k);\r
+}\r
+\r
+//compute fog^-1. ("zero" = double comparison threshold. *!*we might divide by "zero"*!*)\r
+//TODO: compute order according to tol?\r
+//TODO: requires g(0)=0 & g(1)=1 atm... adaptation to other cases should be obvious!\r
+SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order, double zero){\r
+    SBasis result; //result\r
+    SBasis r=f; //remainder\r
+    SBasis Pk=Linear(1)-g,Qk=g,sg=Pk*Qk;\r
+    Pk.truncate(order);\r
+    Qk.truncate(order);\r
+    Pk.resize(order,Linear(0.));\r
+    Qk.resize(order,Linear(0.));\r
+    r.resize(order,Linear(0.));\r
+\r
+    int vs= valuation(sg,zero);\r
+    \r
+    for (unsigned k=0; k<order; k+=vs){\r
+        double p10 = Pk.at(k)[0];// we have to solve the linear system:\r
+        double p01 = Pk.at(k)[1];//\r
+        double q10 = Qk.at(k)[0];//   p10*a + q10*b = r10\r
+        double q01 = Qk.at(k)[1];// &                    \r
+        double r10 =  r.at(k)[0];//   p01*a + q01*b = r01\r
+        double r01 =  r.at(k)[1];//\r
+        double a,b;\r
+        double det = p10*q01-p01*q10;\r
+\r
+        //TODO: handle det~0!! \r
+        if (fabs(det)<zero){\r
+            det = zero;\r
+            a=b=0;\r
+        }else{\r
+            a=( q01*r10-q10*r01)/det;\r
+            b=(-p01*r10+p10*r01)/det;\r
+        }\r
+        result.push_back(Linear(a,b));\r
+        r=r-Pk*a-Qk*b;\r
+        \r
+        Pk=Pk*sg;\r
+        Qk=Qk*sg;\r
+        Pk.truncate(order);\r
+        Qk.truncate(order);\r
+        r.truncate(order);\r
+    }\r
+    result.normalize();\r
+    return result;\r
+}\r
+\r
+}\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
diff --git a/src/2geom/sbasis.h b/src/2geom/sbasis.h
new file mode 100644 (file)
index 0000000..a27d4bf
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ *  sbasis.h - S-power basis function class
+ *
+ *  Authors:
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 2006-2007 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_SBASIS_H
+#define SEEN_SBASIS_H
+#include <vector>
+#include <cassert>
+#include <iostream>
+
+#include "linear.h"
+#include "interval.h"
+
+namespace Geom {
+
+/*** An empty SBasis is identically 0. */
+class SBasis : public std::vector<Linear>{
+public:
+    SBasis() {}
+    explicit SBasis(double a) {
+        push_back(Linear(a,a));
+    }
+    SBasis(SBasis const & a) :
+        std::vector<Linear>(a)
+    {}
+    SBasis(Linear const & bo) {
+        push_back(bo);
+    }
+
+    //IMPL: FragmentConcept
+    typedef double output_type;
+    inline bool isZero() const {
+        if(empty()) return true;
+        for(unsigned i = 0; i < size(); i++) {
+            if(!(*this)[i].isZero()) return false;
+        }
+        return true;
+    }
+    bool isFinite() const;
+    inline double at0() const { 
+        if(empty()) return 0; else return (*this)[0][0];
+    }
+    inline double at1() const{
+        if(empty()) return 0; else return (*this)[0][1];
+    }
+
+    double valueAt(double t) const {
+        double s = t*(1-t);
+        double p0 = 0, p1 = 0;
+        double sk = 1;
+//TODO: rewrite as horner
+        for(unsigned k = 0; k < size(); k++) {
+            p0 += sk*(*this)[k][0];
+            p1 += sk*(*this)[k][1];
+            sk *= s;
+        }
+        return (1-t)*p0 + t*p1;
+    }
+    double operator()(double t) const {
+        return valueAt(t);
+    }
+    SBasis toSBasis() const { return SBasis(*this); }
+
+    double tailError(unsigned tail) const;
+
+// compute f(g)
+    SBasis operator()(SBasis const & g) const;
+
+    Linear operator[](unsigned i) const {
+        assert(i < size());
+        return std::vector<Linear>::operator[](i);
+    }
+
+//MUTATOR PRISON
+    Linear& operator[](unsigned i) { return this->at(i); }
+
+    //remove extra zeros
+    void normalize() {
+        while(!empty() && 0 == back()[0] && 0 == back()[1])
+            pop_back();
+    }
+    void truncate(unsigned k) { if(k < size()) resize(k); }
+};
+
+//TODO: figure out how to stick this in linear, while not adding an sbasis dep
+inline SBasis Linear::toSBasis() const { return SBasis(*this); }
+
+//implemented in sbasis-roots.cpp
+Interval bounds_exact(SBasis const &a);
+Interval bounds_fast(SBasis const &a, int order = 0);
+Interval bounds_local(SBasis const &a, const Interval &t, int order = 0);
+
+inline SBasis reverse(SBasis const &a) {
+    SBasis result;
+    result.reserve(a.size());
+    for(unsigned k = 0; k < a.size(); k++)
+       result.push_back(reverse(a[k]));
+    return result;
+}
+
+//IMPL: ScalableConcept
+inline SBasis operator-(const SBasis& p) {
+    if(p.isZero()) return SBasis();
+    SBasis result;
+    result.reserve(p.size());
+        
+    for(unsigned i = 0; i < p.size(); i++) {
+        result.push_back(-p[i]);
+    }
+    return result;
+}
+SBasis operator*(SBasis const &a, double k);
+inline SBasis operator*(double k, SBasis const &a) { return a*k; }
+inline SBasis operator/(SBasis const &a, double k) { return a*(1./k); }
+SBasis& operator*=(SBasis& a, double b);
+inline SBasis& operator/=(SBasis& a, double b) { return (a*=(1./b)); }
+
+//IMPL: AddableConcept
+SBasis operator+(const SBasis& a, const SBasis& b);
+SBasis operator-(const SBasis& a, const SBasis& b);
+SBasis& operator+=(SBasis& a, const SBasis& b);
+SBasis& operator-=(SBasis& a, const SBasis& b);
+
+//TODO: remove?
+inline SBasis operator+(const SBasis & a, Linear const & b) {
+    if(b.isZero()) return a;
+    if(a.isZero()) return b;
+    SBasis result(a);
+    result[0] += b;
+    return result;
+}
+inline SBasis operator-(const SBasis & a, Linear const & b) {
+    if(b.isZero()) return a;
+    SBasis result(a);
+    result[0] -= b;
+    return result;
+}
+inline SBasis& operator+=(SBasis& a, const Linear& b) {
+    if(a.isZero())
+        a.push_back(b);
+    else
+        a[0] += b;
+    return a;
+}
+inline SBasis& operator-=(SBasis& a, const Linear& b) {
+    if(a.isZero())
+        a.push_back(-b);
+    else
+        a[0] -= b;
+    return a;
+}
+
+//IMPL: OffsetableConcept
+inline SBasis operator+(const SBasis & a, double b) {
+    if(a.isZero()) return Linear(b, b);
+    SBasis result(a);
+    result[0] += b;
+    return result;
+}
+inline SBasis operator-(const SBasis & a, double b) {
+    if(a.isZero()) return Linear(-b, -b);
+    SBasis result(a);
+    result[0] -= b;
+    return result;
+}
+inline SBasis& operator+=(SBasis& a, double b) {
+    if(a.isZero())
+        a.push_back(Linear(b,b));
+    else
+        a[0] += b;
+    return a;
+}
+inline SBasis& operator-=(SBasis& a, double b) {
+    if(a.isZero())
+        a.push_back(Linear(-b,-b));
+    else
+        a[0] -= b;
+    return a;
+}
+
+SBasis shift(SBasis const &a, int sh);
+SBasis shift(Linear const &a, int sh);
+
+inline SBasis truncate(SBasis const &a, unsigned terms) {
+    SBasis c;
+    c.insert(c.begin(), a.begin(), a.begin() + std::min(terms, (unsigned)a.size()));
+    return c;
+}
+
+SBasis multiply(SBasis const &a, SBasis const &b);
+
+SBasis integral(SBasis const &c);
+SBasis derivative(SBasis const &a);
+
+SBasis sqrt(SBasis const &a, int k);
+
+// return a kth order approx to 1/a)
+SBasis reciprocal(Linear const &a, int k);
+SBasis divide(SBasis const &a, SBasis const &b, int k);
+
+inline SBasis operator*(SBasis const & a, SBasis const & b) {
+    return multiply(a, b);
+}
+
+inline SBasis& operator*=(SBasis& a, SBasis const & b) {
+    a = multiply(a, b);
+    return a;
+}
+
+//valuation: degree of the first non zero coefficient.
+inline unsigned 
+valuation(SBasis const &a, double tol=0){
+    unsigned val=0;
+    while( val<a.size() &&
+           fabs(a[val][0])<tol &&
+           fabs(a[val][1])<tol ) 
+        val++;
+    return val;
+}
+
+// a(b(t))
+SBasis compose(SBasis const &a, SBasis const &b);
+SBasis compose(SBasis const &a, SBasis const &b, unsigned k);
+SBasis inverse(SBasis a, int k);
+//compose_inverse(f,g)=compose(f,inverse(g)), but is numerically more stable in some good cases...
+//TODO: requires g(0)=0 & g(1)=1 atm. generalization should be obvious.
+SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order=2, double tol=1e-3);
+
+inline SBasis portion(const SBasis &t, double from, double to) { return compose(t, Linear(from, to)); }
+
+// compute f(g)
+inline SBasis
+SBasis::operator()(SBasis const & g) const {
+    return compose(*this, g);
+}
+inline std::ostream &operator<< (std::ostream &out_file, const Linear &bo) {
+    out_file << "{" << bo[0] << ", " << bo[1] << "}";
+    return out_file;
+}
+
+inline std::ostream &operator<< (std::ostream &out_file, const SBasis & p) {
+    for(unsigned i = 0; i < p.size(); i++) {
+        out_file << p[i] << "s^" << i << " + ";
+    }
+    return out_file;
+}
+
+SBasis sin(Linear bo, int k);
+SBasis cos(Linear bo, int k);
+
+std::vector<double> roots(SBasis const & s);
+std::vector<std::vector<double> > multi_roots(SBasis const &f,
+                                 std::vector<double> const &levels,
+                                 double htol=1e-7,
+                                 double vtol=1e-7,
+                                 double a=0,
+                                 double b=1);
+    
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+#endif
diff --git a/src/2geom/solve-bezier-one-d.cpp b/src/2geom/solve-bezier-one-d.cpp
new file mode 100644 (file)
index 0000000..558c10c
--- /dev/null
@@ -0,0 +1,268 @@
+#include "solver.h"
+#include "point.h"
+#include <algorithm>
+
+/*** Find the zeros of the bernstein function.  The code subdivides until it is happy with the
+ * linearity of the function.  This requires an O(degree^2) subdivision for each step, even when
+ * there is only one solution.
+ */
+
+namespace Geom{
+
+template<class t>
+static int SGN(t x) { return (x > 0 ? 1 : (x < 0 ? -1 : 0)); } 
+
+/*
+ *  Forward declarations
+ */
+static void 
+Bernstein(double const *V,
+          unsigned degree,
+          double t,
+          double *Left,
+          double *Right);
+
+static unsigned 
+control_poly_flat_enough(double const *V, unsigned degree,
+                        double left_t, double right_t);
+
+const unsigned MAXDEPTH = 64;  /*  Maximum depth for recursion */
+
+const double BEPSILON = ldexp(1.0,-MAXDEPTH-1); /*Flatness control value */
+
+/*
+ *  find_bernstein_roots : Given an equation in Bernstein-Bernstein form, find all 
+ *    of the roots in the open interval (0, 1).  Return the number of roots found.
+ */
+void
+find_bernstein_roots(double *w, /* The control points  */
+                     unsigned degree,  /* The degree of the polynomial */
+                     std::vector<double> &solutions, /* RETURN candidate t-values */
+                     unsigned depth,   /* The depth of the recursion */
+                     double left_t, double right_t)
+{  
+    unsigned   n_crossings = 0;        /*  Number of zero-crossings */
+    
+    double split = 0.5;
+    int old_sign = SGN(w[0]);
+    for (unsigned i = 1; i <= degree; i++) {
+        int sign = SGN(w[i]);
+        if (sign) {
+            if (sign != old_sign && old_sign) {
+                split = (i-0.5)/degree;
+               n_crossings++;
+            }
+            old_sign = sign;
+        }
+    }
+    
+    switch (n_crossings) {
+    case 0:    /* No solutions here    */
+        return;
+       
+    case 1:
+       /* Unique solution      */
+        /* Stop recursion when the tree is deep enough */
+        /* if deep enough, return 1 solution at midpoint  */
+        if (depth >= MAXDEPTH) {
+            solutions.push_back((left_t + right_t) / 2.0);
+            return;
+        }
+        
+        // I thought secant method would be faster here, but it'aint. -- njh
+
+        if (control_poly_flat_enough(w, degree, left_t, right_t)) {
+            const double Ax = right_t - left_t;
+            const double Ay = w[degree] - w[0];
+
+            solutions.push_back(left_t + Ax*w[0] / Ay);
+            return;
+        }
+        break;
+    }
+
+    /* Otherwise, solve recursively after subdividing control polygon  */
+    
+    
+    /* This bit is very clever (if I say so myself) - rather than arbitrarily subdividing at the t = 0.5 point, we subdivide at the most likely point of change of direction.  This buys us a factor of 10 speed up.  We also avoid lots of stack frames by avoiding tail recursion. */
+    double Left[degree+1],     /* New left and right  */
+           Right[degree+1];    /* control polygons  */
+    Bernstein(w, degree, split, Left, Right);
+    
+    double mid_t = left_t*(1-split) + right_t*split;
+    
+    find_bernstein_roots(Left,  degree, solutions, depth+1, left_t, mid_t);
+            
+    /* Solution is exactly on the subdivision point. */
+    if (Right[0] == 0)
+        solutions.push_back(mid_t);
+        
+    find_bernstein_roots(Right, degree, solutions, depth+1, mid_t, right_t);
+}
+
+/*
+ *  find_bernstein_roots : Given an equation in Bernstein-Bernstein form, find all 
+ *    of the roots in the interval [0, 1].  Return the number of roots found.
+ */
+void
+find_bernstein_roots_buggy(double *w, /* The control points  */
+                     unsigned degree,  /* The degree of the polynomial */
+                     std::vector<double> &solutions, /* RETURN candidate t-values */
+                     unsigned depth,   /* The depth of the recursion */
+                     double left_t, double right_t)
+{  
+    unsigned   n_crossings = 0;        /*  Number of zero-crossings */
+    
+    int old_sign = SGN(w[0]);
+    for (unsigned i = 1; i <= degree; i++) {
+        int sign = SGN(w[i]);
+        if (sign) {
+            if (sign != old_sign && old_sign)
+               n_crossings++;
+            old_sign = sign;
+        }
+    }
+    
+    switch (n_crossings) {
+    case 0:    /* No solutions here    */
+        return;
+       
+    case 1:
+       /* Unique solution      */
+        /* Stop recursion when the tree is deep enough */
+        /* if deep enough, return 1 solution at midpoint  */
+        if (depth >= MAXDEPTH) {
+            solutions.push_back((left_t + right_t) / 2.0);
+            return;
+        }
+        
+        // I thought secant method would be faster here, but it'aint. -- njh
+
+        if (control_poly_flat_enough(w, degree, left_t, right_t)) {
+            const double Ax = right_t - left_t;
+            const double Ay = w[degree] - w[0];
+
+            solutions.push_back(left_t + Ax*w[0] / Ay);
+            return;
+        }
+        break;
+    }
+
+    /* Otherwise, solve recursively after subdividing control polygon  */
+    
+    double split = 0.5;
+    
+    double Left[degree+1],     /* New left and right  */
+           Right[degree+1];    /* control polygons  */
+    Bernstein(w, degree, split, Left, Right);
+    
+    double mid_t = left_t*(1-split) + right_t*split;
+    
+    find_bernstein_roots_buggy(Left,  degree, solutions, depth+1, left_t, mid_t);
+            
+    /* Solution is exactly on the subdivision point. */
+    if (Right[0] == 0)
+        solutions.push_back(mid_t);
+        
+    find_bernstein_roots_buggy(Right, degree, solutions, depth+1, mid_t, right_t);
+}
+
+/*
+ *  control_poly_flat_enough :
+ *     Check if the control polygon of a Bernstein curve is flat enough
+ *     for recursive subdivision to bottom out.
+ *
+ */
+static unsigned 
+control_poly_flat_enough(double const *V, /* Control points    */
+                        unsigned degree,
+                        double left_t, double right_t) /* Degree of polynomial */
+{
+    /* Find the perpendicular distance from each interior control point to line connecting V[0] and
+     * V[degree] */
+
+    /* Derive the implicit equation for line connecting first */
+    /*  and last control points */
+    const double a = V[0] - V[degree];
+    const double b = right_t - left_t;
+    const double c = left_t * V[degree] - right_t * V[0] + a * left_t;
+
+    double max_distance_above = 0.0;
+    double max_distance_below = 0.0;
+    double ii = 0, dii = 1./degree;
+    for (unsigned i = 1; i < degree; i++) {
+        ii += dii;
+        /* Compute distance from each of the points to that line */
+        const double d = (a + V[i]) * ii*b  + c;
+        double dist = d*d;
+    // Find the largest distance
+        if (d < 0.0)
+            max_distance_below = std::min(max_distance_below, -dist);
+        else
+            max_distance_above = std::max(max_distance_above, dist);
+    }
+    
+    const double abSquared = (a * a) + (b * b);
+
+    const double intercept_1 = -(c + max_distance_above / abSquared);
+    const double intercept_2 = -(c + max_distance_below / abSquared);
+
+    /* Compute bounding interval*/
+    const double left_intercept = std::min(intercept_1, intercept_2);
+    const double right_intercept = std::max(intercept_1, intercept_2);
+
+    const double error = 0.5 * (right_intercept - left_intercept);
+    
+    if (error < BEPSILON * a)
+        return 1;
+    
+    return 0;
+}
+
+
+
+/*
+ *  Bernstein : 
+ *     Evaluate a Bernstein function at a particular parameter value
+ *      Fill in control points for resulting sub-curves.
+ * 
+ */
+static void 
+Bernstein(double const *V, /* Control pts      */
+          unsigned degree,     /* Degree of bernstein curve */
+          double t,    /* Parameter value */
+          double *Left,        /* RETURN left half ctl pts */
+          double *Right)       /* RETURN right half ctl pts */
+{
+    double Vtemp[degree+1][degree+1];
+
+    /* Copy control points     */
+    std::copy(V, V+degree+1, Vtemp[0]);
+
+    /* Triangle computation    */
+    const double omt = (1-t);
+    Left[0] = Vtemp[0][0];
+    Right[degree] = Vtemp[0][degree];
+    for (unsigned i = 1; i <= degree; i++) {
+        for (unsigned j = 0; j <= degree - i; j++) {
+            Vtemp[i][j] = omt*Vtemp[i-1][j] + t*Vtemp[i-1][j+1];
+        }
+        Left[i] = Vtemp[i][0];
+        Right[degree-i] = Vtemp[i][degree-i];
+    }
+}
+
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(substatement-open . 0))
+  indent-tabs-mode:nil
+  c-brace-offset:0
+  fill-column:99
+  End:
+  vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
+*/
+
diff --git a/src/2geom/solve-bezier-parametric.cpp b/src/2geom/solve-bezier-parametric.cpp
new file mode 100644 (file)
index 0000000..576ac34
--- /dev/null
@@ -0,0 +1,227 @@
+#include "solver.h"
+#include "point.h"
+#include <algorithm>
+
+namespace  Geom{
+
+/*** Find the zeros of the parametric function in 2d defined by two beziers X(t), Y(t).  The code subdivides until it happy with the linearity of the bezier.  This requires an n^2 subdivision for each step, even when there is only one solution.
+ * 
+ * Perhaps it would be better to subdivide particularly around nodes with changing sign, rather than simply cutting in half.
+ */
+
+#define SGN(a)      (((a)<0) ? -1 : 1)
+
+/*
+ *  Forward declarations
+ */
+static Geom::Point 
+Bezier(Geom::Point const *V,
+       unsigned degree,
+       double t,
+       Geom::Point *Left,
+       Geom::Point *Right);
+
+unsigned
+crossing_count(Geom::Point const *V, unsigned degree);
+static unsigned 
+control_poly_flat_enough(Geom::Point const *V, unsigned degree);
+static double
+compute_x_intercept(Geom::Point const *V, unsigned degree);
+
+const unsigned MAXDEPTH = 64;  /*  Maximum depth for recursion */
+
+const double BEPSILON = ldexp(1.0,-MAXDEPTH-1); /*Flatness control value */
+
+unsigned total_steps, total_subs;
+
+/*
+ *  find_bezier_roots : Given an equation in Bernstein-Bezier form, find all 
+ *    of the roots in the interval [0, 1].  Return the number of roots found.
+ */
+void
+find_parametric_bezier_roots(Geom::Point const *w, /* The control points  */
+                  unsigned degree,     /* The degree of the polynomial */
+                  std::vector<double> &solutions, /* RETURN candidate t-values */
+                  unsigned depth)      /* The depth of the recursion */
+{  
+    total_steps++;
+    const unsigned max_crossings = crossing_count(w, degree);
+    switch (max_crossings) {
+    case 0:    /* No solutions here    */
+        return;
+       
+    case 1:
+       /* Unique solution      */
+        /* Stop recursion when the tree is deep enough */
+        /* if deep enough, return 1 solution at midpoint  */
+        if (depth >= MAXDEPTH) {
+            solutions.push_back((w[0][Geom::X] + w[degree][Geom::X]) / 2.0);
+            return;
+        }
+        
+        // I thought secant method would be faster here, but it'aint. -- njh
+
+        if (control_poly_flat_enough(w, degree)) {
+            solutions.push_back(compute_x_intercept(w, degree));
+            return;
+        }
+        break;
+    }
+
+    /* Otherwise, solve recursively after subdividing control polygon  */
+    Geom::Point Left[degree+1],        /* New left and right  */
+        Right[degree+1];       /* control polygons  */
+    Bezier(w, degree, 0.5, Left, Right);
+    total_subs ++;
+    find_parametric_bezier_roots(Left,  degree, solutions, depth+1);
+    find_parametric_bezier_roots(Right, degree, solutions, depth+1);
+}
+
+
+/*
+ * crossing_count:
+ *  Count the number of times a Bezier control polygon 
+ *  crosses the 0-axis. This number is >= the number of roots.
+ *
+ */
+unsigned
+crossing_count(Geom::Point const *V,   /*  Control pts of Bezier curve */
+              unsigned degree) /*  Degree of Bezier curve      */
+{
+    unsigned   n_crossings = 0;        /*  Number of zero-crossings */
+    
+    int old_sign = SGN(V[0][Geom::Y]);
+    for (unsigned i = 1; i <= degree; i++) {
+        int sign = SGN(V[i][Geom::Y]);
+        if (sign != old_sign)
+            n_crossings++;
+        old_sign = sign;
+    }
+    return n_crossings;
+}
+
+
+
+/*
+ *  control_poly_flat_enough :
+ *     Check if the control polygon of a Bezier curve is flat enough
+ *     for recursive subdivision to bottom out.
+ *
+ */
+static unsigned 
+control_poly_flat_enough(Geom::Point const *V, /* Control points       */
+                        unsigned degree)       /* Degree of polynomial */
+{
+    /* Find the perpendicular distance from each interior control point to line connecting V[0] and
+     * V[degree] */
+
+    /* Derive the implicit equation for line connecting first */
+    /*  and last control points */
+    const double a = V[0][Geom::Y] - V[degree][Geom::Y];
+    const double b = V[degree][Geom::X] - V[0][Geom::X];
+    const double c = V[0][Geom::X] * V[degree][Geom::Y] - V[degree][Geom::X] * V[0][Geom::Y];
+
+    const double abSquared = (a * a) + (b * b);
+
+    double distance[degree]; /* Distances from pts to line */
+    for (unsigned i = 1; i < degree; i++) {
+        /* Compute distance from each of the points to that line */
+        double & dist(distance[i-1]);
+        const double d = a * V[i][Geom::X] + b * V[i][Geom::Y] + c;
+        dist = d*d / abSquared;
+        if (d < 0.0)
+            dist = -dist;
+    }
+
+
+    // Find the largest distance
+    double max_distance_above = 0.0;
+    double max_distance_below = 0.0;
+    for (unsigned i = 0; i < degree-1; i++) {
+        const double d = distance[i];
+        if (d < 0.0)
+            max_distance_below = std::min(max_distance_below, d);
+        if (d > 0.0)
+            max_distance_above = std::max(max_distance_above, d);
+    }
+
+    const double intercept_1 = (c + max_distance_above) / -a;
+    const double intercept_2 = (c + max_distance_below) / -a;
+
+    /* Compute bounding interval*/
+    const double left_intercept = std::min(intercept_1, intercept_2);
+    const double right_intercept = std::max(intercept_1, intercept_2);
+
+    const double error = 0.5 * (right_intercept - left_intercept);
+    
+    if (error < BEPSILON)
+        return 1;
+    
+    return 0;
+}
+
+
+
+/*
+ *  compute_x_intercept :
+ *     Compute intersection of chord from first control point to last
+ *     with 0-axis.
+ * 
+ */
+static double
+compute_x_intercept(Geom::Point const *V, /*  Control points   */
+                   unsigned degree) /*  Degree of curve        */
+{
+    const Geom::Point A = V[degree] - V[0];
+
+    return (A[Geom::X]*V[0][Geom::Y] - A[Geom::Y]*V[0][Geom::X]) / -A[Geom::Y];
+}
+
+
+/*
+ *  Bezier : 
+ *     Evaluate a Bezier curve at a particular parameter value
+ *      Fill in control points for resulting sub-curves.
+ * 
+ */
+static Geom::Point 
+Bezier(Geom::Point const *V, /* Control pts    */
+       unsigned degree,        /* Degree of bezier curve */
+       double t,       /* Parameter value */
+       Geom::Point *Left,      /* RETURN left half ctl pts */
+       Geom::Point *Right)     /* RETURN right half ctl pts */
+{
+    Geom::Point Vtemp[degree+1][degree+1];
+
+    /* Copy control points     */
+    std::copy(V, V+degree+1, Vtemp[0]);
+
+    /* Triangle computation    */
+    for (unsigned i = 1; i <= degree; i++) {   
+        for (unsigned j = 0; j <= degree - i; j++) {
+            Vtemp[i][j] = lerp(t, Vtemp[i-1][j], Vtemp[i-1][j+1]);
+        }
+    }
+    
+    for (unsigned j = 0; j <= degree; j++)
+        Left[j]  = Vtemp[j][0];
+    for (unsigned j = 0; j <= degree; j++)
+        Right[j] = Vtemp[degree-j][j];
+
+    return (Vtemp[degree][0]);
+}
+
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(substatement-open . 0))
+  indent-tabs-mode:nil
+  c-brace-offset:0
+  fill-column:99
+  End:
+  vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
+*/
+
diff --git a/src/2geom/solver.h b/src/2geom/solver.h
new file mode 100644 (file)
index 0000000..c2e2902
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _SOLVE_SBASIS_H
+#define _SOLVE_SBASIS_H
+#include "point.h"
+#include "sbasis.h"
+
+namespace Geom{
+
+unsigned
+crossing_count(Geom::Point const *V,   /*  Control pts of Bezier curve */
+              unsigned degree);        /*  Degree of Bezier curve */
+void
+find_parametric_bezier_roots(
+    Geom::Point const *w, /* The control points  */
+    unsigned degree,   /* The degree of the polynomial */
+    std::vector<double> & solutions,   /* RETURN candidate t-values */
+    unsigned depth);   /* The depth of the recursion */
+
+unsigned
+crossing_count(double const *V,        /*  Control pts of Bezier curve */
+              unsigned degree, /*  Degree of Bezier curve */
+              double left_t, double right_t);
+void
+find_bernstein_roots(
+    double *w, /* The control points  */
+    unsigned degree,   /* The degree of the polynomial */
+    std::vector<double> & solutions,   /* RETURN candidate t-values */
+    unsigned depth,    /* The depth of the recursion */
+    double left_t=0, double right_t=1);
+void
+find_bernstein_roots_buggy(
+    double *w, /* The control points  */
+    unsigned degree,   /* The degree of the polynomial */
+    std::vector<double> & solutions,   /* RETURN candidate t-values */
+    unsigned depth,    /* The depth of the recursion */
+    double left_t=0, double right_t=1);
+
+};
+#endif
diff --git a/src/2geom/sturm.h b/src/2geom/sturm.h
new file mode 100644 (file)
index 0000000..4a9fcc6
--- /dev/null
@@ -0,0 +1,62 @@
+#include "poly.h"
+#include "utils.h"
+
+class sturm : public std::vector<Poly>{
+public:
+    sturm(Poly const &X) {
+        push_back(X);
+        push_back(derivative(X));
+        Poly Xi = back();
+        Poly Xim1 = X;
+        std::cout << "sturm:\n" << Xim1 << std::endl;
+        std::cout << Xi << std::endl;
+        while(Xi.size() > 1) {
+            Poly r;
+            divide(Xim1, Xi, r);
+            std::cout << r << std::endl;
+            assert(r.size() < Xi.size());
+            Xim1 = Xi;
+            Xi = -r;
+            assert(Xim1.size() > Xi.size());
+            push_back(Xi);
+        }
+    }
+    
+    unsigned count_signs(double t) {
+        unsigned n_signs = 0;/*  Number of sign-changes */
+        const double big = 1e20; // a number such that practical polys would overflow on evaluation
+        if(t >= big) {
+            int old_sign = sgn((*this)[0].back());
+            for (unsigned i = 1; i < size(); i++) {
+                int sign = sgn((*this)[i].back());
+                if (sign != old_sign)
+                    n_signs++;
+                old_sign = sign;
+            }
+        } else {
+            int old_sign = sgn((*this)[0].eval(t));
+            for (unsigned i = 1; i < size(); i++) {
+                int sign = sgn((*this)[i].eval(t));
+                if (sign != old_sign)
+                    n_signs++;
+                old_sign = sign;
+            }
+        }
+        return n_signs;
+    }
+    
+    unsigned n_roots_between(double l, double r) {
+        return count_signs(l) - count_signs(r);
+    }
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/svg-path-parser.cpp b/src/2geom/svg-path-parser.cpp
new file mode 100644 (file)
index 0000000..0063fcf
--- /dev/null
@@ -0,0 +1,1603 @@
+#line 1 "/home/michael/2geom/src/svg-path-parser.rl"
+/*
+ * parse SVG path specifications
+ *
+ * Copyright 2007 MenTaLguY <mental@rydia.net>
+ * Copyright 2007 Aaron Spike <aaron@ekips.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+
+#include <cmath>
+#include <vector>
+#include <glib.h>
+
+#include "point.h"
+
+#include "svg-path-parser.h"
+
+namespace Geom {
+
+namespace {
+
+class Parser {
+public:
+    Parser(SVGPathSink &sink) : _sink(sink) {}
+
+    void parse(char const *str) throw(SVGPathParseError);
+
+private:
+    bool _absolute;
+    Point _current;
+    Point _initial;
+    Point _cubic_tangent;
+    Point _quad_tangent;
+    std::vector<double> _params;
+    SVGPathSink &_sink;
+
+    void _reset() {
+        _absolute = false;
+        _current = _initial = Point(0, 0);
+        _quad_tangent = _cubic_tangent = Point(0, 0);
+        _params.clear();
+    }
+
+    void _push(double value) {
+        _params.push_back(value);
+    }
+
+    double _pop() {
+        double value = _params.back();
+        _params.pop_back();
+        return value;
+    }
+
+    bool _pop_flag() {
+        return _pop() != 0.0;
+    }
+
+    double _pop_coord(Geom::Dim2 axis) {
+        if (_absolute) {
+            return _pop();
+        } else {
+            return _pop() + _current[axis];
+        }
+    }
+
+    Point _pop_point() {
+        double y = _pop_coord(Geom::Y);
+        double x = _pop_coord(Geom::X);
+        return Point(x, y);
+    }
+
+    void _moveTo(Point p) {
+        _quad_tangent = _cubic_tangent = _current = _initial = p;
+        _sink.moveTo(p);
+    }
+
+    void _lineTo(Point p) {
+        _quad_tangent = _cubic_tangent = _current = p;
+        _sink.lineTo(p);
+    }
+
+    void _curveTo(Point c0, Point c1, Point p) {
+        _quad_tangent = _current = p;
+        _cubic_tangent = p + ( p - c1 );
+        _sink.curveTo(c0, c1, p);
+    }
+
+    void _quadTo(Point c, Point p) {
+        _cubic_tangent = _current = p;
+        _quad_tangent = p + ( p - c );
+        _sink.quadTo(c, p);
+    }
+
+    void _arcTo(double rx, double ry, double angle,
+                bool large_arc, bool sweep, Point p)
+    {
+        _quad_tangent = _cubic_tangent = _current = p;
+        _sink.arcTo(rx, ry, angle, large_arc, sweep, p);
+    }
+
+    void _closePath() {
+        _quad_tangent = _cubic_tangent = _current = _initial;
+        _sink.closePath();
+    }
+};
+
+
+#line 133 "/home/michael/2geom/src/svg-path-parser.cpp"
+static const char _svg_path_actions[] = {
+       0, 1, 0, 1, 1, 1, 2, 1, 
+       3, 1, 4, 1, 5, 1, 15, 1, 
+       16, 2, 1, 0, 2, 1, 2, 2, 
+       1, 3, 2, 1, 6, 2, 1, 7, 
+       2, 1, 8, 2, 1, 9, 2, 1, 
+       10, 2, 1, 11, 2, 1, 12, 2, 
+       1, 13, 2, 1, 14, 2, 2, 1, 
+       2, 3, 1, 2, 4, 0, 2, 5, 
+       0, 2, 15, 16, 3, 1, 6, 0, 
+       3, 1, 6, 16, 3, 1, 7, 0, 
+       3, 1, 7, 16, 3, 1, 8, 0, 
+       3, 1, 8, 16, 3, 1, 9, 0, 
+       3, 1, 9, 16, 3, 1, 10, 0, 
+       3, 1, 10, 16, 3, 1, 11, 0, 
+       3, 1, 11, 16, 3, 1, 12, 0, 
+       3, 1, 12, 16, 3, 1, 13, 0, 
+       3, 1, 13, 16, 3, 1, 14, 0, 
+       3, 1, 14, 16
+};
+
+static const short _svg_path_key_offsets[] = {
+       0, 7, 7, 16, 25, 28, 30, 42, 
+       52, 55, 57, 90, 121, 124, 126, 138, 
+       148, 151, 153, 186, 195, 207, 216, 249, 
+       256, 263, 265, 275, 283, 290, 292, 304, 
+       314, 317, 319, 328, 335, 341, 346, 353, 
+       359, 364, 374, 377, 379, 391, 401, 404, 
+       406, 437, 466, 476, 488, 498, 507, 509, 
+       521, 533, 544, 554, 561, 567, 572, 584, 
+       595, 607, 617, 620, 622, 655, 664, 695, 
+       704, 713, 716, 718, 730, 740, 743, 745, 
+       757, 767, 770, 772, 784, 794, 797, 799, 
+       811, 821, 824, 826, 838, 848, 851, 853, 
+       886, 917, 929, 938, 950, 959, 971, 980, 
+       992, 1001, 1013, 1022, 1055, 1059, 1061, 1092, 
+       1101, 1110, 1113, 1115, 1148, 1179, 1182, 1184, 
+       1217, 1226, 1259, 1263, 1265, 1296, 1305, 1314, 
+       1323, 1326, 1328, 1340, 1350, 1353, 1355, 1367, 
+       1377, 1380, 1382, 1394, 1404, 1407, 1409, 1442, 
+       1473, 1485, 1494, 1506, 1515, 1527, 1536, 1569, 
+       1573, 1575, 1606, 1615, 1624, 1627, 1629, 1641, 
+       1651, 1654, 1656, 1668, 1678, 1681, 1683, 1695, 
+       1705, 1708, 1710, 1743, 1774, 1786, 1795, 1807, 
+       1816, 1828, 1837, 1870, 1874, 1876, 1907, 1916, 
+       1925, 1928, 1930, 1942, 1952, 1955, 1957, 1990, 
+       2021, 2033, 2042, 2075, 2079, 2081, 2112, 2121, 
+       2130, 2133, 2135, 2168, 2199, 2202, 2204, 2237, 
+       2246, 2279, 2283, 2285, 2316, 2341, 2366, 2373, 
+       2382, 2391, 2424, 2428, 2430, 2461, 2494, 2503, 
+       2512, 2524, 2533, 2566, 2570, 2572, 2603, 2612, 
+       2621, 2630, 2639, 2672, 2676, 2678, 2709, 2742, 
+       2746, 2748, 2758, 2791, 2795, 2797, 2807, 2811, 
+       2813, 2823, 2827, 2829, 2839, 2843, 2845, 2855, 
+       2859, 2861, 2871, 2875, 2877, 2887, 2891, 2893, 
+       2903, 2936, 2940, 2942, 2952, 2956, 2958, 2968, 
+       2972, 2974, 2984, 2988, 2990, 3000, 3004, 3006, 
+       3016, 3020, 3022, 3051, 3055, 3057, 3088, 3121, 
+       3130, 3142, 3146, 3148, 3158, 3170, 3175, 3185, 
+       3189, 3191, 3198, 3208, 3212, 3214, 3224, 3236, 
+       3248, 3260, 3264, 3266, 3276, 3288, 3292, 3294, 
+       3304, 3313, 3317, 3319, 3327, 3336, 3341, 3346, 
+       3358, 3362, 3364, 3395, 3399, 3401, 3411
+};
+
+static const char _svg_path_trans_keys[] = {
+       0, 13, 32, 77, 109, 9, 10, 13, 
+       32, 43, 45, 46, 9, 10, 48, 57, 
+       13, 32, 43, 45, 46, 9, 10, 48, 
+       57, 46, 48, 57, 48, 57, 13, 32, 
+       44, 46, 69, 101, 9, 10, 43, 45, 
+       48, 57, 13, 32, 44, 46, 9, 10, 
+       43, 45, 48, 57, 46, 48, 57, 48, 
+       57, 0, 13, 32, 44, 46, 65, 67, 
+       69, 72, 76, 77, 81, 83, 84, 86, 
+       90, 97, 99, 101, 104, 108, 109, 113, 
+       115, 116, 118, 122, 9, 10, 43, 45, 
+       48, 57, 0, 13, 32, 44, 46, 65, 
+       67, 72, 76, 77, 81, 83, 84, 86, 
+       90, 97, 99, 104, 108, 109, 113, 115, 
+       116, 118, 122, 9, 10, 43, 45, 48, 
+       57, 46, 48, 57, 48, 57, 13, 32, 
+       44, 46, 69, 101, 9, 10, 43, 45, 
+       48, 57, 13, 32, 44, 46, 9, 10, 
+       43, 45, 48, 57, 46, 48, 57, 48, 
+       57, 0, 13, 32, 44, 46, 65, 67, 
+       69, 72, 76, 77, 81, 83, 84, 86, 
+       90, 97, 99, 101, 104, 108, 109, 113, 
+       115, 116, 118, 122, 9, 10, 43, 45, 
+       48, 57, 13, 32, 43, 45, 46, 9, 
+       10, 48, 57, 13, 32, 44, 46, 69, 
+       101, 9, 10, 43, 45, 48, 57, 13, 
+       32, 43, 45, 46, 9, 10, 48, 57, 
+       0, 13, 32, 44, 46, 65, 67, 69, 
+       72, 76, 77, 81, 83, 84, 86, 90, 
+       97, 99, 101, 104, 108, 109, 113, 115, 
+       116, 118, 122, 9, 10, 43, 45, 48, 
+       57, 13, 32, 46, 9, 10, 48, 57, 
+       13, 32, 46, 9, 10, 48, 57, 48, 
+       57, 13, 32, 44, 46, 69, 101, 9, 
+       10, 48, 57, 13, 32, 44, 46, 9, 
+       10, 48, 57, 13, 32, 46, 9, 10, 
+       48, 57, 48, 57, 13, 32, 44, 46, 
+       69, 101, 9, 10, 43, 45, 48, 57, 
+       13, 32, 44, 46, 9, 10, 43, 45, 
+       48, 57, 46, 48, 57, 48, 57, 13, 
+       32, 44, 69, 101, 9, 10, 48, 57, 
+       13, 32, 44, 48, 49, 9, 10, 13, 
+       32, 48, 49, 9, 10, 13, 32, 44, 
+       9, 10, 13, 32, 44, 48, 49, 9, 
+       10, 13, 32, 48, 49, 9, 10, 13, 
+       32, 44, 9, 10, 13, 32, 44, 46, 
+       9, 10, 43, 45, 48, 57, 46, 48, 
+       57, 48, 57, 13, 32, 44, 46, 69, 
+       101, 9, 10, 43, 45, 48, 57, 13, 
+       32, 44, 46, 9, 10, 43, 45, 48, 
+       57, 46, 48, 57, 48, 57, 0, 13, 
+       32, 44, 46, 65, 67, 69, 72, 76, 
+       77, 81, 83, 84, 86, 90, 97, 99, 
+       101, 104, 108, 109, 113, 115, 116, 118, 
+       122, 9, 10, 48, 57, 0, 13, 32, 
+       44, 46, 65, 67, 72, 76, 77, 81, 
+       83, 84, 86, 90, 97, 99, 104, 108, 
+       109, 113, 115, 116, 118, 122, 9, 10, 
+       48, 57, 13, 32, 44, 46, 69, 101, 
+       9, 10, 48, 57, 13, 32, 44, 46, 
+       69, 101, 9, 10, 43, 45, 48, 57, 
+       13, 32, 44, 46, 9, 10, 43, 45, 
+       48, 57, 13, 32, 43, 45, 46, 9, 
+       10, 48, 57, 48, 57, 13, 32, 44, 
+       46, 69, 101, 9, 10, 43, 45, 48, 
+       57, 13, 32, 44, 46, 48, 49, 9, 
+       10, 43, 45, 50, 57, 13, 32, 43, 
+       45, 46, 48, 49, 9, 10, 50, 57, 
+       13, 32, 44, 46, 69, 101, 9, 10, 
+       48, 57, 13, 32, 44, 48, 49, 9, 
+       10, 13, 32, 48, 49, 9, 10, 13, 
+       32, 44, 9, 10, 13, 32, 44, 46, 
+       48, 49, 9, 10, 43, 45, 50, 57, 
+       13, 32, 43, 45, 46, 48, 49, 9, 
+       10, 50, 57, 13, 32, 44, 46, 69, 
+       101, 9, 10, 43, 45, 48, 57, 13, 
+       32, 44, 46, 9, 10, 43, 45, 48, 
+       57, 46, 48, 57, 48, 57, 0, 13, 
+       32, 44, 46, 65, 67, 69, 72, 76, 
+       77, 81, 83, 84, 86, 90, 97, 99, 
+       101, 104, 108, 109, 113, 115, 116, 118, 
+       122, 9, 10, 43, 45, 48, 57, 13, 
+       32, 43, 45, 46, 9, 10, 48, 57, 
+       0, 13, 32, 44, 46, 65, 67, 69, 
+       72, 76, 77, 81, 83, 84, 86, 90, 
+       97, 99, 101, 104, 108, 109, 113, 115, 
+       116, 118, 122, 9, 10, 48, 57, 13, 
+       32, 43, 45, 46, 9, 10, 48, 57, 
+       13, 32, 43, 45, 46, 9, 10, 48, 
+       57, 46, 48, 57, 48, 57, 13, 32, 
+       44, 46, 69, 101, 9, 10, 43, 45, 
+       48, 57, 13, 32, 44, 46, 9, 10, 
+       43, 45, 48, 57, 46, 48, 57, 48, 
+       57, 13, 32, 44, 46, 69, 101, 9, 
+       10, 43, 45, 48, 57, 13, 32, 44, 
+       46, 9, 10, 43, 45, 48, 57, 46, 
+       48, 57, 48, 57, 13, 32, 44, 46, 
+       69, 101, 9, 10, 43, 45, 48, 57, 
+       13, 32, 44, 46, 9, 10, 43, 45, 
+       48, 57, 46, 48, 57, 48, 57, 13, 
+       32, 44, 46, 69, 101, 9, 10, 43, 
+       45, 48, 57, 13, 32, 44, 46, 9, 
+       10, 43, 45, 48, 57, 46, 48, 57, 
+       48, 57, 13, 32, 44, 46, 69, 101, 
+       9, 10, 43, 45, 48, 57, 13, 32, 
+       44, 46, 9, 10, 43, 45, 48, 57, 
+       46, 48, 57, 48, 57, 0, 13, 32, 
+       44, 46, 65, 67, 69, 72, 76, 77, 
+       81, 83, 84, 86, 90, 97, 99, 101, 
+       104, 108, 109, 113, 115, 116, 118, 122, 
+       9, 10, 43, 45, 48, 57, 0, 13, 
+       32, 44, 46, 65, 67, 72, 76, 77, 
+       81, 83, 84, 86, 90, 97, 99, 104, 
+       108, 109, 113, 115, 116, 118, 122, 9, 
+       10, 43, 45, 48, 57, 13, 32, 44, 
+       46, 69, 101, 9, 10, 43, 45, 48, 
+       57, 13, 32, 43, 45, 46, 9, 10, 
+       48, 57, 13, 32, 44, 46, 69, 101, 
+       9, 10, 43, 45, 48, 57, 13, 32, 
+       43, 45, 46, 9, 10, 48, 57, 13, 
+       32, 44, 46, 69, 101, 9, 10, 43, 
+       45, 48, 57, 13, 32, 43, 45, 46, 
+       9, 10, 48, 57, 13, 32, 44, 46, 
+       69, 101, 9, 10, 43, 45, 48, 57, 
+       13, 32, 43, 45, 46, 9, 10, 48, 
+       57, 13, 32, 44, 46, 69, 101, 9, 
+       10, 43, 45, 48, 57, 13, 32, 43, 
+       45, 46, 9, 10, 48, 57, 0, 13, 
+       32, 44, 46, 65, 67, 69, 72, 76, 
+       77, 81, 83, 84, 86, 90, 97, 99, 
+       101, 104, 108, 109, 113, 115, 116, 118, 
+       122, 9, 10, 43, 45, 48, 57, 43, 
+       45, 48, 57, 48, 57, 0, 13, 32, 
+       44, 46, 65, 67, 72, 76, 77, 81, 
+       83, 84, 86, 90, 97, 99, 104, 108, 
+       109, 113, 115, 116, 118, 122, 9, 10, 
+       43, 45, 48, 57, 13, 32, 43, 45, 
+       46, 9, 10, 48, 57, 13, 32, 43, 
+       45, 46, 9, 10, 48, 57, 46, 48, 
+       57, 48, 57, 0, 13, 32, 44, 46, 
+       65, 67, 69, 72, 76, 77, 81, 83, 
+       84, 86, 90, 97, 99, 101, 104, 108, 
+       109, 113, 115, 116, 118, 122, 9, 10, 
+       43, 45, 48, 57, 0, 13, 32, 44, 
+       46, 65, 67, 72, 76, 77, 81, 83, 
+       84, 86, 90, 97, 99, 104, 108, 109, 
+       113, 115, 116, 118, 122, 9, 10, 43, 
+       45, 48, 57, 46, 48, 57, 48, 57, 
+       0, 13, 32, 44, 46, 65, 67, 69, 
+       72, 76, 77, 81, 83, 84, 86, 90, 
+       97, 99, 101, 104, 108, 109, 113, 115, 
+       116, 118, 122, 9, 10, 43, 45, 48, 
+       57, 13, 32, 43, 45, 46, 9, 10, 
+       48, 57, 0, 13, 32, 44, 46, 65, 
+       67, 69, 72, 76, 77, 81, 83, 84, 
+       86, 90, 97, 99, 101, 104, 108, 109, 
+       113, 115, 116, 118, 122, 9, 10, 43, 
+       45, 48, 57, 43, 45, 48, 57, 48, 
+       57, 0, 13, 32, 44, 46, 65, 67, 
+       72, 76, 77, 81, 83, 84, 86, 90, 
+       97, 99, 104, 108, 109, 113, 115, 116, 
+       118, 122, 9, 10, 43, 45, 48, 57, 
+       13, 32, 43, 45, 46, 9, 10, 48, 
+       57, 13, 32, 43, 45, 46, 9, 10, 
+       48, 57, 13, 32, 43, 45, 46, 9, 
+       10, 48, 57, 46, 48, 57, 48, 57, 
+       13, 32, 44, 46, 69, 101, 9, 10, 
+       43, 45, 48, 57, 13, 32, 44, 46, 
+       9, 10, 43, 45, 48, 57, 46, 48, 
+       57, 48, 57, 13, 32, 44, 46, 69, 
+       101, 9, 10, 43, 45, 48, 57, 13, 
+       32, 44, 46, 9, 10, 43, 45, 48, 
+       57, 46, 48, 57, 48, 57, 13, 32, 
+       44, 46, 69, 101, 9, 10, 43, 45, 
+       48, 57, 13, 32, 44, 46, 9, 10, 
+       43, 45, 48, 57, 46, 48, 57, 48, 
+       57, 0, 13, 32, 44, 46, 65, 67, 
+       69, 72, 76, 77, 81, 83, 84, 86, 
+       90, 97, 99, 101, 104, 108, 109, 113, 
+       115, 116, 118, 122, 9, 10, 43, 45, 
+       48, 57, 0, 13, 32, 44, 46, 65, 
+       67, 72, 76, 77, 81, 83, 84, 86, 
+       90, 97, 99, 104, 108, 109, 113, 115, 
+       116, 118, 122, 9, 10, 43, 45, 48, 
+       57, 13, 32, 44, 46, 69, 101, 9, 
+       10, 43, 45, 48, 57, 13, 32, 43, 
+       45, 46, 9, 10, 48, 57, 13, 32, 
+       44, 46, 69, 101, 9, 10, 43, 45, 
+       48, 57, 13, 32, 43, 45, 46, 9, 
+       10, 48, 57, 13, 32, 44, 46, 69, 
+       101, 9, 10, 43, 45, 48, 57, 13, 
+       32, 43, 45, 46, 9, 10, 48, 57, 
+       0, 13, 32, 44, 46, 65, 67, 69, 
+       72, 76, 77, 81, 83, 84, 86, 90, 
+       97, 99, 101, 104, 108, 109, 113, 115, 
+       116, 118, 122, 9, 10, 43, 45, 48, 
+       57, 43, 45, 48, 57, 48, 57, 0, 
+       13, 32, 44, 46, 65, 67, 72, 76, 
+       77, 81, 83, 84, 86, 90, 97, 99, 
+       104, 108, 109, 113, 115, 116, 118, 122, 
+       9, 10, 43, 45, 48, 57, 13, 32, 
+       43, 45, 46, 9, 10, 48, 57, 13, 
+       32, 43, 45, 46, 9, 10, 48, 57, 
+       46, 48, 57, 48, 57, 13, 32, 44, 
+       46, 69, 101, 9, 10, 43, 45, 48, 
+       57, 13, 32, 44, 46, 9, 10, 43, 
+       45, 48, 57, 46, 48, 57, 48, 57, 
+       13, 32, 44, 46, 69, 101, 9, 10, 
+       43, 45, 48, 57, 13, 32, 44, 46, 
+       9, 10, 43, 45, 48, 57, 46, 48, 
+       57, 48, 57, 13, 32, 44, 46, 69, 
+       101, 9, 10, 43, 45, 48, 57, 13, 
+       32, 44, 46, 9, 10, 43, 45, 48, 
+       57, 46, 48, 57, 48, 57, 0, 13, 
+       32, 44, 46, 65, 67, 69, 72, 76, 
+       77, 81, 83, 84, 86, 90, 97, 99, 
+       101, 104, 108, 109, 113, 115, 116, 118, 
+       122, 9, 10, 43, 45, 48, 57, 0, 
+       13, 32, 44, 46, 65, 67, 72, 76, 
+       77, 81, 83, 84, 86, 90, 97, 99, 
+       104, 108, 109, 113, 115, 116, 118, 122, 
+       9, 10, 43, 45, 48, 57, 13, 32, 
+       44, 46, 69, 101, 9, 10, 43, 45, 
+       48, 57, 13, 32, 43, 45, 46, 9, 
+       10, 48, 57, 13, 32, 44, 46, 69, 
+       101, 9, 10, 43, 45, 48, 57, 13, 
+       32, 43, 45, 46, 9, 10, 48, 57, 
+       13, 32, 44, 46, 69, 101, 9, 10, 
+       43, 45, 48, 57, 13, 32, 43, 45, 
+       46, 9, 10, 48, 57, 0, 13, 32, 
+       44, 46, 65, 67, 69, 72, 76, 77, 
+       81, 83, 84, 86, 90, 97, 99, 101, 
+       104, 108, 109, 113, 115, 116, 118, 122, 
+       9, 10, 43, 45, 48, 57, 43, 45, 
+       48, 57, 48, 57, 0, 13, 32, 44, 
+       46, 65, 67, 72, 76, 77, 81, 83, 
+       84, 86, 90, 97, 99, 104, 108, 109, 
+       113, 115, 116, 118, 122, 9, 10, 43, 
+       45, 48, 57, 13, 32, 43, 45, 46, 
+       9, 10, 48, 57, 13, 32, 43, 45, 
+       46, 9, 10, 48, 57, 46, 48, 57, 
+       48, 57, 13, 32, 44, 46, 69, 101, 
+       9, 10, 43, 45, 48, 57, 13, 32, 
+       44, 46, 9, 10, 43, 45, 48, 57, 
+       46, 48, 57, 48, 57, 0, 13, 32, 
+       44, 46, 65, 67, 69, 72, 76, 77, 
+       81, 83, 84, 86, 90, 97, 99, 101, 
+       104, 108, 109, 113, 115, 116, 118, 122, 
+       9, 10, 43, 45, 48, 57, 0, 13, 
+       32, 44, 46, 65, 67, 72, 76, 77, 
+       81, 83, 84, 86, 90, 97, 99, 104, 
+       108, 109, 113, 115, 116, 118, 122, 9, 
+       10, 43, 45, 48, 57, 13, 32, 44, 
+       46, 69, 101, 9, 10, 43, 45, 48, 
+       57, 13, 32, 43, 45, 46, 9, 10, 
+       48, 57, 0, 13, 32, 44, 46, 65, 
+       67, 69, 72, 76, 77, 81, 83, 84, 
+       86, 90, 97, 99, 101, 104, 108, 109, 
+       113, 115, 116, 118, 122, 9, 10, 43, 
+       45, 48, 57, 43, 45, 48, 57, 48, 
+       57, 0, 13, 32, 44, 46, 65, 67, 
+       72, 76, 77, 81, 83, 84, 86, 90, 
+       97, 99, 104, 108, 109, 113, 115, 116, 
+       118, 122, 9, 10, 43, 45, 48, 57, 
+       13, 32, 43, 45, 46, 9, 10, 48, 
+       57, 13, 32, 43, 45, 46, 9, 10, 
+       48, 57, 46, 48, 57, 48, 57, 0, 
+       13, 32, 44, 46, 65, 67, 69, 72, 
+       76, 77, 81, 83, 84, 86, 90, 97, 
+       99, 101, 104, 108, 109, 113, 115, 116, 
+       118, 122, 9, 10, 43, 45, 48, 57, 
+       0, 13, 32, 44, 46, 65, 67, 72, 
+       76, 77, 81, 83, 84, 86, 90, 97, 
+       99, 104, 108, 109, 113, 115, 116, 118, 
+       122, 9, 10, 43, 45, 48, 57, 46, 
+       48, 57, 48, 57, 0, 13, 32, 44, 
+       46, 65, 67, 69, 72, 76, 77, 81, 
+       83, 84, 86, 90, 97, 99, 101, 104, 
+       108, 109, 113, 115, 116, 118, 122, 9, 
+       10, 43, 45, 48, 57, 13, 32, 43, 
+       45, 46, 9, 10, 48, 57, 0, 13, 
+       32, 44, 46, 65, 67, 69, 72, 76, 
+       77, 81, 83, 84, 86, 90, 97, 99, 
+       101, 104, 108, 109, 113, 115, 116, 118, 
+       122, 9, 10, 43, 45, 48, 57, 43, 
+       45, 48, 57, 48, 57, 0, 13, 32, 
+       44, 46, 65, 67, 72, 76, 77, 81, 
+       83, 84, 86, 90, 97, 99, 104, 108, 
+       109, 113, 115, 116, 118, 122, 9, 10, 
+       43, 45, 48, 57, 0, 13, 32, 65, 
+       67, 72, 76, 77, 81, 83, 84, 86, 
+       90, 97, 99, 104, 108, 109, 113, 115, 
+       116, 118, 122, 9, 10, 0, 13, 32, 
+       65, 67, 72, 76, 77, 81, 83, 84, 
+       86, 90, 97, 99, 104, 108, 109, 113, 
+       115, 116, 118, 122, 9, 10, 13, 32, 
+       46, 9, 10, 48, 57, 13, 32, 43, 
+       45, 46, 9, 10, 48, 57, 13, 32, 
+       43, 45, 46, 9, 10, 48, 57, 0, 
+       13, 32, 44, 46, 65, 67, 69, 72, 
+       76, 77, 81, 83, 84, 86, 90, 97, 
+       99, 101, 104, 108, 109, 113, 115, 116, 
+       118, 122, 9, 10, 43, 45, 48, 57, 
+       43, 45, 48, 57, 48, 57, 0, 13, 
+       32, 44, 46, 65, 67, 72, 76, 77, 
+       81, 83, 84, 86, 90, 97, 99, 104, 
+       108, 109, 113, 115, 116, 118, 122, 9, 
+       10, 43, 45, 48, 57, 0, 13, 32, 
+       44, 46, 65, 67, 69, 72, 76, 77, 
+       81, 83, 84, 86, 90, 97, 99, 101, 
+       104, 108, 109, 113, 115, 116, 118, 122, 
+       9, 10, 43, 45, 48, 57, 13, 32, 
+       43, 45, 46, 9, 10, 48, 57, 13, 
+       32, 43, 45, 46, 9, 10, 48, 57, 
+       13, 32, 44, 46, 69, 101, 9, 10, 
+       43, 45, 48, 57, 13, 32, 43, 45, 
+       46, 9, 10, 48, 57, 0, 13, 32, 
+       44, 46, 65, 67, 69, 72, 76, 77, 
+       81, 83, 84, 86, 90, 97, 99, 101, 
+       104, 108, 109, 113, 115, 116, 118, 122, 
+       9, 10, 43, 45, 48, 57, 43, 45, 
+       48, 57, 48, 57, 0, 13, 32, 44, 
+       46, 65, 67, 72, 76, 77, 81, 83, 
+       84, 86, 90, 97, 99, 104, 108, 109, 
+       113, 115, 116, 118, 122, 9, 10, 43, 
+       45, 48, 57, 13, 32, 43, 45, 46, 
+       9, 10, 48, 57, 13, 32, 43, 45, 
+       46, 9, 10, 48, 57, 13, 32, 43, 
+       45, 46, 9, 10, 48, 57, 13, 32, 
+       43, 45, 46, 9, 10, 48, 57, 0, 
+       13, 32, 44, 46, 65, 67, 69, 72, 
+       76, 77, 81, 83, 84, 86, 90, 97, 
+       99, 101, 104, 108, 109, 113, 115, 116, 
+       118, 122, 9, 10, 43, 45, 48, 57, 
+       43, 45, 48, 57, 48, 57, 0, 13, 
+       32, 44, 46, 65, 67, 72, 76, 77, 
+       81, 83, 84, 86, 90, 97, 99, 104, 
+       108, 109, 113, 115, 116, 118, 122, 9, 
+       10, 43, 45, 48, 57, 0, 13, 32, 
+       44, 46, 65, 67, 69, 72, 76, 77, 
+       81, 83, 84, 86, 90, 97, 99, 101, 
+       104, 108, 109, 113, 115, 116, 118, 122, 
+       9, 10, 43, 45, 48, 57, 43, 45, 
+       48, 57, 48, 57, 13, 32, 44, 46, 
+       9, 10, 43, 45, 48, 57, 0, 13, 
+       32, 44, 46, 65, 67, 69, 72, 76, 
+       77, 81, 83, 84, 86, 90, 97, 99, 
+       101, 104, 108, 109, 113, 115, 116, 118, 
+       122, 9, 10, 43, 45, 48, 57, 43, 
+       45, 48, 57, 48, 57, 13, 32, 44, 
+       46, 9, 10, 43, 45, 48, 57, 43, 
+       45, 48, 57, 48, 57, 13, 32, 44, 
+       46, 9, 10, 43, 45, 48, 57, 43, 
+       45, 48, 57, 48, 57, 13, 32, 44, 
+       46, 9, 10, 43, 45, 48, 57, 43, 
+       45, 48, 57, 48, 57, 13, 32, 44, 
+       46, 9, 10, 43, 45, 48, 57, 43, 
+       45, 48, 57, 48, 57, 13, 32, 44, 
+       46, 9, 10, 43, 45, 48, 57, 43, 
+       45, 48, 57, 48, 57, 13, 32, 44, 
+       46, 9, 10, 43, 45, 48, 57, 43, 
+       45, 48, 57, 48, 57, 13, 32, 44, 
+       46, 9, 10, 43, 45, 48, 57, 0, 
+       13, 32, 44, 46, 65, 67, 69, 72, 
+       76, 77, 81, 83, 84, 86, 90, 97, 
+       99, 101, 104, 108, 109, 113, 115, 116, 
+       118, 122, 9, 10, 43, 45, 48, 57, 
+       43, 45, 48, 57, 48, 57, 13, 32, 
+       44, 46, 9, 10, 43, 45, 48, 57, 
+       43, 45, 48, 57, 48, 57, 13, 32, 
+       44, 46, 9, 10, 43, 45, 48, 57, 
+       43, 45, 48, 57, 48, 57, 13, 32, 
+       44, 46, 9, 10, 43, 45, 48, 57, 
+       43, 45, 48, 57, 48, 57, 13, 32, 
+       44, 46, 9, 10, 43, 45, 48, 57, 
+       43, 45, 48, 57, 48, 57, 13, 32, 
+       44, 46, 9, 10, 43, 45, 48, 57, 
+       43, 45, 48, 57, 48, 57, 0, 13, 
+       32, 44, 46, 65, 67, 72, 76, 77, 
+       81, 83, 84, 86, 90, 97, 99, 104, 
+       108, 109, 113, 115, 116, 118, 122, 9, 
+       10, 48, 57, 43, 45, 48, 57, 48, 
+       57, 0, 13, 32, 44, 46, 65, 67, 
+       72, 76, 77, 81, 83, 84, 86, 90, 
+       97, 99, 104, 108, 109, 113, 115, 116, 
+       118, 122, 9, 10, 43, 45, 48, 57, 
+       0, 13, 32, 44, 46, 65, 67, 69, 
+       72, 76, 77, 81, 83, 84, 86, 90, 
+       97, 99, 101, 104, 108, 109, 113, 115, 
+       116, 118, 122, 9, 10, 43, 45, 48, 
+       57, 13, 32, 43, 45, 46, 9, 10, 
+       48, 57, 13, 32, 44, 46, 69, 101, 
+       9, 10, 43, 45, 48, 57, 43, 45, 
+       48, 57, 48, 57, 13, 32, 44, 46, 
+       9, 10, 43, 45, 48, 57, 13, 32, 
+       44, 46, 69, 101, 9, 10, 43, 45, 
+       48, 57, 13, 32, 44, 9, 10, 13, 
+       32, 44, 46, 69, 101, 9, 10, 48, 
+       57, 43, 45, 48, 57, 48, 57, 13, 
+       32, 44, 9, 10, 48, 57, 13, 32, 
+       44, 46, 69, 101, 9, 10, 48, 57, 
+       43, 45, 48, 57, 48, 57, 13, 32, 
+       44, 46, 9, 10, 43, 45, 48, 57, 
+       13, 32, 44, 46, 69, 101, 9, 10, 
+       43, 45, 48, 57, 13, 32, 44, 46, 
+       69, 101, 9, 10, 43, 45, 48, 57, 
+       13, 32, 44, 46, 69, 101, 9, 10, 
+       43, 45, 48, 57, 43, 45, 48, 57, 
+       48, 57, 13, 32, 44, 46, 9, 10, 
+       43, 45, 48, 57, 13, 32, 44, 46, 
+       69, 101, 9, 10, 43, 45, 48, 57, 
+       43, 45, 48, 57, 48, 57, 13, 32, 
+       44, 46, 9, 10, 43, 45, 48, 57, 
+       13, 32, 43, 45, 46, 9, 10, 48, 
+       57, 43, 45, 48, 57, 48, 57, 13, 
+       32, 44, 46, 9, 10, 48, 57, 13, 
+       32, 43, 45, 46, 9, 10, 48, 57, 
+       13, 32, 44, 9, 10, 13, 32, 44, 
+       9, 10, 13, 32, 44, 46, 69, 101, 
+       9, 10, 43, 45, 48, 57, 43, 45, 
+       48, 57, 48, 57, 0, 13, 32, 44, 
+       46, 65, 67, 72, 76, 77, 81, 83, 
+       84, 86, 90, 97, 99, 104, 108, 109, 
+       113, 115, 116, 118, 122, 9, 10, 43, 
+       45, 48, 57, 43, 45, 48, 57, 48, 
+       57, 13, 32, 44, 46, 9, 10, 43, 
+       45, 48, 57, 0
+};
+
+static const char _svg_path_single_lengths[] = {
+       5, 0, 5, 5, 1, 0, 6, 4, 
+       1, 0, 27, 25, 1, 0, 6, 4, 
+       1, 0, 27, 5, 6, 5, 27, 3, 
+       3, 0, 6, 4, 3, 0, 6, 4, 
+       1, 0, 5, 5, 4, 3, 5, 4, 
+       3, 4, 1, 0, 6, 4, 1, 0, 
+       27, 25, 6, 6, 4, 5, 0, 6, 
+       6, 7, 6, 5, 4, 3, 6, 7, 
+       6, 4, 1, 0, 27, 5, 27, 5, 
+       5, 1, 0, 6, 4, 1, 0, 6, 
+       4, 1, 0, 6, 4, 1, 0, 6, 
+       4, 1, 0, 6, 4, 1, 0, 27, 
+       25, 6, 5, 6, 5, 6, 5, 6, 
+       5, 6, 5, 27, 2, 0, 25, 5, 
+       5, 1, 0, 27, 25, 1, 0, 27, 
+       5, 27, 2, 0, 25, 5, 5, 5, 
+       1, 0, 6, 4, 1, 0, 6, 4, 
+       1, 0, 6, 4, 1, 0, 27, 25, 
+       6, 5, 6, 5, 6, 5, 27, 2, 
+       0, 25, 5, 5, 1, 0, 6, 4, 
+       1, 0, 6, 4, 1, 0, 6, 4, 
+       1, 0, 27, 25, 6, 5, 6, 5, 
+       6, 5, 27, 2, 0, 25, 5, 5, 
+       1, 0, 6, 4, 1, 0, 27, 25, 
+       6, 5, 27, 2, 0, 25, 5, 5, 
+       1, 0, 27, 25, 1, 0, 27, 5, 
+       27, 2, 0, 25, 23, 23, 3, 5, 
+       5, 27, 2, 0, 25, 27, 5, 5, 
+       6, 5, 27, 2, 0, 25, 5, 5, 
+       5, 5, 27, 2, 0, 25, 27, 2, 
+       0, 4, 27, 2, 0, 4, 2, 0, 
+       4, 2, 0, 4, 2, 0, 4, 2, 
+       0, 4, 2, 0, 4, 2, 0, 4, 
+       27, 2, 0, 4, 2, 0, 4, 2, 
+       0, 4, 2, 0, 4, 2, 0, 4, 
+       2, 0, 25, 2, 0, 25, 27, 5, 
+       6, 2, 0, 4, 6, 3, 6, 2, 
+       0, 3, 6, 2, 0, 4, 6, 6, 
+       6, 2, 0, 4, 6, 2, 0, 4, 
+       5, 2, 0, 4, 5, 3, 3, 6, 
+       2, 0, 25, 2, 0, 4, 0
+};
+
+static const char _svg_path_range_lengths[] = {
+       1, 0, 2, 2, 1, 1, 3, 3, 
+       1, 1, 3, 3, 1, 1, 3, 3, 
+       1, 1, 3, 2, 3, 2, 3, 2, 
+       2, 1, 2, 2, 2, 1, 3, 3, 
+       1, 1, 2, 1, 1, 1, 1, 1, 
+       1, 3, 1, 1, 3, 3, 1, 1, 
+       2, 2, 2, 3, 3, 2, 1, 3, 
+       3, 2, 2, 1, 1, 1, 3, 2, 
+       3, 3, 1, 1, 3, 2, 2, 2, 
+       2, 1, 1, 3, 3, 1, 1, 3, 
+       3, 1, 1, 3, 3, 1, 1, 3, 
+       3, 1, 1, 3, 3, 1, 1, 3, 
+       3, 3, 2, 3, 2, 3, 2, 3, 
+       2, 3, 2, 3, 1, 1, 3, 2, 
+       2, 1, 1, 3, 3, 1, 1, 3, 
+       2, 3, 1, 1, 3, 2, 2, 2, 
+       1, 1, 3, 3, 1, 1, 3, 3, 
+       1, 1, 3, 3, 1, 1, 3, 3, 
+       3, 2, 3, 2, 3, 2, 3, 1, 
+       1, 3, 2, 2, 1, 1, 3, 3, 
+       1, 1, 3, 3, 1, 1, 3, 3, 
+       1, 1, 3, 3, 3, 2, 3, 2, 
+       3, 2, 3, 1, 1, 3, 2, 2, 
+       1, 1, 3, 3, 1, 1, 3, 3, 
+       3, 2, 3, 1, 1, 3, 2, 2, 
+       1, 1, 3, 3, 1, 1, 3, 2, 
+       3, 1, 1, 3, 1, 1, 2, 2, 
+       2, 3, 1, 1, 3, 3, 2, 2, 
+       3, 2, 3, 1, 1, 3, 2, 2, 
+       2, 2, 3, 1, 1, 3, 3, 1, 
+       1, 3, 3, 1, 1, 3, 1, 1, 
+       3, 1, 1, 3, 1, 1, 3, 1, 
+       1, 3, 1, 1, 3, 1, 1, 3, 
+       3, 1, 1, 3, 1, 1, 3, 1, 
+       1, 3, 1, 1, 3, 1, 1, 3, 
+       1, 1, 2, 1, 1, 3, 3, 2, 
+       3, 1, 1, 3, 3, 1, 2, 1, 
+       1, 2, 2, 1, 1, 3, 3, 3, 
+       3, 1, 1, 3, 3, 1, 1, 3, 
+       2, 1, 1, 2, 2, 1, 1, 3, 
+       1, 1, 3, 1, 1, 3, 0
+};
+
+static const short _svg_path_index_offsets[] = {
+       0, 7, 7, 15, 23, 26, 28, 38, 
+       46, 49, 51, 82, 111, 114, 116, 126, 
+       134, 137, 139, 170, 178, 188, 196, 227, 
+       233, 239, 241, 250, 257, 263, 265, 275, 
+       283, 286, 288, 296, 303, 309, 314, 321, 
+       327, 332, 340, 343, 345, 355, 363, 366, 
+       368, 398, 426, 435, 445, 453, 461, 463, 
+       473, 483, 493, 502, 509, 515, 520, 530, 
+       540, 550, 558, 561, 563, 594, 602, 632, 
+       640, 648, 651, 653, 663, 671, 674, 676, 
+       686, 694, 697, 699, 709, 717, 720, 722, 
+       732, 740, 743, 745, 755, 763, 766, 768, 
+       799, 828, 838, 846, 856, 864, 874, 882, 
+       892, 900, 910, 918, 949, 953, 955, 984, 
+       992, 1000, 1003, 1005, 1036, 1065, 1068, 1070, 
+       1101, 1109, 1140, 1144, 1146, 1175, 1183, 1191, 
+       1199, 1202, 1204, 1214, 1222, 1225, 1227, 1237, 
+       1245, 1248, 1250, 1260, 1268, 1271, 1273, 1304, 
+       1333, 1343, 1351, 1361, 1369, 1379, 1387, 1418, 
+       1422, 1424, 1453, 1461, 1469, 1472, 1474, 1484, 
+       1492, 1495, 1497, 1507, 1515, 1518, 1520, 1530, 
+       1538, 1541, 1543, 1574, 1603, 1613, 1621, 1631, 
+       1639, 1649, 1657, 1688, 1692, 1694, 1723, 1731, 
+       1739, 1742, 1744, 1754, 1762, 1765, 1767, 1798, 
+       1827, 1837, 1845, 1876, 1880, 1882, 1911, 1919, 
+       1927, 1930, 1932, 1963, 1992, 1995, 1997, 2028, 
+       2036, 2067, 2071, 2073, 2102, 2127, 2152, 2158, 
+       2166, 2174, 2205, 2209, 2211, 2240, 2271, 2279, 
+       2287, 2297, 2305, 2336, 2340, 2342, 2371, 2379, 
+       2387, 2395, 2403, 2434, 2438, 2440, 2469, 2500, 
+       2504, 2506, 2514, 2545, 2549, 2551, 2559, 2563, 
+       2565, 2573, 2577, 2579, 2587, 2591, 2593, 2601, 
+       2605, 2607, 2615, 2619, 2621, 2629, 2633, 2635, 
+       2643, 2674, 2678, 2680, 2688, 2692, 2694, 2702, 
+       2706, 2708, 2716, 2720, 2722, 2730, 2734, 2736, 
+       2744, 2748, 2750, 2778, 2782, 2784, 2813, 2844, 
+       2852, 2862, 2866, 2868, 2876, 2886, 2891, 2900, 
+       2904, 2906, 2912, 2921, 2925, 2927, 2935, 2945, 
+       2955, 2965, 2969, 2971, 2979, 2989, 2993, 2995, 
+       3003, 3011, 3015, 3017, 3024, 3032, 3037, 3042, 
+       3052, 3056, 3058, 3087, 3091, 3093, 3101
+};
+
+static const short _svg_path_indicies[] = {
+       73, 74, 74, 75, 76, 74, 0, 571, 
+       571, 572, 572, 573, 571, 574, 0, 631, 
+       631, 632, 632, 633, 631, 634, 0, 670, 
+       513, 0, 512, 0, 509, 509, 511, 548, 
+       514, 514, 509, 510, 512, 0, 490, 490, 
+       434, 436, 490, 435, 437, 0, 393, 127, 
+       0, 126, 0, 122, 123, 123, 125, 148, 
+       128, 129, 130, 131, 132, 133, 134, 135, 
+       136, 137, 138, 139, 140, 130, 141, 142, 
+       143, 144, 145, 146, 147, 138, 123, 124, 
+       126, 0, 73, 611, 611, 613, 614, 78, 
+       79, 80, 81, 75, 82, 83, 84, 85, 
+       86, 87, 88, 89, 90, 76, 91, 92, 
+       93, 94, 86, 611, 612, 615, 0, 671, 
+       519, 0, 518, 0, 515, 515, 517, 550, 
+       520, 520, 515, 516, 518, 0, 491, 491, 
+       438, 440, 491, 439, 441, 0, 394, 155, 
+       0, 154, 0, 150, 151, 151, 153, 176, 
+       156, 157, 158, 159, 160, 161, 162, 163, 
+       164, 165, 166, 167, 168, 158, 169, 170, 
+       171, 172, 173, 174, 175, 166, 151, 152, 
+       154, 0, 613, 613, 612, 612, 614, 613, 
+       615, 0, 515, 515, 517, 518, 520, 520, 
+       515, 516, 519, 0, 438, 438, 439, 439, 
+       440, 438, 441, 0, 150, 151, 151, 153, 
+       154, 156, 157, 158, 159, 160, 161, 162, 
+       163, 164, 165, 166, 167, 168, 158, 169, 
+       170, 171, 172, 173, 174, 175, 166, 151, 
+       152, 155, 0, 47, 47, 48, 47, 49, 
+       0, 8, 8, 9, 8, 10, 0, 385, 
+       0, 25, 25, 26, 27, 31, 31, 25, 
+       498, 0, 12, 12, 13, 14, 12, 15, 
+       0, 13, 13, 14, 13, 15, 0, 35, 
+       0, 32, 32, 34, 38, 37, 37, 32, 
+       33, 35, 0, 20, 20, 16, 18, 20, 
+       17, 19, 0, 2, 3, 0, 23, 0, 
+       21, 21, 22, 24, 24, 21, 23, 0, 
+       11, 11, 5, 6, 7, 11, 0, 5, 
+       5, 6, 7, 5, 0, 45, 45, 46, 
+       45, 0, 722, 722, 716, 717, 718, 722, 
+       0, 716, 716, 717, 718, 716, 0, 567, 
+       567, 568, 567, 0, 651, 651, 647, 430, 
+       651, 429, 433, 0, 676, 69, 0, 68, 
+       0, 351, 351, 352, 375, 70, 70, 351, 
+       66, 68, 0, 497, 497, 478, 480, 497, 
+       479, 481, 0, 406, 380, 0, 379, 0, 
+       350, 377, 377, 378, 382, 355, 356, 381, 
+       358, 359, 360, 361, 362, 363, 364, 365, 
+       366, 367, 381, 368, 369, 370, 371, 372, 
+       373, 374, 365, 377, 379, 0, 73, 95, 
+       95, 8, 9, 78, 79, 80, 81, 75, 
+       82, 83, 84, 85, 86, 87, 88, 89, 
+       90, 76, 91, 92, 93, 94, 86, 95, 
+       10, 0, 25, 25, 26, 29, 31, 31, 
+       25, 30, 0, 40, 40, 41, 562, 561, 
+       561, 40, 33, 30, 0, 483, 483, 425, 
+       426, 483, 17, 427, 0, 425, 425, 17, 
+       17, 426, 425, 427, 0, 501, 0, 499, 
+       499, 500, 38, 503, 503, 499, 33, 501, 
+       0, 482, 482, 422, 18, 423, 424, 482, 
+       17, 19, 0, 422, 422, 17, 17, 18, 
+       423, 424, 422, 19, 0, 63, 63, 64, 
+       23, 24, 24, 63, 3, 0, 723, 723, 
+       719, 720, 721, 723, 0, 719, 719, 720, 
+       721, 719, 0, 569, 569, 570, 569, 0, 
+       489, 489, 428, 430, 431, 432, 489, 429, 
+       433, 0, 428, 428, 429, 429, 430, 431, 
+       432, 428, 433, 0, 71, 71, 72, 68, 
+       70, 70, 71, 66, 69, 0, 496, 496, 
+       474, 476, 496, 475, 477, 0, 405, 354, 
+       0, 353, 0, 350, 351, 351, 352, 375, 
+       355, 356, 357, 358, 359, 360, 361, 362, 
+       363, 364, 365, 366, 367, 357, 368, 369, 
+       370, 371, 372, 373, 374, 365, 351, 66, 
+       353, 0, 478, 478, 479, 479, 480, 478, 
+       481, 0, 350, 377, 377, 378, 379, 355, 
+       356, 381, 358, 359, 360, 361, 362, 363, 
+       364, 365, 366, 367, 381, 368, 369, 370, 
+       371, 372, 373, 374, 365, 377, 380, 0, 
+       50, 50, 51, 51, 52, 50, 53, 0, 
+       98, 98, 97, 97, 99, 98, 100, 0, 
+       391, 392, 0, 507, 0, 504, 504, 506, 
+       546, 508, 508, 504, 505, 507, 0, 484, 
+       484, 486, 487, 484, 485, 488, 0, 790, 
+       788, 0, 755, 0, 751, 751, 753, 754, 
+       756, 756, 751, 752, 755, 0, 787, 787, 
+       773, 775, 787, 774, 776, 0, 748, 739, 
+       0, 710, 0, 689, 689, 691, 692, 711, 
+       711, 689, 690, 710, 0, 736, 736, 724, 
+       726, 736, 725, 727, 0, 677, 656, 0, 
+       655, 0, 652, 652, 654, 704, 657, 657, 
+       652, 653, 655, 0, 648, 648, 635, 637, 
+       648, 636, 638, 0, 672, 525, 0, 524, 
+       0, 521, 521, 523, 552, 526, 526, 521, 
+       522, 524, 0, 492, 492, 458, 460, 492, 
+       459, 461, 0, 401, 243, 0, 242, 0, 
+       238, 239, 239, 241, 264, 244, 245, 246, 
+       247, 248, 249, 250, 251, 252, 253, 254, 
+       255, 256, 246, 257, 258, 259, 260, 261, 
+       262, 263, 254, 239, 240, 242, 0, 73, 
+       96, 96, 98, 99, 78, 79, 80, 81, 
+       75, 82, 83, 84, 85, 86, 87, 88, 
+       89, 90, 76, 91, 92, 93, 94, 86, 
+       96, 97, 100, 0, 504, 504, 506, 507, 
+       508, 508, 504, 505, 392, 0, 486, 486, 
+       485, 485, 487, 486, 488, 0, 751, 751, 
+       753, 755, 756, 756, 751, 752, 788, 0, 
+       773, 773, 774, 774, 775, 773, 776, 0, 
+       689, 689, 691, 710, 711, 711, 689, 690, 
+       739, 0, 724, 724, 725, 725, 726, 724, 
+       727, 0, 652, 652, 654, 655, 657, 657, 
+       652, 653, 656, 0, 635, 635, 636, 636, 
+       637, 635, 638, 0, 521, 521, 523, 524, 
+       526, 526, 521, 522, 525, 0, 458, 458, 
+       459, 459, 460, 458, 461, 0, 238, 239, 
+       239, 241, 242, 244, 245, 246, 247, 248, 
+       249, 250, 251, 252, 253, 254, 255, 256, 
+       246, 257, 258, 259, 260, 261, 262, 263, 
+       254, 239, 240, 243, 0, 416, 416, 265, 
+       0, 265, 0, 238, 239, 239, 241, 264, 
+       244, 245, 247, 248, 249, 250, 251, 252, 
+       253, 254, 255, 256, 257, 258, 259, 260, 
+       261, 262, 263, 254, 239, 240, 265, 0, 
+       583, 583, 584, 584, 585, 583, 586, 0, 
+       446, 446, 447, 447, 448, 446, 449, 0, 
+       396, 397, 0, 388, 0, 178, 179, 179, 
+       181, 204, 184, 185, 624, 187, 188, 189, 
+       190, 191, 192, 193, 194, 195, 196, 624, 
+       197, 198, 199, 200, 201, 202, 203, 194, 
+       179, 180, 623, 0, 73, 621, 621, 442, 
+       444, 78, 79, 80, 81, 75, 82, 83, 
+       84, 85, 86, 87, 88, 89, 90, 76, 
+       91, 92, 93, 94, 86, 621, 443, 445, 
+       0, 395, 183, 0, 182, 0, 178, 179, 
+       179, 181, 204, 184, 185, 186, 187, 188, 
+       189, 190, 191, 192, 193, 194, 195, 196, 
+       186, 197, 198, 199, 200, 201, 202, 203, 
+       194, 179, 180, 182, 0, 442, 442, 443, 
+       443, 444, 442, 445, 0, 178, 179, 179, 
+       181, 182, 184, 185, 186, 187, 188, 189, 
+       190, 191, 192, 193, 194, 195, 196, 186, 
+       197, 198, 199, 200, 201, 202, 203, 194, 
+       179, 180, 183, 0, 412, 412, 205, 0, 
+       205, 0, 178, 179, 179, 181, 204, 184, 
+       185, 187, 188, 189, 190, 191, 192, 193, 
+       194, 195, 196, 197, 198, 199, 200, 201, 
+       202, 203, 194, 179, 180, 205, 0, 575, 
+       575, 576, 576, 577, 575, 578, 0, 761, 
+       761, 762, 762, 763, 761, 764, 0, 781, 
+       781, 782, 782, 783, 781, 784, 0, 750, 
+       741, 0, 714, 0, 699, 699, 701, 702, 
+       715, 715, 699, 700, 714, 0, 738, 738, 
+       732, 734, 738, 733, 735, 0, 679, 668, 
+       0, 667, 0, 664, 664, 666, 708, 669, 
+       669, 664, 665, 667, 0, 650, 650, 643, 
+       645, 650, 644, 646, 0, 674, 537, 0, 
+       536, 0, 533, 533, 535, 556, 538, 538, 
+       533, 534, 536, 0, 494, 494, 466, 468, 
+       494, 467, 469, 0, 403, 299, 0, 298, 
+       0, 294, 295, 295, 297, 320, 300, 301, 
+       302, 303, 304, 305, 306, 307, 308, 309, 
+       310, 311, 312, 302, 313, 314, 315, 316, 
+       317, 318, 319, 310, 295, 296, 298, 0, 
+       73, 786, 786, 781, 783, 78, 79, 80, 
+       81, 75, 82, 83, 84, 85, 86, 87, 
+       88, 89, 90, 76, 91, 92, 93, 94, 
+       86, 786, 782, 784, 0, 699, 699, 701, 
+       714, 715, 715, 699, 700, 741, 0, 732, 
+       732, 733, 733, 734, 732, 735, 0, 664, 
+       664, 666, 667, 669, 669, 664, 665, 668, 
+       0, 643, 643, 644, 644, 645, 643, 646, 
+       0, 533, 533, 535, 536, 538, 538, 533, 
+       534, 537, 0, 466, 466, 467, 467, 468, 
+       466, 469, 0, 294, 295, 295, 297, 298, 
+       300, 301, 302, 303, 304, 305, 306, 307, 
+       308, 309, 310, 311, 312, 302, 313, 314, 
+       315, 316, 317, 318, 319, 310, 295, 296, 
+       299, 0, 418, 418, 321, 0, 321, 0, 
+       294, 295, 295, 297, 320, 300, 301, 303, 
+       304, 305, 306, 307, 308, 309, 310, 311, 
+       312, 313, 314, 315, 316, 317, 318, 319, 
+       310, 295, 296, 321, 0, 757, 757, 758, 
+       758, 759, 757, 760, 0, 777, 777, 778, 
+       778, 779, 777, 780, 0, 749, 740, 0, 
+       712, 0, 694, 694, 696, 697, 713, 713, 
+       694, 695, 712, 0, 737, 737, 728, 730, 
+       737, 729, 731, 0, 678, 662, 0, 661, 
+       0, 658, 658, 660, 706, 663, 663, 658, 
+       659, 661, 0, 649, 649, 639, 641, 649, 
+       640, 642, 0, 673, 531, 0, 530, 0, 
+       527, 527, 529, 554, 532, 532, 527, 528, 
+       530, 0, 493, 493, 462, 464, 493, 463, 
+       465, 0, 402, 271, 0, 270, 0, 266, 
+       267, 267, 269, 292, 272, 273, 274, 275, 
+       276, 277, 278, 279, 280, 281, 282, 283, 
+       284, 274, 285, 286, 287, 288, 289, 290, 
+       291, 282, 267, 268, 270, 0, 73, 785, 
+       785, 777, 779, 78, 79, 80, 81, 75, 
+       82, 83, 84, 85, 86, 87, 88, 89, 
+       90, 76, 91, 92, 93, 94, 86, 785, 
+       778, 780, 0, 694, 694, 696, 712, 713, 
+       713, 694, 695, 740, 0, 728, 728, 729, 
+       729, 730, 728, 731, 0, 658, 658, 660, 
+       661, 663, 663, 658, 659, 662, 0, 639, 
+       639, 640, 640, 641, 639, 642, 0, 527, 
+       527, 529, 530, 532, 532, 527, 528, 531, 
+       0, 462, 462, 463, 463, 464, 462, 465, 
+       0, 266, 267, 267, 269, 270, 272, 273, 
+       274, 275, 276, 277, 278, 279, 280, 281, 
+       282, 283, 284, 274, 285, 286, 287, 288, 
+       289, 290, 291, 282, 267, 268, 271, 0, 
+       417, 417, 293, 0, 293, 0, 266, 267, 
+       267, 269, 292, 272, 273, 275, 276, 277, 
+       278, 279, 280, 281, 282, 283, 284, 285, 
+       286, 287, 288, 289, 290, 291, 282, 267, 
+       268, 293, 0, 579, 579, 580, 580, 581, 
+       579, 582, 0, 618, 618, 617, 617, 619, 
+       618, 620, 0, 675, 543, 0, 542, 0, 
+       539, 539, 541, 558, 544, 544, 539, 540, 
+       542, 0, 495, 495, 470, 472, 495, 471, 
+       473, 0, 404, 327, 0, 326, 0, 322, 
+       323, 323, 325, 348, 328, 329, 330, 331, 
+       332, 333, 334, 335, 336, 337, 338, 339, 
+       340, 330, 341, 342, 343, 344, 345, 346, 
+       347, 338, 323, 324, 326, 0, 73, 616, 
+       616, 618, 619, 78, 79, 80, 81, 75, 
+       82, 83, 84, 85, 86, 87, 88, 89, 
+       90, 76, 91, 92, 93, 94, 86, 616, 
+       617, 620, 0, 539, 539, 541, 542, 544, 
+       544, 539, 540, 543, 0, 470, 470, 471, 
+       471, 472, 470, 473, 0, 322, 323, 323, 
+       325, 326, 328, 329, 330, 331, 332, 333, 
+       334, 335, 336, 337, 338, 339, 340, 330, 
+       341, 342, 343, 344, 345, 346, 347, 338, 
+       323, 324, 327, 0, 419, 419, 349, 0, 
+       349, 0, 322, 323, 323, 325, 348, 328, 
+       329, 331, 332, 333, 334, 335, 336, 337, 
+       338, 339, 340, 341, 342, 343, 344, 345, 
+       346, 347, 338, 323, 324, 349, 0, 587, 
+       587, 588, 588, 589, 587, 590, 0, 454, 
+       454, 455, 455, 456, 454, 457, 0, 399, 
+       400, 0, 390, 0, 208, 209, 209, 211, 
+       234, 214, 215, 628, 217, 218, 219, 220, 
+       221, 222, 223, 224, 225, 226, 628, 227, 
+       228, 229, 230, 231, 232, 233, 224, 209, 
+       210, 627, 0, 73, 622, 622, 450, 452, 
+       78, 79, 80, 81, 75, 82, 83, 84, 
+       85, 86, 87, 88, 89, 90, 76, 91, 
+       92, 93, 94, 86, 622, 451, 453, 0, 
+       398, 213, 0, 212, 0, 208, 209, 209, 
+       211, 234, 214, 215, 216, 217, 218, 219, 
+       220, 221, 222, 223, 224, 225, 226, 216, 
+       227, 228, 229, 230, 231, 232, 233, 224, 
+       209, 210, 212, 0, 450, 450, 451, 451, 
+       452, 450, 453, 0, 208, 209, 209, 211, 
+       212, 214, 215, 216, 217, 218, 219, 220, 
+       221, 222, 223, 224, 225, 226, 216, 227, 
+       228, 229, 230, 231, 232, 233, 224, 209, 
+       210, 213, 0, 414, 414, 235, 0, 235, 
+       0, 208, 209, 209, 211, 234, 214, 215, 
+       217, 218, 219, 220, 221, 222, 223, 224, 
+       225, 226, 227, 228, 229, 230, 231, 232, 
+       233, 224, 209, 210, 235, 0, 101, 102, 
+       102, 103, 104, 105, 106, 107, 108, 109, 
+       110, 111, 112, 113, 114, 115, 116, 117, 
+       118, 119, 120, 121, 112, 102, 0, 73, 
+       77, 77, 78, 79, 80, 81, 75, 82, 
+       83, 84, 85, 86, 87, 88, 89, 90, 
+       76, 91, 92, 93, 94, 86, 77, 0, 
+       54, 54, 55, 54, 56, 0, 57, 57, 
+       58, 58, 59, 57, 60, 0, 603, 603, 
+       604, 604, 605, 603, 606, 0, 178, 179, 
+       179, 181, 625, 184, 185, 624, 187, 188, 
+       189, 190, 191, 192, 193, 194, 195, 196, 
+       624, 197, 198, 199, 200, 201, 202, 203, 
+       194, 179, 180, 626, 0, 413, 413, 387, 
+       0, 387, 0, 178, 179, 179, 181, 204, 
+       184, 185, 187, 188, 189, 190, 191, 192, 
+       193, 194, 195, 196, 197, 198, 199, 200, 
+       201, 202, 203, 194, 179, 180, 206, 0, 
+       178, 179, 179, 181, 207, 184, 185, 186, 
+       187, 188, 189, 190, 191, 192, 193, 194, 
+       195, 196, 186, 197, 198, 199, 200, 201, 
+       202, 203, 194, 179, 180, 206, 0, 595, 
+       595, 596, 596, 597, 595, 598, 0, 591, 
+       591, 592, 592, 593, 591, 594, 0, 509, 
+       509, 511, 512, 514, 514, 509, 510, 513, 
+       0, 434, 434, 435, 435, 436, 434, 437, 
+       0, 122, 123, 123, 125, 126, 128, 129, 
+       130, 131, 132, 133, 134, 135, 136, 137, 
+       138, 139, 140, 130, 141, 142, 143, 144, 
+       145, 146, 147, 138, 123, 124, 127, 0, 
+       410, 410, 149, 0, 149, 0, 122, 123, 
+       123, 125, 148, 128, 129, 131, 132, 133, 
+       134, 135, 136, 137, 138, 139, 140, 141, 
+       142, 143, 144, 145, 146, 147, 138, 123, 
+       124, 149, 0, 769, 769, 770, 770, 771, 
+       769, 772, 0, 765, 765, 766, 766, 767, 
+       765, 768, 0, 599, 599, 600, 600, 601, 
+       599, 602, 0, 607, 607, 608, 608, 609, 
+       607, 610, 0, 208, 209, 209, 211, 629, 
+       214, 215, 628, 217, 218, 219, 220, 221, 
+       222, 223, 224, 225, 226, 628, 227, 228, 
+       229, 230, 231, 232, 233, 224, 209, 210, 
+       630, 0, 415, 415, 389, 0, 389, 0, 
+       208, 209, 209, 211, 234, 214, 215, 217, 
+       218, 219, 220, 221, 222, 223, 224, 225, 
+       226, 227, 228, 229, 230, 231, 232, 233, 
+       224, 209, 210, 236, 0, 208, 209, 209, 
+       211, 237, 214, 215, 216, 217, 218, 219, 
+       220, 221, 222, 223, 224, 225, 226, 216, 
+       227, 228, 229, 230, 231, 232, 233, 224, 
+       209, 210, 236, 0, 682, 682, 549, 0, 
+       549, 0, 509, 509, 511, 548, 509, 510, 
+       549, 0, 208, 209, 209, 211, 237, 214, 
+       215, 628, 217, 218, 219, 220, 221, 222, 
+       223, 224, 225, 226, 628, 227, 228, 229, 
+       230, 231, 232, 233, 224, 209, 210, 627, 
+       0, 687, 687, 559, 0, 559, 0, 539, 
+       539, 541, 558, 539, 540, 559, 0, 685, 
+       685, 555, 0, 555, 0, 527, 527, 529, 
+       554, 527, 528, 555, 0, 746, 746, 707, 
+       0, 707, 0, 658, 658, 660, 706, 658, 
+       659, 707, 0, 743, 743, 698, 0, 698, 
+       0, 694, 694, 696, 697, 694, 695, 698, 
+       0, 686, 686, 557, 0, 557, 0, 533, 
+       533, 535, 556, 533, 534, 557, 0, 747, 
+       747, 709, 0, 709, 0, 664, 664, 666, 
+       708, 664, 665, 709, 0, 744, 744, 703, 
+       0, 703, 0, 699, 699, 701, 702, 699, 
+       700, 703, 0, 178, 179, 179, 181, 207, 
+       184, 185, 624, 187, 188, 189, 190, 191, 
+       192, 193, 194, 195, 196, 624, 197, 198, 
+       199, 200, 201, 202, 203, 194, 179, 180, 
+       623, 0, 684, 684, 553, 0, 553, 0, 
+       521, 521, 523, 552, 521, 522, 553, 0, 
+       745, 745, 705, 0, 705, 0, 652, 652, 
+       654, 704, 652, 653, 705, 0, 742, 742, 
+       693, 0, 693, 0, 689, 689, 691, 692, 
+       689, 690, 693, 0, 791, 791, 789, 0, 
+       789, 0, 751, 751, 753, 754, 751, 752, 
+       789, 0, 681, 681, 547, 0, 547, 0, 
+       504, 504, 506, 546, 504, 505, 547, 0, 
+       421, 421, 383, 0, 383, 0, 350, 377, 
+       377, 378, 382, 355, 356, 358, 359, 360, 
+       361, 362, 363, 364, 365, 366, 367, 368, 
+       369, 370, 371, 372, 373, 374, 365, 377, 
+       383, 0, 420, 420, 376, 0, 376, 0, 
+       350, 351, 351, 352, 375, 355, 356, 358, 
+       359, 360, 361, 362, 363, 364, 365, 366, 
+       367, 368, 369, 370, 371, 372, 373, 374, 
+       365, 351, 66, 376, 0, 350, 351, 351, 
+       352, 353, 355, 356, 357, 358, 359, 360, 
+       361, 362, 363, 364, 365, 366, 367, 357, 
+       368, 369, 370, 371, 372, 373, 374, 365, 
+       351, 66, 354, 0, 474, 474, 475, 475, 
+       476, 474, 477, 0, 351, 351, 352, 68, 
+       70, 70, 351, 66, 69, 0, 688, 688, 
+       560, 0, 560, 0, 351, 351, 352, 375, 
+       351, 66, 560, 0, 65, 65, 67, 68, 
+       70, 70, 65, 66, 69, 0, 565, 565, 
+       566, 565, 0, 21, 21, 22, 23, 24, 
+       24, 21, 3, 0, 4, 4, 1, 0, 
+       1, 0, 21, 21, 22, 21, 1, 0, 
+       61, 61, 62, 23, 24, 24, 61, 3, 
+       0, 680, 680, 545, 0, 545, 0, 499, 
+       499, 500, 38, 499, 33, 545, 0, 499, 
+       499, 500, 501, 503, 503, 499, 33, 502, 
+       0, 40, 40, 41, 27, 561, 561, 40, 
+       33, 498, 0, 40, 40, 41, 42, 561, 
+       561, 40, 33, 498, 0, 409, 409, 386, 
+       0, 386, 0, 40, 40, 41, 27, 40, 
+       33, 28, 0, 40, 40, 41, 42, 37, 
+       37, 40, 33, 28, 0, 408, 408, 39, 
+       0, 39, 0, 32, 32, 34, 38, 32, 
+       33, 39, 0, 16, 16, 17, 17, 18, 
+       16, 19, 0, 407, 407, 384, 0, 384, 
+       0, 25, 25, 26, 27, 25, 28, 0, 
+       647, 647, 429, 429, 430, 647, 433, 0, 
+       563, 563, 564, 563, 0, 43, 43, 44, 
+       43, 0, 32, 32, 34, 35, 37, 37, 
+       32, 33, 36, 0, 411, 411, 177, 0, 
+       177, 0, 150, 151, 151, 153, 176, 156, 
+       157, 159, 160, 161, 162, 163, 164, 165, 
+       166, 167, 168, 169, 170, 171, 172, 173, 
+       174, 175, 166, 151, 152, 177, 0, 683, 
+       683, 551, 0, 551, 0, 515, 515, 517, 
+       550, 515, 516, 551, 0, 0, 0
+};
+
+static const short _svg_path_trans_targs_wi[] = {
+       1, 297, 33, 294, 296, 36, 37, 318, 
+       24, 25, 50, 35, 27, 28, 29, 319, 
+       312, 32, 33, 294, 31, 35, 36, 34, 
+       295, 27, 28, 29, 308, 26, 51, 313, 
+       31, 32, 312, 30, 319, 309, 33, 311, 
+       52, 53, 30, 38, 39, 38, 39, 24, 
+       25, 50, 72, 73, 74, 97, 24, 25, 
+       50, 72, 73, 74, 97, 59, 60, 59, 
+       60, 65, 46, 287, 44, 288, 289, 65, 
+       287, 326, 0, 2, 223, 213, 23, 71, 
+       111, 125, 126, 154, 182, 198, 212, 214, 
+       215, 216, 222, 230, 231, 232, 233, 49, 
+       96, 73, 72, 74, 97, 326, 213, 23, 
+       71, 111, 125, 2, 126, 154, 182, 198, 
+       212, 214, 215, 216, 222, 223, 230, 231, 
+       232, 233, 326, 11, 12, 19, 10, 226, 
+       23, 71, 227, 111, 125, 2, 126, 154, 
+       182, 198, 212, 214, 215, 216, 222, 223, 
+       230, 231, 232, 233, 13, 229, 326, 11, 
+       12, 19, 18, 22, 23, 71, 320, 111, 
+       125, 2, 126, 154, 182, 198, 212, 214, 
+       215, 216, 222, 223, 230, 231, 232, 233, 
+       13, 322, 326, 116, 117, 120, 119, 121, 
+       23, 71, 122, 111, 125, 2, 126, 154, 
+       182, 198, 212, 214, 215, 216, 222, 223, 
+       230, 231, 232, 233, 118, 124, 221, 119, 
+       326, 203, 204, 207, 206, 208, 23, 71, 
+       209, 111, 125, 2, 126, 154, 182, 198, 
+       212, 214, 215, 216, 222, 223, 230, 231, 
+       232, 233, 205, 211, 238, 206, 326, 96, 
+       73, 72, 95, 107, 23, 71, 108, 111, 
+       125, 2, 126, 154, 182, 198, 212, 214, 
+       215, 216, 222, 223, 230, 231, 232, 233, 
+       74, 110, 326, 171, 156, 155, 170, 178, 
+       23, 71, 179, 111, 125, 2, 126, 154, 
+       182, 198, 212, 214, 215, 216, 222, 223, 
+       230, 231, 232, 233, 157, 181, 326, 143, 
+       128, 127, 142, 150, 23, 71, 151, 111, 
+       125, 2, 126, 154, 182, 198, 212, 214, 
+       215, 216, 222, 223, 230, 231, 232, 233, 
+       129, 153, 326, 191, 184, 183, 190, 194, 
+       23, 71, 195, 111, 125, 2, 126, 154, 
+       182, 198, 212, 214, 215, 216, 222, 223, 
+       230, 231, 232, 233, 185, 197, 326, 45, 
+       69, 68, 286, 23, 71, 283, 111, 125, 
+       2, 126, 154, 182, 198, 212, 214, 215, 
+       216, 222, 223, 230, 231, 232, 233, 47, 
+       285, 49, 24, 48, 70, 280, 25, 282, 
+       315, 26, 307, 220, 115, 237, 202, 74, 
+       97, 9, 17, 118, 114, 217, 205, 201, 
+       234, 94, 169, 141, 189, 67, 47, 314, 
+       310, 306, 228, 321, 123, 219, 210, 236, 
+       109, 180, 152, 196, 284, 281, 57, 58, 
+       298, 53, 54, 302, 63, 42, 43, 64, 
+       292, 288, 225, 8, 9, 226, 21, 16, 
+       17, 22, 120, 117, 118, 121, 112, 113, 
+       114, 217, 207, 204, 205, 208, 199, 200, 
+       201, 234, 106, 93, 94, 107, 177, 168, 
+       169, 178, 149, 140, 141, 150, 193, 188, 
+       189, 194, 287, 66, 67, 286, 69, 46, 
+       47, 70, 56, 52, 76, 77, 98, 78, 
+       99, 62, 7, 15, 92, 167, 139, 187, 
+       65, 45, 304, 56, 57, 55, 302, 299, 
+       76, 77, 98, 75, 277, 7, 8, 225, 
+       6, 224, 239, 15, 16, 21, 14, 20, 
+       323, 92, 93, 106, 91, 105, 265, 167, 
+       168, 177, 166, 176, 246, 139, 140, 149, 
+       138, 148, 255, 187, 188, 193, 186, 192, 
+       243, 301, 78, 279, 9, 241, 17, 325, 
+       94, 267, 169, 248, 141, 257, 189, 245, 
+       291, 305, 303, 41, 316, 62, 63, 41, 
+       316, 62, 63, 3, 4, 5, 224, 19, 
+       12, 13, 20, 183, 184, 185, 192, 112, 
+       113, 114, 217, 199, 200, 201, 234, 3, 
+       4, 5, 224, 19, 12, 13, 20, 183, 
+       184, 185, 192, 112, 113, 114, 217, 199, 
+       200, 201, 234, 11, 12, 19, 13, 20, 
+       191, 184, 183, 185, 192, 116, 203, 264, 
+       218, 115, 217, 242, 235, 202, 234, 3, 
+       4, 5, 224, 104, 89, 90, 105, 175, 
+       164, 165, 176, 147, 136, 137, 148, 316, 
+       88, 163, 135, 41, 88, 89, 104, 87, 
+       103, 268, 163, 164, 175, 162, 174, 249, 
+       135, 136, 147, 134, 146, 258, 5, 13, 
+       90, 165, 137, 185, 43, 86, 161, 133, 
+       300, 278, 240, 324, 266, 247, 256, 244, 
+       290, 84, 85, 102, 86, 273, 159, 160, 
+       173, 161, 254, 131, 132, 145, 133, 263, 
+       90, 270, 165, 251, 137, 260, 83, 271, 
+       158, 252, 130, 261, 39, 40, 317, 60, 
+       61, 293, 38, 59, 102, 85, 86, 103, 
+       173, 160, 161, 174, 145, 132, 133, 146, 
+       84, 159, 131, 101, 172, 144, 272, 253, 
+       262, 269, 250, 259, 82, 157, 129, 80, 
+       81, 100, 82, 79, 274, 155, 156, 157, 
+       172, 127, 128, 129, 144, 155, 156, 157, 
+       172, 127, 128, 129, 144, 100, 81, 82, 
+       101, 155, 156, 157, 172, 127, 128, 129, 
+       144, 171, 143, 80, 99, 276, 78, 275
+};
+
+static const unsigned char _svg_path_trans_actions_wi[] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 1, 1, 0, 0, 0, 1, 1, 
+       0, 1, 1, 1, 0, 3, 3, 0, 
+       0, 3, 3, 17, 17, 17, 17, 0, 
+       3, 17, 3, 0, 0, 0, 17, 0, 
+       3, 3, 17, 5, 5, 7, 7, 9, 
+       59, 59, 9, 59, 59, 59, 11, 62, 
+       62, 11, 62, 62, 62, 20, 20, 23, 
+       23, 53, 17, 53, 0, 0, 0, 56, 
+       56, 15, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 1, 0, 1, 1, 65, 13, 13, 
+       13, 13, 13, 13, 13, 13, 13, 13, 
+       13, 13, 13, 13, 13, 13, 13, 13, 
+       13, 13, 72, 26, 68, 26, 0, 0, 
+       26, 26, 0, 26, 26, 26, 26, 26, 
+       26, 26, 26, 26, 26, 26, 26, 26, 
+       26, 26, 26, 26, 68, 0, 80, 29, 
+       76, 29, 0, 0, 29, 29, 0, 29, 
+       29, 29, 29, 29, 29, 29, 29, 29, 
+       29, 29, 29, 29, 29, 29, 29, 29, 
+       76, 0, 88, 32, 84, 32, 0, 0, 
+       32, 32, 0, 32, 32, 32, 32, 32, 
+       32, 32, 32, 32, 32, 32, 32, 32, 
+       32, 32, 32, 32, 84, 0, 84, 84, 
+       96, 35, 92, 35, 0, 0, 35, 35, 
+       0, 35, 35, 35, 35, 35, 35, 35, 
+       35, 35, 35, 35, 35, 35, 35, 35, 
+       35, 35, 92, 0, 92, 92, 104, 38, 
+       100, 38, 0, 0, 38, 38, 0, 38, 
+       38, 38, 38, 38, 38, 38, 38, 38, 
+       38, 38, 38, 38, 38, 38, 38, 38, 
+       100, 0, 112, 41, 108, 41, 0, 0, 
+       41, 41, 0, 41, 41, 41, 41, 41, 
+       41, 41, 41, 41, 41, 41, 41, 41, 
+       41, 41, 41, 41, 108, 0, 120, 44, 
+       116, 44, 0, 0, 44, 44, 0, 44, 
+       44, 44, 44, 44, 44, 44, 44, 44, 
+       44, 44, 44, 44, 44, 44, 44, 44, 
+       116, 0, 128, 47, 124, 47, 0, 0, 
+       47, 47, 0, 47, 47, 47, 47, 47, 
+       47, 47, 47, 47, 47, 47, 47, 47, 
+       47, 47, 47, 47, 124, 0, 136, 3, 
+       3, 0, 0, 50, 50, 0, 50, 50, 
+       50, 50, 50, 50, 50, 50, 50, 50, 
+       50, 50, 50, 50, 50, 50, 50, 17, 
+       0, 50, 50, 0, 0, 0, 132, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 1, 
+       1, 0, 1, 1, 0, 1, 1, 1, 
+       1, 1, 0, 1, 1, 1, 0, 1, 
+       1, 1, 0, 1, 1, 1, 0, 1, 
+       1, 1, 0, 1, 1, 1, 0, 1, 
+       1, 1, 0, 1, 1, 1, 0, 1, 
+       1, 1, 0, 1, 1, 1, 0, 1, 
+       1, 1, 0, 1, 1, 1, 0, 1, 
+       1, 1, 0, 0, 0, 1, 0, 1, 
+       1, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 17, 3, 3, 0, 0, 0, 
+       3, 17, 3, 0, 0, 3, 17, 3, 
+       0, 0, 0, 3, 17, 3, 0, 0, 
+       0, 3, 17, 3, 0, 0, 0, 3, 
+       17, 3, 0, 0, 0, 3, 17, 3, 
+       0, 0, 0, 3, 17, 3, 0, 0, 
+       0, 0, 17, 0, 17, 0, 17, 0, 
+       17, 0, 17, 0, 17, 0, 17, 0, 
+       0, 0, 17, 5, 5, 5, 5, 7, 
+       7, 7, 7, 9, 59, 59, 59, 9, 
+       59, 59, 59, 9, 59, 59, 59, 9, 
+       59, 59, 59, 9, 59, 59, 59, 11, 
+       62, 62, 62, 11, 62, 62, 62, 11, 
+       62, 62, 62, 11, 62, 62, 62, 11, 
+       62, 62, 62, 0, 1, 0, 1, 1, 
+       0, 1, 0, 1, 1, 0, 0, 84, 
+       0, 84, 84, 92, 0, 92, 92, 0, 
+       1, 1, 1, 0, 1, 1, 1, 0, 
+       1, 1, 1, 0, 1, 1, 1, 0, 
+       0, 0, 0, 0, 3, 17, 3, 0, 
+       0, 0, 3, 17, 3, 0, 0, 0, 
+       3, 17, 3, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 3, 17, 3, 17, 0, 3, 17, 
+       3, 17, 0, 3, 17, 3, 17, 0, 
+       17, 0, 17, 0, 17, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 1, 1, 1, 
+       0, 1, 1, 1, 0, 1, 1, 1, 
+       0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 3, 
+       17, 3, 17, 0, 0, 9, 59, 59, 
+       59, 9, 59, 59, 59, 11, 62, 62, 
+       62, 11, 62, 62, 62, 0, 1, 1, 
+       1, 0, 1, 1, 1, 0, 1, 1, 
+       1, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const int svg_path_start = 0;
+
+static const int svg_path_first_final = 326;
+
+#line 133 "/home/michael/2geom/src/svg-path-parser.rl"
+
+
+void Parser::parse(char const *str)
+throw(SVGPathParseError)
+{
+    char const *p = str;
+    char const *start = NULL;
+    int cs;
+
+    _reset();
+
+    
+#line 1373 "/home/michael/2geom/src/svg-path-parser.cpp"
+       {
+       cs = svg_path_start;
+       }
+       {
+       int _klen;
+       unsigned int _trans;
+       const char *_acts;
+       unsigned int _nacts;
+       const char *_keys;
+
+_resume:
+       if ( cs == 1 )
+               goto _out;
+       _keys = _svg_path_trans_keys + _svg_path_key_offsets[cs];
+       _trans = _svg_path_index_offsets[cs];
+
+       _klen = _svg_path_single_lengths[cs];
+       if ( _klen > 0 ) {
+               const char *_lower = _keys;
+               const char *_mid;
+               const char *_upper = _keys + _klen - 1;
+               while (1) {
+                       if ( _upper < _lower )
+                               break;
+
+                       _mid = _lower + ((_upper-_lower) >> 1);
+                       if ( (*p) < *_mid )
+                               _upper = _mid - 1;
+                       else if ( (*p) > *_mid )
+                               _lower = _mid + 1;
+                       else {
+                               _trans += (_mid - _keys);
+                               goto _match;
+                       }
+               }
+               _keys += _klen;
+               _trans += _klen;
+       }
+
+       _klen = _svg_path_range_lengths[cs];
+       if ( _klen > 0 ) {
+               const char *_lower = _keys;
+               const char *_mid;
+               const char *_upper = _keys + (_klen<<1) - 2;
+               while (1) {
+                       if ( _upper < _lower )
+                               break;
+
+                       _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+                       if ( (*p) < _mid[0] )
+                               _upper = _mid - 2;
+                       else if ( (*p) > _mid[1] )
+                               _lower = _mid + 2;
+                       else {
+                               _trans += ((_mid - _keys)>>1);
+                               goto _match;
+                       }
+               }
+               _trans += _klen;
+       }
+
+_match:
+       _trans = _svg_path_indicies[_trans];
+       cs = _svg_path_trans_targs_wi[_trans];
+
+       if ( _svg_path_trans_actions_wi[_trans] == 0 )
+               goto _again;
+
+       _acts = _svg_path_actions + _svg_path_trans_actions_wi[_trans];
+       _nacts = (unsigned int) *_acts++;
+       while ( _nacts-- > 0 )
+       {
+               switch ( *_acts++ )
+               {
+       case 0:
+#line 145 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            start = p;
+        }
+       break;
+       case 1:
+#line 149 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            char const *end=p;
+            std::string buf(start, end);
+            _push(g_ascii_strtod(buf.c_str(), NULL));
+            start = NULL;
+        }
+       break;
+       case 2:
+#line 156 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            _push(1.0);
+        }
+       break;
+       case 3:
+#line 160 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            _push(0.0);
+        }
+       break;
+       case 4:
+#line 164 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            _absolute = true;
+        }
+       break;
+       case 5:
+#line 168 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            _absolute = false;
+        }
+       break;
+       case 6:
+#line 172 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            _moveTo(_pop_point());
+        }
+       break;
+       case 7:
+#line 176 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            _lineTo(_pop_point());
+        }
+       break;
+       case 8:
+#line 180 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            _lineTo(Point(_pop_coord(X), _current[Y]));
+        }
+       break;
+       case 9:
+#line 184 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            _lineTo(Point(_current[X], _pop_coord(Y)));
+        }
+       break;
+       case 10:
+#line 188 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            Point p = _pop_point();
+            Point c1 = _pop_point();
+            Point c0 = _pop_point();
+            _curveTo(c0, c1, p);
+        }
+       break;
+       case 11:
+#line 195 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            Point p = _pop_point();
+            Point c1 = _pop_point();
+            _curveTo(_cubic_tangent, c1, p);
+        }
+       break;
+       case 12:
+#line 201 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            Point p = _pop_point();
+            Point c = _pop_point();
+            _quadTo(c, p);
+        }
+       break;
+       case 13:
+#line 207 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            Point p = _pop_point();
+            _quadTo(_quad_tangent, p);
+        }
+       break;
+       case 14:
+#line 212 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            Point point = _pop_point();
+            bool sweep = _pop_flag();
+            bool large_arc = _pop_flag();
+            double angle = _pop();
+            double ry = _pop();
+            double rx = _pop();
+
+            _arcTo(rx, ry, angle, large_arc, sweep, point);
+        }
+       break;
+       case 15:
+#line 223 "/home/michael/2geom/src/svg-path-parser.rl"
+       {
+            _closePath();
+        }
+       break;
+       case 16:
+#line 360 "/home/michael/2geom/src/svg-path-parser.rl"
+       {goto _out;}
+       break;
+#line 1566 "/home/michael/2geom/src/svg-path-parser.cpp"
+               }
+       }
+
+_again:
+       p += 1;
+       goto _resume;
+       _out: {}
+       }
+#line 370 "/home/michael/2geom/src/svg-path-parser.rl"
+
+
+    if ( cs < svg_path_first_final ) {
+        throw SVGPathParseError();
+    }
+}
+
+}
+
+void parse_svg_path(char const *str, SVGPathSink &sink)
+throw(SVGPathParseError)
+{
+    Parser parser(sink);
+    parser.parse(str);
+    sink.finish();
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/svg-path-parser.h b/src/2geom/svg-path-parser.h
new file mode 100644 (file)
index 0000000..4017df4
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * parse SVG path specifications
+ *
+ * Copyright 2007 MenTaLguY <mental@rydia.net>
+ * Copyright 2007 Aaron Spike <aaron@ekips.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef SEEN_SVG_PATH_PARSER_H
+#define SEEN_SVG_PATH_PARSER_H
+
+#include <vector>
+#include <iterator>
+#include <exception>
+#include "point.h"
+#include "svg-path.h"
+
+namespace Geom {
+
+struct SVGPathParseError : public std::exception {
+    char const *what() const throw() { return "parse error"; }
+};
+
+void parse_svg_path(char const *str, SVGPathSink &sink) throw(SVGPathParseError);
+
+inline std::vector<Path> parse_svg_path(char const *str) throw(SVGPathParseError) {
+    /*PathBuilder b;
+    parse_svg_path(str, b);
+    return b.peek();*/
+    std::vector<Path> subpaths;
+    std::back_insert_iterator<std::vector<Path> > iter(subpaths);
+    SVGPathGenerator<std::back_insert_iterator<std::vector<Path> > > generator(iter);
+    parse_svg_path(str, generator);
+    return subpaths;
+}
+
+inline std::vector<Path> read_svgd(char const * name) throw(SVGPathParseError) {
+    FILE* fi = fopen(name, "r");
+    if(fi == NULL) throw(std::runtime_error("Error opening file"));
+    char input[1024 * 10];
+    fgets(input, 1024 * 10, fi);
+    fclose(fi);
+    return parse_svg_path(input);
+}
+
+}
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/svg-path.cpp b/src/2geom/svg-path.cpp
new file mode 100644 (file)
index 0000000..141ddbc
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * callback interface for SVG path data
+ *
+ * Copyright 2007 MenTaLguY <mental@rydia.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "sbasis-to-bezier.h"
+#include "svg-path.h"
+
+namespace Geom {
+
+void output(Curve const &curve, SVGPathSink &sink) {
+    std::vector<Point> pts = sbasis_to_bezier(curve.sbasis(), 2); //TODO: use something better!
+    sink.curveTo(pts[0], pts[1], pts[2]);
+}
+
+void output(LineSegment const &curve, SVGPathSink &sink) {
+    sink.lineTo(curve[1]);
+}
+
+void output(CubicBezier const &curve, SVGPathSink &sink) {
+    sink.curveTo(curve[1], curve[2], curve[3]);
+}
+
+void output(QuadraticBezier const &curve, SVGPathSink &sink) {
+    sink.quadTo(curve[1], curve[2]);
+}
+
+void output(SVGEllipticalArc const &curve, SVGPathSink &sink) {
+    // FIXME
+}
+
+template <typename T>
+bool output_as(Curve const &curve, SVGPathSink &sink) {
+    T const *t = dynamic_cast<T const *>(&curve);
+    if (t) {
+        output(*t, sink);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void output_svg_path(Path &path, SVGPathSink &sink) {
+    sink.moveTo(path.front().initialPoint());
+
+    Path::iterator iter;
+    for ( iter = path.begin() ; iter != path.end() ; ++iter ) {
+        output_as<LineSegment>(*iter, sink) ||
+        output_as<CubicBezier>(*iter, sink) ||
+        output_as<QuadraticBezier>(*iter, sink) ||
+        output_as<SVGEllipticalArc>(*iter, sink) ||
+        output_as<Curve>(*iter, sink);
+    }
+
+    if (path.closed()) {
+        sink.closePath();
+    }
+    sink.finish();
+}
+
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/svg-path.h b/src/2geom/svg-path.h
new file mode 100644 (file)
index 0000000..d090022
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * callback interface for SVG path data
+ *
+ * Copyright 2007 MenTaLguY <mental@rydia.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef SEEN_SVG_PATH_H
+#define SEEN_SVG_PATH_H
+
+#include "path.h"
+#include <iterator>
+
+namespace Geom {
+
+class SVGPathSink {
+public:
+    virtual void moveTo(Point p) = 0;
+    virtual void lineTo(Point p) = 0;
+    virtual void curveTo(Point c0, Point c1, Point p) = 0;
+    virtual void quadTo(Point c, Point p) = 0;
+    virtual void arcTo(double rx, double ry, double angle,
+                       bool large_arc, bool sweep, Point p) = 0;
+    virtual void closePath() = 0;
+    virtual void finish() = 0;
+};
+
+void output_svg_path(Path &path, SVGPathSink &sink);
+
+template <typename OutputIterator>
+class SVGPathGenerator : public SVGPathSink {
+public:
+    explicit SVGPathGenerator(OutputIterator out)
+    : _in_path(false), _out(out) {}
+
+    void moveTo(Point p) {
+        finish();
+        _path.start(p);
+        _in_path = true;
+    }
+//TODO: what if _in_path = false?
+    void lineTo(Point p) {
+        _path.appendNew<LineSegment>(p);
+    }
+
+    void curveTo(Point c0, Point c1, Point p) {
+        _path.appendNew<CubicBezier>(c0, c1, p);
+    }
+
+    void quadTo(Point c, Point p) {
+        _path.appendNew<QuadraticBezier>(c, p);
+    }
+
+    void arcTo(double rx, double ry, double angle,
+               bool large_arc, bool sweep, Point p)
+    {
+        _path.appendNew<SVGEllipticalArc>(rx, ry, angle,
+                                                 large_arc, sweep, p);
+    }
+
+    void closePath() {
+        _path.close();
+        finish();
+    }
+
+    void finish() {
+        if (_in_path) {
+            _in_path = false;
+            *_out = _path;
+            _path.clear();
+            _path.close(false);
+        }
+    }
+
+protected:
+    bool _in_path;
+    OutputIterator _out;
+    Path _path;
+};
+
+typedef std::back_insert_iterator<std::vector<Path> > iter;
+
+class PathBuilder : public SVGPathGenerator<iter> {
+private:
+    std::vector<Path> _pathset;
+public:
+    PathBuilder() : SVGPathGenerator<iter>(iter(_pathset)) {}
+    std::vector<Path> const &peek() const {return _pathset;}
+};
+
+}
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/transforms.cpp b/src/2geom/transforms.cpp
new file mode 100644 (file)
index 0000000..8c01643
--- /dev/null
@@ -0,0 +1,48 @@
+#include "transforms.h"
+
+namespace Geom {
+
+Matrix operator*(Translate const &t, Scale const &s) {
+    Matrix ret(s);
+    ret[4] = t[X] * s[X];
+    ret[5] = t[Y] * s[Y];
+    return ret;
+}
+
+Matrix operator*(Translate const &t, Rotate const &r) {
+    Matrix ret(r);
+    ret.setTranslation(t.vec * ret);
+    return ret;
+}
+
+Matrix operator*(Scale const &s, Translate const &t) {
+    return Matrix(s[0], 0,
+                  0   , s[1],
+                  t[0], t[1]);
+}
+
+Matrix operator*(Scale const &s, Matrix const &m) {
+    Matrix ret(m);
+    ret[0] *= s[X];
+    ret[1] *= s[X];
+    ret[2] *= s[Y];
+    ret[3] *= s[Y];
+    return ret;
+}
+
+Matrix operator*(Matrix const &m, Translate const &t) {
+    Matrix ret(m);
+    ret[4] += t[X];
+    ret[5] += t[Y];
+    return ret;
+}
+
+Matrix operator*(Matrix const &m, Scale const &s) {
+    Matrix ret(m);
+    ret[0] *= s[X]; ret[1] *= s[Y];
+    ret[2] *= s[X]; ret[3] *= s[Y];
+    ret[4] *= s[X]; ret[5] *= s[Y];
+    return ret;
+}
+
+}
diff --git a/src/2geom/transforms.h b/src/2geom/transforms.h
new file mode 100644 (file)
index 0000000..1cd1d3e
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef SEEN_Geom_TRANSFORMS_H
+#define SEEN_Geom_TRANSFORMS_H
+
+#include "matrix.h"
+#include <cmath>
+
+namespace Geom {
+
+template <typename T>
+struct TransformConcept {
+    T t;
+    Matrix m;
+    Point p;
+    void constraints() {
+        m = t;  //implicit conversion
+        t = t.inverse();
+        p = p * t;
+        t = t * t;
+    }
+};
+
+
+class Rotate;
+class Translate {
+  private:
+    Translate();
+    Point vec;
+  public:
+    explicit Translate(Point const &p) : vec(p) {}
+    explicit Translate(Coord const x, Coord const y) : vec(x, y) {}
+    inline operator Matrix() const { return Matrix(0, 0, 0, 0, vec[X], vec[Y]); }
+
+    inline Coord operator[](Dim2 const dim) const { return vec[dim]; }
+    inline Coord operator[](unsigned const dim) const { return vec[dim]; }
+    inline bool operator==(Translate const &o) const { return vec == o.vec; }
+    inline bool operator!=(Translate const &o) const { return vec != o.vec; }
+
+    inline Translate inverse() const { return Translate(-vec); }
+
+    friend Point operator*(Point const &v, Translate const &t);
+    inline Translate operator*(Translate const &b) const { return Translate(vec + b.vec); }
+    
+    friend Matrix operator*(Translate const &t, Rotate const &r);
+};
+
+inline Point operator*(Point const &v, Translate const &t) { return v + t.vec; }
+
+class Scale {
+  private:
+    Point vec;
+    Scale();
+  public:
+    explicit Scale(Point const &p) : vec(p) {}
+    Scale(Coord const x, Coord const y) : vec(x, y) {}
+    explicit Scale(Coord const s) : vec(s, s) {}
+    inline operator Matrix() const { return Matrix(vec[X], 0, 0, vec[Y], 0, 0); }
+
+    inline Coord operator[](Dim2 const d) const { return vec[d]; }
+    inline Coord operator[](unsigned const d) const { return vec[d]; }
+    //TODO: should we keep these mutators? add them to the other transforms?
+    inline Coord &operator[](Dim2 const d) { return vec[d]; }
+    inline Coord &operator[](unsigned const d) { return vec[d]; }
+    inline bool operator==(Scale const &o) const { return vec == o.vec; }
+    inline bool operator!=(Scale const &o) const { return vec != o.vec; }
+
+    inline Scale inverse() const { return Scale(1./vec[0], 1./vec[1]); }
+
+    friend Point operator*(Point const &v, Translate const &t);
+    inline Scale operator*(Scale const &b) const { return Scale(vec[X]*b[X], vec[Y]*b[Y]); }
+};
+
+inline Point operator*(Point const &p, Scale const &s) { return Point(p[X] * s[X], p[Y] * s[Y]); }
+
+/** Notionally an Geom::Matrix corresponding to rotation about the origin.
+    Behaves like Geom::Matrix for multiplication.
+**/
+class Rotate {
+  private:
+    Rotate();
+    Point vec;
+  public:    
+    explicit Rotate(Coord theta) : vec(std::cos(theta), std::sin(theta)) {}
+    Rotate(Point const &p) {Point v = p; v.normalize(); vec = v;} //TODO: UGLY!
+    explicit Rotate(Coord x, Coord y) { Rotate(Point(x, y)); }
+    inline operator Matrix() const { return Matrix(vec[X], vec[Y], vec[Y], -vec[X], 0, 0); }
+
+    inline Coord operator[](Dim2 const dim) const { return vec[dim]; }
+    inline Coord operator[](unsigned const dim) const { return vec[dim]; }
+    inline bool operator==(Rotate const &o) const { return vec == o.vec; }
+    inline bool operator!=(Rotate const &o) const { return vec != o.vec; }
+
+    Rotate inverse() const { return Rotate( Point(vec[X], -vec[Y]) ); }
+    static Rotate from_degrees(Coord deg) {
+        Coord rad = (deg / 180.0) * M_PI;
+        return Rotate(rad);
+    }
+
+    friend Point operator*(Point const &v, Rotate const &r);
+    inline Rotate operator*(Rotate const &b) const { return Rotate(vec * b); }
+};
+
+inline Point operator*(Point const &v, Rotate const &r) { return v ^ r.vec; }
+
+Matrix operator*(Translate const &t, Scale const &s);
+Matrix operator*(Translate const &t, Rotate const &r);
+
+Matrix operator*(Scale const &s, Translate const &t);
+Matrix operator*(Scale const &s, Matrix const &m);
+
+Matrix operator*(Matrix const &m, Translate const &t);
+Matrix operator*(Matrix const &m, Scale const &s);
+Matrix operator*(Matrix const &m, Rotate const &r);
+Matrix operator*(Matrix const &m1, Matrix const &m2);
+
+//TODO: matrix to trans/scale/rotate
+
+} /* namespace Geom */
+
+
+#endif /* !SEEN_Geom_TRANSFORMS_H */
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/utils.h b/src/2geom/utils.h
new file mode 100644 (file)
index 0000000..a2f906f
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef MATH_UTILS_HEADER
+#define MATH_UTILS_HEADER
+
+/** Various utility functions.
+ *
+ * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include <cmath>
+#include <stdexcept>
+
+class NotImplemented : public std::logic_error {
+public:
+  NotImplemented() : std::logic_error("method not implemented") {}
+};
+
+/** Sign function - indicates the sign of a numeric type.  -1 indicates negative, 1 indicates
+ *  positive, and 0 indicates, well, 0.  Mathsy people will know this is basically the derivative
+ *  of abs, except for the fact that it is defined on 0.
+ */
+template <class T> inline int sgn(const T& x) {return (x < 0 ? -1 : (x > 0 ? 1 : 0) );}
+
+template <class T> inline T sqr(const T& x) {return x * x;}
+template <class T> inline T cube(const T& x) {return x * x * x;}
+
+/** Between function - returns true if a number x is within a range. The values delimiting the
+ *  range and the number must have the same type.
+ */
+template <class T> inline const T& between (const T& min, const T& max, const T& x)
+    { return min < x && max > x; }
+
+/** Returns x rounded to the nearest integer.  It is unspecified what happens
+ *  if x is half way between two integers: we may in future use rint/round
+ *  on platforms that have them.
+ */
+inline double round(double const x) { return std::floor(x + .5); }
+
+/** Returns x rounded to the nearest \a places decimal places.
+
+    Implemented in terms of round, i.e. we make no guarantees as to what happens if x is
+    half way between two rounded numbers.
+    
+    Note: places is the number of decimal places without using scientific (e) notation, not the
+    number of significant figures.  This function may not be suitable for values of x whose
+    magnitude is so far from 1 that one would want to use scientific (e) notation.
+
+    places may be negative: e.g. places = -2 means rounding to a multiple of .01
+**/
+inline double decimal_round(double const x, int const places) {
+    //TODO: possibly implement with modulus instead?
+    double const multiplier = std::pow(10.0, places);
+    return round( x * multiplier ) / multiplier;
+}
+
+#endif
index 9f33ae5f4e6ce6ca8353d37e90a7f61b5bb44d89..260280a409a55e1d08e2857f0a04307b95c11418 100644 (file)
@@ -47,6 +47,8 @@ include libnr/Makefile_insert
 include libnrtype/Makefile_insert
 include libavoid/Makefile_insert
 include livarot/Makefile_insert
+include live_effects/Makefile_insert
+include live_effects/parameter/Makefile_insert
 include libvpsc/Makefile_insert
 include libcola/Makefile_insert
 include removeoverlap/Makefile_insert
@@ -65,6 +67,7 @@ include ui/view/Makefile_insert
 include ui/widget/Makefile_insert
 include util/Makefile_insert
 include trace/Makefile_insert
+include 2geom/Makefile_insert
 
 bin_PROGRAMS = inkscape inkview
 
@@ -84,6 +87,8 @@ noinst_LIBRARIES =    \
        helper/libspchelp.a     \
        io/libio.a      \
        libcroco/libcroco.a     \
+       live_effects/liblive_effects.a \
+       live_effects/parameter/liblpeparam.a \
        ui/libui.a      \
        ui/cache/libuicache.a   \
        ui/dialog/libuidialog.a \
@@ -104,6 +109,7 @@ noinst_LIBRARIES =  \
        widgets/libspwidgets.a  \
        trace/libtrace.a  \
        xml/libspxml.a  \
+       2geom/lib2geom.a        \
        libinkpost.a
 
 check_LIBRARIES =      \
@@ -143,6 +149,8 @@ EXTRA_DIST =        \
        libnrtype/makefile.in   \
        libavoid/makefile.in    \
        livarot/makefile.in     \
+       live_effects/makefile.in        \
+       live_effects/parameter/makefile.in      \
        removeoverlap/makefile.in       \
        svg/makefile.in         \
        trace/makefile.in       \
@@ -156,6 +164,7 @@ EXTRA_DIST =        \
        util/makefile.in        \
        widgets/makefile.in     \
        xml/makefile.in         \
+       2geom/makefile.in       \
        extension/internal/gnome.cpp    \
        extension/internal/gnome.h      \
        extension/internal/win32.cpp    \
index 89fa9cb1129a12107c6401a3bdd076b5b75fa51f..ebba357ef4bfa0baf800a20df76a867c2858f0db 100644 (file)
@@ -334,6 +334,8 @@ inkscape_private_libs =     \
        libnr/libnr.a           \
        libavoid/libavoid.a     \
        livarot/libvarot.a      \
+       live_effects/liblive_effects.a  \
+       live_effects/parameter/liblpeparam.a    \
        ui/view/libuiview.a     \
        ui/libui.a              \
        ui/widget/libuiwidget.a \
@@ -348,6 +350,7 @@ inkscape_private_libs =     \
        extension/script/libscript.a    \
         dom/libdom.a            \
        xml/libspxml.a          \
+       2geom/lib2geom.a        \
        util/libinkutil.a       \
        io/libio.a              \
        $(inkjar_libs)          \
index 3810cb0f7117969c03acb625948258ffbb347415..9541ff01e769a829b61c37971aa78f91bfef3620 100644 (file)
@@ -39,6 +39,7 @@ static SPStyleProp const props[] = {
     {SP_ATTR_STYLE, "style"},
     {SP_ATTR_TRANSFORM_CENTER_X, "inkscape:transform-center-x"},
     {SP_ATTR_TRANSFORM_CENTER_Y, "inkscape:transform-center-y"},
+    {SP_ATTR_INKSCAPE_PATH_EFFECT, "inkscape:path-effect"},
     /* SPAnchor */
     {SP_ATTR_XLINK_HREF, "xlink:href"},
     {SP_ATTR_XLINK_TYPE, "xlink:type"},
@@ -102,6 +103,7 @@ static SPStyleProp const props[] = {
     {SP_ATTR_Y, "y"},
     /* SPPath */
     {SP_ATTR_D, "d"},
+    {SP_ATTR_INKSCAPE_ORIGINAL_D, "inkscape:original-d"},    
     /* (Note: XML representation of connectors may change in future.) */
     {SP_ATTR_CONNECTOR_TYPE, "inkscape:connector-type"},
     {SP_ATTR_CONNECTION_START, "inkscape:connection-start"},
@@ -385,6 +387,8 @@ static SPStyleProp const props[] = {
     {SP_PROP_SYSTEM_LANGUAGE, "systemLanguage"},
     {SP_PROP_REQUIRED_FEATURES, "requiredFeatures"},
     {SP_PROP_REQUIRED_EXTENSIONS, "requiredExtensions"},
+    /* LivePathEffect */
+    {SP_PROP_PATH_EFFECT, "effect"},
 };
 
 #define n_attrs (sizeof(props) / sizeof(props[0]))
index 752500e24e86d89f2f64fe79f71fbfb201a2856d..847266ff3e0f079d07a619fde361d5d3723d6082 100644 (file)
@@ -38,7 +38,8 @@ enum SPAttributeEnum {
     SP_ATTR_CONNECTOR_AVOID,
     SP_ATTR_STYLE,
     SP_ATTR_TRANSFORM_CENTER_X,
-       SP_ATTR_TRANSFORM_CENTER_Y,
+    SP_ATTR_TRANSFORM_CENTER_Y,
+    SP_ATTR_INKSCAPE_PATH_EFFECT,
     /* SPAnchor */
     SP_ATTR_XLINK_HREF,
     SP_ATTR_XLINK_TYPE,
@@ -103,6 +104,7 @@ enum SPAttributeEnum {
     SP_ATTR_Y,
     /* SPPath */
     SP_ATTR_D,
+    SP_ATTR_INKSCAPE_ORIGINAL_D,
     SP_ATTR_CONNECTOR_TYPE,
     SP_ATTR_CONNECTION_START,
     SP_ATTR_CONNECTION_END,
@@ -386,6 +388,8 @@ enum SPAttributeEnum {
     SP_PROP_SYSTEM_LANGUAGE,
     SP_PROP_REQUIRED_FEATURES,
     SP_PROP_REQUIRED_EXTENSIONS,
+    /* LivePathEffect */
+    SP_PROP_PATH_EFFECT,
 };
 
 #endif
diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert
new file mode 100644 (file)
index 0000000..f285416
--- /dev/null
@@ -0,0 +1,27 @@
+## Makefile.am fragment sourced by src/Makefile.am.\r
+\r
+live_effects/all: live_effects/liblive_effects.a live_effects/parameter/all\r
+\r
+live_effects/clean:\r
+       rm -f live_effects/liblive_effects.a $(live_effects_liblive_effects_a_OBJECTS)\r
+\r
+live_effects_liblive_effects_a_SOURCES = \\r
+       live_effects/effect.cpp \\r
+       live_effects/effect.h   \\r
+       live_effects/lpeobject.cpp      \\r
+       live_effects/lpeobject.h        \\r
+       live_effects/lpeobject-reference.cpp    \\r
+       live_effects/lpeobject-reference.h      \\r
+       live_effects/n-art-bpath-2geom.cpp      \\r
+       live_effects/n-art-bpath-2geom.h        \\r
+       live_effects/lpe-skeletalstrokes.cpp    \\r
+       live_effects/lpe-skeletalstrokes.h      \\r
+       live_effects/lpe-gears.cpp      \\r
+       live_effects/lpe-gears.h        \\r
+       live_effects/lpe-test-doEffect-stack.cpp        \\r
+       live_effects/lpe-test-doEffect-stack.h  \\r
+       live_effects/lpe-slant.cpp      \\r
+       live_effects/lpe-slant.h\r
+\r
+\r
+\r
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
new file mode 100644 (file)
index 0000000..a0a8f3d
--- /dev/null
@@ -0,0 +1,232 @@
+#define INKSCAPE_LIVEPATHEFFECT_CPP\r
+\r
+/*\r
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include "display/display-forward.h"\r
+#include "xml/node-event-vector.h"\r
+#include "sp-object.h"\r
+#include "attributes.h"\r
+\r
+#include "desktop.h"\r
+\r
+#include "document.h"\r
+#include <glibmm/i18n.h>\r
+\r
+#include "live_effects/effect.h"\r
+#include "live_effects/lpeobject.h"\r
+#include "live_effects/parameter/parameter.h"\r
+#include <glibmm/ustring.h>\r
+#include "live_effects/n-art-bpath-2geom.h"\r
+#include "display/curve.h"\r
+#include <2geom/sbasis-to-bezier.h>\r
+#include <gtkmm.h>\r
+\r
+// include effects:\r
+#include "live_effects/lpe-skeletalstrokes.h"\r
+#include "live_effects/lpe-slant.h"\r
+#include "live_effects/lpe-test-doEffect-stack.h"\r
+#include "live_effects/lpe-gears.h"\r
+\r
+namespace Inkscape {\r
+\r
+namespace LivePathEffect {\r
+\r
+const Util::EnumData<EffectType> LPETypeData[ENDTYPE_LPE] = {\r
+    {INVALID_LPE,           _("Invalid effect"),        "invalid"},\r
+    {SKELETAL_STROKES,      _("Skeletal Strokes"),      "skeletal"},\r
+    {SLANT,                 _("Slant"),                 "slant"},\r
+    {DOEFFECTSTACK_TEST,    _("doEffect stack test"),   "doeffectstacktest"},\r
+    {GEARS,                 _("Gears"),                 "gears"}\r
+};\r
+const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, ENDTYPE_LPE);\r
+\r
+Effect*\r
+Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)\r
+{\r
+    switch (lpenr) {\r
+        case INVALID_LPE:\r
+            g_warning("LivePathEffect::Effect::New   called with invalid patheffect type");\r
+            return NULL;\r
+        case SKELETAL_STROKES:\r
+            return (Effect*) new LPESkeletalStrokes(lpeobj);\r
+        case SLANT:\r
+            return (Effect*) new LPESlant(lpeobj);\r
+        case DOEFFECTSTACK_TEST:\r
+            return (Effect*) new LPEdoEffectStackTest(lpeobj);\r
+        case GEARS:\r
+            return (Effect*) new LPEGears(lpeobj);\r
+        case ENDTYPE_LPE:\r
+            return NULL;\r
+    }\r
+\r
+    return NULL;\r
+}\r
+\r
+Effect::Effect(LivePathEffectObject *lpeobject)\r
+{\r
+    vbox = NULL;\r
+    tooltips = NULL;\r
+    lpeobj = lpeobject;\r
+}\r
+\r
+Effect::~Effect()\r
+{\r
+    if (tooltips) {\r
+        delete tooltips;\r
+    }\r
+}\r
+\r
+Glib::ustring \r
+Effect::getName()\r
+{\r
+    return Glib::ustring( LPETypeConverter.get_label(lpeobj->effecttype) );\r
+}\r
+\r
+/*\r
+ *  Here be the doEffect function chain:\r
+ */\r
+void\r
+Effect::doEffect (SPCurve * curve)\r
+{\r
+    NArtBpath *new_bpath = doEffect(SP_CURVE_BPATH(curve));\r
+\r
+    if (new_bpath) {        // FIXME, add function to SPCurve to change bpath? or a copy function?\r
+        if (curve->_bpath) {\r
+            g_free(curve->_bpath); //delete old bpath\r
+        }\r
+        curve->_bpath = new_bpath;\r
+    }\r
+}\r
+\r
+NArtBpath *\r
+Effect::doEffect (NArtBpath * path_in)\r
+{\r
+    std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);\r
+\r
+    std::vector<Geom::Path> result_pathv = doEffect(orig_pathv);\r
+\r
+    NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);\r
+\r
+    return new_bpath;\r
+}\r
+\r
+std::vector<Geom::Path>\r
+Effect::doEffect (std::vector<Geom::Path> & path_in)\r
+{\r
+    Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;\r
+    // FIXME: find standard function to convert std::vector<Geom::Path> ==> Piecewise< D2<SBasis> >\r
+    for (unsigned int i=0; i < path_in.size(); i++) {\r
+        pwd2_in.concat( path_in[i].toPwSb() );\r
+    }\r
+\r
+    Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect(pwd2_in);\r
+\r
+    std::vector<Geom::Path> path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);\r
+\r
+    return path_out;\r
+}\r
+\r
+Geom::Piecewise<Geom::D2<Geom::SBasis> >\r
+Effect::doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)\r
+{\r
+    g_warning("Effect has no doEffect implementation");\r
+    return pwd2_in;\r
+}\r
+\r
+void\r
+Effect::readallParameters(Inkscape::XML::Node * repr)\r
+{\r
+    param_map_type::iterator it = param_map.begin();\r
+    while (it != param_map.end()) {\r
+        const gchar * key = (*it).first.c_str();\r
+        const gchar * value = repr->attribute(key);\r
+        if(value) {\r
+            setParameter(repr, key, NULL, value);\r
+        }\r
+        it++;\r
+    }\r
+}\r
+\r
+void\r
+Effect::setParameter(Inkscape::XML::Node * repr, const gchar * key, const gchar * old_value, const gchar * new_value)\r
+{\r
+    Glib::ustring stringkey(key);\r
+\r
+    param_map_type::iterator it = param_map.find(stringkey);\r
+    if (it != param_map.end()) {\r
+        bool accepted = it->second->param_readSVGValue(new_value);\r
+        /* think: can this backfire and create infinite loop when started with unacceptable old_value?\r
+        if (!accepted) { // change was not accepted, so change it back.\r
+            repr->setAttribute(key, old_value);\r
+        } */\r
+    }\r
+}\r
+\r
+void\r
+Effect::registerParameter(Parameter * param)\r
+{\r
+    param_map[param->param_key] = param; // inserts or updates\r
+}\r
+\r
+Gtk::Widget *\r
+Effect::getWidget()\r
+{\r
+    if (!vbox) {\r
+        vbox = Gtk::manage( new Gtk::VBox() ); // use manage here, because after deletion of Effect object, others might still be pointing to this widget.\r
+        //if (!tooltips)\r
+            tooltips = new Gtk::Tooltips();\r
+\r
+        vbox->set_border_width(5);\r
+\r
+        param_map_type::iterator it = param_map.begin();\r
+        while (it != param_map.end()) {\r
+            Parameter * param = it->second;\r
+            Gtk::Widget * widg = param->param_getWidget();\r
+            Glib::ustring * tip = param->param_getTooltip();\r
+            if (widg) {\r
+               vbox->pack_start(*widg, true, true, 2);\r
+                if (tip != NULL) {\r
+                    tooltips->set_tip(*widg, *tip);\r
+                }\r
+            }\r
+\r
+            it++;\r
+        }\r
+    }\r
+\r
+    return dynamic_cast<Gtk::Widget *>(vbox);\r
+}\r
+\r
+\r
+Inkscape::XML::Node * \r
+Effect::getRepr()\r
+{\r
+    return SP_OBJECT_REPR(lpeobj);\r
+}\r
+\r
+SPDocument * \r
+Effect::getSPDoc()\r
+{\r
+    if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("oh crap");\r
+    return SP_OBJECT_DOCUMENT(lpeobj);\r
+}\r
+\r
+\r
+}; /* namespace LivePathEffect */\r
+\r
+}; /* namespace Inkscape */\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h
new file mode 100644 (file)
index 0000000..0ebd5d5
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_H\r
+#define INKSCAPE_LIVEPATHEFFECT_H\r
+\r
+/*\r
+ * Inkscape::LivePathEffect\r
+ *\r
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+\r
+#include "display/display-forward.h"\r
+#include <map>\r
+#include <glibmm/ustring.h>\r
+#include <2geom/path.h>\r
+#include "ui/widget/registry.h"\r
+#include "util/enums.h"\r
+\r
+#define  LPE_CONVERSION_TOLERANCE 0.01    // FIXME: find good solution for this.\r
+\r
+struct SPShape;\r
+struct SPDocument;\r
+class NArtBpath;\r
+struct LivePathEffectObject;\r
+\r
+namespace Gtk {\r
+    class Widget;\r
+    class VBox;\r
+    class Tooltips;\r
+}\r
+\r
+namespace Inkscape {\r
+\r
+namespace XML {\r
+    class Node;\r
+};\r
+\r
+namespace LivePathEffect {\r
+\r
+enum EffectType {\r
+    INVALID_LPE = 0,\r
+    SKELETAL_STROKES,\r
+    SLANT,\r
+    DOEFFECTSTACK_TEST,\r
+    GEARS,\r
+    ENDTYPE_LPE // This must be last\r
+};\r
+\r
+extern const Util::EnumData<EffectType> LPETypeData[ENDTYPE_LPE];\r
+extern const Util::EnumDataConverter<EffectType> LPETypeConverter;\r
+\r
+class Parameter;\r
+\r
+class Effect {\r
+public:\r
+    virtual ~Effect();\r
+\r
+    Glib::ustring getName();\r
+\r
+    virtual void doEffect (SPCurve * curve);\r
+\r
+    static Effect* New(EffectType lpenr, LivePathEffectObject *lpeobj);\r
+\r
+    virtual Gtk::Widget * getWidget();\r
+\r
+    Inkscape::XML::Node * getRepr();\r
+    SPDocument * getSPDoc();\r
+\r
+    void readallParameters(Inkscape::XML::Node * repr);\r
+    void setParameter(Inkscape::XML::Node * repr, const gchar * key, const gchar * old_value, const gchar * new_value);\r
+\r
+protected:\r
+    Effect(LivePathEffectObject *lpeobject);\r
+\r
+    // provide a set of doEffect functions so the developer has a choice \r
+    // of what kind of input/output parameters he desires.\r
+    // the order in which they appear is the order in which they are \r
+    // called by this base class. (i.e. doEffect(SPCurve * curve) defaults to calling\r
+    // doEffect(std::vector<Geom::Path> )\r
+    virtual NArtBpath *\r
+            doEffect (NArtBpath * path_in);\r
+    virtual std::vector<Geom::Path> \r
+            doEffect (std::vector<Geom::Path> & path_in);\r
+    virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > \r
+            doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in);\r
+\r
+    void registerParameter(Parameter * param);\r
+\r
+    typedef std::map<Glib::ustring, Parameter *> param_map_type;\r
+    param_map_type param_map;\r
+\r
+    Inkscape::UI::Widget::Registry wr; \r
+    Gtk::VBox * vbox;\r
+    Gtk::Tooltips * tooltips;\r
+\r
+    LivePathEffectObject *lpeobj;\r
+\r
+private:\r
+    Effect(const Effect&);\r
+    Effect& operator=(const Effect&);\r
+};\r
+\r
+\r
+}; //namespace LivePathEffect\r
+}; //namespace Inkscape\r
+\r
+#endif\r
diff --git a/src/live_effects/lpe-gears.cpp b/src/live_effects/lpe-gears.cpp
new file mode 100644 (file)
index 0000000..8da819e
--- /dev/null
@@ -0,0 +1,266 @@
+#define INKSCAPE_LPE_DOEFFECT_STACK_CPP\r
+\r
+/*\r
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include "live_effects/lpe-gears.h"\r
+\r
+#include <vector>\r
+#include <2geom/d2.h>\r
+#include <2geom/sbasis.h>\r
+#include <2geom/bezier-to-sbasis.h>\r
+#include <2geom/path.h>\r
+\r
+using std::vector;\r
+using namespace Geom;\r
+\r
+class Gear {\r
+public:\r
+    // pitch circles touch on two properly meshed gears\r
+    // all measurements are taken from the pitch circle\r
+    double pitch_diameter() {return (_number_of_teeth * _module) / M_PI;}\r
+    double pitch_radius() {return pitch_diameter() / 2.0;}\r
+    void pitch_radius(double R) {_module = (2 * M_PI * R) / _number_of_teeth;}\r
+    \r
+    // base circle serves as the basis for the involute toothe profile\r
+    double base_diameter() {return pitch_diameter() * cos(_pressure_angle);}\r
+    double base_radius() {return base_diameter() / 2.0;}\r
+    \r
+    // diametrical pitch\r
+    double diametrical_pitch() {return _number_of_teeth / pitch_diameter();}\r
+    \r
+    // height of the tooth above the pitch circle\r
+    double addendum() {return 1.0 / diametrical_pitch();}\r
+    // depth of the tooth below the pitch circle\r
+    double dedendum() {return addendum() + _clearance;}\r
+    \r
+    // root circle specifies the bottom of the fillet between teeth\r
+    double root_radius() {return pitch_radius() - dedendum();}\r
+    double root_diameter() {return root_radius() * 2.0;}\r
+    \r
+    // outer circle is the outside diameter of the gear\r
+    double outer_radius() {return pitch_radius() + addendum();}\r
+    double outer_diameter() {return outer_radius() * 2.0;}\r
+    \r
+    // angle covered by the tooth on the pitch circle\r
+    double tooth_thickness_angle() {return M_PI / _number_of_teeth;}\r
+    \r
+    Geom::Point centre() {return _centre;}\r
+    void centre(Geom::Point c) {_centre = c;}\r
+    \r
+    double angle() {return _angle;}\r
+    void angle(double a) {_angle = a;}\r
+    \r
+    int number_of_teeth() {return _number_of_teeth;}\r
+    \r
+    Geom::Path path();\r
+    Gear spawn(Geom::Point p);\r
+    \r
+    Gear(int n, double m, double phi) {\r
+        _number_of_teeth = n;\r
+        _module = m;\r
+        _pressure_angle = phi;\r
+        _clearance = 0.0;\r
+        _angle = 0.0;\r
+        _centre = Geom::Point(0.0,0.0);\r
+    }\r
+private:\r
+    int _number_of_teeth;\r
+    double _pressure_angle;\r
+    double _module;\r
+    double _clearance;\r
+    double _angle;\r
+    Geom::Point _centre;\r
+    D2<SBasis> _involute(double start, double stop) {\r
+        D2<SBasis> B;\r
+        D2<SBasis> I;\r
+        Linear bo = Linear(start,stop);\r
+        \r
+        B[0] = cos(bo,2);\r
+        B[1] = sin(bo,2);\r
+        \r
+        I = B - Linear(0,1) * derivative(B);\r
+        I = I*base_radius() + _centre;\r
+        return I;\r
+    }\r
+    D2<SBasis> _arc(double start, double stop, double R) {\r
+        D2<SBasis> B;\r
+        Linear bo = Linear(start,stop);\r
+        \r
+        B[0] = cos(bo,2);\r
+        B[1] = sin(bo,2);\r
+        \r
+        B = B*R + _centre;\r
+        return B;\r
+    }\r
+    // angle of the base circle used to create the involute to a certain radius\r
+    double involute_swath_angle(double R) {\r
+        if (R <= base_radius()) return 0.0;\r
+        return sqrt(R*R - base_radius()*base_radius())/base_radius();\r
+    }\r
+\r
+    // angle of the base circle between the origin of the involute and the intersection on another radius\r
+    double involute_intersect_angle(double R) {\r
+        if (R <= base_radius()) return 0.0;\r
+        return (sqrt(R*R - base_radius()*base_radius())/base_radius()) - acos(base_radius()/R);\r
+    }\r
+};\r
+\r
+void makeContinuous(D2<SBasis> &a, Point const b) {\r
+    for(unsigned d=0;d<2;d++)\r
+        a[d][0][0] = b[d];\r
+}\r
+\r
+Geom::Path Gear::path() {\r
+    Geom::Path pb;\r
+    \r
+    // angle covered by a full tooth and fillet\r
+    double tooth_rotation = 2.0 * tooth_thickness_angle();\r
+    // angle covered by an involute\r
+    double involute_advance = involute_intersect_angle(outer_radius()) - involute_intersect_angle(root_radius());\r
+    // angle covered by the tooth tip\r
+    double tip_advance = tooth_thickness_angle() - (2 * (involute_intersect_angle(outer_radius()) - involute_intersect_angle(pitch_radius())));\r
+    // angle covered by the toothe root\r
+    double root_advance = (tooth_rotation - tip_advance) - (2.0 * involute_advance);\r
+    // begin drawing the involute at t if the root circle is larger than the base circle\r
+    double involute_t = involute_swath_angle(root_radius())/involute_swath_angle(outer_radius());\r
+    \r
+    //rewind angle to start drawing from the leading edge of the tooth\r
+    double first_tooth_angle = _angle - ((0.5 * tip_advance) + involute_advance);\r
+    \r
+    Geom::Point prev;\r
+    for (int i=0; i < _number_of_teeth; i++)\r
+    {\r
+        double cursor = first_tooth_angle + (i * tooth_rotation);\r
+\r
+        D2<SBasis> leading_I = compose(_involute(cursor, cursor + involute_swath_angle(outer_radius())), Linear(involute_t,1));\r
+        if(i != 0) makeContinuous(leading_I, prev);\r
+        pb.append(SBasisCurve(leading_I));\r
+        cursor += involute_advance;\r
+        prev = leading_I.at1();\r
+\r
+        D2<SBasis> tip = _arc(cursor, cursor+tip_advance, outer_radius());\r
+        makeContinuous(tip, prev);\r
+        pb.append(SBasisCurve(tip));\r
+        cursor += tip_advance;\r
+        prev = tip.at1();\r
+\r
+        cursor += involute_advance;\r
+        D2<SBasis> trailing_I = compose(_involute(cursor, cursor - involute_swath_angle(outer_radius())), Linear(1,involute_t));\r
+        makeContinuous(trailing_I, prev);\r
+        pb.append(SBasisCurve(trailing_I));\r
+        prev = trailing_I.at1();\r
+\r
+        if (base_radius() > root_radius()) {\r
+            Geom::Point leading_start = trailing_I.at1();\r
+            Geom::Point leading_end = (root_radius() * unit_vector(leading_start - _centre)) + _centre;\r
+            prev = leading_end;\r
+            pb.appendNew<LineSegment>(leading_end);\r
+        }\r
+        \r
+        D2<SBasis> root = _arc(cursor, cursor+root_advance, root_radius());\r
+        makeContinuous(root, prev);\r
+        pb.append(SBasisCurve(root));\r
+        cursor += root_advance;\r
+        prev = root.at1();\r
+        \r
+        if (base_radius() > root_radius()) {\r
+            Geom::Point trailing_start = root.at1();\r
+            Geom::Point trailing_end = (base_radius() * unit_vector(trailing_start - _centre)) + _centre;\r
+            pb.appendNew<LineSegment>(trailing_end);\r
+            prev = trailing_end;\r
+        }\r
+    }\r
+    \r
+    return pb;\r
+}\r
+\r
+Gear Gear::spawn(Geom::Point p) {\r
+    double radius = Geom::distance(this->centre(), p) - this->pitch_radius();\r
+    int N  = (int) floor( (radius / this->pitch_radius()) * this->number_of_teeth() );\r
+\r
+    Gear gear(N, _module, _pressure_angle);\r
+    gear.centre(p);\r
+\r
+    double a = atan2(p - this->centre());\r
+    double new_angle = 0.0;\r
+    if (gear.number_of_teeth() % 2 == 0)\r
+        new_angle -= gear.tooth_thickness_angle();\r
+    new_angle -= (_angle) * (pitch_radius() / gear.pitch_radius());\r
+    new_angle += (a) * (pitch_radius() / gear.pitch_radius());\r
+    gear.angle(new_angle + a);\r
+    return gear;\r
+}\r
+\r
+\r
+\r
+// #################################################################\r
+\r
+\r
+\r
+namespace Inkscape {\r
+namespace LivePathEffect {\r
+\r
+\r
+LPEGears::LPEGears(LivePathEffectObject *lpeobject) :\r
+    Effect(lpeobject),\r
+    teeth(_("Teeth"), _("The number of teeth"), "teeth", &wr, this, 10),\r
+    phi(_("Phi"), _("???"), "phi", &wr, this, 5)\r
+{\r
+    registerParameter( dynamic_cast<Parameter *>(&teeth) );\r
+    registerParameter( dynamic_cast<Parameter *>(&phi) );\r
+}\r
+\r
+LPEGears::~LPEGears()\r
+{\r
+\r
+}\r
+\r
+std::vector<Geom::Path>\r
+LPEGears::doEffect (std::vector<Geom::Path> & path_in)\r
+{\r
+    std::vector<Geom::Path> path_out;\r
+    Geom::Path gearpath = path_in[0];\r
+\r
+    Geom::Path::iterator it(gearpath.begin());\r
+    if ( it == gearpath.end() ) return path_out;\r
+\r
+    Gear * gear = new Gear(teeth, 200.0, phi * M_PI / 180);\r
+    Geom::Point gear_centre = (*it).finalPoint();\r
+    gear->centre(gear_centre);\r
+    gear->angle(atan2((*it).initialPoint() - gear_centre));\r
+\r
+    it++; if ( it == gearpath.end() ) return path_out;\r
+    gear->pitch_radius(Geom::distance(gear_centre, (*it).finalPoint()));\r
+\r
+    path_out.push_back( gear->path());\r
+\r
+    for (it++ ; it != gearpath.end() ; it++) {\r
+        // iterate through Geom::Curve in path_in\r
+        Gear* gearnew = new Gear(gear->spawn( (*it).finalPoint() ));\r
+        path_out.push_back( gearnew->path() );\r
+        delete gear;\r
+        gear = gearnew;\r
+    }\r
+    delete gear;\r
+\r
+    return path_out;\r
+}\r
+\r
+\r
+}; // namespace LivePathEffect\r
+}; /* namespace Inkscape */\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/lpe-gears.h b/src/live_effects/lpe-gears.h
new file mode 100644 (file)
index 0000000..81a4ef0
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef INKSCAPE_LPE_GEARS_H\r
+#define INKSCAPE_LPE_GEARS_H\r
+\r
+/*\r
+ * Inkscape::LPEGears\r
+ *\r
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+*\r
+*\r
+ */\r
+\r
+#include "live_effects/effect.h"\r
+#include "live_effects/parameter/parameter.h"\r
+\r
+namespace Inkscape {\r
+namespace LivePathEffect {\r
+\r
+class LPEGears : public Effect {\r
+public:\r
+    LPEGears(LivePathEffectObject *lpeobject);\r
+    ~LPEGears();\r
+\r
+    std::vector<Geom::Path> doEffect (std::vector<Geom::Path> & path_in);\r
+\r
+private:\r
+    RealParam teeth;\r
+    RealParam phi;\r
+\r
+    LPEGears(const LPEGears&);\r
+    LPEGears& operator=(const LPEGears&);\r
+};\r
+\r
+}; //namespace LivePathEffect\r
+}; //namespace Inkscape\r
+\r
+#endif\r
diff --git a/src/live_effects/lpe-skeletalstrokes.cpp b/src/live_effects/lpe-skeletalstrokes.cpp
new file mode 100644 (file)
index 0000000..47c73a5
--- /dev/null
@@ -0,0 +1,161 @@
+#define INKSCAPE_LPE_SKELETAL_STROKES_CPP\r
+\r
+/*\r
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include "live_effects/lpe-skeletalstrokes.h"\r
+#include "sp-shape.h"\r
+#include "display/curve.h"\r
+#include <libnr/n-art-bpath.h>\r
+#include "live_effects/n-art-bpath-2geom.h"\r
+#include "svg/svg.h"\r
+\r
+#include <2geom/sbasis.h>\r
+#include <2geom/sbasis-geometric.h>\r
+#include <2geom/bezier-to-sbasis.h>\r
+#include <2geom/sbasis-to-bezier.h>\r
+#include <2geom/d2.h>\r
+#include <2geom/piecewise.h>\r
+\r
+#include <algorithm>\r
+using std::vector;\r
+\r
+\r
+/* Theory in e-mail from J.F. Barraud\r
+Let B be the skeleton path, and P the pattern (the path to be deformed).\r
+\r
+P is a map t --> P(t) = ( x(t), y(t) ).\r
+B is a map t --> B(t) = ( a(t), b(t) ).\r
+\r
+The first step is to re-parametrize B by its arc length: this is the parametrization in which a point p on B is located by its distance s from start. One obtains a new map s --> U(s) = (a'(s),b'(s)), that still describes the same path B, but where the distance along B from start to\r
+U(s) is s itself.\r
+\r
+We also need a unit normal to the path. This can be obtained by computing a unit tangent vector, and rotate it by 90°. Call this normal vector N(s).\r
+\r
+The basic deformation associated to B is then given by:\r
+\r
+   (x,y) --> U(x)+y*N(x)\r
+\r
+(i.e. we go for distance x along the path, and then for distance y along the normal)\r
+\r
+Of course this formula needs some minor adaptations (as is it depends on the absolute position of P for instance, so a little translation is needed\r
+first) but I think we can first forget about them.\r
+*/\r
+\r
+namespace Inkscape {\r
+namespace LivePathEffect {\r
+\r
+static const Util::EnumData<SkelCopyType> SkelCopyTypeData[SSCT_END] = {\r
+    {SSCT_SINGLE,               N_("Single"),               "single"},\r
+    {SSCT_SINGLE_STRETCHED,     N_("Single, stretched"),    "single_stretched"},\r
+    {SSCT_REPEATED,             N_("Repeated"),             "repeated"},\r
+    {SSCT_REPEATED_STRETCHED,   N_("Repeated, stretched"),  "repeated_stretched"}\r
+};\r
+static const Util::EnumDataConverter<SkelCopyType> SkelCopyTypeConverter(SkelCopyTypeData, SSCT_END);\r
+\r
+LPESkeletalStrokes::LPESkeletalStrokes(LivePathEffectObject *lpeobject) :\r
+    Effect(lpeobject),\r
+    pattern(_("Pattern"), _("Pattern to put along path"), "pattern", &wr, this),\r
+    origin(_("Origin"), _("Origin of ?"), "origin", &wr, this),\r
+    copytype(_("Copytype"), _("tooltip"), "copytype", SkelCopyTypeConverter, &wr, this)\r
+{\r
+    registerParameter( dynamic_cast<Parameter *>(&origin) );\r
+    registerParameter( dynamic_cast<Parameter *>(&pattern) );\r
+    registerParameter( dynamic_cast<Parameter *>(&copytype) );\r
+\r
+    pattern.signal_path_pasted.connect(sigc::mem_fun(*this, &LPESkeletalStrokes::on_pattern_pasted));\r
+}\r
+\r
+LPESkeletalStrokes::~LPESkeletalStrokes()\r
+{\r
+\r
+}\r
+\r
+\r
+Geom::Piecewise<Geom::D2<Geom::SBasis> >\r
+LPESkeletalStrokes::doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)\r
+{\r
+    using namespace Geom;\r
+\r
+/* LOTS OF CODE COPIED FROM 2geom/src/toys/path-along-path.cpp\r
+ * All credits should go to jfb and mgsloan of lib2geom development! */\r
+\r
+    const Util::EnumData<SkelCopyType> * data = copytype.get_selected_data();\r
+    if (!data)\r
+        return pwd2_in;\r
+    SkelCopyType type = data->id;\r
+\r
+    Piecewise<D2<SBasis> > uskeleton = arc_length_parametrization(Piecewise<D2<SBasis> >(pwd2_in),2,.1);\r
+    uskeleton = remove_short_cuts(uskeleton,.01);\r
+    Piecewise<D2<SBasis> > n = rot90(derivative(uskeleton));\r
+    n = force_continuity(remove_short_cuts(n,.1));\r
+\r
+    D2<Piecewise<SBasis> > patternd2 = make_cuts_independant(pattern);\r
+    Piecewise<SBasis> x=Piecewise<SBasis>(patternd2[0]-origin[0]);\r
+    Piecewise<SBasis> y=Piecewise<SBasis>(patternd2[1]-origin[1]);\r
+    Interval pattBnds = bounds_exact(x);\r
+\r
+    int nbCopies = int(uskeleton.cuts.back()/pattBnds.extent());\r
+    double scaling = 1;\r
+\r
+    switch(type) {\r
+        case SSCT_REPEATED:\r
+            break;\r
+\r
+        case SSCT_SINGLE:\r
+            nbCopies = (nbCopies > 0) ? 1 : 0;\r
+            break;\r
+\r
+        case SSCT_SINGLE_STRETCHED:\r
+            nbCopies = 1;\r
+            scaling = uskeleton.cuts.back()/pattBnds.extent();\r
+            break;\r
+\r
+        case SSCT_REPEATED_STRETCHED:\r
+             scaling = uskeleton.cuts.back()/(((double)nbCopies)*pattBnds.extent());\r
+            break;\r
+\r
+        default:\r
+            return pwd2_in;\r
+    };\r
+\r
+    double pattWidth = pattBnds.extent() * scaling;\r
+\r
+    x-=pattBnds.min();\r
+    if (scaling != 1)\r
+        x*=scaling;\r
+\r
+    double offs = 0;\r
+    Piecewise<D2<SBasis> >output;\r
+    for (int i=0; i<nbCopies; i++){\r
+        output.concat(compose(uskeleton,x+offs)+y*compose(n,x+offs));\r
+        offs+=pattWidth;\r
+    }\r
+    return output;\r
+}\r
+\r
+void\r
+LPESkeletalStrokes::on_pattern_pasted()\r
+{\r
+    // a new pattern was pasted through the dialog.  overwrite the origin thingie to the first point of the path\r
+    origin.param_setValue(pattern.valueAt(0));\r
+}\r
+\r
+\r
+\r
+}; // namespace LivePathEffect\r
+}; /* namespace Inkscape */\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/lpe-skeletalstrokes.h b/src/live_effects/lpe-skeletalstrokes.h
new file mode 100644 (file)
index 0000000..159a82f
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef INKSCAPE_LPE_SKELETAL_STROKES_H\r
+#define INKSCAPE_LPE_SKELETAL_STROKES_H\r
+\r
+/*\r
+ * Inkscape::LPESkeletalStrokes\r
+ *\r
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include "live_effects/effect.h"\r
+#include "live_effects/parameter/path.h"\r
+#include "live_effects/parameter/point.h"\r
+#include "live_effects/parameter/enum.h"\r
+\r
+namespace Inkscape {\r
+namespace LivePathEffect {\r
+\r
+enum SkelCopyType {\r
+    SSCT_SINGLE = 0,\r
+    SSCT_SINGLE_STRETCHED,\r
+    SSCT_REPEATED,\r
+    SSCT_REPEATED_STRETCHED,\r
+    SSCT_END // This must be last\r
+};\r
+\r
+class LPESkeletalStrokes : public Effect {\r
+public:\r
+    LPESkeletalStrokes(LivePathEffectObject *lpeobject);\r
+    ~LPESkeletalStrokes();\r
+\r
+    Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in);\r
+\r
+private:\r
+    PathParam  pattern;\r
+    PointParam origin;\r
+    EnumParam<SkelCopyType> copytype;\r
+\r
+    void on_pattern_pasted();\r
+\r
+    LPESkeletalStrokes(const LPESkeletalStrokes&);\r
+    LPESkeletalStrokes& operator=(const LPESkeletalStrokes&);\r
+};\r
+\r
+}; //namespace LivePathEffect\r
+}; //namespace Inkscape\r
+\r
+#endif\r
diff --git a/src/live_effects/lpe-slant.cpp b/src/live_effects/lpe-slant.cpp
new file mode 100644 (file)
index 0000000..93107f8
--- /dev/null
@@ -0,0 +1,62 @@
+#define INKSCAPE_LPE_SLANT_CPP\r
+\r
+/*\r
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include "live_effects/lpe-slant.h"\r
+#include "sp-shape.h"\r
+#include "display/curve.h"\r
+#include <libnr/n-art-bpath.h>\r
+#include "live_effects/n-art-bpath-2geom.h"\r
+\r
+#include <2geom/path.h>\r
+#include <2geom/transforms.h>\r
+\r
+#include "svg/svg.h"\r
+\r
+namespace Inkscape {\r
+namespace LivePathEffect {\r
+\r
+LPESlant::LPESlant(LivePathEffectObject *lpeobject) :\r
+    Effect(lpeobject),\r
+    factor(_("Slant factor"), _("y = y + x*(slant factor)"), "factor", &wr, this),\r
+    center(_("Center"), _("The x-coord of this point is around which the slant will happen"), "center", &wr, this)\r
+{\r
+    registerParameter( dynamic_cast<Parameter *>(&factor) );\r
+    registerParameter( dynamic_cast<Parameter *>(&center) );\r
+}\r
+\r
+LPESlant::~LPESlant()\r
+{\r
+}\r
+\r
+void\r
+LPESlant::doEffect(SPCurve * curve)\r
+{\r
+    NArtBpath *bpath = curve->_bpath;\r
+    int i = 0;\r
+    while(bpath[i].code != NR_END) {\r
+        bpath[i].y1 += (bpath[i].x1-center[Geom::X]) * factor;\r
+        bpath[i].y2 += (bpath[i].x2-center[Geom::X]) * factor;\r
+        bpath[i].y3 += (bpath[i].x3-center[Geom::X]) * factor;\r
+        i++;\r
+    }\r
+\r
+}\r
+\r
+}; //namespace LivePathEffect\r
+}; /* namespace Inkscape */\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/lpe-slant.h b/src/live_effects/lpe-slant.h
new file mode 100644 (file)
index 0000000..95d4420
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef INKSCAPE_LPE_SLANT_H\r
+#define INKSCAPE_LPE_SLANT_H\r
+\r
+/*\r
+ * Inkscape::LPESlant\r
+ *\r
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include "live_effects/effect.h"\r
+#include "live_effects/parameter/parameter.h"\r
+#include "live_effects/parameter/point.h"\r
+#include "ui/widget/registered-widget.h"\r
+\r
+\r
+\r
+namespace Inkscape {\r
+namespace LivePathEffect {\r
+\r
+class LPESlant : public Effect {\r
+public:\r
+    LPESlant(LivePathEffectObject *lpeobject);\r
+    ~LPESlant();\r
+\r
+    void doEffect(SPCurve * curve);\r
+    \r
+private:\r
+    RealParam factor;\r
+    PointParam center;\r
+\r
+    LPESlant(const LPESlant&);\r
+    LPESlant& operator=(const LPESlant&);\r
+};\r
+\r
+}; //namespace LivePathEffect\r
+}; //namespace Inkscape\r
+\r
+#endif\r
diff --git a/src/live_effects/lpe-test-doEffect-stack.cpp b/src/live_effects/lpe-test-doEffect-stack.cpp
new file mode 100644 (file)
index 0000000..2e3d127
--- /dev/null
@@ -0,0 +1,96 @@
+#define INKSCAPE_LPE_DOEFFECT_STACK_CPP\r
+\r
+/*\r
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include "live_effects/lpe-test-doEffect-stack.h"\r
+\r
+#include <2geom/piecewise.h>\r
+#include <vector>\r
+#include <libnr/n-art-bpath.h>\r
+\r
+namespace Inkscape {\r
+namespace LivePathEffect {\r
+\r
+\r
+LPEdoEffectStackTest::LPEdoEffectStackTest(LivePathEffectObject *lpeobject) :\r
+    Effect(lpeobject),\r
+    step(_("Stack step"), _(""), "step", &wr, this)\r
+{\r
+    registerParameter( dynamic_cast<Parameter *>(&step) );\r
+}\r
+\r
+LPEdoEffectStackTest::~LPEdoEffectStackTest()\r
+{\r
+\r
+}\r
+\r
+void\r
+LPEdoEffectStackTest::doEffect (SPCurve * curve)\r
+{\r
+    if (step >= 1) {\r
+        Effect::doEffect(curve);\r
+    } else {\r
+        // return here\r
+        return;\r
+    }\r
+}\r
+\r
+NArtBpath *\r
+LPEdoEffectStackTest::doEffect (NArtBpath * path_in)\r
+{\r
+    if (step >= 2) {\r
+        return Effect::doEffect(path_in);\r
+    } else {\r
+        // return here\r
+        NArtBpath *path_out;\r
+\r
+        unsigned ret = 0;\r
+        while ( path_in[ret].code != NR_END ) {\r
+            ++ret;\r
+        }\r
+        unsigned len = ++ret;\r
+\r
+        path_out = g_new(NArtBpath, len);\r
+        memcpy(path_out, path_in, len * sizeof(NArtBpath));\r
+        return path_out;\r
+    }\r
+}\r
+\r
+std::vector<Geom::Path>\r
+LPEdoEffectStackTest::doEffect (std::vector<Geom::Path> & path_in)\r
+{\r
+    if (step >= 3) {\r
+        return Effect::doEffect(path_in);\r
+    } else {\r
+        // return here\r
+        std::vector<Geom::Path> path_out = path_in;\r
+        return path_out;\r
+    }\r
+}\r
+\r
+Geom::Piecewise<Geom::D2<Geom::SBasis> > \r
+LPEdoEffectStackTest::doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)\r
+{\r
+    Geom::Piecewise<Geom::D2<Geom::SBasis> > output = pwd2_in;\r
+\r
+    return output;\r
+}\r
+\r
+\r
+}; // namespace LivePathEffect\r
+}; /* namespace Inkscape */\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/lpe-test-doEffect-stack.h b/src/live_effects/lpe-test-doEffect-stack.h
new file mode 100644 (file)
index 0000000..57748f5
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef INKSCAPE_LPE_DOEFFECT_STACK_H\r
+#define INKSCAPE_LPE_DOEFFECT_STACK_H\r
+\r
+/*\r
+ * Inkscape::LPEdoEffectStackTest\r
+ *\r
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+*\r
+*   This effect is to test whether running up and down the doEffect stack does not change the original-d too much. \r
+*   i.e. for this effect, the output should match more or less exactly with the input.\r
+*\r
+ */\r
+\r
+#include "live_effects/effect.h"\r
+#include "live_effects/parameter/parameter.h"\r
+\r
+namespace Inkscape {\r
+namespace LivePathEffect {\r
+\r
+class LPEdoEffectStackTest : public Effect {\r
+public:\r
+    LPEdoEffectStackTest(LivePathEffectObject *lpeobject);\r
+    ~LPEdoEffectStackTest();\r
+\r
+    void                                     doEffect (SPCurve * curve);\r
+    NArtBpath *                              doEffect (NArtBpath * path_in);\r
+    std::vector<Geom::Path>                  doEffect (std::vector<Geom::Path> & path_in);\r
+    Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in);\r
+\r
+private:\r
+    RealParam step;\r
+\r
+    LPEdoEffectStackTest(const LPEdoEffectStackTest&);\r
+    LPEdoEffectStackTest& operator=(const LPEdoEffectStackTest&);\r
+};\r
+\r
+}; //namespace LivePathEffect\r
+}; //namespace Inkscape\r
+\r
+#endif\r
diff --git a/src/live_effects/lpeobject-reference.cpp b/src/live_effects/lpeobject-reference.cpp
new file mode 100644 (file)
index 0000000..a5392ef
--- /dev/null
@@ -0,0 +1,161 @@
+/*\r
+ * The reference corresponding to the inkscape:live-effect attribute\r
+ *\r
+ * Copyright (C) 2007 Johan Engelen\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information.\r
+ */\r
+\r
+#include "enums.h"\r
+#include "live_effects/lpeobject-reference.h"\r
+#include "live_effects/lpeobject.h"\r
+\r
+#include "prefs-utils.h"\r
+#include "uri.h"\r
+\r
+namespace Inkscape {\r
+\r
+namespace LivePathEffect {\r
+\r
+static void lpeobjectreference_href_changed(SPObject *old_ref, SPObject *ref, LPEObjectReference *lpeobjref);\r
+static void lpeobjectreference_delete_self(SPObject *deleted, LPEObjectReference *lpeobjref);\r
+static void lpeobjectreference_source_modified(SPObject *iSource, guint flags, LPEObjectReference *lpeobjref);\r
+\r
+LPEObjectReference::LPEObjectReference(SPObject* i_owner) : URIReference(i_owner)\r
+{\r
+    owner=i_owner;\r
+    lpeobject_href = NULL;\r
+    lpeobject_repr = NULL;\r
+    lpeobject = NULL;\r
+    _changed_connection = changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobjectreference_href_changed), this)); // listening to myself, this should be virtual instead\r
+\r
+    user_unlink = NULL;\r
+}\r
+\r
+LPEObjectReference::~LPEObjectReference(void)\r
+{\r
+    _changed_connection.disconnect(); // to do before unlinking\r
+\r
+    quit_listening();\r
+    unlink();\r
+}\r
+\r
+bool LPEObjectReference::_acceptObject(SPObject * const obj) const\r
+{\r
+    if (IS_LIVEPATHEFFECT(obj)) {\r
+        SPObject * const owner = getOwner();\r
+        /* Refuse references to us or to an ancestor. */\r
+        for ( SPObject *iter = owner ; iter ; iter = SP_OBJECT_PARENT(iter) ) {\r
+            if ( iter == obj ) {\r
+                return false;\r
+            }\r
+        }\r
+        return true;\r
+    } else {\r
+        return false;\r
+    }\r
+}\r
+\r
+void\r
+LPEObjectReference::link(char *to)\r
+{\r
+    if ( to == NULL ) {\r
+        quit_listening();\r
+        unlink();\r
+    } else {\r
+        if ( !lpeobject_href || ( strcmp(to, lpeobject_href) != 0 ) ) {\r
+            g_free(lpeobject_href);\r
+            lpeobject_href = g_strdup(to);\r
+            try {\r
+                attach(Inkscape::URI(to));\r
+            } catch (Inkscape::BadURIException &e) {\r
+                /* TODO: Proper error handling as per\r
+                 * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.\r
+                 */\r
+                g_warning("%s", e.what());\r
+                detach();\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+void\r
+LPEObjectReference::unlink(void)\r
+{\r
+    g_free(lpeobject_href);\r
+    lpeobject_href = NULL;\r
+    detach();\r
+}\r
+\r
+void\r
+LPEObjectReference::start_listening(LivePathEffectObject* to)\r
+{\r
+    if ( to == NULL ) {\r
+        return;\r
+    }\r
+    lpeobject = to;\r
+    lpeobject_repr = SP_OBJECT_REPR(to);\r
+    _delete_connection = to->connectDelete(sigc::bind(sigc::ptr_fun(&lpeobjectreference_delete_self), this));\r
+    _modified_connection = to->connectModified(sigc::bind<2>(sigc::ptr_fun(&lpeobjectreference_source_modified), this));\r
+}\r
+\r
+void\r
+LPEObjectReference::quit_listening(void)\r
+{\r
+    if ( lpeobject == NULL ) {\r
+        return;\r
+    }\r
+    _modified_connection.disconnect();\r
+    _delete_connection.disconnect();\r
+    lpeobject_repr = NULL;\r
+    lpeobject = NULL;\r
+}\r
+\r
+static void\r
+lpeobjectreference_href_changed(SPObject */*old_ref*/, SPObject */*ref*/, LPEObjectReference *lpeobjref)\r
+{\r
+    lpeobjref->quit_listening();\r
+    LivePathEffectObject *refobj = LIVEPATHEFFECT( lpeobjref->getObject() );\r
+    if ( refobj ) {\r
+        lpeobjref->start_listening(refobj);\r
+    }\r
+\r
+    lpeobjref->owner->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);\r
+}\r
+\r
+static void\r
+lpeobjectreference_delete_self(SPObject */*deleted*/, LPEObjectReference *lpeobjref)\r
+{\r
+    guint const mode = prefs_get_int_attribute("options.cloneorphans", "value", SP_CLONE_ORPHANS_UNLINK);\r
+\r
+    if (mode == SP_CLONE_ORPHANS_UNLINK) {\r
+        // leave it be. just forget about the source\r
+        lpeobjref->quit_listening();\r
+        lpeobjref->unlink();\r
+        if (lpeobjref->user_unlink)\r
+            lpeobjref->user_unlink(lpeobjref->owner);\r
+    } else if (mode == SP_CLONE_ORPHANS_DELETE) {\r
+        lpeobjref->owner->deleteObject();\r
+    }\r
+}\r
+\r
+static void\r
+lpeobjectreference_source_modified(SPObject *iSource, guint flags, LPEObjectReference *lpeobjref)\r
+{\r
+    lpeobjref->owner->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);\r
+}\r
+\r
+}; //namespace LivePathEffect\r
+\r
+}; // namespace inkscape\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/lpeobject-reference.h b/src/live_effects/lpeobject-reference.h
new file mode 100644 (file)
index 0000000..7aaa5f6
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef SEEN_LPEOBJECT_REFERENCE_H\r
+#define SEEN_LPEOBJECT_REFERENCE_H\r
+\r
+/*\r
+ * The reference corresponding to the inkscape:live-effect attribute\r
+ *\r
+ * Copyright (C) 2007 Johan Engelen\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information.\r
+ */\r
+\r
+#include <forward.h>\r
+#include <uri-references.h>\r
+#include <sigc++/sigc++.h>\r
+\r
+namespace Inkscape {\r
+namespace XML {\r
+struct Node;\r
+}\r
+}\r
+\r
+struct LivePathEffectObject;\r
+\r
+namespace Inkscape {\r
+\r
+namespace LivePathEffect {\r
+\r
+class LPEObjectReference : public Inkscape::URIReference {\r
+public:\r
+    LPEObjectReference(SPObject *owner);\r
+    ~LPEObjectReference();\r
+\r
+    SPObject       *owner;\r
+\r
+    // concerning the LPEObject that is refered to:\r
+    gchar                *lpeobject_href;\r
+    Inkscape::XML::Node  *lpeobject_repr;\r
+    LivePathEffectObject *lpeobject;\r
+\r
+    sigc::connection _modified_connection;\r
+    sigc::connection _delete_connection;\r
+    sigc::connection _changed_connection;\r
+\r
+    void            link(char* to);\r
+    void            unlink(void);\r
+    void            start_listening(LivePathEffectObject* to);\r
+    void            quit_listening(void);\r
+\r
+    void (*user_unlink) (SPObject *user);\r
+\r
+protected:\r
+    bool _acceptObject(SPObject * const obj) const;\r
+\r
+};\r
+\r
+}; //namespace LivePathEffect\r
+\r
+}; // namespace inkscape\r
+\r
+#endif /* !SEEN_LPEOBJECT_REFERENCE_H */\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/lpeobject.cpp b/src/live_effects/lpeobject.cpp
new file mode 100644 (file)
index 0000000..0ce2ce2
--- /dev/null
@@ -0,0 +1,260 @@
+#define INKSCAPE_LIVEPATHEFFECT_OBJECT_CPP\r
+\r
+/*\r
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include "xml/repr.h"\r
+#include "xml/node-event-vector.h"\r
+#include "sp-object.h"\r
+#include "attributes.h"\r
+\r
+#include "document.h"\r
+#include <glibmm/i18n.h>\r
+\r
+#include "live_effects/lpeobject.h"\r
+#include "live_effects/effect.h"\r
+\r
+//#define LIVEPATHEFFECT_VERBOSE\r
+\r
+static void livepatheffect_class_init(LivePathEffectObjectClass *klass);\r
+static void livepatheffect_init(LivePathEffectObject *stop);\r
+\r
+static void livepatheffect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);\r
+static void livepatheffect_release(SPObject *object);\r
+\r
+static void livepatheffect_set(SPObject *object, unsigned key, gchar const *value);\r
+static Inkscape::XML::Node *livepatheffect_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);\r
+\r
+static void livepatheffect_on_repr_attr_changed (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive, void * data);\r
+\r
+static SPObjectClass *livepatheffect_parent_class;\r
+/**\r
+ * Registers the LivePathEffect class with Gdk and returns its type number.\r
+ */\r
+GType\r
+livepatheffect_get_type ()\r
+{\r
+    static GType livepatheffect_type = 0;\r
+\r
+    if (!livepatheffect_type) {\r
+        GTypeInfo livepatheffect_info = {\r
+            sizeof (LivePathEffectObjectClass),\r
+            NULL, NULL,\r
+            (GClassInitFunc) livepatheffect_class_init,\r
+            NULL, NULL,\r
+            sizeof (LivePathEffectObject),\r
+            16,\r
+            (GInstanceInitFunc) livepatheffect_init,\r
+            NULL,\r
+        };\r
+        livepatheffect_type = g_type_register_static (SP_TYPE_OBJECT, "LivePathEffectObject", &livepatheffect_info, (GTypeFlags)0);\r
+    }\r
+    return livepatheffect_type;\r
+}\r
+\r
+static Inkscape::XML::NodeEventVector const livepatheffect_repr_events = {\r
+    NULL, /* child_added */\r
+    NULL, /* child_removed */\r
+    livepatheffect_on_repr_attr_changed,\r
+    NULL, /* content_changed */\r
+    NULL  /* order_changed */\r
+};\r
+\r
+\r
+/**\r
+ * Callback to initialize livepatheffect vtable.\r
+ */\r
+static void \r
+livepatheffect_class_init(LivePathEffectObjectClass *klass)\r
+{\r
+    SPObjectClass *sp_object_class = (SPObjectClass *) klass;\r
+\r
+    livepatheffect_parent_class = (SPObjectClass *) g_type_class_ref(SP_TYPE_OBJECT);\r
+\r
+    sp_object_class->build = livepatheffect_build;\r
+    sp_object_class->release = livepatheffect_release;\r
+\r
+    sp_object_class->set = livepatheffect_set;\r
+    sp_object_class->write = livepatheffect_write;\r
+}\r
+\r
+/**\r
+ * Callback to initialize livepatheffect object.\r
+ */\r
+static void\r
+livepatheffect_init(LivePathEffectObject *lpeobj)\r
+{\r
+#ifdef LIVEPATHEFFECT_VERBOSE\r
+    g_message("Init livepatheffectobject");\r
+#endif\r
+    lpeobj->effecttype = Inkscape::LivePathEffect::INVALID_LPE;\r
+    lpeobj->lpe = NULL;\r
+\r
+    lpeobj->effecttype_set = false;\r
+}\r
+\r
+/**\r
+ * Virtual build: set livepatheffect attributes from its associated XML node.\r
+ */\r
+static void \r
+livepatheffect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)\r
+{\r
+#ifdef LIVEPATHEFFECT_VERBOSE\r
+    g_message("Build livepatheffect");\r
+#endif\r
+\r
+    if (((SPObjectClass *) livepatheffect_parent_class)->build)\r
+        (* ((SPObjectClass *) livepatheffect_parent_class)->build)(object, document, repr);\r
+\r
+    sp_object_read_attr(object, "effect");\r
+\r
+    if (repr) {\r
+        repr->addListener (&livepatheffect_repr_events, object);\r
+    }\r
+\r
+    /* Register ourselves */\r
+//    sp_document_add_resource(document, "path-effect", object);\r
+}\r
+\r
+/**\r
+ * Virtual release of livepatheffect members before destruction.\r
+ */\r
+static void\r
+livepatheffect_release(SPObject *object)\r
+{\r
+    LivePathEffectObject *lpeobj = LIVEPATHEFFECT(object);\r
+\r
+#ifdef LIVEPATHEFFECT_VERBOSE\r
+    g_print("Releasing livepatheffect");\r
+#endif\r
+\r
+/*\r
+    if (SP_OBJECT_DOCUMENT(object)) {\r
+        // Unregister ourselves\r
+        sp_document_remove_resource(SP_OBJECT_DOCUMENT(object), "livepatheffect", SP_OBJECT(object));\r
+    }\r
+\r
+    if (gradient->ref) {\r
+        gradient->modified_connection.disconnect();\r
+        gradient->ref->detach();\r
+        delete gradient->ref;\r
+        gradient->ref = NULL;\r
+    }\r
+\r
+    gradient->modified_connection.~connection();\r
+\r
+*/\r
+\r
+    if (lpeobj->lpe) {\r
+        delete lpeobj->lpe;\r
+        lpeobj->lpe = NULL;\r
+    }\r
+    lpeobj->effecttype = Inkscape::LivePathEffect::INVALID_LPE;\r
+\r
+    if (((SPObjectClass *) livepatheffect_parent_class)->release)\r
+        ((SPObjectClass *) livepatheffect_parent_class)->release(object);\r
+}\r
+\r
+/**\r
+ * Virtual set: set attribute to value.\r
+ */\r
+static void\r
+livepatheffect_set(SPObject *object, unsigned key, gchar const *value)\r
+{\r
+    LivePathEffectObject *lpeobj = LIVEPATHEFFECT(object);\r
+#ifdef LIVEPATHEFFECT_VERBOSE\r
+    g_print("Set livepatheffect");\r
+#endif\r
+    switch (key) {\r
+        case SP_PROP_PATH_EFFECT:\r
+            if (lpeobj->lpe) {\r
+                delete lpeobj->lpe;\r
+                lpeobj->lpe = NULL;\r
+            }\r
+\r
+            if (value) {\r
+                lpeobj->effecttype = Inkscape::LivePathEffect::LPETypeConverter.get_id_from_key(value);\r
+                if (lpeobj->effecttype != Inkscape::LivePathEffect::INVALID_LPE) {\r
+                    lpeobj->lpe = Inkscape::LivePathEffect::Effect::New(lpeobj->effecttype, lpeobj);\r
+                    lpeobj->lpe->readallParameters(SP_OBJECT_REPR(object));\r
+                }\r
+                lpeobj->effecttype_set = true;\r
+            } else {\r
+                lpeobj->effecttype = Inkscape::LivePathEffect::INVALID_LPE;\r
+                lpeobj->effecttype_set = false;\r
+            }\r
+            object->requestModified(SP_OBJECT_MODIFIED_FLAG);\r
+            break;\r
+    }\r
+\r
+    if (((SPObjectClass *) livepatheffect_parent_class)->set) {\r
+        ((SPObjectClass *) livepatheffect_parent_class)->set(object, key, value);\r
+    }\r
+}\r
+\r
+/**\r
+ * Virtual write: write object attributes to repr.\r
+ */\r
+static Inkscape::XML::Node *\r
+livepatheffect_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)\r
+{\r
+#ifdef LIVEPATHEFFECT_VERBOSE\r
+    g_print("Write livepatheffect");\r
+#endif\r
+\r
+    LivePathEffectObject *lpeobj = LIVEPATHEFFECT(object);\r
+\r
+    if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {\r
+        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));\r
+        repr = xml_doc->createElement("inkscape:path-effect");\r
+    }\r
+\r
+    if ((flags & SP_OBJECT_WRITE_ALL) || lpeobj->effecttype_set)\r
+        repr->setAttribute("effect", Inkscape::LivePathEffect::LPETypeConverter.get_key(lpeobj->effecttype).c_str());\r
+\r
+//    lpeobj->lpe->write(repr); something like this.\r
+\r
+    if (((SPObjectClass *) livepatheffect_parent_class)->write)\r
+        (* ((SPObjectClass *) livepatheffect_parent_class)->write)(object, repr, flags);\r
+\r
+    return repr;\r
+}\r
+\r
+static void \r
+livepatheffect_on_repr_attr_changed ( Inkscape::XML::Node * repr, \r
+                                      const gchar *key, \r
+                                      const gchar *oldval, \r
+                                      const gchar *newval, \r
+                                      bool is_interactive, \r
+                                      void * data )\r
+{\r
+#ifdef LIVEPATHEFFECT_VERBOSE\r
+    g_print("livepatheffect_on_repr_attr_changed");\r
+#endif\r
+\r
+    if (!data)\r
+        return;\r
+\r
+    LivePathEffectObject *lpeobj = (LivePathEffectObject*) data;\r
+    if (!lpeobj->lpe) \r
+        return;\r
+\r
+    lpeobj->lpe->setParameter(repr, key, oldval, newval);\r
+\r
+    lpeobj->requestModified(SP_OBJECT_MODIFIED_FLAG);\r
+}\r
+\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/lpeobject.h b/src/live_effects/lpeobject.h
new file mode 100644 (file)
index 0000000..fff43cd
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_OBJECT_H\r
+#define INKSCAPE_LIVEPATHEFFECT_OBJECT_H\r
+\r
+/*\r
+ * Inkscape::LivePathEffect\r
+ *\r
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
\r
+#include "sp-object.h"\r
+#include "effect.h"\r
+\r
+#define TYPE_LIVEPATHEFFECT  (livepatheffect_get_type())\r
+#define LIVEPATHEFFECT(o)    (G_TYPE_CHECK_INSTANCE_CAST((o), TYPE_LIVEPATHEFFECT, LivePathEffectObject))\r
+#define IS_LIVEPATHEFFECT(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), TYPE_LIVEPATHEFFECT))\r
+\r
+/*\r
+namespace Inkscape {\r
+namespace LivePathEffect {\r
+    class Effect;\r
+};\r
+};\r
+*/\r
+\r
+struct LivePathEffectObject : public SPObject {\r
+    Inkscape::LivePathEffect::EffectType effecttype;  // fixme: i think this is not needed\r
+    Inkscape::LivePathEffect::Effect *lpe;\r
+\r
+    bool effecttype_set;\r
+};\r
+\r
+/// The LivePathEffect vtable.\r
+struct LivePathEffectObjectClass {\r
+    SPObjectClass parent_class;\r
+};\r
+\r
+GType livepatheffect_get_type();\r
+\r
+#endif\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
diff --git a/src/live_effects/makefile.in b/src/live_effects/makefile.in
new file mode 100644 (file)
index 0000000..3607734
--- /dev/null
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.\r
+\r
+@SET_MAKE@\r
+\r
+# Explicit so that it's the default rule.\r
+all:\r
+       cd .. && $(MAKE) live_effects/all\r
+\r
+clean %.a %.o:\r
+       cd .. && $(MAKE) live_effects/$@\r
+\r
+.PHONY: all clean\r
+\r
+OBJEXT = @OBJEXT@\r
+\r
+.SUFFIXES:\r
+.SUFFIXES: .a .$(OBJEXT)\r
diff --git a/src/live_effects/n-art-bpath-2geom.cpp b/src/live_effects/n-art-bpath-2geom.cpp
new file mode 100644 (file)
index 0000000..5f4e4c7
--- /dev/null
@@ -0,0 +1,416 @@
+#define SEEN_LIBNR_N_ART_BPATH_2GEOM_CPP\r
+\r
+/** \file\r
+ * Contains functions to convert from NArtBpath to 2geom's Path\r
+ *\r
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
\r
+\r
+#include "live_effects/n-art-bpath-2geom.h"\r
+#include "svg/svg.h"\r
+#include <glib.h>\r
+#include <2geom/path.h>\r
+#include <2geom/svg-path.h>\r
+#include <2geom/svg-path-parser.h>\r
+#include <2geom/sbasis-to-bezier.h>\r
+\r
+#define LPE_USE_2GEOM_CONVERSION\r
+\r
+//##########################################################\r
+\r
+#include <iostream>\r
+#include <sstream>\r
+#include <string>\r
+#include <boost/format.hpp>\r
+\r
+static void curve_to_svgd(std::ostream & f, Geom::Curve const* c) {\r
+    if(Geom::LineSegment const *line_segment = dynamic_cast<Geom::LineSegment const  *>(c)) {\r
+        f << boost::format("L %g,%g ") % (*line_segment)[1][0] % (*line_segment)[1][1];\r
+    }\r
+    else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const  *>(c)) {\r
+        f << boost::format("Q %g,%g %g,%g ") % (*quadratic_bezier)[1][0] % (*quadratic_bezier)[1][0]  \r
+                % (*quadratic_bezier)[2][0] % (*quadratic_bezier)[2][1];\r
+    }\r
+    else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const  *>(c)) {\r
+        f << boost::format("C %g,%g %g,%g %g,%g ") \r
+                % (*cubic_bezier)[1][0] % (*cubic_bezier)[1][1] \r
+                % (*cubic_bezier)[2][0] % (*cubic_bezier)[2][1] \r
+                % (*cubic_bezier)[3][0] % (*cubic_bezier)[3][1];\r
+    }\r
+//    else if(Geom::SVGEllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::SVGEllipticalArc *>(c)) {\r
+//        //get at the innards and spit them out as svgd\r
+//    }\r
+    else { \r
+        //this case handles sbasis as well as all other curve types\r
+        Geom::Path sbasis_path;\r
+        path_from_sbasis(sbasis_path, c->sbasis(), 0.1);\r
+\r
+        //recurse to convert the new path resulting from the sbasis to svgd\r
+        for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {\r
+            curve_to_svgd(f, &(*iter));\r
+        }\r
+    }\r
+}\r
+\r
+static void write_svgd(std::ostream & f, Geom::Path const &p) {\r
+    if(f == NULL) {\r
+        f << "ERRRRRRORRRRR";\r
+        return;\r
+    }\r
+\r
+    f << boost::format("M %g,%g ") % p.initialPoint()[0] % p.initialPoint()[1];\r
+    \r
+    for(Geom::Path::const_iterator iter(p.begin()), end(p.end()); iter != end; ++iter) {\r
+        curve_to_svgd(f, &(*iter));\r
+    }\r
+    if(p.closed())\r
+        f << "Z ";\r
+}\r
+\r
+static void write_svgd(std::ostream & f, std::vector<Geom::Path> const &p) {\r
+    std::vector<Geom::Path>::const_iterator it(p.begin());\r
+    for(; it != p.end(); it++) {\r
+        write_svgd(f, *it);\r
+    }\r
+}\r
+\r
+//##########################################################\r
+#ifndef LPE_USE_2GEOM_CONVERSION\r
+\r
+static\r
+Geom::Point point(double *nums, int ix) {\r
+    return Geom::Point(nums[ix], nums[ix + 1]);\r
+}\r
+\r
+using namespace Geom;\r
+\r
+class OldPathBuilder {\r
+public:\r
+    OldPathBuilder(double const &c = Geom_EPSILON) : _current_path(NULL) {\r
+        _continuity_tollerance = c;\r
+    }\r
+\r
+    void startPathRel(Point const &p0) { startPath(p0 + _current_point); }\r
+    void startPath(Point const &p0) {\r
+        _pathset.push_back(Geom::Path());\r
+        _current_path = &_pathset.back();\r
+        _initial_point = _current_point = p0;\r
+    }\r
+\r
+    void pushLineRel(Point const &p0) { pushLine(p0 + _current_point); }\r
+    void pushLine(Point const &p1) {\r
+        if (!_current_path) startPath(_current_point);\r
+        _current_path->appendNew<LineSegment>(p1);\r
+        _current_point = p1;\r
+    }\r
+\r
+    void pushLineRel(Point const &p0, Point const &p1) { pushLine(p0 + _current_point, p1 + _current_point); }\r
+    void pushLine(Point const &p0, Point const &p1) {\r
+        if(p0 != _current_point) startPath(p0);\r
+        pushLine(p1);\r
+    }\r
+\r
+    void pushHorizontalRel(Coord y) { pushHorizontal(y + _current_point[1]); }\r
+    void pushHorizontal(Coord y) {\r
+        if (!_current_path) startPath(_current_point);\r
+        pushLine(Point(_current_point[0], y));\r
+    }\r
+\r
+    void pushVerticalRel(Coord x) { pushVertical(x + _current_point[0]); }\r
+    void pushVertical(Coord x) {\r
+        if (!_current_path) startPath(_current_point);\r
+        pushLine(Point(x, _current_point[1]));\r
+    }\r
+\r
+    void pushQuadraticRel(Point const &p1, Point const &p2) { pushQuadratic(p1 + _current_point, p2 + _current_point); }\r
+    void pushQuadratic(Point const &p1, Point const &p2) {\r
+        if (!_current_path) startPath(_current_point);\r
+        _current_path->appendNew<QuadraticBezier>(p1, p2);\r
+        _current_point = p2;\r
+    }\r
+\r
+    void pushQuadraticRel(Point const &p0, Point const &p1, Point const &p2) {\r
+        pushQuadratic(p0 + _current_point, p1 + _current_point, p2 + _current_point);\r
+    }\r
+    void pushQuadratic(Point const &p0, Point const &p1, Point const &p2) {\r
+        if(p0 != _current_point) startPath(p0);\r
+        pushQuadratic(p1, p2);\r
+    }\r
+\r
+    void pushCubicRel(Point const &p1, Point const &p2, Point const &p3) {\r
+        pushCubic(p1 + _current_point, p2 + _current_point, p3 + _current_point);\r
+    }\r
+    void pushCubic(Point const &p1, Point const &p2, Point const &p3) {\r
+        if (!_current_path) startPath(_current_point);\r
+        _current_path->appendNew<CubicBezier>(p1, p2, p3);\r
+        _current_point = p3;\r
+    }\r
+\r
+    void pushCubicRel(Point const &p0, Point const &p1, Point const &p2, Point const &p3) {\r
+        pushCubic(p0 + _current_point, p1 + _current_point, p2 + _current_point, p3 + _current_point);\r
+    }\r
+    void pushCubic(Point const &p0, Point const &p1, Point const &p2, Point const &p3) {\r
+        if(p0 != _current_point) startPath(p0);\r
+        pushCubic(p1, p2, p3);\r
+    }\r
+/*\r
+    void pushEllipseRel(Point const &radii, double rotation, bool large, bool sweep, Point const &end) {\r
+        pushEllipse(radii, rotation, large, sweep, end + _current_point);\r
+    }\r
+    void pushEllipse(Point const &radii, double rotation, bool large, bool sweep, Point const &end) {\r
+        if (!_current_path) startPath(_current_point);\r
+        _current_path->append(SVGEllipticalArc(_current_point, radii[0], radii[1], rotation, large, sweep, end));\r
+        _current_point = end;\r
+    }\r
+\r
+    void pushEllipseRel(Point const &initial, Point const &radii, double rotation, bool large, bool sweep, Point const &end) {\r
+        pushEllipse(initial + _current_point, radii, rotation, large, sweep, end + _current_point);\r
+    }\r
+    void pushEllipse(Point const &initial, Point const &radii, double rotation, bool large, bool sweep, Point const &end) {\r
+        if(initial != _current_point) startPath(initial);\r
+        pushEllipse(radii, rotation, large, sweep, end);\r
+    }*/\r
+    \r
+    void pushSBasis(SBasisCurve &sb) {\r
+        pushSBasis(sb.sbasis());\r
+    }\r
+    void pushSBasis(D2<SBasis> sb) {\r
+        Point initial = Point(sb[X][0][0], sb[Y][0][0]);\r
+        if (!_current_path) startPath(_current_point);\r
+        if (distance(initial, _current_point) > _continuity_tollerance) {\r
+            startPath(initial);\r
+        } else if (_current_point != initial) {\r
+            /* in this case there are three possible options\r
+               1. connect the points with tiny line segments\r
+                  this may well translate into bug reports from\r
+                  users claiming "duplicate or extraneous nodes"\r
+               2. fudge the initial point of the multidimsb\r
+                  we've chosen to do this here but question the \r
+                  numerical stability of this decision\r
+               3. translate the whole sbasis so that initial is coincident\r
+                  with _current_point. this could very well lead\r
+                  to an accumulation of error for paths that expect \r
+                  to meet in the end.\r
+               perhaps someday an option could be made to allow \r
+               the user to choose between these alternatives\r
+               if the need arises\r
+            */\r
+            sb[X][0][0] = _current_point[X];\r
+            sb[Y][0][0] = _current_point[Y]; \r
+        }\r
+        _current_path->append(sb);\r
+    }\r
+    \r
+    void closePath() {\r
+        if (_current_path) {\r
+            _current_path->close(true);\r
+            _current_path = NULL;\r
+        }\r
+        _current_point = _initial_point = Point();\r
+    }\r
+\r
+    std::vector<Path> const &peek() const { return _pathset; }\r
+\r
+private:\r
+    std::vector<Path> _pathset;\r
+    Path *_current_path;\r
+    Point _current_point;\r
+    Point _initial_point;\r
+    double _continuity_tollerance;\r
+};\r
+\r
+static\r
+std::vector<Geom::Path>\r
+read_svgd(std::istringstream & s) {\r
+    assert(s);\r
+\r
+    OldPathBuilder builder;\r
+\r
+    char mode = 0;\r
+\r
+    double nums[7];\r
+    int cur = 0;\r
+    while(!s.eof()) {\r
+        char ch;\r
+        s >> ch;\r
+        if((ch >= 'A' and ch <= 'Z') or (ch >= 'a' and ch <= 'z')) {\r
+            mode = ch;\r
+            cur = 0;\r
+        } else if (ch == ' ' or ch == '\t' or ch == '\n' or ch == '\r' or ch == ',')\r
+            continue;\r
+        else if ((ch >= '0' and ch <= '9') or ch == '-' or ch == '.' or ch == '+') {\r
+            s.unget();\r
+            //TODO: use something else, perhaps.  Unless the svg path number spec matches scan.\r
+            s >> nums[cur];\r
+            cur++;\r
+        }\r
+        \r
+        switch(mode) {\r
+        //FIXME: "If a moveto is followed by multiple pairs of coordinates, the subsequent pairs are treated as implicit lineto commands."\r
+        case 'm':\r
+            if(cur >= 2) {\r
+                builder.startPathRel(point(nums, 0));\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'M':\r
+            if(cur >= 2) {\r
+                builder.startPath(point(nums, 0));\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'l':\r
+            if(cur >= 2) {\r
+                builder.pushLineRel(point(nums, 0));\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'L':\r
+            if(cur >= 2) {\r
+                builder.pushLine(point(nums, 0));\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'h':\r
+            if(cur >= 1) {\r
+                builder.pushHorizontalRel(nums[0]);\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'H':\r
+            if(cur >= 1) {\r
+                builder.pushHorizontal(nums[0]);\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'v':\r
+            if(cur >= 1) {\r
+                builder.pushVerticalRel(nums[0]);\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'V':\r
+            if(cur >= 1) {\r
+                builder.pushVertical(nums[0]);\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'c':\r
+            if(cur >= 6) {\r
+                builder.pushCubicRel(point(nums, 0), point(nums, 2), point(nums, 4));\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'C':\r
+            if(cur >= 6) {\r
+                builder.pushCubic(point(nums, 0), point(nums, 2), point(nums, 4));\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'q':\r
+            if(cur >= 4) {\r
+                builder.pushQuadraticRel(point(nums, 0), point(nums, 2));\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'Q':\r
+            if(cur >= 4) {\r
+                builder.pushQuadratic(point(nums, 0), point(nums, 2));\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'a':\r
+            if(cur >= 7) {\r
+                //builder.pushEllipseRel(point(nums, 0), nums[2], nums[3] > 0, nums[4] > 0, point(nums, 5));\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'A':\r
+            if(cur >= 7) {\r
+                //builder.pushEllipse(point(nums, 0), nums[2], nums[3] > 0, nums[4] > 0, point(nums, 5));\r
+                cur = 0;\r
+            }\r
+            break;\r
+        case 'z':\r
+        case 'Z':\r
+            builder.closePath();\r
+            break;\r
+        }\r
+    }\r
+    return builder.peek();\r
+}\r
+\r
+\r
+#endif \r
+//##########################################################\r
+\r
+std::vector<Geom::Path>  \r
+SVGD_to_2GeomPath (char const *svgd)\r
+{\r
+    std::vector<Geom::Path> pathv;\r
+#ifdef LPE_USE_2GEOM_CONVERSION\r
+    try {\r
+        pathv = Geom::parse_svg_path(svgd);\r
+    }\r
+    catch (std::runtime_error e) {\r
+        g_warning("SVGPathParseError: %s", e.what());\r
+    }\r
+#else\r
+    std::istringstream ss;\r
+    std::string svgd_string = svgd;\r
+    ss.str(svgd_string);\r
+    pathv = read_svgd(ss);\r
+#endif\r
+    return pathv;\r
+}\r
+\r
+\r
+std::vector<Geom::Path>\r
+BPath_to_2GeomPath(NArtBpath const * bpath)\r
+{\r
+    std::vector<Geom::Path> pathv;\r
+    char *svgpath = sp_svg_write_path(bpath);\r
+    if (!svgpath) {\r
+        g_warning("BPath_to_2GeomPath - empty path returned");\r
+        return pathv;\r
+    }\r
+    pathv = SVGD_to_2GeomPath(svgpath);\r
+    g_free(svgpath);\r
+    return pathv;\r
+}\r
+\r
+char *\r
+SVGD_from_2GeomPath(std::vector<Geom::Path> const & path)\r
+{\r
+    std::ostringstream ss;\r
+    write_svgd(ss, path);\r
+    ss.flush();\r
+    std::string str = ss.str();\r
+    char * svgd = g_strdup(str.c_str());\r
+    return svgd;\r
+}\r
+\r
+NArtBpath *\r
+BPath_from_2GeomPath(std::vector<Geom::Path> const & path)\r
+{\r
+    char * svgd = SVGD_from_2GeomPath(path);\r
+    NArtBpath *bpath = sp_svg_read_path(svgd);\r
+    g_free(svgd);\r
+    return bpath;\r
+}\r
+\r
+\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/n-art-bpath-2geom.h b/src/live_effects/n-art-bpath-2geom.h
new file mode 100644 (file)
index 0000000..52b4cb9
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef SEEN_LIBNR_N_ART_BPATH_2GEOM_H\r
+#define SEEN_LIBNR_N_ART_BPATH_2GEOM_H\r
+\r
+/** \file\r
+ * Contains functions to convert from NArtBpath to 2geom's Path\r
+ *\r
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include <vector>\r
+#include <2geom/path.h>\r
+#include <libnr/n-art-bpath.h>\r
+\r
+std::vector<Geom::Path>  SVGD_to_2GeomPath (char const *svgd);\r
+std::vector<Geom::Path>  BPath_to_2GeomPath (NArtBpath const *bpath);\r
+char *                   SVGD_from_2GeomPath(std::vector<Geom::Path> const & path);\r
+NArtBpath *              BPath_from_2GeomPath (std::vector<Geom::Path> const & path);\r
+\r
+\r
+#endif /* !SEEN_LIBNR_N_ART_BPATH_2GEOM_H */\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/parameter/Makefile_insert b/src/live_effects/parameter/Makefile_insert
new file mode 100644 (file)
index 0000000..a737da1
--- /dev/null
@@ -0,0 +1,18 @@
+## Makefile.am fragment sourced by src/Makefile.am.\r
+\r
+live_effects/parameter/all: live_effects/parameter/liblpeparam.a\r
+\r
+live_effects/parameter/clean:\r
+       rm -f live_effects/parameter/liblpeparam.a $(live_effects_parameter_liblpeparam_a_OBJECTS)\r
+\r
+live_effects_parameter_liblpeparam_a_SOURCES = \\r
+       live_effects/parameter/parameter.cpp    \\r
+       live_effects/parameter/parameter.h      \\r
+       live_effects/parameter/point.cpp        \\r
+       live_effects/parameter/point.h  \\r
+       live_effects/parameter/enum.h   \\r
+       live_effects/parameter/path.cpp \\r
+       live_effects/parameter/path.h   \r
+\r
+\r
+\r
diff --git a/src/live_effects/parameter/enum.h b/src/live_effects/parameter/enum.h
new file mode 100644 (file)
index 0000000..d70360a
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ENUM_H\r
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ENUM_H\r
+\r
+/*\r
+ * Inkscape::LivePathEffectParameters\r
+ *\r
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include <glib/gtypes.h>\r
+\r
+#include "ui/widget/registry.h"\r
+#include "ui/widget/registered-enums.h"\r
+#include <gtkmm/tooltips.h>\r
+\r
+#include "live_effects/parameter/parameter.h"\r
+#include "verbs.h"\r
+\r
+namespace Inkscape {\r
+\r
+namespace LivePathEffect {\r
+\r
+template<typename E> class EnumParam : public Parameter {\r
+public:\r
+    EnumParam(  const Glib::ustring& label,\r
+                const Glib::ustring& tip,\r
+                const Glib::ustring& key,\r
+                const Util::EnumDataConverter<E>& c,\r
+                Inkscape::UI::Widget::Registry* wr, Effect* effect)\r
+        : Parameter(label, tip, key, wr, effect)\r
+    {\r
+        regenum = NULL;\r
+        enumdataconv = &c;\r
+    };\r
+    ~EnumParam() {\r
+        if (regenum)\r
+            delete regenum;\r
+    };\r
+\r
+    Gtk::Widget * param_getWidget() {\r
+        if (!regenum) {\r
+            regenum = new Inkscape::UI::Widget::RegisteredEnum<E>();\r
+            regenum->init(param_label, param_tooltip, param_key, *enumdataconv, *param_wr, param_effect->getRepr(), param_effect->getSPDoc());\r
+            regenum->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change enum parameter"));\r
+        }\r
+        return dynamic_cast<Gtk::Widget *> (regenum->labelled);\r
+    };\r
+\r
+    bool param_readSVGValue(const gchar * strvalue) {\r
+        if (regenum)\r
+            regenum->combobox()->set_active_by_key(Glib::ustring(strvalue));\r
+        return true;\r
+    };\r
+    gchar * param_writeSVGValue() const {\r
+        if (regenum) {\r
+            gchar * str = g_strdup(regenum->combobox()->get_active_data()->key.c_str());\r
+            return str;\r
+        } else {\r
+            return NULL;\r
+        }\r
+    };\r
+\r
+    const Util::EnumData<E>* get_selected_data() {\r
+        if (regenum) {\r
+            return regenum->combobox()->get_active_data();\r
+        } else {\r
+            return NULL;\r
+        }\r
+    };\r
+\r
+private:\r
+    EnumParam(const EnumParam&);\r
+    EnumParam& operator=(const EnumParam&);\r
+\r
+    UI::Widget::RegisteredEnum<E> * regenum;\r
+\r
+    const Util::EnumDataConverter<E> * enumdataconv;\r
+};\r
+\r
+\r
+}; //namespace LivePathEffect\r
+\r
+}; //namespace Inkscape\r
+\r
+#endif\r
diff --git a/src/live_effects/parameter/makefile.in b/src/live_effects/parameter/makefile.in
new file mode 100644 (file)
index 0000000..2a6294c
--- /dev/null
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.\r
+\r
+@SET_MAKE@\r
+\r
+# Explicit so that it's the default rule.\r
+all:\r
+       cd ../.. && $(MAKE) live_effects/parameter/all\r
+\r
+clean %.a %.o:\r
+       cd ../.. && $(MAKE) live_effects/parameter/$@\r
+\r
+.PHONY: all clean\r
+\r
+OBJEXT = @OBJEXT@\r
+\r
+.SUFFIXES:\r
+.SUFFIXES: .a .$(OBJEXT)\r
diff --git a/src/live_effects/parameter/parameter.cpp b/src/live_effects/parameter/parameter.cpp
new file mode 100644 (file)
index 0000000..6806a1d
--- /dev/null
@@ -0,0 +1,105 @@
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_CPP\r
+\r
+/*\r
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include "live_effects/parameter/parameter.h"\r
+#include "live_effects/effect.h"\r
+#include "svg/svg.h"\r
+\r
+#include <gtkmm.h>\r
+#include "ui/widget/scalar.h"\r
+\r
+#include "svg/stringstream.h"\r
+\r
+#include "verbs.h"\r
+\r
+#define noLPEREALPARAM_DEBUG\r
+\r
+namespace Inkscape {\r
+\r
+namespace LivePathEffect {\r
+\r
+\r
+Parameter::Parameter( const Glib::ustring& label, const Glib::ustring& tip,\r
+                      const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,\r
+                      Effect* effect )\r
+{\r
+    param_label = label;\r
+    param_tooltip = tip;\r
+    param_key = key;\r
+    param_wr = wr;\r
+    param_effect = effect;\r
+}\r
+\r
+\r
+\r
+/*###########################################\r
+ *   REAL PARAM\r
+ */\r
+RealParam::RealParam( const Glib::ustring& label, const Glib::ustring& tip,\r
+                      const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,\r
+                      Effect* effect, gdouble initial_value)\r
+    : Parameter(label, tip, key, wr, effect)\r
+{\r
+    value = initial_value;\r
+    rsu = NULL;\r
+}\r
+\r
+RealParam::~RealParam()\r
+{\r
+    if (rsu)\r
+        delete rsu;\r
+}\r
+\r
+bool\r
+RealParam::param_readSVGValue(const gchar * strvalue)\r
+{\r
+    double newval;\r
+    unsigned int success = sp_svg_number_read_d(strvalue, &newval);\r
+    if (success == 1) {\r
+        value = newval;\r
+        return true;\r
+    }\r
+    return false;\r
+}\r
+\r
+gchar *\r
+RealParam::param_writeSVGValue() const\r
+{\r
+    Inkscape::SVGOStringStream os;\r
+    os << rsu->getS()->getValue();\r
+    gchar * str = g_strdup(os.str().c_str());\r
+    return str;\r
+}\r
+\r
+Gtk::Widget *\r
+RealParam::param_getWidget()\r
+{\r
+    if (!rsu) {\r
+        rsu = new Inkscape::UI::Widget::RegisteredScalar();\r
+        rsu->init(param_label, param_tooltip, param_key, *param_wr, param_effect->getRepr(), param_effect->getSPDoc());\r
+        rsu->setValue(value);\r
+        rsu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change scalar parameter"));\r
+    }\r
+    return dynamic_cast<Gtk::Widget *> (rsu->getS());\r
+}\r
+\r
+\r
+}; /* namespace LivePathEffect */\r
+\r
+}; /* namespace Inkscape */\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/parameter/parameter.h b/src/live_effects/parameter/parameter.h
new file mode 100644 (file)
index 0000000..327d3d1
--- /dev/null
@@ -0,0 +1,84 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_H\r
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_H\r
+\r
+/*\r
+ * Inkscape::LivePathEffectParameters\r
+ *\r
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include <glibmm/ustring.h>\r
+#include <2geom/point.h>\r
+#include <2geom/path.h>\r
+\r
+#include "ui/widget/registry.h"\r
+#include "ui/widget/registered-widget.h"\r
+\r
+namespace Gtk {\r
+    class Widget;\r
+}\r
+\r
+namespace Inkscape {\r
+\r
+namespace LivePathEffect {\r
+\r
+class Effect;\r
+\r
+class Parameter {\r
+public:\r
+    Parameter(const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, Effect* effect);\r
+    virtual ~Parameter() {};\r
+\r
+    virtual bool param_readSVGValue(const gchar * strvalue) = 0;   // returns true if new value is valid / accepted.\r
+    virtual gchar * param_writeSVGValue() const = 0;\r
+\r
+    // This returns pointer to the parameter's widget to be put in the live-effects dialog. Must also create the\r
+    // necessary widget if it does not exist yet.\r
+    virtual Gtk::Widget * param_getWidget() = 0;\r
+    virtual Glib::ustring * param_getTooltip() { return &param_tooltip; };\r
+\r
+    Glib::ustring param_key;\r
+    Inkscape::UI::Widget::Registry * param_wr;\r
+    Glib::ustring param_label;\r
+\r
+protected:\r
+    Glib::ustring param_tooltip;\r
+\r
+    Effect* param_effect;\r
+\r
+private:\r
+    Parameter(const Parameter&);\r
+    Parameter& operator=(const Parameter&);\r
+};\r
+\r
+\r
+class RealParam : public Parameter {\r
+public:\r
+    RealParam( const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key, \r
+               Inkscape::UI::Widget::Registry* wr, Effect* effect, gdouble initial_value = 1.0);\r
+    ~RealParam();\r
+\r
+    bool param_readSVGValue(const gchar * strvalue);\r
+    gchar * param_writeSVGValue() const;\r
+\r
+    Gtk::Widget * param_getWidget();\r
+\r
+    inline operator gdouble()\r
+        { return value; };\r
+\r
+private:\r
+    RealParam(const RealParam&);\r
+    RealParam& operator=(const RealParam&);\r
+\r
+    gdouble value;\r
+    Inkscape::UI::Widget::RegisteredScalar * rsu;\r
+};\r
+\r
+\r
+}; //namespace LivePathEffect\r
+\r
+}; //namespace Inkscape\r
+\r
+#endif\r
diff --git a/src/live_effects/parameter/path.cpp b/src/live_effects/parameter/path.cpp
new file mode 100644 (file)
index 0000000..90974f6
--- /dev/null
@@ -0,0 +1,166 @@
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_PATH_CPP\r
+\r
+/*\r
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include "live_effects/parameter/path.h"\r
+#include "live_effects/effect.h"\r
+#include "live_effects/n-art-bpath-2geom.h"\r
+#include "svg/svg.h"\r
+#include <2geom/svg-path-parser.h>\r
+#include <2geom/sbasis-to-bezier.h>\r
+#include <2geom/d2.h>\r
+\r
+#include "ui/widget/point.h"\r
+#include "widgets/icon.h"\r
+#include <gtk/gtkstock.h>\r
+#include "selection-chemistry.h"\r
+\r
+#include "desktop.h"\r
+#include "inkscape.h"\r
+#include "message-stack.h"\r
+#include "verbs.h"\r
+#include "document.h"\r
+\r
+#define noLPEPATHPARAM_DEBUG\r
+\r
+namespace Inkscape {\r
+\r
+namespace LivePathEffect {\r
+\r
+PathParam::PathParam( const Glib::ustring& label, const Glib::ustring& tip,\r
+                      const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,\r
+                      Effect* effect )\r
+    : Parameter(label, tip, key, wr, effect)\r
+{\r
+    _widget = NULL;\r
+    _tooltips = NULL;\r
+}\r
+\r
+PathParam::~PathParam()\r
+{\r
+    if (_tooltips)\r
+        delete _tooltips;\r
+    // _widget is managed by GTK so do not delete!\r
+}\r
+\r
+bool\r
+PathParam::param_readSVGValue(const gchar * strvalue)\r
+{\r
+    if (strvalue) {\r
+        Geom::Piecewise<Geom::D2<Geom::SBasis> > newpath;\r
+        std::vector<Geom::Path> temppath = SVGD_to_2GeomPath(strvalue);\r
+        for (unsigned int i=0; i < temppath.size(); i++) {\r
+            newpath.concat( temppath[i].toPwSb() );\r
+        }\r
+        *( dynamic_cast<Geom::Piecewise<Geom::D2<Geom::SBasis> > *> (this) ) = newpath;\r
+        return true;\r
+    }\r
+\r
+    return false;\r
+}\r
+\r
+gchar *\r
+PathParam::param_writeSVGValue() const\r
+{\r
+    const std::vector<Geom::Path> temppath =\r
+        Geom::path_from_piecewise(* dynamic_cast<const Geom::Piecewise<Geom::D2<Geom::SBasis> > *> (this), LPE_CONVERSION_TOLERANCE);\r
+    gchar * svgd = SVGD_from_2GeomPath( temppath );\r
+    return svgd;\r
+}\r
+\r
+Gtk::Widget *\r
+PathParam::param_getWidget()\r
+{\r
+    if (!_widget) {\r
+        _widget = Gtk::manage(new Gtk::HBox());\r
+        _tooltips = new Gtk::Tooltips();\r
+\r
+        Gtk::Label* pLabel = Gtk::manage(new Gtk::Label(param_label));\r
+        static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true);\r
+        _tooltips->set_tip(*pLabel, param_tooltip);\r
+\r
+        Gtk::Widget*  pIcon = Gtk::manage( sp_icon_get_icon( "draw_node", Inkscape::ICON_SIZE_BUTTON) );\r
+        Gtk::Button * pButton = Gtk::manage(new Gtk::Button());\r
+        pButton->set_relief(Gtk::RELIEF_NONE);\r
+        pIcon->show();\r
+        pButton->add(*pIcon);\r
+        pButton->show();\r
+        pButton->signal_clicked().connect(sigc::mem_fun(*this, &PathParam::on_edit_button_click));\r
+        static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);\r
+        _tooltips->set_tip(*pButton, _("Edit on-canvas"));\r
+#ifndef LPEPATHPARAM_DEBUG\r
+        pButton->set_sensitive(false);\r
+#endif\r
+\r
+        pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_PASTE, Inkscape::ICON_SIZE_BUTTON) );\r
+        pButton = Gtk::manage(new Gtk::Button());\r
+        pButton->set_relief(Gtk::RELIEF_NONE);\r
+        pIcon->show();\r
+        pButton->add(*pIcon);\r
+        pButton->show();\r
+        pButton->signal_clicked().connect(sigc::mem_fun(*this, &PathParam::on_paste_button_click));\r
+        static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);\r
+        _tooltips->set_tip(*pButton, _("Paste path"));\r
+\r
+        static_cast<Gtk::HBox*>(_widget)->show_all_children();\r
+\r
+    }\r
+    return dynamic_cast<Gtk::Widget *> (_widget);\r
+}\r
+\r
+void\r
+PathParam::on_edit_button_click()\r
+{\r
+    g_message("give this path to edit on canvas!");\r
+}\r
+\r
+void\r
+PathParam::on_paste_button_click()\r
+{\r
+    // check if something is in the clipboard\r
+    GSList * clipboard = sp_selection_get_clipboard();\r
+    if (clipboard == NULL || clipboard->data == NULL) {\r
+        SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Nothing on the clipboard."));\r
+        return;\r
+    }\r
+\r
+    Inkscape::XML::Node *repr = (Inkscape::XML::Node *) clipboard->data;\r
+    if (!strcmp (repr->name(), "svg:path")) {\r
+        const char * svgd = repr->attribute("d");\r
+        if (svgd) {\r
+            param_write_to_repr(svgd);\r
+            signal_path_pasted.emit();\r
+            sp_document_done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, \r
+                             _("Paste path parameter"));\r
+        }\r
+    } else {\r
+        SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Clipboard does not contain a path."));\r
+    }\r
+\r
+}\r
+\r
+void\r
+PathParam::param_write_to_repr(const char * svgd)\r
+{\r
+    param_effect->getRepr()->setAttribute(param_key.c_str(), svgd);\r
+}\r
+\r
+\r
+}; /* namespace LivePathEffect */\r
+\r
+}; /* namespace Inkscape */\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/parameter/path.h b/src/live_effects/parameter/path.h
new file mode 100644 (file)
index 0000000..39ea9e2
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_PATH_H\r
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_PATH_H\r
+\r
+/*\r
+ * Inkscape::LivePathEffectParameters\r
+ *\r
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include <glib/gtypes.h>\r
+#include <2geom/path.h>\r
+\r
+#include "ui/widget/registry.h"\r
+#include <gtkmm/tooltips.h>\r
+\r
+#include "live_effects/parameter/parameter.h"\r
+\r
+#include <sigc++/sigc++.h>\r
+\r
+namespace Inkscape {\r
+\r
+namespace LivePathEffect {\r
+\r
+class PathParam : public Geom::Piecewise<Geom::D2<Geom::SBasis> >, public Parameter {\r
+public:\r
+    PathParam(const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, Effect* effect);;\r
+    ~PathParam();\r
+\r
+    Gtk::Widget * param_getWidget();\r
+\r
+    bool param_readSVGValue(const gchar * strvalue);\r
+    gchar * param_writeSVGValue() const;\r
+\r
+    sigc::signal <void> signal_path_pasted;\r
+\r
+private:\r
+    PathParam(const PathParam&);\r
+    PathParam& operator=(const PathParam&);\r
+\r
+    Gtk::Widget * _widget;\r
+    Gtk::Tooltips * _tooltips;\r
+\r
+    void param_write_to_repr(const char * svgd);\r
+\r
+    void on_edit_button_click();\r
+    void on_paste_button_click();\r
+};\r
+\r
+\r
+}; //namespace LivePathEffect\r
+\r
+}; //namespace Inkscape\r
+\r
+#endif\r
diff --git a/src/live_effects/parameter/point.cpp b/src/live_effects/parameter/point.cpp
new file mode 100644 (file)
index 0000000..8079f54
--- /dev/null
@@ -0,0 +1,166 @@
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_POINT_CPP\r
+\r
+/*\r
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include "live_effects/parameter/point.h"\r
+#include "live_effects/effect.h"\r
+#include "svg/svg.h"\r
+#include "svg/stringstream.h"\r
+#include <gtkmm.h>\r
+#include "ui/widget/point.h"\r
+#include "widgets/icon.h"\r
+\r
+#include "knot.h"\r
+#include "inkscape.h"\r
+#include "verbs.h"\r
+\r
+#define noLPEPOINTPARAM_DEBUG\r
+\r
+#define PRM_KNOT_COLOR_NORMAL 0xffffff00\r
+#define PRM_KNOT_COLOR_SELECTED 0x0000ff00\r
+\r
+namespace Inkscape {\r
+\r
+namespace LivePathEffect {\r
+\r
+PointParam::PointParam( const Glib::ustring& label, const Glib::ustring& tip,\r
+                        const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,\r
+                        Effect* effect )\r
+    : Geom::Point(0,0), Parameter(label, tip, key, wr, effect)\r
+{\r
+    _widget = NULL;\r
+    pointwdg = NULL;\r
+    knot = NULL;\r
+    _tooltips = NULL;\r
+}\r
+\r
+PointParam::~PointParam()\r
+{\r
+    if (pointwdg)\r
+        delete pointwdg;\r
+    if (_tooltips)\r
+        delete _tooltips;\r
+\r
+    if (knot)\r
+        g_object_unref (G_OBJECT (knot));\r
+}\r
+\r
+bool\r
+PointParam::param_readSVGValue(const gchar * strvalue)\r
+{\r
+    gchar ** strarray = g_strsplit(strvalue, ",", 2);\r
+    double newx, newy;\r
+    unsigned int success = sp_svg_number_read_d(strarray[0], &newx);\r
+    success += sp_svg_number_read_d(strarray[1], &newy);\r
+    g_strfreev (strarray);\r
+    if (success == 2) {\r
+        *dynamic_cast<Geom::Point *>( this ) = Geom::Point(newx, newy);\r
+        return true;\r
+    }\r
+    return false;\r
+}\r
+\r
+gchar *\r
+PointParam::param_writeSVGValue() const\r
+{\r
+    Inkscape::SVGOStringStream os;\r
+    os << pointwdg->getPoint()->getXValue() << "," << pointwdg->getPoint()->getYValue();\r
+    gchar * str = g_strdup(os.str().c_str());\r
+    return str;\r
+}\r
+\r
+Gtk::Widget *\r
+PointParam::param_getWidget()\r
+{\r
+    if (!_widget) {\r
+        pointwdg = new Inkscape::UI::Widget::RegisteredPoint();\r
+        pointwdg->init(param_label, param_tooltip, param_key, *param_wr, param_effect->getRepr(), param_effect->getSPDoc());\r
+        pointwdg->setValue(0.1, 0.2);\r
+        pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change point parameter"));\r
+\r
+        Gtk::Widget*  pIcon = Gtk::manage( sp_icon_get_icon( "draw_node", Inkscape::ICON_SIZE_BUTTON) );\r
+        Gtk::Button * pButton = Gtk::manage(new Gtk::Button());\r
+        pButton->set_relief(Gtk::RELIEF_NONE);\r
+        pIcon->show();\r
+        pButton->add(*pIcon);\r
+        pButton->show();\r
+        pButton->signal_clicked().connect(sigc::mem_fun(*this, &PointParam::on_button_click));\r
+#ifndef LPEPOINTPARAM_DEBUG\r
+        pButton->set_sensitive(false);\r
+#endif\r
+\r
+        _widget = Gtk::manage( new Gtk::HBox() );\r
+        static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);\r
+        static_cast<Gtk::HBox*>(_widget)->pack_start(*(pointwdg->getPoint()), true, true);\r
+        static_cast<Gtk::HBox*>(_widget)->show_all_children();\r
+\r
+        _tooltips = new Gtk::Tooltips();\r
+        _tooltips->set_tip(*pButton, _("Edit on-canvas"));\r
+    }\r
+    return dynamic_cast<Gtk::Widget *> (_widget);\r
+}\r
+\r
+void\r
+PointParam::param_setValue(Geom::Point newpoint)\r
+{\r
+    *dynamic_cast<Geom::Point *>( this ) = newpoint;\r
+    pointwdg->setValue(newpoint[0], newpoint[1]);\r
+}\r
+\r
+\r
+// CALLBACKS:\r
+\r
+void\r
+PointParam::on_button_click()\r
+{\r
+    g_message("add knot to canvas on correct location :S");\r
+\r
+    if (!knot) {\r
+        // create the knot\r
+        knot = sp_knot_new (SP_ACTIVE_DESKTOP, NULL);\r
+        knot->setMode(SP_KNOT_MODE_XOR);\r
+        knot->setFill(PRM_KNOT_COLOR_NORMAL, PRM_KNOT_COLOR_NORMAL, PRM_KNOT_COLOR_NORMAL);\r
+        knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);\r
+        sp_knot_update_ctrl(knot);\r
+\r
+        // move knot to the given point\r
+        sp_knot_set_position (knot, &NR::Point((*this)[0], (*this)[1]), SP_KNOT_STATE_NORMAL);\r
+        sp_knot_show (knot);\r
+/*\r
+        // connect knot's signals\r
+        if ( (draggable)  // it can be NULL if a node in unsnapped (eg. focus point unsnapped from center)\r
+                           // luckily, midstops never snap to other nodes so are never unsnapped...\r
+             && ( (draggable->point_type == POINT_LG_MID)\r
+                  || (draggable->point_type == POINT_RG_MID1)\r
+                  || (draggable->point_type == POINT_RG_MID2) ) )\r
+        {\r
+            this->handler_id = g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (gr_knot_moved_midpoint_handler), this);\r
+        } else {\r
+            this->handler_id = g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (gr_knot_moved_handler), this);\r
+        }\r
+        g_signal_connect (G_OBJECT (this->knot), "clicked", G_CALLBACK (gr_knot_clicked_handler), this);\r
+        g_signal_connect (G_OBJECT (this->knot), "doubleclicked", G_CALLBACK (gr_knot_doubleclicked_handler), this);\r
+        g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (gr_knot_grabbed_handler), this);\r
+        g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (gr_knot_ungrabbed_handler), this);\r
+*/\r
+    }\r
+}\r
+\r
+}; /* namespace LivePathEffect */\r
+\r
+}; /* namespace Inkscape */\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/live_effects/parameter/point.h b/src/live_effects/parameter/point.h
new file mode 100644 (file)
index 0000000..1240ea3
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_POINT_H\r
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_POINT_H\r
+\r
+/*\r
+ * Inkscape::LivePathEffectParameters\r
+ *\r
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Released under GNU GPL, read the file 'COPYING' for more information\r
+ */\r
+\r
+#include <glib/gtypes.h>\r
+#include <2geom/point.h>\r
+\r
+#include "ui/widget/registry.h"\r
+#include "ui/widget/registered-widget.h"\r
+#include <gtkmm/tooltips.h>\r
+\r
+#include "live_effects/parameter/parameter.h"\r
+\r
+struct SPKnot;\r
+\r
+namespace Inkscape {\r
+\r
+namespace LivePathEffect {\r
+\r
+\r
+class PointParam : public Geom::Point, public Parameter {\r
+public:\r
+    PointParam(const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, Effect* effect);;\r
+    ~PointParam();\r
+\r
+    Gtk::Widget * param_getWidget();\r
+\r
+    bool param_readSVGValue(const gchar * strvalue);\r
+    gchar * param_writeSVGValue() const;\r
+\r
+    void param_setValue(Geom::Point newpoint);\r
+\r
+private:\r
+    PointParam(const PointParam&);\r
+    PointParam& operator=(const PointParam&);\r
+\r
+    Gtk::Widget * _widget;\r
+    Gtk::Tooltips * _tooltips;\r
+    Inkscape::UI::Widget::RegisteredPoint * pointwdg;\r
+    void on_button_click();\r
+\r
+    SPKnot *knot;\r
+};\r
+\r
+\r
+}; //namespace LivePathEffect\r
+\r
+}; //namespace Inkscape\r
+\r
+#endif\r
diff --git a/src/live_effects/todo.txt b/src/live_effects/todo.txt
new file mode 100644 (file)
index 0000000..87bafe4
--- /dev/null
@@ -0,0 +1,19 @@
+reminder list\r
+\r
+\r
+cleanup nodepath code that draws helper path\r
+\r
+implement effect application to shapes: sp_shape_apply_path_effect\r
+   (done: star, ellipse, spiral)\r
+ARCS !!!  see sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)\r
+\r
+make sp_nodepath_is_over_stroke perhaps\r
+\r
+Parameters:\r
+- make robust  (?)\r
+- add range checking etc\r
+- add more types! (straightlinepath, enum, bool)\r
+- -->> add write to svg functionality (for lpeobject->write)\r
+\r
+\r
+find dir "fixme" and fix'em!
\ No newline at end of file
index cdf725232df6a188979ee59c334fbcc8c033d263..422d1c80405732176363ca50e305bf99efabe9ea 100644 (file)
@@ -215,6 +215,8 @@ static char const menus_skeleton[] =
 "       <separator/>\n"
 "       <verb verb-id=\"SelectionSimplify\" />\n"
 "       <verb verb-id=\"SelectionReverse\" />\n"
+"       <separator/>\n"
+"       <verb verb-id=\"DialogLivePathEffect\" />\n"
 "   </submenu>\n"
 "   <submenu name=\"" N_("_Text") "\">\n"
 "       <verb verb-id=\"DialogText\" />\n"
index 3084cdb296ba34bcfa46472a44f9bc03dbd75373..3b7a085bef1702b1df007dbddc2cf2f55e5805ca 100644 (file)
@@ -191,111 +191,8 @@ sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEve
 {
     gint ret = FALSE;
 
-    SPDesktop *desktop = event_context->desktop;
-    Inkscape::Selection *selection = sp_desktop_selection (desktop);
-
-    SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
-
-    switch (event->type) {
-        case GDK_2BUTTON_PRESS:
-        case GDK_BUTTON_RELEASE:
-            if (event->button.button == 1 && !event_context->space_panning) {
-                if (!nc->drag) {
-
-                    // find out clicked item, disregarding groups, honoring Alt
-                    SPItem *item_clicked = sp_event_context_find_item (desktop,
-                            NR::Point(event->button.x, event->button.y),
-                            (event->button.state & GDK_MOD1_MASK) && !(event->button.state & GDK_CONTROL_MASK), TRUE);
-                    // find out if we're over the selected item, disregarding groups
-                    SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
-                                                                    NR::Point(event->button.x, event->button.y));
-
-                    bool over_stroke = false;
-                    if (item_over && nc->shape_editor->has_nodepath()) {
-                        over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), false);
-                    }
-
-                    if (over_stroke || nc->added_node) {
-                        switch (event->type) {
-                            case GDK_BUTTON_RELEASE:
-                                if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) {
-                                    //add a node
-                                    nc->shape_editor->add_node_near_point();
-                                } else {
-                                    if (nc->added_node) { // we just received double click, ignore release
-                                        nc->added_node = false;
-                                        break;
-                                    }
-                                    //select the segment
-                                    if (event->button.state & GDK_SHIFT_MASK) {
-                                        nc->shape_editor->select_segment_near_point(true);
-                                    } else {
-                                        nc->shape_editor->select_segment_near_point(false);
-                                    }
-                                    desktop->updateNow();
-                                }
-                                break;
-                            case GDK_2BUTTON_PRESS:
-                                //add a node
-                                nc->shape_editor->add_node_near_point();
-                                nc->added_node = true;
-                                break;
-                            default:
-                                break;
-                        }
-                    } else if (event->button.state & GDK_SHIFT_MASK) {
-                        selection->toggle(item_clicked);
-                        desktop->updateNow();
-                    } else {
-                        selection->set(item_clicked);
-                        desktop->updateNow();
-                    }
-
-                    ret = TRUE;
-                }
-                break;
-            }
-            break;
-        case GDK_BUTTON_PRESS:
-            if (event->button.button == 1 && !(event->button.state & GDK_SHIFT_MASK) && !event_context->space_panning) {
-                // save drag origin
-                event_context->xp = (gint) event->button.x;
-                event_context->yp = (gint) event->button.y;
-                event_context->within_tolerance = true;
-                nc->shape_editor->cancel_hit();
-
-                if (!nc->drag) {
-                    // find out if we're over the selected item, disregarding groups
-                    SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
-                                                                    NR::Point(event->button.x, event->button.y));
-
-                    if (nc->shape_editor->has_nodepath() && selection->single() && item_over) {
-
-                            // save drag origin
-                            bool over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), true);
-                            //only dragging curves
-                            if (over_stroke) {
-                                ret = TRUE;
-                            } else {
-                                break;
-                            }
-                        } else {
-                            break;
-                        }
-
-                    ret = TRUE;
-                }
-                break;
-            }
-            break;
-        default:
-            break;
-    }
-
-    if (!ret) {
-        if (((SPEventContextClass *) parent_class)->item_handler)
-            ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
-    }
+    if (((SPEventContextClass *) parent_class)->item_handler)
+        ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
 
     return ret;
 }
@@ -313,7 +210,6 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
     double const offset = prefs_get_double_attribute_limited("options.defaultscale", "value", 2, 0, 1000);
 
     gint ret = FALSE;
-
     switch (event->type) {
         case GDK_BUTTON_PRESS:
             if (event->button.button == 1 && !event_context->space_panning) {
@@ -323,6 +219,19 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 event_context->within_tolerance = true;
                 nc->shape_editor->cancel_hit();
 
+                if (!(event->button.state & GDK_SHIFT_MASK)) {
+                    if (!nc->drag) {
+                        if (nc->shape_editor->has_nodepath() && selection->single() /* && item_over */) {
+                            // save drag origin
+                            bool over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), true);
+                            //only dragging curves
+                            if (over_stroke) {
+                                ret = TRUE;
+                                break;
+                            }
+                        }
+                    }
+                }
                 NR::Point const button_w(event->button.x,
                                          event->button.y);
                 NR::Point const button_dt(desktop->w2d(button_w));
@@ -389,12 +298,8 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                     break;
                 }
 
-                SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
-                                                                NR::Point(event->motion.x, event->motion.y));
                 bool over_stroke = false;
-                if (item_over && nc->shape_editor->has_nodepath()) {
-                    over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->motion.x, event->motion.y), false);
-                }
+                over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->motion.x, event->motion.y), false);
 
                 if (nc->cursor_drag && !over_stroke) {
                     event_context->cursor_shape = cursor_node_xpm;
@@ -411,32 +316,87 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
                 }
             }
             break;
+
+        case GDK_2BUTTON_PRESS:
         case GDK_BUTTON_RELEASE:
-            event_context->xp = event_context->yp = 0;
-            if (event->button.button == 1 && !event_context->space_panning) {
+            if ( (event->button.button == 1) && (!nc->drag) && !event_context->space_panning) {
+                // find out clicked item, disregarding groups, honoring Alt
+                SPItem *item_clicked = sp_event_context_find_item (desktop,
+                        NR::Point(event->button.x, event->button.y),
+                        (event->button.state & GDK_MOD1_MASK) && !(event->button.state & GDK_CONTROL_MASK), TRUE);
 
-                NR::Maybe<NR::Rect> b = Inkscape::Rubberband::get()->getRectangle();
+                event_context->xp = event_context->yp = 0;
 
-                if (nc->shape_editor->hits_curve() && !event_context->within_tolerance) { //drag curve
-                    nc->shape_editor->finish_drag();
-                } else if (b && !event_context->within_tolerance) { // drag to select
-                    nc->shape_editor->select_rect(*b, event->button.state & GDK_SHIFT_MASK);
-                } else {
-                    if (!(nc->rb_escaped)) { // unless something was cancelled
-                        if (nc->shape_editor->has_selection())
-                            nc->shape_editor->deselect();
-                        else
-                            sp_desktop_selection(desktop)->clear();
+                bool over_stroke = false;
+                if (nc->shape_editor->has_nodepath()) {
+                    over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), false);
+                }
+
+                if (item_clicked || over_stroke) {
+                    if (over_stroke || nc->added_node) {
+                        switch (event->type) {
+                            case GDK_BUTTON_RELEASE:
+                                if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) {
+                                    //add a node
+                                    nc->shape_editor->add_node_near_point();
+                                } else {
+                                    if (nc->added_node) { // we just received double click, ignore release
+                                        nc->added_node = false;
+                                        break;
+                                    }
+                                    //select the segment
+                                    if (event->button.state & GDK_SHIFT_MASK) {
+                                        nc->shape_editor->select_segment_near_point(true);
+                                    } else {
+                                        nc->shape_editor->select_segment_near_point(false);
+                                    }
+                                    desktop->updateNow();
+                                }
+                                break;
+                            case GDK_2BUTTON_PRESS:
+                                //add a node
+                                nc->shape_editor->add_node_near_point();
+                                nc->added_node = true;
+                                break;
+                            default:
+                                break;
+                        }
+                    } else if (event->button.state & GDK_SHIFT_MASK) {
+                        selection->toggle(item_clicked);
+                        desktop->updateNow();
+                    } else {
+                        selection->set(item_clicked);
+                        desktop->updateNow();
+                    }
+                    ret = TRUE;
+                    break;
+                }
+            } 
+            if (event->type == GDK_BUTTON_RELEASE) {
+                event_context->xp = event_context->yp = 0;
+                if (event->button.button == 1) {
+                    NR::Maybe<NR::Rect> b = Inkscape::Rubberband::get()->getRectangle();
+
+                    if (nc->shape_editor->hits_curve() && !event_context->within_tolerance) { //drag curve
+                        nc->shape_editor->finish_drag();
+                    } else if (b && !event_context->within_tolerance) { // drag to select
+                        nc->shape_editor->select_rect(*b, event->button.state & GDK_SHIFT_MASK);
+                    } else {
+                        if (!(nc->rb_escaped)) { // unless something was cancelled
+                            if (nc->shape_editor->has_selection())
+                                nc->shape_editor->deselect();
+                            else
+                                sp_desktop_selection(desktop)->clear();
+                        }
                     }
+                    ret = TRUE;
+                    Inkscape::Rubberband::get()->stop();
+                    desktop->updateNow();
+                    nc->rb_escaped = false;
+                    nc->drag = FALSE;
+                    nc->shape_editor->cancel_hit();
+                    nc->current_state = SP_NODE_CONTEXT_INACTIVE;
                 }
-                ret = TRUE;
-                Inkscape::Rubberband::get()->stop();
-                desktop->updateNow();
-                nc->rb_escaped = false;
-                nc->drag = FALSE;
-                nc->shape_editor->cancel_hit();
-                nc->current_state = SP_NODE_CONTEXT_INACTIVE;
-                break;
             }
             break;
         case GDK_KEY_PRESS:
index 28c845492eaa515255644a302828e2f5509717e3..29a6f94cfab028e0f062cc0cc54310bbd827e190 100644 (file)
 #endif
 
 #include <gdk/gdkkeysyms.h>
+#include "display/canvas-bpath.h"
 #include "display/curve.h"
 #include "display/sp-ctrlline.h"
 #include "display/sodipodi-ctrl.h"
 #include <glibmm/i18n.h>
 #include "libnr/n-art-bpath.h"
+#include "libnr/nr-path.h"
 #include "helper/units.h"
 #include "knot.h"
 #include "inkscape.h"
@@ -45,6 +47,7 @@
 #include "display/bezier-utils.h"
 #include <vector>
 #include <algorithm>
+#include "live_effects/lpeobject.h"
 
 class NR::Matrix;
 
@@ -135,71 +138,96 @@ static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *
 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
 
+static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
+static void sp_nodepath_object_set_curve (SPObject *object, SPCurve *curve);
+
 // active_node indicates mouseover node
 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
 
 /**
  * \brief Creates new nodepath from item
+*   repr_key_in should be NULL,  unless you are called Johan or really know what you are doing! (See "if (repr_key_in)" below)
  */
-Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
+Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in)
 {
-    Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
+    Inkscape::XML::Node *repr = object->repr;
 
     /** \todo
      * FIXME: remove this. We don't want to edit paths inside flowtext.
      * Instead we will build our flowtext with cloned paths, so that the
      * real paths are outside the flowtext and thus editable as usual.
      */
-    if (SP_IS_FLOWTEXT(item)) {
-        for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
+    if (SP_IS_FLOWTEXT(object)) {
+        for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
             if SP_IS_FLOWREGION(child) {
                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
                 if (grandchild && SP_IS_PATH(grandchild)) {
-                    item = SP_ITEM(grandchild);
+                    object = SP_ITEM(grandchild);
                     break;
                 }
             }
         }
     }
 
-    if (!SP_IS_PATH(item))
-        return NULL;
-    SPPath *path = SP_PATH(item);
-    SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
+    SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
+
     if (curve == NULL)
         return NULL;
 
     NArtBpath *bpath = sp_curve_first_bpath(curve);
     gint length = curve->end;
-    if (length == 0)
+    if (length == 0) {
+        sp_curve_unref(curve);
         return NULL; // prevent crash for one-node paths
-
-    gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
-    gchar *typestr = parse_nodetypes(nodetypes, length);
+    }
 
     //Create new nodepath
     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
-    if (!np)
+    if (!np) {
+        sp_curve_unref(curve);
         return NULL;
+    }
 
     // Set defaults
     np->desktop     = desktop;
-    np->path        = path;
+    np->object      = object;
     np->subpaths    = NULL;
     np->selected    = NULL;
     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
     np->livarot_path = NULL;
     np->local_change = 0;
     np->show_handles = show_handles;
+    np->helper_path = NULL;
+    np->curve = sp_curve_copy(curve);
+    np->show_helperpath = false;
+    np->straight_path = false;
+    
 
     // we need to update item's transform from the repr here,
     // because they may be out of sync when we respond
     // to a change in repr by regenerating nodepath     --bb
-    sp_object_read_attr(SP_OBJECT(item), "transform");
+    sp_object_read_attr(object, "transform");
 
-    np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
+    np->i2d  = sp_item_i2d_affine(SP_ITEM(object));
     np->d2i  = np->i2d.inverse();
+
     np->repr = repr;
+    if (repr_key_in) {
+        np->repr_key = g_strdup(repr_key_in);
+        np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
+        np->show_helperpath = true;
+    } else {
+        np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
+        if ( SP_SHAPE(np->object)->path_effect_href ) {
+            np->repr_key = g_strdup("inkscape:original-d");
+            np->show_helperpath = true;
+        } else {
+            np->repr_key = g_strdup("d");
+        }
+    }
+
+    gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
+    gchar *typestr = parse_nodetypes(nodetypes, length);
 
     // create the subpath(s) from the bpath
     NArtBpath *b = bpath;
@@ -216,6 +244,17 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool
     // create the livarot representation from the same item
     sp_nodepath_ensure_livarot_path(np);
 
+    // Draw helper curve
+    if (np->show_helperpath) {
+        SPCurve *helper_curve = sp_curve_copy(np->curve);
+        sp_curve_transform(helper_curve, np->i2d );
+        np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
+        sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), 0xff0000ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
+        sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
+        sp_canvas_item_show(np->helper_path);
+        sp_curve_unref(helper_curve);
+    }
+
     return np;
 }
 
@@ -241,6 +280,25 @@ void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
         delete np->livarot_path;
         np->livarot_path = NULL;
     }
+    
+    if (np->helper_path) {
+        GtkObject *temp = np->helper_path;
+        np->helper_path = NULL;
+        gtk_object_destroy(temp);
+    }
+    if (np->curve) {
+        sp_curve_unref(np->curve);
+        np->curve = NULL;
+    }
+
+    if (np->repr_key) {
+        g_free(np->repr_key);
+        np->repr_key = NULL;
+    }
+    if (np->repr_nodetypes_key) {
+        g_free(np->repr_nodetypes_key);
+        np->repr_nodetypes_key = NULL;
+    }
 
     np->desktop = NULL;
 
@@ -250,10 +308,15 @@ void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
 
 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
 {
-    if (np && np->livarot_path == NULL && np->path && SP_IS_ITEM(np->path)) {
-        np->livarot_path = Path_for_item (np->path, true, true);
+    if (np && np->livarot_path == NULL && np->object && SP_IS_ITEM(np->object)) {
+        SPCurve *curve = create_curve(np);
+        NArtBpath *bpath = SP_CURVE_BPATH(curve);
+        np->livarot_path = bpath_to_Path(bpath);
+
         if (np->livarot_path)
             np->livarot_path->ConvertWithBackData(0.01);
+
+        sp_curve_unref(curve);
     }
 }
 
@@ -450,11 +513,17 @@ static void update_object(Inkscape::NodePath::Path *np)
 {
     g_assert(np);
 
-    SPCurve *curve = create_curve(np);
+    sp_curve_unref(np->curve);
+    np->curve = create_curve(np);
 
-    sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
+    sp_nodepath_object_set_curve(np->object, np->curve);
 
-    sp_curve_unref(curve);
+    if (np->show_helperpath) {
+        SPCurve * helper_curve = sp_curve_copy(np->curve);
+        sp_curve_transform(helper_curve, np->i2d );
+        sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
+        sp_curve_unref(helper_curve);
+    }
 }
 
 /**
@@ -464,26 +533,35 @@ static void update_repr_internal(Inkscape::NodePath::Path *np)
 {
     g_assert(np);
 
-    Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
+    Inkscape::XML::Node *repr = np->object->repr;
 
-    SPCurve *curve = create_curve(np);
+    sp_curve_unref(np->curve);
+    np->curve = create_curve(np);
+    
     gchar *typestr = create_typestr(np);
-    gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
+    gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
 
-    if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
+    // determine if path has an effect applied and write to correct "d" attribute.
+    if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
         np->local_change++;
-        repr->setAttribute("d", svgpath);
+        repr->setAttribute(np->repr_key, svgpath);
     }
 
-    if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
+    if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
         np->local_change++;
-        repr->setAttribute("sodipodi:nodetypes", typestr);
+        repr->setAttribute(np->repr_nodetypes_key, typestr);
     }
 
     g_free(svgpath);
     g_free(typestr);
-    sp_curve_unref(curve);
-}
+
+    if (np->show_helperpath) {
+        SPCurve * helper_curve = sp_curve_copy(np->curve);
+        sp_curve_transform(helper_curve, np->i2d );
+        sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
+        sp_curve_unref(helper_curve);
+    }
+ }
 
 /**
  * Update XML path node with data from path object, commit changes forever.
@@ -527,7 +605,7 @@ static void stamp_repr(Inkscape::NodePath::Path *np)
 {
     g_assert(np);
 
-    Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
+    Inkscape::XML::Node *old_repr = np->object->repr;
     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
 
     // remember the position of the item
@@ -540,8 +618,8 @@ static void stamp_repr(Inkscape::NodePath::Path *np)
 
     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
 
-    new_repr->setAttribute("d", svgpath);
-    new_repr->setAttribute("sodipodi:nodetypes", typestr);
+    new_repr->setAttribute(np->repr_key, svgpath);
+    new_repr->setAttribute(np->repr_nodetypes_key, typestr);
 
     // add the new repr to the parent
     parent->appendChild(new_repr);
@@ -999,7 +1077,7 @@ static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath,
         
         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
-            Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, n->subpath->nodepath->path);
+            Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->object));
             if (s.getDistance() < best) {
                 best = s.getDistance();
                 best_pt = s.getPoint() - n->pos;
@@ -4338,6 +4416,60 @@ sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to Sha
     }
 }
 
+/*
+ * returns a *copy* of the curve of that object.
+ */
+SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
+    if (!object)
+        return NULL;
+
+    SPCurve *curve = NULL;
+    if (SP_IS_PATH(object)) {
+        SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
+        curve = sp_curve_copy(curve_new);
+    } else if ( IS_LIVEPATHEFFECT(object) && key) {
+        const gchar *svgd = object->repr->attribute(key);
+        if (svgd) {
+            NArtBpath *bpath = sp_svg_read_path(svgd);
+            SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
+            if (curve_new) {
+                curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
+            } else {
+                g_free(bpath);
+            }
+        }
+    }
+
+    return curve;
+}
+
+void sp_nodepath_object_set_curve (SPObject *object, SPCurve *curve) {
+    if (!object || !curve)
+        return;
+
+    if (SP_IS_PATH(object)) {
+        if (SP_SHAPE(object)->path_effect_href) {
+            sp_path_set_original_curve(SP_PATH(object), curve, true, false);
+        } else {
+            sp_shape_set_curve(SP_SHAPE(object), curve, true);
+        }
+    } else if ( IS_LIVEPATHEFFECT(object) ) {
+        g_warning("sp_nodepath_set_curve not implemented yet for lpeobjects");
+    }
+}
+
+void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
+    np->show_helperpath = show;
+}
+
+void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
+    np->straight_path = true;
+    np->show_handles = false;
+    g_message("add code to make the path straight.");
+    // do sp_nodepath_convert_node_type on all nodes?
+    // search for this text !!!   "Make selected segments lines"
+}
+
 
 /*
   Local Variables:
index 7e4066769b9f0c4c90a638c290a3bd28cd9d38bd..29d34addd774cbbf02ccc193873701615a35f118 100644 (file)
@@ -17,6 +17,7 @@
 //#include "desktop-handles.h"
 #include "libnr/nr-path-code.h"
 #include "livarot/Path.h"
+#include <glibmm/ustring.h>
 
 #include <list>
 
@@ -111,7 +112,7 @@ class Path {
 /**  Pointer to the current desktop, for reporting purposes */
        SPDesktop * desktop;
 /**  The parent path of this nodepath */
-       SPPath * path;
+       SPObject * object;
 /**  The context which created this nodepath.  Important if this nodepath is deleted */
        ShapeEditor *shape_editor;
 /**  The subpaths which comprise this NodePath */
@@ -122,12 +123,19 @@ class Path {
         njh: I'd be guessing that these are item <-> desktop transforms.*/
        NR::Matrix i2d, d2i;
 /**  The DOM node which describes this NodePath */
-       Inkscape::XML::Node *repr;
+    Inkscape::XML::Node *repr;
+    gchar *repr_key;
+    gchar *repr_nodetypes_key;
        //STL compliant method to get the selected nodes
        void selection(std::list<Node *> &l);
 
       /// livarot library is used for "point on path" and "nearest position on path", so we need to maintain its path representation as well
        ::Path *livarot_path;
+    
+    /// draw a "sketch" of the path by using these variables
+    SPCanvasItem *helper_path;
+    SPCurve *curve;
+    bool show_helperpath;
 
       /// true if we changed repr, to tell this change from an external one such as from undo, simplify, or another desktop
        unsigned int local_change;
@@ -135,6 +143,9 @@ class Path {
        /// true if we're showing selected nodes' handles
        bool show_handles;
 
+    /// true if the path cannot contain curves, just straight lines
+    bool straight_path;
+
        /// active_node points to the node that is currently mouseovered (= NULL if
        /// there isn't any); we also consider the node mouseovered if it is covered
        /// by one of its handles and the latter is mouseovered
@@ -246,7 +257,7 @@ enum {
 };
 
 // Do function documentation in nodepath.cpp
-Inkscape::NodePath::Path * sp_nodepath_new (SPDesktop * desktop, SPItem * item, bool show_handles);
+Inkscape::NodePath::Path * sp_nodepath_new (SPDesktop * desktop, SPObject *object, bool show_handles, const char * repr_key = NULL);
 void sp_nodepath_destroy (Inkscape::NodePath::Path * nodepath);
 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np);
 void sp_nodepath_deselect (Inkscape::NodePath::Path *nodepath);
index 8e916193bae7882eedb1c1acd56b119885a77966..1e1f135f9d08b88c35bed8ae8ba6c314eda0f0a0 100644 (file)
@@ -2962,6 +2962,11 @@ void unhide_all_in_all_layers(SPDesktop *dt) {
     process_all(&unhide, dt, false);
 }
 
+
+GSList * sp_selection_get_clipboard() {
+    return clipboard;
+}
+
 /*
   Local Variables:
   mode:c++
index 0ad465ec472439ef7ab1e425a9357e212102700f..b21cbc2a182ccfa2a12ebdd88b76335d33ed86bb 100644 (file)
@@ -105,6 +105,7 @@ void unlock_all_in_all_layers(SPDesktop *dt);
 void unhide_all(SPDesktop *dt);
 void unhide_all_in_all_layers(SPDesktop *dt);
 
+GSList * sp_selection_get_clipboard();
 
 /* selection cycling */
 
index f865d6a4db648dd2d4a8006a855a9f15604f0119..72c5ef0585f257b57b7b0701558d2f9c8846428d 100644 (file)
@@ -113,7 +113,7 @@ void ShapeEditor::decrement_local_change () {
 SPItem *ShapeEditor::get_item () {
     SPItem *item = NULL;
     if (this->has_nodepath()) {
-        item = SP_ITEM(this->nodepath->path);
+        item = SP_ITEM(this->nodepath->object);
     } else if (this->has_knotholder()) {
         item = SP_ITEM(this->knotholder->item);
     }
@@ -205,6 +205,37 @@ void ShapeEditor::set_item(SPItem *item) {
     }
 }
 
+void ShapeEditor::set_livepatheffect_parameter(SPObject *lpeobject, const char * key) {
+
+    unset_item();
+
+    this->grab_node = -1;
+
+    if (lpeobject) {
+        this->nodepath = sp_nodepath_new( desktop, lpeobject, 
+                                          (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0),
+                                          key);
+        if (this->nodepath) {
+            this->nodepath->shape_editor = this; 
+        }
+        //this->knotholder = sp_item_knot_holder(item, desktop);
+        g_message("create knotholder?");
+
+        if (this->nodepath || this->knotholder) {
+            // setting new listener
+            Inkscape::XML::Node *repr;
+            if (this->knotholder)
+                repr = this->knotholder->repr;
+            else
+                repr = SP_OBJECT_REPR(lpeobject);
+            if (repr) {
+                Inkscape::GC::anchor(repr);
+                sp_repr_add_listener(repr, &shapeeditor_repr_events, this);
+            }
+        }
+    }
+}
+
 void ShapeEditor::nodepath_destroyed () {
     this->nodepath = NULL;
 }
@@ -224,7 +255,6 @@ bool ShapeEditor::is_over_stroke (NR::Point event_p, bool remember) {
     //Translate click point into proper coord system
     this->curvepoint_doc = desktop->w2d(event_p);
     this->curvepoint_doc *= sp_item_dt2i_affine(item);
-    this->curvepoint_doc *= sp_item_i2doc_affine(item);
 
     sp_nodepath_ensure_livarot_path(this->nodepath);
 
@@ -285,7 +315,7 @@ bool ShapeEditor::hits_curve() {
 
 
 void ShapeEditor::curve_drag(gdouble eventx, gdouble eventy) {
-    if (this->nodepath) {
+    if (this->nodepath && !this->nodepath->straight_path) {
 
         if (this->grab_node == -1) // don't know which segment to drag
             return;
index 8eca230a4c9468b8c8fdffe512a29b67c379a413..0e5bc22ef6b92f3629ead0953d08bb953bef35a2 100644 (file)
@@ -36,6 +36,7 @@ public:
     ~ShapeEditor();
 
     void set_item (SPItem *item);
+    void set_livepatheffect_parameter(SPObject *lpeobject, const char * key);
     void unset_item ();
 
     SPItem *get_item ();
index 6ab49711607fc6f878ed0e52065a33cbde57726f..f558bc229322735dc33a728f1d6edb12666b367c 100644 (file)
@@ -76,6 +76,8 @@ static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags);
 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p);
 
 static void sp_genericellipse_set_shape(SPShape *shape);
+static void sp_genericellipse_update_patheffect (SPShape *shape, bool write);
+
 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr,
                                                     guint flags);
 
@@ -119,6 +121,7 @@ static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
     item_class->snappoints = sp_genericellipse_snappoints;
 
     shape_class->set_shape = sp_genericellipse_set_shape;
+    shape_class->update_patheffect = sp_genericellipse_update_patheffect;
 }
 
 static void
@@ -154,6 +157,31 @@ sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
         ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
 }
 
+static void
+sp_genericellipse_update_patheffect(SPShape *shape, bool write)
+{
+    sp_genericellipse_set_shape(shape);
+
+    if (write) {
+        Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
+        if ( shape->curve != NULL ) {
+            NArtBpath *abp = sp_curve_first_bpath(shape->curve);
+            if (abp) {
+                gchar *str = sp_svg_write_path(abp);
+                repr->setAttribute("d", str);
+                g_free(str);
+            } else {
+                repr->setAttribute("d", "");
+            }
+        } else {
+            repr->setAttribute("d", NULL);
+        }
+    }
+
+    ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
+
 #define C1 0.552
 
 /* fixme: Think (Lauris) */
@@ -248,6 +276,7 @@ static void sp_genericellipse_set_shape(SPShape *shape)
     SPCurve *c = sp_curve_new_from_bpath(nr_artpath_affine(bpath, aff));
     g_assert(c != NULL);
 
+    sp_shape_perform_path_effect(c, SP_SHAPE (ellipse));
     sp_shape_set_curve_insync((SPShape *) ellipse, c, TRUE);
     sp_curve_unref(c);
 }
index f05455e84f39fa9b73519ee846f99d4cf9408bd1..9338f101996372d0eb811f27f672e3ebe17dd955 100644 (file)
@@ -66,6 +66,8 @@
 #include "sp-fetile.h"
 #include "sp-feturbulence.h"
 #include "sp-femergenode.h"
+#include "live_effects/lpeobject.h"
+
 
 enum NameType { REPR_NAME, SODIPODI_TYPE };
 static unsigned const N_NAME_TYPES = SODIPODI_TYPE + 1;
@@ -176,7 +178,8 @@ populate_dtables()
         { "svg:textPath", SP_TYPE_TEXTPATH },
         { "svg:tref", SP_TYPE_TREF },
         { "svg:tspan", SP_TYPE_TSPAN },
-        { "svg:use", SP_TYPE_USE }
+        { "svg:use", SP_TYPE_USE },
+        { "inkscape:path-effect", TYPE_LIVEPATHEFFECT }
     };
     NameTypeEntry const sodipodi_name_entries[] = {
         { "arc", SP_TYPE_ARC },
index 6526435de98101f25ae6fc984d79a4876648b52b..f76e70a51cbc9460fc7645c917d809d9001b4369 100644 (file)
@@ -848,7 +848,6 @@ sp_object_invoke_build(SPObject *object, SPDocument *document, Inkscape::XML::No
     }
 
     /* Invoke derived methods, if any */
-
     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->build) {
         (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->build)(object, document, repr);
     }
index 3b455e50532a74cff05f14107cf666db144df300..53cbb16374a51d8feaa260f4e3a7a99ec3d52118 100644 (file)
@@ -48,6 +48,7 @@ static NR::Matrix sp_path_set_transform(SPItem *item, NR::Matrix const &xform);
 static gchar * sp_path_description(SPItem *item);
 
 static void sp_path_update(SPObject *object, SPCtx *ctx, guint flags);
+static void sp_path_update_patheffect(SPShape *shape, bool write);
 
 static SPShapeClass *parent_class;
 
@@ -86,6 +87,7 @@ sp_path_class_init(SPPathClass * klass)
     GObjectClass *gobject_class = (GObjectClass *) klass;
     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
     SPItemClass *item_class = (SPItemClass *) klass;
+    SPShapeClass *shape_class = (SPShapeClass *) klass;
 
     parent_class = (SPShapeClass *)g_type_class_peek_parent(klass);
 
@@ -99,6 +101,8 @@ sp_path_class_init(SPPathClass * klass)
 
     item_class->description = sp_path_description;
     item_class->set_transform = sp_path_set_transform;
+
+    shape_class->update_patheffect = sp_path_update_patheffect;
 }
 
 
@@ -125,12 +129,14 @@ sp_path_description(SPItem * item)
 }
 
 /**
- * Initializes an SPPath.  Currently does nothing.
+ * Initializes an SPPath.
  */
 static void
 sp_path_init(SPPath *path)
 {
     new (&path->connEndPair) SPConnEndPair(path);
+
+    path->original_curve = NULL;
 }
 
 static void
@@ -148,14 +154,6 @@ sp_path_finalize(GObject *obj)
 static void
 sp_path_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
 {
-    sp_object_read_attr(object, "d");
-
-    /* d is a required attribute */
-    gchar const *d = sp_object_getAttribute(object, "d", NULL);
-    if (d == NULL) {
-        sp_object_set(object, sp_attribute_lookup("d"), "");
-    }
-
     /* Are these calls actually necessary? */
     sp_object_read_attr(object, "marker");
     sp_object_read_attr(object, "marker-start");
@@ -167,6 +165,15 @@ sp_path_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
     if (((SPObjectClass *) parent_class)->build) {
         ((SPObjectClass *) parent_class)->build(object, document, repr);
     }
+
+    sp_object_read_attr(object, "inkscape:original-d");
+    sp_object_read_attr(object, "d");
+
+    /* d is a required attribute */
+    gchar const *d = sp_object_getAttribute(object, "d", NULL);
+    if (d == NULL) {
+        sp_object_set(object, sp_attribute_lookup("d"), "");
+    }
 }
 
 static void
@@ -176,6 +183,10 @@ sp_path_release(SPObject *object)
 
     path->connEndPair.release();
 
+    if (path->original_curve) {
+        path->original_curve = sp_curve_unref (path->original_curve);
+    }
+
     if (((SPObjectClass *) parent_class)->release) {
         ((SPObjectClass *) parent_class)->release(object);
     }
@@ -191,18 +202,33 @@ sp_path_set(SPObject *object, unsigned int key, gchar const *value)
     SPPath *path = (SPPath *) object;
 
     switch (key) {
-        case SP_ATTR_D:
-            if (value) {
-                NArtBpath *bpath = sp_svg_read_path(value);
-                SPCurve *curve = sp_curve_new_from_bpath(bpath);
-                if (curve) {
-                    sp_shape_set_curve((SPShape *) path, curve, TRUE);
-                    sp_curve_unref(curve);
+        case SP_ATTR_INKSCAPE_ORIGINAL_D:
+                if (value) {
+                    NArtBpath *bpath = sp_svg_read_path(value);
+                    SPCurve *curve = sp_curve_new_from_bpath(bpath);
+                    if (curve) {
+                        sp_path_set_original_curve(path, curve, TRUE, true);
+                        sp_curve_unref(curve);
+                    }
+                } else {
+                    sp_path_set_original_curve(path, NULL, TRUE, true);
                 }
-            } else {
-                sp_shape_set_curve((SPShape *) path, NULL, TRUE);
+                object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+            break;
+       case SP_ATTR_D:
+            if (!((SPShape *) path)->path_effect_href) {
+                if (value) {
+                    NArtBpath *bpath = sp_svg_read_path(value);
+                    SPCurve *curve = sp_curve_new_from_bpath(bpath);
+                    if (curve) {
+                        sp_shape_set_curve((SPShape *) path, curve, TRUE);
+                        sp_curve_unref(curve);
+                    }
+                } else {
+                    sp_shape_set_curve((SPShape *) path, NULL, TRUE);
+                }
+                object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
             }
-            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
             break;
         case SP_PROP_MARKER:
         case SP_PROP_MARKER_START:
@@ -251,6 +277,20 @@ sp_path_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
         repr->setAttribute("d", NULL);
     }
 
+    SPPath *path = (SPPath *) object;
+    if ( path->original_curve != NULL ) {
+        NArtBpath *abp = sp_curve_first_bpath(path->original_curve);
+        if (abp) {
+            gchar *str = sp_svg_write_path(abp);
+            repr->setAttribute("inkscape:original-d", str);
+            g_free(str);
+        } else {
+            repr->setAttribute("inkscape:original-d", "");
+        }
+    } else {
+        repr->setAttribute("inkscape:original-d", NULL);
+    }
+
     SP_PATH(shape)->connEndPair.writeRepr(repr);
 
     if (((SPObjectClass *)(parent_class))->write) {
@@ -283,19 +323,30 @@ static NR::Matrix
 sp_path_set_transform(SPItem *item, NR::Matrix const &xform)
 {
     SPShape *shape = (SPShape *) item;
+    SPPath *path = (SPPath *) item;
 
     if (!shape->curve) { // 0 nodes, nothing to transform
         return NR::identity();
     }
 
-    /* Transform the path */
-    NRBPath dpath, spath;
-    spath.path = SP_CURVE_BPATH(shape->curve);
-    nr_path_duplicate_transform(&dpath, &spath, xform);
-    SPCurve *curve = sp_curve_new_from_bpath(dpath.path);
-    if (curve) {
-        sp_shape_set_curve(shape, curve, TRUE);
-        sp_curve_unref(curve);
+    if (path->original_curve) { /* Transform the original-d path */
+        NRBPath dorigpath, sorigpath;
+        sorigpath.path = SP_CURVE_BPATH(path->original_curve);
+        nr_path_duplicate_transform(&dorigpath, &sorigpath, xform);
+        SPCurve *origcurve = sp_curve_new_from_bpath(dorigpath.path);
+        if (origcurve) {
+            sp_path_set_original_curve(path, origcurve, TRUE, true);
+            sp_curve_unref(origcurve);
+        }
+    } else {    /* Transform the path */
+        NRBPath dpath, spath;
+        spath.path = SP_CURVE_BPATH(shape->curve);
+        nr_path_duplicate_transform(&dpath, &spath, xform);
+        SPCurve *curve = sp_curve_new_from_bpath(dpath.path);
+        if (curve) {
+            sp_shape_set_curve(shape, curve, TRUE);
+            sp_curve_unref(curve);
+        }
     }
 
     // Adjust stroke
@@ -313,6 +364,88 @@ sp_path_set_transform(SPItem *item, NR::Matrix const &xform)
     return NR::identity();
 }
 
+static void
+sp_path_update_patheffect(SPShape *shape, bool write)
+{
+    SPPath *path = (SPPath *) shape;
+    if (path->original_curve) {
+        SPCurve *curve = sp_curve_copy (path->original_curve);
+        sp_shape_perform_path_effect(curve, shape);
+        sp_shape_set_curve(shape, curve, TRUE);
+        sp_curve_unref(curve);
+
+        if (write) {
+            // could also do SP_OBJECT(shape)->updateRepr();  but only the d attribute needs updating.
+            Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
+            if ( shape->curve != NULL ) {
+                NArtBpath *abp = sp_curve_first_bpath(shape->curve);
+                if (abp) {
+                    gchar *str = sp_svg_write_path(abp);
+                    repr->setAttribute("d", str);
+                    g_free(str);
+                } else {
+                    repr->setAttribute("d", "");
+                }
+            } else {
+                repr->setAttribute("d", NULL);
+            }
+        }
+    } else {
+
+    }
+}
+
+
+/**
+ * Adds a original_curve to the path.  If owner is specified, a reference
+ * will be made, otherwise the curve will be copied into the path.
+ * Any existing curve in the path will be unreferenced first.
+ * This routine triggers reapplication of the an effect is present
+ * an also triggers a request to update the display. Does not write
+* result to XML when write=false.
+ */
+void
+sp_path_set_original_curve (SPPath *path, SPCurve *curve, unsigned int owner, bool write)
+{
+    if (path->original_curve) {
+        path->original_curve = sp_curve_unref (path->original_curve);
+    }
+    if (curve) {
+        if (owner) {
+            path->original_curve = sp_curve_ref (curve);
+        } else {
+            path->original_curve = sp_curve_copy (curve);
+        }
+    }
+    sp_path_update_patheffect(path, write);
+    SP_OBJECT(path)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
+/**
+ * Return duplicate of original_curve (if any exists) or NULL if there is no curve
+ */
+SPCurve *
+sp_path_get_original_curve (SPPath *path)
+{
+    if (path->original_curve) {
+        return sp_curve_copy (path->original_curve);
+    }
+    return NULL;
+}
+
+/**
+ * Return duplicate of edittable curve which is original_curve if it exists or
+ * shape->curve if not.
+ */
+SPCurve*
+sp_path_get_curve_for_edit (SPPath *path)
+{
+    if (path->original_curve) {
+        return sp_path_get_original_curve(path);
+    } else {
+        return sp_shape_get_curve( (SPShape *) path );
+    }
+}
 
 /*
   Local Variables:
index d55c978295d55a53ff12b64de01e728060a0e503..cd413db5447481140eee38553b1a7e1540206eaf 100644 (file)
@@ -22,6 +22,8 @@
 #define SP_IS_PATH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_PATH))
 
 struct SPPath : public SPShape {
+    SPCurve *original_curve;
+
     SPConnEndPair connEndPair;
 };
 
@@ -32,6 +34,10 @@ struct SPPathClass {
 GType sp_path_get_type (void);
 gint sp_nodes_in_path(SPPath *path);
 
+void     sp_path_set_original_curve (SPPath *path, SPCurve *curve, unsigned int owner, bool write);
+SPCurve* sp_path_get_original_curve (SPPath *path);
+SPCurve* sp_path_get_curve_for_edit (SPPath *path);
+
 #endif
 
 /*
index 6dba2afb67b2f36a63b0d6f3ecd0ead17c03c4e6..9d08fc58de4e838f443ced3d7c775daa9bee81b4 100644 (file)
 #include "marker.h"
 #include "sp-path.h"
 #include "prefs-utils.h"
+#include "attributes.h"
+
+#include "live_effects/effect.h"
+#include "live_effects/lpeobject.h"
+#include "live_effects/lpeobject-reference.h"
+#include "uri.h"
+#include "extract-uri.h"
+#include "uri-references.h"
+#include "bad-uri-exception.h"
+#include "xml/repr.h"
 
 #define noSHAPE_VERBOSE
 
@@ -45,8 +55,10 @@ static void sp_shape_finalize (GObject *object);
 static void sp_shape_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
 static void sp_shape_release (SPObject *object);
 
+static void sp_shape_set(SPObject *object, unsigned key, gchar const *value);
 static void sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags);
 static void sp_shape_modified (SPObject *object, unsigned int flags);
+static Inkscape::XML::Node *sp_shape_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
 
 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
 void sp_shape_print (SPItem * item, SPPrintContext * ctx);
@@ -56,6 +68,9 @@ static void sp_shape_snappoints (SPItem const *item, SnapPointsIter p);
 
 static void sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai);
 
+static void lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPShape *shape);
+static void lpeobject_ref_modified(SPObject *href, guint flags, SPShape *shape);
+
 static SPItemClass *parent_class;
 
 /**
@@ -104,14 +119,19 @@ sp_shape_class_init (SPShapeClass *klass)
 
        sp_object_class->build = sp_shape_build;
        sp_object_class->release = sp_shape_release;
+    sp_object_class->set = sp_shape_set;
        sp_object_class->update = sp_shape_update;
        sp_object_class->modified = sp_shape_modified;
+    sp_object_class->write = sp_shape_write;
 
        item_class->bbox = sp_shape_bbox;
        item_class->print = sp_shape_print;
        item_class->show = sp_shape_show;
        item_class->hide = sp_shape_hide;
     item_class->snappoints = sp_shape_snappoints;
+
+    klass->set_shape = NULL;
+    klass->update_patheffect = NULL;
 }
 
 /**
@@ -120,6 +140,10 @@ sp_shape_class_init (SPShapeClass *klass)
 static void
 sp_shape_init (SPShape *shape)
 {
+    shape->path_effect_href = NULL;
+    shape->path_effect_ref  = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(shape));
+    shape->path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), shape));
+
     for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
         new (&shape->release_connect[i]) sigc::connection();
         new (&shape->modified_connect[i]) sigc::connection();
@@ -146,17 +170,18 @@ sp_shape_finalize (GObject *object)
 /**
  * Virtual build callback for SPMarker.
  *
- * This is to be invoked immediately after creation of an SPShape.  This is 
- * just a stub.
+ * This is to be invoked immediately after creation of an SPShape.
  *
  * \see sp_object_build()
  */
 static void
 sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
 {
-       if (((SPObjectClass *) (parent_class))->build) {
-         (*((SPObjectClass *) (parent_class))->build) (object, document, repr);
-       }
+    sp_object_read_attr(object, "inkscape:path-effect");
+    if (((SPObjectClass *) (parent_class))->build) {
+       (*((SPObjectClass *) (parent_class))->build) (object, document, repr);
+    }
 }
 
 /**
@@ -193,11 +218,78 @@ sp_shape_release (SPObject *object)
                shape->curve = sp_curve_unref (shape->curve);
        }
 
+    if (shape->path_effect_href) {
+        g_free(shape->path_effect_href);
+    }
+    shape->path_effect_ref->detach();
+    
        if (((SPObjectClass *) parent_class)->release) {
          ((SPObjectClass *) parent_class)->release (object);
        }
 }
 
+
+
+static void
+sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
+{
+    SPShape *shape = (SPShape *) object;
+    bool path_effect_changed = false;
+
+    switch (key) {
+        case SP_ATTR_INKSCAPE_PATH_EFFECT:
+            if ( value && shape->path_effect_href && ( strcmp(value, shape->path_effect_href) == 0 ) ) {
+                /* No change, do nothing. */
+            } else {
+                if (shape->path_effect_href) {
+                    g_free(shape->path_effect_href);
+                    shape->path_effect_href = NULL;
+                }
+                if (value) {
+                    shape->path_effect_href = g_strdup(value);
+
+                    // Now do the attaching, which emits the changed signal.
+                    try {
+                        shape->path_effect_ref->attach(Inkscape::URI(value));
+                    } catch (Inkscape::BadURIException &e) {
+                        g_warning("%s", e.what());
+                        shape->path_effect_ref->detach();
+                    }
+                } else {
+                    shape->path_effect_ref->detach();
+                }
+            }
+            path_effect_changed = true;  // updated twice now when connected to changed signal??
+            break;
+        default:
+            if (((SPObjectClass *) parent_class)->set) {
+                ((SPObjectClass *) parent_class)->set(object, key, value);
+            }
+            break;
+    }
+
+    if (path_effect_changed) 
+        sp_shape_update_patheffect ((SPShape *) object, false);
+}
+
+static Inkscape::XML::Node *
+sp_shape_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
+{
+    SPShape *shape = (SPShape *) object;
+
+    if ( shape->path_effect_href ) {
+        repr->setAttribute("inkscape:path-effect", shape->path_effect_href);
+    } else {
+        repr->setAttribute("inkscape:path-effect", NULL);
+    }
+
+    if (((SPObjectClass *)(parent_class))->write) {
+        ((SPObjectClass *)(parent_class))->write(object, repr, flags);
+    }
+
+    return repr;
+}
+
 /** 
  * Updates the shape when its attributes have changed.  Also establishes
  * marker objects to match the style settings.  
@@ -1033,6 +1125,83 @@ static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p)
 }
 
 
+LivePathEffectObject *
+sp_shape_get_livepatheffectobject(SPShape *shape) {
+    if (!shape) return NULL;
+
+    return shape->path_effect_ref->lpeobject;
+}
+
+/**
+ * Calls any registered handlers for the update_patheffect action
+ */
+void
+sp_shape_update_patheffect (SPShape *shape, bool write)
+{
+    g_return_if_fail (shape != NULL);
+    g_return_if_fail (SP_IS_SHAPE (shape));
+
+    if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->update_patheffect) {
+        SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->update_patheffect (shape, write);
+    }
+}
+
+void sp_shape_perform_path_effect(SPCurve *curve, SPShape *shape) {
+    if (!shape) return;
+    if (!curve) return;
+
+    LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(shape);
+    if (lpeobj && lpeobj->lpe) {
+        lpeobj->lpe->doEffect(curve);
+    }
+}
+
+/**
+ * Gets called when (re)attached to another lpeobject.
+ */
+static void
+lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPShape *shape)
+{
+    if (old_ref) {
+        sp_signal_disconnect_by_data(old_ref, shape);
+    }
+    if ( IS_LIVEPATHEFFECT(ref) && ref != shape )
+    {
+        ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), shape));
+    }
+
+    lpeobject_ref_modified(ref, 0, shape);
+}
+
+/**
+ * Gets called when lpeobject repr contents change: i.e. parameter change.
+ */
+static void
+lpeobject_ref_modified(SPObject *href, guint flags, SPShape *shape)
+{
+    sp_shape_update_patheffect (shape, true);
+}
+
+void sp_shape_set_path_effect(SPShape *shape, gchar *value)
+{
+    if (!value) {
+        sp_shape_remove_path_effect(shape);
+    } else {
+        SP_OBJECT_REPR(shape)->setAttribute("inkscape:path-effect", value);
+    }
+}
+
+void sp_shape_remove_path_effect(SPShape *shape)
+{
+    Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
+    repr->setAttribute("inkscape:path-effect", NULL);
+    if (SP_IS_PATH(shape)) {
+        repr->setAttribute("d", repr->attribute("inkscape:original-d"));
+        repr->setAttribute("inkscape:original-d", NULL);
+    }
+}
+
+
 /*
   Local Variables:
   mode:c++
index 2b265fdaaf6b397c8c4247098dbf3ee7354c9068..e8ec6d040f8577137dbc102268d848692ab94880 100644 (file)
 
 #define SP_SHAPE_WRITE_PATH (1 << 2)
 
+struct LivePathEffectObject;
+namespace Inkscape{ 
+namespace LivePathEffect{
+    class LPEObjectReference;
+};
+};
+
+
 struct SPShape : public SPItem {
-       SPCurve *curve;
+    SPCurve *curve;
 
       SPObject *marker[SP_MARKER_LOC_QTY];
       sigc::connection release_connect [SP_MARKER_LOC_QTY];
       sigc::connection modified_connect [SP_MARKER_LOC_QTY];
+
+    gchar *path_effect_href;
+    Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref;
 };
 
 struct SPShapeClass {
@@ -40,6 +51,8 @@ struct SPShapeClass {
 
        /* Build bpath from extra shape attributes */
        void (* set_shape) (SPShape *shape);
+
+    void (* update_patheffect) (SPShape *shape, bool write);
 };
 
 GType sp_shape_get_type (void);
@@ -62,4 +75,11 @@ int sp_shape_number_of_markers (SPShape* Shape, int type);
 NR::Matrix sp_shape_marker_get_transform(SPShape const *shape, NArtBpath const *bp);
 bool sp_shape_marker_required(SPShape const *shape, int const m, NArtBpath *bp);
 
+LivePathEffectObject * sp_shape_get_livepatheffectobject(SPShape *shape);
+void sp_shape_update_patheffect (SPShape *shape, bool write);
+void sp_shape_perform_path_effect(SPCurve *curve, SPShape *shape);
+
+void sp_shape_set_path_effect(SPShape *shape, gchar *value);
+void sp_shape_remove_path_effect(SPShape *shape);
+
 #endif
index a2449fb9a334a8d6edfa9db187251acf4a6cd297..3e8ce49973afa41e2b830f018b8e1cff40d73b41 100644 (file)
@@ -37,7 +37,9 @@ static void sp_spiral_update (SPObject *object, SPCtx *ctx, guint flags);
 
 static gchar * sp_spiral_description (SPItem * item);
 static void sp_spiral_snappoints(SPItem const *item, SnapPointsIter p);
+
 static void sp_spiral_set_shape (SPShape *shape);
+static void sp_spiral_update_patheffect (SPShape *shape, bool write);
 
 static NR::Point sp_spiral_get_tangent (SPSpiral const *spiral, gdouble t);
 
@@ -95,7 +97,8 @@ sp_spiral_class_init (SPSpiralClass *klass)
        item_class->description = sp_spiral_description;
        item_class->snappoints = sp_spiral_snappoints;
 
-       shape_class->set_shape = sp_spiral_set_shape;
+    shape_class->set_shape = sp_spiral_set_shape;
+    shape_class->update_patheffect = sp_spiral_update_patheffect;
 }
 
 /**
@@ -293,6 +296,30 @@ sp_spiral_update (SPObject *object, SPCtx *ctx, guint flags)
                ((SPObjectClass *) parent_class)->update (object, ctx, flags);
 }
 
+static void
+sp_spiral_update_patheffect(SPShape *shape, bool write)
+{
+    sp_spiral_set_shape(shape);
+
+    if (write) {
+        Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
+        if ( shape->curve != NULL ) {
+            NArtBpath *abp = sp_curve_first_bpath(shape->curve);
+            if (abp) {
+                gchar *str = sp_svg_write_path(abp);
+                repr->setAttribute("d", str);
+                g_free(str);
+            } else {
+                repr->setAttribute("d", "");
+            }
+        } else {
+            repr->setAttribute("d", NULL);
+        }
+    }
+
+    ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
 /**
  * Return textual description of spiral.
  */
@@ -436,8 +463,9 @@ sp_spiral_set_shape (SPShape *shape)
                sp_spiral_fit_and_draw (spiral, c, (1.0 - t)/(SAMPLE_SIZE - 1.0),
                                        darray, hat1, hat2, &t);
 
-       sp_shape_set_curve_insync ((SPShape *) spiral, c, TRUE);
-       sp_curve_unref (c);
+    sp_shape_perform_path_effect(c, SP_SHAPE (spiral));
+    sp_shape_set_curve_insync ((SPShape *) spiral, c, TRUE);
+    sp_curve_unref (c);
 }
 
 /**
index ea25250541faadfa83d718b8079c9ae629d6ef6b..767175e878efeaa34feeff1d99969dafbc64ddd3 100644 (file)
@@ -38,6 +38,7 @@ static gchar * sp_star_description (SPItem * item);
 static void sp_star_snappoints(SPItem const *item, SnapPointsIter p);
 
 static void sp_star_set_shape (SPShape *shape);
+static void sp_star_update_patheffect (SPShape *shape, bool write);
 
 static SPShapeClass *parent_class;
 
@@ -88,6 +89,7 @@ sp_star_class_init (SPStarClass *klass)
        item_class->snappoints = sp_star_snappoints;
 
        shape_class->set_shape = sp_star_set_shape;
+    shape_class->update_patheffect = sp_star_update_patheffect;
 }
 
 static void
@@ -269,6 +271,30 @@ sp_star_update (SPObject *object, SPCtx *ctx, guint flags)
                ((SPObjectClass *) parent_class)->update (object, ctx, flags);
 }
 
+static void
+sp_star_update_patheffect(SPShape *shape, bool write)
+{
+    sp_star_set_shape(shape);
+
+    if (write) {
+        Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
+        if ( shape->curve != NULL ) {
+            NArtBpath *abp = sp_curve_first_bpath(shape->curve);
+            if (abp) {
+                gchar *str = sp_svg_write_path(abp);
+                repr->setAttribute("d", str);
+                g_free(str);
+            } else {
+                repr->setAttribute("d", "");
+            }
+        } else {
+            repr->setAttribute("d", NULL);
+        }
+    }
+
+    ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
 static gchar *
 sp_star_description (SPItem *item)
 {
@@ -470,9 +496,10 @@ sp_star_set_shape (SPShape *shape)
                        }
                }
 
-       sp_curve_closepath (c);
-       sp_shape_set_curve_insync (SP_SHAPE (star), c, TRUE);
-       sp_curve_unref (c);
+    sp_curve_closepath (c);
+    sp_shape_perform_path_effect(c, SP_SHAPE (star));
+    sp_shape_set_curve_insync (SP_SHAPE (star), c, TRUE);
+    sp_curve_unref (c);
 }
 
 void
index 60be616788954d79367f1455d85acef136e1cd6c..11011d0c5eaeb003b575d15a3aba94467ca7121d 100644 (file)
@@ -34,6 +34,8 @@ ui_dialog_libuidialog_a_SOURCES =             \
        ui/dialog/inkscape-preferences.h        \
        ui/dialog/layer-editor.cpp              \
        ui/dialog/layer-editor.h                \
+       ui/dialog/livepatheffect-editor.cpp             \
+       ui/dialog/livepatheffect-editor.h               \
        ui/dialog/memory.cpp                    \
        ui/dialog/memory.h                      \
        ui/dialog/messages.cpp                  \
index 6b02776102238e9d2c25c2a2bdfc03b26c11df4d..23c64f1d5fa01c3f63d8a7f253060807f0892c7c 100644 (file)
@@ -27,6 +27,7 @@
 #include "ui/dialog/find.h"
 #include "ui/dialog/inkscape-preferences.h"
 #include "ui/dialog/layer-editor.h"
+#include "ui/dialog/livepatheffect-editor.h"
 #include "ui/dialog/memory.h"
 #include "ui/dialog/messages.h"
 #include "ui/dialog/scriptdialog.h"
@@ -80,6 +81,7 @@ DialogManager::DialogManager() {
     registerFactory("Find",                &create<Find>);
     registerFactory("InkscapePreferences", &create<InkscapePreferences>);
     registerFactory("LayerEditor",         &create<LayerEditor>);
+    registerFactory("LivePathEffect",      &create<LivePathEffectEditor>);
     registerFactory("Memory",              &create<Memory>);
     registerFactory("Messages",            &create<Messages>);
     registerFactory("Script",              &create<ScriptDialog>);
diff --git a/src/ui/dialog/livepatheffect-editor.cpp b/src/ui/dialog/livepatheffect-editor.cpp
new file mode 100644 (file)
index 0000000..5f765b4
--- /dev/null
@@ -0,0 +1,290 @@
+/**\r
+ * \brief LivePathEffect dialog\r
+ *\r
+ * Authors:\r
+ *   Johan Engelen <j.b.c.engelen@utwente.nl>\r
+ *\r
+ * Copyright (C) 2007 Author\r
+ *\r
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.\r
+ */\r
+\r
+#ifdef HAVE_CONFIG_H\r
+# include <config.h>\r
+#endif\r
+\r
+#include <glibmm/i18n.h>\r
+#include "livepatheffect-editor.h"\r
+#include "verbs.h"\r
+#include "selection.h"\r
+#include "sp-shape.h"\r
+#include "sp-path.h"\r
+#include "live_effects/effect.h"\r
+#include "live_effects/lpeobject.h"\r
+#include "gtkmm/widget.h"\r
+#include <vector>\r
+#include "inkscape.h"\r
+#include "desktop-handles.h"\r
+#include "desktop.h"\r
+#include "document-private.h"\r
+#include "xml/node.h"\r
+#include "xml/document.h"\r
+\r
+namespace Inkscape {\r
+class Application;\r
+\r
+namespace UI {\r
+namespace Dialog {\r
+\r
+\r
+/*####################\r
+ * Callback functions\r
+ */\r
+static void lpeeditor_selection_changed (Inkscape::Selection * selection, gpointer data)\r
+{\r
+    LivePathEffectEditor *lpeeditor = static_cast<LivePathEffectEditor *>(data);\r
+    lpeeditor->onSelectionChanged(selection);\r
+}\r
+\r
+static void lpeeditor_selection_modified (Inkscape::Selection *selection, guint flags, gpointer data)\r
+{\r
+    lpeeditor_selection_changed (selection, data);\r
+}\r
+\r
+\r
+static void lpeeditor_desktop_change(Inkscape::Application*, SPDesktop* desktop, void *data)\r
+{\r
+    if (!desktop) {\r
+        return;\r
+    }\r
+    LivePathEffectEditor* editor = reinterpret_cast<LivePathEffectEditor*>(data);\r
+    editor->setDesktop(desktop);\r
+}\r
+\r
+\r
+\r
+/*#######################\r
+ * LivePathEffectEditor\r
+ */\r
+LivePathEffectEditor::LivePathEffectEditor() \r
+    : Dialog ("dialogs.livepatheffect", SP_VERB_DIALOG_LIVE_PATH_EFFECT),\r
+      combo_effecttype(Inkscape::LivePathEffect::LPETypeConverter),\r
+      button_apply(_("_Apply"), _("Apply chosen effect to selection")),\r
+      button_remove(_("_Remove"), _("Remove effect from selection")),\r
+      effectwidget(NULL),\r
+      explain_label("", Gtk::ALIGN_CENTER),\r
+      effectapplication_frame(_("Apply new effect")),\r
+      effectcontrol_frame(_("Current effect")),\r
+      current_desktop(NULL)\r
+{\r
+    // Top level vbox\r
+    Gtk::VBox *vbox = get_vbox();\r
+    vbox->set_spacing(4);\r
+\r
+    effectapplication_vbox.set_spacing(4);\r
+    effectcontrol_vbox.set_spacing(4);\r
+\r
+    effectapplication_vbox.pack_start(combo_effecttype, true, true);\r
+    effectapplication_vbox.pack_start(button_apply, true, true);\r
+    effectapplication_vbox.pack_start(button_remove, true, true);\r
+    effectapplication_frame.add(effectapplication_vbox);\r
+\r
+    effectcontrol_vbox.pack_start(explain_label, true, true);\r
+    effectcontrol_frame.add(effectcontrol_vbox);\r
+\r
+    vbox->pack_start(effectapplication_frame, true, true);\r
+    vbox->pack_start(effectcontrol_frame, true, true);\r
+\r
+    // connect callback functions to buttons\r
+    button_apply.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onApply));\r
+    button_remove.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onRemove));\r
+\r
+    // connect callback functions to changes in selected desktop.\r
+    g_signal_connect( G_OBJECT(INKSCAPE), "activate_desktop",\r
+                       G_CALLBACK(lpeeditor_desktop_change), this);\r
+\r
+    g_signal_connect( G_OBJECT(INKSCAPE), "deactivate_desktop",\r
+                       G_CALLBACK(lpeeditor_desktop_change), this);\r
+\r
+    setDesktop(SP_ACTIVE_DESKTOP);\r
+    show_all_children();\r
+}\r
+\r
+LivePathEffectEditor::~LivePathEffectEditor() \r
+{\r
+    if (effectwidget) {\r
+        effectcontrol_vbox.remove(*effectwidget);\r
+        effectwidget = NULL;\r
+    }\r
+\r
+    if (current_desktop) {\r
+        selection_changed_connection.disconnect();\r
+        selection_modified_connection.disconnect();\r
+    }\r
+}\r
+\r
+void\r
+LivePathEffectEditor::showParams(LivePathEffect::Effect* effect)\r
+{\r
+    if (effectwidget) {\r
+        effectcontrol_vbox.remove(*effectwidget);\r
+        effectwidget = NULL;\r
+    }\r
+\r
+    explain_label.set_markup("<b>" + effect->getName() + "</b>");\r
+    effectwidget = effect->getWidget();\r
+    if (effectwidget) {\r
+        effectcontrol_vbox.pack_start(*effectwidget, true, true);\r
+    }\r
+\r
+    effectcontrol_vbox.show_all_children();\r
+    // fixme: do resizing of dialog \r
+}\r
+\r
+void\r
+LivePathEffectEditor::showText(Glib::ustring const &str)\r
+{\r
+    if (effectwidget) {\r
+        effectcontrol_vbox.remove(*effectwidget);\r
+        effectwidget = NULL;\r
+    }\r
+\r
+    explain_label.set_label(str);\r
+\r
+    // fixme: do resizing of dialog ?\r
+}\r
+\r
+void\r
+LivePathEffectEditor::set_sensitize_all(bool sensitive)\r
+{\r
+    combo_effecttype.set_sensitive(sensitive);\r
+    button_apply.set_sensitive(sensitive);\r
+    button_remove.set_sensitive(sensitive);\r
+}\r
+\r
+void\r
+LivePathEffectEditor::onSelectionChanged(Inkscape::Selection *sel)\r
+{\r
+    if ( sel && !sel->isEmpty() ) {\r
+        SPItem *item = sel->singleItem();\r
+        if ( item ) {\r
+            if ( SP_IS_SHAPE(item) ) {\r
+                SPShape *shape = SP_SHAPE(item);\r
+                LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(shape);\r
+                set_sensitize_all(true);\r
+                if (lpeobj) {\r
+                    if (lpeobj->lpe) {\r
+                        showParams(lpeobj->lpe);\r
+                    } else {\r
+                        showText(_("Unknown effect is applied"));\r
+                    }\r
+                } else {\r
+                    showText(_("No effect applied"));\r
+                    button_remove.set_sensitive(false);\r
+                }\r
+            } else {\r
+                showText(_("Item is not a shape"));\r
+                set_sensitize_all(false);\r
+            }\r
+        } else {\r
+            showText(_("Only one item can be selected"));\r
+            set_sensitize_all(false);\r
+        }\r
+    } else {\r
+        showText(_("Empty selection"));\r
+        set_sensitize_all(false);\r
+    }\r
+}\r
+\r
+void \r
+LivePathEffectEditor::setDesktop(SPDesktop *desktop)\r
+{\r
+\r
+    if ( desktop == current_desktop ) {\r
+        return;\r
+    }\r
+\r
+    if (current_desktop) {\r
+        selection_changed_connection.disconnect();\r
+        selection_modified_connection.disconnect();\r
+    }\r
+\r
+    current_desktop = desktop;\r
+    if (desktop) {\r
+        Inkscape::Selection *selection = sp_desktop_selection(desktop);\r
+        selection_changed_connection = selection->connectChanged(\r
+            sigc::bind (sigc::ptr_fun(&lpeeditor_selection_changed), this ) );\r
+        selection_modified_connection = selection->connectModified(\r
+            sigc::bind (sigc::ptr_fun(&lpeeditor_selection_modified), this ) );\r
+\r
+        onSelectionChanged(selection);\r
+    } else {\r
+        onSelectionChanged(NULL);\r
+    }\r
+}\r
+\r
+\r
+\r
+\r
+/*########################################################################\r
+# BUTTON CLICK HANDLERS    (callbacks)\r
+########################################################################*/\r
+\r
+void\r
+LivePathEffectEditor::onApply()\r
+{\r
+    Inkscape::Selection *sel = _getSelection();\r
+    if ( sel && !sel->isEmpty() ) {\r
+        SPItem *item = sel->singleItem();\r
+        if ( item && SP_IS_SHAPE(item) ) {\r
+            SPDocument *doc = current_desktop->doc();\r
+\r
+            const Util::EnumData<LivePathEffect::EffectType>* data = combo_effecttype.get_active_data();\r
+            if (!data) return;\r
+\r
+            Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);\r
+            Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");\r
+            repr->setAttribute("effect", data->key.c_str() );\r
+\r
+            SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute\r
+            const gchar * repr_id = repr->attribute("id");\r
+            Inkscape::GC::release(repr);\r
+\r
+            gchar *href = g_strdup_printf("#%s", repr_id);\r
+            sp_shape_set_path_effect(SP_SHAPE(item), href);\r
+            g_free(href);\r
+\r
+            // make sure there is an original-d for paths!!!\r
+            if ( SP_IS_PATH(item) ) {\r
+                Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(item);\r
+                if ( ! pathrepr->attribute("inkscape:original-d") ) {\r
+                    pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));\r
+                }\r
+            }\r
+\r
+            sp_document_done(doc, SP_VERB_DIALOG_LIVE_PATH_EFFECT, \r
+                             _("Create and apply live effect"));\r
+        }\r
+    }\r
+}\r
+\r
+void\r
+LivePathEffectEditor::onRemove()\r
+{\r
+    Inkscape::Selection *sel = _getSelection();\r
+    if ( sel && !sel->isEmpty() ) {\r
+        SPItem *item = sel->singleItem();\r
+        if ( item && SP_IS_SHAPE(item) ) {\r
+            sp_shape_remove_path_effect(SP_SHAPE(item));\r
+            sp_document_done ( sp_desktop_document (current_desktop), SP_VERB_DIALOG_LIVE_PATH_EFFECT, \r
+                               _("Remove live path effect") );\r
+        }\r
+    }\r
+}\r
+\r
+\r
+\r
+} // namespace Dialog\r
+} // namespace UI\r
+} // namespace Inkscape\r
+\r
diff --git a/src/ui/dialog/livepatheffect-editor.h b/src/ui/dialog/livepatheffect-editor.h
new file mode 100644 (file)
index 0000000..5476f8a
--- /dev/null
@@ -0,0 +1,85 @@
+/**\r
+ * \brief LivePathEffect dialog\r
+ *\r
+ * Author:\r
+ *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>\r
+ *\r
+ * Copyright (C) 2007 Author\r
+ *\r
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.\r
+ */\r
+\r
+#ifndef INKSCAPE_UI_DIALOG_LIVE_PATH_EFFECT_H\r
+#define INKSCAPE_UI_DIALOG_LIVE_PATH_EFFECT_H\r
+\r
+#include "dialog.h"\r
+#include "ui/widget/button.h"\r
+\r
+#include <gtkmm/label.h>\r
+#include <gtkmm/comboboxtext.h>\r
+#include <gtkmm/frame.h>\r
+#include "ui/widget/combo-enums.h"\r
+#include "live_effects/effect.h"\r
+\r
+class SPDesktop;\r
+\r
+namespace Inkscape {\r
+\r
+namespace UI {\r
+namespace Dialog {\r
+\r
+class LivePathEffectEditor : public Dialog {\r
+public:\r
+    LivePathEffectEditor();\r
+    virtual ~LivePathEffectEditor();\r
+\r
+    static LivePathEffectEditor *create() { return new LivePathEffectEditor(); }\r
+\r
+    void onSelectionChanged(Inkscape::Selection *sel);\r
+    void setDesktop(SPDesktop *desktop);\r
+\r
+private:\r
+    sigc::connection selection_changed_connection;\r
+    sigc::connection selection_modified_connection;\r
+\r
+    void set_sensitize_all(bool sensitive);\r
+\r
+    void showParams(LivePathEffect::Effect* effect);\r
+    void showText(Glib::ustring const &str);\r
+\r
+    // callback methods for buttons on grids page.\r
+    void onApply();\r
+    void onRemove();\r
+\r
+    Inkscape::UI::Widget::ComboBoxEnum<LivePathEffect::EffectType> combo_effecttype;\r
+    Inkscape::UI::Widget::Button button_apply;\r
+    Inkscape::UI::Widget::Button button_remove;\r
+    Gtk::Widget * effectwidget;\r
+    Gtk::Label explain_label;\r
+    Gtk::Frame effectapplication_frame;\r
+    Gtk::Frame effectcontrol_frame;\r
+    Gtk::VBox effectapplication_vbox;\r
+    Gtk::VBox effectcontrol_vbox;\r
+\r
+    SPDesktop * current_desktop;\r
+\r
+    LivePathEffectEditor(LivePathEffectEditor const &d);\r
+    LivePathEffectEditor& operator=(LivePathEffectEditor const &d);\r
+};\r
+\r
+} // namespace Dialog\r
+} // namespace UI\r
+} // namespace Inkscape\r
+\r
+#endif // INKSCAPE_UI_DIALOG_LIVE_PATH_EFFECT_H\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
index 11c18738c002117163c23dbd3f084aafe638ba38..178e954a41208b710ea881d10c181569d055867e 100644 (file)
@@ -37,10 +37,13 @@ ui_widget_libuiwidget_a_SOURCES =   \
        ui/widget/page-sizer.h          \
        ui/widget/panel.cpp             \
        ui/widget/panel.h               \
+       ui/widget/point.cpp             \
+       ui/widget/point.h               \
        ui/widget/preferences-widget.cpp \
        ui/widget/preferences-widget.h   \
        ui/widget/registered-widget.cpp \
        ui/widget/registered-widget.h   \
+       ui/widget/registered-enums.h   \
        ui/widget/registry.cpp          \
        ui/widget/registry.h            \
        ui/widget/ruler.cpp             \
index b384ca4742e326a0435e2d17031f329765f208f7..22aa55dab3615d4a066a7a9f02a352b555db51ab 100644 (file)
@@ -17,6 +17,7 @@
 #include <gtkmm/liststore.h>
 #include "attr-widget.h"
 #include "util/enums.h"
+#include "ui/widget/labelled.h"
 
 namespace Inkscape {
 namespace UI {
@@ -74,6 +75,23 @@ public:
         row[_columns.data] = 0;
         row[_columns.label] = s;
     }
+
+    void set_active_by_id(E id) {
+        for(Gtk::TreeModel::iterator i = _model->children().begin();
+            i != _model->children().end(); ++i) 
+        {
+            const Util::EnumData<E>* data = (*i)[_columns.data];
+            if(data->id == id) {
+                set_active(i);
+                break;
+            }
+        }
+    };
+
+    void set_active_by_key(const Glib::ustring& key) {
+        set_active_by_id( _converter.get_id_from_key(key) );
+    };
+
 private:
     class Columns : public Gtk::TreeModel::ColumnRecord
     {
@@ -93,6 +111,24 @@ private:
     const Util::EnumDataConverter<E>& _converter;
 };
 
+
+template<typename E> class LabelledComboBoxEnum : public Labelled
+{
+public:
+    LabelledComboBoxEnum( Glib::ustring const &label,
+                          Glib::ustring const &tooltip,
+                          const Util::EnumDataConverter<E>& c,
+                          Glib::ustring const &suffix = "",
+                          Glib::ustring const &icon = "",
+                          bool mnemonic = true)
+        : Labelled(label, tooltip, new ComboBoxEnum<E>(c), suffix, icon, mnemonic)
+    { }
+
+    ComboBoxEnum<E>* getCombobox() {
+        return static_cast< ComboBoxEnum<E>* > (_widget);
+    }
+};
+
 }
 }
 }
diff --git a/src/ui/widget/point.cpp b/src/ui/widget/point.cpp
new file mode 100644 (file)
index 0000000..cfaa430
--- /dev/null
@@ -0,0 +1,237 @@
+/**\r
+ * \brief Point Widget - A labelled text box, with spin buttons and optional\r
+ *        icon or suffix, for entering arbitrary coordinate values.\r
+ *\r
+ * Authors:\r
+ *   Johan Engelen <j.b.c.engelen@utwente.nl>\r
+ *   Carl Hetherington <inkscape@carlh.net>\r
+ *   Derek P. Moore <derekm@hackunix.org>\r
+ *   Bryce Harrington <bryce@bryceharrington.org>\r
+ *\r
+ * Copyright (C) 2007 Authors\r
+ * Copyright (C) 2004 Authors\r
+ *\r
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.\r
+ */\r
+\r
+#ifdef HAVE_CONFIG_H\r
+# include <config.h>\r
+#endif\r
+\r
+\r
+#include "ui/widget/point.h"\r
+#include "ui/widget/labelled.h"\r
+#include "ui/widget/scalar.h"\r
+#include <gtkmm/box.h>\r
+\r
+namespace Inkscape {\r
+namespace UI {\r
+namespace Widget {\r
+\r
+/**\r
+ * Construct a Point Widget.\r
+ *\r
+ * \param label     Label.\r
+ * \param suffix    Suffix, placed after the widget (defaults to "").\r
+ * \param icon      Icon filename, placed before the label (defaults to "").\r
+ * \param mnemonic  Mnemonic toggle; if true, an underscore (_) in the label\r
+ *                  indicates the next character should be used for the\r
+ *                  mnemonic accelerator key (defaults to false).\r
+ */\r
+Point::Point(Glib::ustring const &label, Glib::ustring const &tooltip,\r
+               Glib::ustring const &suffix,\r
+               Glib::ustring const &icon,\r
+               bool mnemonic)\r
+    : Labelled(label, tooltip, new Gtk::VBox(), suffix, icon, mnemonic),\r
+      setProgrammatically(false),\r
+      xwidget("X:",""),\r
+      ywidget("Y:","")\r
+{\r
+    static_cast<Gtk::VBox*>(_widget)->pack_start(xwidget, true, true);\r
+    static_cast<Gtk::VBox*>(_widget)->pack_start(ywidget, true, true);\r
+    static_cast<Gtk::VBox*>(_widget)->show_all_children();\r
+}\r
+\r
+/**\r
+ * Construct a Point Widget.\r
+ *\r
+ * \param label     Label.\r
+ * \param digits    Number of decimal digits to display.\r
+ * \param suffix    Suffix, placed after the widget (defaults to "").\r
+ * \param icon      Icon filename, placed before the label (defaults to "").\r
+ * \param mnemonic  Mnemonic toggle; if true, an underscore (_) in the label\r
+ *                  indicates the next character should be used for the\r
+ *                  mnemonic accelerator key (defaults to false).\r
+ */\r
+Point::Point(Glib::ustring const &label, Glib::ustring const &tooltip,\r
+               unsigned digits,\r
+               Glib::ustring const &suffix,\r
+               Glib::ustring const &icon,\r
+               bool mnemonic)\r
+    : Labelled(label, tooltip, new Gtk::VBox(), suffix, icon, mnemonic),\r
+      setProgrammatically(false),\r
+      xwidget("X:","", digits),\r
+      ywidget("Y:","", digits)\r
+{\r
+    static_cast<Gtk::VBox*>(_widget)->pack_start(xwidget, true, true);\r
+    static_cast<Gtk::VBox*>(_widget)->pack_start(ywidget, true, true);\r
+    static_cast<Gtk::VBox*>(_widget)->show_all_children();\r
+}\r
+\r
+/**\r
+ * Construct a Point Widget.\r
+ *\r
+ * \param label     Label.\r
+ * \param adjust    Adjustment to use for the SpinButton.\r
+ * \param digits    Number of decimal digits to display (defaults to 0).\r
+ * \param suffix    Suffix, placed after the widget (defaults to "").\r
+ * \param icon      Icon filename, placed before the label (defaults to "").\r
+ * \param mnemonic  Mnemonic toggle; if true, an underscore (_) in the label\r
+ *                  indicates the next character should be used for the\r
+ *                  mnemonic accelerator key (defaults to true).\r
+ */\r
+Point::Point(Glib::ustring const &label, Glib::ustring const &tooltip,\r
+               Gtk::Adjustment &adjust,\r
+               unsigned digits,\r
+               Glib::ustring const &suffix,\r
+               Glib::ustring const &icon,\r
+               bool mnemonic)\r
+    : Labelled(label, tooltip, new Gtk::VBox(), suffix, icon, mnemonic),\r
+      setProgrammatically(false),\r
+      xwidget("X:","", adjust, digits),\r
+      ywidget("Y:","", adjust, digits)\r
+{\r
+    static_cast<Gtk::VBox*>(_widget)->pack_start(xwidget, true, true);\r
+    static_cast<Gtk::VBox*>(_widget)->pack_start(ywidget, true, true);\r
+    static_cast<Gtk::VBox*>(_widget)->show_all_children();\r
+}\r
+\r
+/** Fetches the precision of the spin buton */\r
+unsigned\r
+Point::getDigits() const\r
+{\r
+    return xwidget.getDigits();\r
+}\r
+\r
+/** Gets the current step ingrement used by the spin button */\r
+double\r
+Point::getStep() const\r
+{\r
+    return xwidget.getStep();\r
+}\r
+\r
+/** Gets the current page increment used by the spin button */\r
+double\r
+Point::getPage() const\r
+{\r
+    return xwidget.getPage();\r
+}\r
+\r
+/** Gets the minimum range value allowed for the spin button */\r
+double\r
+Point::getRangeMin() const\r
+{\r
+    return xwidget.getRangeMin();\r
+}\r
+\r
+/** Gets the maximum range value allowed for the spin button */\r
+double\r
+Point::getRangeMax() const\r
+{\r
+    return xwidget.getRangeMax();\r
+}\r
+\r
+/** Get the value in the spin_button . */\r
+double\r
+Point::getXValue() const\r
+{\r
+    return xwidget.getValue();\r
+}\r
+double\r
+Point::getYValue() const\r
+{\r
+    return ywidget.getValue();\r
+}\r
+\r
+/** Get the value spin_button represented as an integer. */\r
+int\r
+Point::getXValueAsInt() const\r
+{\r
+    return xwidget.getValueAsInt();\r
+}\r
+int\r
+Point::getYValueAsInt() const\r
+{\r
+    return ywidget.getValueAsInt();\r
+}\r
+\r
+\r
+/** Sets the precision to be displayed by the spin button */\r
+void\r
+Point::setDigits(unsigned digits)\r
+{\r
+    xwidget.setDigits(digits);\r
+    ywidget.setDigits(digits);\r
+}\r
+\r
+/** Sets the step and page increments for the spin button */\r
+void\r
+Point::setIncrements(double step, double page)\r
+{\r
+    xwidget.setIncrements(step, page);\r
+    ywidget.setIncrements(step, page);\r
+}\r
+\r
+/** Sets the minimum and maximum range allowed for the spin button */\r
+void\r
+Point::setRange(double min, double max)\r
+{\r
+    xwidget.setRange(min, max);\r
+    ywidget.setRange(min, max);\r
+}\r
+\r
+/** Sets the value of the spin button */\r
+void\r
+Point::setValue(double xvalue, double yvalue)\r
+{\r
+    setProgrammatically = true; // callback is supposed to reset back, if it cares\r
+    xwidget.setValue(xvalue);\r
+    ywidget.setValue(yvalue);\r
+}\r
+\r
+/** Manually forces an update of the spin button */\r
+void\r
+Point::update() {\r
+    xwidget.update();\r
+    ywidget.update();\r
+}\r
+\r
+\r
+\r
+/** Signal raised when the spin button's value changes */\r
+Glib::SignalProxy0<void>\r
+Point::signal_x_value_changed()\r
+{\r
+    return xwidget.signal_value_changed();\r
+}\r
+Glib::SignalProxy0<void>\r
+Point::signal_y_value_changed()\r
+{\r
+    return ywidget.signal_value_changed();\r
+}\r
+\r
+\r
+} // namespace Widget\r
+} // namespace UI\r
+} // namespace Inkscape\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
diff --git a/src/ui/widget/point.h b/src/ui/widget/point.h
new file mode 100644 (file)
index 0000000..291ae13
--- /dev/null
@@ -0,0 +1,96 @@
+/**\r
+ * \brief Point Widget - A labelled text box, with spin buttons and optional\r
+ *        icon or suffix, for entering arbitrary coordinate values.\r
+ *\r
+ * Authors:\r
+ *   Johan Engelen <j.b.c.engelen@utwente.nl>\r
+ *   Carl Hetherington <inkscape@carlh.net>\r
+ *   Derek P. Moore <derekm@hackunix.org>\r
+ *   Bryce Harrington <bryce@bryceharrington.org>\r
+ *\r
+ * Copyright (C) 2007 Authors\r
+ * Copyright (C) 2004 Authors\r
+ *\r
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.\r
+ */\r
+\r
+#ifndef INKSCAPE_UI_WIDGET_POINT_H\r
+#define INKSCAPE_UI_WIDGET_POINT_H\r
+\r
+#include <gtkmm/adjustment.h>\r
+#include <gtkmm/spinbutton.h>\r
+\r
+#include "ui/widget/labelled.h"\r
+#include "ui/widget/scalar.h"\r
+\r
+namespace Inkscape {\r
+namespace UI {\r
+namespace Widget {\r
+\r
+class Point : public Labelled\r
+{\r
+public:\r
+    Point( Glib::ustring const &label,\r
+           Glib::ustring const &tooltip,\r
+           Glib::ustring const &suffix = "",\r
+           Glib::ustring const &icon = "",\r
+           bool mnemonic = true);\r
+    Point( Glib::ustring const &label,\r
+           Glib::ustring const &tooltip,\r
+           unsigned digits,\r
+           Glib::ustring const &suffix = "",\r
+           Glib::ustring const &icon = "",\r
+           bool mnemonic = true);\r
+    Point( Glib::ustring const &label,\r
+           Glib::ustring const &tooltip,\r
+           Gtk::Adjustment &adjust,\r
+           unsigned digits = 0,\r
+           Glib::ustring const &suffix = "",\r
+           Glib::ustring const &icon = "",\r
+           bool mnemonic = true);\r
+\r
+    unsigned getDigits() const;\r
+    double  getStep() const;\r
+    double  getPage() const;\r
+    double  getRangeMin() const;\r
+    double  getRangeMax() const;\r
+    bool    getSnapToTicks() const;\r
+    double  getXValue() const;\r
+    double  getYValue() const;\r
+    int     getXValueAsInt() const;\r
+    int     getYValueAsInt() const;\r
+\r
+    void    setDigits(unsigned digits);\r
+    void    setIncrements(double step, double page);\r
+    void    setRange(double min, double max);\r
+    void    setValue(double xvalue, double yvalue);\r
+\r
+    void    update();\r
+\r
+    Glib::SignalProxy0<void> signal_x_value_changed();\r
+    Glib::SignalProxy0<void> signal_y_value_changed();\r
+\r
+    bool setProgrammatically; // true if the value was set by setValue, not changed by the user; \r
+                              // if a callback checks it, it must reset it back to false\r
+\r
+protected:\r
+    Scalar xwidget, ywidget;\r
+\r
+};\r
+\r
+} // namespace Widget\r
+} // namespace UI\r
+} // namespace Inkscape\r
+\r
+#endif // INKSCAPE_UI_WIDGET_POINT_H\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
diff --git a/src/ui/widget/registered-enums.h b/src/ui/widget/registered-enums.h
new file mode 100644 (file)
index 0000000..bfa866e
--- /dev/null
@@ -0,0 +1,110 @@
+/**\r
+ * \brief Simplified management of enumerations in the UI as combobox.\r
+ *\r
+ * Authors:\r
+ *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>\r
+ *\r
+ * Copyright (C) 2007 Authors\r
+ *\r
+ * Released under GNU GPL.  Read the file 'COPYING' for more information.\r
+ */\r
+\r
+#ifndef INKSCAPE_UI_WIDGET_REGISTERED_ENUMS_H\r
+#define INKSCAPE_UI_WIDGET_REGISTERED_ENUMS_H\r
+\r
+#include "ui/widget/combo-enums.h"\r
+#include "ui/widget/registered-widget.h"\r
+\r
+namespace Inkscape {\r
+namespace UI {\r
+namespace Widget {\r
+\r
+template<typename E> class RegisteredEnum : public RegisteredWidget\r
+{\r
+public:\r
+    RegisteredEnum() {\r
+        labelled = NULL;\r
+    }\r
+\r
+    ~RegisteredEnum() {\r
+        _changed_connection.disconnect();\r
+        if (labelled)\r
+            delete labelled;\r
+    }\r
+\r
+    void init ( const Glib::ustring& label,\r
+                const Glib::ustring& tip,\r
+                const Glib::ustring& key,\r
+                const Util::EnumDataConverter<E>& c,\r
+                Registry& wr,\r
+                Inkscape::XML::Node* repr_in,\r
+                SPDocument *doc_in)\r
+    {\r
+        init_parent(key, wr, repr_in, doc_in);\r
+\r
+        labelled = new LabelledComboBoxEnum<E> (label, tip, c);\r
+\r
+        _changed_connection = combobox()->signal_changed().connect (sigc::mem_fun (*this, &RegisteredEnum::on_changed));\r
+    }\r
+\r
+    inline void init ( const Glib::ustring& label,\r
+                       const Glib::ustring& key,\r
+                       Registry& wr)\r
+    {\r
+        init(label, key, wr, NULL, NULL);\r
+    }\r
+\r
+    void set_active_by_id (E id) {\r
+        combobox()->set_active_by_id(id);\r
+    };\r
+\r
+    void set_active_by_key (const Glib::ustring& key) {\r
+        combobox()->set_active_by_key(key);\r
+    }\r
+\r
+    inline const Util::EnumData<E>* get_active_data() {\r
+        return combobox()->get_active_data();\r
+    }\r
+\r
+    ComboBoxEnum<E> * combobox() {\r
+        if (labelled) {\r
+            return labelled->getCombobox();\r
+        } else {\r
+            return NULL;\r
+        }\r
+    }\r
+\r
+    LabelledComboBoxEnum<E> * labelled;\r
+    sigc::connection _changed_connection;\r
+\r
+protected:\r
+    void on_changed() {\r
+        if (_wr->isUpdating())\r
+            return;\r
+        _wr->setUpdating (true);\r
+\r
+        const Util::EnumData<E>* data = combobox()->get_active_data();\r
+        if (data) {\r
+            write_to_xml(data->key.c_str());\r
+        }\r
+\r
+        _wr->setUpdating (false);\r
+    }\r
+};\r
+\r
+}\r
+}\r
+}\r
+\r
+#endif\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
index 6ef264b0d13cffc1f9fb1f2235e41e95c360e3b1..8a569bc3271ac7ebff60035008b9c5d466e2426d 100644 (file)
@@ -2,13 +2,14 @@
  *
  *
  * Authors:
+ *   Johan Engelen <j.b.c.engelen@utwente.nl>
  *   bulia byak <buliabyak@users.sf.net>
  *   Bryce W. Harrington <bryce@bryceharrington.org>
  *   Lauris Kaplinski <lauris@kaplinski.com>
  *   Jon Phillips <jon@rejon.org>
  *   Ralf Stephan <ralf@ark.in-berlin.de> (Gtkmm)
  *
- * Copyright (C) 2000 - 2005 Authors
+ * Copyright (C) 2000 - 2007 Authors
  *
  * Released under GNU GPL.  Read the file 'COPYING' for more information
  */
@@ -21,6 +22,8 @@
 #include "ui/widget/color-picker.h"
 #include "ui/widget/registry.h"
 #include "ui/widget/scalar-unit.h"
+#include "ui/widget/point.h"
+#include "widgets/spinbutton-events.h"
 
 #include "helper/units.h"
 #include "xml/repr.h"
@@ -35,6 +38,9 @@
 #include "registered-widget.h"
 #include "verbs.h"
 
+// for interruptability bug:
+#include "display/sp-canvas.h"
+
 namespace Inkscape {
 namespace UI {
 namespace Widget {
@@ -44,6 +50,31 @@ namespace Widget {
 //---------------------------------------------------
 
 
+void
+RegisteredWidget::write_to_xml(const char * svgstr)
+{
+    // Use local repr here. When repr is specified, use that one, but
+    // if repr==NULL, get the repr of namedview of active desktop.
+    Inkscape::XML::Node *local_repr = repr;
+    SPDocument *local_doc = doc;
+    if (!local_repr) {
+        // no repr specified, use active desktop's namedview's repr
+        SPDesktop* dt = SP_ACTIVE_DESKTOP;
+        local_repr = SP_OBJECT_REPR (sp_desktop_namedview(dt));
+        local_doc = sp_desktop_document(dt);
+    }
+
+    bool saved = sp_document_get_undo_sensitive (local_doc);
+    sp_document_set_undo_sensitive (local_doc, false);
+    if (!write_undo) local_repr->setAttribute(_key.c_str(), svgstr);
+    local_doc->rroot->setAttribute("sodipodi:modified", "true");
+    sp_document_set_undo_sensitive (local_doc, saved);
+    if (write_undo) {
+        local_repr->setAttribute(_key.c_str(), svgstr);
+        sp_document_done (local_doc, event_type, event_description);
+    }
+}
+
 
 //====================================================
 
@@ -61,20 +92,15 @@ RegisteredCheckButton::~RegisteredCheckButton()
 void
 RegisteredCheckButton::init (const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key, Registry& wr, bool right, Inkscape::XML::Node* repr_in, SPDocument *doc_in)
 {
+    init_parent(key, wr, repr_in, doc_in);
+
     _button = new Gtk::CheckButton;
     _tt.set_tip (*_button, tip);
     Gtk::Label *l = new Gtk::Label (label);
     l->set_use_underline (true);
     _button->add (*manage (l));
     _button->set_alignment (right? 1.0 : 0.0, 0.5);
-    _key = key;
-    _wr = &wr;
     _toggled_connection = _button->signal_toggled().connect (sigc::mem_fun (*this, &RegisteredCheckButton::on_toggled));
-
-    repr = repr_in;
-    doc = doc_in;
-    if (repr && !doc)  // doc cannot be NULL when repr is not NULL
-        g_error("Initialization of registered widget using defined repr but with doc==NULL");
 }
 
 void
@@ -93,33 +119,15 @@ RegisteredCheckButton::on_toggled()
     if (_wr->isUpdating())
         return;
 
-    // Use local repr here. When repr is specified, use that one, but
-    // if repr==NULL, get the repr of namedview of active desktop.
-    Inkscape::XML::Node *local_repr = repr;
-    SPDocument *local_doc = doc;
-    if (!local_repr) {
-        // no repr specified, use active desktop's namedview's repr
-        SPDesktop *dt = SP_ACTIVE_DESKTOP;
-        if (!dt)
-            return;
-        local_repr = SP_OBJECT_REPR (sp_desktop_namedview(dt));
-        local_doc = sp_desktop_document(dt);
-    }
-
     _wr->setUpdating (true);
 
-       //The slave button is greyed out if the master button is unchecked
-       for (std::list<Gtk::ToggleButton*>::const_iterator i = _slavebuttons.begin(); i != _slavebuttons.end(); i++) {
-               (*i)->set_sensitive(_button->get_active());
-       }
-       
-    bool saved = sp_document_get_undo_sensitive (local_doc);
-    sp_document_set_undo_sensitive (local_doc, false);
-    sp_repr_set_boolean(local_repr, _key.c_str(), _button->get_active());
-    local_doc->rroot->setAttribute("sodipodi:modified", "true");
-    sp_document_set_undo_sensitive (local_doc, saved);
-    sp_document_done (local_doc, SP_VERB_NONE,
-                      /* TODO: annotate */ "registered-widget.cpp: RegisteredCheckButton::on_toggled");
+    write_to_xml(_button->get_active() ? "true" : "false");
+    //The slave button is greyed out if the master button is unchecked
+    for (std::list<Gtk::ToggleButton*>::const_iterator i = _slavebuttons.begin(); i != _slavebuttons.end(); i++) {
+        (*i)->set_sensitive(_button->get_active());
+    }
+
+    write_to_xml(_button->get_active() ? "true" : "false");
 
     _wr->setUpdating (false);
 }
@@ -139,19 +147,14 @@ RegisteredUnitMenu::~RegisteredUnitMenu()
 void
 RegisteredUnitMenu::init (const Glib::ustring& label, const Glib::ustring& key, Registry& wr, Inkscape::XML::Node* repr_in, SPDocument *doc_in)
 {
+    init_parent(key, wr, repr_in, doc_in);
+
     _label = new Gtk::Label (label, 1.0, 0.5);
     _label->set_use_underline (true);
     _sel = new UnitMenu ();
     _label->set_mnemonic_widget (*_sel);
     _sel->setUnitType (UNIT_TYPE_LINEAR);
-    _wr = &wr;
-    _key = key;
     _changed_connection = _sel->signal_changed().connect (sigc::mem_fun (*this, &RegisteredUnitMenu::on_changed));
-
-    repr = repr_in;
-    doc = doc_in;
-    if (repr && !doc)  // doc cannot be NULL when repr is not NULL
-        g_warning("Initialization of registered widget using defined repr but with doc==NULL");
 }
 
 void
@@ -166,31 +169,12 @@ RegisteredUnitMenu::on_changed()
     if (_wr->isUpdating())
         return;
 
-    // Use local repr here. When repr is specified, use that one, but
-    // if repr==NULL, get the repr of namedview of active desktop.
-    Inkscape::XML::Node *local_repr = repr;
-    SPDocument *local_doc = doc;
-    if (!local_repr) {
-        // no repr specified, use active desktop's namedview's repr
-        SPDesktop *dt = SP_ACTIVE_DESKTOP;
-        if (!dt)
-            return;
-        local_repr = SP_OBJECT_REPR (sp_desktop_namedview(dt));
-        local_doc = sp_desktop_document(dt);
-    }
-
     Inkscape::SVGOStringStream os;
     os << _sel->getUnitAbbr();
 
     _wr->setUpdating (true);
 
-    bool saved = sp_document_get_undo_sensitive (local_doc);
-    sp_document_set_undo_sensitive (local_doc, false);
-    local_repr->setAttribute(_key.c_str(), os.str().c_str());
-    local_doc->rroot->setAttribute("sodipodi:modified", "true");
-    sp_document_set_undo_sensitive (local_doc, saved);
-    sp_document_done (local_doc, SP_VERB_NONE,
-                      /* TODO: annotate */ "registered-widget.cpp: RegisteredUnitMenu::on_changed");
+    write_to_xml(os.str().c_str());
 
     _wr->setUpdating (false);
 }
@@ -210,19 +194,14 @@ RegisteredScalarUnit::~RegisteredScalarUnit()
 void
 RegisteredScalarUnit::init (const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key, const RegisteredUnitMenu &rum, Registry& wr, Inkscape::XML::Node* repr_in, SPDocument *doc_in)
 {
+    init_parent(key, wr, repr_in, doc_in);
+
     _widget = new ScalarUnit (label, tip, UNIT_TYPE_LINEAR, "", "", rum._sel);
     _widget->initScalar (-1e6, 1e6);
     _widget->setUnit (rum._sel->getUnitAbbr());
     _widget->setDigits (2);
-    _key = key;
     _um = rum._sel;
     _value_changed_connection = _widget->signal_value_changed().connect (sigc::mem_fun (*this, &RegisteredScalarUnit::on_value_changed));
-    _wr = &wr;
-
-    repr = repr_in;
-    doc = doc_in;
-    if (repr && !doc)  // doc cannot be NULL when repr is not NULL
-        g_warning("Initialization of registered widget using defined repr but with doc==NULL");
 }
 
 ScalarUnit*
@@ -244,37 +223,83 @@ RegisteredScalarUnit::on_value_changed()
     if (_wr->isUpdating())
         return;
 
-    // Use local repr here. When repr is specified, use that one, but
-    // if repr==NULL, get the repr of namedview of active desktop.
-    Inkscape::XML::Node *local_repr = repr;
-    SPDocument *local_doc = doc;
-    if (!local_repr) {
-        // no repr specified, use active desktop's namedview's repr
-        SPDesktop *dt = SP_ACTIVE_DESKTOP;
-        if (!dt)
-            return;
-        local_repr = SP_OBJECT_REPR (sp_desktop_namedview(dt));
-        local_doc = sp_desktop_document(dt);
-    }
+    _wr->setUpdating (true);
 
     Inkscape::SVGOStringStream os;
     os << _widget->getValue("");
     if (_um)
         os << _um->getUnitAbbr();
 
+    write_to_xml(os.str().c_str());
+
+    _wr->setUpdating (false);
+}
+
+
+RegisteredScalar::RegisteredScalar()
+{
+    _widget = NULL;
+}
+
+RegisteredScalar::~RegisteredScalar()
+{
+    if (_widget) 
+        delete _widget;
+
+    _value_changed_connection.disconnect();
+}
+
+void
+RegisteredScalar::init ( const Glib::ustring& label, const Glib::ustring& tip, 
+                         const Glib::ustring& key, Registry& wr, Inkscape::XML::Node* repr_in,
+                         SPDocument * doc_in )
+{
+    init_parent(key, wr, repr_in, doc_in);
+
+    _widget = new Scalar (label, tip);
+    _widget->setRange (-1e6, 1e6);
+    _widget->setDigits (2);
+    _widget->setIncrements(0.1, 1.0);
+    _value_changed_connection = _widget->signal_value_changed().connect (sigc::mem_fun (*this, &RegisteredScalar::on_value_changed));
+}
+
+Scalar*
+RegisteredScalar::getS()
+{
+    return _widget;
+}
+
+void
+RegisteredScalar::setValue (double val)
+{
+    _widget->setValue (val);
+    on_value_changed();
+}
+
+void
+RegisteredScalar::on_value_changed()
+{
+    if (_wr->isUpdating())
+        return;
     _wr->setUpdating (true);
 
-    bool saved = sp_document_get_undo_sensitive (local_doc);
-    sp_document_set_undo_sensitive (local_doc, false);
-    local_repr->setAttribute(_key.c_str(), os.str().c_str());
-    local_doc->rroot->setAttribute("sodipodi:modified", "true");
-    sp_document_set_undo_sensitive (local_doc, saved);
-    sp_document_done (local_doc, SP_VERB_NONE,
-                      /* TODO: annotate */ "registered-widget.cpp: RegisteredScalarUnit::on_value_changed");
+    // FIXME:  gtk bug?
+    // disable interruptibility: see http://inkscape.svn.sourceforge.net/viewvc/inkscape/inkscape/trunk/src/ui/widget/selected-style.cpp?r1=13149&r2=13257&sortby=date
+    SPDesktop* dt = SP_ACTIVE_DESKTOP;
+    sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(dt), 0);
+
+    Inkscape::SVGOStringStream os;
+    os << _widget->getValue();
+
+    write_to_xml(os.str().c_str());
+
+    // resume interruptibility
+    sp_canvas_end_forced_full_redraws(sp_desktop_canvas(dt));
 
     _wr->setUpdating (false);
 }
 
+
 RegisteredColorPicker::RegisteredColorPicker()
 : _label(0), _cp(0)
 {
@@ -290,19 +315,15 @@ RegisteredColorPicker::~RegisteredColorPicker()
 void
 RegisteredColorPicker::init (const Glib::ustring& label, const Glib::ustring& title, const Glib::ustring& tip, const Glib::ustring& ckey, const Glib::ustring& akey, Registry& wr, Inkscape::XML::Node* repr_in, SPDocument *doc_in)
 {
+    init_parent("", wr, repr_in, doc_in);
+
     _label = new Gtk::Label (label, 1.0, 0.5);
     _label->set_use_underline (true);
     _cp = new ColorPicker (title,tip,0,true);
     _label->set_mnemonic_widget (*_cp);
     _ckey = ckey;
     _akey = akey;
-    _wr = &wr;
     _changed_connection = _cp->connectChanged (sigc::mem_fun (*this, &RegisteredColorPicker::on_changed));
-
-    repr = repr_in;
-    doc = doc_in;
-    if (repr && !doc)  // doc cannot be NULL when repr is not NULL
-        g_warning("Initialization of registered widget using defined repr but with doc==NULL");
 }
 
 void
@@ -370,7 +391,8 @@ RegisteredSuffixedInteger::~RegisteredSuffixedInteger()
 void
 RegisteredSuffixedInteger::init (const Glib::ustring& label, const Glib::ustring& suffix, const Glib::ustring& key, Registry& wr, Inkscape::XML::Node* repr_in, SPDocument *doc_in)
 {
-    _key = key;
+    init_parent(key, wr, repr_in, doc_in);
+
     _label = new Gtk::Label (label);
     _label->set_alignment (1.0, 0.5);
     _label->set_use_underline();
@@ -381,12 +403,6 @@ RegisteredSuffixedInteger::init (const Glib::ustring& label, const Glib::ustring
     _hbox.pack_start (*_suffix, false, false, 0);
 
     _changed_connection = _adj.signal_value_changed().connect (sigc::mem_fun(*this, &RegisteredSuffixedInteger::on_value_changed));
-    _wr = &wr;
-
-    repr = repr_in;
-    doc = doc_in;
-    if (repr && !doc)  // doc cannot be NULL when repr is not NULL
-        g_warning("Initialization of registered widget using defined repr but with doc==NULL");
 }
 
 void
@@ -403,26 +419,11 @@ RegisteredSuffixedInteger::on_value_changed()
 
     _wr->setUpdating (true);
 
-    // Use local repr here. When repr is specified, use that one, but
-    // if repr==NULL, get the repr of namedview of active desktop.
-    Inkscape::XML::Node *local_repr = repr;
-    SPDocument *local_doc = doc;
-    if (!local_repr) {
-        // no repr specified, use active desktop's namedview's repr
-        SPDesktop *dt = SP_ACTIVE_DESKTOP;
-        if (!dt)
-            return;
-        local_repr = SP_OBJECT_REPR (sp_desktop_namedview(dt));
-        local_doc = sp_desktop_document(dt);
-    }
-
     Inkscape::SVGOStringStream os;
     int value = int(_adj.get_value());
     os << value;
 
-    local_repr->setAttribute(_key.c_str(), os.str().c_str());
-    sp_document_done(local_doc, SP_VERB_NONE,
-                     /* TODO: annotate */ "registered-widget.cpp: RegisteredSuffixedInteger::on_value_changed");
+    write_to_xml(os.str().c_str());
 
     _wr->setUpdating (false);
 }
@@ -443,6 +444,8 @@ const Glib::ustring& label1, const Glib::ustring& label2,
 const Glib::ustring& tip1, const Glib::ustring& tip2,
 const Glib::ustring& key, Registry& wr, Inkscape::XML::Node* repr_in, SPDocument *doc_in)
 {
+    init_parent(key, wr, repr_in, doc_in);
+
     _hbox = new Gtk::HBox;
     _hbox->add (*manage (new Gtk::Label (label)));
     _rb1 = manage (new Gtk::RadioButton (label1, true));
@@ -453,14 +456,7 @@ const Glib::ustring& key, Registry& wr, Inkscape::XML::Node* repr_in, SPDocument
     _rb2->set_active();
     _tt.set_tip (*_rb1, tip1);
     _tt.set_tip (*_rb2, tip2);
-    _key = key;
-    _wr = &wr;
     _changed_connection = _rb1->signal_toggled().connect (sigc::mem_fun (*this, &RegisteredRadioButtonPair::on_value_changed));
-
-    repr = repr_in;
-    doc = doc_in;
-    if (repr && !doc)  // doc cannot be NULL when repr is not NULL
-        g_error("Initialization of registered widget using defined repr but with doc==NULL");
 }
 
 void
@@ -476,29 +472,72 @@ RegisteredRadioButtonPair::on_value_changed()
     if (_wr->isUpdating())
         return;
 
-    // Use local repr here. When repr is specified, use that one, but
-    // if repr==NULL, get the repr of namedview of active desktop.
-    Inkscape::XML::Node *local_repr = repr;
-    SPDocument *local_doc = doc;
-    if (!local_repr) {
-        // no repr specified, use active desktop's namedview's repr
-        SPDesktop *dt = SP_ACTIVE_DESKTOP;
-        if (!dt)
-            return;
-        local_repr = SP_OBJECT_REPR (sp_desktop_namedview(dt));
-        local_doc = sp_desktop_document(dt);
-    }
-
     _wr->setUpdating (true);
 
     bool second = _rb2->get_active();
-    bool saved = sp_document_get_undo_sensitive (local_doc);
-    sp_document_set_undo_sensitive (local_doc, false);
-    local_repr->setAttribute(_key.c_str(), second ? "true" : "false");
-    local_doc->rroot->setAttribute("sodipodi:modified", "true");
-    sp_document_set_undo_sensitive (local_doc, saved);
-    sp_document_done (local_doc, SP_VERB_NONE,
-                      /* TODO: annotate */ "registered-widget.cpp: RegisteredRadioButtonPair::on_value_changed");
+    write_to_xml(second ? "true" : "false");
+
+    _wr->setUpdating (false);
+}
+
+/*#########################################
+ * Registered POINT
+ */
+
+RegisteredPoint::RegisteredPoint()
+{
+    _widget = NULL;
+}
+
+RegisteredPoint::~RegisteredPoint()
+{
+    if (_widget) 
+        delete _widget;
+
+    _value_x_changed_connection.disconnect();
+    _value_y_changed_connection.disconnect();
+}
+
+void
+RegisteredPoint::init ( const Glib::ustring& label, const Glib::ustring& tip, 
+                        const Glib::ustring& key, Registry& wr, Inkscape::XML::Node* repr_in, 
+                        SPDocument* doc_in )
+{
+    init_parent(key, wr, repr_in, doc_in);
+
+    _widget = new Point (label, tip);
+    _widget->setRange (-1e6, 1e6);
+    _widget->setDigits (2);
+    _widget->setIncrements(0.1, 1.0);
+    _value_x_changed_connection = _widget->signal_x_value_changed().connect (sigc::mem_fun (*this, &RegisteredPoint::on_value_changed));
+    _value_y_changed_connection = _widget->signal_y_value_changed().connect (sigc::mem_fun (*this, &RegisteredPoint::on_value_changed));
+}
+
+Point*
+RegisteredPoint::getPoint()
+{
+    return _widget;
+}
+
+void
+RegisteredPoint::setValue (double xval, double yval)
+{
+    _widget->setValue(xval, yval);
+    on_value_changed();
+}
+
+void
+RegisteredPoint::on_value_changed()
+{
+    if (_wr->isUpdating())
+        return;
+
+    _wr->setUpdating (true);
+
+    Inkscape::SVGOStringStream os;
+    os << _widget->getXValue() << "," << _widget->getYValue();
+
+    write_to_xml(os.str().c_str());
 
     _wr->setUpdating (false);
 }
index 8e54e1bbd6c19137127921d91dc03fbc03e6d83d..008ca0077f4e9d3423027aa0241fc06b4d2a30e3 100644 (file)
@@ -14,6 +14,9 @@
 
 #include <gtkmm/box.h>
 #include <gtkmm/adjustment.h>
+#include <gtkmm/tooltips.h>
+
+#include "xml/node.h"
 
 class SPUnit;
 class SPDocument;
@@ -31,10 +34,53 @@ namespace Widget {
 
 class ColorPicker;
 class Registry;
+class Scalar;
 class ScalarUnit;
 class UnitMenu;
+class Point;
+
+class RegisteredWidget {
+public:
+    void set_undo_parameters(const unsigned int _event_type, Glib::ustring _event_description)
+    {
+        event_type = _event_type;
+        event_description = _event_description;
+        write_undo = true;
+    }
+
+protected:
+    RegisteredWidget()
+    {
+        _wr = NULL;
+        repr = NULL;
+        doc = NULL;
+        write_undo = false;
+    }
+
+    void init_parent(const Glib::ustring& key, Registry& wr, Inkscape::XML::Node* repr_in, SPDocument *doc_in)
+    {
+        _wr = &wr;
+        _key = key;
+        repr = repr_in;
+        doc = doc_in;
+        if (repr && !doc)  // doc cannot be NULL when repr is not NULL
+            g_warning("Initialization of registered widget using defined repr but with doc==NULL");
+    }
+
+    void write_to_xml(const char * svgstr);
 
-class RegisteredCheckButton {
+    Registry * _wr;
+    Glib::ustring _key;
+    Inkscape::XML::Node * repr;
+    SPDocument * doc;
+    unsigned int event_type;
+    Glib::ustring event_description;
+    bool write_undo;
+};
+
+//#######################################################
+
+class RegisteredCheckButton : public RegisteredWidget {
 public:
     RegisteredCheckButton();
     ~RegisteredCheckButton();
@@ -55,18 +101,23 @@ public:
 protected:
     Gtk::Tooltips     _tt;
     sigc::connection  _toggled_connection;
-    Registry    *_wr;
-    Glib::ustring      _key;
     void on_toggled();
-    Inkscape::XML::Node *repr;
-    SPDocument *doc;
 };
 
-class RegisteredUnitMenu {
+class RegisteredUnitMenu : public RegisteredWidget {
 public:
     RegisteredUnitMenu();
     ~RegisteredUnitMenu();
-    void init (const Glib::ustring& label, const Glib::ustring& key, Registry& wr, Inkscape::XML::Node* repr_in=NULL, SPDocument *doc_in=NULL);
+    void init ( const Glib::ustring& label,
+                const Glib::ustring& key,
+                Registry& wr,
+                Inkscape::XML::Node* repr_in,
+                SPDocument *doc_in);
+    inline void init ( const Glib::ustring& label, 
+                       const Glib::ustring& key, 
+                       Registry& wr)
+        { init(label, key, wr, NULL, NULL); };
+
     void setUnit (const SPUnit*);
     Gtk::Label   *_label;
     UnitMenu     *_sel;
@@ -74,13 +125,9 @@ public:
 
 protected:
     void on_changed();
-    Registry     *_wr;
-    Glib::ustring _key;
-    Inkscape::XML::Node *repr;
-    SPDocument *doc;
 };
 
-class RegisteredScalarUnit {
+class RegisteredScalarUnit : public RegisteredWidget {
 public:
     RegisteredScalarUnit();
     ~RegisteredScalarUnit();
@@ -89,8 +136,15 @@ public:
             const Glib::ustring& key, 
             const RegisteredUnitMenu &rum,
             Registry& wr,
-            Inkscape::XML::Node* repr_in=NULL,
-            SPDocument *doc_in=NULL);
+            Inkscape::XML::Node* repr_in,
+            SPDocument *doc_in);
+    inline void init ( const Glib::ustring& label, 
+                       const Glib::ustring& tip, 
+                       const Glib::ustring& key, 
+                       const RegisteredUnitMenu &rum,
+                       Registry& wr)
+        { init(label, tip, key, rum, wr, NULL, NULL); };
+
     ScalarUnit* getSU();
     void setValue (double);
 
@@ -98,14 +152,35 @@ protected:
     ScalarUnit   *_widget;
     sigc::connection  _value_changed_connection;
     UnitMenu         *_um;
-    Registry         *_wr;
-    Glib::ustring    _key;
     void on_value_changed();
-    Inkscape::XML::Node *repr;
-    SPDocument *doc;
 };
 
-class RegisteredColorPicker {
+class RegisteredScalar : public RegisteredWidget {
+public:
+    RegisteredScalar();
+    ~RegisteredScalar();
+    void init (const Glib::ustring& label, 
+            const Glib::ustring& tip, 
+            const Glib::ustring& key, 
+            Registry& wr,
+            Inkscape::XML::Node* repr_in,
+            SPDocument *doc_in);
+    inline void init ( const Glib::ustring& label, 
+                       const Glib::ustring& tip, 
+                       const Glib::ustring& key, 
+                       Registry& wr)
+        { init(label, tip, key, wr, NULL, NULL); };
+
+    Scalar* getS();
+    void setValue (double);
+
+protected:
+    Scalar   *_widget;
+    sigc::connection  _value_changed_connection;
+    void on_value_changed();
+};
+
+class RegisteredColorPicker : public RegisteredWidget {
 public:
     RegisteredColorPicker();
     ~RegisteredColorPicker();
@@ -115,8 +190,16 @@ public:
             const Glib::ustring& ckey, 
             const Glib::ustring& akey,
             Registry& wr,
-            Inkscape::XML::Node* repr_in=NULL,
-            SPDocument *doc_in=NULL);
+            Inkscape::XML::Node* repr_in,
+            SPDocument *doc_in);
+    inline void init ( const Glib::ustring& label, 
+                       const Glib::ustring& title, 
+                       const Glib::ustring& tip, 
+                       const Glib::ustring& ckey, 
+                       const Glib::ustring& akey, 
+                       Registry& wr)
+        { init(label, title, tip, ckey, akey, wr, NULL, NULL); };
+
     void setRgba32 (guint32);
     void closeWindow();
 
@@ -125,14 +208,11 @@ public:
 
 protected:
     Glib::ustring _ckey, _akey;
-    Registry      *_wr;
     void on_changed (guint32);
     sigc::connection _changed_connection;
-    Inkscape::XML::Node *repr;
-    SPDocument *doc;
 };
 
-class RegisteredSuffixedInteger {
+class RegisteredSuffixedInteger : public RegisteredWidget {
 public:
     RegisteredSuffixedInteger();
     ~RegisteredSuffixedInteger();
@@ -140,8 +220,14 @@ public:
                const Glib::ustring& label2, 
                const Glib::ustring& key,
                Registry& wr,
-               Inkscape::XML::Node* repr_in=NULL,
-               SPDocument *doc_in=NULL);
+               Inkscape::XML::Node* repr_in,
+               SPDocument *doc_in);
+    inline void init ( const Glib::ustring& label1, 
+                       const Glib::ustring& label2, 
+                       const Glib::ustring& key, 
+                       Registry& wr)
+        { init(label1, label2, key, wr, NULL, NULL); };
+
     void setValue (int);
     Gtk::Label *_label;
     Gtk::HBox _hbox;
@@ -150,15 +236,11 @@ protected:
     Gtk::SpinButton *_sb;
     Gtk::Adjustment _adj;
     Gtk::Label      *_suffix;
-    Glib::ustring   _key;
-    Registry        *_wr;
     sigc::connection _changed_connection;
     void on_value_changed();
-    Inkscape::XML::Node *repr;
-    SPDocument *doc;
 };
 
-class RegisteredRadioButtonPair {
+class RegisteredRadioButtonPair : public RegisteredWidget {
 public:
     RegisteredRadioButtonPair();
     ~RegisteredRadioButtonPair();
@@ -169,23 +251,52 @@ public:
                const Glib::ustring& tip2, 
                const Glib::ustring& key,
                Registry& wr,
-               Inkscape::XML::Node* repr_in=NULL,
-               SPDocument *doc_in=NULL);
+               Inkscape::XML::Node* repr_in,
+               SPDocument *doc_in);
+    inline void init ( const Glib::ustring& label, 
+                       const Glib::ustring& label1, 
+                       const Glib::ustring& label2, 
+                       const Glib::ustring& tip1, 
+                       const Glib::ustring& tip2, 
+                       const Glib::ustring& key, 
+                       Registry& wr)
+        { init(label, label1, label2, tip1, tip2, key, wr, NULL, NULL); };
+
     void setValue (bool second);
     Gtk::HBox *_hbox;
 
 protected:
     Gtk::RadioButton *_rb1, *_rb2;
     Gtk::Tooltips     _tt;
-    Glib::ustring   _key;
-    Registry        *_wr;
     sigc::connection _changed_connection;
     void on_value_changed();
-    Inkscape::XML::Node *repr;
-    SPDocument *doc;
 };
 
+class RegisteredPoint : public RegisteredWidget {
+public:
+    RegisteredPoint();
+    ~RegisteredPoint();
+    void init (const Glib::ustring& label, 
+               const Glib::ustring& tip, 
+               const Glib::ustring& key, 
+               Registry& wr,
+               Inkscape::XML::Node* repr_in,
+               SPDocument *doc_in);
+    inline void init ( const Glib::ustring& label, 
+                       const Glib::ustring& tip, 
+                       const Glib::ustring& key, 
+                       Registry& wr)
+        { init(label, tip, key, wr, NULL, NULL); };
+
+    Point* getPoint();
+    void setValue (double xval, double yval);
 
+protected:
+    Point   *_widget;
+    sigc::connection  _value_x_changed_connection;
+    sigc::connection  _value_y_changed_connection;
+    void on_value_changed();
+};
 
 } // namespace Widget
 } // namespace UI
index dc9ebc38e56f1a090119daca18623f2e1ca678e8..fbf13ef7ad5e3ae796383d6fa15862db79be2056 100644 (file)
@@ -1745,6 +1745,9 @@ DialogVerb::perform(SPAction *action, void *data, void *pdata)
         case SP_VERB_DIALOG_LAYERS:
             show_panel( Inkscape::UI::Dialogs::LayersPanel::getInstance(), "dialogs.layers", SP_VERB_DIALOG_LAYERS );
             break;
+        case SP_VERB_DIALOG_LIVE_PATH_EFFECT:
+            dt->_dlg_mgr->showDialog("LivePathEffect");
+            break;
         case SP_VERB_DIALOG_FILTER_EFFECTS:
             dt->_dlg_mgr->showDialog("FilterEffectsDialog");
             break;
@@ -2529,6 +2532,8 @@ Verb *Verb::_base_verbs[] = {
                    N_("Query information about extensions"), NULL),
     new DialogVerb(SP_VERB_DIALOG_LAYERS, "DialogLayers", N_("Layer_s..."),
                    N_("View Layers"), "layers"),
+    new DialogVerb(SP_VERB_DIALOG_LIVE_PATH_EFFECT, "DialogLivePathEffect", N_("Live Path Effect..."),
+                   N_("View Live Path Effect parameters"), NULL),
     new DialogVerb(SP_VERB_DIALOG_FILTER_EFFECTS, "DialogFilterEffects", N_("Filter Effects..."),
                    N_("Manage SVG filter effects"), NULL),
 
index ebe5e2fedb1a1fe08bca8ca381032b468b998a9a..a9100de7179312882fa249f166fe433aba093f1d 100644 (file)
@@ -218,6 +218,7 @@ enum {
     SP_VERB_DIALOG_INPUT,
     SP_VERB_DIALOG_EXTENSIONEDITOR,
     SP_VERB_DIALOG_LAYERS,
+    SP_VERB_DIALOG_LIVE_PATH_EFFECT,
     SP_VERB_DIALOG_FILTER_EFFECTS,
     /* Help */
     SP_VERB_HELP_KEYS,