/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 * 
 *
 *   This file is part of MoleCuilder.
 *
 *    MoleCuilder is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    MoleCuilder 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
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoleCuilder.  If not, see .
 */
/*
 * Tesselation_BoundaryTriangleUnitTest.cpp
 *
 *  Created on: Jan 13, 2010
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
using namespace std;
#include 
#include 
#include 
#include 
#include 
#include "CodePatterns/Log.hpp"
#include "Helpers/defs.hpp"
#include "Atom/TesselPoint.hpp"
#include "LinearAlgebra/Plane.hpp"
#include "LinearAlgebra/RealSpaceMatrix.hpp"
#include "LinearAlgebra/VectorSet.hpp"
#include "Tesselation/BoundaryPointSet.hpp"
#include "Tesselation/BoundaryLineSet.hpp"
#include "Tesselation/BoundaryTriangleSet.hpp"
#include "Tesselation/CandidateForTesselation.hpp"
#include "Tesselation_BoundaryTriangleUnitTest.hpp"
#ifdef HAVE_TESTRUNNER
#include "UnitTestMain.hpp"
#endif /*HAVE_TESTRUNNER*/
const double TesselationBoundaryTriangleTest::SPHERERADIUS=2.;
/********************************************** Test classes **************************************/
// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( TesselationBoundaryTriangleTest );
void TesselationBoundaryTriangleTest::createTriangle(const std::vector &Vectors)
{
  CPPUNIT_ASSERT_EQUAL( (size_t)NDIM, Vectors.size() );
  // create nodes
  for (size_t count = 0; count < NDIM; ++count) {
    tesselpoints[count] = new TesselPoint;
    tesselpoints[count]->setPosition( Vectors[count] );
    tesselpoints[count]->setName(toString(count));
    tesselpoints[count]->setNr(count);
    points[count] = new BoundaryPointSet(tesselpoints[count]);
  }
  // create line
  lines[0] = new BoundaryLineSet(points[0], points[1], 0);
  lines[1] = new BoundaryLineSet(points[1], points[2], 1);
  lines[2] = new BoundaryLineSet(points[0], points[2], 2);
  // create triangle
  triangle = new BoundaryTriangleSet(lines, 0);
  Plane p(Vectors[0], Vectors[1], Vectors[2]);
  triangle->GetNormalVector(p.getNormal());
}
/** This cleanly removes a triangle created via createTriangle() from memory.
 *
 */
