1 /* graphene-point.c: Point
2  *
3  * SPDX-License-Identifier: MIT
4  *
5  * Copyright 2014  Emmanuele Bassi
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 
26 /**
27  * SECTION:graphene-point
28  * @Title: Point
29  * @short_description: A point with 2 coordinates
30  *
31  * #graphene_point_t is a data structure capable of describing a point with
32  * two coordinates:
33  *
34  *  * @graphene_point_t.x
35  *  * @graphene_point_t.y
36  */
37 
38 #include "graphene-private.h"
39 
40 #include "graphene-point.h"
41 
42 #include "graphene-simd4f.h"
43 #include "graphene-vec2.h"
44 
45 #include <math.h>
46 
47 /**
48  * graphene_point_alloc: (constructor)
49  *
50  * Allocates a new #graphene_point_t structure.
51  *
52  * The coordinates of the returned point are (0, 0).
53  *
54  * It's possible to chain this function with graphene_point_init()
55  * or graphene_point_init_from_point(), e.g.:
56  *
57  * |[<!-- language="C" -->
58  *   graphene_point_t *
59  *   point_new (float x, float y)
60  *   {
61  *     return graphene_point_init (graphene_point_alloc (), x, y);
62  *   }
63  *
64  *   graphene_point_t *
65  *   point_copy (const graphene_point_t *p)
66  *   {
67  *     return graphene_point_init_from_point (graphene_point_alloc (), p);
68  *   }
69  * ]|
70  *
71  * Returns: (transfer full): the newly allocated #graphene_point_t.
72  *   Use graphene_point_free() to free the resources allocated by
73  *   this function.
74  *
75  * Since: 1.0
76  */
77 graphene_point_t *
graphene_point_alloc(void)78 graphene_point_alloc (void)
79 {
80   return calloc (1, sizeof (graphene_point_t));
81 }
82 
83 /**
84  * graphene_point_free:
85  * @p: a #graphene_point_t
86  *
87  * Frees the resources allocated by graphene_point_alloc().
88  *
89  * Since: 1.0
90  */
91 void
graphene_point_free(graphene_point_t * p)92 graphene_point_free (graphene_point_t *p)
93 {
94   free (p);
95 }
96 
97 /**
98  * graphene_point_init:
99  * @p: a #graphene_point_t
100  * @x: the X coordinate
101  * @y: the Y coordinate
102  *
103  * Initializes @p to the given @x and @y coordinates.
104  *
105  * It's safe to call this function multiple times.
106  *
107  * Returns: (transfer none): the initialized point
108  *
109  * Since: 1.0
110  */
111 graphene_point_t *
graphene_point_init(graphene_point_t * p,float x,float y)112 graphene_point_init (graphene_point_t *p,
113                      float             x,
114                      float             y)
115 {
116   p->x = x;
117   p->y = y;
118 
119   return p;
120 }
121 
122 /**
123  * graphene_point_init_from_point:
124  * @p: a #graphene_point_t
125  * @src: the #graphene_point_t to use
126  *
127  * Initializes @p with the same coordinates of @src.
128  *
129  * Returns: (transfer none): the initialized point
130  *
131  * Since: 1.0
132  */
133 graphene_point_t *
graphene_point_init_from_point(graphene_point_t * p,const graphene_point_t * src)134 graphene_point_init_from_point (graphene_point_t       *p,
135                                 const graphene_point_t *src)
136 {
137   *p = *src;
138 
139   return p;
140 }
141 
142 /**
143  * graphene_point_init_from_vec2:
144  * @p: the #graphene_point_t to initialize
145  * @src: a #graphene_vec2_t
146  *
147  * Initializes @p with the coordinates inside the given #graphene_vec2_t.
148  *
149  * Returns: (transfer none): the initialized point
150  *
151  * Since: 1.4
152  */
153 graphene_point_t *
graphene_point_init_from_vec2(graphene_point_t * p,const graphene_vec2_t * src)154 graphene_point_init_from_vec2 (graphene_point_t      *p,
155                                const graphene_vec2_t *src)
156 {
157   p->x = graphene_simd4f_get_x (src->value);
158   p->y = graphene_simd4f_get_y (src->value);
159 
160   return p;
161 }
162 
163 static bool
point_equal(const void * p1,const void * p2)164 point_equal (const void *p1,
165              const void *p2)
166 {
167   const graphene_point_t *a = p1;
168   const graphene_point_t *b = p2;
169 
170   return graphene_point_near (a, b, GRAPHENE_FLOAT_EPSILON);
171 }
172 
173 /**
174  * graphene_point_equal:
175  * @a: a #graphene_point_t
176  * @b: a #graphene_point_t
177  *
178  * Checks if the two points @a and @b point to the same
179  * coordinates.
180  *
181  * This function accounts for floating point fluctuations; if
182  * you want to control the fuzziness of the match, you can use
183  * graphene_point_near() instead.
184  *
185  * Returns: `true` if the points have the same coordinates
186  *
187  * Since: 1.0
188  */
189 bool
graphene_point_equal(const graphene_point_t * a,const graphene_point_t * b)190 graphene_point_equal (const graphene_point_t *a,
191                       const graphene_point_t *b)
192 {
193   return graphene_pointer_equal (a, b, point_equal);
194 }
195 
196 /**
197  * graphene_point_distance:
198  * @a: a #graphene_point_t
199  * @b: a #graphene_point_t
200  * @d_x: (out) (optional): distance component on the X axis
201  * @d_y: (out) (optional): distance component on the Y axis
202  *
203  * Computes the distance between @a and @b.
204  *
205  * Returns: the distance between the two points
206  *
207  * Since: 1.0
208  */
209 float
graphene_point_distance(const graphene_point_t * a,const graphene_point_t * b,float * d_x,float * d_y)210 graphene_point_distance (const graphene_point_t *a,
211                          const graphene_point_t *b,
212                          float                  *d_x,
213                          float                  *d_y)
214 {
215   if (a == b)
216     return 0.f;
217 
218   graphene_simd4f_t v_a = graphene_simd4f_init (a->x, a->y, 0.f, 0.f);
219   graphene_simd4f_t v_b = graphene_simd4f_init (b->x, b->y, 0.f, 0.f);
220   graphene_simd4f_t v_res = graphene_simd4f_sub (v_a, v_b);
221 
222   if (d_x != NULL)
223     *d_x = fabsf (graphene_simd4f_get_x (v_res));
224 
225   if (d_y != NULL)
226     *d_y = fabsf (graphene_simd4f_get_y (v_res));
227 
228   return graphene_simd4f_get_x (graphene_simd4f_length2 (v_res));
229 }
230 
231 /**
232  * graphene_point_near:
233  * @a: a #graphene_point_t
234  * @b: a #graphene_point_t
235  * @epsilon: threshold between the two points
236  *
237  * Checks whether the two points @a and @b are within
238  * the threshold of @epsilon.
239  *
240  * Returns: `true` if the distance is within @epsilon
241  *
242  * Since: 1.0
243  */
244 bool
graphene_point_near(const graphene_point_t * a,const graphene_point_t * b,float epsilon)245 graphene_point_near (const graphene_point_t *a,
246                      const graphene_point_t *b,
247                      float                   epsilon)
248 {
249   if (a == b)
250     return true;
251 
252   graphene_simd4f_t v_a = graphene_simd4f_init (a->x, a->y, 0.f, 0.f);
253   graphene_simd4f_t v_b = graphene_simd4f_init (b->x, b->y, 0.f, 0.f);
254   graphene_simd4f_t v_res = graphene_simd4f_sub (v_a, v_b);
255 
256   return fabsf (graphene_simd4f_get_x (v_res)) < epsilon &&
257          fabsf (graphene_simd4f_get_y (v_res)) < epsilon;
258 }
259 
260 /**
261  * graphene_point_interpolate:
262  * @a: a #graphene_point_t
263  * @b: a #graphene_point_t
264  * @factor: the linear interpolation factor
265  * @res: (out caller-allocates): return location for the interpolated
266  *   point
267  *
268  * Linearly interpolates the coordinates of @a and @b using the
269  * given @factor.
270  *
271  * Since: 1.0
272  */
273 void
graphene_point_interpolate(const graphene_point_t * a,const graphene_point_t * b,double factor,graphene_point_t * res)274 graphene_point_interpolate (const graphene_point_t *a,
275                             const graphene_point_t *b,
276                             double                  factor,
277                             graphene_point_t       *res)
278 {
279   res->x = graphene_lerp (a->x, b->x, factor);
280   res->y = graphene_lerp (a->y, b->y, factor);
281 }
282 
283 /**
284  * graphene_point_to_vec2:
285  * @p: a #graphene_point_t
286  * @v: (out caller-allocates): return location for the vertex
287  *
288  * Stores the coordinates of the given #graphene_point_t into a
289  * #graphene_vec2_t.
290  *
291  * Since: 1.4
292  */
293 void
graphene_point_to_vec2(const graphene_point_t * p,graphene_vec2_t * v)294 graphene_point_to_vec2 (const graphene_point_t *p,
295                         graphene_vec2_t        *v)
296 {
297   v->value = graphene_simd4f_init (p->x, p->y, 0.f, 0.f);
298 }
299 
300 static const graphene_point_t _graphene_point_zero;
301 
302 /**
303  * graphene_point_zero:
304  *
305  * Returns a point fixed at (0, 0).
306  *
307  * Returns: (transfer none): a fixed point
308  *
309  * Since: 1.0
310  */
311 const graphene_point_t *
graphene_point_zero(void)312 graphene_point_zero (void)
313 {
314   return &_graphene_point_zero;
315 }
316