1 module dkh.geo.primitive; 2 3 import std.traits; 4 5 template EPS(R) { 6 R EPS; 7 } 8 9 int sgn(R)(R a) { 10 static if (isFloatingPoint!R) { 11 import std.math : isNaN; 12 assert(!isNaN(EPS!R)); 13 } 14 if (a < -EPS!R) return -1; 15 if (a > EPS!R) return 1; 16 return 0; 17 } 18 19 struct Point2D(T) { 20 T[2] d; 21 this(T x, T y) {this.d = [x, y];} 22 this(T[2] d) {this.d = d;} 23 @property ref inout(T) x() inout {return d[0];} 24 @property ref inout(T) y() inout {return d[1];} 25 ref inout(T) opIndex(size_t i) inout {return d[i];} 26 auto opOpAssign(string op)(in Point2D r) { 27 return mixin("this=this"~op~"r"); 28 } 29 auto opBinary(string op:"+")(in Point2D r) const {return Point2D(x+r.x, y+r.y);} 30 auto opBinary(string op:"-")(in Point2D r) const {return Point2D(x-r.x, y-r.y);} 31 static if (isFloatingPoint!T) { 32 T abs() { 33 import std.math : sqrt; 34 return (x*x+y*y).sqrt; 35 } 36 T arg() { 37 import std.math : atan2; 38 return atan2(y, x); 39 } 40 Point2D rot(T ar) { 41 import std.math : cos, sin; 42 auto cosAr = cos(ar), sinAr = sin(ar); 43 return Point2D(x*cosAr - y*sinAr, x*sinAr + y*cosAr); 44 } 45 } 46 } 47 48 int robustCmp(T)(Point2D!T a, Point2D!T b) { 49 if (sgn(a.x-b.x)) return sgn(a.x-b.x); 50 if (sgn(a.y-b.y)) return sgn(a.y-b.y); 51 return 0; 52 } 53 54 bool near(T)(Point2D!T a, Point2D!T b) if (isIntegral!T) { 55 return a == b; 56 } 57 58 bool near(T)(Point2D!T a, Point2D!T b) if (isFloatingPoint!T) { 59 return !sgn((a-b).abs); 60 } 61 62 T dot(T)(in Point2D!T l, in Point2D!T r) { 63 return l[0]*r[0] + l[1]*r[1]; 64 } 65 66 T cross(T)(in Point2D!T l, in Point2D!T r) { 67 return l[0]*r[1] - l[1]*r[0]; 68 } 69 70 71 unittest { 72 alias P = Point2D!int; 73 P x = P(1, 2); 74 P y = P(3, 4); 75 assert((x+y).x == 4); 76 assert((x+y)[1] == 6); 77 assert(dot(x, y) == 11); 78 assert(cross(x, y) == -2); 79 } 80 81 82 int ccw(R)(Point2D!R a, Point2D!R b, Point2D!R c) { 83 import std.stdio; 84 assert(!near(a, b)); 85 if (near(a, c) || near(b, c)) return 0; 86 int s = sgn(cross(b-a, c-a)); 87 if (s) return s; 88 if (dot(b-a, c-a) < 0) return 2; 89 if (dot(a-b, c-b) < 0) return -2; 90 return 0; 91 } 92 93 94 struct Line2D(R) { 95 Point2D!R x, y; 96 this(Point2D!R x, Point2D!R y) { 97 this.x = x; 98 this.y = y; 99 } 100 Point2D!R vec() const { return y-x; } 101 } 102 103 R distLP(R)(in Line2D!R l, in Point2D!R p) if (isFloatingPoint!R) { 104 import std.math : abs; 105 return abs(cross(l.vec, p-l.x) / l.vec.abs); 106 } 107 R distSP(R)(in Line2D!R s, in Point2D!R p) if (isFloatingPoint!R) { 108 import std.algorithm : min; 109 auto s2 = Point2D!R(-s.vec.y, s.vec.x); 110 if (ccw(s.x, s.x+s2, p) == 1) return (s.x-p).abs; 111 if (ccw(s.y, s.y+s2, p) == -1) return (s.y-p).abs; 112 return min((s.x-p).abs, (s.y-p).abs, distLP(s, p)); 113 } 114 115 unittest { 116 import std.math; 117 alias P = Point2D!double; 118 alias L = Line2D!double; 119 EPS!double = 1e-6; 120 assert(approxEqual( 121 distLP(L(P(-1, 0), P(1, 0)), P(0, 1)), 122 1.0 123 )); 124 assert(approxEqual( 125 distSP(L(P(-1, 0), P(1, 0)), P(-2, 1)), 126 sqrt(2.0) 127 )); 128 } 129 130 // cmp by argment, precise 131 // (-PI, PI], (-1, 0)=PI, (0, 0)=0, (1, 0) = 0 132 int argcmp(T)(Point2D!T l, Point2D!T r) if (isIntegral!T) { 133 int sgn(Point2D!T p) { 134 if (p[1] < 0) return -1; 135 if (p[1] > 0) return 1; 136 if (p[0] < 0) return 2; 137 return 0; 138 } 139 int lsgn = sgn(l); 140 int rsgn = sgn(r); 141 if (lsgn < rsgn) return -1; 142 if (lsgn > rsgn) return 1; 143 144 T x = cross(l, r); 145 if (x > 0) return -1; 146 if (x < 0) return 1; 147 148 return 0; 149 } 150 151 152 unittest { 153 import std.math, std.random; 154 int naive(Point2D!double l, Point2D!double r) { 155 double la, ra; 156 if (abs(l[1]) < 1e-9) { 157 if (l[0] < -(1e-9)) la = PI; 158 else la = 0; 159 } else { 160 la = l.arg; 161 } 162 if (abs(r[1]) < 1e-9) { 163 if (r[0] < -(1e-9)) ra = PI; 164 else ra = 0; 165 } else { 166 ra = r.arg; 167 } 168 169 if (abs(la-ra) < 1e-9) return 0; 170 if (la < ra) return -1; 171 return 1; 172 } 173 foreach (ya; -10..10) { 174 foreach (yb; -10..10) { 175 foreach (xa; -10..10) { 176 foreach (xb; -10..10) { 177 auto pa = Point2D!long(xa, ya); 178 auto pb = Point2D!long(xb, yb); 179 auto qa = Point2D!double(xa, ya); 180 auto qb = Point2D!double(xb, yb); 181 assert(argcmp(pa, pb) == naive(qa, qb)); 182 } 183 } 184 } 185 } 186 }