first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / graphiq / library / geometric / warper.h
1 #ifndef RECTANGLE_WARPER_CLASS
2 #define RECTANGLE_WARPER_CLASS
3
4 /*****************************************************************************\
5 *                                                                             *
6 *  Name   : rectangle_warper                                                  *
7 *  Author : Chris Koeritz                                                     *
8 *                                                                             *
9 *******************************************************************************
10 * Copyright (c) 1992-$now By Author.  This program is free software; you can  *
11 * redistribute it and/or modify it under the terms of the GNU General Public  *
12 * License as published by the Free Software Foundation; either version 2 of   *
13 * the License or (at your option) any later version.  This is online at:      *
14 *     http://www.fsf.org/copyleft/gpl.html                                    *
15 * Please send any updates to: fred@gruntose.com                               *
16 \*****************************************************************************/
17
18 //! Warps points in one frame of reference to a different one.
19 /*!
20   This class encapsulates the notion of a rectangular region that is
21   referred to from two different points of view.  This relates two
22   two-dimensional frames of reference to each other.  Each frame of reference
23   is specified by two rectangles.  A point that is measured in one frame of
24   reference can be transformed into a point that is measured in the other,
25   and vice-versa.
26 */
27
28 #include "rectangle.h"
29
30 #include <basis/astring.h>
31
32 namespace geometric {
33
34 template <class numeric_type>
35 class rectangle_warper
36 {
37 public:
38   //! describes where a rectangle's origin is located on the rectangle.
39   /*! our model is to consider the first vertex point of the rectangle as its
40   origin and the second vertex point (diagonally opposite the first point) as
41   its extent.  since it may make sense for that first vertex point to be
42   located at any one of the vertices of the rectangle (as in windowing
43   coordinate system conversions), the enumeration below allows any one of the
44   rectangle's vertices to be chosen as its origin. */
45   enum origin_vertex { BOTTOM_LEFT, TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT };
46
47   rectangle_warper(const rectangle<numeric_type> &system_1,
48           const rectangle<numeric_type> &system_2,
49           origin_vertex system_1_origin = BOTTOM_LEFT,
50           origin_vertex system_2_origin = BOTTOM_LEFT);
51     //!< constructs a warper given the two reference systems.
52     /*!< constructs a warper where the first rectangular system is in
53     "system_1", the second system is in "system_2" and the respective origins
54     for these systems are in "system_1_origin" and "system_2_origin". */
55
56   ~rectangle_warper();
57
58   point<numeric_type> to_system_1(const point<numeric_type> &in_system_2) const;
59     //!< Converts from the second system into the first.
60     /*!< This returns a point that is measured in the first frame of reference
61     when given a point "in_system_2" that is measured in the second frame of
62     reference. */
63
64   point<numeric_type> to_system_2(const point<numeric_type> &in_system_1) const;
65     //!< Converts from the first system into the second.
66     /*!< This returns a point that is measured in the second frame of reference
67     when given a point "in_system_1" that is measured in the first frame of
68     reference. */
69
70   rectangle<numeric_type> to_system_1
71           (const rectangle<numeric_type> &in_system_2) const;
72     //!< flips a rectangle from the second system into the first.
73   rectangle<numeric_type> to_system_2
74           (const rectangle<numeric_type> &in_system_1) const;
75     //!< flips a rectangle from the first system into the second.
76
77   rectangle<numeric_type> system_1() const { return _system_1; }
78   rectangle<numeric_type> system_2() const { return _system_2; }
79   origin_vertex origin_1() const { return _vert_1; }
80   origin_vertex origin_2() const { return _vert_2; }
81
82   void system_1(const rectangle<numeric_type> &to_set,
83           origin_vertex origin_corner = BOTTOM_LEFT);
84   void system_2(const rectangle<numeric_type> &to_set,
85           origin_vertex origin_corner = BOTTOM_LEFT);
86
87   basis::astring text_form() const;
88     //!< Prints out the two systems held in the rectangle_warper.
89
90   basis::astring vertex_name(origin_vertex v) const;
91     //!< Prints out the name of the vertex location.
92
93   enum vertical_component { RW_BOTTOM, RW_TOP };
94   enum horizontal_component { RW_LEFT, RW_RIGHT };
95
96   void separate_vertical(origin_vertex v, vertical_component &to_set) const;
97   void separate_horizontal(origin_vertex v, horizontal_component &to_set) const;
98     //!< separates out a component of the placement of the vertex.
99
100 private:
101   rectangle<numeric_type> _system_1;
102   rectangle<numeric_type> _system_2;
103   origin_vertex _vert_1;
104   origin_vertex _vert_2;
105
106   point<numeric_type> scale_point(const rectangle<numeric_type> &source,
107           const rectangle<numeric_type> &target,
108           origin_vertex v1, origin_vertex v2,
109           const point<numeric_type> &old) const;
110   rectangle<numeric_type> scale_rectangle(const rectangle<numeric_type> &source,
111           const rectangle<numeric_type> &target,
112           origin_vertex v1, origin_vertex v2,
113           const rectangle<numeric_type> &old) const;
114   rectangle<numeric_type> flip_accordingly
115           (const rectangle<numeric_type> &to_flip, origin_vertex to_flip_origin,
116           origin_vertex target_origin) const;
117     //!< Flips the points in "to_flip" to match the "target_origin".
118     /*!< swaps the points contained in a rectangle that uses a particular point
119     as the vertex ("to_flip_origin") so that the points are arranged
120     according to a second choice of vertex ("target_origin"). */
121 };
122
123 //////////////
124
125 // implementations for longer methods below...
126
127 template <class numeric_type>
128 rectangle_warper<numeric_type>::rectangle_warper
129     (const rectangle<numeric_type> &system_1,
130      const rectangle<numeric_type> &system_2,
131      origin_vertex v1, origin_vertex v2)
132 : _system_1(system_1), _system_2(system_2), _vert_1(v1), _vert_2(v2)
133 {}
134
135 template <class numeric_type>
136 rectangle_warper<numeric_type>::~rectangle_warper() {}
137
138 template <class numeric_type>
139 void rectangle_warper<numeric_type>::system_1
140     (const rectangle<numeric_type> &to_set, origin_vertex v)
141 { _system_1 = to_set; _vert_1 = v; }
142
143 template <class numeric_type>
144 void rectangle_warper<numeric_type>::system_2
145     (const rectangle<numeric_type> &to_set, origin_vertex v)
146 { _system_2 = to_set; _vert_2 = v; }
147
148 template <class numeric_type>
149 point<numeric_type> rectangle_warper<numeric_type>::to_system_1
150     (const point<numeric_type> &in_system_2) const
151 { return scale_point(_system_2, _system_1, _vert_2, _vert_1, in_system_2); }
152
153 template <class numeric_type>
154 point<numeric_type> rectangle_warper<numeric_type>::to_system_2
155     (const point<numeric_type> &in_system_1) const
156 { return scale_point(_system_1, _system_2, _vert_1, _vert_2, in_system_1); }
157
158 template <class numeric_type>
159 rectangle<numeric_type> rectangle_warper<numeric_type>::to_system_1
160     (const rectangle<numeric_type> &in_system_2) const
161
162   return scale_rectangle(_system_2, _system_1, _vert_2, _vert_1, 
163       in_system_2); 
164 }
165
166 template <class numeric_type>
167 rectangle<numeric_type> rectangle_warper<numeric_type>::to_system_2
168     (const rectangle<numeric_type> &in_system_1) const
169 {
170   return scale_rectangle(_system_1, _system_2, _vert_1, _vert_2,
171       in_system_1); 
172 }
173
174 template <class numeric_type>
175 void rectangle_warper<numeric_type>::separate_vertical
176     (origin_vertex v, vertical_component &to_set) const
177 {
178   if ( (v == BOTTOM_LEFT) || (v == BOTTOM_RIGHT) ) to_set = RW_BOTTOM;
179   to_set = RW_TOP;
180 }
181
182 template <class numeric_type>
183 void rectangle_warper<numeric_type>::separate_horizontal
184     (origin_vertex v, horizontal_component &to_set) const
185 {
186   if ( (v == BOTTOM_LEFT) || (v == TOP_LEFT) ) to_set = RW_LEFT;
187   to_set = RW_RIGHT;
188 }
189
190 template <class numeric_type>
191 rectangle<numeric_type> rectangle_warper<numeric_type>::flip_accordingly
192     (const rectangle<numeric_type> &to_flip, origin_vertex flipo,
193     origin_vertex targo) const
194 {
195 //LOG(basis::astring("flipping ") + to_flip.text_form() + " from " + flipo.text_form() + " to " + targo.text_form());
196   if (flipo == targo) return to_flip;
197   numeric_type x1(to_flip.vertex_1().x());
198   numeric_type y1(to_flip.vertex_1().y());
199   numeric_type x2(to_flip.vertex_2().x());
200   numeric_type y2(to_flip.vertex_2().y());
201   horizontal_component horiz1;
202   separate_horizontal(flipo, horiz1);
203   horizontal_component horiz2;
204   separate_horizontal(targo, horiz2);
205   bool flip_x = bool(horiz1 != horiz2);
206   vertical_component vert1;
207   separate_vertical(flipo, vert1);
208   vertical_component vert2;
209   separate_vertical(targo, vert2);
210   bool flip_y = bool(vert1 != vert2);
211   if (flip_x) swap_values(x1, x2);
212   if (flip_y) swap_values(y1, y2);
213 //LOG(basis::astring("it becomes ") + rectangle<numeric_type>(x1, y1, x2, y2).text_form());
214   return rectangle<numeric_type>(x1, y1, x2, y2);
215 }
216
217 template <class numeric_type>
218 rectangle<numeric_type> rectangle_warper<numeric_type>::scale_rectangle
219     (const rectangle<numeric_type> &source,
220     const rectangle<numeric_type> &target, origin_vertex source_origin,
221     origin_vertex target_origin, const rectangle<numeric_type> &old) const
222 {
223   rectangle<numeric_type> s = rectangle<numeric_type>
224       (flip_accordingly(source, source_origin, BOTTOM_LEFT));
225   numeric_type width_source = s.vertex_2().x() - s.vertex_1().x();
226   numeric_type height_source = s.vertex_2().y() - s.vertex_1().y();
227   if ( !width_source || !height_source ) {
228 //    cerr << "degenerate rectangle in rectangle_warper::scaler: " << s
229 //      << endl << flush;
230     return old;
231   }
232   rectangle<numeric_type> t(flip_accordingly(target, target_origin, BOTTOM_LEFT));
233   numeric_type width_target = t.vertex_2().x() - t.vertex_1().x();
234   numeric_type height_target = t.vertex_2().y() - t.vertex_1().y();
235   numeric_type x_scale = width_target / width_source;
236   numeric_type y_scale = height_target / height_source;
237
238 //LOG(basis::astring("scaler: source ") + source.text_form() + " with vert " + source_origin.text_form() + " becomes " + s + " target " + target + " with vert " + target_origin + " becomes " + t + ".");
239
240   rectangle<numeric_type> o(flip_accordingly(old, source_origin, BOTTOM_LEFT));
241
242   rectangle<numeric_type> to_return = flip_accordingly(rectangle<numeric_type>
243      ((o.vertex_1().x() - s.vertex_1().x()) * x_scale + t.vertex_1().x(),
244       (o.vertex_1().y() - s.vertex_1().y()) * y_scale + t.vertex_1().y(),
245       (o.vertex_2().x() - s.vertex_1().x()) * x_scale + t.vertex_1().x(),
246       (o.vertex_2().y() - s.vertex_1().y()) * y_scale + t.vertex_1().y()),
247      BOTTOM_LEFT, target_origin);
248
249 //  LOG(basis::astring("old ") + old.text_form() + " with source vert becomes " + o.text_form() + " and then is moved into " + to_return.text_form());
250
251   return to_return;
252 }
253
254 template <class numeric_type>
255 point<numeric_type> rectangle_warper<numeric_type>::scale_point
256   (const rectangle<numeric_type> &source, const rectangle<numeric_type> &target,
257    origin_vertex source_origin, origin_vertex target_origin,
258    const point<numeric_type> &old) const
259 {
260   // gross but simple.
261   return scale_rectangle(source, target, source_origin, target_origin,
262       rectangle<numeric_type>(old, old)).vertex_1();
263 }
264
265 template <class numeric_type>
266 basis::astring rectangle_warper<numeric_type>::vertex_name(origin_vertex v) const
267 {
268   basis::astring name("unknown");
269   switch (v) {
270     case BOTTOM_LEFT: name = "bottom-left"; break;
271     case BOTTOM_RIGHT: name = "bottom-right"; break;
272     case TOP_LEFT: name = "top-left"; break;
273     case TOP_RIGHT: name = "top-right"; break;
274   }
275   return name;
276 }
277
278 template <class numeric_type>
279 basis::astring rectangle_warper<numeric_type>::text_form() const
280 {
281   return basis::astring("<warps from: ") + _system_1.text_form()
282       + basis::astring(" with vertex at ") + vertex_name(_vert_1)
283       + basis::astring(" into ") + _system_2.text_form()
284       + basis::astring(" with vertex at ") + vertex_name(_vert_2)
285       + basis::astring(">");
286 }
287
288 } // namespace.
289
290 #endif
291