1 module des.math.linear.ray; 2 3 import std.math; 4 import std.traits; 5 import des.util.testsuite; 6 import des.math.linear.vector; 7 import des.math.linear.matrix; 8 import des.math.basic; 9 10 version(unittest) 11 { 12 bool eq_seg(A,B,E=float)( in Ray!A a, in Ray!B b, in E eps=E.epsilon ) 13 { return eq_approx( a.pos.data ~ a.dir.data, b.pos.data ~ b.dir.data, eps ); } 14 } 15 16 /// 17 struct Ray(T) if( isFloatingPoint!T ) 18 { 19 /// 20 alias Vector3!T vectype; 21 22 /// 23 vectype pos, dir; 24 25 mixin( BasicMathOp!"pos dir" ); 26 27 pure: 28 /// 29 static auto fromPoints(A,B)( in Vector!(3,A) start, in Vector!(3,B) end ) 30 if( is(typeof(vectype(start))) && is(typeof(vectype(start))) ) 31 { return Ray!T( start, end - start ); } 32 33 /// 34 this(A,B)( in Vector!(3,A) a, in Vector!(3,B) b ) 35 if( is(typeof(vectype(a))) && is(typeof(vectype(b))) ) 36 { 37 pos = a; 38 dir = b; 39 } 40 41 @property 42 { 43 /// 44 ref vectype start() { return pos; } 45 /// 46 ref const(vectype) start() const { return pos; } 47 /// 48 vectype end() const { return pos + dir; } 49 /// 50 vectype end( in vectype p ) 51 { 52 dir = p - pos; 53 return p; 54 } 55 56 /// 57 auto revert() const 58 { return Ray!(T).fromPoints( end, start ); } 59 60 /// 61 T len2() const { return dir.len2; } 62 /// 63 T len() const { return dir.len; } 64 } 65 66 /// affine transform 67 auto tr(X)( in Matrix!(4,4,X) mtr ) const 68 { 69 return Ray!T( (mtr * Vector4!T( pos, 1 )).xyz, 70 (mtr * Vector4!T( dir, 0 )).xyz ); 71 } 72 73 /+ высота проведённая из точки это отрезок, 74 соединяющий проекцию точки на прямую и 75 саму точку (Ray) +/ 76 /// 77 auto altitude( in vectype pp ) const 78 { 79 auto n = dir.e; 80 auto dd = pos + n * dot(n,(pp-pos)); 81 return Ray!T( dd, pp - dd ); 82 } 83 84 /+ общий перпендикуляр +/ 85 /// 86 auto altitude(F)( in Ray!F seg ) const 87 { 88 /+ находим нормаль для паралельных 89 плоскостей в которых лежат s1 и s2 +/ 90 auto norm = cross(dir,seg.dir).e; 91 92 /+ расстояние между началами точками на прямых +/ 93 auto mv = pos - seg.pos; 94 95 /+ нормальный вектор, длиной в расстояние между плоскостями +/ 96 auto dist = norm * dot(norm,mv); 97 98 /+ переносим отрезок на плоскость первой прямой 99 и сразу находим пересечение +/ 100 auto pp = calcIntersection( Ray!T( seg.pos + dist, seg.dir ) ); 101 102 return Ray!T( pp, -dist ); 103 } 104 105 /+ пересечение с другой прямой 106 если она в той же плоскости +/ 107 /// 108 auto intersect(F)( in Ray!F seg ) const 109 { 110 auto a = altitude( seg ); 111 return a.pos + a.dir * 0.5; 112 } 113 114 /// 115 auto calcIntersection(F)( in Ray!F seg ) const 116 { 117 auto a = pos; 118 auto v = dir; 119 120 auto b = seg.pos; 121 auto w = seg.dir; 122 123 static T resolve( T a0, T r, T a1, T q, T b0, T p, T b1, T s ) 124 { 125 T pq = p * q, rs = r * s; 126 return ( a0 * pq + ( b1 - b0 ) * r * q - a1 * rs ) / ( pq - rs ); 127 } 128 129 auto x = resolve( a.x, v.x, b.x, w.x, a.y, v.y, b.y, w.y ); 130 auto y = resolve( a.y, v.y, b.y, w.y, a.x, v.x, b.x, w.x ); 131 auto z = resolve( a.z, v.z, b.z, w.z, a.y, v.y, b.y, w.y ); 132 133 x = isFinite(x) ? x : resolve( a.x, v.x, b.x, w.x, a.z, v.z, b.z, w.z ); 134 y = isFinite(y) ? y : resolve( a.y, v.y, b.y, w.y, a.z, v.z, b.z, w.z ); 135 z = isFinite(z) ? z : resolve( a.z, v.z, b.z, w.z, a.x, v.x, b.x, w.x ); 136 137 if( !isFinite(x) ) x = pos.x; 138 if( !isFinite(y) ) y = pos.y; 139 if( !isFinite(z) ) z = pos.z; 140 141 return vectype( x, y, z ); 142 } 143 } 144 145 /// 146 alias Ray!float fRay; 147 /// 148 alias Ray!double dRay; 149 /// 150 alias Ray!real rRay; 151 152 /// 153 unittest 154 { 155 auto r1 = fRay( vec3(1,2,3), vec3(2,3,4) ); 156 auto r2 = fRay( vec3(4,5,6), vec3(5,2,3) ); 157 auto rs = fRay( vec3(5,7,9), vec3(7,5,7) ); 158 assert( r1 + r2 == rs ); 159 } 160 161 unittest 162 { 163 auto a = vec3(1,2,3); 164 auto b = vec3(2,3,4); 165 auto r1 = fRay( a, b ); 166 assert( r1.start == a ); 167 assert( r1.end == a + b ); 168 r1.start = b; 169 assert( r1.start == b ); 170 r1.start = a; 171 assert( r1.len == b.len ); 172 r1.end = a; 173 assert( r1.len == 0 ); 174 } 175 176 unittest 177 { 178 import des.math.linear.matrix; 179 auto mtr = mat4( 2, 0.1, 0.04, 2, 180 0.3, 5, 0.01, 5, 181 0.1, 0.02, 3, 1, 182 0, 0, 0, 1 ); 183 184 auto s = fRay( vec3(1,2,3), vec3(2,3,4) ); 185 186 auto ta = (mtr * vec4(s.start,1)).xyz; 187 auto tb = (mtr * vec4(s.end,1)).xyz; 188 auto ts = s.tr( mtr ); 189 190 assert( eq( ts.start, ta ) ); 191 assert( eq_approx( ts.end, tb, 1e-5 ) ); 192 } 193 194 /// 195 unittest 196 { 197 auto s = fRay( vec3(2,0,0), vec3(-4,4,0) ); 198 auto p = vec3( 0,0,0 ); 199 auto r = s.altitude(p); 200 assert( eq( p, r.end ) ); 201 assert( eq( r.pos, vec3(1,1,0) ) ); 202 } 203 204 unittest 205 { 206 auto s = fRay( vec3(2,0,0), vec3(0,4,0) ); 207 auto p = vec3( 0,0,0 ); 208 auto r = s.altitude(p).len; 209 assert( r == 2.0f ); 210 } 211 212 unittest 213 { 214 auto s1 = fRay( vec3(0,0,1), vec3(2,2,0) ); 215 auto s2 = fRay( vec3(2,0,-1), vec3(-4,4,0) ); 216 auto a1 = s1.altitude(s2); 217 auto a2 = s2.altitude(s1); 218 assert( eq_seg( a1, a2.revert ) ); 219 assert( a1.len == 2 ); 220 assert( eq( a1.pos, vec3(1,1,1) ) ); 221 assert( eq( a1.dir, vec3(0,0,-2) ) ); 222 } 223 224 /// 225 unittest 226 { 227 auto s1 = fRay( vec3(-2,0,0), vec3(1,0,0) ); 228 auto s2 = fRay( vec3(0,0,2), vec3(0,1,-1) ); 229 230 auto a1 = s1.altitude(s2); 231 assert( eq_seg( a1, fRay(vec3(0,0,0), vec3(0,1,1)) ) ); 232 } 233 234 /// 235 unittest 236 { 237 auto s1 = fRay( vec3(0,0,0), vec3(2,2,0) ); 238 auto s2 = fRay( vec3(2,0,0), vec3(-4,4,0) ); 239 assert( eq( s1.intersect(s2), s2.intersect(s1) ) ); 240 assert( eq( s1.intersect(s2), vec3(1,1,0) ) ); 241 }