1use vec2f::{Line2f, Vec2f};
16
17#[derive(Clone, Copy, Debug, PartialEq)]
31pub struct Quad2f {
32 pub points: (Vec2f, Vec2f, Vec2f, Vec2f)
33}
34
35#[derive(Clone, Copy, Debug, PartialEq)]
37pub enum IBLerpResult {
38 NoSolution,
39 OneSolution(Vec2f),
41 TwoSolutions(Vec2f, Vec2f),
43 ManySolutions
44}
45
46#[derive(Clone, Copy, Debug, PartialEq)]
48pub enum InvBilerpResult {
49 NoSolution,
50 OneSolution(f32),
52 TwoSolutions(f32, f32),
54 ManySolutions
55}
56
57impl Quad2f {
58 pub fn new(a: Vec2f, b: Vec2f, c: Vec2f, d: Vec2f) -> Quad2f {
59 Quad2f {
60 points: (a, b, c, d)
61 }
62 }
63
64 pub fn iblerp(self, point: Vec2f) -> IBLerpResult {
70 let p0mp = self.points.0 - point;
71 let p1mp = self.points.1 - point;
72 let p0mp3 = self.points.0 - self.points.3;
73 let p1mp2 = self.points.1 - self.points.2;
74
75 let a = p0mp.cross(p0mp3);
76 let b0 = p0mp.cross(p1mp2);
77 let b1 = p1mp.cross(p0mp3);
78 let b = (b0 + b1) / 2.0;
79 let c = p1mp.cross(p1mp2);
80
81 let calc_st = |s| {
82 let den = (1.0 - s) * p0mp3.x + s * p1mp2.x;
83 let t = if den == 0.0 {
84 let rb = self.points.0.lerp(self.points.1, s);
88 let rt = self.points.3.lerp(self.points.2, s);
89 Line2f::new(rb, rt).closest_parametric_point(point)
90 }
91 else {
92 ((1.0 - s) * (p0mp.x) + s * p1mp.x) / den
93 };
94 Vec2f::new(s, t)
95 };
96
97 let den = a - (2.0 * b) + c;
98 if den == 0.0 {
99 let m = a - c;
100 if m == 0.0 {
101 if a == 0.0 {
102 IBLerpResult::ManySolutions
103 }
104 else {
105 IBLerpResult::NoSolution
106 }
107 }
108 else {
109 IBLerpResult::OneSolution(calc_st(a / m))
110 }
111 }
112 else {
113 let left = a - b;
114 let right = (b.powi(2) - a*c).sqrt();
115 let s0 = (left + right) / den;
116 let s1 = (left - right) / den;
117 IBLerpResult::TwoSolutions(calc_st(s0), calc_st(s1))
118 }
119 }
120
121 pub fn inv_bilerp_u(self, point: Vec2f) -> InvBilerpResult {
123 let p0mp = self.points.0 - point;
124 let p1mp = self.points.1 - point;
125 let p0mp3 = self.points.0 - self.points.3;
126 let p1mp2 = self.points.1 - self.points.2;
127
128 let a = p0mp.cross(p0mp3);
129 let b0 = p0mp.cross(p1mp2);
130 let b1 = p1mp.cross(p0mp3);
131 let b = (b0 + b1) / 2.0;
132 let c = p1mp.cross(p1mp2);
133
134 let den = a - (2.0 * b) + c;
135 if den == 0.0 {
136 let m = a - c;
137 if m == 0.0 {
138 if a == 0.0 {
139 InvBilerpResult::ManySolutions
140 }
141 else {
142 InvBilerpResult::NoSolution
143 }
144 }
145 else {
146 InvBilerpResult::OneSolution(a / m)
147 }
148 }
149 else {
150 let left = a - b;
151 let right = (b.powi(2) - a*c).sqrt();
152 let s0 = (left + right) / den;
153 let s1 = (left - right) / den;
154 InvBilerpResult::TwoSolutions(s0, s1)
155 }
156 }
157
158 pub fn lerp_bottom(self, u: f32) -> Vec2f {
159 self.points.0.lerp(self.points.1, u)
160 }
161
162 pub fn lerp_top(self, u: f32) -> Vec2f {
163 self.points.3.lerp(self.points.2, u)
164 }
165
166 pub fn lerp_left(self, v: f32) -> Vec2f {
167 self.points.0.lerp(self.points.3, v)
168 }
169
170 pub fn lerp_right(self, v: f32) -> Vec2f {
171 self.points.1.lerp(self.points.2, v)
172 }
173
174 pub fn blerp(self, uv: Vec2f) -> Vec2f {
175 let rb = self.points.0.lerp(self.points.1, uv.x);
176 let rt = self.points.3.lerp(self.points.2, uv.x);
177 rb.lerp(rt, uv.y)
178 }
179}
180
181#[cfg(test)]
182mod test {
183 use super::*;
184 use vec2f::vec2f;
185
186 #[test]
187 fn test_inv_bilerp_u() {
188 let q = Quad2f::new(vec2f(0, 0),
189 vec2f(4, 0),
190 vec2f(4, 4),
191 vec2f(0, 4));
192 assert_eq!(q.inv_bilerp_u(vec2f(2, 2)),
193 InvBilerpResult::OneSolution(0.5));
194 assert_eq!(q.inv_bilerp_u(vec2f(1, 2)),
195 InvBilerpResult::OneSolution(0.25));
196 assert_eq!(q.inv_bilerp_u(vec2f(1, 3)),
197 InvBilerpResult::OneSolution(0.25));
198 }
199
200 #[test]
201 fn test_iblerp() {
202 let q = Quad2f::new(vec2f(0, 0),
203 vec2f(4, 0),
204 vec2f(4, 4),
205 vec2f(0, 4));
206 assert_eq!(q.iblerp(vec2f(2, 2)),
207 IBLerpResult::OneSolution(vec2f(0.5, 0.5)));
208 }
209}