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;
100 }
102 Effect::~Effect()
103 {
104 if (tooltips) {
105 delete tooltips;
106 }
107 }
109 Glib::ustring
110 Effect::getName()
111 {
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") );
116 }
118 /*
119 * Here be the doEffect function chain:
120 */
121 void
122 Effect::doEffect (SPCurve * curve)
123 {
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 }
132 }
134 NArtBpath *
135 Effect::doEffect (NArtBpath * path_in)
136 {
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 }
163 }
165 std::vector<Geom::Path>
166 Effect::doEffect (std::vector<Geom::Path> & path_in)
167 {
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;
179 }
181 Geom::Piecewise<Geom::D2<Geom::SBasis> >
182 Effect::doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
183 {
184 g_warning("Effect has no doEffect implementation");
185 return pwd2_in;
186 }
188 void
189 Effect::readallParameters(Inkscape::XML::Node * repr)
190 {
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 }
200 }
202 /* This function does not and SHOULD NOT write to XML */
203 void
204 Effect::setParameter(const gchar * key, const gchar * new_value)
205 {
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 }
220 }
222 void
223 Effect::registerParameter(Parameter * param)
224 {
225 param_map[param->param_key] = param; // inserts or updates
226 }
228 Gtk::Widget *
229 Effect::getWidget()
230 {
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);
255 }
258 Inkscape::XML::Node *
259 Effect::getRepr()
260 {
261 return SP_OBJECT_REPR(lpeobj);
262 }
264 SPDocument *
265 Effect::getSPDoc()
266 {
267 if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
268 return SP_OBJECT_DOCUMENT(lpeobj);
269 }
271 Parameter *
272 Effect::getNextOncanvasEditableParam()
273 {
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;
293 }
295 void
296 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
297 {
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 }
310 }
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*/)
317 {
318 // do nothing for simple effects
319 }
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 :