void TesselationBoundaryTriangleTest::removeTriangle()
{
  delete(triangle);
  for (int i=0;i Vectors;
  Vectors.push_back( Vector(0., 0., 0.) );
  Vectors.push_back( Vector(0., 1., 0.) );
  Vectors.push_back( Vector(1., 0., 0.) );
  // create triangle
  createTriangle(Vectors);
}
void TesselationBoundaryTriangleTest::tearDown()
{
  removeTriangle();
  logger::purgeInstance();
  errorLogger::purgeInstance();
}
/** UnitTest for Tesselation::IsInsideTriangle()
 */
void TesselationBoundaryTriangleTest::IsInsideTriangleTest()
{
  // inside points
  {
    // check each endnode
    for (size_t i=0; i< NDIM; ++i) {
      const Vector testPoint(triangle->endpoints[i]->node->getPosition());
      LOG(1, "INFO: Testing whether " << testPoint << " is an inner point.");
      CPPUNIT_ASSERT( triangle->IsInsideTriangle( testPoint ) );
    }
  }
  {
    // check points along each BoundaryLine
    for (size_t i=0; i< NDIM; ++i) {
      Vector offset = triangle->endpoints[i%3]->node->getPosition();
      Vector direction = triangle->endpoints[(i+1)%3]->node->getPosition() - offset;
      for (double s = 0.1; s < 1.; s+= 0.1) {
        Vector testPoint = offset + s*direction;
        LOG(1, "INFO: Testing whether " << testPoint << " is an inner point.");
        CPPUNIT_ASSERT( triangle->IsInsideTriangle( testPoint ) );
      }
    }
  }
  {
    // check central point
    Vector center;
    triangle->GetCenter(center);
    LOG(1, "INFO: Testing whether " << center << " is an inner point.");
    CPPUNIT_ASSERT( triangle->IsInsideTriangle( center ) );
  }
  // outside points
  {
    // check points outside (i.e. those not in xy-plane through origin)
    double n[3];
    const double boundary = 4.;
    const double step = 1.;
    // go through the cube and check each point
    for (n[0] = -boundary; n[0] <= boundary; n[0]+=step)
      for (n[1] = -boundary; n[1] <= boundary; n[1]+=step)
        for (n[2] = -boundary; n[2] <= boundary; n[2]+=step) {
          const Vector testPoint(n[0], n[1], n[2]);
          if (n[2] != 0) {
            LOG(1, "INFO: Testing whether " << testPoint << " is not an inner point.");
            CPPUNIT_ASSERT( !triangle->IsInsideTriangle( testPoint ) );
          }
        }
  }
  {
    // check points within the plane but outside of triangle
    double n[3];
    const double boundary = 4.;
    const double step = 1.;
    n[2] = 0;
    for (n[0] = -boundary; n[0] <= boundary; n[0]+=step)
      for (n[1] = -boundary; n[1] <= boundary; n[1]+=step) {
        const Vector testPoint(n[0], n[1], n[2]);
        if ((n[0] >=0) && (n[1] >=0) && (n[0]<=1) && (n[1]<=1)) {
          if (n[0]+n[1] <= 1) {
            LOG(1, "INFO: Testing whether " << testPoint << " is an inner point.");
            CPPUNIT_ASSERT( triangle->IsInsideTriangle( testPoint) );
          } else {
            LOG(1, "INFO: Testing whether " << testPoint << " is not an inner point.");
            CPPUNIT_ASSERT( !triangle->IsInsideTriangle( testPoint) );
          }
        } else {
          LOG(1, "INFO: Testing whether " << testPoint << " is not an inner point.");
          CPPUNIT_ASSERT( !triangle->IsInsideTriangle( testPoint) );
        }
      }
  }
}
/** UnitTest for Tesselation::IsInsideTriangle()
 *
 * We test for some specific points that occured in larger examples of the code.
 *
 */
void TesselationBoundaryTriangleTest::IsInsideTriangle_specificTest()
{
  {
    removeTriangle();
    // test is from --create-micelle 200 --radius 30. --position "0,0,0" of sles.data
    // failure is: Intersection (23.1644,24.1867,65.1272) is not inside triangle [659|Na2451,O3652,Na3762].
    const Vector testPoint(1.57318,1.57612,10.9874);
    const Vector testIntersection(23.1644,24.1867,65.1272);
    std::vector vectors;
    vectors.push_back( Vector(23.0563,30.4673,73.7555) );
    vectors.push_back( Vector(25.473,25.1512,68.5467) );
    vectors.push_back( Vector(23.1644,24.1867,65.1272) );
    createTriangle(vectors);
    CPPUNIT_ASSERT( triangle->IsInsideTriangle( testIntersection ) );
  }
  {
    removeTriangle();
    // test is from --create-micelle 200 --radius 30. --position "0,0,0" of sles.data
    // failure is: Intersection (20.6787,70.655,71.5657) is not inside triangle [622|Na1197,Na2166,O3366].
    // fix is lower LINALG_MYEPSILON (not std::numeric_limits*100 but *1e-4)
    const Vector testPoint(1.57318,14.185,61.2155);
    const Vector testIntersection(20.67867516517798,70.65496977054023,71.56572984946152);
    std::vector vectors;
    vectors.push_back( Vector(22.9592,68.7791,77.5907) );
    vectors.push_back( Vector(18.4729,72.0386,68.08839999999999) );
    vectors.push_back( Vector(20.3834,71.0154,70.1443) );
    createTriangle(vectors);
    CPPUNIT_ASSERT( triangle->IsInsideTriangle( testIntersection ) );
  }
  {
    removeTriangle();
    // test is from --create-micelle 200 --radius 30. --position "0,0,0" of sles.data
    // failure is:Intersection (27.56537519896,13.40256646925,6.672946688134) is not inside triangle [702|Na5016,O6388,Na6498].
    // note that the Intersection cannot possibly lie be within the triangle!
    // we test now against correct intersection
    const Vector testIntersection(14.6872,36.204,39.8043);
    std::vector vectors;
    vectors.push_back( Vector(10.7513,43.4247,48.4127) );
    vectors.push_back( Vector(13.7119,37.0827,47.4203) );
    vectors.push_back( Vector(14.6872,36.204,39.8043) );
    createTriangle(vectors);
    CPPUNIT_ASSERT( triangle->IsInsideTriangle( testIntersection ) );
  }
}
/** UnitTest for Tesselation::GetClosestPointInsideTriangle()
 *
 * We check whether this function always returns a intersection inside the
 * triangle.
 *
 */
void TesselationBoundaryTriangleTest::GetClosestPointInsideTriangleTest()
{
  Vector TestIntersection;
  {
    // march through a cube mesh on triangle in xy plane
    double n[3];
    const double boundary = 4.;
    const double step = 1.;
    // go through the cube and check each point
    for (n[0] = -boundary; n[0] <= boundary; n[0]+=step)
      for (n[1] = -boundary; n[1] <= boundary; n[1]+=step)
        for (n[2] = -boundary; n[2] <= boundary; n[2]+=step) {
          const Vector testPoint(n[0], n[1], n[2]);
          triangle->GetClosestPointInsideTriangle(testPoint, TestIntersection);
          CPPUNIT_ASSERT( triangle->IsInsideTriangle( TestIntersection ));
        }
  }
  removeTriangle();
  // create better triangle;
  VECTORSET(std::vector) Vectors;
  Vectors.push_back( Vector(0., 0., 0.) );
  Vectors.push_back( Vector(0., 1., 0.) );
  Vectors.push_back( Vector(1., 0., 0.) );
  RealSpaceMatrix M;
  M.setRotation(M_PI/3., M_PI/4., 2.*M_PI/3.);
  Vectors.transform(M);
  createTriangle(Vectors);
  {
    // march through a cube mesh on rotated triangle
    double n[3];
    const double boundary = 4.;
    const double step = 1.;
    // go through the cube and check each point
    for (n[0] = -boundary; n[0] <= boundary; n[0]+=step)
      for (n[1] = -boundary; n[1] <= boundary; n[1]+=step)
        for (n[2] = -boundary; n[2] <= boundary; n[2]+=step) {
          const Vector testPoint(n[0], n[1], n[2]);
          triangle->GetClosestPointInsideTriangle(testPoint, TestIntersection);
          CPPUNIT_ASSERT( triangle->IsInsideTriangle( TestIntersection ));
        }
  }
}
/** UnitTest for Tesselation::GetClosestPointInsideTriangle()
 *
 * We test for some specific points that occured in larger examples of the code.
 *
 */
void TesselationBoundaryTriangleTest::GetClosestPointInsideTriangle_specificTest()
{
  {
    removeTriangle();
    // test is from --create-micelle 200 --radius 30. --position "0,0,0" of sles.data
    // failure is:Intersection (27.56537519896,13.40256646925,6.672946688134) is not inside triangle [702|Na5016,O6388,Na6498].
    // note that the Intersection cannot possibly lie be within the triangle
    // however, it is on its plane (off only by 2.7e-12)
    // testPoint2 is corrected version
    const Vector testPoint(27.56537519896,13.40256646925,6.672946688134);
    const Vector testPoint2(14.6872,36.204,39.8043);
    Vector testIntersection;
    std::vector vectors;
    vectors.push_back( Vector(10.7513,43.4247,48.4127) );
    vectors.push_back( Vector(13.7119,37.0827,47.4203) );
    vectors.push_back( Vector(14.6872,36.204,39.8043) );
    createTriangle(vectors);
    triangle->GetClosestPointInsideTriangle(testPoint, testIntersection);
    CPPUNIT_ASSERT ( testPoint != testIntersection );
    CPPUNIT_ASSERT ( testPoint2 == testIntersection );
  }
}
/** UnitTest for Tesselation::IsInnerPoint()
 */
void TesselationBoundaryTriangleTest::GetClosestPointOnPlaneTest()
{
  Vector TestIntersection;
  Vector Point;
  // simple test on y line
  Point = Vector(-1.,0.5,0.);
  CPPUNIT_ASSERT_EQUAL( 1., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.,0.5,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  Point = Vector(-4.,0.5,0.);
  CPPUNIT_ASSERT_EQUAL( 16., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.,0.5,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  // simple test on x line
  Point = Vector(0.5,-1.,0.);
  CPPUNIT_ASSERT_EQUAL( 1., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.5,0.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  Point = Vector(0.5,-6.,0.);
  CPPUNIT_ASSERT_EQUAL( 36., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.5,0.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  // simple test on slanted line
  Point = Vector(1.,1.,0.);
  CPPUNIT_ASSERT_EQUAL( 0.5, triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.5,0.5,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  Point = Vector(5.,5.,0.);
  CPPUNIT_ASSERT_EQUAL( 40.5, triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.5,0.5,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  // simple test on first node
  Point = Vector(-1.,-1.,0.);
  CPPUNIT_ASSERT_EQUAL( 2., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.,0.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  // simple test on second node
  Point = Vector(0.,2.,0.);
  CPPUNIT_ASSERT_EQUAL( 1., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.,1.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  // simple test on third node
  Point = Vector(2.,0.,0.);
  CPPUNIT_ASSERT_EQUAL( 1., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(1.,0.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
};
/** UnitTest for Tesselation::IsInnerPoint()
 */
void TesselationBoundaryTriangleTest::GetClosestPointOffPlaneTest()
{
  Vector TestIntersection;
  Vector Point;
  // straight down/up
  Point = Vector(1./3.,1./3.,+5.);
  CPPUNIT_ASSERT_EQUAL( 25. , triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(1./3.,1./3.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  Point = Vector(1./3.,1./3.,-5.);
  CPPUNIT_ASSERT_EQUAL( 25. , triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(1./3.,1./3.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  // simple test on y line
  Point = Vector(-1.,0.5,+2.);
  CPPUNIT_ASSERT_EQUAL( 5., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.,0.5,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  Point = Vector(-1.,0.5,-3.);
  CPPUNIT_ASSERT_EQUAL( 10., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.,0.5,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  // simple test on x line
  Point = Vector(0.5,-1.,+1.);
  CPPUNIT_ASSERT_EQUAL( 2., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.5,0.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  Point = Vector(0.5,-1.,-2.);
  CPPUNIT_ASSERT_EQUAL( 5., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.5,0.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  // simple test on slanted line
  Point = Vector(1.,1.,+3.);
  CPPUNIT_ASSERT_EQUAL( 9.5, triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.5,0.5,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  Point = Vector(1.,1.,-4.);
  CPPUNIT_ASSERT_EQUAL( 16.5, triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.5,0.5,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  // simple test on first node
  Point = Vector(-1.,-1.,5.);
  CPPUNIT_ASSERT_EQUAL( 27., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.,0.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  // simple test on second node
  Point = Vector(0.,2.,5.);
  CPPUNIT_ASSERT_EQUAL( 26., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(0.,1.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
  // simple test on third node
  Point = Vector(2.,0.,5.);
  CPPUNIT_ASSERT_EQUAL( 26., triangle->GetClosestPointInsideTriangle(Point, TestIntersection) );
  Point = Vector(1.,0.,0.);
  CPPUNIT_ASSERT_EQUAL( true , Point == TestIntersection );
};