1 /* SPDX-License-Identifier: MIT */
2
3 #include <stdio.h>
4 #include <graphene.h>
5 #include <mutest.h>
6
7 static void
triangle_init_from_point3d(mutest_spec_t * spec)8 triangle_init_from_point3d (mutest_spec_t *spec)
9 {
10 graphene_point3d_t a = GRAPHENE_POINT3D_INIT ( 0.f, 1.f, 0.f);
11 graphene_point3d_t b = GRAPHENE_POINT3D_INIT ( 1.f, -1.f, 0.f);
12 graphene_point3d_t c = GRAPHENE_POINT3D_INIT (-1.f, -1.f, 0.f);
13 graphene_point3d_t zero = GRAPHENE_POINT3D_INIT_ZERO;
14 graphene_point3d_t check_a, check_b, check_c;
15 graphene_triangle_t *t;
16
17 t = graphene_triangle_init_from_point3d (graphene_triangle_alloc (), &a, &b, &c);
18 graphene_triangle_get_points (t, &check_a, &check_b, &check_c);
19 mutest_expect ("triangle.a to match first point",
20 mutest_bool_value (graphene_point3d_equal (&check_a, &a)),
21 mutest_to_be_true,
22 NULL);
23 mutest_expect ("triangle.b to match second point",
24 mutest_bool_value (graphene_point3d_equal (&check_b, &b)),
25 mutest_to_be_true,
26 NULL);
27 mutest_expect ("triangle.c to match third point",
28 mutest_bool_value (graphene_point3d_equal (&check_c, &c)),
29 mutest_to_be_true,
30 NULL);
31 mutest_expect ("a unit triangle to have an area of 2",
32 mutest_float_value (graphene_triangle_get_area (t)),
33 mutest_to_be_close_to, 2.0, 0.0001,
34 NULL);
35
36 graphene_triangle_init_from_point3d (t, NULL, NULL, NULL);
37 graphene_triangle_get_points (t, &check_a, &check_b, &check_c);
38 mutest_expect ("triangle.a to be 0 when initialized to NULL",
39 mutest_bool_value (graphene_point3d_equal (&check_a, &zero)),
40 mutest_to_be_true,
41 NULL);
42 mutest_expect ("triangle.b to be 0 when initialized to NULL",
43 mutest_bool_value (graphene_point3d_equal (&check_b, &zero)),
44 mutest_to_be_true,
45 NULL);
46 mutest_expect ("triangle.c to be 0 when initialized to NULL",
47 mutest_bool_value (graphene_point3d_equal (&check_c, &zero)),
48 mutest_to_be_true,
49 NULL);
50 mutest_expect ("degenerate triangle to have an area of zero",
51 mutest_float_value (graphene_triangle_get_area (t)),
52 mutest_to_be_close_to, 0.0, 0.00001,
53 NULL);
54
55 graphene_triangle_free (t);
56 }
57
58 static void
triangle_init_from_vec3(mutest_spec_t * spec)59 triangle_init_from_vec3 (mutest_spec_t *spec)
60 {
61 graphene_vec3_t a, b, c;
62 graphene_vec3_t check_a, check_b, check_c;
63 graphene_triangle_t *t;
64
65 graphene_vec3_init (&a, 0, 1, 0);
66 graphene_vec3_init (&b, 1, -1, 0);
67 graphene_vec3_init (&c, -1, -1, 0);
68
69 t = graphene_triangle_init_from_vec3 (graphene_triangle_alloc (), &a, &b, &c);
70 graphene_triangle_get_vertices (t, &check_a, &check_b, &check_c);
71 mutest_expect ("triangle.a to match first vector",
72 mutest_bool_value (graphene_vec3_equal (&check_a, &a)),
73 mutest_to_be_true,
74 NULL);
75 mutest_expect ("triangle.b to match second vector",
76 mutest_bool_value (graphene_vec3_equal (&check_b, &b)),
77 mutest_to_be_true,
78 NULL);
79 mutest_expect ("triangle.c to match third vector",
80 mutest_bool_value (graphene_vec3_equal (&check_c, &c)),
81 mutest_to_be_true,
82 NULL);
83 mutest_expect ("a unit triangle to have an area of 2",
84 mutest_float_value (graphene_triangle_get_area (t)),
85 mutest_to_be_close_to, 2.0, 0.0001,
86 NULL);
87
88 graphene_triangle_init_from_vec3 (t, NULL, NULL, NULL);
89 graphene_triangle_get_vertices (t, &check_a, &check_b, &check_c);
90 mutest_expect ("triangle.a to be 0 when initialized to NULL",
91 mutest_bool_value (graphene_vec3_equal (&check_a, graphene_vec3_zero ())),
92 mutest_to_be_true,
93 NULL);
94 mutest_expect ("triangle.b to be 0 when initialized to NULL",
95 mutest_bool_value (graphene_vec3_equal (&check_b, graphene_vec3_zero ())),
96 mutest_to_be_true,
97 NULL);
98 mutest_expect ("triangle.c to be 0 when initialized to NULL",
99 mutest_bool_value (graphene_vec3_equal (&check_c, graphene_vec3_zero ())),
100 mutest_to_be_true,
101 NULL);
102 mutest_expect ("degenerate triangle to have an area of zero",
103 mutest_float_value (graphene_triangle_get_area (t)),
104 mutest_to_be_close_to, 0.0, 0.00001,
105 NULL);
106
107 graphene_triangle_free (t);
108 }
109
110 static void
triangle_init_from_float(mutest_spec_t * spec)111 triangle_init_from_float (mutest_spec_t *spec)
112 {
113 graphene_vec3_t a, b, c;
114 graphene_vec3_t check_a, check_b, check_c;
115 graphene_triangle_t *t;
116 float v[9] = { 0.f, 1.f, 0.f, 1.f, -1.f, 0.f, -1.f, -1.f, 0.f };
117
118 graphene_vec3_init_from_float (&a, v);
119 graphene_vec3_init_from_float (&b, v + 3);
120 graphene_vec3_init_from_float (&c, v + 6);
121
122 t = graphene_triangle_init_from_float (graphene_triangle_alloc (), v, v + 3, v + 6);
123 graphene_triangle_get_vertices (t, &check_a, &check_b, &check_c);
124 mutest_expect ("triangle.a to match first vector",
125 mutest_bool_value (graphene_vec3_equal (&check_a, &a)),
126 mutest_to_be_true,
127 NULL);
128 mutest_expect ("triangle.b to match second vector",
129 mutest_bool_value (graphene_vec3_equal (&check_b, &b)),
130 mutest_to_be_true,
131 NULL);
132 mutest_expect ("triangle.c to match third vector",
133 mutest_bool_value (graphene_vec3_equal (&check_c, &c)),
134 mutest_to_be_true,
135 NULL);
136 mutest_expect ("a unit triangle to have an area of 2",
137 mutest_float_value (graphene_triangle_get_area (t)),
138 mutest_to_be_close_to, 2.0, 0.0001,
139 NULL);
140
141 graphene_triangle_free (t);
142 }
143
144 static void
triangle_contains_point(mutest_spec_t * spec)145 triangle_contains_point (mutest_spec_t *spec)
146 {
147 graphene_point3d_t a = GRAPHENE_POINT3D_INIT ( 0.f, 1.f, 0.f);
148 graphene_point3d_t b = GRAPHENE_POINT3D_INIT ( 1.f, -1.f, 0.f);
149 graphene_point3d_t c = GRAPHENE_POINT3D_INIT (-1.f, -1.f, 0.f);
150 graphene_triangle_t *t;
151 graphene_point3d_t zero = GRAPHENE_POINT3D_INIT_ZERO;
152 graphene_point3d_t one = GRAPHENE_POINT3D_INIT (1.f, 1.f, 1.f);
153 graphene_point3d_t midpoint;
154
155 t = graphene_triangle_init_from_point3d (graphene_triangle_alloc (), &a, &b, &c);
156 graphene_triangle_get_midpoint (t, &midpoint);
157
158 mutest_expect ("triangle contains (0, 0, 0)",
159 mutest_bool_value (graphene_triangle_contains_point (t, &zero)),
160 mutest_to_be_true,
161 NULL);
162 mutest_expect ("triangle does not contain (1, 1, 1)",
163 mutest_bool_value (graphene_triangle_contains_point (t, &one)),
164 mutest_to_be_false,
165 NULL);
166 mutest_expect ("triangle contains its midpoint",
167 mutest_bool_value (graphene_triangle_contains_point (t, &midpoint)),
168 mutest_to_be_true,
169 NULL);
170
171 graphene_triangle_free (t);
172 }
173
174 static void
triangle_plane(mutest_spec_t * spec)175 triangle_plane (mutest_spec_t *spec)
176 {
177 graphene_triangle_t t;
178 graphene_plane_t p;
179 graphene_point3d_t a, b, c;
180 graphene_vec3_t t_norm, p_norm;
181
182 graphene_triangle_init_from_point3d (&t, NULL, NULL, NULL);
183 graphene_triangle_get_plane (&t, &p);
184 graphene_triangle_get_points (&t, &a, &b, &c);
185
186 mutest_expect ("degenerate triangle point A sits on the triangle's plane",
187 mutest_float_value (graphene_plane_distance (&p, &a)),
188 mutest_to_be_close_to, 0.0, 0.0001,
189 NULL);
190 mutest_expect ("degenerate triangle point B sits on the triangle's plane",
191 mutest_float_value (graphene_plane_distance (&p, &b)),
192 mutest_to_be_close_to, 0.0, 0.0001,
193 NULL);
194 mutest_expect ("degenerate triangle point C sits on the triangle's plane",
195 mutest_float_value (graphene_plane_distance (&p, &c)),
196 mutest_to_be_close_to, 0.0, 0.0001,
197 NULL);
198
199 graphene_triangle_get_normal (&t, &t_norm);
200 graphene_plane_get_normal (&p, &p_norm);
201
202 mutest_expect ("degenerate triangle normal is zero vector",
203 mutest_bool_value (graphene_vec3_equal (&t_norm, graphene_vec3_zero ())),
204 mutest_to_be_true,
205 NULL);
206 mutest_expect ("degenerate triangle normal is equal to the plane normal",
207 mutest_bool_value (graphene_vec3_equal (&t_norm, &p_norm)),
208 mutest_to_be_true,
209 NULL);
210
211 graphene_point3d_init (&a, 0.f, 0.f, 0.f);
212 graphene_point3d_init (&b, 1.f, 0.f, 0.f);
213 graphene_point3d_init (&c, 0.f, 1.f, 0.f);
214 graphene_triangle_init_from_point3d (&t, &a, &b, &c);
215 graphene_triangle_get_plane (&t, &p);
216
217 mutest_expect ("unit triangle point A sits on the triangle's plane",
218 mutest_float_value (graphene_plane_distance (&p, &a)),
219 mutest_to_be_close_to, 0.0, 0.0001,
220 NULL);
221 mutest_expect ("unit triangle point B sits on the triangle's plane",
222 mutest_float_value (graphene_plane_distance (&p, &b)),
223 mutest_to_be_close_to, 0.0, 0.0001,
224 NULL);
225 mutest_expect ("unit triangle point C sits on the triangle's plane",
226 mutest_float_value (graphene_plane_distance (&p, &c)),
227 mutest_to_be_close_to, 0.0, 0.0001,
228 NULL);
229
230 graphene_triangle_get_normal (&t, &t_norm);
231 graphene_plane_get_normal (&p, &p_norm);
232 mutest_expect ("unit triangle normal is equal to the plane normal",
233 mutest_bool_value (graphene_vec3_near (&t_norm, &p_norm, 0.0001f)),
234 mutest_to_be_true,
235 NULL);
236
237 graphene_point3d_init (&a, 2.f, 0.f, 0.f);
238 graphene_point3d_init (&b, 0.f, 0.f, 0.f);
239 graphene_point3d_init (&c, 0.f, 0.f, 2.f);
240 graphene_triangle_init_from_point3d (&t, &a, &b, &c);
241 graphene_triangle_get_plane (&t, &p);
242
243 mutest_expect ("triangle point A sits on the triangle's plane",
244 mutest_float_value (graphene_plane_distance (&p, &a)),
245 mutest_to_be_close_to, 0.0, 0.0001,
246 NULL);
247 mutest_expect ("triangle point B sits on the triangle's plane",
248 mutest_float_value (graphene_plane_distance (&p, &b)),
249 mutest_to_be_close_to, 0.0, 0.0001,
250 NULL);
251 mutest_expect ("triangle point C sits on the triangle's plane",
252 mutest_float_value (graphene_plane_distance (&p, &c)),
253 mutest_to_be_close_to, 0.0, 0.0001,
254 NULL);
255
256 graphene_triangle_get_normal (&t, &t_norm);
257 graphene_vec3_normalize (&t_norm, &t_norm);
258 graphene_plane_get_normal (&p, &p_norm);
259 mutest_expect ("triangle normal is equal to the plane normal",
260 mutest_bool_value (graphene_vec3_near (&t_norm, &p_norm, 0.0001f)),
261 mutest_to_be_true,
262 NULL);
263 }
264
265 static void
triangle_barycoords(mutest_spec_t * spec)266 triangle_barycoords (mutest_spec_t *spec)
267 {
268 graphene_triangle_t t;
269 graphene_point3d_t a, b, c, p;
270 graphene_vec2_t barycoords;
271 graphene_vec2_t check;
272
273 graphene_point3d_init (&a, 0.f, 0.f, 0.f);
274 graphene_point3d_init (&b, 1.f, 0.f, 0.f);
275 graphene_point3d_init (&c, 1.f, 1.f, 0.f);
276 graphene_triangle_init_from_point3d (&t, &a, &b, &c);
277
278 struct {
279 const char *description;
280 graphene_point3d_t point;
281 float uv[2];
282 } border_points[] = {
283 { "unit triangle point A",
284 GRAPHENE_POINT3D_INIT (0.f, 0.f, 0.f),
285 { 0.f, 0.f },
286 },
287 { "unit triangle point B",
288 GRAPHENE_POINT3D_INIT (1.f, 0.f, 0.f),
289 { 0.f, 1.f },
290 },
291 { "unit triangle point C",
292 GRAPHENE_POINT3D_INIT (1.f, 1.f, 0.f),
293 { 1.f, 0.f },
294 },
295 };
296
297 /* Border */
298 for (int i = 0; i < (sizeof (border_points) / sizeof (border_points[0])); i++)
299 {
300 char desc[128];
301
302 snprintf (desc, 128, "barycoords for %s to exist", border_points[i].description);
303
304 mutest_expect (desc,
305 mutest_bool_value (graphene_triangle_get_barycoords (&t, &(border_points[i].point), &barycoords)),
306 mutest_to_be_true,
307 NULL);
308
309 snprintf (desc, 128, "barycoords for %s to be (%g, %g)",
310 border_points[i].description,
311 border_points[i].uv[0],
312 border_points[i].uv[1]);
313 graphene_vec2_init_from_float (&check, border_points[i].uv);
314 mutest_expect (desc,
315 mutest_bool_value (graphene_vec2_near (&barycoords, &check, 0.0001f)),
316 mutest_to_be_true,
317 NULL);
318 }
319
320 /* Inside */
321 struct {
322 graphene_point3d_t point;
323 float uv[2];
324 } inside_points[] = {
325 { GRAPHENE_POINT3D_INIT (0.5f, 0.0f, 0.0f), { 0.0f, 0.5f } },
326 { GRAPHENE_POINT3D_INIT (0.5f, 0.5f, 0.0f), { 0.5f, 0.0f } },
327 { GRAPHENE_POINT3D_INIT (1.0f, 0.5f, 0.0f), { 0.5f, 0.5f } },
328 };
329
330 for (int i = 0; i < (sizeof (inside_points) / sizeof (inside_points[0])); i++)
331 {
332 char desc[128];
333
334 snprintf (desc, 128, "barycoords for inside point (%g, %g, %g) to exists",
335 inside_points[i].point.x,
336 inside_points[i].point.y,
337 inside_points[i].point.z);
338 mutest_expect (desc,
339 mutest_bool_value (graphene_triangle_get_barycoords (&t, &(inside_points[i].point), &barycoords)),
340 mutest_to_be_true,
341 NULL);
342
343 snprintf (desc, 128, "barycoords for inside point (%g, %g, %g) to be (%g, %g)",
344 inside_points[i].point.x,
345 inside_points[i].point.y,
346 inside_points[i].point.z,
347 inside_points[i].uv[0],
348 inside_points[i].uv[1]);
349 graphene_vec2_init_from_float (&check, inside_points[i].uv);
350 mutest_expect (desc,
351 mutest_bool_value (graphene_vec2_near (&barycoords, &check, 0.0001f)),
352 mutest_to_be_true,
353 NULL);
354 }
355
356 /* Outside the triangle, same plane */
357 struct {
358 graphene_point3d_t point;
359 float uv[2];
360 } outside_points[] = {
361 { GRAPHENE_POINT3D_INIT (2.0f, 1.0f, 0.0f), { 1.0f, 1.0f } },
362 { GRAPHENE_POINT3D_INIT (-1.0f, 0.0f, 0.0f), { 0.0f, -1.0f } },
363 };
364
365 for (int i = 0; i < (sizeof (outside_points) / sizeof (outside_points[0])); i++)
366 {
367 char desc[128];
368
369 snprintf (desc, 128, "barycoords for outside point (%g, %g, %g) to exists",
370 outside_points[i].point.x,
371 outside_points[i].point.y,
372 outside_points[i].point.z);
373 mutest_expect (desc,
374 mutest_bool_value (graphene_triangle_get_barycoords (&t, &(outside_points[i].point), &barycoords)),
375 mutest_to_be_true,
376 NULL);
377
378 snprintf (desc, 128, "barycoords for outside point (%g, %g, %g) to be (%g, %g)",
379 outside_points[i].point.x,
380 outside_points[i].point.y,
381 outside_points[i].point.z,
382 outside_points[i].uv[0],
383 outside_points[i].uv[1]);
384 graphene_vec2_init_from_float (&check, outside_points[i].uv);
385 mutest_expect (desc,
386 mutest_bool_value (graphene_vec2_near (&barycoords, &check, 0.0001f)),
387 mutest_to_be_true,
388 NULL);
389 }
390
391 /* Outside the triangle plane */
392 /* FIXME is that normal? */
393 graphene_point3d_init (&p, 0.f, 0.f, 1.f);
394 mutest_expect ("barycoords for point outside triangle plane to exist",
395 mutest_bool_value (graphene_triangle_get_barycoords (&t, &p, &barycoords)),
396 mutest_to_be_true,
397 NULL);
398 graphene_vec2_init (&check, 0.f, 0.f);
399 mutest_expect ("barycoords for point outside triangle plane to be (0, 0)",
400 mutest_bool_value (graphene_vec2_near (&barycoords, &check, 0.0001f)),
401 mutest_to_be_true,
402 NULL);
403 }
404
405 static void
triangle_area(mutest_spec_t * spec)406 triangle_area (mutest_spec_t *spec)
407 {
408 graphene_triangle_t t;
409 graphene_point3d_t a, b, c;
410
411 /* Counterclockwise */
412 graphene_point3d_init (&a, 0.f, 0.f, 0.f);
413 graphene_point3d_init (&b, 1.f, 0.f, 0.f);
414 graphene_point3d_init (&c, 1.f, 1.f, 0.f);
415
416 graphene_triangle_init_from_point3d (&t, &a, &b, &c);
417 mutest_expect ("area of unit triangle constructed counterclockwise to be 0.5",
418 mutest_float_value (graphene_triangle_get_area (&t)),
419 mutest_to_be_close_to, 0.5, 0.0001,
420 NULL);
421
422 /* Clockwise (positive too) */
423 graphene_triangle_init_from_point3d (&t, &a, &c, &b);
424 mutest_expect ("area of unit triangle constructed clockwise to be 0.5",
425 mutest_float_value (graphene_triangle_get_area (&t)),
426 mutest_to_be_close_to, 0.5, 0.0001,
427 NULL);
428 }
429
430 static void
triangle_suite(mutest_suite_t * suite)431 triangle_suite (mutest_suite_t *suite)
432 {
433 mutest_it ("initializes from points", triangle_init_from_point3d);
434 mutest_it ("initializes from vectors", triangle_init_from_vec3);
435 mutest_it ("initialized from float arrays", triangle_init_from_float);
436 mutest_it ("contains points", triangle_contains_point);
437 mutest_it ("defines planes", triangle_plane);
438 mutest_it ("defines barycoords", triangle_barycoords);
439 mutest_it ("defines areas", triangle_area);
440 }
441
442 MUTEST_MAIN (
443 mutest_describe ("graphene_triangle_t", triangle_suite);
444 )
445