Code

3c8ad8d507697e8936d747424132eb74229aaa26
[inkscape.git] / src / live_effects / effect.cpp
1 #define INKSCAPE_LIVEPATHEFFECT_CPP
3 /*
4  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
5  *
6  * Released under GNU GPL, read the file 'COPYING' for more information
7  */
9 #include "live_effects/effect.h"
11 #include "display/display-forward.h"
12 #include "xml/node-event-vector.h"
13 #include "sp-object.h"
14 #include "attributes.h"
15 #include "message-stack.h"
16 #include "desktop.h"
17 #include "inkscape.h"
18 #include "document.h"
19 #include <glibmm/i18n.h>
21 #include "live_effects/lpeobject.h"
22 #include "live_effects/parameter/parameter.h"
23 #include <glibmm/ustring.h>
24 #include "live_effects/n-art-bpath-2geom.h"
25 #include "display/curve.h"
26 #include <gtkmm.h>
28 #include <exception>
30 #include <2geom/sbasis-to-bezier.h>
31 #include <2geom/matrix.h>
34 // include effects:
35 #include "live_effects/lpe-patternalongpath.h"
36 #include "live_effects/lpe-bendpath.h"
37 #include "live_effects/lpe-sketch.h"
38 #include "live_effects/lpe-vonkoch.h"
39 #include "live_effects/lpe-knot.h"
40 #include "live_effects/lpe-slant.h"
41 #include "live_effects/lpe-test-doEffect-stack.h"
42 #include "live_effects/lpe-gears.h"
43 #include "live_effects/lpe-curvestitch.h"
44 #include "live_effects/lpe-circle_with_radius.h"
45 #include "live_effects/lpe-perspective_path.h"
47 #include "nodepath.h"
49 namespace Inkscape {
51 namespace LivePathEffect {
53 const Util::EnumData<EffectType> LPETypeData[INVALID_LPE] = {
54     // {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"}
55     {BEND_PATH,             N_("Bend"),                  "bend_path"},
56     {PATTERN_ALONG_PATH,    N_("Pattern Along Path"),    "skeletal"},   // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
57     {SKETCH,                N_("Sketch"),                "sketch"},
58     {VONKOCH,               N_("VonKoch"),               "vonkoch"},
59     {KNOT,                  N_("Knot"),                  "knot"},
60 #ifdef LPE_ENABLE_TEST_EFFECTS
61     {SLANT,                 N_("Slant"),                 "slant"},
62     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),   "doeffectstacktest"},
63 #endif
64     {GEARS,                 N_("Gears"),                 "gears"},
65     {CURVE_STITCH,          N_("Stitch Sub-Paths"),      "curvestitching"},
66     {CIRCLE_WITH_RADIUS,    N_("Circle (center+radius)"), "circle_with_radius"},
67     {PERSPECTIVE_PATH,      N_("Perspective path"),      "perspective_path"},
68 };
69 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, INVALID_LPE);
71 Effect*
72 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
73 {
74     Effect* neweffect = NULL;
75     switch (lpenr) {
76         case PATTERN_ALONG_PATH:
77             neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
78             break;
79         case BEND_PATH:
80             neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
81             break;
82         case SKETCH:
83             neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
84             break;
85         case VONKOCH:
86             neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
87             break;
88         case KNOT:
89             neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
90             break;
91 #ifdef LPE_ENABLE_TEST_EFFECTS
92             case SLANT:
93             neweffect = static_cast<Effect*> ( new LPESlant(lpeobj) );
94             break;
95         case DOEFFECTSTACK_TEST:
96             neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
97             break;
98 #endif
99         case GEARS:
100             neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
101             break;
102         case CURVE_STITCH:
103             neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
104             break;
105         case CIRCLE_WITH_RADIUS:
106             neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
107             break;
108         case PERSPECTIVE_PATH:
109             neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
110             break;
111         default:
112             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
113             neweffect = NULL;
114             break;
115     }
117     if (neweffect) {
118         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
119     }
121     return neweffect;
124 Effect::Effect(LivePathEffectObject *lpeobject)
125     : concatenate_before_pwd2(false)
127     lpeobj = lpeobject;
128     oncanvasedit_it = 0;
131 Effect::~Effect()
135 Glib::ustring
136 Effect::getName()
138     if (lpeobj->effecttype_set && lpeobj->effecttype < INVALID_LPE)
139         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
140     else
141         return Glib::ustring( _("No effect") );
144 void
145 Effect::doBeforeEffect (SPLPEItem *lpeitem)
147     //Do nothing for simple effects
151 /*
152  *  Here be the doEffect function chain:
153  */
154 void
155 Effect::doEffect (SPCurve * curve)
157     NArtBpath *new_bpath = doEffect_nartbpath(SP_CURVE_BPATH(curve));
159     if (new_bpath && new_bpath != SP_CURVE_BPATH(curve)) {        // FIXME, add function to SPCurve to change bpath? or a copy function?
160         if (curve->_bpath) {
161             g_free(curve->_bpath); //delete old bpath
162         }
163         curve->_bpath = new_bpath;
164     }
167 NArtBpath *
168 Effect::doEffect_nartbpath (NArtBpath * path_in)
170     try {
171         std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);
173         std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
175         NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);
177         return new_bpath;
178     }
179     catch (std::exception & e) {
180         g_warning("Exception during LPE %s execution. \n %s", getName().c_str(), e.what());
181         SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
182             _("An exception occurred during execution of the Path Effect.") );
184         NArtBpath *path_out;
186         unsigned ret = 0;
187         while ( path_in[ret].code != NR_END ) {
188             ++ret;
189         }
190         unsigned len = ++ret;
192         path_out = g_new(NArtBpath, len);
193         memcpy(path_out, path_in, len * sizeof(NArtBpath));
194         return path_out;
195     }
198 std::vector<Geom::Path>
199 Effect::doEffect_path (std::vector<Geom::Path> & path_in)
201     std::vector<Geom::Path> path_out;
203     if ( !concatenate_before_pwd2 ) {
204         // default behavior
205         for (unsigned int i=0; i < path_in.size(); i++) {
206             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
207             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
208             std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
209             // add the output path vector to the already accumulated vector:
210             for (unsigned int j=0; j < path.size(); j++) {
211                 path_out.push_back(path[j]);
212             }
213         }
214     } else {
215       // concatenate the path into possibly discontinuous pwd2
216         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
217         for (unsigned int i=0; i < path_in.size(); i++) {
218             pwd2_in.concat( path_in[i].toPwSb() );
219         }
220         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
221         path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
222     }
224     return path_out;
227 Geom::Piecewise<Geom::D2<Geom::SBasis> >
228 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
230     g_warning("Effect has no doEffect implementation");
231     return pwd2_in;
234 void
235 Effect::readallParameters(Inkscape::XML::Node * repr)
237     std::vector<Parameter *>::iterator it = param_vector.begin();
238     while (it != param_vector.end()) {
239         Parameter * param = *it;
240         const gchar * key = param->param_key.c_str();
241         const gchar * value = repr->attribute(key);
242         if (value) {
243             bool accepted = param->param_readSVGValue(value);
244             if (!accepted) { 
245                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
246             }
247         } else {
248             // set default value
249             param->param_set_default();
250         }
252         it++;
253     }
256 /* This function does not and SHOULD NOT write to XML */
257 void
258 Effect::setParameter(const gchar * key, const gchar * new_value)
260     Parameter * param = getParameter(key);
261     if (param) {
262         if (new_value) {
263             bool accepted = param->param_readSVGValue(new_value);
264             if (!accepted) { 
265                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
266             }
267         } else {
268             // set default value
269             param->param_set_default();
270         }
271     }
274 void
275 Effect::registerParameter(Parameter * param)
277     param_vector.push_back(param);
280 /**
281 * This *creates* a new widget, management of deletion should be done by the caller
282 */
283 Gtk::Widget *
284 Effect::newWidget(Gtk::Tooltips * tooltips)
286     // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
287     Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
289     vbox->set_border_width(5);
291     std::vector<Parameter *>::iterator it = param_vector.begin();
292     while (it != param_vector.end()) {
293         Parameter * param = *it;
294         Gtk::Widget * widg = param->param_newWidget(tooltips);
295         Glib::ustring * tip = param->param_getTooltip();
296         if (widg) {
297            vbox->pack_start(*widg, true, true, 2);
298             if (tip != NULL) {
299                 tooltips->set_tip(*widg, *tip);
300             }
301         }
303         it++;
304     }
306     return dynamic_cast<Gtk::Widget *>(vbox);
310 Inkscape::XML::Node *
311 Effect::getRepr()
313     return SP_OBJECT_REPR(lpeobj);
316 SPDocument *
317 Effect::getSPDoc()
319     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
320     return SP_OBJECT_DOCUMENT(lpeobj);
323 Parameter *
324 Effect::getParameter(const char * key)
326     Glib::ustring stringkey(key);
328     std::vector<Parameter *>::iterator it = param_vector.begin();
329     while (it != param_vector.end()) {
330         Parameter * param = *it;
331         if ( param->param_key == key) {
332             return param;
333         }
335         it++;
336     }
338     return NULL;
341 Parameter *
342 Effect::getNextOncanvasEditableParam()
344     oncanvasedit_it++;
345     if (oncanvasedit_it == static_cast<int>(param_vector.size())) {
346         oncanvasedit_it = 0;
347     }
348     int old_it = oncanvasedit_it;
350     do {
351         Parameter * param = param_vector[oncanvasedit_it];
352         if(param && param->oncanvas_editable) {
353             return param;
354         } else {
355             oncanvasedit_it++;
356             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
357                 oncanvasedit_it = 0;
358             }
359         }
360     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
362     return NULL;
365 void
366 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
368     if (!desktop) return;
370     Parameter * param = getNextOncanvasEditableParam();
371     if (param) {
372         param->param_editOncanvas(item, desktop);
373         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
374         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
375         g_free(message);
376     } else {
377         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
378                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
379     }
382 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
383 * The nice thing about this is that this function can use knowledge of the original path and set things accordingly for example to the size or origin of the original path!
384 */
385 void
386 Effect::resetDefaults(SPItem * /*item*/)
388     // do nothing for simple effects
391 void
392 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
394     np->show_helperpath = true;
395     np->helperpath_rgba = 0xff0000ff;
396     np->helperpath_width = 1.0;
399 void
400 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
402     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
403     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
404         Parameter * param = *it;
405         param->param_transform_multiply(postmul, set);
406     }
409 } /* namespace LivePathEffect */
411 } /* namespace Inkscape */
413 /*
414   Local Variables:
415   mode:c++
416   c-file-style:"stroustrup"
417   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
418   indent-tabs-mode:nil
419   fill-column:99
420   End:
421 */
422 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :