4ccfa59524720c04365036bf6ca89a44e4f55726
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_("Path along path"), "path_along_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 subcurves"), "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 = param_map.begin();
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 param_map_type::iterator it = param_map.begin();
193 while (it != param_map.end()) {
194 const gchar * key = (*it).first.c_str();
195 const gchar * value = repr->attribute(key);
196 if(value) {
197 setParameter(key, value);
198 }
199 it++;
200 }
201 }
203 /* This function does not and SHOULD NOT write to XML */
204 void
205 Effect::setParameter(const gchar * key, const gchar * new_value)
206 {
207 Glib::ustring stringkey(key);
209 param_map_type::iterator it = param_map.find(stringkey);
210 if (it != param_map.end()) {
211 if (new_value) {
212 bool accepted = it->second->param_readSVGValue(new_value);
213 if (!accepted) {
214 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
215 }
216 } else {
217 // set default value
218 it->second->param_set_default();
219 }
220 }
221 }
223 void
224 Effect::registerParameter(Parameter * param)
225 {
226 param_map[param->param_key] = param; // inserts or updates
227 }
229 Gtk::Widget *
230 Effect::getWidget()
231 {
232 if (!vbox) {
233 vbox = Gtk::manage( new Gtk::VBox() ); // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
234 //if (!tooltips)
235 tooltips = new Gtk::Tooltips();
237 vbox->set_border_width(5);
239 param_map_type::iterator it = param_map.begin();
240 while (it != param_map.end()) {
241 Parameter * param = it->second;
242 Gtk::Widget * widg = param->param_getWidget();
243 Glib::ustring * tip = param->param_getTooltip();
244 if (widg) {
245 vbox->pack_start(*widg, true, true, 2);
246 if (tip != NULL) {
247 tooltips->set_tip(*widg, *tip);
248 }
249 }
251 it++;
252 }
253 }
255 return dynamic_cast<Gtk::Widget *>(vbox);
256 }
259 Inkscape::XML::Node *
260 Effect::getRepr()
261 {
262 return SP_OBJECT_REPR(lpeobj);
263 }
265 SPDocument *
266 Effect::getSPDoc()
267 {
268 if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
269 return SP_OBJECT_DOCUMENT(lpeobj);
270 }
272 Parameter *
273 Effect::getParameter(const char * key)
274 {
275 Glib::ustring stringkey(key);
277 param_map_type::iterator it = param_map.find(stringkey);
278 if (it != param_map.end()) {
279 return it->second;
280 } else {
281 return NULL;
282 }
283 }
285 Parameter *
286 Effect::getNextOncanvasEditableParam()
287 {
288 oncanvasedit_it++;
289 if (oncanvasedit_it == param_map.end()) {
290 oncanvasedit_it = param_map.begin();
291 }
292 param_map_type::iterator old_it = oncanvasedit_it;
294 do {
295 Parameter * param = oncanvasedit_it->second;
296 if(param->oncanvas_editable) {
297 return param;
298 } else {
299 oncanvasedit_it++;
300 if (oncanvasedit_it == param_map.end()) { // loop round the map
301 oncanvasedit_it = param_map.begin();
302 }
303 }
304 } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
306 return NULL;
307 }
309 void
310 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
311 {
312 if (!desktop) return;
314 Parameter * param = getNextOncanvasEditableParam();
315 if (param) {
316 param->param_editOncanvas(item, desktop);
317 gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
318 desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
319 g_free(message);
320 } else {
321 desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
322 _("None of the applied path effect's parameters can be edited on-canvas.") );
323 }
324 }
326 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
327 * 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!
328 */
329 void
330 Effect::resetDefaults(SPItem * /*item*/)
331 {
332 // do nothing for simple effects
333 }
335 void
336 Effect::setup_notepath(Inkscape::NodePath::Path *np)
337 {
338 np->show_helperpath = true;
339 np->helperpath_rgba = 0xff0000ff;
340 np->helperpath_width = 1.0;
341 }
344 } /* namespace LivePathEffect */
346 } /* namespace Inkscape */
348 /*
349 Local Variables:
350 mode:c++
351 c-file-style:"stroustrup"
352 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
353 indent-tabs-mode:nil
354 fill-column:99
355 End:
356 */
357 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :