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