Subversion Repositories Scribus

Compare Revisions

Ignore whitespace Rev 15343 → Rev 15344

/trunk/Scribus/scribus/third_party/lib2geom/poly-dk-solve.h
File deleted
/trunk/Scribus/scribus/third_party/lib2geom/sturm.h
File deleted
/trunk/Scribus/scribus/third_party/lib2geom/poly-dk-solve.cpp
File deleted
/trunk/Scribus/scribus/third_party/lib2geom/matrix.h
File deleted
/trunk/Scribus/scribus/third_party/lib2geom/matrix.cpp
File deleted
/trunk/Scribus/scribus/third_party/lib2geom/poly-laguerre-solve.cpp
File deleted
/trunk/Scribus/scribus/third_party/lib2geom/poly-laguerre-solve.h
File deleted
/trunk/Scribus/scribus/third_party/lib2geom/point-l.h
File deleted
/trunk/Scribus/scribus/third_party/lib2geom/point-ops.h
File deleted
/trunk/Scribus/scribus/third_party/lib2geom/path.h
1,7 → 1,12
/*
* Path - Series of continuous curves
/**
* \file
* \brief Path - Series of continuous curves
*
* Copyright 2007 MenTaLguY <mental@rydia.net>
* Authors:
* MenTaLguY <mental@rydia.net>
* Marco Cecchetti <mrcekets at gmail.com>
*
* Copyright 2007-2008 authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
27,513 → 32,317
* 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;
#ifndef SEEN_GEOM_PATH_H
#define SEEN_GEOM_PATH_H
 
struct CurveHelpers {
protected:
static int root_winding(Curve const &c, Point p);
};
 
class Curve : private CurveHelpers {
public:
virtual ~Curve() {}
#include <boost/shared_ptr.hpp>
#include "curve.h"
#include "bezier-curve.h"
 
virtual Point initialPoint() const = 0;
virtual Point finalPoint() const = 0;
#include <iterator>
#include <algorithm>
 
virtual bool isDegenerate() const = 0;
 
virtual Curve *duplicate() const = 0;
namespace Geom
{
 
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); }
class Path;
 
virtual std::vector<double> roots(double v, Dim2 d) const = 0;
namespace PathInternal {
 
virtual int winding(Point p) const { return root_winding(*this, p); }
typedef std::vector<boost::shared_ptr<Curve const> > Sequence;
 
//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;
template <typename C, typename P>
class BaseIterator {
protected:
BaseIterator() : path(NULL), index(0) {}
BaseIterator(P *p, unsigned i) : path(p), index(i) {}
// default copy, default assign
 
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);
bool operator==(BaseIterator const &other) {
return path == other.path && index == other.index;
}
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));
bool operator!=(BaseIterator const &other) {
return path != other.path || index != other.index;
}
 
Curve *transformed(Matrix const &m) const {
return new SBasisCurve(inner * m);
Curve const &operator*() const { return (*path)[index]; }
Curve const *operator->() const { return &(*path)[index]; }
boost::shared_ptr<Curve const> get_ref() const {
return path->get_ref_at_index(index);
}
 
Curve *derivative() const {
return new SBasisCurve(Geom::derivative(inner));
C &operator++() {
++index;
return static_cast<C &>(*this);
}
 
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)) {
C operator++(int) {
C old(static_cast<C &>(*this));
++(*this);
return old;
}
 
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]);
C &operator--() {
--index;
return static_cast<C &>(*this);
}
 
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]);
C operator--(int) {
C old(static_cast<C &>(*this));
--(*this);
return old;
}
 
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]);
}
private:
P *path;
unsigned index;
 
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);
}
friend class ::Geom::Path;
};
 
// 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 {
class ConstIterator : public BaseIterator<ConstIterator, Path const> {
public:
SVGEllipticalArc() {}
typedef BaseIterator<ConstIterator, Path const> Base;
 
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)
{}
ConstIterator() : Base() {}
// default copy, default assign
 
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_;
ConstIterator(Path const &p, unsigned i) : Base(&p, i) {}
friend class ::Geom::Path;
};
 
template <typename IteratorImpl>
class BaseIterator
: public std::iterator<std::forward_iterator_tag, Curve const>
{
class Iterator : public BaseIterator<Iterator, Path> {
public:
BaseIterator() {}
typedef BaseIterator<Iterator, Path> Base;
 
// default construct
// default copy
Iterator() : Base() {}
// default copy, default assign
 
bool operator==(BaseIterator const &other) {
return other.impl_ == impl_;
operator ConstIterator const &() const {
return reinterpret_cast<ConstIterator const &>(*this);
}
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;
Iterator(Path &p, unsigned i) : Base(&p, i) {}
friend class ::Geom::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_;
};
 
/*
* Open and closed paths: all paths, whether open or closed, store a final
* segment which connects the initial and final endpoints of the "real"
* path data. While similar to the "z" in an SVG path, it exists for
* both open and closed paths, and is not considered part of the "normal"
* path data, which is always covered by the range [begin(), end_open()).
* Conversely, the range [begin(), end_closed()) always contains the "extra"
* closing segment.
*
* The only difference between a closed and an open path is whether
* end_default() returns end_closed() or end_open(). The idea behind this
* is to let any path be stroked using [begin(), end_default()), and filled
* using [begin(), end_closed()), without requiring a separate "filled" version
* of the path to use for filling.
*
* \invariant : curves_ always contains at least one segment. The last segment
* is always of type ClosingSegment. All constructors take care of this.
(curves_.size() > 0) && dynamic_cast<ClosingSegment>(curves_.back())
*/
class Path {
private:
typedef std::vector<Curve *> Sequence;
 
public:
typedef BaseIterator<Sequence::iterator> iterator;
typedef BaseIterator<Sequence::const_iterator> const_iterator;
typedef PathInternal::Sequence Sequence;
typedef PathInternal::Iterator iterator;
typedef PathInternal::ConstIterator const_iterator;
typedef Sequence::size_type size_type;
typedef Sequence::difference_type difference_type;
 
Path()
: final_(new LineSegment()), closed_(false)
{
curves_.push_back(final_);
}
class ClosingSegment : public LineSegment {
public:
ClosingSegment() : LineSegment() {}
ClosingSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
virtual Curve *duplicate() const { return new ClosingSegment(*this); }
virtual Curve *reverse() const { return new ClosingSegment((*this)[1], (*this)[0]); }
};
 
Path(Path const &other)
: final_(new LineSegment()), closed_(other.closed_)
enum Stitching {
NO_STITCHING=0,
STITCH_DISCONTINUOUS
};
 
class StitchSegment : public LineSegment {
public:
StitchSegment() : LineSegment() {}
StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
virtual Curve *duplicate() const { return new StitchSegment(*this); }
virtual Curve *reverse() const { return new StitchSegment((*this)[1], (*this)[0]); }
};
 
// Path(Path const &other) - use default copy constructor
 
explicit Path(Point p=Point())
: curves_(boost::shared_ptr<Sequence>(new Sequence(1, boost::shared_ptr<Curve>()))),
final_(new ClosingSegment(p, p)),
closed_(false)
{
curves_.push_back(final_);
insert(begin(), other.begin(), other.end());
get_curves().back() = boost::shared_ptr<Curve>(final_);
}
 
explicit Path(Point p)
: final_(new LineSegment(p, p)), closed_(false)
Path(const_iterator const &first,
const_iterator const &last,
bool closed=false)
: curves_(boost::shared_ptr<Sequence>(new Sequence(seq_iter(first),
seq_iter(last)))),
closed_(closed)
{
curves_.push_back(final_);
if (!get_curves().empty()) {
final_ = new ClosingSegment(get_curves().back()->finalPoint(),
get_curves().front()->initialPoint());
} else {
final_ = new ClosingSegment();
}
get_curves().push_back(boost::shared_ptr<Curve>(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() {}
// Path &operator=(Path const &other) - use default assignment operator
 
void swap(Path &other) {
std::swap(other.curves_, curves_);
std::swap(other.final_, final_);
std::swap(other.closed_, closed_);
}
 
virtual ~Path() {
delete_range(curves_.begin(), curves_.end()-1);
delete final_;
Curve const &operator[](unsigned i) const { return *get_curves()[i]; }
Curve const &at_index(unsigned i) const { return *get_curves()[i]; }
boost::shared_ptr<Curve const> get_ref_at_index(unsigned i) {
return get_curves()[i];
}
 
Path &operator=(Path const &other) {
clear();
insert(begin(), other.begin(), other.end());
close(other.closed_);
return *this;
Curve const &front() const { return *get_curves()[0]; }
Curve const &back() const { return back_open(); }
Curve const &back_open() const {
if (empty()) { THROW_RANGEERROR("Path contains not enough segments"); }
return *get_curves()[get_curves().size()-2];
}
Curve const &back_closed() const { return *get_curves()[get_curves().size()-1]; }
Curve const &back_default() const {
return ( closed_ ? back_closed() : back_open() );
}
 
void swap(Path &other);
const_iterator begin() const { return const_iterator(*this, 0); }
const_iterator end() const { return const_iterator(*this, size()); }
iterator begin() { return iterator(*this, 0); }
iterator end() { return iterator(*this, size()); }
 
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_open() const { return const_iterator(*this, size()); }
const_iterator end_closed() const { return const_iterator(*this, size()+1); }
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; }
size_type size_open() const { return get_curves().size()-1; }
size_type size_closed() const { return get_curves().size(); }
size_type size_default() const {
return ( closed_ ? size_closed() : size_open() );
}
size_type size() const { return size_open(); }
 
bool empty() const { return curves_.size() == 1; }
size_type max_size() const { return get_curves().max_size()-1; }
 
bool empty() const { return (get_curves().size() == 1); }
bool closed() const { return closed_; }
void close(bool closed=true) { closed_ = closed; }
 
Rect boundsFast() const;
Rect boundsExact() const;
OptRect boundsFast() const;
OptRect 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) {
bool degenerate = true;
// pw<d2<>> is always open. so if path is closed, add closing segment as well to pwd2.
for(const_iterator it = begin(); it != end_default(); ++it) {
if (!it->isDegenerate()) {
ret.push(it->toSBasis(), i++);
degenerate = false;
}
}
if (degenerate) {
// if path only contains degenerate curves, no second cut is added
// so we need to create at least one segment manually
ret = Piecewise<D2<SBasis> >(initialPoint());
}
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;
}
bool operator==(Path const &other) const {
if (this == &other) return true;
if (closed_ != other.closed_) return false;
return get_curves() == other.get_curves();
}
bool operator!=(Path const &other) const {
return !( *this == other );
}
 
Path operator*(Affine const &m) const {
Path ret(*this);
ret *= m;
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);
Path &operator*=(Affine const &m);
Point pointAt(double t) const
{
unsigned int sz = size();
if ( closed() ) ++sz;
if ( t < 0 || t > sz )
{
THROW_RANGEERROR("parameter t out of bounds");
}
if ( empty() ) return initialPoint(); // naked moveto
double k, lt = modf(t, &k);
unsigned int i = static_cast<unsigned int>(k);
if ( i == sz )
{
--i;
lt = 1;
}
return (*this)[i].pointAt(lt);
}
 
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);
double valueAt(double t, Dim2 d) const
{
unsigned int sz = size();
if ( closed() ) ++sz;
if ( t < 0 || t > sz )
{
THROW_RANGEERROR("parameter t out of bounds");
}
if ( empty() ) return initialPoint()[d]; // naked moveto
double k, lt = modf(t, &k);
unsigned int i = static_cast<unsigned int>(k);
if ( i == sz )
{
--i;
lt = 1;
}
return (*this)[i].valueAt(lt, d);
}
 
Point operator() (double t) const
{
return pointAt(t);
}
std::vector<double> roots(double v, Dim2 d) const {
std::vector<double> res;
for(unsigned i = 0; i <= size(); i++) {
543,7 → 352,30
}
return res;
}
 
std::vector<double>
allNearestPoints(Point const& _point, double from, double to) const;
std::vector<double>
allNearestPoints(Point const& _point) const
{
unsigned int sz = size();
if ( closed() ) ++sz;
return allNearestPoints(_point, 0, sz);
}
std::vector<double>
nearestPointPerCurve(Point const& _point) const;
double nearestPoint(Point const& _point, double from, double to, double *distance_squared = NULL) const;
double nearestPoint(Point const& _point, double *distance_squared = NULL) const
{
unsigned int sz = size();
if ( closed() ) ++sz;
return nearestPoint(_point, 0, sz, distance_squared);
}
void appendPortionTo(Path &p, double f, double t) const;
 
Path portion(double f, double t) const {
555,104 → 387,132
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;
Path ret(*this);
ret.unshare();
for ( Sequence::iterator iter = ret.get_curves().begin() ;
iter != ret.get_curves().end()-1 ; ++iter )
{
*iter = boost::shared_ptr<Curve>((*iter)->reverse());
}
std::reverse(ret.get_curves().begin(), ret.get_curves().end()-1);
ret.final_ = static_cast<ClosingSegment *>(ret.final_->reverse());
ret.get_curves().back() = boost::shared_ptr<Curve>(ret.final_);
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;
}
void insert(iterator const &pos,
Curve const &curve, Stitching stitching=NO_STITCHING)
{
unshare();
Sequence::iterator seq_pos(seq_iter(pos));
Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
if (stitching) stitch(seq_pos, seq_pos, source);
do_update(seq_pos, seq_pos, source.begin(), source.end());
}
 
template <typename Impl>
void insert(iterator pos, BaseIterator<Impl> first, BaseIterator<Impl> last)
void insert(iterator const &pos,
const_iterator const &first,
const_iterator const &last,
Stitching stitching=NO_STITCHING)
{
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;
}
unshare();
Sequence::iterator seq_pos(seq_iter(pos));
Sequence source(seq_iter(first), seq_iter(last));
if (stitching) stitch(seq_pos, seq_pos, source);
do_update(seq_pos, seq_pos, source.begin(), source.end());
}
 
void clear() {
do_update(curves_.begin(), curves_.end()-1,
curves_.begin(), curves_.begin());
unshare();
do_update(get_curves().begin(), get_curves().end()-1,
get_curves().begin(), get_curves().begin());
}
 
void erase(iterator pos) {
do_update(pos.impl_, pos.impl_+1, curves_.begin(), curves_.begin());
void erase(iterator const &pos, Stitching stitching=NO_STITCHING) {
unshare();
Sequence::iterator seq_pos(seq_iter(pos));
if (stitching) {
Sequence stitched;
stitch(seq_pos, seq_pos+1, stitched);
do_update(seq_pos, seq_pos+1, stitched.begin(), stitched.end());
} else {
do_update(seq_pos, seq_pos+1, get_curves().begin(), get_curves().begin());
}
}
 
void erase(iterator first, iterator last) {
do_update(first.impl_, last.impl_, curves_.begin(), curves_.begin());
void erase(iterator const &first,
iterator const &last,
Stitching stitching=NO_STITCHING)
{
unshare();
Sequence::iterator seq_first=seq_iter(first);
Sequence::iterator seq_last=seq_iter(last);
if (stitching) {
Sequence stitched;
stitch(seq_first, seq_last, stitched);
do_update(seq_first, seq_last, stitched.begin(), stitched.end());
} else {
do_update(seq_first, seq_last,
get_curves().begin(), get_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;
}
// erase last segment of path
void erase_last() {
erase(iterator(*this, size()-1));
}
 
void replace(iterator first_replaced, iterator last_replaced,
Curve const &curve)
void replace(iterator const &replaced,
Curve const &curve,
Stitching stitching=NO_STITCHING)
{
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;
}
unshare();
Sequence::iterator seq_replaced(seq_iter(replaced));
Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
if (stitching) stitch(seq_replaced, seq_replaced+1, source);
do_update(seq_replaced, seq_replaced+1, source.begin(), source.end());
}
 
template <typename Impl>
void replace(iterator replaced,
BaseIterator<Impl> first, BaseIterator<Impl> last)
void replace(iterator const &first_replaced,
iterator const &last_replaced,
Curve const &curve, Stitching stitching=NO_STITCHING)
{
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;
}
unshare();
Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
if (stitching) stitch(seq_first_replaced, seq_last_replaced, source);
do_update(seq_first_replaced, seq_last_replaced,
source.begin(), source.end());
}
 
template <typename Impl>
void replace(iterator first_replaced, iterator last_replaced,
BaseIterator<Impl> first, BaseIterator<Impl> last)
void replace(iterator const &replaced,
const_iterator const &first,
const_iterator const &last,
Stitching stitching=NO_STITCHING)
{
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;
}
unshare();
Sequence::iterator seq_replaced(seq_iter(replaced));
Sequence source(seq_iter(first), seq_iter(last));
if (stitching) stitch(seq_replaced, seq_replaced+1, source);
do_update(seq_replaced, seq_replaced+1, source.begin(), source.end());
}
 
void replace(iterator const &first_replaced,
iterator const &last_replaced,
const_iterator const &first,
const_iterator const &last,
Stitching stitching=NO_STITCHING)
{
unshare();
Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
Sequence source(seq_iter(first), seq_iter(last));
if (stitching) stitch(seq_first_replaced, seq_last_replaced, source);
do_update(seq_first_replaced, seq_last_replaced,
source.begin(), source.end());
}
 
void start(Point p) {
clear();
final_->setPoint(0, p);
662,40 → 522,92
Point initialPoint() const { return (*final_)[1]; }
Point finalPoint() const { return (*final_)[0]; }
 
void append(Curve const &curve);
void append(D2<SBasis> const &curve);
void setInitial(Point const& p)
{
if ( empty() ) return;
unshare();
boost::shared_ptr<Curve> head(front().duplicate());
head->setInitial(p);
Sequence::iterator replaced = get_curves().begin();
Sequence source(1, head);
do_update(replaced, replaced + 1, source.begin(), source.end());
}
 
void setFinal(Point const& p)
{
if ( empty() ) return;
unshare();
boost::shared_ptr<Curve> tail(back().duplicate());
tail->setFinal(p);
Sequence::iterator replaced = get_curves().end() - 2;
Sequence source(1, tail);
do_update(replaced, replaced + 1, source.begin(), source.end());
}
 
void append(Curve const &curve, Stitching stitching=NO_STITCHING) {
unshare();
if (stitching) stitchTo(curve.initialPoint());
do_append(curve.duplicate());
}
void append(D2<SBasis> const &curve, Stitching stitching=NO_STITCHING) {
unshare();
if (stitching) stitchTo(Point(curve[X][0][0], curve[Y][0][0]));
do_append(new SBasisCurve(curve));
}
void append(Path const &other, Stitching stitching=NO_STITCHING) {
insert(end(), other.begin(), other.end(), stitching);
}
 
void stitchTo(Point const &p) {
if (!empty() && finalPoint() != p) {
unshare();
do_append(new StitchSegment(finalPoint(), p));
}
}
 
 
/**
* It is important to note that the coordinates passed to appendNew should be finite!
* If one of the coordinates is infinite, 2geom will throw a ContinuityError exception.
*/
 
template <typename CurveType, typename A>
void appendNew(A a) {
do_append(new CurveType((*final_)[0], a));
unshare();
do_append(new CurveType(finalPoint(), a));
}
 
template <typename CurveType, typename A, typename B>
void appendNew(A a, B b) {
do_append(new CurveType((*final_)[0], a, b));
unshare();
do_append(new CurveType(finalPoint(), 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));
unshare();
do_append(new CurveType(finalPoint(), 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));
unshare();
do_append(new CurveType(finalPoint(), 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));
unshare();
do_append(new CurveType(finalPoint(), 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));
unshare();
do_append(new CurveType(finalPoint(), a, b, c, d, e, f));
}
 
template <typename CurveType, typename A, typename B, typename C,
702,7 → 614,8
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));
unshare();
do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g));
}
 
template <typename CurveType, typename A, typename B, typename C,
709,7 → 622,8
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));
unshare();
do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h));
}
 
template <typename CurveType, typename A, typename B, typename C,
716,28 → 630,52
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));
unshare();
do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h, i));
}
 
private:
static Sequence::iterator seq_iter(iterator const &iter) {
return iter.path->get_curves().begin() + iter.index;
}
static Sequence::const_iterator seq_iter(const_iterator const &iter) {
return iter.path->get_curves().begin() + iter.index;
}
 
Sequence &get_curves() { return *curves_; }
Sequence const &get_curves() const { return *curves_; }
 
void unshare() {
if (!curves_.unique()) {
curves_ = boost::shared_ptr<Sequence>(new Sequence(*curves_));
}
if (!get_curves().back().unique()) {
final_ = static_cast<ClosingSegment *>(final_->duplicate());
get_curves().back() = boost::shared_ptr<Curve>(final_);
}
}
 
void stitch(Sequence::iterator first_replaced,
Sequence::iterator last_replaced,
Sequence &sequence);
 
void do_update(Sequence::iterator first_replaced,
Sequence::iterator last_replaced,
Sequence::iterator first,
Sequence::iterator last);
 
// n.b. takes ownership of curve object
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_;
boost::shared_ptr<Sequence> curves_;
ClosingSegment *final_;
bool closed_;
};
}; // end class Path
 
inline static Piecewise<D2<SBasis> > paths_to_pw(std::vector<Path> paths) {
Piecewise<D2<SBasis> > ret = paths[0].toPwSb();
747,55 → 685,14
return ret;
}
 
/*
class PathPortion : public Curve {
Path *source;
double f, t;
boost::optional<Path> result;
inline
Coord nearest_point(Point const& p, Path const& c)
{
return c.nearestPoint(p);
}
 
public:
double from() const { return f; }
double to() const { return t; }
} // end namespace Geom
 
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 <>
804,18 → 701,20
a.swap(b);
}
 
}
} // end namespace std
 
#endif // SEEN_GEOM_PATH_H
 
 
 
 
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(substatement-open . 0))
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
c-brace-offset:0
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=2:tabstop=8:softtabstop=2 :
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/sbasis-math.h
1,5 → 1,6
/*
* sbasis-math.h - some std functions to work with (pw)s-basis
/**
* \file
* \brief some std functions to work with (pw)s-basis
*
* Authors:
* Jean-Francois Barraud
41,6 → 42,7
 
#include "sbasis.h"
#include "piecewise.h"
#include "d2.h"
 
namespace Geom{
//-|x|---------------------------------------------------------------
80,9 → 82,12
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);
 
//--interpolate------------------------------------------------------------
Piecewise<SBasis> interpolate( std::vector<double> times, std::vector<double> values, unsigned smoothness = 1);
}
 
#endif //SEEN_GEOM_PW_SB_CALCULUS_H
 
/*
Local Variables:
mode:c++
92,4 → 97,4
fill-column:99
End:
*/
// vim: filetype = cpp:expandtab:shiftwidth = 4:tabstop = 8:softtabstop = 4:encoding = utf-8:textwidth = 99 :
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/conic_section_clipper_impl.h
0,0 → 1,356
/**
* \file
* \brief Conic section clipping with respect to a rectangle
*
* Authors:
* Marco Cecchetti <mrcekets at gmail>
*
* Copyright 2009 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 _2GEOM_CONIC_SECTION_CLIPPER_IMPL_H_
#define _2GEOM_CONIC_SECTION_CLIPPER_IMPL_H_
 
 
#include "conicsec.h"
#include "line.h"
 
#include <list>
#include <map>
 
 
 
#ifdef CLIP_WITH_CAIRO_SUPPORT
#include <toys/path-cairo.h>
#define CLIPPER_CLASS clipper_cr
#else
#define CLIPPER_CLASS clipper
#endif
 
//#define CLIPDBG
 
#ifdef CLIPDBG
#include <2geom/toys/path-cairo.h>
#define DBGINFO(msg) \
std::cerr << msg << std::endl;
#define DBGPRINT(msg, var) \
std::cerr << msg << var << std::endl;
#define DBGPRINTIF(cond, msg, var) \
if (cond) \
std::cerr << msg << var << std::endl;
 
#define DBGPRINT2(msg1, var1, msg2, var2) \
std::cerr << msg1 << var1 << msg2 << var2 << std::endl;
 
#define DBGPRINTCOLL(msg, coll) \
if (coll.size() != 0) \
std::cerr << msg << ":\n"; \
for (size_t i = 0; i < coll.size(); ++i) \
{ \
std::cerr << i << ": " << coll[i] << "\n"; \
}
 
#else
#define DBGINFO(msg)
#define DBGPRINT(msg, var)
#define DBGPRINTIF(cond, msg, var)
#define DBGPRINT2(msg1, var1, msg2, var2)
#define DBGPRINTCOLL(msg, coll)
#endif
 
 
 
 
namespace Geom
{
 
class CLIPPER_CLASS
{
 
public:
 
#ifdef CLIP_WITH_CAIRO_SUPPORT
clipper_cr (cairo_t* _cr, const xAx & _cs, const Rect & _R)
: cr(_cr), cs(_cs), R(_R)
{
DBGPRINT ("CLIP: right side: ", R.right())
DBGPRINT ("CLIP: top side: ", R.top())
DBGPRINT ("CLIP: left side: ", R.left())
DBGPRINT ("CLIP: bottom side: ", R.bottom())
}
#else
clipper (const xAx & _cs, const Rect & _R)
: cs(_cs), R(_R)
{
}
#endif
 
bool clip (std::vector<RatQuad> & arcs);
 
bool found_any_isolated_point() const
{
return (single_points.size() != 0);
}
 
const std::vector<Point> & isolated_points() const
{
return single_points;
}
 
 
private:
bool intersect (std::vector<Point> & crossing_points) const;
 
bool are_paired (Point & M, const Point & P1, const Point & P2) const;
void pairing (std::vector<Point> & paired_points,
std::vector<Point> & inner_points,
const std::vector<Point> & crossing_points);
 
Point find_inner_point_by_bisector_line (const Point & P,
const Point & Q) const;
Point find_inner_point (const Point & P, const Point & Q) const;
 
std::list<Point>::iterator split (std::list<Point> & points,
std::list<Point>::iterator sp,
std::list<Point>::iterator fp) const;
void rsplit (std::list<Point> & points,
std::list<Point>::iterator sp,
std::list<Point>::iterator fp,
size_t k) const;
 
void rsplit (std::list<Point> & points,
std::list<Point>::iterator sp,
std::list<Point>::iterator fp,
double length) const;
 
private:
#ifdef CLIP_WITH_CAIRO_SUPPORT
cairo_t* cr;
#endif
const xAx & cs;
const Rect & R;
std::vector<Point> single_points;
};
 
 
 
 
/*
* Given two point "P", "Q" on the conic section the method computes
* a third point inner to the arc with end-point "P", "Q".
* The new point is found by intersecting the conic with the bisector line
* of the PQ line segment.
*/
inline
Point CLIPPER_CLASS::find_inner_point_by_bisector_line (const Point & P,
const Point & Q) const
{
DBGPRINT ("CLIP: find_inner_point_by_bisector_line: P = ", P)
DBGPRINT ("CLIP: find_inner_point_by_bisector_line: Q = ", Q)
Line bl = make_bisector_line (LineSegment (P, Q));
std::vector<double> rts = cs.roots (bl);
//DBGPRINT ("CLIP: find_inner_point: rts.size = ", rts.size())
double t;
if (rts.size() == 0)
{
THROW_LOGICALERROR ("clipper::find_inner_point_by_bisector_line: "
"no conic-bisector line intersection point");
}
if (rts.size() == 2)
{
// we suppose that the searched point is the nearest
// to the line segment PQ
t = (std::fabs(rts[0]) < std::fabs(rts[1])) ? rts[0] : rts[1];
}
else
{
t = rts[0];
}
return bl.pointAt (t);
}
 
 
/*
* Given two point "P", "Q" on the conic section the method computes
* a third point inner to the arc with end-point "P", "Q".
* The new point is found by intersecting the conic with the line
* passing through the middle point of the PQ line segment and
* the intersection point of the tangent lines at points P and Q.
*/
inline
Point CLIPPER_CLASS::find_inner_point (const Point & P, const Point & Q) const
{
 
Line l1 = cs.tangent (P);
Line l2 = cs.tangent (Q);
Line l;
// in case we fail to find a crossing point we fall back to the bisector
// method
try
{
OptCrossing oc = intersection(l1, l2);
if (!oc)
{
return find_inner_point_by_bisector_line (P, Q);
}
l.setPoints (l1.pointAt (oc->ta), middle_point (P, Q));
}
catch (Geom::InfiniteSolutions e)
{
return find_inner_point_by_bisector_line (P, Q);
}
 
std::vector<double> rts = cs.roots (l);
double t;
if (rts.size() == 0)
{
return find_inner_point_by_bisector_line (P, Q);
}
// the line "l" origin is set to the tangent crossing point so in case
// we find two intersection points only the nearest belongs to the given arc
// pay attention: in case we are dealing with an hyperbola (remember that
// end points are on the same branch, because they are paired) the tangent
// crossing point belongs to the angle delimited by hyperbola asymptotes
// and containing the given hyperbola branch, so the previous statement is
// still true
if (rts.size() == 2)
{
t = (std::fabs(rts[0]) < std::fabs(rts[1])) ? rts[0] : rts[1];
}
else
{
t = rts[0];
}
return l.pointAt (t);
}
 
 
/*
* Given a list of points on the conic section, and given two consecutive
* points belonging to the list and passed by two list iterators, the method
* finds a new point that is inner to the conic arc which has the two passed
* points as initial and final point. This new point is inserted into the list
* between the two passed points and an iterator pointing to the new point
* is returned.
*/
inline
std::list<Point>::iterator CLIPPER_CLASS::split (std::list<Point> & points,
std::list<Point>::iterator sp,
std::list<Point>::iterator fp) const
{
Point new_point = find_inner_point (*sp, *fp);
std::list<Point>::iterator ip = points.insert (fp, new_point);
//std::cerr << "CLIP: split: [" << *sp << ", " << *ip << ", "
// << *fp << "]" << std::endl;
return ip;
}
 
 
/*
* Given a list of points on the conic section, and given two consecutive
* points belonging to the list and passed by two list iterators, the method
* recursively finds new points that are inner to the conic arc which has
* the two passed points as initial and final point. The recursion stop after
* "k" recursive calls. These new points are inserted into the list between
* the two passed points, and in the order we cross them going from
* the initial to the final arc point.
*/
inline
void CLIPPER_CLASS::rsplit (std::list<Point> & points,
std::list<Point>::iterator sp,
std::list<Point>::iterator fp,
size_t k) const
{
if (k == 0)
{
//DBGINFO("CLIP: split: no further split")
return;
}
 
std::list<Point>::iterator ip = split (points, sp, fp);
--k;
rsplit (points, sp, ip, k);
rsplit (points, ip, fp, k);
}
 
 
/*
* Given a list of points on the conic section, and given two consecutive
* points belonging to the list and passed by two list iterators, the method
* recursively finds new points that are inner to the conic arc which has
* the two passed points as initial and final point. The recursion stop when
* the max distance between the new computed inner point and the two passed
* arc end-points is less then the value specified by the "length" parameter.
* These new points are inserted into the list between the two passed points,
* and in the order we cross them going from the initial to the final arc point.
*/
inline
void CLIPPER_CLASS::rsplit (std::list<Point> & points,
std::list<Point>::iterator sp,
std::list<Point>::iterator fp,
double length) const
{
std::list<Point>::iterator ip = split (points, sp, fp);
double d1 = distance (*sp, *ip);
double d2 = distance (*ip, *fp);
double mdist = std::max (d1, d2);
 
if (mdist < length)
{
//DBGINFO("CLIP: split: no further split")
return;
}
 
// they have to be called both to keep the number of points in the list
// in the form 2k+1 where k are the sub-arcs the initial arc is splitted in.
rsplit (points, sp, ip, length);
rsplit (points, ip, fp, length);
}
 
 
} // end namespace Geom
 
 
 
 
#endif // _2GEOM_CONIC_SECTION_CLIPPER_IMPL_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 :
/trunk/Scribus/scribus/third_party/lib2geom/redblacktree.cpp
0,0 → 1,575
#include <redblacktree.h>
//#include <algorithm>
 
 
#define _REDBLACK_PRINT(x) std::cout << x << std::endl;
//comment the following if you want output during RedBlack Tree operations
//#define _REDBLACK_PRINT(x) ;
 
 
namespace Geom{
 
 
 
RedBlack* RedBlackTree::search(Rect const &r, int dimension){
return search( Interval( r[dimension].min(), r[dimension].max() ) );
// TODO get rid of dimension
// TODO put 2 trees (X, Y axis in one lump)
}
 
/*
INTERVAL-SEARCH(T, i)
1 x <- root[T]
2 while x != nil[T] and i does not overlap int[x]
3 do if left[x] != nil[T] and max[left[x]] >= low[i]
4 then x <- left[x]
5 else x <- right[x]
6 return x
 
Two intervals i,x overlap in the 4 following cases:
1) |--------| i
|---| x
 
2) |-----| i
|----------| x
 
3) |------| i
|------| x
 
4) |----| i
|----| x
 
And do not overlap when (the one os left or right of the other)
1) |--------| i
|---| x
 
2) |-----| i
|----------| x
 
 
*/
RedBlack* RedBlackTree::search(Interval i){
_REDBLACK_PRINT( "==============================================================" << std::endl << "ENTER: search(Interval i) : (" << i.min() << ", " << i.max() << ")" )
RedBlack *x;
x = root;
 
while( x!=0 &&
( i.max() < x->interval.min() ||
i.min() > x->interval.max() )
){
_REDBLACK_PRINT( "(" << x->data << ": " << x->key() << ", " << x->high() << " : " << x->subtree_max << ") "
<< " i do not overlap with x")
 
if(x->left != 0 && (x->left)->subtree_max >= i.min() ){
x = x->left;
}
else{
x = x->right;
}
}
_REDBLACK_PRINT( "RETURN: search" << std::endl )
return x;
}
 
 
 
void RedBlackTree::insert(Rect const &r, int shape, int dimension) {
_REDBLACK_PRINT( "==============================================================" << std::endl << "ENTER: insert(Rect, int, dimension): " << " dimension:" << dimension << " shape:" << shape )
insert(r[dimension].min(), r[dimension].max(), shape);
_REDBLACK_PRINT( "RETURN: insert(Rect, int, dimension)")
}
 
// source: book pp 251
void RedBlackTree::insert(Coord dimension_min, Coord dimension_max, int shape) {
_REDBLACK_PRINT( std::endl << "ENTER: insert(Coord, Coord, int): " << dimension_min << ", " << dimension_max << " , shape: " << shape )
// x is the new node we insert
RedBlack *x = new RedBlack();
x->interval = Interval( dimension_min, dimension_max );
x->data = shape;
x->isRed = true;
 
_REDBLACK_PRINT( " x: " << x << " KEY: " << x->key() << " high: " << x->high() )
 
tree_insert(x);
 
print_tree();
 
_REDBLACK_PRINT( " Begin coloring" )
// we now do the coloring of the tree.
_REDBLACK_PRINT( " while( x!= root && (x->parent)->isRed )" )
while( x!= root && (x->parent)->isRed ){
_REDBLACK_PRINT( " ((x->parent)->parent)->left:" << ((x->parent)->parent)->left << " ((x->parent)->parent)->right:" << ((x->parent)->parent)->right )
 
if( x->parent == ((x->parent)->parent)->left ){
_REDBLACK_PRINT( " Left:" )
RedBlack *y = new RedBlack;
y = ((x->parent)->parent)->right;
if( y == 0 ){
/*
This 1st brach is not in the book, but is needed. We must check y->isRed but it is
undefined, so we get segfault. But 0 (undefined) means that y is a leaf, so it is
black by definition. So, do the same as in the else part.
*/
_REDBLACK_PRINT( " y==0" )
if( x == (x->parent)->right ){
x = x->parent;
left_rotate(x);
}
(x->parent)->isRed = false;
((x->parent)->parent)->isRed = true;
right_rotate((x->parent)->parent);
}
else if( y->isRed ){
_REDBLACK_PRINT( " y->isRed" )
(x->parent)->isRed = false;
y->isRed = false;
((x->parent)->parent)->isRed = true;
x = (x->parent)->parent;
}
else{
_REDBLACK_PRINT( " !( y->isRed)" )
if( x == (x->parent)->right ){
x = x->parent;
left_rotate(x);
}
(x->parent)->isRed = false;
((x->parent)->parent)->isRed = true;
right_rotate((x->parent)->parent);
}
}
else{ // this branch is the same with the above if clause with "right", "left" exchanged
_REDBLACK_PRINT( " Right:" )
RedBlack *y = new RedBlack;
y = ((x->parent)->parent)->left;
if( y == 0 ){
/*
This 1st brach is not in the book, but is needed. We must check y->isRed but it is
undefined, so we get segfault. But 0 (undefined) means that y is a leaf, so it is
black by definition. So, do the same as in the else part.
*/
_REDBLACK_PRINT( " y==0" )
if( x == (x->parent)->left ){
x = x->parent;
right_rotate(x);
}
(x->parent)->isRed = false;
((x->parent)->parent)->isRed = true;
left_rotate((x->parent)->parent);
}
else if( y->isRed ){
_REDBLACK_PRINT( " y->isRed" )
(x->parent)->isRed = false;
y->isRed = false;
((x->parent)->parent)->isRed = true;
x = (x->parent)->parent;
}
else{
_REDBLACK_PRINT( " !( y->isRed)" )
if( x == (x->parent)->left ){
x = x->parent;
right_rotate(x);
}
(x->parent)->isRed = false;
((x->parent)->parent)->isRed = true;
left_rotate((x->parent)->parent);
}
}
}
root->isRed = false;
 
// update the max value with a slow/stupid yet certain way, walking all the tree :P
// TODO find better way
_REDBLACK_PRINT( " Update max" )
 
update_max(root);
 
_REDBLACK_PRINT( "RETURN: insert(Coord, Coord, int)" << std::endl)
}
 
// from book p. 266)
void RedBlackTree::left_rotate(RedBlack* x){
// x->right != 0 (assumption book page 266)
// ??? hm problem ???
_REDBLACK_PRINT( "ENTER: left_rotate" )
RedBlack* y = new RedBlack;
y = x->right;
x->right = y->left;
 
if( y->left != 0){
(y->left)->parent = x;
}
 
y->parent = x->parent;
 
if( x->parent == 0){
root = y;
}
else{
if( x == (x->parent)->left ){
(x->parent)->left = y;
}
else{
(x->parent)->right = y;
}
}
y->left = x;
x->parent = y;
_REDBLACK_PRINT( "RETURN: left_rotate" << std::endl )
}
 
// from book p. 266: right_rotate is inverse of left_rotate
// same to left_rotate with "right", "left" exchanged
void RedBlackTree::right_rotate(RedBlack* x){
// x->right != 0 (assumption book page 266)
// ??? hm problem ??
_REDBLACK_PRINT( "ENTER: right_rotate" )
RedBlack* y = new RedBlack;
 
_REDBLACK_PRINT( "x->left: " << x->left )
y = x->left;
x->left = y->right;
if( y->right != 0){
(y->right)->parent = x;
}
 
y->parent = x->parent;
 
if( x->parent == 0){
root = y;
}
else{
if( x == (x->parent)->left ){
(x->parent)->left = y;
}
else{
(x->parent)->right = y;
}
}
y->right = x;
x->parent = y;
_REDBLACK_PRINT( "RETURN: right_rotate" << std::endl )
}
 
// insertion in binary search tree: book page 251
// then the redblack insert performs the coloring
void RedBlackTree::tree_insert(RedBlack* z){
_REDBLACK_PRINT( "ENTER: tree_insert(RedBlack* z)" )
RedBlack* y = 0; // y <- nil
 
RedBlack* x = root;
 
_REDBLACK_PRINT( " while x!=0 " )
while( x != 0 ){
y = x;
// _REDBLACK_PRINT( " x:" << x << " y:" << y << " z:" << z )
_REDBLACK_PRINT( " z->key: " << z->key() << " y->key: " << y->key() << " compare")
if( z->key() < x->key() ){
_REDBLACK_PRINT( " z smaller: go left" )
x = x->left;
}
else{
_REDBLACK_PRINT( " z bigger: go right" )
x = x->right;
}
}
 
_REDBLACK_PRINT( " z->parent = y" )
z->parent = y;
 
if( y == 0 ){
_REDBLACK_PRINT( " set z root (empty tree)" )
root = z;
}
else{
_REDBLACK_PRINT( " z->key: " << z->key() << " y->key: " << y->key() << " compare")
if( z->key() < y->key() ){
_REDBLACK_PRINT( " z->key() smaller: y->left = z; " )
y->left = z;
}
else{
_REDBLACK_PRINT( " z->key() bigger: y->right = z " )
y->right = z;
}
}
_REDBLACK_PRINT( "RETURN: tree_insert(RedBlack* z)" << std::endl )
}
 
 
/*
RB-DELETE(T, z)
1 if left[z] = nil[T] or right[z] = nil[T]
2 then y <- z
3 else y <- TREE-SUCCESSOR(z)
4 if left[y] != nil[T]
5 then x <- left[y]
6 else x <- right[y]
7 p[x] <- p[y]
8 if p[y] = nil[T]
9 then root[T] <- x
10 else if y = left[p[y]]
11 then left[p[y]] <- x
12 else right[p[y]] <- x
13 if y != z
14 then key[z] <- key[y]
15 copy y's satellite data into z
16 if color[y] = BLACK
17 then RB-DELETE-FIXUP(T, x)
18 return y
*/
RedBlack* RedBlackTree::erase(RedBlack* z){
_REDBLACK_PRINT( "==============================================================" << std::endl << "ENTER: earse(z)" )
RedBlack* x = new RedBlack();
RedBlack* y = new RedBlack();
if( z->left == 0 || z->right == 0 ){
y = z;
}
else{
y = tree_successor(z);
}
 
if( y->left != 0 ){
x = y->left;
}
else{
x = y->right;
}
x->parent = y->parent;
 
if( y->parent == 0){
root = x;
}
else {
if( y == (y->parent)->left ){
(y->parent)->left = x;
}
else{
(y->parent)->right = x;
}
}
 
if( y != z){
z->interval = y->interval ; // key[z] <- key[y] TODO check this
//copy y's satellite data into z
z->data = y->data;
z->isRed = y->isRed;
 
z->left = y->left;
z->right = y->right;
z->parent = y->parent;
}
if( y->isRed == false){
erase_fixup(x);
}
 
_REDBLACK_PRINT( "Update max" )
update_max(root);
 
_REDBLACK_PRINT( "RETURN: erase" )
return y;
}
 
/*
RB-DELETE-FIXUP(T, x)
1 while x != root[T] and color[x] = BLACK
2 do if x = left[p[x]]
3 then w <- right[p[x]]
4 if color[w] = RED
5 then color[w] <- BLACK Case 1
6 color[p[x]] <- RED Case 1
7 LEFT-ROTATE(T, p[x]) Case 1
8 w <- right[p[x]]
9 if color[left[w]] = BLACK and color[right[w]] = BLACK
10 then color[w] <- RED Case 2
11 x p[x] Case 2
12 else if color[right[w]] = BLACK
13 then color[left[w]] <- BLACK Case 3
14 color[w] <- RED Case 3
15 RIGHT-ROTATE(T, w) Case 3
16 w <- right[p[x]] Case 3
17 color[w] <- color[p[x]] Case 4
18 color[p[x]] <- BLACK Case 4
19 color[right[w]] <- BLACK Case 4
20 LEFT-ROTATE(T, p[x]) Case 4
21 x <- root[T] Case 4
22 else (same as then clause with "right" and "left" exchanged)
23 color[x] <- BLACK
*/
void RedBlackTree::erase_fixup(RedBlack* x){
RedBlack* w = 0;
while( x != root && x->isRed == false ){
if( x == (x->parent)->left ){
w = (x->parent)->right;
if(w->isRed == true){
w->isRed = false;
(w->parent)->isRed = true;
left_rotate(x->parent);
w = (x->parent)->right;
}
if( (w->left)->isRed == false && (w->right)->isRed == false ){
w->isRed = true;
x = x->parent; // TODO understand why this happens ???
}
else{
if( (w->right)->isRed == false ){
(w->left)->isRed = false;
right_rotate(w);
w = (x->parent)->right;
}
else{ // TODO ??? is this correct ???
w->isRed = (x->parent)->isRed;
(x->parent)->isRed = false;
(w->right)->isRed = false;
left_rotate(x->parent);
x = root; // TODO ??? is this correct ???
}
}
}
else{ // same as then clause with "right" and "left" exchanged
w = (x->parent)->left;
if(w->isRed == true){
w->isRed = false;
(w->parent)->isRed = true;
right_rotate(x->parent);
w = (x->parent)->left;
}
if( (w->right)->isRed == false && (w->left)->isRed == false ){
w->isRed = true;
x = x->parent; // ??? is this correct ???
}
else{
if( (w->left)->isRed == false ){
(w->right)->isRed = false;
left_rotate(w);
w = (x->parent)->left;
}
else{ // TODO ??? is this correct ???
w->isRed = (x->parent)->isRed;
(x->parent)->isRed = false;
(w->left)->isRed = false;
right_rotate(x->parent);
x = root; // TODO ??? is this correct ???
}
}
}
}
x->isRed = false;
}
 
 
void RedBlackTree::print_tree(){
std::cout << "Print RedBlackTree status:" << std::endl;
inorder_tree_walk(root);
}
 
 
void RedBlackTree::inorder_tree_walk(RedBlack* x){
int oops =0;
if( x != 0 ){
inorder_tree_walk(x->left);
std::cout<< "(" << x->data << ": " << x->key() << ", " << x->high() << " : " << x->subtree_max << ") " ;
 
if( x->left != 0 ){
std::cout<< "L:(" << (x->left)->data << ", " << (x->left)->key() << ") " ;
if( x->key() < (x->left)->key()){
std::cout<<" !!! ";
oops = 1;
}
}
else{
std::cout<< "L:0 " ;
}
 
if( x->right != 0 ){
std::cout<< "R:(" << (x->right)->data << ", "<< (x->right)->key() << ") " ;
if( x->key() > (x->right)->key() ){
std::cout<<" !!! ";
oops = 1;
}
}
else{
std::cout<< "R:0 " ;
}
 
if(oops){
std::cout<<" ....... !!! Problem " << oops ;
}
std::cout << std::endl;
inorder_tree_walk(x->right);
}
}
 
// not an norder walk of the tree
void RedBlackTree::update_max(RedBlack* x){
Coord max_left, max_right;
if( x != 0 ){
update_max(x->left);
update_max(x->right);
// check for child
// if child is Nil then max = DBL_MIN
// could there be problems when comparing for max between two DBL_MIN ???
if( x->left == 0 ){
max_left = DBL_MIN ;
}
else{
max_left = (x->left)->subtree_max;
}
 
if( x->right == 0 ){
max_right = DBL_MIN ;
}
else{
max_right = (x->right)->subtree_max;
}
 
//find max of: x->high(), max_left, max_right
Coord temp_max;
temp_max = std::max( x->high(), max_left );
temp_max = std::max( temp_max, max_right );
x->subtree_max = temp_max;
 
}
}
 
 
RedBlack* RedBlackTree::tree_minimum(RedBlack* x){
_REDBLACK_PRINT( "==============================================================" << std::endl << "ENTER: tree_minimum" )
while( x->left <- 0 ) {
x->left = x;
}
_REDBLACK_PRINT( "RETURN: tree_minimum" << std::endl )
return x;
}
 
RedBlack* RedBlackTree::tree_successor(RedBlack* x){
_REDBLACK_PRINT( "==============================================================" << std::endl << "ENTER: tree_successor" )
if( x->right <- 0 ){
_REDBLACK_PRINT( "RETURN: tree_successor" << std::endl )
return tree_minimum(x);
}
RedBlack* y = x->parent;
_REDBLACK_PRINT( "y->parent: y->parent" )
while( y <- 0 && x == y->right ){
x = y;
y = y->parent;
}
_REDBLACK_PRINT( "RETURN: tree_successor" << std::endl )
return y;
}
 
 
};
 
/*
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 :
/trunk/Scribus/scribus/third_party/lib2geom/svg-path-parser.h
0,0 → 1,80
/**
* \file
* \brief parse SVG path specifications
*
* Copyright 2007 MenTaLguY <mental@rydia.net>
* Copyright 2007 Aaron Spike <aaron@ekips.org>
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
*/
 
#ifndef SEEN_SVG_PATH_PARSER_H
#define SEEN_SVG_PATH_PARSER_H
 
#include <vector>
#include <iterator>
#include <stdexcept>
#include "exception.h"
#include "point.h"
#include "svg-path.h"
 
namespace Geom {
 
void parse_svg_path(char const *str, SVGPathSink &sink) throw(SVGPathParseError);
 
inline std::vector<Path> parse_svg_path(char const *str) throw(SVGPathParseError) {
typedef std::vector<Path> Subpaths;
typedef std::back_insert_iterator<Subpaths> Inserter;
Subpaths subpaths;
Inserter iter(subpaths);
SVGPathGenerator<Inserter> generator(iter);
 
parse_svg_path(str, generator);
return subpaths;
}
 
inline std::vector<Path> read_svgd(char const * name) throw(SVGPathParseError) {
FILE* fi = fopen(name, "r");
if(fi == NULL) throw(std::runtime_error("Error opening file"));
char input[1024 * 10];
fgets(input, 1024 * 10, fi);
fclose(fi);
return parse_svg_path(input);
}
 
}
 
#endif
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/recursive-bezier-intersection.cpp
0,0 → 1,472
 
 
 
#include <basic-intersection.h>
#include <sbasis-to-bezier.h>
#include <exception.h>
 
 
#include <gsl/gsl_vector.h>
#include <gsl/gsl_multiroots.h>
 
 
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;
Point operator()(double t) 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 void
find_intersections_bezier_recursive(std::vector<std::pair<double, double> > & xs,
OldBezier a,
OldBezier b);
 
void
find_intersections_bezier_recursive( std::vector<std::pair<double, double> > &xs,
vector<Geom::Point> const & A,
vector<Geom::Point> const & B,
double /*precision*/) {
OldBezier a, b;
a.p = A;
b.p = B;
return find_intersections_bezier_recursive(xs, a,b);
}
 
 
/* The value of 1.0 / (1L<<14) is enough for most applications */
const double INV_EPS = (1L<<14);
 
/*
* split the curve at the midpoint, returning an array with the two parts
* Temporary storage is minimized by using part of the storage for the result
* to hold an intermediate value until it is no longer needed.
*/
void OldBezier::split(double t, OldBezier &left, OldBezier &right) const {
const unsigned sz = p.size();
Geom::Point Vtemp[sz][sz];
 
/* Copy control points */
std::copy(p.begin(), p.end(), Vtemp[0]);
 
/* Triangle computation */
for (unsigned i = 1; i < sz; i++) {
for (unsigned j = 0; j < sz - i; j++) {
Vtemp[i][j] = lerp(t, Vtemp[i-1][j], Vtemp[i-1][j+1]);
}
}
 
left.p.resize(sz);
right.p.resize(sz);
for (unsigned j = 0; j < sz; j++)
left.p[j] = Vtemp[j][0];
for (unsigned j = 0; j < sz; j++)
right.p[j] = Vtemp[sz-1-j][j];
}
 
#if 0
/*
* 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.
*/
Point OldBezier::operator()(double t) const {
const unsigned sz = p.size();
Geom::Point Vtemp[sz][sz];
 
/* Copy control points */
std::copy(p.begin(), p.end(), Vtemp[0]);
 
/* Triangle computation */
for (unsigned i = 1; i < sz; i++) {
for (unsigned j = 0; j < sz - i; j++) {
Vtemp[i][j] = lerp(t, Vtemp[i-1][j], Vtemp[i-1][j+1]);
}
}
return Vtemp[sz-1][0];
}
#endif
 
// suggested by Sederberg.
Point OldBezier::operator()(double t) const {
int n = p.size()-1;
double u, bc, tn, tmp;
int i;
Point r;
for(int dim = 0; dim < 2; dim++) {
u = 1.0 - t;
bc = 1;
tn = 1;
tmp = p[0][dim]*u;
for(i=1; i<n; i++){
tn = tn*t;
bc = bc*(n-i+1)/i;
tmp = (tmp + tn*bc*p[i][dim])*u;
}
r[dim] = (tmp + tn*t*p[n][dim]);
}
return r;
}
 
 
/*
* 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 not( ( minax > maxbx ) || ( minay > maxby )
|| ( minbx > maxax ) || ( minby > maxay ) );
}
 
/*
* Recursively intersect two curves keeping track of their real parameters
* and depths of intersection.
* The results are returned in a 2-D array of doubles indicating the parameters
* for which intersections are found. The parameters are in the order the
* intersections were found, which is probably not in sorted order.
* When an intersection is found, the parameter value for each of the two
* is stored in the index elements array, and the index is incremented.
*
* If either of the curves has subdivisions left before it is straight
* (depth > 0)
* that curve (possibly both) is (are) subdivided at its (their) midpoint(s).
* the depth(s) is (are) decremented, and the parameter value(s) corresponding
* to the midpoints(s) is (are) computed.
* Then each of the subcurves of one curve is intersected with each of the
* subcurves of the other curve, first by testing the bounding boxes for
* interference. If there is any bounding box interference, the corresponding
* subcurves are recursively intersected.
*
* If neither curve has subdivisions left, the line segments from the first
* to last control point of each segment are intersected. (Actually the
* only the parameter value corresponding to the intersection point is found).
*
* The apriori flatness test is probably more efficient than testing at each
* level of recursion, although a test after three or four levels would
* probably be worthwhile, since many curves become flat faster than their
* asymptotic rate for the first few levels of recursion.
*
* The bounding box test fails much more frequently than it succeeds, providing
* substantial pruning of the search space.
*
* Each (sub)curve is subdivided only once, hence it is not possible that for
* one final line intersection test the subdivision was at one level, while
* for another final line intersection test the subdivision (of the same curve)
* was at another. Since the line segments share endpoints, the intersection
* is robust: a near-tangential intersection will yield zero or two
* intersections.
*/
void recursively_intersect( OldBezier a, double t0, double t1, int deptha,
OldBezier b, double u0, double u1, int depthb,
std::vector<std::pair<double, double> > &parameters)
{
intersect_steps ++;
//std::cout << deptha << std::endl;
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 6; // 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;*/
}
 
struct rparams
{
OldBezier &A;
OldBezier &B;
};
 
/*static int
intersect_polish_f (const gsl_vector * x, void *params,
gsl_vector * f)
{
const double x0 = gsl_vector_get (x, 0);
const double x1 = gsl_vector_get (x, 1);
 
Geom::Point dx = ((struct rparams *) params)->A(x0) -
((struct rparams *) params)->B(x1);
 
gsl_vector_set (f, 0, dx[0]);
gsl_vector_set (f, 1, dx[1]);
 
return GSL_SUCCESS;
}*/
 
/*union dbl_64{
long long i64;
double d64;
};*/
 
/*static double EpsilonBy(double value, int eps)
{
dbl_64 s;
s.d64 = value;
s.i64 += eps;
return s.d64;
}*/
 
/*
static void intersect_polish_root (OldBezier &A, double &s,
OldBezier &B, double &t) {
const gsl_multiroot_fsolver_type *T;
gsl_multiroot_fsolver *sol;
 
int status;
size_t iter = 0;
 
const size_t n = 2;
struct rparams p = {A, B};
gsl_multiroot_function f = {&intersect_polish_f, n, &p};
 
double x_init[2] = {s, t};
gsl_vector *x = gsl_vector_alloc (n);
 
gsl_vector_set (x, 0, x_init[0]);
gsl_vector_set (x, 1, x_init[1]);
 
T = gsl_multiroot_fsolver_hybrids;
sol = gsl_multiroot_fsolver_alloc (T, 2);
gsl_multiroot_fsolver_set (sol, &f, x);
 
do
{
iter++;
status = gsl_multiroot_fsolver_iterate (sol);
 
if (status) // check if solver is stuck
break;
 
status =
gsl_multiroot_test_residual (sol->f, 1e-12);
}
while (status == GSL_CONTINUE && iter < 1000);
 
s = gsl_vector_get (sol->x, 0);
t = gsl_vector_get (sol->x, 1);
 
gsl_multiroot_fsolver_free (sol);
gsl_vector_free (x);
// This code does a neighbourhood search for minor improvements.
double best_v = L1(A(s) - B(t));
//std::cout << "------\n" << best_v << std::endl;
Point best(s,t);
while (true) {
Point trial = best;
double trial_v = best_v;
for(int nsi = -1; nsi < 2; nsi++) {
for(int nti = -1; nti < 2; nti++) {
Point n(EpsilonBy(best[0], nsi),
EpsilonBy(best[1], nti));
double c = L1(A(n[0]) - B(n[1]));
//std::cout << c << "; ";
if (c < trial_v) {
trial = n;
trial_v = c;
}
}
}
if(trial == best) {
//std::cout << "\n" << s << " -> " << s - best[0] << std::endl;
//std::cout << t << " -> " << t - best[1] << std::endl;
//std::cout << best_v << std::endl;
s = best[0];
t = best[1];
return;
} else {
best = trial;
best_v = trial_v;
}
}
}*/
 
 
void find_intersections_bezier_recursive( std::vector<std::pair<double, double> > &xs,
OldBezier a, OldBezier b)
{
if( intersect_BB( a, b ) )
{
recursively_intersect( a, 0., 1., wangs_theorem(a),
b, 0., 1., wangs_theorem(b),
xs);
}
/*for(unsigned i = 0; i < xs.size(); i++)
intersect_polish_root(a, xs[i].first,
b, xs[i].second);*/
std::sort(xs.begin(), xs.end());
}
 
 
};
 
/*
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 :
/trunk/Scribus/scribus/third_party/lib2geom/angle.h
1,53 → 1,326
/**
* \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
/**
* \file
* \brief Various trigoniometric helper functions
*//*
* Authors:
* Johan Engelen <goejendaagh@zonnet.nl>
* Marco Cecchetti <mrcekets at gmail.com>
* Krzysztof Kosiński <tweenk.pl@gmail.com>
*
* Copyright (C) 2007-2010 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
 
#include <cmath>
#include <boost/operators.hpp>
#include "exception.h"
#include "coord.h"
#include "point.h"
 
namespace Geom {
 
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
 
/** @brief Wrapper for angular values.
*
* This class is a convenience wrapper that implements the behavior generally expected of angles,
* like addition modulo \f$2\pi\f$. The value returned from the default conversion
* to <tt>double</tt> is in the range \f$[-\pi, \pi)\f$ - the convention used by C's
* math library.
*
* @ingroup Primitives
*/
class Angle
: boost::additive< Angle
, boost::equality_comparable< Angle
> >
{
public:
Angle(Coord v) : _angle(v + M_PI) { normalize(); } // this can be called implicitly
explicit Angle(Point p) : _angle(atan2(p) + M_PI) {}
Angle(Point a, Point b) : _angle(angle_between(a, b) + M_PI) {}
operator Coord() const { return radians(); }
Angle &operator+=(Angle const &o) {
_angle += o._angle;
normalize();
return *this;
}
Angle &operator-=(Angle const &o) {
_angle -= o._angle;
normalize();
return *this;
}
bool operator==(Angle const &o) const {
return _angle == o._angle;
}
 
/** @brief Get the angle as radians.
* @return Number in range \f$[-\pi, \pi)\f$. */
Coord radians() const {
return _angle - M_PI;
}
/** @brief Get the angle as radians starting from zero.
* @return Number in range \f$[0, 2\pi)\f$. 0 corresponds to \f$-\pi\f$ for radians(). */
Coord radians0() const {
return _angle;
}
/** @brief Get the angle as degrees in math convention.
* @return Number in range [-180, 180) obtained by scaling the result of radians()
* by \f$180/\pi\f$. */
Coord degrees() const { return radians() / M_PI * 180.0; }
/** @brief Get the angle as degrees in clock convention.
* This method converts the angle to the "clock convention": angles start from the +Y axis
* and grow clockwise. This means that 0 corresponds to \f$\pi/2\f$ radians,
* 90 to 0 radians, 180 to \f$-\pi/2\f$ radians, and 270 to \f$\pi\f$ radians.
* @return A number in the range [0, 360).
*/
Coord degreesClock() const {
Coord ret = 90.0 - _angle / M_PI * 180.0;
if (ret < 0) ret += 360;
return ret;
}
/** @brief Convert the angle to canonical internal representation.
* Use this periodically to avoid loss of precision when adding lots of angles together. */
void normalize() {
if (_angle >= 2*M_PI || _angle < 0) {
_angle = std::fmod(_angle, 2*M_PI);
if (_angle < 0) _angle += 2*M_PI;
}
}
static Angle from_radians0(Coord r) {
Angle a;
a._angle = std::fmod(r, 2*M_PI);
return a;
}
static Angle from_degrees(Coord d) {
Angle a(d * M_PI / 180);
return a;
}
static Angle from_degrees_clock(Coord d) {
// first make sure d is in [0, 360)
d = std::fmod(d, 360.0);
if (d < 0) d += 360.0;
Coord rad = M_PI/2 - d * M_PI / 180.0;
if (rad < 0) rad += 2*M_PI;
Angle a;
a._angle = rad;
return a;
}
private:
Angle() {}
Coord _angle; // this is always in [0, 2pi), so offset by pi from math library convention
friend class AngleInterval;
};
 
inline Angle distance_ccw(Angle const &as, Angle const &ae) {
Coord s = as.radians0();
Coord e = ae.radians0();
Coord dist;
if (s <= e) dist = s - e;
else dist = 2*M_PI + s - e;
return Angle::from_radians0(dist);
}
inline Angle distance_cw(Angle const &as, Angle const &ae) {
return distance_ccw(ae, as);
}
 
inline Angle distance_abs(Angle const &as, Angle const &ae) {
Angle ret = distance_ccw(as, ae);
if (ret.radians0() > M_PI) {
return Angle::from_radians0(2*M_PI - ret.radians0());
} else {
return ret;
}
}
 
/** @brief Directed angular interval.
*
* Wrapper for directed angles with defined start and end values. Useful e.g. for representing
* the portion of an ellipse in an elliptical arc. Both extreme angles are contained
* in the interval (it is a closed interval). Angular intervals can also be interptered
* as functions \f$f: [0, 1] \to [-\pi, \pi)\f$, which return the start angle for 0,
* the end angle for 1, and interpolate linearly for other values. Note that such functions
* are not continuous if the interval contains the zero angle.
*
* This class is immutable - you cannot change the values of start and end angles
* without creating a new instance of this class.
*
* @ingroup Primitives
*/
class AngleInterval {
public:
AngleInterval(Angle const &s, Angle const &e, bool cw = false)
: _start_angle(cw ? e : s), _end_angle(cw ? s : e)
{}
AngleInterval(double s, double e, bool cw = false)
: _start_angle(cw ? e : s), _end_angle(cw ? s : e)
{}
/** @brief Get the angular coordinate of the interval's initial point
* @return Angle in range \f$[0,2\pi)\f$ corresponding to the start of arc */
Angle const &initialAngle() const { return _start_angle; }
/** @brief Get the angular coordinate of the interval's final point
* @return Angle in range \f$[0,2\pi)\f$ corresponding to the end of arc */
Angle const &finalAngle() const { return _end_angle; }
bool isDegenerate() const { return initialAngle() == finalAngle(); }
/** @brief Get an angle corresponding to the specified time value. */
Angle angleAt(Coord t) const {
Coord span = distance_ccw(_start_angle, _end_angle).radians0();
Angle ret;
ret._angle = std::fmod(_start_angle.radians0() + span * t, 2*M_PI);
return ret;
}
Angle operator()(Coord t) const { return angleAt(t); }
/** @brief Find an angle nearest to the specified time value.
* @param a Query angle
* @return If the interval contains the query angle, a number from the range [0, 1]
* such that a = angleAt(t); otherwise, 0 or 1, depending on which extreme
* angle is nearer. */
Coord nearestAngle(Angle const &a) const {
Coord dist = distance_ccw(_start_angle, a).radians0();
Coord span = distance_ccw(_start_angle, _end_angle).radians0();
if (dist <= span) return dist / span;
else return distance_abs(_start_angle, a).radians0() > distance_abs(_end_angle, a) ? 1.0 : 0.0;
}
/** @brief Check whether the interval includes the given angle. */
bool contains(Angle const &a) const {
Coord s = _start_angle.radians0();
Coord e = _end_angle.radians0();
Coord x = a.radians0();
// If s <= e, then the +X axis (the angular origin) is contained in the interval.
if (s <= e) return x >= s && x <= e;
else return x >= e || x <= s;
}
protected:
AngleInterval() {}
Angle _start_angle;
Angle _end_angle;
};
 
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;}
 
/*
* start_angle and angle must belong to [0, 2PI[
* and angle must belong to the cirsular arc defined by
* start_angle, end_angle and with rotation direction cw
*/
inline
double map_circular_arc_on_unit_interval( double angle, double start_angle, double end_angle, bool cw = true )
{
double d = end_angle - start_angle;
double t = angle - start_angle;
if ( !cw )
{
d = -d;
t = -t;
}
d = std::fmod(d, 2*M_PI);
t = std::fmod(t, 2*M_PI);
if ( d < 0 ) d += 2*M_PI;
if ( t < 0 ) t += 2*M_PI;
return t / d;
}
 
inline
Coord map_unit_interval_on_circular_arc(Coord t, double start_angle, double end_angle, bool cw = true)
{
double sweep_angle = end_angle - start_angle;
if ( !cw ) sweep_angle = -sweep_angle;
sweep_angle = std::fmod(sweep_angle, 2*M_PI);
if ( sweep_angle < 0 ) sweep_angle += 2*M_PI;
 
Coord angle = start_angle;
if ( cw )
{
angle += sweep_angle * t;
}
else
{
angle -= sweep_angle * t;
}
angle = std::fmod(angle, 2*M_PI);
if (angle < 0) angle += 2*M_PI;
return angle;
}
 
/*
* Return true if the given angle is contained in the circular arc determined
* by the passed angles.
*
* a: the angle to be tested
* sa: the angle the arc start from
* ia: an angle strictly inner to the arc
* ea: the angle the arc end to
*
* prerequisite: the inner angle has to be not equal (mod 2PI) to the start
* or the end angle, except when they are equal each other, too.
* warning: when sa == ea (mod 2PI) they define a whole circle
* if ia != sa (mod 2PI), on the contrary if ia == sa == ea (mod 2PI)
* they define a single point.
*/
inline
bool arc_contains (double a, double sa, double ia, double ea)
{
a -= sa;
a = std::fmod(a, 2*M_PI);
if (a < 0) a += 2*M_PI;
ia -= sa;
ia = std::fmod(ia, 2*M_PI);
if (ia < 0) ia += 2*M_PI;
ea -= sa;
ea = std::fmod(ea, 2*M_PI);
if (ea < 0) ea += 2*M_PI;
 
if (ia == 0 && ea == 0) return (a == 0);
if (ia == 0 || ia == ea)
{
THROW_RANGEERROR ("arc_contains: passed angles do not define an arc");
}
return (ia < ea && a <= ea) || (ia > ea && (a >= ea || a == 0));
}
 
} // end 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 :
/trunk/Scribus/scribus/third_party/lib2geom/sbasis-2d.h
1,3 → 1,38
/**
* \file
* \brief \todo brief description
*
* Authors:
* Nathan Hurst <?@?.?>
* JFBarraud <?@?.?>
*
* Copyright 2006-2008 authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
*/
 
#ifndef SEEN_SBASIS_2D_H
#define SEEN_SBASIS_2D_H
#include <vector>
16,7 → 51,12
v 0,2
*/
double a[4];
Linear2d() {}
Linear2d() {
a[0] = 0;
a[1] = 0;
a[2] = 0;
a[3] = 0;
}
Linear2d(double aa) {
for(unsigned i = 0 ; i < 4; i ++)
a[i] = aa;
273,7 → 313,7
 
SBasis2d integral(SBasis2d const &c);
 
SBasis2d derivative(SBasis2d const &a);
SBasis2d partial_derivative(SBasis2d const &a, int dim);
 
SBasis2d sqrt(SBasis2d const &a, int k);
 
310,6 → 350,12
return out_file;
}
 
D2<SBasis>
sb2dsolve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B, unsigned degmax=2);
 
D2<SBasis>
sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B);
 
};
 
/*
/trunk/Scribus/scribus/third_party/lib2geom/quadtree.cpp
1,5 → 1,17
#include "quadtree.h"
#include <quadtree.h>
 
namespace Geom{
Quad* QuadTree::search(Rect const &r) {
return search(r[0].min(), r[1].min(),
r[0].max(), r[1].max());
}
 
void QuadTree::insert(Rect const &r, int shape) {
insert(r[0].min(), r[1].min(),
r[0].max(), r[1].max(), shape);
}
 
 
Quad* QuadTree::search(double x0, double y0, double x1, double y1) {
Quad *q = root;
31,9 → 43,94
}
return q;
}
 
 
/*
Comments by Vangelis (use with caution :P )
 
Insert Rect (x0, y0), (x1, y1) in the QuadTree Q.
 
===================================================================================
* QuadTree Q has: Quadtree's Quad root R, QuadTree's bounding box B.
 
* Each Quad has a Quad::data where we store the id of the Rect that belong to
this Quad. (In reality we'll store a pointer to the shape).
 
* Each Quad has 4 Quad children: 0, 1, 2, 3. Each child Quad represents one of the following quarters
of the bounding box B:
 
+---------------------+
| | |
| NW=0 | NE=1 |
| | |
| | |
+---------------------+
| | |
| SW=2 | SE=3 |
| | |
| | |
+---------------------+
 
Each Quad can further be divided in 4 Quads as above and so on. Below there is an example
Root
/ || \
/ / \ \
0 1 2 3
/\
/ | | \
0 1 2 3
 
+---------------------+
| | 1-0 | 1-1|
| 0 | | |
| |-----|----|
| | 1-2 | 1-3|
| | | |
+---------------------+
| | |
| | |
| 2 | 3 |
| | |
+---------------------+
 
 
 
===================================================================================
Insert Rect (x0, y0), (x1, y1) in the QuadTree Q. Algorithm:
1) check if Rect is bigger than QuadTree's bounding box
2) find in which Quad we should add the Rect:
 
 
 
-----------------------------------------------------------------------------------
How we find in which Quad we should add the Rect R:
 
Q = Quadtree's Quad root
B = QuadTree's bounding box B
WHILE (Q) {
IF ( Rect cannot fit in one unique quarter of B ){
Q = current Quad ;
BREAK;
}
IF ( Rect can fit in the quarter I ) {
IF (Q.children[I] doesn't exist) {
create the Quad Q.children[I];
}
B = bounding box of the Quad Q.children[I] ;
Q = Q.children[I] ;
CHECK(R, B) ;
}
}
add Rect R to Q ;
 
 
*/
void QuadTree::insert(double x0, double y0, double x1, double y1, int shape) {
// loop until a quad would break the box.
 
// empty root => empty QuadTree. Create initial bounding box (0,0), (1,1)
if(root == 0) {
root = new Quad;
43,14 → 140,18
by1 = 1;
}
Quad *q = root;
 
//A temp bounding box. Same as root's bounting box (ie of the whole QuadTree)
double bxx0 = bx0, bxx1 = bx1;
double byy0 = by0, byy1 = by1;
 
while((bxx0 > x0) ||
(bxx1 < x1) ||
(byy0 > y0) ||
(byy1 < y1)) { // too small initial size - double
(byy1 < y1)) {
// QuadTree has small size, can't accomodate new rect. Double the size:
unsigned i = 0;
 
if(bxx0 > x0) {
bxx0 = 2*bxx0 - bxx1;
i += 1;
64,15 → 165,22
byy1 = 2*byy1 - byy0;
}
q = new Quad;
q->children[i] = root;
root = q;
//check if root is empty (no rects, no quad children)
if( clean_root() ){
root = q;
}
else{
q->children[i] = root;
root = q;
}
bx0 = bxx0;
bx1 = bxx1;
by0 = byy0;
by1 = byy1;
}
 
while(q) {
// Find the center of the temp bounding box
double cx = (bxx0 + bxx1)/2;
double cy = (byy0 + byy1)/2;
unsigned i = 0;
80,21 → 188,41
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
} else{
// rect does not fit in one unique quarter (in X axis) of the temp bounding box
break;
}
if(y0 >= cy) {
i += 2;
byy0 = cy;
} else if(y1 <= cy) {
byy1 = cy;
} else
} else{
// rect does not fit in one unique quarter (in Y axis) of the temp bounding box
break;
}
 
// check if rect's bounding box has size 1x1. This means that rect is defined by 2 points
// that are in the same place.
if( ( fabs(bxx0 - bxx1) < 1.0 ) && ( fabs(byy0 - byy1) < 1.0 )){
bxx0 = floor(bxx0);
bxx1 = floor(bxx1);
byy0 = floor(byy0);
byy1 = floor(byy1);
break;
}
 
/*
1 rect does fit in one unique quarter of the temp bounding box. And we have found which.
2 temp bounding box = bounding box of this quarter.
3 "Go in" this quarter (create if doesn't exist)
*/
assert(i < 4);
Quad *qq = q->children[i];
if(qq == 0) {
118,14 → 246,43
}
 
/*
Returns:
false: if root isn't empty
true: if root is empty it cleans root
*/
bool QuadTree::clean_root() {
assert(root);
 
// false if root *has* rects assigned to it.
bool all_clean = root->data.empty();
 
// if root has children we get false
for(unsigned i = 0; i < 4; i++)
{
if(root->children[i])
{
all_clean = false;
}
}
 
if(all_clean)
{
delete root;
root=0;
return true;
}
return false;
}
 
};
 
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(substatement-open . 0))
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
c-brace-offset:0
fill-column:99
End:
vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
*/
 
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/sbasis-math.cpp
34,19 → 34,27
//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"
#include <sbasis-math.h>
 
#include <d2-sbasis.h>
#include <stdio.h>
#include <math.h>
//#define ZERO 1e-3
 
#include <cstdio>
#include <cmath>
#include "angle.h"
 
namespace Geom {
 
 
//-|x|-----------------------------------------------------------------------
/** Return the absolute value of a function pointwise.
\param f function
*/
Piecewise<SBasis> abs(SBasis const &f){
return abs(Piecewise<SBasis>(f));
}
/** Return the absolute value of a function pointwise.
\param f function
*/
Piecewise<SBasis> abs(Piecewise<SBasis> const &f){
Piecewise<SBasis> absf=partition(f,roots(f));
for (unsigned i=0; i<absf.size(); i++){
56,15 → 64,27
}
 
//-max(x,y), min(x,y)--------------------------------------------------------
/** Return the greater of the two functions pointwise.
\param f, g two functions
*/
Piecewise<SBasis> max( SBasis const &f, SBasis const &g){
return max(Piecewise<SBasis>(f),Piecewise<SBasis>(g));
}
/** Return the greater of the two functions pointwise.
\param f, g two functions
*/
Piecewise<SBasis> max(Piecewise<SBasis> const &f, SBasis const &g){
return max(f,Piecewise<SBasis>(g));
}
/** Return the greater of the two functions pointwise.
\param f, g two functions
*/
Piecewise<SBasis> max( SBasis const &f, Piecewise<SBasis> const &g){
return max(Piecewise<SBasis>(f),g);
}
/** Return the greater of the two functions pointwise.
\param f, g two functions
*/
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);
75,20 → 95,38
return max;
}
 
/** Return the more negative of the two functions pointwise.
\param f, g two functions
*/
Piecewise<SBasis>
min( SBasis const &f, SBasis const &g){ return -max(-f,-g); }
/** Return the more negative of the two functions pointwise.
\param f, g two functions
*/
Piecewise<SBasis>
min(Piecewise<SBasis> const &f, SBasis const &g){ return -max(-f,-g); }
/** Return the more negative of the two functions pointwise.
\param f, g two functions
*/
Piecewise<SBasis>
min( SBasis const &f, Piecewise<SBasis> const &g){ return -max(-f,-g); }
/** Return the more negative of the two functions pointwise.
\param f, g two functions
*/
Piecewise<SBasis>
min(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g){ return -max(-f,-g); }
 
 
//-sign(x)---------------------------------------------------------------
/** Return the sign of the two functions pointwise.
\param f function
*/
Piecewise<SBasis> signSb(SBasis const &f){
return signSb(Piecewise<SBasis>(f));
}
/** Return the sign of the two functions pointwise.
\param f function
*/
Piecewise<SBasis> signSb(Piecewise<SBasis> const &f){
Piecewise<SBasis> sign=partition(f,roots(f));
for (unsigned i=0; i<sign.size(); i++){
111,7 → 149,7
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) {
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));
138,10 → 176,16
return sqrtf0;
}
 
/** Compute the sqrt of a function.
\param f function
*/
Piecewise<SBasis> sqrt(SBasis const &f, double tol, int order){
return sqrt(max(f,Linear(tol*tol)),tol,order);
}
 
/** Compute the sqrt of a function.
\param f function
*/
Piecewise<SBasis> sqrt(Piecewise<SBasis> const &f, double tol, int order){
Piecewise<SBasis> result;
Piecewise<SBasis> zero = Piecewise<SBasis>(Linear(tol*tol));
158,9 → 202,24
 
//-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);}
/** Compute the sine of a function.
\param f function
\param tol maximum error
\param order maximum degree polynomial to use
*/
Piecewise<SBasis> sin( SBasis const &f, double tol, int order){return(cos(-f+M_PI/2,tol,order));}
/** Compute the sine of a function.
\param f function
\param tol maximum error
\param order maximum degree polynomial to use
*/
Piecewise<SBasis> sin(Piecewise<SBasis> const &f, double tol, int order){return(cos(-f+M_PI/2,tol,order));}
 
/** Compute the cosine of a function.
\param f function
\param tol maximum error
\param order maximum degree polynomial to use
*/
Piecewise<SBasis> cos(Piecewise<SBasis> const &f, double tol, int order){
Piecewise<SBasis> result;
for (unsigned i=0; i<f.size(); i++){
171,6 → 230,11
return result;
}
 
/** Compute the cosine of a function.
\param f function
\param tol maximum error
\param order maximum degree polynomial to use
*/
Piecewise<SBasis> cos( SBasis const &f, double tol, int order){
double alpha = (f.at0()+f.at1())/2.;
SBasis x = f-alpha;
205,7 → 269,6
return c0;
}
 
 
//--1/x------------------------------------------------------------
//TODO: this implementation is just wrong. Remove or redo!
 
265,20 → 328,46
}
 
Piecewise<SBasis> reciprocal(SBasis const &f, double tol, int order){
Piecewise<SBasis> reciprocal_fn=reciprocalOnDomain(bounds_fast(f), tol);
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> reciprocal_fn=reciprocalOnDomain(*bounds_fast(f), tol);
Piecewise<SBasis> result=compose(reciprocal_fn,f);
truncateResult(result,order);
return(result);
}
 
/**
* \brief Retruns a Piecewise SBasis with prescribed values at prescribed times.
*
* \param times: vector of times at which the values are given. Should be sorted in increasing order.
* \param values: vector of prescribed values. Should have the same size as times and be sorted accordingly.
* \param smoothness: (defaults to 1) regularity class of the result: 0=piecewise linear, 1=continuous derivative, etc...
*/
Piecewise<SBasis> interpolate(std::vector<double> times, std::vector<double> values, unsigned smoothness){
assert ( values.size() == times.size() );
if ( values.size() == 0 ) return Piecewise<SBasis>();
if ( values.size() == 1 ) return Piecewise<SBasis>(values[0]);//what about time??
 
SBasis sk = shift(Linear(1.),smoothness);
SBasis bump_in = integral(sk);
bump_in -= bump_in.at0();
bump_in /= bump_in.at1();
SBasis bump_out = reverse( bump_in );
Piecewise<SBasis> result;
result.cuts.push_back(times[0]);
for (unsigned i = 0; i<values.size()-1; i++){
result.push(bump_out*values[i]+bump_in*values[i+1],times[i+1]);
}
return result;
}
 
}
 
/*
Local Variables:
mode:c++
288,4 → 377,4
fill-column:99
End:
*/
// vim: filetype = cpp:expandtab:shiftwidth = 4:tabstop = 8:softtabstop = 4:encoding = utf-8:textwidth = 99 :
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/curves.h
0,0 → 1,58
/**
* \file
* \brief Include all curve types
*//*
* Authors:
* MenTaLguY <mental@rydia.net>
* Marco Cecchetti <mrcekets at gmail.com>
*
* Copyright 2007-2008 authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*/
 
#ifndef _2GEOM_CURVES_H_
#define _2GEOM_CURVES_H_
 
 
#include "curve.h"
#include "sbasis-curve.h"
#include "bezier-curve.h"
#include "hvlinesegment.h"
#include "elliptical-arc.h"
#include "svg-elliptical-arc.h"
 
#endif // _2GEOM_CURVES_H_
 
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
 
/trunk/Scribus/scribus/third_party/lib2geom/convex-cover.cpp
29,9 → 29,12
*
*/
 
#include "convex-cover.h"
#include <convex-cover.h>
#include <exception.h>
#include <algorithm>
#include <map>
#include <assert.h>
 
/** Todo:
+ modify graham scan to work top to bottom, rather than around angles
+ intersection
46,6 → 49,7
using std::vector;
using std::map;
using std::pair;
using std::make_pair;
 
namespace Geom{
 
61,12 → 65,16
public:
Point o;
angle_cmp(Point o) : o(o) {}
 
#if 0
bool
operator()(Point a, Point b) {
// not remove this check or std::sort could crash
if (a == b) return false;
Point da = a - o;
Point db = b - o;
if (da == -db) return false;
 
#if 1
double aa = da[0];
double ab = db[0];
91,6 → 99,23
return L2sq(da) < L2sq(db);
return false;
}
#else
bool operator() (Point const& a, Point const& b)
{
// not remove this check or std::sort could generate
// a segmentation fault because it needs a strict '<'
// but due to round errors a == b doesn't mean dxy == dyx
if (a == b) return false;
Point da = a - o;
Point db = b - o;
if (da == -db) return false;
double dxy = da[X] * db[Y];
double dyx = da[Y] * db[X];
if (dxy > dyx) return true;
else if (dxy < dyx) return false;
return L2sq(da) < L2sq(db);
}
#endif
};
 
void
97,10 → 122,10
ConvexHull::find_pivot() {
// Find pivot P;
unsigned pivot = 0;
for(unsigned i = 1; i < boundary.size(); i++)
for (unsigned i = 1; i < boundary.size(); i++)
if(boundary[i] <= boundary[pivot])
pivot = i;
 
std::swap(boundary[0], boundary[pivot]);
}
 
111,12 → 136,14
std::sort(boundary.begin()+1, boundary.end(), angle_cmp(boundary[0]));
}
 
 
void
ConvexHull::graham_scan() {
if (boundary.size() < 4) return;
unsigned stac = 2;
for(unsigned i = 2; i < boundary.size(); i++) {
double o = SignedTriangleArea(boundary[stac-2],
boundary[stac-1],
for(unsigned int i = 2; i < boundary.size(); i++) {
double o = SignedTriangleArea(boundary[stac-2],
boundary[stac-1],
boundary[i]);
if(o == 0) { // colinear - dangerous...
stac--;
124,8 → 151,8
} else { // remove concavity
while(o >= 0 && stac > 2) {
stac--;
o = SignedTriangleArea(boundary[stac-2],
boundary[stac-1],
o = SignedTriangleArea(boundary[stac-2],
boundary[stac-1],
boundary[i]);
}
}
136,6 → 163,8
 
void
ConvexHull::graham() {
if(is_degenerate()) // nothing to do
return;
find_pivot();
angle_sort();
graham_scan();
143,7 → 172,7
 
//Mathematically incorrect mod, but more useful.
int mod(int i, int l) {
return i >= 0 ?
return i >= 0 ?
i % l : (i % l) + l;
}
//OPT: usages can often be replaced by conditions
152,6 → 181,13
* 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::strict_left
* Tests if a point is left (outside) of a particular segment, n. */
bool
ConvexHull::is_strict_left(Point p, int n) {
return SignedTriangleArea((*this)[n], (*this)[n+1], p) > 0;
}
 
166,8 → 202,22
}
return -1;
}
//OPT: do a spread iteration - quasi-random with no repeats and full coverage.
 
 
/*** 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_strict_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_strict_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. */
176,6 → 226,14
return find_left(p) == -1;
}
 
/*** ConvexHull::strict_contains_point
* In order to test whether a point is strictly inside (not on the boundary) 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::strict_contains_point(Point p) {
return find_strict_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?*/
192,9 → 250,9
 
bool pushed = false;
 
bool pre = is_left(p, -1);
bool pre = is_strict_left(p, -1);
for(int i = 0; i < l; i++) {
bool cur = is_left(p, i);
bool cur = is_strict_left(p, i);
if(pre) {
if(cur) {
if(!pushed) {
211,7 → 269,7
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.
245,12 → 303,12
bool
ConvexHull::top_point_first() const {
std::vector<Point>::const_iterator pivot = boundary.begin();
for(std::vector<Point>::const_iterator it(boundary.begin()+1),
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]) &&
else if(((*it)[1] == (*pivot)[1]) &&
((*it)[0] < (*pivot)[0]))
pivot = it;
}
264,7 → 322,8
*/
bool
ConvexHull::no_colinear_points() const {
return true;
// XXX: implement me!
THROW_NOTIMPLEMENTED();
}
 
bool
281,47 → 340,81
}
 
 
/* 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;
int sgn(double x) {
if(x == 0) return 0;
return (x<0)?-1:1;
}
 
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;
}
bool same_side(Point L[2], Point xs[4]) {
int side = 0;
for(int i = 0; i < 4; i++) {
int sn = sgn(SignedTriangleArea(L[0], L[1], xs[i]));
if(sn and not side)
side = sn;
else if(sn != side) return false;
}
return make_pair(abridges, bbridges);
return true;
}
 
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]);
/** find bridging pairs between two convex hulls.
* this code is based on Hormoz Pirzadeh's masters thesis. There is room for optimisation:
* 1. reduce recomputation
* 2. use more efficient angle code
* 3. write as iterator
*/
std::vector<pair<int, int> > bridges(ConvexHull a, ConvexHull b) {
vector<pair<int, int> > ret;
// 1. find maximal points on a and b
int ai = 0, bi = 0;
// 2. find first copodal pair
double ap_angle = atan2(a[ai+1] - a[ai]);
double bp_angle = atan2(b[bi+1] - b[bi]);
Point L[2] = {a[ai], b[bi]};
while(ai < int(a.size()) or bi < int(b.size())) {
if(ap_angle == bp_angle) {
// In the case of parallel support lines, we must consider all four pairs of copodal points
{
assert(0); // untested
Point xs[4] = {a[ai-1], a[ai+1], b[bi-1], b[bi+1]};
if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
xs[2] = b[bi];
xs[3] = b[bi+2];
if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
xs[0] = a[ai];
xs[1] = a[ai+2];
if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
xs[2] = b[bi-1];
xs[3] = b[bi+1];
if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
}
ai++;
ap_angle += angle_between(a[ai] - a[ai-1], a[ai+1] - a[ai]);
L[0] = a[ai];
bi++;
bp_angle += angle_between(b[bi] - b[bi-1], b[bi+1] - b[bi]);
L[1] = b[bi];
std::cout << "parallel\n";
} else if(ap_angle < bp_angle) {
ai++;
ap_angle += angle_between(a[ai] - a[ai-1], a[ai+1] - a[ai]);
L[0] = a[ai];
Point xs[4] = {a[ai-1], a[ai+1], b[bi-1], b[bi+1]};
if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
} else {
bi++;
bp_angle += angle_between(b[bi] - b[bi-1], b[bi+1] - b[bi]);
L[1] = b[bi];
Point xs[4] = {a[ai-1], a[ai+1], b[bi-1], b[bi+1]};
if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
}
}
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() &&
while(it < a.boundary.size() &&
a.boundary[it][Y] > a.boundary[it-1][Y])
it++;
return it-1;
335,10 → 428,10
*/
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++;
347,10 → 440,10
(b.boundary[bl+1][Y] > a.boundary[al][Y])) {
bl++;
}
// al and bl now point to the top of the first pair of edges that overlap in y value
//double sweep_y = std::min(a.boundary[al][Y],
// b.boundary[bl][Y]);
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);
358,12 → 451,13
* (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 intersection(ConvexHull /*a*/, ConvexHull /*b*/) {
ConvexHull ret;
// int ai = 0, bi = 0;
// unsigned aj = a.boundary.size() - 1;
// unsigned bj = b.boundary.size() - 1;
/*
int ai = 0, bi = 0;
int aj = a.boundary.size() - 1;
int bj = b.boundary.size() - 1;
*/
/*while (true) {
if(a[ai]
}*/
370,6 → 464,11
return ret;
}
 
template <typename T>
T idx_to_pair(pair<T, T> p, int idx) {
return idx?p.second:p.first;
}
 
/*** ConvexHull merge(ConvexHull a, ConvexHull b);
* find the smallest convex hull that surrounds a and b.
*/
376,52 → 475,80
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;
std::cout << "---\n";
std::vector<pair<int, int> > bpair = bridges(a, b);
// Given our list of bridges {(pb1, qb1), ..., (pbk, qbk)}
// we start with the highest point in p0, q0, say it is p0.
// then the merged hull is p0, ..., pb1, qb1, ..., qb2, pb2, ...
// In other words, either of the two polygons vertices are added in order until the vertex coincides with a bridge point, at which point we swap.
 
unsigned state = (a[0][Y] < b[0][Y])?0:1;
ret.boundary.reserve(a.size() + b.size());
ConvexHull chs[2] = {a, b};
unsigned idx = 0;
for(unsigned k = 0; k < bpair.size(); k++) {
unsigned limit = idx_to_pair(bpair[k], state);
std::cout << bpair[k].first << " , " << bpair[k].second << "; "
<< idx << ", " << limit << ", s: "
<< state
<< " \n";
while(idx <= limit) {
ret.boundary.push_back(chs[state][idx++]);
}
state = 1-state;
idx = idx_to_pair(bpair[k], state);
}
while(idx < chs[state].size()) {
ret.boundary.push_back(chs[state][idx++]);
}
return ret;
 
/*
ab[-1] = 0;
bb[-1] = 0;
 
int i = -1;
int i = -1; // XXX: i is int but refers to vector indices
 
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(i >= (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(i >= (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(),
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
432,7 → 559,67
}
}*/
 
double ConvexHull::centroid_and_area(Geom::Point& centroid) const {
const unsigned n = boundary.size();
if (n < 2)
return 0;
if(n < 3) {
centroid = (boundary[0] + boundary[1])/2;
return 0;
}
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(boundary[j], boundary[i]);
atmp += ai;
centroid_tmp += (boundary[j] + boundary[i])*ai; // first moment.
}
if (atmp != 0) {
centroid = centroid_tmp / (3 * atmp);
}
return atmp / 2;
}
 
// TODO: This can be made lg(n) using golden section/fibonacci search three starting points, say 0,
// n/2, n-1 construct a new point, say (n/2 + n)/2 throw away the furthest boundary point iterate
// until interval is a single value
Point const * ConvexHull::furthest(Point direction) const {
Point const * p = &boundary[0];
double d = dot(*p, direction);
for(unsigned i = 1; i < boundary.size(); i++) {
double dd = dot(boundary[i], direction);
if(d < dd) {
p = &boundary[i];
d = dd;
}
}
return p;
}
 
 
// returns (a, (b,c)), three points which define the narrowest diameter of the hull as the pair of
// lines going through b,c, and through a, parallel to b,c TODO: This can be made linear time by
// moving point tc incrementally from the previous value (it can only move in one direction). It
// is currently n*O(furthest)
double ConvexHull::narrowest_diameter(Point &a, Point &b, Point &c) {
Point tb = boundary.back();
double d = INFINITY;
for(unsigned i = 0; i < boundary.size(); i++) {
Point tc = boundary[i];
Point n = -rot90(tb-tc);
Point ta = *furthest(n);
double td = dot(n, ta-tb)/dot(n,n);
if(td < d) {
a = ta;
b = tb;
c = tc;
d = td;
}
tb = tc;
}
return d;
}
 
};
 
/*
439,12 → 626,9
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(substatement-open . 0))
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
c-brace-offset:0
fill-column:99
End:
vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
*/
 
 
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/linear.h
1,5 → 1,6
/*
* linear.h - Linear fragment function class
/**
* \file
* \brief Linear fragment function class
*
* Authors:
* Nathan Hurst <njh@mail.csse.monash.edu.au>
33,9 → 34,18
 
#ifndef SEEN_LINEAR_H
#define SEEN_LINEAR_H
#include "interval.h"
#include "isnan.h"
#include "interval.h"
 
 
//#define USE_SBASIS_OF
 
#ifdef USE_SBASIS_OF
 
#include "linear-of.h"
 
#else
 
namespace Geom{
 
inline double lerp(double t, double a, double b) { return a*(1-t) + b*t; }
42,37 → 52,13
 
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(double aa) {a[0] = aa; a[1] = aa;}
 
Linear(Hat h) {
a[0] = double(h);
a[1] = double(h);
}
 
double operator[](const int i) const {
assert(i >= 0);
assert(i < 2);
88,7 → 74,7
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 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]; }
99,14 → 85,14
//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)); }
inline OptInterval bounds_exact() const { return Interval(a[0], a[1]); }
inline OptInterval bounds_fast() const { return bounds_exact(); }
inline OptInterval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
 
operator Tri() const {
double tri() const {
return a[1] - a[0];
}
operator Hat() const {
double hat() const {
return (a[1] + a[0])/2;
}
};
168,6 → 154,19
a[0] /= b; a[1] /= b;
return a;
}
};
 
}
#endif
 
#endif //SEEN_LINEAR_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 :
/trunk/Scribus/scribus/third_party/lib2geom/rtree.cpp
0,0 → 1,1350
#include <rtree.h>
#include <limits>
 
/*
Based on source (BibTex):
@inproceedings{DBLP:conf/sigmod/Guttman84,
author = {Antonin Guttman},
title = {R-Trees: A Dynamic Index Structure for Spatial Searching},
booktitle = {SIGMOD Conference},
year = {1984},
pages = {47-57},
ee = {http://doi.acm.org/10.1145/602259.602266, db/conf/sigmod/Guttman84.html},
}
*/
 
/*
#define _RTREE_PRINT(x) std::cout << x << std::endl;
#define _RTREE_PRINT_TREE( x, y ) print_tree( x, y );
#define _RTREE_PRINT_TREE_INS( x, y, z ) print_tree( x, y, z );
*/
//comment the following if you want output during RTree operations
 
 
#define _RTREE_PRINT(x) ;
#define _RTREE_PRINT_TREE( x, y ) ;
#define _RTREE_PRINT_TREE_INS( x, y, z ) ;
 
 
 
/*
TODO 1
some if(non_leaf)
else // leaf
could be eliminated when function starts from a leaf
do leaf action
then repeat function for non-leafs only
candidates:
- adjust_tree
- condense_tree
 
TODO 2
generalize in a different way the splitting techniques
 
*/
 
 
namespace Geom{
 
/*=============================================================================
insert
===============================================================================
insert a new index entry E into the R-tree:
 
I1) find position of new record:
choose_node will find a leaf node L (position) in which to place r
I2) add record to leaf node:
if L has room for another entry install E
else split_node will obtain L and LL containing E and all the old entries of L
from the available splitting strategies we chose quadtratic-cost algorithm (just to begin
with sth)
// TODO implement more of them
I3) propagate changes upward:
Invoke adjust_tree on L, also passing LL if a split was performed.
I4) grow tree taller:
if a node spilt propagation, cuased the root to split
create new root whose children are the 2 resulting nodes
*/
 
void RTree::insert( Rect const &r, unsigned shape ){
_RTREE_PRINT("\n=====================================");
_RTREE_PRINT("insert");
RTreeRecord_Leaf* leaf_record= new RTreeRecord_Leaf( r, shape );
insert( *leaf_record );
}
 
 
 
void RTree::insert( const RTreeRecord_Leaf &leaf_record,
const bool &insert_high /* false */,
const unsigned &stop_height /* 0 */,
const RTreeRecord_NonLeaf &nonleaf_record /* 0 */
)
{
_RTREE_PRINT("\n--------------");
_RTREE_PRINT("insert private. element:" << leaf_record.data << " insert high:" << insert_high << " stop height:" << stop_height );
RTreeNode *position = 0;
 
// if tree is unsued create the root Node, not described in source, stupid me :P
if(root == 0){
root = new RTreeNode();
}
 
_RTREE_PRINT("I1"); // I1
if( insert_high == false ){ // choose leaf node
position = choose_node( leaf_record.bounding_box );
}
else { // choose nonleaf node
position = choose_node( nonleaf_record.bounding_box, insert_high, stop_height );
}
_RTREE_PRINT("leaf node chosen: " );
_RTREE_PRINT_TREE( position , 0 );
std::pair< RTreeNode*, RTreeNode* > node_division;
 
bool split_performed = false;
 
if( position->children_nodes.size() > 0 ){ // non-leaf node: position
// we must reach here only to insert high non leaf node, not insert leaf node
assert( insert_high == true );
 
// put new element in node temporarily. Later on, if we need to, we will split the node.
position->children_nodes.push_back( nonleaf_record );
if( position->children_nodes.size() <= max_records ){
_RTREE_PRINT("I2 nonleaf: no split: " << position->children_nodes.size() ); // I2
}
else{
_RTREE_PRINT("I2 nonleaf: split: " << position->children_nodes.size() ); // I2
node_division = split_node( position );
split_performed = true;
}
 
}
else { // leaf node: position:
// we must reach here only to insert leaf node, not insert high non leaf node
assert( insert_high == false );
 
 
// put new element in node temporarily. Later on, if we need to, we will split the node.
position->children_leaves.push_back( leaf_record );
if( position->children_leaves.size() <= max_records ){
_RTREE_PRINT("I2 leaf: no split: " << position->children_leaves.size() ); // I2
}
else{
_RTREE_PRINT("I2 leaf: split: " << position->children_leaves.size() << " max_records:" << max_records); // I2
node_division = split_node( position );
split_performed = true;
_RTREE_PRINT(" group A");
_RTREE_PRINT_TREE( node_division.first , 3 );
_RTREE_PRINT(" group B");
_RTREE_PRINT_TREE( node_division.second , 3 );
}
 
}
 
_RTREE_PRINT("I3"); // I3
bool root_split_performed = adjust_tree( position, node_division, split_performed );
_RTREE_PRINT("root split: " << root_split_performed);
 
 
// _RTREE_PRINT("TREE:");
// print_tree( root , 2 );
 
_RTREE_PRINT("I4"); // I4
if( root_split_performed ){
std::pair<RTreeNode*, RTreeNode*> root_division;
root_division = quadratic_split( root ); // AT5
 
Rect first_record_bounding_box;
Rect second_record_bounding_box;
 
RTreeRecord_NonLeaf first_new_record = create_nonleaf_record_from_rtreenode( first_record_bounding_box, root_division.first );
RTreeRecord_NonLeaf second_new_record = create_nonleaf_record_from_rtreenode( second_record_bounding_box, root_division.second );
_RTREE_PRINT(" 1st:");
_RTREE_PRINT_TREE( first_new_record.data, 5 );
_RTREE_PRINT(" 2nd:");
_RTREE_PRINT_TREE( second_new_record.data, 5 );
 
// *new* root is by definition non-leaf. Install the new records there
RTreeNode* new_root = new RTreeNode();
new_root->children_nodes.push_back( first_new_record );
new_root->children_nodes.push_back( second_new_record );
 
delete root;
 
root = new_root;
tree_height++; // increse tree height
 
_RTREE_PRINT_TREE( root, 5 );
sanity_check( root, 0 );
}
_RTREE_PRINT("done");
 
/*
the node_division.second is saved on the tree
the node_division.first was copied in existing tree in node
so we don't need this anymore
*/
delete node_division.first;
}
 
/* I1 =========================================================================
 
original: choose_node will find a leaf node L in which to place r
changed to choose_node will find a node L in which to place r
the node L is:
non-leaf: if flag is set. the height of the node is insert_at_height
leaf: if flag is NOT set
 
1) Initialize: set N to be the root node
2) Leaf Check:
insert_height = false
if N is leaf return N
insert_height = true
3) Choose subtree: If N not leaf OR not we are not in the proper height then
let F be an entry in N whose rect Fi needs least enlargement to incude r
ties resolved with rect of smallest area
4) descend until a leaf is reached OR proper height is reached: set N to the child node pointed to by F and goto 2.
*/
 
// TODO keep stack with visited nodes
 
RTreeNode* RTree::choose_node( const Rect &r, const bool &insert_high /* false */, const unsigned &stop_height /* 0 */) const {
 
_RTREE_PRINT(" CL1");// CL1
RTreeNode *pos = root;
 
double min_enlargement;
double current_enlargement;
int node_min_enlargement;
unsigned current_height = 0; // zero is the root
 
_RTREE_PRINT(" CL2 current_height:" << current_height << " stop_height:" << stop_height << " insert_high:" << insert_high);
// CL2 Leaf check && Height check
while( ( insert_high ? true : pos->children_nodes.size() != 0 )
&& ( insert_high ? current_height < stop_height : true ) )
/* Leaf check, during insert leaf */
/* node height check, during insert non-leaf */
{
_RTREE_PRINT(" CL3 current_height:" << current_height << " stop_height:" << stop_height ); // CL3
min_enlargement = std::numeric_limits<double>::max();
current_enlargement = 0;
node_min_enlargement = 0;
 
for(unsigned i=0; i< pos->children_nodes.size(); i++){
current_enlargement = find_enlargement( pos->children_nodes[i].bounding_box, r );
 
// TODO tie not solved!
if( current_enlargement < min_enlargement ){
node_min_enlargement = i;
min_enlargement = current_enlargement;
}
}
_RTREE_PRINT(" CL4"); // CL4
// descend to the node with the min_enlargement
pos = pos->children_nodes[node_min_enlargement].data;
current_height++; // increase current visiting height
}
return pos;
}
 
 
/*
find_enlargement:
 
enlargement that "a" needs in order to incude "b"
b is the new rect we want to insert.
a is the rect of the node we try to see if b should go in.
*/
double RTree::find_enlargement( const Rect &a, const Rect &b ) const{
 
Rect union_rect(a);
union_rect.unionWith(b);
 
OptRect a_intersection_b = intersect( a, b );
 
// a, b do not intersect
if( a_intersection_b.isEmpty() ){
_RTREE_PRINT(" find_enlargement (no intersect): " << union_rect.area() - a.area() - b.area() );
return union_rect.area() - a.area() - b.area();
}
 
// a, b intersect
 
// a contains b
if( a.contains( b ) ){
_RTREE_PRINT(" find_enlargement (intersect: a cont b): " << a.area() - b.area() );
//return a.area() - b.area();
return b.area() - a.area(); // enlargement is negative in this case.
}
 
// b contains a
if( b.contains( a ) ){
_RTREE_PRINT(" find_enlargement (intersect: b cont a): " << a.area() - b.area() );
return b.area() - a.area();
}
 
// a partialy cover b
_RTREE_PRINT(" find_enlargement (intersect: a partial cover b): " << union_rect.area() - a.area() - b.area() - a_intersection_b->area() );
return union_rect.area()
- ( a.area() - a_intersection_b->area() )
- ( b.area() - a_intersection_b->area() );
}
 
 
/* I2 =========================================================================
use one split strategy
*/
 
std::pair<RTreeNode*, RTreeNode*> RTree::split_node( RTreeNode *s ){
/*
if( split_strategy == LINEAR_COST ){
linear_cost_split( ............. );
}
*/
return quadratic_split( s ); // else QUADRATIC_SPIT
}
 
 
/*-----------------------------------------------------------------------------
Quadratic Split
 
QS1) Pick first entry for each group:
Appy pick_seeds to choose 2 entries to be the first elements of the groups. Assign each one of
them to one group
QS2) check if done:
a) if all entries have been assinged stop
b) if one group has so few entries that all the rest must be assignmed to it, in order for it to
have the min number , assign them and stop
QS3) select entry and assign:
Inkvoke pick_next() to choose the next entry to assign.
*[in pick_next] Add it to the group whose covering rectangle will have to be enlrarged least to
accomodate it. Resolve ties by adding the entry to the group with the smaller are, then to the
one with fewer entries, then to either of the two.
goto 2.
*/
std::pair<RTreeNode*, RTreeNode*> RTree::quadratic_split( RTreeNode *s ) {
 
// s is the original leaf node or non-leaf node
RTreeNode* group_a = new RTreeNode(); // a is the 1st group
RTreeNode* group_b = new RTreeNode(); // b is the 2nd group
 
 
_RTREE_PRINT(" QS1"); // QS1
std::pair<unsigned, unsigned> initial_seeds;
initial_seeds = pick_seeds(s);
 
// if non-leaf node: s
if( s->children_nodes.size() > 0 ){
_RTREE_PRINT(" non-leaf node");
// each element is true if the node has been assinged to either "a" or "b"
std::vector<bool> assigned_v( s->children_nodes.size() );
std::fill( assigned_v.begin(), assigned_v.end(), false );
 
group_a->children_nodes.push_back( s->children_nodes[initial_seeds.first] );
assert(initial_seeds.first < assigned_v.size());
assigned_v[ initial_seeds.first ] = true;
 
group_b->children_nodes.push_back( s->children_nodes[initial_seeds.second] );
assert(initial_seeds.second < assigned_v.size());
assigned_v[ initial_seeds.second ] = true;
 
_RTREE_PRINT(" QS2"); // QS2
unsigned num_of_not_assigned = s->children_nodes.size() - 2;
// so far we have assinged 2 out of all
 
while( num_of_not_assigned ){// QS2 a
_RTREE_PRINT(" QS2 b, num_of_not_assigned:" << num_of_not_assigned); // QS2 b
/*
we are on NON leaf node so children of splitted groups must be nodes
 
Check each group to see if one group has so few entries that all the rest must
be assignmed to it, in order for it to have the min number.
*/
if( group_a->children_nodes.size() + num_of_not_assigned <= min_records ){
// add the non-assigned to group_a
for(unsigned i = 0; i < assigned_v.size(); i++){
if(assigned_v[i] == false){
group_a->children_nodes.push_back( s->children_nodes[i] );
assigned_v[i] = true;
}
}
break;
}
 
if( group_b->children_nodes.size() + num_of_not_assigned <= min_records ){
// add the non-assigned to group_b
for( unsigned i = 0; i < assigned_v.size(); i++ ){
if( assigned_v[i] == false ){
group_b->children_nodes.push_back( s->children_nodes[i] );
assigned_v[i] = true;
}
}
break;
}
 
_RTREE_PRINT(" QS3"); // QS3
std::pair<unsigned, enum_add_to_group> next_element;
next_element = pick_next( group_a, group_b, s, assigned_v );
if( next_element.second == ADD_TO_GROUP_A ){
group_a->children_nodes.push_back( s->children_nodes[ next_element.first ] );
}
else{
group_b->children_nodes.push_back( s->children_nodes[ next_element.first ] );
}
 
num_of_not_assigned--;
}
}
// else leaf node: s
else{
_RTREE_PRINT(" leaf node");
// each element is true if the node has been assinged to either "a" or "b"
std::vector<bool> assigned_v( s->children_leaves.size() );
std::fill( assigned_v.begin(), assigned_v.end(), false );
 
// assign 1st seed to group a
group_a->children_leaves.push_back( s->children_leaves[initial_seeds.first] );
assert(initial_seeds.first < assigned_v.size());
assigned_v[ initial_seeds.first ] = true;
 
// assign 2nd seed to group b
group_b->children_leaves.push_back( s->children_leaves[initial_seeds.second] );
assert(initial_seeds.second < assigned_v.size());
assigned_v[ initial_seeds.second ] = true;
 
_RTREE_PRINT(" QS2"); // QS2
unsigned num_of_not_assigned = s->children_leaves.size() - 2;
// so far we have assinged 2 out of all
 
while( num_of_not_assigned ){// QS2 a
_RTREE_PRINT(" QS2 b, num_of_not_assigned:" << num_of_not_assigned); // QS2 b
/*
we are on leaf node so children of splitted groups must be leaves
 
Check each group to see if one group has so few entries that all the rest must
be assignmed to it, in order for it to have the min number.
*/
if( group_a->children_leaves.size() + num_of_not_assigned <= min_records ){
_RTREE_PRINT(" add the non-assigned to group_a");
// add the non-assigned to group_a
for( unsigned i = 0; i < assigned_v.size(); i++ ){
if( assigned_v[i] == false ){
group_a->children_leaves.push_back( s->children_leaves[i] );
assigned_v[i] = true;
}
}
break;
}
 
if( group_b->children_leaves.size() + num_of_not_assigned <= min_records ){
_RTREE_PRINT(" add the non-assigned to group_b");
// add the non-assigned to group_b
for( unsigned i = 0; i < assigned_v.size(); i++ ){
if( assigned_v[i] == false ){
group_b->children_leaves.push_back( s->children_leaves[i] );
assigned_v[i] = true;
}
}
break;
}
 
_RTREE_PRINT(" QS3"); // QS3
std::pair<unsigned, enum_add_to_group> next_element;
next_element = pick_next(group_a, group_b, s, assigned_v);
if( next_element.second == ADD_TO_GROUP_A ){
group_a->children_leaves.push_back( s->children_leaves[ next_element.first ] );
}
else{
group_b->children_leaves.push_back( s->children_leaves[ next_element.first ] );
}
 
num_of_not_assigned--;
}
}
assert( initial_seeds.first != initial_seeds.second );
return std::make_pair( group_a, group_b );
}
 
/*
PS1) caclulate ineffeciency of grouping entries together:
Foreach pair of entries E1 (i), E2 (j) compose rectangle J (i_union_j) inlcuding E1, E2.
Calculate d = area(i_union_j) - area(i) - area(j)
PS2) choose the most wastefull pair:
Choose pair with largest d
*/
 
std::pair<unsigned, unsigned> RTree::pick_seeds( RTreeNode *s ) const{
double current_d = 0;
double max_d = std::numeric_limits<double>::min();
unsigned seed_a = 0;
unsigned seed_b = 1;
_RTREE_PRINT(" pick_seeds");
 
// if non leaf node: s
if( s->children_nodes.size() > 0 ){
_RTREE_PRINT(" non leaf");
_RTREE_PRINT(" PS1"); // PS1
for( unsigned a = 0; a < s->children_nodes.size(); a++ ){
// with j=i we check only the upper (diagonal) half
// with j=i+1 we also avoid checking for b==a (we don't need it)
for( unsigned b = a+1; b < s->children_nodes.size(); b++ ){
_RTREE_PRINT(" PS2 " << a << " - " << b ); // PS2
current_d = find_waste_area( s->children_nodes[a].bounding_box, s->children_nodes[b].bounding_box );
 
if( current_d > max_d ){
max_d = current_d;
seed_a = a;
seed_b = b;
}
}
}
}
// else leaf node: s
else{
_RTREE_PRINT(" leaf node");
_RTREE_PRINT(" PS1"); // PS1
for( unsigned a = 0; a < s->children_leaves.size(); a++ ){
// with j=i we check only the upper (diagonal) half
// with j=i+1 we also avoid checking for j==i (we don't need this one)
for( unsigned b = a+1; b < s->children_leaves.size(); b++ ){
_RTREE_PRINT(" PS2 " << s->children_leaves[a].data << ":" << s->children_leaves[a].bounding_box.area()
<< " - " << s->children_leaves[b].data << ":" << s->children_leaves[b].bounding_box.area() ); // PS2
current_d = find_waste_area( s->children_leaves[a].bounding_box, s->children_leaves[b].bounding_box );
 
if( current_d > max_d ){
max_d = current_d;
seed_a = a;
seed_b = b;
}
}
}
}
_RTREE_PRINT(" seed_a: " << seed_a << " seed_b: " << seed_b );
return std::make_pair( seed_a, seed_b );
}
 
/*
find_waste_area (used in pick_seeds step 1)
 
for a pair A, B compose a rect union_rect that includes a and b
calculate area of union_rect - area of a - area b
*/
double RTree::find_waste_area( const Rect &a, const Rect &b ) const{
Rect union_rect(a);
union_rect.unionWith(b);
 
return union_rect.area() - a.area() - b.area();
}
 
/*
pick_next:
select one remaining entry for classification in a group
 
PN1) Determine cost of putting each entry in each group:
Foreach entry E not yet in a group, calulate
d1= area increase required in the cover rect of Group 1 to inlcude E
d2= area increase required in the cover rect of Group 2 to inlcude E
PN2) Find entry with greatest preference for each group:
Choose any entry with the maximum difference between d1 and d2
 
*/
 
std::pair<unsigned, enum_add_to_group> RTree::pick_next( RTreeNode* group_a,
RTreeNode* group_b,
RTreeNode* s,
std::vector<bool> &assigned_v )
{
double max_increase_difference = std::numeric_limits<double>::min();
unsigned max_increase_difference_node = 0;
double current_increase_difference = 0;
 
enum_add_to_group group_to_add;
 
/*
bounding boxes of the 2 new groups. This info isn't available, since they
have no parent nodes (so that the parent node knows the bounding box).
*/
Rect bounding_box_a;
Rect bounding_box_b;
 
double increase_area_a = 0;
double increase_area_b = 0;
 
_RTREE_PRINT(" pick_next, assigned_v.size:" << assigned_v.size() );
 
// if non leaf node: one of the 2 groups (both groups are the same, either leaf/nonleaf)
if( group_a->children_nodes.size() > 0 ){
_RTREE_PRINT(" non leaf");
 
// calculate the bounding boxes of the 2 new groups.
bounding_box_a = Rect( group_a->children_nodes[0].bounding_box );
for( unsigned i = 1; i < group_a->children_nodes.size(); i++ ){
bounding_box_a.unionWith( group_a->children_nodes[i].bounding_box );
}
 
bounding_box_b = Rect( group_b->children_nodes[0].bounding_box );
for( unsigned i = 1; i < group_b->children_nodes.size(); i++ ){
bounding_box_b.unionWith( group_b->children_nodes[i].bounding_box );
}
 
 
_RTREE_PRINT(" PN1"); // PN1
for( unsigned i = 0; i < assigned_v.size(); i++ ){
_RTREE_PRINT(" i:" << i << " assigned:" << assigned_v[i]);
if( assigned_v[i] == false ){
 
increase_area_a = find_enlargement( bounding_box_a, s->children_nodes[i].bounding_box );
increase_area_b = find_enlargement( bounding_box_b, s->children_nodes[i].bounding_box );
current_increase_difference = std::abs( increase_area_a - increase_area_b );
_RTREE_PRINT(" PN2 " << i << ": " << current_increase_difference ); // PN2
if( current_increase_difference > max_increase_difference ){
max_increase_difference = current_increase_difference;
max_increase_difference_node = i;
 
// TODO tie not solved!
if( increase_area_a < increase_area_b ){
group_to_add = ADD_TO_GROUP_A;
}
else{
group_to_add = ADD_TO_GROUP_B;
}
}
}
}
//assert(max_increase_difference_node >= 0);
assert(max_increase_difference_node < assigned_v.size());
assigned_v[max_increase_difference_node] = true;
_RTREE_PRINT(" ... i:" << max_increase_difference_node << " assigned:" << assigned_v[max_increase_difference_node] );
}
else{ // else leaf node
_RTREE_PRINT(" leaf");
 
// calculate the bounding boxes of the 2 new groups
bounding_box_a = Rect( group_a->children_leaves[0].bounding_box );
for( unsigned i = 1; i < group_a->children_leaves.size(); i++ ){
std::cout<< " lala";
bounding_box_a.unionWith( group_a->children_leaves[i].bounding_box );
}
 
bounding_box_b = Rect( group_b->children_leaves[0].bounding_box );
for( unsigned i = 1; i < group_b->children_leaves.size(); i++ ){
std::cout<< " lala";
bounding_box_b.unionWith( group_b->children_leaves[i].bounding_box );
}
 
 
_RTREE_PRINT(" PN1"); // PN1
for( unsigned i = 0; i < assigned_v.size(); i++ ){
_RTREE_PRINT(" i:" << i << " assigned:" << assigned_v[i]);
if( assigned_v[i] == false ){
increase_area_a = find_enlargement( bounding_box_a, s->children_leaves[i].bounding_box );
increase_area_b = find_enlargement( bounding_box_b, s->children_leaves[i].bounding_box );
 
current_increase_difference = std::abs( increase_area_a - increase_area_b );
_RTREE_PRINT(" PN2 " << i << ": " << current_increase_difference ); // PN2
 
if( current_increase_difference > max_increase_difference ){
max_increase_difference = current_increase_difference;
max_increase_difference_node = i;
 
// TODO tie not solved!
if( increase_area_a < increase_area_b ){
group_to_add = ADD_TO_GROUP_A;
}
else{
group_to_add = ADD_TO_GROUP_B;
}
}
}
}
assert(max_increase_difference_node < assigned_v.size());
assigned_v[max_increase_difference_node] = true;
_RTREE_PRINT(" ... i:" << max_increase_difference_node << " assigned:" << assigned_v[max_increase_difference_node] );
}
 
_RTREE_PRINT(" node:" << max_increase_difference_node << " added:" << group_to_add );
return std::make_pair( max_increase_difference_node, group_to_add );
}
 
/* I3 =========================================================================
 
adjust_tree:
Ascend from a leaf node L to root, adjusting covering rectangles and propagating node splits as
necessary
 
We modified this one from the source in the step AT1 and AT5
 
AT1) Initialize:
Set N=L
IF L was spilt previously, set NN to be the resulting second node AND
(not mentioned in the original source but that's what it should mean)
Assign all entries of first node to L
AT2) check if done:
IF N is root stop
AT3) adjust covering rectangle in parent entry
1) Let P be the parent of N
2) Let EN be the N's entry in P
3) Adjust EN bounding box so that it tightly enclosses all entry rectangles in N
AT4) Propagate node split upward
IF N has a partner NN resulting from an earlier split
create a new entry ENN with ENN "p" pointing to NN and ENN bounding box enclosing all
rectangles in NN
 
IF there is room in P add ENN
ELSE invoke split_node to produce P an PP containing ENN and all P's old entries.
AT5) Move up to next level
Set N=P,
IF a split occurred, set NN=PP
goto AT1 (was goto AT2)
*/
 
bool RTree::adjust_tree( RTreeNode* position,
// modified: it holds the last split group
std::pair<RTreeNode*, RTreeNode*> &node_division,
bool initial_split_performed)
{
RTreeNode* parent;
unsigned child_in_parent; // the element in parent node that points to current posistion
std::pair< RTreeNode*, bool > find_result;
bool split_performed = initial_split_performed;
bool root_split_performed = false;
 
_RTREE_PRINT(" adjust_tree");
_RTREE_PRINT(" AT1");
 
while( true ){
_RTREE_PRINT(" ------- current tree status:");
_RTREE_PRINT_TREE_INS(root, 2, true);
 
// check for loop BREAK
if( position == root ){
_RTREE_PRINT(" AT2: found root");
if( split_performed ){
root_split_performed = true;
}
break;
}
 
if( split_performed ){
copy_group_a_to_existing_node( position, node_division.first );
}
 
/*
pick randomly, let's say the 1st entry of the current node.
Search for this spatial area in the tree, and stop to the parent node.
Then find position of current node pointer, in the parent node.
*/
_RTREE_PRINT(" AT3.1"); // AT3.1 Let P be the parent of N
if( position->children_nodes.size() > 0 ){
find_result = find_parent( root, position->children_nodes[0].bounding_box, position);
}
else{
find_result = find_parent( root, position->children_leaves[0].bounding_box, position);
}
parent = find_result.first;
 
// parent is a non-leaf, by definition
_RTREE_PRINT(" AT3.2"); // AT3.2 Let EN be the N's entry in P
for( child_in_parent = 0; child_in_parent < parent->children_nodes.size(); child_in_parent++ ){
if( parent->children_nodes[ child_in_parent ].data == position){
_RTREE_PRINT(" child_in_parent: " << child_in_parent);
break;
}
}
_RTREE_PRINT(" AT3.3");
// AT3.2 Adjust EN bounding box so that it tightly enclosses all entry rectangles in N
recalculate_bounding_box( parent, position, child_in_parent );
 
 
_RTREE_PRINT(" AT4"); // AT4
if( split_performed ){
// create new record (from group_b)
//RTreeNode* new_node = new RTreeNode();
Rect new_record_bounding;
 
RTreeRecord_NonLeaf new_record = create_nonleaf_record_from_rtreenode( new_record_bounding, node_division.second );
// install new entry (group_b)
if( parent->children_nodes.size() < max_records ){
parent->children_nodes.push_back( new_record );
split_performed = false;
}
else{
parent->children_nodes.push_back( new_record );
node_division = quadratic_split( parent ); // AT5
split_performed = true;
}
}
_RTREE_PRINT(" AT5"); // AT5
position = parent;
}
 
return root_split_performed;
}
 
/*
find_parent:
The source only mentions that we should "find" the parent. But it doesn't seay how. So we made a
modification of search.
 
Initially we take the root, a rect of the node, of which the parent we look for and the node we seek
 
We do a spatial search for this rect. If we find get an intersecttion with the rect we check if the
child is the one we look for.
If not we call find_parent again recursively
*/
 
std::pair< RTreeNode*, bool > RTree::find_parent( RTreeNode* subtree_root,
Rect search_area,
RTreeNode* wanted) const
{
_RTREE_PRINT("find_parent");
 
std::pair< RTreeNode*, bool > result;
if( subtree_root->children_nodes.size() > 0 ){
for( unsigned i=0; i < subtree_root->children_nodes.size(); i++ ){
if( subtree_root->children_nodes[i].data == wanted){
_RTREE_PRINT("FOUND!!"); // non leaf node
return std::make_pair( subtree_root, true );
}
 
if( subtree_root->children_nodes[i].bounding_box.intersects( search_area ) ){
result = find_parent( subtree_root->children_nodes[i].data, search_area, wanted);
if ( result.second ){
break;
}
}
}
}
return result;
}
 
 
void RTree::copy_group_a_to_existing_node( RTreeNode *position, RTreeNode* group_a ){
// clear position (the one that was split) and put there all the nodes of group_a
if( position->children_nodes.size() > 0 ){
_RTREE_PRINT(" copy_group...(): install group A to existing non-leaf node");
// non leaf-node: position
position->children_nodes.clear();
for( unsigned i=0; i < group_a->children_nodes.size(); i++ ){
position->children_nodes.push_back( group_a->children_nodes[i] );
}
}
else{
_RTREE_PRINT(" copy_group...(): install group A to existing leaf node");
// leaf-node: positions
position->children_leaves.clear();
for( unsigned i=0; i < group_a->children_leaves.size(); i++ ){
position->children_leaves.push_back( group_a->children_leaves[i] );
}
}
}
 
 
 
RTreeRecord_NonLeaf RTree::create_nonleaf_record_from_rtreenode( Rect &new_entry_bounding, RTreeNode* rtreenode ){
 
if( rtreenode->children_nodes.size() > 0 ){
// found bounding box of new entry
new_entry_bounding = Rect( rtreenode->children_nodes[0].bounding_box );
for(unsigned i = 1; i < rtreenode->children_nodes.size(); i++ ){
new_entry_bounding.unionWith( rtreenode->children_nodes[ i ].bounding_box );
}
}
else{ // non leaf: rtreenode
// found bounding box of new entry
new_entry_bounding = Rect( rtreenode->children_leaves[0].bounding_box );
for(unsigned i = 1; i < rtreenode->children_leaves.size(); i++ ){
new_entry_bounding.unionWith( rtreenode->children_leaves[ i ].bounding_box );
}
}
return RTreeRecord_NonLeaf( new_entry_bounding, rtreenode );
}
 
 
 
/*
print the elements of the tree
based on ordered tree walking
*/
void RTree::print_tree(RTreeNode* subtree_root, int depth ) const{
 
if( subtree_root->children_nodes.size() > 0 ){
 
// descend in each one of the elements and call print_tree
for( unsigned i=0; i < subtree_root->children_nodes.size(); i++ ){
//print spaces for indentation
for(int j=0; j < depth; j++){
std::cout << " " ;
}
 
std::cout << subtree_root->children_nodes[i].bounding_box << ", " << subtree_root->children_nodes.size() << std::endl ;
_RTREE_PRINT_TREE_INS( subtree_root->children_nodes[i].data, depth+1, used_during_insert);
}
 
}
else{
for(int j=0; j < depth; j++){
std::cout << " " ;
}
std::cout << subtree_root->children_leaves.size() << ": " ;
 
// print all the elements of the leaf node
for( unsigned i=0; i < subtree_root->children_leaves.size(); i++ ){
std::cout << subtree_root->children_leaves[i].data << ", " ;
}
std::cout << std::endl ;
 
}
}
 
 
void RTree::sanity_check(RTreeNode* subtree_root, int depth, bool used_during_insert ) const{
 
if( subtree_root->children_nodes.size() > 0 ){
// descend in each one of the elements and call sanity_check
for( unsigned i=0; i < subtree_root->children_nodes.size(); i++ ){
sanity_check( subtree_root->children_nodes[i].data, depth+1, used_during_insert);
}
 
 
// sanity check
if( subtree_root != root ){
assert( subtree_root->children_nodes.size() >= min_records);
}
/*
else{
assert( subtree_root->children_nodes.size() >= 1);
}
*/
 
if( used_during_insert ){
// allow one more during for insert
assert( subtree_root->children_nodes.size() <= max_records + 1 );
}
else{
assert( subtree_root->children_nodes.size() <= max_records );
}
 
}
else{
// sanity check
if( subtree_root != root ){
assert( subtree_root->children_leaves.size() >= min_records);
}
/*
else{
assert( subtree_root->children_leaves.size() >= 1);
}
*/
 
if( used_during_insert ){
// allow one more during for insert
assert( subtree_root->children_leaves.size() <= max_records + 1 );
}
else{
assert( subtree_root->children_nodes.size() <= max_records );
}
}
}
 
 
 
/*=============================================================================
search
===============================================================================
Given an RTree whose root node is T find all index records whose rects overlap search rect S
S1) Search subtrees:
IF T isnt a leaf, check every entry E to determine whether E I overlaps S
FOR all overlapping entries invoke Search on the tree whose root node is pointed by E P
S2) ELSE T is leaf
check all entries E to determine whether E I overlaps S
IF so E is a qualifying record
*/
 
 
void RTree::search( const Rect &search_area, std::vector< int >* result, const RTreeNode* subtree ) const {
// S1
if( subtree->children_nodes.size() > 0 ){ // non-leaf: subtree
for( unsigned i = 0; i < subtree->children_nodes.size(); i++ ){
if( subtree->children_nodes[ i ].bounding_box.intersects( search_area ) ){
search( search_area, result, subtree->children_nodes[ i ].data );
}
}
}
// S2
else{ // leaf: subtree
for( unsigned i = 0; i < subtree->children_leaves.size(); i++ ){
if( subtree->children_leaves[ i ].bounding_box.intersects( search_area ) ){
result->push_back( subtree->children_leaves[ i ].data );
}
}
}
}
 
 
/*=============================================================================
erase
===============================================================================
we changed steps D2)
D1) Find node containing record
Invoke find_leaf() to locate the leaf node L containing E
IF record is found stop
D2) Delete record
Remove E from L (it happened in find_leaf step FL2)
D3) Propagate changes
Invoke condense_tree, passing L
D4) Shorten tree
If root node has only one child, after the tree was adjusted, make the child new root
 
return
0 on success
1 in case no entry was found
 
*/
//int RTree::erase( const RTreeRecord_Leaf & record_to_erase ){
int RTree::erase( const Rect &search_area, const int shape_to_delete ){
_RTREE_PRINT("\n=====================================");
_RTREE_PRINT("erase element: " << shape_to_delete);
// D1 + D2: entry is deleted in find_leaf
_RTREE_PRINT("D1 & D2 : find and delete the leaf");
RTreeNode* contains_record = find_leaf( root, search_area, shape_to_delete );
if( !contains_record ){ // no entry returned from find_leaf
return 1; // no entry found
}
// D3
//bool root_elimination_performed = condense_tree( contains_record );
 
// D4
 
//if( root_elimination_performed ){
if( root->children_nodes.size() > 0 ){ // non leaf: root
// D4
if( root->children_nodes.size() == 1 ){
_RTREE_PRINT("D4 : non leaf: ");
tree_height--;
RTreeNode* t = root;
root = root->children_nodes[0].data;
delete t;
}
 
}
else { // leaf: root
// D4
// do nothing
}
sanity_check( root, 0 );
return 0; // success
}
 
 
/*
find_leaf()
Given an RTree whose root node is T find the leaf node containing index entry E
 
FL1) Search subtrees
IF T is non leaf, check each entry F in T to determine if F I overlaps E I
foreach such entry invoke find_leaf on the tree whose root is pointed to by F P until E is
found or all entries have been checked
FL2) search leaf node for record
IF T is leaf, check each entry to see if it matches E
IF E is found return T
AND delete element E (step D2)
*/
 
RTreeNode* RTree::find_leaf( RTreeNode* subtree, const Rect &search_area, const int shape_to_delete ) const {
// FL1
if( subtree->children_nodes.size() > 0 ){ // non-leaf: subtree
for( std::vector< RTreeRecord_NonLeaf >::iterator it = subtree->children_nodes.begin(); it!=subtree->children_nodes.end(); ++it ){
if( it->bounding_box.intersects( search_area ) ){
RTreeNode* t = find_leaf( it->data, search_area, shape_to_delete );
if( t ){ // if search was successful terminate
return t;
}
}
}
}
// FL2
else{ // leaf: subtree
for( std::vector< RTreeRecord_Leaf >::iterator it = subtree->children_leaves.begin(); it!=subtree->children_leaves.end(); ++it ){
if( it->data == shape_to_delete ){
// delete element: implement step D2)
subtree->children_leaves.erase( it );
return subtree;
}
}
}
return 0;
}
 
 
/*
condense_tree()
Given a Leaf node L from which an entry has been deleted eliminate the node if it has too few entries and reallocate its entries
Propagate node elimination upwards as necessary
Adjust all covering recsts n the path to the root making them smaller if possible
 
CT1) Initialize
Set N=L
Set Q the set of eliminated nodes to be empty
CT2) // Find parent entry (was there but doesn't make sense)
IF N is the root
goto CT6
ELSE
1) Find parent entry
2) let P be the parent of N
3) and let EN be N's entry in P
CT3) IF N has fewer than m entries
Eliminate underfull node
1) delete EN from P
2) and add N to set Q
CT4) ELSE
adjust EN I to tightly contain all entries in N
CT5) move up one level in tree
set N=P and repeat from CT2
 
CT6) Re insert orphaned entries
Re-inser all entreis of nodes in set Q
Entries from eliminated leaf nodes are re-inserted in tree leaves (like in insert)
BUT non-leaf nodes must be placed higher in the tree so that leaves of their dependent subtrees
will be on the same level as leaves of the main tree. (on the same height they originally were)
(not mentioned in the source description: the criteria for placing the node should be
again TODO ??? least enlargement)
 
*/
// TODO this can be merged with adjust_tree or refactor to reutilize some parts. less readable
bool RTree::condense_tree( RTreeNode* position )
{
RTreeNode* parent;
unsigned child_in_parent = 0; // the element in parent node that points to current posistion
 
std::pair< RTreeNode*, bool > find_result;
bool elimination_performed = false;
bool root_elimination_performed = false;
unsigned current_height = tree_height+1;
Rect special_case_bounding_box;
_RTREE_PRINT(" condense_tree");
_RTREE_PRINT(" CT1");
// leaf records that were eliminated due to under-full node
std::vector< RTreeRecord_Leaf > Q_leaf_records( 0 );
 
// < non-leaf records, their height > that were eliminated due to under-full node
std::vector< std::pair< RTreeRecord_NonLeaf, unsigned > > Q_nonleaf_records( 0 );
 
 
while( true ){
 
// check for loop BREAK
if( position == root ){
_RTREE_PRINT(" CT2 position is root");
if( elimination_performed ){
root_elimination_performed = true;
}
break;
}
 
/*
pick randomly, let's say the 1st entry of the current node.
Search for this spatial area in the tree, and stop to the parent node.
Then find position of current node pointer, in the parent node.
*/
/*
special case. if elimination due to children being underfull was performed AND
AND parent had only 1 record ,then this one record was removed.
*/
if( position->children_nodes.size() > 0 ){
_RTREE_PRINT(" CT2.1 - 2 non leaf: find parent, P is parent");
// CT2.1 find parent. By definition it's nonleaf
find_result = find_parent( root, position->children_nodes[0].bounding_box, position);
}
else if( position->children_nodes.size() == 0
&& position->children_leaves.size() == 0
&& elimination_performed )
{ // special case
_RTREE_PRINT(" CT2.1 - 2 special case: find parent, P is parent");
// CT2.1 find parent. By definition it's nonleaf
find_result = find_parent( root, special_case_bounding_box, position);
}
else{
_RTREE_PRINT(" CT2.1 - 2 leaf: find parent, P is parent");
// CT2.1 find parent. By definition it's nonleaf
find_result = find_parent( root, position->children_leaves[0].bounding_box, position);
}
// CT2.2 Let P be the parent of N
parent = find_result.first;
 
// parent is a non-leaf, by definition. Calculate "child_in_parent"
_RTREE_PRINT(" CT2.3 find in parent, position's record EN");
// CT2.3 Let EN be the N's entry in P
for( child_in_parent = 0; child_in_parent < parent->children_nodes.size(); child_in_parent++ ){
if( parent->children_nodes[ child_in_parent ].data == position){
_RTREE_PRINT(" child_in_parent: " << child_in_parent << " out of " << parent->children_nodes.size() << " (size)" );
break;
}
}
 
if( position->children_nodes.size() > 0 ){ // non leaf node: position
_RTREE_PRINT(" CT3 nonleaf: eliminate underfull node");
// CT3 Eliminate underfull node
if( position->children_nodes.size() < min_records ){
_RTREE_PRINT(" CT3.2 add N to Q");
// CT3.2 add N to set Q ( EN the record that points to N )
for( unsigned i = 0; i < position->children_nodes.size(); i++ ){
_RTREE_PRINT(" i " << i );
std::pair< RTreeRecord_NonLeaf, unsigned > t = std::make_pair( position->children_nodes[i], current_height-1);
Q_nonleaf_records.push_back( t );
 
}
special_case_bounding_box = parent->children_nodes[ child_in_parent ].bounding_box;
 
_RTREE_PRINT(" CT3.1 delete in parent, position's record EN");
// CT3.1 delete EN from P ( parent is by definition nonleaf )
if( remove_record_from_parent( parent, position ) ){ // TODO does erase, delete to the pointer ???
_RTREE_PRINT(" remove_record_from_parent error ");
}
elimination_performed = true;
}
else{
_RTREE_PRINT(" CT4 "); /// CT4) if not underfull
recalculate_bounding_box( parent, position, child_in_parent );
elimination_performed = false;
}
 
}
else{ // leaf node: position
_RTREE_PRINT(" CT3 leaf: eliminate underfull node");
// CT3 Eliminate underfull node
if( position->children_leaves.size() < min_records ){
_RTREE_PRINT(" CT3.2 add N to Q " << position->children_leaves.size() );
// CT3.2 add N to set Q
for( unsigned i = 0; i < position->children_leaves.size(); i++ ){
_RTREE_PRINT(" i " << i );
Q_leaf_records.push_back( position->children_leaves[i] ); // TODO problem here
special_case_bounding_box = position->children_leaves[i].bounding_box;
}
 
_RTREE_PRINT(" CT3.1 delete in parent, position's record EN");
// CT3.1 delete EN from P ( parent is by definition nonleaf )
if( remove_record_from_parent( parent, position ) ){
_RTREE_PRINT(" remove_record_from_parent error ");
}
elimination_performed = true;
}
else{
_RTREE_PRINT(" CT4 "); /// CT4) if not underfull
recalculate_bounding_box( parent, position, child_in_parent );
elimination_performed = false;
}
}
_RTREE_PRINT(" CT5 ");// CT5) move up one level in tree
position = parent;
 
current_height--;
}
// CT6: reinsert
_RTREE_PRINT(" ------ Q_leaf");
for( std::vector< RTreeRecord_Leaf >::iterator it = Q_leaf_records.begin(); it != Q_leaf_records.end(); ++it ){
_RTREE_PRINT(" leaf:" << (*it).data);
}
_RTREE_PRINT(" ------ Q_nonleaf");
for( std::vector< std::pair< RTreeRecord_NonLeaf, unsigned > >::iterator it = Q_nonleaf_records.begin(); it != Q_nonleaf_records.end(); ++it ){
_RTREE_PRINT(" ------- " << it->second );
_RTREE_PRINT_TREE( it->first.data, 0);
}
 
_RTREE_PRINT(" CT6 ");
for( std::vector< RTreeRecord_Leaf >::iterator it = Q_leaf_records.begin(); it != Q_leaf_records.end(); ++it ){
insert( *it );
_RTREE_PRINT(" inserted leaf:" << (*it).data << " ------------");
_RTREE_PRINT_TREE( root, 0);
}
 
for( std::vector< std::pair< RTreeRecord_NonLeaf, unsigned > >::iterator it = Q_nonleaf_records.begin(); it != Q_nonleaf_records.end(); ++it ){
insert( RTreeRecord_Leaf() , true, it->second, it->first );
_RTREE_PRINT(" inserted nonleaf------------");
_RTREE_PRINT_TREE( root, 0);
// TODO this fake RTreeRecord_Leaf() looks stupid. find better way to to this ???
}
return root_elimination_performed;
}
 
 
/*
given:
- a parent
- a child node
- and the position of the child node in the parent
recalculate the parent record's bounding box of the child, in order to ightly contain all entries of child
 
NOTE! child must be indeed child of the parent, otherwise it screws up things. So find parent and child
before calling this function
*/
void RTree::recalculate_bounding_box( RTreeNode* parent, RTreeNode* child, unsigned &child_in_parent ) {
if( child->children_nodes.size() > 0 ){
_RTREE_PRINT(" non-leaf: recalculate bounding box of parent "); // non leaf-node: child
parent->children_nodes[ child_in_parent ].bounding_box = Rect( child->children_nodes[0].bounding_box );
for( unsigned i=1; i < child->children_nodes.size(); i++ ){
parent->children_nodes[ child_in_parent ].bounding_box.unionWith( child->children_nodes[i].bounding_box );
}
}
else{
_RTREE_PRINT(" leaf: recalculate bounding box of parent "); // leaf-node: child
parent->children_nodes[ child_in_parent ].bounding_box = Rect( child->children_leaves[0].bounding_box );
 
for( unsigned i=1; i < child->children_leaves.size(); i++ ){
parent->children_nodes[ child_in_parent ].bounding_box.unionWith( child->children_leaves[i].bounding_box );
}
}
}
 
/*
given:
- a parent
- a child node
it removes the child record from the parent
 
NOTE! child must be indeed child of the parent, otherwise it screws up things.
So find parent and child before calling this function
*/
int RTree::remove_record_from_parent( RTreeNode* parent, RTreeNode* child ) {
_RTREE_PRINT( "remove_record_from_parent)" );
for( std::vector< RTreeRecord_NonLeaf >::iterator it = parent->children_nodes.begin(); it!=parent->children_nodes.end(); ++it ){
if( it->data == child ){
// delete element: implement step D2)
parent->children_nodes.erase( it );
return 0; // sucess
}
}
return 1; // failure
}
 
/*=============================================================================
TODO update
===============================================================================
*/
 
 
};
 
/*
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 :
/trunk/Scribus/scribus/third_party/lib2geom/svg-elliptical-arc.cpp
0,0 → 1,269
/*
* SVG Elliptical Arc 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.
*/
 
 
#include <svg-elliptical-arc.h>
#include <ellipse.h>
#include <sbasis-geometric.h>
#include <bezier-curve.h>
#include <poly.h>
 
#include <cfloat>
#include <limits>
#include <memory>
 
#include <numeric/vector.h>
#include <numeric/fitting-tool.h>
#include <numeric/fitting-model.h>
 
 
 
namespace Geom
{
 
/**
* @class SVGEllipticalArc
* @brief SVG 1.1-compliant elliptical arc.
*
* This class is almost identical to the normal elliptical arc, but it differs slightly
* in the handling of degenerate arcs to be compliant with SVG 1.1 implementation guidelines.
*
* @ingroup Curves
*/
 
namespace detail
{
/*
* ellipse_equation
*
* this is an helper struct, it provides two routines:
* the first one evaluates the implicit form of an ellipse on a given point
* the second one computes the normal versor at a given point of an ellipse
* in implicit form
*/
struct ellipse_equation
{
ellipse_equation(double a, double b, double c, double d, double e, double f)
: A(a), B(b), C(c), D(d), E(e), F(f)
{
}
 
double operator()(double x, double y) const
{
// A * x * x + B * x * y + C * y * y + D * x + E * y + F;
return (A * x + B * y + D) * x + (C * y + E) * y + F;
}
 
double operator()(Point const& p) const
{
return (*this)(p[X], p[Y]);
}
 
Point normal(double x, double y) const
{
Point n( 2 * A * x + B * y + D, 2 * C * y + B * x + E );
return unit_vector(n);
}
 
Point normal(Point const& p) const
{
return normal(p[X], p[Y]);
}
 
double A, B, C, D, E, F;
};
 
}
 
make_elliptical_arc::
make_elliptical_arc( EllipticalArc& _ea,
curve_type const& _curve,
unsigned int _total_samples,
double _tolerance )
: ea(_ea), curve(_curve),
dcurve( unitVector(derivative(curve)) ),
model(), fitter(model, _total_samples),
tolerance(_tolerance), tol_at_extr(tolerance/2),
tol_at_center(0.1), angle_tol(0.1),
initial_point(curve.at0()), final_point(curve.at1()),
N(_total_samples), last(N-1), partitions(N-1), p(N),
svg_compliant(true)
{
}
 
/*
* check that the coefficients computed by the fit method satisfy
* the tolerance parameters at the k-th sample point
*/
bool
make_elliptical_arc::
bound_exceeded( unsigned int k, detail::ellipse_equation const & ee,
double e1x, double e1y, double e2 )
{
dist_err = std::fabs( ee(p[k]) );
dist_bound = std::fabs( e1x * p[k][X] + e1y * p[k][Y] + e2 );
// check that the angle btw the tangent versor to the input curve
// and the normal versor of the elliptical arc, both evaluate
// at the k-th sample point, are really othogonal
angle_err = std::fabs( dot( dcurve(k/partitions), ee.normal(p[k]) ) );
//angle_err *= angle_err;
return ( dist_err > dist_bound || angle_err > angle_tol );
}
 
/*
* check that the coefficients computed by the fit method satisfy
* the tolerance parameters at each sample point
*/
bool
make_elliptical_arc::
check_bound(double A, double B, double C, double D, double E, double F)
{
detail::ellipse_equation ee(A, B, C, D, E, F);
 
// check error magnitude at the end-points
double e1x = (2*A + B) * tol_at_extr;
double e1y = (B + 2*C) * tol_at_extr;
double e2 = ((D + E) + (A + B + C) * tol_at_extr) * tol_at_extr;
if (bound_exceeded(0, ee, e1x, e1y, e2))
{
print_bound_error(0);
return false;
}
if (bound_exceeded(0, ee, e1x, e1y, e2))
{
print_bound_error(last);
return false;
}
 
// e1x = derivative((ee(x,y), x) | x->tolerance, y->tolerance
e1x = (2*A + B) * tolerance;
// e1y = derivative((ee(x,y), y) | x->tolerance, y->tolerance
e1y = (B + 2*C) * tolerance;
// e2 = ee(tolerance, tolerance) - F;
e2 = ((D + E) + (A + B + C) * tolerance) * tolerance;
// std::cerr << "e1x = " << e1x << std::endl;
// std::cerr << "e1y = " << e1y << std::endl;
// std::cerr << "e2 = " << e2 << std::endl;
 
// check error magnitude at sample points
for ( unsigned int k = 1; k < last; ++k )
{
if ( bound_exceeded(k, ee, e1x, e1y, e2) )
{
print_bound_error(k);
return false;
}
}
 
return true;
}
 
/*
* fit
*
* supply the samples to the fitter and compute
* the ellipse implicit equation coefficients
*/
void make_elliptical_arc::fit()
{
for (unsigned int k = 0; k < N; ++k)
{
p[k] = curve( k / partitions );
fitter.append(p[k]);
}
fitter.update();
 
NL::Vector z(N, 0.0);
fitter.result(z);
}
 
bool make_elliptical_arc::make_elliptiarc()
{
const NL::Vector & coeff = fitter.result();
Ellipse e;
try
{
e.set(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
}
catch(LogicalError exc)
{
return false;
}
 
Point inner_point = curve(0.5);
 
if (svg_compliant_flag())
{
std::auto_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point, true) );
ea = *arc;
}
else
{
try
{
std::auto_ptr<EllipticalArc> eap(
e.arc(initial_point, inner_point, final_point, false) );
ea = *eap;
}
catch(RangeError exc)
{
return false;
}
}
 
 
if ( !are_near( e.center(),
ea.center(),
tol_at_center * std::min(e.ray(X),e.ray(Y))
)
)
{
return false;
}
return true;
}
 
 
 
} // end namespace Geom
 
 
 
 
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
 
/trunk/Scribus/scribus/third_party/lib2geom/svg-path-parser.cpp
0,0 → 1,1396
#line 1 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
/**
* \file
* \brief parse SVG path specifications
*
* Copyright 2007 MenTaLguY <mental@rydia.net>
* Copyright 2007 Aaron Spike <aaron@ekips.org>
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
*/
 
 
#include <cmath>
#include <vector>
#include <glib.h>
 
#include <point.h>
#include <svg-path-parser.h>
#include <angle.h>
 
namespace Geom {
 
namespace {
 
class Parser {
public:
Parser(SVGPathSink &sink) : _sink(sink) {}
 
void parse(char const *str) throw(SVGPathParseError);
 
private:
bool _absolute;
Point _current;
Point _initial;
Point _cubic_tangent;
Point _quad_tangent;
std::vector<double> _params;
SVGPathSink &_sink;
 
void _reset() {
_absolute = false;
_current = _initial = Point(0, 0);
_quad_tangent = _cubic_tangent = Point(0, 0);
_params.clear();
}
 
void _push(double value) {
_params.push_back(value);
}
 
double _pop() {
double value = _params.back();
_params.pop_back();
return value;
}
 
bool _pop_flag() {
return _pop() != 0.0;
}
 
double _pop_coord(Geom::Dim2 axis) {
if (_absolute) {
return _pop();
} else {
return _pop() + _current[axis];
}
}
 
Point _pop_point() {
double y = _pop_coord(Geom::Y);
double x = _pop_coord(Geom::X);
return Point(x, y);
}
 
void _moveTo(Point p) {
_quad_tangent = _cubic_tangent = _current = _initial = p;
_sink.moveTo(p);
}
void _hlineTo(Point p) {
_quad_tangent = _cubic_tangent = _current = p;
_sink.hlineTo(p[Geom::X]);
}
void _vlineTo(Point p) {
_quad_tangent = _cubic_tangent = _current = p;
_sink.vlineTo(p[Geom::Y]);
}
 
void _lineTo(Point p) {
_quad_tangent = _cubic_tangent = _current = p;
_sink.lineTo(p);
}
 
void _curveTo(Point c0, Point c1, Point p) {
_quad_tangent = _current = p;
_cubic_tangent = p + ( p - c1 );
_sink.curveTo(c0, c1, p);
}
 
void _quadTo(Point c, Point p) {
_cubic_tangent = _current = p;
_quad_tangent = p + ( p - c );
_sink.quadTo(c, p);
}
 
void _arcTo(double rx, double ry, double angle,
bool large_arc, bool sweep, Point p)
{
_quad_tangent = _cubic_tangent = _current = p;
_sink.arcTo(rx, ry, angle, large_arc, sweep, p);
}
 
void _closePath() {
_quad_tangent = _cubic_tangent = _current = _initial;
_sink.closePath();
}
};
 
 
#line 144 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.cpp"
static const char _svg_path_actions[] = {
0, 1, 0, 1, 1, 1, 2, 1,
3, 1, 4, 1, 5, 1, 15, 1,
16, 2, 1, 0, 2, 1, 6, 2,
1, 7, 2, 1, 8, 2, 1, 9,
2, 1, 10, 2, 1, 11, 2, 1,
12, 2, 1, 13, 2, 1, 14, 2,
4, 0, 2, 5, 0, 2, 15, 16,
3, 1, 6, 0, 3, 1, 6, 16,
3, 1, 7, 0, 3, 1, 7, 16,
3, 1, 8, 0, 3, 1, 8, 16,
3, 1, 9, 0, 3, 1, 9, 16,
3, 1, 10, 0, 3, 1, 10, 16,
3, 1, 11, 0, 3, 1, 11, 16,
3, 1, 12, 0, 3, 1, 12, 16,
3, 1, 13, 0, 3, 1, 13, 16,
3, 1, 14, 0, 3, 1, 14, 16
};
 
static const short _svg_path_key_offsets[] = {
0, 0, 7, 16, 25, 28, 30, 42,
52, 55, 57, 90, 121, 124, 126, 138,
148, 151, 153, 186, 195, 207, 216, 249,
256, 263, 265, 275, 283, 290, 292, 304,
314, 317, 319, 328, 335, 341, 346, 353,
359, 364, 374, 377, 379, 391, 401, 404,
406, 437, 466, 476, 480, 482, 490, 499,
508, 511, 513, 525, 535, 538, 540, 552,
562, 565, 567, 579, 589, 592, 594, 606,
616, 619, 621, 633, 643, 646, 648, 681,
712, 724, 733, 745, 754, 766, 775, 787,
796, 808, 817, 850, 854, 856, 887, 896,
905, 908, 910, 943, 974, 1007, 1011, 1013,
1044, 1053, 1062, 1071, 1074, 1076, 1088, 1098,
1101, 1103, 1115, 1125, 1128, 1130, 1142, 1152,
1155, 1157, 1190, 1221, 1233, 1242, 1254, 1263,
1275, 1284, 1317, 1321, 1323, 1354, 1363, 1372,
1375, 1377, 1389, 1399, 1402, 1404, 1416, 1426,
1429, 1431, 1443, 1453, 1456, 1458, 1491, 1522,
1534, 1543, 1555, 1564, 1576, 1585, 1618, 1622,
1624, 1655, 1664, 1673, 1676, 1678, 1690, 1700,
1703, 1705, 1738, 1769, 1781, 1790, 1823, 1827,
1829, 1860, 1869, 1878, 1881, 1883, 1916, 1947,
1980, 1984, 1986, 2017, 2042, 2067, 2074, 2083,
2092, 2101, 2110, 2122, 2131, 2164, 2168, 2170,
2201, 2210, 2219, 2228, 2237, 2241, 2243, 2253,
2257, 2259, 2269, 2273, 2275, 2285, 2289, 2291,
2301, 2305, 2307, 2317, 2321, 2323, 2333, 2337,
2339, 2349, 2353, 2355, 2365, 2369, 2371, 2381,
2385, 2387, 2397, 2401, 2403, 2413, 2417, 2419,
2429, 2433, 2435, 2445, 2449, 2451, 2480, 2511,
2520, 2524, 2526, 2536, 2548, 2557, 2562, 2567,
2571, 2573, 2580, 2590, 2599, 2603, 2605, 2615,
2627, 2631, 2633, 2664, 2668, 2670, 2680
};
 
static const char _svg_path_trans_keys[] = {
0, 13, 32, 77, 109, 9, 10, 13,
32, 43, 45, 46, 9, 10, 48, 57,
13, 32, 43, 45, 46, 9, 10, 48,
57, 46, 48, 57, 48, 57, 13, 32,
44, 46, 69, 101, 9, 10, 43, 45,
48, 57, 13, 32, 44, 46, 9, 10,
43, 45, 48, 57, 46, 48, 57, 48,
57, 0, 13, 32, 44, 46, 65, 67,
69, 72, 76, 77, 81, 83, 84, 86,
90, 97, 99, 101, 104, 108, 109, 113,
115, 116, 118, 122, 9, 10, 43, 45,
48, 57, 0, 13, 32, 44, 46, 65,
67, 72, 76, 77, 81, 83, 84, 86,
90, 97, 99, 104, 108, 109, 113, 115,
116, 118, 122, 9, 10, 43, 45, 48,
57, 46, 48, 57, 48, 57, 13, 32,
44, 46, 69, 101, 9, 10, 43, 45,
48, 57, 13, 32, 44, 46, 9, 10,
43, 45, 48, 57, 46, 48, 57, 48,
57, 0, 13, 32, 44, 46, 65, 67,
69, 72, 76, 77, 81, 83, 84, 86,
90, 97, 99, 101, 104, 108, 109, 113,
115, 116, 118, 122, 9, 10, 43, 45,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 13, 32, 44, 46, 69,
101, 9, 10, 43, 45, 48, 57, 13,
32, 43, 45, 46, 9, 10, 48, 57,
0, 13, 32, 44, 46, 65, 67, 69,
72, 76, 77, 81, 83, 84, 86, 90,
97, 99, 101, 104, 108, 109, 113, 115,
116, 118, 122, 9, 10, 43, 45, 48,
57, 13, 32, 46, 9, 10, 48, 57,
13, 32, 46, 9, 10, 48, 57, 48,
57, 13, 32, 44, 46, 69, 101, 9,
10, 48, 57, 13, 32, 44, 46, 9,
10, 48, 57, 13, 32, 46, 9, 10,
48, 57, 48, 57, 13, 32, 44, 46,
69, 101, 9, 10, 43, 45, 48, 57,
13, 32, 44, 46, 9, 10, 43, 45,
48, 57, 46, 48, 57, 48, 57, 13,
32, 44, 69, 101, 9, 10, 48, 57,
13, 32, 44, 48, 49, 9, 10, 13,
32, 48, 49, 9, 10, 13, 32, 44,
9, 10, 13, 32, 44, 48, 49, 9,
10, 13, 32, 48, 49, 9, 10, 13,
32, 44, 9, 10, 13, 32, 44, 46,
9, 10, 43, 45, 48, 57, 46, 48,
57, 48, 57, 13, 32, 44, 46, 69,
101, 9, 10, 43, 45, 48, 57, 13,
32, 44, 46, 9, 10, 43, 45, 48,
57, 46, 48, 57, 48, 57, 0, 13,
32, 44, 46, 65, 67, 69, 72, 76,
77, 81, 83, 84, 86, 90, 97, 99,
101, 104, 108, 109, 113, 115, 116, 118,
122, 9, 10, 48, 57, 0, 13, 32,
44, 46, 65, 67, 72, 76, 77, 81,
83, 84, 86, 90, 97, 99, 104, 108,
109, 113, 115, 116, 118, 122, 9, 10,
48, 57, 13, 32, 44, 46, 69, 101,
9, 10, 48, 57, 43, 45, 48, 57,
48, 57, 13, 32, 44, 46, 9, 10,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 46, 48, 57, 48,
57, 13, 32, 44, 46, 69, 101, 9,
10, 43, 45, 48, 57, 13, 32, 44,
46, 9, 10, 43, 45, 48, 57, 46,
48, 57, 48, 57, 13, 32, 44, 46,
69, 101, 9, 10, 43, 45, 48, 57,
13, 32, 44, 46, 9, 10, 43, 45,
48, 57, 46, 48, 57, 48, 57, 13,
32, 44, 46, 69, 101, 9, 10, 43,
45, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 46, 48, 57,
48, 57, 13, 32, 44, 46, 69, 101,
9, 10, 43, 45, 48, 57, 13, 32,
44, 46, 9, 10, 43, 45, 48, 57,
46, 48, 57, 48, 57, 13, 32, 44,
46, 69, 101, 9, 10, 43, 45, 48,
57, 13, 32, 44, 46, 9, 10, 43,
45, 48, 57, 46, 48, 57, 48, 57,
0, 13, 32, 44, 46, 65, 67, 69,
72, 76, 77, 81, 83, 84, 86, 90,
97, 99, 101, 104, 108, 109, 113, 115,
116, 118, 122, 9, 10, 43, 45, 48,
57, 0, 13, 32, 44, 46, 65, 67,
72, 76, 77, 81, 83, 84, 86, 90,
97, 99, 104, 108, 109, 113, 115, 116,
118, 122, 9, 10, 43, 45, 48, 57,
13, 32, 44, 46, 69, 101, 9, 10,
43, 45, 48, 57, 13, 32, 43, 45,
46, 9, 10, 48, 57, 13, 32, 44,
46, 69, 101, 9, 10, 43, 45, 48,
57, 13, 32, 43, 45, 46, 9, 10,
48, 57, 13, 32, 44, 46, 69, 101,
9, 10, 43, 45, 48, 57, 13, 32,
43, 45, 46, 9, 10, 48, 57, 13,
32, 44, 46, 69, 101, 9, 10, 43,
45, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 13, 32, 44, 46,
69, 101, 9, 10, 43, 45, 48, 57,
13, 32, 43, 45, 46, 9, 10, 48,
57, 0, 13, 32, 44, 46, 65, 67,
69, 72, 76, 77, 81, 83, 84, 86,
90, 97, 99, 101, 104, 108, 109, 113,
115, 116, 118, 122, 9, 10, 43, 45,
48, 57, 43, 45, 48, 57, 48, 57,
0, 13, 32, 44, 46, 65, 67, 72,
76, 77, 81, 83, 84, 86, 90, 97,
99, 104, 108, 109, 113, 115, 116, 118,
122, 9, 10, 43, 45, 48, 57, 13,
32, 43, 45, 46, 9, 10, 48, 57,
13, 32, 43, 45, 46, 9, 10, 48,
57, 46, 48, 57, 48, 57, 0, 13,
32, 44, 46, 65, 67, 69, 72, 76,
77, 81, 83, 84, 86, 90, 97, 99,
101, 104, 108, 109, 113, 115, 116, 118,
122, 9, 10, 43, 45, 48, 57, 0,
13, 32, 44, 46, 65, 67, 72, 76,
77, 81, 83, 84, 86, 90, 97, 99,
104, 108, 109, 113, 115, 116, 118, 122,
9, 10, 43, 45, 48, 57, 0, 13,
32, 44, 46, 65, 67, 69, 72, 76,
77, 81, 83, 84, 86, 90, 97, 99,
101, 104, 108, 109, 113, 115, 116, 118,
122, 9, 10, 43, 45, 48, 57, 43,
45, 48, 57, 48, 57, 0, 13, 32,
44, 46, 65, 67, 72, 76, 77, 81,
83, 84, 86, 90, 97, 99, 104, 108,
109, 113, 115, 116, 118, 122, 9, 10,
43, 45, 48, 57, 13, 32, 43, 45,
46, 9, 10, 48, 57, 13, 32, 43,
45, 46, 9, 10, 48, 57, 13, 32,
43, 45, 46, 9, 10, 48, 57, 46,
48, 57, 48, 57, 13, 32, 44, 46,
69, 101, 9, 10, 43, 45, 48, 57,
13, 32, 44, 46, 9, 10, 43, 45,
48, 57, 46, 48, 57, 48, 57, 13,
32, 44, 46, 69, 101, 9, 10, 43,
45, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 46, 48, 57,
48, 57, 13, 32, 44, 46, 69, 101,
9, 10, 43, 45, 48, 57, 13, 32,
44, 46, 9, 10, 43, 45, 48, 57,
46, 48, 57, 48, 57, 0, 13, 32,
44, 46, 65, 67, 69, 72, 76, 77,
81, 83, 84, 86, 90, 97, 99, 101,
104, 108, 109, 113, 115, 116, 118, 122,
9, 10, 43, 45, 48, 57, 0, 13,
32, 44, 46, 65, 67, 72, 76, 77,
81, 83, 84, 86, 90, 97, 99, 104,
108, 109, 113, 115, 116, 118, 122, 9,
10, 43, 45, 48, 57, 13, 32, 44,
46, 69, 101, 9, 10, 43, 45, 48,
57, 13, 32, 43, 45, 46, 9, 10,
48, 57, 13, 32, 44, 46, 69, 101,
9, 10, 43, 45, 48, 57, 13, 32,
43, 45, 46, 9, 10, 48, 57, 13,
32, 44, 46, 69, 101, 9, 10, 43,
45, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 0, 13, 32, 44,
46, 65, 67, 69, 72, 76, 77, 81,
83, 84, 86, 90, 97, 99, 101, 104,
108, 109, 113, 115, 116, 118, 122, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 0, 13, 32, 44, 46,
65, 67, 72, 76, 77, 81, 83, 84,
86, 90, 97, 99, 104, 108, 109, 113,
115, 116, 118, 122, 9, 10, 43, 45,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 46, 48, 57, 48,
57, 13, 32, 44, 46, 69, 101, 9,
10, 43, 45, 48, 57, 13, 32, 44,
46, 9, 10, 43, 45, 48, 57, 46,
48, 57, 48, 57, 13, 32, 44, 46,
69, 101, 9, 10, 43, 45, 48, 57,
13, 32, 44, 46, 9, 10, 43, 45,
48, 57, 46, 48, 57, 48, 57, 13,
32, 44, 46, 69, 101, 9, 10, 43,
45, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 46, 48, 57,
48, 57, 0, 13, 32, 44, 46, 65,
67, 69, 72, 76, 77, 81, 83, 84,
86, 90, 97, 99, 101, 104, 108, 109,
113, 115, 116, 118, 122, 9, 10, 43,
45, 48, 57, 0, 13, 32, 44, 46,
65, 67, 72, 76, 77, 81, 83, 84,
86, 90, 97, 99, 104, 108, 109, 113,
115, 116, 118, 122, 9, 10, 43, 45,
48, 57, 13, 32, 44, 46, 69, 101,
9, 10, 43, 45, 48, 57, 13, 32,
43, 45, 46, 9, 10, 48, 57, 13,
32, 44, 46, 69, 101, 9, 10, 43,
45, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 13, 32, 44, 46,
69, 101, 9, 10, 43, 45, 48, 57,
13, 32, 43, 45, 46, 9, 10, 48,
57, 0, 13, 32, 44, 46, 65, 67,
69, 72, 76, 77, 81, 83, 84, 86,
90, 97, 99, 101, 104, 108, 109, 113,
115, 116, 118, 122, 9, 10, 43, 45,
48, 57, 43, 45, 48, 57, 48, 57,
0, 13, 32, 44, 46, 65, 67, 72,
76, 77, 81, 83, 84, 86, 90, 97,
99, 104, 108, 109, 113, 115, 116, 118,
122, 9, 10, 43, 45, 48, 57, 13,
32, 43, 45, 46, 9, 10, 48, 57,
13, 32, 43, 45, 46, 9, 10, 48,
57, 46, 48, 57, 48, 57, 13, 32,
44, 46, 69, 101, 9, 10, 43, 45,
48, 57, 13, 32, 44, 46, 9, 10,
43, 45, 48, 57, 46, 48, 57, 48,
57, 0, 13, 32, 44, 46, 65, 67,
69, 72, 76, 77, 81, 83, 84, 86,
90, 97, 99, 101, 104, 108, 109, 113,
115, 116, 118, 122, 9, 10, 43, 45,
48, 57, 0, 13, 32, 44, 46, 65,
67, 72, 76, 77, 81, 83, 84, 86,
90, 97, 99, 104, 108, 109, 113, 115,
116, 118, 122, 9, 10, 43, 45, 48,
57, 13, 32, 44, 46, 69, 101, 9,
10, 43, 45, 48, 57, 13, 32, 43,
45, 46, 9, 10, 48, 57, 0, 13,
32, 44, 46, 65, 67, 69, 72, 76,
77, 81, 83, 84, 86, 90, 97, 99,
101, 104, 108, 109, 113, 115, 116, 118,
122, 9, 10, 43, 45, 48, 57, 43,
45, 48, 57, 48, 57, 0, 13, 32,
44, 46, 65, 67, 72, 76, 77, 81,
83, 84, 86, 90, 97, 99, 104, 108,
109, 113, 115, 116, 118, 122, 9, 10,
43, 45, 48, 57, 13, 32, 43, 45,
46, 9, 10, 48, 57, 13, 32, 43,
45, 46, 9, 10, 48, 57, 46, 48,
57, 48, 57, 0, 13, 32, 44, 46,
65, 67, 69, 72, 76, 77, 81, 83,
84, 86, 90, 97, 99, 101, 104, 108,
109, 113, 115, 116, 118, 122, 9, 10,
43, 45, 48, 57, 0, 13, 32, 44,
46, 65, 67, 72, 76, 77, 81, 83,
84, 86, 90, 97, 99, 104, 108, 109,
113, 115, 116, 118, 122, 9, 10, 43,
45, 48, 57, 0, 13, 32, 44, 46,
65, 67, 69, 72, 76, 77, 81, 83,
84, 86, 90, 97, 99, 101, 104, 108,
109, 113, 115, 116, 118, 122, 9, 10,
43, 45, 48, 57, 43, 45, 48, 57,
48, 57, 0, 13, 32, 44, 46, 65,
67, 72, 76, 77, 81, 83, 84, 86,
90, 97, 99, 104, 108, 109, 113, 115,
116, 118, 122, 9, 10, 43, 45, 48,
57, 0, 13, 32, 65, 67, 72, 76,
77, 81, 83, 84, 86, 90, 97, 99,
104, 108, 109, 113, 115, 116, 118, 122,
9, 10, 0, 13, 32, 65, 67, 72,
76, 77, 81, 83, 84, 86, 90, 97,
99, 104, 108, 109, 113, 115, 116, 118,
122, 9, 10, 13, 32, 46, 9, 10,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 13, 32, 43, 45,
46, 9, 10, 48, 57, 13, 32, 43,
45, 46, 9, 10, 48, 57, 13, 32,
44, 46, 69, 101, 9, 10, 43, 45,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 0, 13, 32, 44, 46,
65, 67, 69, 72, 76, 77, 81, 83,
84, 86, 90, 97, 99, 101, 104, 108,
109, 113, 115, 116, 118, 122, 9, 10,
43, 45, 48, 57, 43, 45, 48, 57,
48, 57, 0, 13, 32, 44, 46, 65,
67, 72, 76, 77, 81, 83, 84, 86,
90, 97, 99, 104, 108, 109, 113, 115,
116, 118, 122, 9, 10, 43, 45, 48,
57, 13, 32, 43, 45, 46, 9, 10,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 13, 32, 43, 45,
46, 9, 10, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 0, 13, 32, 44, 46,
65, 67, 72, 76, 77, 81, 83, 84,
86, 90, 97, 99, 104, 108, 109, 113,
115, 116, 118, 122, 9, 10, 48, 57,
0, 13, 32, 44, 46, 65, 67, 69,
72, 76, 77, 81, 83, 84, 86, 90,
97, 99, 101, 104, 108, 109, 113, 115,
116, 118, 122, 9, 10, 48, 57, 13,
32, 43, 45, 46, 9, 10, 48, 57,
43, 45, 48, 57, 48, 57, 13, 32,
44, 46, 9, 10, 43, 45, 48, 57,
13, 32, 44, 46, 69, 101, 9, 10,
43, 45, 48, 57, 13, 32, 43, 45,
46, 9, 10, 48, 57, 13, 32, 44,
9, 10, 13, 32, 44, 9, 10, 43,
45, 48, 57, 48, 57, 13, 32, 44,
9, 10, 48, 57, 13, 32, 44, 46,
69, 101, 9, 10, 48, 57, 13, 32,
43, 45, 46, 9, 10, 48, 57, 43,
45, 48, 57, 48, 57, 13, 32, 44,
46, 9, 10, 43, 45, 48, 57, 13,
32, 44, 46, 69, 101, 9, 10, 43,
45, 48, 57, 43, 45, 48, 57, 48,
57, 0, 13, 32, 44, 46, 65, 67,
72, 76, 77, 81, 83, 84, 86, 90,
97, 99, 104, 108, 109, 113, 115, 116,
118, 122, 9, 10, 43, 45, 48, 57,
43, 45, 48, 57, 48, 57, 13, 32,
44, 46, 9, 10, 43, 45, 48, 57,
0
};
 
static const char _svg_path_single_lengths[] = {
0, 5, 5, 5, 1, 0, 6, 4,
1, 0, 27, 25, 1, 0, 6, 4,
1, 0, 27, 5, 6, 5, 27, 3,
3, 0, 6, 4, 3, 0, 6, 4,
1, 0, 5, 5, 4, 3, 5, 4,
3, 4, 1, 0, 6, 4, 1, 0,
27, 25, 6, 2, 0, 4, 5, 5,
1, 0, 6, 4, 1, 0, 6, 4,
1, 0, 6, 4, 1, 0, 6, 4,
1, 0, 6, 4, 1, 0, 27, 25,
6, 5, 6, 5, 6, 5, 6, 5,
6, 5, 27, 2, 0, 25, 5, 5,
1, 0, 27, 25, 27, 2, 0, 25,
5, 5, 5, 1, 0, 6, 4, 1,
0, 6, 4, 1, 0, 6, 4, 1,
0, 27, 25, 6, 5, 6, 5, 6,
5, 27, 2, 0, 25, 5, 5, 1,
0, 6, 4, 1, 0, 6, 4, 1,
0, 6, 4, 1, 0, 27, 25, 6,
5, 6, 5, 6, 5, 27, 2, 0,
25, 5, 5, 1, 0, 6, 4, 1,
0, 27, 25, 6, 5, 27, 2, 0,
25, 5, 5, 1, 0, 27, 25, 27,
2, 0, 25, 23, 23, 3, 5, 5,
5, 5, 6, 5, 27, 2, 0, 25,
5, 5, 5, 5, 2, 0, 4, 2,
0, 4, 2, 0, 4, 2, 0, 4,
2, 0, 4, 2, 0, 4, 2, 0,
4, 2, 0, 4, 2, 0, 4, 2,
0, 4, 2, 0, 4, 2, 0, 4,
2, 0, 4, 2, 0, 25, 27, 5,
2, 0, 4, 6, 5, 3, 3, 2,
0, 3, 6, 5, 2, 0, 4, 6,
2, 0, 25, 2, 0, 4, 0
};
 
static const char _svg_path_range_lengths[] = {
0, 1, 2, 2, 1, 1, 3, 3,
1, 1, 3, 3, 1, 1, 3, 3,
1, 1, 3, 2, 3, 2, 3, 2,
2, 1, 2, 2, 2, 1, 3, 3,
1, 1, 2, 1, 1, 1, 1, 1,
1, 3, 1, 1, 3, 3, 1, 1,
2, 2, 2, 1, 1, 2, 2, 2,
1, 1, 3, 3, 1, 1, 3, 3,
1, 1, 3, 3, 1, 1, 3, 3,
1, 1, 3, 3, 1, 1, 3, 3,
3, 2, 3, 2, 3, 2, 3, 2,
3, 2, 3, 1, 1, 3, 2, 2,
1, 1, 3, 3, 3, 1, 1, 3,
2, 2, 2, 1, 1, 3, 3, 1,
1, 3, 3, 1, 1, 3, 3, 1,
1, 3, 3, 3, 2, 3, 2, 3,
2, 3, 1, 1, 3, 2, 2, 1,
1, 3, 3, 1, 1, 3, 3, 1,
1, 3, 3, 1, 1, 3, 3, 3,
2, 3, 2, 3, 2, 3, 1, 1,
3, 2, 2, 1, 1, 3, 3, 1,
1, 3, 3, 3, 2, 3, 1, 1,
3, 2, 2, 1, 1, 3, 3, 3,
1, 1, 3, 1, 1, 2, 2, 2,
2, 2, 3, 2, 3, 1, 1, 3,
2, 2, 2, 2, 1, 1, 3, 1,
1, 3, 1, 1, 3, 1, 1, 3,
1, 1, 3, 1, 1, 3, 1, 1,
3, 1, 1, 3, 1, 1, 3, 1,
1, 3, 1, 1, 3, 1, 1, 3,
1, 1, 3, 1, 1, 2, 2, 2,
1, 1, 3, 3, 2, 1, 1, 1,
1, 2, 2, 2, 1, 1, 3, 3,
1, 1, 3, 1, 1, 3, 0
};
 
static const short _svg_path_index_offsets[] = {
0, 0, 7, 15, 23, 26, 28, 38,
46, 49, 51, 82, 111, 114, 116, 126,
134, 137, 139, 170, 178, 188, 196, 227,
233, 239, 241, 250, 257, 263, 265, 275,
283, 286, 288, 296, 303, 309, 314, 321,
327, 332, 340, 343, 345, 355, 363, 366,
368, 398, 426, 435, 439, 441, 448, 456,
464, 467, 469, 479, 487, 490, 492, 502,
510, 513, 515, 525, 533, 536, 538, 548,
556, 559, 561, 571, 579, 582, 584, 615,
644, 654, 662, 672, 680, 690, 698, 708,
716, 726, 734, 765, 769, 771, 800, 808,
816, 819, 821, 852, 881, 912, 916, 918,
947, 955, 963, 971, 974, 976, 986, 994,
997, 999, 1009, 1017, 1020, 1022, 1032, 1040,
1043, 1045, 1076, 1105, 1115, 1123, 1133, 1141,
1151, 1159, 1190, 1194, 1196, 1225, 1233, 1241,
1244, 1246, 1256, 1264, 1267, 1269, 1279, 1287,
1290, 1292, 1302, 1310, 1313, 1315, 1346, 1375,
1385, 1393, 1403, 1411, 1421, 1429, 1460, 1464,
1466, 1495, 1503, 1511, 1514, 1516, 1526, 1534,
1537, 1539, 1570, 1599, 1609, 1617, 1648, 1652,
1654, 1683, 1691, 1699, 1702, 1704, 1735, 1764,
1795, 1799, 1801, 1830, 1855, 1880, 1886, 1894,
1902, 1910, 1918, 1928, 1936, 1967, 1971, 1973,
2002, 2010, 2018, 2026, 2034, 2038, 2040, 2048,
2052, 2054, 2062, 2066, 2068, 2076, 2080, 2082,
2090, 2094, 2096, 2104, 2108, 2110, 2118, 2122,
2124, 2132, 2136, 2138, 2146, 2150, 2152, 2160,
2164, 2166, 2174, 2178, 2180, 2188, 2192, 2194,
2202, 2206, 2208, 2216, 2220, 2222, 2250, 2280,
2288, 2292, 2294, 2302, 2312, 2320, 2325, 2330,
2334, 2336, 2342, 2351, 2359, 2363, 2365, 2373,
2383, 2387, 2389, 2418, 2422, 2424, 2432
};
 
static const short _svg_path_indicies[] = {
0, 2, 2, 3, 4, 2, 1, 5,
5, 6, 6, 7, 5, 8, 1, 9,
9, 10, 10, 11, 9, 12, 1, 13,
14, 1, 15, 1, 16, 16, 18, 19,
20, 20, 16, 17, 15, 1, 21, 21,
23, 24, 21, 22, 25, 1, 26, 27,
1, 28, 1, 29, 30, 30, 32, 33,
34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 36, 47, 48,
49, 50, 51, 52, 53, 44, 30, 31,
28, 1, 0, 54, 54, 56, 57, 59,
60, 61, 62, 3, 63, 64, 65, 66,
67, 68, 69, 70, 71, 4, 72, 73,
74, 75, 67, 54, 55, 58, 1, 76,
77, 1, 78, 1, 79, 79, 81, 82,
83, 83, 79, 80, 78, 1, 84, 84,
86, 87, 84, 85, 88, 1, 89, 90,
1, 91, 1, 92, 93, 93, 95, 96,
97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 99, 110, 111,
112, 113, 114, 115, 116, 107, 93, 94,
91, 1, 56, 56, 55, 55, 57, 56,
58, 1, 79, 79, 81, 78, 83, 83,
79, 80, 77, 1, 86, 86, 85, 85,
87, 86, 88, 1, 92, 93, 93, 95,
91, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 99, 110,
111, 112, 113, 114, 115, 116, 107, 93,
94, 90, 1, 117, 117, 118, 117, 119,
1, 120, 120, 121, 120, 122, 1, 123,
1, 124, 124, 125, 126, 127, 127, 124,
123, 1, 128, 128, 129, 130, 128, 131,
1, 129, 129, 130, 129, 131, 1, 132,
1, 133, 133, 135, 136, 137, 137, 133,
134, 132, 1, 138, 138, 140, 141, 138,
139, 142, 1, 143, 144, 1, 145, 1,
146, 146, 147, 148, 148, 146, 145, 1,
149, 149, 150, 151, 152, 149, 1, 150,
150, 151, 152, 150, 1, 153, 153, 154,
153, 1, 155, 155, 156, 157, 158, 155,
1, 156, 156, 157, 158, 156, 1, 159,
159, 160, 159, 1, 161, 161, 163, 164,
161, 162, 165, 1, 166, 167, 1, 168,
1, 169, 169, 171, 172, 173, 173, 169,
170, 168, 1, 174, 174, 176, 177, 174,
175, 178, 1, 179, 180, 1, 181, 1,
182, 183, 183, 184, 185, 186, 187, 188,
189, 190, 191, 192, 193, 194, 195, 196,
197, 198, 188, 199, 200, 201, 202, 203,
204, 205, 196, 183, 181, 1, 0, 206,
206, 120, 121, 59, 60, 61, 62, 3,
63, 64, 65, 66, 67, 68, 69, 70,
71, 4, 72, 73, 74, 75, 67, 206,
122, 1, 124, 124, 125, 123, 127, 127,
124, 207, 1, 208, 208, 209, 1, 209,
1, 124, 124, 125, 126, 124, 209, 1,
210, 210, 211, 211, 212, 210, 213, 1,
214, 214, 215, 215, 216, 214, 217, 1,
218, 219, 1, 220, 1, 221, 221, 223,
224, 225, 225, 221, 222, 220, 1, 226,
226, 228, 229, 226, 227, 230, 1, 231,
232, 1, 233, 1, 234, 234, 236, 237,
238, 238, 234, 235, 233, 1, 239, 239,
241, 242, 239, 240, 243, 1, 244, 245,
1, 246, 1, 247, 247, 249, 250, 251,
251, 247, 248, 246, 1, 252, 252, 254,
255, 252, 253, 256, 1, 257, 258, 1,
259, 1, 260, 260, 262, 263, 264, 264,
260, 261, 259, 1, 265, 265, 267, 268,
265, 266, 269, 1, 270, 271, 1, 272,
1, 273, 273, 275, 276, 277, 277, 273,
274, 272, 1, 278, 278, 280, 281, 278,
279, 282, 1, 283, 284, 1, 285, 1,
286, 287, 287, 289, 290, 291, 292, 293,
294, 295, 296, 297, 298, 299, 300, 301,
302, 303, 293, 304, 305, 306, 307, 308,
309, 310, 301, 287, 288, 285, 1, 0,
311, 311, 214, 216, 59, 60, 61, 62,
3, 63, 64, 65, 66, 67, 68, 69,
70, 71, 4, 72, 73, 74, 75, 67,
311, 215, 217, 1, 221, 221, 223, 220,
225, 225, 221, 222, 219, 1, 228, 228,
227, 227, 229, 228, 230, 1, 234, 234,
236, 233, 238, 238, 234, 235, 232, 1,
241, 241, 240, 240, 242, 241, 243, 1,
247, 247, 249, 246, 251, 251, 247, 248,
245, 1, 254, 254, 253, 253, 255, 254,
256, 1, 260, 260, 262, 259, 264, 264,
260, 261, 258, 1, 267, 267, 266, 266,
268, 267, 269, 1, 273, 273, 275, 272,
277, 277, 273, 274, 271, 1, 280, 280,
279, 279, 281, 280, 282, 1, 286, 287,
287, 289, 285, 291, 292, 293, 294, 295,
296, 297, 298, 299, 300, 301, 302, 303,
293, 304, 305, 306, 307, 308, 309, 310,
301, 287, 288, 284, 1, 312, 312, 313,
1, 313, 1, 286, 287, 287, 289, 290,
291, 292, 294, 295, 296, 297, 298, 299,
300, 301, 302, 303, 304, 305, 306, 307,
308, 309, 310, 301, 287, 288, 313, 1,
314, 314, 315, 315, 316, 314, 317, 1,
318, 318, 319, 319, 320, 318, 321, 1,
322, 323, 1, 324, 1, 325, 326, 326,
328, 329, 330, 331, 332, 333, 334, 335,
336, 337, 338, 339, 340, 341, 342, 332,
343, 344, 345, 346, 347, 348, 349, 340,
326, 327, 324, 1, 0, 350, 350, 318,
320, 59, 60, 61, 62, 3, 63, 64,
65, 66, 67, 68, 69, 70, 71, 4,
72, 73, 74, 75, 67, 350, 319, 321,
1, 325, 326, 326, 328, 324, 330, 331,
332, 333, 334, 335, 336, 337, 338, 339,
340, 341, 342, 332, 343, 344, 345, 346,
347, 348, 349, 340, 326, 327, 323, 1,
351, 351, 352, 1, 352, 1, 325, 326,
326, 328, 329, 330, 331, 333, 334, 335,
336, 337, 338, 339, 340, 341, 342, 343,
344, 345, 346, 347, 348, 349, 340, 326,
327, 352, 1, 353, 353, 354, 354, 355,
353, 356, 1, 357, 357, 358, 358, 359,
357, 360, 1, 361, 361, 362, 362, 363,
361, 364, 1, 365, 366, 1, 367, 1,
368, 368, 370, 371, 372, 372, 368, 369,
367, 1, 373, 373, 375, 376, 373, 374,
377, 1, 378, 379, 1, 380, 1, 381,
381, 383, 384, 385, 385, 381, 382, 380,
1, 386, 386, 388, 389, 386, 387, 390,
1, 391, 392, 1, 393, 1, 394, 394,
396, 397, 398, 398, 394, 395, 393, 1,
399, 399, 401, 402, 399, 400, 403, 1,
404, 405, 1, 406, 1, 407, 408, 408,
410, 411, 412, 413, 414, 415, 416, 417,
418, 419, 420, 421, 422, 423, 424, 414,
425, 426, 427, 428, 429, 430, 431, 422,
408, 409, 406, 1, 0, 432, 432, 361,
363, 59, 60, 61, 62, 3, 63, 64,
65, 66, 67, 68, 69, 70, 71, 4,
72, 73, 74, 75, 67, 432, 362, 364,
1, 368, 368, 370, 367, 372, 372, 368,
369, 366, 1, 375, 375, 374, 374, 376,
375, 377, 1, 381, 381, 383, 380, 385,
385, 381, 382, 379, 1, 388, 388, 387,
387, 389, 388, 390, 1, 394, 394, 396,
393, 398, 398, 394, 395, 392, 1, 401,
401, 400, 400, 402, 401, 403, 1, 407,
408, 408, 410, 406, 412, 413, 414, 415,
416, 417, 418, 419, 420, 421, 422, 423,
424, 414, 425, 426, 427, 428, 429, 430,
431, 422, 408, 409, 405, 1, 433, 433,
434, 1, 434, 1, 407, 408, 408, 410,
411, 412, 413, 415, 416, 417, 418, 419,
420, 421, 422, 423, 424, 425, 426, 427,
428, 429, 430, 431, 422, 408, 409, 434,
1, 435, 435, 436, 436, 437, 435, 438,
1, 439, 439, 440, 440, 441, 439, 442,
1, 443, 444, 1, 445, 1, 446, 446,
448, 449, 450, 450, 446, 447, 445, 1,
451, 451, 453, 454, 451, 452, 455, 1,
456, 457, 1, 458, 1, 459, 459, 461,
462, 463, 463, 459, 460, 458, 1, 464,
464, 466, 467, 464, 465, 468, 1, 469,
470, 1, 471, 1, 472, 472, 474, 475,
476, 476, 472, 473, 471, 1, 477, 477,
479, 480, 477, 478, 481, 1, 482, 483,
1, 484, 1, 485, 486, 486, 488, 489,
490, 491, 492, 493, 494, 495, 496, 497,
498, 499, 500, 501, 502, 492, 503, 504,
505, 506, 507, 508, 509, 500, 486, 487,
484, 1, 0, 510, 510, 439, 441, 59,
60, 61, 62, 3, 63, 64, 65, 66,
67, 68, 69, 70, 71, 4, 72, 73,
74, 75, 67, 510, 440, 442, 1, 446,
446, 448, 445, 450, 450, 446, 447, 444,
1, 453, 453, 452, 452, 454, 453, 455,
1, 459, 459, 461, 458, 463, 463, 459,
460, 457, 1, 466, 466, 465, 465, 467,
466, 468, 1, 472, 472, 474, 471, 476,
476, 472, 473, 470, 1, 479, 479, 478,
478, 480, 479, 481, 1, 485, 486, 486,
488, 484, 490, 491, 492, 493, 494, 495,
496, 497, 498, 499, 500, 501, 502, 492,
503, 504, 505, 506, 507, 508, 509, 500,
486, 487, 483, 1, 511, 511, 512, 1,
512, 1, 485, 486, 486, 488, 489, 490,
491, 493, 494, 495, 496, 497, 498, 499,
500, 501, 502, 503, 504, 505, 506, 507,
508, 509, 500, 486, 487, 512, 1, 513,
513, 514, 514, 515, 513, 516, 1, 517,
517, 518, 518, 519, 517, 520, 1, 521,
522, 1, 523, 1, 524, 524, 526, 527,
528, 528, 524, 525, 523, 1, 529, 529,
531, 532, 529, 530, 533, 1, 534, 535,
1, 536, 1, 537, 538, 538, 540, 541,
542, 543, 544, 545, 546, 547, 548, 549,
550, 551, 552, 553, 554, 544, 555, 556,
557, 558, 559, 560, 561, 552, 538, 539,
536, 1, 0, 562, 562, 517, 519, 59,
60, 61, 62, 3, 63, 64, 65, 66,
67, 68, 69, 70, 71, 4, 72, 73,
74, 75, 67, 562, 518, 520, 1, 524,
524, 526, 523, 528, 528, 524, 525, 522,
1, 531, 531, 530, 530, 532, 531, 533,
1, 537, 538, 538, 540, 536, 542, 543,
544, 545, 546, 547, 548, 549, 550, 551,
552, 553, 554, 544, 555, 556, 557, 558,
559, 560, 561, 552, 538, 539, 535, 1,
563, 563, 564, 1, 564, 1, 537, 538,
538, 540, 541, 542, 543, 545, 546, 547,
548, 549, 550, 551, 552, 553, 554, 555,
556, 557, 558, 559, 560, 561, 552, 538,
539, 564, 1, 565, 565, 566, 566, 567,
565, 568, 1, 569, 569, 570, 570, 571,
569, 572, 1, 573, 574, 1, 575, 1,
576, 577, 577, 579, 580, 581, 582, 583,
584, 585, 586, 587, 588, 589, 590, 591,
592, 593, 583, 594, 595, 596, 597, 598,
599, 600, 591, 577, 578, 575, 1, 0,
601, 601, 569, 571, 59, 60, 61, 62,
3, 63, 64, 65, 66, 67, 68, 69,
70, 71, 4, 72, 73, 74, 75, 67,
601, 570, 572, 1, 576, 577, 577, 579,
575, 581, 582, 583, 584, 585, 586, 587,
588, 589, 590, 591, 592, 593, 583, 594,
595, 596, 597, 598, 599, 600, 591, 577,
578, 574, 1, 602, 602, 603, 1, 603,
1, 576, 577, 577, 579, 580, 581, 582,
584, 585, 586, 587, 588, 589, 590, 591,
592, 593, 594, 595, 596, 597, 598, 599,
600, 591, 577, 578, 603, 1, 604, 605,
605, 606, 607, 608, 609, 610, 611, 612,
613, 614, 615, 616, 617, 618, 619, 620,
621, 622, 623, 624, 615, 605, 1, 0,
625, 625, 59, 60, 61, 62, 3, 63,
64, 65, 66, 67, 68, 69, 70, 71,
4, 72, 73, 74, 75, 67, 625, 1,
626, 626, 627, 626, 628, 1, 629, 629,
630, 630, 631, 629, 632, 1, 633, 633,
634, 634, 635, 633, 636, 1, 637, 637,
638, 638, 639, 637, 640, 1, 641, 641,
642, 642, 643, 641, 644, 1, 16, 16,
18, 15, 20, 20, 16, 17, 14, 1,
23, 23, 22, 22, 24, 23, 25, 1,
29, 30, 30, 32, 28, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44,
45, 46, 36, 47, 48, 49, 50, 51,
52, 53, 44, 30, 31, 27, 1, 645,
645, 646, 1, 646, 1, 29, 30, 30,
32, 33, 34, 35, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 44, 30, 31,
646, 1, 647, 647, 648, 648, 649, 647,
650, 1, 651, 651, 652, 652, 653, 651,
654, 1, 655, 655, 656, 656, 657, 655,
658, 1, 659, 659, 660, 660, 661, 659,
662, 1, 663, 663, 664, 1, 664, 1,
16, 16, 18, 19, 16, 17, 664, 1,
665, 665, 666, 1, 666, 1, 524, 524,
526, 527, 524, 525, 666, 1, 667, 667,
668, 1, 668, 1, 472, 472, 474, 475,
472, 473, 668, 1, 669, 669, 670, 1,
670, 1, 459, 459, 461, 462, 459, 460,
670, 1, 671, 671, 672, 1, 672, 1,
446, 446, 448, 449, 446, 447, 672, 1,
673, 673, 674, 1, 674, 1, 394, 394,
396, 397, 394, 395, 674, 1, 675, 675,
676, 1, 676, 1, 381, 381, 383, 384,
381, 382, 676, 1, 677, 677, 678, 1,
678, 1, 368, 368, 370, 371, 368, 369,
678, 1, 679, 679, 680, 1, 680, 1,
273, 273, 275, 276, 273, 274, 680, 1,
681, 681, 682, 1, 682, 1, 260, 260,
262, 263, 260, 261, 682, 1, 683, 683,
684, 1, 684, 1, 247, 247, 249, 250,
247, 248, 684, 1, 685, 685, 686, 1,
686, 1, 234, 234, 236, 237, 234, 235,
686, 1, 687, 687, 688, 1, 688, 1,
221, 221, 223, 224, 221, 222, 688, 1,
689, 689, 690, 1, 690, 1, 182, 183,
183, 184, 185, 186, 187, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199,
200, 201, 202, 203, 204, 205, 196, 183,
690, 1, 182, 183, 183, 184, 181, 186,
187, 188, 189, 190, 191, 192, 193, 194,
195, 196, 197, 198, 188, 199, 200, 201,
202, 203, 204, 205, 196, 183, 180, 1,
176, 176, 175, 175, 177, 176, 178, 1,
691, 691, 692, 1, 692, 1, 169, 169,
171, 172, 169, 170, 692, 1, 169, 169,
171, 168, 173, 173, 169, 170, 167, 1,
163, 163, 162, 162, 164, 163, 165, 1,
693, 693, 694, 693, 1, 695, 695, 696,
695, 1, 697, 697, 698, 1, 698, 1,
146, 146, 147, 146, 698, 1, 146, 146,
147, 145, 148, 148, 146, 144, 1, 140,
140, 139, 139, 141, 140, 142, 1, 699,
699, 700, 1, 700, 1, 133, 133, 135,
136, 133, 134, 700, 1, 133, 133, 135,
132, 137, 137, 133, 134, 701, 1, 702,
702, 703, 1, 703, 1, 92, 93, 93,
95, 96, 97, 98, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 107, 93, 94,
703, 1, 704, 704, 705, 1, 705, 1,
79, 79, 81, 82, 79, 80, 705, 1,
1, 0
};
 
static const short _svg_path_trans_targs_wi[] = {
270, 0, 1, 2, 193, 3, 4, 5,
194, 3, 4, 5, 194, 5, 194, 6,
7, 8, 195, 9, 204, 7, 8, 195,
9, 196, 9, 196, 10, 270, 11, 12,
19, 13, 23, 54, 197, 94, 104, 2,
105, 133, 161, 177, 187, 189, 190, 191,
192, 193, 200, 201, 202, 203, 11, 12,
19, 13, 20, 23, 54, 94, 104, 105,
133, 161, 177, 187, 189, 190, 191, 192,
200, 201, 202, 203, 13, 20, 14, 15,
16, 21, 17, 267, 15, 16, 21, 17,
22, 17, 22, 18, 270, 11, 12, 19,
13, 23, 54, 264, 94, 104, 2, 105,
133, 161, 177, 187, 189, 190, 191, 192,
193, 200, 201, 202, 203, 24, 25, 50,
24, 25, 50, 26, 27, 28, 29, 51,
27, 28, 29, 263, 30, 31, 32, 259,
33, 260, 31, 32, 259, 33, 258, 33,
258, 34, 35, 36, 255, 35, 36, 37,
254, 38, 39, 38, 39, 40, 253, 41,
252, 41, 42, 252, 43, 251, 43, 251,
44, 45, 46, 247, 47, 248, 45, 46,
247, 47, 246, 47, 246, 48, 270, 49,
24, 25, 23, 54, 243, 94, 104, 2,
105, 133, 161, 177, 187, 189, 190, 191,
192, 193, 200, 201, 202, 203, 49, 50,
52, 53, 55, 56, 57, 80, 55, 56,
57, 80, 57, 80, 58, 59, 60, 81,
61, 240, 59, 60, 81, 61, 82, 61,
82, 62, 63, 64, 83, 65, 237, 63,
64, 83, 65, 84, 65, 84, 66, 67,
68, 85, 69, 234, 67, 68, 85, 69,
86, 69, 86, 70, 71, 72, 87, 73,
231, 71, 72, 87, 73, 88, 73, 88,
74, 75, 76, 89, 77, 228, 75, 76,
89, 77, 90, 77, 90, 78, 270, 79,
56, 55, 57, 23, 54, 91, 94, 104,
2, 105, 133, 161, 177, 187, 189, 190,
191, 192, 193, 200, 201, 202, 203, 79,
92, 93, 95, 96, 97, 100, 95, 96,
97, 100, 97, 100, 98, 270, 99, 96,
95, 97, 23, 54, 101, 94, 104, 2,
105, 133, 161, 177, 187, 189, 190, 191,
192, 193, 200, 201, 202, 203, 99, 102,
103, 19, 12, 13, 20, 106, 107, 108,
123, 106, 107, 108, 123, 108, 123, 109,
110, 111, 124, 112, 225, 110, 111, 124,
112, 125, 112, 125, 113, 114, 115, 126,
116, 222, 114, 115, 126, 116, 127, 116,
127, 117, 118, 119, 128, 120, 219, 118,
119, 128, 120, 129, 120, 129, 121, 270,
122, 107, 106, 108, 23, 54, 130, 94,
104, 2, 105, 133, 161, 177, 187, 189,
190, 191, 192, 193, 200, 201, 202, 203,
122, 131, 132, 134, 135, 136, 151, 134,
135, 136, 151, 136, 151, 137, 138, 139,
152, 140, 216, 138, 139, 152, 140, 153,
140, 153, 141, 142, 143, 154, 144, 213,
142, 143, 154, 144, 155, 144, 155, 145,
146, 147, 156, 148, 210, 146, 147, 156,
148, 157, 148, 157, 149, 270, 150, 135,
134, 136, 23, 54, 158, 94, 104, 2,
105, 133, 161, 177, 187, 189, 190, 191,
192, 193, 200, 201, 202, 203, 150, 159,
160, 162, 163, 164, 171, 162, 163, 164,
171, 164, 171, 165, 166, 167, 172, 168,
207, 166, 167, 172, 168, 173, 168, 173,
169, 270, 170, 163, 162, 164, 23, 54,
174, 94, 104, 2, 105, 133, 161, 177,
187, 189, 190, 191, 192, 193, 200, 201,
202, 203, 170, 175, 176, 178, 179, 180,
183, 178, 179, 180, 183, 180, 183, 181,
270, 182, 179, 178, 180, 23, 54, 184,
94, 104, 2, 105, 133, 161, 177, 187,
189, 190, 191, 192, 193, 200, 201, 202,
203, 182, 185, 186, 270, 188, 23, 54,
94, 104, 2, 105, 133, 161, 177, 187,
189, 190, 191, 192, 193, 200, 201, 202,
203, 188, 24, 25, 50, 55, 56, 57,
80, 95, 96, 97, 100, 19, 12, 13,
20, 3, 4, 5, 194, 198, 199, 106,
107, 108, 123, 134, 135, 136, 151, 162,
163, 164, 171, 178, 179, 180, 183, 205,
206, 208, 209, 211, 212, 214, 215, 217,
218, 220, 221, 223, 224, 226, 227, 229,
230, 232, 233, 235, 236, 238, 239, 241,
242, 244, 245, 249, 250, 41, 252, 38,
39, 256, 257, 261, 262, 263, 265, 266,
268, 269
};
 
static const char _svg_path_trans_actions_wi[] = {
15, 0, 0, 0, 0, 9, 47, 47,
47, 0, 1, 1, 1, 0, 0, 0,
3, 17, 3, 17, 0, 0, 1, 0,
1, 1, 0, 0, 0, 60, 20, 56,
20, 56, 20, 20, 0, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 0, 1,
0, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 3,
17, 3, 17, 0, 0, 1, 0, 1,
1, 0, 0, 0, 68, 23, 64, 23,
64, 23, 23, 0, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 9, 47, 47,
0, 1, 1, 0, 3, 3, 17, 0,
0, 0, 1, 1, 0, 3, 17, 3,
17, 0, 0, 1, 0, 1, 1, 0,
0, 0, 3, 3, 0, 0, 0, 0,
0, 7, 7, 0, 0, 0, 0, 7,
7, 0, 1, 0, 1, 1, 0, 0,
0, 3, 17, 3, 17, 0, 0, 1,
0, 1, 1, 0, 0, 0, 124, 44,
44, 120, 44, 44, 0, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 0, 0,
0, 0, 9, 47, 47, 47, 0, 1,
1, 1, 0, 0, 0, 3, 17, 3,
17, 0, 0, 1, 0, 1, 1, 0,
0, 0, 3, 17, 3, 17, 0, 0,
1, 0, 1, 1, 0, 0, 0, 3,
17, 3, 17, 0, 0, 1, 0, 1,
1, 0, 0, 0, 3, 17, 3, 17,
0, 0, 1, 0, 1, 1, 0, 0,
0, 3, 17, 3, 17, 0, 0, 1,
0, 1, 1, 0, 0, 0, 92, 32,
88, 32, 88, 32, 32, 0, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 0,
0, 0, 9, 47, 47, 47, 0, 1,
1, 1, 0, 0, 0, 76, 26, 72,
26, 72, 26, 26, 0, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 0, 0,
0, 9, 47, 47, 47, 9, 47, 47,
47, 0, 1, 1, 1, 0, 0, 0,
3, 17, 3, 17, 0, 0, 1, 0,
1, 1, 0, 0, 0, 3, 17, 3,
17, 0, 0, 1, 0, 1, 1, 0,
0, 0, 3, 17, 3, 17, 0, 0,
1, 0, 1, 1, 0, 0, 0, 108,
38, 104, 38, 104, 38, 38, 0, 38,
38, 38, 38, 38, 38, 38, 38, 38,
38, 38, 38, 38, 38, 38, 38, 38,
0, 0, 0, 9, 47, 47, 47, 0,
1, 1, 1, 0, 0, 0, 3, 17,
3, 17, 0, 0, 1, 0, 1, 1,
0, 0, 0, 3, 17, 3, 17, 0,
0, 1, 0, 1, 1, 0, 0, 0,
3, 17, 3, 17, 0, 0, 1, 0,
1, 1, 0, 0, 0, 100, 35, 96,
35, 96, 35, 35, 0, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 0, 0,
0, 9, 47, 47, 47, 0, 1, 1,
1, 0, 0, 0, 3, 17, 3, 17,
0, 0, 1, 0, 1, 1, 0, 0,
0, 116, 41, 112, 41, 112, 41, 41,
0, 41, 41, 41, 41, 41, 41, 41,
41, 41, 41, 41, 41, 41, 41, 41,
41, 41, 0, 0, 0, 9, 47, 47,
47, 0, 1, 1, 1, 0, 0, 0,
84, 29, 80, 29, 80, 29, 29, 0,
29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29,
29, 0, 0, 0, 53, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13,
13, 0, 11, 50, 50, 11, 50, 50,
50, 11, 50, 50, 50, 11, 50, 50,
50, 11, 50, 50, 50, 0, 0, 11,
50, 50, 50, 11, 50, 50, 50, 11,
50, 50, 50, 11, 50, 50, 50, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 5, 5, 5,
5, 0, 0, 0, 0, 0, 0, 0,
0, 0
};
 
static const int svg_path_start = 1;
static const int svg_path_first_final = 270;
 
static const int svg_path_en_main = 1;
 
#line 144 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
 
 
void Parser::parse(char const *str)
throw(SVGPathParseError)
{
char const *p = str;
char const *start = NULL;
int cs;
 
_reset();
 
#line 1162 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.cpp"
{
cs = svg_path_start;
}
 
#line 1167 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.cpp"
{
int _klen;
unsigned int _trans;
const char *_acts;
unsigned int _nacts;
const char *_keys;
 
if ( cs == 0 )
goto _out;
_resume:
_keys = _svg_path_trans_keys + _svg_path_key_offsets[cs];
_trans = _svg_path_index_offsets[cs];
 
_klen = _svg_path_single_lengths[cs];
if ( _klen > 0 ) {
const char *_lower = _keys;
const char *_mid;
const char *_upper = _keys + _klen - 1;
while (1) {
if ( _upper < _lower )
break;
 
_mid = _lower + ((_upper-_lower) >> 1);
if ( (*p) < *_mid )
_upper = _mid - 1;
else if ( (*p) > *_mid )
_lower = _mid + 1;
else {
_trans += (_mid - _keys);
goto _match;
}
}
_keys += _klen;
_trans += _klen;
}
 
_klen = _svg_path_range_lengths[cs];
if ( _klen > 0 ) {
const char *_lower = _keys;
const char *_mid;
const char *_upper = _keys + (_klen<<1) - 2;
while (1) {
if ( _upper < _lower )
break;
 
_mid = _lower + (((_upper-_lower) >> 1) & ~1);
if ( (*p) < _mid[0] )
_upper = _mid - 2;
else if ( (*p) > _mid[1] )
_lower = _mid + 2;
else {
_trans += ((_mid - _keys)>>1);
goto _match;
}
}
_trans += _klen;
}
 
_match:
_trans = _svg_path_indicies[_trans];
cs = _svg_path_trans_targs_wi[_trans];
 
if ( _svg_path_trans_actions_wi[_trans] == 0 )
goto _again;
 
_acts = _svg_path_actions + _svg_path_trans_actions_wi[_trans];
_nacts = (unsigned int) *_acts++;
while ( _nacts-- > 0 )
{
switch ( *_acts++ )
{
case 0:
#line 156 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
start = p;
}
break;
case 1:
#line 160 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
char const *end=p;
std::string buf(start, end);
_push(g_ascii_strtod(buf.c_str(), NULL));
start = NULL;
}
break;
case 2:
#line 167 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_push(1.0);
}
break;
case 3:
#line 171 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_push(0.0);
}
break;
case 4:
#line 175 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_absolute = true;
}
break;
case 5:
#line 179 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_absolute = false;
}
break;
case 6:
#line 183 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_moveTo(_pop_point());
}
break;
case 7:
#line 187 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_lineTo(_pop_point());
}
break;
case 8:
#line 191 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_hlineTo(Point(_pop_coord(X), _current[Y]));
}
break;
case 9:
#line 195 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_vlineTo(Point(_current[X], _pop_coord(Y)));
}
break;
case 10:
#line 199 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
Point c0 = _pop_point();
_curveTo(c0, c1, p);
}
break;
case 11:
#line 206 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
_curveTo(_cubic_tangent, c1, p);
}
break;
case 12:
#line 212 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c = _pop_point();
_quadTo(c, p);
}
break;
case 13:
#line 218 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
_quadTo(_quad_tangent, p);
}
break;
case 14:
#line 223 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
Point point = _pop_point();
bool sweep = _pop_flag();
bool large_arc = _pop_flag();
double angle = deg_to_rad(_pop());
double ry = _pop();
double rx = _pop();
 
_arcTo(rx, ry, angle, large_arc, sweep, point);
}
break;
case 15:
#line 234 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_closePath();
}
break;
case 16:
#line 370 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{goto _out;}
break;
#line 1357 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.cpp"
}
}
 
_again:
if ( cs == 0 )
goto _out;
p += 1;
goto _resume;
_out: {}
}
#line 380 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
 
 
if ( cs < svg_path_first_final ) {
throw SVGPathParseError();
}
}
 
}
 
void parse_svg_path(char const *str, SVGPathSink &sink)
throw(SVGPathParseError)
{
Parser parser(sink);
parser.parse(str);
sink.finish();
}
 
}
 
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/bezier-clipping.cpp
0,0 → 1,1292
/*
* Implement the Bezier clipping algorithm for finding
* Bezier curve intersection points and collinear normals
*
* Authors:
* Marco Cecchetti <mrcekets at gmail.com>
*
* Copyright 2008 authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*/
 
 
 
 
#include <basic-intersection.h>
#include <choose.h>
#include <point.h>
#include <interval.h>
#include <bezier.h>
//#include <2geom/convex-cover.h>
#include <numeric/matrix.h>
 
#include <cassert>
#include <vector>
#include <algorithm>
#include <utility>
//#include <iomanip>
 
 
 
 
#define VERBOSE 0
#define CHECK 0
 
 
namespace Geom {
 
namespace detail { namespace bezier_clipping {
 
////////////////////////////////////////////////////////////////////////////////
// for debugging
//
 
inline
void print(std::vector<Point> const& cp, const char* msg = "")
{
std::cerr << msg << std::endl;
for (size_t i = 0; i < cp.size(); ++i)
std::cerr << i << " : " << cp[i] << std::endl;
}
 
template< class charT >
inline
std::basic_ostream<charT> &
operator<< (std::basic_ostream<charT> & os, const Interval & I)
{
os << "[" << I.min() << ", " << I.max() << "]";
return os;
}
 
inline
double angle (std::vector<Point> const& A)
{
size_t n = A.size() -1;
double a = std::atan2(A[n][Y] - A[0][Y], A[n][X] - A[0][X]);
return (180 * a / M_PI);
}
 
inline
size_t get_precision(Interval const& I)
{
double d = I.extent();
double e = 0.1, p = 10;
int n = 0;
while (n < 16 && d < e)
{
p *= 10;
e = 1/p;
++n;
}
return n;
}
 
inline
void range_assertion(int k, int m, int n, const char* msg)
{
if ( k < m || k > n)
{
std::cerr << "range assertion failed: \n"
<< msg << std::endl
<< "value: " << k
<< " range: " << m << ", " << n << std::endl;
assert (k >= m && k <= n);
}
}
 
 
////////////////////////////////////////////////////////////////////////////////
// convex hull
 
/*
* return true in case the oriented polyline p0, p1, p2 is a right turn
*/
inline
bool is_a_right_turn (Point const& p0, Point const& p1, Point const& p2)
{
if (p1 == p2) return false;
Point q1 = p1 - p0;
Point q2 = p2 - p0;
if (q1 == -q2) return false;
return (cross (q1, q2) < 0);
}
 
/*
* return true if p < q wrt the lexicographyc order induced by the coordinates
*/
struct lex_less
{
bool operator() (Point const& p, Point const& q)
{
return ((p[X] < q[X]) || (p[X] == q[X] && p[Y] < q[Y]));
}
};
 
/*
* return true if p > q wrt the lexicographyc order induced by the coordinates
*/
struct lex_greater
{
bool operator() (Point const& p, Point const& q)
{
return ((p[X] > q[X]) || (p[X] == q[X] && p[Y] > q[Y]));
}
};
 
/*
* Compute the convex hull of a set of points.
* The implementation is based on the Andrew's scan algorithm
* note: in the Bezier clipping for collinear normals it seems
* to be more stable wrt the Graham's scan algorithm and in general
* a bit quikier
*/
void convex_hull (std::vector<Point> & P)
{
size_t n = P.size();
if (n < 2) return;
std::sort(P.begin(), P.end(), lex_less());
if (n < 4) return;
// upper hull
size_t u = 2;
for (size_t i = 2; i < n; ++i)
{
while (u > 1 && !is_a_right_turn(P[u-2], P[u-1], P[i]))
{
--u;
}
std::swap(P[u], P[i]);
++u;
}
std::sort(P.begin() + u, P.end(), lex_greater());
std::rotate(P.begin(), P.begin() + 1, P.end());
// lower hull
size_t l = u;
size_t k = u - 1;
for (size_t i = l; i < n; ++i)
{
while (l > k && !is_a_right_turn(P[l-2], P[l-1], P[i]))
{
--l;
}
std::swap(P[l], P[i]);
++l;
}
P.resize(l);
}
 
 
////////////////////////////////////////////////////////////////////////////////
// numerical routines
 
/*
* Compute the binomial coefficient (n, k)
*/
inline
double binomial(unsigned int n, unsigned int k)
{
return choose<double>(n, k);
}
 
/*
* Compute the determinant of the 2x2 matrix with column the point P1, P2
*/
inline
double det(Point const& P1, Point const& P2)
{
return P1[X]*P2[Y] - P1[Y]*P2[X];
}
 
/*
* Solve the linear system [P1,P2] * P = Q
* in case there isn't exactly one solution the routine returns false
*/
inline
bool solve(Point & P, Point const& P1, Point const& P2, Point const& Q)
{
double d = det(P1, P2);
if (d == 0) return false;
d = 1 / d;
P[X] = det(Q, P2) * d;
P[Y] = det(P1, Q) * d;
return true;
}
 
////////////////////////////////////////////////////////////////////////////////
// interval routines
 
/*
* Map the sub-interval I in [0,1] into the interval J and assign it to J
*/
inline
void map_to(Interval & J, Interval const& I)
{
double length = J.extent();
J[1] = I.max() * length + J[0];
J[0] = I.min() * length + J[0];
}
 
/*
* The interval [1,0] is used to represent the empty interval, this routine
* is just an helper function for creating such an interval
*/
inline
Interval make_empty_interval()
{
Interval I(0);
I[0] = 1;
return I;
}
 
 
////////////////////////////////////////////////////////////////////////////////
// bezier curve routines
 
/*
* Return true if all the Bezier curve control points are near,
* false otherwise
*/
inline
bool is_constant(std::vector<Point> const& A, double precision = EPSILON)
{
for (unsigned int i = 1; i < A.size(); ++i)
{
if(!are_near(A[i], A[0], precision))
return false;
}
return true;
}
 
/*
* Compute the hodograph of the bezier curve B and return it in D
*/
inline
void derivative(std::vector<Point> & D, std::vector<Point> const& B)
{
D.clear();
size_t sz = B.size();
if (sz == 0) return;
if (sz == 1)
{
D.resize(1, Point(0,0));
return;
}
size_t n = sz-1;
D.reserve(n);
for (size_t i = 0; i < n; ++i)
{
D.push_back(n*(B[i+1] - B[i]));
}
}
 
/*
* Compute the hodograph of the Bezier curve B rotated of 90 degree
* and return it in D; we have N(t) orthogonal to B(t) for any t
*/
inline
void normal(std::vector<Point> & N, std::vector<Point> const& B)
{
derivative(N,B);
for (size_t i = 0; i < N.size(); ++i)
{
N[i] = rot90(N[i]);
}
}
 
/*
* Compute the portion of the Bezier curve "B" wrt the interval [0,t]
*/
inline
void left_portion(Coord t, std::vector<Point> & B)
{
size_t n = B.size();
for (size_t i = 1; i < n; ++i)
{
for (size_t j = n-1; j > i-1 ; --j)
{
B[j] = lerp(t, B[j-1], B[j]);
}
}
}
 
/*
* Compute the portion of the Bezier curve "B" wrt the interval [t,1]
*/
inline
void right_portion(Coord t, std::vector<Point> & B)
{
size_t n = B.size();
for (size_t i = 1; i < n; ++i)
{
for (size_t j = 0; j < n-i; ++j)
{
B[j] = lerp(t, B[j], B[j+1]);
}
}
}
 
/*
* Compute the portion of the Bezier curve "B" wrt the interval "I"
*/
inline
void portion (std::vector<Point> & B , Interval const& I)
{
if (I.min() == 0)
{
if (I.max() == 1) return;
left_portion(I.max(), B);
return;
}
right_portion(I.min(), B);
if (I.max() == 1) return;
double t = I.extent() / (1 - I.min());
left_portion(t, B);
}
 
 
////////////////////////////////////////////////////////////////////////////////
// tags
 
struct intersection_point_tag;
struct collinear_normal_tag;
template <typename Tag>
void clip(Interval & dom,
std::vector<Point> const& A,
std::vector<Point> const& B);
template <typename Tag>
void iterate(std::vector<Interval>& domsA,
std::vector<Interval>& domsB,
std::vector<Point> const& A,
std::vector<Point> const& B,
Interval const& domA,
Interval const& domB,
double precision );
 
 
////////////////////////////////////////////////////////////////////////////////
// intersection
 
/*
* Make up an orientation line using the control points c[i] and c[j]
* the line is returned in the output parameter "l" in the form of a 3 element
* vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
*/
inline
void orientation_line (std::vector<double> & l,
std::vector<Point> const& c,
size_t i, size_t j)
{
l[0] = c[j][Y] - c[i][Y];
l[1] = c[i][X] - c[j][X];
l[2] = cross(c[i], c[j]);
double length = std::sqrt(l[0] * l[0] + l[1] * l[1]);
assert (length != 0);
l[0] /= length;
l[1] /= length;
l[2] /= length;
}
 
/*
* Pick up an orientation line for the Bezier curve "c" and return it in
* the output parameter "l"
*/
inline
void pick_orientation_line (std::vector<double> & l,
std::vector<Point> const& c)
{
size_t i = c.size();
while (--i > 0 && are_near(c[0], c[i]))
{}
if (i == 0)
{
// this should never happen because when a new curve portion is created
// we check that it is not constant;
// however this requires that the precision used in the is_constant
// routine has to be the same used here in the are_near test
assert(i != 0);
}
orientation_line(l, c, 0, i);
//std::cerr << "i = " << i << std::endl;
}
 
/*
* Make up an orientation line for constant bezier curve;
* the orientation line is made up orthogonal to the other curve base line;
* the line is returned in the output parameter "l" in the form of a 3 element
* vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
*/
inline
void orthogonal_orientation_line (std::vector<double> & l,
std::vector<Point> const& c,
Point const& p)
{
if (is_constant(c))
{
// this should never happen
assert(!is_constant(c));
}
std::vector<Point> ol(2);
ol[0] = p;
ol[1] = (c.back() - c.front()).cw() + p;
orientation_line(l, ol, 0, 1);
}
 
/*
* Compute the signed distance of the point "P" from the normalized line l
*/
inline
double distance (Point const& P, std::vector<double> const& l)
{
return l[X] * P[X] + l[Y] * P[Y] + l[2];
}
 
/*
* Compute the min and max distance of the control points of the Bezier
* curve "c" from the normalized orientation line "l".
* This bounds are returned through the output Interval parameter"bound".
*/
inline
void fat_line_bounds (Interval& bound,
std::vector<Point> const& c,
std::vector<double> const& l)
{
bound[0] = 0;
bound[1] = 0;
double d;
for (size_t i = 0; i < c.size(); ++i)
{
d = distance(c[i], l);
if (bound[0] > d) bound[0] = d;
if (bound[1] < d) bound[1] = d;
}
}
 
/*
* return the x component of the intersection point between the line
* passing through points p1, p2 and the line Y = "y"
*/
inline
double intersect (Point const& p1, Point const& p2, double y)
{
// we are sure that p2[Y] != p1[Y] because this routine is called
// only when the lower or the upper bound is crossed
double dy = (p2[Y] - p1[Y]);
double s = (y - p1[Y]) / dy;
return (p2[X]-p1[X])*s + p1[X];
}
 
/*
* Clip the Bezier curve "B" wrt the fat line defined by the orientation
* line "l" and the interval range "bound", the new parameter interval for
* the clipped curve is returned through the output parameter "dom"
*/
void clip_interval (Interval& dom,
std::vector<Point> const& B,
std::vector<double> const& l,
Interval const& bound)
{
double n = B.size() - 1; // number of sub-intervals
std::vector<Point> D; // distance curve control points
D.reserve (B.size());
double d;
for (size_t i = 0; i < B.size(); ++i)
{
d = distance (B[i], l);
D.push_back (Point(i/n, d));
}
//print(D);
 
convex_hull(D);
std::vector<Point> & p = D;
//print(p);
 
bool plower, phigher;
bool clower, chigher;
double t, tmin = 1, tmax = 0;
// std::cerr << "bound : " << bound << std::endl;
 
plower = (p[0][Y] < bound.min());
phigher = (p[0][Y] > bound.max());
if (!(plower || phigher)) // inside the fat line
{
if (tmin > p[0][X]) tmin = p[0][X];
if (tmax < p[0][X]) tmax = p[0][X];
// std::cerr << "0 : inside " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
 
for (size_t i = 1; i < p.size(); ++i)
{
clower = (p[i][Y] < bound.min());
chigher = (p[i][Y] > bound.max());
if (!(clower || chigher)) // inside the fat line
{
if (tmin > p[i][X]) tmin = p[i][X];
if (tmax < p[i][X]) tmax = p[i][X];
// std::cerr << i << " : inside " << p[i]
// << " : tmin = " << tmin << ", tmax = " << tmax
// << std::endl;
}
if (clower != plower) // cross the lower bound
{
t = intersect(p[i-1], p[i], bound.min());
if (tmin > t) tmin = t;
if (tmax < t) tmax = t;
plower = clower;
// std::cerr << i << " : lower " << p[i]
// << " : tmin = " << tmin << ", tmax = " << tmax
// << std::endl;
}
if (chigher != phigher) // cross the upper bound
{
t = intersect(p[i-1], p[i], bound.max());
if (tmin > t) tmin = t;
if (tmax < t) tmax = t;
phigher = chigher;
// std::cerr << i << " : higher " << p[i]
// << " : tmin = " << tmin << ", tmax = " << tmax
// << std::endl;
}
}
 
// we have to test the closing segment for intersection
size_t last = p.size() - 1;
clower = (p[0][Y] < bound.min());
chigher = (p[0][Y] > bound.max());
if (clower != plower) // cross the lower bound
{
t = intersect(p[last], p[0], bound.min());
if (tmin > t) tmin = t;
if (tmax < t) tmax = t;
// std::cerr << "0 : lower " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
if (chigher != phigher) // cross the upper bound
{
t = intersect(p[last], p[0], bound.max());
if (tmin > t) tmin = t;
if (tmax < t) tmax = t;
// std::cerr << "0 : higher " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
 
dom[0] = tmin;
dom[1] = tmax;
}
 
/*
* Clip the Bezier curve "B" wrt the Bezier curve "A" for individuating
* intersection points the new parameter interval for the clipped curve
* is returned through the output parameter "dom"
*/
template <>
inline
void clip<intersection_point_tag> (Interval & dom,
std::vector<Point> const& A,
std::vector<Point> const& B)
{
std::vector<double> bl(3);
Interval bound;
if (is_constant(A))
{
Point M = middle_point(A.front(), A.back());
orthogonal_orientation_line(bl, B, M);
}
else
{
pick_orientation_line(bl, A);
}
fat_line_bounds(bound, A, bl);
clip_interval(dom, B, bl, bound);
}
 
 
///////////////////////////////////////////////////////////////////////////////
// collinear normal
 
/*
* Compute a closed focus for the Bezier curve B and return it in F
* A focus is any curve through which all lines perpendicular to B(t) pass.
*/
inline
void make_focus (std::vector<Point> & F, std::vector<Point> const& B)
{
assert (B.size() > 2);
size_t n = B.size() - 1;
normal(F, B);
Point c(1, 1);
#if VERBOSE
if (!solve(c, F[0], -F[n-1], B[n]-B[0]))
{
std::cerr << "make_focus: unable to make up a closed focus" << std::endl;
}
#else
solve(c, F[0], -F[n-1], B[n]-B[0]);
#endif
// std::cerr << "c = " << c << std::endl;
 
 
// B(t) + c(t) * N(t)
double n_inv = 1 / (double)(n);
Point c0ni;
F.push_back(c[1] * F[n-1]);
F[n] += B[n];
for (size_t i = n-1; i > 0; --i)
{
F[i] *= -c[0];
c0ni = F[i];
F[i] += (c[1] * F[i-1]);
F[i] *= (i * n_inv);
F[i] -= c0ni;
F[i] += B[i];
}
F[0] *= c[0];
F[0] += B[0];
}
 
/*
* Compute the projection on the plane (t, d) of the control points
* (t, u, D(t,u)) where D(t,u) = <(B(t) - F(u)), B'(t)> with 0 <= t, u <= 1
* B is a Bezier curve and F is a focus of another Bezier curve.
* See Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping.
*/
void distance_control_points (std::vector<Point> & D,
std::vector<Point> const& B,
std::vector<Point> const& F)
{
assert (B.size() > 1);
assert (F.size() > 0);
const size_t n = B.size() - 1;
const size_t m = F.size() - 1;
const size_t r = 2 * n - 1;
const double r_inv = 1 / (double)(r);
D.clear();
D.reserve (B.size() * F.size());
 
std::vector<Point> dB;
dB.reserve(n);
for (size_t k = 0; k < n; ++k)
{
dB.push_back (B[k+1] - B[k]);
}
NL::Matrix dBB(n,B.size());
for (size_t i = 0; i < n; ++i)
for (size_t j = 0; j < B.size(); ++j)
dBB(i,j) = dot (dB[i], B[j]);
NL::Matrix dBF(n, F.size());
for (size_t i = 0; i < n; ++i)
for (size_t j = 0; j < F.size(); ++j)
dBF(i,j) = dot (dB[i], F[j]);
 
size_t k0, kn, l;
double bc, bri;
Point dij;
std::vector<double> d(F.size());
for (size_t i = 0; i <= r; ++i)
{
for (size_t j = 0; j <= m; ++j)
{
d[j] = 0;
}
k0 = std::max(i, n) - n;
kn = std::min(i, n-1);
bri = n / binomial(r,i);
for (size_t k = k0; k <= kn; ++k)
{
//if (k > i || (i-k) > n) continue;
l = i - k;
#if CHECK
assert (l <= n);
#endif
bc = bri * binomial(n,l) * binomial(n-1, k);
for (size_t j = 0; j <= m; ++j)
{
//d[j] += bc * dot(dB[k], B[l] - F[j]);
d[j] += bc * (dBB(k,l) - dBF(k,j));
}
}
double dmin, dmax;
dmin = dmax = d[m];
for (size_t j = 0; j < m; ++j)
{
if (dmin > d[j]) dmin = d[j];
if (dmax < d[j]) dmax = d[j];
}
dij[0] = i * r_inv;
dij[1] = dmin;
D.push_back (dij);
dij[1] = dmax;
D.push_back (dij);
}
}
 
/*
* Clip the Bezier curve "B" wrt the focus "F"; the new parameter interval for
* the clipped curve is returned through the output parameter "dom"
*/
void clip_interval (Interval& dom,
std::vector<Point> const& B,
std::vector<Point> const& F)
{
std::vector<Point> D; // distance curve control points
distance_control_points(D, B, F);
//print(D, "D");
// ConvexHull chD(D);
// std::vector<Point>& p = chD.boundary; // convex hull vertices
 
convex_hull(D);
std::vector<Point> & p = D;
//print(p, "CH(D)");
 
bool plower, clower;
double t, tmin = 1, tmax = 0;
 
plower = (p[0][Y] < 0);
if (p[0][Y] == 0) // on the x axis
{
if (tmin > p[0][X]) tmin = p[0][X];
if (tmax < p[0][X]) tmax = p[0][X];
// std::cerr << "0 : on x axis " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
 
for (size_t i = 1; i < p.size(); ++i)
{
clower = (p[i][Y] < 0);
if (p[i][Y] == 0) // on x axis
{
if (tmin > p[i][X]) tmin = p[i][X];
if (tmax < p[i][X]) tmax = p[i][X];
// std::cerr << i << " : on x axis " << p[i]
// << " : tmin = " << tmin << ", tmax = " << tmax
// << std::endl;
}
else if (clower != plower) // cross the x axis
{
t = intersect(p[i-1], p[i], 0);
if (tmin > t) tmin = t;
if (tmax < t) tmax = t;
plower = clower;
// std::cerr << i << " : lower " << p[i]
// << " : tmin = " << tmin << ", tmax = " << tmax
// << std::endl;
}
}
 
// we have to test the closing segment for intersection
size_t last = p.size() - 1;
clower = (p[0][Y] < 0);
if (clower != plower) // cross the x axis
{
t = intersect(p[last], p[0], 0);
if (tmin > t) tmin = t;
if (tmax < t) tmax = t;
// std::cerr << "0 : lower " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
dom[0] = tmin;
dom[1] = tmax;
}
 
/*
* Clip the Bezier curve "B" wrt the Bezier curve "A" for individuating
* points which have collinear normals; the new parameter interval
* for the clipped curve is returned through the output parameter "dom"
*/
template <>
inline
void clip<collinear_normal_tag> (Interval & dom,
std::vector<Point> const& A,
std::vector<Point> const& B)
{
std::vector<Point> F;
make_focus(F, A);
clip_interval(dom, B, F);
}
 
 
 
const double MAX_PRECISION = 1e-8;
const double MIN_CLIPPED_SIZE_THRESHOLD = 0.8;
const Interval UNIT_INTERVAL(0,1);
const Interval EMPTY_INTERVAL = make_empty_interval();
const Interval H1_INTERVAL(0, 0.5);
const Interval H2_INTERVAL(0.5 + MAX_PRECISION, 1.0);
 
/*
* iterate
*
* input:
* A, B: control point sets of two bezier curves
* domA, domB: real parameter intervals of the two curves
* precision: required computational precision of the returned parameter ranges
* output:
* domsA, domsB: sets of parameter intervals
*
* The parameter intervals are computed by using a Bezier clipping algorithm,
* in case the clipping doesn't shrink the initial interval more than 20%,
* a subdivision step is performed.
* If during the computation both curves collapse to a single point
* the routine exits indipendently by the precision reached in the computation
* of the curve intervals.
*/
template <>
void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
std::vector<Interval>& domsB,
std::vector<Point> const& A,
std::vector<Point> const& B,
Interval const& domA,
Interval const& domB,
double precision )
{
// in order to limit recursion
static size_t counter = 0;
if (domA.extent() == 1 && domB.extent() == 1) counter = 0;
if (++counter > 100) return;
#if VERBOSE
std::cerr << std::fixed << std::setprecision(16);
std::cerr << ">> curve subdision performed <<" << std::endl;
std::cerr << "dom(A) : " << domA << std::endl;
std::cerr << "dom(B) : " << domB << std::endl;
// std::cerr << "angle(A) : " << angle(A) << std::endl;
// std::cerr << "angle(B) : " << angle(B) << std::endl;
#endif
 
if (precision < MAX_PRECISION)
precision = MAX_PRECISION;
 
std::vector<Point> pA = A;
std::vector<Point> pB = B;
std::vector<Point>* C1 = &pA;
std::vector<Point>* C2 = &pB;
 
Interval dompA = domA;
Interval dompB = domB;
Interval* dom1 = &dompA;
Interval* dom2 = &dompB;
 
Interval dom;
 
if ( is_constant(A) && is_constant(B) ){
Point M1 = middle_point(C1->front(), C1->back());
Point M2 = middle_point(C2->front(), C2->back());
if (are_near(M1,M2)){
domsA.push_back(domA);
domsB.push_back(domB);
}
return;
}
 
size_t iter = 0;
while (++iter < 100
&& (dompA.extent() >= precision || dompB.extent() >= precision))
{
#if VERBOSE
std::cerr << "iter: " << iter << std::endl;
#endif
clip<intersection_point_tag>(dom, *C1, *C2);
 
// [1,0] is utilized to represent an empty interval
if (dom == EMPTY_INTERVAL)
{
#if VERBOSE
std::cerr << "dom: empty" << std::endl;
#endif
return;
}
#if VERBOSE
std::cerr << "dom : " << dom << std::endl;
#endif
// all other cases where dom[0] > dom[1] are invalid
if (dom.min() > dom.max())
{
assert(dom.min() < dom.max());
}
 
map_to(*dom2, dom);
 
portion(*C2, dom);
if (is_constant(*C2) && is_constant(*C1))
{
Point M1 = middle_point(C1->front(), C1->back());
Point M2 = middle_point(C2->front(), C2->back());
#if VERBOSE
std::cerr << "both curves are constant: \n"
<< "M1: " << M1 << "\n"
<< "M2: " << M2 << std::endl;
print(*C2, "C2");
print(*C1, "C1");
#endif
if (are_near(M1,M2))
break; // append the new interval
else
return; // exit without appending any new interval
}
 
 
// if we have clipped less than 20% than we need to subdive the curve
// with the largest domain into two sub-curves
if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD)
{
#if VERBOSE
std::cerr << "clipped less than 20% : " << dom.extent() << std::endl;
std::cerr << "angle(pA) : " << angle(pA) << std::endl;
std::cerr << "angle(pB) : " << angle(pB) << std::endl;
#endif
std::vector<Point> pC1, pC2;
Interval dompC1, dompC2;
if (dompA.extent() > dompB.extent())
{
pC1 = pC2 = pA;
portion(pC1, H1_INTERVAL);
portion(pC2, H2_INTERVAL);
dompC1 = dompC2 = dompA;
map_to(dompC1, H1_INTERVAL);
map_to(dompC2, H2_INTERVAL);
iterate<intersection_point_tag>(domsA, domsB, pC1, pB,
dompC1, dompB, precision);
iterate<intersection_point_tag>(domsA, domsB, pC2, pB,
dompC2, dompB, precision);
}
else
{
pC1 = pC2 = pB;
portion(pC1, H1_INTERVAL);
portion(pC2, H2_INTERVAL);
dompC1 = dompC2 = dompB;
map_to(dompC1, H1_INTERVAL);
map_to(dompC2, H2_INTERVAL);
iterate<intersection_point_tag>(domsB, domsA, pC1, pA,
dompC1, dompA, precision);
iterate<intersection_point_tag>(domsB, domsA, pC2, pA,
dompC2, dompA, precision);
}
return;
}
 
std::swap(C1, C2);
std::swap(dom1, dom2);
#if VERBOSE
std::cerr << "dom(pA) : " << dompA << std::endl;
std::cerr << "dom(pB) : " << dompB << std::endl;
#endif
}
domsA.push_back(dompA);
domsB.push_back(dompB);
}
 
 
/*
* iterate
*
* input:
* A, B: control point sets of two bezier curves
* domA, domB: real parameter intervals of the two curves
* precision: required computational precision of the returned parameter ranges
* output:
* domsA, domsB: sets of parameter intervals
*
* The parameter intervals are computed by using a Bezier clipping algorithm,
* in case the clipping doesn't shrink the initial interval more than 20%,
* a subdivision step is performed.
* If during the computation one of the two curve interval length becomes less
* than MAX_PRECISION the routine exits indipendently by the precision reached
* in the computation of the other curve interval.
*/
template <>
void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
std::vector<Interval>& domsB,
std::vector<Point> const& A,
std::vector<Point> const& B,
Interval const& domA,
Interval const& domB,
double precision)
{
// in order to limit recursion
static size_t counter = 0;
if (domA.extent() == 1 && domB.extent() == 1) counter = 0;
if (++counter > 100) return;
#if VERBOSE
std::cerr << std::fixed << std::setprecision(16);
std::cerr << ">> curve subdision performed <<" << std::endl;
std::cerr << "dom(A) : " << domA << std::endl;
std::cerr << "dom(B) : " << domB << std::endl;
// std::cerr << "angle(A) : " << angle(A) << std::endl;
// std::cerr << "angle(B) : " << angle(B) << std::endl;
#endif
 
if (precision < MAX_PRECISION)
precision = MAX_PRECISION;
 
std::vector<Point> pA = A;
std::vector<Point> pB = B;
std::vector<Point>* C1 = &pA;
std::vector<Point>* C2 = &pB;
 
Interval dompA = domA;
Interval dompB = domB;
Interval* dom1 = &dompA;
Interval* dom2 = &dompB;
 
Interval dom;
 
size_t iter = 0;
while (++iter < 100
&& (dompA.extent() >= precision || dompB.extent() >= precision))
{
#if VERBOSE
std::cerr << "iter: " << iter << std::endl;
#endif
clip<collinear_normal_tag>(dom, *C1, *C2);
 
// [1,0] is utilized to represent an empty interval
if (dom == EMPTY_INTERVAL)
{
#if VERBOSE
std::cerr << "dom: empty" << std::endl;
#endif
return;
}
#if VERBOSE
std::cerr << "dom : " << dom << std::endl;
#endif
// all other cases where dom[0] > dom[1] are invalid
if (dom.min() > dom.max())
{
assert(dom.min() < dom.max());
}
 
map_to(*dom2, dom);
 
// it's better to stop before losing computational precision
if (iter > 1 && (dom2->extent() <= MAX_PRECISION))
{
#if VERBOSE
std::cerr << "beyond max precision limit" << std::endl;
#endif
break;
}
 
portion(*C2, dom);
if (iter > 1 && is_constant(*C2))
{
#if VERBOSE
std::cerr << "new curve portion pC1 is constant" << std::endl;
#endif
break;
}
 
 
// if we have clipped less than 20% than we need to subdive the curve
// with the largest domain into two sub-curves
if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD)
{
#if VERBOSE
std::cerr << "clipped less than 20% : " << dom.extent() << std::endl;
std::cerr << "angle(pA) : " << angle(pA) << std::endl;
std::cerr << "angle(pB) : " << angle(pB) << std::endl;
#endif
std::vector<Point> pC1, pC2;
Interval dompC1, dompC2;
if (dompA.extent() > dompB.extent())
{
if ((dompA.extent() / 2) < MAX_PRECISION)
{
break;
}
pC1 = pC2 = pA;
portion(pC1, H1_INTERVAL);
if (false && is_constant(pC1))
{
#if VERBOSE
std::cerr << "new curve portion pC1 is constant" << std::endl;
#endif
break;
}
portion(pC2, H2_INTERVAL);
if (is_constant(pC2))
{
#if VERBOSE
std::cerr << "new curve portion pC2 is constant" << std::endl;
#endif
break;
}
dompC1 = dompC2 = dompA;
map_to(dompC1, H1_INTERVAL);
map_to(dompC2, H2_INTERVAL);
iterate<collinear_normal_tag>(domsA, domsB, pC1, pB,
dompC1, dompB, precision);
iterate<collinear_normal_tag>(domsA, domsB, pC2, pB,
dompC2, dompB, precision);
}
else
{
if ((dompB.extent() / 2) < MAX_PRECISION)
{
break;
}
pC1 = pC2 = pB;
portion(pC1, H1_INTERVAL);
if (is_constant(pC1))
{
#if VERBOSE
std::cerr << "new curve portion pC1 is constant" << std::endl;
#endif
break;
}
portion(pC2, H2_INTERVAL);
if (is_constant(pC2))
{
#if VERBOSE
std::cerr << "new curve portion pC2 is constant" << std::endl;
#endif
break;
}
dompC1 = dompC2 = dompB;
map_to(dompC1, H1_INTERVAL);
map_to(dompC2, H2_INTERVAL);
iterate<collinear_normal_tag>(domsB, domsA, pC1, pA,
dompC1, dompA, precision);
iterate<collinear_normal_tag>(domsB, domsA, pC2, pA,
dompC2, dompA, precision);
}
return;
}
 
std::swap(C1, C2);
std::swap(dom1, dom2);
#if VERBOSE
std::cerr << "dom(pA) : " << dompA << std::endl;
std::cerr << "dom(pB) : " << dompB << std::endl;
#endif
}
domsA.push_back(dompA);
domsB.push_back(dompB);
}
 
 
/*
* get_solutions
*
* input: A, B - set of control points of two Bezier curve
* input: precision - required precision of computation
* input: clip - the routine used for clipping
* output: xs - set of pairs of parameter values
* at which the clipping algorithm converges
*
* This routine is based on the Bezier Clipping Algorithm,
* see: Sederberg - Computer Aided Geometric Design
*/
template <typename Tag>
void get_solutions (std::vector< std::pair<double, double> >& xs,
std::vector<Point> const& A,
std::vector<Point> const& B,
double precision)
{
std::pair<double, double> ci;
std::vector<Interval> domsA, domsB;
iterate<Tag> (domsA, domsB, A, B, UNIT_INTERVAL, UNIT_INTERVAL, precision);
if (domsA.size() != domsB.size())
{
assert (domsA.size() == domsB.size());
}
xs.clear();
xs.reserve(domsA.size());
for (size_t i = 0; i < domsA.size(); ++i)
{
#if VERBOSE
std::cerr << i << " : domA : " << domsA[i] << std::endl;
std::cerr << "extent A: " << domsA[i].extent() << " ";
std::cerr << "precision A: " << get_precision(domsA[i]) << std::endl;
std::cerr << i << " : domB : " << domsB[i] << std::endl;
std::cerr << "extent B: " << domsB[i].extent() << " ";
std::cerr << "precision B: " << get_precision(domsB[i]) << std::endl;
#endif
ci.first = domsA[i].middle();
ci.second = domsB[i].middle();
xs.push_back(ci);
}
}
 
} /* end namespace bezier_clipping */ } /* end namespace detail */
 
 
/*
* find_collinear_normal
*
* input: A, B - set of control points of two Bezier curve
* input: precision - required precision of computation
* output: xs - set of pairs of parameter values
* at which there are collinear normals
*
* This routine is based on the Bezier Clipping Algorithm,
* see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
*/
void find_collinear_normal (std::vector< std::pair<double, double> >& xs,
std::vector<Point> const& A,
std::vector<Point> const& B,
double precision)
{
using detail::bezier_clipping::get_solutions;
using detail::bezier_clipping::collinear_normal_tag;
get_solutions<collinear_normal_tag>(xs, A, B, precision);
}
 
 
/*
* find_intersections_bezier_clipping
*
* input: A, B - set of control points of two Bezier curve
* input: precision - required precision of computation
* output: xs - set of pairs of parameter values
* at which crossing happens
*
* This routine is based on the Bezier Clipping Algorithm,
* see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
*/
void find_intersections_bezier_clipping (std::vector< std::pair<double, double> >& xs,
std::vector<Point> const& A,
std::vector<Point> const& B,
double precision)
{
using detail::bezier_clipping::get_solutions;
using detail::bezier_clipping::intersection_point_tag;
get_solutions<intersection_point_tag>(xs, A, B, precision);
}
 
} // end namespace Geom
 
 
 
 
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/intersection-by-smashing.cpp
0,0 → 1,350
#include <d2.h>
#include <sbasis.h>
#include <sbasis-geometric.h>
#include <intersection-by-smashing.h>
 
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <algorithm>
 
 
namespace Geom {
using namespace Geom;
 
/*
* Computes the top and bottom boundaries of the L_\infty neighborhood
* of a curve. The curve is supposed to be a graph over the x-axis.
*/
static
void computeLinfinityNeighborhood( D2<SBasis > const &f, double tol, D2<Piecewise<SBasis> > &topside, D2<Piecewise<SBasis> > &botside ){
double signx = ( f[X].at0() > f[X].at1() )? -1 : 1;
double signy = ( f[Y].at0() > f[Y].at1() )? -1 : 1;
 
Piecewise<D2<SBasis> > top, bot;
top = Piecewise<D2<SBasis> > (f);
top.cuts.insert( top.cuts.end(), 2);
top.segs.insert( top.segs.end(), D2<SBasis>(Linear( f[X].at1(), f[X].at1()+2*tol*signx),
Linear( f[Y].at1() )) );
bot = Piecewise<D2<SBasis> >(f);
bot.cuts.insert( bot.cuts.begin(), - 1 );
bot.segs.insert( bot.segs.begin(), D2<SBasis>(Linear( f[X].at0()-2*tol*signx, f[X].at0()),
Linear( f[Y].at0() )) );
top += Point(-tol*signx, tol);
bot += Point( tol*signx, -tol);
 
if ( signy < 0 ){
std::swap( top, bot );
top += Point( 0, 2*tol);
bot += Point( 0, -2*tol);
}
topside = make_cuts_independent(top);
botside = make_cuts_independent(bot);
}
 
 
/*
* Compute top and bottom boundaries of the L^infty nbhd of the graph of a *monotonic* function f.
* if f is increasing, it is given by [f(t-tol)-tol, f(t+tol)+tol].
* if not, it is [f(t+tol)-tol, f(t-tol)+tol].
*/
static
void computeLinfinityNeighborhood( Piecewise<SBasis> const &f, double tol, Piecewise<SBasis> &top, Piecewise<SBasis> &bot){
top = f + tol;
top.offsetDomain( - tol );
top.cuts.insert( top.cuts.end(), f.domain().max() + tol);
top.segs.insert( top.segs.end(), SBasis(Linear( f.lastValue() + tol )) );
 
bot = f - tol;
bot.offsetDomain( tol );
bot.cuts.insert( bot.cuts.begin(), f.domain().min() - tol);
bot.segs.insert( bot.segs.begin(), SBasis(Linear( f.firstValue() - tol )) );
 
if ( f.firstValue() > f.lastValue() ){
std::swap( top, bot );
top += 2*tol;
bot -= 2*tol;
}
}
 
/*
* Returns the intervals over which the curve keeps its slope
* in one of the 8 sectors delimited by x=0, y=0, y=x, y=-x.
*/
static
std::vector<Interval> monotonicSplit(D2<SBasis> const &p){
std::vector<Interval> result;
 
D2<SBasis> v = derivative(p);
 
std::vector<double> someroots;
std::vector<double> cuts (2,0.);
cuts[1] = 1.;
 
someroots = roots(v[X]);
cuts.insert( cuts.end(), someroots.begin(), someroots.end() );
 
someroots = roots(v[Y]);
cuts.insert( cuts.end(), someroots.begin(), someroots.end() );
 
//we could split in the middle to avoid computing roots again...
someroots = roots(v[X]-v[Y]);
cuts.insert( cuts.end(), someroots.begin(), someroots.end() );
 
someroots = roots(v[X]+v[Y]);
cuts.insert( cuts.end(), someroots.begin(), someroots.end() );
 
sort(cuts.begin(),cuts.end());
unique(cuts.begin(), cuts.end() );
 
for (unsigned i=1; i<cuts.size(); i++){
result.push_back( Interval( cuts[i-1], cuts[i] ) );
}
return result;
}
 
//std::vector<Interval> level_set( D2<SBasis> const &f, Rect region){
// std::vector<Interval> x_in_reg = level_set( f[X], region[X] );
// std::vector<Interval> y_in_reg = level_set( f[Y], region[Y] );
// std::vector<Interval> result = intersect ( x_in_reg, y_in_reg );
// return result;
//}
 
/*TODO: remove this!!!
* the minimum would be to move it to piecewise.h but this would be stupid.
* The best would be to let 'compose' be aware of extension modes (constant, linear, polynomial..)
* (I think the extension modes (at start and end) should be properties of the pwsb).
*/
static
void prolongateByConstants( Piecewise<SBasis> &f, double paddle_width ){
if ( f.size() == 0 ) return; //do we have a covention about the domain of empty pwsb?
f.cuts.insert( f.cuts.begin(), f.cuts.front() - paddle_width );
f.segs.insert( f.segs.begin(), SBasis( f.segs.front().at0() ) );
f.cuts.insert( f.cuts.end(), f.cuts.back() + paddle_width );
f.segs.insert( f.segs.end(), SBasis( f.segs.back().at1() ) );
}
 
static
bool compareIntersectionsTimesX( Intersection const &inter1, Intersection const &inter2 ){
return inter1.times[X].min() < inter2.times[Y].min();
}
/*Fuse contiguous intersection domains
*
*/
static
void cleanup_and_fuse( std::vector<Intersection> &inters ){
std::sort( inters.begin(), inters.end(), compareIntersectionsTimesX);
for (unsigned i=0; i < inters.size(); i++ ){
for (unsigned j=i+1; j < inters.size() && inters[i].times[X].intersects( inters[j].times[X]) ; j++ ){
if (inters[i].times[Y].intersects( inters[j].times[Y] ) ){
inters[i].times.unionWith(inters[j].times);
inters[i].bbox.unionWith(inters[j].bbox);
inters.erase( inters.begin() + j );
}
}
}
}
 
/* Computes the intersection of two sets given as (ordered) union intervals.
*/
static
std::vector<Interval> intersect( std::vector<Interval> const &a, std::vector<Interval> const &b){
std::vector<Interval> result;
//TODO: use order to optimize this!
for (unsigned i=0; i < a.size(); i++){
for (unsigned j=0; j < b.size(); j++){
OptInterval c( a[i] );
c &= b[j];
if ( c ) {
result.push_back( *c );
}
}
}
return result;
}
 
/* Returns the intervals over which the curves are in the
* tol-neighborhood one of the other for the L_\infty metric.
* WARNING: each curve is supposed to be a graph over x or y axis
* (but not necessarily the same axis for both) and the smaller
* the slope the better (typically <=45°).
*/
std::vector<Intersection> monotonic_smash_intersect( D2<SBasis> const &a, D2<SBasis> const &b, double tol){
 
// a and b or X and Y may have to be exchanged, so make local copies.
D2<SBasis> aa = a;
D2<SBasis> bb = b;
bool swapresult = false;
bool swapcoord = false;//debug only!
 
//if the (enlarged) bounding boxes don't intersect, stop.
OptRect abounds = bounds_fast( a );
OptRect bbounds = bounds_fast( b );
if ( !abounds || !bbounds ) return std::vector<Intersection>();
abounds->expandBy(tol);
if ( !(abounds->intersects(*bbounds))){
return std::vector<Intersection>();
}
 
//Choose the best curve to be re-parametrized by x or y values.
OptRect dabounds = bounds_exact(derivative(a));
OptRect dbbounds = bounds_exact(derivative(b));
if ( dbbounds->min().length() > dabounds->min().length() ){
aa=b;
bb=a;
std::swap( dabounds, dbbounds );
swapresult = true;
}
 
//Choose the best coordinate to use as new parameter
double dxmin = std::min( std::abs((*dabounds)[X].max()), std::abs((*dabounds)[X].min()) );
double dymin = std::min( std::abs((*dabounds)[Y].max()), std::abs((*dabounds)[Y].min()) );
if ( (*dabounds)[X].max()*(*dabounds)[X].min() < 0 ) dxmin=0;
if ( (*dabounds)[Y].max()*(*dabounds)[Y].min() < 0 ) dymin=0;
assert (dxmin>=0 && dymin>=0);
 
if (dxmin < dymin) {
aa = D2<SBasis>( aa[Y], aa[X] );
bb = D2<SBasis>( bb[Y], bb[X] );
swapcoord = true;
}
 
//re-parametrize aa by the value of x.
Interval x_range_strict( aa[X].at0(), aa[X].at1() );
Piecewise<SBasis> y_of_x = pw_compose_inverse(aa[Y],aa[X], 2, 1e-5);
 
//Compute top and bottom boundaries of the L^infty nbhd of aa.
Piecewise<SBasis> top_ay, bot_ay;
computeLinfinityNeighborhood( y_of_x, tol, top_ay, bot_ay);
 
Interval ax_range = top_ay.domain();//i.e. aa[X] domain ewpanded by tol.
std::vector<Interval> bx_in_ax_range = level_set(bb[X], ax_range );
 
// find times when bb is in the neighborhood of aa.
std::vector<Interval> tbs;
for (unsigned i=0; i<bx_in_ax_range.size(); i++){
D2<Piecewise<SBasis> > bb_in;
bb_in[X] = Piecewise<SBasis> ( portion( bb[X], bx_in_ax_range[i] ) );
bb_in[Y] = Piecewise<SBasis> ( portion( bb[Y], bx_in_ax_range[i]) );
bb_in[X].setDomain( bx_in_ax_range[i] );
bb_in[Y].setDomain( bx_in_ax_range[i] );
 
Piecewise<SBasis> h;
Interval level;
h = bb_in[Y] - compose( top_ay, bb_in[X] );
level = Interval( -infinity(), 0 );
std::vector<Interval> rts_lo = level_set( h, level);
h = bb_in[Y] - compose( bot_ay, bb_in[X] );
level = Interval( 0, infinity());
std::vector<Interval> rts_hi = level_set( h, level);
 
std::vector<Interval> rts = intersect( rts_lo, rts_hi );
tbs.insert(tbs.end(), rts.begin(), rts.end() );
}
 
std::vector<Intersection > result(tbs.size(), Intersection() );
 
/* for each solution I, find times when aa is in the neighborhood of bb(I).
* (Note: the preimage of bb[X](I) by aa[X], enlarged by tol, is a good approximation of this:
* it would give points in the 2*tol neighborhood of bb (if the slope of aa is never more than 1).
* + faster computation.
* - implies little jumps depending on the subdivision of the input curve into monotonic pieces
* and on the choice of prefered axis. If noticable, these jumps would feel random to the user :-(
*/
for (unsigned j=0; j<tbs.size(); j++){
result[j].times[Y] = tbs[j];
std::vector<Interval> tas;
//TODO: replace this by some option in the "compose(pw,pw)" method!
Piecewise<SBasis> fat_y_of_x = y_of_x;
prolongateByConstants( fat_y_of_x, 100*(1+tol) );
 
D2<Piecewise<SBasis> > top_b, bot_b;
D2<SBasis> bbj = portion( bb, tbs[j] );
computeLinfinityNeighborhood( bbj, tol, top_b, bot_b );
 
Piecewise<SBasis> h;
Interval level;
h = top_b[Y] - compose( fat_y_of_x, top_b[X] );
level = Interval( +infinity(), 0 );
std::vector<Interval> rts_top = level_set( h, level);
for (unsigned idx=0; idx < rts_top.size(); idx++){
rts_top[idx] = Interval( top_b[X].valueAt( rts_top[idx].min() ),
top_b[X].valueAt( rts_top[idx].max() ) );
}
assert( rts_top.size() == 1 );
 
h = bot_b[Y] - compose( fat_y_of_x, bot_b[X] );
level = Interval( 0, -infinity());
std::vector<Interval> rts_bot = level_set( h, level);
for (unsigned idx=0; idx < rts_bot.size(); idx++){
rts_bot[idx] = Interval( bot_b[X].valueAt( rts_bot[idx].min() ),
bot_b[X].valueAt( rts_bot[idx].max() ) );
}
assert( rts_bot.size() == 1 );
rts_top = intersect( rts_top, rts_bot );
assert (rts_top.size() == 1);
Interval x_dom = rts_top[0];
 
if ( x_dom.max() <= x_range_strict.min() ){
tas.push_back( Interval ( ( aa[X].at0() < aa[X].at1() ) ? 0 : 1 ) );
}else if ( x_dom.min() >= x_range_strict.max() ){
tas.push_back( Interval ( ( aa[X].at0() < aa[X].at1() ) ? 1 : 0 ) );
}else{
tas = level_set(aa[X], x_dom );
}
assert( tas.size()==1 );
result[j].times[X] = tas.front();
 
result[j].bbox = Rect( bbj.at0(), bbj.at1() );
Interval y_dom( aa[Y](result[j].times[X].min()), aa[Y](result[j].times[X].max()) );
result[j].bbox.unionWith( Rect( x_dom, y_dom ) );
}
 
if (swapresult) {
for ( unsigned i=0; i<result.size(); i++){
std::swap( result[i].times[X], result[i].times[Y]);
}
}
if (swapcoord) {
for ( unsigned i=0; i<result.size(); i++){
std::swap( result[i].bbox[X], result[i].bbox[Y] );
}
}
 
//TODO: cleanup result? fuse contiguous intersections?
return result;
}
 
std::vector<Intersection> smash_intersect( D2<SBasis> const &a, D2<SBasis> const &b, double tol){
std::vector<Intersection> result;
 
std::vector<Interval> acuts = monotonicSplit(a);
std::vector<Interval> bcuts = monotonicSplit(b);
for (unsigned i=0; i<acuts.size(); i++){
D2<SBasis> ai = portion( a, acuts[i]);
for (unsigned j=0; j<bcuts.size(); j++){
D2<SBasis> bj = portion( b, bcuts[j]);
std::vector<Intersection> ai_cap_bj = monotonic_smash_intersect( ai, bj, tol );
for (unsigned k=0; k < ai_cap_bj.size(); k++){
ai_cap_bj[k].times[X] = ai_cap_bj[k].times[X] * acuts[i].extent() + acuts[i].min();
ai_cap_bj[k].times[Y] = ai_cap_bj[k].times[Y] * bcuts[j].extent() + bcuts[j].min();
}
result.insert( result.end(), ai_cap_bj.begin(), ai_cap_bj.end() );
}
}
cleanup_and_fuse( result );
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 :
/trunk/Scribus/scribus/third_party/lib2geom/sbasis-2d.cpp
1,9 → 1,10
#include "sbasis-2d.h"
#include <sbasis-2d.h>
#include <sbasis-geometric.h>
 
namespace Geom{
 
SBasis extract_u(SBasis2d const &a, double u) {
SBasis sb;
SBasis sb(a.vs, Linear());
double s = u*(1-u);
for(unsigned vi = 0; vi < a.vs; vi++) {
13,7 → 14,7
bo += (extract_u(a.index(ui, vi), u))*sk;
sk *= s;
}
sb.push_back(bo);
sb[vi] = bo;
}
return sb;
20,7 → 21,7
}
 
SBasis extract_v(SBasis2d const &a, double v) {
SBasis sb;
SBasis sb(a.us, Linear());
double s = v*(1-v);
for(unsigned ui = 0; ui < a.us; ui++) {
30,7 → 31,7
bo += (extract_v(a.index(ui, vi), v))*sk;
sk *= s;
}
sb.push_back(bo);
sb[ui] = bo;
}
return sb;
69,4 → 70,133
return D2<SBasis>(compose(fg[X], p), compose(fg[Y], p));
}
 
SBasis2d partial_derivative(SBasis2d const &f, int dim) {
SBasis2d result;
for(unsigned i = 0; i < f.size(); i++) {
result.push_back(Linear2d(0,0,0,0));
}
result.us = f.us;
result.vs = f.vs;
 
for(unsigned i = 0; i < f.us; i++) {
for(unsigned j = 0; j < f.vs; j++) {
Linear2d lin = f.index(i,j);
Linear2d dlin(lin[1+dim]-lin[0], lin[1+2*dim]-lin[dim], lin[3-dim]-lin[2*(1-dim)], lin[3]-lin[2-dim]);
result[i+j*result.us] += dlin;
unsigned di = dim?j:i;
if (di>=1){
float motpi = dim?-1:1;
Linear2d ds_lin_low( lin[0], -motpi*lin[1], motpi*lin[2], -lin[3] );
result[(i+dim-1)+(j-dim)*result.us] += di*ds_lin_low;
Linear2d ds_lin_hi( lin[1+dim]-lin[0], lin[1+2*dim]-lin[dim], lin[3]-lin[2-dim], lin[3-dim]-lin[2-dim] );
result[i+j*result.us] += di*ds_lin_hi;
}
}
}
return result;
}
 
/**
* Finds a path which traces the 0 contour of f, traversing from A to B as a single d2<sbasis>.
* degmax specifies the degree (degree = 2*degmax-1, so a degmax of 2 generates a cubic fit).
* The algorithm is based on dividing out derivatives at each end point and does not use the curvature for fitting.
* It is less accurate than sb2d_cubic_solve, although this may be fixed in the future.
*/
D2<SBasis>
sb2dsolve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B, unsigned degmax){
//g_warning("check f(A)= %f = f(B) = %f =0!", f.apply(A[X],A[Y]), f.apply(B[X],B[Y]));
 
SBasis2d dfdu = partial_derivative(f, 0);
SBasis2d dfdv = partial_derivative(f, 1);
Geom::Point dfA(dfdu.apply(A[X],A[Y]),dfdv.apply(A[X],A[Y]));
Geom::Point dfB(dfdu.apply(B[X],B[Y]),dfdv.apply(B[X],B[Y]));
Geom::Point nA = dfA/(dfA[X]*dfA[X]+dfA[Y]*dfA[Y]);
Geom::Point nB = dfB/(dfB[X]*dfB[X]+dfB[Y]*dfB[Y]);
 
D2<SBasis>result(SBasis(degmax, Linear()), SBasis(degmax, Linear()));
double fact_k=1;
double sign = 1.;
for(int dim = 0; dim < 2; dim++)
result[dim][0] = Linear(A[dim],B[dim]);
for(unsigned k=1; k<degmax; k++){
// these two lines make the solutions worse!
//fact_k *= k;
//sign = -sign;
SBasis f_on_curve = compose(f,result);
Linear reste = f_on_curve[k];
double ax = -reste[0]/fact_k*nA[X];
double ay = -reste[0]/fact_k*nA[Y];
double bx = -sign*reste[1]/fact_k*nB[X];
double by = -sign*reste[1]/fact_k*nB[Y];
 
result[X][k] = Linear(ax,bx);
result[Y][k] = Linear(ay,by);
//sign *= 3;
}
return result;
}
 
/**
* Finds a path which traces the 0 contour of f, traversing from A to B as a single cubic d2<sbasis>.
* The algorithm is based on matching direction and curvature at each end point.
*/
//TODO: handle the case when B is "behind" A for the natural orientation of the level set.
//TODO: more generally, there might be up to 4 solutions. Choose the best one!
D2<SBasis>
sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B){
D2<SBasis>result;//(Linear(A[X],B[X]),Linear(A[Y],B[Y]));
//g_warning("check 0 = %f = %f!", f.apply(A[X],A[Y]), f.apply(B[X],B[Y]));
 
SBasis2d f_u = partial_derivative(f , 0);
SBasis2d f_v = partial_derivative(f , 1);
SBasis2d f_uu = partial_derivative(f_u, 0);
SBasis2d f_uv = partial_derivative(f_v, 0);
SBasis2d f_vv = partial_derivative(f_v, 1);
 
Geom::Point dfA(f_u.apply(A[X],A[Y]),f_v.apply(A[X],A[Y]));
Geom::Point dfB(f_u.apply(B[X],B[Y]),f_v.apply(B[X],B[Y]));
 
Geom::Point V0 = rot90(dfA);
Geom::Point V1 = rot90(dfB);
double D2fVV0 = f_uu.apply(A[X],A[Y])*V0[X]*V0[X]+
2*f_uv.apply(A[X],A[Y])*V0[X]*V0[Y]+
f_vv.apply(A[X],A[Y])*V0[Y]*V0[Y];
double D2fVV1 = f_uu.apply(B[X],B[Y])*V1[X]*V1[X]+
2*f_uv.apply(B[X],B[Y])*V1[X]*V1[Y]+
f_vv.apply(B[X],B[Y])*V1[Y]*V1[Y];
 
std::vector<D2<SBasis> > candidates = cubics_fitting_curvature(A,B,V0,V1,D2fVV0,D2fVV1);
if (candidates.size()==0) {
return D2<SBasis>(Linear(A[X],B[X]),Linear(A[Y],B[Y]));
}
//TODO: I'm sure std algorithm could do that for me...
double error = -1;
unsigned best = 0;
for (unsigned i=0; i<candidates.size(); i++){
Interval bounds = *bounds_fast(compose(f,candidates[i]));
double new_error = (fabs(bounds.max())>fabs(bounds.min()) ? fabs(bounds.max()) : fabs(bounds.min()) );
if ( new_error < error || error < 0 ){
error = new_error;
best = i;
}
}
return candidates[best];
}
 
 
 
 
};
 
/*
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 :
/trunk/Scribus/scribus/third_party/lib2geom/affine.cpp
0,0 → 1,489
#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 "affine.h"
#include "point.h"
 
namespace Geom {
 
/** Creates a Affine 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 Affine.
*/
//NOTE: Inkscape's version is broken, so when including this version, you'll have to search for code with this func
Affine from_basis(Point const x_basis, Point const y_basis, Point const offset) {
return Affine(x_basis[X], x_basis[Y],
y_basis[X], y_basis[Y],
offset [X], offset [Y]);
}
 
Point Affine::xAxis() const {
return Point(_c[0], _c[1]);
}
 
Point Affine::yAxis() const {
return Point(_c[2], _c[3]);
}
 
/** Gets the translation imparted by the Affine.
*/
Point Affine::translation() const {
return Point(_c[4], _c[5]);
}
 
void Affine::setXAxis(Point const &vec) {
for(int i = 0; i < 2; i++)
_c[i] = vec[i];
}
 
void Affine::setYAxis(Point const &vec) {
for(int i = 0; i < 2; i++)
_c[i + 2] = vec[i];
}
 
/** Sets the translation imparted by the Affine.
*/
void Affine::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 Affine. 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 Affine::expansionX() const {
return sqrt(_c[0] * _c[0] + _c[1] * _c[1]);
}
 
/** Calculates the amount of y-scaling imparted by the Affine. 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 Affine::expansionY() const {
return sqrt(_c[2] * _c[2] + _c[3] * _c[3]);
}
 
void Affine::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 Affine::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 Affine. */
void Affine::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;
}
 
/** @brief Check whether this matrix is an identity matrix.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1 \end{array}\right]\f$ */
bool Affine::isIdentity(Coord eps) const {
return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
 
/** @brief Check whether this matrix represents a pure translation.
* Will return true for the identity matrix, which represents a zero translation.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
1 & 0 & 0 \\
0 & 1 & 0 \\
a & b & 1 \end{array}\right]\f$ */
bool Affine::isTranslation(Coord eps) const {
return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps);
}
/** @brief Check whether this matrix represents a pure nonzero translation.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
1 & 0 & 0 \\
0 & 1 & 0 \\
a & b & 1 \end{array}\right]\f$ and \f$a, b \neq 0\f$ */
bool Affine::isNonzeroTranslation(Coord eps) const {
return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
(!are_near(_c[4], 0.0, eps) || !are_near(_c[5], 0.0, eps));
}
 
/** @brief Check whether this matrix represents pure scaling.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
a & 0 & 0 \\
0 & b & 0 \\
0 & 0 & 1 \end{array}\right]\f$. */
bool Affine::isScale(Coord eps) const {
return are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
 
/** @brief Check whether this matrix represents pure, nonzero scaling.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
a & 0 & 0 \\
0 & b & 0 \\
0 & 0 & 1 \end{array}\right]\f$ and \f$a, b \neq 1\f$. */
bool Affine::isNonzeroScale(Coord eps) const {
return (!are_near(_c[0], 1.0, eps) || !are_near(_c[3], 1.0, eps)) && //NOTE: these are the diags, and the next line opposite diags
are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
 
/** @brief Check whether this matrix represents pure uniform scaling.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
a & 0 & 0 \\
0 & a & 0 \\
0 & 0 & 1 \end{array}\right]\f$. */
bool Affine::isUniformScale(Coord eps) const {
return are_near(_c[0], _c[3], eps) &&
are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
 
/** @brief Check whether this matrix represents pure, nonzero uniform scaling.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
a & 0 & 0 \\
0 & a & 0 \\
0 & 0 & 1 \end{array}\right]\f$ and \f$a \neq 1\f$. */
bool Affine::isNonzeroUniformScale(Coord eps) const {
return !are_near(_c[0], 1.0, eps) && are_near(_c[0], _c[3], eps) &&
are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
 
/** @brief Check whether this matrix represents a pure rotation.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
a & b & 0 \\
-b & a & 0 \\
0 & 0 & 1 \end{array}\right]\f$ and \f$a^2 + b^2 = 1\f$. */
bool Affine::isRotation(Coord eps) const {
return are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps) &&
are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps);
}
 
/** @brief Check whether this matrix represents a pure, nonzero rotation.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
a & b & 0 \\
-b & a & 0 \\
0 & 0 & 1 \end{array}\right]\f$, \f$a^2 + b^2 = 1\f$ and \f$a \neq 1\f$. */
bool Affine::isNonzeroRotation(Coord eps) const {
return !are_near(_c[0], 1.0, eps) &&
are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps) &&
are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps);
}
 
/** @brief Check whether this matrix represents pure horizontal shearing.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
1 & 0 & 0 \\
k & 1 & 0 \\
0 & 0 & 1 \end{array}\right]\f$. */
bool Affine::isHShear(Coord eps) const {
return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
are_near(_c[3], 1.0, eps) && are_near(_c[4], 0.0, eps) &&
are_near(_c[5], 0.0, eps);
}
/** @brief Check whether this matrix represents pure, nonzero horizontal shearing.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
1 & 0 & 0 \\
k & 1 & 0 \\
0 & 0 & 1 \end{array}\right]\f$ and \f$k \neq 0\f$. */
bool Affine::isNonzeroHShear(Coord eps) const {
return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
!are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
 
/** @brief Check whether this matrix represents pure vertical shearing.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
1 & k & 0 \\
0 & 1 & 0 \\
0 & 0 & 1 \end{array}\right]\f$. */
bool Affine::isVShear(Coord eps) const {
return are_near(_c[0], 1.0, eps) && are_near(_c[2], 0.0, eps) &&
are_near(_c[3], 1.0, eps) && are_near(_c[4], 0.0, eps) &&
are_near(_c[5], 0.0, eps);
}
 
/** @brief Check whether this matrix represents pure, nonzero vertical shearing.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
1 & k & 0 \\
0 & 1 & 0 \\
0 & 0 & 1 \end{array}\right]\f$ and \f$k \neq 0\f$. */
bool Affine::isNonzeroVShear(Coord eps) const {
return are_near(_c[0], 1.0, eps) && !are_near(_c[1], 0.0, eps) &&
are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
 
/** @brief Check whether this matrix represents zooming.
* Zooming is any combination of translation and uniform scaling. It preserves angles, ratios
* of distances between arbitrary points and unit vectors of line segments.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
a & 0 & 0 \\
0 & a & 0 \\
b & c & 1 \end{array}\right]\f$. */
bool Affine::isZoom(Coord eps) const {
return are_near(_c[0], _c[3], eps) && are_near(_c[1], 0, eps) && are_near(_c[2], 0, eps);
}
 
/** @brief Check whether the transformation preserves areas of polygons.
* This means that the transformation can be any combination of translation, rotation,
* shearing and squeezing (non-uniform scaling such that the absolute value of the product
* of Y-scale and X-scale is 1).
* @param eps Numerical tolerance
* @return True iff \f$|\det A| = 1\f$. */
bool Affine::preservesArea(Coord eps) const
{
return are_near(descrim2(), 1.0, eps);
}
 
/** @brief Check whether the transformation preserves angles between lines.
* This means that the transformation can be any combination of translation, uniform scaling
* and rotation.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
a & b & 0 \\
-b & a & 0 \\
c & d & 1 \end{array}\right]\f$. */
bool Affine::preservesAngles(Coord eps) const
{
return are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps);
}
 
/** @brief Check whether the transformation preserves distances between points.
* This means that the transformation can be any combination of translation and rotation.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
a & b & 0 \\
-b & a & 0 \\
c & d & 1 \end{array}\right]\f$ and \f$a^2 + b^2 = 1\f$. */
bool Affine::preservesDistances(Coord eps) const
{
return are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) &&
are_near(_c[0] * _c[0] + _c[1] * _c[1], 1.0, eps);
}
 
/** @brief Check whether this transformation flips objects.
* A transformation flips objects if it has a negative scaling component. */
bool Affine::flips() const {
// TODO shouldn't this be det() < 0?
return cross(xAxis(), yAxis()) > 0;
}
 
/** @brief Check whether this matrix is singular.
* Singular matrices have no inverse, which means that applying them to a set of points
* results in a loss of information.
* @param eps Numerical tolerance
* @return True iff the determinant is near zero. */
bool Affine::isSingular(Coord eps) const {
return are_near(det(), 0.0, eps);
}
 
/** @brief Compute the inverse matrix.
* Inverse is a matrix (denoted \f$A^{-1}) such that \f$AA^{-1} = A^{-1}A = I\f$.
* Singular matrices have no inverse (for example a matrix that has two of its columns equal).
* For such matrices, the identity matrix will be returned instead.
* @param eps Numerical tolerance
* @return Inverse of the matrix, or the identity matrix if the inverse is undefined.
* @post (m * m.inverse()).isIdentity() == true */
Affine Affine::inverse() const {
Affine d;
double mx = std::max(fabs(_c[0]) + fabs(_c[1]),
fabs(_c[2]) + fabs(_c[3])); // a random matrix norm (either l1 or linfty
if(mx > 0) {
Geom::Coord const determ = det();
if (!rel_error_bound(determ, mx*mx)) {
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();
}
} else {
d.setIdentity();
}
 
return d;
}
 
/** @brief Calculate the determinant.
* @return \f$\det A\f$. */
Coord Affine::det() const {
// TODO this can overflow
return _c[0] * _c[3] - _c[1] * _c[2];
}
 
/** @brief Calculate the square of the descriminant.
* This is simply the absolute value of the determinant.
* @return \f$|\det A|\f$. */
Coord Affine::descrim2() const {
return fabs(det());
}
 
/** @brief Calculate the descriminant.
* If the matrix doesn't contain a non-uniform scaling or shearing component, this value says
* how will the length any line segment change after applying this transformation
* to arbitrary objects on a plane (the new length will be
* @code line_seg.length() * m.descrim()) @endcode.
* @return \f$\sqrt{|\det A|}\f$. */
Coord Affine::descrim() const {
return sqrt(descrim2());
}
 
/** @brief Combine this transformation with another one.
* After this operation, the matrix will correspond to the transformation
* obtained by first applying the original version of this matrix, and then
* applying @a m. */
Affine &Affine::operator*=(Affine const &o) {
Coord nc[6];
for(int a = 0; a < 5; a += 2) {
for(int b = 0; b < 2; b++) {
nc[a + b] = _c[a] * o._c[b] + _c[a + 1] * o._c[b + 2];
}
}
for(int a = 0; a < 6; ++a) {
_c[a] = nc[a];
}
_c[4] += o._c[4];
_c[5] += o._c[5];
return *this;
}
 
//TODO: What's this!?!
Affine elliptic_quadratic_form(Affine const &m) {
double od = m[0] * m[1] + m[2] * m[3];
Affine ret (m[0]*m[0] + m[1]*m[1], od,
od, m[2]*m[2] + m[3]*m[3],
0, 0);
return ret; // allow NRVO
}
 
Eigen::Eigen(Affine 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])));
}
}
 
static void quadratic_roots(double q0, double q1, double q2, int &n, double&r0, double&r1) {
std::vector<double> r;
if(q2 == 0) {
if(q1 == 0) { // zero or infinite roots
n = 0;
} else {
n = 1;
r0 = -q0/q1;
}
} else {
double desc = q1*q1 - 4*q2*q0;
if (desc < 0)
n = 0;
else if (desc == 0) {
n = 1;
r0 = -q1/(2*q2);
} else {
n = 2;
desc = std::sqrt(desc);
double t = -0.5*(q1+sgn(q1)*desc);
r0 = t/q2;
r1 = q0/t;
}
}
}
 
Eigen::Eigen(double m[2][2]) {
double const B = -m[0][0] - m[1][1];
double const C = m[0][0]*m[1][1] - m[1][0]*m[0][1];
//double const desc = B*B-4*C;
//double t = -0.5*(B+sgn(B)*desc);
int n;
values[0] = values[1] = 0;
quadratic_roots(C, B, 1, n, values[0], values[1]);
for (int i = 0; i < n; i++)
vectors[i] = unit_vector(rot90(Point(m[0][0]-values[i], m[0][1])));
for (int i = n; i < 2; i++)
vectors[i] = Point(0,0);
}
 
} //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 :
/trunk/Scribus/scribus/third_party/lib2geom/point.cpp
1,27 → 1,46
#include "point.h"
#include <assert.h>
#include "coord.h"
#include "isnan.h" //temporary fix for isnan()
#include "matrix.h"
#include <limits>
#include <math.h>
#include <point.h>
#include <transforms.h>
 
 
namespace Geom {
 
/** Scales this vector to make it a unit vector (within rounding error).
/**
* @class Point
* @brief Two-dimensional point that doubles as a vector.
*
* The current version tries to handle infinite coordinates gracefully,
* but it's not clear that any callers need that.
* Points in 2Geom are represented in Cartesian coordinates, e.g. as a pair of numbers
* that store the X and Y coordinates. Each point is also a vector in \f$\mathbb{R}^2\f$
* from the origin (point at 0,0) to the stored coordinates,
* and has methods implementing several vector operations (like length()).
*
* \pre \f$this \neq (0, 0)\f$
* \pre Neither component is NaN.
* \post \f$-\epsilon<\left|this\right|-1<\epsilon\f$
*/
* \par Operator note
* \par
* Most operators are provided by Boost operator helpers, so they are not visible in this class.
* If @a p, @a q, @a r denote points, @a s a floating-point scalar, and @a m a transformation matrix,
* then the following operations are available:
* @code
p += q; p -= q; r = p + q; r = p - q;
p *= s; p /= s; q = p * s; q = s * p; q = p / s;
p *= m; p /= m; q = p * m; q = m * p; q = p / m;
@endcode
* It is possible to left-multiply a point by a matrix, even though mathematically speaking
* this is undefined. The result is a point identical to that obtained by right-multiplying.
* Division of points by matrices is defined as multiplication by their inverses.
*
* @ingroup Primitives */
 
/** @brief Normalize the vector representing the point.
* After this method returns, the length of the vector will be 1 (unless both coordinates are
* zero - the zero point will be returned then). The function tries to handle infinite
* coordinates gracefully. If any of the coordinates are NaN, the function will do nothing.
* @post \f$-\epsilon < \left|this\right| - 1 < \epsilon\f$
* @see unit_vector(Geom::Point const &) */
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(IS_NAN(len)) return;
static double const inf = HUGE_VAL;
if(len != inf) {
*this /= len;
} else {
40,27 → 59,30
}
}
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 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;
}
}
case 1: {
*this = tmp;
break;
}
case 2: {
*this = tmp * sqrt(0.5);
break;
}
}
}
}
 
/** Compute the L1 norm, or manhattan distance, of \a p. */
/** @brief Compute the first norm (Manhattan distance) of @a p.
* This is equal to the sum of absolutes values of the coordinates.
* @return \f$|p_X| + |p_Y|\f$
* @relates Point */
Coord L1(Point const &p) {
Coord d = 0;
for ( int i = 0 ; i < 2 ; i++ ) {
69,57 → 91,53
return d;
}
 
/** Compute the L infinity, or maximum, norm of \a p. */
/** @brief Compute the infinity norm (maximum norm) of @a p.
* @return \f$\max(p_X, p_Y)\f$
* @relates Point */
Coord LInfty(Point const &p) {
Coord const a(fabs(p[0]));
Coord const b(fabs(p[1]));
return ( a < b || is_nan(b)
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)
{
/** @brief True if the point has both coordinates zero.
* NaNs are treated as not equal to zero.
* @relates Point */
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. */
/** @brief True if the point has a length near 1. The are_near() function is used.
* @relates Point */
bool is_unit_vector(Point const &p) {
return are_near(L2(p), 1.0);
}
 
Coord atan2(Point const p) {
/** @brief Return the angle between the point and the +X axis.
* @return Angle in \f$(-\pi, \pi]\f$.
* @relates Point */
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) {
/** @brief Compute the angle between a and b relative to the origin.
* The computation is done by projecting b onto the basis defined by a, rot90(a).
* @return Angle in \f$(-\pi, \pi]\f$.
* @relates Point */
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.
*/
/** @brief Create a normalized version of a point.
* This is equivalent to copying the point and calling its normalize() method.
* The returned point will be (0,0) if the argument has both coordinates equal to zero.
* If any coordinate is NaN, this function will do nothing.
* @param a Input point
* @return Point on the unit circle in the same direction from origin as a, or the origin
* if a has both coordinates equal to zero
* @relates Point */
Point unit_vector(Point const &a)
{
Point ret(a);
126,33 → 144,54
ret.normalize();
return ret;
}
 
/** @brief Return the "absolute value" of the point's vector.
* This is defined in terms of the default lexicographical ordering. If the point is "larger"
* that the origin (0, 0), its negation is returned. This corresponds to making the Y coordinate
* positive. You can check whether the points' vectors have the same direction (e.g. lie
* on the same line passing through the origin) using
* @code abs(a).normalize() == abs(b).normalize() @endcode.
* To check with some margin of error, use
* @code are_near(abs(a).normalize(), abs(b).normalize()) @endcode.
* Although naively this should take the absolute value of each coordinate, such an operation
* is not very useful.
* @return \f$p' = (p_X, -p_Y)\f$
* @relates Point */
Point abs(Point const &b)
{
Point ret;
for ( int i = 0 ; i < 2 ; i++ ) {
ret[i] = fabs(b[i]);
}
Point ret = b;
ret[Y] = fabs(ret[Y]);
return ret;
}
 
Point operator*(Point const &v, Matrix const &m) {
Point ret;
/** @brief Transform the point by the specified matrix. */
Point &Point::operator*=(Affine const &m) {
double x = _pt[X], y = _pt[Y];
for(int i = 0; i < 2; i++) {
ret[i] = v[X] * m[i] + v[Y] * m[i + 2] + m[i + 4];
_pt[i] = x * m[i] + y * m[i + 2] + m[i + 4];
}
return ret;
return *this;
}
 
Point operator/(Point const &p, Matrix const &m) { return p * m.inverse(); }
 
Point &Point::operator*=(Matrix const &m)
/** @brief Snap the angle B - A - dir to miltiples of \f$2\pi/n\f$.
* The 'dir' argument must be normalized (have an unit length), otherwise the result
* is undefined.
* @return Point with the same distance from A as B, with a snapped angle.
* @post distance(A, B) == distance(A, result)
* @post angle_between(result - A, dir) == \f$2k\pi/n, k \in \mathbb{N}\f$
* @relates Point */
Point constrain_angle(Point const &A, Point const &B, unsigned int n, Point const &dir)
{
*this = *this * m;
return *this;
// for special cases we could perhaps use explicit testing (which might be faster)
if (n == 0.0) {
return B;
}
Point diff(B - A);
double angle = -angle_between(diff, dir);
double k = round(angle * (double)n / (2.0*M_PI));
return A + dir * Rotate(k * 2.0 * M_PI / (double)n) * L2(diff);
}
 
} //Namespace Geom
} //namespace Geom
 
/*
Local Variables:
/trunk/Scribus/scribus/third_party/lib2geom/sbasis.h
1,5 → 1,6
/*
* sbasis.h - S-power basis function class
/**
* \file
* \brief Defines S-power basis function class
*
* Authors:
* Nathan Hurst <njh@mail.csse.monash.edu.au>
42,21 → 43,79
#include "utils.h"
#include "exception.h"
 
namespace Geom {
//#define USE_SBASISN 1
 
 
#if defined(USE_SBASIS_OF)
 
#include "sbasis-of.h"
 
#elif defined(USE_SBASISN)
 
#include "sbasisN.h"
namespace Geom{
 
/*** An empty SBasis is identically 0. */
class SBasis : public std::vector<Linear>{
class SBasis : public SBasisN<1>;
 
};
#else
 
namespace Geom{
 
/**
* \brief S-power basis function class
*
* An empty SBasis is identically 0. */
class SBasis{
std::vector<Linear> d;
void push_back(Linear const&l) { d.push_back(l); }
 
public:
// As part of our migration away from SBasis isa vector we provide this minimal set of vector interface methods.
size_t size() const {return d.size();}
Linear operator[](unsigned i) const {
return d[i];
}
Linear& operator[](unsigned i) { return d.at(i); }
Linear const* begin() const { return (Linear const*)&*d.begin();}
Linear const* end() const { return (Linear const*)&*d.end();}
Linear* begin() { return (Linear*)&*d.begin();}
Linear* end() { return (Linear*)&*d.end();}
bool empty() const {return d.empty();}
Linear &back() {return d.back();}
Linear const &back() const {return d.back();}
void pop_back() { d.pop_back();}
void resize(unsigned n) { d.resize(n);}
void resize(unsigned n, Linear const& l) { d.resize(n, l);}
void reserve(unsigned n) { d.reserve(n);}
void clear() {d.clear();}
void insert(Linear* before, const Linear* src_begin, const Linear* src_end) { d.insert(std::vector<Linear>::iterator(before), src_begin, src_end);}
//void insert(Linear* aa, Linear* bb, Linear* cc} { d.insert(aa, bb, cc);}
Linear& at(unsigned i) { return d.at(i);}
//void insert(Linear* before, int& n, Linear const &l) { d.insert(std::vector<Linear>::iterator(before), n, l);}
bool operator==(SBasis const&B) const { return d == B.d;}
bool operator!=(SBasis const&B) const { return d != B.d;}
operator std::vector<Linear>() { return d;}
 
SBasis() {}
explicit SBasis(double a) {
push_back(Linear(a,a));
}
explicit SBasis(double a, double b) {
push_back(Linear(a,b));
}
SBasis(SBasis const & a) :
std::vector<Linear>(a)
d(a.d)
{}
SBasis(Linear const & bo) {
push_back(bo);
}
SBasis(Linear* bo) {
push_back(*bo);
}
explicit SBasis(size_t n, Linear const&l) : d(n, l) {}
 
//IMPL: FragmentConcept
typedef double output_type;
69,8 → 128,9
}
inline bool isConstant() const {
if (empty()) return true;
for (unsigned i = 0; i < size(); i++) {
if(!(*this)[i].isConstant()) return false;
if(!(*this)[0].isConstant()) return false;
for (unsigned i = 1; i < size(); i++) {
if(!(*this)[i].isZero()) return false;
}
return true;
}
82,41 → 142,26
inline double at1() const{
if(empty()) return 0; else return (*this)[0][1];
}
int degreesOfFreedom() const { return size()*2;}
 
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;
for(unsigned k = size(); k > 0; k--) {
const Linear &lin = (*this)[k-1];
p0 = p0*s + lin[0];
p1 = p1*s + lin[1];
}
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 valueAndDerivative(double t, double &der) const {
//}
double operator()(double t) const {
return valueAt(t);
}
 
std::vector<double> valueAndDerivatives(double /*t*/, unsigned /*n*/) const {
//TODO
throwNotImplemented(0);
}
std::vector<double> valueAndDerivatives(double t, unsigned n) const;
 
SBasis toSBasis() const { return SBasis(*this); }
 
125,20 → 170,16
// 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); }
private:
void derive(); // in place version
};
 
//TODO: figure out how to stick this in linear, while not adding an sbasis dep
145,15 → 186,21
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);
OptInterval bounds_exact(SBasis const &a);
OptInterval bounds_fast(SBasis const &a, int order = 0);
OptInterval bounds_local(SBasis const &a, const OptInterval &t, int order = 0);
 
/** Returns a function which reverses the domain of a.
\param a sbasis function
\relates SBasis
 
useful for reversing a parameteric curve.
*/
inline SBasis reverse(SBasis const &a) {
SBasis result;
result.reserve(a.size());
SBasis result(a.size(), Linear());
for(unsigned k = 0; k < a.size(); k++)
result.push_back(reverse(a[k]));
result[k] = reverse(a[k]);
return result;
}
 
160,11 → 207,10
//IMPL: ScalableConcept
inline SBasis operator-(const SBasis& p) {
if(p.isZero()) return SBasis();
SBasis result;
result.reserve(p.size());
SBasis result(p.size(), Linear());
for(unsigned i = 0; i < p.size(); i++) {
result.push_back(-p[i]);
result[i] = -p[i];
}
return result;
}
181,7 → 227,7
SBasis& operator-=(SBasis& a, const SBasis& b);
 
//TODO: remove?
inline SBasis operator+(const SBasis & a, Linear const & b) {
/*inline SBasis operator+(const SBasis & a, Linear const & b) {
if(b.isZero()) return a;
if(a.isZero()) return b;
SBasis result(a);
207,7 → 253,7
else
a[0] -= b;
return a;
}
}*/
 
//IMPL: OffsetableConcept
inline SBasis operator+(const SBasis & a, double b) {
224,7 → 270,7
}
inline SBasis& operator+=(SBasis& a, double b) {
if(a.isZero())
a.push_back(Linear(b,b));
a = SBasis(Linear(b,b));
else
a[0] += b;
return a;
231,7 → 277,7
}
inline SBasis& operator-=(SBasis& a, double b) {
if(a.isZero())
a.push_back(Linear(-b,-b));
a = SBasis(Linear(-b,-b));
else
a[0] -= b;
return a;
247,6 → 293,8
}
 
SBasis multiply(SBasis const &a, SBasis const &b);
// This performs a multiply and accumulate operation in about the same time as multiply. return a*b + c
SBasis multiply_add(SBasis const &a, SBasis const &b, SBasis c);
 
SBasis integral(SBasis const &c);
SBasis derivative(SBasis const &a);
266,7 → 314,12
return a;
}
 
//valuation: degree of the first non zero coefficient.
/** Returns the degree of the first non zero coefficient.
\param a sbasis function
\param tol largest abs val considered 0
\return first non zero coefficient
\relates SBasis
*/
inline unsigned
valuation(SBasis const &a, double tol=0){
unsigned val=0;
285,7 → 338,14
//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);
 
/** Returns the sbasis on domain [0,1] that was t on [from, to]
\param t sbasis function
\param from,to interval
\return sbasis
\relates SBasis
*/
inline SBasis portion(const SBasis &t, double from, double to) { return compose(t, Linear(from, to)); }
inline SBasis portion(const SBasis &t, Interval ivl) { return compose(t, Linear(ivl[0], ivl[1])); }
 
// compute f(g)
inline SBasis
305,10 → 365,12
return out_file;
}
 
// These are deprecated, use sbasis-math.h versions if possible
SBasis sin(Linear bo, int k);
SBasis cos(Linear bo, int k);
 
std::vector<double> roots(SBasis const & s);
std::vector<double> roots(SBasis const & s, Interval const inside);
std::vector<std::vector<double> > multi_roots(SBasis const &f,
std::vector<double> const &levels,
double htol=1e-7,
315,8 → 377,70
double vtol=1e-7,
double a=0,
double b=1);
 
//--------- Levelset like functions -----------------------------------------------------
 
/** Solve f(t) = v +/- tolerance. The collection of intervals where
* v - vtol <= f(t) <= v+vtol
* is returned (with a precision tol on the boundaries).
\param f sbasis function
\param level the value of v.
\param vtol: error tolerance on v.
\param a, b limit search on domain [a,b]
\param tol: tolerance on the result bounds.
\returns a vector of intervals.
*/
std::vector<Interval> level_set (SBasis const &f,
double level,
double vtol = 1e-5,
double a=0.,
double b=1.,
double tol = 1e-5);
 
/** Solve f(t)\in I=[u,v], which defines a collection of intervals (J_k). More precisely,
* a collection (J'_k) is returned with J'_k = J_k up to a given tolerance.
\param f sbasis function
\param level: the given interval of deisred values for f.
\param a, b limit search on domain [a,b]
\param tol: tolerance on the bounds of the result.
\returns a vector of intervals.
*/
std::vector<Interval> level_set (SBasis const &f,
Interval const &level,
double a=0.,
double b=1.,
double tol = 1e-5);
 
/** 'Solve' f(t) = v +/- tolerance for several values of v at once.
\param f sbasis function
\param levels vector of values, that should be sorted.
\param vtol: error tolerance on v.
\param a, b limit search on domain [a,b]
\param tol: the bounds of the returned intervals are exact up to that tolerance.
\returns a vector of vectors of intervals.
*/
std::vector<std::vector<Interval> > level_sets (SBasis const &f,
std::vector<double> const &levels,
double a=0.,
double b=1.,
double vtol = 1e-5,
double tol = 1e-5);
 
/** 'Solve' f(t)\in I=[u,v] for several intervals I at once.
\param f sbasis function
\param levels vector of 'y' intervals, that should be disjoints and sorted.
\param a, b limit search on domain [a,b]
\param tol: the bounds of the returned intervals are exact up to that tolerance.
\returns a vector of vectors of intervals.
*/
std::vector<std::vector<Interval> > level_sets (SBasis const &f,
std::vector<Interval> const &levels,
double a=0.,
double b=1.,
double tol = 1e-5);
 
}
#endif
 
/*
Local Variables:
/trunk/Scribus/scribus/third_party/lib2geom/toposweep.h
0,0 → 1,222
 
/**
* \file
* \brief TopoSweep - topology / graph representation of a PathVector, for boolean operations and related tasks
*
* Authors:
* Michael Sloan <mgsloan at gmail.com>
* Nathan Hurst <njhurst at njhurst.com>
*
* Copyright 2007-2009 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_GEOM_TOPOSWEEP_H
#define SEEN_GEOM_TOPOSWEEP_H
 
#include "coord.h"
#include "point.h"
#include "pathvector.h"
#include "rect.h"
#include "path.h"
#include "curve.h"
 
#include <boost/shared_ptr.hpp>
 
namespace Geom {
 
// indicates a particular curve in a pathvector
struct CurveIx {
unsigned path, ix;
CurveIx(unsigned p, unsigned i) : path(p), ix(i) {}
// retrieves the indicated curve from the pathvector
Curve const &get(PathVector const &ps) const {
return ps[path][ix];
}
bool operator==(CurveIx const &other) const {
return other.path == path && other.ix == ix;
}
};
 
// represents a monotonic section of a path
struct Section {
CurveIx curve;
double f, t;
Point fp, tp;
std::vector<int> windings;
Section(CurveIx cix, double fd, double td, Point fdp, Point tdp) : curve(cix), f(fd), t(td), fp(fdp), tp(tdp) { }
Section(CurveIx cix, double fd, double td, PathVector ps, Dim2 d) : curve(cix), f(fd), t(td) {
fp = curve.get(ps).pointAt(f), tp = curve.get(ps).pointAt(t);
if (Point::LexOrderRt(d)(tp, fp)) {
//swap from and to, since tp is left or above fp
std::swap(f, t);
std::swap(fp, tp);
}
}
Rect bbox() const { return Rect(fp, tp); }
bool operator==(Section const &other) const {
return (curve == other.curve) && (f == other.f) && (t == other.t);
}
};
 
class TopoGraph {
public:
 
// Represents an e double tol;dge on a vertex
class Edge {
public:
boost::shared_ptr<Section> section; // section associated with this edge
unsigned other; // index of the vertex this edge points to
Edge(boost::shared_ptr<Section> s, unsigned o) : section(s), other(o) {}
};
// Represents a vertex in the graph, in terms of a point and edges which enter and exit.
// A vertex has an "avg" point, which is a representative point for the vertex. All
// edges have an endpoint tol away.
class Vertex {
public:
std::vector<Edge> enters, exits; // indexes of the enter / exit edges
Point avg;
Vertex(Point p) : avg(p) {}
inline unsigned degree() const { return enters.size() + exits.size(); }
Edge operator[](unsigned ix) const;
Edge &operator[](unsigned ix);
void erase(unsigned ix);
void insert(unsigned ix, Edge e);
unsigned find_section(boost::shared_ptr<Section> section) const;
};
TopoGraph(PathVector const &ps, Dim2 d, double t);
unsigned size() const { return vertices.size(); }
Vertex &operator[](unsigned ix) { return vertices[ix]; }
Vertex const &operator[](unsigned ix) const { return vertices[ix]; }
//removes both edges, and returns the vertices[ix][jx] one
Edge remove_edge(unsigned ix, unsigned jx);
//returns a graph with all zero degree vertices and unused edges removed
void cannonize();
//checks invariants
void assert_invariants() const;
std::vector<Vertex> vertices;
Dim2 dim;
double tol;
};
 
//TODO: convert to classes
typedef std::vector<boost::shared_ptr<Section> > Area;
typedef std::vector<Area> Areas;
 
//TopoGraph sweep_graph(PathVector const &ps, Dim2 d = X, double tol = 0.00001);
 
void trim_whiskers(TopoGraph &g);
void double_whiskers(TopoGraph &g);
//void remove_degenerate(TopoGraph &g);
//void remove_vestigial(TopoGraph &g);
//Areas traverse_areas(TopoGraph const &g);
 
 
void remove_area_whiskers(Areas &areas);
PathVector areas_to_paths(PathVector const &ps, Areas const &areas);
 
class SectionSorter {
const PathVector &ps;
Dim2 dim;
double tol;
bool section_order(Section const &a, double at, Section const &b, double bt) const;
public:
typedef Section first_argument_type;
typedef Section second_argument_type;
typedef bool result_type;
SectionSorter(const PathVector &rs, Dim2 d, double t = 0.00001) : ps(rs), dim(d), tol(t) {}
bool operator()(Section const &a, Section const &b) const;
};
 
//sorter used to create the initial sweep of sections, such that they are dealt with in order
struct SweepSorter {
typedef Section first_argument_type;
typedef Section second_argument_type;
typedef bool result_type;
Dim2 dim;
SweepSorter(Dim2 d) : dim(d) {}
bool operator()(const Section &a, const Section &b) const {
return Point::LexOrderRt(dim)(a.fp, b.fp);
}
};
 
struct UnionOp {
unsigned ix;
bool nz1, nz2;
UnionOp(unsigned i, bool a, bool b) : ix(i), nz1(a), nz2(b) {}
bool operator()(std::vector<int> const &windings) const {
int w1 = 0, w2 = 0;
for(unsigned j = 0; j < ix; j++) w1 += windings[j];
for(unsigned j = ix; j < windings.size(); j++) w2 += windings[j];
return (nz1 ? w1 : w1 % 2) != 0 || (nz2 ? w2 : w2 % 2) != 0;
}
};
 
//returns all areas for which the winding -> bool function yields true
template<class Z>
Areas filter_areas(PathVector const &ps, Areas const & areas, Z const &z) {
Areas ret;
SweepSorter sorty = SweepSorter(Y);
SectionSorter sortx = SectionSorter(ps, X);
for(unsigned i = 0; i < areas.size(); i++) {
if(areas[i].size() < 2) continue;
//find a representative section
unsigned rj = 0;
bool rev = are_near(areas[i][0]->fp, areas[i][1]->tp);
for(unsigned j = 1; j < areas[i].size(); j++)
if(sorty(*areas[i][rj], *areas[i][j])) rj = j;
if(sortx(*areas[i][rj], *areas[i][(rj+areas[i].size() - 1) % areas[i].size()])) {
rj = 0;
for(unsigned j = 1; j < areas[i].size(); j++)
if(sorty(*areas[i][j], *areas[i][rj])) rj = j;
}
if(z(areas[i][rj]->windings)) ret.push_back(areas[i]);
}
return ret;
}
 
} // end namespace Geom
 
#endif // SEEN_GEOM_TOPOSWEEP_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 :
/trunk/Scribus/scribus/third_party/lib2geom/d2-sbasis.h
1,4 → 1,43
#ifdef _2GEOM_D2 /*This is intentional: we don't actually want anyone to
/**
* \file
* \brief Do not include this file \todo brief description
*
* 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.
*
* Authors:
* ? <?@?.?>
*
* Copyright ?-? 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.
*
*/
 
#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
7,7 → 46,7
#include "sbasis.h"
#include "sbasis-2d.h"
#include "piecewise.h"
#include "matrix.h"
#include "affine.h"
 
//TODO: implement intersect
 
32,17 → 71,20
//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);
D2<Piecewise<SBasis> > make_cuts_independent(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> dot(Piecewise<D2<SBasis> > const &a, Point 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> > operator*(Piecewise<D2<SBasis> > const &a, Affine const &m);
 
Piecewise<D2<SBasis> > force_continuity(Piecewise<D2<SBasis> > const &f,
double tol=0,
bool closed=false);
Piecewise<D2<SBasis> > force_continuity(Piecewise<D2<SBasis> > const &f, double tol=0, bool closed=false);
 
std::vector<Piecewise<D2<SBasis> > > fuse_nearby_ends(std::vector<Piecewise<D2<SBasis> > > const &f, double tol=0);
 
std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > split_at_discontinuities (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwsbin, double tol = .0001);
 
class CoordIterator
: public std::iterator<std::input_iterator_tag, SBasis const>
{
76,16 → 118,45
}
 
//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 OptRect bounds_fast(D2<SBasis> const & s, unsigned order=0) {
OptRect retval;
OptInterval xint = bounds_fast(s[X], order);
if (xint) {
OptInterval yint = bounds_fast(s[Y], order);
if (yint) {
retval = Rect(*xint, *yint);
}
}
return retval;
}
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));
inline OptRect bounds_local(D2<SBasis> const & s, OptInterval i, unsigned order=0) {
OptRect retval;
OptInterval xint = bounds_local(s[X], i, order);
OptInterval yint = bounds_local(s[Y], i, order);
if (xint && yint) {
retval = Rect(*xint, *yint);
}
return retval;
}
 
std::vector<Interval> level_set( D2<SBasis> const &f, Rect region);
std::vector<Interval> level_set( D2<SBasis> const &f, Point p, double tol);
std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Rect> regions);
std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Point> pts, double tol);
 
}
 
#endif
#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 :
/trunk/Scribus/scribus/third_party/lib2geom/circle-circle.cpp
42,7 → 42,7
*/
#include <stdio.h>
#include <math.h>
#include "point.h"
#include <point.h>
 
namespace Geom{
 
129,3 → 129,13
}
#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 :
/trunk/Scribus/scribus/third_party/lib2geom/ray.h
0,0 → 1,264
/**
* \file
* \brief Infinite Straight Ray
*
* 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 LIB2GEOM_SEEN_RAY_H
#define LIB2GEOM_SEEN_RAY_H
 
#include <vector>
#include "point.h"
#include "bezier-curve.h" // for LineSegment
#include "exception.h"
 
namespace Geom
{
 
/**
* @brief Straight ray from a specific point to infinity.
*
* Rays are "half-lines" - they begin at some specific point and extend in a straight line
* to infinity.
*
* @ingroup Primitives
*/
class Ray {
private:
Point m_origin;
Point m_versor;
 
public:
Ray()
: m_origin(0,0), m_versor(1,0)
{
}
 
Ray(Point const& _origin, Coord angle )
: m_origin(_origin), m_versor(std::cos(angle), std::sin(angle))
{
}
 
Ray(Point const& A, Point const& B)
{
setBy2Points(A, B);
}
 
Point origin() const
{
return m_origin;
}
 
Point versor() const
{
return m_versor;
}
 
void origin(Point const& _point)
{
m_origin = _point;
}
 
void versor(Point const& _versor)
{
m_versor = _versor;
}
 
Coord angle() const
{
double a = std::atan2(m_versor[Y], m_versor[X]);
if (a < 0) a += 2*M_PI;
return a;
}
 
void angle(Coord _angle)
{
m_versor[X] = std::cos(_angle);
m_versor[Y] = std::sin(_angle);
}
 
void setBy2Points(Point const& A, Point const& B)
{
m_origin = A;
m_versor = B - A;
if ( are_near(m_versor, Point(0,0)) )
m_versor = Point(0,0);
else
m_versor.normalize();
}
 
bool isDegenerate() const
{
return ( m_versor[X] == 0 && m_versor[Y] == 0 );
}
 
Point pointAt(Coord t) const
{
if (t < 0) THROW_RANGEERROR("Ray::pointAt, negative t value passed");
return m_origin + m_versor * t;
}
 
Coord valueAt(Coord t, Dim2 d) const
{
if (t < 0)
THROW_RANGEERROR("Ray::valueAt, negative t value passed");
if (d < 0 || d > 1)
THROW_RANGEERROR("Ray::valueAt, dimension argument out of range");
return m_origin[d] + m_versor[d] * t;
}
 
std::vector<Coord> roots(Coord v, Dim2 d) const
{
if (d < 0 || d > 1)
THROW_RANGEERROR("Ray::roots, dimension argument out of range");
std::vector<Coord> result;
if ( m_versor[d] != 0 )
{
double t = (v - m_origin[d]) / m_versor[d];
if (t >= 0) result.push_back(t);
}
// TODO: else ?
return result;
}
 
// require are_near(_point, *this)
// on the contrary the result value is meaningless
Coord timeAt(Point const& _point) const
{
Coord t;
if ( m_versor[X] != 0 )
{
t = (_point[X] - m_origin[X]) / m_versor[X];
}
else if ( m_versor[Y] != 0 )
{
t = (_point[Y] - m_origin[Y]) / m_versor[Y];
}
else // degenerate case
{
t = 0;
}
return t;
}
 
Coord nearestPoint(Point const& _point) const
{
if ( isDegenerate() ) return 0;
double t = dot( _point - m_origin, m_versor );
if (t < 0) t = 0;
return t;
}
 
Ray reverse() const
{
Ray result;
result.origin(m_origin);
result.versor(-m_versor);
return result;
}
 
Curve* portion(Coord f, Coord t) const
{
LineSegment* seg = new LineSegment(pointAt(f), pointAt(t));
return seg;
}
 
LineSegment segment(Coord f, Coord t) const
{
return LineSegment(pointAt(f), pointAt(t));
}
 
Ray transformed(Affine const& m) const
{
return Ray(m_origin * m, (m_origin + m_versor) * m);
}
}; // end class ray
 
inline
double distance(Point const& _point, Ray const& _ray)
{
double t = _ray.nearestPoint(_point);
return ::Geom::distance(_point, _ray.pointAt(t));
}
 
inline
bool are_near(Point const& _point, Ray const& _ray, double eps = EPSILON)
{
return are_near(distance(_point, _ray), 0, eps);
}
 
inline
bool are_same(Ray const& r1, Ray const& r2, double eps = EPSILON)
{
return are_near(r1.versor(), r2.versor(), eps)
&& are_near(r1.origin(), r2.origin(), eps);
}
 
// evaluate the angle between r1 and r2 rotating r1 in cw or ccw direction on r2
// the returned value is an angle in the interval [0, 2PI[
inline
double angle_between(Ray const& r1, Ray const& r2, bool cw = true)
{
double angle = angle_between(r1.versor(), r2.versor());
if (angle < 0) angle += 2*M_PI;
if (!cw) angle = 2*M_PI - angle;
return angle;
}
 
 
inline
Ray make_angle_bisector_ray(Ray const& r1, Ray const& r2)
{
if ( !are_near(r1.origin(), r2.origin()) )
{
THROW_RANGEERROR("passed rays have not the same origin");
}
 
Point M = middle_point(r1.pointAt(1), r2.pointAt(1) );
if (angle_between(r1, r2) > M_PI) M = 2 * r1.origin() - M;
return Ray(r1.origin(), M);
}
 
 
} // end namespace Geom
 
 
 
#endif /*_2GEOM_RAY_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 :
*/
/trunk/Scribus/scribus/third_party/lib2geom/circle.cpp
0,0 → 1,116
/*
* Circle Curve
*
* Authors:
* Marco Cecchetti <mrcekets at gmail.com>
*
* Copyright 2008 authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*/
 
 
#include <circle.h>
#include <ellipse.h>
#include <svg-elliptical-arc.h>
#include <numeric/fitting-tool.h>
#include <numeric/fitting-model.h>
 
 
 
namespace Geom
{
 
void Circle::set(double A, double B, double C, double D)
{
if (A == 0)
{
THROW_RANGEERROR("square term coefficient == 0");
}
 
//std::cerr << "B = " << B << " C = " << C << " D = " << D << std::endl;
 
double b = B / A;
double c = C / A;
double d = D / A;
 
m_centre[X] = -b/2;
m_centre[Y] = -c/2;
double r2 = m_centre[X] * m_centre[X] + m_centre[Y] * m_centre[Y] - d;
 
if (r2 < 0)
{
THROW_RANGEERROR("ray^2 < 0");
}
 
m_ray = std::sqrt(r2);
}
 
 
void Circle::set(std::vector<Point> const& points)
{
size_t sz = points.size();
if (sz < 3)
{
THROW_RANGEERROR("fitting error: too few points passed");
}
NL::LFMCircle model;
NL::least_squeares_fitter<NL::LFMCircle> fitter(model, sz);
 
for (size_t i = 0; i < sz; ++i)
{
fitter.append(points[i]);
}
fitter.update();
 
NL::Vector z(sz, 0.0);
model.instance(*this, fitter.result(z));
}
 
 
EllipticalArc *
Circle::arc(Point const& initial, Point const& inner, Point const& final,
bool _svg_compliant)
{
// TODO native implementation!
Ellipse e(center(X), center(Y), ray(), ray(), 0);
return e.arc(initial, inner, final, _svg_compliant);
}
 
 
} // end namespace Geom
 
 
 
 
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/sbasis-poly.h
4,8 → 4,38
#include "poly.h"
#include "sbasis.h"
 
/*** Conversion between SBasis and Poly. Not recommended for general
* use due to instability.
/**
* \file
* \brief Conversion between SBasis and Poly. Not recommended for general use due to instability.
*
* Authors:
* ? <?@?.?>
*
* Copyright ?-? 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.
*
*/
 
namespace Geom{
/trunk/Scribus/scribus/third_party/lib2geom/coord.h
1,5 → 1,6
/*
* coord.h
/**
* \file
* \brief Defines the Coord "real" type with sufficient precision for coordinates.
*
* Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
*
32,6 → 33,7
#define SEEN_Geom_COORD_H
 
#include <cmath>
#include <limits>
 
namespace Geom {
 
46,8 → 48,11
 
const Coord EPSILON = 1e-5; //1e-18;
 
inline Coord infinity() { return std::numeric_limits<Coord>::infinity(); }
 
//IMPL: NearConcept
inline bool are_near(Coord a, Coord b, double eps=EPSILON) { return fabs(a-b) <= eps; }
inline bool are_near(Coord a, Coord b, double eps=EPSILON) { return a-b <= eps && a-b >= -eps; }
inline bool rel_error_bound(Coord a, Coord b, double eps=EPSILON) { return a <= eps*b && a >= -eps*b; }
 
} /* namespace Geom */
 
/trunk/Scribus/scribus/third_party/lib2geom/circulator.h
1,5 → 1,6
/*
* ciculator.h
/**
* \file circulator.h
* \brief \todo brief description
*
* Copyright 2006 MenTaLguY <mental@rydia.net>
*
139,11 → 140,9
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(substatement-open . 0))
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
c-brace-offset:0
fill-column:99
End:
vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
*/
 
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/quadtree.h
1,6 → 1,44
/**
* \file
* \brief \todo brief description
*
* Authors:
* ? <?@?.?>
*
* Copyright ?-? 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 <vector>
#include <cassert>
 
#include "d2.h"
 
namespace Geom{
 
class Quad{
public:
Quad* children[4];
10,6 → 48,23
children[i] = 0;
}
typedef std::vector<int>::iterator iterator;
Rect bounds(unsigned i, double x, double y, double d) {
double dd = d/2;
switch(i % 4) {
case 0:
return Rect(Interval(x, x+dd), Interval(y, y+dd));
case 1:
return Rect(Interval(x+dd, x+d), Interval(y, y+dd));
case 2:
return Rect(Interval(x, x+dd), Interval(y+dd, y+d));
case 3:
return Rect(Interval(x+dd, x+d), Interval(y+dd, y+d));
default:
/* just to suppress warning message
* this case should be never reached */
assert(false);
}
}
};
 
class QuadTree{
23,19 → 78,22
 
Quad* search(double x0, double y0, double x1, double y1);
void insert(double x0, double y0, double x1, double y1, int shape);
Quad* search(Geom::Rect const &r);
void insert(Geom::Rect const &r, int shape);
void erase(Quad *q, int shape);
private:
bool clean_root();
};
 
};
 
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(substatement-open . 0))
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
c-brace-offset:0
fill-column:99
End:
vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
*/
 
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/convex-cover.h
1,8 → 1,9
#ifndef GEOM_CONVEX_COVER_H
#define GEOM_CONVEX_COVER_H
 
/*
* convex-cover.h
/**
* \file
* \brief \todo brief description
*
* Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
* Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
61,11 → 62,14
public:
std::vector<Point> boundary;
//Point centroid;
 
void merge(Point p);
bool contains_point(Point p);
bool strict_contains_point(Point p);
 
inline size_t size() const { return boundary.size();}
inline Point operator[](int i) const {
 
int l = boundary.size();
if(l == 0) return Point();
return boundary[i >= 0 ? i % l : (i % l) + l];
86,7 → 90,11
 
template <typename T>
ConvexHull(T b, T e) :boundary(b,e) {}
 
~ConvexHull()
{
}
 
public:
/** Is the convex hull clockwise? We use the definition of clockwise from point.h
**/
94,10 → 102,10
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;}
 
104,21 → 112,29
// 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)
double centroid_and_area(Geom::Point& centroid) const;
double area() const {
Point tmp;
return centroid_and_area(tmp);
}
 
// furthest point in a direction (lg time)
Point const * furthest(Point direction) const;
 
bool is_left(Point p, int n);
bool is_strict_left(Point p, int n);
int find_left(Point p);
int find_strict_left(Point p);
double narrowest_diameter(Point &a, Point &b, Point &c);
 
};
 
// do two convex hulls intersect?
bool intersectp(ConvexHull a, ConvexHull b);
 
std::vector<Point> bridge_points(ConvexHull a, ConvexHull b);
std::vector<std::pair<int, int> > bridges(ConvexHull a, ConvexHull b);
 
// find the convex hull intersection
ConvexHull intersection(ConvexHull a, ConvexHull b);
138,9 → 154,9
*/
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);
}
147,12 → 163,14
return pr;
}
 
ConvexHull clip(ConvexHull const & ch, Point n, double d);
 
//TODO: reinstate
/*class ConvexCover{
public:
Path const* path;
std::vector<ConvexHull> cc;
 
ConvexCover(Path const &sp);
};*/
 
164,11 → 182,9
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(substatement-open . 0))
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
c-brace-offset:0
fill-column:99
End:
vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
*/
 
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
/trunk/Scribus/scribus/third_party/lib2geom/pathvector.h
0,0 → 1,139
/**
* \file
* \brief PathVector - std::vector containing Geom::Path
* This file provides a set of operations that can be performed on PathVector,
* e.g. an affine transform.
*
* Authors:
* Johan Engelen <goejendaagh@zonnet.nl>
*
* Copyright 2008 authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*/
 
#ifndef SEEN_GEOM_PATHVECTOR_H
#define SEEN_GEOM_PATHVECTOR_H
 
#include "forward.h"
#include "path.h"
#include "transforms.h"
 
namespace Geom {
 
typedef std::vector<Geom::Path> PathVector;
 
/* general path transformation: */
inline
void operator*= (PathVector & path_in, Affine const &m) {
for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) {
(*it) *= m;
}
}
inline
PathVector operator*(PathVector const & path_in, Affine const &m) {
PathVector ret(path_in);
ret *= m;
return ret;
}
 
/* specific path transformations: Translation:
* This makes it possible to make optimized implementations for Translate transforms */
inline
void operator*= (PathVector & path_in, Translate const &m) {
for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) {
(*it) *= m;
}
}
inline
PathVector operator*(PathVector const & path_in, Translate const &m) {
PathVector ret(path_in);
ret *= m;
return ret;
}
 
/* user friendly approach to Translate transforms: just add an offset Point to the whole path */
inline
void operator+=(PathVector &path_in, Point const &p) {
for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) {
(*it) *= Translate(p);
}
}
inline
PathVector operator+(PathVector const &path_in, Point const &p) {
PathVector ret(path_in);
ret *= Translate(p);
return ret;
}
 
inline
Geom::Point initialPoint(PathVector const &path_in)
{
return path_in.front().initialPoint();
}
 
inline
Geom::Point finalPoint(PathVector const &path_in)
{
return path_in.back().finalPoint();
}
 
PathVector reverse_paths_and_order (PathVector const & path_in);
 
OptRect bounds_fast( PathVector const & pv );
OptRect bounds_exact( PathVector const & pv );
 
struct PathVectorPosition {
// pathvector[path_nr].pointAt(t) is the position
unsigned int path_nr;
double t;
PathVectorPosition() {}
PathVectorPosition(unsigned int path_nr,
double t) : path_nr(path_nr), t(t) {}
};
boost::optional<PathVectorPosition> nearestPoint(PathVector const & path_in, Point const& _point, double *distance_squared = NULL);
 
std::vector<PathVectorPosition> allNearestPoints(PathVector const & path_in, Point const& _point, double *distance_squared = NULL);
 
inline
Point pointAt(PathVector const & path_in, PathVectorPosition const pvp) {
return path_in[pvp.path_nr].pointAt(pvp.t);
}
 
 
 
} // end namespace Geom
 
#endif // SEEN_GEOM_PATHVECTOR_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 :
/trunk/Scribus/scribus/third_party/lib2geom/basic-intersection.cpp
1,87 → 1,93
#include "basic-intersection.h"
#include "exception.h"
#include "angle.h"
#include <basic-intersection.h>
#include <sbasis-to-bezier.h>
#include <exception.h>
 
#ifndef M_SQRT2
#define M_SQRT2 1.41421356237309504880
#ifdef HAVE_GSL
#include <gsl/gsl_vector.h>
#include <gsl/gsl_multiroots.h>
#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;
//#ifdef USE_RECURSIVE_INTERSECTOR
 
// void find_intersections(std::vector<std::pair<double, double> > &xs,
// D2<SBasis> const & A,
// D2<SBasis> const & B) {
// vector<Point> BezA, BezB;
// sbasis_to_bezier(BezA, A);
// sbasis_to_bezier(BezB, B);
~OldBezier() {}
// xs.clear();
// find_intersections_bezier_recursive(xs, BezA, BezB);
// }
// void find_intersections(std::vector< std::pair<double, double> > & xs,
// std::vector<Point> const& A,
// std::vector<Point> const& B,
// double precision){
// find_intersections_bezier_recursive(xs, A, B, precision);
// }
 
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];
}
//#else
 
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];
}
namespace detail{ namespace bezier_clipping {
void portion (std::vector<Point> & B, Interval const& I);
}; };
 
}
void find_intersections(std::vector<std::pair<double, double> > &xs,
D2<SBasis> const & A,
D2<SBasis> const & B) {
vector<Point> BezA, BezB;
sbasis_to_bezier(BezA, A);
sbasis_to_bezier(BezB, B);
 
xs.clear();
};
find_intersections_bezier_clipping(xs, BezA, BezB);
}
void find_intersections(std::vector< std::pair<double, double> > & xs,
std::vector<Point> const& A,
std::vector<Point> const& B,
double precision){
find_intersections_bezier_clipping(xs, A, B, precision);
}
 
static std::vector<std::pair<double, double> >
find_intersections( OldBezier a, OldBezier b);
//#endif
 
static std::vector<std::pair<double, double> >
find_self_intersections(OldBezier const &Sb, D2<SBasis> const & A);
/*
* 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 split(vector<Point> const &p, double t,
vector<Point> &left, vector<Point> &right) {
const unsigned sz = p.size();
Geom::Point Vtemp[sz][sz];
 
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);
}
/* Copy control points */
std::copy(p.begin(), p.end(), Vtemp[0]);
 
std::vector<std::pair<double, double> >
find_self_intersections(OldBezier const &Sb) {
throwNotImplemented(0);
}
/* Triangle computation */
for (unsigned i = 1; i < sz; i++) {
for (unsigned j = 0; j < sz - i; j++) {
Vtemp[i][j] = lerp(t, Vtemp[i-1][j], Vtemp[i-1][j+1]);
}
}
 
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);
left.resize(sz);
right.resize(sz);
for (unsigned j = 0; j < sz; j++)
left[j] = Vtemp[j][0];
for (unsigned j = 0; j < sz; j++)
right[j] = Vtemp[sz-1-j][j];
}
 
 
static std::vector<std::pair<double, double> >
find_self_intersections(OldBezier const &Sb, D2<SBasis> const & A) {
 
void
find_self_intersections(std::vector<std::pair<double, double> > &xs,
D2<SBasis> const & A) {
vector<double> dr = roots(derivative(A[X]));
{
vector<double> dyr = roots(derivative(A[Y]));
91,266 → 97,320
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;
vector<double>::iterator new_end = unique(dr.begin(), dr.end());
dr.resize( new_end - dr.begin() );
 
vector<vector<Point> > pieces;
{
OldBezier in = Sb, l, r;
vector<Point> in, l, r;
sbasis_to_bezier(in, A);
for(unsigned i = 0; i < dr.size()-1; i++) {
in.split((dr[i+1]-dr[i]) / (1 - dr[i]), l, r);
split(in, (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]);
std::vector<std::pair<double, double> > section;
find_intersections( section, 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))
//if((l == 1) && (r == 0))
if( ( l > 1-1e-4 ) && (r < 1e-4) )//FIXME: what precision should be used here???
continue;
all_si.push_back(std::make_pair((1-l)*dr[i] + l*dr[i+1],
xs.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;
 
// Because i is in order, xs should be roughly already in order?
//sort(xs.begin(), xs.end());
//unique(xs.begin(), xs.end());
}
 
/* The value of 1.0 / (1L<<14) is enough for most applications */
const double INV_EPS = (1L<<14);
#ifdef HAVE_GSL
#include <gsl/gsl_multiroots.h>
 
struct rparams
{
D2<SBasis> const &A;
D2<SBasis> const &B;
};
 
static int
intersect_polish_f (const gsl_vector * x, void *params,
gsl_vector * f)
{
const double x0 = gsl_vector_get (x, 0);
const double x1 = gsl_vector_get (x, 1);
 
Geom::Point dx = ((struct rparams *) params)->A(x0) -
((struct rparams *) params)->B(x1);
 
gsl_vector_set (f, 0, dx[0]);
gsl_vector_set (f, 1, dx[1]);
 
return GSL_SUCCESS;
}
#endif
 
union dbl_64{
long long i64;
double d64;
};
 
static double EpsilonBy(double value, int eps)
{
dbl_64 s;
s.d64 = value;
s.i64 += eps;
return s.d64;
}
 
 
static void intersect_polish_root (D2<SBasis> const &A, double &s,
D2<SBasis> const &B, double &t) {
#ifdef HAVE_GSL
const gsl_multiroot_fsolver_type *T;
gsl_multiroot_fsolver *sol;
 
int status;
size_t iter = 0;
#endif
std::vector<Point> as, bs;
as = A.valueAndDerivatives(s, 2);
bs = B.valueAndDerivatives(t, 2);
Point F = as[0] - bs[0];
double best = dot(F, F);
/*
* 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);
for(int i = 0; i < 4; i++) {
/**
we want to solve
J*(x1 - x0) = f(x0)
|dA(s)[0] -dB(t)[0]| (X1 - X0) = A(s) - B(t)
|dA(s)[1] -dB(t)[1]|
**/
 
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]);
// We're using the standard transformation matricies, which is numerically rather poor. Much better to solve the equation using elimination.
 
Affine jack(as[1][0], as[1][1],
-bs[1][0], -bs[1][1],
0, 0);
Point soln = (F)*jack.inverse();
double ns = s - soln[0];
double nt = t - soln[1];
as = A.valueAndDerivatives(ns, 2);
bs = B.valueAndDerivatives(nt, 2);
F = as[0] - bs[0];
double trial = dot(F, F);
if (trial > best*0.1) {// we have standards, you know
// At this point we could do a line search
break;
}
left.p[i] = Vtemp[0];
right.p[sz-1-i] = Vtemp[sz-1-i];
best = trial;
s = ns;
t = nt;
}
}
 
/*
* 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.
*/
#ifdef HAVE_GSL
const size_t n = 2;
struct rparams p = {A, B};
gsl_multiroot_function f = {&intersect_polish_f, n, &p};
 
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> > &parameters)
{
intersect_steps ++;
if( deptha > 0 )
double x_init[2] = {s, t};
gsl_vector *x = gsl_vector_alloc (n);
 
gsl_vector_set (x, 0, x_init[0]);
gsl_vector_set (x, 1, x_init[1]);
 
T = gsl_multiroot_fsolver_hybrids;
sol = gsl_multiroot_fsolver_alloc (T, 2);
gsl_multiroot_fsolver_set (sol, &f, x);
 
do
{
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 );
iter++;
status = gsl_multiroot_fsolver_iterate (sol);
 
if (status) /* check if solver is stuck */
break;
 
status =
gsl_multiroot_test_residual (sol->f, 1e-12);
}
while (status == GSL_CONTINUE && iter < 1000);
 
s = gsl_vector_get (sol->x, 0);
t = gsl_vector_get (sol->x, 1);
 
gsl_multiroot_fsolver_free (sol);
gsl_vector_free (x);
#endif
{
// This code does a neighbourhood search for minor improvements.
double best_v = L1(A(s) - B(t));
//std::cout << "------\n" << best_v << std::endl;
Point best(s,t);
while (true) {
Point trial = best;
double trial_v = best_v;
for(int nsi = -1; nsi < 2; nsi++) {
for(int nti = -1; nti < 2; nti++) {
Point n(EpsilonBy(best[0], nsi),
EpsilonBy(best[1], nti));
double c = L1(A(n[0]) - B(n[1]));
//std::cout << c << "; ";
if (c < trial_v) {
trial = n;
trial_v = c;
}
}
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 );
}
if(trial == best) {
//std::cout << "\n" << s << " -> " << s - best[0] << std::endl;
//std::cout << t << " -> " << t - best[1] << std::endl;
//std::cout << best_v << std::endl;
s = best[0];
t = best[1];
return;
} else {
best = trial;
best_v = trial_v;
}
}
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]));
void polish_intersections(std::vector<std::pair<double, double> > &xs,
D2<SBasis> const &A, D2<SBasis> const &B)
{
for(unsigned i = 0; i < xs.size(); i++)
intersect_polish_root(A, xs[i].first,
B, xs[i].second);
}
 
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;
 
/**
* Compute the Hausdorf distance from A to B only.
*/
 
 
#if 0
/** Compute the value of a bezier
Todo: find a good palce for this.
*/
// suggested by Sederberg.
Point OldBezier::operator()(double t) const {
int n = p.size()-1;
double u, bc, tn, tmp;
int i;
Point r;
for(int dim = 0; dim < 2; dim++) {
u = 1.0 - t;
bc = 1;
tn = 1;
tmp = p[0][dim]*u;
for(i=1; i<n; i++){
tn = tn*t;
bc = bc*(n-i+1)/i;
tmp = (tmp + tn*bc*p[i][dim])*u;
}
r[dim] = (tmp + tn*t*p[n][dim]);
}
return r;
}
#endif
 
std::vector<std::pair<double, double> > find_intersections( OldBezier a, OldBezier b)
{
std::vector<std::pair<double, double> > parameters;
if( intersect_BB( a, b ) )
/**
* Compute the Hausdorf distance from A to B only.
*/
double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B,
double m_precision,
double *a_t, double* b_t) {
std::vector< std::pair<double, double> > xs;
std::vector<Point> Az, Bz;
sbasis_to_bezier (Az, A);
sbasis_to_bezier (Bz, B);
find_collinear_normal(xs, Az, Bz, m_precision);
double h_dist = 0, h_a_t = 0, h_b_t = 0;
double dist = 0;
Point Ax = A.at0();
double t = Geom::nearest_point(Ax, B);
dist = Geom::distance(Ax, B(t));
if (dist > h_dist) {
h_a_t = 0;
h_b_t = t;
h_dist = dist;
}
Ax = A.at1();
t = Geom::nearest_point(Ax, B);
dist = Geom::distance(Ax, B(t));
if (dist > h_dist) {
h_a_t = 1;
h_b_t = t;
h_dist = dist;
}
for (size_t i = 0; i < xs.size(); ++i)
{
recursively_intersect( a, 0., 1., wangs_theorem(a),
b, 0., 1., wangs_theorem(b),
parameters);
Point At = A(xs[i].first);
Point Bu = B(xs[i].second);
double distAtBu = Geom::distance(At, Bu);
t = Geom::nearest_point(At, B);
dist = Geom::distance(At, B(t));
//FIXME: we might miss it due to floating point precision...
if (dist >= distAtBu-.1 && distAtBu > h_dist) {
h_a_t = xs[i].first;
h_b_t = xs[i].second;
h_dist = distAtBu;
}
}
std::sort(parameters.begin(), parameters.end());
return parameters;
if(a_t) *a_t = h_a_t;
if(b_t) *b_t = h_b_t;
return h_dist;
}
 
/**
* Compute the symmetric Hausdorf distance.
*/
double hausdorf(D2<SBasis>& A, D2<SBasis> const& B,
double m_precision,
double *a_t, double* b_t) {
double h_dist = hausdorfl(A, B, m_precision, a_t, b_t);
double dist = 0;
Point Bx = B.at0();
double t = Geom::nearest_point(Bx, A);
dist = Geom::distance(Bx, A(t));
if (dist > h_dist) {
if(a_t) *a_t = t;
if(b_t) *b_t = 0;
h_dist = dist;
}
Bx = B.at1();
t = Geom::nearest_point(Bx, A);
dist = Geom::distance(Bx, A(t));
if (dist > h_dist) {
if(a_t) *a_t = t;
if(b_t) *b_t = 1;
h_dist = dist;
}
return h_dist;
}
};
 
/*
/trunk/Scribus/scribus/third_party/lib2geom/svg-elliptical-arc.h
1,7 → 1,14
/*
* SVG Elliptical Path Support Class
/**
* \file
* \brief SVG 1.1-compliant elliptical arc curve
*
* Copyright 2008 Marco Cecchetti <mrcekets at gmail.com>
*//*
* Authors:
* MenTaLguY <mental@rydia.net>
* Marco Cecchetti <mrcekets at gmail.com>
* Krzysztof Kosiński <tweenk.pl@gmail.com>
*
* Copyright 2007-2009 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
28,359 → 35,236
*/
 
 
#ifndef _SVG_ELLIPTICAL_ARC_H_
#define _SVG_ELLIPTICAL_ARC_H_
#ifndef _2GEOM_SVG_ELLIPTICAL_ARC_H_
#define _2GEOM_SVG_ELLIPTICAL_ARC_H_
 
 
#include "curve.h"
#include "angle.h"
#include "matrix.h"
#include "sbasis.h"
#include "d2.h"
#include "utils.h"
#include "bezier-curve.h"
#include "elliptical-arc.h"
#include "sbasis-curve.h" // for non-native methods
#include "numeric/vector.h"
#include "numeric/fitting-tool.h"
#include "numeric/fitting-model.h"
#include <algorithm>
 
 
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();
}
class SVGEllipticalArc : public EllipticalArc {
public:
SVGEllipticalArc()
: EllipticalArc()
{}
SVGEllipticalArc( Point _initial_point, double _rx, double _ry,
double _rot_angle, bool _large_arc, bool _sweep,
Point _final_point
)
: EllipticalArc(_initial_point, _rx, _ry, _rot_angle, _large_arc, _sweep, _final_point)
{}
 
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;
#ifndef DOXYGEN_SHOULD_SKIP_THIS
virtual Curve *duplicate() const {
return new SVGEllipticalArc(*this);
}
 
public:
double center(Geom::Dim2 i) const
{
return m_center[i];
virtual Coord valueAt(Coord t, Dim2 d) const {
if (isDegenerate()) return chord().valueAt(t, d);
return EllipticalArc::valueAt(t, d);
}
 
Point center() const
{
return m_center;
virtual Point pointAt(Coord t) const {
if (isDegenerate()) return chord().pointAt(t);
return EllipticalArc::pointAt(t);
}
 
Point initialPoint() const
{
return m_initial_point;
virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const {
if (isDegenerate()) return chord().pointAndDerivatives(t, n);
return EllipticalArc::pointAndDerivatives(t, n);
}
 
Point finalPoint() const
{
return m_final_point;
virtual Rect boundsExact() const {
if (isDegenerate()) return chord().boundsExact();
return EllipticalArc::boundsExact();
}
 
double start_angle() const
{
return m_start_angle;
virtual OptRect boundsLocal(OptInterval const &i, unsigned int deg) const {
if (isDegenerate()) return chord().boundsLocal(i, deg);
return EllipticalArc::boundsLocal(i, deg);
}
 
double end_angle() const
{
return m_end_angle;
virtual Curve *derivative() const {
if (isDegenerate()) return chord().derivative();
return EllipticalArc::derivative();
}
 
double ray(Geom::Dim2 i) const
{
return (i == 0) ? m_rx : m_ry;
virtual std::vector<Coord> roots(Coord v, Dim2 d) const {
if (isDegenerate()) return chord().roots(v, d);
return EllipticalArc::roots(v, d);
}
 
bool large_arc_flag() const
{
return m_large_arc;
virtual std::vector<Coord> allNearestPoints( Point const& p, double from = 0, double to = 1 ) const {
if (isDegenerate()) {
std::vector<Coord> result;
result.push_back(chord().nearestPoint(p, from, to));
return result;
}
return EllipticalArc::allNearestPoints(p, from, to);
}
virtual D2<SBasis> toSBasis() const {
if (isDegenerate()) return chord().toSBasis();
return EllipticalArc::toSBasis();
}
virtual bool isSVGCompliant() const { return true; }
// TODO move SVG-specific behavior here.
//protected:
//virtual void _updateCenterAndAngles();
#endif
}; // end class SVGEllipticalArc
 
// void large_arc_flag(bool v)
// {
// m_large_arc = v;
// }
/*
* useful for testing and debugging
*/
template< class charT >
inline
std::basic_ostream<charT> &
operator<< (std::basic_ostream<charT> & os, const SVGEllipticalArc & ea)
{
os << "{ cx: " << ea.center(X) << ", cy: " << ea.center(Y)
<< ", rx: " << ea.ray(X) << ", ry: " << ea.ray(Y)
<< ", rot angle: " << decimal_round(rad_to_deg(ea.rotationAngle()),2)
<< ", start angle: " << decimal_round(rad_to_deg(ea.initialAngle()),2)
<< ", end angle: " << decimal_round(rad_to_deg(ea.finalAngle()),2)
<< " }";
 
bool sweep_flag() const
{
return m_sweep;
}
return os;
}
 
// 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();
}
// forward declation
namespace detail
{
struct ellipse_equation;
}
 
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();
}
// TODO this needs to be rewritten and moved to EllipticalArc header
/*
* make_elliptical_arc
*
* convert a parametric polynomial curve given in symmetric power basis form
* into an SVGEllipticalArc type; in order to be successfull the input curve
* has to look like an actual elliptical arc even if a certain tolerance
* is allowed through an ad-hoc parameter.
* The conversion is performed through an interpolation on a certain amount of
* sample points computed on the input curve;
* the interpolation computes the coefficients of the general implicit equation
* of an ellipse (A*X^2 + B*XY + C*Y^2 + D*X + E*Y + F = 0), then from the
* implicit equation we compute the parametric form.
*
*/
class make_elliptical_arc
{
public:
typedef D2<SBasis> curve_type;
 
bool isDegenerate() const
{
return are_near(initialPoint(), finalPoint());
}
/*
* constructor
*
* it doesn't execute the conversion but set the input and output parameters
*
* _ea: the output SVGEllipticalArc that will be generated;
* _curve: the input curve to be converted;
* _total_samples: the amount of sample points to be taken
* on the input curve for performing the conversion
* _tolerance: how much likelihood is required between the input curve
* and the generated elliptical arc; the smaller it is the
* the tolerance the higher it is the likelihood.
*/
make_elliptical_arc( EllipticalArc& _ea,
curve_type const& _curve,
unsigned int _total_samples,
double _tolerance );
 
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);
}
}
private:
bool bound_exceeded( unsigned int k, detail::ellipse_equation const & ee,
double e1x, double e1y, double e2 );
 
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;
}
bool check_bound(double A, double B, double C, double D, double E, double F);
 
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;
}
void fit();
 
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;
}
bool make_elliptiarc();
 
EllipticalArc* portion(double f, double t) const
void print_bound_error(unsigned int k)
{
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;
std::cerr
<< "tolerance error" << std::endl
<< "at point: " << k << std::endl
<< "error value: "<< dist_err << std::endl
<< "bound: " << dist_bound << std::endl
<< "angle error: " << angle_err
<< " (" << angle_tol << ")" << std::endl;
}
 
// the arc is the same but traversed in the opposite direction
EllipticalArc* reverse() const
public:
/*
* perform the actual conversion
* return true if the conversion is successfull, false on the contrary
*/
bool operator()()
{
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;
// initialize the reference
const NL::Vector & coeff = fitter.result();
fit();
if ( !check_bound(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]) )
return false;
if ( !(make_elliptiarc()) ) return false;
return true;
}
 
private:
 
double sweep_angle() const
/*
* you can set a boolean parameter to tell the conversion routine
* if the output elliptical arc has to be svg compliant or not;
* the default value is true
*/
bool svg_compliant_flag() 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;
return svg_compliant;
}
 
Coord from_01_to_02PI(Coord t) const
void svg_compliant_flag(bool _svg_compliant)
{
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;
}
svg_compliant = _svg_compliant;
}
 
// 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;
EllipticalArc& ea; // output elliptical arc
const curve_type & curve; // input curve
Piecewise<D2<SBasis> > dcurve; // derivative of the input curve
NL::LFMEllipse model; // model used for fitting
// perform the actual fitting task
NL::least_squeares_fitter<NL::LFMEllipse> fitter;
// tolerance: the user-defined tolerance parameter;
// tol_at_extr: the tolerance at end-points automatically computed
// on the value of "tolerance", and usually more strict;
// tol_at_center: tolerance at the center of the ellipse
// angle_tol: tolerance for the angle btw the input curve tangent
// versor and the ellipse normal versor at the sample points
double tolerance, tol_at_extr, tol_at_center, angle_tol;
Point initial_point, final_point; // initial and final end-points
unsigned int N; // total samples
unsigned int last; // N-1
double partitions; // N-1
std::vector<Point> p; // sample points
double dist_err, dist_bound, angle_err;
bool svg_compliant;
};
 
 
}
} // end namespace Geom
 
 
#endif /*_SVG_ELLIPTICAL_ARC_H_*/
 
 
#endif /* _2GEOM_SVG_ELLIPTICAL_ARC_H_ */
 
/*
Local Variables:
mode:c++
391,3 → 275,4
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
 
/trunk/Scribus/scribus/third_party/lib2geom/d2-sbasis.cpp
1,4 → 1,4
#include "d2.h"
#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. */
38,7 → 38,7
return ret;
}
 
D2<Piecewise<SBasis> > make_cuts_independant(Piecewise<D2<SBasis> > const &a) {
D2<Piecewise<SBasis> > make_cuts_independent(Piecewise<D2<SBasis> > const &a) {
D2<Piecewise<SBasis> > ret;
for(unsigned d = 0; d < 2; d++) {
for(unsigned i = 0; i < a.size(); i++)
58,8 → 58,19
return result;
}
 
Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a,
Piecewise<D2<SBasis> > const &b){
/** @brief Calculates the 'dot product' or 'inner product' of \c a and \c b
* @return \f[
* f(t) \rightarrow \left\{
* \begin{array}{c}
* a_1 \bullet b_1 \\
* a_2 \bullet b_2 \\
* \ldots \\
* a_n \bullet b_n \\
* \end{array}\right.
* \f]
* @relates Piecewise */
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);
72,6 → 83,30
return result;
}
 
/** @brief Calculates the 'dot product' or 'inner product' of \c a and \c b
* @return \f[
* f(t) \rightarrow \left\{
* \begin{array}{c}
* a_1 \bullet b \\
* a_2 \bullet b \\
* \ldots \\
* a_n \bullet b \\
* \end{array}\right.
* \f]
* @relates Piecewise */
Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Point const &b)
{
Piecewise<SBasis > result;
if (a.empty()) return result;
 
result.push_cut(a.cuts.front());
for (unsigned i = 0; i < a.size(); ++i){
result.push(dot(a.segs[i],b), a.cuts[i+1]);
}
return result;
}
 
 
Piecewise<SBasis> cross(Piecewise<D2<SBasis> > const &a,
Piecewise<D2<SBasis> > const &b){
Piecewise<SBasis > result;
86,7 → 121,7
return result;
}
 
Piecewise<D2<SBasis> > operator*(Piecewise<D2<SBasis> > const &a, Matrix const &m) {
Piecewise<D2<SBasis> > operator*(Piecewise<D2<SBasis> > const &a, Affine const &m) {
Piecewise<D2<SBasis> > result;
if(a.empty()) return result;
result.push_cut(a.cuts[0]);
97,9 → 132,8
}
 
//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){
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;
114,12 → 148,12
SBasis &cur_sb =result.segs[cur][dim];
Coord const c=pt0[dim];
if (prev_sb.empty()) {
prev_sb.push_back(Linear(0.0, c));
prev_sb = SBasis(Linear(0.0, c));
} else {
prev_sb[0][1] = c;
}
if (cur_sb.empty()) {
cur_sb.push_back(Linear(c, 0.0));
cur_sb = SBasis(Linear(c, 0.0));
} else {
cur_sb[0][0] = c;
}
129,4 → 163,164
}
return result;
}
 
std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > >
split_at_discontinuities (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwsbin, double tol)
{
using namespace Geom;
std::vector<Piecewise<D2<SBasis> > > ret;
unsigned piece_start = 0;
for (unsigned i=0; i<pwsbin.segs.size(); i++){
if (i==(pwsbin.segs.size()-1) || L2(pwsbin.segs[i].at1()- pwsbin.segs[i+1].at0()) > tol){
Piecewise<D2<SBasis> > piece;
piece.cuts.push_back(pwsbin.cuts[piece_start]);
for (unsigned j = piece_start; j<i+1; j++){
piece.segs.push_back(pwsbin.segs[j]);
piece.cuts.push_back(pwsbin.cuts[j+1]);
}
ret.push_back(piece);
piece_start = i+1;
}
}
return ret;
}
 
static void set_first_point(Piecewise<D2<SBasis> > &f, Point a){
if ( f.empty() ){
f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(Linear(a[X]),Linear(a[Y]))));
return;
}
for (unsigned dim=0; dim<2; dim++){
if (f.segs.front()[dim].size() == 0){
f.segs.front()[dim] = SBasis(Linear(a[dim],0));
}else{
f.segs.front()[dim][0][0] = a[dim];
}
}
}
static void set_last_point(Piecewise<D2<SBasis> > &f, Point a){
if ( f.empty() ){
f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(Linear(a[X]),Linear(a[Y]))));
return;
}
for (unsigned dim=0; dim<2; dim++){
if (f.segs.back()[dim].size() == 0){
f.segs.back()[dim] = SBasis(Linear(0,a[dim]));
}else{
f.segs.back()[dim][0][1] = a[dim];
}
}
}
 
std::vector<Piecewise<D2<SBasis> > > fuse_nearby_ends(std::vector<Piecewise<D2<SBasis> > > const &f, double tol){
 
if ( f.size()==0 ) return f;
std::vector<Piecewise<D2<SBasis> > > result;
std::vector<std::vector<unsigned> > pre_result;
for (unsigned i=0; i<f.size(); i++){
bool inserted = false;
Point a = f[i].firstValue();
Point b = f[i].lastValue();
for (unsigned j=0; j<pre_result.size(); j++){
Point aj = f.at(pre_result[j].back()).lastValue();
Point bj = f.at(pre_result[j].front()).firstValue();
if ( L2(a-aj) < tol ) {
pre_result[j].push_back(i);
inserted = true;
break;
}
if ( L2(b-bj) < tol ) {
pre_result[j].insert(pre_result[j].begin(),i);
inserted = true;
break;
}
}
if (!inserted) {
pre_result.push_back(std::vector<unsigned>());
pre_result.back().push_back(i);
}
}
for (unsigned i=0; i<pre_result.size(); i++){
Piecewise<D2<SBasis> > comp;
for (unsigned j=0; j<pre_result[i].size(); j++){
Piecewise<D2<SBasis> > new_comp = f.at(pre_result[i][j]);
if ( j>0 ){
set_first_point( new_comp, comp.segs.back().at1() );
}
comp.concat(new_comp);
}
if ( L2(comp.firstValue()-comp.lastValue()) < tol ){
//TODO: check sizes!!!
set_last_point( comp, comp.segs.front().at0() );
}
result.push_back(comp);
}
return result;
return f;
}
 
/*
* Computes the intersection of two sets given as (ordered) union of intervals.
*/
static std::vector<Interval> intersect( std::vector<Interval> const &a, std::vector<Interval> const &b){
std::vector<Interval> result;
//TODO: use order!
for (unsigned i=0; i < a.size(); i++){
for (unsigned j=0; j < b.size(); j++){
OptInterval c( a[i] );
c &= b[j];
if ( c ) {
result.push_back( *c );
}
}
}
return result;
}
 
std::vector<Interval> level_set( D2<SBasis> const &f, Rect region){
std::vector<Rect> regions( 1, region );
return level_sets( f, regions ).front();
}
std::vector<Interval> level_set( D2<SBasis> const &f, Point p, double tol){
Rect region(p, p);
region.expandBy( tol );
return level_set( f, region );
}
std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Rect> regions){
std::vector<Interval> regsX (regions.size(), Interval() );
std::vector<Interval> regsY (regions.size(), Interval() );
for ( unsigned i=0; i < regions.size(); i++ ){
regsX[i] = regions[i][X];
regsY[i] = regions[i][Y];
}
std::vector<std::vector<Interval> > x_in_regs = level_sets( f[X], regsX );
std::vector<std::vector<Interval> > y_in_regs = level_sets( f[Y], regsY );
std::vector<std::vector<Interval> >result(regions.size(), std::vector<Interval>() );
for (unsigned i=0; i<regions.size(); i++){
result[i] = intersect ( x_in_regs[i], y_in_regs[i] );
}
return result;
}
std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Point> pts, double tol){
std::vector<Rect> regions( pts.size(), Rect() );
for (unsigned i=0; i<pts.size(); i++){
regions[i] = Rect( pts[i], pts[i] );
regions[i].expandBy( tol );
}
return level_sets( f, regions );
}
 
 
} // 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 :
/trunk/Scribus/scribus/third_party/lib2geom/poly.h
1,5 → 1,39
#ifndef SEEN_POLY_H
#define SEEN_POLY_H
/**
* \file
* \brief \todo brief description
*
* Authors:
* ? <?@?.?>
*
* Copyright ?-? 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_POLY_H
#define LIB2GEOM_SEEN_POLY_H
#include <assert.h>
#include <vector>
#include <iostream>
7,22 → 41,24
#include <complex>
#include "utils.h"
 
namespace Geom {
 
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 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]);
}
30,7 → 66,7
result.push_back((*this)[i]);
for(unsigned i = min_size; i < p.size(); i++)