#pragma region CPL License
/*
Nuclex Native Framework
Copyright (C) 2002-2015 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#pragma endregion // CPL License
#ifndef NUCLEX_GEOMETRY_AREAS_TRACERS_POLYGON2TRACER_H
#define NUCLEX_GEOMETRY_AREAS_TRACERS_POLYGON2TRACER_H
#include "Nuclex/Geometry/Config.h"
#include "Nuclex/Geometry/Areas/Polygon2.h"
#include "Nuclex/Geometry/Lines/Line2.h"
#include "Nuclex/Geometry/LineContacts.h"
namespace Nuclex { namespace Geometry { namespace Areas { namespace Tracers {
// ------------------------------------------------------------------------------------------- //
/// Determines contacts between lines and 2D polygons
template
class Polygon2Tracer {
/// Determines where a line enters and where it exits a polygon
///
/// Line whose entry and exit through the polygon will be determined
///
/// Polygon the line will be checked against
/// The times at which the line enters and leaves the polygon
///
/// If the polygon is concave or self-intersects, this method will report
/// the times of first entry and last exit.
///
public: static LineContacts FindLineContacts(
const Lines::Line2 &line, const Polygon2 &polygon
) {
std::size_t vertexCount = polygon.Vertices.size();
if(vertexCount < 2) { // Point or empty polygon?
return LineContacts::None;
}
// Initialize the line contacts with the closing line segment from the polygon's
// last vertex to its first
LineContacts contacts;
contacts.EntryTime = contacts.ExitTime = findLineContact(
line, polygon.Vertices[vertexCount - 1], polygon.Vertices[0]
);
if(vertexCount == 2) {
return contacts;
}
// Now check for collisions with each of the polygon's edges and track the time
// of first entry and last exit.
for(std::size_t index = 0; index < vertexCount - 1; ++index) {
TScalar time = findLineContact(
line, polygon.Vertices[index], polygon.Vertices[index + 1]
);
if(Math::IsNan(contacts.EntryTime) || (time < contacts.EntryTime)) {
contacts.EntryTime = time;
}
if(Math::IsNan(contacts.ExitTime) || (time >= contacts.ExitTime)) {
contacts.ExitTime = time;
}
}
return contacts;
}
/// Determines where a line crosses a line segment
/// Line whose time of crossing will be determined
/// Point at which the line segment begins
/// Point at which the line segment ends
/// The time at which the line crosses the line segment
private: static TScalar findLineContact(
const Lines::Line2 &line,
const Point2 &start, const Point2 &end
) {
Vector2 segmentDirection = (end - start);
// Time of intersection of the segment with the traced line. This allows us to see
// if the segment hits the traced line inside its length.
TScalar s = (
line.Direction.X * (line.Origin.Y - start.Y) -
line.Direction.Y * (line.Origin.X - start.X)
) / (line.Direction.X * segmentDirection.Y - segmentDirection.X * line.Direction.Y);
if((s >= 0) && (s < 1)) {
TScalar t = (
segmentDirection.X * (line.Origin.Y - start.Y) -
segmentDirection.Y * (line.Origin.X - start.X)
) / (line.Direction.X * segmentDirection.Y - segmentDirection.X * line.Direction.Y);
return t;
} else {
return std::numeric_limits::signaling_NaN();
}
}
};
// ------------------------------------------------------------------------------------------- //
}}}} // namespace Nuclex::Geometry::Areas::Tracers
#endif // NUCLEX_GEOMETRY_AREAS_TRACERS_POLYGON2TRACER_H