DIRECTORY JunoStorage, JunoImage, JunoOldSolver USING [Solve, Outcome], RealFns USING [SqRt]; JunoImageImpl: PROGRAM IMPORTS JunoStorage, JunoOldSolver, RealFns EXPORTS JunoImage = BEGIN OPEN JunoStorage, JunoImage, Solv: JunoOldSolver; points: PointList _ [NIL, NIL]; constrs: ConstrList _ [NIL, NIL]; actions: ActionList _ [NIL, NIL]; -- The current image PurgeImage: PUBLIC PROC = BEGIN GcPoints [start: points.first, lim: NIL]; points _ [NIL, NIL]; GcConstrs[start: constrs.first, lim: NIL]; constrs _ [NIL, NIL]; GcActions[start: actions.first, lim: NIL]; actions _ [NIL, NIL] END; AddPoint: PUBLIC PROC [p: Point] = BEGIN points _ InsertPoint[p, points.last, points] END; RemovePoint: PUBLIC PROC [p: Point] = BEGIN points _ DeletePoint[p, NIL, points]; GcPoints[p, p.link] END; SortPoints: PUBLIC PROC = BEGIN [points.first, points.last] _ DoSortPoints[points.first] END; DoSortPoints: PROC [in: Point] RETURNS [first, last: Point] = BEGIN p, q, pl, ql, t: Point _ NIL; runs: INTEGER _ 0; DO runs _ 0; last _ NIL; DO IF in = NIL THEN EXIT; runs _ runs+1; p _ in; pl _ in; in _ in.link; WHILE in # NIL AND in.coords.x >= pl.coords.x DO pl _ in; in _ in.link ENDLOOP; IF in = NIL THEN {last _ pl; EXIT}; q _ in; ql _ in; in _ in.link; WHILE in # NIL AND in.coords.x >= ql.coords.x DO ql _ in; in _ in.link ENDLOOP; ql.link _ NIL; WHILE p # q AND q # in DO IF p.coords.x < q.coords.x THEN {t _ p; p _ t.link} ELSE {t _ q; q _ t.link; t.link _ p; pl.link _ q}; IF last = NIL THEN {first _ t} ELSE {last.link _ t}; last _ t ENDLOOP; ENDLOOP; IF runs < 2 THEN RETURN; in _ first ENDLOOP END; AddConstr: PUBLIC PROC [c: Constr] = BEGIN constrs _ InsertConstr [c, constrs.last, constrs] END; AddAction: PUBLIC PROC [a: Action] = BEGIN actions _ InsertAction [a, actions.last, actions] END; EnumPoints: PUBLIC PROC [Proc: PointVisitProc] = BEGIN FOR p: Point _ points.first, p.link WHILE p # NIL DO Proc[p] ENDLOOP END; ReplacePoints: PUBLIC PROC [Proc: PointReplaceProc] = BEGIN pAnt, pNew, pNext: Point _ NIL; FOR p: Point _ points.first, pNext WHILE p # NIL DO pNext _ p.link; pNew _ Proc[p]; IF pNew # p THEN {points _ DeletePoint[p, pAnt, points]; GcPoints [p, p.link]; IF pNew # NIL THEN {points _ InsertPoint[pNew, pAnt, points]; pAnt _ pNew}} ELSE {pAnt _ p} ENDLOOP END; EnumConstrs: PUBLIC PROC [Proc: ConstrVisitProc] = BEGIN FOR c: Constr _ constrs.first, c.link WHILE c # NIL DO Proc[c] ENDLOOP END; EnumConstrPoints: PUBLIC PROC [c: Constr, Proc: PointVisitProc] = BEGIN WITH c SELECT FROM cc: HorConstr => {Proc[cc.i]; Proc[cc.j]}; cc: VerConstr => {Proc[cc.i]; Proc[cc.j]}; cc: CongConstr => {Proc[cc.i]; Proc[cc.j]; Proc[cc.k]; Proc[cc.l]}; cc: ParaConstr => {Proc[cc.i]; Proc[cc.j]; Proc[cc.k]; Proc[cc.l]}; cc: PerpConstr => {Proc[cc.i]; Proc[cc.j]; Proc[cc.k]; Proc[cc.l]}; cc: AtConstr => {Proc[cc.p]}; cc: CcwConstr => {Proc[cc.i]; Proc[cc.j]; Proc[cc.k]}; ENDCASE => ERROR; IF c.frame.org # NIL THEN Proc[c.frame.org]; IF c.frame.hor # NIL THEN Proc[c.frame.hor]; IF c.frame.ver # NIL THEN Proc[c.frame.ver] END; ReplaceConstrPoints: PUBLIC PROC [c: Constr, Proc: PointReplaceProc] = BEGIN WITH c SELECT FROM cc: HorConstr => {cc.i _ Proc[cc.i]; cc.j _ Proc[cc.j]}; cc: VerConstr => {cc.i _ Proc[cc.i]; cc.j _ Proc[cc.j]}; cc: CongConstr => {cc.i _ Proc[cc.i]; cc.j _ Proc[cc.j]; cc.k _ Proc[cc.k]; cc.l _ Proc[cc.l]}; cc: ParaConstr => {cc.i _ Proc[cc.i]; cc.j _ Proc[cc.j]; cc.k _ Proc[cc.k]; cc.l _ Proc[cc.l]}; cc: PerpConstr => {cc.i _ Proc[cc.i]; cc.j _ Proc[cc.j]; cc.k _ Proc[cc.k]; cc.l _ Proc[cc.l]}; cc: AtConstr => {cc.p _ Proc[cc.p]}; cc: CcwConstr => {cc.i _ Proc[cc.i]; cc.j _ Proc[cc.j]; cc.k _ Proc[cc.k]}; ENDCASE => ERROR; IF c.frame.org # NIL THEN c.frame.org _ Proc[c.frame.org]; IF c.frame.hor # NIL THEN c.frame.hor _ Proc[c.frame.hor]; IF c.frame.ver # NIL THEN c.frame.ver _ Proc[c.frame.ver] END; EnumActions: PUBLIC PROC [Proc: ActionVisitProc] = BEGIN FOR a: Action _ actions.first, a.link WHILE a # NIL DO Proc[a] ENDLOOP END; EnumActionPoints: PUBLIC PROC [a: Action, Proc: PointVisitProc] = BEGIN FOR args: ActionArgs _ a.args, args.rest WHILE args # NIL DO WITH args.first SELECT FROM p: Point => Proc[p]; ENDCASE; ENDLOOP END; ReplaceActionPoints: PUBLIC PROC [a: Action, Proc: PointReplaceProc] = BEGIN FOR args: ActionArgs _ a.args, args.rest WHILE args # NIL DO WITH args.first SELECT FROM p: Point => args.first _ Proc[p]; ENDCASE; ENDLOOP END; Distance: PROC [p, q: Coords] RETURNS [REAL] = INLINE BEGIN RETURN[RealFns.SqRt[(p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y)]] END; FindPoint: PUBLIC PROC [coords: Coords, wound: BOOL _ FALSE] RETURNS [champ: Point] = BEGIN champdistance, pdistance: REAL; champ _ NIL; champdistance _ 1.0E+30; FOR p: Point _ points.first, p.link WHILE p # NIL DO IF wound AND p.wn = 0 THEN LOOP; pdistance _ Distance[p.coords, coords]; IF pdistance < champdistance THEN {champ _ p; champdistance _ pdistance} ENDLOOP END; BaloonSelect: PUBLIC PROC [start: IntCoords, NextPoint: NextPointProc] = BEGIN temp, pl, pr: Point; old: IntCoords _ start; new: IntCoords; lastPoint: BOOL _ FALSE; Wind: PROCEDURE = BEGIN IF old.x < new.x THEN -- move right: {WHILE pr # NIL AND pr.coords.x < new.x DO pc: Coords = pl.coords; -- transfer one point from the list pr to the list pl: temp _ pr.link; pr.link _ pl; pl _ pr; pr _ temp; IF (pc.y - old.y)*(new.x - old.x) > (new.y - old.y)*(pc.x - old.x) THEN {pl.wn _ pl.wn + 1} ENDLOOP} ELSE IF old.x > new.x THEN -- move left: {WHILE pl # NIL AND pl.coords.x >= new.x DO pc: Coords = pr.coords; temp _ pl.link; pl.link _ pr; pr _ pl; pl _ temp; IF (pc.y - new.y)*(old.x - new.x) > (old.y - new.y)*(pc.x - new.x) THEN {pr.wn _ pr.wn - 1} ENDLOOP}; END; SortPoints; -- just to make sure pl _ points.first; -- initialize linked lists pl and pr: pr _ NIL; WHILE pr # NIL AND pr.coords.x < start.x DO temp _ pr.link; pr.link _ pl; pl _ pr; pr _ temp ENDLOOP; UNTIL lastPoint DO [new, lastPoint] _ NextPoint[]; Wind; old _ new; ENDLOOP; new _ start; Wind; WHILE pl # NIL DO temp _ pl.link; pl.link _ pr; pr _ pl; pl _ temp ENDLOOP; END; AnyWoundPoints: PUBLIC PROC RETURNS [BOOL] = BEGIN FOR p: Point _ points.first, p.link WHILE p # NIL DO IF p.wn#0 THEN RETURN [TRUE] ENDLOOP; RETURN [FALSE] END; ConstrIsWound: PUBLIC PROC [c: Constr] RETURNS [wound: BOOL] = BEGIN TestPoint: PointVisitProc = {wound _ wound AND p.wn # 0}; wound _ TRUE; EnumConstrPoints[c, TestPoint] END; ActionIsWound: PUBLIC PROC [a: Action] RETURNS [wound: BOOL] = BEGIN TestPoint: PointVisitProc = {wound _ wound AND p.wn # 0}; wound _ TRUE; EnumActionPoints[a, TestPoint]; END; DeleteWoundItems: PUBLIC PROCEDURE = BEGIN MarkIt: PointVisitProc = {p.mark _ TRUE}; {UnMark: PointVisitProc = {p.mark _ FALSE}; EnumPoints[UnMark]}; BEGIN cAnt, cNext: Constr _ NIL; FOR c: Constr _ constrs.first, cNext WHILE c # NIL DO cNext _ c.link; IF ConstrIsWound[c] THEN {constrs _ DeleteConstr[c, cAnt, constrs]; GcConstrs[c, c.link]} ELSE {EnumConstrPoints[c, MarkIt]; cAnt _ c}; ENDLOOP END; BEGIN aAnt, aNext: Action _ NIL; FOR a: Action _ actions.first, aNext WHILE a # NIL DO aNext _ a.link; IF ActionIsWound[a] THEN {actions _ DeleteAction[a, aAnt, actions]; GcActions[a, a.link]} ELSE {EnumActionPoints[a, MarkIt]; aAnt _ a}; ENDLOOP END; -- Now delete all wound points that belong to no action of constraint, -- and reset the winding numbers and marks of the others: BEGIN pAnt, pNext: Point _ NIL; FOR p: Point _ points.first, pNext WHILE p # NIL DO pNext _ p.link; IF p.wn # 0 AND NOT p.mark THEN {points _ DeletePoint[p, pAnt, points]; GcPoints[p, p.link]} ELSE {pAnt _ p}; p.wn _ 0; p.mark _ FALSE; ENDLOOP END END; DuplicateWoundItems: PUBLIC PROCEDURE = BEGIN RepByCopy: PointReplaceProc = {RETURN [IF p.copy # NIL THEN p.copy ELSE p]}; CopyConstr: PROC [c: Constr] RETURNS [cCopy: Constr] = BEGIN cCopy _ WITH c SELECT FROM cc: HorConstr => NewHor[cc.i, cc.j], cc: VerConstr => NewHor[cc.i, cc.j], cc: CongConstr => NewCong[cc.i, cc.j, cc.k, cc.l], cc: ParaConstr => NewPara[cc.i, cc.j, cc.k, cc.l], cc: PerpConstr => NewPerp[cc.i, cc.j, cc.k, cc.l], cc: AtConstr => NewAt[cc.p, cc.coords], cc: CcwConstr => NewCcw[cc.i, cc.j, cc.k], ENDCASE => ERROR; cCopy.frame _ c.frame END; CopyAction: PROC [a: Action] RETURNS [aCopy: Action] = BEGIN CopyArgs: PROC [args: ActionArgs] RETURNS [copy: ActionArgs] = {RETURN[IF args = NIL THEN NIL ELSE Cons [args.first, CopyArgs[args.rest]]]}; aCopy _ NewAction[kind: a.kind, args: CopyArgs[a.args]]; END; BEGIN pCopy, pNext: Point _ NIL; FOR p: Point _ points.first, pNext WHILE p # NIL DO pNext _ p.link; IF p.wn # 0 THEN {pCopy _ NewPoint[p.coords, p.visible]; points _ InsertPoint[pCopy, p, points]; p.copy _ pCopy; pCopy.wn _ p.wn; pCopy.mark _ FALSE} ELSE {p.copy _ NIL} ENDLOOP END; BEGIN cCopy, cNext: Constr _ NIL; FOR c: Constr _ constrs.first, cNext WHILE c # NIL DO cNext _ c.link; IF ConstrIsWound[c] THEN {cCopy _ CopyConstr[c]; ReplaceConstrPoints [cCopy, RepByCopy]; constrs _ InsertConstr[cCopy, c, constrs]} ENDLOOP END; BEGIN aCopy, aNext: Action _ NIL; FOR a: Action _ actions.first, aNext WHILE a # NIL DO aNext _ a.link; IF ActionIsWound[a] THEN {aCopy _ CopyAction[a]; ReplaceActionPoints [aCopy, RepByCopy]; actions _ InsertAction[aCopy, a, actions]} ENDLOOP END; {Unw: PointVisitProc = {IF p.copy # NIL THEN p.wn _ 0}; EnumPoints[Unw]} END; IdentifyPoints: PUBLIC PROC = BEGIN UnMark: PointVisitProc = {p.mark _ FALSE}; UpdateAndMark: PointReplaceProc = {IF p.copy # NIL THEN {p.copy.mark _ TRUE; RETURN [p.copy]} ELSE RETURN [p]}; IdConstrArgs: ConstrVisitProc = {ReplaceConstrPoints[c, UpdateAndMark]}; IdActionArgs: ActionVisitProc = {ReplaceActionPoints[a, UpdateAndMark]}; DeleteUnreachableOriginals: PointReplaceProc = {pNew _ IF p.mark OR p.copy=NIL THEN p ELSE NIL; p.mark _ FALSE; p.copy _ NIL}; -- Mark copies so that we know who becomes unreachable EnumPoints[UnMark]; -- Replace p by p.copy in all actions and constraints, whenever p.copy # NIL EnumConstrs[IdConstrArgs]; EnumActions[IdActionArgs]; -- Now delete original points that have become unreachable ReplacePoints[DeleteUnreachableOriginals] END; SolveImage: PUBLIC PROC [eps: REAL] RETURNS [outcome: Solv.Outcome] = {-- Should display an hourglass and perhaps disable mouse/keyboard input. outcome _ Solv.Solve[constrs, eps]; SortPoints[]; -- Should turn off hourglass and re-enable mouse/keyboard input. }; END. Basis: TYPE = REF BasisRec; BasisRec: TYPE = RECORD[head:TangentVector _ NIL, tail:Basis]; TangentVector: TYPE = REF TvRec; TvRec: TYPE = RECORD[head:PointPtr _ NIL, x, y: REAL _ 0, tail:TangentVector]; PushState: PUBLIC PROCEDURE; PopState: PUBLIC PROCEDURE; InitJunoStorage: PROC; ResetJunoStorage: PROC; -- reclaims all records. pointLpad, pointRpad: PointPtr; -- The lists of points, edges, arcs edgeLpad, edgeRpad: EdgePtr; -- line constraints, and cong. constraints arcLpad, arcRpad: ArcPtr; -- are padded on both sides. lineLpad, lineRpad: LinPtr; congLpad, congRpad: CongPtr; stringLpad, stringRpad: StringPtr; horLpad, horRpad: HorPtr; verLpad, verRpad: VerPtr; ccLpad, ccRpad: CCPtr; SortPoints: PROC = BEGIN p _ NIL; q _ image.points; image.points _ NIL; UNTIL q = NIL DO t _ q; q _ t.link; t.link _ NIL; r _ p; rant _ NIL; WHILE r # NIL AND (t.x < r.x OR (t.x = r.x AND t.y < r.y)) DO rant _ r; r _ r.link; ENDLOOP; p _ InsertPoint [t, rant, p]; ENDLOOP; -- Now reverse the list by moving backwards through it: UNTIL p = NIL DO t _ p.link; p.link _ image.points; image.points _ p; p _ t ENDLOOP} END; NewPoint: PROC[x,y: INTEGER, visible: BOOL _ TRUE] RETURNS [PointPtr] = {p: PointPtr _ AddPoint[x,y]; p.visible _ visible; IF visible THEN JG.DrawPoint[x,y]; SortPoints[]; RETURN [p]}; AddLambda: PROC [f: REF ANY, args: LIST OF PointPtr] = {JG.SetPaintMode[opaque]; JA.Apply[f, args, NIL]; -- f is an atom JG.SetPaintMode[invert]; JG.viewerChanged _ TRUE; AddX[f, args]; SortPoints[] }; CopyToViewer: PROC = { DO -- IF JunoA.newVersion = TRUE AND gotNewJunoA = FALSE -- THEN { RefreshCursors[]; -- numCursor _ 11; notice that this is one-macro dependent!! -- JunoA.newVersion _ FALSE; -- gotNewJunoA _ TRUE; -- RefreshCursors[]}; IF viewerDead THEN EXIT; {t: Terminal.Virtual = Terminal.Current[]; Terminal.WaitForBWVerticalRetrace [t]; --! Should there be one instead of two calls? Terminal.WaitForBWVerticalRetrace [t]}; IF JG.viewerChanged THEN {ViewerOps.PaintViewer [viewer: self, hint: client, clearClient: FALSE]; JG.viewerChanged _ FALSE;} ENDLOOP}; MoveStep: PROC[copying: BOOLEAN, solving: BOOLEAN] = -- this proc is used by both MoveCmd and CopyCmd. It (a) identifies t with s, -- for each source-target pair (s,t) that satisfies s is selected and t is not -- selected; (b) computes a transform that takes the sources to the targets, -- and (c) applies the transform to all selected points (if copying = FALSE) or -- to all copied points (if copying = TRUE). It also actually copies the selected -- list if copying = TRUE. It is only within this procedure that copy fields are -- ever non-NIL. BEGIN n: INTEGER = MIN[atop,satop]; i: INTEGER; savedFixBit: ARRAY[0..10] OF BOOLEAN; savedSourcePoint: ARRAY[0..10] OF PointPtr; singular _ FALSE; IF n > 2 THEN ComputeTransform[a[atop].p, a[atop-1].p, a[atop-2].p, sa[satop].p, sa[satop-1].p, sa[satop-2].p]; IF n = 2 OR singular THEN ComputeTransform[a[atop].p, a[atop-1].p, NIL, sa[satop].p, sa[satop-1].p, NIL]; IF n = 1 OR singular THEN ComputeTransform[a[atop].p, NIL, NIL, sa[satop].p, NIL, NIL]; IF n = 0 OR singular THEN {JG.Blink["No such affine transformation"]; RETURN}; IF copying THEN {Copy[]; PerformTransform[copiedPoints]} ELSE PerformTransform[pointLpad.slink]; FOR i IN [0.. n-1] DO IF UnSelected[a[atop-i].p] AND NOT(UnSelected[sa[satop-i].p]) THEN {IF copying THEN a[atop-i].p.copy _ sa[satop-i].p.copy ELSE a[atop-i].p.copy _ sa[satop-i].p} ENDLOOP; FOR i IN [0..n-1] DO IF copying THEN savedSourcePoint[i] _ sa[satop-i].p.copy ELSE savedSourcePoint[i] _ sa[satop-i].p; savedFixBit[i] _ savedSourcePoint[i].fixed; savedSourcePoint[i].fixed _ TRUE; ENDLOOP; IF copying THEN {p: PointPtr _ pointLpad.slink; UNTIL p = pointRpad DO p.copy _ NIL; p _ p.slink ENDLOOP}; Identify[]; -- identify p with p.copy for all p such that p.copy # NIL IF solving THEN {JunoClass.cursor _ hourGlass; WindowManager.RestoreCursor; [] _ JunoSolver.Solve[.5]; JunoClass.cursor _ cursor[currentCursor].cursorName; WindowManager.RestoreCursor}; SortPoints[]; FOR i IN [0..n-1] DO savedSourcePoint[i].fixed _ savedFixBit[i] ENDLOOP; Refresh[]; END; AlgebraStep: PROC = {body: REF _ MakeDefBody[sa[satop-1].p, sa[satop].p]; -- MakeDefBody is from JunoBody colon: ATOM _ Atom.MakeAtom[":"]; leftpren: ATOM _ Atom.MakeAtom["("]; comma: ATOM _ Atom.MakeAtom[","]; AddDef[name: $X, locals: LIST[comma, $a, $b], body: body]; PW.AddTree[JunoParserEtc.junoA, LIST[colon, LIST[leftpren, $X, LIST[comma, $a, $b]], body]]}; OtherAlgebraStep: PROC = {body: REF _ MakeDefBody[sa[satop].p, NIL]; -- MakeDefBody is from JunoBody colon: ATOM _ Atom.MakeAtom[":"]; leftpren: ATOM _ Atom.MakeAtom["("]; comma: ATOM _ Atom.MakeAtom[","]; AddDef[name: $X, locals: $a, body: body]; PW.AddTree[JunoParserEtc.junoA, LIST[colon, LIST[leftpren, $X, $a], body]]}; RefreshPoints: PROCEDURE = BEGIN p: PointPtr _ pointLpad.link; WHILE p # pointRpad DO IF p.visible THEN JG.DrawPoint[Real.FixI[p.x], Real.FixI[p.y]]; p _ p.link ENDLOOP; END; RefreshEdges: PROCEDURE = BEGIN p: EdgePtr _ edgeLpad.link; WHILE p # edgeRpad DO JG.DrawEdge[p.b1.x, p.b1.y, p.b2.x, p.b2.y]; p _ p.link ENDLOOP; END; RefreshArcs: PROCEDURE = BEGIN p: ArcPtr _ arcLpad.link; WHILE p # arcRpad DO JG.DrawArc[p.b1.x, p.b1.y, p.b2.x, p.b2.y, p.b3.x, p.b3.y, p.b4.x, p.b4.y]; p _ p.link ENDLOOP; END; RefreshStrings: PROCEDURE = BEGIN p : StringPtr _ stringLpad.link; WHILE p # stringRpad DO JG.DrawString[p.b3.x, p.b3.y, p.stringText, p.stringFont, p.fontName, p.fontSize, p.bold, p.italic]; p _ p.link ENDLOOP; END; RefreshX: PROC = {RefreshXR[constructionList]}; RefreshXR: PROC[l: LIST OF ApplyRecord] = { IF l # NIL THEN {RefreshXR[l.rest]; JA.Apply[l.first.f, l.first.args, NIL]}}; SaveStateRec: TYPE = RECORD[ savePointLpad: PointPtr _ NIL, saveEdgeLpad: EdgePtr _ NIL, saveArcLpad: ArcPtr _ NIL, saveLineLpad: LinPtr _ NIL, saveCongLpad: CongPtr _ NIL, saveHorLpad: HorPtr _ NIL, saveVerLpad: VerPtr _ NIL, saveStringLpad: StringPtr _ NIL, saveCCLpad: CCPtr _ NIL]; UnSelected: PROC[q:PointPtr] RETURNS [BOOLEAN] = {p:PointPtr _ pointLpad.slink; UNTIL p = pointRpad DO IF p = q THEN RETURN [FALSE] ELSE p _ p.slink ENDLOOP; RETURN [TRUE]}; x JunoImageImpl.mesa Pieces from JunoTop.mesa created May 1981 by Greg Nelson, Donna Auguste Last Edited by: Gnelson, January 17, 1984 11:32 am Last Edited by: Maureen Stone January 19, 1984 12:08 pm Last Hacked by: Jorge Stolfi June 7, 1984 4:40:10 pm PDT Procedures to manipulate and paint the current Juno image. - - - - THE CURRENT IMAGE - - - - IMAGE POINTS Sorts in, in.link, in.link.link, ... (up to last point) by increasing x-coordinate. Should be O(n log n) worst case, O(n) if points are already sorted, and mostly fast if points are mostly sorted. Hope it works. Do one more pass over the entire list: Are there any more runs? Get the next ascending run Was it the only one? Get following ascending run Merge them into a single run. - - - - CONSTRAINTS - - - - ACTIONS - - - - ELEMENT ENUMERATION - - - - POINT LOCATION - - - - BALOON SELECTION WARNING: while this procedure is working, the links of the image points list are not valid. BaloonSelect works by repeatedly sampling the mouse coordinates andcalling the procedure Wind: The effect of Wind is to compute the winding number of the small segmentfrom old to new around every point. The winding number of the segment around the point (px, py) is zero unless px is in the range [old.x, new.x) and the point p is abovethe line through old and new. If non-zero, it is 1 or -1 according as new.x > old.x or new.x < old.x. To rapidly find the points (px,py) such that px is in [old.x, new.x), we arrange that (a) the points pl, link[pl], link[link[pl]] ... are exactly those points whose x coordinates are less than old.x, and the points are listed in decreasing order of their x coordinates, and (b) the points pr, link[pr], link[link[pr]], ... are exactly those points whose x coordinates are greater than or equal to old.x, and the points are listed in increasing order of their x coordinates. Now update winding number of point if it is above line of mouse motion. Now the winding numbers have been computed for all points. Must reset thelinked list of points. - - - - OPERATIONS ON BALOON-SELECTED POINTS Delete all constraints and actions whose arguments are all wound, -- and mark the arguments of those that are not deleted. Creates a copy of constraint c using the same points as c. Note: the copy links of the points are not examined Creates a copy of action a (including the top level of the argument list). Note: the points occuring in aCopy are those occuring in a, NOT their copies (if any). Duplicate all wound points (including the wn field), and link every point to its copy (or NIL if not wound): Copy all constraints whose arguments are all wound: Similarly for actions: Reset the winding numbers of the original points (but not the copies) - - - - POINT IDENTIFICATION - - - - CONSTRAINT SOLVING Solves the image constraints for all image points that are not fixed. - - - - JUNKYARD Êx˜šœ™JšœJ™JJšœ3™3Jšœ:™:Jšœ<™œ˜H—šœŸ œœ œ˜>J™Óšœ˜šœœœ ˜8Jšœ'™'Jšœœ˜šœ˜šœ™Jš œœœœœ˜&—šœ™Jš œ œœœœœ˜v—šœ™Jš œœœœœ˜&—šœ™Jš œ œœœœœ œ˜…—šœ™Jšœœœœœœœ9œœœ œ"œ˜è——Jšœœ˜ Jšœœ œœ ˜$—Jšœ˜ —Jšœœ˜——™šœŸ œœœ˜%Jšœœ6œ˜@——™šœŸ œœœ˜%Jšœœ6œ˜@——™šœŸ œœœ˜1Jšœœœ!œœœ œœ˜T—šœŸ œœœ˜6Jšœœœœ œœœ3œ œRœœœ\œœœ˜Ž—šœŸ œœœ˜3Jšœœœ#œœœ œœ˜V—šœŸœœœ$˜BJš!œœœœœôœœœœœœœœœœœœ˜µ—šœŸœœœ&˜GJš!œœœœœ€œœœœœ%œœœ%œœœ!œ˜ë—šœŸ œœœ˜3Jšœœœ#œœœ œœ˜V—šœŸœœœ$˜BJšœœœ&œœœœ œœ'œœœ˜ª—šœŸœœœ&˜GJšœœœ&œœœœ œœ4œœœ˜·——™š œŸœœœœ˜6Jšœœœ8œ˜L—š œŸ œœœœœœ˜VJšœœœ œœ!œœœœœ œœ2œœ0œœ˜½——™šœŸ œœœ/˜Išœ˜Jšœ\™\JšœIœœ˜VJšœ_™_šœŸœ œ˜š˜JšœNÏrœ œ™lJš œ` œ œ- œ œI™ëJšœÞ™Þšžœœž˜'šžœœœ˜+Jšœž8œ1˜‚JšœH™HJšžœAœ˜_—Jšœ˜ —šœœœž˜)š žœœœœ˜,JšœKœAœ˜©—Jšœœ˜ ——Jšœœ˜—Jšœ žœž&œœ˜dJš œœœœœ7œ˜kJšœœ œ=œ˜XJšœ žœ˜Jšœ`™`Jš žœœœ6œ˜P—Jšœœ˜—š œŸœœœœœ˜-Jšœœœ!œœœœœœœœœœœ˜…—š œÐbn œœœ œ œ˜@Jš œœÏb œ"œœ#œ˜z—š œ¡ œœœ œ œ˜?Jš œœ¢ œ"œœ$œ˜{——™/šœŸœœ œ˜&šœ˜Jš¢œœ˜*Jšœ¢œœ˜AJšœ|™|JšIœœœœ"œœœœœRœ:œœœœœ"œœœœœRœ9œœž‚œœœ œœœœ œœœNœ0œœ˜¡—Jšœœ˜—šœŸœœ œ˜)šœ˜Jš œ¢ œœœ œœœ˜Mšœ¡ œœ œ˜7šœ:™:J™3—JšœœœœœŽœœœ˜ë—š¡ œœœ˜7šœJ™JJ™W—šœ˜JšœŸœœœœœœœœœd˜É—Jšœœ˜—Jšœn™nšœ˜Jšœœœ œœœœ œ’œœœ˜¯—Jšœœ˜Jšœ4™4šœ˜Jšœœœ"œœœœœv˜ü—Jšœœ˜Jšœ™šœ˜Jšœœœ"œœœœœv˜ü—Jšœœ˜JšœF™FJš œ¢œœ œœ˜I—Jšœœ˜——™šœŸœœœ˜šœ˜Jšœ¢œœ˜+Jšœ¢ œœ œœœœœœ˜Jšœ¢ œ<˜IJšœ¢ œ<˜IJšœ¢œ$œœœœœœœ œ˜Jšœž7œžMœ6ž;œ)˜³—Jšœœ˜——™š œŸ œœœœœ˜FJšœF™FJšœžIœ;ž?œ˜Î——Jšœœ˜šœ ¢™Jšœœœ ˜Jšœ œœœ˜?Jšœœœ˜!Jš œœœœœ˜OJšœŸ œœ œ˜JšœŸœœ œ˜JšœŸœœ˜JšœŸœœž˜1Jšœ!ž$œž+œžœ¦˜ëšœŸ œœ˜Jš+œœœ#œœœœ#œœœœœœ œ œ+œ+œž8œœœœFœ˜ÚJšœœ˜—š œŸœœœ œœœ ˜HJš œ<œ œœ%œ˜—š œŸ œœœœœœ ˜8Jšœœœœžœœœœ#˜š—šœŸ œœ˜Jš%œœž7œ žœ žIœžœ ž œ žœ œ œœiž.œ=œœœBœœœ œ˜í—š œŸœœ œ œ˜5Jš‘œžOœžOœžMœžPœžRœžQœžœœœœœœœœœœœœœgœœ œ*œœœœ œœœœœœœ œœ7œœ œ9œ(œœ œœœœ$œœ œ9œ'œœœ œœ œ;œzœœœœ-œœ œœž;œœ œªœœ œ,œœ˜“—šœŸ œœ˜Jšœœ,ž œœ"œœ1œœ/œœœ˜ï—šœŸœœ˜Jšœœœž œœ"œœCœ/œœ˜Ã—šœŸ œ œ˜Jšœœ'œœœ œœGœœ˜¶—šœŸ œ œ˜Jš œœ%œœœ@œœ˜›—šœŸ œ œ˜Jš œœ#œœœ[œœ˜³—šœŸœ œ˜Jš œœ,œœ œ—œœ˜‡—JšœŸœœ"˜1š œŸ œœœœ˜*Jš œœœœœ œ˜R—šœœœ˜Jšœœœœœœœœ œœ˜“—š œŸ œœ œœ˜1Jšœ'œœœœœœœ œœœ˜ˆ——J˜—…—Ieþ