Home | History | Annotate | Download | only in collision
      1 /*******************************************************************************
      2  * Copyright 2011 See AUTHORS file.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *   http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  ******************************************************************************/
     16 
     17 package com.badlogic.gdx.math.collision;
     18 
     19 import java.io.Serializable;
     20 import java.util.List;
     21 
     22 import com.badlogic.gdx.math.Matrix4;
     23 import com.badlogic.gdx.math.Vector3;
     24 
     25 /** Encapsulates an axis aligned bounding box represented by a minimum and a maximum Vector. Additionally you can query for the
     26  * bounding box's center, dimensions and corner points.
     27  *
     28  * @author badlogicgames (at) gmail.com, Xoppa */
     29 public class BoundingBox implements Serializable {
     30 	private static final long serialVersionUID = -1286036817192127343L;
     31 
     32 	private final static Vector3 tmpVector = new Vector3();
     33 
     34 	public final Vector3 min = new Vector3();
     35 	public final Vector3 max = new Vector3();
     36 
     37 	private final Vector3 cnt = new Vector3();
     38 	private final Vector3 dim = new Vector3();
     39 
     40 	/** @param out The {@link Vector3} to receive the center of the bounding box.
     41 	 * @return The vector specified with the out argument. */
     42 	public Vector3 getCenter (Vector3 out) {
     43 		return out.set(cnt);
     44 	}
     45 
     46 	public float getCenterX () {
     47 		return cnt.x;
     48 	}
     49 
     50 	public float getCenterY () {
     51 		return cnt.y;
     52 	}
     53 
     54 	public float getCenterZ () {
     55 		return cnt.z;
     56 	}
     57 
     58 	public Vector3 getCorner000 (final Vector3 out) {
     59 		return out.set(min.x, min.y, min.z);
     60 	}
     61 
     62 	public Vector3 getCorner001 (final Vector3 out) {
     63 		return out.set(min.x, min.y, max.z);
     64 	}
     65 
     66 	public Vector3 getCorner010 (final Vector3 out) {
     67 		return out.set(min.x, max.y, min.z);
     68 	}
     69 
     70 	public Vector3 getCorner011 (final Vector3 out) {
     71 		return out.set(min.x, max.y, max.z);
     72 	}
     73 
     74 	public Vector3 getCorner100 (final Vector3 out) {
     75 		return out.set(max.x, min.y, min.z);
     76 	}
     77 
     78 	public Vector3 getCorner101 (final Vector3 out) {
     79 		return out.set(max.x, min.y, max.z);
     80 	}
     81 
     82 	public Vector3 getCorner110 (final Vector3 out) {
     83 		return out.set(max.x, max.y, min.z);
     84 	}
     85 
     86 	public Vector3 getCorner111 (final Vector3 out) {
     87 		return out.set(max.x, max.y, max.z);
     88 	}
     89 
     90 	/** @param out The {@link Vector3} to receive the dimensions of this bounding box on all three axis.
     91 	 * @return The vector specified with the out argument */
     92 	public Vector3 getDimensions (final Vector3 out) {
     93 		return out.set(dim);
     94 	}
     95 
     96 	public float getWidth () {
     97 		return dim.x;
     98 	}
     99 
    100 	public float getHeight () {
    101 		return dim.y;
    102 	}
    103 
    104 	public float getDepth () {
    105 		return dim.z;
    106 	}
    107 
    108 	/** @param out The {@link Vector3} to receive the minimum values.
    109 	 * @return The vector specified with the out argument */
    110 	public Vector3 getMin (final Vector3 out) {
    111 		return out.set(min);
    112 	}
    113 
    114 	/** @param out The {@link Vector3} to receive the maximum values.
    115 	 * @return The vector specified with the out argument */
    116 	public Vector3 getMax (final Vector3 out) {
    117 		return out.set(max);
    118 	}
    119 
    120 	/** Constructs a new bounding box with the minimum and maximum vector set to zeros. */
    121 	public BoundingBox () {
    122 		clr();
    123 	}
    124 
    125 	/** Constructs a new bounding box from the given bounding box.
    126 	 *
    127 	 * @param bounds The bounding box to copy */
    128 	public BoundingBox (BoundingBox bounds) {
    129 		this.set(bounds);
    130 	}
    131 
    132 	/** Constructs the new bounding box using the given minimum and maximum vector.
    133 	 *
    134 	 * @param minimum The minimum vector
    135 	 * @param maximum The maximum vector */
    136 	public BoundingBox (Vector3 minimum, Vector3 maximum) {
    137 		this.set(minimum, maximum);
    138 	}
    139 
    140 	/** Sets the given bounding box.
    141 	 *
    142 	 * @param bounds The bounds.
    143 	 * @return This bounding box for chaining. */
    144 	public BoundingBox set (BoundingBox bounds) {
    145 		return this.set(bounds.min, bounds.max);
    146 	}
    147 
    148 	/** Sets the given minimum and maximum vector.
    149 	 *
    150 	 * @param minimum The minimum vector
    151 	 * @param maximum The maximum vector
    152 	 * @return This bounding box for chaining. */
    153 	public BoundingBox set (Vector3 minimum, Vector3 maximum) {
    154 		min.set(minimum.x < maximum.x ? minimum.x : maximum.x, minimum.y < maximum.y ? minimum.y : maximum.y,
    155 			minimum.z < maximum.z ? minimum.z : maximum.z);
    156 		max.set(minimum.x > maximum.x ? minimum.x : maximum.x, minimum.y > maximum.y ? minimum.y : maximum.y,
    157 			minimum.z > maximum.z ? minimum.z : maximum.z);
    158 		cnt.set(min).add(max).scl(0.5f);
    159 		dim.set(max).sub(min);
    160 		return this;
    161 	}
    162 
    163 	/** Sets the bounding box minimum and maximum vector from the given points.
    164 	 *
    165 	 * @param points The points.
    166 	 * @return This bounding box for chaining. */
    167 	public BoundingBox set (Vector3[] points) {
    168 		this.inf();
    169 		for (Vector3 l_point : points)
    170 			this.ext(l_point);
    171 		return this;
    172 	}
    173 
    174 	/** Sets the bounding box minimum and maximum vector from the given points.
    175 	 *
    176 	 * @param points The points.
    177 	 * @return This bounding box for chaining. */
    178 	public BoundingBox set (List<Vector3> points) {
    179 		this.inf();
    180 		for (Vector3 l_point : points)
    181 			this.ext(l_point);
    182 		return this;
    183 	}
    184 
    185 	/** Sets the minimum and maximum vector to positive and negative infinity.
    186 	 *
    187 	 * @return This bounding box for chaining. */
    188 	public BoundingBox inf () {
    189 		min.set(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
    190 		max.set(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
    191 		cnt.set(0, 0, 0);
    192 		dim.set(0, 0, 0);
    193 		return this;
    194 	}
    195 
    196 	/** Extends the bounding box to incorporate the given {@link Vector3}.
    197 	 * @param point The vector
    198 	 * @return This bounding box for chaining. */
    199 	public BoundingBox ext (Vector3 point) {
    200 		return this.set(min.set(min(min.x, point.x), min(min.y, point.y), min(min.z, point.z)),
    201 			max.set(Math.max(max.x, point.x), Math.max(max.y, point.y), Math.max(max.z, point.z)));
    202 	}
    203 
    204 	/** Sets the minimum and maximum vector to zeros.
    205 	 * @return This bounding box for chaining. */
    206 	public BoundingBox clr () {
    207 		return this.set(min.set(0, 0, 0), max.set(0, 0, 0));
    208 	}
    209 
    210 	/** Returns whether this bounding box is valid. This means that {@link #max} is greater than {@link #min}.
    211 	 * @return True in case the bounding box is valid, false otherwise */
    212 	public boolean isValid () {
    213 		return min.x < max.x && min.y < max.y && min.z < max.z;
    214 	}
    215 
    216 	/** Extends this bounding box by the given bounding box.
    217 	 *
    218 	 * @param a_bounds The bounding box
    219 	 * @return This bounding box for chaining. */
    220 	public BoundingBox ext (BoundingBox a_bounds) {
    221 		return this.set(min.set(min(min.x, a_bounds.min.x), min(min.y, a_bounds.min.y), min(min.z, a_bounds.min.z)),
    222 			max.set(max(max.x, a_bounds.max.x), max(max.y, a_bounds.max.y), max(max.z, a_bounds.max.z)));
    223 	}
    224 
    225 	/** Extends this bounding box by the given sphere.
    226 	 *
    227 	 * @param center Sphere center
    228 	 * @param radius Sphere radius
    229 	 * @return This bounding box for chaining. */
    230 	public BoundingBox ext (Vector3 center, float radius) {
    231 		return this.set(min.set(min(min.x, center.x - radius), min(min.y, center.y - radius), min(min.z, center.z - radius)),
    232 			max.set(max(max.x, center.x + radius), max(max.y, center.y + radius), max(max.z, center.z + radius)));
    233 	}
    234 
    235 	/** Extends this bounding box by the given transformed bounding box.
    236 	 *
    237 	 * @param bounds The bounding box
    238 	 * @param transform The transformation matrix to apply to bounds, before using it to extend this bounding box.
    239 	 * @return This bounding box for chaining. */
    240 	public BoundingBox ext (BoundingBox bounds, Matrix4 transform) {
    241 		ext(tmpVector.set(bounds.min.x, bounds.min.y, bounds.min.z).mul(transform));
    242 		ext(tmpVector.set(bounds.min.x, bounds.min.y, bounds.max.z).mul(transform));
    243 		ext(tmpVector.set(bounds.min.x, bounds.max.y, bounds.min.z).mul(transform));
    244 		ext(tmpVector.set(bounds.min.x, bounds.max.y, bounds.max.z).mul(transform));
    245 		ext(tmpVector.set(bounds.max.x, bounds.min.y, bounds.min.z).mul(transform));
    246 		ext(tmpVector.set(bounds.max.x, bounds.min.y, bounds.max.z).mul(transform));
    247 		ext(tmpVector.set(bounds.max.x, bounds.max.y, bounds.min.z).mul(transform));
    248 		ext(tmpVector.set(bounds.max.x, bounds.max.y, bounds.max.z).mul(transform));
    249 		return this;
    250 	}
    251 
    252 	/** Multiplies the bounding box by the given matrix. This is achieved by multiplying the 8 corner points and then calculating
    253 	 * the minimum and maximum vectors from the transformed points.
    254 	 *
    255 	 * @param transform The matrix
    256 	 * @return This bounding box for chaining. */
    257 	public BoundingBox mul (Matrix4 transform) {
    258 		final float x0 = min.x, y0 = min.y, z0 = min.z, x1 = max.x, y1 = max.y, z1 = max.z;
    259 		inf();
    260 		ext(tmpVector.set(x0, y0, z0).mul(transform));
    261 		ext(tmpVector.set(x0, y0, z1).mul(transform));
    262 		ext(tmpVector.set(x0, y1, z0).mul(transform));
    263 		ext(tmpVector.set(x0, y1, z1).mul(transform));
    264 		ext(tmpVector.set(x1, y0, z0).mul(transform));
    265 		ext(tmpVector.set(x1, y0, z1).mul(transform));
    266 		ext(tmpVector.set(x1, y1, z0).mul(transform));
    267 		ext(tmpVector.set(x1, y1, z1).mul(transform));
    268 		return this;
    269 	}
    270 
    271 	/** Returns whether the given bounding box is contained in this bounding box.
    272 	 * @param b The bounding box
    273 	 * @return Whether the given bounding box is contained */
    274 	public boolean contains (BoundingBox b) {
    275 		return !isValid()
    276 			|| (min.x <= b.min.x && min.y <= b.min.y && min.z <= b.min.z && max.x >= b.max.x && max.y >= b.max.y && max.z >= b.max.z);
    277 	}
    278 
    279 	/** Returns whether the given bounding box is intersecting this bounding box (at least one point in).
    280 	 * @param b The bounding box
    281 	 * @return Whether the given bounding box is intersected */
    282 	public boolean intersects (BoundingBox b) {
    283 		if (!isValid()) return false;
    284 
    285 		// test using SAT (separating axis theorem)
    286 
    287 		float lx = Math.abs(this.cnt.x - b.cnt.x);
    288 		float sumx = (this.dim.x / 2.0f) + (b.dim.x / 2.0f);
    289 
    290 		float ly = Math.abs(this.cnt.y - b.cnt.y);
    291 		float sumy = (this.dim.y / 2.0f) + (b.dim.y / 2.0f);
    292 
    293 		float lz = Math.abs(this.cnt.z - b.cnt.z);
    294 		float sumz = (this.dim.z / 2.0f) + (b.dim.z / 2.0f);
    295 
    296 		return (lx <= sumx && ly <= sumy && lz <= sumz);
    297 
    298 	}
    299 
    300 	/** Returns whether the given vector is contained in this bounding box.
    301 	 * @param v The vector
    302 	 * @return Whether the vector is contained or not. */
    303 	public boolean contains (Vector3 v) {
    304 		return min.x <= v.x && max.x >= v.x && min.y <= v.y && max.y >= v.y && min.z <= v.z && max.z >= v.z;
    305 	}
    306 
    307 	@Override
    308 	public String toString () {
    309 		return "[" + min + "|" + max + "]";
    310 	}
    311 
    312 	/** Extends the bounding box by the given vector.
    313 	 *
    314 	 * @param x The x-coordinate
    315 	 * @param y The y-coordinate
    316 	 * @param z The z-coordinate
    317 	 * @return This bounding box for chaining. */
    318 	public BoundingBox ext (float x, float y, float z) {
    319 		return this.set(min.set(min(min.x, x), min(min.y, y), min(min.z, z)), max.set(max(max.x, x), max(max.y, y), max(max.z, z)));
    320 	}
    321 
    322 	static final float min (final float a, final float b) {
    323 		return a > b ? b : a;
    324 	}
    325 
    326 	static final float max (final float a, final float b) {
    327 		return a > b ? a : b;
    328 	}
    329 }
    330