Code

copyedit
[inkscape.git] / src / live_effects / effect.cpp
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