Code

9513f37b17bedf3d3b293fadd5c85cf2bee5eaa7
[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 "display/display-forward.h"
10 #include "xml/node-event-vector.h"
11 #include "sp-object.h"
12 #include "attributes.h"
13 #include "message-stack.h"
14 #include "desktop.h"
15 #include "inkscape.h"
16 #include "document.h"
17 #include <glibmm/i18n.h>
19 #include "live_effects/effect.h"
20 #include "live_effects/lpeobject.h"
21 #include "live_effects/parameter/parameter.h"
22 #include <glibmm/ustring.h>
23 #include "live_effects/n-art-bpath-2geom.h"
24 #include "display/curve.h"
25 #include <2geom/sbasis-to-bezier.h>
26 #include <gtkmm.h>
28 #include <exception>
30 // include effects:
31 #include "live_effects/lpe-skeletalstrokes.h"
32 #include "live_effects/lpe-pathalongpath.h"
33 #include "live_effects/lpe-slant.h"
34 #include "live_effects/lpe-test-doEffect-stack.h"
35 #include "live_effects/lpe-gears.h"
36 #include "live_effects/lpe-curvestitch.h"
38 #include "nodepath.h"
40 namespace Inkscape {
42 namespace LivePathEffect {
44 const Util::EnumData<EffectType> LPETypeData[INVALID_LPE] = {
45     // {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"}
46     {PATH_ALONG_PATH,       N_("Bend Path"),             "bend_path"},
47     {SKELETAL_STROKES,      N_("Pattern along path"),    "skeletal"},
48 #ifdef LPE_ENABLE_TEST_EFFECTS
49     {SLANT,                 N_("Slant"),                 "slant"},
50     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),   "doeffectstacktest"},
51 #endif
52     {GEARS,                 N_("Gears"),                 "gears"},
53     {CURVE_STITCH,          N_("Stitch subpaths"),       "curvestitching"},
54 };
55 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, INVALID_LPE);
57 Effect*
58 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
59 {
60     Effect* neweffect = NULL;
61     switch (lpenr) {
62         case SKELETAL_STROKES:
63             neweffect = (Effect*) new LPESkeletalStrokes(lpeobj);
64             break;
65         case PATH_ALONG_PATH:
66             neweffect = (Effect*) new LPEPathAlongPath(lpeobj);
67             break;
68 #ifdef LPE_ENABLE_TEST_EFFECTS
69             case SLANT:
70             neweffect = (Effect*) new LPESlant(lpeobj);
71             break;
72         case DOEFFECTSTACK_TEST:
73             neweffect = (Effect*) new LPEdoEffectStackTest(lpeobj);
74             break;
75 #endif
76         case GEARS:
77             neweffect = (Effect*) new LPEGears(lpeobj);
78             break;
79         case CURVE_STITCH:
80             neweffect = (Effect*) new LPECurveStitch(lpeobj);
81             break;
82         default:
83             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
84             neweffect = NULL;
85             break;
86     }
88     if (neweffect) {
89         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
90     }
92     return neweffect;
93 }
95 Effect::Effect(LivePathEffectObject *lpeobject)
96 {
97     vbox = NULL;
98     tooltips = NULL;
99     lpeobj = lpeobject;
100     oncanvasedit_it = param_map.begin();
103 Effect::~Effect()
105     if (tooltips) {
106         delete tooltips;
107     }
110 Glib::ustring 
111 Effect::getName()
113     if (lpeobj->effecttype_set && lpeobj->effecttype < INVALID_LPE)
114         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
115     else
116         return Glib::ustring( _("No effect") );
119 /*
120  *  Here be the doEffect function chain:
121  */
122 void
123 Effect::doEffect (SPCurve * curve)
125     NArtBpath *new_bpath = doEffect_nartbpath(SP_CURVE_BPATH(curve));
127     if (new_bpath && new_bpath != SP_CURVE_BPATH(curve)) {        // FIXME, add function to SPCurve to change bpath? or a copy function?
128         if (curve->_bpath) {
129             g_free(curve->_bpath); //delete old bpath
130         }
131         curve->_bpath = new_bpath;
132     }
135 NArtBpath *
136 Effect::doEffect_nartbpath (NArtBpath * path_in)
138     try {
139         std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);
141         std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
143         NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);
145         return new_bpath;
146     }
147     catch (std::exception & e) {
148         g_warning("Exception during LPE %s execution. \n %s", getName().c_str(), e.what());
149         SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
150             _("An exception occurred during execution of the Path Effect.") );
152         NArtBpath *path_out;
154         unsigned ret = 0;
155         while ( path_in[ret].code != NR_END ) {
156             ++ret;
157         }
158         unsigned len = ++ret;
160         path_out = g_new(NArtBpath, len);
161         memcpy(path_out, path_in, len * sizeof(NArtBpath));
162         return path_out;
163     }
166 std::vector<Geom::Path>
167 Effect::doEffect_path (std::vector<Geom::Path> & path_in)
169     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
171     for (unsigned int i=0; i < path_in.size(); i++) {
172         pwd2_in.concat( path_in[i].toPwSb() );
173     }
175     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
177     std::vector<Geom::Path> path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
179     return path_out;
182 Geom::Piecewise<Geom::D2<Geom::SBasis> >
183 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
185     g_warning("Effect has no doEffect implementation");
186     return pwd2_in;
189 void
190 Effect::readallParameters(Inkscape::XML::Node * repr)
192     param_map_type::iterator it = param_map.begin();
193     while (it != param_map.end()) {
194         const gchar * key = (*it).first.c_str();
195         const gchar * value = repr->attribute(key);
196         if(value) {
197             setParameter(key, value);
198         }
199         it++;
200     }
203 /* This function does not and SHOULD NOT write to XML */
204 void
205 Effect::setParameter(const gchar * key, const gchar * new_value)
207     Glib::ustring stringkey(key);
209     param_map_type::iterator it = param_map.find(stringkey);
210     if (it != param_map.end()) {
211         if (new_value) {
212             bool accepted = it->second->param_readSVGValue(new_value);
213             if (!accepted) { 
214                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
215             }
216         } else {
217             // set default value
218             it->second->param_set_default();
219         }
220     }
223 void
224 Effect::registerParameter(Parameter * param)
226     param_map[param->param_key] = param; // inserts or updates
229 Gtk::Widget *
230 Effect::getWidget()
232     if (!vbox) {
233         vbox = Gtk::manage( new Gtk::VBox() ); // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
234         //if (!tooltips)
235             tooltips = new Gtk::Tooltips();
237         vbox->set_border_width(5);
239         param_map_type::iterator it = param_map.begin();
240         while (it != param_map.end()) {
241             Parameter * param = it->second;
242             Gtk::Widget * widg = param->param_getWidget();
243             Glib::ustring * tip = param->param_getTooltip();
244             if (widg) {
245                vbox->pack_start(*widg, true, true, 2);
246                 if (tip != NULL) {
247                     tooltips->set_tip(*widg, *tip);
248                 }
249             }
251             it++;
252         }
253     }
255     return dynamic_cast<Gtk::Widget *>(vbox);
259 Inkscape::XML::Node *
260 Effect::getRepr()
262     return SP_OBJECT_REPR(lpeobj);
265 SPDocument *
266 Effect::getSPDoc()
268     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
269     return SP_OBJECT_DOCUMENT(lpeobj);
272 Parameter *
273 Effect::getParameter(const char * key)
275     Glib::ustring stringkey(key);
277     param_map_type::iterator it = param_map.find(stringkey);
278     if (it != param_map.end()) {
279         return it->second;
280     } else {
281         return NULL;
282     }
285 Parameter *
286 Effect::getNextOncanvasEditableParam()
288     oncanvasedit_it++;
289     if (oncanvasedit_it == param_map.end()) {
290         oncanvasedit_it = param_map.begin();
291     }
292     param_map_type::iterator old_it = oncanvasedit_it;
294     do {
295         Parameter * param = oncanvasedit_it->second;
296         if(param->oncanvas_editable) {
297             return param;
298         } else {
299             oncanvasedit_it++;
300             if (oncanvasedit_it == param_map.end()) {  // loop round the map
301                 oncanvasedit_it = param_map.begin();
302             }
303         }
304     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
306     return NULL;
309 void
310 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
312     if (!desktop) return;
314     Parameter * param = getNextOncanvasEditableParam();
315     if (param) {
316         param->param_editOncanvas(item, desktop);
317         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
318         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
319         g_free(message);
320     } else {
321         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
322                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
323     }
326 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
327 * 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!
328 */
329 void
330 Effect::resetDefaults(SPItem * /*item*/)
332     // do nothing for simple effects
335 void
336 Effect::setup_notepath(Inkscape::NodePath::Path *np)
338     np->show_helperpath = true;
339     np->helperpath_rgba = 0xff0000ff;
340     np->helperpath_width = 1.0;
344 } /* namespace LivePathEffect */
346 } /* namespace Inkscape */
348 /*
349   Local Variables:
350   mode:c++
351   c-file-style:"stroustrup"
352   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
353   indent-tabs-mode:nil
354   fill-column:99
355   End:
356 */
357 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :