1 /**
2 2D 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.rect2;
14 
15 import godot.core.defs;
16 import godot.core.vector2, godot.core.transform2d;
17 
18 import std.algorithm.comparison;
19 import std.algorithm.mutation : swap;
20 
21 /**
22 Rect2 consists of a position, a size, and several utility functions. It is typically used for fast overlap tests.
23 */
24 struct Rect2
25 {
26 	@nogc nothrow:
27 	
28 	Vector2 pos;
29 	Vector2 size;
30 	
31 	this( real_t p_x, real_t p_y, real_t p_width, real_t p_height)
32 	{
33 		pos=Vector2(p_x,p_y);
34 		size=Vector2(p_width, p_height);
35 	}
36 	
37 	real_t getArea() const { return size.width*size.height; }
38 	
39 	bool intersects(in Rect2 p_rect) const
40 	{
41 		if ( pos.x >= (p_rect.pos.x + p_rect.size.width) )
42 			return false;
43 		if ( (pos.x+size.width) <= p_rect.pos.x  )
44 			return false;
45 		if ( pos.y >= (p_rect.pos.y + p_rect.size.height) )
46 			return false;
47 		if ( (pos.y+size.height) <= p_rect.pos.y  )
48 			return false;
49 		
50 		return true;
51 	}
52 	
53 	bool encloses(in Rect2 p_rect) const
54 	{
55 		return 	(p_rect.pos.x>=pos.x) && (p_rect.pos.y>=pos.y) &&
56 			((p_rect.pos.x+p_rect.size.x)<(pos.x+size.x)) &&
57 			((p_rect.pos.y+p_rect.size.y)<(pos.y+size.y));
58 	}
59 	bool hasNoArea() const
60 	{
61 		return (size.x<=0 || size.y<=0);
62 	}
63 	
64 	bool hasPoint(in Vector2 p_point) const
65 	{
66 		if (p_point.x < pos.x)
67 			return false;
68 		if (p_point.y < pos.y)
69 			return false;
70 		
71 		if (p_point.x >= (pos.x+size.x) )
72 			return false;
73 		if (p_point.y >= (pos.y+size.y) )
74 			return false;
75 		
76 		return true;
77 	}
78 	
79 	Rect2 grow(real_t p_by) const
80 	{
81 		Rect2 g=this;
82 		g.pos.x-=p_by;
83 		g.pos.y-=p_by;
84 		g.size.width+=p_by*2;
85 		g.size.height+=p_by*2;
86 		return g;
87 	}
88 
89 	Rect2 expand(in Vector2 p_vector) const
90 	{
91 		Rect2 r = this;
92 		r.expandTo(p_vector);
93 		return r;
94 	}
95 
96 	void expandTo(in Vector2 p_vector)
97 	{
98 		Vector2 begin=pos;
99 		Vector2 end=pos+size;
100 		
101 		if (p_vector.x<begin.x)
102 			begin.x=p_vector.x;
103 		if (p_vector.y<begin.y)
104 			begin.y=p_vector.y;
105 		
106 		if (p_vector.x>end.x)
107 			end.x=p_vector.x;
108 		if (p_vector.y>end.y)
109 			end.y=p_vector.y;
110 		
111 		pos=begin;
112 		size=end-begin;
113 	}
114 	
115 	
116 	real_t distanceTo(in Vector2 p_point) const
117 	{
118 		real_t dist = 1e20;
119 		
120 		if (p_point.x < pos.x)
121 		{
122 			dist=min(dist,pos.x-p_point.x);
123 		}
124 		if (p_point.y < pos.y)
125 		{
126 			dist=min(dist,pos.y-p_point.y);
127 		}
128 		if (p_point.x >= (pos.x+size.x) )
129 		{
130 			dist=min(p_point.x-(pos.x+size.x),dist);
131 		}
132 		if (p_point.y >= (pos.y+size.y) )
133 	    {
134 		    dist=min(p_point.y-(pos.y+size.y),dist);
135 		}
136 		
137 		if (dist==1e20)
138 			return 0;
139 		else
140 			return dist;
141 	}
142 	
143 	Rect2 clip(in Rect2 p_rect) const
144 	{
145 	
146 		Rect2 new_rect=p_rect;
147 		
148 		if (!intersects( new_rect ))
149 			return Rect2();
150 		
151 		new_rect.pos.x = max( p_rect.pos.x , pos.x );
152 		new_rect.pos.y = max( p_rect.pos.y , pos.y );
153 		
154 		Vector2 p_rect_end=p_rect.pos+p_rect.size;
155 		Vector2 end=pos+size;
156 		
157 		new_rect.size.x=min(p_rect_end.x,end.x) - new_rect.pos.x;
158 		new_rect.size.y=min(p_rect_end.y,end.y) - new_rect.pos.y;
159 		
160 		return new_rect;
161 	}
162 	
163 	Rect2 merge(in Rect2 p_rect) const
164 	{
165 		Rect2 new_rect;
166 		
167 		new_rect.pos.x=min( p_rect.pos.x , pos.x );
168 		new_rect.pos.y=min( p_rect.pos.y , pos.y );
169 		
170 		
171 		new_rect.size.x = max( p_rect.pos.x+p_rect.size.x , pos.x+size.x );
172 		new_rect.size.y = max( p_rect.pos.y+p_rect.size.y , pos.y+size.y );
173 		
174 		new_rect.size = new_rect.size - new_rect.pos; //make relative again
175 		
176 		return new_rect;
177 	}
178 	
179 	
180 	
181 	bool intersectsSegment(in Vector2 p_from, in Vector2 p_to, Vector2* r_pos,Vector2* r_normal) const
182 	{
183 		real_t min=0,max=1;
184 		int axis=0;
185 		real_t sign=0;
186 	
187 		for(int i=0;i<2;i++)
188 		{
189 			real_t seg_from=p_from[i];
190 			real_t seg_to=p_to[i];
191 			real_t box_begin=pos[i];
192 			real_t box_end=box_begin+size[i];
193 			real_t cmin,cmax;
194 			real_t csign;
195 	
196 			if (seg_from < seg_to)
197 			{
198 				if (seg_from > box_end || seg_to < box_begin)
199 					return false;
200 				real_t length=seg_to-seg_from;
201 				cmin = (seg_from < box_begin)?((box_begin - seg_from)/length):0;
202 				cmax = (seg_to > box_end)?((box_end - seg_from)/length):1;
203 				csign=-1.0;
204 	
205 			}
206 			else
207 			{
208 				if (seg_to > box_end || seg_from < box_begin)
209 					return false;
210 				real_t length=seg_to-seg_from;
211 				cmin = (seg_from > box_end)?(box_end - seg_from)/length:0;
212 				cmax = (seg_to < box_begin)?(box_begin - seg_from)/length:1;
213 				csign=1.0;
214 			}
215 	
216 			if (cmin > min)
217 			{
218 				min = cmin;
219 				axis=i;
220 				sign=csign;
221 			}
222 			if (cmax < max)
223 				max = cmax;
224 			if (max < min)
225 				return false;
226 		}
227 	
228 	
229 		Vector2 rel=p_to-p_from;
230 	
231 		if (r_normal)
232 		{
233 			Vector2 normal;
234 			normal[axis]=sign;
235 			*r_normal=normal;
236 		}
237 	
238 		if (r_pos)
239 			*r_pos=p_from+rel*min;
240 	
241 		return true;
242 	}
243 	
244 	
245 	bool intersectsTransformed(in Transform2D p_xform, in Rect2 p_rect) const
246 	{
247 		//SAT intersection between local and transformed rect2
248 	
249 		Vector2[4] xf_points=[
250 			p_xform.xform(p_rect.pos),
251 			p_xform.xform(Vector2(p_rect.pos.x+p_rect.size.x,p_rect.pos.y)),
252 			p_xform.xform(Vector2(p_rect.pos.x,p_rect.pos.y+p_rect.size.y)),
253 			p_xform.xform(Vector2(p_rect.pos.x+p_rect.size.x,p_rect.pos.y+p_rect.size.y)),
254 		];
255 	
256 		real_t low_limit;
257 	
258 		//base rect2 first (faster)
259 	
260 		if (xf_points[0].y>pos.y)
261 			goto next1;
262 		if (xf_points[1].y>pos.y)
263 			goto next1;
264 		if (xf_points[2].y>pos.y)
265 			goto next1;
266 		if (xf_points[3].y>pos.y)
267 			goto next1;
268 	
269 		return false;
270 	
271 		next1:
272 	
273 		low_limit=pos.y+size.y;
274 	
275 		if (xf_points[0].y<low_limit)
276 			goto next2;
277 		if (xf_points[1].y<low_limit)
278 			goto next2;
279 		if (xf_points[2].y<low_limit)
280 			goto next2;
281 		if (xf_points[3].y<low_limit)
282 			goto next2;
283 	
284 		return false;
285 	
286 		next2:
287 	
288 		if (xf_points[0].x>pos.x)
289 			goto next3;
290 		if (xf_points[1].x>pos.x)
291 			goto next3;
292 		if (xf_points[2].x>pos.x)
293 			goto next3;
294 		if (xf_points[3].x>pos.x)
295 			goto next3;
296 	
297 		return false;
298 	
299 		next3:
300 	
301 		low_limit=pos.x+size.x;
302 	
303 		if (xf_points[0].x<low_limit)
304 			goto next4;
305 		if (xf_points[1].x<low_limit)
306 			goto next4;
307 		if (xf_points[2].x<low_limit)
308 			goto next4;
309 		if (xf_points[3].x<low_limit)
310 			goto next4;
311 	
312 		return false;
313 	
314 		next4:
315 	
316 		Vector2[4] xf_points2=[
317 			pos,
318 			Vector2(pos.x+size.x,pos.y),
319 			Vector2(pos.x,pos.y+size.y),
320 			Vector2(pos.x+size.x,pos.y+size.y),
321 		];
322 	
323 		real_t maxa=p_xform.elements[0].dot(xf_points2[0]);
324 		real_t mina=maxa;
325 	
326 		real_t dp = p_xform.elements[0].dot(xf_points2[1]);
327 		maxa=max(dp,maxa);
328 		mina=min(dp,mina);
329 	
330 		dp = p_xform.elements[0].dot(xf_points2[2]);
331 		maxa=max(dp,maxa);
332 		mina=min(dp,mina);
333 	
334 		dp = p_xform.elements[0].dot(xf_points2[3]);
335 		maxa=max(dp,maxa);
336 		mina=min(dp,mina);
337 	
338 		real_t maxb=p_xform.elements[0].dot(xf_points[0]);
339 		real_t minb=maxb;
340 	
341 		dp = p_xform.elements[0].dot(xf_points[1]);
342 		maxb=max(dp,maxb);
343 		minb=min(dp,minb);
344 	
345 		dp = p_xform.elements[0].dot(xf_points[2]);
346 		maxb=max(dp,maxb);
347 		minb=min(dp,minb);
348 	
349 		dp = p_xform.elements[0].dot(xf_points[3]);
350 		maxb=max(dp,maxb);
351 		minb=min(dp,minb);
352 	
353 	
354 		if ( mina > maxb )
355 			return false;
356 		if ( minb > maxa  )
357 			return false;
358 	
359 		maxa=p_xform.elements[1].dot(xf_points2[0]);
360 		mina=maxa;
361 	
362 		dp = p_xform.elements[1].dot(xf_points2[1]);
363 		maxa=max(dp,maxa);
364 		mina=min(dp,mina);
365 	
366 		dp = p_xform.elements[1].dot(xf_points2[2]);
367 		maxa=max(dp,maxa);
368 		mina=min(dp,mina);
369 	
370 		dp = p_xform.elements[1].dot(xf_points2[3]);
371 		maxa=max(dp,maxa);
372 		mina=min(dp,mina);
373 	
374 		maxb=p_xform.elements[1].dot(xf_points[0]);
375 		minb=maxb;
376 	
377 		dp = p_xform.elements[1].dot(xf_points[1]);
378 		maxb=max(dp,maxb);
379 		minb=min(dp,minb);
380 	
381 		dp = p_xform.elements[1].dot(xf_points[2]);
382 		maxb=max(dp,maxb);
383 		minb=min(dp,minb);
384 	
385 		dp = p_xform.elements[1].dot(xf_points[3]);
386 		maxb=max(dp,maxb);
387 		minb=min(dp,minb);
388 	
389 	
390 		if ( mina > maxb )
391 			return false;
392 		if ( minb > maxa  )
393 			return false;
394 	
395 	
396 		return true;
397 	
398 	}
399 }
400