/*
* 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 .
*/
/*
* Line.cpp
*
* Created on: Apr 30, 2010
* Author: crueger
*/
// include config.h
#ifdef HAVE_CONFIG_H
#include
#endif
#include "CodePatterns/MemDebug.hpp"
#include "Line.hpp"
#include
#include
#include
#include "CodePatterns/Info.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Verbose.hpp"
#include "defs.hpp"
#include "Exceptions.hpp"
#include "LinePoint.hpp"
#include "MatrixContent.hpp"
#include "Plane.hpp"
#include "RealSpaceMatrix.hpp"
#include "Vector.hpp"
using namespace std;
Line::Line(const Vector &_origin, const Vector &_direction) :
direction(new Vector(_direction))
{
direction->Normalize();
origin.reset(new Vector(_origin.partition(*direction).second));
}
Line::Line(const Line &src) :
origin(new Vector(*src.origin)),
direction(new Vector(*src.direction))
{}
Line::~Line()
{}
Line &Line::operator=(const Line& rhs){
if(this!=&rhs){
origin.reset(new Vector(*rhs.origin));
direction.reset(new Vector(*rhs.direction));
}
return *this;
}
double Line::distance(const Vector &point) const{
// get any vector from line to point
Vector helper = point - *origin;
// partition this vector along direction
// the residue points from the line to the point
return helper.partition(*direction).second.Norm();
}
Vector Line::getClosestPoint(const Vector &point) const{
// get any vector from line to point
Vector helper = point - *origin;
// partition this vector along direction
// add only the part along the direction
return *origin + helper.partition(*direction).first;
}
Vector Line::getDirection() const{
return *direction;
}
Vector Line::getOrigin() const{
return *origin;
}
vector Line::getPointsOnLine() const{
vector res;
res.reserve(2);
res.push_back(*origin);
res.push_back(*origin+*direction);
return res;
}
/** Calculates the intersection of the two lines that are both on the same plane.
* This is taken from Weisstein, Eric W. "Line-Line Intersection." From MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/Line-LineIntersection.html
* \param *out output stream for debugging
* \param *Line1a first vector of first line
* \param *Line1b second vector of first line
* \param *Line2a first vector of second line
* \param *Line2b second vector of second line
* \return true - \a this will contain the intersection on return, false - lines are parallel
*/
Vector Line::getIntersection(const Line& otherLine) const{
pointset line1Points = getPointsOnLine();
Vector Line1a = line1Points[0];
Vector Line1b = line1Points[1];
pointset line2Points = otherLine.getPointsOnLine();
Vector Line2a = line2Points[0];
Vector Line2b = line2Points[1];
Vector res;
auto_ptr M = auto_ptr(new MatrixContent(4,4));
M->setValue(1.);
for (int i=0;i<3;i++) {
M->set(0, i, Line1a[i]);
M->set(1, i, Line1b[i]);
M->set(2, i, Line2a[i]);
M->set(3, i, Line2b[i]);
}
//Log() << Verbose(1) << "Coefficent matrix is:" << endl;
//for (int i=0;i<4;i++) {
// for (int j=0;j<4;j++)
// cout << "\t" << M->Get(i,j);
// cout << endl;
//}
if (fabs(M->Determinant()) > LINALG_MYEPSILON()) {
eLog() << Verbose(2) << "Determinant of coefficient matrix is NOT zero." << endl;
throw SkewException() << LinearAlgebraDeterminant(M->Determinant()) << LinearAlgebraMatrixContent(&(*M));
}
Log() << Verbose(6) << "INFO: Line1a = " << Line1a << ", Line1b = " << Line1b << ", Line2a = " << Line2a << ", Line2b = " << Line2b << "." << endl;
// constuct a,b,c
Vector a = Line1b - Line1a;
Vector b = Line2b - Line2a;
Vector c = Line2a - Line1a;
Vector d = Line2b - Line1b;
Log() << Verbose(7) << "INFO: a = " << a << ", b = " << b << ", c = " << c << "." << endl;
if ((a.NormSquared() <= LINALG_MYEPSILON()) || (b.NormSquared() <= LINALG_MYEPSILON())) {
res.Zero();
eLog() << Verbose(2) << "At least one of the lines is ill-defined, i.e. offset equals second vector." << endl;
throw LinearDependenceException() << LinearAlgebraVectorPair( make_pair(&a, &b) );
}
// check for parallelity
Vector parallel;
double factor = 0.;
if (fabs(a.ScalarProduct(b)*a.ScalarProduct(b)/a.NormSquared()/b.NormSquared() - 1.) <= LINALG_MYEPSILON()) {
parallel = Line1a - Line2a;
factor = parallel.ScalarProduct(a)/a.Norm();
if ((factor > -LINALG_MYEPSILON()) && (factor - 1. <= LINALG_MYEPSILON())) {
res = Line2a;
Log() << Verbose(5) << "Lines conincide." << endl;
return res;
} else {
parallel = Line1a - Line2b;
factor = parallel.ScalarProduct(a)/a.Norm();
if ((factor > -LINALG_MYEPSILON()) && (factor - 1. <= LINALG_MYEPSILON())) {
res = Line2b;
Log() << Verbose(5) << "Lines conincide." << endl;
return res;
}
}
eLog() << Verbose(2) << "Lines are parallel." << endl;
res.Zero();
throw LinearDependenceException() << LinearAlgebraVectorPair( make_pair(&a, &b) );
}
// obtain s
double s;
Vector temp1, temp2;
temp1 = c;
temp1.VectorProduct(b);
temp2 = a;
temp2.VectorProduct(b);
Log() << Verbose(7) << "INFO: temp1 = " << temp1 << ", temp2 = " << temp2 << "." << endl;
if (fabs(temp2.NormSquared()) > LINALG_MYEPSILON())
s = temp1.ScalarProduct(temp2)/temp2.NormSquared();
else
s = 0.;
Log() << Verbose(6) << "Factor s is " << temp1.ScalarProduct(temp2) << "/" << temp2.NormSquared() << " = " << s << "." << endl;
// construct intersection
res = a;
res.Scale(s);
res += Line1a;
Log() << Verbose(5) << "Intersection is at " << res << "." << endl;
return res;
}
/** Rotates the vector by an angle of \a alpha around this line.
* \param rhs Vector to rotate
* \param alpha rotation angle in radian
*/
Vector Line::rotateVector(const Vector &rhs, double alpha) const{
Vector helper = rhs;
// translate the coordinate system so that the line goes through (0,0,0)
helper -= *origin;
// partition the vector into a part that gets rotated and a part that lies along the line
pair parts = helper.partition(*direction);
// we just keep anything that is along the axis
Vector res = parts.first;
// the rest has to be rotated
Vector a = parts.second;
// we only have to do the rest, if we actually could partition the vector
if(!a.IsZero()){
// construct a vector that is orthogonal to a and direction and has length |a|
Vector y = a;
// direction is normalized, so the result has length |a|
y.VectorProduct(*direction);
res += cos(alpha) * a + sin(alpha) * y;
}
// translate the coordinate system back
res += *origin;
return res;
}
Line Line::rotateLine(const Line &rhs, double alpha) const{
Vector lineOrigin = rotateVector(rhs.getOrigin(),alpha);
Vector helper = rhs.getDirection();
// rotate the direction without considering the ofset
pair parts = helper.partition(*direction);
Vector lineDirection = parts.first;
Vector a = parts.second;
if(!a.IsZero()){
// construct a vector that is orthogonal to a and direction and has length |a|
Vector y = a;
// direction is normalized, so the result has length |a|
y.VectorProduct(*direction);
lineDirection += cos(alpha) * a + sin(alpha) * y;
}
return Line(lineOrigin,lineDirection);
}
Plane Line::rotatePlane(const Plane &rhs, double alpha) const{
vector points = rhs.getPointsOnPlane();
transform(points.begin(),
points.end(),
points.begin(),
boost::bind(&Line::rotateVector,this,_1,alpha));
return Plane(points[0],points[1],points[2]);
}
RealSpaceMatrix Line::getRotationMatrix(double alpha) const
{
RealSpaceMatrix M;
M.setRotation(getDirection(), alpha);
return M;
}
Plane Line::getOrthogonalPlane(const Vector &origin) const{
return Plane(getDirection(),origin);
}
std::vector Line::getSphereIntersections() const{
std::vector res;
// line is kept in normalized form, so we can skip a lot of calculations
double discriminant = 1-origin->NormSquared();
// we might have 2, 1 or 0 solutions, depending on discriminant
if(discriminant>=0){
if(discriminant==0){
res.push_back(*origin);
}
else{
Vector helper = sqrt(discriminant)*(*direction);
res.push_back(*origin+helper);
res.push_back(*origin-helper);
}
}
return res;
}
LinePoint Line::getLinePoint(const Vector &point) const{
ASSERT(isContained(point),"Line point queried for point not on line");
Vector helper = point - (*origin);
double param = helper.ScalarProduct(*direction);
return LinePoint(*this,param);
}
LinePoint Line::posEndpoint() const{
return LinePoint(*this, numeric_limits::infinity());
}
LinePoint Line::negEndpoint() const{
return LinePoint(*this,-numeric_limits::infinity());
}
bool operator==(const Line &x,const Line &y){
return *x.origin == *y.origin && *x.direction == *y.direction;
}
Line makeLineThrough(const Vector &x1, const Vector &x2){
if(x1==x2){
throw LinearDependenceException() << LinearAlgebraVectorPair( make_pair(&x1, &x2) );
}
return Line(x1,x1-x2);
}
std::ostream& operator<<(std::ostream& ost, const Line& m)
{
const Vector origin = m.getOrigin();
const Vector direction = m.getDirection();
ost << "(";
for (int i=0;i (";
for (int i=0;i