1 /*
2  * cylinder.c
3  *
4  * Copyright (C) 1989, 1991, Craig E. Kolb
5  * All rights reserved.
6  *
7  * This software may be freely copied, modified, and redistributed
8  * provided that this copyright notice is preserved on all copies.
9  *
10  * You may not distribute this software, in whole or in part, as part of
11  * any commercial product without the express consent of the authors.
12  *
13  * There is no warranty or other guarantee of fitness of this software
14  * for any purpose.  It is provided solely "as is".
15  *
16  * $Id: cylinder.c,v 4.0 91/07/17 14:37:12 kolb Exp Locker: kolb $
17  *
18  * $Log:	cylinder.c,v $
19  * Revision 4.0  91/07/17  14:37:12  kolb
20  * Initial version.
21  *
22  */
23 #include "geom.h"
24 #include "cylinder.h"
25 
26 static Methods *iCylinderMethods = NULL;
27 static char cylName[] = "cylinder";
28 
29 unsigned long CylTests, CylHits;
30 
31 Cylinder *
CylinderCreate(r,bot,top)32 CylinderCreate(r, bot, top)
33 Float r;
34 Vector *bot, *top;
35 {
36 	Cylinder *cyl;
37 	Float len;
38 	Vector axis;
39 
40 	if (r <= 0.) {
41 		RLerror(RL_WARN, "Invalid cylinder radius.\n");
42 		return (Cylinder*)NULL;
43 	}
44 
45 	VecSub(*top, *bot, &axis);
46 
47 	len = VecNormalize(&axis);
48 
49 	if (len < EPSILON) {
50 		RLerror(RL_WARN, "Degenerate cylinder.\n");
51 		return (Cylinder *)NULL;
52 	}
53 
54 	cyl = (Cylinder *)share_malloc(sizeof(Cylinder));
55 	CoordSysTransform(bot, &axis, r, len, &cyl->trans);
56 	return cyl;
57 }
58 
59 Methods *
CylinderMethods()60 CylinderMethods()
61 {
62 	if (iCylinderMethods == (Methods *)NULL) {
63 		iCylinderMethods = MethodsCreate();
64 		iCylinderMethods->name = CylinderName;
65 		iCylinderMethods->create = (GeomCreateFunc *)CylinderCreate;
66 		iCylinderMethods->methods = CylinderMethods;
67 		iCylinderMethods->intersect = CylinderIntersect;
68 		iCylinderMethods->normal = CylinderNormal;
69 		iCylinderMethods->uv = CylinderUV;
70 		iCylinderMethods->bounds = CylinderBounds;
71 		iCylinderMethods->stats = CylinderStats;
72 		iCylinderMethods->checkbounds = TRUE;
73 		iCylinderMethods->closed = FALSE;
74 	}
75 	return iCylinderMethods;
76 }
77 
78 /*
79  * Ray-cylinder intersection test.
80  */
81 int
CylinderIntersect(cyl,ray,mindist,maxdist)82 CylinderIntersect(cyl, ray, mindist, maxdist)
83 Cylinder *cyl;
84 Ray *ray;
85 Float mindist, *maxdist;
86 {
87 	Float t1, t2, a, b, c, zpos1, zpos2, disc;
88 	Float distfact;
89 	Ray newray;
90 	Vector nray, npos;
91 	Float nmin;
92 
93 	CylTests++;
94 
95 	/*
96 	 * Transform ray into canonical cylinder space.
97 	 */
98 	newray = *ray;
99 	distfact = RayTransform(&newray, &cyl->trans.itrans);
100 	nray = newray.dir;
101 	npos = newray.pos;
102 	nmin = mindist * distfact;
103 
104 	a = nray.x * nray.x + nray.y * nray.y;
105 	if (a < EPSILON*EPSILON)
106 		/* |nray.z| == 1. */
107 		return FALSE;
108 
109 	b = nray.x * npos.x + nray.y * npos.y;
110 	c = npos.x*npos.x + npos.y*npos.y - 1;
111 	disc = b*b - a*c;
112 	if(disc < 0.)
113 		return FALSE;
114 	disc = sqrt(disc);
115 	t1 = (-b + disc) / a;
116 	t2 = (-b - disc) / a;
117 	if (t1 < nmin && t2 < nmin)
118 		return FALSE;
119 	zpos1 = npos.z + t1 * nray.z;
120 	zpos2 = npos.z + t2 * nray.z;
121 
122 	if (t1 < nmin || zpos1 < 0. || zpos1 > 1.) {
123 		if (t2 < nmin || zpos2 < 0. || zpos2 > 1.)
124 			return FALSE;
125 		else
126 			t1 = t2 / distfact;
127 
128 	} else {
129 		if (t2 < nmin || zpos2 < 0. || zpos2 > 1.)
130 			t1 /= distfact;
131 		else {
132 			t1 = min(t1, t2) / distfact;
133 		}
134 	}
135 
136 	if (t1 < *maxdist) {
137 		*maxdist = t1;
138 		CylHits++;
139 		return TRUE;
140 	}
141 	return FALSE;
142 }
143 
144 int
CylinderNormal(cyl,pos,nrm,gnrm)145 CylinderNormal(cyl, pos, nrm, gnrm)
146 Cylinder *cyl;
147 Vector *pos, *nrm, *gnrm;
148 {
149 	/*
150 	 * Transform position into cylinder space.
151 	 */
152 	*nrm = *pos;
153 	PointTransform(nrm, &cyl->trans.itrans);
154 	/*
155 	 * The normal is equal to the point of intersection in cylinder
156 	 * space, but with Z = 0.;
157 	 */
158 	nrm->z = 0.;
159 
160 	/*
161 	 * Tranform normal back to world space.
162 	 */
163 	NormalTransform(nrm, &cyl->trans.itrans);
164 	*gnrm = *nrm;
165 	return FALSE;
166 }
167 
168 void
CylinderUV(cyl,pos,norm,uv,dpdu,dpdv)169 CylinderUV(cyl, pos, norm, uv, dpdu, dpdv)
170 Cylinder *cyl;
171 Vector *pos, *norm, *dpdu, *dpdv;
172 Vec2d *uv;
173 {
174 	Vector npos;
175 
176 	npos = *pos;
177 	PointTransform(&npos, &cyl->trans.itrans);
178 
179 	uv->v = npos.z;
180 	/*
181 	 * Due to roundoff error, |npos.x| may be > 1.
182 	 */
183 	if (npos.x > 1.)
184 		uv->u = 0.;
185 	else if (npos.x < -1.)
186 		uv->u = 0.5;
187 	else
188 		uv->u = acos(npos.x) / TWOPI;
189 	if (npos.y < 0.)
190 		uv->u = 1. - uv->u;
191 
192 	if (dpdu) {
193 		dpdv->x = dpdv->y = 0.;
194 		dpdv->z = 1.;
195 		dpdu->x = -npos.y;
196 		dpdu->y = npos.x;
197 		dpdu->z = 0.;
198 		VecTransform(dpdu, &cyl->trans.trans);
199 		VecTransform(dpdv, &cyl->trans.trans);
200 		(void)VecNormalize(dpdu);
201 		(void)VecNormalize(dpdv);
202 	}
203 }
204 
205 void
CylinderBounds(cyl,bounds)206 CylinderBounds(cyl, bounds)
207 Cylinder *cyl;
208 Float bounds[2][3];
209 {
210 	bounds[LOW][X] = bounds[LOW][Y] = -1;
211 	bounds[HIGH][X] = bounds[HIGH][Y] = 1;
212 	bounds[LOW][Z] = 0.;
213 	bounds[HIGH][Z] = 1;
214 	/*
215 	 * Transform bounding box to world space.
216 	 */
217 	BoundsTransform(&cyl->trans.trans, bounds);
218 }
219 
220 char *
CylinderName()221 CylinderName()
222 {
223 	return cylName;
224 }
225 
226 void
CylinderStats(tests,hits)227 CylinderStats(tests, hits)
228 unsigned long *tests, *hits;
229 {
230 	*tests = CylTests;
231 	*hits = CylHits;
232 }
233 
234 void
CylinderMethodRegister(meth)235 CylinderMethodRegister(meth)
236 UserMethodType meth;
237 {
238 	if (iCylinderMethods)
239 		iCylinderMethods->user = meth;
240 }
241