DIRECTORY HerculesBody USING [MakeBody, EraseAllNames, NamePoint, NestAtoms], HerculesSolver USING [Solve, displayingMotions], HerculesStorage, HerculesImage, HerculesAlgebra USING [Se, Value, comma, colon, draw, print, leftPren, Cadr, Caddr, Apply, globals, AddGlobalDef, UnnestAtoms, FunValRec, FunVal, GetDef], HerculesSyntax USING [], HerculesParseViewer USING [], HerculesButtons, Real USING [RoundI, FixI], HerculesGraphics USING [Hylite, DrawPattern, viewerChanged, cursorOffsetY, DrawChar, DrawPoint, Blink, currentFont, currentPointSize, currentBold, currentItalic, fontHeight, CharWidth, DrawRope, RopeBox, SetPaintMode, DcGetsPressContext, GetNewFont, Whiten, PaintMe, DcGetsScreenContext], Terminal USING [WaitForBWVerticalRetrace, Virtual, Current], ViewerClasses USING [NotifyProc, ModifyProc, DestroyProc, ViewerClass, Viewer, ViewerClassRec], ViewerOps USING [RegisterViewerClass, CreateViewer, PaintViewer], ViewerTools USING [GetSelectionContents], TIPUser USING [InstantiateNewTIPTable], JunoKeyboard USING [Quit, x, y, Next, event, button, status, oldx, EnterAndNotify, char], InputFocus USING [SetInputFocus], Rope USING [ROPE, FromChar, Concat, Fetch, Length, Substr], Carets USING [StartCaret, StopCaret], Cursors USING [CursorArray, NewCursor, CursorType], WindowManager USING [RestoreCursor], Process USING [Detach], Atom USING [GetPName, MakeAtom]; HerculesTop: PROGRAM IMPORTS HerculesStorage, HerculesImage, Real,Terminal, HerculesBody, HerculesSolver, HerculesGraphics, HerculesAlgebra, Carets, Rope, ViewerOps, ViewerTools, TIPUser, JunoKeyboard, InputFocus, Cursors, WindowManager, Process, HerculesSyntax, Atom, HerculesParseViewer EXPORTS HerculesButtons = BEGIN OPEN Stor: HerculesStorage, Im: HerculesImage, Solv: HerculesSolver, Body: HerculesBody, Kbd: JunoKeyboard, Gr: HerculesGraphics, Alg: HerculesAlgebra, PView: HerculesParseViewer, Rope, Syn: HerculesSyntax; Se: TYPE = Alg.Se; PointPtr: TYPE = Stor.PointPtr; ActionPtr: TYPE = Stor.ActionPtr; ConstrPtr: TYPE= Stor.ConstrPtr; debug1, debug2: BOOL _ FALSE; -- can be set by BugBane to avoid debug output amax: INTEGER = 50; atop: [0..amax] _ 0; a: ARRAY [1..amax] OF RECORD [p: PointPtr, c: [1..amax]]; Push: PROC[p:PointPtr] = BEGIN IF p=NIL THEN SIGNAL NullPoint; --no point found IF atop = amax THEN ERROR; atop _ atop + 1; a[atop].p _ p; a[atop].c _ 1; FOR i: INTEGER IN [1..atop - 1] DO IF ABS[a[i].p.x - p.x] <= 1 AND ABS[a[i].p.y - p.y] <= 1 THEN a[atop].c _ a[atop].c + 1 ENDLOOP; FlipLite [a[atop].p, a[atop].c] END; Pop: PROC = BEGIN i: INTEGER; IF atop = 0 THEN ERROR; FlipLite [a[1].p, a[1].c]; FOR i IN [1 .. atop - 1] DO a[i] _ a[i + 1] ENDLOOP; atop _ atop - 1 END; sa: ARRAY [1..amax] OF RECORD [p: PointPtr]; satop: [0..amax] _ 0; NullPoint: SIGNAL=CODE; PushS: PROC[p:PointPtr] = BEGIN IF p = NIL THEN SIGNAL NullPoint; IF satop = amax THEN ERROR; satop _ satop + 1; sa[satop].p _ p; FlipLite [sa[satop].p, 0] END; PopS: PROC = BEGIN i: INTEGER; IF satop = 0 THEN ERROR; FlipLite [sa[1].p, 0]; FOR i IN [1 .. satop - 1] DO sa[i] _ sa[i + 1] ENDLOOP; satop _ satop - 1 END; FlipLite: PROC [p: PointPtr, c: INTEGER] = -- Highlights (or erases highlight) of point p. -- c is # of times p is in queue. If 0, uses square box. BEGIN Gr.Hylite [Real.RoundI[p.x], Real.RoundI[p.y], c] END; ClearSelectedPoints: PROC = -- Cleans up all selections, marks, etc. BEGIN p: PointPtr _ Im.image.points; UNTIL atop = 0 DO Pop[] ENDLOOP; UNTIL satop = 0 DO PopS[] ENDLOOP; WHILE p # NIL DO p.wn _ 0; p.mark _ FALSE; p.copy _ NIL; p _ p.link ENDLOOP END; numCursors: INTEGER _ 12; cursor: ARRAY [1..20] OF RECORD [y: INTEGER, pattern: Cursors.CursorArray, proc: PROC, cursorName: Cursors.CursorType]; InitCursors: PROC = BEGIN cursor[5].y _ 620; cursor[5].proc _ DrawCmd; cursor[5].pattern _ [4440B, 4440B, 4440B, 4440B, 4440B, 4440B, 4440B, 4440B, 4040B, 4040B, 2100B, 2100B, 1600B, 1600B, 400B, 400B]; cursor[5].cursorName _ Cursors.NewCursor [ bits: cursor[5].pattern, hotX: -8, hotY: -16]; cursor[8].y _ 590; cursor[8].proc _ TypeCmd; cursor[8].pattern _ [0,0,0,0, 17760B, 10020B, 10020B, 37770B, 40004B, 45244B, 100002B, 132532B, 100002B, 117762B, 40004B, 37770B]; cursor[8].cursorName _ Cursors.NewCursor [ bits: cursor[8].pattern, hotX: -5, hotY: -4]; cursor[12].y _ 560; cursor[12].proc _ XCmd; cursor[12].pattern _ [040001B, 020002B, 010004B, 004010B, 002020B, 001040B, 000500B, 000200B, 000500B, 001040B, 002020B, 004010B, 010004B, 020002B, 040001B,0]; cursor[12].cursorName _ Cursors.NewCursor[ bits: cursor[12].pattern, hotX: -8, hotY: -8]; cursor[3].y _ 500; cursor[3].proc _ HorCmd; cursor[3].pattern _ [0B, 0B, 20000B, 60000B, 60000B, 60000B, 160000B, 177777B, 177777B, 160000B,060000B, 060000B, 060000B, 020000B, 0b, 0b]; cursor[3].cursorName _ Cursors.NewCursor [ bits: cursor[3].pattern, hotX: -9, hotY: -6]; cursor[2].y _ 470; cursor[2].proc _ VerCmd; cursor[2].pattern _ [1700B, 17770B, 37774B, 600B, 600B, 600B, 600B, 600B, 600B, 600B, 600B, 600B, 600B, 600B, 600B, 600B]; cursor[2].cursorName _ Cursors.NewCursor [ bits: cursor[2].pattern, hotX: -10, hotY: -9]; cursor[1].y _ 440; cursor[1].proc _ CongCmd; cursor[1].pattern _ [4000B, 4000B, 6000B, 16000B, 13000B, 11000B, 31540B, 20700B, 23600B, 76200B, 40300B, 40100B, 140140B, 100040B, 100040B, 100000B]; cursor[1].cursorName _ Cursors.NewCursor [ bits: cursor[1].pattern, hotX: 0, hotY: -16]; cursor[4].y _ 410; cursor[4].proc _ ParaCmd; cursor[4].pattern _ [1040B, 1040B, 1040B, 1040B, 1040B, 1040B, 1040B, 1040B, 1040B, 1040B, 1040B, 1040B, 1040B, 1040B, 1040B, 1040B]; cursor[4].cursorName _ Cursors.NewCursor [ bits: cursor[4].pattern, hotX: -7, hotY: -8]; --! fix the hot spot for this cursor cursor[12].y _ 380; cursor[12].proc _ PerpCmd; cursor[12].pattern _ [040000B, 060000B, 060000B, 050000B, 044000B, 044000B, 042000B, 041000B, 041000B, 040400B, 040200B, 040200B, 040100B, 040040B, 040040B, 077740B]; cursor[12].cursorName _ Cursors.NewCursor[ bits: cursor[12].pattern, hotX: 0, hotY: 0]; cursor[7].y _ 320; cursor[7].proc _ FreezeCmd; cursor[7].pattern _ [1700B, 1700B, 37774B, 4020B, 11110B, 10010B, 4020B, 3140B, 14030B, 20004B, 40002B, 40002B, 40002B, 20004B, 14030B, 3740B]; cursor[7].cursorName _ Cursors.NewCursor [ bits: cursor[7].pattern, hotX: -9, hotY: -12]; cursor[10].y _ 290; cursor[10].proc _ MoveCmd; cursor[10].pattern _ [0,0,0,40B, 160B, 370B, 774B, 1776B, 3777B, 370B, 370B, 370B, 370B, 370B, 370B, 370B]; cursor[10].cursorName _ Cursors.NewCursor [ bits: cursor[10].pattern, hotX: -8, hotY: -3]; cursor[9].y _ 260; cursor[9].proc _ CopyCmd; cursor[9].pattern _ [40B, 120B, 210B, 444B, 1162B, 3373B, 774B, 1776B, 3777B, 370B, 370B, 370B, 370B, 370B, 370B, 370B]; cursor[9].cursorName _ Cursors.NewCursor [ bits: cursor[9].pattern, hotX: -8, hotY: 0]; cursor[6].y _ 230; cursor[6].proc _ EraseCmd; cursor[6].pattern _ [4440B, 4440B, 4440B, 4440B, 4440B, 4440B, 4440B, 7740B, 4040B, 4040B, 4040B, 4040B, 4040B, 4040B, 4040B, 3700B]; cursor[6].cursorName _ Cursors.NewCursor [ bits: cursor[6].pattern, hotX: -8, hotY: -16]; cursor[11].y _ 200; cursor[11].proc _ YCmd; cursor[11].pattern _ [040001B, 020002B, 010004B, 004010B, 002020B, 001040B, 000500B, 000200B, 000200B, 000200B, 000200B, 000200B, 000200B, 000200B, 000200B,0]; cursor[11].cursorName _ Cursors.NewCursor [ bits: cursor[11].pattern, hotX: 8, hotY: 8]; END; RefreshCursors: PROC = BEGIN i: INTEGER; FOR i IN [1..numCursors] DO Gr.DrawPattern[cursor[i].pattern, cursorLine, cursor[i].y]; ENDLOOP; Gr.viewerChanged _ TRUE END; currentCursor: [0..20] _ 5; cursorLine: INTEGER = 30; -- x-coordinate of cursors/workarea boundary HotCoords: PROCEDURE RETURNS [x, y: INT] = {RETURN [Kbd.x, Kbd.y - Real.FixI[Gr.cursorOffsetY]]}; TrackTopPoint: PROC [primary: BOOL] = BEGIN -- Continuously updates the top of the primary or secondary stack to the -- image point that is closed to the current mouse coordinates, until the mouse -- button is released. -- If primary=TRUE tracks the top of the primary selection stack (middle button)., -- If primary=FALSE tracks the secondary selection, and considers only points -- within the blue baloon (i.e. with nonzero winding number). x, y: INTEGER; p: PointPtr; Kbd.Next[]; DO Kbd.Next[]; IF Kbd.event = clickUp AND Kbd.button = (IF primary THEN yellow ELSE blue) THEN RETURN; IF Kbd.event = roll THEN {[x, y] _ HotCoords[]; p _ Im.FindPoint [x: x, y: y, wound: NOT primary]; IF primary THEN {IF p # a[atop].p THEN {FlipLite [a[atop].p, a[atop].c]; atop _ atop - 1; Push[p]}} ELSE {IF p # sa[satop].p THEN {FlipLite [sa[satop].p, 0]; satop _ satop - 1; PushS[p]}}}; ENDLOOP END; TrackBlueBaloon: PROCEDURE = BEGIN -- The effect of TrackBlueBaloon is to track the cursor until the Blue button is -- released, setting the wn (winding number) field for all image points -- contained in the loop made by the cursor. prevx, prevy: INTEGER; NextBaloonPoint: Im.NextPointProc = BEGIN -- Waits for the mouse to move far enough, or blue button to go up, -- and returns its coordinates. DO Kbd.Next[]; [x, y] _ HotCoords[]; lastPoint _ Kbd.status[blue] = up; IF lastPoint OR ABS[x - prevx] > 5 OR ABS[y - prevy] > 5 THEN {Gr.DrawChar['., x, y]; prevx _ x; prevy _ y; RETURN} ENDLOOP END; -- body of procedure TrackBlueBaloon: [prevx, prevy] _ HotCoords[]; -- starting point of loop is current mouse position Gr.DrawChar['., prevx, prevy]; Im.BaloonSelect[prevx, prevy, NextBaloonPoint]; END; viewerDead: BOOL _ FALSE; MainLoop: PROC = BEGIN e: Event; x, y: INTEGER; DropCursor: PROC = BEGIN -- UserTerminal.SetCursorPattern[arrowCursor]; HerculesClass.cursor _ pointRight; WindowManager.RestoreCursor; currentCursor _ 0; ClearSelectedPoints[] END; PickUpCursor: PROC = BEGIN i : INTEGER _ 1; champ: INTEGER _ 5; -- five is the pencil cursor index UNTIL i > numCursors DO IF ABS[y - cursor[i].y+8] < ABS[y - cursor[champ].y+8] THEN champ _ i; i _ i + 1 -- each cursor has a height of 30 pixels; boundary is 15 above & 15 below. --cursor[i].y is top left corner of icon. Drop it to the center of the icon ENDLOOP; currentCursor _ champ; HerculesClass.cursor _ cursor[currentCursor].cursorName; WindowManager.RestoreCursor; END; WaitForCrossRight: PROC = BEGIN DO [e, x, y] _ NextEvent[]; IF e = crossright THEN RETURN ENDLOOP END; Kbd.Next[]; DO ENABLE {ABORTED => LOOP; Quit => {viewerDead _ TRUE; EXIT}}; DropCursor[]; WaitForCrossRight[]; PickUpCursor[]; cursor[currentCursor].proc[]; ENDLOOP END; Event: TYPE = {left, middle, right, esc, crossleft, crossright, tab, backspace, cr, keyboard}; NextEvent: PROCEDURE RETURNS [e: Event, x, y: INTEGER] = BEGIN -- The procedure call NextEvent[] returns one of the tokens left, middle, right, escape, -- crossleft, crossright. -- Also returns hot coordinates of cursor at time of click DO Kbd.Next[]; [x, y] _ HotCoords[]; SELECT TRUE FROM Kbd.event = roll AND Kbd.x <= cursorLine AND Kbd.oldx > cursorLine => {e _ crossleft}; Kbd.event = roll AND Kbd.x > cursorLine AND Kbd.oldx <= cursorLine => {e _ crossright}; Kbd.event = clickDown AND Kbd.button = red => {e _ left}; Kbd.event = clickDown AND Kbd.button = yellow => {e _ middle}; Kbd.event = clickDown AND Kbd.button = blue => {e _ right}; Kbd.event = keyDown AND Kbd.char = 33C => {e _ esc}; Kbd.event = keyDown AND Kbd.char = 11C => {e _ tab}; Kbd.event = keyDown AND Kbd.char = 10C => {e _ backspace}; Kbd.event = keyDown AND Kbd.char = 15C => {e _ cr}; Kbd.event = keyDown AND Kbd.char # 33C AND Kbd.char # 11C AND Kbd.char # 10C AND Kbd.char # 15C => {e _ keyboard}; ENDCASE => LOOP; RETURN ENDLOOP END; AddVisiblePoint: PROC[x, y: INTEGER] RETURNS [p: PointPtr] = BEGIN p _ Stor.NewPoint[x, y, TRUE]; Im.AddPoint[p]; Gr.DrawPoint [x,y] END; PointProcessingProc: TYPE = PROC [event: Event]; GeneralCmd: PROC [maxPrim, maxSec, nLeave: INT _ 0, baloonSel: BOOL _ FALSE, ProcessPoints: PointProcessingProc, solve, refresh: BOOL _ FALSE] = BEGIN bluBalloon: BOOLEAN _ baloonSel; p: PointPtr; xE, yE: INTEGER; event: Event; InputFocus.SetInputFocus [self]; DO [event, xE, yE] _ NextEvent[]; SELECT event FROM left => {IF maxPrim >0 THEN {Push[AddVisiblePoint[xE, yE]]; WHILE atop > maxPrim DO Pop[] ENDLOOP}}; middle => {p _ Im.FindPoint[x: xE, y: yE, wound: FALSE]; IF p # NIL AND maxPrim > 0 THEN {Push[p]; TrackTopPoint[primary: TRUE]; WHILE atop > maxPrim DO Pop[] ENDLOOP}}; right => {IF bluBalloon THEN {UNTIL satop = 0 DO PopS[] ENDLOOP; TrackBlueBaloon[]; IF Im.AnyWoundPoints[] THEN {bluBalloon _ FALSE} ELSE {Refresh[]}} ELSE {IF maxSec > 0 THEN {p _ Im.FindPoint[x: xE, y: yE, wound: TRUE]; IF p # NIL THEN {PushS[p]; TrackTopPoint[primary: FALSE]; WHILE satop > maxSec DO PopS[] ENDLOOP}}}}; esc, tab => {ProcessPoints[event]; WHILE atop > 0 AND atop # nLeave DO Pop[] ENDLOOP; WHILE satop > 0 DO PopS[] ENDLOOP; IF solve AND event = esc THEN {DoSolve[]; Refresh[]} ELSE IF refresh THEN {Refresh[]}}; crossright => {}; crossleft => RETURN; ENDCASE => {} ENDLOOP END; AddNewAction: PROC [op: Alg.Se, arg: Alg.Value, doIt: BOOL _ FALSE] = -- Adds a new action to the current image. If doIt is TRUE , also execurtes the action. BEGIN Im.AddAction[Stor.NewAction[op: op, arg: arg]]; IF doIt THEN [] _ Alg.Apply [function: op, arg: arg, alist: NIL, mode: effect] END; DrawCmd: PROC = BEGIN DrawThem: PointProcessingProc = BEGIN IF atop = 3 OR atop = 1 THEN Pop[]; IF atop = 0 THEN {RETURN} ELSE {AddNewAction [op: Alg.draw, arg: IF atop = 2 THEN LIST[LIST[a[1].p, a[2].p]] ELSE LIST[LIST[a[1].p, a[2].p, a[3].p, a[4].p]], doIt: TRUE]} END; GeneralCmd [maxPrim: 4, maxSec: 0, nLeave: 1, baloonSel: FALSE, ProcessPoints: DrawThem] END; CongCmd: PROC = BEGIN CongThem: PointProcessingProc = BEGIN IF atop # 4 THEN {WHILE atop > 0 DO Pop[] ENDLOOP; RETURN} ELSE {Im.AddConstr[Stor.NewCong[a[1].p, a[2].p, a[3].p, a[4].p]]} END; GeneralCmd [maxPrim: 4, maxSec: 0, nLeave: 2, baloonSel: FALSE, ProcessPoints: CongThem, solve: TRUE] END; ParaCmd: PROC = BEGIN ParaThem: PointProcessingProc = BEGIN IF atop # 4 THEN {WHILE atop > 0 DO Pop[] ENDLOOP; RETURN} ELSE {Im.AddConstr[Stor.NewPara[a[1].p, a[2].p, a[3].p, a[4].p]]} END; GeneralCmd [maxPrim: 4, maxSec: 0, nLeave: 2, baloonSel: FALSE, ProcessPoints: ParaThem, solve: TRUE] END; PerpCmd: PROC = BEGIN PerpThem: PointProcessingProc = BEGIN IF atop # 4 THEN {WHILE atop > 0 DO Pop[] ENDLOOP; RETURN} ELSE {Im.AddConstr[Stor.NewPerp[a[1].p, a[2].p, a[3].p, a[4].p]]} END; GeneralCmd [maxPrim: 4, maxSec: 0, nLeave: 2, baloonSel: FALSE, ProcessPoints: PerpThem, solve: TRUE] END; HorCmd: PROC = BEGIN HorThem: PointProcessingProc = BEGIN IF atop # 2 THEN {WHILE atop > 0 DO Pop[] ENDLOOP; RETURN} ELSE {Im.AddConstr[Stor.NewHor[a[1].p, a[2].p]]} END; GeneralCmd [maxPrim: 2, maxSec: 0, nLeave: 1, baloonSel: FALSE, ProcessPoints: HorThem, solve: TRUE] END; VerCmd: PROC = BEGIN VerThem: PointProcessingProc = BEGIN IF atop # 2 THEN {WHILE atop > 0 DO Pop[] ENDLOOP; RETURN} ELSE {Im.AddConstr[Stor.NewVer[a[1].p, a[2].p]]} END; GeneralCmd [maxPrim: 2, maxSec: 0, nLeave: 1, baloonSel: FALSE, ProcessPoints: VerThem, solve: TRUE] END; EraseCmd: PROC = BEGIN DeleteThem: PointProcessingProc = {Im.DeleteWoundItems[]}; GeneralCmd [maxPrim: 0, maxSec: 0, nLeave: 0, baloonSel: TRUE, ProcessPoints: DeleteThem, solve: TRUE] END; FreezeCmd: PROC = BEGIN xE, yE: INTEGER; event: Event; p: PointPtr; WHILE p # NIL DO IF p.fixed THEN FlipLite [p, 1]; p _ p.link; ENDLOOP; InputFocus.SetInputFocus [self]; DO [event, xE, yE] _ NextEvent[]; SELECT event FROM middle => {p _ Im.FindPoint[x: xE, y: yE, wound: FALSE]; IF p # NIL THEN {p.fixed _ NOT p.fixed; FlipLite [p, 1]}}; crossleft => {p _ Im.image.points; WHILE p # NIL DO IF p.fixed THEN FlipLite [p, 1]; p _ p.link; ENDLOOP; RETURN}; ENDCASE => {} ENDLOOP END; MoveCmd: PROC = BEGIN MoveThem: PointProcessingProc = BEGIN IF satop = 0 AND atop > 1 THEN {UNTIL atop = 2 DO Pop[] ENDLOOP; PushS[a[1].p]; Pop[]; sa[1].p.wn _ 1}; MovePointsInBaloon[solve: event = esc]; END; GeneralCmd [maxPrim: 3, maxSec: 3, nLeave: 0, baloonSel: TRUE, ProcessPoints: MoveThem, refresh: TRUE] END; CopyCmd: PROC = BEGIN CopyThem: PointProcessingProc = BEGIN n: INTEGER = MIN[atop,satop]; p: PointPtr; -- Copy points, constraints and actions in baloon. Im.CopyWoundItems[]; -- Im.CopyWoundItems should -- a) copy the points with wn#0 (& attached constraints &actions); -- b) add the copies to Im.imageXXX, -- c) set the copy link of the originals to the copies, -- d) set the coppy links of the copies to NIL, -- e) set the wn's of the copies to the wn's of the originals, -- f) set the wn's of the originals to zero -- (i.e. take out the originals from the baloon, and put in the copies). -- Now replace the selected frame points by their copies FOR i: INTEGER IN [0.. n-1] DO sa[satop-i].p _ sa[satop-i].p.copy ENDLOOP; -- Now reset copy links of all original points p _ Im.image.points; WHILE p # NIL DO IF p.wn = 0 THEN p.copy _ NIL; p _ p.link ENDLOOP; -- Now do a MoveCmd on the copies MovePointsInBaloon[solve: event = esc]; END; GeneralCmd [maxPrim: 3, maxSec: 3, nLeave: 0, baloonSel: TRUE, ProcessPoints: CopyThem, refresh: TRUE] END; MovePointsInBaloon: PROC [solve: BOOLEAN] = -- this proc is used by both MoveCmd and CopyCmd. It -- (a) pairs t with s, for each source-target pair (s,t) with s in sa[] and t in a[]; -- (b) computes a transform that takes the sources to the targets, -- (c) applies the transform to all points with wn#0, -- (d) identifies the sources with the targets, and finally -- (e) solves the constraints (for the moved points only), if solve = TRUE. -- This procedure assumes all copy links are NIL on entry, all points in the source frame -- have wn#0, and all points in the destination frame have wn=0. BEGIN n: INTEGER = MIN[atop,satop]; singular: BOOL _ FALSE; ap, bp, cp, sap, sbp, scp: PointPtr _ NIL; WHILE atop > n DO Pop[] ENDLOOP; WHILE satop > n DO PopS[] ENDLOOP; IF n>0 THEN {ap _ a[atop].p; sap _ sa[satop].p; IF n>2 THEN {cp _ a[atop-2].p; scp _ sa[satop-2].p}; IF n>1 THEN {bp _ a[atop-1].p; sbp _ sa[satop-1].p}} ELSE {Gr.Blink["You must specify at least one pair of points"]; RETURN}; -- Transform the coordinates of points in baloon singular _ Im.MoveWoundPoints[source: [sap, sbp, scp], dest: [ap, bp, cp]]; IF singular THEN {Gr.Blink["No such affine transformation"]; RETURN}; IF solve THEN {-- temporarily freeze the points on the destination frame -- Should remove them from the list of points passed to the solver! FOR i: INTEGER IN [0..n-1] DO a[atop-i].p.fixed _ TRUE ENDLOOP; HerculesClass.cursor _ hourGlass; WindowManager.RestoreCursor; [] _ Solv.Solve[Im.image.points, Im.image.constrs]; HerculesClass.cursor _ cursor[currentCursor].cursorName; WindowManager.RestoreCursor; -- Unfreeze the points on the destination frame FOR i: INTEGER IN [0..n-1] DO a[atop-i].p.fixed _ FALSE ENDLOOP}; Im.SortPoints[] END; oldRope: ROPE _ NIL; -- last typein, for automatic repeat TypeCmd: PROC = BEGIN event: Event; p: PointPtr; xE, yE: INTEGER; waitingForClick: BOOL _ TRUE; myRope: ROPE _ NIL; xw, yw: REAL; xCaretBase, xCaret, yCaret, len: INTEGER; yOffset: INTEGER = Real.FixI[Gr.cursorOffsetY]; InputFocus.SetInputFocus [self]; DO [event, xE, yE] _ NextEvent[]; SELECT event FROM left, middle => {IF waitingForClick THEN {IF event = left THEN {p _ Stor.NewPoint[xE, yE]; Im.AddPoint[p]} ELSE {p _ Im.FindPoint[x: xE, y: yE, wound: FALSE]}; IF p # NIL THEN {waitingForClick _ FALSE; xCaretBase _ xCaret _ Real.FixI[p.x]; yCaret _ Real.FixI[p.y]; Carets.StartCaret [self, xCaret, yCaret + yOffset, primary]}}}; cr => {IF NOT waitingForClick THEN {AddNewAction [op: Alg.print, arg: LIST [p, myRope, Gr.currentFont, NEW[INT _ Gr.currentPointSize], NEW[INT _ (IF Gr.currentBold THEN 2 ELSE 0) + (IF Gr.currentItalic THEN 1 ELSE 0)]], doIt: FALSE]; Carets.StopCaret[primary]; xCaret _ xCaretBase; yCaret _ yCaret - Gr.fontHeight; p _ Stor.NewPoint[p.x, p.y - Gr.fontHeight]; Im.AddPoint[p]; Carets.StartCaret [self, xCaret, yCaret + yOffset, primary]; myRope _ NIL; }}; keyboard => {IF NOT waitingForClick THEN {Gr.DrawChar [Kbd.char, xCaret, yCaret]; [xw, yw] _ Gr.CharWidth [Kbd.char]; xCaret _ xCaret + Real.FixI [ xw ]; Carets.StopCaret[primary]; Carets.StartCaret [ self, xCaret, yCaret + yOffset, primary]; -- concatenate the new character onto myRope _ Concat[myRope, FromChar[Kbd.char] ]}}; backspace => {IF NOT waitingForClick THEN {len _ Rope.Length [myRope]; IF len # 0 THEN BEGIN [xw, yw] _ Gr.CharWidth [Fetch[myRope, len - 1]]; xCaret _ xCaret - Real.FixI [xw]; -- doesn't unpaint char yet -- Gr.DrawChar [Fetch[myRope, len - 1], xCaret, yCaret]; -- now it is unpainted -- Carets.StopCaret[primary]; Carets.StartCaret [ self, xCaret, yCaret + yOffset, primary]; myRope _ Substr [ myRope, 0, len - 1] END}}; crossright => {}; crossleft, esc => {IF NOT waitingForClick THEN {IF myRope = NIL THEN {IF oldRope # NIL THEN {xmin, ymin, xmax, ymax: REAL; Gr.DrawRope [oldRope, xCaret, yCaret]; Carets.StopCaret[primary]; [xmin, ymin, xmax, ymax] _ Gr.RopeBox[myRope]; xCaret _ xCaret + Real.FixI [ xmax - xmin ]; Carets.StartCaret [ self, xCaret, yCaret + yOffset, primary]; myRope _ oldRope}}; IF myRope # NIL THEN {AddNewAction [op: Alg.print, arg: LIST [p, myRope, Gr.currentFont, NEW[INT _ Gr.currentPointSize], NEW[INT _ (IF Gr.currentBold THEN 2 ELSE 0) + (IF Gr.currentItalic THEN 1 ELSE 0)]], doIt: TRUE]}}; Carets.StopCaret[primary]; -- save oldRope in case user just hits ESC (or CR) next time oldRope _ myRope; IF event = crossleft THEN RETURN}; ENDCASE => {} ENDLOOP END; viewer: PView.Viewer _ NIL; -- Viewer for Juno procedures defaultProcName: ATOM = $X; YCmd: PROC = BEGIN YThem: PointProcessingProc = BEGIN IF satop > 1 THEN BEGIN AssignParmNames: PROC RETURNS [names: LIST OF ATOM] = BEGIN -- Assigns names to all secondary selections. -- Also builds a list of the assigned names, in the Juno S-expression format. Body.EraseAllNames[]; names _ NIL; FOR i: INT DECREASING IN [1..satop] DO names _ CONS[Body.NamePoint[sa[i].p, i], names] ENDLOOP END; org: PointPtr = sa[1].p; xP: PointPtr = IF satop<2 THEN NIL ELSE sa[2].p; yP: PointPtr = IF satop<3 THEN NIL ELSE sa[3].p; parms: LIST OF ATOM = AssignParmNames[]; body: Alg.Se = Body.MakeBody[[org, xP, yP], satop]; funVal: Alg.FunVal = NEW [Alg.FunValRec _ [parms, body]]; defSe: Se = LIST [Alg.is, defaultProcName, LIST [Alg.leftParen, LIST [Alg.colon, Body.NestAtoms[parms, Alg.comma, NIL], body]]]; Alg.AddGlobalDef[name: defaultProcName, value: funVal]; -- ( (leftParen ( ))), -- PView.AddExpr [defSe, PView.viewer] END ELSE {Gr.Blink["Right-select at least one parameter"]} END; GeneralCmd [maxPrim: 0, maxSec: amax, nLeave: 0, baloonSel: TRUE, ProcessPoints: YThem, refresh: FALSE] END; nameOfX: ATOM _ defaultProcName; XCmd: PROC = BEGIN ConsPointList: PROC [i, atop: INT] RETURNS [LIST OF REF] = BEGIN IF i > atop THEN RETURN[NIL] ELSE RETURN[CONS[a[i].p, ConsPointList[i+1, atop]]] END; valOfX: Alg.Value = Alg.GetDef[nameOfX, Alg.globals]; IF ISTYPE[valOfX, Alg.FunVal] AND valOfX # NIL THEN BEGIN funVal: Alg.FunVal = NARROW [valOfX]; nParms: INTEGER _ 0; XThem: PointProcessingProc = BEGIN IF atop # nParms THEN {Gr.Blink["Too few arguments!"]} ELSE {arg: Alg.Value _ ConsPointList[1, atop]; Gr.SetPaintMode[opaque]; AddNewAction[op: nameOfX, arg: arg, doIt: TRUE]; Gr.SetPaintMode[invert]; Gr.viewerChanged _ TRUE; Im.SortPoints[]}; END; p: LIST OF ATOM _ funVal.parms; WHILE p # NIL DO nParms _ nParms + 1; p _ p.rest ENDLOOP; -- #@#!$#%!!! Can't do nParms _ List.Length[funVal.parms] just because -- it is LIST OF ATOM instead of LIST OF REF ANY! GeneralCmd [maxPrim: nParms, maxSec: 0, nLeave: 0, baloonSel: FALSE, ProcessPoints: XThem, refresh: FALSE] END ELSE {Gr.Blink ["Procedure ", Atom.GetPName[nameOfX], " not defined; Parse or LoadX again!"]} END; LoadX: PUBLIC PROC = BEGIN r: ROPE _ ViewerTools.GetSelectionContents[]; nameOfX _ Atom.MakeAtom[r]; IF Alg.GetDef[nameOfX, Alg.globals] = NIL THEN {Gr.Blink["Warning: not defined - try Parse"]} END; DoSolve: PROC = BEGIN succ, imp: BOOL; HerculesClass.cursor _ hourGlass; WindowManager.RestoreCursor; [succ, imp] _ Solv.Solve[Im.image.points, Im.image.constrs]; IF NOT succ THEN Gr.Blink["The solver failed"]; Im.SortPoints[]; HerculesClass.cursor _ cursor[currentCursor].cursorName; WindowManager.RestoreCursor END; Redisplay: PUBLIC PROC = BEGIN DoSolve[]; ClearSelectedPoints[]; Refresh[] END; screenOnly: BOOL _ TRUE; Hardcopy: PUBLIC PROC [filename: Rope.ROPE] = BEGIN Gr.DcGetsPressContext[filename]; screenOnly _ FALSE; Refresh[]; Gr.Blink["press file created: ", filename]; Gr.DcGetsScreenContext[]; screenOnly _ TRUE; END; ChangeFont: PUBLIC PROC[font: ROPE, pointSize: INT, bold: BOOL, italic: BOOL] = BEGIN Gr.currentFont _ font; Gr.currentPointSize _ pointSize; Gr.currentBold _ bold; Gr.currentItalic _ italic; Gr.GetNewFont[] END; DisplayMotion: PUBLIC PROC = BEGIN Solv.displayingMotions _ NOT Solv.displayingMotions; END; StartOver: PUBLIC PROC = BEGIN Im.PurgeImage []; --! need to add a proc to junokeyboardimpl which empties the queue. --! i.e. queueRight = queueLeft & broadcast nonfull Refresh[]; END; Parse: PUBLIC PROC = BEGIN list: LIST OF Se; error: BOOL; saveGlobals: Alist _ Im.globals; Im.globals _ Im.basicGlobals; -- definitions in the Proc file may use only standard globals [list, error] _ PView.ParseViewer[PView.viewer]; IF error THEN {Gr.Blink["Parse error"]; RETURN}; -- list is a list of definitions (( ) ( ) ...) -- The values are all valid definitions. WHILE list # NIL DO {expr: Se _ list.first; name: ATOM = NARROW [Alg.Cadr[Se]]; valex: Se = Alg.Caddr[Se]; -- creates a FunVal. value: Alg.Value = Alg.Eval[valex, NIL, result]; saveGlobals _ Alg.InsertDef [name, value, saveGlobals]}; list _ list.rest ENDLOOP; Im.globals _ saveGlobals; Refresh[] END; NewProc: PUBLIC PROC = BEGIN viewer: PView.ParseViewer = PView.viewer; PView.AddText[viewer, "CommandName is (Args:\n Body)\n"] END; ProcFile: PUBLIC PROC[fileName: Rope.ROPE] = BEGIN PView.SetFile[PView.viewer, fileName]; Parse[] END; Refresh: PROCEDURE = BEGIN i:INTEGER; -- Gr.Whiten[]; Gr.SetPaintMode[opaque]; IF screenOnly THEN RefreshCursors[]; RefreshActions[]; RefreshPoints[]; Gr.SetPaintMode[invert]; FOR i IN [1..atop] DO FlipLite [a[i].p, a[i].c] ENDLOOP; FOR i IN [1..satop] DO FlipLite [sa[i].p, 0] ENDLOOP; Gr.viewerChanged _ TRUE; END; RefreshPoints: PROCEDURE = BEGIN p: PointPtr _ Im.image.points; WHILE p # NIL DO IF p.visible THEN Gr.DrawPoint[Real.FixI[p.x], Real.FixI[p.y]]; p _ p.link ENDLOOP; END; RefreshActions: PROC = BEGIN pp: ActionPtr _ Im.image.actions.first; WHILE pp # NIL DO [] _ Alg.Apply[pp.op, pp.arg, NIL, effect]; pp _ pp.link ENDLOOP END; Quit: SIGNAL = Kbd.Quit; CopyToViewer: PROC = BEGIN DO IF viewerDead THEN EXIT; BEGIN t: Terminal.Virtual = Terminal.Current[]; Terminal.WaitForBWVerticalRetrace [t]; --! Should there be one instead of two calls? Terminal.WaitForBWVerticalRetrace [t] END; IF Gr.viewerChanged THEN {ViewerOps.PaintViewer [viewer: self, hint: client, clearClient: FALSE]; Gr.viewerChanged _ FALSE} ENDLOOP END; NotifyMe: ViewerClasses.NotifyProc = -- [self : ViewerClasses.Viewer, -- input : LIST OF REF ANY] TRUSTED BEGIN Kbd.EnterAndNotify [self, input] END; InputFocusChanged: ViewerClasses.ModifyProc = -- [ self: Viewer, -- change: ModifyAction] TRUSTED BEGIN END; DestroyMe: ViewerClasses.DestroyProc = -- [self: Viewer] TRUSTED BEGIN input : LIST OF REF ANY _ LIST[$Destroy]; IF NOT self.iconic THEN InputFocus.SetInputFocus [self]; Kbd.EnterAndNotify [self, input] END; HerculesClass: ViewerClasses.ViewerClass _ NEW [ViewerClasses.ViewerClassRec _ [ paint: Gr.PaintMe, --called whenever the viewer should repaint notify: NotifyMe, --TIP input events destroy: DestroyMe, modify: InputFocusChanged, --InputFocus changes reported through here tipTable: TIPUser.InstantiateNewTIPTable["Juno.Tip"], -- cursor: questionMark cursor: cursor[currentCursor].cursorName --currentCursor is initialized to 5, so default cursor is pencil ] ]; self: ViewerClasses.Viewer; ViewerOps.RegisterViewerClass[$Hercules, HerculesClass]; Im.PurgeImage[]; InitCursors[]; self _ ViewerOps.CreateViewer[flavor: $Hercules, info: [name: "Hercules Image", iconic: FALSE, column: left]]; Process.Detach[FORK CopyToViewer[] ]; Process.Detach[FORK MainLoop[]]; END. zHerculesTop.mesa Last Edited by: Stolfi, March 23, 1984 7:41:49 pm PST Was JunoTop.mesa Coded May 1981 by Greg Nelson Converted to use the Viewers window package by Donna Auguste Last Edited by: Gnelson, January 17, 1984 11:32 am Last Edited by: Maureen Stone January 19, 1984 12:08 pm This is the top level of an experimental interactive graphics program that allows a user to specify the positions of points by declaring geometrical contraints that the positions are to satisfy. The program solves constraints (which can be viewed as simultaneous polynomial equations) by an n-dimensional version of Newton's method for finding the root of a differentiable function. To do: Fix ParseViewerImpl (February 22, 1984 12:30 pm) To do: Fix Parser to generate lambdas/FunVals, and Unparser to handle them (February 8, 1984 9:54 pm) To do: Keep constraints in symbolic expression form (February 13, 1984 10:52 pm) To do: Make AddAction to do NewAction, same for constraints (February 13, 1984 11:30 pm) To do: Allow selection type-in (February 10, 1984 3:26 am) - - - - MAIN ARGUMENT QUEUE (left- and middle-clicked points) c is count of # of times point appeared on stack when it was pushed on stack; when a point is selected a second time it is hilighted differently. - - - - SECONDARY ARGUMENT QUEUE (right-clicked points) - - - - CURSORS X, pencil, typewriter, compass, parallels, tsquares (2), snowman, move, copy, eraser, Y. - - - - MOUSE TRACKING AND POINT SELECTION - - - - MAIN LOOP The main loop executes the pencil procedure until the pencil is put down, then it waits until another cursor is picked up, in which case it executes that command, etc. - - - - CLICK PROCESSING - - - - BASIC COMMANDS - - - - PROCEDURE CREATION - - - - PROCEDURE INVOCATION - - - - BUTTONS - - - - PAINTING OF CURRENT IMAGE - - - - VIEWER PROCEDURES - - - - VIEWER SETUP Edited on January 23, 1984 9:46 pm, by Stolfi -- Added tioga formatting -- Changed Y command to accept more than two parameters changes to: YCmd, AlgebraStep (deleted), OtherAlgebraStep (deleted), GeneralAlgebraStep (new), MakeParmsList (new) Edited on January 24, 1984 3:37 am, by Stolfi -- Added Tioga node structure -- Extended YCmd to accept more than two arguments changes to: YCmd, GeneralAlgebraStep (replaces AlgebraStep and OtherAlgebraStep), mat (new global work matrix), MoveStep (removed a piece to Body) Edited on January 24, 1984 11:03 pm, by Stolfi -- More formatting changes changes to: LoadX Added a return in case of error, JunoClass, self Edited on January 25, 1984 3:44 am, by Stolfi changes to: , RefreshXR, StartOver, CollinearCmd, Refresh, RefreshItems, NotifyMe, OPEN, PointPtr, ClearSelectedPoints, TrackPoint, NewPoint, PencilCmd, CongruentCmd, YCmd, GeneralAlgebraStep, HorizontalCmd, VerticalCmd, FreezeCmd, typeWriterState, LoadX, XCmd, AddLambda, MoveStep, MoveCmd, UnSelected, CopyCmd, TrackLoop, RefreshPoints, Parse, InitJunoStorage, amax Edited on January 27, 1984 3:11 am, by Stolfi changes to: InitCursors, RefreshCursors, MainLoop, WaitForCrossRight, Next, DropCursor, Push, Pop, PushS, PopS, ClearSelectedPoints, PickUpCursor, TrackPoint, TrackSelectedPoint, NewPoint, PencilCmd, CongruentCmd, CollinearCmd, YCmd, GeneralAlgebraStep, HorizontalCmd, VerticalCmd, FreezeCmd, TypeCmd, EraseCmd, LoadX, XCmd, ConsPointList, AddLambda, MoveStep, MoveCmd, UnSelected, CopyCmd, TrackLoop, Redisplay, CopyToViewer, Refresh, RefreshPoints, RefreshItems, DestroyMe, JunoClass, Hardcopy, ChangeFont, StartOver, Parse, NewProc, ProcFile, ArgListLength Edited on February 3, 1984 10:04 pm, by Stolfi changes to: DisplayMotion (new button - toggles Solv.displayingMotions), Redisplay (accepts impossible return parameter from solver) Edited on February 4, 1984 6:15 am, by Stolfi -- Propagated changes due to HerculesStorage, HerculesImage -- Removed slink field from Points -- Cleaned up code changes to: UnSelected (deleted), AddVisiblePoint (new - replaces NewPoint), FindPoint (moved here from JunoStorage; combined with FindSelectedPoint), GeneralCmd (new), TrackTopPoint (replaces TrackPoint and TrackSelectedPoint), TrackBlueBaloon (renames TrackLoop), image (made local to this module) Edited on February 7, 1984 7:35 pm, by Stolfi -- Changed more interface names to hercules XXX changes to: Edited on February 8, 1984 6:13 pm, by Stolfi changes to: MovePointsInBaloon (moved a piece to HerculesImage as MoveWoundPoints) , Car, Cdr, Cadr, Caddr (moved to HerculesStorage), TrackBlueBaloon (modified to use BaloonSelect of HerculesImage), AnyPointsInBaloon (moved to HerculesImage), Parse (alist change, added refresh), LoadX (alist change), XCmd (default proc name is $X, alist change), YCmd (alist change) Edited on February 10, 1984 3:23 am, by Stolfi changes to: DoSolve (new, was part of Redisplay), GenCmd (added solve option), ClearSelectedPoints (clears also mark, copy and tempfixed), Edited on February 13, 1984 7:58 pm, by Stolfi changes to: a, sa (removed x, y components; use p.x, p.y instead), GenCmd (fixed bugs), xhot, yhot (removed), cursor (removed xhot, yhot components), InitCursors (added perp cursor), AddNewAction (new), DrawCmd, TypeCmd, XCmd (use AddNewAction), ʺ˜Jšœ™šœ6™6šœ™Jšœ™Jšœ<™™>Jšœžœ˜Jšœ˜šœžœ žœžœ˜:Jšœ’™’—šÏnœžœ˜Jš)žœžœžœžœžœ ÐciŸœžœ žœžœ3žœžœžœžœžœžœžœžœžœžœ#žœ˜¾—š œžœ˜ Jšžœžœžœ žœžœ#žœžœžœžœžœ˜ž—Jšœ8™8Jšœžœ žœžœ˜-Jšœ˜Jšœ žœžœ˜š œžœ˜JšžœžœžœžœžœžœžœžœFžœ˜‘—š œžœ˜ Jšžœžœžœ žœžœžœžœžœžœžœ˜ —š œžœžœŸiœ˜•Jšžœ5žœ˜?—š œžœŸ)œ˜FJšžœ$žœ žœžœžœ žœžœžœžœžœžœžœžœžœ˜Õ—Jšœ™šœ žœ˜Jšœa™a—Jš œžœ žœžœžœ,žœ&˜ƒš  œžœ˜Jšž˜Jšœ®˜®Jšœ«˜«JšœÏ˜ÏJšœÁ˜ÁJšœ¤˜¤JšœÁ˜ÁJšœ°Ÿ$˜ÔJšœ×˜×Jšœ¼˜¼Jšœ˜˜˜Jšœ£˜£Jšœ±˜±JšœÒ˜ÒJšœžœ˜—š œžœ˜Jšžœ žœžœžœžœHžœžœžœ˜±—Jš œ˜Jšœ žœŸ-˜GJšœ,™,Jš   œž œžœžœžœ1˜gš  œžœ žœ˜%Jš9œžœŸIœŸRœŸœŸSœŸNœŸ@œ žœ%žœžœžœžœ žœžœžœžœžœžœNžœžœ žœžœžœlžœžœžœežœžœ˜ž—š œž œ˜Jš žœŸQœŸÏrŸ-œŸ0œžœ˜ýšœÐbnœ˜(Jšœžœ ŸCœ Ÿ#œžœržœ žœžœžœžœžœLžœ žœžœ ˜´—JšœŸ&œ"Ÿ4œXžœ˜Þ—J™Jšœ™Jšœ¨™¨Jšœ žœžœ˜š œžœ˜JšQœžœžœ  œžœ žœŸ/œ‹žœ  œžœ žœ žœžœŸ$œžœžœ žœžœžœžœ*ŸKœ ŸMœ žœ‹žœ œžœ žœžœžœžœžœžœžœžœžœžœžœžœžœƒžœžœ˜Ã —J™Jšœ™JšœžœT˜_š  œž œžœžœ˜9Jš8žœŸYœŸœŸ;œžœ-žœžœžœžœžœOžœžœUžœIžœNžœIžœDžœDžœJžœNžœžœžœžœ4žœžœžœžœžœ˜³ —š œžœžœžœ˜=Jšžœžœ,žœ˜V—Jš œžœžœ˜1š  œžœžœžœžœ  œ'žœžœ˜˜Jšržœžœ)žœ:žœ*žœžœžœ žœAžœžœžœHžœžœžœžœ žœAžœžœžœžœ!žœ žœžœ žœžœ3žœžœ!žœžœ-žœžœ žœ<žœžœžœžœTžœžœžœžœHžœ žœžœžœžœ žœžœžœžœ žœ3žœžœ žœcžœ žœ žœžœ˜¾ —Jšœ™š   œžœ$žœžœŸW˜Jš œžœ7žœžœ0žœžœ˜‘—š œžœ˜Jš+œžœœžœ žœ žœ žœžœ žœžœ žœNžœžœžœžœ$žœžœžœ9žœ žœDžœ!žœ˜„—š œžœ˜Jšœžœœžœ žœ žœžœ žœžœžœ žœPžœDžœ(žœžœ˜ã—š œžœ˜Jšœžœ£œžœ žœ žœžœ žœžœžœ žœPžœDžœ(žœžœ˜ã—š œžœ˜Jšœžœ£œžœ žœ žœžœ žœžœžœ žœPžœDžœ(žœžœ˜ã—š œžœ˜Jšœžœœžœ žœ žœžœ žœžœžœ žœ?žœDžœ'žœžœ˜Ð—š œžœ˜Jšœžœ£œžœ žœ žœžœ žœžœžœ žœ?žœDžœ'žœžœ˜Ð—š œžœ˜Jšœžœ˜Jšœ œ6˜BJšœ@žœ*žœžœ˜{—š  œžœ˜Jš5œžœ žœ&žœžœžœžœ žœ'žœ*žœ*žœžœEžœžœžœžœžœqžœžœžœžœ žœ=žœžœ žœ žœžœ˜§—š œžœ˜Jšžœœžœ žœ žœ žœžœ žœžœžœDžœ*žœžœ˜Ÿ—š œžœ˜Jš žœœžœ žœžœ$˜sJš9Ÿ<œ)Ÿœ ŸFœ Ÿ(œ Ÿ;œ Ÿ3œ ŸBœ Ÿ/œ ŸJœ Ÿ9œ žœžœžœ žœ9žŸœ Ÿ/œ'žœžœžœ žœ žœ žœžœ Ÿ"œ7žœDžœ*žœžœ˜¡ —š œžœ žœŸÚœŸ¹ÐcrŸ/¤Ÿœ˜€JšTžœžœžœžœžœ,žœžœ žœžœžœ žœžœžœžœ2žœžœ1žœžœ/žœCžœŸ1œTžœ žœ8žœžœžœŸ:œŸDœžœžœžœ žœžœ žœöŸ0œžœžœžœ žœžœ žœžœ˜° —Jšœ žœžœŸ%˜;š œžœ˜Jš§œžœ-žœžœžœ žœžœ žœ&žœžœGžœ*žœžœ%žœžœžœžœežœ;žœžœžœžœ'žœ×žœžœžœZžœ}žœžœ1žœžœžœžœžœ*žœžœžœ!žœ·žœ6žœžœžœ­Ÿ(œažœžœžœ=žœ žœžœ„ŸœdŸœÕžœOžœžœžœžœ žœžœžœ žœžœ1žœ•žœ žœžœožœœžœžœ9žœžœžœžœžœ2žœžœžœ)žœ8Ÿ>œ*žœžœžœ žœ žœžœ˜ê —Jšœ™JšœžœŸ˜;Jšœžœ˜š œžœ˜ Jš_œžœœžœ žœ žœ žœ œžœžœ žœžœžœžœŸ.œŸOœDžœžœžœž œžœ žœžœ7žœžœIžœ žœžœžœ(žœ žœžœžœ žœžœžœ}žœ=žœ@žœ+žœ.žœSŸ^œ@žœ žœEžœGžœ'žœžœ˜Ô —J™Jšœ™Jšœ žœ˜!š œžœ˜ šœžœ  œžœ žœžœžœžœžœ˜EJšœžœžœžœžœžœ žœžœžœ'žœ˜w—Jš=œ>žœžœžœ žœžœžœžœžœœ#žœžœžœ@žœ žœRžœ0žœžœžœžœžœžœžœ!žœ ŸGœ Ÿ5œVžœ0žœžœžœ†žœ˜Â —J™Jšœ™š œžœžœ˜Jš žœžœGžœ$žœžœ6žœ˜À—š œžœ˜Jš žœžœ‹žœžœ žœžœ˜Ò—š  œžœžœ˜Jšžœ6žœ˜D—Jšœ žœžœ˜š œžœžœžœ˜.Jšžœ7žœpžœžœ˜À—š  œžœžœžœ žœžœ žœ˜OJšžœŠžœ˜˜—š  œžœžœ˜Jšžœžœžœ˜E—š  œžœžœ˜JšžœŸDœŸ6œžœ ˜»—š œžœžœ˜Jš'œžœ žœžœžœGŸ?œ7žœžœžœŸSœŸ)œžœžœžœ,žœžœ:Ÿœ*žœižœ,žœ ˜‘—š œžœžœ˜Jšžœmžœ˜{—š œžœžœžœ˜-Jšžœ6žœ˜B—J™Jšœ"™"š œž œ˜Jšžœžœ<žœ žœ_žœžœ žœžœžœžœ žœžœžœžœ˜Ù—š  œž œ˜Jšžœ(žœžœžœžœ žœHžœžœ˜­—š œžœ˜Jšžœ1žœžœžœ&žœ$žœžœ˜£—Jšœ™Jšœžœ˜š  œžœ˜Jšžœžœžœ žœžœžœdŸ.œ5žœžœžœKžœ!žœžœžœ˜¬—š œ Ÿ@˜hJšžœžœ&žœ˜8—š œŸ.˜\Jšžœžœžœ ˜—š  œŸ˜:Jšžœžœžœžœžœžœžœžœžœ žœKžœ˜µ—Jšœ™š œ˜)Jš œžœIŸ,œ&ŸœNŸ+œJŸ"œ?ŸAœ ˜¥—Jšœ˜Jšœ¶žœ!žœ"žœ˜”Jšžœ˜J™™-J™J™8Jš œ ¢œ ¢œ ¢œ¢œ™r—J™™-J™J™2Jšœ ¢œ,¢œ¢ œ™’J™—™.J™Jšœ ¢œ ¢™B—J™™-Jšœ ¢ã™ï—J™™-Jšœ ¢£™¯—J™™.Jš œ ¢ œ¢œ¢ œ ¢ œ™…—J™™-J™sJšœŠ¢œ™«—J™™-J™/Jšœ ™ —J™™-Jšœ ¢œ¢ œ¢œ¢œ ¢œ¢œ¢ œ¢ œ¢œ ¢ œ¢œ ¢œ¢œ*¢œ™ñ—J™™.Jš œ ¢œ¢ œ¢œ¢œ ¢œ(™Š—J™™.Jšœ ¢œ ¢œ"¢œ¢œ¢œ ¢œ ¢œ¢œ  ¢ œ¢ œ¢œ¢œ¢œ¢ œ™ö—J™—…—‰r³¦