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