DIRECTORY Atom USING [PutPropOnList], Basics USING [Comparison], CADTypes USING [Scad, VariableRec, VisibleMask], Convert USING [CardFromRope, Error], Geometry3dBasics USING [Triple, TripleSequence, TripleSequenceRep], Geometry3dVector USING [Add], ImagerColor USING [RGB], IO USING [BreakProc, Error, GetRope, GetTokenRope, int, PutFR, RIS, STREAM], MessageWindow USING [Append], Rope USING [Compare, IsPrefix, ROPE], SceneUtilities USING [AddShape, CreateDefaultContext, DeleteShape, Hide, NameBackgroundColor, PlaceShape, Reveal, SetColor, SetHiddenLines, SetFaceted, SetLight, SetLines, SetNormaledLines, SetShadedLines, SetSmooth, ShapeInstance], SurfaceRender USING [MakeFrame], SurfaceViewer, ThreeDBasics USING [Context, RGB, SetView], ThreeDHacks USING [RegisterNewClasses, MakeFatPoint, MakeFatSeg, MakeTwoCell], ThreeDViewer USING [ButtonDesc, MakeViewer], ViewerClasses USING [Column, Viewer], ViewerOps USING [ChangeColumn, OpenIcon]; SurfaceViewerImpl: CEDAR PROGRAM IMPORTS Atom, Convert, IO, MessageWindow, Rope, SurfaceRender, ThreeDBasics, ThreeDHacks, ThreeDViewer, SceneUtilities, Geometry3dVector, ViewerOps EXPORTS SurfaceViewer ~ BEGIN initialPosition: Geometry3dBasics.Triple _ [2.0, -10.0, 3.0]; initialForward: Geometry3dBasics.Triple _ [-2.0, 10.0, -3.0]; initialUp: Geometry3dBasics.Triple _ [0., 0., 1.]; maxNumberOfSurfaces: NAT ~ 3; backgroundColorName: Rope.ROPE ~ "White"; -- "Darkish Blue" Error: PUBLIC ERROR[why: ATOM] = CODE; CreateSurfaceViewer: PUBLIC PROC[] RETURNS[sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN workingDirectory: Rope.ROPE; sviewer _ NEW[SurfaceViewer.SurfaceViewer]; sviewer.surfaces _ NEW[SurfaceViewer.SurfaceSeq[maxNumberOfSurfaces]]; sviewer.numberOfSurfaces _ 0; sviewer.nextID _ 0; sviewer.context3d _ SceneUtilities.CreateDefaultContext[]; SceneUtilities.NameBackgroundColor[sviewer.context3d, backgroundColorName]; SceneUtilities.SetLight [sviewer.context3d, "Initial", initialPosition]; ThreeDBasics.SetView [ context: sviewer.context3d, eyePoint: initialPosition, ptOfInterest: Geometry3dVector.Add[initialPosition, initialForward], upDirection: initialUp]; ThreeDHacks.RegisterNewClasses[sviewer.context3d]; sviewer.context3d.props _ Atom.PutPropOnList[sviewer.context3d.props, $WDir, workingDirectory]; ThreeDViewer.MakeViewer[ context: sviewer.context3d, bannerName: "ThreeDWorld -- Algebraic Surface Viewer", menu: NIL, mouseAction: NIL]; IF sviewer.context3d.viewer.column # color THEN ViewerOps.ChangeColumn[sviewer.context3d.viewer, color]; IF sviewer.context3d.viewer.iconic THEN ViewerOps.OpenIcon[icon: sviewer.context3d.viewer]; DrawFrame[sviewer]; END; GetThreeDContext: PUBLIC PROC[sviewer: REF SurfaceViewer.SurfaceViewer] RETURNS[context3d: REF ThreeDBasics.Context] ~ BEGIN context3d _ sviewer.context3d; END; LoadSurface: PUBLIC PROC[surface: CADTypes.Scad, sviewer: REF SurfaceViewer.SurfaceViewer] RETURNS[id: NAT] ~ BEGIN index, newID: NAT; IF sviewer.numberOfSurfaces >= maxNumberOfSurfaces THEN ERROR Error[$AllFilledUp]; index _ sviewer.numberOfSurfaces; sviewer.numberOfSurfaces _ sviewer.numberOfSurfaces + 1; newID _ sviewer.nextID; sviewer.nextID _ sviewer.nextID + 1; sviewer.surfaces[index] _ [ scad: surface, id: newID, mask: NEW[CADTypes.VisibleMask[surface.cells.nCells]] ]; FOR i: NAT IN [0..surface.cells.nCells) DO sviewer.surfaces[index].mask[i] _ TRUE; ENDLOOP; FOR cell: NAT IN [0..surface.cells.nCells) DO newCell: REF SceneUtilities.ShapeInstance; cellName: Rope.ROPE _ MakeShapeName[ newID, surface.cells[cell].indexX, surface.cells[cell].indexY, surface.cells[cell].indexZ]; MessageWindow.Append[IO.PutFR["SurfaceViewer loading cell %g of %g.", IO.int[cell + 1], IO.int[surface.cells.nCells]], TRUE]; SELECT surface.cells[cell].dimension FROM 0 => BEGIN newCell _ ThreeDHacks.MakeFatPoint[ name: cellName, position: surface.cells[cell].vertices[0]]; SceneUtilities.AddShape[sviewer.context3d, newCell]; SceneUtilities.PlaceShape[sviewer.context3d, cellName, [0, 0, 0]]; SceneUtilities.SetColor[sviewer.context3d, cellName, [0, 0.1, 0]]; SceneUtilities.SetFaceted[sviewer.context3d, cellName]; END; 1 => BEGIN vertexSequence: Geometry3dBasics.TripleSequence _ NEW[Geometry3dBasics.TripleSequenceRep[surface.cells[cell].vertices.nVertices]]; vertexSequence.length _ surface.cells[cell].vertices.nVertices; FOR i: NAT IN [0..surface.cells[cell].vertices.nVertices) DO vertexSequence[i] _ surface.cells[cell].vertices[i] ENDLOOP; newCell _ ThreeDHacks.MakeFatSeg[ name: cellName, points: vertexSequence]; SceneUtilities.AddShape[sviewer.context3d, newCell]; SceneUtilities.PlaceShape[sviewer.context3d, cellName, [0, 0, 0]]; SceneUtilities.SetColor[sviewer.context3d, cellName, [0, 0.1, 0]]; SceneUtilities.SetFaceted[sviewer.context3d, cellName]; IF surface.cells[cell].vertices.nVertices < 2 THEN SceneUtilities.Hide[sviewer.context3d, cellName]; END; 2 => BEGIN newCell _ ThreeDHacks.MakeTwoCell[ name: cellName, vertices: surface.cells[cell].vertices, triangles: surface.cells[cell].polygons]; SceneUtilities.AddShape[sviewer.context3d, newCell]; SceneUtilities.PlaceShape[sviewer.context3d, cellName, [0, 0, 0]]; SceneUtilities.SetColor[sviewer.context3d, cellName, [0.8, 0.2, 0.2]]; SceneUtilities.SetLines[sviewer.context3d, cellName]; IF surface.cells[cell].polygons.nTriangles = 0 THEN SceneUtilities.Hide[sviewer.context3d, cellName]; END; ENDCASE; ENDLOOP; MessageWindow.Append["Surface loaded.", TRUE]; DrawFrame[sviewer]; RETURN[newID]; END; MaskSurface: PUBLIC PROC[id: NAT, mask: REF CADTypes.VisibleMask, sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN index: NAT _ IndexFromID[id, sviewer]; --verify ID FOR i: NAT IN [0..sviewer.surfaces[index].scad.cells.nCells) DO nameToMask: Rope.ROPE _ MakeShapeName[ id, sviewer.surfaces[index].scad.cells[i].indexX, sviewer.surfaces[index].scad.cells[i].indexY, sviewer.surfaces[index].scad.cells[i].indexZ ]; IF mask[i] THEN SceneUtilities.Reveal[sviewer.context3d, nameToMask] ELSE SceneUtilities.Hide[sviewer.context3d, nameToMask]; ENDLOOP; sviewer.surfaces[index].mask _ mask; DrawFrame[sviewer]; END; HideSurface: PUBLIC PROC[id: NAT, sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN index: NAT _ IndexFromID[id, sviewer]; --verify ID namesToHide: LIST OF Rope.ROPE _ ShapesMatchingID[id, sviewer]; UNTIL namesToHide = NIL DO SceneUtilities.Hide[sviewer.context3d, namesToHide.first]; namesToHide _ namesToHide.rest; ENDLOOP; DrawFrame[sviewer]; END; UnHideSurface: PUBLIC PROC[id: NAT, sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN index: NAT _ IndexFromID[id, sviewer]; --verify ID namesToHide: LIST OF Rope.ROPE _ ShapesMatchingID[id, sviewer]; UNTIL namesToHide = NIL DO SceneUtilities.Reveal[sviewer.context3d, namesToHide.first]; namesToHide _ namesToHide.rest; ENDLOOP; DrawFrame[sviewer]; END; DeleteSurface: PUBLIC PROC[id: NAT, sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN index: NAT _ IndexFromID[id, sviewer]; namesToDelete: LIST OF Rope.ROPE _ ShapesMatchingID[id, sviewer]; FOR i: NAT IN [index..sviewer.numberOfSurfaces - 1) DO sviewer.surfaces[i] _ sviewer.surfaces[i + 1]; ENDLOOP; sviewer.numberOfSurfaces _ sviewer.numberOfSurfaces - 1; UNTIL namesToDelete = NIL DO SceneUtilities.DeleteShape[sviewer.context3d, namesToDelete.first]; namesToDelete _ namesToDelete.rest; ENDLOOP; DrawFrame[sviewer]; END; SetSurfaceRenderingMode: PUBLIC PROC[id: NAT, mode: ATOM, sviewer: REF SurfaceViewer.SurfaceViewer, twoCellsOnly: BOOL _ TRUE] ~ BEGIN index: NAT _ IndexFromID[id, sviewer]; namesOfCells: LIST OF Rope.ROPE _ ShapesMatchingID[id, sviewer]; UNTIL namesOfCells = NIL DO name: Rope.ROPE _ namesOfCells.first; SELECT mode FROM $Lines => SceneUtilities.SetLines[sviewer.context3d, name]; $Faceted => SceneUtilities.SetFaceted[sviewer.context3d, name]; $Smooth => SceneUtilities.SetSmooth[sviewer.context3d, name]; $HiddenLines => SceneUtilities.SetHiddenLines[sviewer.context3d, name]; $NormaledLines => SceneUtilities.SetNormaledLines[sviewer.context3d, name]; $ShadedLines => SceneUtilities.SetShadedLines[sviewer.context3d, name]; ENDCASE => ERROR Error[$InvalidRenderingMode]; namesOfCells _ namesOfCells.rest; ENDLOOP; DrawFrame[sviewer]; END; SetSurfaceColor: PUBLIC PROC[id: NAT, color: ThreeDBasics.RGB, sviewer: REF SurfaceViewer.SurfaceViewer, twoCellsOnly: BOOL _ TRUE] ~ BEGIN index: NAT _ IndexFromID[id, sviewer]; namesOfCells: LIST OF Rope.ROPE _ ShapesMatchingID[id, sviewer]; UNTIL namesOfCells = NIL DO SceneUtilities.SetColor[sviewer.context3d, namesOfCells.first, color]; namesOfCells _ namesOfCells.rest; ENDLOOP; DrawFrame[sviewer]; END; FlushSurfaces: PUBLIC PROC[sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN sviewer.numberOfSurfaces _ 0; DrawFrame[sviewer]; END; IndexFromID: PROC [id: NAT, sviewer: REF SurfaceViewer.SurfaceViewer] RETURNS [index: NAT] ~ BEGIN found: BOOLEAN _ FALSE; FOR i: NAT IN [0..sviewer.numberOfSurfaces) DO IF sviewer.surfaces[i].id = id THEN {index _ i; found _ TRUE}; ENDLOOP; IF ~found THEN ERROR Error[$InvalidID]; END; ShapesMatchingID: PROC [id: NAT, sviewer: REF SurfaceViewer.SurfaceViewer] RETURNS [shapes: LIST OF Rope.ROPE] ~ BEGIN names: LIST OF Rope.ROPE _ NIL; FOR i: NAT IN [0..sviewer.context3d.shapes.length) DO name: Rope.ROPE _ sviewer.context3d.shapes[i].name; IF Rope.IsPrefix["Cell", name] THEN BEGIN surfaceID: NAT; [id: surfaceID] _ ParseShapeName[name]; IF surfaceID = id THEN names _ CONS[name, names]; END; ENDLOOP; RETURN[names]; END; MakeShapeName: PROC [id, indexX, indexY, indexZ: NAT] RETURNS [name: Rope.ROPE] ~ BEGIN name _ IO.PutFR [ "CellS%gX%gY%gZ%g", IO.int[id], IO.int[indexX], IO.int[indexY], IO.int[indexZ]]; RETURN; END; ParseError: ERROR = CODE; ParseShapeName: PROC [name: Rope.ROPE] RETURNS [id, indexX, indexY, indexZ: NAT] ~ BEGIN BreakProc: IO.BreakProc ~ BEGIN RETURN [SELECT char FROM IN ['0..'9] => other, ENDCASE => break]; END; inStream: IO.STREAM _ IO.RIS[name]; token: Rope.ROPE; token _ IO.GetRope[inStream, 4, FALSE]; IF Rope.Compare[token, "Cell"] # equal THEN ERROR ParseError; [token: token] _ IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError]; IF Rope.Compare[token, "S"] # equal THEN ERROR ParseError; [token: token] _ IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError]; id _ Convert.CardFromRope[token ! Convert.Error => ERROR ParseError]; [token: token] _ IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError]; IF Rope.Compare[token, "X"] # equal THEN ERROR ParseError; [token: token] _ IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError]; indexX _ Convert.CardFromRope[token ! Convert.Error => ERROR ParseError]; [token: token] _ IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError]; IF Rope.Compare[token, "Y"] # equal THEN ERROR ParseError; [token: token] _ IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError]; indexY _ Convert.CardFromRope[token ! Convert.Error => ERROR ParseError]; [token: token] _ IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError]; IF Rope.Compare[token, "Z"] # equal THEN ERROR ParseError; [token: token] _ IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError]; indexZ _ Convert.CardFromRope[token ! Convert.Error => ERROR ParseError]; END; InvokeRayTracer: PUBLIC PROC[variables: CADTypes.VariableRec, filename: Rope.ROPE, pixelsU, pixelsV: NAT, sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN END; DrawFrame: PUBLIC PROC [sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN ThreeDBasics.SetView[ -- get new screen dimensions into transformations context: sviewer.context3d, eyePoint: sviewer.context3d.eyePoint, ptOfInterest: sviewer.context3d.ptOfInterest, fieldOfView: sviewer.context3d.fieldOfView, rollAngle: sviewer.context3d.rollAngle, upDirection: sviewer.context3d.upDirection, hitherLimit: sviewer.context3d.hitherLimit, yonLimit: sviewer.context3d.yonLimit ]; SceneUtilities.SetLight[ context: sviewer.context3d, name: "Torch", position: sviewer.context3d.eyePoint, color: [1, 1, 1] ]; SurfaceRender.MakeFrame[sviewer.context3d]; END; END. SurfaceViewerImpl.mesa James Rauen, August 26, 1986 2:14:07 am PDT Last edited by: James Rauen January 18, 1988 3:14:59 pm PST LightingModels USING [Model, PointSourceSequence], ShadingModels USING [ShadingSequence], SurfaceTracer, Constants and type declarations Note: This has some resemblance to Init in ThreeDDemoImpl Create the sviewer and initialize its slots. Initialize the context's graphics Register the $FatPoint and $FatSeg classes Store away the working directory (motivated by ThreeDDemoImpl) Create the actual viewer, move it to the color display, and open it. Draw it LoadPicture: PROC[context: REF ThreeDBasics.Context, key: ATOM] ~ BEGIN SceneUtilities.AddShapeAt[context, "Glass", "[Cedar]ThreeDWorld>ChampagneGlass.shape"]; SurfaceRender.MakeFrame[context]; END; MenuHit: PROC [bttn, choice: ATOM, x, y: REAL] ~ BEGIN END; Assign the surface the next available position (index) in the surface sequence. If there is no room, signal so. Assign the surface an ID number. Construct a SurfaceViewer record for the surface. Add each cell as a different shape to the 3d context. Zero-cell. newCell _ SceneUtilities.NewShape[cellName]; SceneUtilities.ReadShape[newCell, "///Users/Rauen.pa/AS/TinyOctahedron.shape"]; SceneUtilities.PlaceShape[sviewer.context3d, cellName, surface.cells[cell].vertices[0]]; One-cell. 9/22/86 - check for empty shape Two-cell. 9/22/86 - check for empty shape Do the next cell. Looks to see if there is a surface in the surface sequence with the given id. If there is, returns its index in the sequence. If not, raises Error[$InvalidID]. Returns a list of all the shape names in the 3d context that match id. ChangePosition: PUBLIC PROC[newPosition: Geometry3dBasics.Triple] ~ BEGIN position _ newPosition; ThreeDBasics.SetView [ context: context3d, eyePoint: position, ptOfInterest: Geometry3dVector.Add[position, forward], upDirection: up]; viewHasChanged _ TRUE; END; ChangeOrientation: PUBLIC PROC[newForward, newUp: Geometry3dBasics.Triple] ~ BEGIN forward _ newForward; up _ newUp; ThreeDBasics.SetView [ context: context3d, eyePoint: position, ptOfInterest: Geometry3dVector.Add[position, forward], upDirection: up]; viewHasChanged _ TRUE; END; ChangeScope: PUBLIC PROC[newScope: REAL] ~ BEGIN viewHasChanged _ TRUE; END; DrawFrame: PUBLIC PROC [] ~ BEGIN IF viewHasChanged THEN MakeFrame[]; MakeFrame[]; END; Declarations. scads: REF CADTypes.ScadSequence; colors: REF ShadingModels.ShadingSequence; masks: REF CADTypes.VisibleMaskSequence; behind, right, behindAndRight, above, pointSourcePosition: Geometry3dBasics.Triple; pointSources: REF LightingModels.PointSourceSequence; lighting: LightingModels.Model; Assemble the sequences needed by the ray tracer. n: NAT _ sviewer.numberOfSurfaces; scads _ NEW[CADTypes.ScadSequence[n]]; colors _ NEW[ShadingModels.ShadingSequence[n]]; masks _ NEW[CADTypes.VisibleMaskSequence[n]]; FOR i: NAT IN [0..n) DO scads[i] _ sviewer.surfaces[i].scad; colors[i] _ [ surfaceColor: sviewer.surfaces[i].scad.color, ambientReflectionCoefficient: 1.0, diffuseReflectionCoefficient: 0.4, specularReflectionCoefficient: 0.8]; masks[i] _ sviewer.surfaces[i].mask; ENDLOOP; Assemble a lighting model. behind _ Geometry3dVector.Mul[forward, -5]; right _ Geometry3dVector.Mul[Geometry3dVector.Cross[forward, up], 2]; above _ Geometry3dVector.Mul[up, 2]; behindAndRight _ Geometry3dVector.Add[Geometry3dVector.Add[behind, right], above]; pointSourcePosition _ Geometry3dVector.Add[position, behindAndRight]; pointSources _ NEW[LightingModels.PointSourceSequence[1]]; pointSources[0] _ [position: pointSourcePosition, color: [1, 1, 1]]; lighting _ [background: [1, 1, 1], ambientSource: [0.2, 0.2, 0.2], pointSources: pointSources]; Invoke the ray tracer. SurfaceTracer.TraceCells[ surfaces: scads, variables: variables, colors: colors, masks: masks, screenCenter: position, screenU: up, screenV: Geometry3dVector.Cross[up, forward], pixelsU: pixelsU, pixelsV: pixelsV, lightingModel: lighting, filename: filename]; Private procedures Reset: PROC [] ~ BEGIN Shamelessly stolen from ThreeDDemoImpl imagerContext: Imager.Context _ ThreeDMisc.GetImagerContext[context3d]; QuickViewer.Reset[imagerContext]; ThreeDSurfaces.EnableDisplay[]; ThreeDMisc.SetViewPortFromImager[context3d, imagerContext]; ThreeDScenes.SetWindow[context3d, ThreeDScenes.WindowFromViewPort[context3d.viewPort]]; context3d.shapes _ context3d.lights; -- delete all the objects ThreeDScenes.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ]; DoReDraw: PROCEDURE [imagerCtx: Imager.Context] ~ { ReDraw[imagerCtx, NIL]; }; context3d.stopMe _ FALSE; -- get stop flag unstuck, if necessary ThreeDScenes.SetWindow[context3d,ThreeDScenes.WindowFromViewPort[context3d.viewPort]]; context3d.shapes _ context3d.lights; -- delete all the objects ThreeDScenes.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ]; QuickViewer.DrawInViewer[window3d, DoReDraw]; -- gets a new imager context END; ThreeDSurfaces.EnableDisplay[]; SetViewPort: PROC [size: Imager.Rectangle] ~ BEGIN SceneUtilities.SetViewPort[context3d, size]; -- set clippers etc. FOR i: NAT IN [0..context3d.shapes.length) DO context3d.shapes[i].vtcesInValid _ TRUE; context3d.shapes[i].shadingInValid _ TRUE; ENDLOOP; ThreeDBasics.SetView[ -- get new screen dimensions into transformations context: context3d, eyePoint: context3d.eyePoint, ptOfInterest: context3d.ptOfInterest, fieldOfView: context3d.fieldOfView, rollAngle: context3d.rollAngle, upDirection: context3d.upDirection, hitherLimit: context3d.hitherLimit, yonLimit: context3d.yonLimit ]; END; ΚO˜™J™+Icode™;J™—šΟk ˜ Kšœœ˜Kšœœ˜Kšœ œ"˜0Kšœœ˜$Kšœœ-˜CKšœœ˜Kšœ œœ˜Kšœœ7œœ˜LKšœœ™2Kšœœ ˜Kšœœœ˜%KšœœΤ˜θKšœœ™&Kšœœ ˜ Kšœ™Kšœ˜Kšœ œ œ ˜+Kšœ œ=˜NKšœ œ˜,Kšœœ˜%Kšœ œ˜)K˜—K˜šΠlnœœ˜ Kšœœ{˜”Kšœ˜Kšœ˜K˜™Kšœ=˜=Kšœ=˜=Kšœ2˜2Kšœœ˜Kšœœ Οc˜<—K˜Kš Οnœœœœœ˜&K˜š  œœœœ œ ˜\šœ$Οbœ™:Kšœœ˜K˜—™,Kšœ œ˜+Jšœœ0˜FJšœ˜Jšœ˜Jšœ:˜:—K™™!KšœK˜KJšœI˜Išœ˜Jšœ˜Jšœ˜JšœD˜DJšœ˜J˜——™*Jšœ2˜2J˜—™>Jšœ_˜_J˜—™Dšœ˜Jšœ˜J˜8Jšœœ˜ Jšœ œ˜—Idefaultšœ)œ9˜hLšœ!œ4˜[L˜—™Kšœ˜—Kšœ˜K™š   œœ œœ™GKšœg™gK™!Kšœ™K™—š  œœœœ™6Kšœ™K™——š  œœœ œœ œ˜|K˜Kšœ˜K˜—š   œœœ"œœœ˜sšœœ˜K˜—™pKšœ1œœ˜RKšœ!˜!Kšœ8˜8K˜—™ Kšœ˜Kšœ$˜$K˜—™1šœ˜Kšœ˜Kšœ ˜ Kšœœ,˜5Kšœ˜—šœœœ˜*Kšœ"œ˜'Kšœ˜—K™—™5šœœœ˜-Kšœ œ˜*šœœ˜$Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kš œœ/œœœ˜~šœ˜)K˜™ šœ˜ ˜#K˜Kšœ+˜+—Kšœ,™,KšœO™OKšœ4˜4KšœB˜BKšœX™XKšœB˜BKšœ7˜7Kšœ˜K˜——™ šœ˜ Kšœ2œM˜‚Kšœ?˜?šœœœ-˜˜>KšœG˜GKšœK˜KKšœG˜GKšœœ˜/—Kšœ!˜!Kšœ˜—J˜Kšœ˜K˜—š œœœœœ œ,œœ˜‹Kšœœ˜&Kšœœœœ!˜@šœœ˜KšœF˜FKšœ!˜!Kšœ˜—J˜Kšœ˜K˜—š   œœœ œ ˜LKšœ˜J˜Kšœ˜K˜—š   œœœ œœ œ˜bK™‘Kšœœœ˜šœœœ˜.Kšœœœ˜>Kšœ˜—Kšœœœ˜'Kšœ˜K˜—š œœœ œœ œœœ˜vK™FKš œœœœœ˜šœœœ&˜5Kšœ œ$˜3šœœ˜)Kšœ œ˜Kšœ'˜'Kšœœ œ˜1Kšœ˜—Kšœ˜—Kšœ˜Kšœ˜K˜—š   œœœœ œ˜Wšœœ˜K˜Kšœ ˜ Kšœ ˜Kšœ ˜Kšœ˜—Kšœ˜Kšœ˜K˜—Kš  œœœ˜š  œœ œœœ˜Xš  œœ ˜šœœ˜Kšœ˜Kšœ ˜—Kšœ˜—Kš œ œœœœ˜#Kšœ œ˜Kšœœœ˜'Kšœ%œœ ˜=Kšœœ$œ œ ˜UKšœ"œœ ˜:Kšœœ$œ œ ˜UKšœ3œ ˜EKšœœ$œ œ ˜UKšœ"œœ ˜:Kšœœ$œ œ ˜UKšœ7œ ˜IKšœœ$œ œ ˜UKšœ"œœ ˜:Kšœœ$œ œ ˜UKšœ7œ ˜IKšœœ$œ œ ˜UKšœ"œœ ˜:Kšœœ$œ œ ˜UKšœ7œ ˜IKšœ˜K˜K˜—š œœœ)™IK™™K™K™K™6K™—Kšœœ™Kšœ™K™—š œœœ/™RK™K™ ™K™K™K™6K™—Kšœœ™Kšœ™K™—š   œœœ œ™0Kšœœ™Kšœ™K™—š  œœœ™!Kšœœ ™#K™ Kšœ™K™—š  œœœ1œœ œ ˜›K™™ Kšœœ™!Kšœœ™*Kšœœ™(KšœS™SKšœœ$™5Kšœ™K™—™0Kšœœ™"Kšœœ™&Kšœ œ#™/Kšœœ"™-šœœœ™Kšœ$™$™ Kšœ-™-Kšœ"™"Kšœ"™"Kšœ$™$—Kšœ$™$Kšœ™K™——™K™+K™EK™$K™RK™EKšœœ(™:K™Dšœ_™_K™——™™Kšœ™Kšœ™Kšœ™Kšœ ™ Kšœ™K™ K™-Kšœ™Kšœ™Kšœ™Kšœ™—K™—Kšœ˜˜K˜—K˜K˜—š ™K˜š œœ™K™&K™JšœG™GJšœ!™!Jšœ™Jšœ;™;JšœW™WJšœ'Ÿ™@JšœF™FJ™Jš œ œ4œ™PJšœœŸ&™FJšœV™VJšœ'Ÿ™@JšœF™FJšœ0Ÿ™LJ™Jšœ™J™—š   œœœ œ ˜IJšœ™šœŸ1˜LJšœ˜Jšœ&˜&Jšœ-˜-Jšœ+˜+Jšœ'˜'Jšœ+˜+Jšœ+˜+Jšœ%˜%Jšœ˜—šœ˜Jšœ˜J˜Jšœ&˜&J˜J˜—Jšœ,˜,Jšœ˜K˜—š  œœ™2Jšœ/Ÿ™Cšœœœ™-Jšœ#œ™(Jšœ%œ™*Jšœ™—šœŸ1™LJšœ™Jšœ™Jšœ%™%Jšœ#™#Jšœ™Jšœ#™#Jšœ#™#Jšœ™Jšœ™—Jšœ™K™———Kšœ˜—…—/BV‘