0771edb4761b67fc1daa4b6861f1a88200d1dd3d
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 subpaths"), "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)
100 {
101 vbox = NULL;
102 tooltips = NULL;
103 lpeobj = lpeobject;
104 oncanvasedit_it = 0;
105 }
107 Effect::~Effect()
108 {
109 if (tooltips) {
110 delete tooltips;
111 }
112 }
114 Glib::ustring
115 Effect::getName()
116 {
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") );
121 }
123 /*
124 * Here be the doEffect function chain:
125 */
126 void
127 Effect::doEffect (SPCurve * curve)
128 {
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 }
137 }
139 NArtBpath *
140 Effect::doEffect_nartbpath (NArtBpath * path_in)
141 {
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 }
168 }
170 std::vector<Geom::Path>
171 Effect::doEffect_path (std::vector<Geom::Path> & path_in)
172 {
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;
184 }
186 Geom::Piecewise<Geom::D2<Geom::SBasis> >
187 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
188 {
189 g_warning("Effect has no doEffect implementation");
190 return pwd2_in;
191 }
193 void
194 Effect::readallParameters(Inkscape::XML::Node * repr)
195 {
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 }
213 }
215 /* This function does not and SHOULD NOT write to XML */
216 void
217 Effect::setParameter(const gchar * key, const gchar * new_value)
218 {
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 }
231 }
233 void
234 Effect::registerParameter(Parameter * param)
235 {
236 param_vector.push_back(param);
237 }
239 Gtk::Widget *
240 Effect::getWidget()
241 {
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);
266 }
269 Inkscape::XML::Node *
270 Effect::getRepr()
271 {
272 return SP_OBJECT_REPR(lpeobj);
273 }
275 SPDocument *
276 Effect::getSPDoc()
277 {
278 if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
279 return SP_OBJECT_DOCUMENT(lpeobj);
280 }
282 Parameter *
283 Effect::getParameter(const char * key)
284 {
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;
298 }
300 Parameter *
301 Effect::getNextOncanvasEditableParam()
302 {
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;
322 }
324 void
325 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
326 {
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 }
339 }
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*/)
346 {
347 // do nothing for simple effects
348 }
350 void
351 Effect::setup_notepath(Inkscape::NodePath::Path *np)
352 {
353 np->show_helperpath = true;
354 np->helperpath_rgba = 0xff0000ff;
355 np->helperpath_width = 1.0;
356 }
358 void
359 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
360 {
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 }
366 }
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 :