Exemple #1
0
/**
 * b u _ v l s _ s t r n c m p
 *
 * Lexicographically compare two vls strings up to n characters.
 * Returns an integer greater than, equal to, or less than 0,
 * according as the string s1 is greater than, equal to, or less than
 * the string s2.
 */
int
bu_vls_strncmp(struct bu_vls *s1, struct bu_vls *s2, size_t n)
{
    BU_CK_VLS(s1);
    BU_CK_VLS(s2);

    if (n <= 0) {
	/* they match at zero chars */
	return 0;
    }

    /* A zero-length VLS is a null string, account for it */
    if ( s1->vls_max == 0 || s1->vls_str == (char *)NULL ) {
	/* s1 is empty */
	/* ensure first-time allocation for null-termination */
	bu_vls_extend(s1, 1);
    }
    if ( s2->vls_max == 0 || s2->vls_str == (char *)NULL ) {
	/* s2 is empty */
	/* ensure first-time allocation for null-termination */
	bu_vls_extend(s2, 1);
    }

    return strncmp(s1->vls_str+s1->vls_offset, s2->vls_str+s2->vls_offset, n);
}
Exemple #2
0
/**
 * b u _ v l s _ r e a d
 *
 * Read the remainder of a UNIX file onto the end of a vls.
 *
 * Returns -
 *	nread	number of characters read
 *	0	if EOF encountered immediately
 *	-1	read error
 */
int
bu_vls_read( struct bu_vls *vp, int fd )
{
    size_t todo;
    int	got;
    int	ret = 0;

    BU_CK_VLS(vp);

    for (;;)  {
	bu_vls_extend( vp, _VLS_ALLOC_READ );
	todo = (size_t)vp->vls_max - vp->vls_len - vp->vls_offset - 1;
	
	bu_semaphore_acquire(BU_SEM_SYSCALL);
	got = read(fd, vp->vls_str+vp->vls_offset+vp->vls_len, todo );
	bu_semaphore_release(BU_SEM_SYSCALL);
	
	if ( got < 0 )  {
	    /* Read error, abandon the read */
	    return -1;
	}
	if (got == 0)
	    break;

	vp->vls_len += got;
	ret += got;
    }

    /* force null termination */
    vp->vls_str[vp->vls_len+vp->vls_offset] = '\0';

    return ret;
}
Exemple #3
0
/**
 * b u _ v l s _ s t r n c p y
 *
 * Empty the vls string, and copy in a regular string, up to N bytes
 * long.
 */
void
bu_vls_strncpy(register struct bu_vls *vp, const char *s, size_t n)
{
    register size_t len;

    BU_CK_VLS(vp);

    if ( s == (const char *)NULL )
	return;

    len = strlen(s);
    if ( len > n )
	len = n;
    if ( len <= 0 )  {
	vp->vls_len = 0; /* ensure string is empty */
	return;
    }

    /* cancel offset before extending */
    vp->vls_offset = 0;
    if ( len+1 >= (size_t)vp->vls_max )
	bu_vls_extend( vp, len+1 );

    memcpy(vp->vls_str, s, len);
    vp->vls_str[len] = '\0'; /* force null termination */
    vp->vls_len = len;
}
Exemple #4
0
/**
 * b u _ v l s _ s t r c p y
 *
 * Empty the vls string, and copy in a regular string.
 */
