Code

noop: Set svn:eol-style to native on all .cpp and .h files under src. (find \( ...
authorpjrm <pjrm@users.sourceforge.net>
Tue, 7 Apr 2009 06:32:25 +0000 (06:32 +0000)
committerpjrm <pjrm@users.sourceforge.net>
Tue, 7 Apr 2009 06:32:25 +0000 (06:32 +0000)
src/2geom/bezier-clipping.cpp
src/2geom/chebyshev.cpp
src/2geom/chebyshev.h
src/2geom/utils.cpp
src/extension/internal/javafx-out.cpp
src/extension/internal/javafx-out.h
src/helper/geom-curves.h
src/live_effects/lpe-rough-hatches.cpp
src/live_effects/lpe-rough-hatches.h

index c8d99c4308fbcdb22c7918627d774205b0144ab3..96a06376cb43a1b9a2299f2bf27e08290fc0d75d 100644 (file)
-/*\r
- * Implement the Bezier clipping algorithm for finding\r
- * Bezier curve intersection points and collinear normals\r
- *\r
- * Authors:\r
- *      Marco Cecchetti <mrcekets at gmail.com>\r
- *\r
- * Copyright 2008  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
-\r
-\r
-\r
-#include <2geom/basic-intersection.h>\r
-#include <2geom/choose.h>\r
-#include <2geom/point.h>\r
-#include <2geom/interval.h>\r
-#include <2geom/bezier.h>\r
-//#include <2geom/convex-cover.h>\r
-#include <2geom/numeric/matrix.h>\r
-\r
-#include <cassert>\r
-#include <vector>\r
-#include <algorithm>\r
-#include <utility>\r
-//#include <iomanip>\r
-\r
-\r
-\r
-\r
-#define VERBOSE 0\r
-#define CHECK 0\r
-\r
-\r
-namespace Geom {\r
-\r
-namespace detail { namespace bezier_clipping {\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// for debugging\r
-//\r
-\r
-inline\r
-void print(std::vector<Point> const& cp, const char* msg = "")\r
-{\r
-    std::cerr << msg << std::endl;\r
-    for (size_t i = 0; i < cp.size(); ++i)\r
-        std::cerr << i << " : " << cp[i] << std::endl;\r
-}\r
-\r
-template< class charT >\r
-inline\r
-std::basic_ostream<charT> &\r
-operator<< (std::basic_ostream<charT> & os, const Interval & I)\r
-{\r
-    os << "[" << I.min() << ", " << I.max() << "]";\r
-    return os;\r
-}\r
-\r
-inline\r
-double angle (std::vector<Point> const& A)\r
-{\r
-    size_t n = A.size() -1;\r
-    double a = std::atan2(A[n][Y] - A[0][Y], A[n][X] - A[0][X]);\r
-    return (180 * a / M_PI);\r
-}\r
-\r
-inline\r
-size_t get_precision(Interval const& I)\r
-{\r
-    double d = I.extent();\r
-    double e = 0.1, p = 10;\r
-    int n = 0;\r
-    while (n < 16 && d < e)\r
-    {\r
-        p *= 10;\r
-        e = 1/p;\r
-        ++n;\r
-    }\r
-    return n;\r
-}\r
-\r
-inline\r
-void range_assertion(int k, int m, int n, const char* msg)\r
-{\r
-    if ( k < m || k > n)\r
-    {\r
-        std::cerr << "range assertion failed: \n"\r
-                  << msg << std::endl\r
-                  << "value: " << k\r
-                  << "  range: " << m << ", " << n << std::endl;\r
-        assert (k >= m && k <= n);\r
-    }\r
-}\r
-\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-//  convex hull\r
-\r
-/*\r
- * return true in case the oriented polyline p0, p1, p2 is a right turn\r
- */\r
-inline\r
-bool is_a_right_turn (Point const& p0, Point const& p1, Point const& p2)\r
-{\r
-    if (p1 == p2) return false;\r
-    Point q1 = p1 - p0;\r
-    Point q2 = p2 - p0;\r
-    if (q1 == -q2) return false;\r
-    return (cross (q1, q2) < 0);\r
-}\r
-\r
-/*\r
- * return true if p < q wrt the lexicographyc order induced by the coordinates\r
- */\r
-struct lex_less\r
-{\r
-    bool operator() (Point const& p, Point const& q)\r
-    {\r
-      return ((p[X] < q[X]) || (p[X] == q[X] && p[Y] < q[Y]));\r
-    }\r
-};\r
-\r
-/*\r
- * return true if p > q wrt the lexicographyc order induced by the coordinates\r
- */\r
-struct lex_greater\r
-{\r
-    bool operator() (Point const& p, Point const& q)\r
-    {\r
-        return ((p[X] > q[X]) || (p[X] == q[X] && p[Y] > q[Y]));\r
-    }\r
-};\r
-\r
-/*\r
- * Compute the convex hull of a set of points.\r
- * The implementation is based on the Andrew's scan algorithm\r
- * note: in the Bezier clipping for collinear normals it seems\r
- * to be more stable wrt the Graham's scan algorithm and in general\r
- * a bit quikier\r
- */\r
-void convex_hull (std::vector<Point> & P)\r
-{\r
-    size_t n = P.size();\r
-    if (n < 2)  return;\r
-    std::sort(P.begin(), P.end(), lex_less());\r
-    if (n < 4) return;\r
-    // upper hull\r
-    size_t u = 2;\r
-    for (size_t i = 2; i < n; ++i)\r
-    {\r
-        while (u > 1 && !is_a_right_turn(P[u-2], P[u-1], P[i]))\r
-        {\r
-            --u;\r
-        }\r
-        std::swap(P[u], P[i]);\r
-        ++u;\r
-    }\r
-    std::sort(P.begin() + u, P.end(), lex_greater());\r
-    std::rotate(P.begin(), P.begin() + 1, P.end());\r
-    // lower hull\r
-    size_t l = u;\r
-    size_t k = u - 1;\r
-    for (size_t i = l; i < n; ++i)\r
-    {\r
-        while (l > k && !is_a_right_turn(P[l-2], P[l-1], P[i]))\r
-        {\r
-            --l;\r
-        }\r
-        std::swap(P[l], P[i]);\r
-        ++l;\r
-    }\r
-    P.resize(l);\r
-}\r
-\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-//  numerical routines\r
-\r
-/*\r
- * Compute the binomial coefficient (n, k)\r
- */\r
-inline\r
-double binomial(unsigned int n, unsigned int k)\r
-{\r
-    return choose<double>(n, k);\r
-}\r
-\r
-/*\r
- * Compute the determinant of the 2x2 matrix with column the point P1, P2\r
- */\r
-inline\r
-double det(Point const& P1, Point const& P2)\r
-{\r
-    return P1[X]*P2[Y] - P1[Y]*P2[X];\r
-}\r
-\r
-/*\r
- * Solve the linear system [P1,P2] * P = Q\r
- * in case there isn't exactly one solution the routine returns false\r
- */\r
-inline\r
-bool solve(Point & P, Point const& P1, Point const& P2, Point const& Q)\r
-{\r
-    double d = det(P1, P2);\r
-    if (d == 0)  return false;\r
-    d = 1 / d;\r
-    P[X] = det(Q, P2) * d;\r
-    P[Y] = det(P1, Q) * d;\r
-    return true;\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// interval routines\r
-\r
-/*\r
- * Map the sub-interval I in [0,1] into the interval J and assign it to J\r
- */\r
-inline\r
-void map_to(Interval & J, Interval const& I)\r
-{\r
-    double length = J.extent();\r
-    J[1] = I.max() * length + J[0];\r
-    J[0] = I.min() * length + J[0];\r
-}\r
-\r
-/*\r
- * The interval [1,0] is used to represent the empty interval, this routine\r
- * is just an helper function for creating such an interval\r
- */\r
-inline\r
-Interval make_empty_interval()\r
-{\r
-    Interval I(0);\r
-    I[0] = 1;\r
-    return I;\r
-}\r
-\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// bezier curve routines\r
-\r
-/*\r
- * Return true if all the Bezier curve control points are near,\r
- * false otherwise\r
- */\r
-inline\r
-bool is_constant(std::vector<Point> const& A, double precision = EPSILON)\r
-{\r
-    for (unsigned int i = 1; i < A.size(); ++i)\r
-    {\r
-        if(!are_near(A[i], A[0], precision))\r
-            return false;\r
-    }\r
-    return true;\r
-}\r
-\r
-/*\r
- * Compute the hodograph of the bezier curve B and return it in D\r
- */\r
-inline\r
-void derivative(std::vector<Point> & D, std::vector<Point> const& B)\r
-{\r
-    D.clear();\r
-    size_t sz = B.size();\r
-    if (sz == 0) return;\r
-    if (sz == 1)\r
-    {\r
-        D.resize(1, Point(0,0));\r
-        return;\r
-    }\r
-    size_t n = sz-1;\r
-    D.reserve(n);\r
-    for (size_t i = 0; i < n; ++i)\r
-    {\r
-        D.push_back(n*(B[i+1] - B[i]));\r
-    }\r
-}\r
-\r
-/*\r
- * Compute the hodograph of the Bezier curve B rotated of 90 degree\r
- * and return it in D; we have N(t) orthogonal to B(t) for any t\r
- */\r
-inline\r
-void normal(std::vector<Point> & N, std::vector<Point> const& B)\r
-{\r
-    derivative(N,B);\r
-    for (size_t i = 0; i < N.size(); ++i)\r
-    {\r
-        N[i] = rot90(N[i]);\r
-    }\r
-}\r
-\r
-/*\r
- *  Compute the portion of the Bezier curve "B" wrt the interval [0,t]\r
- */\r
-inline\r
-void left_portion(Coord t, std::vector<Point> & B)\r
-{\r
-    size_t n = B.size();\r
-    for (size_t i = 1; i < n; ++i)\r
-    {\r
-        for (size_t j = n-1; j > i-1 ; --j)\r
-        {\r
-            B[j] = lerp(t, B[j-1], B[j]);\r
-        }\r
-    }\r
-}\r
-\r
-/*\r
- *  Compute the portion of the Bezier curve "B" wrt the interval [t,1]\r
- */\r
-inline\r
-void right_portion(Coord t, std::vector<Point> & B)\r
-{\r
-    size_t n = B.size();\r
-    for (size_t i = 1; i < n; ++i)\r
-    {\r
-        for (size_t j = 0; j < n-i; ++j)\r
-        {\r
-            B[j] = lerp(t, B[j], B[j+1]);\r
-        }\r
-    }\r
-}\r
-\r
-/*\r
- *  Compute the portion of the Bezier curve "B" wrt the interval "I"\r
- */\r
-inline\r
-void portion (std::vector<Point> & B , Interval const& I)\r
-{\r
-    if (I.min() == 0)\r
-    {\r
-        if (I.max() == 1)  return;\r
-        left_portion(I.max(), B);\r
-        return;\r
-    }\r
-    right_portion(I.min(), B);\r
-    if (I.max() == 1)  return;\r
-    double t = I.extent() / (1 - I.min());\r
-    left_portion(t, B);\r
-}\r
-\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// tags\r
-\r
-struct intersection_point_tag;\r
-struct collinear_normal_tag;\r
-template <typename Tag>\r
-void clip(Interval & dom,\r
-          std::vector<Point> const& A,\r
-          std::vector<Point> const& B);\r
-template <typename Tag>\r
-void iterate(std::vector<Interval>& domsA,\r
-             std::vector<Interval>& domsB,\r
-             std::vector<Point> const& A,\r
-             std::vector<Point> const& B,\r
-             Interval const& domA,\r
-             Interval const& domB,\r
-             double precision );\r
-\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// intersection\r
-\r
-/*\r
- *  Make up an orientation line using the control points c[i] and c[j]\r
- *  the line is returned in the output parameter "l" in the form of a 3 element\r
- *  vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.\r
- */\r
-inline\r
-void orientation_line (std::vector<double> & l,\r
-                       std::vector<Point> const& c,\r
-                       size_t i, size_t j)\r
-{\r
-    l[0] = c[j][Y] - c[i][Y];\r
-    l[1] = c[i][X] - c[j][X];\r
-    l[2] = cross(c[i], c[j]);\r
-    double length = std::sqrt(l[0] * l[0] + l[1] * l[1]);\r
-    assert (length != 0);\r
-    l[0] /= length;\r
-    l[1] /= length;\r
-    l[2] /= length;\r
-}\r
-\r
-/*\r
- * Pick up an orientation line for the Bezier curve "c" and return it in\r
- * the output parameter "l"\r
- */\r
-inline\r
-void pick_orientation_line (std::vector<double> & l,\r
-                            std::vector<Point> const& c)\r
-{\r
-    size_t i = c.size();\r
-    while (--i > 0 && are_near(c[0], c[i]))\r
-    {}\r
-    if (i == 0)\r
-    {\r
-        // this should never happen because when a new curve portion is created\r
-        // we check that it is not constant;\r
-        // however this requires that the precision used in the is_constant\r
-        // routine has to be the same used here in the are_near test\r
-        assert(i != 0);\r
-    }\r
-    orientation_line(l, c, 0, i);\r
-    //std::cerr << "i = " << i << std::endl;\r
-}\r
-\r
-/*\r
- *  Make up an orientation line for constant bezier curve;\r
- *  the orientation line is made up orthogonal to the other curve base line;\r
- *  the line is returned in the output parameter "l" in the form of a 3 element\r
- *  vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.\r
- */\r
-inline\r
-void orthogonal_orientation_line (std::vector<double> & l,\r
-                                  std::vector<Point> const& c,\r
-                                  Point const& p)\r
-{\r
-    if (is_constant(c))\r
-    {\r
-        // this should never happen\r
-        assert(!is_constant(c));\r
-    }\r
-    std::vector<Point> ol(2);\r
-    ol[0] = p;\r
-    ol[1] = (c.back() - c.front()).cw() + p;\r
-    orientation_line(l, ol, 0, 1);\r
-}\r
-\r
-/*\r
- *  Compute the signed distance of the point "P" from the normalized line l\r
- */\r
-inline\r
-double distance (Point const& P, std::vector<double> const& l)\r
-{\r
-    return l[X] * P[X] + l[Y] * P[Y] + l[2];\r
-}\r
-\r
-/*\r
- * Compute the min and max distance of the control points of the Bezier\r
- * curve "c" from the normalized orientation line "l".\r
- * This bounds are returned through the output Interval parameter"bound".\r
- */\r
-inline\r
-void fat_line_bounds (Interval& bound,\r
-                      std::vector<Point> const& c,\r
-                      std::vector<double> const& l)\r
-{\r
-    bound[0] = 0;\r
-    bound[1] = 0;\r
-    double d;\r
-    for (size_t i = 0; i < c.size(); ++i)\r
-    {\r
-        d = distance(c[i], l);\r
-        if (bound[0] > d)  bound[0] = d;\r
-        if (bound[1] < d)  bound[1] = d;\r
-    }\r
-}\r
-\r
-/*\r
- * return the x component of the intersection point between the line\r
- * passing through points p1, p2 and the line Y = "y"\r
- */\r
-inline\r
-double intersect (Point const& p1, Point const& p2, double y)\r
-{\r
-    // we are sure that p2[Y] != p1[Y] because this routine is called\r
-    // only when the lower or the upper bound is crossed\r
-    double dy = (p2[Y] - p1[Y]);\r
-    double s = (y - p1[Y]) / dy;\r
-    return (p2[X]-p1[X])*s + p1[X];\r
-}\r
-\r
-/*\r
- * Clip the Bezier curve "B" wrt the fat line defined by the orientation\r
- * line "l" and the interval range "bound", the new parameter interval for\r
- * the clipped curve is returned through the output parameter "dom"\r
- */\r
-void clip_interval (Interval& dom,\r
-                    std::vector<Point> const& B,\r
-                    std::vector<double> const& l,\r
-                    Interval const& bound)\r
-{\r
-    double n = B.size() - 1;  // number of sub-intervals\r
-    std::vector<Point> D;     // distance curve control points\r
-    D.reserve (B.size());\r
-    double d;\r
-    for (size_t i = 0; i < B.size(); ++i)\r
-    {\r
-        d = distance (B[i], l);\r
-        D.push_back (Point(i/n, d));\r
-    }\r
-    //print(D);\r
-\r
-    convex_hull(D);\r
-    std::vector<Point> & p = D;\r
-    //print(p);\r
-\r
-    bool plower, phigher;\r
-    bool clower, chigher;\r
-    double t, tmin = 1, tmax = 0;\r
-//    std::cerr << "bound : " << bound << std::endl;\r
-\r
-    plower = (p[0][Y] < bound.min());\r
-    phigher = (p[0][Y] > bound.max());\r
-    if (!(plower || phigher))  // inside the fat line\r
-    {\r
-        if (tmin > p[0][X])  tmin = p[0][X];\r
-        if (tmax < p[0][X])  tmax = p[0][X];\r
-//        std::cerr << "0 : inside " << p[0]\r
-//                  << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;\r
-    }\r
-\r
-    for (size_t i = 1; i < p.size(); ++i)\r
-    {\r
-        clower = (p[i][Y] < bound.min());\r
-        chigher = (p[i][Y] > bound.max());\r
-        if (!(clower || chigher))  // inside the fat line\r
-        {\r
-            if (tmin > p[i][X])  tmin = p[i][X];\r
-            if (tmax < p[i][X])  tmax = p[i][X];\r
-//            std::cerr << i << " : inside " << p[i]\r
-//                      << " : tmin = " << tmin << ", tmax = " << tmax\r
-//                      << std::endl;\r
-        }\r
-        if (clower != plower)  // cross the lower bound\r
-        {\r
-            t = intersect(p[i-1], p[i], bound.min());\r
-            if (tmin > t)  tmin = t;\r
-            if (tmax < t)  tmax = t;\r
-            plower = clower;\r
-//            std::cerr << i << " : lower " << p[i]\r
-//                      << " : tmin = " << tmin << ", tmax = " << tmax\r
-//                      << std::endl;\r
-        }\r
-        if (chigher != phigher)  // cross the upper bound\r
-        {\r
-            t = intersect(p[i-1], p[i], bound.max());\r
-            if (tmin > t)  tmin = t;\r
-            if (tmax < t)  tmax = t;\r
-            phigher = chigher;\r
-//            std::cerr << i << " : higher " << p[i]\r
-//                      << " : tmin = " << tmin << ", tmax = " << tmax\r
-//                      << std::endl;\r
-        }\r
-    }\r
-\r
-    // we have to test the closing segment for intersection\r
-    size_t last = p.size() - 1;\r
-    clower = (p[0][Y] < bound.min());\r
-    chigher = (p[0][Y] > bound.max());\r
-    if (clower != plower)  // cross the lower bound\r
-    {\r
-        t = intersect(p[last], p[0], bound.min());\r
-        if (tmin > t)  tmin = t;\r
-        if (tmax < t)  tmax = t;\r
-//        std::cerr << "0 : lower " << p[0]\r
-//                  << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;\r
-    }\r
-    if (chigher != phigher)  // cross the upper bound\r
-    {\r
-        t = intersect(p[last], p[0], bound.max());\r
-        if (tmin > t)  tmin = t;\r
-        if (tmax < t)  tmax = t;\r
-//        std::cerr << "0 : higher " << p[0]\r
-//                  << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;\r
-    }\r
-\r
-    dom[0] = tmin;\r
-    dom[1] = tmax;\r
-}\r
-\r
-/*\r
- *  Clip the Bezier curve "B" wrt the Bezier curve "A" for individuating\r
- *  intersection points the new parameter interval for the clipped curve\r
- *  is returned through the output parameter "dom"\r
- */\r
-template <>\r
-inline\r
-void clip<intersection_point_tag> (Interval & dom,\r
-                                   std::vector<Point> const& A,\r
-                                   std::vector<Point> const& B)\r
-{\r
-    std::vector<double> bl(3);\r
-    Interval bound;\r
-    if (is_constant(A))\r
-    {\r
-        Point M = middle_point(A.front(), A.back());\r
-        orthogonal_orientation_line(bl, B, M);\r
-    }\r
-    else\r
-    {\r
-        pick_orientation_line(bl, A);\r
-    }\r
-    fat_line_bounds(bound, A, bl);\r
-    clip_interval(dom, B, bl, bound);\r
-}\r
-\r
-\r
-///////////////////////////////////////////////////////////////////////////////\r
-// collinear normal\r
-\r
-/*\r
- * Compute a closed focus for the Bezier curve B and return it in F\r
- * A focus is any curve through which all lines perpendicular to B(t) pass.\r
- */\r
-inline\r
-void make_focus (std::vector<Point> & F, std::vector<Point> const& B)\r
-{\r
-    assert (B.size() > 2);\r
-    size_t n = B.size() - 1;\r
-    normal(F, B);\r
-    Point c(1, 1);\r
-#if VERBOSE\r
-    if (!solve(c, F[0], -F[n-1], B[n]-B[0]))\r
-    {\r
-        std::cerr << "make_focus: unable to make up a closed focus" << std::endl;\r
-    }\r
-#else\r
-    solve(c, F[0], -F[n-1], B[n]-B[0]);\r
-#endif\r
-//    std::cerr << "c = " << c << std::endl;\r
-\r
-\r
-    // B(t) + c(t) * N(t)\r
-    double n_inv = 1 / (double)(n);\r
-    Point c0ni;\r
-    F.push_back(c[1] * F[n-1]);\r
-    F[n] += B[n];\r
-    for (size_t i = n-1; i > 0; --i)\r
-    {\r
-        F[i] *= -c[0];\r
-        c0ni = F[i];\r
-        F[i] += (c[1] * F[i-1]);\r
-        F[i] *= (i * n_inv);\r
-        F[i] -= c0ni;\r
-        F[i] += B[i];\r
-    }\r
-    F[0] *= c[0];\r
-    F[0] += B[0];\r
-}\r
-\r
-/*\r
- * Compute the projection on the plane (t, d) of the control points\r
- * (t, u, D(t,u)) where D(t,u) = <(B(t) - F(u)), B'(t)> with 0 <= t, u <= 1\r
- * B is a Bezier curve and F is a focus of another Bezier curve.\r
- * See Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping.\r
- */\r
-void distance_control_points (std::vector<Point> & D,\r
-                              std::vector<Point> const& B,\r
-                              std::vector<Point> const& F)\r
-{\r
-    assert (B.size() > 1);\r
-    assert (F.size() > 0);\r
-    const size_t n = B.size() - 1;\r
-    const size_t m = F.size() - 1;\r
-    const size_t r = 2 * n - 1;\r
-    const double r_inv = 1 / (double)(r);\r
-    D.clear();\r
-    D.reserve (B.size() * F.size());\r
-\r
-    std::vector<Point> dB;\r
-    dB.reserve(n);\r
-    for (size_t k = 0; k < n; ++k)\r
-    {\r
-        dB.push_back (B[k+1] - B[k]);\r
-    }\r
-    NL::Matrix dBB(n,B.size());\r
-    for (size_t i = 0; i < n; ++i)\r
-        for (size_t j = 0; j < B.size(); ++j)\r
-            dBB(i,j) = dot (dB[i], B[j]);\r
-    NL::Matrix dBF(n, F.size());\r
-    for (size_t i = 0; i < n; ++i)\r
-        for (size_t j = 0; j < F.size(); ++j)\r
-            dBF(i,j) = dot (dB[i], F[j]);\r
-\r
-    size_t k0, kn, l;\r
-    double bc, bri;\r
-    Point dij;\r
-    std::vector<double> d(F.size());\r
-    for (size_t i = 0; i <= r; ++i)\r
-    {\r
-        for (size_t j = 0; j <= m; ++j)\r
-        {\r
-            d[j] = 0;\r
-        }\r
-        k0 = std::max(i, n) - n;\r
-        kn = std::min(i, n-1);\r
-        bri = n / binomial(r,i);\r
-        for (size_t k = k0; k <= kn; ++k)\r
-        {\r
-            //if (k > i || (i-k) > n) continue;\r
-            l = i - k;\r
-#if CHECK\r
-            assert (l <= n);\r
-#endif\r
-            bc = bri * binomial(n,l) * binomial(n-1, k);\r
-            for (size_t j = 0; j <= m; ++j)\r
-            {\r
-                //d[j] += bc * dot(dB[k], B[l] - F[j]);\r
-                d[j] += bc * (dBB(k,l) - dBF(k,j));\r
-            }\r
-        }\r
-        double dmin, dmax;\r
-        dmin = dmax = d[m];\r
-        for (size_t j = 0; j < m; ++j)\r
-        {\r
-            if (dmin > d[j])  dmin = d[j];\r
-            if (dmax < d[j])  dmax = d[j];\r
-        }\r
-        dij[0] = i * r_inv;\r
-        dij[1] = dmin;\r
-        D.push_back (dij);\r
-        dij[1] = dmax;\r
-        D.push_back (dij);\r
-    }\r
-}\r
-\r
-/*\r
- * Clip the Bezier curve "B" wrt the focus "F"; the new parameter interval for\r
- * the clipped curve is returned through the output parameter "dom"\r
- */\r
-void clip_interval (Interval& dom,\r
-                    std::vector<Point> const& B,\r
-                    std::vector<Point> const& F)\r
-{\r
-    std::vector<Point> D;     // distance curve control points\r
-    distance_control_points(D, B, F);\r
-    //print(D, "D");\r
-//    ConvexHull chD(D);\r
-//    std::vector<Point>& p = chD.boundary; // convex hull vertices\r
-\r
-    convex_hull(D);\r
-    std::vector<Point> & p = D;\r
-    //print(p, "CH(D)");\r
-\r
-    bool plower, clower;\r
-    double t, tmin = 1, tmax = 0;\r
-\r
-    plower = (p[0][Y] < 0);\r
-    if (p[0][Y] == 0)  // on the x axis\r
-    {\r
-        if (tmin > p[0][X])  tmin = p[0][X];\r
-        if (tmax < p[0][X])  tmax = p[0][X];\r
-//        std::cerr << "0 : on x axis " << p[0]\r
-//                  << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;\r
-    }\r
-\r
-    for (size_t i = 1; i < p.size(); ++i)\r
-    {\r
-        clower = (p[i][Y] < 0);\r
-        if (p[i][Y] == 0)  // on x axis\r
-        {\r
-            if (tmin > p[i][X])  tmin = p[i][X];\r
-            if (tmax < p[i][X])  tmax = p[i][X];\r
-//            std::cerr << i << " : on x axis " << p[i]\r
-//                      << " : tmin = " << tmin << ", tmax = " << tmax\r
-//                      << std::endl;\r
-        }\r
-        else if (clower != plower)  // cross the x axis\r
-        {\r
-            t = intersect(p[i-1], p[i], 0);\r
-            if (tmin > t)  tmin = t;\r
-            if (tmax < t)  tmax = t;\r
-            plower = clower;\r
-//            std::cerr << i << " : lower " << p[i]\r
-//                      << " : tmin = " << tmin << ", tmax = " << tmax\r
-//                      << std::endl;\r
-        }\r
-    }\r
-\r
-    // we have to test the closing segment for intersection\r
-    size_t last = p.size() - 1;\r
-    clower = (p[0][Y] < 0);\r
-    if (clower != plower)  // cross the x axis\r
-    {\r
-        t = intersect(p[last], p[0], 0);\r
-        if (tmin > t)  tmin = t;\r
-        if (tmax < t)  tmax = t;\r
-//        std::cerr << "0 : lower " << p[0]\r
-//                  << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;\r
-    }\r
-    dom[0] = tmin;\r
-    dom[1] = tmax;\r
-}\r
-\r
-/*\r
- *  Clip the Bezier curve "B" wrt the Bezier curve "A" for individuating\r
- *  points which have collinear normals; the new parameter interval\r
- *  for the clipped curve is returned through the output parameter "dom"\r
- */\r
-template <>\r
-inline\r
-void clip<collinear_normal_tag> (Interval & dom,\r
-                                 std::vector<Point> const& A,\r
-                                 std::vector<Point> const& B)\r
-{\r
-    std::vector<Point> F;\r
-    make_focus(F, A);\r
-    clip_interval(dom, B, F);\r
-}\r
-\r
-\r
-\r
-const double MAX_PRECISION = 1e-8;\r
-const double MIN_CLIPPED_SIZE_THRESHOLD = 0.8;\r
-const Interval UNIT_INTERVAL(0,1);\r
-const Interval EMPTY_INTERVAL = make_empty_interval();\r
-const Interval H1_INTERVAL(0, 0.5);\r
-const Interval H2_INTERVAL(0.5 + MAX_PRECISION, 1.0);\r
-\r
-/*\r
- * iterate\r
- *\r
- * input:\r
- * A, B: control point sets of two bezier curves\r
- * domA, domB: real parameter intervals of the two curves\r
- * precision: required computational precision of the returned parameter ranges\r
- * output:\r
- * domsA, domsB: sets of parameter intervals\r
- *\r
- * The parameter intervals are computed by using a Bezier clipping algorithm,\r
- * in case the clipping doesn't shrink the initial interval more than 20%,\r
- * a subdivision step is performed.\r
- * If during the computation both curves collapse to a single point\r
- * the routine exits indipendently by the precision reached in the computation\r
- * of the curve intervals.\r
- */\r
-template <>\r
-void iterate<intersection_point_tag> (std::vector<Interval>& domsA,\r
-                                      std::vector<Interval>& domsB,\r
-                                      std::vector<Point> const& A,\r
-                                      std::vector<Point> const& B,\r
-                                      Interval const& domA,\r
-                                      Interval const& domB,\r
-                                      double precision )\r
-{\r
-    // in order to limit recursion\r
-    static size_t counter = 0;\r
-    if (domA.extent() == 1 && domB.extent() == 1) counter  = 0;\r
-    if (++counter > 100) return;\r
-#if VERBOSE\r
-    std::cerr << std::fixed << std::setprecision(16);\r
-    std::cerr << ">> curve subdision performed <<" << std::endl;\r
-    std::cerr << "dom(A) : " << domA << std::endl;\r
-    std::cerr << "dom(B) : " << domB << std::endl;\r
-//    std::cerr << "angle(A) : " << angle(A) << std::endl;\r
-//    std::cerr << "angle(B) : " << angle(B) << std::endl;\r
-#endif\r
-\r
-    if (precision < MAX_PRECISION)\r
-        precision = MAX_PRECISION;\r
-\r
-    std::vector<Point> pA = A;\r
-    std::vector<Point> pB = B;\r
-    std::vector<Point>* C1 = &pA;\r
-    std::vector<Point>* C2 = &pB;\r
-\r
-    Interval dompA = domA;\r
-    Interval dompB = domB;\r
-    Interval* dom1 = &dompA;\r
-    Interval* dom2 = &dompB;\r
-\r
-    Interval dom;\r
-\r
-    if ( is_constant(A) && is_constant(B) ){\r
-        Point M1 = middle_point(C1->front(), C1->back());\r
-        Point M2 = middle_point(C2->front(), C2->back());\r
-        if (are_near(M1,M2)){\r
-            domsA.push_back(domA);\r
-            domsB.push_back(domB);\r
-        }\r
-        return;\r
-    }\r
-\r
-    size_t iter = 0;\r
-    while (++iter < 100\r
-            && (dompA.extent() >= precision || dompB.extent() >= precision))\r
-    {\r
-#if VERBOSE\r
-        std::cerr << "iter: " << iter << std::endl;\r
-#endif\r
-        clip<intersection_point_tag>(dom, *C1, *C2);\r
-\r
-        // [1,0] is utilized to represent an empty interval\r
-        if (dom == EMPTY_INTERVAL)\r
-        {\r
-#if VERBOSE\r
-            std::cerr << "dom: empty" << std::endl;\r
-#endif\r
-            return;\r
-        }\r
-#if VERBOSE\r
-        std::cerr << "dom : " << dom << std::endl;\r
-#endif\r
-        // all other cases where dom[0] > dom[1] are invalid\r
-        if (dom.min() >  dom.max())\r
-        {\r
-            assert(dom.min() <  dom.max());\r
-        }\r
-\r
-        map_to(*dom2, dom);\r
-\r
-        portion(*C2, dom);\r
-        if (is_constant(*C2) && is_constant(*C1))\r
-        {\r
-            Point M1 = middle_point(C1->front(), C1->back());\r
-            Point M2 = middle_point(C2->front(), C2->back());\r
-#if VERBOSE\r
-            std::cerr << "both curves are constant: \n"\r
-                      << "M1: " << M1 << "\n"\r
-                      << "M2: " << M2 << std::endl;\r
-            print(*C2, "C2");\r
-            print(*C1, "C1");\r
-#endif\r
-            if (are_near(M1,M2))\r
-                break;  // append the new interval\r
-            else\r
-                return; // exit without appending any new interval\r
-        }\r
-\r
-\r
-        // if we have clipped less than 20% than we need to subdive the curve\r
-        // with the largest domain into two sub-curves\r
-        if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD)\r
-        {\r
-#if VERBOSE\r
-            std::cerr << "clipped less than 20% : " << dom.extent() << std::endl;\r
-            std::cerr << "angle(pA) : " << angle(pA) << std::endl;\r
-            std::cerr << "angle(pB) : " << angle(pB) << std::endl;\r
-#endif\r
-            std::vector<Point> pC1, pC2;\r
-            Interval dompC1, dompC2;\r
-            if (dompA.extent() > dompB.extent())\r
-            {\r
-                pC1 = pC2 = pA;\r
-                portion(pC1, H1_INTERVAL);\r
-                portion(pC2, H2_INTERVAL);\r
-                dompC1 = dompC2 = dompA;\r
-                map_to(dompC1, H1_INTERVAL);\r
-                map_to(dompC2, H2_INTERVAL);\r
-                iterate<intersection_point_tag>(domsA, domsB, pC1, pB,\r
-                                                dompC1, dompB, precision);\r
-                iterate<intersection_point_tag>(domsA, domsB, pC2, pB,\r
-                                                dompC2, dompB, precision);\r
-            }\r
-            else\r
-            {\r
-                pC1 = pC2 = pB;\r
-                portion(pC1, H1_INTERVAL);\r
-                portion(pC2, H2_INTERVAL);\r
-                dompC1 = dompC2 = dompB;\r
-                map_to(dompC1, H1_INTERVAL);\r
-                map_to(dompC2, H2_INTERVAL);\r
-                iterate<intersection_point_tag>(domsB, domsA, pC1, pA,\r
-                                                dompC1, dompA, precision);\r
-                iterate<intersection_point_tag>(domsB, domsA, pC2, pA,\r
-                                                dompC2, dompA, precision);\r
-            }\r
-            return;\r
-        }\r
-\r
-        std::swap(C1, C2);\r
-        std::swap(dom1, dom2);\r
-#if VERBOSE\r
-        std::cerr << "dom(pA) : " << dompA << std::endl;\r
-        std::cerr << "dom(pB) : " << dompB << std::endl;\r
-#endif\r
-    }\r
-    domsA.push_back(dompA);\r
-    domsB.push_back(dompB);\r
-}\r
-\r
-\r
-/*\r
- * iterate\r
- *\r
- * input:\r
- * A, B: control point sets of two bezier curves\r
- * domA, domB: real parameter intervals of the two curves\r
- * precision: required computational precision of the returned parameter ranges\r
- * output:\r
- * domsA, domsB: sets of parameter intervals\r
- *\r
- * The parameter intervals are computed by using a Bezier clipping algorithm,\r
- * in case the clipping doesn't shrink the initial interval more than 20%,\r
- * a subdivision step is performed.\r
- * If during the computation one of the two curve interval length becomes less\r
- * than MAX_PRECISION the routine exits indipendently by the precision reached\r
- * in the computation of the other curve interval.\r
- */\r
-template <>\r
-void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,\r
-                                    std::vector<Interval>& domsB,\r
-                                    std::vector<Point> const& A,\r
-                                    std::vector<Point> const& B,\r
-                                    Interval const& domA,\r
-                                    Interval const& domB,\r
-                                    double precision)\r
-{\r
-    // in order to limit recursion\r
-    static size_t counter = 0;\r
-    if (domA.extent() == 1 && domB.extent() == 1) counter  = 0;\r
-    if (++counter > 100) return;\r
-#if VERBOSE\r
-    std::cerr << std::fixed << std::setprecision(16);\r
-    std::cerr << ">> curve subdision performed <<" << std::endl;\r
-    std::cerr << "dom(A) : " << domA << std::endl;\r
-    std::cerr << "dom(B) : " << domB << std::endl;\r
-//    std::cerr << "angle(A) : " << angle(A) << std::endl;\r
-//    std::cerr << "angle(B) : " << angle(B) << std::endl;\r
-#endif\r
-\r
-    if (precision < MAX_PRECISION)\r
-        precision = MAX_PRECISION;\r
-\r
-    std::vector<Point> pA = A;\r
-    std::vector<Point> pB = B;\r
-    std::vector<Point>* C1 = &pA;\r
-    std::vector<Point>* C2 = &pB;\r
-\r
-    Interval dompA = domA;\r
-    Interval dompB = domB;\r
-    Interval* dom1 = &dompA;\r
-    Interval* dom2 = &dompB;\r
-\r
-    Interval dom;\r
-\r
-    size_t iter = 0;\r
-    while (++iter < 100\r
-            && (dompA.extent() >= precision || dompB.extent() >= precision))\r
-    {\r
-#if VERBOSE\r
-        std::cerr << "iter: " << iter << std::endl;\r
-#endif\r
-        clip<collinear_normal_tag>(dom, *C1, *C2);\r
-\r
-        // [1,0] is utilized to represent an empty interval\r
-        if (dom == EMPTY_INTERVAL)\r
-        {\r
-#if VERBOSE\r
-            std::cerr << "dom: empty" << std::endl;\r
-#endif\r
-            return;\r
-        }\r
-#if VERBOSE\r
-        std::cerr << "dom : " << dom << std::endl;\r
-#endif\r
-        // all other cases where dom[0] > dom[1] are invalid\r
-        if (dom.min() >  dom.max())\r
-        {\r
-            assert(dom.min() <  dom.max());\r
-        }\r
-\r
-        map_to(*dom2, dom);\r
-\r
-        // it's better to stop before losing computational precision\r
-        if (iter > 1 && (dom2->extent() <= MAX_PRECISION))\r
-        {\r
-#if VERBOSE\r
-            std::cerr << "beyond max precision limit" << std::endl;\r
-#endif\r
-            break;\r
-        }\r
-\r
-        portion(*C2, dom);\r
-        if (iter > 1 && is_constant(*C2))\r
-        {\r
-#if VERBOSE\r
-            std::cerr << "new curve portion pC1 is constant" << std::endl;\r
-#endif\r
-            break;\r
-        }\r
-\r
-\r
-        // if we have clipped less than 20% than we need to subdive the curve\r
-        // with the largest domain into two sub-curves\r
-        if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD)\r
-        {\r
-#if VERBOSE\r
-            std::cerr << "clipped less than 20% : " << dom.extent() << std::endl;\r
-            std::cerr << "angle(pA) : " << angle(pA) << std::endl;\r
-            std::cerr << "angle(pB) : " << angle(pB) << std::endl;\r
-#endif\r
-            std::vector<Point> pC1, pC2;\r
-            Interval dompC1, dompC2;\r
-            if (dompA.extent() > dompB.extent())\r
-            {\r
-                if ((dompA.extent() / 2) < MAX_PRECISION)\r
-                {\r
-                    break;\r
-                }\r
-                pC1 = pC2 = pA;\r
-                portion(pC1, H1_INTERVAL);\r
-                if (false && is_constant(pC1))\r
-                {\r
-#if VERBOSE\r
-                    std::cerr << "new curve portion pC1 is constant" << std::endl;\r
-#endif\r
-                    break;\r
-                }\r
-                portion(pC2, H2_INTERVAL);\r
-                if (is_constant(pC2))\r
-                {\r
-#if VERBOSE\r
-                    std::cerr << "new curve portion pC2 is constant" << std::endl;\r
-#endif\r
-                    break;\r
-                }\r
-                dompC1 = dompC2 = dompA;\r
-                map_to(dompC1, H1_INTERVAL);\r
-                map_to(dompC2, H2_INTERVAL);\r
-                iterate<collinear_normal_tag>(domsA, domsB, pC1, pB,\r
-                                              dompC1, dompB, precision);\r
-                iterate<collinear_normal_tag>(domsA, domsB, pC2, pB,\r
-                                              dompC2, dompB, precision);\r
-            }\r
-            else\r
-            {\r
-                if ((dompB.extent() / 2) < MAX_PRECISION)\r
-                {\r
-                    break;\r
-                }\r
-                pC1 = pC2 = pB;\r
-                portion(pC1, H1_INTERVAL);\r
-                if (is_constant(pC1))\r
-                {\r
-#if VERBOSE\r
-                    std::cerr << "new curve portion pC1 is constant" << std::endl;\r
-#endif\r
-                    break;\r
-                }\r
-                portion(pC2, H2_INTERVAL);\r
-                if (is_constant(pC2))\r
-                {\r
-#if VERBOSE\r
-                    std::cerr << "new curve portion pC2 is constant" << std::endl;\r
-#endif\r
-                    break;\r
-                }\r
-                dompC1 = dompC2 = dompB;\r
-                map_to(dompC1, H1_INTERVAL);\r
-                map_to(dompC2, H2_INTERVAL);\r
-                iterate<collinear_normal_tag>(domsB, domsA, pC1, pA,\r
-                                              dompC1, dompA, precision);\r
-                iterate<collinear_normal_tag>(domsB, domsA, pC2, pA,\r
-                                              dompC2, dompA, precision);\r
-            }\r
-            return;\r
-        }\r
-\r
-        std::swap(C1, C2);\r
-        std::swap(dom1, dom2);\r
-#if VERBOSE\r
-        std::cerr << "dom(pA) : " << dompA << std::endl;\r
-        std::cerr << "dom(pB) : " << dompB << std::endl;\r
-#endif\r
-    }\r
-    domsA.push_back(dompA);\r
-    domsB.push_back(dompB);\r
-}\r
-\r
-\r
-/*\r
- * get_solutions\r
- *\r
- *  input: A, B       - set of control points of two Bezier curve\r
- *  input: precision  - required precision of computation\r
- *  input: clip       - the routine used for clipping\r
- *  output: xs        - set of pairs of parameter values\r
- *                      at which the clipping algorithm converges\r
- *\r
- *  This routine is based on the Bezier Clipping Algorithm,\r
- *  see: Sederberg - Computer Aided Geometric Design\r
- */\r
-template <typename Tag>\r
-void get_solutions (std::vector< std::pair<double, double> >& xs,\r
-                    std::vector<Point> const& A,\r
-                    std::vector<Point> const& B,\r
-                    double precision)\r
-{\r
-    std::pair<double, double> ci;\r
-    std::vector<Interval> domsA, domsB;\r
-    iterate<Tag> (domsA, domsB, A, B, UNIT_INTERVAL, UNIT_INTERVAL, precision);\r
-    if (domsA.size() != domsB.size())\r
-    {\r
-        assert (domsA.size() == domsB.size());\r
-    }\r
-    xs.clear();\r
-    xs.reserve(domsA.size());\r
-    for (size_t i = 0; i < domsA.size(); ++i)\r
-    {\r
-#if VERBOSE\r
-        std::cerr << i << " : domA : " << domsA[i] << std::endl;\r
-        std::cerr << "extent A: " << domsA[i].extent() << "  ";\r
-        std::cerr << "precision A: " << get_precision(domsA[i]) << std::endl;\r
-        std::cerr << i << " : domB : " << domsB[i] << std::endl;\r
-        std::cerr << "extent B: " << domsB[i].extent() << "  ";\r
-        std::cerr << "precision B: " << get_precision(domsB[i]) << std::endl;\r
-#endif\r
-        ci.first = domsA[i].middle();\r
-        ci.second = domsB[i].middle();\r
-        xs.push_back(ci);\r
-    }\r
-}\r
-\r
-} /* end namespace bezier_clipping */ } /* end namespace detail */\r
-\r
-\r
-/*\r
- * find_collinear_normal\r
- *\r
- *  input: A, B       - set of control points of two Bezier curve\r
- *  input: precision  - required precision of computation\r
- *  output: xs        - set of pairs of parameter values\r
- *                      at which there are collinear normals\r
- *\r
- *  This routine is based on the Bezier Clipping Algorithm,\r
- *  see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping\r
- */\r
-void find_collinear_normal (std::vector< std::pair<double, double> >& xs,\r
-                            std::vector<Point> const& A,\r
-                            std::vector<Point> const& B,\r
-                            double precision)\r
-{\r
-    using detail::bezier_clipping::get_solutions;\r
-    using detail::bezier_clipping::collinear_normal_tag;\r
-    get_solutions<collinear_normal_tag>(xs, A, B, precision);\r
-}\r
-\r
-\r
-/*\r
- * find_intersections_bezier_clipping\r
- *\r
- *  input: A, B       - set of control points of two Bezier curve\r
- *  input: precision  - required precision of computation\r
- *  output: xs        - set of pairs of parameter values\r
- *                      at which crossing happens\r
- *\r
- *  This routine is based on the Bezier Clipping Algorithm,\r
- *  see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping\r
- */\r
-void find_intersections_bezier_clipping (std::vector< std::pair<double, double> >& xs,\r
-                         std::vector<Point> const& A,\r
-                         std::vector<Point> const& B,\r
-                         double precision)\r
-{\r
-    using detail::bezier_clipping::get_solutions;\r
-    using detail::bezier_clipping::intersection_point_tag;\r
-    get_solutions<intersection_point_tag>(xs, A, B, precision);\r
-}\r
-\r
-}  // end namespace Geom\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
+/*
+ * Implement the Bezier clipping algorithm for finding
+ * Bezier curve intersection points and collinear normals
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  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.
+ */
+
+
+
+
+#include <2geom/basic-intersection.h>
+#include <2geom/choose.h>
+#include <2geom/point.h>
+#include <2geom/interval.h>
+#include <2geom/bezier.h>
+//#include <2geom/convex-cover.h>
+#include <2geom/numeric/matrix.h>
+
+#include <cassert>
+#include <vector>
+#include <algorithm>
+#include <utility>
+//#include <iomanip>
+
+
+
+
+#define VERBOSE 0
+#define CHECK 0
+
+
+namespace Geom {
+
+namespace detail { namespace bezier_clipping {
+
+////////////////////////////////////////////////////////////////////////////////
+// for debugging
+//
+
+inline
+void print(std::vector<Point> const& cp, const char* msg = "")
+{
+    std::cerr << msg << std::endl;
+    for (size_t i = 0; i < cp.size(); ++i)
+        std::cerr << i << " : " << cp[i] << std::endl;
+}
+
+template< class charT >
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os, const Interval & I)
+{
+    os << "[" << I.min() << ", " << I.max() << "]";
+    return os;
+}
+
+inline
+double angle (std::vector<Point> const& A)
+{
+    size_t n = A.size() -1;
+    double a = std::atan2(A[n][Y] - A[0][Y], A[n][X] - A[0][X]);
+    return (180 * a / M_PI);
+}
+
+inline
+size_t get_precision(Interval const& I)
+{
+    double d = I.extent();
+    double e = 0.1, p = 10;
+    int n = 0;
+    while (n < 16 && d < e)
+    {
+        p *= 10;
+        e = 1/p;
+        ++n;
+    }
+    return n;
+}
+
+inline
+void range_assertion(int k, int m, int n, const char* msg)
+{
+    if ( k < m || k > n)
+    {
+        std::cerr << "range assertion failed: \n"
+                  << msg << std::endl
+                  << "value: " << k
+                  << "  range: " << m << ", " << n << std::endl;
+        assert (k >= m && k <= n);
+    }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//  convex hull
+
+/*
+ * return true in case the oriented polyline p0, p1, p2 is a right turn
+ */
+inline
+bool is_a_right_turn (Point const& p0, Point const& p1, Point const& p2)
+{
+    if (p1 == p2) return false;
+    Point q1 = p1 - p0;
+    Point q2 = p2 - p0;
+    if (q1 == -q2) return false;
+    return (cross (q1, q2) < 0);
+}
+
+/*
+ * return true if p < q wrt the lexicographyc order induced by the coordinates
+ */
+struct lex_less
+{
+    bool operator() (Point const& p, Point const& q)
+    {
+      return ((p[X] < q[X]) || (p[X] == q[X] && p[Y] < q[Y]));
+    }
+};
+
+/*
+ * return true if p > q wrt the lexicographyc order induced by the coordinates
+ */
+struct lex_greater
+{
+    bool operator() (Point const& p, Point const& q)
+    {
+        return ((p[X] > q[X]) || (p[X] == q[X] && p[Y] > q[Y]));
+    }
+};
+
+/*
+ * Compute the convex hull of a set of points.
+ * The implementation is based on the Andrew's scan algorithm
+ * note: in the Bezier clipping for collinear normals it seems
+ * to be more stable wrt the Graham's scan algorithm and in general
+ * a bit quikier
+ */
+void convex_hull (std::vector<Point> & P)
+{
+    size_t n = P.size();
+    if (n < 2)  return;
+    std::sort(P.begin(), P.end(), lex_less());
+    if (n < 4) return;
+    // upper hull
+    size_t u = 2;
+    for (size_t i = 2; i < n; ++i)
+    {
+        while (u > 1 && !is_a_right_turn(P[u-2], P[u-1], P[i]))
+        {
+            --u;
+        }
+        std::swap(P[u], P[i]);
+        ++u;
+    }
+    std::sort(P.begin() + u, P.end(), lex_greater());
+    std::rotate(P.begin(), P.begin() + 1, P.end());
+    // lower hull
+    size_t l = u;
+    size_t k = u - 1;
+    for (size_t i = l; i < n; ++i)
+    {
+        while (l > k && !is_a_right_turn(P[l-2], P[l-1], P[i]))
+        {
+            --l;
+        }
+        std::swap(P[l], P[i]);
+        ++l;
+    }
+    P.resize(l);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//  numerical routines
+
+/*
+ * Compute the binomial coefficient (n, k)
+ */
+inline
+double binomial(unsigned int n, unsigned int k)
+{
+    return choose<double>(n, k);
+}
+
+/*
+ * Compute the determinant of the 2x2 matrix with column the point P1, P2
+ */
+inline
+double det(Point const& P1, Point const& P2)
+{
+    return P1[X]*P2[Y] - P1[Y]*P2[X];
+}
+
+/*
+ * Solve the linear system [P1,P2] * P = Q
+ * in case there isn't exactly one solution the routine returns false
+ */
+inline
+bool solve(Point & P, Point const& P1, Point const& P2, Point const& Q)
+{
+    double d = det(P1, P2);
+    if (d == 0)  return false;
+    d = 1 / d;
+    P[X] = det(Q, P2) * d;
+    P[Y] = det(P1, Q) * d;
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// interval routines
+
+/*
+ * Map the sub-interval I in [0,1] into the interval J and assign it to J
+ */
+inline
+void map_to(Interval & J, Interval const& I)
+{
+    double length = J.extent();
+    J[1] = I.max() * length + J[0];
+    J[0] = I.min() * length + J[0];
+}
+
+/*
+ * The interval [1,0] is used to represent the empty interval, this routine
+ * is just an helper function for creating such an interval
+ */
+inline
+Interval make_empty_interval()
+{
+    Interval I(0);
+    I[0] = 1;
+    return I;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// bezier curve routines
+
+/*
+ * Return true if all the Bezier curve control points are near,
+ * false otherwise
+ */
+inline
+bool is_constant(std::vector<Point> const& A, double precision = EPSILON)
+{
+    for (unsigned int i = 1; i < A.size(); ++i)
+    {
+        if(!are_near(A[i], A[0], precision))
+            return false;
+    }
+    return true;
+}
+
+/*
+ * Compute the hodograph of the bezier curve B and return it in D
+ */
+inline
+void derivative(std::vector<Point> & D, std::vector<Point> const& B)
+{
+    D.clear();
+    size_t sz = B.size();
+    if (sz == 0) return;
+    if (sz == 1)
+    {
+        D.resize(1, Point(0,0));
+        return;
+    }
+    size_t n = sz-1;
+    D.reserve(n);
+    for (size_t i = 0; i < n; ++i)
+    {
+        D.push_back(n*(B[i+1] - B[i]));
+    }
+}
+
+/*
+ * Compute the hodograph of the Bezier curve B rotated of 90 degree
+ * and return it in D; we have N(t) orthogonal to B(t) for any t
+ */
+inline
+void normal(std::vector<Point> & N, std::vector<Point> const& B)
+{
+    derivative(N,B);
+    for (size_t i = 0; i < N.size(); ++i)
+    {
+        N[i] = rot90(N[i]);
+    }
+}
+
+/*
+ *  Compute the portion of the Bezier curve "B" wrt the interval [0,t]
+ */
+inline
+void left_portion(Coord t, std::vector<Point> & B)
+{
+    size_t n = B.size();
+    for (size_t i = 1; i < n; ++i)
+    {
+        for (size_t j = n-1; j > i-1 ; --j)
+        {
+            B[j] = lerp(t, B[j-1], B[j]);
+        }
+    }
+}
+
+/*
+ *  Compute the portion of the Bezier curve "B" wrt the interval [t,1]
+ */
+inline
+void right_portion(Coord t, std::vector<Point> & B)
+{
+    size_t n = B.size();
+    for (size_t i = 1; i < n; ++i)
+    {
+        for (size_t j = 0; j < n-i; ++j)
+        {
+            B[j] = lerp(t, B[j], B[j+1]);
+        }
+    }
+}
+
+/*
+ *  Compute the portion of the Bezier curve "B" wrt the interval "I"
+ */
+inline
+void portion (std::vector<Point> & B , Interval const& I)
+{
+    if (I.min() == 0)
+    {
+        if (I.max() == 1)  return;
+        left_portion(I.max(), B);
+        return;
+    }
+    right_portion(I.min(), B);
+    if (I.max() == 1)  return;
+    double t = I.extent() / (1 - I.min());
+    left_portion(t, B);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// tags
+
+struct intersection_point_tag;
+struct collinear_normal_tag;
+template <typename Tag>
+void clip(Interval & dom,
+          std::vector<Point> const& A,
+          std::vector<Point> const& B);
+template <typename Tag>
+void iterate(std::vector<Interval>& domsA,
+             std::vector<Interval>& domsB,
+             std::vector<Point> const& A,
+             std::vector<Point> const& B,
+             Interval const& domA,
+             Interval const& domB,
+             double precision );
+
+
+////////////////////////////////////////////////////////////////////////////////
+// intersection
+
+/*
+ *  Make up an orientation line using the control points c[i] and c[j]
+ *  the line is returned in the output parameter "l" in the form of a 3 element
+ *  vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
+ */
+inline
+void orientation_line (std::vector<double> & l,
+                       std::vector<Point> const& c,
+                       size_t i, size_t j)
+{
+    l[0] = c[j][Y] - c[i][Y];
+    l[1] = c[i][X] - c[j][X];
+    l[2] = cross(c[i], c[j]);
+    double length = std::sqrt(l[0] * l[0] + l[1] * l[1]);
+    assert (length != 0);
+    l[0] /= length;
+    l[1] /= length;
+    l[2] /= length;
+}
+
+/*
+ * Pick up an orientation line for the Bezier curve "c" and return it in
+ * the output parameter "l"
+ */
+inline
+void pick_orientation_line (std::vector<double> & l,
+                            std::vector<Point> const& c)
+{
+    size_t i = c.size();
+    while (--i > 0 && are_near(c[0], c[i]))
+    {}
+    if (i == 0)
+    {
+        // this should never happen because when a new curve portion is created
+        // we check that it is not constant;
+        // however this requires that the precision used in the is_constant
+        // routine has to be the same used here in the are_near test
+        assert(i != 0);
+    }
+    orientation_line(l, c, 0, i);
+    //std::cerr << "i = " << i << std::endl;
+}
+
+/*
+ *  Make up an orientation line for constant bezier curve;
+ *  the orientation line is made up orthogonal to the other curve base line;
+ *  the line is returned in the output parameter "l" in the form of a 3 element
+ *  vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
+ */
+inline
+void orthogonal_orientation_line (std::vector<double> & l,
+                                  std::vector<Point> const& c,
+                                  Point const& p)
+{
+    if (is_constant(c))
+    {
+        // this should never happen
+        assert(!is_constant(c));
+    }
+    std::vector<Point> ol(2);
+    ol[0] = p;
+    ol[1] = (c.back() - c.front()).cw() + p;
+    orientation_line(l, ol, 0, 1);
+}
+
+/*
+ *  Compute the signed distance of the point "P" from the normalized line l
+ */
+inline
+double distance (Point const& P, std::vector<double> const& l)
+{
+    return l[X] * P[X] + l[Y] * P[Y] + l[2];
+}
+
+/*
+ * Compute the min and max distance of the control points of the Bezier
+ * curve "c" from the normalized orientation line "l".
+ * This bounds are returned through the output Interval parameter"bound".
+ */
+inline
+void fat_line_bounds (Interval& bound,
+                      std::vector<Point> const& c,
+                      std::vector<double> const& l)
+{
+    bound[0] = 0;
+    bound[1] = 0;
+    double d;
+    for (size_t i = 0; i < c.size(); ++i)
+    {
+        d = distance(c[i], l);
+        if (bound[0] > d)  bound[0] = d;
+        if (bound[1] < d)  bound[1] = d;
+    }
+}
+
+/*
+ * return the x component of the intersection point between the line
+ * passing through points p1, p2 and the line Y = "y"
+ */
+inline
+double intersect (Point const& p1, Point const& p2, double y)
+{
+    // we are sure that p2[Y] != p1[Y] because this routine is called
+    // only when the lower or the upper bound is crossed
+    double dy = (p2[Y] - p1[Y]);
+    double s = (y - p1[Y]) / dy;
+    return (p2[X]-p1[X])*s + p1[X];
+}
+
+/*
+ * Clip the Bezier curve "B" wrt the fat line defined by the orientation
+ * line "l" and the interval range "bound", the new parameter interval for
+ * the clipped curve is returned through the output parameter "dom"
+ */
+void clip_interval (Interval& dom,
+                    std::vector<Point> const& B,
+                    std::vector<double> const& l,
+                    Interval const& bound)
+{
+    double n = B.size() - 1;  // number of sub-intervals
+    std::vector<Point> D;     // distance curve control points
+    D.reserve (B.size());
+    double d;
+    for (size_t i = 0; i < B.size(); ++i)
+    {
+        d = distance (B[i], l);
+        D.push_back (Point(i/n, d));
+    }
+    //print(D);
+
+    convex_hull(D);
+    std::vector<Point> & p = D;
+    //print(p);
+
+    bool plower, phigher;
+    bool clower, chigher;
+    double t, tmin = 1, tmax = 0;
+//    std::cerr << "bound : " << bound << std::endl;
+
+    plower = (p[0][Y] < bound.min());
+    phigher = (p[0][Y] > bound.max());
+    if (!(plower || phigher))  // inside the fat line
+    {
+        if (tmin > p[0][X])  tmin = p[0][X];
+        if (tmax < p[0][X])  tmax = p[0][X];
+//        std::cerr << "0 : inside " << p[0]
+//                  << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
+    }
+
+    for (size_t i = 1; i < p.size(); ++i)
+    {
+        clower = (p[i][Y] < bound.min());
+        chigher = (p[i][Y] > bound.max());
+        if (!(clower || chigher))  // inside the fat line
+        {
+            if (tmin > p[i][X])  tmin = p[i][X];
+            if (tmax < p[i][X])  tmax = p[i][X];
+//            std::cerr << i << " : inside " << p[i]
+//                      << " : tmin = " << tmin << ", tmax = " << tmax
+//                      << std::endl;
+        }
+        if (clower != plower)  // cross the lower bound
+        {
+            t = intersect(p[i-1], p[i], bound.min());
+            if (tmin > t)  tmin = t;
+            if (tmax < t)  tmax = t;
+            plower = clower;
+//            std::cerr << i << " : lower " << p[i]
+//                      << " : tmin = " << tmin << ", tmax = " << tmax
+//                      << std::endl;
+        }
+        if (chigher != phigher)  // cross the upper bound
+        {
+            t = intersect(p[i-1], p[i], bound.max());
+            if (tmin > t)  tmin = t;
+            if (tmax < t)  tmax = t;
+            phigher = chigher;
+//            std::cerr << i << " : higher " << p[i]
+//                      << " : tmin = " << tmin << ", tmax = " << tmax
+//                      << std::endl;
+        }
+    }
+
+    // we have to test the closing segment for intersection
+    size_t last = p.size() - 1;
+    clower = (p[0][Y] < bound.min());
+    chigher = (p[0][Y] > bound.max());
+    if (clower != plower)  // cross the lower bound
+    {
+        t = intersect(p[last], p[0], bound.min());
+        if (tmin > t)  tmin = t;
+        if (tmax < t)  tmax = t;
+//        std::cerr << "0 : lower " << p[0]
+//                  << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
+    }
+    if (chigher != phigher)  // cross the upper bound
+    {
+        t = intersect(p[last], p[0], bound.max());
+        if (tmin > t)  tmin = t;
+        if (tmax < t)  tmax = t;
+//        std::cerr << "0 : higher " << p[0]
+//                  << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
+    }
+
+    dom[0] = tmin;
+    dom[1] = tmax;
+}
+
+/*
+ *  Clip the Bezier curve "B" wrt the Bezier curve "A" for individuating
+ *  intersection points the new parameter interval for the clipped curve
+ *  is returned through the output parameter "dom"
+ */
+template <>
+inline
+void clip<intersection_point_tag> (Interval & dom,
+                                   std::vector<Point> const& A,
+                                   std::vector<Point> const& B)
+{
+    std::vector<double> bl(3);
+    Interval bound;
+    if (is_constant(A))
+    {
+        Point M = middle_point(A.front(), A.back());
+        orthogonal_orientation_line(bl, B, M);
+    }
+    else
+    {
+        pick_orientation_line(bl, A);
+    }
+    fat_line_bounds(bound, A, bl);
+    clip_interval(dom, B, bl, bound);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// collinear normal
+
+/*
+ * Compute a closed focus for the Bezier curve B and return it in F
+ * A focus is any curve through which all lines perpendicular to B(t) pass.
+ */
+inline
+void make_focus (std::vector<Point> & F, std::vector<Point> const& B)
+{
+    assert (B.size() > 2);
+    size_t n = B.size() - 1;
+    normal(F, B);
+    Point c(1, 1);
+#if VERBOSE
+    if (!solve(c, F[0], -F[n-1], B[n]-B[0]))
+    {
+        std::cerr << "make_focus: unable to make up a closed focus" << std::endl;
+    }
+#else
+    solve(c, F[0], -F[n-1], B[n]-B[0]);
+#endif
+//    std::cerr << "c = " << c << std::endl;
+
+
+    // B(t) + c(t) * N(t)
+    double n_inv = 1 / (double)(n);
+    Point c0ni;
+    F.push_back(c[1] * F[n-1]);
+    F[n] += B[n];
+    for (size_t i = n-1; i > 0; --i)
+    {
+        F[i] *= -c[0];
+        c0ni = F[i];
+        F[i] += (c[1] * F[i-1]);
+        F[i] *= (i * n_inv);
+        F[i] -= c0ni;
+        F[i] += B[i];
+    }
+    F[0] *= c[0];
+    F[0] += B[0];
+}
+
+/*
+ * Compute the projection on the plane (t, d) of the control points
+ * (t, u, D(t,u)) where D(t,u) = <(B(t) - F(u)), B'(t)> with 0 <= t, u <= 1
+ * B is a Bezier curve and F is a focus of another Bezier curve.
+ * See Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping.
+ */
+void distance_control_points (std::vector<Point> & D,
+                              std::vector<Point> const& B,
+                              std::vector<Point> const& F)
+{
+    assert (B.size() > 1);
+    assert (F.size() > 0);
+    const size_t n = B.size() - 1;
+    const size_t m = F.size() - 1;
+    const size_t r = 2 * n - 1;
+    const double r_inv = 1 / (double)(r);
+    D.clear();
+    D.reserve (B.size() * F.size());
+
+    std::vector<Point> dB;
+    dB.reserve(n);
+    for (size_t k = 0; k < n; ++k)
+    {
+        dB.push_back (B[k+1] - B[k]);
+    }
+    NL::Matrix dBB(n,B.size());
+    for (size_t i = 0; i < n; ++i)
+        for (size_t j = 0; j < B.size(); ++j)
+            dBB(i,j) = dot (dB[i], B[j]);
+    NL::Matrix dBF(n, F.size());
+    for (size_t i = 0; i < n; ++i)
+        for (size_t j = 0; j < F.size(); ++j)
+            dBF(i,j) = dot (dB[i], F[j]);
+
+    size_t k0, kn, l;
+    double bc, bri;
+    Point dij;
+    std::vector<double> d(F.size());
+    for (size_t i = 0; i <= r; ++i)
+    {
+        for (size_t j = 0; j <= m; ++j)
+        {
+            d[j] = 0;
+        }
+        k0 = std::max(i, n) - n;
+        kn = std::min(i, n-1);
+        bri = n / binomial(r,i);
+        for (size_t k = k0; k <= kn; ++k)
+        {
+            //if (k > i || (i-k) > n) continue;
+            l = i - k;
+#if CHECK
+            assert (l <= n);
+#endif
+            bc = bri * binomial(n,l) * binomial(n-1, k);
+            for (size_t j = 0; j <= m; ++j)
+            {
+                //d[j] += bc * dot(dB[k], B[l] - F[j]);
+                d[j] += bc * (dBB(k,l) - dBF(k,j));
+            }
+        }
+        double dmin, dmax;
+        dmin = dmax = d[m];
+        for (size_t j = 0; j < m; ++j)
+        {
+            if (dmin > d[j])  dmin = d[j];
+            if (dmax < d[j])  dmax = d[j];
+        }
+        dij[0] = i * r_inv;
+        dij[1] = dmin;
+        D.push_back (dij);
+        dij[1] = dmax;
+        D.push_back (dij);
+    }
+}
+
+/*
+ * Clip the Bezier curve "B" wrt the focus "F"; the new parameter interval for
+ * the clipped curve is returned through the output parameter "dom"
+ */
+void clip_interval (Interval& dom,
+                    std::vector<Point> const& B,
+                    std::vector<Point> const& F)
+{
+    std::vector<Point> D;     // distance curve control points
+    distance_control_points(D, B, F);
+    //print(D, "D");
+//    ConvexHull chD(D);
+//    std::vector<Point>& p = chD.boundary; // convex hull vertices
+
+    convex_hull(D);
+    std::vector<Point> & p = D;
+    //print(p, "CH(D)");
+
+    bool plower, clower;
+    double t, tmin = 1, tmax = 0;
+
+    plower = (p[0][Y] < 0);
+    if (p[0][Y] == 0)  // on the x axis
+    {
+        if (tmin > p[0][X])  tmin = p[0][X];
+        if (tmax < p[0][X])  tmax = p[0][X];
+//        std::cerr << "0 : on x axis " << p[0]
+//                  << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
+    }
+
+    for (size_t i = 1; i < p.size(); ++i)
+    {
+        clower = (p[i][Y] < 0);
+        if (p[i][Y] == 0)  // on x axis
+        {
+            if (tmin > p[i][X])  tmin = p[i][X];
+            if (tmax < p[i][X])  tmax = p[i][X];
+//            std::cerr << i << " : on x axis " << p[i]
+//                      << " : tmin = " << tmin << ", tmax = " << tmax
+//                      << std::endl;
+        }
+        else if (clower != plower)  // cross the x axis
+        {
+            t = intersect(p[i-1], p[i], 0);
+            if (tmin > t)  tmin = t;
+            if (tmax < t)  tmax = t;
+            plower = clower;
+//            std::cerr << i << " : lower " << p[i]
+//                      << " : tmin = " << tmin << ", tmax = " << tmax
+//                      << std::endl;
+        }
+    }
+
+    // we have to test the closing segment for intersection
+    size_t last = p.size() - 1;
+    clower = (p[0][Y] < 0);
+    if (clower != plower)  // cross the x axis
+    {
+        t = intersect(p[last], p[0], 0);
+        if (tmin > t)  tmin = t;
+        if (tmax < t)  tmax = t;
+//        std::cerr << "0 : lower " << p[0]
+//                  << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
+    }
+    dom[0] = tmin;
+    dom[1] = tmax;
+}
+
+/*
+ *  Clip the Bezier curve "B" wrt the Bezier curve "A" for individuating
+ *  points which have collinear normals; the new parameter interval
+ *  for the clipped curve is returned through the output parameter "dom"
+ */
+template <>
+inline
+void clip<collinear_normal_tag> (Interval & dom,
+                                 std::vector<Point> const& A,
+                                 std::vector<Point> const& B)
+{
+    std::vector<Point> F;
+    make_focus(F, A);
+    clip_interval(dom, B, F);
+}
+
+
+
+const double MAX_PRECISION = 1e-8;
+const double MIN_CLIPPED_SIZE_THRESHOLD = 0.8;
+const Interval UNIT_INTERVAL(0,1);
+const Interval EMPTY_INTERVAL = make_empty_interval();
+const Interval H1_INTERVAL(0, 0.5);
+const Interval H2_INTERVAL(0.5 + MAX_PRECISION, 1.0);
+
+/*
+ * iterate
+ *
+ * input:
+ * A, B: control point sets of two bezier curves
+ * domA, domB: real parameter intervals of the two curves
+ * precision: required computational precision of the returned parameter ranges
+ * output:
+ * domsA, domsB: sets of parameter intervals
+ *
+ * The parameter intervals are computed by using a Bezier clipping algorithm,
+ * in case the clipping doesn't shrink the initial interval more than 20%,
+ * a subdivision step is performed.
+ * If during the computation both curves collapse to a single point
+ * the routine exits indipendently by the precision reached in the computation
+ * of the curve intervals.
+ */
+template <>
+void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
+                                      std::vector<Interval>& domsB,
+                                      std::vector<Point> const& A,
+                                      std::vector<Point> const& B,
+                                      Interval const& domA,
+                                      Interval const& domB,
+                                      double precision )
+{
+    // in order to limit recursion
+    static size_t counter = 0;
+    if (domA.extent() == 1 && domB.extent() == 1) counter  = 0;
+    if (++counter > 100) return;
+#if VERBOSE
+    std::cerr << std::fixed << std::setprecision(16);
+    std::cerr << ">> curve subdision performed <<" << std::endl;
+    std::cerr << "dom(A) : " << domA << std::endl;
+    std::cerr << "dom(B) : " << domB << std::endl;
+//    std::cerr << "angle(A) : " << angle(A) << std::endl;
+//    std::cerr << "angle(B) : " << angle(B) << std::endl;
+#endif
+
+    if (precision < MAX_PRECISION)
+        precision = MAX_PRECISION;
+
+    std::vector<Point> pA = A;
+    std::vector<Point> pB = B;
+    std::vector<Point>* C1 = &pA;
+    std::vector<Point>* C2 = &pB;
+
+    Interval dompA = domA;
+    Interval dompB = domB;
+    Interval* dom1 = &dompA;
+    Interval* dom2 = &dompB;
+
+    Interval dom;
+
+    if ( is_constant(A) && is_constant(B) ){
+        Point M1 = middle_point(C1->front(), C1->back());
+        Point M2 = middle_point(C2->front(), C2->back());
+        if (are_near(M1,M2)){
+            domsA.push_back(domA);
+            domsB.push_back(domB);
+        }
+        return;
+    }
+
+    size_t iter = 0;
+    while (++iter < 100
+            && (dompA.extent() >= precision || dompB.extent() >= precision))
+    {
+#if VERBOSE
+        std::cerr << "iter: " << iter << std::endl;
+#endif
+        clip<intersection_point_tag>(dom, *C1, *C2);
+
+        // [1,0] is utilized to represent an empty interval
+        if (dom == EMPTY_INTERVAL)
+        {
+#if VERBOSE
+            std::cerr << "dom: empty" << std::endl;
+#endif
+            return;
+        }
+#if VERBOSE
+        std::cerr << "dom : " << dom << std::endl;
+#endif
+        // all other cases where dom[0] > dom[1] are invalid
+        if (dom.min() >  dom.max())
+        {
+            assert(dom.min() <  dom.max());
+        }
+
+        map_to(*dom2, dom);
+
+        portion(*C2, dom);
+        if (is_constant(*C2) && is_constant(*C1))
+        {
+            Point M1 = middle_point(C1->front(), C1->back());
+            Point M2 = middle_point(C2->front(), C2->back());
+#if VERBOSE
+            std::cerr << "both curves are constant: \n"
+                      << "M1: " << M1 << "\n"
+                      << "M2: " << M2 << std::endl;
+            print(*C2, "C2");
+            print(*C1, "C1");
+#endif
+            if (are_near(M1,M2))
+                break;  // append the new interval
+            else
+                return; // exit without appending any new interval
+        }
+
+
+        // if we have clipped less than 20% than we need to subdive the curve
+        // with the largest domain into two sub-curves
+        if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD)
+        {
+#if VERBOSE
+            std::cerr << "clipped less than 20% : " << dom.extent() << std::endl;
+            std::cerr << "angle(pA) : " << angle(pA) << std::endl;
+            std::cerr << "angle(pB) : " << angle(pB) << std::endl;
+#endif
+            std::vector<Point> pC1, pC2;
+            Interval dompC1, dompC2;
+            if (dompA.extent() > dompB.extent())
+            {
+                pC1 = pC2 = pA;
+                portion(pC1, H1_INTERVAL);
+                portion(pC2, H2_INTERVAL);
+                dompC1 = dompC2 = dompA;
+                map_to(dompC1, H1_INTERVAL);
+                map_to(dompC2, H2_INTERVAL);
+                iterate<intersection_point_tag>(domsA, domsB, pC1, pB,
+                                                dompC1, dompB, precision);
+                iterate<intersection_point_tag>(domsA, domsB, pC2, pB,
+                                                dompC2, dompB, precision);
+            }
+            else
+            {
+                pC1 = pC2 = pB;
+                portion(pC1, H1_INTERVAL);
+                portion(pC2, H2_INTERVAL);
+                dompC1 = dompC2 = dompB;
+                map_to(dompC1, H1_INTERVAL);
+                map_to(dompC2, H2_INTERVAL);
+                iterate<intersection_point_tag>(domsB, domsA, pC1, pA,
+                                                dompC1, dompA, precision);
+                iterate<intersection_point_tag>(domsB, domsA, pC2, pA,
+                                                dompC2, dompA, precision);
+            }
+            return;
+        }
+
+        std::swap(C1, C2);
+        std::swap(dom1, dom2);
+#if VERBOSE
+        std::cerr << "dom(pA) : " << dompA << std::endl;
+        std::cerr << "dom(pB) : " << dompB << std::endl;
+#endif
+    }
+    domsA.push_back(dompA);
+    domsB.push_back(dompB);
+}
+
+
+/*
+ * iterate
+ *
+ * input:
+ * A, B: control point sets of two bezier curves
+ * domA, domB: real parameter intervals of the two curves
+ * precision: required computational precision of the returned parameter ranges
+ * output:
+ * domsA, domsB: sets of parameter intervals
+ *
+ * The parameter intervals are computed by using a Bezier clipping algorithm,
+ * in case the clipping doesn't shrink the initial interval more than 20%,
+ * a subdivision step is performed.
+ * If during the computation one of the two curve interval length becomes less
+ * than MAX_PRECISION the routine exits indipendently by the precision reached
+ * in the computation of the other curve interval.
+ */
+template <>
+void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
+                                    std::vector<Interval>& domsB,
+                                    std::vector<Point> const& A,
+                                    std::vector<Point> const& B,
+                                    Interval const& domA,
+                                    Interval const& domB,
+                                    double precision)
+{
+    // in order to limit recursion
+    static size_t counter = 0;
+    if (domA.extent() == 1 && domB.extent() == 1) counter  = 0;
+    if (++counter > 100) return;
+#if VERBOSE
+    std::cerr << std::fixed << std::setprecision(16);
+    std::cerr << ">> curve subdision performed <<" << std::endl;
+    std::cerr << "dom(A) : " << domA << std::endl;
+    std::cerr << "dom(B) : " << domB << std::endl;
+//    std::cerr << "angle(A) : " << angle(A) << std::endl;
+//    std::cerr << "angle(B) : " << angle(B) << std::endl;
+#endif
+
+    if (precision < MAX_PRECISION)
+        precision = MAX_PRECISION;
+
+    std::vector<Point> pA = A;
+    std::vector<Point> pB = B;
+    std::vector<Point>* C1 = &pA;
+    std::vector<Point>* C2 = &pB;
+
+    Interval dompA = domA;
+    Interval dompB = domB;
+    Interval* dom1 = &dompA;
+    Interval* dom2 = &dompB;
+
+    Interval dom;
+
+    size_t iter = 0;
+    while (++iter < 100
+            && (dompA.extent() >= precision || dompB.extent() >= precision))
+    {
+#if VERBOSE
+        std::cerr << "iter: " << iter << std::endl;
+#endif
+        clip<collinear_normal_tag>(dom, *C1, *C2);
+
+        // [1,0] is utilized to represent an empty interval
+        if (dom == EMPTY_INTERVAL)
+        {
+#if VERBOSE
+            std::cerr << "dom: empty" << std::endl;
+#endif
+            return;
+        }
+#if VERBOSE
+        std::cerr << "dom : " << dom << std::endl;
+#endif
+        // all other cases where dom[0] > dom[1] are invalid
+        if (dom.min() >  dom.max())
+        {
+            assert(dom.min() <  dom.max());
+        }
+
+        map_to(*dom2, dom);
+
+        // it's better to stop before losing computational precision
+        if (iter > 1 && (dom2->extent() <= MAX_PRECISION))
+        {
+#if VERBOSE
+            std::cerr << "beyond max precision limit" << std::endl;
+#endif
+            break;
+        }
+
+        portion(*C2, dom);
+        if (iter > 1 && is_constant(*C2))
+        {
+#if VERBOSE
+            std::cerr << "new curve portion pC1 is constant" << std::endl;
+#endif
+            break;
+        }
+
+
+        // if we have clipped less than 20% than we need to subdive the curve
+        // with the largest domain into two sub-curves
+        if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD)
+        {
+#if VERBOSE
+            std::cerr << "clipped less than 20% : " << dom.extent() << std::endl;
+            std::cerr << "angle(pA) : " << angle(pA) << std::endl;
+            std::cerr << "angle(pB) : " << angle(pB) << std::endl;
+#endif
+            std::vector<Point> pC1, pC2;
+            Interval dompC1, dompC2;
+            if (dompA.extent() > dompB.extent())
+            {
+                if ((dompA.extent() / 2) < MAX_PRECISION)
+                {
+                    break;
+                }
+                pC1 = pC2 = pA;
+                portion(pC1, H1_INTERVAL);
+                if (false && is_constant(pC1))
+                {
+#if VERBOSE
+                    std::cerr << "new curve portion pC1 is constant" << std::endl;
+#endif
+                    break;
+                }
+                portion(pC2, H2_INTERVAL);
+                if (is_constant(pC2))
+                {
+#if VERBOSE
+                    std::cerr << "new curve portion pC2 is constant" << std::endl;
+#endif
+                    break;
+                }
+                dompC1 = dompC2 = dompA;
+                map_to(dompC1, H1_INTERVAL);
+                map_to(dompC2, H2_INTERVAL);
+                iterate<collinear_normal_tag>(domsA, domsB, pC1, pB,
+                                              dompC1, dompB, precision);
+                iterate<collinear_normal_tag>(domsA, domsB, pC2, pB,
+                                              dompC2, dompB, precision);
+            }
+            else
+            {
+                if ((dompB.extent() / 2) < MAX_PRECISION)
+                {
+                    break;
+                }
+                pC1 = pC2 = pB;
+                portion(pC1, H1_INTERVAL);
+                if (is_constant(pC1))
+                {
+#if VERBOSE
+                    std::cerr << "new curve portion pC1 is constant" << std::endl;
+#endif
+                    break;
+                }
+                portion(pC2, H2_INTERVAL);
+                if (is_constant(pC2))
+                {
+#if VERBOSE
+                    std::cerr << "new curve portion pC2 is constant" << std::endl;
+#endif
+                    break;
+                }
+                dompC1 = dompC2 = dompB;
+                map_to(dompC1, H1_INTERVAL);
+                map_to(dompC2, H2_INTERVAL);
+                iterate<collinear_normal_tag>(domsB, domsA, pC1, pA,
+                                              dompC1, dompA, precision);
+                iterate<collinear_normal_tag>(domsB, domsA, pC2, pA,
+                                              dompC2, dompA, precision);
+            }
+            return;
+        }
+
+        std::swap(C1, C2);
+        std::swap(dom1, dom2);
+#if VERBOSE
+        std::cerr << "dom(pA) : " << dompA << std::endl;
+        std::cerr << "dom(pB) : " << dompB << std::endl;
+#endif
+    }
+    domsA.push_back(dompA);
+    domsB.push_back(dompB);
+}
+
+
+/*
+ * get_solutions
+ *
+ *  input: A, B       - set of control points of two Bezier curve
+ *  input: precision  - required precision of computation
+ *  input: clip       - the routine used for clipping
+ *  output: xs        - set of pairs of parameter values
+ *                      at which the clipping algorithm converges
+ *
+ *  This routine is based on the Bezier Clipping Algorithm,
+ *  see: Sederberg - Computer Aided Geometric Design
+ */
+template <typename Tag>
+void get_solutions (std::vector< std::pair<double, double> >& xs,
+                    std::vector<Point> const& A,
+                    std::vector<Point> const& B,
+                    double precision)
+{
+    std::pair<double, double> ci;
+    std::vector<Interval> domsA, domsB;
+    iterate<Tag> (domsA, domsB, A, B, UNIT_INTERVAL, UNIT_INTERVAL, precision);
+    if (domsA.size() != domsB.size())
+    {
+        assert (domsA.size() == domsB.size());
+    }
+    xs.clear();
+    xs.reserve(domsA.size());
+    for (size_t i = 0; i < domsA.size(); ++i)
+    {
+#if VERBOSE
+        std::cerr << i << " : domA : " << domsA[i] << std::endl;
+        std::cerr << "extent A: " << domsA[i].extent() << "  ";
+        std::cerr << "precision A: " << get_precision(domsA[i]) << std::endl;
+        std::cerr << i << " : domB : " << domsB[i] << std::endl;
+        std::cerr << "extent B: " << domsB[i].extent() << "  ";
+        std::cerr << "precision B: " << get_precision(domsB[i]) << std::endl;
+#endif
+        ci.first = domsA[i].middle();
+        ci.second = domsB[i].middle();
+        xs.push_back(ci);
+    }
+}
+
+} /* end namespace bezier_clipping */ } /* end namespace detail */
+
+
+/*
+ * find_collinear_normal
+ *
+ *  input: A, B       - set of control points of two Bezier curve
+ *  input: precision  - required precision of computation
+ *  output: xs        - set of pairs of parameter values
+ *                      at which there are collinear normals
+ *
+ *  This routine is based on the Bezier Clipping Algorithm,
+ *  see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
+ */
+void find_collinear_normal (std::vector< std::pair<double, double> >& xs,
+                            std::vector<Point> const& A,
+                            std::vector<Point> const& B,
+                            double precision)
+{
+    using detail::bezier_clipping::get_solutions;
+    using detail::bezier_clipping::collinear_normal_tag;
+    get_solutions<collinear_normal_tag>(xs, A, B, precision);
+}
+
+
+/*
+ * find_intersections_bezier_clipping
+ *
+ *  input: A, B       - set of control points of two Bezier curve
+ *  input: precision  - required precision of computation
+ *  output: xs        - set of pairs of parameter values
+ *                      at which crossing happens
+ *
+ *  This routine is based on the Bezier Clipping Algorithm,
+ *  see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
+ */
+void find_intersections_bezier_clipping (std::vector< std::pair<double, double> >& xs,
+                         std::vector<Point> const& A,
+                         std::vector<Point> const& B,
+                         double precision)
+{
+    using detail::bezier_clipping::get_solutions;
+    using detail::bezier_clipping::intersection_point_tag;
+    get_solutions<intersection_point_tag>(xs, A, B, precision);
+}
+
+}  // end 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 :
index 4ba3e2325a3b2c1b362da8059e1fe88e4f3c1e03..447c5183f920aeab0913118741f7b20b5dc84d77 100644 (file)
-#include <2geom/chebyshev.h>\r
-\r
-#include <2geom/sbasis.h>\r
-#include <2geom/sbasis-poly.h>\r
-\r
-#include <vector>\r
-using std::vector;\r
-\r
-#include <gsl/gsl_math.h>\r
-#include <gsl/gsl_chebyshev.h>\r
-\r
-namespace Geom{\r
-\r
-SBasis cheb(unsigned n) {\r
-    static std::vector<SBasis> basis;\r
-    if(basis.empty()) {\r
-        basis.push_back(Linear(1,1));\r
-        basis.push_back(Linear(0,1));\r
-    }\r
-    for(unsigned i = basis.size(); i <= n; i++) {\r
-        basis.push_back(Linear(0,2)*basis[i-1] - basis[i-2]);\r
-    }\r
-    \r
-    return basis[n];\r
-}\r
-\r
-SBasis cheb_series(unsigned n, double* cheb_coeff) {\r
-    SBasis r;\r
-    for(unsigned i = 0; i < n; i++) {\r
-        double cof = cheb_coeff[i];\r
-        //if(i == 0)\r
-        //cof /= 2;\r
-        r += cheb(i)*cof;\r
-    }\r
-    \r
-    return r;\r
-}\r
-\r
-SBasis clenshaw_series(unsigned m, double* cheb_coeff) {\r
-    /** b_n = a_n\r
-        b_n-1 = 2*x*b_n + a_n-1\r
-        b_n-k = 2*x*b_{n-k+1} + a_{n-k} - b_{n - k + 2}\r
-        b_0 = x*b_1 + a_0 - b_2\r
-    */\r
-    \r
-    double a = -1, b = 1;\r
-    SBasis d, dd;\r
-    SBasis y = (Linear(0, 2) - (a+b)) / (b-a);\r
-    SBasis y2 = 2*y;\r
-    for(int j = m - 1; j >= 1; j--) {\r
-        SBasis sv = d;\r
-        d = y2*d - dd + cheb_coeff[j];\r
-        dd = sv;\r
-    }\r
-    \r
-    return y*d - dd + 0.5*cheb_coeff[0];\r
-}\r
-\r
-SBasis chebyshev_approximant (double (*f)(double,void*), int order, Interval in, void* p) {\r
-    gsl_cheb_series *cs = gsl_cheb_alloc (order+2);\r
-\r
-    gsl_function F;\r
-\r
-    F.function = f;\r
-    F.params = p;\r
-\r
-    gsl_cheb_init (cs, &F, in[0], in[1]);\r
-    \r
-    SBasis r = compose(clenshaw_series(order, cs->c), Linear(-1,1));\r
-        \r
-    gsl_cheb_free (cs);\r
-    return r;\r
-}\r
-\r
-struct wrap {\r
-    double (*f)(double,void*);\r
-    void* pp;\r
-    double fa, fb;\r
-    Interval in;\r
-};\r
-\r
-double f_interp(double x, void* p) {\r
-    struct wrap *wr = (struct wrap *)p;\r
-    double z = (x - wr->in[0]) / (wr->in[1] - wr->in[0]);\r
-    return (wr->f)(x, wr->pp) - ((1 - z)*wr->fa + z*wr->fb);\r
-}\r
-\r
-SBasis chebyshev_approximant_interpolating (double (*f)(double,void*), \r
-                                            int order, Interval in, void* p) {\r
-    double fa = f(in[0], p);\r
-    double fb = f(in[1], p);\r
-    struct wrap wr;\r
-    wr.fa = fa;\r
-    wr.fb = fb;\r
-    wr.in = in;\r
-    printf("%f %f\n", fa, fb);\r
-    wr.f = f;\r
-    wr.pp = p;\r
-    return compose(Linear(in[0], in[1]), Linear(fa, fb)) + chebyshev_approximant(f_interp, order, in, &wr) + Linear(fa, fb);\r
-}\r
-\r
-SBasis chebyshev(unsigned n) {\r
-    static std::vector<SBasis> basis;\r
-    if(basis.empty()) {\r
-        basis.push_back(Linear(1,1));\r
-        basis.push_back(Linear(0,1));\r
-    }\r
-    for(unsigned i = basis.size(); i <= n; i++) {\r
-        basis.push_back(Linear(0,2)*basis[i-1] - basis[i-2]);\r
-    }\r
-    \r
-    return basis[n];\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
+#include <2geom/chebyshev.h>
+
+#include <2geom/sbasis.h>
+#include <2geom/sbasis-poly.h>
+
+#include <vector>
+using std::vector;
+
+#include <gsl/gsl_math.h>
+#include <gsl/gsl_chebyshev.h>
+
+namespace Geom{
+
+SBasis cheb(unsigned n) {
+    static std::vector<SBasis> basis;
+    if(basis.empty()) {
+        basis.push_back(Linear(1,1));
+        basis.push_back(Linear(0,1));
+    }
+    for(unsigned i = basis.size(); i <= n; i++) {
+        basis.push_back(Linear(0,2)*basis[i-1] - basis[i-2]);
+    }
+    
+    return basis[n];
+}
+
+SBasis cheb_series(unsigned n, double* cheb_coeff) {
+    SBasis r;
+    for(unsigned i = 0; i < n; i++) {
+        double cof = cheb_coeff[i];
+        //if(i == 0)
+        //cof /= 2;
+        r += cheb(i)*cof;
+    }
+    
+    return r;
+}
+
+SBasis clenshaw_series(unsigned m, double* cheb_coeff) {
+    /** b_n = a_n
+        b_n-1 = 2*x*b_n + a_n-1
+        b_n-k = 2*x*b_{n-k+1} + a_{n-k} - b_{n - k + 2}
+        b_0 = x*b_1 + a_0 - b_2
+    */
+    
+    double a = -1, b = 1;
+    SBasis d, dd;
+    SBasis y = (Linear(0, 2) - (a+b)) / (b-a);
+    SBasis y2 = 2*y;
+    for(int j = m - 1; j >= 1; j--) {
+        SBasis sv = d;
+        d = y2*d - dd + cheb_coeff[j];
+        dd = sv;
+    }
+    
+    return y*d - dd + 0.5*cheb_coeff[0];
+}
+
+SBasis chebyshev_approximant (double (*f)(double,void*), int order, Interval in, void* p) {
+    gsl_cheb_series *cs = gsl_cheb_alloc (order+2);
+
+    gsl_function F;
+
+    F.function = f;
+    F.params = p;
+
+    gsl_cheb_init (cs, &F, in[0], in[1]);
+    
+    SBasis r = compose(clenshaw_series(order, cs->c), Linear(-1,1));
+        
+    gsl_cheb_free (cs);
+    return r;
+}
+
+struct wrap {
+    double (*f)(double,void*);
+    void* pp;
+    double fa, fb;
+    Interval in;
+};
+
+double f_interp(double x, void* p) {
+    struct wrap *wr = (struct wrap *)p;
+    double z = (x - wr->in[0]) / (wr->in[1] - wr->in[0]);
+    return (wr->f)(x, wr->pp) - ((1 - z)*wr->fa + z*wr->fb);
+}
+
+SBasis chebyshev_approximant_interpolating (double (*f)(double,void*), 
+                                            int order, Interval in, void* p) {
+    double fa = f(in[0], p);
+    double fb = f(in[1], p);
+    struct wrap wr;
+    wr.fa = fa;
+    wr.fb = fb;
+    wr.in = in;
+    printf("%f %f\n", fa, fb);
+    wr.f = f;
+    wr.pp = p;
+    return compose(Linear(in[0], in[1]), Linear(fa, fb)) + chebyshev_approximant(f_interp, order, in, &wr) + Linear(fa, fb);
+}
+
+SBasis chebyshev(unsigned n) {
+    static std::vector<SBasis> basis;
+    if(basis.empty()) {
+        basis.push_back(Linear(1,1));
+        basis.push_back(Linear(0,1));
+    }
+    for(unsigned i = basis.size(); i <= n; i++) {
+        basis.push_back(Linear(0,2)*basis[i-1] - basis[i-2]);
+    }
+    
+    return basis[n];
+}
+
+};
+
+/*
+  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 :
index 309e09b3e788533c187d8cea4f436540f493e02e..6de9e9cc0ed1f742e3182ee2dd3939ec56f60c9f 100644 (file)
@@ -1,30 +1,30 @@
-#ifndef _CHEBYSHEV\r
-#define _CHEBYSHEV\r
-\r
-#include <2geom/sbasis.h>\r
-#include <2geom/interval.h>\r
-\r
-/*** Conversion between Chebyshev approximation and SBasis.\r
- * \r
- */\r
-\r
-namespace Geom{\r
-\r
-SBasis chebyshev_approximant (double (*f)(double,void*), int order, Interval in, void* p=0);\r
-SBasis chebyshev_approximant_interpolating (double (*f)(double,void*), int order, Interval in, void* p=0);\r
-SBasis chebyshev(unsigned n);\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
-\r
-#endif\r
+#ifndef _CHEBYSHEV
+#define _CHEBYSHEV
+
+#include <2geom/sbasis.h>
+#include <2geom/interval.h>
+
+/*** Conversion between Chebyshev approximation and SBasis.
+ * 
+ */
+
+namespace Geom{
+
+SBasis chebyshev_approximant (double (*f)(double,void*), int order, Interval in, void* p=0);
+SBasis chebyshev_approximant_interpolating (double (*f)(double,void*), int order, Interval in, void* p=0);
+SBasis chebyshev(unsigned n);
+
+};
+
+/*
+  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
index f0f42ce0179e8ac33ed7a5e085207e0298e394e3..579718553503dd13f0509e839468dff1cadbe44f 100644 (file)
@@ -1,87 +1,87 @@
-/** Various utility functions.\r
- *\r
- * Copyright 2008 Marco Cecchetti <mrcekets at gmail.com>\r
- * Copyright 2007 Johan Engelen <goejendaagh@zonnet.nl>\r
- * Copyright 2006 Michael G. 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, 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
-\r
-\r
-#include <2geom/utils.h>\r
-\r
-\r
-namespace Geom \r
-{\r
-\r
-// return a vector that contains all the binomial coefficients of degree n \r
-void binomial_coefficients(std::vector<size_t>& bc, size_t n)\r
-{\r
-    size_t s = n+1;\r
-    bc.clear();\r
-    bc.resize(s);\r
-    bc[0] = 1;\r
-    size_t k;\r
-    for (size_t i = 1; i < n; ++i)\r
-    {\r
-        k = i >> 1;\r
-        if (i & 1u)\r
-        {\r
-            bc[k+1] = bc[k] << 1;\r
-        }\r
-        for (size_t j = k; j > 0; --j)\r
-        {\r
-            bc[j] += bc[j-1];\r
-        }\r
-    }\r
-    s >>= 1;\r
-    for (size_t i = 0; i < s; ++i)\r
-    {\r
-        bc[n-i] = bc[i];\r
-    }\r
-}\r
-\r
-} // end namespace Geom\r
-\r
-\r
-\r
-\r
-\r
-\r
-\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
+/** Various utility functions.
+ *
+ * Copyright 2008 Marco Cecchetti <mrcekets at gmail.com>
+ * Copyright 2007 Johan Engelen <goejendaagh@zonnet.nl>
+ * 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 <2geom/utils.h>
+
+
+namespace Geom 
+{
+
+// return a vector that contains all the binomial coefficients of degree n 
+void binomial_coefficients(std::vector<size_t>& bc, size_t n)
+{
+    size_t s = n+1;
+    bc.clear();
+    bc.resize(s);
+    bc[0] = 1;
+    size_t k;
+    for (size_t i = 1; i < n; ++i)
+    {
+        k = i >> 1;
+        if (i & 1u)
+        {
+            bc[k+1] = bc[k] << 1;
+        }
+        for (size_t j = k; j > 0; --j)
+        {
+            bc[j] += bc[j-1];
+        }
+    }
+    s >>= 1;
+    for (size_t i = 0; i < s; ++i)
+    {
+        bc[n-i] = bc[i];
+    }
+}
+
+} // end 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 :
index fd28011c353c553e93f89abc3dc830b10046b51a..0a1108b1ebbafb5eeaa70067ac7cee061fbe49ab 100644 (file)
-/*\r
- * A simple utility for exporting Inkscape svg Shapes as JavaFX paths.\r
- *\r
- *  For information on the JavaFX file format, see:\r
- *      https://openjfx.dev.java.net/\r
- *\r
- * Authors:\r
- *   Bob Jamison <ishmal@inkscape.org>\r
- *   Silveira Neto <silveiraneto@gmail.com>\r
- *   Jim Clarke <Jim.Clarke@sun.com>\r
- *\r
- * Copyright (C) 2008 Authors\r
- *\r
- * Released under GNU GPL, read the file 'COPYING' for more information\r
- */\r
-\r
-\r
-#ifdef HAVE_CONFIG_H\r
-# include <config.h>\r
-#endif\r
-#include "javafx-out.h"\r
-#include <inkscape.h>\r
-#include <inkscape-version.h>\r
-#include <sp-path.h>\r
-#include <sp-linear-gradient.h>\r
-#include <sp-radial-gradient.h>\r
-#include <style.h>\r
-#include <display/curve.h>\r
-#include <display/canvas-bpath.h>\r
-#include <svg/svg.h>\r
-#include <extension/system.h>\r
-#include <2geom/pathvector.h>\r
-#include <2geom/rect.h>\r
-#include <2geom/bezier-curve.h>\r
-#include <2geom/hvlinesegment.h>\r
-#include "helper/geom.h"\r
-#include "helper/geom-curves.h"\r
-#include <io/sys.h>\r
-\r
-\r
-#include <string>\r
-#include <stdio.h>\r
-#include <stdarg.h>\r
-\r
-\r
-namespace Inkscape\r
-{\r
-namespace Extension\r
-{\r
-namespace Internal\r
-{\r
-\r
-\r
-\r
-\r
-//########################################################################\r
-//# M E S S A G E S\r
-//########################################################################\r
-\r
-static void err(const char *fmt, ...)\r
-{\r
-    va_list args;\r
-    g_log(NULL,  G_LOG_LEVEL_WARNING, "javafx-out err: ");\r
-    va_start(args, fmt);\r
-    g_logv(NULL, G_LOG_LEVEL_WARNING, fmt, args);\r
-    va_end(args);\r
-    g_log(NULL,  G_LOG_LEVEL_WARNING, "\n");\r
-}\r
-\r
-\r
-//########################################################################\r
-//# U T I L I T Y\r
-//########################################################################\r
-\r
-/**\r
- * Got this method from Bulia, and modified it a bit.  It basically\r
- * starts with this style, gets its SPObject parent, walks up the object\r
- * tree and finds all of the opacities and multiplies them.\r
- *\r
- * We use this for our "flat" object output.  If the code is modified\r
- * to reflect a tree of <groups>, then this will be unneccessary.\r
- */\r
-static double effective_opacity(const SPStyle *style)\r
-{\r
-    double val = 1.0;\r
-    for (SPObject const *obj = style->object; obj ; obj = obj->parent)\r
-        {\r
-        style = SP_OBJECT_STYLE(obj);\r
-        if (style)\r
-            val *= SP_SCALE24_TO_FLOAT(style->opacity.value);\r
-        }\r
-    return val;\r
-}\r
-\r
-//########################################################################\r
-//# OUTPUT FORMATTING\r
-//########################################################################\r
-\r
-\r
-/**\r
- * We want to control floating output format.\r
- * Especially to avoid localization. (decimal ',' for example)\r
- */\r
-static JavaFXOutput::String dstr(double d)\r
-{\r
-    char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];\r
-    g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,\r
-                  "%.8f", (gdouble)d);\r
-    JavaFXOutput::String s = dbuf;\r
-    return s;\r
-}\r
-\r
-#define DSTR(d) (dstr(d).c_str())\r
-\r
-\r
-/**\r
- * Format a double as an integer\r
- */\r
-static JavaFXOutput::String istr(double d)\r
-{\r
-    char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];\r
-    g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,\r
-                  "%.0f", (gdouble)d);\r
-    JavaFXOutput::String s = dbuf;\r
-    return s;\r
-}\r
-\r
-#define ISTR(d) (istr(d).c_str())\r
-\r
-\r
-/**\r
- * Format an rgba() string\r
- */\r
-static JavaFXOutput::String rgba(guint32 rgba)\r
-{\r
-    unsigned int r = SP_RGBA32_R_U(rgba);\r
-    unsigned int g = SP_RGBA32_G_U(rgba);\r
-    unsigned int b = SP_RGBA32_B_U(rgba);\r
-    unsigned int a = SP_RGBA32_A_U(rgba);\r
-    char buf[80];\r
-    snprintf(buf, 79, "Color.rgb(0x%02x, 0x%02x, 0x%02x, %s)",\r
-                           r, g, b, DSTR((double)a/256.0));\r
-    JavaFXOutput::String s = buf;\r
-    return s;\r
-}\r
-\r
-\r
-/**\r
- * Format an rgba() string for a color and a 0.0-1.0 alpha\r
- */\r
-static JavaFXOutput::String rgba(SPColor color, gdouble alpha)\r
-{\r
-    return rgba(color.toRGBA32(alpha));\r
-}\r
-\r
-/**\r
- * Map Inkscape linecap styles to JavaFX\r
- */\r
-static JavaFXOutput::String getStrokeLineCap(unsigned value) {\r
-    switch(value) {\r
-        case SP_STROKE_LINECAP_BUTT:\r
-            return "StrokeLineCap.BUTT";\r
-        case SP_STROKE_LINECAP_ROUND:\r
-            return "StrokeLineCap.ROUND";\r
-        case SP_STROKE_LINECAP_SQUARE:\r
-            return "StrokeLineCap.SQUARE";\r
-        default:\r
-            return "INVALID LINE CAP";\r
-    }\r
-}\r
-\r
-\r
-/**\r
- * Map Inkscape linejoin styles to JavaFX\r
- */\r
-static JavaFXOutput::String getStrokeLineJoin(unsigned value) {\r
-    switch(value) {\r
-        case SP_STROKE_LINEJOIN_MITER:\r
-            return "StrokeLineJoin.MITER";\r
-        case SP_STROKE_LINEJOIN_ROUND:\r
-            return "StrokeLineJoin.ROUND";\r
-        case SP_STROKE_LINEJOIN_BEVEL:\r
-            return "StrokeLineJoin.BEVEL";\r
-        default:\r
-            return "INVALID LINE JOIN";\r
-    }\r
-}\r
-\r
-\r
-/**\r
- * Replace illegal characters for JavaFX for a underscore.\r
- */\r
-static JavaFXOutput::String sanatize(const JavaFXOutput::String &badstr){\r
-    JavaFXOutput::String good(badstr);\r
-    for (int pos = 0; pos < badstr.length(); ++pos )\r
-        if((badstr.at(pos)=='-')||(badstr.at(pos)==' '))\r
-            good.replace(pos, 1, "_");\r
-    return good;\r
-}\r
-\r
-/**\r
- *  Output data to the buffer, printf()-style\r
- */\r
-void JavaFXOutput::out(const char *fmt, ...)\r
-{\r
-    va_list args;\r
-    va_start(args, fmt);\r
-    gchar *output = g_strdup_vprintf(fmt, args);\r
-    va_end(args);\r
-    outbuf.append(output);\r
-    g_free(output);\r
-}\r
-\r
-\r
-\r
-/**\r
- * Output the file header\r
- */\r
-bool JavaFXOutput::doHeader()\r
-{\r
-    time_t tim = time(NULL);\r
-    out("/*###################################################################\n");\r
-    out("### This JavaFX document was generated by Inkscape\n");\r
-    out("### http://www.inkscape.org\n");\r
-    out("### Created: %s",   ctime(&tim));\r
-    out("### Version: %s\n", Inkscape::version_string);\r
-    out("#####################################################################\n");\r
-    out("### NOTES:\n");\r
-    out("### ============\n");\r
-    out("### JavaFX information can be found at\n");\r
-    out("### hhttps://openjfx.dev.java.net\n");\r
-    out("###\n");\r
-    out("### If you have any problems with this output, please see the\n");\r
-    out("### Inkscape project at http://www.inkscape.org, or visit\n");\r
-    out("### the #inkscape channel on irc.freenode.net . \n");\r
-    out("###\n");\r
-    out("###################################################################*/\n");\r
-    out("\n\n");\r
-    out("/*###################################################################\n");\r
-    out("##   Exports in this file\n");\r
-    out("##==========================\n");\r
-    out("##    Shapes   : %d\n", nrShapes);\r
-    out("##    Nodes    : %d\n", nrNodes);\r
-    out("###################################################################*/\n");\r
-    out("\n\n");\r
-\r
-    // import javafx libraries we can need\r
-    out("import javafx.application.*;\n");\r
-    out("import javafx.scene.*;\n");\r
-    out("import javafx.scene.geometry.*;\n");\r
-    out("import javafx.scene.transform.*;\n");\r
-    out("import javafx.scene.paint.*;\n");\r
-    out("\n");\r
-\r
-    out("\n\n");\r
-\r
-    // Creates a class extended from CustomNode\r
-    out("public class %s extends CustomNode {\n", name.c_str());\r
-\r
-    return true;\r
-}\r
-\r
-\r
-\r
-/**\r
- *  Output the file footer\r
- */\r
-bool JavaFXOutput::doTail()\r
-{\r
-    float border = 25.0;\r
-\r
-    // Write the tail of CustomNode\r
-    out("           ] // content\n");\r
-    out("           transform: Translate { x : %s, y : %s }\n",\r
-                 DSTR((-minx) + border), DSTR((-miny) + border) );\r
-    out("       } // Group\n");\r
-    out("   } // function create()\n");\r
-    out("} // class %s\n", name.c_str());\r
-    out("\n");\r
-\r
-    // Frame\r
-    out("Frame {\n");\r
-    out("    title: \"%s\"\n", name.c_str());\r
-    out("    width: %s\n",  ISTR(maxx-minx + border * 2.0));\r
-    out("    height: %s\n", ISTR(maxy-miny + border * 2.0));\r
-    out("    visible: true\n");\r
-\r
-    // Stage\r
-    out("    stage: Stage {\n");\r
-    out("        content: %s{}\n", name.c_str());\r
-    out("    } // Stage\n");\r
-\r
-    out("} // Frame\n");\r
-\r
-    out("\n");\r
-\r
-    out("/*###################################################################\n");\r
-    out("### E N D   C L A S S    %s\n", name.c_str());\r
-    out("###################################################################*/\n");\r
-    out("\n\n");\r
-    return true;\r
-}\r
-\r
-\r
-\r
-/**\r
- *  Output gradient information to the buffer\r
- */\r
-bool JavaFXOutput::doGradient(SPGradient *grad, const String &id)\r
-{\r
-    String jfxid = sanatize(id);\r
-\r
-    if (SP_IS_LINEARGRADIENT(grad))\r
-        {\r
-        SPLinearGradient *g = SP_LINEARGRADIENT(grad);\r
-        out("    /* create LinearGradient for %s */\n", jfxid.c_str());\r
-        out("    private function %s(): LinearGradient {\n",  jfxid.c_str());\r
-        out("        LinearGradient {\n");\r
-        std::vector<SPGradientStop> stops = g->vector.stops;\r
-        if (stops.size() > 0)\r
-            {\r
-            out("            stops:\n");\r
-            out("                [\n");\r
-            for (unsigned int i = 0 ; i<stops.size() ; i++)\r
-                {\r
-                SPGradientStop stop = stops[i];\r
-                out("                Stop {\n");\r
-                out("                    offset: %s\n", DSTR(stop.offset));\r
-                out("                    color: %s\n",  rgba(stop.color, stop.opacity).c_str());\r
-                out("                },\n");\r
-                }\r
-            out("            ]\n");\r
-            }\r
-        out("        };\n");\r
-        out("    } // end LinearGradient: %s\n", jfxid.c_str());\r
-        out("\n\n");\r
-        }\r
-    else if (SP_IS_RADIALGRADIENT(grad))\r
-        {\r
-        SPRadialGradient *g = SP_RADIALGRADIENT(grad);\r
-        out("    /* create RadialGradient for %s */\n", jfxid.c_str());\r
-        out("    private function %s() {\n", jfxid.c_str());\r
-        out("        RadialGradient {\n");\r
-        out("            centerX: %s\n", DSTR(g->cx.value));\r
-        out("            centerY: %s\n", DSTR(g->cy.value));\r
-        out("            focusX: %s\n",  DSTR(g->fx.value));\r
-        out("            focusY: %s\n",  DSTR(g->fy.value));\r
-        out("            radius: %s\n",  DSTR(g->r.value ));\r
-        std::vector<SPGradientStop> stops = g->vector.stops;\r
-        if (stops.size() > 0)\r
-            {\r
-            out("            stops:\n");\r
-            out("            [\n");\r
-            for (unsigned int i = 0 ; i<stops.size() ; i++)\r
-                {\r
-                SPGradientStop stop = stops[i];\r
-                out("                Stop {\n");\r
-                out("                    offset: %s\n", DSTR(stop.offset));\r
-                out("                    color: %s\n",  rgba(stop.color, stop.opacity).c_str());\r
-                out("                },\n");\r
-                }\r
-            out("            ]\n");\r
-            }\r
-        out("        };\n");\r
-        out("    } // end RadialGradient: %s\n", jfxid.c_str());\r
-        out("\n\n");\r
-        }\r
-    else\r
-        {\r
-        err("Unknown gradient type for '%s'\n", jfxid.c_str());\r
-        return false;\r
-        }\r
-\r
-\r
-    return true;\r
-}\r
-\r
-\r
-\r
-\r
-/**\r
- *  Output an element's style attribute\r
- */\r
-bool JavaFXOutput::doStyle(SPStyle *style)\r
-{\r
-    if (!style)\r
-        return true;\r
-\r
-    out("            opacity: %s\n", DSTR(effective_opacity(style)));\r
-\r
-    /**\r
-     * Fill\r
-     */\r
-    SPIPaint fill = style->fill;\r
-    if (fill.isColor())\r
-        {\r
-        // see color.h for how to parse SPColor\r
-        out("            fill: %s\n",\r
-            rgba(fill.value.color, SP_SCALE24_TO_FLOAT(style->fill_opacity.value)).c_str());\r
-        }\r
-    else if (fill.isPaintserver()){\r
-        if (fill.value.href && fill.value.href->getURI() ){\r
-            String uri = fill.value.href->getURI()->toString();\r
-            /* trim the anchor '#' from the front */\r
-            if (uri.size() > 0 && uri[0]=='#')\r
-                uri = uri.substr(1);\r
-            out("            fill: %s()\n", sanatize(uri).c_str());\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Stroke\r
-     */\r
-    /**\r
-     *NOTE:  Things in style we can use:\r
-     * SPIPaint stroke;\r
-     * SPILength stroke_width;\r
-     * SPIEnum stroke_linecap;\r
-     * SPIEnum stroke_linejoin;\r
-     * SPIFloat stroke_miterlimit;\r
-     * NRVpathDash stroke_dash;\r
-     * unsigned stroke_dasharray_set : 1;\r
-     * unsigned stroke_dasharray_inherit : 1;\r
-     * unsigned stroke_dashoffset_set : 1;\r
-     * SPIScale24 stroke_opacity;\r
-     */\r
-    if (style->stroke_opacity.value > 0)\r
-        {\r
-        SPIPaint stroke = style->stroke;\r
-        out("            stroke: %s\n",\r
-            rgba(stroke.value.color, SP_SCALE24_TO_FLOAT(style->stroke_opacity.value)).c_str());\r
-        double strokewidth = style->stroke_width.value;\r
-        unsigned linecap   = style->stroke_linecap.value;\r
-        unsigned linejoin  = style->stroke_linejoin.value;\r
-        out("            strokeWidth: %s\n",      DSTR(strokewidth));\r
-        out("            strokeLineCap: %s\n",    getStrokeLineCap(linecap).c_str());\r
-        out("            strokeLineJoin: %s\n",   getStrokeLineJoin(linejoin).c_str());\r
-        out("            strokeMiterLimit: %s\n", DSTR(style->stroke_miterlimit.value));\r
-        if(style->stroke_dasharray_set) {\r
-           if(style->stroke_dashoffset_set) {\r
-               out("            strokeDashOffset: %s\n", DSTR(style->stroke_dash.offset));\r
-           }\r
-           out("            strokeDashArray: [ ");\r
-           for(int i = 0; i < style->stroke_dash.n_dash; i++ ) {\r
-               if(i > 0) {\r
-                   out(", %.2lf", style->stroke_dash.dash[i]);\r
-               }else {\r
-                   out(" %.2lf", style->stroke_dash.dash[i]);\r
-               }\r
-           }\r
-           out(" ]\n");\r
-        }\r
-\r
-        }\r
-\r
-    return true;\r
-}\r
-\r
-\r
-#if 1\r
-\r
-/**\r
- *  Output the curve data to buffer\r
- */\r
-bool JavaFXOutput::doCurve(SPItem *item, const String &id)\r
-{\r
-    using Geom::X;\r
-    using Geom::Y;\r
-\r
-    String jfxid = sanatize(id);\r
-\r
-    //### Get the Shape\r
-    if (!SP_IS_SHAPE(item))//Bulia's suggestion.  Allow all shapes\r
-        return true;\r
-\r
-    SPShape *shape = SP_SHAPE(item);\r
-    SPCurve *curve = shape->curve;\r
-    if (curve->is_empty())\r
-        return true;\r
-\r
-    nrShapes++;\r
-\r
-    out("    /** path %s */\n", jfxid.c_str());\r
-    out("    private function %s() : Path {\n",jfxid.c_str());\r
-    out("        Path {\n");\r
-    out("            id: \"%s\"\n", jfxid.c_str());\r
-\r
-    /**\r
-     * Output the style information\r
-     */\r
-    if (!doStyle(SP_OBJECT_STYLE(shape)))\r
-        return false;\r
-\r
-    // convert the path to only lineto's and cubic curveto's:\r
-    Geom::Scale yflip(1.0, -1.0);\r
-    Geom::Matrix tf = sp_item_i2d_affine(item) * yflip;\r
-    Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );\r
-\r
-    //Count the NR_CURVETOs/LINETOs (including closing line segment)\r
-    guint segmentCount = 0;\r
-    for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {\r
-        segmentCount += (*it).size();\r
-        if (it->closed())\r
-            segmentCount += 1;\r
-    }\r
-\r
-    out("            elements: [\n");\r
-\r
-    unsigned int segmentNr = 0;\r
-\r
-    nrNodes += segmentCount;\r
-\r
-    Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() );\r
-\r
-    /**\r
-     * For all Subpaths in the <path>\r
-     */\r
-    for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)\r
-        {\r
-        Geom::Point p = pit->front().initialPoint();\r
-        cminmax.expandTo(p);\r
-        out("                MoveTo {\n");\r
-        out("                    x: %s\n", DSTR(p[X]));\r
-        out("                    y: %s\n", DSTR(p[Y]));\r
-        out("                },\n");\r
-\r
-        /**\r
-         * For all segments in the subpath\r
-         */\r
-        for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)\r
-            {\r
-            //### LINE\r
-            if( dynamic_cast<Geom::LineSegment  const *> (&*cit) ||\r
-                dynamic_cast<Geom::HLineSegment const *> (&*cit) ||\r
-                dynamic_cast<Geom::VLineSegment const *> (&*cit) )\r
-                {\r
-                Geom::Point p = cit->finalPoint();\r
-                out("                LineTo {\n");\r
-                out("                    x: %s\n", DSTR(p[X]));\r
-                out("                    y: %s\n", DSTR(p[Y]));\r
-                out("                },\n");\r
-                nrNodes++;\r
-                }\r
-            //### BEZIER\r
-            else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))\r
-                {\r
-                std::vector<Geom::Point> points = cubic->points();\r
-                Geom::Point p1 = points[1];\r
-                Geom::Point p2 = points[2];\r
-                Geom::Point p3 = points[3];\r
-                out("                CurveTo {\n");\r
-                out("                    controlX1: %s\n", DSTR(p1[X]));\r
-                out("                    controlY1: %s\n", DSTR(p1[Y]));\r
-                out("                    controlX2: %s\n", DSTR(p2[X]));\r
-                out("                    controlY2: %s\n", DSTR(p2[Y]));\r
-                out("                    x: %s\n",         DSTR(p3[X]));\r
-                out("                    y: %s\n",         DSTR(p3[Y]));\r
-                out("                },\n");\r
-                nrNodes++;\r
-                }\r
-            else\r
-                {\r
-                g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");\r
-                }\r
-            segmentNr++;\r
-            cminmax.expandTo(cit->finalPoint());\r
-            }\r
-        if (pit->closed())\r
-            {\r
-            out("                ClosePath {},\n");\r
-            }\r
-        }\r
-\r
-    out("            ] // elements\n");\r
-    out("        }; // Path\n");\r
-    out("    } // end path %s\n\n", jfxid.c_str());\r
-\r
-    double cminx = cminmax.min()[X];\r
-    double cmaxx = cminmax.max()[X];\r
-    double cminy = cminmax.min()[Y];\r
-    double cmaxy = cminmax.max()[Y];\r
-\r
-    if (cminx < minx)\r
-        minx = cminx;\r
-    if (cmaxx > maxx)\r
-        maxx = cmaxx;\r
-    if (cminy < miny)\r
-        miny = cminy;\r
-    if (cmaxy > maxy)\r
-        maxy = cmaxy;\r
-\r
-    return true;\r
-}\r
-\r
-\r
-\r
-#else\r
-\r
-/**\r
- *  Output the curve data to buffer\r
- */\r
-bool JavaFXOutput::doCurve(SPItem *item, const String &id)\r
-{\r
-    using Geom::X;\r
-    using Geom::Y;\r
-\r
-    //### Get the Shape\r
-    if (!SP_IS_SHAPE(item))//Bulia's suggestion.  Allow all shapes\r
-        return true;\r
-\r
-    SPShape *shape = SP_SHAPE(item);\r
-    SPCurve *curve = shape->curve;\r
-    if (curve->is_empty())\r
-        return true;\r
-\r
-    nrShapes++;\r
-\r
-    out("        SVGPath \n");\r
-    out("        {\n");\r
-    out("        id: \"%s\"\n", id.c_str());\r
-\r
-    /**\r
-     * Output the style information\r
-     */\r
-    if (!doStyle(SP_OBJECT_STYLE(shape)))\r
-        return false;\r
-\r
-    // convert the path to only lineto's and cubic curveto's:\r
-    Geom::Scale yflip(1.0, -1.0);\r
-    Geom::Matrix tf = sp_item_i2d_affine(item) * yflip;\r
-    Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );\r
-    \r
-    //Count the NR_CURVETOs/LINETOs (including closing line segment)\r
-    nrNodes = 0;\r
-    for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {\r
-        nrNodes += (*it).size();\r
-        if (it->closed())\r
-            nrNodes += 1;\r
-    }\r
-\r
-    char *dataStr = sp_svg_write_path(pathv);\r
-    out("        content: \"%s\"\n", dataStr);\r
-    free(dataStr);\r
-\r
-    Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() ); \r
-\r
-    /**\r
-     * Get the Min and Max X and Y extends for the Path. \r
-     * ....For all Subpaths in the <path>\r
-     */             \r
-    for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)\r
-        {\r
-        cminmax.expandTo(pit->front().initialPoint());\r
-        /**\r
-         * For all segments in the subpath\r
-         */                     \r
-        for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)\r
-            {\r
-            cminmax.expandTo(cit->finalPoint());\r
-            }\r
-        }\r
-\r
-    out("        },\n");\r
-\r
-    double cminx = cminmax.min()[X];\r
-    double cmaxx = cminmax.max()[X];\r
-    double cminy = cminmax.min()[Y];\r
-    double cmaxy = cminmax.max()[Y];\r
-\r
-    if (cminx < minx)\r
-        minx = cminx;\r
-    if (cmaxx > maxx)\r
-        maxx = cmaxx;\r
-    if (cminy < miny)\r
-        miny = cminy;\r
-    if (cmaxy > maxy)\r
-        maxy = cmaxy;\r
-\r
-    return true;\r
-}\r
-\r
-\r
-\r
-#endif  /* #if o */\r
-\r
-\r
-\r
-/**\r
- *  Output the tree data to buffer\r
- */\r
-bool JavaFXOutput::doTreeRecursive(SPDocument *doc, SPObject *obj)\r
-{\r
-    /**\r
-     * Check the type of node and process\r
-     */\r
-    String id;\r
-    if (!obj->id)\r
-        {\r
-        char buf[16];\r
-        sprintf(buf, "id%d", idindex++);\r
-        id = buf;\r
-        }\r
-    else\r
-        {\r
-        id = obj->id;\r
-        }\r
-    if (SP_IS_ITEM(obj))\r
-        {\r
-        SPItem *item = SP_ITEM(obj);\r
-        if (!doCurve(item, id))\r
-            return false;\r
-        }\r
-    else if (SP_IS_GRADIENT(obj))\r
-        {\r
-        SPGradient *grad = SP_GRADIENT(obj);\r
-        if (!doGradient(grad, id))\r
-            return false;\r
-        }\r
-\r
-    /**\r
-     * Descend into children\r
-     */             \r
-    for (SPObject *child = obj->firstChild() ; child ; child = child->next)\r
-        {\r
-               if (!doTreeRecursive(doc, child))\r
-                   return false;\r
-               }\r
-\r
-    return true;\r
-}\r
-\r
-\r
-/**\r
- *  Output the curve data to buffer\r
- */\r
-bool JavaFXOutput::doTree(SPDocument *doc)\r
-{\r
-\r
-    double bignum = 1000000.0;\r
-    minx  =  bignum;\r
-    maxx  = -bignum;\r
-    miny  =  bignum;\r
-    maxy  = -bignum;\r
-\r
-    if (!doTreeRecursive(doc, doc->root))\r
-        return false;\r
-\r
-    return true;\r
-\r
-}\r
-\r
-\r
-bool JavaFXOutput::doBody(SPDocument *doc, SPObject *obj)\r
-{\r
-    /**\r
-     * Check the type of node and process\r
-     */\r
-    String id;\r
-    if (!obj->id)\r
-        {\r
-        char buf[16];\r
-        sprintf(buf, "id%d", idindex++);\r
-        id = buf;\r
-        }\r
-    else\r
-        {\r
-        id = obj->id;\r
-        }\r
-\r
-    if (SP_IS_ITEM(obj)) {\r
-        SPItem *item = SP_ITEM(obj);\r
-        //### Get the Shape\r
-        if (SP_IS_SHAPE(item)) {//Bulia's suggestion.  Allow all shapes\r
-            SPShape *shape = SP_SHAPE(item);\r
-            SPCurve *curve = shape->curve;\r
-            if (!curve->is_empty())\r
-                out("               %s(),\n", id.c_str());\r
-        }\r
-    }\r
-    else if (SP_IS_GRADIENT(obj)) {\r
-        //TODO: what to do with Gradient within body?????\r
-        //SPGradient *grad = SP_GRADIENT(reprobj);\r
-        //if (!doGradient(grad, id))\r
-        //    return false;\r
-    }\r
-\r
-    /**\r
-     * Descend into children\r
-     */\r
-    for (SPObject *child = obj->firstChild() ; child ; child = child->next)\r
-        {\r
-               if (!doBody(doc, child))\r
-                   return false;\r
-               }\r
-\r
-    return true;\r
-}\r
-\r
-\r
-\r
-//########################################################################\r
-//# M A I N    O U T P U T\r
-//########################################################################\r
-\r
-\r
-\r
-/**\r
- *  Set values back to initial state\r
- */\r
-void JavaFXOutput::reset()\r
-{\r
-    nrNodes    = 0;\r
-    nrShapes   = 0;\r
-    idindex    = 0;\r
-    name.clear();\r
-    outbuf.clear();\r
-    foutbuf.clear();\r
-}\r
-\r
-\r
-\r
-/**\r
- * Saves the <paths> of an Inkscape SVG file as JavaFX spline definitions\r
- */\r
-bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)\r
-{\r
-    reset();\r
-\r
-\r
-    name = Glib::path_get_basename(filename_utf8);\r
-    int pos = name.find('.');\r
-    if (pos > 0)\r
-        name = name.substr(0, pos);\r
-\r
-\r
-    //###### SAVE IN JAVAFX FORMAT TO BUFFER\r
-    //# Lets do the curves first, to get the stats\r
-\r
-    if (!doTree(doc))\r
-        return false;\r
-    String curveBuf = outbuf;\r
-    outbuf.clear();\r
-\r
-    if (!doHeader())\r
-        return false;\r
-\r
-    outbuf.append(curveBuf);\r
-\r
-#ifdef JAVAFX_SDK_1_0\r
-    out("   override function create(): Node {\n");\r
-#else\r
-    out("   public function create(): Node {\n");\r
-#endif\r
-    out("       Group {\n");\r
-    out("           content: [\n");\r
-    idindex    = 0;\r
-\r
-    doBody(doc, doc->root);\r
-\r
-    if (!doTail())\r
-        return false;\r
-\r
-\r
-\r
-    //###### WRITE TO FILE\r
-    FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w");\r
-    if (!f)\r
-        {\r
-        err("Could open JavaFX file '%s' for writing", filename_utf8);\r
-        return false;\r
-        }\r
-\r
-    for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)\r
-        {\r
-        fputc(*iter, f);\r
-        }\r
-        \r
-    fclose(f);\r
-    \r
-    return true;\r
-}\r
-\r
-\r
-\r
-\r
-//########################################################################\r
-//# EXTENSION API\r
-//########################################################################\r
-\r
-\r
-\r
-#include "clear-n_.h"\r
-\r
-\r
-\r
-/**\r
- * API call to save document\r
-*/\r
-void\r
-JavaFXOutput::save(Inkscape::Extension::Output */*mod*/,\r
-                        SPDocument *doc, gchar const *filename_utf8)\r
-{\r
-    /* N.B. The name `filename_utf8' represents the fact that we want it to be in utf8; whereas in\r
-     * fact we know that some callers of Extension::save pass something in the filesystem's\r
-     * encoding, while others do g_filename_to_utf8 before calling.\r
-     *\r
-     * In terms of safety, it's best to make all callers pass actual filenames, since in general\r
-     * one can't round-trip from filename to utf8 back to the same filename.  Whereas the argument\r
-     * for passing utf8 filenames is one of convenience: we often want to pass to g_warning or\r
-     * store as a string (rather than a byte stream) in XML, or the like. */\r
-    if (!saveDocument(doc, filename_utf8))\r
-        {\r
-        g_warning("Could not save JavaFX file '%s'", filename_utf8);\r
-        }\r
-}\r
-\r
-\r
-\r
-/**\r
- * Make sure that we are in the database\r
- */\r
-bool JavaFXOutput::check (Inkscape::Extension::Extension */*module*/)\r
-{\r
-    /* We don't need a Key\r
-    if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_JFX))\r
-        return FALSE;\r
-    */\r
-\r
-    return true;\r
-}\r
-\r
-\r
-\r
-/**\r
- * This is the definition of JavaFX output.  This function just\r
- * calls the extension system with the memory allocated XML that\r
- * describes the data.\r
-*/\r
-void\r
-JavaFXOutput::init()\r
-{\r
-    Inkscape::Extension::build_from_mem(\r
-        "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"\r
-            "<name>" N_("JavaFX Output") "</name>\n"\r
-            "<id>org.inkscape.output.jfx</id>\n"\r
-            "<output>\n"\r
-                "<extension>.fx</extension>\n"\r
-                "<mimetype>text/x-javafx-script</mimetype>\n"\r
-                "<filetypename>" N_("JavaFX (*.fx)") "</filetypename>\n"\r
-                "<filetypetooltip>" N_("JavaFX Raytracer File") "</filetypetooltip>\n"\r
-            "</output>\n"\r
-        "</inkscape-extension>",\r
-        new JavaFXOutput());\r
-}\r
-\r
-\r
-\r
-\r
-\r
-}  // namespace Internal\r
-}  // namespace Extension\r
-}  // namespace Inkscape\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
+/*
+ * A simple utility for exporting Inkscape svg Shapes as JavaFX paths.
+ *
+ *  For information on the JavaFX file format, see:
+ *      https://openjfx.dev.java.net/
+ *
+ * Authors:
+ *   Bob Jamison <ishmal@inkscape.org>
+ *   Silveira Neto <silveiraneto@gmail.com>
+ *   Jim Clarke <Jim.Clarke@sun.com>
+ *
+ * Copyright (C) 2008 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "javafx-out.h"
+#include <inkscape.h>
+#include <inkscape-version.h>
+#include <sp-path.h>
+#include <sp-linear-gradient.h>
+#include <sp-radial-gradient.h>
+#include <style.h>
+#include <display/curve.h>
+#include <display/canvas-bpath.h>
+#include <svg/svg.h>
+#include <extension/system.h>
+#include <2geom/pathvector.h>
+#include <2geom/rect.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/hvlinesegment.h>
+#include "helper/geom.h"
+#include "helper/geom-curves.h"
+#include <io/sys.h>
+
+
+#include <string>
+#include <stdio.h>
+#include <stdarg.h>
+
+
+namespace Inkscape
+{
+namespace Extension
+{
+namespace Internal
+{
+
+
+
+
+//########################################################################
+//# M E S S A G E S
+//########################################################################
+
+static void err(const char *fmt, ...)
+{
+    va_list args;
+    g_log(NULL,  G_LOG_LEVEL_WARNING, "javafx-out err: ");
+    va_start(args, fmt);
+    g_logv(NULL, G_LOG_LEVEL_WARNING, fmt, args);
+    va_end(args);
+    g_log(NULL,  G_LOG_LEVEL_WARNING, "\n");
+}
+
+
+//########################################################################
+//# U T I L I T Y
+//########################################################################
+
+/**
+ * Got this method from Bulia, and modified it a bit.  It basically
+ * starts with this style, gets its SPObject parent, walks up the object
+ * tree and finds all of the opacities and multiplies them.
+ *
+ * We use this for our "flat" object output.  If the code is modified
+ * to reflect a tree of <groups>, then this will be unneccessary.
+ */
+static double effective_opacity(const SPStyle *style)
+{
+    double val = 1.0;
+    for (SPObject const *obj = style->object; obj ; obj = obj->parent)
+        {
+        style = SP_OBJECT_STYLE(obj);
+        if (style)
+            val *= SP_SCALE24_TO_FLOAT(style->opacity.value);
+        }
+    return val;
+}
+
+//########################################################################
+//# OUTPUT FORMATTING
+//########################################################################
+
+
+/**
+ * We want to control floating output format.
+ * Especially to avoid localization. (decimal ',' for example)
+ */
+static JavaFXOutput::String dstr(double d)
+{
+    char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];
+    g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,
+                  "%.8f", (gdouble)d);
+    JavaFXOutput::String s = dbuf;
+    return s;
+}
+
+#define DSTR(d) (dstr(d).c_str())
+
+
+/**
+ * Format a double as an integer
+ */
+static JavaFXOutput::String istr(double d)
+{
+    char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];
+    g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,
+                  "%.0f", (gdouble)d);
+    JavaFXOutput::String s = dbuf;
+    return s;
+}
+
+#define ISTR(d) (istr(d).c_str())
+
+
+/**
+ * Format an rgba() string
+ */
+static JavaFXOutput::String rgba(guint32 rgba)
+{
+    unsigned int r = SP_RGBA32_R_U(rgba);
+    unsigned int g = SP_RGBA32_G_U(rgba);
+    unsigned int b = SP_RGBA32_B_U(rgba);
+    unsigned int a = SP_RGBA32_A_U(rgba);
+    char buf[80];
+    snprintf(buf, 79, "Color.rgb(0x%02x, 0x%02x, 0x%02x, %s)",
+                           r, g, b, DSTR((double)a/256.0));
+    JavaFXOutput::String s = buf;
+    return s;
+}
+
+
+/**
+ * Format an rgba() string for a color and a 0.0-1.0 alpha
+ */
+static JavaFXOutput::String rgba(SPColor color, gdouble alpha)
+{
+    return rgba(color.toRGBA32(alpha));
+}
+
+/**
+ * Map Inkscape linecap styles to JavaFX
+ */
+static JavaFXOutput::String getStrokeLineCap(unsigned value) {
+    switch(value) {
+        case SP_STROKE_LINECAP_BUTT:
+            return "StrokeLineCap.BUTT";
+        case SP_STROKE_LINECAP_ROUND:
+            return "StrokeLineCap.ROUND";
+        case SP_STROKE_LINECAP_SQUARE:
+            return "StrokeLineCap.SQUARE";
+        default:
+            return "INVALID LINE CAP";
+    }
+}
+
+
+/**
+ * Map Inkscape linejoin styles to JavaFX
+ */
+static JavaFXOutput::String getStrokeLineJoin(unsigned value) {
+    switch(value) {
+        case SP_STROKE_LINEJOIN_MITER:
+            return "StrokeLineJoin.MITER";
+        case SP_STROKE_LINEJOIN_ROUND:
+            return "StrokeLineJoin.ROUND";
+        case SP_STROKE_LINEJOIN_BEVEL:
+            return "StrokeLineJoin.BEVEL";
+        default:
+            return "INVALID LINE JOIN";
+    }
+}
+
+
+/**
+ * Replace illegal characters for JavaFX for a underscore.
+ */
+static JavaFXOutput::String sanatize(const JavaFXOutput::String &badstr){
+    JavaFXOutput::String good(badstr);
+    for (int pos = 0; pos < badstr.length(); ++pos )
+        if((badstr.at(pos)=='-')||(badstr.at(pos)==' '))
+            good.replace(pos, 1, "_");
+    return good;
+}
+
+/**
+ *  Output data to the buffer, printf()-style
+ */
+void JavaFXOutput::out(const char *fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    gchar *output = g_strdup_vprintf(fmt, args);
+    va_end(args);
+    outbuf.append(output);
+    g_free(output);
+}
+
+
+
+/**
+ * Output the file header
+ */
+bool JavaFXOutput::doHeader()
+{
+    time_t tim = time(NULL);
+    out("/*###################################################################\n");
+    out("### This JavaFX document was generated by Inkscape\n");
+    out("### http://www.inkscape.org\n");
+    out("### Created: %s",   ctime(&tim));
+    out("### Version: %s\n", Inkscape::version_string);
+    out("#####################################################################\n");
+    out("### NOTES:\n");
+    out("### ============\n");
+    out("### JavaFX information can be found at\n");
+    out("### hhttps://openjfx.dev.java.net\n");
+    out("###\n");
+    out("### If you have any problems with this output, please see the\n");
+    out("### Inkscape project at http://www.inkscape.org, or visit\n");
+    out("### the #inkscape channel on irc.freenode.net . \n");
+    out("###\n");
+    out("###################################################################*/\n");
+    out("\n\n");
+    out("/*###################################################################\n");
+    out("##   Exports in this file\n");
+    out("##==========================\n");
+    out("##    Shapes   : %d\n", nrShapes);
+    out("##    Nodes    : %d\n", nrNodes);
+    out("###################################################################*/\n");
+    out("\n\n");
+
+    // import javafx libraries we can need
+    out("import javafx.application.*;\n");
+    out("import javafx.scene.*;\n");
+    out("import javafx.scene.geometry.*;\n");
+    out("import javafx.scene.transform.*;\n");
+    out("import javafx.scene.paint.*;\n");
+    out("\n");
+
+    out("\n\n");
+
+    // Creates a class extended from CustomNode
+    out("public class %s extends CustomNode {\n", name.c_str());
+
+    return true;
+}
+
+
+
+/**
+ *  Output the file footer
+ */
+bool JavaFXOutput::doTail()
+{
+    float border = 25.0;
+
+    // Write the tail of CustomNode
+    out("           ] // content\n");
+    out("           transform: Translate { x : %s, y : %s }\n",
+                 DSTR((-minx) + border), DSTR((-miny) + border) );
+    out("       } // Group\n");
+    out("   } // function create()\n");
+    out("} // class %s\n", name.c_str());
+    out("\n");
+
+    // Frame
+    out("Frame {\n");
+    out("    title: \"%s\"\n", name.c_str());
+    out("    width: %s\n",  ISTR(maxx-minx + border * 2.0));
+    out("    height: %s\n", ISTR(maxy-miny + border * 2.0));
+    out("    visible: true\n");
+
+    // Stage
+    out("    stage: Stage {\n");
+    out("        content: %s{}\n", name.c_str());
+    out("    } // Stage\n");
+
+    out("} // Frame\n");
+
+    out("\n");
+
+    out("/*###################################################################\n");
+    out("### E N D   C L A S S    %s\n", name.c_str());
+    out("###################################################################*/\n");
+    out("\n\n");
+    return true;
+}
+
+
+
+/**
+ *  Output gradient information to the buffer
+ */
+bool JavaFXOutput::doGradient(SPGradient *grad, const String &id)
+{
+    String jfxid = sanatize(id);
+
+    if (SP_IS_LINEARGRADIENT(grad))
+        {
+        SPLinearGradient *g = SP_LINEARGRADIENT(grad);
+        out("    /* create LinearGradient for %s */\n", jfxid.c_str());
+        out("    private function %s(): LinearGradient {\n",  jfxid.c_str());
+        out("        LinearGradient {\n");
+        std::vector<SPGradientStop> stops = g->vector.stops;
+        if (stops.size() > 0)
+            {
+            out("            stops:\n");
+            out("                [\n");
+            for (unsigned int i = 0 ; i<stops.size() ; i++)
+                {
+                SPGradientStop stop = stops[i];
+                out("                Stop {\n");
+                out("                    offset: %s\n", DSTR(stop.offset));
+                out("                    color: %s\n",  rgba(stop.color, stop.opacity).c_str());
+                out("                },\n");
+                }
+            out("            ]\n");
+            }
+        out("        };\n");
+        out("    } // end LinearGradient: %s\n", jfxid.c_str());
+        out("\n\n");
+        }
+    else if (SP_IS_RADIALGRADIENT(grad))
+        {
+        SPRadialGradient *g = SP_RADIALGRADIENT(grad);
+        out("    /* create RadialGradient for %s */\n", jfxid.c_str());
+        out("    private function %s() {\n", jfxid.c_str());
+        out("        RadialGradient {\n");
+        out("            centerX: %s\n", DSTR(g->cx.value));
+        out("            centerY: %s\n", DSTR(g->cy.value));
+        out("            focusX: %s\n",  DSTR(g->fx.value));
+        out("            focusY: %s\n",  DSTR(g->fy.value));
+        out("            radius: %s\n",  DSTR(g->r.value ));
+        std::vector<SPGradientStop> stops = g->vector.stops;
+        if (stops.size() > 0)
+            {
+            out("            stops:\n");
+            out("            [\n");
+            for (unsigned int i = 0 ; i<stops.size() ; i++)
+                {
+                SPGradientStop stop = stops[i];
+                out("                Stop {\n");
+                out("                    offset: %s\n", DSTR(stop.offset));
+                out("                    color: %s\n",  rgba(stop.color, stop.opacity).c_str());
+                out("                },\n");
+                }
+            out("            ]\n");
+            }
+        out("        };\n");
+        out("    } // end RadialGradient: %s\n", jfxid.c_str());
+        out("\n\n");
+        }
+    else
+        {
+        err("Unknown gradient type for '%s'\n", jfxid.c_str());
+        return false;
+        }
+
+
+    return true;
+}
+
+
+
+
+/**
+ *  Output an element's style attribute
+ */
+bool JavaFXOutput::doStyle(SPStyle *style)
+{
+    if (!style)
+        return true;
+
+    out("            opacity: %s\n", DSTR(effective_opacity(style)));
+
+    /**
+     * Fill
+     */
+    SPIPaint fill = style->fill;
+    if (fill.isColor())
+        {
+        // see color.h for how to parse SPColor
+        out("            fill: %s\n",
+            rgba(fill.value.color, SP_SCALE24_TO_FLOAT(style->fill_opacity.value)).c_str());
+        }
+    else if (fill.isPaintserver()){
+        if (fill.value.href && fill.value.href->getURI() ){
+            String uri = fill.value.href->getURI()->toString();
+            /* trim the anchor '#' from the front */
+            if (uri.size() > 0 && uri[0]=='#')
+                uri = uri.substr(1);
+            out("            fill: %s()\n", sanatize(uri).c_str());
+        }
+    }
+
+
+    /**
+     * Stroke
+     */
+    /**
+     *NOTE:  Things in style we can use:
+     * SPIPaint stroke;
+     * SPILength stroke_width;
+     * SPIEnum stroke_linecap;
+     * SPIEnum stroke_linejoin;
+     * SPIFloat stroke_miterlimit;
+     * NRVpathDash stroke_dash;
+     * unsigned stroke_dasharray_set : 1;
+     * unsigned stroke_dasharray_inherit : 1;
+     * unsigned stroke_dashoffset_set : 1;
+     * SPIScale24 stroke_opacity;
+     */
+    if (style->stroke_opacity.value > 0)
+        {
+        SPIPaint stroke = style->stroke;
+        out("            stroke: %s\n",
+            rgba(stroke.value.color, SP_SCALE24_TO_FLOAT(style->stroke_opacity.value)).c_str());
+        double strokewidth = style->stroke_width.value;
+        unsigned linecap   = style->stroke_linecap.value;
+        unsigned linejoin  = style->stroke_linejoin.value;
+        out("            strokeWidth: %s\n",      DSTR(strokewidth));
+        out("            strokeLineCap: %s\n",    getStrokeLineCap(linecap).c_str());
+        out("            strokeLineJoin: %s\n",   getStrokeLineJoin(linejoin).c_str());
+        out("            strokeMiterLimit: %s\n", DSTR(style->stroke_miterlimit.value));
+        if(style->stroke_dasharray_set) {
+           if(style->stroke_dashoffset_set) {
+               out("            strokeDashOffset: %s\n", DSTR(style->stroke_dash.offset));
+           }
+           out("            strokeDashArray: [ ");
+           for(int i = 0; i < style->stroke_dash.n_dash; i++ ) {
+               if(i > 0) {
+                   out(", %.2lf", style->stroke_dash.dash[i]);
+               }else {
+                   out(" %.2lf", style->stroke_dash.dash[i]);
+               }
+           }
+           out(" ]\n");
+        }
+
+        }
+
+    return true;
+}
+
+
+#if 1
+
+/**
+ *  Output the curve data to buffer
+ */
+bool JavaFXOutput::doCurve(SPItem *item, const String &id)
+{
+    using Geom::X;
+    using Geom::Y;
+
+    String jfxid = sanatize(id);
+
+    //### Get the Shape
+    if (!SP_IS_SHAPE(item))//Bulia's suggestion.  Allow all shapes
+        return true;
+
+    SPShape *shape = SP_SHAPE(item);
+    SPCurve *curve = shape->curve;
+    if (curve->is_empty())
+        return true;
+
+    nrShapes++;
+
+    out("    /** path %s */\n", jfxid.c_str());
+    out("    private function %s() : Path {\n",jfxid.c_str());
+    out("        Path {\n");
+    out("            id: \"%s\"\n", jfxid.c_str());
+
+    /**
+     * Output the style information
+     */
+    if (!doStyle(SP_OBJECT_STYLE(shape)))
+        return false;
+
+    // convert the path to only lineto's and cubic curveto's:
+    Geom::Scale yflip(1.0, -1.0);
+    Geom::Matrix tf = sp_item_i2d_affine(item) * yflip;
+    Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );
+
+    //Count the NR_CURVETOs/LINETOs (including closing line segment)
+    guint segmentCount = 0;
+    for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {
+        segmentCount += (*it).size();
+        if (it->closed())
+            segmentCount += 1;
+    }
+
+    out("            elements: [\n");
+
+    unsigned int segmentNr = 0;
+
+    nrNodes += segmentCount;
+
+    Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() );
+
+    /**
+     * For all Subpaths in the <path>
+     */
+    for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)
+        {
+        Geom::Point p = pit->front().initialPoint();
+        cminmax.expandTo(p);
+        out("                MoveTo {\n");
+        out("                    x: %s\n", DSTR(p[X]));
+        out("                    y: %s\n", DSTR(p[Y]));
+        out("                },\n");
+
+        /**
+         * For all segments in the subpath
+         */
+        for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)
+            {
+            //### LINE
+            if( dynamic_cast<Geom::LineSegment  const *> (&*cit) ||
+                dynamic_cast<Geom::HLineSegment const *> (&*cit) ||
+                dynamic_cast<Geom::VLineSegment const *> (&*cit) )
+                {
+                Geom::Point p = cit->finalPoint();
+                out("                LineTo {\n");
+                out("                    x: %s\n", DSTR(p[X]));
+                out("                    y: %s\n", DSTR(p[Y]));
+                out("                },\n");
+                nrNodes++;
+                }
+            //### BEZIER
+            else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))
+                {
+                std::vector<Geom::Point> points = cubic->points();
+                Geom::Point p1 = points[1];
+                Geom::Point p2 = points[2];
+                Geom::Point p3 = points[3];
+                out("                CurveTo {\n");
+                out("                    controlX1: %s\n", DSTR(p1[X]));
+                out("                    controlY1: %s\n", DSTR(p1[Y]));
+                out("                    controlX2: %s\n", DSTR(p2[X]));
+                out("                    controlY2: %s\n", DSTR(p2[Y]));
+                out("                    x: %s\n",         DSTR(p3[X]));
+                out("                    y: %s\n",         DSTR(p3[Y]));
+                out("                },\n");
+                nrNodes++;
+                }
+            else
+                {
+                g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");
+                }
+            segmentNr++;
+            cminmax.expandTo(cit->finalPoint());
+            }
+        if (pit->closed())
+            {
+            out("                ClosePath {},\n");
+            }
+        }
+
+    out("            ] // elements\n");
+    out("        }; // Path\n");
+    out("    } // end path %s\n\n", jfxid.c_str());
+
+    double cminx = cminmax.min()[X];
+    double cmaxx = cminmax.max()[X];
+    double cminy = cminmax.min()[Y];
+    double cmaxy = cminmax.max()[Y];
+
+    if (cminx < minx)
+        minx = cminx;
+    if (cmaxx > maxx)
+        maxx = cmaxx;
+    if (cminy < miny)
+        miny = cminy;
+    if (cmaxy > maxy)
+        maxy = cmaxy;
+
+    return true;
+}
+
+
+
+#else
+
+/**
+ *  Output the curve data to buffer
+ */
+bool JavaFXOutput::doCurve(SPItem *item, const String &id)
+{
+    using Geom::X;
+    using Geom::Y;
+
+    //### Get the Shape
+    if (!SP_IS_SHAPE(item))//Bulia's suggestion.  Allow all shapes
+        return true;
+
+    SPShape *shape = SP_SHAPE(item);
+    SPCurve *curve = shape->curve;
+    if (curve->is_empty())
+        return true;
+
+    nrShapes++;
+
+    out("        SVGPath \n");
+    out("        {\n");
+    out("        id: \"%s\"\n", id.c_str());
+
+    /**
+     * Output the style information
+     */
+    if (!doStyle(SP_OBJECT_STYLE(shape)))
+        return false;
+
+    // convert the path to only lineto's and cubic curveto's:
+    Geom::Scale yflip(1.0, -1.0);
+    Geom::Matrix tf = sp_item_i2d_affine(item) * yflip;
+    Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );
+    
+    //Count the NR_CURVETOs/LINETOs (including closing line segment)
+    nrNodes = 0;
+    for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {
+        nrNodes += (*it).size();
+        if (it->closed())
+            nrNodes += 1;
+    }
+
+    char *dataStr = sp_svg_write_path(pathv);
+    out("        content: \"%s\"\n", dataStr);
+    free(dataStr);
+
+    Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() ); 
+
+    /**
+     * Get the Min and Max X and Y extends for the Path. 
+     * ....For all Subpaths in the <path>
+     */             
+    for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)
+        {
+        cminmax.expandTo(pit->front().initialPoint());
+        /**
+         * For all segments in the subpath
+         */                     
+        for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)
+            {
+            cminmax.expandTo(cit->finalPoint());
+            }
+        }
+
+    out("        },\n");
+
+    double cminx = cminmax.min()[X];
+    double cmaxx = cminmax.max()[X];
+    double cminy = cminmax.min()[Y];
+    double cmaxy = cminmax.max()[Y];
+
+    if (cminx < minx)
+        minx = cminx;
+    if (cmaxx > maxx)
+        maxx = cmaxx;
+    if (cminy < miny)
+        miny = cminy;
+    if (cmaxy > maxy)
+        maxy = cmaxy;
+
+    return true;
+}
+
+
+
+#endif  /* #if o */
+
+
+
+/**
+ *  Output the tree data to buffer
+ */
+bool JavaFXOutput::doTreeRecursive(SPDocument *doc, SPObject *obj)
+{
+    /**
+     * Check the type of node and process
+     */
+    String id;
+    if (!obj->id)
+        {
+        char buf[16];
+        sprintf(buf, "id%d", idindex++);
+        id = buf;
+        }
+    else
+        {
+        id = obj->id;
+        }
+    if (SP_IS_ITEM(obj))
+        {
+        SPItem *item = SP_ITEM(obj);
+        if (!doCurve(item, id))
+            return false;
+        }
+    else if (SP_IS_GRADIENT(obj))
+        {
+        SPGradient *grad = SP_GRADIENT(obj);
+        if (!doGradient(grad, id))
+            return false;
+        }
+
+    /**
+     * Descend into children
+     */             
+    for (SPObject *child = obj->firstChild() ; child ; child = child->next)
+        {
+               if (!doTreeRecursive(doc, child))
+                   return false;
+               }
+
+    return true;
+}
+
+
+/**
+ *  Output the curve data to buffer
+ */
+bool JavaFXOutput::doTree(SPDocument *doc)
+{
+
+    double bignum = 1000000.0;
+    minx  =  bignum;
+    maxx  = -bignum;
+    miny  =  bignum;
+    maxy  = -bignum;
+
+    if (!doTreeRecursive(doc, doc->root))
+        return false;
+
+    return true;
+
+}
+
+
+bool JavaFXOutput::doBody(SPDocument *doc, SPObject *obj)
+{
+    /**
+     * Check the type of node and process
+     */
+    String id;
+    if (!obj->id)
+        {
+        char buf[16];
+        sprintf(buf, "id%d", idindex++);
+        id = buf;
+        }
+    else
+        {
+        id = obj->id;
+        }
+
+    if (SP_IS_ITEM(obj)) {
+        SPItem *item = SP_ITEM(obj);
+        //### Get the Shape
+        if (SP_IS_SHAPE(item)) {//Bulia's suggestion.  Allow all shapes
+            SPShape *shape = SP_SHAPE(item);
+            SPCurve *curve = shape->curve;
+            if (!curve->is_empty())
+                out("               %s(),\n", id.c_str());
+        }
+    }
+    else if (SP_IS_GRADIENT(obj)) {
+        //TODO: what to do with Gradient within body?????
+        //SPGradient *grad = SP_GRADIENT(reprobj);
+        //if (!doGradient(grad, id))
+        //    return false;
+    }
+
+    /**
+     * Descend into children
+     */
+    for (SPObject *child = obj->firstChild() ; child ; child = child->next)
+        {
+               if (!doBody(doc, child))
+                   return false;
+               }
+
+    return true;
+}
+
+
+
+//########################################################################
+//# M A I N    O U T P U T
+//########################################################################
+
+
+
+/**
+ *  Set values back to initial state
+ */
+void JavaFXOutput::reset()
+{
+    nrNodes    = 0;
+    nrShapes   = 0;
+    idindex    = 0;
+    name.clear();
+    outbuf.clear();
+    foutbuf.clear();
+}
+
+
+
+/**
+ * Saves the <paths> of an Inkscape SVG file as JavaFX spline definitions
+ */
+bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
+{
+    reset();
+
+
+    name = Glib::path_get_basename(filename_utf8);
+    int pos = name.find('.');
+    if (pos > 0)
+        name = name.substr(0, pos);
+
+
+    //###### SAVE IN JAVAFX FORMAT TO BUFFER
+    //# Lets do the curves first, to get the stats
+
+    if (!doTree(doc))
+        return false;
+    String curveBuf = outbuf;
+    outbuf.clear();
+
+    if (!doHeader())
+        return false;
+
+    outbuf.append(curveBuf);
+
+#ifdef JAVAFX_SDK_1_0
+    out("   override function create(): Node {\n");
+#else
+    out("   public function create(): Node {\n");
+#endif
+    out("       Group {\n");
+    out("           content: [\n");
+    idindex    = 0;
+
+    doBody(doc, doc->root);
+
+    if (!doTail())
+        return false;
+
+
+
+    //###### WRITE TO FILE
+    FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w");
+    if (!f)
+        {
+        err("Could open JavaFX file '%s' for writing", filename_utf8);
+        return false;
+        }
+
+    for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)
+        {
+        fputc(*iter, f);
+        }
+        
+    fclose(f);
+    
+    return true;
+}
+
+
+
+
+//########################################################################
+//# EXTENSION API
+//########################################################################
+
+
+
+#include "clear-n_.h"
+
+
+
+/**
+ * API call to save document
+*/
+void
+JavaFXOutput::save(Inkscape::Extension::Output */*mod*/,
+                        SPDocument *doc, gchar const *filename_utf8)
+{
+    /* N.B. The name `filename_utf8' represents the fact that we want it to be in utf8; whereas in
+     * fact we know that some callers of Extension::save pass something in the filesystem's
+     * encoding, while others do g_filename_to_utf8 before calling.
+     *
+     * In terms of safety, it's best to make all callers pass actual filenames, since in general
+     * one can't round-trip from filename to utf8 back to the same filename.  Whereas the argument
+     * for passing utf8 filenames is one of convenience: we often want to pass to g_warning or
+     * store as a string (rather than a byte stream) in XML, or the like. */
+    if (!saveDocument(doc, filename_utf8))
+        {
+        g_warning("Could not save JavaFX file '%s'", filename_utf8);
+        }
+}
+
+
+
+/**
+ * Make sure that we are in the database
+ */
+bool JavaFXOutput::check (Inkscape::Extension::Extension */*module*/)
+{
+    /* We don't need a Key
+    if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_JFX))
+        return FALSE;
+    */
+
+    return true;
+}
+
+
+
+/**
+ * This is the definition of JavaFX output.  This function just
+ * calls the extension system with the memory allocated XML that
+ * describes the data.
+*/
+void
+JavaFXOutput::init()
+{
+    Inkscape::Extension::build_from_mem(
+        "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+            "<name>" N_("JavaFX Output") "</name>\n"
+            "<id>org.inkscape.output.jfx</id>\n"
+            "<output>\n"
+                "<extension>.fx</extension>\n"
+                "<mimetype>text/x-javafx-script</mimetype>\n"
+                "<filetypename>" N_("JavaFX (*.fx)") "</filetypename>\n"
+                "<filetypetooltip>" N_("JavaFX Raytracer File") "</filetypetooltip>\n"
+            "</output>\n"
+        "</inkscape-extension>",
+        new JavaFXOutput());
+}
+
+
+
+
+
+}  // namespace Internal
+}  // namespace Extension
+}  // namespace Inkscape
+
+
+/*
+  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 :
index 691812ee250a372f258ccc4282d482f3975ab012..9c1c8778ba0489ab798a00e00fbcf126375bae45 100644 (file)
-/*\r
- * A simple utility for exporting an Inkscape svg image as a JavaFX\r
- * scene tree.\r
- *\r
- * Authors:\r
- *   Bob Jamison <ishmal@inkscape.org>\r
- *   Silveira Neto <silveiraneto@gmail.com>\r
- *   Jim Clarke <Jim.Clarke@sun.com>\r
- *\r
- * Copyright (C) 2008 Authors\r
- *\r
- * Released under GNU GPL, read the file 'COPYING' for more information\r
- */\r
-\r
-#ifndef EXTENSION_INTERNAL_JAVAFX_OUT_H\r
-#define EXTENSION_INTERNAL_JAVAFX_OUT_H\r
-\r
-#include <glib.h>\r
-#include "extension/implementation/implementation.h"\r
-#include <document.h>\r
-#include <sp-gradient.h>\r
-\r
-namespace Inkscape\r
-{\r
-namespace Extension\r
-{\r
-namespace Internal\r
-{\r
-\r
-\r
-\r
-/**\r
- * Output the current svg document in JavaFX format.\r
- * \r
- * For information, @see:  \r
- * https://openjfx.dev.java.net/\r
- */ \r
-class JavaFXOutput : public Inkscape::Extension::Implementation::Implementation\r
-{\r
-\r
-\r
-public:\r
-\r
-    /**\r
-     * Our internal String definition\r
-     */\r
-    typedef Glib::ustring String;\r
-\r
-\r
-    /**\r
-     * Check whether we can actually output using this module\r
-     */\r
-       virtual bool check (Inkscape::Extension::Extension * module);\r
-\r
-    /**\r
-     * API call to perform the output to a file\r
-     */\r
-    virtual void save(Inkscape::Extension::Output *mod,\r
-                      SPDocument *doc, gchar const *filename);\r
-\r
-    /**\r
-     * Inkscape runtime startup call.\r
-     */\r
-       static void init(void);\r
-       \r
-    /**\r
-     * Reset variables to initial state\r
-     */\r
-       void reset();\r
-       \r
-private:\r
-\r
-    //output class name\r
-    String name;\r
-\r
-    //For formatted output\r
-       String outbuf;   //main output buffer\r
-       String foutbuf;  //header function buffer\r
-\r
-\r
-       /**\r
-        * Format text to our output buffer\r
-        */\r
-       void out(const char *fmt, ...) G_GNUC_PRINTF(2,3);\r
-\r
-       /**\r
-        * Format text to our function output buffer\r
-        */\r
-       void fout(const char *fmt, ...) G_GNUC_PRINTF(2,3);\r
-\r
-       //Output the parts of the file\r
-\r
-    /**\r
-     * Output the file header\r
-     */\r
-       bool doHeader();\r
-\r
-    /**\r
-     *  Output gradient information to the buffer\r
-     */\r
-    bool doGradient(SPGradient *grad, const String &id);\r
-\r
-    /**\r
-     *  Output an element's style attribute\r
-     */\r
-    bool doStyle(SPStyle *style);\r
-\r
-    /**\r
-     * Output the SVG document's curve data as JavaFX geometry types\r
-     */\r
-    bool doCurve(SPItem *item, const String &id);\r
-    bool doTreeRecursive(SPDocument *doc, SPObject *obj);\r
-    bool doTree(SPDocument *doc);\r
-\r
-    bool doBody(SPDocument *doc, SPObject *obj);\r
-\r
-    /**\r
-     * Output the file footer\r
-     */\r
-       bool doTail();\r
-\r
-\r
-\r
-    /**\r
-     * Actual method to save document\r
-     */\r
-       bool saveDocument(SPDocument *doc, gchar const *filename);\r
-\r
-    //For statistics\r
-    int nrNodes;\r
-    int nrShapes;\r
-    \r
-    int idindex;\r
-\r
-    double minx;\r
-    double miny;\r
-    double maxx;\r
-    double maxy;\r
-    \r
-\r
-};\r
-\r
-\r
-\r
-\r
-}  // namespace Internal\r
-}  // namespace Extension\r
-}  // namespace Inkscape\r
-\r
-\r
-\r
-#endif /* EXTENSION_INTERNAL_POV_OUT_H */\r
-\r
+/*
+ * A simple utility for exporting an Inkscape svg image as a JavaFX
+ * scene tree.
+ *
+ * Authors:
+ *   Bob Jamison <ishmal@inkscape.org>
+ *   Silveira Neto <silveiraneto@gmail.com>
+ *   Jim Clarke <Jim.Clarke@sun.com>
+ *
+ * Copyright (C) 2008 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef EXTENSION_INTERNAL_JAVAFX_OUT_H
+#define EXTENSION_INTERNAL_JAVAFX_OUT_H
+
+#include <glib.h>
+#include "extension/implementation/implementation.h"
+#include <document.h>
+#include <sp-gradient.h>
+
+namespace Inkscape
+{
+namespace Extension
+{
+namespace Internal
+{
+
+
+
+/**
+ * Output the current svg document in JavaFX format.
+ * 
+ * For information, @see:  
+ * https://openjfx.dev.java.net/
+ */ 
+class JavaFXOutput : public Inkscape::Extension::Implementation::Implementation
+{
+
+
+public:
+
+    /**
+     * Our internal String definition
+     */
+    typedef Glib::ustring String;
+
+
+    /**
+     * Check whether we can actually output using this module
+     */
+       virtual bool check (Inkscape::Extension::Extension * module);
+
+    /**
+     * API call to perform the output to a file
+     */
+    virtual void save(Inkscape::Extension::Output *mod,
+                      SPDocument *doc, gchar const *filename);
+
+    /**
+     * Inkscape runtime startup call.
+     */
+       static void init(void);
+       
+    /**
+     * Reset variables to initial state
+     */
+       void reset();
+       
+private:
+
+    //output class name
+    String name;
+
+    //For formatted output
+       String outbuf;   //main output buffer
+       String foutbuf;  //header function buffer
+
+
+       /**
+        * Format text to our output buffer
+        */
+       void out(const char *fmt, ...) G_GNUC_PRINTF(2,3);
+
+       /**
+        * Format text to our function output buffer
+        */
+       void fout(const char *fmt, ...) G_GNUC_PRINTF(2,3);
+
+       //Output the parts of the file
+
+    /**
+     * Output the file header
+     */
+       bool doHeader();
+
+    /**
+     *  Output gradient information to the buffer
+     */
+    bool doGradient(SPGradient *grad, const String &id);
+
+    /**
+     *  Output an element's style attribute
+     */
+    bool doStyle(SPStyle *style);
+
+    /**
+     * Output the SVG document's curve data as JavaFX geometry types
+     */
+    bool doCurve(SPItem *item, const String &id);
+    bool doTreeRecursive(SPDocument *doc, SPObject *obj);
+    bool doTree(SPDocument *doc);
+
+    bool doBody(SPDocument *doc, SPObject *obj);
+
+    /**
+     * Output the file footer
+     */
+       bool doTail();
+
+
+
+    /**
+     * Actual method to save document
+     */
+       bool saveDocument(SPDocument *doc, gchar const *filename);
+
+    //For statistics
+    int nrNodes;
+    int nrShapes;
+    
+    int idindex;
+
+    double minx;
+    double miny;
+    double maxx;
+    double maxy;
+    
+
+};
+
+
+
+
+}  // namespace Internal
+}  // namespace Extension
+}  // namespace Inkscape
+
+
+
+#endif /* EXTENSION_INTERNAL_POV_OUT_H */
+
index daf208e789787e84d91e31fd2ca2dcf4f1bc0468..f3dc364f2cb44410e46b322a55114e6837886e33 100644 (file)
@@ -1,39 +1,39 @@
-#ifndef INKSCAPE_HELPER_GEOM_CURVES_H\r
-#define INKSCAPE_HELPER_GEOM_CURVES_H\r
-\r
-/**\r
- * Specific curve type functions for Inkscape, not provided by lib2geom.\r
- *\r
- * Author:\r
- *   Johan Engelen <goejendaagh@zonnet.nl>\r
- *\r
- * Copyright (C) 2008 Johan Engelen\r
- *\r
- * Released under GNU GPL\r
- */\r
-\r
-#include <2geom/hvlinesegment.h>\r
-\r
-inline bool is_straight_curve(Geom::Curve const & c) {\r
-    if( dynamic_cast<Geom::LineSegment const*>(&c) ||\r
-        dynamic_cast<Geom::HLineSegment const*>(&c) ||\r
-        dynamic_cast<Geom::VLineSegment const*>(&c) )\r
-    {\r
-        return true;\r
-    } else {\r
-        return false;\r
-    }\r
-}\r
-\r
-#endif  // INKSCAPE_HELPER_GEOM_CURVES_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
+#ifndef INKSCAPE_HELPER_GEOM_CURVES_H
+#define INKSCAPE_HELPER_GEOM_CURVES_H
+
+/**
+ * Specific curve type functions for Inkscape, not provided by lib2geom.
+ *
+ * Author:
+ *   Johan Engelen <goejendaagh@zonnet.nl>
+ *
+ * Copyright (C) 2008 Johan Engelen
+ *
+ * Released under GNU GPL
+ */
+
+#include <2geom/hvlinesegment.h>
+
+inline bool is_straight_curve(Geom::Curve const & c) {
+    if( dynamic_cast<Geom::LineSegment const*>(&c) ||
+        dynamic_cast<Geom::HLineSegment const*>(&c) ||
+        dynamic_cast<Geom::VLineSegment const*>(&c) )
+    {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+#endif  // INKSCAPE_HELPER_GEOM_CURVES_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 :
index d75941b122d55151658dca43b96fe29293d98449..4cf3e35e67123254141b69dcb8a4ccfc89c353ce 100644 (file)
-#define INKSCAPE_LPE_ROUGH_HATCHES_CPP\r
-/** \file\r
- * LPE Curve Stitching implementation, used as an example for a base starting class\r
- * when implementing new LivePathEffects.\r
- *\r
- */\r
-/*\r
- * Authors:\r
- *   JF Barraud.\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-rough-hatches.h"\r
-\r
-#include "sp-item.h"\r
-#include "sp-path.h"\r
-#include "svg/svg.h"\r
-#include "xml/repr.h"\r
-\r
-#include <2geom/path.h>\r
-#include <2geom/piecewise.h>\r
-#include <2geom/sbasis.h>\r
-#include <2geom/sbasis-math.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/matrix.h>\r
-\r
-#include "ui/widget/scalar.h"\r
-#include "libnr/nr-values.h"\r
-\r
-namespace Inkscape {\r
-namespace LivePathEffect {\r
-\r
-using namespace Geom;\r
-\r
-//------------------------------------------------\r
-// Some goodies to navigate through curve's levels.\r
-//------------------------------------------------\r
-struct LevelCrossing{\r
-    Point pt;\r
-    double t;\r
-    bool sign;\r
-    bool used;\r
-    std::pair<unsigned,unsigned> next_on_curve;\r
-    std::pair<unsigned,unsigned> prev_on_curve;\r
-};\r
-struct LevelCrossingOrder {\r
-    bool operator()(LevelCrossing a, LevelCrossing b) {\r
-        return ( a.pt[Y] < b.pt[Y] );// a.pt[X] == b.pt[X] since we are supposed to be on the same level...\r
-        //return ( a.pt[X] < b.pt[X] || ( a.pt[X] == b.pt[X]  && a.pt[Y] < b.pt[Y] ) );\r
-    }\r
-};\r
-struct LevelCrossingInfo{\r
-    double t;\r
-    unsigned level;\r
-    unsigned idx;\r
-};\r
-struct LevelCrossingInfoOrder {\r
-    bool operator()(LevelCrossingInfo a, LevelCrossingInfo b) {\r
-        return a.t < b.t;\r
-    }\r
-};\r
-\r
-typedef std::vector<LevelCrossing> LevelCrossings;\r
-\r
-std::vector<double>\r
-discontinuities(Piecewise<D2<SBasis> > const &f){\r
-    std::vector<double> result;\r
-    if (f.size()==0) return result;\r
-    result.push_back(f.cuts[0]);\r
-    Point prev_pt = f.segs[0].at1();\r
-    //double old_t  = f.cuts[0];\r
-    for(unsigned i=1; i<f.size(); i++){\r
-        if ( f.segs[i].at0()!=prev_pt){\r
-            result.push_back(f.cuts[i]);\r
-            //old_t = f.cuts[i];\r
-            //assert(f.segs[i-1].at1()==f.valueAt(old_t));\r
-        }\r
-        prev_pt = f.segs[i].at1();\r
-    }\r
-    result.push_back(f.cuts.back());\r
-    //assert(f.segs.back().at1()==f.valueAt(old_t));\r
-    return result;\r
-}\r
-\r
-class LevelsCrossings: public std::vector<LevelCrossings>{\r
-public:\r
-    LevelsCrossings():std::vector<LevelCrossings>(){};\r
-    LevelsCrossings(std::vector<std::vector<double> > const &times,\r
-                    Piecewise<D2<SBasis> > const &f,\r
-                    Piecewise<SBasis> const &dx){\r
-        \r
-        for (unsigned i=0; i<times.size(); i++){\r
-            LevelCrossings lcs;\r
-            for (unsigned j=0; j<times[i].size(); j++){\r
-                LevelCrossing lc;\r
-                lc.pt = f.valueAt(times[i][j]);\r
-                lc.t = times[i][j];\r
-                lc.sign = ( dx.valueAt(times[i][j])>0 );\r
-                lc.used = false;\r
-                lcs.push_back(lc);\r
-            }\r
-            std::sort(lcs.begin(), lcs.end(), LevelCrossingOrder());\r
-            push_back(lcs);\r
-        }\r
-        //Now create time ordering.\r
-        std::vector<LevelCrossingInfo>temp;\r
-        for (unsigned i=0; i<size(); i++){\r
-            for (unsigned j=0; j<(*this)[i].size(); j++){\r
-                LevelCrossingInfo elem;\r
-                elem.t = (*this)[i][j].t;\r
-                elem.level = i;\r
-                elem.idx = j;\r
-                temp.push_back(elem);\r
-            }\r
-        }\r
-        std::sort(temp.begin(),temp.end(),LevelCrossingInfoOrder());\r
-        std::vector<double> jumps = discontinuities(f);\r
-        unsigned jump_idx = 0;\r
-        unsigned first_in_comp = 0;\r
-        for (unsigned i=0; i<temp.size(); i++){\r
-            unsigned lvl = temp[i].level, idx = temp[i].idx;\r
-            if ( i == temp.size()-1 || temp[i+1].t > jumps[jump_idx+1]){\r
-                std::pair<unsigned,unsigned>next_data(temp[first_in_comp].level,temp[first_in_comp].idx);\r
-                (*this)[lvl][idx].next_on_curve = next_data;\r
-                first_in_comp = i+1;\r
-                jump_idx += 1;\r
-            }else{\r
-                std::pair<unsigned,unsigned> next_data(temp[i+1].level,temp[i+1].idx);\r
-                (*this)[lvl][idx].next_on_curve = next_data;\r
-            }\r
-        }\r
-\r
-        for (unsigned i=0; i<size(); i++){\r
-            for (unsigned j=0; j<(*this)[i].size(); j++){\r
-                std::pair<unsigned,unsigned> next = (*this)[i][j].next_on_curve;\r
-                (*this)[next.first][next.second].prev_on_curve = std::pair<unsigned,unsigned>(i,j);\r
-            }\r
-        }\r
-    }\r
-\r
-    void findFirstUnused(unsigned &level, unsigned &idx){\r
-        level = size();\r
-        idx = 0;\r
-        for (unsigned i=0; i<size(); i++){\r
-            for (unsigned j=0; j<(*this)[i].size(); j++){\r
-                if (!(*this)[i][j].used){\r
-                    level = i;\r
-                    idx = j;\r
-                    return;\r
-                }\r
-            }\r
-        }\r
-    }\r
-    //set indexes to point to the next point in the "snake walk"\r
-    //follow_level's meaning: \r
-    //  0=yes upward\r
-    //  1=no, last move was upward,\r
-    //  2=yes downward\r
-    //  3=no, last move was downward.\r
-    void step(unsigned &level, unsigned &idx, int &direction){\r
-        if ( direction % 2 == 0 ){\r
-            if (direction == 0) {\r
-                if ( idx >= (*this)[level].size()-1 || (*this)[level][idx+1].used ) {\r
-                    level = size();\r
-                    return;\r
-                }\r
-                idx += 1;\r
-            }else{\r
-                if ( idx <= 0  || (*this)[level][idx-1].used ) {\r
-                    level = size();\r
-                    return;\r
-                }\r
-                idx -= 1;\r
-            }\r
-            direction += 1;\r
-            return;\r
-        }\r
-        double t = (*this)[level][idx].t;\r
-        double sign = ((*this)[level][idx].sign ? 1 : -1);\r
-        //---double next_t = t;\r
-        //level += 1;\r
-        direction = (direction + 1)%4;\r
-        if (level == size()){\r
-            return;\r
-        }\r
-\r
-        std::pair<unsigned,unsigned> next;\r
-        if ( sign > 0 ){\r
-            next = (*this)[level][idx].next_on_curve;\r
-        }else{\r
-            next = (*this)[level][idx].prev_on_curve;\r
-        }\r
-\r
-        if ( level+1 != next.first || (*this)[next.first][next.second].used ) {\r
-            level = size();\r
-            return;\r
-        }\r
-        level = next.first;\r
-        idx = next.second;\r
-        return;\r
-    }\r
-};\r
-\r
-//-------------------------------------------------------\r
-// Bend a path...\r
-//-------------------------------------------------------\r
-\r
-Piecewise<D2<SBasis> > bend(Piecewise<D2<SBasis> > const &f, Piecewise<SBasis> bending){\r
-    D2<Piecewise<SBasis> > ff = make_cuts_independent(f);\r
-    ff[X] += compose(bending, ff[Y]);\r
-    return sectionize(ff);\r
-}\r
-\r
-//--------------------------------------------------------\r
-// The RoughHatches lpe.\r
-//--------------------------------------------------------\r
-LPERoughHatches::LPERoughHatches(LivePathEffectObject *lpeobject) :\r
-    Effect(lpeobject),\r
-    direction(_("Hatches width and dir"), _("Defines hatches frequency and direction"), "direction", &wr, this, Geom::Point(50,0)),\r
-    dist_rdm(_("Frequency randomness"), _("Variation of dist between hatches, in %."), "dist_rdm", &wr, this, 75),\r
-    growth(_("Growth"), _("Growth of distance between hatches."), "growth", &wr, this, 0.),\r
-//FIXME: top/bottom names are inverted in the UI/svg and in the code!!\r
-    scale_tf(_("Half turns smoothness: 1st side, in"), _("Set smoothness/sharpness of path when reaching a 'bottom' halfturn. 0=sharp, 1=default"), "scale_bf", &wr, this, 1.),\r
-    scale_tb(_("1st side, out"), _("Set smoothness/sharpness of path when leaving a 'bottom' halfturn. 0=sharp, 1=default"), "scale_bb", &wr, this, 1.),\r
-    scale_bf(_("2nd side, in "), _("Set smoothness/sharpness of path when reaching a 'top' halfturn. 0=sharp, 1=default"), "scale_tf", &wr, this, 1.),\r
-    scale_bb(_("2nd side, out"), _("Set smoothness/sharpness of path when leaving a 'top' halfturn. 0=sharp, 1=default"), "scale_tb", &wr, this, 1.),\r
-    top_smth_variation(_("variance: 1st side"), _("Randomness of 'bottom' halfturns smoothness"), "top_smth_variation", &wr, this, 0),\r
-    bot_smth_variation(_("2nd side"), _("Randomness of 'top' halfturns smoothness"), "bottom_smth_variation", &wr, this, 0),\r
-//\r
-    top_edge_variation(_("Magnitude jitter: 1st side"), _("Randomly moves 'bottom' halfsturns to produce magnitude variations."), "bottom_edge_variation", &wr, this, 0),\r
-    bot_edge_variation(_("2nd side"), _("Randomly moves 'top' halfsturns to produce magnitude variations."), "top_edge_variation", &wr, this, 0),\r
-    top_tgt_variation(_("Parallelism jitter: 1st side"), _("Add direction randomness by moving 'bottom' halfsturns tangentially to the boundary."), "bottom_tgt_variation", &wr, this, 0),\r
-    bot_tgt_variation(_("2nd side"), _("Add direction randomness by randomly moving 'top' halfsturns tangentially to the boundary."), "top_tgt_variation", &wr, this, 0),\r
-//\r
-    do_bend(_("Bend hatches"), _("Add a global bend to the hatches (slower)"), "do_bend", &wr, this, true),\r
-    //bender(_("Global bending"), _("Relative position to ref point defines global bending direction and amount"), "bender", &wr, this, NULL, Geom::Point(-5,0)),\r
-    bender(_("Global bending"), _("Relative position to ref point defines global bending direction and amount"), "bender", &wr, this, Geom::Point(-5,0)),\r
-//\r
-    fat_output(_("Generate thick/thin path"), _("Simulate a stroke of varrying width"), "fat_output", &wr, this, true),\r
-    stroke_width_top(_("Thikness: at 1st side"), _("Width at 'bottom' half turns"), "stroke_width_top", &wr, this, 1.),\r
-    stroke_width_bot(_("at 2nd side"), _("Width at 'top' halfturns"), "stroke_width_bottom", &wr, this, 1.),\r
-    front_thickness(_("from 2nd to 1st side"), _("Width of paths from 'top' to 'bottom' halfturns"), "front_thickness", &wr, this, 1.),\r
-    back_thickness(_("from 1st to 2nd side"), _("Width of paths from 'top' to 'bottom' halfturns"), "back_thickness", &wr, this, .25)\r
-{\r
-    registerParameter( dynamic_cast<Parameter *>(&direction) );\r
-    registerParameter( dynamic_cast<Parameter *>(&dist_rdm) );\r
-    registerParameter( dynamic_cast<Parameter *>(&growth) );\r
-    registerParameter( dynamic_cast<Parameter *>(&do_bend) );\r
-    registerParameter( dynamic_cast<Parameter *>(&bender) );\r
-    registerParameter( dynamic_cast<Parameter *>(&top_edge_variation) );\r
-    registerParameter( dynamic_cast<Parameter *>(&bot_edge_variation) );\r
-    registerParameter( dynamic_cast<Parameter *>(&top_tgt_variation) );\r
-    registerParameter( dynamic_cast<Parameter *>(&bot_tgt_variation) );\r
-    registerParameter( dynamic_cast<Parameter *>(&scale_tf) );\r
-    registerParameter( dynamic_cast<Parameter *>(&scale_tb) );\r
-    registerParameter( dynamic_cast<Parameter *>(&scale_bf) );\r
-    registerParameter( dynamic_cast<Parameter *>(&scale_bb) );\r
-    registerParameter( dynamic_cast<Parameter *>(&top_smth_variation) );\r
-    registerParameter( dynamic_cast<Parameter *>(&bot_smth_variation) );\r
-    registerParameter( dynamic_cast<Parameter *>(&fat_output) );\r
-    registerParameter( dynamic_cast<Parameter *>(&stroke_width_top) );\r
-    registerParameter( dynamic_cast<Parameter *>(&stroke_width_bot) );\r
-    registerParameter( dynamic_cast<Parameter *>(&front_thickness) );\r
-    registerParameter( dynamic_cast<Parameter *>(&back_thickness) );\r
-\r
-    //hatch_dist.param_set_range(0.1, NR_HUGE);\r
-    growth.param_set_range(0, NR_HUGE);\r
-    dist_rdm.param_set_range(0, 99.);\r
-    stroke_width_top.param_set_range(0,  NR_HUGE);\r
-    stroke_width_bot.param_set_range(0,  NR_HUGE);\r
-    front_thickness.param_set_range(0, NR_HUGE);\r
-    back_thickness.param_set_range(0, NR_HUGE);\r
-\r
-    concatenate_before_pwd2 = false;\r
-    show_orig_path = true;\r
-}\r
-\r
-LPERoughHatches::~LPERoughHatches()\r
-{\r
-\r
-}\r
-\r
-Geom::Piecewise<Geom::D2<Geom::SBasis> > \r
-LPERoughHatches::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in){\r
-\r
-    //std::cout<<"doEffect_pwd2:\n";\r
-\r
-    Piecewise<D2<SBasis> > result;\r
-    \r
-    Piecewise<D2<SBasis> > transformed_pwd2_in = pwd2_in;\r
-    Point start = pwd2_in.segs.front().at0();\r
-    Point end = pwd2_in.segs.back().at1();\r
-    if (end != start ){\r
-        transformed_pwd2_in.push_cut( transformed_pwd2_in.cuts.back() + 1 );\r
-        D2<SBasis> stitch( SBasis( 1, Linear(end[X],start[X]) ), SBasis( 1, Linear(end[Y],start[Y]) ) );\r
-        transformed_pwd2_in.push_seg( stitch );\r
-    }\r
-    Point transformed_org = direction.getOrigin();\r
-    Piecewise<SBasis> tilter;//used to bend the hatches\r
-    Matrix bend_mat;//used to bend the hatches\r
-\r
-    if (do_bend.get_value()){\r
-        Point bend_dir = -rot90(unit_vector(bender.getVector()));\r
-        double bend_amount = L2(bender.getVector());\r
-        bend_mat = Matrix(-bend_dir[Y], bend_dir[X], bend_dir[X], bend_dir[Y],0,0);\r
-        transformed_pwd2_in = transformed_pwd2_in * bend_mat;\r
-        tilter = Piecewise<SBasis>(shift(Linear(-bend_amount),1));\r
-        OptRect bbox = bounds_exact( transformed_pwd2_in );\r
-        if (not(bbox)) return pwd2_in;\r
-        tilter.setDomain((*bbox)[Y]);\r
-        transformed_pwd2_in = bend(transformed_pwd2_in, tilter);\r
-        transformed_pwd2_in = transformed_pwd2_in * bend_mat.inverse();\r
-    }\r
-    hatch_dist = Geom::L2(direction.getVector())/5;\r
-    Point hatches_dir = rot90(unit_vector(direction.getVector()));\r
-    Matrix mat(-hatches_dir[Y], hatches_dir[X], hatches_dir[X], hatches_dir[Y],0,0);\r
-    transformed_pwd2_in = transformed_pwd2_in * mat;\r
-    transformed_org *= mat;\r
-        \r
-    std::vector<std::vector<Point> > snakePoints;\r
-    snakePoints = linearSnake(transformed_pwd2_in, transformed_org);\r
-    if ( snakePoints.size() > 0 ){\r
-        Piecewise<D2<SBasis> >smthSnake = smoothSnake(snakePoints); \r
-        smthSnake = smthSnake*mat.inverse();\r
-        if (do_bend.get_value()){\r
-            smthSnake = smthSnake*bend_mat;\r
-            smthSnake = bend(smthSnake, -tilter);\r
-            smthSnake = smthSnake*bend_mat.inverse();\r
-        }\r
-        return (smthSnake);\r
-    }\r
-    return pwd2_in;\r
-}\r
-\r
-//------------------------------------------------\r
-// Generate the levels with random, growth...\r
-//------------------------------------------------\r
-std::vector<double>\r
-LPERoughHatches::generateLevels(Interval const &domain, double x_org){\r
-    std::vector<double> result;\r
-    int n = int((domain.min()-x_org)/hatch_dist);\r
-    double x = x_org +  n * hatch_dist;\r
-    //double x = domain.min() + double(hatch_dist)/2.;\r
-    double step = double(hatch_dist);\r
-    double scale = 1+(hatch_dist*growth/domain.extent());\r
-    while (x < domain.max()){\r
-        result.push_back(x);\r
-        double rdm = 1;\r
-        if (dist_rdm.get_value() != 0) \r
-            rdm = 1.+ double((2*dist_rdm - dist_rdm.get_value()))/100.;\r
-        x+= step*rdm;\r
-        step*=scale;//(1.+double(growth));\r
-    }\r
-    return result;\r
-}\r
-\r
-\r
-//-------------------------------------------------------\r
-// Walk through the intersections to create linear hatches\r
-//-------------------------------------------------------\r
-std::vector<std::vector<Point> > \r
-LPERoughHatches::linearSnake(Piecewise<D2<SBasis> > const &f, Point const &org){\r
-\r
-    //std::cout<<"linearSnake:\n";\r
-    std::vector<std::vector<Point> > result;\r
-    Piecewise<SBasis> x = make_cuts_independent(f)[X];\r
-    //Remark: derivative is computed twice in the 2 lines below!!\r
-    Piecewise<SBasis> dx = derivative(x);\r
-    OptInterval range = bounds_exact(x);\r
-\r
-    if (not range) return result;\r
-    std::vector<double> levels = generateLevels(*range, org[X]);\r
-    std::vector<std::vector<double> > times;\r
-    times = multi_roots(x,levels);\r
-//TODO: fix multi_roots!!!*****************************************\r
-//remove doubles :-(\r
-    std::vector<std::vector<double> > cleaned_times(levels.size(),std::vector<double>());\r
-    for (unsigned i=0; i<times.size(); i++){\r
-        if ( times[i].size()>0 ){\r
-            double last_t = times[i][0]-1;//ugly hack!!\r
-            for (unsigned j=0; j<times[i].size(); j++){\r
-                if (times[i][j]-last_t >0.000001){\r
-                    last_t = times[i][j];\r
-                    cleaned_times[i].push_back(last_t);\r
-                }\r
-            }\r
-        }\r
-    }\r
-    times = cleaned_times;\r
-//*******************************************************************\r
-\r
-    LevelsCrossings lscs(times,f,dx);\r
-\r
-    unsigned i,j;\r
-    lscs.findFirstUnused(i,j);\r
-    \r
-    std::vector<Point> result_component;\r
-    int n = int((range->min()-org[X])/hatch_dist);\r
-    \r
-    while ( i < lscs.size() ){ \r
-        int dir = 0;\r
-        //switch orientation of first segment according to starting point.\r
-        if (i % 2 == n%2 && j < lscs[i].size()-1 && !lscs[i][j].used){\r
-            j += 1;\r
-            dir = 2;\r
-        }\r
-\r
-        while ( i < lscs.size() ){\r
-            result_component.push_back(lscs[i][j].pt);\r
-            lscs[i][j].used = true;\r
-            lscs.step(i,j, dir);\r
-        }\r
-        result.push_back(result_component);\r
-        result_component = std::vector<Point>();\r
-        lscs.findFirstUnused(i,j);\r
-    }\r
-    return result;\r
-}\r
-\r
-//-------------------------------------------------------\r
-// Smooth the linear hatches according to params...\r
-//-------------------------------------------------------\r
-Piecewise<D2<SBasis> > \r
-LPERoughHatches::smoothSnake(std::vector<std::vector<Point> > const &linearSnake){\r
-\r
-    Piecewise<D2<SBasis> > result;\r
-    for (unsigned comp=0; comp<linearSnake.size(); comp++){\r
-        if (linearSnake[comp].size()>=2){\r
-            Point last_pt = linearSnake[comp][0];\r
-            Point last_top = linearSnake[comp][0];\r
-            Point last_bot = linearSnake[comp][0];\r
-            Point last_hdle = linearSnake[comp][0];\r
-            Point last_top_hdle = linearSnake[comp][0];\r
-            Point last_bot_hdle = linearSnake[comp][0];\r
-            Geom::Path res_comp(last_pt);\r
-            Geom::Path res_comp_top(last_pt);\r
-            Geom::Path res_comp_bot(last_pt);\r
-            unsigned i=1;\r
-            //bool is_top = true;//Inversion here; due to downward y? \r
-            bool is_top = ( linearSnake[comp][0][Y] < linearSnake[comp][1][Y] );\r
-\r
-            while( i+1<linearSnake[comp].size() ){\r
-                Point pt0 = linearSnake[comp][i];\r
-                Point pt1 = linearSnake[comp][i+1];\r
-                Point new_pt = (pt0+pt1)/2;\r
-                double scale_in = (is_top ? scale_tf : scale_bf );\r
-                double scale_out = (is_top ? scale_tb : scale_bb );\r
-                if (is_top){\r
-                    if (top_edge_variation.get_value() != 0) \r
-                        new_pt[Y] += double(top_edge_variation)-top_edge_variation.get_value()/2.;\r
-                    if (top_tgt_variation.get_value() != 0) \r
-                        new_pt[X] += double(top_tgt_variation)-top_tgt_variation.get_value()/2.;\r
-                    if (top_smth_variation.get_value() != 0) {\r
-                        scale_in*=(100.-double(top_smth_variation))/100.;\r
-                        scale_out*=(100.-double(top_smth_variation))/100.;\r
-                    }\r
-                }else{\r
-                    if (bot_edge_variation.get_value() != 0) \r
-                        new_pt[Y] += double(bot_edge_variation)-bot_edge_variation.get_value()/2.;\r
-                    if (bot_tgt_variation.get_value() != 0) \r
-                        new_pt[X] += double(bot_tgt_variation)-bot_tgt_variation.get_value()/2.;\r
-                    if (bot_smth_variation.get_value() != 0) {\r
-                        scale_in*=(100.-double(bot_smth_variation))/100.;\r
-                        scale_out*=(100.-double(bot_smth_variation))/100.;\r
-                    }\r
-                }\r
-                Point new_hdle_in  = new_pt + (pt0-pt1) * (scale_in /2.);\r
-                Point new_hdle_out = new_pt - (pt0-pt1) * (scale_out/2.);\r
-                \r
-                if ( fat_output.get_value() ){\r
-                    //double scaled_width = double((is_top ? stroke_width_top : stroke_width_bot))/(pt1[X]-pt0[X]);\r
-                    double scaled_width = 1./(pt1[X]-pt0[X]);\r
-                    Point hdle_offset = (pt1-pt0)*scaled_width;\r
-                    Point inside = new_pt;\r
-                    Point inside_hdle_in;\r
-                    Point inside_hdle_out;\r
-                    inside[Y]+= double((is_top ? -stroke_width_top : stroke_width_bot));\r
-                    inside_hdle_in  = inside + (new_hdle_in -new_pt);// + hdle_offset * double((is_top ? front_thickness : back_thickness));\r
-                    inside_hdle_out = inside + (new_hdle_out-new_pt);// - hdle_offset * double((is_top ? back_thickness : front_thickness));\r
-\r
-                    inside_hdle_in  +=  (pt1-pt0)/2*( double((is_top ? front_thickness : back_thickness)) / (pt1[X]-pt0[X]) );\r
-                    inside_hdle_out -=  (pt1-pt0)/2*( double((is_top ? back_thickness : front_thickness)) / (pt1[X]-pt0[X]) );\r
-\r
-                    new_hdle_in  -=  (pt1-pt0)/2*( double((is_top ? front_thickness : back_thickness)) / (pt1[X]-pt0[X]) );\r
-                    new_hdle_out +=  (pt1-pt0)/2*( double((is_top ? back_thickness : front_thickness)) / (pt1[X]-pt0[X]) );\r
-                    //TODO: find a good way to handle limit cases (small smthness, large stroke).\r
-                    //if (inside_hdle_in[X]  > inside[X]) inside_hdle_in = inside;\r
-                    //if (inside_hdle_out[X] < inside[X]) inside_hdle_out = inside;\r
-                    \r
-                    if (is_top){\r
-                        res_comp_top.appendNew<CubicBezier>(last_top_hdle,new_hdle_in,new_pt);\r
-                        res_comp_bot.appendNew<CubicBezier>(last_bot_hdle,inside_hdle_in,inside);\r
-                        last_top_hdle = new_hdle_out;\r
-                        last_bot_hdle = inside_hdle_out;\r
-                    }else{\r
-                        res_comp_top.appendNew<CubicBezier>(last_top_hdle,inside_hdle_in,inside);\r
-                        res_comp_bot.appendNew<CubicBezier>(last_bot_hdle,new_hdle_in,new_pt);\r
-                        last_top_hdle = inside_hdle_out;\r
-                        last_bot_hdle = new_hdle_out;\r
-                    }\r
-                }else{\r
-                    res_comp.appendNew<CubicBezier>(last_hdle,new_hdle_in,new_pt);\r
-                }\r
-            \r
-                last_hdle = new_hdle_out;\r
-                i+=2;\r
-                is_top = !is_top;\r
-            }\r
-            if ( i<linearSnake[comp].size() ){\r
-                if ( fat_output.get_value() ){\r
-                    res_comp_top.appendNew<CubicBezier>(last_top_hdle,linearSnake[comp][i],linearSnake[comp][i]);\r
-                    res_comp_bot.appendNew<CubicBezier>(last_bot_hdle,linearSnake[comp][i],linearSnake[comp][i]);\r
-                }else{\r
-                    res_comp.appendNew<CubicBezier>(last_hdle,linearSnake[comp][i],linearSnake[comp][i]);\r
-                }\r
-            }\r
-            if ( fat_output.get_value() ){\r
-                res_comp = res_comp_bot;\r
-                res_comp.append(res_comp_top.reverse(),Geom::Path::STITCH_DISCONTINUOUS);\r
-            }    \r
-            result.concat(res_comp.toPwSb());\r
-        }\r
-    }\r
-    return result;\r
-}\r
-\r
-void\r
-LPERoughHatches::doBeforeEffect (SPLPEItem */*lpeitem*/)\r
-{\r
-    using namespace Geom;\r
-    top_edge_variation.resetRandomizer();\r
-    bot_edge_variation.resetRandomizer();\r
-    top_tgt_variation.resetRandomizer();\r
-    bot_tgt_variation.resetRandomizer();\r
-    top_smth_variation.resetRandomizer();\r
-    bot_smth_variation.resetRandomizer();\r
-    dist_rdm.resetRandomizer();\r
-\r
-    //original_bbox(lpeitem);\r
-}\r
-\r
-\r
-void\r
-LPERoughHatches::resetDefaults(SPItem * item)\r
-{\r
-    Effect::resetDefaults(item);\r
-\r
-    Geom::OptRect bbox = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);\r
-    Geom::Point origin(0.,0.);\r
-    Geom::Point vector(50.,0.);\r
-    if (bbox) {\r
-        origin = bbox->midpoint();\r
-        vector = Geom::Point((*bbox)[X].extent()/4, 0.);\r
-        top_edge_variation.param_set_value( (*bbox)[Y].extent()/10, 0 );\r
-        bot_edge_variation.param_set_value( (*bbox)[Y].extent()/10, 0 );\r
-        top_edge_variation.write_to_SVG();\r
-        bot_edge_variation.write_to_SVG();\r
-    }\r
-    //direction.set_and_write_new_values(origin, vector);\r
-    //bender.param_set_and_write_new_value( origin + Geom::Point(5,0) );\r
-    direction.set_and_write_new_values(origin + Geom::Point(0,-5), vector);\r
-    bender.set_and_write_new_values( origin, Geom::Point(5,0) );\r
-    hatch_dist = Geom::L2(vector)/2;\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
+#define INKSCAPE_LPE_ROUGH_HATCHES_CPP
+/** \file
+ * LPE Curve Stitching implementation, used as an example for a base starting class
+ * when implementing new LivePathEffects.
+ *
+ */
+/*
+ * Authors:
+ *   JF Barraud.
+*
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/lpe-rough-hatches.h"
+
+#include "sp-item.h"
+#include "sp-path.h"
+#include "svg/svg.h"
+#include "xml/repr.h"
+
+#include <2geom/path.h>
+#include <2geom/piecewise.h>
+#include <2geom/sbasis.h>
+#include <2geom/sbasis-math.h>
+#include <2geom/sbasis-geometric.h>
+#include <2geom/bezier-to-sbasis.h>
+#include <2geom/sbasis-to-bezier.h>
+#include <2geom/d2.h>
+#include <2geom/matrix.h>
+
+#include "ui/widget/scalar.h"
+#include "libnr/nr-values.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+using namespace Geom;
+
+//------------------------------------------------
+// Some goodies to navigate through curve's levels.
+//------------------------------------------------
+struct LevelCrossing{
+    Point pt;
+    double t;
+    bool sign;
+    bool used;
+    std::pair<unsigned,unsigned> next_on_curve;
+    std::pair<unsigned,unsigned> prev_on_curve;
+};
+struct LevelCrossingOrder {
+    bool operator()(LevelCrossing a, LevelCrossing b) {
+        return ( a.pt[Y] < b.pt[Y] );// a.pt[X] == b.pt[X] since we are supposed to be on the same level...
+        //return ( a.pt[X] < b.pt[X] || ( a.pt[X] == b.pt[X]  && a.pt[Y] < b.pt[Y] ) );
+    }
+};
+struct LevelCrossingInfo{
+    double t;
+    unsigned level;
+    unsigned idx;
+};
+struct LevelCrossingInfoOrder {
+    bool operator()(LevelCrossingInfo a, LevelCrossingInfo b) {
+        return a.t < b.t;
+    }
+};
+
+typedef std::vector<LevelCrossing> LevelCrossings;
+
+std::vector<double>
+discontinuities(Piecewise<D2<SBasis> > const &f){
+    std::vector<double> result;
+    if (f.size()==0) return result;
+    result.push_back(f.cuts[0]);
+    Point prev_pt = f.segs[0].at1();
+    //double old_t  = f.cuts[0];
+    for(unsigned i=1; i<f.size(); i++){
+        if ( f.segs[i].at0()!=prev_pt){
+            result.push_back(f.cuts[i]);
+            //old_t = f.cuts[i];
+            //assert(f.segs[i-1].at1()==f.valueAt(old_t));
+        }
+        prev_pt = f.segs[i].at1();
+    }
+    result.push_back(f.cuts.back());
+    //assert(f.segs.back().at1()==f.valueAt(old_t));
+    return result;
+}
+
+class LevelsCrossings: public std::vector<LevelCrossings>{
+public:
+    LevelsCrossings():std::vector<LevelCrossings>(){};
+    LevelsCrossings(std::vector<std::vector<double> > const &times,
+                    Piecewise<D2<SBasis> > const &f,
+                    Piecewise<SBasis> const &dx){
+        
+        for (unsigned i=0; i<times.size(); i++){
+            LevelCrossings lcs;
+            for (unsigned j=0; j<times[i].size(); j++){
+                LevelCrossing lc;
+                lc.pt = f.valueAt(times[i][j]);
+                lc.t = times[i][j];
+                lc.sign = ( dx.valueAt(times[i][j])>0 );
+                lc.used = false;
+                lcs.push_back(lc);
+            }
+            std::sort(lcs.begin(), lcs.end(), LevelCrossingOrder());
+            push_back(lcs);
+        }
+        //Now create time ordering.
+        std::vector<LevelCrossingInfo>temp;
+        for (unsigned i=0; i<size(); i++){
+            for (unsigned j=0; j<(*this)[i].size(); j++){
+                LevelCrossingInfo elem;
+                elem.t = (*this)[i][j].t;
+                elem.level = i;
+                elem.idx = j;
+                temp.push_back(elem);
+            }
+        }
+        std::sort(temp.begin(),temp.end(),LevelCrossingInfoOrder());
+        std::vector<double> jumps = discontinuities(f);
+        unsigned jump_idx = 0;
+        unsigned first_in_comp = 0;
+        for (unsigned i=0; i<temp.size(); i++){
+            unsigned lvl = temp[i].level, idx = temp[i].idx;
+            if ( i == temp.size()-1 || temp[i+1].t > jumps[jump_idx+1]){
+                std::pair<unsigned,unsigned>next_data(temp[first_in_comp].level,temp[first_in_comp].idx);
+                (*this)[lvl][idx].next_on_curve = next_data;
+                first_in_comp = i+1;
+                jump_idx += 1;
+            }else{
+                std::pair<unsigned,unsigned> next_data(temp[i+1].level,temp[i+1].idx);
+                (*this)[lvl][idx].next_on_curve = next_data;
+            }
+        }
+
+        for (unsigned i=0; i<size(); i++){
+            for (unsigned j=0; j<(*this)[i].size(); j++){
+                std::pair<unsigned,unsigned> next = (*this)[i][j].next_on_curve;
+                (*this)[next.first][next.second].prev_on_curve = std::pair<unsigned,unsigned>(i,j);
+            }
+        }
+    }
+
+    void findFirstUnused(unsigned &level, unsigned &idx){
+        level = size();
+        idx = 0;
+        for (unsigned i=0; i<size(); i++){
+            for (unsigned j=0; j<(*this)[i].size(); j++){
+                if (!(*this)[i][j].used){
+                    level = i;
+                    idx = j;
+                    return;
+                }
+            }
+        }
+    }
+    //set indexes to point to the next point in the "snake walk"
+    //follow_level's meaning: 
+    //  0=yes upward
+    //  1=no, last move was upward,
+    //  2=yes downward
+    //  3=no, last move was downward.
+    void step(unsigned &level, unsigned &idx, int &direction){
+        if ( direction % 2 == 0 ){
+            if (direction == 0) {
+                if ( idx >= (*this)[level].size()-1 || (*this)[level][idx+1].used ) {
+                    level = size();
+                    return;
+                }
+                idx += 1;
+            }else{
+                if ( idx <= 0  || (*this)[level][idx-1].used ) {
+                    level = size();
+                    return;
+                }
+                idx -= 1;
+            }
+            direction += 1;
+            return;
+        }
+        double t = (*this)[level][idx].t;
+        double sign = ((*this)[level][idx].sign ? 1 : -1);
+        //---double next_t = t;
+        //level += 1;
+        direction = (direction + 1)%4;
+        if (level == size()){
+            return;
+        }
+
+        std::pair<unsigned,unsigned> next;
+        if ( sign > 0 ){
+            next = (*this)[level][idx].next_on_curve;
+        }else{
+            next = (*this)[level][idx].prev_on_curve;
+        }
+
+        if ( level+1 != next.first || (*this)[next.first][next.second].used ) {
+            level = size();
+            return;
+        }
+        level = next.first;
+        idx = next.second;
+        return;
+    }
+};
+
+//-------------------------------------------------------
+// Bend a path...
+//-------------------------------------------------------
+
+Piecewise<D2<SBasis> > bend(Piecewise<D2<SBasis> > const &f, Piecewise<SBasis> bending){
+    D2<Piecewise<SBasis> > ff = make_cuts_independent(f);
+    ff[X] += compose(bending, ff[Y]);
+    return sectionize(ff);
+}
+
+//--------------------------------------------------------
+// The RoughHatches lpe.
+//--------------------------------------------------------
+LPERoughHatches::LPERoughHatches(LivePathEffectObject *lpeobject) :
+    Effect(lpeobject),
+    direction(_("Hatches width and dir"), _("Defines hatches frequency and direction"), "direction", &wr, this, Geom::Point(50,0)),
+    dist_rdm(_("Frequency randomness"), _("Variation of dist between hatches, in %."), "dist_rdm", &wr, this, 75),
+    growth(_("Growth"), _("Growth of distance between hatches."), "growth", &wr, this, 0.),
+//FIXME: top/bottom names are inverted in the UI/svg and in the code!!
+    scale_tf(_("Half turns smoothness: 1st side, in"), _("Set smoothness/sharpness of path when reaching a 'bottom' halfturn. 0=sharp, 1=default"), "scale_bf", &wr, this, 1.),
+    scale_tb(_("1st side, out"), _("Set smoothness/sharpness of path when leaving a 'bottom' halfturn. 0=sharp, 1=default"), "scale_bb", &wr, this, 1.),
+    scale_bf(_("2nd side, in "), _("Set smoothness/sharpness of path when reaching a 'top' halfturn. 0=sharp, 1=default"), "scale_tf", &wr, this, 1.),
+    scale_bb(_("2nd side, out"), _("Set smoothness/sharpness of path when leaving a 'top' halfturn. 0=sharp, 1=default"), "scale_tb", &wr, this, 1.),
+    top_smth_variation(_("variance: 1st side"), _("Randomness of 'bottom' halfturns smoothness"), "top_smth_variation", &wr, this, 0),
+    bot_smth_variation(_("2nd side"), _("Randomness of 'top' halfturns smoothness"), "bottom_smth_variation", &wr, this, 0),
+//
+    top_edge_variation(_("Magnitude jitter: 1st side"), _("Randomly moves 'bottom' halfsturns to produce magnitude variations."), "bottom_edge_variation", &wr, this, 0),
+    bot_edge_variation(_("2nd side"), _("Randomly moves 'top' halfsturns to produce magnitude variations."), "top_edge_variation", &wr, this, 0),
+    top_tgt_variation(_("Parallelism jitter: 1st side"), _("Add direction randomness by moving 'bottom' halfsturns tangentially to the boundary."), "bottom_tgt_variation", &wr, this, 0),
+    bot_tgt_variation(_("2nd side"), _("Add direction randomness by randomly moving 'top' halfsturns tangentially to the boundary."), "top_tgt_variation", &wr, this, 0),
+//
+    do_bend(_("Bend hatches"), _("Add a global bend to the hatches (slower)"), "do_bend", &wr, this, true),
+    //bender(_("Global bending"), _("Relative position to ref point defines global bending direction and amount"), "bender", &wr, this, NULL, Geom::Point(-5,0)),
+    bender(_("Global bending"), _("Relative position to ref point defines global bending direction and amount"), "bender", &wr, this, Geom::Point(-5,0)),
+//
+    fat_output(_("Generate thick/thin path"), _("Simulate a stroke of varrying width"), "fat_output", &wr, this, true),
+    stroke_width_top(_("Thikness: at 1st side"), _("Width at 'bottom' half turns"), "stroke_width_top", &wr, this, 1.),
+    stroke_width_bot(_("at 2nd side"), _("Width at 'top' halfturns"), "stroke_width_bottom", &wr, this, 1.),
+    front_thickness(_("from 2nd to 1st side"), _("Width of paths from 'top' to 'bottom' halfturns"), "front_thickness", &wr, this, 1.),
+    back_thickness(_("from 1st to 2nd side"), _("Width of paths from 'top' to 'bottom' halfturns"), "back_thickness", &wr, this, .25)
+{
+    registerParameter( dynamic_cast<Parameter *>(&direction) );
+    registerParameter( dynamic_cast<Parameter *>(&dist_rdm) );
+    registerParameter( dynamic_cast<Parameter *>(&growth) );
+    registerParameter( dynamic_cast<Parameter *>(&do_bend) );
+    registerParameter( dynamic_cast<Parameter *>(&bender) );
+    registerParameter( dynamic_cast<Parameter *>(&top_edge_variation) );
+    registerParameter( dynamic_cast<Parameter *>(&bot_edge_variation) );
+    registerParameter( dynamic_cast<Parameter *>(&top_tgt_variation) );
+    registerParameter( dynamic_cast<Parameter *>(&bot_tgt_variation) );
+    registerParameter( dynamic_cast<Parameter *>(&scale_tf) );
+    registerParameter( dynamic_cast<Parameter *>(&scale_tb) );
+    registerParameter( dynamic_cast<Parameter *>(&scale_bf) );
+    registerParameter( dynamic_cast<Parameter *>(&scale_bb) );
+    registerParameter( dynamic_cast<Parameter *>(&top_smth_variation) );
+    registerParameter( dynamic_cast<Parameter *>(&bot_smth_variation) );
+    registerParameter( dynamic_cast<Parameter *>(&fat_output) );
+    registerParameter( dynamic_cast<Parameter *>(&stroke_width_top) );
+    registerParameter( dynamic_cast<Parameter *>(&stroke_width_bot) );
+    registerParameter( dynamic_cast<Parameter *>(&front_thickness) );
+    registerParameter( dynamic_cast<Parameter *>(&back_thickness) );
+
+    //hatch_dist.param_set_range(0.1, NR_HUGE);
+    growth.param_set_range(0, NR_HUGE);
+    dist_rdm.param_set_range(0, 99.);
+    stroke_width_top.param_set_range(0,  NR_HUGE);
+    stroke_width_bot.param_set_range(0,  NR_HUGE);
+    front_thickness.param_set_range(0, NR_HUGE);
+    back_thickness.param_set_range(0, NR_HUGE);
+
+    concatenate_before_pwd2 = false;
+    show_orig_path = true;
+}
+
+LPERoughHatches::~LPERoughHatches()
+{
+
+}
+
+Geom::Piecewise<Geom::D2<Geom::SBasis> > 
+LPERoughHatches::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in){
+
+    //std::cout<<"doEffect_pwd2:\n";
+
+    Piecewise<D2<SBasis> > result;
+    
+    Piecewise<D2<SBasis> > transformed_pwd2_in = pwd2_in;
+    Point start = pwd2_in.segs.front().at0();
+    Point end = pwd2_in.segs.back().at1();
+    if (end != start ){
+        transformed_pwd2_in.push_cut( transformed_pwd2_in.cuts.back() + 1 );
+        D2<SBasis> stitch( SBasis( 1, Linear(end[X],start[X]) ), SBasis( 1, Linear(end[Y],start[Y]) ) );
+        transformed_pwd2_in.push_seg( stitch );
+    }
+    Point transformed_org = direction.getOrigin();
+    Piecewise<SBasis> tilter;//used to bend the hatches
+    Matrix bend_mat;//used to bend the hatches
+
+    if (do_bend.get_value()){
+        Point bend_dir = -rot90(unit_vector(bender.getVector()));
+        double bend_amount = L2(bender.getVector());
+        bend_mat = Matrix(-bend_dir[Y], bend_dir[X], bend_dir[X], bend_dir[Y],0,0);
+        transformed_pwd2_in = transformed_pwd2_in * bend_mat;
+        tilter = Piecewise<SBasis>(shift(Linear(-bend_amount),1));
+        OptRect bbox = bounds_exact( transformed_pwd2_in );
+        if (not(bbox)) return pwd2_in;
+        tilter.setDomain((*bbox)[Y]);
+        transformed_pwd2_in = bend(transformed_pwd2_in, tilter);
+        transformed_pwd2_in = transformed_pwd2_in * bend_mat.inverse();
+    }
+    hatch_dist = Geom::L2(direction.getVector())/5;
+    Point hatches_dir = rot90(unit_vector(direction.getVector()));
+    Matrix mat(-hatches_dir[Y], hatches_dir[X], hatches_dir[X], hatches_dir[Y],0,0);
+    transformed_pwd2_in = transformed_pwd2_in * mat;
+    transformed_org *= mat;
+        
+    std::vector<std::vector<Point> > snakePoints;
+    snakePoints = linearSnake(transformed_pwd2_in, transformed_org);
+    if ( snakePoints.size() > 0 ){
+        Piecewise<D2<SBasis> >smthSnake = smoothSnake(snakePoints); 
+        smthSnake = smthSnake*mat.inverse();
+        if (do_bend.get_value()){
+            smthSnake = smthSnake*bend_mat;
+            smthSnake = bend(smthSnake, -tilter);
+            smthSnake = smthSnake*bend_mat.inverse();
+        }
+        return (smthSnake);
+    }
+    return pwd2_in;
+}
+
+//------------------------------------------------
+// Generate the levels with random, growth...
+//------------------------------------------------
+std::vector<double>
+LPERoughHatches::generateLevels(Interval const &domain, double x_org){
+    std::vector<double> result;
+    int n = int((domain.min()-x_org)/hatch_dist);
+    double x = x_org +  n * hatch_dist;
+    //double x = domain.min() + double(hatch_dist)/2.;
+    double step = double(hatch_dist);
+    double scale = 1+(hatch_dist*growth/domain.extent());
+    while (x < domain.max()){
+        result.push_back(x);
+        double rdm = 1;
+        if (dist_rdm.get_value() != 0) 
+            rdm = 1.+ double((2*dist_rdm - dist_rdm.get_value()))/100.;
+        x+= step*rdm;
+        step*=scale;//(1.+double(growth));
+    }
+    return result;
+}
+
+
+//-------------------------------------------------------
+// Walk through the intersections to create linear hatches
+//-------------------------------------------------------
+std::vector<std::vector<Point> > 
+LPERoughHatches::linearSnake(Piecewise<D2<SBasis> > const &f, Point const &org){
+
+    //std::cout<<"linearSnake:\n";
+    std::vector<std::vector<Point> > result;
+    Piecewise<SBasis> x = make_cuts_independent(f)[X];
+    //Remark: derivative is computed twice in the 2 lines below!!
+    Piecewise<SBasis> dx = derivative(x);
+    OptInterval range = bounds_exact(x);
+
+    if (not range) return result;
+    std::vector<double> levels = generateLevels(*range, org[X]);
+    std::vector<std::vector<double> > times;
+    times = multi_roots(x,levels);
+//TODO: fix multi_roots!!!*****************************************
+//remove doubles :-(
+    std::vector<std::vector<double> > cleaned_times(levels.size(),std::vector<double>());
+    for (unsigned i=0; i<times.size(); i++){
+        if ( times[i].size()>0 ){
+            double last_t = times[i][0]-1;//ugly hack!!
+            for (unsigned j=0; j<times[i].size(); j++){
+                if (times[i][j]-last_t >0.000001){
+                    last_t = times[i][j];
+                    cleaned_times[i].push_back(last_t);
+                }
+            }
+        }
+    }
+    times = cleaned_times;
+//*******************************************************************
+
+    LevelsCrossings lscs(times,f,dx);
+
+    unsigned i,j;
+    lscs.findFirstUnused(i,j);
+    
+    std::vector<Point> result_component;
+    int n = int((range->min()-org[X])/hatch_dist);
+    
+    while ( i < lscs.size() ){ 
+        int dir = 0;
+        //switch orientation of first segment according to starting point.
+        if (i % 2 == n%2 && j < lscs[i].size()-1 && !lscs[i][j].used){
+            j += 1;
+            dir = 2;
+        }
+
+        while ( i < lscs.size() ){
+            result_component.push_back(lscs[i][j].pt);
+            lscs[i][j].used = true;
+            lscs.step(i,j, dir);
+        }
+        result.push_back(result_component);
+        result_component = std::vector<Point>();
+        lscs.findFirstUnused(i,j);
+    }
+    return result;
+}
+
+//-------------------------------------------------------
+// Smooth the linear hatches according to params...
+//-------------------------------------------------------
+Piecewise<D2<SBasis> > 
+LPERoughHatches::smoothSnake(std::vector<std::vector<Point> > const &linearSnake){
+
+    Piecewise<D2<SBasis> > result;
+    for (unsigned comp=0; comp<linearSnake.size(); comp++){
+        if (linearSnake[comp].size()>=2){
+            Point last_pt = linearSnake[comp][0];
+            Point last_top = linearSnake[comp][0];
+            Point last_bot = linearSnake[comp][0];
+            Point last_hdle = linearSnake[comp][0];
+            Point last_top_hdle = linearSnake[comp][0];
+            Point last_bot_hdle = linearSnake[comp][0];
+            Geom::Path res_comp(last_pt);
+            Geom::Path res_comp_top(last_pt);
+            Geom::Path res_comp_bot(last_pt);
+            unsigned i=1;
+            //bool is_top = true;//Inversion here; due to downward y? 
+            bool is_top = ( linearSnake[comp][0][Y] < linearSnake[comp][1][Y] );
+
+            while( i+1<linearSnake[comp].size() ){
+                Point pt0 = linearSnake[comp][i];
+                Point pt1 = linearSnake[comp][i+1];
+                Point new_pt = (pt0+pt1)/2;
+                double scale_in = (is_top ? scale_tf : scale_bf );
+                double scale_out = (is_top ? scale_tb : scale_bb );
+                if (is_top){
+                    if (top_edge_variation.get_value() != 0) 
+                        new_pt[Y] += double(top_edge_variation)-top_edge_variation.get_value()/2.;
+                    if (top_tgt_variation.get_value() != 0) 
+                        new_pt[X] += double(top_tgt_variation)-top_tgt_variation.get_value()/2.;
+                    if (top_smth_variation.get_value() != 0) {
+                        scale_in*=(100.-double(top_smth_variation))/100.;
+                        scale_out*=(100.-double(top_smth_variation))/100.;
+                    }
+                }else{
+                    if (bot_edge_variation.get_value() != 0) 
+                        new_pt[Y] += double(bot_edge_variation)-bot_edge_variation.get_value()/2.;
+                    if (bot_tgt_variation.get_value() != 0) 
+                        new_pt[X] += double(bot_tgt_variation)-bot_tgt_variation.get_value()/2.;
+                    if (bot_smth_variation.get_value() != 0) {
+                        scale_in*=(100.-double(bot_smth_variation))/100.;
+                        scale_out*=(100.-double(bot_smth_variation))/100.;
+                    }
+                }
+                Point new_hdle_in  = new_pt + (pt0-pt1) * (scale_in /2.);
+                Point new_hdle_out = new_pt - (pt0-pt1) * (scale_out/2.);
+                
+                if ( fat_output.get_value() ){
+                    //double scaled_width = double((is_top ? stroke_width_top : stroke_width_bot))/(pt1[X]-pt0[X]);
+                    double scaled_width = 1./(pt1[X]-pt0[X]);
+                    Point hdle_offset = (pt1-pt0)*scaled_width;
+                    Point inside = new_pt;
+                    Point inside_hdle_in;
+                    Point inside_hdle_out;
+                    inside[Y]+= double((is_top ? -stroke_width_top : stroke_width_bot));
+                    inside_hdle_in  = inside + (new_hdle_in -new_pt);// + hdle_offset * double((is_top ? front_thickness : back_thickness));
+                    inside_hdle_out = inside + (new_hdle_out-new_pt);// - hdle_offset * double((is_top ? back_thickness : front_thickness));
+
+                    inside_hdle_in  +=  (pt1-pt0)/2*( double((is_top ? front_thickness : back_thickness)) / (pt1[X]-pt0[X]) );
+                    inside_hdle_out -=  (pt1-pt0)/2*( double((is_top ? back_thickness : front_thickness)) / (pt1[X]-pt0[X]) );
+
+                    new_hdle_in  -=  (pt1-pt0)/2*( double((is_top ? front_thickness : back_thickness)) / (pt1[X]-pt0[X]) );
+                    new_hdle_out +=  (pt1-pt0)/2*( double((is_top ? back_thickness : front_thickness)) / (pt1[X]-pt0[X]) );
+                    //TODO: find a good way to handle limit cases (small smthness, large stroke).
+                    //if (inside_hdle_in[X]  > inside[X]) inside_hdle_in = inside;
+                    //if (inside_hdle_out[X] < inside[X]) inside_hdle_out = inside;
+                    
+                    if (is_top){
+                        res_comp_top.appendNew<CubicBezier>(last_top_hdle,new_hdle_in,new_pt);
+                        res_comp_bot.appendNew<CubicBezier>(last_bot_hdle,inside_hdle_in,inside);
+                        last_top_hdle = new_hdle_out;
+                        last_bot_hdle = inside_hdle_out;
+                    }else{
+                        res_comp_top.appendNew<CubicBezier>(last_top_hdle,inside_hdle_in,inside);
+                        res_comp_bot.appendNew<CubicBezier>(last_bot_hdle,new_hdle_in,new_pt);
+                        last_top_hdle = inside_hdle_out;
+                        last_bot_hdle = new_hdle_out;
+                    }
+                }else{
+                    res_comp.appendNew<CubicBezier>(last_hdle,new_hdle_in,new_pt);
+                }
+            
+                last_hdle = new_hdle_out;
+                i+=2;
+                is_top = !is_top;
+            }
+            if ( i<linearSnake[comp].size() ){
+                if ( fat_output.get_value() ){
+                    res_comp_top.appendNew<CubicBezier>(last_top_hdle,linearSnake[comp][i],linearSnake[comp][i]);
+                    res_comp_bot.appendNew<CubicBezier>(last_bot_hdle,linearSnake[comp][i],linearSnake[comp][i]);
+                }else{
+                    res_comp.appendNew<CubicBezier>(last_hdle,linearSnake[comp][i],linearSnake[comp][i]);
+                }
+            }
+            if ( fat_output.get_value() ){
+                res_comp = res_comp_bot;
+                res_comp.append(res_comp_top.reverse(),Geom::Path::STITCH_DISCONTINUOUS);
+            }    
+            result.concat(res_comp.toPwSb());
+        }
+    }
+    return result;
+}
+
+void
+LPERoughHatches::doBeforeEffect (SPLPEItem */*lpeitem*/)
+{
+    using namespace Geom;
+    top_edge_variation.resetRandomizer();
+    bot_edge_variation.resetRandomizer();
+    top_tgt_variation.resetRandomizer();
+    bot_tgt_variation.resetRandomizer();
+    top_smth_variation.resetRandomizer();
+    bot_smth_variation.resetRandomizer();
+    dist_rdm.resetRandomizer();
+
+    //original_bbox(lpeitem);
+}
+
+
+void
+LPERoughHatches::resetDefaults(SPItem * item)
+{
+    Effect::resetDefaults(item);
+
+    Geom::OptRect bbox = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
+    Geom::Point origin(0.,0.);
+    Geom::Point vector(50.,0.);
+    if (bbox) {
+        origin = bbox->midpoint();
+        vector = Geom::Point((*bbox)[X].extent()/4, 0.);
+        top_edge_variation.param_set_value( (*bbox)[Y].extent()/10, 0 );
+        bot_edge_variation.param_set_value( (*bbox)[Y].extent()/10, 0 );
+        top_edge_variation.write_to_SVG();
+        bot_edge_variation.write_to_SVG();
+    }
+    //direction.set_and_write_new_values(origin, vector);
+    //bender.param_set_and_write_new_value( origin + Geom::Point(5,0) );
+    direction.set_and_write_new_values(origin + Geom::Point(0,-5), vector);
+    bender.set_and_write_new_values( origin, Geom::Point(5,0) );
+    hatch_dist = Geom::L2(vector)/2;
+}
+
+
+} //namespace LivePathEffect
+} /* namespace Inkscape */
+
+/*
+  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 :
index 816955e4d1050b4164efbf7b85cc205d0e334bdd..cbc3c3b26d9d19debeb7064d45daecd1eaa7b4ea 100644 (file)
@@ -1,78 +1,78 @@
-#ifndef INKSCAPE_LPE_ROUGH_HATCHES_H\r
-#define INKSCAPE_LPE_ROUGH_HATCHES_H\r
-\r
-/** \file\r
- * Fills an area with rough hatches.\r
- */\r
-\r
-/*\r
- * Authors:\r
- *   JFBarraud\r
- *\r
- * Copyright (C) JF Barraud 2008.\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/point.h"\r
-#include "live_effects/parameter/parameter.h"\r
-#include "live_effects/parameter/bool.h"\r
-#include "live_effects/parameter/random.h"\r
-#include "live_effects/parameter/vector.h"\r
-\r
-namespace Inkscape {\r
-namespace LivePathEffect {\r
-\r
-class LPERoughHatches : public Effect {\r
-public:\r
-    LPERoughHatches(LivePathEffectObject *lpeobject);\r
-    virtual ~LPERoughHatches();\r
-\r
-    virtual Geom::Piecewise<Geom::D2<Geom::SBasis> >\r
-    doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);\r
-\r
-    virtual void resetDefaults(SPItem * item);\r
-\r
-    virtual void doBeforeEffect(SPLPEItem * item);\r
-\r
-  std::vector<double>\r
-    generateLevels(Geom::Interval const &domain, double x_org);\r
-\r
-  std::vector<std::vector<Geom::Point> >\r
-    linearSnake(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &f, Geom::Point const &org);\r
-\r
-  Geom::Piecewise<Geom::D2<Geom::SBasis> > \r
-  smoothSnake(std::vector<std::vector<Geom::Point> > const &linearSnake);\r
-    \r
-private:\r
-  double hatch_dist;\r
-  RandomParam dist_rdm;\r
-  ScalarParam growth;\r
-  //topfront,topback,bottomfront,bottomback handle scales.\r
-  ScalarParam scale_tf, scale_tb, scale_bf, scale_bb;\r
-\r
-  RandomParam top_edge_variation;\r
-  RandomParam bot_edge_variation;\r
-  RandomParam top_tgt_variation;\r
-  RandomParam bot_tgt_variation;\r
-  RandomParam top_smth_variation;\r
-  RandomParam bot_smth_variation;\r
-\r
-  BoolParam fat_output, do_bend;\r
-  ScalarParam stroke_width_top;\r
-  ScalarParam stroke_width_bot;\r
-  ScalarParam front_thickness, back_thickness;\r
-\r
-  //PointParam  bender;\r
-  VectorParam direction;\r
-  VectorParam bender;\r
-\r
-  LPERoughHatches(const LPERoughHatches&);\r
-  LPERoughHatches& operator=(const LPERoughHatches&);\r
-};\r
-\r
-} //namespace LivePathEffect\r
-} //namespace Inkscape\r
-\r
-#endif\r
+#ifndef INKSCAPE_LPE_ROUGH_HATCHES_H
+#define INKSCAPE_LPE_ROUGH_HATCHES_H
+
+/** \file
+ * Fills an area with rough hatches.
+ */
+
+/*
+ * Authors:
+ *   JFBarraud
+ *
+ * Copyright (C) JF Barraud 2008.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/point.h"
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/bool.h"
+#include "live_effects/parameter/random.h"
+#include "live_effects/parameter/vector.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPERoughHatches : public Effect {
+public:
+    LPERoughHatches(LivePathEffectObject *lpeobject);
+    virtual ~LPERoughHatches();
+
+    virtual Geom::Piecewise<Geom::D2<Geom::SBasis> >
+    doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
+
+    virtual void resetDefaults(SPItem * item);
+
+    virtual void doBeforeEffect(SPLPEItem * item);
+
+  std::vector<double>
+    generateLevels(Geom::Interval const &domain, double x_org);
+
+  std::vector<std::vector<Geom::Point> >
+    linearSnake(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &f, Geom::Point const &org);
+
+  Geom::Piecewise<Geom::D2<Geom::SBasis> > 
+  smoothSnake(std::vector<std::vector<Geom::Point> > const &linearSnake);
+    
+private:
+  double hatch_dist;
+  RandomParam dist_rdm;
+  ScalarParam growth;
+  //topfront,topback,bottomfront,bottomback handle scales.
+  ScalarParam scale_tf, scale_tb, scale_bf, scale_bb;
+
+  RandomParam top_edge_variation;
+  RandomParam bot_edge_variation;
+  RandomParam top_tgt_variation;
+  RandomParam bot_tgt_variation;
+  RandomParam top_smth_variation;
+  RandomParam bot_smth_variation;
+
+  BoolParam fat_output, do_bend;
+  ScalarParam stroke_width_top;
+  ScalarParam stroke_width_bot;
+  ScalarParam front_thickness, back_thickness;
+
+  //PointParam  bender;
+  VectorParam direction;
+  VectorParam bender;
+
+  LPERoughHatches(const LPERoughHatches&);
+  LPERoughHatches& operator=(const LPERoughHatches&);
+};
+
+} //namespace LivePathEffect
+} //namespace Inkscape
+
+#endif