/* $Id$ * * Copyright (c) 2010 Anders Wallin (anders.e.e.wallin "at" gmail.com). * * This file is part of OpenCAMlib * (see https://github.com/aewallin/opencamlib). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This program 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include // sqrt, sin, cos, fabs #include #include #include "point.hpp" #include "triangle.hpp" #include "numeric.hpp" namespace ocl { Point::Point() { x=0; y=0; z=0; } Point::Point(double xin, double yin, double zin) { x=xin; y=yin; z=zin; } Point::Point(double xin, double yin) { x=xin; y=yin; z=0.0; } Point::Point(const Point &p) { x=p.x; y=p.y; z=p.z; } //******** methods ********************** */ double Point::norm() const { return sqrt( square(x) + square(y) + square(z) ); } Point Point::cross(const Point &p) const { double xc = y * p.z - z * p.y; double yc = z * p.x - x * p.z; double zc = x * p.y - y * p.x; return Point(xc, yc, zc); } double Point::dot(const Point &p) const { return x * p.x + y * p.y + z * p.z; } void Point::normalize() { double norm = this->norm(); if (norm != 0.0) *this *=(1/norm); } double Point::xyNorm() const { return sqrt( square(x) + square(y) ); } void Point::xyNormalize() { if (this->xyNorm() != 0.0) *this *=(1/this->xyNorm()); } Point Point::xyPerp() const { return Point(-y, x, z); } void Point::xyRotate(double cosa, double sina) { // rotate vector by angle double temp = -y * sina + x * cosa; y = x * sina + cosa * y; x = temp; } void Point::xyRotate(double angle) { xyRotate(cos(angle), sin(angle)); } void Point::xRotate(double theta) { matrixRotate(1, 0 , 0 , 0, cos(theta), -sin(theta), 0, sin(theta), cos(theta) ); } void Point::yRotate(double theta) { matrixRotate(cos(theta) , 0 , sin(theta) , 0 , 1 , 0 , -sin(theta), 0 , cos(theta) ); } void Point::zRotate(double theta) { matrixRotate(cos(theta), -sin(theta) , 0 , sin(theta), cos(theta), 0, 0, 0, 1 ); } // http://en.wikipedia.org/wiki/Rotation_matrix void Point::matrixRotate(double a,double b, double c, double d,double e, double f, double g,double h, double i) { // multiply point with matrix double xr = a*x + b*y + c*z; double yr = d*x + e*y + f*z; double zr = g*x + h*y + i*z; x=xr; y=yr; z=zr; } double Point::xyDistance(const Point &p) const { return (*this - p).xyNorm(); //return sqrt(pow(x - p.x, 2) + pow((y - p.y), 2)); } double Point::xyDistanceToLine(const Point &p1, const Point &p2) const { // see for example // http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html if ((p1.x == p2.x) && (p1.y == p2.y)) {// no line in xy plane std::cout << "point.cpp: xyDistanceToLine ERROR!: can't calculate distance from \n"; std::cout << "point.cpp: xyDistanceToLine ERROR!: *this ="<<*this <<" to line through\n"; std::cout << "point.cpp: xyDistanceToLine ERROR!: p1="< 0.0 ); double u = (*this - p1).dot(v) / v.dot(v); // u = (p3-p1) dot v / (v dot v) return p1 + u*v; } /// return Point on p1-p2 line which is closest in XY-plane to this Point Point::xyClosestPoint(const Point &p1, const Point &p2) const { // one explanation is here // http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/ Point pt1 = p1; // this required because of "const" arguments above. Point pt2 = p2; Point v = pt2 - pt1; if ( isZero_tol( v.xyNorm() ) ) { // if p1 and p2 do not make a line in the xy-plane std::cout << "point.cpp: xyClosestPoint ERROR!: can't calculate closest point from \n"; std::cout << "point.cpp: xyClosestPoint ERROR!: *this ="<<*this <<" to line through\n"; std::cout << "point.cpp: xyClosestPoint ERROR!: p1="<x - p1.x) * (v.x) + (this->y - p1.y)*(v.y); u = u/ (v.x*v.x + v.y*v.y); // coordinates for closest point double x = p1.x + u*v.x; double y = p1.y + u*v.y; return Point(x,y,0); } bool Point::isRight(const Point &p1, const Point &p2) const { // is Point right of line through points p1 and p2 ?, in the XY plane. // this is an ugly way of doing a determinant // should be prettyfied sometime... /// \todo FIXME: what if p1==p2 ? (in the XY plane) double a1 = p2.x - p1.x; double a2 = p2.y - p1.y; double t1 = a2; double t2 = -a1; double b1 = x - p1.x; double b2 = y - p1.y; double t = t1 * b1 + t2 * b2; if (t > 0.00000000000001) /// \todo FIXME: hardcoded magic number... return true; else return false; } bool Point::isInside(const Triangle &t) const { Point p = *this; Point a = t.p[0]; Point b = t.p[1]; Point c = t.p[2]; // Compute barycentric coordinates double u = (a.y * c.x - a.x * c.y + (c.y - a.y) * p.x + (a.x - c.x) * p.y) / (a.y * c.x - a.x * c.y + (c.y - a.y) * b.x + (a.x - c.x) * b.y); double v = (a.x * b.y - a.y * b.x + (a.y - b.y) * p.x + (b.x - a.x) * p.y) / (a.x * b.y - a.y * b.x + (a.y - b.y) * c.x + (b.x - a.x) * c.y); // Check if point is inside triangle return u > 0.0 && v > 0.0 && (u + v) < 1.0; } bool Point::isInside(const Point& p1, const Point& p2) const { // segment is p1 + t*(p2-p1) // p1 + t*(p2-p1) = p // p1*(p2-p1) + t * (p2-p1)*(p2-p1) = p*(p2-p1) // t = (p - p1 )*(p2-p1) / (p2-p1)*(p2-p1) Point p2minusp1 = p2 - p1; Point thisminusp1 = (*this - p1); double t = thisminusp1.dot(p2minusp1) / p2minusp1.dot(p2minusp1); if (t > 1.0 || t < 0.0) { return false; } return true; } /* **************** Operators *************** * see * http://www.cs.caltech.edu/courses/cs11/material/cpp/donnie/cpp-ops.html */ Point& Point::operator=(const Point &p) { if (this == &p) return *this; x=p.x; y=p.y; z=p.z; return *this; } // Point*scalar multiplication Point& Point::operator*=(const double &a) { x*=a; y*=a; z*=a; return *this; } Point& Point::operator+=(const Point &p) { x+=p.x; y+=p.y; z+=p.z; return *this; } Point& Point::operator-=(const Point &p) { x-=p.x; y-=p.y; z-=p.z; return *this; } const Point Point::operator+(const Point &p) const { return Point(*this) += p; } const Point Point::operator-(const Point &p) const { return Point(*this) -= p; } const Point Point::operator*(const double &a) const { return Point(*this) *= a; } // scalar*Point const Point operator*(const double &a, const Point &p) { return Point(p) *= a; } bool Point::operator==(const Point &p) const { return (this == &p) || (x==p.x && y==p.y && z==p.z); } bool Point::operator!=(const Point &p) const { return !(*this == p); } bool Point::zParallel() const { if (x != 0.0) return false; else if (y != 0.0) return false; else return true; } bool Point::xParallel() const { if ( isZero_tol( y ) && isZero_tol( z ) ) return true; return false; } bool Point::yParallel() const { if ( isZero_tol( x ) && isZero_tol( z ) ) return true; return false; } void Point::z_projectOntoEdge(const Point& p1, const Point& p2) { // edge is p1+t*(p2-p1) // now locate z-coord of *this on edge double t; if ( fabs(p2.x-p1.x) > fabs(p2.y-p1.y) ) { t = (this->x - p1.x) / (p2.x-p1.x); } else { t = (this->y - p1.y) / (p2.y-p1.y); } this->z = p1.z + t*(p2.z-p1.z); } std::string Point::str() const { std::ostringstream o; o << *this; return o.str(); } std::ostream& operator<<(std::ostream &stream, const Point& p) { stream << "(" << p.x << ", " << p.y << ", " << p.z << ")"; return stream; } } // end namespace // end file point.cpp