BRL-CAD
nmg_extrude.c
Go to the documentation of this file.
1 /* N M G _ E X T R U D E . C
2  * BRL-CAD
3  *
4  * Copyright (c) 1994-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_extrude.c
23  *
24  * Routines for extruding nmg's.
25  *
26  */
27 /** @} */
28 
29 #include "common.h"
30 
31 #include <math.h>
32 #include <string.h>
33 #include "bio.h"
34 
35 #include "vmath.h"
36 #include "db.h"
37 #include "nmg.h"
38 #include "rtgeom.h"
39 #include "raytrace.h"
40 
41 
42 /**
43  * Count number of vertices in an NMG loop.
44  */
45 static int
46 verts_in_nmg_loop(struct loopuse *lu)
47 {
48  int cnt;
49  struct edgeuse *eu;
50  struct vertex *v;
51 
52  /* Count number of vertices in loop. */
53  cnt = 0;
54  NMG_CK_LOOPUSE(lu);
55  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_EDGEUSE_MAGIC) {
56  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
57  NMG_CK_EDGEUSE(eu);
58  NMG_CK_EDGE(eu->e_p);
59  NMG_CK_VERTEXUSE(eu->vu_p);
60  NMG_CK_VERTEX(eu->vu_p->v_p);
61  cnt++;
62  }
63  } else if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC) {
64  v = BU_LIST_PNEXT(vertexuse, &lu->down_hd)->v_p;
65  NMG_CK_VERTEX(v);
66  cnt++;
67  } else
68  bu_bomb("verts_in_nmg_loop: bad loopuse\n");
69  return cnt;
70 }
71 
72 
73 /**
74  * Count number of vertices in an NMG face.
75  */
76 static int
77 verts_in_nmg_face(struct faceuse *fu)
78 {
79  int cnt;
80  struct loopuse *lu;
81 
82  cnt = 0;
83  for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd))
84  cnt += verts_in_nmg_loop(lu);
85  return cnt;
86 }
87 
88 
89 /**
90  * Translate a face using a vector's magnitude and direction.
91  */
92 void
93 nmg_translate_face(struct faceuse *fu, const fastf_t *Vec)
94 {
95  int cnt, /* Number of vertices in face. */
96  cur,
97  i,
98  in_there;
99  struct vertex **verts; /* List of verts in face. */
100  struct edgeuse *eu;
101  struct loopuse *lu;
102  struct vertex *v;
103  struct faceuse *fu_tmp;
104  plane_t pl;
105  struct bu_ptbl edge_g_tbl;
106 
107  bu_ptbl_init(&edge_g_tbl, 64, " &edge_g_tbl ");
108 
109  cur = 0;
110  cnt = verts_in_nmg_face(fu);
111  verts = (struct vertex **)
112  bu_malloc(cnt * sizeof(struct vertex *), "verts");
113  for (i = 0; i < cnt; i++)
114  verts[i] = NULL;
115 
116  /* Go through each loop and translate it. */
117  for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
118  NMG_CK_LOOPUSE(lu);
119  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_EDGEUSE_MAGIC) {
120  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
121  in_there = 0;
122  for (i = 0; i < cur && !in_there; i++)
123  if (verts[i] == eu->vu_p->v_p)
124  in_there = 1;
125  if (!in_there) {
126  verts[cur++] = eu->vu_p->v_p;
127  VADD2(eu->vu_p->v_p->vg_p->coord,
128  eu->vu_p->v_p->vg_p->coord,
129  Vec);
130  }
131  }
132  } else if (BU_LIST_FIRST_MAGIC(&lu->down_hd)
133  == NMG_VERTEXUSE_MAGIC) {
134  v = BU_LIST_FIRST(vertexuse, &lu->down_hd)->v_p;
135  NMG_CK_VERTEX(v);
136  VADD2(v->vg_p->coord, v->vg_p->coord, Vec);
137  } else
138  bu_bomb("nmg_translate_face: bad loopuse\n");
139  }
140 
141  fu_tmp = fu;
142  if (fu_tmp->orientation != OT_SAME)
143  fu_tmp = fu_tmp->fumate_p;
144 
145  /* Move edge geometry */
146  nmg_edge_g_tabulate(&edge_g_tbl, &fu->l.magic);
147  for (i=0; i<BU_PTBL_END(&edge_g_tbl); i++) {
148  long *ep;
149  struct edge_g_lseg *eg;
150 
151  ep = BU_PTBL_GET(&edge_g_tbl, i);
152  switch (*ep) {
154  eg = (struct edge_g_lseg *)ep;
155  NMG_CK_EDGE_G_LSEG(eg);
156  VADD2(eg->e_pt, eg->e_pt, Vec);
157  break;
159  /* XXX Move cnurb edge geometry??? */
160  break;
161  }
162  }
163 
164  bu_ptbl_free(&edge_g_tbl);
165 
166  if (nmg_loop_plane_area(BU_LIST_FIRST(loopuse, &fu_tmp->lu_hd), pl) < 0.0) {
167  bu_bomb("nmg_translate_face: Cannot calculate plane equation for face\n");
168  }
169  nmg_face_g(fu_tmp, pl);
170  bu_free((char *)verts, "verts");
171 }
172 
173 
174 /**
175  * Duplicate a given NMG face, move it by specified vector, and create
176  * a solid bounded by these faces.
177  */
178 int
179 nmg_extrude_face(struct faceuse *fu, const fastf_t *Vec, const struct bn_tol *tol)
180 /* Face to extrude. */
181 /* Magnitude and direction of extrusion. */
182 /* NMG tolerances. */
183 {
184  fastf_t cosang;
185  int nfaces;
186  struct faceuse *fu2;
187  struct faceuse **outfaces;
188  struct loopuse *lu, *lu2;
189  plane_t n;
190  int face_count=2;
191 
192  struct faceuse *nmg_dup_face(struct faceuse *, struct shell *);
193 
194 #define MIKE_TOL 0.0001
195 
196  NMG_CK_FACEUSE(fu);
197  BN_CK_TOL(tol);
198 
199  /* Duplicate and reverse face. */
200  fu2 = nmg_dup_face(fu, fu->s_p);
201  nmg_reverse_face(fu2);
202  if (fu2->orientation != OT_OPPOSITE)
203  fu2 = fu2->fumate_p;
204 
205  /* Figure out which face to translate. */
206  NMG_GET_FU_PLANE(n, fu);
207  cosang = VDOT(Vec, n);
208  if (NEAR_ZERO(cosang, MIKE_TOL))
209  bu_bomb("extrude_nmg_face: extrusion cannot be parallel to face\n");
210  if (cosang > 0.)
211  nmg_translate_face(fu, Vec);
212  else if (cosang < 0.)
213  nmg_translate_face(fu2->fumate_p, Vec);
214 
215  nfaces = verts_in_nmg_face(fu);
216  outfaces = (struct faceuse **)bu_calloc(nfaces+2, sizeof(struct faceuse *) ,
217  "nmg_extrude_face: outfaces");
218 
219  outfaces[0] = fu;
220  outfaces[1] = fu2->fumate_p;
221 
222  for (BU_LIST_FOR2(lu, lu2, loopuse, &fu->lu_hd, &fu2->lu_hd)) {
223  struct edgeuse *eu, *eu2;
224 
225  NMG_CK_LOOPUSE(lu);
226  NMG_CK_LOOPUSE(lu2);
227 
228  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC)
229  continue;
230  if (BU_LIST_FIRST_MAGIC(&lu2->down_hd) != NMG_EDGEUSE_MAGIC) {
231  bu_log("nmg_extrude_face: Original face and dup face don't match up!!\n");
232  return -1;
233  }
234  for (BU_LIST_FOR2(eu, eu2, edgeuse, &lu->down_hd, &lu2->down_hd)) {
235  struct vertex *vertlist[4];
236 
237  NMG_CK_EDGEUSE(eu);
238  NMG_CK_EDGEUSE(eu2);
239 
240  vertlist[0] = eu->vu_p->v_p;
241  vertlist[1] = eu2->vu_p->v_p;
242  vertlist[2] = eu2->eumate_p->vu_p->v_p;
243  vertlist[3] = eu->eumate_p->vu_p->v_p;
244  outfaces[face_count] = nmg_cface(fu->s_p, vertlist, 4);
245  if (nmg_calc_face_g(outfaces[face_count])) {
246  bu_log("nmg_extrude_face: failed to calculate plane eqn\n");
247  return -1;
248  }
249  face_count++;
250  }
251 
252  }
253 
254  nmg_gluefaces(outfaces, face_count, tol);
255 
256  bu_free((char *)outfaces, "nmg_extrude_face: outfaces");
257 
258  return 0;
259 }
260 
261 
262 /**
263  * find a use of vertex v in loopuse lu
264  */
265 struct vertexuse *
266 nmg_find_vertex_in_lu(const struct vertex *v, const struct loopuse *lu)
267 {
268  register struct edgeuse *eu;
269  struct vertexuse *ret_vu;
270 
271  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC) {
272  struct vertexuse *vu;
273 
274  vu = BU_LIST_FIRST(vertexuse, &lu->down_hd);
275 
276  if (vu->v_p == v)
277  return vu;
278  else
279  return (struct vertexuse *)NULL;
280  }
281 
282  ret_vu = (struct vertexuse *)NULL;
283  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
284  if (eu->vu_p->v_p == v) {
285  ret_vu = eu->vu_p;
286  break;
287  }
288  }
289 
290  return ret_vu;
291 }
292 
293 
294 /**
295  * recursive routine to build tables of edgeuse that may be used to
296  * create new loopuses. lu1 and lu2 are overlapping edgeuses from the
297  * same faceuse. This is a support routine for nmg_fix_overlapping
298  * loops
299  */
300 static void
301 nmg_start_new_loop(struct edgeuse *start_eu, struct loopuse *lu1, struct loopuse *lu2, struct bu_ptbl *loops)
302 {
303  struct bu_ptbl *new_lu_tab;
304  struct loopuse *this_lu;
305  struct loopuse *other_lu;
306  struct edgeuse *eu;
307  int edges=0;
308  int done=0;
309 
310  NMG_CK_EDGEUSE(start_eu);
311  NMG_CK_LOOPUSE(lu1);
312  NMG_CK_LOOPUSE(lu2);
313 
314  /* create a table to hold eu pointers for a new loop */
315  BU_ALLOC(new_lu_tab, struct bu_ptbl);
316  bu_ptbl_init(new_lu_tab, 64, " new_lu_tab ");
317 
318  /* add this table to the list of loops */
319  bu_ptbl_ins(loops, (long *)new_lu_tab);
320 
321  /* put edgeuses from lu1 into new_lu_tab until a vertex shared by
322  * lu1 and lu2 is encountered or until start_eu is encountered
323  */
324 
325  this_lu = lu1;
326  other_lu = lu2;
327  eu = start_eu;
328  while (!done) {
329  struct edgeuse *next_eu;
330  struct vertexuse *vu2;
331 
332  next_eu = BU_LIST_PNEXT_CIRC(edgeuse, &eu->l);
333 
334  /* skip this checking until we get by the first edgeuse */
335  if (edges) {
336  /* Are we back to the beginning? */
337  if (eu->vu_p->v_p == start_eu->vu_p->v_p) {
338  /* done with this loop */
339  done = 1;
340  break;
341  }
342 
343  /* Are we at an intersect point? */
344  vu2 = nmg_find_vertex_in_lu(eu->vu_p->v_p, other_lu);
345  if (vu2) {
346  /* Yes, we may need to start another loop */
347  struct edgeuse *eu2;
348  struct loopuse *lu_tmp;
349  int loop_started=0;
350  int i;
351 
352  eu2 = vu2->up.eu_p;
353 
354  /* check if a loop has already been started here */
355  for (i=0; i<BU_PTBL_END(loops); i++) {
356  struct bu_ptbl *loop_tab;
357  struct edgeuse *loop_start_eu;
358 
359  loop_tab = (struct bu_ptbl *)BU_PTBL_GET(loops, i);
360  loop_start_eu = (struct edgeuse *)BU_PTBL_GET(loop_tab, 0);
361  if (loop_start_eu == eu) {
362  loop_started = 1;
363  break;
364  }
365  }
366 
367  /* if a loop has not already been started here
368  * start one with the current edgeuse
369  */
370  if (!loop_started)
371  nmg_start_new_loop(eu, this_lu, other_lu, loops);
372 
373  /* continue this loop by switching to the other loopuse */
374  eu = eu2;
375  next_eu = BU_LIST_PNEXT_CIRC(edgeuse, &eu->l);
376  lu_tmp = this_lu;
377  this_lu = other_lu;
378  other_lu = lu_tmp;
379  }
380  }
381 
382  /* add this edgeuse to the current list */
383  bu_ptbl_ins(new_lu_tab, (long *)eu);
384 
385  edges++;
386 
387  /* go to the next edgeuse */
388  eu = next_eu;
389  }
390 
391 }
392 
393 
394 /**
395  * Looks at each faceuse in the shell and checks if loopuses in that
396  * faceuse overlap each other. This code can only handle faceuses that
397  * have at most one OT_SAME loopuse and one OT_OPPOSITE loopuse, so
398  * nmg_split_loops_into_faces is called to simplify the faceuses.
399  *
400  * Overlapping OT_SAME and OT_OPPOSITE loops are broken into some
401  * number of OT_SAME loopuses. An edgeuse (from the OT_SAME loopuse)
402  * departing from a point where the loops intersect and outside the
403  * OT_OPPOSITE loopuse is found as a starting point. Edgeuses from
404  * this loopuse are moved to a new loopuse until another intersect
405  * point is encountered. At that point, another loop is started using
406  * the next edgeuse and the current loopuse is continued by following
407  * the other loopuse. this is continued until the original edgeuse is
408  * encountered.
409  *
410  * If overlapping loops are found, new loopuses are created and the
411  * original loopuses are killed
412  */
413 void
414 nmg_fix_overlapping_loops(struct shell *s, const struct bn_tol *tol)
415 {
416  struct faceuse *fu;
417  struct edgeuse *start_eu;
418  struct bu_ptbl loops;
419  int i;
420 
421  NMG_CK_SHELL(s);
422 
423  if (RTG.NMG_debug & DEBUG_BASIC)
424  bu_log("nmg_fix_overlapping_loops: s = %p\n", (void *)s);
425 
426  /* this routine needs simple faceuses */
427  nmg_split_loops_into_faces(&s->l.magic, tol);
428 
429  /* This table will contain a list of bu_ptbl's when we are
430  * finished. Each of those bu_ptbl's will be a list of
431  * edgeuses that comprise a new loop
432  */
433  bu_ptbl_init(&loops, 64, " &loops ");
434 
435  /* process all faceuses in the shell */
436  for (BU_LIST_FOR(fu, faceuse, &s->fu_hd)) {
437  struct loopuse *lu1, *lu2;
438  struct edgeuse *eu1;
439  struct edgeuse *eu;
440  int inside=0;
441  int outside=0;
442 
443  NMG_CK_FACEUSE(fu);
444 
445  /* don't process the same face twice */
446  if (fu->orientation != OT_SAME)
447  continue;
448 
449  /* This is pretty simple-minded right now, assuming that there
450  * are only two loopuses
451  */
452  lu1 = BU_LIST_FIRST(loopuse, &fu->lu_hd);
453  NMG_CK_LOOPUSE(lu1);
454 
455  lu2 = BU_LIST_PNEXT(loopuse, &lu1->l);
456 
457  /* if there is only one loopuse, nothing to do */
458  if (BU_LIST_IS_HEAD(lu2, &fu->lu_hd))
459  continue;
460 
461  NMG_CK_LOOPUSE(lu2);
462 
463 
464  /* if the loopuses aren't both loops af edges, nothing to do */
465  if (BU_LIST_FIRST_MAGIC(&lu1->down_hd) != NMG_EDGEUSE_MAGIC)
466  continue;
467 
468  if (BU_LIST_FIRST_MAGIC(&lu2->down_hd) != NMG_EDGEUSE_MAGIC)
469  continue;
470 
471  /* if both loopuses are the same orientation, something is wrong */
472  if (lu1->orientation == lu2->orientation) {
473  bu_log("nmg_fix_overlapping_loops: Cannot handle loops of same orientation\n");
474  nmg_pr_fu_briefly(fu, (char *)NULL);
475  continue;
476  }
477 
478  /* At this point we have an OT_SAME and an OT_OPPOSITE
479  * loopuses for simplicity, force lu1 to be the OT_SAME
480  * loopuse */
481  if (lu1->orientation == OT_OPPOSITE && lu2->orientation == OT_SAME) {
482  struct loopuse *lu_tmp;
483 
484  lu_tmp = lu1;
485  lu1 = lu2;
486  lu2 = lu_tmp;
487  } else if (lu2->orientation != OT_OPPOSITE || lu1->orientation != OT_SAME) {
488  bu_log("nmg_fix_overlapping_loops: bad loop orientations %s and %s\n",
489  nmg_orientation(lu1->orientation),
490  nmg_orientation(lu2->orientation));
491  continue;
492  }
493 
494  /* lu1 is OT_SAME and lu2 is OT_OPPOSITE, check for overlap */
495 
496  /* count how many vertices in lu2 are inside lu1 and outside lu1 */
497  for (BU_LIST_FOR(eu, edgeuse, &lu2->down_hd)) {
498  struct vertexuse *vu;
499 
500  NMG_CK_EDGEUSE(eu);
501 
502  vu = eu->vu_p;
503 
504  /* ignore vertices that are shared between the loops */
505  if (!nmg_find_vertex_in_lu(vu->v_p, lu1)) {
506  int nmg_class;
507 
508  nmg_class = nmg_classify_pt_loop(vu->v_p->vg_p->coord, lu1, tol);
509  if (nmg_class == NMG_CLASS_AoutB)
510  outside++;
511  else if (nmg_class == NMG_CLASS_AinB)
512  inside++;
513  }
514  }
515 
516  /* if we don't have vertices both inside and outside lu1,
517  * then there is no overlap
518  */
519  if (!inside || !outside) /* no overlap */
520  continue;
521 
522  /* the loops overlap, now fix it */
523 
524  /* first, split the edges where the two loops cross each other */
525  for (BU_LIST_FOR(eu1, edgeuse, &lu1->down_hd)) {
526  vect_t v1;
527  struct edgeuse *eu2;
528 
529  VSUB2(v1, eu1->eumate_p->vu_p->v_p->vg_p->coord, eu1->vu_p->v_p->vg_p->coord);
530  for (BU_LIST_FOR(eu2, edgeuse, &lu2->down_hd)) {
531  vect_t v2;
532  fastf_t dist[2];
533  struct vertex *v=(struct vertex *)NULL;
534 
535  VSUB2(v2, eu2->eumate_p->vu_p->v_p->vg_p->coord ,
536  eu2->vu_p->v_p->vg_p->coord);
537 
538  if (bn_isect_lseg3_lseg3(dist, eu1->vu_p->v_p->vg_p->coord, v1 ,
539  eu2->vu_p->v_p->vg_p->coord, v2, tol) >= 0) {
540  struct edgeuse *new_eu;
541 
542  if (dist[0]>0.0 && dist[0]<1.0 &&
543  dist[1]>=0.0 && dist[1]<=1.0) {
544  point_t pt;
545 
546  if (ZERO(dist[1]))
547  v = eu2->vu_p->v_p;
548  else if (EQUAL(dist[1], 1.0)) /* i.e., == 1.0 */
549  v = eu2->eumate_p->vu_p->v_p;
550  else {
551  VJOIN1(pt, eu1->vu_p->v_p->vg_p->coord, dist[0], v1);
552  new_eu = nmg_esplit(v, eu1, 0);
553  v = new_eu->vu_p->v_p;
554  if (!v->vg_p)
555  nmg_vertex_gv(v, pt);
556  }
557 
558  VSUB2(v1, eu1->eumate_p->vu_p->v_p->vg_p->coord ,
559  eu1->vu_p->v_p->vg_p->coord);
560  }
561  if (dist[1]>0.0 && dist[1]<1.0 && dist[0]>=0.0 && dist[0]<=1.0) {
562  point_t pt;
563 
564  if (ZERO(dist[0]))
565  v = eu1->vu_p->v_p;
566  else if (EQUAL(dist[0], 1.0)) /* i.e., == 1.0 */
567  v = eu2->eumate_p->vu_p->v_p;
568  else {
569  VJOIN1(pt, eu2->vu_p->v_p->vg_p->coord, dist[1], v2);
570  new_eu = nmg_esplit(v, eu2, 0);
571  v = new_eu->vu_p->v_p;
572  if (!v->vg_p)
573  nmg_vertex_gv(v, pt);
574  }
575 
576  VSUB2(v2, eu2->eumate_p->vu_p->v_p->vg_p->coord ,
577  eu2->vu_p->v_p->vg_p->coord);
578  }
579  }
580  }
581  }
582 
583  /* find a vertex that lu1 and lu2 share, where eu1 is outside
584  * lu2 this will be a starting edgeuse for a new loopuse
585  */
586  start_eu = (struct edgeuse *)NULL;
587  for (BU_LIST_FOR(eu1, edgeuse, &lu1->down_hd)) {
588  struct vertex *v1, *v2;
589  point_t mid_pt;
590 
591  /* must be a shared vertex */
592  if (!nmg_find_vertex_in_lu(eu1->vu_p->v_p, lu2))
593  continue;
594 
595  v1 = eu1->vu_p->v_p;
596  NMG_CK_VERTEX(v1);
597  v2 = eu1->eumate_p->vu_p->v_p;
598  NMG_CK_VERTEX(v2);
599 
600  /* use midpoint to determine if edgeuse is in or out of
601  * lu2
602  */
603  VBLEND2(mid_pt, 0.5, v1->vg_p->coord, 0.5, v2->vg_p->coord);
604 
605  if (nmg_classify_pt_loop(mid_pt, lu2, tol) == NMG_CLASS_AoutB) {
606  start_eu = eu1;
607  break;
608  }
609  }
610 
611  if (!start_eu) {
612  bu_log("nmg_fix_overlapping_loops: cannot find start point for new loops\n");
613  bu_log("lu1=%p, lu2=%p\n", (void *)lu1, (void *)lu2);
614  nmg_pr_fu_briefly(fu, (char *)NULL);
615  continue;
616  }
617 
618  bu_ptbl_reset(&loops);
619 
620  /* start new loop. this routine will recurse, building as
621  * many tables as needed
622  */
623  nmg_start_new_loop(start_eu, lu1, lu2, &loops);
624 
625  /* use loops table to create the new loops */
626  for (i=0; i<BU_PTBL_END(&loops); i++) {
627  struct loopuse *new_lu;
628  struct loopuse *new_lu_mate;
629  struct bu_ptbl *loop_tab;
630  int eu_no;
631 
632  /* each table represents a new loopuse to be
633  * constructed
634  */
635  loop_tab = (struct bu_ptbl *)BU_PTBL_GET(&loops, i);
636 
637  /* if there are some entries in this table, make a new
638  * loopuse
639  */
640  if (BU_PTBL_END(loop_tab)) {
641  /* create new loop */
642  new_lu = nmg_mlv(&fu->l.magic, (struct vertex *)NULL, OT_SAME);
643  new_lu_mate = new_lu->lumate_p;
644 
645  /* get rid of vertex just created */
646  nmg_kvu(BU_LIST_FIRST(vertexuse, &new_lu->down_hd));
647  nmg_kvu(BU_LIST_FIRST(vertexuse, &new_lu_mate->down_hd));
648 
649  /* move edgeuses to new loops */
650  for (eu_no=0; eu_no<BU_PTBL_END(loop_tab); eu_no++) {
651  struct edgeuse *mv_eu;
652 
653  /* get edgeuse to be moved */
654  mv_eu = (struct edgeuse *)BU_PTBL_GET(loop_tab, eu_no);
655  NMG_CK_EDGEUSE(mv_eu);
656 
657  /* move it to new loopuse */
658  BU_LIST_DEQUEUE(&mv_eu->l);
659  BU_LIST_INSERT(&new_lu->down_hd, &mv_eu->l);
660  mv_eu->up.lu_p = new_lu;
661 
662  /* move edgeuse mate to loopuse mate */
663  BU_LIST_DEQUEUE(&mv_eu->eumate_p->l);
664  BU_LIST_APPEND(&new_lu_mate->down_hd, &mv_eu->eumate_p->l);
665  mv_eu->eumate_p->up.lu_p = new_lu_mate;
666  }
667 
668  bu_ptbl_free(loop_tab);
669  bu_free((char *)loop_tab, "nmg_fix_overlapping_loops: loop_tab");
670  }
671  }
672 
673  /* kill empty loopuses left in faceuse */
674  lu1 = BU_LIST_FIRST(loopuse, &fu->lu_hd);
675  while (BU_LIST_NOT_HEAD(lu1, &fu->lu_hd)) {
676  struct loopuse *next_lu;
677 
678  next_lu = BU_LIST_PNEXT(loopuse, &lu1->l);
679 
680  if (BU_LIST_IS_EMPTY(&lu1->down_hd)) {
681  if (nmg_klu(lu1))
682  bu_bomb("nmg_fix_overlapping_loops: Emptied faceuse!!\n");
683  }
684  lu1 = next_lu;
685  }
686  }
687  bu_ptbl_free(&loops);
688 
689  if (RTG.NMG_debug & DEBUG_BASIC)
690  bu_log("nmg_fix_overlapping_loops: done\n");
691 }
692 
693 
694 /**
695  * Extrusion may create crossed loops within a face. This routine
696  * intersects each edge within a loop with every other edge in the
697  * loop
698  */
699 void
700 nmg_break_crossed_loops(struct shell *is, const struct bn_tol *tol)
701 {
702  struct faceuse *fu;
703 
704  NMG_CK_SHELL(is);
705  BN_CK_TOL(tol);
706 
707  for (BU_LIST_FOR(fu, faceuse, &is->fu_hd)) {
708  struct loopuse *lu;
709 
710  NMG_CK_FACEUSE(fu);
711 
712  if (fu->orientation != OT_SAME)
713  continue;
714 
715  for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
716  struct edgeuse *eu1, *eu2;
717  vect_t v1, v2;
718 
719  NMG_CK_LOOPUSE(lu);
720 
721  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC)
722  continue;
723 
724  for (BU_LIST_FOR(eu1, edgeuse, &lu->down_hd)) {
725  VSUB2(v1, eu1->eumate_p->vu_p->v_p->vg_p->coord ,
726  eu1->vu_p->v_p->vg_p->coord);
727 
728  eu2 = BU_LIST_PNEXT(edgeuse, eu1);
729  while (BU_LIST_NOT_HEAD(eu2, &lu->down_hd)) {
730  fastf_t dist[2];
731 
732  VSUB2(v2, eu2->eumate_p->vu_p->v_p->vg_p->coord ,
733  eu2->vu_p->v_p->vg_p->coord);
734 
735  /* The logic below needs to be changed, the meaning of
736  * dist[1] is different depending on if the result is '0'
737  * or '1'. Presently this function is not called.
738  */
739  if (bn_isect_lseg3_lseg3(dist, eu1->vu_p->v_p->vg_p->coord, v1 ,
740  eu2->vu_p->v_p->vg_p->coord, v2, tol) >= 0) {
741  point_t pt = VINIT_ZERO;
742  struct edgeuse *new_eu;
743  struct vertex *v=(struct vertex *)NULL;
744 
745  if (dist[0]>0.0 && dist[0]<1.0 &&
746  dist[1]>=0.0 && dist[1]<=1.0) {
747  if (ZERO(dist[1])) {
748  v = eu2->vu_p->v_p;
749  VMOVE(pt, v->vg_p->coord);
750  } else if (EQUAL(dist[1], 1.0)) { /* i.e., == 1.0 */
751  v = eu2->eumate_p->vu_p->v_p;
752  VMOVE(pt, v->vg_p->coord);
753  } else {
754  VJOIN1(pt, eu1->vu_p->v_p->vg_p->coord ,
755  dist[0], v1);
756  v = nmg_find_pt_in_shell(is, pt, tol);
757  }
758 
759  new_eu = nmg_esplit(v, eu1, 0);
760  v = new_eu->vu_p->v_p;
761  if (!v->vg_p)
762  nmg_vertex_gv(v, pt);
763 
764  VSUB2(v1, eu1->eumate_p->vu_p->v_p->vg_p->coord ,
765  eu1->vu_p->v_p->vg_p->coord);
766  }
767  if (dist[1] > 0.0 && dist[1] < 1.0 &&
768  dist[0]>=0.0 && dist[0]<=1.0)
769  {
770  if (ZERO(dist[0])) {
771  v = eu1->vu_p->v_p;
772  VMOVE(pt, v->vg_p->coord);
773  } else if (EQUAL(dist[0], 1.0)) { /* i.e., == 1.0 */
774  v = eu1->eumate_p->vu_p->v_p;
775  VMOVE(pt, v->vg_p->coord);
776  } else {
777  VJOIN1(pt, eu2->vu_p->v_p->vg_p->coord, dist[1], v2);
778  v = nmg_find_pt_in_shell(is, pt, tol);
779  }
780 
781  new_eu = nmg_esplit(v, eu2, 0);
782  v = new_eu->vu_p->v_p;
783  if (!v->vg_p)
784  nmg_vertex_gv(v, pt);
785  }
786  }
787  eu2 = BU_LIST_PNEXT(edgeuse, eu2);
788  }
789  }
790  }
791  }
792 }
793 
794 
795 /**
796  * Clean up after nmg_extrude_shell. intersects each face with every
797  * other face in the shell and makes new face boundaries at the
798  * intersections. decomposes the result into separate shells. where
799  * faces have intersected, new shells will be created. These shells
800  * are detected and killed
801  */
802 struct shell *
803 nmg_extrude_cleanup(struct shell *in_shell, const int is_void, const struct bn_tol *tol)
804 {
805  struct model *m;
806  struct nmgregion *new_r;
807  struct faceuse *fu;
808  struct loopuse *lu;
809  struct vertexuse *vu;
810  struct nmgregion *old_r;
811  struct shell *s_tmp;
812  const int UNDETERMINED = -1;
813 
814  NMG_CK_SHELL(in_shell);
815  BN_CK_TOL(tol);
816 
817  if (RTG.NMG_debug & DEBUG_BASIC)
818  bu_log("nmg_extrude_cleanup(in_shell=%p)\n", (void *)in_shell);
819 
820  m = nmg_find_model(&in_shell->l.magic);
821 
822  /* intersect each face in the shell with every other face in the
823  * same shell
824  */
825  nmg_isect_shell_self(in_shell, tol);
826 
827  /* Extrusion may create loops that overlap */
828  nmg_fix_overlapping_loops(in_shell, tol);
829 
830  /* look for self-touching loops */
831  for (BU_LIST_FOR(fu, faceuse, &in_shell->fu_hd)) {
832  if (fu->orientation != OT_SAME)
833  continue;
834 
835  lu = BU_LIST_LAST(loopuse, &fu->lu_hd);
836  while (BU_LIST_NOT_HEAD(lu, &fu->lu_hd)) {
837  struct loopuse *new_lu;
838  int orientation;
839 
840  /* check this loopuse */
841  while ((vu=(struct vertexuse *)nmg_loop_touches_self(lu)) != (struct vertexuse *)NULL) {
842  /* Split this touching loop, but give both resulting
843  * loops the same orientation as the original. This
844  * will result in the part of the loop that needs to
845  * be discarded having an incorrect orientation with
846  * respect to the face. This incorrect orientation
847  * will be discovered later by "nmg_bad_face_normals"
848  * and will result in the undesirable portion's demise
849  */
850 
851  orientation = lu->orientation;
852  new_lu = nmg_split_lu_at_vu(lu, vu);
853  new_lu->orientation = orientation;
854  lu->orientation = orientation;
855  new_lu->lumate_p->orientation = orientation;
856  lu->lumate_p->orientation = orientation;
857  }
858 
859  lu = BU_LIST_PLAST(loopuse, &lu->l);
860  }
861  }
862 
863  nmg_rebound(m, tol);
864 
865  /* We now build a temporary region in the model where we can move in_shell
866  * in order to isolate it from other shells.
867  */
868 
869  /* remember the nmgregion where "in_shell" came from */
870  old_r = in_shell->r_p;
871 
872  /* NMG rules require a region to always have a shell, so in order
873  * to get our in_shell in a new region by itself we must:
874  * 1) Create a new region containing a temporary shell.
875  * 2) Add our in_shell to the region.
876  * 3) Remove the temporary shell.
877  */
878  new_r = nmg_mrsv(m);
879  s_tmp = BU_LIST_FIRST(shell, &new_r->s_hd);
880 
881  (void)nmg_mv_shell_to_region(in_shell, new_r);
882 
883  if (nmg_ks(s_tmp))
884  bu_bomb("nmg_extrude_shell: Nothing got moved to new region\n");
885 
886  /* now decompose our shell, count number of inside shells */
887  if (nmg_decompose_shell(in_shell, tol) < 2) {
888 
889  /* We still have only one shell. If there's a problem with it, then
890  * kill it. Otherwise, move it back to the region it came from.
891  */
892  if (nmg_bad_face_normals(in_shell, tol)) {
893  /* shell contains bad face normals */
894  (void)nmg_ks(in_shell);
895  in_shell = (struct shell *)NULL;
896  } else if (is_void != UNDETERMINED && is_void != nmg_shell_is_void(in_shell)) {
897  /* shell was known to be a void shell and became an exterior shell
898  * OR
899  * shell was known to be an exterior shell and became a void shell
900  */
901  (void)nmg_ks(in_shell);
902  in_shell = (struct shell *)NULL;
903  } else {
904  (void)nmg_mv_shell_to_region(in_shell, old_r);
905  }
906 
907  /* kill temporary region */
908  nmg_kr(new_r);
909  new_r = NULL;
910  } else {
911  /* look at each shell in "new_r" */
912  s_tmp = BU_LIST_FIRST(shell, &new_r->s_hd);
913  while (BU_LIST_NOT_HEAD(s_tmp, &new_r->s_hd)) {
914  struct shell *next_s;
915  int kill_it=0;
916 
917  next_s = BU_LIST_PNEXT(shell, &s_tmp->l);
918 
919  if (nmg_bad_face_normals(s_tmp, tol))
920  kill_it = 1;
921 
922  if (!kill_it) {
923  if (is_void != UNDETERMINED && is_void != nmg_shell_is_void(s_tmp))
924  kill_it = 1;
925  }
926 
927  if (kill_it) {
928  /* Bad shell, kill it */
929  if (nmg_ks(s_tmp)) {
930 
931  /* All shells have been removed (all were bad).
932  * Kill the now-empty temporary region.
933  */
934  nmg_kr(new_r);
935  new_r = (struct nmgregion *)NULL;
936  in_shell = (struct shell *)NULL;
937  break;
938  }
939  }
940  s_tmp = next_s;
941  }
942  }
943 
944  if (new_r) {
945  /* temp region contains multiple valid shells that we must merge */
946 
947  in_shell = BU_LIST_FIRST(shell, &new_r->s_hd);
948  for (BU_LIST_FOR(s_tmp, shell, &new_r->s_hd)) {
949  if (s_tmp == in_shell) {
950  continue;
951  }
952  nmg_js(in_shell, s_tmp, tol);
953  }
954 
955  /* move resulting shell back to the original region */
956  (void)nmg_mv_shell_to_region(in_shell, old_r);
957 
958  /* kill the temporary region */
959  if (BU_LIST_NON_EMPTY(&new_r->s_hd))
960  bu_log("nmg_extrude_cleanup: temporary nmgregion not empty!!\n");
961 
962  (void)nmg_kr(new_r);
963  }
964  return in_shell;
965 }
966 
967 
968 /**
969  * Hollows out a shell producing a wall thickness of thickness "thick"
970  * by creating a new "inner" shell and combining the two shells.
971  *
972  * If the original shell is closed, the new shell is simply merged
973  * with the original shell. If the original shell is open, then faces
974  * are constructed along the free edges of the two shells to make a
975  * closed shell.
976  *
977  * if approximate is non-zero, new vertex geometry at vertices where
978  * more than three faces intersect may be approximated by a point of
979  * minimum distance from the intersecting faces.
980  */
981 void
982 nmg_hollow_shell(struct shell *s, const fastf_t thick, const int approximate, const struct bn_tol *tol)
983 {
984  struct nmgregion *new_r, *old_r;
985  struct vertexuse *vu;
986  struct edgeuse *eu;
987  struct loopuse *lu;
988  struct faceuse *fu;
989  struct face_g_plane *fg_p;
990  struct model *m;
991  struct shell *is; /* inside shell */
992  struct shell *s_tmp;
993  struct bu_ptbl shells;
994  long *flags;
995  long **copy_tbl;
996  int shell_no;
997  int is_void;
998  int s_tmp_is_closed;
999 
1000  if (RTG.NMG_debug & DEBUG_BASIC)
1001  bu_log("nmg_extrude_shell(s=%p, thick=%f)\n", (void *)s, thick);
1002 
1003  NMG_CK_SHELL(s);
1004  BN_CK_TOL(tol);
1005 
1006  if (thick < 0.0) {
1007  bu_log("nmg_extrude_shell: thickness less than zero not allowed");
1008  return;
1009  }
1010 
1011  if (thick < tol->dist) {
1012  bu_log("nmg_extrude_shell: thickness less than tolerance not allowed");
1013  return;
1014  }
1015 
1016  m = nmg_find_model((uint32_t *)s);
1017 
1018  /* remember region where this shell came from */
1019  old_r = s->r_p;
1020  NMG_CK_REGION(old_r);
1021 
1022  /* move this shell to another region */
1023  new_r = nmg_mrsv(m);
1024  s_tmp = BU_LIST_FIRST(shell, &new_r->s_hd);
1025  (void)nmg_mv_shell_to_region(s, new_r);
1026 
1027  /* decompose this shell */
1028  (void)nmg_decompose_shell(s, tol);
1029 
1030  /* kill the extra shell created by nmg_mrsv above */
1031  (void)nmg_ks(s_tmp);
1032 
1033  /* recompute the bounding boxes */
1034  nmg_region_a(new_r, tol);
1035 
1036  /* make a list of all the shells in the new region */
1037  bu_ptbl_init(&shells, 64, " &shells ");
1038  for (BU_LIST_FOR(s_tmp, shell, &new_r->s_hd))
1039  bu_ptbl_ins(&shells, (long *)s_tmp);
1040 
1041  /* extrude a copy of each shell, one at a time */
1042  for (shell_no=0; shell_no<BU_PTBL_END(&shells); shell_no ++) {
1043  s_tmp = (struct shell *)BU_PTBL_GET(&shells, shell_no);
1044 
1045  /* first make a copy of this shell */
1046  is = nmg_dup_shell(s_tmp, &copy_tbl, tol);
1047 
1048  /* make a translation table for this model */
1049  flags = (long *)bu_calloc(m->maxindex, sizeof(long), "nmg_extrude_shell flags");
1050 
1051  /* now adjust all the planes, first move them inward by distance "thick" */
1052  for (BU_LIST_FOR(fu, faceuse, &is->fu_hd)) {
1053  NMG_CK_FACEUSE(fu);
1054  NMG_CK_FACE(fu->f_p);
1055  fg_p = fu->f_p->g.plane_p;
1056  NMG_CK_FACE_G_PLANE(fg_p);
1057 
1058  /* move the faces by the distance "thick" */
1059  if (NMG_INDEX_TEST_AND_SET(flags, fg_p)) {
1060  if (fu->f_p->flip)
1061  fg_p->N[W] += thick;
1062  else
1063  fg_p->N[W] -= thick;
1064  }
1065  }
1066 
1067  /* Reverse the normals of all the faces */
1068  nmg_invert_shell(is);
1069 
1070  is_void = nmg_shell_is_void(is);
1071 
1072  /* now start adjusting the vertices. Use the original shell
1073  * so that we can pass the original vertex to nmg_inside_vert
1074  */
1075  for (BU_LIST_FOR(fu, faceuse, &s_tmp->fu_hd)) {
1076  if (fu->orientation != OT_SAME)
1077  continue;
1078 
1079  for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
1080  NMG_CK_LOOPUSE(lu);
1081  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC) {
1082  /* the vertex in a loop of one vertex must show up
1083  * in an edgeuse somewhere, so don't mess with it
1084  * here
1085  */
1086  continue;
1087  } else {
1088  for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
1089  struct vertex *new_v;
1090 
1091  NMG_CK_EDGEUSE(eu);
1092  vu = eu->vu_p;
1093  NMG_CK_VERTEXUSE(vu);
1094  new_v = NMG_INDEX_GETP(vertex, copy_tbl, vu->v_p);
1095  NMG_CK_VERTEX(new_v);
1096  if (NMG_INDEX_TEST_AND_SET(flags, new_v)) {
1097  /* move this vertex */
1098  if (nmg_in_vert(new_v, approximate, tol))
1099  bu_bomb("Failed to get a new point from nmg_inside_vert\n");
1100  }
1101  }
1102  }
1103  }
1104  }
1105 
1106  /* recompute the bounding boxes */
1107  nmg_region_a(is->r_p, tol);
1108 
1109  nmg_vmodel(m);
1110 
1111  s_tmp_is_closed = !nmg_check_closed_shell(s_tmp, tol);
1112  if (s_tmp_is_closed)
1113  is = nmg_extrude_cleanup(is, is_void, tol);
1114 
1115  /* Inside shell is done */
1116  if (is) {
1117  if (s_tmp_is_closed) {
1118  if (nmg_check_closed_shell(is, tol)) {
1119  bu_log("nmg_extrude_shell: inside shell is not closed, calling nmg_close_shell\n");
1120  nmg_close_shell(is, tol);
1121  }
1122 
1123  nmg_shell_coplanar_face_merge(is, tol, 0);
1124  nmg_simplify_shell(is);
1125 
1126  /* now merge the inside and outside shells */
1127  nmg_js(s_tmp, is, tol);
1128  } else {
1129  if (!nmg_check_closed_shell(is, tol)) {
1130  bu_log("nmg_extrude_shell: inside shell is closed, outer isn't!!\n");
1131  nmg_shell_coplanar_face_merge(is, tol, 0);
1132  nmg_simplify_shell(is);
1133  nmg_js(s_tmp, is, tol);
1134  } else {
1135  /* connect the boundaries of the two open shells */
1136  nmg_open_shells_connect(s_tmp, is ,
1137  (const long **)copy_tbl, tol);
1138  }
1139  }
1140  }
1141 
1142  /* recompute the bounding boxes */
1143  nmg_region_a(s_tmp->r_p, tol);
1144 
1145  /* free memory */
1146  bu_free((char *)flags, "nmg_extrude_shell: flags");
1147  bu_free((char *)copy_tbl, "nmg_extrude_shell: copy_tbl");
1148  }
1149 
1150  /* put it all back together */
1151  for (shell_no=0; shell_no<BU_PTBL_END(&shells); shell_no++) {
1152  struct shell *s2;
1153 
1154  s2 = (struct shell *)BU_PTBL_GET(&shells, shell_no);
1155  if (s2 != s)
1156  nmg_js(s, s2, tol);
1157  }
1158 
1159  bu_ptbl_free(&shells);
1160 
1161  (void)nmg_mv_shell_to_region(s, old_r);
1162  nmg_kr(new_r);
1163 }
1164 
1165 
1166 /**
1167  * Extrudes a shell (s) by a distance (dist) in the direction of the
1168  * face normals if normal_ward, or the opposite direction if
1169  * !normal_ward. The shell (s) is modified by adjusting the plane
1170  * equations for each face and calculating new vertex geometry. if
1171  * approximate is non-zero, new vertex geometry, for vertices where
1172  * more than three faces intersect, will be approximated by a point
1173  * with minimum distance from the intersecting faces. if approximate
1174  * is zero, additional faces and/or edges may be added to the shell.
1175  *
1176  * returns:
1177  * a pointer to the modified shell on success
1178  * NULL on failure
1179  */
1180 struct shell *
1181 nmg_extrude_shell(struct shell *s, const fastf_t dist, const int normal_ward, const int approximate, const struct bn_tol *tol)
1182 {
1183  fastf_t thick;
1184  int along_normal;
1185  struct model *m;
1186  struct nmgregion *new_r, *old_r;
1187  struct shell *s_tmp, *s2;
1188  struct bu_ptbl shells;
1189  struct bu_ptbl verts;
1190  int shell_no;
1191  int failed=0;
1192 
1193  NMG_CK_SHELL(s);
1194  BN_CK_TOL(tol);
1195 
1196  if (NEAR_ZERO(dist, tol->dist)) {
1197  bu_log("nmg_extrude_shell: Cannot extrude a distance less than tolerance distance\n");
1198  return s;
1199  }
1200 
1201  along_normal = normal_ward;
1202  if (dist < 0.0) {
1203  thick = (-dist);
1204  along_normal = (!normal_ward);
1205  } else
1206  thick = dist;
1207 
1208  m = nmg_find_model(&s->l.magic);
1209  NMG_CK_MODEL(m);
1210 
1211  old_r = s->r_p;
1212  NMG_CK_REGION(old_r);
1213 
1214  /* decompose this shell and extrude each piece separately */
1215  new_r = nmg_mrsv(m);
1216  s_tmp = BU_LIST_FIRST(shell, &new_r->s_hd);
1217  (void)nmg_mv_shell_to_region(s, new_r);
1218  (void)nmg_decompose_shell(s, tol);
1219 
1220  /* kill the not-needed shell created by nmg_mrsv() */
1221  (void)nmg_ks(s_tmp);
1222 
1223  /* recompute the bounding boxes */
1224  nmg_region_a(new_r, tol);
1225 
1226  /* make a list of all the shells to be extruded */
1227  bu_ptbl_init(&shells, 64, " &shells ");
1228  for (BU_LIST_FOR(s_tmp, shell, &new_r->s_hd))
1229  bu_ptbl_ins(&shells, (long *)s_tmp);
1230 
1231  bu_ptbl_init(&verts, 64, " &verts ");
1232 
1233  /* extrude each shell */
1234  for (shell_no=0; shell_no < BU_PTBL_END(&shells); shell_no++) {
1235  int vert_no;
1236  int is_void;
1237  long *flags;
1238  struct faceuse *fu;
1239 
1240  s_tmp = (struct shell *)BU_PTBL_GET(&shells, shell_no);
1241  NMG_CK_SHELL(s_tmp);
1242 
1243  is_void = nmg_shell_is_void(s_tmp);
1244 
1245  /* make a translation table for this model */
1246  flags = (long *)bu_calloc(m->maxindex, sizeof(long), "nmg_extrude_shell flags");
1247 
1248  /* now adjust all the planes, first move them by distance "thick" */
1249  for (BU_LIST_FOR(fu, faceuse, &s_tmp->fu_hd)) {
1250  struct face_g_plane *fg_p;
1251 
1252  NMG_CK_FACEUSE(fu);
1253  NMG_CK_FACE(fu->f_p);
1254  fg_p = fu->f_p->g.plane_p;
1255  NMG_CK_FACE_G_PLANE(fg_p);
1256 
1257  /* move the faces by the distance "thick" */
1258  if (NMG_INDEX_TEST_AND_SET(flags, fg_p)) {
1259  if (along_normal ^ fu->f_p->flip)
1260  fg_p->N[W] += thick;
1261  else
1262  fg_p->N[W] -= thick;
1263  }
1264  }
1265 
1266  bu_free((char *)flags, "nmg_extrude_shell flags");
1267 
1268  /* get table of vertices in this shell */
1269  nmg_vertex_tabulate(&verts, &s_tmp->l.magic);
1270 
1271  /* now move all the vertices */
1272  for (vert_no = 0; vert_no < BU_PTBL_END(&verts); vert_no++) {
1273  struct vertex *new_v;
1274 
1275  new_v = (struct vertex *)BU_PTBL_GET(&verts, vert_no);
1276  NMG_CK_VERTEX(new_v);
1277 
1278  if (nmg_in_vert(new_v, approximate, tol)) {
1279  bu_log("nmg_extrude_shell: Failed to calculate new vertex at v=%p was (%f %f %f)\n",
1280  (void *)new_v, V3ARGS(new_v->vg_p->coord));
1281  failed = 1;
1282  goto out;
1283  }
1284  }
1285 
1286  bu_ptbl_free(&verts);
1287 
1288  if (approximate) {
1289  /* need to recalculate plane eqns */
1290  for (BU_LIST_FOR(fu, faceuse, &s_tmp->fu_hd)) {
1291  struct loopuse *lu;
1292  int got_plane=0;
1293 
1294  if (fu->orientation != OT_SAME)
1295  continue;
1296 
1297  for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
1298  fastf_t area;
1299  plane_t pl;
1300 
1301  if (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC)
1302  continue;
1303 
1304  if (lu->orientation != OT_SAME)
1305  continue;
1306 
1307  area = nmg_loop_plane_area(lu, pl);
1308 
1309  if (area > 0.0) {
1310  nmg_face_g(fu, pl);
1311  got_plane = 1;
1312  break;
1313  }
1314  }
1315  if (!got_plane) {
1316  bu_log("nmg_extrude_shell: Cannot recalculate plane for face:\n");
1317  nmg_pr_fu_briefly(fu, (char *)NULL);
1318  failed = 1;
1319  goto out;
1320  }
1321  }
1322  }
1323 
1324  /* recompute the bounding boxes */
1325  nmg_region_a(s_tmp->r_p, tol);
1326 
1327  (void)nmg_extrude_cleanup(s_tmp, is_void, tol);
1328  }
1329 
1330 out:
1331  bu_ptbl_free(&shells);
1332 
1333  /* put it all back together */
1334  if (BU_LIST_NON_EMPTY(&new_r->s_hd)) {
1335  s_tmp = BU_LIST_FIRST(shell, &new_r->s_hd);
1336  s2 = BU_LIST_PNEXT(shell, &s_tmp->l);
1337  while (BU_LIST_NOT_HEAD(s2, &new_r->s_hd)) {
1338  struct shell *next_s;
1339 
1340  next_s = BU_LIST_PNEXT(shell, &s2->l);
1341  nmg_js(s_tmp, s2, tol);
1342 
1343  s2 = next_s;
1344  }
1345  } else
1346  s_tmp = (struct shell *)NULL;
1347 
1348  if (s_tmp)
1349  (void)nmg_mv_shell_to_region(s_tmp, old_r);
1350 
1351  nmg_kr(new_r);
1352 
1353  if (failed)
1354  return (struct shell *)NULL;
1355  else
1356  return s_tmp;
1357 }
1358 
1359 
1360 /*
1361  * Local Variables:
1362  * mode: C
1363  * tab-width: 8
1364  * indent-tabs-mode: t
1365  * c-file-style: "stroustrup"
1366  * End:
1367  * ex: shiftwidth=4 tabstop=8
1368  */
#define BU_LIST_PNEXT_CIRC(structure, p)
Definition: list.h:442
#define BU_LIST_FOR(p, structure, hp)
Definition: list.h:365
int nmg_classify_pt_loop(const point_t pt, const struct loopuse *lu, const struct bn_tol *tol)
Definition: nmg_class.c:2050
void nmg_isect_shell_self(struct shell *s, const struct bn_tol *tol)
Definition: nmg_misc.c:936
#define NMG_EDGEUSE_MAGIC
Definition: magic.h:120
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
#define BU_LIST_INSERT(old, new)
Definition: list.h:183
struct faceuse * nmg_cface(struct shell *s, struct vertex **verts, int n)
Definition: nmg_mod.c:1130
void nmg_close_shell(struct shell *s, const struct bn_tol *tol)
Definition: nmg_misc.c:2256
#define BU_LIST_LAST(structure, hp)
Definition: list.h:306
void nmg_reverse_face(register struct faceuse *fu)
Definition: nmg_mod.c:1511
#define BU_LIST_FOR2(p1, p2, structure, hp1, hp2)
Definition: list.h:392
void nmg_edge_g_tabulate(struct bu_ptbl *tab, const uint32_t *magic_p)
Definition: nmg_info.c:2197
double dist
>= 0
Definition: tol.h:73
if lu s
Definition: nmg_mod.c:3860
void bu_ptbl_init(struct bu_ptbl *b, size_t len, const char *str)
Definition: ptbl.c:32
void nmg_fix_overlapping_loops(struct shell *s, const struct bn_tol *tol)
Definition: nmg_extrude.c:414
lu
Definition: nmg_mod.c:3855
int nmg_kr(struct nmgregion *r)
Definition: nmg_mk.c:1595
#define BU_LIST_IS_EMPTY(hp)
Definition: list.h:295
void nmg_vertex_gv(struct vertex *v, const fastf_t *pt)
Definition: nmg_mk.c:1668
void nmg_face_g(struct faceuse *fu, const fastf_t *p)
Definition: nmg_mk.c:2235
void nmg_rebound(struct model *m, const struct bn_tol *tol)
Definition: nmg_misc.c:2072
Header file for the BRL-CAD common definitions.
#define MIKE_TOL
struct faceuse * nmg_dup_face(struct faceuse *fu, struct shell *s)
Definition: nmg_mod.c:1827
#define BU_LIST_APPEND(old, new)
Definition: list.h:197
int bu_ptbl_ins(struct bu_ptbl *b, long *p)
fastf_t nmg_loop_plane_area(const struct loopuse *lu, fastf_t *pl)
Definition: nmg_misc.c:1320
void nmg_break_crossed_loops(struct shell *is, const struct bn_tol *tol)
Definition: nmg_extrude.c:700
#define BU_LIST_NON_EMPTY(hp)
Definition: list.h:296
int nmg_kvu(struct vertexuse *vu)
Definition: nmg_mk.c:1095
void bu_ptbl_reset(struct bu_ptbl *b)
Definition: ptbl.c:49
int nmg_calc_face_g(struct faceuse *fu)
Definition: nmg_misc.c:1786
NMG_CK_LOOPUSE(lu)
void nmg_invert_shell(struct shell *s)
Definition: nmg_mod.c:890
BU_LIST_DEQUEUE & eu1
Definition: nmg_mod.c:3839
void * bu_malloc(size_t siz, const char *str)
Definition: malloc.c:314
Definition: ptbl.h:62
int nmg_ks(struct shell *s)
Definition: nmg_mk.c:1546
#define BU_LIST_IS_HEAD(p, hp)
Definition: list.h:322
#define BU_LIST_PLAST(structure, p)
Definition: list.h:424
int nmg_check_closed_shell(const struct shell *s, const struct bn_tol *tol)
Definition: nmg_misc.c:1142
struct loopuse * nmg_split_lu_at_vu(struct loopuse *lu, struct vertexuse *split_vu)
Definition: nmg_mod.c:2457
uint32_t NMG_debug
debug bits for NMG's see nmg.h
Definition: raytrace.h:1699
int nmg_extrude_face(struct faceuse *fu, const fastf_t *Vec, const struct bn_tol *tol)
Definition: nmg_extrude.c:179
void nmg_shell_coplanar_face_merge(struct shell *s, const struct bn_tol *tol, const int simplify)
Definition: nmg_mod.c:93
#define BU_ALLOC(_ptr, _type)
Definition: malloc.h:223
void * bu_calloc(size_t nelem, size_t elsize, const char *str)
Definition: malloc.c:321
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
const struct vertexuse * nmg_loop_touches_self(const struct loopuse *lu)
Definition: nmg_info.c:578
#define V3ARGS(a)
Definition: color.c:56
#define NEAR_ZERO(val, epsilon)
Definition: color.c:55
struct loopuse * nmg_mlv(uint32_t *magic, struct vertex *v, int orientation)
Definition: nmg_mk.c:571
struct shell * nmg_extrude_shell(struct shell *s, const fastf_t dist, const int normal_ward, const int approximate, const struct bn_tol *tol)
Definition: nmg_extrude.c:1181
#define BU_LIST_PNEXT(structure, p)
Definition: list.h:422
struct bu_list l
Definition: ptbl.h:63
int nmg_decompose_shell(struct shell *s, const struct bn_tol *tol)
Definition: nmg_misc.c:4082
goto out
Definition: nmg_mod.c:3846
Support for uniform tolerances.
Definition: tol.h:71
#define BN_CK_TOL(_p)
Definition: tol.h:82
int nmg_split_loops_into_faces(uint32_t *magic_p, const struct bn_tol *tol)
Definition: nmg_misc.c:4035
#define BU_LIST_FIRST_MAGIC(hp)
Definition: list.h:416
#define NMG_VERTEXUSE_MAGIC
Definition: magic.h:145
uint32_t magic
Magic # for mem id/check.
Definition: list.h:119
int nmg_mv_shell_to_region(struct shell *s, struct nmgregion *r)
Definition: nmg_misc.c:4690
struct nmgregion * nmg_mrsv(struct model *m)
Definition: nmg_mk.c:306
void nmg_vmodel(const struct model *m)
Definition: nmg_ck.c:635
int nmg_shell_is_void(const struct shell *s)
Definition: nmg_misc.c:2975
int nmg_open_shells_connect(struct shell *dst, struct shell *src, const long int **copy_tbl, const struct bn_tol *tol)
Definition: nmg_misc.c:7836
void bu_ptbl_free(struct bu_ptbl *b)
Definition: ptbl.c:226
void nmg_translate_face(struct faceuse *fu, const fastf_t *Vec)
Definition: nmg_extrude.c:93
int nmg_simplify_shell(struct shell *s)
Definition: nmg_mod.c:209
#define NMG_EDGE_G_LSEG_MAGIC
Definition: magic.h:122
#define ZERO(val)
Definition: units.c:38
int nmg_klu(struct loopuse *lu1)
Definition: nmg_mk.c:1277
int bn_isect_lseg3_lseg3(fastf_t *dist, const point_t p, const vect_t pdir, const point_t q, const vect_t qdir, const struct bn_tol *tol)
Intersect two 3D line segments, defined by two points and two vectors. The vectors are unlikely to be...
struct shell * nmg_extrude_cleanup(struct shell *in_shell, const int is_void, const struct bn_tol *tol)
Definition: nmg_extrude.c:803
#define BU_PTBL_END(ptbl)
Definition: ptbl.h:106
#define NMG_EDGE_G_CNURB_MAGIC
Definition: magic.h:121
void nmg_js(register struct shell *s1, register struct shell *s2, const struct bn_tol *tol)
Definition: nmg_mod.c:740
struct shell * nmg_dup_shell(struct shell *s, long int ***trans_tbl, const struct bn_tol *tol)
Definition: nmg_misc.c:2673
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
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)
void nmg_gluefaces(struct faceuse **fulist, int n, const struct bn_tol *tol)
Definition: nmg_mod.c:1408
#define BU_LIST_DEQUEUE(cur)
Definition: list.h:209
void nmg_hollow_shell(struct shell *s, const fastf_t thick, const int approximate, const struct bn_tol *tol)
Definition: nmg_extrude.c:982
void bu_bomb(const char *str) _BU_ATTR_NORETURN
Definition: bomb.c:91
double fastf_t
Definition: defines.h:300
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
int nmg_bad_face_normals(const struct shell *s, const struct bn_tol *tol)
Definition: nmg_misc.c:6882
#define BU_LIST_FIRST(structure, hp)
Definition: list.h:312
eu1 orientation
Definition: nmg_mod.c:3916
void nmg_vertex_tabulate(struct bu_ptbl *tab, const uint32_t *magic_p)
Definition: nmg_info.c:1985
int nmg_in_vert(struct vertex *new_v, const int approximate, const struct bn_tol *tol)
Definition: nmg_misc.c:8086
struct rt_g RTG
Definition: globals.c:39
struct model * nmg_find_model(const uint32_t *magic_p_arg)
Definition: nmg_info.c:57
void nmg_region_a(struct nmgregion *r, const struct bn_tol *tol)
Definition: nmg_mk.c:2557