/** * Calculate a bounding RPP for an ARBN */ int rt_arbn_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol)) { size_t i, j, k; struct rt_arbn_internal *aip; RT_CK_DB_INTERNAL(ip); aip = (struct rt_arbn_internal *)ip->idb_ptr; RT_ARBN_CK_MAGIC(aip); VSETALL((*min), INFINITY); VSETALL((*max), -INFINITY); /* Discover all vertices, use to calculate RPP */ for (i = 0; i < aip->neqn-2; i++) { for (j = i+1; j < aip->neqn-1; j++) { double dot; /* If normals are parallel, no intersection */ dot = VDOT(aip->eqn[i], aip->eqn[j]); if (((dot) <= -SMALL_FASTF) ? (NEAR_EQUAL((dot), -1.0, RT_DOT_TOL)) : (NEAR_EQUAL((dot), 1.0, RT_DOT_TOL))) continue; /* Have an edge line, isect with higher numbered planes */ for (k = j + 1; k < aip->neqn; k++) { size_t m; size_t next_k; point_t pt; next_k = 0; if (bn_mkpoint_3planes(pt, aip->eqn[i], aip->eqn[j], aip->eqn[k]) < 0) continue; /* See if point is outside arb */ for (m = 0; m < aip->neqn; m++) { if (i == m || j == m || k == m) continue; if (VDOT(pt, aip->eqn[m])-aip->eqn[m][3] > RT_LEN_TOL) { next_k = 1; break; } } if (next_k != 0) continue; VMINMAX((*min), (*max), pt); } } } return 0; }
/* * Default keypoint in model space is established in "pt". Returns * GED_ERROR if unable to determine a keypoint, otherwise returns * GED_OK. */ int _ged_get_solid_keypoint(struct ged *const gedp, fastf_t *const pt, const struct rt_db_internal *const ip, const fastf_t *const mat) { point_t mpt; RT_CK_DB_INTERNAL(ip); switch (ip->idb_type) { case ID_CLINE: { struct rt_cline_internal *cli = (struct rt_cline_internal *)ip->idb_ptr; RT_CLINE_CK_MAGIC(cli); VMOVE(mpt, cli->v); break; } case ID_PARTICLE: { struct rt_part_internal *part = (struct rt_part_internal *)ip->idb_ptr; RT_PART_CK_MAGIC(part); VMOVE(mpt, part->part_V); break; } case ID_PIPE: { struct rt_pipe_internal *pipeip; struct wdb_pipept *pipe_seg; pipeip = (struct rt_pipe_internal *)ip->idb_ptr; RT_PIPE_CK_MAGIC(pipeip); pipe_seg = BU_LIST_FIRST(wdb_pipept, &pipeip->pipe_segs_head); VMOVE(mpt, pipe_seg->pp_coord); break; } case ID_METABALL: { struct rt_metaball_internal *metaball = (struct rt_metaball_internal *)ip->idb_ptr; struct wdb_metaballpt *metaballpt; RT_METABALL_CK_MAGIC(metaball); VSETALL(mpt, 0.0); metaballpt = BU_LIST_FIRST(wdb_metaballpt, &metaball->metaball_ctrl_head); VMOVE(mpt, metaballpt->coord); break; } case ID_ARBN: { struct rt_arbn_internal *arbn = (struct rt_arbn_internal *)ip->idb_ptr; size_t i, j, k; int good_vert = 0; RT_ARBN_CK_MAGIC(arbn); for (i = 0; i < arbn->neqn; i++) { for (j = i + 1; j < arbn->neqn; j++) { for (k = j + 1; k < arbn->neqn; k++) { if (!bn_mkpoint_3planes(mpt, arbn->eqn[i], arbn->eqn[j], arbn->eqn[k])) { size_t l; good_vert = 1; for (l = 0; l < arbn->neqn; l++) { if (l == i || l == j || l == k) continue; if (DIST_PT_PLANE(mpt, arbn->eqn[l]) > gedp->ged_wdbp->wdb_tol.dist) { good_vert = 0; break; } } if (good_vert) break; } } if (good_vert) break; } if (good_vert) break; } break; } case ID_EBM: { struct rt_ebm_internal *ebm = (struct rt_ebm_internal *)ip->idb_ptr; point_t pnt; RT_EBM_CK_MAGIC(ebm); VSETALL(pnt, 0.0); MAT4X3PNT(mpt, ebm->mat, pnt); break; } case ID_BOT: { struct rt_bot_internal *bot = (struct rt_bot_internal *)ip->idb_ptr; VMOVE(mpt, bot->vertices); break; } case ID_DSP: { struct rt_dsp_internal *dsp = (struct rt_dsp_internal *)ip->idb_ptr; point_t pnt; RT_DSP_CK_MAGIC(dsp); VSETALL(pnt, 0.0); MAT4X3PNT(mpt, dsp->dsp_stom, pnt); break; } case ID_HF: { struct rt_hf_internal *hf = (struct rt_hf_internal *)ip->idb_ptr; RT_HF_CK_MAGIC(hf); VMOVE(mpt, hf->v); break; } case ID_VOL: { struct rt_vol_internal *vol = (struct rt_vol_internal *)ip->idb_ptr; point_t pnt; RT_VOL_CK_MAGIC(vol); VSETALL(pnt, 0.0); MAT4X3PNT(mpt, vol->mat, pnt); break; } case ID_HALF: { struct rt_half_internal *haf = (struct rt_half_internal *)ip->idb_ptr; RT_HALF_CK_MAGIC(haf); VSCALE(mpt, haf->eqn, haf->eqn[H]); break; } case ID_ARB8: { struct rt_arb_internal *arb = (struct rt_arb_internal *)ip->idb_ptr; RT_ARB_CK_MAGIC(arb); VMOVE(mpt, arb->pt[0]); break; } case ID_ELL: case ID_SPH: { struct rt_ell_internal *ell = (struct rt_ell_internal *)ip->idb_ptr; RT_ELL_CK_MAGIC(ell); VMOVE(mpt, ell->v); break; } case ID_SUPERELL: { struct rt_superell_internal *superell = (struct rt_superell_internal *)ip->idb_ptr; RT_SUPERELL_CK_MAGIC(superell); VMOVE(mpt, superell->v); break; } case ID_TOR: { struct rt_tor_internal *tor = (struct rt_tor_internal *)ip->idb_ptr; RT_TOR_CK_MAGIC(tor); VMOVE(mpt, tor->v); break; } case ID_TGC: case ID_REC: { struct rt_tgc_internal *tgc = (struct rt_tgc_internal *)ip->idb_ptr; RT_TGC_CK_MAGIC(tgc); VMOVE(mpt, tgc->v); break; } case ID_GRIP: { struct rt_grip_internal *gip = (struct rt_grip_internal *)ip->idb_ptr; RT_GRIP_CK_MAGIC(gip); VMOVE(mpt, gip->center); break; } case ID_ARS: { struct rt_ars_internal *ars = (struct rt_ars_internal *)ip->idb_ptr; RT_ARS_CK_MAGIC(ars); VMOVE(mpt, &ars->curves[0][0]); break; } case ID_RPC: { struct rt_rpc_internal *rpc = (struct rt_rpc_internal *)ip->idb_ptr; RT_RPC_CK_MAGIC(rpc); VMOVE(mpt, rpc->rpc_V); break; } case ID_RHC: { struct rt_rhc_internal *rhc = (struct rt_rhc_internal *)ip->idb_ptr; RT_RHC_CK_MAGIC(rhc); VMOVE(mpt, rhc->rhc_V); break; } case ID_EPA: { struct rt_epa_internal *epa = (struct rt_epa_internal *)ip->idb_ptr; RT_EPA_CK_MAGIC(epa); VMOVE(mpt, epa->epa_V); break; } case ID_EHY: { struct rt_ehy_internal *ehy = (struct rt_ehy_internal *)ip->idb_ptr; RT_EHY_CK_MAGIC(ehy); VMOVE(mpt, ehy->ehy_V); break; } case ID_HYP: { struct rt_hyp_internal *hyp = (struct rt_hyp_internal *)ip->idb_ptr; RT_HYP_CK_MAGIC(hyp); VMOVE(mpt, hyp->hyp_Vi); break; } case ID_ETO: { struct rt_eto_internal *eto = (struct rt_eto_internal *)ip->idb_ptr; RT_ETO_CK_MAGIC(eto); VMOVE(mpt, eto->eto_V); break; } case ID_POLY: { struct rt_pg_face_internal *_poly; struct rt_pg_internal *pg = (struct rt_pg_internal *)ip->idb_ptr; RT_PG_CK_MAGIC(pg); _poly = pg->poly; VMOVE(mpt, _poly->verts); break; } case ID_SKETCH: { struct rt_sketch_internal *skt = (struct rt_sketch_internal *)ip->idb_ptr; RT_SKETCH_CK_MAGIC(skt); VMOVE(mpt, skt->V); break; } case ID_EXTRUDE: { struct rt_extrude_internal *extr = (struct rt_extrude_internal *)ip->idb_ptr; RT_EXTRUDE_CK_MAGIC(extr); if (extr->skt && extr->skt->verts) { VJOIN2(mpt, extr->V, extr->skt->verts[0][0], extr->u_vec, extr->skt->verts[0][1], extr->v_vec); } else { VMOVE(mpt, extr->V); } break; } case ID_NMG: { struct vertex *v; struct vertexuse *vu; struct edgeuse *eu; struct loopuse *lu; struct faceuse *fu; struct shell *s; struct nmgregion *r; struct model *m = (struct model *) ip->idb_ptr; NMG_CK_MODEL(m); /* set default first */ VSETALL(mpt, 0.0); if (BU_LIST_IS_EMPTY(&m->r_hd)) break; r = BU_LIST_FIRST(nmgregion, &m->r_hd); if (!r) break; NMG_CK_REGION(r); if (BU_LIST_IS_EMPTY(&r->s_hd)) break; s = BU_LIST_FIRST(shell, &r->s_hd); if (!s) break; NMG_CK_SHELL(s); if (BU_LIST_IS_EMPTY(&s->fu_hd)) fu = (struct faceuse *)NULL; else fu = BU_LIST_FIRST(faceuse, &s->fu_hd); if (fu) { NMG_CK_FACEUSE(fu); lu = BU_LIST_FIRST(loopuse, &fu->lu_hd); NMG_CK_LOOPUSE(lu); if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_EDGEUSE_MAGIC) { eu = BU_LIST_FIRST(edgeuse, &lu->down_hd); NMG_CK_EDGEUSE(eu); NMG_CK_VERTEXUSE(eu->vu_p); v = eu->vu_p->v_p; } else { vu = BU_LIST_FIRST(vertexuse, &lu->down_hd); NMG_CK_VERTEXUSE(vu); v = vu->v_p; } NMG_CK_VERTEX(v); if (!v->vg_p) break; VMOVE(mpt, v->vg_p->coord); break; } if (BU_LIST_IS_EMPTY(&s->lu_hd)) lu = (struct loopuse *)NULL; else lu = BU_LIST_FIRST(loopuse, &s->lu_hd); if (lu) { NMG_CK_LOOPUSE(lu); if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_EDGEUSE_MAGIC) { eu = BU_LIST_FIRST(edgeuse, &lu->down_hd); NMG_CK_EDGEUSE(eu); NMG_CK_VERTEXUSE(eu->vu_p); v = eu->vu_p->v_p; } else { vu = BU_LIST_FIRST(vertexuse, &lu->down_hd); NMG_CK_VERTEXUSE(vu); v = vu->v_p; } NMG_CK_VERTEX(v); if (!v->vg_p) break; VMOVE(mpt, v->vg_p->coord); break; } if (BU_LIST_IS_EMPTY(&s->eu_hd)) eu = (struct edgeuse *)NULL; else eu = BU_LIST_FIRST(edgeuse, &s->eu_hd); if (eu) { NMG_CK_EDGEUSE(eu); NMG_CK_VERTEXUSE(eu->vu_p); v = eu->vu_p->v_p; NMG_CK_VERTEX(v); if (!v->vg_p) break; VMOVE(mpt, v->vg_p->coord); break; } vu = s->vu_p; if (vu) { NMG_CK_VERTEXUSE(vu); v = vu->v_p; NMG_CK_VERTEX(v); if (!v->vg_p) break; VMOVE(mpt, v->vg_p->coord); break; } } default: VSETALL(mpt, 0.0); bu_vls_printf(gedp->ged_result_str, "get_solid_keypoint: unrecognized solid type"); return GED_ERROR; } MAT4X3PNT(pt, mat, mpt); return GED_OK; }
int read_arbn(char *name) { int npt; /* # vertex pts to be read in */ int npe; /* # planes from 3 vertex points */ int neq; /* # planes from equation */ int nae; /* # planes from az, el & vertex index */ int nface; /* total number of faces */ double *input_points = (double *)0; double *vertex = (double *)0; /* vertex list of final solid */ int last_vertex; /* index of first unused vertex */ int max_vertex; /* size of vertex array */ int *used = (int *)0; /* plane eqn use count */ plane_t *eqn = (plane_t *)0; /* plane equations */ int cur_eq = 0; /* current (free) equation number */ int symm = 0; /* symmetry about Y used */ register int i; int j; int k; register int m; point_t cent; /* centroid of arbn */ struct bn_tol tol; /* XXX The tolerance here is sheer guesswork */ tol.magic = BN_TOL_MAGIC; tol.dist = 0.005; tol.dist_sq = tol.dist * tol.dist; tol.perp = 1e-6; tol.para = 1 - tol.perp; npt = getint( scard, 10+0*10, 10 ); npe = getint( scard, 10+1*10, 10 ); neq = getint( scard, 10+2*10, 10 ); nae = getint( scard, 10+3*10, 10 ); nface = npe + neq + nae; if ( npt < 1 ) { /* Having one point is necessary to compute centroid */ printf("arbn defined without at least one point\n"); bad: if (npt>0) eat( (npt+1)/2 ); /* vertex input_points */ if (npe>0) eat( (npe+5)/6 ); /* vertex pt index numbers */ if (neq>0) eat( neq ); /* plane eqns? */ if (nae>0) eat( (nae+1)/2 ); /* az el & vertex index? */ return(-1); } /* Allocate storage for plane equations */ eqn = (plane_t *)bu_malloc(nface*sizeof(plane_t), "eqn"); /* Allocate storage for per-plane use count */ used = (int *)bu_malloc(nface*sizeof(int), "used"); if ( npt >= 1 ) { /* Obtain vertex input_points */ input_points = (double *)bu_malloc(npt*3*sizeof(double), "input_points"); if ( getxsoldata( input_points, npt*3, sol_work ) < 0 ) goto bad; } /* Get planes defined by three points, 6 per card */ for ( i=0; i<npe; i += 6 ) { if ( get_line( scard, sizeof(scard), "arbn vertex point indices" ) == EOF ) { printf("too few cards for arbn %d\n", sol_work); return(-1); } for ( j=0; j<6; j++ ) { int q, r, s; point_t a, b, c; q = getint( scard, 10+j*10+0, 4 ); r = getint( scard, 10+j*10+4, 3 ); s = getint( scard, 10+j*10+7, 3 ); if ( q == 0 || r == 0 || s == 0 ) continue; if ( q < 0 ) { VMOVE( a, &input_points[((-q)-1)*3] ); a[Y] = -a[Y]; symm = 1; } else { VMOVE( a, &input_points[((q)-1)*3] ); } if ( r < 0 ) { VMOVE( b, &input_points[((-r)-1)*3] ); b[Y] = -b[Y]; symm = 1; } else { VMOVE( b, &input_points[((r)-1)*3] ); } if ( s < 0 ) { VMOVE( c, &input_points[((-s)-1)*3] ); c[Y] = -c[Y]; symm = 1; } else { VMOVE( c, &input_points[((s)-1)*3] ); } if ( bn_mk_plane_3pts( eqn[cur_eq], a, b, c, &tol ) < 0 ) { printf("arbn degenerate plane\n"); VPRINT("a", a); VPRINT("b", b); VPRINT("c", c); continue; } cur_eq++; } } /* Get planes defined by their equation */ for ( i=0; i < neq; i++ ) { register double scale; if ( get_line( scard, sizeof(scard), "arbn plane equation card" ) == EOF ) { printf("too few cards for arbn %d\n", sol_work); return(-1); } eqn[cur_eq][0] = getdouble( scard, 10+0*10, 10 ); eqn[cur_eq][1] = getdouble( scard, 10+1*10, 10 ); eqn[cur_eq][2] = getdouble( scard, 10+2*10, 10 ); eqn[cur_eq][3] = getdouble( scard, 10+3*10, 10 ); scale = MAGNITUDE(eqn[cur_eq]); if ( scale < SMALL ) { printf("arbn plane normal too small\n"); continue; } scale = 1/scale; VSCALE( eqn[cur_eq], eqn[cur_eq], scale ); eqn[cur_eq][3] *= scale; cur_eq++; } /* Get planes defined by azimuth, elevation, and pt, 2 per card */ for ( i=0; i < nae; i += 2 ) { if ( get_line( scard, sizeof(scard), "arbn az/el card" ) == EOF ) { printf("too few cards for arbn %d\n", sol_work); return(-1); } for ( j=0; j<2; j++ ) { double az, el; int vert_no; double cos_el; point_t pt; az = getdouble( scard, 10+j*30+0*10, 10 ) * bn_degtorad; el = getdouble( scard, 10+j*30+1*10, 10 ) * bn_degtorad; vert_no = getint( scard, 10+j*30+2*10, 10 ); if ( vert_no == 0 ) break; cos_el = cos(el); eqn[cur_eq][X] = cos(az)*cos_el; eqn[cur_eq][Y] = sin(az)*cos_el; eqn[cur_eq][Z] = sin(el); if ( vert_no < 0 ) { VMOVE( pt, &input_points[((-vert_no)-1)*3] ); pt[Y] = -pt[Y]; } else { VMOVE( pt, &input_points[((vert_no)-1)*3] ); } eqn[cur_eq][3] = VDOT(pt, eqn[cur_eq]); cur_eq++; } } if ( nface != cur_eq ) { printf("arbn expected %d faces, got %d\n", nface, cur_eq); return(-1); } /* Average all given points together to find centroid */ /* This is why there must be at least one (two?) point given */ VSETALL(cent, 0); for ( i=0; i<npt; i++ ) { VADD2( cent, cent, &input_points[i*3] ); } VSCALE( cent, cent, 1.0/npt ); if ( symm ) cent[Y] = 0; /* Point normals away from centroid */ for ( i=0; i<nface; i++ ) { double dist; dist = VDOT( eqn[i], cent ) - eqn[i][3]; /* If dist is negative, 'cent' is inside halfspace */ #define DIST_TOL (1.0e-8) #define DIST_TOL_SQ (1.0e-10) if ( dist < -DIST_TOL ) continue; if ( dist > DIST_TOL ) { /* Flip halfspace over */ VREVERSE( eqn[i], eqn[i] ); eqn[i][3] = -eqn[i][3]; } else { /* Centroid lies on this face */ printf("arbn centroid lies on face\n"); return(-1); } } /* Release storage for input points */ bu_free( (char *)input_points, "input_points" ); input_points = (double *)0; /* * ARBN must be convex. Test for concavity. * Byproduct is an enumeration of all the verticies. */ last_vertex = max_vertex = 0; /* Zero face use counts */ for ( i=0; i<nface; i++ ) { used[i] = 0; } for ( i=0; i<nface-2; i++ ) { for ( j=i+1; j<nface-1; j++ ) { double dot; int point_count; /* # points on this line */ /* If normals are parallel, no intersection */ dot = VDOT( eqn[i], eqn[j] ); if ( !NEAR_ZERO( dot, 0.999999 ) ) continue; point_count = 0; for ( k=j+1; k<nface; k++ ) { point_t pt; if ( bn_mkpoint_3planes( pt, eqn[i], eqn[j], eqn[k] ) < 0 ) continue; /* See if point is outside arb */ for ( m=0; m<nface; m++ ) { if ( i==m || j==m || k==m ) continue; if ( VDOT(pt, eqn[m])-eqn[m][3] > DIST_TOL ) goto next_k; } /* See if vertex already was found */ for ( m=0; m<last_vertex; m++ ) { vect_t dist; VSUB2( dist, pt, &vertex[m*3] ); if ( MAGSQ(dist) < DIST_TOL_SQ ) goto next_k; } /* * Add point to vertex array. * If more room needed, realloc. */ if ( last_vertex >= max_vertex ) { if ( max_vertex == 0 ) { max_vertex = 3; vertex = (double *)bu_malloc( max_vertex*3*sizeof(double), "vertex" ); } else { max_vertex *= 10; vertex = (double *)bu_realloc( (char *)vertex, max_vertex*3*sizeof(double), "vertex" ); } } VMOVE( &vertex[last_vertex*3], pt ); last_vertex++; point_count++; /* Increment "face used" counts */ used[i]++; used[j]++; used[k]++; next_k: ; } if ( point_count > 2 ) { printf("arbn: warning, point_count on line=%d\n", point_count); } } } /* If any planes were not used, then arbn is not convex */ for ( i=0; i<nface; i++ ) { if ( used[i] != 0 ) continue; /* face was used */ printf("arbn face %d unused, solid is not convex\n", i); return(-1); } /* Write out the solid ! */ i = mk_arbn( outfp, name, nface, eqn ); if ( input_points ) bu_free( (char *)input_points, "input_points" ); if ( vertex ) bu_free( (char *)vertex, "vertex" ); if ( eqn ) bu_free( (char *)eqn, "eqn" ); if ( used ) bu_free( (char *)used, "used" ); return(i); }
/** * "Tessellate" an ARB into an NMG data structure. * Purely a mechanical transformation of one faceted object * into another. * * Returns - * -1 failure * 0 OK. *r points to nmgregion that holds this tessellation. */ int rt_arbn_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, const struct rt_tess_tol *UNUSED(ttol), const struct bn_tol *tol) { struct rt_arbn_internal *aip; struct shell *s; struct faceuse **fu; /* array of faceuses */ size_t nverts; /* maximum possible number of vertices = neqn!/(3!(neqn-3)! */ size_t point_count = 0; /* actual number of vertices */ size_t face_count = 0; /* actual number of faces built */ size_t i, j, k, l, n; struct arbn_pts *pts; struct arbn_edges *edges; /* A list of edges for each plane eqn (each face) */ size_t *edge_count; /* number of edges for each face */ size_t max_edge_count; /* maximum number of edges for any face */ struct vertex **verts; /* Array of pointers to vertex structs */ struct vertex ***loop_verts; /* Array of pointers to vertex structs to pass to nmg_cmface */ RT_CK_DB_INTERNAL(ip); aip = (struct rt_arbn_internal *)ip->idb_ptr; RT_ARBN_CK_MAGIC(aip); /* Allocate memory for the vertices */ nverts = aip->neqn * (aip->neqn-1) * (aip->neqn-2) / 6; pts = (struct arbn_pts *)bu_calloc(nverts, sizeof(struct arbn_pts), "rt_arbn_tess: pts"); /* Allocate memory for arbn_edges */ edges = (struct arbn_edges *)bu_calloc(aip->neqn*aip->neqn, sizeof(struct arbn_edges) , "rt_arbn_tess: edges"); edge_count = (size_t *)bu_calloc(aip->neqn, sizeof(size_t), "rt_arbn_tess: edge_count"); /* Allocate memory for faceuses */ fu = (struct faceuse **)bu_calloc(aip->neqn, sizeof(struct faceuse *), "rt_arbn_tess: fu"); /* Calculate all vertices */ for (i = 0; i < aip->neqn; i++) { for (j = i + 1; j < aip->neqn; j++) { for (k = j + 1; k < aip->neqn; k++) { int keep_point = 1; if (bn_mkpoint_3planes(pts[point_count].pt, aip->eqn[i], aip->eqn[j], aip->eqn[k])) continue; for (l = 0; l < aip->neqn; l++) { if (l == i || l == j || l == k) continue; if (DIST_PT_PLANE(pts[point_count].pt, aip->eqn[l]) > tol->dist) { keep_point = 0; break; } } if (keep_point) { pts[point_count].plane_no[0] = i; pts[point_count].plane_no[1] = j; pts[point_count].plane_no[2] = k; point_count++; } } } } /* Allocate memory for the NMG vertex pointers */ verts = (struct vertex **)bu_calloc(point_count, sizeof(struct vertex *) , "rt_arbn_tess: verts"); /* Associate points with vertices */ for (i = 0; i < point_count; i++) pts[i].vp = &verts[i]; /* Check for duplicate points */ for (i = 0; i < point_count; i++) { for (j = i + 1; j < point_count; j++) { if (DIST_PT_PT_SQ(pts[i].pt, pts[j].pt) < tol->dist_sq) { /* These two points should point to the same vertex */ pts[j].vp = pts[i].vp; } } } /* Make list of edges for each face */ for (i = 0; i < aip->neqn; i++) { /* look for a point that lies in this face */ for (j = 0; j < point_count; j++) { if (pts[j].plane_no[0] != (int)i && pts[j].plane_no[1] != (int)i && pts[j].plane_no[2] != (int)i) continue; /* look for another point that shares plane "i" and another with this one */ for (k = j + 1; k < point_count; k++) { size_t match = (size_t)-1; size_t pt1, pt2; int duplicate = 0; /* skip points not on plane "i" */ if (pts[k].plane_no[0] != (int)i && pts[k].plane_no[1] != (int)i && pts[k].plane_no[2] != (int)i) continue; for (l = 0; l < 3; l++) { for (n = 0; n < 3; n++) { if (pts[j].plane_no[l] == pts[k].plane_no[n] && pts[j].plane_no[l] != (int)i) { match = pts[j].plane_no[l]; break; } } if (match != (size_t)-1) break; } if (match == (size_t)-1) continue; /* convert equivalent points to lowest point number */ pt1 = j; pt2 = k; for (l = 0; l < pt1; l++) { if (pts[pt1].vp == pts[l].vp) { pt1 = l; break; } } for (l = 0; l < pt2; l++) { if (pts[pt2].vp == pts[l].vp) { pt2 = l; break; } } /* skip null edges */ if (pt1 == pt2) continue; /* check for duplicate edge */ for (l = 0; l < edge_count[i]; l++) { if ((edges[LOC(i, l)].v1_no == (int)pt1 && edges[LOC(i, l)].v2_no == (int)pt2) || (edges[LOC(i, l)].v2_no == (int)pt1 && edges[LOC(i, l)].v1_no == (int)pt2)) { duplicate = 1; break; } } if (duplicate) continue; /* found an edge belonging to faces "i" and "match" */ if (edge_count[i] == aip->neqn) { bu_log("Too many edges found for one face\n"); goto fail; } edges[LOC(i, edge_count[i])].v1_no = pt1; edges[LOC(i, edge_count[i])].v2_no = pt2; edge_count[i]++; } } } /* for each face, sort the list of edges into a loop */ Sort_edges(edges, edge_count, aip); /* Get max number of edges for any face */ max_edge_count = 0; for (i = 0; i < aip->neqn; i++) if (edge_count[i] > max_edge_count) max_edge_count = edge_count[i]; /* Allocate memory for array to pass to nmg_cmface */ loop_verts = (struct vertex ***) bu_calloc(max_edge_count, sizeof(struct vertex **) , "rt_arbn_tess: loop_verts"); *r = nmg_mrsv(m); /* Make region, empty shell, vertex */ s = BU_LIST_FIRST(shell, &(*r)->s_hd); /* Make the faces */ for (i = 0; i < aip->neqn; i++) { int loop_length = 0; for (j = 0; j < edge_count[i]; j++) { /* skip zero length edges */ if (pts[edges[LOC(i, j)].v1_no].vp == pts[edges[LOC(i, j)].v2_no].vp) continue; /* put vertex pointers into loop_verts array */ loop_verts[loop_length] = pts[edges[LOC(i, j)].v2_no].vp; loop_length++; } /* Make the face if there is are least 3 vertices */ if (loop_length > 2) fu[face_count++] = nmg_cmface(s, loop_verts, loop_length); } /* Associate vertex geometry */ for (i = 0; i < point_count; i++) { if (!(*pts[i].vp)) continue; if ((*pts[i].vp)->vg_p) continue; nmg_vertex_gv(*pts[i].vp, pts[i].pt); } bu_free((char *)pts, "rt_arbn_tess: pts"); bu_free((char *)edges, "rt_arbn_tess: edges"); bu_free((char *)edge_count, "rt_arbn_tess: edge_count"); bu_free((char *)verts, "rt_arbn_tess: verts"); bu_free((char *)loop_verts, "rt_arbn_tess: loop_verts"); /* Associate face geometry */ for (i = 0; i < face_count; i++) { if (nmg_fu_planeeqn(fu[i], tol)) { bu_log("Failed to calculate face plane equation\n"); bu_free((char *)fu, "rt_arbn_tess: fu"); nmg_kr(*r); *r = (struct nmgregion *)NULL; return -1; } } bu_free((char *)fu, "rt_arbn_tess: fu"); nmg_fix_normals(s, tol); (void)nmg_mark_edges_real(&s->l.magic); /* Compute "geometry" for region and shell */ nmg_region_a(*r, tol); return 0; fail: bu_free((char *)pts, "rt_arbn_tess: pts"); bu_free((char *)edges, "rt_arbn_tess: edges"); bu_free((char *)edge_count, "rt_arbn_tess: edge_count"); bu_free((char *)verts, "rt_arbn_tess: verts"); return -1; }
/** * Brute force through all possible plane intersections. * Generate all edge lines, then intersect the line with all * the other faces to find the vertices on that line. * If the geometry is correct, there will be no more than two. * While not the fastest strategy, this will produce an accurate * plot without requiring extra bookkeeping. * Note that the vectors will be drawn in no special order. */ int rt_arbn_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct rt_tess_tol *UNUSED(ttol), const struct bn_tol *tol, const struct rt_view_info *UNUSED(info)) { struct rt_arbn_internal *aip; size_t i; size_t j; size_t k; BU_CK_LIST_HEAD(vhead); RT_CK_DB_INTERNAL(ip); aip = (struct rt_arbn_internal *)ip->idb_ptr; RT_ARBN_CK_MAGIC(aip); for (i = 0; i < aip->neqn - 1; i++) { for (j = i + 1; j < aip->neqn; j++) { double dot; int point_count; /* # points on this line */ point_t a, b; /* start and end points */ vect_t dist; VSETALL(a, 0); VSETALL(b, 0); /* If normals are parallel, no intersection */ dot = VDOT(aip->eqn[i], aip->eqn[j]); if (BN_VECT_ARE_PARALLEL(dot, tol)) continue; /* Have an edge line, isect with all other planes */ point_count = 0; for (k = 0; k < aip->neqn; k++) { size_t m; point_t pt; size_t next_k; next_k = 0; if (k == i || k == j) continue; if (bn_mkpoint_3planes(pt, aip->eqn[i], aip->eqn[j], aip->eqn[k]) < 0) continue; /* See if point is outside arb */ for (m = 0; m < aip->neqn; m++) { if (i == m || j == m || k == m) continue; if (VDOT(pt, aip->eqn[m])-aip->eqn[m][3] > tol->dist) { next_k = 1; break; } } if (next_k != 0) continue; if (point_count <= 0) { RT_ADD_VLIST(vhead, pt, BN_VLIST_LINE_MOVE); VMOVE(a, pt); } else if (point_count == 1) { VSUB2(dist, pt, a); if (MAGSQ(dist) < tol->dist_sq) continue; RT_ADD_VLIST(vhead, pt, BN_VLIST_LINE_DRAW); VMOVE(b, pt); } else { VSUB2(dist, pt, a); if (MAGSQ(dist) < tol->dist_sq) continue; VSUB2(dist, pt, b); if (MAGSQ(dist) < tol->dist_sq) continue; bu_log("rt_arbn_plot() error, point_count=%d (>2) on edge %zu/%zu, non-convex\n", point_count+1, i, j); VPRINT(" a", a); VPRINT(" b", b); VPRINT("pt", pt); RT_ADD_VLIST(vhead, pt, BN_VLIST_LINE_DRAW); /* draw it */ } point_count++; } /* Point counts of 1 are (generally) not harmful, * occurring on pyramid peaks and the like. */ } } return 0; }
/** * Returns - * 0 OK * !0 failure */ int rt_arbn_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip) { struct rt_arbn_internal *aip; vect_t work; fastf_t f; size_t i; size_t j; size_t k; int *used = (int *)0; /* plane eqn use count */ const struct bn_tol *tol = &rtip->rti_tol; RT_CK_DB_INTERNAL(ip); aip = (struct rt_arbn_internal *)ip->idb_ptr; RT_ARBN_CK_MAGIC(aip); used = (int *)bu_malloc(aip->neqn*sizeof(int), "arbn used[]"); /* * ARBN must be convex. Test for concavity. * Byproduct is an enumeration of all the vertices, * which are used to make the bounding RPP. No need * to call the bbox routine, as the work must be duplicated * here to count faces. */ /* Zero face use counts * and make sure normal vectors are unit vectors */ for (i = 0; i < aip->neqn; i++) { double normalLen = MAGNITUDE(aip->eqn[i]); double scale; if (ZERO(normalLen)) { bu_log("arbn has zero length normal vector\n"); return 1; } scale = 1.0 / normalLen; HSCALE(aip->eqn[i], aip->eqn[i], scale); used[i] = 0; } for (i = 0; i < aip->neqn-2; i++) { for (j=i+1; j<aip->neqn-1; j++) { double dot; /* If normals are parallel, no intersection */ dot = VDOT(aip->eqn[i], aip->eqn[j]); if (BN_VECT_ARE_PARALLEL(dot, tol)) continue; /* Have an edge line, isect with higher numbered planes */ for (k=j+1; k<aip->neqn; k++) { size_t m; size_t next_k; point_t pt; next_k = 0; if (bn_mkpoint_3planes(pt, aip->eqn[i], aip->eqn[j], aip->eqn[k]) < 0) continue; /* See if point is outside arb */ for (m = 0; m < aip->neqn; m++) { if (i == m || j == m || k == m) continue; if (VDOT(pt, aip->eqn[m])-aip->eqn[m][3] > tol->dist) { next_k = 1; break; } } if (next_k != 0) continue; VMINMAX(stp->st_min, stp->st_max, pt); /* Increment "face used" counts */ used[i]++; used[j]++; used[k]++; } } } /* If any planes were not used, then arbn is not convex */ for (i = 0; i < aip->neqn; i++) { if (used[i] != 0) continue; /* face was used */ bu_log("arbn(%s) face %zu unused, solid is not convex\n", stp->st_name, i); bu_free((char *)used, "arbn used[]"); return -1; /* BAD */ } bu_free((char *)used, "arbn used[]"); stp->st_specific = (void *)aip; ip->idb_ptr = ((void *)0); /* indicate we stole it */ VADD2SCALE(stp->st_center, stp->st_min, stp->st_max, 0.5); VSUB2SCALE(work, stp->st_max, stp->st_min, 0.5); f = work[X]; if (work[Y] > f) f = work[Y]; if (work[Z] > f) f = work[Z]; stp->st_aradius = f; stp->st_bradius = MAGNITUDE(work); return 0; /* OK */ }