1 /** @file
2 * @brief LPE perspective path effect implementation.
3 */
4 /* Authors:
5 * Maximilian Albert <maximilian.albert@gmail.com>
6 * Johan Engelen <j.b.c.engelen@utwente.nl>
7 *
8 * Copyright (C) 2007-2008 Authors
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
13 #include "persp3d.h"
14 //#include "transf_mat_3x4.h"
15 #include "document.h"
17 #include "live_effects/lpe-perspective_path.h"
18 #include "sp-item-group.h"
19 #include "knot-holder-entity.h"
21 #include "inkscape.h"
23 #include <2geom/path.h>
25 namespace Inkscape {
26 namespace LivePathEffect {
28 namespace PP {
30 class KnotHolderEntityOffset : public LPEKnotHolderEntity
31 {
32 public:
33 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
34 virtual Geom::Point knot_get();
35 };
37 } // namespace PP
39 LPEPerspectivePath::LPEPerspectivePath(LivePathEffectObject *lpeobject) :
40 Effect(lpeobject),
41 // initialise your parameters here:
42 scalex(_("Scale x"), _("Scale factor in x direction"), "scalex", &wr, this, 1.0),
43 scaley(_("Scale y"), _("Scale factor in y direction"), "scaley", &wr, this, 1.0),
44 offsetx(_("Offset x"), _("Offset in x direction"), "offsetx", &wr, this, 0.0),
45 offsety(_("Offset y"), _("Offset in y direction"), "offsety", &wr, this, 0.0),
46 uses_plane_xy(_("Uses XY plane?"), _("If true, put the path on the left side of an imaginary box, otherwise on the right side"), "uses_plane_xy", &wr, this, true)
47 {
48 // register all your parameters here, so Inkscape knows which parameters this effect has:
49 registerParameter( dynamic_cast<Parameter *>(&scalex) );
50 registerParameter( dynamic_cast<Parameter *>(&scaley) );
51 registerParameter( dynamic_cast<Parameter *>(&offsetx) );
52 registerParameter( dynamic_cast<Parameter *>(&offsety) );
53 registerParameter( dynamic_cast<Parameter *>(&uses_plane_xy) );
55 registerKnotHolderHandle(new PP::KnotHolderEntityOffset(), _("Adjust the origin"));
57 concatenate_before_pwd2 = true; // don't split the path into its subpaths
59 Persp3D *persp = persp3d_document_first_persp(inkscape_active_document());
61 Proj::TransfMat3x4 pmat = persp->perspective_impl->tmat;
63 pmat.copy_tmat(tmat);
64 }
66 LPEPerspectivePath::~LPEPerspectivePath()
67 {
69 }
71 void
72 LPEPerspectivePath::doBeforeEffect (SPLPEItem *lpeitem)
73 {
74 original_bbox(lpeitem, true);
75 }
77 Geom::Piecewise<Geom::D2<Geom::SBasis> >
78 LPEPerspectivePath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
79 {
80 using namespace Geom;
82 Piecewise<D2<SBasis> > path_a_pw = pwd2_in;
84 // FIXME: the minus sign is there because the SVG coordinate system goes down;
85 // remove this once we have unified coordinate systems
86 path_a_pw = path_a_pw + Geom::Point(offsetx, -offsety);
88 D2<Piecewise<SBasis> > B = make_cuts_independent(path_a_pw);
89 Piecewise<SBasis> preimage[4];
91 //Geom::Point orig = Geom::Point(bounds_X.min(), bounds_Y.middle());
92 //orig = Geom::Point(orig[X], sp_document_height(inkscape_active_document()) - orig[Y]);
94 //double offset = uses_plane_xy ? boundingbox_X.extent() : 0.0;
96 orig = Point(uses_plane_xy ? boundingbox_X.max() : boundingbox_X.min(), boundingbox_Y.middle());
98 /**
99 g_print ("Orig: (%8.2f, %8.2f)\n", orig[X], orig[Y]);
101 g_print ("B[1] - orig[1]: %8.2f\n", (B[1] - orig[1])[0].valueAt(0));
102 g_print ("B[0] - orig[0]: %8.2f\n", (B[0] - orig[0])[0].valueAt(0));
103 **/
105 if (uses_plane_xy) {
106 preimage[0] = (-B[0] + orig[0]) * scalex / 200.0;
107 preimage[1] = ( B[1] - orig[1]) * scaley / 400.0;
108 preimage[2] = B[0] - B[0]; // hack!
109 } else {
110 preimage[0] = B[0] - B[0]; // hack!
111 preimage[1] = (B[1] - orig[1]) * scaley / 400.0;
112 preimage[2] = (B[0] - orig[0]) * scalex / 200.0;
113 }
115 /* set perspective origin to first point of path */
116 tmat[0][3] = orig[0];
117 tmat[1][3] = orig[1];
119 /**
120 g_print ("preimage[1]: %8.2f\n", preimage[1][0].valueAt(0));
121 g_print ("preimage[2]: %8.2f\n", preimage[2][0].valueAt(0));
122 **/
124 Piecewise<SBasis> res[3];
125 for (int j = 0; j < 3; ++j) {
126 res[j] =
127 preimage[0] * tmat[j][0]
128 + preimage[1] * tmat[j][1]
129 + preimage[2] * tmat[j][2]
130 + tmat[j][3];
131 }
132 D2<Piecewise<SBasis> > result(divide(res[0],res[2], 3),
133 divide(res[1],res[2], 3));
135 Piecewise<D2<SBasis> > output = sectionize(result);
137 return output;
138 }
140 namespace PP {
142 // TODO: make this more generic
143 static LPEPerspectivePath *
144 get_effect(SPItem *item)
145 {
146 Effect *effect = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item));
147 if (effect->effectType() != PERSPECTIVE_PATH) {
148 g_print ("Warning: Effect is not of type LPEPerspectivePath!\n");
149 return NULL;
150 }
151 return static_cast<LPEPerspectivePath *>(effect);
152 }
154 void
155 KnotHolderEntityOffset::knot_set(Geom::Point const &p, Geom::Point const &origin, guint /*state*/)
156 {
157 using namespace Geom;
159 LPEPerspectivePath* lpe = get_effect(item);
161 Geom::Point const s = snap_knot_position(p);
163 lpe->offsetx.param_set_value((s - origin)[Geom::X]);
164 lpe->offsety.param_set_value(-(s - origin)[Geom::Y]); // additional minus sign is due to coordinate system flipping
166 // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
167 sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
168 }
170 Geom::Point
171 KnotHolderEntityOffset::knot_get()
172 {
173 LPEPerspectivePath* lpe = get_effect(item);
174 return lpe->orig + Geom::Point(lpe->offsetx, -lpe->offsety);
175 }
177 } // namespace PP
179 } //namespace LivePathEffect
180 } /* namespace Inkscape */
182 /*
183 Local Variables:
184 mode:c++
185 c-file-style:"stroustrup"
186 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
187 indent-tabs-mode:nil
188 fill-column:99
189 End:
190 */
191 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :