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