//
// ********************************************************************
// * License and Disclaimer                                           *
// *                                                                  *
// * The  Geant4 software  is  copyright of the Copyright Holders  of *
// * the Geant4 Collaboration.  It is provided  under  the terms  and *
// * conditions of the Geant4 Software License,  included in the file *
// * LICENSE and available at  http://cern.ch/geant4/license .  These *
// * include a list of copyright holders.                             *
// *                                                                  *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work  make  any representation or  warranty, express or implied, *
// * regarding  this  software system or assume any liability for its *
// * use.  Please see the license in the file  LICENSE  and URL above *
// * for the full disclaimer and the limitation of liability.         *
// *                                                                  *
// * This  code  implementation is the result of  the  scientific and *
// * technical work of the GEANT4 collaboration and of QinetiQ Ltd,   *
// * subject to DEFCON 705 IPR conditions.                            *
// * By using,  copying,  modifying or  distributing the software (or *
// * any work based  on the software)  you  agree  to acknowledge its *
// * use  in  resulting  scientific  publications,  and indicate your *
// * acceptance of all terms of the Geant4 Software license.          *
// ********************************************************************
//
//
// $Id: G4TessellatedGeometryAlgorithms.cc,v 1.6 2008/12/18 12:57:36 gunter Exp $
// GEANT4 tag $Name: geant4-09-03 $
//
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// MODULE:              G4TessellatedGeometryAlgorithms.cc
//
// Date:                07/08/2005
// Author:              Rickard Holmberg & Pete Truscott
// Organisation:        QinetiQ Ltd, UK (PT)
// Customer:            ESA-ESTEC / TEC-EES
// Contract:            
//
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// CHANGE HISTORY
// --------------
//
// 07 August 2007, P R Truscott, QinetiQ Ltd, UK - Created, with member
//                 functions based on the work of Rickard Holmberg.
//
// 26 September 2007
//                 P R Truscott, qinetiQ Ltd, UK
//                 Updated to assign values of location array, not update
//                 just the pointer.
//
///////////////////////////////////////////////////////////////////////////////

#include "G4TessellatedGeometryAlgorithms.hh"
///////////////////////////////////////////////////////////////////////////////
//
// Pointer to single instance of class.
//
G4TessellatedGeometryAlgorithms* G4TessellatedGeometryAlgorithms::fInstance = 0;

///////////////////////////////////////////////////////////////////////////////
//
// G4TessellatedGeometryAlgorithms
//
// Constructor doesn't need to do anything since this class just allows access
// to the geometric algorithms contained in member functions.
//
G4TessellatedGeometryAlgorithms::G4TessellatedGeometryAlgorithms ()
{
}

///////////////////////////////////////////////////////////////////////////////
//
// GetInstance
//
// This is the access point for this singleton.
//
G4TessellatedGeometryAlgorithms* G4TessellatedGeometryAlgorithms::GetInstance()
{
  static G4TessellatedGeometryAlgorithms worldStdGeom;
  if (!fInstance)
  {
    fInstance = &worldStdGeom;
  }
  return fInstance;
}

///////////////////////////////////////////////////////////////////////////////
//
// IntersectLineAndTriangle2D
//
// Determines whether there is an intersection between a line defined
// by r = p + s.v and a triangle defined by verticies P0, P0+E0 and P0+E1.
//
// Here:
//        p = 2D vector
//        s = scaler on [0,infinity)
//        v = 2D vector
//        P0, E0 and E1 are 2D vectors
// Information about where the intersection occurs is returned in the
// variable location.
//
// This is based on the work of Rickard Holmberg.
//
G4bool G4TessellatedGeometryAlgorithms::IntersectLineAndTriangle2D (
  const G4TwoVector p,  const G4TwoVector v,
  const G4TwoVector P0, const G4TwoVector E0, const G4TwoVector E1,
  G4TwoVector location[2])
{
  G4TwoVector loc0[2];
  G4int e0i = IntersectLineAndLineSegment2D (p,v,P0,E0,loc0);
  if (e0i == 2)
  {
    location[0] = loc0[0];
    location[1] = loc0[1];
    return true;
  }
  
  G4TwoVector loc1[2];
  G4int e1i = IntersectLineAndLineSegment2D (p,v,P0,E1,loc1);
  if (e1i == 2)
  {
    location[0] = loc1[0];
    location[1] = loc1[1];
    return true;
  }
  
  if ((e0i == 1) && (e1i == 1))
  {
    if ((loc0[0]-p).mag2() < (loc1[0]-p).mag2())
    {
      location[0] = loc0[0];
      location[1] = loc1[0];
    }
    else
    {
      location[0] = loc1[0];
      location[1] = loc0[0];
    }
    return true;
  }
  
  G4TwoVector P1 = P0 + E0;
  G4TwoVector DE = E1 - E0;
  G4TwoVector loc2[2];
  G4int e2i = IntersectLineAndLineSegment2D (p,v,P1,DE,loc2);
  if (e2i == 2)
  {
    location[0] = loc2[0];
    location[1] = loc2[1];
    return true;
  }

  if ((e0i == 0) && (e1i == 0) && (e2i == 0)) { return false; }

  if ((e0i == 1) && (e2i == 1))
  {
    if ((loc0[0]-p).mag2() < (loc2[0]-p).mag2())
    {
      location[0] = loc0[0];
      location[1] = loc2[0];
    }
    else
    {
      location[0] = loc2[0];
      location[1] = loc0[0];
    }
    return true;
  }

  if ((e1i == 1) && (e2i == 1))
  {
    if ((loc1[0]-p).mag2() < (loc2[0]-p).mag2())
    {
      location[0] = loc1[0];
      location[1] = loc2[0];
    }
    else
    {
      location[0] = loc2[0];
      location[1] = loc1[0];
    }
    return true;
  }

  return false;
}

