/** @file * @brief LPE knot effect implementation */ /* Authors: * Jean-Francois Barraud * * Copyright (C) 2007 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ #include "sp-shape.h" #include "display/curve.h" #include "live_effects/lpe-knot.h" #include "svg/svg.h" #include "style.h" #include <2geom/sbasis-to-bezier.h> #include <2geom/sbasis.h> #include <2geom/d2.h> #include <2geom/d2-sbasis.h> #include <2geom/piecewise.h> #include <2geom/path.h> #include <2geom/d2.h> #include <2geom/crossing.h> #include <2geom/path-intersection.h> #include <2geom/elliptical-arc.h> #include namespace Inkscape { namespace LivePathEffect { class KnotHolderEntityCrossingSwitcher : public LPEKnotHolderEntity { public: virtual ~KnotHolderEntityCrossingSwitcher() {} virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); virtual Geom::Point knot_get(); virtual void knot_click(guint state); }; //--------------------------------------------------------------------------- //LPEKnot specific Interval manipulation. //--------------------------------------------------------------------------- //remove an interval from an union of intervals. //TODO: is it worth moving it to 2Geom? static std::vector complementOf(Geom::Interval I, std::vector domain){ std::vector ret; double min = domain.front().min(); double max = domain.back().max(); Geom::Interval I1 = Geom::Interval(min,I.min()); Geom::Interval I2 = Geom::Interval(I.max(),max); for (unsigned i = 0; i I1i = intersect(domain.at(i),I1); if (I1i && !I1i->isSingular()) ret.push_back(I1i.get()); boost::optional I2i = intersect(domain.at(i),I2); if (I2i && !I2i->isSingular()) ret.push_back(I2i.get()); } return ret; } //find the time interval during which patha is hidden by pathb near a given crossing. // Warning: not accurate! static Geom::Interval findShadowedTime(Geom::Path const &patha, Geom::Path const &pathb, Geom::Crossing const &crossing, unsigned idx, double width){ using namespace Geom; double curveidx, timeoncurve = modf(crossing.getOtherTime(idx),&curveidx); if(curveidx == pathb.size() ) { curveidx--; timeoncurve = 1.;}//FIXME: 0.99999; needed? assert(curveidx >= 0 && curveidx < pathb.size()); std::vector MV = pathb[unsigned(curveidx)].pointAndDerivatives(timeoncurve,1); Point T = unit_vector(MV.at(1)); Point N = T.cw(); Point A = MV.at(0)-3*width*T, B = MV.at(0)+3*width*T; std::vector cutter; Geom::Path cutterPath(A-width*N); cutterPath.appendNew (B-width*N); cutterPath.appendNew (B+width*N); cutterPath.appendNew (A+width*N); cutterPath.close(); //cutterPath.appendNew (A-width*N); cutter.push_back(cutterPath); std::vector patha_as_vect = std::vector(1,patha); CrossingSet crossingTable = crossings (patha_as_vect, cutter); double t0 = crossing.getTime(idx); double tmin = 0,tmax = patha.size();//-0.00001;FIXME: FIXED? assert(crossingTable.size()>=1); for (unsigned c=0; ctmin and tt0) tmax = t; } return Interval(tmin,tmax); } // TODO: Fix all this in 2geom!!!! //--------------------------------------------------------------------------- // some 2Geom work around. //--------------------------------------------------------------------------- //Cubic Bezier curves might self intersect; the 2geom code used to miss them. //This is a quick work around; maybe not needed anymore? -- TODO: check! //TODO/TOCHECK/TOFIX: I think the BUG is in path-intersection.cpp -> curve_mono_split(...): //the derivative of the curve should be used instead of the curve itself. std::vector split_at_horiz_vert_tgt (std::vector const & path_in){ std::vector ret; using namespace Geom; Piecewise > f = paths_to_pw(path_in); D2 > df = make_cuts_independent(derivative(f)); std::vector xyroots = roots(df[X]); std::vector yroots = roots(df[Y]); xyroots.insert(xyroots.end(), yroots.begin(), yroots.end()); std::sort(xyroots.begin(),xyroots.end()); Piecewise > newf = partition(f,xyroots); ret = path_from_piecewise(newf,LPE_CONVERSION_TOLERANCE); return ret; } //TODO: Fix this in 2Geom; I think CrossingSets should not contain duplicates. Geom::CrossingSet crossingSet_remove_double(Geom::CrossingSet const &input){ Geom::CrossingSet result(input.size()); //Yeah, I know, there is a "unique" algorithm for that... //Note: I'm not sure the duplicates are always consecutive!! (can be first and last, I think) //Note: I also found crossings c with c.a==c.b and c.ta==c.tb . Is it normal? //Note: I also found crossings c with c.ta or c.tb not in path[a] or path[b] domain. This is definitely not normal. Geom::Crossing last; for( unsigned i=0; iGiven a point, you immediately know which strings are meeting there, // and the index of the crossing along each string. This makes it easy to check // topology change. In a CrossingSet, you have to do some search to know the index // of the crossing along "the second" string (i.e. find the symetric crossing)... // >Each point is stored only once. // However, we don't have so many crossing points in general, so none of these points might be relevant. // // -: one more clumsy data representation, and "parallelism" failures to expect... // (in particular, duplicates are hateful with this respect...) CrossingPoints::CrossingPoints(Geom::CrossingSet const &input, std::vector const &path) : std::vector() { using namespace Geom; //g_print("DBG>\nCrossing set content:\n"); for( unsigned i=0; i [%u,%u]:(%u,%u) at times (%f,%f) ----->",i,n,c.a,c.b,c.ta,c.tb); unsigned j = c.getOther(i); if (i 1)g_print("oops! -->"); if (ti > 1) ti=1; if (ti < 0) ti=0; cp.pt = path[i].pointAt(c.getTime(i)); cp.i = i; cp.j = j; cp.ni = n; Crossing c_bar = c; if (i==j){ c_bar.a = c.b; c_bar.b = c.a; c_bar.ta = c.tb; c_bar.tb = c.ta; c_bar.dir = !c.dir; } cp.nj = std::find(input[j].begin(),input[j].end(),c_bar)-input[j].begin(); cp.sign = 1; push_back(cp); //g_print("i=%u, ni=%u, j=%u, nj=%u\n",cp.i,cp.ni,cp.j,cp.nj); }/* else{ //debug purpose only: //This crossing is already registered in output. Just make sure it has a "mirror". g_print("deja trouve?"); get(i,n); bool found = false; for( unsigned ii=0; ii const &input) : std::vector() { if (input.size()>0 && input.size()%7 ==0){ using namespace Geom; for( unsigned n=0; n CrossingPoints::to_vector() { using namespace Geom; std::vector result; for( unsigned n=0; ndist_k){ result = k; dist = dist_k; } } return result; } //TODO: Find a way to warn the user when the topology changes. //TODO: be smarter at guessing the signs when the topology changed? void CrossingPoints::inherit_signs(CrossingPoints const &other, int default_value) { bool topo_changed = false; for (unsigned n=0; n