ProjGeom 1.0.11
Loading...
Searching...
No Matches
transform.hpp
Go to the documentation of this file.
1
5#pragma once
6
7#include <array>
8#include <cstdint>
9#include <stdexcept>
10
11#include "fractions.hpp"
12#include "pg_object.hpp"
13
14namespace fun {
15
22 class Transform {
23 public:
24 using Mat3x3 = std::array<std::array<Fraction, 3>, 3>;
25
30 constexpr explicit Transform(Mat3x3 matrix) : matrix_{std::move(matrix)} {}
31
32 // ---- factory methods ------------------------------------------------
33
40 static constexpr auto identity() -> Transform {
41 const Fraction Z{0, 1}, O{1, 1};
42 return Transform{Mat3x3{{{{O, Z, Z}}, {{Z, O, Z}}, {{Z, Z, O}}}}};
43 }
44
55 static constexpr auto translation(std::int64_t tx, std::int64_t ty) -> Transform {
56 const Fraction Z{0, 1}, O{1, 1};
57 return Transform{
58 Mat3x3{{{{O, Z, Fraction{tx, 1}}}, {{Z, O, Fraction{ty, 1}}}, {{Z, Z, O}}}}};
59 }
60
75 static constexpr auto rotation(const Fraction& angle_cos, const Fraction& angle_sin)
76 -> Transform {
77 const Fraction Z{0, 1};
78 return Transform{Mat3x3{{{{angle_cos, -angle_sin, Z}},
79 {{angle_sin, angle_cos, Z}},
80 {{Z, Z, Fraction{1, 1}}}}}};
81 }
82
97 static constexpr auto scaling(const Fraction& sx, const Fraction& sy) -> Transform {
98 const Fraction Z{0, 1};
99 return Transform{Mat3x3{{{{sx, Z, Z}}, {{Z, sy, Z}}, {{Z, Z, Fraction{1, 1}}}}}};
100 }
101
116 static constexpr auto shear(const Fraction& shx, const Fraction& shy) -> Transform {
117 const Fraction Z{0, 1}, O{1, 1};
118 return Transform{Mat3x3{{{{O, shx, Z}}, {{shy, O, Z}}, {{Z, Z, O}}}}};
119 }
120
121 // ---- operations -----------------------------------------------------
122
130 constexpr auto compose(const Transform& other) const -> Transform {
131 Mat3x3 result{};
132 for (int i = 0; i < 3; ++i) {
133 for (int j = 0; j < 3; ++j) {
134 Fraction sum{0, 1};
135 for (int k = 0; k < 3; ++k) {
136 sum = sum + matrix_[i][k] * other.matrix_[k][j];
137 }
138 result[i][j] = sum;
139 }
140 }
141 return Transform{result};
142 }
143
151 constexpr auto apply_point(const PgPoint& point) const -> PgPoint {
152 const Fraction x{point.coord[0], 1};
153 const Fraction y{point.coord[1], 1};
154 const Fraction z{point.coord[2], 1};
155
156 const auto& m = matrix_;
157 const auto xn = m[0][0] * x + m[0][1] * y + m[0][2] * z;
158 const auto yn = m[1][0] * x + m[1][1] * y + m[1][2] * z;
159 const auto zn = m[2][0] * x + m[2][1] * y + m[2][2] * z;
160
161 return PgPoint{
162 {xn.numer() / xn.denom(), yn.numer() / yn.denom(), zn.numer() / zn.denom()}};
163 }
164
172 constexpr auto apply_line(const PgLine& line) const -> PgLine {
173 const auto inv = inverse();
174 const Fraction x{line.coord[0], 1};
175 const Fraction y{line.coord[1], 1};
176 const Fraction z{line.coord[2], 1};
177
178 const auto& m = inv.matrix_;
179 const auto xn = m[0][0] * x + m[1][0] * y + m[2][0] * z;
180 const auto yn = m[0][1] * x + m[1][1] * y + m[2][1] * z;
181 const auto zn = m[0][2] * x + m[1][2] * y + m[2][2] * z;
182
183 return PgLine{
184 {xn.numer() / xn.denom(), yn.numer() / yn.denom(), zn.numer() / zn.denom()}};
185 }
186
194 constexpr auto inverse() const -> Transform {
195 const auto& m = matrix_;
196 const auto& a = m[0][0];
197 const auto& b = m[0][1];
198 const auto& c = m[0][2];
199 const auto& d = m[1][0];
200 const auto& e = m[1][1];
201 const auto& f = m[1][2];
202 const auto& g = m[2][0];
203 const auto& h = m[2][1];
204 const auto& i_ = m[2][2];
205
206 const auto det = a * (e * i_ - f * h) - b * (d * i_ - f * g) + c * (d * h - e * g);
207 if (det == Fraction{0, 1}) {
208 throw std::domain_error{"Cannot invert singular transformation matrix"};
209 }
210 const auto inv_det = Fraction{1, 1} / det;
211
212 return Transform {
213 Mat3x3{{{
214 {inv_det * (e * i_ - f * h), inv_det * (c * h - b * i_),
215 inv_det * (b * f - c * e)},
216 {inv_det * (f * g - d * i_), inv_det * (a * i_ - c * g),
217 inv_det * (c * d - a * f)},
218 {inv_det * (d * h - e * g), inv_det * (b * g - a * h),
219 inv_det * (a * e - b * d)},
220 }}};
221 }
222
224 constexpr auto matrix() const -> const Mat3x3& { return matrix_; }
225
226 constexpr auto operator==(const Transform& other) const->bool {
227 return matrix_ == other.matrix_;
228 }
229
230 constexpr auto operator!=(const Transform& other) const->bool {
231 return !(*this == other);
232 }
233
234 private:
235 Mat3x3 matrix_;
236 };
237
238 // ---- convenience free functions -----------------------------------------
239
247 inline constexpr auto rotate_point(const PgPoint& point, const Fraction& angle_cos,
248 const Fraction& angle_sin) -> PgPoint {
249 return Transform::rotation(angle_cos, angle_sin).apply_point(point);
250 }
251
259 inline constexpr auto translate_point(const PgPoint& point, std::int64_t tx,
260 std::int64_t ty) -> PgPoint {
261 return Transform::translation(tx, ty).apply_point(point);
262 }
263
271 inline constexpr auto scale_point(const PgPoint& point, const Fraction& sx,
272 const Fraction& sy) -> PgPoint {
273 return Transform::scaling(sx, sy).apply_point(point);
274 }
275
276 } // namespace fun
PG Line.
Definition pg_object.hpp:250
PG Point.
Definition pg_object.hpp:235
Definition ck_concepts.hpp:11
Core projective geometry object template (pg_object, PgObject, PgPoint, PgLine).
std::array< int64_t, 3 > coord
Definition pg_object.hpp:132