Code

Set teeth selector in lpe-gears to an integer value >= 3.
[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 = 0;
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     std::vector<Parameter *>::iterator it = param_vector.begin();
193     while (it != param_vector.end()) {
194         Parameter * param = *it;
195         const gchar * key = param->param_key.c_str();
196         const gchar * value = repr->attribute(key);
197         if (value) {
198             bool accepted = param->param_readSVGValue(value);
199             if (!accepted) { 
200                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
201             }
202         } else {
203             // set default value
204             param->param_set_default();
205         }
207         it++;
208     }
211 /* This function does not and SHOULD NOT write to XML */
212 void
213 Effect::setParameter(const gchar * key, const gchar * new_value)
215     Parameter * param = getParameter(key);
216     if (param) {
217         if (new_value) {
218             bool accepted = param->param_readSVGValue(new_value);
219             if (!accepted) { 
220                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
221             }
222         } else {
223             // set default value
224             param->param_set_default();
225         }
226     }
229 void
230 Effect::registerParameter(Parameter * param)
232     param_vector.push_back(param);
235 Gtk::Widget *
236 Effect::getWidget()
238     if (!vbox) {
239         vbox = Gtk::manage( new Gtk::VBox() ); // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
240         //if (!tooltips)
241             tooltips = new Gtk::Tooltips();
243         vbox->set_border_width(5);
245         std::vector<Parameter *>::iterator it = param_vector.begin();
246         while (it != param_vector.end()) {
247             Parameter * param = *it;
248             Gtk::Widget * widg = param->param_getWidget();
249             Glib::ustring * tip = param->param_getTooltip();
250             if (widg) {
251                vbox->pack_start(*widg, true, true, 2);
252                 if (tip != NULL) {
253                     tooltips->set_tip(*widg, *tip);
254                 }
255             }
257             it++;
258         }
259     }
261     return dynamic_cast<Gtk::Widget *>(vbox);
265 Inkscape::XML::Node *
266 Effect::getRepr()
268     return SP_OBJECT_REPR(lpeobj);
271 SPDocument *
272 Effect::getSPDoc()
274     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
275     return SP_OBJECT_DOCUMENT(lpeobj);
278 Parameter *
279 Effect::getParameter(const char * key)
281     Glib::ustring stringkey(key);
283     std::vector<Parameter *>::iterator it = param_vector.begin();
284     while (it != param_vector.end()) {
285         Parameter * param = *it;
286         if ( param->param_key == key) {
287             return param;
288         }
290         it++;
291     }
293     return NULL;
296 Parameter *
297 Effect::getNextOncanvasEditableParam()
299     oncanvasedit_it++;
300     if (oncanvasedit_it == static_cast<int>(param_vector.size())) {
301         oncanvasedit_it = 0;
302     }
303     int old_it = oncanvasedit_it;
305     do {
306         Parameter * param = param_vector[oncanvasedit_it];
307         if(param && param->oncanvas_editable) {
308             return param;
309         } else {
310             oncanvasedit_it++;
311             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
312                 oncanvasedit_it = 0;
313             }
314         }
315     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
317     return NULL;
320 void
321 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
323     if (!desktop) return;
325     Parameter * param = getNextOncanvasEditableParam();
326     if (param) {
327         param->param_editOncanvas(item, desktop);
328         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
329         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
330         g_free(message);
331     } else {
332         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
333                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
334     }
337 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
338 * 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!
339 */
340 void
341 Effect::resetDefaults(SPItem * /*item*/)
343     // do nothing for simple effects
346 void
347 Effect::setup_notepath(Inkscape::NodePath::Path *np)
349     np->show_helperpath = true;
350     np->helperpath_rgba = 0xff0000ff;
351     np->helperpath_width = 1.0;
355 } /* namespace LivePathEffect */
357 } /* namespace Inkscape */
359 /*
360   Local Variables:
361   mode:c++
362   c-file-style:"stroustrup"
363   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
364   indent-tabs-mode:nil
365   fill-column:99
366   End:
367 */
368 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :