Example #1
0
static int
lambda(			/* compute lambda for material */
	OBJREC  *m,
	FVECT  v2,
	FVECT  dv,
	FVECT  lr
)
{
	FVECT  lrXdv, v2Xlr;
	double  dtmp, denom;
	int  i;

	fcross(lrXdv, lr, dv);
	for (i = 0; i < 3; i++)	
		if ((lrXdv[i] > FTINY) | (lrXdv[i] < -FTINY))
			break;
	if (i >= 3)
		return(-1);

	fcross(v2Xlr, v2, lr);

	dtmp = m->oargs.farg[4] / MLAMBDA;
	denom = dtmp + v2Xlr[i]/lrXdv[i] * (m->oargs.farg[3] + dtmp);

	if (denom < FTINY)
		return(-1);

	return(m->oargs.farg[4] / denom);
}
Example #2
0
static void
getacoords_as(		/* set up coordinate system */
	ASHIKDAT  *np
)
{
	MFUNC  *mf;
	int  i;

	mf = getfunc(np->mp, 3, 0x7, 1);
	setfunc(np->mp, np->rp);
	errno = 0;
	for (i = 0; i < 3; i++)
		np->u[i] = evalue(mf->ep[i]);
	if ((errno == EDOM) | (errno == ERANGE)) {
		objerror(np->mp, WARNING, "compute error");
		np->specfl |= SPA_BADU;
		return;
	}
	if (mf->fxp != &unitxf)
		multv3(np->u, np->u, mf->fxp->xfm);
	fcross(np->v, np->pnorm, np->u);
	if (normalize(np->v) == 0.0) {
		objerror(np->mp, WARNING, "illegal orientation vector");
		np->specfl |= SPA_BADU;
		return;
	}
	fcross(np->u, np->v, np->pnorm);
}
Example #3
0
static void
getacoords(		/* set up coordinate system */
	ANISODAT  *np
)
{
	MFUNC  *mf;
	int  i;

	mf = getfunc(np->mp, 3, 0x7, 1);
	setfunc(np->mp, np->rp);
	errno = 0;
	for (i = 0; i < 3; i++)
		np->u[i] = evalue(mf->ep[i]);
	if ((errno == EDOM) | (errno == ERANGE))
		np->u[0] = np->u[1] = np->u[2] = 0.0;
	if (mf->fxp != &unitxf)
		multv3(np->u, np->u, mf->fxp->xfm);
	fcross(np->v, np->pnorm, np->u);
	if (normalize(np->v) == 0.0) {
		if (fabs(np->u_alpha - np->v_alpha) > 0.001)
			objerror(np->mp, WARNING, "illegal orientation vector");
		getperpendicular(np->u, np->pnorm);	/* punting */
		fcross(np->v, np->pnorm, np->u);
		np->u_alpha = np->v_alpha = sqrt( 0.5 *
			(np->u_alpha*np->u_alpha + np->v_alpha*np->v_alpha) );
	} else
		fcross(np->u, np->v, np->pnorm);
}
Example #4
0
/* Compute World->BSDF transform from surface normal and up (Y) vector */
SDError	
SDcompXform(RREAL vMtx[3][3], const FVECT sNrm, const FVECT uVec)
{
	if ((vMtx == NULL) | (sNrm == NULL) | (uVec == NULL))
		return SDEargument;
	VCOPY(vMtx[2], sNrm);
	if (normalize(vMtx[2]) == 0)
		return SDEargument;
	fcross(vMtx[0], uVec, vMtx[2]);
	if (normalize(vMtx[0]) == 0)
		return SDEargument;
	fcross(vMtx[1], vMtx[2], vMtx[0]);
	return SDEnone;
}
Example #5
0
static void
circle(		/* indicate a solid angle on image */
    FVECT	dir,
    double	dom
)
{
    FVECT	start, cur;
    XPoint	pt[NSEG+1];
    FVECT	pp;
    int	ip[2];
    register int	i;

    fcross(cur, dir, ourview.vup);
    if (normalize(cur) == 0.0)
        goto fail;
    spinvector(start, dir, cur, acos(1.-dom/(2.*PI)));
    for (i = 0; i <= NSEG; i++) {
        spinvector(cur, start, dir, 2.*PI*i/NSEG);
        cur[0] += ourview.vp[0];
        cur[1] += ourview.vp[1];
        cur[2] += ourview.vp[2];
        viewloc(pp, &ourview, cur);
        if (pp[2] <= 0.0)
            goto fail;
        loc2pix(ip, &pres, pp[0], pp[1]);
        pt[i].x = ip[0];
        pt[i].y = ip[1];
    }
    XDrawLines(theDisplay, gwind, vecGC, pt, NSEG+1, CoordModeOrigin);
    return;
fail:
    fprintf(stderr, "%s: cannot draw source at (%f,%f,%f)\n",
            progname, dir[0], dir[1], dir[2]);
}
Example #6
0
int
nonplanar(			/* are vertices non-planar? */
	int	ac,
	char	**av
)
{
	VNDX	vi;
	RREAL	*p0, *p1;
	FVECT	v1, v2, nsum, newn;
	double	d;
	int	i;

	if (!cvtndx(vi, av[0]))
		return(0);
	if (!flatten && vi[2] >= 0)
		return(1);		/* has interpolated normals */
	if (ac < 4)
		return(0);		/* it's a triangle! */
					/* set up */
	p0 = vlist[vi[0]];
	if (!cvtndx(vi, av[1]))
		return(0);		/* error gets caught later */
	nsum[0] = nsum[1] = nsum[2] = 0.;
	p1 = vlist[vi[0]];
	fvsum(v2, p1, p0, -1.0);
	for (i = 2; i < ac; i++) {
		VCOPY(v1, v2);
		if (!cvtndx(vi, av[i]))
			return(0);
		p1 = vlist[vi[0]];
		fvsum(v2, p1, p0, -1.0);
		fcross(newn, v1, v2);
		if (normalize(newn) == 0.0) {
			if (i < 3)
				return(1);	/* can't deal with this */
			fvsum(nsum, nsum, nsum, 1./(i-2));
			continue;
		}
		d = fdot(newn,nsum);
		if (d >= 0) {
			if (d < (1.0-FTINY)*(i-2))
				return(1);
			fvsum(nsum, nsum, newn, 1.0);
		} else {
			if (d > -(1.0-FTINY)*(i-2))
				return(1);
			fvsum(nsum, nsum, newn, -1.0);
		}
	}
	return(0);
}
Example #7
0
static double
l_psize(char *nm)		/* compute pixel size in steradians */
{
	static unsigned long	ltick[MAXINP];
	static double	psize[MAXINP];
	FVECT	dir0, org, dirx, diry;
	RREAL	locx[2], locy[2];
	double	d;
	int	fn;
	register int	i;

	d = argument(1);
	if (d <= -0.5 || d >= nfiles+0.5) {
		errno = EDOM;
		return(0.0);
	}
	if (d < 0.5)
		return((double)nfiles);
	fn = d - 0.5;
	if (ltick[fn] != eclock) {		/* need to compute? */
		psize[fn] = 0.0;
		if (input[fn].vw.type == 0)
			errno = EDOM;
		else if (input[fn].vw.type != VT_PAR &&
				funvalue(vray[6], 1, &d) >= -FTINY) {
			for (i = 0; i < 3; i++)
				dir0[i] = funvalue(vray[3+i], 1, &d);
			pix2loc(locx, &input[fn].rs, xscan+1, ymax-1-yscan);
			pix2loc(locy, &input[fn].rs, xscan, ymax-yscan);
			if (viewray(org, dirx, &input[fn].vw,
					locx[0], locx[1]) >= -FTINY &&
					viewray(org, diry, &input[fn].vw,
					locy[0], locy[1]) >= -FTINY) {
						/* approximate solid angle */
				for (i = 0; i < 3; i++) {
					dirx[i] -= dir0[i];
					diry[i] -= dir0[i];
				}
				fcross(dir0, dirx, diry);
				psize[fn] = VLEN(dir0);
			}
		}
		ltick[fn] = eclock;
	}
	return(psize[fn]);
}
Example #8
0
static void
add_holo(		/* register a new holodeck section */
	HDGRID	*hdg,
	char	*gfn,
	char	*pfn
)
{
	VIEW	nv;
	double	d;
	register int	hd;

	for (hd = 0; hd < HDMAX && hdlist[hd] != NULL; hd++)
		;
	if (hd >= HDMAX)
		error(INTERNAL, "too many holodeck sections in add_holo");
	hdlist[hd] = (HOLO *)malloc(sizeof(HOLO));
	if (hdlist[hd] == NULL)
		error(SYSTEM, "out of memory in add_holo");
	memcpy((void *)hdlist[hd], (void *)hdg, sizeof(HDGRID));
	hdcompgrid(hdlist[hd]);
	hdgfn[hd] = savestr(gfn);
	hdpfn[hd] = pfn && *pfn ? savestr(pfn) : (char *)NULL;
	if (hd)
		return;
					/* set initial viewpoint */
	nv = odev.v;
	VSUM(nv.vp, hdlist[0]->orig, hdlist[0]->xv[0], 0.5);
	VSUM(nv.vp, nv.vp, hdlist[0]->xv[1], 0.5);
	VSUM(nv.vp, nv.vp, hdlist[0]->xv[2], 0.5);
	fcross(nv.vdir, hdlist[0]->xv[1], hdlist[0]->xv[2]);
	VCOPY(nv.vup, hdlist[0]->xv[2]);
	if (do_outside) {
		normalize(nv.vdir);
		d = VLEN(hdlist[0]->xv[1]);
		d += VLEN(hdlist[0]->xv[2]);
		VSUM(nv.vp, nv.vp, nv.vdir, -d);
	}
	new_view(&nv);
}
Example #9
0
void
hdcompgrid(			/* compute derived grid vector and index */
	HOLO	*hp
)
{
	double	d;
	int	i, j;
				/* initialize depth map */
	if (hd_depthmap[0] < 1.) {
		d = 1. + .5/DCLIN;
		for (i = 0; i < DCINF-DCLIN; i++) {
			hd_depthmap[i] = d;
			d *= 1. + 1./DCLIN;
		}
		logstep = log(1. + 1./DCLIN);
	}
				/* compute grid coordinate vectors */
	for (i = 0; i < 3; i++) {
		fcross(hp->wg[i], hp->xv[(i+1)%3], hp->xv[(i+2)%3]);
		d = DOT(hp->wg[i],hp->xv[i]);
		if ((d <= FTINY) & (d >= -FTINY))
			error(USER, "degenerate holodeck section");
		d = hp->grid[i] / d;
		hp->wg[i][0] *= d; hp->wg[i][1] *= d; hp->wg[i][2] *= d;
	}
				/* compute linear depth range */
	hp->tlin = VLEN(hp->xv[0]) + VLEN(hp->xv[1]) + VLEN(hp->xv[2]);
				/* compute wall super-indices from grid */
	hp->wi[0] = 1;		/**** index values begin at 1 ****/
	for (i = 1; i < 6; i++) {
		hp->wi[i] = 0;
		for (j = i; j < 6; j++)
			hp->wi[i] += hp->grid[hdwg0[j]] * hp->grid[hdwg1[j]];
		hp->wi[i] *= hp->grid[hdwg0[i-1]] * hp->grid[hdwg1[i-1]];
		hp->wi[i] += hp->wi[i-1];
	}
}
Example #10
0
static int
disperse(  /* check light sources for dispersion */
	OBJREC  *m,
	RAY  *r,
	FVECT  vt,
	double  tr,
	COLOR  cet,
	COLOR  abt
)
{
	RAY  sray;
	const RAY  *entray;
	FVECT  v1, v2, n1, n2;
	FVECT  dv, v2Xdv;
	double  v2Xdvv2Xdv;
	int  success = 0;
	SRCINDEX  si;
	FVECT  vtmp1, vtmp2;
	double  dtmp1, dtmp2;
	int  l1, l2;
	COLOR  ctmp;
	int  i;
	
	/*
	 *     This routine computes dispersion to the first order using
	 *  the following assumptions:
	 *
	 *	1) The dependency of the index of refraction on wavelength
	 *		is approximated by Hartmann's equation with lambda0
	 *		equal to zero.
	 *	2) The entry and exit locations are constant with respect
	 *		to dispersion.
	 *
	 *     The second assumption permits us to model dispersion without
	 *  having to sample refracted directions.  We assume that the
	 *  geometry inside the material is constant, and concern ourselves
	 *  only with the relationship between the entering and exiting ray.
	 *  We compute the first derivatives of the entering and exiting
	 *  refraction with respect to the index of refraction.  This 
	 *  is then used in a first order Taylor series to determine the
	 *  index of refraction necessary to send the exiting ray to each
	 *  light source.
	 *     If an exiting ray hits a light source within the refraction
	 *  boundaries, we sum all the frequencies over the disc of the
	 *  light source to determine the resulting color.  A smaller light
	 *  source will therefore exhibit a sharper spectrum.
	 */

	if (!(r->crtype & REFRACTED)) {		/* ray started in material */
		VCOPY(v1, r->rdir);
		n1[0] = -r->rdir[0]; n1[1] = -r->rdir[1]; n1[2] = -r->rdir[2];
	} else {
						/* find entry point */
		for (entray = r; entray->rtype != REFRACTED;
				entray = entray->parent)
			;
		entray = entray->parent;
		if (entray->crtype & REFRACTED)		/* too difficult */
			return(0);
		VCOPY(v1, entray->rdir);
		VCOPY(n1, entray->ron);
	}
	VCOPY(v2, vt);			/* exiting ray */
	VCOPY(n2, r->ron);

					/* first order dispersion approx. */
	dtmp1 = 1./DOT(n1, v1);
	dtmp2 = 1./DOT(n2, v2);
	for (i = 0; i < 3; i++)
		dv[i] = v1[i] + v2[i] - n1[i]*dtmp1 - n2[i]*dtmp2;
		
	if (DOT(dv, dv) <= FTINY)	/* null effect */
		return(0);
					/* compute plane normal */
	fcross(v2Xdv, v2, dv);
	v2Xdvv2Xdv = DOT(v2Xdv, v2Xdv);

					/* check sources */
	initsrcindex(&si);
	while (srcray(&sray, r, &si)) {

		if (DOT(sray.rdir, v2) < MINCOS)
			continue;			/* bad source */
						/* adjust source ray */

		dtmp1 = DOT(v2Xdv, sray.rdir) / v2Xdvv2Xdv;
		sray.rdir[0] -= dtmp1 * v2Xdv[0];
		sray.rdir[1] -= dtmp1 * v2Xdv[1];
		sray.rdir[2] -= dtmp1 * v2Xdv[2];

		l1 = lambda(m, v2, dv, sray.rdir);	/* mean lambda */

		if (l1 > MAXLAMBDA || l1 < MINLAMBDA)	/* not visible */
			continue;
						/* trace source ray */
		copycolor(sray.cext, cet);
		copycolor(sray.albedo, abt);
		normalize(sray.rdir);
		rayvalue(&sray);
		if (bright(sray.rcol) <= FTINY)	/* missed it */
			continue;
		
		/*
		 *	Compute spectral sum over diameter of source.
		 *  First find directions for rays going to opposite
		 *  sides of source, then compute wavelengths for each.
		 */
		 
		fcross(vtmp1, v2Xdv, sray.rdir);
		dtmp1 = sqrt(si.dom  / v2Xdvv2Xdv / PI);

							/* compute first ray */
		VSUM(vtmp2, sray.rdir, vtmp1, dtmp1);

		l1 = lambda(m, v2, dv, vtmp2);		/* first lambda */
		if (l1 < 0)
			continue;
							/* compute second ray */
		VSUM(vtmp2, sray.rdir, vtmp1, -dtmp1);

		l2 = lambda(m, v2, dv, vtmp2);		/* second lambda */
		if (l2 < 0)
			continue;
					/* compute color from spectrum */
		if (l1 < l2)
			spec_rgb(ctmp, l1, l2);
		else
			spec_rgb(ctmp, l2, l1);
		multcolor(ctmp, sray.rcol);
		scalecolor(ctmp, tr);
		addcolor(r->rcol, ctmp);
		success++;
	}
	return(success);
}
Example #11
0
char *
setview(		/* set hvec and vvec, return message on error */
VIEW  *v
)
{
	static char  ill_horiz[] = "illegal horizontal view size";
	static char  ill_vert[] = "illegal vertical view size";
	
	if ((v->vfore < -FTINY) | (v->vaft < -FTINY) ||
			(v->vaft > FTINY) & (v->vaft <= v->vfore))
		return("illegal fore/aft clipping plane");

	if (v->vdist <= FTINY)
		return("illegal view distance");
	v->vdist *= normalize(v->vdir);		/* normalize direction */
	if (v->vdist == 0.0)
		return("zero view direction");

	if (normalize(v->vup) == 0.0)		/* normalize view up */
		return("zero view up vector");

	fcross(v->hvec, v->vdir, v->vup);	/* compute horiz dir */

	if (normalize(v->hvec) == 0.0)
		return("view up parallel to view direction");

	fcross(v->vvec, v->hvec, v->vdir);	/* compute vert dir */

	if (v->horiz <= FTINY)
		return(ill_horiz);
	if (v->vert <= FTINY)
		return(ill_vert);

	switch (v->type) {
	case VT_PAR:				/* parallel view */
		v->hn2 = v->horiz;
		v->vn2 = v->vert;
		break;
	case VT_PER:				/* perspective view */
		if (v->horiz >= 180.0-FTINY)
			return(ill_horiz);
		if (v->vert >= 180.0-FTINY)
			return(ill_vert);
		v->hn2 = 2.0 * tan(v->horiz*(PI/180.0/2.0));
		v->vn2 = 2.0 * tan(v->vert*(PI/180.0/2.0));
		break;
	case VT_CYL:				/* cylindrical panorama */
		if (v->horiz > 360.0+FTINY)
			return(ill_horiz);
		if (v->vert >= 180.0-FTINY)
			return(ill_vert);
		v->hn2 = v->horiz * (PI/180.0);
		v->vn2 = 2.0 * tan(v->vert*(PI/180.0/2.0));
		break;
	case VT_ANG:				/* angular fisheye */
		if (v->horiz > 360.0+FTINY)
			return(ill_horiz);
		if (v->vert > 360.0+FTINY)
			return(ill_vert);
		v->hn2 = v->horiz * (PI/180.0);
		v->vn2 = v->vert * (PI/180.0);
		break;
	case VT_HEM:				/* hemispherical fisheye */
		if (v->horiz > 180.0+FTINY)
			return(ill_horiz);
		if (v->vert > 180.0+FTINY)
			return(ill_vert);
		v->hn2 = 2.0 * sin(v->horiz*(PI/180.0/2.0));
		v->vn2 = 2.0 * sin(v->vert*(PI/180.0/2.0));
		break;
	case VT_PLS:				/* planispheric fisheye */
		if (v->horiz >= 360.0-FTINY)
			return(ill_horiz);
		if (v->vert >= 360.0-FTINY)
			return(ill_vert);
		v->hn2 = 2.*sin(v->horiz*(PI/180.0/2.0)) /
				(1.0 + cos(v->horiz*(PI/180.0/2.0)));
		v->vn2 = 2.*sin(v->vert*(PI/180.0/2.0)) /
				(1.0 + cos(v->vert*(PI/180.0/2.0)));
		break;
	default:
		return("unknown view type");
	}
	if (v->type != VT_ANG && v->type != VT_PLS) {
		if (v->type != VT_CYL) {
			v->hvec[0] *= v->hn2;
			v->hvec[1] *= v->hn2;
			v->hvec[2] *= v->hn2;
		}
		v->vvec[0] *= v->vn2;
		v->vvec[1] *= v->vn2;
		v->vvec[2] *= v->vn2;
	}
	v->hn2 *= v->hn2;
	v->vn2 *= v->vn2;

	return(NULL);
}
Example #12
0
void					/* initialize occlusion cache */
initobscache(int sn)
{
	register SRCREC *srcp = &source[sn];
	int		cachelen;
	FVECT		rorg, rdir;
	RREAL		d;
	int		i, j, k;
	int		ax=0, ax1=1, ax2=2;

	if (srcp->sflags & (SSKIP|SPROX|SSPOT|SVIRTUAL))
		return;			/* don't cache these */
	if (srcp->sflags & SDISTANT)
		cachelen = 4*SHADCACHE*SHADCACHE;
	else if (srcp->sflags & SFLAT)
		cachelen = SHADCACHE*SHADCACHE*3 + (SHADCACHE&1)*SHADCACHE*4;
	else /* spherical distribution */
		cachelen = SHADCACHE*SHADCACHE*6;
					/* allocate cache */
	srcp->obscache = (OBSCACHE *)malloc(sizeof(OBSCACHE) +
						sizeof(OBJECT)*(cachelen-1));
	if (srcp->obscache == NULL)
		error(SYSTEM, "out of memory in initobscache()");
					/* set parameters */
	if (srcp->sflags & SDISTANT) {
		RREAL   amax = 0;
		for (ax1 = 3; ax1--; )
			if (ABS(srcp->sloc[ax1]) > amax) {
				amax = ABS(srcp->sloc[ax1]);
				ax = ax1;
			}
		srcp->obscache->p.d.ax = ax;
		ax1 = (ax+1)%3;
		ax2 = (ax+2)%3;
		VCOPY(srcp->obscache->p.d.o, thescene.cuorg);
		if (srcp->sloc[ax] > 0)
			srcp->obscache->p.d.o[ax] += thescene.cusize;
		if (srcp->sloc[ax1] < 0)
			srcp->obscache->p.d.o[ax1] += thescene.cusize *
					srcp->sloc[ax1] / amax;
		if (srcp->sloc[ax2] < 0)
			srcp->obscache->p.d.o[ax2] += thescene.cusize *
					srcp->sloc[ax2] / amax;
		srcp->obscache->p.d.e1 = 1. / (thescene.cusize*(1. +
				fabs(srcp->sloc[ax1])/amax));
		srcp->obscache->p.d.e2 = 1. / (thescene.cusize*(1. +
				fabs(srcp->sloc[ax2])/amax));
	} else if (srcp->sflags & SFLAT) {
		VCOPY(srcp->obscache->p.f.u, srcp->ss[SU]);
		normalize(srcp->obscache->p.f.u);
		fcross(srcp->obscache->p.f.v,
				srcp->snorm, srcp->obscache->p.f.u);
	}
					/* clear cache */
	for (i = cachelen; i--; )
		srcp->obscache->obs[i] = OVOID;
					/* cast shadow rays */
	if (srcp->sflags & SDISTANT) {
		for (k = 3; k--; )
			rdir[k] = -srcp->sloc[k];
		for (i = 2*SHADCACHE; i--; )
			for (j = 2*SHADCACHE; j--; ) {
				VCOPY(rorg, srcp->obscache->p.d.o);
				rorg[ax1] += (i+.5) /
					(2*SHADCACHE*srcp->obscache->p.d.e1);
				rorg[ax2] += (j+.5) /
					(2*SHADCACHE*srcp->obscache->p.d.e2);
				castshadow(sn, rorg, rdir);
			}
	} else if (srcp->sflags & SFLAT) {
		d = 0.01*srcp->srad;
		VSUM(rorg, srcp->sloc, srcp->snorm, d);
		for (i = SHADCACHE; i--; )
			for (j = SHADCACHE; j--; ) {
				d = 2./SHADCACHE*(i+.5) - 1.;
				VSUM(rdir, srcp->snorm,
						srcp->obscache->p.f.u, d);
				d = 2./SHADCACHE*(j+.5) - 1.;
				VSUM(rdir, rdir, srcp->obscache->p.f.v, d);
				normalize(rdir);
				castshadow(sn, rorg, rdir);
			}
		for (k = 2; k--; )
		    for (i = SHADCACHE; i--; )
			for (j = SHADCACHE>>1; j--; ) {
Example #13
0
int
buildxf(		/* build transform for marker */
	register char	*xf,
	double	markscale,
	FILE	*fin
)
{
	static FVECT	vlist[MAXVERT];
	static EDGE	elist[MAXVERT];
	FVECT	xvec, yvec, zvec;
	double	xlen;
	int	n;
	register int	i;
	/*
	 * Read and sort vectors:  longest is hypotenuse,
	 *	second longest is x' axis,
	 *	third longest is y' axis (approximate),
	 *	other vectors are ignored.
	 * It is an error if the x' and y' axes do
	 *	not share a common vertex (their origin).
	 */
	if (fscanf(fin, "%*d %*d %d", &n) != 1)
		return(-1);
	if (n%3 != 0)
		return(-1);
	n /= 3;
	if (n < 3 || n > MAXVERT)
		return(-1);
					/* sort edges in descending order */
	for (i = 0; i < n; i++) {
		if (fscanf(fin, "%lf %lf %lf", &vlist[i][0],
				&vlist[i][1], &vlist[i][2]) != 3)
			return(-1);
		if (i) {
			elist[i].beg = i-1;
			elist[i].end = i;
			elist[i].len2 = dist2(vlist[i-1],vlist[i]);
		}
	}
	elist[0].beg = n-1;
	elist[0].end = 0;
	elist[0].len2 = dist2(vlist[n-1],vlist[0]);
	qsort(elist, n, sizeof(EDGE), edgecmp);
					/* find x' and y' */
	if (elist[1].end == elist[2].beg || elist[1].end == elist[2].end) {
		i = elist[1].beg;
		elist[1].beg = elist[1].end;
		elist[1].end = i;
	}
	if (elist[2].end == elist[1].beg) {
		i = elist[2].beg;
		elist[2].beg = elist[2].end;
		elist[2].end = i;
	}
	if (elist[1].beg != elist[2].beg)
		return(-1);		/* x' and y' not connected! */
	for (i = 0; i < 3; i++) {
		xvec[i] = vlist[elist[1].end][i] - vlist[elist[1].beg][i];
		yvec[i] = vlist[elist[2].end][i] - vlist[elist[2].beg][i];
	}
	if ((xlen = normalize(xvec)) == 0.0)
		return(-1);
	fcross(zvec, xvec, yvec);
	if (normalize(zvec) == 0.0)
		return(-1);
	fcross(yvec, zvec, xvec);
	n = 0;				/* start transformation... */
	if (markscale > 0.0) {		/* add scale factor */
		sprintf(xf, " -s %f", xlen*markscale);
		n += 2;
		while (*xf) ++xf;
	}
					/* add rotation */
	n += addrot(xf, xvec, yvec, zvec);
	while (*xf) ++xf;
					/* add translation */
	n += 4;
	sprintf(xf, " -t %f %f %f", vlist[elist[1].beg][0],
			vlist[elist[1].beg][1], vlist[elist[1].beg][2]);
	return(n);			/* all done */
}