DIRECTORY BiScrollers, CodeTimer, Commander, EBEditors, EBTypes, EmbeddedButtons, Feedback, FeedbackTypes, GGActive, GGAlign, GGBasicTypes, GGControlPanelTypes, GGCoreTypes, GGEmbedTypes, GGEvent, GGEventExtras, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGProps, GGScene, GGSliceOps, GGState, GGUIUtility, GGUserInput, GGWindow, Imager, ImagerTransformation, InputFocus, IO, KeyGlyphs, List, MultiCursors, Rope, TIPPrivate, TIPTypes, TIPUser, TIPUserExtras, UserInput, ViewerClasses, ViewerOps; GGActiveImpl: CEDAR PROGRAM IMPORTS InputFocus, BiScrollers, CodeTimer, Commander, EBEditors, EmbeddedButtons, Feedback, GGActive, GGAlign, GGEventExtras, GGMouseEvent, GGMultiGravity, GGProps, GGScene, GGSliceOps, GGState, GGUIUtility, GGUserInput, GGWindow, ImagerTransformation, IO, List, MultiCursors, TIPPrivate, TIPUserExtras, UserInput, ViewerOps EXPORTS EBTypes, GGActive, GGInterfaceTypes = BEGIN ActionQueue: TYPE ~ REF ActionQueueRep; ActionQueueRep: PUBLIC TYPE ~ UserInput.Rep; RawAction: TYPE ~ REF RawActionRep; RawActionRep: PUBLIC TYPE ~ UserInput.ActionBody; RawInput: TYPE ~ GGActive.RawInput; ActiveDoc: TYPE = EBTypes.ActiveDoc; ActiveButton: TYPE = EBTypes.ActiveButton; BoundBox: TYPE = GGModelTypes.BoundBox; Color: TYPE = Imager.Color; ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; -- exported to GGInterfaceTypes FeatureData: TYPE = GGModelTypes.FeatureData; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; MsgRouter: TYPE = FeedbackTypes.MsgRouter; Point: TYPE = GGBasicTypes.Point; Scene: TYPE = GGModelTypes.Scene; SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; StrokeEnd: TYPE = Imager.StrokeEnd; Transformation: TYPE = ImagerTransformation.Transformation; UserInputProc: TYPE = GGEvent.UserInputProc; Viewer: TYPE = ViewerClasses.Viewer; InitPaletteApplication: PROC = { EmbeddedButtons.RegisterApplication[$Palette, PaletteNotify]; }; PaletteNotify: EmbeddedButtons.RegisteredNotifyProc = { ggData: GGData _ NARROW[buttonInfo.doc.theDoc]; sliceD: SliceDescriptor _ NARROW[buttonInfo.button]; FOR list: LIST OF REF ANY _ events, list.rest UNTIL list = NIL DO SELECT list.first FROM $TransferFillColor => [] _ GGMouseEvent.SetFillColorRemote[ggData, sliceD]; $TransferStrokeColor => [] _ GGMouseEvent.SetStrokeColorRemote[ggData, sliceD]; $TransferBothColors => { [] _ GGMouseEvent.SetFillColorRemote[ggData, sliceD]; [] _ GGMouseEvent.SetStrokeColorRemote[ggData, sliceD]; }; ENDCASE; ENDLOOP; }; ggActiveDocClass: EBEditors.ActiveDocClass _ NEW[EBEditors.ActiveDocClassObj _ [ name: $Gargoyle, getRef: GGGetRef, setRef: GGSetRef, mapRef: GGMapRef, getDocName: GGGetDocName, feedback: GGButtonFeedback, -- Feedback is the name of an interface inButton: GGInButton ]]; LookupDoc: PUBLIC PROC [ggData: GGData] RETURNS [doc: ActiveDoc] ~ { IF ggData.behavior.activeDoc = NIL THEN { doc _ EBEditors.CreateActiveDoc[ggData, ggActiveDocClass]; ggData.behavior.activeDoc _ doc; } ELSE doc _ NARROW[ggData.behavior.activeDoc] }; GGInButton: EBEditors.InButtonProc = { pt: Imager.VEC; feature: FeatureData; ggData: GGData _ NARROW[doc.theDoc]; sliceD: SliceDescriptor _ NARROW[button]; sceneBag: GGAlign.TriggerBag _ GGAlign.CreateTriggerBag[]; completeD: SliceDescriptor _ GGSliceOps.NewParts[sliceD.slice, NIL, slice]; tsc: TIPTypes.TIPScreenCoords _ NEW[TIPTypes.TIPScreenCoordsRec _ [x, y, FALSE] ]; [] _ ViewerOps.MouseInViewer[tsc]; -- modifies tsc to viewer coords pt _ GGWindow.ViewerToWorld[[tsc.mouseX, tsc.mouseY], ggData]; [] _ GGAlign.AddSliceFeature[completeD, sceneBag]; feature _ GGMultiGravity.FacesPreferred[ pt, 18.0, GGAlign.emptyAlignBag, sceneBag, ggData].feature; IF feature = NIL THEN RETURN[FALSE] ELSE { RETURN[TRUE]; }; }; GGGetDocName: EBEditors.GetDocNameProc ~ { ggData: GGData _ NARROW[doc.theDoc]; name _ GGState.GetFullName[ggData]; }; GGGetRef: EBEditors.GetRefProc = { sliceD: SliceDescriptor _ NARROW[button]; isUnique: BOOL _ TRUE; [ref, isUnique] _ GGProps.Get[sliceD.slice, sliceD.parts, key]; IF ref = NIL THEN { -- try inheritance ggData: GGData _ NARROW[doc.theDoc]; rootSlice: Slice _ ggData.rootSlice; [ref, isUnique] _ GGProps.Get[rootSlice, NIL, key]; }; }; GGSetRef: EBEditors.SetRefProc = { sliceD: SliceDescriptor _ NARROW[button]; GGProps.Put[sliceD.slice, sliceD.parts, key, ref]; }; GGMapRef: EBEditors.MapRefProc = { ForEachButton: PROC [leaf: Slice] RETURNS [done: BOOL _ FALSE] = { leafD: SliceDescriptor _ GGSliceOps.NewParts[leaf, NIL, slice]; buttonData: REF _ GGProps.Get[leaf, leafD.parts, $ButtonData].val; IF buttonData = NIL THEN RETURN; done _ mapProc[leafD, doc]; }; ggData: GGData _ NARROW[doc.theDoc]; aborted _ GGScene.WalkSlices[ggData.scene, leaf, ForEachButton]; }; GGButtonFeedback: EBEditors.FeedbackProc = { ggData: GGData _ NARROW[doc.theDoc]; feedbackRef: REF ANY; WITH feedback SELECT FROM rope: Rope.ROPE => feedbackRef _ GGUIUtility.ParseFeedbackRope[rope]; ENDCASE => feedbackRef _ feedback; WITH feedbackRef SELECT FROM a: ATOM => QueueButtonEvent[LIST[a], ggData, button]; l: LIST OF REF ANY => HandleList[l, ggData, button]; ENDCASE => ERROR; RETURN[feedbackRef]; }; NoOpFileIn: GGProps.FileinProc = { val _ IO.GetRope[s]; }; ButtonDataFileOut: GGProps.FileoutProc = { rope: Rope.ROPE; rope _ EBEditors.RopeFromButtonData[val]; s.PutRope[rope]; }; ButtonDataFileIn: GGProps.FileinProc = { rope: Rope.ROPE _ IO.GetRope[s]; val _ EBEditors.ButtonDataFromRope[rope, FALSE]; }; ButtonDataCopy: GGProps.CopyProc = { valAsRope: Rope.ROPE _ EBEditors.RopeFromButtonData[val]; copy _ EBEditors.ButtonDataFromRope[valAsRope, FALSE]; }; GargoyleHandler: EmbeddedButtons.RegisteredNotifyProc = { viewer: ViewerClasses.Viewer; inputFocus: InputFocus.Focus; inputFocus _ InputFocus.GetInputFocus[]; IF inputFocus # NIL THEN { viewer _ inputFocus.owner; IF viewer # NIL AND viewer.class.flavor = $ActionArea THEN { ggData: GGData _ NARROW[BiScrollers.ClientDataOfViewer[viewer]]; HandleList[events, ggData, buttonInfo.button]; }; }; }; ControlPanelButtonHandler: PUBLIC PROC [ggData: GGData, events: LIST OF REF, buttonInfo: EBTypes.ButtonInfo] = { HandleList[events, ggData, buttonInfo.button]; }; TransferButtonDashes: UserInputProc = { sliceD: SliceDescriptor _ NARROW[event.rest.first]; dashed: BOOL _ FALSE; pattern: SequenceOfReal _ NIL; offset, length: REAL _ 0.0; isUnique: BOOL _ TRUE; targetGGData: GGData _ GGState.GetGGInputFocus[]; IF targetGGData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; }; [dashed, pattern, offset, length, isUnique] _ GGSliceOps.GetDashed[sliceD.slice, sliceD.parts]; IF isUnique THEN { GGEventExtras.SetDashed[ggData, dashed, pattern, offset, length]; } ELSE Feedback.Append[targetGGData.router, oneLiner, $Complaint, "TransferButtonDashes failed: the button has multiple dash patterns"]; }; TransferButtonStrokeWidth: UserInputProc = { sliceD: SliceDescriptor _ NARROW[event.rest.first]; strokeWidth: REAL; isUnique: BOOL _ TRUE; targetGGData: GGData _ GGState.GetGGInputFocus[]; IF targetGGData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; }; [strokeWidth, isUnique] _ GGSliceOps.GetStrokeWidth[sliceD.slice, sliceD.parts]; IF isUnique THEN { GGEventExtras.SetStrokeWidth[ggData, strokeWidth]; } ELSE Feedback.Append[targetGGData.router, oneLiner, $Complaint, "TransferButtonStrokeWidth failed: the button has multiple dash patterns"]; }; TransferButtonStrokeEnd: UserInputProc = { sliceD: SliceDescriptor _ NARROW[event.rest.first]; strokeEnd: StrokeEnd; isUnique: BOOL _ TRUE; targetGGData: GGData _ GGState.GetGGInputFocus[]; IF targetGGData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; }; [strokeEnd, isUnique] _ GGSliceOps.GetStrokeEnd[sliceD.slice, sliceD.parts]; IF isUnique THEN { GGEventExtras.SetStrokeEnd[ggData, strokeEnd]; } ELSE Feedback.Append[targetGGData.router, oneLiner, $Complaint, "TransferButtonStrokeEnd failed: the button has multiple dash patterns"]; }; ActiveInputHandler: PROC [self: Viewer, ggData: GGData, input: LIST OF REF ANY] = { IF ggData.embed.beingBorn THEN RETURN; -- don't handle input while window is being built IF GGActive.IsRawInput[input] THEN { -- raw input CodeTimer.StartInt[$RawInputNotify, $Gargoyle]; { rawInput: GGActive.RawInput ~ GGActive.NarrowRawInput[input]; mouseAllUp: BOOL ~ MouseAllUp[rawInput]; button: REF _ NIL; -- will be set to the active button, if any doc: ActiveDoc; mouseAction: BOOL _ MouseAction[rawInput]; bs: BiScrollers.BiScroller ~ BiScrollers.QuaBiScroller[self]; IF GGState.GetActive[ggData] -- activity enabled AND ggData.behavior.rawInputMode=$None -- not in $Gargoyle mode AND mouseAction -- it's some kind of mouse action THEN { -- test for an active button at the cursor position GetMousePosition: PROC [mc: TIPUser.TIPScreenCoordsRec] RETURNS [mouseGG: Point] ~ { viewerToClient: Transformation ~ bs.class.style.GetTransforms[bs].viewerToClient; mouseBiScroller: TIPUser.TIPScreenCoords ~ NEW[TIPUser.TIPScreenCoordsRec _ mc]; v: Viewer; inClient: BOOL; [v, inClient] _ ViewerOps.MouseInViewer[mouseBiScroller]; IF NOT inClient THEN Feedback.PutF[ggData.router, oneLiner, $Debug, "GG: %g,%g; ", [integer[mouseBiScroller.mouseX]], [integer[mouseBiScroller.mouseY]] ]; mouseGG _ ImagerTransformation.Transform[m: viewerToClient, v: [mouseBiScroller.mouseX, mouseBiScroller.mouseY]]; }; mouseGG: Point ~ GetMousePosition[MouseCoords[rawInput]]; feature: FeatureData ~ GGMultiGravity.FacesPreferred[mouseGG, 18.0, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData].feature; IF feature#NIL THEN { doc _ LookupDoc[ggData]; WITH feature.shape SELECT FROM sliceD: SliceDescriptor => { buttonData: REF ~ GGGetRef[$ButtonData, sliceD, doc]; IF buttonData#NIL THEN button _ sliceD; -- found an active button }; ENDCASE; }; }; IF button#NIL THEN { EBEditors.HandleEvent[rawInput.rawAction, rawInput.actionQueue, button, doc]; } ELSE { IF GGState.GetReadOnly[ggData] THEN { IF MultiCursors.GetACursor[NIL]#textPointer THEN MultiCursors.SetACursor[textPointer, NIL]; } ELSE { event: LIST OF REF; IF mouseAction THEN { cursorType: MultiCursors.CursorType; cursorType _ ggData.controls.cursor; IF cursorType#MultiCursors.GetACursor[NIL] THEN MultiCursors.SetACursor[cursorType, NIL]; }; event _ ParseEvent[ggData.parseInfo, rawInput]; IF NOT mouseAllUp THEN ggData.behavior.rawInputMode _ $Gargoyle; IF event # NIL THEN { actionArea: Viewer _ BiScrollers.QuaViewer[bs: bs, inner: TRUE]; actionArea.class.notify[actionArea, event]; }; }; }; IF mouseAllUp THEN ggData.behavior.rawInputMode _ $None; }; CodeTimer.StopInt[$RawInputNotify, $Gargoyle]; } ELSE ERROR; -- should never be called if the input isn't from the Transparent TIP table }; QueueButtonEvent: PROC [event: LIST OF REF ANY, ggData: GGData, button: REF ANY] = { newEvent: LIST OF REF ANY; IF event.first = $SelectButton OR event.first = $BeginButton OR event.first = $TransferButtonDashes OR event.first = $TransferButtonStrokeWidth OR event.first = $TransferButtonStrokeEnd THEN { sliceD: SliceDescriptor _ NARROW[button]; newEvent _ LIST[event.first, sliceD]; -- make a copy of the eventList, because EmbeddedButtons resuses the original } ELSE IF event.first = $ButtonFillColorFromIntensity OR event.first = $ButtonStrokeColorFromIntensity THEN { sliceD: SliceDescriptor _ NARROW[button]; newEvent _ LIST[event.first, event.rest.first, sliceD]; -- make a copy of the eventList, because EmbeddedButtons resuses the original } ELSE newEvent _ List.Append[event]; -- make a copy of the eventList, because EmbeddedButtons resuses the original GGUserInput.EventNotify[ggData, newEvent]; }; HandleList: PROC [list: LIST OF REF ANY, ggData: GGData, button: REF ANY] = { WITH list.first SELECT FROM l: LIST OF REF ANY => { FOR list _ list, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM childList: LIST OF REF ANY => HandleList[childList, ggData, button]; ENDCASE => SIGNAL SyntaxError[msg: "Input list mixes LIST elements and ATOM elements"]; ENDLOOP; }; ENDCASE => QueueButtonEvent[list, ggData, button]; }; SyntaxError: PUBLIC SIGNAL[msg: Rope.ROPE] = CODE; GGActiveHandler: Commander.CommandProc = {}; IsRawInput: PUBLIC PROC [input: LIST OF REF ANY] RETURNS [BOOL] ~ { RETURN[ISTYPE[input.first, ActionQueue]]; }; NarrowRawInput: PUBLIC PROC [input: LIST OF REF ANY] RETURNS [RawInput] ~ { actionQueue: ActionQueue ~ NARROW[input.first]; rawAction: RawAction ~ NARROW[input.rest.first]; actionQueueEB: EBTypes.ActionQueue ~ actionQueue; rawActionEB: EBTypes.RawAction ~ rawAction; RETURN[[actionQueue: actionQueueEB, rawAction: rawActionEB]]; }; mouseButton1: UserInput.KeySym ~ KeyGlyphs.LeftMouse; mouseButton2: UserInput.KeySym ~ KeyGlyphs.MiddleMouse; mouseButton3: UserInput.KeySym ~ KeyGlyphs.RightMouse; MouseAction: PUBLIC PROC [input: RawInput] RETURNS [BOOL] ~ { actionRef: REF UserInput.ActionBody ~ input.rawAction; WITH a: actionRef^ SELECT FROM keyDown, keyStillDown, keyUp => { keySym: UserInput.KeySym ~ UserInput.GetKeySym[input.actionQueue, a.keyCode, 0]; SELECT keySym FROM mouseButton1, mouseButton2, mouseButton3 => RETURN[TRUE]; ENDCASE; }; mousePosition, fakeMouseMotion => RETURN[TRUE]; -- does fakeMouseMotion count ??? ENDCASE; RETURN[FALSE]; }; MouseAllUp: PUBLIC PROC [input: RawInput] RETURNS [BOOL] ~ { RETURN[ UserInput.GetKeySymState[input.actionQueue, mouseButton1]=up AND UserInput.GetKeySymState[input.actionQueue, mouseButton2]=up AND UserInput.GetKeySymState[input.actionQueue, mouseButton3]=up ]; }; MouseCoords: PUBLIC PROC [input: RawInput] RETURNS [TIPUser.TIPScreenCoordsRec] ~ { handle: UserInput.Handle ~ input.actionQueue; RETURN[handle.mousePosition]; }; MapCoordinateResults: PROC [results: LIST OF REF ANY] = { FOR lst: LIST OF REF _ results, lst.rest UNTIL lst = NIL DO SELECT lst.first FROM TIPPrivate.stdCoords => [] _ ViewerOps.MouseInViewer[TIPPrivate.stdCoords]; ENDCASE; ENDLOOP; }; ParseEvent: PUBLIC PROC [parseInfo: TIPPrivate.TIPParseInfo, input: RawInput] RETURNS [event: LIST OF REF] ~ { actionRef: REF UserInput.ActionBody ~ input.rawAction; parseInfo.inCreek _ input.actionQueue; event _ TIPPrivate.MatchEvent[parseInfo, actionRef^]; MapCoordinateResults[event]; }; ggActionAreaClass: ViewerClasses.ViewerClass ~ ViewerOps.FetchViewerClass[$ActionArea]; ggActionAreaClass.tipTable _ TIPUserExtras.TransparentTIPTable[]; -- all viewers made after this call will be able to be active GGUserInput.RegisterRawInputHandler[ActiveInputHandler]; GGProps.Register[$ButtonData, ButtonDataFileIn, ButtonDataFileOut, ButtonDataCopy]; InitPaletteApplication[]; GGUserInput.RegisterAction[$TransferButtonDashes, TransferButtonDashes, none]; GGUserInput.RegisterAction[$TransferButtonStrokeWidth, TransferButtonStrokeWidth, none]; GGUserInput.RegisterAction[$TransferButtonStrokeEnd, TransferButtonStrokeEnd, none]; Commander.Register[key: "GGActive", proc: GGActiveHandler, doc: "Does nothing. Active Gargoyle is already loaded", clientData: NIL, interpreted: TRUE]; EmbeddedButtons.RegisterApplication[$Gargoyle, GargoyleHandler]; END. & GGActiveImpl.mesa Copyright Σ 1988, 1989 by Xerox Corporation. All rights reserved. Bier, February 19, 1992 10:52 am PST Doug Wyatt, December 20, 1989 4:39:38 pm PST Contents: Routines that support embedded buttons within Gargoyle. Kenneth A. Pier, September 4, 1991 3:42 pm PDT Color Palette Mini-Application PROC[events: LIST OF REF ANY, buttonInfo: ButtonInfo]; ActiveDoc Class Gargoyle does us a favor here. When a new file is loaded into a viewer, ggData.behavior.activeDoc _ NIL. Thus, we can tell if we need to create a new activeDoc. PROC [button: ActiveButton, doc: ActiveDoc, x, y: INTEGER] RETURNS [BOOL]; scale: REAL _ GGState.GetScaleUnit[ggData]; Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "Cursor on button at [%g, %g]", [real[pt.x/scale]], [real[pt.y/scale]] ]; PROC [doc: ActiveDoc] RETURNS [name: ROPE]; PROC [key: ATOM, button: ActiveButton, doc: ActiveDoc] RETURNS [ref: REF]; PROC [key: ATOM, button: ActiveButton, doc: ActiveDoc, ref: REF]; PROC [doc: ActiveDoc, docClass: ActiveDocClass, mapProc: EachButtonProc]; PROC[button: ActiveButton, doc: ActiveDoc, feedback: REF] RETURNS [REF]; Property support PROC [s: STREAM] RETURNS [val: REF]; PROC [s: STREAM, val: REF] RETURNS [vf: ValFormat _ delimited]; PROC [s: STREAM] RETURNS [val: REF]; PROC [val: REF] RETURNS [copy: REF]; Gargoyle as a Button Application An obsolete comment: However, if the combination $EBApplications $GetKeyValue $ occurs, the named value is retrieved from the active document. This allows a button to base its operation on any of the values in the document. PROC [events: LIST OF REF, buttonInfo: ButtonInfo]; events will be a list of Gargoyle actions, such as (LineWidth "2.3"). Send the event to the named ggData, but extract any needed info from the named buttonInfo to prepare the event. PROC [ggData: GGData, event: LIST OF REF ANY]; PROC [ggData: GGData, event: LIST OF REF ANY]; PROC [ggData: GGData, event: LIST OF REF ANY]; Notify procedures Ignore this gargoyle action and display the "read-only" cursor, currently textPointer The only other SetACursor call in Gargoyle is in GGWindowImpl.SetCursorLooks. At this point, event contains viewer (not client) coordinates self.class.notify[self, event]; -- self is the BiScroller User Input Handling (formerly in GGPortImpl) Initialization ΚR™codešœ™KšœB™BKšœ$™$K™,K™—šΟbœ:™BK™.K™—šΟk ˜ Jšœžœy˜ˆK˜—šΟn œžœž˜JšžœχžœE˜ΕKšžœ(ž˜4K˜Kšœ žœžœ˜'Kšœžœžœ˜,Kšœ žœžœ˜#Kšœžœžœ˜1Kšœ žœ˜#K˜Kšœ žœ žœ ˜$Kšœžœ žœ ˜*Kšœ žœ˜'Kšœžœ˜Kšœ ž œ$Οc˜[Kšœž œ  œ ˜VKšœ žœ˜-Kšœžœ˜'Kšœžœ˜1Kšœ žœ˜*Kšœžœ˜!Kšœžœ˜!Kšœžœ˜2Kšœžœ˜!Kšœžœ ˜5Kšœ žœ˜#Kšœžœ'˜;Kšœžœ˜,Kšœžœ˜$—Idefault™L™šŸœžœ˜ Kšœ=˜=K˜K˜—šŸ œ*˜7Kš žœ žœžœžœžœ™6Kšœžœ˜/Kšœžœ˜4šžœžœžœžœžœžœžœž˜Ašžœ ž˜JšœK˜KJšœO˜Ošœ˜Jšœ5˜5Jšœ7˜7J˜—Jšžœ˜—Jšžœ˜—K˜—L™L™šœ-žœ ˜PKšœ˜K˜K˜K˜Kšœ˜Kšœ '˜CK˜K˜K˜—šŸ œž œžœ˜DKšœežœ:™’šžœžœžœ˜)Kšœ:˜:Kšœ ˜ K˜—Kšžœžœ˜,K˜K˜—šŸ œ˜&Kšžœ.žœžœžœ™JKšœ žœ˜Kšœ˜Kšœžœ ˜$Kšœžœ ˜)Kšœ:˜:Kšœ?žœ ˜KKšœ žœ&žœ˜RKšœ#  ˜CKšœ>˜>Kšœ2˜2Kšœe˜eKš žœ žœžœžœžœ˜#šžœ˜Kšœžœ ™+šœT™TKšœ)™)—Kšžœžœ˜ K˜—K˜K˜—šŸ œ˜*Kšžœžœžœ™+Kšœžœ ˜$Kšœ#˜#K˜K˜—šŸœ˜"Kšžœžœ(žœžœ™JKšœžœ ˜)Kšœ žœžœ˜Kšœ?˜?šžœžœžœ ˜&Kšœžœ ˜$Kšœ$˜$Kšœ)žœ˜3K˜—K˜—šŸœ˜"Kšžœžœ-žœ™AKšœžœ ˜)Kšœ2˜2K˜—šŸœ˜"KšžœE™Iš Ÿ œžœžœžœžœ˜BKšœ3žœ ˜?Kšœ žœ3˜BKšžœžœžœžœ˜ Kšœ˜Kšœ˜—Kšœžœ ˜$Kšœ@˜@K˜K˜—šŸœ˜,Kšžœ1žœžœžœ™HKšœžœ ˜$Kšœ žœžœ˜šžœ žœž˜Kšœ žœ6˜EKšžœ˜"—šžœ žœž˜Kšœžœžœ˜5Kš œžœžœžœžœ"˜4Kšžœžœ˜—Kšžœ˜K˜—K˜L™šŸ œ  œ˜"Kšžœžœžœžœ™$Kšœžœ ˜K˜K˜—šŸœ  œ˜*Kšžœžœžœžœ™?Kšœ žœ˜Kšœ)˜)Kšœ˜K˜K˜—šŸœ  œ˜(Kšžœžœžœžœ™$Kšœ žœžœ ˜ K˜Kšœ)žœ˜0K˜K˜—šŸœ˜$Kšžœžœžœžœ™$Kšœžœ%˜9Kšœ/žœ˜6K˜—K˜™ Kšœρ™ρK™—šŸœ*˜9Kšžœ žœžœžœ™3KšœG™GK˜K˜K˜K˜(šžœžœžœ˜K˜šžœ žœžœ#žœ˜Kšœ˜Kšœ žœ˜*Kšœ=˜=šžœ œžœ$ œžœ  !œžœ 3˜έšŸœžœ"žœΟuœ ˜TKšœQ˜QKšœ‘ œžœ"˜PKšœžœ˜Kšœ-‘ œ˜9Kš žœžœ žœM‘ œ‘ œ ˜šK–4[m: ImagerTransformation.Transformation, v: VEC]šœ‘œ>‘ œ‘ œ ˜qKšœ˜—Kšœ9˜9Kšœ„˜„šžœ žœžœ˜Kšœ˜šžœžœž˜˜Kšœ žœ&˜5Kšžœ žœžœ ˜AK˜—Kšžœ˜—K˜—K˜—šžœžœžœ˜KšœM˜MK˜—šžœ˜šžœžœ˜%K™Ušžœžœ ˜+Kšžœ œžœ˜/—K˜—šžœ˜Kšœžœžœž˜šžœ žœ˜K˜$Kšœ$˜$šžœ$žœ˜*šžœ œ žœ˜.KšœM™M——K˜—šœ/˜/K™=—Kšžœžœ žœ*˜@šžœ žœžœ˜Kšœ:žœ˜@Kšœ  ™9Kšœ+˜+K˜—K˜—K˜—Kšžœ žœ&˜8Kšœ3˜3—Kšžœžœ K˜WK˜K˜—šŸœžœ žœžœžœžœžœžœ˜TKš œ žœžœžœžœ˜š žœžœžœ%žœ*žœ(žœ˜ΐKšœžœ ˜)Kšœ žœ M˜sK˜—šžœ-žœ/žœ˜kKšœžœ ˜)Kšœ žœ) M˜…K˜—Kšžœ  M˜qKšœ*˜*K˜K˜—šŸ œžœžœžœžœžœžœžœ˜Mšžœ žœž˜š œžœžœžœžœ˜šžœžœžœž˜.šžœ žœž˜Kš œ žœžœžœžœ*˜DKšžœžœF˜W—Jšžœ˜—Kšœ˜—Kšžœ+˜2—K˜K˜—š Ÿ œžœžœ žœžœ˜2K˜—Kšœ,˜,K˜L™,šŸ œžœžœ žœžœžœžœžœžœ˜CKšžœžœ˜)K˜K˜—šŸœžœžœ žœžœžœžœžœ˜KKšœžœ˜/Kšœžœ˜0Kšœ1˜1Kšœ+˜+Kšžœ7˜=K˜K˜—Kšœ5˜5Kšœ7˜7šœ6˜6K˜—š Ÿ œžœžœžœžœ˜=Kšœ žœ(˜6šžœžœž˜šœ!˜!KšœP˜Pšžœž˜Kšœ,žœžœ˜9Kšžœ˜—K˜—Kšœ"žœžœ !˜QKšžœ˜—Kšžœžœ˜Kšœ˜K˜—š Ÿ œžœžœžœžœ˜<šžœ˜Kšœ=žœ˜AKšœ=žœ˜AKšœ<˜