void
bu_vls_strcpy(register struct bu_vls *vp, const char *s)
{
    register size_t len;

    BU_CK_VLS(vp);

    if ( s == (const char *)NULL )
	return;

    if ( (len = strlen(s)) <= 0 )  {
	vp->vls_len = 0;
	vp->vls_offset = 0;
	if (vp->vls_max > 0)
	    vp->vls_str[0] = '\0';
	return;
    }

    /* cancel offset before extending */
    vp->vls_offset = 0;
    if ( len+1 >= (size_t)vp->vls_max )
	bu_vls_extend( vp, len+1 );

    memcpy(vp->vls_str, s, len+1); /* include null */
    vp->vls_len = len;
}
void
random_hex_or_binary_string(struct bu_vls *v, const hex_bin_enum_t typ, const int nbytes)
{
    const char hex_chars[] = "0123456789abcdef";
    const char bin_chars[] = "01";
    const int nstrchars = (typ & HEX) ? nbytes * HEXCHARS_PER_BYTE : nbytes * BITS_PER_BYTE;
    const char *chars = (typ & HEX) ? hex_chars : bin_chars;
    const int nchars = (typ & HEX) ? sizeof(hex_chars)/sizeof(char) : sizeof(bin_chars)/sizeof(char);
    int i;
    long int seed;

    /* get a random seed from system entropy to seed "random()" */
    seed = bu_get_urandom_number();
    srand(seed);

    bu_vls_trunc(v, 0);
    bu_vls_extend(v, nchars);
    for (i = 0; i < nstrchars; ++i) {
	long int r = rand();
	int n = r ? (int)(r % (nchars - 1)) : 0;
	char c = chars[n];
	bu_vls_putc(v, c);
    }

    if (typ == HEX) {
	bu_vls_prepend(v, "0x");
    } else if (typ == BINARY) {
	bu_vls_prepend(v, "0b");
    }

}
Exemple #6
0
/**
 * b u _ v l s _ v p r i n t f
 *
 * Format a string into a vls.  This version should work on
 * practically any machine, but it serves to highlight the the
 * grossness of the varargs package requiring the size of a parameter
 * to be known at compile time.
 *
 * %s continues to be a regular 'C' string, null terminated.
 * %S is a pointer to a (struct bu_vls *) string.
 *
 * This routine appends to the given vls similar to how vprintf
 * appends to stdout (see bu_vls_vsprintf for overwriting the vls).
 */
