Пример #1
0
/**
 * b n _ p o l y _ q u a d r a t i c _ r o o t s
 *@brief
 * Uses the quadratic formula to find the roots (in `complex' form) of
 * any quadratic equation with real coefficients.
 *
 *	@return 1 for success
 *	@return 0 for fail.
 */
int
bn_poly_quadratic_roots(register struct bn_complex *roots, register const struct bn_poly *quadrat)
{
    fastf_t discrim, denom, rad;
    const fastf_t small = SMALL_FASTF;

    if ( NEAR_ZERO( quadrat->cf[0], small ) )  {
	/* root = -cf[2] / cf[1] */
	if ( NEAR_ZERO( quadrat->cf[1], small ) )  {
	    /* No solution.  Now what? */
	    /*	    bu_log("bn_poly_quadratic_roots(): ERROR, no solution\n"); */
	    return 0;
	}
	/* Fake it as a repeated root. */
	roots[0].re = roots[1].re = -quadrat->cf[2]/quadrat->cf[1];
	roots[0].im = roots[1].im = 0.0;
	return 1;	/* OK - repeated root */
    }

    discrim = quadrat->cf[1]*quadrat->cf[1] - 4.0* quadrat->cf[0]*quadrat->cf[2];
    denom = 0.5 / quadrat->cf[0];
#if 0
    bu_log("discrim=%.20f cf0=%.20f cf1=%.20f cf2=%.20f; ", discrim, quadrat->cf[0], quadrat->cf[1], quadrat->cf[2]);
#endif
    if (discrim > 0.0) {
	rad = sqrt( discrim );

	if (NEAR_ZERO(quadrat->cf[1], small)) {
	    double r = fabs(rad * denom);
	    roots[0].re = r;
	    roots[1].re = -r;
	} else {
	    double t, r1, r2;

	    if (quadrat->cf[1] > 0.0) {
		t = -0.5 * (quadrat->cf[1] + rad);
	    } else {
		t = -0.5 * (quadrat->cf[1] - rad);
	    }
	    r1 = t / quadrat->cf[0];
	    r2 = quadrat->cf[2] / t;

	    if (r1 < r2) {
		roots[0].re = r1;
		roots[1].re = r2;
	    } else {
		roots[0].re = r2;
		roots[1].re = r1;
	    }
	}
	roots[1].im = roots[0].im = 0.0;
    } else if (NEAR_ZERO(discrim, small)) {
	roots[1].re = roots[0].re = -quadrat->cf[1] * denom;
	roots[1].im = roots[0].im = 0.0;
    } else {
	roots[1].re = roots[0].re = -quadrat->cf[1] * denom;
	roots[1].im = -(roots[0].im = sqrt( -discrim ) * denom);
    }
    return 1;		/* OK */
}
int
gcv_compute_intervals_isectline(struct face_s *f,
			    fastf_t VV0, fastf_t VV1, fastf_t VV2, fastf_t D0, fastf_t D1, fastf_t D2,
			    fastf_t D0D1, fastf_t D0D2, fastf_t *isect0, fastf_t *isect1,
			    point_t *isectpoint0, point_t *isectpoint1,
			    const struct bn_tol *tol)
{
    if (D0D1>0.0f)
	/* here we know that D0D2<=0.0 */
	/* that is D0, D1 are on the same side, D2 on the other or on the plane */
	gcv_fisect2(f->vert[2], f->vert[0], f->vert[1], VV2, VV0, VV1, D2, D0, D1, isect0, isect1, isectpoint0, isectpoint1);
    else if (D0D2>0.0f)
	/* here we know that d0d1<=0.0 */
	gcv_fisect2(f->vert[1], f->vert[0], f->vert[2], VV1, VV0, VV2, D1, D0, D2, isect0, isect1, isectpoint0, isectpoint1);
    else if (D1*D2>0.0f || !NEAR_ZERO(D0, tol->dist))
	/* here we know that d0d1<=0.0 or that D0!=0.0 */
	gcv_fisect2(f->vert[0], f->vert[1], f->vert[2], VV0, VV1, VV2, D0, D1, D2, isect0, isect1, isectpoint0, isectpoint1);
    else if (!NEAR_ZERO(D1, tol->dist))
	gcv_fisect2(f->vert[1], f->vert[0], f->vert[2], VV1, VV0, VV2, D1, D0, D2, isect0, isect1, isectpoint0, isectpoint1);
    else if (!NEAR_ZERO(D2, tol->dist))
	gcv_fisect2(f->vert[2], f->vert[0], f->vert[1], VV2, VV0, VV1, D2, D0, D1, isect0, isect1, isectpoint0, isectpoint1);
    else
	/* triangles are coplanar */
	return 1;
    return 0;
}
Пример #3
0
/**
 *			B N _ C X _ D I V
 *@brief
 *	Divide one complex by another
 *
 *	bn_cx_div( &a, &b )	divides  a  by  b .  Zero divisor fails.
 *	a and b may coincide.  Result stored in a.
 */
void
bn_cx_div(register bn_complex_t *ap, register const bn_complex_t *bp)
{
    register fastf_t r, s;
    register fastf_t ap__re;

    /* Note: classical formula may cause unnecessary overflow */
    ap__re = ap->re;
    r = bp->re;
    s = bp->im;
    if ( ABS( r ) >= ABS( s ) )  {
	if ( NEAR_ZERO( r, SQRT_SMALL_FASTF ) )
	    goto err;
	r = s / r;			/* <= 1 */
	s = 1.0 / (bp->re + r * s);
	ap->re = (ap->re + ap->im * r) * s;
	ap->im = (ap->im - ap__re * r) * s;
	return;
    }  else  {
	/* ABS( s ) > ABS( r ) */
	if ( NEAR_ZERO( s, SQRT_SMALL_FASTF ) )
	    goto err;
	r = r / s;			/* < 1 */
	s = 1.0 / (s + r * bp->re);
	ap->re = (ap->re * r + ap->im) * s;
	ap->im = (ap->im * r - ap__re) * s;
	return;
    }
 err:
    bu_log("bn_cx_div: division by zero: %gR+%gI / %gR+%gI\n",
	   ap->re, ap->im, bp->re, bp->im );
    ap->re = ap->im = 1.0e20;		/* "INFINITY" */
}
Пример #4
0
int
ir_shootray_octree(struct application *ap)
{
    vect_t	inv_dir;	/* Inverses of ap->a_ray.r_dir	*/
    Octree	*leafp = NULL;	/* Intersected octree leaf.	*/
    inv_dir[X] = inv_dir[Y] = inv_dir[Z] = INFINITY;
    if ( !NEAR_ZERO(ap->a_ray.r_dir[X], SMALL_FASTF) )
	inv_dir[X] = 1.0 / ap->a_ray.r_dir[X];
    if ( !NEAR_ZERO(ap->a_ray.r_dir[Y], SMALL_FASTF) )
	inv_dir[Y] = 1.0 / ap->a_ray.r_dir[Y];
    if ( !NEAR_ZERO(ap->a_ray.r_dir[Z], SMALL_FASTF) )
	inv_dir[Z] = 1.0 / ap->a_ray.r_dir[Z];
    /* Descend octree from root to find the closest intersected leaf node.
       Store minimum hit distance in "a_uvec[0]" field of application
       structure.  Implicitly return the leaf node in "leafp".
    */
    ap->a_uvec[0] = INFINITY; /* Minimum hit point, safe distance.	*/
    hit_octant( ap, &ir_octree, &leafp, inv_dir, 0 );
    if ( leafp != OCTREE_NULL )
	/* Hit model.						*/
	/* a_hit is f_IR_Model(), uses 2nd arg as (Octree *) */
	return	ap->a_hit( ap, (struct partition *)leafp, RT_SEG_NULL );
    else	/* Missed it.						*/
	return	ap->a_miss( ap );
}
Пример #5
0
int compute_intervals_isectline(point_t VERT0, point_t VERT1, point_t VERT2,
				fastf_t VV0, fastf_t VV1, fastf_t VV2, fastf_t D0, fastf_t D1, fastf_t D2,
				fastf_t D0D1, fastf_t D0D2, fastf_t *isect0, fastf_t *isect1,
				point_t isectpoint0, point_t isectpoint1)
{
    if (D0D1>0.0f) {
	/* here we know that D0D2<=0.0 */
	/* that is D0, D1 are on the same side, D2 on the other or on the plane */
	calc_isect2(VERT2, VERT0, VERT1, VV2, VV0, VV1, D2, D0, D1, isect0, isect1, isectpoint0, isectpoint1);
    } else if (D0D2>0.0f) {
	/* here we know that d0d1<=0.0 */
	calc_isect2(VERT1, VERT0, VERT2, VV1, VV0, VV2, D1, D0, D2, isect0, isect1, isectpoint0, isectpoint1);
    } else if (D1*D2>0.0f || !NEAR_ZERO(D0, SMALL_FASTF)) {
	/* here we know that d0d1<=0.0 or that D0!=0.0 */
	calc_isect2(VERT0, VERT1, VERT2, VV0, VV1, VV2, D0, D1, D2, isect0, isect1, isectpoint0, isectpoint1);
    } else if (!NEAR_ZERO(D1, SMALL_FASTF)) {
	calc_isect2(VERT1, VERT0, VERT2, VV1, VV0, VV2, D1, D0, D2, isect0, isect1, isectpoint0, isectpoint1);
    } else if (!NEAR_ZERO(D2, SMALL_FASTF)) {
	calc_isect2(VERT2, VERT0, VERT1, VV2, VV0, VV1, D2, D0, D1, isect0, isect1, isectpoint0, isectpoint1);
    } else {
	/* triangles are coplanar */
	return 1;
    }
    return 0;
}
Пример #6
0
int
rt_nurb_uv_dist(struct edge_g_cnurb *trim, fastf_t u, fastf_t v)
{

    fastf_t dist;
    fastf_t * ptr;
    int coords;
    int rat;
    fastf_t u2, v2;

    ptr = trim->ctl_points;
    coords = RT_NURB_EXTRACT_COORDS(trim->pt_type);
    rat = RT_NURB_IS_PT_RATIONAL(trim->pt_type);

    u2 = 0.0;
    v2 = 0.0;

    if ( rat )
    {
	u2 = ptr[0]/ptr[2] - u; u2 *= u2;
	v2 = ptr[1]/ptr[2] - v; v2 *= v2;
    }
    else
    {
	u2 = ptr[0] - u; u2 *= u2;
	v2 = ptr[1] - v; v2 *= v2;
    }

    dist = sqrt( u2 + v2);
    if ( NEAR_ZERO( dist, 1.0e-4) )
	return TRIM_ON;

    ptr = trim->ctl_points + coords * (trim->c_size -1);

    u2 = 0.0;
    v2 = 0.0;

    if ( rat )
    {
	u2 = ptr[0]/ptr[2] - u; u2 *= u2;
	v2 = ptr[1]/ptr[2] - v; v2 *= v2;
    }
    else
    {
	u2 = ptr[0] - u; u2 *= u2;
	v2 = ptr[1] - v; v2 *= v2;
    }

    dist = sqrt( u2 + v2);
    if ( NEAR_ZERO( dist, 1.0e-4) )
	return TRIM_ON;

    return TRIM_OUT;

}
Пример #7
0
int
bn_mat_is_equal(const mat_t a, const mat_t b, const struct bn_tol *tol)
{
    register int i;
    register double f;
    register double tdist, tperp;

    BN_CK_TOL(tol);

    tdist = tol->dist;
    tperp = tol->perp;

    /* First, check that the translation part of the matrix (dist) are
     * within the distance tolerance.  Because most instancing
     * involves translation and no rotation, doing this first should
     * detect most non-equal cases rapidly.
     */
    for (i = 3; i < 12; i += 4) {
	f = a[i] - b[i];
	if (!NEAR_ZERO(f, tdist)) {
	    return 0;
	}
    }

    /* Check that the rotation part of the matrix (cosines) are within
     * the perpendicular tolerance.
     */
    for (i = 0; i < 16; i += 4) {
	f = a[i] - b[i];
	if (!NEAR_ZERO(f, tperp)) {
	    return 0;
	}
	f = a[i + 1] - b[i + 1];
	if (!NEAR_ZERO(f, tperp)) {
	    return 0;
	}
	f = a[i + 2] - b[i + 2];
	if (!NEAR_ZERO(f, tperp)) {
	    return 0;
	}
    }

    /* Check that the scale part of the matrix (ratio) is within the
     * perpendicular tolerance.  There is no ratio tolerance so we use
     * the tighter of dist or perp.
     */
    f = a[15] - b[15];
    if (!NEAR_ZERO(f, tperp)) {
	return 0;
    }

    return 1;
}
Пример #8
0
/**
 * Create a perspective matrix that transforms the +/1 viewing cube,
 * with the actual eye position (not at Z=+1) specified in viewing
 * coords, into a related space where the eye has been sheared onto
 * the Z axis and repositioned at Z=(0, 0, 1), with the same
 * perspective field of view as before.
 *
 * The Zbuffer clips off stuff with negative Z values.
 *
 * pmat = persp * xlate * shear
 */
