dc43af0d5073023a93f7fbfaeef94c7fee01e999
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 //here!!
38 #include "live_effects/lpe-sketch.h"
39 #include "live_effects/lpe-vonkoch.h"
40 #include "live_effects/lpe-knot.h"
41 #include "live_effects/lpe-slant.h"
42 #include "live_effects/lpe-test-doEffect-stack.h"
43 #include "live_effects/lpe-gears.h"
44 #include "live_effects/lpe-curvestitch.h"
46 #include "nodepath.h"
48 namespace Inkscape {
50 namespace LivePathEffect {
52 const Util::EnumData<EffectType> LPETypeData[INVALID_LPE] = {
53 // {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"}
54 {PATH_ALONG_PATH, N_("Bend Path"), "bend_path"},
55 {SKELETAL_STROKES, N_("Pattern Along Path"), "skeletal"},
56 {SKETCH, N_("Sketch"), "sketch"},
57 {VONKOCH, N_("VonKoch"), "vonkoch"},
58 {KNOT, N_("Knot"), "knot"},
59 #ifdef LPE_ENABLE_TEST_EFFECTS
60 {SLANT, N_("Slant"), "slant"},
61 {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"},
62 #endif
63 {GEARS, N_("Gears"), "gears"},
64 {CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"},
65 };
66 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, INVALID_LPE);
68 Effect*
69 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
70 {
71 Effect* neweffect = NULL;
72 switch (lpenr) {
73 case SKELETAL_STROKES:
74 neweffect = (Effect*) new LPESkeletalStrokes(lpeobj);
75 break;
76 case PATH_ALONG_PATH:
77 neweffect = (Effect*) new LPEPathAlongPath(lpeobj);
78 break;
79 //here!!
80 case SKETCH:
81 neweffect = (Effect*) new LPESketch(lpeobj);
82 break;
83 case VONKOCH:
84 neweffect = (Effect*) new LPEVonKoch(lpeobj);
85 break;
86 case KNOT:
87 neweffect = (Effect*) new LPEKnot(lpeobj);
88 break;
89 #ifdef LPE_ENABLE_TEST_EFFECTS
90 case SLANT:
91 neweffect = (Effect*) new LPESlant(lpeobj);
92 break;
93 case DOEFFECTSTACK_TEST:
94 neweffect = (Effect*) new LPEdoEffectStackTest(lpeobj);
95 break;
96 #endif
97 case GEARS:
98 neweffect = (Effect*) new LPEGears(lpeobj);
99 break;
100 case CURVE_STITCH:
101 neweffect = (Effect*) new LPECurveStitch(lpeobj);
102 break;
103 default:
104 g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
105 neweffect = NULL;
106 break;
107 }
109 if (neweffect) {
110 neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
111 }
113 return neweffect;
114 }
116 Effect::Effect(LivePathEffectObject *lpeobject)
117 {
118 lpeobj = lpeobject;
119 oncanvasedit_it = 0;
120 }
122 Effect::~Effect()
123 {
124 }
126 Glib::ustring
127 Effect::getName()
128 {
129 if (lpeobj->effecttype_set && lpeobj->effecttype < INVALID_LPE)
130 return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
131 else
132 return Glib::ustring( _("No effect") );
133 }
135 void
136 Effect::doBeforeEffect (SPLPEItem *lpeitem)
137 {
138 //Do nothing for simple effects
139 }
142 /*
143 * Here be the doEffect function chain:
144 */
145 void
146 Effect::doEffect (SPCurve * curve)
147 {
148 NArtBpath *new_bpath = doEffect_nartbpath(SP_CURVE_BPATH(curve));
150 if (new_bpath && new_bpath != SP_CURVE_BPATH(curve)) { // FIXME, add function to SPCurve to change bpath? or a copy function?
151 if (curve->_bpath) {
152 g_free(curve->_bpath); //delete old bpath
153 }
154 curve->_bpath = new_bpath;
155 }
156 }
158 NArtBpath *
159 Effect::doEffect_nartbpath (NArtBpath * path_in)
160 {
161 try {
162 std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);
164 std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
166 NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);
168 return new_bpath;
169 }
170 catch (std::exception & e) {
171 g_warning("Exception during LPE %s execution. \n %s", getName().c_str(), e.what());
172 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
173 _("An exception occurred during execution of the Path Effect.") );
175 NArtBpath *path_out;
177 unsigned ret = 0;
178 while ( path_in[ret].code != NR_END ) {
179 ++ret;
180 }
181 unsigned len = ++ret;
183 path_out = g_new(NArtBpath, len);
184 memcpy(path_out, path_in, len * sizeof(NArtBpath));
185 return path_out;
186 }
187 }
189 std::vector<Geom::Path>
190 Effect::doEffect_path (std::vector<Geom::Path> & path_in)
191 {
192 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
194 for (unsigned int i=0; i < path_in.size(); i++) {
195 pwd2_in.concat( path_in[i].toPwSb() );
196 }
198 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
200 std::vector<Geom::Path> path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
202 return path_out;
203 }
205 Geom::Piecewise<Geom::D2<Geom::SBasis> >
206 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
207 {
208 g_warning("Effect has no doEffect implementation");
209 return pwd2_in;
210 }
212 void
213 Effect::readallParameters(Inkscape::XML::Node * repr)
214 {
215 std::vector<Parameter *>::iterator it = param_vector.begin();
216 while (it != param_vector.end()) {
217 Parameter * param = *it;
218 const gchar * key = param->param_key.c_str();
219 const gchar * value = repr->attribute(key);
220 if (value) {
221 bool accepted = param->param_readSVGValue(value);
222 if (!accepted) {
223 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
224 }
225 } else {
226 // set default value
227 param->param_set_default();
228 }
230 it++;
231 }
232 }
234 /* This function does not and SHOULD NOT write to XML */
235 void
236 Effect::setParameter(const gchar * key, const gchar * new_value)
237 {
238 Parameter * param = getParameter(key);
239 if (param) {
240 if (new_value) {
241 bool accepted = param->param_readSVGValue(new_value);
242 if (!accepted) {
243 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
244 }
245 } else {
246 // set default value
247 param->param_set_default();
248 }
249 }
250 }
252 void
253 Effect::registerParameter(Parameter * param)
254 {
255 param_vector.push_back(param);
256 }
258 /**
259 * This *creates* a new widget, management of deletion should be done by the caller
260 */
261 Gtk::Widget *
262 Effect::newWidget(Gtk::Tooltips * tooltips)
263 {
264 // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
265 Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
267 vbox->set_border_width(5);
269 std::vector<Parameter *>::iterator it = param_vector.begin();
270 while (it != param_vector.end()) {
271 Parameter * param = *it;
272 Gtk::Widget * widg = param->param_newWidget(tooltips);
273 Glib::ustring * tip = param->param_getTooltip();
274 if (widg) {
275 vbox->pack_start(*widg, true, true, 2);
276 if (tip != NULL) {
277 tooltips->set_tip(*widg, *tip);
278 }
279 }
281 it++;
282 }
284 return dynamic_cast<Gtk::Widget *>(vbox);
285 }
288 Inkscape::XML::Node *
289 Effect::getRepr()
290 {
291 return SP_OBJECT_REPR(lpeobj);
292 }
294 SPDocument *
295 Effect::getSPDoc()
296 {
297 if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
298 return SP_OBJECT_DOCUMENT(lpeobj);
299 }
301 Parameter *
302 Effect::getParameter(const char * key)
303 {
304 Glib::ustring stringkey(key);
306 std::vector<Parameter *>::iterator it = param_vector.begin();
307 while (it != param_vector.end()) {
308 Parameter * param = *it;
309 if ( param->param_key == key) {
310 return param;
311 }
313 it++;
314 }
316 return NULL;
317 }
319 Parameter *
320 Effect::getNextOncanvasEditableParam()
321 {
322 oncanvasedit_it++;
323 if (oncanvasedit_it == static_cast<int>(param_vector.size())) {
324 oncanvasedit_it = 0;
325 }
326 int old_it = oncanvasedit_it;
328 do {
329 Parameter * param = param_vector[oncanvasedit_it];
330 if(param && param->oncanvas_editable) {
331 return param;
332 } else {
333 oncanvasedit_it++;
334 if (oncanvasedit_it == static_cast<int>(param_vector.size())) { // loop round the map
335 oncanvasedit_it = 0;
336 }
337 }
338 } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
340 return NULL;
341 }
343 void
344 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
345 {
346 if (!desktop) return;
348 Parameter * param = getNextOncanvasEditableParam();
349 if (param) {
350 param->param_editOncanvas(item, desktop);
351 gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
352 desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
353 g_free(message);
354 } else {
355 desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
356 _("None of the applied path effect's parameters can be edited on-canvas.") );
357 }
358 }
360 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
361 * 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!
362 */
363 void
364 Effect::resetDefaults(SPItem * /*item*/)
365 {
366 // do nothing for simple effects
367 }
369 void
370 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
371 {
372 np->show_helperpath = true;
373 np->helperpath_rgba = 0xff0000ff;
374 np->helperpath_width = 1.0;
375 }
377 void
378 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
379 {
380 // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
381 for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
382 Parameter * param = *it;
383 param->param_transform_multiply(postmul, set);
384 }
385 }
387 } /* namespace LivePathEffect */
389 } /* namespace Inkscape */
391 /*
392 Local Variables:
393 mode:c++
394 c-file-style:"stroustrup"
395 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
396 indent-tabs-mode:nil
397 fill-column:99
398 End:
399 */
400 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :