DIRECTORY Atom USING [ GetPropFromList ], Basics USING [ BITOR, BITAND ], Real USING [ FixC, Float ], RealFns USING [ SqRt ], Vector3d USING [ Normalize, Cross, Dot, Null ], ScanConvert USING [ justNoticeable ], ThreeDBasics USING [ ClipState, Context, NatSequence, NoneOut, OutCode, ShadingSequence, ShadingValue, ShapeInstance, SixSides, Triple, Vertex, VertexInfo, VertexInfoProc, VertexInfoSequence, VertexSequence ], ThreeDScenes USING [ GetClipCodeForPt, XfmPtToDisplay ], ThreeDSurfaces USING [ GetPatch, GetPatchClipState, Patch, PtrPatchSequence, PtrPatch, PatchSequence, PatchExpandProc, PatchDisplayProc, ReleasePatch, ShadePoly ], StandardPatches; StandardPatchesImpl: CEDAR MONITOR IMPORTS Atom, Basics, Real, RealFns, Vector3d, ThreeDScenes, ThreeDSurfaces EXPORTS StandardPatches = BEGIN StandardPatchesError: PUBLIC SIGNAL [reason: ATOM] = CODE; Context: TYPE ~ ThreeDBasics.Context; SixSides: TYPE ~ ThreeDBasics.SixSides; Triple: TYPE ~ ThreeDBasics.Triple; NatSequence: TYPE ~ ThreeDBasics.NatSequence; ShapeInstance: TYPE ~ ThreeDBasics.ShapeInstance; Patch: TYPE ~ ThreeDSurfaces.Patch; PatchSequence: TYPE ~ ThreeDSurfaces.PatchSequence; PtrPatch: TYPE ~ ThreeDSurfaces.PtrPatch; PtrPatchSequence: TYPE ~ ThreeDSurfaces.PtrPatchSequence; Vertex: TYPE ~ ThreeDBasics.Vertex; VertexInfo: TYPE ~ ThreeDBasics.VertexInfo; VertexInfoSequence: TYPE ~ ThreeDBasics.VertexInfoSequence; MidPtProc: TYPE ~ PROC[v0, v1: REF VertexInfo] RETURNS[REF VertexInfo]; vertexStore: REF VertexInfoSequence _ NEW[ VertexInfoSequence[96] ]; -- Vertex Cache vertexStoreLength: NAT _ 32; vertexStorePtr: NAT _ 0; -- place to return next free record Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; }; GetVertexInfo: ENTRY PROC[] RETURNS[REF VertexInfo] ~ { ENABLE UNWIND => NULL; vtx: REF VertexInfo; IF vertexStorePtr = 0 THEN vtx _ NEW[VertexInfo] ELSE { vertexStorePtr _ vertexStorePtr - 1; vtx _ vertexStore[vertexStorePtr]; vertexStore[vertexStorePtr] _ NIL; }; RETURN[ vtx ]; }; ReleaseVertexInfo: ENTRY PROC[vtx: REF VertexInfo] ~ { ENABLE UNWIND => NULL; IF vertexStorePtr = vertexStoreLength THEN { vertexStore _ NEW[ VertexInfoSequence[vertexStoreLength + 32] ]; vertexStoreLength _ vertexStoreLength + 32; vertexStorePtr _ 0; }; vertexStore[vertexStorePtr] _ vtx; vertexStorePtr _ vertexStorePtr + 1; }; DiffPosns: PROC[vtx1, vtx2: Vertex, space: ATOM _ NIL] RETURNS[Triple] ~ { SELECT space FROM $Eye => RETURN[[vtx1.ex - vtx2.ex, vtx1.ey - vtx2.ey, vtx1.ez - vtx2.ez]]; $Screen => RETURN[[vtx1.sx - vtx2.sx, vtx1.sy - vtx2.sy, vtx1.sz - vtx2.sz]]; ENDCASE => RETURN[[vtx1.x - vtx2.x, vtx1.y - vtx2.y, vtx1.z - vtx2.z]]; -- object space }; VtxShapeMidPt: MidPtProc ~ { v: REF VertexInfo _ GetVertexInfo[]; 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; v.shade.xn _ (v0.shade.xn + v1.shade.xn) / 2; v.shade.yn _ (v0.shade.yn + v1.shade.yn) / 2; v.shade.zn _ (v0.shade.zn + v1.shade.zn) / 2; v.shade.r _ (v0.shade.r + v1.shade.r) / 2; v.shade.g _ (v0.shade.g + v1.shade.g) / 2; v.shade.b _ (v0.shade.b + v1.shade.b) / 2; v.shade.t _ (v0.shade.t + v1.shade.t) / 2; IF v0.aux # NIL AND v1.aux # NIL THEN { -- get auxiliary info from supplied proc ref: REF ANY _ Atom.GetPropFromList[v0.props, $ShadeVtx]; IF ref # NIL THEN { -- call custom interpolator lerpProc: REF ThreeDBasics.VertexInfoProc _ NARROW[ref]; data: LIST OF REF ANY _ LIST[ v0.aux, v1.aux, NEW[REAL _ .5], NEW[REAL _ .5] ]; [] _ lerpProc^[ NIL, v, data]; } ELSE v.aux _ v0.aux; }; RETURN[v]; }; VtxDisplayMidPt: MidPtProc ~ { v: REF VertexInfo _ GetVertexInfo[]; 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; v.coord.ex _ (v0.coord.ex + v1.coord.ex) / 2; v.coord.ey _ (v0.coord.ey + v1.coord.ey) / 2; v.coord.ez _ (v0.coord.ez + v1.coord.ez) / 2; v.coord.sx _ (v0.coord.sx + v1.coord.sx) / 2; v.coord.sy _ (v0.coord.sy + v1.coord.sy) / 2; v.coord.sz _ (v0.coord.sz + v1.coord.sz) / 2; v.shade.exn _ (v0.shade.exn + v1.shade.exn) / 2; v.shade.eyn _ (v0.shade.eyn + v1.shade.eyn) / 2; v.shade.ezn _ (v0.shade.ezn + v1.shade.ezn) / 2; v.shade.r _ (v0.shade.r + v1.shade.r) / 2; v.shade.g _ (v0.shade.g + v1.shade.g) / 2; v.shade.b _ (v0.shade.b + v1.shade.b) / 2; v.shade.t _ (v0.shade.t + v1.shade.t) / 2; v.shade.er _ (v0.shade.er + v1.shade.er) / 2; v.shade.eg _ (v0.shade.eg + v1.shade.eg) / 2; v.shade.eb _ (v0.shade.eb + v1.shade.eb) / 2; v.shade.et _ (v0.shade.et + v1.shade.et) / 2; IF v0.aux # NIL AND v1.aux # NIL THEN { -- get auxiliary info from supplied proc ref: REF ANY _ Atom.GetPropFromList[v0.props, $ShadeVtx]; IF ref # NIL THEN { -- call custom interpolator lerpProc: REF ThreeDBasics.VertexInfoProc _ NARROW[ref]; data: LIST OF REF ANY _ LIST[ v0.aux, v1.aux, NEW[REAL _ .5], NEW[REAL _ .5] ]; [] _ lerpProc^[ NIL, v, data]; } ELSE v.aux _ v0.aux; }; RETURN[v]; }; ApplyCubicBasis: PROC[ v0, v1, v2, v3: REF VertexInfo, f0, f1, f2, f3: REAL] RETURNS[vOut: REF VertexInfo] ~ { vOut _ GetVertexInfo[]; vOut.coord.x _ f0*v0.coord.x + f1*v1.coord.x + f2*v2.coord.x + f3*v3.coord.x; vOut.coord.y _ f0*v0.coord.y + f1*v1.coord.y + f2*v2.coord.y + f3*v3.coord.y; vOut.coord.z _ f0*v0.coord.z + f1*v1.coord.z + f2*v2.coord.z + f3*v3.coord.z; vOut.shade.xn _ f0*v0.shade.xn + f1*v1.shade.xn + f2*v2.shade.xn + f3*v3.shade.xn; vOut.shade.yn _ f0*v0.shade.yn + f1*v1.shade.yn + f2*v2.shade.yn + f3*v3.shade.yn; vOut.shade.zn _ f0*v0.shade.zn + f1*v1.shade.zn + f2*v2.shade.zn + f3*v3.shade.zn; vOut.shade.r _ f0*v0.shade.r + f1*v1.shade.r + f2*v2.shade.r + f3*v3.shade.r; vOut.shade.g _ f0*v0.shade.g + f1*v1.shade.g + f2*v2.shade.g + f3*v3.shade.g; vOut.shade.b _ f0*v0.shade.b + f1*v1.shade.b + f2*v2.shade.b + f3*v3.shade.b; vOut.shade.t _ f0*v0.shade.t + f1*v1.shade.t + f2*v2.shade.t + f3*v3.shade.t; IF v0.aux # NIL THEN { -- get auxiliary info from supplied proc ref: REF ANY _ Atom.GetPropFromList[v0.props, $ShadeVtx]; IF ref # NIL THEN { -- call custom interpolator 4 times to sum basis fns lerpProc: REF ThreeDBasics.VertexInfoProc _ NARROW[ref]; data: LIST OF REF ANY _ LIST[v0.aux, vOut.aux, NEW[REAL _ f0], NEW[REAL _ 0.0]]; [] _ lerpProc^[ NIL, vOut, data]; data _ LIST[ v1.aux, vOut.aux, NEW[REAL _ f1], NEW[REAL _ 1.0]]; [] _ lerpProc^[ NIL, vOut, data]; data _ LIST[ v2.aux, vOut.aux, NEW[REAL _ f2], NEW[REAL _ 1.0]]; [] _ lerpProc^[ NIL, vOut, data]; data _ LIST[ v3.aux, vOut.aux, NEW[REAL _ f3], NEW[REAL _ 1.0]]; [] _ lerpProc^[ NIL, vOut, data]; } ELSE vOut.aux _ v0.aux; }; }; LerpToVtx: PROC[ v0, v1, vMid: VertexInfo] RETURNS[VertexInfo] ~ { a, b, alpha: REAL; v: VertexInfo; mag: REAL _ RealFns.SqRt[ Sqr[v1.coord.sx - v0.coord.sx] + Sqr[v1.coord.sy - v0.coord.sy] ]; IF mag < ScanConvert.justNoticeable THEN RETURN[v0]; -- effectively invisible detail 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; v.coord.x _ a * v0.coord.x + b * v1.coord.x ; v.coord.y _ a * v0.coord.y + b * v1.coord.y ; v.coord.z _ a * v0.coord.z + b * v1.coord.z ; v.coord.ex _ a * v0.coord.ex + b * v1.coord.ex ; v.coord.ey _ a * v0.coord.ey + b * v1.coord.ey ; v.coord.ez _ a * v0.coord.ez + b * v1.coord.ez ; v.coord.sx _ a * v0.coord.sx + b * v1.coord.sx ; v.coord.sy _ a * v0.coord.sy + b * v1.coord.sy ; v.coord.sz _ a * v0.coord.sz + b * v1.coord.sz ; v.shade.exn _ a * v0.shade.exn + b * v1.shade.exn ; v.shade.eyn _ a * v0.shade.eyn + b * v1.shade.eyn ; v.shade.ezn _ a * v0.shade.ezn + b * v1.shade.ezn ; v.shade.r _ a * v0.shade.r + b * v1.shade.r ; v.shade.g _ a * v0.shade.g + b * v1.shade.g ; v.shade.b _ a * v0.shade.b + b * v1.shade.b ; v.shade.t _ a * v0.shade.t + b * v1.shade.t ; v.shade.er _ a * v0.shade.er + b * v1.shade.er ; v.shade.eg _ a * v0.shade.eg + b * v1.shade.eg ; v.shade.eb _ a * v0.shade.eb + b * v1.shade.eb ; v.shade.et _ a * v0.shade.et + b * v1.shade.et ; IF v0.aux # NIL AND v1.aux # NIL THEN { -- get auxiliary info from supplied proc ref: REF ANY _ Atom.GetPropFromList[v0.props, $ShadeVtx]; IF ref # NIL THEN { -- call custom interpolator lerpProc: REF ThreeDBasics.VertexInfoProc _ NARROW[ref]; data: LIST OF REF ANY _ LIST[ v0.aux, v1.aux, NEW[REAL _ a], NEW[REAL _ b] ]; v _ lerpProc^[ NIL, NEW[VertexInfo _ v], data]^; } ELSE v.aux _ v0.aux; }; RETURN[v]; }; StraightWithin: PROC[ p: REF Patch, v0, v1, v2, v3: NAT, tol: REAL] RETURNS[BOOLEAN] ~ { dist, a, b, mag: REAL; IF p[v3].coord.sx < p[v0].coord.sx THEN { -- enforce consistent evaluation t: NAT _ v3; v3 _ v0; v0 _ t; t _ v2; v2 _ v1; v1 _ t; }; a _ p[v3].coord.sy - p[v0].coord.sy; b _ p[v3].coord.sx - p[v0].coord.sx; mag _ RealFns.SqRt[a*a + b*b]; IF mag > tol / 1000.0 THEN { a _ a/mag; b _ b/mag; dist _ a * (p[v1].coord.sx - p[v0].coord.sx) + b * (p[v0].coord.sy - p[v1].coord.sy); IF ABS[dist] > tol THEN RETURN[FALSE]; dist _ a * (p[v2].coord.sx - p[v0].coord.sx) + b * (p[v0].coord.sy - p[v2].coord.sy); IF ABS[dist] > tol THEN RETURN[FALSE]; }; p[v1] _ LerpToVtx[ p[v0], p[v3], p[v1] ]; p[v2] _ LerpToVtx[ p[v0], p[v3], p[v2] ]; RETURN[TRUE]; }; 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.alphaBuffer THEN RETURN[ pOut[0], pOut[1], pOut[2], pOut[3] ] ELSE RETURN[ pOut[3], pOut[2], pOut[1], pOut[0] ]; }; BezierExpand: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { nPerSide: NAT _ Real.FixC[limit]; IF limitType # NIL THEN SIGNAL StandardPatchesError[$Unimplemented]; [v, p] _ BezierParametricExpand[shape, patch, nPerSide]; RETURN[ v, p ]; }; BezierSubdivide: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { IF limitType # NIL THEN SIGNAL StandardPatchesError[$Unimplemented]; [v, p] _ BezierSubdivideExpand[shape, patch]; RETURN[ v, p ]; }; minLimit: REAL _ .25; -- subdivision precision limit recurseLimit: NAT _ 14; -- safety valve BezierDisplay: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { Display: PROC[ p: REF Patch, level: NAT] ~ { allStraight: BOOLEAN _ TRUE; IF (p.clipState = out) OR BezierBackFacing[p] THEN RETURN[]; IF NOT StraightWithin[p, 0, 1, 2, 3, limit] THEN allStraight _ FALSE; IF NOT StraightWithin[p, 0, 4, 8, 12, limit] THEN allStraight _ FALSE; IF NOT StraightWithin[p, 3, 7, 11,15, limit] THEN allStraight _ FALSE; IF NOT StraightWithin[p, 12,13,14,15, limit] THEN allStraight _ FALSE; IF allStraight OR level >= recurseLimit THEN { -- outer edges straight, display as polygon p.nVtces _ 4; p[1] _ p[12]; p[2] _ p[15]; -- copy corners (p[0], p[3] stay put) p.type _ outputType; -- type of output patch ThreeDSurfaces.ShadePoly[context, p]; action[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.depthBuffer THEN [p0, p1, p2, p3] _ PatchSort[ context, p0, p1, p2, p3]; -- sort to display order Display[p0, level+1]; Display[p1, level+1]; Display[p2, level+1]; Display[p3, level+1]; -- display in order }; }; outputType: ATOM _ NARROW[ Atom.GetPropFromList[ patch.props, $InterimPatchType ] ]; IF outputType = NIL THEN outputType _ $ConvexPolygon; IF limitType # NIL AND limitType # $StraightEdges THEN SIGNAL StandardPatchesError[$Unimplemented]; limit _ MAX[limit, minLimit]; -- straight to precision limit is sufficient (it says here) BezierPatchNormals[patch]; -- Get Normals at control points Display[patch, 0]; -- Divide till all outside patch edges are straight or recursion exceeded ThreeDSurfaces.ReleasePatch[patch]; -- end of life for patch }; BezierDisplayLines: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { EvalCoords: PROC[ context: REF Context, patch: REF Patch, v0, v1, v2, v3: NAT, numPts: NAT _ 8 ] RETURNS [outP: REF Patch] ~ { clipState: ThreeDBasics.ClipState; IF patch.clipState = in -- get clipState of convex hull THEN clipState _ in ELSE { outCode: ThreeDBasics.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] ] ], ThreeDBasics.OutCode ]; IF outCode = ThreeDBasics.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] ] ], ThreeDBasics.OutCode ]; IF outCode # ThreeDBasics.NoneOut THEN clipState _ out ELSE clipState _ clipped; }; }; outP _ ThreeDSurfaces.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.alphaBuffer 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 _ ThreeDScenes.GetClipCodeForPt[ context, [outP[i].coord.ex, outP[i].coord.ey, outP[i].coord.ez] ]; IF context.alphaBuffer THEN { -- set up color for display outP[i].shade.er _ f0*patch[v0].shade.r + f1*patch[v1].shade.r + f2*patch[v2].shade.r + f3*patch[v3].shade.r; outP[i].shade.eg _ f0*patch[v0].shade.g + f1*patch[v1].shade.g + f2*patch[v2].shade.g + f3*patch[v3].shade.g; outP[i].shade.eb _ 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; }; ENDLOOP; outP.type _ $Path; outP.clipState _ clipState; outP.nVtces _ numPts; }; path: REF Patch; path _ EvalCoords[ context, patch, 0, 1, 2, 3 ]; action[context, path ]; path _ EvalCoords[ context, patch, 12, 13, 14, 15 ]; action[context, path ]; path _ EvalCoords[ context, patch, 0, 4, 8, 12 ]; action[context, path ]; path _ EvalCoords[ context, patch, 3, 7, 11, 15 ]; action[context, path ]; ThreeDSurfaces.ReleasePatch[patch]; -- end of life for patch }; BezierDisplayCtrlPts: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { EvalCoords: PROC[ context: REF Context, patch: REF Patch, v0, v1, v2, v3: NAT, numPts: NAT _ 4 ] RETURNS [outP: REF Patch] ~ { clipState: ThreeDBasics.ClipState; IF patch.clipState = in -- get clipState of convex hull THEN clipState _ in ELSE { outCode: ThreeDBasics.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] ] ], ThreeDBasics.OutCode ]; IF outCode = ThreeDBasics.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] ] ], ThreeDBasics.OutCode ]; IF outCode # ThreeDBasics.NoneOut THEN clipState _ out ELSE clipState _ clipped; }; }; outP _ ThreeDSurfaces.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 _ $Path; outP.clipState _ clipState; outP.nVtces _ numPts; }; path: REF Patch; path _ EvalCoords[ context, patch, 0, 1, 2, 3 ]; action[context, path ]; path _ EvalCoords[ context, patch, 4, 5, 6, 7 ]; action[context, path ]; path _ EvalCoords[ context, patch, 8, 9, 10, 11 ]; action[context, path ]; path _ EvalCoords[ context, patch, 12, 13, 14, 15 ]; action[context, path ]; path _ EvalCoords[ context, patch, 0, 4, 8, 12 ]; action[context, path ]; path _ EvalCoords[ context, patch, 3, 7, 11, 15 ]; action[context, path ]; ThreeDSurfaces.ReleasePatch[patch]; -- end of life for patch }; BezierBackFacing: PROC[p: REF Patch] RETURNS [BOOLEAN] ~ { frontFacing: BOOLEAN _ FALSE; FOR i: NAT IN [0..16) DO -- this is not an adequate test!!!! cosAng: REAL _ Vector3d.Dot[ [p[i].coord.ex, p[i].coord.ey, p[i].coord.ez], [p[i].shade.exn, p[i].shade.eyn, p[i].shade.ezn] ]; IF cosAng <= 0.0 THEN frontFacing _ TRUE; ENDLOOP; IF NOT frontFacing 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..7) OF REF VertexInfoSequence; col: ARRAY [0..4) OF REF VertexInfoSequence; FOR i: NAT IN [0..4) DO p0, p1, p2, p3: REF VertexInfo; p0 _ GetVertexInfo[]; p0^ _ p[i]; p1 _ GetVertexInfo[]; p1^ _ p[i+4]; p2 _ GetVertexInfo[]; p2^ _ p[i+8]; p3 _ GetVertexInfo[]; p3^ _ p[i+12]; col[i] _ BezierCurveDivide[ p0, p1, p2, p3, midPt ]; ReleaseVertexInfo[p0]; ReleaseVertexInfo[p1]; ReleaseVertexInfo[p2]; ReleaseVertexInfo[p3]; ENDLOOP; FOR i: NAT IN [0..7) DO row[i] _ BezierCurveDivide[ col[0][i], col[1][i], col[2][i], col[3][i], midPt ]; ENDLOOP; FOR i: NAT IN [0..4) DO rowBase: NAT _ (i MOD 2) * 3; colBase: NAT _ (i / 2) * 3; outPatch[i] _ ThreeDSurfaces.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 _ ThreeDScenes.GetClipCodeForPt[ context, [ex, ey, ez] ]; [ [sx, sy, sz] ] _ ThreeDScenes.XfmPtToDisplay[ context, NIL, [ex, ey, ez] ]; ENDLOOP; ThreeDSurfaces.GetPatchClipState[ outPatch[i] ]; }; ENDLOOP; FOR i: NAT IN [0..7) DO FOR j: NAT IN [0..row[i].length) DO -- return borrowed storage ReleaseVertexInfo[ row[i][j] ]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..4) DO FOR j: NAT IN [0..col[i].length) DO 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] RETURNS[pt: REF VertexInfoSequence] ~ { tempPt: REF VertexInfo; pt _ NEW[ VertexInfoSequence[7] ]; pt[0] _ GetVertexInfo[]; pt[0]^ _ v0^; pt[6] _ GetVertexInfo[]; pt[6]^ _ v3^; pt[1] _ midPt[v0, v1]; pt[5] _ midPt[v2, v3]; tempPt _ midPt[v1, v2]; pt[2] _ midPt[pt[1], tempPt]; pt[4] _ midPt[tempPt, pt[5]]; pt[3] _ midPt[pt[2], pt[4]]; ReleaseVertexInfo[ tempPt ]; }; BezierPatchNormals: PROC[ patch: REF Patch] ~ { d1, d2: Triple; GetXProd: PROC[v1b, v1e, v2b, v2e: NAT] RETURNS[Triple] ~ { d1 _ DiffPosns[ patch[v1e].coord, patch[v1b].coord, $Eye ]; d2 _ DiffPosns[ patch[v2e].coord, patch[v2b].coord, $Eye ]; IF Vector3d.Null[d1] OR Vector3d.Null[d2] THEN SIGNAL StandardPatchesError[$DegeneratePatch]; RETURN [ Vector3d.Normalize[ Vector3d.Cross[ d2, d1 ] ] ]; -- in eye space, left-handed }; FOR i: INTEGER IN [0..4) DO FOR j: INTEGER IN [0..4) DO k: NAT _ j + i*4; v1Beg: NAT _ 4*i + MAX[j-1, 0]; v1End: NAT _ 4*i + MIN[j+1, 3]; v2Beg: NAT _ 4*MAX[i-1, 0] + j; v2End: NAT _ 4*MIN[i+1, 3] + j; { [[ patch[k].shade.exn, patch[k].shade.eyn, patch[k].shade.ezn ]] _ GetXProd[ v1Beg, v1End, v2Beg, v2End ! ANY => GO TO FixUp ]; EXITS FixUp => { -- degenerate edges, use alternate edges IF NOT Vector3d.Null[d1] THEN { l: NAT _ IF j = 3 THEN 2 ELSE j+1; -- use parallel pair of control points v2Beg _ 4*MAX[i-1, 0] + l; v2End _ 4*MIN[i+1, 3] + l; } ELSE IF NOT Vector3d.Null[d2] THEN { l: NAT _ IF i = 3 THEN 2 ELSE i+1; -- use parallel pair of control points v1Beg _ 4*l + MAX[j-1, 0]; v1End _ 4*l + MIN[j+1, 3]; } ELSE SIGNAL StandardPatchesError[$DegeneratePatch]; -- failure [[ patch[k].shade.exn, patch[k].shade.eyn, patch[k].shade.ezn ]] _ GetXProd[ v1Beg, v1End, v2Beg, v2End ]; -- try again with alternate edges }; }; ENDLOOP; ENDLOOP; }; BezierPtrPatchNormals: PROC[ shape: REF ShapeInstance, patch: REF PtrPatch] ~ { GetXProd: PROC[vertex: REF ThreeDBasics.VertexSequence, v1b, v1e, v2b, v2e: NAT] RETURNS[Triple] ~ { RETURN [ Vector3d.Normalize[ Vector3d.Cross[ -- in object space so do right-handed DiffPosns[ vertex[v1e]^, vertex[v1b]^ ], DiffPosns[ vertex[v2e]^, vertex[v2b]^ ] ] ] ]; }; vertex: REF ThreeDBasics.VertexSequence _ shape.vertex; shade: REF ThreeDBasics.ShadingSequence _ shape.shade; FOR i: INTEGER IN [0..4) DO FOR j: INTEGER IN [0..4) DO k: NAT _ patch.vtxPtr[j + i*4]; IF shade[k].xn = 0.0 AND shade[k].yn = 0.0 AND shade[k].zn = 0.0 THEN { -- untouched v1Beg: NAT _ 4*i + MAX[j-1, 0]; v1End: NAT _ 4*i + MIN[j+1, 3]; v2Beg: NAT _ 4*MAX[i-1, 0] + j; v2End: NAT _ 4*MIN[i+1, 3] + j; IF patch.vtxPtr[v1Beg] = patch.vtxPtr[v1End] THEN { -- try to fix coalesced points v1Beg _ 4*MAX[i-1, 0] + MAX[j-1, 0]; v1End _ 4*MIN[i+1, 3] + MIN[j+1, 3]; IF patch.vtxPtr[v1Beg] = patch.vtxPtr[v1End] THEN SIGNAL StandardPatchesError[$DegeneratePatch]; }; IF patch.vtxPtr[v2Beg] = patch.vtxPtr[v2End] THEN { -- try to fix coalesced points v2Beg _ 4*MAX[i-1, 0] + MAX[j-1, 0]; v2End _ 4*MIN[i+1, 3] + MIN[j+1, 3]; IF patch.vtxPtr[v2Beg] = patch.vtxPtr[v2End] THEN SIGNAL StandardPatchesError[$DegeneratePatch]; }; [[ shade[k].xn, shade[k].yn, shade[k].zn ]] _ GetXProd[ vertex, patch.vtxPtr[v1Beg], patch.vtxPtr[v1End], patch.vtxPtr[v2Beg], patch.vtxPtr[v2End] ]; }; ENDLOOP; ENDLOOP; }; BezierParametricExpand: PROC[shape: REF ShapeInstance, patch: REF PtrPatch, nPrSide: NAT] RETURNS [REF VertexInfoSequence, REF PtrPatchSequence] ~ { vertex: REF ThreeDBasics.VertexSequence _ shape.vertex; shade: REF ThreeDBasics.ShadingSequence _ shape.shade; RowEval: PROC[ v0, v1, v2, v3: REF VertexInfo, numPts: NAT ] RETURNS [pt: REF VertexInfoSequence] ~ { pt _ NEW[ VertexInfoSequence[numPts] ]; 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; pt[i] _ ApplyCubicBasis[v0, v1, v2, v3, f0, f1, f2, f3]; ENDLOOP; }; row: ARRAY [0..4) OF REF VertexInfoSequence; colA, colB: REF VertexInfoSequence; vtxCnt, ptchCnt: NAT _ 0; nVtces: NAT _ (nPrSide+1) * (nPrSide+1); outVtx: REF VertexInfoSequence _ NEW[ VertexInfoSequence[nVtces] ]; outPatch: REF PtrPatchSequence _ NEW[ PtrPatchSequence[nPrSide*nPrSide] ]; ctlPtRow: ARRAY [0..4) OF REF VertexInfo; outputType: ATOM _ NARROW[ Atom.GetPropFromList[ patch.props, $InterimPatchType ] ]; IF outputType = NIL THEN outputType _ $ConvexPolygon; BezierPtrPatchNormals[shape, patch]; FOR i: NAT IN [0..4) DO FOR j: NAT IN [0..4) DO IF ctlPtRow[j] = NIL THEN ctlPtRow[j] _ NEW[ VertexInfo ]; ctlPtRow[j].coord _ vertex[patch.vtxPtr[i*4 + j]]^; ctlPtRow[j].shade _ shade[patch.vtxPtr[i*4 + j]]^; ENDLOOP; row[i] _ RowEval[ ctlPtRow[0], ctlPtRow[1], ctlPtRow[2], ctlPtRow[3], nPrSide+1 ]; ENDLOOP; colA _ RowEval[ row[0][0], row[1][0], row[2][0], row[3][0], nPrSide+1 ]; FOR i: NAT IN [0..nPrSide] DO outVtx[i+vtxCnt] _ colA[i]; ENDLOOP; -- store vertices vtxCnt _ vtxCnt + nPrSide+1; FOR i: NAT IN [1..nPrSide] DO colB _ RowEval[ row[0][i], row[1][i], row[2][i], row[3][i], nPrSide+1 ]; FOR j: NAT IN [0..nPrSide] DO outVtx[j+vtxCnt] _ colB[j]; ENDLOOP; -- store vertices vtxCnt _ vtxCnt + nPrSide+1; FOR j: NAT IN [1..nPrSide] DO IF outPatch[ptchCnt] = NIL THEN outPatch[ptchCnt] _ NEW[PtrPatch]; outPatch[ptchCnt].vtxPtr _ NEW[NatSequence[4]]; outPatch[ptchCnt].vtxPtr[0] _ (nPrSide+1) * (i-1) + j-1; outPatch[ptchCnt].vtxPtr[1] _ (nPrSide+1) * (i-1) + j; outPatch[ptchCnt].vtxPtr[2] _ (nPrSide+1) * i + j; outPatch[ptchCnt].vtxPtr[3] _ (nPrSide+1) * i + j-1; outPatch[ptchCnt].type _ outputType; outPatch[ptchCnt].oneSided _ patch.oneSided; outPatch[ptchCnt].nVtces _ 4; outPatch[ptchCnt].clipState _ patch.clipState; outPatch[ptchCnt].props _ patch.props; ptchCnt _ ptchCnt + 1; ENDLOOP; colA _ colB; ENDLOOP; RETURN[ outVtx, outPatch ]; }; BezierSubdivideExpand: PROC [shape: REF ThreeDBasics.ShapeInstance, patch: REF PtrPatch] RETURNS [REF VertexInfoSequence, REF PtrPatchSequence] ~{ vertex: REF ThreeDBasics.VertexSequence _shape.vertex; shade: REF ThreeDBasics.ShadingSequence _ shape.shade; outVtx: REF VertexInfoSequence _ NEW [ VertexInfoSequence [7*7] ]; outPatch: REF PtrPatchSequence _ NEW[PtrPatchSequence [4]]; row: ARRAY [0..4) OF REF VertexInfoSequence; ctlPtRow: ARRAY [0..4) OF REF VertexInfo; FOR i: NAT IN [0..4) DO FOR j: NAT IN [0..4) DO IF ctlPtRow[j] = NIL THEN ctlPtRow[j] _ NEW[ VertexInfo ]; ctlPtRow[j].coord _ vertex[patch.vtxPtr[i*4 + j]]^; ctlPtRow[j].shade _ shade[patch.vtxPtr[i*4 + j]]^; ENDLOOP; row[i] _ BezierCurveDivide[ ctlPtRow[0], ctlPtRow[1], ctlPtRow[2], ctlPtRow[3], VtxShapeMidPt ]; ENDLOOP; FOR i: NAT IN [0..7) DO vtces: REF VertexInfoSequence _ BezierCurveDivide[ row[0][i], row[1][i], row[2][i], row[3][i], VtxShapeMidPt ]; FOR j: NAT IN [0..7) DO outVtx[i*7 + j] _ NEW[ VertexInfo _ vtces[j]^ ]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..4) DO base: NAT _ (i / 2) * 21 + (i MOD 2) * 3; -- {0, 3, 21, 24}, least corners of 7x7 array outPatch[i].vtxPtr _ NEW [NatSequence[16]]; outPatch[i].type _ patch.type; outPatch[i].oneSided _ patch.oneSided; outPatch[i].nVtces _ 16; outPatch[i].clipState _ patch.clipState; outPatch[i].props _ patch.props; FOR j: NAT IN [0..4) DO FOR k: NAT IN [0..4) DO outPatch[i].vtxPtr[(3-j)*4+k] _ base + 7*j + k; -- count along rows, jump by 7 to next row ENDLOOP; ENDLOOP; ENDLOOP; RETURN[outVtx, outPatch]; }; BSplineExpand: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { RETURN[ NIL, NIL ]; }; BSplineSubdivide: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { RETURN[ NIL, NIL ]; }; BSplineDisplay: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { }; BSplineDisplayLines: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { }; PolygonExpand: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { RETURN[ NIL, NIL ]; }; PolygonSubdivide: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { RETURN[ NIL, NIL ]; }; PolygonDisplay: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { }; PolygonDisplayLines: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { }; NonConvexDisplay: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { }; END. „StandardPatchesImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last Edited by: Crow, October 10, 1986 5:19:57 pm PDT Internal Declarations Utility Procedures Interpolate to point defined by projection of vMid on v1 - v0 Projection given by dot product normalized by magnitude of v1 - v0 Straight! Force inner control points to linear Get average of depths at corners Procedures for expansion of Bezier patches to convex polygons PROC[ shape: REF ShapeInstance, patch: REF PtrPatch, limitType: ATOM, limit: REAL] RETURNS [v: REF VertexInfoSequence, p: REF PtrPatchSequence] PROC[ shape: REF ShapeInstance, patch: REF PtrPatch, limitType: ATOM, limit: REAL] RETURNS [v: REF VertexInfoSequence, p: REF PtrPatchSequence] PROC[ context: REF Context, patch: REF Patch, limitType: ATOM, limit: REAL action: PROC[context: REF Context, patch: REF Patch] ];] Quit if out of field of view or all normals point away from observer Straightness test, if straight enough make perfectly straight Get special output type for weird things like displacement mapping Subdivides a REF Patch for display purposes Expand patch by evaluating control point rows, to get 49 vertices Get eyespace normals at control points, given by cross product of vectors formed from adjacent control points (limited to patch bounds, of course) Get normals at control points, given by cross product of vectors formed from adjacent control points (limited to patch bounds, of course) Get special output type for weird things like displacement mapping Get normals at control points Expand patch by evaluating control point rows, then spitting out polygons columnwise Expand patch by evaluating control point rows, then spitting out polygons columnwise Procedures for expansion of B-Spline patches to convex polygons Procedures for expansion of convex polygons Procedure for expansion of Non-convex to semi-convex polygons ΚN˜Iheadšœ™šœ Οmœ1™Jšœ žœžœ˜9Mšœžœžœžœžœžœžœžœžœžœ ˜PMšœžœ˜!Mš œžœžœžœžœžœ ˜@Mšœžœ˜!Mš œžœžœžœžœžœ ˜@Mšœžœ˜!Mš œžœžœžœžœžœ ˜@Mšœžœ˜!M˜—Mšžœ˜—M˜—Jšœ˜—šΠbn œžœžœ˜BJšœ=™=Jšœ žœ˜šœ˜J™C—JšœžœS˜\Jšžœ"žœžœ‘˜UJšœG‘œN˜ͺJšœ˜Jšœ.˜.Nšœ.˜.Nšœ/˜/Jšœ1˜1Nšœ1˜1Nšœ1˜1Jšœ0˜0Nšœ0˜0Nšœ0˜0Nšœ3˜3Nšœ3˜3Nšœ3˜3Nšœ.˜.Nšœ.˜.Nšœ.˜.Nšœ.˜.Nšœ1˜1Nšœ1˜1Nšœ1˜1Nšœ1˜1š žœ žœžœ žœžœ‘(˜RJšœžœžœ-˜9šžœžœ˜ šžœ ‘˜,Jšœ žœžœ˜9Mšœžœžœžœžœžœžœžœžœžœ˜MMšœžœžœ˜0M˜—Mšžœ˜—M˜—Jšžœ˜ J˜—š œžœžœžœžœžœžœ˜XJšœžœ˜šžœ!žœ‘ ˜LJšœžœ7˜=J˜—Jšœ%˜%Jšœ%˜%Jšœ˜šžœžœ˜J˜JšœX˜XJš žœžœ žœžœžœ˜&JšœX˜XJš žœžœ žœžœžœ˜&J˜J™0—Jšœ)˜)Jšœ)˜)Jšžœžœ˜ Jšœ˜—š  œžœ žœžœžœžœžœžœžœ ˜€Nšœžœžœžœ˜šœžœžœžœ˜ J™ —Jšœ_˜_Jšœ_˜_Jšœ_˜_Jšœ_˜_šžœžœžœžœ˜š žœžœž œžœ žœ‘!˜Išžœžœ˜Jšœžœžœ˜/Jšœ%˜%Jšœ˜J˜—Jšžœ˜—Jšžœ˜—šžœ˜Jšžœžœ&˜1Jšžœžœ'˜2—Jšœ˜—J˜—™=M˜šŸ œžœ#˜7Mšžœ žœžœžœ žœžœžœžœ™“Mšœ žœ˜!Mšžœ žœžœžœ&˜DMšœ8˜8Mšžœ ˜Mšœ˜—šŸœžœ#˜:Mšžœ žœžœžœ žœžœžœžœ™“Mšžœ žœžœžœ&˜DMšœ-˜-Mšžœ ˜M˜Mšœ žœ‘˜;Mšœžœ ‘˜-—šŸ œžœ$˜9Mšžœ žœžœžœ žœ žœ žœžœ ™‡š œžœžœžœ˜,šœ žœžœ˜Nš‘D™D—šžœžœžœžœ˜š  œžœ žœžœžœžœ ž œžœ ˜ŽNšœ#˜#šžœ‘˜;Nšžœ˜šžœ˜šœ žœ˜*šœžœ˜Nšœžœžœžœ˜ONšœžœžœžœ˜N—N˜Jšœ˜—šžœ ˜"Jšžœ˜šžœ˜šœ žœ˜šœžœ˜šœžœ˜Nšžœžœ˜A—šœžœ˜Nšžœžœ˜@—N˜—Jšœ˜—šžœ ˜"Jšžœ˜Jšžœ˜—J˜——Jšœ˜——Nšœ*‘"˜Lšžœžœžœ ž˜Nšœžœ&žœžœ ˜PNšœžœ ˜(Nšœžœ˜$Nšœžœ˜Nšœžœ ˜Nšžœžœ˜(šžœ˜Nšœv˜vNšœv˜vNšœv˜všžœžœ4˜JNšœ ˜ Nšœ7˜7Nšœ˜—šžœžœ‘˜>Nšœv˜vNšœu˜uNšœt˜tJ˜—N˜—šžœ˜Nšœw˜wNšœw˜wNšœw˜wNšœ˜—Jšžœ˜—Nšœ˜Nšœ˜Nšœ˜Mšœ˜—Nšœžœ˜NšœN˜NNšœQ˜QNšœN˜NNšœO˜OJšœ+‘˜CM˜—M˜šŸœžœ$˜@š  œžœ žœžœžœžœ ž œžœ ˜ŽNšœ#˜#šžœ‘˜;Nšžœ˜šžœ˜šœ žœ˜*šœžœ˜Nšœžœžœžœ˜ONšœžœžœžœ˜N—N˜Jšœ˜—šžœ ˜"Jšžœ˜šžœ˜šœ žœ˜šœžœ˜šœžœ˜Nšžœžœ˜A—šœžœ˜Nšžœžœ˜@—N˜—Jšœ˜—šžœ ˜"Jšžœ˜Jšžœ˜—J˜——Jšœ˜——Nšœ%‘"˜GNšœ˜Nšœ˜Nšœ˜Nšœ˜Nšœ˜Nšœ˜Nšœ˜Mšœ˜—Nšœžœ˜NšœN˜NNšœN˜NNšœO˜ONšœQ˜QNšœN˜NNšœO˜OJšœ+‘˜CM˜—š  œžœžœ žœžœ˜;Jšœ žœžœ˜š žœžœžœ žœ‘#˜?šœžœ˜Jšœ2˜2Jšœ0˜0J˜—Jšžœžœžœ˜)Jšžœ˜—Jšžœžœ žœžœžœžœžœžœ˜=Jšœ˜—š œžœ žœ žœ žœžœžœžœžœ ˜‹J™+Nšœ žœžœžœ˜$Nšœžœžœžœ˜,šœžœžœžœ˜,N™A—šžœžœžœž˜Nšœžœ ˜NšœL˜LNšœK˜KNšœ4˜4Nšœ]˜]Nšžœ˜—šžœžœžœž˜JšœP˜PJšžœ˜—šžœžœžœž˜Jšœ žœžœ˜Jšœ žœžœ˜Jšœ+‘-˜XNšœ˜Jšœ"˜"Jšœ˜Jšœ$˜$Jšœ˜Jšœ˜šžœžœžœž˜šžœžœžœž˜Jšœ2‘˜HJšžœ˜—Jšžœ˜—šžœžœ˜$šžœžœžœ ž˜Jšžœ˜Jšœ>˜>Jšœ9žœ˜MJšžœ˜—Jšœ0˜0Jšœ˜—Jšžœ˜—šžœžœžœžœžœžœžœžœ‘˜XNšœ ˜ Nšžœžœ˜—šžœžœžœžœžœžœžœž˜;Nšœ˜Nšžœžœ˜—Jšžœ8‘˜XJšœŸ˜—J˜š  œžœžœž œžœ˜wJšœžœ ˜Jšœžœ˜"JšœR˜RJšœ0˜0Jšœ˜Jšœ>˜>Jšœ˜Jšœ˜JšœŸ˜—š œžœ žœ ˜/N™’Nšœ˜š œžœžœžœ ˜?Jšœ;˜;Jšœ;˜;šžœžœ˜*Jšžœžœ(˜3—Jšžœ4‘˜WN˜—šžœžœžœž˜šžœžœžœž˜Nšœžœ ˜Nš œžœ žœžœ žœ ˜BNš œžœžœžœžœ ˜Bšœ˜šœM˜MNšœ˜Nšœžœžœžœ˜Nšœ˜—šžœ‘(˜<šžœžœžœ˜Nš œžœžœžœžœ‘&˜INšœ žœžœ ˜8N˜—šžœžœžœžœ˜$Nš œžœžœžœžœ‘&˜INšœžœžœ ˜8N˜—Nšžœžœ)‘ ˜>šœM˜MNšœ˜Nšœ‘!˜/—N˜—N˜—Nšžœ˜—Nšžœ˜—M˜—š œžœ žœžœ˜ON™‰š  œžœ žœ2žœžœ ˜hšžœ'‘%˜RJšœ(˜(Jšœ(˜(J˜—N˜—Nšœžœ,˜7Nšœžœ,˜6N˜šžœžœžœž˜šžœžœžœž˜Nšœžœ˜š žœžœžœžœ‘ ˜TNš œžœ žœžœ žœ ˜BNš œžœžœžœžœ ˜Bšžœ+žœ‘˜RNšœ žœ žœ ˜(Nšœ žœ žœ ˜$šžœ+˜-Nšžœžœ(˜3—N˜—šžœ+žœ‘˜SNšœ žœ žœ ˜(Nšœ žœ žœ ˜$šžœ+˜-Nšžœžœ(˜3—N˜—šœ@˜@NšœR˜RNšœ˜—N˜—Nšžœ˜—Nšžœ˜—M˜—š œžœžœžœžœ žœžœžœ˜Nšœžœ,˜7Nšœžœ,˜6š  œžœžœžœžœžœ˜jNšœžœ˜'šžœžœžœ ž˜Nšœžœ&žœžœ ˜PNšœžœ ˜(Nšœžœ˜$Nšœžœ˜Nšœžœ ˜Nšœ8˜8Mšžœ˜—M˜—Nšœžœžœžœ˜,Nšœ žœ˜#Mšœžœ˜Mšœžœ˜(Mšœžœžœ˜CMšœ žœžœ&˜Jšœ žœžœžœ ˜)MšœB™B—Mšœ žœžœ;˜Tšžœžœžœ˜6M™—šœ$˜$N™T—šžœžœžœž˜šžœžœžœžœ˜Nšžœžœžœžœ˜:Nšœ3˜3Nšœ2˜2Nšžœ˜—NšœV˜VNšžœ˜—NšœH˜HNš žœžœžœžœžœ‘˜VNšœ˜šžœžœžœž˜NšœH˜HNš žœžœžœžœž œ‘˜VNšœ˜šžœžœžœž˜Jšžœžœžœžœ ˜BNšœžœ˜/Nšœ8˜8Nšœ6˜6Nšœ2˜2Nšœ4˜4Nšœ$˜$Nšœ,˜,Nšœ˜Nšœ.˜.Nšœ&˜&Nšœ˜Nšžœ˜—Nšœ ˜ Nšžœ˜ —Mšžœ˜M˜—šŸœžœ žœ$žœžœžœžœ˜˜Jšœžœ+˜6Jšœžœ,˜6Jšœžœžœ˜BJšœ žœžœ˜;Nšœžœžœžœ˜,šœ žœžœžœ ˜)J˜N™T—šžœžœžœž˜šžœžœžœžœ˜Nšžœžœžœžœ˜:Nšœ3˜3Nšœ2˜2Nšžœ˜—šœ˜Nšœ3˜3Nšœ ˜ Nšœ˜—Nšžœ˜—šžœžœžœž˜šœžœ(˜2Jšœ+˜+Nšœ ˜ Jšœ˜—šžœžœžœžœ˜Nšœžœ˜2Nšžœ˜—Jšžœ˜—šžœžœžœž˜Jšœžœžœ ‘.˜XJšœžœ˜+Nšœ˜Jšœ&˜&Jšœ˜Jšœ(˜(Jšœ ˜ šžœžœžœž˜šžœžœžœž˜Jšœ/‘+˜ZJšžœ˜—Jšžœ˜—Jšžœ˜—Jšžœ˜J˜——™?šŸ œžœ#˜8Mšžœžœžœ˜M˜—šŸœžœ#˜;Mšžœžœžœ˜M˜—šŸœžœ$˜:M˜—šŸœžœ$˜?M˜——™+šŸ œžœ#˜8Mšžœžœžœ˜M˜—šŸœžœ#˜;Mšžœžœžœ˜M˜—šŸœžœ$˜:M˜—šŸœžœ$˜?M˜——™=šŸœžœ$˜