#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_RECTANGLE2TRACER_H
#define NUCLEX_GEOMETRY_AREAS_TRACERS_RECTANGLE2TRACER_H
#include "Nuclex/Geometry/Config.h"
#include "Nuclex/Geometry/Areas/Rectangle2.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 rectangles
  template 
  class Rectangle2Tracer {
    /// Determines where a line enters and where it exits a rectangle
    /// Line whose entry and exit times will be found
    /// Rectangle that will be traced against the line
    /// The times at which the line enters and leaves the rectangle
    public: static LineContacts FindLineContacts(
      const Lines::Line2 &line, const Rectangle2 &rectangle
    ) {
      LineContacts contacts;
      // If the line is not parallel to the Y axis, find out when it will cross
      // the limits of the rectangle horizontally
      if(line.Direction.X != 0) {
        TScalar leftTouchTime = (rectangle.Min.X - line.Origin.X) / line.Direction.X;
        TScalar rightTouchTime = (rectangle.Max.X - line.Origin.X) / line.Direction.X;
        contacts = LineContacts::FromExtremes(leftTouchTime, rightTouchTime);
      } else if((line.Origin.X < rectangle.Min.X) || (line.Origin.X >= rectangle.Max.X)) {
        return LineContacts::None;
      }
      // If the line is not parallel to the X axis, find out when it will cross
      // the limits of the rectangle vertically
      if(line.Direction.Y != 0) {
        TScalar topTouchTime = (rectangle.Min.Y - line.Origin.Y) / line.Direction.Y;
        TScalar bottomTouchTime = (rectangle.Max.Y - line.Origin.Y) / line.Direction.Y;
        // If the line was parallel to the Y axis (and we already made sure that it actually
        // goes through the rectangle), only the Y axis needs to be taken into account.
        if(line.Direction.X == 0) {
          contacts = LineContacts::FromExtremes(topTouchTime, bottomTouchTime);
        } else {
          std::pair times = Math::Extremes(
            topTouchTime, bottomTouchTime
          );
          // If the line already left the rectangle on one axis before entering it on
          // the other, it is going past the corner of the rectangle without touching it
          if((times.second < contacts.EntryTime) || (contacts.ExitTime < times.first)) {
            return LineContacts::None;
          }
          contacts.EntryTime = Math::Max(times.first, contacts.EntryTime);
          contacts.ExitTime = Math::Min(times.second, contacts.ExitTime);
        }
      } else if((line.Origin.Y < rectangle.Min.Y) || (line.Origin.Y > rectangle.Max.Y)) {
        return LineContacts::None;
      }
      return contacts;
    }
  };
  // ------------------------------------------------------------------------------------------- //
}}}} // namespace Nuclex::Geometry::Areas::Tracers
#endif // NUCLEX_GEOMETRY_AREAS_TRACERS_RECTANGLE2TRACER_H