Code

dc43af0d5073023a93f7fbfaeef94c7fee01e999
[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 //here!!
38 #include "live_effects/lpe-sketch.h"
39 #include "live_effects/lpe-vonkoch.h"
40 #include "live_effects/lpe-knot.h"
41 #include "live_effects/lpe-slant.h"
42 #include "live_effects/lpe-test-doEffect-stack.h"
43 #include "live_effects/lpe-gears.h"
44 #include "live_effects/lpe-curvestitch.h"
46 #include "nodepath.h"
48 namespace Inkscape {
50 namespace LivePathEffect {
52 const Util::EnumData<EffectType> LPETypeData[INVALID_LPE] = {
53     // {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"}
54     {PATH_ALONG_PATH,       N_("Bend Path"),             "bend_path"},
55     {SKELETAL_STROKES,      N_("Pattern Along Path"),    "skeletal"},
56     {SKETCH,                N_("Sketch"),                "sketch"},
57     {VONKOCH,               N_("VonKoch"),               "vonkoch"},
58     {KNOT,                  N_("Knot"),                  "knot"},
59 #ifdef LPE_ENABLE_TEST_EFFECTS
60     {SLANT,                 N_("Slant"),                 "slant"},
61     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),   "doeffectstacktest"},
62 #endif
63     {GEARS,                 N_("Gears"),                 "gears"},
64     {CURVE_STITCH,          N_("Stitch Sub-Paths"),       "curvestitching"},
65 };
66 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, INVALID_LPE);
68 Effect*
69 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
70 {
71     Effect* neweffect = NULL;
72     switch (lpenr) {
73         case SKELETAL_STROKES:
74             neweffect = (Effect*) new LPESkeletalStrokes(lpeobj);
75             break;
76         case PATH_ALONG_PATH:
77             neweffect = (Effect*) new LPEPathAlongPath(lpeobj);
78             break;
79 //here!!
80         case SKETCH:
81             neweffect = (Effect*) new LPESketch(lpeobj);
82             break;
83         case VONKOCH:
84             neweffect = (Effect*) new LPEVonKoch(lpeobj);
85             break;
86         case KNOT:
87             neweffect = (Effect*) new LPEKnot(lpeobj);
88             break;
89 #ifdef LPE_ENABLE_TEST_EFFECTS
90             case SLANT:
91             neweffect = (Effect*) new LPESlant(lpeobj);
92             break;
93         case DOEFFECTSTACK_TEST:
94             neweffect = (Effect*) new LPEdoEffectStackTest(lpeobj);
95             break;
96 #endif
97         case GEARS:
98             neweffect = (Effect*) new LPEGears(lpeobj);
99             break;
100         case CURVE_STITCH:
101             neweffect = (Effect*) new LPECurveStitch(lpeobj);
102             break;
103         default:
104             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
105             neweffect = NULL;
106             break;
107     }
109     if (neweffect) {
110         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
111     }
113     return neweffect;
116 Effect::Effect(LivePathEffectObject *lpeobject)
118     lpeobj = lpeobject;
119     oncanvasedit_it = 0;
122 Effect::~Effect()
126 Glib::ustring
127 Effect::getName()
129     if (lpeobj->effecttype_set && lpeobj->effecttype < INVALID_LPE)
130         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
131     else
132         return Glib::ustring( _("No effect") );
135 void
136 Effect::doBeforeEffect (SPLPEItem *lpeitem)
138     //Do nothing for simple effects
142 /*
143  *  Here be the doEffect function chain:
144  */
145 void
146 Effect::doEffect (SPCurve * curve)
148     NArtBpath *new_bpath = doEffect_nartbpath(SP_CURVE_BPATH(curve));
150     if (new_bpath && new_bpath != SP_CURVE_BPATH(curve)) {        // FIXME, add function to SPCurve to change bpath? or a copy function?
151         if (curve->_bpath) {
152             g_free(curve->_bpath); //delete old bpath
153         }
154         curve->_bpath = new_bpath;
155     }
158 NArtBpath *
159 Effect::doEffect_nartbpath (NArtBpath * path_in)
161     try {
162         std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);
164         std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
166         NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);
168         return new_bpath;
169     }
170     catch (std::exception & e) {
171         g_warning("Exception during LPE %s execution. \n %s", getName().c_str(), e.what());
172         SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
173             _("An exception occurred during execution of the Path Effect.") );
175         NArtBpath *path_out;
177         unsigned ret = 0;
178         while ( path_in[ret].code != NR_END ) {
179             ++ret;
180         }
181         unsigned len = ++ret;
183         path_out = g_new(NArtBpath, len);
184         memcpy(path_out, path_in, len * sizeof(NArtBpath));
185         return path_out;
186     }
189 std::vector<Geom::Path>
190 Effect::doEffect_path (std::vector<Geom::Path> & path_in)
192     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
194     for (unsigned int i=0; i < path_in.size(); i++) {
195         pwd2_in.concat( path_in[i].toPwSb() );
196     }
198     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
200     std::vector<Geom::Path> path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
202     return path_out;
205 Geom::Piecewise<Geom::D2<Geom::SBasis> >
206 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
208     g_warning("Effect has no doEffect implementation");
209     return pwd2_in;
212 void
213 Effect::readallParameters(Inkscape::XML::Node * repr)
215     std::vector<Parameter *>::iterator it = param_vector.begin();
216     while (it != param_vector.end()) {
217         Parameter * param = *it;
218         const gchar * key = param->param_key.c_str();
219         const gchar * value = repr->attribute(key);
220         if (value) {
221             bool accepted = param->param_readSVGValue(value);
222             if (!accepted) { 
223                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
224             }
225         } else {
226             // set default value
227             param->param_set_default();
228         }
230         it++;
231     }
234 /* This function does not and SHOULD NOT write to XML */
235 void
236 Effect::setParameter(const gchar * key, const gchar * new_value)
238     Parameter * param = getParameter(key);
239     if (param) {
240         if (new_value) {
241             bool accepted = param->param_readSVGValue(new_value);
242             if (!accepted) { 
243                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
244             }
245         } else {
246             // set default value
247             param->param_set_default();
248         }
249     }
252 void
253 Effect::registerParameter(Parameter * param)
255     param_vector.push_back(param);
258 /**
259 * This *creates* a new widget, management of deletion should be done by the caller
260 */
261 Gtk::Widget *
262 Effect::newWidget(Gtk::Tooltips * tooltips)
264     // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
265     Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
267     vbox->set_border_width(5);
269     std::vector<Parameter *>::iterator it = param_vector.begin();
270     while (it != param_vector.end()) {
271         Parameter * param = *it;
272         Gtk::Widget * widg = param->param_newWidget(tooltips);
273         Glib::ustring * tip = param->param_getTooltip();
274         if (widg) {
275            vbox->pack_start(*widg, true, true, 2);
276             if (tip != NULL) {
277                 tooltips->set_tip(*widg, *tip);
278             }
279         }
281         it++;
282     }
284     return dynamic_cast<Gtk::Widget *>(vbox);
288 Inkscape::XML::Node *
289 Effect::getRepr()
291     return SP_OBJECT_REPR(lpeobj);
294 SPDocument *
295 Effect::getSPDoc()
297     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
298     return SP_OBJECT_DOCUMENT(lpeobj);
301 Parameter *
302 Effect::getParameter(const char * key)
304     Glib::ustring stringkey(key);
306     std::vector<Parameter *>::iterator it = param_vector.begin();
307     while (it != param_vector.end()) {
308         Parameter * param = *it;
309         if ( param->param_key == key) {
310             return param;
311         }
313         it++;
314     }
316     return NULL;
319 Parameter *
320 Effect::getNextOncanvasEditableParam()
322     oncanvasedit_it++;
323     if (oncanvasedit_it == static_cast<int>(param_vector.size())) {
324         oncanvasedit_it = 0;
325     }
326     int old_it = oncanvasedit_it;
328     do {
329         Parameter * param = param_vector[oncanvasedit_it];
330         if(param && param->oncanvas_editable) {
331             return param;
332         } else {
333             oncanvasedit_it++;
334             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
335                 oncanvasedit_it = 0;
336             }
337         }
338     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
340     return NULL;
343 void
344 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
346     if (!desktop) return;
348     Parameter * param = getNextOncanvasEditableParam();
349     if (param) {
350         param->param_editOncanvas(item, desktop);
351         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
352         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
353         g_free(message);
354     } else {
355         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
356                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
357     }
360 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
361 * 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!
362 */
363 void
364 Effect::resetDefaults(SPItem * /*item*/)
366     // do nothing for simple effects
369 void
370 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
372     np->show_helperpath = true;
373     np->helperpath_rgba = 0xff0000ff;
374     np->helperpath_width = 1.0;
377 void
378 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
380     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
381     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
382         Parameter * param = *it;
383         param->param_transform_multiply(postmul, set);
384     }
387 } /* namespace LivePathEffect */
389 } /* namespace Inkscape */
391 /*
392   Local Variables:
393   mode:c++
394   c-file-style:"stroustrup"
395   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
396   indent-tabs-mode:nil
397   fill-column:99
398   End:
399 */
400 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :