BRL-CAD
nmg_class.c
Go to the documentation of this file.
1 /* N M G _ C L A S S . C
2  * BRL-CAD
3  *
4  * Copyright (c) 1993-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @addtogroup nmg */
21 /** @{ */
22 /** @file primitives/nmg/nmg_class.c
23  *
24  * Subroutines to classify one object with respect to another.
25  * Possible classifications are AinB, AoutB, AinBshared, AinBanti.
26  *
27  * The first set of routines (nmg_class_pt_xxx) are used to classify
28  * an arbitrary point specified by its Cartesian coordinates, against
29  * various kinds of NMG elements. nmg_class_pt_f() and
30  * nmg_class_pt_s() are available to applications programmers for
31  * direct use, and have no side effects.
32  *
33  * The second set of routines (class_xxx_vs_s) are used only to
34  * support the routine nmg_class_shells() mid-way through the NMG
35  * Boolean algorithm. These routines operate with special knowledge
36  * about the state of the data structures after the intersector has
37  * been called, and depends on all geometric equivalences to have been
38  * converted into shared topology.
39  *
40  */
41 /** @} */
42 
43 #include "common.h"
44 
45 #include <math.h>
46 #include "bio.h"
47 
48 #include "vmath.h"
49 #include "nmg.h"
50 #include "raytrace.h"
51 #include "plot3.h"
52 
53 
54 #define MAX_DIR_TRYS 10
55 
56 /* XXX These should go the way of the dodo bird. */
57 #define INSIDE 32
58 #define ON_SURF 64
59 #define OUTSIDE 128
60 
61 extern int nmg_class_nothing_broken;
62 
63 /* Structure for keeping track of how close a point/vertex is to
64  * its potential neighbors.
65  */
66 struct neighbor {
67  union {
68  const struct vertexuse *vu;
69  const struct edgeuse *eu;
70  } p;
71  /* XXX For efficiency, this should have been dist_sq */
72  fastf_t dist; /* distance from point to neighbor */
73  int nmg_class; /* Classification of this neighbor */
74 };
75 
76 
77 static void nmg_class_pt_e(struct neighbor *closest,
78  const point_t pt, const struct edgeuse *eu,
79  const struct bn_tol *tol);
80 static void nmg_class_pt_l(struct neighbor *closest,
81  const point_t pt, const struct loopuse *lu,
82  const struct bn_tol *tol);
83 static int class_vu_vs_s(struct vertexuse *vu, struct shell *sB,
84  char **classlist, const struct bn_tol *tol);
85 static int class_eu_vs_s(struct edgeuse *eu, struct shell *s,
86  char **classlist, const struct bn_tol *tol);
87 static int class_lu_vs_s(struct loopuse *lu, struct shell *s,
88  char **classlist, const struct bn_tol *tol);
89 static void class_fu_vs_s(struct faceuse *fu, struct shell *s,
90  char **classlist, const struct bn_tol *tol);
91 
92 /**
93  * Convert classification status to string.
94  */
95 const char *
96 nmg_class_status(int status)
97 {
98  switch (status) {
99  case INSIDE:
100  return "INSIDE";
101  case OUTSIDE:
102  return "OUTSIDE";
103  case ON_SURF:
104  return "ON_SURF";
105  }
106  return "BOGUS_CLASS_STATUS";
107 }
108 
109 
110 void
111 nmg_pr_class_status(char *prefix, int status)
112 {
113  bu_log("%s has classification status %s\n",
114  prefix, nmg_class_status(status));
115 }
116 
117 
118 /**
119  * The ray hit an edge. We have to decide whether it hit the
120  * edge, or missed it.
121  *
122  * XXX DANGER: This routine does not work well.
123  *
124  * Called by -
125  * nmg_class_pt_e
126  */
127 static void
128 joint_hitmiss2(struct neighbor *closest, const struct edgeuse *eu, int code)
129 {
130  const struct edgeuse *eu_rinf;
131 
132  eu_rinf = nmg_faceradial(eu);
133 
134  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
135  bu_log("joint_hitmiss2\n");
136  }
137  if (eu_rinf == eu) {
138  bu_bomb("joint_hitmiss2: radial eu is me?\n");
139  }
140  /* If eu_rinf == eu->eumate_p, that's OK, this is a dangling face,
141  * or a face that has not been fully hooked up yet.
142  * It's OK as long as the orientations both match.
143  */
144  if (eu->up.lu_p->orientation == eu_rinf->up.lu_p->orientation) {
145  if (eu->up.lu_p->orientation == OT_SAME) {
146  closest->nmg_class = NMG_CLASS_AonBshared;
147  } else if (eu->up.lu_p->orientation == OT_OPPOSITE) {
148  closest->nmg_class = NMG_CLASS_AoutB;
149  } else {
150  nmg_pr_lu_briefly(eu->up.lu_p, (char *)0);
151  bu_bomb("joint_hitmiss2: bad loop orientation\n");
152  }
153  closest->dist = 0.0;
154  switch (code) {
155  case 0:
156  /* Hit the edge */
157  closest->p.eu = eu;
158  break;
159  case 1:
160  /* Hit first vertex */
161  closest->p.vu = eu->vu_p;
162  break;
163  case 2:
164  /* Hit second vertex */
165  closest->p.vu = BU_LIST_PNEXT_CIRC(edgeuse, eu)->vu_p;
166  break;
167  }
168  if (RTG.NMG_debug & DEBUG_CLASSIFY) bu_log("\t\t%s\n", nmg_class_name(closest->nmg_class));
169  return;
170  }
171 
172  /* XXX What goes here? */
173  bu_bomb("nmg_class.c/joint_hitmiss2() unable to resolve ray/edge hit\n");
174  bu_log("joint_hitmiss2: NO CODE HERE, assuming miss\n");
175 
176  if (RTG.NMG_debug & (DEBUG_CLASSIFY|DEBUG_NMGRT)) {
177  nmg_euprint("Hard question time", eu);
178  bu_log(" eu_rinf=%p, eu->eumate_p=%p, eu=%p\n", (void *)eu_rinf, (void *)eu->eumate_p, (void *)eu);
179  bu_log(" eu lu orient=%s, eu_rinf lu orient=%s\n",
180  nmg_orientation(eu->up.lu_p->orientation),
181  nmg_orientation(eu_rinf->up.lu_p->orientation));
182  }
183 }
184 
185 
186 /**
187  * XXX DANGER: This routine does not work well.
188  *
189  * Given the Cartesian coordinates of a point, determine if the point
190  * is closer to this edgeuse than the previous neighbor(s) as given
191  * in the "closest" structure.
192  * If it is, record how close the point is, and whether it is IN, ON, or OUT.
193  * The neighbor's "p" element will indicate the edgeuse or vertexuse closest.
194  *
195  * This routine should print everything indented two tab stops.
196  *
197  * Implicit Return -
198  * Updated "closest" structure if appropriate.
199  *
200  * Called by -
201  * nmg_class_pt_l
202  */
203 static void
204 nmg_class_pt_e(struct neighbor *closest, const fastf_t *pt, const struct edgeuse *eu, const struct bn_tol *tol)
205 {
206  vect_t ptvec; /* vector from lseg to pt */
207  vect_t left; /* vector left of edge -- into inside of loop */
208  const fastf_t *eupt;
209  const fastf_t *matept;
210  point_t pca; /* point of closest approach from pt to edge lseg */
211  fastf_t dist; /* distance from pca to pt */
212  fastf_t dot;
213  int code;
214 
215  NMG_CK_EDGEUSE(eu);
216  NMG_CK_EDGEUSE(eu->eumate_p);
217  NMG_CK_VERTEX_G(eu->vu_p->v_p->vg_p);
218  NMG_CK_VERTEX_G(eu->eumate_p->vu_p->v_p->vg_p);
219  BN_CK_TOL(tol);
220  VSETALL(left, 0);
221 
222  eupt = eu->vu_p->v_p->vg_p->coord;
223  matept = eu->eumate_p->vu_p->v_p->vg_p->coord;
224 
225  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
226  VPRINT("nmg_class_pt_e\tPt", pt);
227  nmg_euprint(" \tvs. eu", eu);
228  }
229  /*
230  * Note that "pca" can be one of the edge endpoints,
231  * even if "pt" is far, far away. This can be confusing.
232  *
233  * Some compilers don't get that fastf_t * and point_t are related
234  * So we have to pass the whole bloody mess for the point arguments.
235  */
236  code = bn_dist_pt3_lseg3(&dist, pca, eu->vu_p->v_p->vg_p->coord,
237  eu->eumate_p->vu_p->v_p->vg_p->coord,
238  pt, tol);
239  if (code <= 0) dist = 0;
240  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
241  bu_log(" \tcode=%d, dist: %g\n", code, dist);
242  VPRINT(" \tpca:", pca);
243  }
244 
245  if (dist >= closest->dist + tol->dist) {
246  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
247  bu_log("\t\tskipping, earlier eu is closer (%g)\n", closest->dist);
248  }
249  return;
250  }
251  if (dist >= closest->dist - tol->dist) {
252  /*
253  * Distances are very nearly the same.
254  *
255  * If closest eu and this eu are from the same lu,
256  * and the earlier result was OUT, that's all it takes
257  * to decide things.
258  */
259  if (closest->p.eu && closest->p.eu->up.lu_p == eu->up.lu_p) {
260  if (closest->nmg_class == NMG_CLASS_AoutB ||
261  closest->nmg_class == NMG_CLASS_AonBshared ||
262  closest->nmg_class == NMG_CLASS_AonBanti) {
263  if (RTG.NMG_debug & DEBUG_CLASSIFY)
264  bu_log("\t\tSkipping, earlier eu from same lu at same dist, is OUT or ON.\n");
265  return;
266  }
267  if (RTG.NMG_debug & DEBUG_CLASSIFY)
268  bu_log("\t\tEarlier eu from same lu at same dist, is IN, continue processing.\n");
269  } else {
270  /*
271  * If this is now a new lu, it is more complicated.
272  * If "closest" result so far was a NMG_CLASS_AinB or
273  * or NMG_CLASS_AonB, then keep it,
274  * otherwise, replace that result with whatever we find
275  * here. Logic: Two touching loops, one concave ("A")
276  * which wraps around part of the other ("B"), with the
277  * point inside A near the contact with B. If loop B is
278  * processed first, the closest result will be NMG_CLASS_AoutB,
279  * and when loop A is visited the distances will be exactly
280  * equal, not giving A a chance to claim its hit.
281  */
282  if (closest->nmg_class == NMG_CLASS_AinB ||
283  closest->nmg_class == NMG_CLASS_AonBshared ||
284  closest->nmg_class == NMG_CLASS_AonBanti) {
285  if (RTG.NMG_debug & DEBUG_CLASSIFY)
286  bu_log("\t\tSkipping, earlier eu from other another lu at same dist, is IN or ON\n");
287  return;
288  }
289  if (RTG.NMG_debug & DEBUG_CLASSIFY)
290  bu_log("\t\tEarlier eu from other lu at same dist, is OUT, continue processing.\n");
291  }
292  }
293 
294  /* Plane hit point is closer to this edgeuse than previous one(s) */
295  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
296  bu_log("\t\tCLOSER dist=%g (closest=%g), tol=%g\n",
297  dist, closest->dist, tol->dist);
298  }
299 
300  if (*eu->up.magic_p != NMG_LOOPUSE_MAGIC ||
301  *eu->up.lu_p->up.magic_p != NMG_FACEUSE_MAGIC) {
302  bu_log("Trying to classify a pt (%g, %g, %g)\n\tvs a wire edge? (%g, %g, %g -> %g, %g, %g)\n",
303  V3ARGS(pt),
304  V3ARGS(eupt),
305  V3ARGS(matept)
306  );
307  return;
308  }
309 
310  if (code <= 2) {
311  /* code==0: The point is ON the edge! */
312  /* code==1 or 2: The point is ON a vertex! */
313  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
314  bu_log("\t\tThe point is ON the edge, calling joint_hitmiss2()\n");
315  }
316  joint_hitmiss2(closest, eu, code);
317  return;
318  } else {
319  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
320  bu_log("\t\tThe point is not on the edge\n");
321  }
322  }
323 
324  /* The point did not lie exactly ON the edge, calculate in/out */
325 
326  /* Get vector which lies on the plane, and points
327  * left, towards the interior of the loop, regardless of
328  * whether it's an interior (OT_OPPOSITE) or exterior (OT_SAME) loop.
329  */
330  if (nmg_find_eu_leftvec(left, eu) < 0) bu_bomb("nmg_class_pt_e() bad LEFT vector\n");
331 
332  VSUB2(ptvec, pt, pca); /* pt - pca */
333  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
334  VPRINT("\t\tptvec unnorm", ptvec);
335  VPRINT("\t\tleft", left);
336  }
337  VUNITIZE(ptvec);
338 
339  dot = VDOT(left, ptvec);
340  if (NEAR_ZERO(dot, RT_DOT_TOL)) {
341  if (RTG.NMG_debug & DEBUG_CLASSIFY)
342  bu_log("\t\tpt lies on line of edge, outside verts. Skipping this edge\n");
343  goto out;
344  }
345 
346  if (dot > -SMALL_FASTF) {
347  closest->dist = dist;
348  closest->p.eu = eu;
349  closest->nmg_class = NMG_CLASS_AinB;
350  if (RTG.NMG_debug & DEBUG_CLASSIFY)
351  bu_log("\t\tpt is left of edge, INSIDE loop, dot=%g\n", dot);
352  } else {
353  closest->dist = dist;
354  closest->p.eu = eu;
355  closest->nmg_class = NMG_CLASS_AoutB;
356  if (RTG.NMG_debug & DEBUG_CLASSIFY)
357  bu_log("\t\tpt is right of edge, OUTSIDE loop\n");
358  }
359 
360 out:
361  /* XXX Temporary addition for chasing bug in Bradley r5 */
362  /* XXX Should at least add DEBUG_PLOTEM check, later */
363  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
364  struct faceuse *fu;
365  char buf[128];
366  static int num;
367  FILE *fp;
368  long *bits;
369  point_t mid_pt;
370  point_t left_pt;
371  fu = eu->up.lu_p->up.fu_p;
372  bits = (long *)bu_calloc(nmg_find_model(&fu->l.magic)->maxindex, sizeof(long), "bits[]");
373  sprintf(buf, "faceclass%d.plot3", num++);
374  if ((fp = fopen(buf, "wb")) == NULL)
375  bu_bomb(buf);
376  nmg_pl_fu(fp, fu, bits, 0, 0, 255); /* blue */
377  pl_color(fp, 0, 255, 0); /* green */
378  pdv_3line(fp, pca, pt);
379  pl_color(fp, 255, 0, 0); /* red */
380  VADD2SCALE(mid_pt, eupt, matept, 0.5);
381  VJOIN1(left_pt, mid_pt, 500, left);
382  pdv_3line(fp, mid_pt, left_pt);
383  fclose(fp);
384  bu_free((char *)bits, "bits[]");
385  bu_log("wrote %s\n", buf);
386  }
387 }
388 
389 
390 /**
391  * XXX DANGER: This routine does not work well.
392  *
393  * Given the coordinates of a point which lies on the plane of the face
394  * containing a loopuse, determine if the point is IN, ON, or OUT of the
395  * area enclosed by the loop.
396  *
397  * Implicit Return -
398  * Updated "closest" structure if appropriate.
399  *
400  * Called by -
401  * nmg_class_pt_loop()
402  * from: nmg_extrude.c / nmg_fix_overlapping_loops()
403  * nmg_classify_lu_lu()
404  * from: nmg_misc.c / nmg_split_loops_handler()
405  */
406 static void
407 nmg_class_pt_l(struct neighbor *closest, const fastf_t *pt, const struct loopuse *lu, const struct bn_tol *tol)
408 {
409  vect_t delta;
410  pointp_t lu_pt;
411  fastf_t dist;
412  struct edgeuse *eu;
413  struct loop_g *lg;
414 
415  NMG_CK_LOOPUSE(lu);
416  NMG_CK_LOOP(lu->l_p);
417  lg = lu->l_p->lg_p;
418  NMG_CK_LOOP_G(lg);
419 
420  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
421  VPRINT("nmg_class_pt_l\tPt:", pt);
422  }
423 
424  if (*lu->up.magic_p != NMG_FACEUSE_MAGIC)
425  return;
426 
427  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
428  plane_t peqn;
429  nmg_pr_lu_briefly(lu, 0);
430  NMG_GET_FU_PLANE(peqn, lu->up.fu_p);
431  HPRINT("\tplane eqn", peqn);
432  }
433 
434  if (V3PT_OUT_RPP_TOL(pt, lg->min_pt, lg->max_pt, tol->dist)) {
435  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
436  bu_log("\tPoint is outside loop RPP\n");
437  }
438  closest->nmg_class = NMG_CLASS_AoutB;
439  return;
440  }
441  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_EDGEUSE_MAGIC) {
442  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
443  nmg_class_pt_e(closest, pt, eu, tol);
444  /* If point lies ON edge, we are done */
445  if (closest->nmg_class == NMG_CLASS_AonBanti) {
446  break;
447  } else if (closest->nmg_class == NMG_CLASS_AonBshared) {
448  bu_bomb("nmg_class_pt_l(): nmg_class_pt_e returned AonBshared but can only return AonBanti\n");
449  }
450  }
451  } else if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC) {
452  register struct vertexuse *vu;
453  vu = BU_LIST_FIRST(vertexuse, &lu->down_hd);
454  lu_pt = vu->v_p->vg_p->coord;
455  VSUB2(delta, pt, lu_pt);
456  if ((dist = MAGNITUDE(delta)) < closest->dist) {
457  if (lu->orientation == OT_OPPOSITE) {
458  closest->nmg_class = NMG_CLASS_AoutB;
459  } else if (lu->orientation == OT_SAME) {
460  closest->nmg_class = NMG_CLASS_AonBanti;
461  } else {
462  nmg_pr_orient(lu->orientation, "\t");
463  bu_bomb("nmg_class_pt_l: bad orientation for face loopuse\n");
464  }
465  if (RTG.NMG_debug & DEBUG_CLASSIFY)
466  bu_log("\t\t closer to loop pt (%g, %g, %g)\n",
467  V3ARGS(lu_pt));
468 
469  closest->dist = dist;
470  closest->p.vu = vu;
471  }
472  } else {
473  bu_bomb("nmg_class_pt_l() bad child of loopuse\n");
474  }
475  if (RTG.NMG_debug & DEBUG_CLASSIFY)
476  bu_log("nmg_class_pt_l\treturning, closest=%g %s\n",
477  closest->dist, nmg_class_name(closest->nmg_class));
478 }
479 
480 
481 /**
482  * This is intended as an internal routine to support nmg_lu_reorient().
483  *
484  * Given a loopuse in a face, pick one of its vertexuses, and classify
485  * that point with respect to all the rest of the loopuses in the face.
486  * The containment status of that point is the status of the loopuse.
487  *
488  * If the first vertex chosen is "ON" another loop boundary,
489  * choose the next vertex and try again. Only return an "ON"
490  * status if _all_ the vertices are ON.
491  *
492  * The point is "A", and the face is "B".
493  *
494  * Returns -
495  * NMG_CLASS_AinB lu is INSIDE the area of the face.
496  * NMG_CLASS_AonBshared ALL of lu is ON other loop boundaries.
497  * NMG_CLASS_AoutB lu is OUTSIDE the area of the face.
498  *
499  * Called by -
500  * nmg_mod.c, nmg_lu_reorient()
501  */
502 int
503 nmg_class_lu_fu(const struct loopuse *lu, const struct bn_tol *tol)
504 {
505  const struct faceuse *fu;
506  struct vertexuse *vu;
507  const struct vertex_g *vg;
508  struct edgeuse *eu;
509  struct edgeuse *eu_first;
510  fastf_t dist;
511  plane_t n;
512  int nmg_class;
513 
514  NMG_CK_LOOPUSE(lu);
515  BN_CK_TOL(tol);
516 
517  fu = lu->up.fu_p;
518  NMG_CK_FACEUSE(fu);
519  NMG_CK_FACE(fu->f_p);
520  NMG_CK_FACE_G_PLANE(fu->f_p->g.plane_p);
521 
522  if (RTG.NMG_debug & DEBUG_CLASSIFY)
523  bu_log("nmg_class_lu_fu(lu=%p) START\n", (void *)lu);
524 
525  /* Pick first vertex in loopuse, for point */
526  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC) {
527  vu = BU_LIST_FIRST(vertexuse, &lu->down_hd);
528  eu = (struct edgeuse *)NULL;
529  } else {
530  eu = BU_LIST_FIRST(edgeuse, &lu->down_hd);
531  NMG_CK_EDGEUSE(eu);
532  vu = eu->vu_p;
533  }
534  eu_first = eu;
535 again:
536  NMG_CK_VERTEXUSE(vu);
537  NMG_CK_VERTEX(vu->v_p);
538  vg = vu->v_p->vg_p;
539  NMG_CK_VERTEX_G(vg);
540 
541  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
542  VPRINT("nmg_class_lu_fu\tPt:", vg->coord);
543  }
544 
545  /* Validate distance from point to plane */
546  NMG_GET_FU_PLANE(n, fu);
547  if ((dist = fabs(DIST_PT_PLANE(vg->coord, n))) > tol->dist) {
548  bu_log("nmg_class_lu_fu() ERROR, point (%g, %g, %g) not on face, dist=%g\n",
549  V3ARGS(vg->coord), dist);
550  }
551 
552  /* find the closest approach in this face to the projected point */
553  nmg_class = nmg_class_pt_fu_except(vg->coord, fu, lu,
554  NULL, NULL, NULL, 0, 0, tol);
555 
556  /* If this vertex lies ON loop edge, must check all others. */
557  if (nmg_class == NMG_CLASS_AonBshared) {
558  if (!eu_first) {
559  /* was self-loop, nothing more to test */
560  } else {
561  eu = BU_LIST_PNEXT_CIRC(edgeuse, &eu->l);
562  if (eu != eu_first) {
563  vu = eu->vu_p;
564  goto again;
565  }
566  /* all match, call it "ON" */
567  }
568  }
569 
570  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
571  bu_log("nmg_class_lu_fu(lu=%p) END, ret=%s\n",
572  (void *)lu,
573  nmg_class_name(nmg_class));
574  }
575  return nmg_class;
576 }
577 
578 
579 /* Ray direction vectors for Jordan curve algorithm */
580 static const point_t nmg_good_dirs[MAX_DIR_TRYS] = {
581 #if 1
582  {3, 2, 1}, /* Normally the first dir */
583 #else
584  {1, 0, 0}, /* DEBUG: Make this first dir to wring out ray-tracer */
585 #endif
586  {1, 0, 0},
587  {0, 1, 0},
588  {0, 0, 1},
589  {1, 1, 1},
590  {-3, -2, -1},
591  {-1, 0, 0},
592  {0, -1, 0},
593  {0, 0, -1},
594  {-1, -1, -1}
595 };
596 
597 
598 /**
599  * This is intended as a general user interface routine.
600  * Given the Cartesian coordinates for a point in space,
601  * return the classification for that point with respect to a shell.
602  *
603  * The algorithm used is to fire a ray from the point, and count
604  * the number of times it crosses a face.
605  *
606  * The flag "in_or_out_only" specifies that the point is known to not
607  * be on the shell, therefore only returns of NMG_CLASS_AinB or
608  * NMG_CLASS_AoutB are acceptable.
609  *
610  * The point is "A", and the face is "B".
611  *
612  * Returns -
613  * NMG_CLASS_AinB pt is INSIDE the volume of the shell.
614  * NMG_CLASS_AonBshared pt is ON the shell boundary.
615  * NMG_CLASS_AoutB pt is OUTSIDE the volume of the shell.
616  */
617 int
618 nmg_class_pt_s(const fastf_t *pt, const struct shell *s, const int in_or_out_only, const struct bn_tol *tol)
619 {
620  const struct faceuse *fu;
621  struct model *m;
622  long *faces_seen = NULL;
623  vect_t region_diagonal;
624  fastf_t region_diameter;
625  int nmg_class = 0;
626  vect_t projection_dir = VINIT_ZERO;
627  int tries = 0;
628  struct xray rp;
629  fastf_t model_bb_max_width;
630  point_t m_min_pt, m_max_pt; /* nmg model min and max points */
631 
632  m = s->r_p->m_p;
633  NMG_CK_MODEL(m);
634  NMG_CK_SHELL(s);
635  BN_CK_TOL(tol);
636 
637  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
638  bu_log("nmg_class_pt_s(): pt=(%g, %g, %g), s=%p\n",
639  V3ARGS(pt), (void *)s);
640  }
641  if (V3PT_OUT_RPP_TOL(pt, s->sa_p->min_pt, s->sa_p->max_pt, tol->dist)) {
642  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
643  bu_log("nmg_class_pt_s(): OUT, point not in RPP\n");
644  }
645  return NMG_CLASS_AoutB;
646  }
647 
648  if (!in_or_out_only) {
649  faces_seen = (long *)bu_calloc(m->maxindex, sizeof(long), "nmg_class_pt_s faces_seen[]");
650  /*
651  * First pass: Try hard to see if point is ON a face.
652  */
653  for (BU_LIST_FOR(fu, faceuse, &s->fu_hd)) {
654  plane_t n;
655 
656  /* If this face processed before, skip on */
657  if (NMG_INDEX_TEST(faces_seen, fu->f_p)) {
658  continue;
659  }
660  /* Only consider the outward pointing faceuses */
661  if (fu->orientation != OT_SAME) {
662  continue;
663  }
664 
665  /* See if this point lies on this face */
666  NMG_GET_FU_PLANE(n, fu);
667  if ((fabs(DIST_PT_PLANE(pt, n))) < tol->dist) {
668  /* Point lies on this plane, it may be possible to
669  * short circuit everything.
670  */
671  nmg_class = nmg_class_pt_fu_except(pt, fu, (struct loopuse *)0,
672  (void (*)(struct edgeuse *, point_t, const char *))NULL,
673  (void (*)(struct vertexuse *, point_t, const char *))NULL,
674  (const char *)NULL, 0, 0, tol);
675  if (nmg_class == NMG_CLASS_AonBshared) {
676  bu_bomb("nmg_class_pt_s(): function nmg_class_pt_fu_except returned AonBshared when it can only return AonBanti\n");
677  }
678  if (nmg_class == NMG_CLASS_AonBshared) {
679  /* Point is ON face, therefore it must be
680  * ON the shell also.
681  */
682  nmg_class = NMG_CLASS_AonBshared;
683  goto out;
684  }
685  if (nmg_class == NMG_CLASS_AonBanti) {
686  /* Point is ON face, therefore it must be
687  * ON the shell also.
688  */
689  nmg_class = NMG_CLASS_AonBanti;
690  goto out;
691  }
692 
693  if (nmg_class == NMG_CLASS_AinB) {
694  /* Point is IN face, therefor it must be
695  * ON the shell also.
696  */
697  nmg_class = NMG_CLASS_AonBanti;
698  goto out;
699  }
700  /* Point is OUTside face, it's undecided. */
701  }
702 
703  /* Mark this face as having been processed */
704  NMG_INDEX_SET(faces_seen, fu->f_p);
705  }
706  }
707 
708  /* If we got here, the point isn't ON any of the faces.
709  * Time to do the Jordan Curve Theorem. We fire an arbitrary
710  * ray and count the number of crossings (in the positive direction)
711  * If that number is even, we're outside the shell, otherwise we're
712  * inside the shell.
713  */
714  NMG_CK_REGION_A(s->r_p->ra_p);
715  VSUB2(region_diagonal, s->r_p->ra_p->max_pt, s->r_p->ra_p->min_pt);
716  region_diameter = MAGNITUDE(region_diagonal);
717 
718  nmg_model_bb(m_min_pt, m_max_pt, m);
719  model_bb_max_width = bn_dist_pt3_pt3(m_min_pt, m_max_pt);
720 
721  /* Choose an unlikely direction */
722  tries = 0;
723 retry:
724  if (tries < MAX_DIR_TRYS) {
725  projection_dir[X] = nmg_good_dirs[tries][X];
726  projection_dir[Y] = nmg_good_dirs[tries][Y];
727  projection_dir[Z] = nmg_good_dirs[tries][Z];
728  }
729 
730  if (++tries >= MAX_DIR_TRYS) {
731  goto out; /* only nmg_good_dirs to try */
732  }
733 
734  VUNITIZE(projection_dir);
735 
736  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
737  bu_log("nmg_class_pt_s(): Pt=(%g, %g, %g) dir=(%g, %g, %g), reg_diam=%g\n",
738  V3ARGS(pt), V3ARGS(projection_dir), region_diameter);
739  }
740 
741  VMOVE(rp.r_pt, pt);
742 
743  /* give the ray a length which is at least the max
744  * length of the nmg model bounding box.
745  */
746  VSCALE(rp.r_dir, projection_dir, model_bb_max_width * 1.25);
747 
748  /* get NMG ray-tracer to tell us if start point is inside or outside */
749  nmg_class = nmg_class_ray_vs_shell(&rp, s, in_or_out_only, tol);
750 
751  if (nmg_class == NMG_CLASS_AonBshared) {
752  bu_bomb("nmg_class_pt_s(): function nmg_class_ray_vs_shell returned AonBshared when it can only return AonBanti\n");
753  }
754  if (nmg_class == NMG_CLASS_Unknown) {
755  goto retry;
756  }
757 
758 out:
759  if (!in_or_out_only) {
760  bu_free((char *)faces_seen, "nmg_class_pt_s faces_seen[]");
761  }
762 
763  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
764  bu_log("nmg_class_pt_s(): returning %s, s=%p, tries=%d\n",
765  nmg_class_name(nmg_class), (void *)s, tries);
766  }
767 
768  return nmg_class;
769 }
770 
771 
772 /**
773  * Classify a loopuse/vertexuse from shell A WRT shell B.
774  */
775 static int
776 class_vu_vs_s(struct vertexuse *vu, struct shell *sB, char **classlist, const struct bn_tol *tol)
777 {
778  struct vertexuse *vup;
779  pointp_t pt;
780  char *reason;
781  int status = 0;
782  int nmg_class;
783  struct vertex *sv;
784 
785  NMG_CK_VERTEXUSE(vu);
786  NMG_CK_SHELL(sB);
787  BN_CK_TOL(tol);
788 
789  pt = vu->v_p->vg_p->coord;
790 
791  if (RTG.NMG_debug & DEBUG_CLASSIFY)
792  bu_log("class_vu_vs_s(vu=%p, v=%p) pt=(%g, %g, %g)\n", (void *)vu, (void *)vu->v_p, V3ARGS(pt));
793 
794  /* As a mandatory consistency measure, check for cached classification */
795  reason = "of cached classification";
796  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], vu->v_p)) {
797  status = INSIDE;
798  goto out;
799  }
800  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AonBshared], vu->v_p)) {
801  status = ON_SURF;
802  goto out;
803  }
804  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AonBanti], vu->v_p)) {
805  status = ON_SURF;
806  goto out;
807  }
808  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], vu->v_p)) {
809  status = OUTSIDE;
810  goto out;
811  }
812 
813  /* we use topology to determine if the vertex is "ON" the
814  * other shell.
815  */
816  for (BU_LIST_FOR(vup, vertexuse, &vu->v_p->vu_hd)) {
817 
818  if (*vup->up.magic_p == NMG_LOOPUSE_MAGIC) {
819  if (nmg_find_s_of_lu(vup->up.lu_p) != sB) {
820  continue;
821  }
822  NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], vu->v_p);
823  reason = "a loopuse of vertex is on shell";
824  status = ON_SURF;
825  goto out;
826  } else if (*vup->up.magic_p == NMG_EDGEUSE_MAGIC) {
827  if (nmg_find_s_of_eu(vup->up.eu_p) != sB) {
828  continue;
829  }
830  NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], vu->v_p);
831  reason = "an edgeuse of vertex is on shell";
832  status = ON_SURF;
833  goto out;
834  } else if (*vup->up.magic_p == NMG_SHELL_MAGIC) {
835  if (vup->up.s_p != sB) {
836  continue;
837  }
838  NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], vu->v_p);
839  reason = "vertex is shell's lone vertex";
840  status = ON_SURF;
841  goto out;
842  } else {
843  bu_bomb("class_vu_vs_s(): bad vertex UP pointer\n");
844  }
845  }
846 
847  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
848  bu_log("class_vu_vs_s(): Can't classify vertex via topology\n");
849  }
850 
851  /* The topology doesn't tell us about the vertex being "on" the shell
852  * so now it's time to look at the geometry to determine the vertex
853  * relationship to the shell.
854  *
855  * The vertex should *not* lie ON any of the faces or
856  * edges, since the intersection algorithm would have combined the
857  * topology if that had been the case.
858  */
859 
860  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
861  if ((sv = nmg_find_pt_in_shell(sB, pt, tol))) {
862  bu_log("vu=%p, v=%p, sv=%p, pt=(%g, %g, %g)\n",
863  (void *)vu, (void *)vu->v_p, (void *)sv, V3ARGS(pt));
864  bu_bomb("class_vu_vs_s(): logic error, vertex topology not shared properly\n");
865  }
866  }
867 
868  reason = "of nmg_class_pt_s()";
869  /* 3rd parameter '0' allows return of AonBanti instead of
870  * only AinB or AoutB
871  */
872  nmg_class = nmg_class_pt_s(pt, sB, 0, tol);
873 
874  if (nmg_class == NMG_CLASS_AonBshared) {
875  bu_bomb("class_vu_vs_s(): function nmg_class_pt_s returned AonBshared when it can only return AonBanti\n");
876  }
877  if (nmg_class == NMG_CLASS_AoutB) {
878  NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], vu->v_p);
879  status = OUTSIDE;
880  } else if (nmg_class == NMG_CLASS_AinB) {
881  NMG_INDEX_SET(classlist[NMG_CLASS_AinB], vu->v_p);
882  status = INSIDE;
883  } else if (nmg_class == NMG_CLASS_AonBshared) {
884  NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], vu->v_p);
885  status = ON_SURF;
886  } else if (nmg_class == NMG_CLASS_AonBanti) {
887  NMG_INDEX_SET(classlist[NMG_CLASS_AonBanti], vu->v_p);
888  status = ON_SURF;
889  } else {
890  bu_log("class_vu_vs_s(): nmg_class=%s\n", nmg_class_name(nmg_class));
891  VPRINT("pt", pt);
892  bu_bomb("class_vu_vs_s(): nmg_class_pt_s() failure\n");
893  }
894 
895 out:
896  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
897  bu_log("class_vu_vs_s(vu=%p) return %s because %s\n",
898  (void *)vu, nmg_class_status(status), reason);
899  }
900  return status;
901 }
902 
903 
904 static int
905 class_eu_vs_s(struct edgeuse *eu, struct shell *s, char **classlist, const struct bn_tol *tol)
906 {
907  int euv_cl, matev_cl;
908  int status = 0;
909  struct edgeuse *eup;
910  point_t pt;
911  pointp_t eupt, matept;
912  char *reason = "Unknown";
913  int nmg_class;
914  vect_t e_min_pt;
915  vect_t e_max_pt;
916 
917  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
918  bu_log("class_eu_vs_s(eu=%p (e_p=%p, lu=%p), s=%p)\n",
919  (void *)eu, (void *)eu->e_p, (void *)eu->up.lu_p, (void *)s);
920  nmg_euprint("class_eu_vs_s\t", eu);
921  }
922 
923  NMG_CK_EDGEUSE(eu);
924  NMG_CK_SHELL(s);
925  BN_CK_TOL(tol);
926 
927  /* As a mandatory consistency measure, check for cached classification */
928  reason = "of cached classification";
929  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], eu->e_p)) {
930  status = INSIDE;
931  goto out;
932  }
933  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AonBshared], eu->e_p)) {
934  status = ON_SURF;
935  goto out;
936  }
937  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AonBanti], eu->e_p)) {
938  status = ON_SURF;
939  goto out;
940  }
941  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], eu->e_p)) {
942  status = OUTSIDE;
943  goto out;
944  }
945 
946  /* find the bounding box of the edge */
947  VMOVE(e_min_pt, eu->vu_p->v_p->vg_p->coord);
948  VMIN(e_min_pt, eu->eumate_p->vu_p->v_p->vg_p->coord);
949  VMOVE(e_max_pt, eu->vu_p->v_p->vg_p->coord);
950  VMAX(e_max_pt, eu->eumate_p->vu_p->v_p->vg_p->coord);
951 
952  /* if the edge and shell bounding boxes are disjoint by at least
953  * distance tolerance then the edge is outside the shell. also
954  * both vertices of the edge are outside the shell.
955  */
956  if (V3RPP_DISJOINT_TOL(e_min_pt, e_max_pt, s->sa_p->min_pt, s->sa_p->max_pt, tol->dist)) {
957  NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], eu->e_p);
958  NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], eu->vu_p->v_p);
959  NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], eu->eumate_p->vu_p->v_p);
960  status = OUTSIDE;
961  goto out;
962  }
963 
964  euv_cl = class_vu_vs_s(eu->vu_p, s, classlist, tol);
965  matev_cl = class_vu_vs_s(eu->eumate_p->vu_p, s, classlist, tol);
966 
967  /* sanity check */
968  if ((euv_cl == INSIDE && matev_cl == OUTSIDE) ||
969  (euv_cl == OUTSIDE && matev_cl == INSIDE)) {
970  static int num = 0;
971  char buf[128];
972  FILE *fp;
973 
974  sprintf(buf, "nmg_class%d.plot3", num++);
975  if ((fp = fopen(buf, "wb")) == NULL) {
976  bu_bomb(buf);
977  }
978  nmg_pl_s(fp, s);
979  /* A yellow line for the angry edge */
980  pl_color(fp, 255, 255, 0);
981  pdv_3line(fp, eu->vu_p->v_p->vg_p->coord,
982  eu->eumate_p->vu_p->v_p->vg_p->coord);
983  fclose(fp);
984 
985  nmg_pr_class_status("class_eu_vs_s(): eu vu", euv_cl);
986  nmg_pr_class_status("class_eu_vs_s(): eumate vu", matev_cl);
987  if (RT_G_DEBUG || RTG.NMG_debug) {
988  /* Do them over, so we can watch */
989  bu_log("class_eu_vs_s(): Edge not cut, doing it over\n");
990  NMG_INDEX_CLEAR(classlist[NMG_CLASS_AinB], eu->vu_p);
991  NMG_INDEX_CLEAR(classlist[NMG_CLASS_AoutB], eu->vu_p);
992  NMG_INDEX_CLEAR(classlist[NMG_CLASS_AinB], eu->eumate_p->vu_p);
993  NMG_INDEX_CLEAR(classlist[NMG_CLASS_AoutB], eu->eumate_p->vu_p);
994  RTG.NMG_debug |= DEBUG_CLASSIFY;
995  (void)class_vu_vs_s(eu->vu_p, s, classlist, tol);
996  (void)class_vu_vs_s(eu->eumate_p->vu_p, s, classlist, tol);
997  nmg_euprint("class_eu_vs_s(): didn't this edge get cut?", eu);
998  nmg_pr_eu(eu, "class_eu_vs_s():");
999  }
1000 
1001  bu_log("class_eu_vs_s(): wrote %s\n", buf);
1002  bu_bomb("class_eu_vs_s(): edge didn't get cut\n");
1003  }
1004 
1005  if (euv_cl == ON_SURF && matev_cl == ON_SURF) {
1006  vect_t eu_dir;
1007  int tries;
1008 
1009  /* check for radial uses of this edge by the shell */
1010  eup = eu->radial_p->eumate_p;
1011  do {
1012  NMG_CK_EDGEUSE(eup);
1013  if (nmg_find_s_of_eu(eup) == s) {
1014  NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], eu->e_p);
1015  reason = "a radial edgeuse is on shell";
1016  status = ON_SURF;
1017  goto out;
1018  }
1019  eup = eup->radial_p->eumate_p;
1020  } while (eup != eu->radial_p->eumate_p);
1021 
1022  /* look for another eu between these two vertices */
1023  if (nmg_find_matching_eu_in_s(eu, s)) {
1024  NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], eu->e_p);
1025  reason = "another eu between same vertices is on shell";
1026  status = ON_SURF;
1027  goto out;
1028  }
1029 
1030  /* although the two endpoints are "on" the shell,
1031  * the edge would appear to be either "inside" or "outside",
1032  * since there are no uses of this edge in the shell.
1033  *
1034  * So we classify the midpoint of the line WRT the shell.
1035  *
1036  * Consider AE as being "eu", with M as the geometric midpoint.
1037  * Consider AD as being a side view of a perpendicular plane
1038  * in the other shell, which AE _almost_ lies in.
1039  * A problem occurs when the angle DAE is very small.
1040  * Point A is ON because of shared topology.
1041  * Point E is marked ON because it is within tolerance of
1042  * the face, even though there is no corresponding vertex.
1043  * Naturally, point M is also going to be found to be ON
1044  * because it is also within tolerance.
1045  *
1046  * D
1047  * /|
1048  * / |
1049  * B |
1050  * / |
1051  * / |
1052  * A--M--E
1053  *
1054  * This would seem to be an intersector problem.
1055  */
1056  nmg_class = NMG_CLASS_Unknown;
1057  eupt = eu->vu_p->v_p->vg_p->coord;
1058  matept = eu->eumate_p->vu_p->v_p->vg_p->coord;
1059  if (nmg_class == NMG_CLASS_Unknown) {
1060  VSUB2(eu_dir, matept, eupt);
1061  }
1062 
1063  tries = 0;
1064  while (nmg_class == NMG_CLASS_Unknown && tries < 3) {
1065  /* Must resort to ray trace */
1066  switch (tries) {
1067  case 0 :
1068  VJOIN1(pt, eupt, 0.5, eu_dir);
1069  reason = "midpoint classification (both verts ON)";
1070  break;
1071  case 1:
1072  VJOIN1(pt, eupt, 0.1, eu_dir);
1073  reason = "point near EU start classification (both verts ON)";
1074  break;
1075  case 2:
1076  VJOIN1(pt, eupt, 0.9, eu_dir);
1077  reason = "point near EU end classification (both verts ON)";
1078  break;
1079  }
1080  /* 3rd parameter of '0' allows a return of AonBanti
1081  * instead of only AinB or AoutB.
1082  */
1083  nmg_class = nmg_class_pt_s(pt, s, 0, tol);
1084  tries++;
1085  }
1086 
1087  if (nmg_class == NMG_CLASS_AonBshared) {
1088  bu_bomb("class_eu_vs_s(): function nmg_class_pt_s returned AonBshared when it can only return AonBanti\n");
1089  }
1090  if (nmg_class == NMG_CLASS_AoutB) {
1091  NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], eu->e_p);
1092  status = OUTSIDE;
1093  } else if (nmg_class == NMG_CLASS_AinB) {
1094  NMG_INDEX_SET(classlist[NMG_CLASS_AinB], eu->e_p);
1095  status = INSIDE;
1096  } else if (nmg_class == NMG_CLASS_AonBanti) {
1097  NMG_INDEX_SET(classlist[NMG_CLASS_AonBanti], eu->e_p);
1098  status = ON_SURF;
1099  } else {
1100  bu_log("class_eu_vs_s(): nmg_class=%s\n", nmg_class_name(nmg_class));
1101  nmg_euprint("class_eu_vs_s(): Why wasn't this edge in or out?", eu);
1102  bu_bomb("class_eu_vs_s(): nmg_class_pt_s() midpoint failure\n");
1103  }
1104  goto out;
1105  }
1106 
1107  if (euv_cl == OUTSIDE || matev_cl == OUTSIDE) {
1108  NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], eu->e_p);
1109  reason = "at least one vert OUT";
1110  status = OUTSIDE;
1111  goto out;
1112  }
1113  if (euv_cl == INSIDE && matev_cl == INSIDE) {
1114  NMG_INDEX_SET(classlist[NMG_CLASS_AinB], eu->e_p);
1115  reason = "both verts IN";
1116  status = INSIDE;
1117  goto out;
1118  }
1119  if ((euv_cl == INSIDE && matev_cl == ON_SURF) ||
1120  (euv_cl == ON_SURF && matev_cl == INSIDE)) {
1121  NMG_INDEX_SET(classlist[NMG_CLASS_AinB], eu->e_p);
1122  reason = "one vert IN, one ON";
1123  status = INSIDE;
1124  goto out;
1125  }
1126  bu_log("class_eu_vs_s(): eu's vert is %s, mate's vert is %s\n",
1127  nmg_class_status(euv_cl), nmg_class_status(matev_cl));
1128  bu_bomb("class_eu_vs_s(): inconsistent edgeuse\n");
1129 
1130 out:
1131  if (RTG.NMG_debug & DEBUG_GRAPHCL)
1132  nmg_show_broken_classifier_stuff((uint32_t *)eu, classlist, nmg_class_nothing_broken, 0, (char *)NULL);
1133  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1134  bu_log("class_eu_vs_s(eu=%p) return %s because %s\n",
1135  (void *)eu, nmg_class_status(status), reason);
1136  }
1137  return status;
1138 }
1139 
1140 
1141 /* XXX move to nmg_info.c */
1142 /**
1143  * Given two edgeuses in different faces that share a common edge,
1144  * determine if they are from identical loops or not.
1145  *
1146  * Note that two identical loops in an anti-shared pair of faces
1147  * (faces with opposite orientations) will also have opposite orientations.
1148  *
1149  * Returns -
1150  * 0 Loops not identical
1151  * 1 Loops identical, faces are ON-shared
1152  * 2 Loops identical, faces are ON-anti-shared
1153  * 3 Loops identical, at least one is a wire loop.
1154  */
1155 int
1156 nmg_2lu_identical(const struct edgeuse *eu1, const struct edgeuse *eu2)
1157 {
1158  const struct loopuse *lu1;
1159  const struct loopuse *lu2;
1160  const struct edgeuse *eu1_first;
1161  const struct faceuse *fu1;
1162  const struct faceuse *fu2;
1163  int ret;
1164 
1165  NMG_CK_EDGEUSE(eu1);
1166  NMG_CK_EDGEUSE(eu2);
1167 
1168  if (eu1->e_p != eu2->e_p) bu_bomb("nmg_2lu_identical() differing edges?\n");
1169 
1170  /* get the starting vertex of each edgeuse to be the same. */
1171  if (eu2->vu_p->v_p != eu1->vu_p->v_p) {
1172  eu2 = eu2->eumate_p;
1173  if (eu2->vu_p->v_p != eu1->vu_p->v_p)
1174  bu_bomb("nmg_2lu_identical() radial edgeuse doesn't share vertices\n");
1175  }
1176 
1177  lu1 = eu1->up.lu_p;
1178  lu2 = eu2->up.lu_p;
1179 
1180  NMG_CK_LOOPUSE(lu1);
1181  NMG_CK_LOOPUSE(lu2);
1182 
1183  /* march around the two loops to see if they
1184  * are the same all the way around.
1185  */
1186  eu1_first = eu1;
1187  do {
1188  if (eu1->vu_p->v_p != eu2->vu_p->v_p) {
1189  ret = 0;
1190  goto out;
1191  }
1192  eu1 = BU_LIST_PNEXT_CIRC(edgeuse, &eu1->l);
1193  eu2 = BU_LIST_PNEXT_CIRC(edgeuse, &eu2->l);
1194  } while (eu1 != eu1_first);
1195 
1196  if (*lu1->up.magic_p != NMG_FACEUSE_MAGIC ||
1197  *lu2->up.magic_p != NMG_FACEUSE_MAGIC) {
1198  ret = 3; /* one is a wire loop */
1199  goto out;
1200  }
1201 
1202  fu1 = lu1->up.fu_p;
1203  fu2 = lu2->up.fu_p;
1204 
1205  if (fu1->f_p->g.plane_p != fu2->f_p->g.plane_p) {
1206  vect_t fu1_norm, fu2_norm;
1207 
1208  /* Drop back to using a geometric calculation */
1209  if (fu1->orientation != fu2->orientation)
1210  NMG_GET_FU_NORMAL(fu2_norm, fu2->fumate_p)
1211  else
1212  NMG_GET_FU_NORMAL(fu2_norm, fu2)
1213 
1214  NMG_GET_FU_NORMAL(fu1_norm, fu1);
1215 
1216  if (VDOT(fu1_norm, fu2_norm) < -SMALL_FASTF)
1217  ret = 2; /* ON anti-shared */
1218  else
1219  ret = 1; /* ON shared */
1220  goto out;
1221  }
1222 
1223  if (RTG.NMG_debug & DEBUG_BASIC) {
1224  bu_log("---- fu1, f=%p, flip=%d\n", (void *)fu1->f_p, fu1->f_p->flip);
1225  nmg_pr_fu_briefly(fu1, 0);
1226  bu_log("---- fu2, f=%p, flip=%d\n", (void *)fu2->f_p, fu2->f_p->flip);
1227  nmg_pr_fu_briefly(fu2, 0);
1228  }
1229 
1230  /*
1231  * The two loops are identical, compare the two faces.
1232  * Only raw face orientations count here.
1233  * Loopuse and faceuse orientations do not matter.
1234  */
1235  if (fu1->f_p->flip != fu2->f_p->flip)
1236  ret = 2; /* ON anti-shared */
1237  else
1238  ret = 1; /* ON shared */
1239 out:
1240  if (RTG.NMG_debug & DEBUG_BASIC) {
1241  bu_log("nmg_2lu_identical(eu1=%p, eu2=%p) ret=%d\n",
1242  (void *)eu1, (void *)eu2, ret);
1243  }
1244  return ret;
1245 }
1246 
1247 
1248 /**
1249  * Make all the edges and vertices of a loop carry the same classification
1250  * as the loop.
1251  * There is no intrinsic way to tell if an edge is "shared" or
1252  * "antishared", except by reference to its loopuse, but the heritage
1253  * of the edgeuse makes a difference to the boolean evaluator.
1254  *
1255  * "newclass" should only be AonBshared or AonBanti.
1256  */
1257 void
1258 nmg_reclassify_lu_eu(struct loopuse *lu, char **classlist, int newclass)
1259 {
1260  struct vertexuse *vu;
1261  struct edgeuse *eu;
1262  struct vertex *v;
1263 
1264  NMG_CK_LOOPUSE(lu);
1265 
1266  if (RTG.NMG_debug & DEBUG_BASIC) {
1267  bu_log("nmg_reclassify_lu_eu(lu=%p, classlist=%p, newclass=%s)\n",
1268  (void *)lu, (void *)classlist, nmg_class_name(newclass));
1269  }
1270 
1271  if (newclass != NMG_CLASS_AonBshared && newclass != NMG_CLASS_AonBanti)
1272  bu_bomb("nmg_reclassify_lu_eu() bad newclass\n");
1273 
1274  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC) {
1275  vu = BU_LIST_FIRST(vertexuse, &lu->down_hd);
1276  NMG_CK_VERTEXUSE(vu);
1277  v = vu->v_p;
1278  NMG_CK_VERTEX(v);
1279 
1280  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], v) ||
1281  NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], v))
1282  bu_bomb("nmg_reclassify_lu_eu() changing in/out vert of lone vu loop to ON?\n");
1283 
1284  /* Clear out other classifications */
1285  NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBshared], v);
1286  NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBanti], v);
1287  /* Establish new classification */
1288  NMG_INDEX_SET(classlist[newclass], v);
1289  return;
1290  }
1291 
1292  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
1293  struct edge *e;
1294  NMG_CK_EDGEUSE(eu);
1295  e = eu->e_p;
1296  NMG_CK_EDGE(e);
1297 
1298  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], e) ||
1299  NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], e))
1300  bu_bomb("nmg_reclassify_lu_eu() changing in/out edge to ON?\n");
1301 
1302  /* Clear out other edge classifications */
1303  NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBshared], e);
1304  NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBanti], e);
1305  /* Establish new edge classification */
1306  NMG_INDEX_SET(classlist[newclass], e);
1307 
1308  /* Same thing for the vertices. Only need to do start vert here. */
1309  vu = eu->vu_p;
1310  NMG_CK_VERTEXUSE(vu);
1311  v = vu->v_p;
1312  NMG_CK_VERTEX(v);
1313 
1314  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], v) ||
1315  NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], v))
1316  bu_bomb("nmg_reclassify_lu_eu() changing in/out vert to ON?\n");
1317 
1318  /* Clear out other classifications */
1319  NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBshared], v);
1320  NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBanti], v);
1321  /* Establish new classification */
1322  NMG_INDEX_SET(classlist[newclass], v);
1323  }
1324 }
1325 
1326 
1327 /**
1328  * Classify LU w.r.t. LU_REF.
1329  *
1330  * Pre-requisites, required (but not checked for here):
1331  *
1332  * LU shares all its edges with LU_REF
1333  * LU and LU_REF are loopuses from faceuses
1334  * LU and LU_REF are from different shells
1335  * LU and LU_REF must both be loops of edges
1336  *
1337  * LU is classified w.r.t. LU_REF by the following algorithm:
1338  *
1339  * 1. Select any EU from LU.
1340  *
1341  * 2. Find EU_REF from LU_REF that is shared with EU.
1342  *
1343  * 3. If left vector of EU is opposite left vector of EU_REF, then
1344  * classification is either NMG_CLASS_AoutB or NMG_CLASS_AinB. (This
1345  * is the case where one lu exactly fills a hole that the other lu
1346  * creates in a face).
1347  *
1348  * 4. If the left vectors agree, then the loops are shared - If the
1349  * direction of EU agrees with the direction of EU_REF, then classify
1350  * as NMG_CLASS_AonBshared. Otherwise classify as NMG_CLASS_AonBanti.
1351  *
1352  * returns:
1353  * NMG_CLASS_AonBshared
1354  * NMG_CLASS_AonBanti
1355  * NMG_CLASS_AoutB
1356  */
1357 static int
1358 class_shared_lu(const struct loopuse *lu, const struct loopuse *lu_ref, const struct bn_tol *tol)
1359 {
1360  struct shell *s_ref;
1361  struct edgeuse *eu;
1362  struct edgeuse *eu_ref;
1363  struct edgeuse *eu_start, *eu_tmp;
1364  struct faceuse *fu_of_lu;
1365  vect_t left;
1366  vect_t left_ref;
1367 
1368  if (RTG.NMG_debug & DEBUG_CLASSIFY)
1369  bu_log("class_shared_lu: classifying lu %p w.r.t. lu_ref %p\n",
1370  (void *)lu, (void *)lu_ref);
1371 
1372  NMG_CK_LOOPUSE(lu);
1373  NMG_CK_LOOPUSE(lu_ref);
1374  BN_CK_TOL(tol);
1375 
1376  eu = BU_LIST_FIRST(edgeuse, &lu->down_hd);
1377  for (BU_LIST_FOR(eu_ref, edgeuse, &lu_ref->down_hd)) {
1378  if (eu_ref->e_p == eu->e_p)
1379  break;
1380  }
1381 
1382  if (eu_ref->e_p != eu->e_p) {
1383  bu_log("class_shared_lu() couldn't find a shared EU between LU's %p and %p\n",
1384  (void *)lu, (void *)lu_ref);
1385  bu_bomb("class_shared_lu() couldn't find a shared EU between LU's\n");
1386  }
1387 
1388  if (nmg_find_eu_leftvec(left, eu)) {
1389  bu_log("class_shared_lu() couldn't get a left vector for EU %p\n", (void *)eu);
1390  bu_bomb("class_shared_lu() couldn't get a left vector for EU\n");
1391  }
1392  if (nmg_find_eu_leftvec(left_ref, eu_ref)) {
1393  bu_log("class_shared_lu() couldn't get a left vector for EU %p\n", (void *)eu_ref);
1394  bu_bomb("class_shared_lu() couldn't get a left vector for EU\n");
1395  }
1396 
1397  if (VDOT(left, left_ref) > SMALL_FASTF) {
1398  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1399  bu_log("eu %p goes from v %p to v %p\n",
1400  (void *)eu, (void *)eu->vu_p->v_p, (void *)eu->eumate_p->vu_p->v_p);
1401  bu_log("eu_ref %p goes from v %p to v %p\n",
1402  (void *)eu_ref, (void *)eu_ref->vu_p->v_p, (void *)eu_ref->eumate_p->vu_p->v_p);
1403  }
1404 
1405  /* loop is either shared or anti */
1406  if (eu->vu_p->v_p == eu_ref->vu_p->v_p) {
1407  if (RTG.NMG_debug & DEBUG_CLASSIFY)
1408  bu_log("class_shared_lu returning NMG_CLASS_AonBshared\n");
1409  return NMG_CLASS_AonBshared;
1410  } else {
1411  if (RTG.NMG_debug & DEBUG_CLASSIFY)
1412  bu_log("class_shared_lu returning NMG_CLASS_AonBanti\n");
1413  return NMG_CLASS_AonBanti;
1414  }
1415  }
1416 
1417  /* loop is either in or out
1418  * look at next radial edge from lu_ref shell if that faceuse
1419  * is OT_OPPOSITE, then we are "in", else "out"
1420  */
1421  s_ref = nmg_find_s_of_eu(eu_ref);
1422  fu_of_lu = nmg_find_fu_of_lu(lu);
1423 
1424  for (BU_LIST_FOR(eu_start, edgeuse, &lu->down_hd)) {
1425  int use_this_eu = 1;
1426 
1427  eu_tmp = eu_start->eumate_p->radial_p;
1428  while (eu_tmp != eu_start) {
1429  struct faceuse *fu_tmp;
1430  struct loopuse *lu_tmp;
1431  int nmg_class;
1432 
1433  fu_tmp = nmg_find_fu_of_eu(eu_tmp);
1434  if (fu_tmp != fu_of_lu && fu_tmp->fumate_p != fu_of_lu) {
1435  eu_tmp = eu_tmp->eumate_p->radial_p;
1436  continue;
1437  }
1438 
1439  /* radial edge is part of same face
1440  * make sure it is part of a loop */
1441  if (*eu_tmp->up.magic_p != NMG_LOOPUSE_MAGIC) {
1442  eu_tmp = eu_tmp->eumate_p->radial_p;
1443  continue;
1444  }
1445  lu_tmp = eu_tmp->up.lu_p;
1446  if (fu_tmp->fumate_p == fu_of_lu)
1447  lu_tmp = lu_tmp->lumate_p;
1448 
1449  if (lu_tmp == lu) {
1450  /* cannot classify based on this edgeuse */
1451  use_this_eu = 0;
1452  break;
1453  }
1454 
1455  nmg_class = nmg_classify_lu_lu(lu_tmp, lu, tol);
1456  if (lu->orientation == OT_OPPOSITE && nmg_class == NMG_CLASS_AoutB) {
1457  /* cannot classify based on this edgeuse */
1458  use_this_eu = 0;
1459  break;
1460  } else if (lu->orientation == OT_SAME && nmg_class == NMG_CLASS_AinB) {
1461  /* cannot classify based on this edgeuse */
1462  use_this_eu = 0;
1463  break;
1464  }
1465 
1466  eu_tmp = eu_tmp->eumate_p->radial_p;
1467  }
1468  if (!use_this_eu)
1469  continue;
1470 
1471  /* O.K. we can classify using this eu, look radially for a faceuse
1472  * from the reference shell
1473  */
1474  eu_tmp = eu_start->eumate_p->radial_p;
1475  while (eu_tmp != eu_start) {
1476  struct faceuse *fu_tmp;
1477 
1478  if (nmg_find_s_of_eu(eu_tmp) == s_ref) {
1479  fu_tmp = nmg_find_fu_of_eu(eu_tmp);
1480  if (fu_tmp->orientation == OT_SAME)
1481  return NMG_CLASS_AoutB;
1482  else
1483  return NMG_CLASS_AinB;
1484  }
1485 
1486  eu_tmp = eu_tmp->eumate_p->radial_p;
1487  }
1488  }
1489 
1490  if (RTG.NMG_debug & DEBUG_CLASSIFY)
1491  bu_log("class_shared_lu returning NMG_CLASS_Unknown at end\n");
1492  return NMG_CLASS_Unknown;
1493 }
1494 
1495 
1496 /**
1497  * Called by -
1498  * class_fu_vs_s
1499  */
1500 static int
1501 class_lu_vs_s(struct loopuse *lu, struct shell *s, char **classlist, const struct bn_tol *tol)
1502 {
1503  int nmg_class;
1504  unsigned int in, outside, on;
1505  struct edgeuse *eu, *p;
1506  struct loopuse *q_lu;
1507  struct vertexuse *vu;
1508  uint32_t magic1;
1509  char *reason = "Unknown";
1510  int seen_error = 0;
1511  int status = 0;
1512 
1513  if (RTG.NMG_debug & DEBUG_CLASSIFY)
1514  bu_log("class_lu_vs_s(lu=%p, s=%p)\n", (void *)lu, (void *)s);
1515 
1516  NMG_CK_LOOPUSE(lu);
1517  NMG_CK_SHELL(s);
1518  BN_CK_TOL(tol);
1519 
1520  if (nmg_find_s_of_lu(lu) == s) {
1521  bu_log("class_lu_vs_s() is trying to classify lu %p vs its own shell (%p)\n",
1522  (void *)lu, (void *)s);
1523  bu_bomb("class_lu_vs_s() is trying to classify lu vs its own shell");
1524  }
1525 
1526  /* check to see if loop is already in one of the lists */
1527  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], lu->l_p)) {
1528  reason = "of classlist";
1529  nmg_class = NMG_CLASS_AinB;
1530  status = INSIDE;
1531  goto out;
1532  }
1533 
1534  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AonBshared], lu->l_p)) {
1535  reason = "of classlist";
1536  nmg_class = NMG_CLASS_AonBshared;
1537  status = ON_SURF;
1538  goto out;
1539  }
1540  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AonBanti], lu->l_p)) {
1541  reason = "of classlist";
1542  nmg_class = NMG_CLASS_AonBanti;
1543  status = ON_SURF;
1544  goto out;
1545  }
1546 
1547  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], lu->l_p)) {
1548  reason = "of classlist";
1549  nmg_class = NMG_CLASS_AoutB;
1550  status = OUTSIDE;
1551  goto out;
1552  }
1553 
1554  magic1 = BU_LIST_FIRST_MAGIC(&lu->down_hd);
1555  if (magic1 == NMG_VERTEXUSE_MAGIC) {
1556  /* Loop of a single vertex */
1557  reason = "of vertex classification";
1558  vu = BU_LIST_PNEXT(vertexuse, &lu->down_hd);
1559  NMG_CK_VERTEXUSE(vu);
1560  nmg_class = class_vu_vs_s(vu, s, classlist, tol);
1561  switch (nmg_class) {
1562  case INSIDE:
1563  NMG_INDEX_SET(classlist[NMG_CLASS_AinB], lu->l_p);
1564  break;
1565  case OUTSIDE:
1566  NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], lu->l_p);
1567  break;
1568  case ON_SURF:
1569  NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], lu->l_p);
1570  break;
1571  default:
1572  bu_bomb("class_lu_vs_s(): bad vertexloop classification\n");
1573  }
1574  status = nmg_class;
1575  goto out;
1576  } else if (magic1 != NMG_EDGEUSE_MAGIC) {
1577  bu_bomb("class_lu_vs_s(): bad child of loopuse\n");
1578  }
1579 
1580  /* loop is collection of edgeuses */
1581  seen_error = 0;
1582 
1583  do {
1584 
1585  in = outside = on = 0;
1586  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
1587  /* Classify each edgeuse */
1588  nmg_class = class_eu_vs_s(eu, s, classlist, tol);
1589  switch (nmg_class) {
1590  case INSIDE : ++in;
1591  break;
1592  case OUTSIDE : ++outside;
1593  break;
1594  case ON_SURF : ++on;
1595  break;
1596  default : bu_bomb("class_lu_vs_s(): bad class for edgeuse\n");
1597  }
1598  }
1599 
1600  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1601  bu_log("class_lu_vs_s: Loopuse edges in:%d on:%d out:%d\n", in, on, outside);
1602  }
1603 
1604  if (in > 0 && outside > 0) {
1605  FILE *fp;
1606 
1607  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1608  char buf[128];
1609  static int num;
1610  long *b;
1611  struct model *m;
1612 
1613  m = nmg_find_model(lu->up.magic_p);
1614  b = (long *)bu_calloc(m->maxindex, sizeof(long), "nmg_pl_lu flag[]");
1615  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
1616  if (NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], eu->e_p))
1617  nmg_euprint("In: edgeuse", eu);
1618  else if (NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], eu->e_p))
1619  nmg_euprint("Out: edgeuse", eu);
1620  else if (NMG_INDEX_TEST(classlist[NMG_CLASS_AonBshared], eu->e_p))
1621  nmg_euprint("OnShare: edgeuse", eu);
1622  else if (NMG_INDEX_TEST(classlist[NMG_CLASS_AonBanti], eu->e_p))
1623  nmg_euprint("OnAnti: edgeuse", eu);
1624  else
1625  nmg_euprint("BAD: edgeuse", eu);
1626  }
1627  sprintf(buf, "badloop%d.plot3", num++);
1628  if ((fp=fopen(buf, "wb")) != NULL) {
1629  nmg_pl_lu(fp, lu, b, 255, 255, 255);
1630  nmg_pl_s(fp, s);
1631  fclose(fp);
1632  bu_log("wrote %s\n", buf);
1633  }
1634  nmg_pr_lu(lu, "");
1635  nmg_stash_model_to_file("class.g", nmg_find_model((uint32_t *)lu), "class_ls_vs_s: loop transits plane of shell/face?");
1636  bu_free((char *)b, "nmg_pl_lu flag[]");
1637  }
1638 
1639  if (seen_error > 3) {
1640  bu_bomb("class_lu_vs_s(): loop transits plane of shell/face?\n");
1641  }
1642  seen_error++;
1643  continue;
1644  }
1645 
1646  /* in <=0 || outside <= 0 so were good */
1647  break;
1648 
1649  } while (1);
1650 
1651  if (outside > 0) {
1652  NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], lu->l_p);
1653  reason = "edgeuses were OUT and ON";
1654  nmg_class = NMG_CLASS_AoutB;
1655  status = OUTSIDE;
1656  goto out;
1657  } else if (in > 0) {
1658  NMG_INDEX_SET(classlist[NMG_CLASS_AinB], lu->l_p);
1659  reason = "edgeuses were IN and ON";
1660  nmg_class = NMG_CLASS_AinB;
1661  status = INSIDE;
1662  goto out;
1663  } else if (on == 0) {
1664  bu_bomb("class_lu_vs_s(): missing edgeuses\n");
1665  }
1666 
1667  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1668  bu_log("class_lu_vs_s(): All edgeuses of loop are ON\n");
1669  }
1670 
1671  /* since all of the edgeuses of this loop are "on" the other shell,
1672  * we need to see if this loop is "on" the other shell
1673  *
1674  * if we're a wire edge loop, simply having all edges "on" is
1675  * sufficient.
1676  *
1677  * foreach radial edgeuse
1678  * if edgeuse vertex is same and edgeuse parent shell is the one
1679  * desired, then....
1680  *
1681  * p = edgeuse, q = radial edgeuse
1682  * while p's vertex equals q's vertex and we
1683  * haven't come full circle
1684  * move p and q forward
1685  * if we made it full circle, loop is on
1686  */
1687 
1688  if (*lu->up.magic_p == NMG_SHELL_MAGIC) {
1689  NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], lu->l_p);
1690  reason = "loop is a wire loop in the shell";
1691  nmg_class = NMG_CLASS_AonBshared;
1692  status = ON_SURF;
1693  goto out;
1694  }
1695 
1696  NMG_CK_FACEUSE(lu->up.fu_p);
1697 
1698  nmg_class = NMG_CLASS_Unknown;
1699  eu = BU_LIST_FIRST(edgeuse, &lu->down_hd);
1700  for (
1701  eu = eu->radial_p->eumate_p;
1702  eu != BU_LIST_FIRST(edgeuse, &lu->down_hd);
1703  eu = eu->radial_p->eumate_p)
1704  {
1705  struct faceuse *fu_qlu;
1706  struct edgeuse *eu1;
1707  struct edgeuse *eu2;
1708  int found_match;
1709 
1710  /* if the radial edge is a part of a loop which is part of
1711  * a face, then it's one that we might be "on"
1712  */
1713  if (*eu->up.magic_p != NMG_LOOPUSE_MAGIC) {
1714  continue;
1715  }
1716 
1717  q_lu = eu->up.lu_p;
1718  if (*q_lu->up.magic_p != NMG_FACEUSE_MAGIC) {
1719  continue;
1720  }
1721 
1722  fu_qlu = q_lu->up.fu_p;
1723  NMG_CK_FACEUSE(fu_qlu);
1724 
1725  if (q_lu == lu) {
1726  continue;
1727  }
1728 
1729  /* Only consider faces from shell 's' */
1730  if (q_lu->up.fu_p->s_p != s) {
1731  continue;
1732  }
1733 
1734  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1735  bu_log("\tfound radial lu (%p), check for match\n", (void *)q_lu);
1736  }
1737 
1738  /* now check if eu's match in both LU's */
1739  eu1 = BU_LIST_FIRST(edgeuse, &lu->down_hd);
1740  if (eu1->vu_p->v_p == eu->vu_p->v_p) {
1741  /* true when eu1-lu and eu-lu are opposite i.e. cw -vs- ccw */
1742  eu2 = eu;
1743  } else if (eu1->vu_p->v_p == eu->eumate_p->vu_p->v_p) {
1744  /* true when eu1-lu and eu-lu are same i.e. cw or ccw */
1745  eu2 = BU_LIST_PNEXT_CIRC(edgeuse, &eu->l);
1746  } else {
1747  eu2 = BU_LIST_PPREV_CIRC(edgeuse, &eu->l);
1748  }
1749 
1750  found_match = 1;
1751  do {
1752  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1753  bu_log("\t\tcompare vertex %p to vertex %p\n", (void *)eu1->vu_p->v_p, (void *)eu2->vu_p->v_p);
1754  }
1755  if (eu1->vu_p->v_p != eu2->vu_p->v_p) {
1756  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1757  bu_log("\t\t\tnot a match\n");
1758  }
1759  found_match = 0;
1760  break;
1761  }
1762  eu1 = BU_LIST_PNEXT_CIRC(edgeuse, &eu1->l);
1763  eu2 = BU_LIST_PNEXT_CIRC(edgeuse, &eu2->l);
1764  } while (eu1 != BU_LIST_FIRST(edgeuse, &lu->down_hd));
1765 
1766  if (!found_match) {
1767  /* check opposite direction */
1768  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1769  bu_log("\tChecking for match in opposite direction\n");
1770  }
1771  eu1 = BU_LIST_FIRST(edgeuse, &lu->down_hd);
1772  if (eu1->vu_p->v_p == eu->vu_p->v_p) {
1773  eu2 = eu;
1774  } else if (eu1->vu_p->v_p == eu->eumate_p->vu_p->v_p) {
1775  eu2 = BU_LIST_PNEXT_CIRC(edgeuse, &eu->l);
1776  } else {
1777  eu2 = BU_LIST_PPREV_CIRC(edgeuse, &eu->l);
1778  }
1779 
1780  found_match = 1;
1781  do {
1782  if (RTG.NMG_debug & DEBUG_CLASSIFY)
1783  bu_log("\t\tcompare vertex %p to vertex %p\n",
1784  (void *)eu1->vu_p->v_p, (void *)eu2->vu_p->v_p);
1785  if (eu1->vu_p->v_p != eu2->vu_p->v_p) {
1786  if (RTG.NMG_debug & DEBUG_CLASSIFY)
1787  bu_log("\t\t\tnot a match\n");
1788  found_match = 0;
1789  break;
1790  }
1791  eu1 = BU_LIST_PNEXT_CIRC(edgeuse, &eu1->l);
1792  eu2 = BU_LIST_PPREV_CIRC(edgeuse, &eu2->l);
1793  } while (eu1 != BU_LIST_FIRST(edgeuse, &lu->down_hd));
1794  }
1795 
1796  if (found_match) {
1797  int test_class = NMG_CLASS_Unknown;
1798 
1799  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1800  bu_log("\tFound a matching LU's %p and %p\n", (void *)lu, (void *)q_lu);
1801  }
1802 
1803  if (fu_qlu->orientation == OT_SAME) {
1804  test_class = class_shared_lu(lu, q_lu, tol);
1805  } else if (fu_qlu->orientation == OT_OPPOSITE) {
1806  test_class = class_shared_lu(lu, q_lu->lumate_p, tol);
1807  } else {
1808  bu_log("class_lu_vs_s: FU %p for lu %p matching lu %p has bad orientation (%s)\n",
1809  (void *)fu_qlu, (void *)lu, (void *)q_lu, nmg_orientation(fu_qlu->orientation));
1810  bu_bomb("class_lu_vs_s: FU has bad orientation\n");
1811  }
1812 
1813  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1814  bu_log("\tclass_shared_lu says %s\n", nmg_class_name(test_class));
1815  }
1816 
1817  if (nmg_class == NMG_CLASS_Unknown) {
1818  nmg_class = test_class;
1819  } else if (test_class == NMG_CLASS_AonBshared || test_class == NMG_CLASS_AonBanti) {
1820  nmg_class = test_class;
1821  }
1822 
1823  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1824  bu_log("\tclass set to %s\n", nmg_class_name(nmg_class));
1825  }
1826 
1827  if (nmg_class == NMG_CLASS_AonBshared) {
1828  break;
1829  }
1830  }
1831  }
1832 
1833  if (nmg_class != NMG_CLASS_Unknown) {
1834  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1835  bu_log("Final nmg_class = %s\n", nmg_class_name(nmg_class));
1836  }
1837  NMG_INDEX_SET(classlist[nmg_class], lu->l_p);
1838  if (nmg_class == NMG_CLASS_AonBanti) {
1839  nmg_reclassify_lu_eu(lu, classlist, NMG_CLASS_AonBanti);
1840  }
1841  reason = "edges identical with radial face";
1842  status = ON_SURF;
1843  goto out;
1844  }
1845 
1846  /* OK, the edgeuses are all "on", but the loop isn't. Time to
1847  * decide if the loop is "out" or "in". To do this, we look for
1848  * an edgeuse radial to one of the edgeuses in the loop which is
1849  * a part of a face in the other shell. If/when we find such a
1850  * radial edge, we check the direction (in/out) of the faceuse normal.
1851  * if the faceuse normal is pointing out of the shell, we are outside.
1852  * if the faceuse normal is pointing into the shell, we are inside.
1853  */
1854 
1855  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1856  bu_log("Checking radial faces:\n");
1857  nmg_pr_fu_around_eu(eu, tol);
1858  }
1859 
1860  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
1861  p = eu->radial_p;
1862  do {
1863  if (*p->up.magic_p == NMG_LOOPUSE_MAGIC &&
1864  *p->up.lu_p->up.magic_p == NMG_FACEUSE_MAGIC &&
1865  p->up.lu_p->up.fu_p->s_p == s) {
1866 
1867  if (p->up.lu_p->up.fu_p->orientation == OT_OPPOSITE) {
1868  NMG_INDEX_SET(classlist[NMG_CLASS_AinB], lu->l_p);
1869  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1870  bu_log("Loop is INSIDE of fu %p\n", (void *)p->up.lu_p->up.fu_p);
1871  }
1872  reason = "radial faceuse is OT_OPPOSITE";
1873  nmg_class = NMG_CLASS_AinB;
1874  status = INSIDE;
1875  goto out;
1876  } else if (p->up.lu_p->up.fu_p->orientation == OT_SAME) {
1877  NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], lu->l_p);
1878  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1879  bu_log("Loop is OUTSIDEof fu %p\n", (void *)p->up.lu_p->up.fu_p);
1880  }
1881  reason = "radial faceuse is OT_SAME";
1882  nmg_class = NMG_CLASS_AoutB;
1883  status = OUTSIDE;
1884  goto out;
1885  } else {
1886  bu_bomb("class_lu_vs_s(): bad fu orientation\n");
1887  }
1888  }
1889  p = p->eumate_p->radial_p;
1890  } while (p != eu->eumate_p);
1891  }
1892 
1893  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1894  bu_log("Loop is OUTSIDE 'cause it isn't anything else\n");
1895  }
1896 
1897  /* Since we didn't find any radial faces to classify ourselves against
1898  * and we already know that the edges are all "on" that must mean that
1899  * the loopuse is "on" a wireframe portion of the shell.
1900  */
1901 
1902  NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], lu->l_p);
1903  reason = "loopuse is ON a wire loop in the shell";
1904  nmg_class = NMG_CLASS_AoutB;
1905  status = OUTSIDE;
1906 
1907 out:
1908  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1909  bu_log("class_lu_vs_s(lu=%p) return %s (%s) because %s\n",
1910  (void *)lu, nmg_class_status(status), nmg_class_name(nmg_class), reason);
1911  }
1912 
1913  return status;
1914 }
1915 
1916 
1917 /**
1918  * Called by -
1919  * nmg_class_shells()
1920  */
1921 static void
1922 class_fu_vs_s(struct faceuse *fu, struct shell *s, char **classlist, const struct bn_tol *tol)
1923 {
1924  struct loopuse *lu;
1925  int nmg_class, in, out, on;
1926  plane_t n;
1927 
1928  NMG_CK_FACEUSE(fu);
1929  NMG_CK_SHELL(s);
1930 
1931  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
1932  NMG_GET_FU_PLANE(n, fu);
1933  PLPRINT("\nclass_fu_vs_s plane equation:", n);
1934  }
1935 
1936  in = out = on = 0;
1937  for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
1938  NMG_CK_LOOPUSE(lu);
1939  nmg_class = class_lu_vs_s(lu, s, classlist, tol);
1940  switch (nmg_class) {
1941  case INSIDE : ++in;
1942  break;
1943  case OUTSIDE : ++out;
1944  break;
1945  case ON_SURF : ++on;
1946  break;
1947  default : bu_bomb("class_fu_vs_s: bad class for faceuse\n");
1948  }
1949  }
1950 
1951  if (in == 0 && out == 0 && on > 0) {
1952  NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], fu->f_p);
1953  } else if (in == 0 && out > 0 && on == 0) {
1954  NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], fu->f_p);
1955  } else {
1956  NMG_INDEX_SET(classlist[NMG_CLASS_AinB], fu->f_p);
1957  }
1958 
1959  if (RTG.NMG_debug & DEBUG_CLASSIFY)
1960  bu_log("class_fu_vs_s() END\n");
1961 }
1962 
1963 
1964 /**
1965  * Classify one shell WRT the other shell
1966  *
1967  * Implicit return -
1968  * Each element's classification will be represented by a
1969  * SET entry in the appropriate classlist[] array.
1970  *
1971  * Called by -
1972  * nmg_bool.c
1973  */
1974 void
1975 nmg_class_shells(struct shell *sA, struct shell *sB, char **classlist, const struct bn_tol *tol)
1976 {
1977  struct faceuse *fu;
1978  struct loopuse *lu;
1979  struct edgeuse *eu;
1980 
1981  NMG_CK_SHELL(sA);
1982  NMG_CK_SHELL(sB);
1983  BN_CK_TOL(tol);
1984 
1985  if (RTG.NMG_debug & DEBUG_CLASSIFY &&
1986  BU_LIST_NON_EMPTY(&sA->fu_hd))
1987  bu_log("nmg_class_shells - doing faces\n");
1988 
1989  fu = BU_LIST_FIRST(faceuse, &sA->fu_hd);
1990  while (BU_LIST_NOT_HEAD(fu, &sA->fu_hd)) {
1991 
1992  class_fu_vs_s(fu, sB, classlist, tol);
1993 
1994  if (BU_LIST_PNEXT(faceuse, fu) == fu->fumate_p)
1995  fu = BU_LIST_PNEXT_PNEXT(faceuse, fu);
1996  else
1997  fu = BU_LIST_PNEXT(faceuse, fu);
1998  }
1999 
2000  if (RTG.NMG_debug & DEBUG_CLASSIFY &&
2001  BU_LIST_NON_EMPTY(&sA->lu_hd))
2002  bu_log("nmg_class_shells - doing loops\n");
2003 
2004  lu = BU_LIST_FIRST(loopuse, &sA->lu_hd);
2005  while (BU_LIST_NOT_HEAD(lu, &sA->lu_hd)) {
2006 
2007  (void)class_lu_vs_s(lu, sB, classlist, tol);
2008 
2009  if (BU_LIST_PNEXT(loopuse, lu) == lu->lumate_p)
2010  lu = BU_LIST_PNEXT_PNEXT(loopuse, lu);
2011  else
2012  lu = BU_LIST_PNEXT(loopuse, lu);
2013  }
2014 
2015  if (RTG.NMG_debug & DEBUG_CLASSIFY &&
2016  BU_LIST_NON_EMPTY(&sA->eu_hd))
2017  bu_log("nmg_class_shells - doing edges\n");
2018 
2019  eu = BU_LIST_FIRST(edgeuse, &sA->eu_hd);
2020  while (BU_LIST_NOT_HEAD(eu, &sA->eu_hd)) {
2021 
2022  (void)class_eu_vs_s(eu, sB, classlist, tol);
2023 
2024  if (BU_LIST_PNEXT(edgeuse, eu) == eu->eumate_p)
2025  eu = BU_LIST_PNEXT_PNEXT(edgeuse, eu);
2026  else
2027  eu = BU_LIST_PNEXT(edgeuse, eu);
2028  }
2029 
2030  if (sA->vu_p) {
2031  if (RTG.NMG_debug)
2032  bu_log("nmg_class_shells - doing vertex\n");
2033  (void)class_vu_vs_s(sA->vu_p, sB, classlist, tol);
2034  }
2035 }
2036 
2037 
2038 /**
2039  * A generally available interface to nmg_class_pt_l
2040  *
2041  * returns the classification from nmg_class_pt_l
2042  * or a (-1) on error
2043  *
2044  * Called by -
2045  * nmg_extrude.c / nmg_fix_overlapping_loops()
2046  *
2047  * XXX DANGER: Calls nmg_class_pt_l(), which does not work well.
2048  */
2049 int
2050 nmg_classify_pt_loop(const point_t pt,
2051  const struct loopuse *lu,
2052  const struct bn_tol *tol)
2053 {
2054  struct neighbor closest;
2055  struct faceuse *fu;
2056  plane_t n;
2057  fastf_t dist;
2058 
2059  NMG_CK_LOOPUSE(lu);
2060  BN_CK_TOL(tol);
2061 
2062  bu_log("DANGER: nmg_classify_pt_loop() is calling nmg_class_pt_l(), which does not work well\n");
2063  if (*lu->up.magic_p != NMG_FACEUSE_MAGIC) {
2064  bu_log("nmg_classify_pt_loop: lu not part of a faceuse!!\n");
2065  return -1;
2066  }
2067 
2068  fu = lu->up.fu_p;
2069 
2070  /* Validate distance from point to plane */
2071  NMG_GET_FU_PLANE(n, fu);
2072  if ((dist = fabs(DIST_PT_PLANE(pt, n))) > tol->dist) {
2073  bu_log("nmg_classify_pt_l() ERROR, point (%g, %g, %g) not on face, dist=%g\n",
2074  V3ARGS(pt), dist);
2075  return -1;
2076  }
2077 
2078 
2079  /* find the closest approach in this face to the projected point */
2080  closest.dist = MAX_FASTF;
2081  closest.p.eu = (struct edgeuse *)NULL;
2082  closest.nmg_class = NMG_CLASS_AoutB; /* default return */
2083 
2084  nmg_class_pt_l(&closest, pt, lu, tol);
2085 
2086  return closest.nmg_class;
2087 }
2088 
2089 
2090 /**
2091  * Find any point that is interior to LU
2092  *
2093  * Returns:
2094  * 0 - All is well
2095  * 1 - Loop is not part of a faceuse
2096  * 2 - Loop is a single vertexuse
2097  * 3 - Loop is a crack
2098  * 4 - Just plain can't find an interior point
2099  */
2100 int
2101 nmg_get_interior_pt(fastf_t *pt, const struct loopuse *lu, const struct bn_tol *tol)
2102 {
2103  struct edgeuse *eu;
2104  fastf_t point_count = 0.0;
2105  double one_over_count;
2106  point_t test_pt;
2107  point_t average_pt;
2108  int i;
2109 
2110  NMG_CK_LOOPUSE(lu);
2111  BN_CK_TOL(tol);
2112 
2113  if (*lu->up.magic_p != NMG_FACEUSE_MAGIC)
2114  return 1;
2115 
2116  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC)
2117  return 2;
2118 
2119  if (nmg_loop_is_a_crack(lu))
2120  return 3;
2121 
2122  /* first try just averaging all the vertices */
2123  VSETALL(average_pt, 0.0);
2124  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
2125  struct vertex_g *vg;
2126  NMG_CK_EDGEUSE(eu);
2127 
2128  vg = eu->vu_p->v_p->vg_p;
2129  NMG_CK_VERTEX_G(vg);
2130 
2131  VADD2(average_pt, average_pt, vg->coord);
2132  point_count++;
2133  }
2134 
2135  one_over_count = 1.0/point_count;
2136  VSCALE(average_pt, average_pt, one_over_count);
2137  VMOVE(test_pt, average_pt);
2138 
2139  if (nmg_class_pt_lu_except(test_pt, lu, (struct edge *)NULL, tol) == NMG_CLASS_AinB) {
2140  VMOVE(pt, test_pt);
2141  return 0;
2142  }
2143 
2144  for (i = 0; i < 3; i++) {
2145 
2146  double tol_mult;
2147 
2148  /* Try moving just a little left of an edge */
2149  switch (i) {
2150  case 0:
2151  tol_mult = 5.0 * tol->dist;
2152  break;
2153  case 1:
2154  tol_mult = 1.5 * tol->dist;
2155  break;
2156  case 2:
2157  tol_mult = 1.005 * tol->dist;
2158  break;
2159  default:
2160  /* sanity check */
2161  tol_mult = 1;
2162  break;
2163  }
2164  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
2165  vect_t left;
2166  struct vertex_g *vg1, *vg2;
2167 
2168  (void)nmg_find_eu_leftvec(left, eu);
2169 
2170  vg1 = eu->vu_p->v_p->vg_p;
2171  vg2 = eu->eumate_p->vu_p->v_p->vg_p;
2172 
2173  VADD2(test_pt, vg1->coord, vg2->coord);
2174  VSCALE(test_pt, test_pt, 0.5);
2175 
2176  VJOIN1(test_pt, test_pt, tol_mult, left);
2177  if (nmg_class_pt_lu_except(test_pt, lu, (struct edge *)NULL, tol) == NMG_CLASS_AinB) {
2178  VMOVE(pt, test_pt);
2179  return 0;
2180  }
2181  }
2182  }
2183 
2184  if (RTG.NMG_debug & DEBUG_CLASSIFY) {
2185  bu_log("nmg_get_interior_pt: Couldn't find interior point for lu %p\n", (void *)lu);
2186  nmg_pr_lu_briefly(lu, "");
2187  }
2188 
2189  VMOVE(pt, average_pt);
2190  return 4;
2191 }
2192 
2193 
2194 /**
2195  * Generally available classifier for
2196  * determining if one loop is within another
2197  *
2198  * returns classification based on nmg_class_pt_l results
2199  *
2200  * Called by -
2201  * nmg_misc.c / nmg_split_loops_handler()
2202  *
2203  */
2204 int
2205 nmg_classify_lu_lu(const struct loopuse *lu1, const struct loopuse *lu2, const struct bn_tol *tol)
2206 {
2207  struct faceuse *fu1, *fu2;
2208  struct edgeuse *eu;
2209  int share_edges;
2210  int lu1_eu_count = 0;
2211  int lu2_eu_count = 0;
2212 
2213  NMG_CK_LOOPUSE(lu1);
2214  NMG_CK_LOOPUSE(lu2);
2215  BN_CK_TOL(tol);
2216 
2217  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2218  bu_log("nmg_classify_lu_lu(lu1=%p, lu2=%p)\n", (void *)lu1, (void *)lu2);
2219 
2220  if (lu1 == lu2 || lu1 == lu2->lumate_p)
2221  return NMG_CLASS_AonBshared;
2222 
2223  if (*lu1->up.magic_p != NMG_FACEUSE_MAGIC) {
2224  bu_log("nmg_classify_lu_lu: lu1 not part of a faceuse\n");
2225  return -1;
2226  }
2227 
2228  if (*lu2->up.magic_p != NMG_FACEUSE_MAGIC) {
2229  bu_log("nmg_classify_lu_lu: lu2 not part of a faceuse\n");
2230  return -1;
2231  }
2232 
2233  fu1 = lu1->up.fu_p;
2234  NMG_CK_FACEUSE(fu1);
2235  fu2 = lu2->up.fu_p;
2236  NMG_CK_FACEUSE(fu2);
2237 
2238  if (fu1->f_p != fu2->f_p) {
2239  bu_log("nmg_classify_lu_lu: loops are not in same face\n");
2240  return -1;
2241  }
2242 
2243  /* do simple check for two loops of the same vertices */
2244  if (BU_LIST_FIRST_MAGIC(&lu1->down_hd) == NMG_EDGEUSE_MAGIC &&
2245  BU_LIST_FIRST_MAGIC(&lu2->down_hd) == NMG_EDGEUSE_MAGIC) {
2246  struct edgeuse *eu1_start, *eu2_start;
2247  struct edgeuse *eu1, *eu2;
2248 
2249  /* count EU's in lu1 */
2250  for (BU_LIST_FOR(eu, edgeuse, &lu1->down_hd))
2251  lu1_eu_count++;
2252 
2253  /* count EU's in lu2 */
2254  for (BU_LIST_FOR(eu, edgeuse, &lu2->down_hd))
2255  lu2_eu_count++;
2256 
2257  share_edges = 1;
2258  eu1_start = BU_LIST_FIRST(edgeuse, &lu1->down_hd);
2259  NMG_CK_EDGEUSE(eu1_start);
2260  eu2_start = BU_LIST_FIRST(edgeuse, &lu2->down_hd);
2261  NMG_CK_EDGEUSE(eu2_start);
2262  while (BU_LIST_NOT_HEAD(eu2_start, &lu2->down_hd) &&
2263  eu2_start->e_p != eu1_start->e_p)
2264  {
2265  NMG_CK_EDGEUSE(eu2_start);
2266  eu2_start = BU_LIST_PNEXT(edgeuse, &eu2_start->l);
2267  }
2268 
2269  if (BU_LIST_NOT_HEAD(eu2_start, &lu2->down_hd) &&
2270  eu1_start->e_p == eu2_start->e_p)
2271  {
2272  /* check the rest of the loop */
2273  share_edges = 1;
2274  eu1 = eu1_start;
2275  eu2 = eu2_start;
2276  do {
2277  if (eu1->e_p != eu2->e_p) {
2278  share_edges = 0;
2279  break;
2280  }
2281  eu1 = BU_LIST_PNEXT_CIRC(edgeuse, &eu1->l);
2282  eu2 = BU_LIST_PNEXT_CIRC(edgeuse, &eu2->l);
2283  } while (eu1 != eu1_start);
2284 
2285  if (!share_edges) {
2286  /* maybe the other way round */
2287  share_edges = 1;
2288  eu1 = eu1_start;
2289  eu2 = eu2_start;
2290  do {
2291  if (eu1->e_p != eu2->e_p) {
2292  share_edges = 0;
2293  break;
2294  }
2295  eu1 = BU_LIST_PNEXT_CIRC(edgeuse, &eu1->l);
2296  eu2 = BU_LIST_PPREV_CIRC(edgeuse, &eu2->l);
2297  } while (eu1 != eu1_start);
2298  }
2299 
2300  if (share_edges && lu1_eu_count == lu2_eu_count) {
2301  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2302  bu_log("nmg_classify_lu_lu returning NMG_CLASS_AonBshared\n");
2303  return NMG_CLASS_AonBshared;
2304  } else {
2305  vect_t inward1, inward2;
2306 
2307  /* not all edges are shared,
2308  * try to fnd a vertex in lu1
2309  * that is not in lu2
2310  */
2311  for (BU_LIST_FOR(eu, edgeuse, &lu1->down_hd)) {
2312  struct vertex_g *vg;
2313  int nmg_class;
2314 
2315  NMG_CK_EDGEUSE(eu);
2316 
2317  vg = eu->vu_p->v_p->vg_p;
2318  NMG_CK_VERTEX_G(vg);
2319 
2320  if (nmg_find_vertex_in_lu(eu->vu_p->v_p, lu2) != NULL)
2321  continue;
2322 
2323  nmg_class = nmg_class_pt_lu_except(vg->coord, lu2, (struct edge *)NULL, tol);
2324  if (nmg_class != NMG_CLASS_AonBshared && nmg_class != NMG_CLASS_AonBanti) {
2325  if (lu2->orientation == OT_SAME) {
2326  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2327  bu_log("nmg_classify_lu_lu returning %s\n", nmg_class_name(nmg_class));
2328  return nmg_class;
2329  } else {
2330  if (nmg_class == NMG_CLASS_AinB) {
2331  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2332  bu_log("nmg_classify_lu_lu returning NMG_CLASS_AoutB\n");
2333  return NMG_CLASS_AoutB;
2334  }
2335  if (nmg_class == NMG_CLASS_AoutB) {
2336  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2337  bu_log("nmg_classify_lu_lu returning NMG_CLASS_AinB\n");
2338  return NMG_CLASS_AinB;
2339  }
2340  }
2341  }
2342  }
2343 
2344  /* Some of lu1 edges are on lu2, but loops are not shared.
2345  * Compare inward vectors of a shared edge.
2346  */
2347 
2348  nmg_find_eu_leftvec(inward1, eu1_start);
2349  if (lu1->orientation == OT_OPPOSITE)
2350  VREVERSE(inward1, inward1);
2351 
2352  nmg_find_eu_leftvec(inward2, eu2_start);
2353  if (lu2->orientation == OT_OPPOSITE)
2354  VREVERSE(inward2, inward2);
2355 
2356  if (VDOT(inward1, inward2) < -SMALL_FASTF)
2357  return NMG_CLASS_AoutB;
2358  else
2359  return NMG_CLASS_AinB;
2360  }
2361  } else {
2362  /* no matching edges, classify by vertices */
2363  for (BU_LIST_FOR(eu, edgeuse, &lu1->down_hd)) {
2364  struct vertex_g *vg;
2365  int nmg_class;
2366 
2367  NMG_CK_EDGEUSE(eu);
2368 
2369  vg = eu->vu_p->v_p->vg_p;
2370  NMG_CK_VERTEX_G(vg);
2371 
2372  if (nmg_find_vertex_in_lu(eu->vu_p->v_p, lu2) != NULL)
2373  continue;
2374 
2375  nmg_class = nmg_class_pt_lu_except(vg->coord, lu2, (struct edge *)NULL, tol);
2376  if (nmg_class != NMG_CLASS_AonBshared && nmg_class != NMG_CLASS_AonBanti) {
2377  if (lu2->orientation == OT_SAME) {
2378  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2379  bu_log("nmg_classify_lu_lu returning %s\n", nmg_class_name(nmg_class));
2380  return nmg_class;
2381  } else {
2382  if (nmg_class == NMG_CLASS_AinB) {
2383  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2384  bu_log("nmg_classify_lu_lu returning NMG_CLASS_AoutB\n");
2385  return NMG_CLASS_AoutB;
2386  }
2387  if (nmg_class == NMG_CLASS_AoutB) {
2388  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2389  bu_log("nmg_classify_lu_lu returning NMG_CLASS_AinB\n");
2390  return NMG_CLASS_AinB;
2391  }
2392  }
2393  }
2394  }
2395 
2396  /* if we get here, all vertices are shared,
2397  * but no edges are!!!!! Check the midpoint of edges.
2398  */
2399  for (BU_LIST_FOR(eu, edgeuse, &lu1->down_hd)) {
2400  struct vertex_g *vg1, *vg2;
2401  int nmg_class;
2402  point_t mid_pt;
2403 
2404  vg1 = eu->vu_p->v_p->vg_p;
2405  vg2 = eu->eumate_p->vu_p->v_p->vg_p;
2406 
2407  VADD2(mid_pt, vg1->coord, vg2->coord);
2408  VSCALE(mid_pt, mid_pt, 0.5);
2409 
2410  nmg_class = nmg_class_pt_lu_except(mid_pt, lu2, (struct edge *)NULL, tol);
2411  if (nmg_class != NMG_CLASS_AonBshared && nmg_class != NMG_CLASS_AonBanti) {
2412  if (lu2->orientation == OT_SAME) {
2413  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2414  bu_log("nmg_classify_lu_lu returning %s\n", nmg_class_name(nmg_class));
2415  return nmg_class;
2416  } else {
2417  if (nmg_class == NMG_CLASS_AinB) {
2418  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2419  bu_log("nmg_classify_lu_lu returning NMG_CLASS_AoutB\n");
2420  return NMG_CLASS_AoutB;
2421  }
2422  if (nmg_class == NMG_CLASS_AoutB) {
2423  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2424  bu_log("nmg_classify_lu_lu returning NMG_CLASS_AinB\n");
2425  return NMG_CLASS_AinB;
2426  }
2427  }
2428  }
2429  }
2430 
2431  /* Should never get here */
2432  bu_log("nmg_classify_lu_lu: Cannot classify lu %p w.r.t. lu %p\n",
2433  (void *)lu1, (void *)lu2);
2434  return NMG_CLASS_Unknown;
2435  }
2436  } else if (BU_LIST_FIRST_MAGIC(&lu1->down_hd) == NMG_VERTEXUSE_MAGIC &&
2437  BU_LIST_FIRST_MAGIC(&lu2->down_hd) == NMG_VERTEXUSE_MAGIC)
2438  {
2439  struct vertexuse *vu1, *vu2;
2440 
2441  vu1 = BU_LIST_FIRST(vertexuse, &lu1->down_hd);
2442  vu2 = BU_LIST_FIRST(vertexuse, &lu2->down_hd);
2443 
2444  if (vu1->v_p == vu2->v_p) {
2445  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2446  bu_log("nmg_classify_lu_lu returning NMG_CLASS_AonBshared\n");
2447  return NMG_CLASS_AonBshared;
2448  } else {
2449  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2450  bu_log("nmg_classify_lu_lu returning NMG_CLASS_AoutB\n");
2451  return NMG_CLASS_AoutB;
2452  }
2453  }
2454 
2455  if (BU_LIST_FIRST_MAGIC(&lu1->down_hd) == NMG_VERTEXUSE_MAGIC) {
2456  struct vertexuse *vu;
2457  struct vertex_g *vg;
2458  int nmg_class;
2459 
2460  vu = BU_LIST_FIRST(vertexuse, &lu1->down_hd);
2461  NMG_CK_VERTEXUSE(vu);
2462  vg = vu->v_p->vg_p;
2463  NMG_CK_VERTEX_G(vg);
2464 
2465  nmg_class = nmg_class_pt_lu_except(vg->coord, lu2,
2466  (struct edge *)NULL, tol);
2467 
2468  if (lu2->orientation == OT_OPPOSITE) {
2469  if (nmg_class == NMG_CLASS_AoutB)
2470  nmg_class = NMG_CLASS_AinB;
2471  else if (nmg_class == NMG_CLASS_AinB)
2472  nmg_class = NMG_CLASS_AoutB;
2473  }
2474  if (RTG.NMG_debug & DEBUG_CLASSIFY)
2475  bu_log("nmg_classify_lu_lu returning %s\n", nmg_class_name(nmg_class));
2476  return nmg_class;
2477  } else if (BU_LIST_FIRST_MAGIC(&lu2->down_hd) == NMG_VERTEXUSE_MAGIC)
2478  return NMG_CLASS_AoutB;
2479 
2480  bu_log("nmg_classify_lu_lu: ERROR, Should not get here!!!\n");
2481  bu_bomb("nmg_classify_lu_lu: ERROR, Should not get here!!!\n");
2482 
2483  return -1; /* to make the compilers happy */
2484 }
2485 
2486 
2487 /**
2488  * Classify one shell (s2) with respect to another (s).
2489  *
2490  * returns:
2491  * NMG_CLASS_AinB if s2 is inside s
2492  * NMG_CLASS_AoutB is s2 is outside s
2493  * NMG_CLASS_Unknown if we can't tell
2494  *
2495  * Assumes (but does not verify) that these two shells do not
2496  * overlap, but are either entirely separate or entirely within
2497  * one or the other.
2498  */
2499 int
2500 nmg_classify_s_vs_s(struct shell *s2, struct shell *s, const struct bn_tol *tol)
2501 {
2502  int i;
2503  int nmg_class;
2504  struct faceuse *fu;
2505  struct loopuse *lu;
2506  struct edgeuse *eu;
2507  point_t pt_in_s2;
2508  struct bu_ptbl verts;
2509 
2510  if (!V3RPP1_IN_RPP2(s2->sa_p->min_pt, s2->sa_p->max_pt, s->sa_p->min_pt, s->sa_p->max_pt))
2511  return NMG_CLASS_AoutB;
2512 
2513  /* shell s2 may be inside shell s
2514  Get a point from s2 to classify vs s */
2515 
2516  if (BU_LIST_NON_EMPTY(&s2->fu_hd)) {
2517  fu = BU_LIST_FIRST(faceuse, &s2->fu_hd);
2518  lu = BU_LIST_FIRST(loopuse, &fu->lu_hd);
2519  eu = BU_LIST_FIRST(edgeuse, &lu->down_hd);
2520  VMOVE(pt_in_s2, eu->vu_p->v_p->vg_p->coord);
2521  nmg_class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
2522  if (nmg_class == NMG_CLASS_AinB)
2523  return NMG_CLASS_AinB; /* shell s2 is inside shell s */
2524  else if (nmg_class == NMG_CLASS_AoutB)
2525  return NMG_CLASS_AoutB; /* shell s2 is not inside shell s */
2526 
2527  /* try other end of this EU */
2528  VMOVE(pt_in_s2, eu->eumate_p->vu_p->v_p->vg_p->coord);
2529  nmg_class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
2530  if (nmg_class == NMG_CLASS_AinB)
2531  return NMG_CLASS_AinB; /* shell s2 is inside shell s */
2532  else if (nmg_class == NMG_CLASS_AoutB)
2533  return NMG_CLASS_AoutB; /* shell s2 is not inside shell s */
2534  }
2535 
2536  if (BU_LIST_NON_EMPTY(&s2->lu_hd)) {
2537  lu = BU_LIST_FIRST(loopuse, &s2->lu_hd);
2538  eu = BU_LIST_FIRST(edgeuse, &lu->down_hd);
2539  VMOVE(pt_in_s2, eu->vu_p->v_p->vg_p->coord);
2540  nmg_class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
2541  if (nmg_class == NMG_CLASS_AinB)
2542  return NMG_CLASS_AinB; /* shell s2 is inside shell s */
2543  else if (nmg_class == NMG_CLASS_AoutB)
2544  return NMG_CLASS_AoutB; /* shell s2 is not inside shell s */
2545 
2546  /* try other end of this EU */
2547  VMOVE(pt_in_s2, eu->eumate_p->vu_p->v_p->vg_p->coord);
2548  nmg_class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
2549  if (nmg_class == NMG_CLASS_AinB)
2550  return NMG_CLASS_AinB; /* shell s2 is inside shell s */
2551  else if (nmg_class == NMG_CLASS_AoutB)
2552  return NMG_CLASS_AoutB; /* shell s2 is not inside shell s */
2553  }
2554 
2555  if (BU_LIST_NON_EMPTY(&s2->eu_hd)) {
2556  eu = BU_LIST_FIRST(edgeuse, &s2->eu_hd);
2557  VMOVE(pt_in_s2, eu->vu_p->v_p->vg_p->coord);
2558  nmg_class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
2559  if (nmg_class == NMG_CLASS_AinB)
2560  return NMG_CLASS_AinB; /* shell s2 is inside shell s */
2561  else if (nmg_class == NMG_CLASS_AoutB)
2562  return NMG_CLASS_AoutB; /* shell s2 is not inside shell s */
2563 
2564  /* try other end of this EU */
2565  VMOVE(pt_in_s2, eu->eumate_p->vu_p->v_p->vg_p->coord);
2566  nmg_class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
2567  if (nmg_class == NMG_CLASS_AinB)
2568  return NMG_CLASS_AinB; /* shell s2 is inside shell s */
2569  else if (nmg_class == NMG_CLASS_AoutB)
2570  return NMG_CLASS_AoutB; /* shell s2 is not inside shell s */
2571  }
2572 
2573  if (s2->vu_p && s2->vu_p->v_p->vg_p) {
2574  VMOVE(pt_in_s2, s2->vu_p->v_p->vg_p->coord);
2575  nmg_class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
2576  if (nmg_class == NMG_CLASS_AinB)
2577  return NMG_CLASS_AinB; /* shell s2 is inside shell s */
2578  else if (nmg_class == NMG_CLASS_AoutB)
2579  return NMG_CLASS_AoutB; /* shell s2 is not inside shell s */
2580  }
2581 
2582  /* classification returned NMG_CLASS_AonB, so need to try other points */
2583  nmg_vertex_tabulate(&verts, &s2->l.magic);
2584  for (i = 0; i < BU_PTBL_END(&verts); i++) {
2585  struct vertex *v;
2586 
2587  v = (struct vertex *)BU_PTBL_GET(&verts, i);
2588 
2589  VMOVE(pt_in_s2, v->vg_p->coord);
2590  nmg_class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
2591  if (nmg_class == NMG_CLASS_AinB) {
2592  bu_ptbl_free(&verts);
2593  return NMG_CLASS_AinB; /* shell s2 is inside shell s */
2594  } else if (nmg_class == NMG_CLASS_AoutB) {
2595  bu_ptbl_free(&verts);
2596  return NMG_CLASS_AoutB; /* shell s2 is not inside shell s */
2597  }
2598  }
2599  bu_ptbl_free(&verts);
2600 
2601  /* every point of s2 is on s !!!!!!! */
2602  return NMG_CLASS_Unknown;
2603 }
2604 
2605 
2606 /*
2607  * Local Variables:
2608  * mode: C
2609  * tab-width: 8
2610  * indent-tabs-mode: t
2611  * c-file-style: "stroustrup"
2612  * End:
2613  * ex: shiftwidth=4 tabstop=8
2614  */
void nmg_pl_s(FILE *fp, const struct shell *s)
Definition: nmg_plot.c:746
#define BU_LIST_PNEXT_CIRC(structure, p)
Definition: list.h:442
#define BU_LIST_FOR(p, structure, hp)
Definition: list.h:365
void nmg_pr_class_status(char *prefix, int status)
Definition: nmg_class.c:111
int nmg_classify_pt_loop(const point_t pt, const struct loopuse *lu, const struct bn_tol *tol)
Definition: nmg_class.c:2050
#define NMG_EDGEUSE_MAGIC
Definition: magic.h:120
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
struct faceuse * nmg_find_fu_of_eu(const struct edgeuse *eu)
Definition: nmg_info.c:270
void nmg_stash_model_to_file(const char *filename, const struct model *m, const char *title)
Definition: nmg_misc.c:4500
int nmg_class
Definition: nmg_class.c:73
#define RT_DOT_TOL
Definition: raytrace.h:170
#define OUTSIDE
Definition: nmg_class.c:59
#define NMG_SHELL_MAGIC
Definition: magic.h:142
int nmg_class_pt_fu_except(const point_t pt, const struct faceuse *fu, const struct loopuse *ignore_lu, void(*eu_func)(struct edgeuse *, point_t, const char *), void(*vu_func)(struct vertexuse *, point_t, const char *), const char *priv, const int call_on_hits, const int in_or_out_only, const struct bn_tol *tol)
double dist
>= 0
Definition: tol.h:73
void nmg_euprint(const char *str, const struct edgeuse *eu)
Definition: nmg_pr.c:751
if lu s
Definition: nmg_mod.c:3860
lu
Definition: nmg_mod.c:3855
int nmg_get_interior_pt(fastf_t *pt, const struct loopuse *lu, const struct bn_tol *tol)
Definition: nmg_class.c:2101
#define VSETALL(a, s)
Definition: color.c:54
Definition: raytrace.h:215
int nmg_class_pt_lu_except(fastf_t *pt, const struct loopuse *lu, const struct edge *e_p, const struct bn_tol *tol)
Definition: nmg_pt_fu.c:1386
#define SMALL_FASTF
Definition: defines.h:342
void nmg_model_bb(fastf_t *min_pt, fastf_t *max_pt, const struct model *m)
Definition: nmg_info.c:166
Header file for the BRL-CAD common definitions.
ustring closest
int nmg_classify_lu_lu(const struct loopuse *lu1, const struct loopuse *lu2, const struct bn_tol *tol)
Definition: nmg_class.c:2205
void nmg_show_broken_classifier_stuff(uint32_t *p, char **classlist, int all_new, int fancy, const char *a_string)
Definition: nmg_plot.c:1733
#define BU_LIST_NON_EMPTY(hp)
Definition: list.h:296
const struct edgeuse * eu
Definition: nmg_class.c:69
#define MAX_FASTF
Definition: defines.h:340
#define NMG_LOOPUSE_MAGIC
Definition: magic.h:130
NMG_CK_LOOPUSE(lu)
BU_LIST_DEQUEUE & eu1
Definition: nmg_mod.c:3839
Definition: ptbl.h:62
Definition: color.c:49
#define MAX_DIR_TRYS
Definition: nmg_class.c:54
void nmg_pl_fu(FILE *fp, const struct faceuse *fu, long *b, int red, int green, int blue)
Definition: nmg_plot.c:722
int nmg_loop_is_a_crack(const struct loopuse *lu)
Definition: nmg_info.c:449
#define RT_G_DEBUG
Definition: raytrace.h:1718
int nmg_2lu_identical(const struct edgeuse *eu1, const struct edgeuse *eu2)
Definition: nmg_class.c:1156
uint32_t NMG_debug
debug bits for NMG's see nmg.h
Definition: raytrace.h:1699
struct faceuse * nmg_find_fu_of_lu(const struct loopuse *lu)
Definition: nmg_info.c:283
void * bu_calloc(size_t nelem, size_t elsize, const char *str)
Definition: malloc.c:321
struct shell * nmg_find_s_of_lu(const struct loopuse *lu)
Definition: nmg_info.c:220
struct vertexuse * nmg_find_vertex_in_lu(const struct vertex *v, const struct loopuse *lu)
Definition: nmg_extrude.c:266
#define BU_PTBL_GET(ptbl, i)
Definition: ptbl.h:108
void nmg_pr_orient(int orientation, const char *h)
Definition: nmg_pr.c:72
#define V3ARGS(a)
Definition: color.c:56
#define NEAR_ZERO(val, epsilon)
Definition: color.c:55
void nmg_pr_lu_briefly(const struct loopuse *lu, char *h)
Definition: nmg_pr.c:455
void nmg_class_shells(struct shell *sA, struct shell *sB, char **classlist, const struct bn_tol *tol)
Definition: nmg_class.c:1975
const char * nmg_class_status(int status)
Definition: nmg_class.c:96
#define BU_LIST_PNEXT(structure, p)
Definition: list.h:422
struct bu_list l
Definition: ptbl.h:63
#define HPRINT(a, b)
Definition: raytrace.h:1882
goto out
Definition: nmg_mod.c:3846
Support for uniform tolerances.
Definition: tol.h:71
struct shell * nmg_find_s_of_eu(const struct edgeuse *eu)
Definition: nmg_info.c:235
#define BN_CK_TOL(_p)
Definition: tol.h:82
#define BU_LIST_FIRST_MAGIC(hp)
Definition: list.h:416
int nmg_find_eu_leftvec(fastf_t *left, const struct edgeuse *eu)
Definition: nmg_info.c:1274
#define NMG_VERTEXUSE_MAGIC
Definition: magic.h:145
uint32_t magic
Magic # for mem id/check.
Definition: list.h:119
#define ON_SURF
Definition: nmg_class.c:58
void pl_color(register FILE *plotfp, int r, int g, int b)
Definition: plot3.c:325
void nmg_pl_lu(FILE *fp, const struct loopuse *lu, long *b, int red, int green, int blue)
Definition: nmg_plot.c:710
vect_t r_dir
Direction of ray (UNIT Length)
Definition: raytrace.h:219
void pdv_3line(register FILE *plotfp, const fastf_t *a, const fastf_t *b)
Definition: plot3.c:642
int nmg_classify_s_vs_s(struct shell *s2, struct shell *s, const struct bn_tol *tol)
Definition: nmg_class.c:2500
void bu_ptbl_free(struct bu_ptbl *b)
Definition: ptbl.c:226
const struct edgeuse * nmg_faceradial(const struct edgeuse *eu)
Definition: nmg_info.c:995
struct edgeuse * nmg_find_matching_eu_in_s(const struct edgeuse *eu1, const struct shell *s2)
Definition: nmg_info.c:641
int nmg_class_nothing_broken
Definition: nmg_plot.c:1443
#define BU_LIST_PPREV_CIRC(structure, p)
Definition: list.h:450
HIDDEN int code(fastf_t x, fastf_t y)
Definition: clip.c:43
#define INSIDE
Definition: nmg_class.c:57
double bn_dist_pt3_pt3(const point_t a, const point_t b)
Returns distance between two points.
point_t r_pt
Point at which ray starts.
Definition: raytrace.h:218
#define BU_PTBL_END(ptbl)
Definition: ptbl.h:106
void nmg_pr_lu(const struct loopuse *lu, char *h)
Definition: nmg_pr.c:405
int nmg_class_ray_vs_shell(struct xray *rp, const struct shell *s, const int in_or_out_only, const struct bn_tol *tol)
void nmg_pr_fu_around_eu(const struct edgeuse *eu, const struct bn_tol *tol)
Definition: nmg_pr.c:953
int nmg_class_lu_fu(const struct loopuse *lu, const struct bn_tol *tol)
Definition: nmg_class.c:503
int bn_dist_pt3_lseg3(fastf_t *dist, point_t pca, const point_t a, const point_t b, const point_t p, const struct bn_tol *tol)
Find the distance from a point P to a line segment described by the two endpoints A and B...
Definition: color.c:51
void nmg_pr_eu(const struct edgeuse *eu, char *h)
Definition: nmg_pr.c:552
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
int nmg_class_pt_s(const fastf_t *pt, const struct shell *s, const int in_or_out_only, const struct bn_tol *tol)
Definition: nmg_class.c:618
const char * nmg_class_name(int nmg_class)
Definition: nmg_eval.c:122
struct vertex * nmg_find_pt_in_shell(const struct shell *s, const fastf_t *pt, const struct bn_tol *tol)
Definition: nmg_info.c:1634
void nmg_pr_fu_briefly(const struct faceuse *fu, char *h)
Definition: nmg_pr.c:359
NMG_CK_SHELL(s)
fastf_t dist
Definition: nmg_class.c:72
#define NMG_FACEUSE_MAGIC
Definition: magic.h:124
union neighbor::@12 p
void bu_bomb(const char *str) _BU_ATTR_NORETURN
Definition: bomb.c:91
HIDDEN const point_t delta
Definition: sh_prj.c:618
double fastf_t
Definition: defines.h:300
#define VPRINT(a, b)
Definition: raytrace.h:1881
eu2
Definition: nmg_mod.c:3875
char * nmg_orientation(int orientation)
Definition: nmg_pr.c:50
#define BU_LIST_NOT_HEAD(p, hp)
Definition: list.h:324
#define BU_LIST_PNEXT_PNEXT(structure, p)
Definition: list.h:430
Definition: color.c:50
#define BU_LIST_FIRST(structure, hp)
Definition: list.h:312
const struct vertexuse * vu
Definition: nmg_class.c:68
void nmg_vertex_tabulate(struct bu_ptbl *tab, const uint32_t *magic_p)
Definition: nmg_info.c:1985
void nmg_reclassify_lu_eu(struct loopuse *lu, char **classlist, int newclass)
Definition: nmg_class.c:1258
struct rt_g RTG
Definition: globals.c:39
struct model * nmg_find_model(const uint32_t *magic_p_arg)
Definition: nmg_info.c:57