1 // 2 // Copyright (C) 2003-2021 Greg Landrum and other RDKit contributors 3 // 4 // @@ All Rights Reserved @@ 5 // This file is part of the RDKit. 6 // The contents are covered by the terms of the BSD license 7 // which is included in the file license.txt, found at the root 8 // of the RDKit source tree. 9 // 10 11 #include <RDGeneral/export.h> 12 #ifndef __RD_POINT_H__ 13 #define __RD_POINT_H__ 14 #include <iostream> 15 #include <cmath> 16 #include <vector> 17 #include <map> 18 19 #ifndef M_PI 20 #define M_PI 3.14159265358979323846 21 #endif 22 23 #include <RDGeneral/Invariant.h> 24 #include <Numerics/Vector.h> 25 #include <boost/smart_ptr.hpp> 26 27 namespace RDGeom { 28 29 class RDKIT_RDGEOMETRYLIB_EXPORT Point { 30 // this is the virtual base class, mandating certain functions 31 public: ~Point()32 virtual ~Point(){}; 33 34 virtual double operator[](unsigned int i) const = 0; 35 virtual double &operator[](unsigned int i) = 0; 36 37 virtual void normalize() = 0; 38 virtual double length() const = 0; 39 virtual double lengthSq() const = 0; 40 virtual unsigned int dimension() const = 0; 41 42 virtual Point *copy() const = 0; 43 }; 44 #ifndef _MSC_VER 45 // g++ (at least as of v9.3.0) generates some spurious warnings from here. 46 // disable them 47 #if !defined(__clang__) and defined(__GNUC__) 48 #pragma GCC diagnostic push 49 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 50 #endif 51 #endif 52 53 // typedef class Point3D Point; 54 class RDKIT_RDGEOMETRYLIB_EXPORT Point3D : public Point { 55 public: 56 double x{0.0}; 57 double y{0.0}; 58 double z{0.0}; 59 Point3D()60 Point3D(){}; Point3D(double xv,double yv,double zv)61 Point3D(double xv, double yv, double zv) : x(xv), y(yv), z(zv){}; 62 ~Point3D()63 ~Point3D(){}; 64 Point3D(const Point3D & other)65 Point3D(const Point3D &other) 66 : Point(other), x(other.x), y(other.y), z(other.z) {} 67 copy()68 virtual Point *copy() const { return new Point3D(*this); } 69 dimension()70 inline unsigned int dimension() const { return 3; } 71 72 inline double operator[](unsigned int i) const { 73 PRECONDITION(i < 3, "Invalid index on Point3D"); 74 if (i == 0) { 75 return x; 76 } else if (i == 1) { 77 return y; 78 } else { 79 return z; 80 } 81 } 82 83 inline double &operator[](unsigned int i) { 84 PRECONDITION(i < 3, "Invalid index on Point3D"); 85 if (i == 0) { 86 return x; 87 } else if (i == 1) { 88 return y; 89 } else { 90 return z; 91 } 92 } 93 94 Point3D &operator=(const Point3D &other) { 95 if (&other == this) { 96 return *this; 97 } 98 x = other.x; 99 y = other.y; 100 z = other.z; 101 return *this; 102 }; 103 104 Point3D &operator+=(const Point3D &other) { 105 x += other.x; 106 y += other.y; 107 z += other.z; 108 return *this; 109 }; 110 111 Point3D &operator-=(const Point3D &other) { 112 x -= other.x; 113 y -= other.y; 114 z -= other.z; 115 return *this; 116 }; 117 118 Point3D &operator*=(double scale) { 119 x *= scale; 120 y *= scale; 121 z *= scale; 122 return *this; 123 }; 124 125 Point3D &operator/=(double scale) { 126 x /= scale; 127 y /= scale; 128 z /= scale; 129 return *this; 130 }; 131 132 Point3D operator-() const { 133 Point3D res(x, y, z); 134 res.x *= -1.0; 135 res.y *= -1.0; 136 res.z *= -1.0; 137 return res; 138 } 139 normalize()140 void normalize() { 141 double l = this->length(); 142 x /= l; 143 y /= l; 144 z /= l; 145 }; 146 length()147 double length() const { 148 double res = x * x + y * y + z * z; 149 return sqrt(res); 150 }; 151 lengthSq()152 double lengthSq() const { 153 // double res = pow(x,2) + pow(y,2) + pow(z,2); 154 double res = x * x + y * y + z * z; 155 return res; 156 }; 157 dotProduct(const Point3D & other)158 double dotProduct(const Point3D &other) const { 159 double res = x * (other.x) + y * (other.y) + z * (other.z); 160 return res; 161 }; 162 163 /*! \brief determines the angle between a vector to this point 164 * from the origin and a vector to the other point. 165 * 166 * The angle is unsigned: the results of this call will always 167 * be between 0 and M_PI 168 */ angleTo(const Point3D & other)169 double angleTo(const Point3D &other) const { 170 double lsq = lengthSq() * other.lengthSq(); 171 double dotProd = dotProduct(other); 172 dotProd /= sqrt(lsq); 173 174 // watch for roundoff error: 175 if (dotProd <= -1.0) { 176 return M_PI; 177 } 178 if (dotProd >= 1.0) { 179 return 0.0; 180 } 181 182 return acos(dotProd); 183 } 184 185 /*! \brief determines the signed angle between a vector to this point 186 * from the origin and a vector to the other point. 187 * 188 * The results of this call will be between 0 and M_2_PI 189 */ signedAngleTo(const Point3D & other)190 double signedAngleTo(const Point3D &other) const { 191 double res = this->angleTo(other); 192 // check the sign of the z component of the cross product: 193 if ((this->x * other.y - this->y * other.x) < -1e-6) res = 2.0 * M_PI - res; 194 return res; 195 } 196 197 /*! \brief Returns a normalized direction vector from this 198 * point to another. 199 * 200 */ directionVector(const Point3D & other)201 Point3D directionVector(const Point3D &other) const { 202 Point3D res; 203 res.x = other.x - x; 204 res.y = other.y - y; 205 res.z = other.z - z; 206 res.normalize(); 207 return res; 208 } 209 210 /*! \brief Cross product of this point with the another point 211 * 212 * The order is important here 213 * The result is "this" cross with "other" not (other x this) 214 */ crossProduct(const Point3D & other)215 Point3D crossProduct(const Point3D &other) const { 216 Point3D res; 217 res.x = y * (other.z) - z * (other.y); 218 res.y = -x * (other.z) + z * (other.x); 219 res.z = x * (other.y) - y * (other.x); 220 return res; 221 }; 222 223 /*! \brief Get a unit perpendicular from this point (treating it as a vector): 224 * 225 */ getPerpendicular()226 Point3D getPerpendicular() const { 227 Point3D res(0.0, 0.0, 0.0); 228 if (x) { 229 if (y) { 230 res.y = -1 * x; 231 res.x = y; 232 } else if (z) { 233 res.z = -1 * x; 234 res.x = z; 235 } else { 236 res.y = 1; 237 } 238 } else if (y) { 239 if (z) { 240 res.z = -1 * y; 241 res.y = z; 242 } else { 243 res.x = 1; 244 } 245 } else if (z) { 246 res.x = 1; 247 } 248 double l = res.length(); 249 POSTCONDITION(l > 0.0, "zero perpendicular"); 250 res /= l; 251 return res; 252 } 253 }; 254 255 // given a set of four pts in 3D compute the dihedral angle between the 256 // plane of the first three points (pt1, pt2, pt3) and the plane of the 257 // last three points (pt2, pt3, pt4) 258 // the computed angle is between 0 and PI 259 RDKIT_RDGEOMETRYLIB_EXPORT double computeDihedralAngle(const Point3D &pt1, 260 const Point3D &pt2, 261 const Point3D &pt3, 262 const Point3D &pt4); 263 264 // given a set of four pts in 3D compute the signed dihedral angle between the 265 // plane of the first three points (pt1, pt2, pt3) and the plane of the 266 // last three points (pt2, pt3, pt4) 267 // the computed angle is between -PI and PI 268 RDKIT_RDGEOMETRYLIB_EXPORT double computeSignedDihedralAngle( 269 const Point3D &pt1, const Point3D &pt2, const Point3D &pt3, 270 const Point3D &pt4); 271 272 class RDKIT_RDGEOMETRYLIB_EXPORT Point2D : public Point { 273 public: 274 double x{0.0}; 275 double y{0.0}; 276 Point2D()277 Point2D(){}; Point2D(double xv,double yv)278 Point2D(double xv, double yv) : x(xv), y(yv){}; ~Point2D()279 ~Point2D(){}; 280 Point2D(const Point2D & other)281 Point2D(const Point2D &other) : Point(other), x(other.x), y(other.y) {} 282 //! construct from a Point3D (ignoring the z coordinate) Point2D(const Point3D & p3d)283 Point2D(const Point3D &p3d) : Point(p3d), x(p3d.x), y(p3d.y){}; 284 copy()285 virtual Point *copy() const { return new Point2D(*this); } 286 dimension()287 inline unsigned int dimension() const { return 2; } 288 289 inline double operator[](unsigned int i) const { 290 PRECONDITION(i < 2, "Invalid index on Point2D"); 291 if (i == 0) { 292 return x; 293 } else { 294 return y; 295 } 296 } 297 298 inline double &operator[](unsigned int i) { 299 PRECONDITION(i < 2, "Invalid index on Point2D"); 300 if (i == 0) { 301 return x; 302 } else { 303 return y; 304 } 305 } 306 307 Point2D &operator=(const Point2D &other) { 308 x = other.x; 309 y = other.y; 310 return *this; 311 }; 312 313 Point2D &operator+=(const Point2D &other) { 314 x += other.x; 315 y += other.y; 316 return *this; 317 }; 318 319 Point2D &operator-=(const Point2D &other) { 320 x -= other.x; 321 y -= other.y; 322 return *this; 323 }; 324 325 Point2D &operator*=(double scale) { 326 x *= scale; 327 y *= scale; 328 return *this; 329 }; 330 331 Point2D &operator/=(double scale) { 332 x /= scale; 333 y /= scale; 334 return *this; 335 }; 336 337 Point2D operator-() const { 338 Point2D res(x, y); 339 res.x *= -1.0; 340 res.y *= -1.0; 341 return res; 342 } 343 normalize()344 void normalize() { 345 double ln = this->length(); 346 x /= ln; 347 y /= ln; 348 }; 349 rotate90()350 void rotate90() { 351 double temp = x; 352 x = -y; 353 y = temp; 354 } 355 length()356 double length() const { 357 // double res = pow(x,2) + pow(y,2); 358 double res = x * x + y * y; 359 return sqrt(res); 360 }; 361 lengthSq()362 double lengthSq() const { 363 double res = x * x + y * y; 364 return res; 365 }; 366 dotProduct(const Point2D & other)367 double dotProduct(const Point2D &other) const { 368 double res = x * (other.x) + y * (other.y); 369 return res; 370 }; 371 angleTo(const Point2D & other)372 double angleTo(const Point2D &other) const { 373 Point2D t1, t2; 374 t1 = *this; 375 t2 = other; 376 t1.normalize(); 377 t2.normalize(); 378 double dotProd = t1.dotProduct(t2); 379 // watch for roundoff error: 380 if (dotProd < -1.0) 381 dotProd = -1.0; 382 else if (dotProd > 1.0) 383 dotProd = 1.0; 384 return acos(dotProd); 385 } 386 signedAngleTo(const Point2D & other)387 double signedAngleTo(const Point2D &other) const { 388 double res = this->angleTo(other); 389 if ((this->x * other.y - this->y * other.x) < -1e-6) res = 2.0 * M_PI - res; 390 return res; 391 } 392 directionVector(const Point2D & other)393 Point2D directionVector(const Point2D &other) const { 394 Point2D res; 395 res.x = other.x - x; 396 res.y = other.y - y; 397 res.normalize(); 398 return res; 399 } 400 }; 401 402 class RDKIT_RDGEOMETRYLIB_EXPORT PointND : public Point { 403 public: 404 typedef boost::shared_ptr<RDNumeric::Vector<double>> VECT_SH_PTR; 405 PointND(unsigned int dim)406 PointND(unsigned int dim) { 407 RDNumeric::Vector<double> *nvec = new RDNumeric::Vector<double>(dim, 0.0); 408 dp_storage.reset(nvec); 409 }; 410 PointND(const PointND & other)411 PointND(const PointND &other) : Point(other) { 412 RDNumeric::Vector<double> *nvec = 413 new RDNumeric::Vector<double>(*other.getStorage()); 414 dp_storage.reset(nvec); 415 } 416 copy()417 virtual Point *copy() const { return new PointND(*this); } 418 419 #if 0 420 template <typename T> 421 PointND(const T &vals){ 422 RDNumeric::Vector<double> *nvec = new RDNumeric::Vector<double>(vals.size(), 0.0); 423 dp_storage.reset(nvec); 424 unsigned int idx=0; 425 typename T::const_iterator it; 426 for(it=vals.begin(); 427 it!=vals.end(); 428 ++it){ 429 nvec->setVal(idx,*it); 430 ++idx; 431 }; 432 }; 433 #endif 434 ~PointND()435 ~PointND() {} 436 437 inline double operator[](unsigned int i) const { 438 return dp_storage.get()->getVal(i); 439 } 440 441 inline double &operator[](unsigned int i) { return (*dp_storage.get())[i]; } 442 normalize()443 inline void normalize() { dp_storage.get()->normalize(); } 444 length()445 inline double length() const { return dp_storage.get()->normL2(); } 446 lengthSq()447 inline double lengthSq() const { return dp_storage.get()->normL2Sq(); } 448 dimension()449 unsigned int dimension() const { return dp_storage.get()->size(); } 450 451 PointND &operator=(const PointND &other) { 452 if (this == &other) return *this; 453 454 RDNumeric::Vector<double> *nvec = 455 new RDNumeric::Vector<double>(*other.getStorage()); 456 dp_storage.reset(nvec); 457 return *this; 458 } 459 460 PointND &operator+=(const PointND &other) { 461 (*dp_storage.get()) += (*other.getStorage()); 462 return *this; 463 } 464 465 PointND &operator-=(const PointND &other) { 466 (*dp_storage.get()) -= (*other.getStorage()); 467 return *this; 468 } 469 470 PointND &operator*=(double scale) { 471 (*dp_storage.get()) *= scale; 472 return *this; 473 } 474 475 PointND &operator/=(double scale) { 476 (*dp_storage.get()) /= scale; 477 return *this; 478 } 479 directionVector(const PointND & other)480 PointND directionVector(const PointND &other) { 481 PRECONDITION(this->dimension() == other.dimension(), 482 "Point dimensions do not match"); 483 PointND np(other); 484 np -= (*this); 485 np.normalize(); 486 return np; 487 } 488 dotProduct(const PointND & other)489 double dotProduct(const PointND &other) const { 490 return dp_storage.get()->dotProduct(*other.getStorage()); 491 } 492 angleTo(const PointND & other)493 double angleTo(const PointND &other) const { 494 double dp = this->dotProduct(other); 495 double n1 = this->length(); 496 double n2 = other.length(); 497 if ((n1 > 1.e-8) && (n2 > 1.e-8)) { 498 dp /= (n1 * n2); 499 } 500 if (dp < -1.0) 501 dp = -1.0; 502 else if (dp > 1.0) 503 dp = 1.0; 504 return acos(dp); 505 } 506 507 private: 508 VECT_SH_PTR dp_storage; getStorage()509 inline const RDNumeric::Vector<double> *getStorage() const { 510 return dp_storage.get(); 511 } 512 }; 513 #ifndef _MSC_VER 514 #if !defined(__clang__) and defined(__GNUC__) 515 #pragma GCC diagnostic pop 516 #endif 517 #endif 518 519 typedef std::vector<RDGeom::Point *> PointPtrVect; 520 typedef PointPtrVect::iterator PointPtrVect_I; 521 typedef PointPtrVect::const_iterator PointPtrVect_CI; 522 523 typedef std::vector<RDGeom::Point3D *> Point3DPtrVect; 524 typedef std::vector<RDGeom::Point2D *> Point2DPtrVect; 525 typedef Point3DPtrVect::iterator Point3DPtrVect_I; 526 typedef Point3DPtrVect::const_iterator Point3DPtrVect_CI; 527 typedef Point2DPtrVect::iterator Point2DPtrVect_I; 528 typedef Point2DPtrVect::const_iterator Point2DPtrVect_CI; 529 530 typedef std::vector<const RDGeom::Point3D *> Point3DConstPtrVect; 531 typedef Point3DConstPtrVect::iterator Point3DConstPtrVect_I; 532 typedef Point3DConstPtrVect::const_iterator Point3DConstPtrVect_CI; 533 534 typedef std::vector<Point3D> POINT3D_VECT; 535 typedef std::vector<Point3D>::iterator POINT3D_VECT_I; 536 typedef std::vector<Point3D>::const_iterator POINT3D_VECT_CI; 537 538 typedef std::map<int, Point2D> INT_POINT2D_MAP; 539 typedef INT_POINT2D_MAP::iterator INT_POINT2D_MAP_I; 540 typedef INT_POINT2D_MAP::const_iterator INT_POINT2D_MAP_CI; 541 542 RDKIT_RDGEOMETRYLIB_EXPORT std::ostream &operator<<(std::ostream &target, 543 const RDGeom::Point &pt); 544 545 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::Point3D operator+(const RDGeom::Point3D &p1, 546 const RDGeom::Point3D &p2); 547 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::Point3D operator-(const RDGeom::Point3D &p1, 548 const RDGeom::Point3D &p2); 549 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::Point3D operator*(const RDGeom::Point3D &p1, 550 double v); 551 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::Point3D operator/(const RDGeom::Point3D &p1, 552 double v); 553 554 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::Point2D operator+(const RDGeom::Point2D &p1, 555 const RDGeom::Point2D &p2); 556 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::Point2D operator-(const RDGeom::Point2D &p1, 557 const RDGeom::Point2D &p2); 558 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::Point2D operator*(const RDGeom::Point2D &p1, 559 double v); 560 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::Point2D operator/(const RDGeom::Point2D &p1, 561 double v); 562 563 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::PointND operator+(const RDGeom::PointND &p1, 564 const RDGeom::PointND &p2); 565 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::PointND operator-(const RDGeom::PointND &p1, 566 const RDGeom::PointND &p2); 567 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::PointND operator*(const RDGeom::PointND &p1, 568 double v); 569 RDKIT_RDGEOMETRYLIB_EXPORT RDGeom::PointND operator/(const RDGeom::PointND &p1, 570 double v); 571 } // namespace RDGeom 572 573 #endif 574