<> <> <> <> <> DIRECTORY Algebra3d, CastRays, ObjectCast, CSG, Rope, SV2d, SV3d, Feedback, SVModelTypes, SVRayTypes, SVVector3d; ObjectCastImplA: CEDAR PROGRAM IMPORTS Algebra3d, CastRays, CSG, Feedback, SVVector3d EXPORTS ObjectCast = BEGIN Classification: TYPE = SVRayTypes.Classification; Composite: TYPE = SVRayTypes.Composite; CSGTree: TYPE = SVRayTypes.CSGTree; LightSourceList: TYPE = SVModelTypes.LightSourceList; Matrix4by4: TYPE = SV3d.Matrix4by4; Point2d: TYPE = SV2d.Point2d; Point3d: TYPE = SV3d.Point3d; PointSetOp: TYPE = SVRayTypes.PointSetOp; Primitive: TYPE = SVRayTypes.Primitive; Ray: TYPE = SVRayTypes.Ray; Surface: TYPE = REF ANY; Vector3d: TYPE = SV3d.Vector3d; RectSurface: TYPE = ObjectCast.RectSurface; RectType: TYPE = ObjectCast.RectType;-- {up, down, front, back, left, right}; TubeSurface: TYPE = ObjectCast.TubeSurface; DiskSurface: TYPE = ObjectCast.DiskSurface; DiskType: TYPE = ObjectCast.DiskType;-- {top, bottom}; ShellSurface: TYPE = ObjectCast.ShellSurface; HitRec: TYPE = REF HitRecObj; HitRecObj: TYPE = ObjectCast.HitRecObj; HitArray: TYPE = REF HitArrayObj; HitArrayObj: TYPE = ObjectCast.HitArrayObj; SurfaceArray: TYPE = SVRayTypes.SurfaceArray; ParameterArray: TYPE = SVRayTypes.ParameterArray; InOutArray: TYPE = SVRayTypes.InOutArray; NormalArray: TYPE = SVRayTypes.NormalArray; globalHitArray: HitArray; globalHitArray2: HitArray; BlockCast: PUBLIC PROC [localRay: Ray, surfs: SurfaceArray, prim: Primitive] RETURNS [class: Classification] = { <> <> <> << -d[2] < p[1]d[2] + d[1] - p[2]d[1] < d[2] x test for top face -d[2] < p[3]d[2] + d[3] - p[2]d[3] < d[2] z test for top face -d[2] < p[1]d[2] - d[1] - p[2]d[1] < d[2] x test for bottom face -d[2] < p[3]d[2] - d[3] - p[2]d[3] < d[2] z test for bottom face>> << -d[1] < p[2]d[1] + d[2] - p[1]d[2] < d[1] y test for right face -d[1] < p[3]d[1] + d[3] - p[1]d[3] < d[1] z test for right face -d[1] < p[2]d[1] - d[2] - p[1]d[2] < d[1] y test for right face -d[1] < p[3]d[1] - d[3] - p[1]d[3] < d[1] z test for right face change the sign of the limit d[*] in d[*] < * < d[*] if d[*] is negative notice that with the six p(*)d(*) cross terms, we can do all of these tests>> t: REAL; xd, yd, zd, D: REAL; hits: HitArray _ globalHitArray; p1d2, p1d3, p2d1, p2d3, p3d1, p3d2: REAL; p: Point3d; d: Vector3d; hitCount: NAT _ 0; doesHit: BOOL; upS, downS, frontS, backS, rightS, leftS: Surface; almostZero: REAL _ 1.0e-12; upS _ surfs[1]; downS _ surfs[2]; frontS _ surfs[3]; backS _ surfs[4]; rightS _ surfs[5]; leftS _ surfs[6]; class _ CastRays.GetClassFromPool[]; [p, d] _ CSG.GetLocalRay[localRay]; p1d2 _ p[1]*d[2]; p1d3 _ p[1]*d[3]; p2d1 _ p[2]*d[1]; p2d3 _ p[2]*d[3]; p3d1 _ p[3]*d[1]; p3d2 _ p[3]*d[2]; <> D _ Abs[d[3]]; IF D > almostZero THEN { <> xd _ p1d3 + d[1] - p3d1; IF -D <= xd AND xd <= D THEN { yd _ p2d3 + d[2] - p3d2; IF -D <= yd AND yd <= D THEN doesHit _ TRUE ELSE doesHit _ FALSE} ELSE doesHit _ FALSE; IF doesHit THEN { t _ (1.0 - p[3])/d[3]; IF t>0 THEN { hitCount _ hitCount + 1; hits[hitCount]^ _ [t, frontS, [0,0,1]]; }; }; <> xd _ p1d3 - d[1] - p3d1; IF -D <= xd AND xd <= D THEN { yd _ p2d3 - d[2] - p3d2; IF -D <= yd AND yd <= D THEN doesHit _ TRUE ELSE doesHit _ FALSE} ELSE doesHit _ FALSE; IF doesHit THEN { t _ (-1.0 - p[3])/d[3]; IF t>0 THEN { hitCount _ hitCount + 1; hits[hitCount]^ _ [t, backS, [0,0,-1]]; }; }; }; <> D _ Abs[d[2]]; IF D > almostZero THEN { <> xd _ p1d2 + d[1] - p2d1; IF -D <= xd AND xd <= D THEN { zd _ p3d2 + d[3] - p2d3; IF -D <= zd AND zd <= D THEN doesHit _ TRUE ELSE doesHit _ FALSE} ELSE doesHit _ FALSE; IF doesHit THEN { t _ (1.0 - p[2])/d[2]; IF t>0 THEN { hitCount _ hitCount + 1; hits[hitCount]^ _ [t, upS, [0,1,0]]; }; }; <> xd _ p1d2 - d[1] - p2d1; IF -D <= xd AND xd <= D THEN { zd _ p3d2 - d[3] - p2d3; IF -D <= zd AND zd <= D THEN doesHit _ TRUE ELSE doesHit _ FALSE} ELSE doesHit _ FALSE; IF doesHit THEN { t _ (-1.0 - p[2])/d[2]; IF t>0 THEN { hitCount _ hitCount + 1; hits[hitCount]^ _ [t, downS, [0,-1,0]]; }; }; }; <> D _ Abs[d[1]]; IF D > almostZero THEN { <> yd _ p2d1 + d[2] - p1d2; IF -D <= yd AND yd <= D THEN { zd _ p3d1 + d[3] - p1d3; IF -D <= zd AND zd <= D THEN doesHit _ TRUE ELSE doesHit _ FALSE} ELSE doesHit _ FALSE; IF doesHit THEN { t _ (1.0 - p[1])/d[1]; IF t>0 THEN { hitCount _ hitCount + 1; hits[hitCount]^ _ [t, rightS, [1,0,0]]; }; }; <> yd _ p2d1 - d[2] - p1d2; IF -D <= yd AND yd <= D THEN { zd _ p3d1 - d[3] - p1d3; IF -D <= zd AND zd <= D THEN doesHit _ TRUE ELSE doesHit _ FALSE} ELSE doesHit _ FALSE; IF doesHit THEN { t _ (-1.0 - p[1])/d[1]; IF t>0 THEN { hitCount _ hitCount + 1; hits[hitCount]^ _ [t, leftS, [-1,0,0]]; }; }; }; SELECT hitCount FROM 0 => { -- ray misses block completely class.count _ 0; class.classifs[1] _ FALSE; }; 1 => { -- ray skims an edge and for some reason is noticed by only one surface or ray originates from inside the cube. If the later is true, class has one hit and classifs are TRUE, FALSE. IF p[1] > -1.0 AND p[1] < 1.0 AND p[2] > -1.0 AND p[2] < 1.0 AND p[3] > -1.0 AND p[3] < 1.0 THEN { -- ray starts from inside the cube. class.count _ 1; class.params[1] _ hits[1].t; class.surfaces[1] _ hits[1].surf; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.normals[1] _ hits[1].normal; class.primitives[1] _ prim; } ELSE { -- Count as a miss. CastRays.MakeClassAMiss[class]; }; }; 2 => { -- ray enters and exits block properly <> class.count _ 2; IF hits[1].t < hits[2].t THEN { class.params[1] _ hits[1].t; class.params[2] _ hits[2].t; class.surfaces[1] _ hits[1].surf; class.surfaces[2] _ hits[2].surf; class.normals[1] _ hits[1].normal; class.normals[2] _ hits[2].normal;} ELSE { class.params[2] _ hits[1].t; class.params[1] _ hits[2].t; class.surfaces[2] _ hits[1].surf; class.surfaces[1] _ hits[2].surf; class.normals[2] _ hits[1].normal; class.normals[1] _ hits[2].normal;}; class.classifs[1] _ FALSE; class.classifs[2] _ TRUE; class.classifs[3] _ FALSE; class.primitives[1] _ class.primitives[2] _ prim; }; 3 => { -- ray hits a vertex and is noticed by all 3 surfaces. Treat as two hits for now using the frontmost and rearmost values of class.params. perm: Permutation _ Rank[[hits[1].t, hits[2].t, hits[3].t]]; class.count _ 2; class.params[1] _ hits[perm[1]].t; class.params[2] _ hits[perm[3]].t; class.surfaces[1] _ hits[perm[1]].surf; class.surfaces[2] _ hits[perm[3]].surf; class.normals[1] _ hits[perm[1]].normal; class.normals[2] _ hits[perm[3]].normal; class.classifs[1] _ FALSE; class.classifs[2] _ TRUE; class.classifs[3] _ FALSE; class.primitives[1] _ class.primitives[2] _ prim; Feedback.AppendTypescriptRaw[$Solidviews, "Point of interest: Some block was hit 3 times", oneLiner]; }; ENDCASE => SIGNAL WierdHitCount; }; -- end of BlockCast WierdHitCount: SIGNAL = CODE; Permutation: TYPE = ARRAY [1..3] OF NAT; Rank: PROC [t: ARRAY[1..3] OF REAL] RETURNS [perm: Permutation] = { tempT: REAL; tempP: NAT; perm _ [1, 2, 3]; FOR i: NAT IN[1..2] DO FOR j: NAT IN[1..3-i] DO IF t[j] > t[j+1] THEN { tempT _ t[j]; tempP _ perm[j]; t[j] _ t[j+1]; perm[j] _ perm[j+1]; t[j+1] _ tempT; perm[j+1] _ tempP;}; ENDLOOP; ENDLOOP; }; -- end of Rank PositiveRoots: PROCEDURE [a, b, c: REAL] RETURNS [rootArray: ARRAY[1..2] OF REAL, rootCount, totalRealRoots: 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; }; AllRoots: PROCEDURE [a, b, c: REAL] RETURNS [rootArray: ARRAY[1..2] OF REAL, positiveRootCount, totalRealRoots: NAT] = { <> positiveRootCount _ 0; [rootArray, totalRealRoots] _ Algebra3d.QuadraticFormula[a, b, c]; IF totalRealRoots = 0 THEN RETURN; FOR i: NAT IN [1..totalRealRoots] DO IF rootArray[i] <= 0 THEN positiveRootCount _ positiveRootCount + 1; ENDLOOP; }; SphereCast: PUBLIC PROC [localRay: Ray, thisShell: Surface, prim: Primitive] RETURNS [class: Classification] = { <> <> <> r: REAL = 1.0; <> << x = p1 + t*d1>> << y = p2 + t*d2>> << z = p3 + t*d3>> <> <<(p1 + t*d1)^2 + (p2 + t*d2)^2 + (p3 + t*d3)^2 = r^2>> <> <> <<(p1^2 + p2^2 + p3^2 - r^2) = 0;>> <> <> p: Point3d; d: Vector3d; tArray: ARRAY[1..2] OF REAL; rootCount: NAT; dp: REAL; dp2: REAL; dd: REAL; pp: REAL; t1, t2: REAL; [p, d] _ CSG.GetLocalRay[localRay]; dp _ SVVector3d.DotProduct[d,p]; dp2 _ dp*dp; dd _ SVVector3d.DotProduct[d,d]; pp _ SVVector3d.DotProduct[p,p]; class _ CastRays.GetClassFromPool[]; [tArray, rootCount, ----] _ PositiveRoots[dd,2*dp,pp-r*r]; <<>> SELECT rootCount FROM 0 => { class.count _ 0; class.classifs[1] _ FALSE; }; 1 => { <> IF pp <1.0 THEN { -- ray originates inside the sphere class.count _ 1; t1 _ tArray[1]; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.params[1] _ t1; class.surfaces[1] _ thisShell; class.normals[1][1] _ p[1] + d[1]*t1; class.normals[1][2] _ p[2] + d[2]*t1; class.normals[1][3] _ p[3] + d[3]*t1; class.primitives[1] _ prim; } ELSE CastRays.MakeClassAMiss[class]; }; 2 => { class.count _ 2; t1 _ tArray[1]; t2 _ tArray[2]; -- since tArray[1] < tArray[2] is guaranteed class.params[1] _ t1; class.params[2] _ t2; class.surfaces[1] _ thisShell; class.surfaces[2] _ thisShell; class.classifs[1] _ FALSE; class.classifs[2] _ TRUE; class.classifs[3] _ FALSE; class.primitives[1] _ class.primitives[2] _ prim; class.normals[1][1] _ p[1] + d[1]*t1; class.normals[1][2] _ p[2] + d[2]*t1; class.normals[1][3] _ p[3] + d[3]*t1; class.normals[2][1] _ p[1] + d[1]*t2; class.normals[2][2] _ p[2] + d[2]*t2; class.normals[2][3] _ p[3] + d[3]*t2; }; ENDCASE => ERROR; }; CylinderCast: PUBLIC PROC [localRay: Ray, surfaces: SurfaceArray, prim: Primitive] RETURNS [class: Classification] = { <<2 ray-plane tests and 1 ray-tube test>> <> <> << y = y0 + t*Dy>> << z = z0 + t*Dz>> <> <<(x0 + tDx)^2 + (z0 + tDz)^2 = r^2 for yLow <= y <= yHigh>> <> <> <> <> yLow: REAL = -1.0; yHigh: REAL = 1.0; <> p: Point3d; d: Vector3d; tArray: ARRAY[1..2] OF REAL; a, b, c: REAL;-- the quadratic formula variables positiveRootCount, totalRealRoots: NAT; almostZero: REAL _ 1.0e-15; t, fromYaxis2, y, tempT: REAL; tubeHits: HitArray _ globalHitArray; tubeHitCount: NAT _ 0; planeHits: HitArray _ globalHitArray2; planeHitCount: NAT _ 0; totalHits: NAT; topS, bottomS, tubeS, tempSurface: Surface; <> tubeNormal, tempNormal: Vector3d; p1d2, p2d1, p2d3, p3d2, d2d2, xd2, zd2: REAL; [p, d] _ CSG.GetLocalRay[localRay]; topS _ surfaces[1]; bottomS _ surfaces[2]; tubeS _ surfaces[3]; class _ CastRays.GetClassFromPool[]; <> 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] - 1.0;-- r*r = 1.0 [tArray, positiveRootCount, totalRealRoots] _ AllRoots[a, b, c]; <> <<.top face and bottom face>> IF Abs[d[2]] > almostZero THEN { <> <> <> <> <> <> <> <> <> <> p1d2 _ p[1]*d[2]; p2d1 _ p[2]*d[1]; p2d3 _ p[2]*d[3]; p3d2 _ p[3]*d[2]; d2d2 _ d[2]*d[2]; xd2 _ p1d2 + d[1] - p2d1; zd2 _ p3d2 + d[3] - p2d3; fromYaxis2 _ xd2*xd2 + zd2*zd2; IF fromYaxis2 < d2d2-- r*r = 1.0 THEN { t _ (1 - p[2])/d[2]; IF t > 0 THEN { planeHitCount _ planeHitCount + 1; planeHits[planeHitCount]^ _ [t, topS, [0,1.0,0] ]; }; }; <> xd2 _ p1d2 - d[1] - p2d1; zd2 _ p3d2 - d[3] - p2d3; fromYaxis2 _ xd2*xd2 + zd2*zd2; IF fromYaxis2 < d2d2-- r*r = 1.0 THEN { t _ (yLow - p[2])/d[2]; IF t > 0 THEN { planeHitCount _ planeHitCount + 1; planeHits[planeHitCount]^ _ [t, bottomS, [0,-1.0,0] ] }; }; -- end check ray-plane intersections }; <> IF planeHitCount = 2 THEN { IF planeHits[1].t > planeHits[2].t THEN { tempT _ planeHits[1].t; tempSurface _ planeHits[1].surf; tempNormal _ planeHits[1].normal; planeHits[1].t _ planeHits[2].t; planeHits[1].surf _ planeHits[2].surf; planeHits[1].normal _ planeHits[2].normal; planeHits[2].t _ tempT; planeHits[2].surf _ tempSurface; planeHits[2].normal _ tempNormal; }; }; totalHits _ planeHitCount + positiveRootCount; SELECT totalRealRoots FROM 0 => {-- ray does not hit tube but may pass through the planes on the ends SELECT planeHitCount FROM 0=> { class.count _ 0; class.classifs[1] _ FALSE; }; 1=> { <> IF p[2] > yLow AND p[2] < yHigh AND c < 0.0 THEN { class.count _ 1; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.params[1] _ planeHits[1].t; class.normals[1] _ planeHits[1].normal; class.surfaces[1] _ planeHits[1].surf; class.primitives[1] _ prim; } ELSE CastRays.MakeClassAMiss[class]; }; 2 => { class.count _ 2; class.params[1] _ planeHits[1].t; -- we know planeHits[1].t < planeHits[2].t class.params[2] _ planeHits[2].t; class.surfaces[1] _ planeHits[1].surf; class.surfaces[2] _ planeHits[2].surf; class.normals[1] _ planeHits[1].normal; class.normals[2] _ planeHits[2].normal; class.classifs[1] _ FALSE; class.classifs[2] _ TRUE; class.classifs[3] _ FALSE; class.primitives[1] _ class.primitives[2] _ prim; }; -- end 0 tube, 2 disk ENDCASE => ERROR; }; -- end of zero tube hits 1 => { <> CastRays.MakeClassAMiss[class]; <> <> < 0 THEN ERROR; -- could actually happen if d[1]=d[3] = 0;>> <> <> <> <> <> <> <> <> <> <> <<}>> <> <> <<};>> }; 2 => { <> <> <> <> <> tubeHitCount _ 0; y _ p[2] + tArray[1]*d[2]; IF yLow <= y AND y <= yHigh THEN {-- first tube hit in bounds tubeHitCount _ tubeHitCount + 1; tubeNormal[1] _ p[1] + tArray[1]*d[1]; tubeNormal[2] _ 0; tubeNormal[3] _ p[3] + tArray[1]*d[3]; tubeHits[tubeHitCount]^ _ [tArray[1], tubeS, tubeNormal]}; y _ p[2] + tArray[2]*d[2]; IF yLow <= y AND y <= yHigh THEN {-- second tube hit in bounds tubeHitCount _ tubeHitCount + 1; tubeNormal[1] _ p[1] + tArray[2]*d[1]; tubeNormal[2] _ 0; tubeNormal[3] _ p[3] + tArray[2]*d[3]; tubeHits[tubeHitCount]^ _ [tArray[2], tubeS, tubeNormal]}; SELECT tubeHitCount FROM 2 => { -- both intersections are at the tube IF planeHitCount >1 THEN ERROR; -- planeHitCount could be 1 if the ray enters in the middle of one tube and exits at a brim. Ignore the plane intersection. <> IF tArray[1] <= 0 THEN { IF tArray[2] <= 0 THEN CastRays.MakeClassAMiss[class] ELSE { class.count _ 1; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.params[1] _ tubeHits[2].t; class.normals[1] _ tubeHits[2].normal; class.surfaces[1] _ tubeS; class.primitives[1] _ prim; }; RETURN; }; class.count _ 2; class.params[1] _ tubeHits[1].t; -- we know that tubeHits[1].t < tubeHits[2].t class.params[2] _ tubeHits[2].t; class.normals[1] _ tubeHits[1].normal; class.normals[2] _ tubeHits[2].normal; class.surfaces[1] _ tubeS; class.surfaces[2] _ tubeS; class.classifs[1] _ FALSE; class.classifs[2] _ TRUE; class.classifs[3] _ FALSE; class.primitives[1] _ class.primitives[2] _ prim; }; 1 => { -- one line intersection on the tube. The other is out of bounds. This must be an angled ray. It should either originate from inside or hit a disk. SELECT planeHitCount FROM 0 => { -- originate from inside? IF p[2] > yLow AND p[2] < yHigh AND c < 0.0 THEN { class.count _ 1; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.params[1] _ planeHits[1].t; class.normals[1] _ planeHits[1].normal; class.surfaces[1] _ planeHits[1].surf; class.primitives[1] _ prim; } ELSE CastRays.MakeClassAMiss[class]; }; 1 => { <> IF tubeHits[1].t <= 0 THEN { IF p[2] > yLow AND p[2] < yHigh AND c < 0.0 THEN { class.count _ 1; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.params[1] _ planeHits[1].t; class.normals[1] _ planeHits[1].normal; class.surfaces[1] _ planeHits[1].surf; class.primitives[1] _ prim; } ELSE CastRays.MakeClassAMiss[class]; RETURN; }; IF planeHits[1].t <=0 THEN { IF p[2] > yLow AND p[2] < yHigh AND c < 0.0 THEN { class.count _ 1; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.params[1] _ tubeHits[1].t; class.normals[1] _ tubeHits[1].normal; class.surfaces[1] _ tubeS; class.primitives[1] _ prim; } ELSE CastRays.MakeClassAMiss[class]; RETURN; }; class.count _ 2; IF tubeHits[1].t < planeHits[1].t THEN { class.params[1] _ tubeHits[1].t; class.params[2] _ planeHits[1].t; class.surfaces[1] _ tubeHits[1].surf; class.surfaces[2] _ planeHits[1].surf; class.normals[1] _ tubeHits[1].normal; class.normals[2] _ planeHits[1].normal;} ELSE { class.params[2] _ tubeHits[1].t; class.params[1] _ planeHits[1].t; class.surfaces[2] _ tubeHits[1].surf; class.surfaces[1] _ planeHits[1].surf; class.normals[2] _ tubeHits[1].normal; class.normals[1] _ planeHits[1].normal;}; class.classifs[1] _ FALSE; class.classifs[2] _ TRUE; class.classifs[3] _ FALSE; class.primitives[1] _ class.primitives[2] _ prim; }; 2 => {-- we split a disk-plane boundary. Use both plane hits. <> IF planeHits[1].t <=0 THEN { IF p[2] > yLow AND p[2] < yHigh AND c < 0.0 THEN { class.count _ 1; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.params[1] _ planeHits[2].t; class.normals[1] _ planeHits[2].normal; class.surfaces[1] _ planeHits[2].surf; class.primitives[1] _ prim; } ELSE CastRays.MakeClassAMiss[class]; RETURN; }; IF planeHits[2].t <= 0 THEN { IF p[2] > yLow AND p[2] < yHigh AND c < 0.0 THEN { class.count _ 1; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.params[1] _ planeHits[1].t; class.normals[1] _ planeHits[1].normal; class.surfaces[1] _ planeHits[1].surf; class.primitives[1] _ prim; } ELSE CastRays.MakeClassAMiss[class]; RETURN; }; class.count _ 2; IF planeHits[1].t < planeHits[2].t THEN { class.params[1] _ planeHits[1].t; class.params[2] _ planeHits[2].t; class.surfaces[1] _ planeHits[1].surf; class.surfaces[2] _ planeHits[2].surf; class.normals[1] _ planeHits[1].normal; class.normals[2] _ planeHits[2].normal;} ELSE { class.params[2] _ planeHits[1].t; class.params[1] _ planeHits[2].t; class.surfaces[2] _ planeHits[1].surf; class.surfaces[1] _ planeHits[2].surf; class.normals[2] _ planeHits[1].normal; class.normals[1] _ planeHits[2].normal;}; class.classifs[1] _ FALSE; class.classifs[2] _ TRUE; class.classifs[3] _ FALSE; class.primitives[1] _ class.primitives[2] _ prim; }; ENDCASE => ERROR;-- can't have four hits total (for now) }; 0 => { -- both intersections are at the disk faces SELECT planeHitCount FROM 0 => { -- a miss CastRays.MakeClassAMiss[class]; }; 1 => { -- perhaps skims a rim but is only noticed by the disk, not the tube. Or perhaps the ray originates from inside the cylinder just below a disk face. IF p[2] > yLow AND p[2] < yHigh AND c < 0.0 THEN { class.count _ 1; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.params[1] _ planeHits[1].t; class.normals[1] _ planeHits[1].normal; class.surfaces[1] _ planeHits[1].surf; class.primitives[1] _ prim; } ELSE CastRays.MakeClassAMiss[class]; }; 2 => { -- goes through both disk faces <> IF planeHits[1].t <=0 THEN { IF p[2] > yLow AND p[2] < yHigh AND c < 0.0 THEN { class.count _ 1; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.params[1] _ planeHits[2].t; class.normals[1] _ planeHits[2].normal; class.surfaces[1] _ planeHits[2].surf; class.primitives[1] _ prim; } ELSE CastRays.MakeClassAMiss[class]; RETURN; }; IF planeHits[2].t <= 0 THEN { IF p[2] > yLow AND p[2] < yHigh AND c < 0.0 THEN { class.count _ 1; class.classifs[1] _ TRUE; class.classifs[2] _ FALSE; class.params[1] _ planeHits[1].t; class.normals[1] _ planeHits[1].normal; class.surfaces[1] _ planeHits[1].surf; class.primitives[1] _ prim; } ELSE CastRays.MakeClassAMiss[class]; RETURN; }; class.count _ 2; IF planeHits[1].t < planeHits[2].t THEN { class.params[1] _ planeHits[1].t; class.params[2] _ planeHits[2].t; class.surfaces[1] _ planeHits[1].surf; class.surfaces[2] _ planeHits[2].surf; class.normals[1] _ planeHits[1].normal; class.normals[2] _ planeHits[2].normal;} ELSE { class.params[2] _ planeHits[1].t; class.params[1] _ planeHits[2].t; class.surfaces[2] _ planeHits[1].surf; class.surfaces[1] _ planeHits[2].surf; class.normals[2] _ planeHits[1].normal; class.normals[1] _ planeHits[2].normal;}; class.classifs[1] _ FALSE; class.classifs[2] _ TRUE; class.classifs[3] _ FALSE; class.primitives[1] _ class.primitives[2] _ prim; }; ENDCASE => ERROR; -- can't have more than 2 plane hits <> }; ENDCASE => ERROR; -- end of select on tubeHitCount with 2 tube roots }; -- end of 2 tube roots ENDCASE => ERROR; -- end of select on number of tube roots }; -- end of procedure CylinderCast Abs: PROC [r: REAL] RETURNS [REAL] = { RETURN [IF r>=0 THEN r ELSE -r]; }; Init: PROC = { <> globalHitArray _ NEW[HitArrayObj]; globalHitArray2 _ NEW[HitArrayObj]; FOR i: NAT IN[1..ObjectCast.globalObjDepth] DO globalHitArray[i] _ NEW[HitRecObj]; globalHitArray2[i] _ NEW[HitRecObj]; ENDLOOP; }; Init[]; END.