Code

543d60fc043220c46947c7f3ad29c7dfe46d2b1d
[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-skeletalstrokes.h"
36 #include "live_effects/lpe-pathalongpath.h"
37 #include "live_effects/lpe-slant.h"
38 #include "live_effects/lpe-test-doEffect-stack.h"
39 #include "live_effects/lpe-gears.h"
40 #include "live_effects/lpe-curvestitch.h"
42 #include "nodepath.h"
44 namespace Inkscape {
46 namespace LivePathEffect {
48 const Util::EnumData<EffectType> LPETypeData[INVALID_LPE] = {
49     // {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"}
50     {PATH_ALONG_PATH,       N_("Bend Path"),             "bend_path"},
51     {SKELETAL_STROKES,      N_("Pattern Along Path"),    "skeletal"},
52 #ifdef LPE_ENABLE_TEST_EFFECTS
53     {SLANT,                 N_("Slant"),                 "slant"},
54     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),   "doeffectstacktest"},
55 #endif
56     {GEARS,                 N_("Gears"),                 "gears"},
57     {CURVE_STITCH,          N_("Stitch Sub-Paths"),       "curvestitching"},
58 };
59 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, INVALID_LPE);
61 Effect*
62 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
63 {
64     Effect* neweffect = NULL;
65     switch (lpenr) {
66         case SKELETAL_STROKES:
67             neweffect = (Effect*) new LPESkeletalStrokes(lpeobj);
68             break;
69         case PATH_ALONG_PATH:
70             neweffect = (Effect*) new LPEPathAlongPath(lpeobj);
71             break;
72 #ifdef LPE_ENABLE_TEST_EFFECTS
73             case SLANT:
74             neweffect = (Effect*) new LPESlant(lpeobj);
75             break;
76         case DOEFFECTSTACK_TEST:
77             neweffect = (Effect*) new LPEdoEffectStackTest(lpeobj);
78             break;
79 #endif
80         case GEARS:
81             neweffect = (Effect*) new LPEGears(lpeobj);
82             break;
83         case CURVE_STITCH:
84             neweffect = (Effect*) new LPECurveStitch(lpeobj);
85             break;
86         default:
87             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
88             neweffect = NULL;
89             break;
90     }
92     if (neweffect) {
93         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
94     }
96     return neweffect;
97 }
99 Effect::Effect(LivePathEffectObject *lpeobject)
101     vbox = NULL;
102     tooltips = NULL;
103     lpeobj = lpeobject;
104     oncanvasedit_it = 0;
107 Effect::~Effect()
109     if (tooltips) {
110         delete tooltips;
111     }
114 Glib::ustring
115 Effect::getName()
117     if (lpeobj->effecttype_set && lpeobj->effecttype < INVALID_LPE)
118         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
119     else
120         return Glib::ustring( _("No effect") );
123 /*
124  *  Here be the doEffect function chain:
125  */
126 void
127 Effect::doEffect (SPCurve * curve)
129     NArtBpath *new_bpath = doEffect_nartbpath(SP_CURVE_BPATH(curve));
131     if (new_bpath && new_bpath != SP_CURVE_BPATH(curve)) {        // FIXME, add function to SPCurve to change bpath? or a copy function?
132         if (curve->_bpath) {
133             g_free(curve->_bpath); //delete old bpath
134         }
135         curve->_bpath = new_bpath;
136     }
139 NArtBpath *
140 Effect::doEffect_nartbpath (NArtBpath * path_in)
142     try {
143         std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);
145         std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
147         NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);
149         return new_bpath;
150     }
151     catch (std::exception & e) {
152         g_warning("Exception during LPE %s execution. \n %s", getName().c_str(), e.what());
153         SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
154             _("An exception occurred during execution of the Path Effect.") );
156         NArtBpath *path_out;
158         unsigned ret = 0;
159         while ( path_in[ret].code != NR_END ) {
160             ++ret;
161         }
162         unsigned len = ++ret;
164         path_out = g_new(NArtBpath, len);
165         memcpy(path_out, path_in, len * sizeof(NArtBpath));
166         return path_out;
167     }
170 std::vector<Geom::Path>
171 Effect::doEffect_path (std::vector<Geom::Path> & path_in)
173     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
175     for (unsigned int i=0; i < path_in.size(); i++) {
176         pwd2_in.concat( path_in[i].toPwSb() );
177     }
179     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
181     std::vector<Geom::Path> path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
183     return path_out;
186 Geom::Piecewise<Geom::D2<Geom::SBasis> >
187 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
189     g_warning("Effect has no doEffect implementation");
190     return pwd2_in;
193 void
194 Effect::readallParameters(Inkscape::XML::Node * repr)
196     std::vector<Parameter *>::iterator it = param_vector.begin();
197     while (it != param_vector.end()) {
198         Parameter * param = *it;
199         const gchar * key = param->param_key.c_str();
200         const gchar * value = repr->attribute(key);
201         if (value) {
202             bool accepted = param->param_readSVGValue(value);
203             if (!accepted) { 
204                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
205             }
206         } else {
207             // set default value
208             param->param_set_default();
209         }
211         it++;
212     }
215 /* This function does not and SHOULD NOT write to XML */
216 void
217 Effect::setParameter(const gchar * key, const gchar * new_value)
219     Parameter * param = getParameter(key);
220     if (param) {
221         if (new_value) {
222             bool accepted = param->param_readSVGValue(new_value);
223             if (!accepted) { 
224                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
225             }
226         } else {
227             // set default value
228             param->param_set_default();
229         }
230     }
233 void
234 Effect::registerParameter(Parameter * param)
236     param_vector.push_back(param);
239 Gtk::Widget *
240 Effect::getWidget()
242     if (!vbox) {
243         vbox = Gtk::manage( new Gtk::VBox() ); // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
244         //if (!tooltips)
245             tooltips = new Gtk::Tooltips();
247         vbox->set_border_width(5);
249         std::vector<Parameter *>::iterator it = param_vector.begin();
250         while (it != param_vector.end()) {
251             Parameter * param = *it;
252             Gtk::Widget * widg = param->param_getWidget();
253             Glib::ustring * tip = param->param_getTooltip();
254             if (widg) {
255                vbox->pack_start(*widg, true, true, 2);
256                 if (tip != NULL) {
257                     tooltips->set_tip(*widg, *tip);
258                 }
259             }
261             it++;
262         }
263     }
265     return dynamic_cast<Gtk::Widget *>(vbox);
269 Inkscape::XML::Node *
270 Effect::getRepr()
272     return SP_OBJECT_REPR(lpeobj);
275 SPDocument *
276 Effect::getSPDoc()
278     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
279     return SP_OBJECT_DOCUMENT(lpeobj);
282 Parameter *
283 Effect::getParameter(const char * key)
285     Glib::ustring stringkey(key);
287     std::vector<Parameter *>::iterator it = param_vector.begin();
288     while (it != param_vector.end()) {
289         Parameter * param = *it;
290         if ( param->param_key == key) {
291             return param;
292         }
294         it++;
295     }
297     return NULL;
300 Parameter *
301 Effect::getNextOncanvasEditableParam()
303     oncanvasedit_it++;
304     if (oncanvasedit_it == static_cast<int>(param_vector.size())) {
305         oncanvasedit_it = 0;
306     }
307     int old_it = oncanvasedit_it;
309     do {
310         Parameter * param = param_vector[oncanvasedit_it];
311         if(param && param->oncanvas_editable) {
312             return param;
313         } else {
314             oncanvasedit_it++;
315             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
316                 oncanvasedit_it = 0;
317             }
318         }
319     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
321     return NULL;
324 void
325 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
327     if (!desktop) return;
329     Parameter * param = getNextOncanvasEditableParam();
330     if (param) {
331         param->param_editOncanvas(item, desktop);
332         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
333         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
334         g_free(message);
335     } else {
336         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
337                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
338     }
341 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
342 * 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!
343 */
344 void
345 Effect::resetDefaults(SPItem * /*item*/)
347     // do nothing for simple effects
350 void
351 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
353     np->show_helperpath = true;
354     np->helperpath_rgba = 0xff0000ff;
355     np->helperpath_width = 1.0;
358 void
359 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
361     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
362     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
363         Parameter * param = *it;
364         param->param_transform_multiply(postmul, set);
365     }
368 } /* namespace LivePathEffect */
370 } /* namespace Inkscape */
372 /*
373   Local Variables:
374   mode:c++
375   c-file-style:"stroustrup"
376   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
377   indent-tabs-mode:nil
378   fill-column:99
379   End:
380 */
381 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :