0,0 → 1,97 |
SET(2GEOM_VERSION 0.1.0) |
|
# We need to pass -fPIC only to lib2geom on amd64 - avoid it elsewhere see: http://www.gentoo.org/proj/en/base/amd64/howtos/index.xml?part=1&chap=3 |
IF (ARCH_X86_64 EQUAL 1) |
SET(CMAKE_CXX_FLAGS "$(CMAKE_CXX_FLAGS) -fPIC") |
ENDIF (ARCH_X86_64 EQUAL 1) |
|
OPTION(2GEOM_BUILD_SHARED |
"Build lib2geom and libtoy as shared libraries." |
OFF) |
SET(LIB_TYPE STATIC) |
|
SET(2GEOM_SRC |
svg-path.h |
svg-path.cpp |
ord.h |
path.cpp |
path.h |
path-intersection.cpp |
path-intersection.h |
svg-elliptical-arc.h |
shape.cpp |
shape.h |
region.cpp |
region.h |
crossing.h |
crossing.cpp |
sweep.cpp |
sweep.h |
poly.cpp |
poly.h |
poly-dk-solve.cpp |
poly-dk-solve.h |
poly-laguerre-solve.cpp |
poly-laguerre-solve.h |
quadtree.cpp |
quadtree.h |
matrix.cpp |
matrix.h |
transforms.cpp |
transforms.h |
point.h |
point.cpp |
point-l.h |
coord.h |
d2.h |
d2-sbasis.h |
d2-sbasis.cpp |
rect.h |
piecewise.h |
piecewise.cpp |
sbasis.cpp |
sbasis.h |
sbasis-2d.h |
sbasis-2d.cpp |
sbasis-geometric.cpp |
sbasis-geometric.h |
sbasis-math.h |
sbasis-math.cpp |
sbasis-poly.cpp |
sbasis-poly.h |
sbasis-roots.cpp |
sbasis-to-bezier.cpp |
sbasis-to-bezier.h |
bezier-to-sbasis.h |
basic-intersection.h |
basic-intersection.cpp |
geom.cpp |
geom.h |
utils.h |
exception.h |
angle.h |
bezier-utils.cpp |
bezier-utils.h |
choose.h |
circulator.h |
conjugate_gradient.cpp |
conjugate_gradient.h |
convex-cover.cpp |
convex-cover.h |
solve-bezier-one-d.cpp |
solve-bezier-parametric.cpp |
solver.h |
sturm.h |
scribushelper.h |
scribushelper.cpp |
) |
|
SET(SCRIBUS_2GEOM_LIB "scribus_2geom_lib") |
ADD_LIBRARY(${SCRIBUS_2GEOM_LIB} STATIC ${2GEOM_SRC}) |
|
# This is a convenience library that for linkage purposes is part of Scribus's |
# main API. |
SET_TARGET_PROPERTIES(${SCRIBUS_2GEOM_LIB} |
PROPERTIES |
COMPILE_FLAGS -DCOMPILE_SCRIBUS_MAIN_APP |
) |
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis-math.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis-math.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis-math.cpp (revision 14937) |
@@ -0,0 +1,291 @@ |
+/* |
+ * sbasis-math.cpp - some std functions to work with (pw)s-basis |
+ * |
+ * Authors: |
+ * Jean-Francois Barraud |
+ * |
+ * Copyright (C) 2006-2007 authors |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ */ |
+ |
+//this a first try to define sqrt, cos, sin, etc... |
+//TODO: define a truncated compose(sb,sb, order) and extend it to pw<sb>. |
+//TODO: in all these functions, compute 'order' according to 'tol'. |
+ |
+#include "sbasis-math.h" |
+//#define ZERO 1e-3 |
+ |
+#include <cstdio> |
+#include <cmath> |
+#include "angle.h" |
+ |
+namespace Geom { |
+ |
+//-|x|----------------------------------------------------------------------- |
+Piecewise<SBasis> abs(SBasis const &f){ |
+ return abs(Piecewise<SBasis>(f)); |
+} |
+Piecewise<SBasis> abs(Piecewise<SBasis> const &f){ |
+ Piecewise<SBasis> absf=partition(f,roots(f)); |
+ for (unsigned i=0; i<absf.size(); i++){ |
+ if (absf.segs[i](.5)<0) absf.segs[i]*=-1; |
+ } |
+ return absf; |
+} |
+ |
+//-max(x,y), min(x,y)-------------------------------------------------------- |
+Piecewise<SBasis> max( SBasis const &f, SBasis const &g){ |
+ return max(Piecewise<SBasis>(f),Piecewise<SBasis>(g)); |
+} |
+Piecewise<SBasis> max(Piecewise<SBasis> const &f, SBasis const &g){ |
+ return max(f,Piecewise<SBasis>(g)); |
+} |
+Piecewise<SBasis> max( SBasis const &f, Piecewise<SBasis> const &g){ |
+ return max(Piecewise<SBasis>(f),g); |
+} |
+Piecewise<SBasis> max(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g){ |
+ Piecewise<SBasis> max=partition(f,roots(f-g)); |
+ Piecewise<SBasis> gg =partition(g,max.cuts); |
+ max = partition(max,gg.cuts); |
+ for (unsigned i=0; i<max.size(); i++){ |
+ if (max.segs[i](.5)<gg.segs[i](.5)) max.segs[i]=gg.segs[i]; |
+ } |
+ return max; |
+} |
+ |
+Piecewise<SBasis> |
+min( SBasis const &f, SBasis const &g){ return -max(-f,-g); } |
+Piecewise<SBasis> |
+min(Piecewise<SBasis> const &f, SBasis const &g){ return -max(-f,-g); } |
+Piecewise<SBasis> |
+min( SBasis const &f, Piecewise<SBasis> const &g){ return -max(-f,-g); } |
+Piecewise<SBasis> |
+min(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g){ return -max(-f,-g); } |
+ |
+ |
+//-sign(x)--------------------------------------------------------------- |
+Piecewise<SBasis> signSb(SBasis const &f){ |
+ return signSb(Piecewise<SBasis>(f)); |
+} |
+Piecewise<SBasis> signSb(Piecewise<SBasis> const &f){ |
+ Piecewise<SBasis> sign=partition(f,roots(f)); |
+ for (unsigned i=0; i<sign.size(); i++){ |
+ sign.segs[i] = (sign.segs[i](.5)<0)? Linear(-1.):Linear(1.); |
+ } |
+ return sign; |
+} |
+ |
+//-Sqrt---------------------------------------------------------- |
+static Piecewise<SBasis> sqrt_internal(SBasis const &f, |
+ double tol, |
+ int order){ |
+ SBasis sqrtf; |
+ if(f.isZero() || order == 0){ |
+ return Piecewise<SBasis>(sqrtf); |
+ } |
+ if (f.at0()<-tol*tol && f.at1()<-tol*tol){ |
+ return sqrt_internal(-f,tol,order); |
+ }else if (f.at0()>tol*tol && f.at1()>tol*tol){ |
+ sqrtf.resize(order+1, Linear(0,0)); |
+ sqrtf[0] = Linear(std::sqrt(f[0][0]), std::sqrt(f[0][1])); |
+ SBasis r = f - multiply(sqrtf, sqrtf); // remainder |
+ for(unsigned i = 1; int(i) <= order && i<r.size(); ++i) { |
+ Linear ci(r[i][0]/(2*sqrtf[0][0]), r[i][1]/(2*sqrtf[0][1])); |
+ SBasis cisi = shift(ci, i); |
+ r -= multiply(shift((sqrtf*2 + cisi), i), SBasis(ci)); |
+ r.truncate(order+1); |
+ sqrtf[i] = ci; |
+ if(r.tailError(i) == 0) // if exact |
+ break; |
+ } |
+ }else{ |
+ sqrtf = Linear(std::sqrt(fabs(f.at0())), std::sqrt(fabs(f.at1()))); |
+ } |
+ |
+ double err = (f - multiply(sqrtf, sqrtf)).tailError(0); |
+ if (err<tol){ |
+ return Piecewise<SBasis>(sqrtf); |
+ } |
+ |
+ Piecewise<SBasis> sqrtf0,sqrtf1; |
+ sqrtf0 = sqrt_internal(compose(f,Linear(0.,.5)),tol,order); |
+ sqrtf1 = sqrt_internal(compose(f,Linear(.5,1.)),tol,order); |
+ sqrtf0.setDomain(Interval(0.,.5)); |
+ sqrtf1.setDomain(Interval(.5,1.)); |
+ sqrtf0.concat(sqrtf1); |
+ return sqrtf0; |
+} |
+ |
+Piecewise<SBasis> sqrt(SBasis const &f, double tol, int order){ |
+ return sqrt(max(f,Linear(tol*tol)),tol,order); |
+} |
+ |
+Piecewise<SBasis> sqrt(Piecewise<SBasis> const &f, double tol, int order){ |
+ Piecewise<SBasis> result; |
+ Piecewise<SBasis> zero = Piecewise<SBasis>(Linear(tol*tol)); |
+ zero.setDomain(f.domain()); |
+ Piecewise<SBasis> ff=max(f,zero); |
+ |
+ for (unsigned i=0; i<ff.size(); i++){ |
+ Piecewise<SBasis> sqrtfi = sqrt_internal(ff.segs[i],tol,order); |
+ sqrtfi.setDomain(Interval(ff.cuts[i],ff.cuts[i+1])); |
+ result.concat(sqrtfi); |
+ } |
+ return result; |
+} |
+ |
+//-Yet another sin/cos-------------------------------------------------------------- |
+ |
+Piecewise<SBasis> sin( SBasis const &f, double tol, int order){return cos(-f+M_PI_2,tol,order);} |
+Piecewise<SBasis> sin(Piecewise<SBasis> const &f, double tol, int order){return cos(-f+M_PI_2,tol,order);} |
+ |
+Piecewise<SBasis> cos(Piecewise<SBasis> const &f, double tol, int order){ |
+ Piecewise<SBasis> result; |
+ for (unsigned i=0; i<f.size(); i++){ |
+ Piecewise<SBasis> cosfi = cos(f.segs[i],tol,order); |
+ cosfi.setDomain(Interval(f.cuts[i],f.cuts[i+1])); |
+ result.concat(cosfi); |
+ } |
+ return result; |
+} |
+ |
+Piecewise<SBasis> cos( SBasis const &f, double tol, int order){ |
+ double alpha = (f.at0()+f.at1())/2.; |
+ SBasis x = f-alpha; |
+ double d = x.tailError(0),err=1; |
+ //estimate cos(x)-sum_0^order (-1)^k x^2k/2k! by the first neglicted term |
+ for (int i=1; i<=2*order; i++) err*=d/i; |
+ |
+ if (err<tol){ |
+ SBasis xk=Linear(1), c=Linear(1), s=Linear(0); |
+ for (int k=1; k<=2*order; k+=2){ |
+ xk*=x/k; |
+ //take also truncature errors into account... |
+ err+=xk.tailError(order); |
+ xk.truncate(order); |
+ s+=xk; |
+ xk*=-x/(k+1); |
+ //take also truncature errors into account... |
+ err+=xk.tailError(order); |
+ xk.truncate(order); |
+ c+=xk; |
+ } |
+ if (err<tol){ |
+ return Piecewise<SBasis>(std::cos(alpha)*c-std::sin(alpha)*s); |
+ } |
+ } |
+ Piecewise<SBasis> c0,c1; |
+ c0 = cos(compose(f,Linear(0.,.5)),tol,order); |
+ c1 = cos(compose(f,Linear(.5,1.)),tol,order); |
+ c0.setDomain(Interval(0.,.5)); |
+ c1.setDomain(Interval(.5,1.)); |
+ c0.concat(c1); |
+ return c0; |
+} |
+ |
+ |
+//--1/x------------------------------------------------------------ |
+//TODO: this implementation is just wrong. Remove or redo! |
+ |
+void truncateResult(Piecewise<SBasis> &f, int order){ |
+ if (order>=0){ |
+ for (unsigned k=0; k<f.segs.size(); k++){ |
+ f.segs[k].truncate(order); |
+ } |
+ } |
+} |
+ |
+Piecewise<SBasis> reciprocalOnDomain(Interval range, double tol){ |
+ Piecewise<SBasis> reciprocal_fn; |
+ //TODO: deduce R from tol... |
+ double R=2.; |
+ SBasis reciprocal1_R=reciprocal(Linear(1,R),3); |
+ double a=range.min(), b=range.max(); |
+ if (a*b<0){ |
+ b=std::max(fabs(a),fabs(b)); |
+ a=0; |
+ }else if (b<0){ |
+ a=-range.max(); |
+ b=-range.min(); |
+ } |
+ |
+ if (a<=tol){ |
+ reciprocal_fn.push_cut(0); |
+ int i0=(int) floor(std::log(tol)/std::log(R)); |
+ a=pow(R,i0); |
+ reciprocal_fn.push(Linear(1/a),a); |
+ }else{ |
+ int i0=(int) floor(std::log(a)/std::log(R)); |
+ a=pow(R,i0); |
+ reciprocal_fn.cuts.push_back(a); |
+ } |
+ |
+ while (a<b){ |
+ reciprocal_fn.push(reciprocal1_R/a,R*a); |
+ a*=R; |
+ } |
+ if (range.min()<0 || range.max()<0){ |
+ Piecewise<SBasis>reciprocal_fn_neg; |
+ //TODO: define reverse(pw<sb>); |
+ reciprocal_fn_neg.cuts.push_back(-reciprocal_fn.cuts.back()); |
+ for (unsigned i=0; i<reciprocal_fn.size(); i++){ |
+ int idx=reciprocal_fn.segs.size()-1-i; |
+ reciprocal_fn_neg.push_seg(-reverse(reciprocal_fn.segs.at(idx))); |
+ reciprocal_fn_neg.push_cut(-reciprocal_fn.cuts.at(idx)); |
+ } |
+ if (range.max()>0){ |
+ reciprocal_fn_neg.concat(reciprocal_fn); |
+ } |
+ reciprocal_fn=reciprocal_fn_neg; |
+ } |
+ |
+ return(reciprocal_fn); |
+} |
+ |
+Piecewise<SBasis> reciprocal(SBasis const &f, double tol, int order){ |
+ Piecewise<SBasis> reciprocal_fn=reciprocalOnDomain(bounds_fast(f), tol); |
+ Piecewise<SBasis> result=compose(reciprocal_fn,f); |
+ truncateResult(result,order); |
+ return(result); |
+} |
+Piecewise<SBasis> reciprocal(Piecewise<SBasis> const &f, double tol, int order){ |
+ Piecewise<SBasis> reciprocal_fn=reciprocalOnDomain(bounds_fast(f), tol); |
+ Piecewise<SBasis> result=compose(reciprocal_fn,f); |
+ truncateResult(result,order); |
+ return(result); |
+} |
+ |
+} |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype = cpp:expandtab:shiftwidth = 4:tabstop = 8:softtabstop = 4:encoding = utf-8:textwidth = 99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/scribushelper.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/scribushelper.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/scribushelper.h (revision 14937) |
@@ -0,0 +1,40 @@ |
+/* |
+For general Scribus (>=1.3.2) copyright and licensing information please refer |
+to the COPYING file provided with the program. Following this notice may exist |
+a copyright and/or license notice that predates the release of Scribus 1.3.2 |
+for which a new license (GPL+exception) is in place. |
+*/ |
+#ifndef SCRIBUSHELPER_H |
+#define SCRIBUSHELPER_H |
+ |
+#include "fpointarray.h" |
+#include <vector> |
+#include "sbasis.h" |
+#include "sbasis-geometric.h" |
+#include "bezier-to-sbasis.h" |
+#include "sbasis-to-bezier.h" |
+#include "d2.h" |
+#include "piecewise.h" |
+#include "utils.h" |
+#include "path.h" |
+#include "sbasis-2d.h" |
+#include <QPainterPath> |
+ |
+static FPoint currentPoint; |
+// Functions to convert from lib2geom to FPointArray and vice versa |
+void scribus_curve(FPointArray *cr, Geom::Curve const& c); |
+void geomPath2FPointArray(FPointArray *p, Geom::Path &pp); |
+void Piecewise2FPointArray(FPointArray *p, Geom::Piecewise<Geom::D2<Geom::SBasis> > &pp); |
+void D2sb2d2FPointArray(FPointArray* cr, Geom::D2<Geom::SBasis2d> const &sb2, int num, double width); |
+std::vector<Geom::Path> FPointArray2geomPath(FPointArray &p, bool closed); |
+Geom::Piecewise<Geom::D2<Geom::SBasis> > FPointArray2Piecewise(FPointArray &p, bool closed); |
+ |
+// Functions to convert from lib2geom to QPainterPath and vice versa |
+void arthur_curve(QPainterPath *cr, Geom::Curve const& c); |
+void geomPath2QPainterPath(QPainterPath *p, Geom::Path &pp); |
+void Piecewise2QPainterPath(QPainterPath *p, Geom::Piecewise<Geom::D2<Geom::SBasis> > &pp); |
+void D2sb2d2QPainterPath(QPainterPath* cr, Geom::D2<Geom::SBasis2d> const &sb2, int num, double width); |
+std::vector<Geom::Path> QPainterPath2geomPath(QPainterPath &p, bool closed); |
+Geom::Piecewise<Geom::D2<Geom::SBasis> > QPainterPath2Piecewise(QPainterPath &p, bool closed); |
+ |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/scribushelper.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/scribushelper.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/scribushelper.cpp (revision 14937) |
@@ -0,0 +1,311 @@ |
+/* |
+For general Scribus (>=1.3.2) copyright and licensing information please refer |
+to the COPYING file provided with the program. Following this notice may exist |
+a copyright and/or license notice that predates the release of Scribus 1.3.2 |
+for which a new license (GPL+exception) is in place. |
+*/ |
+ |
+#include "scribushelper.h" |
+ |
+// Functions to convert from lib2geom to FPointArray and vice versa |
+void scribus_curve(FPointArray *cr, Geom::Curve const& c) |
+{ |
+ if(Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&c)) |
+ { |
+ cr->addPoint(currentPoint); |
+ cr->addPoint(currentPoint); |
+ cr->addPoint((*line_segment)[1][0], (*line_segment)[1][1]); |
+ cr->addPoint((*line_segment)[1][0], (*line_segment)[1][1]); |
+ currentPoint = FPoint((*line_segment)[1][0], (*line_segment)[1][1]); |
+ } |
+ else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const*>(&c)) |
+ { |
+ std::vector<Geom::Point> points = quadratic_bezier->points(); |
+ Geom::Point b1 = points[0] + (2./3) * (points[1] - points[0]); |
+ Geom::Point b2 = b1 + (1./3) * (points[2] - points[0]); |
+ cr->addPoint(currentPoint); |
+ cr->addPoint(b1[0], b1[1]); |
+ cr->addPoint(points[2][0], points[2][1]); |
+ cr->addPoint(b2[0], b2[1]); |
+ currentPoint = FPoint(points[2][0], points[2][1]); |
+ } |
+ else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) |
+ { |
+ std::vector<Geom::Point> points = cubic_bezier->points(); |
+ cr->addPoint(currentPoint); |
+ cr->addPoint(points[1][0], points[1][1]); |
+ cr->addPoint(points[3][0], points[3][1]); |
+ cr->addPoint(points[2][0], points[2][1]); |
+ currentPoint = FPoint(points[3][0], points[3][1]); |
+ } |
+ else |
+ { |
+ //this case handles sbasis as well as all other curve types |
+ Geom::Path sbasis_path = Geom::path_from_sbasis(c.toSBasis(), 0.1); |
+ currentPoint = FPoint(sbasis_path.initialPoint()[0], sbasis_path.initialPoint()[1]); |
+ //recurse to convert the new path resulting from the sbasis to svgd |
+ for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) |
+ { |
+ scribus_curve(cr, *iter); |
+ } |
+ } |
+} |
+ |
+void geomPath2FPointArray(FPointArray *p, Geom::Path &pp) |
+{ |
+ currentPoint = FPoint(pp.initialPoint()[0], pp.initialPoint()[1]); |
+ for(Geom::Path::iterator iter(pp.begin()), end(pp.end()); iter != end; ++iter) |
+ { |
+ scribus_curve(p, *iter); |
+ } |
+ if (pp.closed()) |
+ p->setMarker(); |
+} |
+ |
+void Piecewise2FPointArray(FPointArray *p, Geom::Piecewise<Geom::D2<Geom::SBasis> > &pp) |
+{ |
+ std::vector<Geom::Path> pa = path_from_piecewise( pp, 0.1); |
+ std::vector<Geom::Path>::iterator it(pa.begin()); |
+ for(; it != pa.end(); it++) |
+ { |
+ geomPath2FPointArray(p, *it); |
+ } |
+} |
+ |
+void D2sb2d2FPointArray(FPointArray* cr, Geom::D2<Geom::SBasis2d> const &sb2, int num, double width) |
+{ |
+ Geom::D2<Geom::SBasis> B; |
+ for(int ui = 0; ui <= num; ui++) |
+ { |
+ double u = ui / static_cast<double>(num); |
+ B[0] = extract_u(sb2[0], u);// + Linear(u); |
+ B[1] = extract_u(sb2[1], u); |
+ for(unsigned i = 0; i < 2; i ++) |
+ { |
+ B[i] = B[i]*(width/2) + Geom::Linear(width/4); |
+ } |
+ Geom::Path pp = path_from_sbasis(B, 0.1); |
+ geomPath2FPointArray(cr, pp); |
+ cr->setMarker(); |
+ } |
+ for(int vi = 0; vi <= num; vi++) |
+ { |
+ double v = vi / static_cast<double>(num); |
+ B[1] = extract_v(sb2[1], v);// + Linear(v); |
+ B[0] = extract_v(sb2[0], v); |
+ for(unsigned i = 0; i < 2; i ++) |
+ { |
+ B[i] = B[i]*(width/2) + Geom::Linear(width/4); |
+ } |
+ Geom::Path pp = path_from_sbasis(B, 0.1); |
+ geomPath2FPointArray(cr, pp); |
+ cr->setMarker(); |
+ } |
+} |
+ |
+std::vector<Geom::Path> FPointArray2geomPath(FPointArray &p, bool closed) |
+{ |
+ std::vector<Geom::Path> pa; |
+ Geom::Path ret = Geom::Path(); |
+ Geom::Point cur; |
+ FPoint np, np1, np2, np3; |
+ bool nPath = true; |
+ if (p.size() > 3) |
+ { |
+ for (uint poi=0; poi < p.size()-3; poi += 4) |
+ { |
+ if (p.point(poi).x() > 900000) |
+ { |
+ if (closed) |
+ ret.close(); |
+ pa.push_back(ret); |
+ ret.clear(); |
+ nPath = true; |
+ continue; |
+ } |
+ if (nPath) |
+ { |
+ np = p.point(poi); |
+ cur = Geom::Point(np.x(), np.y()); |
+ nPath = false; |
+ } |
+ np = p.point(poi); |
+ np1 = p.point(poi+1); |
+ np2 = p.point(poi+3); |
+ np3 = p.point(poi+2); |
+ if ((np == np1) && (np2 == np3)) |
+ { |
+ // Geom::Point pe = Geom::Point(np3.x(), np3.y()); |
+ // ret.append(Geom::LineSegment(cur, pe)); |
+ Geom::Point pc1 = Geom::Point(np1.x()+0.001, np1.y()+0.001); |
+ Geom::Point pc2 = Geom::Point(np2.x()+0.001, np2.y()+0.001); |
+ Geom::Point pe = Geom::Point(np3.x(), np3.y()); |
+ ret.append(Geom::CubicBezier(cur, pc1, pc2, pe)); |
+ cur = pe; |
+ } |
+ else |
+ { |
+ Geom::Point pc1 = Geom::Point(np1.x(), np1.y()); |
+ Geom::Point pc2 = Geom::Point(np2.x(), np2.y()); |
+ Geom::Point pe = Geom::Point(np3.x(), np3.y()); |
+ ret.append(Geom::CubicBezier(cur, pc1, pc2, pe)); |
+ cur = pe; |
+ } |
+ } |
+ } |
+ if (closed) |
+ ret.close(); |
+ pa.push_back(ret); |
+ return pa; |
+} |
+ |
+Geom::Piecewise<Geom::D2<Geom::SBasis> > FPointArray2Piecewise(FPointArray &p, bool closed) |
+{ |
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > patternpwd2; |
+ std::vector<Geom::Path> originald = FPointArray2geomPath(p, closed); |
+ for (unsigned int i=0; i < originald.size(); i++) |
+ { |
+ patternpwd2.concat( originald[i].toPwSb() ); |
+ } |
+ return patternpwd2; |
+} |
+ |
+// Functions to convert from lib2geom to QPainterPath and vice versa |
+void arthur_curve(QPainterPath *cr, Geom::Curve const& c) |
+{ |
+ if(Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&c)) |
+ { |
+ cr->lineTo(QPointF((*line_segment)[1][0], (*line_segment)[1][1])); |
+ } |
+ else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const*>(&c)) |
+ { |
+ std::vector<Geom::Point> points = quadratic_bezier->points(); |
+ Geom::Point b1 = points[0] + (2./3) * (points[1] - points[0]); |
+ Geom::Point b2 = b1 + (1./3) * (points[2] - points[0]); |
+ cr->cubicTo(b1[0], b1[1], b2[0], b2[1], points[2][0], points[2][1]); |
+ } |
+ else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) |
+ { |
+ std::vector<Geom::Point> points = cubic_bezier->points(); |
+ cr->cubicTo(points[1][0], points[1][1], points[2][0], points[2][1], points[3][0], points[3][1]); |
+ } |
+ else |
+ { |
+ //this case handles sbasis as well as all other curve types |
+ Geom::Path sbasis_path = Geom::path_from_sbasis(c.toSBasis(), 0.1); |
+ cr->moveTo(sbasis_path.initialPoint()[0], sbasis_path.initialPoint()[1]); |
+ //recurse to convert the new path resulting from the sbasis to svgd |
+ for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) |
+ { |
+ arthur_curve(cr, *iter); |
+ } |
+ } |
+} |
+ |
+void geomPath2QPainterPath(QPainterPath *p, Geom::Path &pp) |
+{ |
+ p->moveTo(pp.initialPoint()[0], pp.initialPoint()[1]); |
+ for(Geom::Path::iterator iter(pp.begin()), end(pp.end()); iter != end; ++iter) |
+ { |
+ arthur_curve(p, *iter); |
+ } |
+ if (pp.closed()) |
+ p->closeSubpath(); |
+} |
+ |
+void Piecewise2QPainterPath(QPainterPath *p, Geom::Piecewise<Geom::D2<Geom::SBasis> > &pp) |
+{ |
+ std::vector<Geom::Path> pa = path_from_piecewise( pp, 0.1); |
+ std::vector<Geom::Path>::iterator it(pa.begin()); |
+ for(; it != pa.end(); it++) |
+ { |
+ geomPath2QPainterPath(p, *it); |
+ } |
+} |
+ |
+void D2sb2d2QPainterPath(QPainterPath* cr, Geom::D2<Geom::SBasis2d> const &sb2, int num, double width) |
+{ |
+ Geom::D2<Geom::SBasis> B; |
+ for(int ui = 0; ui <= num; ui++) |
+ { |
+ double u = ui / static_cast<double>(num); |
+ B[0] = extract_u(sb2[0], u);// + Linear(u); |
+ B[1] = extract_u(sb2[1], u); |
+ for(unsigned i = 0; i < 2; i ++) |
+ { |
+ B[i] = B[i]*(width/2) + Geom::Linear(width/4); |
+ } |
+ Geom::Path pp = path_from_sbasis(B, 0.1); |
+ geomPath2QPainterPath(cr, pp); |
+// cr->setMarker(); |
+ } |
+ for(int vi = 0; vi <= num; vi++) |
+ { |
+ double v = vi / static_cast<double>(num); |
+ B[1] = extract_v(sb2[1], v);// + Linear(v); |
+ B[0] = extract_v(sb2[0], v); |
+ for(unsigned i = 0; i < 2; i ++) |
+ { |
+ B[i] = B[i]*(width/2) + Geom::Linear(width/4); |
+ } |
+ Geom::Path pp = path_from_sbasis(B, 0.1); |
+ geomPath2QPainterPath(cr, pp); |
+// cr->setMarker(); |
+ } |
+} |
+ |
+std::vector<Geom::Path> QPainterPath2geomPath(QPainterPath &p, bool closed) |
+{ |
+ std::vector<Geom::Path> pa; |
+ Geom::Path ret = Geom::Path(); |
+ Geom::Point cur; |
+ bool WasM = false; |
+ for (int i = 0; i < p.elementCount(); ++i) |
+ { |
+ const QPainterPath::Element &elm = p.elementAt(i); |
+ switch (elm.type) |
+ { |
+ case QPainterPath::MoveToElement: |
+ if (WasM) |
+ { |
+ if (closed) |
+ ret.close(); |
+ pa.push_back(ret); |
+ ret.clear(); |
+ } |
+ WasM = true; |
+ cur = Geom::Point(elm.x, elm.y); |
+ break; |
+ case QPainterPath::LineToElement: |
+ ret.append(Geom::LineSegment(cur, Geom::Point(elm.x, elm.y))); |
+ cur = Geom::Point(elm.x, elm.y); |
+ break; |
+ case QPainterPath::CurveToElement: |
+ { |
+ Geom::Point pc1 = Geom::Point(elm.x, elm.y); |
+ Geom::Point pc2 = Geom::Point(p.elementAt(i+1).x, p.elementAt(i+1).y); |
+ Geom::Point pe = Geom::Point(p.elementAt(i+2).x, p.elementAt(i+2).y); |
+ ret.append(Geom::CubicBezier(cur, pc1, pc2, pe)); |
+ cur = pe; |
+ } |
+ break; |
+ default: |
+ break; |
+ } |
+ } |
+ if (closed) |
+ ret.close(); |
+ pa.push_back(ret); |
+ return pa; |
+} |
+ |
+Geom::Piecewise<Geom::D2<Geom::SBasis> > QPainterPath2Piecewise(QPainterPath &p, bool closed) |
+{ |
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > patternpwd2; |
+ std::vector<Geom::Path> originald = QPainterPath2geomPath(p, closed); |
+ for (unsigned int i=0; i < originald.size(); i++) |
+ { |
+ patternpwd2.concat( originald[i].toPwSb() ); |
+ } |
+ return patternpwd2; |
+} |
Index: trunk/Scribus/scribus/third_party/lib2geom/path.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/path.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/path.h (revision 14937) |
@@ -0,0 +1,821 @@ |
+/* |
+ * Path - Series of continuous curves |
+ * |
+ * Copyright 2007 MenTaLguY <mental@rydia.net> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ */ |
+ |
+#ifndef SEEN_GEOM_PATH_H |
+#define SEEN_GEOM_PATH_H |
+ |
+#include "point.h" |
+#include <iterator> |
+#include <algorithm> |
+#include "exception.h" |
+#include "d2.h" |
+#include "matrix.h" |
+#include "bezier.h" |
+#include "crossing.h" |
+#include "utils.h" |
+ |
+namespace Geom { |
+ |
+class Curve; |
+ |
+struct CurveHelpers { |
+protected: |
+ static int root_winding(Curve const &c, Point p); |
+}; |
+ |
+class Curve : private CurveHelpers { |
+public: |
+ virtual ~Curve() {} |
+ |
+ virtual Point initialPoint() const = 0; |
+ virtual Point finalPoint() const = 0; |
+ |
+ virtual bool isDegenerate() const = 0; |
+ |
+ virtual Curve *duplicate() const = 0; |
+ |
+ virtual Rect boundsFast() const = 0; |
+ virtual Rect boundsExact() const = 0; |
+ virtual Rect boundsLocal(Interval i, unsigned deg) const = 0; |
+ Rect boundsLocal(Interval i) const { return boundsLocal(i, 0); } |
+ |
+ virtual std::vector<double> roots(double v, Dim2 d) const = 0; |
+ |
+ virtual int winding(Point p) const { return root_winding(*this, p); } |
+ |
+ //mental: review these |
+ virtual Curve *portion(double f, double t) const = 0; |
+ virtual Curve *reverse() const { return portion(1, 0); } |
+ virtual Curve *derivative() const = 0; |
+ |
+ virtual void setInitial(Point v) = 0; |
+ virtual void setFinal(Point v) = 0; |
+ |
+ virtual Curve *transformed(Matrix const &m) const = 0; |
+ |
+ virtual Point pointAt(Coord t) const { return pointAndDerivatives(t, 1).front(); } |
+ virtual Coord valueAt(Coord t, Dim2 d) const { return pointAt(t)[d]; } |
+ virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const = 0; |
+ virtual D2<SBasis> toSBasis() const = 0; |
+}; |
+ |
+class SBasisCurve : public Curve { |
+private: |
+ SBasisCurve(); |
+ D2<SBasis> inner; |
+public: |
+ explicit SBasisCurve(D2<SBasis> const &sb) : inner(sb) {} |
+ explicit SBasisCurve(Curve const &other) : inner(other.toSBasis()) {} |
+ Curve *duplicate() const { return new SBasisCurve(*this); } |
+ |
+ Point initialPoint() const { return inner.at0(); } |
+ Point finalPoint() const { return inner.at1(); } |
+ bool isDegenerate() const { return inner.isConstant(); } |
+ Point pointAt(Coord t) const { return inner.valueAt(t); } |
+ std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const { |
+ return inner.valueAndDerivatives(t, n); |
+ } |
+ double valueAt(Coord t, Dim2 d) const { return inner[d].valueAt(t); } |
+ |
+ void setInitial(Point v) { for(unsigned d = 0; d < 2; d++) { inner[d][0][0] = v[d]; } } |
+ void setFinal(Point v) { for(unsigned d = 0; d < 2; d++) { inner[d][0][1] = v[d]; } } |
+ |
+ Rect boundsFast() const { return bounds_fast(inner); } |
+ Rect boundsExact() const { return bounds_exact(inner); } |
+ Rect boundsLocal(Interval i, unsigned deg) const { return bounds_local(inner, i, deg); } |
+ |
+ std::vector<double> roots(double v, Dim2 d) const { return Geom::roots(inner[d] - v); } |
+ |
+ Curve *portion(double f, double t) const { |
+ return new SBasisCurve(Geom::portion(inner, f, t)); |
+ } |
+ |
+ Curve *transformed(Matrix const &m) const { |
+ return new SBasisCurve(inner * m); |
+ } |
+ |
+ Curve *derivative() const { |
+ return new SBasisCurve(Geom::derivative(inner)); |
+ } |
+ |
+ D2<SBasis> toSBasis() const { return inner; } |
+ |
+}; |
+ |
+template <unsigned order> |
+class BezierCurve : public Curve { |
+private: |
+ D2<Bezier > inner; |
+public: |
+ template <unsigned required_degree> |
+ static void assert_degree(BezierCurve<required_degree> const *) {} |
+ |
+ BezierCurve() : inner(Bezier::Order(order), Bezier::Order(order)) { |
+ } |
+ |
+ explicit BezierCurve(D2<Bezier > const &x) : inner(x) {} |
+ |
+ BezierCurve(Bezier x, Bezier y) : inner(x, y) {} |
+ |
+ // default copy |
+ // default assign |
+ |
+ BezierCurve(Point c0, Point c1) { |
+ assert_degree<1>(this); |
+ for(unsigned d = 0; d < 2; d++) |
+ inner[d] = Bezier(c0[d], c1[d]); |
+ } |
+ |
+ BezierCurve(Point c0, Point c1, Point c2) { |
+ assert_degree<2>(this); |
+ for(unsigned d = 0; d < 2; d++) |
+ inner[d] = Bezier(c0[d], c1[d], c2[d]); |
+ } |
+ |
+ BezierCurve(Point c0, Point c1, Point c2, Point c3) { |
+ assert_degree<3>(this); |
+ for(unsigned d = 0; d < 2; d++) |
+ inner[d] = Bezier(c0[d], c1[d], c2[d], c3[d]); |
+ } |
+ |
+ unsigned degree() const { return order; } |
+ |
+ Curve *duplicate() const { return new BezierCurve(*this); } |
+ |
+ Point initialPoint() const { return inner.at0(); } |
+ Point finalPoint() const { return inner.at1(); } |
+ |
+ bool isDegenerate() const { return inner.isConstant(); } |
+ |
+ void setInitial(Point v) { setPoint(0, v); } |
+ void setFinal(Point v) { setPoint(1, v); } |
+ |
+ void setPoint(unsigned ix, Point v) { inner[X].setPoint(ix, v[X]); inner[Y].setPoint(ix, v[Y]); } |
+ Point const operator[](unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); } |
+ |
+ Rect boundsFast() const { return bounds_fast(inner); } |
+ Rect boundsExact() const { return bounds_exact(inner); } |
+ Rect boundsLocal(Interval i, unsigned deg) const { |
+ if(i.min() == 0 && i.max() == 1) return boundsFast(); |
+ if(deg == 0) return bounds_local(inner, i); |
+ // TODO: UUUUUUGGGLLY |
+ if(deg == 1 && order > 1) return Rect(bounds_local(Geom::derivative(inner[X]), i), |
+ bounds_local(Geom::derivative(inner[Y]), i)); |
+ return Rect(Interval(0,0), Interval(0,0)); |
+ } |
+//TODO: local |
+ |
+//TODO: implement next 3 natively |
+ int winding(Point p) const { |
+ return SBasisCurve(toSBasis()).winding(p); |
+ } |
+ |
+ std::vector<double> |
+ roots(double v, Dim2 d) const { |
+ return (inner[d] - v).roots(); |
+ } |
+ |
+ void setPoints(std::vector<Point> ps) { |
+ for(unsigned i = 0; i <= order; i++) { |
+ setPoint(i, ps[i]); |
+ } |
+ } |
+ std::vector<Point> points() const { return bezier_points(inner); } |
+ |
+ std::pair<BezierCurve<order>, BezierCurve<order> > subdivide(Coord t) const { |
+ std::pair<Bezier, Bezier > sx = inner[X].subdivide(t), sy = inner[Y].subdivide(t); |
+ return std::pair<BezierCurve<order>, BezierCurve<order> >( |
+ BezierCurve<order>(sx.first, sy.first), |
+ BezierCurve<order>(sx.second, sy.second)); |
+ } |
+ |
+ Curve *portion(double f, double t) const { |
+ return new BezierCurve(Geom::portion(inner, f, t)); |
+ } |
+ |
+ Curve *reverse() const { |
+ return new BezierCurve(Geom::reverse(inner)); |
+ } |
+ |
+ Curve *transformed(Matrix const &m) const { |
+ BezierCurve *ret = new BezierCurve(); |
+ std::vector<Point> ps = points(); |
+ for(unsigned i = 0; i <= order; i++) ps[i] = ps[i] * m; |
+ ret->setPoints(ps); |
+ return ret; |
+ } |
+ |
+ Curve *derivative() const { |
+ if(order > 1) |
+ return new BezierCurve<order-1>(Geom::derivative(inner[X]), Geom::derivative(inner[Y])); |
+ else if (order == 1) { |
+ double dx = inner[X][1] - inner[X][0], dy = inner[Y][1] - inner[Y][0]; |
+ if(dx == 0) return new BezierCurve<1>(Point(0,0), Point(0,0)); |
+ double slope = dy / dx; |
+ Geom::Point pnt; |
+ if(slope == 0) pnt = Geom::Point(0, 0); else pnt = Geom::Point(slope, 1./slope); |
+ return new BezierCurve<1>(pnt, pnt); |
+ } |
+ } |
+ |
+ Point pointAt(double t) const { return inner.valueAt(t); } |
+ std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const { return inner.valueAndDerivatives(t, n); } |
+ |
+ double valueAt(double t, Dim2 d) const { return inner[d].valueAt(t); } |
+ |
+ D2<SBasis> toSBasis() const {return inner.toSBasis(); } |
+ |
+protected: |
+ BezierCurve(Point c[]) { |
+ Coord x[order+1], y[order+1]; |
+ for(unsigned i = 0; i <= order; i++) { |
+ x[i] = c[i][X]; y[i] = c[i][Y]; |
+ } |
+ inner = Bezier(x, y); |
+ } |
+}; |
+ |
+// BezierCurve<0> is meaningless; specialize it out |
+template<> class BezierCurve<0> : public BezierCurve<1> { public: BezierCurve(); BezierCurve(Bezier x, Bezier y) {}; }; |
+ |
+typedef BezierCurve<1> LineSegment; |
+typedef BezierCurve<2> QuadraticBezier; |
+typedef BezierCurve<3> CubicBezier; |
+ |
+class SVGEllipticalArc : public Curve { |
+public: |
+ SVGEllipticalArc() {} |
+ |
+ SVGEllipticalArc(Point initial, double rx, double ry, |
+ double x_axis_rotation, bool large_arc, |
+ bool sweep, Point final) |
+ : initial_(initial), rx_(rx), ry_(ry), x_axis_rotation_(x_axis_rotation), |
+ large_arc_(large_arc), sweep_(sweep), final_(final) |
+ {} |
+ |
+ Curve *duplicate() const { return new SVGEllipticalArc(*this); } |
+ |
+ Point initialPoint() const { return initial_; } |
+ Point finalPoint() const { return final_; } |
+ |
+ void setInitial(Point v) { initial_ = v; } |
+ void setFinal(Point v) { final_ = v; } |
+ |
+ //TODO: implement funcs |
+ |
+ bool isDegenerate() const { return toSBasis().isConstant(); } |
+ Rect boundsFast() const; |
+ Rect boundsExact() const; |
+ Rect boundsLocal(Interval i, unsigned deg) const; |
+ |
+ int winding(Point p) const { |
+ return SBasisCurve(toSBasis()).winding(p); |
+ } |
+ |
+ std::vector<double> roots(double v, Dim2 d) const; |
+ |
+ inline std::pair<SVGEllipticalArc, SVGEllipticalArc> |
+ subdivide(Coord t) { |
+ SVGEllipticalArc a(*this), b(*this); |
+ a.final_ = b.initial_ = pointAt(t); |
+ return std::pair<SVGEllipticalArc, SVGEllipticalArc>(a, b); |
+ } |
+ |
+// TODO: how are the flags affected by reducing an arc from more than 180deg to less than 180deg? |
+ Curve *portion(double f, double t) const { |
+ SVGEllipticalArc *ret = new SVGEllipticalArc (*this); |
+ ret->initial_ = pointAt(f); |
+ ret->final_ = pointAt(t); |
+ return ret; |
+ } |
+ |
+// TODO: incomplete/buggy |
+ Curve *reverse(double /*f*/, double /*t*/) const { |
+ SVGEllipticalArc *ret = new SVGEllipticalArc (*this); |
+ ret->initial_ = final_; |
+ ret->final_ = initial_; |
+ return ret; |
+ } |
+ |
+ //TODO: this next def isn't right |
+ Curve *transformed(Matrix const & m) const { |
+ SVGEllipticalArc *ret = new SVGEllipticalArc (*this); |
+ ret->initial_ = initial_ * m; |
+ ret->final_ = final_ * m; |
+ return ret; |
+ } |
+ |
+ Curve *derivative() const { throwNotImplemented(0); } |
+ |
+ std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const; |
+ |
+ D2<SBasis> toSBasis() const; |
+ |
+private: |
+ Point initial_; |
+ double rx_; |
+ double ry_; |
+ double x_axis_rotation_; |
+ bool large_arc_; |
+ bool sweep_; |
+ Point final_; |
+}; |
+ |
+template <typename IteratorImpl> |
+class BaseIterator |
+: public std::iterator<std::forward_iterator_tag, Curve const> |
+{ |
+public: |
+ BaseIterator() {} |
+ |
+ // default construct |
+ // default copy |
+ |
+ bool operator==(BaseIterator const &other) { |
+ return other.impl_ == impl_; |
+ } |
+ bool operator!=(BaseIterator const &other) { |
+ return other.impl_ != impl_; |
+ } |
+ |
+ Curve const &operator*() const { return **impl_; } |
+ Curve const *operator->() const { return *impl_; } |
+ |
+ BaseIterator &operator++() { |
+ ++impl_; |
+ return *this; |
+ } |
+ |
+ BaseIterator operator++(int) { |
+ BaseIterator old=*this; |
+ ++(*this); |
+ return old; |
+ } |
+ |
+private: |
+ BaseIterator(IteratorImpl const &pos) : impl_(pos) {} |
+ |
+ IteratorImpl impl_; |
+ friend class Path; |
+}; |
+ |
+template <typename Iterator> |
+class DuplicatingIterator |
+: public std::iterator<std::input_iterator_tag, Curve *> |
+{ |
+public: |
+ DuplicatingIterator() {} |
+ DuplicatingIterator(Iterator const &iter) : impl_(iter) {} |
+ |
+ bool operator==(DuplicatingIterator const &other) { |
+ return other.impl_ == impl_; |
+ } |
+ bool operator!=(DuplicatingIterator const &other) { |
+ return other.impl_ != impl_; |
+ } |
+ |
+ Curve *operator*() const { return (*impl_)->duplicate(); } |
+ |
+ DuplicatingIterator &operator++() { |
+ ++impl_; |
+ return *this; |
+ } |
+ DuplicatingIterator operator++(int) { |
+ DuplicatingIterator old=*this; |
+ ++(*this); |
+ return old; |
+ } |
+ |
+private: |
+ Iterator impl_; |
+}; |
+ |
+class Path { |
+private: |
+ typedef std::vector<Curve *> Sequence; |
+ |
+public: |
+ typedef BaseIterator<Sequence::iterator> iterator; |
+ typedef BaseIterator<Sequence::const_iterator> const_iterator; |
+ typedef Sequence::size_type size_type; |
+ typedef Sequence::difference_type difference_type; |
+ |
+ Path() |
+ : final_(new LineSegment()), closed_(false) |
+ { |
+ curves_.push_back(final_); |
+ } |
+ |
+ Path(Path const &other) |
+ : final_(new LineSegment()), closed_(other.closed_) |
+ { |
+ curves_.push_back(final_); |
+ insert(begin(), other.begin(), other.end()); |
+ } |
+ |
+ explicit Path(Point p) |
+ : final_(new LineSegment(p, p)), closed_(false) |
+ { |
+ curves_.push_back(final_); |
+ } |
+ |
+ template <typename Impl> |
+ Path(BaseIterator<Impl> first, BaseIterator<Impl> last, bool closed=false) |
+ : closed_(closed), final_(new LineSegment()) |
+ { |
+ curves_.push_back(final_); |
+ insert(begin(), first, last); |
+ } |
+ |
+ virtual ~Path() { |
+ delete_range(curves_.begin(), curves_.end()-1); |
+ delete final_; |
+ } |
+ |
+ Path &operator=(Path const &other) { |
+ clear(); |
+ insert(begin(), other.begin(), other.end()); |
+ close(other.closed_); |
+ return *this; |
+ } |
+ |
+ void swap(Path &other); |
+ |
+ Curve const &operator[](unsigned i) const { return *curves_[i]; } |
+ |
+ iterator begin() { return curves_.begin(); } |
+ iterator end() { return curves_.end()-1; } |
+ |
+ Curve const &front() const { return *curves_[0]; } |
+ Curve const &back() const { return *curves_[curves_.size()-2]; } |
+ |
+ const_iterator begin() const { return curves_.begin(); } |
+ const_iterator end() const { return curves_.end()-1; } |
+ |
+ const_iterator end_open() const { return curves_.end()-1; } |
+ const_iterator end_closed() const { return curves_.end(); } |
+ const_iterator end_default() const { |
+ return ( closed_ ? end_closed() : end_open() ); |
+ } |
+ |
+ size_type size() const { return curves_.size()-1; } |
+ size_type max_size() const { return curves_.max_size()-1; } |
+ |
+ bool empty() const { return curves_.size() == 1; } |
+ bool closed() const { return closed_; } |
+ void close(bool closed=true) { closed_ = closed; } |
+ |
+ Rect boundsFast() const; |
+ Rect boundsExact() const; |
+ |
+ Piecewise<D2<SBasis> > toPwSb() const { |
+ Piecewise<D2<SBasis> > ret; |
+ ret.push_cut(0); |
+ unsigned i = 1; |
+ // ignore that path is closed or open. pw<d2<>> is always open. |
+ for(const_iterator it = begin(); it != end(); ++it) { |
+ if (!it->isDegenerate()) { |
+ ret.push(it->toSBasis(), i++); |
+ } |
+ } |
+ return ret; |
+ } |
+ |
+ Path operator*(Matrix const &m) const { |
+ Path ret; |
+ for(const_iterator it = begin(); it != end(); ++it) { |
+ Curve *temp = it->transformed(m); |
+ //Possible point of discontinuity? |
+ ret.append(*temp); |
+ delete temp; |
+ } |
+ return ret; |
+ } |
+ |
+ Point pointAt(double t) const { |
+ if(empty()) return Point(0,0); |
+ double i, f = modf(t, &i); |
+ if(i == size() && f == 0) { i--; } |
+ assert(i >= 0 && i <= size()); |
+ return (*this)[unsigned(i)].pointAt(f); |
+ } |
+ |
+ double valueAt(double t, Dim2 d) const { |
+ if(empty()) return 0; |
+ double i, f = modf(t, &i); |
+ if(i == size() && f == 0) { i--; } |
+ assert(i >= 0 && i <= size()); |
+ return (*this)[unsigned(i)].valueAt(f, d); |
+ } |
+ |
+ std::vector<double> roots(double v, Dim2 d) const { |
+ std::vector<double> res; |
+ for(unsigned i = 0; i <= size(); i++) { |
+ std::vector<double> temp = (*this)[i].roots(v, d); |
+ for(unsigned j = 0; j < temp.size(); j++) |
+ res.push_back(temp[j] + i); |
+ } |
+ return res; |
+ } |
+ |
+ void appendPortionTo(Path &p, double f, double t) const; |
+ |
+ Path portion(double f, double t) const { |
+ Path ret; |
+ ret.close(false); |
+ appendPortionTo(ret, f, t); |
+ return ret; |
+ } |
+ Path portion(Interval i) const { return portion(i.min(), i.max()); } |
+ |
+ Path reverse() const { |
+ Path ret; |
+ ret.close(closed_); |
+ for(int i = size() - (closed_ ? 0 : 1); i >= 0; i--) { |
+ //TODO: do we really delete? |
+ Curve *temp = (*this)[i].reverse(); |
+ ret.append(*temp); |
+ delete temp; |
+ } |
+ return ret; |
+ } |
+ |
+ void insert(iterator pos, Curve const &curve) { |
+ Sequence source(1, curve.duplicate()); |
+ try { |
+ do_update(pos.impl_, pos.impl_, source.begin(), source.end()); |
+ } catch (...) { |
+ delete_range(source.begin(), source.end()); |
+ throw; |
+ } |
+ } |
+ |
+ template <typename Impl> |
+ void insert(iterator pos, BaseIterator<Impl> first, BaseIterator<Impl> last) |
+ { |
+ Sequence source(DuplicatingIterator<Impl>(first.impl_), |
+ DuplicatingIterator<Impl>(last.impl_)); |
+ try { |
+ do_update(pos.impl_, pos.impl_, source.begin(), source.end()); |
+ } catch (...) { |
+ delete_range(source.begin(), source.end()); |
+ throw; |
+ } |
+ } |
+ |
+ void clear() { |
+ do_update(curves_.begin(), curves_.end()-1, |
+ curves_.begin(), curves_.begin()); |
+ } |
+ |
+ void erase(iterator pos) { |
+ do_update(pos.impl_, pos.impl_+1, curves_.begin(), curves_.begin()); |
+ } |
+ |
+ void erase(iterator first, iterator last) { |
+ do_update(first.impl_, last.impl_, curves_.begin(), curves_.begin()); |
+ } |
+ |
+ void replace(iterator replaced, Curve const &curve) { |
+ Sequence source(1, curve.duplicate()); |
+ try { |
+ do_update(replaced.impl_, replaced.impl_+1, source.begin(), source.end()); |
+ } catch (...) { |
+ delete_range(source.begin(), source.end()); |
+ throw; |
+ } |
+ } |
+ |
+ void replace(iterator first_replaced, iterator last_replaced, |
+ Curve const &curve) |
+ { |
+ Sequence source(1, curve.duplicate()); |
+ try { |
+ do_update(first_replaced.impl_, last_replaced.impl_, |
+ source.begin(), source.end()); |
+ } catch (...) { |
+ delete_range(source.begin(), source.end()); |
+ throw; |
+ } |
+ } |
+ |
+ template <typename Impl> |
+ void replace(iterator replaced, |
+ BaseIterator<Impl> first, BaseIterator<Impl> last) |
+ { |
+ Sequence source(DuplicatingIterator<Impl>(first.impl_), |
+ DuplicatingIterator<Impl>(last.impl_)); |
+ try { |
+ do_update(replaced.impl_, replaced.impl_+1, source.begin(), source.end()); |
+ } catch (...) { |
+ delete_range(source.begin(), source.end()); |
+ throw; |
+ } |
+ } |
+ |
+ template <typename Impl> |
+ void replace(iterator first_replaced, iterator last_replaced, |
+ BaseIterator<Impl> first, BaseIterator<Impl> last) |
+ { |
+ Sequence source(first.impl_, last.impl_); |
+ try { |
+ do_update(first_replaced.impl_, last_replaced.impl_, |
+ source.begin(), source.end()); |
+ } catch (...) { |
+ delete_range(source.begin(), source.end()); |
+ throw; |
+ } |
+ } |
+ |
+ void start(Point p) { |
+ clear(); |
+ final_->setPoint(0, p); |
+ final_->setPoint(1, p); |
+ } |
+ |
+ Point initialPoint() const { return (*final_)[1]; } |
+ Point finalPoint() const { return (*final_)[0]; } |
+ |
+ void append(Curve const &curve); |
+ void append(D2<SBasis> const &curve); |
+ |
+ template <typename CurveType, typename A> |
+ void appendNew(A a) { |
+ do_append(new CurveType((*final_)[0], a)); |
+ } |
+ |
+ template <typename CurveType, typename A, typename B> |
+ void appendNew(A a, B b) { |
+ do_append(new CurveType((*final_)[0], a, b)); |
+ } |
+ |
+ template <typename CurveType, typename A, typename B, typename C> |
+ void appendNew(A a, B b, C c) { |
+ do_append(new CurveType((*final_)[0], a, b, c)); |
+ } |
+ |
+ template <typename CurveType, typename A, typename B, typename C, |
+ typename D> |
+ void appendNew(A a, B b, C c, D d) { |
+ do_append(new CurveType((*final_)[0], a, b, c, d)); |
+ } |
+ |
+ template <typename CurveType, typename A, typename B, typename C, |
+ typename D, typename E> |
+ void appendNew(A a, B b, C c, D d, E e) { |
+ do_append(new CurveType((*final_)[0], a, b, c, d, e)); |
+ } |
+ |
+ template <typename CurveType, typename A, typename B, typename C, |
+ typename D, typename E, typename F> |
+ void appendNew(A a, B b, C c, D d, E e, F f) { |
+ do_append(new CurveType((*final_)[0], a, b, c, d, e, f)); |
+ } |
+ |
+ template <typename CurveType, typename A, typename B, typename C, |
+ typename D, typename E, typename F, |
+ typename G> |
+ void appendNew(A a, B b, C c, D d, E e, F f, G g) { |
+ do_append(new CurveType((*final_)[0], a, b, c, d, e, f, g)); |
+ } |
+ |
+ template <typename CurveType, typename A, typename B, typename C, |
+ typename D, typename E, typename F, |
+ typename G, typename H> |
+ void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) { |
+ do_append(new CurveType((*final_)[0], a, b, c, d, e, f, g, h)); |
+ } |
+ |
+ template <typename CurveType, typename A, typename B, typename C, |
+ typename D, typename E, typename F, |
+ typename G, typename H, typename I> |
+ void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) { |
+ do_append(new CurveType((*final_)[0], a, b, c, d, e, f, g, h, i)); |
+ } |
+ |
+private: |
+ void do_update(Sequence::iterator first_replaced, |
+ Sequence::iterator last_replaced, |
+ Sequence::iterator first, |
+ Sequence::iterator last); |
+ |
+ void do_append(Curve *curve); |
+ |
+ void delete_range(Sequence::iterator first, Sequence::iterator last); |
+ |
+ void check_continuity(Sequence::iterator first_replaced, |
+ Sequence::iterator last_replaced, |
+ Sequence::iterator first, |
+ Sequence::iterator last); |
+ |
+ Sequence curves_; |
+ LineSegment *final_; |
+ bool closed_; |
+}; |
+ |
+inline static Piecewise<D2<SBasis> > paths_to_pw(std::vector<Path> paths) { |
+ Piecewise<D2<SBasis> > ret = paths[0].toPwSb(); |
+ for(unsigned i = 1; i < paths.size(); i++) { |
+ ret.concat(paths[i].toPwSb()); |
+ } |
+ return ret; |
+} |
+ |
+/* |
+class PathPortion : public Curve { |
+ Path *source; |
+ double f, t; |
+ boost::optional<Path> result; |
+ |
+ public: |
+ double from() const { return f; } |
+ double to() const { return t; } |
+ |
+ explicit PathPortion(Path *s, double fp, double tp) : source(s), f(fp), t(tp) {} |
+ Curve *duplicate() const { return new PathPortion(*this); } |
+ |
+ Point initialPoint() const { return source->pointAt(f); } |
+ Point finalPoint() const { return source->pointAt(t); } |
+ |
+ Path actualPath() { |
+ if(!result) *result = source->portion(f, t); |
+ return *result; |
+ } |
+ |
+ Rect boundsFast() const { return actualPath().boundsFast; } |
+ Rect boundsExact() const { return actualPath().boundsFast; } |
+ Rect boundsLocal(Interval i) const { throwNotImplemented(); } |
+ |
+ std::vector<double> roots(double v, Dim2 d) const = 0; |
+ |
+ virtual int winding(Point p) const { return root_winding(*this, p); } |
+ |
+ virtual Curve *portion(double f, double t) const = 0; |
+ virtual Curve *reverse() const { return portion(1, 0); } |
+ |
+ virtual Crossings crossingsWith(Curve const & other) const; |
+ |
+ virtual void setInitial(Point v) = 0; |
+ virtual void setFinal(Point v) = 0; |
+ |
+ virtual Curve *transformed(Matrix const &m) const = 0; |
+ |
+ virtual Point pointAt(Coord t) const { return pointAndDerivatives(t, 1).front(); } |
+ virtual Coord valueAt(Coord t, Dim2 d) const { return pointAt(t)[d]; } |
+ virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const = 0; |
+ virtual D2<SBasis> toSBasis() const = 0; |
+ |
+}; |
+*/ |
+ |
+} |
+ |
+namespace std { |
+ |
+template <> |
+inline void swap<Geom::Path>(Geom::Path &a, Geom::Path &b) |
+{ |
+ a.swap(b); |
+} |
+ |
+} |
+ |
+#endif // SEEN_GEOM_PATH_H |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(substatement-open . 0)) |
+ indent-tabs-mode:nil |
+ c-brace-offset:0 |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=2:tabstop=8:softtabstop=2 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/poly-laguerre-solve.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/poly-laguerre-solve.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/poly-laguerre-solve.cpp (revision 14937) |
@@ -0,0 +1,151 @@ |
+#include "poly-laguerre-solve.h" |
+#include <iterator> |
+ |
+typedef std::complex<double> cdouble; |
+ |
+cdouble laguerre_internal_complex(Poly const & p, |
+ double x0, |
+ double tol, |
+ bool & quad_root) { |
+ cdouble a = 2*tol; |
+ cdouble xk = x0; |
+ double n = p.degree(); |
+ quad_root = false; |
+ const unsigned shuffle_rate = 10; |
+// static double shuffle[] = {0, 0.5, 0.25, 0.75, 0.125, 0.375, 0.625, 0.875, 1.0}; |
+ unsigned shuffle_counter = 0; |
+ while(std::norm(a) > (tol*tol)) { |
+ //std::cout << "xk = " << xk << std::endl; |
+ cdouble b = p.back(); |
+ cdouble d = 0, f = 0; |
+ double err = abs(b); |
+ double abx = abs(xk); |
+ for(int j = p.size()-2; j >= 0; j--) { |
+ f = xk*f + d; |
+ d = xk*d + b; |
+ b = xk*b + p[j]; |
+ err = abs(b) + abx*err; |
+ } |
+ |
+ err *= 1e-7; // magic epsilon for convergence, should be computed from tol |
+ |
+ cdouble px = b; |
+ if(abs(b) < err) |
+ return xk; |
+ //if(std::norm(px) < tol*tol) |
+ // return xk; |
+ cdouble G = d / px; |
+ cdouble H = G*G - f / px; |
+ |
+ //std::cout << "G = " << G << "H = " << H; |
+ cdouble radicand = (n - 1)*(n*H-G*G); |
+ //assert(radicand.real() > 0); |
+ if(radicand.real() < 0) |
+ quad_root = true; |
+ //std::cout << "radicand = " << radicand << std::endl; |
+ if(G.real() < 0) // here we try to maximise the denominator avoiding cancellation |
+ a = - sqrt(radicand); |
+ else |
+ a = sqrt(radicand); |
+ //std::cout << "a = " << a << std::endl; |
+ a = n / (a + G); |
+ //std::cout << "a = " << a << std::endl; |
+ if(shuffle_counter % shuffle_rate == 0) |
+ { |
+ //a *= shuffle[shuffle_counter / shuffle_rate]; |
+ } |
+ xk -= a; |
+ shuffle_counter++; |
+ if(shuffle_counter >= 90) |
+ break; |
+ } |
+ //std::cout << "xk = " << xk << std::endl; |
+ return xk; |
+} |
+ |
+double laguerre_internal(Poly const & p, |
+ Poly const & pp, |
+ Poly const & ppp, |
+ double x0, |
+ double tol, |
+ bool & quad_root) { |
+ double a = 2*tol; |
+ double xk = x0; |
+ double n = p.degree(); |
+ quad_root = false; |
+ while(a*a > (tol*tol)) { |
+ //std::cout << "xk = " << xk << std::endl; |
+ double px = p(xk); |
+ if(px*px < tol*tol) |
+ return xk; |
+ double G = pp(xk) / px; |
+ double H = G*G - ppp(xk) / px; |
+ |
+ //std::cout << "G = " << G << "H = " << H; |
+ double radicand = (n - 1)*(n*H-G*G); |
+ assert(radicand > 0); |
+ //std::cout << "radicand = " << radicand << std::endl; |
+ if(G < 0) // here we try to maximise the denominator avoiding cancellation |
+ a = - sqrt(radicand); |
+ else |
+ a = sqrt(radicand); |
+ //std::cout << "a = " << a << std::endl; |
+ a = n / (a + G); |
+ //std::cout << "a = " << a << std::endl; |
+ xk -= a; |
+ } |
+ //std::cout << "xk = " << xk << std::endl; |
+ return xk; |
+} |
+ |
+ |
+std::vector<cdouble > |
+laguerre(Poly p, const double tol) { |
+ std::vector<cdouble > solutions; |
+ //std::cout << "p = " << p << " = "; |
+ while(p.size() > 1) |
+ { |
+ double x0 = 0; |
+ bool quad_root = false; |
+ cdouble sol = laguerre_internal_complex(p, x0, tol, quad_root); |
+ //if(abs(sol) > 1) break; |
+ Poly dvs; |
+ if(quad_root) { |
+ dvs.push_back((sol*conj(sol)).real()); |
+ dvs.push_back(-(sol + conj(sol)).real()); |
+ dvs.push_back(1.0); |
+ //std::cout << "(" << dvs << ")"; |
+ //solutions.push_back(sol); |
+ //solutions.push_back(conj(sol)); |
+ } else { |
+ //std::cout << sol << std::endl; |
+ dvs.push_back(-sol.real()); |
+ dvs.push_back(1.0); |
+ solutions.push_back(sol); |
+ //std::cout << "(" << dvs << ")"; |
+ } |
+ Poly r; |
+ p = divide(p, dvs, r); |
+ //std::cout << r << std::endl; |
+ } |
+ return solutions; |
+} |
+ |
+std::vector<double> |
+laguerre_real_interval(Poly const & ply, |
+ const double lo, const double hi, |
+ const double tol) { |
+ std::vector<double > solutions; |
+ return solutions; |
+} |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/sweep.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sweep.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sweep.cpp (revision 14937) |
@@ -0,0 +1,105 @@ |
+#include "sweep.h" |
+ |
+#include <algorithm> |
+ |
+namespace Geom { |
+ |
+std::vector<std::vector<unsigned> > sweep_bounds(std::vector<Rect> rs) { |
+ std::vector<Event> events; events.reserve(rs.size()*2); |
+ std::vector<std::vector<unsigned> > pairs(rs.size()); |
+ |
+ for(unsigned i = 0; i < rs.size(); i++) { |
+ events.push_back(Event(rs[i].left(), i, false)); |
+ events.push_back(Event(rs[i].right(), i, true)); |
+ } |
+ std::sort(events.begin(), events.end()); |
+ |
+ std::vector<unsigned> open; |
+ for(unsigned i = 0; i < events.size(); i++) { |
+ unsigned ix = events[i].ix; |
+ if(events[i].closing) { |
+ std::vector<unsigned>::iterator iter = std::find(open.begin(), open.end(), ix); |
+ //if(iter != open.end()) |
+ open.erase(iter); |
+ } else { |
+ for(unsigned j = 0; j < open.size(); j++) { |
+ unsigned jx = open[j]; |
+ if(rs[jx][Y].intersects(rs[ix][Y])) { |
+ pairs[jx].push_back(ix); |
+ } |
+ } |
+ open.push_back(ix); |
+ } |
+ } |
+ return pairs; |
+} |
+ |
+std::vector<std::vector<unsigned> > sweep_bounds(std::vector<Rect> a, std::vector<Rect> b) { |
+ std::vector<std::vector<unsigned> > pairs(a.size()); |
+ if(a.empty() || b.empty()) return pairs; |
+ std::vector<Event> events[2]; |
+ events[0].reserve(a.size()*2); |
+ events[1].reserve(b.size()*2); |
+ |
+ for(unsigned n = 0; n < 2; n++) { |
+ unsigned sz = n ? b.size() : a.size(); |
+ events[n].reserve(sz*2); |
+ for(unsigned i = 0; i < sz; i++) { |
+ events[n].push_back(Event(n ? b[i].left() : a[i].left(), i, false)); |
+ events[n].push_back(Event(n ? b[i].right() : a[i].right(), i, true)); |
+ } |
+ std::sort(events[n].begin(), events[n].end()); |
+ } |
+ |
+ std::vector<unsigned> open[2]; |
+ bool n = events[1].front() < events[0].front(); |
+ unsigned i[2] = {0}; |
+ while( i[n] < events[n].size()) { |
+ unsigned ix = events[n][i[n]].ix; |
+ bool closing = events[n][i[n]].closing; |
+ //std::cout << n << "[" << ix << "] - " << (closing ? "closer" : "opener") << "\n"; |
+ if(closing) { |
+ open[n].erase(std::find(open[n].begin(), open[n].end(), ix)); |
+ } else { |
+ if(n) { |
+ //n = 1 |
+ //opening a B, add to all open a |
+ for(unsigned j = 0; j < open[0].size(); j++) { |
+ unsigned jx = open[0][j]; |
+ if(a[jx][Y].intersects(b[ix][Y])) { |
+ pairs[jx].push_back(ix); |
+ } |
+ } |
+ } else { |
+ //n = 0 |
+ //opening an A, add all open b |
+ for(unsigned j = 0; j < open[1].size(); j++) { |
+ unsigned jx = open[1][j]; |
+ if(b[jx][Y].intersects(a[ix][Y])) { |
+ pairs[ix].push_back(jx); |
+ } |
+ } |
+ } |
+ open[n].push_back(ix); |
+ } |
+ i[n]++; |
+ n = (events[!n][i[!n]] < events[n][i[n]]) ? !n : n; |
+ } |
+ return pairs; |
+} |
+ |
+//Fake cull, until the switch to the real sweep is made. |
+std::vector<std::vector<unsigned> > fake_cull(unsigned a, unsigned b) { |
+ std::vector<std::vector<unsigned> > ret; |
+ |
+ std::vector<unsigned> all; |
+ for(unsigned j = 0; j < b; j++) |
+ all.push_back(j); |
+ |
+ for(unsigned i = 0; i < a; i++) |
+ ret.push_back(all); |
+ |
+ return ret; |
+} |
+ |
+} |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis.cpp (revision 14937) |
@@ -0,0 +1,492 @@ |
+/* |
+ * sbasis.cpp - S-power basis function class + supporting classes |
+ * |
+ * Authors: |
+ * Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * Michael Sloan <mgsloan@gmail.com> |
+ * |
+ * Copyright (C) 2006-2007 authors |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ */ |
+ |
+#include <cmath> |
+ |
+#include "sbasis.h" |
+#include "isnan.h" |
+#include "angle.h" |
+ |
+ |
+namespace Geom{ |
+ |
+/*** At some point we should work on tighter bounds for the error. It is clear that the error is |
+ * bounded by the L1 norm over the tail of the series, but this is very loose, leading to far too |
+ * many cubic beziers. I've changed this to be \sum _i=tail ^\infty |hat a_i| 2^-i but I have no |
+ * evidence that this is correct. |
+ */ |
+ |
+/* |
+double SBasis::tail_error(unsigned tail) const { |
+ double err = 0, s = 1./(1<<(2*tail)); // rough |
+ for(unsigned i = tail; i < size(); i++) { |
+ err += (fabs((*this)[i][0]) + fabs((*this)[i][1]))*s; |
+ s /= 4; |
+ } |
+ return err; |
+} |
+*/ |
+ |
+double SBasis::tailError(unsigned tail) const { |
+ Interval bs = bounds_fast(*this, tail); |
+ return std::max(fabs(bs.min()),fabs(bs.max())); |
+} |
+ |
+bool SBasis::isFinite() const { |
+ for(unsigned i = 0; i < size(); i++) { |
+ if(!(*this)[i].isFinite()) |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+SBasis operator+(const SBasis& a, const SBasis& b) { |
+ SBasis result; |
+ const unsigned out_size = std::max(a.size(), b.size()); |
+ const unsigned min_size = std::min(a.size(), b.size()); |
+ result.reserve(out_size); |
+ |
+ for(unsigned i = 0; i < min_size; i++) { |
+ result.push_back(a[i] + b[i]); |
+ } |
+ for(unsigned i = min_size; i < a.size(); i++) |
+ result.push_back(a[i]); |
+ for(unsigned i = min_size; i < b.size(); i++) |
+ result.push_back(b[i]); |
+ |
+ assert(result.size() == out_size); |
+ return result; |
+} |
+ |
+SBasis operator-(const SBasis& a, const SBasis& b) { |
+ SBasis result; |
+ const unsigned out_size = std::max(a.size(), b.size()); |
+ const unsigned min_size = std::min(a.size(), b.size()); |
+ result.reserve(out_size); |
+ |
+ for(unsigned i = 0; i < min_size; i++) { |
+ result.push_back(a[i] - b[i]); |
+ } |
+ for(unsigned i = min_size; i < a.size(); i++) |
+ result.push_back(a[i]); |
+ for(unsigned i = min_size; i < b.size(); i++) |
+ result.push_back(-b[i]); |
+ |
+ assert(result.size() == out_size); |
+ return result; |
+} |
+ |
+SBasis& operator+=(SBasis& a, const SBasis& b) { |
+ const unsigned out_size = std::max(a.size(), b.size()); |
+ const unsigned min_size = std::min(a.size(), b.size()); |
+ a.reserve(out_size); |
+ |
+ for(unsigned i = 0; i < min_size; i++) |
+ a[i] += b[i]; |
+ for(unsigned i = min_size; i < b.size(); i++) |
+ a.push_back(b[i]); |
+ |
+ assert(a.size() == out_size); |
+ return a; |
+} |
+ |
+SBasis& operator-=(SBasis& a, const SBasis& b) { |
+ const unsigned out_size = std::max(a.size(), b.size()); |
+ const unsigned min_size = std::min(a.size(), b.size()); |
+ a.reserve(out_size); |
+ |
+ for(unsigned i = 0; i < min_size; i++) |
+ a[i] -= b[i]; |
+ for(unsigned i = min_size; i < b.size(); i++) |
+ a.push_back(-b[i]); |
+ |
+ assert(a.size() == out_size); |
+ return a; |
+} |
+ |
+SBasis operator*(SBasis const &a, double k) { |
+ SBasis c; |
+ c.reserve(a.size()); |
+ for(unsigned i = 0; i < a.size(); i++) |
+ c.push_back(a[i] * k); |
+ return c; |
+} |
+ |
+SBasis& operator*=(SBasis& a, double b) { |
+ if (a.isZero()) return a; |
+ if (b == 0) |
+ a.clear(); |
+ else |
+ for(unsigned i = 0; i < a.size(); i++) |
+ a[i] *= b; |
+ return a; |
+} |
+ |
+SBasis shift(SBasis const &a, int sh) { |
+ SBasis c = a; |
+ if(sh > 0) { |
+ c.insert(c.begin(), sh, Linear(0,0)); |
+ } else { |
+ //TODO: truncate |
+ } |
+ return c; |
+} |
+ |
+SBasis shift(Linear const &a, int sh) { |
+ SBasis c; |
+ if(sh > 0) { |
+ c.insert(c.begin(), sh, Linear(0,0)); |
+ c.push_back(a); |
+ } |
+ return c; |
+} |
+ |
+SBasis multiply(SBasis const &a, SBasis const &b) { |
+ // c = {a0*b0 - shift(1, a.Tri*b.Tri), a1*b1 - shift(1, a.Tri*b.Tri)} |
+ |
+ // shift(1, a.Tri*b.Tri) |
+ SBasis c; |
+ if(a.isZero() || b.isZero()) |
+ return c; |
+ c.resize(a.size() + b.size(), Linear(0,0)); |
+ c[0] = Linear(0,0); |
+ for(unsigned j = 0; j < b.size(); j++) { |
+ for(unsigned i = j; i < a.size()+j; i++) { |
+ double tri = Tri(b[j])*Tri(a[i-j]); |
+ c[i+1/*shift*/] += Linear(Hat(-tri)); |
+ } |
+ } |
+ for(unsigned j = 0; j < b.size(); j++) { |
+ for(unsigned i = j; i < a.size()+j; i++) { |
+ for(unsigned dim = 0; dim < 2; dim++) |
+ c[i][dim] += b[j][dim]*a[i-j][dim]; |
+ } |
+ } |
+ c.normalize(); |
+ //assert(!(0 == c.back()[0] && 0 == c.back()[1])); |
+ return c; |
+} |
+ |
+SBasis integral(SBasis const &c) { |
+ SBasis a; |
+ a.resize(c.size() + 1, Linear(0,0)); |
+ a[0] = Linear(0,0); |
+ |
+ for(unsigned k = 1; k < c.size() + 1; k++) { |
+ double ahat = -Tri(c[k-1])/(2*k); |
+ a[k] = Hat(ahat); |
+ } |
+ double aTri = 0; |
+ for(int k = c.size()-1; k >= 0; k--) { |
+ aTri = (Hat(c[k]).d + (k+1)*aTri/2)/(2*k+1); |
+ a[k][0] -= aTri/2; |
+ a[k][1] += aTri/2; |
+ } |
+ a.normalize(); |
+ return a; |
+} |
+ |
+SBasis derivative(SBasis const &a) { |
+ SBasis c; |
+ c.resize(a.size(), Linear(0,0)); |
+ |
+ for(unsigned k = 0; k < a.size(); k++) { |
+ double d = (2*k+1)*Tri(a[k]); |
+ |
+ for(unsigned dim = 0; dim < 2; dim++) { |
+ c[k][dim] = d; |
+ if(k+1 < a.size()) { |
+ if(dim) |
+ c[k][dim] = d - (k+1)*a[k+1][dim]; |
+ else |
+ c[k][dim] = d + (k+1)*a[k+1][dim]; |
+ } |
+ } |
+ } |
+ |
+ return c; |
+} |
+ |
+//TODO: convert int k to unsigned k, and remove cast |
+SBasis sqrt(SBasis const &a, int k) { |
+ SBasis c; |
+ if(a.isZero() || k == 0) |
+ return c; |
+ c.resize(k, Linear(0,0)); |
+ c[0] = Linear(std::sqrt(a[0][0]), std::sqrt(a[0][1])); |
+ SBasis r = a - multiply(c, c); // remainder |
+ |
+ for(unsigned i = 1; i <= (unsigned)k && i<r.size(); i++) { |
+ Linear ci(r[i][0]/(2*c[0][0]), r[i][1]/(2*c[0][1])); |
+ SBasis cisi = shift(ci, i); |
+ r -= multiply(shift((c*2 + cisi), i), SBasis(ci)); |
+ r.truncate(k+1); |
+ c += cisi; |
+ if(r.tailError(i) == 0) // if exact |
+ break; |
+ } |
+ |
+ return c; |
+} |
+ |
+// return a kth order approx to 1/a) |
+SBasis reciprocal(Linear const &a, int k) { |
+ SBasis c; |
+ assert(!a.isZero()); |
+ c.resize(k, Linear(0,0)); |
+ double r_s0 = (Tri(a)*Tri(a))/(-a[0]*a[1]); |
+ double r_s0k = 1; |
+ for(unsigned i = 0; i < (unsigned)k; i++) { |
+ c[i] = Linear(r_s0k/a[0], r_s0k/a[1]); |
+ r_s0k *= r_s0; |
+ } |
+ return c; |
+} |
+ |
+SBasis divide(SBasis const &a, SBasis const &b, int k) { |
+ SBasis c; |
+ assert(!a.isZero()); |
+ SBasis r = a; // remainder |
+ |
+ k++; |
+ r.resize(k, Linear(0,0)); |
+ c.resize(k, Linear(0,0)); |
+ |
+ for(unsigned i = 0; i < (unsigned)k; i++) { |
+ Linear ci(r[i][0]/b[0][0], r[i][1]/b[0][1]); //H0 |
+ c[i] += ci; |
+ r -= shift(multiply(ci,b), i); |
+ r.truncate(k+1); |
+ if(r.tailError(i) == 0) // if exact |
+ break; |
+ } |
+ |
+ return c; |
+} |
+ |
+// a(b) |
+// return a0 + s(a1 + s(a2 +... where s = (1-u)u; ak =(1 - u)a^0_k + ua^1_k |
+SBasis compose(SBasis const &a, SBasis const &b) { |
+ SBasis s = multiply((SBasis(Linear(1,1))-b), b); |
+ SBasis r; |
+ |
+ for(int i = a.size()-1; i >= 0; i--) { |
+ r = SBasis(Linear(Hat(a[i][0]))) - b*a[i][0] + b*a[i][1] + multiply(r,s); |
+ } |
+ return r; |
+} |
+ |
+// a(b) |
+// return a0 + s(a1 + s(a2 +... where s = (1-u)u; ak =(1 - u)a^0_k + ua^1_k |
+SBasis compose(SBasis const &a, SBasis const &b, unsigned k) { |
+ SBasis s = multiply((SBasis(Linear(1,1))-b), b); |
+ SBasis r; |
+ |
+ for(int i = a.size()-1; i >= 0; i--) { |
+ r = SBasis(Linear(Hat(a[i][0]))) - b*a[i][0] + b*a[i][1] + multiply(r,s); |
+ } |
+ r.truncate(k); |
+ return r; |
+} |
+ |
+/* |
+Inversion algorithm. The notation is certainly very misleading. The |
+pseudocode should say: |
+ |
+c(v) := 0 |
+r(u) := r_0(u) := u |
+for i:=0 to k do |
+ c_i(v) := H_0(r_i(u)/(t_1)^i; u) |
+ c(v) := c(v) + c_i(v)*t^i |
+ r(u) := r(u) ? c_i(u)*(t(u))^i |
+endfor |
+*/ |
+ |
+//#define DEBUG_INVERSION 1 |
+ |
+SBasis inverse(SBasis a, int k) { |
+ assert(a.size() > 0); |
+// the function should have 'unit range'("a00 = 0 and a01 = 1") and be monotonic. |
+ double a0 = a[0][0]; |
+ if(a0 != 0) { |
+ a -= a0; |
+ } |
+ double a1 = a[0][1]; |
+ assert(a1 != 0); // not invertable. |
+ |
+ if(a1 != 1) { |
+ a /= a1; |
+ } |
+ SBasis c; // c(v) := 0 |
+ if(a.size() >= 2 && k == 2) { |
+ c.push_back(Linear(0,1)); |
+ Linear t1(1+a[1][0], 1-a[1][1]); // t_1 |
+ c.push_back(Linear(-a[1][0]/t1[0], -a[1][1]/t1[1])); |
+ } else if(a.size() >= 2) { // non linear |
+ SBasis r = Linear(0,1); // r(u) := r_0(u) := u |
+ Linear t1(1./(1+a[1][0]), 1./(1-a[1][1])); // 1./t_1 |
+ Linear one(1,1); |
+ Linear t1i = one; // t_1^0 |
+ SBasis one_minus_a = SBasis(one) - a; |
+ SBasis t = multiply(one_minus_a, a); // t(u) |
+ SBasis ti(one); // t(u)^0 |
+#ifdef DEBUG_INVERSION |
+ std::cout << "a=" << a << std::endl; |
+ std::cout << "1-a=" << one_minus_a << std::endl; |
+ std::cout << "t1=" << t1 << std::endl; |
+ //assert(t1 == t[1]); |
+#endif |
+ |
+ c.resize(k+1, Linear(0,0)); |
+ for(unsigned i = 0; i < (unsigned)k; i++) { // for i:=0 to k do |
+#ifdef DEBUG_INVERSION |
+ std::cout << "-------" << i << ": ---------" <<std::endl; |
+ std::cout << "r=" << r << std::endl |
+ << "c=" << c << std::endl |
+ << "ti=" << ti << std::endl |
+ << std::endl; |
+#endif |
+ if(r.size() <= i) // ensure enough space in the remainder, probably not needed |
+ r.resize(i+1, Linear(0,0)); |
+ Linear ci(r[i][0]*t1i[0], r[i][1]*t1i[1]); // c_i(v) := H_0(r_i(u)/(t_1)^i; u) |
+#ifdef DEBUG_INVERSION |
+ std::cout << "t1i=" << t1i << std::endl; |
+ std::cout << "ci=" << ci << std::endl; |
+#endif |
+ for(int dim = 0; dim < 2; dim++) // t1^-i *= 1./t1 |
+ t1i[dim] *= t1[dim]; |
+ c[i] = ci; // c(v) := c(v) + c_i(v)*t^i |
+ // change from v to u parameterisation |
+ SBasis civ = one_minus_a*ci[0] + a*ci[1]; |
+ // r(u) := r(u) - c_i(u)*(t(u))^i |
+ // We can truncate this to the number of final terms, as no following terms can |
+ // contribute to the result. |
+ r -= multiply(civ,ti); |
+ r.truncate(k); |
+ if(r.tailError(i) == 0) |
+ break; // yay! |
+ ti = multiply(ti,t); |
+ } |
+#ifdef DEBUG_INVERSION |
+ std::cout << "##########################" << std::endl; |
+#endif |
+ } else |
+ c = Linear(0,1); // linear |
+ c -= a0; // invert the offset |
+ c /= a1; // invert the slope |
+ return c; |
+} |
+ |
+SBasis sin(Linear b, int k) { |
+ SBasis s = Linear(std::sin(b[0]), std::sin(b[1])); |
+ Tri tr(s[0]); |
+ double t2 = Tri(b); |
+ s.push_back(Linear(std::cos(b[0])*t2 - tr, -std::cos(b[1])*t2 + tr)); |
+ |
+ t2 *= t2; |
+ for(int i = 0; i < k; i++) { |
+ Linear bo(4*(i+1)*s[i+1][0] - 2*s[i+1][1], |
+ -2*s[i+1][0] + 4*(i+1)*s[i+1][1]); |
+ bo -= s[i]*(t2/(i+1)); |
+ |
+ |
+ s.push_back(bo/double(i+2)); |
+ } |
+ |
+ return s; |
+} |
+ |
+SBasis cos(Linear bo, int k) { |
+ return sin(Linear(bo[0] + M_PI_2, |
+ bo[1] + M_PI_2), |
+ k); |
+} |
+ |
+//compute fog^-1. ("zero" = double comparison threshold. *!*we might divide by "zero"*!*) |
+//TODO: compute order according to tol? |
+//TODO: requires g(0)=0 & g(1)=1 atm... adaptation to other cases should be obvious! |
+SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order, double zero){ |
+ SBasis result; //result |
+ SBasis r=f; //remainder |
+ SBasis Pk=Linear(1)-g,Qk=g,sg=Pk*Qk; |
+ Pk.truncate(order); |
+ Qk.truncate(order); |
+ Pk.resize(order,Linear(0.)); |
+ Qk.resize(order,Linear(0.)); |
+ r.resize(order,Linear(0.)); |
+ |
+ int vs= valuation(sg,zero); |
+ |
+ for (unsigned k=0; k<order; k+=vs){ |
+ double p10 = Pk.at(k)[0];// we have to solve the linear system: |
+ double p01 = Pk.at(k)[1];// |
+ double q10 = Qk.at(k)[0];// p10*a + q10*b = r10 |
+ double q01 = Qk.at(k)[1];// & |
+ double r10 = r.at(k)[0];// p01*a + q01*b = r01 |
+ double r01 = r.at(k)[1];// |
+ double a,b; |
+ double det = p10*q01-p01*q10; |
+ |
+ //TODO: handle det~0!! |
+ if (fabs(det)<zero){ |
+ det = zero; |
+ a=b=0; |
+ }else{ |
+ a=( q01*r10-q10*r01)/det; |
+ b=(-p01*r10+p10*r01)/det; |
+ } |
+ result.push_back(Linear(a,b)); |
+ r=r-Pk*a-Qk*b; |
+ |
+ Pk=Pk*sg; |
+ Qk=Qk*sg; |
+ Pk.truncate(order); |
+ Qk.truncate(order); |
+ r.truncate(order); |
+ } |
+ result.normalize(); |
+ return result; |
+} |
+ |
+} |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/angle.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/angle.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/angle.h (revision 14937) |
@@ -0,0 +1,53 @@ |
+/** |
+ * \file angle.h |
+ * \brief Various trigoniometric helper functions |
+ * |
+ * Authors: |
+ * Johan Engelen <goejendaagh@zonnet.nl> |
+ * |
+ * Copyright (C) 2007 authors |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#ifndef LIB2GEOM_SEEN_ANGLE_H |
+#define LIB2GEOM_SEEN_ANGLE_H |
+ |
+namespace Geom { |
+ |
+#ifndef M_PI |
+# define M_PI 3.14159265358979323846 |
+#endif |
+#ifndef M_PI_2 |
+# define M_PI_2 ( M_PI / 2) |
+#endif |
+ |
+inline double deg_to_rad(double deg) { return deg*M_PI/180.0;} |
+ |
+inline double rad_to_deg(double rad) { return rad*180.0/M_PI;} |
+ |
+} |
+ |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/solve-bezier-parametric.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/solve-bezier-parametric.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/solve-bezier-parametric.cpp (revision 14937) |
@@ -0,0 +1,232 @@ |
+#include "solver.h" |
+#include "point.h" |
+#include <algorithm> |
+ |
+namespace Geom{ |
+ |
+/*** Find the zeros of the parametric function in 2d defined by two beziers X(t), Y(t). The code subdivides until it happy with the linearity of the bezier. This requires an n^2 subdivision for each step, even when there is only one solution. |
+ * |
+ * Perhaps it would be better to subdivide particularly around nodes with changing sign, rather than simply cutting in half. |
+ */ |
+ |
+#define SGN(a) (((a)<0) ? -1 : 1) |
+ |
+/* |
+ * Forward declarations |
+ */ |
+static Geom::Point |
+Bezier(Geom::Point const *V, |
+ unsigned degree, |
+ double t, |
+ Geom::Point *Left, |
+ Geom::Point *Right); |
+ |
+unsigned |
+crossing_count(Geom::Point const *V, unsigned degree); |
+static unsigned |
+control_poly_flat_enough(Geom::Point const *V, unsigned degree); |
+static double |
+compute_x_intercept(Geom::Point const *V, unsigned degree); |
+ |
+const unsigned MAXDEPTH = 64; /* Maximum depth for recursion */ |
+ |
+const double BEPSILON = ldexp(1.0,((signed)-1)-MAXDEPTH); /*Flatness control value */ |
+ |
+unsigned total_steps, total_subs; |
+ |
+/* |
+ * find_bezier_roots : Given an equation in Bernstein-Bezier form, find all |
+ * of the roots in the interval [0, 1]. Return the number of roots found. |
+ */ |
+void |
+find_parametric_bezier_roots(Geom::Point const *w, /* The control points */ |
+ unsigned degree, /* The degree of the polynomial */ |
+ std::vector<double> &solutions, /* RETURN candidate t-values */ |
+ unsigned depth) /* The depth of the recursion */ |
+{ |
+ total_steps++; |
+ const unsigned max_crossings = crossing_count(w, degree); |
+ switch (max_crossings) { |
+ case 0: /* No solutions here */ |
+ return; |
+ |
+ case 1: |
+ /* Unique solution */ |
+ /* Stop recursion when the tree is deep enough */ |
+ /* if deep enough, return 1 solution at midpoint */ |
+ if (depth >= MAXDEPTH) { |
+ solutions.push_back((w[0][Geom::X] + w[degree][Geom::X]) / 2.0); |
+ return; |
+ } |
+ |
+ // I thought secant method would be faster here, but it'aint. -- njh |
+ |
+ if (control_poly_flat_enough(w, degree)) { |
+ solutions.push_back(compute_x_intercept(w, degree)); |
+ return; |
+ } |
+ break; |
+ } |
+ |
+ /* Otherwise, solve recursively after subdividing control polygon */ |
+ std::vector<Geom::Point>Left(degree+1); /* New left and right */ |
+ std::vector<Geom::Point>Right(degree+1); /* control polygons */ |
+ Bezier(w, degree, 0.5, &Left[0], &Right[0]); |
+ total_subs ++; |
+ find_parametric_bezier_roots(&Left[0], degree, solutions, depth+1); |
+ find_parametric_bezier_roots(&Right[0], degree, solutions, depth+1); |
+} |
+ |
+ |
+/* |
+ * crossing_count: |
+ * Count the number of times a Bezier control polygon |
+ * crosses the 0-axis. This number is >= the number of roots. |
+ * |
+ */ |
+unsigned |
+crossing_count(Geom::Point const *V, /* Control pts of Bezier curve */ |
+ unsigned degree) /* Degree of Bezier curve */ |
+{ |
+ unsigned n_crossings = 0; /* Number of zero-crossings */ |
+ |
+ int old_sign = SGN(V[0][Geom::Y]); |
+ for (unsigned i = 1; i <= degree; i++) { |
+ int sign = SGN(V[i][Geom::Y]); |
+ if (sign != old_sign) |
+ n_crossings++; |
+ old_sign = sign; |
+ } |
+ return n_crossings; |
+} |
+ |
+ |
+ |
+/* |
+ * control_poly_flat_enough : |
+ * Check if the control polygon of a Bezier curve is flat enough |
+ * for recursive subdivision to bottom out. |
+ * |
+ */ |
+static unsigned |
+control_poly_flat_enough(Geom::Point const *V, /* Control points */ |
+ unsigned degree) /* Degree of polynomial */ |
+{ |
+ /* Find the perpendicular distance from each interior control point to line connecting V[0] and |
+ * V[degree] */ |
+ |
+ /* Derive the implicit equation for line connecting first */ |
+ /* and last control points */ |
+ const double a = V[0][Geom::Y] - V[degree][Geom::Y]; |
+ const double b = V[degree][Geom::X] - V[0][Geom::X]; |
+ const double c = V[0][Geom::X] * V[degree][Geom::Y] - V[degree][Geom::X] * V[0][Geom::Y]; |
+ |
+ const double abSquared = (a * a) + (b * b); |
+ |
+ std::vector<double> distance(degree); /* Distances from pts to line */ |
+ for (unsigned i = 1; i < degree; i++) { |
+ /* Compute distance from each of the points to that line */ |
+ double & dist(distance[i-1]); |
+ const double d = a * V[i][Geom::X] + b * V[i][Geom::Y] + c; |
+ dist = d*d / abSquared; |
+ if (d < 0.0) |
+ dist = -dist; |
+ } |
+ |
+ |
+ // Find the largest distance |
+ double max_distance_above = 0.0; |
+ double max_distance_below = 0.0; |
+ for (unsigned i = 0; i < degree-1; i++) { |
+ const double d = distance[i]; |
+ if (d < 0.0) |
+ max_distance_below = std::min(max_distance_below, d); |
+ if (d > 0.0) |
+ max_distance_above = std::max(max_distance_above, d); |
+ } |
+ |
+ const double intercept_1 = (c + max_distance_above) / -a; |
+ const double intercept_2 = (c + max_distance_below) / -a; |
+ |
+ /* Compute bounding interval*/ |
+ const double left_intercept = std::min(intercept_1, intercept_2); |
+ const double right_intercept = std::max(intercept_1, intercept_2); |
+ |
+ const double error = 0.5 * (right_intercept - left_intercept); |
+ |
+ if (error < BEPSILON) |
+ return 1; |
+ |
+ return 0; |
+} |
+ |
+ |
+ |
+/* |
+ * compute_x_intercept : |
+ * Compute intersection of chord from first control point to last |
+ * with 0-axis. |
+ * |
+ */ |
+static double |
+compute_x_intercept(Geom::Point const *V, /* Control points */ |
+ unsigned degree) /* Degree of curve */ |
+{ |
+ const Geom::Point A = V[degree] - V[0]; |
+ |
+ return (A[Geom::X]*V[0][Geom::Y] - A[Geom::Y]*V[0][Geom::X]) / -A[Geom::Y]; |
+} |
+ |
+ |
+/* |
+ * Bezier : |
+ * Evaluate a Bezier curve at a particular parameter value |
+ * Fill in control points for resulting sub-curves. |
+ * |
+ */ |
+static Geom::Point |
+Bezier(Geom::Point const *V, /* Control pts */ |
+ unsigned degree, /* Degree of bezier curve */ |
+ double t, /* Parameter value */ |
+ Geom::Point *Left, /* RETURN left half ctl pts */ |
+ Geom::Point *Right) /* RETURN right half ctl pts */ |
+{ |
+const unsigned size=degree+1; |
+ std::vector<Geom::Point> vtemp(V,V+size); |
+ |
+ //storing left/right coordinates |
+ std::vector<Geom::Point> nodata(size); |
+ if(Left == NULL)Left=&nodata[0]; |
+ if(Right == NULL)Right=&nodata[0]; |
+ |
+ /* Copy control points */ |
+ Left[0] = vtemp[0]; |
+ Right[degree]= vtemp[degree]; |
+ |
+ /* Triangle computation */ |
+ for (unsigned i = 1; i < size; ++i) { |
+ for (unsigned j = 0; j < size - i; ++j) { |
+ vtemp[j] = lerp(t, vtemp[j], vtemp[j+1]); |
+ } |
+ Left[i] =vtemp[0]; |
+ Right[degree-i]=vtemp[degree-i]; |
+ } |
+ |
+ return (vtemp[0]); |
+} |
+ |
+}; |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(substatement-open . 0)) |
+ indent-tabs-mode:nil |
+ c-brace-offset:0 |
+ fill-column:99 |
+ End: |
+ vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
+*/ |
+ |
+ |
Index: trunk/Scribus/scribus/third_party/lib2geom/solve-bezier-one-d.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/solve-bezier-one-d.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/solve-bezier-one-d.cpp (revision 14937) |
@@ -0,0 +1,198 @@ |
+#include "solver.h" |
+#include "point.h" |
+#include <algorithm> |
+ |
+/*** Find the zeros of the bernstein function. The code subdivides until it is happy with the |
+ * linearity of the function. This requires an O(degree^2) subdivision for each step, even when |
+ * there is only one solution. |
+ */ |
+ |
+namespace Geom{ |
+ |
+template<class t> |
+static int SGN(t x) { return (x > 0 ? 1 : (x < 0 ? -1 : 0)); } |
+ |
+/* |
+ * Forward declarations |
+ */ |
+static void |
+Bernstein(double const *V, |
+ unsigned degree, |
+ double t, |
+ double *Left, |
+ double *Right); |
+ |
+static unsigned |
+control_poly_flat_enough(double const *V, unsigned degree, |
+ double left_t, double right_t); |
+ |
+const unsigned MAXDEPTH = 64; /* Maximum depth for recursion */ |
+ |
+const double BEPSILON = ldexp(1.0,((signed)-1)-MAXDEPTH); /*Flatness control value */ |
+ |
+/* |
+ * find_bernstein_roots : Given an equation in Bernstein-Bernstein form, find all |
+ * of the roots in the open interval (0, 1). Return the number of roots found. |
+ */ |
+void |
+find_bernstein_roots(double const *w, /* The control points */ |
+ unsigned degree, /* The degree of the polynomial */ |
+ std::vector<double> &solutions, /* RETURN candidate t-values */ |
+ unsigned depth, /* The depth of the recursion */ |
+ double left_t, double right_t) |
+{ |
+ unsigned n_crossings = 0; /* Number of zero-crossings */ |
+ |
+ int old_sign = SGN(w[0]); |
+ for (unsigned i = 1; i <= degree; i++) { |
+ int sign = SGN(w[i]); |
+ if (sign) { |
+ if (sign != old_sign && old_sign) { |
+ n_crossings++; |
+ } |
+ old_sign = sign; |
+ } |
+ } |
+ |
+ switch (n_crossings) { |
+ case 0: /* No solutions here */ |
+ return; |
+ |
+ case 1: |
+ /* Unique solution */ |
+ /* Stop recursion when the tree is deep enough */ |
+ /* if deep enough, return 1 solution at midpoint */ |
+ if (depth >= MAXDEPTH) { |
+ solutions.push_back((left_t + right_t) / 2.0); |
+ return; |
+ } |
+ |
+ // I thought secant method would be faster here, but it'aint. -- njh |
+ |
+ if (control_poly_flat_enough(w, degree, left_t, right_t)) { |
+ const double Ax = right_t - left_t; |
+ const double Ay = w[degree] - w[0]; |
+ |
+ solutions.push_back(left_t - Ax*w[0] / Ay); |
+ return; |
+ } |
+ break; |
+ } |
+ |
+ /* Otherwise, solve recursively after subdividing control polygon */ |
+ std::vector<double> Left(degree+1); /* New left and right */ |
+ std::vector<double> Right(degree+1);/* control polygons */ |
+ const double split = 0.5; |
+ Bernstein(w, degree, split, &Left[0], &Right[0]); |
+ |
+ double mid_t = left_t*(1-split) + right_t*split; |
+ |
+ find_bernstein_roots(&Left[0], degree, solutions, depth+1, left_t, mid_t); |
+ |
+ /* Solution is exactly on the subdivision point. */ |
+ if (Right[0] == 0) |
+ solutions.push_back(mid_t); |
+ |
+ find_bernstein_roots(&Right[0], degree, solutions, depth+1, mid_t, right_t); |
+} |
+ |
+/* |
+ * control_poly_flat_enough : |
+ * Check if the control polygon of a Bernstein curve is flat enough |
+ * for recursive subdivision to bottom out. |
+ * |
+ */ |
+static unsigned |
+control_poly_flat_enough(double const *V, /* Control points */ |
+ unsigned degree, |
+ double left_t, double right_t) /* Degree of polynomial */ |
+{ |
+ /* Find the perpendicular distance from each interior control point to line connecting V[0] and |
+ * V[degree] */ |
+ |
+ /* Derive the implicit equation for line connecting first */ |
+ /* and last control points */ |
+ const double a = V[0] - V[degree]; |
+ const double b = right_t - left_t; |
+ const double c = left_t * V[degree] - right_t * V[0] + a * left_t; |
+ |
+ double max_distance_above = 0.0; |
+ double max_distance_below = 0.0; |
+ double ii = 0, dii = 1./degree; |
+ for (unsigned i = 1; i < degree; i++) { |
+ ii += dii; |
+ /* Compute distance from each of the points to that line */ |
+ const double d = (a + V[i]) * ii*b + c; |
+ double dist = d*d; |
+ // Find the largest distance |
+ if (d < 0.0) |
+ max_distance_below = std::min(max_distance_below, -dist); |
+ else |
+ max_distance_above = std::max(max_distance_above, dist); |
+ } |
+ |
+ const double abSquared = (a * a) + (b * b); |
+ |
+ const double intercept_1 = -(c + max_distance_above / abSquared); |
+ const double intercept_2 = -(c + max_distance_below / abSquared); |
+ |
+ /* Compute bounding interval*/ |
+ const double left_intercept = std::min(intercept_1, intercept_2); |
+ const double right_intercept = std::max(intercept_1, intercept_2); |
+ |
+ const double error = 0.5 * (right_intercept - left_intercept); |
+ |
+ if (error < BEPSILON * a) |
+ return 1; |
+ |
+ return 0; |
+} |
+ |
+ |
+ |
+/* |
+ * Bernstein : |
+ * Evaluate a Bernstein function at a particular parameter value |
+ * Fill in control points for resulting sub-curves. |
+ * |
+ */ |
+static void |
+Bernstein(double const *V, /* Control pts */ |
+ unsigned degree, /* Degree of bernstein curve */ |
+ double t, /* Parameter value */ |
+ double *Left, /* RETURN left half ctl pts */ |
+ double *Right) /* RETURN right half ctl pts */ |
+{ |
+ const unsigned size=degree+1; |
+ std::vector<double> vtemp(V,V+size); |
+ |
+ /* Copy control points */ |
+ Left[0] = vtemp[0]; |
+ Right[degree]= vtemp[degree]; |
+ |
+ /* Triangle computation */ |
+ const double omt = (1-t); |
+ for (unsigned i = 1; i < size; ++i) { |
+ for (unsigned j = 0; j < size - i; ++j) { |
+ vtemp[j] = omt*vtemp[j]+t*vtemp[j+1]; |
+ } |
+ Left[i] =vtemp[0]; |
+ Right[degree-i]=vtemp[degree-i]; |
+ } |
+} |
+ |
+}; |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(substatement-open . 0)) |
+ indent-tabs-mode:nil |
+ c-brace-offset:0 |
+ fill-column:99 |
+ End: |
+ vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
+*/ |
+ |
+ |
Index: trunk/Scribus/scribus/third_party/lib2geom/point.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/point.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/point.cpp (revision 14937) |
@@ -0,0 +1,166 @@ |
+#include "point.h" |
+#include <assert.h> |
+#include "coord.h" |
+#include "isnan.h" //temporary fix for isnan() |
+#include "matrix.h" |
+#include <limits> |
+ |
+ |
+namespace Geom { |
+ |
+/** Scales this vector to make it a unit vector (within rounding error). |
+ * |
+ * The current version tries to handle infinite coordinates gracefully, |
+ * but it's not clear that any callers need that. |
+ * |
+ * \pre \f$this \neq (0, 0)\f$ |
+ * \pre Neither component is NaN. |
+ * \post \f$-\epsilon<\left|this\right|-1<\epsilon\f$ |
+ */ |
+void Point::normalize() { |
+ double len = hypot(_pt[0], _pt[1]); |
+ if(len == 0) return; |
+ if(is_nan(len)) return; |
+ static double const inf = std::numeric_limits<double>::infinity(); |
+ if(len != inf) { |
+ *this /= len; |
+ } else { |
+ unsigned n_inf_coords = 0; |
+ /* Delay updating pt in case neither coord is infinite. */ |
+ Point tmp; |
+ for ( unsigned i = 0 ; i < 2 ; ++i ) { |
+ if ( _pt[i] == inf ) { |
+ ++n_inf_coords; |
+ tmp[i] = 1.0; |
+ } else if ( _pt[i] == -inf ) { |
+ ++n_inf_coords; |
+ tmp[i] = -1.0; |
+ } else { |
+ tmp[i] = 0.0; |
+ } |
+ } |
+ switch (n_inf_coords) { |
+ case 0: { |
+ /* Can happen if both coords are near +/-DBL_MAX. */ |
+ *this /= 4.0; |
+ len = hypot(_pt[0], _pt[1]); |
+ assert(len != inf); |
+ *this /= len; |
+ break; |
+ } |
+ case 1: { |
+ *this = tmp; |
+ break; |
+ } |
+ case 2: { |
+ *this = tmp * sqrt(0.5); |
+ break; |
+ } |
+ } |
+ } |
+} |
+ |
+/** Compute the L1 norm, or manhattan distance, of \a p. */ |
+Coord L1(Point const &p) { |
+ Coord d = 0; |
+ for ( int i = 0 ; i < 2 ; i++ ) { |
+ d += fabs(p[i]); |
+ } |
+ return d; |
+} |
+ |
+/** Compute the L infinity, or maximum, norm of \a p. */ |
+Coord LInfty(Point const &p) { |
+ Coord const a(fabs(p[0])); |
+ Coord const b(fabs(p[1])); |
+ return ( a < b || is_nan(b) |
+ ? b |
+ : a ); |
+} |
+ |
+/** Returns true iff p is a zero vector, i.e.\ Point(0, 0). |
+ * |
+ * (NaN is considered non-zero.) |
+ */ |
+bool |
+is_zero(Point const &p) |
+{ |
+ return ( p[0] == 0 && |
+ p[1] == 0 ); |
+} |
+ |
+bool |
+is_unit_vector(Point const &p) |
+{ |
+ return fabs(1.0 - L2(p)) <= 1e-4; |
+ /* The tolerance of 1e-4 is somewhat arbitrary. Point::normalize is believed to return |
+ points well within this tolerance. I'm not aware of any callers that want a small |
+ tolerance; most callers would be ok with a tolerance of 0.25. */ |
+} |
+ |
+Coord atan2(Point const p) { |
+ return std::atan2(p[Y], p[X]); |
+} |
+ |
+/** compute the angle turning from a to b. This should give \f$\pi/2\f$ for angle_between(a, rot90(a)); |
+ * This works by projecting b onto the basis defined by a, rot90(a) |
+ */ |
+Coord angle_between(Point const a, Point const b) { |
+ return std::atan2(cross(b,a), dot(b,a)); |
+} |
+ |
+ |
+ |
+/** Returns a version of \a a scaled to be a unit vector (within rounding error). |
+ * |
+ * The current version tries to handle infinite coordinates gracefully, |
+ * but it's not clear that any callers need that. |
+ * |
+ * \pre a != Point(0, 0). |
+ * \pre Neither coordinate is NaN. |
+ * \post L2(ret) very near 1.0. |
+ */ |
+Point unit_vector(Point const &a) |
+{ |
+ Point ret(a); |
+ ret.normalize(); |
+ return ret; |
+} |
+ |
+Point abs(Point const &b) |
+{ |
+ Point ret; |
+ for ( int i = 0 ; i < 2 ; i++ ) { |
+ ret[i] = fabs(b[i]); |
+ } |
+ return ret; |
+} |
+ |
+Point operator*(Point const &v, Matrix const &m) { |
+ Point ret; |
+ for(int i = 0; i < 2; i++) { |
+ ret[i] = v[X] * m[i] + v[Y] * m[i + 2] + m[i + 4]; |
+ } |
+ return ret; |
+} |
+ |
+Point operator/(Point const &p, Matrix const &m) { return p * m.inverse(); } |
+ |
+Point &Point::operator*=(Matrix const &m) |
+{ |
+ *this = *this * m; |
+ return *this; |
+} |
+ |
+} //Namespace Geom |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/interval.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/interval.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/interval.h (revision 14937) |
@@ -0,0 +1,228 @@ |
+/* |
+ * interval.h - Simple closed interval class |
+ * |
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com> |
+ * |
+ * Original Rect/Range code by: |
+ * Lauris Kaplinski <lauris@kaplinski.com> |
+ * Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * bulia byak <buliabyak@users.sf.net> |
+ * MenTaLguY <mental@rydia.net> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+#ifndef SEEN_INTERVAL_H |
+#define SEEN_INTERVAL_H |
+ |
+#include <assert.h> |
+#include "coord.h" |
+ |
+#include <boost/optional/optional.hpp> |
+ |
+//error in miesh_distortion |
+#ifdef min |
+# undef min |
+#endif |
+ |
+#ifdef max |
+# undef max |
+#endif |
+ |
+namespace Geom { |
+ |
+// |
+class Interval { |
+private: |
+ Coord _b[2]; |
+ |
+public: |
+ //TODO: I just know this'll pop up somewhere, starting off someone's interval at 0... I can't see how to avoid this. |
+ explicit Interval() { _b[0] = _b[1] = 0; } |
+ explicit Interval(Coord u) { _b[0] = _b[1] = u; } |
+ Interval(Coord u, Coord v) { |
+ if(u < v) { |
+ _b[0] = u; _b[1] = v; |
+ } else { |
+ _b[0] = v; _b[1] = u; |
+ } |
+ } |
+ |
+ double operator[](unsigned i) const { |
+ assert(i < 2); |
+ return _b[i]; |
+ } |
+ inline double& operator[](unsigned i) { return _b[i]; } //Trust the user... |
+ |
+ inline Coord min() const { return _b[0]; } |
+ inline Coord max() const { return _b[1]; } |
+ inline Coord extent() const { return _b[1] - _b[0]; } |
+ inline Coord middle() const { return (_b[1] + _b[0]) * 0.5; } |
+ |
+ inline bool isEmpty() const { return _b[0] == _b[1]; } |
+ inline bool contains(Coord val) const { return _b[0] <= val && val <= _b[1]; } |
+ bool contains(const Interval & val) const { return _b[0] <= val._b[0] && val._b[1] <= _b[1]; } |
+ bool intersects(const Interval & val) const { |
+ return contains(val._b[0]) || contains(val._b[1]) || val.contains(*this); |
+ } |
+ |
+ inline bool operator==(Interval other) { return _b[0] == other._b[0] && _b[1] == other._b[1]; } |
+ inline bool operator!=(Interval other) { return _b[0] != other._b[0] || _b[1] != other._b[1]; } |
+ |
+ //IMPL: OffsetableConcept |
+ //TODO: rename output_type to something else in the concept |
+ typedef Coord output_type; |
+ inline Interval operator+(Coord amnt) { |
+ return Interval(_b[0] + amnt, _b[1] + amnt); |
+ } |
+ inline Interval operator-(Coord amnt) { |
+ return Interval(_b[0] - amnt, _b[1] - amnt); |
+ } |
+ inline Interval operator+=(Coord amnt) { |
+ _b[0] += amnt; _b[1] += amnt; |
+ return *this; |
+ } |
+ inline Interval operator-=(Coord amnt) { |
+ _b[0] -= amnt; _b[1] -= amnt; |
+ return *this; |
+ } |
+ |
+ //IMPL: ScalableConcept |
+ inline Interval operator-() const { return Interval(*this); } |
+ inline Interval operator*(Coord s) const { return Interval(_b[0]*s, _b[1]*s); } |
+ inline Interval operator/(Coord s) const { return Interval(_b[0]/s, _b[1]/s); } |
+ Interval operator*=(Coord s) { |
+ if(s < 0) { |
+ Coord temp = _b[0]; |
+ _b[0] = _b[1]*s; |
+ _b[1] = temp*s; |
+ } else { |
+ _b[0] *= s; |
+ _b[1] *= s; |
+ } |
+ return *this; |
+ } |
+ Interval operator/=(Coord s) { |
+ //TODO: what about s=0? |
+ if(s < 0) { |
+ Coord temp = _b[0]; |
+ _b[0] = _b[1]/s; |
+ _b[1] = temp/s; |
+ } else { |
+ _b[0] /= s; |
+ _b[1] /= s; |
+ } |
+ return *this; |
+ } |
+ |
+ //TODO: NaN handleage for the next two? |
+ //TODO: Evaluate if wrap behaviour is proper. |
+ //If val > max, then rather than becoming a min==max range, it 'wraps' over |
+ void setMin(Coord val) { |
+ if(val > _b[1]) { |
+ _b[0] = _b[1]; |
+ _b[1] = val; |
+ } else { |
+ _b[0] = val; |
+ } |
+ } |
+ //If val < min, then rather than becoming a min==max range, it 'wraps' over |
+ void setMax(Coord val) { |
+ if(val < _b[0]) { |
+ _b[1] = _b[0]; |
+ _b[0] = val; |
+ } else { |
+ _b[1] = val; |
+ } |
+ } |
+ |
+ inline void extendTo(Coord val) { |
+ if(val < _b[0]) _b[0] = val; |
+ if(val > _b[1]) _b[1] = val; //no else, as we want to handle NaN |
+ } |
+ |
+ static Interval fromArray(const Coord* c, int n) { |
+ assert(n > 0); |
+ Interval result(c[0]); |
+ for(int i = 1; i < n; i++) result.extendTo(c[i]); |
+ return result; |
+ } |
+ |
+ inline void expandBy(double amnt) { |
+ _b[0] -= amnt; |
+ _b[1] += amnt; |
+ } |
+ |
+ inline void unionWith(const Interval & a) { |
+ if(a._b[0] < _b[0]) _b[0] = a._b[0]; |
+ if(a._b[1] > _b[1]) _b[1] = a._b[1]; |
+ } |
+}; |
+ |
+//IMPL: AddableConcept |
+inline Interval operator+(const Interval & a, const Interval & b) { |
+ return Interval(a.min() + b.min(), a.max() + b.max()); |
+} |
+inline Interval operator-(const Interval & a, const Interval & b) { |
+ return Interval(a.min() - b.max(), a.max() - b.min()); |
+} |
+inline Interval operator+=(Interval & a, const Interval & b) { a = a + b; return a; } |
+inline Interval operator-=(Interval & a, const Interval & b) { a = a - b; return a; } |
+ |
+//There might be impls of this based off sign checks |
+inline Interval operator*(const Interval & a, const Interval & b) { |
+ Interval res(a.min() * b.min()); |
+ res.extendTo(a.min() * b.max()); |
+ res.extendTo(a.max() * b.min()); |
+ res.extendTo(a.max() * b.max()); |
+ return res; |
+} |
+inline Interval operator*=(Interval & a, const Interval & b) { a = a * b; return a; } |
+ |
+/* reinstate if useful (doesn't do the proper thing for 0 inclusion) |
+inline Interval operator/(const Interval & a, const Interval & b) { |
+ Interval res(a.min() / b.min()); |
+ res.extendTo(a.min() / b.max()); |
+ res.extendTo(a.max() / b.min()); |
+ res.extendTo(a.max() / b.max()); |
+ return res; |
+} |
+inline Interval operator/=(Interval & a, const Interval & b) { a = a / b; return a; } |
+*/ |
+ |
+// 'union' conflicts with C keyword |
+inline Interval unify(const Interval & a, const Interval & b) { |
+ return Interval(std::min(a.min(), b.min()), |
+ std::max(a.max(), b.max())); |
+} |
+inline boost::optional<Interval> intersect(const Interval & a, const Interval & b) { |
+ Coord u = std::max(a.min(), b.min()), |
+ v = std::min(a.max(), b.max()); |
+ //technically >= might be incorrect, but singulars suck |
+ return u >= v ? boost::optional<Interval>() |
+ : boost::optional<Interval>(Interval(u, v)); |
+} |
+ |
+} |
+#endif //SEEN_INTERVAL_H |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis.h (revision 14937) |
@@ -0,0 +1,331 @@ |
+/* |
+ * sbasis.h - S-power basis function class |
+ * |
+ * Authors: |
+ * Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * Michael Sloan <mgsloan@gmail.com> |
+ * |
+ * Copyright (C) 2006-2007 authors |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ */ |
+ |
+#ifndef SEEN_SBASIS_H |
+#define SEEN_SBASIS_H |
+#include <vector> |
+#include <cassert> |
+#include <iostream> |
+ |
+#include "linear.h" |
+#include "interval.h" |
+#include "utils.h" |
+#include "exception.h" |
+ |
+namespace Geom { |
+ |
+/*** An empty SBasis is identically 0. */ |
+class SBasis : public std::vector<Linear>{ |
+public: |
+ SBasis() {} |
+ explicit SBasis(double a) { |
+ push_back(Linear(a,a)); |
+ } |
+ SBasis(SBasis const & a) : |
+ std::vector<Linear>(a) |
+ {} |
+ SBasis(Linear const & bo) { |
+ push_back(bo); |
+ } |
+ |
+ //IMPL: FragmentConcept |
+ typedef double output_type; |
+ inline bool isZero() const { |
+ if(empty()) return true; |
+ for(unsigned i = 0; i < size(); i++) { |
+ if(!(*this)[i].isZero()) return false; |
+ } |
+ return true; |
+ } |
+ inline bool isConstant() const { |
+ if (empty()) return true; |
+ for (unsigned i = 0; i < size(); i++) { |
+ if(!(*this)[i].isConstant()) return false; |
+ } |
+ return true; |
+ } |
+ |
+ bool isFinite() const; |
+ inline double at0() const { |
+ if(empty()) return 0; else return (*this)[0][0]; |
+ } |
+ inline double at1() const{ |
+ if(empty()) return 0; else return (*this)[0][1]; |
+ } |
+ |
+ double valueAt(double t) const { |
+ double s = t*(1-t); |
+ double p0 = 0, p1 = 0; |
+ double sk = 1; |
+//TODO: rewrite as horner |
+ for(unsigned k = 0; k < size(); k++) { |
+ p0 += sk*(*this)[k][0]; |
+ p1 += sk*(*this)[k][1]; |
+ sk *= s; |
+ } |
+ return (1-t)*p0 + t*p1; |
+ } |
+ double valueAndDerivative(double t, double &der) const { |
+ double s = t*(1-t); |
+ double p0 = 0, p1 = 0; |
+ double sk = 1; |
+//TODO: rewrite as horner |
+ for(unsigned k = 0; k < size(); k++) { |
+ p0 += sk*(*this)[k][0]; |
+ p1 += sk*(*this)[k][1]; |
+ sk *= s; |
+ } |
+ // p0 and p1 at this point form a linear approximation at t |
+ der = p1 - p0; |
+ return (1-t)*p0 + t*p1; |
+ } |
+ double operator()(double t) const { |
+ return valueAt(t); |
+ } |
+ |
+ std::vector<double> valueAndDerivatives(double /*t*/, unsigned /*n*/) const { |
+ //TODO |
+ throwNotImplemented(0); |
+ } |
+ |
+ SBasis toSBasis() const { return SBasis(*this); } |
+ |
+ double tailError(unsigned tail) const; |
+ |
+// compute f(g) |
+ SBasis operator()(SBasis const & g) const; |
+ |
+ Linear operator[](unsigned i) const { |
+ assert(i < size()); |
+ return std::vector<Linear>::operator[](i); |
+ } |
+ |
+//MUTATOR PRISON |
+ Linear& operator[](unsigned i) { return this->at(i); } |
+ |
+ //remove extra zeros |
+ void normalize() { |
+ while(!empty() && 0 == back()[0] && 0 == back()[1]) |
+ pop_back(); |
+ } |
+ void truncate(unsigned k) { if(k < size()) resize(k); } |
+}; |
+ |
+//TODO: figure out how to stick this in linear, while not adding an sbasis dep |
+inline SBasis Linear::toSBasis() const { return SBasis(*this); } |
+ |
+//implemented in sbasis-roots.cpp |
+Interval bounds_exact(SBasis const &a); |
+Interval bounds_fast(SBasis const &a, int order = 0); |
+Interval bounds_local(SBasis const &a, const Interval &t, int order = 0); |
+ |
+inline SBasis reverse(SBasis const &a) { |
+ SBasis result; |
+ result.reserve(a.size()); |
+ for(unsigned k = 0; k < a.size(); k++) |
+ result.push_back(reverse(a[k])); |
+ return result; |
+} |
+ |
+//IMPL: ScalableConcept |
+inline SBasis operator-(const SBasis& p) { |
+ if(p.isZero()) return SBasis(); |
+ SBasis result; |
+ result.reserve(p.size()); |
+ |
+ for(unsigned i = 0; i < p.size(); i++) { |
+ result.push_back(-p[i]); |
+ } |
+ return result; |
+} |
+SBasis operator*(SBasis const &a, double k); |
+inline SBasis operator*(double k, SBasis const &a) { return a*k; } |
+inline SBasis operator/(SBasis const &a, double k) { return a*(1./k); } |
+SBasis& operator*=(SBasis& a, double b); |
+inline SBasis& operator/=(SBasis& a, double b) { return (a*=(1./b)); } |
+ |
+//IMPL: AddableConcept |
+SBasis operator+(const SBasis& a, const SBasis& b); |
+SBasis operator-(const SBasis& a, const SBasis& b); |
+SBasis& operator+=(SBasis& a, const SBasis& b); |
+SBasis& operator-=(SBasis& a, const SBasis& b); |
+ |
+//TODO: remove? |
+inline SBasis operator+(const SBasis & a, Linear const & b) { |
+ if(b.isZero()) return a; |
+ if(a.isZero()) return b; |
+ SBasis result(a); |
+ result[0] += b; |
+ return result; |
+} |
+inline SBasis operator-(const SBasis & a, Linear const & b) { |
+ if(b.isZero()) return a; |
+ SBasis result(a); |
+ result[0] -= b; |
+ return result; |
+} |
+inline SBasis& operator+=(SBasis& a, const Linear& b) { |
+ if(a.isZero()) |
+ a.push_back(b); |
+ else |
+ a[0] += b; |
+ return a; |
+} |
+inline SBasis& operator-=(SBasis& a, const Linear& b) { |
+ if(a.isZero()) |
+ a.push_back(-b); |
+ else |
+ a[0] -= b; |
+ return a; |
+} |
+ |
+//IMPL: OffsetableConcept |
+inline SBasis operator+(const SBasis & a, double b) { |
+ if(a.isZero()) return Linear(b, b); |
+ SBasis result(a); |
+ result[0] += b; |
+ return result; |
+} |
+inline SBasis operator-(const SBasis & a, double b) { |
+ if(a.isZero()) return Linear(-b, -b); |
+ SBasis result(a); |
+ result[0] -= b; |
+ return result; |
+} |
+inline SBasis& operator+=(SBasis& a, double b) { |
+ if(a.isZero()) |
+ a.push_back(Linear(b,b)); |
+ else |
+ a[0] += b; |
+ return a; |
+} |
+inline SBasis& operator-=(SBasis& a, double b) { |
+ if(a.isZero()) |
+ a.push_back(Linear(-b,-b)); |
+ else |
+ a[0] -= b; |
+ return a; |
+} |
+ |
+SBasis shift(SBasis const &a, int sh); |
+SBasis shift(Linear const &a, int sh); |
+ |
+inline SBasis truncate(SBasis const &a, unsigned terms) { |
+ SBasis c; |
+ c.insert(c.begin(), a.begin(), a.begin() + std::min(terms, (unsigned)a.size())); |
+ return c; |
+} |
+ |
+SBasis multiply(SBasis const &a, SBasis const &b); |
+ |
+SBasis integral(SBasis const &c); |
+SBasis derivative(SBasis const &a); |
+ |
+SBasis sqrt(SBasis const &a, int k); |
+ |
+// return a kth order approx to 1/a) |
+SBasis reciprocal(Linear const &a, int k); |
+SBasis divide(SBasis const &a, SBasis const &b, int k); |
+ |
+inline SBasis operator*(SBasis const & a, SBasis const & b) { |
+ return multiply(a, b); |
+} |
+ |
+inline SBasis& operator*=(SBasis& a, SBasis const & b) { |
+ a = multiply(a, b); |
+ return a; |
+} |
+ |
+//valuation: degree of the first non zero coefficient. |
+inline unsigned |
+valuation(SBasis const &a, double tol=0){ |
+ unsigned val=0; |
+ while( val<a.size() && |
+ fabs(a[val][0])<tol && |
+ fabs(a[val][1])<tol ) |
+ val++; |
+ return val; |
+} |
+ |
+// a(b(t)) |
+SBasis compose(SBasis const &a, SBasis const &b); |
+SBasis compose(SBasis const &a, SBasis const &b, unsigned k); |
+SBasis inverse(SBasis a, int k); |
+//compose_inverse(f,g)=compose(f,inverse(g)), but is numerically more stable in some good cases... |
+//TODO: requires g(0)=0 & g(1)=1 atm. generalization should be obvious. |
+SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order=2, double tol=1e-3); |
+ |
+inline SBasis portion(const SBasis &t, double from, double to) { return compose(t, Linear(from, to)); } |
+ |
+// compute f(g) |
+inline SBasis |
+SBasis::operator()(SBasis const & g) const { |
+ return compose(*this, g); |
+} |
+ |
+inline std::ostream &operator<< (std::ostream &out_file, const Linear &bo) { |
+ out_file << "{" << bo[0] << ", " << bo[1] << "}"; |
+ return out_file; |
+} |
+ |
+inline std::ostream &operator<< (std::ostream &out_file, const SBasis & p) { |
+ for(unsigned i = 0; i < p.size(); i++) { |
+ out_file << p[i] << "s^" << i << " + "; |
+ } |
+ return out_file; |
+} |
+ |
+SBasis sin(Linear bo, int k); |
+SBasis cos(Linear bo, int k); |
+ |
+std::vector<double> roots(SBasis const & s); |
+std::vector<std::vector<double> > multi_roots(SBasis const &f, |
+ std::vector<double> const &levels, |
+ double htol=1e-7, |
+ double vtol=1e-7, |
+ double a=0, |
+ double b=1); |
+ |
+} |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/conjugate_gradient.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/conjugate_gradient.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/conjugate_gradient.cpp (revision 14937) |
@@ -0,0 +1,133 @@ |
+/* |
+ * conjugate_gradient.cpp |
+ * |
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#include <math.h> |
+#include <stdlib.h> |
+#include <valarray> |
+#include <cassert> |
+#include "conjugate_gradient.h" |
+ |
+/* lifted wholely from wikipedia. */ |
+ |
+using std::valarray; |
+ |
+static void |
+matrix_times_vector(valarray<double> const &matrix, /* m * n */ |
+ valarray<double> const &vec, /* n */ |
+ valarray<double> &result) /* m */ |
+{ |
+ unsigned n = vec.size(); |
+ unsigned m = result.size(); |
+ assert(m*n == matrix.size()); |
+ for (unsigned i = 0; i < m; i++) { |
+ double res = 0; |
+ for (unsigned j = 0; j < n; j++) |
+ res += matrix[i*m+j] * vec[j]; |
+ result[i] = res; |
+ } |
+} |
+/* |
+static double Linfty(valarray<double> const &vec) { |
+ return std::max(vec.max(), -vec.min()); |
+} |
+*/ |
+double |
+inner(valarray<double> const &x, |
+ valarray<double> const &y) { |
+ double total = 0; |
+ for(unsigned i = 0; i < x.size(); i++) |
+ total += x[i]*y[i]; |
+ return total;// (x*y).sum(); <- this is more concise, but ineff |
+} |
+ |
+void |
+conjugate_gradient(double **A, |
+ double *x, |
+ double *b, |
+ unsigned n, |
+ double tol, |
+ int max_iterations, |
+ bool ortho1) { |
+ valarray<double> vA(n*n); |
+ valarray<double> vx(n); |
+ valarray<double> vb(n); |
+ for(unsigned i=0;i<n;i++) { |
+ vx[i]=x[i]; |
+ vb[i]=b[i]; |
+ for(unsigned j=0;j<n;j++) { |
+ vA[i*n+j]=A[i][j]; |
+ } |
+ } |
+ conjugate_gradient(vA,vx,vb,n,tol,max_iterations,ortho1); |
+ for(unsigned i=0;i<n;i++) { |
+ x[i]=vx[i]; |
+ } |
+} |
+void |
+conjugate_gradient(valarray<double> const &A, |
+ valarray<double> &x, |
+ valarray<double> const &b, |
+ unsigned n, double tol, |
+ unsigned max_iterations, bool ortho1) { |
+ valarray<double> Ap(n), p(n), r(n); |
+ matrix_times_vector(A,x,Ap); |
+ r=b-Ap; |
+ double r_r = inner(r,r); |
+ unsigned k = 0; |
+ tol *= tol; |
+ while(k < max_iterations && r_r > tol) { |
+ k++; |
+ double r_r_new = r_r; |
+ if(k == 1) |
+ p = r; |
+ else { |
+ r_r_new = inner(r,r); |
+ p = r + (r_r_new/r_r)*p; |
+ } |
+ matrix_times_vector(A, p, Ap); |
+ double alpha_k = r_r_new / inner(p, Ap); |
+ x += alpha_k*p; |
+ r -= alpha_k*Ap; |
+ r_r = r_r_new; |
+ } |
+ //printf("njh: %d iters, Linfty = %g L2 = %g\n", k, |
+ //std::max(-r.min(), r.max()), sqrt(r_r)); |
+ // x is solution |
+} |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4 |
Index: trunk/Scribus/scribus/third_party/lib2geom/shape.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/shape.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/shape.cpp (revision 14937) |
@@ -0,0 +1,632 @@ |
+#include "shape.h" |
+#include "utils.h" |
+#include "sweep.h" |
+#include "ord.h" |
+ |
+#include <iostream> |
+#include <algorithm> |
+#include <cstdlib> |
+ |
+namespace Geom { |
+ |
+// A little sugar for appending a list to another |
+template<typename T> |
+void append(T &a, T const &b) { |
+ a.insert(a.end(), b.begin(), b.end()); |
+} |
+ |
+//Orders a list of indices according to their containment within eachother. |
+struct ContainmentOrder { |
+ std::vector<Region> const *rs; |
+ explicit ContainmentOrder(std::vector<Region> const *r) : rs(r) {} |
+ bool operator()(unsigned a, unsigned b) const { return (*rs)[b].contains((*rs)[a]); } |
+}; |
+ |
+//Returns the list of regions containing a particular point. Useful in tandem with ContainmentOrder |
+std::vector<unsigned> Shape::containment_list(Point p) const { |
+ std::vector<Rect> pnt; |
+ pnt.push_back(Rect(p, p)); |
+ std::vector<std::vector<unsigned> > cull = sweep_bounds(pnt, bounds(*this)); |
+ std::vector<unsigned> containers; |
+ if(cull[0].size() == 0) return containers; |
+ for(unsigned i = 0; i < cull[0].size(); i++) |
+ if(content[cull[0][i]].contains(p)) containers.push_back(cull[0][i]); |
+ return containers; |
+} |
+ |
+/* Used within shape_boolean and related functions, as the name describes, finds the |
+ * first false within the list of lists of booleans. |
+ */ |
+void first_false(std::vector<std::vector<bool> > visited, unsigned &i, unsigned &j) { |
+ for(i = 0, j = 0; i < visited.size(); i++) { |
+ std::vector<bool>::iterator unvisited = std::find(visited[i].begin(), visited[i].end(), false); |
+ if(unvisited != visited[i].end()) { |
+ j = unvisited - visited[i].begin(); |
+ break; |
+ } |
+ } |
+} |
+ |
+// Finds a crossing in a list of them, given the sorting index. |
+unsigned find_crossing(Crossings const &cr, Crossing x, unsigned i) { |
+ return std::lower_bound(cr.begin(), cr.end(), x, CrossingOrder(i)) - cr.begin(); |
+} |
+ |
+/* This function handles boolean ops on shapes. The first parameter is a bool |
+ * which determines its behavior in each combination of cases. For proper |
+ * fill information and noncrossing behavior, the fill data of the regions |
+ * must be correct. The boolean parameter determines whether the operation |
+ * is a union or a subtraction. Reversed paths represent inverse regions, |
+ * where everything is included in the fill except for the insides. |
+ * |
+ * Here is a chart of the behavior under various circumstances: |
+ * |
+ * rev = false (union) |
+ * A |
+ * F H |
+ * F A+B -> F A-B -> H |
+ *B |
+ * H B-A -> H AxB -> H |
+ * |
+ * rev = true (intersect) |
+ * A |
+ * F H |
+ * F AxB -> F B-A -> F |
+ *B |
+ * H A-B -> F A+B -> H |
+ * |
+ * F/H = Fill outer / Hole outer |
+ * A/B specify operands |
+ * + = union, - = subtraction, x = intersection |
+ * -> read as "produces" |
+ * |
+ * This is the main function of boolops, yet its operation isn't very complicated. |
+ * It traverses the crossings, and uses the crossing direction to decide whether |
+ * the next segment should be taken from A or from B. The second half of the |
+ * function deals with figuring out what to do with bits that have no intersection. |
+ */ |
+Shape shape_boolean(bool rev, Shape const & a, Shape const & b, CrossingSet const & crs) { |
+ const Regions ac = a.content, bc = b.content; |
+ |
+ //Keep track of which crossings we've hit. |
+ std::vector<std::vector<bool> > visited; |
+ for(unsigned i = 0; i < crs.size(); i++) |
+ visited.push_back(std::vector<bool>(crs[i].size(), false)); |
+ |
+ //Traverse the crossings, creating chunks |
+ Regions chunks; |
+ while(true) { |
+ unsigned i, j; |
+ first_false(visited, i, j); |
+ if(i == visited.size()) break; |
+ |
+ Path res; |
+ do { |
+ Crossing cur = crs[i][j]; |
+ visited[i][j] = true; |
+ |
+ //get indices of the dual: |
+ unsigned io = cur.getOther(i), jo = find_crossing(crs[io], cur, io); |
+ if(jo < visited[io].size()) visited[io][jo] = true; |
+ |
+ //main driving logic |
+ if(logical_xor(cur.dir, rev)) { |
+ if(i >= ac.size()) { i = io; j = jo; } |
+ j++; |
+ if(j >= crs[i].size()) j = 0; |
+ Crossing next = crs[i][j]; |
+ ac[next.a].boundary.appendPortionTo(res, cur.ta, next.ta); |
+ } else { |
+ if(i < ac.size()) { i = io; j = jo; } |
+ j++; |
+ if(j >= crs[i].size()) j = 0; |
+ Crossing next = crs[i][j]; |
+ bc[next.b - ac.size()].boundary.appendPortionTo(res, cur.tb, next.tb); |
+ } |
+ } while (!visited[i][j]); |
+ if(res.size() > 0) chunks.push_back(Region(res)); |
+ } |
+ |
+ //If true, then we are on the 'subtraction diagonal' |
+ bool const on_sub = logical_xor(a.fill, b.fill); |
+ //If true, outer paths are filled |
+ bool const res_fill = rev ? (on_sub || (a.fill && b.fill)) : (a.fill && b.fill); |
+ |
+ //Handle unintersecting portions |
+ for(unsigned i = 0; i < crs.size(); i++) { |
+ if(crs[i].size() == 0) { |
+ bool env; |
+ bool on_a = i < ac.size(); |
+ Region const & r(on_a ? ac[i] : bc[i - ac.size()]); |
+ Shape const & other(on_a ? b : a); |
+ |
+ std::vector<unsigned> containers = other.containment_list(r.boundary.initialPoint()); |
+ if(containers.empty()) { |
+ //not included in any container, the environment fill is the opposite of the outer fill |
+ env = !res_fill; |
+ if(on_sub && logical_xor(other.fill, res_fill)) env = !env; //If on the subtractor, invert the environment fill |
+ } else { |
+ //environment fill is the same as the inner-most container |
+ std::vector<unsigned>::iterator cit = std::min_element(containers.begin(), containers.end(), ContainmentOrder(&other.content)); |
+ env = other[*cit].isFill(); |
+ } |
+ if(!logical_xor(rev, env)) chunks.push_back(r); //When unioning, environment must be hole for inclusion, when intersecting, it must be filled |
+ } |
+ } |
+ |
+ return Shape(chunks, res_fill); |
+} |
+ |
+// Just a convenience wrapper for shape_boolean, which handles the crossings |
+Shape shape_boolean(bool rev, Shape const & a, Shape const & b) { |
+ CrossingSet crs = crossings_between(a, b); |
+ |
+ return shape_boolean(rev, a, b, crs); |
+} |
+ |
+ |
+// Some utility functions for boolop: |
+ |
+std::vector<double> region_sizes(Shape const &a) { |
+ std::vector<double> ret; |
+ for(unsigned i = 0; i < a.size(); i++) { |
+ ret.push_back(double(a[i].size())); |
+ } |
+ return ret; |
+} |
+ |
+Shape shape_boolean_ra(bool rev, Shape const &a, Shape const &b, CrossingSet const &crs) { |
+ return shape_boolean(rev, a.inverse(), b, reverse_ta(crs, a.size(), region_sizes(a))); |
+} |
+ |
+Shape shape_boolean_rb(bool rev, Shape const &a, Shape const &b, CrossingSet const &crs) { |
+ return shape_boolean(rev, a, b.inverse(), reverse_tb(crs, a.size(), region_sizes(b))); |
+} |
+ |
+/* This is a function based on shape_boolean which allows boolean operations |
+ * to be specified as a logic table. This logic table is 4 bit-flags, which |
+ * correspond to the elements of the 'truth table' for a particular operation. |
+ * These flags are defined with the enums starting with BOOLOP_ . |
+ * |
+ * NOTE: currently doesn't work, as the CrossingSet reversal functions crash |
+ */ |
+Shape boolop(Shape const &a, Shape const &b, unsigned flags, CrossingSet const &crs) { |
+ throwNotImplemented(0); |
+ flags &= 15; |
+ if(flags <= BOOLOP_UNION) { |
+ switch(flags) { |
+ case BOOLOP_INTERSECT: return shape_boolean(true, a, b, crs); |
+ case BOOLOP_SUBTRACT_A_B: return shape_boolean_rb(true, a, b, crs); |
+ case BOOLOP_IDENTITY_A: return a; |
+ case BOOLOP_SUBTRACT_B_A: return shape_boolean_ra(true, a, b, crs); |
+ case BOOLOP_IDENTITY_B: return b; |
+ case BOOLOP_EXCLUSION: { |
+ Shape res = shape_boolean_rb(true, a, b, crs); |
+ append(res.content, shape_boolean_ra(true, a, b, crs).content); |
+ return res; |
+ } |
+ case BOOLOP_UNION: return shape_boolean(false, a, b); |
+ } |
+ } else { |
+ flags = ~flags & 15; |
+ switch(flags - BOOLOP_NEITHER) { |
+ case BOOLOP_SUBTRACT_A_B: return shape_boolean_ra(false, a, b, crs); |
+ case BOOLOP_SUBTRACT_B_A: return shape_boolean_rb(false, a, b, crs); |
+ case BOOLOP_EXCLUSION: { |
+ Shape res = shape_boolean_ra(false, a, b, CrossingSet(crs)); |
+ append(res.content, shape_boolean_rb(false, a, b, CrossingSet(crs)).content); |
+ return res; |
+ } |
+ } |
+ return boolop(a, b, flags, crs).inverse(); |
+ } |
+ return Shape(); |
+} |
+ |
+/* This version of the boolop function doesn't require a set of crossings, as |
+ * it computes them for you. This is more efficient in some cases, as the |
+ * shape can be inverted before finding crossings. In the special case of |
+ * exclusion it uses the other version of boolop. |
+ */ |
+Shape boolop(Shape const &a, Shape const &b, unsigned flags) { |
+ flags &= 15; |
+ if(flags <= BOOLOP_UNION) { |
+ switch(flags) { |
+ case BOOLOP_INTERSECT: return shape_boolean(true, a, b); |
+ case BOOLOP_SUBTRACT_A_B: return shape_boolean(true, a, b.inverse()); |
+ case BOOLOP_IDENTITY_A: return a; |
+ case BOOLOP_SUBTRACT_B_A: return shape_boolean(true, b, a.inverse()); |
+ case BOOLOP_IDENTITY_B: return b; |
+ case BOOLOP_EXCLUSION: { |
+ Shape res = shape_boolean(true, a, b.inverse()); |
+ append(res.content, shape_boolean(true, b, a.inverse()).content); |
+ return res; |
+ } //return boolop(a, b, flags, crossings_between(a, b)); |
+ case BOOLOP_UNION: return shape_boolean(false, a, b); |
+ } |
+ } else { |
+ flags = ~flags & 15; |
+ switch(flags) { |
+ case BOOLOP_SUBTRACT_A_B: return shape_boolean(false, b, a.inverse()); |
+ case BOOLOP_SUBTRACT_B_A: return shape_boolean(false, a, b.inverse()); |
+ case BOOLOP_EXCLUSION: { |
+ Shape res = shape_boolean(false, a, b.inverse()); |
+ append(res.content, shape_boolean(false, b, a.inverse()).content); |
+ return res; |
+ } //return boolop(a, b, flags, crossings_between(a, b)); |
+ } |
+ return boolop(a, b, flags).inverse(); |
+ } |
+ return Shape(); |
+} |
+ |
+int paths_winding(std::vector<Path> const &ps, Point p) { |
+ int ret = 0; |
+ for(unsigned i = 0; i < ps.size(); i++) |
+ ret += winding(ps[i], p); |
+ return ret; |
+} |
+ |
+void add_to_shape(Shape &s, Path const &p, bool fill) { |
+ if(fill) |
+ s.content.push_back(Region(p).asFill()); |
+ else |
+ s.content.push_back(Region(p).asHole()); |
+} |
+ |
+int inner_winding(Path const & p, std::vector<Path> const &ps) { |
+ Point pnt = p.initialPoint(); |
+ return paths_winding(ps, pnt) - winding(p, pnt) + 1; |
+} |
+ |
+double fudgerize(double d, bool rev) { |
+ double ret = rev ? d - 0.01 : d + 0.01; |
+ if(ret < 0) ret = 0; |
+ return ret; |
+} |
+ |
+unsigned pick_coincident(unsigned ix, unsigned jx, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs) { |
+ unsigned ex_jx = jx; |
+ unsigned oix = crs[ix][jx].getOther(ix); |
+ double otime = crs[ix][jx].getTime(oix); |
+ Point cross_point = ps[oix].pointAt(otime), |
+ along = ps[oix].pointAt(fudgerize(otime, rev)) - cross_point, |
+ prev = -along; |
+ bool ex_dir = rev; |
+ for(unsigned k = jx; k < crs[ix].size(); k++) { |
+ unsigned koix = crs[ix][k].getOther(ix); |
+ if(koix == oix) { |
+ if(!are_near(otime, crs[ix][k].getTime(oix))) break; |
+ for(unsigned dir = 0; dir < 2; dir++) { |
+ Point val = ps[ix].pointAt(fudgerize(crs[ix][k].getTime(ix), dir)) - cross_point; |
+ Cmp to_prev = cmp(cross(val, prev), 0); |
+ Cmp from_along = cmp(cross(along, val), 0); |
+ Cmp c = cmp(from_along, to_prev); |
+ if(c == EQUAL_TO && from_along == LESS_THAN) { |
+ ex_jx = k; |
+ prev = val; |
+ ex_dir = dir; |
+ } |
+ } |
+ } |
+ } |
+ rev = ex_dir; |
+ return ex_jx; |
+} |
+ |
+unsigned crossing_along(double t, unsigned ix, unsigned jx, bool dir, Crossings const & crs) { |
+ Crossing cur = Crossing(t, t, ix, ix, false); |
+ if(jx < crs.size()) { |
+ double ct = crs[jx].getTime(ix); |
+ if(t == ct) { |
+ cur = crs[jx]; |
+ if(cur.a == cur.b) { |
+ if(jx+1 <= crs.size() && crs[jx+1].getOther(ix) == ix) return jx+1; |
+ if(jx > 0 && crs[jx-1].getOther(ix) == ix) return jx-1; |
+ } |
+ } |
+ } |
+ if(!dir) { |
+ jx = std::upper_bound(crs.begin(), crs.end(), cur, CrossingOrder(ix)) - crs.begin(); |
+ } else { |
+ jx = std::lower_bound(crs.begin(), crs.end(), cur, CrossingOrder(ix)) - crs.begin(); |
+ if(jx == 0) jx = crs.size() - 1; else jx--; |
+ jx = std::lower_bound(crs.begin(), crs.end(), crs[jx], CrossingOrder(ix)) - crs.begin(); |
+ } |
+ if(jx >= crs.size()) jx = 0; |
+ return jx; |
+} |
+ |
+void crossing_dual(unsigned &i, unsigned &j, CrossingSet const & crs) { |
+ Crossing cur = crs[i][j]; |
+ i = cur.getOther(i); |
+// std::cout << i << "\n"; |
+ if(crs[i].empty()) |
+ j = 0; |
+ else |
+ j = std::lower_bound(crs[i].begin(), crs[i].end(), cur, CrossingOrder(i)) - crs[i].begin(); |
+} |
+ |
+//locate a crossing on the outside, by casting a ray through the middle of the bbox |
+void outer_crossing(unsigned &ix, unsigned &jx, bool & dir, std::vector<Path> const & ps, CrossingSet const & crs) { |
+ Rect bounds = ps[ix].boundsFast(); |
+ double ry = bounds[Y].middle(); |
+ double max_val = bounds.left(), max_t = 0; |
+ ix = ps.size(); |
+ for(unsigned i = 0; i < ps.size(); i++) { |
+ if(!crs[i].empty()) { |
+ std::vector<double> rts = ps[i].roots(ry, Y); |
+ for(unsigned j = 0; j < rts.size(); j++) { |
+ double val = ps[i].valueAt(rts[j], X); |
+ if(val > max_val) { |
+ ix = i; |
+ max_val = val; |
+ max_t = rts[j]; |
+ } |
+ } |
+ } |
+ } |
+ if(ix != ps.size()) { |
+ dir = ps[ix].valueAt(max_t + 0.01, Y) > |
+ ps[ix].valueAt(max_t - 0.01, Y); |
+ jx = crossing_along(max_t, ix, jx, dir, crs[ix]); |
+ } |
+} |
+ |
+std::vector<Path> inner_sanitize(std::vector<Path> const & ps) { |
+ CrossingSet crs(crossings_among(ps)); |
+ |
+ Regions chunks; |
+ |
+ std::vector<bool> used_path(ps.size(), false); |
+ std::vector<std::vector<bool> > visited; |
+ for(unsigned i = 0; i < crs.size(); i++) |
+ visited.push_back(std::vector<bool>(crs[i].size(), false)); |
+ |
+ std::vector<Path> result_paths; |
+ |
+ while(true) { |
+ unsigned ix = 0, jx = 0; |
+ bool dir = false; |
+ |
+ //find an outer crossing by trying various paths and checking if the crossings are used |
+ for(; ix < crs.size(); ix++) { |
+ //TODO: optimize so it doesn't unecessarily check stuff |
+ bool cont = true; |
+ for(unsigned j = 0; j < crs[ix].size(); j++) { |
+ if(!visited[ix][j]) { cont = false; break; } |
+ } |
+ if(cont) continue; |
+ unsigned rix = ix, rjx = jx; |
+ outer_crossing(rix, rjx, dir, ps, crs); |
+ if(rix >= crs.size() || visited[rix][rjx]) continue; |
+ ix = rix; jx = rjx; |
+ break; |
+ } |
+ if(ix == crs.size()) break; |
+ crossing_dual(ix, jx, crs); |
+ |
+ dir = !dir; |
+ |
+ Path res; |
+ do { |
+ visited[ix][jx] = true; |
+ //unsigned nix = ix, njx = jx; |
+ //crossing_dual(nix, njx, crs); |
+ //visited[nix][njx] = true; |
+ unsigned fix = ix, fjx = jx; |
+ |
+ bool new_dir = dir; |
+ |
+ jx = crossing_along(crs[ix][jx].getTime(ix), ix, jx, dir, crs[ix]); |
+ if(crs[ix][jx].a != crs[ix][jx].b) crossing_dual(ix, jx, crs); else new_dir = !new_dir; |
+ jx = pick_coincident(ix, jx, new_dir, ps, crs); |
+ |
+ //unsigned nix = ix, njx = jx; |
+ //crossing_dual(nix, njx, crs); |
+ |
+ Crossing from = crs[fix][fjx], |
+ to = crs[ix][jx]; |
+ if(dir) { |
+ // backwards |
+// std::cout << "r" << ix << "[" << from.getTime(ix) << ", " << to.getTime(ix) << "]\n"; |
+ Path p = ps[ix].portion(from.getTime(ix), to.getTime(ix)).reverse(); |
+ for(unsigned i = 0; i < p.size(); i++) |
+ res.append(p[i]); |
+ } else { |
+ // forwards |
+// std::cout << "f" << ix << "[" << from.getTime(ix) << ", " << to.getTime(ix) << "]\n"; |
+ ps[ix].appendPortionTo(res, from.getTime(ix), to.getTime(ix)); |
+ } |
+ dir = new_dir; |
+ } while(!visited[ix][jx]); |
+// std::cout << "added " << res.size() << "\n"; |
+ result_paths.push_back(res); |
+ } |
+ for(unsigned i = 0; i < crs.size(); i++) { |
+ if(crs[i].empty() && !used_path[i]) |
+ result_paths.push_back(ps[i]); |
+ } |
+ return result_paths; |
+} |
+ |
+Shape sanitize(std::vector<Path> const & ps) { |
+ std::vector<Path> res; |
+ for(unsigned i = 0; i < ps.size(); i++) { |
+ append(res, inner_sanitize(std::vector<Path>(1, ps[i]))); |
+ } |
+ return stopgap_cleaner(res); |
+} |
+ |
+/* WIP sanitizer: |
+unsigned pick_coincident(unsigned ix, unsigned jx, bool pref, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs) { |
+ unsigned ex_jx = jx; |
+ unsigned oix = crs[ix][jx].getOther(ix); |
+ double otime = crs[ix][jx].getTime(oix); |
+ Point cross_point = ps[oix].pointAt(otime), |
+ along = ps[oix].pointAt(otime + (rev ? -0.01 : 0.01)) - cross_point, |
+ prev = -along; |
+ bool ex_dir = rev; |
+ for(unsigned k = jx; k < crs[ix].size(); k++) { |
+ unsigned koix = crs[ix][k].getOther(ix); |
+ if(koix == oix) { |
+ if(!are_near(otime, crs[ix][k].getTime(oix))) break; |
+ for(unsigned dir = 0; dir < 2; dir++) { |
+ Point val = ps[ix].pointAt(crs[ix][k].getTime(ix) + (dir ? -0.01 : 0.01)) - cross_point; |
+ Cmp to_prev = cmp(cross(val, prev), 0); |
+ Cmp from_along = cmp(cross(along, val), 0); |
+ Cmp c = cmp(from_along, to_prev); |
+ if(c == EQUAL_TO && (from_along == LESS_THAN) == pref) { |
+ ex_jx = k; |
+ prev = val; |
+ ex_dir = dir; |
+ } |
+ } |
+ } |
+ } |
+ rev = ex_dir; |
+ return ex_jx; |
+} |
+ |
+unsigned corner_index(unsigned &i) { |
+ div_t div_res = div(i, 4); |
+ i = div_res.quot; |
+ return div_res.rem; |
+} |
+ |
+bool corner_direction(unsigned ix, unsigned jc, unsigned corner, CrossingSet const &crs) { |
+ if(crs[ix][jc].a == ix) return corner > 1; else return corner %2 == 1; |
+} |
+ |
+Shape sanitize(std::vector<Path> const & ps) { |
+ CrossingSet crs = crossings_among(ps); |
+ |
+ //Keep track of which CORNERS we've hit. |
+ // FF FR RF RR, first is A dir, second B dir |
+ std::vector<std::vector<bool> > visited; |
+ for(unsigned i = 0; i < crs.size(); i++) |
+ visited.push_back(std::vector<bool>(crs[i].size()*4, false)); |
+ |
+ Regions chunks; |
+ while(true) { |
+ unsigned i, j; |
+ first_false(visited, i, j); |
+ unsigned corner = corner_index(j); |
+ |
+ if(i == visited.size()) break; |
+ |
+ bool dir = corner_direction(i, j, corner, crs); |
+ |
+ //Figure out whether we hug the path cw or ccw, based on the orientation of the initial corner: |
+ unsigned oix = crs[i][j].getOther(i); |
+ double otime = crs[i][j].getTime(oix); |
+ bool odir = (oix == crs[i][j].a) ? corner > 1 : corner % 2 == 1; |
+ Point cross_point = ps[oix].pointAt(otime), |
+ along = ps[oix].pointAt(otime + (odir ? -0.01 : 0.01)) - cross_point, |
+ val = ps[i].pointAt(crs[i][j].getTime(i) + (dir ? -0.01 : 0.01)) - cross_point; |
+ |
+ Cmp from_along = cmp(cross(along, val), 0); |
+ bool cw = from_along == LESS_THAN; |
+ std::cout << "cw = " << cw << "\n"; |
+ Path res; |
+ do { |
+ Crossing cur = crs[i][j]; |
+ visited[i][j*4+corner] = true; |
+ |
+ unsigned fix = i, fjx = j; |
+ crossing_dual(i, j, crs); |
+ visited[i][j*4+corner] = true; |
+ i = fix; j = fjx; |
+ |
+ j = crossing_along(crs[i][j].getTime(i), i, j, dir, crs[i]); |
+ |
+ crossing_dual(i, j, crs); |
+ |
+ bool new_dir = dir; |
+ pick_coincident(i, j, cw, new_dir, ps, crs); |
+ |
+ Crossing from = crs[fix][fjx], |
+ to = crs[i][j]; |
+ if(dir) { |
+ // backwards |
+ std::cout << "r" << i << "[" << to.getTime(i) << ", " << from.getTime(i) << "]\n"; |
+ Path p = ps[i].portion(to.getTime(i) + 0.001, from.getTime(i)).reverse(); |
+ for(unsigned k = 0; k < p.size(); k++) |
+ res.append(p[k]); |
+ } else { |
+ // forwards |
+ std::cout << "f" << i << "[" << from.getTime(i) << ", " << to.getTime(i) << "]\n"; |
+ ps[i].appendPortionTo(res, from.getTime(i) + 0.001, to.getTime(i)); |
+ } |
+ if(i == to.a) |
+ corner = (new_dir ? 2 : 0) + (dir ? 1 : 0); |
+ else |
+ corner = (new_dir ? 1 : 0) + (dir ? 2 : 0); |
+ dir = new_dir; |
+ } while(!visited[i][j*4+corner]); |
+ chunks.push_back(Region(res)); |
+// if(use) { |
+// chunks.push_back(Region(res, true)); |
+// } |
+ } |
+ return Shape(chunks); |
+// return ret; |
+} */ |
+ |
+/* This transforms a shape by a matrix. In the case that the matrix flips |
+ * the shape, it reverses the paths in order to preserve the fill. |
+ */ |
+Shape Shape::operator*(Matrix const &m) const { |
+ Shape ret; |
+ for(unsigned i = 0; i < size(); i++) |
+ ret.content.push_back(content[i] * m); |
+ ret.fill = fill; |
+ return ret; |
+} |
+ |
+// Inverse is a boolean not, and simply reverses all the paths & fill flags |
+Shape Shape::inverse() const { |
+ Shape ret; |
+ for(unsigned i = 0; i < size(); i++) |
+ ret.content.push_back(content[i].inverse()); |
+ ret.fill = !fill; |
+ return ret; |
+} |
+ |
+bool Shape::contains(Point const &p) const { |
+ std::vector<unsigned> containers = containment_list(p); |
+ if(containers.empty()) return !isFill(); |
+ unsigned ix = *min_element(containers.begin(), containers.end(), ContainmentOrder(&content)); |
+ return content[ix].isFill(); |
+} |
+ |
+Shape stopgap_cleaner(std::vector<Path> const &ps) { |
+ if(ps.empty()) return Shape(false); |
+ Shape ret; |
+ for(unsigned i = 0; i < ps.size(); i++) |
+ add_to_shape(ret, ps[i], inner_winding(ps[i], ps) % 2 != 0); |
+ return ret; |
+} |
+ |
+bool Shape::inside_invariants() const { //semi-slow & easy to violate |
+ for(unsigned i = 0; i < size(); i++) |
+ if( logical_xor(content[i].isFill(), contains(content[i].boundary.initialPoint())) ) return false; |
+ return true; |
+} |
+bool Shape::region_invariants() const { //semi-slow |
+ for(unsigned i = 0; i < size(); i++) |
+ if(!content[i].invariants()) return false; |
+ return true; |
+} |
+bool Shape::cross_invariants() const { //slow |
+ CrossingSet crs; // = crossings_among(paths_from_regions(content)); |
+ for(unsigned i = 0; i < crs.size(); i++) |
+ if(!crs[i].empty()) return false; |
+ return true; |
+} |
+ |
+bool Shape::invariants() const { |
+ return inside_invariants() && region_invariants() && cross_invariants(); |
+} |
+ |
+} |
Index: trunk/Scribus/scribus/third_party/lib2geom/basic-intersection.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/basic-intersection.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/basic-intersection.cpp (revision 14937) |
@@ -0,0 +1,365 @@ |
+#include "basic-intersection.h" |
+#include "exception.h" |
+#include "angle.h" |
+ |
+#ifndef M_SQRT2 |
+#define M_SQRT2 1.41421356237309504880 |
+#endif |
+ |
+ |
+unsigned intersect_steps = 0; |
+ |
+using std::vector; |
+namespace Geom { |
+ |
+class OldBezier { |
+public: |
+ std::vector<Geom::Point> p; |
+ OldBezier() { |
+ } |
+ void split(double t, OldBezier &a, OldBezier &b) const; |
+ |
+ ~OldBezier() {} |
+ |
+ void bounds(double &minax, double &maxax, |
+ double &minay, double &maxay) { |
+ // Compute bounding box for a |
+ minax = p[0][X]; // These are the most likely to be extremal |
+ maxax = p.back()[X]; |
+ if( minax > maxax ) |
+ std::swap(minax, maxax); |
+ for(unsigned i = 1; i < p.size()-1; i++) { |
+ if( p[i][X] < minax ) |
+ minax = p[i][X]; |
+ else if( p[i][X] > maxax ) |
+ maxax = p[i][X]; |
+ } |
+ |
+ minay = p[0][Y]; // These are the most likely to be extremal |
+ maxay = p.back()[Y]; |
+ if( minay > maxay ) |
+ std::swap(minay, maxay); |
+ for(unsigned i = 1; i < p.size()-1; i++) { |
+ if( p[i][Y] < minay ) |
+ minay = p[i][Y]; |
+ else if( p[i][Y] > maxay ) |
+ maxay = p[i][Y]; |
+ } |
+ |
+ } |
+ |
+}; |
+ |
+static std::vector<std::pair<double, double> > |
+find_intersections( OldBezier a, OldBezier b); |
+ |
+static std::vector<std::pair<double, double> > |
+find_self_intersections(OldBezier const &Sb, D2<SBasis> const & A); |
+ |
+std::vector<std::pair<double, double> > |
+find_intersections( vector<Geom::Point> const & A, |
+ vector<Geom::Point> const & B) { |
+ OldBezier a, b; |
+ a.p = A; |
+ b.p = B; |
+ return find_intersections(a,b); |
+} |
+ |
+std::vector<std::pair<double, double> > |
+find_self_intersections(OldBezier const &Sb) { |
+ throwNotImplemented(0); |
+} |
+ |
+std::vector<std::pair<double, double> > |
+find_self_intersections(D2<SBasis> const & A) { |
+ OldBezier Sb; |
+ Sb.p = sbasis_to_bezier(A); |
+ return find_self_intersections(Sb, A); |
+} |
+ |
+ |
+static std::vector<std::pair<double, double> > |
+find_self_intersections(OldBezier const &Sb, D2<SBasis> const & A) { |
+ |
+ |
+ vector<double> dr = roots(derivative(A[X])); |
+ { |
+ vector<double> dyr = roots(derivative(A[Y])); |
+ dr.insert(dr.begin(), dyr.begin(), dyr.end()); |
+ } |
+ dr.push_back(0); |
+ dr.push_back(1); |
+ // We want to be sure that we have no empty segments |
+ sort(dr.begin(), dr.end()); |
+ unique(dr.begin(), dr.end()); |
+ |
+ std::vector<std::pair<double, double> > all_si; |
+ |
+ vector<OldBezier> pieces; |
+ { |
+ OldBezier in = Sb, l, r; |
+ for(unsigned i = 0; i < dr.size()-1; i++) { |
+ in.split((dr[i+1]-dr[i]) / (1 - dr[i]), l, r); |
+ pieces.push_back(l); |
+ in = r; |
+ } |
+ } |
+ for(unsigned i = 0; i < dr.size()-1; i++) { |
+ for(unsigned j = i+1; j < dr.size()-1; j++) { |
+ std::vector<std::pair<double, double> > section = |
+ find_intersections( pieces[i], pieces[j]); |
+ for(unsigned k = 0; k < section.size(); k++) { |
+ double l = section[k].first; |
+ double r = section[k].second; |
+// XXX: This condition will prune out false positives, but it might create some false negatives. Todo: Confirm it is correct. |
+ if(j == i+1) |
+ if((l == 1) && (r == 0)) |
+ continue; |
+ all_si.push_back(std::make_pair((1-l)*dr[i] + l*dr[i+1], |
+ (1-r)*dr[j] + r*dr[j+1])); |
+ } |
+ } |
+ } |
+ |
+ // Because i is in order, all_si should be roughly already in order? |
+ //sort(all_si.begin(), all_si.end()); |
+ //unique(all_si.begin(), all_si.end()); |
+ |
+ return all_si; |
+} |
+ |
+/* The value of 1.0 / (1L<<14) is enough for most applications */ |
+const double INV_EPS = (1L<<14); |
+ |
+/* |
+ * split the curve at the midpoint, returning an array with the two parts |
+ * Temporary storage is minimized by using part of the storage for the result |
+ * to hold an intermediate value until it is no longer needed. |
+ */ |
+void OldBezier::split(double t, OldBezier &left, OldBezier &right) const { |
+ const unsigned sz = p.size(); |
+ std::vector<Geom::Point> Vtemp(p); |
+ |
+ left.p.resize(sz); |
+ right.p.resize(sz); |
+ left.p[0] = Vtemp[0]; |
+ right.p[sz-1] = Vtemp[sz-1]; |
+ /* Triangle computation */ |
+ for (unsigned i = 1; i < sz; i++) { |
+ for (unsigned j = 0; j < sz - i; j++) { |
+ Vtemp[j] = lerp(t, Vtemp[j], Vtemp[j+1]); |
+ } |
+ left.p[i] = Vtemp[0]; |
+ right.p[sz-1-i] = Vtemp[sz-1-i]; |
+ } |
+} |
+ |
+ |
+/* |
+ * Test the bounding boxes of two OldBezier curves for interference. |
+ * Several observations: |
+ * First, it is cheaper to compute the bounding box of the second curve |
+ * and test its bounding box for interference than to use a more direct |
+ * approach of comparing all control points of the second curve with |
+ * the various edges of the bounding box of the first curve to test |
+ * for interference. |
+ * Second, after a few subdivisions it is highly probable that two corners |
+ * of the bounding box of a given Bezier curve are the first and last |
+ * control point. Once this happens once, it happens for all subsequent |
+ * subcurves. It might be worth putting in a test and then short-circuit |
+ * code for further subdivision levels. |
+ * Third, in the final comparison (the interference test) the comparisons |
+ * should both permit equality. We want to find intersections even if they |
+ * occur at the ends of segments. |
+ * Finally, there are tighter bounding boxes that can be derived. It isn't |
+ * clear whether the higher probability of rejection (and hence fewer |
+ * subdivisions and tests) is worth the extra work. |
+ */ |
+ |
+bool intersect_BB( OldBezier a, OldBezier b ) { |
+ double minax, maxax, minay, maxay; |
+ a.bounds(minax, maxax, minay, maxay); |
+ double minbx, maxbx, minby, maxby; |
+ b.bounds(minbx, maxbx, minby, maxby); |
+ // Test bounding box of b against bounding box of a |
+ // Not >= : need boundary case |
+ return !( ( minax > maxbx ) || ( minay > maxby ) || |
+ ( minbx > maxax ) || ( minby > maxay ) ); |
+} |
+ |
+/* |
+ * Recursively intersect two curves keeping track of their real parameters |
+ * and depths of intersection. |
+ * The results are returned in a 2-D array of doubles indicating the parameters |
+ * for which intersections are found. The parameters are in the order the |
+ * intersections were found, which is probably not in sorted order. |
+ * When an intersection is found, the parameter value for each of the two |
+ * is stored in the index elements array, and the index is incremented. |
+ * |
+ * If either of the curves has subdivisions left before it is straight |
+ * (depth > 0) |
+ * that curve (possibly both) is (are) subdivided at its (their) midpoint(s). |
+ * the depth(s) is (are) decremented, and the parameter value(s) corresponding |
+ * to the midpoints(s) is (are) computed. |
+ * Then each of the subcurves of one curve is intersected with each of the |
+ * subcurves of the other curve, first by testing the bounding boxes for |
+ * interference. If there is any bounding box interference, the corresponding |
+ * subcurves are recursively intersected. |
+ * |
+ * If neither curve has subdivisions left, the line segments from the first |
+ * to last control point of each segment are intersected. (Actually the |
+ * only the parameter value corresponding to the intersection point is found). |
+ * |
+ * The apriori flatness test is probably more efficient than testing at each |
+ * level of recursion, although a test after three or four levels would |
+ * probably be worthwhile, since many curves become flat faster than their |
+ * asymptotic rate for the first few levels of recursion. |
+ * |
+ * The bounding box test fails much more frequently than it succeeds, providing |
+ * substantial pruning of the search space. |
+ * |
+ * Each (sub)curve is subdivided only once, hence it is not possible that for |
+ * one final line intersection test the subdivision was at one level, while |
+ * for another final line intersection test the subdivision (of the same curve) |
+ * was at another. Since the line segments share endpoints, the intersection |
+ * is robust: a near-tangential intersection will yield zero or two |
+ * intersections. |
+ */ |
+void recursively_intersect( OldBezier a, double t0, double t1, int deptha, |
+ OldBezier b, double u0, double u1, int depthb, |
+ std::vector<std::pair<double, double> > ¶meters) |
+{ |
+ intersect_steps ++; |
+ if( deptha > 0 ) |
+ { |
+ OldBezier A[2]; |
+ a.split(0.5, A[0], A[1]); |
+ double tmid = (t0+t1)*0.5; |
+ deptha--; |
+ if( depthb > 0 ) |
+ { |
+ OldBezier B[2]; |
+ b.split(0.5, B[0], B[1]); |
+ double umid = (u0+u1)*0.5; |
+ depthb--; |
+ if( intersect_BB( A[0], B[0] ) ) |
+ recursively_intersect( A[0], t0, tmid, deptha, |
+ B[0], u0, umid, depthb, |
+ parameters ); |
+ if( intersect_BB( A[1], B[0] ) ) |
+ recursively_intersect( A[1], tmid, t1, deptha, |
+ B[0], u0, umid, depthb, |
+ parameters ); |
+ if( intersect_BB( A[0], B[1] ) ) |
+ recursively_intersect( A[0], t0, tmid, deptha, |
+ B[1], umid, u1, depthb, |
+ parameters ); |
+ if( intersect_BB( A[1], B[1] ) ) |
+ recursively_intersect( A[1], tmid, t1, deptha, |
+ B[1], umid, u1, depthb, |
+ parameters ); |
+ } |
+ else |
+ { |
+ if( intersect_BB( A[0], b ) ) |
+ recursively_intersect( A[0], t0, tmid, deptha, |
+ b, u0, u1, depthb, |
+ parameters ); |
+ if( intersect_BB( A[1], b ) ) |
+ recursively_intersect( A[1], tmid, t1, deptha, |
+ b, u0, u1, depthb, |
+ parameters ); |
+ } |
+ } |
+ else |
+ if( depthb > 0 ) |
+ { |
+ OldBezier B[2]; |
+ b.split(0.5, B[0], B[1]); |
+ double umid = (u0 + u1)*0.5; |
+ depthb--; |
+ if( intersect_BB( a, B[0] ) ) |
+ recursively_intersect( a, t0, t1, deptha, |
+ B[0], u0, umid, depthb, |
+ parameters ); |
+ if( intersect_BB( a, B[1] ) ) |
+ recursively_intersect( a, t0, t1, deptha, |
+ B[0], umid, u1, depthb, |
+ parameters ); |
+ } |
+ else // Both segments are fully subdivided; now do line segments |
+ { |
+ double xlk = a.p.back()[X] - a.p[0][X]; |
+ double ylk = a.p.back()[Y] - a.p[0][Y]; |
+ double xnm = b.p.back()[X] - b.p[0][X]; |
+ double ynm = b.p.back()[Y] - b.p[0][Y]; |
+ double xmk = b.p[0][X] - a.p[0][X]; |
+ double ymk = b.p[0][Y] - a.p[0][Y]; |
+ double det = xnm * ylk - ynm * xlk; |
+ if( 1.0 + det == 1.0 ) |
+ return; |
+ else |
+ { |
+ double detinv = 1.0 / det; |
+ double s = ( xnm * ymk - ynm *xmk ) * detinv; |
+ double t = ( xlk * ymk - ylk * xmk ) * detinv; |
+ if( ( s < 0.0 ) || ( s > 1.0 ) || ( t < 0.0 ) || ( t > 1.0 ) ) |
+ return; |
+ parameters.push_back(std::pair<double, double>(t0 + s * ( t1 - t0 ), |
+ u0 + t * ( u1 - u0 ))); |
+ } |
+ } |
+} |
+ |
+inline double log4( double x ) { return log(x)/log(4.); } |
+ |
+/* |
+ * Wang's theorem is used to estimate the level of subdivision required, |
+ * but only if the bounding boxes interfere at the top level. |
+ * Assuming there is a possible intersection, recursively_intersect is |
+ * used to find all the parameters corresponding to intersection points. |
+ * these are then sorted and returned in an array. |
+ */ |
+ |
+double Lmax(Point p) { |
+ return std::max(fabs(p[X]), fabs(p[Y])); |
+} |
+ |
+unsigned wangs_theorem(OldBezier a) { |
+ return 12; // seems a good approximation! |
+ double la1 = Lmax( ( a.p[2] - a.p[1] ) - (a.p[1] - a.p[0]) ); |
+ double la2 = Lmax( ( a.p[3] - a.p[2] ) - (a.p[2] - a.p[1]) ); |
+ double l0 = std::max(la1, la2); |
+ unsigned ra; |
+ if( l0 * 0.75 * M_SQRT2 + 1.0 == 1.0 ) |
+ ra = 0; |
+ else |
+ ra = (unsigned)ceil( log4( M_SQRT2 * 6.0 / 8.0 * INV_EPS * l0 ) ); |
+ std::cout << ra << std::endl; |
+ return ra; |
+} |
+ |
+std::vector<std::pair<double, double> > find_intersections( OldBezier a, OldBezier b) |
+{ |
+ std::vector<std::pair<double, double> > parameters; |
+ if( intersect_BB( a, b ) ) |
+ { |
+ recursively_intersect( a, 0., 1., wangs_theorem(a), |
+ b, 0., 1., wangs_theorem(b), |
+ parameters); |
+ } |
+ std::sort(parameters.begin(), parameters.end()); |
+ return parameters; |
+} |
+}; |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/path.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/path.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/path.cpp (revision 14937) |
@@ -0,0 +1,258 @@ |
+/* |
+ * Path - Series of continuous curves |
+ * |
+ * Copyright 2007 MenTaLguY <mental@rydia.net> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ */ |
+ |
+#include "path.h" |
+ |
+#include "ord.h" |
+ |
+namespace Geom { |
+ |
+int CurveHelpers::root_winding(Curve const &c, Point p) { |
+ std::vector<double> ts = c.roots(p[Y], Y); |
+ |
+ if(ts.empty()) return 0; |
+ |
+ double const fudge = 0.01; //fudge factor used on first and last |
+ |
+ std::sort(ts.begin(), ts.end()); |
+ |
+ // winding determined by crossings at roots |
+ int wind=0; |
+ // previous time |
+ double pt = ts.front() - fudge; |
+ for ( std::vector<double>::iterator ti = ts.begin() |
+ ; ti != ts.end() |
+ ; ++ti ) |
+ { |
+ double t = *ti; |
+ if ( t <= 0. || t >= 1. ) continue; //skip endpoint roots |
+ if ( c.valueAt(t, X) > p[X] ) { // root is ray intersection |
+ // Get t of next: |
+ std::vector<double>::iterator next = ti; |
+ next++; |
+ double nt; |
+ if(next == ts.end()) nt = t + fudge; else nt = *next; |
+ |
+ // Check before in time and after in time for positions |
+ // Currently we're using the average times between next and previous segs |
+ Cmp after_to_ray = cmp(c.valueAt((t + nt) / 2, Y), p[Y]); |
+ Cmp before_to_ray = cmp(c.valueAt((t + pt) / 2, Y), p[Y]); |
+ // if y is included, these will have opposite values, giving order. |
+ Cmp dt = cmp(after_to_ray, before_to_ray); |
+ if(dt != EQUAL_TO) //Should always be true, but yah never know.. |
+ wind += dt; |
+ pt = t; |
+ } |
+ } |
+ |
+ return wind; |
+} |
+ |
+void Path::swap(Path &other) { |
+ std::swap(curves_, other.curves_); |
+ std::swap(closed_, other.closed_); |
+ std::swap(*final_, *other.final_); |
+ curves_[curves_.size()-1] = final_; |
+ other.curves_[other.curves_.size()-1] = other.final_; |
+} |
+ |
+Rect Path::boundsFast() const { |
+ Rect bounds=front().boundsFast(); |
+ for ( const_iterator iter=++begin(); iter != end() ; ++iter ) { |
+ bounds.unionWith(iter->boundsFast()); |
+ } |
+ return bounds; |
+} |
+ |
+Rect Path::boundsExact() const { |
+ Rect bounds=front().boundsExact(); |
+ for ( const_iterator iter=++begin(); iter != end() ; ++iter ) { |
+ bounds.unionWith(iter->boundsExact()); |
+ } |
+ return bounds; |
+} |
+ |
+template<typename iter> |
+iter inc(iter const &x, unsigned n) { |
+ iter ret = x; |
+ for(unsigned i = 0; i < n; i++) |
+ ret++; |
+ return ret; |
+} |
+ |
+//This assumes that you can't be perfect in your t-vals, and as such, tweaks the start |
+void Path::appendPortionTo(Path &ret, double from, double to) const { |
+ assert(from >= 0 && to >= 0); |
+ if(to == 0) to = size()+0.999999; |
+ if(from == to) { return; } |
+ double fi, ti; |
+ double ff = modf(from, &fi), tf = modf(to, &ti); |
+ if(tf == 0) { ti--; tf = 1; } |
+ const_iterator fromi = inc(begin(), (unsigned)fi); |
+ if(fi == ti && from < to) { |
+ Curve *v = fromi->portion(ff, tf); |
+ ret.append(*v); |
+ delete v; |
+ return; |
+ } |
+ const_iterator toi = inc(begin(), (unsigned)ti); |
+ if(ff != 1.) { |
+ Curve *fromv = fromi->portion(ff, 1.); |
+ //fromv->setInitial(ret.finalPoint()); |
+ ret.append(*fromv); |
+ delete fromv; |
+ } |
+ if(from >= to) { |
+ const_iterator ender = end(); |
+ if(ender->initialPoint() == ender->finalPoint()) ender++; |
+ ret.insert(ret.end(), ++fromi, ender); |
+ ret.insert(ret.end(), begin(), toi); |
+ } else { |
+ ret.insert(ret.end(), ++fromi, toi); |
+ } |
+ Curve *tov = toi->portion(0., tf); |
+ ret.append(*tov); |
+ delete tov; |
+} |
+ |
+const double eps = .1; |
+ |
+void Path::append(Curve const &curve) { |
+ if ( curves_.front() != final_ && !are_near(curve.initialPoint(), (*final_)[0], eps) ) { |
+ throwContinuityError(0); |
+ } |
+ do_append(curve.duplicate()); |
+} |
+ |
+void Path::append(D2<SBasis> const &curve) { |
+ if ( curves_.front() != final_ ) { |
+ for ( int i = 0 ; i < 2 ; ++i ) { |
+ if ( !are_near(curve[i][0][0], (*final_)[0][i], eps) ) { |
+ throwContinuityError(0); |
+ } |
+ } |
+ } |
+ do_append(new SBasisCurve(curve)); |
+} |
+ |
+void Path::do_update(Sequence::iterator first_replaced, |
+ Sequence::iterator last_replaced, |
+ Sequence::iterator first, |
+ Sequence::iterator last) |
+{ |
+ // note: modifies the contents of [first,last) |
+ |
+ check_continuity(first_replaced, last_replaced, first, last); |
+ delete_range(first_replaced, last_replaced); |
+ if ( ( last - first ) == ( last_replaced - first_replaced ) ) { |
+ std::copy(first, last, first_replaced); |
+ } else { |
+ // this approach depends on std::vector's behavior WRT iterator stability |
+ curves_.erase(first_replaced, last_replaced); |
+ curves_.insert(first_replaced, first, last); |
+ } |
+ |
+ if ( curves_.front() != final_ ) { |
+ final_->setPoint(0, back().finalPoint()); |
+ final_->setPoint(1, front().initialPoint()); |
+ } |
+} |
+ |
+void Path::do_append(Curve *curve) { |
+ if ( curves_.front() == final_ ) { |
+ final_->setPoint(1, curve->initialPoint()); |
+ } |
+ curves_.insert(curves_.end()-1, curve); |
+ final_->setPoint(0, curve->finalPoint()); |
+} |
+ |
+void Path::delete_range(Sequence::iterator first, Sequence::iterator last) { |
+ for ( Sequence::iterator iter=first ; iter != last ; ++iter ) { |
+ delete *iter; |
+ } |
+} |
+ |
+void Path::check_continuity(Sequence::iterator first_replaced, |
+ Sequence::iterator last_replaced, |
+ Sequence::iterator first, |
+ Sequence::iterator last) |
+{ |
+ if ( first != last ) { |
+ if ( first_replaced != curves_.begin() ) { |
+ if ( !are_near( (*first_replaced)->initialPoint(), (*first)->initialPoint(), eps ) ) { |
+ throwContinuityError(0); |
+ } |
+ } |
+ if ( last_replaced != (curves_.end()-1) ) { |
+ if ( !are_near( (*(last_replaced-1))->finalPoint(), (*(last-1))->finalPoint(), eps ) ) { |
+ throwContinuityError(0); |
+ } |
+ } |
+ } else if ( first_replaced != last_replaced && first_replaced != curves_.begin() && last_replaced != curves_.end()-1) { |
+ if ( !are_near((*first_replaced)->initialPoint(), (*(last_replaced-1))->finalPoint(), eps ) ) { |
+ throwContinuityError(0); |
+ } |
+ } |
+} |
+ |
+Rect SVGEllipticalArc::boundsFast() const { |
+ throwNotImplemented(0); |
+} |
+Rect SVGEllipticalArc::boundsExact() const { |
+ throwNotImplemented(0); |
+} |
+Rect SVGEllipticalArc::boundsLocal(Interval i, unsigned deg) const { |
+ throwNotImplemented(0); |
+} |
+ |
+std::vector<Point> SVGEllipticalArc::pointAndDerivatives(Coord t, unsigned n) const { |
+ throwNotImplemented(0); |
+} |
+ |
+std::vector<double> SVGEllipticalArc::roots(double v, Dim2 d) const { |
+ throwNotImplemented(0); |
+} |
+ |
+D2<SBasis> SVGEllipticalArc::toSBasis() const { |
+ return D2<SBasis>(Linear(initial_[X], final_[X]), Linear(initial_[Y], final_[Y])); |
+} |
+ |
+} |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(substatement-open . 0)) |
+ indent-tabs-mode:nil |
+ c-brace-offset:0 |
+ fill-column:99 |
+ End: |
+ vim: filetype=cpp:expandtab:shiftwidth=2:tabstop=8:softtabstop=2 : |
+*/ |
Index: trunk/Scribus/scribus/third_party/lib2geom/path-intersection.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/path-intersection.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/path-intersection.cpp (revision 14937) |
@@ -0,0 +1,597 @@ |
+#include "path-intersection.h" |
+ |
+#include "ord.h" |
+ |
+//for path_direction: |
+#include "sbasis-geometric.h" |
+ |
+namespace Geom { |
+ |
+/* This function computes the winding of the path, given a reference point. |
+ * Positive values correspond to counter-clockwise in the mathematical coordinate system, |
+ * and clockwise in screen coordinates. This particular implementation casts a ray in |
+ * the positive x direction. It iterates the path, checking for intersection with the |
+ * bounding boxes. If an intersection is found, the initial/final Y value of the curve is |
+ * used to derive a delta on the winding value. If the point is within the bounding box, |
+ * the curve specific winding function is called. |
+ */ |
+int winding(Path const &path, Point p) { |
+ //start on a segment which is not a horizontal line with y = p[y] |
+ Path::const_iterator start; |
+ for(Path::const_iterator iter = path.begin(); ; ++iter) { |
+ if(iter == path.end_closed()) { return 0; } |
+ if(iter->initialPoint()[Y]!=p[Y]) { start = iter; break; } |
+ if(iter->finalPoint()[Y]!=p[Y]) { start = iter; break; } |
+ if(iter->boundsFast().height()!=0.){ start = iter; break; } |
+ } |
+ int wind = 0; |
+ unsigned cnt = 0; |
+ bool starting = true; |
+ for (Path::const_iterator iter = start; iter != start || starting |
+ ; ++iter, iter = (iter == path.end_closed()) ? path.begin() : iter ) |
+ { |
+ cnt++; |
+ if(cnt > path.size()) return wind; //some bug makes this required |
+ starting = false; |
+ Rect bounds = iter->boundsFast(); |
+ Coord x = p[X], y = p[Y]; |
+ |
+ if(x > bounds.right() || !bounds[Y].contains(y)) continue; //ray doesn't intersect box |
+ |
+ Point final = iter->finalPoint(); |
+ Point initial = iter->initialPoint(); |
+ Cmp final_to_ray = cmp(final[Y], y); |
+ Cmp initial_to_ray = cmp(initial[Y], y); |
+ |
+ // if y is included, these will have opposite values, giving order. |
+ Cmp c = cmp(final_to_ray, initial_to_ray); |
+ if(x < bounds.left()) { |
+ // ray goes through bbox |
+ // winding delta determined by position of endpoints |
+ if(final_to_ray != EQUAL_TO) { |
+ wind += int(c); // GT = counter-clockwise = 1; LT = clockwise = -1; EQ = not-included = 0 |
+ //std::cout << int(c) << " "; |
+ goto cont; |
+ } |
+ } else { |
+ //inside bbox, use custom per-curve winding thingie |
+ int delt = iter->winding(p); |
+ wind += delt; |
+ //std::cout << "n" << delt << " "; |
+ } |
+ //Handling the special case of an endpoint on the ray: |
+ if(final[Y] == y) { |
+ //Traverse segments until it breaks away from y |
+ //99.9% of the time this will happen the first go |
+ Path::const_iterator next = iter; |
+ next++; |
+ for(; ; next++) { |
+ if(next == path.end_closed()) next = path.begin(); |
+ Rect bnds = next->boundsFast(); |
+ //TODO: X considerations |
+ if(bnds.height() > 0) { |
+ //It has diverged |
+ if(bnds.contains(p)) { |
+ const double fudge = 0.01; |
+ if(cmp(y, next->valueAt(fudge, Y)) == initial_to_ray) { |
+ wind += int(c); |
+ // std::cout << "!!!!!" << int(c) << " "; |
+ } |
+ iter = next; // No increment, as the rest of the thing hasn't been counted. |
+ } else { |
+ Coord ny = next->initialPoint()[Y]; |
+ if(cmp(y, ny) == initial_to_ray) { |
+ //Is a continuation through the ray, so counts windingwise |
+ wind += int(c); |
+ // std::cout << "!!!!!" << int(c) << " "; |
+ } |
+ iter = ++next; |
+ } |
+ goto cont; |
+ } |
+ if(next==start) return wind; |
+ } |
+ //Looks like it looped, which means everything's flat |
+ return 0; |
+ } |
+ |
+ cont:(void)0; |
+ } |
+ return wind; |
+} |
+ |
+/* This function should only be applied to simple paths (regions), as otherwise |
+ * a boolean winding direction is undefined. It returns true for fill, false for |
+ * hole. Defaults to using the sign of area when it reaches funny cases. |
+ */ |
+bool path_direction(Path const &p) { |
+ if(p.empty()) return false; |
+ //could probably be more efficient, but this is a quick job |
+ double y = p.initialPoint()[Y]; |
+ double x = p.initialPoint()[X]; |
+ Cmp res = cmp(p[0].finalPoint()[Y], y); |
+ goto doh; |
+ for(unsigned i = 1; i <= p.size(); i++) { |
+ Cmp final_to_ray = cmp(p[i].finalPoint()[Y], y); |
+ Cmp initial_to_ray = cmp(p[i].initialPoint()[Y], y); |
+ // if y is included, these will have opposite values, giving order. |
+ Cmp c = cmp(final_to_ray, initial_to_ray); |
+ if(c != EQUAL_TO) { |
+ std::vector<double> rs = p[i].roots(y, Y); |
+ for(unsigned j = 0; j < rs.size(); j++) { |
+ double nx = p[i].valueAt(rs[j], X); |
+ if(nx > x) { |
+ x = nx; |
+ res = c; |
+ } |
+ } |
+ } else if(final_to_ray == EQUAL_TO) goto doh; |
+ } |
+ return res < 0; |
+ |
+ doh: |
+ //Otherwise fallback on area |
+ |
+ Piecewise<D2<SBasis> > pw = p.toPwSb(); |
+ double area; |
+ Point centre; |
+ Geom::centroid(pw, centre, area); |
+ return area > 0; |
+} |
+ |
+//pair intersect code based on njh's pair-intersect |
+ |
+// A little sugar for appending a list to another |
+template<typename T> |
+void append(T &a, T const &b) { |
+ a.insert(a.end(), b.begin(), b.end()); |
+} |
+ |
+/* Finds the intersection between the lines defined by A0 & A1, and B0 & B1. |
+ * Returns through the last 3 parameters, returning the t-values on the lines |
+ * and the cross-product of the deltas (a useful byproduct). The return value |
+ * indicates if the time values are within their proper range on the line segments. |
+ */ |
+bool |
+linear_intersect(Point A0, Point A1, Point B0, Point B1, |
+ double &tA, double &tB, double &det) { |
+ // kramers rule as cross products |
+ Point Ad = A1 - A0, |
+ Bd = B1 - B0, |
+ d = B0 - A0; |
+ det = cross(Ad, Bd); |
+ if( 1.0 + det == 1.0 ) |
+ return false; |
+ else |
+ { |
+ double detinv = 1.0 / det; |
+ tA = cross(d, Bd) * detinv; |
+ tB = cross(d, Ad) * detinv; |
+ return tA >= 0. && tA <= 1. && tB >= 0. && tB <= 1.; |
+ } |
+} |
+ |
+/* This uses the local bounds functions of curves to generically intersect two. |
+ * It passes in the curves, time intervals, and keeps track of depth, while |
+ * returning the results through the Crossings parameter. |
+ */ |
+void pair_intersect(Curve const & A, double Al, double Ah, |
+ Curve const & B, double Bl, double Bh, |
+ Crossings &ret, unsigned depth=0) { |
+ // std::cout << depth << "(" << Al << ", " << Ah << ")\n"; |
+ Rect Ar = A.boundsLocal(Interval(Al, Ah)); |
+ if(Ar.isEmpty()) return; |
+ |
+ Rect Br = B.boundsLocal(Interval(Bl, Bh)); |
+ if(Br.isEmpty()) return; |
+ |
+ if(!Ar.intersects(Br)) return; |
+ |
+ //Checks the general linearity of the function |
+ if((depth > 12)) { // || (A.boundsLocal(Interval(Al, Ah), 1).maxExtent() < 0.1 |
+ //&& B.boundsLocal(Interval(Bl, Bh), 1).maxExtent() < 0.1)) { |
+ double tA, tB, c; |
+ if(linear_intersect(A.pointAt(Al), A.pointAt(Ah), |
+ B.pointAt(Bl), B.pointAt(Bh), |
+ tA, tB, c)) { |
+ tA = tA * (Ah - Al) + Al; |
+ tB = tB * (Bh - Bl) + Bl; |
+ if(depth % 2) |
+ ret.push_back(Crossing(tB, tA, c < 0)); |
+ else |
+ ret.push_back(Crossing(tA, tB, c > 0)); |
+ return; |
+ } |
+ } |
+ if(depth > 12) return; |
+ double mid = (Bl + Bh)/2; |
+ pair_intersect(B, Bl, mid, |
+ A, Al, Ah, |
+ ret, depth+1); |
+ pair_intersect(B, mid, Bh, |
+ A, Al, Ah, |
+ ret, depth+1); |
+} |
+ |
+// A simple wrapper around pair_intersect |
+Crossings SimpleCrosser::crossings(Curve const &a, Curve const &b) { |
+ Crossings ret; |
+ pair_intersect(a, 0, 1, b, 0, 1, ret); |
+ return ret; |
+} |
+ |
+/* Takes two paths and time ranges on them, with the invariant that the |
+ * paths are monotonic on the range. Splits A when the linear intersection |
+ * doesn't exist or is inaccurate. Uses the fact that it is monotonic to |
+ * do very fast local bounds. |
+ */ |
+void mono_pair(Path const &A, double Al, double Ah, |
+ Path const &B, double Bl, double Bh, |
+ Crossings &ret, double tol, unsigned depth = 0) { |
+ if( Al >= Ah || Bl >= Bh) return; |
+ // std::cout << " " << depth << "[" << Al << ", " << Ah << "]" << "[" << Bl << ", " << Bh << "]"; |
+ |
+ Point A0 = A.pointAt(Al), A1 = A.pointAt(Ah), |
+ B0 = B.pointAt(Bl), B1 = B.pointAt(Bh); |
+ //inline code that this implies? (without rect/interval construction) |
+ if(!Rect(A0, A1).intersects(Rect(B0, B1)) || A0 == A1 || B0 == B1) return; |
+ |
+ //Checks the general linearity of the function |
+ //if((depth > 12) || (A.boundsLocal(Interval(Al, Ah), 1).maxExtent() < 0.1 |
+ // && B.boundsLocal(Interval(Bl, Bh), 1).maxExtent() < 0.1)) { |
+ double tA, tB, c; |
+ if(linear_intersect(A0, A1, B0, B1, |
+ tA, tB, c)) { |
+ tA = tA * (Ah - Al) + Al; |
+ tB = tB * (Bh - Bl) + Bl; |
+ if(depth % 2) |
+ ret.push_back(Crossing(tB, tA, c < 0)); |
+ else |
+ ret.push_back(Crossing(tA, tB, c > 0)); |
+ return; |
+ } |
+ //} |
+ if(depth > 12) return; |
+ double mid = (Bl + Bh)/2; |
+ mono_pair(B, Bl, mid, |
+ A, Al, Ah, |
+ ret, depth+1); |
+ mono_pair(B, mid, Bh, |
+ A, Al, Ah, |
+ ret, depth+1); |
+} |
+ |
+// This returns the times when the x or y derivative is 0 in the curve. |
+std::vector<double> curve_mono_splits(Curve const &d) { |
+ std::vector<double> rs = d.roots(0, X); |
+ append(rs, d.roots(0, Y)); |
+ std::sort(rs.begin(), rs.end()); |
+ return rs; |
+} |
+ |
+// Convenience function to add a value to each entry in a vector of doubles. |
+std::vector<double> offset_doubles(std::vector<double> const &x, double offs) { |
+ std::vector<double> ret; |
+ for(unsigned i = 0; i < x.size(); i++) { |
+ ret.push_back(x[i] + offs); |
+ } |
+ return ret; |
+} |
+ |
+/* Finds all the monotonic splits for a path. Only includes the split between |
+ * curves if they switch derivative directions at that point. |
+ */ |
+std::vector<double> path_mono_splits(Path const &p) { |
+ std::vector<double> ret; |
+ if(p.empty()) return ret; |
+ ret.push_back(0); |
+ |
+ Curve* deriv = p[0].derivative(); |
+ append(ret, curve_mono_splits(*deriv)); |
+ delete deriv; |
+ |
+ int pdx=2, pdy=2; //Previous derivative direction |
+ for(unsigned i = 0; i <= p.size(); i++) { |
+ deriv = p[i].derivative(); |
+ std::vector<double> spl = offset_doubles(curve_mono_splits(*deriv), i); |
+ delete deriv; |
+ int dx = p[i].initialPoint()[X] > (spl.empty()? p[i].finalPoint()[X] : |
+ p.valueAt(spl.front(), X)); |
+ int dy = p[i].initialPoint()[Y] > (spl.empty()? p[i].finalPoint()[Y] : |
+ p.valueAt(spl.front(), Y)); |
+ //The direction changed, include the split time |
+ if(dx != pdx || dy != pdy) { |
+ ret.push_back(i); |
+ pdx = dx; pdy = dy; |
+ } |
+ append(ret, spl); |
+ } |
+ return ret; |
+} |
+ |
+/* Applies path_mono_splits to multiple paths, and returns the results such that |
+ * time-set i corresponds to Path i. |
+ */ |
+std::vector<std::vector<double> > paths_mono_splits(std::vector<Path> const &ps) { |
+ std::vector<std::vector<double> > ret; |
+ for(unsigned i = 0; i < ps.size(); i++) |
+ ret.push_back(path_mono_splits(ps[i])); |
+ return ret; |
+} |
+ |
+/* Processes the bounds for a list of paths and a list of splits on them, yielding a list of rects for each. |
+ * Each entry i corresponds to path i of the input. The number of rects in each entry is guaranteed to be the |
+ * number of splits for that path, subtracted by one. |
+ */ |
+std::vector<std::vector<Rect> > split_bounds(std::vector<Path> const &p, std::vector<std::vector<double> > splits) { |
+ std::vector<std::vector<Rect> > ret; |
+ for(unsigned i = 0; i < p.size(); i++) { |
+ std::vector<Rect> res; |
+ for(unsigned j = 1; j < splits[i].size(); j++) |
+ res.push_back(Rect(p[i].pointAt(splits[i][j-1]), p[i].pointAt(splits[i][j]))); |
+ ret.push_back(res); |
+ } |
+ return ret; |
+} |
+ |
+/* This is the main routine of "MonoCrosser", and implements a monotonic strategy on multiple curves. |
+ * Finds crossings between two sets of paths, yielding a CrossingSet. [0, a.size()) of the return correspond |
+ * to the sorted crossings of a with paths of b. The rest of the return, [a.size(), a.size() + b.size()], |
+ * corresponds to the sorted crossings of b with paths of a. |
+ * |
+ * This function does two sweeps, one on the bounds of each path, and after that cull, one on the curves within. |
+ * This leads to a certain amount of code complexity, however, most of that is factored into the above functions |
+ */ |
+CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path> const &b) { |
+ if(b.empty()) return CrossingSet(a.size(), Crossings()); |
+ CrossingSet results(a.size() + b.size(), Crossings()); |
+ if(a.empty()) return results; |
+ |
+ std::vector<std::vector<double> > splits_a = paths_mono_splits(a), splits_b = paths_mono_splits(b); |
+ std::vector<std::vector<Rect> > bounds_a = split_bounds(a, splits_a), bounds_b = split_bounds(b, splits_b); |
+ |
+ std::vector<Rect> bounds_a_union, bounds_b_union; |
+ for(unsigned i = 0; i < bounds_a.size(); i++) bounds_a_union.push_back(union_list(bounds_a[i])); |
+ for(unsigned i = 0; i < bounds_b.size(); i++) bounds_b_union.push_back(union_list(bounds_b[i])); |
+ |
+ std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds_a_union, bounds_b_union); |
+ Crossings n; |
+ for(unsigned i = 0; i < cull.size(); i++) { |
+ for(unsigned jx = 0; jx < cull[i].size(); jx++) { |
+ unsigned j = cull[i][jx]; |
+ unsigned jc = j + a.size(); |
+ Crossings res; |
+ |
+ //Sweep of the monotonic portions |
+ std::vector<std::vector<unsigned> > cull2 = sweep_bounds(bounds_a[i], bounds_b[j]); |
+ for(unsigned k = 0; k < cull2.size(); k++) { |
+ for(unsigned lx = 0; lx < cull2[k].size(); lx++) { |
+ unsigned l = cull2[k][lx]; |
+ mono_pair(a[i], splits_a[i][k-1], splits_a[i][k], |
+ b[j], splits_b[j][l-1], splits_b[j][l], |
+ res, .1); |
+ } |
+ } |
+ |
+ for(unsigned k = 0; k < res.size(); k++) { res[k].a = i; res[k].b = jc; } |
+ |
+ merge_crossings(results[i], res, i); |
+ merge_crossings(results[i], res, jc); |
+ } |
+ } |
+ |
+ return results; |
+} |
+ |
+/* This function is similar codewise to the MonoCrosser, the main difference is that it deals with |
+ * only one set of paths and includes self intersection |
+CrossingSet crossings_among(std::vector<Path> const &p) { |
+ CrossingSet results(p.size(), Crossings()); |
+ if(p.empty()) return results; |
+ |
+ std::vector<std::vector<double> > splits = paths_mono_splits(p); |
+ std::vector<std::vector<Rect> > prs = split_bounds(p, splits); |
+ std::vector<Rect> rs; |
+ for(unsigned i = 0; i < prs.size(); i++) rs.push_back(union_list(prs[i])); |
+ |
+ std::vector<std::vector<unsigned> > cull = sweep_bounds(rs); |
+ |
+ //we actually want to do the self-intersections, so add em in: |
+ for(unsigned i = 0; i < cull.size(); i++) cull[i].push_back(i); |
+ |
+ for(unsigned i = 0; i < cull.size(); i++) { |
+ for(unsigned jx = 0; jx < cull[i].size(); jx++) { |
+ unsigned j = cull[i][jx]; |
+ Crossings res; |
+ |
+ //Sweep of the monotonic portions |
+ std::vector<std::vector<unsigned> > cull2 = sweep_bounds(prs[i], prs[j]); |
+ for(unsigned k = 0; k < cull2.size(); k++) { |
+ for(unsigned lx = 0; lx < cull2[k].size(); lx++) { |
+ unsigned l = cull2[k][lx]; |
+ mono_pair(p[i], splits[i][k-1], splits[i][k], |
+ p[j], splits[j][l-1], splits[j][l], |
+ res, .1); |
+ } |
+ } |
+ |
+ for(unsigned k = 0; k < res.size(); k++) { res[k].a = i; res[k].b = j; } |
+ |
+ merge_crossings(results[i], res, i); |
+ merge_crossings(results[j], res, j); |
+ } |
+ } |
+ |
+ return results; |
+} |
+*/ |
+ |
+ |
+Crossings curve_self_crossings(Curve const &a) { |
+ Crossings res; |
+ std::vector<double> spl; |
+ spl.push_back(0); |
+ append(spl, curve_mono_splits(a)); |
+ spl.push_back(1); |
+ for(unsigned i = 1; i < spl.size(); i++) |
+ for(unsigned j = i+1; j < spl.size(); j++) |
+ pair_intersect(a, spl[i-1], spl[i], a, spl[j-1], spl[j], res); |
+ return res; |
+} |
+ |
+/* |
+void mono_curve_intersect(Curve const & A, double Al, double Ah, |
+ Curve const & B, double Bl, double Bh, |
+ Crossings &ret, unsigned depth=0) { |
+ // std::cout << depth << "(" << Al << ", " << Ah << ")\n"; |
+ Point A0 = A.pointAt(Al), A1 = A.pointAt(Ah), |
+ B0 = B.pointAt(Bl), B1 = B.pointAt(Bh); |
+ //inline code that this implies? (without rect/interval construction) |
+ if(!Rect(A0, A1).intersects(Rect(B0, B1)) || A0 == A1 || B0 == B1) return; |
+ |
+ //Checks the general linearity of the function |
+ if((depth > 12) || (A.boundsLocal(Interval(Al, Ah), 1).maxExtent() < 0.1 |
+ && B.boundsLocal(Interval(Bl, Bh), 1).maxExtent() < 0.1)) { |
+ double tA, tB, c; |
+ if(linear_intersect(A0, A1, B0, B1, tA, tB, c)) { |
+ tA = tA * (Ah - Al) + Al; |
+ tB = tB * (Bh - Bl) + Bl; |
+ if(depth % 2) |
+ ret.push_back(Crossing(tB, tA, c < 0)); |
+ else |
+ ret.push_back(Crossing(tA, tB, c > 0)); |
+ return; |
+ } |
+ } |
+ if(depth > 12) return; |
+ double mid = (Bl + Bh)/2; |
+ mono_curve_intersect(B, Bl, mid, |
+ A, Al, Ah, |
+ ret, depth+1); |
+ mono_curve_intersect(B, mid, Bh, |
+ A, Al, Ah, |
+ ret, depth+1); |
+} |
+ |
+std::vector<std::vector<double> > curves_mono_splits(Path const &p) { |
+ std::vector<std::vector<double> > ret; |
+ for(unsigned i = 0; i <= p.size(); i++) { |
+ std::vector<double> spl; |
+ spl.push_back(0); |
+ append(spl, curve_mono_splits(p[i])); |
+ spl.push_back(1); |
+ ret.push_back(spl); |
+ } |
+} |
+ |
+std::vector<std::vector<Rect> > curves_split_bounds(Path const &p, std::vector<std::vector<double> > splits) { |
+ std::vector<std::vector<Rect> > ret; |
+ for(unsigned i = 0; i < splits.size(); i++) { |
+ std::vector<Rect> res; |
+ for(unsigned j = 1; j < splits[i].size(); j++) |
+ res.push_back(Rect(p.pointAt(splits[i][j-1]+i), p.pointAt(splits[i][j]+i))); |
+ ret.push_back(res); |
+ } |
+ return ret; |
+} |
+ |
+Crossings path_self_crossings(Path const &p) { |
+ Crossings ret; |
+ std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds(p)); |
+ std::vector<std::vector<double> > spl = curves_mono_splits(p); |
+ std::vector<std::vector<Rect> > bnds = curves_split_bounds(p, spl); |
+ for(unsigned i = 0; i < cull.size(); i++) { |
+ Crossings res; |
+ for(unsigned k = 1; k < spl[i].size(); k++) |
+ for(unsigned l = k+1; l < spl[i].size(); l++) |
+ mono_curve_intersect(p[i], spl[i][k-1], spl[i][k], p[i], spl[i][l-1], spl[i][l], res); |
+ offset_crossings(res, i, i); |
+ append(ret, res); |
+ for(unsigned jx = 0; jx < cull[i].size(); jx++) { |
+ unsigned j = cull[i][jx]; |
+ res.clear(); |
+ |
+ std::vector<std::vector<unsigned> > cull2 = sweep_bounds(bnds[i], bnds[j]); |
+ for(unsigned k = 0; k < cull2.size(); k++) { |
+ for(unsigned lx = 0; lx < cull2[k].size(); lx++) { |
+ unsigned l = cull2[k][lx]; |
+ mono_curve_intersect(p[i], spl[i][k-1], spl[i][k], p[j], spl[j][l-1], spl[j][l], res); |
+ } |
+ } |
+ |
+ //if(fabs(int(i)-j) == 1 || fabs(int(i)-j) == p.size()-1) { |
+ Crossings res2; |
+ for(unsigned k = 0; k < res.size(); k++) { |
+ if(res[k].ta != 0 && res[k].ta != 1 && res[k].tb != 0 && res[k].tb != 1) { |
+ res.push_back(res[k]); |
+ } |
+ } |
+ res = res2; |
+ //} |
+ offset_crossings(res, i, j); |
+ append(ret, res); |
+ } |
+ } |
+ return ret; |
+} |
+*/ |
+ |
+Crossings self_crossings(Path const &p) { |
+ Crossings ret; |
+ std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds(p)); |
+ for(unsigned i = 0; i < cull.size(); i++) { |
+ Crossings res = curve_self_crossings(p[i]); |
+ offset_crossings(res, i, i); |
+ append(ret, res); |
+ for(unsigned jx = 0; jx < cull[i].size(); jx++) { |
+ unsigned j = cull[i][jx]; |
+ res.clear(); |
+ pair_intersect(p[i], 0, 1, p[j], 0, 1, res); |
+ |
+ //if(fabs(int(i)-j) == 1 || fabs(int(i)-j) == p.size()-1) { |
+ Crossings res2; |
+ for(unsigned k = 0; k < res.size(); k++) { |
+ if(res[k].ta != 0 && res[k].ta != 1 && res[k].tb != 0 && res[k].tb != 1) { |
+ res2.push_back(res[k]); |
+ } |
+ } |
+ res = res2; |
+ //} |
+ offset_crossings(res, i, j); |
+ append(ret, res); |
+ } |
+ } |
+ return ret; |
+} |
+ |
+void flip_crossings(Crossings &crs) { |
+ for(unsigned i = 0; i < crs.size(); i++) |
+ crs[i] = Crossing(crs[i].tb, crs[i].ta, crs[i].b, crs[i].a, !crs[i].dir); |
+} |
+ |
+CrossingSet crossings_among(std::vector<Path> const &p) { |
+ CrossingSet results(p.size(), Crossings()); |
+ if(p.empty()) return results; |
+ |
+ SimpleCrosser cc; |
+ |
+ std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds(p)); |
+ for(unsigned i = 0; i < cull.size(); i++) { |
+ Crossings res = self_crossings(p[i]); |
+ for(unsigned k = 0; k < res.size(); k++) { res[k].a = res[k].b = i; } |
+ merge_crossings(results[i], res, i); |
+ flip_crossings(res); |
+ merge_crossings(results[i], res, i); |
+ for(unsigned jx = 0; jx < cull[i].size(); jx++) { |
+ unsigned j = cull[i][jx]; |
+ |
+ Crossings res = cc.crossings(p[i], p[j]); |
+ for(unsigned k = 0; k < res.size(); k++) { res[k].a = i; res[k].b = j; } |
+ merge_crossings(results[i], res, i); |
+ merge_crossings(results[j], res, j); |
+ } |
+ } |
+ return results; |
+} |
+ |
+} |
Index: trunk/Scribus/scribus/third_party/lib2geom/exception.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/exception.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/exception.h (revision 14937) |
@@ -0,0 +1,116 @@ |
+#ifndef LIB2GEOM_EXCEPTION_HEADER |
+#define LIB2GEOM_EXCEPTION_HEADER |
+ |
+/** Defines the different types of exceptions that 2geom can throw. |
+ * |
+ * Copyright 2007 Johan Engelen <goejendaagh@zonnet.nl> |
+ * |
+ * 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 <exception> |
+#include <sstream> |
+#include <string> |
+ |
+namespace Geom { |
+ |
+// Base exception class, all 2geom exceptions should be derrived from this one. |
+class Exception : public std::exception { |
+public: |
+ Exception(const char * message, const char *file, const int line) { |
+ std::ostringstream os; |
+ os << "lib2geom exception: " << message << " (" << file << ":" << line << ")"; |
+ msgstr = os.str(); |
+ } |
+ |
+ virtual ~Exception() throw() {} // necessary to destroy the string object!!! |
+ |
+ virtual const char* what() const throw () { |
+ return msgstr.c_str(); |
+ } |
+protected: |
+ std::string msgstr; |
+}; |
+#define throwException(message) throw(Geom::Exception(message, __FILE__, __LINE__)) |
+ |
+//----------------------------------------------------------------------- |
+// Two main exception classes: LogicalError and RangeError. |
+// Logical errors are 2geom faults/bugs, RangeErrors are 'user' faults. |
+// This way, the 'user' can distinguish between groups of exceptions |
+// ('user' is the coder that uses lib2geom) |
+class LogicalError : public Exception { |
+public: |
+ LogicalError(const char * message, const char *file, const int line) |
+ : Exception(message, file, line) {} |
+}; |
+#define throwLogicalError(message) throw(LogicalError(message, __FILE__, __LINE__)) |
+ |
+class RangeError : public Exception { |
+public: |
+ RangeError(const char * message, const char *file, const int line) |
+ : Exception(message, file, line) {} |
+}; |
+#define throwRangeError(message) throw(RangeError(message, __FILE__, __LINE__)) |
+ |
+//----------------------------------------------------------------------- |
+// Special case exceptions. Best used with the defines :) |
+ |
+class NotImplemented : public LogicalError { |
+public: |
+ NotImplemented(const char *file, const int line) |
+ : LogicalError("Method not implemented", file, line) {} |
+}; |
+#define throwNotImplemented(i) throw(NotImplemented(__FILE__, __LINE__)) |
+ |
+class InvariantsViolation : public LogicalError { |
+public: |
+ InvariantsViolation(const char *file, const int line) |
+ : LogicalError("Invariants violation", file, line) {} |
+}; |
+#define throwInvariantsViolation(i) throw(InvariantsViolation(__FILE__, __LINE__)) |
+#define assert_invariants(e) ((e) ? (void)0 : throwInvariantsViolation(0)) |
+ |
+class NotInvertible : public RangeError { |
+public: |
+ NotInvertible(const char *file, const int line) |
+ : RangeError("Function does not have a unique inverse", file, line) {} |
+}; |
+#define throwNotInvertible(i) throw(NotInvertible(__FILE__, __LINE__)) |
+ |
+class ContinuityError : public RangeError { |
+public: |
+ ContinuityError(const char *file, const int line) |
+ : RangeError("Non-contiguous path", file, line) {} |
+}; |
+#define throwContinuityError(i) throw(ContinuityError(__FILE__, __LINE__)) |
+ |
+struct SVGPathParseError : public std::exception { |
+ char const *what() const throw() { return "parse error"; } |
+}; |
+ |
+ |
+} // namespace Geom |
+ |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/transforms.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/transforms.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/transforms.h (revision 14937) |
@@ -0,0 +1,135 @@ |
+#ifndef SEEN_Geom_TRANSFORMS_H |
+#define SEEN_Geom_TRANSFORMS_H |
+ |
+#include "matrix.h" |
+#include <cmath> |
+ |
+#ifndef M_PI |
+#define M_PI 3.14159265359; |
+#endif |
+ |
+namespace Geom { |
+ |
+template <typename T> |
+struct TransformConcept { |
+ T t; |
+ Matrix m; |
+ Point p; |
+ void constraints() { |
+ m = t; //implicit conversion |
+ t = t.inverse(); |
+ p = p * t; |
+ t = t * t; |
+ } |
+}; |
+ |
+ |
+class Rotate; |
+class Translate { |
+ private: |
+ Translate(); |
+ Point vec; |
+ public: |
+ explicit Translate(Point const &p) : vec(p) {} |
+ explicit Translate(Coord const x, Coord const y) : vec(x, y) {} |
+ inline operator Matrix() const { return Matrix(1, 0, 0, 1, vec[X], vec[Y]); } |
+ |
+ inline Coord operator[](Dim2 const dim) const { return vec[dim]; } |
+ inline Coord operator[](unsigned const dim) const { return vec[dim]; } |
+ inline bool operator==(Translate const &o) const { return vec == o.vec; } |
+ inline bool operator!=(Translate const &o) const { return vec != o.vec; } |
+ |
+ inline Translate inverse() const { return Translate(-vec); } |
+ |
+ friend Point operator*(Point const &v, Translate const &t); |
+ inline Translate operator*(Translate const &b) const { return Translate(vec + b.vec); } |
+ |
+ friend Matrix operator*(Translate const &t, Rotate const &r); |
+}; |
+ |
+inline Point operator*(Point const &v, Translate const &t) { return v + t.vec; } |
+ |
+class Scale { |
+ private: |
+ Point vec; |
+ Scale(); |
+ public: |
+ explicit Scale(Point const &p) : vec(p) {} |
+ Scale(Coord const x, Coord const y) : vec(x, y) {} |
+ explicit Scale(Coord const s) : vec(s, s) {} |
+ inline operator Matrix() const { return Matrix(vec[X], 0, 0, vec[Y], 0, 0); } |
+ |
+ inline Coord operator[](Dim2 const d) const { return vec[d]; } |
+ inline Coord operator[](unsigned const d) const { return vec[d]; } |
+ //TODO: should we keep these mutators? add them to the other transforms? |
+ inline Coord &operator[](Dim2 const d) { return vec[d]; } |
+ inline Coord &operator[](unsigned const d) { return vec[d]; } |
+ inline bool operator==(Scale const &o) const { return vec == o.vec; } |
+ inline bool operator!=(Scale const &o) const { return vec != o.vec; } |
+ |
+ inline Scale inverse() const { return Scale(1./vec[0], 1./vec[1]); } |
+ |
+ friend Point operator*(Point const &v, Translate const &t); |
+ inline Scale operator*(Scale const &b) const { return Scale(vec[X]*b[X], vec[Y]*b[Y]); } |
+}; |
+ |
+inline Point operator*(Point const &p, Scale const &s) { return Point(p[X] * s[X], p[Y] * s[Y]); } |
+ |
+/** Notionally an Geom::Matrix corresponding to rotation about the origin. |
+ Behaves like Geom::Matrix for multiplication. |
+**/ |
+class Rotate { |
+ private: |
+ Rotate(); |
+ Point vec; |
+ public: |
+ explicit Rotate(Coord theta) : vec(std::cos(theta), std::sin(theta)) {} |
+ Rotate(Point const &p) {Point v = p; v.normalize(); vec = v;} //TODO: UGLY! |
+ explicit Rotate(Coord x, Coord y) { Rotate(Point(x, y)); } |
+ inline operator Matrix() const { return Matrix(vec[X], vec[Y], vec[Y], -vec[X], 0, 0); } |
+ |
+ inline Coord operator[](Dim2 const dim) const { return vec[dim]; } |
+ inline Coord operator[](unsigned const dim) const { return vec[dim]; } |
+ inline bool operator==(Rotate const &o) const { return vec == o.vec; } |
+ inline bool operator!=(Rotate const &o) const { return vec != o.vec; } |
+ |
+ Rotate inverse() const { return Rotate( Point(vec[X], -vec[Y]) ); } |
+ static Rotate from_degrees(Coord deg) { |
+ Coord rad = (deg / 180.0) * M_PI; |
+ return Rotate(rad); |
+ } |
+ |
+ friend Point operator*(Point const &v, Rotate const &r); |
+ inline Rotate operator*(Rotate const &b) const { return Rotate(vec * b); } |
+}; |
+ |
+inline Point operator*(Point const &v, Rotate const &r) { return v ^ r.vec; } |
+ |
+Matrix operator*(Translate const &t, Scale const &s); |
+Matrix operator*(Translate const &t, Rotate const &r); |
+ |
+Matrix operator*(Scale const &s, Translate const &t); |
+Matrix operator*(Scale const &s, Matrix const &m); |
+ |
+Matrix operator*(Matrix const &m, Translate const &t); |
+Matrix operator*(Matrix const &m, Scale const &s); |
+Matrix operator*(Matrix const &m, Rotate const &r); |
+Matrix operator*(Matrix const &m1, Matrix const &m2); |
+ |
+//TODO: matrix to trans/scale/rotate |
+ |
+} /* namespace Geom */ |
+ |
+ |
+#endif /* !SEEN_Geom_TRANSFORMS_H */ |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/isnan.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/isnan.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/isnan.h (revision 14937) |
@@ -0,0 +1,64 @@ |
+#ifndef __ISNAN_H__ |
+#define __ISNAN_H__ |
+ |
+/* |
+ * Temporary fix for various misdefinitions of isnan(). |
+ * isnan() is becoming undef'd in some .h files. |
+ * #include this last in your .cpp file to get it right. |
+ * |
+ * The problem is that isnan and isfinite are part of C99 but aren't part of |
+ * the C++ standard (which predates C99). |
+ * |
+ * Authors: |
+ * Inkscape groupies and obsessive-compulsives |
+ * |
+ * Copyright (C) 2004 authors |
+ * |
+ * Released under GNU GPL, read the file 'COPYING' for more information |
+ * |
+ * 2005 modification hereby placed in public domain. Probably supercedes the 2004 copyright |
+ * for the code itself. |
+ */ |
+ |
+#include <cmath> |
+#include <float.h> |
+/* You might try changing the above to <cmath> if you have problems. |
+ * Whether you use math.h or cmath, you may need to edit the .cpp file |
+ * and/or other .h files to use the same header file. |
+ */ |
+ |
+#if defined(__isnan) |
+# define is_nan(_a) (__isnan(_a)) |
+#elif defined(__APPLE__) && __GNUC__ == 3 |
+# define is_nan(_a) (__isnan(_a)) /* MacOSX/Darwin definition < 10.4 */ |
+#elif defined(WIN32) || defined(_isnan) |
+# define is_nan(_a) (_isnan(_a)) /* Win32 definition */ |
+#elif defined(isnan) || defined(__FreeBSD__) |
+# define is_nan(_a) (isnan(_a)) /* GNU definition */ |
+#else |
+# define is_nan(_a) (std::isnan(_a)) |
+#endif |
+/* If the above doesn't work, then try (a != a). |
+ * Also, please report a bug as per http://www.inkscape.org/report_bugs.php, |
+ * giving information about what platform and compiler version you're using. |
+ */ |
+ |
+ |
+#if defined(__isfinite) |
+# define is_finite(_a) (__isfinite(_a)) |
+#elif defined(__APPLE__) && __GNUC__ == 3 |
+# define is_finite(_a) (__isfinite(_a)) /* MacOSX/Darwin definition < 10.4 */ |
+#elif defined(isfinite) |
+# define is_finite(_a) (isfinite(_a)) |
+#elif defined(_MSC_VER) |
+# define is_finite(_a) (_finite(_a) && !_isnan(_a)) |
+#else |
+# define is_finite(_a) (std::isfinite(_a)) |
+#endif |
+/* If the above doesn't work, then try (finite(_a) && !isNaN(_a)) or (!isNaN((_a) - (_a))). |
+ * Also, please report a bug as per http://www.inkscape.org/report_bugs.php, |
+ * giving information about what platform and compiler version you're using. |
+ */ |
+ |
+ |
+#endif /* __ISNAN_H__ */ |
Index: trunk/Scribus/scribus/third_party/lib2geom/bezier.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/bezier.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/bezier.h (revision 14937) |
@@ -0,0 +1,321 @@ |
+/* |
+ * bezier.h |
+ * |
+ * Copyright 2007 MenTaLguY <mental@rydia.net> |
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com> |
+ * Copyright 2007 Nathan Hurst <njh@njhurst.com> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#ifndef SEEN_BEZIER_H |
+#define SEEN_BEZIER_H |
+ |
+#include "coord.h" |
+#include <valarray> |
+#include "isnan.h" |
+#include "bezier-to-sbasis.h" |
+#include "d2.h" |
+#include "solver.h" |
+#include <boost/optional/optional.hpp> |
+ |
+namespace Geom { |
+ |
+inline Coord subdivideArr(Coord t, Coord const *v, Coord *left, Coord *right, unsigned order) { |
+ const unsigned size=order+1; |
+ std::vector<Coord> vtemp(v,v+size); |
+ |
+ //storing left/right coordinates |
+ std::vector<Coord> nodata(size); |
+ if(left == NULL)left=&nodata[0]; |
+ if(right == NULL)right=&nodata[0]; |
+ |
+ /* Copy control points */ |
+ left[0] = vtemp[0]; |
+ right[order]= vtemp[order]; |
+ |
+ /* Triangle computation */ |
+ for (unsigned i = 1; i < size; ++i) { |
+ for (unsigned j = 0; j < size - i; ++j) { |
+ vtemp[j] = lerp(t, vtemp[j], vtemp[j+1]); |
+ } |
+ left[i] =vtemp[0]; |
+ right[order-i]=vtemp[order-i]; |
+ } |
+ |
+ return (vtemp[0]); |
+} |
+ |
+ |
+class Bezier { |
+private: |
+ std::vector<Coord> c_; |
+ |
+ friend Bezier portion(const Bezier & a, Coord from, Coord to); |
+ |
+ friend Interval bounds_fast(Bezier const & b); |
+ |
+ friend Bezier derivative(const Bezier & a); |
+ |
+protected: |
+ Bezier(Coord const c[], unsigned ord) : c_(c,c+ord+1){ |
+ //std::copy(c, c+order()+1, &c_[0]); |
+ } |
+ |
+public: |
+ unsigned int order() const { return c_.size()-1;} |
+ unsigned int size() const { return c_.size();} |
+ |
+ Bezier() :c_(32) {} |
+ Bezier(const Bezier& b) :c_(b.c_) {} |
+ Bezier &operator=(Bezier const &other) { |
+ if ( c_.size() != other.c_.size() ) { |
+ c_.resize(other.c_.size()); |
+ } |
+ c_ = other.c_; |
+ return *this; |
+ } |
+ |
+ struct Order { |
+ unsigned order; |
+ explicit Order(Bezier const &b) : order(b.order()) {} |
+ explicit Order(unsigned o) : order(o) {} |
+ operator unsigned() const { return order; } |
+ }; |
+ |
+ //Construct an arbitrary order bezier |
+ Bezier(Order ord) : c_(ord.order+1) { |
+ assert(ord.order == order()); |
+ } |
+ |
+ explicit Bezier(Coord c0) : c_(1) { |
+ c_[0] = c0; |
+ } |
+ |
+ //Construct an order-1 bezier (linear Bézier) |
+ Bezier(Coord c0, Coord c1) : c_(2) { |
+ c_[0] = c0; c_[1] = c1; |
+ } |
+ |
+ //Construct an order-2 bezier (quadratic Bézier) |
+ Bezier(Coord c0, Coord c1, Coord c2) : c_(3) { |
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; |
+ } |
+ |
+ //Construct an order-3 bezier (cubic Bézier) |
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3) : c_(4) { |
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; |
+ } |
+ |
+ inline unsigned degree() const { return order(); } |
+ |
+ //IMPL: FragmentConcept |
+ typedef Coord output_type; |
+ inline bool isZero() const { |
+ for(unsigned i = 0; i <= order(); i++) { |
+ if(c_[i] != 0) return false; |
+ } |
+ return true; |
+ } |
+ inline bool isConstant() const { |
+ for(unsigned i = 1; i <= order(); i++) { |
+ if(c_[i] != c_[0]) return false; |
+ } |
+ return true; |
+ } |
+ inline bool isFinite() const { |
+ for(unsigned i = 0; i <= order(); i++) { |
+ if(!is_finite(c_[i])) return false; |
+ } |
+ return true; |
+ } |
+ inline Coord at0() const { return c_[0]; } |
+ inline Coord at1() const { return c_[order()]; } |
+ |
+ inline Coord valueAt(double t) const { |
+ return subdivideArr(t, &c_[0], NULL, NULL, order()); |
+ } |
+ inline Coord operator()(double t) const { return valueAt(t); } |
+ |
+ inline SBasis toSBasis() const { |
+ return bezier_to_sbasis(&c_[0], order()); |
+ } |
+ |
+ //Only mutator |
+ inline Coord &operator[](unsigned ix) { return c_[ix]; } |
+ inline Coord const &operator[](unsigned ix) const { return c_[ix]; } |
+ inline void setPoint(unsigned ix, double val) { c_[ix] = val; } |
+ |
+ /* This is inelegant, as it uses several extra stores. I think there might be a way to |
+ * evaluate roughly in situ. */ |
+ |
+ std::vector<Coord> valueAndDerivatives(Coord t, unsigned n_derivs) const { |
+ std::vector<Coord> val_n_der; |
+ |
+ unsigned nn = n_derivs; |
+ if(nn > order()) |
+ nn = order(); |
+ val_n_der.reserve(n_derivs); |
+ |
+ std::vector<Coord> d_(c_); |
+ for(unsigned di = 0; di < nn; di++) { |
+ val_n_der.push_back(subdivideArr(t, &d_[0], NULL, NULL, order() - di)); |
+ for(unsigned i = 0; i < order() - di; i++) { |
+ d_[i] = (order()-di)*(d_[i+1] - d_[i]); |
+ } |
+ } |
+ |
+ val_n_der.resize(n_derivs); |
+ return val_n_der; |
+ } |
+ |
+ std::pair<Bezier, Bezier > subdivide(Coord t) const { |
+ Bezier a(Bezier::Order(*this)), b(Bezier::Order(*this)); |
+ subdivideArr(t, &c_[0], &a.c_[0], &b.c_[0], order()); |
+ return std::pair<Bezier, Bezier >(a, b); |
+ } |
+ |
+ std::vector<double> roots() const { |
+ std::vector<double> solutions; |
+ find_bernstein_roots(&c_[0], order(), solutions, 0, 0.0, 1.0); |
+ return solutions; |
+ } |
+}; |
+ |
+//TODO: implement others |
+inline Bezier operator+(const Bezier & a, double v) { |
+ Bezier result = Bezier(Bezier::Order(a)); |
+ for(unsigned i = 0; i <= a.order(); i++) |
+ result[i] = a[i] + v; |
+ return result; |
+} |
+ |
+inline Bezier operator-(const Bezier & a, double v) { |
+ Bezier result = Bezier(Bezier::Order(a)); |
+ for(unsigned i = 0; i <= a.order(); i++) |
+ result[i] = a[i] - v; |
+ return result; |
+} |
+ |
+inline Bezier operator*(const Bezier & a, double v) { |
+ Bezier result = Bezier(Bezier::Order(a)); |
+ for(unsigned i = 0; i <= a.order(); i++) |
+ result[i] = a[i] * v; |
+ return result; |
+} |
+ |
+inline Bezier operator/(const Bezier & a, double v) { |
+ Bezier result = Bezier(Bezier::Order(a)); |
+ for(unsigned i = 0; i <= a.order(); i++) |
+ result[i] = a[i] / v; |
+ return result; |
+} |
+ |
+inline Bezier reverse(const Bezier & a) { |
+ Bezier result = Bezier(Bezier::Order(a)); |
+ for(unsigned i = 0; i <= a.order(); i++) |
+ result[i] = a[a.order() - i]; |
+ return result; |
+} |
+ |
+inline Bezier portion(const Bezier & a, double from, double to) { |
+ //TODO: implement better? |
+ std::vector<Coord> res(a.order()+1); |
+ if(from == 0) { |
+ if(to == 1) { return Bezier(a); } |
+ subdivideArr(to, &a.c_[0], &res[0], NULL, a.order()); |
+ return Bezier(&res[0], a.order()); |
+ } |
+ subdivideArr(from, &a.c_[0], NULL, &res[0], a.order()); |
+ if(to == 1) return Bezier(&res[0], a.order()); |
+ std::vector<Coord> res2(a.order()+1); |
+ subdivideArr((to - from)/(1 - from), &res[0], &res2[0], NULL, a.order()); |
+ return Bezier(&res2[0], a.order()); |
+} |
+ |
+// XXX Todo: how to handle differing orders |
+inline std::vector<Point> bezier_points(const D2<Bezier > & a) { |
+ std::vector<Point> result; |
+ for(unsigned i = 0; i <= a[0].order(); i++) { |
+ Point p; |
+ for(unsigned d = 0; d < 2; d++) p[d] = a[d][i]; |
+ result.push_back(p); |
+ } |
+ return result; |
+} |
+ |
+inline Bezier derivative(const Bezier & a) { |
+ if(a.order() == 1) return Bezier(0.0); |
+ Bezier der(Bezier::Order(a.order()-1)); |
+ |
+ for(unsigned i = 0; i < a.order(); i++) { |
+ der.c_[i] = a.order()*(a.c_[i+1] - a.c_[i]); |
+ } |
+ return der; |
+} |
+ |
+inline Bezier integral(const Bezier & a) { |
+ Bezier inte(Bezier::Order(a.order()+1)); |
+ |
+ inte[0] = 0; |
+ for(unsigned i = 0; i < inte.order(); i++) { |
+ inte[i+1] = inte[i] + a[i]/(inte.order()); |
+ } |
+ return inte; |
+} |
+ |
+inline Interval bounds_fast(Bezier const & b) { |
+ return Interval::fromArray(&b.c_[0], b.size()); |
+} |
+ |
+//TODO: better bounds exact |
+inline Interval bounds_exact(Bezier const & b) { |
+ return bounds_exact(b.toSBasis()); |
+} |
+ |
+inline Interval bounds_local(Bezier const & b, Interval i) { |
+ return bounds_fast(portion(b, i.min(), i.max())); |
+ //return bounds_local(b.toSBasis(), i); |
+} |
+ |
+inline std::ostream &operator<< (std::ostream &out_file, const Bezier & b) { |
+ for(unsigned i = 0; i < b.size(); i++) { |
+ out_file << b[i] << ", "; |
+ } |
+ return out_file; |
+} |
+ |
+} |
+#endif //SEEN_BEZIER_H |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/poly-dk-solve.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/poly-dk-solve.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/poly-dk-solve.cpp (revision 14937) |
@@ -0,0 +1,64 @@ |
+#include "poly-dk-solve.h" |
+#include <iterator> |
+ |
+/*** implementation of the Durand-Kerner method. seems buggy*/ |
+ |
+std::complex<double> evalu(Poly const & p, std::complex<double> x) { |
+ std::complex<double> result = 0; |
+ std::complex<double> xx = 1; |
+ |
+ for(unsigned i = 0; i < p.size(); i++) { |
+ result += p[i]*xx; |
+ xx *= x; |
+ } |
+ return result; |
+} |
+ |
+std::vector<std::complex<double> > DK(Poly const & ply, const double tol) { |
+ std::vector<std::complex<double> > roots; |
+ const int N = ply.degree(); |
+ |
+ std::complex<double> b(0.4, 0.9); |
+ std::complex<double> p = 1; |
+ for(int i = 0; i < N; i++) { |
+ roots.push_back(p); |
+ p *= b; |
+ } |
+ assert(roots.size() == ply.degree()); |
+ |
+ double error = 0; |
+ int i; |
+ for( i = 0; i < 30; i++) { |
+ error = 0; |
+ for(int r_i = 0; r_i < N; r_i++) { |
+ std::complex<double> denom = 1; |
+ std::complex<double> R = roots[r_i]; |
+ for(int d_i = 0; d_i < N; d_i++) { |
+ if(r_i != d_i) |
+ denom *= R-roots[d_i]; |
+ } |
+ assert(norm(denom) != 0); |
+ std::complex<double> dr = evalu(ply, R)/denom; |
+ error += norm(dr); |
+ roots[r_i] = R - dr; |
+ } |
+ /*std::copy(roots.begin(), roots.end(), std::ostream_iterator<std::complex<double> >(std::cout, ",\t")); |
+ std::cout << std::endl;*/ |
+ if(error < tol) |
+ break; |
+ } |
+ //std::cout << error << ", " << i<< std::endl; |
+ return roots; |
+} |
+ |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/path-intersection.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/path-intersection.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/path-intersection.h (revision 14937) |
@@ -0,0 +1,65 @@ |
+#ifndef __GEOM_PATH_INTERSECTION_H |
+#define __GEOM_PATH_INTERSECTION_H |
+ |
+#include "path.h" |
+ |
+#include "crossing.h" |
+ |
+#include "sweep.h" |
+ |
+namespace Geom { |
+ |
+int winding(Path const &path, Point p); |
+bool path_direction(Path const &p); |
+ |
+inline bool contains(Path const & p, Point i, bool evenodd = true) { |
+ return (evenodd ? winding(p, i) % 2 : winding(p, i)) != 0; |
+} |
+ |
+template<typename T> |
+Crossings curve_sweep(Path const &a, Path const &b) { |
+ T t; |
+ Crossings ret; |
+ std::vector<Rect> bounds_a = bounds(a), bounds_b = bounds(b); |
+ std::vector<std::vector<unsigned> > ixs = sweep_bounds(bounds_a, bounds_b); |
+ for(unsigned i = 0; i < a.size(); i++) { |
+ for(std::vector<unsigned>::iterator jp = ixs[i].begin(); jp != ixs[i].end(); jp++) { |
+ Crossings cc = t.crossings(a[i], b[*jp]); |
+ offset_crossings(cc, i, *jp); |
+ ret.insert(ret.end(), cc.begin(), cc.end()); |
+ } |
+ } |
+ return ret; |
+} |
+ |
+struct SimpleCrosser : public Crosser<Path> { |
+ Crossings crossings(Curve const &a, Curve const &b); |
+ Crossings crossings(Path const &a, Path const &b) { return curve_sweep<SimpleCrosser>(a, b); } |
+ CrossingSet crossings(std::vector<Path> const &a, std::vector<Path> const &b) { return Crosser<Path>::crossings(a, b); } |
+}; |
+ |
+struct MonoCrosser : public Crosser<Path> { |
+ Crossings crossings(Path const &a, Path const &b) { return crossings(std::vector<Path>(1,a), std::vector<Path>(1,b))[0]; } |
+ CrossingSet crossings(std::vector<Path> const &a, std::vector<Path> const &b); |
+}; |
+ |
+typedef SimpleCrosser DefaultCrosser; |
+ |
+std::vector<double> path_mono_splits(Path const &p); |
+ |
+CrossingSet crossings_among(std::vector<Path> const & p); |
+Crossings self_crossings(Path const & a); |
+ |
+inline Crossings crossings(Path const & a, Path const & b) { |
+ DefaultCrosser c = DefaultCrosser(); |
+ return c.crossings(a, b); |
+} |
+ |
+inline CrossingSet crossings(std::vector<Path> const & a, std::vector<Path> const & b) { |
+ DefaultCrosser c = DefaultCrosser(); |
+ return c.crossings(a, b); |
+} |
+ |
+} |
+ |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/utils.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/utils.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/utils.h (revision 14937) |
@@ -0,0 +1,81 @@ |
+#ifndef LIB2GEOM_UTILS_HEADER |
+#define LIB2GEOM_UTILS_HEADER |
+ |
+/** Various utility functions. |
+ * |
+ * 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 <cmath> |
+ |
+namespace Geom { |
+ |
+// proper logical xor |
+inline bool logical_xor (bool a, bool b) { return (a || b) && !(a && b); } |
+ |
+/** Sign function - indicates the sign of a numeric type. -1 indicates negative, 1 indicates |
+ * positive, and 0 indicates, well, 0. Mathsy people will know this is basically the derivative |
+ * of abs, except for the fact that it is defined on 0. |
+ */ |
+template <class T> inline int sgn(const T& x) {return (x < 0 ? -1 : (x > 0 ? 1 : 0) );} |
+ |
+template <class T> inline T sqr(const T& x) {return x * x;} |
+template <class T> inline T cube(const T& x) {return x * x * x;} |
+ |
+/** Between function - returns true if a number x is within a range. The values delimiting the |
+ * range and the number must have the same type. |
+ */ |
+template <class T> inline const T& between (const T& min, const T& max, const T& x) |
+ { return min < x && max > x; } |
+ |
+/** Returns x rounded to the nearest integer. It is unspecified what happens |
+ * if x is half way between two integers: we may in future use rint/round |
+ * on platforms that have them. |
+ */ |
+inline double round(double const x) { return std::floor(x + .5); } |
+ |
+/** Returns x rounded to the nearest \a places decimal places. |
+ |
+ Implemented in terms of round, i.e. we make no guarantees as to what happens if x is |
+ half way between two rounded numbers. |
+ |
+ Note: places is the number of decimal places without using scientific (e) notation, not the |
+ number of significant figures. This function may not be suitable for values of x whose |
+ magnitude is so far from 1 that one would want to use scientific (e) notation. |
+ |
+ places may be negative: e.g. places = -2 means rounding to a multiple of .01 |
+**/ |
+inline double decimal_round(double const x, int const places) { |
+ //TODO: possibly implement with modulus instead? |
+ double const multiplier = std::pow(10.0, places); |
+ return round( x * multiplier ) / multiplier; |
+} |
+ |
+} |
+ |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis-math.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis-math.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis-math.h (revision 14937) |
@@ -0,0 +1,95 @@ |
+/* |
+ * sbasis-math.h - some std functions to work with (pw)s-basis |
+ * |
+ * Authors: |
+ * Jean-Francois Barraud |
+ * |
+ * Copyright (C) 2006-2007 authors |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ */ |
+ |
+//this a first try to define sqrt, cos, sin, etc... |
+//TODO: define a truncated compose(sb,sb, order) and extend it to pw<sb>. |
+//TODO: in all these functions, compute 'order' according to 'tol'. |
+//TODO: use template to define the pw version automatically from the sb version? |
+ |
+#ifndef SEEN_GEOM_SB_CALCULS_H |
+#define SEEN_GEOM_SB_CALCULS_H |
+ |
+ |
+#include "sbasis.h" |
+#include "piecewise.h" |
+ |
+namespace Geom{ |
+//-|x|--------------------------------------------------------------- |
+Piecewise<SBasis> abs( SBasis const &f); |
+Piecewise<SBasis> abs(Piecewise<SBasis>const &f); |
+ |
+//- max(f,g), min(f,g) ---------------------------------------------- |
+Piecewise<SBasis> max( SBasis const &f, SBasis const &g); |
+Piecewise<SBasis> max(Piecewise<SBasis> const &f, SBasis const &g); |
+Piecewise<SBasis> max( SBasis const &f, Piecewise<SBasis> const &g); |
+Piecewise<SBasis> max(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g); |
+Piecewise<SBasis> min( SBasis const &f, SBasis const &g); |
+Piecewise<SBasis> min(Piecewise<SBasis> const &f, SBasis const &g); |
+Piecewise<SBasis> min( SBasis const &f, Piecewise<SBasis> const &g); |
+Piecewise<SBasis> min(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g); |
+ |
+//-sign(x)--------------------------------------------------------------- |
+Piecewise<SBasis> signSb( SBasis const &f); |
+Piecewise<SBasis> signSb(Piecewise<SBasis>const &f); |
+ |
+//-Sqrt--------------------------------------------------------------- |
+Piecewise<SBasis> sqrt( SBasis const &f, double tol=1e-3, int order=3); |
+Piecewise<SBasis> sqrt(Piecewise<SBasis>const &f, double tol=1e-3, int order=3); |
+ |
+//-sin/cos-------------------------------------------------------------- |
+Piecewise<SBasis> cos( SBasis const &f, double tol=1e-3, int order=3); |
+Piecewise<SBasis> cos(Piecewise<SBasis> const &f, double tol=1e-3, int order=3); |
+Piecewise<SBasis> sin( SBasis const &f, double tol=1e-3, int order=3); |
+Piecewise<SBasis> sin(Piecewise<SBasis> const &f, double tol=1e-3, int order=3); |
+//-Log--------------------------------------------------------------- |
+Piecewise<SBasis> log( SBasis const &f, double tol=1e-3, int order=3); |
+Piecewise<SBasis> log(Piecewise<SBasis>const &f, double tol=1e-3, int order=3); |
+ |
+//--1/x------------------------------------------------------------ |
+//TODO: change this... |
+Piecewise<SBasis> reciprocalOnDomain(Interval range, double tol=1e-3); |
+Piecewise<SBasis> reciprocal( SBasis const &f, double tol=1e-3, int order=3); |
+Piecewise<SBasis> reciprocal(Piecewise<SBasis>const &f, double tol=1e-3, int order=3); |
+ |
+} |
+ |
+#endif //SEEN_GEOM_PW_SB_CALCULUS_H |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype = cpp:expandtab:shiftwidth = 4:tabstop = 8:softtabstop = 4:encoding = utf-8:textwidth = 99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/point-l.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/point-l.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/point-l.h (revision 14937) |
@@ -0,0 +1,86 @@ |
+#ifndef SEEN_Geom_POINT_L_H |
+#define SEEN_Geom_POINT_L_H |
+ |
+#include <stdexcept> |
+#include "point.h" |
+ |
+namespace Geom { |
+ |
+typedef long ICoord; |
+ |
+class IPoint { |
+ ICoord _pt[2]; |
+ |
+ public: |
+ IPoint() { } |
+ |
+ IPoint(ICoord x, ICoord y) { |
+ _pt[X] = x; |
+ _pt[Y] = y; |
+ } |
+ |
+ IPoint(NRPointL const &p) { |
+ _pt[X] = p.x; |
+ _pt[Y] = p.y; |
+ } |
+ |
+ IPoint(IPoint const &p) { |
+ for (unsigned i = 0; i < 2; ++i) { |
+ _pt[i] = p._pt[i]; |
+ } |
+ } |
+ |
+ IPoint &operator=(IPoint const &p) { |
+ for (unsigned i = 0; i < 2; ++i) { |
+ _pt[i] = p._pt[i]; |
+ } |
+ return *this; |
+ } |
+ |
+ operator Point() { |
+ return Point(_pt[X], _pt[Y]); |
+ } |
+ |
+ ICoord operator[](unsigned i) const throw(std::out_of_range) { |
+ if ( i > Y ) throw std::out_of_range("index out of range"); |
+ return _pt[i]; |
+ } |
+ |
+ ICoord &operator[](unsigned i) throw(std::out_of_range) { |
+ if ( i > Y ) throw std::out_of_range("index out of range"); |
+ return _pt[i]; |
+ } |
+ |
+ ICoord operator[](Dim2 d) const throw() { return _pt[d]; } |
+ ICoord &operator[](Dim2 d) throw() { return _pt[d]; } |
+ |
+ IPoint &operator+=(IPoint const &o) { |
+ for ( unsigned i = 0 ; i < 2 ; ++i ) { |
+ _pt[i] += o._pt[i]; |
+ } |
+ return *this; |
+ } |
+ |
+ IPoint &operator-=(IPoint const &o) { |
+ for ( unsigned i = 0 ; i < 2 ; ++i ) { |
+ _pt[i] -= o._pt[i]; |
+ } |
+ return *this; |
+ } |
+}; |
+ |
+ |
+} // namespace Geom |
+ |
+#endif /* !SEEN_Geom_POINT_L_H */ |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis-to-bezier.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis-to-bezier.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis-to-bezier.h (revision 14937) |
@@ -0,0 +1,24 @@ |
+#ifndef _SBASIS_TO_BEZIER |
+#define _SBASIS_TO_BEZIER |
+ |
+#include "d2.h" |
+#include "path.h" |
+ |
+namespace Geom{ |
+// this produces a degree k bezier from a degree k sbasis |
+Bezier |
+sbasis_to_bezier(SBasis const &B, unsigned q = 0); |
+ |
+// inverse |
+SBasis bezier_to_sbasis(Bezier const &B); |
+ |
+ |
+std::vector<Geom::Point> |
+sbasis_to_bezier(D2<SBasis> const &B, unsigned q = 0); |
+ |
+std::vector<Path> path_from_piecewise(Piecewise<D2<SBasis> > const &B, double tol); |
+ |
+Path path_from_sbasis(D2<SBasis> const &B, double tol); |
+ |
+}; |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/bezier-to-sbasis.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/bezier-to-sbasis.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/bezier-to-sbasis.h (revision 14937) |
@@ -0,0 +1,73 @@ |
+/* |
+ * bezier-to-sbasis.h |
+ * |
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#ifndef _BEZIER_TO_SBASIS |
+#define _BEZIER_TO_SBASIS |
+ |
+#include "coord.h" |
+ |
+#include "d2.h" |
+#include "point.h" |
+ |
+namespace Geom{ |
+ |
+inline SBasis bezier_to_sbasis(Coord const *handles, unsigned order) { |
+ if(order == 0) |
+ return Linear(handles[0]); |
+ else if(order == 1) |
+ return Linear(handles[0], handles[1]); |
+ else |
+ return multiply(Linear(1, 0), bezier_to_sbasis(handles, order-1)) + |
+ multiply(Linear(0, 1), bezier_to_sbasis(handles+1, order-1)); |
+} |
+ |
+ |
+template <typename T> |
+inline D2<SBasis> handles_to_sbasis(T const &handles, unsigned order) { |
+ double v[2][order+1]; |
+ for(unsigned i = 0; i <= order; i++) |
+ for(unsigned j = 0; j < 2; j++) |
+ v[j][i] = handles[i][j]; |
+ return D2<SBasis>(bezier_to_sbasis(v[0], order), |
+ bezier_to_sbasis(v[1], order)); |
+} |
+ |
+}; |
+#endif |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis-2d.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis-2d.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis-2d.h (revision 14937) |
@@ -0,0 +1,325 @@ |
+#ifndef SEEN_SBASIS_2D_H |
+#define SEEN_SBASIS_2D_H |
+#include <vector> |
+#include <cassert> |
+#include <algorithm> |
+#include "d2.h" |
+#include "sbasis.h" |
+#include <iostream> |
+ |
+namespace Geom{ |
+ |
+class Linear2d{ |
+public: |
+ /* |
+ u 0,1 |
+ v 0,2 |
+ */ |
+ double a[4]; |
+ Linear2d() {} |
+ Linear2d(double aa) { |
+ for(unsigned i = 0 ; i < 4; i ++) |
+ a[i] = aa; |
+ } |
+ Linear2d(double a00, double a01, double a10, double a11) |
+ { |
+ a[0] = a00; |
+ a[1] = a01; |
+ a[2] = a10; |
+ a[3] = a11; |
+ } |
+ |
+ double operator[](const int i) const { |
+ assert(i >= 0); |
+ assert(i < 4); |
+ return a[i]; |
+ } |
+ double& operator[](const int i) { |
+ assert(i >= 0); |
+ assert(i < 4); |
+ return a[i]; |
+ } |
+ double apply(double u, double v) { |
+ return (a[0]*(1-u)*(1-v) + |
+ a[1]*u*(1-v) + |
+ a[2]*(1-u)*v + |
+ a[3]*u*v); |
+ } |
+}; |
+ |
+inline Linear extract_u(Linear2d const &a, double u) { |
+ return Linear(a[0]*(1-u) + |
+ a[1]*u, |
+ a[2]*(1-u) + |
+ a[3]*u); |
+} |
+inline Linear extract_v(Linear2d const &a, double v) { |
+ return Linear(a[0]*(1-v) + |
+ a[2]*v, |
+ a[1]*(1-v) + |
+ a[3]*v); |
+} |
+inline Linear2d operator-(Linear2d const &a) { |
+ return Linear2d(-a.a[0], -a.a[1], |
+ -a.a[2], -a.a[3]); |
+} |
+inline Linear2d operator+(Linear2d const & a, Linear2d const & b) { |
+ return Linear2d(a[0] + b[0], |
+ a[1] + b[1], |
+ a[2] + b[2], |
+ a[3] + b[3]); |
+} |
+inline Linear2d operator-(Linear2d const & a, Linear2d const & b) { |
+ return Linear2d(a[0] - b[0], |
+ a[1] - b[1], |
+ a[2] - b[2], |
+ a[3] - b[3]); |
+} |
+inline Linear2d& operator+=(Linear2d & a, Linear2d const & b) { |
+ for(unsigned i = 0; i < 4; i++) |
+ a[i] += b[i]; |
+ return a; |
+} |
+inline Linear2d& operator-=(Linear2d & a, Linear2d const & b) { |
+ for(unsigned i = 0; i < 4; i++) |
+ a[i] -= b[i]; |
+ return a; |
+} |
+inline Linear2d& operator*=(Linear2d & a, double b) { |
+ for(unsigned i = 0; i < 4; i++) |
+ a[i] *= b; |
+ return a; |
+} |
+ |
+inline bool operator==(Linear2d const & a, Linear2d const & b) { |
+ for(unsigned i = 0; i < 4; i++) |
+ if(a[i] != b[i]) |
+ return false; |
+ return true; |
+} |
+inline bool operator!=(Linear2d const & a, Linear2d const & b) { |
+ for(unsigned i = 0; i < 4; i++) |
+ if(a[i] == b[i]) |
+ return false; |
+ return true; |
+} |
+inline Linear2d operator*(double const a, Linear2d const & b) { |
+ return Linear2d(a*b[0], a*b[1], |
+ a*b[2], a*b[3]); |
+} |
+ |
+class SBasis2d : public std::vector<Linear2d>{ |
+public: |
+ // vector in u,v |
+ unsigned us, vs; // number of u terms, v terms |
+ SBasis2d() {} |
+ SBasis2d(Linear2d const & bo) |
+ : us(1), vs(1) { |
+ push_back(bo); |
+ } |
+ SBasis2d(SBasis2d const & a) |
+ : std::vector<Linear2d>(a), us(a.us), vs(a.vs) {} |
+ |
+ Linear2d& index(unsigned ui, unsigned vi) { |
+ assert(ui < us); |
+ assert(vi < vs); |
+ return (*this)[ui + vi*us]; |
+ } |
+ |
+ Linear2d index(unsigned ui, unsigned vi) const { |
+ if(ui >= us) |
+ return Linear2d(0); |
+ if(vi >= vs) |
+ return Linear2d(0); |
+ return (*this)[ui + vi*us]; |
+ } |
+ |
+ double apply(double u, double v) const { |
+ double s = u*(1-u); |
+ double t = v*(1-v); |
+ Linear2d p; |
+ double tk = 1; |
+// XXX rewrite as horner |
+ for(unsigned vi = 0; vi < vs; vi++) { |
+ double sk = 1; |
+ for(unsigned ui = 0; ui < us; ui++) { |
+ p += (sk*tk)*index(ui, vi); |
+ sk *= s; |
+ } |
+ tk *= t; |
+ } |
+ return p.apply(u,v); |
+ } |
+ |
+ void clear() { |
+ fill(begin(), end(), Linear2d(0)); |
+ } |
+ |
+ void normalize(); // remove extra zeros |
+ |
+ double tail_error(unsigned tail) const; |
+ |
+ void truncate(unsigned k); |
+}; |
+ |
+inline SBasis2d operator-(const SBasis2d& p) { |
+ SBasis2d result; |
+ result.reserve(p.size()); |
+ |
+ for(unsigned i = 0; i < p.size(); i++) { |
+ result.push_back(-p[i]); |
+ } |
+ return result; |
+} |
+ |
+inline SBasis2d operator+(const SBasis2d& a, const SBasis2d& b) { |
+ SBasis2d result; |
+ result.us = std::max(a.us, b.us); |
+ result.vs = std::max(a.vs, b.vs); |
+ const unsigned out_size = result.us*result.vs; |
+ result.resize(out_size); |
+ |
+ for(unsigned vi = 0; vi < result.vs; vi++) { |
+ for(unsigned ui = 0; ui < result.us; ui++) { |
+ Linear2d bo; |
+ if(ui < a.us && vi < a.vs) |
+ bo += a.index(ui, vi); |
+ if(ui < b.us && vi < b.vs) |
+ bo += b.index(ui, vi); |
+ result.index(ui, vi) = bo; |
+ } |
+ } |
+ return result; |
+} |
+ |
+inline SBasis2d operator-(const SBasis2d& a, const SBasis2d& b) { |
+ SBasis2d result; |
+ result.us = std::max(a.us, b.us); |
+ result.vs = std::max(a.vs, b.vs); |
+ const unsigned out_size = result.us*result.vs; |
+ result.resize(out_size); |
+ |
+ for(unsigned vi = 0; vi < result.vs; vi++) { |
+ for(unsigned ui = 0; ui < result.us; ui++) { |
+ Linear2d bo; |
+ if(ui < a.us && vi < a.vs) |
+ bo += a.index(ui, vi); |
+ if(ui < b.us && vi < b.vs) |
+ bo -= b.index(ui, vi); |
+ result.index(ui, vi) = bo; |
+ } |
+ } |
+ return result; |
+} |
+ |
+ |
+inline SBasis2d& operator+=(SBasis2d& a, const Linear2d& b) { |
+ if(a.size() < 1) |
+ a.push_back(b); |
+ else |
+ a[0] += b; |
+ return a; |
+} |
+ |
+inline SBasis2d& operator-=(SBasis2d& a, const Linear2d& b) { |
+ if(a.size() < 1) |
+ a.push_back(-b); |
+ else |
+ a[0] -= b; |
+ return a; |
+} |
+ |
+inline SBasis2d& operator+=(SBasis2d& a, double b) { |
+ if(a.size() < 1) |
+ a.push_back(Linear2d(b)); |
+ else { |
+ for(unsigned i = 0; i < 4; i++) |
+ a[0] += double(b); |
+ } |
+ return a; |
+} |
+ |
+inline SBasis2d& operator-=(SBasis2d& a, double b) { |
+ if(a.size() < 1) |
+ a.push_back(Linear2d(-b)); |
+ else { |
+ a[0] -= b; |
+ } |
+ return a; |
+} |
+ |
+inline SBasis2d& operator*=(SBasis2d& a, double b) { |
+ for(unsigned i = 0; i < a.size(); i++) |
+ a[i] *= b; |
+ return a; |
+} |
+ |
+inline SBasis2d& operator/=(SBasis2d& a, double b) { |
+ for(unsigned i = 0; i < a.size(); i++) |
+ a[i] *= (1./b); |
+ return a; |
+} |
+ |
+SBasis2d operator*(double k, SBasis2d const &a); |
+SBasis2d operator*(SBasis2d const &a, SBasis2d const &b); |
+ |
+SBasis2d shift(SBasis2d const &a, int sh); |
+ |
+SBasis2d shift(Linear2d const &a, int sh); |
+ |
+SBasis2d truncate(SBasis2d const &a, unsigned terms); |
+ |
+SBasis2d multiply(SBasis2d const &a, SBasis2d const &b); |
+ |
+SBasis2d integral(SBasis2d const &c); |
+ |
+SBasis2d derivative(SBasis2d const &a); |
+ |
+SBasis2d sqrt(SBasis2d const &a, int k); |
+ |
+// return a kth order approx to 1/a) |
+SBasis2d reciprocal(Linear2d const &a, int k); |
+ |
+SBasis2d divide(SBasis2d const &a, SBasis2d const &b, int k); |
+ |
+// a(b(t)) |
+SBasis2d compose(SBasis2d const &a, SBasis2d const &b); |
+SBasis2d compose(SBasis2d const &a, SBasis2d const &b, unsigned k); |
+SBasis2d inverse(SBasis2d const &a, int k); |
+ |
+// these two should probably be replaced with compose |
+SBasis extract_u(SBasis2d const &a, double u); |
+SBasis extract_v(SBasis2d const &a, double v); |
+ |
+SBasis compose(Linear2d const &a, D2<SBasis> const &p); |
+ |
+SBasis compose(SBasis2d const &fg, D2<SBasis> const &p); |
+ |
+D2<SBasis> compose_each(D2<SBasis2d> const &fg, D2<SBasis> const &p); |
+ |
+inline std::ostream &operator<< (std::ostream &out_file, const Linear2d &bo) { |
+ out_file << "{" << bo[0] << ", " << bo[1] << "}, "; |
+ out_file << "{" << bo[2] << ", " << bo[3] << "}"; |
+ return out_file; |
+} |
+ |
+inline std::ostream &operator<< (std::ostream &out_file, const SBasis2d & p) { |
+ for(unsigned i = 0; i < p.size(); i++) { |
+ out_file << p[i] << "s^" << i << " + "; |
+ } |
+ return out_file; |
+} |
+ |
+}; |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/point.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/point.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/point.h (revision 14937) |
@@ -0,0 +1,229 @@ |
+#ifndef SEEN_Geom_POINT_H |
+#define SEEN_Geom_POINT_H |
+ |
+/** \file |
+ * Cartesian point class. |
+ */ |
+ |
+#include <iostream> |
+ |
+#include "coord.h" |
+#include "utils.h" |
+ |
+namespace Geom { |
+ |
+enum Dim2 { X=0, Y=1 }; |
+ |
+class Matrix; |
+ |
+/// Cartesian point. |
+class Point { |
+ Coord _pt[2]; |
+ |
+ public: |
+ inline Point() |
+ { _pt[X] = _pt[Y] = 0; } |
+ |
+ inline Point(Coord x, Coord y) { |
+ _pt[X] = x; _pt[Y] = y; |
+ } |
+ |
+ inline Point(Point const &p) { |
+ for (unsigned i = 0; i < 2; ++i) |
+ _pt[i] = p._pt[i]; |
+ } |
+ |
+ inline Point &operator=(Point const &p) { |
+ for (unsigned i = 0; i < 2; ++i) |
+ _pt[i] = p._pt[i]; |
+ return *this; |
+ } |
+ |
+ inline Coord operator[](unsigned i) const { return _pt[i]; } |
+ inline Coord &operator[](unsigned i) { return _pt[i]; } |
+ |
+ Coord operator[](Dim2 d) const throw() { return _pt[d]; } |
+ Coord &operator[](Dim2 d) throw() { return _pt[d]; } |
+ |
+ static inline Point polar(Coord angle, Coord radius) { |
+ return Point(radius * std::cos(angle), radius * std::sin(angle)); |
+ } |
+ |
+ inline Coord length() const { return hypot(_pt[0], _pt[1]); } |
+ |
+ /** Return a point like this point but rotated -90 degrees. |
+ (If the y axis grows downwards and the x axis grows to the |
+ right, then this is 90 degrees counter-clockwise.) |
+ **/ |
+ Point ccw() const { |
+ return Point(_pt[Y], -_pt[X]); |
+ } |
+ |
+ /** Return a point like this point but rotated +90 degrees. |
+ (If the y axis grows downwards and the x axis grows to the |
+ right, then this is 90 degrees clockwise.) |
+ **/ |
+ Point cw() const { |
+ return Point(-_pt[Y], _pt[X]); |
+ } |
+ |
+ /** |
+ \brief A function to lower the precision of the point |
+ \param places The number of decimal places that should be in |
+ the final number. |
+ */ |
+ inline void round (int places = 0) { |
+ _pt[X] = (Coord)(decimal_round((double)_pt[X], places)); |
+ _pt[Y] = (Coord)(decimal_round((double)_pt[Y], places)); |
+ return; |
+ } |
+ |
+ void normalize(); |
+ |
+ inline Point operator+(Point const &o) const { |
+ return Point(_pt[X] + o._pt[X], _pt[Y] + o._pt[Y]); |
+ } |
+ inline Point operator-(Point const &o) const { |
+ return Point(_pt[X] - o._pt[X], _pt[Y] - o._pt[Y]); |
+ } |
+ inline Point &operator+=(Point const &o) { |
+ for ( unsigned i = 0 ; i < 2 ; ++i ) { |
+ _pt[i] += o._pt[i]; |
+ } |
+ return *this; |
+ } |
+ inline Point &operator-=(Point const &o) { |
+ for ( unsigned i = 0 ; i < 2 ; ++i ) { |
+ _pt[i] -= o._pt[i]; |
+ } |
+ return *this; |
+ } |
+ |
+ inline Point operator-() const { |
+ return Point(-_pt[X], -_pt[Y]); |
+ } |
+ inline Point operator*(double const s) const { |
+ return Point(_pt[X] * s, _pt[Y] * s); |
+ } |
+ inline Point operator/(double const s) const { |
+ //TODO: s == 0? |
+ return Point(_pt[X] / s, _pt[Y] / s); |
+ } |
+ inline Point &operator*=(double const s) { |
+ for ( unsigned i = 0 ; i < 2 ; ++i ) _pt[i] *= s; |
+ return *this; |
+ } |
+ inline Point &operator/=(double const s) { |
+ //TODO: s == 0? |
+ for ( unsigned i = 0 ; i < 2 ; ++i ) _pt[i] /= s; |
+ return *this; |
+ } |
+ |
+ Point &operator*=(Matrix const &m); |
+ |
+ inline int operator == (const Point &in_pnt) { |
+ return ((_pt[X] == in_pnt[X]) && (_pt[Y] == in_pnt[Y])); |
+ } |
+ |
+ friend inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_pnt); |
+}; |
+ |
+inline Point operator*(double const s, Point const &p) { return p * s; } |
+ |
+/** A function to print out the Point. It just prints out the coords |
+ on the given output stream */ |
+inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_pnt) { |
+ out_file << "X: " << in_pnt[X] << " Y: " << in_pnt[Y]; |
+ return out_file; |
+} |
+ |
+/** This is a rotation (sort of). */ |
+inline Point operator^(Point const &a, Point const &b) { |
+ Point const ret(a[0] * b[0] - a[1] * b[1], |
+ a[1] * b[0] + a[0] * b[1]); |
+ return ret; |
+} |
+ |
+//IMPL: boost::EqualityComparableConcept |
+inline bool operator==(Point const &a, Point const &b) { |
+ return (a[X] == b[X]) && (a[Y] == b[Y]); |
+} |
+inline bool operator!=(Point const &a, Point const &b) { |
+ return (a[X] != b[X]) || (a[Y] != b[Y]); |
+} |
+ |
+/** This is a lexicographical ordering for points. It is remarkably useful for sweepline algorithms*/ |
+inline bool operator<=(Point const &a, Point const &b) { |
+ return ( ( a[Y] < b[Y] ) || |
+ (( a[Y] == b[Y] ) && ( a[X] < b[X] ))); |
+} |
+ |
+Coord L1(Point const &p); |
+ |
+/** Compute the L2, or euclidean, norm of \a p. */ |
+inline Coord L2(Point const &p) { return p.length(); } |
+ |
+/** Compute the square of L2 norm of \a p. Warning: this can overflow where L2 won't.*/ |
+inline Coord L2sq(Point const &p) { return p[0]*p[0] + p[1]*p[1]; } |
+ |
+double LInfty(Point const &p); |
+bool is_zero(Point const &p); |
+bool is_unit_vector(Point const &p); |
+ |
+extern double atan2(Point const p); |
+/** compute the angle turning from a to b (signed). */ |
+extern double angle_between(Point const a, Point const b); |
+ |
+//IMPL: NearConcept |
+inline bool are_near(Point const &a, Point const &b, double const eps=EPSILON) { |
+ return ( are_near(a[X],b[X],eps) && are_near(a[Y],b[Y],eps) ); |
+} |
+ |
+/** Returns p * Geom::rotate_degrees(90), but more efficient. |
+ * |
+ * Angle direction in Inkscape code: If you use the traditional mathematics convention that y |
+ * increases upwards, then positive angles are anticlockwise as per the mathematics convention. If |
+ * you take the common non-mathematical convention that y increases downwards, then positive angles |
+ * are clockwise, as is common outside of mathematics. |
+ * |
+ * There is no rot_neg90 function: use -rot90(p) instead. |
+ */ |
+inline Point rot90(Point const &p) { return Point(-p[Y], p[X]); } |
+ |
+/** Given two points and a parameter t \in [0, 1], return a point |
+ * proportionally from a to b by t. Akin to 1 degree bezier.*/ |
+inline Point lerp(double const t, Point const a, Point const b) { return (a * (1 - t) + b * t); } |
+ |
+Point unit_vector(Point const &a); |
+ |
+/** compute the dot product (inner product) between the vectors a and b. */ |
+inline Coord dot(Point const &a, Point const &b) { return a[0] * b[0] + a[1] * b[1]; } |
+/** Defined as dot(a, b.cw()). */ |
+inline Coord cross(Point const &a, Point const &b) { return dot(a, b.cw()); } |
+ |
+/** compute the euclidean distance between points a and b. TODO: hypot safer/faster? */ |
+inline Coord distance (Point const &a, Point const &b) { return L2(a - b); } |
+ |
+/** compute the square of the distance between points a and b. */ |
+inline Coord distanceSq (Point const &a, Point const &b) { return L2sq(a - b); } |
+ |
+Point abs(Point const &b); |
+ |
+Point operator*(Point const &v, Matrix const &m); |
+ |
+Point operator/(Point const &p, Matrix const &m); |
+ |
+} /* namespace Geom */ |
+ |
+#endif /* !SEEN_Geom_POINT_H */ |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/quadtree.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/quadtree.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/quadtree.cpp (revision 14937) |
@@ -0,0 +1,131 @@ |
+#include "quadtree.h" |
+ |
+Quad* QuadTree::search(double x0, double y0, double x1, double y1) { |
+ Quad *q = root; |
+ |
+ double bxx0 = bx1, bxx1 = bx1; |
+ double byy0 = by1, byy1 = by1; |
+ while(q) { |
+ double cx = (bxx0 + bxx1)/2; |
+ double cy = (byy0 + byy1)/2; |
+ unsigned i = 0; |
+ if(x0 >= cx) { |
+ i += 1; |
+ bxx0 = cx; // zoom in a quad |
+ } else if(x1 <= cx) { |
+ bxx1 = cx; |
+ } else |
+ break; |
+ if(y0 >= cy) { |
+ i += 2; |
+ byy0 = cy; |
+ } else if(y1 <= cy) { |
+ byy1 = cy; |
+ } else |
+ break; |
+ |
+ assert(i < 4); |
+ Quad *qq = q->children[i]; |
+ if(qq == 0) break; // last non-null |
+ q = qq; |
+ } |
+ return q; |
+} |
+ |
+void QuadTree::insert(double x0, double y0, double x1, double y1, int shape) { |
+ // loop until a quad would break the box. |
+ if(root == 0) { |
+ root = new Quad; |
+ |
+ bx0 = 0; |
+ bx1 = 1; |
+ by0 = 0; |
+ by1 = 1; |
+ } |
+ Quad *q = root; |
+ |
+ double bxx0 = bx0, bxx1 = bx1; |
+ double byy0 = by0, byy1 = by1; |
+ while((bxx0 > x0) || |
+ (bxx1 < x1) || |
+ (byy0 > y0) || |
+ (byy1 < y1)) { // too small initial size - double |
+ unsigned i = 0; |
+ if(bxx0 > x0) { |
+ bxx0 = 2*bxx0 - bxx1; |
+ i += 1; |
+ } else { |
+ bxx1 = 2*bxx1 - bxx0; |
+ } |
+ if(byy0 > y0) { |
+ byy0 = 2*byy0 - byy1; |
+ i += 2; |
+ } else { |
+ byy1 = 2*byy1 - byy0; |
+ } |
+ q = new Quad; |
+ q->children[i] = root; |
+ root = q; |
+ bx0 = bxx0; |
+ bx1 = bxx1; |
+ by0 = byy0; |
+ by1 = byy1; |
+ } |
+ |
+ while(q) { |
+ double cx = (bxx0 + bxx1)/2; |
+ double cy = (byy0 + byy1)/2; |
+ unsigned i = 0; |
+ assert(x0 >= bxx0); |
+ assert(x1 <= bxx1); |
+ assert(y0 >= byy0); |
+ assert(y1 <= byy1); |
+ if(x0 >= cx) { |
+ i += 1; |
+ bxx0 = cx; // zoom in a quad |
+ } else if(x1 <= cx) { |
+ bxx1 = cx; |
+ } else |
+ break; |
+ if(y0 >= cy) { |
+ i += 2; |
+ byy0 = cy; |
+ } else if(y1 <= cy) { |
+ byy1 = cy; |
+ } else |
+ break; |
+ |
+ assert(i < 4); |
+ Quad *qq = q->children[i]; |
+ if(qq == 0) { |
+ qq = new Quad; |
+ q->children[i] = qq; |
+ } |
+ q = qq; |
+ } |
+ q->data.push_back(shape); |
+} |
+void QuadTree::erase(Quad *q, int shape) { |
+ for(Quad::iterator i = q->data.begin(); i != q->data.end(); i++) { |
+ if(*i == shape) { |
+ q->data.erase(i); |
+ if(q->data.empty()) { |
+ |
+ } |
+ } |
+ } |
+ return; |
+} |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(substatement-open . 0)) |
+ indent-tabs-mode:nil |
+ c-brace-offset:0 |
+ fill-column:99 |
+ End: |
+ vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
+*/ |
+ |
Index: trunk/Scribus/scribus/third_party/lib2geom/convex-cover.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/convex-cover.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/convex-cover.cpp (revision 14937) |
@@ -0,0 +1,450 @@ |
+/* |
+ * convex-cover.cpp |
+ * |
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#include "convex-cover.h" |
+#include <algorithm> |
+#include <map> |
+/** Todo: |
+ + modify graham scan to work top to bottom, rather than around angles |
+ + intersection |
+ + minimum distance between convex hulls |
+ + maximum distance between convex hulls |
+ + hausdorf metric? |
+ + check all degenerate cases carefully |
+ + check all algorithms meet all invariants |
+ + generalise rotating caliper algorithm (iterator/circulator?) |
+*/ |
+ |
+using std::vector; |
+using std::map; |
+using std::pair; |
+ |
+namespace Geom{ |
+ |
+/*** SignedTriangleArea |
+ * returns the area of the triangle defined by p0, p1, p2. A clockwise triangle has positive area. |
+ */ |
+double |
+SignedTriangleArea(Point p0, Point p1, Point p2) { |
+ return cross((p1 - p0), (p2 - p0)); |
+} |
+ |
+class angle_cmp{ |
+public: |
+ Point o; |
+ angle_cmp(Point o) : o(o) {} |
+ |
+ bool |
+ operator()(Point a, Point b) { |
+ Point da = a - o; |
+ Point db = b - o; |
+ |
+#if 1 |
+ double aa = da[0]; |
+ double ab = db[0]; |
+ if((da[1] == 0) && (db[1] == 0)) |
+ return da[0] < db[0]; |
+ if(da[1] == 0) |
+ return true; // infinite tangent |
+ if(db[1] == 0) |
+ return false; // infinite tangent |
+ aa = da[0] / da[1]; |
+ ab = db[0] / db[1]; |
+ if(aa > ab) |
+ return true; |
+#else |
+ //assert((ata > atb) == (aa < ab)); |
+ double aa = atan2(da); |
+ double ab = atan2(db); |
+ if(aa < ab) |
+ return true; |
+#endif |
+ if(aa == ab) |
+ return L2sq(da) < L2sq(db); |
+ return false; |
+ } |
+}; |
+ |
+void |
+ConvexHull::find_pivot() { |
+ // Find pivot P; |
+ unsigned pivot = 0; |
+ for(unsigned i = 1; i < boundary.size(); i++) |
+ if(boundary[i] <= boundary[pivot]) |
+ pivot = i; |
+ |
+ std::swap(boundary[0], boundary[pivot]); |
+} |
+ |
+void |
+ConvexHull::angle_sort() { |
+// sort points by angle (resolve ties in favor of point farther from P); |
+// we leave the first one in place as our pivot |
+ std::sort(boundary.begin()+1, boundary.end(), angle_cmp(boundary[0])); |
+} |
+ |
+void |
+ConvexHull::graham_scan() { |
+ unsigned stac = 2; |
+ for(unsigned i = 2; i < boundary.size(); i++) { |
+ double o = SignedTriangleArea(boundary[stac-2], |
+ boundary[stac-1], |
+ boundary[i]); |
+ if(o == 0) { // colinear - dangerous... |
+ stac--; |
+ } else if(o < 0) { // anticlockwise |
+ } else { // remove concavity |
+ while(o >= 0 && stac > 2) { |
+ stac--; |
+ o = SignedTriangleArea(boundary[stac-2], |
+ boundary[stac-1], |
+ boundary[i]); |
+ } |
+ } |
+ boundary[stac++] = boundary[i]; |
+ } |
+ boundary.resize(stac); |
+} |
+ |
+void |
+ConvexHull::graham() { |
+ find_pivot(); |
+ angle_sort(); |
+ graham_scan(); |
+} |
+ |
+//Mathematically incorrect mod, but more useful. |
+int mod(int i, int l) { |
+ return i >= 0 ? |
+ i % l : (i % l) + l; |
+} |
+//OPT: usages can often be replaced by conditions |
+ |
+/*** ConvexHull::left |
+ * Tests if a point is left (outside) of a particular segment, n. */ |
+bool |
+ConvexHull::is_left(Point p, int n) { |
+ return SignedTriangleArea((*this)[n], (*this)[n+1], p) > 0; |
+} |
+ |
+/*** ConvexHull::find_positive |
+ * May return any number n where the segment n -> n + 1 (possibly looped around) in the hull such |
+ * that the point is on the wrong side to be within the hull. Returns -1 if it is within the hull.*/ |
+int |
+ConvexHull::find_left(Point p) { |
+ int l = boundary.size(); //Who knows if C++ is smart enough to optimize this? |
+ for(int i = 0; i < l; i++) { |
+ if(is_left(p, i)) return i; |
+ } |
+ return -1; |
+} |
+//OPT: do a spread iteration - quasi-random with no repeats and full coverage. |
+ |
+/*** ConvexHull::contains_point |
+ * In order to test whether a point is inside a convex hull we can travel once around the outside making |
+ * sure that each triangle made from an edge and the point has positive area. */ |
+bool |
+ConvexHull::contains_point(Point p) { |
+ return find_left(p) == -1; |
+} |
+ |
+/*** ConvexHull::add_point |
+ * to add a point we need to find whether the new point extends the boundary, and if so, what it |
+ * obscures. Tarjan? Jarvis?*/ |
+void |
+ConvexHull::merge(Point p) { |
+ std::vector<Point> out; |
+ |
+ int l = boundary.size(); |
+ |
+ if(l < 2) { |
+ boundary.push_back(p); |
+ return; |
+ } |
+ |
+ bool pushed = false; |
+ |
+ bool pre = is_left(p, -1); |
+ for(int i = 0; i < l; i++) { |
+ bool cur = is_left(p, i); |
+ if(pre) { |
+ if(cur) { |
+ if(!pushed) { |
+ out.push_back(p); |
+ pushed = true; |
+ } |
+ continue; |
+ } |
+ else if(!pushed) { |
+ out.push_back(p); |
+ pushed = true; |
+ } |
+ } |
+ out.push_back(boundary[i]); |
+ pre = cur; |
+ } |
+ |
+ boundary = out; |
+} |
+//OPT: quickly find an obscured point and find the bounds by extending from there. then push all points not within the bounds in order. |
+ //OPT: use binary searches to find the actual starts/ends, use known rights as boundaries. may require cooperation of find_left algo. |
+ |
+/*** ConvexHull::is_clockwise |
+ * We require that successive pairs of edges always turn right. |
+ * proposed algorithm: walk successive edges and require triangle area is positive. |
+ */ |
+bool |
+ConvexHull::is_clockwise() const { |
+ if(is_degenerate()) |
+ return true; |
+ Point first = boundary[0]; |
+ Point second = boundary[1]; |
+ for(std::vector<Point>::const_iterator it(boundary.begin()+2), e(boundary.end()); |
+ it != e;) { |
+ if(SignedTriangleArea(first, second, *it) > 0) |
+ return false; |
+ first = second; |
+ second = *it; |
+ ++it; |
+ } |
+ return true; |
+} |
+ |
+/*** ConvexHull::top_point_first |
+ * We require that the first point in the convex hull has the least y coord, and that off all such points on the hull, it has the least x coord. |
+ * proposed algorithm: track lexicographic minimum while walking the list. |
+ */ |
+bool |
+ConvexHull::top_point_first() const { |
+ std::vector<Point>::const_iterator pivot = boundary.begin(); |
+ for(std::vector<Point>::const_iterator it(boundary.begin()+1), |
+ e(boundary.end()); |
+ it != e; it++) { |
+ if((*it)[1] < (*pivot)[1]) |
+ pivot = it; |
+ else if(((*it)[1] == (*pivot)[1]) && |
+ ((*it)[0] < (*pivot)[0])) |
+ pivot = it; |
+ } |
+ return pivot == boundary.begin(); |
+} |
+//OPT: since the Y values are orderly there should be something like a binary search to do this. |
+ |
+/*** ConvexHull::no_colinear_points |
+ * We require that no three vertices are colinear. |
+proposed algorithm: We must be very careful about rounding here. |
+*/ |
+bool |
+ConvexHull::no_colinear_points() const { |
+ return true; |
+} |
+ |
+bool |
+ConvexHull::meets_invariants() const { |
+ return is_clockwise() && top_point_first() && no_colinear_points(); |
+} |
+ |
+/*** ConvexHull::is_degenerate |
+ * We allow three degenerate cases: empty, 1 point and 2 points. In many cases these should be handled explicitly. |
+ */ |
+bool |
+ConvexHull::is_degenerate() const { |
+ return boundary.size() < 3; |
+} |
+ |
+ |
+/* Here we really need a rotating calipers implementation. This implementation is slow and incorrect. |
+ This incorrectness is a problem because it throws off the algorithms. Perhaps I will come up with |
+ something better tomorrow. The incorrectness is in the order of the bridges - they must be in the |
+ order of traversal around. Since the a->b and b->a bridges are seperated, they don't need to be merge |
+ order, just the order of the traversal of the host hull. Currently some situations make a n->0 bridge |
+ first.*/ |
+pair< map<int, int>, map<int, int> > |
+bridges(ConvexHull a, ConvexHull b) { |
+ map<int, int> abridges; |
+ map<int, int> bbridges; |
+ |
+ for(unsigned ia = 0; ia < a.boundary.size(); ia++) { |
+ for(unsigned ib = 0; ib < b.boundary.size(); ib++) { |
+ Point d = b[ib] - a[ia]; |
+ Geom::Coord e = cross(d, a[ia - 1] - a[ia]), f = cross(d, a[ia + 1] - a[ia]); |
+ Geom::Coord g = cross(d, b[ib - 1] - a[ia]), h = cross(d, b[ib + 1] - a[ia]); |
+ if (e > 0 && f > 0 && g > 0 && h > 0) abridges[ia] = ib; |
+ else if(e < 0 && f < 0 && g < 0 && h < 0) bbridges[ib] = ia; |
+ } |
+ } |
+ |
+ return make_pair(abridges, bbridges); |
+} |
+ |
+std::vector<Point> bridge_points(ConvexHull a, ConvexHull b) { |
+ vector<Point> ret; |
+ pair< map<int, int>, map<int, int> > indices = bridges(a, b); |
+ for(map<int, int>::iterator it = indices.first.begin(); it != indices.first.end(); it++) { |
+ ret.push_back(a[it->first]); |
+ ret.push_back(b[it->second]); |
+ } |
+ for(map<int, int>::iterator it = indices.second.begin(); it != indices.second.end(); it++) { |
+ ret.push_back(b[it->first]); |
+ ret.push_back(a[it->second]); |
+ } |
+ return ret; |
+} |
+ |
+unsigned find_bottom_right(ConvexHull const &a) { |
+ unsigned it = 1; |
+ while(it < a.boundary.size() && |
+ a.boundary[it][Y] > a.boundary[it-1][Y]) |
+ it++; |
+ return it-1; |
+} |
+ |
+/*** ConvexHull sweepline_intersection(ConvexHull a, ConvexHull b); |
+ * find the intersection between two convex hulls. The intersection is also a convex hull. |
+ * (Proof: take any two points both in a and in b. Any point between them is in a by convexity, |
+ * and in b by convexity, thus in both. Need to prove still finite bounds.) |
+ * This algorithm works by sweeping a line down both convex hulls in parallel, working out the left and right edges of the new hull. |
+ */ |
+ConvexHull sweepline_intersection(ConvexHull const &a, ConvexHull const &b) { |
+ ConvexHull ret; |
+ |
+ unsigned al = 0; |
+ unsigned bl = 0; |
+ |
+ while(al+1 < a.boundary.size() && |
+ (a.boundary[al+1][Y] > b.boundary[bl][Y])) { |
+ al++; |
+ } |
+ while(bl+1 < b.boundary.size() && |
+ (b.boundary[bl+1][Y] > a.boundary[al][Y])) { |
+ bl++; |
+ } |
+ return ret; |
+ // al and bl now point to the top of the first pair of edges that overlap in y value |
+// double sweep_y = std::min(a.boundary[al][Y], |
+// b.boundary[bl][Y]); |
+} |
+ |
+/*** ConvexHull intersection(ConvexHull a, ConvexHull b); |
+ * find the intersection between two convex hulls. The intersection is also a convex hull. |
+ * (Proof: take any two points both in a and in b. Any point between them is in a by convexity, |
+ * and in b by convexity, thus in both. Need to prove still finite bounds.) |
+ */ |
+ConvexHull intersection(ConvexHull a, ConvexHull b) { |
+ ConvexHull ret; |
+// int ai = 0, bi = 0; |
+// unsigned aj = a.boundary.size() - 1; |
+// unsigned bj = b.boundary.size() - 1; |
+ |
+ /*while (true) { |
+ if(a[ai] |
+ }*/ |
+ return ret; |
+} |
+ |
+/*** ConvexHull merge(ConvexHull a, ConvexHull b); |
+ * find the smallest convex hull that surrounds a and b. |
+ */ |
+ConvexHull merge(ConvexHull a, ConvexHull b) { |
+ ConvexHull ret; |
+ |
+ pair< map<int, int>, map<int, int> > bpair = bridges(a, b); |
+ map<int, int> ab = bpair.first; |
+ map<int, int> bb = bpair.second; |
+ |
+ ab[-1] = 0; |
+ bb[-1] = 0; |
+ |
+ int i = -1; |
+ |
+ if(a.boundary[0][1] > b.boundary[0][1]) goto start_b; |
+ while(true) { |
+ for(; ab.count(i) == 0; i++) { |
+ ret.boundary.push_back(a[i]); |
+ if(i >= static_cast<int>(a.boundary.size())) return ret; |
+ } |
+ if(ab[i] == 0 && i != -1) break; |
+ i = ab[i]; |
+ start_b: |
+ |
+ for(; bb.count(i) == 0; i++) { |
+ ret.boundary.push_back(b[i]); |
+ if(i >= static_cast<int>(b.boundary.size())) return ret; |
+ } |
+ if(bb[i] == 0 && i != -1) break; |
+ i = bb[i]; |
+ } |
+ return ret; |
+} |
+ |
+ConvexHull graham_merge(ConvexHull a, ConvexHull b) { |
+ ConvexHull result; |
+ |
+ // we can avoid the find pivot step because of top_point_first |
+ if(b.boundary[0] <= a.boundary[0]) |
+ std::swap(a, b); |
+ |
+ result.boundary = a.boundary; |
+ result.boundary.insert(result.boundary.end(), |
+ b.boundary.begin(), b.boundary.end()); |
+ |
+/** if we modified graham scan to work top to bottom as proposed in lect754.pdf we could replace the |
+ angle sort with a simple merge sort type algorithm. furthermore, we could do the graham scan |
+ online, avoiding a bunch of memory copies. That would probably be linear. -- njh*/ |
+ result.angle_sort(); |
+ result.graham_scan(); |
+ |
+ return result; |
+} |
+//TODO: reinstate |
+/*ConvexCover::ConvexCover(Path const &sp) : path(&sp) { |
+ cc.reserve(sp.size()); |
+ for(Geom::Path::const_iterator it(sp.begin()), end(sp.end()); it != end; ++it) { |
+ cc.push_back(ConvexHull((*it).begin(), (*it).end())); |
+ } |
+}*/ |
+ |
+ |
+}; |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(substatement-open . 0)) |
+ indent-tabs-mode:nil |
+ c-brace-offset:0 |
+ fill-column:99 |
+ End: |
+ vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
+*/ |
+ |
+ |
Index: trunk/Scribus/scribus/third_party/lib2geom/solver.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/solver.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/solver.h (revision 14937) |
@@ -0,0 +1,33 @@ |
+#ifndef _SOLVE_SBASIS_H |
+#define _SOLVE_SBASIS_H |
+#include "point.h" |
+#include "sbasis.h" |
+ |
+namespace Geom{ |
+ |
+ class Point; |
+ |
+unsigned |
+crossing_count(Geom::Point const *V, /* Control pts of Bezier curve */ |
+ unsigned degree); /* Degree of Bezier curve */ |
+void |
+find_parametric_bezier_roots( |
+ Geom::Point const *w, /* The control points */ |
+ unsigned degree, /* The degree of the polynomial */ |
+ std::vector<double> & solutions, /* RETURN candidate t-values */ |
+ unsigned depth); /* The depth of the recursion */ |
+ |
+unsigned |
+crossing_count(double const *V, /* Control pts of Bezier curve */ |
+ unsigned degree, /* Degree of Bezier curve */ |
+ double left_t, double right_t); |
+void |
+find_bernstein_roots( |
+ double const *w, /* The control points */ |
+ unsigned degree, /* The degree of the polynomial */ |
+ std::vector<double> & solutions, /* RETURN candidate t-values */ |
+ unsigned depth, /* The depth of the recursion */ |
+ double left_t=0, double right_t=1); |
+ |
+}; |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/linear.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/linear.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/linear.h (revision 14937) |
@@ -0,0 +1,173 @@ |
+/* |
+ * linear.h - Linear fragment function class |
+ * |
+ * Authors: |
+ * Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * Michael Sloan <mgsloan@gmail.com> |
+ * |
+ * Copyright (C) 2006-2007 authors |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ */ |
+ |
+#ifndef SEEN_LINEAR_H |
+#define SEEN_LINEAR_H |
+#include "isnan.h" |
+#include "interval.h" |
+ |
+namespace Geom{ |
+ |
+inline double lerp(double t, double a, double b) { return a*(1-t) + b*t; } |
+ |
+class SBasis; |
+ |
+class Hat{ |
+public: |
+ Hat () {} |
+ Hat(double d) :d(d) {} |
+ operator double() const { return d; } |
+ double d; |
+}; |
+ |
+class Tri{ |
+public: |
+ Tri () {} |
+ Tri(double d) :d(d) {} |
+ operator double() const { return d; } |
+ double d; |
+}; |
+ |
+class Linear{ |
+public: |
+ double a[2]; |
+ Linear() {} |
+ Linear(double aa, double b) {a[0] = aa; a[1] = b;} |
+ Linear(Hat h, Tri t) { |
+ a[0] = double(h) - double(t)/2; |
+ a[1] = double(h) + double(t)/2; |
+ } |
+ |
+ Linear(Hat h) { |
+ a[0] = double(h); |
+ a[1] = double(h); |
+ } |
+ |
+ double operator[](const int i) const { |
+ assert(i >= 0); |
+ assert(i < 2); |
+ return a[i]; |
+ } |
+ double& operator[](const int i) { |
+ assert(i >= 0); |
+ assert(i < 2); |
+ return a[i]; |
+ } |
+ |
+ //IMPL: FragmentConcept |
+ typedef double output_type; |
+ inline bool isZero() const { return a[0] == 0 && a[1] == 0; } |
+ inline bool isConstant() const { return a[0] == a[1]; } |
+ inline bool isFinite() const { return is_finite(a[0]) && is_finite(a[1]); } |
+ |
+ inline double at0() const { return a[0]; } |
+ inline double at1() const { return a[1]; } |
+ |
+ inline double valueAt(double t) const { return lerp(t, a[0], a[1]); } |
+ inline double operator()(double t) const { return valueAt(t); } |
+ |
+ //defined in sbasis.h |
+ inline SBasis toSBasis() const; |
+ |
+ inline Interval bounds_exact() const { return Interval(a[0], a[1]); } |
+ inline Interval bounds_fast() const { return bounds_exact(); } |
+ inline Interval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); } |
+ |
+ operator Tri() const { |
+ return a[1] - a[0]; |
+ } |
+ operator Hat() const { |
+ return (a[1] + a[0])/2; |
+ } |
+}; |
+ |
+inline Linear reverse(Linear const &a) { return Linear(a[1], a[0]); } |
+ |
+//IMPL: AddableConcept |
+inline Linear operator+(Linear const & a, Linear const & b) { |
+ return Linear(a[0] + b[0], a[1] + b[1]); |
+} |
+inline Linear operator-(Linear const & a, Linear const & b) { |
+ return Linear(a[0] - b[0], a[1] - b[1]); |
+} |
+inline Linear& operator+=(Linear & a, Linear const & b) { |
+ a[0] += b[0]; a[1] += b[1]; |
+ return a; |
+} |
+inline Linear& operator-=(Linear & a, Linear const & b) { |
+ a[0] -= b[0]; a[1] -= b[1]; |
+ return a; |
+} |
+//IMPL: OffsetableConcept |
+inline Linear operator+(Linear const & a, double b) { |
+ return Linear(a[0] + b, a[1] + b); |
+} |
+inline Linear operator-(Linear const & a, double b) { |
+ return Linear(a[0] - b, a[1] - b); |
+} |
+inline Linear& operator+=(Linear & a, double b) { |
+ a[0] += b; a[1] += b; |
+ return a; |
+} |
+inline Linear& operator-=(Linear & a, double b) { |
+ a[0] -= b; a[1] -= b; |
+ return a; |
+} |
+//IMPL: boost::EqualityComparableConcept |
+inline bool operator==(Linear const & a, Linear const & b) { |
+ return a[0] == b[0] && a[1] == b[1]; |
+} |
+inline bool operator!=(Linear const & a, Linear const & b) { |
+ return a[0] != b[0] || a[1] != b[1]; |
+} |
+//IMPL: ScalableConcept |
+inline Linear operator-(Linear const &a) { |
+ return Linear(-a[0], -a[1]); |
+} |
+inline Linear operator*(Linear const & a, double b) { |
+ return Linear(a[0]*b, a[1]*b); |
+} |
+inline Linear operator/(Linear const & a, double b) { |
+ return Linear(a[0]/b, a[1]/b); |
+} |
+inline Linear operator*=(Linear & a, double b) { |
+ a[0] *= b; a[1] *= b; |
+ return a; |
+} |
+inline Linear operator/=(Linear & a, double b) { |
+ a[0] /= b; a[1] /= b; |
+ return a; |
+} |
+}; |
+ |
+#endif //SEEN_LINEAR_H |
Index: trunk/Scribus/scribus/third_party/lib2geom/choose.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/choose.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/choose.h (revision 14937) |
@@ -0,0 +1,67 @@ |
+/* |
+ * choose.h |
+ * |
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#ifndef _CHOOSE_H |
+#define _CHOOSE_H |
+ |
+// XXX: Can we keep only the left terms easily? |
+// this would more than halve the array |
+// row index becomes n2 = n/2, row2 = n2*(n2+1)/2, row = row2*2+(n&1)?n2:0 |
+// we could also leave off the ones |
+ |
+template <typename T> |
+T choose(unsigned n, unsigned k) { |
+ static std::vector<T> pascals_triangle; |
+ static unsigned rows_done = 0; |
+ // indexing is (0,0,), (1,0), (1,1), (2, 0)... |
+ // to get (i, j) i*(i+1)/2 + j |
+ if(k < 0 || k > n) return 0; |
+ if(rows_done <= n) {// we haven't got there yet |
+ if(rows_done == 0) { |
+ pascals_triangle.push_back(1); |
+ rows_done = 1; |
+ } |
+ while(rows_done <= n) { |
+ unsigned p = pascals_triangle.size() - rows_done; |
+ pascals_triangle.push_back(1); |
+ for(unsigned i = 0; i < rows_done-1; i++) { |
+ pascals_triangle.push_back(pascals_triangle[p] |
+ + pascals_triangle[p+1]); |
+ p++; |
+ } |
+ pascals_triangle.push_back(1); |
+ rows_done ++; |
+ } |
+ } |
+ unsigned row = (n*(n+1))/2; |
+ return pascals_triangle[row+k]; |
+} |
+ |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis-2d.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis-2d.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis-2d.cpp (revision 14937) |
@@ -0,0 +1,72 @@ |
+#include "sbasis-2d.h" |
+ |
+namespace Geom{ |
+ |
+SBasis extract_u(SBasis2d const &a, double u) { |
+ SBasis sb; |
+ double s = u*(1-u); |
+ |
+ for(unsigned vi = 0; vi < a.vs; vi++) { |
+ double sk = 1; |
+ Linear bo(0,0); |
+ for(unsigned ui = 0; ui < a.us; ui++) { |
+ bo += (extract_u(a.index(ui, vi), u))*sk; |
+ sk *= s; |
+ } |
+ sb.push_back(bo); |
+ } |
+ |
+ return sb; |
+} |
+ |
+SBasis extract_v(SBasis2d const &a, double v) { |
+ SBasis sb; |
+ double s = v*(1-v); |
+ |
+ for(unsigned ui = 0; ui < a.us; ui++) { |
+ double sk = 1; |
+ Linear bo(0,0); |
+ for(unsigned vi = 0; vi < a.vs; vi++) { |
+ bo += (extract_v(a.index(ui, vi), v))*sk; |
+ sk *= s; |
+ } |
+ sb.push_back(bo); |
+ } |
+ |
+ return sb; |
+} |
+ |
+SBasis compose(Linear2d const &a, D2<SBasis> const &p) { |
+ D2<SBasis> omp(-p[X] + 1, -p[Y] + 1); |
+ return multiply(omp[0], omp[1])*a[0] + |
+ multiply(p[0], omp[1])*a[1] + |
+ multiply(omp[0], p[1])*a[2] + |
+ multiply(p[0], p[1])*a[3]; |
+} |
+ |
+SBasis |
+compose(SBasis2d const &fg, D2<SBasis> const &p) { |
+ SBasis B; |
+ SBasis s[2]; |
+ SBasis ss[2]; |
+ for(unsigned dim = 0; dim < 2; dim++) |
+ s[dim] = p[dim]*(Linear(1) - p[dim]); |
+ ss[1] = Linear(1); |
+ for(unsigned vi = 0; vi < fg.vs; vi++) { |
+ ss[0] = ss[1]; |
+ for(unsigned ui = 0; ui < fg.us; ui++) { |
+ unsigned i = ui + vi*fg.us; |
+ B += ss[0]*compose(fg[i], p); |
+ ss[0] *= s[0]; |
+ } |
+ ss[1] *= s[1]; |
+ } |
+ return B; |
+} |
+ |
+D2<SBasis> |
+compose_each(D2<SBasis2d> const &fg, D2<SBasis> const &p) { |
+ return D2<SBasis>(compose(fg[X], p), compose(fg[Y], p)); |
+} |
+ |
+}; |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis-roots.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis-roots.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis-roots.cpp (revision 14937) |
@@ -0,0 +1,350 @@ |
+/** root finding for sbasis functions. |
+ * Copyright 2006 N Hurst |
+ * Copyright 2007 JF Barraud |
+ * |
+ * It is more efficient to find roots of f(t) = c_0, c_1, ... all at once, rather than iterating. |
+ * |
+ * Todo/think about: |
+ * multi-roots using bernstein method, one approach would be: |
+ sort c |
+ take median and find roots of that |
+ whenever a segment lies entirely on one side of the median, |
+ find the median of the half and recurse. |
+ |
+ in essence we are implementing quicksort on a continuous function |
+ |
+ * the gsl poly roots finder is faster than bernstein too, but we don't use it for 3 reasons: |
+ |
+ a) it requires convertion to poly, which is numerically unstable |
+ |
+ b) it requires gsl (which is currently not a dependency, and would bring in a whole slew of unrelated stuff) |
+ |
+ c) it finds all roots, even complex ones. We don't want to accidently treat a nearly real root as a real root |
+ |
+From memory gsl poly roots was about 10 times faster than bernstein in the case where all the roots |
+are in [0,1] for polys of order 5. I spent some time working out whether eigenvalue root finding |
+could be done directly in sbasis space, but the maths was too hard for me. -- njh |
+ |
+jfbarraud: eigenvalue root finding could be done directly in sbasis space ? |
+ |
+njh: I don't know, I think it should. You would make a matrix whose characteristic polynomial was |
+correct, but do it by putting the sbasis terms in the right spots in the matrix. normal eigenvalue |
+root finding makes a matrix that is a diagonal + a row along the top. This matrix has the property |
+that its characteristic poly is just the poly whose coefficients are along the top row. |
+ |
+Now an sbasis function is a linear combination of the poly coeffs. So it seems to me that you |
+should be able to put the sbasis coeffs directly into a matrix in the right spots so that the |
+characteristic poly is the sbasis. You'll still have problems b) and c). |
+ |
+We might be able to lift an eigenvalue solver and include that directly into 2geom. Eigenvalues |
+also allow you to find intersections of multiple curves but require solving n*m x n*m matrices. |
+ |
+ **/ |
+ |
+#include <cmath> |
+#include <map> |
+ |
+#include "sbasis.h" |
+#include "sbasis-to-bezier.h" |
+#include "solver.h" |
+ |
+using namespace std; |
+ |
+namespace Geom{ |
+ |
+Interval bounds_exact(SBasis const &a) { |
+ Interval result = Interval(a.at0(), a.at1()); |
+ SBasis df = derivative(a); |
+ vector<double>extrema = roots(df); |
+ for (unsigned i=0; i<extrema.size(); i++){ |
+ result.extendTo(a(extrema[i])); |
+ } |
+ return result; |
+} |
+ |
+Interval bounds_fast(const SBasis &sb, int order) { |
+ Interval res; |
+ for(int j = sb.size()-1; j>=order; j--) { |
+ double a=sb[j][0]; |
+ double b=sb[j][1]; |
+ |
+ double v, t = 0; |
+ v = res[0]; |
+ if (v<0) t = ((b-a)/v+1)*0.5; |
+ if (v>=0 || t<0 || t>1) { |
+ res[0] = std::min(a,b); |
+ }else{ |
+ res[0]=lerp(t, a+v*t, b); |
+ } |
+ |
+ v = res[1]; |
+ if (v>0) t = ((b-a)/v+1)*0.5; |
+ if (v<=0 || t<0 || t>1) { |
+ res[1] = std::max(a,b); |
+ }else{ |
+ res[1]=lerp(t, a+v*t, b); |
+ } |
+ } |
+ if (order>0) res*=pow(.25,order); |
+ return res; |
+} |
+ |
+Interval bounds_local(const SBasis &sb, const Interval &i, int order) { |
+ double t0=i.min(), t1=i.max(), lo=0., hi=0.; |
+ for(int j = sb.size()-1; j>=order; j--) { |
+ double a=sb[j][0]; |
+ double b=sb[j][1]; |
+ |
+ double t = 0; |
+ if (lo<0) t = ((b-a)/lo+1)*0.5; |
+ if (lo>=0 || t<t0 || t>t1) { |
+ lo = std::min(a*(1-t0)+b*t0+lo*t0*(1-t0),a*(1-t1)+b*t1+lo*t1*(1-t1)); |
+ }else{ |
+ lo = lerp(t, a+lo*t, b); |
+ } |
+ |
+ if (hi>0) t = ((b-a)/hi+1)*0.5; |
+ if (hi<=0 || t<t0 || t>t1) { |
+ hi = std::max(a*(1-t0)+b*t0+hi*t0*(1-t0),a*(1-t1)+b*t1+hi*t1*(1-t1)); |
+ }else{ |
+ hi = lerp(t, a+hi*t, b); |
+ } |
+ } |
+ Interval res = Interval(lo,hi); |
+ if (order>0) res*=pow(.25,order); |
+ return res; |
+} |
+ |
+//-- multi_roots ------------------------------------ |
+// goal: solve f(t)=c for several c at once. |
+/* algo: -compute f at both ends of the given segment [a,b]. |
+ -compute bounds m<df(t)<M for df on the segment. |
+ let c and C be the levels below and above f(a): |
+ going from f(a) down to c with slope m takes at least time (f(a)-c)/m |
+ going from f(a) up to C with slope M takes at least time (C-f(a))/M |
+ From this we conclude there are no roots before a'=a+min((f(a)-c)/m,(C-f(a))/M). |
+ Do the same for b: compute some b' such that there are no roots in (b',b]. |
+ -if [a',b'] is not empty, repeat the process with [a',(a'+b')/2] and [(a'+b')/2,b']. |
+ unfortunately, extra care is needed about rounding errors, and also to avoid the repetition of roots, |
+ making things tricky and unpleasant... |
+*/ |
+//TODO: Make sure the code is "rounding-errors proof" and take care about repetition of roots! |
+ |
+ |
+static int upper_level(vector<double> const &levels,double x,double tol=0.){ |
+ return(upper_bound(levels.begin(),levels.end(),x-tol)-levels.begin()); |
+} |
+ |
+static void multi_roots_internal(SBasis const &f, |
+ SBasis const &df, |
+ std::vector<double> const &levels, |
+ std::vector<std::vector<double> > &roots, |
+ double htol, |
+ double vtol, |
+ double a, |
+ double fa, |
+ double b, |
+ double fb){ |
+ |
+ if (f.size()==0){ |
+ int idx; |
+ idx=upper_level(levels,0,vtol); |
+ if (idx<(int)levels.size()&&fabs(levels.at(idx))<=vtol){ |
+ roots[idx].push_back(a); |
+ roots[idx].push_back(b); |
+ } |
+ return; |
+ } |
+////usefull? |
+// if (f.size()==1){ |
+// int idxa=upper_level(levels,fa); |
+// int idxb=upper_level(levels,fb); |
+// if (fa==fb){ |
+// if (fa==levels[idxa]){ |
+// roots[a]=idxa; |
+// roots[b]=idxa; |
+// } |
+// return; |
+// } |
+// int idx_min=std::min(idxa,idxb); |
+// int idx_max=std::max(idxa,idxb); |
+// if (idx_max==levels.size()) idx_max-=1; |
+// for(int i=idx_min;i<=idx_max; i++){ |
+// double t=a+(b-a)*(levels[i]-fa)/(fb-fa); |
+// if(a<t&&t<b) roots[t]=i; |
+// } |
+// return; |
+// } |
+ if ((b-a)<htol){ |
+ //TODO: use different tol for t and f ? |
+ //TODO: unsigned idx ? (remove int casts when fixed) |
+ int idx=std::min(upper_level(levels,fa,vtol),upper_level(levels,fb,vtol)); |
+ if (idx==(int)levels.size()) idx-=1; |
+ double c=levels.at(idx); |
+ if((fa-c)*(fb-c)<=0||fabs(fa-c)<vtol||fabs(fb-c)<vtol){ |
+ roots[idx].push_back((a+b)/2); |
+ } |
+ return; |
+ } |
+ |
+ int idxa=upper_level(levels,fa,vtol); |
+ int idxb=upper_level(levels,fb,vtol); |
+ |
+ Interval bs = bounds_local(df,Interval(a,b)); |
+ |
+ //first times when a level (higher or lower) can be reached from a or b. |
+ double ta_hi,tb_hi,ta_lo,tb_lo; |
+ ta_hi=ta_lo=b+1;//default values => no root there. |
+ tb_hi=tb_lo=a-1;//default values => no root there. |
+ |
+ if (idxa<(int)levels.size() && fabs(fa-levels.at(idxa))<vtol){//a can be considered a root. |
+ //ta_hi=ta_lo=a; |
+ roots[idxa].push_back(a); |
+ ta_hi=ta_lo=a+htol; |
+ }else{ |
+ if (bs.max()>0 && idxa<(int)levels.size()) |
+ ta_hi=a+(levels.at(idxa )-fa)/bs.max(); |
+ if (bs.min()<0 && idxa>0) |
+ ta_lo=a+(levels.at(idxa-1)-fa)/bs.min(); |
+ } |
+ if (idxb<(int)levels.size() && fabs(fb-levels.at(idxb))<vtol){//b can be considered a root. |
+ //tb_hi=tb_lo=b; |
+ roots[idxb].push_back(b); |
+ tb_hi=tb_lo=b-htol; |
+ }else{ |
+ if (bs.min()<0 && idxb<(int)levels.size()) |
+ tb_hi=b+(levels.at(idxb )-fb)/bs.min(); |
+ if (bs.max()>0 && idxb>0) |
+ tb_lo=b+(levels.at(idxb-1)-fb)/bs.max(); |
+ } |
+ |
+ double t0,t1; |
+ t0=std::min(ta_hi,ta_lo); |
+ t1=std::max(tb_hi,tb_lo); |
+ //hum, rounding errors frighten me! so I add this +tol... |
+ if (t0>t1+htol) return;//no root here. |
+ |
+ if (fabs(t1-t0)<htol){ |
+ multi_roots_internal(f,df,levels,roots,htol,vtol,t0,f(t0),t1,f(t1)); |
+ }else{ |
+ double t,t_left,t_right,ft,ft_left,ft_right; |
+ t_left =t_right =t =(t0+t1)/2; |
+ ft_left=ft_right=ft=f(t); |
+ int idx=upper_level(levels,ft,vtol); |
+ if (idx<(int)levels.size() && fabs(ft-levels.at(idx))<vtol){//t can be considered a root. |
+ roots[idx].push_back(t); |
+ //we do not want to count it twice (from the left and from the right) |
+ t_left =t-htol/2; |
+ t_right=t+htol/2; |
+ ft_left =f(t_left); |
+ ft_right=f(t_right); |
+ } |
+ multi_roots_internal(f,df,levels,roots,htol,vtol,t0 ,f(t0) ,t_left,ft_left); |
+ multi_roots_internal(f,df,levels,roots,htol,vtol,t_right,ft_right,t1 ,f(t1) ); |
+ } |
+} |
+ |
+std::vector<std::vector<double> > multi_roots(SBasis const &f, |
+ std::vector<double> const &levels, |
+ double htol, |
+ double vtol, |
+ double a, |
+ double b){ |
+ |
+ std::vector<std::vector<double> > roots(levels.size(), std::vector<double>()); |
+ |
+ SBasis df=derivative(f); |
+ multi_roots_internal(f,df,levels,roots,htol,vtol,a,f(a),b,f(b)); |
+ |
+ return(roots); |
+} |
+//------------------------------------- |
+ |
+#if 0 |
+double Laguerre_internal(SBasis const & p, |
+ double x0, |
+ double tol, |
+ bool & quad_root) { |
+ double a = 2*tol; |
+ double xk = x0; |
+ double n = p.size(); |
+ quad_root = false; |
+ while(a > tol) { |
+ //std::cout << "xk = " << xk << std::endl; |
+ Linear b = p.back(); |
+ Linear d(0), f(0); |
+ double err = fabs(b); |
+ double abx = fabs(xk); |
+ for(int j = p.size()-2; j >= 0; j--) { |
+ f = xk*f + d; |
+ d = xk*d + b; |
+ b = xk*b + p[j]; |
+ err = fabs(b) + abx*err; |
+ } |
+ |
+ err *= 1e-7; // magic epsilon for convergence, should be computed from tol |
+ |
+ double px = b; |
+ if(fabs(b) < err) |
+ return xk; |
+ //if(std::norm(px) < tol*tol) |
+ // return xk; |
+ double G = d / px; |
+ double H = G*G - f / px; |
+ |
+ //std::cout << "G = " << G << "H = " << H; |
+ double radicand = (n - 1)*(n*H-G*G); |
+ //assert(radicand.real() > 0); |
+ if(radicand < 0) |
+ quad_root = true; |
+ //std::cout << "radicand = " << radicand << std::endl; |
+ if(G.real() < 0) // here we try to maximise the denominator avoiding cancellation |
+ a = - std::sqrt(radicand); |
+ else |
+ a = std::sqrt(radicand); |
+ //std::cout << "a = " << a << std::endl; |
+ a = n / (a + G); |
+ //std::cout << "a = " << a << std::endl; |
+ xk -= a; |
+ } |
+ //std::cout << "xk = " << xk << std::endl; |
+ return xk; |
+} |
+#endif |
+ |
+void subdiv_sbasis(SBasis const & s, |
+ std::vector<double> & roots, |
+ double left, double right) { |
+ Interval bs = bounds_fast(s); |
+ if(bs.min() > 0 || bs.max() < 0) |
+ return; // no roots here |
+ if(s.tailError(1) < 1e-7) { |
+ double t = s[0][0] / (s[0][0] - s[0][1]); |
+ roots.push_back(left*(1-t) + t*right); |
+ return; |
+ } |
+ double middle = (left + right)/2; |
+ subdiv_sbasis(compose(s, Linear(0, 0.5)), roots, left, middle); |
+ subdiv_sbasis(compose(s, Linear(0.5, 1.)), roots, middle, right); |
+} |
+ |
+// It is faster to use the bernstein root finder for small degree polynomials (<100?. |
+ |
+std::vector<double> roots(SBasis const & s) { |
+ if(s.size() == 0) return std::vector<double>(); |
+ |
+ return sbasis_to_bezier(s).roots(); |
+} |
+ |
+}; |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/basic-intersection.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/basic-intersection.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/basic-intersection.h (revision 14937) |
@@ -0,0 +1,34 @@ |
+#include "sbasis.h" |
+#include "bezier-to-sbasis.h" |
+#include "sbasis-to-bezier.h" |
+#include "d2.h" |
+ |
+namespace Geom { |
+ |
+std::vector<std::pair<double, double> > |
+find_intersections( D2<SBasis> const & A, |
+ D2<SBasis> const & B); |
+ |
+std::vector<std::pair<double, double> > |
+find_self_intersections(D2<SBasis> const & A); |
+ |
+// Bezier form |
+std::vector<std::pair<double, double> > |
+find_intersections( std::vector<Point> const & A, |
+ std::vector<Point> const & B); |
+ |
+std::vector<std::pair<double, double> > |
+find_self_intersections(std::vector<Point> const & A); |
+ |
+}; |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/geom.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/geom.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/geom.h (revision 14937) |
@@ -0,0 +1,76 @@ |
+/** |
+ * \file geom.h |
+ * \brief Various geometrical calculations |
+ * |
+ * Authors: |
+ * Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * |
+ * Copyright (C) 1999-2002 authors |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+#ifndef LIB2GEOM_SEEN_GEOM_H |
+#define LIB2GEOM_SEEN_GEOM_H |
+ |
+//TODO: move somewhere else |
+ |
+#include <vector> |
+#include "point.h" |
+ |
+namespace Geom { |
+ |
+enum IntersectorKind { |
+ intersects = 0, |
+ parallel, |
+ coincident, |
+ no_intersection |
+}; |
+ |
+int |
+intersector_ccw(const Geom::Point& p0, const Geom::Point& p1, |
+ const Geom::Point& p2); |
+ |
+/* intersectors */ |
+ |
+IntersectorKind |
+line_intersection(Geom::Point const &n0, double const d0, |
+ Geom::Point const &n1, double const d1, |
+ Geom::Point &result); |
+ |
+IntersectorKind |
+segment_intersect(Geom::Point const &p00, Geom::Point const &p01, |
+ Geom::Point const &p10, Geom::Point const &p11, |
+ Geom::Point &result); |
+ |
+IntersectorKind |
+line_twopoint_intersect(Geom::Point const &p00, Geom::Point const &p01, |
+ Geom::Point const &p10, Geom::Point const &p11, |
+ Geom::Point &result); |
+ |
+int centroid(std::vector<Geom::Point> p, Geom::Point& centroid, double &area); |
+ |
+} |
+ |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/d2-sbasis.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/d2-sbasis.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/d2-sbasis.h (revision 14937) |
@@ -0,0 +1,91 @@ |
+#ifdef _2GEOM_D2 /*This is intentional: we don't actually want anyone to |
+ include this, other than D2.h. If somone else tries, D2 |
+ won't be defined. If it is, this will already be included. */ |
+#ifndef __2GEOM_SBASIS_CURVE_H |
+#define __2GEOM_SBASIS_CURVE_H |
+ |
+#include "sbasis.h" |
+#include "sbasis-2d.h" |
+#include "piecewise.h" |
+#include "matrix.h" |
+ |
+//TODO: implement intersect |
+ |
+namespace Geom { |
+ |
+inline D2<SBasis> compose(D2<SBasis> const & a, SBasis const & b) { |
+ return D2<SBasis>(compose(a[X], b), compose(a[Y], b)); |
+} |
+ |
+SBasis L2(D2<SBasis> const & a, unsigned k); |
+double L2(D2<double> const & a); |
+ |
+D2<SBasis> multiply(Linear const & a, D2<SBasis> const & b); |
+inline D2<SBasis> operator*(Linear const & a, D2<SBasis> const & b) { return multiply(a, b); } |
+D2<SBasis> multiply(SBasis const & a, D2<SBasis> const & b); |
+inline D2<SBasis> operator*(SBasis const & a, D2<SBasis> const & b) { return multiply(a, b); } |
+D2<SBasis> truncate(D2<SBasis> const & a, unsigned terms); |
+ |
+unsigned sbasis_size(D2<SBasis> const & a); |
+double tail_error(D2<SBasis> const & a, unsigned tail); |
+ |
+//Piecewise<D2<SBasis> > specific decls: |
+ |
+Piecewise<D2<SBasis> > sectionize(D2<Piecewise<SBasis> > const &a); |
+D2<Piecewise<SBasis> > make_cuts_independant(Piecewise<D2<SBasis> > const &a); |
+Piecewise<D2<SBasis> > rot90(Piecewise<D2<SBasis> > const &a); |
+Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b); |
+Piecewise<SBasis> cross(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b); |
+ |
+Piecewise<D2<SBasis> > operator*(Piecewise<D2<SBasis> > const &a, Matrix const &m); |
+ |
+Piecewise<D2<SBasis> > force_continuity(Piecewise<D2<SBasis> > const &f, |
+ double tol=0, |
+ bool closed=false); |
+ |
+class CoordIterator |
+: public std::iterator<std::input_iterator_tag, SBasis const> |
+{ |
+public: |
+ CoordIterator(std::vector<D2<SBasis> >::const_iterator const &iter, unsigned d) : impl_(iter), ix_(d) {} |
+ |
+ inline bool operator==(CoordIterator const &other) { return other.impl_ == impl_; } |
+ inline bool operator!=(CoordIterator const &other) { return other.impl_ != impl_; } |
+ |
+ inline SBasis operator*() const { |
+ return (*impl_)[ix_]; |
+ } |
+ |
+ inline CoordIterator &operator++() { |
+ ++impl_; |
+ return *this; |
+ } |
+ inline CoordIterator operator++(int) { |
+ CoordIterator old=*this; |
+ ++(*this); |
+ return old; |
+ } |
+ |
+private: |
+ std::vector<D2<SBasis> >::const_iterator impl_; |
+ unsigned ix_; |
+}; |
+ |
+inline CoordIterator iterateCoord(Piecewise<D2<SBasis> > const &a, unsigned d) { |
+ return CoordIterator(a.segs.begin(), d); |
+} |
+ |
+//bounds specializations with order |
+inline Rect bounds_fast(D2<SBasis> const & s, unsigned order=0) { |
+ return Rect(bounds_fast(s[X], order), |
+ bounds_fast(s[Y], order)); |
+} |
+inline Rect bounds_local(D2<SBasis> const & s, Interval i, unsigned order=0) { |
+ return Rect(bounds_local(s[X], i, order), |
+ bounds_local(s[Y], i, order)); |
+} |
+ |
+} |
+ |
+#endif |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/svg-path.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/svg-path.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/svg-path.h (revision 14937) |
@@ -0,0 +1,127 @@ |
+/* |
+ * callback interface for SVG path data |
+ * |
+ * Copyright 2007 MenTaLguY <mental@rydia.net> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#ifndef SEEN_SVG_PATH_H |
+#define SEEN_SVG_PATH_H |
+ |
+#include "path.h" |
+#include <iterator> |
+ |
+namespace Geom { |
+ |
+class SVGPathSink { |
+public: |
+ virtual void moveTo(Point p) = 0; |
+ virtual void lineTo(Point p) = 0; |
+ virtual void curveTo(Point c0, Point c1, Point p) = 0; |
+ virtual void quadTo(Point c, Point p) = 0; |
+ virtual void arcTo(double rx, double ry, double angle, |
+ bool large_arc, bool sweep, Point p) = 0; |
+ virtual void closePath() = 0; |
+ virtual void finish() = 0; |
+ virtual ~SVGPathSink() {} |
+}; |
+ |
+void output_svg_path(Path &path, SVGPathSink &sink); |
+ |
+template <typename OutputIterator> |
+class SVGPathGenerator : public SVGPathSink { |
+public: |
+ explicit SVGPathGenerator(OutputIterator out) |
+ : _in_path(false), _out(out) {} |
+ |
+ void moveTo(Point p) { |
+ finish(); |
+ _path.start(p); |
+ _in_path = true; |
+ } |
+//TODO: what if _in_path = false? |
+ void lineTo(Point p) { |
+ _path.template appendNew<LineSegment>(p); |
+ } |
+ |
+ void quadTo(Point c, Point p) { |
+ _path.template appendNew<QuadraticBezier>(c, p); |
+ } |
+ |
+ void curveTo(Point c0, Point c1, Point p) { |
+ _path.template appendNew<CubicBezier>(c0, c1, p); |
+ } |
+ |
+ void arcTo(double rx, double ry, double angle, |
+ bool large_arc, bool sweep, Point p) |
+ { |
+ _path.template appendNew<SVGEllipticalArc>(rx, ry, angle, |
+ large_arc, sweep, p); |
+ } |
+ |
+ void closePath() { |
+ _path.close(); |
+ finish(); |
+ } |
+ |
+ void finish() { |
+ if (_in_path) { |
+ _in_path = false; |
+ *_out = _path; |
+ _path.clear(); |
+ _path.close(false); |
+ } |
+ } |
+ |
+protected: |
+ bool _in_path; |
+ OutputIterator _out; |
+ Path _path; |
+}; |
+ |
+typedef std::back_insert_iterator<std::vector<Path> > iter; |
+ |
+class PathBuilder : public SVGPathGenerator<iter> { |
+private: |
+ std::vector<Path> _pathset; |
+public: |
+ PathBuilder() : SVGPathGenerator<iter>(iter(_pathset)) {} |
+ std::vector<Path> const &peek() const {return _pathset;} |
+}; |
+ |
+} |
+ |
+#endif |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/transforms.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/transforms.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/transforms.cpp (revision 14937) |
@@ -0,0 +1,48 @@ |
+#include "transforms.h" |
+ |
+namespace Geom { |
+ |
+Matrix operator*(Translate const &t, Scale const &s) { |
+ Matrix ret(s); |
+ ret[4] = t[X] * s[X]; |
+ ret[5] = t[Y] * s[Y]; |
+ return ret; |
+} |
+ |
+Matrix operator*(Translate const &t, Rotate const &r) { |
+ Matrix ret(r); |
+ ret.setTranslation(t.vec * ret); |
+ return ret; |
+} |
+ |
+Matrix operator*(Scale const &s, Translate const &t) { |
+ return Matrix(s[0], 0, |
+ 0 , s[1], |
+ t[0], t[1]); |
+} |
+ |
+Matrix operator*(Scale const &s, Matrix const &m) { |
+ Matrix ret(m); |
+ ret[0] *= s[X]; |
+ ret[1] *= s[X]; |
+ ret[2] *= s[Y]; |
+ ret[3] *= s[Y]; |
+ return ret; |
+} |
+ |
+Matrix operator*(Matrix const &m, Translate const &t) { |
+ Matrix ret(m); |
+ ret[4] += t[X]; |
+ ret[5] += t[Y]; |
+ return ret; |
+} |
+ |
+Matrix operator*(Matrix const &m, Scale const &s) { |
+ Matrix ret(m); |
+ ret[0] *= s[X]; ret[1] *= s[Y]; |
+ ret[2] *= s[X]; ret[3] *= s[Y]; |
+ ret[4] *= s[X]; ret[5] *= s[Y]; |
+ return ret; |
+} |
+ |
+} |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis-geometric.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis-geometric.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis-geometric.cpp (revision 14937) |
@@ -0,0 +1,378 @@ |
+#include "sbasis-geometric.h" |
+#include "sbasis.h" |
+#include "sbasis-math.h" |
+//#include "solver.h" |
+#include "sbasis-geometric.h" |
+ |
+/** Geometric operators on D2<SBasis> (1D->2D). |
+ * Copyright 2007 JF Barraud |
+ * Copyright 2007 N Hurst |
+ * |
+ * The functions defined in this header related to 2d geometric operations such as arc length, |
+ * unit_vector, curvature, and centroid. Most are built on top of unit_vector, which takes an |
+ * arbitrary D2 and returns a D2 with unit length with the same direction. |
+ * |
+ * Todo/think about: |
+ * arclength D2 -> sbasis (giving arclength function) |
+ * does uniform_speed return natural parameterisation? |
+ * integrate sb2d code from normal-bundle |
+ * angle(md<2>) -> sbasis (gives angle from vector - discontinuous?) |
+ * osculating circle center? |
+ * |
+ **/ |
+ |
+//namespace Geom{ |
+using namespace Geom; |
+using namespace std; |
+ |
+//Some utils first. |
+//TODO: remove this!! |
+static vector<double> |
+vect_intersect(vector<double> const &a, vector<double> const &b, double tol=0.){ |
+ vector<double> inter; |
+ unsigned i=0,j=0; |
+ while ( i<a.size() && j<b.size() ){ |
+ if (fabs(a[i]-b[j])<tol){ |
+ inter.push_back(a[i]); |
+ i+=1; |
+ j+=1; |
+ }else if (a[i]<b[j]){ |
+ i+=1; |
+ }else if (a[i]>b[j]){ |
+ j+=1; |
+ } |
+ } |
+ return inter; |
+} |
+ |
+static SBasis divide_by_sk(SBasis const &a, int k) { |
+ assert( k<(int)a.size()); |
+ if(k < 0) return shift(a,-k); |
+ SBasis c; |
+ c.insert(c.begin(), a.begin()+k, a.end()); |
+ return c; |
+} |
+ |
+static SBasis divide_by_t0k(SBasis const &a, int k) { |
+ if(k < 0) { |
+ SBasis c = Linear(0,1); |
+ for (int i=2; i<=-k; i++){ |
+ c*=c; |
+ } |
+ c*=a; |
+ return(c); |
+ }else{ |
+ SBasis c = Linear(1,0); |
+ for (int i=2; i<=k; i++){ |
+ c*=c; |
+ } |
+ c*=a; |
+ return(divide_by_sk(c,k)); |
+ } |
+} |
+ |
+static SBasis divide_by_t1k(SBasis const &a, int k) { |
+ if(k < 0) { |
+ SBasis c = Linear(1,0); |
+ for (int i=2; i<=-k; i++){ |
+ c*=c; |
+ } |
+ c*=a; |
+ return(c); |
+ }else{ |
+ SBasis c = Linear(0,1); |
+ for (int i=2; i<=k; i++){ |
+ c*=c; |
+ } |
+ c*=a; |
+ return(divide_by_sk(c,k)); |
+ } |
+} |
+ |
+static D2<SBasis> RescaleForNonVanishingEnds(D2<SBasis> const &MM, double ZERO=1.e-4){ |
+ D2<SBasis> M = MM; |
+ //TODO: divide by all the s at once!!! |
+ while (fabs(M[0].at0())<ZERO && |
+ fabs(M[1].at0())<ZERO && |
+ fabs(M[0].at1())<ZERO && |
+ fabs(M[1].at1())<ZERO){ |
+ M[0] = divide_by_sk(M[0],1); |
+ M[1] = divide_by_sk(M[1],1); |
+ } |
+ while (fabs(M[0].at0())<ZERO && fabs(M[1].at0())<ZERO){ |
+ M[0] = divide_by_t0k(M[0],1); |
+ M[1] = divide_by_t0k(M[1],1); |
+ } |
+ while (fabs(M[0].at1())<ZERO && fabs(M[1].at1())<ZERO){ |
+ M[0] = divide_by_t1k(M[0],1); |
+ M[1] = divide_by_t1k(M[1],1); |
+ } |
+ return M; |
+} |
+ |
+//================================================================= |
+//TODO: what's this for?!?! |
+Piecewise<D2<SBasis> > |
+Geom::cutAtRoots(Piecewise<D2<SBasis> > const &M, double ZERO){ |
+ vector<double> rts; |
+ for (unsigned i=0; i<M.size(); i++){ |
+ vector<double> seg_rts = roots((M.segs[i])[0]); |
+ seg_rts = vect_intersect(seg_rts, roots((M.segs[i])[1]), ZERO); |
+ Linear mapToDom = Linear(M.cuts[i],M.cuts[i+1]); |
+ for (unsigned r=0; r<seg_rts.size(); r++){ |
+ seg_rts[r]= mapToDom(seg_rts[r]); |
+ } |
+ rts.insert(rts.end(),seg_rts.begin(),seg_rts.end()); |
+ } |
+ return partition(M,rts); |
+} |
+ |
+Piecewise<SBasis> |
+Geom::atan2(Piecewise<D2<SBasis> > const &vect, double tol, unsigned order){ |
+ Piecewise<SBasis> result; |
+ Piecewise<D2<SBasis> > v = cutAtRoots(vect); |
+ result.cuts.push_back(v.cuts.front()); |
+ for (unsigned i=0; i<v.size(); i++){ |
+ |
+ D2<SBasis> vi = RescaleForNonVanishingEnds(v.segs[i]); |
+ SBasis x=vi[0], y=vi[1]; |
+ Piecewise<SBasis> angle; |
+ angle = divide (x*derivative(y)-y*derivative(x), x*x+y*y, tol, order); |
+ |
+ //TODO: I don't understand this - sign. |
+ angle = integral(-angle); |
+ Point vi0 = vi.at0(); |
+ angle += -std::atan2(vi0[1],vi0[0]) - angle[0].at0(); |
+ //TODO: deal with 2*pi jumps form one seg to the other... |
+ //TODO: not exact at t=1 because of the integral. |
+ //TODO: force continuity? |
+ |
+ angle.setDomain(Interval(v.cuts[i],v.cuts[i+1])); |
+ result.concat(angle); |
+ } |
+ return result; |
+} |
+Piecewise<SBasis> |
+Geom::atan2(D2<SBasis> const &vect, double tol, unsigned order){ |
+ return atan2(Piecewise<D2<SBasis> >(vect),tol,order); |
+} |
+ |
+//unitVector(x,y) is computed as (b,-a) where a and b are solutions of: |
+// ax+by=0 (eqn1) and a^2+b^2=1 (eqn2) |
+Piecewise<D2<SBasis> > |
+Geom::unitVector(D2<SBasis> const &V_in, double tol, unsigned order){ |
+ D2<SBasis> V = RescaleForNonVanishingEnds(V_in); |
+ if (V[0].empty() && V[1].empty()) |
+ return Piecewise<D2<SBasis> >(D2<SBasis>(Linear(1),SBasis())); |
+ SBasis x = V[0], y = V[1], a, b; |
+ SBasis r_eqn1, r_eqn2; |
+ |
+ Point v0 = unit_vector(V.at0()); |
+ Point v1 = unit_vector(V.at1()); |
+ a.push_back(Linear(-v0[1],-v1[1])); |
+ b.push_back(Linear( v0[0], v1[0])); |
+ |
+ r_eqn1 = -(a*x+b*y); |
+ r_eqn2 = Linear(1.)-(a*a+b*b); |
+ |
+ for (unsigned k=1; k<=order; k++){ |
+ double r0 = (k<r_eqn1.size())? r_eqn1.at(k).at0() : 0; |
+ double r1 = (k<r_eqn1.size())? r_eqn1.at(k).at1() : 0; |
+ double rr0 = (k<r_eqn2.size())? r_eqn2.at(k).at0() : 0; |
+ double rr1 = (k<r_eqn2.size())? r_eqn2.at(k).at1() : 0; |
+ double a0,a1,b0,b1;// coeffs in a[k] and b[k] |
+ |
+ //the equations to solve at this point are: |
+ // a0*x(0)+b0*y(0)=r0 & 2*a0*a(0)+2*b0*b(0)=rr0 |
+ //and |
+ // a1*x(1)+b1*y(1)=r1 & 2*a1*a(1)+2*b1*b(1)=rr1 |
+ a0 = r0/dot(v0,V(0))*v0[0]-rr0/2*v0[1]; |
+ b0 = r0/dot(v0,V(0))*v0[1]+rr0/2*v0[0]; |
+ a1 = r1/dot(v1,V(1))*v1[0]-rr1/2*v1[1]; |
+ b1 = r1/dot(v1,V(1))*v1[1]+rr1/2*v1[0]; |
+ |
+ a.push_back(Linear(a0,a1)); |
+ b.push_back(Linear(b0,b1)); |
+ //TODO: use "incremental" rather than explicit formulas. |
+ r_eqn1 = -(a*x+b*y); |
+ r_eqn2 = Linear(1)-(a*a+b*b); |
+ } |
+ |
+ //our candidate is: |
+ D2<SBasis> unitV; |
+ unitV[0] = b; |
+ unitV[1] = -a; |
+ |
+ //is it good? |
+ double rel_tol = std::max(1.,std::max(V_in[0].tailError(0),V_in[1].tailError(0)))*tol; |
+ |
+ if (r_eqn1.tailError(order)>rel_tol || r_eqn2.tailError(order)>tol){ |
+ //if not: subdivide and concat results. |
+ Piecewise<D2<SBasis> > unitV0, unitV1; |
+ unitV0 = unitVector(compose(V,Linear(0,.5)),tol,order); |
+ unitV1 = unitVector(compose(V,Linear(.5,1)),tol,order); |
+ unitV0.setDomain(Interval(0.,.5)); |
+ unitV1.setDomain(Interval(.5,1.)); |
+ unitV0.concat(unitV1); |
+ return(unitV0); |
+ }else{ |
+ //if yes: return it as pw. |
+ Piecewise<D2<SBasis> > result; |
+ result=(Piecewise<D2<SBasis> >)unitV; |
+ return result; |
+ } |
+} |
+ |
+Piecewise<D2<SBasis> > |
+Geom::unitVector(Piecewise<D2<SBasis> > const &V, double tol, unsigned order){ |
+ Piecewise<D2<SBasis> > result; |
+ Piecewise<D2<SBasis> > VV = cutAtRoots(V); |
+ result.cuts.push_back(VV.cuts.front()); |
+ for (unsigned i=0; i<VV.size(); i++){ |
+ Piecewise<D2<SBasis> > unit_seg; |
+ unit_seg = unitVector(VV.segs[i],tol, order); |
+ unit_seg.setDomain(Interval(VV.cuts[i],VV.cuts[i+1])); |
+ result.concat(unit_seg); |
+ } |
+ return result; |
+} |
+ |
+Piecewise<SBasis> |
+Geom::arcLengthSb(Piecewise<D2<SBasis> > const &M, double tol){ |
+ Piecewise<D2<SBasis> > dM = derivative(M); |
+ Piecewise<SBasis> dMlength = sqrt(dot(dM,dM),tol,3); |
+ Piecewise<SBasis> length = integral(dMlength); |
+ length-=length.segs.front().at0(); |
+ return length; |
+} |
+Piecewise<SBasis> |
+Geom::arcLengthSb(D2<SBasis> const &M, double tol){ |
+ return arcLengthSb(Piecewise<D2<SBasis> >(M), tol); |
+} |
+ |
+double |
+Geom::length(D2<SBasis> const &M, |
+ double tol){ |
+ Piecewise<SBasis> length = arcLengthSb(M, tol); |
+ return length.segs.back().at1(); |
+} |
+double |
+Geom::length(Piecewise<D2<SBasis> > const &M, |
+ double tol){ |
+ Piecewise<SBasis> length = arcLengthSb(M, tol); |
+ return length.segs.back().at1(); |
+} |
+ |
+ |
+// incomplete. |
+Piecewise<SBasis> |
+Geom::curvature(D2<SBasis> const &M, double tol) { |
+ D2<SBasis> dM=derivative(M); |
+ Piecewise<SBasis> result; |
+ Piecewise<D2<SBasis> > unitv = unitVector(dM,tol); |
+ Piecewise<SBasis> dMlength = dot(Piecewise<D2<SBasis> >(dM),unitv); |
+ Piecewise<SBasis> k = cross(derivative(unitv),unitv); |
+ k = divide(k,dMlength,tol,3); |
+ return(k); |
+} |
+ |
+Piecewise<SBasis> |
+Geom::curvature(Piecewise<D2<SBasis> > const &V, double tol){ |
+ Piecewise<SBasis> result; |
+ Piecewise<D2<SBasis> > VV = cutAtRoots(V); |
+ result.cuts.push_back(VV.cuts.front()); |
+ for (unsigned i=0; i<VV.size(); i++){ |
+ Piecewise<SBasis> curv_seg; |
+ curv_seg = curvature(VV.segs[i],tol); |
+ curv_seg.setDomain(Interval(VV.cuts[i],VV.cuts[i+1])); |
+ result.concat(curv_seg); |
+ } |
+ return result; |
+} |
+ |
+//================================================================= |
+ |
+Piecewise<D2<SBasis> > |
+Geom::arc_length_parametrization(D2<SBasis> const &M, |
+ unsigned order, |
+ double tol){ |
+ Piecewise<D2<SBasis> > u; |
+ u.push_cut(0); |
+ |
+ Piecewise<SBasis> s = arcLengthSb(Piecewise<D2<SBasis> >(M),tol); |
+ for (unsigned i=0; i < s.size();i++){ |
+ double t0=s.cuts[i],t1=s.cuts[i+1]; |
+ D2<SBasis> sub_M = compose(M,Linear(t0,t1)); |
+ D2<SBasis> sub_u; |
+ for (unsigned dim=0;dim<2;dim++){ |
+ SBasis sub_s = s.segs[i]; |
+ sub_s-=sub_s.at0(); |
+ sub_s/=sub_s.at1(); |
+ sub_u[dim]=compose_inverse(sub_M[dim],sub_s, order, tol); |
+ } |
+ u.push(sub_u,s(t1)); |
+ } |
+ return u; |
+} |
+ |
+Piecewise<D2<SBasis> > |
+Geom::arc_length_parametrization(Piecewise<D2<SBasis> > const &M, |
+ unsigned order, |
+ double tol){ |
+ Piecewise<D2<SBasis> > result; |
+ for (unsigned i=0; i<M.size(); i++ ){ |
+ Piecewise<D2<SBasis> > uniform_seg=arc_length_parametrization(M[i],order,tol); |
+ result.concat(uniform_seg); |
+ } |
+ return(result); |
+} |
+ |
+/** centroid using sbasis integration. |
+ * This approach uses green's theorem to compute the area and centroid using integrals. For curved |
+ * shapes this is much faster than converting to polyline. |
+ |
+ * Returned values: |
+ 0 for normal execution; |
+ 2 if area is zero, meaning centroid is meaningless. |
+ |
+ * Copyright Nathan Hurst 2006 |
+ */ |
+ |
+unsigned Geom::centroid(Piecewise<D2<SBasis> > const &p, Point& centroid, double &area) { |
+ Point centroid_tmp(0,0); |
+ double atmp = 0; |
+ for(unsigned i = 0; i < p.size(); i++) { |
+ SBasis curl = dot(p[i], rot90(derivative(p[i]))); |
+ SBasis A = integral(curl); |
+ D2<SBasis> C = integral(multiply(curl, p[i])); |
+ atmp += A.at1() - A.at0(); |
+ centroid_tmp += C.at1()- C.at0(); // first moment. |
+ } |
+// join ends |
+ centroid_tmp *= 2; |
+ Point final = p[p.size()-1].at1(), initial = p[0].at0(); |
+ const double ai = cross(final, initial); |
+ atmp += ai; |
+ centroid_tmp += (final + initial)*ai; // first moment. |
+ |
+ area = atmp / 2; |
+ if (atmp != 0) { |
+ centroid = centroid_tmp / (3 * atmp); |
+ return 0; |
+ } |
+ return 2; |
+} |
+ |
+//}; // namespace |
+ |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/circle-circle.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/circle-circle.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/circle-circle.cpp (revision 14937) |
@@ -0,0 +1,131 @@ |
+/* circle_circle_intersection() * |
+ * Determine the points where 2 circles in a common plane intersect. |
+ * |
+ * int circle_circle_intersection( |
+ * // center and radius of 1st circle |
+ * double x0, double y0, double r0, |
+ * // center and radius of 2nd circle |
+ * double x1, double y1, double r1, |
+ * // 1st intersection point |
+ * double *xi, double *yi, |
+ * // 2nd intersection point |
+ * double *xi_prime, double *yi_prime) |
+ * |
+ * This is a public domain work. 3/26/2005 Tim Voght |
+ * Ported to lib2geom, 2006 Nathan Hurst |
+ * |
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+#include <stdio.h> |
+#include <math.h> |
+#include "point.h" |
+ |
+namespace Geom{ |
+ |
+int circle_circle_intersection(Point X0, double r0, |
+ Point X1, double r1, |
+ Point & p0, Point & p1) |
+{ |
+ /* dx and dy are the vertical and horizontal distances between |
+ * the circle centers. |
+ */ |
+ Point D = X1 - X0; |
+ |
+ /* Determine the straight-line distance between the centers. */ |
+ double d = L2(D); |
+ |
+ /* Check for solvability. */ |
+ if (d > (r0 + r1)) |
+ { |
+ /* no solution. circles do not intersect. */ |
+ return 0; |
+ } |
+ if (d <= fabs(r0 - r1)) |
+ { |
+ /* no solution. one circle is contained in the other */ |
+ return 1; |
+ } |
+ |
+ /* 'point 2' is the point where the line through the circle |
+ * intersection points crosses the line between the circle |
+ * centers. |
+ */ |
+ |
+ /* Determine the distance from point 0 to point 2. */ |
+ double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ; |
+ |
+ /* Determine the coordinates of point 2. */ |
+ Point p2 = X0 + D * (a/d); |
+ |
+ /* Determine the distance from point 2 to either of the |
+ * intersection points. |
+ */ |
+ double h = sqrt((r0*r0) - (a*a)); |
+ |
+ /* Now determine the offsets of the intersection points from |
+ * point 2. |
+ */ |
+ Point r = (h/d)*rot90(D); |
+ |
+ /* Determine the absolute intersection points. */ |
+ p0 = p2 + r; |
+ p1 = p2 - r; |
+ |
+ return 2; |
+} |
+ |
+}; |
+ |
+ |
+#ifdef TEST |
+ |
+void run_test(double x0, double y0, double r0, |
+ double x1, double y1, double r1) |
+{ |
+ double x3, y3, x3_prime, y3_prime; |
+ |
+ printf("x0=%F, y0=%F, r0=%F, x1=%F, y1=%F, r1=%F :\n", |
+ x0, y0, r0, x1, y1, r1); |
+ Geom::Point p0, p1; |
+ Geom::circle_circle_intersection(Geom::Point(x0, y0), r0, |
+ Geom::Point(x1, y1), r1, |
+ p0, p1); |
+ printf(" x3=%F, y3=%F, x3_prime=%F, y3_prime=%F\n", |
+ p0[0], p0[1], p1[0], p1[1]); |
+} |
+ |
+int main(void) |
+{ |
+ /* Add more! */ |
+ run_test(-1.0, -1.0, 1.5, 1.0, 1.0, 2.0); |
+ run_test(1.0, -1.0, 1.5, -1.0, 1.0, 2.0); |
+ run_test(-1.0, 1.0, 1.5, 1.0, -1.0, 2.0); |
+ run_test(1.0, 1.0, 1.5, -1.0, -1.0, 2.0); |
+ exit(0); |
+} |
+#endif |
+ |
Index: trunk/Scribus/scribus/third_party/lib2geom/matrix.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/matrix.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/matrix.cpp (revision 14937) |
@@ -0,0 +1,253 @@ |
+#define __Geom_MATRIX_C__ |
+ |
+/** \file |
+ * Various matrix routines. Currently includes some Geom::Rotate etc. routines too. |
+ */ |
+ |
+/* |
+ * Authors: |
+ * Lauris Kaplinski <lauris@kaplinski.com> |
+ * Michael G. Sloan <mgsloan@gmail.com> |
+ * |
+ * This code is in public domain |
+ */ |
+ |
+#include "utils.h" |
+#include "matrix.h" |
+#include "point.h" |
+ |
+namespace Geom { |
+ |
+/** Creates a Matrix given an axis and origin point. |
+ * The axis is represented as two vectors, which represent skew, rotation, and scaling in two dimensions. |
+ * from_basis(Point(1, 0), Point(0, 1), Point(0, 0)) would return the identity matrix. |
+ |
+ \param x_basis the vector for the x-axis. |
+ \param y_basis the vector for the y-axis. |
+ \param offset the translation applied by the matrix. |
+ \return The new Matrix. |
+ */ |
+//NOTE: Inkscape's version is broken, so when including this version, you'll have to search for code with this func |
+//TODO: move to Matrix::from_basis |
+Matrix from_basis(Point const x_basis, Point const y_basis, Point const offset) { |
+ return Matrix(x_basis[X], x_basis[Y], |
+ y_basis[X], y_basis[Y], |
+ offset [X], offset [Y]); |
+} |
+ |
+Point Matrix::xAxis() const { |
+ return Point(_c[0], _c[1]); |
+} |
+ |
+Point Matrix::yAxis() const { |
+ return Point(_c[2], _c[3]); |
+} |
+ |
+/** Gets the translation imparted by the Matrix. |
+ */ |
+Point Matrix::translation() const { |
+ return Point(_c[4], _c[5]); |
+} |
+ |
+void Matrix::setXAxis(Point const &vec) { |
+ for(int i = 0; i < 2; i++) |
+ _c[i] = vec[i]; |
+} |
+ |
+void Matrix::setYAxis(Point const &vec) { |
+ for(int i = 0; i < 2; i++) |
+ _c[i + 2] = vec[i]; |
+} |
+ |
+/** Sets the translation imparted by the Matrix. |
+ */ |
+void Matrix::setTranslation(Point const &loc) { |
+ for(int i = 0; i < 2; i++) |
+ _c[i + 4] = loc[i]; |
+} |
+ |
+/** Calculates the amount of x-scaling imparted by the Matrix. This is the scaling applied to |
+ * the original x-axis region. It is \emph{not} the overall x-scaling of the transformation. |
+ * Equivalent to L2(m.xAxis()) |
+ */ |
+double Matrix::expansionX() const { |
+ return sqrt(_c[0] * _c[0] + _c[1] * _c[1]); |
+} |
+ |
+/** Calculates the amount of y-scaling imparted by the Matrix. This is the scaling applied before |
+ * the other transformations. It is \emph{not} the overall y-scaling of the transformation. |
+ * Equivalent to L2(m.yAxis()) |
+ */ |
+double Matrix::expansionY() const { |
+ return sqrt(_c[2] * _c[2] + _c[3] * _c[3]); |
+} |
+ |
+void Matrix::setExpansionX(double val) { |
+ double exp_x = expansionX(); |
+ if(!are_near(exp_x, 0.0)) { //TODO: best way to deal with it is to skip op? |
+ double coef = val / expansionX(); |
+ for(unsigned i=0;i<2;i++) _c[i] *= coef; |
+ } |
+} |
+ |
+void Matrix::setExpansionY(double val) { |
+ double exp_y = expansionY(); |
+ if(!are_near(exp_y, 0.0)) { //TODO: best way to deal with it is to skip op? |
+ double coef = val / expansionY(); |
+ for(unsigned i=2; i<4; i++) _c[i] *= coef; |
+ } |
+} |
+ |
+/** Sets this matrix to be the Identity Matrix. */ |
+void Matrix::setIdentity() { |
+ _c[0] = 1.0; _c[1] = 0.0; |
+ _c[2] = 0.0; _c[3] = 1.0; |
+ _c[4] = 0.0; _c[5] = 0.0; |
+} |
+ |
+//TODO: use eps |
+ |
+bool Matrix::isIdentity(Coord const eps) const { |
+ return are_near(_c[0], 1.0) && are_near(_c[1], 0.0) && |
+ are_near(_c[2], 0.0) && are_near(_c[3], 1.0) && |
+ are_near(_c[4], 0.0) && are_near(_c[5], 0.0); |
+} |
+ |
+/** Answers the question "Does this matrix perform a translation, and \em{only} a translation?" |
+ \param eps an epsilon value defaulting to EPSILON |
+ \return A bool representing yes/no. |
+ */ |
+bool Matrix::isTranslation(Coord const eps) const { |
+ return are_near(_c[0], 1.0) && are_near(_c[1], 0.0) && |
+ are_near(_c[2], 0.0) && are_near(_c[3], 1.0) && |
+ (!are_near(_c[4], 0.0) || !are_near(_c[5], 0.0)); |
+} |
+ |
+/** Answers the question "Does this matrix perform a scale, and \em{only} a Scale?" |
+ \param eps an epsilon value defaulting to EPSILON |
+ \return A bool representing yes/no. |
+ */ |
+bool Matrix::isScale(Coord const eps) const { |
+ return !are_near(_c[0], 1.0) || !are_near(_c[3], 1.0) && //NOTE: these are the diags, and the next line opposite diags |
+ are_near(_c[1], 0.0) && are_near(_c[2], 0.0) && |
+ are_near(_c[4], 0.0) && are_near(_c[5], 0.0); |
+} |
+ |
+/** Answers the question "Does this matrix perform a uniform scale, and \em{only} a uniform scale?" |
+ \param eps an epsilon value defaulting to EPSILON |
+ \return A bool representing yes/no. |
+ */ |
+bool Matrix::isUniformScale(Coord const eps) const { |
+ return !are_near(_c[0], 1.0) && are_near(_c[0], _c[3]) && |
+ are_near(_c[1], 0.0) && are_near(_c[2], 0.0) && |
+ are_near(_c[4], 0.0) && are_near(_c[5], 0.0); |
+} |
+ |
+/** Answers the question "Does this matrix perform a rotation, and \em{only} a rotation?" |
+ \param eps an epsilon value defaulting to EPSILON |
+ \return A bool representing yes/no. |
+ */ |
+bool Matrix::isRotation(Coord const eps) const { |
+ return !are_near(_c[0], _c[3]) && are_near(_c[1], -_c[2]) && |
+ are_near(_c[4], 0.0) && are_near(_c[5], 0.0) && |
+ are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0); |
+} |
+ |
+bool Matrix::onlyScaleAndTranslation(Coord const eps) const { |
+ return are_near(_c[0], _c[3]) && are_near(_c[1], 0) && are_near(_c[2], 0); |
+} |
+ |
+bool Matrix::flips() const { |
+ return cross(xAxis(), yAxis()) > 0; |
+} |
+ |
+/** Returns the Scale/Rotate/skew part of the matrix without the translation part. */ |
+Matrix Matrix::without_translation() const { |
+ return Matrix(_c[0], _c[1], _c[2], _c[3], 0, 0); |
+} |
+ |
+/** Attempts to calculate the inverse of a matrix. |
+ * This is a Matrix such that m * m.inverse() is very near (hopefully < epsilon difference) the identity Matrix. |
+ * \textbf{The Identity Matrix is returned if the matrix has no inverse.} |
+ \return The inverse of the Matrix if defined, otherwise the Identity Matrix. |
+ */ |
+Matrix Matrix::inverse() const { |
+ Matrix d; |
+ |
+ Geom::Coord const determ = det(); |
+ if (!are_near(determ, 0.0)) { |
+ Geom::Coord const ideterm = 1.0 / determ; |
+ |
+ d._c[0] = _c[3] * ideterm; |
+ d._c[1] = -_c[1] * ideterm; |
+ d._c[2] = -_c[2] * ideterm; |
+ d._c[3] = _c[0] * ideterm; |
+ d._c[4] = -_c[4] * d._c[0] - _c[5] * d._c[2]; |
+ d._c[5] = -_c[4] * d._c[1] - _c[5] * d._c[3]; |
+ } else { |
+ d.setIdentity(); |
+ } |
+ |
+ return d; |
+} |
+ |
+/** Calculates the determinant of a Matrix. */ |
+Geom::Coord Matrix::det() const { |
+ return _c[0] * _c[3] - _c[1] * _c[2]; |
+} |
+ |
+/** Calculates the scalar of the descriminant of the Matrix. |
+ * This is simply the absolute value of the determinant. |
+ */ |
+Geom::Coord Matrix::descrim2() const { |
+ return fabs(det()); |
+} |
+ |
+/** Calculates the descriminant of the Matrix. */ |
+Geom::Coord Matrix::descrim() const { |
+ return sqrt(descrim2()); |
+} |
+ |
+Matrix operator*(Matrix const &m0, Matrix const &m1) { |
+ Matrix ret; |
+ for(int a = 0; a < 5; a += 2) { |
+ for(int b = 0; b < 2; b++) { |
+ ret[a + b] = m0[a] * m1[b] + m0[a + 1] * m1[b + 2]; |
+ } |
+ } |
+ ret[4] += m1[4]; |
+ ret[5] += m1[5]; |
+ return ret; |
+} |
+ |
+//TODO: What's this!?! |
+Matrix elliptic_quadratic_form(Matrix const &m) { |
+ double const od = m[0] * m[1] + m[2] * m[3]; |
+ return Matrix(m[0]*m[0] + m[1]*m[1], od, |
+ od, m[2]*m[2] + m[3]*m[3], |
+ 0, 0); |
+} |
+ |
+Eigen::Eigen(Matrix const &m) { |
+ double const B = -m[0] - m[3]; |
+ double const C = m[0]*m[3] - m[1]*m[2]; |
+ double const center = -B/2.0; |
+ double const delta = sqrt(B*B-4*C)/2.0; |
+ values[0] = center + delta; values[1] = center - delta; |
+ for (int i = 0; i < 2; i++) { |
+ vectors[i] = unit_vector(rot90(Point(m[0]-values[i], m[1]))); |
+ } |
+} |
+ |
+} //namespace Geom |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis-poly.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis-poly.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis-poly.h (revision 14937) |
@@ -0,0 +1,29 @@ |
+#ifndef _SBASIS_TO_POLY |
+#define _SBASIS_TO_POLY |
+ |
+#include "poly.h" |
+#include "sbasis.h" |
+ |
+/*** Conversion between SBasis and Poly. Not recommended for general |
+ * use due to instability. |
+ */ |
+ |
+namespace Geom{ |
+ |
+SBasis poly_to_sbasis(Poly const & p); |
+Poly sbasis_to_poly(SBasis const & s); |
+ |
+}; |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
+ |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/coord.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/coord.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/coord.h (revision 14937) |
@@ -0,0 +1,66 @@ |
+/* |
+ * coord.h |
+ * |
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#ifndef SEEN_Geom_COORD_H |
+#define SEEN_Geom_COORD_H |
+ |
+#include <cmath> |
+ |
+namespace Geom { |
+ |
+/** |
+ * A "real" type with sufficient precision for coordinates. |
+ * |
+ * You may safely assume that double (or even float) provides enough precision for storing |
+ * on-canvas points, and hence that double provides enough precision for dot products of |
+ * differences of on-canvas points. |
+ */ |
+typedef double Coord; |
+ |
+const Coord EPSILON = 1e-5; //1e-18; |
+ |
+//IMPL: NearConcept |
+inline bool are_near(Coord a, Coord b, double eps=EPSILON) { return fabs(a-b) <= eps; } |
+ |
+} /* namespace Geom */ |
+ |
+ |
+#endif /* !SEEN_Geom_COORD_H */ |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/circulator.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/circulator.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/circulator.h (revision 14937) |
@@ -0,0 +1,149 @@ |
+/* |
+ * ciculator.h |
+ * |
+ * Copyright 2006 MenTaLguY <mental@rydia.net> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#ifndef SEEN_Circulator_H |
+#define SEEN_Circulator_H |
+ |
+#include <iterator> |
+ |
+namespace Geom { |
+ |
+template <typename Iterator> |
+class Circulator { |
+public: |
+ typedef std::random_access_iterator_tag iterator_category; |
+ typedef typename std::iterator_traits<Iterator>::value_type value_type; |
+ typedef typename std::iterator_traits<Iterator>::difference_type difference_type; |
+ typedef typename std::iterator_traits<Iterator>::pointer pointer; |
+ typedef typename std::iterator_traits<Iterator>::reference reference; |
+ |
+ Circulator(Iterator const &first, |
+ Iterator const &last, |
+ Iterator const &pos) |
+ : _first(first), _last(last), _pos(pos) |
+ { |
+ match_random_access(iterator_category(first)); |
+ } |
+ |
+ reference operator*() const { |
+ return *_pos; |
+ } |
+ pointer operator->() const { |
+ return &*_pos; |
+ } |
+ |
+ Circulator &operator++() { |
+ if ( _first == _last ) return *this; |
+ ++_pos; |
+ if ( _pos == _last ) _pos = _first; |
+ return *this; |
+ } |
+ Circulator operator++(int) { |
+ Circulator saved=*this; |
+ ++(*this); |
+ return saved; |
+ } |
+ |
+ Circulator &operator--() { |
+ if ( _pos == _first ) _pos = _last; |
+ --_pos; |
+ return *this; |
+ } |
+ Circulator operator--(int) { |
+ Circulator saved=*this; |
+ --(*this); |
+ return saved; |
+ } |
+ |
+ Circulator &operator+=(int n) { |
+ _pos = _offset(n); |
+ return *this; |
+ } |
+ Circulator operator+(int n) const { |
+ return Circulator(_first, _last, _offset(n)); |
+ } |
+ Circulator &operator-=(int n) { |
+ _pos = _offset(-n); |
+ return *this; |
+ } |
+ Circulator operator-(int n) const { |
+ return Circulator(_first, _last, _offset(-n)); |
+ } |
+ |
+ difference_type operator-(Circulator const &other) { |
+ return _pos - other._pos; |
+ } |
+ |
+ reference operator[](int n) const { |
+ return *_offset(n); |
+ } |
+ |
+private: |
+ void match_random_access(iterator_category) {} |
+ |
+ Iterator _offset(int n) { |
+ difference_type range=( _last - _first ); |
+ difference_type offset=( _pos - _first + n ); |
+ |
+ if ( offset < 0 ) { |
+ // modulus not well-defined for negative numbers in C++ |
+ offset += ( ( -offset / range ) + 1 ) * range; |
+ } else if ( offset >= range ) { |
+ offset %= range; |
+ } |
+ return _first + offset; |
+ } |
+ |
+ Iterator _first; |
+ Iterator _last; |
+ Iterator _pos; |
+}; |
+ |
+} |
+ |
+template <typename T> |
+Geom::Circulator<T> operator+(int n, Geom::Circulator<T> const &c) { |
+ return c + n; |
+} |
+ |
+#endif // SEEN_Circulator_H |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(substatement-open . 0)) |
+ indent-tabs-mode:nil |
+ c-brace-offset:0 |
+ fill-column:99 |
+ End: |
+ vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
+*/ |
+ |
Index: trunk/Scribus/scribus/third_party/lib2geom/bezier-utils.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/bezier-utils.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/bezier-utils.cpp (revision 14937) |
@@ -0,0 +1,1004 @@ |
+#define __SP_BEZIER_UTILS_C__ |
+ |
+/** \file |
+ * Bezier interpolation for inkscape drawing code. |
+ */ |
+/* |
+ * Original code published in: |
+ * An Algorithm for Automatically Fitting Digitized Curves |
+ * by Philip J. Schneider |
+ * "Graphics Gems", Academic Press, 1990 |
+ * |
+ * Authors: |
+ * Philip J. Schneider |
+ * Lauris Kaplinski <lauris@kaplinski.com> |
+ * Peter Moulder <pmoulder@mail.csse.monash.edu.au> |
+ * |
+ * Copyright (C) 1990 Philip J. Schneider |
+ * Copyright (C) 2001 Lauris Kaplinski |
+ * Copyright (C) 2001 Ximian, Inc. |
+ * Copyright (C) 2003,2004 Monash University |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#define SP_HUGE 1e5 |
+#define noBEZIER_DEBUG |
+ |
+#ifdef HAVE_IEEEFP_H |
+# include <ieefp.h> |
+#endif |
+ |
+#include "bezier-utils.h" |
+ |
+#include "isnan.h" |
+#include <assert.h> |
+ |
+namespace Geom{ |
+ |
+typedef Point BezierCurve[]; |
+ |
+/* Forward declarations */ |
+static void generate_bezier(Point b[], Point const d[], double const u[], unsigned len, |
+ Point const &tHat1, Point const &tHat2, double tolerance_sq); |
+static void estimate_lengths(Point bezier[], |
+ Point const data[], double const u[], unsigned len, |
+ Point const &tHat1, Point const &tHat2); |
+static void estimate_bi(Point b[4], unsigned ei, |
+ Point const data[], double const u[], unsigned len); |
+static void reparameterize(Point const d[], unsigned len, double u[], BezierCurve const bezCurve); |
+static double NewtonRaphsonRootFind(BezierCurve const Q, Point const &P, double u); |
+static Point darray_center_tangent(Point const d[], unsigned center, unsigned length); |
+static Point darray_right_tangent(Point const d[], unsigned const len); |
+static unsigned copy_without_nans_or_adjacent_duplicates(Point const src[], unsigned src_len, Point dest[]); |
+static void chord_length_parameterize(Point const d[], double u[], unsigned len); |
+static double compute_max_error_ratio(Point const d[], double const u[], unsigned len, |
+ BezierCurve const bezCurve, double tolerance, |
+ unsigned *splitPoint); |
+static double compute_hook(Point const &a, Point const &b, double const u, BezierCurve const bezCurve, |
+ double const tolerance); |
+ |
+ |
+static Point const unconstrained_tangent(0, 0); |
+ |
+ |
+/* |
+ * B0, B1, B2, B3 : Bezier multipliers |
+ */ |
+ |
+#define B0(u) ( ( 1.0 - u ) * ( 1.0 - u ) * ( 1.0 - u ) ) |
+#define B1(u) ( 3 * u * ( 1.0 - u ) * ( 1.0 - u ) ) |
+#define B2(u) ( 3 * u * u * ( 1.0 - u ) ) |
+#define B3(u) ( u * u * u ) |
+ |
+#ifdef BEZIER_DEBUG |
+# define DOUBLE_ASSERT(x) assert( ( (x) > -SP_HUGE ) && ( (x) < SP_HUGE ) ) |
+# define BEZIER_ASSERT(b) do { \ |
+ DOUBLE_ASSERT((b)[0][X]); DOUBLE_ASSERT((b)[0][Y]); \ |
+ DOUBLE_ASSERT((b)[1][X]); DOUBLE_ASSERT((b)[1][Y]); \ |
+ DOUBLE_ASSERT((b)[2][X]); DOUBLE_ASSERT((b)[2][Y]); \ |
+ DOUBLE_ASSERT((b)[3][X]); DOUBLE_ASSERT((b)[3][Y]); \ |
+ } while(0) |
+#else |
+# define DOUBLE_ASSERT(x) do { } while(0) |
+# define BEZIER_ASSERT(b) do { } while(0) |
+#endif |
+ |
+ |
+/** |
+ * Fit a single-segment Bezier curve to a set of digitized points. |
+ * |
+ * \return Number of segments generated, or -1 on error. |
+ */ |
+int |
+bezier_fit_cubic(Point *bezier, Point const *data, int len, double error) |
+{ |
+ return bezier_fit_cubic_r(bezier, data, len, error, 1); |
+} |
+ |
+/** |
+ * Fit a multi-segment Bezier curve to a set of digitized points, with |
+ * possible weedout of identical points and NaNs. |
+ * |
+ * \param max_beziers Maximum number of generated segments |
+ * \param Result array, must be large enough for n. segments * 4 elements. |
+ * |
+ * \return Number of segments generated, or -1 on error. |
+ */ |
+int |
+bezier_fit_cubic_r(Point bezier[], Point const data[], int const len, double const error, unsigned const max_beziers) |
+{ |
+ if(bezier == NULL || |
+ data == NULL || |
+ len <= 0 || |
+ max_beziers >= (1ul << (31 - 2 - 1 - 3))) |
+ return -1; |
+ |
+ Point *uniqued_data = new Point[len]; |
+ unsigned uniqued_len = copy_without_nans_or_adjacent_duplicates(data, len, uniqued_data); |
+ |
+ if ( uniqued_len < 2 ) { |
+ delete[] uniqued_data; |
+ return 0; |
+ } |
+ |
+ /* Call fit-cubic function with recursion. */ |
+ int const ret = bezier_fit_cubic_full(bezier, NULL, uniqued_data, uniqued_len, |
+ unconstrained_tangent, unconstrained_tangent, |
+ error, max_beziers); |
+ delete[] uniqued_data; |
+ return ret; |
+} |
+ |
+/** |
+ * Copy points from src to dest, filter out points containing NaN and |
+ * adjacent points with equal x and y. |
+ * \return length of dest |
+ */ |
+static unsigned |
+copy_without_nans_or_adjacent_duplicates(Point const src[], unsigned src_len, Point dest[]) |
+{ |
+ unsigned si = 0; |
+ for (;;) { |
+ if ( si == src_len ) { |
+ return 0; |
+ } |
+ if (!is_nan(src[si][X]) && |
+ !is_nan(src[si][Y])) { |
+ dest[0] = Point(src[si]); |
+ ++si; |
+ break; |
+ } |
+ } |
+ unsigned di = 0; |
+ for (; si < src_len; ++si) { |
+ Point const src_pt = Point(src[si]); |
+ if ( src_pt != dest[di] |
+ && !is_nan(src_pt[X]) |
+ && !is_nan(src_pt[Y])) { |
+ dest[++di] = src_pt; |
+ } |
+ } |
+ unsigned dest_len = di + 1; |
+ assert( dest_len <= src_len ); |
+ return dest_len; |
+} |
+ |
+/** |
+ * Fit a multi-segment Bezier curve to a set of digitized points, without |
+ * possible weedout of identical points and NaNs. |
+ * |
+ * \pre data is uniqued, i.e. not exist i: data[i] == data[i + 1]. |
+ * \param max_beziers Maximum number of generated segments |
+ * \param Result array, must be large enough for n. segments * 4 elements. |
+ */ |
+int |
+bezier_fit_cubic_full(Point bezier[], int split_points[], |
+ Point const data[], int const len, |
+ Point const &tHat1, Point const &tHat2, |
+ double const error, unsigned const max_beziers) |
+{ |
+ int const maxIterations = 4; /* std::max times to try iterating */ |
+ |
+ if(!(bezier != NULL) || |
+ !(data != NULL) || |
+ !(len > 0) || |
+ !(max_beziers >= 1) || |
+ !(error >= 0.0)) |
+ return -1; |
+ |
+ if ( len < 2 ) return 0; |
+ |
+ if ( len == 2 ) { |
+ /* We have 2 points, which can be fitted trivially. */ |
+ bezier[0] = data[0]; |
+ bezier[3] = data[len - 1]; |
+ double const dist = distance(bezier[0], bezier[3]) / 3.0; |
+ if (is_nan(dist)) { |
+ /* Numerical problem, fall back to straight line segment. */ |
+ bezier[1] = bezier[0]; |
+ bezier[2] = bezier[3]; |
+ } else { |
+ bezier[1] = ( is_zero(tHat1) |
+ ? ( 2 * bezier[0] + bezier[3] ) / 3. |
+ : bezier[0] + dist * tHat1 ); |
+ bezier[2] = ( is_zero(tHat2) |
+ ? ( bezier[0] + 2 * bezier[3] ) / 3. |
+ : bezier[3] + dist * tHat2 ); |
+ } |
+ BEZIER_ASSERT(bezier); |
+ return 1; |
+ } |
+ |
+ /* Parameterize points, and attempt to fit curve */ |
+ unsigned splitPoint; /* Point to split point set at. */ |
+ bool is_corner; |
+ { |
+ double *u = new double[len]; |
+ chord_length_parameterize(data, u, len); |
+ if ( u[len - 1] == 0.0 ) { |
+ /* Zero-length path: every point in data[] is the same. |
+ * |
+ * (Clients aren't allowed to pass such data; handling the case is defensive |
+ * programming.) |
+ */ |
+ delete[] u; |
+ return 0; |
+ } |
+ |
+ generate_bezier(bezier, data, u, len, tHat1, tHat2, error); |
+ reparameterize(data, len, u, bezier); |
+ |
+ /* Find max deviation of points to fitted curve. */ |
+ double const tolerance = sqrt(error + 1e-9); |
+ double maxErrorRatio = compute_max_error_ratio(data, u, len, bezier, tolerance, &splitPoint); |
+ |
+ if ( fabs(maxErrorRatio) <= 1.0 ) { |
+ BEZIER_ASSERT(bezier); |
+ delete[] u; |
+ return 1; |
+ } |
+ |
+ /* If error not too large, then try some reparameterization and iteration. */ |
+ if ( 0.0 <= maxErrorRatio && maxErrorRatio <= 3.0 ) { |
+ for (int i = 0; i < maxIterations; i++) { |
+ generate_bezier(bezier, data, u, len, tHat1, tHat2, error); |
+ reparameterize(data, len, u, bezier); |
+ maxErrorRatio = compute_max_error_ratio(data, u, len, bezier, tolerance, &splitPoint); |
+ if ( fabs(maxErrorRatio) <= 1.0 ) { |
+ BEZIER_ASSERT(bezier); |
+ delete[] u; |
+ return 1; |
+ } |
+ } |
+ } |
+ delete[] u; |
+ is_corner = (maxErrorRatio < 0); |
+ } |
+ |
+ if (is_corner) { |
+ assert(splitPoint < unsigned(len)); |
+ if (splitPoint == 0) { |
+ if (is_zero(tHat1)) { |
+ /* Got spike even with unconstrained initial tangent. */ |
+ ++splitPoint; |
+ } else { |
+ return bezier_fit_cubic_full(bezier, split_points, data, len, unconstrained_tangent, tHat2, |
+ error, max_beziers); |
+ } |
+ } else if (splitPoint == unsigned(len - 1)) { |
+ if (is_zero(tHat2)) { |
+ /* Got spike even with unconstrained final tangent. */ |
+ --splitPoint; |
+ } else { |
+ return bezier_fit_cubic_full(bezier, split_points, data, len, tHat1, unconstrained_tangent, |
+ error, max_beziers); |
+ } |
+ } |
+ } |
+ |
+ if ( 1 < max_beziers ) { |
+ /* |
+ * Fitting failed -- split at max error point and fit recursively |
+ */ |
+ unsigned const rec_max_beziers1 = max_beziers - 1; |
+ |
+ Point recTHat2, recTHat1; |
+ if (is_corner) { |
+ if(!(0 < splitPoint && splitPoint < unsigned(len - 1))) |
+ return -1; |
+ recTHat1 = recTHat2 = unconstrained_tangent; |
+ } else { |
+ /* Unit tangent vector at splitPoint. */ |
+ recTHat2 = darray_center_tangent(data, splitPoint, len); |
+ recTHat1 = -recTHat2; |
+ } |
+ int const nsegs1 = bezier_fit_cubic_full(bezier, split_points, data, splitPoint + 1, |
+ tHat1, recTHat2, error, rec_max_beziers1); |
+ if ( nsegs1 < 0 ) { |
+#ifdef BEZIER_DEBUG |
+ g_print("fit_cubic[1]: recursive call failed\n"); |
+#endif |
+ return -1; |
+ } |
+ assert( nsegs1 != 0 ); |
+ if (split_points != NULL) { |
+ split_points[nsegs1 - 1] = splitPoint; |
+ } |
+ unsigned const rec_max_beziers2 = max_beziers - nsegs1; |
+ int const nsegs2 = bezier_fit_cubic_full(bezier + nsegs1*4, |
+ ( split_points == NULL |
+ ? NULL |
+ : split_points + nsegs1 ), |
+ data + splitPoint, len - splitPoint, |
+ recTHat1, tHat2, error, rec_max_beziers2); |
+ if ( nsegs2 < 0 ) { |
+#ifdef BEZIER_DEBUG |
+ g_print("fit_cubic[2]: recursive call failed\n"); |
+#endif |
+ return -1; |
+ } |
+ |
+#ifdef BEZIER_DEBUG |
+ g_print("fit_cubic: success[nsegs: %d+%d=%d] on max_beziers:%u\n", |
+ nsegs1, nsegs2, nsegs1 + nsegs2, max_beziers); |
+#endif |
+ return nsegs1 + nsegs2; |
+ } else { |
+ return -1; |
+ } |
+} |
+ |
+ |
+/** |
+ * Fill in \a bezier[] based on the given data and tangent requirements, using |
+ * a least-squares fit. |
+ * |
+ * Each of tHat1 and tHat2 should be either a zero vector or a unit vector. |
+ * If it is zero, then bezier[1 or 2] is estimated without constraint; otherwise, |
+ * it bezier[1 or 2] is placed in the specified direction from bezier[0 or 3]. |
+ * |
+ * \param tolerance_sq Used only for an initial guess as to tangent directions |
+ * when \a tHat1 or \a tHat2 is zero. |
+ */ |
+static void |
+generate_bezier(Point bezier[], |
+ Point const data[], double const u[], unsigned const len, |
+ Point const &tHat1, Point const &tHat2, |
+ double const tolerance_sq) |
+{ |
+ bool const est1 = is_zero(tHat1); |
+ bool const est2 = is_zero(tHat2); |
+ Point est_tHat1( est1 |
+ ? darray_left_tangent(data, len, tolerance_sq) |
+ : tHat1 ); |
+ Point est_tHat2( est2 |
+ ? darray_right_tangent(data, len, tolerance_sq) |
+ : tHat2 ); |
+ estimate_lengths(bezier, data, u, len, est_tHat1, est_tHat2); |
+ /* We find that darray_right_tangent tends to produce better results |
+ for our current freehand tool than full estimation. */ |
+ if (est1) { |
+ estimate_bi(bezier, 1, data, u, len); |
+ if (bezier[1] != bezier[0]) { |
+ est_tHat1 = unit_vector(bezier[1] - bezier[0]); |
+ } |
+ estimate_lengths(bezier, data, u, len, est_tHat1, est_tHat2); |
+ } |
+} |
+ |
+ |
+static void |
+estimate_lengths(Point bezier[], |
+ Point const data[], double const uPrime[], unsigned const len, |
+ Point const &tHat1, Point const &tHat2) |
+{ |
+ double C[2][2]; /* Matrix C. */ |
+ double X[2]; /* Matrix X. */ |
+ |
+ /* Create the C and X matrices. */ |
+ C[0][0] = 0.0; |
+ C[0][1] = 0.0; |
+ C[1][0] = 0.0; |
+ C[1][1] = 0.0; |
+ X[0] = 0.0; |
+ X[1] = 0.0; |
+ |
+ /* First and last control points of the Bezier curve are positioned exactly at the first and |
+ last data points. */ |
+ bezier[0] = data[0]; |
+ bezier[3] = data[len - 1]; |
+ |
+ for (unsigned i = 0; i < len; i++) { |
+ /* Bezier control point coefficients. */ |
+ double const b0 = B0(uPrime[i]); |
+ double const b1 = B1(uPrime[i]); |
+ double const b2 = B2(uPrime[i]); |
+ double const b3 = B3(uPrime[i]); |
+ |
+ /* rhs for eqn */ |
+ Point const a1 = b1 * tHat1; |
+ Point const a2 = b2 * tHat2; |
+ |
+ C[0][0] += dot(a1, a1); |
+ C[0][1] += dot(a1, a2); |
+ C[1][0] = C[0][1]; |
+ C[1][1] += dot(a2, a2); |
+ |
+ /* Additional offset to the data point from the predicted point if we were to set bezier[1] |
+ to bezier[0] and bezier[2] to bezier[3]. */ |
+ Point const shortfall |
+ = ( data[i] |
+ - ( ( b0 + b1 ) * bezier[0] ) |
+ - ( ( b2 + b3 ) * bezier[3] ) ); |
+ X[0] += dot(a1, shortfall); |
+ X[1] += dot(a2, shortfall); |
+ } |
+ |
+ /* We've constructed a pair of equations in the form of a matrix product C * alpha = X. |
+ Now solve for alpha. */ |
+ double alpha_l, alpha_r; |
+ |
+ /* Compute the determinants of C and X. */ |
+ double const det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1]; |
+ if ( det_C0_C1 != 0 ) { |
+ /* Apparently Kramer's rule. */ |
+ double const det_C0_X = C[0][0] * X[1] - C[0][1] * X[0]; |
+ double const det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1]; |
+ alpha_l = det_X_C1 / det_C0_C1; |
+ alpha_r = det_C0_X / det_C0_C1; |
+ } else { |
+ /* The matrix is under-determined. Try requiring alpha_l == alpha_r. |
+ * |
+ * One way of implementing the constraint alpha_l == alpha_r is to treat them as the same |
+ * variable in the equations. We can do this by adding the columns of C to form a single |
+ * column, to be multiplied by alpha to give the column vector X. |
+ * |
+ * We try each row in turn. |
+ */ |
+ double const c0 = C[0][0] + C[0][1]; |
+ if (c0 != 0) { |
+ alpha_l = alpha_r = X[0] / c0; |
+ } else { |
+ double const c1 = C[1][0] + C[1][1]; |
+ if (c1 != 0) { |
+ alpha_l = alpha_r = X[1] / c1; |
+ } else { |
+ /* Let the below code handle this. */ |
+ alpha_l = alpha_r = 0.; |
+ } |
+ } |
+ } |
+ |
+ /* If alpha negative, use the Wu/Barsky heuristic (see text). (If alpha is 0, you get |
+ coincident control points that lead to divide by zero in any subsequent |
+ NewtonRaphsonRootFind() call.) */ |
+ /// \todo Check whether this special-casing is necessary now that |
+ /// NewtonRaphsonRootFind handles non-positive denominator. |
+ if ( alpha_l < 1.0e-6 || |
+ alpha_r < 1.0e-6 ) |
+ { |
+ alpha_l = alpha_r = distance(data[0], data[len-1]) / 3.0; |
+ } |
+ |
+ /* Control points 1 and 2 are positioned an alpha distance out on the tangent vectors, left and |
+ right, respectively. */ |
+ bezier[1] = alpha_l * tHat1 + bezier[0]; |
+ bezier[2] = alpha_r * tHat2 + bezier[3]; |
+ |
+ return; |
+} |
+ |
+static double lensq(Point const p) { |
+ return dot(p, p); |
+} |
+ |
+static void |
+estimate_bi(Point bezier[4], unsigned const ei, |
+ Point const data[], double const u[], unsigned const len) |
+{ |
+ if(!(1 <= ei && ei <= 2)) |
+ return; |
+ unsigned const oi = 3 - ei; |
+ double num[2] = {0., 0.}; |
+ double den = 0.; |
+ for (unsigned i = 0; i < len; ++i) { |
+ double const ui = u[i]; |
+ double const b[4] = { |
+ B0(ui), |
+ B1(ui), |
+ B2(ui), |
+ B3(ui) |
+ }; |
+ |
+ for (unsigned d = 0; d < 2; ++d) { |
+ num[d] += b[ei] * (b[0] * bezier[0][d] + |
+ b[oi] * bezier[oi][d] + |
+ b[3] * bezier[3][d] + |
+ - data[i][d]); |
+ } |
+ den -= b[ei] * b[ei]; |
+ } |
+ |
+ if (den != 0.) { |
+ for (unsigned d = 0; d < 2; ++d) { |
+ bezier[ei][d] = num[d] / den; |
+ } |
+ } else { |
+ bezier[ei] = ( oi * bezier[0] + ei * bezier[3] ) / 3.; |
+ } |
+} |
+ |
+/** |
+ * Given set of points and their parameterization, try to find a better assignment of parameter |
+ * values for the points. |
+ * |
+ * \param d Array of digitized points. |
+ * \param u Current parameter values. |
+ * \param bezCurve Current fitted curve. |
+ * \param len Number of values in both d and u arrays. |
+ * Also the size of the array that is allocated for return. |
+ */ |
+static void |
+reparameterize(Point const d[], |
+ unsigned const len, |
+ double u[], |
+ BezierCurve const bezCurve) |
+{ |
+ assert( 2 <= len ); |
+ |
+ unsigned const last = len - 1; |
+ assert( bezCurve[0] == d[0] ); |
+ assert( bezCurve[3] == d[last] ); |
+ assert( u[0] == 0.0 ); |
+ assert( u[last] == 1.0 ); |
+ /* Otherwise, consider including 0 and last in the below loop. */ |
+ |
+ for (unsigned i = 1; i < last; i++) { |
+ u[i] = NewtonRaphsonRootFind(bezCurve, d[i], u[i]); |
+ } |
+} |
+ |
+/** |
+ * Use Newton-Raphson iteration to find better root. |
+ * |
+ * \param Q Current fitted curve |
+ * \param P Digitized point |
+ * \param u Parameter value for "P" |
+ * |
+ * \return Improved u |
+ */ |
+static double |
+NewtonRaphsonRootFind(BezierCurve const Q, Point const &P, double const u) |
+{ |
+ assert( 0.0 <= u ); |
+ assert( u <= 1.0 ); |
+ |
+ /* Generate control vertices for Q'. */ |
+ Point Q1[3]; |
+ for (unsigned i = 0; i < 3; i++) { |
+ Q1[i] = 3.0 * ( Q[i+1] - Q[i] ); |
+ } |
+ |
+ /* Generate control vertices for Q''. */ |
+ Point Q2[2]; |
+ for (unsigned i = 0; i < 2; i++) { |
+ Q2[i] = 2.0 * ( Q1[i+1] - Q1[i] ); |
+ } |
+ |
+ /* Compute Q(u), Q'(u) and Q''(u). */ |
+ Point const Q_u = bezier_pt(3, Q, u); |
+ Point const Q1_u = bezier_pt(2, Q1, u); |
+ Point const Q2_u = bezier_pt(1, Q2, u); |
+ |
+ /* Compute f(u)/f'(u), where f is the derivative wrt u of distsq(u) = 0.5 * the square of the |
+ distance from P to Q(u). Here we're using Newton-Raphson to find a stationary point in the |
+ distsq(u), hopefully corresponding to a local minimum in distsq (and hence a local minimum |
+ distance from P to Q(u)). */ |
+ Point const diff = Q_u - P; |
+ double numerator = dot(diff, Q1_u); |
+ double denominator = dot(Q1_u, Q1_u) + dot(diff, Q2_u); |
+ |
+ double improved_u; |
+ if ( denominator > 0. ) { |
+ /* One iteration of Newton-Raphson: |
+ improved_u = u - f(u)/f'(u) */ |
+ improved_u = u - ( numerator / denominator ); |
+ } else { |
+ /* Using Newton-Raphson would move in the wrong direction (towards a local maximum rather |
+ than local minimum), so we move an arbitrary amount in the right direction. */ |
+ if ( numerator > 0. ) { |
+ improved_u = u * .98 - .01; |
+ } else if ( numerator < 0. ) { |
+ /* Deliberately asymmetrical, to reduce the chance of cycling. */ |
+ improved_u = .031 + u * .98; |
+ } else { |
+ improved_u = u; |
+ } |
+ } |
+ |
+ if (!is_finite(improved_u)) { |
+ improved_u = u; |
+ } else if ( improved_u < 0.0 ) { |
+ improved_u = 0.0; |
+ } else if ( improved_u > 1.0 ) { |
+ improved_u = 1.0; |
+ } |
+ |
+ /* Ensure that improved_u isn't actually worse. */ |
+ { |
+ double const diff_lensq = lensq(diff); |
+ for (double proportion = .125; ; proportion += .125) { |
+ if ( lensq( bezier_pt(3, Q, improved_u) - P ) > diff_lensq ) { |
+ if ( proportion > 1.0 ) { |
+ //g_warning("found proportion %g", proportion); |
+ improved_u = u; |
+ break; |
+ } |
+ improved_u = ( ( 1 - proportion ) * improved_u + |
+ proportion * u ); |
+ } else { |
+ break; |
+ } |
+ } |
+ } |
+ |
+ DOUBLE_ASSERT(improved_u); |
+ return improved_u; |
+} |
+ |
+/** |
+ * Evaluate a Bezier curve at parameter value \a t. |
+ * |
+ * \param degree The degree of the Bezier curve: 3 for cubic, 2 for quadratic etc. Must be less |
+ * than 4. |
+ * \param V The control points for the Bezier curve. Must have (\a degree+1) |
+ * elements. |
+ * \param t The "parameter" value, specifying whereabouts along the curve to |
+ * evaluate. Typically in the range [0.0, 1.0]. |
+ * |
+ * Let s = 1 - t. |
+ * BezierII(1, V) gives (s, t) * V, i.e. t of the way |
+ * from V[0] to V[1]. |
+ * BezierII(2, V) gives (s**2, 2*s*t, t**2) * V. |
+ * BezierII(3, V) gives (s**3, 3 s**2 t, 3s t**2, t**3) * V. |
+ * |
+ * The derivative of BezierII(i, V) with respect to t |
+ * is i * BezierII(i-1, V'), where for all j, V'[j] = |
+ * V[j + 1] - V[j]. |
+ */ |
+Point |
+bezier_pt(unsigned const degree, Point const V[], double const t) |
+{ |
+ /** Pascal's triangle. */ |
+ static int const pascal[4][4] = {{1}, |
+ {1, 1}, |
+ {1, 2, 1}, |
+ {1, 3, 3, 1}}; |
+ assert( degree < 4); |
+ double const s = 1.0 - t; |
+ |
+ /* Calculate powers of t and s. */ |
+ double spow[4]; |
+ double tpow[4]; |
+ spow[0] = 1.0; spow[1] = s; |
+ tpow[0] = 1.0; tpow[1] = t; |
+ for (unsigned i = 1; i < degree; ++i) { |
+ spow[i + 1] = spow[i] * s; |
+ tpow[i + 1] = tpow[i] * t; |
+ } |
+ |
+ Point ret = spow[degree] * V[0]; |
+ for (unsigned i = 1; i <= degree; ++i) { |
+ ret += pascal[degree][i] * spow[degree - i] * tpow[i] * V[i]; |
+ } |
+ return ret; |
+} |
+ |
+/* |
+ * ComputeLeftTangent, ComputeRightTangent, ComputeCenterTangent : |
+ * Approximate unit tangents at endpoints and "center" of digitized curve |
+ */ |
+ |
+/** |
+ * Estimate the (forward) tangent at point d[first + 0.5]. |
+ * |
+ * Unlike the center and right versions, this calculates the tangent in |
+ * the way one might expect, i.e., wrt increasing index into d. |
+ * \pre (2 \<= len) and (d[0] != d[1]). |
+ **/ |
+Point |
+darray_left_tangent(Point const d[], unsigned const len) |
+{ |
+ assert( len >= 2 ); |
+ assert( d[0] != d[1] ); |
+ return unit_vector( d[1] - d[0] ); |
+} |
+ |
+/** |
+ * Estimates the (backward) tangent at d[last - 0.5]. |
+ * |
+ * \note The tangent is "backwards", i.e. it is with respect to |
+ * decreasing index rather than increasing index. |
+ * |
+ * \pre 2 \<= len. |
+ * \pre d[len - 1] != d[len - 2]. |
+ * \pre all[p in d] in_svg_plane(p). |
+ */ |
+static Point |
+darray_right_tangent(Point const d[], unsigned const len) |
+{ |
+ assert( 2 <= len ); |
+ unsigned const last = len - 1; |
+ unsigned const prev = last - 1; |
+ assert( d[last] != d[prev] ); |
+ return unit_vector( d[prev] - d[last] ); |
+} |
+ |
+/** |
+ * Estimate the (forward) tangent at point d[0]. |
+ * |
+ * Unlike the center and right versions, this calculates the tangent in |
+ * the way one might expect, i.e., wrt increasing index into d. |
+ * |
+ * \pre 2 \<= len. |
+ * \pre d[0] != d[1]. |
+ * \pre all[p in d] in_svg_plane(p). |
+ * \post is_unit_vector(ret). |
+ **/ |
+Point |
+darray_left_tangent(Point const d[], unsigned const len, double const tolerance_sq) |
+{ |
+ assert( 2 <= len ); |
+ assert( 0 <= tolerance_sq ); |
+ for (unsigned i = 1;;) { |
+ Point const pi(d[i]); |
+ Point const t(pi - d[0]); |
+ double const distsq = dot(t, t); |
+ if ( tolerance_sq < distsq ) { |
+ return unit_vector(t); |
+ } |
+ ++i; |
+ if (i == len) { |
+ return ( distsq == 0 |
+ ? darray_left_tangent(d, len) |
+ : unit_vector(t) ); |
+ } |
+ } |
+} |
+ |
+/** |
+ * Estimates the (backward) tangent at d[last]. |
+ * |
+ * \note The tangent is "backwards", i.e. it is with respect to |
+ * decreasing index rather than increasing index. |
+ * |
+ * \pre 2 \<= len. |
+ * \pre d[len - 1] != d[len - 2]. |
+ * \pre all[p in d] in_svg_plane(p). |
+ */ |
+Point |
+darray_right_tangent(Point const d[], unsigned const len, double const tolerance_sq) |
+{ |
+ assert( 2 <= len ); |
+ assert( 0 <= tolerance_sq ); |
+ unsigned const last = len - 1; |
+ for (unsigned i = last - 1;; i--) { |
+ Point const pi(d[i]); |
+ Point const t(pi - d[last]); |
+ double const distsq = dot(t, t); |
+ if ( tolerance_sq < distsq ) { |
+ return unit_vector(t); |
+ } |
+ if (i == 0) { |
+ return ( distsq == 0 |
+ ? darray_right_tangent(d, len) |
+ : unit_vector(t) ); |
+ } |
+ } |
+} |
+ |
+/** |
+ * Estimates the (backward) tangent at d[center], by averaging the two |
+ * segments connected to d[center] (and then normalizing the result). |
+ * |
+ * \note The tangent is "backwards", i.e. it is with respect to |
+ * decreasing index rather than increasing index. |
+ * |
+ * \pre (0 \< center \< len - 1) and d is uniqued (at least in |
+ * the immediate vicinity of \a center). |
+ */ |
+static Point |
+darray_center_tangent(Point const d[], |
+ unsigned const center, |
+ unsigned const len) |
+{ |
+ assert( center != 0 ); |
+ assert( center < len - 1 ); |
+ |
+ Point ret; |
+ if ( d[center + 1] == d[center - 1] ) { |
+ /* Rotate 90 degrees in an arbitrary direction. */ |
+ Point const diff = d[center] - d[center - 1]; |
+ ret = rot90(diff); |
+ } else { |
+ ret = d[center - 1] - d[center + 1]; |
+ } |
+ ret.normalize(); |
+ return ret; |
+} |
+ |
+ |
+/** |
+ * Assign parameter values to digitized points using relative distances between points. |
+ * |
+ * \pre Parameter array u must have space for \a len items. |
+ */ |
+static void |
+chord_length_parameterize(Point const d[], double u[], unsigned const len) |
+{ |
+ if(!( 2 <= len )) |
+ return; |
+ |
+ /* First let u[i] equal the distance travelled along the path from d[0] to d[i]. */ |
+ u[0] = 0.0; |
+ for (unsigned i = 1; i < len; i++) { |
+ double const dist = distance(d[i], d[i-1]); |
+ u[i] = u[i-1] + dist; |
+ } |
+ |
+ /* Then scale to [0.0 .. 1.0]. */ |
+ double tot_len = u[len - 1]; |
+ if(!( tot_len != 0 )) |
+ return; |
+ if (is_finite(tot_len)) { |
+ for (unsigned i = 1; i < len; ++i) { |
+ u[i] /= tot_len; |
+ } |
+ } else { |
+ /* We could do better, but this probably never happens anyway. */ |
+ for (unsigned i = 1; i < len; ++i) { |
+ u[i] = i / (double) ( len - 1 ); |
+ } |
+ } |
+ |
+ /** \todo |
+ * It's been reported that u[len - 1] can differ from 1.0 on some |
+ * systems (amd64), despite it having been calculated as x / x where x |
+ * is isFinite and non-zero. |
+ */ |
+ if (u[len - 1] != 1) { |
+ double const diff = u[len - 1] - 1; |
+ if (fabs(diff) > 1e-13) { |
+ assert(0); // No warnings in 2geom |
+ //g_warning("u[len - 1] = %19g (= 1 + %19g), expecting exactly 1", |
+ // u[len - 1], diff); |
+ } |
+ u[len - 1] = 1; |
+ } |
+ |
+#ifdef BEZIER_DEBUG |
+ assert( u[0] == 0.0 && u[len - 1] == 1.0 ); |
+ for (unsigned i = 1; i < len; i++) { |
+ assert( u[i] >= u[i-1] ); |
+ } |
+#endif |
+} |
+ |
+ |
+ |
+ |
+/** |
+ * Find the maximum squared distance of digitized points to fitted curve, and (if this maximum |
+ * error is non-zero) set \a *splitPoint to the corresponding index. |
+ * |
+ * \pre 2 \<= len. |
+ * \pre u[0] == 0. |
+ * \pre u[len - 1] == 1.0. |
+ * \post ((ret == 0.0) |
+ * || ((*splitPoint \< len - 1) |
+ * \&\& (*splitPoint != 0 || ret \< 0.0))). |
+ */ |
+static double |
+compute_max_error_ratio(Point const d[], double const u[], unsigned const len, |
+ BezierCurve const bezCurve, double const tolerance, |
+ unsigned *const splitPoint) |
+{ |
+ assert( 2 <= len ); |
+ unsigned const last = len - 1; |
+ assert( bezCurve[0] == d[0] ); |
+ assert( bezCurve[3] == d[last] ); |
+ assert( u[0] == 0.0 ); |
+ assert( u[last] == 1.0 ); |
+ /* I.e. assert that the error for the first & last points is zero. |
+ * Otherwise we should include those points in the below loop. |
+ * The assertion is also necessary to ensure 0 < splitPoint < last. |
+ */ |
+ |
+ double maxDistsq = 0.0; /* Maximum error */ |
+ double max_hook_ratio = 0.0; |
+ unsigned snap_end = 0; |
+ Point prev = bezCurve[0]; |
+ for (unsigned i = 1; i <= last; i++) { |
+ Point const curr = bezier_pt(3, bezCurve, u[i]); |
+ double const distsq = lensq( curr - d[i] ); |
+ if ( distsq > maxDistsq ) { |
+ maxDistsq = distsq; |
+ *splitPoint = i; |
+ } |
+ double const hook_ratio = compute_hook(prev, curr, .5 * (u[i - 1] + u[i]), bezCurve, tolerance); |
+ if (max_hook_ratio < hook_ratio) { |
+ max_hook_ratio = hook_ratio; |
+ snap_end = i; |
+ } |
+ prev = curr; |
+ } |
+ |
+ double const dist_ratio = sqrt(maxDistsq) / tolerance; |
+ double ret; |
+ if (max_hook_ratio <= dist_ratio) { |
+ ret = dist_ratio; |
+ } else { |
+ assert(0 < snap_end); |
+ ret = -max_hook_ratio; |
+ *splitPoint = snap_end - 1; |
+ } |
+ assert( ret == 0.0 |
+ || ( ( *splitPoint < last ) |
+ && ( *splitPoint != 0 || ret < 0. ) ) ); |
+ return ret; |
+} |
+ |
+/** |
+ * Whereas compute_max_error_ratio() checks for itself that each data point |
+ * is near some point on the curve, this function checks that each point on |
+ * the curve is near some data point (or near some point on the polyline |
+ * defined by the data points, or something like that: we allow for a |
+ * "reasonable curviness" from such a polyline). "Reasonable curviness" |
+ * means we draw a circle centred at the midpoint of a..b, of radius |
+ * proportional to the length |a - b|, and require that each point on the |
+ * segment of bezCurve between the parameters of a and b be within that circle. |
+ * If any point P on the bezCurve segment is outside of that allowable |
+ * region (circle), then we return some metric that increases with the |
+ * distance from P to the circle. |
+ * |
+ * Given that this is a fairly arbitrary criterion for finding appropriate |
+ * places for sharp corners, we test only one point on bezCurve, namely |
+ * the point on bezCurve with parameter halfway between our estimated |
+ * parameters for a and b. (Alternatives are taking the farthest of a |
+ * few parameters between those of a and b, or even using a variant of |
+ * NewtonRaphsonFindRoot() for finding the maximum rather than minimum |
+ * distance.) |
+ */ |
+static double |
+compute_hook(Point const &a, Point const &b, double const u, BezierCurve const bezCurve, |
+ double const tolerance) |
+{ |
+ Point const P = bezier_pt(3, bezCurve, u); |
+ double const dist = distance((a+b)*.5, P); |
+ if (dist < tolerance) { |
+ return 0; |
+ } |
+ double const allowed = distance(a, b) + tolerance; |
+ return dist / allowed; |
+ /** \todo |
+ * effic: Hooks are very rare. We could start by comparing |
+ * distsq, only resorting to the more expensive L2 in cases of |
+ * uncertainty. |
+ */ |
+} |
+ |
+} |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/quadtree.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/quadtree.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/quadtree.h (revision 14937) |
@@ -0,0 +1,41 @@ |
+#include <vector> |
+#include <cassert> |
+ |
+class Quad{ |
+public: |
+ Quad* children[4]; |
+ std::vector<int> data; |
+ Quad() { |
+ for(int i = 0; i < 4; i++) |
+ children[i] = 0; |
+ } |
+ typedef std::vector<int>::iterator iterator; |
+}; |
+ |
+class QuadTree{ |
+public: |
+ Quad* root; |
+ double scale; |
+ double bx0, bx1; |
+ double by0, by1; |
+ |
+ QuadTree() : root(0), scale(1) {} |
+ |
+ Quad* search(double x0, double y0, double x1, double y1); |
+ void insert(double x0, double y0, double x1, double y1, int shape); |
+ void erase(Quad *q, int shape); |
+}; |
+ |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(substatement-open . 0)) |
+ indent-tabs-mode:nil |
+ c-brace-offset:0 |
+ fill-column:99 |
+ End: |
+ vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
+*/ |
+ |
Index: trunk/Scribus/scribus/third_party/lib2geom/sturm.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sturm.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sturm.h (revision 14937) |
@@ -0,0 +1,70 @@ |
+#ifndef LIB2GEOM_STURM_HEADER |
+#define LIB2GEOM_STURM_HEADER |
+ |
+#include "poly.h" |
+#include "utils.h" |
+ |
+namespace Geom { |
+ |
+class sturm : public std::vector<Poly>{ |
+public: |
+ sturm(Poly const &X) { |
+ push_back(X); |
+ push_back(derivative(X)); |
+ Poly Xi = back(); |
+ Poly Xim1 = X; |
+ std::cout << "sturm:\n" << Xim1 << std::endl; |
+ std::cout << Xi << std::endl; |
+ while(Xi.size() > 1) { |
+ Poly r; |
+ divide(Xim1, Xi, r); |
+ std::cout << r << std::endl; |
+ assert(r.size() < Xi.size()); |
+ Xim1 = Xi; |
+ Xi = -r; |
+ assert(Xim1.size() > Xi.size()); |
+ push_back(Xi); |
+ } |
+ } |
+ |
+ unsigned count_signs(double t) { |
+ unsigned n_signs = 0;/* Number of sign-changes */ |
+ const double big = 1e20; // a number such that practical polys would overflow on evaluation |
+ if(t >= big) { |
+ int old_sign = sgn((*this)[0].back()); |
+ for (unsigned i = 1; i < size(); i++) { |
+ int sign = sgn((*this)[i].back()); |
+ if (sign != old_sign) |
+ n_signs++; |
+ old_sign = sign; |
+ } |
+ } else { |
+ int old_sign = sgn((*this)[0].eval(t)); |
+ for (unsigned i = 1; i < size(); i++) { |
+ int sign = sgn((*this)[i].eval(t)); |
+ if (sign != old_sign) |
+ n_signs++; |
+ old_sign = sign; |
+ } |
+ } |
+ return n_signs; |
+ } |
+ |
+ unsigned n_roots_between(double l, double r) { |
+ return count_signs(l) - count_signs(r); |
+ } |
+}; |
+ |
+} //namespace Geom |
+ |
+#endif |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/piecewise.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/piecewise.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/piecewise.h (revision 14937) |
@@ -0,0 +1,690 @@ |
+/* |
+ * piecewise.h - Piecewise function class |
+ * |
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#ifndef SEEN_GEOM_PW_SB_H |
+#define SEEN_GEOM_PW_SB_H |
+ |
+#include "sbasis.h" |
+#include <vector> |
+#include <map> |
+ |
+#include "concepts.h" |
+#include "isnan.h" |
+#include <boost/concept_check.hpp> |
+ |
+namespace Geom { |
+ |
+template <typename T> |
+class Piecewise { |
+ BOOST_CLASS_REQUIRE(T, Geom, FragmentConcept); |
+ |
+ public: |
+ std::vector<double> cuts; |
+ std::vector<T> segs; |
+ //segs[i] stretches from cuts[i] to cuts[i+1]. |
+ |
+ Piecewise() {} |
+ |
+ explicit Piecewise(const T &s) { |
+ push_cut(0.); |
+ push_seg(s); |
+ push_cut(1.); |
+ } |
+ |
+ typedef typename T::output_type output_type; |
+ |
+ explicit Piecewise(const output_type & v) { |
+ push_cut(0.); |
+ push_seg(T(v)); |
+ push_cut(1.); |
+ } |
+ |
+ inline T operator[](unsigned i) const { return segs[i]; } |
+ inline T &operator[](unsigned i) { return segs[i]; } |
+ inline output_type operator()(double t) const { return valueAt(t); } |
+ inline output_type valueAt(double t) const { |
+ unsigned n = segN(t); |
+ return segs[n](segT(t, n)); |
+ } |
+ //TODO: maybe it is not a good idea to have this? |
+ Piecewise<T> operator()(SBasis f); |
+ Piecewise<T> operator()(Piecewise<SBasis>f); |
+ |
+ inline unsigned size() const { return segs.size(); } |
+ inline bool empty() const { return segs.empty(); } |
+ |
+ /**Convenience/implementation hiding function to add segment/cut pairs. |
+ * Asserts that basic size and order invariants are correct |
+ */ |
+ inline void push(const T &s, double to) { |
+ assert(cuts.size() - segs.size() == 1); |
+ push_seg(s); |
+ push_cut(to); |
+ } |
+ //Convenience/implementation hiding function to add cuts. |
+ inline void push_cut(double c) { |
+ assert_invariants(cuts.empty() || c > cuts.back()); |
+ cuts.push_back(c); |
+ } |
+ //Convenience/implementation hiding function to add segments. |
+ inline void push_seg(const T &s) { segs.push_back(s); } |
+ |
+ /**Returns the segment index which corresponds to a 'global' piecewise time. |
+ * Also takes optional low/high parameters to expedite the search for the segment. |
+ */ |
+ inline unsigned segN(double t, int low = 0, int high = -1) const { |
+ high = (high == -1) ? size() : high; |
+ if(t < cuts[0]) return 0; |
+ if(t >= cuts[size()]) return size() - 1; |
+ while(low < high) { |
+ int mid = (high + low) / 2; //Lets not plan on having huge (> INT_MAX / 2) cut sequences |
+ double mv = cuts[mid]; |
+ if(mv < t) { |
+ if(t < cuts[mid + 1]) return mid; else low = mid + 1; |
+ } else if(t < mv) { |
+ if(cuts[mid - 1] < t) return mid - 1; else high = mid - 1; |
+ } else { |
+ return mid; |
+ } |
+ } |
+ return low; |
+ } |
+ |
+ /**Returns the time within a segment, given the 'global' piecewise time. |
+ * Also takes an optional index parameter which may be used for efficiency or to find the time on a |
+ * segment outside its range. If it is left to its default, -1, it will call segN to find the index. |
+ */ |
+ inline double segT(double t, int i = -1) const { |
+ if(i == -1) i = segN(t); |
+ assert(i >= 0); |
+ return (t - cuts[i]) / (cuts[i+1] - cuts[i]); |
+ } |
+ |
+ inline double mapToDomain(double t, unsigned i) const { |
+ return (1-t)*cuts[i] + t*cuts[i+1]; //same as: t * (cuts[i+1] - cuts[i]) + cuts[i] |
+ } |
+ |
+ //Offsets the piecewise domain |
+ inline void offsetDomain(double o) { |
+ assert(is_finite(o)); |
+ if(o != 0) |
+ for(unsigned i = 0; i <= size(); i++) |
+ cuts[i] += o; |
+ } |
+ |
+ //Scales the domain of the function by a value. 0 will result in an empty Piecewise. |
+ inline void scaleDomain(double s) { |
+ assert(s > 0); |
+ if(s == 0) { |
+ cuts.clear(); segs.clear(); |
+ return; |
+ } |
+ for(unsigned i = 0; i <= size(); i++) |
+ cuts[i] *= s; |
+ } |
+ |
+ //Retrieves the domain in interval form |
+ inline Interval domain() const { return Interval(cuts.front(), cuts.back()); } |
+ |
+ //Transforms the domain into another interval |
+ inline void setDomain(Interval dom) { |
+ if(empty()) return; |
+ if(dom.isEmpty()) { |
+ cuts.clear(); segs.clear(); |
+ return; |
+ } |
+ double cf = cuts.front(); |
+ double o = dom.min() - cf, s = dom.extent() / (cuts.back() - cf); |
+ for(unsigned i = 0; i <= size(); i++) |
+ cuts[i] = (cuts[i] - cf) * s + o; |
+ } |
+ |
+ //Concatenates this Piecewise function with another, offseting time of the other to match the end. |
+ inline void concat(const Piecewise<T> &other) { |
+ if(other.empty()) return; |
+ |
+ if(empty()) { |
+ cuts = other.cuts; segs = other.segs; |
+ return; |
+ } |
+ |
+ segs.insert(segs.end(), other.segs.begin(), other.segs.end()); |
+ double t = cuts.back() - other.cuts.front(); |
+ for(unsigned i = 0; i < other.size(); i++) |
+ push_cut(other.cuts[i + 1] + t); |
+ } |
+ |
+ //Like concat, but ensures continuity. |
+ inline void continuousConcat(const Piecewise<T> &other) { |
+ boost::function_requires<AddableConcept<typename T::output_type> >(); |
+ if(other.empty()) return; |
+ typename T::output_type y = segs.back().at1() - other.segs.front().at0(); |
+ |
+ if(empty()) { |
+ for(unsigned i = 0; i < other.size(); i++) |
+ push_seg(other[i] + y); |
+ cuts = other.cuts; |
+ return; |
+ } |
+ |
+ double t = cuts.back() - other.cuts.front(); |
+ for(unsigned i = 0; i < other.size(); i++) |
+ push(other[i] + y, other.cuts[i + 1] + t); |
+ } |
+ |
+ //returns true if the Piecewise<T> meets some basic invariants. |
+ inline bool invariants() const { |
+ // segs between cuts |
+ if(!(segs.size() + 1 == cuts.size() || (segs.empty() && cuts.empty()))) |
+ return false; |
+ // cuts in order |
+ for(unsigned i = 0; i < segs.size(); i++) |
+ if(cuts[i] >= cuts[i+1]) |
+ return false; |
+ return true; |
+ } |
+ |
+}; |
+ |
+template<typename T> |
+inline typename FragmentConcept<T>::BoundsType bounds_fast(const Piecewise<T> &f) { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ |
+ if(f.empty()) return typename FragmentConcept<T>::BoundsType(); |
+ typename FragmentConcept<T>::BoundsType ret(bounds_fast(f[0])); |
+ for(unsigned i = 1; i < f.size(); i++) |
+ ret.unionWith(bounds_fast(f[i])); |
+ return ret; |
+} |
+ |
+template<typename T> |
+inline typename FragmentConcept<T>::BoundsType bounds_exact(const Piecewise<T> &f) { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ |
+ if(f.empty()) return typename FragmentConcept<T>::BoundsType(); |
+ typename FragmentConcept<T>::BoundsType ret(bounds_exact(f[0])); |
+ for(unsigned i = 1; i < f.size(); i++) |
+ ret.unionWith(bounds_exact(f[i])); |
+ return ret; |
+} |
+ |
+template<typename T> |
+inline typename FragmentConcept<T>::BoundsType bounds_local(const Piecewise<T> &f, const Interval &m) { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ |
+ if(f.empty()) return typename FragmentConcept<T>::BoundsType(); |
+ if(m.isEmpty()) return typename FragmentConcept<T>::BoundsType(f(m.min())); |
+ |
+ unsigned fi = f.segN(m.min()), ti = f.segN(m.max()); |
+ double ft = f.segT(m.min(), fi), tt = f.segT(m.max(), ti); |
+ |
+ if(fi == ti) return bounds_local(f[fi], Interval(ft, tt)); |
+ |
+ typename FragmentConcept<T>::BoundsType ret(bounds_local(f[fi], Interval(ft, 1.))); |
+ for(unsigned i = fi + 1; i < ti; i++) |
+ ret.unionWith(bounds_exact(f[i])); |
+ if(tt != 0.) ret.unionWith(bounds_local(f[ti], Interval(0., tt))); |
+ |
+ return ret; |
+} |
+ |
+//returns a portion of a piece of a Piecewise<T>, given the piece's index and a to/from time. |
+template<typename T> |
+T elem_portion(const Piecewise<T> &a, unsigned i, double from, double to) { |
+ assert(i < a.size()); |
+ double rwidth = 1 / (a.cuts[i+1] - a.cuts[i]); |
+ return portion( a[i], (from - a.cuts[i]) * rwidth, (to - a.cuts[i]) * rwidth ); |
+} |
+ |
+/**Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c); |
+ * Further subdivides the Piecewise<T> such that there is a cut at every value in c. |
+ * Precondition: c sorted lower to higher. |
+ * |
+ * //Given Piecewise<T> a and b: |
+ * Piecewise<T> ac = a.partition(b.cuts); |
+ * Piecewise<T> bc = b.partition(a.cuts); |
+ * //ac.cuts should be equivalent to bc.cuts |
+ */ |
+template<typename T> |
+Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c) { |
+ assert(pw.invariants()); |
+ if(c.empty()) return Piecewise<T>(pw); |
+ |
+ Piecewise<T> ret = Piecewise<T>(); |
+ ret.cuts.reserve(c.size() + pw.cuts.size()); |
+ ret.segs.reserve(c.size() + pw.cuts.size() - 1); |
+ |
+ if(pw.empty()) { |
+ ret.cuts = c; |
+ for(unsigned i = 0; i < c.size() - 1; i++) |
+ ret.push_seg(T()); |
+ return ret; |
+ } |
+ |
+ unsigned si = 0, ci = 0; //Segment index, Cut index |
+ |
+ //if the cuts have something earlier than the Piecewise<T>, add portions of the first segment |
+ while(c[ci] < pw.cuts.front() && ci < c.size()) { |
+ bool isLast = (ci == c.size()-1 || c[ci + 1] >= pw.cuts.front()); |
+ ret.push_cut(c[ci]); |
+ ret.push_seg( elem_portion(pw, 0, c[ci], isLast ? pw.cuts.front() : c[ci + 1]) ); |
+ ci++; |
+ } |
+ |
+ ret.push_cut(pw.cuts[0]); |
+ double prev = pw.cuts[0]; //previous cut |
+ //Loop which handles cuts within the Piecewise<T> domain |
+ //Should have the cuts = segs + 1 invariant |
+ while(si < pw.size() && ci <= c.size()) { |
+ if(ci == c.size() && prev <= pw.cuts[si]) { //cuts exhausted, straight copy the rest |
+ ret.segs.insert(ret.segs.end(), pw.segs.begin() + si, pw.segs.end()); |
+ ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + si + 1, pw.cuts.end()); |
+ return ret; |
+ }else if(ci == c.size() || c[ci] >= pw.cuts[si + 1]) { //no more cuts within this segment, finalize |
+ if(prev > pw.cuts[si]) { //segment already has cuts, so portion is required |
+ ret.push_seg(portion(pw[si], pw.segT(prev, si), 1.0)); |
+ } else { //plain copy is fine |
+ ret.push_seg(pw[si]); |
+ } |
+ ret.push_cut(pw.cuts[si + 1]); |
+ prev = pw.cuts[si + 1]; |
+ si++; |
+ } else if(c[ci] == pw.cuts[si]){ //coincident |
+ //Already finalized the seg with the code immediately above |
+ ci++; |
+ } else { //plain old subdivision |
+ ret.push(elem_portion(pw, si, prev, c[ci]), c[ci]); |
+ prev = c[ci]; |
+ ci++; |
+ } |
+ } |
+ |
+ //input cuts extend further than this Piecewise<T>, extend the last segment. |
+ while(ci < c.size()) { |
+ if(c[ci] > prev) { |
+ ret.push(elem_portion(pw, pw.size() - 1, prev, c[ci]), c[ci]); |
+ prev = c[ci]; |
+ } |
+ ci++; |
+ } |
+ return ret; |
+} |
+ |
+/**Piecewise<T> portion(const Piecewise<T> &pw, double from, double to); |
+ * Returns a Piecewise<T> with a defined domain of [min(from, to), max(from, to)]. |
+ */ |
+template<typename T> |
+Piecewise<T> portion(const Piecewise<T> &pw, double from, double to) { |
+ if(pw.empty() || from == to) return Piecewise<T>(); |
+ |
+ Piecewise<T> ret; |
+ |
+ double temp = from; |
+ from = std::min(from, to); |
+ to = std::max(temp, to); |
+ |
+ unsigned i = pw.segN(from); |
+ ret.push_cut(from); |
+ if(i == pw.size() - 1 || to < pw.cuts[i + 1]) { //to/from inhabit the same segment |
+ ret.push(elem_portion(pw, i, from, to), to); |
+ return ret; |
+ } |
+ ret.push_seg(portion( pw[i], pw.segT(from, i), 1.0 )); |
+ i++; |
+ unsigned fi = pw.segN(to, i); |
+ |
+ ret.segs.insert(ret.segs.end(), pw.segs.begin() + i, pw.segs.begin() + fi); //copy segs |
+ ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + i, pw.cuts.begin() + fi + 1); //and their cuts |
+ |
+ ret.push_seg( portion(pw[fi], 0.0, pw.segT(to, fi))); |
+ if(to != ret.cuts.back()) ret.push_cut(to); |
+ ret.invariants(); |
+ return ret; |
+} |
+ |
+template<typename T> |
+Piecewise<T> remove_short_cuts(Piecewise<T> const &f, double tol) { |
+ if(f.empty()) return f; |
+ Piecewise<T> ret; |
+ ret.push_cut(f.cuts[0]); |
+ for(unsigned i=0; i<f.size(); i++){ |
+ if (f.cuts[i+1]-f.cuts[i] >= tol || i==f.size()-1) { |
+ ret.push(f[i], f.cuts[i+1]); |
+ } |
+ } |
+ return ret; |
+} |
+ |
+template<typename T> |
+Piecewise<T> remove_short_cuts_extending(Piecewise<T> const &f, double tol) { |
+ if(f.empty()) return f; |
+ Piecewise<T> ret; |
+ ret.push_cut(f.cuts[0]); |
+ double last = f.cuts[0]; // last cut included |
+ for(unsigned i=0; i<f.size(); i++){ |
+ if (f.cuts[i+1]-f.cuts[i] >= tol) { |
+ ret.push(elem_portion(f, i, last, f.cuts[i+1]), f.cuts[i+1]); |
+ last = f.cuts[i+1]; |
+ } |
+ } |
+ return ret; |
+} |
+ |
+template<typename T> |
+std::vector<double> roots(const Piecewise<T> &pw) { |
+ std::vector<double> ret; |
+ for(unsigned i = 0; i < pw.size(); i++) { |
+ std::vector<double> sr = roots(pw[i]); |
+ for (unsigned j = 0; j < sr.size(); j++) ret.push_back(sr[j] * (pw.cuts[i + 1] - pw.cuts[i]) + pw.cuts[i]); |
+ |
+ } |
+ return ret; |
+} |
+ |
+//IMPL: OffsetableConcept |
+template<typename T> |
+Piecewise<T> operator+(Piecewise<T> const &a, typename T::output_type b) { |
+ boost::function_requires<OffsetableConcept<T> >(); |
+//TODO:empty |
+ Piecewise<T> ret = Piecewise<T>(); |
+ ret.cuts = a.cuts; |
+ for(unsigned i = 0; i < a.size();i++) |
+ ret.push_seg(a[i] + b); |
+ return ret; |
+} |
+template<typename T> |
+Piecewise<T> operator-(Piecewise<T> const &a, typename T::output_type b) { |
+ boost::function_requires<OffsetableConcept<T> >(); |
+//TODO: empty |
+ Piecewise<T> ret = Piecewise<T>(); |
+ ret.cuts = a.cuts; |
+ for(unsigned i = 0; i < a.size();i++) |
+ ret.push_seg(a[i] - b); |
+ return ret; |
+} |
+template<typename T> |
+Piecewise<T> operator+=(Piecewise<T>& a, typename T::output_type b) { |
+ boost::function_requires<OffsetableConcept<T> >(); |
+ |
+ if(a.empty()) { a.push_cut(0.); a.push(T(b), 1.); return a; } |
+ |
+ for(unsigned i = 0; i < a.size();i++) |
+ a[i] += b; |
+ return a; |
+} |
+template<typename T> |
+Piecewise<T> operator-=(Piecewise<T>& a, typename T::output_type b) { |
+ boost::function_requires<OffsetableConcept<T> >(); |
+ |
+ if(a.empty()) { a.push_cut(0.); a.push(T(b), 1.); return a; } |
+ |
+ for(unsigned i = 0;i < a.size();i++) |
+ a[i] -= b; |
+ return a; |
+} |
+ |
+//IMPL: ScalableConcept |
+template<typename T> |
+Piecewise<T> operator-(Piecewise<T> const &a) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ |
+ Piecewise<T> ret; |
+ ret.cuts = a.cuts; |
+ for(unsigned i = 0; i < a.size();i++) |
+ ret.push_seg(- a[i]); |
+ return ret; |
+} |
+template<typename T> |
+Piecewise<T> operator*(Piecewise<T> const &a, double b) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ |
+ if(a.empty()) return Piecewise<T>(); |
+ |
+ Piecewise<T> ret; |
+ ret.cuts = a.cuts; |
+ for(unsigned i = 0; i < a.size();i++) |
+ ret.push_seg(a[i] * b); |
+ return ret; |
+} |
+template<typename T> |
+Piecewise<T> operator/(Piecewise<T> const &a, double b) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ |
+ //FIXME: b == 0? |
+ if(a.empty()) return Piecewise<T>(); |
+ |
+ Piecewise<T> ret; |
+ ret.cuts = a.cuts; |
+ for(unsigned i = 0; i < a.size();i++) |
+ ret.push_seg(a[i] / b); |
+ return ret; |
+} |
+template<typename T> |
+Piecewise<T> operator*=(Piecewise<T>& a, double b) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ |
+ if(a.empty()) return Piecewise<T>(); |
+ |
+ for(unsigned i = 0; i < a.size();i++) |
+ a[i] *= b; |
+ return a; |
+} |
+template<typename T> |
+Piecewise<T> operator/=(Piecewise<T>& a, double b) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ |
+ //FIXME: b == 0? |
+ if(a.empty()) return Piecewise<T>(); |
+ |
+ for(unsigned i = 0; i < a.size();i++) |
+ a[i] /= b; |
+ return a; |
+} |
+ |
+//IMPL: AddableConcept |
+template<typename T> |
+Piecewise<T> operator+(Piecewise<T> const &a, Piecewise<T> const &b) { |
+ boost::function_requires<AddableConcept<T> >(); |
+ |
+ Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts); |
+ Piecewise<T> ret = Piecewise<T>(); |
+ assert(pa.size() == pb.size()); |
+ ret.cuts = pa.cuts; |
+ for (unsigned i = 0; i < pa.size(); i++) |
+ ret.push_seg(pa[i] + pb[i]); |
+ return ret; |
+} |
+template<typename T> |
+Piecewise<T> operator-(Piecewise<T> const &a, Piecewise<T> const &b) { |
+ boost::function_requires<AddableConcept<T> >(); |
+ |
+ Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts); |
+ Piecewise<T> ret = Piecewise<T>(); |
+ assert(pa.size() == pb.size()); |
+ ret.cuts = pa.cuts; |
+ for (unsigned i = 0; i < pa.size(); i++) |
+ ret.push_seg(pa[i] - pb[i]); |
+ return ret; |
+} |
+template<typename T> |
+inline Piecewise<T> operator+=(Piecewise<T> &a, Piecewise<T> const &b) { |
+ a = a+b; |
+ return a; |
+} |
+template<typename T> |
+inline Piecewise<T> operator-=(Piecewise<T> &a, Piecewise<T> const &b) { |
+ a = a-b; |
+ return a; |
+} |
+ |
+template<typename T1,typename T2> |
+Piecewise<T2> operator*(Piecewise<T1> const &a, Piecewise<T2> const &b) { |
+ //function_requires<MultiplicableConcept<T1> >(); |
+ //function_requires<MultiplicableConcept<T2> >(); |
+ |
+ Piecewise<T1> pa = partition(a, b.cuts); |
+ Piecewise<T2> pb = partition(b, a.cuts); |
+ Piecewise<T2> ret = Piecewise<T2>(); |
+ assert(pa.size() == pb.size()); |
+ ret.cuts = pa.cuts; |
+ for (unsigned i = 0; i < pa.size(); i++) |
+ ret.push_seg(pa[i] * pb[i]); |
+ return ret; |
+} |
+ |
+template<typename T> |
+inline Piecewise<T> operator*=(Piecewise<T> &a, Piecewise<T> const &b) { |
+ a = a * b; |
+ return a; |
+} |
+ |
+Piecewise<SBasis> divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, unsigned k); |
+//TODO: replace divide(a,b,k) by divide(a,b,tol,k)? |
+//TODO: atm, relative error is ~(tol/a)%. Find a way to make it independant of a. |
+//Nota: the result is 'truncated' where b is smaller than 'zero': ~ a/max(b,zero). |
+Piecewise<SBasis> |
+divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3); |
+Piecewise<SBasis> |
+divide(SBasis const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3); |
+Piecewise<SBasis> |
+divide(Piecewise<SBasis> const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3); |
+Piecewise<SBasis> |
+divide(SBasis const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3); |
+ |
+//Composition: functions called compose_* are pieces of compose that are factored out in pw.cpp. |
+std::map<double,unsigned> compose_pullback(std::vector<double> const &cuts, SBasis const &g); |
+int compose_findSegIdx(std::map<double,unsigned>::iterator const &cut, |
+ std::map<double,unsigned>::iterator const &next, |
+ std::vector<double> const &levels, |
+ SBasis const &g); |
+ |
+//TODO: add concept check |
+template<typename T> |
+Piecewise<T> compose(Piecewise<T> const &f, SBasis const &g){ |
+ Piecewise<T> result; |
+ if (f.empty()) return result; |
+ if (g.isZero()) return Piecewise<T>(f(0)); |
+ if (f.size()==1){ |
+ double t0 = f.cuts[0], width = f.cuts[1] - t0; |
+ return (Piecewise<T>) compose(f.segs[0],compose(Linear(-t0 / width, (1-t0) / width), g)); |
+ } |
+ |
+ //first check bounds... |
+ Interval bs = bounds_fast(g); |
+ if (f.cuts.front() > bs.max() || bs.min() > f.cuts.back()){ |
+ int idx = (bs.max() < f.cuts[1]) ? 0 : f.cuts.size()-2; |
+ double t0 = f.cuts[idx], width = f.cuts[idx+1] - t0; |
+ return (Piecewise<T>) compose(f.segs[idx],compose(Linear(-t0 / width, (1-t0) / width), g)); |
+ } |
+ |
+ std::vector<double> levels;//we can forget first and last cuts... |
+ levels.insert(levels.begin(),f.cuts.begin()+1,f.cuts.end()-1); |
+ //TODO: use a std::vector<pairs<double,unsigned> > instead of a map<double,unsigned>. |
+ std::map<double,unsigned> cuts_pb = compose_pullback(levels,g); |
+ |
+ //-- Compose each piece of g with the relevant seg of f. |
+ result.cuts.push_back(0.); |
+ std::map<double,unsigned>::iterator cut=cuts_pb.begin(); |
+ std::map<double,unsigned>::iterator next=cut; next++; |
+ while(next!=cuts_pb.end()){ |
+ //assert(std::abs(int((*cut).second-(*next).second))<1); |
+ //TODO: find a way to recover from this error? the root finder missed some root; |
+ // the levels/variations of f might be too close/fast... |
+ int idx = compose_findSegIdx(cut,next,levels,g); |
+ double t0=(*cut).first; |
+ double t1=(*next).first; |
+ |
+ SBasis sub_g=compose(g, Linear(t0,t1)); |
+ sub_g=compose(Linear(-f.cuts[idx]/(f.cuts[idx+1]-f.cuts[idx]), |
+ (1-f.cuts[idx])/(f.cuts[idx+1]-f.cuts[idx])),sub_g); |
+ result.push(compose(f[idx],sub_g),t1); |
+ cut++; |
+ next++; |
+ } |
+ return(result); |
+} |
+ |
+//TODO: add concept check for following composition functions |
+template<typename T> |
+Piecewise<T> compose(Piecewise<T> const &f, Piecewise<SBasis> const &g){ |
+ Piecewise<T> result; |
+ for(unsigned i = 0; i < g.segs.size(); i++){ |
+ Piecewise<T> fgi=compose(f, g.segs[i]); |
+ fgi.setDomain(Interval(g.cuts[i], g.cuts[i+1])); |
+ result.concat(fgi); |
+ } |
+ return result; |
+} |
+ |
+template <typename T> |
+Piecewise<T> Piecewise<T>::operator()(SBasis f){return compose((*this),f);} |
+template <typename T> |
+Piecewise<T> Piecewise<T>::operator()(Piecewise<SBasis>f){return compose((*this),f);} |
+ |
+template<typename T> |
+Piecewise<T> integral(Piecewise<T> const &a) { |
+ Piecewise<T> result; |
+ result.segs.resize(a.segs.size()); |
+ result.cuts = a.cuts; |
+ typename T::output_type c = a.segs[0].at0(); |
+ for(unsigned i = 0; i < a.segs.size(); i++){ |
+ result.segs[i] = integral(a.segs[i])*(a.cuts[i+1]-a.cuts[i]); |
+ result.segs[i]+= c-result.segs[i].at0(); |
+ c = result.segs[i].at1(); |
+ } |
+ return result; |
+} |
+ |
+template<typename T> |
+Piecewise<T> derivative(Piecewise<T> const &a) { |
+ Piecewise<T> result; |
+ result.segs.resize(a.segs.size()); |
+ result.cuts = a.cuts; |
+ for(unsigned i = 0; i < a.segs.size(); i++){ |
+ result.segs[i] = derivative(a.segs[i])/(a.cuts[i+1]-a.cuts[i]); |
+ } |
+ return result; |
+} |
+ |
+std::vector<double> roots(Piecewise<SBasis> const &f); |
+ |
+} |
+ |
+#endif //SEEN_GEOM_PW_SB_H |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/convex-cover.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/convex-cover.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/convex-cover.h (revision 14937) |
@@ -0,0 +1,174 @@ |
+#ifndef GEOM_CONVEX_COVER_H |
+#define GEOM_CONVEX_COVER_H |
+ |
+/* |
+ * convex-cover.h |
+ * |
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+/** A convex cover is a sequence of convex polygons that completely cover the path. For now a |
+ * convex hull class is included here (the convex-hull header is wrong) |
+ */ |
+ |
+#include "point.h" |
+#include <vector> |
+ |
+namespace Geom{ |
+ |
+/** ConvexHull |
+ * A convexhull is a convex region - every point between two points in the convex hull is also in |
+ * the convex hull. It is defined by a set of points travelling in a clockwise direction. We require the first point to be top most, and of the topmost, leftmost. |
+ |
+ * An empty hull has no points, we allow a single point or two points degenerate cases. |
+ |
+ * We could provide the centroid as a member for efficient direction determination. We can update the |
+ * centroid with all operations with the same time complexity as the operation. |
+ */ |
+ |
+class ConvexHull{ |
+public: // XXX: should be private :) |
+ // extracts the convex hull of boundary. internal use only |
+ void find_pivot(); |
+ void angle_sort(); |
+ void graham_scan(); |
+ void graham(); |
+public: |
+ std::vector<Point> boundary; |
+ //Point centroid; |
+ |
+ void merge(Point p); |
+ bool contains_point(Point p); |
+ |
+ inline Point operator[](int i) const { |
+ int l = boundary.size(); |
+ if(l == 0) return Point(); |
+ return boundary[i >= 0 ? i % l : (i % l) + l]; |
+ } |
+ |
+ /*inline Point &operator[](unsigned i) { |
+ int l = boundary.size(); |
+ if(l == 0) return Point(); |
+ return boundary[i >= 0 ? i % l : i % l + l]; |
+ }*/ |
+ |
+public: |
+ ConvexHull() {} |
+ ConvexHull(std::vector<Point> const & points) { |
+ boundary = points; |
+ graham(); |
+ } |
+ |
+ template <typename T> |
+ ConvexHull(T b, T e) :boundary(b,e) {} |
+ |
+public: |
+ /** Is the convex hull clockwise? We use the definition of clockwise from point.h |
+ **/ |
+ bool is_clockwise() const; |
+ bool no_colinear_points() const; |
+ bool top_point_first() const; |
+ bool meets_invariants() const; |
+ |
+ // contains no points |
+ bool empty() const { return boundary.empty();} |
+ |
+ // contains exactly one point |
+ bool singular() const { return boundary.size() == 1;} |
+ |
+ // all points are on a line |
+ bool linear() const { return boundary.size() == 2;} |
+ bool is_degenerate() const; |
+ |
+ // area of the convex hull |
+ double area() const; |
+ |
+ // furthest point in a direction (lg time) |
+ Point const * furthest(Point direction) const; |
+ |
+ bool is_left(Point p, int n); |
+ int find_left(Point p); |
+}; |
+ |
+// do two convex hulls intersect? |
+bool intersectp(ConvexHull a, ConvexHull b); |
+ |
+std::vector<Point> bridge_points(ConvexHull a, ConvexHull b); |
+ |
+// find the convex hull intersection |
+ConvexHull intersection(ConvexHull a, ConvexHull b); |
+ConvexHull sweepline_intersection(ConvexHull const &a, ConvexHull const &b); |
+ |
+// find the convex hull of a set of convex hulls |
+ConvexHull merge(ConvexHull a, ConvexHull b); |
+ |
+// naive approach |
+ConvexHull graham_merge(ConvexHull a, ConvexHull b); |
+ |
+unsigned find_bottom_right(ConvexHull const &a); |
+ |
+/*** Arbitrary transform operator. |
+ * Take a convex hull and apply an arbitrary convexity preserving transform. |
+ * we should be concerned about singular tranforms here. |
+ */ |
+template <class T> ConvexHull operator*(ConvexHull const &p, T const &m) { |
+ ConvexHull pr; |
+ |
+ pr.boundary.reserve(p.boundary.size()); |
+ |
+ for(unsigned i = 0; i < p.boundary.size(); i++) { |
+ pr.boundary.push_back(p.boundary[i]*m); |
+ } |
+ return pr; |
+} |
+ |
+//TODO: reinstate |
+/*class ConvexCover{ |
+public: |
+ Path const* path; |
+ std::vector<ConvexHull> cc; |
+ |
+ ConvexCover(Path const &sp); |
+};*/ |
+ |
+}; |
+ |
+#endif //2GEOM_CONVEX_COVER_H |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(substatement-open . 0)) |
+ indent-tabs-mode:nil |
+ c-brace-offset:0 |
+ fill-column:99 |
+ End: |
+ vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
+*/ |
+ |
Index: trunk/Scribus/scribus/third_party/lib2geom/svg-elliptical-arc.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/svg-elliptical-arc.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/svg-elliptical-arc.h (revision 14937) |
@@ -0,0 +1,393 @@ |
+/* |
+ * SVG Elliptical Path Support Class |
+ * |
+ * Copyright 2008 Marco Cecchetti <mrcekets at gmail.com> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ */ |
+ |
+ |
+#ifndef _SVG_ELLIPTICAL_ARC_H_ |
+#define _SVG_ELLIPTICAL_ARC_H_ |
+ |
+ |
+#include "angle.h" |
+#include "matrix.h" |
+#include "sbasis.h" |
+#include "d2.h" |
+ |
+ |
+namespace Geom |
+{ |
+ |
+class EllipticalArc |
+{ |
+ public: |
+ EllipticalArc( Point _initial_point, Point _final_point, |
+ double _rx, double _ry, |
+ bool _large_arc, bool _sweep, |
+ double _rot_angle = 0.0 |
+ ) |
+ : m_initial_point(_initial_point), m_final_point(_final_point), |
+ m_rx(_rx), m_ry(_ry), m_rot_angle(_rot_angle), |
+ m_large_arc(_large_arc), m_sweep(_sweep) |
+ { |
+ assert( (ray(X) >= 0) && (ray(Y) >= 0) ); |
+ if ( are_near(initialPoint(), finalPoint()) ) |
+ { |
+ m_start_angle = m_end_angle = 0; |
+ m_center = initialPoint(); |
+ } |
+ else |
+ { |
+ calculate_center_and_extreme_angles(); |
+ } |
+ |
+ std::cerr << "start_angle: " << decimal_round(rad_to_deg(m_start_angle),2) << " ( " << m_start_angle << " )" << std::endl |
+ << "end_angle: " << decimal_round(rad_to_deg(m_end_angle),2) << " ( " << m_end_angle << " )" << std::endl |
+ << "center: " << m_center << std::endl; |
+ } |
+ |
+ public: |
+ double center(Geom::Dim2 i) const |
+ { |
+ return m_center[i]; |
+ } |
+ |
+ Point center() const |
+ { |
+ return m_center; |
+ } |
+ |
+ Point initialPoint() const |
+ { |
+ return m_initial_point; |
+ } |
+ |
+ Point finalPoint() const |
+ { |
+ return m_final_point; |
+ } |
+ |
+ double start_angle() const |
+ { |
+ return m_start_angle; |
+ } |
+ |
+ double end_angle() const |
+ { |
+ return m_end_angle; |
+ } |
+ |
+ double ray(Geom::Dim2 i) const |
+ { |
+ return (i == 0) ? m_rx : m_ry; |
+ } |
+ |
+ bool large_arc_flag() const |
+ { |
+ return m_large_arc; |
+ } |
+ |
+// void large_arc_flag(bool v) |
+// { |
+// m_large_arc = v; |
+// } |
+ |
+ bool sweep_flag() const |
+ { |
+ return m_sweep; |
+ } |
+ |
+// void sweep_flag(bool v) |
+// { |
+// m_sweep = v; |
+// } |
+ |
+ double rotation_angle() const |
+ { |
+ return m_rot_angle; |
+ } |
+ |
+ void setInitial( const Point _point) |
+ { |
+ m_initial_point = _point; |
+ calculate_center_and_extreme_angles(); |
+ } |
+ |
+ void setFinal( const Point _point) |
+ { |
+ m_final_point = _point; |
+ calculate_center_and_extreme_angles(); |
+ } |
+ |
+ void setExtremes( const Point& _initial_point, const Point& _final_point ) |
+ { |
+ m_initial_point = _initial_point; |
+ m_final_point = _final_point; |
+ calculate_center_and_extreme_angles(); |
+ } |
+ |
+ bool isDegenerate() const |
+ { |
+ return are_near(initialPoint(), finalPoint()); |
+ } |
+ |
+ double valueAt(Coord t, Dim2 d) const |
+ { |
+ Coord tt = from_01_to_02PI(t); |
+ double sin_rot_angle = std::sin(rotation_angle()); |
+ double cos_rot_angle = std::cos(rotation_angle()); |
+ if ( d == X ) |
+ { |
+ return ray(X) * cos_rot_angle * std::cos(tt) |
+ - ray(Y) * sin_rot_angle * std::sin(tt) |
+ + center(X); |
+ } |
+ else |
+ { |
+ return ray(X) * sin_rot_angle * std::cos(tt) |
+ + ray(Y) * cos_rot_angle * std::sin(tt) |
+ + center(X); |
+ } |
+ } |
+ |
+ Point pointAt(Coord t) const |
+ { |
+ Coord tt = from_01_to_02PI(t); |
+ double sin_rot_angle = std::sin(rotation_angle()); |
+ double cos_rot_angle = std::cos(rotation_angle()); |
+ Matrix m( ray(X) * cos_rot_angle, ray(X) * sin_rot_angle, |
+ -ray(Y) * sin_rot_angle, ray(Y) * cos_rot_angle, |
+ center(X), center(Y) ); |
+ Point p( std::cos(tt), std::sin(tt) ); |
+ return p * m; |
+ } |
+ |
+ D2<SBasis> toSBasis() const |
+ { |
+ // the interval of parametrization has to be [0,1] |
+ Coord et = start_angle() + ( sweep_flag() ? sweep_angle() : -sweep_angle() ); |
+ Linear param(start_angle(), et); |
+ // std::cerr << "param : " << param << std::endl; |
+ Coord cos_rot_angle = std::cos(rotation_angle()); |
+ Coord sin_rot_angle = std::sin(rotation_angle()); |
+ // order = 4 seems to be enough to get perfect looking elliptical arc |
+ // should it be choosen in function of the arc length anyway ? |
+ // a user settable parameter: toSBasis(unsigned int order) ? |
+ SBasis arc_x = ray(X) * cos(param,4); |
+ SBasis arc_y = ray(Y) * sin(param,4); |
+ D2<SBasis> arc; |
+ arc[0] = arc_x * cos_rot_angle - arc_y * sin_rot_angle + Linear(center(X),center(X)); |
+ arc[1] = arc_x * sin_rot_angle + arc_y * cos_rot_angle + Linear(center(Y),center(Y)); |
+ return arc; |
+ } |
+ |
+ std::pair<EllipticalArc, EllipticalArc> |
+ subdivide(Coord t) const |
+ { |
+ EllipticalArc* arc1 = portion(0, t); |
+ EllipticalArc* arc2 = portion(t, 1); |
+ assert( arc1 != NULL && arc2 != NULL); |
+ std::pair<EllipticalArc, EllipticalArc> arc_pair(*arc1, *arc2); |
+ delete arc1; |
+ delete arc2; |
+ return arc_pair; |
+ } |
+ |
+ EllipticalArc* portion(double f, double t) const |
+ { |
+ static const double M_2PI = 2*M_PI; |
+ EllipticalArc* arc = new EllipticalArc( *this ); |
+ arc->m_initial_point = pointAt(f); |
+ arc->m_final_point = pointAt(t); |
+ //std::cerr << "initial point: " << arc->m_initial_point << std::endl; |
+ //std::cerr << "final point: " << arc->m_final_point << std::endl; |
+ double sa = sweep_angle(); |
+ //std::cerr << "sa: " << sa << std::endl; |
+ arc->m_start_angle = m_start_angle + sa * f; |
+ if ( arc->m_start_angle > M_2PI || are_near(arc->m_start_angle, M_2PI) ) |
+ arc->m_start_angle -= M_2PI; |
+ arc->m_end_angle = m_start_angle + sa * t; |
+ if ( arc->m_end_angle > M_2PI || are_near(arc->m_end_angle, M_2PI) ) |
+ arc->m_end_angle -= M_2PI; |
+ //std::cerr << "start angle: " << arc->m_start_angle << std::endl; |
+ //std::cerr << "end angle: " << arc->m_end_angle << std::endl; |
+ //std::cerr << "sweep angle: " << arc->sweep_angle() << std::endl; |
+ if (f > t) arc->m_sweep = !m_sweep; |
+ if ( m_large_arc && (arc->sweep_angle() < M_PI) ) |
+ arc->m_large_arc = false; |
+ return arc; |
+ } |
+ |
+ // the arc is the same but traversed in the opposite direction |
+ EllipticalArc* reverse() const |
+ { |
+ EllipticalArc* rarc = new EllipticalArc( *this ); |
+ rarc->m_sweep = !m_sweep; |
+ rarc->m_initial_point = m_final_point; |
+ rarc->m_final_point = m_initial_point; |
+ rarc->m_start_angle = m_end_angle; |
+ rarc->m_end_angle = m_start_angle; |
+ return rarc; |
+ } |
+ |
+ private: |
+ |
+ double sweep_angle() const |
+ { |
+ Coord d = end_angle() - start_angle(); |
+ if ( !sweep_flag() ) d = -d; |
+ if ( d < 0 || are_near(d, 0) ) |
+ d += 2*M_PI; |
+ return d; |
+ } |
+ |
+ Coord from_01_to_02PI(Coord t) const |
+ { |
+ if ( sweep_flag() ) |
+ { |
+ Coord angle = start_angle() + sweep_angle() * t; |
+ if ( (angle > 2*M_PI) || are_near(angle, 2*M_PI) ) |
+ angle -= 2*M_PI; |
+ return angle; |
+ } |
+ else |
+ { |
+ Coord angle = start_angle() - sweep_angle() * t; |
+ if ( angle < 0 ) angle += 2*M_PI; |
+ return angle; |
+ } |
+ } |
+ |
+ // NOTE: doesn't work with 360 deg arcs |
+ void calculate_center_and_extreme_angles() |
+ { |
+ const double M_HALF_PI = M_PI/2; |
+ const double M_2PI = 2*M_PI; |
+ |
+ double sin_rot_angle = std::sin(rotation_angle()); |
+ double cos_rot_angle = std::cos(rotation_angle()); |
+ |
+ Point sp = sweep_flag() ? initialPoint() : finalPoint(); |
+ Point ep = sweep_flag() ? finalPoint() : initialPoint(); |
+ |
+ Matrix m( ray(X) * cos_rot_angle, ray(X) * sin_rot_angle, |
+ -ray(Y) * sin_rot_angle, ray(Y) * cos_rot_angle, |
+ 0, 0 ); |
+ Matrix im = m.inverse(); |
+ Point sol = (ep - sp) * im; |
+ std::cerr << "sol : " << sol << std::endl; |
+ double half_sum_angle = std::atan2(-sol[X], sol[Y]); |
+ double half_diff_angle; |
+ if ( are_near(std::fabs(half_sum_angle), M_HALF_PI) ) |
+ { |
+ double anti_sgn_hsa = (half_sum_angle > 0) ? -1 : 1; |
+ double arg = anti_sgn_hsa * sol[X] / 2; |
+ // if |arg| is a little bit > 1 acos returns nan |
+ if ( are_near(arg, 1) ) |
+ half_diff_angle = 0; |
+ else if ( are_near(arg, -1) ) |
+ half_diff_angle = M_PI; |
+ else |
+ { |
+ assert( -1 < arg && arg < 1 ); |
+ // if it fails => there is no ellipse that satisfies the given constraints |
+ half_diff_angle = std::acos( arg ); |
+ } |
+ |
+ half_diff_angle = M_HALF_PI - half_diff_angle; |
+ } |
+ else |
+ { |
+ double arg = sol[Y] / ( 2 * std::cos(half_sum_angle) ); |
+ // if |arg| is a little bit > 1 asin returns nan |
+ if ( are_near(arg, 1) ) |
+ half_diff_angle = M_HALF_PI; |
+ else if ( are_near(arg, -1) ) |
+ half_diff_angle = -M_HALF_PI; |
+ else |
+ { |
+ assert( -1 < arg && arg < 1 ); |
+ // if it fails => there is no ellipse that satisfies the given constraints |
+ half_diff_angle = std::asin( arg ); |
+ } |
+ } |
+ std::cerr << "half_sum_angle : " << decimal_round(rad_to_deg(half_sum_angle),2) << " ( " << half_sum_angle << " )" << std::endl; |
+ std::cerr << "half_diff_angle : " << decimal_round(rad_to_deg(half_diff_angle),2) << " ( " << half_diff_angle << " )" << std::endl; |
+ //std::cerr << "cos(half_sum_angle) : " << std::cos(half_sum_angle) << std::endl; |
+ //std::cerr << "sol[Y] / ( 2 * std::cos(half_sum_angle) ) : " << sol[Y] / ( 2 * std::cos(half_sum_angle) ) << std::endl; |
+ |
+ if ( ( m_large_arc && half_diff_angle > 0 ) |
+ || (!m_large_arc && half_diff_angle < 0 ) ) |
+ { |
+ half_diff_angle = -half_diff_angle; |
+ } |
+ if ( half_sum_angle < 0 ) half_sum_angle += M_2PI; |
+ if ( half_diff_angle < 0 ) half_diff_angle += M_PI; |
+ std::cerr << "half_sum_angle : " << decimal_round(rad_to_deg(half_sum_angle),2) << " ( " << half_sum_angle << " )" << std::endl; |
+ std::cerr << "half_diff_angle : " << decimal_round(rad_to_deg(half_diff_angle),2) << " ( " << half_diff_angle << " )" << std::endl; |
+ |
+ m_start_angle = half_sum_angle - half_diff_angle; |
+ m_end_angle = half_sum_angle + half_diff_angle; |
+ // 0 <= m_start_angle, m_end_angle < 2PI |
+ if ( m_start_angle < 0 ) m_start_angle += M_2PI; |
+ if ( m_end_angle > M_2PI || are_near(m_end_angle, M_2PI) ) m_end_angle -= M_2PI; |
+ sol[0] = std::cos(m_start_angle); |
+ sol[1] = std::sin(m_start_angle); |
+ m_center = sp - sol * m; |
+ if ( !sweep_flag() ) |
+ { |
+ double angle = m_start_angle; |
+ m_start_angle = m_end_angle; |
+ m_end_angle = angle; |
+ } |
+ } |
+ |
+ private: |
+ Point m_initial_point, m_final_point; |
+ double m_rx, m_ry, m_rot_angle; |
+ bool m_large_arc, m_sweep; |
+ |
+ double m_start_angle, m_end_angle; |
+ Point m_center; |
+}; |
+ |
+ |
+} |
+ |
+ |
+#endif /*_SVG_ELLIPTICAL_ARC_H_*/ |
+ |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/geom.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/geom.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/geom.cpp (revision 14937) |
@@ -0,0 +1,222 @@ |
+/** |
+ * \file src/geom.cpp |
+ * \brief Various geometrical calculations. |
+ */ |
+ |
+// #ifdef HAVE_CONFIG_H |
+// # include <config.h> |
+// #endif |
+#include "geom.h" |
+#include "point.h" |
+ |
+namespace Geom { |
+ |
+/** |
+ * Finds the intersection of the two (infinite) lines |
+ * defined by the points p such that dot(n0, p) == d0 and dot(n1, p) == d1. |
+ * |
+ * If the two lines intersect, then \a result becomes their point of |
+ * intersection; otherwise, \a result remains unchanged. |
+ * |
+ * This function finds the intersection of the two lines (infinite) |
+ * defined by n0.X = d0 and x1.X = d1. The algorithm is as follows: |
+ * To compute the intersection point use kramer's rule: |
+ * \verbatim |
+ * convert lines to form |
+ * ax + by = c |
+ * dx + ey = f |
+ * |
+ * ( |
+ * e.g. a = (x2 - x1), b = (y2 - y1), c = (x2 - x1)*x1 + (y2 - y1)*y1 |
+ * ) |
+ * |
+ * In our case we use: |
+ * a = n0.x d = n1.x |
+ * b = n0.y e = n1.y |
+ * c = d0 f = d1 |
+ * |
+ * so: |
+ * |
+ * adx + bdy = cd |
+ * adx + aey = af |
+ * |
+ * bdy - aey = cd - af |
+ * (bd - ae)y = cd - af |
+ * |
+ * y = (cd - af)/(bd - ae) |
+ * |
+ * repeat for x and you get: |
+ * |
+ * x = (fb - ce)/(bd - ae) \endverbatim |
+ * |
+ * If the denominator (bd-ae) is 0 then the lines are parallel, if the |
+ * numerators are then 0 then the lines coincide. |
+ * |
+ * \todo Why not use existing but outcommented code below |
+ * (HAVE_NEW_INTERSECTOR_CODE)? |
+ */ |
+IntersectorKind |
+line_intersection(Geom::Point const &n0, double const d0, |
+ Geom::Point const &n1, double const d1, |
+ Geom::Point &result) |
+{ |
+ double denominator = dot(Geom::rot90(n0), n1); |
+ double X = n1[Geom::Y] * d0 - |
+ n0[Geom::Y] * d1; |
+ /* X = (-d1, d0) dot (n0[Y], n1[Y]) */ |
+ |
+ if (denominator == 0) { |
+ if ( X == 0 ) { |
+ return coincident; |
+ } else { |
+ return parallel; |
+ } |
+ } |
+ |
+ double Y = n0[Geom::X] * d1 - |
+ n1[Geom::X] * d0; |
+ |
+ result = Geom::Point(X, Y) / denominator; |
+ |
+ return intersects; |
+} |
+ |
+ |
+ |
+ |
+/* ccw exists as a building block */ |
+int |
+intersector_ccw(const Geom::Point& p0, const Geom::Point& p1, |
+ const Geom::Point& p2) |
+/* Determine which way a set of three points winds. */ |
+{ |
+ Geom::Point d1 = p1 - p0; |
+ Geom::Point d2 = p2 - p0; |
+ /* compare slopes but avoid division operation */ |
+ double c = dot(Geom::rot90(d1), d2); |
+ if(c > 0) |
+ return +1; // ccw - do these match def'n in header? |
+ if(c < 0) |
+ return -1; // cw |
+ |
+ /* Colinear [or NaN]. Decide the order. */ |
+ if ( ( d1[0] * d2[0] < 0 ) || |
+ ( d1[1] * d2[1] < 0 ) ) { |
+ return -1; // p2 < p0 < p1 |
+ } else if ( dot(d1,d1) < dot(d2,d2) ) { |
+ return +1; // p0 <= p1 < p2 |
+ } else { |
+ return 0; // p0 <= p2 <= p1 |
+ } |
+} |
+ |
+/** Determine whether two line segments intersect. This doesn't find |
+ the point of intersection, use the line_intersect function above, |
+ or the segment_intersection interface below. |
+ |
+ \pre neither segment is zero-length; i.e. p00 != p01 and p10 != p11. |
+*/ |
+static bool |
+segment_intersectp(Geom::Point const &p00, Geom::Point const &p01, |
+ Geom::Point const &p10, Geom::Point const &p11) |
+{ |
+ if(p00 == p01) return false; |
+ if(p10 == p11) return false; |
+ |
+ /* true iff ( (the p1 segment straddles the p0 infinite line) |
+ * and (the p0 segment straddles the p1 infinite line) ). */ |
+ return ((intersector_ccw(p00,p01, p10) |
+ *intersector_ccw(p00, p01, p11)) <=0 ) |
+ && |
+ ((intersector_ccw(p10,p11, p00) |
+ *intersector_ccw(p10, p11, p01)) <=0 ); |
+} |
+ |
+ |
+/** Determine whether \& where two line segments intersect. |
+ |
+If the two segments don't intersect, then \a result remains unchanged. |
+ |
+\pre neither segment is zero-length; i.e. p00 != p01 and p10 != p11. |
+**/ |
+IntersectorKind |
+segment_intersect(Geom::Point const &p00, Geom::Point const &p01, |
+ Geom::Point const &p10, Geom::Point const &p11, |
+ Geom::Point &result) |
+{ |
+ if(segment_intersectp(p00, p01, p10, p11)) { |
+ Geom::Point n0 = (p01 - p00).ccw(); |
+ double d0 = dot(n0,p00); |
+ |
+ Geom::Point n1 = (p11 - p10).ccw(); |
+ double d1 = dot(n1,p10); |
+ return line_intersection(n0, d0, n1, d1, result); |
+ } else { |
+ return no_intersection; |
+ } |
+} |
+ |
+/** Determine whether \& where two line segments intersect. |
+ |
+If the two segments don't intersect, then \a result remains unchanged. |
+ |
+\pre neither segment is zero-length; i.e. p00 != p01 and p10 != p11. |
+**/ |
+IntersectorKind |
+line_twopoint_intersect(Geom::Point const &p00, Geom::Point const &p01, |
+ Geom::Point const &p10, Geom::Point const &p11, |
+ Geom::Point &result) |
+{ |
+ Geom::Point n0 = (p01 - p00).ccw(); |
+ double d0 = dot(n0,p00); |
+ |
+ Geom::Point n1 = (p11 - p10).ccw(); |
+ double d1 = dot(n1,p10); |
+ return line_intersection(n0, d0, n1, d1, result); |
+} |
+ |
+/** |
+ * polyCentroid: Calculates the centroid (xCentroid, yCentroid) and area of a polygon, given its |
+ * vertices (x[0], y[0]) ... (x[n-1], y[n-1]). It is assumed that the contour is closed, i.e., that |
+ * the vertex following (x[n-1], y[n-1]) is (x[0], y[0]). The algebraic sign of the area is |
+ * positive for counterclockwise ordering of vertices in x-y plane; otherwise negative. |
+ |
+ * Returned values: |
+ 0 for normal execution; |
+ 1 if the polygon is degenerate (number of vertices < 3); |
+ 2 if area = 0 (and the centroid is undefined). |
+ |
+ * for now we require the path to be a polyline and assume it is closed. |
+**/ |
+ |
+int centroid(std::vector<Geom::Point> p, Geom::Point& centroid, double &area) { |
+ const unsigned n = p.size(); |
+ if (n < 3) |
+ return 1; |
+ Geom::Point centroid_tmp(0,0); |
+ double atmp = 0; |
+ for (unsigned i = n-1, j = 0; j < n; i = j, j++) { |
+ const double ai = -cross(p[j], p[i]); |
+ atmp += ai; |
+ centroid_tmp += (p[j] + p[i])*ai; // first moment. |
+ } |
+ area = atmp / 2; |
+ if (atmp != 0) { |
+ centroid = centroid_tmp / (3 * atmp); |
+ return 0; |
+ } |
+ return 2; |
+} |
+ |
+} |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/d2-sbasis.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/d2-sbasis.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/d2-sbasis.cpp (revision 14937) |
@@ -0,0 +1,132 @@ |
+#include "d2.h" |
+/* One would think that we would include d2-sbasis.h, however, |
+ * you cannot actually include it in anything - only d2 may import it. |
+ * This is due to the trickinesses of template submatching. */ |
+ |
+namespace Geom { |
+ |
+SBasis L2(D2<SBasis> const & a, unsigned k) { return sqrt(dot(a, a), k); } |
+ |
+D2<SBasis> multiply(Linear const & a, D2<SBasis> const & b) { |
+ return D2<SBasis>(multiply(a, b[X]), multiply(a, b[Y])); |
+} |
+ |
+D2<SBasis> multiply(SBasis const & a, D2<SBasis> const & b) { |
+ return D2<SBasis>(multiply(a, b[X]), multiply(a, b[Y])); |
+} |
+ |
+D2<SBasis> truncate(D2<SBasis> const & a, unsigned terms) { |
+ return D2<SBasis>(truncate(a[X], terms), truncate(a[Y], terms)); |
+} |
+ |
+unsigned sbasis_size(D2<SBasis> const & a) { |
+ return std::max((unsigned) a[0].size(), (unsigned) a[1].size()); |
+} |
+ |
+//TODO: Is this sensical? shouldn't it be like pythagorean or something? |
+double tail_error(D2<SBasis> const & a, unsigned tail) { |
+ return std::max(a[0].tailError(tail), a[1].tailError(tail)); |
+} |
+ |
+Piecewise<D2<SBasis> > sectionize(D2<Piecewise<SBasis> > const &a) { |
+ Piecewise<SBasis> x = partition(a[0], a[1].cuts), y = partition(a[1], a[0].cuts); |
+ assert(x.size() == y.size()); |
+ Piecewise<D2<SBasis> > ret; |
+ for(unsigned i = 0; i < x.size(); i++) |
+ ret.push_seg(D2<SBasis>(x[i], y[i])); |
+ ret.cuts.insert(ret.cuts.end(), x.cuts.begin(), x.cuts.end()); |
+ return ret; |
+} |
+ |
+D2<Piecewise<SBasis> > make_cuts_independant(Piecewise<D2<SBasis> > const &a) { |
+ D2<Piecewise<SBasis> > ret; |
+ for(unsigned d = 0; d < 2; d++) { |
+ for(unsigned i = 0; i < a.size(); i++) |
+ ret[d].push_seg(a[i][d]); |
+ ret[d].cuts.insert(ret[d].cuts.end(), a.cuts.begin(), a.cuts.end()); |
+ } |
+ return ret; |
+} |
+ |
+Piecewise<D2<SBasis> > rot90(Piecewise<D2<SBasis> > const &M){ |
+ Piecewise<D2<SBasis> > result; |
+ if (M.empty()) return M; |
+ result.push_cut(M.cuts[0]); |
+ for (unsigned i=0; i<M.size(); i++){ |
+ result.push(rot90(M[i]),M.cuts[i+1]); |
+ } |
+ return result; |
+} |
+ |
+Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, |
+ Piecewise<D2<SBasis> > const &b){ |
+ Piecewise<SBasis > result; |
+ if (a.empty() || b.empty()) return result; |
+ Piecewise<D2<SBasis> > aa = partition(a,b.cuts); |
+ Piecewise<D2<SBasis> > bb = partition(b,a.cuts); |
+ |
+ result.push_cut(aa.cuts.front()); |
+ for (unsigned i=0; i<aa.size(); i++){ |
+ result.push(dot(aa.segs[i],bb.segs[i]),aa.cuts[i+1]); |
+ } |
+ return result; |
+} |
+ |
+Piecewise<SBasis> cross(Piecewise<D2<SBasis> > const &a, |
+ Piecewise<D2<SBasis> > const &b){ |
+ Piecewise<SBasis > result; |
+ if (a.empty() || b.empty()) return result; |
+ Piecewise<D2<SBasis> > aa = partition(a,b.cuts); |
+ Piecewise<D2<SBasis> > bb = partition(b,a.cuts); |
+ |
+ result.push_cut(aa.cuts.front()); |
+ for (unsigned i=0; i<a.size(); i++){ |
+ result.push(cross(aa.segs[i],bb.segs[i]),aa.cuts[i+1]); |
+ } |
+ return result; |
+} |
+ |
+Piecewise<D2<SBasis> > operator*(Piecewise<D2<SBasis> > const &a, Matrix const &m) { |
+ Piecewise<D2<SBasis> > result; |
+ if(a.empty()) return result; |
+ result.push_cut(a.cuts[0]); |
+ for (unsigned i = 0; i < a.size(); i++) { |
+ result.push(a[i] * m, a.cuts[i+1]); |
+ } |
+ return result; |
+} |
+ |
+//if tol>0, only force continuity where the jump is smaller than tol. |
+Piecewise<D2<SBasis> > force_continuity(Piecewise<D2<SBasis> > const &f, |
+ double tol, |
+ bool closed){ |
+ if (f.size()==0) return f; |
+ Piecewise<D2<SBasis> > result=f; |
+ unsigned cur = (closed)? 0:1; |
+ unsigned prev = (closed)? f.size()-1:0; |
+ while(cur<f.size()){ |
+ Point pt0 = f.segs[prev].at1(); |
+ Point pt1 = f.segs[cur ].at0(); |
+ if (tol<=0 || L2sq(pt0-pt1)<tol*tol){ |
+ pt0 = (pt0+pt1)/2; |
+ for (unsigned dim=0; dim<2; dim++){ |
+ SBasis &prev_sb=result.segs[prev][dim]; |
+ SBasis &cur_sb =result.segs[cur][dim]; |
+ Coord const c=pt0[dim]; |
+ if (prev_sb.empty()) { |
+ prev_sb.push_back(Linear(0.0, c)); |
+ } else { |
+ prev_sb[0][1] = c; |
+ } |
+ if (cur_sb.empty()) { |
+ cur_sb.push_back(Linear(c, 0.0)); |
+ } else { |
+ cur_sb[0][0] = c; |
+ } |
+ } |
+ } |
+ prev = cur++; |
+ } |
+ return result; |
+} |
+} |
Index: trunk/Scribus/scribus/third_party/lib2geom/poly.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/poly.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/poly.h (revision 14937) |
@@ -0,0 +1,222 @@ |
+#ifndef SEEN_POLY_H |
+#define SEEN_POLY_H |
+#include <assert.h> |
+#include <vector> |
+#include <iostream> |
+#include <algorithm> |
+#include <complex> |
+#include "utils.h" |
+ |
+class Poly : public std::vector<double>{ |
+public: |
+ // coeff; // sum x^i*coeff[i] |
+ |
+ //unsigned size() const { return coeff.size();} |
+ unsigned degree() const { return size()-1;} |
+ |
+ //double operator[](const int i) const { return (*this)[i];} |
+ //double& operator[](const int i) { return (*this)[i];} |
+ |
+ Poly operator+(const Poly& p) const { |
+ Poly result; |
+ const unsigned out_size = std::max(size(), p.size()); |
+ const unsigned min_size = std::min(size(), p.size()); |
+ //result.reserve(out_size); |
+ |
+ for(unsigned i = 0; i < min_size; i++) { |
+ result.push_back((*this)[i] + p[i]); |
+ } |
+ for(unsigned i = min_size; i < size(); i++) |
+ result.push_back((*this)[i]); |
+ for(unsigned i = min_size; i < p.size(); i++) |
+ result.push_back(p[i]); |
+ assert(result.size() == out_size); |
+ return result; |
+ } |
+ Poly operator-(const Poly& p) const { |
+ Poly result; |
+ const unsigned out_size = std::max(size(), p.size()); |
+ const unsigned min_size = std::min(size(), p.size()); |
+ result.reserve(out_size); |
+ |
+ for(unsigned i = 0; i < min_size; i++) { |
+ result.push_back((*this)[i] - p[i]); |
+ } |
+ for(unsigned i = min_size; i < size(); i++) |
+ result.push_back((*this)[i]); |
+ for(unsigned i = min_size; i < p.size(); i++) |
+ result.push_back(-p[i]); |
+ assert(result.size() == out_size); |
+ return result; |
+ } |
+ Poly operator-=(const Poly& p) { |
+ const unsigned out_size = std::max(size(), p.size()); |
+ const unsigned min_size = std::min(size(), p.size()); |
+ resize(out_size); |
+ |
+ for(unsigned i = 0; i < min_size; i++) { |
+ (*this)[i] -= p[i]; |
+ } |
+ for(unsigned i = min_size; i < out_size; i++) |
+ (*this)[i] = -p[i]; |
+ return *this; |
+ } |
+ Poly operator-(const double k) const { |
+ Poly result; |
+ const unsigned out_size = size(); |
+ result.reserve(out_size); |
+ |
+ for(unsigned i = 0; i < out_size; i++) { |
+ result.push_back((*this)[i]); |
+ } |
+ result[0] -= k; |
+ return result; |
+ } |
+ Poly operator-() const { |
+ Poly result; |
+ result.resize(size()); |
+ |
+ for(unsigned i = 0; i < size(); i++) { |
+ result[i] = -(*this)[i]; |
+ } |
+ return result; |
+ } |
+ Poly operator*(const double p) const { |
+ Poly result; |
+ const unsigned out_size = size(); |
+ result.reserve(out_size); |
+ |
+ for(unsigned i = 0; i < out_size; i++) { |
+ result.push_back((*this)[i]*p); |
+ } |
+ assert(result.size() == out_size); |
+ return result; |
+ } |
+// equivalent to multiply by x^terms, discard negative terms |
+ Poly shifted(unsigned terms) const { |
+ Poly result; |
+ // This was a no-op and breaks the build on x86_64, as it's trying |
+ // to take maximum of 32-bit and 64-bit integers |
+ //const unsigned out_size = std::max(unsigned(0), size()+terms); |
+ const size_type out_size = size() + terms; |
+ result.reserve(out_size); |
+ |
+ if(terms < 0) { |
+ for(unsigned i = 0; i < out_size; i++) { |
+ result.push_back((*this)[i-terms]); |
+ } |
+ } else { |
+ for(unsigned i = 0; i < terms; i++) { |
+ result.push_back(0.0); |
+ } |
+ for(unsigned i = 0; i < size(); i++) { |
+ result.push_back((*this)[i]); |
+ } |
+ } |
+ |
+ assert(result.size() == out_size); |
+ return result; |
+ } |
+ Poly operator*(const Poly& p) const; |
+ |
+ template <typename T> |
+ T eval(T x) const { |
+ T r = 0; |
+ for(int k = size()-1; k >= 0; k--) { |
+ r = r*x + T((*this)[k]); |
+ } |
+ return r; |
+ } |
+ |
+ template <typename T> |
+ T operator()(T t) const { return (T)eval(t);} |
+ |
+ void normalize(); |
+ |
+ void monicify(); |
+ Poly() {} |
+ Poly(const Poly& p) : std::vector<double>(p) {} |
+ Poly(const double a) {push_back(a);} |
+ |
+public: |
+ template <class T, class U> |
+ void val_and_deriv(T x, U &pd) const { |
+ pd[0] = back(); |
+ int nc = size() - 1; |
+ int nd = pd.size() - 1; |
+ for(unsigned j = 1; j < pd.size(); j++) |
+ pd[j] = 0.0; |
+ for(int i = nc -1; i >= 0; i--) { |
+ int nnd = std::min(nd, nc-i); |
+ for(int j = nnd; j >= 1; j--) |
+ pd[j] = pd[j]*x + operator[](i); |
+ pd[0] = pd[0]*x + operator[](i); |
+ } |
+ double cnst = 1; |
+ for(int i = 2; i <= nd; i++) { |
+ cnst *= i; |
+ pd[i] *= cnst; |
+ } |
+ } |
+ |
+ static Poly linear(double ax, double b) { |
+ Poly p; |
+ p.push_back(b); |
+ p.push_back(ax); |
+ return p; |
+ } |
+}; |
+ |
+inline Poly operator*(double a, Poly const & b) { return b * a;} |
+ |
+Poly integral(Poly const & p); |
+Poly derivative(Poly const & p); |
+Poly divide_out_root(Poly const & p, double x); |
+Poly compose(Poly const & a, Poly const & b); |
+Poly divide(Poly const &a, Poly const &b, Poly &r); |
+Poly gcd(Poly const &a, Poly const &b, const double tol=1e-10); |
+ |
+/*** solve(Poly p) |
+ * find all p.degree() roots of p. |
+ * This function can take a long time with suitably crafted polynomials, but in practice it should be fast. Should we provide special forms for degree() <= 4? |
+ */ |
+std::vector<std::complex<double> > solve(const Poly & p); |
+ |
+/*** solve_reals(Poly p) |
+ * find all real solutions to Poly p. |
+ * currently we just use solve and pick out the suitably real looking values, there may be a better algorithm. |
+ */ |
+std::vector<double> solve_reals(const Poly & p); |
+double polish_root(Poly const & p, double guess, double tol); |
+ |
+inline std::ostream &operator<< (std::ostream &out_file, const Poly &in_poly) { |
+ if(in_poly.size() == 0) |
+ out_file << "0"; |
+ else { |
+ for(int i = (int)in_poly.size()-1; i >= 0; --i) { |
+ if(i == 1) { |
+ out_file << "" << in_poly[i] << "*x"; |
+ out_file << " + "; |
+ } else if(i) { |
+ out_file << "" << in_poly[i] << "*x^" << i; |
+ out_file << " + "; |
+ } else |
+ out_file << in_poly[i]; |
+ |
+ } |
+ } |
+ return out_file; |
+} |
+ |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/region.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/region.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/region.h (revision 14937) |
@@ -0,0 +1,85 @@ |
+#ifndef __2GEOM_REGION_H |
+#define __2GEOM_REGION_H |
+ |
+#include "path.h" |
+#include "path-intersection.h" |
+ |
+namespace Geom { |
+ |
+class Shape; |
+ |
+class Region { |
+ friend Crossings crossings(Region const &a, Region const &b); |
+ friend class Shape; |
+ friend Shape shape_boolean(bool rev, Shape const & a, Shape const & b, CrossingSet const & crs); |
+ |
+ Path boundary; |
+ mutable boost::optional<Rect> box; |
+ bool fill; |
+ public: |
+ Region() : fill(true) {} |
+ explicit Region(Path const &p) : boundary(p) { fill = path_direction(p); } |
+ Region(Path const &p, bool dir) : boundary(p), fill(dir) {} |
+ Region(Path const &p, boost::optional<Rect> const &b) : boundary(p), box(b) { fill = path_direction(p); } |
+ Region(Path const &p, boost::optional<Rect> const &b, bool dir) : boundary(p), box(b), fill(dir) {} |
+ |
+ unsigned size() const { return boundary.size(); } |
+ |
+ bool isFill() const { return fill; } |
+ Region asFill() const { if(fill) return Region(*this); else return inverse(); } |
+ Region asHole() const { if(fill) return inverse(); else return Region(*this); } |
+ |
+ operator Path() const { return boundary; } |
+ Rect boundsFast() const { |
+ if(!box) box = boost::optional<Rect>(boundary.boundsFast()); |
+ return *box; |
+ } |
+ |
+ bool contains(Point const &p) const { |
+ if(box && !box->contains(p)) return false; |
+ return Geom::contains(boundary, p); |
+ } |
+ bool contains(Region const &other) const { return contains(other.boundary.initialPoint()); } |
+ |
+ bool includes(Point const &p) const { |
+ return logical_xor(!fill, contains(p)); |
+ } |
+ |
+ Region inverse() const { return Region(boundary.reverse(), box, !fill); } |
+ |
+ Region operator*(Matrix const &m) const; |
+ |
+ bool invariants() const; |
+}; |
+ |
+typedef std::vector<Region> Regions; |
+ |
+unsigned outer_index(Regions const &ps); |
+ |
+//assumes they're already sanitized somewhat |
+inline Regions regions_from_paths(std::vector<Path> const &ps) { |
+ Regions res; |
+ for(unsigned i = 0; i < ps.size(); i++) |
+ res.push_back(Region(ps[i])); |
+ return res; |
+} |
+ |
+inline std::vector<Path> paths_from_regions(Regions const &rs) { |
+ std::vector<Path> res; |
+ for(unsigned i = 0; i < rs.size(); i++) |
+ res.push_back(rs[i]); |
+ return res; |
+} |
+ |
+Regions sanitize_path(Path const &p); |
+ |
+Regions region_boolean(bool rev, Region const & a, Region const & b, Crossings const &cr); |
+Regions region_boolean(bool rev, Region const & a, Region const & b, Crossings const & cr_a, Crossings const & cr_b); |
+ |
+inline Regions region_boolean(bool rev, Region const & a, Region const & b) { |
+ return region_boolean(rev, a, b, crossings(a, b)); |
+} |
+ |
+} |
+ |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/svg-path.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/svg-path.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/svg-path.cpp (revision 14937) |
@@ -0,0 +1,97 @@ |
+/* |
+ * callback interface for SVG path data |
+ * |
+ * Copyright 2007 MenTaLguY <mental@rydia.net> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#include "sbasis-to-bezier.h" |
+#include "svg-path.h" |
+ |
+namespace Geom { |
+ |
+void output(Curve const &curve, SVGPathSink &sink) { |
+ std::vector<Point> pts = sbasis_to_bezier(curve.toSBasis(), 2); //TODO: use something better! |
+ sink.curveTo(pts[0], pts[1], pts[2]); |
+} |
+ |
+void output(LineSegment const &curve, SVGPathSink &sink) { |
+ sink.lineTo(curve[1]); |
+} |
+ |
+void output(CubicBezier const &curve, SVGPathSink &sink) { |
+ sink.curveTo(curve[1], curve[2], curve[3]); |
+} |
+ |
+void output(QuadraticBezier const &curve, SVGPathSink &sink) { |
+ sink.quadTo(curve[1], curve[2]); |
+} |
+ |
+void output(SVGEllipticalArc const &curve, SVGPathSink &sink) { |
+ // FIXME |
+} |
+ |
+template <typename T> |
+bool output_as(Curve const &curve, SVGPathSink &sink) { |
+ T const *t = dynamic_cast<T const *>(&curve); |
+ if (t) { |
+ output(*t, sink); |
+ return true; |
+ } else { |
+ return false; |
+ } |
+} |
+ |
+void output_svg_path(Path &path, SVGPathSink &sink) { |
+ sink.moveTo(path.front().initialPoint()); |
+ |
+ Path::iterator iter; |
+ for ( iter = path.begin() ; iter != path.end() ; ++iter ) { |
+ output_as<LineSegment>(*iter, sink) || |
+ output_as<CubicBezier>(*iter, sink) || |
+ output_as<QuadraticBezier>(*iter, sink) || |
+ output_as<SVGEllipticalArc>(*iter, sink) || |
+ output_as<Curve>(*iter, sink); |
+ } |
+ |
+ if (path.closed()) { |
+ sink.closePath(); |
+ } |
+ sink.finish(); |
+} |
+ |
+} |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/ord.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/ord.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/ord.h (revision 14937) |
@@ -0,0 +1,37 @@ |
+ |
+#ifndef __2GEOM_ORD__ |
+#define __2GEOM_ORD__ |
+ |
+namespace { |
+ |
+enum Cmp { |
+ LESS_THAN=-1, |
+ GREATER_THAN=1, |
+ EQUAL_TO=0 |
+}; |
+ |
+inline Cmp operator-(Cmp x) { |
+ switch(x) { |
+ case LESS_THAN: |
+ return GREATER_THAN; |
+ case GREATER_THAN: |
+ return LESS_THAN; |
+ case EQUAL_TO: |
+ return EQUAL_TO; |
+ } |
+} |
+ |
+template <typename T1, typename T2> |
+inline Cmp cmp(T1 const &a, T2 const &b) { |
+ if ( a < b ) { |
+ return LESS_THAN; |
+ } else if ( b < a ) { |
+ return GREATER_THAN; |
+ } else { |
+ return EQUAL_TO; |
+ } |
+} |
+ |
+} |
+ |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/crossing.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/crossing.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/crossing.h (revision 14937) |
@@ -0,0 +1,146 @@ |
+#ifndef __GEOM_CROSSING_H |
+#define __GEOM_CROSSING_H |
+ |
+#include <vector> |
+#include "rect.h" |
+#include "sweep.h" |
+ |
+namespace Geom { |
+ |
+//Crossing between one or two paths |
+struct Crossing { |
+ bool dir; //True: along a, a becomes outside. |
+ double ta, tb; //time on a and b of crossing |
+ unsigned a, b; //storage of indices |
+ Crossing() : dir(false), ta(0), tb(1), a(0), b(1) {} |
+ Crossing(double t_a, double t_b, bool direction) : dir(direction), ta(t_a), tb(t_b), a(0), b(1) {} |
+ Crossing(double t_a, double t_b, unsigned ai, unsigned bi, bool direction) : dir(direction), ta(t_a), tb(t_b), a(ai), b(bi) {} |
+ bool operator==(const Crossing & other) const { return a == other.a && b == other.b && dir == other.dir && ta == other.ta && tb == other.tb; } |
+ bool operator!=(const Crossing & other) const { return !(*this == other); } |
+ |
+ unsigned getOther(unsigned cur) const { return a == cur ? b : a; } |
+ double getTime(unsigned cur) const { return a == cur ? ta : tb; } |
+ double getOtherTime(unsigned cur) const { return a == cur ? tb : ta; } |
+ bool onIx(unsigned ix) const { return a == ix || b == ix; } |
+}; |
+ |
+ |
+struct Edge { |
+ unsigned node, path; |
+ double time; |
+ bool reverse; |
+ Edge(unsigned p, double t, bool r) : path(p), time(t), reverse(r) {} |
+ bool operator==(Edge const &other) const { return other.path == path && other.time == time && other.reverse == reverse; } |
+}; |
+ |
+struct CrossingNode { |
+ std::vector<Edge> edges; |
+ CrossingNode() : edges(std::vector<Edge>()) {} |
+ explicit CrossingNode(std::vector<Edge> es) : edges(es) {} |
+ void add_edge(Edge const &e) { |
+ if(std::find(edges.begin(), edges.end(), e) == edges.end()) |
+ edges.push_back(e); |
+ } |
+ double time_on(unsigned p) { |
+ for(unsigned i = 0; i < edges.size(); i++) |
+ if(edges[i].path == p) return edges[i].time; |
+ std::cout << "CrossingNode time_on failed\n"; |
+ return 0; |
+ } |
+}; |
+ |
+typedef std::vector<Crossing> Crossings; |
+ |
+typedef std::vector<CrossingNode> CrossingGraph; |
+ |
+struct TimeOrder { |
+ bool operator()(Edge a, Edge b) { |
+ return a.time < b.time; |
+ } |
+}; |
+ |
+class Path; |
+CrossingGraph create_crossing_graph(std::vector<Path> const &p, Crossings const &crs); |
+ |
+/*inline bool are_near(Crossing a, Crossing b) { |
+ return are_near(a.ta, b.ta) && are_near(a.tb, b.tb); |
+} |
+ |
+struct NearF { bool operator()(Crossing a, Crossing b) { return are_near(a, b); } }; |
+*/ |
+ |
+struct CrossingOrder { |
+ unsigned ix; |
+ CrossingOrder(unsigned i) : ix(i) {} |
+ bool operator()(Crossing a, Crossing b) { |
+ return (ix == a.a ? a.ta : a.tb) < |
+ (ix == b.a ? b.ta : b.tb); |
+ } |
+}; |
+ |
+ |
+typedef std::vector<Crossings> CrossingSet; |
+ |
+template<typename C> |
+std::vector<Rect> bounds(C const &a) { |
+ std::vector<Rect> rs; |
+ for(unsigned i = 0; i < a.size(); i++) rs.push_back(a[i].boundsFast()); |
+ return rs; |
+} |
+ |
+inline void sort_crossings(Crossings &cr, unsigned ix) { std::sort(cr.begin(), cr.end(), CrossingOrder(ix)); } |
+ |
+template<typename T> |
+struct Crosser { |
+ virtual ~Crosser() {} |
+ virtual Crossings crossings(T const &a, T const &b) { return crossings(std::vector<T>(1,a), std::vector<T>(1,b))[0]; } |
+ virtual CrossingSet crossings(std::vector<T> const &a, std::vector<T> const &b) { |
+ CrossingSet results(a.size() + b.size(), Crossings()); |
+ |
+ std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds(a), bounds(b)); |
+ for(unsigned i = 0; i < cull.size(); i++) { |
+ for(unsigned jx = 0; jx < cull[i].size(); jx++) { |
+ unsigned j = cull[i][jx]; |
+ unsigned jc = j + a.size(); |
+ Crossings cr = crossings(a[i], b[j]); |
+ for(unsigned k = 0; k < cr.size(); k++) { cr[k].a = i; cr[k].b = jc; } |
+ |
+ //Sort & add A-sorted crossings |
+ sort_crossings(cr, i); |
+ Crossings n(results[i].size() + cr.size()); |
+ std::merge(results[i].begin(), results[i].end(), cr.begin(), cr.end(), n.begin(), CrossingOrder(i)); |
+ results[i] = n; |
+ |
+ //Sort & add B-sorted crossings |
+ sort_crossings(cr, jc); |
+ n.resize(results[jc].size() + cr.size()); |
+ std::merge(results[jc].begin(), results[jc].end(), cr.begin(), cr.end(), n.begin(), CrossingOrder(jc)); |
+ results[jc] = n; |
+ } |
+ } |
+ return results; |
+ } |
+}; |
+void merge_crossings(Crossings &a, Crossings &b, unsigned i); |
+void offset_crossings(Crossings &cr, double a, double b); |
+ |
+Crossings reverse_ta(Crossings const &cr, std::vector<double> max); |
+Crossings reverse_tb(Crossings const &cr, unsigned split, std::vector<double> max); |
+CrossingSet reverse_ta(CrossingSet const &cr, unsigned split, std::vector<double> max); |
+CrossingSet reverse_tb(CrossingSet const &cr, unsigned split, std::vector<double> max); |
+ |
+void clean(Crossings &cr_a, Crossings &cr_b); |
+ |
+} |
+ |
+#endif |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/conjugate_gradient.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/conjugate_gradient.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/conjugate_gradient.h (revision 14937) |
@@ -0,0 +1,46 @@ |
+/* |
+ * conjugate_gradient.h |
+ * |
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#ifndef _CONJUGATE_GRADIENT_H |
+#define _CONJUGATE_GRADIENT_H |
+ |
+#include <valarray> |
+ |
+double |
+inner(std::valarray<double> const &x, |
+ std::valarray<double> const &y); |
+ |
+void |
+conjugate_gradient(std::valarray<double> const &A, |
+ std::valarray<double> &x, |
+ std::valarray<double> const &b, |
+ unsigned n, double tol, |
+ unsigned max_iterations, bool ortho1); |
+#endif // _CONJUGATE_GRADIENT_H |
Index: trunk/Scribus/scribus/third_party/lib2geom/rect.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/rect.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/rect.h (revision 14937) |
@@ -0,0 +1,169 @@ |
+/* |
+ * rect.h - D2<Interval> specialization to Rect |
+ * |
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+/* Authors of original rect class: |
+ * Lauris Kaplinski <lauris@kaplinski.com> |
+ * Nathan Hurst <njh@mail.csse.monash.edu.au> |
+ * bulia byak <buliabyak@users.sf.net> |
+ * MenTaLguY <mental@rydia.net> |
+ */ |
+ |
+#ifdef _2GEOM_D2 /*This is intentional: we don't actually want anyone to |
+ include this, other than D2.h. If somone else tries, D2 |
+ won't be defined. If it is, this will already be included. */ |
+#ifndef _2GEOM_RECT |
+#define _2GEOM_RECT |
+ |
+#include "matrix.h" |
+#include <boost/optional/optional.hpp> |
+ |
+namespace Geom { |
+ |
+typedef D2<Interval> Rect; |
+ |
+Rect unify(const Rect &, const Rect &); |
+ |
+template<> |
+class D2<Interval> { |
+ private: |
+ Interval f[2]; |
+ public: |
+ D2<Interval>() { f[X] = f[Y] = Interval(0, 0); } |
+ |
+ D2<Interval>(Interval const &a, Interval const &b) { |
+ f[X] = a; |
+ f[Y] = b; |
+ } |
+ |
+ D2<Interval>(Point const & a, Point const & b) { |
+ f[X] = Interval(a[X], b[X]); |
+ f[Y] = Interval(a[Y], b[Y]); |
+ } |
+ |
+ inline Interval& operator[](unsigned i) { return f[i]; } |
+ inline Interval const & operator[](unsigned i) const { return f[i]; } |
+ |
+ inline Point min() const { return Point(f[X].min(), f[Y].min()); } |
+ inline Point max() const { return Point(f[X].max(), f[Y].max()); } |
+ |
+ /** returns the four corners of the rectangle in positive order |
+ * (clockwise if +Y is up, anticlockwise if +Y is down) */ |
+ Point corner(unsigned i) const { |
+ switch(i % 4) { |
+ case 0: return Point(f[X].min(), f[Y].min()); |
+ case 1: return Point(f[X].max(), f[Y].min()); |
+ case 2: return Point(f[X].max(), f[Y].max()); |
+ default: return Point(f[X].min(), f[Y].max()); |
+ } |
+ } |
+ |
+ //We should probably remove these - they're coord sys gnostic |
+ inline double top() const { return f[Y].min(); } |
+ inline double bottom() const { return f[Y].max(); } |
+ inline double left() const { return f[X].min(); } |
+ inline double right() const { return f[X].max(); } |
+ |
+ inline double width() const { return f[X].extent(); } |
+ inline double height() const { return f[Y].extent(); } |
+ |
+ /** returns a vector from min to max. */ |
+ inline Point dimensions() const { return Point(f[X].extent(), f[Y].extent()); } |
+ inline Point midpoint() const { return Point(f[X].middle(), f[Y].middle()); } |
+ |
+ inline double area() const { return f[X].extent() * f[Y].extent(); } |
+ inline double maxExtent() const { return std::max(f[X].extent(), f[Y].extent()); } |
+ |
+ inline bool isEmpty() const { |
+ return f[X].isEmpty() && f[Y].isEmpty(); |
+ } |
+ inline bool intersects(Rect const &r) const { |
+ return f[X].intersects(r[X]) && f[Y].intersects(r[Y]); |
+ } |
+ inline bool contains(Rect const &r) const { |
+ return f[X].contains(r[X]) && f[Y].contains(r[Y]); |
+ } |
+ inline bool contains(Point const &p) const { |
+ return f[X].contains(p[X]) && f[Y].contains(p[Y]); |
+ } |
+ |
+ inline void expandTo(Point p) { |
+ f[X].extendTo(p[X]); f[Y].extendTo(p[Y]); |
+ } |
+ inline void unionWith(Rect const &b) { |
+ f[X].unionWith(b[X]); f[Y].unionWith(b[Y]); |
+ } |
+ |
+ inline void expandBy(double amnt) { |
+ f[X].expandBy(amnt); f[Y].expandBy(amnt); |
+ } |
+ inline void expandBy(Point const p) { |
+ f[X].expandBy(p[X]); f[Y].expandBy(p[Y]); |
+ } |
+ |
+ /** Transforms the rect by m. Note that it gives correct results only for scales and translates, |
+ in the case of rotations, the area of the rect will grow as it cannot rotate. */ |
+ inline Rect operator*(Matrix const m) const { |
+ return unify(Rect(corner(0) * m, corner(2) * m), |
+ Rect(corner(1) * m, corner(3) * m)); |
+ } |
+}; |
+ |
+inline Rect unify(Rect const & a, Rect const & b) { |
+ return Rect(unify(a[X], b[X]), unify(a[Y], b[Y])); |
+} |
+ |
+inline Rect union_list(std::vector<Rect> const &r) { |
+ if(r.empty()) return Rect(Interval(0,0), Interval(0,0)); |
+ Rect ret = r[0]; |
+ for(unsigned i = 1; i < r.size(); i++) |
+ ret.unionWith(r[i]); |
+ return ret; |
+} |
+ |
+inline boost::optional<Rect> intersect(Rect const & a, Rect const & b) { |
+ boost::optional<Interval> x = intersect(a[X], b[X]); |
+ boost::optional<Interval> y = intersect(a[Y], b[Y]); |
+ return x && y ? boost::optional<Rect>(Rect(*x, *y)) : boost::optional<Rect>(); |
+} |
+ |
+} |
+ |
+#endif //_2GEOM_RECT |
+#endif //_2GEOM_D2 |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis-geometric.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis-geometric.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis-geometric.h (revision 14937) |
@@ -0,0 +1,83 @@ |
+#ifndef _SBASIS_GEOMETRIC |
+#define _SBASIS_GEOMETRIC |
+#include "d2.h" |
+#include "piecewise.h" |
+#include <vector> |
+ |
+/** two-dimensional geometric operators. |
+ * Copyright 2007, JFBarraud |
+ * Copyright 2007, njh |
+ * |
+ * These operators are built on a more 'polynomially robust' |
+ * transformation to map a function that takes a [0,1] parameter to a |
+ * 2d vector into a function that takes the same [0,1] parameter to a |
+ * unit vector with the same direction. |
+ * |
+ * Rather that using (X/sqrt(X))(t) which involves two unstable |
+ * operations, sqrt and divide, this approach forms a curve directly |
+ * from the various tangent directions at each end (angular jet). As |
+ * a result, the final path has a convergence behaviour derived from |
+ * that of the sin and cos series. -- njh |
+ */ |
+ |
+namespace Geom{ |
+ |
+Piecewise<D2<SBasis> > |
+cutAtRoots(Piecewise<D2<SBasis> > const &M, double tol=1e-4); |
+ |
+Piecewise<SBasis> |
+atan2(D2<SBasis> const &vect, |
+ double tol=.01, unsigned order=3); |
+ |
+Piecewise<SBasis> |
+atan2(Piecewise<D2<SBasis> >const &vect, |
+ double tol=.01, unsigned order=3); |
+ |
+Piecewise<D2<SBasis> > |
+unitVector(D2<SBasis> const &vect, |
+ double tol=.01, unsigned order=3); |
+Piecewise<D2<SBasis> > |
+unitVector(Piecewise<D2<SBasis> > const &vect, |
+ double tol=.01, unsigned order=3); |
+ |
+// Piecewise<D2<SBasis> > |
+// uniform_speed(D2<SBasis> const M, |
+// double tol=.1); |
+ |
+Piecewise<SBasis> curvature( D2<SBasis> const &M, double tol=.01); |
+Piecewise<SBasis> curvature(Piecewise<D2<SBasis> > const &M, double tol=.01); |
+ |
+Piecewise<SBasis> arcLengthSb( D2<SBasis> const &M, double tol=.01); |
+Piecewise<SBasis> arcLengthSb(Piecewise<D2<SBasis> > const &M, double tol=.01); |
+ |
+double length( D2<SBasis> const &M, double tol=.01); |
+double length(Piecewise<D2<SBasis> > const &M, double tol=.01); |
+ |
+Piecewise<D2<SBasis> > |
+arc_length_parametrization(D2<SBasis> const &M, |
+ unsigned order=3, |
+ double tol=.01); |
+Piecewise<D2<SBasis> > |
+arc_length_parametrization(Piecewise<D2<SBasis> > const &M, |
+ unsigned order=3, |
+ double tol=.01); |
+ |
+ |
+unsigned centroid(Piecewise<D2<SBasis> > const &p, Point& centroid, double &area); |
+ |
+}; |
+ |
+#endif |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
+ |
+ |
Index: trunk/Scribus/scribus/third_party/lib2geom/shape.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/shape.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/shape.h (revision 14937) |
@@ -0,0 +1,102 @@ |
+#ifndef __2GEOM_SHAPE_H |
+#define __2GEOM_SHAPE_H |
+ |
+#include <vector> |
+#include <set> |
+ |
+#include "region.h" |
+ |
+//TODO: BBOX optimizations |
+ |
+namespace Geom { |
+ |
+enum { |
+ BOOLOP_JUST_A = 1, |
+ BOOLOP_JUST_B = 2, |
+ BOOLOP_BOTH = 4, |
+ BOOLOP_NEITHER = 8 |
+}; |
+ |
+enum { |
+ BOOLOP_NULL = 0, |
+ BOOLOP_INTERSECT = BOOLOP_BOTH, |
+ BOOLOP_SUBTRACT_A_B = BOOLOP_JUST_B, |
+ BOOLOP_IDENTITY_A = BOOLOP_JUST_A | BOOLOP_BOTH, |
+ BOOLOP_SUBTRACT_B_A = BOOLOP_JUST_A, |
+ BOOLOP_IDENTITY_B = BOOLOP_JUST_B | BOOLOP_BOTH, |
+ BOOLOP_EXCLUSION = BOOLOP_JUST_A | BOOLOP_JUST_B, |
+ BOOLOP_UNION = BOOLOP_JUST_A | BOOLOP_JUST_B | BOOLOP_BOTH |
+}; |
+ |
+class Shape { |
+ Regions content; |
+ mutable bool fill; |
+ //friend Shape shape_region_boolean(bool rev, Shape const & a, Region const & b); |
+ friend CrossingSet crossings_between(Shape const &a, Shape const &b); |
+ friend Shape shape_boolean(bool rev, Shape const &, Shape const &, CrossingSet const &); |
+ friend Shape boolop(Shape const &a, Shape const &b, unsigned); |
+ friend Shape boolop(Shape const &a, Shape const &b, unsigned, CrossingSet const &); |
+ friend void add_to_shape(Shape &s, Path const &p, bool); |
+ public: |
+ Shape() : fill(true) {} |
+ explicit Shape(Region const & r) { |
+ content = Regions(1, r); |
+ fill = r.fill; |
+ } |
+ explicit Shape(Regions const & r) : content(r) { update_fill(); } |
+ explicit Shape(bool f) : fill(f) {} |
+ Shape(Regions const & r, bool f) : content(r), fill(f) {} |
+ |
+ Regions getContent() const { return content; } |
+ bool isFill() const { return fill; } |
+ |
+ unsigned size() const { return content.size(); } |
+ const Region &operator[](unsigned ix) const { return content[ix]; } |
+ |
+ Shape inverse() const; |
+ Shape operator*(Matrix const &m) const; |
+ |
+ bool contains(Point const &p) const; |
+ |
+ bool inside_invariants() const; //semi-slow & easy to violate : checks that the insides are inside, the outsides are outside |
+ bool region_invariants() const; //semi-slow : checks for self crossing |
+ bool cross_invariants() const; //slow : checks that everything is disjoint |
+ bool invariants() const; //vera slow (combo, checks the above) |
+ |
+ private: |
+ std::vector<unsigned> containment_list(Point p) const; |
+ void update_fill() const { |
+ unsigned ix = outer_index(content); |
+ if(ix < size()) |
+ fill = content[ix].fill; |
+ else if(size() > 0) |
+ fill = content.front().fill; |
+ else |
+ fill = true; |
+ } |
+}; |
+ |
+inline CrossingSet crossings_between(Shape const &a, Shape const &b) { return crossings(paths_from_regions(a.content), paths_from_regions(b.content)); } |
+ |
+Shape shape_boolean(bool rev, Shape const &, Shape const &, CrossingSet const &); |
+Shape shape_boolean(bool rev, Shape const &, Shape const &); |
+ |
+//unsigned pick_coincident(unsigned ix, unsigned jx, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs); |
+//void outer_crossing(unsigned &ix, unsigned &jx, bool & dir, std::vector<Path> const & ps, CrossingSet const & crs); |
+void crossing_dual(unsigned &i, unsigned &j, CrossingSet const & crs); |
+unsigned crossing_along(double t, unsigned ix, unsigned jx, bool dir, Crossings const & crs); |
+ |
+Shape boolop(Shape const &, Shape const &, unsigned flags); |
+Shape boolop(Shape const &, Shape const &, unsigned flags, CrossingSet &); |
+ |
+Shape sanitize(std::vector<Path> const &ps); |
+ |
+Shape stopgap_cleaner(std::vector<Path> const &ps); |
+ |
+inline std::vector<Path> desanitize(Shape const & s) { |
+ return paths_from_regions(s.getContent()); |
+} |
+ |
+} |
+ |
+#endif |
Index: trunk/Scribus/scribus/third_party/lib2geom/sbasis-poly.cpp |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/sbasis-poly.cpp (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/sbasis-poly.cpp (revision 14937) |
@@ -0,0 +1,47 @@ |
+#include "sbasis-poly.h" |
+ |
+namespace Geom{ |
+ |
+SBasis poly_to_sbasis(Poly const & p) { |
+ SBasis x = Linear(0, 1); |
+ SBasis r; |
+ |
+ for(int i = p.size()-1; i >= 0; i--) { |
+ r = SBasis(Linear(p[i], p[i])) + multiply(x, r); |
+ } |
+ r.normalize(); |
+ return r; |
+ |
+} |
+ |
+Poly sbasis_to_poly(SBasis const & sb) { |
+ if(sb.isZero()) |
+ return Poly(); |
+ Poly S; // (1-x)x = -1*x^2 + 1*x + 0 |
+ Poly A, B; |
+ B.push_back(0); |
+ B.push_back(1); |
+ A.push_back(1); |
+ A.push_back(-1); |
+ S = A*B; |
+ Poly r; |
+ |
+ for(int i = sb.size()-1; i >= 0; i--) { |
+ r = S*r + sb[i][0]*A + sb[i][1]*B; |
+ } |
+ r.normalize(); |
+ return r; |
+} |
+ |
+}; |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/matrix.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/matrix.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/matrix.h (revision 14937) |
@@ -0,0 +1,161 @@ |
+#ifndef __Geom_MATRIX_H__ |
+#define __Geom_MATRIX_H__ |
+ |
+/** \file |
+ * Definition of Geom::Matrix types. |
+ * |
+ * Main authors: |
+ * Lauris Kaplinski <lauris@kaplinski.com>: |
+ * Original NRMatrix definition and related macros. |
+ * |
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>: |
+ * Geom::Matrix class version of the above. |
+ * |
+ * Michael G. Sloan <mgsloan@gmail.com>: |
+ * Reorganization and additions. |
+ * |
+ * This code is in public domain. |
+ */ |
+ |
+//#include <glib/gmessages.h> |
+ |
+#include "point.h" |
+ |
+namespace Geom { |
+ |
+/** |
+ * The Matrix class. |
+ * |
+ * For purposes of multiplication, points should be thought of as row vectors |
+ * |
+ * \f$(p_X p_Y 1)\f$ |
+ * |
+ * to be right-multiplied by transformation matrices of the form |
+ * \f[ |
+ \left[ |
+ \begin{array}{ccc} |
+ c_0&c_1&0 \\ |
+ c_2&c_3&0 \\ |
+ c_4&c_5&1 |
+ \end{array} |
+ \right] |
+ \f] |
+ * (so the columns of the matrix correspond to the columns (elements) of the result, |
+ * and the rows of the matrix correspond to columns (elements) of the "input"). |
+ */ |
+class Matrix { |
+ private: |
+ Coord _c[6]; |
+ public: |
+ Matrix() {} |
+ |
+ Matrix(Matrix const &m) { |
+ for(int i = 0; i < 6; i++) { |
+ _c[i] = m[i]; |
+ } |
+ } |
+ |
+ Matrix(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5) { |
+ _c[0] = c0; _c[1] = c1; |
+ _c[2] = c2; _c[3] = c3; |
+ _c[4] = c4; _c[5] = c5; |
+ } |
+ |
+ Matrix &operator=(Matrix const &m) { |
+ for(int i = 0; i < 6; i++) |
+ _c[i] = m._c[i]; |
+ return *this; |
+ } |
+ |
+ inline Coord operator[](unsigned const i) const { return _c[i]; } |
+ inline Coord &operator[](unsigned const i) { return _c[i]; } |
+ |
+ |
+ Point xAxis() const; |
+ Point yAxis() const; |
+ Point translation() const; |
+ void setXAxis(Point const &vec); |
+ void setYAxis(Point const &vec); |
+ void setTranslation(Point const &loc); |
+ |
+ double expansionX() const; |
+ double expansionY() const; |
+ void setExpansionX(double val); |
+ void setExpansionY(double val); |
+ |
+ void setIdentity(); |
+ |
+ bool isIdentity(Coord eps = EPSILON) const; |
+ bool isTranslation(Coord eps = EPSILON) const; |
+ bool isRotation(double eps = EPSILON) const; |
+ bool isScale(double eps = EPSILON) const; |
+ bool isUniformScale(double eps = EPSILON) const; |
+ bool onlyScaleAndTranslation(double eps = EPSILON) const; |
+ |
+ bool flips() const; |
+ |
+ Matrix without_translation() const; |
+ |
+ Matrix inverse() const; |
+ |
+ Coord det() const; |
+ Coord descrim2() const; |
+ Coord descrim() const; |
+}; |
+ |
+Matrix operator*(Matrix const &a, Matrix const &b); |
+ |
+/** A function to print out the Matrix (for debugging) */ |
+inline std::ostream &operator<< (std::ostream &out_file, const Geom::Matrix &m) { |
+ out_file << "A: " << m[0] << " C: " << m[2] << " E: " << m[4] << "\n"; |
+ out_file << "B: " << m[1] << " D: " << m[3] << " F: " << m[5] << "\n"; |
+ return out_file; |
+} |
+ |
+/** Given a matrix m such that unit_circle = m*x, this returns the |
+ * quadratic form x*A*x = 1. */ |
+Matrix elliptic_quadratic_form(Matrix const &m); |
+ |
+/** Given a matrix (ignoring the translation) this returns the eigen |
+ * values and vectors. */ |
+class Eigen{ |
+public: |
+ Point vectors[2]; |
+ double values[2]; |
+ Eigen(Matrix const &m); |
+}; |
+ |
+// Matrix factories |
+Matrix from_basis(const Point x_basis, const Point y_basis, const Point offset=Point(0,0)); |
+ |
+/** Returns the Identity Matrix. */ |
+inline Matrix identity() { |
+ return Matrix(1.0, 0.0, |
+ 0.0, 1.0, |
+ 0.0, 0.0); |
+} |
+ |
+inline bool operator==(Matrix const &a, Matrix const &b) { |
+ for(unsigned i = 0; i < 6; ++i) { |
+ if ( a[i] != b[i] ) return false; |
+ } |
+ return true; |
+} |
+inline bool operator!=(Matrix const &a, Matrix const &b) { return !( a == b ); } |
+ |
+ |
+ |
+} /* namespace Geom */ |
+ |
+#endif /* !__Geom_MATRIX_H__ */ |
+ |
+/* |
+ Local Variables: |
+ mode:c++ |
+ c-file-style:"stroustrup" |
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
+ indent-tabs-mode:nil |
+ fill-column:99 |
+ End: |
+*/ |
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
Index: trunk/Scribus/scribus/third_party/lib2geom/d2.h |
=================================================================== |
--- trunk/Scribus/scribus/third_party/lib2geom/d2.h (nonexistent) |
+++ trunk/Scribus/scribus/third_party/lib2geom/d2.h (revision 14937) |
@@ -0,0 +1,409 @@ |
+/* |
+ * d2.h - Lifts one dimensional objects into 2d |
+ * |
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com> |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it either under the terms of the GNU Lesser General Public |
+ * License version 2.1 as published by the Free Software Foundation |
+ * (the "LGPL") or, at your option, under the terms of the Mozilla |
+ * Public License Version 1.1 (the "MPL"). If you do not alter this |
+ * notice, a recipient may use your version of this file under either |
+ * the MPL or the LGPL. |
+ * |
+ * You should have received a copy of the LGPL along with this library |
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * You should have received a copy of the MPL along with this library |
+ * in the file COPYING-MPL-1.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License |
+ * Version 1.1 (the "License"); you may not use this file except in |
+ * compliance with the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
+ * the specific language governing rights and limitations. |
+ * |
+ */ |
+ |
+#ifndef _2GEOM_D2 //If this is change, change the guard in rect.h as well. |
+#define _2GEOM_D2 |
+ |
+#include "point.h" |
+#include "interval.h" |
+#include "matrix.h" |
+ |
+#include <boost/concept_check.hpp> |
+#include "concepts.h" |
+ |
+namespace Geom{ |
+ |
+template <class T> |
+class D2{ |
+ //BOOST_CLASS_REQUIRE(T, boost, AssignableConcept); |
+ private: |
+ T f[2]; |
+ |
+ public: |
+ D2() {f[X] = f[Y] = T();} |
+ explicit D2(Point const &a) { |
+ f[X] = T(a[X]); f[Y] = T(a[Y]); |
+ } |
+ |
+ D2(T const &a, T const &b) { |
+ f[X] = a; |
+ f[Y] = b; |
+ } |
+ |
+ //TODO: ask mental about operator= as seen in Point |
+ |
+ T& operator[](unsigned i) { return f[i]; } |
+ T const & operator[](unsigned i) const { return f[i]; } |
+ |
+ //IMPL: FragmentConcept |
+ typedef Point output_type; |
+ bool isZero() const { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ return f[X].isZero() && f[Y].isZero(); |
+ } |
+ bool isConstant() const { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ return f[X].isConstant() && f[Y].isConstant(); |
+ } |
+ bool isFinite() const { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ return f[X].isFinite() && f[Y].isFinite(); |
+ } |
+ Point at0() const { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ return Point(f[X].at0(), f[Y].at0()); |
+ } |
+ Point at1() const { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ return Point(f[X].at1(), f[Y].at1()); |
+ } |
+ Point valueAt(double t) const { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ return (*this)(t); |
+ } |
+ std::vector<Point > valueAndDerivatives(double t, unsigned count) const { |
+ std::vector<Coord> x = f[X].valueAndDerivatives(t, count), |
+ y = f[Y].valueAndDerivatives(t, count); |
+ std::vector<Point> res; |
+ for(unsigned i = 0; i < count; i++) { |
+ res.push_back(Point(x[i], y[i])); |
+ } |
+ return res; |
+ } |
+ D2<SBasis> toSBasis() const { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ return D2<SBasis>(f[X].toSBasis(), f[Y].toSBasis()); |
+ } |
+ |
+ Point operator()(double t) const; |
+ Point operator()(double x, double y) const; |
+}; |
+template <typename T> |
+inline D2<T> reverse(const D2<T> &a) { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ return D2<T>(reverse(a[X]), reverse(a[Y])); |
+} |
+ |
+template <typename T> |
+inline D2<T> portion(const D2<T> &a, Coord f, Coord t) { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ return D2<T>(portion(a[X], f, t), portion(a[Y], f, t)); |
+} |
+ |
+//IMPL: boost::EqualityComparableConcept |
+template <typename T> |
+inline bool |
+operator==(D2<T> const &a, D2<T> const &b) { |
+ boost::function_requires<boost::EqualityComparableConcept<T> >(); |
+ return a[0]==b[0] && a[1]==b[1]; |
+} |
+template <typename T> |
+inline bool |
+operator!=(D2<T> const &a, D2<T> const &b) { |
+ boost::function_requires<boost::EqualityComparableConcept<T> >(); |
+ return a[0]!=b[0] || a[1]!=b[1]; |
+} |
+ |
+//IMPL: NearConcept |
+template <typename T> |
+inline bool |
+are_near(D2<T> const &a, D2<T> const &b, double tol) { |
+ boost::function_requires<NearConcept<T> >(); |
+ return are_near(a[0], b[0]) && are_near(a[1], b[1]); |
+} |
+ |
+//IMPL: AddableConcept |
+template <typename T> |
+inline D2<T> |
+operator+(D2<T> const &a, D2<T> const &b) { |
+ boost::function_requires<AddableConcept<T> >(); |
+ |
+ D2<T> r; |
+ for(unsigned i = 0; i < 2; i++) |
+ r[i] = a[i] + b[i]; |
+ return r; |
+} |
+template <typename T> |
+inline D2<T> |
+operator-(D2<T> const &a, D2<T> const &b) { |
+ boost::function_requires<AddableConcept<T> >(); |
+ |
+ D2<T> r; |
+ for(unsigned i = 0; i < 2; i++) |
+ r[i] = a[i] - b[i]; |
+ return r; |
+} |
+template <typename T> |
+inline D2<T> |
+operator+=(D2<T> &a, D2<T> const &b) { |
+ boost::function_requires<AddableConcept<T> >(); |
+ |
+ for(unsigned i = 0; i < 2; i++) |
+ a[i] += b[i]; |
+ return a; |
+} |
+template <typename T> |
+inline D2<T> |
+operator-=(D2<T> &a, D2<T> const & b) { |
+ boost::function_requires<AddableConcept<T> >(); |
+ |
+ for(unsigned i = 0; i < 2; i++) |
+ a[i] -= b[i]; |
+ return a; |
+} |
+ |
+//IMPL: ScalableConcept |
+template <typename T> |
+inline D2<T> |
+operator-(D2<T> const & a) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ D2<T> r; |
+ for(unsigned i = 0; i < 2; i++) |
+ r[i] = -a[i]; |
+ return r; |
+} |
+template <typename T> |
+inline D2<T> |
+operator*(D2<T> const & a, Point const & b) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ |
+ D2<T> r; |
+ for(unsigned i = 0; i < 2; i++) |
+ r[i] = a[i] * b[i]; |
+ return r; |
+} |
+template <typename T> |
+inline D2<T> |
+operator/(D2<T> const & a, Point const & b) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ //TODO: b==0? |
+ D2<T> r; |
+ for(unsigned i = 0; i < 2; i++) |
+ r[i] = a[i] / b[i]; |
+ return r; |
+} |
+template <typename T> |
+inline D2<T> |
+operator*=(D2<T> &a, Point const & b) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ |
+ for(unsigned i = 0; i < 2; i++) |
+ a[i] *= b[i]; |
+ return a; |
+} |
+template <typename T> |
+inline D2<T> |
+operator/=(D2<T> &a, Point const & b) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ //TODO: b==0? |
+ for(unsigned i = 0; i < 2; i++) |
+ a[i] /= b[i]; |
+ return a; |
+} |
+ |
+template <typename T> |
+inline D2<T> operator*(D2<T> const & a, double b) { return D2<T>(a[0]*b, a[1]*b); } |
+template <typename T> |
+inline D2<T> operator*=(D2<T> & a, double b) { a[0] *= b; a[1] *= b; return a; } |
+template <typename T> |
+inline D2<T> operator/(D2<T> const & a, double b) { return D2<T>(a[0]/b, a[1]/b); } |
+template <typename T> |
+inline D2<T> operator/=(D2<T> & a, double b) { a[0] /= b; a[1] /= b; return a; } |
+ |
+template<typename T> |
+D2<T> operator*(D2<T> const &v, Matrix const &m) { |
+ boost::function_requires<AddableConcept<T> >(); |
+ boost::function_requires<ScalableConcept<T> >(); |
+ D2<T> ret; |
+ for(unsigned i = 0; i < 2; i++) |
+ ret[i] = v[X] * m[i] + v[Y] * m[i + 2] + m[i + 4]; |
+ return ret; |
+} |
+ |
+//IMPL: OffsetableConcept |
+template <typename T> |
+inline D2<T> |
+operator+(D2<T> const & a, Point b) { |
+ boost::function_requires<OffsetableConcept<T> >(); |
+ D2<T> r; |
+ for(unsigned i = 0; i < 2; i++) |
+ r[i] = a[i] + b[i]; |
+ return r; |
+} |
+template <typename T> |
+inline D2<T> |
+operator-(D2<T> const & a, Point b) { |
+ boost::function_requires<OffsetableConcept<T> >(); |
+ D2<T> r; |
+ for(unsigned i = 0; i < 2; i++) |
+ r[i] = a[i] - b[i]; |
+ return r; |
+} |
+template <typename T> |
+inline D2<T> |
+operator+=(D2<T> & a, Point b) { |
+ boost::function_requires<OffsetableConcept<T> >(); |
+ for(unsigned i = 0; i < 2; i++) |
+ a[i] += b[i]; |
+ return a; |
+} |
+template <typename T> |
+inline D2<T> |
+operator-=(D2<T> & a, Point b) { |
+ boost::function_requires<OffsetableConcept<T> >(); |
+ for(unsigned i = 0; i < 2; i++) |
+ a[i] -= b[i]; |
+ return a; |
+} |
+ |
+template <typename T> |
+inline T |
+dot(D2<T> const & a, D2<T> const & b) { |
+ boost::function_requires<AddableConcept<T> >(); |
+ boost::function_requires<MultiplicableConcept<T> >(); |
+ |
+ T r; |
+ for(unsigned i = 0; i < 2; i++) |
+ r += a[i] * b[i]; |
+ return r; |
+} |
+ |
+template <typename T> |
+inline T |
+cross(D2<T> const & a, D2<T> const & b) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ boost::function_requires<MultiplicableConcept<T> >(); |
+ |
+ return a[1] * b[0] - a[0] * b[1]; |
+} |
+ |
+ |
+//equivalent to cw/ccw, for use in situations where rotation direction doesn't matter. |
+template <typename T> |
+inline D2<T> |
+rot90(D2<T> const & a) { |
+ boost::function_requires<ScalableConcept<T> >(); |
+ return D2<T>(-a[Y], a[X]); |
+} |
+ |
+//TODO: concepterize the following functions |
+template <typename T> |
+inline D2<T> |
+compose(D2<T> const & a, T const & b) { |
+ D2<T> r; |
+ for(unsigned i = 0; i < 2; i++) |
+ r[i] = compose(a[i],b); |
+ return r; |
+} |
+ |
+template <typename T> |
+inline D2<T> |
+compose_each(D2<T> const & a, D2<T> const & b) { |
+ D2<T> r; |
+ for(unsigned i = 0; i < 2; i++) |
+ r[i] = compose(a[i],b[i]); |
+ return r; |
+} |
+ |
+template <typename T> |
+inline D2<T> |
+compose_each(T const & a, D2<T> const & b) { |
+ D2<T> r; |
+ for(unsigned i = 0; i < 2; i++) |
+ r[i] = compose(a,b[i]); |
+ return r; |
+} |
+ |
+ |
+template<typename T> |
+inline Point |
+D2<T>::operator()(double t) const { |
+ Point p; |
+ for(unsigned i = 0; i < 2; i++) |
+ p[i] = (*this)[i](t); |
+ return p; |
+} |
+ |
+//TODO: we might want to have this take a Point as the parameter. |
+template<typename T> |
+inline Point |
+D2<T>::operator()(double x, double y) const { |
+ Point p; |
+ for(unsigned i = 0; i < 2; i++) |
+ p[i] = (*this)[i](x, y); |
+ return p; |
+} |
+ |
+ |
+template<typename T> |
+D2<T> derivative(D2<T> const & a) { |
+ return D2<T>(derivative(a[X]), derivative(a[Y])); |
+} |
+template<typename T> |
+D2<T> integral(D2<T> const & a) { |
+ return D2<T>(integral(a[X]), integral(a[Y])); |
+} |
+ |
+} //end namespace Geom |
+ |
+#include "rect.h" |
+#include "d2-sbasis.h" |
+ |
+namespace Geom{ |
+ |
+//Some D2 Fragment implementation which requires rect: |
+template <typename T> |
+Rect bounds_fast(const D2<T> &a) { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ return Rect(bounds_fast(a[X]), bounds_fast(a[Y])); |
+} |
+template <typename T> |
+Rect bounds_exact(const D2<T> &a) { |
+ boost::function_requires<FragmentConcept<T> >(); |
+ return Rect(bounds_exact(a[X]), bounds_exact(a[Y])); |
+} |
+template <typename T> |
+Rect bounds_local(const D2<T> &a, const Interval &t) { |
+ boost::function_requires<FragmentConcept<T> >(); |
|
---|