1 /*
2 * go-geometry.c : A collection of geometry related functions.
3 *
4 * Copyright (C) 2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 #include <goffice/goffice-config.h>
23
24 #include <goffice/math/go-math.h>
25 #include <goffice/utils/go-geometry.h>
26
27 #include <glib/gi18n-lib.h>
28
29 #define dist(x0,y0,x1,y1) hypot((x0) - (x1),(y0) - (y1))
30
31 /**
32 * go_geometry_cartesian_to_polar:
33 * @x: cartesian coordinate
34 * @y: cartesian coordinate
35 * @rho: polar coordinate
36 * @theta: polar coordinate
37 *
38 * Converts cartesion coordinates to polar coordinates.
39 *
40 **/
41 void
go_geometry_cartesian_to_polar(double x,double y,double * rho,double * theta)42 go_geometry_cartesian_to_polar (double x, double y, double *rho, double *theta)
43 {
44 *rho = hypot (x, y);
45 *theta = atan2 (y, x);
46 }
47
48 /**
49 * go_geometry_point_to_segment:
50 * @xp: point coordinate
51 * @yp: point coordinate
52 * @xs: segment start coordinate
53 * @ys: segment start coordinate
54 * @w: extent of segment
55 * @h: extent of segment
56 *
57 * Returns: the distance between a point and a segment.
58 **/
59 double
go_geometry_point_to_segment(double xp,double yp,double xs,double ys,double w,double h)60 go_geometry_point_to_segment (double xp, double yp, double xs, double ys, double w, double h)
61 {
62 double c1, c2, b;
63
64 c1 = w * (xp - xs) + h * (yp - ys);
65 if (c1 <= 0.0)
66 return dist (xp, yp, xs, ys);
67
68 c2 = w * w + h * h;
69 if (c2 <= c1)
70 return dist (xp, yp, xs + w, ys + h);
71
72 b = c1 / c2;
73 return dist (xp, yp, xs + b * w, ys + b * h);
74 }
75
76 /**
77 * go_geometry_AABR_add:
78 * @aabr0: a #GOGeometryAABR
79 * @aabr1: a #GOGeometryAABR
80 *
81 * Computes the Axis Aligned Bounding Rectangle of aabr0 and aabr1,
82 * and stores result in aabr0.
83 *
84 **/
85 void
go_geometry_AABR_add(GOGeometryAABR * aabr0,GOGeometryAABR const * aabr1)86 go_geometry_AABR_add (GOGeometryAABR *aabr0, GOGeometryAABR const *aabr1)
87 {
88 double min, max;
89
90 min = MIN (aabr0->x, aabr1->x);
91 max = MAX (aabr0->x + aabr0->w, aabr1->x + aabr1->w);
92 aabr0->x = min;
93 aabr0->w = max - min;
94
95 min = MIN (aabr0->y, aabr1->y);
96 max = MAX (aabr0->y + aabr0->h, aabr1->y + aabr1->h);
97 aabr0->y = min;
98 aabr0->h = max - min;
99 }
100
101 /**
102 * go_geometry_OBR_to_AABR:
103 * @obr: a #GOGeometryOBR
104 * @aabr: a #GOGeometryAABR
105 *
106 * Stores Axis Aligned Bounding Rectangle of @obr in @aabr.
107 *
108 **/
109 void
go_geometry_OBR_to_AABR(GOGeometryOBR const * obr,GOGeometryAABR * aabr)110 go_geometry_OBR_to_AABR (GOGeometryOBR const *obr, GOGeometryAABR *aabr)
111 {
112 double cos_alpha = cos (obr->alpha);
113 double sin_alpha = sin (obr->alpha);
114
115 aabr->w = fabs (obr->w * cos_alpha) + fabs (obr->h * sin_alpha);
116 aabr->h = fabs (obr->w * sin_alpha) + fabs (obr->h * cos_alpha);
117 aabr->x = obr->x - aabr->w / 2.0 ;
118 aabr->y = obr->y - aabr->h / 2.0 ;
119 }
120
121 /**
122 * go_geometry_test_OBR_overlap:
123 * @obr0: a #GOGeometryOBR
124 * @obr1: a #GOGeometryOBR
125 *
126 * Overlap test of Oriented Bounding Rectangles by the separating axis method.
127 *
128 * return value: %TRUE if OOBRs overlap
129 **/
130 gboolean
go_geometry_test_OBR_overlap(GOGeometryOBR const * obr0,GOGeometryOBR const * obr1)131 go_geometry_test_OBR_overlap (GOGeometryOBR const *obr0, GOGeometryOBR const *obr1)
132 {
133 double TL, pa, pb;
134 double cos_delta, sin_delta;
135 double a00, a01, a10, a11;
136 double alpha, delta;
137
138 cos_delta = fabs (cos (obr1->alpha - obr0->alpha));
139 sin_delta = fabs (sin (obr1->alpha - obr0->alpha));
140
141 go_geometry_cartesian_to_polar (obr1->x - obr0->x,
142 obr1->y - obr0->y,
143 &delta, &alpha);
144
145 a00 = fabs (obr0->w / 2.0);
146 a01 = fabs (obr0->h / 2.0);
147 a10 = fabs (obr1->w / 2.0);
148 a11 = fabs (obr1->h / 2.0);
149
150 /* Separating axis parallel to obr0->w */
151 TL = fabs (delta * cos (alpha - obr0->alpha));
152 pa = a00;
153 pb = a10 * cos_delta + a11 * sin_delta;
154 if (TL > pa + pb) return FALSE;
155
156 /* Separating axis parallel to obr->h */
157 TL = fabs (delta * sin (alpha - obr0->alpha));
158 pa = a01;
159 pb = a10 * sin_delta + a11 * cos_delta;
160 if (TL > pa + pb) return FALSE;
161
162 /* Separating axis parallel to obr1->w */
163 TL = fabs (delta * cos (obr1->alpha - alpha));
164 pa = a00 * cos_delta + a01 * sin_delta;
165 pb = a10;
166 if (TL > pa + pb) return FALSE;
167
168 /* Separating axis parallel to obr1->h */
169 TL = fabs (delta * sin (obr1->alpha - alpha));
170 pa = a00 * sin_delta + a01 * cos_delta;
171 pb = a11;
172 if (TL > pa + pb) return FALSE;
173
174 return TRUE;
175 }
176
177 /**
178 * go_geometry_get_rotation_type:
179 * @alpha: angle in radians
180 *
181 * Calculates rotation type for handling of special angles (alpha = n * pi / 2)
182 *
183 * Returns: a #GOGeometryRotationType
184 **/
185 GOGeometryRotationType
go_geometry_get_rotation_type(double alpha)186 go_geometry_get_rotation_type (double alpha)
187 {
188 unsigned index;
189
190 if (alpha < 0 || alpha > 2 * M_PI)
191 alpha = alpha - 2 * M_PI * floor (alpha / (2 * M_PI));
192
193 if (fmod(alpha + GO_GEOMETRY_ANGLE_TOLERANCE, M_PI / 2.0) > 2 * GO_GEOMETRY_ANGLE_TOLERANCE)
194 return GO_ROTATE_FREE;
195 index = go_rint (2.0 * alpha / M_PI);
196 return index < GO_ROTATE_FREE ? index : GO_ROTATE_NONE;
197 }
198
199 /**
200 * go_geometry_calc_label_anchor:
201 * @obr: bounding rectangle of label
202 * @alpha: angle of axis
203 *
204 * Returns: computed label anchor, to be used by go_geometry_calc_label_position.
205 **/
206 GOGeometrySide
go_geometry_calc_label_anchor(GOGeometryOBR * obr,double alpha)207 go_geometry_calc_label_anchor (GOGeometryOBR *obr, double alpha)
208 {
209 double dt, ds;
210
211 dt = fabs (obr->w * sin (obr->alpha - alpha) / 2.0);
212 ds = fabs (obr->h * cos (obr->alpha - alpha) / 2.0);
213
214 return dt < ds ? GO_SIDE_TOP_BOTTOM : GO_SIDE_LEFT_RIGHT;
215 }
216
217 /**
218 * go_geometry_calc_label_position:
219 * @obr: bounding rectangle of label
220 * @alpha: angle of axis
221 * @offset: minimum distance between label and axis
222 * @side: side of label with respect to axis
223 * @anchor: where to anchor the label
224 *
225 * Convenience routine that computes position of a label relative to an axis.
226 *
227 * Returns: the computed anchor if @anchor == GO_SIDE_AUTO, or @anchor value.
228 **/
229 GOGeometrySide
go_geometry_calc_label_position(GOGeometryOBR * obr,double alpha,double offset,GOGeometrySide side,GOGeometrySide anchor)230 go_geometry_calc_label_position (GOGeometryOBR *obr, double alpha, double offset,
231 GOGeometrySide side, GOGeometrySide anchor)
232 {
233 double dt, ds;
234 double sinus, cosinus;
235
236 if (side == GO_SIDE_RIGHT)
237 alpha += M_PI;
238
239 sinus = sin (obr->alpha - alpha);
240 cosinus = cos (obr->alpha - alpha);
241
242 dt = fabs (obr->w * sinus / 2.0);
243 ds = fabs (obr->h * cosinus / 2.0);
244
245 if (anchor == GO_SIDE_AUTO)
246 anchor = dt < ds ? GO_SIDE_TOP_BOTTOM : GO_SIDE_LEFT_RIGHT;
247
248 if ((anchor & GO_SIDE_TOP_BOTTOM) != 0) {
249 offset += dt;
250 obr->x = obr->h * sin (obr->alpha) / 2.0;
251 obr->y = -obr->h * cos (obr->alpha) / 2.0;
252 if (cosinus < 0.0) {
253 obr->x = -obr->x;
254 obr->y = -obr->y;
255 }
256 } else {
257 offset += ds;
258 obr->x = -obr->w * cos (obr->alpha) / 2.0;
259 obr->y = -obr->w * sin (obr->alpha) / 2.0;
260 if (sinus < 0.0) {
261 obr->x = -obr->x;
262 obr->y = -obr->y;
263 }
264 }
265 obr->x += offset * sin (alpha);
266 obr->y += offset * -cos (alpha);
267
268 return anchor;
269 }
270
271 /**
272 * go_direction_is_horizontal :
273 * @d: #GODirection
274 *
275 * Returns: TRUE for GO_DIRECTION_LEFT and GO_DIRECTION_RIGHT.
276 **/
277 gboolean
go_direction_is_horizontal(GODirection d)278 go_direction_is_horizontal (GODirection d)
279 {
280 return (d & 2) != 0;
281 }
282
283 /**
284 * go_direction_is_forward :
285 * @d: #GODirection
286 *
287 * Returns: TRUE for GO_DIRECTION_DOWN or GO_DIRECTION_RIGHT.
288 **/
289 gboolean
go_direction_is_forward(GODirection d)290 go_direction_is_forward (GODirection d)
291 {
292 return (d & 1) != 0;
293 }
294
295 static GEnumValue const directions[] = {
296 { GO_DIRECTION_NONE, "GO_DIRECTION_NONE", N_("none") },
297 { GO_DIRECTION_DOWN, "GO_DIRECTION_DOWN", N_("down") },
298 { GO_DIRECTION_UP, "GO_DIRECTION_UP", N_("up") },
299 { GO_DIRECTION_RIGHT, "GO_DIRECTION_RIGHT", N_("right") },
300 { GO_DIRECTION_LEFT, "GO_DIRECTION_LEFT", N_("left") },
301 { 0, NULL, NULL }
302 };
303
304 GType
go_direction_get_type(void)305 go_direction_get_type (void)
306 {
307 static GType etype = 0;
308 if (etype == 0) {
309 etype = g_enum_register_static (g_intern_static_string ("GODirection"), directions);
310 }
311 return etype;
312 }
313
314 char const *
go_direction_get_name(GODirection d)315 go_direction_get_name (GODirection d)
316 {
317 unsigned i;
318 g_return_val_if_fail (d < G_N_ELEMENTS (directions), NULL);
319 for (i = 0; i < G_N_ELEMENTS (directions); i++)
320 if (d == (GODirection) directions[i].value)
321 return _(directions[i].value_nick);
322 return NULL;
323 }
324