DIRECTORY Atom, GGAlign, GGCaret, GGError, GGGravity, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGObjects, GGRefresh, GGSegment, GGSelect, GGTouch, GGTransform, GGVector, GGWindow, Imager, ImagerTransformation, InputFocus, IO, Menus, Rope, Rosary; GGMouseEventImpl: CEDAR PROGRAM IMPORTS Atom, GGAlign, GGCaret, GGError, GGGravity, GGObjects, GGRefresh, GGSegment, GGSelect, GGTouch, GGTransform, GGVector, GGWindow, ImagerTransformation, InputFocus, IO, Rosary EXPORTS GGMouseEvent = BEGIN Caret: TYPE = GGInterfaceTypes.Caret; Cluster: TYPE = GGModelTypes.Cluster; EntityGenerator: TYPE = GGModelTypes.EntityGenerator; FeatureData: TYPE = GGGravity.FeatureData; Joint: TYPE = GGModelTypes.Joint; GargoyleData: TYPE = GGInterfaceTypes.GargoyleData; MouseButton: TYPE = Menus.MouseButton; ObjectBag: TYPE = GGGravity.ObjectBag; Outline: TYPE = GGModelTypes.Outline; Point: TYPE = GGModelTypes.Point; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGModelTypes.Segment; Sequence: TYPE = GGModelTypes.Sequence; TouchGroup: TYPE = GGModelTypes.TouchGroup; TouchItem: TYPE = GGModelTypes.TouchItem; TouchItemGenerator: TYPE = GGTouch.TouchItemGenerator; Traj: TYPE = GGModelTypes.Traj; TrajGenerator: TYPE = GGObjects.TrajGenerator; Vector: TYPE = GGModelTypes.Vector; NotYetImplemented: PUBLIC SIGNAL = CODE; SittingOnEnd: PROC [caret: Caret] RETURNS [BOOL] = { chair: Traj; isJoint: BOOL; jointNum: NAT; [chair, isJoint, ----, jointNum, ----] _ GGCaret.GetChair[caret]; IF chair = NIL THEN RETURN[FALSE]; IF NOT isJoint THEN RETURN[FALSE]; RETURN[GGObjects.IsEndJoint[chair, jointNum]]; }; ExtendTrajToMouse: PRIVATE PROC [scene: Scene, worldPt: Point, gargoyleData: GargoyleData] RETURNS [traj: Traj, new: BOOL, success: BOOL] = { caret: Caret _ gargoyleData.caret; newLine: Segment; caretPoint: Point; jointNum: NAT; caretPoint _ GGCaret.GetPoint[caret]; IF SittingOnEnd[caret] THEN { -- extend the existing trajectory new _ FALSE; [traj, ----, ----, jointNum, ----] _ GGCaret.GetChair[caret]; IF jointNum = 0 THEN { -- add to the low end of the trajectory newLine _ GGSegment.MakeLine[worldPt, caretPoint]; success _ GGObjects.AddSegment[traj, lo, newLine, hi]; IF NOT success THEN RETURN; GGCaret.Update[gargoyleData, worldPt, NIL]; GGCaret.SitOnJoint[gargoyleData.caret, traj, 0]; } ELSE IF jointNum = GGObjects.HiJoint[traj] THEN { -- add to the high end of the trajectory newLine _ GGSegment.MakeLine[caretPoint, worldPt]; success _ GGObjects.AddSegment[traj, hi, newLine, lo]; IF NOT success THEN RETURN; GGCaret.Update[gargoyleData, worldPt, NIL]; GGCaret.SitOnJoint[gargoyleData.caret, traj, jointNum + 1]; } ELSE ERROR; } ELSE { -- Create a new trajectory starting at the caret (making touching constraints, if any). newOutline: Outline; new _ TRUE; newLine _ GGSegment.MakeLine[caretPoint, worldPt]; traj _ GGObjects.CreateTraj[caretPoint]; success _ GGObjects.AddSegment[traj, hi, newLine, lo]; IF NOT success THEN RETURN; newOutline _ GGObjects.OutlineFromTraj[traj]; GGObjects.AddOutline[scene, newOutline, -1]; GGCaret.MakeChairTouchTrajJoint[gargoyleData.caret, gargoyleData, traj, 0]; GGCaret.Update[gargoyleData, worldPt, NIL]; GGCaret.SitOnJoint[gargoyleData.caret, traj, 1]; }; }; UpdateCaretToFeature: PROC [gargoyleData: GargoyleData, mapPoint: Point, feature: FeatureData] = { IF feature = NIL THEN GGCaret.Update[gargoyleData, mapPoint, NIL] ELSE SELECT feature.resultType FROM joint => GGCaret.Update[gargoyleData, mapPoint, feature.tseq.traj, TRUE, feature.jointNum]; segment => GGCaret.Update[gargoyleData, mapPoint, feature.tseq.traj, FALSE,,feature.segNum]; ENDCASE => GGCaret.Update[gargoyleData, mapPoint, NIL]; ReportFeature[feature, gargoyleData]; }; StartAdd: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { jointSeq: Sequence; trajUnderCaret: Traj; objectBag: ObjectBag; jointNum: NAT; new, success: BOOL; [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; IF NOT CheckProperAction[gargoyleData, $None] THEN { gargoyleData.currentAction _ $None; RETURN; }; gargoyleData.currentAction _ $StartAdd; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay GGWindow.SaveCaretPos[gargoyleData]; [trajUnderCaret, new, success] _ ExtendTrajToMouse[gargoyleData.scene, worldPt, gargoyleData]; IF NOT success THEN { gargoyleData.currentAction _ $None; RETURN; }; objectBag _ GGGravity.CreateObjectBag[]; gargoyleData.hitTest.environ _ objectBag; GGAlign.AddItemsForAction[gargoyleData, objectBag, $Add]; GGGravity.AdjustVisibilityPoint[worldPt, gargoyleData.hitTest.tolerance, objectBag]; [----, ----, ----, jointNum, ----] _ GGCaret.GetChair[gargoyleData.caret]; jointSeq _ GGObjects.CreateSimpleSequence[trajUnderCaret, jointNum, jointNum]; GGCaret.SetSequence[gargoyleData.caret, jointSeq]; GGRefresh.MoveToOverlay[jointSeq, gargoyleData]; -- traj on overlay (why = joint). GGRefresh.MoveToOverlay[gargoyleData.caret, gargoyleData]; -- caret on overlay gargoyleData.drag.startPoint _ worldPt; gargoyleData.drag.transform _ ImagerTransformation.Scale[1.0]; }; FirstDuringAdd: PRIVATE PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { IF NOT CheckProperAction[gargoyleData, $StartAdd] THEN RETURN; gargoyleData.currentAction _ $Add; GGRefresh.StoreBackground[gargoyleData]; -- all but caret and traj on background DuringAdd[input, gargoyleData, worldPt]; }; DuringAdd: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { totalDragVector: Vector; mapPoint: Point; feature: FeatureData; environ: ObjectBag _ NARROW[gargoyleData.hitTest.environ]; IF gargoyleData.currentAction = $StartAdd THEN { FirstDuringAdd[input, gargoyleData, worldPt]; RETURN; }; IF NOT CheckProperAction[gargoyleData, $Add] THEN RETURN; GGWindow.Painter[$EraseOverlay, gargoyleData]; -- Draw the background [mapPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, environ, gargoyleData]; UpdateCaretToFeature[gargoyleData, mapPoint, feature]; GGGravity.AdjustVisibilityPoint[worldPt, gargoyleData.hitTest.tolerance, environ]; totalDragVector _ GGVector.Sub[mapPoint, gargoyleData.drag.startPoint]; gargoyleData.drag.transform _ ImagerTransformation.Translate[[totalDragVector[1], totalDragVector[2]]]; GGWindow.Painter[$PaintDragOverlay, gargoyleData]; }; EndAdd: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { selSeq: Sequence; IF NOT CheckProperActions[gargoyleData, $Add, $StartAdd] THEN RETURN; selSeq _ GGCaret.GetSequence[gargoyleData.caret]; GGObjects.TransformSequence[selSeq, gargoyleData.drag.transform]; GGCaret.MakeChairTouchAttractor[gargoyleData.caret, gargoyleData]; GGRefresh.MoveOverlayToBackground[gargoyleData]; gargoyleData.currentAction _ $None; GGWindow.Painter[$PaintEntireScene, gargoyleData]; }; ReportFeature: PROC [feature: FeatureData, gargoyleData: GargoyleData] = { IF feature = NIL THEN { GGError.Append["", oneLiner]; } ELSE { SELECT feature.resultType FROM joint => GGError.Append["Caret on joint", oneLiner]; segment => GGError.Append["Caret on segment", oneLiner]; distanceLine => GGError.Append["Caret on distance line", oneLiner]; slopeLine => GGError.Append["Caret on slope line", oneLiner]; symmetryLine => GGError.Append["Caret on symmetry line", oneLiner]; radiiCircle => GGError.Append["Caret on compass circle", oneLiner]; intersectionPoint => GGError.Append["Caret on intersection point", oneLiner]; ENDCASE => ERROR; }; }; CheckProperAction: PROC [gargoyleData: GargoyleData, properState: ATOM] RETURNS [BOOL] = { msgRope: Rope.ROPE; IF gargoyleData.currentAction = properState THEN RETURN[TRUE]; IF gargoyleData.currentAction = $None THEN RETURN[FALSE]; -- but don't complain msgRope _ IO.PutFR["User switched from %g to %g. Gargoyle confused.", [rope[Atom.GetPName[properState]]], [rope[Atom.GetPName[gargoyleData.currentAction]]]]; GGError.Append[msgRope, oneLiner]; GGError.Blink[]; ResetMouseMachinery[gargoyleData]; RETURN[FALSE]; }; CheckProperActions: PROC [gargoyleData: GargoyleData, properState1, properState2: ATOM] RETURNS [BOOL] = { msgRope: Rope.ROPE; IF gargoyleData.currentAction = properState1 OR gargoyleData.currentAction = properState2 THEN RETURN[TRUE]; msgRope _ IO.PutFR["User switched from %g or %g to %g. Gargoyle confused.", [rope[Atom.GetPName[properState1]]], [rope[Atom.GetPName[properState2]]], [rope[Atom.GetPName[gargoyleData.currentAction]]]]; GGError.Append[msgRope, oneLiner]; GGError.Blink[]; ResetMouseMachinery[gargoyleData]; RETURN[FALSE]; }; ResetMouseMachinery: PUBLIC PROC [gargoyleData: GargoyleData] = { gargoyleData.currentAction _ $None; gargoyleData.hitTest.responsibleFor _ NIL; GGSelect.DeselectAll[gargoyleData, normal]; GGSelect.DeselectAll[gargoyleData, copy]; GGSelect.DeselectAll[gargoyleData, hot]; GGSelect.DeselectAll[gargoyleData, active]; GGRefresh.MoveOverlayToBackground[gargoyleData]; }; SitOnFeature: PROC [caret: Caret, feature: FeatureData] = { IF feature = NIL THEN { GGCaret.DoNotSit[caret]; RETURN; }; SELECT feature.resultType FROM joint => GGCaret.SitOnJoint[caret, feature.tseq.traj, feature.jointNum]; segment => GGCaret.SitOnSegment[caret, feature.tseq.traj, feature.segNum]; ENDCASE => GGCaret.DoNotSit[caret]; }; SelectAllTouchingFeature: PROC [feature: FeatureData, gargoyleData: GargoyleData] = { item: TouchItem; jointSeq, segSeq: Sequence; SELECT feature.resultType FROM joint => { item _ TouchItemOfJoint[feature.tseq.traj, feature.jointNum]; IF item = NIL THEN { jointSeq _ GGObjects.CreateSimpleSequence[feature.tseq.traj, feature.jointNum, feature.jointNum]; [----,----] _ GGSelect.SelectSequence[jointSeq, gargoyleData, normal]; IF GGObjects.IsEndJoint[feature.tseq.traj, feature.jointNum] THEN { -- an end joint GGError.Append["End Joint selected", oneLiner]; } ELSE { -- a middle joint GGError.Append["Middle Joint selected", oneLiner]; }; } ELSE { group: TouchGroup _ GGTouch.TouchGroupOfItem[item]; jointNum, segNum: NAT; itemGen: TouchItemGenerator; itemGen _ GGTouch.AllTouchItems[group]; FOR thisItem: TouchItem _ GGTouch.NextTouchItem[itemGen], GGTouch.NextTouchItem[itemGen] UNTIL thisItem = NIL DO SELECT thisItem.touchingPartType FROM joint => { jointNum _ GGObjects.IndexOfJoint[thisItem.joint, thisItem.traj]; jointSeq _ GGObjects.CreateSequenceFromJoint[thisItem.traj, jointNum]; [----,----] _ GGSelect.SelectSequence[jointSeq, gargoyleData, normal]; }; segment => { segNum _ GGObjects.IndexOfSegment[thisItem.seg, thisItem.traj]; segSeq _ GGObjects.CreateSequenceFromSegment[thisItem.traj, segNum]; [----,----] _ GGSelect.SelectSequence[segSeq, gargoyleData, normal]; }; ENDCASE; ENDLOOP; GGError.Append["Multiple entities selected", oneLiner]; }; }; ENDCASE => ERROR NotYetImplemented; }; TouchItemOfJoint: PROC [traj: Traj, jointNum: NAT] RETURNS [item: TouchItem] = { joint: Joint _ NARROW[Rosary.Fetch[traj.joints, jointNum]]; item _ joint.touchItem; }; UpdateCaretSelectionsAndOverlay: PRIVATE PROC [gargoyleData: GargoyleData, worldPt: Point] = { resultPoint: Point; feature: FeatureData; environ: ObjectBag; environ _ NARROW[gargoyleData.hitTest.environ]; [resultPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, environ, gargoyleData]; UpdateCaretToFeature[gargoyleData, resultPoint, feature]; GGSelect.DeselectAll[gargoyleData, normal]; IF feature = NIL THEN { -- no near trajectories, place caret in free space IF gargoyleData.hitTest.responsibleFor # NIL THEN { GGRefresh.RemoveJointsFromOverlay[gargoyleData.hitTest.responsibleFor.traj, gargoyleData]; gargoyleData.hitTest.responsibleFor _ NIL; }; } ELSE { SELECT feature.resultType FROM joint => { jointSeq: Sequence _ GGObjects.CreateSimpleSequence[feature.tseq.traj, feature.jointNum, feature.jointNum]; IF gargoyleData.hitTest.responsibleFor # NIL THEN { GGRefresh.RemoveJointsFromOverlay[gargoyleData.hitTest.responsibleFor.traj, gargoyleData]; }; gargoyleData.hitTest.responsibleFor _ jointSeq; [----,----] _ GGSelect.SelectSequence[jointSeq, gargoyleData, normal]; GGRefresh.MoveJointsToOverlay[feature.tseq.traj, gargoyleData]; }; segment => { -- we are in the middle of a segment seq: Sequence _ GGObjects.CreateSimpleSequence[feature.tseq.traj, feature.segNum, GGObjects.FollowingJoint[feature.tseq.traj, feature.segNum]]; IF gargoyleData.hitTest.responsibleFor # NIL THEN { GGRefresh.RemoveJointsFromOverlay[gargoyleData.hitTest.responsibleFor.traj, gargoyleData]; }; gargoyleData.hitTest.responsibleFor _ seq; [----,----] _ GGSelect.SelectSequence[seq, gargoyleData, normal]; GGRefresh.MoveJointsToOverlay[feature.tseq.traj, gargoyleData]; }; slopeLine, distanceLine, intersectionPoint, radiiCircle => { }; ENDCASE => ERROR NotYetImplemented; }; GGGravity.AdjustVisibilityPoint[worldPt, gargoyleData.hitTest.tolerance, environ]; }; StartSelectPoint: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { environ: ObjectBag; IF NOT CheckProperAction[gargoyleData, $None] THEN { gargoyleData.currentAction _ $None; RETURN; }; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; GGWindow.SaveCaretPos[gargoyleData]; gargoyleData.currentAction _ $StartSelectPoint; GGRefresh.MoveToOverlay[gargoyleData.caret, gargoyleData]; -- caret on overlay environ _ GGGravity.CreateObjectBag[]; gargoyleData.hitTest.environ _ environ; GGAlign.AddItemsForAction[gargoyleData, environ, $SelectPoint]; GGSelect.DeselectAll[gargoyleData, normal]; }; FirstDuringSelectPoint: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { IF NOT CheckProperAction[gargoyleData, $StartSelectPoint] THEN RETURN; gargoyleData.currentAction _ $SelectPoint; GGRefresh.StoreBackground[gargoyleData]; -- all but caret is background GGWindow.Painter[$EraseOverlay, gargoyleData]; -- show all but caret UpdateCaretSelectionsAndOverlay[gargoyleData, worldPt]; GGWindow.Painter[$PaintDragOverlay, gargoyleData]; -- show caret in new position }; DuringSelectPoint: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { IF gargoyleData.currentAction = $StartSelectPoint THEN { FirstDuringSelectPoint[input, gargoyleData, worldPt]; RETURN; }; IF NOT CheckProperAction[gargoyleData, $SelectPoint] THEN RETURN; GGWindow.Painter[$EraseOverlay, gargoyleData]; -- show all but caret UpdateCaretSelectionsAndOverlay[gargoyleData, worldPt]; GGWindow.Painter[$PaintDragOverlay, gargoyleData]; -- show caret in new position }; EndSelectPoint: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { resultPoint: Point; feature: FeatureData; environ: ObjectBag; IF NOT CheckProperActions[gargoyleData, $SelectPoint, $StartSelectPoint] THEN RETURN; GGWindow.Painter[$EraseOverlay, gargoyleData]; -- show all but caret environ _ NARROW[gargoyleData.hitTest.environ]; [resultPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, environ, gargoyleData]; UpdateCaretToFeature[gargoyleData, resultPoint, feature]; SitOnFeature[gargoyleData.caret, feature]; GGSelect.DeselectAll[gargoyleData, normal]; IF gargoyleData.hitTest.responsibleFor # NIL THEN { GGRefresh.RemoveJointsFromOverlay[gargoyleData.hitTest.responsibleFor.traj, gargoyleData]; gargoyleData.hitTest.responsibleFor _ NIL; }; IF feature = NIL THEN { -- no near trajectories, place caret in free space } ELSE { GGSelect.DeselectAll[gargoyleData, normal]; SELECT feature.resultType FROM joint => { SelectAllTouchingFeature[feature, gargoyleData]; gargoyleData.extendMode _ joint; }; segment => { -- we are in the middle of a segment seq: Sequence _ GGObjects.CreateSimpleSequence[feature.tseq.traj, feature.segNum, GGObjects.FollowingJoint[feature.tseq.traj, feature.segNum]]; [----,----] _ GGSelect.SelectSequence[seq, gargoyleData, normal]; GGError.Append["Segment selected", oneLiner]; gargoyleData.extendMode _ segment; }; slopeLine => { GGError.Append["Direction line hit.", oneLiner]; }; intersectionPoint => { GGError.Append["Intersection point hit.", oneLiner]; }; radiiCircle => { GGError.Append["Compass circle hit.", oneLiner]; }; distanceLine => { GGError.Append["Distance line hit.", oneLiner]; }; ENDCASE => ERROR NotYetImplemented; }; GGRefresh.MoveOverlayToBackground[gargoyleData]; -- caret is back in background gargoyleData.currentAction _ $None; GGWindow.Painter[$PaintEntireScene, gargoyleData]; -- caret in proper place }; UpdateSelectedAfterMove: PROC [gargoyleData: GargoyleData] = { entityGen: EntityGenerator; GGTouch.InitializeTouching[gargoyleData]; entityGen _ GGSelect.SelectedEntities[gargoyleData, normal]; FOR entity: REF ANY _ GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM selSeq: Sequence => { GGObjects.TransformSequence[selSeq, gargoyleData.drag.transform]; GGTouch.SequenceMoved[selSeq, gargoyleData]; }; outline: Outline => { FOR trajs: LIST OF Traj _ outline.children, trajs.rest UNTIL trajs = NIL DO GGObjects.TransformTraj[trajs.first, gargoyleData.drag.transform]; GGTouch.TrajMoved[trajs.first, gargoyleData]; ENDLOOP; }; cluster: Cluster => ERROR NotYetImplemented; ENDCASE => ERROR NotYetImplemented; ENDLOOP; }; StartDrag: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { startUpBag, duringBag: ObjectBag; mapPoint: Point; feature: FeatureData; IF NOT CheckProperAction[gargoyleData, $None] THEN { gargoyleData.currentAction _ $None; RETURN; }; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; IF GGSelect.NoSelections[gargoyleData, normal] THEN { GGError.Append["Select some objects to drag.", oneLiner]; GGError.Blink[]; gargoyleData.currentAction _ $None; RETURN; }; GGWindow.SaveCaretPos[gargoyleData]; gargoyleData.currentAction _ $Drag; GGRefresh.MoveToOverlay[gargoyleData.caret, gargoyleData]; -- caret on overlay GGRefresh.MoveAllSelectedToOverlay[gargoyleData, normal]; -- selected objects on overlay startUpBag _ GGGravity.CreateObjectBag[]; GGAlign.AddItemsForAction[gargoyleData, startUpBag, $DragStartUp]; [mapPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, startUpBag, gargoyleData]; IF feature = NIL THEN gargoyleData.drag.startPoint _ worldPt ELSE gargoyleData.drag.startPoint _ mapPoint; GGCaret.Update[gargoyleData, gargoyleData.drag.startPoint, NIL]; SitOnFeature[gargoyleData.caret, feature]; duringBag _ GGGravity.CreateObjectBag[]; gargoyleData.hitTest.environ _ duringBag; GGAlign.AddItemsForAction[gargoyleData, duringBag, $Drag]; gargoyleData.drag.transform _ ImagerTransformation.Scale[1.0]; GGRefresh.StoreBackground[gargoyleData]; -- all but selected and caret are on the background }; DuringDrag: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { totalDragVector: Vector; mapPoint: Point; feature: FeatureData; environ: ObjectBag _ NARROW[gargoyleData.hitTest.environ]; IF NOT CheckProperAction[gargoyleData, $Drag] THEN RETURN; GGWindow.Painter[$EraseOverlay, gargoyleData]; -- Draw the background [mapPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, environ, gargoyleData]; UpdateCaretToFeature[gargoyleData, mapPoint, feature]; GGGravity.AdjustVisibilityPoint[worldPt, gargoyleData.hitTest.tolerance, environ]; totalDragVector _ GGVector.Sub[mapPoint, gargoyleData.drag.startPoint]; gargoyleData.drag.transform _ ImagerTransformation.Translate[[totalDragVector[1], totalDragVector[2]]]; GGWindow.Painter[$PaintDragOverlay, gargoyleData]; }; EndDrag: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { IF NOT CheckProperAction[gargoyleData, $Drag] THEN RETURN; UpdateSelectedAfterMove[gargoyleData]; GGCaret.MakeChairTouchAttractor[gargoyleData.caret, gargoyleData]; GGRefresh.MoveOverlayToBackground[gargoyleData]; gargoyleData.currentAction _ $None; GGWindow.Painter[$PaintSelectedRegion, gargoyleData]; }; StartRotate: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { startUpBag, duringBag: ObjectBag; mapPoint: Point; feature: FeatureData; IF NOT CheckProperAction[gargoyleData, $None] THEN { gargoyleData.currentAction _ $None; RETURN; }; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; IF GGSelect.NoSelections[gargoyleData, normal] THEN { GGError.Append["Select some objects to rotate.", oneLiner]; GGError.Blink[]; gargoyleData.currentAction _ $None; RETURN; }; IF NOT GGCaret.Exists[gargoyleData.anchor] THEN { GGError.Append["Place an anchor to rotate around.", oneLiner]; GGError.Blink[]; gargoyleData.currentAction _ $None; RETURN; }; GGWindow.SaveCaretPos[gargoyleData]; gargoyleData.currentAction _ $Rotate; GGRefresh.MoveToOverlay[gargoyleData.caret, gargoyleData]; -- caret on overlay GGRefresh.MoveAllSelectedToOverlay[gargoyleData, normal]; -- selected objects on overlay startUpBag _ GGGravity.CreateObjectBag[]; GGAlign.AddItemsForAction[gargoyleData, startUpBag, $DragStartUp]; [mapPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, startUpBag, gargoyleData]; IF feature = NIL THEN gargoyleData.drag.startPoint _ worldPt ELSE gargoyleData.drag.startPoint _ mapPoint; duringBag _ GGGravity.CreateObjectBag[]; gargoyleData.hitTest.environ _ duringBag; GGAlign.AddItemsForAction[gargoyleData, duringBag, $Drag]; GGCaret.Update[gargoyleData, gargoyleData.drag.startPoint, NIL]; gargoyleData.drag.transform _ ImagerTransformation.Scale[1.0]; GGRefresh.StoreBackground[gargoyleData]; -- all but selected and caret are on the background }; DuringRotate: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { originalVector, newVector: Vector; mapPoint: Point; feature: FeatureData; environ: ObjectBag _ NARROW[gargoyleData.hitTest.environ]; degrees: REAL; anchorPoint: Point; IF NOT CheckProperAction[gargoyleData, $Rotate] THEN RETURN; anchorPoint _ GGCaret.GetPoint[gargoyleData.anchor]; GGWindow.Painter[$EraseOverlay, gargoyleData]; [mapPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, environ, gargoyleData]; UpdateCaretToFeature[gargoyleData, mapPoint, feature]; GGGravity.AdjustVisibilityPoint[worldPt, gargoyleData.hitTest.tolerance, environ]; originalVector _ GGVector.Sub[gargoyleData.drag.startPoint, anchorPoint]; newVector _ GGVector.Sub[mapPoint, anchorPoint]; degrees _ GGVector.AngleCCWBetweenVectors[originalVector, newVector]; gargoyleData.drag.transform _ GGTransform.RotateAboutPoint[anchorPoint, degrees]; GGWindow.Painter[$PaintDragOverlay, gargoyleData]; }; EndRotate: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { IF NOT CheckProperAction[gargoyleData, $Rotate] THEN RETURN; UpdateSelectedAfterMove[gargoyleData]; GGRefresh.MoveOverlayToBackground[gargoyleData]; gargoyleData.currentAction _ $None; GGWindow.Painter[$PaintSelectedRegion, gargoyleData]; }; StartScale: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { startUpBag, duringBag: ObjectBag; mapPoint: Point; feature: FeatureData; IF NOT CheckProperAction[gargoyleData, $None] THEN { gargoyleData.currentAction _ $None; RETURN; }; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; IF GGSelect.NoSelections[gargoyleData, normal] THEN { GGError.Append["Select some objects to scale.", oneLiner]; GGError.Blink[]; gargoyleData.currentAction _ $None; RETURN; }; IF NOT GGCaret.Exists[gargoyleData.anchor] THEN { GGError.Append["Place an anchor to scale around.", oneLiner]; GGError.Blink[]; gargoyleData.currentAction _ $None; RETURN; }; GGWindow.SaveCaretPos[gargoyleData]; gargoyleData.currentAction _ $Scale; GGRefresh.MoveToOverlay[gargoyleData.caret, gargoyleData]; -- caret on overlay GGRefresh.MoveAllSelectedToOverlay[gargoyleData, normal]; -- selected objects on overlay startUpBag _ GGGravity.CreateObjectBag[]; GGAlign.AddItemsForAction[gargoyleData, startUpBag, $DragStartUp]; [mapPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, startUpBag, gargoyleData]; IF feature = NIL THEN gargoyleData.drag.startPoint _ worldPt ELSE gargoyleData.drag.startPoint _ mapPoint; duringBag _ GGGravity.CreateObjectBag[]; gargoyleData.hitTest.environ _ duringBag; GGAlign.AddItemsForAction[gargoyleData, duringBag, $Drag]; GGCaret.Update[gargoyleData, gargoyleData.drag.startPoint, NIL]; gargoyleData.drag.transform _ ImagerTransformation.Scale[1.0]; GGRefresh.StoreBackground[gargoyleData]; -- all but selected and caret are on the background }; DuringScale: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { epsilon: REAL = 1.0e-3; originalVector, newVector: Vector; mapPoint: Point; feature: FeatureData; environ: ObjectBag _ NARROW[gargoyleData.hitTest.environ]; ratio: REAL; anchorPoint: Point; IF NOT CheckProperAction[gargoyleData, $Scale] THEN RETURN; anchorPoint _ GGCaret.GetPoint[gargoyleData.anchor]; GGWindow.Painter[$EraseOverlay, gargoyleData]; [mapPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, environ, gargoyleData]; UpdateCaretToFeature[gargoyleData, mapPoint, feature]; GGGravity.AdjustVisibilityPoint[worldPt, gargoyleData.hitTest.tolerance, environ]; originalVector _ GGVector.Sub[gargoyleData.drag.startPoint, anchorPoint]; newVector _ GGVector.Sub[mapPoint, anchorPoint]; ratio _ GGVector.Magnitude[newVector]/GGVector.Magnitude[originalVector]; gargoyleData.drag.transform _ GGTransform.ScaleAboutPoint[anchorPoint, ratio]; GGWindow.Painter[$PaintDragOverlay, gargoyleData]; }; EndScale: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { IF NOT CheckProperAction[gargoyleData, $Scale] THEN RETURN; UpdateSelectedAfterMove[gargoyleData]; GGRefresh.MoveOverlayToBackground[gargoyleData]; gargoyleData.currentAction _ $None; GGWindow.Painter[$PaintSelectedRegion, gargoyleData]; }; NearestTrajectory: PROC [scene: Scene, worldPt: Point, gargoyleData: GargoyleData] RETURNS [traj: Traj] = { objectBag: ObjectBag; feature: FeatureData; resultPoint: Point; objectBag _ GGGravity.CreateObjectBag[]; GGAlign.AddItemsForAction[gargoyleData, objectBag, $SelectTrajectory]; [resultPoint, feature] _ GGGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, objectBag]; traj _ IF feature = NIL THEN NIL ELSE feature.tseq.traj; }; StartSelectTrajectory: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { nearTraj: Traj; IF NOT CheckProperAction[gargoyleData, $None] THEN { gargoyleData.currentAction _ $None; RETURN; }; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; GGSelect.DeselectAll[gargoyleData, normal]; nearTraj _ NearestTrajectory[gargoyleData.scene, worldPt, gargoyleData]; IF nearTraj # NIL THEN { GGSelect.SelectTraj[nearTraj, gargoyleData, normal]; GGError.Append["Trajectory Selected", oneLiner]; gargoyleData.extendMode _ traj; } ELSE { GGError.Append["No near trajectory found.", oneLiner]; }; GGWindow.Painter[$PaintEntireScene, gargoyleData]; }; StartCopySelectTrajectory: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { nearTraj, newTraj: Traj; newOutline, oldOutline: Outline; translatePoint, caretPos, firstJointPos: Point; translate: ImagerTransformation.Transformation; IF NOT CheckProperAction[gargoyleData, $None] THEN { gargoyleData.currentAction _ $None; RETURN; }; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; GGSelect.DeselectAll[gargoyleData, copy]; nearTraj _ NearestTrajectory[gargoyleData.scene, worldPt, gargoyleData]; IF nearTraj # NIL THEN { GGError.Append["Trajectory Copy Selected", oneLiner]; GGSelect.SelectTraj[nearTraj, gargoyleData, copy]; oldOutline _ GGObjects.OutlineOfTraj[nearTraj]; newTraj _ GGObjects.CopyTraj[nearTraj]; newOutline _ GGObjects.OutlineFromTraj[newTraj, oldOutline.lineEnds, oldOutline.fillColor]; GGObjects.AddOutline[gargoyleData.scene, newOutline, -1]; firstJointPos _ GGObjects.FetchJointPos[newTraj, 0]; caretPos _ GGCaret.GetPoint[gargoyleData.caret]; translatePoint _ GGVector.Sub[caretPos, firstJointPos]; translate _ ImagerTransformation.Translate[[translatePoint[1], translatePoint[2]]]; GGObjects.TransformTraj[newTraj, translate]; } ELSE { GGError.Append["No near trajectory found.", oneLiner]; }; GGWindow.Painter[$PaintEntireScene, gargoyleData]; }; StartExtend: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { nearTraj: Traj; IF NOT CheckProperAction[gargoyleData, $None] THEN { gargoyleData.currentAction _ $None; RETURN; }; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; SELECT gargoyleData.extendMode FROM controlPoint, outline, cluster, joint, segment => { GGError.Append["Extend selection is only implemented for trajectories.", oneLiner]; RETURN; }; traj => { nearTraj _ NearestTrajectory[gargoyleData.scene, worldPt, gargoyleData]; IF nearTraj # NIL THEN { GGSelect.SelectTraj[nearTraj, gargoyleData, normal]; GGError.Append["Additional Trajectory Selected", oneLiner]; } ELSE { GGError.Append["No near trajectory found.", oneLiner]; }; }; ENDCASE => ERROR; GGWindow.Painter[$PaintEntireScene, gargoyleData]; }; END. šGGMouseEventImpl.mesa Last edited by Bier on November 7, 1985 11:53:33 pm PST Contents: Once a mouse event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Stone, August 5, 1985 4:14:05 pm PDT If the caret is on the end of a trajectory, then extend the trajectory from that end. Otherwise, create a new simple outline with a segment from the caret to the current mouse position. In either case, move the trajectory to the overlay plane so we can rubberband the new line. This is a little strange, since the new line is not selected. The caret sticks to the new endpoint. Add a new segment. Choose appropriate alignment lines. Move the trajectory's outline to the overlay plane. Initialize the transformation and remember the starting position. Map the endpoint and the caret and reposition them. The dragging is done. Update the endpoint with the current transform. After this time, a Splice will create a new trajectory. And deselect anything we're responsible for. The user is about to try to pick a trajectory point. While he holds the mouse down, we provide selection feedback in the form of new caret positions. Build the alignment lines bag. Eventually, this procedure should be replaced by putting optimizations into GGRefreshImpl (e.g. the paint queue). In the mean while, we want the user to be able to select a point without waiting to long. So, if the mouse goes down-up without moving, we do not save the background, draw the alignment lines or any such hurrendous things. These wait until the first mouse motion, which is handled by this procedure. This procedure has several purposes. First off, we try to find a joint which is near the mouse and put the caret on it. If the joint is a trajectory endpoint, then it can serve as a site for extending a trajectory. Otherwise, it can serve as the beginning of a new trajectory. If no joints are near the mouse, then we position the caret in free space. If a near joint is found, make it selected. Use gravity to find a nearby point on the selected objects. Move the selected objects so that this point coincides with the cursor. Then we can use the same gravity paradigm that we used for SelectPoint: The caret is drawn toward interesting lines. We should also keep track of which object point the caret is on so we can create fragile touching constraints. The caret and the selected objects will be on the overlay plane. Build the alignment lines bag. Time to move the nearby object (if any) to the cursor. Build the alignment lines bag. We initialize the transform to the identity. Later, it will describe how far the mappoint has moved from the startpoint. We keep track of the total vector of motion since the drag began (i.e. the difference between the initial mouse position and the current mouse position. We transform all of the dragged objects before redrawing them. This style should be better than changing the actual data since there is less accumulated error, and the undo transformation is easily derived. The dragging is done. Update all of the drag entities with the total drag vector and repaint the entire scene. Use gravity to find a nearby point on the selected objects. Move the selected objects so that this point coincides with the cursor. Then we can use the same gravity paradigm that we used for SelectPoint: The caret is drawn toward interesting lines. We should also keep track of which object point the caret is on so we can create fragile touching constraints. The caret and the selected objects will be on the overlay plane. Build the alignment lines bag. Time to move the nearby object (if any) to the cursor. Build the alignment lines bag. We initialize the transform to the identity. Later, it will describe how far the mappoint has moved from the startpoint. We keep track of the total vector of motion since the drag began (i.e. the difference between the initial mouse position and the current mouse position. We transform all of the dragged objects before redrawing them. This style should be better than changing the actual data since there is less accumulated error, and the undo transformation is easily derived. The rotating is done. Update all of the drag entities with the full rotation and repaint the entire scene. Use gravity to find a nearby point on the selected objects. Move the selected objects so that this point coincides with the cursor. Then we can use the same gravity paradigm that we used for SelectPoint: The caret is drawn toward interesting lines. We should also keep track of which object point the caret is on so we can create fragile touching constraints. The caret and the selected objects will be on the overlay plane. Build the alignment lines bag. Time to move the nearby object (if any) to the cursor. Build the alignment lines bag. We initialize the transform to the identity. Later, it will describe how far the mappoint has moved from the startpoint. We keep track of the total vector of motion since the drag began (i.e. the difference between the initial mouse position and the current mouse position. We transform all of the dragged objects before redrawing them. This style should be better than changing the actual data since there is less accumulated error, and the undo transformation is easily derived. ratioX, ratioY: REAL; IF ABS[originalVector[1]] > epsilon THEN ratioX _ ABS[newVector[1]/originalVector[1]] ELSE ratioX _ 1.0; IF ABS[originalVector[2]] > epsilon THEN ratioY _ ABS[newVector[2]/originalVector[2]] ELSE ratioY _ 1.0; IF ratioX > epsilon AND ratioY > epsilon THEN gargoyleData.drag.transform _ GGTransform.ScaleUnevenAboutPoint[anchorPoint, ratioX, ratioY]; The rotating is done. Update all of the drag entities with the full rotation and repaint the entire scene. Deselect all selections. Locate the nearest trajectory and make it the one and only selected entity. Deselect all cpy selections. Locate the nearest trajectory and make it the one and only copy selected entity. Ê£˜Ihead1™J™7™…Icode™$—J™šÏk ˜ J˜Jšœ˜J˜Jšœ˜Jšœ ˜ Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ ˜ J˜Jšœ ˜ Jšœ ˜ Jšœ ˜ J˜Jšœ˜Jšœ ˜ J˜Jšœ˜Jšœ˜Jšœ˜J˜—šœœ˜Jšœ®˜µJšœ˜—Jš˜J˜Jšœœ˜%Jšœ œ˜%Jšœœ ˜5Jšœ œ˜*Jšœœ˜!Jšœœ!˜3Jšœ œ˜&Jšœ œ˜&Jšœ œ˜%Jšœœ˜!Jšœœ˜!Jšœ œ˜%Jšœ œ˜'Jšœ œ˜+Jšœ œ˜)Jšœœ˜6Jšœœ˜Jšœœ˜.Jšœœ˜#J˜Jšœœœœ˜(šÏn œœœœ˜4J˜ Jšœ œ˜Jšœ œ˜JšœÏcœ Ÿœ˜AJš œ œœœœ˜"Jš œœ œœœ˜"Jšœ(˜.J˜J˜—š žœœœ<œœ œ˜Jšœ"˜"Jšœ˜J˜Jšœ œ˜Jšœ%˜%šœœŸ!˜?Jšœœ˜ JšœŸœŸœ Ÿœ˜=šœœŸ'˜>Jšœ2˜2Jšœ6˜6Jšœœ œœ˜JšÏbœœ˜+Jš œ˜0J˜—šœœ$œŸ(˜ZJšœ2˜2Jšœ6˜6Jšœœ œœ˜Jš œœ˜+Jš œ)˜;J˜—Jšœœ˜ J˜—šœŸW˜^J˜Jšœœ˜ Jšœ2˜2Jšœ(˜(Jšœ6˜6Jšœœ œœ˜Jšœ-˜-Jšœ,˜,Jš œ,˜KJš œœ˜+Jš œ˜0J˜—J˜J˜—šžœœH˜bJšœ œœ(œ˜Aš˜Jšœ˜JšœCœ˜[JšœEœ˜\Jšœ+œ˜7—Jšœ%˜%J˜J˜—J˜šžœœœ œœœœ1˜^J™ýJ˜Jšœ˜J˜J˜Jšœœ˜Jšœ7˜7šœœ(œ˜4Jšœ#˜#Jšœ˜J˜—Jšœ'˜'Jš œœ   œœœŸ˜Nšœ$˜$J™—Jšœ^˜^šœœ œ˜Jšœ#˜#Jšœ˜J˜J™#—Jšœ(˜(Jšœ)˜)Jšœ9˜9šœT˜TJšœ3™3—Jš œŸœŸœŸœ Ÿœ)˜JJšœN˜NJšœ2˜2Jšœ2Ÿ!˜Sšœ<Ÿ˜OJ™A—Jšœ'˜'Jšœ>˜>J˜J˜—šžœœœ œœœœ1˜eJšœœ,œœ˜>Jšœ"˜"Jšœ*Ÿ'˜QJšœ(˜(J˜J˜—šž œœœ œœœœ1˜_J™3J˜Jšœ˜Jšœ˜Jšœœ˜:J˜šœ(œ˜0Jšœ-˜-Jšœ˜J˜—Jšœœ'œœ˜9Jšœ0Ÿ˜FJšœd˜dJšœ6˜6JšœR˜RJšœG˜GJšœg˜gJšœ2˜2J˜J˜—šžœœœ œœœœ1˜\J™FJšœ˜Jšœœ3œœ˜EJšœ2˜2JšœA˜AJšœB˜BJšœ  œ˜0Jšœ#˜#Jšœ2˜2J˜—J˜šž œœ7˜Jšœ œœ˜Jšœ˜J˜—šœ˜Jšœ˜Jšœ4˜4Jšœ8˜8JšœC˜CJšœ=˜=JšœC˜CJšœC˜CJšœM˜MJšœœ˜J˜—J˜J˜—š žœœ+œœœ˜ZJšœœ˜Jšœ*œœœ˜>Jš œ$œœœŸ˜OJšœ œ’˜žJšœ"˜"Jšœ˜Jšœ"˜"Jšœœ˜J˜J˜—š žœœ:œœœ˜jJšœœ˜Jš œ+œ+œœœ˜lJšœ œ¾˜ÊJšœ"˜"Jšœ˜Jš "˜"Jšœœ˜J˜J˜J˜—šžœœœ!˜AJšœ#˜#Jšœ&œ˜*Jšœ+˜+Jšœ)˜)Jšœ(˜(Jšœ+˜+Jšœ  œ˜0J˜J˜—J˜šž œœ)˜;šœ œœ˜Jšœ˜Jšœ˜J˜—Jšœ˜JšœH˜HJšœJ˜JJšœ˜#J˜J˜—šžœœ7˜UJ˜Jšœ˜Jšœ˜šœ ˜ Jšœ=˜=šœœœ˜Jšœb˜bJšœŸœŸœ<˜Fšœ;œŸ˜TJšœ/˜/J˜—šœŸ˜Jšœ7™7Jšœ2˜2J˜—J˜—šœ˜Jšœ3˜3Jšœœ˜J˜Jšœ'˜'šœVœ œ˜pJšœ˜%˜ JšœA˜AJšœF˜FJšœŸœŸœ<˜FJ˜—˜ Jšœ?˜?JšœD˜DJšœŸœŸœ:˜DJ˜—Jšœ˜—Jšœ˜Jšœ7˜7J˜—J˜—Jšœœ˜#J˜J˜—šžœœœœ˜PJšœœ&˜;J˜J˜J˜—J˜šžœœœ1˜^Jšœ˜Jšœ˜J˜Jšœ œ˜/Jšœg˜gJš œ%˜9Jšœ   œ˜+šœ œœŸ2˜JJ™,šœ'œœ˜3Jšœ  œ9˜ZJšœ&œ˜*J˜—J˜—šœ˜Jšœ˜šœ ˜ Jšœk˜kšœ'œœ˜3Jšœ  œ9˜ZJ˜—Jšœ/˜/JšœŸœŸœ<˜FJšœ  œ"˜?J˜—šœ Ÿ$˜1Jšœ˜šœ'œœ˜3Jšœ  œ9˜ZJ˜—Jšœ*˜*JšœŸœŸœ7˜AJšœ  œ"˜?J˜—šœ<˜J˜Jšœ œ˜)Jšœ<˜<š œ œœDœ œ˜lJšœœ˜šœ˜JšœA˜AJšœ  œ˜,J˜—˜š œœœ%œ œ˜KJšœB˜BJšœ  œ˜-—Jšœ˜J˜—Jšœœ˜,Jšœœ˜#—Jšœ˜J˜J˜—šž œœœ œœœœ1˜_J™ûJ™nJ™@J˜!J˜Jšœ˜šœœ(œ˜4Jšœ#˜#Jšœ˜J˜—Jš œœ   œœœŸ˜NJšœ7˜7šœ-œ˜5Jšœ9˜9J˜Jšœ#˜#Jšœ˜J˜—Jš $˜$Jšœ#˜#J˜Jšœ<Ÿ˜OšœY˜YJ™J™—Jšœ)˜)šœžœ)˜BJ™6—Jšœg˜gJšœ œœ'˜˜>J™y—Jšœ)Ÿ3˜\J˜J˜—šž œœœ œœœœ1˜`J™éJ˜Jšœ˜Jšœ˜Jšœœ˜:Jšœœ(œœ˜:J˜JšœF˜FJšœ  œA˜dJš œ"˜6JšœR˜RJšœG˜GJšœg˜gJšœ2˜2J˜J˜—šžœœœ œœœœ1˜]J™oJšœœ(œœ˜:J˜Jšœ&˜&JšœB˜BJšœ  œ˜0Jšœ#˜#Jšœ5˜5J˜—J˜šž œœœ œœœœ1˜aJ™ûJ™nJ™@J˜!J˜Jšœ˜šœœ(œ˜4Jšœ#˜#Jšœ˜J˜—Jš œœ   œœœŸ˜NJšœ7˜7šœ-œ˜5Jšœ;˜;J˜Jšœ#˜#Jšœ˜J˜—šœœ%œ˜1Jšœ>˜>J˜Jšœ#˜#Jšœ˜J˜—Jš $˜$Jšœ%˜%J˜Jšœ<Ÿ˜OšœY˜YJ™J™—Jšœ)˜)šœžœ)˜BJ™J™6—Jšœg˜gJšœ œœ'˜˜>J™y—Jšœ)Ÿ3˜\J˜J˜—šž œœœ œœœœ1˜bJ™éJ˜"Jšœ˜Jšœ˜Jšœœ˜:Jšœ œ˜J˜Jšœœ*œœ˜˜>J™y—Jšœ)Ÿ3˜\J˜J˜—šž œœœ œœœœ1˜aJ™éJšœ œ ˜J˜"Jšœ˜Jšœ˜Jšœœ˜:Jšœœ˜ Jšœœ™J˜Jšœœ)œœ˜;Jšœ4˜4Jšœ.˜.Jšœ  œA˜dJšœ6˜6JšœR˜RJšœI˜IJšœ0˜0JšœI˜IJšœ* œ˜NJšœœœ œ ™UJšœ™Jšœœœ œ ™UJšœ™šœœ™-Jšœ* œ™]—Jšœ2˜2J˜J˜—šžœœœ œœœœ1˜^J™kJšœœ)œœ˜;J˜Jšœ&˜&Jšœ  œ˜0Jšœ#˜#Jšœ5˜5J˜J˜—J˜šžœœ<œ˜kJšœ˜Jšœ˜Jšœ˜Jšœ(˜(JšœF˜FJšœf˜fJš œœ œœœœ˜8J˜J˜J˜—šžœœœ œœœœ1˜kJ™eJšœ˜šœœ(œ˜4Jšœ#˜#Jšœ˜J˜—Jš œœ   œœœ˜7Jšœ7˜7Jšœ+˜+JšœH˜Hšœ œœ˜Jšœ4˜4Jšœ0˜0Jšœ˜J˜—šœ˜Jšœ6˜6J˜—Jšœ2˜2J˜J˜—šžœœœ œœœœ1˜oJ™nJšœ˜Jšœ ˜ Jšœ/˜/Jšœ/˜/šœœ(œ˜4Jšœ#˜#Jšœ˜J˜—Jš œœ   œœœ˜7Jšœ7˜7Jšœ)˜)JšœH˜Hšœ œœ˜Jšœ5˜5Jšœ2˜2Jšœ/˜/Jšœ'˜'Jšœ[˜[Jšœ9˜9Jšœ4˜4Jšœ0˜0Jšœ7˜7JšœS˜SJšœ,˜,J˜—šœ˜Jšœ6˜6J˜—Jšœ2˜2J˜J˜—˜J˜—šž œœœ œœœœ1˜aJšœ˜šœœ(œ˜4Jšœ#˜#Jšœ˜J˜—Jš œœ   œœœ˜7Jšœ7˜7Jšœ˜#šœ3˜3JšœS˜SJšœ˜J˜—šœ ˜ JšœH˜Hšœ œœ˜Jšœ4˜4Jšœ;˜;J˜—šœ˜Jšœ6˜6J˜—J˜—Jšœœ˜Jšœ2˜2J˜J˜—J˜Jšœ˜J˜—…—sZ¦—