X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Flive_effects%2Flpe-knot.cpp;h=b8e9b8cf9609bd6d858d9eea25a579a6d6c92e22;hb=e72ec96ad2ab870dc0af88bc559c86b32c63dd9c;hp=3e87bc51bbf03bb03d8548f9f2364e316ae314a5;hpb=dc98accfae7a38326b92d74fa4330ac8ccb5b778;p=inkscape.git diff --git a/src/live_effects/lpe-knot.cpp b/src/live_effects/lpe-knot.cpp index 3e87bc51b..b8e9b8cf9 100644 --- a/src/live_effects/lpe-knot.cpp +++ b/src/live_effects/lpe-knot.cpp @@ -1,260 +1,662 @@ -#define INKSCAPE_LPE_KNOT_CPP -/** \file - * LPE implementation - */ -/* - * Authors: - * Johan Engelen -* -* Copyright (C) Johan Engelen 2007 - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "live_effects/lpe-knot.h" -#include "display/curve.h" -#include - -// You might need to include other 2geom files. You can add them here: -#include <2geom/path.h> -#include <2geom/sbasis.h> -#include <2geom/sbasis-geometric.h> -#include <2geom/bezier-to-sbasis.h> -#include <2geom/sbasis-to-bezier.h> -#include <2geom/d2.h> -#include <2geom/sbasis-math.h> -#include <2geom/piecewise.h> -#include <2geom/crossing.h> -#include <2geom/path-intersection.h> - -namespace Inkscape { -namespace LivePathEffect { - -LPEKnot::LPEKnot(LivePathEffectObject *lpeobject) : - Effect(lpeobject), - // initialise your parameters here: - interruption_width(_("Interruption width"), _("Howmuch the lower strand is obscured by the upper."), "interruption_width", &wr, this, 1.2) -{ - // register all your parameters here, so Inkscape knows which parameters this effect has: - registerParameter( dynamic_cast(&interruption_width) ); -} - -LPEKnot::~LPEKnot() -{ - -} - - -/* ######################## - * Choose to implement one of the doEffect functions. You can delete or comment out the others. -*/ - -/* -void -LPEKnot::doEffect (SPCurve * curve) -{ - // spice this up to make the effect actually *do* something! -} - -NArtBpath * -LPEKnot::doEffect_nartbpath (NArtBpath * path_in) -{ - NArtBpath *path_out; - unsigned ret = 0; - while ( path_in[ret].code != NR_END ) { - ++ret; - } - unsigned len = ++ret; - path_out = g_new(NArtBpath, len); - - memcpy(path_out, path_in, len * sizeof(NArtBpath)); // spice this up to make the effect actually *do* something! - - return path_out; -} -*/ - -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) ret.push_back(I1i.get()); - boost::optional I2i = intersect(domain.at(i),I2); - if (I2i) ret.push_back(I2i.get()); - } - return ret; -} - -Geom::Interval -findShadowedTime(Geom::Path &patha, - Geom::Path &pathb, - Geom::Crossing crossing, - unsigned idx, double width){ - using namespace Geom; - double curveidx, timeoncurve = modf(crossing.getOtherTime(idx),&curveidx); - if(curveidx == pathb.size() && timeoncurve == 0) { curveidx--; timeoncurve = 0.99999;} - assert(curveidx >= 0 && curveidx < pathb.size()); - - std::vector MV = pathb[unsigned(curveidx)].pointAndDerivatives(timeoncurve,2); - Point T = unit_vector(MV.at(1)); - Point N = T.cw(); - Point A = MV.at(0)-10*width*T, B = MV.at(0)+10*width*T; - - std::vector cutter; - Geom::Path cutterLeft = Geom::Path(); - Geom::Path cutterRight = Geom::Path(); - cutterLeft.append (LineSegment (A-width*N, B-width*N)); - cutterRight.append(LineSegment (A+width*N, B+width*N)); - cutter.push_back(cutterLeft); - cutter.push_back(cutterRight); - - 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.0001; - assert(crossingTable.size()>=1); - for (unsigned c=0; ctmin and tt0) tmax = t; - } - //return Interval(t0-0.1,t0+0.1); - return Interval(tmin,tmax); -} - - -std::vector -LPEKnot::doEffect_path (std::vector & path_in) -{ - using namespace Geom; - std::vector path_out; - double width = interruption_width; - - CrossingSet crossingTable = crossings_among(path_in); - for (unsigned i = 0; i < crossingTable.size(); i++){ - std::vector dom; - dom.push_back(Interval(0.,path_in.at(i).size()-0.00001)); - //TODO: handle closed curves... - for (unsigned crs = 0; crs < crossingTable.at(i).size(); crs++){ - Crossing crossing = crossingTable.at(i).at(crs); - unsigned j = crossing.getOther(i); - //TODO: select dir according to a parameter... - if ((crossing.dir and crossing.a==i) or (not crossing.dir and crossing.b==i) or (i==j)){ - if (i==j and not crossing.dir) { - double temp = crossing.ta; - crossing.ta = crossing.tb; - crossing.tb = temp; - crossing.dir = not crossing.dir; - } - Interval hidden = findShadowedTime(path_in.at(i),path_in.at(j),crossing,i,width); - dom = complementOf(hidden,dom); - } - } - for (unsigned comp = 0; comp < dom.size(); comp++){ - assert(dom.at(comp).min() >=0 and dom.at(comp).max() < path_in.at(i).size()); - path_out.push_back(path_in.at(i).portion(dom.at(comp))); - } - -// std::vector MV = path_in[0][0].pointAndDerivatives(crossingTable[0][0].getTime(0),2); -// Point U = unit_vector(MV[1]); -// Point N = U.cw(); -// Point A = MV[0]-10.*width*U, B = MV[0]+10*width*U; - -// Geom::Path cutter; -// cutter = Geom::Path(); -// cutter.append( LineSegment(A+width*N,B+width*N)); -// path_out.push_back(cutter); -// cutter = Geom::Path(); -// cutter.append( LineSegment(A-width*N,B-width*N)); -// path_out.push_back(cutter); - - } - return path_out; -} - - -/* -Geom::Piecewise > -addLinearEnds (Geom::Piecewise > & m){ - using namespace Geom; - Piecewise > output; - Piecewise > start; - Piecewise > end; - double x,y,vx,vy; - - x = m.segs.front()[0].at0(); - y = m.segs.front()[1].at0(); - vx = m.segs.front()[0][1][0]+Tri(m.segs.front()[0][0]); - vy = m.segs.front()[1][1][0]+Tri(m.segs.front()[1][0]); - start = Piecewise >(D2(Linear (x-vx,x),Linear (y-vy,y))); - start.offsetDomain(m.cuts.front()-1.); - - x = m.segs.back()[0].at1(); - y = m.segs.back()[1].at1(); - vx = -m.segs.back()[0][1][1]+Tri(m.segs.back()[0][0]);; - vy = -m.segs.back()[1][1][1]+Tri(m.segs.back()[1][0]);; - end = Piecewise >(D2(Linear (x,x+vx),Linear (y,y+vy))); - //end.offsetDomain(m.cuts.back()); - - output = start; - output.concat(m); - output.concat(end); - return output; -} - -Geom::Piecewise > -LPEKnot::doEffect_pwd2 (Geom::Piecewise > & pwd2_in) -{ - using namespace Geom; - - - Piecewise > output; - Piecewise > m = addLinearEnds(pwd2_in); - - Piecewise > v = derivative(pwd2_in); - Piecewise > n = unitVector(v); - -// // -------- Pleins et delies vs courbure ou direction... -// Piecewise > a = derivative(v); -// Piecewise a_cross_n = cross(a,n); -// Piecewise v_dot_n = dot(v,n); -// //Piecewise > rfrac = sectionize(D2 >(a_cross_n,v_dot_n)); -// //Piecewise h = atan2(rfrac)*interruption_width; -// Piecewise h = reciprocal(curvature(pwd2_in))*interruption_width; -// -// // Piecewise > dir = Piecewise >(D2(Linear(0),Linear(-1))); -// // Piecewise h = dot(n,dir)+1.; -// // h *= h*(interruption_width/4.); -// -// n = rot90(n); -// output = pwd2_in+h*n; -// output.concat(pwd2_in-h*n); -// -// //----------- - - //output.concat(m); - return output; -} -*/ - -/* ######################## */ - -} //namespace LivePathEffect (setq default-directory "c:/Documents And Settings/jf/Mes Documents/InkscapeSVN") -} /* namespace Inkscape */ - -/* - 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 : +/** @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; + if (!domain.empty()) { + 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