DIRECTORY CedarProcess, Controls, Draw2d, FileNames, G3dBasic, G3dMatrix, G3dRender, G3dScene, G3dShape, G3dTimeTrees, G3dTool, ImagerSample, IO, MessageWindow, Process, Real, Rope, TiogaMenuOps, TiogaOps, ViewerOps, ViewerTools; G3dToolSceneImpl: CEDAR PROGRAM IMPORTS CedarProcess, Controls, Draw2d, FileNames, G3dMatrix, G3dScene, G3dTimeTrees, G3dTool, ImagerSample, IO, MessageWindow, Process, Real, Rope, TiogaMenuOps, TiogaOps, ViewerOps, ViewerTools EXPORTS G3dTool ~ BEGIN ROPE: TYPE ~ Rope.ROPE; ForkableProc: TYPE ~ CedarProcess.ForkableProc; ClickProc: TYPE ~ Controls.ClickProc; Viewer: TYPE ~ Controls.Viewer; Triple: TYPE ~ G3dBasic.Triple; RealSequence: TYPE ~ G3dBasic.RealSequence; Matrix: TYPE ~ G3dMatrix.Matrix; SourceText: TYPE ~ G3dScene.SourceText; Animation: TYPE ~ G3dTool.Animation; Animations: TYPE ~ G3dTool.Animations; Tool: TYPE ~ G3dTool.Tool; Shape: TYPE ~ G3dShape.Shape; ShapeProc: TYPE ~ G3dShape.ShapeProc; TimeTree: TYPE ~ G3dTimeTrees.TimeTree; TTNode: TYPE ~ G3dTimeTrees.Node; SampleMap: TYPE ~ ImagerSample.SampleMap; ShowFile: PROC [t: Tool] ~ { FindViewer: PROC [name: ROPE] RETURNS [viewer: Viewer] = { MatchName: PROC [v: Viewer] RETURNS [continue: BOOL ฌ TRUE] ~ { continue ฌ NOT Rope.Equal[name, FileNames.ConvertToSlashFormat[v.name], FALSE]; IF continue THEN viewer ฌ v; }; name ฌ FileNames.ConvertToSlashFormat[name]; ViewerOps.EnumerateViewers[MatchName]; }; node: TTNode ฌ NARROW[G3dTimeTrees.GetActiveNode[t.timeTree]]; st: SourceText ฌ NARROW[node.clientData]; viewer: Viewer ฌ FindViewer[st.fileName]; selection: ViewerTools.SelPos ฌ NEW[ViewerTools.SelPosRec ฌ [start: st.position, length: 1]]; IF viewer = NIL THEN { viewer ฌ ViewerOps.CreateViewer[ flavor: $Text, paint: FALSE, info: [iconic: TRUE, column: right, openHeight: 140]]; TiogaMenuOps.Load[viewer: viewer, fileName: st.fileName]; }; ViewerOps.SetOpenHeight[viewer: viewer, clientHeight: 140]; IF ViewerOps.ViewerColumn[viewer]#right THEN ViewerOps.ChangeColumn[viewer, right]; ViewerOps.OpenIcon[icon: viewer, bottom: FALSE, paint: FALSE]; -- must Open before Bottom ViewerOps.BottomViewer[viewer: viewer, paint: FALSE]; ViewerOps.ComputeColumn[right]; ViewerTools.SetSelection[viewer, selection]; TiogaOps.GrowSelection[]; -- select the entire word }; SelectFromFile: PROC [t: Tool] ~ { FindNode: PROC RETURNS [found: TTNode ฌ NIL] ~ { Search: PROC [node: TTNode] ~ { IF node # NIL AND node.clientData # NIL THEN { st: SourceText ฌ NARROW[node.clientData]; IF Rope.Equal[st.fileName, viewerName] AND ABS[st.position - selPos.start] < 2 THEN { found ฌ node; RETURN; }; IF node.children # NIL THEN FOR i: INT IN [0..node.children.length) DO IF found = NIL THEN Search[node.children[i]]; ENDLOOP; }; }; viewerName: ROPE ฌ FileNames.ConvertToSlashFormat[viewer.name]; Search[t.timeTree.root]; }; node: TTNode; viewer: Viewer; save, selPos: ViewerTools.SelPos; level: TiogaOps.SelectionGrain; [viewer: viewer, level: level] ฌ TiogaOps.GetSelection[]; save ฌ ViewerTools.GetSelection[viewer]; WHILE level < word DO TiogaOps.GrowSelection[]; [viewer: viewer, level: level] ฌ TiogaOps.GetSelection[]; ENDLOOP; selPos ฌ ViewerTools.GetSelection[viewer]; ViewerTools.SetSelection[viewer, save]; node ฌ FindNode[]; IF node = NIL THEN Controls.TypescriptWrite[t.typescript, "cannot find that node in this tree\n"] ELSE { s: Shape ฌ NARROW[node.object]; Controls.TypescriptWrite[t.typescript, "setting that node to selected\n"]; G3dTimeTrees.SetActiveNode[t.timeTree, node]; G3dTool.SelectShape[t, s, middle]; }; }; TreeMove: PROC [t: Tool, dir: {up, down, left, right}] ~ { Child: PROC [n: TTNode] RETURNS [c: TTNode] ~ { GetNodeSequenceNumber: PROC [node: TTNode] RETURNS [INT] ~ { p: TTNode ฌ node.parent; IF p = NIL THEN RETURN[-1]; -- root is not a child of anything FOR i: INT IN [0..p.children.length) DO IF p.children[i] = node THEN RETURN[i]; ENDLOOP; RETURN[-2]; -- error }; i: INT ฌ GetNodeSequenceNumber[n]; IF i < 0 THEN RETURN[NIL]; c ฌ n.parent.children[(IF dir = left THEN i-1 ELSE i+1) MOD n.parent.children.length]; }; newNode: TTNode ฌ SELECT dir FROM up => t.focusNode.parent, down => IF t.focusNode.children # NIL AND t.focusNode.children.length > 0 THEN t.focusNode.children[0] ELSE NIL, left, right => Child[t.focusNode], ENDCASE => NIL; IF newNode = NIL THEN Controls.TypescriptWrite[t.typescript, "no node available!\n"] ELSE t.focusNode ฌ newNode; }; TransformAndPropagateNode: PUBLIC PROC [ node: TTNode, matrix: Matrix, order: G3dMatrix.TransformOrder] ~ { G3dTimeTrees.TransformNode[node, matrix, order]; G3dTool.PropagateShapeMatrices[node]; }; SceneOpsButton: PUBLIC ClickProc ~ { t: Tool ฌ NARROW[clientData]; oldFocus: TTNode ฌ t.focusNode; choice: INT ฌ Controls.PopUpRequest[["Scene Ops"], LIST[ -- 1 -- ["Write Parameters", "All settings in RIB format to a viewer"], -- 2 -- ["Write Camera Only", "Camera settings in RIB format to a viewer"], -- 3 -- ["Up", "move selected node up one branch"], -- 4 -- ["Down", "move selected node down to first child"], -- 5 -- ["Left", "move selected node left to sibling"], -- 6 -- ["Right", "move selected node right to sibling"], -- 7 -- ["Root", "move selected node to root"], -- 8 -- ["Show File", "show source file for focusNode"], -- 9 -- ["From File", "select focusNode from selected text"], -- 10 -- ["Read Parameters", "Set Parameters from a Viewer"] ]]; SELECT choice FROM 1 => WriteParameters[t, FALSE]; 2 => WriteParameters[t, TRUE]; 3 => TreeMove[t, up]; 4 => TreeMove[t, down]; 5 => TreeMove[t, left]; 6 => TreeMove[t, right]; 7 => t.focusNode ฌ t.timeTree.root; 8 => ShowFile[t]; 9 => SelectFromFile[t]; 10 => ReadParameters[t]; ENDCASE ; IF oldFocus # t.focusNode THEN { G3dTool.SelectNodeShapes[t.focusNode]; G3dTool.Repaint[t]; }; }; WriteParameters: PROC [t: Tool, cameraOnly: BOOL] ~ { name: ROPE ฌ Controls.TypescriptReadFileName[t.typescript]; IF name = NIL THEN RETURN; G3dTool.SetRenderView[t.context3d, t.camera]; IF cameraOnly THEN G3dScene.WriteCameraParameters[t.context3d] ELSE G3dScene.WriteParameters[t.context3d, name]; }; ReadParameters: PROC [t: Tool] ~ { [] ฌ G3dScene.ReadParameters[t.context3d]; G3dTool.UpdateCamera[t]; IF Controls.GetPopUpButton[] = right THEN G3dTool.Repaint[t, $Scene]; }; AnimateOpsButton: PUBLIC ClickProc ~ { t: Tool ฌ NARROW[clientData]; choice: INT ฌ Controls.PopUpRequest[["Animation Ops"], LIST[ -- 1 -- [IO.PutFR1["Set # Frames (now %g)", IO.int[t.nFrames]], "# frames in animation"], -- 2 -- [IO.PutFR["Set Times (now %g to %g)", IO.real[t.time0], IO.real[t.time1]], "times"], -- 3 -- [IO.PutFR1["Set frame (now %g)", IO.int[t.frame]], "set current frame"], -- 4 -- [IO.PutFR1["Set time (now: %g)", IO.real[t.time]], "set current time"], -- 5 -- ["ANIMATE", "Show a sequence of frames"], -- 6 -- ["RENDER Frames", "Render a sequence of frames"], -- 7 -- [Rope.Cat["Ouptut IP (now ", t.animateIPName, ")"], "Write IP out per frame"], -- 8 -- ["Store Animation", "Store previously created animation"], -- 9 -- ["Restore Animation", "Restore previously stored animation"], -- 10 -- [IO.PutFR1["Playback pause (now: %g)", [real[t.playbackPause]]], "frame wait"], -- 11 -- ["Playback", "Replay animated frames"], -- 12 -- ["Cycle", "continuous replay"], -- 13 -- ["Shuttle", "replay back and forth"], -- 14 -- ["Shuttle Ease In/Out", "replay back and forth"], -- 15 -- ["--", ""], -- 16 -- ["Insert Key", "insert key at this time"] ]]; G3dTool.SetCursor[t, TRUE]; SELECT choice FROM 1 => t.nFrames ฌ Controls.GetNat[t.typescript, "# frames", t.nFrames]; 2 => SetTimes[t]; 3 => SetCurrentFrame[t, Controls.GetNat[t.typescript, "current frame", t.frame]]; 4 => SetCurrentTime[t, Controls.GetReal[t.typescript, "current time", t.time]]; 5 => [] ฌ G3dTool.MaybeFork[t, ForkAnimation]; 6 => Blink["unimplemented"]; 7 => t.animateIPName ฌ Controls.TypescriptReadFileName[t.typescript]; 8 => StoreAnimation[t]; 9 => RestoreAnimation[t]; 10 => t.playbackPause ฌ Controls.GetReal[t.typescript, "playback pause (secs)", t.playbackPause]; 11 => [] ฌ G3dTool.MaybeFork[t, ForkPlayback]; 12 => [] ฌ G3dTool.MaybeFork[t, ForkCycle]; 13 => [] ฌ G3dTool.MaybeFork[t, ForkShuttle]; 14 => [] ฌ G3dTool.MaybeFork[t, ForkShuttleEasy]; 16 => InsertKey[t]; ENDCASE; G3dTool.SetCursor[t, FALSE]; }; InsertKey: PROC [t: Tool] ~ { DoTransform: G3dTimeTrees.TTNodeActionProc ~ { IF node # NIL AND G3dTimeTrees.GetTransformDirty[node] THEN G3dTimeTrees.InsertTransform[t.timeTree, t.time, G3dMatrix.CopyMatrix[node.localTransform]]; }; G3dTimeTrees.EnumerateNodes[t.timeTree, DoTransform]; }; SetTimes: PROC [t: Tool] ~ { times: RealSequence ฌ Controls.TypescriptReadValues[ t.typescript, "times", LIST[["time0", t.time0], ["time1", t.time1]]]; IF times # NIL THEN {t.time0 ฌ times[0]; t.time1 ฌ times[1]}; }; SetCurrentFrame: PROC [t: Tool, frame: NAT] ~ { IF frame >= t.nFrames THEN Blink[IO.PutFR1["frame must be < %g", IO.int[t.nFrames]]]; IF frame = t.frame OR frame >= t.nFrames THEN RETURN; t.frame ฌ frame; t.time ฌ t.time0+(REAL[t.frame]/REAL[MAX[1, t.nFrames-1]])*(t.time1-t.time0); [] ฌ G3dTool.MaybeFork[t, ForkFrame]; }; SetCurrentTime: PROC [t: Tool, time: REAL] ~ { IF time # t.time THEN { t.time ฌ time; IF t.time1 # t.time0 THEN t.frame ฌ Real.Round[REAL[t.nFrames]*t.time/(t.time1-t.time0)]; [] ฌ G3dTool.MaybeFork[t, ForkFrame]; }; }; ForkFrame: CedarProcess.ForkableProc ~ {DoFrame[NARROW[data, Tool]]}; ForkAnimation: CedarProcess.ForkableProc ~ { t: Tool ฌ NARROW[data]; view: G3dTool.View ฌ G3dTool.GetView[NIL, t]; IF t.playback # NIL THEN t.playback.length ฌ 0; FOR i: INT IN [0..t.nFrames) DO map: SampleMap; CedarProcess.CheckAbort[]; AnimateFrame[t, i]; map ฌ G3dTool.GetBufferedSampleMap[view]; IF map # NIL THEN t.playback ฌ AddToAnimation[ImagerSample.Copy[map], t.playback]; REPEAT FINISHED => G3dTool.Finish[t, "animation done"]; ENDLOOP; }; AnimateFrame: PROC [t: Tool, frame: NAT] ~ { Draw: Controls.DrawProc ~ { v: G3dTool.View ฌ G3dTool.GetView[viewer, t]; G3dTool.Draw[context, viewer, NIL, v.camera.matrix, v.viewport, v, t, t]; }; alpha: REAL ฌ REAL[t.frame ฌ frame]/REAL[MAX[1, t.nFrames-1]]; t.time ฌ t.time0+alpha*(t.time1-t.time0); MessageWindow.Append[IO.PutFR["frame %g (time:%g)", IO.int[frame], IO.real[t.time]], TRUE]; DoFrame[t]; IF t.animateIPName # NIL THEN Draw2d.IPOut[ IO.PutFR["%g.%g", IO.rope[t.animateIPName], IO.int[frame]], Draw, t]; }; DoFrame: PROC [t: Tool, repaint: BOOL ฌ TRUE, render: BOOL ฌ FALSE] ~ { context3d: G3dRender.Context3d ฌ t.context3d; IF t.client.animate # NIL THEN { alpha: REAL ฌ REAL[t.frame]/REAL[MAX[1, t.nFrames-1]]; t.client.animate[t.frame, alpha, t.time, t.client.data]; }; IF t.shapes # NIL THEN { tt: TimeTree ฌ G3dTimeTrees.GetRoot[NARROW[t.shapes[0].hierarchyData]]; G3dTimeTrees.StrobeTimeTree[tt, t.time]; G3dTimeTrees.ReportTimeTree[t.timeTree]; }; IF repaint THEN G3dTool.Repaint[t, $Scene]; IF render THEN G3dTool.Context3dOk[t]; }; AddToAnimation: PROC [m: SampleMap, a: Animation] RETURNS [ret: Animation] ~ { IF (ret ฌ a) = NIL THEN ret ฌ NEW[G3dTool.AnimationRep[10]]; IF ret.length = ret.maxLength THEN { old: Animation ฌ ret; ret ฌ NEW[G3dTool.AnimationRep[2*old.length]]; FOR n: NAT IN [0..ret.length ฌ old.length) DO ret[n] ฌ old[n]; ENDLOOP; }; ret[(ret.length ฌ ret.length+1)-1] ฌ m; }; StoreAnimation: PROC [t: Tool] ~ { AddToAnimations: PROC [a: Animation, anims: Animations] RETURNS [ret: Animations] ~ { IF (ret ฌ anims) = NIL THEN ret ฌ NEW[G3dTool.AnimationsRep[10]]; FOR n: NAT IN [0..ret.length) DO IF Rope.Equal[ret[n].name, a.name, FALSE] THEN {ret[n] ฌ a; RETURN}; ENDLOOP; IF ret.length = ret.maxLength THEN { old: Animations ฌ ret; ret ฌ NEW[G3dTool.AnimationsRep[2*old.length]]; FOR n: NAT IN [0..ret.length ฌ old.length) DO ret[n] ฌ old[n]; ENDLOOP; }; ret[(ret.length ฌ ret.length+1)-1] ฌ a; }; IF (t.playback.name ฌ Controls.TypescriptRead[t.typescript, "animation name: "]) # NIL THEN t.animations ฌ AddToAnimations[t.playback, t.animations]; }; RestoreAnimation: PROC [t: Tool] ~ { IF t.animations = NIL OR t.animations.length = 0 THEN G3dTool.TSWrite[t, "no saved frames"] ELSE { c: INTEGER; r, l: LIST OF Controls.Request; FOR i: NAT IN [0..t.animations.length) DO l ฌ CONS[[t.animations[i].name], l]; ENDLOOP; FOR s: LIST OF Controls.Request ฌ l, s.rest WHILE s#NIL DO r ฌ CONS[s.first, r]; ENDLOOP; IF (c ฌ Controls.PopUpRequest[["Restore"], r]) > 0 THEN t.playback ฌ t.animations[c-1]; }; }; ForkPlayback: ForkableProc ~ {Playback[NARROW[data], playback]}; ForkCycle: ForkableProc ~ {Playback[NARROW[data], cycle]}; ForkShuttle: ForkableProc ~ {Playback[NARROW[data], shuttle]}; ForkShuttleEasy: ForkableProc ~ {Playback[NARROW[data], shuttleEasy]}; playbackFactor: REAL ฌ 250.0; Playback: PROC [t: Tool, mode: {playback, cycle, shuttle, shuttleEasy}] ~ { Once: PROC [op: {forward, backward}] ~ { FOR n: NAT IN [0..t.playback.length) DO map: SampleMap ฌ t.playback[IF op = forward THEN n ELSE t.playback.length-n-1]; CedarProcess.CheckAbort[]; G3dTool.SetBufferedSampleMap[map, view]; G3dTool.Repaint[t, $Restore]; Process.Pause[Process.MsecToTicks[Real.Round[playbackFactor*(IF mode = shuttleEasy THEN ABS[0.5-REAL[n]/REAL[t.playback.length-1]] ELSE t.playbackPause)]]]; ENDLOOP; }; view: G3dTool.View ฌ G3dTool.GetView[NIL, t]; IF t.playback # NIL THEN SELECT mode FROM playback => Once[forward]; cycle => DO Once[forward]; ENDLOOP; ENDCASE => DO Once[forward]; Once[backward]; ENDLOOP; }; Blink: PROC [r: ROPE] ~ { MessageWindow.Append[Rope.Concat["\t\t", r], TRUE]; MessageWindow.Blink[]; }; END... DumpTimeTree: PROC [tt: TimeTree] ~ { DumpNode: PROC [prefix: ROPE, node: TTNode] ~ { ShowMatrix: PROC [name: ROPE, m: Matrix] ~ { TerminalIO.PutRope[IO.PutFR["%g%g matrix:\n", IO.rope[prefix], IO.rope[name]]]; IF m = NIL THEN { TerminalIO.PutRope[IO.PutFR["%g -- NIL --\n", IO.rope[prefix]]]; RETURN; }; FOR row: INT IN [0 .. 4) DO TerminalIO.PutRope[ IO.PutFR["%g [%g %g %g %g]\n", IO.rope[prefix], IO.real[m[row][0]], IO.real[m[row][1]], IO.real[m[row][2]], IO.real[m[row][3]]]]; ENDLOOP; }; TerminalIO.PutRope[IO.PutFR["%g", IO.rope[prefix]]]; IF node.object = NIL THEN TerminalIO.PutRope["node.object = NIL\n"] ELSE TerminalIO.PutRope[IO.PutFR["node.object.name = %g\n", IO.rope[NARROW[node.object, Shape].name]]]; ShowMatrix["LOCAL", node.localTransform]; ShowMatrix["GLOBAL", node.globalTransform]; IF node.children = NIL THEN TerminalIO.PutRope[IO.PutFR["%gno children\n", IO.rope[prefix]]] ELSE FOR i: INT IN [0 .. node.children.length) DO DumpNode[Rope.Cat[prefix, ".|."], node.children[i]]; ENDLOOP; }; DumpNode["", tt.root]; }; ShadowNode: TYPE ~ REF ShadowNodeRep; ShadowNodeRep: TYPE ~ RECORD [ parent: ShadowNode ฌ NIL, children: LIST OF ShadowNode ฌ NIL, timeTreeNode: TTNode ฌ NIL ]; ShadowTree: TYPE ~ REF ShadowTreeRep; ShadowTreeRep: TYPE ~ RECORD [root: ShadowNode ฌ NIL]; BuildShadow: PROC [timeTree: TimeTree] RETURNS [newTree: ShadowTree] ~ { newTree ฌ NEW[ShadowTreeRep ฌ []]; newTree.root ฌ NEW[ShadowNodeRep ฌ []]; };  G3dToolSceneImpl.mesa Copyright ำ 1985, 1992 by Xerox Corporation. All rights reserved. Bloomenthal, October 20, 1992 4:32 pm PDT Heckbert, August 9, 1988 5:51:51 pm PDT Glassner, July 19, 1990 12:21:59 pm PDT Shoemake, November 23, 1989 3:43:56 am PST Wyvill, September 19, 1989 10:34:42 am PDT Crow, September 21, 1989 5:23:04 pm PDT 3d Tool: Scene Definition and Animation Imported Types File Referencing HaveFunWith[viewer.file, selPos.start]; TimeTrees Operations Scene Ops Animation Ops s: IO.STREAM _ IO.RIS[Controls.TypescriptRead[t.typescript, "Start and end times: "]]; IF s = NIL THEN RETURN; t.time0 _ IO.GetReal[s ! IO.EndOfStream => GOTO EOF]; t.time1 _ IO.GetReal[s ! IO.EndOfStream => GOTO EOF]; EXITS EOF => Blink["Missing value(s)"]; Support Debug Unused สf•NewlineDelimiter –"cedarcode" style™™Jšœ ฯeœ6™BJ™)J™'J™'J™*J™*J™'—headšฯl'™'Jšฯk œ…ŸœU˜ๅJ˜—šัblnœŸœŸ˜JšŸœfŸœT˜รJšŸœ˜J˜—JšœŸ˜šž™JšŸœŸœŸœ˜JšœŸœ˜1JšœŸœ˜(Jšœ Ÿœ˜#Jšœ Ÿœ˜#JšœŸœ˜-Jšœ Ÿœ˜$JšœŸœ˜*JšœŸœ˜'JšœŸœ˜)Jšœ Ÿœ˜Jšœ Ÿœ˜!JšœŸœ˜(Jšœ Ÿœ˜*Jšœ Ÿœ˜%JšœŸœ˜,—šž™šฯnœŸœ˜šก œŸœŸœŸœ˜:š ก œŸœ Ÿœ ŸœŸœ˜?Jšœ Ÿœ:Ÿœ˜OJšŸœ Ÿœ ˜Jšœ˜—J˜,J˜&Jšœ˜—JšœŸœ)˜>JšœŸœ˜)J˜)Jšœ Ÿœ:˜]šŸœ ŸœŸœ˜˜ JšœŸœŸœ#˜S—J˜9J˜—J˜;JšŸœ&Ÿœ'˜SJšœ)Ÿœ Ÿœฯc˜YJšœ.Ÿœ˜5J˜J˜,Jšœข˜8J˜J˜—šกœŸœ˜"šกœŸœŸœŸœ˜0šกœŸœ˜š ŸœŸœŸœŸœŸœ˜.JšœŸœ˜)šŸœ%ŸœŸœ!Ÿœ˜UJ˜ JšŸœ˜J˜—šŸœŸœŸ˜šŸœŸœŸœŸ˜*JšŸœ ŸœŸœ˜-JšŸœ˜——J˜—J˜—Jšœ Ÿœ/˜?J˜J˜—J˜ J˜J˜!J˜J˜9J˜(šŸœŸ˜J˜J˜9JšŸœ˜—J˜*J˜'J˜šŸœŸ˜ JšŸœO˜SšŸœ˜Jšœ Ÿœ˜J˜JJ˜-J˜"J˜——J™'J˜——šž™šกœŸœ,˜:šกœŸœ Ÿœ˜/šกœŸœŸœŸœ˜JšŸฯsœฃŸฃŸฃœฃŸะksŸฃœ ฃœฃœฃŸฃŸœคŸœ˜XJšŸœข˜J˜—JšœŸœ˜"JšŸœŸœŸœŸœ˜Jš œŸœ ŸœŸœŸœ˜VJ˜—šœŸœŸ˜!Jšœ˜šœŸœŸœŸœ ˜IJšŸœŸœŸœ˜&—Jšœ"˜"JšŸœŸœ˜—šŸœ Ÿ˜JšŸœ?˜CJšŸœ˜—J˜J˜—šกœŸœŸœ˜(Jšœ ˜ Jšœ˜Jšœ ˜ Jšœ˜Jšœ0˜0J˜%J˜——šž ™ šกœŸœ˜$Jšœ Ÿœ ˜J˜šœŸœ(Ÿœ˜8Jšขœ@˜GJšขœD˜KJšขœ,˜4Jšขœ4˜Jšขœ4˜˜FJšขœŸœฃœฃœฃœฃœฃœฃœ˜XJšขœ(˜0Jšขœ ˜(Jšขœ&˜.Jšขœ2˜:Jšขœ ˜Jšขœ*˜2JšœŸ˜—JšœŸœ˜šŸœŸ˜J˜GJšœ˜JšœR˜RJšœP˜PJ˜/J˜J˜FJšœ˜Jšœ˜˜Jšœ ฃœ ฃœฃœ˜K—J˜.J˜+J˜-J˜1Jšœ˜JšŸœ˜—JšœŸœ˜J˜J˜—šก œŸœ˜šก œ#˜.šŸœŸœŸœ&Ÿ˜;Jšœ\˜\—J˜—Jšœ5˜5Jšœ˜J˜—šกœŸœ˜Jš œŸœŸœŸœŸœA™VJšŸœŸœŸœŸœ™Jš œ Ÿœ ŸœŸœŸœ™5Jš œ Ÿœ ŸœŸœŸœ™5JšŸœŸœ™'˜4JšœŸœ*˜E—JšŸœ ŸœŸœ*˜=J˜J˜—šกœฃŸฃœฃœฃœฃŸœฃœฃœ˜/JšŸœŸœŸœŸœ˜UJšŸœŸœŸœŸœ˜5J˜JšœŸœ ŸœŸœ%˜MJ˜%J˜J˜—šกœŸœŸœ˜.šŸœŸœ˜J˜JšŸœŸœŸœ&˜YJ˜%Jšœ˜—J˜J˜—šก œ'Ÿœ˜EJ˜—šก œ˜,Jšœ Ÿœ˜Jšœ%Ÿœ˜-JšŸœŸœŸœ˜/š ŸฃœฃŸฃŸฃœฃŸ˜J˜J˜Jšœฃœ˜J˜)JšŸœŸœŸœA˜RJšŸœŸœ(˜7JšŸœ˜—Jšœ˜J˜—šก œŸœŸœ˜,šกœ˜J˜-JšœŸœ)˜JJ˜—Jš œŸœŸœŸœŸœ˜>J˜)JšœŸœ ฃœฃœ ฃŸœ ฃŸœฃŸœ˜[J˜ šŸœŸœŸœ˜+JšŸœŸœŸœ˜E—J˜J˜—š กœŸœŸœŸœ ŸœŸœ˜GJ˜-šŸœŸœŸœ˜ Jš œŸœŸœ ŸœŸœ˜6Jšœ8˜8J˜—šŸœ ŸœŸœ˜Jšœ$Ÿœ˜GJ˜(Jšœ(˜(J˜—JšŸœ Ÿœ˜+JšŸœŸœ˜&J˜J˜—šกœŸœŸœ˜NJšŸœ ŸœŸœŸœ˜<šŸœŸœ˜$J˜JšœŸœ%˜.Jš ŸœŸœŸœŸœŸœ˜GJ˜—J˜'J˜J˜—šกœŸœ˜"šกœŸœ#Ÿœ˜UJšŸœŸœŸœŸœ˜AšŸœŸœŸœŸ˜ JšŸœ!ŸœŸœŸœ˜DJšŸœ˜—šŸœŸœ˜$J˜JšœŸœ&˜/Jš ŸœŸœŸœŸœŸœ˜GJ˜—J˜'J˜—šŸฃœฃœฃœ%ฃœ ฃœฃœฃœฃŸฃ˜WJšŸœ:˜>—Jšœ˜J˜—šกœŸœ˜$šŸœŸœŸœ˜0JšŸœ&˜*šŸœ˜JšœŸœ˜ JšœŸœŸœ˜JšŸœŸœŸœŸฃœฃœฃŸœฃœฃŸœ˜WJš"ŸฃœฃŸฃŸฃœฃœฃœฃœฃŸฃœŸฃŸฃœฃœฃŸœ ฃœฃŸœ˜YJšŸฃœฃœฃœ"ฃœคŸฃœ ฃœฃœ˜WJ˜——Jšœ˜J˜—Jšก œŸœ˜@Jšก œŸœ˜:Jšก œŸœ˜>šกœŸœ˜FJ˜—šœŸœ ˜J˜—šกœŸœ=˜KšกœŸœ˜(šŸœŸœŸœŸ˜'JšœŸœŸœŸœ˜OJ˜Jšœ(˜(Jšœ˜šœ=Ÿœ˜RJš ŸœŸœŸœŸœŸœ˜I—JšŸœ˜—J˜—Jšœ%Ÿœ˜-š ŸœŸœŸœŸœŸ˜)Jšœ˜Jšœ ŸœŸœ˜#JšŸœŸœ Ÿœ˜5—Jšœ˜——šž™šกœŸœŸœ˜Jšœ-Ÿœ˜3J˜J˜J˜——JšŸœ˜šž™šก œŸœ˜%šกœŸœ Ÿœ˜/šก œŸœŸœ˜,JšœŸœŸœŸœ˜OšŸœŸœŸœ˜JšœŸœŸœ˜AJšŸœ˜J˜—šŸœŸœŸœ Ÿ˜˜šŸœ!˜#Jš ŸœŸœŸœŸœŸœ˜b——JšŸœ˜—J˜—JšœŸœ Ÿœ˜4šŸœŸ˜JšŸœ*˜.JšŸœŸœ"ŸœŸœ˜g—Jšœ)˜)Jšœ+˜+šŸœŸœ˜JšŸœŸœŸœ˜Eš ŸœŸœŸœŸœŸ˜1J˜4JšŸœ˜——J˜—J˜J˜——šก™JšœŸœŸœ˜(šœŸœŸœ˜ JšœŸœ˜ JšœŸœŸœŸœ˜)JšœŸ˜J˜J˜—JšœŸœŸœ˜(šœŸœŸœŸœ˜8J˜—šก œŸœŸœ˜HJšœ Ÿœ˜"JšœŸœ˜'J˜J˜——J˜—…—:าOF