void
ged_mike_persp_mat(mat_t pmat,
                   const point_t eye)
{
    mat_t shear;
    mat_t persp;
    mat_t xlate;
    mat_t t1, t2;
    point_t sheared_eye;

    if (eye[Z] <= SMALL) {
        VPRINT("mike_persp_mat(): ERROR, z<0, eye", eye);
        return;
    }

    /* Shear "eye" to +Z axis */
    MAT_IDN(shear);
    shear[2] = -eye[X]/eye[Z];
    shear[6] = -eye[Y]/eye[Z];

    MAT4X3VEC(sheared_eye, shear, eye);
    if (!NEAR_ZERO(sheared_eye[X], .01) || !NEAR_ZERO(sheared_eye[Y], .01)) {
        VPRINT("ERROR sheared_eye", sheared_eye);
        return;
    }

    /* Translate along +Z axis to put sheared_eye at (0, 0, 1). */
    MAT_IDN(xlate);
    /* XXX should I use MAT_DELTAS_VEC_NEG()?  X and Y should be 0 now */
    MAT_DELTAS(xlate, 0, 0, 1-sheared_eye[Z]);

    /* Build perspective matrix inline, substituting fov=2*atan(1, Z) */
    MAT_IDN(persp);
    /* From page 492 of Graphics Gems */
    persp[0] = sheared_eye[Z];	/* scaling: fov aspect term */
    persp[5] = sheared_eye[Z];	/* scaling: determines fov */

    /* From page 158 of Rogers Mathematical Elements */
    /* Z center of projection at Z=+1, r=-1/1 */
    persp[14] = -1;

    bn_mat_mul(t1, xlate, shear);
    bn_mat_mul(t2, persp, t1);

    /* Now, move eye from Z=1 to Z=0, for clipping purposes */
    MAT_DELTAS(xlate, 0, 0, -1);

    bn_mat_mul(pmat, xlate, t2);
}
Пример #9
0
/*
 * R E F R A C T
 *
 * Compute the refracted ray 'v_2' from the incident ray 'v_1' with
 * the refractive indices 'ri_2' and 'ri_1' respectively.
 * Using Schnell's Law:
 *
 * theta_1 = angle of v_1 with surface normal
 * theta_2 = angle of v_2 with reversed surface normal
 * ri_1 * sin(theta_1) = ri_2 * sin(theta_2)
 *
 * sin(theta_2) = ri_1/ri_2 * sin(theta_1)
 *
 * The above condition is undefined for ri_1/ri_2 * sin(theta_1)
 * being greater than 1, and this represents the condition for total
 * reflection, the 'critical angle' is the angle theta_1 for which
 * ri_1/ri_2 * sin(theta_1) equals 1.
 *
 * Returns TRUE if refracted, FALSE if reflected.
 *
 * Note:  output (v_2) can be same storage as an input.
 */
HIDDEN int
rr_refract(vect_t v_1, vect_t norml, double ri_1, double ri_2, vect_t v_2)
{
    vect_t w, u;
    fastf_t beta;

    if (NEAR_ZERO(ri_1, 0.0001) || NEAR_ZERO(ri_2, 0.0001)) {
	bu_log("rr_refract:ri1=%g, ri2=%g\n", ri_1, ri_2);
	beta = 1;
    } else {
	beta = ri_1/ri_2;		/* temp */
	if (beta > 10000) {
	    bu_log("rr_refract:  beta=%g\n", beta);
	    beta = 1000;
	}
    }
    VSCALE(w, v_1, beta);
    VCROSS(u, w, norml);

    /*
     *	|w X norml| = |w||norml| * sin(theta_1)
     *	        |u| = ri_1/ri_2 * sin(theta_1) = sin(theta_2)
     */
    if ((beta = VDOT(u, u)) > 1.0) {
	/* Past critical angle, total reflection.
	 * Calculate reflected (bounced) incident ray.
	 */
	if (R_DEBUG&RDEBUG_REFRACT) bu_log("rr_refract: reflected.  ri1=%g ri2=%g beta=%g\n",
					   ri_1, ri_2, beta);
	VREVERSE(u, v_1);
	beta = 2 * VDOT(u, norml);
	VSCALE(w, norml, beta);
	VSUB2(v_2, w, u);
	return 0;		/* reflected */
    } else {
	/*
	 * 1 - beta = 1 - sin(theta_2)^^2
	 *	    = cos(theta_2)^^2.
	 * beta = -1.0 * cos(theta_2) - Dot(w, norml).
	 */
	if (R_DEBUG&RDEBUG_REFRACT) bu_log("rr_refract: refracted.  ri1=%g ri2=%g beta=%g\n",
					   ri_1, ri_2, beta);
	beta = -sqrt(1.0 - beta) - VDOT(w, norml);
	VSCALE(u, norml, beta);
	VADD2(v_2, w, u);
	return 1;		/* refracted */
    }
    /* NOTREACHED */
}
Пример #10
0
/**
 * Make sure that the lu and fu orientation flags are consistent with
 * the geometric arrangement of the vertices and the faceuse normal.
 */
