Exemple #1
0
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;
}