Code

Pot and Dutch translation update
[inkscape.git] / src / live_effects / lpe-knot.cpp
1 /** @file
2  * @brief LPE knot effect implementation
3  */
4 /* Authors:
5  *   Jean-Francois Barraud <jf.barraud@gmail.com>
6  *
7  * Copyright (C) 2007 Authors
8  *
9  * Released under GNU GPL, read the file 'COPYING' for more information
10  */
12 #include "sp-shape.h"
13 #include "sp-path.h"
14 #include "display/curve.h"
15 #include "live_effects/lpe-knot.h"
16 #include "svg/svg.h"
17 #include "style.h"
18 #include "knot-holder-entity.h"
20 #include <2geom/sbasis-to-bezier.h>
21 #include <2geom/sbasis.h>
22 #include <2geom/d2.h>
23 #include <2geom/d2-sbasis.h>
24 #include <2geom/path.h>
25 //#include <2geom/crossing.h>
26 #include <2geom/bezier-to-sbasis.h>
27 #include <2geom/basic-intersection.h>
28 #include <2geom/exception.h>
30 // for change crossing undo
31 #include "verbs.h"
32 #include "document.h"
34 #include <exception>
36 namespace Inkscape {
37 namespace LivePathEffect {
39 class KnotHolderEntityCrossingSwitcher : public LPEKnotHolderEntity
40 {
41 public:
42     virtual ~KnotHolderEntityCrossingSwitcher() {}
44     virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
45     virtual Geom::Point knot_get();
46     virtual void knot_click(guint state);
47 };
50 Geom::Path::size_type size_nondegenerate(Geom::Path const &path) {
51     Geom::Path::size_type retval = path.size_open();
53     // if path is closed and closing segment is not degenerate
54     if (path.closed() && !path.back_closed().isDegenerate()) {
55         retval = path.size_closed();
56     }
58     return retval;
59 }
61 //---------------------------------------------------------------------------
62 //LPEKnot specific Interval manipulation.
63 //---------------------------------------------------------------------------
65 //remove an interval from an union of intervals.
66 //TODO: is it worth moving it to 2Geom?
67 static
68 std::vector<Geom::Interval> complementOf(Geom::Interval I, std::vector<Geom::Interval> domain){
69     std::vector<Geom::Interval> ret;
70     if (!domain.empty()) {
71         double min = domain.front().min();
72         double max = domain.back().max();
73         Geom::Interval I1 = Geom::Interval(min,I.min());
74         Geom::Interval I2 = Geom::Interval(I.max(),max);
76         for (unsigned i = 0; i<domain.size(); i++){
77             boost::optional<Geom::Interval> I1i = intersect(domain.at(i),I1);
78             if (I1i && !I1i->isSingular()) ret.push_back(I1i.get());
79             boost::optional<Geom::Interval> I2i = intersect(domain.at(i),I2);
80             if (I2i && !I2i->isSingular()) ret.push_back(I2i.get());
81         }
82     }
83     return ret;
84 }
86 //find the time interval during which patha is hidden by pathb near a given crossing.
87 // Warning: not accurate!
88 static
89 Geom::Interval
90 findShadowedTime(Geom::Path const &patha, std::vector<Geom::Point> const &pt_and_dir,
91                  double const ta, double const width){
92     using namespace Geom;
93     Point T = unit_vector(pt_and_dir[1]);
94     Point N = T.cw();
95     Point A = pt_and_dir[0]-3*width*T, B = A+6*width*T;
97     Matrix mat = from_basis( T, N, pt_and_dir[0] );
98     mat = mat.inverse();
99     Path p = patha * mat;
100     
101     std::vector<double> times;
102     
103     //TODO: explore the path fwd/backward from ta (worth?)
104     for (unsigned i = 0; i < size_nondegenerate(patha); i++){
105         D2<SBasis> f = p[i].toSBasis();
106         std::vector<double> times_i, temptimes;
107         temptimes = roots(f[Y]-width);
108         times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() ); 
109         temptimes = roots(f[Y]+width);
110         times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() ); 
111         temptimes = roots(f[X]-3*width);
112         times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() ); 
113         temptimes = roots(f[X]+3*width);
114         times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() );
115         for (unsigned k=0; k<times_i.size(); k++){
116             times_i[k]+=i;
117         }
118         times.insert(times.end(), times_i.begin(), times_i.end() );
119     }
120     std::sort( times.begin(),  times.end() );
121     std::vector<double>::iterator new_end = std::unique( times.begin(),  times.end() );
122     times.resize( new_end - times.begin() );
124     double tmin = 0, tmax = size_nondegenerate(patha);
125     double period = size_nondegenerate(patha);
126     if (times.size()>0){
127         unsigned rk = upper_bound( times.begin(),  times.end(), ta ) - times.begin();
128         if ( rk < times.size() ) 
129             tmax = times[rk];
130         else if ( patha.closed() ) 
131             tmax = times[0]+period;
133         if ( rk > 0 ) 
134             tmin = times[rk-1];
135         else if ( patha.closed() ) 
136             tmin = times.back()-period;
137     }
138     return Interval(tmin,tmax);
141 //---------------------------------------------------------------------------
142 //LPEKnot specific Crossing Data manipulation.
143 //---------------------------------------------------------------------------
145 //Yet another crossing data representation.
146 // an CrossingPoint stores
147 //    -an intersection point
148 //    -the involved path components
149 //    -for each component, the time at which this crossing occurs + the order of this crossing along the component (when starting from 0).
151 namespace LPEKnotNS {//just in case...
152 CrossingPoints::CrossingPoints(std::vector<Geom::Path> const &paths) : std::vector<CrossingPoint>(){
153 //    std::cout<<"\nCrossingPoints creation from path vector\n";
154     for( unsigned i=0; i<paths.size(); i++){
155         for( unsigned ii=0; ii < size_nondegenerate(paths[i]); ii++){
156             for( unsigned j=i; j<paths.size(); j++){
157                 for( unsigned jj=(i==j?ii:0); jj < size_nondegenerate(paths[j]); jj++){
158                     std::vector<std::pair<double,double> > times;
159                     if ( i==j && ii==jj){
161 //                         std::cout<<"--(self int)\n";
162 //                         std::cout << paths[i][ii].toSBasis()[Geom::X] <<"\n";
163 //                         std::cout << paths[i][ii].toSBasis()[Geom::Y] <<"\n";
165                         find_self_intersections( times, paths[i][ii].toSBasis() );
166                     }else{
167 //                         std::cout<<"--(pair int)\n";
168 //                         std::cout << paths[i][ii].toSBasis()[Geom::X] <<"\n";
169 //                         std::cout << paths[i][ii].toSBasis()[Geom::Y] <<"\n";
170 //                         std::cout<<"with\n";
171 //                         std::cout << paths[j][jj].toSBasis()[Geom::X] <<"\n";
172 //                         std::cout << paths[j][jj].toSBasis()[Geom::Y] <<"\n";
174                         find_intersections( times, paths[i][ii].toSBasis(), paths[j][jj].toSBasis() );
175                     }
176                     for (unsigned k=0; k<times.size(); k++){
177                         //std::cout<<"intersection "<<i<<"["<<ii<<"]("<<times[k].first<<")= "<<j<<"["<<jj<<"]("<<times[k].second<<")\n";
178                         if (times[k].first == times[k].first && times[k].second == times[k].second ){//is this the way to test NaN?
179                             double zero = 1e-4;
180                             if ( i==j && fabs(times[k].first+ii - times[k].second-jj)<=zero ){//this is just end=start of successive curves in a path.
181                                 continue;
182                             }
183                             if ( i==j && ii == 0 && jj == size_nondegenerate(paths[i])-1 &&
184                                  paths[i].closed() &&
185                                  fabs(times[k].first) <= zero && 
186                                  fabs(times[k].second - 1) <= zero ){//this is just end=start of a closed path.
187                                 continue;
188                             }
189                             CrossingPoint cp;
190                             cp.pt = paths[i][ii].pointAt(times[k].first);
191                             cp.sign = 1;
192                             cp.i = i;
193                             cp.j = j;
194                             cp.ni = 0; cp.nj=0;//not set yet
195                             cp.ti = times[k].first + ii;
196                             cp.tj = times[k].second + jj;
197                             push_back(cp);
198                         }else{
199                             std::cout<<"ooops: find_(self)_intersections returned NaN:";
200                             //std::cout<<"intersection "<<i<<"["<<ii<<"](NaN)= "<<j<<"["<<jj<<"](NaN)\n";
201                         }
202                     }
203                 }
204             }
205         }
206     }
207     for( unsigned i=0; i<paths.size(); i++){
208         std::map < double, unsigned > cuts;
209         for( unsigned k=0; k<size(); k++){
210             CrossingPoint cp = (*this)[k];
211             if (cp.i == i) cuts[cp.ti] = k;
212             if (cp.j == i) cuts[cp.tj] = k;
213         }
214         unsigned count = 0;
215         for ( std::map < double, unsigned >::iterator m=cuts.begin(); m!=cuts.end(); m++ ){
216             if ( (*this)[m->second].i == i && (*this)[m->second].ti == m->first ){
217                 (*this)[m->second].ni = count;
218             }else{
219                 (*this)[m->second].nj = count;
220             }
221             count++;
222         }
223     }
226 CrossingPoints::CrossingPoints(std::vector<double> const &input) : std::vector<CrossingPoint>()
228     if (input.size()>0 && input.size()%9 ==0){
229         using namespace Geom;
230         for( unsigned n=0; n<input.size();  ){
231             CrossingPoint cp;
232             cp.pt[X] = input[n++];
233             cp.pt[Y] = input[n++];
234             cp.i = input[n++];
235             cp.j = input[n++];
236             cp.ni = input[n++];
237             cp.nj = input[n++];
238             cp.ti = input[n++];
239             cp.tj = input[n++];
240             cp.sign = input[n++];
241             push_back(cp);
242         }
243     }
246 std::vector<double>
247 CrossingPoints::to_vector()
249     using namespace Geom;
250     std::vector<double> result;
251     for( unsigned n=0; n<size(); n++){
252         CrossingPoint cp = (*this)[n];
253         result.push_back(cp.pt[X]);
254         result.push_back(cp.pt[Y]);
255         result.push_back(double(cp.i));
256         result.push_back(double(cp.j));
257         result.push_back(double(cp.ni));
258         result.push_back(double(cp.nj));
259         result.push_back(double(cp.ti));
260         result.push_back(double(cp.tj));
261         result.push_back(double(cp.sign));
262     }
263     return result;
266 //FIXME: rewrite to check success: return bool, put result in arg.
267 CrossingPoint
268 CrossingPoints::get(unsigned const i, unsigned const ni)
270     for (unsigned k=0; k<size(); k++){
271         if (
272             ((*this)[k].i==i && (*this)[k].ni==ni) ||
273             ((*this)[k].j==i && (*this)[k].nj==ni)
274             ) return (*this)[k];
275     }
276     g_warning("LPEKnotNS::CrossingPoints::get error. %uth crossing along string %u not found.",ni,i);
277     assert(false);//debug purpose...
278     return CrossingPoint();
281 unsigned
282 idx_of_nearest(CrossingPoints const &cpts, Geom::Point const &p)
284     double dist=-1;
285     unsigned result = cpts.size();
286     for (unsigned k=0; k<cpts.size(); k++){
287         double dist_k = Geom::L2(p-cpts[k].pt);
288         if (dist<0 || dist>dist_k){
289             result = k;
290             dist = dist_k;
291         }
292     }
293     return result;
296 //TODO: Find a way to warn the user when the topology changes.
297 //TODO: be smarter at guessing the signs when the topology changed?
298 void
299 CrossingPoints::inherit_signs(CrossingPoints const &other, int default_value)
301     bool topo_changed = false;
302     for (unsigned n=0; n<size(); n++){
303         if ( n<other.size() &&
304              other[n].i  == (*this)[n].i  &&
305              other[n].j  == (*this)[n].j  &&
306              other[n].ni == (*this)[n].ni &&
307              other[n].nj == (*this)[n].nj    )
308         {
309             (*this)[n].sign = other[n].sign;
310         }else{
311             topo_changed = true;
312             break;
313         }
314     }
315     if (topo_changed){
316         //TODO: Find a way to warn the user!!
317 //        std::cout<<"knot topolgy changed!\n";
318         for (unsigned n=0; n<size(); n++){
319             Geom::Point p = (*this)[n].pt;
320             unsigned idx = idx_of_nearest(other,p);
321             if (idx<other.size()){
322                 (*this)[n].sign = other[idx].sign;
323             }else{
324                 (*this)[n].sign = default_value;
325             }
326         }
327     }
332 //---------------------------------------------------------------------------
333 //---------------------------------------------------------------------------
334 //LPEKnot effect.
335 //---------------------------------------------------------------------------
336 //---------------------------------------------------------------------------
339 LPEKnot::LPEKnot(LivePathEffectObject *lpeobject) :
340     Effect(lpeobject),
341     // initialise your parameters here:
342     interruption_width(_("Fixed width:"), _("Size of hidden region of lower string"), "interruption_width", &wr, this, 3),
343     prop_to_stroke_width(_("In units of stroke width"), _("Consider 'Interruption width' as a ratio of stroke width"), "prop_to_stroke_width", &wr, this, true),
344     add_stroke_width(_("Stroke width"), _("Add the stroke width to the interruption size"), "add_stroke_width", &wr, this, true),
345     add_other_stroke_width(_("Crossing path stroke width"), _("Add crossed stroke width to the interruption size"), "add_other_stroke_width", &wr, this, true),
346     switcher_size(_("Switcher size:"), _("Orientation indicator/switcher size"), "switcher_size", &wr, this, 15),
347     crossing_points_vector(_("Crossing Signs"), _("Crossings signs"), "crossing_points_vector", &wr, this),
348     gpaths(),gstroke_widths()
350     // register all your parameters here, so Inkscape knows which parameters this effect has:
351     registerParameter( dynamic_cast<Parameter *>(&interruption_width) );
352     registerParameter( dynamic_cast<Parameter *>(&prop_to_stroke_width) );
353     registerParameter( dynamic_cast<Parameter *>(&add_stroke_width) );
354     registerParameter( dynamic_cast<Parameter *>(&add_other_stroke_width) );
355     registerParameter( dynamic_cast<Parameter *>(&switcher_size) );
356     registerParameter( dynamic_cast<Parameter *>(&crossing_points_vector) );
358     registerKnotHolderHandle(new KnotHolderEntityCrossingSwitcher(), _("Drag to select a crossing, click to flip it"));
359     crossing_points = LPEKnotNS::CrossingPoints();
360     selectedCrossing = 0;
361     switcher = Geom::Point(0,0);
364 LPEKnot::~LPEKnot()
369 void
370 LPEKnot::updateSwitcher(){
371     if (selectedCrossing < crossing_points.size()){
372         switcher = crossing_points[selectedCrossing].pt;
373         //std::cout<<"placing switcher at "<<switcher<<" \n";
374     }else if (crossing_points.size()>0){
375         selectedCrossing = 0;
376         switcher = crossing_points[selectedCrossing].pt;
377         //std::cout<<"placing switcher at "<<switcher<<" \n";
378     }else{
379         //std::cout<<"hiding switcher!\n";
380         //TODO: is there a way to properly hide the helper.
381         //switcher = Geom::Point(Geom::infinity(),Geom::infinity());
382         switcher = Geom::Point(1e10,1e10);
383     }
386 std::vector<Geom::Path>
387 LPEKnot::doEffect_path (std::vector<Geom::Path> const &path_in)
389     using namespace Geom;
390     std::vector<Geom::Path> path_out;
392     if (gpaths.size()==0){
393         return path_in;
394     }
396     for (unsigned comp=0; comp<path_in.size(); comp++){
398         //find the relevant path component in gpaths (required to allow groups!)
399         //Q: do we always recieve the group members in the same order? can we rest on that?
400         unsigned i0 = 0;
401         for (i0=0; i0<gpaths.size(); i0++){
402             if (path_in[comp]==gpaths[i0]) break;
403         }
404         if (i0 == gpaths.size() ) {THROW_EXCEPTION("lpe-knot error: group member not recognized");}// this should not happen...
406         std::vector<Interval> dom;
407         dom.push_back(Interval(0., size_nondegenerate(gpaths[i0])));
408         for (unsigned p = 0; p < crossing_points.size(); p++){
409             if (crossing_points[p].i == i0 || crossing_points[p].j == i0){
410                 unsigned i = crossing_points[p].i;
411                 unsigned j = crossing_points[p].j;
412                 double ti = crossing_points[p].ti;
413                 double tj = crossing_points[p].tj;
414                 
415                 double curveidx, t;
416                 
417                 t = modf(ti, &curveidx);
418                 if(curveidx == size_nondegenerate(gpaths[i]) ) { curveidx--; t = 1.;}
419                 assert(curveidx >= 0 && curveidx < size_nondegenerate(gpaths[i]));
420                 std::vector<Point> flag_i = gpaths[i][curveidx].pointAndDerivatives(t,1);
422                 t = modf(tj, &curveidx);
423                 if(curveidx == size_nondegenerate(gpaths[j]) ) { curveidx--; t = 1.;}
424                 assert(curveidx >= 0 && curveidx < size_nondegenerate(gpaths[j]));
425                 std::vector<Point> flag_j = gpaths[j][curveidx].pointAndDerivatives(t,1);
428                 int geom_sign = ( cross(flag_i[1],flag_j[1]) > 0 ? 1 : -1);
430                 bool i0_is_under = false;
431                 if ( crossing_points[p].sign * geom_sign > 0 ){
432                     i0_is_under = ( i == i0 );
433                 }else if ( crossing_points[p].sign * geom_sign < 0 ){
434                     if (j == i0){
435                         std::swap( i, j);
436                         std::swap(ti, tj);
437                         std::swap(flag_i,flag_j);
438                         i0_is_under = true;
439                     }
440                 }
441                 if (i0_is_under){
442                     double width = interruption_width;
443                     if ( prop_to_stroke_width.get_value() ) {
444                         width *= gstroke_widths[i];
445                     }
446                     if ( add_stroke_width.get_value() ) {
447                         width += gstroke_widths[i];
448                     }
449                     if ( add_other_stroke_width.get_value() ) {
450                         width += gstroke_widths[j];
451                     }
452                     Interval hidden = findShadowedTime(gpaths[i0], flag_j, ti, width/2);
453                     double period  = size_nondegenerate(gpaths[i0]);
454                     if (hidden.max() > period ) hidden -= period;
455                     if (hidden.min()<0){
456                         dom = complementOf( Interval(0,hidden.max()) ,dom);
457                         dom = complementOf( Interval(hidden.min()+period, period) ,dom);
458                     }else{
459                         dom = complementOf(hidden,dom);
460                     }
461                 }
462             }
463         }
465         //If the all component is hidden, continue.
466         if ( dom.size() == 0){
467             continue;
468         }
470         //If the current path is closed and the last/first point is still there, glue first and last piece.
471         unsigned beg_comp = 0, end_comp = dom.size();
472         if ( gpaths[i0].closed() && dom.front().min() == 0 && dom.back().max() ==  size_nondegenerate(gpaths[i0]) ){
473             if ( dom.size() == 1){
474                 path_out.push_back(gpaths[i0]);
475                 continue;
476             }else{
477 //                std::cout<<"fusing first and last component\n";
478                 beg_comp++;
479                 end_comp--;
480                 Path first = gpaths[i0].portion(dom.back());
481                 //FIXME: STITCH_DISCONTINUOUS should not be necessary (?!?)
482                 first.append(gpaths[i0].portion(dom.front()), Path::STITCH_DISCONTINUOUS);
483                 path_out.push_back(first);
484             }
485         }
486         for (unsigned comp = beg_comp; comp < end_comp; comp++){
487             assert(dom.at(comp).min() >=0 and dom.at(comp).max() <= size_nondegenerate(gpaths.at(i0)));
488             path_out.push_back(gpaths[i0].portion(dom.at(comp)));
489         }
490     }
491     return path_out;
496 //recursively collect gpaths and stroke widths (stolen from "sp-lpe_item.cpp").
497 void collectPathsAndWidths (SPLPEItem const *lpeitem, std::vector<Geom::Path> &paths, std::vector<double> &stroke_widths){
498     if (SP_IS_GROUP(lpeitem)) {
499         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
500         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
501             SPObject *subitem = static_cast<SPObject *>(iter->data);
502             if (SP_IS_LPE_ITEM(subitem)) {
503                 collectPathsAndWidths(SP_LPE_ITEM(subitem), paths, stroke_widths);
504             }
505         }
506     }
507     else if (SP_IS_SHAPE(lpeitem)) {
508         SPCurve * c = NULL;
509         if (SP_IS_PATH(lpeitem)) {
510             c = sp_path_get_curve_for_edit(SP_PATH(lpeitem));
511         } else {
512             c = sp_shape_get_curve(SP_SHAPE(lpeitem));
513         }
514         if (c) {
515             Geom::PathVector subpaths = c->get_pathvector();
516             for (unsigned i=0; i<subpaths.size(); i++){
517                 paths.push_back(subpaths[i]);
518                 //FIXME: do we have to be more carefull when trying to access stroke width?
519                 stroke_widths.push_back(SP_ITEM(lpeitem)->style->stroke_width.computed);
520             }
521         }
522     }
526 void
527 LPEKnot::doBeforeEffect (SPLPEItem *lpeitem)
529     using namespace Geom;
530     original_bbox(lpeitem);
532     gpaths = std::vector<Geom::Path>();
533     gstroke_widths = std::vector<double>();
534     collectPathsAndWidths(lpeitem, gpaths, gstroke_widths);
536 //     std::cout<<"\nPaths on input:\n";
537 //     for (unsigned i=0; i<gpaths.size(); i++){
538 //         for (unsigned ii=0; ii<gpaths[i].size(); ii++){
539 //             std::cout << gpaths[i][ii].toSBasis()[Geom::X] <<"\n";
540 //             std::cout << gpaths[i][ii].toSBasis()[Geom::Y] <<"\n";
541 //             std::cout<<"--\n";
542 //         }
543 //     }
544                         
545     //std::cout<<"crossing_pts_vect: "<<crossing_points_vector.param_getSVGValue()<<".\n";
546     //std::cout<<"prop_to_stroke_width: "<<prop_to_stroke_width.param_getSVGValue()<<".\n";
548     LPEKnotNS::CrossingPoints old_crdata(crossing_points_vector.data());
550 //     std::cout<<"\nVectorParam size:"<<crossing_points_vector.data().size()<<"\n";
552 //     std::cout<<"\nOld crdata ("<<old_crdata.size()<<"): \n";
553 //     for (unsigned toto=0; toto<old_crdata.size(); toto++){
554 //         std::cout<<"(";
555 //         std::cout<<old_crdata[toto].i<<",";
556 //         std::cout<<old_crdata[toto].j<<",";
557 //         std::cout<<old_crdata[toto].ni<<",";
558 //         std::cout<<old_crdata[toto].nj<<",";
559 //         std::cout<<old_crdata[toto].ti<<",";
560 //         std::cout<<old_crdata[toto].tj<<",";
561 //         std::cout<<old_crdata[toto].sign<<"),";
562 //     }
564     //if ( old_crdata.size() > 0 ) std::cout<<"first crossing sign = "<<old_crdata[0].sign<<".\n";
565     //else std::cout<<"old data is empty!!\n";
566     crossing_points = LPEKnotNS::CrossingPoints(gpaths);
567 //     std::cout<<"\nNew crdata ("<<crossing_points.size()<<"): \n";
568 //     for (unsigned toto=0; toto<crossing_points.size(); toto++){
569 //         std::cout<<"(";
570 //         std::cout<<crossing_points[toto].i<<",";
571 //         std::cout<<crossing_points[toto].j<<",";
572 //         std::cout<<crossing_points[toto].ni<<",";
573 //         std::cout<<crossing_points[toto].nj<<",";
574 //         std::cout<<crossing_points[toto].ti<<",";
575 //         std::cout<<crossing_points[toto].tj<<",";
576 //         std::cout<<crossing_points[toto].sign<<"),";
577 //     }
578     crossing_points.inherit_signs(old_crdata);
579     crossing_points_vector.param_set_and_write_new_value(crossing_points.to_vector());
580     updateSwitcher();
584 static LPEKnot *
585 get_effect(SPItem *item)
587     Effect *effect = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item));
588     if (effect->effectType() != KNOT) {
589         g_print ("Warning: Effect is not of type LPEKnot!\n");
590         return NULL;
591     }
592     return static_cast<LPEKnot *>(effect);
595 void
596 LPEKnot::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
598     using namespace Geom;
599     double r = switcher_size*.1;
600     char const * svgd;
601     //TODO: use a nice path!
602     if (selectedCrossing >= crossing_points.size()||crossing_points[selectedCrossing].sign > 0){
603         //svgd = "M -10,0 A 10 10 0 1 0 0,-10 l  5,-1 -1,2";
604         svgd = "m -7.07,7.07 c 3.9,3.91 10.24,3.91 14.14,0 3.91,-3.9 3.91,-10.24 0,-14.14 -3.9,-3.91 -10.24,-3.91 -14.14,0 l 2.83,-4.24 0.7,2.12";
605     }else if (crossing_points[selectedCrossing].sign < 0){
606         //svgd = "M  10,0 A 10 10 0 1 1 0,-10 l -5,-1  1,2";
607         svgd = "m 7.07,7.07 c -3.9,3.91 -10.24,3.91 -14.14,0 -3.91,-3.9 -3.91,-10.24 0,-14.14 3.9,-3.91 10.24,-3.91 14.14,0 l -2.83,-4.24 -0.7,2.12";
608     }else{
609         //svgd = "M 10,0 A 10 10 0 1 0 -10,0 A 10 10 0 1 0 10,0 ";
610         svgd = "M 10,0 C 10,5.52 5.52,10 0,10 -5.52,10 -10,5.52 -10,0 c 0,-5.52 4.48,-10 10,-10 5.52,0 10,4.48 10,10 z";
611     }
612     PathVector pathv = sp_svg_read_pathv(svgd);
613     pathv *= Matrix(r,0,0,r,0,0);
614     pathv+=switcher;
615     hp_vec.push_back(pathv);
618 void
619 KnotHolderEntityCrossingSwitcher::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint /*state*/)
621     LPEKnot* lpe = get_effect(item);
623     lpe->selectedCrossing = idx_of_nearest(lpe->crossing_points,p);
624     lpe->updateSwitcher();
625     // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
626     sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
629 Geom::Point
630 KnotHolderEntityCrossingSwitcher::knot_get()
632     LPEKnot* lpe = get_effect(item);
633     return snap_knot_position(lpe->switcher);
636 void
637 KnotHolderEntityCrossingSwitcher::knot_click(guint state)
639     LPEKnot* lpe = get_effect(item);
640     unsigned s = lpe->selectedCrossing;
641     if (s < lpe->crossing_points.size()){
642         if (state & GDK_SHIFT_MASK){
643             lpe->crossing_points[s].sign = 1;
644         }else{
645             int sign = lpe->crossing_points[s].sign;
646             lpe->crossing_points[s].sign = ((sign+2)%3)-1;
647             //std::cout<<"crossing set to"<<lpe->crossing_points[s].sign<<".\n";
648         }
649         lpe->crossing_points_vector.param_set_and_write_new_value(lpe->crossing_points.to_vector());
650         sp_document_done(lpe->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, /// @todo Is this the right verb?
651                  _("Change knot crossing"));
653         // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
654 //        sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
655     }
659 /* ######################## */
661 } // namespace LivePathEffect
662 } // namespace Inkscape
664 /*
665   Local Variables:
666   mode:c++
667   c-file-style:"stroustrup"
668   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
669   indent-tabs-mode:nil
670   fill-column:99
671   End:
672 */
673 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :