DIRECTORY CoordSys, CSG, DisplayList3d, Filein3d, GraphicsColor, IO, Matrix3d, Rope, Shading, SVArtwork, SVDisplayListFiling, SVMappings, SVMatrix2d, SVVector3d, SweepGeometry, TFI3d; Filein3dImpl: PROGRAM IMPORTS CoordSys, DisplayList3d, IO, Matrix3d, Rope, SVArtwork, SVDisplayListFiling, TFI3d EXPORTS Filein3d = BEGIN Artwork: TYPE = SVArtwork.Artwork; Color: TYPE = GraphicsColor.Color; FileCamera: TYPE = DisplayList3d.FileCamera; Matrix3by3: TYPE = SVMatrix2d.Matrix3by3; Matrix4by4: TYPE = Matrix3d.Matrix4by4; OMap: TYPE = SVArtwork.OMap; Point3d: TYPE = Matrix3d.Point3d; Point2d: TYPE = Matrix3d.Point2d; Material: TYPE = SVArtwork.Material; SMap: TYPE = SVArtwork.SMap; AssemblyList: TYPE = REF AssemblyListObj; AssemblyListObj: TYPE = DisplayList3d.AssemblyListObj; PointSetOp: TYPE = DisplayList3d.PointSetOp;-- {union, intersection, difference} Assembly: TYPE = REF AssemblyObj; AssemblyObj: TYPE = DisplayList3d.AssemblyObj; MasterObject: TYPE = REF MasterObjectRec; MasterObjectRec: TYPE = DisplayList3d.MasterObjectRec; MasterObjectList: TYPE = DisplayList3d.MasterObjectList; MasterObjectClass: TYPE = REF MasterObjectClassObj; MasterObjectClassObj: TYPE = DisplayList3d.MasterObjectClassObj; Scene: TYPE = REF SceneObj; SceneObj: TYPE = DisplayList3d.SceneObj; Vector: TYPE = SVVector3d.Vector; LightSource: TYPE = REF LightSourceObj; LightSourceObj: TYPE = Shading.LightSourceObj; LightSourceList: TYPE = LIST OF LightSource; LinearMesh: TYPE = REF LinearMeshRecord; LinearMeshRecord: TYPE = SweepGeometry.LinearMeshRecord; RevoluteMesh: TYPE = REF RevoluteMeshRecord; RevoluteMeshRecord: TYPE = SweepGeometry.RevoluteMeshRecord; LinearMeshArray: TYPE = SweepGeometry.LinearMeshArray; RevoluteMeshArray: TYPE = SweepGeometry.RevoluteMeshArray; CoordSystem: TYPE = REF CoordSysObj; CoordSysObj: TYPE = CoordSys.CoordSysObj; CoordSysList: TYPE = CoordSys.CoordSysList; NameList: TYPE = LIST OF Rope.ROPE; FileinScene: PUBLIC PROC [scene: Scene, f: IO.STREAM ] = { csList: CoordSysList; worldCS: CoordSystem; sceneName: Rope.ROPE; version: REAL; restOfVersionRope: Rope.ROPE; TFI3d.ReadBlankAndRope[f, "File:"]; TFI3d.ReadBlank[f]; [] _ TFI3d.ReadWord[f]; TFI3d.ReadWhiteSpace[f]; TFI3d.ReadRope[f, "3D file for scene:"]; TFI3d.ReadBlank[f]; sceneName _ TFI3d.ReadWord[f]; TFI3d.ReadWhiteSpace[f]; TFI3d.ReadRope[f, "Produced by Solidviews Version"]; TFI3d.ReadBlank[f]; version _ TFI3d.ReadReal[f]; restOfVersionRope _ TFI3d.ReadLine[f]; IF version >= 2.0 THEN ReadOptions[f, scene]; TFI3d.ReadBlank[f]; csList _ FileinCoordSystems[f, scene]; FileinLightSources[f, scene]; worldCS _ CoordSys.FindCoordSysFromName["WORLD", csList]; FileinObjects[f, scene]; FileinSceneAssembly[f, scene, csList]; IF version >= 3.0 THEN FileinCameras[f, scene, csList] ELSE scene.cameras _ DisplayList3d.InitialCameraList[]; IF version >= 3.0 THEN FileinCameraOrder[f, scene] ELSE scene.cameraOrder _ LIST["Front"]; TFI3d.ReadBlank[f]; TFI3d.ReadRope[f, "END"]; f.Close[]; }; ReadOptions: PROCEDURE [f: IO.STREAM, scene: Scene] = { keyWord, option: Rope.ROPE; good: BOOL; nextChar: CHAR; twoCRsFound: BOOL _ FALSE; TFI3d.ReadBlank[f]; UNTIL twoCRsFound DO [keyWord, good] _ TFI3d.ReadKeyWord[f]; IF NOT good THEN { nextChar _ IO.PeekChar[f]; IF nextChar = IO.CR THEN { [] _ IO.GetChar[f]; twoCRsFound _ TRUE; }; LOOP}; good _ TFI3d.ReadHorizontalBlank[f]; IF NOT good THEN { nextChar _ IO.PeekChar[f]; IF nextChar = IO.CR THEN { [] _ IO.GetChar[f]; twoCRsFound _ TRUE; }; LOOP}; option _ TFI3d.ReadLine[f]; ProcessOption[keyWord, option, scene]; nextChar _ IO.PeekChar[f]; IF nextChar = IO.CR THEN { [] _ IO.GetChar[f]; twoCRsFound _ TRUE; }; ENDLOOP; }; ProcessOption: PROC [keyWord: Rope.ROPE, option: Rope.ROPE, scene: Scene] = { SELECT TRUE FROM Rope.Equal[keyWord, "background color", FALSE] => { scene.backgroundColor _ TFI3d.ReadColor[IO.RIS[option]]; }; Rope.Equal[keyWord, "shadows", FALSE] => { good, truth: BOOL; [truth, good] _ TFI3d.ReadBool[IO.RIS[option]]; IF NOT good THEN RETURN; scene.shadows _ truth; }; ENDCASE; }; -- end of ProcessOption FileinCoordSystems: PROC [f: IO.STREAM, scene: Scene] RETURNS [csList: CoordSysList] = { count: NAT; mat: Matrix4by4; newCS: CoordSystem; csName, wrtName: Rope.ROPE; csList _ NIL; TFI3d.ReadRope[f, "CoordSystems"]; TFI3d.ReadWhiteSpace[f]; TFI3d.ReadRope[f, "["]; count _ TFI3d.ReadNAT[f]; TFI3d.ReadRope[f, "]:"]; TFI3d.ReadWhiteSpace[f]; FOR i: NAT IN[1..count] DO TFI3d.ReadRope[f, "CoordSys:"]; csName _ TFI3d.ReadBlankAndWord[f]; TFI3d.ReadWhiteSpace[f]; TFI3d.ReadRope[f, "mat:"]; TFI3d.ReadBlank[f]; mat _ TFI3d.FileinMatrix[f]; TFI3d.ReadBlankAndRope[f, "withRespectTo:"]; wrtName _ TFI3d.ReadBlankAndWord[f]; TFI3d.ReadWhiteSpace[f]; SELECT TRUE FROM Rope.Equal[csName,"WORLD"] => { newCS _ CoordSys.CreateCoordSys["WORLD", Matrix3d.Identity[], NIL]; scene.worldCS _ newCS; DisplayList3d.AddCoordSysToScene[newCS, scene]; csList _ AppendToCoordSystems[newCS, csList]; }; Rope.Equal[csName,"SCREEN"] => {}; ENDCASE => { wrtCS: CoordSystem _ CoordSys.FindCoordSysFromName[wrtName, csList]; newCS _ CoordSys.CreateCoordSys[csName,mat,wrtCS]; csList _ AppendToCoordSystems[newCS, csList]; }; ENDLOOP; }; -- end of FileinCoordSystems AppendToCoordSystems: PROC [cs: CoordSystem, list: CoordSysList] RETURNS [CoordSysList] = { z: CoordSysList _ list; IF z = NIL THEN RETURN[CONS[cs,NIL]]; UNTIL z.rest = NIL DO z _ z.rest; ENDLOOP; z.rest _ CONS[cs,NIL]; RETURN[list]; }; FileinLightSources: PROC [f: IO.STREAM, scene: Scene] = { count: NAT; thisLS: LightSource; lsName: Rope.ROPE; point3d: Point3d; color: Color; TFI3d.ReadRope[f, "LightSources"]; TFI3d.ReadBlankAndRope[f, "["]; count _ TFI3d.ReadNAT[f]; TFI3d.ReadRope[f, "]:"]; TFI3d.ReadWhiteSpace[f]; FOR i: NAT IN[1..count] DO TFI3d.ReadRope[f, "LightSource:"]; lsName _ TFI3d.ReadBlankAndWord[f]; TFI3d.ReadBlankAndRope[f, "position:"]; TFI3d.ReadBlank[f]; point3d _ TFI3d.ReadPoint3d[f]; TFI3d.ReadBlankAndRope[f, "color:"]; TFI3d.ReadBlank[f]; color _ TFI3d.ReadColor[f]; TFI3d.ReadWhiteSpace[f]; thisLS _ DisplayList3d.CreateLightSource[lsName, point3d, color]; DisplayList3d.AddLightSourceToScene[thisLS, scene]; ENDLOOP; }; FileinCameras: PROC [f: IO.STREAM, scene: Scene, csList: CoordSysList] = { worldCS: CoordSystem _ CoordSys.FindCoordSysFromName["WORLD", csList]; count: NAT; fileCamera: FileCamera; TFI3d.ReadBlankAndRope[f, "Cameras"]; TFI3d.ReadBlankAndRope[f, "["]; count _ TFI3d.ReadNAT[f]; TFI3d.ReadRope[f, "]:"]; TFI3d.ReadWhiteSpace[f]; FOR i: NAT IN[1..count] DO fileCamera _ TFI3d.ReadCamera[f, worldCS, scene]; DisplayList3d.AddCameraToScene[fileCamera, scene]; ENDLOOP; }; FileinCameraOrder: PROC [f: IO.STREAM, scene: Scene] = { count: NAT; fileCameraName: Rope.ROPE; TFI3d.ReadBlankAndRope[f, "Initial Camera Order"]; TFI3d.ReadBlankAndRope[f, "["]; count _ TFI3d.ReadNAT[f]; TFI3d.ReadRope[f, "]:"]; IF count > 0 THEN { TFI3d.ReadBlank[f]; fileCameraName _ TFI3d.ReadWord[f]; DisplayList3d.AddCameraOrderNameToScene[fileCameraName, scene]; FOR i: NAT IN[2..count] DO TFI3d.ReadBlankAndRope[f, ","]; TFI3d.ReadBlank[f]; fileCameraName _ TFI3d.ReadWord[f]; DisplayList3d.AddCameraOrderNameToScene[fileCameraName, scene]; ENDLOOP; }; TFI3d.ReadBlank[f]; }; FileinSceneAssembly: PROC [f: IO.STREAM, scene: Scene, csList: CoordSysList] = { cs: CoordSystem; cs _ CoordSys.FindCoordSysFromName["sceneAssembly", csList]; DisplayList3d.AddCoordSysToScene[cs, scene]; -- sceneAssembly is added to scene first scene.assembly _ FileinAssembly[f, scene, csList]; -- doesn't fill in coordSys field scene.assembly.coordSys _ cs; -- so fill in coordSys field now. }; ReadArtwork: PRIVATE PROC [f: IO.STREAM] RETURNS [artwork: Artwork] = { source, isColorRope, nextWord: Rope.ROPE _ NIL; surface: REF ANY; material: Material; mat: Matrix3by3; color: GraphicsColor.Color; isColorFile: BOOL; sMap: SMap; oMap: OMap; resolution: REAL; TFI3d.ReadBlankAndRope[f, "material:"]; TFI3d.ReadBlank[f]; material _ TFI3d.ReadMaterial[f]; TFI3d.ReadBlankAndRope[f, "surface:"]; surface _ TFI3d.ReadSurface[f]; IF surface # NIL THEN { nextWord _ TFI3d.ReadBlankAndWord[f]; SELECT TRUE FROM Rope.Equal[nextWord, "source:"] => {-- oMap and sMap are defaulted source _ TFI3d.ReadBlankAndWord[f]; SELECT TRUE FROM ISTYPE[surface, SVMappings.Tube] => {oMap _ tubeO; sMap _ tubeS}; ISTYPE[surface, SVMappings.Box] => {oMap _ orthogonal; sMap _ unfoldedBox}; ENDCASE => ERROR; }; Rope.Equal[nextWord, "SMap:"] => { TFI3d.ReadBlank[f]; sMap _ TFI3d.ReadSMap[f]; TFI3d.ReadBlankAndRope[f, "OMap:"]; TFI3d.ReadBlank[f]; oMap _ TFI3d.ReadOMap[f]; TFI3d.ReadBlankAndRope[f, "source:"]; source _ TFI3d.ReadBlankAndWord[f]; }; ENDCASE => ERROR; isColorRope _ TFI3d.ReadBlankAndWord[f]; SELECT TRUE FROM Rope.Equal[isColorRope, "colorFile", FALSE] => isColorFile _ TRUE; Rope.Equal[isColorRope, "bwFile", FALSE] => isColorFile _ FALSE; ENDCASE => ERROR; TFI3d.ReadBlankAndRope[f, "resolution:"]; TFI3d.ReadBlank[f]; resolution _ TFI3d.ReadReal[f]; TFI3d.ReadBlankAndRope[f, "mat:"]; TFI3d.ReadBlank[f]; mat _ TFI3d.FileinMatrix3by3[f]; }; TFI3d.ReadBlankAndRope[f, "color:"]; TFI3d.ReadBlank[f]; color _ TFI3d.ReadColor[f]; IF surface = NIL THEN artwork _ SVArtwork.CreateColorArtwork[color, material] ELSE artwork _ SVArtwork.CreateFileArtwork[material, surface, oMap, sMap, source, isColorFile, color, resolution, mat]; -- may generate ERROR SVArtwork.FileNotFound }; FileinAssembly: PROC [f: IO.STREAM, scene: Scene, csList: CoordSysList] RETURNS [thisA: Assembly] = { objectOrSubassemblies: Rope.ROPE; thisCS: CoordSystem; artwork: Artwork; scalars: Vector; asName, csName, opRope: Rope.ROPE; pointSetOp: PointSetOp; subAssemblies: LIST OF Assembly; TFI3d.ReadRope[f, "Assembly:"]; asName _ TFI3d.ReadBlankAndWord[f]; TFI3d.ReadBlankAndRope[f, ","]; TFI3d.ReadBlankAndRope[f, "coordSys:"]; csName _ TFI3d.ReadBlankAndWord[f]; thisCS _ CoordSys.FindCoordSysFromName[csName, csList]; TFI3d.ReadBlankAndRope[f, ","]; TFI3d.ReadBlankAndRope[f, "scalars:"]; TFI3d.ReadBlank[f]; scalars _ TFI3d.ReadPoint3d[f]; TFI3d.ReadBlankAndRope[f, "artwork:"]; TFI3d.ReadBlank[f]; artwork _ ReadArtwork[f]; TFI3d.ReadWhiteSpace[f]; objectOrSubassemblies _ TFI3d.ReadWord[f]; SELECT TRUE FROM Rope.Equal[objectOrSubassemblies, "subassemblies", TRUE] => { TFI3d.ReadBlankAndRope[f, "[point set op:"]; opRope _ TFI3d.ReadBlankAndWord[f]; SELECT TRUE FROM Rope.Equal[opRope, "union", TRUE] => pointSetOp _ union; Rope.Equal[opRope, "intersection", TRUE] => pointSetOp _ intersection; Rope.Equal[opRope, "difference", TRUE] => pointSetOp _ difference; ENDCASE => ERROR; TFI3d.ReadBlankAndRope[f, "]"]; TFI3d.ReadWhiteSpace[f]; [] _ FileinSubassemblyNames[f]; thisA _ DisplayList3d.CreateClusterAssembly[asName, pointSetOp]; thisA.coordSys _ thisCS; thisA.scalars _ scalars;-- but of course this will never be used by a composite thisA.artwork _ artwork; subAssemblies _ FileinAssemblies[f, scene, csList]; WireUpAssemblyToSubassemblies[thisA, subAssemblies, scene];}; Rope.Equal[objectOrSubassemblies, "object:", TRUE] => { obName: Rope.ROPE; masterObjectFound: BOOL; TFI3d.ReadBlank[f]; obName _ TFI3d.ReadWord[f]; TFI3d.ReadWhiteSpace[f]; [thisA, masterObjectFound] _ DisplayList3d.CreatePrimitiveAssembly[asName, obName, [1,1,1], scene]; IF NOT masterObjectFound THEN ERROR MasterObjectNotFound; thisA.coordSys _ thisCS; thisA.scalars _ scalars; thisA.artwork _ artwork; }; ENDCASE => SIGNAL ErrorInAssemblyFileout; }; -- end of FileinAssembly MasterObjectNotFound: PUBLIC ERROR = CODE; FileinAssemblies: PROC [f: IO.STREAM, scene: Scene, csList: CoordSysList] RETURNS [asList: LIST OF Assembly] = { count: NAT; thisA: Assembly; asList _ NIL;-- initially TFI3d.ReadBlankAndRope[f, "Subassemblies"]; TFI3d.ReadBlankAndRope[f, "["]; count _ TFI3d.ReadBlankAndNAT[f]; TFI3d.ReadBlankAndRope[f, "]:"];TFI3d.ReadWhiteSpace[f]; FOR i: NAT IN[1..count] DO thisA _ FileinAssembly[f, scene, csList]; asList _ AppendToAssemblyList[thisA, asList]; ENDLOOP; }; ErrorInAssemblyFileout: PUBLIC SIGNAL = CODE; WireUpAssemblyToSubassemblies: PROC [assem: Assembly, subA: LIST OF Assembly, scene: Scene] = { thisSub: Assembly; FOR al: LIST OF Assembly _ subA, al.rest UNTIL al = NIL DO thisSub _ al.first; SVDisplayListFiling.AddSubassemblyToAssemblyWithCS[thisSub, assem, scene, thisSub.coordSys]; ENDLOOP; }; AppendToAssemblyList: PROC [as: Assembly, list: LIST OF Assembly] RETURNS [LIST OF Assembly] = { z: LIST OF Assembly _ list; IF z = NIL THEN RETURN[CONS[as,NIL]]; UNTIL z.rest = NIL DO z _ z.rest; ENDLOOP; z.rest _ CONS[as,NIL]; RETURN[list]; }; AppendToNameList: PROC [name: Rope.ROPE, list: NameList] RETURNS [NameList] = { z: NameList _ list; IF z = NIL THEN RETURN[CONS[name,NIL]]; UNTIL z.rest = NIL DO z _ z.rest; ENDLOOP; z.rest _ CONS[name,NIL]; RETURN[list]; }; FileinSubassemblyNames: PROC [f: IO.STREAM] RETURNS [nameList: NameList] = { count: NAT; thisName: Rope.ROPE; nameList _ NIL; TFI3d.ReadBlankAndRope[f, "["]; count _ TFI3d.ReadBlankAndNAT[f]; TFI3d.ReadBlankAndRope[f, "]:"]; TFI3d.ReadBlankAndRope[f, "("]; IF count = 0 THEN {TFI3d.ReadBlankAndRope[f, ")"]; RETURN;}; thisName _ TFI3d.ReadBlankAndWord[f]; nameList _ AppendToNameList[thisName, nameList]; FOR i: NAT IN[2..count] DO TFI3d.ReadRope[f, ", "]; thisName _ TFI3d.ReadBlankAndWord[f]; nameList _ AppendToNameList[thisName, nameList]; ENDLOOP; TFI3d.ReadBlankAndRope[f, ")"]; TFI3d.ReadWhiteSpace[f]; }; FileinObjects: PROC [f: IO.STREAM, scene: Scene] = { count: NAT; obName, obClass: Rope.ROPE; moClass: MasterObjectClass; moClassFound: BOOL; mo: MasterObject; TFI3d.ReadRope[f, "Objects"]; TFI3d.ReadBlankAndRope[f, "["]; count _ TFI3d.ReadNAT[f]; TFI3d.ReadRope[f, "]:"]; TFI3d.ReadWhiteSpace[f]; FOR i: NAT IN[1..count] DO TFI3d.ReadBlankAndRope[f, "Master Object:"]; obName _ TFI3d.ReadBlankAndWord[f]; TFI3d.ReadWhiteSpace[f]; TFI3d.ReadRope[f, "class:"]; obClass _ TFI3d.ReadBlankAndWord[f]; TFI3d.ReadBlank[f]; [moClass, moClassFound] _ DisplayList3d.FindClassFromName[obClass]; IF NOT moClassFound THEN ERROR MasterObjectClassNotFound; mo _ moClass.filein[f, obName]; DisplayList3d.AddMasterObjectToScene[mo, scene]; ENDLOOP;-- next master object TFI3d.ReadBlank[f]; }; -- end of FileinObjects MasterObjectClassNotFound: PUBLIC ERROR = CODE; END. 0File: Filein3dImpl.mesa Last edited by Bier on August 16, 1983 9:59 am Contents: Recreates a 3d database from a 3d text file of the sort produced by Fileout3d.mesa. Note: When you change this file, you may wish to update versionRope in DisplayList3dImplA. Known Options WORLD must be defined with respect to NIL, and must be the identity matrix. SCREEN must be in terms of NIL. The matrix between them must be a translation SCREEN is ignored since it depends on viewer size and shouldn't be in these files anyway. Other coord systems are unrestricted. Create them with the new data. All coordinate systems other than screen and world will be added to the scene's coord sys list (not the same as csList!) when assemblies (see FileinAssemblies) are added. A copy of List.Nconc1 for CoordSysList instead of LIST OF REF ANY is it primitive or compostite? composite primitive This operation also adds the coordinate system to the scene. A copy of List.Nconc1 for AssemblyListObj instead of LIST OF REF ANY A copy of List.Nconc1 for NameList instead of LIST OF REF ANY. Κ– "cedar" style˜Iheadšœ™Iprocšœ.™.Lšœ]™]LšœZ™ZL˜šΟk ˜ Lšœ ˜ Lšœ˜Lšœ˜Lšœ ˜ Lšœ˜Lšœ˜Lšœ ˜ Lšœ˜Lšœ˜Lšœ ˜ Lšœ˜Lšœ ˜ Lšœ ˜ Lšœ ˜ Lšœ˜Lšœ˜—L˜šœ˜Lšœœ7˜ZLšœ ˜—Lš˜˜Lšœ œ˜"Lšœœ˜"Lšœ œ˜,Lšœ œ˜)Lšœ œ˜'Lšœœ˜Lšœ œ˜!Lšœ œ˜!Lšœ œ˜$Lšœœ˜L˜Lšœœœ˜)Lšœœ!˜6Lšœ œΟc$˜PLšœ œœ ˜!Lšœ œ˜.L˜Lšœœœ˜)Lšœœ!˜6Lšœœ"˜8Lšœœœ˜3Lšœœ&˜@Lšœœœ ˜Lšœ œ˜(Lšœœ˜!L˜Lšœ œœ˜'Lšœœ˜.Lšœœœœ ˜,L˜Lšœ œœ˜(Lšœœ"˜8Lšœœœ˜,Lšœœ$˜œ˜CLšœ*™*Lšœ ™ Lšœ˜Lšœ/˜/Lšœ-˜-Lšœ˜—Lšœ"˜"Lšœ™Lšœ-™-LšœY™Yšœ˜ LšœE™ELšœD˜DLšœ2˜2Lšœ-˜-Lšœ˜——Lšœͺ™ͺ—Lšœ˜Lšœž˜—L˜šŸœœ(œ˜\LšœA™ALšœ˜Lš œœœœœœ˜%šœ œœ œ˜*Lšœ œœ˜—Lšœ˜ Lšœ˜—L˜šŸœœœœ˜9Lšœœ˜ Lšœ˜Lšœ œ˜Lšœ˜Lšœ ˜ Lšœ"˜"Lšœ˜Lšœ˜Lšœ2˜2šœœœ ˜Lšœ"˜"Lšœ#˜#Lšœ'˜'Lšœ˜Lšœ˜Lšœ$˜$Lšœ˜Lšœ˜Lšœ˜LšœA˜ALšœ3˜3—Lšœ˜Lšœ˜—L˜šŸ œœœœ)˜JLšœF˜FLšœœ˜ L˜L˜%Lšœ˜Lšœ˜Lšœ2˜2šœœœ ˜Lšœ1˜1Lšœ2˜2—Lšœ˜Lšœ˜—šŸœœœœ˜8Lšœœ˜ Lšœœ˜L˜2Lšœ˜Lšœ˜Lšœ˜šœ œ˜Lšœ˜Lšœ#˜#Lšœ?˜?šœœœ ˜L˜Lšœ˜Lšœ#˜#Lšœ?˜?—Lšœ˜L˜Lšœ˜—Lšœ˜—šŸœœœœ*˜QLšœ˜Lšœ<˜™>Lšœ˜Lš œœœœœœ˜'šœ œœ œ˜*Lšœ œœ˜—Lšœ˜ Lšœ˜—L˜š Ÿœœœœœ˜LLšœœ˜ Lšœœ˜Lšœ œ˜Lšœ˜Lšœ!˜!Lšœ ˜ Lšœ˜Lšœ œ"œ˜