DIRECTORY Atom USING [PutPropOnList], Basics USING [Comparison], CADTypes USING [Scad, ScadSequence, VariableRec, VisibleMask, VisibleMaskSequence], Commander USING [CommandProc, Register], Convert USING [CardFromRope, Error], Geometry3dBasics USING [Triple, TripleSequence, TripleSequenceRep], Geometry3dVector USING [Add, Cross, Mul], Imager USING [Rectangle], ImagerColor USING [RGB], IO USING [BreakProc, Error, GetTokenRope, int, PutFR, RIS, STREAM], LightingModels USING [Model, PointSourceSequence], Rope USING [Compare, ROPE], SceneUtilities USING [AddShape, AddShapeAt, CreateDefaultContext, DeleteShape, Hide, NameBackgroundColor, PlaceShape, Reveal, SetColor, SetFaceted, SetLight, SetViewPort, ShapeInstance], ShadingModels USING [ShadingSequence], SurfaceRender USING [MakeFrame, SetView], SurfaceTracer, SurfaceViewer USING [], ThreeDBasics USING [Context, Create, SetView], ThreeDHacks USING [RegisterNewClasses, MakeFatPoint, MakeFatSeg, MakeTwoCell], ThreeDViewer USING [ButtonDesc, MakeViewer], ViewerClasses USING [Column, Viewer], ViewerOps USING [ChangeColumn, OpenIcon]; SurfaceViewerImpl: CEDAR PROGRAM IMPORTS Atom, Commander, Convert, IO, Rope, SurfaceRender, SurfaceTracer, ThreeDBasics, ThreeDHacks, ThreeDViewer, SceneUtilities, Geometry3dVector, ViewerOps EXPORTS SurfaceViewer ~ BEGIN SurfaceRec: TYPE ~ RECORD [ scad: CADTypes.Scad, id: NAT, mask: REF CADTypes.VisibleMask]; SurfaceSeq: TYPE ~ RECORD [ surfaces: SEQUENCE length: NAT OF SurfaceRec]; context3d: REF ThreeDBasics.Context; noReDraw: BOOLEAN; position: Geometry3dBasics.Triple; forward: Geometry3dBasics.Triple; up: Geometry3dBasics.Triple; viewHasChanged: BOOLEAN; surfaces: REF SurfaceSeq; numberOfSurfaces: NAT; maxNumberOfSurfaces: NAT ~ 3; nextID: NAT; Error: PUBLIC ERROR[why: ATOM] = CODE; CreateSurfaceViewer: PUBLIC PROC[] ~ BEGIN workingDirectory: Rope.ROPE; menuButtons: LIST OF ThreeDViewer.ButtonDesc ~ LIST [ [ proc: LoadPicture, choices: LIST[ [$Default, "Default Picture"], [NIL, NIL], [NIL, NIL], [NIL, NIL], [NIL, NIL], [NIL, NIL], [NIL, NIL], [NIL, NIL], [NIL, NIL]], label: "Load Picture", purpose: "Load a default picture" ] ]; noReDraw _ TRUE; surfaces _ NEW[SurfaceSeq[maxNumberOfSurfaces]]; numberOfSurfaces _ 0; nextID _ 0; position _ [2.0, -10.0, 3.0]; forward _ [-2, 10, -3]; up _ [0., 0., 1.]; context3d _ SceneUtilities.CreateDefaultContext[]; SceneUtilities.NameBackgroundColor[context3d, "Darkish Blue"]; SceneUtilities.SetLight [context3d, "Initial", position]; ThreeDBasics.SetView [ context: context3d, eyePoint: position, ptOfInterest: Geometry3dVector.Add[position, forward], upDirection: up]; -- ThreeDHacks.RegisterNewClasses[context3d]; context3d.props _ Atom.PutPropOnList[context3d.props, $WDir, workingDirectory]; ThreeDViewer.MakeViewer[ context: context3d, bannerName: "New and Improved Algebraic Surface Viewer", menu: menuButtons, mouseAction: NIL]; IF context3d.viewer.column # color THEN ViewerOps.ChangeColumn[context3d.viewer, color]; IF context3d.viewer.iconic THEN ViewerOps.OpenIcon[icon: context3d.viewer]; noReDraw _ FALSE; viewHasChanged _ TRUE; SurfaceRender.MakeFrame[context3d]; END; ShutDown: PROC [] ~ BEGIN END; 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; LoadSurface: PUBLIC PROC[surface: CADTypes.Scad] RETURNS[id: NAT] ~ BEGIN index, newID: NAT; IF numberOfSurfaces >= maxNumberOfSurfaces THEN ERROR Error[$AllFilledUp]; index _ numberOfSurfaces; numberOfSurfaces _ numberOfSurfaces + 1; newID _ nextID; nextID _ nextID + 1; surfaces[index] _ [ scad: surface, id: newID, mask: NEW[CADTypes.VisibleMask[surface.cells.nCells]] ]; FOR i: NAT IN [0..surface.cells.nCells) DO 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]; SELECT surface.cells[cell].dimension FROM 0 => BEGIN newCell _ ThreeDHacks.MakeFatPoint[ name: cellName, position: surface.cells[cell].vertices[0]]; SceneUtilities.PlaceShape[context3d, cellName, [0, 0, 0]]; SceneUtilities.AddShape[context3d, newCell]; SceneUtilities.SetColor[context3d, cellName, [0.1, 0.9, 0.9]]; SceneUtilities.SetFaceted[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.PlaceShape[context3d, cellName, [0, 0, 0]]; SceneUtilities.AddShape[context3d, newCell]; SceneUtilities.SetColor[context3d, cellName, [0.1, 0.9, 0.9]]; SceneUtilities.SetFaceted[context3d, cellName]; IF surface.cells[cell].vertices.nVertices < 2 THEN SceneUtilities.Hide[context3d, cellName]; END; 2 => BEGIN newCell _ ThreeDHacks.MakeTwoCell[ name: cellName, vertices: surface.cells[cell].vertices, triangles: surface.cells[cell].polygons]; SceneUtilities.PlaceShape[context3d, cellName, [0, 0, 0]]; SceneUtilities.AddShape[context3d, newCell]; SceneUtilities.SetColor[context3d, cellName, [0.1, 0.9, 0.9]]; SceneUtilities.SetFaceted[context3d, cellName]; IF surface.cells[cell].polygons.nTriangles = 0 THEN SceneUtilities.Hide[context3d, cellName]; END; ENDCASE; ENDLOOP; viewHasChanged _ TRUE; MakeFrame[]; RETURN[newID]; END; MaskSurface: PUBLIC PROC[id: NAT, mask: REF CADTypes.VisibleMask] ~ BEGIN index: NAT _ IndexFromID[id]; --verify ID FOR i: NAT IN [0..surfaces[index].scad.cells.nCells) DO nameToMask: Rope.ROPE _ MakeShapeName[ id, surfaces[index].scad.cells[i].indexX, surfaces[index].scad.cells[i].indexY, surfaces[index].scad.cells[i].indexZ ]; IF mask[i] THEN SceneUtilities.Reveal[context3d, nameToMask] ELSE SceneUtilities.Hide[context3d, nameToMask]; ENDLOOP; surfaces[index].mask _ mask; viewHasChanged _ TRUE; MakeFrame[]; END; HideSurface: PUBLIC PROC[id: NAT] ~ BEGIN index: NAT _ IndexFromID[id]; --verify ID namesToHide: LIST OF Rope.ROPE _ ShapesMatchingID[id]; UNTIL namesToHide = NIL DO SceneUtilities.Hide[context3d, namesToHide.first]; namesToHide _ namesToHide.rest; ENDLOOP; viewHasChanged _ TRUE; MakeFrame[]; END; UnHideSurface: PUBLIC PROC[id: NAT] ~ BEGIN index: NAT _ IndexFromID[id]; --verify ID namesToHide: LIST OF Rope.ROPE _ ShapesMatchingID[id]; UNTIL namesToHide = NIL DO SceneUtilities.Reveal[context3d, namesToHide.first]; namesToHide _ namesToHide.rest; ENDLOOP; viewHasChanged _ TRUE; MakeFrame[]; END; DeleteSurface: PUBLIC PROC[id: NAT] ~ BEGIN index: NAT _ IndexFromID[id]; namesToDelete: LIST OF Rope.ROPE _ ShapesMatchingID[id]; FOR i: NAT IN [index..numberOfSurfaces - 1) DO surfaces[i] _ surfaces[i + 1]; ENDLOOP; numberOfSurfaces _ numberOfSurfaces - 1; UNTIL namesToDelete = NIL DO SceneUtilities.DeleteShape[context3d, namesToDelete.first]; namesToDelete _ namesToDelete.rest; ENDLOOP; viewHasChanged _ TRUE; MakeFrame[]; END; FlushSurfaces: PUBLIC PROC[] ~ BEGIN numberOfSurfaces _ 0; viewHasChanged _ TRUE; MakeFrame[]; END; IndexFromID: PROC [id: NAT] RETURNS [index: NAT] ~ BEGIN found: BOOLEAN _ FALSE; FOR i: NAT IN [0..numberOfSurfaces) DO IF surfaces[i].id = id THEN {index _ i; found _ TRUE}; ENDLOOP; IF ~found THEN ERROR Error[$InvalidID]; END; ShapesMatchingID: PROC [id: NAT] RETURNS [shapes: LIST OF Rope.ROPE] ~ BEGIN names: LIST OF Rope.ROPE _ NIL; FOR i: NAT IN [0..context3d.shapes.length) DO name: Rope.ROPE _ context3d.shapes[i].name; IF Rope.Compare[name, "Initial"] # equal AND Rope.Compare[name, "Torch"] # equal 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 [ "S%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: 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; ChangePosition: PUBLIC PROC[newPosition: Geometry3dBasics.Triple] ~ BEGIN position _ newPosition; SurfaceRender.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; SurfaceRender.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[]; END; InvokeRayTracer: PUBLIC PROC[variables: CADTypes.VariableRec, filename: Rope.ROPE, pixelsU, pixelsV: NAT] ~ BEGIN 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; n: NAT _ 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] _ surfaces[i].scad; colors[i] _ [ surfaceColor: surfaces[i].scad.color, ambientReflectionCoefficient: 1.0, diffuseReflectionCoefficient: 0.4, specularReflectionCoefficient: 0.8]; masks[i] _ surfaces[i].mask; ENDLOOP; 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]; 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]; END; MakeFrame: PROC [] ~ BEGIN SurfaceRender.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 ]; SceneUtilities.SetLight[ context: context3d, name: "Torch", position: context3d.eyePoint, color: [1, 1, 1] ]; SurfaceRender.MakeFrame[context3d]; viewHasChanged _ FALSE; END; 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; SurfaceRender.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; Foo: Commander.CommandProc ~ BEGIN CreateSurfaceViewer[]; END; Commander.Register["MakeSurfaceViewer", Foo]; END. 4SurfaceViewerImpl.mesa James Rauen, August 26, 1986 2:14:07 am PDT Last edited by: James Rauen January 13, 1988 6:17:51 pm PST Type declarations Global variables window3d: REF QuickViewer.QuickView; Note: This has some resemblance to Init in ThreeDDemoImpl Declarations Menu Buttons Don't want ReDraw to do anything during the intitialization sequence. Initialize the global variables. Initialize the context's graphics Register the $FatPoint and $FatSeg classes Store away the working directory (motivated by ThreeDDemoImpl) Create the viewer and move it to the color display Draw it Private procedures for CreateSurfaceViewer ReDraw: PROC [imagerCtx: Imager.Context, toDo: REF ANY] ~ BEGIN Declarations imagerContext: Imager.Context _ imagerCtx; -- 9/12/86 - arg name change in new QV newContext: REF ThreeDScenes.Context; oldContext: Imager.Context; Don't do anything during Init sequence. IF noReDraw THEN RETURN; ReDraw according to the new parameters. oldContext _ ThreeDMisc.GetImagerContext[context3d]; IF oldContext # NIL THEN imagerContext _ oldContext; newContext _ ThreeDScenes.GetFromImagerContext[imagerContext, context3d.alphaBuffer, context3d.depthBuffer]; context3d.display _ newContext.display; context3d.alphaBuffer _ newContext.alphaBuffer; context3d.depthBuffer _ newContext.depthBuffer; context3d.renderMode _ newContext.renderMode; context3d.viewPort _ newContext.viewPort; SetViewPort[context3d.viewPort]; ThreeDScenes.SetWindow[context3d, ThreeDScenes.WindowFromViewPort[context3d.viewPort]]; ENABLE UNWIND => NULL; ThreeDScenes.DisplayFromImagerContext[context3d, imagerCtx]; IF context3d.depthBuffer THEN ThreeDScenes.AddDepthBuffer[context3d]; IF context3d.alphaBuffer THEN ThreeDScenes.AddAlphaBuffer[context3d]; viewHasChanged _ TRUE; MakeFrame[]; END; Declarations 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. ThreeDScenes.PlaceShape[newCell, [0, 0, 0]]; context3d.shapes _ ThreeDScenes.AddShape[context3d.shapes, newCell]; ThreeDMisc.SetFacetedColor[context3d, cellName, [0.1, 0.9, 0.9]]; One-cell. ThreeDScenes.PlaceShape[newCell, [0, 0, 0]]; context3d.shapes _ ThreeDScenes.AddShape[context3d.shapes, newCell]; ThreeDMisc.SetFacetedColor[context3d, cellName, [0.1, 0.9, 0.9]]; 9/22/86 - check for empty shape Two-cell. ThreeDScenes.PlaceShape[newCell, [0, 0, 0]]; context3d.shapes _ ThreeDScenes.AddShape[context3d.shapes, newCell]; ThreeDMisc.SetFacetedColor[context3d, cellName, [0.1, 0.9, 0.9]]; 9/22/86 - check for empty shape Do the next cell. context3d.shapes _ SceneUtilities.DeleteShape[context3d, namesToDelete.first]; 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. Declarations. Assemble the sequences needed by the ray tracer. Assemble a lighting model. Invoke the ray tracer. 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[]; Κβ˜™J™+Icode™;J™—šΟk ˜ Kšœœ˜Kšœœ˜Kšœ œE˜SKšœ œ˜(Kšœœ˜$Kšœœ-˜CKšœœ˜)Kšœœ ˜Kšœ œœ˜Kšœœ.œœ˜CKšœœ˜2Kšœœ œ˜Kšœœ§˜»Kšœœ˜&Kšœœ˜)Kšœ˜Kšœœ˜Kšœ œ˜.Kšœ œ=˜NKšœ œ˜,Kšœœ˜%Kšœ œ˜)K˜—K˜šΠlnœœ˜ Kšœœ{˜ŸKšœ˜Kšœ˜K˜™šœ œœ˜K˜Kšœœ˜Kšœœ˜ —šœ œœ˜Kšœ œ œœ ˜.—K˜—™Kšœ œ˜$Jšœ œ™$Kšœ œ˜Kšœ"˜"Kšœ!˜!Kšœ˜Kšœœ˜Kšœ œ ˜Kšœœ˜Kšœœ˜Kšœœ˜ —K˜Kš Οnœœœœœ˜&K˜šŸœœœ˜*K˜šœ$Οbœ™:K™—™ Kšœœ˜K˜—™ šœ œœœ˜5J˜˜J˜8J˜%J˜%J˜J˜!J˜—J˜——J™™EKšœ œ˜K˜—™ Jšœ œ"˜0Jšœ˜Jšœ ˜ Kšœ˜K˜K˜—K™™!Jšœ2˜2Kšœ>˜>Jšœ:˜:šœ˜Jšœ˜Jšœ˜Jšœ6˜6Jšœ˜J˜——™*Jšœ-˜-J˜—™>JšœO˜OJ˜—™2šœ˜J˜J˜8Jšœ˜Jšœ œ˜—Idefaultšœ!œ1˜XLšœœ,˜KL˜—™Jšœ œ˜Jšœœ˜Kšœ#˜#—Kšœ˜K™KšŸ*™*K™š Ÿœœ#œœ™?K™™ Kšœ+Οc'™RKšœ œ™%K™—K™™'Kšœ œœ™K™—šœ'™'K™4Kšœœœ™4Kšœl™lJšœ'™'Jšœ/™/Jšœ/™/Jšœ-™-Jšœ)™)Jšœ ™ JšœW™WJ™Jšœœœ™Jšœ<™K˜/K™AKšœ˜K˜——™ šœ˜ Kšœ2œM˜‚Kšœ?˜?šœœœ-˜K˜/K™AK˜K™šœ,˜2Kšœ)˜)—K˜Kšœ˜K˜——™ šœ˜ ˜"K˜Kšœ'˜'Kšœ)˜)—K˜:K™,K˜,KšœD™DK˜>K˜/K™AK˜K™šœ-˜3Kšœ)˜)—K˜Kšœ˜K˜—Kšœ˜K˜———™Kšœ˜K˜——Kšœœ˜K˜ Kšœ˜Kšœ˜K™—š Ÿ œœœœœ˜IKšœœ‘ ˜*šœœœ(˜7šœœ˜&K˜Kšœ%˜%Kšœ%˜%Kšœ$˜$Kšœ˜—Kšœ œ-˜