-- 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.