void
nmg_ck_lu_orientation(struct loopuse *lu, const struct bn_tol *tolp)
{
    struct faceuse *fu;
    plane_t fu_peqn;
    plane_t lu_peqn;
    fastf_t dot;

    NMG_CK_LOOPUSE(lu);
    fu = lu->up.fu_p;		/* parent had better be faceuse */
    NMG_CK_FACEUSE(fu);

    NMG_GET_FU_PLANE(fu_peqn, fu);
    nmg_loop_plane_newell(lu, lu_peqn);

    dot = VDOT(fu_peqn, lu_peqn);

    if (NEAR_ZERO(dot, tolp->perp))
	return;		/* can't determine geometric orientation */


    if (dot < 0.0) {
	bu_log("nmg_ck_lu_orientation() lu=%p, dot=%g, fu_orient=%s, lu_orient=%s\n",
	       (void *)lu, dot,
	       nmg_orientation(fu->orientation),
	       nmg_orientation(lu->orientation)
	    );
	bu_bomb("nmg_ck_lu_orientation() loop orientation flags do not match geometry\n");
    }
}
Пример #11
0
static int
hit_headon(struct application *ap, struct partition *PartHeadp)
{
    register char diff_solid;
    vect_t	diff;
    register fastf_t len;

    if (PartHeadp->pt_forw->pt_forw != PartHeadp)
	Tcl_AppendResult(interp, "hit_headon: multiple partitions\n", (char *)NULL);

    VJOIN1(PartHeadp->pt_forw->pt_inhit->hit_point, ap->a_ray.r_pt,
	   PartHeadp->pt_forw->pt_inhit->hit_dist, ap->a_ray.r_dir);
    VSUB2(diff, PartHeadp->pt_forw->pt_inhit->hit_point, aim_point);

    diff_solid = (FIRST_SOLID(sp) !=
		  PartHeadp->pt_forw->pt_inseg->seg_stp->st_dp);
    len = MAGNITUDE(diff);

    if (	NEAR_ZERO(len, epsilon)
		||
		( diff_solid &&
		  VDOT(diff, ap->a_ray.r_dir) > 0 )
	)
	return(1);
    else
	return(0);
}
Пример #12
0
int
bn_mat_ck(const char *title, const mat_t m)
{
    vect_t A, B, C;
    fastf_t fx, fy, fz;

    if (!m) {
	return 0;    /* implies identity matrix */
    }

    /* Validate that matrix preserves perpendicularity of axis by
     * checking that A.B == 0, B.C == 0, A.C == 0 XXX these vectors
     * should just be grabbed out of the matrix
     */
    VMOVE(A, &m[0]);
    VMOVE(B, &m[4]);
    VMOVE(C, &m[8]);

    fx = VDOT(A, B);
    fy = VDOT(B, C);
    fz = VDOT(A, C);

    /* NOTE: this tolerance cannot be any more tight than 0.00001 due
     * to default calculation tolerancing used by models.  Matrices
     * exported to disk outside of tolerance will fail import if
     * set too restrictive.
     */
    if (!NEAR_ZERO(fx, 0.00001)
	|| !NEAR_ZERO(fy, 0.00001)
	|| !NEAR_ZERO(fz, 0.00001)
	|| NEAR_ZERO(m[15], VDIVIDE_TOL)) {
	if (bu_debug & BU_DEBUG_MATH) {
	    bu_log("bn_mat_ck(%s):  bad matrix, does not preserve axis perpendicularity.\n  X.Y=%g, Y.Z=%g, X.Z=%g, s=%g\n", title, fx, fy, fz, m[15]);
	    bn_mat_print("bn_mat_ck() bad matrix", m);
	}

	if (bu_debug & (BU_DEBUG_MATH | BU_DEBUG_COREDUMP)) {
	    bu_debug |= BU_DEBUG_COREDUMP;
	    bu_bomb("bn_mat_ck() bad matrix\n");
	}
	return -1;	/* FAIL */
    }
    return 0;		/* OK */
}
Пример #13
0
void
ged_view_update(struct ged_view *gvp)
{
    vect_t work, work1;
    vect_t temp, temp1;

    if (!gvp)
        return;

    bn_mat_mul(gvp->gv_model2view,
               gvp->gv_rotation,
               gvp->gv_center);
    gvp->gv_model2view[15] = gvp->gv_scale;
    bn_mat_inv(gvp->gv_view2model, gvp->gv_model2view);

    /* Find current azimuth, elevation, and twist angles */
    VSET(work, 0.0, 0.0, 1.0);       /* view z-direction */
    MAT4X3VEC(temp, gvp->gv_view2model, work);
    VSET(work1, 1.0, 0.0, 0.0);      /* view x-direction */
    MAT4X3VEC(temp1, gvp->gv_view2model, work1);

    /* calculate angles using accuracy of 0.005, since display
     * shows 2 digits right of decimal point */
    bn_aet_vec(&gvp->gv_aet[0],
               &gvp->gv_aet[1],
               &gvp->gv_aet[2],
               temp, temp1, (fastf_t)0.005);

    /* Force azimuth range to be [0, 360] */
    if ((NEAR_EQUAL(gvp->gv_aet[1], 90.0, (fastf_t)0.005) ||
            NEAR_EQUAL(gvp->gv_aet[1], -90.0, (fastf_t)0.005)) &&
            gvp->gv_aet[0] < 0 &&
            !NEAR_ZERO(gvp->gv_aet[0], (fastf_t)0.005))
        gvp->gv_aet[0] += 360.0;
    else if (NEAR_ZERO(gvp->gv_aet[0], (fastf_t)0.005))
        gvp->gv_aet[0] = 0.0;

    /* apply the perspective angle to model2view */
    bn_mat_mul(gvp->gv_pmodel2view, gvp->gv_pmat, gvp->gv_model2view);

    if (gvp->gv_callback)
        (*gvp->gv_callback)(gvp, gvp->gv_clientData);
}
int
rt_gen_elliptical_grid(struct xrays *rays, const struct xray *center_ray, const fastf_t *avec, const fastf_t *bvec, fastf_t gridsize)
{
    register struct xrays *xrayp;
    int count = 0;
    point_t C;
    vect_t dir;
    vect_t a_dir;
    vect_t b_dir;

    fastf_t a = MAGNITUDE(avec);
    fastf_t b = MAGNITUDE(bvec);
    fastf_t x, y;

    int acpr = a / gridsize;
    int bcpr = b / gridsize;

    VMOVE(a_dir, avec);
    VUNITIZE(a_dir);

    VMOVE(b_dir, bvec);
    VUNITIZE(b_dir);

    VMOVE(C, center_ray->r_pt);
    VMOVE(dir, center_ray->r_dir);
    /* make sure avec perpendicular to bvec perpendicular to ray direction */
    BU_ASSERT(NEAR_ZERO(VDOT(avec, bvec), VUNITIZE_TOL));
    BU_ASSERT(NEAR_ZERO(VDOT(avec, dir), VUNITIZE_TOL));

    for (y=gridsize * (-bcpr); y <= b; y=y+gridsize) {
	for (x= gridsize * (-acpr); x <= a; x=x+gridsize) {
	    if (((x*x)/(a*a) + (y*y)/(b*b)) < 1) {
		BU_ALLOC(xrayp, struct xrays);
		VJOIN2(xrayp->ray.r_pt, C, x, a_dir, y, b_dir);
		VMOVE(xrayp->ray.r_dir, dir);
		xrayp->ray.index = count++;
		xrayp->ray.magic = RT_RAY_MAGIC;
		BU_LIST_APPEND(&rays->l, &xrayp->l);
	    }
	}
    }
    return count;
}
Пример #15
0
/*	t e m p _ T o _ R G B ( )
	Map temperatures to spectrum of colors.
	This routine is extracted from the "mandel" program written by
	Douglas A. Gwyn here at BRL, and has been modified slightly
	to suit the input data.
*/
static void
temp_To_RGB(unsigned char *rgb, int temp)
{
    fastf_t		scale = 4.0 / RANGE;
    fastf_t		t = temp;
    fastf_t		hue = 4.0 - ((t < AMBIENT ? AMBIENT :
				      t > HOTTEST ? HOTTEST :
				      t) - AMBIENT) * scale;
    register int	h = (int) hue;	/* integral part	*/
    register int	f = (int)(256.0 * (hue - (fastf_t)h));
    /* fractional part * 256	*/
    if ( NEAR_ZERO(t - ABSOLUTE_ZERO, SMALL_FASTF) )
	rgb[RED] = rgb[GRN] = rgb[BLU] = 0;
    else
	switch ( h )
	{
	    default:	/* 0 */
		rgb[RED] = 255;
		rgb[GRN] = f;
		rgb[BLU] = 0;
		break;
	    case 1:
		rgb[RED] = 255 - f;
		rgb[GRN] = 255;
		rgb[BLU] = 0;
		break;
	    case 2:
		rgb[RED] = 0;
		rgb[GRN] = 255;
		rgb[BLU] = f;
		break;
	    case 3:
		rgb[RED] = 0;
		rgb[GRN] = 255 - f;
		rgb[BLU] = 255;
		break;
	    case 4:
		rgb[RED] = f;
		rgb[GRN] = 0;
		rgb[BLU] = 255;
		break;
/*	case 5:
	rgb[RED] = 255;
	rgb[GRN] = 0;
	rgb[BLU] = 255 - f;
	break;
*/
	}
/*	bu_log( "temp=%d rgb=(%d %d %d)\n", temp, rgb[RED], rgb[GRN], rgb[BLU] );
 */
    return;
}
Пример #16
0
int
get_de_pointers(union tree *tp, struct directory *dp, int de_len,
		int *de_pointers)
{
    RT_CK_TREE(tp);
    RT_CK_DIR(dp);

    switch (tp->tr_op) {
	case OP_UNION:
	case OP_SUBTRACT:
	case OP_INTERSECT:
	    get_de_pointers(tp->tr_b.tb_left, dp, de_len, de_pointers);
	    get_de_pointers(tp->tr_b.tb_right, dp, de_len, de_pointers);
	    break;
	case OP_DB_LEAF: {
	    struct directory *dp_M;

	    dp_M = db_lookup(DBIP, tp->tr_l.tl_name, LOOKUP_NOISY);
	    if (dp_M == RT_DIR_NULL)
		return 1;

	    if (dp_M->d_uses >= 0) {
		bu_log("g-iges: member (%s) in combination (%s) has not been written to iges file\n", dp_M->d_namep, dp->d_namep);
		de_pointers[de_pointer_number++] = 0;
		return 1;
	    }

	    if (tp->tr_l.tl_mat && !bn_mat_is_identity(tp->tr_l.tl_mat)) {
		/* write a solid instance entity for this member
		   with a pointer to the new matrix */

		if (!NEAR_ZERO(tp->tr_l.tl_mat[15] - 1.0, tol.dist)) {
		    /* scale factor is not 1.0, IGES can't handle it.
		       go ahead and write the solid instance anyway,
		       but warn the user twice */
		    bu_log("g-iges WARNING: member (%s) of combination (%s) is scaled, IGES cannot handle this\n", dp_M->d_namep, dp->d_namep);
		    scale_error++;
		}
		de_pointers[de_pointer_number++] = write_solid_instance(-dp_M->d_uses, tp->tr_l.tl_mat, fp_dir, fp_param);
	    } else
		de_pointers[de_pointer_number++] = (-dp_M->d_uses);
	    if (dp_M->d_nref)
		comb_form = 1;
	}
	    break;
	default:
	    bu_log("Unrecognized operator in combination!\n");
	    return 1;
    }
    return 0;
}
Пример #17
0
void
bn_eigen2x2(
    fastf_t *val1,
    fastf_t *val2,
    fastf_t *vec1,
    fastf_t *vec2,
    fastf_t a,
    fastf_t b,
    fastf_t c)
{
    fastf_t d, root;
    fastf_t v1, v2;

    d = 0.5 * (c - a);

    /* Check for diagonal matrix */
    if (NEAR_ZERO(b, 1.0e-10)) {
	/* smaller mag first */
	if (fabs(c) < fabs(a)) {
	    *val1 = c;
	    VSET(vec1, 0.0, 1.0, 0.0);
	    *val2 = a;
	    VSET(vec2, -1.0, 0.0, 0.0);
	} else {
	    *val1 = a;
	    VSET(vec1, 1.0, 0.0, 0.0);
	    *val2 = c;
	    VSET(vec2, 0.0, 1.0, 0.0);
	}
	return;
    }

    root = sqrt(d * d + b * b);
    v1 = 0.5 * (c + a) - root;
    v2 = 0.5 * (c + a) + root;

    /* smaller mag first */
    if (fabs(v1) < fabs(v2)) {
	*val1 = v1;
	*val2 = v2;
	VSET(vec1, b, d - root, 0.0);
    } else {
	*val1 = v2;
	*val2 = v1;
	VSET(vec1, root - d, b, 0.0);
    }
    VUNITIZE(vec1);
    VSET(vec2, -vec1[Y], vec1[X], 0.0);	/* vec1 X vec2 = +Z */
}
Пример #18
0
/**			B N _ A E T _ V E C
 *@brief
 * Find the azimuth, elevation, and twist from two vectors.
 * Vec_ae is in the direction of view (+z in mged view)
 * and vec_twist points to the viewers right (+x in mged view).
 * Accuracy (degrees) is used to stabilze flutter between
 * equivalent extremes of atan2(), and to snap twist to zero
 * when elevation is near +/- 90
 */
