summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Moulder <peter.moulder@monash.edu>2009-04-07 06:32:25 +0000
committerpjrm <pjrm@users.sourceforge.net>2009-04-07 06:32:25 +0000
commit4f0165ebdaf2a07079d6b9f166a585fd89e8b064 (patch)
treeb56a880772fe7c03e4c9957d54da53bc9cfc85f5
parentnoop: svg/svg-path-geom-test.h: Change to consistent end-of-line separators, ... (diff)
downloadinkscape-4f0165ebdaf2a07079d6b9f166a585fd89e8b064.tar.gz
inkscape-4f0165ebdaf2a07079d6b9f166a585fd89e8b064.zip
noop: Set svn:eol-style to native on all .cpp and .h files under src. (find \( -name '*.cpp' -o -name '*.h' \) -print0 | xargs -0 svn propset svn:eol-style native)
(bzr r7649)
-rw-r--r--src/2geom/bezier-clipping.cpp2584
-rw-r--r--src/2geom/chebyshev.cpp252
-rw-r--r--src/2geom/chebyshev.h60
-rw-r--r--src/2geom/utils.cpp174
-rw-r--r--src/extension/internal/javafx-out.cpp1950
-rw-r--r--src/extension/internal/javafx-out.h306
-rw-r--r--src/helper/geom-curves.h78
-rw-r--r--src/live_effects/lpe-rough-hatches.cpp1170
-rw-r--r--src/live_effects/lpe-rough-hatches.h156
9 files changed, 3365 insertions, 3365 deletions
diff --git a/src/2geom/bezier-clipping.cpp b/src/2geom/bezier-clipping.cpp
index c8d99c430..96a06376c 100644
--- a/src/2geom/bezier-clipping.cpp
+++ b/src/2geom/bezier-clipping.cpp
@@ -1,1292 +1,1292 @@
-/*
- * 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 :
+/*
+ * 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 :
diff --git a/src/2geom/chebyshev.cpp b/src/2geom/chebyshev.cpp
index 4ba3e2325..447c5183f 100644
--- a/src/2geom/chebyshev.cpp
+++ b/src/2geom/chebyshev.cpp
@@ -1,126 +1,126 @@
-#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 :
+#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 :
diff --git a/src/2geom/chebyshev.h b/src/2geom/chebyshev.h
index 309e09b3e..6de9e9cc0 100644
--- a/src/2geom/chebyshev.h
+++ b/src/2geom/chebyshev.h
@@ -1,30 +1,30 @@
-#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
+#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
diff --git a/src/2geom/utils.cpp b/src/2geom/utils.cpp
index f0f42ce01..579718553 100644
--- a/src/2geom/utils.cpp
+++ b/src/2geom/utils.cpp
@@ -1,87 +1,87 @@
-/** 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 :
+/** 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 :
diff --git a/src/extension/internal/javafx-out.cpp b/src/extension/internal/javafx-out.cpp
index fd28011c3..0a1108b1e 100644
--- a/src/extension/internal/javafx-out.cpp
+++ b/src/extension/internal/javafx-out.cpp
@@ -1,975 +1,975 @@
-/*
- * 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 :
+/*
+ * 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 :
diff --git a/src/extension/internal/javafx-out.h b/src/extension/internal/javafx-out.h
index 691812ee2..9c1c8778b 100644
--- a/src/extension/internal/javafx-out.h
+++ b/src/extension/internal/javafx-out.h
@@ -1,153 +1,153 @@
-/*
- * 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 */
-
+/*
+ * 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 */
+
diff --git a/src/helper/geom-curves.h b/src/helper/geom-curves.h
index daf208e78..f3dc364f2 100644
--- a/src/helper/geom-curves.h
+++ b/src/helper/geom-curves.h
@@ -1,39 +1,39 @@
-#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 :
+#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 :
diff --git a/src/live_effects/lpe-rough-hatches.cpp b/src/live_effects/lpe-rough-hatches.cpp
index d75941b12..4cf3e35e6 100644
--- a/src/live_effects/lpe-rough-hatches.cpp
+++ b/src/live_effects/lpe-rough-hatches.cpp
@@ -1,585 +1,585 @@
-#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 :
+#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 :
diff --git a/src/live_effects/lpe-rough-hatches.h b/src/live_effects/lpe-rough-hatches.h
index 816955e4d..cbc3c3b26 100644
--- a/src/live_effects/lpe-rough-hatches.h
+++ b/src/live_effects/lpe-rough-hatches.h
@@ -1,78 +1,78 @@
-#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
+#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