DIRECTORY Atom, CardTab, CodeTimer, Convert, Feedback, FeedbackTypes, GGBasicTypes, GGCoreOps, GGEvent, GGFileIn, GGFileOut, GGFont, GGInterfaceTypes, GGModelTypes, GGParseIn, GGParseOut, GGScene, GGSegmentTypes, GGSelect, GGSlice, GGSliceOps, GGState, GGUIUtility, GGUtility, Imager, ImagerColor, ImagerColorPrivate, ImagerTransformation, IO, Real, RefTab, Rope, SimpleFeedback, SymTab, ViewerClasses; GGFileImpl: CEDAR PROGRAM IMPORTS Atom, CardTab, CodeTimer, Convert, Feedback, GGCoreOps, GGEvent, GGFont, GGParseIn, GGParseOut, GGScene, GGSelect, GGSlice, GGSliceOps, GGState, GGUIUtility, GGUtility, Imager, ImagerColor, ImagerColorPrivate, IO, Real, RefTab, Rope, SimpleFeedback, SymTab EXPORTS GGFileIn, GGFileOut = BEGIN Camera: TYPE = GGModelTypes.Camera; Caret: TYPE = GGInterfaceTypes.Caret; Color: TYPE = Imager.Color; DefaultData: TYPE = GGModelTypes.DefaultData; MsgRouter: TYPE = FeedbackTypes.MsgRouter; FontData: TYPE = GGModelTypes.FontData; GGData: TYPE = GGInterfaceTypes.GGData; Point: TYPE = GGBasicTypes.Point; ROPE: TYPE = Rope.ROPE; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SegmentClass: TYPE = GGSegmentTypes.SegmentClass; Slice: TYPE = GGModelTypes.Slice; SliceClass: TYPE = GGModelTypes.SliceClass; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; StrokeEnd: TYPE = Imager.StrokeEnd; StrokeJoint: TYPE = Imager.StrokeJoint; Traj: TYPE = GGModelTypes.Traj; Transformation: TYPE = ImagerTransformation.Transformation; Viewer: TYPE = ViewerClasses.Viewer; OutputPixelArrayTable: TYPE = REF OutputPixelArrayTableObj; OutputPixelArrayTableObj: TYPE = RECORD [ nextIndex: NAT ¬ 0, table: RefTab.Ref ¬ NIL -- a mapping from pixel arrays to small integers ]; OutputColorTable: TYPE = REF OutputColorTableObj; OutputColorTableObj: TYPE = RECORD [ pixelArrayTable: OutputPixelArrayTable, nextColorIndex: NAT ¬ 0, constantOp: CardTab.Ref, -- keys off the intensity constantSpecial: RefTab.Ref, -- keys off the REF sampled: RefTab.Ref -- keys off the REF, used for sampled colors and sampled blacks ]; InputPixelArrayTable: TYPE = REF InputPixelArrayTableObj; InputPixelArrayTableObj: TYPE = RECORD [ table: CardTab.Ref ¬ NIL -- a mapping from small integers to pixel arrays ]; InputColorTable: TYPE = REF InputColorTableObj; InputColorTableObj: TYPE = RECORD [ pixelArrayTable: InputPixelArrayTable, colors: CardTab.Ref ¬ NIL -- a mapping from small integers to colors ]; CreateOutputColorTable: PROC [] RETURNS [ct: OutputColorTable] = { ct ¬ NEW[OutputColorTableObj]; ct.pixelArrayTable ¬ NEW[OutputPixelArrayTableObj]; ct.pixelArrayTable.table ¬ RefTab.Create[mod: 17]; ct.constantOp ¬ CardTab.Create[mod: 17]; ct.constantSpecial ¬ RefTab.Create[mod: 11]; ct.sampled ¬ RefTab.Create[mod: 5]; }; NatRef: TYPE = REF NAT; PickANumber: PROC [ct: OutputColorTable] RETURNS [natRef: NatRef] = { natRef ¬ NEW[NAT ¬ ct.nextColorIndex]; ct.nextColorIndex ¬ ct.nextColorIndex + 1; }; FindColorInIntensityTable: PROC [op: ImagerColor.OpConstantColor, table: CardTab.Ref] RETURNS [found: BOOL ¬ FALSE, nat: NAT ¬ 0] = { intensity: REAL ¬ ImagerColorPrivate.IntensityFromColor[op]; key: CARD ¬ Real.Floor[intensity*32767]; }; AddColorToOutputTable: PROC [color: Imager.Color, ct: OutputColorTable] = { gray: ImagerColor.OpConstantColor ¬ ImagerColor.ColorFromGray[1]; IF color=NIL THEN { } ELSE { WITH color SELECT FROM op: ImagerColor.OpConstantColor => { IF gray.colorOperator=op.colorOperator THEN { -- "MakeGray" } ELSE { --put out an RGB r, g, b: REAL; [r,g,b] ¬ GGCoreOps.ExtractRGB[op]; }; }; special: ImagerColor.SpecialColor => { }; sampledBlack: ImagerColor.SampledBlack => { }; sampledColor: ImagerColor.SampledColor => { }; ENDCASE => ERROR; -- Gargoyle doesn't know about this type of color }; }; FileinSceneAndOptions: PUBLIC PROC [f: IO.STREAM, ggData: GGData, alignmentsOnly: BOOL, selectSlices: BOOL, closeStreamWhenDone: BOOL _ TRUE] RETURNS [success: BOOL ¬ FALSE, sceneName: ROPE] = { ENABLE GGParseIn.SyntaxError => { Feedback.PutFL[ggData.router, oneLiner, $Error, "position: %g, wasThere: %g, notThere: %g", LIST[[integer[position]], [rope[wasThere]], [rope[notThere]]] ]; GOTO Abort; }; scene: Scene ¬ ggData.scene; version: REAL; count: NAT; entity: Slice; finger, newEntities: LIST OF Slice; [sceneName, version] ¬ ReadHeaderAndOptions[f, ggData, IF alignmentsOnly THEN alignments ELSE all]; GGParseIn.ReadRope[f, "Entities:"]; GGParseIn.ReadChar[f, '[]; count ¬ GGParseIn.ReadNAT[f]; GGParseIn.ReadRope[f, "]:"]; GGParseIn.ReadWhiteSpace[f]; [newEntities, finger] ¬ GGUtility.StartSliceList[]; FOR i: NAT IN[1..count] DO entity ¬ FileinEntity[f, version, ggData.router, ggData.camera]; IF entity#NIL THEN [newEntities, finger] ¬ GGUtility.AddSlice[entity, newEntities, finger]; IF ggData.aborted[filein] THEN { ggData.aborted[filein] ¬ FALSE; GGUIUtility.SafeClose[f, ggData.router]; RETURN[FALSE, NIL]; -- signals a failed read }; ENDLOOP; IF closeStreamWhenDone THEN GGUIUtility.SafeClose[f, ggData.router]; GGSelect.DeselectAll[scene, normal]; GGScene.AddSlices[scene, newEntities]; IF selectSlices THEN FOR sliceList: LIST OF Slice ¬ newEntities, sliceList.rest UNTIL sliceList=NIL DO GGSelect.SelectEntireSlice[sliceList.first, scene, normal]; ENDLOOP; RETURN[TRUE, sceneName]; EXITS Abort => {}; -- RETURN[FALSE, NIL]; }; FileinSceneOnly: PUBLIC PROC [f: IO.STREAM, scene: Scene, selectSlices: BOOL, camera: Camera, closeStreamWhenDone: BOOL _ TRUE] RETURNS [success: BOOL ¬ FALSE, sceneName: Rope.ROPE] = { ENABLE GGParseIn.SyntaxError => { SimpleFeedback.PutFL[$Gargoyle, oneLiner, $Error, "position: %g, wasThere: %g, notThere: %g", LIST[[integer[position]], [rope[wasThere]], [rope[notThere]]] ]; GOTO Abort; }; version: REAL; count: NAT; entity: Slice; finger, newEntities: LIST OF Slice; GGParseIn.ReadRope[f, "Gargoyle file for scene: "]; sceneName ¬ GGParseIn.ReadLine[f]; GGParseIn.ReadRope[f, "Produced by version"]; version ¬ GGParseIn.ReadReal[f]; version ¬ Real.Round[version*100.0]/100.0; -- compensate for fuzzy ReadReal of version IF version >= 8607.17 THEN ReadSceneFields[f, version, scene]; GGParseIn.ReadRope[f, "Entities:"]; GGParseIn.ReadChar[f, '[]; count ¬ GGParseIn.ReadNAT[f]; GGParseIn.ReadRope[f, "]:"]; GGParseIn.ReadWhiteSpace[f]; [newEntities, finger] ¬ GGUtility.StartSliceList[]; FOR i: NAT IN[1..count] DO entity ¬ FileinEntity[f, version, NIL, camera]; IF entity#NIL THEN [newEntities, finger] ¬ GGUtility.AddSlice[entity, newEntities, finger]; ENDLOOP; IF closeStreamWhenDone THEN GGUIUtility.SafeClose[f]; GGSelect.DeselectAll[scene, normal]; GGScene.AddSlices[scene, newEntities]; IF selectSlices THEN FOR sliceList: LIST OF Slice ¬ newEntities, sliceList.rest UNTIL sliceList=NIL DO GGSelect.SelectEntireSlice[sliceList.first, scene, normal]; ENDLOOP; RETURN[TRUE, sceneName]; EXITS Abort => {}; -- RETURN[FALSE, NIL]; }; FileinOptionsOnly: PUBLIC PROC [f: IO.STREAM, ggData: GGData, alignmentsOnly: BOOL, closeStreamWhenDone: BOOL _ TRUE] RETURNS [success: BOOL ¬ FALSE, sceneName: Rope.ROPE] = { ENABLE GGParseIn.SyntaxError => { Feedback.PutFL[ggData.router, oneLiner, $Error, "position: %g, wasThere: %g, notThere: %g", LIST[[integer[position]], [rope[wasThere]], [rope[notThere]]] ]; GOTO Abort; }; version: REAL; [sceneName, version] ¬ ReadHeaderAndOptions[f, ggData, IF alignmentsOnly THEN alignments ELSE all]; IF closeStreamWhenDone THEN GGUIUtility.SafeClose[f, ggData.router]; RETURN[TRUE, sceneName]; EXITS Abort => {}; -- RETURN[FALSE, NIL]; }; ReadHeaderAndOptions: PROC [f: IO.STREAM, ggData: GGData, type: OptionType] RETURNS [sceneName: Rope.ROPE, version: REAL] = { GGParseIn.ReadRope[f, "Gargoyle file for scene: "]; sceneName ¬ GGParseIn.ReadLine[f]; GGParseIn.ReadRope[f, "Produced by version"]; version ¬ GGParseIn.ReadReal[f]; version ¬ Real.Round[version*100.0]/100.0; -- compensate for fuzzy ReadReal of version CodeTimer.StartInt[$ReadOptions, $Gargoyle]; IF version >= 8607.17 THEN ReadOptions[f, version, ggData, type] ELSE GGEvent.StandardAlignments[ggData, LIST[NIL]]; CodeTimer.StopInt[$ReadOptions, $Gargoyle]; }; ReadSceneFields: PROC [f: IO.STREAM, version: REAL, scene: Scene] = { keyWord, option: Rope.ROPE; good: BOOL; nextChar: CHAR; twoCRsFound: BOOL ¬ FALSE; UNTIL twoCRsFound DO [keyWord, good] ¬ GGParseIn.ReadKeyWord[f]; IF NOT good THEN { nextChar ¬ IO.PeekChar[f]; IF IsEndLine[nextChar] THEN { [] ¬ IO.GetChar[f]; twoCRsFound ¬ TRUE; }; LOOP}; good ¬ GGParseIn.ReadHorizontalBlank[f]; IF NOT good THEN { nextChar ¬ IO.PeekChar[f]; IF IsEndLine[nextChar] THEN { [] ¬ IO.GetChar[f]; twoCRsFound ¬ TRUE; }; LOOP}; option ¬ GGParseIn.ReadLine[f]; ProcessSceneField[keyWord, option, version, scene]; nextChar ¬ IO.PeekChar[f]; IF IsEndLine[nextChar] THEN { [] ¬ IO.GetChar[f]; twoCRsFound ¬ TRUE; }; ENDLOOP; }; ReadOptions: PROC [f: IO.STREAM, version: REAL, ggData: GGData, type: OptionType] = { keyWord, option: Rope.ROPE; good: BOOL; nextChar: CHAR; twoCRsFound: BOOL ¬ FALSE; GGState.SetPalette[ggData, FALSE]; UNTIL twoCRsFound DO [keyWord, good] ¬ GGParseIn.ReadKeyWord[f]; IF NOT good THEN { nextChar ¬ IO.PeekChar[f]; IF IsEndLine[nextChar] THEN { [] ¬ IO.GetChar[f]; twoCRsFound ¬ TRUE; }; LOOP}; good ¬ GGParseIn.ReadHorizontalBlank[f]; IF NOT good THEN { nextChar ¬ IO.PeekChar[f]; IF IsEndLine[nextChar] THEN { [] ¬ IO.GetChar[f]; twoCRsFound ¬ TRUE; }; LOOP}; option ¬ GGParseIn.ReadLine[f]; ProcessOption[keyWord, option, version, ggData, type]; nextChar ¬ IO.PeekChar[f]; IF IsEndLine[nextChar] THEN { [] ¬ IO.GetChar[f]; twoCRsFound ¬ TRUE; }; ENDLOOP; }; ReadListOfAtom: PUBLIC PROC [f: IO.STREAM] RETURNS [atomList: LIST OF ATOM] = { cr: Rope.ROPE = Rope.FromChar[IO.CR]; lf: Rope.ROPE = Rope.FromChar[IO.LF]; rightParen: Rope.ROPE = Rope.FromChar[')]; rightBracket: Rope.ROPE = Rope.FromChar[']]; AtomsOnOneLineOrParenProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = { SELECT char FROM IO.LF, IO.CR, '), '] =>RETURN [break]; IO.SP, IO.TAB, ', , '; => RETURN [sepr]; ENDCASE => RETURN [other]; }; rope: Rope.ROPE; end: BOOL ¬ FALSE; [] ¬ IO.SkipWhitespace[f, TRUE]; atomList ¬ NIL; WHILE TRUE DO [rope, ----] ¬ IO.GetTokenRope[f, AtomsOnOneLineOrParenProc !IO.EndOfStream => {end ¬ TRUE; CONTINUE}]; IF end OR rope = NIL THEN RETURN; IF Rope.Equal[rope, cr] OR Rope.Equal[rope, lf] THEN RETURN; IF Rope.Equal[rope, rightParen] THEN { f.Backup[')]; RETURN; }; IF Rope.Equal[rope, rightBracket] THEN { f.Backup[']]; RETURN; }; atomList ¬ AppendAtomToAtomList[Atom.MakeAtom[rope], atomList]; ENDLOOP; }; AppendAtomToAtomList: PROC [atom: ATOM, list: LIST OF ATOM] RETURNS [LIST OF ATOM] = { z: LIST OF ATOM ¬ list; IF z = NIL THEN RETURN[CONS[atom,NIL]]; UNTIL z.rest = NIL DO z ¬ z.rest; ENDLOOP; z.rest ¬ CONS[atom,NIL]; RETURN[list]; }; OptionType: TYPE = {alignments, normal, sceneField, all, none}; optionTable: SymTab.Ref; OptionData: TYPE = REF OptionDataObj; OptionDataObj: TYPE = RECORD [ process: ProcessOptionProc _ NIL, processScene: ProcessSceneFieldProc _ NIL, type: OptionType ]; FindOption: PROC [name: ROPE] RETURNS [optionData: OptionData _ NIL] = { found: BOOL; val: REF; [found, val] _ SymTab.Fetch[optionTable, name]; IF found THEN optionData _ NARROW[val]; }; RegisterAllOptions: PROC [] = { RegisterOption: PROC [name: ROPE, process: ProcessOptionProc, type: OptionType] = { optionData: OptionData; optionData _ NEW[OptionDataObj _ [process: process, processScene: NIL, type: type]]; [] _ SymTab.Store[optionTable, name, optionData]; }; RegisterSceneField: PROC [name: ROPE, process: ProcessSceneFieldProc] = { optionData: OptionData; optionData _ NEW[OptionDataObj _ [process: NIL, processScene: process, type: sceneField]]; [] _ SymTab.Store[optionTable, name, optionData]; }; optionTable _ SymTab.Create[29]; RegisterOption["ViewTransform", ProcessViewTransform, normal]; RegisterOption["PaletteForFillColor", ProcessPaletteForFillColor, normal]; RegisterOption["PaletteForStrokeColor", ProcessPaletteForStrokeColor, normal]; RegisterOption["Palette", ProcessPalette, normal]; RegisterOption["Scripts", ProcessScripts, normal]; RegisterOption["Slope", ProcessSlope, alignments]; RegisterOption["Angle", ProcessAngle, alignments]; RegisterOption["Radius", ProcessRadius, alignments]; RegisterOption["LineDistance", ProcessLineDistance, alignments]; RegisterOption["Anchor", ProcessAnchor, alignments]; RegisterOption["Midpoints", ProcessMidpoints, normal]; RegisterOption["Heuristics", ProcessHeuristics, normal]; RegisterOption["ShowAlignments", ProcessShowAlignments, normal]; RegisterOption["ShowColors", ProcessShowColors, normal]; RegisterOption["Gravity", ProcessGravity, normal]; RegisterOption["ScaleUnit", ProcessScaleUnit, normal]; RegisterOption["DisplayStyle", ProcessDisplayStyle, normal]; RegisterOption["GravityExtent", ProcessGravityExtent, normal]; RegisterOption["GravityType", ProcessGravityType, normal]; RegisterOption["DefaultFont", ProcessDefaultFont, normal]; RegisterOption["Defaults", ProcessDefaults, normal]; RegisterOption["Dashed", ProcessDashed, normal]; RegisterOption["Shadows", ProcessShadows, normal]; RegisterOption["Active", ProcessActive, normal]; RegisterSceneField["BackgroundColor", ProcessBackgroundColor]; }; ProcessSceneField: PROC [keyWord, option: Rope.ROPE, version: REAL, scene: Scene] = { optionStream: IO.STREAM; optionData: OptionData; optionStream ¬ IO.RIS[option]; optionData _ FindOption[keyWord]; IF optionData = NIL THEN { SimpleFeedback.PutF[$Gargoyle, oneLiner, $Error, "Unknown keyword in gargoyle file: %g", [rope[keyWord]]]; } ELSE { IF optionData.type # sceneField THEN RETURN; optionData.processScene[keyWord, scene, optionStream, version]; }; }; ProcessOption: PROC [keyWord, option: Rope.ROPE, version: REAL, ggData: GGData, types: OptionType] = { optionStream: IO.STREAM; optionData: OptionData; IF types = none THEN RETURN; optionStream ¬ IO.RIS[option]; optionData _ FindOption[keyWord]; IF optionData = NIL THEN { SimpleFeedback.PutF[$Gargoyle, oneLiner, $Error, "Unknown keyword in gargoyle file: %g", [rope[keyWord]]]; } ELSE { IF types = sceneField AND NOT optionData.type = sceneField THEN RETURN; IF types = alignments AND NOT optionData.type = alignments THEN RETURN; IF types = normal AND NOT optionData.type = normal THEN RETURN; IF optionData.type = sceneField THEN optionData.processScene[keyWord, ggData.scene, optionStream, version] ELSE optionData.process[keyWord, ggData, optionStream, version]; }; }; ProcessOptionProc: TYPE = PROC [keyWord: ROPE, ggData: GGData, optionStream: IO.STREAM, version: REAL]; ProcessSceneFieldProc: TYPE = PROC [keyWord: ROPE, scene: Scene, optionStream: IO.STREAM, version: REAL]; ProcessViewTransform: ProcessOptionProc = { clientToViewer: Transformation _ GGParseIn.ReadTransformation[optionStream]; GGState.SetBiScrollersTransform[ggData, clientToViewer]; }; ProcessPaletteForFillColor: ProcessOptionProc = { active: BOOL ¬ GGParseIn.ReadBool[optionStream]; IF active THEN GGState.SetPalette[ggData, TRUE]; }; ProcessPaletteForStrokeColor: ProcessOptionProc = { active: BOOL ¬ GGParseIn.ReadBool[optionStream]; IF active THEN GGState.SetPalette[ggData, TRUE]; }; ProcessPalette: ProcessOptionProc = { active: BOOL ¬ GGParseIn.ReadBool[optionStream]; IF active THEN GGState.SetPalette[ggData, TRUE]; }; ProcessScripts: ProcessOptionProc = { names: LIST OF Rope.ROPE ¬ GGParseIn.ReadListOfRope[optionStream]; ggData.debug.autoScriptNames ¬ GGCoreOps.NewRopeListt[]; FOR list: LIST OF Rope.ROPE ¬ names, list.rest UNTIL list = NIL DO GGCoreOps.AppendRope[list.first, ggData.debug.autoScriptNames]; ENDLOOP; }; ProcessSlope: ProcessOptionProc = { IF version>=8706.16 THEN { -- slope values and ON states values: LIST OF REAL; on: LIST OF BOOL; [----, values, on] ¬ GGParseIn.ReadScalarButtonValues[optionStream, version]; GGState.AddSlopeList[ggData, values, on]; } ELSE { -- slope values only values, ptr: LIST OF REAL; optionList: LIST OF ROPE; value: REAL; optionList ¬ GGParseIn.ReadListOfRope[optionStream]; [values, ptr] ¬ GGCoreOps.StartRealList[]; FOR list: LIST OF Rope.ROPE ¬ optionList, list.rest UNTIL list = NIL DO value ¬ Convert.RealFromRope[list.first]; [values, ptr] ¬ GGCoreOps.AddReal[value, values, ptr]; ENDLOOP; GGState.AddSlopeList[ggData, values, NIL]; }; }; ProcessAngle: ProcessOptionProc = { IF version>=8706.16 THEN { -- angle values and ON states values: LIST OF REAL; on: LIST OF BOOL; [----, values, on] ¬ GGParseIn.ReadScalarButtonValues[optionStream, version]; GGState.AddAngleList[ggData, values, on]; } ELSE { -- angle values only values, ptr: LIST OF REAL; value: REAL; optionList: LIST OF ROPE; optionList ¬ GGParseIn.ReadListOfRope[optionStream]; [values, ptr] ¬ GGCoreOps.StartRealList[]; FOR list: LIST OF Rope.ROPE ¬ optionList, list.rest UNTIL list = NIL DO value ¬ Convert.RealFromRope[list.first]; [values, ptr] ¬ GGCoreOps.AddReal[value, values, ptr]; ENDLOOP; GGState.AddAngleList[ggData, values, NIL]; }; }; ProcessRadius: ProcessOptionProc = { IF version>=8706.16 THEN { -- radius values and ON states values: LIST OF REAL; on: LIST OF BOOL; names: LIST OF Rope.ROPE; [names, values, on] ¬ GGParseIn.ReadScalarButtonValues[optionStream, version]; GGState.AddRadiusList[ggData, names, values, on]; } ELSE { -- radius values only values, ptr: LIST OF REAL; value: REAL; optionList: LIST OF ROPE; optionList ¬ GGParseIn.ReadListOfRope[optionStream]; [values, ptr] ¬ GGCoreOps.StartRealList[]; FOR list: LIST OF Rope.ROPE ¬ optionList, list.rest UNTIL list = NIL DO value ¬ Convert.RealFromRope[list.first]; [values, ptr] ¬ GGCoreOps.AddReal[value, values, ptr]; ENDLOOP; GGState.AddRadiusList[ggData, NIL, values, NIL]; }; }; ProcessLineDistance: ProcessOptionProc = { IF version>=8706.16 THEN { -- LineDistance values and ON states values: LIST OF REAL; on: LIST OF BOOL; names: LIST OF Rope.ROPE; [names, values, on] ¬ GGParseIn.ReadScalarButtonValues[optionStream, version]; GGState.AddLineDistanceList[ggData, names, values, on]; } ELSE { -- LineDistance values only values, ptr: LIST OF REAL; value: REAL; optionList: LIST OF ROPE; optionList ¬ GGParseIn.ReadListOfRope[optionStream]; [values, ptr] ¬ GGCoreOps.StartRealList[]; FOR list: LIST OF Rope.ROPE ¬ optionList, list.rest UNTIL list = NIL DO value ¬ Convert.RealFromRope[list.first]; [values, ptr] ¬ GGCoreOps.AddReal[value, values, ptr]; ENDLOOP; GGState.AddLineDistanceList[ggData, NIL, values, NIL]; }; }; ProcessAnchor: ProcessOptionProc = { anchored: BOOL; anchored ¬ GGParseIn.ReadBool[optionStream]; IF anchored THEN { point: Point ¬ GGParseIn.ReadPoint[optionStream]; ggData.anchor­ ¬ [exists: TRUE, point: point, chair: NIL, attractor: NIL]; } ELSE ggData.anchor.exists ¬ FALSE; }; ProcessMidpoints: ProcessOptionProc = { on: BOOL; on ¬ GGParseIn.ReadBool[optionStream]; GGState.SetMidpoints[ggData, on]; }; ProcessHeuristics: ProcessOptionProc = { on: BOOL; on ¬ GGParseIn.ReadBool[optionStream]; GGState.SetHeuristics[ggData, on]; }; ProcessShowAlignments: ProcessOptionProc = { on: BOOL; on ¬ GGParseIn.ReadBool[optionStream]; GGState.SetShowAlignments[ggData, on]; }; ProcessShowColors: ProcessOptionProc = { on: BOOL; on ¬ GGParseIn.ReadBool[optionStream]; }; ProcessGravity: ProcessOptionProc = { on: BOOL; on ¬ GGParseIn.ReadBool[optionStream]; GGState.SetGravity[ggData, on]; }; ProcessScaleUnit: ProcessOptionProc = { real: REAL ¬ GGParseIn.ReadReal[optionStream]; IF real>0.0 THEN GGState.SetScaleUnit[ggData, real, NIL]; }; ProcessDisplayStyle: ProcessOptionProc = { rope: Rope.ROPE ¬ GGParseIn.ReadWord[optionStream]; GGState.SetDisplayStyle[ggData, GGUIUtility.DisplayStyleFromRope[rope]]; }; ProcessGravityExtent: ProcessOptionProc = { real: REAL ¬ GGParseIn.ReadReal[optionStream]; GGState.SetGravityExtent[ggData, real]; }; ProcessGravityType: ProcessOptionProc = { rope: Rope.ROPE ¬ GGParseIn.ReadWord[optionStream]; GGState.SetGravityType[ggData, GGUIUtility.GravityTypeFromRope[rope]]; }; ProcessDefaultFont: ProcessOptionProc = { fontData: FontData ¬ GGFont.CreateFontData[]; -- assure non-NIL font data fontData ¬ GGFont.ParseFontData[data: fontData, inStream: optionStream, literalP: TRUE, transformP: TRUE, storedSizeP: TRUE, designSizeP: TRUE ! GGFont.ParseError => CONTINUE;]; fontData.substituteOK ¬ TRUE; -- bad hack here. Avoids a bogus default font GGState.SetDefaultFont[ggData, fontData]; }; ProcessDefaults: ProcessOptionProc = { strokeJoint, strokeEnd: Rope.ROPE; ggData.defaults.fillColor ¬ GGParseIn.ReadColor[optionStream, version]; ggData.defaults.strokeColor ¬ GGParseIn.ReadColor[optionStream, version]; ggData.defaults.strokeWidth ¬ GGParseIn.ReadReal[optionStream]; strokeJoint ¬ GGParseIn.ReadWord[optionStream]; ggData.defaults.strokeJoint ¬ SELECT TRUE FROM Rope.Equal[strokeJoint, "miter", FALSE] => miter, Rope.Equal[strokeJoint, "bevel", FALSE] => bevel, ENDCASE => round; strokeEnd ¬ GGParseIn.ReadWord[optionStream]; ggData.defaults.strokeEnd ¬ SELECT TRUE FROM Rope.Equal[strokeEnd, "square", FALSE] => square, Rope.Equal[strokeEnd, "butt", FALSE] => butt, ENDCASE => round; }; ProcessDashed: ProcessOptionProc = { dashed: BOOL; dashed ¬ GGParseIn.ReadBool[optionStream]; ggData.defaults.dashed ¬ dashed; IF ggData.defaults.dashed THEN { ggData.defaults.pattern ¬ GGParseIn.ReadArrayOfReal[optionStream]; ggData.defaults.offset ¬ GGParseIn.ReadReal[optionStream]; ggData.defaults.length ¬ GGParseIn.ReadReal[optionStream]; }; }; ProcessShadows: ProcessOptionProc = { shadowed: BOOL; ggData.defaults.textColor ¬ GGParseIn.ReadColor[optionStream, version]; IF ggData.defaults.textColor=NIL THEN ggData.defaults.textColor ¬ Imager.black; shadowed ¬ GGParseIn.ReadBool[optionStream]; ggData.defaults.dropShadowOn ¬ shadowed; IF ggData.defaults.dropShadowOn THEN { ggData.defaults.dropShadowOffset ¬ GGParseIn.ReadPoint[optionStream]; ggData.defaults.dropShadowColor ¬ GGParseIn.ReadColor[optionStream, version]; }; }; ProcessActive: ProcessOptionProc = { active: BOOL; active ¬ GGParseIn.ReadBool[optionStream]; GGState.SetActive[ggData, active]; }; ProcessBackgroundColor: ProcessSceneFieldProc = { color: Imager.Color ¬ GGParseIn.ReadColor[optionStream, version]; GGScene.SetBackgroundColor[scene, color]; }; SkipOptions: PROC [f: IO.STREAM] = { keyWord, option: Rope.ROPE; good: BOOL; nextChar: CHAR; twoCRsFound: BOOL ¬ FALSE; UNTIL twoCRsFound DO [keyWord, good] ¬ GGParseIn.ReadKeyWord[f]; IF NOT good THEN { nextChar ¬ IO.PeekChar[f]; IF IsEndLine[nextChar] THEN { [] ¬ IO.GetChar[f]; twoCRsFound ¬ TRUE; }; LOOP}; good ¬ GGParseIn.ReadHorizontalBlank[f]; IF NOT good THEN { nextChar ¬ IO.PeekChar[f]; IF IsEndLine[nextChar] THEN { [] ¬ IO.GetChar[f]; twoCRsFound ¬ TRUE; }; LOOP}; option ¬ GGParseIn.ReadLine[f]; nextChar ¬ IO.PeekChar[f]; IF IsEndLine[nextChar] THEN { [] ¬ IO.GetChar[f]; twoCRsFound ¬ TRUE; }; ENDLOOP; }; FileinEntity: PROC [f: IO.STREAM, version: REAL, router: MsgRouter, camera: Camera] RETURNS [entity: Slice] = { nextWord: Rope.ROPE; IF version > 8605.22 THEN { IF version >= 8705.14 THEN { entity ¬ GGSliceOps.FileinSlice[f, version, router, camera]; } ELSE { nextWord ¬ GGParseIn.ReadWord[f]; SELECT TRUE FROM Rope.Equal[nextWord, "Outline:"] => { class: SliceClass ¬ GGSlice.FetchSliceClass[$Outline]; entity ¬ class.filein[f, version, router, camera]; }; Rope.Equal[nextWord, "Slice"] => { entity ¬ GGSliceOps.FileinSlice[f, version, router, camera]; }; ENDCASE => ERROR } } ELSE { nextWord ¬ GGParseIn.ReadWord[f]; SELECT TRUE FROM Rope.Equal[nextWord, "Outline:"] => { class: SliceClass ¬ GGSlice.FetchSliceClass[$Outline]; entity ¬ class.filein[f, version, router, camera]; }; Rope.Equal[nextWord, "Cluster"] => { entity ¬ GGSliceOps.FileinSlice[f, version, router, camera]; }; ENDCASE => ERROR; }; }; FileoutSceneAndOptions: PUBLIC PROC [f: IO.STREAM, ggData: GGData, fileName: Rope.ROPE, scene: Scene ¬ NIL] = { sceneOut: Scene ¬ IF scene#NIL THEN scene ELSE ggData.scene; sliceCount: NAT ¬ GGScene.CountSlices[sceneOut, first]; DoFileOut: PROC [slice: Slice] RETURNS [done: BOOL ¬ FALSE] = { f.PutChar[IO.LF]; -- CR before each top-Level slice GGSliceOps.FileoutSlice[f, slice]; }; f.PutF1["Gargoyle file for scene: %g\n", [rope[fileName]]]; f.PutF1["Produced by version %g\n\n", [rope[GGUtility.versionRope]]]; FileoutOptions[f, ggData]; FileoutSceneFields[f, ggData.scene]; f.PutChar[IO.LF]; f.PutF1["Entities: [%g]:\n\n", [integer[sliceCount]]]; [] ¬ GGScene.WalkSlices[sceneOut, first, DoFileOut]; }; FileoutSceneOnly: PUBLIC PROC [f: IO.STREAM, scene: Scene, fileName: Rope.ROPE] = { sliceCount: NAT ¬ GGScene.CountSlices[scene, first]; DoFileOut: PROC [slice: Slice] RETURNS [done: BOOL ¬ FALSE] = { f.PutChar[IO.LF]; GGSliceOps.FileoutSlice[f, slice]; }; f.PutF1["Gargoyle file for scene: %g\n", [rope[fileName]]]; f.PutF1["Produced by version %g\n\n", [rope[GGUtility.versionRope]]]; FileoutSceneFields[f, scene]; f.PutChar[IO.LF]; f.PutF1["Entities: [%g]:\n\n", [integer[sliceCount]]]; [] ¬ GGScene.WalkSlices[scene, first, DoFileOut]; }; FileoutSceneFields: PROC [f: IO.STREAM, scene: Scene] = { f.PutRope["BackgroundColor: "]; GGParseOut.WriteColor[f, GGScene.GetBackgroundColor[scene]]; f.PutChar[IO.LF]; }; FileoutOptions: PROC [f: IO.STREAM, ggData: GGData] = { values: LIST OF REAL; on: LIST OF BOOL; names: LIST OF Rope.ROPE; defaults: DefaultData; anchor: Caret; f.PutRope["ViewTransform: "]; GGParseOut.WriteTransformation[f, GGState.GetBiScrollersTransform[ggData]]; f.PutChar[IO.LF]; f.PutRope["Scripts: "]; GGParseOut.WriteListOfRope[f, ggData.debug.autoScriptNames.list]; f.PutChar[IO.LF]; f.PutRope["Slope: "]; [values, on] ¬ GGState.GetSlopeAlignments[ggData]; GGParseOut.WriteScalarButtonValues[f, NIL, values, on]; f.PutChar[IO.LF]; f.PutRope["Angle: "]; [values, on] ¬ GGState.GetAngleAlignments[ggData]; GGParseOut.WriteScalarButtonValues[f, NIL, values, on]; f.PutChar[IO.LF]; f.PutRope["Radius: "]; [names, values, on] ¬ GGState.GetRadiusAlignments[ggData]; GGParseOut.WriteScalarButtonValues[f, names, values, on]; f.PutChar[IO.LF]; f.PutRope["LineDistance: "]; [names, values, on] ¬ GGState.GetLineDistanceAlignments[ggData]; GGParseOut.WriteScalarButtonValues[f, names, values, on]; f.PutRope["\nMidpoints: "]; GGParseOut.WriteBool[f, GGState.GetMidpoints[ggData]]; f.PutRope["\nHeuristics: "]; GGParseOut.WriteBool[f, GGState.GetHeuristics[ggData]]; f.PutRope["\nShowAlignments: "]; GGParseOut.WriteBool[f, GGState.GetShowAlignments[ggData]]; f.PutF1["\nScaleUnit: %g", [real[GGState.GetScaleUnit[ggData]]] ]; f.PutF1["\nDisplayStyle: %g", [rope[GGUIUtility.DisplayStyleToRope[GGState.GetDisplayStyle[ggData]] ]] ]; f.PutRope["\nGravity: "]; GGParseOut.WriteBool[f, GGState.GetGravity[ggData]]; f.PutF1["\nGravityExtent: %g", [real[GGState.GetGravityExtent[ggData]]] ]; f.PutF1["\nGravityType: %g", [rope[GGUIUtility.GravityTypeToRope[GGState.GetGravityType[ggData]] ]] ]; defaults ¬ GGState.GetDefaults[ggData]; f.PutF1["\nDefaultFont: %g ", [rope[defaults.font.literal]] ]; GGParseOut.WriteFactoredTransformationVEC[f, defaults.font.transform]; f.PutF[" %g %g", [real[defaults.font.storedSize]], [real[defaults.font.designSize]] ]; f.PutRope["\nDefaults: "]; GGParseOut.WriteColor[f, defaults.fillColor]; f.PutChar[' ]; -- put a SPACE GGParseOut.WriteColor[f, defaults.strokeColor]; f.PutF1[" %g", [real[defaults.strokeWidth]] ]; f.PutF1[" %g", [rope[SELECT defaults.strokeJoint FROM bevel => "bevel", miter => "miter", ENDCASE => "round" ]] ]; f.PutF1[" %g", [rope[SELECT defaults.strokeEnd FROM square => "square", butt => "butt", ENDCASE => "round" ]] ]; f.PutRope["\nDashed: "]; GGParseOut.WriteBool[f, defaults.dashed]; IF defaults.dashed THEN { f.PutChar[' ]; -- put a SPACE GGParseOut.WriteArrayOfReal[f, defaults.pattern]; f.PutF[" %g %g", [real[defaults.offset]], [real[defaults.length]] ]; }; f.PutChar[' ]; -- put a SPACE f.PutRope["\nShadows: "]; GGParseOut.WriteColor[f, defaults.textColor]; GGParseOut.WriteBool[f, defaults.dropShadowOn]; IF defaults.dropShadowOn THEN { f.PutChar[' ]; -- put a SPACE GGParseOut.WritePoint[f, defaults.dropShadowOffset]; GGParseOut.WriteColor[f, defaults.dropShadowColor]; }; anchor ¬ GGState.GetAnchor[ggData]; f.PutRope["\nAnchor: "]; GGParseOut.WriteBool[f, IF anchor=NIL THEN FALSE ELSE anchor.exists]; IF anchor.exists THEN { f.PutChar[' ]; -- put a SPACE GGParseOut.WritePoint[f, anchor.point]; }; f.PutRope["\nPalette: "]; GGParseOut.WriteBool[f, GGState.GetPalette[ggData]]; f.PutRope["\nActive: "]; GGParseOut.WriteBool[f, GGState.GetActive[ggData]]; f.PutChar[IO.LF]; }; IsEndLine: PROC [c: CHAR] RETURNS [BOOL] = { RETURN[c=IO.CR OR c=IO.LF]; }; RegisterAllOptions[]; END. bGGFileImpl.mesa Copyright Σ 1987, 1989, 1991, 1992 by Xerox Corporation. All rights reserved. Pier, December 1, 1992 2:26 pm PST Kurlander, September 1, 1987 1:47:01 pm PDT Bier, December 3, 1992 5:27 pm PST Doug Wyatt, April 16, 1992 4:32 pm PDT Contents: Created October 12, 1987 by merging GGFileInImpl and GGFileOutImpl Treat as a special color. f.PutF[" %g]", [rope[special.name]]]; Filing in Loads any objects in f into ggData scene. Merges any alignment line types in f into the viewer represented by ggData. Changes other control panel state variables to options found in f if NOT alignmentsOnly. Read in the scene entities Ignores the control panel state variables options in f. Merges any objects in f into scene. Read in the Header Read in the alignment objects, etc. IF version >= 8607.17 THEN SkipOptions[f]; IF version >= 9207.29 THEN ReadSceneFields[f, version, scene]; Read the scene entities Merges any alignment line types in f into the viewer represented by ggData. Changes other control panel state variables to options found in f if NOT alignmentsOnly. Read in the Header Read in the options. Read in the Scene Fields that are Mentioned Initialize Some Options Read in the Options that are Mentioned A copy of List.Nconc1 for LIST OF Rope.ROPE instead of LIST OF REF ANY SIGNAL Problem[msg: Rope.Concat["Unknown keyword in gargoyle file: ", keyWord]]; SIGNAL Problem[msg: Rope.Concat["Unknown keyword in gargoyle file: ", keyWord]]; GGState.SetShowColors[ggData, on]; fixes NIL text color defaults in many older files. Added by Pier, July 17, 1991 ProcessOption[keyWord, option, ggData]; Filing out ΚΤ–(cedarcode) style•NewlineDelimiter ˜proc™Icodešœ ΟeœC™NL™"L™+L™"L™&L™LšΟbœD™LL™—šΟk ˜ JšœΚŸœ<˜ˆK˜—šΟn œŸœŸ˜JšŸœΣŸœ,˜ˆKšŸœŸ˜#—˜KšœŸœ˜#KšœŸœ˜%KšœŸœ˜Kšœ Ÿœ˜-Lšœ Ÿœ˜*Kšœ Ÿœ˜'KšœŸœ˜'KšœŸœ˜!KšŸœŸœŸœ˜KšœŸœ˜!Kšœ Ÿœ˜'KšœŸœ˜1KšœŸœ˜!Kšœ Ÿœ˜+KšœŸœ˜3Kšœ Ÿœ˜#Kšœ Ÿœ˜'KšœŸœ˜KšœŸœ'˜;KšœŸœ˜$K˜—KšœŸœŸœ˜;šœŸœŸœ˜)Lšœ Ÿœ˜LšœŸœΟc0˜HL˜L˜—KšœŸœŸœ˜1šœŸœŸœ˜$Lšœ&‘˜'LšœŸœ˜Lšœ‘˜2Lšœ‘˜0Lšœ‘?˜SL˜L˜—KšœŸœŸœ˜9šœŸœŸœ˜(LšœŸœ‘0˜IL˜L˜—KšœŸœŸœ˜/šœŸœŸœ˜#Lšœ%‘˜&LšœŸœ‘*˜DL˜—L˜L˜š œŸœŸœ˜BKšœŸœ˜KšœŸœ˜3K•StartOfExpansionE[mod: NAT _ 17, equal: RefTab.EqualProc, hash: RefTab.HashProc]˜2K˜(K˜,K˜#K˜K˜—KšœŸœŸœŸœ˜š  œŸœŸœ˜EKšœ ŸœŸœ˜&K˜*Kšœ˜K˜—š  œŸœ7Ÿœ ŸœŸœŸœ ˜…Kšœ Ÿœ-˜KšŸœŸœ$˜>Kšž™K˜#K˜K˜K˜9K˜3šŸœŸœŸœ Ÿ˜Kšœ ž œ Ÿœ ˜/LšŸœŸœŸœ#žœ˜[KšŸœ˜—LšŸœŸœ˜5Kšœ$˜$Kšœ&˜&šŸœŸœŸœ ŸœŸœ%Ÿœ ŸœŸ˜fKšœ;˜;KšŸœ˜—KšŸœŸœ ˜šŸ˜Kšœ ‘˜#—Kšœ˜K˜—š œŸœŸœŸœŸœ"ŸœŸœŸœŸœ ŸœŸœŸœ˜―Kšœ₯™₯šŸœŸ˜!Kšœ\Ÿœ<˜œKšŸœ˜ K˜—Kšœ Ÿœ˜Kšœ7ŸœŸœ Ÿœ˜cLšŸœŸœ)˜DKšŸœŸœ ˜šŸ˜Kšœ ‘˜#—K˜K˜—š œŸœŸœŸœ$ŸœŸœ Ÿœ˜}Kšž™K˜3K˜"K˜-K˜ Kšœ+‘+˜VKšž™Lšœ,˜,šŸœŸœ&˜@KšŸœ$ŸœŸœ˜3—Lšœ+˜+K˜K˜—š  œŸœŸœŸœ Ÿœ˜ELšœŸœ˜LšœŸœ˜ Lšœ Ÿœ˜Lšœ ŸœŸœ˜Jšž+™+šŸœ Ÿ˜L˜+šŸœŸœŸœ˜Lšœ Ÿœ ˜šŸœŸœ˜LšœŸœ ˜LšœŸœ˜L˜—LšŸœ˜—L˜(šŸœŸœŸœ˜Lšœ Ÿœ ˜šŸœŸœ˜LšœŸœ ˜LšœŸœ˜L˜—LšŸœ˜—L˜L˜3Lšœ Ÿœ ˜šŸœŸœ˜LšœŸœ ˜LšœŸœ˜L˜——LšŸœ˜L˜L˜—š   œŸœŸœŸœ Ÿœ'˜ULšœŸœ˜LšœŸœ˜ Lšœ Ÿœ˜Lšœ ŸœŸœ˜Jšž™LšœŸœ˜"Jšž&™&šŸœ Ÿ˜L˜+šŸœŸœŸœ˜Lšœ Ÿœ ˜šŸœŸœ˜LšœŸœ ˜LšœŸœ˜L˜—LšŸœ˜—L˜(šŸœŸœŸœ˜Lšœ Ÿœ ˜šŸœŸœ˜LšœŸœ ˜LšœŸœ˜L˜—LšŸœ˜—L˜L˜6Lšœ Ÿœ ˜šŸœŸœ˜LšœŸœ ˜LšœŸœ˜L˜——LšŸœ˜L˜L˜—š œŸœŸœŸœŸœŸœ ŸœŸœŸœ˜OKšœ ŸœŸœŸœ˜%Kšœ ŸœŸœŸœ˜%KšœŸœ˜*KšœŸœ˜,š  œŸœŸœŸœŸœŸœ˜LšŸœŸ˜KšŸ œŸœ Ÿœ ˜&Kš ŸœŸœŸœŸœ Ÿœ˜(KšŸœŸœ ˜—Kšœ˜—Kšœ Ÿœ˜KšœŸœŸœ˜KšœŸœŸœ˜ Kšœ Ÿœ˜šŸœŸœŸ˜ šœ‘œŸœ*˜;KšœŸœŸœŸœ˜+—Kš ŸœŸœŸœŸœŸœ˜!KšŸœŸœŸœŸœ˜<šŸœŸœ˜&Kšœ ˜ KšŸœ˜K˜—šŸœ Ÿœ˜(Kšœ ˜ KšŸœ˜K˜—K˜?KšŸœ˜—K˜K˜—š œŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜VJšœŸœŸœŸœ ŸœŸœŸœŸ™FLšœŸœŸœŸœ˜Lš ŸœŸœŸœŸœŸœŸœ˜'LšŸœ ŸœŸœ Ÿœ˜*Lšœ ŸœŸœ˜LšŸœ˜ Lšœ˜L˜—Lšœ Ÿœ/˜?L˜Lšœ ŸœŸœ˜%šœŸœŸœ˜LšœŸœ˜!Lšœ&Ÿœ˜*L˜L˜—L˜š   œŸœŸœŸœŸœ˜HLšœŸœ˜ LšœŸœ˜ L˜/LšŸœŸœŸœ˜'L˜L˜—š œŸœ˜š œŸœŸœ3˜SL˜Lšœ Ÿœ2Ÿœ˜TL˜1L˜—š œŸœŸœ%˜IL˜Lšœ ŸœŸœ,˜ZL˜1L˜—L˜ L˜>L˜JL˜NL˜2L˜2L˜2L˜2L˜4L˜@L˜4L˜6L˜8L˜@L˜8L˜2L˜6L˜L˜:L˜:L˜4L˜0L˜2L˜0L˜>L˜L˜—š œŸœŸœ Ÿœ˜ULšœŸœŸœ˜L˜L˜LšœŸœŸœ ˜L˜!šŸœŸœŸœ˜LšŸœJ™PL˜jL˜—šŸœ˜LšŸœŸœŸœ˜,L˜?L˜—L˜L˜—š  œŸœŸœ Ÿœ(˜fLšœŸœŸ˜L˜L˜LšŸœŸœŸœ˜LšœŸœŸœ ˜L˜!šŸœŸœŸœ˜LšŸœJ™PL˜jL˜—šŸœ˜Lš ŸœŸœŸœŸœŸœ˜GLš ŸœŸœŸœŸœŸœ˜GLš ŸœŸœŸœŸœŸœ˜?šŸœ˜LšŸœF˜JLšŸœ<˜@—L˜—L˜L˜—Lš œŸœŸœ Ÿœ ŸœŸœ Ÿœ˜gLš œŸœŸœ ŸœŸœŸœ Ÿœ˜iš œ˜+L˜LL˜8Lšœ˜—š œ˜1LšœŸœ$˜0LšŸœŸœŸœ˜0Lšœ˜—š œ˜3LšœŸœ$˜0LšŸœŸœŸœ˜0Lšœ˜—š œ˜%LšœŸœ$˜0LšŸœŸœŸœ˜0Lšœ˜—š œ˜%LšœŸœŸœŸœ*˜BL˜8š ŸœŸœŸœŸœŸœŸœŸ˜BLšœ?˜?LšŸœ˜—Lšœ˜—š  œ˜#šŸœŸœ‘˜8LšœŸœŸœŸœ˜LšœŸœŸœŸœ˜Lšœ‘œH˜MLšœ)˜)L˜—šŸœ‘˜Lšœ ŸœŸœŸœ˜Lšœ ŸœŸœŸœ˜LšœŸœ˜ L˜4L˜*š ŸœŸœŸœŸœŸœŸœŸ˜GL˜)L˜6LšŸœ˜—Lšœ%Ÿœ˜*L˜—Lšœ˜—š  œ˜#šŸœŸœ‘˜8LšœŸœŸœŸœ˜LšœŸœŸœŸœ˜Lšœ‘œH˜MLšœ)˜)L˜—šŸœ‘˜Lšœ ŸœŸœŸœ˜LšœŸœ˜ Lšœ ŸœŸœŸœ˜L˜4L˜*š ŸœŸœŸœŸœŸœŸœŸ˜GL˜)L˜6LšŸœ˜—Lšœ%Ÿœ˜*L˜—Lšœ˜—š  œ˜$šŸœŸœ‘˜9LšœŸœŸœŸœ˜LšœŸœŸœŸœ˜LšœŸœŸœŸœ˜L˜NLšœ1˜1L˜—šŸœ‘˜Lšœ ŸœŸœŸœ˜LšœŸœ˜ Lšœ ŸœŸœŸœ˜L˜4L˜*š ŸœŸœŸœŸœŸœŸœŸ˜GL˜)L˜6LšŸœ˜—LšœŸœ Ÿœ˜0Lšœ˜—Lšœ˜—š œ˜*šŸœŸœ‘$˜?LšœŸœŸœŸœ˜LšœŸœŸœŸœ˜LšœŸœŸœŸœ˜L˜NLšœ7˜7L˜—šŸœ‘˜"Lšœ ŸœŸœŸœ˜LšœŸœ˜ Lšœ ŸœŸœŸœ˜L˜4L˜*š ŸœŸœŸœŸœŸœŸœŸ˜GL˜)L˜6LšŸœ˜—Lšœ$Ÿœ Ÿœ˜6Lšœ˜—Lšœ˜—š  œ˜$Lšœ Ÿœ˜L˜,šŸœ Ÿœ˜L– [f: STREAM]˜1LšœŸœŸœ Ÿœ˜JL˜—LšŸœŸœ˜"Lšœ˜—š œ˜'LšœŸœ˜ L˜&Lšœž œ ˜!Lšœ˜—š œ˜(LšœŸœ˜ L˜&L˜"Lšœ˜—š œ˜,LšœŸœ˜ L˜&L˜&Lšœ˜—š œ˜(LšœŸœ˜ L˜&Jšœž œ ™"Lšœ˜—š œ˜%LšœŸœ˜ L˜&Lšœž œ ˜Lšœ˜—š œ˜'LšœŸœ$˜.LšŸœ Ÿœ ž œŸœ˜9Lšœ˜—š œ˜*Lšœ Ÿœ$˜3LšœH˜HLšœ˜—š œ˜+LšœŸœ$˜.Lšœžœ˜'Lšœ˜—š œ˜)Lšœ Ÿœ$˜3Lšœžœ0˜FLšœ˜—š œ˜)Lšœ.‘˜ILš œRŸœŸœŸœŸœŸœ˜±LšœŸœ‘-˜KLšœžœ˜)Lšœ˜—š œ˜&LšœŸœ˜"L˜GL˜IL˜?L˜/šœŸœŸœŸ˜.Lšœ!Ÿœ ˜1Lšœ!Ÿœ ˜1LšŸœ ˜—L˜-šœŸœŸœŸ˜,Lšœ Ÿœ ˜1LšœŸœ ˜-LšŸœ ˜—Lšœ˜—š  œ˜$LšœŸœ˜ L˜*L˜ šŸœŸœ˜ L– [f: STREAM]˜BL˜:L˜:L˜—Lšœ˜—š œ˜%Lšœ Ÿœ˜L˜GšŸœŸœŸœ*˜OL™P—L˜,L˜(šŸœŸœ˜&L– [f: STREAM]˜EL˜ML˜—Lšœ˜—š  œ˜$LšœŸœ˜ L˜*˜"Lšœ˜——š œ˜1L˜AL˜)Lšœ˜L˜—š  œŸœŸœŸœ˜$LšœŸœ˜LšœŸœ˜ Lšœ Ÿœ˜Lšœ ŸœŸœ˜šŸœ Ÿ˜L˜+šŸœŸœŸœ˜Lšœ Ÿœ ˜šŸœŸœ˜LšœŸœ ˜LšœŸœ˜L˜—LšŸœ˜—L˜(šŸœŸœŸœ˜Lšœ Ÿœ ˜šŸœŸœ˜LšœŸœ ˜LšœŸœ˜L˜—LšŸœ˜—L˜Jšœ'™'Lšœ Ÿœ ˜šŸœŸœ˜LšœŸœ ˜LšœŸœ˜L˜——LšŸœ˜L˜L˜—š   œŸœŸœŸœ Ÿœ%Ÿœ˜oKšœŸœ˜šŸœŸœ˜šŸœŸœ˜L˜Lšœ  œ˜FLšœV˜VL˜Lšœ-˜-Lšœ‘˜Lšœ/˜/L˜.šœŸœŸ˜5Lšœ˜Lšœ˜LšŸœ ˜Lšœ˜—šœŸœŸ˜3Lšœ˜Lšœ˜LšŸœ ˜Lšœ˜—K˜Lšœ)˜)šŸœŸœ˜Lšœ‘˜L˜1LšœD˜DL˜—Lšœ‘˜K˜Lšœ-˜-Lšœ/˜/šŸœŸœ˜Lšœ‘˜L˜4Lšœ3˜3L˜—L˜#L˜Lš œŸœŸœŸœŸœŸœ˜EšŸœŸœ˜Lšœ‘˜L˜'L˜—L˜Lšœ4˜4L˜L˜3Lšœ ŸœŸœ˜Kšœ˜K˜—š   œŸœŸœŸœŸœ˜,Lš ŸœŸœŸœŸœŸœŸœ˜L˜L˜—L˜L˜KšŸœ˜—…—op•¦