int main(int argc, char **argv) { int c; double percent; int i; bu_setprogname(argv[0]); bu_setlinebuf(stderr); RTG.debug = 0; tree_state = rt_initial_tree_state; /* struct copy */ tree_state.ts_tol = &tol; tree_state.ts_ttol = &ttol; tree_state.ts_m = &the_model; ttol.magic = RT_TESS_TOL_MAGIC; /* Defaults, updated by command line options. */ ttol.abs = 0.0; ttol.rel = 0.01; ttol.norm = 0.0; /* FIXME: These need to be improved */ tol.magic = BN_TOL_MAGIC; tol.dist = BN_TOL_DIST; tol.dist_sq = tol.dist * tol.dist; tol.perp = 1e-6; tol.para = 1 - tol.perp; the_model = nmg_mm(); BU_LIST_INIT(&RTG.rtg_vlfree); /* for vlist macros */ /* Get command line arguments. */ while ((c = bu_getopt(argc, argv, "a:n:o:r:vx:D:P:X:e:ih?")) != -1) { switch (c) { case 'a': /* Absolute tolerance. */ ttol.abs = atof(bu_optarg); ttol.rel = 0.0; break; case 'n': /* Surface normal tolerance. */ ttol.norm = atof(bu_optarg); ttol.rel = 0.0; break; case 'o': /* Output file name. */ output_file = bu_optarg; break; case 'r': /* Relative tolerance. */ ttol.rel = atof(bu_optarg); break; case 'v': verbose++; break; case 'P': ncpu = atoi(bu_optarg); break; case 'x': sscanf(bu_optarg, "%x", (unsigned int *)&RTG.debug); break; case 'D': tol.dist = atof(bu_optarg); tol.dist_sq = tol.dist * tol.dist; rt_pr_tol(&tol); break; case 'X': sscanf(bu_optarg, "%x", (unsigned int *)&RTG.NMG_debug); NMG_debug = RTG.NMG_debug; break; case 'e': /* Error file name. */ error_file = bu_optarg; break; case 'i': inches = 1; break; default: usage(argv[0]); } } if (bu_optind+1 >= argc) usage(argv[0]); if (!output_file) { fp = stdout; setmode(fileno(fp), O_BINARY); } else { /* Open output file */ if ((fp=fopen(output_file, "wb+")) == NULL) { perror(argv[0]); bu_exit(1, "Cannot open output file (%s) for writing\n", output_file); } } /* Open g-acad error log file */ if (!error_file) { fpe = stderr; setmode(fileno(fpe), O_BINARY); } else if ((fpe=fopen(error_file, "wb")) == NULL) { perror(argv[0]); bu_exit(1, "Cannot open output file (%s) for writing\n", error_file); } /* Open BRL-CAD database */ argc -= bu_optind; argv += bu_optind; if ((dbip = db_open(argv[0], DB_OPEN_READONLY)) == DBI_NULL) { perror(argv[0]); bu_exit(1, "Cannot open geometry database file (%s)\n", argv[0]); } if (db_dirbuild(dbip)) { bu_exit(1, "db_dirbuild failed\n"); } BN_CK_TOL(tree_state.ts_tol); RT_CK_TESS_TOL(tree_state.ts_ttol); fprintf(fpe, "Model: %s\n", argv[0]); fprintf(fpe, "Objects:"); for (i=1; i<argc; i++) fprintf(fpe, " %s", argv[i]); fprintf(fpe, "\nTessellation tolerances:\n\tabs = %g mm\n\trel = %g\n\tnorm = %g\n", tree_state.ts_ttol->abs, tree_state.ts_ttol->rel, tree_state.ts_ttol->norm); fprintf(fpe, "Calculational tolerances:\n\tdist = %g mm perp = %g\n", tree_state.ts_tol->dist, tree_state.ts_tol->perp); bu_log("Model: %s\n", argv[0]); bu_log("Objects:"); for (i=1; i<argc; i++) bu_log(" %s", argv[i]); bu_log("\nTessellation tolerances:\n\tabs = %g mm\n\trel = %g\n\tnorm = %g\n", tree_state.ts_ttol->abs, tree_state.ts_ttol->rel, tree_state.ts_ttol->norm); bu_log("Calculational tolerances:\n\tdist = %g mm perp = %g\n", tree_state.ts_tol->dist, tree_state.ts_tol->perp); /* Write out ACAD facet header */ if (inches) fprintf(fp, "BRL-CAD generated ACAD FACET FILE (Units in)\n"); else fprintf(fp, "BRL-CAD generated ACAD FACET FILE (Units mm)\n"); /* Generate space for number of facet entities, will write over later */ fprintf(fp, " "); /* Walk indicated tree(s). Each region will be output separately */ (void) db_walk_tree(dbip, argc-1, (const char **)(argv+1), 1, /* ncpu */ &tree_state, 0, /* take all regions */ do_region_end, nmg_booltree_leaf_tess, (void *)NULL); /* in librt/nmg_bool.c */ percent = 0; if (regions_tried>0) { percent = ((double)regions_converted * 100) / regions_tried; printf("Tried %d regions, %d converted to NMG's successfully. %g%%\n", regions_tried, regions_converted, percent); } percent = 0; if (regions_tried > 0) { percent = ((double)regions_written * 100) / regions_tried; printf(" %d triangulated successfully. %g%%\n", regions_written, percent); } bu_log("%ld triangles written\n", tot_polygons); fprintf(fpe, "%ld triangles written\n", tot_polygons); /* Write out number of facet entities to .facet file */ rewind(fp); bu_fseek(fp, 46, 0); /* Re-position pointer to 2nd line */ fprintf(fp, "%d\n", regions_written); /* Write out number of regions */ fclose(fp); /* Release dynamic storage */ nmg_km(the_model); rt_vlist_cleanup(); db_close(dbip); return 0; }
/* * Called from db_walk_tree(). * * This routine must be prepared to run in parallel. */ union tree * do_region_end(struct db_tree_state *tsp, const struct db_full_path *pathp, union tree *curtree, void *UNUSED(client_data)) { union tree *ret_tree = NULL; struct bu_list vhead; /* static due to longjmp */ static struct nmgregion *r = NULL; RT_CK_FULL_PATH(pathp); RT_CK_TREE(curtree); RT_CK_TESS_TOL(tsp->ts_ttol); BN_CK_TOL(tsp->ts_tol); NMG_CK_MODEL(*tsp->ts_m); BU_LIST_INIT(&vhead); if (RT_G_DEBUG&DEBUG_TREEWALK || verbose) { char *sofar = db_path_to_string(pathp); bu_log("\ndo_region_end(%d %d%%) %s\n", regions_tried, regions_tried>0 ? (regions_converted * 100) / regions_tried : 0, sofar); bu_free(sofar, "path string"); } if (curtree->tr_op == OP_NOP) return curtree; regions_tried++; /* do the deed */ ret_tree = process_region(pathp, curtree, tsp); if (ret_tree) r = ret_tree->tr_d.td_r; else { if (verbose) { printf("\tNothing left of this region after Boolean evaluation\n"); fprintf(fpe, "WARNING: Nothing left after Boolean evaluation: %s\n", db_path_to_string(pathp)); fflush(fpe); } regions_written++; /* don't count as a failure */ r = (struct nmgregion *)NULL; } regions_converted++; if (r != (struct nmgregion *)NULL) { struct shell *s; int empty_region=0; int empty_model=0; /* Kill cracks */ s = BU_LIST_FIRST(shell, &r->s_hd); while (BU_LIST_NOT_HEAD(&s->l, &r->s_hd)) { struct shell *next_s; next_s = BU_LIST_PNEXT(shell, &s->l); if (nmg_kill_cracks(s)) { if (nmg_ks(s)) { empty_region = 1; break; } } s = next_s; } /* kill zero length edgeuses */ if (!empty_region) { empty_model = nmg_kill_zero_length_edgeuses(*tsp->ts_m); } if (!empty_region && !empty_model) { if (!BU_SETJUMP) { /* try */ /* Write the region to the TANKILL file */ nmg_to_acad(r, pathp, tsp->ts_regionid); regions_written++; } else { /* catch */ char *sofar; BU_UNSETJUMP; sofar = db_path_to_string(pathp); bu_free((char *)sofar, "sofar"); /* Sometimes the NMG library adds debugging bits when * it detects an internal error, before bombing out. */ RTG.NMG_debug = NMG_debug; /* restore mode */ /* Release any intersector 2d tables */ nmg_isect2d_final_cleanup(); /* Get rid of (m)any other intermediate structures */ if ((*tsp->ts_m)->magic == NMG_MODEL_MAGIC) { nmg_km(*tsp->ts_m); } else { bu_log("WARNING: tsp->ts_m pointer corrupted, ignoring it.\n"); } /* Now, make a new, clean model structure for next pass. */ *tsp->ts_m = nmg_mm(); /* FIXME: leaking memory with curtree */ return TREE_NULL; } BU_UNSETJUMP; } if (!empty_model) nmg_kr(r); } /* * Dispose of original tree, so that all associated dynamic * memory is released now, not at the end of all regions. * A return of TREE_NULL from this routine signals an error, * and there is no point to adding _another_ message to our output, * so we need to cons up an OP_NOP node to return. */ if (regions_tried>0) { float npercent, tpercent; npercent = (float)(regions_converted * 100) / regions_tried; tpercent = (float)(regions_written * 100) / regions_tried; printf("Tried %d regions, %d conv. to NMG's %d conv. to tri. nmgper = %.2f%% triper = %.2f%% \n", regions_tried, regions_converted, regions_written, npercent, tpercent); } BU_ALLOC(curtree, union tree); RT_TREE_INIT(curtree); curtree->tr_op = OP_NOP; return curtree; }
/** * If there is no ve_dist structure for this edge, compute one and * add it to the list. * * Sort an edge_info structure into the loops list of edgeuse status */ static struct edge_info * nmg_class_pt_eu(struct fpi *fpi, struct edgeuse *eu, struct edge_info *edge_list, const int in_or_out_only) { struct bn_tol tmp_tol; struct edgeuse *next_eu; struct ve_dist *ved, *ed; struct edge_info *ei_p; struct edge_info *ei; pointp_t eu_pt; vect_t left; vect_t v_to_pt; int found_data = 0; NMG_CK_FPI(fpi); BN_CK_TOL(fpi->tol); if (RTG.NMG_debug & DEBUG_PT_FU) { bu_log("pt (%g %g %g) vs_edge (%g %g %g) (%g %g %g) (eu=%p)\n", V3ARGS(fpi->pt), V3ARGS(eu->vu_p->v_p->vg_p->coord), V3ARGS(eu->eumate_p->vu_p->v_p->vg_p->coord), (void *)eu); } /* we didn't find a ve_dist structure for this edge, so we'll * have to do the calculations. */ tmp_tol = (*fpi->tol); if (in_or_out_only) { tmp_tol.dist = 0.0; tmp_tol.dist_sq = 0.0; } BU_ALLOC(ved, struct ve_dist); ved->magic_p = &eu->e_p->magic; ved->status = bn_distsq_pt3_lseg3(&ved->dist, eu->vu_p->v_p->vg_p->coord, eu->eumate_p->vu_p->v_p->vg_p->coord, fpi->pt, &tmp_tol); ved->v1 = eu->vu_p->v_p; ved->v2 = eu->eumate_p->vu_p->v_p; BU_LIST_MAGIC_SET(&ved->l, NMG_VE_DIST_MAGIC); BU_LIST_APPEND(&fpi->ve_dh, &ved->l); eu_pt = ved->v1->vg_p->coord; if (RTG.NMG_debug & DEBUG_PT_FU) { bu_log("nmg_class_pt_eu: status for eu %p (%g %g %g)<->(%g %g %g) vs pt (%g %g %g) is %d\n", (void *)eu, V3ARGS(eu->vu_p->v_p->vg_p->coord), V3ARGS(eu->eumate_p->vu_p->v_p->vg_p->coord), V3ARGS(fpi->pt), ved->status); bu_log("\tdist = %g\n", ved->dist); } /* Add a struct for this edgeuse to the loop's list of dist-sorted * edgeuses. */ BU_ALLOC(ei, struct edge_info); ei->ved_p = ved; ei->eu_p = eu; BU_LIST_MAGIC_SET(&ei->l, NMG_EDGE_INFO_MAGIC); /* compute the status (ei->status) of the point WRT this edge */ switch (ved->status) { case 0: /* pt is on the edge(use) */ ei->nmg_class = NMG_CLASS_AonBshared; if (fpi->eu_func && (fpi->hits == NMG_FPI_PERUSE || (fpi->hits == NMG_FPI_PERGEOM && !found_data))) { /* need to cast eu_func pointer for actual use as a function */ void (*cfp)(struct edgeuse *, point_t, const char*); cfp = (void (*)(struct edgeuse *, point_t, const char *))fpi->eu_func; cfp(eu, fpi->pt, fpi->priv); } break; case 1: /* within tolerance of endpoint at ved->v1 */ ei->nmg_class = NMG_CLASS_AonBshared; /* add an entry for the vertex in the edge list so that * other uses of this vertex will claim the point is within * tolerance without re-computing */ BU_ALLOC(ed, struct ve_dist); ed->magic_p = &ved->v1->magic; ed->status = ved->status; ed->v1 = ed->v2 = ved->v1; BU_LIST_MAGIC_SET(&ed->l, NMG_VE_DIST_MAGIC); BU_LIST_APPEND(&fpi->ve_dh, &ed->l); if (fpi->vu_func && (fpi->hits == NMG_FPI_PERUSE || (fpi->hits == NMG_FPI_PERGEOM && !found_data))) { /* need to cast vu_func pointer for actual use as a function */ void (*cfp)(struct vertexuse *, point_t, const char*); cfp = (void (*)(struct vertexuse *, point_t, const char *))fpi->vu_func; cfp(eu->vu_p, fpi->pt, fpi->priv); } break; case 2: /* within tolerance of endpoint at ved->v2 */ ei->nmg_class = NMG_CLASS_AonBshared; /* add an entry for the vertex in the edge list so that * other uses of this vertex will claim the point is within * tolerance without re-computing */ BU_ALLOC(ed, struct ve_dist); ed->magic_p = &ved->v2->magic; ed->status = ved->status; ed->v1 = ed->v2 = ved->v2; BU_LIST_MAGIC_SET(&ed->l, NMG_VE_DIST_MAGIC); BU_LIST_APPEND(&fpi->ve_dh, &ed->l); if (fpi->vu_func && (fpi->hits == NMG_FPI_PERUSE || (fpi->hits == NMG_FPI_PERGEOM && !found_data))) { /* need to cast vu_func pointer for actual use as a function */ void (*cfp)(struct vertexuse *, point_t, const char*); cfp = (void (*)(struct vertexuse *, point_t, const char *))fpi->vu_func; cfp(eu->eumate_p->vu_p, fpi->pt, fpi->priv); } break; case 3: /* PCA of pt on line is within tolerance of ved->v1 of segment */ ei->nmg_class = nmg_class_pt_euvu(fpi->pt, eu, fpi->tol); if (ei->nmg_class == NMG_CLASS_Unknown) ei->ved_p->dist = MAX_FASTF; break; case 4: /* PCA of pt on line is within tolerance of ved->v2 of segment */ next_eu = BU_LIST_PNEXT_CIRC(edgeuse, &eu->l); ei->nmg_class = nmg_class_pt_euvu(fpi->pt, next_eu, fpi->tol); if (ei->nmg_class == NMG_CLASS_Unknown) ei->ved_p->dist = MAX_FASTF; break; case 5: /* PCA is along length of edge, but point is NOT on edge. */ if (nmg_find_eu_left_non_unit(left, eu)) bu_bomb("can't find left vector\n"); /* take dot product of v->pt vector with left to determine * if pt is inside/left of edge */ VSUB2(v_to_pt, fpi->pt, eu_pt); if (VDOT(v_to_pt, left) > -SMALL_FASTF) ei->nmg_class = NMG_CLASS_AinB; else ei->nmg_class = NMG_CLASS_AoutB; break; default: bu_log("%s:%d status = %d\n", __FILE__, __LINE__, ved->status); bu_bomb("Why did this happen?"); break; } if (RTG.NMG_debug & DEBUG_PT_FU) { bu_log("pt @ dist %g from edge classed %s vs edge\n", ei->ved_p->dist, nmg_class_name(ei->nmg_class)); /* pl_pt_e(fpi, ei); */ } /* now that it's complete, add ei to the edge list */ for (BU_LIST_FOR(ei_p, edge_info, &edge_list->l)) { /* if the distance to this edge is smaller, or * if the distance is the same & the edge is the same * Insert edge_info struct here in list */ if (ved->dist < ei_p->ved_p->dist || (ZERO(ved->dist - ei_p->ved_p->dist) && ei_p->ved_p->magic_p == ved->magic_p)) { break; } } BU_LIST_INSERT(&ei_p->l, &ei->l); return ei; }
int nmg_class_pt_euvu(const fastf_t *pt, struct edgeuse *eu_in, const struct bn_tol *tol) { struct loopuse *lu; struct edgeuse *prev_eu; struct edgeuse *eu; struct vertex *v0, *v1, *v2; vect_t left; vect_t eu_dir; vect_t other_eudir; vect_t pt_dir; fastf_t xo, yo; fastf_t xpt, ypt; fastf_t len; int quado, quadpt; int nmg_class = NMG_CLASS_Unknown; int eu_is_crack = 0; int prev_eu_is_crack = 0; NMG_CK_EDGEUSE(eu_in); BN_CK_TOL(tol); eu = eu_in; if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("nmg_class_pt_euvu((%g %g %g), eu=%p)\n", V3ARGS(pt), (void *)eu); if (UNLIKELY(*eu->up.magic_p != NMG_LOOPUSE_MAGIC)) { bu_log("nmg_class_pt_euvu() called with eu (%p) that isn't part of a loop\n", (void *)eu); bu_bomb("nmg_class_pt_euvu() called with eu that isn't part of a loop"); } lu = eu->up.lu_p; NMG_CK_LOOPUSE(lu); eu_is_crack = nmg_eu_is_part_of_crack(eu); prev_eu = BU_LIST_PPREV_CIRC(edgeuse, &eu->l); prev_eu_is_crack = nmg_eu_is_part_of_crack(prev_eu); /* if both EU's are cracks, we cannot classify */ if (eu_is_crack && prev_eu_is_crack) return NMG_CLASS_Unknown; if (eu_is_crack) { struct edgeuse *eu_test; int done = 0; if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("nmg_class_pt_euvu: eu %p is a crack\n", (void *)eu); /* find next eu from this vertex that is not a crack */ eu_test = BU_LIST_PNEXT_CIRC(edgeuse, &eu->l); while (!done) { while (eu_test->vu_p->v_p != eu->vu_p->v_p && eu_test != eu) eu_test = BU_LIST_PNEXT_CIRC(edgeuse, &eu_test->l); if (eu_test == eu) done = 1; if (!nmg_eu_is_part_of_crack(eu_test)) done = 1; if (!done) eu_test = BU_LIST_PNEXT_CIRC(edgeuse, &eu_test->l); } if (eu_test == eu) /* can't get away from crack */ return NMG_CLASS_Unknown; else eu = eu_test; if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("\tUsing eu %p instead\n", (void *)eu); } if (prev_eu_is_crack) { struct edgeuse *eu_test; int done = 0; if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("nmg_class_pt_euvu: prev_eu (%p) is a crack\n", (void *)prev_eu); /* find previous eu ending at this vertex that is not a crack */ eu_test = BU_LIST_PPREV_CIRC(edgeuse, &prev_eu->l); while (!done) { while (eu_test->eumate_p->vu_p->v_p != eu->vu_p->v_p && eu_test != prev_eu) eu_test = BU_LIST_PPREV_CIRC(edgeuse, &eu_test->l); if (eu_test == prev_eu) done = 1; if (!nmg_eu_is_part_of_crack(eu_test)) done = 1; if (!done) eu_test = BU_LIST_PPREV_CIRC(edgeuse, &eu_test->l); } if (eu_test == prev_eu) /* can't get away from crack */ return NMG_CLASS_Unknown; else prev_eu = eu_test; if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("\tUsing prev_eu %p instead\n", (void *)prev_eu); } /* left is the Y-axis of our XY-coordinate system */ if (UNLIKELY(nmg_find_eu_leftvec(left, eu))) { bu_log("nmg_class_pt_euvu: nmg_find_eu_leftvec() for eu=%p failed!\n", (void *)eu); bu_bomb("nmg_class_pt_euvu: nmg_find_eu_leftvec() failed!"); } if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("\tprev_eu = %p, left = (%g %g %g)\n", (void *)prev_eu, V3ARGS(left)); /* v0 is the origin of the XY-coordinate system */ v0 = eu->vu_p->v_p; NMG_CK_VERTEX(v0); /* v1 is on the X-axis */ v1 = eu->eumate_p->vu_p->v_p; NMG_CK_VERTEX(v1); /* v2 determines angle prev_eu makes with X-axis */ v2 = prev_eu->vu_p->v_p; NMG_CK_VERTEX(v2); if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("\tv0=%p, v1=%p, v2=%p\n", (void *)v0, (void *)v1, (void *)v2); /* eu_dir is our X-direction */ VSUB2(eu_dir, v1->vg_p->coord, v0->vg_p->coord); /* other_eudir is direction along the previous EU (from origin) */ VSUB2(other_eudir, v2->vg_p->coord, v0->vg_p->coord); if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("\teu_dir=(%g %g %g), other_eudir=(%g %g %g)\n", V3ARGS(eu_dir), V3ARGS(other_eudir)); /* get X and Y components for other_eu */ xo = VDOT(eu_dir, other_eudir); yo = VDOT(left, other_eudir); /* which quadrant does this XY point lie in */ quado = Quadrant(xo, yo); if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("\txo=%g, yo=%g, quadrant=%d\n", xo, yo, quado); /* get direction to PT from origin */ VSUB2(pt_dir, pt, v0->vg_p->coord); if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("\tpt_dir=(%g %g %g)\n", V3ARGS(pt_dir)); /* get X and Y components for PT */ xpt = VDOT(eu_dir, pt_dir); ypt = VDOT(left, pt_dir); /* which quadrant does this XY point lie in */ quadpt = Quadrant(xpt, ypt); if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("\txpt=%g, ypt=%g, quadrant=%d\n", xpt, ypt, quadpt); /* do a quadrant comparison first (cheap!!!) */ if (quadpt < quado) return NMG_CLASS_AinB; if (quadpt > quado) return NMG_CLASS_AoutB; /* both are in the same quadrant, need to normalize the coordinates */ len = sqrt(xo*xo + yo*yo); xo = xo/len; yo = yo/len; len = sqrt(xpt*xpt + ypt*ypt); xpt = xpt/len; ypt = ypt/len; if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("\tNormalized xo, yo=(%g %g), xpt, ypt=(%g %g)\n", xo, yo, xpt, ypt); switch (quadpt) { case 1: if (xpt >= xo && ypt <= yo) nmg_class = NMG_CLASS_AinB; else nmg_class = NMG_CLASS_AoutB; break; case 2: if (xpt >= xo && ypt >= yo) nmg_class = NMG_CLASS_AinB; else nmg_class = NMG_CLASS_AoutB; break; case 3: if (xpt <= xo && ypt >= yo) nmg_class = NMG_CLASS_AinB; else nmg_class = NMG_CLASS_AoutB; break; case 4: if (xpt <= xo && ypt <= yo) nmg_class = NMG_CLASS_AinB; else nmg_class = NMG_CLASS_AoutB; break; default: bu_log("This can't happen (illegal quadrant %d)\n", quadpt); bu_bomb("This can't happen (illegal quadrant)\n"); break; } if (UNLIKELY(RTG.NMG_debug & DEBUG_PT_FU)) bu_log("returning %s\n", nmg_class_name(nmg_class)); return nmg_class; }
/** * Find the square of the distance from a point P to a line segment described * by the two endpoints A and B. * * P * * * /. * / . * / . * / . (dist) * / . * / . * *------*--------* * A PCA B * * There are six distinct cases, with these return codes - * 0 P is within tolerance of lseg AB. *dist = 0. * 1 P is within tolerance of point A. *dist = 0. * 2 P is within tolerance of point B. *dist = 0. * 3 PCA is within tolerance of A. *dist = |P-A|**2. * 4 PCA is within tolerance of B. *dist = |P-B|**2. * 5 P is "above/below" lseg AB. *dist=|PCA-P|**2. * * This routine was formerly called bn_dist_pt_lseg(). * This is a special version that returns the square of the distance * and does not actually calculate PCA. * */ int bn_distsq_pt3_lseg3(fastf_t *dist, const fastf_t *a, const fastf_t *b, const fastf_t *p, const struct bn_tol *tol) { vect_t PtoA; /* P-A */ vect_t PtoB; /* P-B */ vect_t AtoB; /* B-A */ fastf_t P_A_sq; /* |P-A|**2 */ fastf_t P_B_sq; /* |P-B|**2 */ fastf_t B_A_sq; /* |B-A|**2 */ fastf_t t; /* distance squared along ray of projection of P */ fastf_t dot; BN_CK_TOL(tol); /* Check proximity to endpoint A */ VSUB2(PtoA, p, a); P_A_sq = MAGSQ(PtoA); if (P_A_sq < tol->dist_sq) { /* P is within the tol->dist radius circle around A */ *dist = 0.0; return 1; } /* Check proximity to endpoint B */ VSUB2(PtoB, p, b); P_B_sq = MAGSQ(PtoB); if (P_B_sq < tol->dist_sq) { /* P is within the tol->dist radius circle around B */ *dist = 0.0; return 2; } VSUB2(AtoB, b, a); B_A_sq = MAGSQ(AtoB); /* compute distance squared (in actual units) along line to PROJECTION of * point p onto the line: point pca */ dot = VDOT(PtoA, AtoB); t = dot * dot / B_A_sq; if (t < tol->dist_sq) { /* PCA is at A */ *dist = P_A_sq; return 3; } if (dot < -SMALL_FASTF && t > tol->dist_sq) { /* P is "left" of A */ *dist = P_A_sq; return 3; } if (t < (B_A_sq - tol->dist_sq)) { /* PCA falls between A and B */ register fastf_t dsq; /* Find distance from PCA to line segment (Pythagorus) */ dsq = P_A_sq - t; if (dsq < tol->dist_sq) { /* PCA is on lseg */ *dist = 0.0; return 0; } /* P is above or below lseg */ *dist = dsq; return 5; } /* P is "right" of B */ *dist = P_B_sq; return 4; }
/** * This is the gist for what is going on (not verified): * * 1. initialize tree_state (db_tree_state) * 2. Deal with command line arguments. Strip off everything but regions for processing. * 3. Open geometry (.g) file and build directory db_dirbuild * 4. db_walk_tree (get_layer) for layer names only * 5. Initialize tree_state * 6. Initialize model (nmg)\ * 7. db_walk_tree (gcv_region_end) * 8. Cleanup */ int main(int argc, char *argv[]) { int c; double percent; bu_setlinebuf(stderr); tree_state = rt_initial_tree_state; /* struct copy */ tree_state.ts_tol = &tol; tree_state.ts_ttol = &ttol; tree_state.ts_m = &the_model; /* Set up tessellation tolerance defaults */ ttol.magic = RT_TESS_TOL_MAGIC; /* Defaults, updated by command line options. */ ttol.abs = 0.0; ttol.rel = 0.01; ttol.norm = 0.0; /* Set up calculation tolerance defaults */ /* FIXME: These need to be improved */ tol.magic = BN_TOL_MAGIC; tol.dist = 0.0005; tol.dist_sq = tol.dist * tol.dist; tol.perp = 1e-6; tol.para = 1 - tol.perp; /* init resources we might need */ rt_init_resource(&rt_uniresource, 0, NULL); BU_LIST_INIT(&RTG.rtg_vlfree); /* for vlist macros */ /* Get command line arguments. */ while ((c = bu_getopt(argc, argv, "a:n:o:pr:vx:D:P:X:ih?")) != -1) { switch (c) { case 'a': /* Absolute tolerance. */ ttol.abs = atof(bu_optarg); ttol.rel = 0.0; break; case 'n': /* Surface normal tolerance. */ ttol.norm = atof(bu_optarg); ttol.rel = 0.0; break; case 'o': /* Output file name. */ output_file = bu_optarg; break; case 'p': polyface_mesh = 1; break; case 'r': /* Relative tolerance. */ ttol.rel = atof(bu_optarg); break; case 'v': verbose++; break; case 'P': ncpu = atoi(bu_optarg); break; case 'x': sscanf(bu_optarg, "%x", (unsigned int *)&RTG.debug); break; case 'D': tol.dist = atof(bu_optarg); tol.dist_sq = tol.dist * tol.dist; rt_pr_tol(&tol); break; case 'X': sscanf(bu_optarg, "%x", (unsigned int *)&RTG.NMG_debug); NMG_debug = RTG.NMG_debug; break; case 'i': inches = 1; break; default: usage(argv[0]); bu_exit(1, "%s\n", brlcad_ident("BRL-CAD to DXF Exporter")); break; } } if (bu_optind+1 >= argc) { usage(argv[0]); bu_exit(1, "%s\n", brlcad_ident("BRL-CAD to DXF Exporter")); } if (!output_file) { fp = stdout; setmode(fileno(fp), O_BINARY); } else { /* Open output file */ if ((fp=fopen(output_file, "w+b")) == NULL) { perror(argv[0]); bu_exit(1, " Cannot open output file (%s) for writing\n", output_file); } } /* Open BRL-CAD database */ argc -= bu_optind; argv += bu_optind; if ((dbip = db_open(argv[0], DB_OPEN_READONLY)) == DBI_NULL) { perror(argv[0]); bu_exit(1, "Unable to open geometry database file (%s)\n", argv[0]); } if (db_dirbuild(dbip)) { bu_exit(1, "db_dirbuild failed\n"); } BN_CK_TOL(tree_state.ts_tol); RT_CK_TESS_TOL(tree_state.ts_ttol); if (verbose) { int i; bu_log("Model: %s\n", argv[0]); bu_log("Objects:"); for (i = 1; i < argc; i++) bu_log(" %s", argv[i]); bu_log("\nTessellation tolerances:\n\tabs = %g mm\n\trel = %g\n\tnorm = %g\n", tree_state.ts_ttol->abs, tree_state.ts_ttol->rel, tree_state.ts_ttol->norm); bu_log("Calculational tolerances:\n\tdist = %g mm perp = %g\n", tree_state.ts_tol->dist, tree_state.ts_tol->perp); } /* output DXF header and start of TABLES section */ fprintf(fp, "0\nSECTION\n2\nHEADER\n999\n%s\n0\nENDSEC\n0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n", argv[argc-1]); /* Walk indicated tree(s) just for layer names to put in TABLES section */ (void)db_walk_tree(dbip, argc-1, (const char **)(argv+1), 1, /* ncpu */ &tree_state, 0, /* take all regions */ get_layer, NULL, (void *)NULL); /* in librt/nmg_bool.c */ /* end of layers section, start of ENTITIES SECTION */ fprintf(fp, "0\nENDTAB\n0\nENDSEC\n0\nSECTION\n2\nENTITIES\n"); /* Walk indicated tree(s). Each region will be output separately */ tree_state = rt_initial_tree_state; /* struct copy */ tree_state.ts_tol = &tol; tree_state.ts_ttol = &ttol; /* make empty NMG model */ the_model = nmg_mm(); tree_state.ts_m = &the_model; (void) db_walk_tree(dbip, argc-1, (const char **)(argv+1), 1, /* ncpu */ &tree_state, 0, /* take all regions */ gcv_region_end, nmg_booltree_leaf_tess, (void *)&gcvwriter); /* callback for gcv_region_end */ percent = 0; if (regions_tried>0) { percent = ((double)regions_converted * 100) / regions_tried; if (verbose) bu_log("Tried %d regions, %d converted to NMG's successfully. %g%%\n", regions_tried, regions_converted, percent); } percent = 0; if (regions_tried > 0) { percent = ((double)regions_written * 100) / regions_tried; if (verbose) bu_log(" %d triangulated successfully. %g%%\n", regions_written, percent); } bu_log("%ld triangles written\n", (long int)tot_polygons); fprintf(fp, "0\nENDSEC\n0\nEOF\n"); if (output_file) { fclose(fp); } /* Release dynamic storage */ nmg_km(the_model); rt_vlist_cleanup(); db_close(dbip); return 0; }