1 #define SEEN_TRANSF_MAT_3x4_C
3 /*
4 * 3x4 transformation matrix to map points from projective 3-space into the projective plane
5 *
6 * Authors:
7 * Maximilian Albert <Anhalter42@gmx.de>
8 *
9 * Copyright (C) 2007 Authors
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
14 #include "transf_mat_3x4.h"
15 #include <gtk/gtk.h>
16 #include "svg/stringstream.h"
17 #include "syseq.h"
18 #include "libnr/nr-matrix.h"
19 #include "document.h"
20 #include "inkscape.h"
22 namespace Proj {
24 TransfMat3x4::TransfMat3x4 () {
25 for (unsigned int i = 0; i < 3; ++i) {
26 for (unsigned int j = 0; j < 4; ++j) {
27 tmat[i][j] = (i == j ? 1 : 0); // or should we initialize all values with 0? does it matter at all?
28 }
29 }
30 }
32 TransfMat3x4::TransfMat3x4 (Proj::Pt2 vp_x, Proj::Pt2 vp_y, Proj::Pt2 vp_z, Proj::Pt2 origin) {
33 for (unsigned int i = 0; i < 3; ++i) {
34 tmat[i][0] = vp_x[i];
35 tmat[i][1] = vp_y[i];
36 tmat[i][2] = vp_z[i];
37 tmat[i][3] = origin[i];
38 }
39 }
41 TransfMat3x4::TransfMat3x4(TransfMat3x4 const &rhs) {
42 for (unsigned int i = 0; i < 3; ++i) {
43 for (unsigned int j = 0; j < 4; ++j) {
44 tmat[i][j] = rhs.tmat[i][j];
45 }
46 }
47 }
49 Pt2
50 TransfMat3x4::column (Proj::Axis axis) const {
51 return Proj::Pt2 (tmat[0][axis], tmat[1][axis], tmat[2][axis]);
52 }
54 Pt2
55 TransfMat3x4::image (Pt3 const &point) {
56 double x = tmat[0][0] * point[0] + tmat[0][1] * point[1] + tmat[0][2] * point[2] + tmat[0][3] * point[3];
57 double y = tmat[1][0] * point[0] + tmat[1][1] * point[1] + tmat[1][2] * point[2] + tmat[1][3] * point[3];
58 double w = tmat[2][0] * point[0] + tmat[2][1] * point[1] + tmat[2][2] * point[2] + tmat[2][3] * point[3];
60 return Pt2 (x, y, w);
61 }
63 Pt3
64 TransfMat3x4::preimage (NR::Point const &pt, double coord, Proj::Axis axis) {
65 double x[4];
66 double v[3];
67 v[0] = pt[NR::X];
68 v[1] = pt[NR::Y];
69 v[2] = 1.0;
70 int index = (int) axis;
72 SysEq::SolutionKind sol = SysEq::gaussjord_solve<3,4>(tmat, x, v, index, coord, true);
74 if (sol != SysEq::unique) {
75 if (sol == SysEq::no_solution) {
76 g_print ("No solution. Please investigate.\n");
77 } else {
78 g_print ("Infinitely many solutions. Please investigate.\n");
79 }
80 }
81 return Pt3(x[0], x[1], x[2], x[3]);
82 }
84 void
85 TransfMat3x4::set_image_pt (Proj::Axis axis, Proj::Pt2 const &pt) {
86 // FIXME: Do we need to adapt the coordinates in any way or can we just use them as they are?
87 for (int i = 0; i < 3; ++i) {
88 tmat[i][axis] = pt[i];
89 }
90 }
92 void
93 TransfMat3x4::toggle_finite (Proj::Axis axis) {
94 g_return_if_fail (axis != Proj::W);
95 if (has_finite_image(axis)) {
96 NR::Point dir (column(axis).affine());
97 NR::Point origin (column(Proj::W).affine());
98 dir -= origin;
99 set_column (axis, Proj::Pt2(dir[NR::X], dir[NR::Y], 0));
100 } else {
101 Proj::Pt2 dir (column(axis));
102 Proj::Pt2 origin (column(Proj::W).affine());
103 dir = dir + origin;
104 dir[2] = 1.0;
105 set_column (axis, dir);
106 }
107 }
109 gchar *
110 TransfMat3x4::pt_to_str (Proj::Axis axis) {
111 Inkscape::SVGOStringStream os;
112 os << tmat[0][axis] << " : "
113 << tmat[1][axis] << " : "
114 << tmat[2][axis];
115 return g_strdup(os.str().c_str());
116 }
118 bool
119 TransfMat3x4::operator==(const TransfMat3x4 &rhs) const
120 {
121 // Should we allow a certain tolerance or "normalize" the matrices first?
122 for (int i = 0; i < 3; ++i) {
123 Proj::Pt2 pt1 = column(Proj::axes[i]);
124 Proj::Pt2 pt2 = rhs.column(Proj::axes[i]);
125 if (pt1 != pt2) {
126 return false;
127 }
128 }
129 return true;
130 }
132 /* multiply a projective matrix by an affine matrix */
133 TransfMat3x4
134 TransfMat3x4::operator*(NR::Matrix const &A) const {
135 TransfMat3x4 ret;
137 // Is it safe to always use the currently active document?
138 double h = sp_document_height(inkscape_active_document());
140 /*
141 * Note: The strange multiplication involving the document height is due to the buggy
142 * intertwining of SVG and document coordinates. Essentially, what we do is first
143 * convert from "real-world" to SVG coordinates, then apply the transformation A
144 * (by multiplying with the NR::Matrix) and then convert back from SVG to real-world
145 * coordinates. Maybe there is even a more Inkscape-ish way to achieve this?
146 * Once Inkscape has gotton rid of the two different coordiate systems, we can change
147 * this function to an ordinary matrix multiplication.
148 */
149 for (int j = 0; j < 4; ++j) {
150 ret.tmat[0][j] = A[0]*tmat[0][j] + A[2]*(h*tmat[2][j] - tmat[1][j]) + A[4]*tmat[2][j];
151 ret.tmat[1][j] = A[1]*tmat[0][j] + A[3]*(h*tmat[2][j] - tmat[1][j]) + A[5]*tmat[2][j];
152 ret.tmat[2][j] = tmat[2][j];
154 ret.tmat[1][j] = h*ret.tmat[2][j] - ret.tmat[1][j]; // switch back from SVG to desktop coordinates
155 }
157 return ret;
158 }
160 // FIXME: Shouldn't rather operator* call operator*= for efficiency? (Because in operator*=
161 // there is in principle no need to create a temporary object, which happens in the assignment)
162 TransfMat3x4 &
163 TransfMat3x4::operator*=(NR::Matrix const &A) {
164 *this = *this * A;
165 return *this;
166 }
168 void
169 TransfMat3x4::copy_tmat(double rhs[3][4]) {
170 for (int i = 0; i < 3; ++i) {
171 for (int j = 0; j < 4; ++j) {
172 rhs[i][j] = tmat[i][j];
173 }
174 }
175 }
177 void
178 TransfMat3x4::print () const {
179 g_print ("Transformation matrix:\n");
180 for (int i = 0; i < 3; ++i) {
181 g_print (" ");
182 for (int j = 0; j < 4; ++j) {
183 g_print ("%8.2f ", tmat[i][j]);
184 }
185 g_print ("\n");
186 }
187 }
189 void
190 TransfMat3x4::normalize_column (Proj::Axis axis) {
191 Proj::Pt2 new_col(column(axis));
192 new_col.normalize();
193 set_image_pt(axis, new_col);
194 }
197 } // namespace Proj
199 /*
200 Local Variables:
201 mode:c++
202 c-file-style:"stroustrup"
203 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
204 indent-tabs-mode:nil
205 fill-column:99
206 End:
207 */
208 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :