-- ALELinesA.mesa -- Edited by Sweet, 9-Apr-82 10:22:13 DIRECTORY ALEOps, Storage, String, Table, Window; ALELinesA: PROGRAM IMPORTS ALEOps, Storage, String, Table, Window EXPORTS ALEOps = BEGIN OPEN ALEOps; header: PUBLIC ALEHeader ← []; ptb, ltb, lbb, hrb, vrb: Table.Base; tableSpace: POINTER; hArray: PUBLIC DESCRIPTOR FOR ARRAY OF HorizRec; vArray: PUBLIC DESCRIPTOR FOR ARRAY OF VertRec; lastH, lastV: PUBLIC INTEGER; DrawnWidth: ARRAY [-3..4] OF CARDINAL = [1, 1, 1, 1, 1, 2, 4, 4]; LineNotify: Table.Notifier = BEGIN ptb ← base[ptType]; ltb ← base[ltType]; lbb ← base[lbType]; hrb ← base[hrType]; hArray ← DESCRIPTOR[hrb, LENGTH[hArray]]; vrb ← base[vrType]; vArray ← DESCRIPTOR[vrb, LENGTH[vArray]]; ALELinesBNotify[base]; END; tablePages: CARDINAL ← 100; InitLines: PUBLIC PROC = BEGIN weights: ARRAY [0..nTables) OF CARDINAL ← [10, 10, 10, 10, 10, 10, 10]; p: POINTER; header ← []; tableSpace ← p ← Storage.Pages[tablePages]; hArray ← DESCRIPTOR[NIL, 0]; vArray ← DESCRIPTOR[NIL, 0]; lastH ← lastV ← -1; Table.Create[[LOOPHOLE[p], tablePages*256], DESCRIPTOR[weights]]; Table.AddNotify[LineNotify]; InitHash[]; END; ResetLines: PUBLIC PROC = BEGIN FOR i: CARDINAL IN [0..nTables) DO Table.Trim[i, 0] ENDLOOP; hArray ← DESCRIPTOR[NIL, 0]; vArray ← DESCRIPTOR[NIL, 0]; lastH ← lastV ← -1; ResetHash[]; header ← []; END; MulDiv: PUBLIC PROC [a, b, c: LONG INTEGER] RETURNS [LONG INTEGER] = BEGIN threshold: LONG INTEGER; IF a < b THEN {t: LONG INTEGER = a; a ← b; b ← t}; threshold ← LAST[LONG INTEGER] / ABS[b]; WHILE ABS[a] > threshold DO a ← a/8; c ← c/8; ENDLOOP; RETURN[(a*b)/c] END; lastHY: ADistance; FindHList: PUBLIC PROC [y: ADistance, addNew: BOOLEAN ← FALSE] RETURNS [hi: INTEGER] = BEGIN l, u: INTEGER; l ← 0; u ← LENGTH[hArray] - 1; IF u = -1 AND ~addNew THEN RETURN[-1]; IF lastH # -1 THEN SELECT y FROM > lastHY => l ← MIN[lastH + 1, LENGTH[hArray]-1]; < lastHY => u ← MAX[lastH - 1, 0]; ENDCASE => RETURN[lastH]; WHILE l <= u DO hi ← (l+u)/2; SELECT hArray[hi].y FROM < y => l ← hi + 1; > y => u ← hi - 1; ENDCASE => {lastHY ← y; lastH ← hi; RETURN}; ENDLOOP; hi ← l; IF ~addNew THEN {IF hi = LENGTH[hArray] THEN RETURN[-1]; lastHY ← hArray[hi].y; lastH ← hi; RETURN}; -- first item > y [] ← Table.Allocate[hrType, SIZE[HorizRec]]; hArray ← DESCRIPTOR[hrb, LENGTH[hArray]+1]; FOR i: INTEGER DECREASING IN (hi..LENGTH[hArray]) DO hArray[i] ← hArray[i-1]; ENDLOOP; hArray[hi] ← [y: y]; lastHY ← y; lastH ← hi; RETURN END; UnChainHoriz: PROC [l: LTIndex] = BEGIN hi: INTEGER = FindHList[ptb[ltb[l].p1].pos.y]; prev: LTIndex ← LTNull; IF hi = -1 THEN ERROR; FOR nl: LTIndex ← hArray[hi].lines, ltb[nl].thread WHILE nl # l DO IF nl = LTNull THEN ERROR; prev ← nl; ENDLOOP; IF hArray[hi].l.min = ptb[ltb[l].p1].pos.x OR hArray[hi].l.max = ptb[ltb[l].p2].pos.x THEN BEGIN max: ADistance ← 0; min: ADistance ← LAST[ADistance]; FOR nl: LTIndex ← hArray[hi].lines, ltb[nl].thread WHILE nl # LTNull DO min ← MIN[ptb[ltb[nl].p1].pos.x, min]; max ← MAX[ptb[ltb[nl].p2].pos.x, max]; ENDLOOP; hArray[hi].l ← [min: min, max: max]; END; IF prev = LTNull THEN hArray[hi].lines ← ltb[l].thread ELSE ltb[prev].thread ← ltb[l].thread; END; UnChainVert: PROC [l: LTIndex] = BEGIN vi: INTEGER = FindVList[ptb[ltb[l].p1].pos.x]; prev: LTIndex ← LTNull; IF vi = -1 THEN ERROR; FOR nl: LTIndex ← vArray[vi].lines, ltb[nl].thread WHILE nl # l DO IF nl = LTNull THEN ERROR; prev ← nl; ENDLOOP; IF vArray[vi].l.min = ptb[ltb[l].p1].pos.y OR vArray[vi].l.max = ptb[ltb[l].p2].pos.y THEN BEGIN max: ADistance ← 0; min: ADistance ← LAST[ADistance]; FOR nl: LTIndex ← vArray[vi].lines, ltb[nl].thread WHILE nl # LTNull DO min ← MIN[ptb[ltb[nl].p1].pos.y, min]; max ← MAX[ptb[ltb[nl].p2].pos.y, max]; ENDLOOP; vArray[vi].l ← [min: min, max: max]; END; IF prev = LTNull THEN vArray[vi].lines ← ltb[l].thread ELSE ltb[prev].thread ← ltb[l].thread; END; UnChainDiag: PROC [l: LTIndex] = BEGIN prev: LTIndex ← LTNull; FOR nl: LTIndex ← header.diagLines, ltb[nl].thread WHILE nl # l DO IF nl = LTNull THEN ERROR; prev ← nl; ENDLOOP; IF prev = LTNull THEN header.diagLines ← ltb[l].thread ELSE ltb[prev].thread ← ltb[l].thread; END; lastVX: ADistance; FindVList: PUBLIC PROC [x: ADistance, addNew: BOOLEAN ← FALSE] RETURNS [vi: INTEGER] = BEGIN l, u: INTEGER; l ← 0; u ← LENGTH[vArray] - 1; IF u = -1 AND ~addNew THEN RETURN[-1]; IF lastV # -1 THEN SELECT x FROM > lastVX => l ← MIN[lastV + 1, LENGTH[vArray]-1]; < lastVX => u ← MAX[lastV - 1, 0]; ENDCASE => RETURN[lastV]; WHILE l <= u DO vi ← (l+u)/2; SELECT vArray[vi].x FROM < x => l ← vi + 1; > x => u ← vi - 1; ENDCASE => {lastVX ← x; lastV ← vi; RETURN}; ENDLOOP; vi ← l; IF ~addNew THEN {IF vi = LENGTH[vArray] THEN RETURN[-1]; lastVX ← vArray[vi].x; lastV ← vi; RETURN}; -- first item > x [] ← Table.Allocate[vrType, SIZE[VertRec]]; vArray ← DESCRIPTOR[vrb, LENGTH[vArray]+1]; FOR i: INTEGER DECREASING IN (vi..LENGTH[vArray]) DO vArray[i] ← vArray[i-1]; ENDLOOP; vArray[vi] ← [x: x]; lastVX ← x; lastV ← vi; RETURN END; AllPoints: PUBLIC PROC [action: PointScan] RETURNS [p: PTIndex] = BEGIN pointTableSize: CARDINAL = Table.Bounds[ptType].size; FOR p ← FIRST[PTIndex], p + SIZE[Point] WHILE LOOPHOLE[p, CARDINAL] < pointTableSize DO IF ~ptb[p].free AND action[p, @ptb[p]] THEN RETURN; ENDLOOP; RETURN[PTNull] END; SelectedPoints: PUBLIC PROC [action: PointScan] RETURNS [p: PTIndex] = BEGIN FOR p ← header.selectedPoints, header.nextSelPoint WHILE p # PTNull DO header.nextSelPoint ← ptb[p].selNext; IF action[p, @ptb[p]] THEN RETURN; ENDLOOP; END; AllLines: PUBLIC PROC [action: LineScan] RETURNS [l: LTIndex] = BEGIN lineTableSize: CARDINAL = Table.Bounds[ltType].size; FOR l ← FIRST[LTIndex], l + SIZE[Line] WHILE LOOPHOLE[l, CARDINAL] < lineTableSize DO IF ~ltb[l].free AND action[l, @ltb[l]] THEN RETURN; ENDLOOP; RETURN[LTNull] END; SelectedLines: PUBLIC PROC [action: LineScan] RETURNS [l: LTIndex] = BEGIN next: LTIndex; FOR l ← header.selectedLines, next WHILE l # LTNull DO next ← ltb[l].selNext; IF action[l, @ltb[l]] THEN RETURN; ENDLOOP; END; LinesThru: PUBLIC PROC [p: PTIndex, action: LineScan] RETURNS [l: LTIndex] = BEGIN next: LTIndex; l ← ptb[p].lines; WHILE l # LTNull DO next ← IF ltb[l].p1 = p THEN ltb[l].p1Chain ELSE ltb[l].p2Chain; IF action[l, @ltb[l]] THEN RETURN; l ← next; ENDLOOP; RETURN END; HLinesInABox: PROC [box: POINTER TO ABox, action: LineScan, completely: BOOLEAN ← FALSE] RETURNS [l: LTIndex] = BEGIN y1: ADistance ← box.y1; hi: INTEGER; IF ~completely THEN y1 ← y1 - AdjForWidth[4]; -- min possible hi ← FindHList[y1]; IF hi = -1 THEN RETURN[LTNull]; DO hr: HorizRec ← hArray[hi]; IF hr.y > box.y2 THEN EXIT; IF hr.l.min < box.x2 AND hr.l.max > box.x1 THEN BEGIN FOR l ← hr.lines, ltb[l].thread WHILE l # LTNull DO x1: ADistance = ptb[ltb[l].p1].pos.x; x2: ADistance = ptb[ltb[l].p2].pos.x; IF x1 > box.x2 THEN EXIT; IF ( (completely AND x1>= box.x1 AND x2 <= box.x2 AND hr.y + AdjForWidth[ltb[l].width] <= box.y2) OR (x2 >= box.x1 AND hr.y + AdjForWidth[ltb[l].width] >= box.y1)) AND action[l, @ltb[l]] THEN RETURN; ENDLOOP; END; hi ← hi + 1; IF hi >= LENGTH[hArray] THEN EXIT; ENDLOOP; RETURN [LTNull]; END; VLinesInABox: PROC [box: POINTER TO ABox, action: LineScan, completely: BOOLEAN ← FALSE] RETURNS [l: LTIndex] = BEGIN x1: ADistance ← box.x1; vi: INTEGER; IF ~completely THEN x1 ← x1 - AdjForWidth[4]; -- min possible vi ← FindVList[x1]; IF vi = -1 THEN RETURN[LTNull]; DO vr: VertRec ← vArray[vi]; IF vr.x > box.x2 THEN EXIT; IF vr.l.min < box.y2 AND vr.l.max > box.y1 THEN BEGIN FOR l ← vr.lines, ltb[l].thread WHILE l # LTNull DO y1: ADistance = ptb[ltb[l].p1].pos.y; y2: ADistance = ptb[ltb[l].p2].pos.y; IF y1 > box.y2 THEN EXIT; IF ( (completely AND y1>= box.y1 AND y2 <= box.y2 AND vr.x + AdjForWidth[ltb[l].width] <= box.x2) OR (y2 >= box.y1 AND vr.x + AdjForWidth[ltb[l].width] >= box.x1)) AND action[l, @ltb[l]] THEN RETURN; ENDLOOP; END; vi ← vi + 1; IF vi >= LENGTH[vArray] THEN EXIT; ENDLOOP; RETURN [LTNull]; END; AdjForWidth: PROC [w: INTEGER] RETURNS [ADistance] = INLINE {RETURN [DrawnWidth[state.magnify]*w-1]}; DLinesInABox: PROC [box: POINTER TO ABox, action: LineScan, completely: BOOLEAN ← FALSE] RETURNS [l: LTIndex] = BEGIN -- rework for completely FOR l ← header.diagLines, ltb[l].thread WHILE l # LTNull DO lBox: ABox ← ABoxForBox[BoxForLine[l]]; IF ~(lBox.x1 > box.x2 OR box.x1 > lBox.x2 OR lBox.y1 > box.y2 OR box.y1 > lBox.y2) AND action[l, @ltb[l]] THEN RETURN; ENDLOOP; RETURN [LTNull]; END; LinesInABox: PUBLIC PROC [box: POINTER TO ABox, action: LineScan, completely: BOOLEAN ← FALSE] RETURNS [l: LTIndex] = BEGIN l ← HLinesInABox[box, action, completely]; IF l # LTNull THEN RETURN; l ← VLinesInABox[box, action, completely]; IF l # LTNull THEN RETURN; RETURN [DLinesInABox[box, action, completely]]; END; ABoxForBox: PUBLIC PROCEDURE [box: Window.Box] RETURNS [aBox: ABox] = BEGIN -- is conservative for magnify < 0 fudge: ARRAY [-3..4] OF ADistance = [7*16, 3*16, 16, 0, 0, 0, 0, 0]; aPlace: APosition = APosForPlace[box.place]; aBox ← [ x1: aPlace.x, x2: aPlace.x + ADistanceForDots[box.dims.w] + fudge[state.magnify], y1: aPlace.y, y2: aPlace.y + ADistanceForDots[box.dims.h] + fudge[state.magnify]]; END; SelectedLabels: PUBLIC PROC [action: LabelScan] RETURNS [lb: LBIndex] = BEGIN next: LBIndex; FOR lb ← header.selectedLabels, next WHILE lb # LBNull DO next ← lbb[lb].selNext; IF action[lb, @lbb[lb]] THEN RETURN; ENDLOOP; END; currentWidth: PUBLIC LineWidth ← 1; currentTexture: PUBLIC LineTexture ← solid; ClearSelections: PUBLIC PROC = BEGIN DeselectLine: LineScan = BEGIN lth.selected ← FALSE; lth.selNext ← LTNull; MaybeDisplayLine[l]; RETURN[FALSE] END; DeselectLabel: LabelScan = BEGIN lbh.selected ← FALSE; lbh.selNext ← LBNull; PaintLabel[lb]; RETURN[FALSE] END; DeselectPoint: PointScan = BEGIN Window.InvalidateBox[pictureWindow, BoxForPoint[p]]; pth.selNext ← PTNull; pth.selected ← FALSE; RETURN[FALSE] END; [] ← SelectedPoints[DeselectPoint]; [] ← SelectedLines[DeselectLine]; [] ← SelectedLabels[DeselectLabel]; header.selectedPoints ← PTNull; header.selectedLines ← LTNull; header.selectedLabels ← LBNull; END; BoxForPoint: PUBLIC PROC [p: PTIndex] RETURNS [Window.Box] = BEGIN pPlace: Window.Place = PicturePlace[ptb[p].pos]; RETURN[[[pPlace.x-4, pPlace.y-4], [9,9]]] END; ClosePoint: PROC [pos: APosition, selected: BOOLEAN ← FALSE] RETURNS [p: PTIndex] = BEGIN epsilon: ADistance ← ADistanceForDots[2 * DrawnWidth[state.magnify]]; Check: PointScan = BEGIN RETURN [ (~selected OR pth.selected) AND ABS[pth.pos.x-pos.x] <= epsilon AND ABS[pth.pos.y-pos.y] <= epsilon]; END; p ← AllPoints[Check]; END; CloseLine: PROC [pos: APosition, selected: BOOLEAN ← FALSE] RETURNS [l: LTIndex] = BEGIN epsilon: ADistance ← (2*ADistanceForDots[1]); box: ABox ← [x1: pos.x - epsilon, x2: pos.x + epsilon, y1: pos.y - epsilon, y2: pos.y + epsilon]; Check: LineScan = BEGIN y, x: ADistance; pos1: APosition = ptb[lth.p1].pos; pos2: APosition = ptb[lth.p2].pos; IF selected AND ~lth.selected THEN RETURN[FALSE]; SELECT lth.class FROM horiz => RETURN [TRUE]; vert => RETURN [TRUE]; shallow => BEGIN IF pos.x ~IN [pos1.x..pos2.x] THEN RETURN[FALSE]; y ← pos1.y + MulDiv[ (pos.x - pos1.x), (pos2.y - pos1.y), (pos2.x - pos1.x)]; RETURN [pos.y IN [y-epsilon.. y + ADistanceForDots[DrawnWidth[lth.width]] + epsilon]]; END; ENDCASE => IF pos1.y > pos2.y THEN BEGIN IF pos.y ~IN [pos2.y..pos1.y] THEN RETURN[FALSE]; END ELSE IF pos.y ~IN [pos1.y..pos2.y] THEN RETURN[FALSE]; x ← pos1.x + MulDiv[ (pos1.y - pos.y), (pos2.x - pos1.x), (pos1.y - pos2.y)]; RETURN [pos.x IN [x-epsilon.. x + ADistanceForDots[DrawnWidth[lth.width]] + epsilon]]; END; l ← LinesInABox[@box, Check]; END; CloseLabel: PROC [pos: APosition, selected: BOOLEAN ← FALSE] RETURNS [lb: LBIndex] = BEGIN place: Window.Place ← PicturePlace[pos]; box: Window.Box ← [[place.x-2, place.y-2], [4,4]]; Check: LabelScan = BEGIN labelBox: Window.Box; IF selected AND ~lbh.selected THEN RETURN[FALSE]; labelBox ← BoxForLabel[lb]; RETURN [~Disjoint[@labelBox, @box]]; END; lb ← AllLabels[Check]; END; AddSelection: PUBLIC PROC [pos: APosition] = BEGIN l: LTIndex; lb: LBIndex; p: PTIndex; p ← ClosePoint[pos]; IF p # PTNull THEN BEGIN pPlace: Window.Place = PicturePlace[ptb[p].pos]; IF ~ptb[p].selected THEN BEGIN ptb[p].selected ← TRUE; Window.DisplayData[ window: pictureWindow, box: [[x: pPlace.x-8, y: pPlace.y-8], [16,16]], data: @Cursors[selPt], wpl: 1]; ptb[p].selNext ← header.selectedPoints; header.selectedPoints ← p; END; ASetSourcePos[ptb[p].pos]; RETURN END; l ← CloseLine[pos]; IF l # LTNull THEN BEGIN IF ~ltb[l].selected THEN BEGIN ltb[l].selected ← TRUE; MaybeDisplayLine[l]; ltb[l].selNext ← header.selectedLines; header.selectedLines ← l; END; ASetSourcePos[ptb[ltb[l].p1].pos]; RETURN; END; lb ← CloseLabel[pos]; IF lb # LBNull THEN BEGIN IF ~lbb[lb].selected THEN BEGIN lbb[lb].selected ← TRUE; PaintLabel[lb]; lbb[lb].selNext ← header.selectedLabels; header.selectedLabels ← lb; END; ASetSourcePos[lbb[lb].pos]; RETURN; END; END; UnSelChainPoint: PROC [p: PTIndex] = BEGIN prev: PTIndex ← PTNull; FOR np: PTIndex ← header.selectedPoints, ptb[np].selNext WHILE np # p DO IF np = PTNull THEN ERROR; prev ← np; ENDLOOP; IF prev = PTNull THEN header.selectedPoints ← ptb[p].selNext ELSE ptb[prev].selNext ← ptb[p].selNext; IF header.nextSelPoint = p THEN header.nextSelPoint ← ptb[p].selNext; ptb[p].selNext ← PTNull; END; UnSelChainLine: PROC [l: LTIndex] = BEGIN prev: LTIndex ← LTNull; FOR nl: LTIndex ← header.selectedLines, ltb[nl].selNext WHILE nl # l DO IF nl = LTNull THEN ERROR; prev ← nl; ENDLOOP; IF prev = LTNull THEN header.selectedLines ← ltb[l].selNext ELSE ltb[prev].selNext ← ltb[l].selNext; ltb[l].selNext ← LTNull; END; UnSelChainLabel: PUBLIC PROC [lb: LBIndex] = BEGIN prev: LBIndex ← LBNull; FOR nlb: LBIndex ← header.selectedLabels, lbb[nlb].selNext WHILE nlb # lb DO IF nlb = LBNull THEN ERROR; prev ← nlb; ENDLOOP; IF prev = LBNull THEN header.selectedLabels ← lbb[lb].selNext ELSE lbb[prev].selNext ← lbb[lb].selNext; lbb[lb].selNext ← LBNull; END; SubSelection: PUBLIC PROC [pos: APosition] = BEGIN l: LTIndex; lb: LBIndex; p: PTIndex; p ← ClosePoint[pos, TRUE]; IF p # PTNull THEN BEGIN Window.InvalidateBox[pictureWindow, BoxForPoint[p]]; ptb[p].selected ← FALSE; UnSelChainPoint[p]; RETURN END; l ← CloseLine[pos, TRUE]; IF l # LTNull THEN {ltb[l].selected ← FALSE; UnSelChainLine[l]; MaybeDisplayLine[l]; RETURN}; lb ← CloseLabel[pos, TRUE]; IF lb # LBNull THEN {lbb[lb].selected ← FALSE; UnSelChainLabel[lb]; PaintLabel[lb]; RETURN}; END; SelectInBox: PUBLIC PROC [pos1, pos2: APosition] = BEGIN ul: APosition = [x: MIN[pos1.x, pos2.x], y: MIN[pos1.y, pos2.y]]; lr: APosition = [x: MAX[pos1.x, pos2.x], y: MAX[pos1.y, pos2.y]]; newSource: APosition ← [LAST[ADistance], LAST[ADistance]]; CheckLine: LineScan = BEGIN lp1: APosition = ptb[lth.p1].pos; lp2: APosition = ptb[lth.p2].pos; IF lp1.x IN [ul.x..lr.x] AND lp2.x IN [ul.x..lr.x] AND lp1.y IN [ul.y..lr.y] AND lp2.y IN [ul.y..lr.y] THEN {lth.selected ← TRUE; lth.selNext ← header.selectedLines; header.selectedLines ← l; MaybeDisplayLine[l]; newSource.x ← MIN[newSource.x, lp1.x]; newSource.y ← MIN[newSource.y, lp1.y, lp2.y]}; RETURN[FALSE]; END; CheckLabel: LabelScan = BEGIN IF lbh.pos.x IN [ul.x..lr.x] AND lbh.pos.y IN [ul.y..lr.y] THEN {lbh.selected ← TRUE; lbh.selNext ← header.selectedLabels; header.selectedLabels ← lb; PaintLabel[lb]; newSource.x ← MIN[newSource.x, lbh.pos.x]; newSource.y ← MIN[newSource.y, lbh.pos.y]}; RETURN[FALSE]; END; ClearSelections[]; [] ← AllLines[CheckLine]; [] ← AllLabels[CheckLabel]; IF newSource # [LAST[ADistance], LAST[ADistance]] THEN ASetSourcePos[newSource]; END; DeleteSelections: PUBLIC PROC = BEGIN deleted: Redraw ← NIL; KillLine: LineScan = BEGIN new: Redraw = Storage.Node[SIZE[line RedrawObject]]; pos1: APosition = ptb[lth.p1].pos; pos2: APosition = ptb[lth.p2].pos; new↑ ← [next: deleted, var: line[ pos1: pos1, pos2: pos2, width: lth.width, texture: lth.texture]]; deleted ← new; DeleteLine[l]; RETURN[FALSE]; END; KillLabel: LabelScan = BEGIN new: Redraw = Storage.Node[SIZE[label RedrawObject]]; new↑ ← [next: deleted, var: label[ font: lbh.font, mode: lbh.mode, pos: lbh.pos, hti: lbh.hti]]; deleted ← new; DeleteLabel[lb]; RETURN[FALSE]; END; [] ← SelectedLines[KillLine]; [] ← SelectedLabels[KillLabel]; header.selectedLines ← LTNull; header.selectedPoints ← PTNull; ToWasteBasket[deleted]; END; UndeleteItems: PUBLIC PROC = BEGIN ClearSelections[]; RedrawItems[FromWasteBasket[]]; END; wbDepth: CARDINAL = 4; wasteBasket: ARRAY [0..wbDepth) OF Redraw ← ALL[NIL]; ToWasteBasket: PROC [new: Redraw] = BEGIN FreeItems[wasteBasket[wbDepth-1]]; FOR i: CARDINAL DECREASING IN (0..wbDepth) DO wasteBasket[i] ← wasteBasket[i-1] ENDLOOP; wasteBasket[0] ← new; END; FromWasteBasket: PROC RETURNS [rd: Redraw] = BEGIN rd ← wasteBasket[0]; FOR i: CARDINAL IN (0..wbDepth) DO wasteBasket[i-1] ← wasteBasket[i] ENDLOOP; wasteBasket[wbDepth-1] ← NIL; END; FreeItems: PROC [rd: Redraw] = BEGIN next: Redraw; WHILE rd # NIL DO next ← rd.next; Storage.Free[rd]; rd ← next; ENDLOOP; END; SourceToClosePoint: PUBLIC PROC [pos: APosition] = BEGIN p: PTIndex ← ClosePoint[pos]; IF p # PTNull THEN ASetSourcePos[ptb[p].pos]; END; DestToClosePoint: PUBLIC PROC [pos: APosition] = BEGIN p: PTIndex ← ClosePoint[pos]; IF p # PTNull THEN ASetDestPos[ptb[p].pos]; END; PosOf: PUBLIC PROC [p: PTIndex] RETURNS [APosition] = {RETURN [ptb[p].pos]}; PointsOf: PUBLIC PROC [l: LTIndex] RETURNS [p1, p2: PTIndex] = {RETURN [ltb[l].p1, ltb[l].p2]}; WidthOf: PUBLIC PROC [l: LTIndex] RETURNS [[1..4]] = {RETURN [ltb[l].width]}; DeleteLine: PUBLIC PROC [l: LTIndex] = BEGIN prev: LTIndex; pt, pb: PTIndex; FindPT: LineScan = BEGIN p: PTIndex = IF ltb[l].p1 = pb THEN ltb[l].p2 ELSE ltb[l].p1; IF p = pt THEN RETURN[TRUE]; prev ← l; RETURN[FALSE] END; Window.InvalidateBox[pictureWindow, BoxForLine[l]]; pb ← ltb[l].p1; pt ← ltb[l].p2; prev ← LTNull; IF LinesThru[pb, FindPT] = LTNull THEN ERROR; IF prev = LTNull THEN ptb[pb].lines ← ltb[l].p1Chain ELSE IF ltb[prev].p1 = pb THEN ltb[prev].p1Chain ← ltb[l].p1Chain ELSE ltb[prev].p2Chain ← ltb[l].p1Chain; pb ← ltb[l].p2; pt ← ltb[l].p1; prev ← LTNull; IF LinesThru[pb, FindPT] = LTNull THEN ERROR; IF prev = LTNull THEN ptb[pb].lines ← ltb[l].p2Chain ELSE IF ltb[prev].p1 = pb THEN ltb[prev].p1Chain ← ltb[l].p2Chain ELSE ltb[prev].p2Chain ← ltb[l].p2Chain; SELECT ltb[l].class FROM horiz => UnChainHoriz[l]; vert => UnChainVert[l]; ENDCASE => UnChainDiag[l]; FreeLine[l]; IF ptb[pb].lines = LTNull THEN FreePoint[pb]; IF ptb[pt].lines = LTNull THEN FreePoint[pt]; END; FreeLine: PROC [l: LTIndex] = BEGIN ltb[LOOPHOLE[l, FNIndex]] ← [next: header.freeLine]; header.freeLine ← l; END; FreePoint: PROC [p: PTIndex] = BEGIN hi: INTEGER = FindHList[ptb[p].pos.y, FALSE]; follows: PTIndex ← PTNull; np: PTIndex; FOR np ← hArray[hi].points, ptb[np].thread WHILE np # p DO follows ← np; ENDLOOP; IF follows = PTNull THEN hArray[hi].points ← ptb[p].thread ELSE ptb[follows].thread ← ptb[p].thread; IF ptb[p].selected THEN UnSelChainPoint[p]; ptb[LOOPHOLE[p, FNIndex]] ← [next: header.freePoint]; header.freePoint ← p; END; DimSelection: PUBLIC PROC [pos: APosition, feet: BOOLEAN] = BEGIN Count: LineScan = {n ← n + 1; RETURN[FALSE]}; This: LineScan = {RETURN[TRUE]}; n: CARDINAL ← 0; l: LTIndex; s: STRING ← [20]; pos1, pos2: APosition; dy, dx, len, inches, nfeet, num: ADistance; denom: CARDINAL; [] ← SelectedLines[Count]; IF n # 1 THEN {OutString["Select a single line first"L]; RETURN}; l ← SelectedLines[This]; pos1 ← ptb[ltb[l].p1].pos; pos2 ← ptb[ltb[l].p2].pos; dx ← ABS[pos1.x - pos2.x]; dy ← ABS[pos1.y - pos2.y]; len ← SELECT TRUE FROM dx = 0 => dy, dy = 0 => dx, ENDCASE => Hypot[dx, dy]; inches ← len / 16; num ← len MOD 16; IF feet THEN {nfeet ← inches / 12; inches ← inches MOD 12} ELSE nfeet ← 0; IF nfeet # 0 THEN { String.AppendLongDecimal[s, nfeet]; String.AppendChar[s, '']}; IF inches # 0 THEN String.AppendLongDecimal[s, inches]; IF num # 0 THEN { String.AppendChar[s, '-]; denom ← 16; WHILE num MOD 2 = 0 DO num ← num / 2; denom ← denom / 2; ENDLOOP; String.AppendLongDecimal[s, num]; String.AppendChar[s, '/]; String.AppendDecimal[s, denom]}; IF inches # 0 OR num # 0 THEN String.AppendChar[s, '"]; DrawLabel[s, pos]; END; END.