DIRECTORY Ascii, Atom, AtomButtons, BasicTime, --CombinePoly,-- CubicSplines, FS, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGError, GGEvent, GGFromImager, GGGravity, GGInterface, GGInterfaceTypes, GGModelTypes, GGMultiGravity, GGObjects, GGOutline, GGParseOut, GGRefresh, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGShapes, GGSlice, GGStatistics, --GGTouch,-- GGTraj, GGUtility, GGVector, GGWindow, Imager, ImagerInterpress, ImagerTransformation, Interpress, IO, IPMaster, MessageWindow, --PrincOpsUtils,-- RealFns, Random, Rope, Rosary, ViewerTools; GGEventImplA: CEDAR PROGRAM IMPORTS Atom, AtomButtons, BasicTime, --CombinePoly,-- FS, GGAlign, GGBoundBox, GGCaret, GGError, GGEvent, GGFromImager, GGGravity, GGInterface, GGMultiGravity, GGObjects, GGOutline, GGParseOut, GGRefresh, GGSegment, GGSelect, GGSequence, GGShapes, GGSlice, GGStatistics, --GGTouch,-- GGTraj, GGUtility, GGVector, GGWindow, Imager, ImagerInterpress, ImagerTransformation, Interpress, IO, MessageWindow, --PrincOpsUtils,-- Random, RealFns, Rope, ViewerTools EXPORTS GGEvent = BEGIN BoundBox: TYPE = GGBoundBox.BoundBox; Slice: TYPE = GGModelTypes.Slice; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; EntityGenerator: TYPE = GGModelTypes.EntityGenerator; FeatureData: TYPE = GGInterfaceTypes.FeatureData; GargoyleData: TYPE = GGInterfaceTypes.GargoyleData; Joint: TYPE = GGModelTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; ObjectBag: TYPE = GGGravity.ObjectBag; OutlineDescriptor: TYPE = GGModelTypes.OutlineDescriptor; TriggerBag: TYPE = GGGravity.TriggerBag; Outline: TYPE = GGModelTypes.Outline; Point: TYPE = GGBasicTypes.Point; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SegAndIndex: TYPE = GGSequence.SegAndIndex; Sequence: TYPE = GGModelTypes.Sequence; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = GGModelTypes.Traj; TrajGenerator: TYPE = GGModelTypes.TrajGenerator; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator; Vector: TYPE = GGBasicTypes.Vector; NotYetImplemented: PUBLIC SIGNAL = CODE; EnterEditingMode: PROC [gargoyleData: GargoyleData, refChar: REF CHAR] = { IF refChar^=Ascii.BS THEN { -- try to enter editing of a single selected text slice sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; sliceDesc: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF sliceDesc=NIL OR sliceDesc.slice.class.type#$Text OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN { GGError.AppendHerald[gargoyleData.feedback, "Select a single text slice for editing", oneLiner]; GGError.Blink[gargoyleData.feedback]; RETURN; }; GGSelect.DeselectSlice[slice: sliceDesc.slice, parts: sliceDesc.parts, scene: gargoyleData.scene, selectClass: normal]; gargoyleData.refresh.textInProgress _ sliceDesc.slice; -- successfully enter editing mode GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; } ELSE { slice: Slice _ gargoyleData.refresh.textInProgress _ NewTextSlice[text: Rope.FromChar[refChar^], gargoyleData: gargoyleData]; -- start a new text slice IF slice=NIL THEN RETURN; -- can't start a string with whitespace gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; gargoyleData.refresh.addedObject _ slice; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; }; -- end EnterEditingMode AddChar: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; slice: Slice; obBox: BoundBox; refChar: REF CHAR; IF NARROW[event.first, ATOM]#$AddChar THEN ERROR; refChar _ NARROW[event.rest.first]; IF gargoyleData.refresh.textInProgress=NIL THEN { GGCaret.NoAttractor[gargoyleData.caret]; -- so the feedback goes away. EnterEditingMode[gargoyleData, refChar]; RETURN; } ELSE { -- add to textInProgress GGStatistics.StartInterval[$AddChar, GGStatistics.GlobalTable[]]; slice _ gargoyleData.refresh.textInProgress; obBox _ GGBoundBox.CopyBoundBox[slice.boundBox]; -- remember old bound box for now IF refChar^=Ascii.BS THEN { GGSlice.BackspaceText[slice: slice]; gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; -- bBox was updated by GGSlice GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: obBox]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; } ELSE { GGSlice.AppendText[slice: slice, text: Rope.FromChar[refChar^] ]; gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; -- boundBox was updated by GGSlice GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: obBox]; gargoyleData.refresh.addedObject _ slice; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; GGStatistics.StopInterval[$AddChar, GGStatistics.GlobalTable[]]; }; }; -- end AddChar NewTextSlice: PRIVATE PROC [text: Rope.ROPE, gargoyleData: GargoyleData] RETURNS [slice: Slice] = { caretPos: Point _ GGCaret.GetPoint[gargoyleData.caret]; success: BOOL; fontName, problem: Rope.ROPE; slice _ GGSlice.MakeTextSlice[text, Imager.black, 1.0]; [fontName, ----, ----, problem] _ GGUtility.FontDataFromUserData[defaultPrefix, defaultFamily, defaultFace, 1.0, defaultPreferredSize]; IF problem#NIL THEN ERROR; success _ GGSlice.SetTextFont[slice, defaultPrefix, defaultFamily, defaultFace, fontName, 1.0, defaultPreferredSize, ImagerTransformation.TranslateTo[defaultTransform, caretPos], gargoyleData.feedback]; IF NOT success THEN ERROR; GGObjects.AddSlice[gargoyleData.scene, slice, -1]; gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; GGSelect.DeselectAll[gargoyleData.scene, normal]; -- speeds up text entry gargoyleData.refresh.addedObject _ slice; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; AddText: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; text: Rope.ROPE _ IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]; [] _ NewTextSlice[text, gargoyleData]; }; AmplifySpaceFromSelection: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; amplifySpace: REAL _ NARROW[event.rest.first, REF REAL]^; slices: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; gargoyleData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]^; -- remember original bound box FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[slices], GGSelect.NextSliceDescriptor[slices] UNTIL sliceD=NIL DO GGSlice.SetTextAmplifySpace[sliceD.slice, amplifySpace, gargoyleData.feedback]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; -- GGSlice.SetTextAmplifySpace can post error messages }; -- end AmplifySpaceFromSelection PrintAmplifySpace: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; sliceDesc: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF sliceDesc=NIL OR sliceDesc.slice.class.type#$Text OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN { GGError.AppendHerald[gargoyleData.feedback, "Select the single text slice whose amplify space need", oneLiner]; GGError.Blink[gargoyleData.feedback]; } ELSE GGError.PutF[gargoyleData.feedback, oneLiner, "Amplify space: %g", [real[GGSlice.GetTextAmplifySpace[sliceDesc.slice]]]]; }; DropShadowOn: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; offsetX: REAL _ NARROW[event.rest.first, REF REAL]^; offsetY: REAL _ NARROW[event.rest.rest.first, REF REAL]^; slices: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; gargoyleData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]^; -- remember original bound box FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[slices], GGSelect.NextSliceDescriptor[slices] UNTIL sliceD=NIL DO GGSlice.DropShadowOn[sliceD.slice, [offsetX, offsetY]]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; DropShadowOff: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; slices: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; gargoyleData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]^; -- remember original bound box FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[slices], GGSelect.NextSliceDescriptor[slices] UNTIL sliceD=NIL DO GGSlice.DropShadowOff[sliceD.slice]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; SetFontAux: PROC [clientData: REF ANY, prefix: Rope.ROPE, family: Rope.ROPE, face: Rope.ROPE, fontName: Rope.ROPE, size: REAL, preferredSize: REAL, transform: ImagerTransformation.Transformation] = { gargoyleData: GargoyleData _ NARROW[clientData]; slices: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; gargoyleData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]^; -- remember original bound box FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[slices], GGSelect.NextSliceDescriptor[slices] UNTIL sliceD=NIL DO [] _ GGSlice.SetTextFont[sliceD.slice, prefix, family, face, fontName, size, preferredSize, transform, gargoyleData.feedback]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; -- GGSlice.SetTextFont can post error messages }; SetLookAlikes: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; lastDesc: SliceDescriptor; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO lastDesc _ sliceD; -- find the LAST selected slice ENDLOOP; BEGIN IF lastDesc=NIL OR lastDesc.slice.class.type#$Text THEN GOTO BadLastSlice ELSE { prefix, family, face, fontName, problem: Rope.ROPE; transform: ImagerTransformation.Transformation; preferredSize: REAL _ -1.0; fontRope: Rope.ROPE _ GGSlice.GetFontValuesRope[lastDesc.slice]; [prefix, family, face, transform, preferredSize] _ GGSlice.GetFontValues[lastDesc.slice]; [fontName, ----, ----, problem] _ GGUtility.FontDataFromUserData[prefix, family, face, 1.0, preferredSize]; IF problem#NIL THEN ERROR; gargoyleData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]^; -- remember original bound box sliceDescGen _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO [] _ GGSlice.SetTextFont[sliceD.slice, prefix, family, face, fontName, 1.0, preferredSize, ImagerTransformation.TranslateTo[transform, ImagerTransformation.Factor[GGSlice.GetFontValues[sliceD.slice].transform].t ], gargoyleData.feedback]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Text slices with font %g selected", [rope[fontRope]] ]; }; EXITS BadLastSlice => { GGError.AppendHerald[gargoyleData.feedback, "Select a final text slice for Font Look Alike", oneLiner]; GGError.Blink[gargoyleData.feedback]; }; END; }; ChangeFont: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; fail: BOOL _ FALSE; newFamily, newFace: Rope.ROPE; prefix, family, face, fontName, problem: Rope.ROPE; transform: ImagerTransformation.Transformation; preferredSize: REAL _ -1.0; fontRope: Rope.ROPE; inStream: IO.STREAM _ IO.RIS[IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]]; [fail, ----, newFamily, newFace, ----, ----] _ GGUtility.ParseFontData[inStream, FALSE, TRUE, TRUE]; BEGIN IF fail THEN GOTO ParseError ELSE { sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; gargoyleData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]^; -- remember original bound box FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO IF sliceD.slice.class.type#$Text THEN LOOP; [prefix, family, face, transform, preferredSize] _ GGSlice.GetFontValues[sliceD.slice]; [fontName, ----, ----, problem] _ GGUtility.FontDataFromUserData[prefix, newFamily, newFace, 1.0, preferredSize]; IF problem#NIL THEN GOTO FontNameProblem; [] _ GGSlice.SetTextFont[sliceD.slice, prefix, newFamily, newFace, fontName, 1.0, preferredSize, transform, gargoyleData.feedback]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Text slices with font %g selected", [rope[fontRope]] ]; }; EXITS ParseError => GGError.PutF[gargoyleData.feedback, oneLiner, "Font Parse Error: %g%g", [rope[family]], [rope[face]] ]; FontNameProblem => { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.PutF[gargoyleData.feedback, oneLiner, "%g", [rope[problem]] ]; }; END; }; SetPressFont: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { inStream: IO.STREAM _ IO.RIS[NARROW[event.rest.first]]; -- "Cream-BI 12" fail: BOOL _ FALSE; size: REAL _ 1.0; prefix, family, face, fontName, problem: Rope.ROPE; prefix _ "xerox/pressfonts/"; -- default prefix for SetPressFont BEGIN [fail, ----, family, face, ----, size] _ GGUtility.ParseFontData[inStream, FALSE, TRUE, TRUE, FALSE, TRUE]; IF fail THEN GOTO ParseError ELSE { [fontName, ----, ----, problem] _ GGUtility.FontDataFromUserData[prefix, family, face, 1.0, size]; IF problem#NIL THEN GOTO FontNameProblem; SetFontAux[clientData, prefix, family, face, fontName, size, 1.0, NIL]; }; EXITS FontNameProblem => { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.PutF[gargoyleData.feedback, oneLiner, "%g", [rope[problem]] ]; }; ParseError => { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.PutF[gargoyleData.feedback, oneLiner, "Font Parse Error: %g%g%g %g", [rope[prefix]], [rope[family]], [rope[face]], [real[size]] ]; }; END; }; SetPrintFont: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { inStream: IO.STREAM _ IO.RIS[NARROW[event.rest.first]]; -- "Modern-BI 23" fail: BOOL _ FALSE; size: REAL _ -1.0; prefix, family, face, fontName, problem: Rope.ROPE; prefix _ "xerox/xc1-2-2/"; -- default prefix for SetPrintFont BEGIN [fail, ----, family, face, ----, size] _ GGUtility.ParseFontData[inStream, FALSE, TRUE, TRUE, FALSE, TRUE]; IF fail THEN GOTO ParseError ELSE { [fontName, ----, ----, problem] _ GGUtility.FontDataFromUserData[prefix, family, face, 1.0, size]; IF problem#NIL THEN GOTO FontNameProblem; SetFontAux[clientData, prefix, family, face, fontName, size, 1.0, NIL]; }; EXITS FontNameProblem => { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.PutF[gargoyleData.feedback, oneLiner, "%g", [rope[problem]] ]; }; ParseError => { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.PutF[gargoyleData.feedback, oneLiner, "Font Parse Error: %g%g%g %g", [rope[prefix]], [rope[family]], [rope[face]], [real[size]] ]; }; END; }; SetScreenFont: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { prefix, family, face, fontName, problem: Rope.ROPE; preferredSize: REAL _ -1.0; inStream: IO.STREAM _ IO.RIS[NARROW[event.rest.first]]; -- "Cream-BI 12" fail: BOOL _ FALSE; BEGIN prefix _ "xerox/tiogafonts/"; -- default prefix for SetScreenFont [fail, ----, family, face, ----, preferredSize] _ GGUtility.ParseFontData[inStream, FALSE, TRUE, TRUE, FALSE, TRUE]; IF fail THEN GOTO ParseError ELSE { [fontName, ----, ----, problem] _ GGUtility.FontDataFromUserData[prefix, family, face, 1.0, preferredSize]; IF problem#NIL THEN GOTO FontNameProblem; SetFontAux[clientData, prefix, family, face, fontName, preferredSize, preferredSize, NIL]; }; EXITS ParseError => { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.PutF[gargoyleData.feedback, oneLiner, "Font Parse Error: %g%g%g %g", [rope[prefix]], [rope[family]], [rope[face]], [real[preferredSize]] ]; }; FontNameProblem => { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.PutF[gargoyleData.feedback, oneLiner, "%g", [rope[problem]] ]; }; END; }; SetFontDetailed: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { fail: BOOL _ FALSE; prefix, family, face, fontName, problem: Rope.ROPE; transform: ImagerTransformation.Transformation; preferredSize: REAL _ -1.0; inStream: IO.STREAM _ IO.RIS[NARROW[event.rest.first]]; [fail, prefix, family, face, transform, preferredSize] _ GGUtility.ParseFontData[inStream, TRUE, TRUE, TRUE, TRUE, TRUE]; BEGIN IF fail THEN { gargoyleData: GargoyleData _ NARROW[clientData]; scratch: IO.STREAM _ IO.ROS[]; IF transform#NIL THEN GGParseOut.WriteFactoredTransformation[scratch, transform]; GGError.PutF[gargoyleData.feedback, oneLiner, "Font Parse Error: %g%g%g %g %g", [rope[prefix]], [rope[family]], [rope[face]], [rope[IO.RopeFromROS[scratch]]], [real[preferredSize]] ]; } ELSE { [fontName, ----, ----, problem] _ GGUtility.FontDataFromUserData[prefix, family, face, 1.0, preferredSize]; IF problem#NIL THEN GOTO FontNameProblem; SetFontAux[clientData, prefix, family, face, fontName, 1.0, preferredSize, transform]; }; EXITS FontNameProblem => { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.PutF[gargoyleData.feedback, oneLiner, "%g", [rope[problem]] ]; }; END; }; SetFontLiteral: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {}; SetDefaultFontValues: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; sliceDesc: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF sliceDesc=NIL OR sliceDesc.slice.class.type#$Text OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN { GGError.AppendHerald[gargoyleData.feedback, "Select a single text slice for default font setting", oneLiner]; GGError.Blink[gargoyleData.feedback]; } ELSE { [defaultPrefix, defaultFamily, defaultFace, defaultTransform, defaultPreferredSize] _ GGSlice.GetFontValues[sliceDesc.slice]; GGError.PutF[gargoyleData.feedback, oneLiner, "New Default Font: %g", [rope[GGSlice.GetFontValuesRope[sliceDesc.slice]]] ]; }; }; ShowFontValues: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; sliceDesc: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF sliceDesc=NIL OR sliceDesc.slice.class.type#$Text OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN { GGError.AppendHerald[gargoyleData.feedback, "Select a single text slice for font printing", oneLiner]; GGError.Blink[gargoyleData.feedback]; } ELSE GGError.PutF[gargoyleData.feedback, oneLiner, "Font Values: %g", [rope[GGSlice.GetFontValuesRope[sliceDesc.slice]]] ]; }; ShowFontValuesLiteral: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {}; ShowDefaultFontValues: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; scratch: IO.STREAM _ IO.ROS[]; GGParseOut.WriteFactoredTransformation[scratch, defaultTransform]; GGError.PutF[gargoyleData.feedback, oneLiner, "Default Font Values: %g %g%g %g %g", [rope[defaultPrefix]], [rope[defaultFamily]], [rope[defaultFace]], [rope[IO.RopeFromROS[scratch]]], [real[defaultPreferredSize]] ]; }; FindTextMatchingSelected: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; sliceDesc: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF sliceDesc=NIL OR sliceDesc.slice.class.type#$Text OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN { GGError.AppendHerald[gargoyleData.feedback, "Select a single text slice for font matching", oneLiner]; GGError.Blink[gargoyleData.feedback]; } ELSE { -- select all the text slices with matching fonts prefix, family, face: Rope.ROPE; transform: ImagerTransformation.Transformation; preferredSize: REAL _ -1.0; [prefix, family, face, transform, preferredSize] _ GGSlice.GetFontValues[sliceDesc.slice]; SelectMatchingFonts[prefix, family, face, transform, preferredSize, GGSlice.GetFontValuesRope[sliceDesc.slice], gargoyleData]; }; }; FindTextMatchingValues: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; matchRope: Rope.ROPE _ NARROW[event.rest.first]; IF Rope.Equal[matchRope, ""] THEN { GGError.AppendHerald[gargoyleData.feedback, "Select font values for matching", oneLiner]; } ELSE { prefix, family, face: Rope.ROPE; transform: ImagerTransformation.Transformation; preferredSize: REAL _ -1.0; fail: BOOL; [fail, prefix, family, face, transform, preferredSize] _ GGUtility.ParseFontData[IO.RIS[matchRope], TRUE, TRUE, TRUE, TRUE, TRUE]; -- parse the match rope into values IF fail THEN GGError.AppendHerald[gargoyleData.feedback, "Ill formed font values for matching", oneLiner] ELSE SelectMatchingFonts[prefix, family, face, transform, preferredSize, matchRope, gargoyleData]; }; }; SelectMatchingFonts: PROC [prefix, family, face: Rope.ROPE, transform: ImagerTransformation.Transformation, preferredSize: REAL, fontRope: Rope.ROPE, gargoyleData: GargoyleData] = { maxPixels: REAL _ 10000.0; sliceGen: SliceGenerator _ GGObjects.SlicesInScene[gargoyleData.scene]; GGSelect.DeselectAll[gargoyleData.scene, normal]; FOR slice: Slice _ GGObjects.NextSlice[sliceGen], GGObjects.NextSlice[sliceGen] UNTIL slice=NIL DO prefixS, familyS, faceS: Rope.ROPE; transformS: ImagerTransformation.Transformation; preferredSizeS: REAL; [prefixS, familyS, faceS, transformS, preferredSizeS] _ GGSlice.GetFontValues[slice]; IF Rope.Equal[prefix, prefixS, FALSE] AND Rope.Equal[family, familyS, FALSE] AND Rope.Equal[face, faceS, FALSE] AND RealFns.AlmostEqual[preferredSize, preferredSizeS, -9] AND --ImagerTransformation.--CloseToTranslation[transform, transformS, maxPixels] THEN GGSelect.SelectSlice[slice: slice, parts: slice.class.newParts[slice, NIL, topLevel], scene: gargoyleData.scene, selectClass: normal]; ENDLOOP; GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Text slices with font %g selected", [rope[fontRope]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; FindTextMatchingFamily: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; matchRope: Rope.ROPE _ NARROW[event.rest.first]; IF Rope.Equal[matchRope, ""] THEN { GGError.AppendHerald[gargoyleData.feedback, "Select font family for matching", oneLiner]; RETURN; } ELSE { sliceGen: SliceGenerator _ GGObjects.SlicesInScene[gargoyleData.scene]; GGSelect.DeselectAll[gargoyleData.scene, normal]; FOR slice: Slice _ GGObjects.NextSlice[sliceGen], GGObjects.NextSlice[sliceGen] UNTIL slice=NIL DO IF Rope.Equal[matchRope, GGSlice.GetFontValues[slice].family, FALSE] THEN GGSelect.SelectSlice[slice: slice, parts: slice.class.newParts[slice, NIL, topLevel], scene: gargoyleData.scene, selectClass: normal]; ENDLOOP; GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Text slices with font %g selected", [rope[matchRope]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; }; ReadIP: PROC [ipName: Rope.ROPE, clientData: REF ANY, opName: Rope.ROPE] RETURNS [scene: Scene _ NIL] = { ReadMaster: PROC [context: Imager.Context] = { Imager.ScaleT[context, 2834.646]; -- pointsPerMeter=2834.646 Interpress.DoPage[master: ipmaster, page: 1, context: context, log: NIL]; }; gargoyleData: GargoyleData _ NARROW[clientData]; ipmaster: Interpress.Master _ NIL; fullName: Rope.ROPE; startTime, endTime: BasicTime.GMT; totalTime: INT; success: BOOL _ FALSE; [fullName, success] _ GGUtility.GetInterpressFileName[ipName, gargoyleData.currentWDir, gargoyleData.feedback]; IF NOT success THEN RETURN; [ipmaster, success] _ GGUtility.OpenInterpressOrComplain[gargoyleData.feedback, fullName]; IF NOT success THEN RETURN; GGError.PutF[gargoyleData.feedback, begin, "%g %g . . . ", [rope[opName]], [rope[fullName]]]; startTime _ BasicTime.Now[]; scene _ GGFromImager.Capture[action: ReadMaster ! GGFromImager.Warning => { GGError.Append[gargoyleData.feedback, message, oneLiner]; RESUME; };]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; GGError.PutF[gargoyleData.feedback, end, " Done in time (%r)", [integer[totalTime]]]; }; MergeIPEditable: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; ipName: Rope.ROPE _ NARROW[event.rest.first]; scene: Scene _ ReadIP[ipName, clientData, "MergeIPEditable"]; IF scene = NIL THEN RETURN; gargoyleData.scene _ GGObjects.MergeScenes[back: gargoyleData.scene, front: scene]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; MergeIPSlice: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { slice: Slice; gargoyleData: GargoyleData _ NARROW[clientData]; shortName, fullName: Rope.ROPE; startTime, endTime: BasicTime.GMT; totalTime: INT; ipMaster: Interpress.Master; success: BOOL; shortName _ NARROW[event.rest.first]; [fullName, success] _ GGUtility.GetInterpressFileName[shortName, gargoyleData.currentWDir, gargoyleData.feedback]; IF NOT success THEN RETURN; [ipMaster, success] _ GGUtility.OpenInterpressOrComplain[gargoyleData.feedback, fullName]; IF NOT success THEN RETURN; GGError.PutF[gargoyleData.feedback, begin, "MergeIPSlice %g . . . ", [rope[fullName]]]; startTime _ BasicTime.Now[]; slice _ GGSlice.MakeIPSliceFromMaster[ipMaster, 2834.646, fullName, gargoyleData.feedback, NIL, NIL, FALSE]; IF slice = NIL THEN RETURN; GGObjects.AddSlice[gargoyleData.scene, slice, -1]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; GGError.PutF[gargoyleData.feedback, end, " Done in time (%r)", [integer[totalTime]]]; gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; -- end MergeIPSlice IncludeIPByReference: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: GGSelect.SliceDescriptorGenerator; sliceDescGen _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: GGSelect.SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO IF sliceD.slice.class.type = $IP THEN GGSlice.SetIncludeByValue[sliceD.slice, FALSE]; ENDLOOP; }; IncludeIPByValue: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: GGSelect.SliceDescriptorGenerator; sliceDescGen _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: GGSelect.SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO IF sliceD.slice.class.type = $IP THEN GGSlice.SetIncludeByValue[sliceD.slice, TRUE]; ENDLOOP; }; ShowIPIncludeMode: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: GGSelect.SliceDescriptorGenerator; includeByValue: BOOL; sliceDescGen _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: GGSelect.SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO IF sliceD.slice.class.type = $IP THEN { includeByValue _ GGSlice.GetIncludeByValue[sliceD.slice]; IF includeByValue THEN GGError.Append[gargoyleData.feedback, "include by value", oneLiner] ELSE GGError.Append[gargoyleData.feedback, "include by reference", oneLiner]; RETURN; }; ENDLOOP; GGError.Append[gargoyleData.feedback, "ShowIPIncludeMode: No IP Slices selected", oneLiner]; GGError.Blink[gargoyleData.feedback]; }; ToIPAux: PROC [gargoyleData: GargoyleData, ipName: Rope.ROPE, actionAtom: ATOM, makeInterpress: PROC [dc: Imager.Context]] = { ipRef: ImagerInterpress.Ref; fullName: Rope.ROPE; success: BOOL; startTime: BasicTime.GMT; endTime: BasicTime.GMT; totalTime: INT; msgRope: Rope.ROPE; [fullName, success] _ GGUtility.GetInterpressFileName[ipName, gargoyleData.currentWDir, gargoyleData.feedback]; IF NOT success THEN RETURN; ipRef _ ImagerInterpress.Create[fullName]; [fullName] _ FS.FileInfo[fullName]; -- append the version number msgRope _ IO.PutFR["%g %g . . . ", [rope[Atom.GetPName[actionAtom]]], [rope[fullName]]]; GGError.Append[gargoyleData.feedback, msgRope, begin]; startTime _ BasicTime.Now[]; ImagerInterpress.DoPage[ipRef, makeInterpress, 1.0]; ImagerInterpress.Close[ipRef]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; msgRope _ IO.PutFR[" Done in time (%r)", [integer[totalTime]]]; GGError.Append[gargoyleData.feedback, msgRope, end]; GGEvent.SawTextFinish[NIL, gargoyleData]; }; ToIP: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { DoMakeInterpress: PROC [dc: Imager.Context] = { tempQuality: GGInterfaceTypes.QualityMode; tempStyle: GGInterfaceTypes.DisplayStyle; pixelsPerMeter: REAL = 0.0254/72.0; tempQuality _ gargoyleData.camera.quality; gargoyleData.camera.quality _ quality; tempStyle _ gargoyleData.camera.displayStyle; gargoyleData.camera.displayStyle _ print; Imager.ScaleT[dc, pixelsPerMeter]; GGRefresh.InterpressEntireScene[dc, gargoyleData]; gargoyleData.camera.quality _ tempQuality; gargoyleData.camera.displayStyle _ tempStyle; }; gargoyleData: GargoyleData _ NARROW[clientData]; ipName: Rope.ROPE _ NARROW[event.rest.first]; ToIPAux[gargoyleData, ipName, NARROW[event.first], DoMakeInterpress]; }; ToIPTestGravity: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { DoMakeInterpress: PROC [dc: Imager.Context] = { tempQuality: GGInterfaceTypes.QualityMode; tempStyle: GGInterfaceTypes.DisplayStyle; pixelsPerMeter: REAL = 0.0254/72.0; tempQuality _ gargoyleData.camera.quality; gargoyleData.camera.quality _ quality; tempStyle _ gargoyleData.camera.displayStyle; gargoyleData.camera.displayStyle _ print; Imager.ScaleT[dc, pixelsPerMeter]; TestGravity2[dc, gargoyleData]; gargoyleData.camera.quality _ tempQuality; gargoyleData.camera.displayStyle _ tempStyle; }; gargoyleData: GargoyleData _ NARROW[clientData]; ipName: Rope.ROPE _ NARROW[event.rest.first]; ToIPAux[gargoyleData, ipName, NARROW[event.first], DoMakeInterpress]; }; ToIPScreen: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { DoMakeInterpress: PROC [dc: Imager.Context] = { tempQuality: GGInterfaceTypes.QualityMode; tempStyle: GGInterfaceTypes.DisplayStyle; pixelsPerMeter: REAL = 0.0254/72.0; tempQuality _ gargoyleData.camera.quality; gargoyleData.camera.quality _ quality; tempStyle _ gargoyleData.camera.displayStyle; gargoyleData.camera.displayStyle _ screen; Imager.ScaleT[dc, pixelsPerMeter]; GGRefresh.InterpressEntireScene[dc, gargoyleData]; gargoyleData.camera.quality _ tempQuality; gargoyleData.camera.displayStyle _ tempStyle; }; gargoyleData: GargoyleData _ NARROW[clientData]; ipName: Rope.ROPE _ NARROW[event.rest.first]; ToIPAux[gargoyleData, ipName, NARROW[event.first], DoMakeInterpress]; }; ToIPLit: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { DoMakeInterpress: PROC [dc: Imager.Context] = { switched: BOOL _ FALSE; tempQuality _ gargoyleData.camera.quality; gargoyleData.camera.quality _ showall; tempStyle _ gargoyleData.camera.displayStyle; gargoyleData.camera.displayStyle _ print; IF AtomButtons.GetButtonState[gargoyleData.refresh.showColors] = off THEN { GGEvent.ToggleShowColors[NIL, gargoyleData]; switched _ TRUE; }; Imager.ScaleT[dc, pixelsPerMeter]; GGRefresh.PaintEntireScene[dc, gargoyleData]; gargoyleData.camera.quality _ tempQuality; gargoyleData.camera.displayStyle _ tempStyle; IF switched THEN GGEvent.ToggleShowColors[NIL, gargoyleData]; }; gargoyleData: GargoyleData _ NARROW[clientData]; ipRef: ImagerInterpress.Ref; fullName: Rope.ROPE; success: BOOL; pixelsPerMeter: REAL = 0.0254/72.0; startTime: BasicTime.GMT; endTime: BasicTime.GMT; totalTime: INT; msgRope: Rope.ROPE; tempQuality: GGInterfaceTypes.QualityMode; tempStyle: GGInterfaceTypes.DisplayStyle; ipName: Rope.ROPE _ "litshot.ip"; [fullName, success] _ GGUtility.GetInterpressFileName[ipName, gargoyleData.currentWDir, gargoyleData.feedback]; IF NOT success THEN RETURN; ipRef _ ImagerInterpress.Create[fullName]; msgRope _ IO.PutFR["Writing to IP file: %g . . . ", [rope[fullName]]]; GGError.Append[gargoyleData.feedback, msgRope, begin]; startTime _ BasicTime.Now[]; ImagerInterpress.DoPage[ipRef, DoMakeInterpress, 1.0]; ImagerInterpress.Close[ipRef]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; msgRope _ IO.PutFR[" Done in time (%r)", [integer[totalTime]]]; GGError.Append[gargoyleData.feedback, msgRope, end]; GGEvent.SawTextFinish[NIL, gargoyleData]; }; TestGravity2: PUBLIC PROC [dc: Imager.Context, gargoyleData: GargoyleData] = { xRandomStream, yRandomStream: Random.RandomStream; testPoint: Point; x, y: INT; totalCount, multiHitCount, uniHitCount, diffCount: NAT _ 0; uniPoint, multiPoint: Point; uniFeature, multiFeature: FeatureData; currentObjects: ObjectBag; sceneObjects: TriggerBag; xRandomStream _ Random.Create[gargoyleData.actionArea.cw]; yRandomStream _ Random.Create[gargoyleData.actionArea.ch]; GGAlign.SetBagsForAction[gargoyleData, $CaretPos]; gargoyleData.aborted[gravitytest] _ FALSE; -- in case there was one left over from prior abort UNTIL totalCount > 1000 DO IF gargoyleData.aborted[gravitytest] THEN { gargoyleData.aborted[gravitytest] _ FALSE; EXIT; }; x _ Random.NextInt[xRandomStream]; y _ Random.NextInt[yRandomStream]; testPoint _ [x, y]; testPoint _ GGWindow.ViewerToWorld[viewerPoint: testPoint, gargoyleData: gargoyleData]; gargoyleData.refresh.spotPoint _ testPoint; currentObjects _ gargoyleData.hitTest.currentObjectBag; sceneObjects _ gargoyleData.hitTest.sceneTriggerBag; [uniPoint, uniFeature] _ GGGravity.UniMap[testPoint, gargoyleData.hitTest.tolerance, currentObjects, sceneObjects, gargoyleData, TRUE]; [multiPoint, multiFeature] _ GGMultiGravity.Map[testPoint, gargoyleData.hitTest.tolerance, currentObjects, sceneObjects, gargoyleData, TRUE]; IF uniFeature = NIL AND multiFeature = NIL THEN { PaintSpot[dc, gargoyleData]; totalCount _ totalCount + 1; LOOP; }; IF uniFeature = NIL OR multiFeature = NIL OR uniFeature # multiFeature OR uniFeature.resultType # multiFeature.resultType OR uniPoint # multiPoint THEN { ReportResultsAndPaint2[dc, testPoint, uniPoint, uniFeature, multiPoint, multiFeature, gargoyleData]; totalCount _ totalCount + 1; diffCount _ diffCount + 1; IF multiFeature # NIL THEN multiHitCount _ multiHitCount + 1; IF uniFeature # NIL THEN uniHitCount _ uniHitCount + 1; } ELSE { multiHitCount _ multiHitCount + 1; uniHitCount _ uniHitCount + 1; totalCount _ totalCount + 1; gargoyleData.refresh.hitPoint _ multiPoint; PaintHitLine[dc, gargoyleData]; }; ENDLOOP; GGError.PutF[gargoyleData.feedback, oneLiner, "Tested %g total points. %g unihits. %g multihits. %g differences", [integer[totalCount]], [integer[uniHitCount]], [integer[multiHitCount]], [integer[diffCount]]]; }; -- end TestGravity2 PaintSpot: PROC [screen: Imager.Context, gargoyleData: GargoyleData] = { Imager.SetColor[screen, Imager.black]; GGShapes.DrawSpot[screen, gargoyleData.refresh.spotPoint]; }; PaintOddHitLine: PROC [screen: Imager.Context, gargoyleData: GargoyleData] = { Imager.SetColor[screen, Imager.black]; Imager.SetStrokeEnd[screen, round]; Imager.MaskVector[screen, [gargoyleData.refresh.spotPoint.x, gargoyleData.refresh.spotPoint.y], [gargoyleData.refresh.hitPoint.x, gargoyleData.refresh.hitPoint.y]]; GGShapes.DrawCP[screen, gargoyleData.refresh.spotPoint]; }; PaintHitLine: PROC [screen: Imager.Context, gargoyleData: GargoyleData] = { Imager.SetColor[screen, Imager.black]; Imager.SetStrokeEnd[screen, round]; Imager.MaskVector[screen, [gargoyleData.refresh.spotPoint.x, gargoyleData.refresh.spotPoint.y], [gargoyleData.refresh.hitPoint.x, gargoyleData.refresh.hitPoint.y]]; GGShapes.DrawFilledRect[screen, gargoyleData.refresh.spotPoint, 3.0]; }; ReportResultsAndPaint2: PROC [dc: Imager.Context, testPoint: Point, uniPoint: Point, uniFeature: FeatureData, multiPoint: Point, multiFeature: FeatureData, gargoyleData: GargoyleData] = { IF uniFeature = NIL THEN { gargoyleData.refresh.hitPoint _ multiPoint; } ELSE IF multiFeature = NIL THEN { gargoyleData.refresh.hitPoint _ uniPoint; } ELSE IF uniFeature # multiFeature THEN { gargoyleData.refresh.hitPoint _ multiPoint; } ELSE IF uniFeature.resultType # multiFeature.resultType THEN { gargoyleData.refresh.hitPoint _ multiPoint; } ELSE IF uniPoint # multiPoint THEN { gargoyleData.refresh.hitPoint _ uniPoint; PaintOddHitLine[dc, gargoyleData]; gargoyleData.refresh.hitPoint _ multiPoint; } ELSE ERROR; PaintOddHitLine[dc, gargoyleData]; }; UnionCombine: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.AppendHerald[gargoyleData.feedback, "UnionCombine not implemented", oneLiner]; GGError.Blink[gargoyleData.feedback]; }; -- end UnionCombine AddHoles: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; seqGen: SequenceGenerator _ GGSelect.SelectedSequences[gargoyleData.scene, normal]; fenceSeq, firstHoleSeq: Sequence; fence, hole: Traj; outline: Outline; bBox: BoundBox _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]; fenceSeq _ GGSequence.NextSequence[seqGen]; IF fenceSeq = NIL OR fenceSeq.traj.role#fence THEN { GGError.AppendHerald[gargoyleData.feedback, "Select a closed trajectory to add holes to.", oneLiner]; GOTO Abort; }; fence _ fenceSeq.traj; outline _ GGOutline.OutlineOfTraj[fence]; firstHoleSeq _ GGSequence.NextSequence[seqGen]; IF firstHoleSeq = NIL THEN { GGError.AppendHerald[gargoyleData.feedback, "Extend your selection to some closed trajectories to make holes.", oneLiner]; GOTO Abort; }; FOR holeSeq: Sequence _ firstHoleSeq, GGSequence.NextSequence[seqGen] UNTIL holeSeq = NIL DO hole _ holeSeq.traj; IF NOT GGObjects.IsTopLevel[hole] THEN LOOP; IF holeSeq.traj.role#fence THEN { GGError.AppendHerald[gargoyleData.feedback, "Select only CLOSED trajectories as holes.", oneLiner]; LOOP; }; outline _ GGInterface.AddHole[outline, hole, gargoyleData.scene]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: bBox, by: outline.boundBox]; -- boundBox of old shape and new shape gargoyleData.refresh.startBoundBox^ _ bBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; EXITS Abort => GGError.Blink[NARROW[clientData, GargoyleData].feedback]; }; -- end AddHoles DeleteAllSelected: PROC [gargoyleData: GargoyleData] RETURNS [bBox: BoundBox] = { DeleteRun: GGSelect.RunProc = { traj _ NIL; }; sliceDescGen: SliceDescriptorGenerator; thisBox: BoundBox; bBox _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]; sliceDescGen _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO GGSelect.DeselectEntityAllClasses[sliceD.slice, gargoyleData.scene]; GGSlice.DeleteSlice[gargoyleData.scene, sliceD.slice]; ENDLOOP; GGCaret.NoAttractor[gargoyleData.caret]; thisBox _ GGSelect.ForEachOutlineRun[gargoyleData.scene, gargoyleData.caret, normal, DeleteRun, FALSE]; GGBoundBox.EnlargeByBox[bBox: bBox, by: thisBox]; }; Delete: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; bBoxOfSelected: BoundBox; GGStatistics.StartInterval[$Delete, GGStatistics.GlobalTable[]]; bBoxOfSelected _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]; IF bBoxOfSelected=NIL THEN RETURN; -- nothing selected GGObjects.PushScene[gargoyleData.scene]; -- save for Undelete gargoyleData.refresh.startBoundBox^ _ DeleteAllSelected[gargoyleData]^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; GGStatistics.StopInterval[$Delete, GGStatistics.GlobalTable[]]; }; -- end Delete Undelete: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; IF GGObjects.EmptySceneStack[gargoyleData.scene] OR NOT MessageWindow.Confirm["UNDELETE WILL LOSE MOST EDITS performed since last Delete"] THEN RETURN; GGSelect.DeselectAllAllClasses[gargoyleData.scene]; GGObjects.PopScene[gargoyleData.scene]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; SelectAll: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGStatistics.StartInterval[$SelectAll, GGStatistics.GlobalTable[]]; GGSelect.DeselectAll[gargoyleData.scene, normal]; GGSelect.SelectAll[gargoyleData.scene, normal]; GGEvent.SawTextFinish[NIL, gargoyleData]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; GGStatistics.StopInterval[$SelectAll, GGStatistics.GlobalTable[]]; }; gScale: REAL _ 0.33; gAngle: REAL _ 33; MakeCurveProc: TYPE = PROC [lo, hi: Point, props: LIST OF REF ANY] RETURNS [seg: Segment]; GetPt: PROC[p0, dir: Point] RETURNS [pt: Point] = { pt _ GGVector.Add[GGVector.Scale[GGVector.VectorPlusAngle[dir,gAngle], gScale*GGVector.Magnitude[dir] ], p0]; }; SetStraight: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { MakeStraightAux: MakeCurveProc = { seg _ GGSegment.MakeLine[lo, hi, props]; }; gargoyleData: GargoyleData _ NARROW[clientData]; SetCurveAux[gargoyleData, MakeStraightAux, $Line]; }; -- end SetStraight SetArc: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { MakeArcAux: MakeCurveProc = { p1: Point; p1 _ GGVector.Add[GGVector.Scale[GGVector.Sub[hi,lo], 0.5], lo]; seg _ GGSegment.MakeArc[lo, p1, hi, props]; }; gargoyleData: GargoyleData _ NARROW[clientData]; SetCurveAux[gargoyleData, MakeArcAux, $Arc]; }; -- end SetArc SetSnowflake: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { OPEN GGVector; MakeSnowflakeAux: PROC [seg: Segment, traj: Traj] RETURNS [patternTraj: Traj] = { length: REAL; p1, p2, p3: Point; sideVec, dir, x: Vector; seg1, seg2, seg3, seg4: Segment; lo: Point _ seg.lo; hi: Point _ seg.hi; success: BOOL; length _ GGVector.Distance[lo,hi]; dir _ Sub[hi,lo]; x _ Scale[dir, 1.0/3.0]; p1 _ Add[lo, x]; p3 _ Add[lo, Scale[dir, 2.0/3.0]]; sideVec _ Scale[VectorPlusAngle[dir, 90], length*RealFns.SqRt[3]/6.0]; p2 _ Add[lo, GGVector.Add[Scale[dir, 0.5], sideVec]]; patternTraj _ traj; seg1 _ GGSegment.MakeLine[p0: lo, p1: p1, props: seg.props]; seg2 _ GGSegment.MakeLine[p0: p1, p1: p2, props: seg.props]; seg3 _ GGSegment.MakeLine[p0: p2, p1: p3, props: seg.props]; seg4 _ GGSegment.MakeLine[p0: p3, p1: hi, props: seg.props]; success _ GGTraj.AddSegment[patternTraj, hi, seg1, lo]; success _ GGTraj.AddSegment[patternTraj, hi, seg2, lo]; success _ GGTraj.AddSegment[patternTraj, hi, seg3, lo]; success _ GGTraj.AddSegment[patternTraj, hi, seg4, lo]; }; gargoyleData: GargoyleData _ NARROW[clientData]; SetPatternAux[gargoyleData, MakeSnowflakeAux]; }; SetConic: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { MakeConicAux: MakeCurveProc = { p1: Point; p1 _ GGVector.Add[GGVector.Scale[GGVector.Sub[hi,lo], 0.5], lo]; seg _ GGSegment.MakeConic[lo, p1, hi, 0.7, props]; }; gargoyleData: GargoyleData _ NARROW[clientData]; SetCurveAux[gargoyleData, MakeConicAux, $Conic]; }; -- end SetConic SetBezier: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { MakeBezierAux: MakeCurveProc = { length: REAL; dir: Point; p1, p2: Point; length _ GGVector.Distance[lo, hi]; dir _ GGVector.Sub[hi,lo]; p1 _ GetPt[lo, dir]; p2 _ GetPt[hi, [-dir.x, -dir.y] ]; seg _ GGSegment.MakeBezier[lo, p1, p2, hi, props]; }; gargoyleData: GargoyleData _ NARROW[clientData]; SetCurveAux[gargoyleData, MakeBezierAux, $Bezier]; }; -- end SetBezier SegsToSegsMatchCp: PROC [seg: Segment, traj: Traj, makeCurve: MakeCurveProc, type: ATOM] = { tSeg: Segment; success: BOOL; IF seg.class.type = type THEN -- if seg is already the right type, then we don't change it success _ GGTraj.AddSegment[traj, hi, GGSegment.CopySegment[seg], lo] ELSE IF type = $Arc AND seg.class.controlPointCount[seg] = 1 THEN { -- if old and new segment types have single control points, keep the control points the same tSeg _ GGSegment.MakeArc[seg.lo, seg.class.controlPointGet[seg, 0], seg.hi, seg.props]; tSeg.color _ seg.color; tSeg.strokeWidth _ seg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg , lo] } ELSE IF type = $Conic AND seg.class.controlPointCount[seg] = 1 THEN { -- if old and new segment types have single control points, keep the control points the same tSeg _ GGSegment.MakeConic[seg.lo, seg.class.controlPointGet[seg, 0], seg.hi, .7, seg.props]; tSeg.color _ seg.color; tSeg.strokeWidth _ seg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg, lo] } ELSE { -- make segment of new type with old endpoint info tSeg _ makeCurve[seg.lo, seg.hi, seg.props]; tSeg.color _ seg.color; tSeg.strokeWidth _ seg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; }; IF NOT success THEN ERROR; }; SegsAndCpsToSegs: PROC [seg: Segment, traj: Traj, makeCurve: MakeCurveProc, type: ATOM] = { success: BOOL; IF seg.class.type = type THEN -- if seg is already the right type, then we don't change it success _ GGTraj.AddSegment[traj, hi, GGSegment.CopySegment[seg], lo] ELSE { -- seg isn't the right type, so we convert all of its point/controlpoint spans tSeg: Segment; last, next: Point _ seg.lo; FOR i:INT IN [0..seg.class.controlPointCount[seg]) DO next _ seg.class.controlPointGet[seg, i]; tSeg _ makeCurve[last, next, seg.props]; tSeg.color _ seg.color; tSeg.strokeWidth _ seg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; IF NOT success THEN ERROR; last _ next; ENDLOOP; tSeg _ makeCurve[last, seg.hi, seg.props]; tSeg.color _ seg.color; tSeg.strokeWidth _ seg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; IF NOT success THEN ERROR; }; }; SetCurveAux: PROC [gargoyleData: GargoyleData, makeCurve: MakeCurveProc, type: ATOM] = { RunOfSegs: GGSelect.RunProc = { segGen: GGSequence.SegmentGenerator _ GGSequence.SegmentsInSequence[run]; initialSeg: Segment _ GGSequence.NextSegment[segGen]; IF initialSeg = NIL THEN RETURN[NIL]; -- the run is a single vertex traj _ GGTraj.CreateTraj[initialSeg.lo]; FOR seg: Segment _ initialSeg, GGSequence.NextSegment[segGen] UNTIL seg = NIL DO SELECT seg.class.type FROM -- choose from old segment type $Line, $Arc, $Conic, $Bezier => SegsToSegsMatchCp[seg, traj, makeCurve, type]; -- modify traj $CubicSpline => { IF type=$Line OR (run.traj.segCount=1 AND run.traj.role#open) THEN SegsAndCpsToSegs[seg, traj, makeCurve, type] -- Splines are changed to multiple lines...(or multiple segs if spline was cyclic) ELSE SegsToSegsMatchCp[seg, traj, makeCurve, type]; -- ...but single everything else }; ENDCASE => ERROR; ENDLOOP; }; bBox: BoundBox; bBox _ GGSelect.ForEachOutlineRun[gargoyleData.scene, gargoyleData.caret, normal, RunOfSegs, TRUE, TRUE]; gargoyleData.refresh.startBoundBox^ _ bBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; PatternProc: TYPE = PROC [seg: Segment, traj: Traj] RETURNS [patternTraj: Traj]; SetPatternAux: PROC [gargoyleData: GargoyleData, pattern: PatternProc] = { RunOfSegs: GGSelect.RunProc = { segGen: GGSequence.SegmentGenerator _ GGSequence.SegmentsInSequence[run]; initialSeg: Segment _ GGSequence.NextSegment[segGen]; IF initialSeg = NIL THEN RETURN[NIL]; -- the run is a single vertex traj _ GGTraj.CreateTraj[initialSeg.lo]; FOR seg: Segment _ initialSeg, GGSequence.NextSegment[segGen] UNTIL seg = NIL DO SELECT seg.class.type FROM -- choose from old segment type $Line => traj _ pattern[seg, traj]; ENDCASE => NULL; ENDLOOP; }; bBox: BoundBox; bBox _ GGSelect.ForEachOutlineRun[gargoyleData.scene, gargoyleData.caret, normal, RunOfSegs, TRUE, TRUE]; gargoyleData.refresh.startBoundBox^ _ bBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; SetNaturalSpline: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; SetSpline[splineType: naturalAL, gargoyleData: gargoyleData]; }; SetBSpline: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; SetSpline[splineType: bspline, gargoyleData: gargoyleData]; }; SetSpline: PROC [splineType: CubicSplines.SplineType, gargoyleData: GargoyleData] = { RunToSpline: GGSelect.RunProc = { X: NAT = CubicSplines.X; Y: NAT = CubicSplines.Y; tSeg: Segment; controlPoint: Point; cyclic: BOOL _ run.traj.role#open AND GGSequence.IsComplete[run]; cps: CubicSplines.KnotSequence _ NEW[CubicSplines.KnotSequenceRec[IF cyclic THEN run.jointCount+run.controlPointCount+1 ELSE run.jointCount+run.controlPointCount]]; segGen: GGSequence.SegmentGenerator _ GGSequence.OrderedSegmentsInSequence[run]; initialSeg: Segment _ GGSequence.NextSegment[segGen]; success: BOOL; cpCount: NAT _ 0; IF initialSeg = NIL THEN RETURN[NIL]; IF cyclic AND splineType = naturalAL THEN splineType _ cyclicAL; traj _ GGTraj.CreateTraj[initialSeg.lo]; cps[cpCount] _ [initialSeg.lo.x, initialSeg.lo.y]; cpCount _ cpCount + 1; FOR seg: Segment _ initialSeg, GGSequence.NextSegment[segGen] UNTIL seg = NIL DO FOR i:INT IN [0..seg.class.controlPointCount[seg]) DO controlPoint _ seg.class.controlPointGet[seg, i]; cps[cpCount] _ [controlPoint.x, controlPoint.y]; cpCount _ cpCount + 1; ENDLOOP; cps[cpCount] _ [seg.hi.x, seg.hi.y]; cpCount _ cpCount + 1; ENDLOOP; IF cyclic THEN cps[cps.length-1] _ cps[0]; -- cumulative error sometimes causes small differences. DJK. tSeg _ GGSegment.MakeCubicSpline[cps, splineType, NIL]; tSeg.color _ initialSeg.color; tSeg.strokeWidth _ initialSeg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; IF NOT success THEN ERROR; }; gargoyleData.refresh.startBoundBox^ _ GGSelect.ForEachOutlineRun[gargoyleData.scene, gargoyleData.caret, normal, RunToSpline, TRUE, TRUE]^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE] }; SelectMatchingCurve: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; name: Rope.ROPE _ IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]; trajGen: TrajGenerator _ GGObjects.TrajsInScene[gargoyleData.scene]; GGSelect.DeselectAll[gargoyleData.scene, normal]; FOR traj: Traj _ GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL traj = NIL DO segGen: SegmentGenerator _ GGSequence.SegmentsInTraj[traj]; FOR segAndIndex: SegAndIndex _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL segAndIndex.seg = NIL DO IF Rope.Equal[segAndIndex.seg.class.describe[segAndIndex.seg, TRUE, TRUE, TRUE, NIL], name, FALSE] THEN { seq: Sequence _ GGSequence.CreateFromSegment[traj, segAndIndex.index]; GGSelect.SelectSequence[seq, gargoyleData.scene, normal]; }; ENDLOOP; ENDLOOP; GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Curves of type %g selected", [rope[name]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; Close: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; chair: REF ANY; traj: Traj; firstPoint, lastPoint: Point; seg: Segment; success: BOOL; chair _ GGCaret.GetChair[gargoyleData.caret]; BEGIN IF chair = NIL THEN GOTO NoCaretTraj; WITH chair SELECT FROM oD: OutlineDescriptor => { [success, ----, traj] _ GGOutline.UnpackSimpleDescriptorOld[oD]; IF NOT success THEN ERROR; }; sD: SliceDescriptor => { IF sD.slice.class.type # $Outline THEN GOTO NoCaretTraj; [success, ----, traj] _ GGOutline.UnpackSimpleDescriptor[sD]; IF NOT success THEN GOTO NoCaretTraj; }; ENDCASE => ERROR; IF traj.role = fence OR traj.role = hole THEN GOTO AlreadyClosed; GGError.AppendHerald[gargoyleData.feedback, "The caret trajectory will be closed.", oneLiner]; firstPoint _ GGTraj.FetchJointPos[traj, 0]; lastPoint _ GGTraj.LastJointPos[traj]; IF firstPoint # lastPoint THEN { seg _ GGSegment.MakeLine[lastPoint, firstPoint, NIL]; GGTraj.CloseWithSegment[traj, seg, lo]; GGSelect.ReselectTraj[traj, hi, gargoyleData.scene, TRUE]; } ELSE { GGOutline.SaveSelectionsInOutline[traj.parent, gargoyleData.scene]; GGSelect.DeselectEntityAllClasses[traj.parent, gargoyleData.scene]; GGTraj.CloseByDistorting[traj, hi]; GGOutline.RemakeSelectionsFromOutline[traj.parent, gargoyleData.scene]; }; GGOutline.OutlineOfTraj[traj].class.setFillColor[GGOutline.OutlineOfTraj[traj], GGObjects.fillColor]; GGSelect.SelectTraj[traj, gargoyleData.scene, normal]; GGCaret.SitOn[gargoyleData.caret, NIL]; GGCaret.NoAttractor[caret: gargoyleData.caret]; gargoyleData.refresh.startBoundBox^ _ traj.boundBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; EXITS NoCaretTraj => { GGError.AppendHerald[gargoyleData.feedback, "There is no caret trajectory to close.", oneLiner]; GGError.Blink[gargoyleData.feedback]; }; AlreadyClosed => { GGError.AppendHerald[gargoyleData.feedback, "That trajectory is already closed.", oneLiner]; GGError.Blink[gargoyleData.feedback]; }; END; }; -- end Close Refresh: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; doNotClear: BOOL _ event#NIL AND event.rest#NIL AND event.rest.first=$DoNotClearFeedback; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: NOT doNotClear]; }; DisableRefresh: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; gargoyleData.refresh.suppressRefresh _ TRUE; }; EnableRefresh: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; gargoyleData.refresh.suppressRefresh _ FALSE; }; CloseToTranslation: PROC [s, t: ImagerTransformation.Transformation, range: REAL] RETURNS [BOOL] ~ { OPEN ImagerTransformation; WithinQuarterPixel: PROC [p, q: VEC] RETURNS [BOOL] ~ { RETURN [ABS[p.x-q.x] <= 0.25 AND ABS[p.y-q.y] <= 0.25] }; p10: VEC _ TransformVec[s, InverseTransformVec[t, [range, 0]]]; p01: VEC _ TransformVec[s, InverseTransformVec[t, [0, range]]]; RETURN [ WithinQuarterPixel[p10, [range, 0]] AND WithinQuarterPixel[p01, [0, range]] ] }; defaultPrefix: Rope.ROPE; defaultFamily: Rope.ROPE; defaultFace: Rope.ROPE; defaultTransform: ImagerTransformation.Transformation; defaultPreferredSize: REAL; Init: PROC [] = { defaultPrefix _ "xerox/pressfonts/"; defaultFamily _ "Helvetica"; defaultFace _ ""; defaultTransform _ ImagerTransformation.Scale[s: 10.0]; defaultPreferredSize _ 1.0; GGStatistics.AddInterval[GGStatistics.CreateInterval[$SelectAll, NIL], GGStatistics.GlobalTable[]]; GGStatistics.AddInterval[GGStatistics.CreateInterval[$Delete, NIL], GGStatistics.GlobalTable[]]; }; Init[]; END. CÒGGEventImplA.mesa Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Bier, January 30, 1987 8:52:08 pm PST Ken Pier, February 3, 1987 1:32:25 pm PST Kurlander August 28, 1986 6:31:20 pm PDT Stone, August 9, 1985 2:38:57 pm PDT If we ever allow text slices to have hot joints, we will have to be more careful here. If we ever allow text slices to have hot joints, we will have to be more careful here. FONTS Each text string should have a single font associated with it. That font may be either a print font or a screen font. For our purposes here, a font is a collection of shapes, defined in some font coordinate system, together with a transformation to apply to a string of those shapes when the string is rendered. File Format Ignoring user interface for the moment, here is what we will store in .gargoyle files: If the font is a resolution-independent font (e.g. press font), we store: FontPrefix FontFamily Face Transformation Preferred Size Xerox/PressFonts/ Helvetica -BI M = [a, b, c, d, e, f] 1.0 Xerox/xc1-2-2/ Modern -BI M = [a, b, c, d, e, f] 1.0 The shapes of the font are assumed to be 1 unit large in font master coordinates. If the named FontPrefix/FontFamily does not obey this property, the font will be scaled to be a unit font before the Transformation is applied. Hence, if M = Scale[12], the font will appear as 12 screen dots high. Our units are 1/72.0 of an inch. Tioga uses 1/72.27 inches. Life is hard. If the font is a resolution-dependent font (e.g. screen font, like Tioga10), we store: FontPrefix FontFamily Face Transformation Preferred Size Xerox/TiogaFonts/ Tioga -BI M = [a, b, c, d, e, f] 10.0 The shapes of the font are assumed to be 1 unit large in font master coordinates. This is often not true. However, the font will be scaled to be a unit font before the Transformation is applied. Gargoyle will have to know about fonts, but can hide this knowledge from the user. For instance: CMR10 is a 1 unit font, which must be scaled by 10 to look good. Tioga10, however, is stored at 10 units high in font master coordinates. Gargoyle will scale it by 0.1 to get a unit font, but M will usually be Scale[10] so all will be well. We store both kinds of fonts in .gargoyle files using the same format with Preferred Size = 1.0 for press fonts. This is a unique value which signals the fileIn code that it is a resolution independent font. It's size is one unit, and the appearance scaling is in the transformation. User Interface for Output There will be a "look readable" mode which calculates a more readable font from the font in each text string. As we do now, we will probably cache this font in each string along with the "real font". However, the readable font will not necessarily be a Screen Font. For large strings, it may be the same as the real font, for instance. Setting Fonts Here's where we have to be flexible. We'll probably want to have several kinds of SetFont commands, some which are convenient for everyday use and some which let you be very specific. These four may make a good starting set: SetPressFont. The user selects a pair such as "Helvetica-BI 12". The <-FontFace> may be left off, denoting regular font. Gargoyle assumes FontPrefix = Xerox/PressFonts/ and lets transformation M = Scale[]. The font is made with ImagerFont.Scale[font, 1] (in all cases I know about). SetPrintFont. The user selects a pair such as "Helvetica-BI 12". The <-FontFace> may be left off, denoting regular font. Gargoyle assumes FontPrefix = Xerox/XC1-2-2/ and lets transformation M = Scale[]. The font is made with ImagerFont.Scale[font, 1] (in all cases I know about). SetScreenFont. The user selects a pair such as "CMR 10". Gargoyle assumes FontPrefix = Xerox/TiogaFonts/ and lets transformation M = Scale[]. The font is made with ImagerFont.Scale[font, 1] if the font is CMR, with ImagerFont.Scale[font, 1.0/], if the font is Helvetica, TimesRoman, Tioga. If the font is one of the traditional TiogaFonts (e.g. Helvetica, TimesRoman, Tioga, ...) Gargoyle will attempt to find the corresponding strike font. For example, SetScreenFont TimesRoman-BI 9 will find the font in file ///fonts/xerox/tiogafonts/TimesRoman9BI.ks. If the user requests a TiogaFont not in the Tioga font set (usually an odd size), Gargoyle will fail to change the font. I don't know which camp Terminal is in. At any rate, Gargoyle will have to keep track of which fonts are in which camp. SetPrintFontDetailed. The user selects a triple. Gargoyle makes no assumptions. The user must provide a factored transformation. For example: xerox/pressfonts/ gacha-bi [r1: REAL, s: VEC, r2: REAL, t: VEC] xerox/xc1-2-2/ classic-b [r1: REAL, s: VEC, r2: REAL, t: VEC] WHITESPACE IS CRITICAL !! SetScreenFontDetailed. The user selects a quadruple. Again, Gargoyle uses its information about font camps to successfully make a unit sized font. Otherwise, Gargoyle makes no assumptions. Note: For screen fonts, we may run into trouble when *(1.0/) isn't quite 1.0. We can either round within epsilon for screen fonts, or we can keep around an unscaled font when we know we should be hitting the fast case. Asking About Fonts A single function PrintFontOfSelected should say exactly what font is being used. The format of this output should be suitable for selecting and feeding to SetPrintFontDetailed or SetScreenFontDetailed. Unfortunately, the Gargoyle feedback region isn't selectable. Of course, the user could open a Typescript first. Suggestions? Fonts Menu In GGEventImplA.SetPrintFont Copies the font data from the LAST selected text slice to all the other selected text slices Copies the from the Tioga selection to all selected text slices SetPressFont expects input of the form . e.g. "Cream-BI 10" SetPrintFont expects input of the form . e.g. "Modern-BI 23" SetPrintFontDetailed: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { SetPrintFontDetailed expects input of the form . e.g. "xerox/myfonts/ fontOne-BI [1.0 2.0 3.0 4.0 5.0 6.0]. Whitespace is critical!! fail: BOOL _ FALSE; prefix, family, face: Rope.ROPE; faceRope, problem: Rope.ROPE; transform: ImagerTransformation.Transformation; inStream: IO.STREAM _ IO.RIS[NARROW[event.rest.first]]; [fail, prefix, family, face, transform, ----] _ GGUtility.ParseFontData[inStream, TRUE, TRUE, TRUE, TRUE, FALSE]; BEGIN IF fail THEN { gargoyleData: GargoyleData _ NARROW[clientData]; scratch: IO.STREAM _ IO.ROS[]; IF transform#NIL THEN GGParseOut.WriteFactoredTransformation[scratch, transform]; GGError.PutF[gargoyleData.feedback, oneLiner, "Font Parse Error: %g%g%g %g %g", [rope[prefix]], [rope[family]], [rope[face]], [rope[IO.RopeFromROS[scratch]]], [real[1.0]] ]; } ELSE { [faceRope, problem] _ GGUtility.FaceRopeFromFace[prefix, family, face, 1.0]; IF problem#NIL THEN GOTO FaceRopeProblem; SetFontAux[clientData, prefix, family, face, faceRope, 1.0, 1.0, transform]; }; EXITS FaceRopeProblem => { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.PutF[gargoyleData.feedback, oneLiner, "%g", [rope[problem]] ]; }; ParseError => { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.PutF[gargoyleData.feedback, oneLiner, "Font Parse Error: %g%g%g %g", [rope[prefix]], [rope[family]], [rope[face]], [real[size]] ]; }; END; }; SetScreenFont expects input of the form . e.g. "Cream-BI 10" SetFontDetailed expects input of the form e.g. "xerox/myfonts/ fontOne-BI [1.0 2.0 3.0 4.0 5.0 6.0] 12. Whitespace is critical!! SetDefaultFontValues checks that there is a single GG text slice selected, then copies its font information to the default font Outputs string in the form transformation preferredSize Checks for a single selected GG text slice, then selects all the text slices with fonts matching the font of that slice. selects all the text slices with fonts that match the Tioga text selection selects all the text slices with font family that matches the Tioga text selection Interpress Utilities All selected IP Slices are now store-by-reference. All selected IP Slices are now store-by-value. Show whether the selected IP Slices is store-by-reference or store-by-value TIMING VARIABLES START TIMING TIMING VARIABLES START TIMING Gravity Utilities Within the bounds of the viewer, randomly choose mouse positions. See if that mouse position is in range of any object. If so, draw a dot at that point. Repeat until 100 points have been drawn. Hierarchy Menu PolyType: TYPE = {positive, negative}; PolyData: TYPE = REF PolyDataObj; PolyDataObj: TYPE = RECORD [ type: PolyType ]; HandleOverlap: PROC [currentClientdata, inputClientdata: REF] RETURNS [resultClientData: REF] = { resultClientData _ NEW[PolyDataObj _ [positive]]; }; HandleGlue: PROC [clientData1, clientData2: REF ANY] RETURNS [BOOL] = { RETURN[FALSE]; }; IsPositive: PROC [clientData: REF ANY] RETURNS [BOOL] = { polyData: PolyData _ NARROW[clientData]; IF polyData = NIL THEN RETURN[FALSE]; RETURN[polyData.type = positive]; }; bBoxOfSelected: BoundBox _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]; IF bBoxOfSelected=NIL THEN RETURN; -- nothing selected IF PrincOpsUtils.IsBound[LOOPHOLE[CombinePoly.CreateDatabase]] THEN { startTime, endTime: BasicTime.GMT; totalTime: INT; GGError.PutF[gargoyleData.feedback, begin, "Combining selected convex polygons . . . "]; startTime _ BasicTime.Now[]; UnionCombineAux[gargoyleData]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; GGError.PutF[gargoyleData.feedback, end, " Done in time (%r)", [integer[totalTime]]]; gargoyleData.refresh.startBoundBox^ _ bBoxOfSelected^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; } ELSE { GGError.Append[gargoyleData.feedback, "Please run BasicCombiner.bcd and retry this operation", oneLiner]; GGError.Blink[gargoyleData.feedback]; }; UnionCombineAux: PROC [clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; Use Arnon's Combiner to find the union of all selected trajectories. The result of such a union may be a set of outlines with holes. For now, we assume that the result will be a set of outlines without holes (since holes are not implemented in Gargoyle yet. dbid: CombinePoly.DBID; polyID: CombinePoly.PolyID; seqGen: SequenceGenerator; regionGen: CombinePoly.RegionGenerator; jointGen: JointGenerator; pointGen: CombinePoly.PointGenerator; point, firstPoint, secondPoint: Point; polyData: REF ANY; firstPointAndDone, previousPointAndDone: CombinePoly.PointAndDone; success: BOOL; traj: Traj; seg: Segment; outline: Outline; jointNum: INT; dbid _ CombinePoly.CreateDatabase[]; seqGen _ GGSelect.SelectedSequences[gargoyleData.scene, normal]; FOR seq: Sequence _ GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen] UNTIL seq = NIL DO IF NOT GGSequence.IsComplete[seq] THEN LOOP; IF NOT seq.traj.role = fence THEN LOOP; polyData _ NEW[PolyDataObj _ [positive]]; jointGen _ GGSequence.JointsInSequence[seq]; Create the Polygon jointNum _ GGSequence.NextJoint[jointGen]; firstPoint _ GGTraj.FetchJointPos[seq.traj, jointNum]; jointNum _ GGSequence.NextJoint[jointGen]; secondPoint _ GGTraj.FetchJointPos[seq.traj, jointNum]; polyID _ CombinePoly.CreatePolygon[firstPoint, secondPoint, polyData]; Add the other points. FOR jointNum: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL jointNum = -1 DO point _ GGTraj.FetchJointPos[seq.traj, jointNum]; CombinePoly.AddPointToPolygon[point, polyID]; ENDLOOP; dbid _ CombinePoly.PolygonIntoDatabase[polyID, dbid, HandleOverlap, HandleGlue]; ENDLOOP; [] _ DeleteAllSelected[gargoyleData]; regionGen _ CombinePoly.MaximalRegions[dbid, IsPositive]; FOR region: CombinePoly.OutlineHolesDataAndDone _ CombinePoly.NextRegion[regionGen], CombinePoly.NextRegion[regionGen] UNTIL region.done DO pointGen _ region.outline; previousPointAndDone _ firstPointAndDone _ CombinePoly.NextPoint[pointGen]; IF previousPointAndDone.done THEN LOOP; traj _ GGTraj.CreateTraj[previousPointAndDone.point]; FOR pointAndDone: CombinePoly.PointAndDone _ CombinePoly.NextPoint[pointGen], CombinePoly.NextPoint[pointGen] UNTIL pointAndDone.done DO seg _ GGSegment.MakeLine[previousPointAndDone.point, pointAndDone.point, NIL]; success _ GGTraj.AddSegment[traj, hi, seg, lo]; IF NOT success THEN ERROR; previousPointAndDone _ pointAndDone; REPEAT FINISHED => { seg _ GGSegment.MakeLine[previousPointAndDone.point, firstPointAndDone.point, NIL]; GGTraj.CloseWithSegment[traj, seg, lo]; }; ENDLOOP; outline _ GGOutline.CreateOutline[traj, round, GGObjects.fillColor]; GGObjects.AddOutline[gargoyleData.scene, outline, -1]; ENDLOOP; }; OldDeleteAllSelected: PROC [gargoyleData: GargoyleData] RETURNS [bBox: BoundBox] = { sliceDescGen: SliceDescriptorGenerator; outSeqGen: GGSelect.OutlineSequenceGenerator; oldOutline: Outline; fenceSeq: Sequence; thisBox: BoundBox; bBox _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]; Implemented enough for non-gargoyle slices only. sliceDescGen _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO GGSelect.DeselectEntityAllClasses[sliceD.slice, gargoyleData.scene]; GGSlice.DeleteSlice[gargoyleData.scene, sliceD.slice]; ENDLOOP; Now delete selected sequences. outSeqGen _ GGSelect.SelectedOutlineSequences[gargoyleData.scene, normal]; WHILE outSeqGen.list # NIL DO FOR outSeq: GGSelect.OutlineSequence _ GGSelect.NextOutlineSequences[outSeqGen], GGSelect.NextOutlineSequences[outSeqGen] UNTIL outSeq = NIL DO fenceSeq _ outSeq.fenceSeq; FOR holeSeq: Sequence _ GGSequence.NextSequence[outSeq.holeSeqs], GGSequence.NextSequence[outSeq.holeSeqs] UNTIL holeSeq = NIL DO [oldOutline,----] _ GGInterface.DeleteSequence[holeSeq, gargoyleData.scene]; thisBox _ GGTraj.GetBoundBox[holeSeq.traj]; GGBoundBox.EnlargeByBox[bBox: bBox, by: thisBox]; GOTO SelectionsHaveChanged; ENDLOOP; IF fenceSeq # NIL THEN { [oldOutline,----] _ GGInterface.DeleteSequence[fenceSeq, gargoyleData.scene]; IF fenceSeq.traj.role = open THEN { GGSequence.UpdateBoundBox[fenceSeq]; GGBoundBox.EnlargeByBox[bBox: bBox, by: fenceSeq.boundBox]; } ELSE { thisBox _ GGTraj.GetBoundBox[fenceSeq.traj]; GGBoundBox.EnlargeByBox[bBox: bBox, by: thisBox]; }; GOTO SelectionsHaveChanged; }; REPEAT SelectionsHaveChanged => { outSeqGen _ GGSelect.SelectedOutlineSequences[gargoyleData.scene, normal]; }; ENDLOOP; ENDLOOP; }; Implemented enough for non-gargoyle slices only. Overly conservative. Just in case the caret was attracted to something that was just deleted. Curve Menu Replace segs of the old type with segs of the new type, maintaining control point data if possible Replace old segments with segments of the new type, but if the old segment had control points, use these also as joints of new segments. Use GGSelect.ForEachOutlineRun to replace all selected runs with runs of a new type. Strategy: Replace each selected run with a new trajectory which has a segment (of the proper type) for each joints/control point span. Note that we we copy verbatim all segs which were already of the specified type. The new segments inherit color and strokeWidth from the old ones. [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj] Use GGSelect.ForEachOutlineRun to replace all selected runs with runs of a new type. Strategy: Replace each selected run with a new trajectory which has a segment (of the proper type) for each joints/control point span. [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj] [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj] Edit Curve Menu N.B. It's a shame we have to remake the triggerBag here. This is because the closed trajectory might have been hot. Its hot sequence is obsolete. ShowPoints: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; seqGen: SequenceGenerator _ GGSelect.SelectedSequences[gargoyleData.scene, normal]; FOR seq: Sequence _ GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen] UNTIL seq = NIL DO seq.traj.visibleJoints _ TRUE; ENDLOOP; }; HidePoints: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; seqGen: SequenceGenerator _ GGSelect.SelectedSequences[gargoyleData.scene, normal]; FOR seq: Sequence _ GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen] UNTIL seq = NIL DO seq.traj.visibleJoints _ FALSE; ENDLOOP; }; View Menu Debug menu ops moved to GGEventImplD TAKE THIS CODE OUT WHEN MOVING TO CEDAR 7.0 !! ÊNû˜Icode™šÏnœx™€Kšœ%™%Kšœ)™)Kšœ%Ïk™(Kšœ!ž™$—K™šž ˜ Kš œ%Ïcœ¦Ÿ œdžœŸœ,˜¦K˜—š œžœž˜Kš žœŸœÚŸ œdžœŸœ#˜ÈKšžœ ž˜—˜Kšœ žœ˜%Kšœžœ˜!Kšœžœ˜3Kšœžœ ˜5Kšœžœ)˜GKšœžœ ˜5Kšœ žœ ˜1Kšœžœ!˜3Kšœžœ˜!Kšœžœ˜3Kšœ žœ˜&Kšœžœ"˜9Kšœ žœ˜(Kšœ žœ˜%Kšœžœ˜!Kšœžœ˜!Kšœ žœ˜'Kšœ žœ˜+Kšœ žœ˜'Kšœ žœ˜+Kšœžœ˜Kšœžœ˜1Kšœžœ!˜7Kšœžœ"˜9Kšœžœ˜#—K˜Kšœžœžœžœ˜(K˜šœžœ'žœžœ˜JšžœžœžœŸ7˜SK•StartOfExpansion_[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass]šœ^˜^KšœH˜Hš žœ žœžœ"žœ,žœžœ˜mKšœ`˜`Kšœ%˜%Kšžœ˜K˜—K–|[slice: GGModelTypes.Slice, gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass]šœw˜wKšœ7Ÿ"˜YKš œ1Ïbœ7žœ žœžœ˜§Kšœ˜—šžœ˜Kšœ~Ÿ˜—Kš žœžœžœžœŸ'˜AKšœ6˜6Kšœ)˜)Kš œ1  œ;žœ žœžœ˜¦Kšœ˜—KšœŸ˜K˜—šœžœžœ žœžœžœžœžœžœ˜FKšœžœ ˜0Kšœ ˜ Kšœ˜Kšœ žœžœ˜Kš žœžœžœ žœžœ˜1Kšœ žœ˜#šžœ%žœžœ˜1K–ß[caret: GGInterfaceTypes.Caret, point: GGBasicTypes.Point, attractor: REF ANY _ NIL, on: GGInterfaceTypes.CaretOn _ nothing, attractorJointNum: NAT _ 999, attractorCPNum: NAT _ 999, attractorSegNum: NAT _ 999]šœ)Ÿ˜FKšœ(˜(Kšžœ˜Kšœ˜—šžœŸ˜KšœA˜AKšœ,˜,Kšœ1Ÿ!˜Ršžœžœžœ˜Kšœ$˜$Kšœ7Ÿ˜UKšœM˜Mš œ1 œ;žœ žœžœ˜¸KšœV™V—Kšœ˜—šžœ˜KšœA˜AKšœ7Ÿ"˜YKšœM˜MKšœ)˜)š œ1  œ7žœ žœžœ˜¢KšœV™V—Kšœ˜—Kšœ@˜@Kšœ˜—KšœŸ˜K˜—š œžœžœ žœžœ˜cKšœ7˜7Kšœ žœ˜Kšœžœ˜Kšœ7˜7Kšœ,œG˜‡Kšžœ žœžœžœ˜Kšœ œ­˜ÊKšžœžœ žœžœ˜Kšœ2˜2Kšœ6˜6Kšœ2Ÿ˜IKšœ)˜)Kšœxžœ žœžœ˜¦Kšœ˜K˜—šœžœžœ žœžœžœžœžœžœ˜FKšœžœ ˜0Kš œ žœžœžœžœ$žœžœ˜lKšœ&˜&K˜K˜—šœžœžœ žœžœžœžœžœžœ˜XKšœžœ ˜0Kš œžœžœžœžœ˜9KšœW˜WK–h[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass _ normal]šœbŸ˜€šžœfžœžœž˜|KšœO˜OKšžœ˜—K–<[bBox: GGModelTypes.BoundBox, by: GGModelTypes.BoundBox]šœ‚˜‚KšœŒžœ žœžœŸ6˜òKšœŸ ˜#K˜—šœžœžœ žœžœžœžœžœžœ˜PKšœžœ ˜0Kšœ]˜]KšœH˜Hš žœ žœžœ"žœ,žœžœ˜mKšœo˜oKšœ%˜%K˜—Kšžœz˜~K˜K˜K˜—š œžœžœ žœžœžœžœžœžœ˜KKšœžœ ˜0Kš œ žœžœžœžœ˜4Kš œ žœžœžœžœ˜9KšœW˜WK–h[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass _ normal]šœbŸ˜€šžœfžœžœž˜|Kšœ7˜7Kšžœ˜—K–<[bBox: GGModelTypes.BoundBox, by: GGModelTypes.BoundBox]šœ‚˜‚KšœŒžœ žœžœ˜ºK˜K˜—š œžœžœ žœžœžœžœžœžœ˜LKšœžœ ˜0KšœW˜WK–h[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass _ normal]šœbŸ˜€šžœfžœžœž˜|Kšœ$˜$Kšžœ˜—K–<[bBox: GGModelTypes.BoundBox, by: GGModelTypes.BoundBox]šœ‚˜‚KšœŒžœ žœžœ˜ºK˜—K˜head™Ibodyšœ¹™¹—šœ ™ Mšœ¡™¡J™Jšœ=™=Jšœžœ™=Jšœžœ™8J™I continuationšœö™öMšœV™VJ™Jšœ=™=Jšœžœ™;N™Nšœ›™›Mšœ™—šœ™MšœÒ™Ò—šœ ™ Mšœâ™âIitemšœÅ™ÅOšœÂ™ÂOšœˆžœ¢žœ½™ìšœÅ™ÅOš œ!žœžœžœžœ™@Oš œžœžœžœžœ™>Ošž œžœžœ™—Ošœ„™„Mšœè™ì—šœ™MšœÌ™Ì—M™šœ ™ Kšœ™—š œžœžœžœžœžœ žœžœžœžœ5˜ÇKšœžœ ˜0KšœW˜WK–h[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass _ normal]šœbŸ˜€šžœfžœžœž˜|Kšœ~˜~Kšžœ˜—K–<[bBox: GGModelTypes.BoundBox, by: GGModelTypes.BoundBox]šœ‚˜‚KšœŒžœ žœžœŸ.˜êKšœ˜K˜—š œžœžœ žœžœžœžœžœžœ˜MKšœžœ:™\Kšœžœ ˜0Kšœ˜Kšœ]˜]šžœržœžœž˜ˆKšœŸ˜2Kšžœ˜—šž˜Kš žœ žœžœ!žœžœ ˜Išžœ˜Kšœ.žœ˜3Kšœ/˜/Kšœžœ˜Kšœžœ-˜@KšœY˜YKšœ,œ+˜kKšžœ žœžœžœ˜K–h[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass _ normal]šœbŸ˜€KšœC˜Cšžœržœžœž˜ˆKšœî˜îKšžœ˜—K–<[bBox: GGModelTypes.BoundBox, by: GGModelTypes.BoundBox]šœ‚˜‚KšœŒžœ žœžœ˜ºK–Û[feedback: ViewerClasses.Viewer, msgType: GGError.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœl˜lKšœ˜—šž˜˜Kšœg˜gKšœ%˜%K˜——Kšžœ˜—Kšœ˜K˜—š œžœžœ žœžœžœžœžœžœ˜JKšœQ™QKšœžœ ˜0Kšœžœžœ˜Kšœžœ˜Kšœ.žœ˜3Kšœ/˜/Kšœžœ˜Kšœžœ˜Kšœ žœžœžœžœžœžœžœ$žœžœ˜xKš œŸœŸœŸœ&žœžœžœ˜dšž˜Kšžœžœžœ ˜šžœ˜Kšœ]˜]K–h[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass _ normal]šœbŸ˜€šžœržœžœž˜ˆKšžœžœžœ˜+KšœW˜WKšœ,œ1˜qKšžœ žœžœžœ˜)Kšœƒ˜ƒKšžœ˜—K–<[bBox: GGModelTypes.BoundBox, by: GGModelTypes.BoundBox]šœ‚˜‚KšœŒžœ žœžœ˜ºK–Û[feedback: ViewerClasses.Viewer, msgType: GGError.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœl˜lKšœ˜—šž˜Kšœu˜ušœ˜Kšœžœ ˜0KšœF˜FKšœ˜——Kšžœ˜—Kšœ˜K˜—š œžœžœ žœžœžœžœžœžœ˜KKšœW™WK–)[rope: ROPE, oldStream: STREAM _ NIL]š œ žœžœžœžœžœŸ˜HKšœžœžœ˜Kšœžœ˜Kšœ.žœ˜3KšœŸ"˜@šž˜KšœŸœŸœ,žœžœžœžœžœ˜kKšžœžœžœ ˜šžœ˜Kšœ,œ"˜bKšžœ žœžœžœ˜)KšœBžœ˜GK˜—šž˜šœ˜Kšœžœ ˜0KšœF˜FKšœ˜—˜Kšœžœ ˜0KšœŠ˜ŠK˜——Kšžœ˜—Kšœ˜K˜—š œžœžœ žœžœžœžœžœžœ˜KKšœX™XK–)[rope: ROPE, oldStream: STREAM _ NIL]š œ žœžœžœžœžœŸ˜IKšœžœžœ˜Kšœžœ˜Kšœ.žœ˜3KšœŸ"˜=šž˜KšœŸœŸœ,žœžœžœžœžœ˜kKšžœžœžœ ˜–<[prefix: ROPE, family: ROPE, face: ROPE, size: REAL]šžœ˜Kšœ,œ"˜bKšžœ žœžœžœ˜)KšœBžœ˜GK˜—šž˜šœ˜Kšœžœ ˜0KšœF˜FKšœ˜—˜Kšœžœ ˜0KšœŠ˜ŠK˜——Kšžœ˜—Kšœ˜K˜—šœžœžœ žœžœžœžœžœžœ™SKšœ·™·Kšœžœžœ™Kšœžœ™ Kšœžœ™K–)[rope: ROPE, oldStream: STREAM _ NIL]šœ/™/Kš œ žœžœžœžœžœ™7Kš œ(Ÿœ&žœžœžœžœžœ™qšž™šžœžœ™Kšœžœ ™0Kš œ žœžœžœžœ™Kšžœ žœžœ<™QKšœ„žœ'™­Kšœ™—šžœ™KšœL™LKšžœ žœžœžœ™)KšœL™LK™—šž™šœ™Kšœžœ ™0KšœF™FKšœ™—™Kšœžœ ™0KšœŠ™ŠK™——Kšžœ™—Kšœ™K™—š œžœžœ žœžœžœžœžœžœ˜LKšœa™aKšœ.žœ˜3Kšœžœ˜K–)[rope: ROPE, oldStream: STREAM _ NIL]š œ žœžœžœžœžœŸ˜HKšœžœžœ˜šž˜KšœŸ#˜AKšœŸœŸœ5žœžœžœžœžœ˜tKšžœžœžœ ˜šžœ˜Kšœ,œ+˜kKšžœ žœžœžœ˜)KšœUžœ˜ZK˜—šž˜šœ˜Kšœžœ ˜0Kšœ“˜“Kšœ˜—šœ˜Kšœžœ ˜0KšœF˜FKšœ˜——Kšžœ˜—Kšœ˜K˜—šœžœžœ žœžœžœžœžœžœ˜NKšœÅ™ÅKšœžœžœ˜Kšœ.žœ˜3K–)[rope: ROPE, oldStream: STREAM _ NIL]šœ/˜/Kšœžœ˜Kš œ žœžœžœžœžœ˜7Kš œ[žœžœžœžœžœ˜yšž˜šžœžœ˜Kšœžœ ˜0Kš œ žœžœžœžœ˜Kšžœ žœžœ<˜QKšœ„žœ1˜·Kšœ˜—šžœ˜Kšœ,œ+˜kKšžœ žœžœžœ˜)KšœV˜VK˜—Kšž˜šœ˜Kšœžœ ˜0KšœF˜FKšœ˜—Kšžœ˜—Kšœ˜K˜—šœžœžœ žœžœžœžœžœžœ˜OK˜—šœžœžœ žœžœžœžœžœžœ˜SKšœ3žœJ™Kšœžœ ˜0Kšœ]˜]KšœH˜Hš žœ žœžœ"žœ,žœžœ˜mKšœm˜mKšœ%˜%Kšœ˜—šžœ˜Kšœ}˜}Kšœ{˜{Kšœ˜—Kšœ˜K˜—šœžœžœ žœžœžœžœžœžœ˜MKšœ\™\Kšœžœ ˜0Kšœ]˜]KšœH˜Hš žœ žœžœ"žœ,žœžœ˜mKšœf˜fKšœ%˜%Kšœ˜—Kšžœw˜{Kšœ˜K˜—šœžœžœ žœžœžœžœžœžœ˜VK˜—šœžœžœ žœžœžœžœžœžœ˜TKšœžœ ˜0K–ldStream: STREAM _ NIL]š œ žœžœžœžœ˜KšœB˜BK–Û[feedback: ViewerClasses.Viewer, msgType: GGError.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœžœ8˜×Kšœ˜K˜—šœžœžœ žœžœžœžœžœžœ˜WKšœžœY™xKšœžœ ˜0Kšœ]˜]KšœH˜Hš žœ žœžœ"žœ,žœžœ˜mKšœf˜fKšœ%˜%Kšœ˜—šžœŸ1˜8Kšœžœ˜ Kšœ/˜/Kšœžœ˜KšœZ˜ZKšœ~˜~Kšœ˜—Kšœ˜K˜—šœžœžœ žœžœžœžœžœžœ˜UKšœJ™JKšœžœ ˜0Kšœžœžœ˜0šžœžœ˜#K–K[feedback: ViewerClasses.Viewer, msg: ROPE, msgType: GGError.MsgType]šœY˜YKšœ˜—šžœ˜Kšœžœ˜ Kšœ/˜/Kšœžœ˜Kšœžœ˜ KšœQžœžœ žœžœžœžœžœŸ#˜¦Kšžœžœ]˜iKšžœ^˜bKšœ˜—Kšœ˜K˜—š œžœžœAžœžœ!˜µKšœ žœ ˜KšœG˜GKšœ1˜1šžœMžœžœž˜bKšœžœ˜#Kšœ0˜0Kšœžœ˜KšœU˜UK–-[y: REAL, x: REAL, distance: [-126..0]]šžœžœžœžœžœžœžœ8žœŸœ6žœGžœ=˜ŠKšžœ˜—K–Û[feedback: ViewerClasses.Viewer, msgType: GGError.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœl˜lKšœyžœ žœžœ˜¨Kšœ˜K˜—šœžœžœ žœžœžœžœžœžœ˜UKšœR™RKšœžœ ˜0Kšœžœžœ˜0šžœžœ˜#K–K[feedback: ViewerClasses.Viewer, msg: ROPE, msgType: GGError.MsgType]šœY˜YKšžœ˜Kšœ˜—šžœ˜KšœG˜GKšœ1˜1šžœMžœžœž˜bKšžœ<žœžœGžœ=˜ÐKšžœ˜—K–Û[feedback: ViewerClasses.Viewer, msgType: GGError.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœm˜mKšœyžœ žœžœ˜¨Kšœ˜—Kšœ˜—K˜K™šœžœžœžœžœžœžœžœ˜iš œžœ˜.Jš "Ðbc˜Kšœ+˜+K˜—šžœžœžœ˜$Kšœ)˜)Kšœ"˜"Kšœ+˜+K˜—Kšžœžœ˜ Kšœ"˜"K˜K˜—K˜K™Kšœ žœ™&Kšœ žœžœ ™!šœ žœžœ™K™Kšœ™—š œžœ&žœžœžœ™aKšœžœ™1K™—š œžœžœžœžœžœ™GKšžœžœ™K™K™—š œžœžœžœžœžœ™9Kšœžœ ™(Kš žœ žœžœžœžœ™%Kšžœ™!K™K™—š œžœžœ žœžœžœžœžœžœ˜KKšœžœ ˜0KšœU™UKš žœžœžœžœŸ™6šžœžœžœ™EKšœžœ™"Kšœ žœ™KšœX™XKš ™Kšœ™Kš ™Kšœ1™1KšœU™UKšœ6™6KšœŒžœ žœžœ™»K™—šžœ™Kšœi™iKšœ%™%K™—KšœV˜VKšœ%˜%KšœŸ˜K˜—šœžœžœžœ™/Kšœžœ ™0K™ƒKšœžœ™Kšœ™K™Kšœ'™'K™Kšœ%™%K™&Kšœ žœžœ™KšœB™BKšœ žœ™K™ K™ Kšœ™Kšœ žœ™Kšœ$™$Kšœ@™@šžœRžœžœž™gKšžœžœžœžœ™,Kšžœžœžœžœ™'Kšœ žœ™)šœ,™,Kš ™—Kšœ*™*Kšœ6™6Kšœ*™*Kšœ7™7šœF™FKš ™—šžœ žœBžœž™iKšœ1™1Kšœ-™-Kšžœ™—KšœP™PKšžœ™—Kšœ%™%Kšœ9™9šžœtžœ ž™‹Kšœ™KšœK™KKšžœžœžœ™'Kšœ5™5šžœkžœž™ˆKšœIžœ™NKšœ/™/Kšžœžœ žœžœ™Kšœ$™$šž™šžœ™ KšœNžœ™SKšœ'™'K™——Kšžœ™—KšœD™DKšœ6™6Kšžœ™—™K™——šœžœžœ žœžœžœžœžœžœ˜GKšœžœ ˜0KšœS˜SKšœ!˜!Kšœ˜K˜KšœK˜KKšœ+˜+šžœ žœžœžœ˜4Kšœe˜eKšžœ˜ K˜—Kšœ˜Kšœ)˜)Kšœ/˜/šžœžœžœ˜Kšœz˜zKšžœ˜ Kšœ˜—šžœCžœ žœž˜\Kšœ˜Kšžœžœžœžœ˜,šžœžœ˜!Kšœc˜cKšžœ˜K˜—Kš A˜AKšžœ˜—Kšœ;Ÿ&˜aKšœ,˜,KšœŒžœ žœžœ˜»šž˜Kšœžœ%˜B—KšœŸ˜—K˜šœžœžœ™UK™'Kšœ-™-Kšœ™Kšœ™K™šœA™AKš 0™0—KšœC™Cšžœržœ žœž™ŠKšœD™DKšœ6™6Jšžœ™Jš ™—JšœJ™Jšžœžœž™šžœwžœ žœž™Jšœ™šžœhžœ žœž™Kšœ Ÿœ<™LKšœ+™+Kšœ1™1Kšžœ™Jšžœ™—šžœ žœžœ™Kšœ Ÿœ=™Mšžœžœ™#Kšœ$™$Kšœ;™;K™—šžœ™Kšœ,™,Kšœ1™1K™—Kšžœ™K™—šž™™JšœJ™JJ™——šžœ™J™——Jšžœ™—K™K™—K˜šœžœžœ˜Rš œ˜Jšœžœ˜ J˜—K˜'K˜šœA˜AKš 0™0—KšœC˜Cšžœržœ žœž˜ŠKšœD˜DKšœ6˜6Jšžœ˜—šœ(˜(J™^—Jšœ`žœ˜gKšœ1˜1K˜K˜—šœžœžœ žœžœžœžœžœžœ˜EKšœžœ ˜0Kšœ˜Kšœ@˜@KšœK˜KKš žœžœžœžœŸ˜6Kšœ=˜=Kšœ& œ˜GKšœŒžœ žœžœ˜ºKšœ?˜?KšœŸ ˜K˜—šœžœžœ žœžœžœžœžœžœ˜GKšœžœ ˜0Kš žœ/žœžœTžœžœ˜—Kšœ3˜3Kšœ'˜'Kšœžœ žœžœ˜­Kšœ˜K˜—š œžœžœ žœžœžœžœžœžœ˜HKšœžœ ˜0KšœC˜CKšœ1˜1Kšœ/˜/Kšœžœ˜)Kšœyžœ žœžœ˜§KšœB˜BK˜—K˜KšÏz ™ Kšœžœ˜Kšœžœ˜Kšœžœžœžœžœžœžœžœ˜ZK˜šœžœžœ˜3Kšœm˜mKšœ˜K˜—š œžœžœ žœžœžœžœžœžœ˜Jšœ˜"Kšœ(˜(Kšœ˜—Kšœžœ ˜0Kšœ2˜2KšœŸ˜K˜—šœžœžœ žœžœžœžœžœžœ˜Eš œ˜K˜ Kšœ@˜@Kšœ+˜+Kšœ˜—Kšœžœ ˜0Kšœ,˜,KšœŸ ˜K˜—š œžœžœ žœžœžœžœžœžœ˜KKšžœ ˜šœžœžœ˜QKšœžœ˜ Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ žœ˜Kšœ"˜"Kšœ˜Kšœ˜Kšœ˜Kšœ"˜"KšœF˜FKšœ5˜5Kšœ˜K–N[p0: GGBasicTypes.Point, p1: GGBasicTypes.Point, props: LIST OF REF ANY]šœ<˜žœ˜XKšœT™TK™œ–B -- [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj]š œ˜KšÐck>™>KšœI˜IK˜5Kš žœžœžœžœžœŸ˜CK˜(šžœ;žœžœž˜PšžœžœŸ˜:Kšœ]˜]šœ˜Kš žœ žœžœžœ.ŸR˜ÂKšžœ1Ÿ ˜UK˜—Kšžœžœ˜—Kšžœ˜—K˜—Kšœ˜Kšœ]žœžœ˜iKšœ,˜,KšœŒžœ žœžœ˜ºK˜K˜—Kšœ žœžœžœ˜Pš œžœ7˜JKšœT™TKšœ™‡–B -- [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj]š œ˜Kšœžœ™>KšœI˜IKšœ5˜5Kš žœžœžœžœžœŸ˜CKšœ(˜(šžœ;žœžœž˜PšžœžœŸ˜:Kšœ#˜#Kšžœžœ˜—Kšžœ˜—Kšœ˜—Kšœ˜Kšœ]žœžœ˜iKšœ,˜,KšœŒžœ žœžœ˜ºKšœ˜K˜—šœžœžœ žœžœžœžœžœžœ˜OKšœžœ ˜0K˜=K˜K˜—š œžœžœ žœžœžœžœžœžœ˜IKšœžœ ˜0K˜;K˜K˜—š œžœF˜Uš œ˜!Kš¤>™>Kšœžœ˜Kšœžœ˜K˜K˜Kšœžœžœ˜BKš œ!žœžœžœ(žœ(˜¤KšœP˜PKšœ5˜5Kšœ žœ˜Kšœ žœ˜Kš žœžœžœžœžœ˜%Kšžœžœžœ˜@Kšœ(˜(Kšœ2˜2K˜šžœ;žœžœž˜Pšžœžœžœ'ž˜5Kšœ1˜1K˜0K˜Kšžœ˜—Jšœ$˜$J˜Kšžœ˜—KšžœžœŸ=˜hKšœ2žœ˜7KšœJ˜JJšœ0˜0Jšžœžœ žœžœ˜Kšœ˜—Kšœ~žœžœ˜‹KšœŒžœ žœžœ˜¹K˜K™—šœžœžœ žœžœžœžœžœžœ˜RKšœžœ ˜0Kš œ žœžœžœžœ$žœžœ˜lKšœD˜DKšœ1˜1šžœGžœžœž˜]Kšœ;˜;šžœkžœžœž˜Œ–[atom: ATOM]šžœ<žœžœžœžœ žœžœ˜iKšœF˜FKšœ9˜9Kšœ˜—Kšžœ˜—Kšžœ˜—K–Û[feedback: ViewerClasses.Viewer, msgType: GGError.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœa˜aKšœyžœ žœžœ˜¨K˜—K˜K™šœžœžœ žœžœžœžœžœžœ˜DKšœžœ ˜0Kšœžœžœ˜Kšœ ˜ Kšœ˜K˜ Kšœ žœ˜Kšœ-˜-šž˜Kšžœ žœžœžœ ˜%šžœžœž˜˜Kšœ@˜@Kšžœžœ žœžœ˜K˜—šœž˜Kšžœ žœžœ ˜8Kšœ=˜=Kšžœžœ žœžœ ˜%K˜—Kšžœžœ˜—Kšžœžœžœ˜AKšœ^˜^Kšœ+˜+Kšœ&˜&šžœžœ˜ Kšœ0žœ˜5Kšœ'˜'Kšœ4žœ˜:K˜—šžœ˜KšœC˜CKšœC˜CKšœ#˜#KšœG˜GK˜—Kšœe˜eK–x[traj: GGModelTypes.Traj, gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGSegmentTypes.SelectionClass]šœ6˜6K–£[caret: GGInterfaceTypes.Caret, on: GGInterfaceTypes.CaretOn _ nothing, chair: REF ANY _ NIL, jointNum: NAT _ 999, cpNum: NAT _ 999, segNum: NAT _ 999]šœ"žœ˜'K–![caret: GGInterfaceTypes.Caret]šœ/˜/Kšœ5˜5šœŒžœ žœžœ˜»Kšœ”™”—šž˜˜Kšœ`˜`Kšœ%˜%K˜—šœ˜Kšœ\˜\Kšœ%˜%K˜——Kšžœ˜—KšœŸ ˜K˜—š œžœžœ žœžœžœžœžœžœ™IKšœžœ ™0KšœS™SšžœRžœžœž™gKšœžœ™Kšžœ™—K™K™—š œžœžœ žœžœžœžœžœžœ™IKšœžœ ™0KšœS™SšžœRžœžœž™gKšœžœ™Kšžœ™—K™—K™K™ šœžœžœ žœžœžœžœžœžœ˜FKšœžœ ˜0Kš œ žœ žœžœ žœžœ&˜YKšœžœ žœžœ ˜¸K˜K˜—šœžœžœ žœžœžœžœžœžœ˜MKšœžœ ˜0Kšœ'žœ˜,K˜K˜—š œžœžœ žœžœžœžœžœžœ˜LKšœžœ ˜0Kšœ'žœ˜-K˜—K˜Kš $™$K™Kšžœžœžœžœžœžœžœžœ™.š œžœ4žœžœžœ˜dKšžœ˜š œžœžœžœžœ˜7Kšžœžœžœžœ˜6Kšœ˜—Kšœžœ7˜?Kšœžœ7˜?šžœ˜Kšœ$ž˜'Kšœ#˜#Kšœ˜—Kšœ˜—K˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœ6˜6Kšœžœ˜K˜šœžœ˜Kšœ$˜$Kšœ˜Kšœ˜K– [s: REAL]šœ7˜7Kšœ˜KšœAžœ˜cKšœ>žœ˜`K˜—K˜K˜K˜Kšžœ˜—…—æÀy