SAF
Loading...
Searching...
No Matches
saf_utility_geometry.c
Go to the documentation of this file.
1/*
2 * Copyright 2020 Leo McCormack
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
27#include "saf_utilities.h"
28#include "saf_externals.h"
29
31static void getRx
32(
33 float theta_rad,
34 float Rx[3][3]
35)
36{
37 Rx[0][0] = 1.0f;
38 Rx[0][1] = 0.0f;
39 Rx[0][2] = 0.0f;
40 Rx[1][0] = 0.0f;
41 Rx[1][1] = cosf(theta_rad);
42 Rx[1][2] = sinf(theta_rad);
43 Rx[2][0] = 0.0f;
44 Rx[2][1] = -sinf(theta_rad);
45 Rx[2][2] = cosf(theta_rad);
46}
47
49static void getRy
50(
51 float theta_rad,
52 float Ry[3][3]
53)
54{
55 Ry[0][0] = cosf(theta_rad);
56 Ry[0][1] = 0.0f;
57 Ry[0][2] = -sinf(theta_rad);
58 Ry[1][0] = 0.0f;
59 Ry[1][1] = 1.0f;
60 Ry[1][2] = 0.0f;
61 Ry[2][0] = sinf(theta_rad);
62 Ry[2][1] = 0.0f;
63 Ry[2][2] = cosf(theta_rad);
64}
65
67static void getRz
68(
69 float theta_rad,
70 float Rz[3][3]
71)
72{
73 Rz[0][0] = cosf(theta_rad);
74 Rz[0][1] = sinf(theta_rad);
75 Rz[0][2] = 0.0f;
76 Rz[1][0] = -sinf(theta_rad);
77 Rz[1][1] = cosf(theta_rad);
78 Rz[1][2] = 0.0f;
79 Rz[2][0] = 0.0f;
80 Rz[2][1] = 0.0f;
81 Rz[2][2] = 1.0f;
82}
83
84
85/* ========================================================================== */
86/* Basic Geometrical Functions */
87/* ========================================================================== */
88
90(
92 float R[3][3]
93)
94{
95 R[0][0] = 2.0f * (Q->w * Q->w + Q->z * Q->z) - 1.0f;
96 R[0][1] = 2.0f * (Q->z * Q->y - Q->w * Q->x);
97 R[0][2] = 2.0f * (Q->z * Q->x + Q->w * Q->y);
98 R[1][0] = 2.0f * (Q->z * Q->y + Q->w * Q->x);
99 R[1][1] = 2.0f * (Q->w * Q->w + Q->y * Q->y) - 1.0f;
100 R[1][2] = 2.0f * (Q->y * Q->x - Q->w * Q->z);
101 R[2][0] = 2.0f * (Q->z * Q->x - Q->w * Q->y);
102 R[2][1] = 2.0f * (Q->y * Q->x + Q->w * Q->z);
103 R[2][2] = 2.0f * (Q->w * Q->w + Q->x * Q->x) - 1.0f;
104}
105
106/* Adapted from: https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/forum.htm */
108(
109 float R[3][3],
111)
112{
113 Q->w = sqrtf( SAF_MAX( 0.0f, 1.0f + R[0][0] + R[1][1] + R[2][2] ) ) / 2.0f;
114 Q->z = sqrtf( SAF_MAX( 0.0f, 1.0f + R[0][0] - R[1][1] - R[2][2] ) ) / 2.0f;
115 Q->y = sqrtf( SAF_MAX( 0.0f, 1.0f - R[0][0] + R[1][1] - R[2][2] ) ) / 2.0f;
116 Q->x = sqrtf( SAF_MAX( 0.0f, 1.0f - R[0][0] - R[1][1] + R[2][2] ) ) / 2.0f;
117 Q->z = copysignf( Q->z, R[2][1] - R[1][2] );
118 Q->y = copysignf( Q->y, R[0][2] - R[2][0] );
119 Q->x = copysignf( Q->x, R[1][0] - R[0][1] );
120}
121
122/* Adapted from (ISC License): https://github.com/MartinWeigel/Quaternion */
124(
125 float alpha,
126 float beta,
127 float gamma,
128 int degreesFlag,
131)
132{
133 float cy, sy, cr, sr, cp, sp;
134
135 cy = sy = cr = sr = cp = sp = 0.0f;
136 switch(convention){
137 case EULER_ROTATION_Y_CONVENTION: /* fall through*/
138 case EULER_ROTATION_X_CONVENTION: saf_print_error("This convention is not supported"); return;
140 cy = cosf((degreesFlag ? alpha*SAF_PI/180.0f : alpha) * 0.5f); /* x */
141 sy = sinf((degreesFlag ? alpha*SAF_PI/180.0f : alpha) * 0.5f); /* x */
142 cp = cosf((degreesFlag ? beta*SAF_PI/180.0f : beta) * 0.5f); /* y */
143 sp = sinf((degreesFlag ? beta*SAF_PI/180.0f : beta) * 0.5f); /* y */
144 cr = cosf((degreesFlag ? gamma*SAF_PI/180.0f : gamma) * 0.5f); /* z */
145 sr = sinf((degreesFlag ? gamma*SAF_PI/180.0f : gamma) * 0.5f); /* z */
146 break;
148 cy = cosf((degreesFlag ? gamma*SAF_PI/180.0f : gamma) * 0.5f); /* x */
149 sy = sinf((degreesFlag ? gamma*SAF_PI/180.0f : gamma) * 0.5f); /* x */
150 cp = cosf((degreesFlag ? beta*SAF_PI/180.0f : beta) * 0.5f); /* y */
151 sp = sinf((degreesFlag ? beta*SAF_PI/180.0f : beta) * 0.5f); /* y */
152 cr = cosf((degreesFlag ? alpha*SAF_PI/180.0f : alpha) * 0.5f); /* z */
153 sr = sinf((degreesFlag ? alpha*SAF_PI/180.0f : alpha) * 0.5f); /* z */
154 break;
155 }
156 Q->w = cy * cr * cp + sy * sr * sp;
157 Q->x = cy * sr * cp - sy * cr * sp;
158 Q->y = cy * cr * sp + sy * sr * cp;
159 Q->z = sy * cr * cp - cy * sr * sp;
160}
161
162/* Adapted from (ISC License): https://github.com/MartinWeigel/Quaternion */
164(
166 int degreesFlag,
168 float* alpha,
169 float* beta,
170 float* gamma
171)
172{
173 float sinr_cosp, cosr_cosp, sinp, siny_cosp, cosy_cosp;
174
175 sinr_cosp = 2.0f * (Q->w * Q->x + Q->y * Q->z);
176 cosr_cosp = 1.0f - 2.0f * (Q->x * Q->x + Q->y * Q->y);
177 sinp = 2.0f * (Q->w * Q->y - Q->z * Q->x);
178 siny_cosp = 2.0f * (Q->w * Q->z + Q->x * Q->y);
179 cosy_cosp = 1.0f - 2.0f * (Q->y * Q->y + Q->z * Q->z);
180 switch(convention){
181 case EULER_ROTATION_Y_CONVENTION: /* fall through */
182 case EULER_ROTATION_X_CONVENTION: saf_print_error("This convention is not supported"); break;
184 /* Yaw (z-axis rotation) */
185 (*gamma) = atan2f(sinr_cosp, cosr_cosp);
186 /* Pitch (y-axis rotation) */
187 if (fabsf(sinp) >= 1.0f)
188 (*beta) = copysignf(SAF_PI / 2.0f, sinp); /* use 90 degrees if out of range */
189 else
190 (*beta) = asinf(sinp);
191 /* Roll (x-axis rotation) */
192 (*alpha) = atan2f(siny_cosp, cosy_cosp);
193 break;
195 /* Roll (x-axis rotation) */
196 (*alpha) = atan2f(sinr_cosp, cosr_cosp);
197 /* Pitch (y-axis rotation) */
198 if (fabs(sinp) >= 1.0f)
199 (*beta) = copysignf(SAF_PI / 2.0f, sinp); /* use 90 degrees if out of range */
200 else
201 (*beta) = asinf(sinp);
202 /* Yaw (z-axis rotation) */
203 (*gamma) = atan2f(siny_cosp, cosy_cosp);
204 break;
205 }
206 if(degreesFlag){
207 (*alpha) *= 180.0f/SAF_PI;
208 (*beta) *= 180.0f/SAF_PI;
209 (*gamma) *= 180.0f/SAF_PI;
210 }
211}
212
214(
215 float alpha,
216 float beta,
217 float gamma,
218 int degreesFlag,
220 float R[3][3]
221)
222{
223 float R1[3][3], R2[3][3], R3[3][3], Rtmp[3][3];
224
225 switch(convention){
227 getRz(degreesFlag ? alpha*SAF_PI/180.0f : alpha, R1);
228 getRy(degreesFlag ? beta*SAF_PI/180.0f : beta, R2);
229 getRz(degreesFlag ? gamma*SAF_PI/180.0f : gamma, R3);
230 break;
232 getRz(degreesFlag ? alpha*SAF_PI/180.0f : alpha, R1);
233 getRx(degreesFlag ? beta*SAF_PI/180.0f : beta, R2);
234 getRz(degreesFlag ? gamma*SAF_PI/180.0f : gamma, R3);
235 break;
237 getRz(degreesFlag ? alpha*SAF_PI/180.0f : alpha, R1);
238 getRy(degreesFlag ? beta*SAF_PI/180.0f : beta, R2);
239 getRx(degreesFlag ? gamma*SAF_PI/180.0f : gamma, R3);
240 break;
242 getRx(degreesFlag ? alpha*SAF_PI/180.0f : alpha, R1);
243 getRy(degreesFlag ? beta*SAF_PI/180.0f : beta, R2);
244 getRz(degreesFlag ? gamma*SAF_PI/180.0f : gamma, R3);
245 break;
246 }
247 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 3, 3, 3, 1.0f,
248 (float*)R2, 3,
249 (float*)R1, 3, 0.0f,
250 (float*)Rtmp, 3);
251 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 3, 3, 3, 1.0f,
252 (float*)R3, 3,
253 (float*)Rtmp, 3, 0.0f,
254 (float*)R, 3);
255}
256
258(
259 float yaw,
260 float pitch,
261 float roll,
262 int rollPitchYawFLAG,
263 float R[3][3]
264)
265{
266 if(rollPitchYawFLAG)
268 else
270}
271
272void sph2cart(float* sph,
273 int nDirs,
274 int anglesInDegreesFLAG,
275 float* cart)
276{
277 int i;
278 float tmp_rad[2];
279
280 if(anglesInDegreesFLAG){
281 for(i=0; i<nDirs; i++){
282 tmp_rad[0] = sph[i*3] * SAF_PI/180.0f;
283 tmp_rad[1] = sph[i*3+1] * SAF_PI/180.0f;
284 cart[i*3] = sph[i*3+2] * cosf(tmp_rad[1]) * cosf(tmp_rad[0]);
285 cart[i*3+1] = sph[i*3+2] * cosf(tmp_rad[1]) * sinf(tmp_rad[0]);
286 cart[i*3+2] = sph[i*3+2] * sinf(tmp_rad[1]);
287 }
288 }
289 else { /* Angles given in radians */
290 for(i=0; i<nDirs; i++){
291 cart[i*3] = sph[i*3+2] * cosf(sph[i*3+1]) * cosf(sph[i*3]);
292 cart[i*3+1] = sph[i*3+2] * cosf(sph[i*3+1]) * sinf(sph[i*3]);
293 cart[i*3+2] = sph[i*3+2] * sinf(sph[i*3+1]);
294 }
295 }
296}
297
298void cart2sph(float* cart,
299 int nDirs,
300 int anglesInDegreesFLAG,
301 float* sph)
302{
303 int i;
304 float hypotxy;
305
306 for(i=0; i<nDirs; i++){
307 hypotxy = sqrtf(cart[i*3]*cart[i*3] + cart[i*3+1]*cart[i*3+1]);
308 sph[i*3] = atan2f(cart[i*3+1], cart[i*3]);
309 sph[i*3+1] = atan2f(cart[i*3+2], hypotxy);
310 sph[i*3+2] = L2_norm3(&cart[i*3]);
311 }
312
313 /* Return in degrees instead... */
314 if(anglesInDegreesFLAG){
315 for(i=0; i<nDirs; i++){
316 sph[i*3] *= (180.0f/SAF_PI);
317 sph[i*3+1] *= (180.0f/SAF_PI);
318 }
319 }
320}
321
323(
324 float* dirs,
325 int nDirs,
326 int anglesInDegreesFLAG,
327 float* dirs_xyz
328)
329{
330 int i;
331 float tmp_rad[2];
332
333 if(anglesInDegreesFLAG){
334 for(i=0; i<nDirs; i++){
335 tmp_rad[0] = dirs[i*2] * SAF_PI/180.0f;
336 tmp_rad[1] = dirs[i*2+1] * SAF_PI/180.0f;
337 dirs_xyz[i*3] = cosf(tmp_rad[1]) * cosf(tmp_rad[0]);
338 dirs_xyz[i*3+1] = cosf(tmp_rad[1]) * sinf(tmp_rad[0]);
339 dirs_xyz[i*3+2] = sinf(tmp_rad[1]);
340 }
341 }
342 else { /* Angles given in radians */
343 for(i=0; i<nDirs; i++){
344 dirs_xyz[i*3] = cosf(dirs[i*2+1]) * cosf(dirs[i*2]);
345 dirs_xyz[i*3+1] = cosf(dirs[i*2+1]) * sinf(dirs[i*2]);
346 dirs_xyz[i*3+2] = sinf(dirs[i*2+1]);
347 }
348 }
349}
350
352(
353 float* dirs_xyz,
354 int nDirs,
355 int anglesInDegreesFLAG,
356 float* dirs
357)
358{
359 int i;
360
361 for(i=0; i<nDirs; i++){
362 dirs[i*2] = atan2f(dirs_xyz[i*3+1], dirs_xyz[i*3]);
363 dirs[i*2+1] = atan2f(dirs_xyz[i*3+2], sqrtf(dirs_xyz[i*3]*dirs_xyz[i*3] + dirs_xyz[i*3+1]*dirs_xyz[i*3+1]));
364 }
365
366 /* Return in degrees instead... */
367 if(anglesInDegreesFLAG)
368 for(i=0; i<nDirs*2; i++)
369 dirs[i] *= (180.0f/SAF_PI);
370}
371
373(
374 float* dirsElev,
375 int nDirs,
376 int degreesFlag,
377 float* dirsIncl
378)
379{
380 int i;
381 if(dirsIncl!=dirsElev)
382 cblas_scopy(nDirs*2, dirsElev, 1, dirsIncl, 1);
383 if(degreesFlag){
384 for (i=0; i<nDirs; i++)
385 dirsIncl[i*2+1] = 90.f - dirsElev[i*2+1];
386 }
387 else{
388 for (i=0; i<nDirs; i++)
389 dirsIncl[i*2+1] = SAF_PI/2.f - dirsElev[i*2+1];
390 }
391}
392
394(
395 float* dirsIncl,
396 int nDirs,
397 int degreesFlag,
398 float* dirsElev
399)
400{
401 int i;
402 if(dirsIncl!=dirsElev)
403 cblas_scopy(nDirs*2, dirsIncl, 1, dirsElev, 1);
404 if(degreesFlag){
405 for (i=0; i<nDirs; i++)
406 dirsElev[i*2+1] = 90.f - dirsIncl[i*2+1];
407 }
408 else{
409 for (i=0; i<nDirs; i++)
410 dirsElev[i*2+1] = SAF_PI/2.f - dirsIncl[i*2+1];
411 }
412}
413
415(
416 float v[3]
417)
418{
419 return sqrtf(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
420}
421
423(
424 float* v,
425 int lenV
426)
427{
428 int i;
429 float res;
430 res = 0.0f;
431 for(i=0; i<lenV; i++)
432 res += (v[i]*v[i]);
433 return sqrtf(res);
434}
435
437(
438 float* M,
439 int lenX,
440 int lenY
441)
442{
443 int i;
444 float res;
445 float* MMT;
446 MMT = malloc1d(lenX*lenX*sizeof(float));
447 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, lenX, lenX, lenY, 1.0f,
448 M, lenY,
449 M, lenY, 0.0f,
450 MMT, lenX);
451 res = 0.0f;
452 for(i=0; i<lenX; i++)
453 res += MMT[i*lenX+i];
454 free(MMT);
455 return sqrtf(res);
456}
457
459(
460 float a[3],
461 float b[3],
462 float c[3]
463)
464{
465 c[0] = a[1]*b[2]-a[2]*b[1];
466 c[1] = a[2]*b[0]-a[0]*b[2];
467 c[2] = a[0]*b[1]-a[1]*b[0];
468}
469
471(
472 float point[3],
473 float v1[3],
474 float v2[3]
475)
476{
477 float a[3], b[3], cross_a_ab[3];
478 a[0] = v1[0] - v2[0];
479 a[1] = v1[1] - v2[1];
480 a[2] = v1[2] - v2[2];
481 b[0] = point[0] - v2[0];
482 b[1] = point[1] - v2[1];
483 b[2] = point[2] - v2[2];
484 crossProduct3(a, b, cross_a_ab);
485 return L2_norm3(cross_a_ab)/(L2_norm3(a)+2.3e-9f);
486}
487
489(
490 float point_a[3],
491 float point_b[3]
492)
493{
494#if defined(SAF_USE_APPLE_ACCELERATE) && 0
495 float sqdist;
496 vDSP_distancesq((const float*)point_a, 1, (const float*)point_b, 1, &sqdist, 3);
497 return sqrtf(sqdist);
498#else
499 float a_b[3];
500 a_b[0] = point_a[0] - point_b[0];
501 a_b[1] = point_a[1] - point_b[1];
502 a_b[2] = point_a[2] - point_b[2];
503 return L2_norm3(a_b);
504#endif
505}
506
507
508/* ========================================================================== */
509/* Computational Geometry Functions */
510/* ========================================================================== */
511
513(
514 const float* vertices,
515 const int nVert,
516 int** faces,
517 int* nFaces
518)
519{
520 int i;
521 ch_vertex* ch_vertices;
522
523 /* convert vertices to use "ch_vertex" format used by convhull_3d_build() */
524 ch_vertices = malloc1d(nVert*sizeof(ch_vertex));
525 for(i = 0; i < nVert; i++) {
526 ch_vertices[i].z = (CH_FLOAT)vertices[i*3+2];
527 ch_vertices[i].x = (CH_FLOAT)vertices[i*3];
528 ch_vertices[i].y = (CH_FLOAT)vertices[i*3+1];
529 }
530
531 /* build convex hull */
532 saf_assert(*faces == NULL, "nFaces not known yet, and so shouldn't be pre-allocated...");
533 convhull_3d_build(ch_vertices, nVert, faces, NULL, NULL, nFaces);
534
535 /* clean-up */
536 free(ch_vertices);
537}
538
540(
541 const float* points,
542 const int nPoints,
543 const int nd,
544 int** faces,
545 int* nFaces
546)
547{
548 int i, j;
549 CH_FLOAT* ch_points;
550
551 /* convert vertices to use CH_FLOAT used by convhull_nd_build() */
552 ch_points = malloc1d(nPoints*nd*sizeof(CH_FLOAT));
553 for(i = 0; i < nPoints; i++) {
554 for(j=0; j<nd; j++)
555 ch_points[i*nd+j] = (CH_FLOAT)points[i*nd+j];
556 }
557
558 /* build convex hull */
559 saf_assert(*faces == NULL, "nFaces not known yet, and so shouldn't be pre-allocated...");
560 convhull_nd_build(ch_points, nPoints, nd, faces, NULL, NULL, nFaces);
561
562 /* clean-up */
563 free(ch_points);
564}
565
567(
568 const float* points,
569 const int nPoints,
570 const int nd,
571 int** DT,
572 int* nDT
573)
574{
575 int i, j, k, nHullFaces, maxW_idx, nVisible;
576 int* hullfaces;
577 CH_FLOAT w0, w_optimal, w_optimal2;
578 CH_FLOAT* projpoints, *cf, *df, *p0, *p, *visible;
579
580 /* Project the N-dimensional points onto a N+1-dimensional paraboloid */
581 projpoints = malloc1d(nPoints*(nd+1)*sizeof(CH_FLOAT));
582 for(i = 0; i < nPoints; i++) {
583 projpoints[i*(nd+1)+nd] = 0.0;
584 for(j=0; j<nd; j++){
585 projpoints[i*(nd+1)+j] = (CH_FLOAT)points[i*nd+j] + 0.0000001*(CH_FLOAT)rand()/(CH_FLOAT)RAND_MAX;
586 projpoints[i*(nd+1)+nd] += (projpoints[i*(nd+1)+j]*projpoints[i*(nd+1)+j]); /* w vector */
587 }
588 }
589
590 /* The N-dimensional delaunay triangulation requires first computing the convex hull of this N+1-dimensional paraboloid */
591 hullfaces = NULL;
592 cf = df = NULL;
593 convhull_nd_build(projpoints, nPoints, nd+1, &hullfaces, &cf, &df, &nHullFaces);
594
595 /* Find the coordinates of the point with the maximum (N+1 dimension) coordinate (i.e. the w vector) */
596 if(sizeof(CH_FLOAT)==sizeof(double))
597 maxW_idx = (int)cblas_idamax(nPoints, (double*)&projpoints[nd], nd+1);
598 else
599 maxW_idx = (int)cblas_isamax(nPoints, (float*)&projpoints[nd], nd+1);
600 w0 = projpoints[maxW_idx*(nd+1)+nd];
601 p0 = malloc1d(nd*sizeof(CH_FLOAT));
602 for(j=0; j<nd; j++)
603 p0[j] = projpoints[maxW_idx*(nd+1)+j];
604
605 /* Find the point where the plane tangent to the point (p0,w0) on the paraboloid crosses the w axis.
606 * This is the point that can see the entire lower hull. */
607 w_optimal = 0.0;
608 for(j=0; j<nd; j++)
609 w_optimal += (2.0*pow(p0[j], 2.0));
610 w_optimal = w0-w_optimal;
611
612 /* Subtract 1000 times the absolute value of w_optimal to ensure that the point where the tangent plane
613 * crosses the w axis will see all points on the lower hull. This avoids numerical roundoff errors. */
614 w_optimal2=w_optimal-1000.0*fabs(w_optimal);
615
616 /* Set the point where the tangent plane crosses the w axis */
617 p = calloc1d((nd+1),sizeof(CH_FLOAT));
618 p[nd] = w_optimal2;
619
620 /* Find all faces that are visible from this point */
621 visible = malloc1d(nHullFaces*sizeof(CH_FLOAT));
622 if(sizeof(CH_FLOAT)==sizeof(double)){
623 cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nHullFaces, 1, nd+1, 1.0,
624 (double*)cf, nd+1,
625 (double*)p, 1, 0.0,
626 (double*)visible, 1);
627 }
628 else{
629 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nHullFaces, 1, nd+1, 1.0f,
630 (float*)cf, nd+1,
631 (float*)p, 1, 0.0f,
632 (float*)visible, 1);
633 }
634 nVisible = 0;
635 for(j=0; j<nHullFaces; j++){
636 visible[j] += df[j];
637 if(visible[j]>0.0)
638 nVisible++;
639 }
640
641 /* Output */
642 (*nDT) = nVisible;
643 if(nVisible>0){
644 (*DT) = malloc1d(nVisible*(nd+1)*sizeof(int));
645 for(i=0, j=0; i<nHullFaces; i++){
646 if(visible[i]>0.0){
647 for(k=0; k<nd+1; k++)
648 (*DT)[j*(nd+1)+k] = hullfaces[i*(nd+1)+k];
649 j++;
650 }
651 }
652 saf_assert(j==nVisible, "Ugly error");
653 }
654
655 /* clean up */
656 free(projpoints);
657 free(hullfaces);
658 free(cf);
659 free(df);
660 free(p0);
661 free(p);
662 free(visible);
663}
664
666(
667 const float* dirs_deg,
668 const int nDirs,
669 int** faces,
670 int* nFaces,
671 float* vertices
672)
673{
674 int i;
675 float* vertices_tmp;
676 float rcoselev;
677
678 /* sph to unit Cart: */
679 vertices_tmp = malloc1d(nDirs*3*sizeof(float));
680 for(i = 0; i < nDirs; i++) {
681 vertices_tmp[i*3+2] = sinf(dirs_deg[i*2+1]* SAF_PI/180.0f);
682 rcoselev = cosf(dirs_deg[i*2+1]*SAF_PI/180.0f);
683 vertices_tmp[i*3] = rcoselev * cosf(dirs_deg[i*2+0]*SAF_PI/180.0f);
684 vertices_tmp[i*3+1] = rcoselev * sinf(dirs_deg[i*2+0]*SAF_PI/180.0f);
685 }
686
687 /* Delaunay triangulation of a spherical grid is equivalent to the computing
688 * the 3d convex hull */
689 convhull3d(vertices_tmp, nDirs, faces, nFaces);
690
691 /* optionally, also output the vertices */
692 if(vertices!=NULL)
693 memcpy(vertices, vertices_tmp, nDirs*3*sizeof(float));
694
695 /* clean up */
696 free(vertices_tmp);
697}
698
700(
701 int* faces,
702 int nFaces,
703 float* vertices,
704 int nDirs,
705 voronoi_data* voronoi
706)
707{
708 int n, m;
709 int* duplicates;
710 float r_12[3], r_13[3], r_normal[3];
711 float norm;
712
713 /* prep */
714 voronoi->nVert = nFaces;
715 voronoi->nFaces = nDirs;
716 voronoi->vert = (float**)malloc2d(voronoi->nVert, 3, sizeof(float));
717 voronoi->faces = (int**)malloc1d(voronoi->nFaces*sizeof(int*));
718 voronoi->nPointsPerFace = malloc1d(voronoi->nFaces*sizeof(int));
719
720 /* Calculate the voronoi vertices for each triangle, which for the unit
721 * sphere are given by the unit normal vector of the triangle */
722 for(n = 0; n<nFaces; n++){
723 r_12[0] = vertices[faces[n*3+1]*3] - vertices[faces[n*3]*3];
724 r_12[1] = vertices[faces[n*3+1]*3+1] - vertices[faces[n*3]*3+1];
725 r_12[2] = vertices[faces[n*3+1]*3+2] - vertices[faces[n*3]*3+2];
726 r_13[0] = vertices[faces[n*3+2]*3] - vertices[faces[n*3]*3];
727 r_13[1] = vertices[faces[n*3+2]*3+1] - vertices[faces[n*3]*3+1];
728 r_13[2] = vertices[faces[n*3+2]*3+2] - vertices[faces[n*3]*3+2];
729 crossProduct3(r_12, r_13, r_normal);
730
731 norm = 1.0f/L2_norm3(r_normal);
732 utility_svsmul(r_normal, &norm, 3, r_normal);
733 memcpy(voronoi->vert[n], r_normal, 3*sizeof(float));
734 }
735
736 /* Find duplicate vertices if any, due to two triangles sharing the same
737 * circumscribed circle */
738 duplicates = calloc1d(voronoi->nVert, sizeof(int));
739 for(n = 0; n<voronoi->nVert; n++){
740 if (duplicates[n] == 0 ){
741 for (m = 0; m<voronoi->nVert; m++){
742 if (n != m){
743 if (fabsf(voronoi->vert[n][0] - voronoi->vert[m][0]) < 1.0e-5f &&
744 fabsf(voronoi->vert[n][1] - voronoi->vert[m][1]) < 1.0e-5f &&
745 fabsf(voronoi->vert[n][2] - voronoi->vert[m][2]) < 1.0e-5f ){
746 duplicates[m] = n;
747 }
748 }
749 }
750 }
751 }
752
753 int NOT_SORTED;
754 int i, j, k, l, nFaceIdx, currentfaceIdx, currentvertIdx, currentvert, nSorted;
755 int currentface[3];
756 int* faceIdx, *sorted, *tempfacelist;
757 faceIdx = NULL;
758 sorted = NULL;
759 tempfacelist = NULL;
760
761 /* Calculate the voronoi polygons
762 *
763 * find the an ordered sequence of the triangles around each vertex and get
764 * the proper sequence of the voronoi vertices that constitute a polygon */
765 for (n = 0; n<voronoi->nFaces; n++){
766 nFaceIdx=0;
767 for(m=0; m<voronoi->nVert; m++)
768 if(faces[m*3+0]==n || faces[m*3+1]==n || faces[m*3+2]==n)
769 nFaceIdx++;
770 faceIdx = realloc1d(faceIdx, nFaceIdx*sizeof(int));
771
772 /* list of triangles that contain this specific vertex */
773 i=0;
774 for(m=0; m<voronoi->nVert; m++){
775 if(faces[m*3+0]==n || faces[m*3+1]==n || faces[m*3+2]==n){
776 faceIdx[i] = m;
777 i++;
778 }
779 }
780
781 /* Each triangle from the list contain the common vertex and two other
782 * vertices - each triangle has two common vertices with each other. One
783 * (brute) way of sorting the sequence is to pick one triangle, find the
784 * neighbour triangle by finding their common
785 * vertex, move to that triangle and iterate till all the number of
786 * triangles have been checked. */
787 k = 0;
788 currentfaceIdx = faceIdx[k]; /* pick-up one of the triangles in the list */
789 memcpy(currentface, &faces[currentfaceIdx*3], 3*sizeof(int)); /* the triangle's vertex indices */
790
791 /* pick-up one of the vertices that is not the central one */
792 currentvertIdx = -1;
793 for(j=0; j<3; j++){
794 if(currentface[j] != n){
795 currentvertIdx = j;
796 break;
797 }
798 }
799 saf_assert(currentvertIdx!=-1, "Ugly error");
800 currentvert = currentface[currentvertIdx];
801
802 /* Prep */
803 nSorted = 1;
804 sorted = realloc1d(sorted, nSorted*sizeof(int));
805 sorted[0] = faceIdx[k]; /* this is the list that keeps the the ordered triangles */
806 NOT_SORTED = 1;
807 while(NOT_SORTED){
808 /* exclude the current triangle from the temporary list */
809 tempfacelist = realloc1d(tempfacelist, (nFaceIdx-1)*sizeof(int));
810 l=0;
811 for(i=0; i<nFaceIdx; i++){
812 if(faceIdx[i]!=currentfaceIdx){
813 tempfacelist[l] = faceIdx[i];
814 l++;
815 }
816 }
817 saf_assert(l==nFaceIdx-1, "Ugly error");
818
819 for (l = 0; l<nFaceIdx-1; l++){
820 currentfaceIdx = tempfacelist[l];
821 memcpy(currentface, &faces[currentfaceIdx*3], 3*sizeof(int));
822
823 /* if the currentvert exists in the current triangles vertices... */
824 if (currentface[0] == currentvert || currentface[1] == currentvert || currentface[2] == currentvert){
825 /* then it's the neighbour triangle, so store its index */
826 nSorted++;
827 sorted = realloc1d(sorted, nSorted*sizeof(int));
828 sorted[nSorted-1] = currentfaceIdx;
829
830 /* if the sorted list has the length of faceIdx, then we're done */
831 if (nSorted == nFaceIdx){
832 NOT_SORTED = 0;
833 break;
834 }
835
836 /* find the next vertex from current triangle that excludes the central one and the previous one */
837 for(j=0; j<3; j++)
838 if(currentface[j] != n && currentface[j] != currentvert)
839 currentvertIdx = j;
840 currentvert = currentface[currentvertIdx];
841 break;
842 }
843 }
844 }
845
846 /* remove the duplicate vertices from the list */
847 for (i=0; i<nSorted; i++)
848 if (duplicates[sorted[i]] != 0)
849 sorted[i] = duplicates[sorted[i]];
850
851
852 /* Identify unique IDs, and sort them in accending order */
853 int nUnique;
854 int* uniqueIDs;
855 uniqueIDs = NULL;
856 unique_i(sorted, nSorted, NULL, &uniqueIDs, &nUnique);
857 sorti(uniqueIDs, uniqueIDs, NULL, nUnique, 0);
858
859 /* Save */
860 voronoi->faces[n] = malloc1d(nUnique*sizeof(int));
861 for(i=0; i<nUnique; i++)
862 voronoi->faces[n][i] = sorted[uniqueIDs[i]];
863 voronoi->nPointsPerFace[n] = nUnique;
864
865 /* clean-up */
866 free(uniqueIDs);
867 }
868
869 /* clean-up */
870 free(duplicates);
871 free(faceIdx);
872 free(sorted);
873 free(tempfacelist);
874}
875
877(
878 voronoi_data* voronoi,
879 float* areas
880)
881{
882 int i, m, n, N_poly, tmp_i;
883 int* face;
884 float tmp, r_21_norm, r_23_norm;
885 float *theta;
886 float r_01[3], r_02[3], r_2x1[3], r_21[3], r_03[3], r_2x3[3], r_23[3];
887
888 face = NULL;
889 theta = NULL;
890 for(m=0; m<voronoi->nFaces; m++){
891 N_poly = voronoi->nPointsPerFace[m]; /* number of vertices in the polygon */
892 face = realloc1d(face, N_poly*sizeof(int));
893 theta = realloc1d(theta, N_poly*sizeof(float));
894 memcpy(face, voronoi->faces[m], N_poly*sizeof(int)); /* current face */
895
896 for(n=0; n<N_poly; n++){
897 /* find vector between vertex origin and vertices 1 & 2 */
898 memcpy(r_01, voronoi->vert[face[0]], 3*sizeof(float));
899 memcpy(r_02, voronoi->vert[face[1]], 3*sizeof(float));
900
901 /* find normal vector to the great circle of 1 & 2 */
902 crossProduct3(r_02, r_01, r_2x1);
903
904 /* find tangent vector to great circle at 2 */
905 crossProduct3(r_2x1, r_02, r_21);
906
907 /* find vector between vertex origin and vertex 3 */
908 memcpy(r_03, voronoi->vert[face[2]], 3*sizeof(float));
909
910 /* find normal vector to the great circle of 2 & 3 */
911 crossProduct3(r_02, r_03, r_2x3);
912
913 /* find tangent vector to great circle at 2 */
914 crossProduct3(r_2x3, r_02, r_23);
915
916 /* normalise tangent vectors */
917 r_21_norm = 1.0f/L2_norm3(r_21);
918 utility_svsmul(r_21, &r_21_norm, 3, r_21);
919 r_23_norm = 1.0f/L2_norm3(r_23);
920 utility_svsmul(r_23, &r_23_norm, 3, r_23);
921
922 /* get angle between the normals */
923 utility_svvdot(r_21, r_23, 3, &tmp);
924 theta[n] = acosf(tmp);
925
926 /* shift the vertex list by one position and repeat */
927 tmp_i = face[0];
928 for(i=1; i<N_poly; i++)
929 face[i-1] = face[i];
930 face[N_poly-1] = tmp_i;
931 }
932
933 /* compute area of spherical polygon by spherical excess */
934 tmp = 0.0f;
935 for(i=0; i<N_poly; i++)
936 tmp += theta[i];
937 areas[m] = tmp - ((float)N_poly-2.0f)*SAF_PI;
938 }
939 free(face);
940 free(theta);
941}
942
944(
945 float* dirs_deg,
946 int nDirs,
947 int diagFLAG,
948 float* weights
949)
950{
951 int i, nFaces;
952 int* faces;
953 float* vertices, *areas;
954 voronoi_data voronoi;
955
956 /* Perform delaunay triangulation */
957 faces = NULL;
958 vertices = malloc1d(nDirs*3*sizeof(float));
959 sphDelaunay(dirs_deg, nDirs, &faces, &nFaces, vertices);
960
961 /* Get voronoi diagrams */
962 sphVoronoi(faces, nFaces, vertices, nDirs, &voronoi);
963
964 /* Compute areas of spherical voronoi polygons */
965 areas = malloc1d(voronoi.nFaces * sizeof(float));
966 sphVoronoiAreas(&voronoi, areas);
967
968 /* Store weights */
969 if(diagFLAG){
970 /* along the diagonal... */
971 memset(weights, 0, nDirs*nDirs*sizeof(float));
972 for(i=0; i<nDirs; i++)
973 weights[i*nDirs+i] = areas[i];
974 }
975 else
976 memcpy(weights, areas, nDirs*sizeof(float));
977
978 /* clean-up */
979 free(faces);
980 free(vertices);
981 free(areas);
982 for(i=0; i<voronoi.nFaces; i++)
983 free(voronoi.faces[i]);
984 free(voronoi.faces);
985 free(voronoi.vert);
986 free(voronoi.nPointsPerFace);
987}
void convhull_3d_build(ch_vertex *const in_vertices, const int nVert, int **out_faces, CH_FLOAT **out_cf, CH_FLOAT **out_df, int *nOut_faces)
Builds the 3-D convexhull using the quickhull algorithm [1].
void convhull_nd_build(CH_FLOAT *const in_vertices, const int nVert, const int d, int **out_faces, CH_FLOAT **out_cf, CH_FLOAT **out_df, int *nOut_faces)
Builds the N-D convexhull using the quickhull algorithm [1].
#define saf_print_error(message)
Macro to print a error message along with the filename and line number.
#define saf_assert(x, message)
Macro to make an assertion, along with a string explaining its purpose.
#define SAF_PI
pi constant (single precision)
void euler2rotationMatrix(float alpha, float beta, float gamma, int degreesFlag, EULER_ROTATION_CONVENTIONS convention, float R[3][3])
Constructs a 3x3 rotation matrix from the Euler angles.
void quaternion2rotationMatrix(quaternion_data *Q, float R[3][3])
Constructs a 3x3 rotation matrix based on a quaternion.
EULER_ROTATION_CONVENTIONS
Available euler2rotationMatrix() conventions.
#define SAF_MAX(a, b)
Returns the maximum of the two values.
void convhull3d(const float *vertices, const int nVert, int **faces, int *nFaces)
Builds the convex hull of an arrangement of vertices in 3-dimensional space.
void sphVoronoiAreas(voronoi_data *voronoi, float *areas)
Computes the areas of a Voronoi diagram on the unit sphere [sum(areas)=4pi].
void euler2Quaternion(float alpha, float beta, float gamma, int degreesFlag, EULER_ROTATION_CONVENTIONS convention, quaternion_data *Q)
Converts Euler angles to a quaternion.
void unique_i(int *input, int nInputs, int **uniqueVals, int **uniqueInds, int *nUnique)
Finds the unique values (and their indices) of the input vector.
void getVoronoiWeights(float *dirs_deg, int nDirs, int diagFLAG, float *weights)
Computes the integration weights, based on the areas of each face of the corresponding Voronoi diagra...
void quaternion2euler(quaternion_data *Q, int degreesFlag, EULER_ROTATION_CONVENTIONS convention, float *alpha, float *beta, float *gamma)
Converts a quaternion to Euler angles.
void sphIncl2Elev(float *dirsIncl, int nDirs, int degreesFlag, float *dirsElev)
Converts spherical coordinates of unit length from inclination to elevation.
void crossProduct3(float a[3], float b[3], float c[3])
Cross product between two 3-element floating point vectors (c = a x b)
void sph2cart(float *sph, int nDirs, int anglesInDegreesFLAG, float *cart)
Converts spherical coordinates to Cartesian coordinates.
void rotationMatrix2quaternion(float R[3][3], quaternion_data *Q)
Calculates the quaternion corresponding to a 3x3 rotation matrix.
void sphElev2incl(float *dirsElev, int nDirs, int degreesFlag, float *dirsIncl)
Converts spherical coordinates of unit length from elevation to inclination.
float getDistBetweenPointAndLine(float point[3], float v1[3], float v2[3])
Returns the distance between a "point" and an infinite line described by the two points "v1" and "v2"...
float Frob_norm(float *M, int lenX, int lenY)
Returns the Frobenius Norm of a matrix M, of dimensions: lenX x lenY.
void sphVoronoi(int *faces, int nFaces, float *vertices, int nDirs, voronoi_data *voronoi)
Computes the Voronoi diagram for a spherical arrangement of points.
void unitSph2cart(float *dirs, int nDirs, int anglesInDegreesFLAG, float *dirs_xyz)
Converts spherical coordinates to Cartesian coordinates of unit length.
void unitCart2sph(float *dirs_xyz, int nDirs, int anglesInDegreesFLAG, float *dirs)
Converts Cartesian coordinates of unit length to spherical coordinates.
void sorti(int *in_vec, int *out_vec, int *new_idices, int len, int descendFLAG)
Sort a vector of integer values into ascending/decending order (optionally returning the new indices ...
void sphDelaunay(const float *dirs_deg, const int nDirs, int **faces, int *nFaces, float *vertices)
Delaunay triangulation of a spherical arrangement of points.
float getDistBetween2Points(float point_a[3], float point_b[3])
Returns the distance between "point_a" and "point_b".
void yawPitchRoll2Rzyx(float yaw, float pitch, float roll, int rollPitchYawFLAG, float R[3][3])
Constructs a 3x3 rotation matrix from the Euler angles, using the yaw-pitch-roll (zyx) convention.
void convhullnd(const float *points, const int nPoints, const int nd, int **faces, int *nFaces)
Builds the convex hull of an arrangement of points in N-dimensional space.
void cart2sph(float *cart, int nDirs, int anglesInDegreesFLAG, float *sph)
Converts Cartesian coordinates to spherical coordinates.
float L2_norm(float *v, int lenV)
Returns the L2 (Euclidean) norm of an arbitrary length vector.
void utility_svvdot(const float *a, const float *b, const int len, float *c)
Single-precision, vector-vector dot product, i.e.
void delaunaynd(const float *points, const int nPoints, const int nd, int **DT, int *nDT)
Computes the Delaunay triangulation of an arrangement of points in N-dimensional space.
float L2_norm3(float v[3])
Returns the L2 (Euclidean) norm of a 3-element vector.
void utility_svsmul(float *a, const float *s, const int len, float *c)
Single-precision, multiplies each element in vector 'a' with a scalar 's', i.e.
@ EULER_ROTATION_YAW_PITCH_ROLL
yaw-pitch-roll, 'zyx'
@ EULER_ROTATION_ROLL_PITCH_YAW
roll-pitch-yaw, 'xyz'
@ EULER_ROTATION_X_CONVENTION
x-convention, 'zxz'
@ EULER_ROTATION_Y_CONVENTION
y-convention, 'zyz'
void ** malloc2d(size_t dim1, size_t dim2, size_t data_size)
2-D malloc (contiguously allocated, so use free() as usual to deallocate)
Definition md_malloc.c:89
void * malloc1d(size_t dim1_data_size)
1-D malloc (same as malloc, but with error checking)
Definition md_malloc.c:59
void * calloc1d(size_t dim1, size_t data_size)
1-D calloc (same as calloc, but with error checking)
Definition md_malloc.c:69
void * realloc1d(void *ptr, size_t dim1_data_size)
1-D realloc (same as realloc, but with error checking)
Definition md_malloc.c:79
Include header for SAF externals.
Main header for the utilities module (SAF_UTILITIES_MODULE)
static void getRz(float theta_rad, float Rz[3][3])
Helper function for euler2rotationMatrix()
static void getRy(float theta_rad, float Ry[3][3])
Helper function for euler2rotationMatrix()
static void getRx(float theta_rad, float Rx[3][3])
Helper function for euler2rotationMatrix()
vertex structure, used by convhull_3d
Definition convhull_3d.h:62
Quaternion data structure.
float x
X value of the quaternion [-1..1].
float z
Z value of the quaternion [-1..1].
float y
Y value of the quaternion [-1..1].
float w
W value of the quaternion [-1..1].
Data structure for Voronoi diagrams.
float ** vert
Vertices; nVert x 3.
int ** faces
faces; nFaces x nPointsPerFace[i]
int nFaces
Number of faces/polygons.
int * nPointsPerFace
Number of points for each face; nFaces x 1.
int nVert
Number of vertices.