1
1
use crate :: core_types:: Vector3 ;
2
+ use euclid:: approxeq:: ApproxEq ;
2
3
3
4
/// Plane in hessian form.
4
5
#[ repr( C ) ]
@@ -20,4 +21,279 @@ impl Plane {
20
21
pub fn from_sys ( c : sys:: godot_plane ) -> Self {
21
22
unsafe { std:: mem:: transmute :: < sys:: godot_plane , Self > ( c) }
22
23
}
24
+
25
+ /// Creates a new `Plane` from the ['Vector3'](./type.Vector3.html) normal and the distance from the origin.
26
+ #[ inline]
27
+ pub fn new ( normal : Vector3 , d : f32 ) -> Plane {
28
+ Plane { normal, d }
29
+ }
30
+
31
+ /// Creates a new `Plane` from four floats.
32
+ /// a, b, c are used for the normal ['Vector3'](./type.Vector3.html).
33
+ /// d is the distance from the origin.
34
+ #[ inline]
35
+ pub fn from_coordinates ( a : f32 , b : f32 , c : f32 , d : f32 ) -> Plane {
36
+ Plane {
37
+ normal : Vector3 :: new ( a, b, c) ,
38
+ d,
39
+ }
40
+ }
41
+
42
+ /// Creates a new `Plane` from three [`Vector3`](./type.Vector3.html), given in clockwise order.
43
+ /// If all three points are collinear, returns `None`.
44
+ #[ inline]
45
+ pub fn from_points ( a : Vector3 , b : Vector3 , c : Vector3 ) -> Option < Plane > {
46
+ let normal = ( a - c) . cross ( a - b) . normalize ( ) ;
47
+
48
+ if normal. x . is_nan ( ) || normal. y . is_nan ( ) || normal. z . is_nan ( ) {
49
+ None
50
+ } else {
51
+ Some ( Plane {
52
+ normal,
53
+ d : normal. dot ( a) ,
54
+ } )
55
+ }
56
+ }
57
+
58
+ /// Returns the center of the `Plane`.
59
+ #[ inline]
60
+ pub fn center ( & self ) -> Vector3 {
61
+ self . normal * self . d
62
+ }
63
+
64
+ /// Returns the shortest distance from the `Plane` to `point`.
65
+ #[ inline]
66
+ pub fn distance_to ( & self , point : Vector3 ) -> f32 {
67
+ ( self . normal . dot ( point) ) - self . d
68
+ }
69
+
70
+ /// Returns `true` if `point` is inside the `Plane`.
71
+ /// `epislon` specifies the minimum threshold to be considered inside the `Plane`.
72
+ #[ inline]
73
+ pub fn has_point ( & self , point : Vector3 , epsilon : f32 ) -> bool {
74
+ let dist = self . distance_to ( point) . abs ( ) ;
75
+
76
+ dist <= epsilon
77
+ }
78
+
79
+ /// Returns the intersection point of the three planes `b`, `c` and this `Plane`.
80
+ /// Returns `None` if the 'Plane's don't intersect.
81
+ #[ inline]
82
+ pub fn intersect_3 ( & self , b : Plane , c : Plane ) -> Option < Vector3 > {
83
+ let a = & self ;
84
+
85
+ let denom = Vector3 :: cross ( a. normal , b. normal ) . dot ( c. normal ) ;
86
+
87
+ if denom. approx_eq ( & 0.0 ) {
88
+ None
89
+ } else {
90
+ Some (
91
+ ( ( Vector3 :: cross ( b. normal , c. normal ) * a. d )
92
+ + ( Vector3 :: cross ( c. normal , a. normal ) * b. d )
93
+ + ( Vector3 :: cross ( a. normal , b. normal ) * c. d ) )
94
+ / denom,
95
+ )
96
+ }
97
+ }
98
+
99
+ /// Returns the intersection point of a ray consisting of the position `from` and the direction normal `dir` with this plane/
100
+ /// Returns `None` if the ray doesn't intersect.
101
+ #[ inline]
102
+ pub fn intersects_ray ( & self , from : Vector3 , dir : Vector3 ) -> Option < Vector3 > {
103
+ let den = self . normal . dot ( dir) ;
104
+
105
+ if den. approx_eq ( & 0.0 ) {
106
+ return None ;
107
+ }
108
+
109
+ let dist = ( self . normal . dot ( from) - self . d ) / den;
110
+
111
+ if dist > std:: f32:: EPSILON {
112
+ return None ;
113
+ }
114
+
115
+ Some ( from + dir * -dist)
116
+ }
117
+
118
+ /// Returns the intersection point of a segment from `begin` to `end` with this `Plane`.
119
+ /// Returns `None` if the the segment doesn't intersect.
120
+ #[ inline]
121
+ pub fn intersects_segment ( & self , begin : Vector3 , end : Vector3 ) -> Option < Vector3 > {
122
+ let segment = begin - end;
123
+ let den = self . normal . dot ( segment) ;
124
+
125
+ if den. approx_eq ( & 0.0 ) {
126
+ return None ;
127
+ }
128
+
129
+ let dist = ( self . normal . dot ( begin) - self . d ) / den;
130
+
131
+ if dist < -std:: f32:: EPSILON || dist > ( 1.0 + std:: f32:: EPSILON ) {
132
+ return None ;
133
+ }
134
+
135
+ Some ( begin + segment * -dist)
136
+ }
137
+
138
+ /// Returns `true` if this `Plane` and `other` are approximately equal.
139
+ /// Determined by running `approx_eq` on both `normal` and `d`.
140
+ #[ inline]
141
+ pub fn approx_eq ( & self , other : Plane ) -> bool {
142
+ self . normal . approx_eq ( & other. normal ) && self . d . approx_eq ( & other. d )
143
+ }
144
+
145
+ /// Returns `true` if `point` is above the `Plane`.
146
+ #[ inline]
147
+ pub fn is_point_over ( & self , point : Vector3 ) -> bool {
148
+ self . normal . dot ( point) > self . d
149
+ }
150
+
151
+ /// Returns the `Plane` normalized.
152
+ #[ inline]
153
+ pub fn normalize ( mut self ) -> Plane {
154
+ let l = self . normal . length ( ) ;
155
+
156
+ if l == 0.0 {
157
+ self . normal = Vector3 :: new ( 0.0 , 0.0 , 0.0 ) ;
158
+ self . d = 0.0 ;
159
+ } else {
160
+ self . normal /= l;
161
+ self . d /= l;
162
+ }
163
+
164
+ self
165
+ }
166
+
167
+ /// Returns the orthogonal projection of `point` into a point in the `Plane`.
168
+ #[ inline]
169
+ pub fn project ( & self , point : Vector3 ) -> Vector3 {
170
+ point - self . normal * self . distance_to ( point)
171
+ }
172
+ }
173
+
174
+ #[ cfg( test) ]
175
+ mod test {
176
+ use super :: * ;
177
+
178
+ fn test_inputs ( ) -> ( Plane , Vector3 ) {
179
+ (
180
+ Plane :: from_coordinates ( 0.01 , 0.02 , 0.04 , 0.08 ) ,
181
+ Vector3 :: new ( 0.16 , 0.32 , 0.64 ) ,
182
+ )
183
+ }
184
+
185
+ #[ test]
186
+ fn from_points ( ) {
187
+ let a = Vector3 :: new ( -1.0 , 1.0 , 0.0 ) ;
188
+ let b = Vector3 :: new ( -1.0 , 0.0 , 0.0 ) ;
189
+ let c = Vector3 :: new ( 1.0 , 1.0 , 1.0 ) ;
190
+ let d = Vector3 :: new ( -1.0 , -1.0 , 0.0 ) ;
191
+
192
+ let expected_valid = Plane :: from_coordinates ( 0.447214 , 0.0 , -0.894427 , -0.447214 ) ;
193
+
194
+ assert ! ( Plane :: from_points( a, b, c)
195
+ . unwrap( )
196
+ . approx_eq( expected_valid) ) ;
197
+ assert_eq ! ( Plane :: from_points( a, b, d) , None ) ;
198
+ }
199
+
200
+ #[ test]
201
+ fn center ( ) {
202
+ let ( p, _v) = test_inputs ( ) ;
203
+
204
+ let expected = Vector3 :: new ( 0.0008 , 0.0016 , 0.0032 ) ;
205
+
206
+ assert ! ( p. center( ) . approx_eq( & expected) ) ;
207
+ }
208
+
209
+ #[ test]
210
+ fn distance_to ( ) {
211
+ let ( p, v) = test_inputs ( ) ;
212
+
213
+ let expected = -0.0464 ;
214
+
215
+ assert ! ( p. distance_to( v) . approx_eq( & expected) ) ;
216
+ }
217
+
218
+ #[ test]
219
+ fn has_point ( ) {
220
+ let p = Plane :: new ( Vector3 :: new ( 1.0 , 1.0 , 1.0 ) , 1.0 ) ;
221
+
222
+ let outside = Vector3 :: new ( 0.0 , 0.0 , 0.0 ) ;
223
+ let inside = Vector3 :: new ( 1.0 / 3.0 , 1.0 / 3.0 , 1.0 / 3.0 ) ;
224
+
225
+ assert ! ( !p. has_point( outside, 0.00001 ) ) ;
226
+ assert ! ( p. has_point( inside, 0.00001 ) ) ;
227
+ }
228
+
229
+ #[ test]
230
+ fn intersect_3 ( ) {
231
+ let ( p, _v) = test_inputs ( ) ;
232
+
233
+ let b = Plane :: from_coordinates ( 0.08 , 0.04 , 0.03 , 0.01 ) ;
234
+ let c = Plane :: from_coordinates ( 0.05 , 0.2 , 0.1 , 0.6 ) ;
235
+
236
+ let expected = Vector3 :: new ( -1.707317 , 2.95122 , 0.95122 ) ;
237
+
238
+ let d = Plane :: from_coordinates ( 0.01 , 0.02 , 0.4 , 0.16 ) ;
239
+ let e = Plane :: from_coordinates ( 0.01 , 0.02 , 0.4 , 0.32 ) ;
240
+
241
+ assert ! ( p. intersect_3( b, c) . unwrap( ) . approx_eq( & expected) ) ;
242
+ assert_eq ! ( p. intersect_3( d, e) , None ) ;
243
+ }
244
+
245
+ #[ test]
246
+ fn intersects_ray ( ) {
247
+ let ( p, v) = test_inputs ( ) ;
248
+
249
+ let expected = Vector3 :: new ( 0.16 , 2.64 , 0.64 ) ;
250
+
251
+ assert ! ( p
252
+ . intersects_ray( v, Vector3 :: new( 0.0 , 1.0 , 0.0 ) )
253
+ . unwrap( )
254
+ . approx_eq( & expected) ) ;
255
+ assert_eq ! ( p. intersects_ray( v, Vector3 :: new( 0.0 , -1.0 , 0.0 ) ) , None ) ;
256
+ }
257
+
258
+ #[ test]
259
+ fn intersects_segment ( ) {
260
+ let ( p, v) = test_inputs ( ) ;
261
+
262
+ let expected = Vector3 :: new ( 0.16 , 2.64 , 0.64 ) ;
263
+
264
+ assert ! ( p
265
+ . intersects_segment( v, Vector3 :: new( 0.16 , 10.0 , 0.64 ) )
266
+ . unwrap( )
267
+ . approx_eq( & expected) ) ;
268
+ assert_eq ! (
269
+ p. intersects_segment( v, Vector3 :: new( 0.16 , -10.0 , 0.64 ) ) ,
270
+ None
271
+ ) ;
272
+ }
273
+
274
+ #[ test]
275
+ fn is_point_over ( ) {
276
+ let ( p, v) = test_inputs ( ) ;
277
+
278
+ assert ! ( !p. is_point_over( v) ) ;
279
+ assert ! ( p. is_point_over( Vector3 :: new( 1.0 , 10.0 , 2.0 ) ) ) ;
280
+ }
281
+
282
+ #[ test]
283
+ fn normalize ( ) {
284
+ let ( p, _v) = test_inputs ( ) ;
285
+
286
+ assert ! ( p. normalize( ) . approx_eq( Plane :: from_coordinates(
287
+ 0.218218 , 0.436436 , 0.872872 , 1.745743
288
+ ) ) ) ;
289
+ }
290
+
291
+ #[ test]
292
+ fn project ( ) {
293
+ let ( p, v) = test_inputs ( ) ;
294
+
295
+ let expected = Vector3 :: new ( 0.160464 , 0.320928 , 0.641856 ) ;
296
+
297
+ assert ! ( p. project( v) . approx_eq( & expected) )
298
+ }
23
299
}
0 commit comments