Code

LPE: implement NEW path-along-path effect, i think that old one has become obsolete...
[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 namespace Inkscape {
40 namespace LivePathEffect {
42 const Util::EnumData<EffectType> LPETypeData[INVALID_LPE] = {
43     // {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"}
44     {PATH_ALONG_PATH,       N_("Path along path"),      "path_along_path"},
45     {SKELETAL_STROKES,      N_("[obsolete?] Pattern along path"),      "skeletal"},
46 #ifdef LPE_ENABLE_TEST_EFFECTS
47     {SLANT,                 N_("Slant"),                 "slant"},
48     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),   "doeffectstacktest"},
49 #endif
50     {GEARS,                 N_("Gears"),                 "gears"},
51     {CURVE_STITCH,          N_("Curve stitching"),       "curvestitching"},
52 };
53 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, INVALID_LPE);
55 Effect*
56 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
57 {
58     Effect* neweffect = NULL;
59     switch (lpenr) {
60         case SKELETAL_STROKES:
61             neweffect = (Effect*) new LPESkeletalStrokes(lpeobj);
62             break;
63         case PATH_ALONG_PATH:
64             neweffect = (Effect*) new LPEPathAlongPath(lpeobj);
65             break;
66 #ifdef LPE_ENABLE_TEST_EFFECTS
67             case SLANT:
68             neweffect = (Effect*) new LPESlant(lpeobj);
69             break;
70         case DOEFFECTSTACK_TEST:
71             neweffect = (Effect*) new LPEdoEffectStackTest(lpeobj);
72             break;
73 #endif
74         case GEARS:
75             neweffect = (Effect*) new LPEGears(lpeobj);
76             break;
77         case CURVE_STITCH:
78             neweffect = (Effect*) new LPECurveStitch(lpeobj);
79             break;
80         default:
81             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
82             neweffect = NULL;
83             break;
84     }
86     if (neweffect) {
87         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
88     }
90     return neweffect;
91 }
93 Effect::Effect(LivePathEffectObject *lpeobject)
94 {
95     vbox = NULL;
96     tooltips = NULL;
97     lpeobj = lpeobject;
98     oncanvasedit_it = param_map.begin();
99     straight_original_path = false;
102 Effect::~Effect()
104     if (tooltips) {
105         delete tooltips;
106     }
109 Glib::ustring 
110 Effect::getName()
112     if (lpeobj->effecttype_set && lpeobj->effecttype < INVALID_LPE)
113         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
114     else
115         return Glib::ustring( _("No effect") );
118 /*
119  *  Here be the doEffect function chain:
120  */
121 void
122 Effect::doEffect (SPCurve * curve)
124     NArtBpath *new_bpath = doEffect(SP_CURVE_BPATH(curve));
126     if (new_bpath && new_bpath != SP_CURVE_BPATH(curve)) {        // FIXME, add function to SPCurve to change bpath? or a copy function?
127         if (curve->_bpath) {
128             g_free(curve->_bpath); //delete old bpath
129         }
130         curve->_bpath = new_bpath;
131     }
134 NArtBpath *
135 Effect::doEffect (NArtBpath * path_in)
137     try {
138         std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);
140         std::vector<Geom::Path> result_pathv = doEffect(orig_pathv);
142         NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);
144         return new_bpath;
145     }
146     catch (std::exception e) {
147         g_warning("An exception occurred during execution of an LPE - %s", e.what());
148         SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
149             _("An exception occurred during execution of a Path Effect.") );
151         NArtBpath *path_out;
153         unsigned ret = 0;
154         while ( path_in[ret].code != NR_END ) {
155             ++ret;
156         }
157         unsigned len = ++ret;
159         path_out = g_new(NArtBpath, len);
160         memcpy(path_out, path_in, len * sizeof(NArtBpath));
161         return path_out;
162     }
165 std::vector<Geom::Path>
166 Effect::doEffect (std::vector<Geom::Path> & path_in)
168     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
170     for (unsigned int i=0; i < path_in.size(); i++) {
171         pwd2_in.concat( path_in[i].toPwSb() );
172     }
174     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect(pwd2_in);
176     std::vector<Geom::Path> path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
178     return path_out;
181 Geom::Piecewise<Geom::D2<Geom::SBasis> >
182 Effect::doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
184     g_warning("Effect has no doEffect implementation");
185     return pwd2_in;
188 void
189 Effect::readallParameters(Inkscape::XML::Node * repr)
191     param_map_type::iterator it = param_map.begin();
192     while (it != param_map.end()) {
193         const gchar * key = (*it).first.c_str();
194         const gchar * value = repr->attribute(key);
195         if(value) {
196             setParameter(key, value);
197         }
198         it++;
199     }
202 /* This function does not and SHOULD NOT write to XML */
203 void
204 Effect::setParameter(const gchar * key, const gchar * new_value)
206     Glib::ustring stringkey(key);
208     param_map_type::iterator it = param_map.find(stringkey);
209     if (it != param_map.end()) {
210         if (new_value) {
211             bool accepted = it->second->param_readSVGValue(new_value);
212             if (!accepted) { 
213                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
214             }
215         } else {
216             // set default value
217             it->second->param_set_default();
218         }
219     }
222 void
223 Effect::registerParameter(Parameter * param)
225     param_map[param->param_key] = param; // inserts or updates
228 Gtk::Widget *
229 Effect::getWidget()
231     if (!vbox) {
232         vbox = Gtk::manage( new Gtk::VBox() ); // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
233         //if (!tooltips)
234             tooltips = new Gtk::Tooltips();
236         vbox->set_border_width(5);
238         param_map_type::iterator it = param_map.begin();
239         while (it != param_map.end()) {
240             Parameter * param = it->second;
241             Gtk::Widget * widg = param->param_getWidget();
242             Glib::ustring * tip = param->param_getTooltip();
243             if (widg) {
244                vbox->pack_start(*widg, true, true, 2);
245                 if (tip != NULL) {
246                     tooltips->set_tip(*widg, *tip);
247                 }
248             }
250             it++;
251         }
252     }
254     return dynamic_cast<Gtk::Widget *>(vbox);
258 Inkscape::XML::Node * 
259 Effect::getRepr()
261     return SP_OBJECT_REPR(lpeobj);
264 SPDocument * 
265 Effect::getSPDoc()
267     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
268     return SP_OBJECT_DOCUMENT(lpeobj);
271 Parameter *
272 Effect::getNextOncanvasEditableParam()
274     oncanvasedit_it++;
275     if (oncanvasedit_it == param_map.end()) {
276         oncanvasedit_it = param_map.begin();
277     }
278     param_map_type::iterator old_it = oncanvasedit_it;
280     do {
281         Parameter * param = oncanvasedit_it->second;
282         if(param->oncanvas_editable) {
283             return param;
284         } else {
285             oncanvasedit_it++;
286             if (oncanvasedit_it == param_map.end()) {  // loop round the map
287                 oncanvasedit_it = param_map.begin();
288             }
289         }
290     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
292     return NULL;
295 void
296 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
298     if (!desktop) return;
300     Parameter * param = getNextOncanvasEditableParam();
301     if (param) {
302         param->param_editOncanvas(item, desktop);
303         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
304         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
305         g_free(message);
306     } else {
307         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
308                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
309     }
312 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
313 * 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!
314 */
315 void
316 Effect::resetDefaults(SPItem * /*item*/)
318     // do nothing for simple effects
322 } /* namespace LivePathEffect */
324 } /* namespace Inkscape */
326 /*
327   Local Variables:
328   mode:c++
329   c-file-style:"stroustrup"
330   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
331   indent-tabs-mode:nil
332   fill-column:99
333   End:
334 */
335 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :