From: johanengelen Date: Mon, 12 Nov 2007 19:47:07 +0000 (+0000) Subject: update to 2geom rev. 1168 X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=e6bdf746e2d9e775704a475a29cc1bb167ec271c;p=inkscape.git update to 2geom rev. 1168 --- diff --git a/src/2geom/basic-intersection.cpp b/src/2geom/basic-intersection.cpp index 694760d5a..0d84ee7f3 100644 --- a/src/2geom/basic-intersection.cpp +++ b/src/2geom/basic-intersection.cpp @@ -1,5 +1,7 @@ #include "basic-intersection.h" +unsigned intersect_steps = 0; + using std::vector; namespace Geom { @@ -224,6 +226,7 @@ void recursively_intersect( OldBezier a, double t0, double t1, int deptha, OldBezier b, double u0, double u1, int depthb, std::vector > ¶meters) { + intersect_steps ++; if( deptha > 0 ) { OldBezier A[2]; diff --git a/src/2geom/bezier.h b/src/2geom/bezier.h index e0d274e3b..16b4d7446 100644 --- a/src/2geom/bezier.h +++ b/src/2geom/bezier.h @@ -1,4 +1,4 @@ - /* +/* * bezier.h * * Copyright 2007 MenTaLguY diff --git a/src/2geom/concepts.h b/src/2geom/concepts.h index bd020ab66..92ec66c7f 100644 --- a/src/2geom/concepts.h +++ b/src/2geom/concepts.h @@ -104,7 +104,7 @@ struct NearConcept { double tol; bool res; void constraints() { - res = near(a, b, tol); + res = are_near(a, b, tol); } }; diff --git a/src/2geom/coord.h b/src/2geom/coord.h index 34a585bf9..e9abfc927 100644 --- a/src/2geom/coord.h +++ b/src/2geom/coord.h @@ -46,19 +46,8 @@ typedef double Coord; const Coord EPSILON = 1e-5; //1e-18; - -#ifdef near -#define lib2geom_near_save near -#undef near -#endif - //IMPL: NearConcept -inline bool near(Coord a, Coord b, double eps=EPSILON) { return fabs(a-b) <= eps; } - -#ifdef lib2geom_near_save -#define near lib2geom_near_save -#undef lib2geom_near_save -#endif +inline bool are_near(Coord a, Coord b, double eps=EPSILON) { return fabs(a-b) <= eps; } } /* namespace Geom */ diff --git a/src/2geom/crossing.cpp b/src/2geom/crossing.cpp index 8f29635e8..a8add1877 100644 --- a/src/2geom/crossing.cpp +++ b/src/2geom/crossing.cpp @@ -1,7 +1,118 @@ #include "crossing.h" +#include "path.h" namespace Geom { +//bool edge_involved_in(Edge const &e, Crossing const &c) { +// if(e.path == c.a) { +// if(e.time == c.ta) return true; +// } else if(e.path == c.b) { +// if(e.time == c.tb) return true; +// } +// return false; +//} + +double wrap_dist(double from, double to, double size, bool rev) { + if(rev) { + if(to > from) { + return from + (size - to); + } else { + return from - to; + } + } else { + if(to < from) { + return to + (size - from); + } else { + return to - from; + } + } +} + +CrossingGraph create_crossing_graph(std::vector const &p, Crossings const &crs) { + std::vector locs; + CrossingGraph ret; + for(unsigned i = 0; i < crs.size(); i++) { + Point pnt = p[crs[i].a].pointAt(crs[i].ta); + unsigned j = 0; + for(; j < locs.size(); j++) { + if(are_near(pnt, locs[j])) break; + } + if(j == locs.size()) { + ret.push_back(CrossingNode()); + locs.push_back(pnt); + } + ret[j].add_edge(Edge(crs[i].a, crs[i].ta, false)); + ret[j].add_edge(Edge(crs[i].a, crs[i].ta, true)); + ret[j].add_edge(Edge(crs[i].b, crs[i].tb, false)); + ret[j].add_edge(Edge(crs[i].b, crs[i].tb, true)); + } + + for(unsigned i = 0; i < ret.size(); i++) { + for(unsigned j = 0; j < ret[i].edges.size(); j++) { + unsigned pth = ret[i].edges[j].path; + double t = ret[i].edges[j].time; + bool rev = ret[i].edges[j].reverse; + double size = p[pth].size()+1; + double best = size; + unsigned bix = ret.size(); + for(unsigned k = 0; k < ret.size(); k++) { + for(unsigned l = 0; l < ret[k].edges.size(); l++) { + if(ret[i].edges[j].path == ret[k].edges[l].path && (k != i || l != j)) { + double d = wrap_dist(t, ret[i].edges[j].time, size, rev); + if(d < best) { + best = d; + bix = k; + } + } + } + } + if(bix == ret.size()) { + std::cout << "couldn't find an adequate next-crossing node"; + bix = i; + } + ret[i].edges[j].node = bix; + } + } + + return ret; + + /* Various incoherent code bits + // list of sets of edges, each set corresponding to those emanating from the path + CrossingGraph ret; + std::vector edges(crs.size()); + + std::vector > used; + unsigned i, j; + do { + first_false(used, i, j); + CrossingNode cn; + do { + unsigned di = i, dj = j; + crossing_dual(di, dj); + if(!used[di,dj]) { + + } + } + + } while(!used[i,j]) + + + for(unsigned j = 0; j < crs[i].size(); j++) { + + edges.push_back(Edge(i, crs[i][j].getOtherTime(i), false)); + edges.push_back(Edge(i, crs[i][j].getOtherTime(i), true)); + } + std::sort(edges.begin(), edges.end(), TimeOrder()); + for(unsigned j = 0; j < edges.size(); ) { + CrossingNode cn; + double t = edges[j].time; + while(j < edges.size() && are_near(edges[j].time, t)) { + cn.edges.push_back(edges[j]); + } + } +*/ +} + void merge_crossings(Crossings &a, Crossings &b, unsigned i) { Crossings n; sort_crossings(b, i); @@ -67,7 +178,7 @@ void clean(Crossings &cr_a, Crossings &cr_b) { const Crossing cur = *i; Eraser next(i); next++; - if(near(cur, *next)) { + if(are_near(cur, *next)) { cr_b.erase(std::find(cr_b.begin(), cr_b.end(), cur)); for(i = next; near(*i, cur); i++) { cr_b.erase(std::find(cr_b.begin(), cr_b.end(), *i)); diff --git a/src/2geom/crossing.h b/src/2geom/crossing.h index 79912f024..8e3a45142 100644 --- a/src/2geom/crossing.h +++ b/src/2geom/crossing.h @@ -2,11 +2,12 @@ #define __GEOM_CROSSING_H #include -#include #include "rect.h" #include "sweep.h" + namespace Geom { +//Crossing between one or two paths struct Crossing { bool dir; //True: along a, a becomes outside. double ta, tb; //time on a and b of crossing @@ -16,6 +17,7 @@ struct Crossing { Crossing(double t_a, double t_b, unsigned ai, unsigned bi, bool direction) : dir(direction), ta(t_a), tb(t_b), a(ai), b(bi) {} bool operator==(const Crossing & other) const { return a == other.a && b == other.b && dir == other.dir && ta == other.ta && tb == other.tb; } bool operator!=(const Crossing & other) const { return !(*this == other); } + unsigned getOther(unsigned cur) const { return a == cur ? b : a; } double getTime(unsigned cur) const { return a == cur ? ta : tb; } double getOtherTime(unsigned cur) const { return a == cur ? tb : ta; } @@ -23,11 +25,48 @@ struct Crossing { }; -/*inline bool near(Crossing a, Crossing b) { - return near(a.ta, b.ta) && near(a.tb, b.tb); +struct Edge { + unsigned node, path; + double time; + bool reverse; + Edge(unsigned p, double t, bool r) : path(p), time(t), reverse(r) {} + bool operator==(Edge const &other) const { return other.path == path && other.time == time && other.reverse == reverse; } +}; + +struct CrossingNode { + std::vector edges; + CrossingNode() : edges(std::vector()) {} + explicit CrossingNode(std::vector es) : edges(es) {} + void add_edge(Edge const &e) { + if(std::find(edges.begin(), edges.end(), e) == edges.end()) + edges.push_back(e); + } + double time_on(unsigned p) { + for(unsigned i = 0; i < edges.size(); i++) + if(edges[i].path == p) return edges[i].time; + std::cout << "CrossingNode time_on failed\n"; + return 0; + } +}; + +typedef std::vector Crossings; + +typedef std::vector CrossingGraph; + +struct TimeOrder { + bool operator()(Edge a, Edge b) { + return a.time < b.time; + } +}; + +class Path; +CrossingGraph create_crossing_graph(std::vector const &p, Crossings const &crs); + +/*inline bool are_near(Crossing a, Crossing b) { + return are_near(a.ta, b.ta) && are_near(a.tb, b.tb); } -struct NearF { bool operator()(Crossing a, Crossing b) { return near(a, b); } }; +struct NearF { bool operator()(Crossing a, Crossing b) { return are_near(a, b); } }; */ struct CrossingOrder { @@ -39,7 +78,7 @@ struct CrossingOrder { } }; -typedef std::vector Crossings; + typedef std::vector CrossingSet; template diff --git a/src/2geom/d2.h b/src/2geom/d2.h index 3a9e14bda..47355eb60 100644 --- a/src/2geom/d2.h +++ b/src/2geom/d2.h @@ -130,9 +130,9 @@ operator!=(D2 const &a, D2 const &b) { //IMPL: NearConcept template inline bool -near(D2 const &a, D2 const &b, double tol) { +are_near(D2 const &a, D2 const &b, double tol) { boost::function_requires >(); - return near(a[0], b[0]) && near(a[1], b[1]); + return are_near(a[0], b[0]) && are_near(a[1], b[1]); } //IMPL: AddableConcept diff --git a/src/2geom/geom.cpp b/src/2geom/geom.cpp index e93247899..8845cefa8 100644 --- a/src/2geom/geom.cpp +++ b/src/2geom/geom.cpp @@ -10,6 +10,7 @@ #include "point.h" namespace Geom { + /** * Finds the intersection of the two (infinite) lines * defined by the points p such that dot(n0, p) == d0 and dot(n1, p) == d1. diff --git a/src/2geom/geom.h b/src/2geom/geom.h index c24acea63..f25e43c49 100644 --- a/src/2geom/geom.h +++ b/src/2geom/geom.h @@ -31,8 +31,8 @@ * the specific language governing rights and limitations. * */ -#ifndef SEEN_2Geom_GEOM_H -#define SEEN_2Geom_GEOM_H +#ifndef LIB2GEOM_SEEN_GEOM_H +#define LIB2GEOM_SEEN_GEOM_H //TODO: move somewhere else diff --git a/src/2geom/matrix.cpp b/src/2geom/matrix.cpp index c0e64ad33..a6e3025d9 100644 --- a/src/2geom/matrix.cpp +++ b/src/2geom/matrix.cpp @@ -84,7 +84,7 @@ double Matrix::expansionY() const { void Matrix::setExpansionX(double val) { double exp_x = expansionX(); - if(!near(exp_x, 0.0)) { //TODO: best way to deal with it is to skip op? + 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; } @@ -92,7 +92,7 @@ void Matrix::setExpansionX(double val) { void Matrix::setExpansionY(double val) { double exp_y = expansionY(); - if(!near(exp_y, 0.0)) { //TODO: best way to deal with it is to skip op? + 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; } @@ -108,9 +108,9 @@ void Matrix::setIdentity() { //TODO: use eps bool Matrix::isIdentity(Coord const eps) const { - return near(_c[0], 1.0) && near(_c[1], 0.0) && - near(_c[2], 0.0) && near(_c[3], 1.0) && - near(_c[4], 0.0) && near(_c[5], 0.0); + return are_near(_c[0], 1.0) && are_near(_c[1], 0.0) && + are_near(_c[2], 0.0) && are_near(_c[3], 1.0) && + are_near(_c[4], 0.0) && are_near(_c[5], 0.0); } /** Answers the question "Does this matrix perform a translation, and \em{only} a translation?" @@ -118,9 +118,9 @@ bool Matrix::isIdentity(Coord const eps) const { \return A bool representing yes/no. */ bool Matrix::isTranslation(Coord const eps) const { - return near(_c[0], 1.0) && near(_c[1], 0.0) && - near(_c[2], 0.0) && near(_c[3], 1.0) && - !near(_c[4], 0.0) && !near(_c[5], 0.0); + return are_near(_c[0], 1.0) && are_near(_c[1], 0.0) && + are_near(_c[2], 0.0) && are_near(_c[3], 1.0) && + !are_near(_c[4], 0.0) && !are_near(_c[5], 0.0); } /** Answers the question "Does this matrix perform a scale, and \em{only} a Scale?" @@ -128,9 +128,9 @@ bool Matrix::isTranslation(Coord const eps) const { \return A bool representing yes/no. */ bool Matrix::isScale(Coord const eps) const { - return !near(_c[0], 1.0) || !near(_c[3], 1.0) && //NOTE: these are the diags, and the next line opposite diags - near(_c[1], 0.0) && near(_c[2], 0.0) && - near(_c[4], 0.0) && near(_c[5], 0.0); + return !are_near(_c[0], 1.0) || !are_near(_c[3], 1.0) && //NOTE: these are the diags, and the next line opposite diags + are_near(_c[1], 0.0) && are_near(_c[2], 0.0) && + are_near(_c[4], 0.0) && are_near(_c[5], 0.0); } /** Answers the question "Does this matrix perform a uniform scale, and \em{only} a uniform scale?" @@ -138,9 +138,9 @@ bool Matrix::isScale(Coord const eps) const { \return A bool representing yes/no. */ bool Matrix::isUniformScale(Coord const eps) const { - return !near(_c[0], 1.0) && near(_c[0], _c[3]) && - near(_c[1], 0.0) && near(_c[2], 0.0) && - near(_c[4], 0.0) && near(_c[5], 0.0); + return !are_near(_c[0], 1.0) && are_near(_c[0], _c[3]) && + are_near(_c[1], 0.0) && are_near(_c[2], 0.0) && + are_near(_c[4], 0.0) && are_near(_c[5], 0.0); } /** Answers the question "Does this matrix perform a rotation, and \em{only} a rotation?" @@ -148,13 +148,13 @@ bool Matrix::isUniformScale(Coord const eps) const { \return A bool representing yes/no. */ bool Matrix::isRotation(Coord const eps) const { - return !near(_c[0], _c[3]) && near(_c[1], -_c[2]) && - near(_c[4], 0.0) && near(_c[5], 0.0) && - near(_c[0]*_c[0] + _c[1]*_c[1], 1.0); + return !are_near(_c[0], _c[3]) && are_near(_c[1], -_c[2]) && + are_near(_c[4], 0.0) && are_near(_c[5], 0.0) && + are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0); } bool Matrix::onlyScaleAndTranslation(Coord const eps) const { - return near(_c[0], _c[3]) && near(_c[1], 0) && near(_c[2], 0); + return are_near(_c[0], _c[3]) && are_near(_c[1], 0) && are_near(_c[2], 0); } bool Matrix::flips() const { @@ -175,7 +175,7 @@ Matrix Matrix::inverse() const { Matrix d; Geom::Coord const determ = det(); - if (!near(determ, 0.0)) { + if (!are_near(determ, 0.0)) { Geom::Coord const ideterm = 1.0 / determ; d._c[0] = _c[3] * ideterm; diff --git a/src/2geom/path-intersection.cpp b/src/2geom/path-intersection.cpp index 2ad78e42f..9a4d65f72 100644 --- a/src/2geom/path-intersection.cpp +++ b/src/2geom/path-intersection.cpp @@ -25,7 +25,7 @@ int winding(Path const &path, Point p) { if(iter->boundsFast().height()!=0.){ start = iter; break; } } int wind = 0; - int cnt = 0; + unsigned cnt = 0; bool starting = true; for (Path::const_iterator iter = start; iter != start || starting ; ++iter, iter = (iter == path.end_closed()) ? path.begin() : iter ) diff --git a/src/2geom/path.cpp b/src/2geom/path.cpp index 79dc0a5f4..98fec6e24 100644 --- a/src/2geom/path.cpp +++ b/src/2geom/path.cpp @@ -144,7 +144,7 @@ void Path::appendPortionTo(Path &ret, double from, double to) const { const double eps = .1; void Path::append(Curve const &curve) { - if ( curves_.front() != final_ && !near(curve.initialPoint(), (*final_)[0], eps) ) { + if ( curves_.front() != final_ && !are_near(curve.initialPoint(), (*final_)[0], eps) ) { throw ContinuityError(); } do_append(curve.duplicate()); @@ -153,7 +153,7 @@ void Path::append(Curve const &curve) { void Path::append(D2 const &curve) { if ( curves_.front() != final_ ) { for ( int i = 0 ; i < 2 ; ++i ) { - if ( !near(curve[i][0][0], (*final_)[0][i], eps) ) { + if ( !are_near(curve[i][0][0], (*final_)[0][i], eps) ) { throw ContinuityError(); } } @@ -205,17 +205,17 @@ void Path::check_continuity(Sequence::iterator first_replaced, { if ( first != last ) { if ( first_replaced != curves_.begin() ) { - if ( !near( (*first_replaced)->initialPoint(), (*first)->initialPoint(), eps ) ) { + if ( !are_near( (*first_replaced)->initialPoint(), (*first)->initialPoint(), eps ) ) { throw ContinuityError(); } } if ( last_replaced != (curves_.end()-1) ) { - if ( !near( (*(last_replaced-1))->finalPoint(), (*(last-1))->finalPoint(), eps ) ) { + if ( !are_near( (*(last_replaced-1))->finalPoint(), (*(last-1))->finalPoint(), eps ) ) { throw ContinuityError(); } } } else if ( first_replaced != last_replaced && first_replaced != curves_.begin() && last_replaced != curves_.end()-1) { - if ( !near((*first_replaced)->initialPoint(), (*(last_replaced-1))->finalPoint(), eps ) ) { + if ( !are_near((*first_replaced)->initialPoint(), (*(last_replaced-1))->finalPoint(), eps ) ) { throw ContinuityError(); } } diff --git a/src/2geom/point.h b/src/2geom/point.h index bf7dc444f..91df159ca 100644 --- a/src/2geom/point.h +++ b/src/2geom/point.h @@ -174,22 +174,11 @@ extern double atan2(Point const p); /** compute the angle turning from a to b (signed). */ extern double angle_between(Point const a, Point const b); - -#ifdef near -#define lib2geom_near_save near -#undef near -#endif - //IMPL: NearConcept -inline bool near(Point const &a, Point const &b, double const eps=EPSILON) { - return ( near(a[X],b[X],eps) && near(a[Y],b[Y],eps) ); +inline bool are_near(Point const &a, Point const &b, double const eps=EPSILON) { + return ( are_near(a[X],b[X],eps) && are_near(a[Y],b[Y],eps) ); } -#ifdef lib2geom_near_save -#define near lib2geom_near_save -#undef lib2geom_near_save -#endif - /** Returns p * Geom::rotate_degrees(90), but more efficient. * * Angle direction in Inkscape code: If you use the traditional mathematics convention that y diff --git a/src/2geom/sbasis-to-bezier.cpp b/src/2geom/sbasis-to-bezier.cpp index 2484af18d..7283dd87d 100644 --- a/src/2geom/sbasis-to-bezier.cpp +++ b/src/2geom/sbasis-to-bezier.cpp @@ -54,6 +54,28 @@ sbasis_to_bezier(SBasis const &B, unsigned q) { return result; } +double mopi(int i) { + return (i&1)?-1:1; +} + +// this produces a degree k sbasis from a degree 2q bezier +SBasis +bezier_to_sbasis(Bezier const &B) { + unsigned n = B.size(); + unsigned q = (n+1)/2; + SBasis result; + result.resize(q+1); + for(unsigned k = 0; k < q; k++) { + result[k][0] = result[k][1] = 0; + for(unsigned j = 0; j <= n-k; j++) { + result[k][0] += mopi(int(j)-int(k))*W(n, j, k)*B[j]; + result[k][1] += mopi(int(j)-int(k))*W(n, j, k)*B[j]; + //W(n, n-j, k)*B[k][1]); + } + } + return result; +} + // this produces a 2q point bezier from a degree q sbasis std::vector sbasis_to_bezier(D2 const &B, unsigned qq) { @@ -188,9 +210,9 @@ path_from_piecewise(Geom::Piecewise > const &B, double to Geom::Point start = B[0].at0(); pb.moveTo(start); for(unsigned i = 0; ; i++) { - if(i+1 == B.size() || !near(B[i+1].at0(), B[i].at1(), tol)) { + if(i+1 == B.size() || !are_near(B[i+1].at0(), B[i].at1(), tol)) { //start of a new path - if(near(start, B[i].at1())) { + if(are_near(start, B[i].at1())) { //it's closed pb.closePath(); if(sbasis_size(B[i]) <= 1) { diff --git a/src/2geom/sbasis-to-bezier.h b/src/2geom/sbasis-to-bezier.h index e566d7156..2c49464f6 100644 --- a/src/2geom/sbasis-to-bezier.h +++ b/src/2geom/sbasis-to-bezier.h @@ -9,6 +9,10 @@ namespace Geom{ Bezier sbasis_to_bezier(SBasis const &B, unsigned q = 0); +// inverse +SBasis bezier_to_sbasis(Bezier const &B); + + std::vector sbasis_to_bezier(D2 const &B, unsigned q = 0); diff --git a/src/2geom/sbasis.h b/src/2geom/sbasis.h index 118cf2c56..9fc16f1c6 100644 --- a/src/2geom/sbasis.h +++ b/src/2geom/sbasis.h @@ -86,6 +86,20 @@ public: } return (1-t)*p0 + t*p1; } + double valueAndDerivative(double t, double &der) const { + double s = t*(1-t); + double p0 = 0, p1 = 0; + double sk = 1; +//TODO: rewrite as horner + for(unsigned k = 0; k < size(); k++) { + p0 += sk*(*this)[k][0]; + p1 += sk*(*this)[k][1]; + sk *= s; + } + // p0 and p1 at this point form a linear approximation at t + der = p1 - p0; + return (1-t)*p0 + t*p1; + } double operator()(double t) const { return valueAt(t); } diff --git a/src/2geom/shape.cpp b/src/2geom/shape.cpp index 54218d4d9..3cf6d0bc0 100644 --- a/src/2geom/shape.cpp +++ b/src/2geom/shape.cpp @@ -296,7 +296,7 @@ unsigned pick_coincident(unsigned ix, unsigned jx, bool &rev, std::vector for(unsigned k = jx; k < crs[ix].size(); k++) { unsigned koix = crs[ix][k].getOther(ix); if(koix == oix) { - if(!near(otime, crs[ix][k].getTime(oix))) break; + if(!are_near(otime, crs[ix][k].getTime(oix))) break; for(unsigned dir = 0; dir < 2; dir++) { Point val = ps[ix].pointAt(fudgerize(crs[ix][k].getTime(ix), dir)) - cross_point; Cmp to_prev = cmp(cross(val, prev), 0); @@ -470,7 +470,7 @@ unsigned pick_coincident(unsigned ix, unsigned jx, bool pref, bool &rev, std::ve for(unsigned k = jx; k < crs[ix].size(); k++) { unsigned koix = crs[ix][k].getOther(ix); if(koix == oix) { - if(!near(otime, crs[ix][k].getTime(oix))) break; + if(!are_near(otime, crs[ix][k].getTime(oix))) break; for(unsigned dir = 0; dir < 2; dir++) { Point val = ps[ix].pointAt(crs[ix][k].getTime(ix) + (dir ? -0.01 : 0.01)) - cross_point; Cmp to_prev = cmp(cross(val, prev), 0); diff --git a/src/2geom/sturm.h b/src/2geom/sturm.h index 4a9fcc6e2..9937a6f15 100644 --- a/src/2geom/sturm.h +++ b/src/2geom/sturm.h @@ -1,6 +1,11 @@ +#ifndef LIB2GEOM_STURM_HEADER +#define LIB2GEOM_STURM_HEADER + #include "poly.h" #include "utils.h" +namespace Geom { + class sturm : public std::vector{ public: sturm(Poly const &X) { @@ -50,6 +55,9 @@ public: } }; +} //namespace Geom + +#endif /* Local Variables: mode:c++ diff --git a/src/2geom/utils.h b/src/2geom/utils.h index a96a34dff..50dfa82fe 100644 --- a/src/2geom/utils.h +++ b/src/2geom/utils.h @@ -1,5 +1,5 @@ -#ifndef UTILS_HEADER -#define UTILS_HEADER +#ifndef LIB2GEOM_UTILS_HEADER +#define LIB2GEOM_UTILS_HEADER /** Various utility functions. * @@ -40,6 +40,11 @@ public: NotImplemented() : std::logic_error("method not implemented") {} }; +class NotInvertible : public std::range_error { + public: + NotInvertible() : std::range_error("function does not have a unique inverse") {} +}; + // proper logical xor inline bool logical_xor (bool a, bool b) { return (a || b) && !(a && b); }