1use crate::simul_eq::simul_eq_solve;
7
8pub struct TransBilinear {
24 mtx: [[f64; 2]; 4],
25 valid: bool,
26}
27
28impl TransBilinear {
29 pub fn new() -> Self {
31 Self {
32 mtx: [[0.0; 2]; 4],
33 valid: false,
34 }
35 }
36
37 pub fn new_quad_to_quad(src: &[f64; 8], dst: &[f64; 8]) -> Self {
39 let mut t = Self::new();
40 t.quad_to_quad(src, dst);
41 t
42 }
43
44 pub fn new_rect_to_quad(x1: f64, y1: f64, x2: f64, y2: f64, quad: &[f64; 8]) -> Self {
46 let mut t = Self::new();
47 t.rect_to_quad(x1, y1, x2, y2, quad);
48 t
49 }
50
51 pub fn new_quad_to_rect(quad: &[f64; 8], x1: f64, y1: f64, x2: f64, y2: f64) -> Self {
53 let mut t = Self::new();
54 t.quad_to_rect(quad, x1, y1, x2, y2);
55 t
56 }
57
58 pub fn quad_to_quad(&mut self, src: &[f64; 8], dst: &[f64; 8]) {
62 let mut left = [[0.0_f64; 4]; 4];
63 let mut right = [[0.0_f64; 2]; 4];
64
65 for i in 0..4 {
66 let ix = i * 2;
67 let iy = ix + 1;
68 left[i][0] = 1.0;
69 left[i][1] = src[ix] * src[iy];
70 left[i][2] = src[ix];
71 left[i][3] = src[iy];
72
73 right[i][0] = dst[ix];
74 right[i][1] = dst[iy];
75 }
76
77 self.valid = simul_eq_solve(&left, &right, &mut self.mtx);
78 }
79
80 pub fn rect_to_quad(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, quad: &[f64; 8]) {
82 let src = [x1, y1, x2, y1, x2, y2, x1, y2];
83 self.quad_to_quad(&src, quad);
84 }
85
86 pub fn quad_to_rect(&mut self, quad: &[f64; 8], x1: f64, y1: f64, x2: f64, y2: f64) {
88 let dst = [x1, y1, x2, y1, x2, y2, x1, y2];
89 self.quad_to_quad(quad, &dst);
90 }
91
92 pub fn is_valid(&self) -> bool {
94 self.valid
95 }
96
97 pub fn transform(&self, x: &mut f64, y: &mut f64) {
99 let tx = *x;
100 let ty = *y;
101 let xy = tx * ty;
102 *x = self.mtx[0][0] + self.mtx[1][0] * xy + self.mtx[2][0] * tx + self.mtx[3][0] * ty;
103 *y = self.mtx[0][1] + self.mtx[1][1] * xy + self.mtx[2][1] * tx + self.mtx[3][1] * ty;
104 }
105
106 pub fn begin(&self, x: f64, y: f64, step: f64) -> IteratorX {
108 IteratorX::new(x, y, step, &self.mtx)
109 }
110}
111
112impl Default for TransBilinear {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118pub struct IteratorX {
126 inc_x: f64,
127 inc_y: f64,
128 pub x: f64,
129 pub y: f64,
130}
131
132impl IteratorX {
133 fn new(tx: f64, ty: f64, step: f64, m: &[[f64; 2]; 4]) -> Self {
134 Self {
135 inc_x: m[1][0] * step * ty + m[2][0] * step,
136 inc_y: m[1][1] * step * ty + m[2][1] * step,
137 x: m[0][0] + m[1][0] * tx * ty + m[2][0] * tx + m[3][0] * ty,
138 y: m[0][1] + m[1][1] * tx * ty + m[2][1] * tx + m[3][1] * ty,
139 }
140 }
141
142 pub fn next(&mut self) {
144 self.x += self.inc_x;
145 self.y += self.inc_y;
146 }
147}
148
149#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_identity_rect() {
159 let src = [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0];
161 let dst = [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0];
162 let t = TransBilinear::new_quad_to_quad(&src, &dst);
163 assert!(t.is_valid());
164 let mut x = 0.5;
165 let mut y = 0.5;
166 t.transform(&mut x, &mut y);
167 assert!((x - 0.5).abs() < 1e-10);
168 assert!((y - 0.5).abs() < 1e-10);
169 }
170
171 #[test]
172 fn test_rect_to_quad_corners() {
173 let quad = [10.0, 10.0, 20.0, 10.0, 20.0, 20.0, 10.0, 20.0];
175 let t = TransBilinear::new_rect_to_quad(0.0, 0.0, 1.0, 1.0, &quad);
176 assert!(t.is_valid());
177
178 let mut x = 0.0;
180 let mut y = 0.0;
181 t.transform(&mut x, &mut y);
182 assert!((x - 10.0).abs() < 1e-10);
183 assert!((y - 10.0).abs() < 1e-10);
184
185 x = 1.0;
187 y = 1.0;
188 t.transform(&mut x, &mut y);
189 assert!((x - 20.0).abs() < 1e-10);
190 assert!((y - 20.0).abs() < 1e-10);
191 }
192
193 #[test]
194 fn test_quad_to_rect() {
195 let quad = [10.0, 10.0, 20.0, 10.0, 20.0, 20.0, 10.0, 20.0];
197 let t = TransBilinear::new_quad_to_rect(&quad, 0.0, 0.0, 1.0, 1.0);
198 assert!(t.is_valid());
199
200 let mut x = 10.0;
201 let mut y = 10.0;
202 t.transform(&mut x, &mut y);
203 assert!((x - 0.0).abs() < 1e-10);
204 assert!((y - 0.0).abs() < 1e-10);
205 }
206
207 #[test]
208 fn test_round_trip_parallelogram() {
209 let quad = [5.0, 0.0, 15.0, 2.0, 17.0, 12.0, 7.0, 10.0];
211 let forward = TransBilinear::new_rect_to_quad(0.0, 0.0, 10.0, 10.0, &quad);
212 let reverse = TransBilinear::new_quad_to_rect(&quad, 0.0, 0.0, 10.0, 10.0);
213 assert!(forward.is_valid());
214 assert!(reverse.is_valid());
215
216 let mut x = 5.0;
217 let mut y = 5.0;
218 forward.transform(&mut x, &mut y);
219 reverse.transform(&mut x, &mut y);
220 assert!((x - 5.0).abs() < 1e-8);
221 assert!((y - 5.0).abs() < 1e-8);
222 }
223
224 #[test]
225 fn test_iterator() {
226 let src = [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0];
227 let dst = [0.0, 0.0, 10.0, 0.0, 10.0, 10.0, 0.0, 10.0];
228 let t = TransBilinear::new_quad_to_quad(&src, &dst);
229
230 let mut it = t.begin(0.0, 0.0, 0.5);
231 assert!((it.x - 0.0).abs() < 1e-10);
232 assert!((it.y - 0.0).abs() < 1e-10);
233 it.next();
234 assert!((it.x - 5.0).abs() < 1e-10);
235 assert!((it.y - 0.0).abs() < 1e-10);
236 }
237
238 #[test]
239 fn test_default_is_invalid() {
240 let t = TransBilinear::new();
241 assert!(!t.is_valid());
242 }
243
244 #[test]
245 fn test_scaling_transform() {
246 let quad = [0.0, 0.0, 2.0, 0.0, 2.0, 2.0, 0.0, 2.0];
248 let t = TransBilinear::new_rect_to_quad(0.0, 0.0, 1.0, 1.0, &quad);
249 assert!(t.is_valid());
250
251 let mut x = 0.5;
252 let mut y = 0.5;
253 t.transform(&mut x, &mut y);
254 assert!((x - 1.0).abs() < 1e-10);
255 assert!((y - 1.0).abs() < 1e-10);
256 }
257
258 #[test]
259 fn test_translation_transform() {
260 let quad = [10.0, 20.0, 11.0, 20.0, 11.0, 21.0, 10.0, 21.0];
262 let t = TransBilinear::new_rect_to_quad(0.0, 0.0, 1.0, 1.0, &quad);
263 assert!(t.is_valid());
264
265 let mut x = 0.0;
266 let mut y = 0.0;
267 t.transform(&mut x, &mut y);
268 assert!((x - 10.0).abs() < 1e-10);
269 assert!((y - 20.0).abs() < 1e-10);
270 }
271}