void
bn_aet_vec(fastf_t *az, fastf_t *el, fastf_t *twist, fastf_t *vec_ae, fastf_t *vec_twist, fastf_t accuracy)
{
    vect_t zero_twist, ninety_twist;
    vect_t z_dir;

    /* Get az and el as usual */
    bn_ae_vec( az, el, vec_ae );

    /* stabilize fluctuation bewteen 0 and 360
     * change azimuth near 360 to 0 */
    if ( NEAR_ZERO( *az - 360.0, accuracy ) )
	*az = 0.0;

    /* if elevation is +/-90 set twist to zero and calculate azimuth */
    if ( NEAR_ZERO( *el - 90.0, accuracy ) || NEAR_ZERO( *el + 90.0, accuracy ) )
    {
	*twist = 0.0;
	*az = bn_atan2( -vec_twist[X], vec_twist[Y] ) * bn_radtodeg;
    }
    else
    {
	/* Calculate twist from vec_twist */
	VSET( z_dir, 0, 0, 1 );
	VCROSS( zero_twist, z_dir, vec_ae );
	VUNITIZE( zero_twist );
	VCROSS( ninety_twist, vec_ae, zero_twist );
	VUNITIZE( ninety_twist );

	*twist = bn_atan2( VDOT( vec_twist, ninety_twist ), VDOT( vec_twist, zero_twist ) ) * bn_radtodeg;

	/* stabilize flutter between +/- 180 */
	if ( NEAR_ZERO( *twist + 180.0, accuracy ) )
	    *twist = 180.0;
    }
}
Пример #19
0
/*
 * Replica of STEP function:
 *   FUNCTION first_proj_axis()
 */
void
Axis2Placement3D::FirstProjAxis(double *proj,double *zaxis, double *refdir) {
    double z[3] = VINIT_ZERO;
    double v[3] = VINIT_ZERO;
    double TOL = 1e-9;

    if (zaxis == NULL)
	return;

    VMOVE(z,zaxis);
    VUNITIZE(z);
    if (refdir == NULL) {
	double xplus[3]=  {1.0,0.0,0.0};
	double xminus[3]=  {-1.0,0.0,0.0};
	if (!VNEAR_EQUAL(z, xplus, TOL) &&
	    !VNEAR_EQUAL(z, xminus, TOL))  {
	    VSET(v,1.0,0.0,0.0);
	} else {
	    VSET(v,0.0,1.0,0.0);
	}
    } else {
	double cross[3];
	double mag;

	VCROSS(cross, refdir, z);
	mag = MAGNITUDE(cross);
	if (NEAR_ZERO(mag,TOL)) {
	    return;
	} else {
	    VMOVE(v,refdir);
	    VUNITIZE(v);
	}

    }
    double x_vec[3];
    double aproj[3];
    double dot = VDOT(v,z);
    ScalarTimesVector(x_vec, dot, z);
    VectorDifference(aproj,v,x_vec);
    VSCALE(x_vec,z,dot);
    VSUB2(aproj,v, x_vec);
    VUNITIZE(aproj);
    VMOVE(proj,aproj);

    return;
}
extern "C" void
rt_generate_mesh(int **faces, int *num_faces, point_t **points, int *num_pnts,
	struct db_i *dbip, const char *obj, fastf_t delta)
{
    fastf_t d = delta;
    struct bu_ptbl *hit_pnts;
    if (!faces || !num_faces || !points || !num_pnts) return;
    if (!dbip || !obj) return;
    BU_GET(hit_pnts, struct bu_ptbl);
    bu_ptbl_init(hit_pnts, 64, "hit pnts");
    if (NEAR_ZERO(d, SMALL_FASTF)) d = 1;
    if (_rt_generate_points(faces, num_faces, points, num_pnts, hit_pnts, dbip, obj, d)) {
	(*num_faces) = 0;
	(*num_pnts) = 0;
	return;
    }
}
Пример #21
0
int
rt_nurb_c_check(register struct edge_g_cnurb *crv)
{
    register fastf_t *mp;	/* Mesh pointer */
    register int i;

    mp = crv->ctl_points;
    i = crv->c_size *
	crv->pt_type;
    for (; i > 0; i--, mp++) {
	/* Sanity checking */
	if (!NEAR_ZERO(*mp, INFINITY)) {
	    bu_log("nurb_c_check:  bad mesh found\n");
	    return -1;	/* BAD */
	}
    }
    return 0;		/* OK */
}
/*
 *	Convert an ascii nmg description into a BRL-CAD data base.
 */
static int
ascii_to_brlcad(FILE *fpin, struct rt_wdb *fpout, char *reg_name, char *grp_name)
{
    struct model	*m;
    struct nmgregion	*r;
    struct bn_tol	tol;
    struct shell	*s;
    vect_t		Ext;
    struct faceuse *fu;
    plane_t		pl;

    VSETALL(Ext, 0.);

    m = nmg_mm();		/* Make nmg model. */
    r = nmg_mrsv(m);	/* Make region, empty shell, vertex */
    s = BU_LIST_FIRST(shell, &r->s_hd);
    descr_to_nmg(s, fpin, Ext);	/* Convert ascii description to nmg. */

    /* Copied from proc-db/nmgmodel.c */
    tol.magic = BN_TOL_MAGIC;
    tol.dist = 0.01;
    tol.dist_sq = tol.dist * tol.dist;
    tol.perp = 0.001;
    tol.para = 0.999;

    /* Associate the face geometry. */
    fu = BU_LIST_FIRST( faceuse, &s->fu_hd );
    if (nmg_loop_plane_area(BU_LIST_FIRST(loopuse, &fu->lu_hd), pl) < 0.0)
	return -1;
    else
	nmg_face_g( fu, pl );

    if (!NEAR_ZERO(MAGNITUDE(Ext), 0.001))
	nmg_extrude_face(BU_LIST_FIRST(faceuse, &s->fu_hd), Ext, &tol);

    nmg_region_a(r, &tol);	/* Calculate geometry for region and shell. */

    nmg_fix_normals( s, &tol ); /* insure that faces have outward pointing normals */

    create_brlcad_db(fpout, m, reg_name, grp_name);

    return 0;
}
Пример #23
0
int
rt_nurb_s_check(register struct face_g_snurb *srf)
{
    register fastf_t *mp;	/* Mesh pointer */
    register int i;

    mp = srf->ctl_points;
    i = srf->s_size[RT_NURB_SPLIT_ROW] *
	srf->s_size[RT_NURB_SPLIT_COL] *
	srf->pt_type;
    for (; i > 0; i--, mp++) {
	/* Sanity checking */
	if (!NEAR_ZERO(*mp, INFINITY)) {
	    bu_log("nurb_s_check:  bad mesh found\n");
	    return -1;	/* BAD */
	}
    }
    return 0;		/* OK */
}
Пример #24
0
void
bn_vec_ortho(register vect_t out, register const vect_t in)
{
    register int j, k;
    register fastf_t f;
    register int i;

    if (UNLIKELY(NEAR_ZERO(MAGSQ(in), SQRT_SMALL_FASTF))) {
	bu_log("bn_vec_ortho(): zero magnitude input vector %g %g %g\n", V3ARGS(in));
	VSETALL(out, 0);
	return;
    }

    /* Find component closest to zero */
    f = fabs(in[X]);
    i = X;
    j = Y;
    k = Z;
    if (fabs(in[Y]) < f) {
	f = fabs(in[Y]);
	i = Y;
	j = Z;
	k = X;
    }
    if (fabs(in[Z]) < f) {
	i = Z;
	j = X;
	k = Y;
    }
    f = hypot(in[j], in[k]);
    if (UNLIKELY(ZERO(f))) {
	bu_log("bn_vec_ortho(): zero hypot on %g %g %g\n", V3ARGS(in));
	VSETALL(out, 0);
	return;
    }
    f = 1.0 / f;
    out[i] = 0.0;
    out[j] = -in[k] * f;
    out[k] =  in[j] * f;

    return;
}
Пример #25
0
/*
 *			R A Y H I T
 *
 *  Rayhit() is called by rt_shootray() when the ray hits one or more objects.
 *  A per-shotline header record is written, followed by information about
 *  each object hit.
 *
 *  Note that the GIFT-3 format uses a different convention for the "zero"
 *  distance along the ray.  RT has zero at the ray origin (emanation plain),
 *  while GIFT has zero at the screen plain translated so that it contains
 *  the model origin.  This difference is compensated for by adding the
 *  'dcorrection' distance correction factor.
 *
 *  Also note that the GIFT-3 format requires information about the start
 *  point of the ray in two formats.  First, the h, v coordinates of the
 *  grid cell CENTERS (in screen space coordinates) are needed.
 *  Second, the ACTUAL h, v coordinates fired from are needed.
 *
 *  An optional rtg3.pl UnixPlot file is written, permitting a
 *  color vector display of ray-model intersections.
 */
