PositiveRoots:
PROCEDURE [a4, a3, a2, a1, a0:
REAL]
RETURNS [rootArray: Polynomial.ShortRealRootRec] = {
Use only the positive roots.
i: NAT ← 1;
globalPoly[4] ← a4; -- optional
globalPoly[3] ← a3; -- optional
globalPoly[2] ← a2; -- optional
globalPoly[1] ← a1; -- optional
globalPoly[0] ← a0; -- optional
rootArray ← Polynomial.CheapRealRoots[globalPoly]; -- optional
[realRootsOneOrigin, rootCount] ← Roots.RealQuartic[a4, a3, a2, a1, a0];
realRoots[0] ← realRootsOneOrigin[1]; realRoots[1] ← realRootsOneOrigin[2];
realRoots[2] ← realRootsOneOrigin[3]; realRoots[3] ← realRootsOneOrigin[4];
IF rootArray.nRoots = 0 THEN RETURN;
WHILE i <= rootArray.nRoots
DO
IF rootArray.realRoot[i-1] <= 0
THEN {
FOR j:
NAT
IN [i..rootArray.nRoots-1]
DO
rootArray.realRoot[j-1] ← rootArray.realRoot[j];
ENDLOOP;
rootArray.nRoots ← rootArray.nRoots - 1;
}
ELSE {i ← i + 1};
ENDLOOP;
};
ToroidCast:
PUBLIC
PROC [localRay: Ray, prim: Primitive, torusRec:
REF
ANY, surface: Surface]
RETURNS [class: Classification] = {
torusData: TorusRec;
realRoots: Polynomial.ShortRealRootRec; -- optional
realRoots: ARRAY[0..3] OF REAL;
realRootsOneOrigin: ARRAY[1..4] OF REAL;
rootCount: NAT;
B,C: REAL;
n,N,q,Q,l,L: REAL;
BB, CC: REAL;
D,E,F: REAL;
nn, NN, qq, QQ, ll, LL, NNNN, QQQQ, LLLL, nN, qQ, lL: REAL;
a4,a3,a2,a1,a0: REAL;
class ← CastRays.GetClassFromPool[];
torusData ← NARROW[torusRec];
B ← torusData.bigRadius;
C ← torusData.sectionRadius;
n ← localRay.basePt[1]; N ← localRay.direction[1];
q ← localRay.basePt[2]; Q ← localRay.direction[2];
l ← localRay.basePt[3]; L ← localRay.direction[3];
BB ← B*B;
CC ← C*C;
D ← BB + CC;
E ← BB - CC;
F ← BB*CC;
nn ← n*n; NN ← N*N; qq ← q*q; QQ ← Q*Q; ll ← l*l; LL ← L*L;
NNNN ← NN*NN; QQQQ ← QQ*QQ; LLLL ← LL*LL;
nN ← n*N; qQ ← q*Q; lL ← l*L;
a4 ← NNNN + QQQQ + LLLL + 2*(NN*(QQ + LL) + QQ*LL);
a3 ← 4*(nN*NN + qQ*QQ + lL*LL + nN*(QQ + LL) + NN*(qQ + lL) + lL*QQ + qQ*LL);
a2 ← 2*(3*(nn*NN + qq*QQ + ll*LL) + NN*(qq + ll) + nn*(QQ+LL) + 4*nN*(qQ+lL)
- D*NN + E*QQ - D*LL + ll*QQ + LL*qq + 4*qQ*lL);
a1 ← 4*(nn*nN + qq*qQ + ll*lL + nN*(qq + ll) + (qQ + lL)*nn - nN*D + qQ*E - lL*D + qQ*ll + lL*qq);
a0 ← nn*nn + qq*qq + ll*ll + BB*BB + CC*CC +
2*(nn*(qq + ll) - D*nn + E*qq - D*ll + qq*ll - F);
realRoots ← PositiveRoots[a4, a3, a2, a1, a0];
SELECT realRoots.nRoots FROM
SELECT rootCount FROM
0 => {
-- complete miss.
class.count ← 0;
class.classifs[1] ← FALSE;
};
1 => {
-- a skim. Count as a miss.
class.count ← 0;
class.classifs[1] ← FALSE;
};
2 => {
-- cuts one cross section of the doughnut. Sort roots by size.
x, y, z, theta: REAL;
class.count ← 2;
IF realRoots.realRoot[0] < realRoots.realRoot[1]
THEN
{class.params[1] ← realRoots.realRoot[0];
class.params[2] ← realRoots.realRoot[1]}
ELSE
{class.params[2] ← realRoots.realRoot[0];
class.params[1] ← realRoots.realRoot[1]};
class.surfaces[1] ← surface; class.surfaces[2] ← surface;
class.classifs[1] ← FALSE; class.classifs[2] ← TRUE; class.classifs[3] ← FALSE;
class.primitives[1] ← class.primitives[2] ← prim;
Calculate the surface normals at the two hits points.
x ← n + class.params[1]*N; y ← q + class.params[1]*Q; z ← l + class.params[1]*L;
theta ← RealFns.ArcTan[-z,x];
class.normals[1] ← [-B*RealFns.Cos[theta] + x, y, B*RealFns.Sin[theta] + z];
To normalize, divide by C. (I won't do this since the lighting model normalizes anyway)
x ← n + class.params[2]*N; y ← q + class.params[2]*Q; z ← l + class.params[2]*L;
theta ← RealFns.ArcTan[-z,x];
class.normals[2] ← [-B*RealFns.Cos[theta] + x, y, B*RealFns.Sin[theta] + z];
};
3 => {
-- cuts one cross section. Skims the other. For now, count as a miss.
class.count ← 0;
class.classifs[1] ← FALSE;
};
4 => {
-- cuts both cross sections. Sort the roots. Pair them in order.
x, y, z, theta: REAL;
SortHitRec[realRoots, 4];
SortHitRec[realRoots];
class.count ← 4;
class.params[1] ← realRoots.realRoot[0];
class.params[2] ← realRoots.realRoot[1];
class.params[3] ← realRoots.realRoot[2];
class.params[4] ← realRoots.realRoot[3];
class.surfaces[1] ← class.surfaces[2] ← class.surfaces[3] ← class.surfaces[4] ← surface;
class.classifs[1] ← FALSE; class.classifs[2] ← TRUE; class.classifs[3] ← FALSE;
class.classifs[1] ← TRUE; class.classifs[2] ← FALSE;
class.primitives[1] ← class.primitives[2] ← class.primitives[3] ← class.primitives[4] ← prim;
Calculate the surface normals at the four hits points.
x ← n + class.params[1]*N; y ← q + class.params[1]*Q; z ← l + class.params[1]*L;
theta ← RealFns.ArcTan[-z,x];
class.normals[1] ← [-B*RealFns.Cos[theta] + x, y, B*RealFns.Sin[theta] + z];
To normalize, divide by C. (I won't do this since the lighting model normalizes anyway)
x ← n + class.params[2]*N; y ← q + class.params[2]*Q; z ← l + class.params[2]*L;
theta ← RealFns.ArcTan[-z,x];
class.normals[2] ← [-B*RealFns.Cos[theta] + x, y, B*RealFns.Sin[theta] + z];
x ← n + class.params[3]*N; y ← q + class.params[3]*Q; z ← l + class.params[3]*L;
theta ← RealFns.ArcTan[-z,x];
class.normals[3] ← [-B*RealFns.Cos[theta] + x, y, B*RealFns.Sin[theta] + z];
x ← n + class.params[4]*N; y ← q + class.params[4]*Q; z ← l + class.params[4]*L;
theta ← RealFns.ArcTan[-z,x];
class.normals[4] ← [-B*RealFns.Cos[theta] + x, y, B*RealFns.Sin[theta] + z];
};
ENDCASE => ERROR;
};