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