int
rayhit(struct application *ap, register struct partition *PartHeadp, struct seg *segp)
{
    register struct partition *pp = PartHeadp->pt_forw;
    int 			comp_count;	/* component count */
    fastf_t			dfirst, dlast;	/* ray distances */
    static fastf_t		dcorrection = 0; /* RT to GIFT dist corr */
    int			card_count;	/* # comp. on this card */
    const char		*fmt;		/* printf() format string */
    struct bu_vls		str;
    char			buf[128];	/* temp. sprintf() buffer */
    point_t			hv;		/* GIFT h, v coords, in inches */
    point_t			hvcen;
    int			prev_id=-1;
    point_t			first_hit;
    int			first;

    if ( pp == PartHeadp )
	return(0);		/* nothing was actually hit?? */

    if ( ap->a_rt_i->rti_save_overlaps )
	rt_rebuild_overlaps( PartHeadp, ap, 1 );

    part_compact(ap, PartHeadp, TOL);

    /* count components in partitions */
    comp_count = 0;
    for ( pp=PartHeadp->pt_forw; pp!=PartHeadp; pp=pp->pt_forw )  {
	if ( pp->pt_regionp->reg_regionid > 0 ) {
	    prev_id = pp->pt_regionp->reg_regionid;
	    comp_count++;
	} else if ( prev_id <= 0 ) {
	    /* normally air would be output along with a solid partition, but this will require a '111' partition */
	    prev_id = pp->pt_regionp->reg_regionid;
	    comp_count++;
	} else
	    prev_id = pp->pt_regionp->reg_regionid;
    }
    pp = PartHeadp->pt_back;
    if ( pp!=PartHeadp && pp->pt_regionp->reg_regionid <= 0 )
	comp_count++;  /* a trailing '111' ident */
    if ( comp_count == 0 )
	return( 0 );

    /* Set up variable length string, to buffer this shotline in.
     * Note that there is one component per card, and that each card
     * (line) is 80 characters long.  Hence the parameters given to
     * rt-vls-extend().
     */

    bu_vls_init( &str );
    bu_vls_extend( &str, 80 * (comp_count+1) );

    /*
     *  Find the H, V coordinates of the grid cell center.
     *  RT uses the lower left corner of each cell.
     */
    {
	point_t		center;
	fastf_t		dx;
	fastf_t		dy;

	dx = ap->a_x + 0.5;
	dy = ap->a_y + 0.5;
	VJOIN2( center, viewbase_model, dx, dx_model, dy, dy_model );
	MAT4X3PNT( hvcen, model2hv, center );
    }

    /*
     *  Find exact h, v coordinates of actual ray start by
     *  projecting start point into GIFT h, v coordinates.
     */
    MAT4X3PNT( hv, model2hv, ap->a_ray.r_pt );

    /*
     *  In RT, rays are launched from the plane of the screen,
     *  and ray distances are relative to the start point.
     *  In GIFT-3 output files, ray distances are relative to
     *  the (H, V) plane translated so that it contains the origin.
     *  A distance correction is required to convert between the two.
     *  Since this really should be computed only once, not every time,
     *  the trip_count flag was added.
     */
    {

	static int  trip_count;
	vect_t	tmp;
	vect_t	viewZdir;

	if ( trip_count == 0) {

	    VSET( tmp, 0, 0, -1 );		/* viewing direction */
	    MAT4X3VEC( viewZdir, view2model, tmp );
	    VUNITIZE( viewZdir );
	    /* dcorrection will typically be negative */
	    dcorrection = VDOT( ap->a_ray.r_pt, viewZdir );
	    trip_count = 1;
	}
    }

    /* This code is for diagnostics.
     * bu_log("dcorrection=%g\n", dcorrection);
     */

    /* dfirst and dlast have been made negative to account for GIFT looking
     * in the opposite direction of RT.
     */

    dfirst = -(PartHeadp->pt_forw->pt_inhit->hit_dist + dcorrection);
    dlast = -(PartHeadp->pt_back->pt_outhit->hit_dist + dcorrection);
#if 0
    /* This code is to note any occurances of negative distances. */
    if ( PartHeadp->pt_forw->pt_inhit->hit_dist < 0)  {
	bu_log("ERROR: dfirst=%g at partition x%x\n", dfirst, PartHeadp->pt_forw );
	bu_log("\tdcorrection = %f\n", dcorrection );
	bu_log("\tray start point is ( %f %f %f ) in direction ( %f %f %f )\n", V3ARGS( ap->a_ray.r_pt ), V3ARGS( ap->a_ray.r_dir ) );
	VJOIN1( PartHeadp->pt_forw->pt_inhit->hit_point, ap->a_ray.r_pt, PartHeadp->pt_forw->pt_inhit->hit_dist, ap->a_ray.r_dir );
	VJOIN1( PartHeadp->pt_back->pt_outhit->hit_point, ap->a_ray.r_pt, PartHeadp->pt_forw->pt_outhit->hit_dist, ap->a_ray.r_dir );
	rt_pr_partitions(ap->a_rt_i, PartHeadp, "Defective partion:");
    }
    /* End of bug trap. */
#endif
    /*
     *  Output the ray header.  The GIFT statements that
     *  would have generated this are:
     *  410	write(1, 411) hcen, vcen, h, v, ncomp, dfirst, dlast, a, e
     *  411	format(2f7.1, 2f9.3, i3, 2f8.2,' A', f6.1,' E', f6.1)
     */

#define	SHOT_FMT	"%7.1f%7.1f%9.3f%9.3f%3d%8.2f%8.2f A%6.1f E%6.1f"

    if ( rt_perspective > 0 )  {
	bn_ae_vec( &azimuth, &elevation, ap->a_ray.r_dir );
    }

    bu_vls_printf( &str, SHOT_FMT,
		   hvcen[0], hvcen[1],
		   hv[0], hv[1],
		   comp_count,
		   dfirst * MM2IN, dlast * MM2IN,
		   azimuth, elevation );

    /*
     *  As an aid to debugging, take advantage of the fact that
     *  there are more than 80 columns on UNIX "cards", and
     *  add debugging information to the end of the line to
     *  allow this shotline to be reproduced offline.
     *   -b gives the shotline x, y coordinates when re-running RTG3,
     *   -p and -d are used with RTSHOT
     *  The easy way to activate this is with the harmless -!1 option
     *  when running RTG3.
     */
    if ( R_DEBUG || bu_debug || RT_G_DEBUG )  {
	bu_vls_printf( &str, "   -b%d,%d -p %26.20e %26.20e %26.20e -d %26.20e %26.20e %26.20e\n",
		       ap->a_x, ap->a_y,
		       V3ARGS(ap->a_ray.r_pt),
		       V3ARGS(ap->a_ray.r_dir) );
    } else {
	bu_vls_putc( &str, '\n' );
    }

    /* loop here to deal with individual components */
    card_count = 0;
    prev_id = -1;
    first = 1;
    for ( pp=PartHeadp->pt_forw; pp!=PartHeadp; pp=pp->pt_forw )  {
	/*
	 *  The GIFT statements that would have produced
	 *  this output are:
	 *	do 632 i=icomp, iend
	 *	if (clos(icomp).gt.999.99.or.slos(i).gt.999.9) goto 635
	 * 632	continue
	 * 	write(1, 633)(item(i), clos(i), cangi(i), cango(i),
	 * &			kspac(i), slos(i), i=icomp, iend)
	 * 633	format(1x, 3(i4, f6.2, 2f5.1, i1, f5.1))
	 *	goto 670
	 * 635	write(1, 636)(item(i), clos(i), cangi(i), cango(i),
	 * &			kspac(i), slos(i), i=icomp, iend)
	 * 636	format(1x, 3(i4, f6.1, 2f5.1, i1, f5.0))
	 */
	fastf_t	comp_thickness;	/* component line of sight thickness */
	fastf_t	in_obliq;	/* in obliquity angle */
	fastf_t	out_obliq;	/* out obliquity angle */
	int	region_id;	/* solid region's id */
	int	air_id;		/* air id */
	fastf_t	dot_prod;	/* dot product of normal and ray dir */
	fastf_t	air_thickness;	/* air line of sight thickness */
	vect_t	normal;		/* surface normal */
	register struct partition	*nextpp = pp->pt_forw;

	region_id = pp->pt_regionp->reg_regionid;

	if ( region_id <= 0 && prev_id > 0 )
	{
	    /* air region output with previous partition */
	    prev_id = region_id;
	    continue;
	}
	comp_thickness = pp->pt_outhit->hit_dist -
	    pp->pt_inhit->hit_dist;

	/* The below code is meant to catch components with zero or
	 * negative thicknesses.  This is not supposed to be possible,
	 * but the condition has been seen.
	 */
#if 0
	if ( comp_thickness <= 0 )  {
	    VJOIN1( pp->pt_inhit->hit_point, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir );
	    VJOIN1( pp->pt_outhit->hit_point, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, ap->a_ray.r_dir );
	    bu_log("ERROR: comp_thickness=%g for region id = %d at h=%g, v=%g (x=%d, y=%d), partition at x%x\n",
		   comp_thickness, region_id, hv[0], hv[1], ap->a_x, ap->a_y, pp );
	    rt_pr_partitions(ap->a_rt_i, PartHeadp, "Defective partion:");
	    bu_log("Send this output to the BRL-CAD Developers ([email protected])\n");
	    if ( ! (RT_G_DEBUG & DEBUG_ARB8)) {
		rt_g.debug |= DEBUG_ARB8;
		rt_shootray(ap);
		rt_g.debug &= ~DEBUG_ARB8;
	    }
	}
#endif

	if ( nextpp == PartHeadp )  {
	    if ( region_id <= 0 ) {
		/* last partition is air, need a 111 'phantom armor' before AND after */
		bu_log( "WARNING: adding 'phantom armor' (id=111) with zero thickness before and after air region %s\n",
			pp->pt_regionp->reg_name );
		region_id = 111;
		air_id = pp->pt_regionp->reg_aircode;
		air_thickness = comp_thickness;
		comp_thickness = 0.0;
	    } else {
		/* Last partition, no air follows, use code 9 */
		air_id = 9;
		air_thickness = 0.0;
	    }
	} else if ( region_id <= 0 ) {
	    /* air region, need a 111 'phantom armor' */
	    bu_log( "WARNING: adding 'phantom armor' (id=111) with zero thickness before air region %s\n",
		    pp->pt_regionp->reg_name );
	    prev_id = region_id;
	    region_id = 111;
	    air_id = pp->pt_regionp->reg_aircode;
	    air_thickness = comp_thickness;
	    comp_thickness = 0.0;
	} else if ( nextpp->pt_regionp->reg_regionid <= 0 &&
		    nextpp->pt_regionp->reg_aircode != 0 )  {
	    /* Next partition is air region */
	    air_id = nextpp->pt_regionp->reg_aircode;
	    air_thickness = nextpp->pt_outhit->hit_dist -
		nextpp->pt_inhit->hit_dist;
	    prev_id = air_id;
	} else {
	    /* 2 solid regions, maybe with gap */
	    air_id = 0;
	    air_thickness = nextpp->pt_inhit->hit_dist -
		pp->pt_outhit->hit_dist;
	    if ( air_thickness < 0.0 )
		air_thickness = 0.0;
	    if ( !NEAR_ZERO( air_thickness, 0.1 ) )  {
		air_id = 1;	/* air gap */
		if ( R_DEBUG & RDEBUG_HITS )
		    bu_log("air gap added\n");
	    } else {
		air_thickness = 0.0;
	    }
	    prev_id = region_id;
	}

	/*
	 *  Compute the obliquity angles in degrees, ie,
	 *  the "declension" angle down off the normal vector.
	 *  RT normals always point outwards;
	 *  the "inhit" normal points opposite the ray direction,
	 *  the "outhit" normal points along the ray direction.
	 *  Hence the one sign change.
	 *  XXX this should probably be done with atan2()
	 */

	if ( first ) {
	    first = 0;
	    VJOIN1( first_hit, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir );
	}
    out:
	RT_HIT_NORMAL( normal, pp->pt_inhit, pp->pt_inseg->seg_stp, &(ap->a_ray), pp->pt_inflip );
	dot_prod = VDOT( ap->a_ray.r_dir, normal );
	if ( dot_prod > 1.0 )
	    dot_prod = 1.0;
	if ( dot_prod < -1.0 )
	    dot_prod = (-1.0);

	in_obliq = acos( -dot_prod ) *
	    bn_radtodeg;
	RT_HIT_NORMAL( normal, pp->pt_outhit, pp->pt_outseg->seg_stp, &(ap->a_ray), pp->pt_outflip );
	dot_prod = VDOT( ap->a_ray.r_dir, normal );
	if ( dot_prod > 1.0 )
	    dot_prod = 1.0;
	if ( dot_prod < -1.0 )
	    dot_prod = (-1.0);

	out_obliq = acos( dot_prod ) *
	    bn_radtodeg;

	/* Check for exit obliquties greater than 90 degrees. */
#if 0
	if ( in_obliq > 90 || in_obliq < 0 )  {
	    bu_log("ERROR: in_obliquity=%g\n", in_obliq);
	    rt_pr_partitions(ap->a_rt_i, PartHeadp, "Defective partion:");
	}
	if ( out_obliq > 90 || out_obliq < 0 )  {
	    bu_log("ERROR: out_obliquity=%g\n", out_obliq);
	    VPRINT(" r_dir", ap->a_ray.r_dir);
	    VPRINT("normal", normal);
	    bu_log("dot=%g, acos(dot)=%g\n",
		   VDOT( ap->a_ray.r_dir, normal ),
		   acos( VDOT( ap->a_ray.r_dir, normal ) ) );
	    /* Print the defective one */
	    rt_pr_pt( ap->a_rt_i, pp );
	    /* Print the whole ray's partition list */
	    rt_pr_partitions(ap->a_rt_i, PartHeadp, "Defective partion:");
	}
#endif

	if ( in_obliq > 90.0 )
	    in_obliq = 90.0;
	if ( in_obliq < 0.0 )
	    in_obliq = 0.0;
	if ( out_obliq > 90.0 )
	    out_obliq = 90.0;
	if ( out_obliq < 0.0 )
	    out_obliq = 0.0;

	/*
	 *  Handle 3-components per card output format, with
	 *  a leading space in front of the first component.
	 */
	if ( card_count == 0 )  {
	    bu_vls_strcat( &str, " " );
	}
	comp_thickness *= MM2IN;
	/* Check thickness fields for format overflow */
	if ( comp_thickness > 999.99 || air_thickness*MM2IN > 999.9 )
	    fmt = "%4d%6.1f%5.1f%5.1f%1d%5.0f";
	else
	    fmt = "%4d%6.2f%5.1f%5.1f%1d%5.1f";
#ifdef SPRINTF_NOT_PARALLEL
	bu_semaphore_acquire( BU_SEM_SYSCALL );
#endif
	snprintf(buf, 128, fmt,
		 region_id,
		 comp_thickness,
		 in_obliq, out_obliq,
		 air_id, air_thickness*MM2IN );
#ifdef SPRINTF_NOT_PARALLEL
	bu_semaphore_release( BU_SEM_SYSCALL );
#endif
	bu_vls_strcat( &str, buf );
	card_count++;
	if ( card_count >= 3 )  {
	    bu_vls_strcat( &str, "\n" );
	    card_count = 0;
	}

	/* A color rtg3.pl UnixPlot file of output commands
	 * is generated.  This is processed by plot(1)
	 * plotting filters such as pl-fb or pl-sgi.
	 * Portions of a ray passing through air within the
	 * model are represented in blue, while portions
	 * passing through a solid are assigned green.
	 * This will always be done single CPU,
	 * to prevent output garbling.  (See view_init).
	 */
	if (R_DEBUG & RDEBUG_RAYPLOT) {
	    vect_t     inpt;
	    vect_t     outpt;
	    VJOIN1(inpt, ap->a_ray.r_pt, pp->pt_inhit->hit_dist,
		   ap->a_ray.r_dir);
	    VJOIN1(outpt, ap->a_ray.r_pt, pp->pt_outhit->hit_dist,
		   ap->a_ray.r_dir);
	    pl_color(plotfp, 0, 255, 0);	/* green */
	    pdv_3line(plotfp, inpt, outpt);

	    if (air_thickness > 0) {
		vect_t     air_end;
		VJOIN1(air_end, ap->a_ray.r_pt,
		       pp->pt_outhit->hit_dist + air_thickness,
		       ap->a_ray.r_dir);
		pl_color(plotfp, 0, 0, 255);	/* blue */
		pdv_3cont(plotfp, air_end);
	    }
	}
	if ( nextpp == PartHeadp && air_id != 9 ) {
	    /* need to output a 111 'phantom armor' at end of shotline */
	    air_id = 9;
	    air_thickness = 0.0;
	    region_id = 111;
	    comp_thickness = 0.0;
	    goto out;
	}
    }

    /* If partway through building the line, add a newline */
    if ( card_count > 0 )  {
	/*
	 *  Note that GIFT zero-fills the unused component slots,
	 *  but neither COVART II nor COVART III require it,
	 *  so just end the line here.
	 */
	bu_vls_strcat( &str, "\n" );
    }

    /* Single-thread through file output.
     * COVART will accept non-sequential ray data provided the
     * ray header and its associated data are not separated.  CAVEAT:
     * COVART will not accept headers out of sequence.
     */
    bu_semaphore_acquire( BU_SEM_SYSCALL );

    fputs( bu_vls_addr( &str ), outfp );

    if ( shot_fp )
    {
	fprintf( shot_fp, "%.5f %.5f %.5f %.5f %.5f %.5f %.5f %.5f %ld %.5f %.5f %.5f\n",
		 azimuth, elevation, V3ARGS( ap->a_ray.r_pt ), V3ARGS( ap->a_ray.r_dir ),
		 line_num, V3ARGS( first_hit) );

	line_num +=  1 + (comp_count / 3 );
	if ( comp_count % 3 )
	    line_num++;
    }

    /* End of single-thread region */
    bu_semaphore_release( BU_SEM_SYSCALL );

    /* Release vls storage */
    bu_vls_free( &str );

    return(0);
}
Пример #26
0
/**
 * tests whether a point is inside of the triangle using vector math
 * the point has to be in the same plane as the triangle, otherwise
 * it returns false.
 */
