1 /* ----------------------------------------------------------------------
2    SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
3    http://sparta.sandia.gov
4    Steve Plimpton, sjplimp@sandia.gov, Michael Gallis, magalli@sandia.gov
5    Sandia National Laboratories
6 
7    Copyright (2014) Sandia Corporation.  Under the terms of Contract
8    DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
9    certain rights in this software.  This software is distributed under
10    the GNU General Public License.
11 
12    See the README file in the top-level SPARTA directory.
13 ------------------------------------------------------------------------- */
14 
15 #include "math.h"
16 #include "string.h"
17 #include "cut3d.h"
18 #include "cut2d.h"
19 #include "surf.h"
20 #include "domain.h"
21 #include "grid.h"
22 #include "comm.h"
23 #include "math_extra.h"
24 #include "memory.h"
25 #include "error.h"
26 
27 using namespace SPARTA_NS;
28 
29 enum{UNKNOWN,OUTSIDE,INSIDE,OVERLAP};     // several files
30 enum{CTRI,CTRIFACE,FACEPGON,FACE};
31 enum{EXTERIOR,INTERIOR,BORDER};
32 enum{ENTRY,EXIT,TWO,CORNER};              // same as Cut2d
33 
34 #define EPSEDGE 1.0e-9    // minimum edge length (fraction of cell size)
35 #define SHRINK 1.0e-8     // shrink grid cell by this fraction when split fails
36 
37 // cell ID for 2d or 3d cell
38 
39 //#define VERBOSE
40 //#define VERBOSE_ID 61275
41 
42 /* ---------------------------------------------------------------------- */
43 
Cut3d(SPARTA * sparta)44 Cut3d::Cut3d(SPARTA *sparta) : Pointers(sparta)
45 {
46   implicit = surf->implicit;
47 
48   ntiny = 0;
49   nshrink = 0;
50 
51   cut2d = new Cut2d(sparta,0);
52   memory->create(path1,12,3,"cut3d:path1");
53   memory->create(path2,12,3,"cut3d:path2");
54 }
55 
56 /* ---------------------------------------------------------------------- */
57 
~Cut3d()58 Cut3d::~Cut3d()
59 {
60   delete cut2d;
61   memory->destroy(path1);
62   memory->destroy(path2);
63 }
64 
65 /* ----------------------------------------------------------------------
66    compute intersections of a grid cell with all surfs
67    csurfs = indices into global surf list
68    return nsurf = # of surfs
69    return -1 if nsurf > max
70 ------------------------------------------------------------------------- */
71 
surf2grid(cellint id_caller,double * lo_caller,double * hi_caller,surfint * surfs_caller,int max)72 int Cut3d::surf2grid(cellint id_caller, double *lo_caller, double *hi_caller,
73                      surfint *surfs_caller, int max)
74 {
75   id = id_caller;
76   lo[0] = lo_caller[0]; lo[1] = lo_caller[1]; lo[2] = lo_caller[2];
77   hi[0] = hi_caller[0]; hi[1] = hi_caller[1]; hi[2] = hi_caller[2];
78   surfs = surfs_caller;
79 
80   Surf::Tri *tris = surf->tris;
81   int ntotal = surf->nsurf;
82 
83   double value;
84   double *x1,*x2,*x3;
85 
86   nsurf = 0;
87   for (int m = 0; m < ntotal; m++) {
88     x1 = tris[m].p1;
89     x2 = tris[m].p2;
90     x3 = tris[m].p3;
91 
92     value = MAX(x1[0],x2[0]);
93     if (MAX(value,x3[0]) < lo[0]) continue;
94     value = MIN(x1[0],x2[0]);
95     if (MIN(value,x3[0]) > hi[0]) continue;
96 
97     value = MAX(x1[1],x2[1]);
98     if (MAX(value,x3[1]) < lo[1]) continue;
99     value = MIN(x1[1],x2[1]);
100     if (MIN(value,x3[1]) > hi[1]) continue;
101 
102     value = MAX(x1[2],x2[2]);
103     if (MAX(value,x3[2]) < lo[2]) continue;
104     value = MIN(x1[2],x2[2]);
105     if (MIN(value,x3[2]) > hi[2]) continue;
106 
107     // 3 versions of this:
108     // 1 = tri_hex_intersect with geometric line_tri_intersect,
109     //     devel/cut3d.old1.py
110     // 2 = tri_hex_intersect with tetvol line_tri_intersect, here
111     // 3 = Sutherland-Hodgman clip algorithm, here and in devel/cut3d.py
112 
113     //if (tri_hex_intersect(x1,x2,x3,tris[m].norm)) {
114     //  if (nsurf == max) return -1;
115     //  surfs[nsurf++] = m;
116     // }
117 
118     if (clip(x1,x2,x3)) {
119       if (nsurf < max) surfs[nsurf] = m;
120       nsurf++;
121     }
122   }
123 
124   return nsurf;
125 }
126 
127 /* ----------------------------------------------------------------------
128    compute intersections of a grid cell with a provided list of surfs
129    csurfs = indices into global surf list
130    nlist, list = vector of surf indices of length nlist
131    return nsurf = # of surfs
132    return -1 if nsurf > max
133    called by AdaptGrid via Grid::surf2grid_one
134 ------------------------------------------------------------------------- */
135 
surf2grid_list(cellint id_caller,double * lo_caller,double * hi_caller,int nlist,surfint * list,surfint * surfs_caller,int max)136 int Cut3d::surf2grid_list(cellint id_caller,
137                           double *lo_caller, double *hi_caller,
138                           int nlist, surfint *list,
139                           surfint *surfs_caller, int max)
140 {
141   id = id_caller;
142   lo[0] = lo_caller[0]; lo[1] = lo_caller[1]; lo[2] = lo_caller[2];
143   hi[0] = hi_caller[0]; hi[1] = hi_caller[1]; hi[2] = hi_caller[2];
144   surfs = surfs_caller;
145 
146   Surf::Tri *tris = surf->tris;
147 
148   int m;
149   double value;
150   double *x1,*x2,*x3;
151 
152   nsurf = 0;
153   for (int i = 0; i < nlist; i++) {
154     m = list[i];
155     x1 = tris[m].p1;
156     x2 = tris[m].p2;
157     x3 = tris[m].p3;
158 
159     value = MAX(x1[0],x2[0]);
160     if (MAX(value,x3[0]) < lo[0]) continue;
161     value = MIN(x1[0],x2[0]);
162     if (MIN(value,x3[0]) > hi[0]) continue;
163 
164     value = MAX(x1[1],x2[1]);
165     if (MAX(value,x3[1]) < lo[1]) continue;
166     value = MIN(x1[1],x2[1]);
167     if (MIN(value,x3[1]) > hi[1]) continue;
168 
169     value = MAX(x1[2],x2[2]);
170     if (MAX(value,x3[2]) < lo[2]) continue;
171     value = MIN(x1[2],x2[2]);
172     if (MIN(value,x3[2]) > hi[2]) continue;
173 
174     // 3 versions of this:
175     // 1 = tri_hex_intersect with geometric line_tri_intersect,
176     //     devel/cut3d.old1.py
177     // 2 = tri_hex_intersect with tetvol line_tri_intersect, here
178     // 3 = Sutherland-Hodgman clip algorithm, here and in devel/cut3d.py
179 
180     //if (tri_hex_intersect(x1,x2,x3,tris[m].norm)) {
181     //  if (nsurf == max) return -1;
182     //  surfs[nsurf++] = m;
183     // }
184 
185     if (clip(x1,x2,x3)) {
186       if (nsurf < max) surfs[nsurf] = m;
187       nsurf++;
188     }
189   }
190 
191   return nsurf;
192 }
193 
194 /* ----------------------------------------------------------------------
195    compute intersections of a grid cell with a single surf
196    p012 = corner pts of surf
197    lo,hi = grid cell corner points
198    return 1 if intersects, 0 if not
199    called by Grid::surf2grid2
200 ------------------------------------------------------------------------- */
201 
surf2grid_one(double * p0,double * p1,double * p2,double * lo_caller,double * hi_caller)202 int Cut3d::surf2grid_one(double *p0, double *p1, double *p2,
203                          double *lo_caller, double *hi_caller)
204 {
205   lo[0] = lo_caller[0]; lo[1] = lo_caller[1]; lo[2] = lo_caller[2];
206   hi[0] = hi_caller[0]; hi[1] = hi_caller[1]; hi[2] = hi_caller[2];
207   return clip(p0,p1,p2);
208 }
209 
210 /* ----------------------------------------------------------------------
211    Sutherland-Hodgman clipping algorithm
212    don't need to delete duplicate points since touching counts as intersection
213 ------------------------------------------------------------------------- */
214 
clip(double * p0,double * p1,double * p2)215 int Cut3d::clip(double *p0, double *p1, double *p2)
216 {
217   int i,npath,nnew;
218   double value;
219   double *s,*e;
220   double **path,**newpath;
221 
222   // initial path = tri vertices
223 
224   nnew = 3;
225   memcpy(path1[0],p0,3*sizeof(double));
226   memcpy(path1[1],p1,3*sizeof(double));
227   memcpy(path1[2],p2,3*sizeof(double));
228 
229   // intersect if any of tri vertices is within grid cell
230 
231   if (p0[0] >= lo[0] && p0[0] <= hi[0] &&
232       p0[1] >= lo[1] && p0[1] <= hi[1] &&
233       p0[2] >= lo[2] && p0[2] <= hi[2] &&
234       p1[0] >= lo[0] && p1[0] <= hi[0] &&
235       p1[1] >= lo[1] && p1[1] <= hi[1] &&
236       p1[2] >= lo[2] && p1[2] <= hi[2] &&
237       p2[0] >= lo[0] && p2[0] <= hi[0] &&
238       p2[1] >= lo[1] && p2[1] <= hi[1] &&
239       p2[2] >= lo[2] && p2[2] <= hi[2]) return 1;
240 
241   // clip tri against each of 6 grid face planes
242 
243   for (int dim = 0; dim < 3; dim++) {
244     path = path1;
245     newpath = path2;
246     npath = nnew;
247     nnew = 0;
248 
249     value = lo[dim];
250     s = path[npath-1];
251     for (i = 0; i < npath; i++) {
252       e = path[i];
253       if (e[dim] >= value) {
254         if (s[dim] < value) between(s,e,dim,value,newpath[nnew++]);
255         memcpy(newpath[nnew++],e,3*sizeof(double));
256       } else if (s[dim] >= value) between(e,s,dim,value,newpath[nnew++]);
257       s = e;
258     }
259     if (!nnew) return 0;
260 
261     path = path2;
262     newpath = path1;
263     npath = nnew;
264     nnew = 0;
265 
266     value = hi[dim];
267     s = path[npath-1];
268     for (i = 0; i < npath; i++) {
269       e = path[i];
270       if (e[dim] <= value) {
271         if (s[dim] > value) between(s,e,dim,value,newpath[nnew++]);
272         memcpy(newpath[nnew++],e,3*sizeof(double));
273       } else if (s[dim] <= value) between(e,s,dim,value,newpath[nnew++]);
274       s = e;
275     }
276     if (!nnew) return 0;
277   }
278 
279   return nnew;
280 }
281 
282 /* ----------------------------------------------------------------------
283    clip triangle P0 P1 P2 against cell with corners CLO,CHI
284    tri may or may not intersect cell (due to rounding)
285    return # of clipped points, can be 0,1,2,3 up to 8 (I think)
286    return clipped points in cpath as series of x,y,z triplets
287    called externally, depends on no class variables
288    duplicate points in cpath are deleted
289    uses Sutherland-Hodgman clipping algorithm
290 ------------------------------------------------------------------------- */
291 
clip_external(double * p0,double * p1,double * p2,double * clo,double * chi,double * cpath)292 int Cut3d::clip_external(double *p0, double *p1, double *p2,
293                          double *clo, double *chi, double *cpath)
294 {
295   int i,npath,nnew;
296   double value;
297   double *s,*e;
298   double **path,**newpath;
299 
300   // initial path = tri vertices
301 
302   nnew = 3;
303   memcpy(path1[0],p0,3*sizeof(double));
304   memcpy(path1[1],p1,3*sizeof(double));
305   memcpy(path1[2],p2,3*sizeof(double));
306 
307   // clip tri against each of 6 grid face planes
308 
309   for (int dim = 0; dim < 3; dim++) {
310     path = path1;
311     newpath = path2;
312     npath = nnew;
313     nnew = 0;
314 
315     value = clo[dim];
316     s = path[npath-1];
317     for (i = 0; i < npath; i++) {
318       e = path[i];
319       if (e[dim] >= value) {
320         if (s[dim] < value) between(s,e,dim,value,newpath[nnew++]);
321         memcpy(newpath[nnew++],e,3*sizeof(double));
322       } else if (s[dim] >= value) between(e,s,dim,value,newpath[nnew++]);
323       s = e;
324     }
325     if (!nnew) return 0;
326 
327     path = path2;
328     newpath = path1;
329     npath = nnew;
330     nnew = 0;
331 
332     value = chi[dim];
333     s = path[npath-1];
334     for (i = 0; i < npath; i++) {
335       e = path[i];
336       if (e[dim] <= value) {
337         if (s[dim] > value) between(s,e,dim,value,newpath[nnew++]);
338         memcpy(newpath[nnew++],e,3*sizeof(double));
339       } else if (s[dim] <= value) between(e,s,dim,value,newpath[nnew++]);
340       s = e;
341     }
342     if (!nnew) return 0;
343   }
344 
345   // copy path points to cpath
346   // delete any duplicate points while copying
347   // inner-loop check is to not add a point that duplicates previous point
348   // post-loop check is for duplicate due to 1st point = last point
349 
350   int m = 0;
351   int n = nnew;
352   nnew = 0;
353   for (i = 0; i < n; i++) {
354     if (m) {
355       if (path1[i][0] == cpath[m-3] && path1[i][1] == cpath[m-2] &&
356           path1[i][2] == cpath[m-1]) continue;
357     }
358     cpath[m++] = path1[i][0];
359     cpath[m++] = path1[i][1];
360     cpath[m++] = path1[i][2];
361     nnew++;
362   }
363   if (nnew > 1)
364     if (cpath[0] == cpath[m-3] && cpath[1] == cpath[m-2] &&
365         cpath[2] == cpath[m-1]) nnew--;
366 
367   return nnew;
368 }
369 
370 /* ----------------------------------------------------------------------
371    calculate cut volume of a grid cell that contains nsurf tris
372    also calculate if cell is split into distinct sub-volumes by tris
373    return nsplit = # of splits, 1 for no split
374    return vols = ptr to vector of vols = one vol per split
375      if nsplit = 1, cut vol
376      if nsplit > 1, one vol per split cell
377    return corners = UNKNOWN/INSIDE/OUTSIDE for each of 8 corner pts
378    if nsplit > 1, also return:
379      surfmap = which sub-cell (0 to Nsurfs-1) each surf is in
380              = -1 if not in any sub-cell, discarded by add_tris
381      xsplit = coords of a point in one of the split cells
382      xsub = which sub-cell (0 to Nsplit-1) xsplit is in
383    work is done by split_try()
384    call it once more with shrunk grid cell if first attempt fails
385 ------------------------------------------------------------------------- */
386 
split(cellint id_caller,double * lo_caller,double * hi_caller,int nsurf_caller,surfint * surfs_caller,double * & vols_caller,int * surfmap,int * corners,int & xsub,double * xsplit)387 int Cut3d::split(cellint id_caller, double *lo_caller, double *hi_caller,
388                  int nsurf_caller, surfint *surfs_caller,
389                  double *&vols_caller, int *surfmap,
390                  int *corners, int &xsub, double *xsplit)
391 {
392   lo[0] = lo_caller[0]; lo[1] = lo_caller[1]; lo[2] = lo_caller[2];
393   hi[0] = hi_caller[0]; hi[1] = hi_caller[1]; hi[2] = hi_caller[2];
394 
395   // perform split with full-size grid cell
396 
397   int nsplit;
398   int errflag =
399     split_try(id_caller,nsurf_caller,surfs_caller,vols_caller,surfmap,
400               corners,xsub,xsplit,nsplit);
401 
402   // error return
403   // try again after shrinking grid cell by SHRINK factor
404   // this gets rid of pesky errors due to tri pts/edges on cell faces
405 
406   if (errflag) {
407     nshrink++;
408 
409     double newlo = lo[0] + SHRINK*(hi[0]-lo[0]);
410     double newhi = hi[0] - SHRINK*(hi[0]-lo[0]);
411     lo[0] = newlo;
412     hi[0] = newhi;
413 
414     newlo = lo[1] + SHRINK*(hi[1]-lo[1]);
415     newhi = hi[1] - SHRINK*(hi[1]-lo[1]);
416     lo[1] = newlo;
417     hi[1] = newhi;
418 
419     newlo = lo[2] + SHRINK*(hi[2]-lo[2]);
420     newhi = hi[2] - SHRINK*(hi[2]-lo[2]);
421     lo[2] = newlo;
422     hi[2] = newhi;
423 
424     errflag =
425       split_try(id_caller,nsurf_caller,surfs_caller,vols_caller,surfmap,
426                 corners,xsub,xsplit,nsplit);
427   }
428 
429   // could not perform cut/split -> fatal error
430   // print info about cell and final error message
431   // NOTE: could unshrink cell first
432 
433   if (errflag) {
434     failed_cell();
435     split_error(errflag);
436   }
437 
438   return nsplit;
439 }
440 
441 /* ----------------------------------------------------------------------
442    attempt to split cell
443    return 0 if successful, otherwise return an error flag
444    called multiple times by split() with slightly different grid cell sizes
445 ------------------------------------------------------------------------- */
446 
split_try(cellint id_caller,int nsurf_caller,surfint * surfs_caller,double * & vols_caller,int * surfmap,int * corners,int & xsub,double * xsplit,int & nsplit)447 int Cut3d::split_try(cellint id_caller,
448                      int nsurf_caller, surfint *surfs_caller,
449                      double *&vols_caller, int *surfmap,
450                      int *corners, int &xsub, double *xsplit, int &nsplit)
451 {
452   id = id_caller;
453   nsurf = nsurf_caller;
454   surfs = surfs_caller;
455 
456   int errflag = add_tris();
457   if (errflag) return errflag;
458 
459 #ifdef VERBOSE
460   if (id == VERBOSE_ID) print_bpg("BPG after added tris");
461 #endif
462 
463   clip_tris();
464   clip_adjust();
465 
466 #ifdef VERBOSE
467   if (id == VERBOSE_ID) print_bpg("BPG after clipped tris");
468 #endif
469 
470   // all triangles just touched cell surface
471   // mark corner points based on grazecount or touchmark value
472   // return vol = 0.0 for UNKNOWN/INSIDE, full cell vol for OUTSIDE
473   // vol is changed in Grid::set_inout() if OVERLAP cell corners are marked
474 
475   if (empty) {
476     int mark = UNKNOWN;
477     if (grazecount || touchmark == INSIDE) mark = INSIDE;
478     else if (touchmark == OUTSIDE) mark = OUTSIDE;
479     corners[0] = corners[1] = corners[2] = corners[3] =
480       corners[4] = corners[5] = corners[6] = corners[7] = mark;
481 
482     double vol = 0.0;
483     if (mark == OUTSIDE) vol = (hi[0]-lo[0]) * (hi[1]-lo[1]) * (hi[2]-lo[2]);
484 
485     vols.grow(1);
486     vols[0] = vol;
487     vols_caller = &vols[0];
488     nsplit = 1;
489     return 0;
490   }
491 
492   ctri_volume();
493   errflag = edge2face();
494   if (errflag) return errflag;
495 
496   double lo2d[2],hi2d[2];
497 
498   for (int iface = 0; iface < 6; iface++) {
499     if (facelist[iface].n) {
500       face_from_cell(iface,lo2d,hi2d);
501       edge2clines(iface);
502       errflag = cut2d->split_face(id,iface,lo2d,hi2d);
503       if (errflag) return errflag;
504       errflag = add_face_pgons(iface);
505       if (errflag) return errflag;
506     } else {
507       face_from_cell(iface,lo2d,hi2d);
508       errflag = add_face(iface,lo2d,hi2d);
509       if (errflag) return errflag;
510     }
511   }
512 
513   remove_faces();
514 
515 #ifdef VERBOSE
516   if (id == VERBOSE_ID) print_bpg("BPG after faces");
517 #endif
518 
519   errflag = check();
520   if (errflag) return errflag;
521 
522   walk();
523 
524 #ifdef VERBOSE
525   if (id == VERBOSE_ID) print_loops();
526 #endif
527 
528   errflag = loop2ph();
529 
530   // loop2ph detected no positive-volume loops, cell is inside the surf
531 
532   if (errflag == 4) {
533     corners[0] = corners[1] = corners[2] = corners[3] =
534       corners[4] = corners[5] = corners[6] = corners[7] = INSIDE;
535     double volume = 0.0;
536     vols.grow(1);
537     vols[0] = volume;
538     vols_caller = &vols[0];
539     nsplit = 1;
540     return 0;
541   }
542 
543   // other error returns from loop2ph
544 
545   if (errflag) return errflag;
546 
547   // if multiple splits, find a split point
548 
549   nsplit = phs.n;
550   if (nsplit > 1) {
551     create_surfmap(surfmap);
552     if (implicit) errflag = split_point_implicit(surfmap,xsplit,xsub);
553     else errflag = split_point_explicit(surfmap,xsplit,xsub);
554   }
555   if (errflag) return errflag;
556 
557   // successful cut/split
558   // set corners = OUTSIDE if corner pt is in list of edge points
559   // else set corners = INSIDE
560 
561   int icorner;
562   double *p1,*p2;
563 
564   corners[0] = corners[1] = corners[2] = corners[3] =
565     corners[4] = corners[5] = corners[6] = corners[7] = INSIDE;
566 
567   int nedge = edges.n;
568   for (int iedge = 0; iedge < nedge; iedge++) {
569     if (!edges[iedge].active) continue;
570     p1 = edges[iedge].p1;
571     p2 = edges[iedge].p2;
572     icorner = corner(p1);
573     if (icorner >= 0) corners[icorner] = OUTSIDE;
574     icorner = corner(p2);
575     if (icorner >= 0) corners[icorner] = OUTSIDE;
576   }
577 
578   // store volumes in vector so can return ptr to it
579 
580   vols.grow(nsplit);
581   for (int i = 0; i < nsplit; i++) vols[i] = phs[i].volume;
582   vols_caller = &vols[0];
583 
584   // successful exit
585 
586   return 0;
587 }
588 
589 /* ----------------------------------------------------------------------
590    2-letter prefix is which method encountered error
591 ------------------------------------------------------------------------- */
592 
split_error(int errflag)593 void Cut3d::split_error(int errflag)
594 {
595   if (errflag == 1)
596     error->one(FLERR,"FE: Found edge in same direction");
597   if (errflag == 2)
598     error->one(FLERR,"EF: Singlet BPG edge not on cell face");
599   if (errflag == 3)
600     error->one(FLERR,"EF: BPG edge on more than 2 faces");
601   if (errflag == 4)
602     error->one(FLERR,"LP: No positive volumes in cell");
603   if (errflag == 5)
604     error->one(FLERR,"LP: More than one positive volume with "
605                "a negative volume");
606   if (errflag == 6)
607     error->one(FLERR,"LP: Single volume is negative, inverse donut");
608   if (errflag == 7)
609     error->one(FLERR,"SP: Could not find split point in split cell");
610 
611   if (errflag == 11)
612     error->one(FLERR,"CH: Vertex has less than 3 edges");
613   if (errflag == 12)
614     error->one(FLERR,"CH: Vertex contains invalid edge");
615   if (errflag == 13)
616     error->one(FLERR,"CH: Vertex contains edge that doesn't point to it");
617   if (errflag == 14)
618     error->one(FLERR,"CH: Vertex contains duplicate edge");
619   if (errflag == 15)
620     error->one(FLERR,"CH: Vertex pointers to last edge are invalid");
621   if (errflag == 16)
622     error->one(FLERR,"CH: Edge not part of 2 vertices");
623   if (errflag == 17)
624     error->one(FLERR,"CH: Edge part of same vertex twice");
625   if (errflag == 18)
626     error->one(FLERR,"CH: Edge part of invalid vertex");
627   if (errflag == 19)
628     error->one(FLERR,"CH: Edge part of invalid vertex");
629 
630   if (errflag == 21)
631     error->one(FLERR,"WB: Point appears first in more than one CLINE");
632   if (errflag == 22)
633     error->one(FLERR,"WB: Point appears last in more than one CLINE");
634   if (errflag == 23)
635     error->one(FLERR,"WB: Singlet CLINES point not on cell border");
636   if (errflag == 24)
637     error->one(FLERR,"LP: No positive areas in cell");
638   if (errflag == 25)
639     error->one(FLERR,"LP: More than one positive area with a negative area");
640   if (errflag == 26)
641     error->one(FLERR,"LP: Single area is negative, inverse donut");
642 }
643 
644 /* ----------------------------------------------------------------------
645    add each triangle as vertex and edges to BPG
646    add full edge even if outside cell, clipping comes later
647    skip transparent surfs
648 ------------------------------------------------------------------------- */
649 
add_tris()650 int Cut3d::add_tris()
651 {
652   int i,m;
653   int e1,e2,e3,dir1,dir2,dir3;
654   double p1[3],p2[3],p3[3];
655   Surf::Tri *tri;
656   Vertex *vert;
657   Edge *edge;
658 
659   Surf::Tri *tris = surf->tris;
660 
661   verts.grow(nsurf);
662   edges.grow(3*nsurf);
663   verts.n = 0;
664   edges.n = 0;
665 
666   int nvert = 0;
667   for (i = 0; i < nsurf; i++) {
668     m = surfs[i];
669     tri = &tris[m];
670     if (tri->transparent) continue;
671 
672     memcpy(p1,tri->p1,3*sizeof(double));
673     memcpy(p2,tri->p2,3*sizeof(double));
674     memcpy(p3,tri->p3,3*sizeof(double));
675 
676     vert = &verts[nvert];
677     vert->active = 1;
678     vert->style = CTRI;
679     vert->label = i;
680     vert->nedge = 0;
681     vert->volume = 0.0;
682     vert->norm = tri->norm;
683 
684     // look for each edge of tri
685     // add to edges in forward dir if doesn't yet exist
686     // add to edges in reverse dir if already exists
687 
688     e1 = findedge(p1,p2,0,dir1);
689     if (e1 == -2) return 1;
690 
691     if (e1 < 0) {
692       e1 = edges.n++;
693       dir1 = 0;
694       edge = &edges[e1];
695       edge->style = CTRI;
696       edge->nvert = 0;
697       edge->verts[0] = edge->verts[1] = -1;
698       memcpy(edge->p1,p1,3*sizeof(double));
699       memcpy(edge->p2,p2,3*sizeof(double));
700     }
701     edge_insert(e1,dir1,nvert,-1,-1,-1,-1);
702 
703     e2 = findedge(p2,p3,0,dir2);
704     if (e2 == -2) return 1;
705 
706     if (e2 < 0) {
707       e2 = edges.n++;
708       dir2 = 0;
709       edge = &edges[e2];
710       edge->style = CTRI;
711       edge->nvert = 0;
712       edge->verts[0] = edge->verts[1] = -1;
713       memcpy(edge->p1,p2,3*sizeof(double));
714       memcpy(edge->p2,p3,3*sizeof(double));
715     }
716     edge_insert(e2,dir2,nvert,e1,dir1,-1,-1);
717 
718     e3 = findedge(p3,p1,0,dir3);
719     if (e3 == -2) return 1;
720 
721     if (e3 < 0) {
722       e3 = edges.n++;
723       dir3 = 0;
724       edge = &edges[e3];
725       edge->style = CTRI;
726       edge->nvert = 0;
727       edge->verts[0] = edge->verts[1] = -1;
728       memcpy(edge->p1,p3,3*sizeof(double));
729       memcpy(edge->p2,p1,3*sizeof(double));
730     }
731     edge_insert(e3,dir3,nvert,e2,dir2,-1,-1);
732 
733     nvert++;
734   }
735 
736   verts.n = nvert;
737   return 0;
738 }
739 
740 /* ----------------------------------------------------------------------
741    clip collection of tris that overlap cell by 6 faces of cell
742    loop over faces, loop over tris, loop over edges in tri
743    edges fully outside the cell are removed
744    shared edges that intersect the cell are clipped consistently
745 ------------------------------------------------------------------------- */
746 
clip_tris()747 void Cut3d::clip_tris()
748 {
749   int i,n,dim,lohi,ivert,iedge,jedge,idir,jdir,nedge;
750   int p1flag,p2flag;
751   double value;
752   double *p1,*p2,*p3;
753   Edge *edge,*newedge;
754 
755   // loop over all 6 faces of cell
756 
757   int nvert = verts.n;
758 
759   for (int iface = 0; iface < 6; iface++) {
760     dim = iface / 2;
761     lohi = iface % 2;
762     if (lohi == 0) value = lo[dim];
763     else value = hi[dim];
764 
765     // mark all edges as unclipped
766     // some may have been clipped and not cleared on previous face
767 
768     nedge = edges.n;
769     for (iedge = 0; iedge < nedge; iedge++)
770       if (edges[iedge].active) edges[iedge].clipped = 0;
771 
772     // loop over vertices, clip each of its edges to face
773     // if edge already clipped, unset clip flag and keep edge as-is
774 
775     for (ivert = 0; ivert < nvert; ivert++) {
776       iedge = verts[ivert].first;
777       idir = verts[ivert].dirfirst;
778       nedge = verts[ivert].nedge;
779 
780       for (i = 0; i < nedge; i++) {
781         edge = &edges[iedge];
782 
783         if (edge->clipped) {
784           edge->clipped = 0;
785           iedge = edge->next[idir];
786           idir = edge->dirnext[idir];
787           continue;
788         }
789 
790         // p1/p2 are pts in order of traversal
791 
792         if (idir == 0) {
793           p1 = edge->p1;
794           p2 = edge->p2;
795         } else {
796           p1 = edge->p2;
797           p2 = edge->p1;
798         }
799 
800         // p1/p2 flag = OUTSIDE/ON/INSIDE for edge pts
801 
802         if (lohi == 0) {
803           if (p1[dim] < value) p1flag = OUTSIDE;
804           else if (p1[dim] > value) p1flag = INSIDE;
805           else p1flag = OVERLAP;
806           if (p2[dim] < value) p2flag = OUTSIDE;
807           else if (p2[dim] > value) p2flag = INSIDE;
808           else p2flag = OVERLAP;
809         } else {
810           if (p1[dim] < value) p1flag = INSIDE;
811           else if (p1[dim] > value) p1flag = OUTSIDE;
812           else p1flag = OVERLAP;
813           if (p2[dim] < value) p2flag = INSIDE;
814           else if (p2[dim] > value) p2flag = OUTSIDE;
815           else p2flag = OVERLAP;
816         }
817 
818         // if both OUTSIDE or one OUTSIDE and other ON, delete edge
819         // if both INSIDE or one INSIDE and other ON or both ON, keep as-is
820         // if one INSIDE and one OUTSIDE, replace OUTSIDE pt with clip pt
821 
822 #ifdef VERBOSE
823         /*
824         if (id == VERBOSE_ID && ivert == 1) {
825           printf("EDGE %d %d: %d %d: %d %d: %d\n",iedge,idir,p1flag,p2flag,
826                  edge->verts[0],edge->verts[1],edge->prev[0]);
827         }
828         */
829 #endif
830 
831         if (p1flag == OUTSIDE) {
832           if (p2flag == OUTSIDE || p2flag == OVERLAP) edge_remove(edge,idir);
833           else {
834             if (idir == 0) between(p1,p2,dim,value,edge->p1);
835 	    else between(p1,p2,dim,value,edge->p2);
836 	    edge->clipped = 1;
837           }
838         } else if (p1flag == INSIDE) {
839           if (p2flag == OUTSIDE) {
840             if (idir == 0) between(p1,p2,dim,value,edge->p2);
841 	    else between(p1,p2,dim,value,edge->p1);
842 	    edge->clipped = 1;
843 	  }
844         } else {
845           if (p2flag == OUTSIDE) edge_remove(edge,idir);
846         }
847 
848         iedge = edge->next[idir];
849         idir = edge->dirnext[idir];
850       }
851 
852 #ifdef VERBOSE
853       /*
854       if (id == VERBOSE_ID) {
855         char str[24];
856         sprintf(str,"Partial FACE %d %d\n",iface,ivert);
857         print_bpg(str);
858       }
859       */
860 #endif
861 
862       // loop over edges in vertex again
863       // iedge = this edge, jedge = next edge
864       // p1 = last pt in iedge, pt = first pt in jedge
865       // if p1 != p2, add edge between them
866 
867       edges.grow(edges.n + verts[ivert].nedge);
868       iedge = verts[ivert].first;
869       idir = verts[ivert].dirfirst;
870 
871       for (i = 0; i < verts[ivert].nedge; i++) {
872         edge = &edges[iedge];
873         jedge = edge->next[idir];
874         jdir = edge->dirnext[idir];
875         if (jedge < 0) {
876           jedge = verts[ivert].first;
877           jdir = verts[ivert].dirfirst;
878         }
879 
880         if (idir == 0) p1 = edge->p2;
881         else p1 = edge->p1;
882         if (jdir == 0) p2 = edges[jedge].p1;
883         else p2 = edges[jedge].p2;
884 
885         if (!samepoint(p1,p2)) {
886           n = edges.n++;
887           newedge = &edges[n];
888           newedge->style = CTRI;
889           newedge->nvert = 0;
890           newedge->verts[0] = newedge->verts[1] = -1;
891           memcpy(newedge->p1,p1,3*sizeof(double));
892           memcpy(newedge->p2,p2,3*sizeof(double));
893           // convert jedge back to -1 for last vertex
894           if (jedge == verts[ivert].first) jedge = -1;
895           edge_insert(n,0,ivert,iedge,idir,jedge,jdir);
896           i++;
897         }
898 
899         iedge = jedge;
900         idir = jdir;
901       }
902     }
903 
904 #ifdef VERBOSE
905     /*
906     if (id == VERBOSE_ID) {
907       char str[24];
908       sprintf(str,"After FACE %d\n",iface);
909       print_bpg(str);
910     }
911     */
912 #endif
913   }
914 }
915 
916 /* ----------------------------------------------------------------------
917    adjust the collection of clipped triangles (vertices)
918    discard if clipped tri is a single point, increment touchcount
919      touchmark = corner point marking inferred from touching tri orientations
920    discard if grazes cell with outward normal, increment grazecount
921    if all clipped tris are discarded
922      set and return empty, touchcount, grazecount, touchmark
923 ------------------------------------------------------------------------- */
924 
clip_adjust()925 void Cut3d::clip_adjust()
926 {
927   int nvert,nedge,nface1,nface2;
928   int faces1[6],faces2[6];
929   double pboth[3],move1[3],move2[3];
930   double *p1,*p2,*p3;
931   Edge *edge;
932 
933 #ifdef VERBOSE
934   if (id == VERBOSE_ID) print_bpg("BPG after initial clipping");
935 #endif
936 
937   // epsilon = EPSEDGE fraction of largest cell dimension
938 
939   epsilon = EPSEDGE*(hi[0]-lo[0]);
940   epsilon = MAX(epsilon,EPSEDGE*(hi[1]-lo[1]));
941   epsilon = MAX(epsilon,EPSEDGE*(hi[2]-lo[2]));
942 
943   // collapse edges shorter than epsilon to a single point (so will be removed)
944   // one or both of the points should be on cell faces
945   // tiny edges can occur for several reasons:
946   // (1) pre-clip end point is within epsilon of face
947   // (2) a tri barely grazes cell edge or corner
948   // (3) clipping round-off produces tiny edges near cell faces
949 
950   nedge = edges.n;
951 
952   for (int iedge = 0; iedge < nedge; iedge++) {
953     if (!edges[iedge].active) continue;
954 
955     edge = &edges[iedge];
956     p1 = edge->p1;
957     p2 = edge->p2;
958     double dx = p1[0]-p2[0];
959     double dy = p1[1]-p2[1];
960     double dz = p1[2]-p2[2];
961     double edgelen = sqrt(dx*dx+dy*dy+dz*dz);
962 
963     if (edgelen < epsilon) {
964       //printf("TINY EDGE id %ld nsurf %d i/nedge %d %d len %g eps %g\n",
965       //       id,nsurf,iedge,nedge,edgelen,epsilon);
966       ntiny++;
967 
968       nface1 = on_faces(p1,faces1);
969       nface2 = on_faces(p2,faces2);
970 
971       // set both p1 and p2 to same pboth
972       // if both pts are interior (should not happen), pboth = p1
973       // if only one pt X is on a face, pboth = X
974       // if both pts are on one or more faces:
975       //   push both to face(s), recalculate on_faces()
976       //   if pt X is on more faces, pboth = X, else pboth = p1
977 
978       if (!nface1 && !nface2) {
979         memcpy(pboth,p1,3*sizeof(double));
980         //printf("INTERIOR EDGE %ld %d %d %g %g\n",
981         //       id,iedge,nedge,edgelen,epsilon);
982       } else if (nface1 && !nface2) {
983         memcpy(pboth,p1,3*sizeof(double));
984       } else if (nface2 && !nface1) {
985         memcpy(pboth,p2,3*sizeof(double));
986       } else {
987         memcpy(move1,p1,3*sizeof(double));
988         memcpy(move2,p2,3*sizeof(double));
989         move_to_faces(move1);
990         move_to_faces(move2);
991         nface1 = on_faces(move1,faces1);
992         nface2 = on_faces(move2,faces2);
993         if (nface2 > nface1) memcpy(pboth,move2,3*sizeof(double));
994         else memcpy(pboth,move1,3*sizeof(double));
995       }
996 
997       // set all points that are same as old p1 or p2 to pboth
998       // reset first for all jedge != iedge, then reset iedge
999 
1000       for (int jedge = 0; jedge < nedge; jedge++) {
1001         if (!edges[jedge].active) continue;
1002         if (jedge == iedge) continue;
1003 
1004         if (samepoint(edges[jedge].p1,p1))
1005           memcpy(edges[jedge].p1,pboth,3*sizeof(double));
1006         if (samepoint(edges[jedge].p2,p1))
1007           memcpy(edges[jedge].p2,pboth,3*sizeof(double));
1008 
1009         if (samepoint(edges[jedge].p1,p2))
1010           memcpy(edges[jedge].p1,pboth,3*sizeof(double));
1011         if (samepoint(edges[jedge].p2,p2))
1012           memcpy(edges[jedge].p2,pboth,3*sizeof(double));
1013       }
1014 
1015       memcpy(edges[iedge].p1,pboth,3*sizeof(double));
1016       memcpy(edges[iedge].p2,pboth,3*sizeof(double));
1017     }
1018   }
1019 
1020 #ifdef VERBOSE
1021   if (id == VERBOSE_ID) print_bpg("BPG after tiny edge collapse");
1022 #endif
1023 
1024   // remove zero-length edges
1025 
1026   nedge = edges.n;
1027 
1028   for (int iedge = 0; iedge < nedge; iedge++) {
1029     if (!edges[iedge].active) continue;
1030     edge = &edges[iedge];
1031     if (samepoint(edge->p1,edge->p2)) edge_remove(edge);
1032   }
1033 
1034 #ifdef VERBOSE
1035   if (id == VERBOSE_ID) print_bpg("BPG after remove zero-length edges");
1036 #endif
1037 
1038   // remove vertices (triangles) which now have less than 3 edges
1039   // do this after deleting zero-length edges so vertices are updated
1040   // removals should have 2 or 0 edges, no verts should have 1 edge
1041   // if all triangles are removed, BPG will be empty,
1042   //   which will result in cell corner pts being left UNKNOWN in split()
1043   // to try and avoid this, tally inside/outside for all removed tris
1044   //   tri is "outside" if it implies cell is outside the surf (in the flow)
1045   //   tri is "inside" if it implies cell is inside the surf (not in the flow)
1046   //   some tris may not follow this rule, but most should
1047   // outside = tri norm from tri ctr points towards cell ctr
1048   // inside = tri norm from tri ctr points away from cell ctr
1049   // cbox = cell center pt, ctri = triangle center pt, t2b = cbox-ctri
1050 
1051   touchcount = 0;
1052   grazecount = 0;
1053 
1054   int noutside = 0;
1055   int ninside = 0;
1056 
1057   double cbox[3],ctri[3],t2b[3];
1058 
1059   nvert = verts.n;
1060 
1061   for (int ivert = 0; ivert < nvert; ivert++)
1062     if (verts[ivert].nedge <= 2) {
1063       touchcount++;
1064       cbox[0] = 0.5*(lo[0]+hi[0]);
1065       cbox[1] = 0.5*(lo[1]+hi[1]);
1066       cbox[2] = 0.5*(lo[2]+hi[2]);
1067       int itri = surfs[verts[ivert].label];
1068       p1 = surf->tris[itri].p1;
1069       p2 = surf->tris[itri].p2;
1070       p3 = surf->tris[itri].p3;
1071       ctri[0] = (p1[0]+p2[0]+p3[0])/3.0;
1072       ctri[1] = (p1[1]+p2[1]+p3[1])/3.0;
1073       ctri[2] = (p1[2]+p2[2]+p3[2])/3.0;
1074       MathExtra::sub3(cbox,ctri,t2b);
1075       double dot = MathExtra::dot3(verts[ivert].norm,t2b);
1076       if (dot > 0.0) noutside++;
1077       if (dot < 0.0) ninside++;
1078       vertex_remove(&verts[ivert]);
1079     }
1080 
1081   // discard clipped tri if lies on a cell face w/ normal out of cell
1082   // increment grazecount in this case
1083 
1084   for (int ivert = 0; ivert < nvert; ivert++) {
1085     if (!verts[ivert].active) continue;
1086     if (grazing(&verts[ivert])) {
1087       vertex_remove(&verts[ivert]);
1088       grazecount++;
1089     }
1090   }
1091 
1092   // remove edges which now have no vertices
1093 
1094   for (int iedge = 0; iedge < nedge; iedge++) {
1095     if (!edges[iedge].active) continue;
1096     if (edges[iedge].nvert == 0) edges[iedge].active = 0;
1097   }
1098 
1099   // set BPG empty flag if no active vertices
1100 
1101   empty = 1;
1102   for (int ivert = 0; ivert < nvert; ivert++)
1103     if (verts[ivert].active) {
1104       empty = 0;
1105       break;
1106     }
1107 
1108   // if no lines, set touchmark which will be used to mark corner points
1109   // only set touchmark if all deleted tris had same orientation
1110   // NOTE: setting touchmark based on orientation of just majority
1111   //   triggered later corner marking error for cone_test/in.cone problem
1112 
1113   touchmark = UNKNOWN;
1114   if (empty) {
1115     if (ninside && noutside == 0) touchmark = INSIDE;
1116     else if (noutside && ninside == 0) touchmark = OUTSIDE;
1117   }
1118 }
1119 
1120 /* ----------------------------------------------------------------------
1121    compute volume of vertices
1122    when called, only clipped triangles exist
1123 ------------------------------------------------------------------------- */
1124 
ctri_volume()1125 void Cut3d::ctri_volume()
1126 {
1127   int i,iedge,idir,nedge;
1128   double zarea,volume;
1129   double *p0,*p1,*p2;
1130   Edge *edge;
1131 
1132   int nvert = verts.n;
1133   for (int ivert = 0; ivert < nvert; ivert++) {
1134     if (!verts[ivert].active) continue;
1135     iedge = verts[ivert].first;
1136     idir = verts[ivert].dirfirst;
1137     nedge = verts[ivert].nedge;
1138 
1139     if (idir == 0) p0 = edges[iedge].p1;
1140     else p0 = edges[iedge].p2;
1141 
1142     volume = 0.0;
1143 
1144     for (i = 0; i < nedge; i++) {
1145       edge = &edges[iedge];
1146 
1147       // compute projected volume of a convex polygon to zlo face
1148       // split polygon into triangles
1149       // each tri makes a tri-capped volume with zlo face
1150       // zarea = area of oriented tri projected into z plane
1151       // volume based on height of z midpt of tri above zlo face
1152 
1153       if (idir == 0) {
1154         p1 = edge->p1;
1155         p2 = edge->p2;
1156       } else {
1157         p1 = edge->p2;
1158         p2 = edge->p1;
1159       }
1160       zarea = 0.5 * ((p1[0]-p0[0])*(p2[1]-p0[1]) - (p1[1]-p0[1])*(p2[0]-p0[0]));
1161       volume -= zarea * ((p0[2]+p1[2]+p2[2])/3.0 - lo[2]);
1162 
1163       iedge = edge->next[idir];
1164       idir = edge->dirnext[idir];
1165     }
1166 
1167     verts[ivert].volume = volume;
1168   }
1169 }
1170 
1171 /* ----------------------------------------------------------------------
1172    assign all singlet edges to faces (0-5)
1173    singlet edge must be on one or two faces, two if on cell edge
1174    if along cell edge, assign to one of two faces based on
1175      which has larger dot product of its inward face norm
1176      and the norm of the tri containing the edge
1177 ------------------------------------------------------------------------- */
1178 
edge2face()1179 int Cut3d::edge2face()
1180 {
1181   int n,iface,nface,ivert;
1182   int faces[6];
1183   double dot0,dot1;
1184   double norm_inward[3];
1185   double *trinorm;
1186   Edge *edge;
1187 
1188   // insure each facelist has sufficient length
1189 
1190   int nedge = edges.n;
1191   for (int i = 0; i < 6; i++) {
1192     facelist[i].grow(nedge);
1193     facelist[i].n = 0;
1194   }
1195 
1196   // loop over edges, assign singlets to exactly one face
1197 
1198   for (int iedge = 0; iedge < nedge; iedge++) {
1199     if (!edges[iedge].active) continue;
1200     if (edges[iedge].nvert == 3) continue;
1201     edge = &edges[iedge];
1202 
1203     nface = which_faces(edge->p1,edge->p2,faces);
1204     if (nface == 0) {
1205       //printf("ERROR RETURN id %ld nedge %d iedge %d pt1 "
1206       //       "%20.16g %20.16g %20.16g %20.16g %20.16g %20.16g\n",
1207       //       id,nedge,iedge,
1208       //       edge->p1[0],edge->p1[1],edge->p1[2],
1209       //       edge->p2[0],edge->p2[1],edge->p2[2]);
1210       return 2;
1211     }
1212 
1213     else if (nface == 1) iface = faces[0];
1214 
1215     else if (nface == 2) {
1216       if (edge->nvert == 1) ivert = edge->verts[0];
1217       else ivert = edge->verts[1];
1218       trinorm = verts[ivert].norm;
1219 
1220       iface = faces[0];
1221       norm_inward[0] = norm_inward[1] = norm_inward[2] = 0.0;
1222       if (iface % 2) norm_inward[iface/2] = -1.0;
1223       else norm_inward[iface/2] = 1.0;
1224       dot0 = norm_inward[0]*trinorm[0] + norm_inward[1]*trinorm[1] +
1225         norm_inward[2]*trinorm[2];
1226       if (dot0 > 0.0) iface = faces[1];
1227 
1228     } else return 3;
1229 
1230     n = facelist[iface].n;
1231     facelist[iface][n++] = iedge;
1232     facelist[iface].n = n;
1233   }
1234 
1235   return 0;
1236 }
1237 
1238 /* ----------------------------------------------------------------------
1239    build a 2d CLINES data structure
1240    from all singlet edges assigned to iface (0-5)
1241    order pts in edge for tri traversing edge in forward order
1242    flip edge if in a flip face = faces 0,3,4
1243    edge label in clines = edge index in BPG
1244 ------------------------------------------------------------------------- */
1245 
edge2clines(int iface)1246 void Cut3d::edge2clines(int iface)
1247 {
1248   int iedge;
1249   double *p1,*p2;
1250   Edge *edge;
1251   Cut2d::Cline *cline;
1252 
1253   MyVec<Cut2d::Cline> *clines = &cut2d->clines;
1254 
1255   int flip = 0;
1256   if (iface == 0 || iface == 3 || iface == 4) flip = 1;
1257 
1258   int nline = facelist[iface].n;
1259   clines->n = 0;
1260   clines->grow(nline);
1261 
1262   for (int i = 0; i < nline; i++) {
1263     iedge = facelist[iface][i];
1264     edge = &edges[iedge];
1265     if (edge->nvert == 1) {
1266       p1 = edge->p1;
1267       p2 = edge->p2;
1268     } else {
1269       p1 = edge->p2;
1270       p2 = edge->p1;
1271     }
1272     cline = &(*clines)[i];
1273     cline->line = iedge;
1274     if (flip) {
1275       compress2d(iface,p1,cline->y);
1276       compress2d(iface,p2,cline->x);
1277     } else {
1278       compress2d(iface,p1,cline->x);
1279       compress2d(iface,p2,cline->y);
1280     }
1281   }
1282 
1283   clines->n = nline;
1284 }
1285 
1286 /* ----------------------------------------------------------------------
1287    add one or more face polygons as vertices to BPG
1288    have to convert pts computed by cut2d back into 3d pts on face
1289 ------------------------------------------------------------------------- */
1290 
add_face_pgons(int iface)1291 int Cut3d::add_face_pgons(int iface)
1292 {
1293   int iloop,mloop,nloop,ipt,mpt,npt;
1294   int iedge,dir,prev,dirprev;
1295   double p1[3],p2[3];
1296   Vertex *vert;
1297   Edge *edge;
1298   Cut2d::PG *pg;
1299   Cut2d::Loop *loop;
1300   Cut2d::Point *p12d,*p22d;
1301 
1302   MyVec<Cut2d::PG> *pgs = &cut2d->pgs;
1303   MyVec<Cut2d::Loop> *loops = &cut2d->loops;
1304   MyVec<Cut2d::Point> *points = &cut2d->points;
1305 
1306   int flip = 0;
1307   if (iface == 0 || iface == 3 || iface == 4) flip = 1;
1308 
1309   double value;
1310   int dim = iface / 2;
1311   int lohi = iface % 2;
1312   if (lohi == 0) value = lo[dim];
1313   else value = hi[dim];
1314 
1315   int npg = pgs->n;
1316   int nvert = verts.n;
1317   verts.grow(nvert+npg);
1318 
1319   for (int ipg = 0; ipg < npg; ipg++) {
1320     pg = &(*pgs)[ipg];
1321 
1322     vert = &verts[nvert];
1323     vert->active = 1;
1324     vert->style = FACEPGON;
1325     vert->label = iface;
1326     if (iface == 5) vert->volume = pg->area * (hi[2]-lo[2]);
1327     else vert->volume = 0.0;
1328     vert->nedge = 0;
1329     vert->norm = NULL;
1330 
1331     prev = -1;
1332     dirprev = -1;
1333 
1334     nloop = pg->n;
1335     mloop = pg->first;
1336     for (iloop = 0; iloop < nloop; iloop++) {
1337       loop = &(*loops)[mloop];
1338       npt = loop->n;
1339       mpt = loop->first;
1340       edges.grow(edges.n + npt);
1341 
1342       for (ipt = 0; ipt < npt; ipt++) {
1343         p12d = &(*points)[mpt];
1344         mpt = p12d->next;
1345         p22d = &(*points)[mpt];
1346         expand2d(iface,value,p12d->x,p1);
1347         expand2d(iface,value,p22d->x,p2);
1348 
1349         // edge was from a CTRI vertex
1350         // match in opposite order that CTRI vertex matched it
1351 
1352         if (p12d->type == ENTRY || p12d->type == TWO) {
1353           iedge = p12d->line;
1354           edge = &edges[iedge];
1355           edge->style = CTRIFACE;
1356           if (edge->nvert == 1) dir = 1;
1357           else dir = 0;
1358           edge_insert(iedge,dir,nvert,prev,dirprev,-1,-1);
1359           prev = iedge;
1360           dirprev = dir;
1361           continue;
1362         }
1363 
1364         // face edge not from a CTRI
1365         // unflip edge if in a flip face
1366 
1367         if (flip) iedge = findedge(p2,p1,0,dir);
1368         else iedge = findedge(p1,p2,0,dir);
1369         if (iedge == -2) return 1;
1370 
1371         if (iedge >= 0) {
1372           edge_insert(iedge,dir,nvert,prev,dirprev,-1,-1);
1373           prev = iedge;
1374           dirprev = 1;
1375           continue;
1376         }
1377 
1378         iedge = edges.n++;
1379         edge = &edges[iedge];
1380         edge->style = FACEPGON;
1381         edge->nvert = 0;
1382         edge->verts[0] = edge->verts[1] = -1;
1383         if (flip) {
1384           memcpy(edge->p1,p2,3*sizeof(double));
1385           memcpy(edge->p2,p1,3*sizeof(double));
1386         } else {
1387           memcpy(edge->p1,p1,3*sizeof(double));
1388           memcpy(edge->p2,p2,3*sizeof(double));
1389         }
1390         dir = 0;
1391         edge_insert(iedge,dir,nvert,prev,dirprev,-1,-1);
1392         prev = iedge;
1393         dirprev = 0;
1394       }
1395       mloop = loop->next;
1396     }
1397 
1398     nvert++;
1399   }
1400 
1401   verts.n = nvert;
1402   return 0;
1403 }
1404 
1405 /* ----------------------------------------------------------------------
1406    add an entire cell face as vertex to BPG
1407    if outerflag2d = 0, create new vertex
1408    else face polygon already exists, so add edges to it
1409    caller sets outerflag2d if cut2d requires adding perimeter face edges
1410 ------------------------------------------------------------------------- */
1411 
add_face(int iface,double * lo2d,double * hi2d)1412 int Cut3d::add_face(int iface, double *lo2d, double *hi2d)
1413 {
1414   int i,j,iedge,dir,prev,dirprev;
1415   double p1[3],p2[3];
1416   Vertex *vert;
1417   Edge *edge;
1418 
1419   int nvert = verts.n++;
1420   verts.grow(nvert + 1);
1421   vert = &verts[nvert];
1422   vert->active = 1;
1423   vert->style = FACE;
1424   vert->label = iface;
1425   if (iface == 5)
1426     vert->volume = (hi[0]-lo[0]) * (hi[1]-lo[1]) * (hi[2]-lo[2]);
1427   else vert->volume = 0.0;
1428   vert->nedge = 0;
1429   vert->norm = NULL;
1430 
1431   double value;
1432   int dim = iface / 2;
1433   int lohi = iface % 2;
1434   if (lohi == 0) value = lo[dim];
1435   else value = hi[dim];
1436 
1437   // usual ordering of points in face as LL,LR,UR,UL
1438   // flip order if in a flip face
1439 
1440   int flip = 0;
1441   if (iface == 0 || iface == 3 || iface == 4) flip = 1;
1442 
1443   double cpts[4][2];
1444 
1445   if (flip) {
1446     cpts[0][0] = lo2d[0]; cpts[0][1] = lo2d[1];
1447     cpts[1][0] = lo2d[0]; cpts[1][1] = hi2d[1];
1448     cpts[2][0] = hi2d[0]; cpts[2][1] = hi2d[1];
1449     cpts[3][0] = hi2d[0]; cpts[3][1] = lo2d[1];
1450   } else {
1451     cpts[0][0] = lo2d[0]; cpts[0][1] = lo2d[1];
1452     cpts[1][0] = hi2d[0]; cpts[1][1] = lo2d[1];
1453     cpts[2][0] = hi2d[0]; cpts[2][1] = hi2d[1];
1454     cpts[3][0] = lo2d[0]; cpts[3][1] = hi2d[1];
1455   }
1456 
1457   if (vert->nedge) {
1458     prev = vert->last;
1459     dirprev = vert->dirlast;
1460   } else {
1461     prev = -1;
1462     dirprev = -1;
1463   }
1464 
1465   edges.grow(edges.n + 4);
1466 
1467   for (i = 0; i < 4; i++) {
1468     j = i+1;
1469     if (j == 4) j = 0;
1470     expand2d(iface,value,&cpts[i][0],p1);
1471     expand2d(iface,value,&cpts[j][0],p2);
1472     iedge = findedge(p1,p2,1,dir);
1473     if (iedge == -2) return 1;
1474 
1475     if (iedge >= 0) {
1476       edge_insert(iedge,dir,nvert,prev,dirprev,-1,-1);
1477       prev = iedge;
1478       dirprev = 1;
1479       continue;
1480     }
1481 
1482     iedge = edges.n++;
1483     edge = &edges[iedge];
1484     edge->style = vert->style;
1485     edge->nvert = 0;
1486     edge->verts[0] = edge->verts[1] = -1;
1487     memcpy(edge->p1,p1,3*sizeof(double));
1488     memcpy(edge->p2,p2,3*sizeof(double));
1489     dir = 0;
1490     edge_insert(iedge,dir,nvert,prev,dirprev,-1,-1);
1491     prev = iedge;
1492     dirprev = 0;
1493   }
1494 
1495   return 0;
1496 }
1497 
1498 /* ----------------------------------------------------------------------
1499    remove any FACE vertices with one or more unconnected edges
1500    unconnected means the edge is only part of this vertex
1501    mark vertex as inactive, decrement their edges,
1502      also set edges inactive if they are no longer attached to vertices
1503    iterate twice since another face may become unconnected
1504 ------------------------------------------------------------------------- */
1505 
remove_faces()1506 void Cut3d::remove_faces()
1507 {
1508   int i,ivert,iedge,dir;
1509   Vertex *vert;
1510   Edge *edge;
1511 
1512   int nvert = verts.n;
1513 
1514   for (int iter = 0; iter < 2; iter++)
1515     for (ivert = 0; ivert < nvert; ivert++) {
1516       if (!verts[ivert].active) continue;
1517       if (verts[ivert].style != FACE) continue;
1518       vert = &verts[ivert];
1519 
1520       iedge = vert->first;
1521       dir = vert->dirfirst;
1522       for (i = 0; i < 4; i++) {
1523         edge = &edges[iedge];
1524         if (edge->nvert == 1 || edge->nvert == 2) break;
1525         iedge = edge->next[dir];
1526         dir = edge->dirnext[dir];
1527       }
1528       if (i < 4) vertex_remove(vert);
1529     }
1530 }
1531 
1532 /* ----------------------------------------------------------------------
1533    check BPG for consistency
1534    vertices have 3 or more unique edges that point back to it
1535    edges have 2 unique vertices
1536 ------------------------------------------------------------------------- */
1537 
check()1538 int Cut3d::check()
1539 {
1540   int i,iedge,dir,nedge,last,dirlast;
1541   Vertex *vert;
1542   Edge *edge;
1543 
1544   // mark all edges as unclipped
1545   // use for detecting duplicate edges in same vertex
1546 
1547   nedge = edges.n;
1548   for (iedge = 0; iedge < nedge; iedge++)
1549     if (edges[iedge].active) edges[iedge].clipped = 0;
1550 
1551   // check vertices
1552   // for each vertex: mark edges as see them, unmark all edges at end
1553 
1554   int nvert = verts.n;
1555   for (int ivert = 0; ivert < nvert; ivert++) {
1556     if (!verts[ivert].active) continue;
1557     vert = &verts[ivert];
1558     if (vert->nedge < 3) return 11;
1559 
1560     nedge = vert->nedge;
1561     iedge = vert->first;
1562     dir = vert->dirfirst;
1563 
1564     for (i = 0; i < nedge; i++) {
1565       edge = &edges[iedge];
1566       if (!edge->active) return 12;
1567       if (edge->verts[dir] != ivert) return 13;
1568       if (edge->clipped) return 14;
1569       edge->clipped = 1;
1570       last = iedge;
1571       dirlast = dir;
1572       iedge = edge->next[dir];
1573       dir = edge->dirnext[dir];
1574     }
1575 
1576     if (last != vert->last || dirlast != vert->dirlast) return 15;
1577 
1578     iedge = vert->first;
1579     dir = vert->dirfirst;
1580     for (i = 0; i < nedge; i++) {
1581       edge = &edges[iedge];
1582       edge->clipped = 0;
1583       iedge = edge->next[dir];
1584       dir = edge->dirnext[dir];
1585     }
1586   }
1587 
1588   // check edges
1589 
1590   nedge = edges.n;
1591   for (int iedge = 0; iedge < nedge; iedge++) {
1592     if (!edges[iedge].active) continue;
1593     edge = &edges[iedge];
1594 
1595     if (edge->nvert != 3) return 16;
1596     if (edge->verts[0] == edge->verts[1]) return 17;
1597     if (edge->verts[0] >= nvert || !verts[edge->verts[0]].active) return 18;
1598     if (edge->verts[1] >= nvert || !verts[edge->verts[1]].active) return 19;
1599   }
1600 
1601   return 0;
1602 }
1603 
1604 /* ----------------------------------------------------------------------
1605    convert BPG into simple closed polyhedra, not nested
1606    walk BPG from any unused vertex, flagging vertices as used
1607    stack is list of new vertices to process
1608    loop over edges of pgon, add its unused neighbors to stack
1609    when stack is empty, loop is closed
1610    accumulate volume of polyhedra as walk it from volume of each vertex
1611 ------------------------------------------------------------------------- */
1612 
walk()1613 void Cut3d::walk()
1614 {
1615   int flag,ncount,ivert,firstvert,iedge,dir,nedge,prev;
1616   double volume;
1617   Vertex *vert;
1618   Edge *edge;
1619 
1620   // used = 0/1 flag for whether a vertex is already part of a loop
1621   // only active vertices are eligible
1622 
1623   int nvert = verts.n;
1624   used.grow(nvert);
1625   for (int ivert = 0; ivert < nvert; ivert++) {
1626     if (verts[ivert].active) used[ivert] = 0;
1627     else used[ivert] = 1;
1628   }
1629   used.n = nvert;
1630 
1631   // stack = list of vertex indices to process
1632   // max size = # of vertices
1633 
1634   stack.grow(nvert);
1635   int nstack = 0;
1636 
1637   // iterate over all vertices
1638   // start a loop at any unused vertex
1639   // add more vertices to loop via stack
1640   // check all neighbor vertices via shared edges
1641   // if neighbor vertex is unused, add to stack
1642   // stop when stack is empty
1643 
1644   int nloop = 0;
1645 
1646   for (int i = 0; i < nvert; i++) {
1647     if (used[i]) continue;
1648     volume = 0.0;
1649     flag = INTERIOR;
1650     ncount = 0;
1651 
1652     stack[0] = firstvert = i;
1653     nstack = 1;
1654     used[i] = 1;
1655     prev = -1;
1656 
1657     while (nstack) {
1658       nstack--;
1659       ivert = stack[nstack];
1660       ncount++;
1661 
1662       vert = &verts[ivert];
1663       if (vert->style != CTRI) flag = BORDER;
1664       volume += vert->volume;
1665 
1666       nedge = vert->nedge;
1667       iedge = vert->first;
1668       dir = vert->dirfirst;
1669 
1670       for (i = 0; i < nedge; i++) {
1671         edge = &edges[iedge];
1672         if (!used[edge->verts[0]]) {
1673           stack[nstack++] = edge->verts[0];
1674           used[edge->verts[0]] = 1;
1675         }
1676         if (!used[edge->verts[1]]) {
1677           stack[nstack++] = edge->verts[1];
1678           used[edge->verts[1]] = 1;
1679         }
1680         iedge = edge->next[dir];
1681         dir = edge->dirnext[dir];
1682       }
1683 
1684       if (prev >= 0) verts[prev].next = ivert;
1685       prev = ivert;
1686     }
1687     vert->next = -1;
1688 
1689     loops.grow(nloop+1);
1690     loops[nloop].volume = volume;
1691     loops[nloop].flag = flag;
1692     loops[nloop].n = ncount;
1693     loops[nloop].first = firstvert;
1694     nloop++;
1695   }
1696 
1697   loops.n = nloop;
1698 }
1699 
1700 /* ----------------------------------------------------------------------
1701 ------------------------------------------------------------------------- */
1702 
loop2ph()1703 int Cut3d::loop2ph()
1704 {
1705   int positive = 0;
1706   int negative = 0;
1707 
1708   int nloop = loops.n;
1709 
1710   for (int i = 0; i < nloop; i++) {
1711     if (loops[i].volume > 0.0) positive++;
1712     else negative++;
1713   }
1714 
1715   // if no positive vols, cell is entirely inside the surf, caller handles it
1716   // this can happen due to epsilon size polyhedron(s)
1717   // e.g. when a tri barely cuts off a cell corner
1718 
1719   if (positive == 0) return 4;
1720 
1721   // do not allow mulitple positive with one or more negative
1722   // too difficult to figure out which positive each negative is inside of
1723 
1724   if (positive > 1 && negative) return 5;
1725 
1726   // positive = 1 means 1 PH with vol = sum of all pos/neg loops
1727   // positive > 1 means each loop is a PH
1728 
1729   phs.grow(positive);
1730 
1731   if (positive == 1) {
1732     double volume = 0.0;
1733     for (int i = 0; i < nloop; i++) {
1734       volume += loops[i].volume;
1735       loops[i].next = i+1;
1736     }
1737     loops[nloop-1].next = -1;
1738 
1739     if (volume < 0.0) return 6;
1740 
1741     phs[0].volume = volume;
1742     phs[0].n = nloop;
1743     phs[0].first = 0;
1744 
1745   } else {
1746     for (int i = 0; i < nloop; i++) {
1747       phs[i].volume = loops[i].volume;
1748       phs[i].n = 1;
1749       phs[i].first = i;
1750       loops[i].next = -1;
1751     }
1752   }
1753 
1754   phs.n = positive;
1755   return 0;
1756 }
1757 
1758 /* ----------------------------------------------------------------------
1759    assign each tri index in list to one of the split cells in PH
1760    return surfmap[i] = which PH the Ith tri index is assigned to
1761    set surfmap[i] = -1 if the tri did not end up in a PH
1762      could have been discarded in clip_tris()
1763      due to touching cell or lying along a cell edge or face
1764 ------------------------------------------------------------------------- */
1765 
create_surfmap(int * surfmap)1766 void Cut3d::create_surfmap(int *surfmap)
1767 {
1768   for (int i = 0; i < nsurf; i++) surfmap[i] = -1;
1769 
1770   int iloop,nloop,mloop,ivert,nvert,mvert;
1771 
1772   int nph = phs.n;
1773   for (int iph = 0; iph < nph; iph++) {
1774     nloop = phs[iph].n;
1775     mloop = phs[iph].first;
1776     for (iloop = 0; iloop < nloop; iloop++) {
1777       nvert = loops[mloop].n;
1778       mvert = loops[mloop].first;
1779       for (ivert = 0; ivert < nvert; ivert++) {
1780         if (verts[mvert].style == CTRI || verts[mvert].style == CTRIFACE)
1781           surfmap[verts[mvert].label] = iph;
1782         mvert = verts[mvert].next;
1783       }
1784       mloop = loops[mloop].next;
1785     }
1786   }
1787 }
1788 
1789 /* ----------------------------------------------------------------------
1790    find a surf point that is inside or on the boundary of the current cell
1791    for external surfs and cells already been flagged as a split cell
1792    surfmap = sub-cell index each surf is part of (-1 if not eligible)
1793    return xsplit = coords of point
1794    return xsub = sub-cell index the chosen surf is in
1795 ------------------------------------------------------------------------- */
1796 
split_point_explicit(int * surfmap,double * xsplit,int & xsub)1797 int Cut3d::split_point_explicit(int *surfmap, double *xsplit, int &xsub)
1798 {
1799   int itri;
1800   double *x1,*x2,*x3;
1801 
1802   Surf::Tri *tris = surf->tris;
1803 
1804   // if end pt of any line with non-negative surfmap is in/on cell, return
1805 
1806   for (int i = 0; i < nsurf; i++) {
1807     if (surfmap[i] < 0) continue;
1808     itri = surfs[i];
1809     x1 = tris[itri].p1;
1810     x2 = tris[itri].p2;
1811     x3 = tris[itri].p3;
1812     if (ptflag(x1) != EXTERIOR) {
1813       xsplit[0] = x1[0]; xsplit[1] = x1[1]; xsplit[2] = x1[2];
1814       xsub = surfmap[i];
1815       return 0;
1816     }
1817     if (ptflag(x2) != EXTERIOR) {
1818       xsplit[0] = x2[0]; xsplit[1] = x2[1]; xsplit[2] = x2[2];
1819       xsub = surfmap[i];
1820       return 0;
1821     }
1822     if (ptflag(x3) != EXTERIOR) {
1823       xsplit[0] = x3[0]; xsplit[1] = x3[1]; xsplit[2] = x3[2];
1824       xsub = surfmap[i];
1825       return 0;
1826     }
1827   }
1828 
1829   // clip 1st line with non-negative surfmap to cell, and return clip point
1830 
1831   for (int i = 0; i < nsurf; i++) {
1832     if (surfmap[i] < 0) continue;
1833     itri = surfs[i];
1834     x1 = tris[itri].p1;
1835     x2 = tris[itri].p2;
1836     x3 = tris[itri].p3;
1837     clip(x1,x2,x3);
1838     xsplit[0] = path1[0][0]; xsplit[1] = path1[0][1]; xsplit[2] = path1[0][2];
1839     xsub = surfmap[i];
1840     return 0;
1841   }
1842 
1843   // error return
1844 
1845   return 7;
1846 }
1847 
1848 /* ----------------------------------------------------------------------
1849    find a surf point that is inside or on the boundary of the current cell
1850    for implicit surfs and cells already been flagged as a split cell
1851    surfmap = sub-cell index each surf is part of (-1 if not eligible)
1852    return xsplit = coords of point
1853    return xsub = sub-cell index the chosen surf is in
1854 ------------------------------------------------------------------------- */
1855 
split_point_implicit(int * surfmap,double * xsplit,int & xsub)1856 int Cut3d::split_point_implicit(int *surfmap, double *xsplit, int &xsub)
1857 {
1858   Surf::Tri *tris = surf->tris;
1859 
1860   // i = 1st surf with non-negative surfmap
1861 
1862   int i = 0;
1863   while (surfmap[i] < 0 && i < nsurf) i++;
1864   if (i == nsurf) return 7;
1865 
1866   // xsplit = center point of triangle wholly contained in cell
1867 
1868   int itri = surfs[i];
1869   double onethird = 1.0/3.0;
1870   xsplit[0] = onethird * (tris[itri].p1[0] + tris[itri].p2[0] + tris[itri].p3[0]);
1871   xsplit[1] = onethird * (tris[itri].p1[1] + tris[itri].p2[1] + tris[itri].p3[1]);
1872   xsplit[2] = onethird * (tris[itri].p1[2] + tris[itri].p2[2] + tris[itri].p3[2]);
1873 
1874   xsub = surfmap[i];
1875 
1876   return 0;
1877 }
1878 
1879 /* ----------------------------------------------------------------------
1880    insert edge IEDGE in DIR for ivert
1881    also update vertex info for added edge
1882 ------------------------------------------------------------------------- */
1883 
edge_insert(int iedge,int dir,int ivert,int iprev,int dirprev,int inext,int dirnext)1884 void Cut3d::edge_insert(int iedge, int dir, int ivert,
1885                         int iprev, int dirprev, int inext, int dirnext)
1886 {
1887   Edge *edge = &edges[iedge];
1888 
1889   if (dir == 0) {
1890     edge->nvert += 1;
1891     edge->verts[0] = ivert;
1892   } else {
1893     edge->nvert += 2;
1894     edge->verts[1] = ivert;
1895   }
1896 
1897   edge->active = 1;
1898   edge->clipped = 0;
1899 
1900   // set prev/next pointers for doubly linked list of edges
1901 
1902   edge->next[dir] = inext;
1903   edge->prev[dir] = iprev;
1904 
1905   if (inext >= 0) {
1906     edge->dirnext[dir] = dirnext;
1907     Edge *next = &edges[inext];
1908     next->prev[dirnext] = iedge;
1909     next->dirprev[dirnext] = dir;
1910   } else edge->dirnext[dir] = -1;
1911 
1912   if (iprev >= 0) {
1913     edge->dirprev[dir] = dirprev;
1914     Edge *prev = &edges[iprev];
1915     prev->next[dirprev] = iedge;
1916     prev->dirnext[dirprev] = dir;
1917   } else edge->dirprev[dir] = -1;
1918 
1919   // add edge info to owning vertex
1920 
1921   verts[ivert].nedge++;
1922   if (iprev < 0) {
1923     verts[ivert].first = iedge;
1924     verts[ivert].dirfirst = dir;
1925   }
1926   if (inext < 0) {
1927     verts[ivert].last = iedge;
1928     verts[ivert].dirlast = dir;
1929   }
1930 }
1931 
1932 /* ----------------------------------------------------------------------
1933    complete edge removal in both dirs
1934    will leave edge marked inactive
1935 ------------------------------------------------------------------------- */
1936 
edge_remove(Edge * edge)1937 void Cut3d::edge_remove(Edge *edge)
1938 {
1939   if (edge->verts[0] >= 0) edge_remove(edge,0);
1940   if (edge->verts[1] >= 0) edge_remove(edge,1);
1941 }
1942 
1943 /* ----------------------------------------------------------------------
1944    edge removal in DIR
1945    also update vertex info for removed edge
1946    mark edge inactive if its nvert -> 0
1947 ------------------------------------------------------------------------- */
1948 
edge_remove(Edge * edge,int dir)1949 void Cut3d::edge_remove(Edge *edge, int dir)
1950 {
1951   int ivert = edge->verts[dir];
1952   edge->verts[dir] = -1;
1953   if (dir == 0) edge->nvert--;
1954   else edge->nvert -= 2;
1955   if (edge->nvert == 0) edge->active = 0;
1956 
1957   // reset prev/next pointers for doubly linked list to skip this edge
1958 
1959   if (edge->prev[dir] >= 0) {
1960     Edge *prev = &edges[edge->prev[dir]];
1961     int dirprev = edge->dirprev[dir];
1962     prev->next[dirprev] = edge->next[dir];
1963     prev->dirnext[dirprev] = edge->dirnext[dir];
1964   }
1965 
1966   if (edge->next[dir] >= 0) {
1967     Edge *next = &edges[edge->next[dir]];
1968     int dirnext = edge->dirnext[dir];
1969     next->prev[dirnext] = edge->prev[dir];
1970     next->dirprev[dirnext] = edge->dirprev[dir];
1971   }
1972 
1973   // update vertex for removal of this edge
1974 
1975   verts[ivert].nedge--;
1976   if (edge->prev[dir] < 0) {
1977     verts[ivert].first = edge->next[dir];
1978     verts[ivert].dirfirst = edge->dirnext[dir];
1979   }
1980   if (edge->next[dir] < 0) {
1981     verts[ivert].last = edge->prev[dir];
1982     verts[ivert].dirlast = edge->dirprev[dir];
1983   }
1984 }
1985 
1986 /* ----------------------------------------------------------------------
1987    remove a vertex and all edges it includes
1988 ------------------------------------------------------------------------- */
1989 
vertex_remove(Vertex * vert)1990 void Cut3d::vertex_remove(Vertex *vert)
1991 {
1992   Edge *edge;
1993 
1994   vert->active = 0;
1995 
1996   int iedge = vert->first;
1997   int dir = vert->dirfirst;
1998   int nedge = vert->nedge;
1999 
2000   for (int i = 0; i < nedge; i++) {
2001     edge = &edges[iedge];
2002     if (dir == 0) edge->nvert--;
2003     else edge->nvert -= 2;
2004     if (edge->nvert == 0) edge->active = 0;
2005     edge->verts[dir] = -1;
2006     iedge = edge->next[dir];
2007     dir = edge->dirnext[dir];
2008   }
2009 }
2010 
2011 /* ----------------------------------------------------------------------
2012    a planar polygon is grazing if it lies entirely in plane of any face of cell
2013    and its normal is outward with respect to cell
2014    return 1 if grazing else 0
2015 ------------------------------------------------------------------------- */
2016 
grazing(Vertex * vert)2017 int Cut3d::grazing(Vertex *vert)
2018 {
2019   int count[6];
2020   double *p;
2021   Edge *edge;
2022 
2023   int iedge = vert->first;
2024   int idir = vert->dirfirst;
2025   int nedge = vert->nedge;
2026 
2027   count[0] = count[1] = count[2] = count[3] = count[4] = count[5] = 0;
2028 
2029   for (int i = 0; i < nedge; i++) {
2030     edge = &edges[iedge];
2031     if (idir == 0) p = edge->p1;
2032     else p = edge->p2;
2033 
2034     if (p[0] == lo[0]) count[0]++;
2035     if (p[0] == hi[0]) count[1]++;
2036     if (p[1] == lo[1]) count[2]++;
2037     if (p[1] == hi[1]) count[3]++;
2038     if (p[2] == lo[2]) count[4]++;
2039     if (p[2] == hi[2]) count[5]++;
2040 
2041     iedge = edge->next[idir];
2042     idir = edge->dirnext[idir];
2043   }
2044 
2045   double *norm = vert->norm;
2046   if (count[0] == nedge && norm[0] < 0.0) return 1;
2047   if (count[1] == nedge && norm[0] > 0.0) return 1;
2048   if (count[2] == nedge && norm[1] < 0.0) return 1;
2049   if (count[3] == nedge && norm[1] > 0.0) return 1;
2050   if (count[4] == nedge && norm[2] < 0.0) return 1;
2051   if (count[5] == nedge && norm[2] > 0.0) return 1;
2052   return 0;
2053 }
2054 
2055 /* ----------------------------------------------------------------------
2056    identify which cell faces point P is on
2057    return list of face IDs (0-5)
2058    list length can be 0,1,2
2059 ------------------------------------------------------------------------- */
2060 
on_faces(double * p,int * faces)2061 int Cut3d::on_faces(double *p, int *faces)
2062 {
2063   int n = 0;
2064   if (p[0] == lo[0]) faces[n++] = 0;
2065   if (p[0] == hi[0]) faces[n++] = 1;
2066   if (p[1] == lo[1]) faces[n++] = 2;
2067   if (p[1] == hi[1]) faces[n++] = 3;
2068   if (p[2] == lo[2]) faces[n++] = 4;
2069   if (p[2] == hi[2]) faces[n++] = 5;
2070   return n;
2071 }
2072 
2073 /* ----------------------------------------------------------------------
2074    identify which cell faces edge between p1,p2 is on
2075    p1,p2 assumed to be on surface or interior of cell
2076    return list of face IDs (0-5)
2077    list length can be 0,1,2
2078 ------------------------------------------------------------------------- */
2079 
which_faces(double * p1,double * p2,int * faces)2080 int Cut3d::which_faces(double *p1, double *p2, int *faces)
2081 {
2082   int n = 0;
2083   if (p1[0] == lo[0] && p2[0] == lo[0]) faces[n++] = 0;
2084   if (p1[0] == hi[0] && p2[0] == hi[0]) faces[n++] = 1;
2085   if (p1[1] == lo[1] && p2[1] == lo[1]) faces[n++] = 2;
2086   if (p1[1] == hi[1] && p2[1] == hi[1]) faces[n++] = 3;
2087   if (p1[2] == lo[2] && p2[2] == lo[2]) faces[n++] = 4;
2088   if (p1[2] == hi[2] && p2[2] == hi[2]) faces[n++] = 5;
2089   return n;
2090 }
2091 
2092 /* ----------------------------------------------------------------------
2093 # extract 2d cell from iface (0-5) of 3d cell
2094 # return lo2d/hi2d = xlo,xhi,ylo,yhi
2095 # for XLO/XHI, keep (y,z) -> (x,y), look at face from inside/outside 3d cell
2096 # for YLO/YHI, keep (x,z) -> (x,y), look at face from outside/inside 3d cell
2097 # for ZLO/ZHI, keep (x,y) -> (x,y), look at face from inside/outside 3d cell
2098 ------------------------------------------------------------------------- */
2099 
face_from_cell(int iface,double * lo2d,double * hi2d)2100 void Cut3d::face_from_cell(int iface, double *lo2d, double *hi2d)
2101 {
2102   if (iface < 2) {
2103     lo2d[0] = lo[1]; hi2d[0] = hi[1];
2104     lo2d[1] = lo[2]; hi2d[1] = hi[2];
2105   } else if (iface < 4) {
2106     lo2d[0] = lo[0]; hi2d[0] = hi[0];
2107     lo2d[1] = lo[2]; hi2d[1] = hi[2];
2108   } else {
2109     lo2d[0] = lo[0]; hi2d[0] = hi[0];
2110     lo2d[1] = lo[1]; hi2d[1] = hi[1];
2111   }
2112 }
2113 
2114 /* ----------------------------------------------------------------------
2115    compress a 3d pt into a 2d pt on iface
2116 ------------------------------------------------------------------------- */
2117 
compress2d(int iface,double * p3,double * p2)2118 void Cut3d::compress2d(int iface, double *p3, double *p2)
2119 {
2120   if (iface < 2) {
2121     p2[0] = p3[1]; p2[1] = p3[2];
2122   } else if (iface < 4) {
2123     p2[0] = p3[0]; p2[1] = p3[2];
2124   } else {
2125     p2[0] = p3[0]; p2[1] = p3[1];
2126   }
2127 }
2128 
2129 /* ----------------------------------------------------------------------
2130    expand a 2d pt into 3d pt on iface with extra coord = value
2131 ------------------------------------------------------------------------- */
2132 
expand2d(int iface,double value,double * p2,double * p3)2133 void Cut3d::expand2d(int iface, double value, double *p2, double *p3)
2134 {
2135   if (iface < 2) {
2136     p3[0] = value; p3[1] = p2[0]; p3[2] = p2[1];
2137   } else if (iface < 4) {
2138     p3[0] = p2[0]; p3[1] = value; p3[2] = p2[1];
2139   } else {
2140     p3[0] = p2[0]; p3[1] = p2[1]; p3[2] = value;
2141   }
2142 }
2143 
2144 /* ----------------------------------------------------------------------
2145    look for edge (x,y) in list of edges
2146    match as (x,y) or (y,x)
2147    if flag, do not match edges that are part of a CTRI (style = CTRI,CTRIFACE)
2148      this is used by add_face() when adding edges of an entire face
2149      this avoids matching an on-face CTRI with norm into cell
2150    error if find edge and it is already part of a vertex in that dir
2151    return = index (0 to nedge-1) if find it, -1 if do not find it
2152    return dir = 0 if matches as (x,y), 1 if matches as (y,x), -1 if no match
2153    return -2 as error if edge already exists in same dir as this one
2154 ------------------------------------------------------------------------- */
2155 
findedge(double * x,double * y,int flag,int & dir)2156 int Cut3d::findedge(double *x, double *y, int flag, int &dir)
2157 {
2158   double *p1,*p2;
2159 
2160   int nedge = edges.n;
2161 
2162   for (int i = 0; i < nedge; i++) {
2163     if (!edges[i].active) continue;
2164     if (flag && (edges[i].style == CTRI || edges[i].style == CTRIFACE))
2165       continue;
2166     p1 = edges[i].p1;
2167     p2 = edges[i].p2;
2168     if (samepoint(x,p1) && samepoint(y,p2)) {
2169       if (edges[i].nvert % 2 == 1) return -2;
2170       dir = 0;
2171       return i;
2172     }
2173     if (samepoint(x,p2) && samepoint(y,p1)) {
2174       if (edges[i].nvert / 2 == 1) return -2;
2175       dir = 1;
2176       return i;
2177     }
2178   }
2179 
2180   dir = -1;
2181   return -1;
2182 }
2183 
2184 /* ----------------------------------------------------------------------
2185    return intersection pt C of line segment A,B in dim with coord value
2186    guaranteed to intersect by caller
2187    C can be same as A or B, will just overwrite
2188 ------------------------------------------------------------------------- */
2189 
between(double * a,double * b,int dim,double value,double * c)2190 void Cut3d::between(double *a, double *b, int dim, double value, double *c)
2191 {
2192   if (dim == 0) {
2193     c[1] = a[1] + (value-a[dim])/(b[dim]-a[dim]) * (b[1]-a[1]);
2194     c[2] = a[2] + (value-a[dim])/(b[dim]-a[dim]) * (b[2]-a[2]);
2195     c[0] = value;
2196   } else if (dim == 1) {
2197     c[0] = a[0] + (value-a[dim])/(b[dim]-a[dim]) * (b[0]-a[0]);
2198     c[2] = a[2] + (value-a[dim])/(b[dim]-a[dim]) * (b[2]-a[2]);
2199     c[1] = value;
2200   } else {
2201     c[0] = a[0] + (value-a[dim])/(b[dim]-a[dim]) * (b[0]-a[0]);
2202     c[1] = a[1] + (value-a[dim])/(b[dim]-a[dim]) * (b[1]-a[1]);
2203     c[2] = value;
2204   }
2205 }
2206 
2207 /* ----------------------------------------------------------------------
2208    return 1 if x,y are same point, else 0
2209 ------------------------------------------------------------------------- */
2210 
samepoint(double * x,double * y)2211 int Cut3d::samepoint(double *x, double *y)
2212 {
2213   if (x[0] == y[0] && x[1] == y[1] && x[2] == y[2]) return 1;
2214   return 0;
2215 }
2216 
2217 /* ----------------------------------------------------------------------
2218    return 0-7 if pt is a corner pt of grid cell
2219    else return -1
2220 ------------------------------------------------------------------------- */
2221 
corner(double * pt)2222 int Cut3d::corner(double *pt)
2223 {
2224   if (pt[2] == lo[2]) {
2225     if (pt[1] == lo[1]) {
2226       if (pt[0] == lo[0]) return 0;
2227       else if (pt[0] == hi[0]) return 1;
2228     } else if (pt[1] == hi[1]) {
2229       if (pt[0] == lo[0]) return 2;
2230       else if (pt[0] == hi[0]) return 3;
2231     }
2232   } else if (pt[2] == hi[2]) {
2233     if (pt[1] == lo[1]) {
2234       if (pt[0] == lo[0]) return 4;
2235       else if (pt[0] == hi[0]) return 5;
2236     } else if (pt[1] == hi[1]) {
2237       if (pt[0] == lo[0]) return 6;
2238       else if (pt[0] == hi[0]) return 7;
2239     }
2240   }
2241 
2242   return -1;
2243 }
2244 
2245 /* ----------------------------------------------------------------------
2246    move point within epsilon of any cell face to be on cell faces
2247 ------------------------------------------------------------------------- */
2248 
move_to_faces(double * pt)2249 void Cut3d::move_to_faces(double *pt)
2250 {
2251   if (fabs(pt[0]-lo[0]) < epsilon) pt[0] = lo[0];
2252   if (fabs(pt[0]-hi[0]) < epsilon) pt[0] = hi[0];
2253   if (fabs(pt[1]-lo[1]) < epsilon) pt[1] = lo[1];
2254   if (fabs(pt[1]-hi[1]) < epsilon) pt[1] = hi[1];
2255   if (fabs(pt[2]-lo[2]) < epsilon) pt[2] = lo[2];
2256   if (fabs(pt[2]-hi[2]) < epsilon) pt[2] = hi[2];
2257 }
2258 
2259 /* ----------------------------------------------------------------------
2260    check if pt is inside or outside or on cell border
2261    return EXTERIOR,BORDER,INTERIOR
2262 ------------------------------------------------------------------------- */
2263 
ptflag(double * pt)2264 int Cut3d::ptflag(double *pt)
2265 {
2266   double x = pt[0];
2267   double y = pt[1];
2268   double z = pt[2];
2269   if (x < lo[0] || x > hi[0] || y < lo[1] || y > hi[1] ||
2270       z < lo[2] || z > hi[2]) return EXTERIOR;
2271   if (x > lo[0] && x < hi[0] && y > lo[1] && y < hi[1] &&
2272       z > lo[2] && z < hi[2]) return INTERIOR;
2273   return BORDER;
2274 }
2275 
2276 /* ----------------------------------------------------------------------
2277    print out cell info for cell which failed at cut/split operation
2278 ------------------------------------------------------------------------- */
2279 
failed_cell()2280 void Cut3d::failed_cell()
2281 {
2282   printf("Cut3d failed on proc %d in cell ID: " CELLINT_FORMAT "\n",comm->me,id);
2283   Surf::Tri *tris = surf->tris;
2284   printf("  lo corner %g %g %g\n",lo[0],lo[1],lo[2]);
2285   printf("  hi corner %g %g %g\n",hi[0],hi[1],hi[2]);
2286   printf("  # of surfs = %d out of " BIGINT_FORMAT "\n",nsurf,surf->nsurf);
2287   for (int i = 0; i < nsurf; i++) {
2288     printf("  surf " SURFINT_FORMAT ":\n",tris[surfs[i]].id);
2289     printf("     p1: %g %g %g\n",
2290 	   tris[surfs[i]].p1[0],tris[surfs[i]].p1[1],tris[surfs[i]].p1[2]);
2291     printf("     p2: %g %g %g\n",
2292 	   tris[surfs[i]].p2[0],tris[surfs[i]].p2[1],tris[surfs[i]].p2[2]);
2293     printf("     p3: %g %g %g\n",
2294 	   tris[surfs[i]].p3[0],tris[surfs[i]].p3[1],tris[surfs[i]].p3[2]);
2295   }
2296 }
2297 
2298 /* ----------------------------------------------------------------------
2299 ------------------------------------------------------------------------- */
2300 
print_bpg(const char * str)2301 void Cut3d::print_bpg(const char *str)
2302 {
2303   int iedge,dir,newedge,newdir,prevedge,prevdir;
2304   double *p1,*p2;
2305 
2306   printf("%s " CELLINT_FORMAT "\n",str,id);
2307   printf("  Sizes: %d %d\n",verts.n,edges.n);
2308 
2309   printf("  Verts:\n");
2310   for (int i = 0; i < verts.n; i++) {
2311     if (verts[i].active == 0) continue;
2312 
2313     printf("   %d %d %d %d:",i,
2314            verts[i].active,verts[i].style,verts[i].label);
2315 
2316     printf(" edges [");
2317     iedge = verts[i].first;
2318     dir = verts[i].dirfirst;
2319     for (int j = 0; j < verts[i].nedge; j++) {
2320       printf("%d",iedge);
2321       if (j < verts[i].nedge-1) printf(" ");
2322       newedge = edges[iedge].next[dir];
2323       newdir = edges[iedge].dirnext[dir];
2324       iedge = newedge;
2325       dir = newdir;
2326     }
2327     printf("]");
2328 
2329     printf(" dirs [");
2330     iedge = verts[i].first;
2331     dir = verts[i].dirfirst;
2332     for (int j = 0; j < verts[i].nedge; j++) {
2333       printf("%d",dir);
2334       if (j < verts[i].nedge-1) printf(" ");
2335       newedge = edges[iedge].next[dir];
2336       newdir = edges[iedge].dirnext[dir];
2337       iedge = newedge;
2338       dir = newdir;
2339     }
2340     printf("]");
2341 
2342     printf(" edgelens [");
2343     iedge = verts[i].first;
2344     dir = verts[i].dirfirst;
2345     for (int j = 0; j < verts[i].nedge; j++) {
2346       p1 = edges[iedge].p1;
2347       p2 = edges[iedge].p2;
2348       double dx = p1[0]-p2[0];
2349       double dy = p1[1]-p2[1];
2350       double dz = p1[2]-p2[2];
2351       double delta = sqrt(dx*dx+dy*dy+dz*dz);
2352       printf("%g",delta);
2353       if (j < verts[i].nedge-1) printf(" ");
2354       newedge = edges[iedge].next[dir];
2355       newdir = edges[iedge].dirnext[dir];
2356       iedge = newedge;
2357       dir = newdir;
2358     }
2359     printf("]");
2360 
2361     printf(" samept [");
2362     iedge = verts[i].first;
2363     dir = verts[i].dirfirst;
2364     for (int j = 0; j < verts[i].nedge; j++) {
2365       if (j > 0) {
2366         prevedge = edges[iedge].prev[dir];
2367         prevdir = edges[iedge].dirprev[dir];
2368       } else {
2369         prevedge = verts[i].last;
2370         prevdir = verts[i].dirlast;
2371       }
2372       if (dir == 0) p1 = edges[iedge].p1;
2373       else p1 = edges[iedge].p2;
2374       if (prevdir == 0) p2 = edges[prevedge].p2;
2375       else p2 = edges[prevedge].p1;
2376       printf("%d",samepoint(p1,p2));
2377       if (j < verts[i].nedge-1) printf(" ");
2378       newedge = edges[iedge].next[dir];
2379       newdir = edges[iedge].dirnext[dir];
2380       iedge = newedge;
2381       dir = newdir;
2382     }
2383     printf("]");
2384 
2385     if (verts[i].norm) {
2386       printf(" norm [%g %g %g]\n",
2387              verts[i].norm[0],verts[i].norm[1],verts[i].norm[2]);
2388     } else printf(" [NULL]\n");
2389   }
2390 
2391   printf("  Edges:\n");
2392   for (int i = 0; i < edges.n; i++) {
2393     if (edges[i].active == 0) continue;
2394 
2395     printf("   %d %d %d",i,edges[i].active,edges[i].style);
2396     printf(" (%g %g %g)",edges[i].p1[0],edges[i].p1[1],edges[i].p1[2]);
2397     printf(" (%g %g %g)",edges[i].p2[0],edges[i].p2[1],edges[i].p2[2]);
2398     if (edges[i].nvert == 0) printf(" [-1]");
2399     if (edges[i].nvert == 1) {
2400       printf(" [%d]",edges[i].verts[0]);
2401       printf(" p1: [%d %d]",edges[i].prev[0],edges[i].dirprev[0]);
2402       printf(" n1: [%d %d]",edges[i].next[0],edges[i].dirnext[0]);
2403     }
2404     if (edges[i].nvert == 2) {
2405       printf(" [%d]",edges[i].verts[1]);
2406       printf(" p1: [%d %d]",edges[i].prev[1],edges[i].dirprev[1]);
2407       printf(" n1: [%d %d]",edges[i].next[1],edges[i].dirnext[1]);
2408     }
2409     if (edges[i].nvert == 3) {
2410       printf(" [%d %d]",edges[i].verts[0],edges[i].verts[1]);
2411       printf(" p1: [%d %d]",edges[i].prev[0],edges[i].dirprev[0]);
2412       printf(" n1: [%d %d]",edges[i].next[0],edges[i].dirnext[0]);
2413       printf(" p2: [%d %d]",edges[i].prev[1],edges[i].dirprev[1]);
2414       printf(" n2: [%d %d]",edges[i].next[1],edges[i].dirnext[1]);
2415     }
2416     if (edges[i].nvert > 3) printf(" [BIG %d]",edges[i].nvert);
2417     printf("\n");
2418   }
2419 }
2420 
2421 /* ----------------------------------------------------------------------
2422 ------------------------------------------------------------------------- */
2423 
print_loops()2424 void Cut3d::print_loops()
2425 {
2426   printf("LOOP " CELLINT_FORMAT "\n",id);
2427   printf("  loops %d\n",loops.n);
2428   for (int i = 0; i < loops.n; i++) {
2429     printf("  loop %d\n",i);
2430     printf("    flag %d\n",loops[i].flag);
2431     printf("    volume %g\n",loops[i].volume);
2432     printf("    nverts %d\n",loops[i].n);
2433     printf("    verts: [");
2434     int ivert = loops[i].first;
2435     for (int j = 0; j < loops[i].n; j++) {
2436       printf("%d ",ivert);
2437       ivert = verts[ivert].next;
2438     }
2439     printf("]\n");
2440   }
2441 }
2442