source: tags/arb_5.3/WINDOW/aw_position.hxx

Last change on this file was 5675, checked in by westram, 15 years ago
  • removed automatic timestamps (the best they were good for, were vc-conflicts)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.5 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : aw_position.hxx                                   //
4//   Purpose   : Positions, Vectors and Angles                     //
5//                                                                 //
6//   Coded by Ralf Westram (coder@reallysoft.de) in July 2007      //
7//   Institute of Microbiology (Technical University Munich)       //
8//   http://www.arb-home.de/                                       //
9//                                                                 //
10// =============================================================== //
11
12#ifndef AW_POSITION_HXX
13#define AW_POSITION_HXX
14
15#ifndef _CPP_CMATH
16#include <cmath>
17#endif
18
19#ifndef AW_ROOT_HXX
20#include <aw_root.hxx>
21#endif
22
23#ifndef aw_assert
24#ifndef ARB_ASSERT_H
25#include <arb_assert.h>
26#endif
27#define aw_assert(bed) arb_assert(bed)
28#endif
29
30// ------------------------
31//      validity checks
32// ------------------------
33
34#if defined(DEBUG)
35#define ISVALID(a) aw_assert((a).valid())
36#else   
37#define ISVALID(a)
38#endif // DEBUG
39
40namespace AW {
41
42    const double EPSILON = 0.001; // how equal is nearly equal
43
44    inline bool nearlyEqual(const double& val1, const double& val2) {
45        return std::abs(val1-val2) < EPSILON;
46    }
47
48
49    // -------------------------------------------------------
50    //      class Position represents 2-dimensional positions
51    // -------------------------------------------------------
52
53    // Note: orientation of drawn canvases is like shown in this figure:
54    //
55    //        __________________\ +x
56    //       |                  /                       .
57    //       |
58    //       |
59    //       |
60    //       |
61    //       |
62    //       |
63    //      \|/
64    //    +y
65    //
66    // i.e. rotating an angle by 90 degrees, means rotating it 3 hours in clockwise direction
67
68    class Vector;
69
70    class Position {
71        double x, y;
72
73        static bool is_between(const double& coord1, const double& between, const double& coord2) {
74            return ((coord1-between)*(between-coord2)) >= 0.0;
75        }
76
77    public:
78
79        bool valid() const { return (x == x) && (y == y); } // fails if one is NAN
80
81        Position(double X, double Y) : x(X), y(Y) { ISVALID(*this); }
82        Position() : x(NAN), y(NAN) {} // default is no position
83        ~Position() {}
84
85        inline Position& operator += (const Vector& v);
86        inline Position& operator -= (const Vector& v);
87
88        const double& xpos() const { return x; }
89        const double& ypos() const { return y; }
90
91        // void set(const double& X, const double& Y) { x = X; y = Y; }
92        void setx(const double& X) { x = X; }
93        void sety(const double& Y) { y = Y; }
94
95        void movex(const double& X) { x += X; }
96        void movey(const double& Y) { y += Y; }
97
98        void move(const Vector& movement) { *this += movement; }
99        void moveTo(const Position& pos) { *this = pos; }
100       
101        inline bool is_between(const Position& p1, const Position& p2) const {
102            return is_between(p1.x, x, p2.x) && is_between(p1.y, y, p2.y);
103        }
104    };
105
106    extern const Position Origin;
107   
108    // -------------------------------
109    //      a 2D vector
110    // -------------------------------
111
112    class Vector {
113        Position       end; // endpoint of vector (vector starts at Position::origin)
114        // double         x_, y_;
115        mutable double len;     // once calculated, length of vector is stored here (negative value means "not calculated")
116
117    public:
118        bool valid() const { return end.valid() && (len == len); } // len == len fails if len is NAN
119
120        Vector()                                         : len(NAN) {} // default is not a vector
121        Vector(const double& X, const double& Y)         : end(X, Y), len(-1) { ISVALID(*this); } // vector (0,0)->(X,Y)
122        Vector(const double& X, const double& Y, const double& Length) : end(X, Y), len(Length) { ISVALID(*this); } // same with known length
123        explicit Vector(const Position& to)              : end(to), len(-1) { ISVALID(*this); } // vector origin->to
124        Vector(const Position& from, const Position& to) : end(to.xpos()-from.xpos(), to.ypos()-from.ypos()), len(-1) { ISVALID(*this); } // vector from->to
125        ~Vector() {}
126
127        const double& x() const { return end.xpos(); }
128        const double& y() const { return end.ypos(); }
129        const Position& endpoint() { return end; }
130
131        Vector& set(const double& X, const double& Y, double Length = -1) { end = Position(X, Y); len = Length; return *this; }
132        Vector& setx(const double& X) { end.setx(X); len = -1; return *this; }
133        Vector& sety(const double& Y) { end.sety(Y); len = -1; return *this; }
134
135        const double& length() const {
136            if (len<0.0) len = sqrt(x()*x() + y()*y());
137            return len;
138        }
139
140        // length-modifying members:
141
142        Vector& operator *= (const double& factor)  { return set(x()*factor, y()*factor, len*std::abs(factor)); }
143        Vector& operator /= (const double& divisor) { return operator *= (1.0/divisor); }
144
145        Vector& operator += (const Vector& other)  { return set(x()+other.x(), y()+other.y()); }
146        Vector& operator -= (const Vector& other)  { return set(x()-other.x(), y()-other.y()); }
147
148        Vector& normalize() {
149            aw_assert(length()>0); // cannot normalize zero-Vector!
150            return *this /= length();
151        }
152        // bool is_normalized() const { return std::abs(length()-1.0) < EPSILON; }
153        bool is_normalized() const { return nearlyEqual(length(), 1); }
154
155        Vector& set_length(double new_length) {
156            double factor  = new_length/length();
157            return *this  *= factor; 
158        }
159
160        // length-constant members:
161
162        Vector& neg()  { end = Position(-x(), -y()); return *this; }
163        Vector& negx() { end.setx(-x()); return *this; }
164        Vector& negy() { end.sety(-y()); return *this; }
165
166        Vector& flipxy() { end = Position(y(), x()); return *this; }
167
168        Vector& rotate90deg()  { return negy().flipxy(); }
169        Vector& rotate180deg() { return neg(); }
170        Vector& rotate270deg() { return negx().flipxy(); }
171
172        Vector& rotate45deg();
173        Vector& rotate135deg() { return rotate45deg().rotate90deg(); }
174        Vector& rotate225deg() { return rotate45deg().rotate180deg(); }
175        Vector& rotate315deg() { return rotate45deg().rotate270deg(); }
176
177        Vector operator-() const { return Vector(-x(), -y(), len); } // unary minus
178    };
179
180    extern const Vector ZeroVector;
181
182    // -----------------------------------------
183    //      inline Position members
184    // -----------------------------------------
185
186    inline Position& Position::operator += (const Vector& v) { x += v.x(); y += v.y(); ISVALID(*this); return *this; }
187    inline Position& Position::operator -= (const Vector& v) { x -= v.x(); y -= v.y(); ISVALID(*this); return *this; }
188
189    // ------------------------------------------
190    //      basic Position / Vector functions
191    // ------------------------------------------
192
193    // Difference between Positions
194    inline Vector operator-(const Position& to, const Position& from) { return Vector(from, to); }
195
196    // Position +- Vector -> new Position
197    inline Position operator+(const Position& p, const Vector& v) { return Position(p) += v; }
198    inline Position operator+(const Vector& v, const Position& p) { return Position(p) += v; }
199    inline Position operator-(const Position& p, const Vector& v) { return Position(p) -= v; }
200
201    // Vector addition
202    inline Vector operator+(const Vector& v1, const Vector& v2) { return Vector(v1) += v2; }
203    inline Vector operator-(const Vector& v1, const Vector& v2) { return Vector(v1) -= v2; }
204
205    // stretch/shrink Vector
206    inline Vector operator*(const Vector& v, const double& f) { return Vector(v) *= f; }
207    inline Vector operator*(const double& f, const Vector& v) { return Vector(v) *= f; }
208    inline Vector operator/(const Vector& v, const double& d) { return Vector(v) /= d; }
209
210    inline Position centroid(const Position& p1, const Position& p2) { return Position((p1.xpos()+p2.xpos())*0.5, (p1.ypos()+p2.ypos())*0.5); }
211
212    inline double Distance(const Position& from, const Position& to) { return Vector(from, to).length(); }
213    inline double scalarProduct(const Vector& v1, const Vector& v2) { return v1.x()*v2.x() + v1.y()*v2.y(); }
214
215    // -------------------------------------------------
216    //      a positioned vector, representing a line
217    // -------------------------------------------------
218
219    class LineVector {
220        Position Start;         // start point
221        Vector   ToEnd;         // vector to end point
222       
223    protected:
224        void standardize();
225
226    public:
227        bool valid() const { return Start.valid() && ToEnd.valid(); }
228       
229        LineVector(const Position& startpos, const Position& end) : Start(startpos), ToEnd(startpos, end) { ISVALID(*this); }
230        LineVector(const Position& startpos, const Vector& to_end) : Start(startpos), ToEnd(to_end) { ISVALID(*this); }
231        LineVector(double X1, double Y1, double X2, double Y2) : Start(X1, Y1), ToEnd(X2-X1, Y2-Y1) { ISVALID(*this); }
232        LineVector(const AW_rectangle& r) : Start(r.l, r.t), ToEnd(r.r-r.l-1, r.b-r.t-1) { ISVALID(*this); }
233        LineVector() {}
234
235        const Vector& line_vector() const { return ToEnd; }
236        const Position& start() const { return Start; }
237        Position head() const { return Start+ToEnd; }
238
239        Position centroid() const { return Start+ToEnd*0.5; }
240        double length() const { return line_vector().length(); }
241
242        const double& xpos() const { return Start.xpos(); }
243        const double& ypos() const { return Start.ypos(); }
244
245        void move(const Vector& movement) { Start += movement; }
246        void moveTo(const Position& pos) { Start = pos; }
247    };
248
249    Position crosspoint(const LineVector& l1, const LineVector& l2, double& factor_l1, double& factor_l2);
250    double Distance(const Position pos, const LineVector line);
251
252    // ---------------------
253    //      a rectangle
254    // ---------------------
255
256    class Rectangle : public LineVector { // the LineVector describes one corner and the diagonal
257    public:
258        explicit Rectangle(const LineVector& Diagonal) : LineVector(Diagonal) { standardize(); }
259        Rectangle(const Position& corner, const Position& opposite_corner) : LineVector(corner, opposite_corner) { standardize(); }
260        Rectangle(const Position& corner, const Vector& to_opposite_corner) : LineVector(corner, to_opposite_corner) { standardize(); }
261        Rectangle(double X1, double Y1, double X2, double Y2) : LineVector(X1, Y1, X2, Y2) { standardize(); }
262        Rectangle(const AW_rectangle& r) : LineVector(r) { standardize(); }
263        Rectangle() {};
264
265        const Vector& diagonal() const { return line_vector(); }
266
267        const Position& upper_left_corner() const { return start(); }
268        Position lower_left_corner()        const { return Position(start().xpos(),                   start().ypos()+line_vector().y()); }
269        Position upper_right_corner()       const { return Position(start().xpos()+line_vector().x(), start().ypos()); }
270        Position lower_right_corner()       const { return head(); }
271
272        double width() const { return diagonal().x(); }
273        double height() const { return diagonal().y(); }
274
275        void standardize() { LineVector::standardize(); }
276       
277        bool contains(const Position& pos) const { return pos.is_between(upper_left_corner(), lower_right_corner()); }
278        bool contains(const LineVector& lvec) const { return contains(lvec.start()) && contains(lvec.head()); }
279    };
280
281    // ------------------------------------------------------------------
282    //      class angle represents an angle using a normalized vector
283    // ------------------------------------------------------------------
284
285    class Angle {
286        mutable Vector Normal;  // the normal vector representing the angle (x = cos(angle), y = sin(angle))
287        mutable double Radian;  // the radian of the angle
288
289        void recalcRadian() const;
290        void recalcNormal() const;
291
292    public:
293        bool valid() const { return Normal.valid() && (Radian == Radian); } // Radian == Radian fails if Radian is NAN
294
295        static const double rad2deg;
296        static const double deg2rad;
297
298        Angle(double Radian_) : Radian(Radian_) { recalcNormal(); ISVALID(*this); }
299        Angle(double x, double y) : Normal(x, y) { Normal.normalize(); recalcRadian(); ISVALID(*this); }
300        explicit Angle(const Vector& v) : Normal(v) { Normal.normalize(); recalcRadian(); ISVALID(*this); }
301        Angle(const Vector& n, double r) : Normal(n), Radian(r) { aw_assert(n.is_normalized()); ISVALID(*this); }
302        Angle(const Position& p1, const Position& p2) : Normal(p1, p2) { Normal.normalize(); recalcRadian(); ISVALID(*this); }
303        Angle() : Radian(NAN) { } // default is not an angle
304
305        Angle& operator = (const Angle& other) { Normal = other.Normal; Radian = other.Radian; return *this; }
306        Angle& operator = (const Vector& vec) { Normal = vec; Normal.normalize(); recalcRadian(); return *this; }
307
308        void fixRadian() const { // force radian into range [0, 2*M_PI[
309            while (Radian<0.0) Radian       += 2*M_PI;
310            while (Radian >= 2*M_PI) Radian -= 2*M_PI;
311        }
312
313        const double& radian() const { fixRadian(); return Radian; }
314        double degrees() const { fixRadian(); return rad2deg*Radian; }
315        const Vector& normal() const { return Normal; }
316
317        const double& sin() const { return Normal.y(); }
318        const double& cos() const { return Normal.x(); }
319
320        Angle& operator += (const Angle& o) {
321            Radian += o.Radian;
322           
323            double norm = normal().length()*o.normal().length();
324            if (nearlyEqual(norm, 1)) {  // fast method
325                Vector newNormal(cos()*o.cos() - sin()*o.sin(),
326                                 sin()*o.cos() + cos()*o.sin());
327                aw_assert(newNormal.is_normalized());
328                Normal = newNormal;
329            }
330            else {
331                recalcNormal();
332            }
333            return *this;
334        }
335        Angle& operator -= (const Angle& o) {
336            Radian -= o.Radian;
337           
338            double norm = normal().length()*o.normal().length();
339            if (nearlyEqual(norm, 1)) { // fast method
340                Vector newNormal(cos()*o.cos() + sin()*o.sin(),
341                                 sin()*o.cos() - cos()*o.sin());
342                aw_assert(newNormal.is_normalized());
343
344                Normal = newNormal;
345            }
346            else {
347                recalcNormal();
348            }
349            return *this;
350        }
351
352        Angle& operator *= (const double& fact) {
353            fixRadian();
354            Radian *= fact;
355            recalcNormal();
356            return *this;
357        }
358
359        Angle& rotate90deg()   { Normal.rotate90deg();  Radian += 0.5*M_PI; return *this; }
360        Angle& rotate180deg()  { Normal.rotate180deg(); Radian +=     M_PI; return *this; }
361        Angle& rotate270deg()  { Normal.rotate270deg(); Radian += 1.5*M_PI; return *this; }
362       
363        Angle operator-() const { return Angle(Vector(Normal).negy(), 2*M_PI-Radian); } // unary minus
364    };
365
366    inline Angle operator+(const Angle& a1, const Angle& a2) { return Angle(a1) += a2; }
367    inline Angle operator-(const Angle& a1, const Angle& a2) { return Angle(a1) -= a2; }
368
369    inline Angle operator*(const Angle& a, const double& fact) { return Angle(a) *= fact; }
370    inline Angle operator/(const Angle& a, const double& divi) { return Angle(a) *= (1.0/divi); }
371
372    // ---------------------
373    //      some helpers
374    // ---------------------
375
376    // pythagoras:
377
378    inline double hypotenuse(double cath1, double cath2) { return sqrt(cath1*cath1 + cath2*cath2); }
379    inline double cathetus(double hypotenuse, double cathetus) {
380        aw_assert(hypotenuse>cathetus);
381        return sqrt(hypotenuse*hypotenuse - cathetus*cathetus);
382    }
383
384#if defined(DEBUG)
385    // dont use these in release code - they are only approximizations!
386   
387    // test whether two doubles are "equal" (slow - use for assertions only!)
388    inline bool are_equal(const double& d1, const double& d2) {
389        double diff = std::abs(d1-d2);
390        return diff < 0.000001;
391    }
392
393    inline bool are_orthographic(const Vector& v1, const Vector& v2) {
394        return are_equal(scalarProduct(v1, v2), 0);
395    }
396#endif // DEBUG
397
398    inline bool isOrigin(const Position& p) {
399        return p.xpos() == 0 && p.ypos() == 0;
400    }
401
402   
403};
404
405#else
406#error aw_position.hxx included twice
407#endif // AW_POSITION_HXX
Note: See TracBrowser for help on using the repository browser.