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¦—