bool PointInTriangle(
    const ON_3dPoint& a,
    const ON_3dPoint& b,
    const ON_3dPoint& c,
    const ON_3dPoint& P,
    double tol
    )
{
    /* First we check to make sure that the point is in the plane */
    double normal[3];
    VCROSS(normal, b - a, c - a);
    VUNITIZE(normal);

    if (!NEAR_ZERO(VDOT(normal, P - a), tol))
	return false;

    /* we have a point that we know is in the plane,
     * but we need to check that it's in the triangle
     * the cleanest way to check this is to check that
     * the crosses of edges and vectors from vertices to P are all parallel or 0
     * The reader could try to prove this if s/he were ambitious
     */
    double v1[3];
    VCROSS(v1, b - a, P - a);
    if (VNEAR_ZERO(v1, tol)) {
	VSETALL(v1, 0.0);
    } else
	VUNITIZE(v1);
    double v2[3];
    VCROSS(v2, c - b, P - b);
    if (VNEAR_ZERO(v2, tol)) {
	VSETALL(v2, 0.0);
    } else
	VUNITIZE(v2);
    double v3[3];
    VCROSS(v3, a - c, P - c);
    if (VNEAR_ZERO(v3, tol)) {
	VSETALL(v3, 0.0);
    } else
	VUNITIZE(v3);

    /* basically we need to check that v1 == v2 == v3, and 0 vectors get in for free
     * if 2 of them are 0 vectors, leaving the final vector with nothing to be equal too
     * then P is in the triangle (this actually means P is a vertex of our triangle)
     * I can't think of any slick way to do this, so it gets kinda ugly
     */

    if (VNEAR_ZERO(v1, tol)) {
	if (VNEAR_ZERO(v2, tol)) {
	    return true;
	} else if (VNEAR_ZERO(v3, tol)) {
	    return true;
	} else if (VNEAR_EQUAL(v2, v3, tol)) {
	    return true;
	} else
	    return false;
    } else if (VNEAR_ZERO(v2, tol)) {
	if (VNEAR_ZERO(v3, tol)) {
	    return true;
	} else if (VNEAR_EQUAL(v1, v3, tol)) {
	    return true;
	} else
	    return false;
    } else if (VNEAR_EQUAL(v1, v2, tol)) {
	if (VNEAR_ZERO(v3, tol)) {
	    return true;
	} else if (VNEAR_EQUAL(v2, v3, tol)) {
	    return true;
	} else
	    return false;
    } else
	return false;
}
Пример #27
0
/**
 * intersects a triangle ABC with a line PQ
 *
 * return values:
 * -1: error
 * 0: no intersection
 * 1: intersects in a point
 * 2: intersects in a line
 */
