1 /** 2 * Axis Aligned Bounding Box 3 */ 4 module d2d.math.AxisAlignedBoundingBox; 5 6 import std.algorithm; 7 import std.math; 8 import std.parallelism; 9 import std.range; 10 import std.traits; 11 import d2d.math.Segment; 12 import d2d.math.Vector; 13 14 /** 15 * A rectangle is a box in 2d space 16 * Because these rectangles are axis aligned, they don't have any rotation 17 */ 18 class AxisAlignedBoundingBox(T, uint dimensions) { 19 20 Vector!(T, dimensions) initialPoint; ///The initial or starting point of the AABB 21 Vector!(T, dimensions) extent; ///The extent in each direction the AABB extends from the initial point (eg.) 22 23 /** 24 * Gives the AABB convenient 2d aliases 25 */ 26 static if (dimensions == 2) { 27 28 //TODO: alias for x, y, w, and h 29 30 @property Vector!(T, 2) topLeft() { 31 return this.vertices[0]; 32 } 33 34 @property Vector!(T, 2) topRight() { 35 return this.vertices[1]; 36 } 37 38 @property Vector!(T, 2) bottomLeft() { 39 return this.vertices[3]; 40 } 41 42 @property Vector!(T, 2) bottomRight() { 43 return this.vertices[2]; 44 } 45 46 } 47 48 /** 49 * Gets all the vertices of the AABB 50 */ 51 @property Vector!(T, dimensions)[] vertices() { 52 Vector!(T, dimensions)[] allVerts = [new Vector!(T, dimensions)(this.initialPoint)]; 53 if (this.extent == new Vector!(T, dimensions)(0)) { 54 return allVerts; 55 } 56 foreach (component; this.extent.components) { 57 foreach (i; 0 .. dimensions) { 58 AxisAlignedBoundingBox!(T, dimensions) copy = new AxisAlignedBoundingBox!(T, 59 dimensions)(new Vector!(T, dimensions)(this.initialPoint.components), 60 new Vector!(T, dimensions)(this.extent.components)); 61 if (copy.extent[i].approxEqual(0)) { 62 continue; 63 } 64 copy.initialPoint[i] += copy.extent[i]; 65 copy.extent[i] = 0; 66 foreach (vertex; copy.vertices) { 67 if (!allVerts.canFind(vertex)) { 68 allVerts ~= vertex; 69 } 70 } 71 } 72 } 73 return allVerts; 74 } 75 76 /** 77 * Gets all the edges of the AABB 78 */ 79 @property Segment!(T, dimensions)[] edges() { 80 if (this.extent == new Vector!(T, dimensions)(0)) { 81 return null; 82 } 83 Segment!(T, dimensions)[] allEdges; 84 foreach(i; 0..dimensions) { 85 AxisAlignedBoundingBox!(T, dimensions) copy = new AxisAlignedBoundingBox!(T, dimensions)(this); 86 if (copy.extent[i].approxEqual(0)) { 87 continue; 88 } 89 copy.initialPoint[i] += copy.extent[i]; 90 copy.extent[i] = 0; 91 allEdges ~= new Segment!(T, dimensions)(this.initialPoint, copy.initialPoint); 92 foreach (edge; copy.edges) { 93 if (!allEdges.canFind(edge)) { 94 allEdges ~= edge; 95 } 96 } 97 } 98 return allEdges; 99 } 100 101 /** 102 * Gets the point that is the middle or center of the AABB 103 */ 104 @property Vector!(T, dimensions) center() { 105 return this.initialPoint + this.extent / 2; 106 } 107 108 /** 109 * Creates an AABB from the initial point, and how much in each direction the box extends 110 */ 111 this(Vector!(T, dimensions) initialPoint, Vector!(T, dimensions) extent) { 112 this.initialPoint = initialPoint; 113 this.extent = extent; 114 } 115 116 /** 117 * Creates an AABB from the same as the vector constructor, but as a varargs input 118 */ 119 this(T[] args...) { 120 this.initialPoint = new Vector!(T, dimensions)(0); 121 this.extent = new Vector!(T, dimensions)(0); 122 foreach (i; 0 .. dimensions) { 123 this.initialPoint[i] = args[i]; 124 this.extent[i] = args[i + dimensions]; 125 } 126 } 127 128 /** 129 * Copy constructor for AABBs 130 */ 131 this(AxisAlignedBoundingBox!(T, dimensions) toCopy) { 132 this(new Vector!(T, dimensions)(toCopy.initialPoint), new Vector!(T, 133 dimensions)(toCopy.extent)); 134 } 135 136 /** 137 * Returns whether the box contains the given point 138 */ 139 bool contains(Vector!(T, dimensions) point) { 140 bool isContained = true; 141 foreach (i, component; (cast(T[]) point.components).parallel) { 142 if ((component - this.initialPoint[i]) * ( 143 component - this.initialPoint[i] - this.extent[i]) >= 0) { 144 isContained = false; 145 } 146 } 147 return isContained; 148 } 149 150 } 151 152 /** 153 * Returns whether two rectangles intersect 154 */ 155 bool intersects(T)(T first, T second) if (is(T : AxisAlignedBoundingBox!V, V...)) { 156 foreach (i; 0 .. first.initialPoint.components.length) { 157 if (first.initialPoint[i] < second.initialPoint[i] 158 && first.initialPoint[i] + first.extent[i] < second.initialPoint[i] 159 && first.initialPoint[i] < second.initialPoint[i] + second.extent[i] 160 && first.initialPoint[i] + first.extent[i] < second.initialPoint[i] + second.extent[i] 161 || first.initialPoint[i] > second.initialPoint[i] 162 && first.initialPoint[i] + first.extent[i] > second.initialPoint[i] 163 && first.initialPoint[i] > second.initialPoint[i] + second.extent[i] 164 && first.initialPoint[i] + first.extent[i] 165 > second.initialPoint[i] + second.extent[i]) { 166 return false; 167 } 168 } 169 return true; 170 }