#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