#include "munga.h" #pragma hdrstop #include "boxsolid.h" #include "line.h" //############################################################################# //############################### BoxedCone ############################# //############################################################################# //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BoxedCone::BoxedCone( const ExtentBox &extents, BoxedSolid::Material material, Simulation *owner, BoxedSolid *next_solid ): BoxedSolid(extents, ConeType, material, owner, next_solid) { Check_Pointer(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BoxedCone::~BoxedCone() { Check_Pointer(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical BoxedCone::IntersectsBounded(const ExtentBox &extents) { Check(this); Check(&extents); Verify(minX <= extents.minX); Verify(maxX >= extents.maxX); Verify(minY <= extents.minY); Verify(maxY >= extents.maxY); Verify(minZ <= extents.minZ); Verify(maxZ >= extents.maxZ); // //------------------------------------------------------------------------ // Find the center point in the XZ plane of the cone, and find the biggest // radius of the cone by measuring in the X axis, along with its height //------------------------------------------------------------------------ // Vector3D center; center.x = (minX + maxX) * 0.5f; center.y = minY; center.z = (minZ + maxZ) * 0.5f; Scalar radius = maxX - center.x; Verify(!Small_Enough(radius)); // //-------------------------------------------------------------------------- // Convert the closest point in the slice to the coordinates of the cone, // putting the apex of the cone at the origin, then make sure that the point // isn't in one of the corners of the box //-------------------------------------------------------------------------- // Vector3D nearest(center); extents.Constrain(&nearest); center.y = maxY; nearest.Subtract(center,nearest); Scalar r = nearest.x*nearest.x + nearest.z*nearest.z; if (r > radius*radius) { return False; } // //-------------------------------------------------------------------------- // Compute the distance from the axis, and then see if the slope of the line // from the cone apex to the test point is less than the slope of the cone //-------------------------------------------------------------------------- // r = Sqrt(r); Scalar height = maxY - minY; return r*height <= nearest.y*radius; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical BoxedCone::ContainsBounded(const Point3D &point) { Check(this); Check(&point); Verify(minX <= point.x); Verify(maxX >= point.x); Verify(minY <= point.y); Verify(maxY >= point.y); Verify(minZ <= point.z); Verify(maxZ >= point.z); // //------------------------------------------------------------------------ // Find the center point in the XZ plane of the cone, and find the biggest // radius of the cone by measuring in the X axis, along with its height //------------------------------------------------------------------------ // Vector3D center; center.x = (minX + maxX) * 0.5f; center.y = maxY; center.z = (minZ + maxZ) * 0.5f; Scalar radius = maxX - center.x; Verify(!Small_Enough(radius)); // //-------------------------------------------------------------------------- // Convert the closest point in the slice to the coordinates of the cone, // putting the apex of the cone at the origin, then make sure that the point // isn't in one of the corners of the box //-------------------------------------------------------------------------- // Vector3D nearest; nearest.Subtract(center, point); Scalar r = nearest.x*nearest.x + nearest.z*nearest.z; if (r > radius*radius) { return False; } // //-------------------------------------------------------------------------- // Compute the distance from the axis, and then see if the slope of the line // from the cone apex to the test point is less than the slope of the cone //-------------------------------------------------------------------------- // r = Sqrt(r); Scalar height = maxY - minY; return r*height <= nearest.y*radius; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Scalar BoxedCone::FindDistanceBelowBounded(const Point3D &point) { Check(this); Check(&point); Verify(minX <= point.x); Verify(maxX >= point.x); Verify(minY <= point.y); Verify(minZ <= point.z); Verify(maxZ >= point.z); // //------------------------------------------------------------------------ // Find the center point in the XZ plane of the cone, and find the biggest // radius of the cone by measuring in the X axis, along with its height //------------------------------------------------------------------------ // Vector3D center; center.x = (minX + maxX) * 0.5f; center.y = maxY; center.z = (minZ + maxZ) * 0.5f; Scalar radius = maxX - center.x; Verify(!Small_Enough(radius)); // //-------------------------------------------------------------------------- // Convert the closest point in the slice to the coordinates of the cone, // putting the apex of the cone at the origin, then make sure that the point // isn't in one of the corners of the box //-------------------------------------------------------------------------- // Vector3D nearest; nearest.Subtract(point, center); Scalar r = nearest.x*nearest.x + nearest.z*nearest.z; if (r > radius*radius) { return -1.0f; } // //-------------------------------------------------------------------------- // Compute the distance from the axis, and then see if the slope of the line // from the cone apex to the test point is less than the slope of the cone //-------------------------------------------------------------------------- // r = Sqrt(r); Scalar height = maxY - minY; nearest.y += r*(height/radius); return Max(nearest.y,0.0f); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // This function is a helper function used in the derivation for the terms of // the quadratic equation to find t when colliding a line and a cone. I'm not // real clear on the physical representation of this kind of dot product // static Scalar Special_Cone_Dot_Product( const Vector3D &v1, const Vector3D &v2, Scalar squared_tan ) { return v1.x*v2.x - squared_tan*v1.y*v2.y + v1.z*v2.z; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical BoxedCone::HitByBounded( Line *line, Scalar enters, Scalar leaves ) { Check(this); Check(line); Verify(enters <= leaves); Verify(leaves >= 0.0f); // //-------------------------------------------------------------------------- // Find out the size of the box, and set up the height and maximum radius of // the cone, along with the squared tangent of the spread angle //-------------------------------------------------------------------------- // Vector3D center; center.x = (minX + maxX) * 0.5f; center.y = maxY; center.z = (minZ + maxZ) * 0.5f; Scalar radius = maxX - center.x; Scalar height = maxY - minY; Verify(!Small_Enough(height)); Scalar squared_tan = radius*radius/(height*height); // //------------------------------------------------------------------------- // Find the line between the point of the cone and the origin of our line, // then set up the conditions for the quadratic equation. The following // equation sets up a*t^2 + b*t + c = 0. The terms a, b, and c are derived // by solving the following equations for t: // // p == line->origin + line->direction * t // r == p - cone->apex // r.x^2 + r.z^2 == tan^2(cone_angle)*r.y^2 // // The special cone dot product function is a handy way to reduce the // complexity of the problem //------------------------------------------------------------------------- // Vector3D v; v.Subtract(line->origin,center); // //-------------------------------------------------------------------------- // If the line diverges from the axis at the same angle as the spread angle, // we need to find the closest point on the line to the axis. We then drop // a vertical plane containing the line through the cone at this point, thus // generating a hyperbola. We then solve the hyperbola vs. line equation to // get our intersection point //-------------------------------------------------------------------------- // Scalar a = Special_Cone_Dot_Product(line->direction, line->direction, squared_tan); if (Small_Enough(a)) { } // //----------------------------------------------- // Otherwise, continue solving the first equation //----------------------------------------------- // else { Scalar b = 2.0f * Special_Cone_Dot_Product(v, line->direction, squared_tan); Scalar c = Special_Cone_Dot_Product(v, v, squared_tan); // //----------------------------------------------------------------------- // Now, use the quadratic equation to determine where the two points // intersect the cone. If there is no solution, than the line missed the // cone //----------------------------------------------------------------------- // Verify(!Small_Enough(a)); Scalar t = -b / (2.0f * a); Scalar i = b*b - 4.0f*a*c; if (i < 0.0f) { return False; } // //---------------------------------------------------------------------- // If the interval is zero, then the line hits the cone in one spot only // (a tangential hit). Check to see if hits the lower half of the conic // equation (our cone) //---------------------------------------------------------------------- // if (Small_Enough(i)) { Scalar y = v.y + line->direction.y * t; if (y > 0.0f) { return False; } // //---------------------------------------------------------------- // It hit the lower cone, so see if it was entering or leaving the // cone //---------------------------------------------------------------- // if (line->direction.y > 0.0f) { // //------------------------------------------------------------- // The line is leaving the cone, so see if we need to reset the // leaving distance //------------------------------------------------------------- // if (t < leaves) { leaves = t; } } // //------------------------------------------------------------ // The line is entering the cone, so see if we need to set the // entering distance //------------------------------------------------------------ // else if (t > enters) { enters = t; } // //------------------------------------------------------------------- // See if we still have a collision with the cone, and if so, set the // new line length //------------------------------------------------------------------- // if (enters > leaves || enters > line->length || leaves < 0.0f) { return False; } line->length = Max(enters, 0.0f); return True; } // //---------------------------------------------------------------------- // If the interval is non-zero, then the conic section was struck in two // places. Find out the lengths to those places and the y values for // those points //---------------------------------------------------------------------- // i = Sqrt(i) / (2.0f * a); Scalar t1,t2; if (i > 0.0f) { t1 = t - i; t2 = t + i; } else { t1 = t + i; t2 = t - i; } Scalar y1 = v.y + line->direction.y * t1; Scalar y2 = v.y + line->direction.y * t2; // //---------------------------------------------------------------------- // There are four combinations of the signs of the y values. Each has a // different effect on missing/hitting, entering or leaving distances. // First check to see if only the upper conic was hit //---------------------------------------------------------------------- // if (y1 > 0.0f) { if (y2 > 0.0f) { return False; } // //----------------------------------------------------------------- // The ray is entering our conic, so set the distance appropriately //----------------------------------------------------------------- // if (t2 > enters) { enters = t2; } } // //---------------------------------------------------------------------- // See if the ray is leaving the lower conic, and if so, set the leaving // distance accordingly //---------------------------------------------------------------------- // else { if (y2 > 0.0f) { if (t1 < leaves) { leaves = t1; } } // //-------------------------------------------------------------------- // Both spots are in the lower conic, so set both leaving and entering // distances //-------------------------------------------------------------------- // else { if (t1 > enters) { enters = t1; } if (t2 < leaves) { leaves = t2; } } } // //----------------------------------------------------------------------- // See if we still have a collision with the cone, and if so, set the new // line length //----------------------------------------------------------------------- // if (enters > leaves || enters > line->length || leaves < 0.0f) { return False; } line->length = Max(enters, 0.0f); } return True; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical BoxedCone::TestInstance() const { return solidType == ConeType; }