385 lines
10 KiB
C++
385 lines
10 KiB
C++
/* Copyright (C) Greg Snook, 2000.
|
|
* All rights reserved worldwide.
|
|
*
|
|
* This software is provided "as is" without express or implied
|
|
* warranties. You may freely copy and compile this source into
|
|
* applications you distribute provided that the copyright text
|
|
* below is included in the resulting source code, for example:
|
|
* "Portions Copyright (C) Greg Snook, 2000"
|
|
*/
|
|
#ifndef LINE2D_H
|
|
#define LINE2D_H
|
|
/****************************************************************************************\
|
|
Line2D.h
|
|
|
|
Line2D component interface for the Navimesh sample program.
|
|
Included as part of the Game Programming Gems sample code.
|
|
|
|
Created 3/18/00 by Greg Snook
|
|
greg@mightystudios.com
|
|
-------------------------------------------------------------------------------------
|
|
Notes/Revisions:
|
|
|
|
\****************************************************************************************/
|
|
|
|
/* Line2D
|
|
------------------------------------------------------------------------------------------
|
|
|
|
Line2D represents a line in 2D space. Line data is held as a line segment having two
|
|
endpoints and as a fictional 3D plane extending verticaly. The Plane is then used for
|
|
spanning and point clasification tests. A Normal vector is used internally to represent
|
|
the fictional plane.
|
|
|
|
------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
class Line2D
|
|
{
|
|
public:
|
|
|
|
// ----- ENUMERATIONS & CONSTANTS -----
|
|
enum POINT_CLASSIFICATION
|
|
{
|
|
ON_LINE, // The point is on, or very near, the line
|
|
LEFT_SIDE, // looking from endpoint A to B, the test point is on the left
|
|
RIGHT_SIDE // looking from endpoint A to B, the test point is on the right
|
|
};
|
|
|
|
enum LINE_CLASSIFICATION
|
|
{
|
|
COLLINEAR, // both lines are parallel and overlap each other
|
|
LINES_INTERSECT, // lines intersect, but their segments do not
|
|
SEGMENTS_INTERSECT, // both line segments bisect each other
|
|
A_BISECTS_B, // line segment B is crossed by line A
|
|
B_BISECTS_A, // line segment A is crossed by line B
|
|
PARALELL // the lines are paralell
|
|
};
|
|
|
|
// ----- CREATORS ---------------------
|
|
|
|
Line2D();
|
|
Line2D( const Line2D& Src);
|
|
Line2D( const D3DXVECTOR2& PointA, const D3DXVECTOR2& PointB);
|
|
~Line2D();
|
|
|
|
// ----- OPERATORS --------------------
|
|
Line2D& operator=( const Line2D& Src);
|
|
|
|
// ----- MUTATORS ---------------------
|
|
void SetEndPointA(const D3DXVECTOR2& Point);
|
|
void SetEndPointB(const D3DXVECTOR2& Point);
|
|
void SetPoints(const D3DXVECTOR2& PointA, const D3DXVECTOR2& PointB);
|
|
void SetPoints(float PointAx, float PointAy, float PointBx, float PointBy);
|
|
|
|
float SignedDistance(const D3DXVECTOR2& Point) const;
|
|
POINT_CLASSIFICATION ClassifyPoint(const D3DXVECTOR2& Point, float Epsilon = 0.0f) const;
|
|
LINE_CLASSIFICATION Intersection(const Line2D& Line, D3DXVECTOR2* pIntersectPoint=0)const;
|
|
|
|
void GetDirection(D3DXVECTOR2& Direction)const;
|
|
|
|
// ----- ACCESSORS --------------------
|
|
const D3DXVECTOR2& EndPointA() const;
|
|
const D3DXVECTOR2& EndPointB() const;
|
|
const D3DXVECTOR2& Normal() const;
|
|
float Length() const;
|
|
|
|
private:
|
|
|
|
// ----- DATA -------------------------
|
|
D3DXVECTOR2 m_PointA; // Endpoint A of our line segment
|
|
D3DXVECTOR2 m_PointB; // Endpoint B of our line segment
|
|
|
|
mutable D3DXVECTOR2 m_Normal; // 'normal' of the ray.
|
|
// a vector pointing to the right-hand side of the line
|
|
// when viewed from PointA towards PointB
|
|
mutable bool m_NormalCalculated; // normals are only calculated on demand
|
|
|
|
// ----- HELPER FUNCTIONS -------------
|
|
void ComputeNormal() const;
|
|
|
|
// ----- UNIMPLEMENTED FUNCTIONS ------
|
|
};
|
|
|
|
//- Inline Functions ---------------------------------------------------------------------
|
|
|
|
//= CREATORS =============================================================================
|
|
|
|
/* Line2D
|
|
------------------------------------------------------------------------------------------
|
|
|
|
Default Object Constructor
|
|
|
|
------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
inline Line2D::Line2D()
|
|
: m_NormalCalculated(false)
|
|
{
|
|
}
|
|
|
|
inline Line2D::Line2D( const D3DXVECTOR2& PointA, const D3DXVECTOR2& PointB)
|
|
{
|
|
m_PointA = PointA;
|
|
m_PointB = PointB;
|
|
m_NormalCalculated = false;
|
|
}
|
|
|
|
|
|
/* Line2D
|
|
------------------------------------------------------------------------------------------
|
|
|
|
Default Copy Constructor
|
|
|
|
------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
inline Line2D::Line2D( const Line2D& Src)
|
|
:m_NormalCalculated(false)
|
|
{
|
|
*this = Src;
|
|
}
|
|
|
|
|
|
/* ~Line2D
|
|
------------------------------------------------------------------------------------------
|
|
|
|
Default Object Destructor
|
|
|
|
------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
inline Line2D::~Line2D()
|
|
{
|
|
}
|
|
|
|
//= OPERATORS ============================================================================
|
|
|
|
|
|
inline Line2D& Line2D::operator=( const Line2D& Src)
|
|
{
|
|
m_PointA = Src.m_PointA;
|
|
m_PointB = Src.m_PointB;
|
|
m_Normal = Src.m_Normal;
|
|
m_NormalCalculated = Src.m_NormalCalculated;
|
|
return (*this);
|
|
}
|
|
|
|
//= MUTATORS =============================================================================
|
|
inline void Line2D::SetEndPointA(const D3DXVECTOR2& Point)
|
|
{
|
|
m_PointA = Point;
|
|
m_NormalCalculated = false;
|
|
|
|
}
|
|
|
|
inline void Line2D::SetEndPointB(const D3DXVECTOR2& Point)
|
|
{
|
|
m_PointB = Point;
|
|
m_NormalCalculated = false;
|
|
}
|
|
|
|
inline const D3DXVECTOR2& Line2D::Normal()const
|
|
{
|
|
if (!m_NormalCalculated)
|
|
{
|
|
ComputeNormal();
|
|
}
|
|
|
|
return (m_Normal);
|
|
}
|
|
|
|
inline void Line2D::SetPoints(const D3DXVECTOR2& PointA, const D3DXVECTOR2& PointB)
|
|
{
|
|
m_PointA = PointA;
|
|
m_PointB = PointB;
|
|
m_NormalCalculated = false;
|
|
}
|
|
|
|
|
|
inline void Line2D::SetPoints(float PointAx, float PointAy, float PointBx, float PointBy)
|
|
{
|
|
m_PointA.x=PointAx;
|
|
m_PointA.y=PointAy;
|
|
m_PointB.x=PointBx;
|
|
m_PointB.y=PointBy;
|
|
m_NormalCalculated = false;
|
|
}
|
|
|
|
/* SignedDistance
|
|
------------------------------------------------------------------------------------------
|
|
|
|
Determines the signed distance from a point to this line. Consider the line as
|
|
if you were standing on PointA of the line looking towards PointB. Positive distances
|
|
are to the right of the line, negative distances are to the left.
|
|
|
|
------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
inline float Line2D::SignedDistance(const D3DXVECTOR2& Point) const
|
|
{
|
|
if (!m_NormalCalculated)
|
|
{
|
|
ComputeNormal();
|
|
}
|
|
D3DXVECTOR2 TestVector = Point - m_PointA;
|
|
return D3DXVec2Dot( &TestVector, &m_Normal );
|
|
|
|
}
|
|
|
|
/* ClassifyPoint
|
|
------------------------------------------------------------------------------------------
|
|
|
|
Determines where a point lies in relation to this line. Consider the line as
|
|
if you were standing on PointA of the line looking towards PointB. The incomming
|
|
point is then classified as being on the Left, Right or Centered on the line.
|
|
|
|
------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
inline Line2D::POINT_CLASSIFICATION Line2D::ClassifyPoint(const D3DXVECTOR2& Point, float Epsilon) const
|
|
{
|
|
POINT_CLASSIFICATION Result = ON_LINE;
|
|
float Distance = SignedDistance(Point);
|
|
|
|
if (Distance > Epsilon)
|
|
{
|
|
Result = RIGHT_SIDE;
|
|
}
|
|
else if (Distance < -Epsilon)
|
|
{
|
|
Result = LEFT_SIDE;
|
|
}
|
|
|
|
return(Result);
|
|
}
|
|
|
|
/* SegmentIntersection
|
|
------------------------------------------------------------------------------------------
|
|
|
|
Determines if two segments intersect, and if so the point of intersection. The current
|
|
member line is considered line AB and the incomming parameter is considered line CD for
|
|
the purpose of the utilized equations.
|
|
|
|
A = PointA of the member line
|
|
B = PointB of the member line
|
|
C = PointA of the provided line
|
|
D = PointB of the provided line
|
|
|
|
------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
inline Line2D::LINE_CLASSIFICATION Line2D::Intersection(const Line2D& Line, D3DXVECTOR2* pIntersectPoint)const
|
|
{
|
|
float Ay_minus_Cy = m_PointA.y - Line.EndPointA().y;
|
|
float Dx_minus_Cx = Line.EndPointB().x - Line.EndPointA().x;
|
|
float Ax_minus_Cx = m_PointA.x - Line.EndPointA().x;
|
|
float Dy_minus_Cy = Line.EndPointB().y - Line.EndPointA().y;
|
|
float Bx_minus_Ax = m_PointB.x - m_PointA.x;
|
|
float By_minus_Ay = m_PointB.y - m_PointA.y;
|
|
|
|
float Numerator = (Ay_minus_Cy * Dx_minus_Cx) - (Ax_minus_Cx * Dy_minus_Cy);
|
|
float Denominator = (Bx_minus_Ax * Dy_minus_Cy) - (By_minus_Ay * Dx_minus_Cx);
|
|
|
|
// if lines do not intersect, return now
|
|
if (!Denominator)
|
|
{
|
|
if (!Numerator)
|
|
{
|
|
return COLLINEAR;
|
|
}
|
|
|
|
return PARALELL;
|
|
}
|
|
|
|
float FactorAB = Numerator / Denominator;
|
|
float FactorCD = ((Ay_minus_Cy * Bx_minus_Ax) - (Ax_minus_Cx * By_minus_Ay)) / Denominator;
|
|
|
|
// posting (hitting a vertex exactly) is not allowed, shift the results
|
|
// if they are within a minute range of the end vertecies
|
|
if (fabsf(FactorCD) < 1.0e-6f)
|
|
{
|
|
FactorCD = 1.0e-6f;
|
|
}
|
|
if (fabsf(FactorCD - 1.0f) < 1.0e-6f)
|
|
{
|
|
FactorCD = 1.0f - 1.0e-6f;
|
|
}
|
|
|
|
// if an interection point was provided, fill it in now
|
|
if (pIntersectPoint)
|
|
{
|
|
pIntersectPoint->x = (m_PointA.x + (FactorAB * Bx_minus_Ax));
|
|
pIntersectPoint->y = (m_PointA.y + (FactorAB * By_minus_Ay));
|
|
}
|
|
|
|
// now determine the type of intersection
|
|
if ((FactorAB >= 0.0f) && (FactorAB <= 1.0f) && (FactorCD >= 0.0f) && (FactorCD <= 1.0f))
|
|
{
|
|
return SEGMENTS_INTERSECT;
|
|
}
|
|
else if ((FactorCD >= 0.0f) && (FactorCD <= 1.0f))
|
|
{
|
|
return (A_BISECTS_B);
|
|
}
|
|
else if ((FactorAB >= 0.0f) && (FactorAB <= 1.0f))
|
|
{
|
|
return (B_BISECTS_A);
|
|
}
|
|
|
|
return LINES_INTERSECT;
|
|
}
|
|
|
|
//= ACCESSORS ============================================================================
|
|
|
|
inline const D3DXVECTOR2& Line2D::EndPointA()const
|
|
{
|
|
return (m_PointA);
|
|
}
|
|
|
|
|
|
inline const D3DXVECTOR2& Line2D::EndPointB()const
|
|
{
|
|
return (m_PointB);
|
|
}
|
|
|
|
|
|
inline float Line2D::Length() const
|
|
{
|
|
float xdist = m_PointB.x-m_PointA.x;
|
|
float ydist = m_PointB.y-m_PointA.y;
|
|
|
|
xdist *= xdist;
|
|
ydist *= ydist;
|
|
|
|
return static_cast<float>(sqrt(xdist + ydist));
|
|
}
|
|
|
|
inline void Line2D::GetDirection(D3DXVECTOR2& Direction)const
|
|
{
|
|
Direction = (m_PointB - m_PointA);
|
|
D3DXVec2Normalize( &Direction, &Direction );
|
|
}
|
|
|
|
inline void Line2D::ComputeNormal() const
|
|
{
|
|
//
|
|
// Get Normailized direction from A to B
|
|
//
|
|
GetDirection(m_Normal);
|
|
|
|
//
|
|
// Rotate by -90 degrees to get normal of line
|
|
//
|
|
float OldYValue = m_Normal[1];
|
|
m_Normal[1] = -m_Normal[0];
|
|
m_Normal[0] = OldYValue;
|
|
m_NormalCalculated = true;
|
|
|
|
}
|
|
|
|
//- End of Line2D -----------------------------------------------------------------------
|
|
|
|
//****************************************************************************************
|
|
|
|
#endif // end of file ( Line2D.h )
|
|
|