1 #define INKSCAPE_LIVEPATHEFFECT_CPP\r
2 \r
3 /*\r
4 * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
5 *\r
6 * Released under GNU GPL, read the file 'COPYING' for more information\r
7 */\r
8 \r
9 #include "display/display-forward.h"\r
10 #include "xml/node-event-vector.h"\r
11 #include "sp-object.h"\r
12 #include "attributes.h"\r
13 \r
14 #include "desktop.h"\r
15 \r
16 #include "document.h"\r
17 #include <glibmm/i18n.h>\r
18 \r
19 #include "live_effects/effect.h"\r
20 #include "live_effects/lpeobject.h"\r
21 #include "live_effects/parameter/parameter.h"\r
22 #include <glibmm/ustring.h>\r
23 #include "live_effects/n-art-bpath-2geom.h"\r
24 #include "display/curve.h"\r
25 #include <2geom/sbasis-to-bezier.h>\r
26 #include <gtkmm.h>\r
27 \r
28 #include <exception>\r
29 \r
30 // include effects:\r
31 #include "live_effects/lpe-skeletalstrokes.h"\r
32 #include "live_effects/lpe-slant.h"\r
33 #include "live_effects/lpe-test-doEffect-stack.h"\r
34 #include "live_effects/lpe-gears.h"\r
35 #include "live_effects/lpe-curvestitch.h"\r
36 \r
37 namespace Inkscape {\r
38 \r
39 namespace LivePathEffect {\r
40 \r
41 const Util::EnumData<EffectType> LPETypeData[INVALID_LPE] = {\r
42 // {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"}\r
43 {SKELETAL_STROKES, N_("Path along path"), "skeletal"},\r
44 #ifdef LPE_ENABLE_TEST_EFFECTS\r
45 {SLANT, N_("Slant"), "slant"},\r
46 {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"},\r
47 #endif\r
48 {GEARS, N_("Gears"), "gears"},\r
49 {CURVE_STITCH, N_("Curve stitching"), "curvestitching"},\r
50 };\r
51 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, INVALID_LPE);\r
52 \r
53 Effect*\r
54 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)\r
55 {\r
56 Effect* neweffect = NULL;\r
57 switch (lpenr) {\r
58 case SKELETAL_STROKES:\r
59 neweffect = (Effect*) new LPESkeletalStrokes(lpeobj);\r
60 break;\r
61 #ifdef LPE_ENABLE_TEST_EFFECTS\r
62 case SLANT:\r
63 neweffect = (Effect*) new LPESlant(lpeobj);\r
64 break;\r
65 case DOEFFECTSTACK_TEST:\r
66 neweffect = (Effect*) new LPEdoEffectStackTest(lpeobj);\r
67 break;\r
68 #endif\r
69 case GEARS:\r
70 neweffect = (Effect*) new LPEGears(lpeobj);\r
71 break;\r
72 case CURVE_STITCH:\r
73 neweffect = (Effect*) new LPECurveStitch(lpeobj);\r
74 break;\r
75 default:\r
76 g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);\r
77 neweffect = NULL;\r
78 break;\r
79 }\r
80 \r
81 if (neweffect) {\r
82 neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));\r
83 }\r
84 \r
85 return neweffect;\r
86 }\r
87 \r
88 Effect::Effect(LivePathEffectObject *lpeobject)\r
89 {\r
90 vbox = NULL;\r
91 tooltips = NULL;\r
92 lpeobj = lpeobject;\r
93 }\r
94 \r
95 Effect::~Effect()\r
96 {\r
97 if (tooltips) {\r
98 delete tooltips;\r
99 }\r
100 }\r
101 \r
102 Glib::ustring \r
103 Effect::getName()\r
104 {\r
105 if (lpeobj->effecttype_set && lpeobj->effecttype < INVALID_LPE)\r
106 return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );\r
107 else\r
108 return Glib::ustring( _("No effect") );\r
109 }\r
110 \r
111 /*\r
112 * Here be the doEffect function chain:\r
113 */\r
114 void\r
115 Effect::doEffect (SPCurve * curve)\r
116 {\r
117 NArtBpath *new_bpath = doEffect(SP_CURVE_BPATH(curve));\r
118 \r
119 if (new_bpath && new_bpath != SP_CURVE_BPATH(curve)) { // FIXME, add function to SPCurve to change bpath? or a copy function?\r
120 if (curve->_bpath) {\r
121 g_free(curve->_bpath); //delete old bpath\r
122 }\r
123 curve->_bpath = new_bpath;\r
124 }\r
125 }\r
126 \r
127 NArtBpath *\r
128 Effect::doEffect (NArtBpath * path_in)\r
129 {\r
130 try {\r
131 std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);\r
132 \r
133 std::vector<Geom::Path> result_pathv = doEffect(orig_pathv);\r
134 \r
135 NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);\r
136 \r
137 return new_bpath;\r
138 }\r
139 catch (std::exception e) {\r
140 g_warning("An exception occurred during execution of an LPE - %s", e.what());\r
141 // return here\r
142 NArtBpath *path_out;\r
143 \r
144 unsigned ret = 0;\r
145 while ( path_in[ret].code != NR_END ) {\r
146 ++ret;\r
147 }\r
148 unsigned len = ++ret;\r
149 \r
150 path_out = g_new(NArtBpath, len);\r
151 memcpy(path_out, path_in, len * sizeof(NArtBpath));\r
152 return path_out;\r
153 }\r
154 }\r
155 \r
156 std::vector<Geom::Path>\r
157 Effect::doEffect (std::vector<Geom::Path> & path_in)\r
158 {\r
159 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;\r
160 \r
161 for (unsigned int i=0; i < path_in.size(); i++) {\r
162 pwd2_in.concat( path_in[i].toPwSb() );\r
163 }\r
164 \r
165 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect(pwd2_in);\r
166 \r
167 std::vector<Geom::Path> path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);\r
168 \r
169 return path_out;\r
170 }\r
171 \r
172 Geom::Piecewise<Geom::D2<Geom::SBasis> >\r
173 Effect::doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)\r
174 {\r
175 g_warning("Effect has no doEffect implementation");\r
176 return pwd2_in;\r
177 }\r
178 \r
179 void\r
180 Effect::readallParameters(Inkscape::XML::Node * repr)\r
181 {\r
182 param_map_type::iterator it = param_map.begin();\r
183 while (it != param_map.end()) {\r
184 const gchar * key = (*it).first.c_str();\r
185 const gchar * value = repr->attribute(key);\r
186 if(value) {\r
187 setParameter(repr, key, NULL, value);\r
188 }\r
189 it++;\r
190 }\r
191 }\r
192 \r
193 void\r
194 Effect::setParameter(Inkscape::XML::Node * repr, const gchar * key, const gchar * old_value, const gchar * new_value)\r
195 {\r
196 Glib::ustring stringkey(key);\r
197 \r
198 param_map_type::iterator it = param_map.find(stringkey);\r
199 if (it != param_map.end()) {\r
200 if (new_value) {\r
201 bool accepted = it->second->param_readSVGValue(new_value);\r
202 if (!accepted) { \r
203 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);\r
204 // change was not accepted, so change it back.\r
205 // think: can this backfire and create infinite loop when started with unacceptable old_value?\r
206 // repr->setAttribute(key, old_value);\r
207 }\r
208 } else {\r
209 // set default value\r
210 it->second->param_set_default();\r
211 }\r
212 }\r
213 }\r
214 \r
215 void\r
216 Effect::registerParameter(Parameter * param)\r
217 {\r
218 param_map[param->param_key] = param; // inserts or updates\r
219 }\r
220 \r
221 Gtk::Widget *\r
222 Effect::getWidget()\r
223 {\r
224 if (!vbox) {\r
225 vbox = Gtk::manage( new Gtk::VBox() ); // use manage here, because after deletion of Effect object, others might still be pointing to this widget.\r
226 //if (!tooltips)\r
227 tooltips = new Gtk::Tooltips();\r
228 \r
229 vbox->set_border_width(5);\r
230 \r
231 param_map_type::iterator it = param_map.begin();\r
232 while (it != param_map.end()) {\r
233 Parameter * param = it->second;\r
234 Gtk::Widget * widg = param->param_getWidget();\r
235 Glib::ustring * tip = param->param_getTooltip();\r
236 if (widg) {\r
237 vbox->pack_start(*widg, true, true, 2);\r
238 if (tip != NULL) {\r
239 tooltips->set_tip(*widg, *tip);\r
240 }\r
241 }\r
242 \r
243 it++;\r
244 }\r
245 }\r
246 \r
247 return dynamic_cast<Gtk::Widget *>(vbox);\r
248 }\r
249 \r
250 \r
251 Inkscape::XML::Node * \r
252 Effect::getRepr()\r
253 {\r
254 return SP_OBJECT_REPR(lpeobj);\r
255 }\r
256 \r
257 SPDocument * \r
258 Effect::getSPDoc()\r
259 {\r
260 if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");\r
261 return SP_OBJECT_DOCUMENT(lpeobj);\r
262 }\r
263 \r
264 \r
265 } /* namespace LivePathEffect */\r
266 \r
267 } /* namespace Inkscape */\r
268 \r
269 /*\r
270 Local Variables:\r
271 mode:c++\r
272 c-file-style:"stroustrup"\r
273 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
274 indent-tabs-mode:nil\r
275 fill-column:99\r
276 End:\r
277 */\r
278 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r