int SegmentTriangleIntersect(
    const ON_3dPoint& a,
    const ON_3dPoint& b,
    const ON_3dPoint& c,
    const ON_3dPoint& p,
    const ON_3dPoint& q,
    ON_3dPoint out[2],
    double tol
    )
{
    ON_3dPoint triangle[3] = {a, b, c}; /* it'll be nice to have this as an array too*/

    /* First we need to get our plane into point normal form (N \dot (P - P0) = 0)
     * Where N is a normal vector, and P0 is a point in the plane
     * P0 can be any of {a, b, c} so that's easy
     * Finding N
     */

    double normal[3];
    VCROSS(normal, b - a, c - a);
    VUNITIZE(normal);

    ON_3dPoint P0 = a; /* could be b or c*/

    /* Now we've got our plane in a manageable form (two of them actually)
     * So here's the rest of the plan:
     * Every point P on the line can be written as: P = p + u (q - p)
     * We just need to find u
     * We know that when u is correct:
     * normal dot (q + u * (q-p) = N dot P0
     *		   N dot (P0 - p)
     * so u =      --------------
     *		   N dot (q - p)
     */

    if (!NEAR_ZERO(VDOT(normal, (p-q)), tol)) {/* if this is 0 it indicates the line and plane are parallel*/
	double u = VDOT(normal, (P0 - p))/VDOT(normal, (q - p));
	if (u < 0.0 || u > 1.0)	/* this means we're on the line but not the line segment*/
	    return 0;		/* so we can return early*/
	ON_3dPoint P = p + u * (q - p);

	if (PointInTriangle(a, b, c, P, tol)) {
	    out[0] = P;
	    return 1;
	}
	return 0;

    } else {
	/* If we're here it means that the line and plane are parallel*/

	if (NEAR_ZERO(VDOT(normal, p-P0), tol)) {/* yahtzee!!*/
	    /* The line segment is in the same plane as the triangle*/
	    /* So first we check if the points are inside or outside the triangle*/
	    bool p_in = PointInTriangle(a, b, c, p, tol);
	    bool q_in = PointInTriangle(a, b , c , q , tol);
	    ON_3dPoint x[2]; /* a place to put our results*/

	    if (q_in && p_in) {
		out[0] = p;
		out[1] = q;
		return 2;
	    } else if (q_in || p_in) {
		if (q_in)
		    out[0] = q;
		else
		    out[0] = p;

		int i;
		int rv;
		for (i=0; i<3; i++) {
		    rv = SegmentSegmentIntersect(triangle[i], triangle[(i+1)%3], p, q, x, tol);
		    if (rv == 1) {
			out[1] = x[0];
			return 1;
		    } else if (rv == 2) {
			out[0] = x[0];
			out[1] = x[1];
			return 2;
		    }
		}
	    } else {
		/* neither q nor p is in the triangle*/

		int i;
		int points_found = 0;
		int rv;
		for (i = 0; i < 3; i++) {
		    rv = SegmentSegmentIntersect(triangle[i], triangle[(i+1)%3], p, q, x, tol);
		    if (rv == 1) {
			if (points_found == 0 || !VNEAR_EQUAL(out[0], x[0], tol)) { /* in rare cases we can get the same point twice*/
			    out[points_found] = x[0];
			    points_found++;
			}
		    } else if (rv == 2) {
			out[0] = x[0];
			out[1] = x[1];
			return 2;
		    }
		}
		return points_found;
	    }
	} else
	    return 0;
    }
    return -1;
}
Пример #28
0
int
get_args(int argc, register char **argv)
{
    register int c = 0;
    double	d = 0.0;

    while ( (c = bu_getopt( argc, argv, "a:s:m:d:Ae:r:cS:O:M:X:t:" )) != EOF )
    {
	switch ( c )  {
	    case 'a':
		op[ numop ] = ADD;
		val[ numop++ ] = atof(bu_optarg);
		break;
	    case 's':
		op[ numop ] = ADD;
		val[ numop++ ] = - atof(bu_optarg);
		break;
	    case 'm':
		op[ numop ] = MULT;
		val[ numop++ ] = atof(bu_optarg);
		break;
	    case 'd':
		op[ numop ] = MULT;
		d = atof(bu_optarg);

		if ( NEAR_ZERO(d, SMALL_FASTF) ) {
		    bu_exit(2, "bwmod: cannot divide by zero!\n");
		}
		val[ numop++ ] = 1.0 / d;
		break;
	    case 'A':
		op[ numop ] = ABS;
		val[ numop++ ] = 0;
		break;
	    case 'e':
		op[ numop ] = POW;
		val[ numop++ ] = atof(bu_optarg);
		break;
	    case 'r':
		op[ numop ] = POW;
		d = atof(bu_optarg);
		if ( NEAR_ZERO(d, SMALL_FASTF) ) {
		    bu_exit(2, "bwmod: zero root!\n");
		}
		val[ numop++ ] = 1.0 / d;
		break;
	    case 'c':
		char_arith = !char_arith; break;
	    case 'S':
		op[ numop ] = SHIFT;
		val[ numop++] = atof(bu_optarg);
		break;
	    case 'M':
		op[ numop ] = AND;
		val[ numop++] = atof(bu_optarg);
		break;
	    case 'O':
		op[ numop ] = OR;
		val[ numop++ ] = atof(bu_optarg);
		break;
	    case 'X':
		op[ numop ] = XOR;
		val[ numop++ ] = atof(bu_optarg);
		break;
	    case 't':
		op[ numop ] = TRUNC;
		val[ numop++ ] = atof(bu_optarg);
		break;
	    default:		/* '?' */
		return(0);
	}
    }

    if ( bu_optind >= argc )  {
	if ( isatty((int)fileno(stdin)) )
	    return(0);
	file_name = "-";
    } else {
	file_name = argv[bu_optind];
	if (freopen(file_name, "rb", stdin) == NULL) {
	    (void)fprintf( stderr,
			   "bwmod: cannot open \"%s\" for reading\n",
			   file_name );
	    return(0);
	}
    }
    
    if ( argc > ++bu_optind )
	(void)fprintf( stderr, "bwmod: excess argument(s) ignored\n" );
    
    return(1);		/* OK */
}
Пример #29
0
void FindLoops(ON_Brep **b) {
    ON_3dPoint ptmatch, ptterminate, pstart, pend;
    int *curvearray;
    curvearray = static_cast<int*>(bu_malloc((*b)->m_C3.Count() * sizeof(int), "sketch edge list"));
    for (int i = 0; i < (*b)->m_C3.Count(); i++) {
	curvearray[i] = -1;
    }
    ON_SimpleArray<ON_Curve *> allsegments;
    ON_SimpleArray<ON_Curve *> loopsegments;
    int loop_complete;
    for (int i = 0; i < (*b)->m_C3.Count(); i++) {
	allsegments.Append((*b)->m_C3[i]);
    }

    int allcurvesassigned = 0;
    int assignedcount = 0;
    int curvecount = 0;
    int loopcount = 0;
    while (allcurvesassigned != 1) {
	int havefirstcurve = 0;
	while ((havefirstcurve == 0) && (curvecount < allsegments.Count())) {
	    if (curvearray[curvecount] == -1) {
		havefirstcurve = 1;
	    } else {
		curvecount++;
	    }
	}
	// First, sort through things to assign curves to loops.
	loop_complete = 0;
	while ((loop_complete != 1) && (allcurvesassigned != 1)) {
	    curvearray[curvecount] = loopcount;
	    ptmatch = (*b)->m_C3[curvecount]->PointAtEnd();
	    ptterminate = (*b)->m_C3[curvecount]->PointAtStart();
	    for (int i = 0; i < allsegments.Count(); i++) {
		pstart = (*b)->m_C3[i]->PointAtStart();
		pend = (*b)->m_C3[i]->PointAtEnd();
		if (NEAR_ZERO(ptmatch.DistanceTo(pstart), ON_ZERO_TOLERANCE) && (curvearray[i] == -1)) {
		    curvecount = i;
		    ptmatch = pend;
		    i = allsegments.Count();
		    if (NEAR_ZERO(pend.DistanceTo(ptterminate), ON_ZERO_TOLERANCE)) {
			loop_complete = 1;
			loopcount++;
		    }
		} else {
		    if (i == allsegments.Count() - 1) {
			loop_complete = 1; //If we reach this pass, loop had better be complete
			loopcount++;
			assignedcount = 0;
			for (int j = 0; j < allsegments.Count(); j++) {
			    if (curvearray[j] != -1) assignedcount++;
			}
			if (allsegments.Count() == assignedcount) allcurvesassigned = 1;
		    }
		}
	    }
	}
    }

    double maxdist = 0.0;
    int largest_loop_index = 0;
    for (int i = 0; i <= loopcount ; i++) {
	ON_BoundingBox lbbox;
	for (int j = 0; j < (*b)->m_C3.Count(); j++) {
	    if (curvearray[j] == i) {
		ON_Curve *currcurve = (*b)->m_C3[j];
		currcurve->GetBoundingBox(lbbox, true);
	    }
	}
	point_t minpt, maxpt;
	double currdist;
	VSET(minpt, lbbox.m_min[0], lbbox.m_min[1], lbbox.m_min[2]);
	VSET(maxpt, lbbox.m_max[0], lbbox.m_max[1], lbbox.m_max[2]);
	currdist = DIST_PT_PT(minpt, maxpt);
	if (currdist > maxdist) {
	    maxdist = currdist;
	    largest_loop_index = i;
	}
    }


    for (int i = 0; i < allsegments.Count(); i++) {
	if (curvearray[i] == largest_loop_index) loopsegments.Append((*b)->m_C3[i]);
    }

    (*b)->NewPlanarFaceLoop(0, ON_BrepLoop::outer, loopsegments, true);

    loopsegments.Empty();

    // If there's anything left, make inner loops out of it
    for (int i = 0; i <= loopcount; i++) {
	if (i != largest_loop_index) {
	    for (int j = 0; j < allsegments.Count(); j++) {
		if (curvearray[j] == i) loopsegments.Append((*b)->m_C3[j]);
	    }
	    (*b)->NewPlanarFaceLoop(0, ON_BrepLoop::inner, loopsegments, true);
	}
	loopsegments.Empty();
    }

    bu_free(curvearray, "sketch edge list");
}
Пример #30
0
/**
 * R T _ P G _ S H O T
 *
 * Function -
 * Shoot a ray at a polygonal object.
 *
 * Returns -
 * 0 MISS
 * >0 HIT
 */
