<> <> <> DIRECTORY Atom USING [ DottedPair, DottedPairNode, GetPropFromList, PropList, PutPropOnList, RemPropFromList ], Basics USING [ BITOR, BITAND, BITSHIFT ], Real USING [ Float ], RealFns USING [ SqRt, AlmostEqual ], G3dVector USING [ Add, Cross, NearestToLine, Normalize, Null ], G3dBasic USING [ origin, Ray ], ScanConvert USING [ justNoticeable ], ThreeDBasics USING [ AllOut, ClipState, Context, Error, FacingDir, GetSurfaceType, NoneOut, OutCode, Pair, Patch, PatchProc, PatchSequence, PtrPatch, PtrPatchSequence, RealSequence, RegisterSurfaceType, RGB, ShadingSequence, ShadingClass, ShadingValue, ShapeClass, ShapeInstance, ShapeProc, SixSides, Triple, Vertex, VertexInfo, VertexInfoProc, VertexInfoSequence, VertexSequence, Xfm3D ], ShapeUtilities USING [ BackFacing, GetClipCodeForPt, GetPatch, GetPatchClipState, GetVertexInfo, ReleasePatch, ReleaseVertexInfo, ShadePoly, XfmPtToDisplay, XfmToEyeSpace ], SurfaceRender USING [ GetPtrPatchClipState, OutputPolygon, ValidatePolyhedron ]; StandardPatchProcs: CEDAR MONITOR IMPORTS Atom, Basics, G3dVector, Real, RealFns, SurfaceRender, ThreeDBasics, ShapeUtilities = BEGIN <> Ray: TYPE ~ G3dBasic.Ray; RGB: TYPE ~ ThreeDBasics.RGB; RealSequence: TYPE ~ ThreeDBasics.RealSequence; Context: TYPE ~ ThreeDBasics.Context; SixSides: TYPE ~ ThreeDBasics.SixSides; Pair: TYPE ~ ThreeDBasics.Pair; Triple: TYPE ~ ThreeDBasics.Triple; Xfm3D: TYPE ~ ThreeDBasics.Xfm3D; ShapeInstance: TYPE ~ ThreeDBasics.ShapeInstance; Patch: TYPE ~ ThreeDBasics.Patch; PatchSequence: TYPE ~ ThreeDBasics.PatchSequence; PatchProc: TYPE ~ ThreeDBasics.PatchProc; PtrPatch: TYPE ~ ThreeDBasics.PtrPatch; PtrPatchSequence: TYPE ~ ThreeDBasics.PtrPatchSequence; Vertex: TYPE ~ ThreeDBasics.Vertex; VertexInfo: TYPE ~ ThreeDBasics.VertexInfo; VertexInfoSequence: TYPE ~ ThreeDBasics.VertexInfoSequence; VertexInfoProc: TYPE ~ ThreeDBasics.VertexInfoProc; ShapeClass: TYPE ~ ThreeDBasics.ShapeClass; ShapeProc: TYPE ~ ThreeDBasics.ShapeProc; ClipState: TYPE ~ ThreeDBasics.ClipState; OutCode: TYPE ~ ThreeDBasics.OutCode; AllOut: OutCode ~ ThreeDBasics.AllOut; NoneOut: OutCode ~ ThreeDBasics.NoneOut; FacingDir: TYPE ~ ThreeDBasics.FacingDir; FacingDirArray: TYPE ~ ARRAY [0..9) OF FacingDir; MidPtProc: TYPE ~ PROC[v0, v1: REF VertexInfo] RETURNS[REF VertexInfo]; LORA: TYPE = LIST OF REF ANY; maxDeviation: REAL _ 0.25; -- subdivision tolerance with antialiasing in pixels maxInteriorDev: REAL _ 0.25; -- (1.0) tolerance on internal edges maxJaggyDeviation: REAL _ 1.0; -- jaggy subdivision tolerance in pixels maxJaggyInteriorDev: REAL _ 1.0; -- (4.0) tolerance on internal edges closenessFactor: REAL _ 20.0; -- times maxDeviation gets clipping cutoff for straightness recurseLimit: NAT _ 14; -- safety valve on recursion stopIfStraight: BOOLEAN _ TRUE; -- set false to defeat termination algorithm stopOnDegenerate: BOOLEAN _ FALSE; -- signals on badly degenerate patches showLines: BOOLEAN _ FALSE; -- debug and pedagogical aid showCtlPts: BOOLEAN _ FALSE; -- debug and pedagogical aid refPt5: REF REAL _ NEW[REAL _ 0.5]; <> ShowCoords: PROC[poly: REF Patch, space: ATOM _ $Screen] RETURNS[list: LIST OF REF Pair] ~ { FOR i: CARD16 DECREASING IN [0..poly.nVtces) DO p: REF Pair; SELECT space FROM $Screen => p _ NEW[ Pair _ [ poly[i].coord.sx, poly[i].coord.sy ] ]; $Eye => p _ NEW[ Pair _ [ poly[i].coord.ex, poly[i].coord.ey ] ]; $Object => p _ NEW[ Pair _ [ poly[i].coord.x, poly[i].coord.y ] ]; ENDCASE; list _ CONS[p, list]; ENDLOOP; RETURN[list]; }; Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; }; CopyVtx: PROC[vtx: VertexInfo] RETURNS[vtxOut: REF VertexInfo] ~ { shape: REF ShapeInstance _ NARROW[ Atom.GetPropFromList[vtx.props, $Shape] ]; lerpProc: VertexInfoProc _ IF shape # NIL THEN shape.shadingClass.lerpVtxAux ELSE NIL; vtxOut _ ShapeUtilities.GetVertexInfo[]; vtxOut^ _ vtx; IF lerpProc # NIL THEN { -- copy vtx.aux, has to be done remotely since type not available data: LORA _ LIST[ vtx.aux, vtx.aux, refPt5, refPt5 ]; vtxOut^ _ lerpProc[ NIL, vtxOut^, data]; -- shoehorn into lerp form since its available } }; Sub: PROC[f, g: REAL] RETURNS[REAL] ~ { << Difference f - g, returns zero if less than 6 decimal places>> IF RealFns.AlmostEqual[f, g, -20] THEN RETURN [0.0] ELSE RETURN[f-g]; }; Sub1: PROC[f, g: REAL] RETURNS[REAL] ~ { << Screen space f - g, returns zero if less than noticeable>> result: REAL _ f-g; IF ABS[result] < ScanConvert.justNoticeable THEN RETURN [0.0] ELSE RETURN[result]; }; DiffTriple: PROC[v1, v2: Triple] RETURNS[Triple] ~ { RETURN[[Sub[v1.x, v2.x], Sub[v1.y, v2.y], Sub[v1.z, v2.z]]]; }; DiffPosns: PROC[vtx1, vtx2: Vertex, space: ATOM _ NIL] RETURNS[Triple] ~ { SELECT space FROM $Eye => RETURN[[Sub[vtx1.ex, vtx2.ex], Sub[vtx1.ey, vtx2.ey], Sub[vtx1.ez, vtx2.ez]]]; $Screen=> RETURN[[Sub1[vtx1.sx, vtx2.sx], Sub1[vtx1.sy, vtx2.sy], Sub1[vtx1.sz, vtx2.sz]]]; ENDCASE => -- object space RETURN[[Sub[vtx1.x, vtx2.x], Sub[vtx1.y, vtx2.y], Sub[vtx1.z, vtx2.z]]]; }; VtxDisplayMidPt: MidPtProc ~ { <> shape: REF ShapeInstance _ NARROW[ Atom.GetPropFromList[v0.props, $Shape] ]; lerpProc: VertexInfoProc _ IF shape # NIL THEN shape.shadingClass.lerpVtxAux ELSE NIL; v: REF VertexInfo _ ShapeUtilities.GetVertexInfo[]; v.coord.sx _ (v0.coord.sx + v1.coord.sx) / 2; -- for position on screen v.coord.sy _ (v0.coord.sy + v1.coord.sy) / 2; v.coord.sz _ (v0.coord.sz + v1.coord.sz) / 2; v.coord.ex _ (v0.coord.ex + v1.coord.ex) / 2; -- for normal-vector shading v.coord.ey _ (v0.coord.ey + v1.coord.ey) / 2; v.coord.ez _ (v0.coord.ez + v1.coord.ez) / 2; <<[[v.coord.sx, v.coord.sy, v.coord.sz]] _ ShapeUtilities.XfmPtToDisplay[>> <> <<];>> v.shade.r _ (v0.shade.r + v1.shade.r) / 2; -- for color-per-vertex (could be avoided) v.shade.g _ (v0.shade.g + v1.shade.g) / 2; v.shade.b _ (v0.shade.b + v1.shade.b) / 2; IF shape.shadingClass.texture # NIL THEN { v.coord.x _ (v0.coord.x + v1.coord.x) / 2; v.coord.y _ (v0.coord.y + v1.coord.y) / 2; v.coord.z _ (v0.coord.z + v1.coord.z) / 2; }; IF lerpProc # NIL THEN { -- get auxiliary info from supplied proc data: LORA _ LIST[ v0.aux, v1.aux, refPt5, refPt5 ]; v^ _ lerpProc[ NIL, v^, data]; } ELSE v.aux _ v0.aux; v.props _ v0.props; RETURN[v]; }; TooClose: PROC[ context: REF Context, v: Vertex, outCode: OutCode, tol: REAL ] RETURNS[BOOLEAN] ~ { <> maxDist: REAL _ 0.0; IF v.sz = 0.0 THEN RETURN [FALSE]; -- invalid screen coord, ignore IF outCode.left THEN maxDist _ MAX[maxDist, v.sx - context.viewPort.x]; IF outCode.right THEN maxDist _ MAX[maxDist, context.viewPort.w + context.viewPort.x - v.sx]; IF outCode.bottom THEN maxDist _ MAX[maxDist, v.sy - context.viewPort.y]; IF outCode.top THEN maxDist _ MAX[maxDist, context.viewPort.h + context.viewPort.y - v.sy]; IF maxDist < tol * closenessFactor THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; PutPropSafely: PROC[propList: Atom.PropList, prop, val: REF ANY] RETURNS[Atom.PropList] ~{ <> newProps: Atom.PropList _ NIL; FOR list: Atom.PropList _ propList, list.rest UNTIL list = NIL DO -- new proplist element: Atom.DottedPair _ NEW[Atom.DottedPairNode _ list.first^]; newProps _ CONS[element, newProps]; ENDLOOP; RETURN[ Atom.PutPropOnList[ newProps, prop, val ] ]; }; RemPropSafely: PROC[ propList: Atom.PropList, prop: REF ANY ] RETURNS[Atom.PropList] ~ { <> newProps: Atom.PropList _ NIL; FOR list: Atom.PropList _ propList, list.rest UNTIL list = NIL DO -- new proplist element: Atom.DottedPair _ NEW[Atom.DottedPairNode _ list.first^]; newProps _ CONS[element, newProps]; ENDLOOP; RETURN[ Atom.RemPropFromList[ newProps, prop ] ]; }; MakeStraight: PROC[ v0, v1, vMid: VertexInfo ] RETURNS[VertexInfo] ~ { <> shape: REF ShapeInstance _ NARROW[ Atom.GetPropFromList[v0.props, $Shape] ]; lerpProc: VertexInfoProc _ IF shape # NIL THEN shape.shadingClass.lerpVtxAux ELSE NIL; a, b, alpha: REAL; <> mag: REAL _ RealFns.SqRt[ Sqr[v1.coord.sx - v0.coord.sx] + Sqr[v1.coord.sy - v0.coord.sy] ]; IF mag < ScanConvert.justNoticeable -- effectively invisible detail THEN { vMid.coord _ v0.coord; vMid.shade _ v0.shade; RETURN[vMid]; }; alpha _ ( (vMid.coord.sx - v0.coord.sx) * (v1.coord.sx - v0.coord.sx) -- vMid-v0 dot v1-v0 + (vMid.coord.sy - v0.coord.sy) * (v1.coord.sy - v0.coord.sy) ) / Sqr[mag]; a _ 1.0 - alpha; b _ alpha; vMid.coord.sx _ a * v0.coord.sx + b * v1.coord.sx ; -- screen coords (for scan conversion) vMid.coord.sy _ a * v0.coord.sy + b * v1.coord.sy ; vMid.coord.sz _ a * v0.coord.sz + b * v1.coord.sz ; <> vMid.shade.r _ a * v0.shade.r + b * v1.shade.r ; -- surface color vMid.shade.g _ a * v0.shade.g + b * v1.shade.g ; vMid.shade.b _ a * v0.shade.b + b * v1.shade.b ; IF shape.shadingClass.texture # NIL THEN { -- original coords (for solid texture) vMid.coord.x _ a * v0.coord.x + b * v1.coord.x ; vMid.coord.y _ a * v0.coord.y + b * v1.coord.y ; vMid.coord.z _ a * v0.coord.z + b * v1.coord.z ; }; IF lerpProc # NIL THEN { -- get auxiliary info from supplied proc data: LORA _ LIST[ v0.aux, v1.aux, NEW[REAL _ a], NEW[REAL _ b] ]; vMid _ lerpProc[ NIL, vMid, data]; }; RETURN[vMid]; }; SetStraight: PROC[ p: REF Patch, v0, v1, v2, v3: NAT] ~ { <> p[v1] _ MakeStraight[ p[v0], p[v3], p[v1] ]; p[v2] _ MakeStraight[ p[v0], p[v3], p[v2] ]; p[v0].props _ PutPropSafely[p[v0].props, $Straight, $Itis]; }; StraightWithin: PROC[ context: REF Context, p: REF Patch, v0, v1, v2, v3: NAT, lilTol, bigTol: REAL] RETURNS[BOOLEAN] ~ { StraightenUp: PROC[] ~ { <> p[v0].props _ PutPropSafely[p[v0].props, $Straight, $DoIt]; }; InteriorEdge: PROC[] RETURNS[BOOLEAN] ~ { <> PolyNo: PROC[v: INTEGER] RETURNS[NAT] ~ { i: NAT _ MAX[v / 4 -1 , 0]; j: NAT _ MAX[v MOD 4 -1 , 0]; RETURN[ i*3 + j ]; }; facingDir: FacingDir; subDirs: REF FacingDirArray _ NARROW[ Atom.GetPropFromList[p.props, $FacingDirArray] ]; IF subDirs = NIL THEN RETURN[FALSE]; facingDir _ subDirs[PolyNo[v0]]; IF subDirs[PolyNo[v1]] # facingDir THEN RETURN[FALSE]; IF subDirs[PolyNo[v2]] # facingDir THEN RETURN[FALSE]; IF subDirs[PolyNo[v3]] # facingDir THEN RETURN[FALSE]; IF facingDir = undetermined THEN RETURN[FALSE]; RETURN[TRUE]; }; DistOffEdge: PROC[v: Vertex, line: Ray] RETURNS[REAL] ~ { ptOnEdge: Triple _ ShapeUtilities.XfmPtToDisplay[ context, G3dVector.NearestToLine[line, [v.ex, v.ey, v.ez]] ]; vs: Triple _ ShapeUtilities.XfmPtToDisplay[ context, [v.ex, v.ey, v.ez] ]; RETURN[ RealFns.SqRt[ Sqr[ptOnEdge.x - vs.x] + Sqr[ptOnEdge.y - vs.y] ] ]; }; dist1, dist2, dist3, baseDist, a, b: REAL _ 0.0; reversed: BOOLEAN _ FALSE; shape: REF ShapeInstance; IF Atom.GetPropFromList[p[v0].props, $Straight] # NIL THEN RETURN[TRUE]; a _ p[v3].coord.sy - p[v0].coord.sy; b _ p[v3].coord.sx - p[v0].coord.sx; baseDist _ RealFns.SqRt[a*a + b*b]; IF baseDist > ScanConvert.justNoticeable THEN { a _ a/baseDist; b _ b/baseDist; -- get normalized distances from v0-v3 dist1 _ a * (p[v1].coord.sx - p[v0].coord.sx) + b * (p[v0].coord.sy - p[v1].coord.sy); IF ABS[dist1] > bigTol THEN RETURN[FALSE]; dist2 _ a * (p[v2].coord.sx - p[v0].coord.sx) + b * (p[v0].coord.sy - p[v2].coord.sy); IF ABS[dist2] > bigTol THEN RETURN[FALSE]; shape _ NARROW[ Atom.GetPropFromList[p.props, $Shape] ]; IF shape.shadingClass.texture # NIL THEN { -- watch for perspective depth distortion point: Triple _ [p[v0].coord.ex, p[v0].coord.ey, p[v0].coord.ez]; line: Ray _ [ point, DiffPosns[p[v3].coord, p[v0].coord, $Eye] ]; <> dist1: REAL _ DistOffEdge[p[v1].coord, line]; dist2: REAL _ DistOffEdge[p[v2].coord, line]; IF dist1 > bigTol OR dist2 > bigTol THEN RETURN[FALSE]; }; }; SELECT TRUE FROM MAX[dist1, dist2] < lilTol => { -- inside minimum deviation used onshape boundary StraightenUp[]; RETURN[ TRUE ]; }; p.dir # undetermined => { StraightenUp[]; RETURN[ TRUE ]; }; -- interior patch InteriorEdge[] => { StraightenUp[]; RETURN[ TRUE ]; }; -- interior edge ENDCASE => RETURN[ FALSE ]; }; PatchSort: PROC[ context: REF Context, p0, p1, p2, p3: REF Patch] RETURNS[REF Patch, REF Patch, REF Patch, REF Patch] ~ { z: ARRAY[0..4) OF REAL; pOut: ARRAY[0..4) OF REF Patch; <> z[0] _ (p0[0].coord.ez + p0[12].coord.ez + p0[15].coord.ez + p0[3].coord.ez)/4; pOut[0] _ p0; z[1] _ (p1[0].coord.ez + p1[12].coord.ez + p1[15].coord.ez + p1[3].coord.ez)/4; pOut[1] _ p1; z[2] _ (p2[0].coord.ez + p2[12].coord.ez + p2[15].coord.ez + p2[3].coord.ez)/4; pOut[2] _ p2; z[3] _ (p3[0].coord.ez + p3[12].coord.ez + p3[15].coord.ez + p3[3].coord.ez)/4; pOut[3] _ p3; FOR i: NAT IN [1..4) DO FOR j: NAT DECREASING IN [0..i) DO -- sort to increasing depth order IF z[j+1] < z[j] THEN { t: REAL _ z[j+1]; p: REF Patch _ pOut[j+1]; z[j+1] _ z[j]; pOut[j+1] _ pOut[j]; z[j] _ t; pOut[j] _ p; }; ENDLOOP; ENDLOOP; IF context.antiAliasing THEN RETURN[ pOut[0], pOut[1], pOut[2], pOut[3] ] ELSE RETURN[ pOut[3], pOut[2], pOut[1], pOut[0] ]; }; ValidatePatchShape: ShapeProc ~ { -- <> <> IF shape.vtcesInValid THEN { shape.clipState _ ShapeUtilities.XfmToEyeSpace[ context, shape ]; <> IF shape.surface # NIL THEN { -- get clipping info for patches patch: REF PtrPatchSequence _ NARROW[shape.surface]; FOR i: NAT IN [0..shape.numSurfaces) DO IF patch[i] # NIL THEN IF shape.clipState = in THEN patch[i].clipState _ in -- unclipped, inside ELSE IF shape.clipState = clipped -- evaluate clipping tags THEN SurfaceRender.GetPtrPatchClipState[ shape, patch[i] ] ELSE patch[i].clipState _ out; ENDLOOP; }; shape.vtcesInValid _ FALSE; }; shape.shadingInValid _ FALSE; RETURN[shape]; }; <> InitClasses: PROC[] ~ { -- register procedures for basic surface types standardClass: ShapeClass _ ThreeDBasics.GetSurfaceType[$ConvexPolygon]; standardClass.type _ $Bezier; standardClass.validate _ ValidatePatchShape; standardClass.displayPatch _ BezierDisplay; ThreeDBasics.RegisterSurfaceType[standardClass, $Bezier]; standardClass.type _ $BSpline; standardClass.validate _ ValidatePatchShape; standardClass.displayPatch _ BSplineDisplay; ThreeDBasics.RegisterSurfaceType[standardClass, $BSpline]; standardClass.type _ $NonConvexPolygon; standardClass.validate _ SurfaceRender.ValidatePolyhedron; standardClass.displayPatch _ NonConvexDisplay; ThreeDBasics.RegisterSurfaceType[standardClass, $NonConvexPolygon]; }; <> BezierDisplay: PatchProc ~ { <> shape: REF ShapeInstance _ NARROW[ Atom.GetPropFromList[patch.props, $Shape] ]; SELECT shape.shadingClass.shadingType FROM $Lines => { [] _ BezierDisplayLines[context, patch]; RETURN[NIL]; }; $CtlPts => { [] _ BezierDisplayCtrlPts[context, patch]; RETURN[NIL]; }; ENDCASE; patch _ BezierClipState[context, patch]; IF patch.nVtces = 16 -- Divide till all outside patch edges are straight or recursion exceeded THEN BezierPatchDisplay[context, patch, 0] ELSE IF patch.nVtces = 10 THEN BezierTriangleDisplay[ context, patch, MIN[6, recurseLimit] ]; RETURN[NIL]; -- end of the line, no patch returned }; BezierPatchDisplay: PROC[ context: REF Context, p: REF Patch, level: NAT] ~ { allStraight: BOOLEAN _ TRUE; lilTol: REAL _ IF context.antiAliasing THEN maxDeviation ELSE maxJaggyDeviation; bigTol: REAL _ IF context.antiAliasing THEN maxInteriorDev ELSE maxJaggyInteriorDev; <> IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received IF p.clipState = out THEN RETURN[]; IF p.oneSided AND p.dir = back THEN RETURN[]; -- backfacing on closed surface IF p.clipState = undetermined THEN allStraight _ FALSE ELSE { -- Straightness test on patch boundary, if straight enough, force straightness IF NOT StraightWithin[context, p, 0, 4, 8,12, lilTol, bigTol] THEN allStraight _ FALSE; IF NOT StraightWithin[context, p,12,13,14,15, lilTol, bigTol] THEN allStraight _ FALSE; IF NOT StraightWithin[context, p,15,11, 7, 3, lilTol, bigTol] THEN allStraight _ FALSE; IF NOT StraightWithin[context, p, 3, 2, 1, 0, lilTol, bigTol] THEN allStraight _ FALSE; }; IF stopIfStraight AND allStraight OR level >= recurseLimit THEN IF showCtlPts -- outer edges straight, display as polygon THEN p _ BezierDisplayCtrlPts[context, p] ELSE { p _ PolygonFromBezierPatch[p]; -- construct polygon ShapeUtilities.ShadePoly[context, p]; IF showLines THEN p.type _ $PolyLine; p _ SurfaceRender.OutputPolygon[context, p]; -- display } ELSE { -- not straight enough, subdivide and recurse p0, p1, p2, p3: REF Patch; [p0, p1, p2, p3] _ BezierPatchDivide[ context, p, VtxDisplayMidPt ]; -- subdivide IF NOT context.depthBuffering THEN [p0, p1, p2, p3] _ PatchSort[ context, p0, p1, p2, p3]; -- sort to display order <> BezierPatchDisplay[context, p0, level+1]; BezierPatchDisplay[context, p1, level+1]; BezierPatchDisplay[context, p2, level+1]; BezierPatchDisplay[context, p3, level+1]; }; IF p # NIL THEN ShapeUtilities.ReleasePatch[p]; -- end of life for patch }; BezierTriangleDisplay: PROC[ context: REF Context, p: REF Patch, divisions: NAT] ~ { MdPt: PROC[v0, v1, v2: VertexInfo, r, s, t: REAL] RETURNS[VertexInfo] ~ { shape: REF ShapeInstance _ NARROW[ Atom.GetPropFromList[v0.props, $Shape] ]; lerpProc: VertexInfoProc _ IF shape # NIL THEN shape.shadingClass.lerpVtxAux ELSE NIL; v: VertexInfo; v.coord.sx _ r * v0.coord.sx + s * v1.coord.sx + t * v2.coord.sx; -- for position on screen v.coord.sy _ r * v0.coord.sy + s * v1.coord.sy + t * v2.coord.sy; v.coord.sz _ r * v0.coord.sz + s * v1.coord.sz + t * v2.coord.sz; v.coord.ex _ r * v0.coord.ex + s * v1.coord.ex + t * v2.coord.ex; -- for nml-vector shading v.coord.ey _ r * v0.coord.ey + s * v1.coord.ey + t * v2.coord.ey; v.coord.ez _ r * v0.coord.ez + s * v1.coord.ez + t * v2.coord.ez; v.shade.r _ r * v0.shade.r + s * v1.shade.r + t * v2.shade.r; -- clr/vtx (could be avoided) v.shade.g _ r * v0.shade.g + s * v1.shade.g + t * v2.shade.g; v.shade.b _ r * v0.shade.b + s * v1.shade.b + t * v2.shade.b; IF shape.shadingClass.texture # NIL THEN { v.coord.x _ r * v0.coord.x + s * v1.coord.x + t * v2.coord.x; v.coord.y _ r * v0.coord.y + s * v1.coord.y + t * v2.coord.y; v.coord.z _ r * v0.coord.z + s * v1.coord.z + t * v2.coord.z; }; IF lerpProc # NIL THEN { -- get auxiliary info from supplied proc data: LORA _ LIST[ v0.aux, v1.aux, NEW[REAL _ r], NEW[REAL _ s] ]; v _ lerpProc[ NIL, v, data]; data _ LIST[ v.aux, v2.aux, NEW[REAL _ r+s], NEW[REAL _ t] ]; v _ lerpProc[ NIL, v, data]; } ELSE v.aux _ v0.aux; v.props _ v0.props; RETURN[v]; }; EvalTriangle: PROC[p: REF Patch, r, s, t: REAL] RETURNS[VertexInfo] ~ { << [Artwork node; type 'Artwork on' to command tool] >> p1: ARRAY[0..6) OF VertexInfo; p2: ARRAY[0..3) OF VertexInfo; p3: VertexInfo; p1[0] _ MdPt[p[1], p[8], p[0], r,s,t]; -- p1[0,0,2] _ r*p[1,0,2] + s*p[0,1,2] + t*p[0,0,3]; p1[2] _ MdPt[p[3], p[4], p[2], r,s,t]; -- p1[2,0,0] _ r*p[3,0,0] + s*p[2,1,0] + t*p[2,0,1]; p1[4] _ MdPt[p[5], p[6], p[7], r,s,t]; -- p1[0,2,0] _ r*p[1,2,0] + s*p[0,3,0] + t*p[0,2,1]; p1[1] _ MdPt[p[2], p[9], p[1], r,s,t]; -- p1[1,0,1] _ r*p[2,0,1] + s*p[1,1,1] + t*p[1,0,2]; p1[3] _ MdPt[p[4], p[5], p[9], r,s,t]; -- p1[1,1,0] _ r*p[2,1,0] + s*p[1,2,0] + t*p[1,1,1]; p1[5] _ MdPt[p[9], p[7], p[8], r,s,t]; -- p1[0,1,1] _ r*p[1,1,1] + s*p[0,2,1] + t*p[0,1,2]; p2[0] _ MdPt[p1[1], p1[5], p1[0], r,s,t]; -- p2[0,0,1] _ r*p[1,0,1] + s*p[0,1,1] + t*p[0,0,2]; p2[1] _ MdPt[p1[2], p1[3], p1[1], r,s,t]; -- p2[1,0,0] _ r*p[2,0,0] + s*p[1,1,0] + t*p[1,0,1]; p2[2] _ MdPt[p1[3], p1[4], p1[5], r,s,t]; -- p2[0,1,0] _ r*p[1,1,0] + s*p[0,2,0] + t*p[0,1,1]; p3 _ MdPt[p2[1], p2[2], p2[0], r,s,t]; -- p3[0,0,0] _ r*p[1,0,0] + s*p[0,1,0] + t*p[0,0,1]; RETURN[p3]; }; steps: NAT _ INTEGER[Basics.BITSHIFT[ 1, divisions ]]; rotateBy: NAT; <> IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received IF p.clipState = out THEN RETURN[]; IF p.oneSided AND p.dir = back THEN RETURN[]; -- backfacing on closed surface IF p[6].coord.ez > p[3].coord.ez -- Reorder outer control points to sort for view THEN IF p[6].coord.ez > p[0].coord.ez THEN rotateBy _ 0 ELSE rotateBy _ 3 ELSE IF p[3].coord.ez > p[0].coord.ez THEN rotateBy _ 6 ELSE rotateBy _ 3; FOR i: NAT IN [0..3) DO -- put vtx 6 farthest from viewer tmpVtx: VertexInfo; SELECT rotateBy FROM 3 => { tmpVtx _ p[i]; p[i] _ p[i+3]; p[i+3] _ p[i+6]; p[i+6] _ tmpVtx }; 6 => { tmpVtx _ p[i]; p[i] _ p[i+6]; p[i+6] _ p[i+3]; p[i+3] _ tmpVtx }; ENDCASE; -- no changes needed if 6 already farthest ENDLOOP; FOR i: NAT IN [1..steps] DO -- walk across patch with barycentric coordinates ls: REAL _ IF context.antiAliasing THEN 1.0 * (i-1) / steps ELSE 1.0 * (steps-i+1) / steps; s: REAL _ IF context.antiAliasing THEN 1.0 * i / steps ELSE 1.0 * (steps-i) / steps; <> FOR j: NAT IN [1..i] DO lr: REAL _ 1.0 * (j-1) / steps; -- step from r=0 to r=s r: REAL _ 1.0 * j / steps; tp: REF Patch _ ShapeUtilities.GetPatch[3]; -- will be released by action proc tp.type _ $ConvexPolygon; tp.oneSided _ p.oneSided; tp.nVtces _ 3; tp.clipState _ p.clipState; tp.dir _ p.dir; tp.props _ p.props; tp[0] _ EvalTriangle[p, lr, ls, 1.0 - lr - ls]; tp[1] _ EvalTriangle[p, r, s, 1.0 - r - s]; tp[2] _ EvalTriangle[p, lr, s, 1.0 - lr - s]; ShapeUtilities.ShadePoly[context, tp]; IF showLines THEN tp.type _ $PolyLine; tp _ SurfaceRender.OutputPolygon[context, tp]; -- display IF j # i THEN { ttp: REF Patch _ ShapeUtilities.GetPatch[3]; -- will be released by action proc ttp.type _ $ConvexPolygon; ttp.oneSided _ p.oneSided; ttp.nVtces _ 3; ttp.clipState _ p.clipState; ttp.dir _ p.dir; ttp.props _ p.props; ttp[0] _ EvalTriangle[p, lr, ls, 1.0 - lr - ls]; ttp[1] _ EvalTriangle[p, r, ls, 1.0 - r - ls]; ttp[2] _ EvalTriangle[p, r, s, 1.0 - r - s]; ShapeUtilities.ShadePoly[context, ttp]; IF showLines THEN ttp.type _ $PolyLine; ttp _ SurfaceRender.OutputPolygon[context, ttp]; -- display }; ENDLOOP; ENDLOOP; IF p # NIL THEN ShapeUtilities.ReleasePatch[p]; -- end of life for patch }; BezierDisplayLines: PatchProc ~ { <> EvalCoords: PROC[ context: REF Context, patch: REF Patch, v0, v1, v2, v3: NAT, numPts: NAT _ 8 ] RETURNS [outP: REF Patch] ~ { clipState: ClipState; IF patch.clipState = in -- get clipState of convex hull THEN clipState _ in ELSE { outCode: OutCode _ LOOPHOLE[ Basics.BITOR[ Basics.BITOR[ LOOPHOLE[patch[v0].coord.clip], LOOPHOLE[patch[v1].coord.clip] ], Basics.BITOR[ LOOPHOLE[patch[v2].coord.clip], LOOPHOLE[patch[v3].coord.clip] ] ], OutCode ]; IF outCode = NoneOut THEN clipState _ in ELSE { outCode _ LOOPHOLE[ Basics.BITAND[ Basics.BITAND[ LOOPHOLE[patch[v0].coord.clip], LOOPHOLE[patch[v1].coord.clip] ], Basics.BITAND[ LOOPHOLE[patch[v2].coord.clip], LOOPHOLE[patch[v3].coord.clip] ] ], OutCode ]; IF outCode # NoneOut THEN clipState _ out ELSE clipState _ clipped; }; }; outP _ ShapeUtilities.GetPatch[numPts]; -- will be released by action proc FOR i: NAT IN [0..numPts) DO t: REAL _ Real.Float[i] / (numPts-1); t2: REAL _ t * t; t3: REAL _ t2 * t; f0: REAL _ -1.0*t3 + 3.0*t2 - 3.0*t + 1; f1: REAL _ 3.0*t3 - 6.0*t2 + 3.0*t; f2: REAL _ -3.0*t3 + 3.0*t2; f3: REAL _ 1.0*t3; IF clipState # in OR context.antiAliasing THEN { outP[i].coord.ex _ f0*patch[v0].coord.ex + f1*patch[v1].coord.ex + f2*patch[v2].coord.ex + f3*patch[v3].coord.ex; outP[i].coord.ey _ f0*patch[v0].coord.ey + f1*patch[v1].coord.ey + f2*patch[v2].coord.ey + f3*patch[v3].coord.ey; outP[i].coord.ez _ f0*patch[v0].coord.ez + f1*patch[v1].coord.ez + f2*patch[v2].coord.ez + f3*patch[v3].coord.ez; IF clipState # in THEN outP[i].coord.clip _ ShapeUtilities.GetClipCodeForPt[ context, [outP[i].coord.ex, outP[i].coord.ey, outP[i].coord.ez] ]; IF context.antiAliasing THEN { outP[i].shade.r _ f0*patch[v0].shade.r + f1*patch[v1].shade.r + f2*patch[v2].shade.r + f3*patch[v3].shade.r; outP[i].shade.g _ f0*patch[v0].shade.g + f1*patch[v1].shade.g + f2*patch[v2].shade.g + f3*patch[v3].shade.g; outP[i].shade.b _ f0*patch[v0].shade.b + f1*patch[v1].shade.b + f2*patch[v2].shade.b + f3*patch[v3].shade.b; }; } ELSE { outP[i].coord.sx _ f0*patch[v0].coord.sx + f1*patch[v1].coord.sx + f2*patch[v2].coord.sx + f3*patch[v3].coord.sx; outP[i].coord.sy _ f0*patch[v0].coord.sy + f1*patch[v1].coord.sy + f2*patch[v2].coord.sy + f3*patch[v3].coord.sy; outP[i].coord.sz _ f0*patch[v0].coord.sz + f1*patch[v1].coord.sz + f2*patch[v2].coord.sz + f3*patch[v3].coord.sz; }; outP[i].coord.x _ f0*patch[v0].coord.x + f1*patch[v1].coord.x -- needed for display lists + f2*patch[v2].coord.x + f3*patch[v3].coord.x; outP[i].coord.y _ f0*patch[v0].coord.y + f1*patch[v1].coord.y + f2*patch[v2].coord.y + f3*patch[v3].coord.y; outP[i].coord.z _ f0*patch[v0].coord.z + f1*patch[v1].coord.z + f2*patch[v2].coord.z + f3*patch[v3].coord.z; outP[i].shade.er _ f0*patch[v0].shade.er + f1*patch[v1].shade.er + f2*patch[v2].shade.er + f3*patch[v3].shade.er; outP[i].shade.eg _ f0*patch[v0].shade.eg + f1*patch[v1].shade.eg + f2*patch[v2].shade.eg + f3*patch[v3].shade.eg; outP[i].shade.eb _ f0*patch[v0].shade.eb + f1*patch[v1].shade.eb + f2*patch[v2].shade.eb + f3*patch[v3].shade.eb; ENDLOOP; outP.type _ $PolyLine; outP.props _ patch.props; outP.clipState _ clipState; outP.nVtces _ numPts; }; path: REF Patch; IF patch.nVtces = 16 THEN { path _ EvalCoords[ context, patch, 0, 1, 2, 3 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch,12,13,14,15 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 0, 4, 8,12 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 3, 7,11,15 ]; [] _ SurfaceRender.OutputPolygon[context, path]; } ELSE IF patch.nVtces = 10 THEN { path _ EvalCoords[ context, patch, 0, 1, 2, 3 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 3, 4, 5, 6 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 6, 7, 8, 0 ]; [] _ SurfaceRender.OutputPolygon[context, path]; }; IF data # $DontRelease THEN ShapeUtilities.ReleasePatch[patch]; -- end of life for patch RETURN[NIL]; -- end of the line, no patch returned }; BezierDisplayCtrlPts: PatchProc ~ { <> EvalCoords: PROC[ context: REF Context, patch: REF Patch, v0, v1, v2, v3: NAT, numPts: NAT _ 4 ] RETURNS [outP: REF Patch] ~ { clipState: ClipState; IF patch.clipState = in -- get clipState of convex hull THEN clipState _ in ELSE { outCode: OutCode _ LOOPHOLE[ Basics.BITOR[ Basics.BITOR[ LOOPHOLE[patch[v0].coord.clip], LOOPHOLE[patch[v1].coord.clip] ], Basics.BITOR[ LOOPHOLE[patch[v2].coord.clip], LOOPHOLE[patch[v3].coord.clip] ] ], OutCode ]; IF outCode = NoneOut THEN clipState _ in ELSE { outCode _ LOOPHOLE[ Basics.BITAND[ Basics.BITAND[ LOOPHOLE[patch[v0].coord.clip], LOOPHOLE[patch[v1].coord.clip] ], Basics.BITAND[ LOOPHOLE[patch[v2].coord.clip], LOOPHOLE[patch[v3].coord.clip] ] ], OutCode ]; IF outCode # NoneOut THEN clipState _ out ELSE clipState _ clipped; }; }; outP _ ShapeUtilities.GetPatch[4]; -- will be released by action proc outP[0] _ patch[v0]; outP[1] _ patch[v1]; outP[2] _ patch[v2]; outP[3] _ patch[v3]; outP.type _ $PolyLine; outP.props _ patch.props; outP.clipState _ clipState; outP.nVtces _ numPts; }; path: REF Patch; IF patch.nVtces = 16 THEN { path _ EvalCoords[ context, patch, 0, 1, 2, 3 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 4, 5, 6, 7 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 8, 9,10,11 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch,12,13,14,15 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 0, 4, 8,12 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 1, 5, 9,13 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 2, 6,10,14 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 3, 7,11,15 ]; [] _ SurfaceRender.OutputPolygon[context, path]; } ELSE IF patch.nVtces = 10 THEN { path _ EvalCoords[ context, patch, 0, 1, 2, 3 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 3, 4, 5, 6 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 6, 7, 8, 0 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 9, 8, 1, 9 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 9, 7, 4, 9 ]; [] _ SurfaceRender.OutputPolygon[context, path]; path _ EvalCoords[ context, patch, 9, 5, 7, 9 ]; [] _ SurfaceRender.OutputPolygon[context, path]; }; IF data # $DontRelease THEN ShapeUtilities.ReleasePatch[patch]; -- end of life for patch RETURN[NIL]; -- end of the line, no patch returned }; PolygonFromBezierPatch: PROC[p: REF Patch] RETURNS [REF Patch] ~ { <> leftCrnr: ARRAY [0..16) OF NAT _ [12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 3]; rghtCrnr: ARRAY [0..16) OF NAT _ [3, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12]; GetNorm: PROC[v, lft, nxtLft, rgt, nxtRgt: NAT] RETURNS[VertexInfo] ~ { <> normal: Triple; d1: Triple _ DiffPosns[ p[lft].coord, p[v].coord, $Eye ]; -- results zero if < 6 sig. digits d2: Triple _ DiffPosns[ p[rgt].coord, p[v].coord, $Eye ]; IF G3dVector.Null[ d1 ] THEN d1 _ DiffPosns[ p[nxtLft].coord, p[v].coord, $Eye ]; IF G3dVector.Null[ d2 ] THEN d2 _ DiffPosns[ p[nxtRgt].coord, p[v].coord, $Eye ]; normal _ G3dVector.Normalize[ G3dVector.Cross[d2, d1 ] ]; <> IF NOT G3dVector.Null[ DiffTriple[ d2, G3dVector.Add[d2, normal] ] ] THEN [p[v].shade.exn, p[v].shade.eyn, p[v].shade.ezn] _ normal -- significant length ELSE IF p.nVtces = 16 THEN { -- use polygon corners, too many degenerate knots d1 _ DiffPosns[ p[leftCrnr[v]].coord, p[v].coord, $Eye ]; d2 _ DiffPosns[ p[rghtCrnr[v]].coord, p[v].coord, $Eye ]; IF G3dVector.Null[ d1 ] THEN d1 _ DiffPosns[ p[leftCrnr[leftCrnr[v]]].coord, p[v].coord, $Eye ]; IF G3dVector.Null[ d2 ] THEN d2 _ DiffPosns[ p[rghtCrnr[rghtCrnr[v]]].coord, p[v].coord, $Eye ]; normal _ G3dVector.Normalize[ G3dVector.Cross[d2, d1 ] ]; IF NOT G3dVector.Null[ DiffTriple[ d2, G3dVector.Add[d2, normal] ] ] THEN [p[v].shade.exn, p[v].shade.eyn, p[v].shade.ezn] _ normal ELSE [p[v].shade.exn, p[v].shade.eyn, p[v].shade.ezn] _ G3dBasic.origin; } ELSE [p[v].shade.exn, p[v].shade.eyn, p[v].shade.ezn] _ G3dBasic.origin; RETURN [p[v]]; }; <> outputType: ATOM _ NARROW[ Atom.GetPropFromList[ p.props, $InterimPatchType ] ]; IF outputType = NIL THEN outputType _ $ConvexPolygon; p.type _ outputType; -- type of output patch IF p.nVtces = 16 THEN { <> << [Artwork node; type 'Artwork on' to command tool] >> p[0] _ GetNorm[v: 0, lft: 1, nxtLft: 7, rgt: 4, nxtRgt: 13]; -- use next control pt and p[3] _ GetNorm[v: 3, lft: 7, nxtLft: 14, rgt: 2, nxtRgt: 4]; -- pt around next corner p[15] _ GetNorm[v: 15, lft: 14, nxtLft: 8, rgt: 11, nxtRgt: 2]; -- to catch degeneracies p[12] _ GetNorm[v: 12, lft: 8, nxtLft: 1, rgt: 13, nxtRgt: 11]; p.nVtces _ 4; -- copy corners (p[0], p[3] stay put) p[1] _ CopyVtx[ p[12] ]^; p[2] _ CopyVtx[ p[15] ]^; } ELSE IF p.nVtces = 10 THEN { -- triangular patches taken clockwise, last pt in middle p[0] _ GetNorm[v: 0, lft: 1, nxtLft: 4, rgt: 8, nxtRgt: 5]; -- use next control pt and p[3] _ GetNorm[v: 3, lft: 4, nxtLft: 7, rgt: 2, nxtRgt: 8]; -- pt around next corner p[6] _ GetNorm[v: 6, lft: 5, nxtLft: 2, rgt: 7, nxtRgt: 1]; -- to catch degeneracies p.nVtces _ 3; -- copy corners (p[0] stays put) p[1] _ CopyVtx[ p[3] ]^; p[2] _ CopyVtx[ p[6] ]^; }; FOR i: NAT IN [0..p.nVtces) DO -- check for null normals (aligned vertices at corner) IF G3dVector.Null[ [p[i].shade.exn, p[i].shade.eyn, p[i].shade.ezn] ] THEN { j: NAT _ (i+1) MOD p.nVtces; -- found a null normal, try next vertex in order p[i].shade.exn _ p[j].shade.exn; p[i].shade.eyn _ p[j].shade.eyn; p[i].shade.ezn _ p[j].shade.ezn; IF G3dVector.Null[ [p[i].shade.exn, p[i].shade.eyn, p[i].shade.ezn] ] THEN { IF stopOnDegenerate THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Very degenerate Patch"]]; p.nVtces _ 2; -- 2 null nmls, polygon will be ignored by scan converter RETURN[p]; }; }; ENDLOOP; IF showLines -- extra vertex closes path for lines THEN { p[p.nVtces] _ CopyVtx[ p[0] ]^; p.nVtces _ p.nVtces+1; }; RETURN[p]; }; BezierBackFacing: PROC[context: REF Context, p: REF Patch] RETURNS[BOOLEAN] ~ { <> facing: FacingDir; back, front, undetermined: BOOLEAN _ FALSE; subdir: FacingDirArray; IF p.dir = front THEN RETURN[FALSE]; IF p.dir = back THEN RETURN[TRUE]; FOR i: NAT IN [0..3) DO FOR j: NAT IN [0..3) DO patch: REF Patch _ ShapeUtilities.GetPatch[3]; patch.type _ $ConvexPolygon; IF p.nVtces = 16 THEN { k: NAT _ 4*i + j; -- index of lower left corner patch[0] _ p[k]; patch[1] _ p[k + 4]; patch[2] _ p[k + 1]; } ELSE IF p.nVtces = 10 THEN { -- triangular patch k: NAT _ 3*j; SELECT i FROM 0 => { patch[0] _ p[k]; patch[1] _ p[k+1]; patch[2] _ p[(k+8) MOD 9]; }; 1 => { patch[0] _ p[k+1]; patch[1] _ p[k+2]; patch[2] _ p[9]; }; 2 => { patch[0] _ p[k+1]; patch[1] _ p[9]; patch[2] _ p[(k+8) MOD 9]; }; ENDCASE; }; facing _ ShapeUtilities.BackFacing[ context, patch, TRUE ! ThreeDBasics.Error => IF reason.code = $Condition THEN { facing _ undetermined; CONTINUE; } ]; IF facing = back THEN back _ TRUE ELSE IF facing = front THEN front _ TRUE ELSE IF facing = undetermined THEN undetermined _ TRUE; ShapeUtilities.ReleasePatch[patch]; subdir[i*3 + j] _ facing; ENDLOOP; ENDLOOP; IF (back AND front) OR undetermined THEN p.dir _ undetermined ELSE IF back THEN p.dir _ back ELSE IF front THEN p.dir _ front ELSE p.dir _ undetermined; IF p.dir = undetermined THEN p.props _ PutPropSafely[p.props, $FacingDirArray, NEW[FacingDirArray _ subdir] ]; IF p.dir = back THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; BezierPatchDivide: PROC[context: REF Context, p: REF Patch, midPt: MidPtProc] RETURNS[REF Patch, REF Patch, REF Patch, REF Patch] ~ { <> outPatch: ARRAY [0..4) OF REF Patch; row: ARRAY [0..8) OF REF VertexInfoSequence; col: ARRAY [0..4) OF REF VertexInfoSequence; <> <> <<3 -> 0 3 -> 0 3 2 1 0>> <<^ ^ 7 6 5 4>> <<2 <- 1 15 <- 12 11 10 9 8>> << 15 14 13 12>> <> IF Atom.GetPropFromList[p[ 0].props, $Straight] = $DoIt THEN SetStraight[ p, 0, 4, 8,12 ]; IF Atom.GetPropFromList[p[12].props, $Straight] = $DoIt THEN SetStraight[ p,12,13,14,15 ]; IF Atom.GetPropFromList[p[15].props, $Straight] = $DoIt THEN SetStraight[ p,15,11, 7, 3 ]; IF Atom.GetPropFromList[p[ 3].props, $Straight] = $DoIt THEN SetStraight[ p, 3, 2, 1, 0 ]; FOR i: NAT IN [0..4) DO p0, p1, p2, p3: REF VertexInfo; p0 _ CopyVtx[ p[i] ]; p1 _ CopyVtx[ p[i+4] ]; p2 _ CopyVtx[ p[i+8] ]; p3 _ CopyVtx[ p[i+12] ]; <<[p0, p1, p2, p3] => [col[0], col[1], col[2], col[3], col[4], col[5], col[6], col[7]]>> <> SELECT i FROM 0 => col[0] _ BezierCurveDivide[ p0, p1, p2, p3, midPt, front ]; -- p[0]-p[12], 0 leads 3 => col[3] _ BezierCurveDivide[ p0, p1, p2, p3, midPt, back ]; -- p[3]-p[15], 15 leads ENDCASE => col[i] _ BezierCurveDivide[ p0, p1, p2, p3, midPt, undetermined ]; ShapeUtilities.ReleaseVertexInfo[p0]; ShapeUtilities.ReleaseVertexInfo[p1]; ShapeUtilities.ReleaseVertexInfo[p2]; ShapeUtilities.ReleaseVertexInfo[p3]; ENDLOOP; FOR i: NAT IN [0..8) DO < row[0],row[1],row[2],row[3], row[4],row[5],row[6],row[7]>> <> SELECT i FROM 0 => -- p[0] - p[3], 3 leads row[0] _ BezierCurveDivide[ col[0][0], col[1][0], col[2][0], col[3][0], midPt, back ]; 7 => -- p[12] - p[15], 12 leads row[7] _ BezierCurveDivide[ col[0][7], col[1][7], col[2][7], col[3][7], midPt, front ]; ENDCASE => row[i] _ BezierCurveDivide[ col[0][i], col[1][i], col[2][i], col[3][i], midPt, undetermined ]; ENDLOOP; FOR i: NAT IN [0..4) DO rowBase: NAT _ (i MOD 2) * 4; colBase: NAT _ (i / 2) * 4; outPatch[i] _ ShapeUtilities.GetPatch[16]; -- 16 point patch, released by display action outPatch[i].type _ p.type; outPatch[i].oneSided _ p.oneSided; outPatch[i].nVtces _ 16; outPatch[i].clipState _ p.clipState; outPatch[i].dir _ p.dir; outPatch[i].props _ p.props; FOR j: NAT IN [0..4) DO FOR k: NAT IN [0..4) DO outPatch[i][j*4 + k] _ row[colBase+j][rowBase+k]^; -- count along rows <> ENDLOOP; ENDLOOP; IF outPatch[i].clipState # in THEN { FOR j: NAT IN [0..16) DO OPEN outPatch[i][j].coord; clip _ ShapeUtilities.GetClipCodeForPt[ context, [ex, ey, ez] ]; IF clip = NoneOut THEN [ [sx, sy, sz] ] _ ShapeUtilities.XfmPtToDisplay[ context, [ex, ey, ez] ]; ENDLOOP; outPatch[i] _ BezierClipState[context, outPatch[i] ]; }; IF outPatch[i].dir = undetermined THEN [] _ BezierBackFacing[context, outPatch[i]]; ENDLOOP; FOR i: NAT IN [0..8) DO FOR j: NAT IN [0..row[i].length) DO -- return borrowed storage ShapeUtilities.ReleaseVertexInfo[ row[i][j] ]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..4) DO FOR j: NAT IN [0..col[i].length) DO ShapeUtilities.ReleaseVertexInfo[ col[i][j] ]; ENDLOOP; ENDLOOP; RETURN[ outPatch[0], outPatch[1], outPatch[2], outPatch[3] ]; -- return four sub-patches }; BezierCurveDivide: PROC[v0, v1, v2, v3: REF VertexInfo, midPt: MidPtProc, dir: FacingDir] RETURNS[pt: REF VertexInfoSequence] ~ { <> tempPt: REF VertexInfo; pt _ NEW[ VertexInfoSequence[8] ]; pt.length _ 8; pt[0] _ CopyVtx[ v0^ ]; pt[7] _ CopyVtx[ v3^ ]; -- preserve endpoints pt[1] _ midPt[v1, v0]; pt[6] _ midPt[v2, v3]; -- tangent midpoints tempPt _ midPt[v1, v2]; -- midway between inner knots pt[2] _ midPt[pt[1], tempPt]; pt[5] _ midPt[pt[6], tempPt]; -- midway between midpoints pt[3] _ midPt[pt[2], pt[5]]; -- midway between midways between midpoints pt[4] _ CopyVtx[ pt[3]^ ]; IF dir = front -- inner ends inherit outer props if on directed patch boundary THEN pt[4].props _ pt[0].props ELSE IF dir = back THEN pt[3].props _ pt[7].props; ShapeUtilities.ReleaseVertexInfo[ tempPt ]; }; BezierClipState: PatchProc ~ { <> ShapeUtilities.GetPatchClipState[ patch ]; IF patch.clipState = clipped THEN { FOR i: NAT IN [0..patch.nVtces) DO IF patch[i].coord.clip # NoneOut THEN { OPEN patch[i].coord; sx _ context.eyeToNdc.scaleX * ex / ez + context.eyeToNdc.addX; sy _ context.eyeToNdc.scaleY * ey / ez + context.eyeToNdc.addY; sz _ context.eyeToNdc.scaleZ / ez + context.eyeToNdc.addZ; IF sx < -0.5 OR sx > 1.5 OR sy < -0.5 OR sy > 1.5 OR sz < 0.0 THEN patch.clipState _ undetermined -- too far out for stability (arbitrary) ELSE { -- convert to screen coordinates sx _ context.ndcToPixels.scaleX * sx + context.ndcToPixels.addX; sy _ context.ndcToPixels.scaleY * sy + context.ndcToPixels.addY; sz _ context.ndcToPixels.scaleZ * sz + context.ndcToPixels.addZ; IF context.antiAliasing THEN { sx _ sx - 0.5; sy _ sy - 0.5; }; }; }; ENDLOOP; }; RETURN[patch]; }; <> BSplineDisplay: PatchProc ~ { RETURN[NIL]; }; BSplineDisplayLines: PatchProc ~ { RETURN[NIL]; }; <> NonConvexDisplay: PatchProc ~ { RETURN[NIL]; }; InitClasses[]; END.