static VALUE m_sqrt(VALUE x) { if (f_real_p(x)) { if (f_positive_p(x)) return m_sqrt_bang(x); return f_complex_new2(rb_cComplex, ZERO, m_sqrt_bang(f_negate(x))); } else { get_dat1(x); if (f_negative_p(dat->imag)) return f_conj(m_sqrt(f_conj(x))); else { VALUE a = f_abs(x); return f_complex_new2(rb_cComplex, m_sqrt_bang(f_div(f_add(a, dat->real), TWO)), m_sqrt_bang(f_div(f_sub(a, dat->real), TWO))); } } }
// R1 and R2 hold the projection of edge endpoints onto cross-section of tube centered on origin // t1 and t2 hold projections of edge onto axis of tube (tube goes from 0 to tubeLen) // we assume t1<t2 // on return hit point is offset from center line but not projected along the line bool edgeInTube(Point3F & R1,Point3F & R2,float t1,float t2, float radius, float invRadius2, float invTubeLen, float & hitTime,Point3F & hitPoint) { // range of edge that could still be in tube and close enough: (0,1) is whole edge float rangeA=0.0f; float rangeB=1.0f; // shrink down range based on time: if (t1<0.0f) { if (t2<0.0f) return false; rangeA = -t1/(t2-t1); } if (invTubeLen * t2 > hitTime) { if (invTubeLen * t1 > hitTime) return false; rangeB = (hitTime - t1 * invTubeLen) / (invTubeLen * (t2-t1)); } // now shrink down range based on distance of projection from origin Point3F R12 = R2; R12 -= R1; float edgeLen2= m_dot(R12,R12); if (edgeLen2==0.0f) { // R1=R2. Are they in the circle? if (R1.x*R1.x + R1.y*R1.y + R1.z*R1.z >= radius*radius) return false; // o.w., don't restrict range based on projection } else { // signed dist from R1 of point on edge line (not edge) closest to origin // actually R12 times above dist. (optimization) float edgePointDist = - m_dot(R1,R12); // dist squared of projected edge line (not edge) from origin float edgeDistSquared = edgeLen2 * m_dot(R1,R1) - edgePointDist*edgePointDist; if (edgeDistSquared>=edgeLen2 * radius * radius) return false; // put off adjusting edgePointDist and edgeDistSquared till now for speed... float invEdgeLen = 1.0f/m_sqrt(edgeLen2); edgePointDist *= invEdgeLen; edgeDistSquared *= invEdgeLen; edgeDistSquared *= invEdgeLen; // projected edge intersects circle iff (0,edgeLen) intersects // edgePointDist +/- sqrt(rad^2-edgeDist^2) // use an approximation to sqrt: float x = 1.0f-invRadius2*edgeDistSquared; float bound = -.699 * radius * (x+.07978539f)*(x-2.277210289f); float loRange = invEdgeLen * (edgePointDist-bound); float hiRange = invEdgeLen * (edgePointDist+bound); if (hiRange<rangeA || loRange>rangeB) return false; if (loRange > rangeA) rangeA=loRange; } hitTime = (t1 + rangeA * (t2-t1)) * invTubeLen; hitPoint.x = R1.x + (R2.x-R1.x) * rangeA; hitPoint.y = R1.y + (R2.y-R1.y) * rangeA; hitPoint.z = R1.z + (R2.z-R1.z) * rangeA; return true; }