1 /+ 2 The MIT License (MIT) 3 4 Copyright (c) <2013> <Oleg Butko (deviator), Anton Akzhigitov (Akzwar)> 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy 7 of this software and associated documentation files (the "Software"), to deal 8 in the Software without restriction, including without limitation the rights 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 copies of the Software, and to permit persons to whom the Software is 11 furnished to do so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in 14 all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 THE SOFTWARE. 23 +/ 24 25 module des.math.linear.ray; 26 27 import std.math; 28 import std.traits; 29 import des.util.testsuite; 30 import des.math.linear.vector; 31 import des.math.linear.matrix; 32 import des.math.basic; 33 34 version(unittest) 35 { 36 bool eq_seg(A,B,E=float)( in Ray!A a, in Ray!B b, in E eps=E.epsilon ) 37 { return eq_approx( a.pos.data ~ a.dir.data, b.pos.data ~ b.dir.data, eps ); } 38 } 39 40 /// 41 struct Ray(T) if( isFloatingPoint!T ) 42 { 43 /// 44 alias Vector3!T vectype; 45 46 /// 47 vectype pos, dir; 48 49 mixin( BasicMathOp!"pos dir" ); 50 51 pure: 52 /// 53 static auto fromPoints(A,B)( in A start, in B end ) 54 if( isCompatibleVector!(3,T,A) && isCompatibleVector!(3,T,B) ) 55 { return Ray!T( start, end - start ); } 56 57 /// 58 this(A,B)( in A a, in B b ) 59 if( isCompatibleVector!(3,T,A) && isCompatibleVector!(3,T,B) ) 60 { 61 pos = a; 62 dir = b; 63 } 64 65 @property 66 { 67 /// 68 ref vectype start() { return pos; } 69 /// 70 ref const(vectype) start() const { return pos; } 71 /// 72 vectype end() const { return pos + dir; } 73 /// 74 vectype end( in vectype p ) 75 { 76 dir = p - pos; 77 return p; 78 } 79 80 /// 81 auto revert() const 82 { return Ray!(T).fromPoints( end, start ); } 83 84 /// 85 T len2() const { return dir.len2; } 86 /// 87 T len() const { return dir.len; } 88 } 89 90 /// affine transform 91 auto tr(X)( in Matrix!(4,4,X) mtr ) const 92 { 93 return Ray!T( (mtr * Vector4!T( pos, 1 )).xyz, 94 (mtr * Vector4!T( dir, 0 )).xyz ); 95 } 96 97 /+ высота проведённая из точки это отрезок, 98 соединяющий проекцию точки на прямую и 99 саму точку (Ray) +/ 100 /// 101 auto altitude( in vectype pp ) const 102 { 103 auto n = dir.e; 104 auto dd = pos + n * dot(n,(pp-pos)); 105 return Ray!T( dd, pp - dd ); 106 } 107 108 /+ общий перпендикуляр +/ 109 /// 110 auto altitude(F)( in Ray!F seg ) const 111 { 112 /+ находим нормаль для паралельных 113 плоскостей в которых лежат s1 и s2 +/ 114 auto norm = cross(dir,seg.dir).e; 115 116 /+ расстояние между началами точками на прямых +/ 117 auto mv = pos - seg.pos; 118 119 /+ нормальный вектор, длиной в расстояние между плоскостями +/ 120 auto dist = norm * dot(norm,mv); 121 122 /+ переносим отрезок на плоскость первой прямой 123 и сразу находим пересечение +/ 124 auto pp = calcIntersection( Ray!T( seg.pos + dist, seg.dir ) ); 125 126 return Ray!T( pp, -dist ); 127 } 128 129 /+ пересечение с другой прямой 130 если она в той же плоскости +/ 131 /// 132 auto intersect(F)( in Ray!F seg ) const 133 { 134 auto a = altitude( seg ); 135 return a.pos + a.dir * 0.5; 136 } 137 138 /// 139 auto calcIntersection(F)( in Ray!F seg ) const 140 { 141 auto a = pos; 142 auto v = dir; 143 144 auto b = seg.pos; 145 auto w = seg.dir; 146 147 static T resolve( T a0, T r, T a1, T q, T b0, T p, T b1, T s ) 148 { 149 T pq = p * q, rs = r * s; 150 return ( a0 * pq + ( b1 - b0 ) * r * q - a1 * rs ) / ( pq - rs ); 151 } 152 153 auto x = resolve( a.x, v.x, b.x, w.x, a.y, v.y, b.y, w.y ); 154 auto y = resolve( a.y, v.y, b.y, w.y, a.x, v.x, b.x, w.x ); 155 auto z = resolve( a.z, v.z, b.z, w.z, a.y, v.y, b.y, w.y ); 156 157 x = isFinite(x) ? x : resolve( a.x, v.x, b.x, w.x, a.z, v.z, b.z, w.z ); 158 y = isFinite(y) ? y : resolve( a.y, v.y, b.y, w.y, a.z, v.z, b.z, w.z ); 159 z = isFinite(z) ? z : resolve( a.z, v.z, b.z, w.z, a.x, v.x, b.x, w.x ); 160 161 if( !isFinite(x) ) x = pos.x; 162 if( !isFinite(y) ) y = pos.y; 163 if( !isFinite(z) ) z = pos.z; 164 165 return vectype( x, y, z ); 166 } 167 } 168 169 /// 170 alias Ray!float fRay; 171 /// 172 alias Ray!double dRay; 173 /// 174 alias Ray!real rRay; 175 176 /// 177 unittest 178 { 179 auto r1 = fRay( vec3(1,2,3), vec3(2,3,4) ); 180 auto r2 = fRay( vec3(4,5,6), vec3(5,2,3) ); 181 auto rs = fRay( vec3(5,7,9), vec3(7,5,7) ); 182 assert( r1 + r2 == rs ); 183 } 184 185 unittest 186 { 187 auto a = vec3(1,2,3); 188 auto b = vec3(2,3,4); 189 auto r1 = fRay( a, b ); 190 assert( r1.start == a ); 191 assert( r1.end == a + b ); 192 r1.start = b; 193 assert( r1.start == b ); 194 r1.start = a; 195 assert( r1.len == b.len ); 196 r1.end = a; 197 assert( r1.len == 0 ); 198 } 199 200 unittest 201 { 202 import des.math.linear.matrix; 203 auto mtr = mat4( 2, 0.1, 0.04, 2, 204 0.3, 5, 0.01, 5, 205 0.1, 0.02, 3, 1, 206 0, 0, 0, 1 ); 207 208 auto s = fRay( vec3(1,2,3), vec3(2,3,4) ); 209 210 auto ta = (mtr * vec4(s.start,1)).xyz; 211 auto tb = (mtr * vec4(s.end,1)).xyz; 212 auto ts = s.tr( mtr ); 213 214 assert( eq( ts.start, ta ) ); 215 assert( eq_approx( ts.end, tb, 1e-5 ) ); 216 } 217 218 /// 219 unittest 220 { 221 auto s = fRay( vec3(2,0,0), vec3(-4,4,0) ); 222 auto p = vec3( 0,0,0 ); 223 auto r = s.altitude(p); 224 assert( eq( p, r.end ) ); 225 assert( eq( r.pos, vec3(1,1,0) ) ); 226 } 227 228 unittest 229 { 230 auto s = fRay( vec3(2,0,0), vec3(0,4,0) ); 231 auto p = vec3( 0,0,0 ); 232 auto r = s.altitude(p).len; 233 assert( r == 2.0f ); 234 } 235 236 unittest 237 { 238 auto s1 = fRay( vec3(0,0,1), vec3(2,2,0) ); 239 auto s2 = fRay( vec3(2,0,-1), vec3(-4,4,0) ); 240 auto a1 = s1.altitude(s2); 241 auto a2 = s2.altitude(s1); 242 assert( eq_seg( a1, a2.revert ) ); 243 assert( a1.len == 2 ); 244 assert( eq( a1.pos, vec3(1,1,1) ) ); 245 assert( eq( a1.dir, vec3(0,0,-2) ) ); 246 } 247 248 /// 249 unittest 250 { 251 auto s1 = fRay( vec3(-2,0,0), vec3(1,0,0) ); 252 auto s2 = fRay( vec3(0,0,2), vec3(0,1,-1) ); 253 254 auto a1 = s1.altitude(s2); 255 assert( eq_seg( a1, fRay(vec3(0,0,0), vec3(0,1,1)) ) ); 256 } 257 258 /// 259 unittest 260 { 261 auto s1 = fRay( vec3(0,0,0), vec3(2,2,0) ); 262 auto s2 = fRay( vec3(2,0,0), vec3(-4,4,0) ); 263 assert( eq( s1.intersect(s2), s2.intersect(s1) ) ); 264 assert( eq( s1.intersect(s2), vec3(1,1,0) ) ); 265 }