<> <> <> <> DIRECTORY Algebra3d, SVRay, SV3d, SVFaces, SVFacesCast, SVSceneTypes, SVVector3d; SVFacesCastImpl: CEDAR PROGRAM IMPORTS Algebra3d, SVRay, SVVector3d EXPORTS SVFacesCast = BEGIN Cone: TYPE = SVFaces.Cone; DiskRing: TYPE = SVFaces.DiskRing; Cylinder: TYPE = SVFaces.Cylinder; EdgeOnRect: TYPE = SVFaces.EdgeOnRect; Point3d: TYPE = SV3d.Point3d; Ray: TYPE = SVSceneTypes.Ray; Vector3d: TYPE = SV3d.Vector3d; FaceHitRec: TYPE = REF FaceHitRecObj; FaceHitRecObj: TYPE = SVFacesCast.FaceHitRecObj; almostZero: REAL = 1.0e-12; PositiveRoots: PROCEDURE [a, b, c: REAL] RETURNS [rootArray: ARRAY[1..2] OF REAL, rootCount: NAT] = { <> i: NAT _ 1; [rootArray, rootCount] _ Algebra3d.QuadraticFormula[a, b, c]; IF rootCount = 0 THEN RETURN; WHILE i <= rootCount DO IF rootArray[i] <= 0 THEN { FOR j: NAT IN [i..rootCount-1] DO rootArray[j] _ rootArray[j+1]; ENDLOOP; rootCount _ rootCount - 1; } ELSE {i _ i + 1}; ENDLOOP; }; ConeCast: PUBLIC PROC [cone: Cone, localRay: Ray] RETURNS [hits: FaceHitRec] = { <> <> <> <> <> <<+ (p1p1 + p3p3 - MhMh + 2*Mhp2 - p2p2) = 0.>> a, b, c: REAL; y: REAL; roots: ARRAY[1..2] OF REAL; rootCount: NAT; MM: REAL _ cone.M*cone.M; MMh: REAL _ MM*cone.h; p: Point3d; d: Vector3d; [p, d] _ SVRay.GetLocalRay[localRay]; hits _ GetFaceHitFromPool[]; a _ d[1]*d[1] + d[3]*d[3] - MM*d[2]*d[2]; b _ 2*(p[1]*d[1] + p[3]*d[3] + MMh*d[2] - MM*p[2]*d[2]); c _ p[1]*p[1] + p[3]*p[3] - MMh*cone.h + 2*MMh*p[2] - MM*p[2]*p[2]; [roots, rootCount] _ PositiveRoots[a, b, c]; hits.count _ 0; FOR i: NAT IN[1..rootCount] DO <> y _ p[2] + roots[i]*d[2]; IF cone.yLo <= y AND y<= cone.yHi THEN { x, z: REAL; hits.count _ hits.count + 1; hits.params[hits.count] _ roots[i]; <> x _ p[1] + roots[i]*d[1]; z _ p[3] + roots[i]*d[3]; <> hits.normals[hits.count] _ [x, MM*(cone.h-y), z]; IF NOT cone.normalPointsOut THEN hits.normals[hits.count] _ SVVector3d.Negate[hits.normals[hits.count]]; }; -- end of root hits ENDLOOP; }; -- end of ConeCast DiskRingCast: PUBLIC PROC [diskRing: DiskRing, localRay: Ray] RETURNS [hits: FaceHitRec] = { <> <> <> <> x, z, r2, t: REAL; p: Point3d; d: Vector3d; [p, d] _ SVRay.GetLocalRay[localRay]; hits _ GetFaceHitFromPool[]; hits.count _ 0; IF ABS[d[2]] < almostZero THEN RETURN; -- ray parallel to plane of disk t _ (diskRing.yPlane - p[2])/d[2]; IF t > 0 THEN { x _ p[1] + t*d[1]; z _ p[3] + t*d[3]; r2 _ x*x + z*z; IF diskRing.rInSquared <= r2 AND r2 <= diskRing.rOutSquared THEN { hits.params[1] _ t; hits.count _ 1; hits.normals[1] _ diskRing.normal; }; }; }; -- end of DiskRingCast CylinderCast: PUBLIC PROC [cylinder: Cylinder, localRay: Ray] RETURNS [hits: FaceHitRec] = { x, y, z: REAL; a, b, c: REAL; p: Point3d; d: Vector3d; roots: ARRAY[1..2] OF REAL; rootCount: NAT; [p, d] _ SVRay.GetLocalRay[localRay]; hits _ GetFaceHitFromPool[]; hits.count _ 0; <> <> <> <> <> <> <> a _ d[1]*d[1] + d[3]*d[3]; b _ 2*(p[1]*d[1] + p[3]*d[3]); c _ p[1]*p[1] + p[3]*p[3] - cylinder.rSquared; [roots, rootCount] _ PositiveRoots[a, b, c]; FOR i: NAT IN[1..rootCount] DO <> y _ p[2] + roots[i]*d[2]; IF cylinder.yLo <= y AND y <= cylinder.yHi THEN { hits.count _ hits.count + 1; hits.params[hits.count] _ roots[i]; <> x _ p[1] + roots[i]*d[1]; z _ p[3] + roots[i]*d[3]; hits.normals[hits.count] _ [x, 0, z]; IF NOT cylinder.normalPointsOut THEN hits.normals[hits.count] _ SVVector3d.Negate[hits.normals[hits.count]]; }; -- end of y in bounds ENDLOOP; }; -- end of CylinderCast EdgeOnRectCast: PUBLIC PROC [rect: EdgeOnRect, localRay: Ray] RETURNS [hits: FaceHitRec] = { <> <> <> <> <> <> x, y, z, t, denom: REAL; p: Point3d; d: Vector3d; [p, d] _ SVRay.GetLocalRay[localRay]; hits _ GetFaceHitFromPool[]; denom _ rect.A*d[1] + rect.B*d[2]; IF ABS[denom] < almostZero THEN { hits.count _ 0; RETURN}; t _ -(rect.A*p[1] + rect.B*p[2] + rect.D)/denom; IF t <= 0 THEN {hits.count _ 0; RETURN}; x _ p[1] + t*d[1]; y _ p[2] + t*d[2]; z _ p[3] + t*d[3]; <> <> IF z > rect.frontZ OR z < rect.backZ THEN {hits.count _ 0; RETURN}; IF rect.valIsY THEN { IF y > rect.valHi OR y < rect.valLo THEN {hits.count _ 0; RETURN}} ELSE { IF x > rect.valHi OR x < rect.valLo THEN {hits.count _ 0; RETURN}}; hits.count _ 1; hits.params[1] _ t; hits.normals[1] _ [rect.A, rect.B, 0]; }; -- end of EdgeOnRectCast globalFaceHitCount: NAT; globalFaceHitMaxCount: NAT = 20; globalFaceHits: ARRAY [1..globalFaceHitMaxCount] OF FaceHitRec; GetFaceHitFromPool: PUBLIC PROC RETURNS [faceHit: FaceHitRec] = { faceHit _ globalFaceHits[globalFaceHitCount]; globalFaceHitCount _ globalFaceHitCount - 1; }; ReturnFaceHitToPool: PUBLIC PROC [faceHit: FaceHitRec] = { globalFaceHitCount _ globalFaceHitCount + 1; globalFaceHits[globalFaceHitCount] _ faceHit; }; Init: PROC = { globalFaceHitCount _ globalFaceHitMaxCount; FOR i: NAT IN[1..globalFaceHitMaxCount] DO globalFaceHits[i] _ NEW[FaceHitRecObj]; ENDLOOP; }; Init[]; END.