DIRECTORY Atom, Basics, G3dBasic, G3dMatrix, G3dRender, G3dScanConvert, G3dShadeClipXfm, G3dSortandDisplay, G3dVector, Real, RealFns; StandardPatchProcs: CEDAR MONITOR IMPORTS Atom, Basics, G3dMatrix, G3dRender, G3dShadeClipXfm, G3dSortandDisplay, G3dVector, Real, RealFns = BEGIN Ray: TYPE ~ G3dBasic.Ray; RGB: TYPE ~ G3dRender.RGB; RealSequence: TYPE ~ G3dRender.RealSequence; Context: TYPE ~ G3dRender.Context; SixSides: TYPE ~ G3dRender.SixSides; Pair: TYPE ~ G3dRender.Pair; Triple: TYPE ~ G3dRender.Triple; Matrix: TYPE ~ G3dRender.Matrix; Shape: TYPE ~ G3dRender.Shape; ShapeClass: TYPE ~ G3dRender.ShapeClass; RenderStyle: TYPE ~ G3dRender.RenderStyle; ShapeProc: TYPE ~ G3dRender.ShapeProc; RenderData: TYPE ~ G3dRender.RenderData; Patch: TYPE ~ G3dRender.Patch; PatchSequence: TYPE ~ G3dRender.PatchSequence; PatchSequenceRep: TYPE ~ G3dRender.PatchSequenceRep; PatchProc: TYPE ~ G3dRender.PatchProc; CtlPoint: TYPE ~ G3dRender.CtlPoint; CtlPtInfo: TYPE ~ G3dRender.CtlPtInfo; CtlPtInfoSequence: TYPE ~ G3dRender.CtlPtInfoSequence; CtlPtInfoSequenceRep: TYPE ~ G3dRender.CtlPtInfoSequenceRep; CtlPtInfoProc: TYPE ~ G3dRender.CtlPtInfoProc; ClipState: TYPE ~ G3dRender.ClipState; OutCode: TYPE ~ G3dRender.OutCode; AllOut: OutCode ~ G3dRender.AllOut; NoneOut: OutCode ~ G3dRender.NoneOut; FacingDir: TYPE ~ G3dRender.FacingDir; FacingDirArray: TYPE ~ ARRAY [0..9) OF FacingDir; MidPtProc: TYPE ~ PROC[v0, v1: REF CtlPtInfo, texture: BOOLEAN _ FALSE] RETURNS[REF CtlPtInfo]; 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]; GetProp: PROC [propList: Atom.PropList, prop: REF ANY] RETURNS [REF ANY] ~ Atom.GetPropFromList; 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: CtlPtInfo] RETURNS[vtxOut: REF CtlPtInfo] ~ { vtxOut _ G3dShadeClipXfm.GetCtlPtInfo[]; vtxOut^ _ vtx; }; Sub: PROC[f, g: REAL] RETURNS[REAL] ~ { IF RealFns.AlmostEqual[f, g, -20] THEN RETURN [0.0] ELSE RETURN[f-g]; }; Sub1: PROC[f, g: REAL] RETURNS[REAL] ~ { result: REAL _ f-g; IF ABS[result] < G3dScanConvert.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: CtlPoint, 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 ~ { v: REF CtlPtInfo _ G3dShadeClipXfm.GetCtlPtInfo[]; 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.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 texture THEN { v.shade.txtrX _ (v0.shade.txtrX + v1.shade.txtrX) / 2; v.shade.txtrY _ (v0.shade.txtrY + v1.shade.txtrY) / 2; v.coord.x _ (v0.coord.x + v1.coord.x) / 2; -- original coords (for solid texture) v.coord.y _ (v0.coord.y + v1.coord.y) / 2; v.coord.z _ (v0.coord.z + v1.coord.z) / 2; }; RETURN[v]; }; TooClose: PROC[ context: Context, v: CtlPoint, 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]; }; MakeStraight: PROC[ v0, v1, vMid: CtlPtInfo, texture: BOOLEAN _ FALSE ] RETURNS[CtlPtInfo] ~ { a, b, alpha: REAL; mag: REAL _ RealFns.SqRt[ Sqr[v1.coord.sx - v0.coord.sx] + Sqr[v1.coord.sy - v0.coord.sy] ]; IF mag < G3dScanConvert.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 texture THEN { vMid.shade.txtrX _ a * v0.shade.txtrX + b * v1.shade.txtrX; vMid.shade.txtrY _ a * v0.shade.txtrY + b * v1.shade.txtrY; vMid.coord.x _ a * v0.coord.x + b * v1.coord.x ; -- original coords (for solid texture) vMid.coord.y _ a * v0.coord.y + b * v1.coord.y ; vMid.coord.z _ a * v0.coord.z + b * v1.coord.z ; }; 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].data _ $Straight; }; StraightWithin: PROC[ context: Context, p: REF Patch, v0, v1, v2, v3: NAT, lilTol, bigTol: REAL] RETURNS[BOOLEAN] ~ { StraightenUp: PROC[] ~ { p[v0].data _ $Straight; }; 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 = unknown THEN RETURN[FALSE]; RETURN[TRUE]; }; DistOffEdge: PROC[v: CtlPoint, line: Ray] RETURNS[REAL] ~ { ptOnEdge: Triple _ G3dShadeClipXfm.XfmPtToDisplay[ context, G3dVector.NearestToLine[line, [v.ex, v.ey, v.ez]] ]; vs: Triple _ G3dShadeClipXfm.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; IF p[v0].data = $Straight 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 > G3dScanConvert.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]; IF p.renderData.shadingClass.texture # NIL THEN { 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 # unknown => { StraightenUp[]; RETURN[ TRUE ]; }; -- interior patch InteriorEdge[] => { StraightenUp[]; RETURN[ TRUE ]; }; -- interior edge ENDCASE => 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 ] ]; }; PatchSort: PROC[ context: 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 ~ { -- render: REF RenderData _ G3dRender.RenderDataFrom[shape]; renderStyle: RenderStyle; xfm: Matrix _ G3dMatrix.Mul[shape.matrix, context.eyeSpaceXfm]; allPolysIn, allPolysOut: BOOLEAN _ TRUE; WITH render.shadingClass.renderMethod SELECT FROM style: REF RenderStyle => renderStyle _ style^; ENDCASE => renderStyle _ smooth; -- default to smooth IF GetProp[render.props, $Hidden] # NIL THEN RETURN[shape]; -- don't bother, not visible IF NOT render.patchesValid OR context.changed OR render.patch = NIL THEN { IF render.patch = NIL THEN { render.patch _ NEW[PatchSequenceRep[shape.surfaces.length]]; render.patch.length _ shape.surfaces.length; FOR i: NAT IN [0..render.patch.length) DO render.patch[i] _ NEW[Patch[shape.surfaces[i].length]]; FOR j: NAT IN [0..shape.surfaces[i].length) DO vtx: NAT _ shape.surfaces[i][j]; render.patch[i][j].coord.x _ shape.vertices[vtx].point.x; render.patch[i][j].coord.y _ shape.vertices[vtx].point.y; render.patch[i][j].coord.z _ shape.vertices[vtx].point.z; render.patch[i][j].shade.xn _ shape.vertices[vtx].normal.x; render.patch[i][j].shade.yn _ shape.vertices[vtx].normal.y; render.patch[i][j].shade.zn _ shape.vertices[vtx].normal.z; render.patch[i][j].shade.txtrX _ shape.vertices[vtx].texture.x; render.patch[i][j].shade.txtrY _ shape.vertices[vtx].texture.y; render.patch[i][j].vtxPtr _ vtx; ENDLOOP; render.patch[i].nVtces _ shape.surfaces[i].length; render.patch[i].type _ shape.type; render.patch[i].oneSided _ NOT shape.showBackfaces; render.patch[i].renderData _ render; -- store REF to shading information ENDLOOP; }; shape.screenExtent _ [[32768, 32768], [0, 0]]; -- initialize for computing screenExtent FOR i: NAT IN [0..render.patch.length) DO andOfCodes: OutCode _ AllOut; -- test for trivially out orOfCodes: OutCode _ NoneOut; -- test for trivially in FOR j: NAT IN [0..render.patch[i].nVtces) DO OPEN render.patch[i][j].coord; -- transform control points to eyespace [ [ex, ey, ez] ] _ G3dMatrix.Transform[ [x, y, z] , xfm]; IF shape.clipState = clipped THEN { clip _ G3dShadeClipXfm.GetClipCodeForPt[ context, [ex, ey, ez] ]; orOfCodes _ LOOPHOLE[ Basics.BITOR[LOOPHOLE[orOfCodes], LOOPHOLE[ clip] ], OutCode]; andOfCodes _ LOOPHOLE[ Basics.BITAND[ LOOPHOLE[andOfCodes], LOOPHOLE[ clip] ], OutCode]; } ELSE clip _ NoneOut; IF renderStyle = lines THEN { OPEN render.patch[i][j].shade; [er, eg, eb] _ render.shadingClass.color; } ELSE { OPEN render.patch[i][j].shade; vtx: NAT _ render.patch[i][j].vtxPtr; [r, g, b] _ shape.vertices[vtx].color; t _ shape.vertices[vtx].transmittance; IF shape.faces # NIL THEN IF shape.faces.valid.color THEN { -- scale w/ face clr clr: Triple _ shape.faces[i].color; r _ clr.x * r; g _ clr.y * g; b _ clr.z * b; }; }; ENDLOOP; IF orOfCodes = NoneOut THEN render.patch[i].clipState _ in -- default case ELSE IF andOfCodes # NoneOut THEN render.patch[i].clipState _ out ELSE render.patch[i].clipState _ clipped; IF shape.clipState = clipped THEN { allPolysIn _ allPolysIn AND (render.patch[i].clipState = in); allPolysOut _ allPolysOut AND (render.patch[i].clipState = out); }; IF render.patch[i].clipState # out THEN FOR j: NAT IN [0..render.patch[i].nVtces) DO OPEN render.patch[i][j].coord; -- xfm to display, update shape screen extent [ [sx, sy, sz] ] _ G3dShadeClipXfm.XfmPtToDisplay[ context, [ex, ey, ez], shape ]; ENDLOOP; ENDLOOP; render.patchesValid _ TRUE; }; RETURN[shape]; }; InitClasses: PROC[] ~ { -- register procedures for basic surface types standardClass: ShapeClass _ G3dRender.GetShapeClass[$ConvexPolygon]; standardClass.type _ $Bezier; standardClass.validate _ ValidatePatchShape; standardClass.displayPatch _ BezierDisplay; G3dRender.RegisterShapeClass[standardClass, $Bezier]; standardClass.type _ $BSpline; standardClass.validate _ ValidatePatchShape; standardClass.displayPatch _ BSplineDisplay; G3dRender.RegisterShapeClass[standardClass, $BSpline]; standardClass.type _ $NonConvexPolygon; standardClass.validate _ G3dSortandDisplay.ValidatePolyhedron; standardClass.displayPatch _ NonConvexDisplay; G3dRender.RegisterShapeClass[standardClass, $NonConvexPolygon]; }; BezierDisplay: PatchProc ~ { renderStyle: RenderStyle; WITH patch.renderData.shadingClass.renderMethod SELECT FROM style: REF RenderStyle => { renderStyle _ style^; SELECT renderStyle FROM lines => { [] _ BezierDisplayLines[context, patch]; RETURN[NIL]; }; controlPoints => { [] _ BezierDisplayCtrlPts[context, patch]; RETURN[NIL]; }; ENDCASE; }; 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[patch]; -- patch should be unmodified }; BezierPatchDisplay: PROC[ context: 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 = unknown 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 G3dShadeClipXfm.ShadePoly[context, p]; IF showLines THEN p.type _ $PolyLine; p _ G3dSortandDisplay.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 level > 0 THEN G3dShadeClipXfm.ReleasePatch[p]; -- end of life for sub-patch }; BezierTriangleDisplay: PROC[ context: Context, p: REF Patch, divisions: NAT] ~ { MdPt: PROC[v0, v1, v2: CtlPtInfo, r, s, t: REAL] RETURNS[CtlPtInfo] ~ { v: CtlPtInfo; 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 p.renderData.shadingClass.texture # NIL THEN { v.shade.txtrX _ r * v0.shade.txtrX + s * v1.shade.txtrX + t * v2.shade.txtrX; v.shade.txtrY _ r * v0.shade.txtrY + s * v1.shade.txtrY + t * v2.shade.txtrY; 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; }; RETURN[v]; }; EvalTriangle: PROC[p: REF Patch, r, s, t: REAL] RETURNS[CtlPtInfo] ~ { p1: ARRAY[0..6) OF CtlPtInfo; p2: ARRAY[0..3) OF CtlPtInfo; p3: CtlPtInfo; 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: CtlPtInfo; 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 _ G3dShadeClipXfm.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]; G3dShadeClipXfm.ShadePoly[context, tp]; IF showLines THEN tp.type _ $PolyLine; tp _ G3dSortandDisplay.OutputPolygon[context, tp]; -- display IF j # i THEN { ttp: REF Patch _ G3dShadeClipXfm.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]; G3dShadeClipXfm.ShadePoly[context, ttp]; IF showLines THEN ttp.type _ $PolyLine; ttp _ G3dSortandDisplay.OutputPolygon[context, ttp]; -- display }; ENDLOOP; ENDLOOP; }; BezierDisplayLines: PatchProc ~ { EvalCoords: PROC[ v0, v1, v2, v3: NAT ] RETURNS[ 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; }; }; FOR i: NAT IN [0..8) DO t: REAL _ Real.Float[i] / (8-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 { path[i].coord.ex _ f0*patch[v0].coord.ex + f1*patch[v1].coord.ex + f2*patch[v2].coord.ex + f3*patch[v3].coord.ex; path[i].coord.ey _ f0*patch[v0].coord.ey + f1*patch[v1].coord.ey + f2*patch[v2].coord.ey + f3*patch[v3].coord.ey; path[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 path[i].coord.clip _ G3dShadeClipXfm.GetClipCodeForPt[ context, [path[i].coord.ex, path[i].coord.ey, path[i].coord.ez] ]; IF context.antiAliasing THEN { path[i].shade.r _ f0*patch[v0].shade.r + f1*patch[v1].shade.r + f2*patch[v2].shade.r + f3*patch[v3].shade.r; path[i].shade.g _ f0*patch[v0].shade.g + f1*patch[v1].shade.g + f2*patch[v2].shade.g + f3*patch[v3].shade.g; path[i].shade.b _ f0*patch[v0].shade.b + f1*patch[v1].shade.b + f2*patch[v2].shade.b + f3*patch[v3].shade.b; }; } ELSE { path[i].coord.sx _ f0*patch[v0].coord.sx + f1*patch[v1].coord.sx + f2*patch[v2].coord.sx + f3*patch[v3].coord.sx; path[i].coord.sy _ f0*patch[v0].coord.sy + f1*patch[v1].coord.sy + f2*patch[v2].coord.sy + f3*patch[v3].coord.sy; path[i].coord.sz _ f0*patch[v0].coord.sz + f1*patch[v1].coord.sz + f2*patch[v2].coord.sz + f3*patch[v3].coord.sz; }; path[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; path[i].coord.y _ f0*patch[v0].coord.y + f1*patch[v1].coord.y + f2*patch[v2].coord.y + f3*patch[v3].coord.y; path[i].coord.z _ f0*patch[v0].coord.z + f1*patch[v1].coord.z + f2*patch[v2].coord.z + f3*patch[v3].coord.z; path[i].shade.er _ f0*patch[v0].shade.er + f1*patch[v1].shade.er + f2*patch[v2].shade.er + f3*patch[v3].shade.er; path[i].shade.eg _ f0*patch[v0].shade.eg + f1*patch[v1].shade.eg + f2*patch[v2].shade.eg + f3*patch[v3].shade.eg; path[i].shade.eb _ f0*patch[v0].shade.eb + f1*patch[v1].shade.eb + f2*patch[v2].shade.eb + f3*patch[v3].shade.eb; ENDLOOP; path.type _ $PolyLine; path.nVtces _ 8; path.clipState _ clipState; path.renderData _ patch.renderData; path.props _ patch.props; RETURN[path]; }; path: REF Patch _ G3dShadeClipXfm.GetPatch[8]; IF patch.nVtces = 16 THEN { path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 1, 2, 3 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[12,13,14,15 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 4, 8,12 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 3, 7,11,15 ]]; } ELSE IF patch.nVtces = 10 THEN { -- triangular patch path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 1, 2, 3 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 3, 4, 5, 6 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 6, 7, 8, 0 ]]; }; G3dShadeClipXfm.ReleasePatch[path]; RETURN[patch]; -- patch should be unmodified }; BezierDisplayCtrlPts: PatchProc ~ { EvalCoords: PROC[ v0, v1, v2, v3: NAT ] RETURNS [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; }; }; path[0] _ patch[v0]; path[1] _ patch[v1]; path[2] _ patch[v2]; path[3] _ patch[v3]; path.type _ $PolyLine; path.nVtces _ 4; path.clipState _ clipState; path.renderData _ patch.renderData; path.props _ patch.props; RETURN[path]; }; path: REF Patch _ G3dShadeClipXfm.GetPatch[4]; IF patch.nVtces = 16 THEN { path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 1, 2, 3 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 4, 5, 6, 7 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 8, 9,10,11 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ ,13,14,15 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 4, 8,12 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 1, 5, 9,13 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 2, 6,10,14 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 3, 7,11,15 ]]; } ELSE IF patch.nVtces = 10 THEN { -- triangular Bezier patch path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 1, 2, 3 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 3, 4, 5, 6 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 6, 7, 8, 0 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 9, 8, 1, 9 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 9, 7, 4, 9 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 9, 5, 7, 9 ]]; }; G3dShadeClipXfm.ReleasePatch[path]; RETURN[patch]; -- patch should be unmodified }; 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[CtlPtInfo] ~ { 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 { 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 G3dRender.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: Context, p: REF Patch] RETURNS[BOOLEAN] ~ { facing: FacingDir; back, front, unknown: 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 _ G3dShadeClipXfm.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 _ G3dShadeClipXfm.BackFacing[ context, patch, TRUE ! G3dRender.Error => IF code = $Condition THEN { facing _ unknown; CONTINUE; } ]; IF facing = back THEN back _ TRUE ELSE IF facing = front THEN front _ TRUE ELSE IF facing = unknown THEN unknown _ TRUE; G3dShadeClipXfm.ReleasePatch[patch]; subdir[i*3 + j] _ facing; ENDLOOP; ENDLOOP; IF (back AND front) OR unknown THEN p.dir _ unknown ELSE IF back THEN p.dir _ back ELSE IF front THEN p.dir _ front ELSE p.dir _ unknown; IF p.dir = unknown THEN p.props _ PutPropSafely[p.props, $FacingDirArray, NEW[FacingDirArray _ subdir] ]; IF p.dir = back THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; BezierPatchDivide: PROC[context: 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 CtlPtInfoSequence; col: ARRAY [0..4) OF CtlPtInfoSequence; IF p[ 0].data = $Straight THEN SetStraight[ p, 0, 4, 8,12 ]; IF p[12].data = $Straight THEN SetStraight[ p,12,13,14,15 ]; IF p[15].data = $Straight THEN SetStraight[ p,15,11, 7, 3 ]; IF p[ 3].data = $Straight THEN SetStraight[ p, 3, 2, 1, 0 ]; FOR i: NAT IN [0..4) DO p0, p1, p2, p3: REF CtlPtInfo; p0 _ CopyVtx[ p[i] ]; p1 _ CopyVtx[ p[i+4] ]; p2 _ CopyVtx[ p[i+8] ]; p3 _ CopyVtx[ p[i+12] ]; 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, unknown ]; G3dShadeClipXfm.ReleaseCtlPtInfo[p0]; G3dShadeClipXfm.ReleaseCtlPtInfo[p1]; G3dShadeClipXfm.ReleaseCtlPtInfo[p2]; G3dShadeClipXfm.ReleaseCtlPtInfo[p3]; ENDLOOP; FOR i: NAT IN [0..8) DO 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, unknown ]; ENDLOOP; FOR i: NAT IN [0..4) DO rowBase: NAT _ (i MOD 2) * 4; colBase: NAT _ (i / 2) * 4; outPatch[i] _ G3dShadeClipXfm.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].renderData _ p.renderData; 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 _ G3dShadeClipXfm.GetClipCodeForPt[ context, [ex, ey, ez] ]; IF clip = NoneOut THEN [ [sx, sy, sz] ] _ G3dShadeClipXfm.XfmPtToDisplay[ context, [ex, ey, ez] ]; ENDLOOP; outPatch[i] _ BezierClipState[context, outPatch[i] ]; }; IF outPatch[i].dir = unknown 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 G3dShadeClipXfm.ReleaseCtlPtInfo[ row[i][j] ]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..4) DO FOR j: NAT IN [0..col[i].length) DO G3dShadeClipXfm.ReleaseCtlPtInfo[ 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 CtlPtInfo, midPt: MidPtProc, dir: FacingDir] RETURNS[pt: CtlPtInfoSequence] ~ { tempPt: REF CtlPtInfo; pt _ NEW[ CtlPtInfoSequenceRep[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 straightness data if on directed patch boundary THEN pt[4].data _ pt[0].data ELSE IF dir = back THEN pt[3].data _ pt[7].data; G3dShadeClipXfm.ReleaseCtlPtInfo[ tempPt ]; }; BezierClipState: PatchProc ~ { G3dShadeClipXfm.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 _ unknown -- 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. &StandardPatchProcs.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last Edited by: Crow, May 3, 1989 3:46:05 pm PDT Internal Declarations Renamed Procedures Utility Procedures Difference f - g, returns zero if less than 6 decimal places Screen space f - g, returns zero if less than noticeable PROC[v0, v1: REF CtlPtInfo, texture: BOOLEAN _ FALSE] RETURNS[REF CtlPtInfo] [[v.coord.sx, v.coord.sy, v.coord.sz]] _ G3dShadeClipXfm.XfmPtToDisplay[ context, [v.coord.ex, v.coord.ey, v.coord.ez] ]; Find max distance from screen edge on inside Interpolate displayed values to point defined by projection of vMid on v1 - v0 Projection given by dot product normalized by magnitude of v1 - v0 Don't straighten eyespace coords, will cause shading discontinuities Straight! Force inner display values to lie on line between patch corners Length of control poly within tolerated deviation from linear All control point polygons touched by this edge face the same way Watch for perspective depth distortion here Get screen distance of inner knots from edge formed by outer knots Put property on new property list, to avoid clobbering other lists inherited from same place Get average of depths at corners PROC[context: Context, shape: Shape, data: REF] RETURNS[Shape]; Update shading and transform matrices) Build patch sequence for shape Transform and shade vertices, get clip state for each polygon Plain lines take object color Else take vertex and face colors Collect clipping data for vertices to tag patches, patches to tag shapes Get screen coordinates for on-screen patches Initialization of Classes Procedures for expansion of Bezier patches to convex polygons PROC[ context: Context, patch: REF Patch, data: REF ANY _ NIL ] RETURNS[REF Patch] Quit if out of field of view or all polys in convex hull point away from observer display in order [Artwork node; type 'Artwork on' to command tool] Quit if out of field of view or all polys in convex hull point away from observer Step from s=0 to s=1 if anti-aliasing else s=1 to s=0 PROC[ context: Context, patch: REF Patch, data: REF ANY _ NIL ] RETURNS[REF Patch] PROC[ context: Context, patch: REF Patch, data: REF ANY _ NIL ] RETURNS[REF Patch] Return patch corner vertices and calculate normal vectors Get normal at corner vertex,v, defined by cross-product of vectors formed by adjacent vertices. Vertices, nxtLft and nxtRgt are used in case of degenerate edges forming triangular patches Check that normal is not insignificantly small, if too small use outer knots only Get special output type for weird things like displacement mapping Polygons taken clockwise: Patches taken rowwise, right to left: [Artwork node; type 'Artwork on' to command tool] Check facing direction of each of the 9 polygons described by the 10 or 16 control points Subdivides a REF Patch for display purposes Expand patch by evaluating control point rows, to get 49 vertices Polygons taken clockwise: Patches taken rowwise, right to left: 3 -> 0 3 -> 0 3 2 1 0 ^ ^ 7 6 5 4 2 <- 1 15 <- 12 11 10 9 8 15 14 13 12 Have any edges been marked straight but not straightened? [p0, p1, p2, p3] => [col[0], col[1], col[2], col[3], col[4], col[5], col[6], col[7]] p0.data copied to col[0][0] and co[0]l[4], p3.data copied to col[3][7] and col[3][3] col[0], col[1], col[2], col[3] => row[0],row[1],row[2],row[3], row[4],row[5],row[6],row[7] col[0].data copied to row[0][0] & row[0][4], col[3].props copied to row[7][7] & row[7][3] outPatch[i][j*4 + k] _ CopyVtx[ row[colBase+j][rowBase+k]^ ]^; -- count along rows DeCasteljau subdivision algorithm PROC[ context: Context, patch: REF Patch, data: REF ANY _ NIL ] RETURNS[REF Patch] Procedures for expansion of B-Spline patches to convex polygons Procedure for expansion of Non-convex to semi-convex polygons Ê{1˜Ihead™šœ Ïmœ1™˜>J˜.Jšœ?˜?J˜——™=šŸ œ˜Mšžœžœžœžœžœžœžœ™RJšœ˜šžœ,žœžœ˜=šœžœ˜Jšœ˜šžœ ž˜Jšœ8žœžœ˜HJšœAžœžœ˜QJšžœ˜—J˜—Jšžœ˜—M˜(šžœ I˜`Nšžœ&˜*šžœžœ˜Jšžœ(žœ˜C——Mšžœ  ˜/Mšœ ˜N˜—š¡œžœžœžœ˜INšœ žœžœ˜Mš œžœžœžœžœ˜Qš œžœžœžœžœ˜UNš Q™Q—Jšžœžœžœ $˜GNšžœžœžœ˜#Nš žœ žœžœžœ ˜Pšžœ˜Nšžœž˜šžœ O˜UNšžœžœ8žœžœ˜WNšžœžœ7žœžœ˜WNšžœžœ7žœžœ˜WNšžœžœ7žœžœ˜WN˜——šžœžœ žœ˜:šžœžœ +˜ENšžœ%˜)šžœ˜ Jšœ" ˜6Nšœ&˜&Nšžœ žœ˜%Nšœ1  ˜;N˜——šžœ  -˜:Mšœžœ˜MšœE  ˜Qšžœžœ˜Mšžœ8 ˜VMš ™—M˜ZM˜VM˜——Jšžœ žœ% ˜RM˜—š¡œžœžœžœ˜Pš¡œžœ!žœžœ˜GJšœ ˜ JšœB ˜[N˜AN˜AJšœB ˜[N˜AN˜ANšœ> ˜[N˜=N˜=šžœ%žœžœ˜1N˜MN˜MJ˜=N˜=N˜=N˜—Jšžœ˜ J˜—š ¡ œžœžœžœžœ˜FJ˜I artworkFigure•GGFileæDGargoyle file for scene: stuffed from Gargoyle at February 27, 1988 4:02:09 pm PST Produced by version 8802.04 Scripts: Slope: [F 0.0] [F 30.0] [F 45.0] [F 60.0] [F 90.0] [F 120.0] [F 135.0] [F 150.0] Angle: [F -90.0] [F -60.0] [F -45.0] [F -30.0] [F 0.0] [F 30.0] [F 45.0] [F 60.0] [F 90.0] Radius: [F 4.0 4] [F 2.0 2] [F 1.0 1] [F 0.75 3/4] [F 0.6666667 2/3] [F 0.5 1/2] [F 0.3333333 1/3] [F 0.25 1/4] [F 0.125 1/8] [F 0.1111111 1/9] [F 5.555556e-2 1/18] LineDistance: [F 1.0 1] [F 0.5 1/2] [F 0.1111111 1/9] [F 5.555556e-2 1/18] [F 0.0 0] Midpoints: F Heuristics: F ShowAlignments: T ShowColors: F ScaleUnit: 72.0 DisplayStyle: print Gravity: T GravityExtent: 8.680555e-2 GravityType: pointsPreferred DefaultFont: xerox/pressfonts/helvetica-mrr [r1: 0.0 s: [10.0 10.0] r2: 0.0] 1.0 1.0 Defaults: [1 0.5] [1 1.0] 2.0 round round Dashed: F Shadows: []F Anchor: F Entities: [69]: Outline fillColor: [1 0.5] ow: T Trajectories: [1] Traj (open) [3] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [54.0,362.0] (Line ) [160.0,509.0] (Line ) [266.0,318.0] (Line ) [54.0,362.0] fwd: T Outline fillColor: [1 0.5] ow: T Trajectories: [1] Traj (open) [9] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [155.0,405.0] (Line ) [189.1274,456.5157] (Line ) [127.7821,464.3204] (Line ) [155.0,405.0] (Line ) [89.90349,411.7907] (Line ) [112.1553,349.93] (Line ) [155.0,405.0] (Line ) [185.7222,334.6614] (Line ) [232.5364,378.2977] (Line ) [155.0,405.0] fwd: T Text T "b" xerox/pressfonts/helvetica [10.0 0.0 271.0 0.0 10.0 311.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 241.0 0.0 10.0 378.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 199.0 0.0 10.0 458.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 167.0 0.0 10.0 513.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 105.0 0.0 10.0 469.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 64.0 0.0 10.0 416.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 29.0 0.0 10.0 363.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 95.0 0.0 10.0 340.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 168.0 0.0 10.0 326.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 167.0 0.0 10.0 409.0][] F 1.0 props: ( F ) Text T "003" xerox/pressfonts/helvetica [10.0 0.0 276.9375 0.0 10.0 306.2928][] F 1.0 props: ( F ) Text T "030" xerox/pressfonts/helvetica [10.0 0.0 172.9375 0.0 10.0 509.1609][] F 1.0 props: ( F ) Text T "300" xerox/pressfonts/helvetica [10.0 0.0 34.9375 0.0 10.0 358.2928][] F 1.0 props: ( F ) Text T "111" xerox/pressfonts/helvetica [10.0 0.0 172.9375 0.0 10.0 405.1609][] F 1.0 props: ( F ) Text T "021" xerox/pressfonts/helvetica [10.0 0.0 204.9375 0.0 10.0 453.2928][] F 1.0 props: ( F ) Text T "012" xerox/pressfonts/helvetica [10.0 0.0 246.9375 0.0 10.0 375.1609][] F 1.0 props: ( F ) Text T "210" xerox/pressfonts/helvetica [10.0 0.0 69.9375 0.0 10.0 413.1609][] F 1.0 props: ( F ) Text T "120" xerox/pressfonts/helvetica [10.0 0.0 110.9375 0.0 10.0 466.1609][] F 1.0 props: ( F ) Text T "201" xerox/pressfonts/helvetica [10.0 0.0 100.9375 0.0 10.0 336.1609][] F 1.0 props: ( F ) Text T "102" xerox/pressfonts/helvetica [10.0 0.0 173.9375 0.0 10.0 321.2928][] F 1.0 props: ( F ) Text T "0" xerox/pressfonts/Timesroman-B [12.0 0.0 266.0 0.0 12.0 326.8207][] F 1.0 props: ( F ) Text T "1" xerox/pressfonts/Timesroman-B [12.0 0.0 206.5 0.0 12.0 334.8207][] F 1.0 props: ( F ) Text T "2" xerox/pressfonts/Timesroman-B [12.0 0.0 124.5 0.0 12.0 349.8207][] F 1.0 props: ( F ) Text T "3" xerox/pressfonts/Timesroman-B [12.0 0.0 66.0 0.0 12.0 362.8207][] F 1.0 props: ( F ) Text T "4" xerox/pressfonts/Timesroman-B [12.0 0.0 101.0 0.0 12.0 414.8207][] F 1.0 props: ( F ) Text T "5" xerox/pressfonts/Timesroman-B [12.0 0.0 140.5 0.0 12.0 464.8207][] F 1.0 props: ( F ) Text T "6" xerox/pressfonts/Timesroman-B [12.0 0.0 143.5 0.0 12.0 506.8207][] F 1.0 props: ( F ) Text T "7" xerox/pressfonts/Timesroman-B [12.0 0.0 186.5 0.0 12.0 469.8207][] F 1.0 props: ( F ) Text T "8" xerox/pressfonts/Timesroman-B [12.0 0.0 229.5 0.0 12.0 393.8207][] F 1.0 props: ( F ) Text T "9" xerox/pressfonts/Timesroman-B [12.0 0.0 154.0 0.0 12.0 419.8207][] F 1.0 props: ( F ) Outline fillColor: [1 0.5] ow: T Trajectories: [1] Traj (open) [3] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [301.4636,373.7023] (Line ) [407.4636,520.7023] (Line ) [513.4636,329.7023] (Line ) [301.4636,373.7023] fwd: T Outline fillColor: [1 0.5] ow: T Trajectories: [1] Traj (open) [9] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [402.4636,416.7023] (Line ) [436.591,468.218] (Line ) [375.2457,476.0227] (Line ) [402.4636,416.7023] (Line ) [337.3671,423.493] (Line ) [359.6189,361.6323] (Line ) [402.4636,416.7023] (Line ) [433.1858,346.3637] (Line ) [480.0,390.0] (Line ) [402.4636,416.7023] fwd: T Outline fillColor: [1 0.0] ow: T Trajectories: [1] Traj (fence) [2] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [341.0,387.0] (Line ) [408.0,485.0] (Line ) [477.0,363.0] (Line ) fwd: T Outline fillColor: [1 0.5] ow: T Trajectories: [1] Traj (open) [3] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [376.2519,438.5625] (Line ) [405.5772,375.604] (Line ) [440.1239,428.2012] (Line ) [376.2519,438.5625] fwd: T Outline fillColor: [1 0.0] ow: T Trajectories: [1] Traj (fence) [2] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [379.0,402.0] (Line ) [409.0,454.0] (Line ) [441.0,391.0] (Line ) fwd: T Text T "0" xerox/pressfonts/Timesroman-B [12.0 0.0 477.0 0.0 12.0 367.5471][] F 1.0 props: ( F ) Text T "1" xerox/pressfonts/Timesroman-B [12.0 0.0 414.8247 0.0 12.0 378.0802][] F 1.0 props: ( F ) Text T "2" xerox/pressfonts/Timesroman-B [12.0 0.0 334.0 0.0 12.0 389.6848][] F 1.0 props: ( F ) Text T "3" xerox/pressfonts/Timesroman-B [12.0 0.0 370.0 0.0 12.0 440.6848][] F 1.0 props: ( F ) Text T "4" xerox/pressfonts/Timesroman-B [12.0 0.0 406.0 0.0 12.0 489.5471][] F 1.0 props: ( F ) Text T "5" xerox/pressfonts/Timesroman-B [12.0 0.0 441.0 0.0 12.0 429.6848][] F 1.0 props: ( F ) Text T "0" xerox/pressfonts/Timesroman-B [12.0 0.0 440.2183 0.0 12.0 398.181][] F 1.0 props: ( F ) Text T "1" xerox/pressfonts/Timesroman-B [12.0 0.0 373.0 0.0 12.0 407.5471][] F 1.0 props: ( F ) Text T "2" xerox/pressfonts/Timesroman-B [12.0 0.0 405.0 0.0 12.0 458.5471][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 469.0 0.0 10.0 354.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 388.0 0.0 10.0 371.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 324.0 0.0 10.0 382.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 344.3988 0.0 10.0 438.6395][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 409.6938 0.0 10.0 450.4688][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 447.0625 0.0 10.0 420.0][] F 1.0 props: ( F ) Text T "200" xerox/pressfonts/helvetica [10.0 0.0 329.9375 0.0 10.0 377.2928][] F 1.0 props: ( F ) Text T "101" xerox/pressfonts/helvetica [10.0 0.0 393.9375 0.0 10.0 366.2928][] F 1.0 props: ( F ) Text T "002" xerox/pressfonts/helvetica [10.0 0.0 474.9375 0.0 10.0 350.1609][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 435.0625 0.0 10.0 381.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 364.0 0.0 10.0 395.0][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 376.4296 0.0 10.0 485.8986][] F 1.0 props: ( F ) Text T "020" xerox/pressfonts/helvetica [10.0 0.0 382.3671 0.0 10.0 483.0595][] F 1.0 props: ( F ) Text T "110" xerox/pressfonts/helvetica [10.0 0.0 350.3363 0.0 10.0 433.9322][] F 1.0 props: ( F ) Text T "011" xerox/pressfonts/helvetica [10.0 0.0 453.0 0.0 10.0 415.2927][] F 1.0 props: ( F ) Text T "b" xerox/pressfonts/helvetica [10.0 0.0 401.3964 0.0 10.0 416.8291][] F 1.0 props: ( F ) Text T "001" xerox/pressfonts/helvetica [10.0 0.0 441.0 0.0 10.0 376.2928][] F 1.0 props: ( F ) Text T "100" xerox/pressfonts/helvetica [10.0 0.0 369.9375 0.0 10.0 391.1609][] F 1.0 props: ( F ) Text T "010" xerox/pressfonts/helvetica [10.0 0.0 415.6313 0.0 10.0 446.4688][] F 1.0 props: ( F ) Text T "000" xerox/pressfonts/helvetica [10.0 0.0 407.3339 0.0 10.0 412.1219][] F 1.0 props: ( F ) Text T "s=0" xerox/pressfonts/helvetica [10.0 0.0 383.69 0.0 10.0 340.1609][] F 1.0 props: ( F ) Text T "r=0" xerox/pressfonts/helvetica [10.0 0.0 458.315 0.0 10.0 443.1609][] F 1.0 props: ( F ) Text T "t=0" xerox/pressfonts/helvetica [10.0 0.0 326.6275 0.0 10.0 456.1609][] F 1.0 props: ( F ) –Ô83.15227 mm topLeading 83.15227 mm topIndent 1.411111 mm bottomLeading 0.5 0.3 0.95 backgroundColor the topLeading 6 pt .sub backgroundAscent 3 pt backgroundDescent 4 pt outlineBoxThickness 1 pt outlineBoxBearoff•Bounds:0.0 mm xmin 0.0 mm ymin 173.0247 mm xmax 80.33005 mm ymax •Artwork Interpress• InterpressÛ2Interpress/Xerox/3.0  f j k j¡¥“ļ= ¤ ¨ x j‡ÄÐ) ¢ ¨Ä¡¨ ¡ ¡™ x j¢¯“¢°“¢·“¡¡¨Ö ™@—ªÞ—Ö —˜ kÄ¡¨ ¡ ¡™ x j¢¯“¢°“¢·“¡¡¨;5™ÄK[fÄr!@—Ä&ïNÄyN—;5—Ä( rÄE++—ÄF‰¡Äˆ±d—;5—Ä ÄQ >—ÄcënÄEt/—;5—˜ k x jª ¤¯× ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤‘ ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤gj ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤G¡ ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤ u ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤à@ ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤½  ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤ÿô ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤Hæ ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤G9 ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤ÄOÄ1) ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á003– k x jª ¤Ä ÏÄ­ W ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á030– k x jª ¤Ä/Ä9b) ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á300– k x jª ¤Ä Ïĉ±W ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á111– k x jª ¤Ä ÏÄH™) ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á021– k x jª ¤ÄoÄW ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á012– k x jª ¤Ä_ÄŒiW ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á210– k x jª ¤ÄïÄžlW ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á120– k x jª ¤ÄOÄr>W ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á201– k x jª ¤Ä ßÄ3u) ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á102– k x j¬ ¤ªÄ‡Sj ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á0– k x j¬ ¤ÄÄŠ£j ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á1– k x j¬ ¤ÄùÄÙj ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á2– k x j¬ ¤âÄ–;j ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á3– k x j¬ ¤Ä«Ãj ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á4– k x j¬ ¤ÄÄFÐ' ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á5– k x j¬ ¤ÄÄM6' ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á6– k x j¬ ¤ÄuÄG“' ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á7– k x j¬ ¤ÄËÄ£j ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á8– k x j¬ ¤:Ä­Õj ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á9– kÄ¡¨ ¡ ¡™ x j¢¯“¢°“¢·“¡¡¨Ä‰nÄDœ/™ÄAB)Ä_™/—ÄR<)Äl/T—ĉnÄDœ/—˜ kÄ¡¨ ¡ ¡™ x j¢¯“¢°“¢·“¡¡¨Ä@u)ÄL/™Ä%…ÄŽ©N—ÄS9ÄQÑ,—Ä@u)ÄL/—ÄhOÄutG—įC3Ä`D—Ä@u)ÄL/—ÄvsFÄâ —€&—Ä@u)ÄL/—˜ k ¡¨õ#™8…—} —õ#—¡¡ ¡™ x j¢¯“¢°“¢·“¡¡¨õ#™8…—} —õ#—¡¸ kÄ¡¨ ¡ ¡™ x j¢¯“¢°“¢·“¡¡¨ÄÀ‰ƒÄi™ÄÂÞ{ÄFm0—ÄÂFqÄQ¤—ÄÀ‰ƒÄi—˜ k ¡¨2™9f—Y'—2—¡¡ ¡™ x j¢¯“¢°“¢·“¡¡¨2™9f—Y'—2—¡¸ k x j¬ ¤}ÄL5 ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á0– k x j¬ ¤Ä\]9Ä-» ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á1– k x j¬ ¤îÄoI ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á2– k x j¬ ¤Ä}ªI ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á3– k x j¬ ¤6ÄeZ5 ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á4– k x j¬ ¤YÄz‡I ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á5– k x j¬ ¤Ä^”7Ä£Qi ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á0– k x j¬ ¤ÄT`5 ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á1– k x j¬ ¤5Ä^ï5 ¢ ¥ ¨ÅxeroxÅ pressfontsÅTimesroman-brr£¡ “ •  —¡¡¨  Š¡²“Á2– k x jª ¤u ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤$ ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤ä ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤ÄÛI£Ä“[V ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤ÄNk1Ä8O  ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤ÄñD ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Áb– k x jª ¤ÄŸÄNšœK˜KNšœK˜KNšœK˜KNšœK˜KNšœK˜KNšœK˜KJ˜——Jšœ#˜#Mšžœ  ˜/M˜—š ¡œžœžœ žœžœ ˜CJ™9Nšœ žœ žœžœ9˜WNšœ žœ žœžœ9˜Wš¡œžœžœžœ˜FJ™¼J˜Jšœ9 #˜\J˜9Nšžœžœ5˜QNšžœžœ5˜Q˜9N™Q—šžœžœ>˜DNšžœ< ˜Ušžœžœ˜šžœ 1˜=J˜9J˜9šžœ˜NšžœD˜H—šžœ˜NšžœD˜H—N˜9šžœžœ>˜DNšžœ:˜>NšžœE˜I—N˜—NšžœE˜I——Nšžœ˜N˜M™B—Mšœ žœžœ7˜PMšžœžœžœ˜5Nšœ ˜4šžœ˜šžœ˜N™BO–ÚInterpress/Xerox/3.0  f j k j¡¥“ļ= ¤ ¨ x jðÄÿpßW ¢ ¨Ä¡¨ ¡ ¡™ x j¢¯“¢°“¢·“¡¡¨Z˜™¼ŽÃT—aY—Z˜—˜ kÄ¡¨ ¡ ¡™ x j¢¯“¢°“¢·“¡¡¨›™ž—F—l–—r…—vn—rV—RŽ,[—X—v—þŠ—›—˜ kÄ¡¨ ¡ ¡™ x j¢¯“¢°“¢·“¡¡¨v™!x—\r—vn—˜ kÄ¡¨ ¡ ¡™ x j¢¯“¢°“¢·“¡¡¨r…™L‹——ÄŠ$eÄÀÆe—˜ kÄ¡¨ ¡ ¡™ x j¢¯“¢°“¢·“¡¡¨ž™—!x—,[—˜ kÄ¡¨ ¡ ¡™ x j¢¯“¢°“¢·“¡¡¨RV™\r—L‹—F—˜ k x jª ¤ûÄ’‡W ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á0– k x jª ¤(Q ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á1– k x jª ¤NÄ‘‚W ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á2– k x jª ¤vP ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á3– k x jª ¤öÄgW ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á4– k x jª ¤n ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á5– k x jª ¤õĤáW ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á8– k x jª ¤‚ ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á9– k x jª ¤A ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á10– k x jª ¤yƒ ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á11– k x jª ¤öž ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á12– k x jª ¤ÄÂa ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á13– k x jª ¤DÄÁËa ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á14– k x jª ¤ÄkÄ¿$a ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á15– k x jª ¤YÄ“ŒW ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á0– k x jª ¤TĬW ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á1– k x jª ¤¿Ä¿$a ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á2– k x jª ¤Ä“Ä‘‚W ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á3– k x jª ¤ÄëĬ“a ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á7– k x jª ¤Mh ¢ ¥ ¨ÅxeroxÅ pressfontsÅ helvetica-mrr£¡ “ •  —¡¡¨  Š¡²“Á6– k k k g– Interpress–:0.0 mm xmin 0.0 mm ymin 111.8306 mm xmax 37.49771 mm ymax –Ô40.31994 mm topLeading 40.31994 mm topIndent 1.411111 mm bottomLeading 0.5 0.3 0.95 backgroundColor the topLeading 6 pt .sub backgroundAscent 3 pt backgroundDescent 4 pt outlineBoxThickness 1 pt outlineBoxBearoff–"Gargoyle file for scene: stuffed from Gargoyle at February 27, 1988 3:45:46 pm PST Produced by version 8802.04 Scripts: ///Users/crow.pa/gargoyle/crow880227-15-04-33.script Slope: [F 0.0] [F 30.0] [F 45.0] [F 60.0] [F 90.0] [F 120.0] [F 135.0] [F 150.0] Angle: [F -90.0] [F -60.0] [F -45.0] [F -30.0] [F 0.0] [F 30.0] [F 45.0] [F 60.0] [F 90.0] Radius: [F 4.0 4] [F 2.0 2] [F 1.0 1] [F 0.75 3/4] [F 0.6666667 2/3] [F 0.5 1/2] [F 0.3333333 1/3] [F 0.25 1/4] [F 0.125 1/8] [F 0.1111111 1/9] [F 5.555556e-2 1/18] LineDistance: [F 1.0 1] [F 0.5 1/2] [F 0.1111111 1/9] [F 5.555556e-2 1/18] [F 0.0 0] Midpoints: F Heuristics: T ShowAlignments: F ShowColors: F ScaleUnit: 72.0 DisplayStyle: print Gravity: T GravityExtent: 8.680555e-2 GravityType: pointsPreferred DefaultFont: xerox/pressfonts/helvetica-mrr [r1: 0.0 s: [10.0 10.0] r2: 0.0] 1.0 1.0 Defaults: [1 0.5] [1 1.0] 2.0 round round Dashed: F Shadows: []F Anchor: F Entities: [26]: Outline fillColor: [1 0.5] ow: T Trajectories: [1] Traj (open) [4] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [186.0,504.0] (Line ) [284.0,504.0] (Line ) [291.0,436.0] (Line ) [193.0,441.0] (Line ) [186.0,504.0] fwd: T Outline fillColor: [1 0.5] ow: T Trajectories: [1] Traj (open) [12] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [352.0,507.0] (Line ) [383.0,510.0] (Line ) [422.0,509.0] (Line ) [460.0,502.0] (Line ) [466.0,485.0] (Line ) [470.0,462.0] (Line ) [466.0,438.0] (Line ) [434.0,438.0] (Line ) [396.0,443.0] (Line ) [353.0,440.0] (Line ) [352.0,470.0] (Line ) [350.0,490.0] (Line ) [352.0,507.0] fwd: T Outline fillColor: [1 0.5] ow: T Trajectories: [1] Traj (open) [3] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [352.0,470.0] (Line ) [385.0,472.0] (Line ) [444.0,466.0] (Line ) [470.0,462.0] fwd: T Outline fillColor: [1 0.5] ow: T Trajectories: [1] Traj (open) [3] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [466.0,485.0] (Line ) [428.0,491.0] (Line ) [376.0,493.0] (Line ) [350.1386,488.6139] fwd: T Outline fillColor: [1 0.5] ow: T Trajectories: [1] Traj (open) [3] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [383.0,510.0] (Line ) [376.0,493.0] (Line ) [385.0,472.0] (Line ) [396.0,443.0] fwd: T Outline fillColor: [1 0.5] ow: T Trajectories: [1] Traj (open) [3] arrows: 0 j: round e: T round w: 2.0 c: T [1 1.0] d: T F [434.0,438.0] (Line ) [444.0,466.0] (Line ) [428.0,491.0] (Line ) [422.0,509.0] fwd: T Text T "0" xerox/pressfonts/helvetica [10.0 0.0 347.0 0.0 10.0 431.1609][] F 1.0 props: ( F ) Text T "1" xerox/pressfonts/helvetica [10.0 0.0 392.0 0.0 10.0 433.0][] F 1.0 props: ( F ) Text T "2" xerox/pressfonts/helvetica [10.0 0.0 430.0 0.0 10.0 428.1609][] F 1.0 props: ( F ) Text T "3" xerox/pressfonts/helvetica [10.0 0.0 470.0 0.0 10.0 432.0][] F 1.0 props: ( F ) Text T "4" xerox/pressfonts/helvetica [10.0 0.0 342.0 0.0 10.0 463.1609][] F 1.0 props: ( F ) Text T "5" xerox/pressfonts/helvetica [10.0 0.0 379.0 0.0 10.0 462.0][] F 1.0 props: ( F ) Text T "8" xerox/pressfonts/helvetica [10.0 0.0 341.0 0.0 10.0 485.1609][] F 1.0 props: ( F ) Text T "9" xerox/pressfonts/helvetica [10.0 0.0 370.0 0.0 10.0 482.0][] F 1.0 props: ( F ) Text T "10" xerox/pressfonts/helvetica [10.0 0.0 417.0 0.0 10.0 481.0][] F 1.0 props: ( F ) Text T "11" xerox/pressfonts/helvetica [10.0 0.0 473.0 0.0 10.0 483.0][] F 1.0 props: ( F ) Text T "12" xerox/pressfonts/helvetica [10.0 0.0 342.0 0.0 10.0 510.0][] F 1.0 props: ( F ) Text T "13" xerox/pressfonts/helvetica [10.0 0.0 377.0 0.0 10.0 513.4536][] F 1.0 props: ( F ) Text T "14" xerox/pressfonts/helvetica [10.0 0.0 420.0 0.0 10.0 511.4536][] F 1.0 props: ( F ) Text T "15" xerox/pressfonts/helvetica [10.0 0.0 461.375 0.0 10.0 504.4536][] F 1.0 props: ( F ) Text T "0" xerox/pressfonts/helvetica [10.0 0.0 185.0 0.0 10.0 434.1609][] F 1.0 props: ( F ) Text T "1" xerox/pressfonts/helvetica [10.0 0.0 180.0 0.0 10.0 506.1609][] F 1.0 props: ( F ) Text T "2" xerox/pressfonts/helvetica [10.0 0.0 287.0 0.0 10.0 504.4536][] F 1.0 props: ( F ) Text T "3" xerox/pressfonts/helvetica [10.0 0.0 297.1875 0.0 10.0 428.1609][] F 1.0 props: ( F ) Text T "7" xerox/pressfonts/helvetica [10.0 0.0 477.375 0.0 10.0 455.4536][] F 1.0 props: ( F ) Text T "6" xerox/pressfonts/helvetica [10.0 0.0 429.0 0.0 10.0 456.0][] F 1.0 props: ( F ) š¡3™3NšœB ˜\NšœA ˜YNšœA ˜YN˜ANšœ %˜>N˜6N˜—šžœžœžœ 9˜VNšœ< ˜VNšœ< ˜TNšœ< ˜TNšœ  ˜9N˜4J˜——š žœžœžœžœ 6˜VšžœDžœ˜MNšœžœ žœ  0˜ON˜#N˜!N˜ šžœDžœ˜Lšžœž˜Nšž œ5˜@—Nšœ 9œ˜INšžœ˜ N˜—N˜—Nšžœ˜ —šžœ &˜>JšžœA˜E—Jšžœ˜ J˜—š ¡œžœžœ žœžœ˜LJ™YJ˜Jšœžœžœ˜&J˜Jšžœžœžœžœ˜$Jšžœžœžœžœ˜"šžœžœžœž˜šžœžœžœž˜Jšœžœ%˜/J˜šžœ˜šžœ˜Jšœžœ ˜1N˜BN˜—šžœžœžœ ˜3Jšœžœ˜ šžœž˜ JšœEžœ˜PJ˜HJšœEžœ˜PJšžœ˜—J˜——šœ$˜$Jšœžœ˜šœžœ˜+Jšžœžœ˜'—J˜—šžœžœž˜!šžœžœ ž˜(Jšžœžœ žœ˜-——Jšœ$˜$J˜Jšžœ˜—Jšžœ˜—šžœžœžœ ˜Jšžœ˜šžœžœ˜ Jšžœ˜šžœžœ˜Jšžœ˜Jšžœ˜———šžœžœ˜Jšœ2žœ˜Q—Jšžœžœžœžœžœžœžœ˜5J˜—š¡œžœžœ žœžœžœžœžœ ˜‡J™+Nšœ žœžœžœ˜$Nšœžœžœ˜'šœžœžœ˜'N™AN™BN™$N™N™$N™—N™9Jšžœžœ˜ ™RJšžœ˜—Jšžœ˜—šžœžœ˜$šžœžœžœ ž˜Jšžœ˜JšœA˜Ašžœ˜JšžœL˜P—Jšžœ˜—J˜5J˜—Nšžœžœ-˜NJšžœ˜—šžœžœžœžœžœžœžœžœ ˜XNšœ/˜/Nšžœžœ˜—šžœžœžœžœžœžœžœž˜;Nšœ.˜.Nšžœžœ˜—Jšžœ8 ˜XJšœŸ˜—š¡œžœžœ.ž œ˜J™!Jšœžœ ˜Jšœžœ.˜6Jšœ3 ˜HJšœ1 ˜FJšœ  ˜=JšœB ˜ZJšœ% (˜MJ˜šžœ K˜]Jšžœ˜Jšžœžœ žœ˜0—Jšœ+˜+Jšœ¡˜—š¡œ˜Mšžœžœžœžœžœžœžœ™RJšœ+˜+šžœžœ˜#šžœžœžœžœ˜#šžœžœ˜'Nšžœ˜N˜@N˜?N˜:š žœ žœ žœ žœ žœ ˜>Jšžœ (˜Hšžœ   ˜0J˜@J˜@J˜@Jšžœžœ*˜FJ˜——J˜—Jšžœ˜—J˜—Jšžœ˜J˜——™?š¢œ˜Mšžœžœ˜ M˜—š¢œ˜"Mšžœžœ˜ M˜——™=š¢œ˜Mšžœžœ˜ M˜—M˜—˜J˜—Jšžœ˜—…—”R©