///////////////////////////////////////////////////////////////////////////////
//
// IntersectLineAndLineSegment2D
//
// Determines whether there is an intersection between a line defined
// by r = P0 + s.D0 and a line-segment with endpoints P1 and P1+D1.
// Here:
//        P0 = 2D vector
//        s  = scaler on [0,infinity)
//        D0 = 2D vector
//        P1 and D1 are 2D vectors
//
// This function returns:
// 0 - if there is no intersection;
// 1 - if there is a unique intersection;
// 2 - if the line and line-segments overlap, and the intersection is a
//     segment itself.
// Information about where the intersection occurs is returned in the
// as ??.
//
// This is based on the work of Rickard Holmberg as well as material published
// by Philip J Schneider and David H Eberly, "Geometric Tools for Computer
// Graphics," ISBN 1-55860-694-0, pp 244-245, 2003.
//
G4int G4TessellatedGeometryAlgorithms::IntersectLineAndLineSegment2D (
  const G4TwoVector P0, const G4TwoVector D0,
  const G4TwoVector P1, const G4TwoVector D1,
  G4TwoVector location[2])
{
  G4TwoVector E     = P1 - P0;
  G4double kross    = cross(D0,D1);
  G4double sqrKross = kross * kross;
  G4double sqrLen0  = D0.mag2();
  G4double sqrLen1  = D1.mag2();
  location[0]       = G4TwoVector(0.0,0.0);
  location[1]       = G4TwoVector(0.0,0.0);

  if (sqrKross > DBL_EPSILON * DBL_EPSILON * sqrLen0 * sqrLen1)
  {
//
//
// The line and line segment are not parallel.  Determine if the intersection
// is in positive s where r = P0 + s*D0, and for 0<=t<=1 where r = p1 + t*D1.
//
    G4double s = cross(E,D1)/kross;
    if (s < 0)          return 0; // Intersection does not occur for positive s.
    G4double t = cross(E,D0)/kross;
    if (t < 0 || t > 1) return 0; // Intersection does not occur on line-segment.
//
//
// Intersection of lines is a single point on the forward-propagating line
// defined by r = P0 + s*D0, and the line segment defined by  r = p1 + t*D1.
//
    location[0] = P0 + s*D0;
    return 1;
  }
//
//
// Line and line segment are parallel.  Determine whether they overlap or not.
//
  G4double sqrLenE = E.mag2();
  kross            = cross(E,D0);
  sqrKross         = kross * kross;
  if (sqrKross > DBL_EPSILON * DBL_EPSILON * sqrLen0 * sqrLenE)
  {
    return 0; //Lines are different.
  }
//
//
// Lines are the same.  Test for overlap.
//
  G4double s0   = D0.dot(E)/sqrLen0;
  G4double s1   = s0 + D0.dot(D1)/sqrLen0;
  G4double smin = 0.0;
  G4double smax = 0.0;

  if (s0 < s1) {smin = s0; smax = s1;}
  else         {smin = s1; smax = s0;}

  if (smax < 0.0) return 0;
  else if (smin < 0.0)
  {
    location[0] = P0;
    location[1] = P0 + smax*D0;
    return 2;
  }
  else
  {
    location[0] = P0 + smin*D0;
    location[1] = P0 + smax*D0;
    return 2;
  }
}
