/* * 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