@@ -9,24 +9,26 @@ use std::ops::Neg;
9
9
use godot_ffi as sys;
10
10
use sys:: { ffi_methods, GodotFfi } ;
11
11
12
- use super :: { is_equal_approx, real, Vector3 } ;
12
+ use super :: { is_zero_approx, is_equal_approx, real, Vector3 } ;
13
+
14
+ pub const CMP_EPSILON : real = 0.00001 ;
13
15
14
16
/// 3D plane in [Hessian normal form](https://mathworld.wolfram.com/HessianNormalForm.html).
15
17
///
16
18
/// The Hessian form defines all points `point` which satisfy the equation
17
19
/// `dot(normal, point) + d == 0`, where `normal` is the normal vector and `d`
18
20
/// the distance from the origin.
19
21
///
20
- /// Currently most methods are only available through [`InnerPlane`](super::inner::InnerPlane).
21
- ///
22
22
/// Note: almost all methods on `Plane` require that the `normal` vector have
23
23
/// unit length and will panic if this invariant is violated. This is not separately
24
24
/// annotated for each method.
25
25
#[ derive( Copy , Clone , PartialEq , Debug ) ]
26
26
#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
27
27
#[ repr( C ) ]
28
28
pub struct Plane {
29
+ /// The plane's normal component.
29
30
pub normal : Vector3 ,
31
+ /// The plane's d component.
30
32
pub d : real ,
31
33
}
32
34
@@ -105,6 +107,90 @@ impl Plane {
105
107
}
106
108
}
107
109
110
+ /// Returns the shortest distance from the `self` `Plane` to the provided `Vector3`. The distance
111
+ /// will be positive if the `Vector3` is above the `Plane`, and will be negative if the `Vector3`
112
+ /// is below the `Plane`.
113
+ ///
114
+ /// _Godot equivalent: `Plane.distance_to(Vector3 point)`_
115
+ #[ inline]
116
+ pub fn distance_to ( & self , point : Vector3 ) -> real {
117
+ self . normal . dot ( point) - self . d
118
+ }
119
+
120
+ /// Returns the center of the `self` `Plane` in `Vector3` form.
121
+ ///
122
+ /// _Godot equivalent: `Plane.get_center()`_
123
+ #[ inline]
124
+ pub fn get_center ( & self ) -> Vector3 {
125
+ self . normal * self . d
126
+ }
127
+
128
+ /// Returns `true` if the specified `Vector3` is inside the `self` `Plane`, will return otherwise
129
+ /// if not. Tolerance of the function unless specified will be using the default value of 1e-05.
130
+ ///
131
+ /// _Godot equivalent: `Plane.has_point(Vector3 point, float tolerance=1e-05)`_
132
+ #[ inline]
133
+ pub fn has_point ( & self , point : Vector3 , tolerance : Option < real > ) -> bool {
134
+ let dist: real = ( self . normal . dot ( point) - self . d ) . abs ( ) ;
135
+ dist <= tolerance. unwrap_or ( CMP_EPSILON )
136
+ }
137
+
138
+ /// Returns the intersection point of the `Plane`s of the current `Plane`, `b`, and `c` enclosed
139
+ /// in `Some`. If no intersection point is found, `None` is returned.
140
+ ///
141
+ /// _Godot equivalent: `Plane.intersect_3(Place b, Plane c)`_
142
+ #[ inline]
143
+ pub fn intersect_3 ( & self , b : & Self , c : & Self ) -> Option < Vector3 > {
144
+ let normal0: Vector3 = self . normal ;
145
+ let normal1: Vector3 = b. normal ;
146
+ let normal2: Vector3 = c. normal ;
147
+ let denom: real = normal0. cross ( normal1) . dot ( normal2) ;
148
+ if is_zero_approx ( denom) {
149
+ return None ;
150
+ }
151
+ let result = ( ( normal1. cross ( normal2) * self . d ) +
152
+ ( normal2. cross ( normal0) * b. d ) +
153
+ ( normal0. cross ( normal1) * c. d ) ) /
154
+ denom;
155
+ Some ( result)
156
+ }
157
+
158
+ /// Returns the intersection point of a ray consisting of the position `from` and the direction
159
+ /// normal `dir` with the `self` `Plane` enclosed in `Some`. If no intersection is found, `None`
160
+ /// is returned.
161
+ ///
162
+ /// _Godot equivalent: `Plane.intersects_ray(Vector3 from, Vector3 dir)`_
163
+ #[ inline]
164
+ pub fn intersects_ray ( & self , from : Vector3 , dir : Vector3 ) -> Option < Vector3 > {
165
+ let den: real = self . normal . dot ( dir) ;
166
+ if is_zero_approx ( den) {
167
+ return None ;
168
+ }
169
+ let dist: real = ( self . normal . dot ( from) - self . d ) / den;
170
+ if dist > CMP_EPSILON {
171
+ return None ;
172
+ }
173
+ Some ( from + dir * -dist)
174
+ }
175
+
176
+ /// Returns the intersection point of a segment from position `from` to position `to` with the
177
+ /// `self` `Plane` enclosed in `Some`. If no intersection is found, `None` will be returned.
178
+ ///
179
+ /// _Godot equivalent: `Plane.intersects_segment(Vector3 from, Vector3 to)`_
180
+ #[ inline]
181
+ pub fn intersects_segment ( & self , from : Vector3 , to : Vector3 ) -> Option < Vector3 > {
182
+ let segment: Vector3 = from - to;
183
+ let den: real = self . normal . dot ( segment) ;
184
+ if is_zero_approx ( den) {
185
+ return None ;
186
+ }
187
+ let dist: real = ( self . normal . dot ( from) - self . d ) / den;
188
+ if dist < -CMP_EPSILON || dist > ( 1.0 + CMP_EPSILON ) {
189
+ return None ;
190
+ }
191
+ Some ( from + segment * -dist)
192
+ }
193
+
108
194
/// Returns `true` if the two `Plane`s are approximately equal, by calling `is_equal_approx` on
109
195
/// `normal` and `d` or on `-normal` and `-d`.
110
196
///
@@ -115,6 +201,39 @@ impl Plane {
115
201
|| ( self . normal . is_equal_approx ( -other. normal ) && is_equal_approx ( self . d , -other. d ) )
116
202
}
117
203
204
+ /// Returns `true` if the `self` `Plane` is finite by calling `is_finite` on `normal` and `d`.
205
+ ///
206
+ /// _Godot equivalent: `Plane.is_finite()`_
207
+ #[ inline]
208
+ pub fn is_finite ( & self ) -> bool {
209
+ self . normal . is_finite ( ) && self . d . is_finite ( )
210
+ }
211
+
212
+ /// Returns `true` if `point` is located above the `self` `Plane`.
213
+ ///
214
+ /// _Godot equivalent: `Plane.is_point_over(Vector3 point)`_
215
+ #[ inline]
216
+ pub fn is_point_over ( & self , point : Vector3 ) -> bool {
217
+ self . normal . dot ( point) > self . d
218
+ }
219
+
220
+ /// Returns a normalized copy of the `self` `Plane`.
221
+ ///
222
+ /// _Godot equivalent: `Plane.normalized()`_
223
+ #[ inline]
224
+ pub fn normalized ( & self ) -> Self {
225
+ self . normal . normalized ( ) ;
226
+ self . clone ( )
227
+ }
228
+
229
+ /// Returns a orthogonal of the variable `point` into a point in the `self` `Plane`.
230
+ ///
231
+ /// _Godot equivalent: `Plane.project(Vector3 point)`_
232
+ #[ inline]
233
+ pub fn project ( & self , point : Vector3 ) -> Vector3 {
234
+ point - self . normal * self . distance_to ( point)
235
+ }
236
+
118
237
#[ inline]
119
238
fn assert_normalized ( self ) {
120
239
assert ! (
@@ -202,4 +321,36 @@ mod test {
202
321
203
322
crate :: builtin:: test_utils:: roundtrip ( & plane, expected_json) ;
204
323
}
324
+
325
+ /// Tests plane methods.
326
+ #[ test]
327
+ fn tests_methods ( ) {
328
+ // Random planes including (0.0, 0.0, 0.0).
329
+ let a: Plane = Plane :: new ( Vector3 :: new ( 1.0 , 2.0 , 3.0 ) . normalized ( ) , 0.0 ) ;
330
+ let b: Plane = Plane :: new ( Vector3 :: new ( 0.5 , 2.1 , 7.1 ) . normalized ( ) , 0.0 ) ;
331
+ let c: Plane = Plane :: new ( Vector3 :: new ( 4.3 , 9.2 , 3.14 ) . normalized ( ) , 0.0 ) ;
332
+ let d: Vector3 = Vector3 :: new ( 0.0 , 0.0 , 0.0 ) ;
333
+ let e: Vector3 = Vector3 :: new ( 0.0 , 0.0 , 1.0 ) ;
334
+ assert_eq ! ( a. distance_to( d) , 0.0 ) ;
335
+ assert_eq ! ( a. get_center( ) , d) ;
336
+ assert_eq ! ( a. has_point( d, None ) , true ) ;
337
+ assert_eq ! ( match a. intersect_3( & b, & c) {
338
+ Some ( x) => x,
339
+ None => Vector3 :: new( 1.0 , 1.0 , 1.0 ) ,
340
+ } , d) ;
341
+ assert_eq ! ( a. is_finite( ) , true ) ;
342
+ assert_eq ! ( a. is_point_over( d) , false ) ;
343
+ assert_eq ! ( a. is_point_over( e) , true ) ;
344
+ assert_eq ! ( a. is_point_over( -e) , false ) ;
345
+ assert_eq ! ( match a. intersects_ray( d, e) {
346
+ Some ( x) => x,
347
+ None => Vector3 :: new( 1.0 , 1.0 , 1.0 ) ,
348
+ } , d) ;
349
+ assert_eq ! ( match a. intersects_segment( d, e) {
350
+ Some ( x) => x,
351
+ None => Vector3 :: new( 1.0 , 1.0 , 1.0 ) ,
352
+ } , d) ;
353
+ assert_eq ! ( a. is_equal_approx( & a. normalized( ) ) , true ) ;
354
+ assert_eq ! ( a. project( Plane :: new( a. normalized( ) . normal, 3.0 ) . get_center( ) ) , a. get_center( ) ) ;
355
+ }
205
356
}
0 commit comments