1/* graphene-plane.c: A plane in 3D space
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-plane
28 * @Title: Plane
29 * @Short_Description: A plane in 3D space
30 *
31 * #graphene_plane_t is a structure representing a plane that extends
32 * infinitely in 3D space, described using the [Hessian normal
33 * form](http://mathworld.wolfram.com/HessianNormalForm.html)
34 * of a unit length normal vector pointing towards the origin, and a
35 * constant distance from the origin along the normal vector.
36 */
37
38#include "graphene-private.h"
39
40#include "graphene-plane.h"
41
42#include "graphene-alloc-private.h"
43#include "graphene-matrix.h"
44#include "graphene-point3d.h"
45#include "graphene-vec3.h"
46#include "graphene-vec4.h"
47
48#include <math.h>
49
50/**
51 * graphene_plane_alloc: (constructor)
52 *
53 * Allocates a new #graphene_plane_t structure.
54 *
55 * The contents of the returned structure are undefined.
56 *
57 * Returns: (transfer full): the newly allocated #graphene_plane_t.
58 * Use graphene_plane_free() to free the resources allocated by
59 * this function
60 *
61 * Since: 1.2
62 */
63graphene_plane_t *
64graphene_plane_alloc (void)
65{
66 return graphene_aligned_alloc0 (size: sizeof (graphene_plane_t), number: 1, alignment: 16);
67}
68
69/**
70 * graphene_plane_free:
71 * @p: a #graphene_plane_t
72 *
73 * Frees the resources allocated by graphene_plane_alloc().
74 *
75 * Since: 1.2
76 */
77void
78graphene_plane_free (graphene_plane_t *p)
79{
80 graphene_aligned_free (mem: p);
81}
82
83/**
84 * graphene_plane_init:
85 * @p: the #graphene_plane_t to initialize
86 * @normal: (nullable): a unit length normal vector defining the plane
87 * pointing towards the origin; if unset, we use the X axis by default
88 * @constant: the distance from the origin to the plane along the
89 * normal vector; the sign determines the half-space occupied by the
90 * plane
91 *
92 * Initializes the given #graphene_plane_t using the given @normal vector
93 * and @constant values.
94 *
95 * Returns: (transfer none): the initialized plane
96 *
97 * Since: 1.2
98 */
99graphene_plane_t *
100graphene_plane_init (graphene_plane_t *p,
101 const graphene_vec3_t *normal,
102 float constant)
103{
104 if (normal != NULL)
105 graphene_vec3_init_from_vec3 (v: &p->normal, src: normal);
106 else
107 graphene_vec3_init_from_vec3 (v: &p->normal, src: graphene_vec3_x_axis ());
108
109 p->constant = constant;
110
111 return p;
112}
113
114/**
115 * graphene_plane_init_from_vec4:
116 * @p: the #graphene_plane_t to initialize
117 * @src: a #graphene_vec4_t containing the normal vector in its first
118 * three components, and the distance in its fourth component
119 *
120 * Initializes the given #graphene_plane_t using the components of
121 * the given #graphene_vec4_t vector.
122 *
123 * Returns: (transfer none): the initialized plane
124 *
125 * Since: 1.2
126 */
127graphene_plane_t *
128graphene_plane_init_from_vec4 (graphene_plane_t *p,
129 const graphene_vec4_t *src)
130{
131 graphene_vec4_get_xyz (v: src, res: &p->normal);
132 p->constant = graphene_vec4_get_w (v: src);
133
134 return p;
135}
136
137/**
138 * graphene_plane_init_from_plane:
139 * @p: the #graphene_plane_t to initialize
140 * @src: a #graphene_plane_t
141 *
142 * Initializes the given #graphene_plane_t using the normal
143 * vector and constant of another #graphene_plane_t.
144 *
145 * Returns: (transfer none): the initialized plane
146 *
147 * Since: 1.2
148 */
149graphene_plane_t *
150graphene_plane_init_from_plane (graphene_plane_t *p,
151 const graphene_plane_t *src)
152{
153 graphene_vec3_init_from_vec3 (v: &p->normal, src: &src->normal);
154 p->constant = src->constant;
155
156 return p;
157}
158
159/**
160 * graphene_plane_init_from_point:
161 * @p: the #graphene_plane_t to initialize
162 * @normal: a normal vector defining the plane pointing towards the origin
163 * @point: a #graphene_point3d_t
164 *
165 * Initializes the given #graphene_plane_t using the given normal vector
166 * and an arbitrary co-planar point.
167 *
168 * Returns: (transfer none): the initialized plane
169 *
170 * Since: 1.2
171 */
172graphene_plane_t *
173graphene_plane_init_from_point (graphene_plane_t *p,
174 const graphene_vec3_t *normal,
175 const graphene_point3d_t *point)
176{
177 graphene_vec3_t v_p;
178
179 graphene_vec3_init_from_vec3 (v: &p->normal, src: normal);
180
181 graphene_point3d_to_vec3 (p: point, v: &v_p);
182 p->constant = graphene_vec3_dot (a: &v_p, b: &p->normal) * -1;
183
184 return p;
185}
186
187/**
188 * graphene_plane_init_from_points:
189 * @p: the #graphene_plane_t to initialize
190 * @a: a #graphene_point3d_t
191 * @b: a #graphene_point3d_t
192 * @c: a #graphene_point3d_t
193 *
194 * Initializes the given #graphene_plane_t using the 3 provided co-planar
195 * points.
196 *
197 * The winding order is counter-clockwise, and determines which direction
198 * the normal vector will point.
199 *
200 * Returns: (transfer none): the initialized plane
201 *
202 * Since: 1.2
203 */
204graphene_plane_t *
205graphene_plane_init_from_points (graphene_plane_t *p,
206 const graphene_point3d_t *a,
207 const graphene_point3d_t *b,
208 const graphene_point3d_t *c)
209{
210 graphene_vec3_t v_a, v_b, v_c;
211 graphene_vec3_t v1, v2;
212 graphene_vec3_t normal;
213
214 graphene_point3d_to_vec3 (p: a, v: &v_a);
215 graphene_point3d_to_vec3 (p: b, v: &v_b);
216 graphene_point3d_to_vec3 (p: c, v: &v_c);
217
218 graphene_vec3_subtract (a: &v_c, b: &v_b, res: &v1);
219 graphene_vec3_subtract (a: &v_a, b: &v_b, res: &v2);
220 graphene_vec3_cross (a: &v1, b: &v2, res: &normal);
221 graphene_vec3_normalize (v: &normal, res: &normal);
222
223 return graphene_plane_init_from_point (p, normal: &normal, point: a);
224}
225
226/**
227 * graphene_plane_normalize:
228 * @p: a #graphene_plane_t
229 * @res: (out caller-allocates): return location for the normalized plane
230 *
231 * Normalizes the vector of the given #graphene_plane_t,
232 * and adjusts the constant accordingly.
233 *
234 * Since: 1.2
235 */
236void
237graphene_plane_normalize (const graphene_plane_t *p,
238 graphene_plane_t *res)
239{
240 float normal_length = graphene_vec3_length (v: &p->normal);
241
242 graphene_vec3_normalize (v: &p->normal, res: &res->normal);
243 res->constant = p->constant / normal_length;
244}
245
246/**
247 * graphene_plane_negate:
248 * @p: a #graphene_plane_t
249 * @res: (out caller-allocates): return location for the negated plane
250 *
251 * Negates the normal vector and constant of a #graphene_plane_t, effectively
252 * mirroring the plane across the origin.
253 *
254 * Since: 1.2
255 */
256void
257graphene_plane_negate (const graphene_plane_t *p,
258 graphene_plane_t *res)
259{
260 graphene_vec3_negate (v: &p->normal, res: &res->normal);
261 res->constant = p->constant * -1.f;
262}
263
264/**
265 * graphene_plane_distance:
266 * @p: a #graphene_plane_t
267 * @point: a #graphene_point3d_t
268 *
269 * Computes the distance of @point from a #graphene_plane_t.
270 *
271 * Returns: the distance of the given #graphene_point3d_t from the plane
272 *
273 * Since: 1.2
274 */
275float
276graphene_plane_distance (const graphene_plane_t *p,
277 const graphene_point3d_t *point)
278{
279 graphene_vec3_t v;
280
281 graphene_point3d_to_vec3 (p: point, v: &v);
282
283 return graphene_vec3_dot (a: &p->normal, b: &v) + p->constant;
284}
285
286/**
287 * graphene_plane_get_normal:
288 * @p: a #graphene_plane_t
289 * @normal: (out caller-allocates): return location for the normal vector
290 *
291 * Retrieves the normal vector pointing towards the origin of the
292 * given #graphene_plane_t.
293 *
294 * Since: 1.2
295 */
296void
297graphene_plane_get_normal (const graphene_plane_t *p,
298 graphene_vec3_t *normal)
299{
300 graphene_vec3_init_from_vec3 (v: normal, src: &p->normal);
301}
302
303/**
304 * graphene_plane_get_constant:
305 * @p: a #graphene_plane_t
306 *
307 * Retrieves the distance along the normal vector of the
308 * given #graphene_plane_t from the origin.
309 *
310 * Returns: the constant value of the plane
311 *
312 * Since: 1.2
313 */
314float
315graphene_plane_get_constant (const graphene_plane_t *p)
316{
317 return p->constant;
318}
319
320static bool
321plane_equal (const void *p1,
322 const void *p2)
323{
324 const graphene_plane_t *a = p1;
325 const graphene_plane_t *b = p2;
326
327 return graphene_vec3_equal (v1: &a->normal, v2: &b->normal) &&
328 graphene_approx_val (a: a->constant, b: b->constant);
329}
330
331/**
332 * graphene_plane_equal:
333 * @a: a #graphene_plane_t
334 * @b: a #graphene_plane_t
335 *
336 * Checks whether the two given #graphene_plane_t are equal.
337 *
338 * Returns: `true` if the given planes are equal
339 *
340 * Since: 1.2
341 */
342bool
343graphene_plane_equal (const graphene_plane_t *a,
344 const graphene_plane_t *b)
345{
346 return graphene_pointer_equal (p1: a, p2: b, func: plane_equal);
347}
348
349/**
350 * graphene_plane_transform:
351 * @p: a #graphene_plane_t
352 * @matrix: a #graphene_matrix_t
353 * @normal_matrix: (nullable): a #graphene_matrix_t
354 * @res: (out caller-allocates): the transformed plane
355 *
356 * Transforms a #graphene_plane_t @p using the given @matrix
357 * and @normal_matrix.
358 *
359 * If @normal_matrix is %NULL, a transformation matrix for the plane
360 * normal will be computed from @matrix. If you are transforming
361 * multiple planes using the same @matrix it's recommended to compute
362 * the normal matrix beforehand to avoid incurring in the cost of
363 * recomputing it every time.
364 *
365 * Since: 1.10
366 */
367void
368graphene_plane_transform (const graphene_plane_t *p,
369 const graphene_matrix_t *matrix,
370 const graphene_matrix_t *normal_matrix,
371 graphene_plane_t *res)
372{
373 float constant = graphene_plane_get_constant (p);
374
375 /* Get other point on plane */
376 graphene_vec3_t coplanar_point;
377
378 graphene_vec3_scale (v: &p->normal, factor: -constant, res: &coplanar_point);
379
380 graphene_vec4_t coplanar_point_v4;
381 graphene_vec4_init_from_vec3 (v: &coplanar_point_v4, src: &coplanar_point, w: 1.0);
382
383 /* Transform other point (including translations, so vec4) */
384 graphene_vec4_t reference_point_v4;
385 graphene_matrix_transform_vec4 (m: matrix, v: &coplanar_point_v4, res: &reference_point_v4);
386
387 graphene_vec3_t reference_point;
388 graphene_vec4_get_xyz (v: &reference_point_v4, res: &reference_point);
389
390 /* Transform normal */
391 graphene_matrix_t normal_m;
392 if (normal_matrix == NULL)
393 {
394 graphene_matrix_inverse (m: matrix, res: &normal_m);
395 graphene_matrix_transpose (m: &normal_m, res: &normal_m);
396 }
397 else
398 normal_m = *normal_matrix;
399
400 graphene_vec3_t normal;
401 graphene_matrix_transform_vec3 (m: &normal_m, v: &p->normal, res: &normal);
402 graphene_vec3_normalize (v: &normal, res: &normal);
403
404 constant = -graphene_vec3_dot (a: &normal, b: &reference_point);
405
406 graphene_plane_init (p: res, normal: &normal, constant);
407}
408

source code of gtk/subprojects/graphene/src/graphene-plane.c