1 /** 2 * 2D geometry math stuff 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.geometry; 15 16 import std.traits; 17 import std.math; 18 19 import ae.utils.math; 20 21 enum TAU = 2*PI; 22 23 auto sqrtx(T)(T x) 24 { 25 static if (is(T : int)) 26 return std.math.sqrt(cast(float)x); 27 else 28 return std.math.sqrt(x); 29 } 30 31 auto dist (T)(T x, T y) { return sqrtx(x*x+y*y); } 32 auto dist2(T)(T x, T y) { return x*x+y*y ; } 33 34 struct Point(T) 35 { 36 T x, y; 37 void translate(T dx, T dy) { x += dx; y += dy; } 38 Point!T getCenter() { return this; } 39 } 40 auto point(T...)(T args) { return Point!(CommonType!T)(args); } 41 42 struct Rect(T) 43 { 44 T x0, y0, x1, y1; 45 @property T w() { return x1-x0; } 46 @property void w(T value) { x1 = x0 + value; } 47 @property T h() { return y1-y0; } 48 @property void h(T value) { y1 = y0 + value; } 49 void sort() { sort2(x0, x1); sort2(y0, y1); } 50 @property bool sorted() { return x0 <= x1 && y0 <= y1; } 51 void translate(T dx, T dy) { x0 += dx; y0 += dy; x1 += dx; y1 += dy; } 52 Point!T getCenter() { return Point!T(cast(T)average(x0, x1), cast(T)average(y0, y1)); } 53 } 54 auto rect(T...)(T args) { return Rect!(CommonType!T)(args); } 55 56 unittest 57 { 58 Rect!int rint; 59 } 60 61 struct Circle(T) 62 { 63 T x, y, r; 64 @property T diameter() { return 2*r; } 65 void translate(T dx, T dy) { x += dx; y += dy; } 66 Point!T getCenter() { return Point!T(x, y); } 67 } 68 auto circle(T...)(T args) { return Circle!(CommonType!T)(args); } 69 70 enum ShapeKind { none, point, rect, circle } 71 struct Shape(T) 72 { 73 ShapeKind kind; 74 union 75 { 76 Point!T point; 77 Rect!T rect; 78 Circle!T circle; 79 } 80 81 this(Point!T point) 82 { 83 this.kind = ShapeKind.point; 84 this.point = point; 85 } 86 87 this(Rect!T rect) 88 { 89 this.kind = ShapeKind.rect; 90 this.rect = rect; 91 } 92 93 this(Circle!T circle) 94 { 95 this.kind = ShapeKind.circle; 96 this.circle = circle; 97 } 98 99 auto opDispatch(string s, T...)(T args) 100 if (is(typeof(mixin("point ." ~ s ~ "(args)"))) && 101 is(typeof(mixin("rect ." ~ s ~ "(args)"))) && 102 is(typeof(mixin("circle." ~ s ~ "(args)")))) 103 { 104 switch (kind) 105 { 106 case ShapeKind.point: 107 return mixin("point ." ~ s ~ "(args)"); 108 case ShapeKind.circle: 109 return mixin("circle." ~ s ~ "(args)"); 110 case ShapeKind.rect: 111 return mixin("rect ." ~ s ~ "(args)"); 112 default: 113 assert(0); 114 } 115 } 116 } 117 auto shape(T)(T shape) { return Shape!(typeof(shape.tupleof[0]))(shape); } 118 119 bool intersects(T)(Shape!T a, Shape!T b) 120 { 121 switch (a.kind) 122 { 123 case ShapeKind.point: 124 switch (b.kind) 125 { 126 case ShapeKind.point: 127 return a.point.x == b.point.x && a.point.y == b.point.y; 128 case ShapeKind.circle: 129 return dist2(a.point.x-b.circle.x, a.point.y-b.circle.y) < sqr(b.circle.r); 130 case ShapeKind.rect: 131 assert(b.rect.sorted); 132 return between(a.point.x, b.rect.x0, b.rect.x1) && between(a.point.y, b.rect.y0, b.rect.y1); 133 default: 134 assert(0); 135 } 136 case ShapeKind.circle: 137 switch (b.kind) 138 { 139 case ShapeKind.point: 140 return dist2(a.circle.x-b.point.x, a.circle.y-b.point.y) < sqr(a.circle.r); 141 case ShapeKind.circle: 142 return dist2(a.circle.x-b.circle.x, a.circle.y-b.circle.y) < sqr(a.circle.r+b.circle.r); 143 case ShapeKind.rect: 144 return intersects!T(a.circle, b.rect); 145 default: 146 assert(0); 147 } 148 case ShapeKind.rect: 149 switch (b.kind) 150 { 151 case ShapeKind.point: 152 assert(a.rect.sorted); 153 return between(b.point.x, a.rect.x0, a.rect.x1) && between(b.point.y, a.rect.y0, a.rect.y1); 154 case ShapeKind.circle: 155 return intersects!T(b.circle, a.rect); 156 case ShapeKind.rect: 157 assert(0); // TODO 158 default: 159 assert(0); 160 } 161 default: 162 assert(0); 163 } 164 } 165 166 bool intersects(T)(Circle!T circle, Rect!T rect) 167 { 168 // http://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection 169 170 Point!T circleDistance; 171 172 auto hw = rect.w/2, hh = rect.h/2; 173 174 circleDistance.x = abs(circle.x - rect.x0 - hw); 175 circleDistance.y = abs(circle.y - rect.y0 - hh); 176 177 if (circleDistance.x > (hw + circle.r)) return false; 178 if (circleDistance.y > (hh + circle.r)) return false; 179 180 if (circleDistance.x <= hw) return true; 181 if (circleDistance.y <= hh) return true; 182 183 auto cornerDistance_sq = 184 sqr(circleDistance.x - hw) + 185 sqr(circleDistance.y - hh); 186 187 return (cornerDistance_sq <= sqr(circle.r)); 188 }