void
bu_vls_vprintf(struct bu_vls *vls, const char *fmt, va_list ap)
{
    register const char	*sp; /* start pointer */
    register const char	*ep; /* end pointer */
    register int len;

#define LONGINT  0x001
#define FIELDLEN 0x002
#define SHORTINT 0x003

    int flags;
    int fieldlen=-1;

    char fbuf[64] = {0}; /* % format buffer */
    char buf[BUFSIZ] = {0};

    if (!vls || !fmt || fmt[0] == '\0') {
	/* nothing to print to or from */
	return;
    }

    BU_CK_VLS(vls);

    bu_vls_extend(vls, _VLS_ALLOC_STEP);

    sp = fmt;
    while ( *sp ) {
	/* Initial state:  just printing chars */
	fmt = sp;
	while (*sp != '%' && *sp)
	    sp++;

	if (sp != fmt)
	    bu_vls_strncat(vls, fmt, (size_t)(sp-fmt));

	if (*sp == '\0')
	    break;

	/* Saw a percent sign, find end of fmt specifier */

	flags = 0;
	ep = sp;
	while ( *ep )  {
	    ++ep;
	    if (*ep == ' ' || *ep == '#' || *ep == '-' ||
		*ep == '+' || *ep == '.' || isdigit(*ep))
		continue;
	    else if (*ep == 'l' || *ep == 'U' || *ep == 'O')
		flags |= LONGINT;
	    else if (*ep == '*') {
		fieldlen = va_arg(ap, int);
		flags |= FIELDLEN;
	    } else if (*ep == 'h') {
		flags |= SHORTINT;
	    } else
		/* Anything else must be the end of the fmt specifier */
		break;
	}
Exemple #7
0
/**
 * b u _ v l s _ p u t c
 *
 * Append the given character to the vls.
 */
void
bu_vls_putc(register struct bu_vls *vp, int c)
{
    BU_CK_VLS(vp);

    if ( vp->vls_offset + vp->vls_len+1 >= vp->vls_max )
	bu_vls_extend( vp, _VLS_ALLOC_STEP );

    vp->vls_str[vp->vls_offset + vp->vls_len++] = (char)c;
    vp->vls_str[vp->vls_offset + vp->vls_len] = '\0'; /* force null termination */
}
Exemple #8
0
/**
 * b u _ v l s _ s e t l e n
 *
 * Ensure that the vls has a length of at least 'newlen', and make
 * that the current length.
 *
 * Useful for subroutines that are planning on mucking with the data
 * array themselves.  Not advisable, but occasionally useful.
 *
 * Does not change the offset from the front of the buffer, if any.
 * Does not initialize the value of any of the new bytes.
 */
void
bu_vls_setlen(struct bu_vls *vp, int newlen)
{
    BU_CK_VLS(vp);

    if ( vp->vls_len >= newlen )
	return;

    bu_vls_extend( vp, (unsigned)newlen - vp->vls_len );
    vp->vls_len = newlen;
}
Exemple #9
0
/**
 * b u _ v l s _ s t r c m p
 *
 * Lexicographically compare to vls strings.  Returns an integer
 * greater than, equal to, or less than 0, according as the string s1
 * is greater than, equal to, or less than the string s2.
 */
int
bu_vls_strcmp(struct bu_vls *s1, struct bu_vls *s2)
{
    BU_CK_VLS(s1);
    BU_CK_VLS(s2);

    /* A zero-length VLS is a null string, account for it */
    if ( s1->vls_max == 0 || s1->vls_str == (char *)NULL ) {
	/* s1 is empty */
	/* ensure first-time allocation for null-termination */
	bu_vls_extend(s1, 1);
    }
    if ( s2->vls_max == 0 || s2->vls_str == (char *)NULL ) {
	/* s2 is empty */
	/* ensure first-time allocation for null-termination */
	bu_vls_extend(s2, 1);
    }

    /* neither empty, straight up comparison */
    return strcmp(s1->vls_str+s1->vls_offset, s2->vls_str+s2->vls_offset);
}
Exemple #10
0
/**
 * b u _ v l s _ v l s c a t
 *
 * Concatenate a new vls string onto the end of an existing vls
 * string.  The storage of the source string is not affected.
 */
void
bu_vls_vlscat(register struct bu_vls *dest, register const struct bu_vls *src)
{
    BU_CK_VLS(src);
    BU_CK_VLS(dest);

    if ( src->vls_len <= 0 )
	return;

    if ( dest->vls_offset + dest->vls_len + src->vls_len+1 >= dest->vls_max )
	bu_vls_extend( dest, (unsigned)src->vls_len+1 );

    /* copy source string, including null */
    memcpy(dest->vls_str +dest->vls_offset + dest->vls_len, src->vls_str+src->vls_offset, (size_t)src->vls_len+1);
    dest->vls_len += src->vls_len;
}
Exemple #11
0
/**
 * b u _ v l s _ s t r c a t
 *
 * Concatenate a new string onto the end of the existing vls string.
 */
void
bu_vls_strcat(register struct bu_vls *vp, const char *s)
{
    register size_t len;

    BU_CK_VLS(vp);

    if ( s == (const char *)NULL )
	return;
    if ( (len = strlen(s)) <= 0 )
	return;

    if ( (size_t)vp->vls_offset + (size_t)vp->vls_len + len+1 >= (size_t)vp->vls_max )
	bu_vls_extend( vp, len+1 );

    memcpy(vp->vls_str +vp->vls_offset + vp->vls_len, s, len+1); /* include null */
    vp->vls_len += len;
}
Exemple #12
0
/**
 *	b n _ p r _ p o l y
 *
 * Print out the polynomial.
 */
void
bn_pr_poly(const char *title, register const struct bn_poly *eqn)
{
    register int	n;
    register int	exp;
    struct bu_vls	str;
    char		buf[48];

    bu_vls_init( &str );
    bu_vls_extend( &str, 196 );
    bu_vls_strcat( &str, title );
    snprintf(buf, 48, " polynomial, degree = %d\n", eqn->dgr);
    bu_vls_strcat( &str, buf );

    exp = eqn->dgr;
    for ( n=0; n<=eqn->dgr; n++, exp-- )  {
	register double coeff = eqn->cf[n];
	if ( n > 0 )  {
	    if ( coeff < 0 )  {
		bu_vls_strcat( &str, " - " );
		coeff = -coeff;
	    }  else  {
		bu_vls_strcat( &str, " + " );
	    }
	}
	bu_vls_printf( &str, "%g", coeff );
	if ( exp > 1 )  {
	    bu_vls_printf( &str, " *X^%d", exp );
	} else if ( exp == 1 )  {

	    bu_vls_strcat( &str, " *X" );
	} else {
	    /* For constant term, add nothing */
	}
    }
    bu_vls_strcat( &str, "\n" );
    bu_log( "%s", bu_vls_addr(&str) );
    bu_vls_free( &str );
}
Exemple #13
0
/**
 * b u _ v l s _ s t r n c a t
 *
 * Concatenate a new string onto the end of the existing vls string.
 */
void
bu_vls_strncat(register struct bu_vls *vp, const char *s, size_t n)
{
    register size_t len;

    BU_CK_VLS(vp);

    if ( s == (const char *)NULL )
	return;

    len = strlen(s);
    if ( len > n )
	len = n;
    if ( len <= 0 )
	return;

    if ( (size_t)vp->vls_offset + (size_t)vp->vls_len + len+1 >= (size_t)vp->vls_max )
	bu_vls_extend( vp, len+1 );

    memcpy(vp->vls_str + vp->vls_offset + vp->vls_len, s, len);
    vp->vls_len += len;
    vp->vls_str[vp->vls_offset + vp->vls_len] = '\0'; /* force null termination */
}
Exemple #14
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);
}
/*
The bu_vls_vprintf function aims to adhere to the following
specifications:

  1.  First, follow the POSIX man page at
      "http://www.unix.com/man-page/POSIX/3/printf/" regarding the
      definition of a format specifier.

  2.  Then modify [1] to accommodate a compatible subset of parts
      applicable to a wide range of standard C libraries including
      GNU/Linux, Windows, FreeBSD, and others as differences are
      brought to our attention.

  3.  The subset [2] shall be the "valid" flags, length modifiers, and
      conversion specifiers ("parts") accepted by this function.
      Those are defined in the following local function:

	format_part_status

  4.  Parts known to be defined outside subset [3] shall generate a
      message stating such invalidity and giving a suitable
      alternative if possible (such parts will be called "obsolete");
      otherwise, the part shall be said to be "unsupported."

  5.  Parts seen by this function but not defined above shall be
      deemed "unknown" and result in a suitable message.

  6.  Library users of this function receiving "unknown" messages
      while attempting to use valid parts according to their O/S and
      compiler need to contact the BRL-CAD developers to resolve the
      issue.  Resolution should normally result in assigning the
      "unknown" part to one of the categories described in [4].

*/
void
bu_vls_vprintf(struct bu_vls *vls, const char *fmt, va_list ap)
{
    const char *sp; /* start pointer */
    const char *ep; /* end pointer */
    int len;

    /* flag variables are reset for each fmt specifier */
    vflags_t f;

    char buf[BUFSIZ] = {0};
    int c;

    struct bu_vls fbuf = BU_VLS_INIT_ZERO; /* % format buffer */
    char *fbufp  = NULL;

    if (UNLIKELY(!vls || !fmt || fmt[0] == '\0')) {
	/* nothing to print to or from */
	return;
    }

    BU_CK_VLS(vls);

    bu_vls_extend(vls, (unsigned int)_VLS_ALLOC_STEP);

    sp = fmt;
    while (*sp) {
	/* Initial state:  just printing chars */
	fmt = sp;
	while (*sp != '%' && *sp)
	    sp++;

	if (sp != fmt)
	    bu_vls_strncat(vls, fmt, (size_t)(sp - fmt));

	if (*sp == '\0')
	    break;

	/* Saw a percent sign, now need to find end of fmt specifier */
	/* All flags get reset for this fmt specifier */
	reset_vflags(&f);

	ep = sp;
	while ((c = *(++ep))) {

	    if (c == ' '
		|| c == '#'
		|| c == '+'
		|| c == '\''
		)
	    {
		/* skip */
	    } else if (c == '.') {
		f.have_dot = 1;
	    } else if (isdigit(c)) {
		/* skip */
	    } else if (c == '-') {
		/* the first occurrence before a dot is the
		 left-justify flag, but the occurrence AFTER a dot is
		 taken to be zero precision */
		if (f.have_dot) {
		  f.precision  = 0;
		  f.have_digit = 0;
		} else if (f.have_digit) {
		    /* FIXME: ERROR condition?: invalid format string
		       (e.g., '%7.8-f') */
		    /* seems as if the fprintf man page is indefinite here,
		       looks like the '-' is passed through and
		       appears in output */
		    ;
		} else {
		    f.left_justify = 1;
		}
	    } else if (c == '*') {
		/* the first occurrence is the field width, but the
		   second occurrence is the precision specifier */
		if (!f.have_dot) {
		    f.fieldlen = va_arg(ap, int);
		    f.flags |= FIELDLEN;
		} else {
		    f.precision = va_arg(ap, int);
		    f.flags |= PRECISION;
		}
		/* all length modifiers below here */
	    } else if (format_part_status(c) == (VP_VALID | VP_LENGTH_MOD)) {