Basilisk CFD
Adaptive Cartesian mesh PDE framework
Loading...
Searching...
No Matches
distance.h
Go to the documentation of this file.
1/** @file distance.h
2 */
3/**
4# Computation of a distance field from a discretised curve or surface
5
6The goal is to compute the (signed) minimal distance from a set of
7oriented segments (2d) or triangles (3D) to any point on the
8grid. This signed distance function is also dynamically updated when
9the mesh is refined or coarsened.
10
11The scheme is robust and will give results even for inconsistent
12surface representations (i.e. surfaces with holes, manifold egdes,
13incompatible orientations etc...). Faces do not need to be properly
14connected i.e. the scheme works also for "triangle soups".
15
16We need a few utility function such as computation of the minimal
17distance between a point and a segment or a point and a triangle.
18*/
19
20#include <stdint.h>
21#include "PointTriangle.h"
22
23/**
24The 3D triangulated surfaces are defined using the (binary) [STL
25format](https://en.wikipedia.org/wiki/STL_%28file_format%29) which can
26be exported from most CAD modelling programs. The *input_stl()*
27function reads such a file a returns an array of triplets of vertex
28coordinates defining the triangles. */
29
32{
33 Array * a = array_new();
34 char tag[6];
35
36 if (fgets (tag, 6, fp) != tag) {
37 fprintf (stderr, "input_stl(): error reading tag\n");
38 exit(1);
39 }
40 rewind (fp);
41 if (!strcmp (tag, "solid")) { /* ASCII file */
42 fprintf (stderr, "input_stl(): ASCII STL not implemented yet "
43 "(use binary instead)\n");
44 exit(1);
45 }
46 else { /* binary file */
47 uint32_t nf;
48 char header[80];
49 unsigned i;
50
51 if (fread (header, sizeof (char), 80, fp) != 80) {
52 fprintf (stderr, "Input file is not a valid STL file\n"
53 "stdin: incomplete header\n");
54 exit (1);
55 }
56 if (fread (&nf, sizeof (uint32_t), 1, fp) != 1) {
57 fprintf (stderr, "Input file is not a valid STL file\n"
58 "stdin: missing number of facets\n");
59 exit (1);
60 }
61 i = nf;
62 while (i > 0) {
63 float x = 0, y = 0, z = 0;
64 /* dim: (x == Delta, y == Delta, z == Delta */);
65 unsigned j;
67
68 if (fread (&x, sizeof (float), 1, fp) != 1) {
69 fprintf (stderr, "Input file is not a valid STL file\n"
70 "stdin: missing normal x-coordinate\n");
71 exit (1);
72 }
73 if (fread (&y, sizeof (float), 1, fp) != 1) {
74 fprintf (stderr, "Input file is not a valid STL file\n"
75 "stdin: missing normal y-coordinate\n");
76 exit (1);
77 }
78 if (fread (&z, sizeof (float), 1, fp) != 1) {
79 fprintf (stderr, "Input file is not a valid STL file\n"
80 "stdin: missing normal z-coordinate\n");
81 exit (1);
82 }
83
84 for (j = 0; j < 3; j++) {
85 if (fread (&x, sizeof (float), 1, fp) != 1) {
86 fprintf (stderr, "Input file is not a valid STL file\n"
87 "stdin: missing vertex x-coordinate\n");
88 exit (1);
89 }
90 if (fread (&y, sizeof (float), 1, fp) != 1) {
91 fprintf (stderr, "Input file is not a valid STL file\n"
92 "stdin: missing vertex y-coordinate\n");
93 exit (1);
94 }
95 if (fread (&z, sizeof (float), 1, fp) != 1) {
96 fprintf (stderr, "Input file is not a valid STL file\n"
97 "stdin: missing vertex z-coordinate\n");
98 exit (1);
99 }
100 coord p = {x,y,z};
101 array_append (a, &p, sizeof(coord));
102 }
103
104 if (fread (&attbytecount, sizeof (uint16_t), 1, fp) != 1) {
105 fprintf (stderr, "Input file is not a valid STL file\n"
106 "stdin: missing attribute byte count\n");
107 exit (1);
108 }
109 i--;
110 }
111 }
112 coord p = {nodata};
113 array_append (a, &p, sizeof(coord));
114 return (coord *) array_shrink (a);
115}
116
117/**
118In 2D dimensions, the file format is that used by gnuplot i.e. pairs
119of 2D vertex coordinates separated by blank lines. An easy way to
120create these files is to use a vector graphics editing program
121(e.g. inkscape) and save the curve as an *.eps* file, then convert it
122to gnuplot format using
123
124~~~bash
125pstoedit -f gnuplot -flat 0.1 file.eps file.gnu
126~~~
127
128The function below reads such a file and returns an array of pairs of
129coordinates. */
130
131trace
133{
134 Array * a = array_new();
135 coord p = {0}, last, * la = NULL;
136 while (!feof(fp)) {
137 if (fscanf (fp, "%lf %lf", &p.x, &p.y) == 2) {
138 if (la) {
139 array_append (a, la, sizeof(coord));
140 array_append (a, &p, sizeof(coord));
141 }
142 last = p, la = &last;
143 }
144 else {
145 int c;
146 while ((c = fgetc(fp)) != EOF && c != '\n');
147 la = NULL;
148 }
149 }
150 p.x = nodata;
151 array_append (a, &p, sizeof(coord));
152 return (coord *) array_shrink (a);
153}
154
155/**
156This function computes the coordinates of the bounding box of a set of
157segments or triangles. */
158
160{
161 for (int _d = 0; _d < dimension; _d++)
162 (*min).x = HUGE, (*max).x = - HUGE;
163 while (p->x != nodata) {
164 for (int _d = 0; _d < dimension; _d++) {
165 if ((*p).x < (*min).x)
166 (*min).x = (*p).x;
167 if ((*p).x > (*max).x)
168 (*max).x = (*p).x;
169 }
170 p++;
171 }
172}
173
174/**
175An extra field, holding a pointer to the elements (segments or
176triangles) intersecting the neighborhood of the cell, is associated
177with the distance function. The neighborhood is a sphere centered on
178the cell center and with a diameter \f$3\Delta\f$. */
179
181 scalar surface;
182}
183
184#define double_to_pointer(d) (*((void **) &(d)))
185#define BSIZE 3. // if larger than 1, cells overlap
186
187/**
188To compute the minimal distance and its sign, we need to store
189information on the closest (2 in 2D, 12 in 3D) elements. */
190
191typedef struct {
192 double d2; // minimal distance
193 coord * v; // the element
194 int type; // 0,1,2: vertex, 3: edge, 4: face
195} closest_t;
196
197#if dimension == 2
198# define ND 2
199#else // dimension == 3
200# define ND 12
201static double vertex_angle (coord * p, int type)
202{
203 if (type >= 3) // edge or face
204 return pi;
205 coord * v = p + type, * v1 = p + (type + 1)%3, * v2 = p + (type + 2)%3;
206 coord vv1 = vecdiff (*v1, *v), vv2 = vecdiff(*v2,*v);
208}
209
210static coord face_normal (coord * q, int type)
211{
212 coord ab = vecdiff(*(q+1),*q), ac = vecdiff(*(q+2),*q);
214 double nn = sqrt(vecdot(n,n));
215 assert (nn > 0.);
216 nn = vertex_angle(q, type)/nn;
217 for (int _d = 0; _d < dimension; _d++)
218 n.x *= nn;
219 return n;
220}
221#endif // dimension == 3
222
224{
225 scalar surface = d.surface;
226 Array * a = array_new();
227 coord c = {x,y,z}, closest = {0};
228 closest_t q[ND];
229 for (int i = 0; i < ND; i++)
230 q[i].d2 = HUGE;
231 int nd = 0;
232 double r2 = sq(BSIZE*Delta/2.);
233 bool first = (level == 0);
234 while (*i) {
235 coord * p = *i;
236#if dimension == 2
237 coord r;
238 double s, d2 = PointSegmentDistance (&c, p, p + 1, &r, &s);
239#elif dimension == 3
240 double s, t, d2 = PointTriangleDistance (&c, p, p + 1, p + 2, &s, &t);
241#endif
242 // keep pointers/distances/types of up to ND closest elements
243 for (int i = 0; i < ND; i++)
244 if (d2 < q[i].d2) {
245 for (int j = ND - 1; j > i; j--)
246 q[j] = q[j-1];
247 q[i].d2 = d2, q[i].v = p;
248#if dimension == 2
249 // vertices
250 if (s == 0.)
251 q[i].type = 0;
252 else if (s == 1.)
253 q[i].type = 1;
254 else
255 // edge
256 q[i].type = 3;
257 if (i == 0)
258 closest = r;
259#elif dimension == 3
260 // vertices
261 if (s == 0. && t == 0.)
262 q[i].type = 0;
263 else if (s == 1. && t == 0.)
264 q[i].type = 1;
265 else if (s == 0. && t == 1.)
266 q[i].type = 2;
267 else if (s == 0. || t == 0. || s + t == 1.)
268 // edge
269 q[i].type = 3;
270 else
271 // face
272 q[i].type = 4;
273 if (i == 0)
274 for (int _d = 0; _d < dimension; _d++)
275 closest.x = ((*q[0].v).x*(1. - s - t) + s*(*(q[0].v+1)).x +
276 t*(*(q[0].v+2)).x);
277#endif // dimension == 3
278 if (i >= nd)
279 nd = i + 1;
280 break;
281 }
282 // add elements which are close enough to the local list
283 if (d2 < r2 || first)
284 array_append (a, &p, sizeof(coord *));
285 first = false, i++;
286 }
287 if (a->len) {
288 // set surface[] to list, ended with NULL
289 coord * p = NULL;
290 array_append (a, &p, sizeof(coord *));
291 p = (coord *) array_shrink (a);
292 assert (sizeof(real) >= sizeof(void *));
293 memcpy (&surface[], &p, sizeof(void *));
294
295 int orient;
296#if dimension == 2
297 if (q[0].type == 3)
298 // edge
299 orient = PointSegmentOrientation (&c, q[0].v, q[0].v + 1);
300 else {
301 // vertex
302 if (nd == 1) // a single vertex, cannot find sign
303 // get sign from parent
304 orient = sign(bilinear (point, d));
305 else { // two vertices
306 orient = PointSegmentOrientation (&c, q[0].v, q[0].v + 1);
307 if (orient != PointSegmentOrientation (&c, q[1].v, q[1].v + 1)) {
308 coord n = {0};
309 for (int i = 0; i < 2; i++) {
310#if DEBUG
311 fprintf (stderr, "q %g %g %g %g\n",
312 x, y, q[i].v->x - x, q[i].v->y - y);
313#endif
314 coord ab = vecdiff(*(q[i].v + 1),*q[i].v);
315 double nn = sqrt(vecdot(ab,ab));
316 assert (nn > 0.);
317 n.x -= ab.y/nn, n.y += ab.x/nn;
318 }
319#if DEBUG
320 fprintf (stderr, "vertex %g %g %g %g %g %g %g %g\n",
321 x, y, closest.x - x, closest.y - y,
322 closest.x, closest.y, n.x, n.y);
323#endif
325 orient = sign(vecdot(n,diff));
326 }
327 }
328 }
329#elif dimension == 3
330 if (q[0].type < 3) {
331 coord n = face_normal (q[0].v, q[0].type), diff = vecdiff(closest,c);
332 int nv = 1;
333 orient = sign(vecdot(n,diff));
334 for (int i = 1; i < nd && q[i].type < 3; i++)
335 if (vecdist2 (closest, *(q[i].v + q[i].type)) < sq(1e-6)) {
336 coord n1 = face_normal (q[i].v, q[i].type);
337 for (int _d = 0; _d < dimension; _d++)
338 n.x += n1.x;
339 nv++;
340 if (orient > -2 && sign(vecdot(n1,diff)) != orient)
341 orient = -2;
342 }
343 if (nv < 3) // less than 3 vertices, cannot find sign
344 // get sign from parent
345 orient = sign(bilinear (point, d));
346 else if (orient == -2) {
347 // the vertices do not have the same orientation
348 // get the proper orientation from the pseudo-normal n
349 orient = sign(vecdot(n,diff));
350#if DEBUG
351 fprintf (stderr, "vertex %g %g %g %g %g %g %d %g %g %g %g %g %g %d\n",
352 x, y, z, closest.x - x, closest.y - y, closest.z - z, orient,
353 closest.x, closest.y, closest.z, n.x, n.y, n.z, nv);
354#endif
355 }
356 }
357 else if (q[0].type == 3) {
358 // edge
359 if (nd == 1 || q[1].type != 3) // a single edge, cannot find sign
360 // get sign from parent
361 orient = sign(bilinear (point, d));
362 else { // two edges
363 orient = PointTriangleOrientation (&c, q[0].v, q[0].v+1, q[0].v+2);
364 if (orient !=
365 PointTriangleOrientation (&c, q[1].v, q[1].v+1, q[1].v+2)) {
366 coord n1 = face_normal (q[0].v, 3), n2 = face_normal (q[1].v, 3), n;
367 for (int _d = 0; _d < dimension; _d++)
368 n.x = n1.x + n2.x;
370 orient = sign(vecdot(n,diff));
371#if DEBUG
372 fprintf (stderr, "edge %g %g %g %g %g %g %d %g %g %g %g %g %g\n",
373 x, y, z, closest.x - x, closest.y - y, closest.z - z, orient,
374 closest.x, closest.y, closest.z, n.x, n.y, n.z);
375#endif
376 }
377#if DEBUG
378 else
379 fprintf (stderr, "edge %g %g %g %g %g %g %d\n",
380 x, y, z, closest.x - x, closest.y - y, closest.z - z, orient);
381#endif
382 }
383 }
384 else { // face
385#if DEBUG
386 fprintf (stderr, "face %g %g %g %g %g %g\n",
387 x, y, z, closest.x - x, closest.y - y, closest.z - z);
388#endif
389 orient = PointTriangleOrientation (&c, q[0].v, q[0].v+1, q[0].v+2);
390 }
391#endif // dimension == 3
392 d[] = sqrt (q[0].d2)*orient;
393 }
394 else { // !a->len
395 free (a);
396 surface[] = 0.;
397 if (level > 0)
398 d[] = bilinear (point, d);
399 else
400 d[] = 0.;
401 }
402}
403
404#undef ND
405
407{
408 scalar surface = d.surface;
409 if (surface[] == 0.)
410 for (int _c = 0; _c < 4; _c++) /* foreach_child */ {
411 surface[] = 0.;
412 d[] = bilinear (point, d);
413 }
414 else {
415 coord ** ap = (coord **) double_to_pointer (surface[]);
416 int s = 0;
417 for (int _c = 0; _c < 4; _c++) /* foreach_child */ {
419 s += sign(d[]);
420 }
421
422 /**
423 To increase robustness to inconsistent input, we check whether all
424 children are included within the minimum distance sphere. If this
425 is the case then the children and parent must have the same
426 orientation. We enforce this, using the "average" orientation. */
427
428 if (fabs(d[]) > sqrt(dimension)/4.*Delta) {
429 if (abs(s) != 1 << dimension) {
430 s = sign(s);
431 for (int _c = 0; _c < 4; _c++) /* foreach_child */
432 d[] = s*fabs(d[]);
433 }
434 if (sign(d[]) != sign(s))
435 d[] = - d[];
436 }
437 }
438}
439
441
443 scalar surface = d.surface;
444 for (int _c = 0; _c < 4; _c++) /* foreach_child */
445 free (double_to_pointer (surface[]));
446}
447
448static void delete_distance (scalar d) {
449 scalar surface = d.surface;
450 for (int _l = 0; _l < 0; _l++)
451 free (*((void **)double_to_pointer (surface[])));
452 for (int l = 0; l <= depth(); l++)
453 for (int _l = 0; _l < l; _l++)
454 free (double_to_pointer (surface[]));
455 delete ({surface});
456}
457
458trace
460{
461 scalar surface = d.surface;
462 if (surface.i)
464 surface = {0} /* new scalar */;
465 surface.restriction = no_restriction;
466#if TREE
467 surface.refine = no_restriction; // handled by refine_distance()
470 d.refine = refine_distance;
471 d.coarsen = coarsen_distance;
472#endif
473 d.surface = surface;
474 d.delete = delete_distance;
475 d.restriction = restriction_distance;
476
477 Array * a = array_new();
478 while (p->x != nodata) {
479#if dimension == 3
480 // filter degenerate triangles
481 coord ab = vecdiff(*(p+1),*p), ac = vecdiff(*(p+2),*p);
483 if (vecdot(n,n) > 0.)
484#endif
485 array_append (a, &p, sizeof (coord *));
486 p += dimension;
487 }
488 p = NULL;
489 array_append (a, &p, sizeof (coord *));
490 p = (coord *) array_shrink (a);
491
492 for (int _l = 0; _l < 0, noauto; _l++)
493 update_distance (point, (coord **) p, d);
494 free (p);
495
496 boundary_level ({d}, 0);
497 for (int l = 0; l < depth(); l++) {
498 for (int _l = 0; _l < l; _l++)
500 boundary_level ({d}, l + 1);
501 }
502}
#define vecdist2(a, b)
#define vecdot(a, b)
double PointSegmentDistance(const coord *p, const coord *p0, const coord *p1, coord *segmentClosest, double *segmentParameter)
Returns the squared distance between p and [p0:p1].
double PointTriangleDistance(const coord *P, const coord *P0, const coord *P1, const coord *P2, double *s, double *t)
Returns the squared distance between point P and triangle P0, P1, P2.
#define vecdotproduct(a, b)
int PointSegmentOrientation(const coord *P, const coord *P0, const coord *P1)
#define vecdiff(a, b)
int PointTriangleOrientation(const coord *P, const coord *P0, const coord *P1, const coord *P2)
vector q[]
The primitive variables are the momentum , pressure , density , (face) specific volume ,...
Definition all-mach.h:44
const vector a
Definition all-mach.h:59
Array * array_new()
Definition array.h:10
void * array_shrink(Array *a)
Definition array.h:35
void * array_append(Array *a, void *elem, size_t size)
Definition array.h:24
int min
Definition balance.h:192
#define dimension
Definition bitree.h:3
void(* boundary_level)(scalar *, int l)
define l
double real
Definition cartesian.h:6
free(list1)
int y
Definition common.h:76
int x
Definition common.h:76
int z
Definition common.h:76
#define pi
Definition common.h:4
#define nodata
Definition common.h:7
#define HUGE
Definition common.h:6
static number sq(number x)
Definition common.h:11
static int sign(number x)
Definition common.h:13
define sysmalloc malloc define syscalloc calloc define sysrealloc realloc define sysfree free define systrdup strdup define line line line line type
Definition config.h:571
#define p
Definition tree.h:320
#define assert(a)
Definition config.h:107
Point point
Definition conserving.h:86
else return n
Definition curvature.h:101
double nn
Definition curvature.h:102
#define BSIZE
Definition distance.h:185
trace coord * input_stl(FILE *fp)
The 3D triangulated surfaces are defined using the (binary) STL format which can be exported from mos...
Definition distance.h:31
static void coarsen_distance(Point point, scalar d)
Definition distance.h:442
#define ND
Definition distance.h:198
trace void distance(scalar d, coord *p)
Definition distance.h:459
static void restriction_distance(Point point, scalar d)
Definition distance.h:440
static void delete_distance(scalar d)
Definition distance.h:448
attribute
An extra field, holding a pointer to the elements (segments or triangles) intersecting the neighborho...
Definition distance.h:180
static void refine_distance(Point point, scalar d)
Definition distance.h:406
#define double_to_pointer(d)
Definition distance.h:184
void bounding_box(coord *p, coord *min, coord *max)
This function computes the coordinates of the bounding box of a set of segments or triangles.
Definition distance.h:159
static void update_distance(Point point, coord **i, scalar d)
Definition distance.h:223
trace coord * input_xy(FILE *fp)
In 2D dimensions, the file format is that used by gnuplot i.e.
Definition distance.h:132
vector ab
After the advection and diffusion terms have been added to , we recover the update by adding the new ...
scalar s
Definition embed-tree.h:56
*cs[i, 0, 0] a *[i -1, 0, 0] j
Definition embed.h:88
double v[2]
Definition embed.h:383
double d[2]
Definition embed.h:383
scalar int i
Definition embed.h:74
double t
Definition events.h:24
#define depth()
Definition cartesian.h:14
size_t max
Definition mtrace.h:8
FILE * fp
Definition mtrace.h:7
static void refine_bilinear(Point point, scalar s)
void set_prolongation(scalar s, void(*prolongation)(Point, scalar))
static void no_restriction(Point point, scalar s)
static double bilinear(Point point, scalar s)
int int tag
int int int level
Definition array.h:5
Definition linear.h:21
To compute the minimal distance and its sign, we need to store information on the closest (2 in 2D,...
Definition distance.h:191
double d2
Definition distance.h:192
coord * v
Definition distance.h:193
Definition common.h:78
double x
Definition common.h:79
int i
Definition common.h:44
scalar x
Definition common.h:47
scalar y
Definition common.h:49
scalar c
Definition vof.h:57