Пример #1
0
static int
test_bn_ae_vec(int argc, char *argv[])
{
    fastf_t expected_az, expected_el, actual_az, actual_el;
    vect_t v;

    if (argc != 5) {
	bu_exit(1, "<args> format: V <expected_az> <expected_el> [%s]\n", argv[0]);
    }

    sscanf(argv[2], "%lg,%lg,%lg", &v[0], &v[1], &v[2]);
    sscanf(argv[3], "%lg", &expected_az);
    sscanf(argv[4], "%lg", &expected_el);

    bn_ae_vec(&actual_az, &actual_el, v);
    return !(NEAR_EQUAL(expected_az, actual_az, BN_TOL_DIST) && NEAR_EQUAL(expected_el, actual_el, BN_TOL_DIST));
}
Пример #2
0
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 between 0 and 360
     * change azimuth near 360 to 0 */
    if (NEAR_EQUAL(*az, 360.0, accuracy)) {
	*az = 0.0;
    }

    /* if elevation is +/-90 set twist to zero and calculate azimuth */
    if (NEAR_EQUAL(*el, 90.0, accuracy) || NEAR_ZERO(*el + 90.0, accuracy)) {
	*twist = 0.0;
	*az = bn_atan2(-vec_twist[X], vec_twist[Y]) * RAD2DEG;
    } 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)) * RAD2DEG;

	/* stabilize flutter between +/- 180 */
	if (NEAR_EQUAL(*twist, -180.0, accuracy)) {
	    *twist = 180.0;
	}
    }
}
Пример #3
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);
}
Пример #4
0
int
bn_math_cmd(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
{
    void (*math_func)();
    struct bu_vls result;

    math_func = (void (*)())clientData; /* object-to-function cast */
    bu_vls_init(&result);

    if (math_func == bn_mat_mul) {
	mat_t o, a, b;
	if (argc < 3 || bn_decode_mat(a, argv[1]) < 16 ||
	    bn_decode_mat(b, argv[2]) < 16) {
	    bu_vls_printf(&result, "usage: %s matA matB", argv[0]);
	    goto error;
	}
	bn_mat_mul(o, a, b);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_mat_inv || math_func == bn_mat_trn) {
	mat_t o, a;

	if (argc < 2 || bn_decode_mat(a, argv[1]) < 16) {
	    bu_vls_printf(&result, "usage: %s mat", argv[0]);
	    goto error;
	}
	(*math_func)(o, a);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_matXvec) {
	mat_t m;
	hvect_t i, o;
	if (argc < 3 || bn_decode_mat(m, argv[1]) < 16 ||
	    bn_decode_hvect(i, argv[2]) < 4) {
	    bu_vls_printf(&result, "usage: %s mat hvect", argv[0]);
	    goto error;
	}
	bn_matXvec(o, m, i);
	bn_encode_hvect(&result, o);
    } else if (math_func == bn_mat4x3pnt) {
	mat_t m;
	point_t i, o;
	if (argc < 3 || bn_decode_mat(m, argv[1]) < 16 ||
	    bn_decode_vect(i, argv[2]) < 3) {
	    bu_vls_printf(&result, "usage: %s mat point", argv[0]);
	    goto error;
	}
	bn_mat4x3pnt(o, m, i);
	bn_encode_vect(&result, o);
    } else if (math_func == bn_mat4x3vec) {
	mat_t m;
	vect_t i, o;
	if (argc < 3 || bn_decode_mat(m, argv[1]) < 16 ||
	    bn_decode_vect(i, argv[2]) < 3) {
	    bu_vls_printf(&result, "usage: %s mat vect", argv[0]);
	    goto error;
	}
	bn_mat4x3vec(o, m, i);
	bn_encode_vect(&result, o);
    } else if (math_func == bn_hdivide) {
	hvect_t i;
	vect_t o;
	if (argc < 2 || bn_decode_hvect(i, argv[1]) < 4) {
	    bu_vls_printf(&result, "usage: %s hvect", argv[0]);
	    goto error;
	}
	bn_hdivide(o, i);
	bn_encode_vect(&result, o);
    } else if (math_func == bn_vjoin1) {
	point_t o;
	point_t b, d;
	fastf_t c;

	if (argc < 4) {
	    bu_vls_printf(&result, "usage: %s pnt scale dir", argv[0]);
	    goto error;
	}
	if ( bn_decode_vect(b, argv[1]) < 3) goto error;
	if (Tcl_GetDouble(interp, argv[2], &c) != TCL_OK) goto error;
	if ( bn_decode_vect(d, argv[3]) < 3) goto error;

	VJOIN1( o, b, c, d );	/* bn_vjoin1( o, b, c, d ) */
	bn_encode_vect(&result, o);

    } else if ( math_func == bn_vblend) {
	point_t a, c, e;
	fastf_t b, d;

	if ( argc < 5 ) {
	    bu_vls_printf(&result, "usage: %s scale pnt scale pnt", argv[0]);
	    goto error;
	}

	if ( Tcl_GetDouble(interp, argv[1], &b) != TCL_OK) goto error;
	if ( bn_decode_vect( c, argv[2] ) < 3) goto error;
	if ( Tcl_GetDouble(interp, argv[3], &d) != TCL_OK) goto error;
	if ( bn_decode_vect( e, argv[4] ) < 3) goto error;

	VBLEND2( a, b, c, d, e )
	    bn_encode_vect( &result, a );

    } else if (math_func == bn_mat_ae) {
	mat_t o;
	double az, el;

	if (argc < 3) {
	    bu_vls_printf(&result, "usage: %s azimuth elevation", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[1], &az) != TCL_OK) goto error;
	if (Tcl_GetDouble(interp, argv[2], &el) != TCL_OK) goto error;

	bn_mat_ae(o, (fastf_t)az, (fastf_t)el);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_ae_vec) {
	fastf_t az, el;
	vect_t v;

	if (argc < 2 || bn_decode_vect(v, argv[1]) < 3) {
	    bu_vls_printf(&result, "usage: %s vect", argv[0]);
	    goto error;
	}

	bn_ae_vec(&az, &el, v);
	bu_vls_printf(&result, "%g %g", az, el);
    } else if (math_func == bn_aet_vec) {
	fastf_t az, el, twist, accuracy;
	vect_t vec_ae, vec_twist;

	if (argc < 4 || bn_decode_vect(vec_ae, argv[1]) < 3 ||
	    bn_decode_vect(vec_twist, argv[2]) < 3 ||
	    sscanf(argv[3], "%lf", &accuracy) < 1) {
	    bu_vls_printf(&result, "usage: %s vec_ae vec_twist accuracy",
			  argv[0]);
	    goto error;
	}

	bn_aet_vec(&az, &el, &twist, vec_ae, vec_twist, accuracy);
	bu_vls_printf(&result, "%g %g %g", az, el, twist);
    } else if (math_func == bn_mat_angles) {
	mat_t o;
	double alpha, beta, ggamma;

	if (argc < 4) {
	    bu_vls_printf(&result, "usage: %s alpha beta gamma", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[1], &alpha) != TCL_OK)  goto error;
	if (Tcl_GetDouble(interp, argv[2], &beta) != TCL_OK)   goto error;
	if (Tcl_GetDouble(interp, argv[3], &ggamma) != TCL_OK) goto error;

	bn_mat_angles(o, alpha, beta, ggamma);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_eigen2x2) {
	fastf_t val1, val2;
	vect_t vec1, vec2;
	double a, b, c;

	if (argc < 4) {
	    bu_vls_printf(&result, "usage: %s a b c", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[1], &a) != TCL_OK) goto error;
	if (Tcl_GetDouble(interp, argv[2], &c) != TCL_OK) goto error;
	if (Tcl_GetDouble(interp, argv[3], &b) != TCL_OK) goto error;

	bn_eigen2x2(&val1, &val2, vec1, vec2, (fastf_t)a, (fastf_t)b,
		    (fastf_t)c);
	bu_vls_printf(&result, "%g %g {%g %g %g} {%g %g %g}", val1, val2,
		      V3ARGS(vec1), V3ARGS(vec2));
    } else if (math_func == bn_mat_fromto) {
	mat_t o;
	vect_t from, to;

	if (argc < 3 || bn_decode_vect(from, argv[1]) < 3 ||
	    bn_decode_vect(to, argv[2]) < 3) {
	    bu_vls_printf(&result, "usage: %s vecFrom vecTo", argv[0]);
	    goto error;
	}
	bn_mat_fromto(o, from, to);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_mat_xrot || math_func == bn_mat_yrot ||
	       math_func == bn_mat_zrot) {
	mat_t o;
	double s, c;
	if (argc < 3) {
	    bu_vls_printf(&result, "usage: %s sinAngle cosAngle", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[1], &s) != TCL_OK) goto error;
	if (Tcl_GetDouble(interp, argv[2], &c) != TCL_OK) goto error;

	(*math_func)(o, s, c);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_mat_lookat) {
	mat_t o;
	vect_t dir;
	int yflip;
	if (argc < 3 || bn_decode_vect(dir, argv[1]) < 3) {
	    bu_vls_printf(&result, "usage: %s dir yflip", argv[0]);
	    goto error;
	}
	if (Tcl_GetBoolean(interp, argv[2], &yflip) != TCL_OK) goto error;

	bn_mat_lookat(o, dir, yflip);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_vec_ortho || math_func == bn_vec_perp) {
	vect_t ov, vec;

	if (argc < 2 || bn_decode_vect(vec, argv[1]) < 3) {
	    bu_vls_printf(&result, "usage: %s vec", argv[0]);
	    goto error;
	}

	(*math_func)(ov, vec);
	bn_encode_vect(&result, ov);
    } else if (math_func == bn_mat_scale_about_pt_wrapper) {
	mat_t o;
	vect_t v;
	double scale;
	int status;

	if (argc < 3 || bn_decode_vect(v, argv[1]) < 3) {
	    bu_vls_printf(&result, "usage: %s pt scale", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[2], &scale) != TCL_OK) goto error;

	bn_mat_scale_about_pt_wrapper(&status, o, v, scale);
	if (status != 0) {
	    bu_vls_printf(&result, "error performing calculation");
	    goto error;
	}
	bn_encode_mat(&result, o);
    } else if (math_func == bn_mat_xform_about_pt) {
	mat_t o, xform;
	vect_t v;

	if (argc < 3 || bn_decode_mat(xform, argv[1]) < 16 ||
	    bn_decode_vect(v, argv[2]) < 3) {
	    bu_vls_printf(&result, "usage: %s xform pt", argv[0]);
	    goto error;
	}

	bn_mat_xform_about_pt(o, xform, v);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_mat_arb_rot) {
	mat_t o;
	point_t pt;
	vect_t dir;
	double angle;

	if (argc < 4 || bn_decode_vect(pt, argv[1]) < 3 ||
	    bn_decode_vect(dir, argv[2]) < 3) {
	    bu_vls_printf(&result, "usage: %s pt dir angle", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[3], &angle) != TCL_OK)
	    return TCL_ERROR;

	bn_mat_arb_rot(o, pt, dir, (fastf_t)angle);
	bn_encode_mat(&result, o);
    } else if (math_func == quat_mat2quat) {
	mat_t mat;
	quat_t quat;

	if (argc < 2 || bn_decode_mat(mat, argv[1]) < 16) {
	    bu_vls_printf(&result, "usage: %s mat", argv[0]);
	    goto error;
	}

	quat_mat2quat(quat, mat);
	bn_encode_quat(&result, quat);
    } else if (math_func == quat_quat2mat) {
	mat_t mat;
	quat_t quat;

	if (argc < 2 || bn_decode_quat(quat, argv[1]) < 4) {
	    bu_vls_printf(&result, "usage: %s quat", argv[0]);
	    goto error;
	}

	quat_quat2mat(mat, quat);
	bn_encode_mat(&result, mat);
    } else if (math_func == bn_quat_distance_wrapper) {
	quat_t q1, q2;
	double d;

	if (argc < 3 || bn_decode_quat(q1, argv[1]) < 4 ||
	    bn_decode_quat(q2, argv[2]) < 4) {
	    bu_vls_printf(&result, "usage: %s quatA quatB", argv[0]);
	    goto error;
	}

	bn_quat_distance_wrapper(&d, q1, q2);
	bu_vls_printf(&result, "%g", d);
    } else if (math_func == quat_double || math_func == quat_bisect ||
	       math_func == quat_make_nearest) {
	quat_t oqot, q1, q2;

	if (argc < 3 || bn_decode_quat(q1, argv[1]) < 4 ||
	    bn_decode_quat(q2, argv[2]) < 4) {
	    bu_vls_printf(&result, "usage: %s quatA quatB", argv[0]);
	    goto error;
	}

	(*math_func)(oqot, q1, q2);
	bn_encode_quat(&result, oqot);
    } else if (math_func == quat_slerp) {
	quat_t oq, q1, q2;
	double d;

	if (argc < 4 || bn_decode_quat(q1, argv[1]) < 4 ||
	    bn_decode_quat(q2, argv[2]) < 4) {
	    bu_vls_printf(&result, "usage: %s quat1 quat2 factor", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[3], &d) != TCL_OK) goto error;

	quat_slerp(oq, q1, q2, d);
	bn_encode_quat(&result, oq);
    } else if (math_func == quat_sberp) {
	quat_t oq, q1, qa, qb, q2;
	double d;

	if (argc < 6 || bn_decode_quat(q1, argv[1]) < 4 ||
	    bn_decode_quat(qa, argv[2]) < 4 || bn_decode_quat(qb, argv[3]) < 4 ||
	    bn_decode_quat(q2, argv[4]) < 4) {
	    bu_vls_printf(&result, "usage: %s quat1 quatA quatB quat2 factor",
			  argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[5], &d) != TCL_OK) goto error;

	quat_sberp(oq, q1, qa, qb, q2, d);
	bn_encode_quat(&result, oq);
    } else if (math_func == quat_exp || math_func == quat_log) {
	quat_t qout, qin;

	if (argc < 2 || bn_decode_quat(qin, argv[1]) < 4) {
	    bu_vls_printf(&result, "usage: %s quat", argv[0]);
	    goto error;
	}

	(*math_func)(qout, qin);
	bn_encode_quat(&result, qout);
    } else if (math_func == (void (*)())bn_isect_line3_line3) {
	double t, u;
	point_t pt, a;
	vect_t dir, c;
	int i;
	static const struct bn_tol tol = {
	    BN_TOL_MAGIC, 0.005, 0.005*0.005, 1e-6, 1-1e-6
	};
	if (argc != 5) {
	    bu_vls_printf(&result,
			  "Usage: bn_isect_line3_line3 pt dir pt dir (%d args specified)",
			  argc-1);
	    goto error;
	}

	if (bn_decode_vect(pt, argv[1]) < 3) {
	    bu_vls_printf(&result, "bn_isect_line3_line3 no pt: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(dir, argv[2]) < 3) {
	    bu_vls_printf(&result, "bn_isect_line3_line3 no dir: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(a, argv[3]) < 3) {
	    bu_vls_printf(&result, "bn_isect_line3_line3 no a pt: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(c, argv[4]) < 3) {
	    bu_vls_printf(&result, "bn_isect_line3_line3 no c dir: %s\n", argv[0]);
	    goto error;
	}
	i = bn_isect_line3_line3(&t, &u, pt, dir, a, c, &tol);
	if (i != 1) {
	    bu_vls_printf(&result, "bn_isect_line3_line3 no intersection: %s\n", argv[0]);
	    goto error;
	}

	VJOIN1(a, pt, t, dir);
	bn_encode_vect(&result, a);

    } else if (math_func == (void (*)())bn_isect_line2_line2) {
	double dist[2];
	point_t pt, a;
	vect_t dir, c;
	int i;
	static const struct bn_tol tol = {
	    BN_TOL_MAGIC, 0.005, 0.005*0.005, 1e-6, 1-1e-6
	};

	if (argc != 5) {
	    bu_vls_printf(&result,
			  "Usage: bn_isect_line2_line2 pt dir pt dir (%d args specified)",
			  argc-1);
	    goto error;
	}

	/* i = bn_isect_line2_line2 {0 0} {1 0} {1 1} {0 -1} */

	VSETALL(pt, 0.0);
	VSETALL(dir, 0.0);
	VSETALL(a, 0.0);
	VSETALL(c, 0.0);

	if (bn_decode_vect(pt, argv[1]) < 2) {
	    bu_vls_printf(&result, "bn_isect_line2_line2 no pt: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(dir, argv[2]) < 2) {
	    bu_vls_printf(&result, "bn_isect_line2_line2 no dir: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(a, argv[3]) < 2) {
	    bu_vls_printf(&result, "bn_isect_line2_line2 no a pt: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(c, argv[4]) < 2) {
	    bu_vls_printf(&result, "bn_isect_line2_line2 no c dir: %s\n", argv[0]);
	    goto error;
	}
	i = bn_isect_line2_line2(dist, pt, dir, a, c, &tol);
	if (i != 1) {
	    bu_vls_printf(&result, "bn_isect_line2_line2 no intersection: %s\n", argv[0]);
	    goto error;
	}

	VJOIN1(a, pt, dist[0], dir);
	bu_vls_printf(&result, "%g %g", a[0], a[1]);

    } else {
	bu_vls_printf(&result, "libbn/bn_tcl.c: math function %s not supported yet\n", argv[0]);
	goto error;
    }

    Tcl_AppendResult(interp, bu_vls_addr(&result), (char *)NULL);
    bu_vls_free(&result);
    return TCL_OK;

 error:
    Tcl_AppendResult(interp, bu_vls_addr(&result), (char *)NULL);
    bu_vls_free(&result);
    return TCL_ERROR;
}