1 /**
2 Axis-Aligned Bounding Box.
3 
4 Copyright:
5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.  
6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)  
7 Copyright (c) 2017-2018 Godot-D contributors  
8 
9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
10 
11 
12 */
13 module godot.core.aabb;
14 
15 import godot.core.defs;
16 import godot.core.vector3;
17 import godot.core.plane;
18 
19 import std.algorithm.mutation : swap;
20 
21 /**
22 AABB consists of a position, a size, and several utility functions. It is typically used for fast overlap tests.
23 */
24 struct AABB
25 {
26 	@nogc nothrow:
27 	
28 	Vector3 pos;
29 	Vector3 size;
30 	
31 	bool hasNoArea() const
32 	{
33 		return (size.x<=CMP_EPSILON || size.y<=CMP_EPSILON  || size.z<=CMP_EPSILON);
34 	}
35 
36 	bool hasNoSurface() const
37 	{
38 		return (size.x<=CMP_EPSILON && size.y<=CMP_EPSILON  && size.z<=CMP_EPSILON);
39 	}
40 	
41 	bool intersects(in AABB p_aabb) const
42 	{
43 		if ( pos.x >= (p_aabb.pos.x + p_aabb.size.x) )
44 			return false;
45 		if ( (pos.x+size.x) <= p_aabb.pos.x  )
46 			return false;
47 		if ( pos.y >= (p_aabb.pos.y + p_aabb.size.y) )
48 			return false;
49 		if ( (pos.y+size.y) <= p_aabb.pos.y  )
50 			return false;
51 		if ( pos.z >= (p_aabb.pos.z + p_aabb.size.z) )
52 			return false;
53 		if ( (pos.z+size.z) <= p_aabb.pos.z  )
54 			return false;
55 		return true;
56 	}
57 	
58 	bool intersectsInclusive(in AABB p_aabb) const
59 	{
60 		if ( pos.x > (p_aabb.pos.x + p_aabb.size.x) )
61 			return false;
62 		if ( (pos.x+size.x) < p_aabb.pos.x  )
63 			return false;
64 		if ( pos.y > (p_aabb.pos.y + p_aabb.size.y) )
65 			return false;
66 		if ( (pos.y+size.y) < p_aabb.pos.y  )
67 			return false;
68 		if ( pos.z > (p_aabb.pos.z + p_aabb.size.z) )
69 			return false;
70 		if ( (pos.z+size.z) < p_aabb.pos.z  )
71 			return false;
72 		return true;
73 	}
74 	
75 	bool encloses(in AABB p_aabb) const
76 	{
77 		Vector3 src_min=pos;
78 		Vector3 src_max=pos+size;
79 		Vector3 dst_min=p_aabb.pos;
80 		Vector3 dst_max=p_aabb.pos+p_aabb.size;
81 	
82 		return  (
83 			(src_min.x <= dst_min.x) &&
84 			(src_max.x > dst_max.x) &&
85 			(src_min.y <= dst_min.y) &&
86 			(src_max.y > dst_max.y) &&
87 			(src_min.z <= dst_min.z) &&
88 			(src_max.z > dst_max.z) );
89 	
90 	}
91 	
92 	Vector3 getSupport(in Vector3 p_normal) const
93 	{
94 		Vector3 half_extents = size * 0.5;
95 		Vector3 ofs = pos + half_extents;
96 		
97 		return Vector3(
98 			(p_normal.x>0) ? -half_extents.x : half_extents.x,
99 			(p_normal.y>0) ? -half_extents.y : half_extents.y,
100 			(p_normal.z>0) ? -half_extents.z : half_extents.z
101 		)+ofs;
102 	}
103 	
104 	
105 	Vector3 getEndpoint(int p_point) const
106 	{
107 		switch(p_point)
108 		{
109 			case 0: return Vector3( pos.x, pos.y, pos.z);
110 			case 1: return Vector3( pos.x, pos.y, pos.z+size.z);
111 			case 2: return Vector3( pos.x, pos.y+size.y, pos.z);
112 			case 3: return Vector3( pos.x, pos.y+size.y, pos.z+size.z);
113 			case 4: return Vector3( pos.x+size.x, pos.y, pos.z);
114 			case 5: return Vector3( pos.x+size.x, pos.y, pos.z+size.z);
115 			case 6: return Vector3( pos.x+size.x, pos.y+size.y, pos.z);
116 			case 7: return Vector3( pos.x+size.x, pos.y+size.y, pos.z+size.z);
117 			default: assert(0); ///ERR_FAIL_V(Vector3())
118 		}
119 	}
120 	
121 	bool intersectsConvexShape(in Plane[] p_planes) const
122 	{
123 		Vector3 half_extents = size * 0.5;
124 		Vector3 ofs = pos + half_extents;
125 	
126 		foreach(const ref p; p_planes)
127 		{
128 			Vector3 point = Vector3(
129 				(p.normal.x>0) ? -half_extents.x : half_extents.x,
130 				(p.normal.y>0) ? -half_extents.y : half_extents.y,
131 				(p.normal.z>0) ? -half_extents.z : half_extents.z
132 			);
133 			point+=ofs;
134 			if (p.isPointOver(point))
135 				return false;
136 		}
137 	
138 		return true;
139 	}
140 	
141 	bool hasPoint(in Vector3 p_point) const
142 	{
143 		if (p_point.x<pos.x)
144 			return false;
145 		if (p_point.y<pos.y)
146 			return false;
147 		if (p_point.z<pos.z)
148 			return false;
149 		if (p_point.x>pos.x+size.x)
150 			return false;
151 		if (p_point.y>pos.y+size.y)
152 			return false;
153 		if (p_point.z>pos.z+size.z)
154 			return false;
155 		
156 		return true;
157 	}
158 	
159 	
160 	void expandTo(in Vector3 p_vector)
161 	{
162 		Vector3 begin=pos;
163 		Vector3 end=pos+size;
164 		
165 		if (p_vector.x<begin.x)
166 			begin.x=p_vector.x;
167 		if (p_vector.y<begin.y)
168 			begin.y=p_vector.y;
169 		if (p_vector.z<begin.z)
170 			begin.z=p_vector.z;
171 		
172 		if (p_vector.x>end.x)
173 			end.x=p_vector.x;
174 		if (p_vector.y>end.y)
175 			end.y=p_vector.y;
176 		if (p_vector.z>end.z)
177 			end.z=p_vector.z;
178 		
179 		pos=begin;
180 		size=end-begin;
181 	}
182 	
183 	void projectRangeInPlane(in Plane p_plane, out real_t r_min, out real_t r_max) const
184 	{
185 		Vector3 half_extents = Vector3( size.x * 0.5, size.y * 0.5, size.z * 0.5 );
186 		Vector3 center = Vector3( pos.x + half_extents.x, pos.y + half_extents.y, pos.z + half_extents.z );
187 	
188 		real_t length = p_plane.normal.abs().dot(half_extents);
189 		real_t distance = p_plane.distanceTo( center );
190 		r_min = distance - length;
191 		r_max = distance + length;
192 	}
193 	
194 	real_t getLongestAxisSize() const
195 	{
196 		real_t max_size=size.x;
197 		
198 		if (size.y > max_size ) {
199 			max_size=size.y;
200 		}
201 		
202 		if (size.z > max_size ) {
203 			max_size=size.z;
204 		}
205 		
206 		return max_size;
207 	}
208 	
209 	real_t getShortestAxisSize() const
210 	{
211 		real_t max_size=size.x;
212 		
213 		if (size.y < max_size ) {
214 			max_size=size.y;
215 		}
216 		
217 		if (size.z < max_size ) {
218 			max_size=size.z;
219 		}
220 		
221 		return max_size;
222 	}
223 	
224 	bool smitsIntersectRay(in Vector3 from, in Vector3 dir, real_t t0, real_t t1) const
225 	{
226 		real_t divx=1.0/dir.x;
227 		real_t divy=1.0/dir.y;
228 		real_t divz=1.0/dir.z;
229 		
230 		Vector3 upbound=pos+size;
231 		real_t tmin, tmax, tymin, tymax, tzmin, tzmax;
232 		if (dir.x >= 0) {
233 			tmin = (pos.x - from.x) * divx;
234 			tmax = (upbound.x - from.x) * divx;
235 		}
236 		else {
237 			tmin = (upbound.x - from.x) * divx;
238 			tmax = (pos.x - from.x) * divx;
239 		}
240 		if (dir.y >= 0) {
241 			tymin = (pos.y - from.y) * divy;
242 			tymax = (upbound.y - from.y) * divy;
243 		}
244 		else {
245 			tymin = (upbound.y - from.y) * divy;
246 			tymax = (pos.y - from.y) * divy;
247 		}
248 		if ( (tmin > tymax) || (tymin > tmax) )
249 			return false;
250 		if (tymin > tmin)
251 				tmin = tymin;
252 		if (tymax < tmax)
253 			tmax = tymax;
254 		if (dir.z >= 0) {
255 			tzmin = (pos.z - from.z) * divz;
256 			tzmax = (upbound.z - from.z) * divz;
257 		}
258 		else {
259 			tzmin = (upbound.z - from.z) * divz;
260 			tzmax = (pos.z - from.z) * divz;
261 		}
262 		if ( (tmin > tzmax) || (tzmin > tmax) )
263 			return false;
264 		if (tzmin > tmin)
265 			tmin = tzmin;
266 		if (tzmax < tmax)
267 			tmax = tzmax;
268 		return ( (tmin < t1) && (tmax > t0) );
269 	}
270 	
271 	void growBy(real_t p_amount)
272 	{
273 		pos.x-=p_amount;
274 		pos.y-=p_amount;
275 		pos.z-=p_amount;
276 		size.x+=2.0*p_amount;
277 		size.y+=2.0*p_amount;
278 		size.z+=2.0*p_amount;
279 	}
280 	
281 	
282 	real_t getArea() const
283 	{
284 		return size.x*size.y*size.z;
285 	}
286 	
287 	void mergeWith(in AABB p_aabb)
288 	{
289 		Vector3 beg_1,beg_2;
290 		Vector3 end_1,end_2;
291 		Vector3 min,max;
292 		
293 		beg_1=pos;
294 		beg_2=p_aabb.pos;
295 		end_1=Vector3(size.x,size.y,size.z)+beg_1;
296 		end_2=Vector3(p_aabb.size.x,p_aabb.size.y,p_aabb.size.z)+beg_2;
297 		
298 		min.x=(beg_1.x<beg_2.x)?beg_1.x:beg_2.x;
299 		min.y=(beg_1.y<beg_2.y)?beg_1.y:beg_2.y;
300 		min.z=(beg_1.z<beg_2.z)?beg_1.z:beg_2.z;
301 		
302 		max.x=(end_1.x>end_2.x)?end_1.x:end_2.x;
303 		max.y=(end_1.y>end_2.y)?end_1.y:end_2.y;
304 		max.z=(end_1.z>end_2.z)?end_1.z:end_2.z;
305 		
306 		pos=min;
307 		size=max-min;
308 	}
309 	
310 	AABB intersection(in AABB p_aabb) const
311 	{
312 		Vector3 src_min=pos;
313 		Vector3 src_max=pos+size;
314 		Vector3 dst_min=p_aabb.pos;
315 		Vector3 dst_max=p_aabb.pos+p_aabb.size;
316 		
317 		Vector3 min,max;
318 		
319 		if (src_min.x > dst_max.x || src_max.x < dst_min.x )
320 			return AABB();
321 		else
322 		{
323 			min.x= ( src_min.x > dst_min.x ) ? src_min.x :dst_min.x;
324 			max.x= ( src_max.x < dst_max.x ) ? src_max.x :dst_max.x;
325 		}
326 		
327 		if (src_min.y > dst_max.y || src_max.y < dst_min.y )
328 			return AABB();
329 		else
330 		{
331 			min.y= ( src_min.y > dst_min.y ) ? src_min.y :dst_min.y;
332 			max.y= ( src_max.y < dst_max.y ) ? src_max.y :dst_max.y;
333 		}
334 		
335 		if (src_min.z > dst_max.z || src_max.z < dst_min.z )
336 			return AABB();
337 		else
338 		{
339 			min.z= ( src_min.z > dst_min.z ) ? src_min.z :dst_min.z;
340 			max.z= ( src_max.z < dst_max.z ) ? src_max.z :dst_max.z;
341 		}
342 		
343 		
344 		return AABB( min, max-min );
345 	}
346 	
347 	bool intersectsRay(in Vector3 p_from, in Vector3 p_dir, Vector3* r_clip = null, Vector3* r_normal = null) const
348 	{
349 		Vector3 c1, c2;
350 		Vector3 end = pos+size;
351 		real_t near=-1e20;
352 		real_t far=1e20;
353 		int axis=0;
354 	
355 		for (int i=0;i<3;i++)
356 		{
357 			if (p_dir[i] == 0)
358 			{
359 				if ((p_from[i] < pos[i]) || (p_from[i] > end[i]))
360 				{
361 					return false;
362 				}
363 			} else { // ray not parallel to planes in this direction
364 				c1[i] = (pos[i] - p_from[i]) / p_dir[i];
365 				c2[i] = (end[i] - p_from[i]) / p_dir[i];
366 	
367 				if(c1[i] > c2[i]){
368 					swap(c1,c2);
369 				}
370 				if (c1[i] > near){
371 					near = c1[i];
372 					axis=i;
373 				}
374 				if (c2[i] < far){
375 					far = c2[i];
376 				}
377 				if( (near > far) || (far < 0) ){
378 					return false;
379 				}
380 			}
381 		}
382 		if (r_clip)
383 			*r_clip=c1;
384 		if (r_normal)
385 		{
386 			*r_normal=Vector3();
387 			(*r_normal)[axis]=p_dir[axis]?-1:1;
388 		}
389 		return true;
390 	}
391 	
392 	bool intersectsSegment(in Vector3 p_from, in Vector3 p_to, Vector3* r_clip = null, Vector3* r_normal = null) const
393 	{
394 		real_t min=0,max=1;
395 		int axis=0;
396 		real_t sign=0;
397 		
398 		for(int i=0;i<3;i++)
399 		{
400 			real_t seg_from=p_from[i];
401 			real_t seg_to=p_to[i];
402 			real_t box_begin=pos[i];
403 			real_t box_end=box_begin+size[i];
404 			real_t cmin,cmax;
405 			real_t csign;
406 			
407 			if (seg_from < seg_to)
408 			{
409 				if (seg_from > box_end || seg_to < box_begin)
410 					return false;
411 				real_t length=seg_to-seg_from;
412 				cmin = (seg_from < box_begin)?((box_begin - seg_from)/length):0;
413 				cmax = (seg_to > box_end)?((box_end - seg_from)/length):1;
414 				csign=-1.0;
415 			} else {
416 				if (seg_to > box_end || seg_from < box_begin)
417 					return false;
418 				real_t length=seg_to-seg_from;
419 				cmin = (seg_from > box_end)?(box_end - seg_from)/length:0;
420 				cmax = (seg_to < box_begin)?(box_begin - seg_from)/length:1;
421 				csign=1.0;
422 			}
423 			
424 			if (cmin > min)
425 			{
426 				min = cmin;
427 				axis=i;
428 				sign=csign;
429 			}
430 			if (cmax < max)
431 				max = cmax;
432 			if (max < min)
433 				return false;
434 		}
435 		
436 		Vector3 rel=p_to-p_from;
437 		
438 		if (r_normal)
439 		{
440 			Vector3 normal;
441 			normal[axis]=sign;
442 			*r_normal=normal;
443 		}
444 		
445 		if (r_clip)
446 			*r_clip=p_from+rel*min;
447 		
448 		return true;
449 	}
450 	
451 	
452 	bool intersectsPlane(in Plane p_plane) const
453 	{
454 		Vector3[8] points = [
455 			Vector3( pos.x, pos.y, pos.z),
456 			Vector3( pos.x, pos.y, pos.z+size.z),
457 			Vector3( pos.x, pos.y+size.y, pos.z),
458 			Vector3( pos.x, pos.y+size.y, pos.z+size.z),
459 			Vector3( pos.x+size.x, pos.y, pos.z),
460 			Vector3( pos.x+size.x, pos.y, pos.z+size.z),
461 			Vector3( pos.x+size.x, pos.y+size.y, pos.z),
462 			Vector3( pos.x+size.x, pos.y+size.y, pos.z+size.z),
463 		];
464 		
465 		bool over=false;
466 		bool under=false;
467 		
468 		for (int i=0;i<8;i++)
469 		{
470 			if (p_plane.distanceTo(points[i])>0)
471 				over=true;
472 			else
473 				under=true;
474 		}
475 		return under && over;
476 	}
477 	
478 	Vector3 getLongestAxis() const
479 	{
480 		Vector3 axis = Vector3(1,0,0);
481 		real_t max_size=size.x;
482 		
483 		if (size.y > max_size )
484 		{
485 			axis=Vector3(0,1,0);
486 			max_size=size.y;
487 		}
488 		
489 		if (size.z > max_size )
490 		{
491 			axis=Vector3(0,0,1);
492 			max_size=size.z;
493 		}
494 		
495 		return axis;
496 	}
497 	int getLongestAxisIndex() const
498 	{
499 		int axis=0;
500 		real_t max_size=size.x;
501 		
502 		if (size.y > max_size )
503 		{
504 			axis=1;
505 			max_size=size.y;
506 		}
507 		
508 		if (size.z > max_size )
509 		{
510 			axis=2;
511 			max_size=size.z;
512 		}
513 		
514 		return axis;
515 	}
516 	
517 	Vector3 getShortestAxis() const
518 	{
519 		Vector3 axis = Vector3(1,0,0);
520 		real_t max_size=size.x;
521 		
522 		if (size.y < max_size )
523 		{
524 			axis=Vector3(0,1,0);
525 			max_size=size.y;
526 		}
527 		
528 		if (size.z < max_size )
529 		{
530 			axis=Vector3(0,0,1);
531 			max_size=size.z;
532 		}
533 		
534 		return axis;
535 	}
536 	int getShortestAxisIndex() const
537 	{
538 		int axis=0;
539 		real_t max_size=size.x;
540 		
541 		if (size.y < max_size )
542 		{
543 			axis=1;
544 			max_size=size.y;
545 		}
546 		
547 		if (size.z < max_size )
548 		{
549 			axis=2;
550 			max_size=size.z;
551 		}
552 		
553 		return axis;
554 	}
555 	
556 	AABB merge(in AABB p_with) const
557 	{
558 		AABB aabb=this;
559 		aabb.mergeWith(p_with);
560 		return aabb;
561 	}
562 	AABB expand(in Vector3 p_vector) const
563 	{
564 		AABB aabb=this;
565 		aabb.expandTo(p_vector);
566 		return aabb;
567 	
568 	}
569 	AABB grow(real_t p_by) const
570 	{
571 		AABB aabb=this;
572 		aabb.growBy(p_by);
573 		return aabb;
574 	}
575 	
576 	void getEdge(int p_edge, out Vector3 r_from, out Vector3 r_to) const
577 	{
578 		///ERR_FAIL_INDEX(p_edge,12);
579 		switch(p_edge)
580 		{
581 			case 0:{
582 				r_from=Vector3( pos.x+size.x	, pos.y		, pos.z		);
583 				r_to=Vector3( pos.x	, pos.y		, pos.z		);
584 			} break;
585 			case 1:{
586 				r_from=Vector3( pos.x+size.x	, pos.y		, pos.z+size.z	);
587 				r_to=Vector3( pos.x+size.x	, pos.y		, pos.z		);
588 			} break;
589 			case 2:{
590 				r_from=Vector3( pos.x	, pos.y		, pos.z+size.z	);
591 				r_to=Vector3( pos.x+size.x	, pos.y		, pos.z+size.z	);
592 			} break;
593 			case 3:{
594 				r_from=Vector3( pos.x	, pos.y		, pos.z		);
595 				r_to=Vector3( pos.x	, pos.y		, pos.z+size.z	);
596 			} break;
597 			case 4:{
598 				r_from=Vector3( pos.x	, pos.y+size.y		, pos.z		);
599 				r_to=Vector3( pos.x+size.x	, pos.y+size.y		, pos.z		);
600 			} break;
601 			case 5:{
602 				r_from=Vector3( pos.x+size.x	, pos.y+size.y		, pos.z		);
603 				r_to=Vector3( pos.x+size.x	, pos.y+size.y		, pos.z+size.z	);
604 			} break;
605 			case 6:{
606 				r_from=Vector3( pos.x+size.x	, pos.y+size.y		, pos.z+size.z	);
607 				r_to=Vector3( pos.x	, pos.y+size.y		, pos.z+size.z	);
608 			} break;
609 			case 7:{
610 				r_from=Vector3( pos.x	, pos.y+size.y		, pos.z+size.z	);
611 				r_to=Vector3( pos.x	, pos.y+size.y		, pos.z		);
612 			} break;
613 			case 8:{
614 				r_from=Vector3( pos.x	, pos.y		, pos.z+size.z	);
615 				r_to=Vector3( pos.x	, pos.y+size.y		, pos.z+size.z	);
616 			} break;
617 			case 9:{
618 				r_from=Vector3( pos.x	, pos.y		, pos.z		);
619 				r_to=Vector3( pos.x	, pos.y+size.y	, pos.z		);
620 			} break;
621 			case 10:{
622 				r_from=Vector3( pos.x+size.x	, pos.y		, pos.z		);
623 				r_to=Vector3( pos.x+size.x	, pos.y+size.y	, pos.z		);
624 			} break;
625 			case 11:{
626 				r_from=Vector3( pos.x+size.x	, pos.y		, pos.z+size.z		);
627 				r_to=Vector3( pos.x+size.x	, pos.y+size.y	, pos.z+size.z		);
628 			} break;
629 			default: assert(0);
630 		}
631 	}
632 }
633