<> <> <> <> DIRECTORY AIS, CastRays, ConvertUnsafe, CoordSys, CSG, CSGGraphics, DisplayList3d, Graphics, GraphicsColor, IO, Matrix3d, MessageWindow, Preprocess3d, Real, Rope, Shading, SVArtwork, SVBoundBox, SVFancyRays, SVImage, SVVector3d, UnsafeStorage; CastRaysImpl: PROGRAM IMPORTS ConvertUnsafe, CoordSys, GraphicsColor, IO, Matrix3d, MessageWindow, Preprocess3d, Real, Rope, Shading, SVArtwork, SVBoundBox, SVFancyRays, SVImage, SVVector3d, UnsafeStorage EXPORTS CastRays = BEGIN BoundBox: TYPE = REF BoundBoxObj; BoundBoxObj: TYPE = SVBoundBox.BoundBoxObj; Camera: TYPE = CSGGraphics.Camera; Color: TYPE = GraphicsColor.Color; CoordSystem: TYPE = REF CoordSysObj; CoordSysObj: TYPE = CoordSys.CoordSysObj; LightSourceList: TYPE = Shading.LightSourceList; NotifyOfProgressProc: TYPE = CastRays.NotifyOfProgressProc; Point3d: TYPE = Matrix3d.Point3d; Point2d: TYPE = Matrix3d.Point2d; Matrix4by4: TYPE = Matrix3d.Matrix4by4; Vector: TYPE = SVVector3d.Vector; Surface: TYPE = REF ANY; Primitive: TYPE = REF PrimitiveObj; PrimitiveObj: TYPE = CSG.PrimitiveObj; Composite: TYPE = REF CompositeObj; CompositeObj: TYPE = CSG.CompositeObj; CSGTree: TYPE = REF CSGTreeObj; CSGTreeObj: TYPE = CSG.CSGTreeObj; PointSetOp: TYPE = CSG.PointSetOp; <<{union, intersection, difference}>> Classification: TYPE = REF ClassificationObj; ClassificationObj: TYPE = CastRays.ClassificationObj; Ray: TYPE = REF RayObj; RayObj: TYPE = CSG.RayObj; SurfaceArray: TYPE = REF SurfaceArrayObj; SurfaceArrayObj: TYPE = CSG.SurfaceArrayObj; ParameterArray: TYPE = CSG.ParameterArray; -- ARRAY [1..maxSceneDepth] OF REAL; InOutArray: TYPE = CSG.InOutArray; -- ARRAY [1..maxSceneDepth] OF BOOL; NormalArray: TYPE = CSG.NormalArray; -- ARRAY [1..maxSceneDepth] OF Vector; PrimitiveArray: TYPE = CSG.PrimitiveArray; -- ARRAY [1..maxSurfacesPerObject] OF Primitive; CompactArray: TYPE = REF CompactArrayObj; CompactArrayObj: TYPE = ARRAY [1..CSG.maxSceneDepth] OF BOOL; Image: TYPE = REF ImageObj; ImageObj: TYPE = SVImage.ImageObj; globalPoolCount: NAT = 10; globalPoolPointer: NAT; Pool: TYPE = REF PoolObj; PoolObj: TYPE = RECORD [seq: SEQUENCE maxClasses: NAT OF Classification]; globalPool: Pool; globalRayPoolCount: NAT = 15; globalRayPoolPointer: NAT; RayPool: TYPE = REF RayPoolObj; RayPoolObj: TYPE = ARRAY[1..globalRayPoolCount] OF Ray; globalRayPool: RayPool; globalCompactPoolCount: NAT = 10; globalCompactPoolPointer: NAT; CompactPool: TYPE = REF CompactPoolObj; CompactPoolObj: TYPE = ARRAY[1..globalCompactPoolCount] OF CompactArray; globalCompactPool: CompactPool; WriteStreamComp: PRIVATE PROC [comp: Composite, class: Classification, makeStream: BOOL, f: IO.STREAM, indent: NAT] = { <> <> opname: Rope.ROPE; IF NOT makeStream THEN RETURN; Indent[f, indent]; SELECT comp.operation FROM union => opname _ "union"; intersection => opname _ "intersection"; difference => opname _ "difference"; ENDCASE => ERROR; f.PutF["Composite %g [op: %g] returns class: [count: %g]\n",[rope[comp.name]],[rope[opname]], [integer[class.count]]]; WritePrimNames[class, f, indent]; }; -- end of WriteStreamComp Indent: PRIVATE PROC [f: IO.STREAM, indent: NAT] = { FOR i: NAT IN[1..indent] DO f.PutChar[IO.TAB]; ENDLOOP; }; WritePrimNames: PRIVATE PROC [class: Classification, f: IO.STREAM, indent: NAT] = { FOR i: NAT IN[1..class.count] DO Indent[f, indent+1]; f.PutF["%g) %g at t = %g\n", [integer[i]], [rope[class.primitives[i].name]], [real[class.params[i]]]]; ENDLOOP; }; -- end of WritePrimNames WriteStreamPrim: PRIVATE PROC [prim: Primitive, class: Classification, makeStream: BOOL, f: IO.STREAM, indent: NAT] = { IF NOT makeStream THEN RETURN; Indent[f, indent]; f.PutF["Primitive %g returns class: [count: %g]\n", [rope[prim.name]], [integer[class.count]]]; WriteParams[class, f, indent]; }; -- end of WriteStreamPrim WriteParams: PRIVATE PROC [class: Classification, f: IO.STREAM, indent: NAT] = { FOR i: NAT IN[1..class.count] DO Indent[f, indent+1]; f.PutF["%g) %g at t = %g\n", [integer[i]], [rope[class.primitives[i].name]], [real[class.params[i]]]]; ENDLOOP; }; -- end of WritePrimNames RayCast: PUBLIC PROC [cameraPoint: Point2d, sceneRay: Ray, node: REF ANY, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL, indent: NAT _ 0] RETURNS [class: Classification] = { <> IF node = NIL THEN {class _ EmptyClass[]; RETURN}; WITH node SELECT FROM comp: Composite => { leftClass, rightClass: Classification; leftBoxHit, leftHit, rightBoxHit, rightHit: BOOL; totalMiss: BOOL _ FALSE; boundBox: BoundBox; <> <> <<1) Check ray for left bound box. Set leftBoxHit if appropriate.>> <<2) If leftBoxHit then cast the ray. Set leftHit if appropriate.>> <<3) If not leftHit then if comp.operation = intersection or difference, return miss.>> <<4) If hit, or union, then right box test. Set RightBoxMiss if appropriate.>> <<5) If miss then return: leftclass for difference, empty for intersection, leftClass for union. >> <<6) Else cast ray.>> <<7) Return rightclass or combination if appropriate>> <<1) Check ray for left bound box. Set leftBoxHit if appropriate. >> WITH comp.leftSolid SELECT FROM p: Primitive => boundBox _ p.boundBox; c: Composite => boundBox _ c.boundBox; ENDCASE => ERROR; leftBoxHit _ SVBoundBox.PointInBoundBox[cameraPoint, boundBox]; <<2) If leftBoxHit then cast the ray. Set leftHit if appropriate. >> IF leftBoxHit THEN { leftClass _ RayCast[cameraPoint, sceneRay, comp.leftSolid, makeStream, f, indent]; leftHit _ (leftClass.count > 0) } ELSE {leftHit _ FALSE; leftClass _ EmptyClass[]}; <<3) If not leftHit then if comp.operation = intersection or difference, return miss. >> IF NOT leftHit THEN IF comp.operation = intersection OR comp.operation = difference THEN { class _ leftClass; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN}; <> <<4) If hit, or union, then right box test. Set RightBoxMiss if appropriate. (we don't have to test for this state. It is the only one left.) >> WITH comp.rightSolid SELECT FROM p: Primitive => boundBox _ p.boundBox; c: Composite => boundBox _ c.boundBox; ENDCASE => ERROR; rightBoxHit _ SVBoundBox.PointInBoundBox[cameraPoint, boundBox]; <<5) If miss then return EmptyClass. Else cast ray. >> IF NOT rightBoxHit THEN <> SELECT comp.operation FROM union => {class _ leftClass; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN}; intersection => IF NOT leftHit THEN RETURN[leftClass] ELSE { ReturnClassToPool[leftClass]; class _ EmptyClass[]; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN}; difference => {class _ leftClass; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN}; ENDCASE => ERROR; <<6) Else cast ray. We have Union, or (intersection/difference) with left hit. Ray hits box. >> rightClass _ RayCast[cameraPoint, sceneRay, comp.rightSolid, makeStream, f, indent]; rightHit _ rightClass.count > 0; <<7) Return rightclass, combination or empty if appropriate >> SELECT comp.operation FROM union => IF rightHit THEN { IF leftHit THEN class _ UnionCombine[leftClass, rightClass] ELSE {ReturnClassToPool[leftClass]; class _ rightClass} } ELSE { ReturnClassToPool[rightClass]; class _ leftClass}; intersection => IF rightHit THEN { IF leftHit THEN class _ IntersectionCombine[leftClass, rightClass] ELSE {ReturnClassToPool[rightClass]; class _ leftClass;} } ELSE IF leftHit THEN {ReturnClassToPool[leftClass]; class _ rightClass} ELSE {ReturnClassToPool[rightClass]; class _ leftClass}; difference => IF rightHit THEN { IF leftHit THEN class _ DifferenceCombine[leftClass, rightClass] ELSE {ReturnClassToPool[rightClass]; class _ leftClass} -- leftClass null } ELSE {ReturnClassToPool[rightClass]; class _ leftClass}; ENDCASE => ERROR; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN}; prim: Primitive => { localRay: Ray; <> localRay _ TransformRay[sceneRay, prim.worldWRTPrim]; -- (takes a new ray from the pool) class _ prim.rayCast[cameraPoint, localRay, prim.mo, prim]; WriteStreamPrim[prim, class, makeStream, f, 0]; ReturnRayToPool[localRay]; -- returns ray to pool RETURN}; ENDCASE => ERROR; }; -- end of RayCast RayCastNoBBoxes: PUBLIC PROC [sceneRay: Ray, node: REF ANY, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL, indent: NAT _ 0] RETURNS [class: Classification] = { <> <> IF node = NIL THEN {class _ EmptyClass[]; RETURN}; WITH node SELECT FROM comp: Composite => { leftClass, rightClass: Classification; leftHit, rightHit: BOOL; totalMiss: BOOL _ FALSE; <> <<1) Cast the left ray. Set leftHit if appropriate.>> <<2) If not leftHit then if comp.operation = intersection or difference, return miss.>> <<3) If hit, or union, then cast right ray.>> <<4) Return rightclass or combination if appropriate>> <<1) Cast the left ray. Set leftHit if appropriate. >> leftClass _ RayCastNoBBoxes[sceneRay, comp.leftSolid, makeStream, f, indent]; leftHit _ (leftClass.count > 0); <<2) If not leftHit then if comp.operation = intersection or difference, return miss. >> IF NOT leftHit THEN IF comp.operation = intersection OR comp.operation = difference THEN { class _ leftClass; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN}; <> <<3) If hit, or union, then cast right ray. >> rightClass _ RayCastNoBBoxes[sceneRay, comp.rightSolid, makeStream, f, indent]; rightHit _ rightClass.count > 0; <<4) Return rightclass, combination or empty if appropriate >> SELECT comp.operation FROM union => IF rightHit THEN { IF leftHit THEN class _ UnionCombine[leftClass, rightClass] ELSE {ReturnClassToPool[leftClass]; class _ rightClass} } ELSE { ReturnClassToPool[rightClass]; class _ leftClass}; intersection => IF rightHit THEN { IF leftHit THEN class _ IntersectionCombine[leftClass, rightClass] ELSE {ReturnClassToPool[rightClass]; class _ leftClass;} } ELSE IF leftHit THEN {ReturnClassToPool[leftClass]; class _ rightClass} ELSE {ReturnClassToPool[rightClass]; class _ leftClass}; difference => IF rightHit THEN { IF leftHit THEN class _ DifferenceCombine[leftClass, rightClass] ELSE {ReturnClassToPool[rightClass]; class _ leftClass} -- leftClass null } ELSE {ReturnClassToPool[rightClass]; class _ leftClass}; ENDCASE => ERROR; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN}; prim: Primitive => { localRay: Ray; <> localRay _ TransformRay[sceneRay, prim.worldWRTPrim]; -- (takes a new ray from the pool) class _ prim.rayCastNoBBoxes[localRay, prim.mo, prim]; WriteStreamPrim[prim, class, makeStream, f, 0]; ReturnRayToPool[localRay]; -- returns ray to pool RETURN}; ENDCASE => ERROR; }; -- end of RayCastNoBboxes HitsTree: PUBLIC PROC [worldRay: Ray, tree: CSGTree] RETURNS [BOOL] = { node: REF ANY _ tree.son; class: Classification; hits: BOOL; class _ RayCastNoBBoxes [sceneRay: worldRay, node: node, makeStream: FALSE]; hits _ class.count > 0; ReturnClassToPool[class]; RETURN[hits]; }; FirstHit: PUBLIC PROC [worldRay: Ray, tree: CSGTree, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL, indent: NAT _ 0] RETURNS [hits: BOOL, t: REAL] = { <> node: REF ANY _ tree.son; class: Classification; class _ RayCastNoBBoxes [sceneRay: worldRay, node: node, makeStream: makeStream, f: f, indent: indent]; hits _ class.count > 0; IF hits THEN t _ class.params[1] ELSE t _ 0.0; ReturnClassToPool[class]; }; EmptyClass: PRIVATE PROC RETURNS [class: Classification] = { class _ GetClassFromPool[]; class.count _ 0; class.classifs[1] _ FALSE; }; -- end of EmptyClass TransformRay: PROC [ray: Ray, mat: Matrix4by4] RETURNS [newRay: Ray] = { newRay _ GetRayFromPool[]; newRay.basePt _ Matrix3d.Update[mat, ray.basePt]; newRay.direction _ Matrix3d.UpdateVectorEvenScaling[mat, ray.direction]; }; -- end of TransformRay AddRay: PROC [ray1, ray2: Ray] = { -- puts sum in ray2 ray2.basePt _ SVVector3d.Add[ray1.basePt, ray2.basePt]; ray2.direction _ SVVector3d.Add[ray1.direction, ray2.direction]; }; -- end of AddRay SubtractRays: PROC [ray1, ray2: Ray] RETURNS [ray1MinusRay2: Ray] = { -- puts sum in ray2 ray1MinusRay2 _ NEW[RayObj]; ray1MinusRay2.basePt _ SVVector3d.Difference[ray1.basePt, ray2.basePt]; ray1MinusRay2.direction _ SVVector3d.Difference[ray1.direction, ray2.direction]; }; -- end of AddRay <> Combine: PUBLIC PROC [leftClass, rightClass: Classification, op: PointSetOp] RETURNS [combinedClass: Classification] = { SELECT op FROM union => combinedClass _ UnionCombine[leftClass, rightClass]; intersection => combinedClass _ IntersectionCombine[leftClass, rightClass]; difference => combinedClass _ DifferenceCombine[leftClass, rightClass]; ENDCASE => ERROR; }; SceneExceedsMaximumDepth: SIGNAL = CODE; UnionCombine: PROC [leftClass, rightClass: Classification] RETURNS [combinedClass: Classification] = { <> lPtr, rPtr: NAT; combinedClass _ GetClassFromPool[]; lPtr _ rPtr _ 1; combinedClass.count _ leftClass.count + rightClass.count; IF combinedClass.count > CSG.maxSceneDepth THEN SIGNAL SceneExceedsMaximumDepth; FOR i: NAT IN[1..combinedClass.count] DO IF rPtr > rightClass.count THEN GOTO RPtrWentOver; IF lPtr > leftClass.count THEN GOTO LPtrWentOver; IF leftClass.params[lPtr] < rightClass.params[rPtr] THEN { combinedClass.normals[i] _ leftClass.normals[lPtr]; combinedClass.params[i] _ leftClass.params[lPtr]; combinedClass.surfaces[i] _ leftClass.surfaces[lPtr]; combinedClass.primitives[i] _ leftClass.primitives[lPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] OR rightClass.classifs[rPtr]; lPtr _ lPtr + 1; } ELSE { combinedClass.normals[i] _ rightClass.normals[rPtr]; combinedClass.params[i] _ rightClass.params[rPtr]; combinedClass.surfaces[i] _ rightClass.surfaces[rPtr]; combinedClass.primitives[i] _ rightClass.primitives[rPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] OR rightClass.classifs[rPtr]; rPtr _ rPtr + 1; }; REPEAT RPtrWentOver => { -- finish up with lPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ leftClass.normals[lPtr]; combinedClass.params[k] _ leftClass.params[lPtr]; combinedClass.surfaces[k] _ leftClass.surfaces[lPtr]; combinedClass.primitives[k] _ leftClass.primitives[lPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] OR rightClass.classifs[rPtr]; lPtr _ lPtr + 1; ENDLOOP}; LPtrWentOver => { -- finish up with rPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ rightClass.normals[rPtr]; combinedClass.params[k] _ rightClass.params[rPtr]; combinedClass.surfaces[k] _ rightClass.surfaces[rPtr]; combinedClass.primitives[k] _ rightClass.primitives[rPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] OR rightClass.classifs[rPtr]; rPtr _ rPtr + 1; ENDLOOP}; ENDLOOP; combinedClass.classifs[combinedClass.count+1] _ leftClass.classifs[lPtr] OR rightClass.classifs[rPtr]; ReturnClassToPool[leftClass]; ReturnClassToPool[rightClass]; }; -- end of UnionCombine IntersectionCombine: PROC [leftClass, rightClass: Classification] RETURNS [combinedClass: Classification] = { <> lPtr, rPtr: NAT; combinedClass _ GetClassFromPool[]; lPtr _ rPtr _ 1; combinedClass.count _ leftClass.count + rightClass.count; IF combinedClass.count > CSG.maxSceneDepth THEN SIGNAL SceneExceedsMaximumDepth; FOR i: NAT IN[1..combinedClass.count] DO IF rPtr > rightClass.count THEN GOTO RPtrWentOver; IF lPtr > leftClass.count THEN GOTO LPtrWentOver; IF leftClass.params[lPtr] < rightClass.params[rPtr] THEN { combinedClass.normals[i] _ leftClass.normals[lPtr]; combinedClass.params[i] _ leftClass.params[lPtr]; combinedClass.surfaces[i] _ leftClass.surfaces[lPtr]; combinedClass.primitives[i] _ leftClass.primitives[lPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] AND rightClass.classifs[rPtr]; lPtr _ lPtr + 1; } ELSE { combinedClass.normals[i] _ rightClass.normals[rPtr]; combinedClass.params[i] _ rightClass.params[rPtr]; combinedClass.surfaces[i] _ rightClass.surfaces[rPtr]; combinedClass.primitives[i] _ rightClass.primitives[rPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] AND rightClass.classifs[rPtr]; rPtr _ rPtr + 1; }; REPEAT RPtrWentOver => { -- finish up with lPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ leftClass.normals[lPtr]; combinedClass.params[k] _ leftClass.params[lPtr]; combinedClass.surfaces[k] _ leftClass.surfaces[lPtr]; combinedClass.primitives[k] _ leftClass.primitives[lPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] AND rightClass.classifs[rPtr]; lPtr _ lPtr + 1; ENDLOOP}; LPtrWentOver => { -- finish up with rPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ rightClass.normals[rPtr]; combinedClass.params[k] _ rightClass.params[rPtr]; combinedClass.surfaces[k] _ rightClass.surfaces[rPtr]; combinedClass.primitives[k] _ rightClass.primitives[rPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] AND rightClass.classifs[rPtr]; rPtr _ rPtr + 1; ENDLOOP}; ENDLOOP; combinedClass.classifs[combinedClass.count+1] _ leftClass.classifs[lPtr] AND rightClass.classifs[rPtr]; ConsolidateClassification[combinedClass]; ReturnClassToPool[leftClass]; ReturnClassToPool[rightClass]; }; -- end of IntersectionCombine ConsolidateClassification: PROC [class: Classification] = { <> <> currentlyWorkingOn: BOOL; compact: CompactArray _ GetCompactFromPool[]; IF class.classifs[1] # FALSE THEN SIGNAL RayClassBeginsWithTrue; currentlyWorkingOn _ class.classifs[1]; FOR i: NAT IN[2..class.count+1] DO IF class.classifs[i] = currentlyWorkingOn THEN -- this is not a transition so throw it out compact[i-1] _ FALSE -- don't keep it ELSE {compact[i-1] _ TRUE; currentlyWorkingOn _ class.classifs[i];}; ENDLOOP; CompactClassification[class, compact]; ReturnCompactToPool[compact]; }; -- end of ConsolidateClassification RayClassEndsWithTrue: SIGNAL = CODE; RayClassBeginsWithTrue: SIGNAL = CODE; CompactClassification: PROC [class: Classification, compact: CompactArray] = { <> newCount: NAT; newCount _ 0; FOR i: NAT IN[1..class.count] DO IF compact[i] THEN {newCount _ newCount + 1; class.params[newCount] _ class.params[i]; class.classifs[newCount] _ class.classifs[i]; class.normals[newCount] _ class.normals[i]; class.surfaces[newCount] _ class.surfaces[i]; class.primitives[newCount] _ class.primitives[i];}; ENDLOOP; class.classifs[newCount+1] _ class.classifs[class.count+1]; <> class.count _ newCount; }; DifferenceCombine: PROC [leftClass, rightClass: Classification] RETURNS [combinedClass: Classification] = { <> lPtr, rPtr: NAT; combinedClass _ GetClassFromPool[]; IF combinedClass.count > CSG.maxSceneDepth THEN SIGNAL SceneExceedsMaximumDepth; lPtr _ rPtr _ 1; combinedClass.count _ leftClass.count + rightClass.count; FOR i: NAT IN[1..combinedClass.count] DO IF rPtr > rightClass.count THEN GOTO RPtrWentOver; IF lPtr > leftClass.count THEN GOTO LPtrWentOver; IF leftClass.params[lPtr] < rightClass.params[rPtr] THEN { combinedClass.normals[i] _ leftClass.normals[lPtr]; combinedClass.params[i] _ leftClass.params[lPtr]; combinedClass.surfaces[i] _ leftClass.surfaces[lPtr]; combinedClass.primitives[i] _ leftClass.primitives[lPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] AND NOT rightClass.classifs[rPtr]; lPtr _ lPtr + 1; } ELSE { combinedClass.normals[i] _ SVVector3d.Negate[rightClass.normals[rPtr]]; combinedClass.params[i] _ rightClass.params[rPtr]; combinedClass.surfaces[i] _ rightClass.surfaces[rPtr]; combinedClass.primitives[i] _ rightClass.primitives[rPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] AND NOT rightClass.classifs[rPtr]; rPtr _ rPtr + 1; }; REPEAT RPtrWentOver => { -- finish up with lPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ leftClass.normals[lPtr]; combinedClass.params[k] _ leftClass.params[lPtr]; combinedClass.surfaces[k] _ leftClass.surfaces[lPtr]; combinedClass.primitives[k] _ leftClass.primitives[lPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] AND NOT rightClass.classifs[rPtr]; lPtr _ lPtr + 1; ENDLOOP}; LPtrWentOver => { -- finish up with rPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ SVVector3d.Negate[rightClass.normals[rPtr]]; combinedClass.params[k] _ rightClass.params[rPtr]; combinedClass.surfaces[k] _ rightClass.surfaces[rPtr]; combinedClass.primitives[k] _ rightClass.primitives[rPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] AND NOT rightClass.classifs[rPtr]; rPtr _ rPtr + 1; ENDLOOP}; ENDLOOP; combinedClass.classifs[combinedClass.count+1] _ leftClass.classifs[lPtr] AND NOT rightClass.classifs[rPtr]; ConsolidateClassification[combinedClass]; ReturnClassToPool[leftClass]; ReturnClassToPool[rightClass]; }; -- end of DifferenceCombine SingleRay: PUBLIC PROC [x, y: INTEGER, tree: CSGTree, lightSources: LightSourceList, camera: Camera, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL] RETURNS [color: Color] = { cameraRay, worldRay: Ray; cameraWRTWorld: Matrix3d.Matrix4by4; boundBox: BoundBox; cameraRay _ NEW[RayObj]; boundBox _ Preprocess3d.Preprocess[tree, camera]; -- must call this before casting rays cameraRay.basePt _ [x,y,0]; cameraRay.direction _ [x,y,-camera.focalLength]; <> <> cameraWRTWorld _ CoordSys.FindInTermsOfWorld[camera.coordSys]; worldRay _ TransformRay[cameraRay, cameraWRTWorld]; -- alocates ray from pool IF makeStream THEN f.PutChar[IO.CR]; color _ TopColorCast[[x,y], worldRay, tree, lightSources, camera, boundBox, makeStream, f, 0]; IF makeStream THEN f.PutChar[IO.CR]; ReturnRayToPool[worldRay]; }; -- end of SingleRay SingleRay2: PUBLIC PROC [cameraPoint: Point2d, tree: CSGTree, lightSources: LightSourceList, camera: Camera, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL] RETURNS [class: Classification] = { <> topNode: REF ANY _ tree.son; cameraRay, worldRay: Ray; cameraWRTWorld: Matrix4by4 _ CoordSys.FindInTermsOfWorld[camera.coordSys]; focalLength: REAL _ - camera.focalLength; cameraRay _ NEW[RayObj]; cameraRay.basePt _ [cameraPoint[1], cameraPoint[2], 0]; cameraRay.direction _ [cameraPoint[1], cameraPoint[2], focalLength]; worldRay _ TransformRay[cameraRay, cameraWRTWorld]; -- allocates ray from pool class _ RayCast[cameraPoint, worldRay, topNode, makeStream, f, 0]; ReturnRayToPool[worldRay]; }; -- end of SingleRay2 NodeToRope: PROC [node: REF ANY, depth: NAT] RETURNS [r: Rope.ROPE] = { IF node = NIL THEN RETURN[NIL]; WITH node SELECT FROM prim: Primitive => {r _ prim.name; RETURN}; comp: Composite => {r _ comp.name; IF depth < 2 THEN RETURN ELSE {r1: Rope.ROPE; r2: Rope.ROPE; leftSon: REF ANY _ comp.leftSolid; rightSon: REF ANY _ comp.rightSolid; r1 _ NodeToRope[leftSon, depth - 1]; r2 _ NodeToRope[rightSon, depth - 1]; r _ Rope.Cat[r,": ",r1,"/",r2]; RETURN}; }; ENDCASE => ERROR; }; -- end of NodeToRope OutputTreeInfo: PRIVATE PROC [node: REF ANY, I: Image, outStream: IO.STREAM] = { debugName, debugRope: Rope.ROPE; -- **** debugStream: IO.STREAM; debugStream _ IO.CreateOutputStreamToRope[]; debugName _ NodeToRope[node, 2]; debugStream.PutF["About to Draw Tree: %g (%g by %g)...", [rope[debugName]], [integer[I.bwWindow.fref.raster.scanCount]], [integer[I.bwWindow.fref.raster.scanLength]]]; outStream.PutF["About to Draw Tree: %g (%g by %g)...", [rope[debugName]], [integer[I.bwWindow.fref.raster.scanCount]], [integer[I.bwWindow.fref.raster.scanLength]]]; debugRope _ debugStream.GetOutputStreamRope[]; MessageWindow.Append[debugRope, TRUE]; MessageWindow.Blink[]; }; -- end of OutputTreeInfo GetXStepRayInWorld: PRIVATE PROC [stepSize: REAL, focalLength: REAL, cameraWRTWorld: Matrix4by4] RETURNS [ray: Ray] = { cameraXStepRay1, cameraXStepRay2: Ray; cameraXStepRay1InWorld, cameraXStepRay2InWorld: Ray; cameraXStepRay1 _ NEW[RayObj _ [[0,0,0], [0,0, focalLength]]]; cameraXStepRay2 _ NEW[RayObj _ [[stepSize,0,0], [stepSize,0, focalLength]]]; cameraXStepRay1InWorld _ TransformRay[cameraXStepRay1, cameraWRTWorld]; cameraXStepRay2InWorld _ TransformRay[cameraXStepRay2, cameraWRTWorld]; ray _ SubtractRays[cameraXStepRay2InWorld, cameraXStepRay1InWorld]; ReturnRayToPool[cameraXStepRay1InWorld]; ReturnRayToPool[cameraXStepRay2InWorld]; }; -- end of GetXStepRayInWorld MasterObjectColorFromPrimitive: PRIVATE PROC [primitive: Primitive, t: REAL, sceneRay: Ray, primitiveNormal: Vector] RETURNS [color: Color] = { scalars: Vector; localRay: Ray; point3d: Point3d; x, y, z: REAL; IF primitive.artwork.source = NIL THEN { -- pure color artwork color _ primitive.artwork.color; } ELSE { scalars _ primitive.scalars; localRay _ TransformRay[sceneRay, primitive.worldWRTPrim]; x _ localRay.basePt[1] + t*localRay.direction[1]; y _ localRay.basePt[2] + t*localRay.direction[2]; z _ localRay.basePt[3] + t*localRay.direction[3]; ReturnRayToPool[localRay]; point3d[1] _ scalars[1]*x; point3d[2] _ scalars[2]*y; point3d[3] _ scalars[3]*z; color _ SVArtwork.FindColorAtSurfacePoint[primitive.artwork, point3d, primitiveNormal]; }; }; ColorFromClass: PRIVATE PROC [class: Classification, x, y: REAL, lightSources: LightSourceList, camera: Camera, sceneRay: Ray, tree: CSGTree, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL, indent: NAT _ 0] RETURNS [color: Color] = { <> <> surf: Surface; surfColor: Color; eyePoint: Point3d; surfacePt: Point3d; primitive: Primitive; visibleLights: LightSourceList; t: REAL; worldNormal, primitiveNormal: Vector; IF class.count = 0 THEN {color _ tree.backgroundColor; RETURN}; surf _ class.surfaces[1]; t _ class.params[1];-- the parameter of the ray intersection primitive _ class.primitives[1]; primitiveNormal _ class.normals[1]; surfColor _ MasterObjectColorFromPrimitive[primitive, t, sceneRay, primitiveNormal]; worldNormal _ Matrix3d.UpdateVectorWithInverse[primitive.worldWRTPrim, primitiveNormal]; surfacePt[1] _ sceneRay.basePt[1] + t*sceneRay.direction[1]; surfacePt[2] _ sceneRay.basePt[2] + t*sceneRay.direction[2]; surfacePt[3] _ sceneRay.basePt[3] + t*sceneRay.direction[3]; <> eyePoint _ SVVector3d.Difference[sceneRay.basePt, sceneRay.direction]; visibleLights _ IF tree.shadows THEN SVFancyRays.VisibleLights[lightSources, surfacePt, tree, makeStream, f, indent] ELSE lightSources; <> SELECT primitive.artwork.material FROM chalk => color _ Shading.DiffuseReflectance[worldNormal, surfacePt, surfColor, visibleLights]; plastic => color _ Shading.DiffuseAndSpecularReflectance[eyePoint, worldNormal, surfacePt, surfColor, visibleLights]; ENDCASE => ERROR; }; -- end of ColorFromClass ScanLine: TYPE = REF ScanLineObj; ScanLineObj: TYPE = RECORD [ seq: SEQUENCE lineLen: NAT OF Color]; CreateScanLine: PRIVATE PROC [len: NAT] RETURNS [scanLine: ScanLine] = { scanLine _ NEW[ScanLineObj[len]]; }; CopyScanLine: PRIVATE PROC [from: ScanLine, to: ScanLine] = { FOR i: NAT IN [0..to.lineLen) DO to[i] _ from[i]; ENDLOOP; }; PutColorInScanLine: PRIVATE PROC [scanLine: ScanLine, index: NAT, color: Color] = { scanLine[index] _ color; }; TopColorCast: PRIVATE PROC [cameraPoint: Point2d, sceneRay: Ray, tree: CSGTree, lightSources: LightSourceList, camera: Camera, sceneBox: BoundBox, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL, indent: NAT _ 0] RETURNS [color: Color] = { node: REF ANY _ tree.son; class: Classification; IF tree.son = NIL THEN RETURN[tree.backgroundColor]; IF SVBoundBox.PointInBoundBox[cameraPoint, sceneBox] THEN { finalClassCount, firstClassCount: NAT; firstClassCount _ NumberOfClassesInPool[]; -- for debugging purposes. class _ RayCast[cameraPoint, sceneRay, node, makeStream, f, indent]; color _ ColorFromClass[class, cameraPoint[1], cameraPoint[2], lightSources, camera, sceneRay, tree, makeStream, f, indent]; ReturnClassToPool[class]; finalClassCount _ NumberOfClassesInPool[]; -- for debugging purposes. IF finalClassCount < firstClassCount THEN { f.PutF["WARNING: A Classification was lost while casting a ray at [%g, %g]", [real[cameraPoint[1]]], [real[cameraPoint[2]]]]; }; } ELSE color _ tree.backgroundColor; }; ColorCast: PRIVATE PROC [cameraPoint: Point2d, sceneRay: Ray, tree: CSGTree, lightSources: LightSourceList, camera: Camera, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL, indent: NAT _ 0] RETURNS [color: Color] = { class: Classification; class _ RayCast[cameraPoint, sceneRay, tree.son, makeStream, f, indent]; color _ ColorFromClass[class, cameraPoint[1], cameraPoint[2], lightSources, camera, sceneRay, tree]; ReturnClassToPool[class]; }; SetUpRayTrace: PROC [boundBox: BoundBox, camera: Camera, aisRope: Rope.ROPE, bAndWOnly: BOOL, resolution: REAL, zone: UNCOUNTED ZONE, raster: AIS.Raster, commentString: LONG STRING] RETURNS [I: Image, xSamples, ySamples: NAT, stepSize, xStart, yStart: REAL] = { <> extentX, extentY, projectionX, projectionY, trueExtentX, trueExtentY: REAL; comment: Rope.ROPE _ IO.PutFR["res: %g dpi", [real[resolution]]]; ConvertUnsafe.AppendRope[commentString, comment]; stepSize _ 72.0/resolution; -- in screen dots per sample <> IF camera.frame.fullScreen THEN { [I, xSamples, ySamples] _ SVImage.OpenImage[aisRope, bAndWOnly, boundBox.minVert[1], boundBox.minVert[2], boundBox.maxVert[1], boundBox.maxVert[2], resolution, raster, commentString]; extentX _ boundBox.maxVert[1] - boundBox.minVert[1]; extentY _ boundBox.maxVert[2] - boundBox.minVert[2]; } ELSE { [I, xSamples, ySamples] _ SVImage.OpenImage[aisRope, bAndWOnly, camera.frame.downLeft[1], camera.frame.downLeft[2], camera.frame.upRight[1], camera.frame.upRight[2], resolution, raster, commentString]; extentX _ camera.frame.upRight[1] - camera.frame.downLeft[1]; extentY _ camera.frame.upRight[2] - camera.frame.downLeft[2]; }; <> trueExtentX _ Real.Float[xSamples-1]*stepSize; trueExtentY _ Real.Float[ySamples-1]*stepSize; projectionX _ (trueExtentX - extentX)/2.0; projectionY _ (trueExtentY - extentY)/2.0; IF camera.frame.fullScreen THEN { xStart _ boundBox.minVert[1] - projectionX; yStart _ boundBox.minVert[2] - projectionY; } ELSE { xStart _ camera.frame.downLeft[1] - projectionX; yStart _ camera.frame.downLeft[2] - projectionY; }; <> xStart _ xStart - stepSize/2.0; yStart _ yStart - stepSize/2.0; }; -- end of SetUpRayTrace ShutDownRayTrace: PROC [aisRope: Rope.ROPE, zone: UNCOUNTED ZONE, raster: AIS.Raster, I: Image] = { SVImage.CloseImage[I, aisRope]; zone.FREE[@raster]; UnsafeStorage.FreeUZone[zone]; }; FillScanLine: PROC [startX, stepSize: REAL, xSamples: NAT, y: REAL, cameraXStepRayInWorld: Ray, worldRay: Ray, tree: CSGTree, lightSources: LightSourceList, camera: Camera, boundBox: BoundBox, scanLine: ScanLine, outStream: IO.STREAM] = { <> color: Color; thisX: REAL; color _ TopColorCast[[startX, y], worldRay, tree, lightSources, camera, boundBox, FALSE, outStream]; PutColorInScanLine[scanLine, 0, color]; AddRay[cameraXStepRayInWorld, worldRay]; -- updates worldRay FOR j: INTEGER IN[1..xSamples] DO -- left to right thisX _ startX+Real.Float[j]*stepSize; color _ TopColorCast[[thisX, y], worldRay, tree, lightSources, camera, boundBox]; PutColorInScanLine[scanLine, j, color]; AddRay[cameraXStepRayInWorld, worldRay]; -- updates worldRay ENDLOOP; }; DrawTree: PUBLIC PROC [dc: Graphics.Context, tree: CSGTree, lightSources: LightSourceList, camera: Camera, aisRope: Rope.ROPE, bAndWOnly: BOOL, notify: NotifyOfProgressProc _ NoOpNotifyOfProgress, clientData: REF ANY _ NIL, outStream: IO.STREAM] RETURNS [success: BOOL] = { topNode: REF ANY; -- tree.son. The top active node of the CSG Tree I: Image; raster: AIS.Raster; zone: UNCOUNTED ZONE; commentString: LONG STRING _ [256]; boundBox: BoundBox; cameraWRTWorld: Matrix4by4; cameraXStepRayInWorld, cameraRay, worldRay: Ray; stepSize, xStart, yStart, thisY: REAL; xSamples, ySamples: NAT; <> focalLength: REAL; color: Color; scanLine1, scanLine2: ScanLine; success _ TRUE; topNode _ tree.son; camera.abort _ FALSE; -- if camera.abort becomes TRUE, close files and return. boundBox _ Preprocess3d.Preprocess[tree, camera]; -- must call this before casting rays IF camera.frame.fullScreen AND boundBox = NIL THEN { MessageWindow.Append["Infinite Scene. Please define a bounding frame.", TRUE]; MessageWindow.Blink[]; success _ FALSE; RETURN; }; zone _ UnsafeStorage.NewUZone[]; raster _ zone.NEW[AIS.RasterPart]; <> [I, xSamples, ySamples, stepSize, xStart, yStart] _ SetUpRayTrace [boundBox, camera, aisRope, bAndWOnly, camera.resolution, zone, raster, commentString]; OutputTreeInfo[topNode, I, outStream]; scanLine1 _ CreateScanLine[xSamples+1]; scanLine2 _ CreateScanLine[xSamples+1]; cameraWRTWorld _ CoordSys.FindInTermsOfWorld[camera.coordSys]; focalLength _ - camera.focalLength; cameraRay _ NEW[RayObj]; -- DrawTree recycles its own ray cameraXStepRayInWorld _ GetXStepRayInWorld[stepSize, focalLength, cameraWRTWorld]; <> cameraRay.basePt _ [xStart, yStart, 0]; cameraRay.direction _ [xStart, yStart, focalLength]; worldRay _ TransformRay[cameraRay, cameraWRTWorld]; -- allocates ray from pool FillScanLine [xStart, stepSize, xSamples, yStart, cameraXStepRayInWorld, worldRay, tree, lightSources, camera, boundBox, scanLine1, outStream]; ReturnRayToPool[worldRay]; FOR i: INTEGER IN[1..ySamples] DO -- bottom to top IF camera.abort = TRUE THEN { SVImage.CloseImage[I, aisRope]; MessageWindow.Append["CastRays aborted. Partial files saved.", TRUE]; outStream.PutF["CastRays aborted. Partial files saved."]; RETURN; }; notify[yStart+i*stepSize, xStart, yStart, xStart+xSamples*stepSize, yStart+ySamples*stepSize, clientData]; -- tell the user interface that we have just cast line i - 1. thisY _ yStart+i*stepSize; cameraRay.basePt _ [xStart, thisY, 0]; cameraRay.direction _ [xStart, thisY, focalLength]; worldRay _ TransformRay[cameraRay, cameraWRTWorld]; -- allocates ray from pool FillScanLine [xStart, stepSize, xSamples, thisY, cameraXStepRayInWorld, worldRay, tree, lightSources, camera, boundBox, scanLine2, outStream]; ReturnRayToPool[worldRay]; <> FOR k: NAT IN[0..xSamples) DO <> <> color _ ColorAverage[scanLine1[k], scanLine1[k+1], scanLine2[k], scanLine2[k+1]]; SVImage.PutImage[I, i, k, color, xSamples, ySamples]; ENDLOOP; CopyScanLine [scanLine2, scanLine1]; ENDLOOP; ShutDownRayTrace[aisRope, zone, raster, I]; }; -- end of DrawTree MoreOrLessTheSame: PRIVATE PROC [a, b, c, d: REAL] RETURNS [BOOL] = { min, max: REAL; min _ max _ a; IF b < min THEN min _ b ELSE IF b > max THEN max _ b; IF c < min THEN min _ c ELSE IF c > max THEN max _ c; IF d < min THEN min _ d ELSE IF d > max THEN max _ d; IF max - min > 10 THEN RETURN[FALSE] ELSE RETURN[TRUE]; }; -- end of MoreOrLessTheSame ColorAverage: PRIVATE PROC [a, b, c, d: Color] RETURNS [avgColor: Color] = { ar, ag, ab, br, bg, bb, cr, cg, cb, dr, dg, db, red, green, blue: REAL; [ar, ag, ab] _ GraphicsColor.ColorToRGB[a]; [br, bg, bb] _ GraphicsColor.ColorToRGB[b]; [cr, cg, cb] _ GraphicsColor.ColorToRGB[c]; [dr, dg, db] _ GraphicsColor.ColorToRGB[d]; red _ (ar + br + cr + dr)/4.0; green _ (ag + bg + cg + dg)/4.0; blue _ (ab + bb + cb + db)/4.0; avgColor _ GraphicsColor.RGBToColor[red, green, blue]; }; -- end of ColorAverage <> CastMoreRays: PRIVATE PROC [ul, ur, dl, dr: Color, left, right, top, bottom: REAL, tree: CSGTree, focalLength: REAL, lightSources: LightSourceList, camera: Camera] RETURNS [color: Color] = { <> cameraRay: Ray _ GetRayFromPool[]; worldRay: Ray; cameraWRTWorld: Matrix4by4 _ camera.coordSys.mat; leftColor, rightColor, topColor, bottomColor, middleColor: Color; midLeftY, midTopX: REAL; midLeftY _ (top-bottom)/2.0; midTopX _ (right-left)/2.0; cameraRay.basePt _ [left, midLeftY, 0];cameraRay.direction _ [left, midLeftY, focalLength]; worldRay _ TransformRay[cameraRay, cameraWRTWorld]; -- allocates ray from pool leftColor _ ColorCast[[left, midLeftY], worldRay, tree, lightSources, camera]; ReturnRayToPool[worldRay]; cameraRay.basePt _ [right, midLeftY, 0];cameraRay.direction _ [right, midLeftY, focalLength]; worldRay _ TransformRay[cameraRay, cameraWRTWorld]; -- allocates ray from pool rightColor _ ColorCast[[right, midLeftY], worldRay, tree, lightSources, camera]; ReturnRayToPool[worldRay]; cameraRay.basePt _ [midTopX, top, 0];cameraRay.direction _ [midTopX, top, focalLength]; worldRay _ TransformRay[cameraRay, cameraWRTWorld]; -- allocates ray from pool topColor _ ColorCast[[midTopX, top], worldRay, tree, lightSources, camera]; ReturnRayToPool[worldRay]; cameraRay.basePt _ [midTopX, bottom, 0];cameraRay.direction _ [midTopX, bottom, focalLength]; worldRay _ TransformRay[cameraRay, cameraWRTWorld]; -- allocates ray from pool bottomColor _ ColorCast[[midTopX, bottom], worldRay, tree, lightSources, camera]; ReturnRayToPool[worldRay]; cameraRay.basePt _ [midTopX, midLeftY, 0];cameraRay.direction _ [midTopX, midLeftY, focalLength]; worldRay _ TransformRay[cameraRay, cameraWRTWorld]; -- allocates ray from pool middleColor _ ColorCast[[midTopX, midLeftY], worldRay, tree, lightSources, camera]; ReturnRayToPool[worldRay]; color _ ColorAverage[ ColorAverage[ul, topColor, leftColor, middleColor], ColorAverage[topColor, ur, middleColor, rightColor], ColorAverage[leftColor, middleColor, dl, bottomColor], ColorAverage[middleColor, rightColor, bottomColor, dr] ]; ReturnRayToPool[cameraRay]; }; -- end of CastMoreRays Init: PROC = { <> globalPool _ NEW[PoolObj[globalPoolCount]]; FOR i: NAT IN[0..globalPoolCount) DO globalPool[i] _ NEW[ClassificationObj]; globalPool[i].surfaces _ NEW[SurfaceArrayObj]; ENDLOOP; globalPoolPointer _ globalPoolCount; <> globalRayPool _ NEW[RayPoolObj]; FOR i: NAT IN[1..globalRayPoolCount] DO globalRayPool[i] _ NEW[RayObj]; ENDLOOP; globalRayPoolPointer _ globalRayPoolCount; <> globalCompactPool _ NEW[CompactPoolObj]; FOR i: NAT IN[1..globalCompactPoolCount] DO globalCompactPool[i] _ NEW[CompactArrayObj]; ENDLOOP; globalCompactPoolPointer _ globalCompactPoolCount; }; NoOpNotifyOfProgress: PUBLIC NotifyOfProgressProc = {}; GetClassFromPool: PUBLIC PROC RETURNS [class: Classification] = { IF globalPoolPointer = 0 THEN AddAClass[]; class _ globalPool[globalPoolPointer - 1]; globalPoolPointer _ globalPoolPointer - 1; }; ClassPoolEmpty: SIGNAL = CODE; ReturnClassToPool: PUBLIC PROC [class: Classification] = { IF globalPoolPointer = globalPool.maxClasses THEN SIGNAL ClassPoolFull; globalPoolPointer _ globalPoolPointer + 1; globalPool[globalPoolPointer - 1] _ class; }; ClassPoolFull: SIGNAL = CODE; NumberOfClassesInPool: PUBLIC PROC RETURNS [count: NAT] = { count _ globalPoolPointer; }; AddAClass: PRIVATE PROC = { <> newPool: Pool _ NEW[PoolObj[globalPool.maxClasses+1]]; IF globalPool.maxClasses > 50 THEN {-- there must be a leak in the classification system MessageWindow.Append["Warning: More than 50 Classifications!!", TRUE]; MessageWindow.Blink[]; }; FOR i: NAT IN [0..globalPoolPointer) DO newPool[i] _ globalPool[i]; ENDLOOP; globalPoolPointer _ globalPoolPointer + 1; globalPool _ newPool; globalPool[globalPoolPointer - 1] _ NEW[ClassificationObj]; globalPool[globalPoolPointer - 1].surfaces _ NEW[SurfaceArrayObj]; }; GetRayFromPool: PUBLIC PROC RETURNS [ray: Ray] = { IF globalRayPoolPointer = 0 THEN SIGNAL RayPoolEmpty; ray _ globalRayPool[globalRayPoolPointer]; globalRayPoolPointer _ globalRayPoolPointer -1; }; RayPoolEmpty: SIGNAL = CODE; ReturnRayToPool: PUBLIC PROC [ray: Ray] = { IF globalRayPoolPointer = globalRayPoolCount THEN SIGNAL RayPoolFull; globalRayPoolPointer _ globalRayPoolPointer + 1; globalRayPool[globalRayPoolPointer] _ ray; }; RayPoolFull: SIGNAL = CODE; GetCompactFromPool: PROC RETURNS [compact: CompactArray] = { IF globalCompactPoolPointer = 0 THEN SIGNAL CompactPoolEmpty; compact _ globalCompactPool[globalCompactPoolPointer]; globalCompactPoolPointer _ globalCompactPoolPointer -1; }; CompactPoolEmpty: SIGNAL = CODE; ReturnCompactToPool: PROC [compact: CompactArray] = { IF globalCompactPoolPointer = globalCompactPoolCount THEN SIGNAL CompactPoolFull; globalCompactPoolPointer _ globalCompactPoolPointer + 1; globalCompactPool[globalCompactPoolPointer] _ compact; }; CompactPoolFull: SIGNAL = CODE; MakeClassAMiss: PUBLIC PROC [class: Classification] = { class.count _ 0; class.classifs[1] _ FALSE; }; Init[]; END.