Beispiel #1
0
bool Polygon::Contains(const float3 &worldSpacePoint, float polygonThickness) const
{
	// Implementation based on the description from http://erich.realtimerendering.com/ptinpoly/

	if (p.size() < 3)
		return false;

	if (PlaneCCW().Distance(worldSpacePoint) > polygonThickness)
		return false;

	int numIntersections = 0;

	float3 basisU = BasisU();
	float3 basisV = BasisV();
	mathassert(basisU.IsNormalized());
	mathassert(basisV.IsNormalized());
	mathassert(basisU.IsPerpendicular(basisV));
	mathassert(basisU.IsPerpendicular(PlaneCCW().normal));
	mathassert(basisV.IsPerpendicular(PlaneCCW().normal));

	float2 localSpacePoint = float2(Dot(worldSpacePoint, basisU), Dot(worldSpacePoint, basisV));

	const float epsilon = 1e-4f;

	float2 p0 = float2(Dot(p[p.size()-1], basisU), Dot(p[p.size()-1], basisV)) - localSpacePoint;
	if (Abs(p0.y) < epsilon)
		p0.y = -epsilon; // Robustness check - if the ray (0,0) -> (+inf, 0) would pass through a vertex, move the vertex slightly.
	for(int i = 0; i < (int)p.size(); ++i)
	{
		float2 p1 = float2(Dot(p[i], basisU), Dot(p[i], basisV)) - localSpacePoint;
		if (Abs(p1.y) < epsilon)
			p0.y = -epsilon; // Robustness check - if the ray (0,0) -> (+inf, 0) would pass through a vertex, move the vertex slightly.

		if (p0.y * p1.y < 0.f)
		{
			if (p0.x > 1e-3f && p1.x > 1e-3f)
				++numIntersections;
			else
			{
				// P = p0 + t*(p1-p0) == (x,0)
				//     p0.x + t*(p1.x-p0.x) == x
				//     p0.y + t*(p1.y-p0.y) == 0
				//                 t == -p0.y / (p1.y - p0.y)

				// Test whether the lines (0,0) -> (+inf,0) and p0 -> p1 intersect at a positive X-coordinate.
				float2 d = p1 - p0;
				if (Abs(d.y) > 1e-5f)
				{
					float t = -p0.y / d.y;
					float x = p0.x + t * d.x;
					if (t >= 0.f && t <= 1.f && x > 1e-3f)
						++numIntersections;
				}
			}
		}
		p0 = p1;
	}

	return numIntersections % 2 == 1;
}
Beispiel #2
0
bool Polygon::Intersects(const Ray &ray) const
{
	float d;
	if (!PlaneCCW().Intersects(ray, &d))
		return false;
	return Contains(ray.GetPoint(d));
}
Beispiel #3
0
bool Polygon::Intersects(const Line &line) const
{
	float d;
	if (!PlaneCCW().Intersects(line, &d))
		return false;
	return Contains(line.GetPoint(d));
}
Beispiel #4
0
bool Polygon::DiagonalExists(int i, int j) const
{
	assume(p.size() >= 3);
	assume(i >= 0);
	assume(j >= 0);
	assume(i < (int)p.size());
	assume(j < (int)p.size());
#ifndef MATH_ENABLE_INSECURE_OPTIMIZATIONS
	if (p.size() < 3 || i < 0 || j < 0 || i >= (int)p.size() || j >= (int)p.size())
		return false;
#endif
	assume(IsPlanar());
	assume(i != j);
	if (i == j) // Degenerate if i == j.
		return false;
	if (i > j)
		Swap(i, j);
	assume(i+1 != j);
	if (i+1 == j) // Is this LineSegment an edge of this polygon?
		return false;

	Plane polygonPlane = PlaneCCW();
	LineSegment diagonal = polygonPlane.Project(LineSegment(p[i], p[j]));

	// First check that this diagonal line is not intersected by an edge of this polygon.
	for(int k = 0; k < (int)p.size(); ++k)
		if (!(k == i || k+1 == i || k == j))
			if (polygonPlane.Project(LineSegment(p[k], p[k+1])).Intersects(diagonal))
				return false;

	return IsConvex();
}
Beispiel #5
0
bool Polygon::Intersects(const Plane &plane) const
{
    Line intersectLine;
    bool intersects = plane.Intersects(PlaneCCW(), &intersectLine);
    if (!intersects)
        return false;
    return Intersects(intersectLine);
}
Beispiel #6
0
bool Triangle::Contains(const float3 &point, float triangleThickness) const
{
    if (PlaneCCW().Distance(point) > triangleThickness) // The winding order of the triangle plane does not matter.
        return false; ///@todo The plane-point distance test is omitted in Real-Time Collision Detection. p. 25. A bug in the book?

    float3 br = BarycentricUVW(point);
    return br.y >= 0.f && br.z >= 0.f && (br.y + br.z) <= 1.f;
}
Beispiel #7
0
bool Triangle::Contains(const float3 &point, float triangleThickness) const
{
	if (PlaneCCW().Distance(point) > triangleThickness) // The winding order of the triangle plane does not matter.
		return false; ///@todo The plane-point distance test is omitted in Real-Time Collision Detection. p. 25. A bug in the book?

	float3 br = BarycentricUVW(point);
	return br.x >= -1e-3f && br.y >= -1e-3f && br.z >= -1e-3f; // Allow for a small epsilon to properly account for points very near the edges of the triangle.
}
Beispiel #8
0
bool Polygon::Intersects(const LineSegment &lineSegment) const
{
	Plane plane = PlaneCCW();
	float t;
	bool intersects = Plane::IntersectLinePlane(plane.normal, plane.d, lineSegment.a, lineSegment.b - lineSegment.a, t);
	if (!intersects || t < 0.f || t > 1.f)
		return false;

	return Contains(lineSegment.GetPoint(t));
}
Beispiel #9
0
bool Polygon::IsPlanar(float epsilon) const
{
	if (p.empty())
		return false;
	if (p.size() <= 3)
		return true;
	Plane plane = PlaneCCW();
	for(size_t i = 3; i < p.size(); ++i)
		if (plane.Distance(p[i]) > epsilon)
			return false;
	return true;
}
Beispiel #10
0
bool Polygon::IsSimple() const
{
	assume(IsPlanar());
	Plane plane = PlaneCCW();
	for(int i = 0; i < (int)p.size(); ++i)
	{
		LineSegment si = plane.Project(Edge(i));
		for(int j = i+2; j < (int)p.size(); ++j)
		{
			if (i == 0 && j == (int)p.size() - 1)
				continue; // These two edges are consecutive and share a vertex. Don't check that pair.
			LineSegment sj = plane.Project(Edge(j));
			if (si.Intersects(sj))
				return false;
		}
	}
	return true;
}
Beispiel #11
0
bool Polygon::Intersects(const LineSegment &lineSegment) const
{
	Plane plane = PlaneCCW();

	// Compute line-plane intersection (unroll Plane::IntersectLinePlane())
	float denom = Dot(plane.normal, lineSegment.b - lineSegment.a);

	if (Abs(denom) < 1e-4f) // The plane of the polygon and the line are planar? Do the test in 2D.
		return Intersects2D(LineSegment(POINT_VEC(MapTo2D(lineSegment.a), 0), POINT_VEC(MapTo2D(lineSegment.b), 0)));

	// The line segment properly intersects the plane of the polygon, so there is exactly one
	// point of intersection between the plane of the polygon and the line segment. Test that intersection point against
	// the line segment end points.
	float t = (plane.d - Dot(plane.normal, lineSegment.a)) / denom;
	if (t < 0.f || t > 1.f)
		return false;

	return Contains(lineSegment.GetPoint(t));
}
Beispiel #12
0
float3 Polygon::ClosestPoint(const float3 &point) const
{
    assume(IsPlanar());

    float3 ptOnPlane = PlaneCCW().Project(point);
    if (Contains(ptOnPlane))
        return ptOnPlane;

    float3 closestPt;
    float closestDist = FLOAT_MAX;
    for(int i = 0; i < NumEdges(); ++i)
    {
        float3 pt = Edge(i).ClosestPoint(point);
        float d = pt.DistanceSq(point);
        if (d < closestDist)
        {
            closestPt = pt;
            closestDist = d;
        }
    }
    return ptOnPlane;
}
Beispiel #13
0
bool Polygon::Contains(const LineSegment &worldSpaceLineSegment, float polygonThickness) const
{
	if (p.size() < 3)
		return false;

	Plane plane = PlaneCCW();
	if (plane.Distance(worldSpaceLineSegment.a) > polygonThickness ||
		plane.Distance(worldSpaceLineSegment.b) > polygonThickness)
		return false;

	// For robustness, project onto the polygon plane.
	LineSegment l = plane.Project(worldSpaceLineSegment);

	if (!Contains(l.a) || !Contains(l.b))
		return false;

	for(int i = 0; i < (int)p.size(); ++i)
		if (plane.Project(Edge(i)).Intersects(l))
			return false;

	return true;
}
Beispiel #14
0
float3 Polygon::EdgeNormal(int edgeIndex) const
{
	return Cross(Edge(edgeIndex).Dir(), PlaneCCW().normal).Normalized();
}
Beispiel #15
0
bool Polygon::Contains(const vec &worldSpacePoint, float polygonThicknessSq) const
{
	// Implementation based on the description from http://erich.realtimerendering.com/ptinpoly/

	if (p.size() < 3)
		return false;

	vec basisU = BasisU();
	vec basisV = BasisV();
	assert1(basisU.IsNormalized(), basisU);
	assert1(basisV.IsNormalized(), basisV);
	assert2(basisU.IsPerpendicular(basisV), basisU, basisV);
	assert3(basisU.IsPerpendicular(PlaneCCW().normal), basisU, PlaneCCW().normal, basisU.Dot(PlaneCCW().normal));
	assert3(basisV.IsPerpendicular(PlaneCCW().normal), basisV, PlaneCCW().normal, basisV.Dot(PlaneCCW().normal));

	vec normal = basisU.Cross(basisV);
//	float lenSq = normal.LengthSq(); ///\todo Could we treat basisU and basisV unnormalized here?
	float dot = normal.Dot(vec(p[0]) - worldSpacePoint);
	if (dot*dot > polygonThicknessSq)
		return false;

	int numIntersections = 0;

	const float epsilon = 1e-4f;

	// General strategy: transform all points on the polygon onto 2D face plane of the polygon, where the target query point is 
	// centered to lie in the origin.
	// If the test ray (0,0) -> (+inf, 0) intersects exactly an odd number of polygon edge segments, then the query point must have been
	// inside the polygon. The test ray is chosen like that to avoid all extra per-edge computations.
	vec vt = vec(p.back()) - worldSpacePoint;
	float2 p0 = float2(Dot(vt, basisU), Dot(vt, basisV));
	if (Abs(p0.y) < epsilon)
		p0.y = -epsilon; // Robustness check - if the ray (0,0) -> (+inf, 0) would pass through a vertex, move the vertex slightly.

	for(int i = 0; i < (int)p.size(); ++i)
	{
		vt = vec(p[i]) - worldSpacePoint;
		float2 p1 = float2(Dot(vt, basisU), Dot(vt, basisV));
		if (Abs(p1.y) < epsilon)
			p1.y = -epsilon; // Robustness check - if the ray (0,0) -> (+inf, 0) would pass through a vertex, move the vertex slightly.

		if (p0.y * p1.y < 0.f) // If the line segment p0 -> p1 straddles the line x=0, it could intersect the ray (0,0) -> (+inf, 0)
		{
			if (Min(p0.x, p1.x) > 0.f) // If both x-coordinates are positive, then there certainly is an intersection with the ray.
				++numIntersections;
			else if (Max(p0.x, p1.x) > 0.f) // If one of them is positive, there could be an intersection. (otherwise both are negative and they can't intersect ray)
			{
				// P = p0 + t*(p1-p0) == (x,0)
				//     p0.x + t*(p1.x-p0.x) == x
				//     p0.y + t*(p1.y-p0.y) == 0
				//                 t == -p0.y / (p1.y - p0.y)

				// Test whether the lines (0,0) -> (+inf,0) and p0 -> p1 intersect at a positive X-coordinate?
				float2 d = p1 - p0;
				if (d.y != 0.f)
				{
					float t = -p0.y / d.y; // The line segment parameter, t \in [0,1] forms the line segment p0->p1.
					float x = p0.x + t * d.x; // The x-coordinate of intersection with the ray.
					if (t >= 0.f && t <= 1.f && x > 0.f)
						++numIntersections;
				}
			}
		}
		p0 = p1;
	}

	return numIntersections % 2 == 1;
}
Beispiel #16
0
bool Polygon::Contains(const float3 &worldSpacePoint, float polygonThickness) const
{
    if (PlaneCCW().Distance(worldSpacePoint) > polygonThickness)
        return false;
    return Contains2D(MapTo2D(worldSpacePoint));
}
Beispiel #17
0
float3 Polygon::BasisV() const
{
	if (p.size() < 2)
		return float3::unitY;
	return Cross(PlaneCCW().normal, BasisU()).Normalized();
}
Beispiel #18
0
Plane Polygon::PlaneCW() const
{
	Plane plane = PlaneCCW();
	plane.ReverseNormal();
	return plane;
}
Beispiel #19
0
float3 Polygon::NormalCCW() const
{
	///@todo Optimize temporaries.
	return PlaneCCW().normal;
}