int
rt_pg_shot(struct soltab *stp, struct xray *rp, struct application *ap, struct seg *seghead)
{
    struct tri_specific *trip =
	(struct tri_specific *)stp->st_specific;
#define MAXHITS 128		/* # surfaces hit, must be even */
    struct hit hits[MAXHITS];
    struct hit *hp;
    size_t nhits;

    nhits = 0;
    hp = &hits[0];

    /* consider each face */
    for (; trip; trip = trip->tri_forw) {
	fastf_t dn;		/* Direction dot Normal */
	fastf_t abs_dn;
	fastf_t k;
	fastf_t alpha, beta;
	vect_t wxb;		/* vertex - ray_start */
	vect_t xp;		/* wxb cross ray_dir */

	/*
	 * Ray Direction dot N.  (N is outward-pointing normal)
	 * wn points inwards, and is not unit length.
	 */
	dn = VDOT(trip->tri_wn, rp->r_dir);

	/*
	 * If ray lies directly along the face, (i.e., dot product
	 * is zero), drop this face.
	 */
	abs_dn = dn >= 0.0 ? dn : (-dn);
	if (abs_dn < SQRT_SMALL_FASTF)
	    continue;
	VSUB2(wxb, trip->tri_A, rp->r_pt);
	VCROSS(xp, wxb, rp->r_dir);

	/* Check for exceeding along the one side */
	alpha = VDOT(trip->tri_CA, xp);
	if (dn < 0.0) alpha = -alpha;
	if (alpha < 0.0 || alpha > abs_dn)
	    continue;

	/* Check for exceeding along the other side */
	beta = VDOT(trip->tri_BA, xp);
	if (dn > 0.0) beta = -beta;
	if (beta < 0.0 || beta > abs_dn)
	    continue;
	if (alpha+beta > abs_dn)
	    continue;
	k = VDOT(wxb, trip->tri_wn) / dn;

	/* For hits other than the first one, might check
	 * to see it this is approx. equal to previous one */

	/* If dn < 0, we should be entering the solid.
	 * However, we just assume in/out sorting later will work.
	 * Really should mark and check this!
	 */
	VJOIN1(hp->hit_point, rp->r_pt, k, rp->r_dir);

	/* HIT is within planar face */
	hp->hit_magic = RT_HIT_MAGIC;
	hp->hit_dist = k;
	VMOVE(hp->hit_normal, trip->tri_N);
	hp->hit_surfno = trip->tri_surfno;
	if (++nhits >= MAXHITS) {
	    bu_log("rt_pg_shot(%s): too many hits (%zu)\n", stp->st_name, nhits);
	    break;
	}
	hp++;
    }
    if (nhits == 0)
	return 0;		/* MISS */

    /* Sort hits, Near to Far */
    rt_hitsort(hits, nhits);

    /* Remove duplicate hits.
       We remove one of a pair of hits when they are
       1) close together, and
       2) both "entry" or both "exit" occurrences.
       Two immediate "entry" or two immediate "exit" hits suggest
       that we hit both of two joined faces, while we want to hit only
       one.  An "entry" followed by an "exit" (or vice versa) suggests
       that we grazed an edge, and thus we should leave both
       in the hit list. */

    {
	size_t i, j;

	for (i=0; i<nhits-1; i++) {
	    fastf_t dist;

	    dist = hits[i].hit_dist - hits[i+1].hit_dist;
	    if (NEAR_ZERO(dist, ap->a_rt_i->rti_tol.dist) &&
		VDOT(hits[i].hit_normal, rp->r_dir) *
		VDOT(hits[i+1].hit_normal, rp->r_dir) > 0)
	    {
		for (j=i; j<nhits-1; j++)
		    hits[j] = hits[j+1];
		nhits--;
		i--;
	    }
	}
    }


    if (nhits == 1)
	nhits = 0;

    if (nhits&1) {
	size_t i;
	static int nerrors = 0;		/* message counter */
	/*
	 * If this condition exists, it is almost certainly due to
	 * the dn==0 check above.  Thus, we will make the last
	 * surface rather thin.
	 * This at least makes the
	 * presence of this solid known.  There may be something
	 * better we can do.
	 */

	if (nerrors++ < 6) {
	    bu_log("rt_pg_shot(%s): WARNING %zu hits:\n", stp->st_name, nhits);
	    bu_log("\tray start = (%g %g %g) ray dir = (%g %g %g)\n",
		   V3ARGS(rp->r_pt), V3ARGS(rp->r_dir));
	    for (i=0; i < nhits; i++) {
		point_t tmp_pt;

		VJOIN1(tmp_pt, rp->r_pt, hits[i].hit_dist, rp->r_dir);
		if (VDOT(rp->r_dir, hits[i].hit_normal) < 0.0)
		    bu_log("\tentrance at dist=%f (%g %g %g)\n", hits[i].hit_dist, V3ARGS(tmp_pt));
		else
		    bu_log("\texit at dist=%f (%g %g %g)\n", hits[i].hit_dist, V3ARGS(tmp_pt));
	    }
	}

	if (nhits > 2) {
	    fastf_t dot1, dot2;
	    size_t j;

	    /* likely an extra hit,
	     * look for consecutive entrances or exits */

	    dot2 = 1.0;
	    i = 0;
	    while (i<nhits) {
		dot1 = dot2;
		dot2 = VDOT(rp->r_dir, hits[i].hit_normal);
		if (dot1 > 0.0 && dot2 > 0.0) {
		    /* two consecutive exits,
		     * manufacture an entrance at same distance
		     * as second exit.
		     */
		    for (j=nhits; j>i; j--)
			hits[j] = hits[j-1];	/* struct copy */

		    VREVERSE(hits[i].hit_normal, hits[i].hit_normal);
		    dot2 = VDOT(rp->r_dir, hits[i].hit_normal);
		    nhits++;
		    bu_log("\t\tadding fictitious entry at %f (%s)\n", hits[i].hit_dist, stp->st_name);
		} else if (dot1 < 0.0 && dot2 < 0.0) {
		    /* two consecutive entrances,
		     * manufacture an exit between them.
		     */

		    for (j=nhits; j>i; j--)
			hits[j] = hits[j-1];	/* struct copy */

		    hits[i] = hits[i-1];	/* struct copy */
		    VREVERSE(hits[i].hit_normal, hits[i-1].hit_normal);
		    dot2 = VDOT(rp->r_dir, hits[i].hit_normal);
		    nhits++;
		    bu_log("\t\tadding fictitious exit at %f (%s)\n", hits[i].hit_dist, stp->st_name);
		}
		i++;
	    }

	} else {
	    hits[nhits] = hits[nhits-1];	/* struct copy */
	    VREVERSE(hits[nhits].hit_normal, hits[nhits-1].hit_normal);
	    bu_log("\t\tadding fictitious hit at %f (%s)\n", hits[nhits].hit_dist, stp->st_name);
	    nhits++;
	}
    }

    if (nhits&1) {
	if (nhits < MAXHITS) {
	    hits[nhits] = hits[nhits-1];	/* struct copy */
	    VREVERSE(hits[nhits].hit_normal, hits[nhits-1].hit_normal);
	    bu_log("\t\tadding fictitious hit at %f (%s)\n", hits[nhits].hit_dist, stp->st_name);
	    nhits++;
	} else
	    nhits--;
    }

    /* nhits is even, build segments */
    {
	struct seg *segp;
	size_t i;
	for (i=0; i < nhits; i += 2) {
	    RT_GET_SEG(segp, ap->a_resource);
	    segp->seg_stp = stp;
	    segp->seg_in = hits[i];		/* struct copy */
	    segp->seg_out = hits[i+1];	/* struct copy */
	    BU_LIST_INSERT(&(seghead->l), &(segp->l));
	}
    }
    return nhits;			/* HIT */
}