You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
5.5 KiB
150 lines
5.5 KiB
// David Eberly, Geometric Tools, Redmond WA 98052
|
|
// Copyright (c) 1998-2021
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// https://www.boost.org/LICENSE_1_0.txt
|
|
// https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
|
|
// Version: 4.0.2019.08.13
|
|
|
|
#pragma once
|
|
|
|
#include <Mathematics/IntrConvexPolygonHyperplane.h>
|
|
#include <Mathematics/Triangle.h>
|
|
#include <Mathematics/Vector2.h>
|
|
|
|
// The test-intersection queries are based on the document
|
|
// https://www.geometrictools.com/Documentation/MethodOfSeparatingAxes.pdf
|
|
// The find-intersection query for stationary triangles is based on clipping
|
|
// one triangle against the edges of the other to compute the intersection
|
|
// set (if it exists). The find-intersection query for moving triangles is
|
|
// based on the previously mentioned document about the method of separating
|
|
// axes.
|
|
|
|
namespace gte
|
|
{
|
|
// Test whether two triangles intersect using the method of separating
|
|
// axes. The set of intersection, if it exists, is not computed. The
|
|
// input triangles' vertices must be counterclockwise ordered.
|
|
template <typename Real>
|
|
class TIQuery<Real, Triangle2<Real>, Triangle2<Real>>
|
|
{
|
|
public:
|
|
struct Result
|
|
{
|
|
bool intersect;
|
|
};
|
|
|
|
Result operator()(Triangle2<Real> const& triangle0, Triangle2<Real> const& triangle1)
|
|
{
|
|
Result result =
|
|
{
|
|
!Separated(triangle0, triangle1) && !Separated(triangle1, triangle0)
|
|
};
|
|
return result;
|
|
}
|
|
|
|
protected:
|
|
// The triangle vertices are projected to t-values for the line P+t*D.
|
|
// The D-vector is nonzero but does not have to be unit length. The
|
|
// return value is +1 if all t >= 0, -1 if all t <= 0, but 0 otherwise
|
|
// in which case the line splits the triangle into two subtriangles,
|
|
// each of positive area.
|
|
int WhichSide(Triangle2<Real> const& triangle, Vector2<Real> const& P, Vector2<Real> const& D) const
|
|
{
|
|
int positive = 0, negative = 0;
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
Real t = Dot(D, triangle.v[i] - P);
|
|
if (t > (Real)0)
|
|
{
|
|
++positive;
|
|
}
|
|
else if (t < (Real)0)
|
|
{
|
|
--negative;
|
|
}
|
|
|
|
if (positive && negative)
|
|
{
|
|
// The triangle has vertices strictly on both sides of
|
|
// the line, so the line splits the triangle into two
|
|
// subtriangles each of positive area.
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Either positive > 0 or negative > 0 but not both are positive.
|
|
return (positive > 0 ? +1 : -1);
|
|
}
|
|
|
|
bool Separated(Triangle2<Real> const& triangle0, Triangle2<Real> const& triangle1) const
|
|
{
|
|
// Test edges of triangle0 for separation. Because of the
|
|
// counterclockwise ordering, the projection interval for
|
|
// triangle0 is [T,0] for some T < 0. Determine whether
|
|
// triangle1 is on the positive side of the line; if it is,
|
|
// the triangles are separated.
|
|
for (int i0 = 2, i1 = 0; i1 < 3; i0 = i1++)
|
|
{
|
|
// The potential separating axis is P+t*D.
|
|
Vector2<Real> P = triangle0.v[i0];
|
|
Vector2<Real> D = Perp(triangle0.v[i1] - triangle0.v[i0]);
|
|
if (WhichSide(triangle1, P, D) > 0)
|
|
{
|
|
// The triangle1 projection interval is [a,b] where a > 0,
|
|
// so the triangles are separated.
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Find the convex polygon, segment or point of intersection of two
|
|
// triangles. The input triangles' vertices must be counterclockwise
|
|
// ordered.
|
|
template <typename Real>
|
|
class FIQuery<Real, Triangle2<Real>, Triangle2<Real>>
|
|
{
|
|
public:
|
|
struct Result
|
|
{
|
|
// An intersection exists iff intersection.size() > 0.
|
|
std::vector<Vector2<Real>> intersection;
|
|
};
|
|
|
|
Result operator()(Triangle2<Real> const& triangle0, Triangle2<Real> const& triangle1)
|
|
{
|
|
Result result;
|
|
|
|
// Start with triangle1 and clip against the edges of triangle0.
|
|
std::vector<Vector2<Real>> polygon =
|
|
{
|
|
triangle1.v[0], triangle1.v[1], triangle1.v[2]
|
|
};
|
|
|
|
typedef FIQuery<Real, std::vector<Vector<2, Real>>, Hyperplane<2, Real>> PPQuery;
|
|
PPQuery ppQuery;
|
|
|
|
for (int i1 = 2, i0 = 0; i0 < 3; i1 = i0++)
|
|
{
|
|
// Create the clipping line for the current edge. The edge
|
|
// normal N points inside the triangle.
|
|
Vector2<Real> P = triangle0.v[i0];
|
|
Vector2<Real> N = Perp(triangle0.v[i1] - triangle0.v[i0]);
|
|
Hyperplane<2, Real> clippingLine(N, Dot(N, P));
|
|
|
|
// Do the clipping operation.
|
|
auto ppResult = ppQuery(polygon, clippingLine);
|
|
if (ppResult.positivePolygon.size() == 0)
|
|
{
|
|
// The current clipped polygon is outside triangle0.
|
|
return result;
|
|
}
|
|
polygon = std::move(ppResult.positivePolygon);
|
|
}
|
|
|
|
result.intersection = polygon;
|
|
return result;
|
|
}
|
|
};
|
|
}
|
|
|