ed2eb62fe286c62ed912ff29128a999ae4855b24
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;
101 }
103 Effect::~Effect()
104 {
105 if (tooltips) {
106 delete tooltips;
107 }
108 }
110 Glib::ustring
111 Effect::getName()
112 {
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") );
117 }
119 /*
120 * Here be the doEffect function chain:
121 */
122 void
123 Effect::doEffect (SPCurve * curve)
124 {
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 }
133 }
135 NArtBpath *
136 Effect::doEffect_nartbpath (NArtBpath * path_in)
137 {
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 }
164 }
166 std::vector<Geom::Path>
167 Effect::doEffect_path (std::vector<Geom::Path> & path_in)
168 {
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;
180 }
182 Geom::Piecewise<Geom::D2<Geom::SBasis> >
183 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
184 {
185 g_warning("Effect has no doEffect implementation");
186 return pwd2_in;
187 }
189 void
190 Effect::readallParameters(Inkscape::XML::Node * repr)
191 {
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 }
209 }
211 /* This function does not and SHOULD NOT write to XML */
212 void
213 Effect::setParameter(const gchar * key, const gchar * new_value)
214 {
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 }
227 }
229 void
230 Effect::registerParameter(Parameter * param)
231 {
232 param_vector.push_back(param);
233 }
235 Gtk::Widget *
236 Effect::getWidget()
237 {
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);
262 }
265 Inkscape::XML::Node *
266 Effect::getRepr()
267 {
268 return SP_OBJECT_REPR(lpeobj);
269 }
271 SPDocument *
272 Effect::getSPDoc()
273 {
274 if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
275 return SP_OBJECT_DOCUMENT(lpeobj);
276 }
278 Parameter *
279 Effect::getParameter(const char * key)
280 {
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;
294 }
296 Parameter *
297 Effect::getNextOncanvasEditableParam()
298 {
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;
318 }
320 void
321 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
322 {
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 }
335 }
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*/)
342 {
343 // do nothing for simple effects
344 }
346 void
347 Effect::setup_notepath(Inkscape::NodePath::Path *np)
348 {
349 np->show_helperpath = true;
350 np->helperpath_rgba = 0xff0000ff;
351 np->helperpath_width = 1.0;
352 }
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 :