/** * Make human-readable formatted presentation of this solid. * First line describes type of solid. * Additional lines are indented one tab, and give parameter values. */ int rt_arbn_describe(struct bu_vls *str, const struct rt_db_internal *ip, int verbose, double mm2local) { struct rt_arbn_internal *aip = (struct rt_arbn_internal *)ip->idb_ptr; char buf[256]; size_t i; RT_ARBN_CK_MAGIC(aip); sprintf(buf, "arbn bounded by %lu planes\n", (long unsigned)aip->neqn); bu_vls_strcat(str, buf); if (!verbose) return 0; for (i = 0; i < aip->neqn; i++) { sprintf(buf, "\t%lu: (%g, %g, %g) %g\n", (long unsigned)i, INTCLAMP(aip->eqn[i][X]), /* should have unit length */ INTCLAMP(aip->eqn[i][Y]), INTCLAMP(aip->eqn[i][Z]), INTCLAMP(aip->eqn[i][W] * mm2local)); bu_vls_strcat(str, buf); } return 0; }
/** * Return the "curvature" of the ARB face. * Pick a principle direction orthogonal to normal, and * indicate no curvature. */ void rt_arbn_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp) { struct rt_arbn_internal *arbn = (struct rt_arbn_internal *)stp->st_specific; RT_ARBN_CK_MAGIC(arbn); bn_vec_ortho(cvp->crv_pdir, hitp->hit_normal); cvp->crv_c1 = cvp->crv_c2 = 0; }
int rt_arbn_params(struct pc_pc_set *UNUSED(ps), const struct rt_db_internal *ip) { struct rt_arbn_internal *aip; RT_CK_DB_INTERNAL(ip); aip = (struct rt_arbn_internal *)ip->idb_ptr; RT_ARBN_CK_MAGIC(aip); return 0; /* OK */ }
int rt_arbn_export4(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip) { struct rt_arbn_internal *aip; union record *rec; size_t ngrans; size_t i; /* scaling buffer must be double, not fastf_t */ double *sbuf; double *sp; if (dbip) RT_CK_DBI(dbip); RT_CK_DB_INTERNAL(ip); if (ip->idb_type != ID_ARBN) return -1; aip = (struct rt_arbn_internal *)ip->idb_ptr; RT_ARBN_CK_MAGIC(aip); if (aip->neqn <= 0) return -1; /* * The network format for a double is 8 bytes and there are 4 * doubles per plane equation. */ ngrans = (aip->neqn * 8 * ELEMENTS_PER_PLANE + sizeof(union record)-1) / sizeof(union record); BU_CK_EXTERNAL(ep); ep->ext_nbytes = (ngrans + 1) * sizeof(union record); ep->ext_buf = (uint8_t *)bu_calloc(1, ep->ext_nbytes, "arbn external"); rec = (union record *)ep->ext_buf; rec[0].n.n_id = DBID_ARBN; *(uint32_t *)rec[0].n.n_neqn = htonl(aip->neqn); *(uint32_t *)rec[0].n.n_grans = htonl(ngrans); /* Take the data from the caller, and scale it, into sbuf */ sp = sbuf = (double *)bu_malloc( aip->neqn * sizeof(double) * ELEMENTS_PER_PLANE, "arbn temp"); for (i = 0; i < aip->neqn; i++) { /* Normal is unscaled, should have unit length; d is scaled */ *sp++ = aip->eqn[i][X]; *sp++ = aip->eqn[i][Y]; *sp++ = aip->eqn[i][Z]; *sp++ = aip->eqn[i][W] * local2mm; } bu_cv_htond((unsigned char *)&rec[1], (unsigned char *)sbuf, aip->neqn * ELEMENTS_PER_PLANE); bu_free((char *)sbuf, "arbn temp"); return 0; /* OK */ }
/** * For a hit on a face of an ARB, return the (u, v) coordinates * of the hit point. 0 <= u, v <= 1. * u extends along the arb_U direction defined by B-A, * v extends along the arb_V direction defined by Nx(B-A). */ void rt_arbn_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp) { struct rt_arbn_internal *arbn = (struct rt_arbn_internal *)stp->st_specific; if (ap) RT_CK_APPLICATION(ap); RT_ARBN_CK_MAGIC(arbn); if (hitp) RT_CK_HIT(hitp); if (uvp) { uvp->uv_u = uvp->uv_v = 0; uvp->uv_du = uvp->uv_dv = 0; } }
/** * Free the storage associated with the rt_db_internal version of this solid. */ void rt_arbn_ifree(struct rt_db_internal *ip) { struct rt_arbn_internal *aip; RT_CK_DB_INTERNAL(ip); aip = (struct rt_arbn_internal *)ip->idb_ptr; RT_ARBN_CK_MAGIC(aip); if (aip->neqn > 0) bu_free((char *)aip->eqn, "rt_arbn_internal eqn[]"); bu_free((char *)aip, "rt_arbn_internal"); ip->idb_ptr = (void *)0; /* sanity */ }
/** * 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; }
int rt_arbn_export5(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip) { struct rt_arbn_internal *aip; size_t i; int double_count; int byte_count; /* must be double for export */ double *vec; double *sp; RT_CK_DB_INTERNAL(ip); if (dbip) RT_CK_DBI(dbip); if (ip->idb_type != ID_ARBN) return -1; aip = (struct rt_arbn_internal *)ip->idb_ptr; RT_ARBN_CK_MAGIC(aip); if (aip->neqn <= 0) return -1; double_count = aip->neqn * ELEMENTS_PER_PLANE; byte_count = double_count * SIZEOF_NETWORK_DOUBLE; BU_CK_EXTERNAL(ep); ep->ext_nbytes = SIZEOF_NETWORK_LONG + byte_count; ep->ext_buf = (uint8_t *)bu_malloc(ep->ext_nbytes, "arbn external"); *(uint32_t *)ep->ext_buf = htonl(aip->neqn); /* Take the data from the caller, and scale it, into vec */ sp = vec = (double *)bu_malloc(byte_count, "arbn temp"); for (i = 0; i < aip->neqn; i++) { /* Normal is unscaled, should have unit length; d is scaled */ *sp++ = aip->eqn[i][X]; *sp++ = aip->eqn[i][Y]; *sp++ = aip->eqn[i][Z]; *sp++ = aip->eqn[i][W] * local2mm; } /* Convert from internal (host) to database (network) format */ bu_cv_htond((unsigned char *)ep->ext_buf + SIZEOF_NETWORK_LONG, (unsigned char *)vec, double_count); bu_free((char *)vec, "arbn temp"); return 0; /* OK */ }
void rt_arbn_print(const struct soltab *stp) { size_t i; struct rt_arbn_internal *arbp = (struct rt_arbn_internal *)stp->st_specific; RT_ARBN_CK_MAGIC(arbp); bu_log("arbn bounded by %zu planes\n", arbp->neqn); for (i = 0; i < arbp->neqn; i++) { bu_log("\t%zu: (%g, %g, %g) %g\n", i, INTCLAMP(arbp->eqn[i][X]), /* should have unit length */ INTCLAMP(arbp->eqn[i][Y]), INTCLAMP(arbp->eqn[i][Z]), INTCLAMP(arbp->eqn[i][W])); } }
/** * Routine to format the parameters of an ARBN primitive for "db get" * * Legal requested parameters include: * "N" - number of equations * "P" - list of all the planes * "P#" - the specified plane number (0 based) * no arguments returns everything */ int rt_arbn_get(struct bu_vls *logstr, const struct rt_db_internal *intern, const char *attr) { struct rt_arbn_internal *arbn=(struct rt_arbn_internal *)intern->idb_ptr; size_t i; long val; RT_ARBN_CK_MAGIC(arbn); if (attr == (char *)NULL) { bu_vls_strcpy(logstr, "arbn"); bu_vls_printf(logstr, " N %zu", arbn->neqn); for (i = 0; i < arbn->neqn; i++) { bu_vls_printf(logstr, " P%zu {%.25g %.25g %.25g %.25g}", i, V4ARGS(arbn->eqn[i])); } } else if (BU_STR_EQUAL(attr, "N")) { bu_vls_printf(logstr, "%zu", arbn->neqn); } else if (BU_STR_EQUAL(attr, "P")) { for (i = 0; i < arbn->neqn; i++) { bu_vls_printf(logstr, " P%zu {%.25g %.25g %.25g %.25g}", i, V4ARGS(arbn->eqn[i])); } } else if (attr[0] == 'P') { if (isdigit((int)attr[1]) == 0) { bu_vls_printf(logstr, "ERROR: Illegal plane number\n"); return BRLCAD_ERROR; } val = atol(&attr[1]); if (val < 0 || (size_t)val >= arbn->neqn) { bu_vls_printf(logstr, "ERROR: Illegal plane number [%ld]\n", val); return BRLCAD_ERROR; } i = (size_t)val; bu_vls_printf(logstr, "%.25g %.25g %.25g %.25g", V4ARGS(arbn->eqn[i])); } else { bu_vls_printf(logstr, "ERROR: Unknown attribute, choices are N, P, or P#\n"); return BRLCAD_ERROR; } return BRLCAD_OK; }
/* * 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; }
/** * Given a pointer to an internal GED database object, mirror the * object's values about the given transformation matrix. */ int rt_arbn_mirror(struct rt_db_internal *ip, register const plane_t plane) { struct rt_arbn_internal *arbn; size_t i; mat_t mirmat; mat_t rmat; mat_t temp; vect_t nvec; vect_t xvec; vect_t mirror_dir; point_t mirror_pt; fastf_t ang; static point_t origin = {0.0, 0.0, 0.0}; RT_CK_DB_INTERNAL(ip); arbn = (struct rt_arbn_internal *)ip->idb_ptr; RT_ARBN_CK_MAGIC(arbn); MAT_IDN(mirmat); VMOVE(mirror_dir, plane); VSCALE(mirror_pt, plane, plane[W]); /* Build mirror transform matrix, for those who need it. */ /* First, perform a mirror down the X axis */ mirmat[0] = -1.0; /* Create the rotation matrix */ VSET(xvec, 1, 0, 0); VCROSS(nvec, xvec, mirror_dir); VUNITIZE(nvec); ang = -acos(VDOT(xvec, mirror_dir)); bn_mat_arb_rot(rmat, origin, nvec, ang*2.0); /* Add the rotation to mirmat */ MAT_COPY(temp, mirmat); bn_mat_mul(mirmat, temp, rmat); /* Add the translation to mirmat */ mirmat[3 + X*4] += mirror_pt[X] * mirror_dir[X]; mirmat[3 + Y*4] += mirror_pt[Y] * mirror_dir[Y]; mirmat[3 + Z*4] += mirror_pt[Z] * mirror_dir[Z]; for (i=0; i<arbn->neqn; i++) { point_t orig_pt; point_t pt; vect_t norm; fastf_t factor; /* unitize the plane equation first */ factor = 1.0 / MAGNITUDE(arbn->eqn[i]); VSCALE(arbn->eqn[i], arbn->eqn[i], factor); arbn->eqn[i][W] = arbn->eqn[i][W] * factor; /* Pick a point on the original halfspace */ VSCALE(orig_pt, arbn->eqn[i], arbn->eqn[i][W]); /* Transform the point, and the normal */ MAT4X3VEC(norm, mirmat, arbn->eqn[i]); MAT4X3PNT(pt, mirmat, orig_pt); /* Measure new distance from origin to new point */ VUNITIZE(norm); VMOVE(arbn->eqn[i], norm); arbn->eqn[i][W] = VDOT(pt, norm); } return 0; }
/** * "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; }
/** * Routine to modify an arbn via the "db adjust" command * * Legal parameters are: * "N" - adjust the number of planes (new ones will be zeroed) * "P" - adjust the entire list of planes * "P#" - adjust a specific plane (0 based) * "P+" - add a new plane to the list of planes */ int rt_arbn_adjust(struct bu_vls *logstr, struct rt_db_internal *intern, int argc, const char **argv) { struct rt_arbn_internal *arbn; unsigned char *c; int len; size_t i, j; long val; fastf_t *new_planes; fastf_t *array; RT_CK_DB_INTERNAL(intern); arbn = (struct rt_arbn_internal *)intern->idb_ptr; RT_ARBN_CK_MAGIC(arbn); while (argc >= 2) { if (BU_STR_EQUAL(argv[0], "N")) { val = atol(argv[1]); if (val < 0) { bu_vls_printf(logstr, "ERROR: number of planes [%ld] must be greater than 0\n", val); val = 1; } i = (size_t)val; if (i == arbn->neqn) goto cont; arbn->eqn = (plane_t *)bu_realloc(arbn->eqn, i * sizeof(plane_t), "arbn->eqn"); for (j=arbn->neqn; j<i; j++) { HSETALL(arbn->eqn[j], 0.0); } arbn->neqn = i; } else if (BU_STR_EQUAL(argv[0], "P")) { /* eliminate all the '{' and '}' chars */ c = (unsigned char *)argv[1]; while (*c != '\0') { if (*c == '{' || *c == '}') *c = ' '; c++; } len = 0; (void)tcl_list_to_fastf_array(brlcad_interp, argv[1], &new_planes, &len); if (len%ELEMENTS_PER_PLANE) { bu_vls_printf(logstr, "ERROR: Incorrect number of plane coefficients\n"); if (len) bu_free((char *)new_planes, "new_planes"); return BRLCAD_ERROR; } if (arbn->eqn) bu_free((char *)arbn->eqn, "arbn->eqn"); arbn->eqn = (plane_t *)new_planes; arbn->neqn = (size_t)len / ELEMENTS_PER_PLANE; for (i = 0; i < arbn->neqn; i++) VUNITIZE(arbn->eqn[i]); } else if (argv[0][0] == 'P') { if (argv[0][1] == '+') { i = arbn->neqn; arbn->neqn++; arbn->eqn = (plane_t *)bu_realloc(arbn->eqn, (arbn->neqn) * sizeof(plane_t), "arbn->eqn"); } else if (isdigit((int)argv[0][1])) { i = atoi(&argv[0][1]); } else { bu_vls_printf(logstr, "ERROR: illegal argument, choices are P, P#, P+, or N\n"); return TCL_ERROR; } if (i >= arbn->neqn) { bu_vls_printf(logstr, "ERROR: plane number out of range\n"); return BRLCAD_ERROR; } len = ELEMENTS_PER_PLANE; array = (fastf_t *)&arbn->eqn[i]; if (tcl_list_to_fastf_array(brlcad_interp, argv[1], &array, &len) != ELEMENTS_PER_PLANE) { bu_vls_printf(logstr, "ERROR: incorrect number of coefficients for a plane\n"); return BRLCAD_ERROR; } VUNITIZE(arbn->eqn[i]); } else { bu_vls_printf(logstr, "ERROR: illegal argument, choices are P, P#, P+, or N\n"); return BRLCAD_ERROR; } cont: argc -= 2; argv += 2; } return BRLCAD_OK; }
/** * 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 */ }