/** # Generate a fractal tree Stem parameters: */ coord stem_pos; double stem_rad = 1.; //Base size /** Length-over-radius ratio, and number of fractal levels. The stem is at level = 0. */ double LoR = 20., LoR_spread = 5.; //LoR_i = LoR + noise()*LoR_spread int levels = 1; /** Branch parameters: Number of branches will be rounded to the nearest integer after the spreading randomization. The `new_angle` parameter is the angle relative to the parent branch (100% randomized rotation). */ double fractal_factor = 0.7, fractal_factor_spread = 0.1; double split_branches = 2.1, split_branches_spread = 0.5; //Starting at the end of parent double side_branches = 4.1, side_branches_spread = 0.5; //Starting along the parent double side_start = 0.65, side_start_spread = .3; //Relative along-parent position double new_angle = 40.*pi/180., new_angle_spread = 10.*pi/180.; /** That concludes the inputs. */ typedef struct { coord start, end; double R; int parent; } Branch; unsigned int nb = 0; //Number of segments coord cross (coord a, coord b) { coord new; new.x = a.y*b.z - a.z*b.y; new.y = a.z*b.x - a.x*b.z; new.z = a.x*b.y - a.y*b.x; return new; } void normalize3 (coord * n) { double norm = 0.; foreach_dimension(3) norm += sq(n->x); norm = sqrt(norm); foreach_dimension(3) n->x /= norm; } unsigned int add_bones (Branch * branches, unsigned int p, unsigned int j) { Branch par = branches[p]; int indp = j; int sidenr = (int)round(side_branches + side_branches_spread*noise()) + 0.5; double rndm_ngl = pi*noise(); coord randomvec = {noise(), noise(), noise()}; for (int m = 0; m < sidenr; m++) { j++; branches[j].parent = indp; double sd_strt = side_start + noise()*side_start_spread; foreach_dimension(3) branches[j].start.x = par.start.x + sd_strt*(par.end.x-par.start.x); double frcl_fctr = fractal_factor + fractal_factor_spread*noise(); branches[j].R = par.R*frcl_fctr; double length = (LoR + noise()*LoR_spread)*branches[j].R; double ngl = new_angle + new_angle_spread*noise(); rndm_ngl += 2.*pi/((double)sidenr) + new_angle_spread*noise(); coord vec; foreach_dimension(3) { vec.x = (par.end.x - par.start.x); } /** We need to rotate this vector with angle `ngl` and `rndm_ngl` (pitch and yaw) This was helpfull: tparker, *Rotate a vector by a randomly oriented angle*, URL (version: 2017-09-30): [https://scicomp.stackexchange.com/q/27969]() */ normalize3 (&vec); coord vecx = cross (vec, randomvec); normalize3 (&vecx); coord vecy = cross (vec, vecx); coord vecn; foreach_dimension(3) vecn.x = (sin(ngl)*cos(rndm_ngl)*vecx.x + sin(ngl)*sin(rndm_ngl)*vecy.x + cos(ngl)*vec.x); foreach_dimension(3) branches[j].end.x = branches[j].start.x + vecn.x*length; foreach_dimension(3) branches[j].start.x += vecn.x*par.R; //away from parent center line } int splitnr = (int)round(split_branches + split_branches_spread*noise()) + 0.5; rndm_ngl = pi*noise(); for (int m = 0; m < splitnr; m++) { j++; branches[j].parent = indp; branches[j].start = par.end; double frcl_fctr = fractal_factor + fractal_factor_spread*noise(); branches[j].R = par.R*frcl_fctr; double length = (LoR + noise()*LoR_spread)*branches[j].R; double ngl = new_angle + new_angle_spread*noise(); rndm_ngl += 2*pi/((double)splitnr) + new_angle_spread*noise(); coord vec; foreach_dimension(3) vec.x = (par.end.x - par.start.x); normalize3 (&vec); coord vecx = cross (vec, randomvec); normalize3 (&vecx); coord vecy = cross (vec, vecx); coord vecn; foreach_dimension(3) vecn.x = (sin(ngl)*cos(rndm_ngl)*vecx.x + cos(ngl)*sin(rndm_ngl)*vecy.x + cos(ngl)*vec.x); foreach_dimension(3) branches[j].end.x = branches[j].start.x + vecn.x*length; foreach_dimension(3) branches[j].start.x += vecn.x*par.R; //away from parent center line } return splitnr + sidenr; } Branch * tree_skeleton () { Branch * branches; //array or branches int maxb = (round(split_branches + split_branches_spread) + round(side_branches + side_branches_spread)); unsigned int maxnl = (unsigned int)(pow(maxb, levels + 1) + 3); branches = (Branch*) malloc(sizeof(Branch)*maxnl); // init stem int j = 0; branches[j].start = stem_pos; branches[j].R = stem_rad; coord ey = {0, 1, 0}; double length = (LoR + noise()*LoR_spread)*branches[j].R; foreach_dimension(3) branches[j].end.x = stem_pos.x + length*ey.x; branches[j].parent = 0; nb ++; int pc = 0; for (int l = 0 ; l < levels; l++) { // At this level, all branches are parents while (pc < nb) { int n = add_bones (branches, pc, j); //parent pc to child j pc++; j += n; } nb = j + 1; } return branches; } /** ## Skeleton to volume fractions */ #include "PointTriangle.h" //PointSegmentDistance exists! #include "fractions.h" struct tree_int { Branch * branches; scalar c; face vector fs; scalar J; double smooth; scalar * alist; //For adaptation double * crit; //... int minlevel; int maxlevel; scalar * ulist; int stop; double stopc; }; #define SMOOTHR ((d - branches[j].R) > 0 ?exp (-sq((d - branches[j].R)/(branches[j].R*ti.smooth))) : 1) //#define SMOOTHR ((d - branches[j].R) > 0 ? ((d - branches[j].R)/branches[j].R < pi/2 ? (cos (d - branches[j].R)/branches[j].R) : 0) : 1) trace void tree_interface (struct tree_int ti) { Branch * branches = ti.branches; vertex scalar phi[], phiR[]; scalar J = automatic (ti.J); if (!ti.smooth) { foreach_vertex() { phi[] = HUGE; for (int j = 0; j < nb; j++) { coord pos = (coord){x, y, z}; coord p0 = branches[j].start; coord p1 = branches[j].end; coord Sc; double sP; double d = sqrt(PointSegmentDistance (&pos, &p0, &p1, &Sc, &sP)) - branches[j].R; if (d < phi[]) { phi[] = d; if (ti.J.i) J[] = j; } } } } else { foreach_vertex() { phi[] = 0; double mind = HUGE, phiR = 0; for (int j = 0; j < nb; j++) { coord pos = (coord){x, y, z}; coord p0 = branches[j].start; coord p1 = branches[j].end; coord Sc; double sP; double d = sqrt(PointSegmentDistance (&pos, &p0, &p1, &Sc, &sP)); phi[] += d > 0 ? sq(1/d) : HUGE ; phiR += SMOOTHR*branches[j].R; if (ti.J.i) if (d < mind) { mind = d; J[] = j; } } phi[] = sqrt(1./phi[]) - phiR; } } boundary ({phi}); fractions (phi, ti.c, ti.fs); } /** # Skeleton to fractions with refinement */ void refine_vertex (Point point, scalar s) { for (int i = 0; i < 2; i++) for (int j = 0; j < 1 + (dimension > 1); j++) for (int k = 0; k < 1 + (dimension > 2); k++) { fine (s, 2*i, 2*j, 2*k) = s[i, j, k]; fine (s, 2*i + 1, 2*j , 2*k) = nodata; //1 #if (dimension > 1) fine (s, 2*i , 2*j + 1, 2*k) = nodata; //1 fine (s, 2*i + 1, 2*j + 1, 2*k) = nodata; //2 #if (dimension > 2) fine (s, 2*i , 2*j , 2*k + 1) = nodata; //1 fine (s, 2*i + 1, 2*j , 2*k + 1) = nodata; //2 fine (s, 2*i , 2*j + 1, 2*k + 1) = nodata; //2 fine (s, 2*i +1 , 2*j + 1, 2*k + 1) = nodata; //3 #endif #endif } } void tree_interface_adapt (struct tree_int ti) { Branch * branches = ti.branches; vertex scalar phi[]; phi.refine = refine_vertex; foreach_vertex() phi[] = nodata; scalar * update = NULL; if (!ti.ulist) update = list_copy (all); else { update = list_copy (ti.ulist); update = list_add(update, phi); } int it = 0, computed; do { computed = 0; if (ti.stopc) ti.stop = grid->tn/(int)ti.stopc; if (!ti.smooth) { foreach_vertex(reduction (+:computed)) { if (phi[] == nodata) { computed++; for (int j = 0; j < nb; j++) { coord pos = (coord){x, y, z}; coord p0 = branches[j].start; coord p1 = branches[j].end; coord Sc; double sP; double d = sqrt(PointSegmentDistance (&pos, &p0, &p1, &Sc, &sP)) - branches[j].R; if (d < phi[]) phi[] = d; } } } } else { foreach_vertex(reduction (+:computed)) { if (phi[] == nodata) { computed++; phi[] = 0; double phiR = 0; for (int j = 0; j < nb; j++) { coord pos = (coord){x, y, z}; coord p0 = branches[j].start; coord p1 = branches[j].end; coord Sc; double sP; double d = sqrt(PointSegmentDistance (&pos, &p0, &p1, &Sc, &sP)); phi[] += d > 0 ? sq(1/d) : HUGE; phiR += SMOOTHR*branches[j].R; } phi[] = sqrt(1./phi[]) - phiR; } } } boundary ({phi}); fractions (phi, ti.c, ti.fs); scalar c = ti.c; face vector fss = ti.fs; boundary ({c, fss}); #if EMBED if (fractions_cleanup (ti.c, ti.fs)) boundary ({c, fss}); #endif if (pid() == 0) printf ("# Tree Reconstruction Adapt Iteration %d: %ld cells\n" "# and %d newly computed vertices\n", ++it, grid->tn, computed); } while (adapt_wavelet (ti.alist, ti.crit, ti.maxlevel, ti.minlevel, update).nf > ti.stop); scalar c = ti.c; face vector fss = ti.fs; boundary ({c, fss}); // Last time for R if (ti.J.i) { tree_interface (ti); boundary ({c, fss}); #if EMBED if (fractions_cleanup (ti.c, ti.fs)) boundary ({c, fss}); #endif } }