/** @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 "sp-path.h" #include "display/curve.h" #include "live_effects/lpe-knot.h" #include "svg/svg.h" #include "style.h" #include "knot-holder-entity.h" #include <2geom/sbasis-to-bezier.h> #include <2geom/sbasis.h> #include <2geom/d2.h> #include <2geom/d2-sbasis.h> #include <2geom/path.h> //#include <2geom/crossing.h> #include <2geom/bezier-to-sbasis.h> #include <2geom/basic-intersection.h> #include <2geom/exception.h> // for change crossing undo #include "verbs.h" #include "document.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, std::vector const &pt_and_dir, double const ta, double const width){ using namespace Geom; Point T = unit_vector(pt_and_dir[1]); Point N = T.cw(); Point A = pt_and_dir[0]-3*width*T, B = A+6*width*T; Matrix mat = from_basis( T, N, pt_and_dir[0] ); mat = mat.inverse(); Path p = patha * mat; std::vector times; //TODO: explore the path fwd/backward from ta (worth?) for (unsigned i=0; i f = p[i].toSBasis(); std::vector times_i, temptimes; temptimes = roots(f[Y]-width); times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() ); temptimes = roots(f[Y]+width); times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() ); temptimes = roots(f[X]-3*width); times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() ); temptimes = roots(f[X]+3*width); times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() ); for (unsigned k=0; k::iterator new_end = std::unique( times.begin(), times.end() ); times.resize( new_end - times.begin() ); double tmin = 0, tmax = patha.size(); double period = patha.size();//hm... Should this be patha.size()+1? if (times.size()>0){ unsigned rk = upper_bound( times.begin(), times.end(), ta ) - times.begin(); if ( rk < times.size() ) tmax = times[rk]; else if ( patha.closed() ) tmax = times[0]+period; if ( rk > 0 ) tmin = times[rk-1]; else if ( patha.closed() ) tmin = times.back()-period; } return Interval(tmin,tmax); } //--------------------------------------------------------------------------- //LPEKnot specific Crossing Data manipulation. //--------------------------------------------------------------------------- //Yet another crossing data representation. // an CrossingPoint stores // -an intersection point // -the involved path components // -for each component, the time at which this crossing occurs + the order of this crossing along the component (when starting from 0). namespace LPEKnotNS {//just in case... CrossingPoints::CrossingPoints(std::vector const &paths) : std::vector(){ // std::cout<<"\nCrossingPoints creation from path vector\n"; for( unsigned i=0; i > times; if ( i==j && ii==jj){ // std::cout<<"--(self int)\n"; // std::cout << paths[i][ii].toSBasis()[Geom::X] <<"\n"; // std::cout << paths[i][ii].toSBasis()[Geom::Y] <<"\n"; find_self_intersections( times, paths[i][ii].toSBasis() ); }else{ // std::cout<<"--(pair int)\n"; // std::cout << paths[i][ii].toSBasis()[Geom::X] <<"\n"; // std::cout << paths[i][ii].toSBasis()[Geom::Y] <<"\n"; // std::cout<<"with\n"; // std::cout << paths[j][jj].toSBasis()[Geom::X] <<"\n"; // std::cout << paths[j][jj].toSBasis()[Geom::Y] <<"\n"; find_intersections( times, paths[i][ii].toSBasis(), paths[j][jj].toSBasis() ); } for (unsigned k=0; k cuts; for( unsigned k=0; k::iterator m=cuts.begin(); m!=cuts.end(); m++ ){ if ( (*this)[m->second].i == i && (*this)[m->second].ti == m->first ){ (*this)[m->second].ni = count; }else{ (*this)[m->second].nj = count; } count++; } } } CrossingPoints::CrossingPoints(std::vector const &input) : std::vector() { if (input.size()>0 && input.size()%9 ==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