Compose:
PUBLIC
PROC[boxes:
LIST
OF
BOX, alignments:
LIST
OF Alignment2D, hOrigin, vOrigin: TaggedOffset]
RETURNS[
BOX,
LIST
OF
BOX] ~ {
effects: Performs the layout composition for boxes using alignments.
Returns information expected from a CompositionProc.
local declarations
relBoxes: LIST OF BOX ← NIL; -- cons up return values; relative units
myBox, myRelBox: BOX; -- box which encloses entire expression
myExtents: ImagerFont.Extents;
tempBox, attachHRelBox, attachVRelBox: BOX;
farLeft, farRight, farUp, farDown: REAL ← 0.0;
scaleVec, originOffset: VEC ← [0.0, 0.0];
dummyPtExtents: ImagerFont.Extents ← [leftExtent: 0.0, rightExtent: 0.001, descent: 0.0, ascent: 0.001]; -- null-sized extents used for alignment purposes
should really ENABLE boxNotFound => ERROR unable
local procedures
LocalScale:
PROC[b:
BOX]
RETURNS[
BOX] ~ {
scaledBox: BOX ← MathBox.Scale[b, scaleVec];
RETURN[MathBox.ChangeType[scaledBox, relative]];
};
LocalFixOffset:
PROC[b:
BOX]
RETURNS[
BOX] ~ {
RETURN[MathBox.ChangeOffset[b, Vector.Sub[b.Offset[], originOffset]]];
};
IF alignments = NIL THEN ERROR unable[$noAlignments];
n.b. input boxes are in absolute units (boxes), output boxes are in relative units (relBoxes)
but we are still working in real absolute units here
get first referenced box & save it as the initial (temporary) origin
tempBox ← MathBox.GetBox[alignments.first.hAttach.tag, boxes];
relBoxes ← CONS[MathBox.ChangeOffset[tempBox, [0.0, 0.0]], relBoxes];
compute offset locations in absolute (n.b. not relative) units
FOR l:
LIST
OF Alignment2D ← alignments, l.rest
UNTIL l =
NIL
DO
tempBox ← MathBox.GetBox[l.first.tag, boxes];
attachHRelBox ← MathBox.GetBox[l.first.hAttach.tag, relBoxes];
attachVRelBox ← MathBox.GetBox[l.first.vAttach.tag, relBoxes];
align tempBox to attach H&V boxes and save it
relBoxes ← CONS[MathBox.ChangeOffset[tempBox, [AlignHorizontal[tempBox.Extents[], l.first.hAttach.offset1, attachHRelBox, l.first.hAttach.offset2], AlignVertical[tempBox.Extents[], l.first.vAttach.offset1, attachVRelBox, l.first.vAttach.offset2]]], relBoxes];
ENDLOOP;
find extreme edge points to determine myBox size
FOR l:
LIST
OF
BOX ← relBoxes, l.rest
UNTIL l =
NIL
DO
farLeft ← MIN[farLeft, l.first.Offset[].x - l.first.Extents[].leftExtent];
farRight ← MAX[farRight, l.first.Offset[].x + l.first.Extents[].rightExtent];
farDown ← MIN[farDown, l.first.Offset[].y - l.first.Extents[].descent];
farUp ← MAX[farUp, l.first.Offset[].y + l.first.Extents[].ascent];
ENDLOOP;
set myBox dimensions in REAL units
myBox ← MathBox.MakeBox[$composeMyBox, NIL, other, absolute, [leftExtent: -farLeft, rightExtent: farRight, descent: -farDown, ascent: farUp]];
scale the absolute boxes => relative boxes (extents & offsets)
scaleVec ← [1 / myBox.Width[], 1/ myBox.Height[]];
relBoxes ← MathBox.MapBoxList[relBoxes, LocalScale];
myRelBox ← MathBox.Scale[myBox, scaleVec];
compute relative origin location
IF hOrigin.tag = $self
THEN {
attachHRelBox ← myRelBox;
}
ELSE {
attachHRelBox ← MathBox.GetBox[hOrigin.tag, relBoxes];
};
IF vOrigin.tag = $self
THEN {
attachVRelBox ← myRelBox;
}
ELSE {
attachVRelBox ← MathBox.GetBox[vOrigin.tag, relBoxes];
};
originOffset ← [
AlignHorizontal[dummyPtExtents, [origin], attachHRelBox, hOrigin.offset],
AlignVertical[dummyPtExtents, [origin], attachVRelBox, vOrigin.offset]
];
adjust all relative box offsets by originOffset
relBoxes ← MathBox.MapBoxList[relBoxes, LocalFixOffset];
adjust myRelBox w.r.t new origin
myExtents ← myRelBox.Extents[];
myRelBox ← MathBox.ChangeExtents[myRelBox, [leftExtent: myExtents.leftExtent + originOffset.x, rightExtent: myExtents.rightExtent - originOffset.x, descent: myExtents.descent + originOffset.y, ascent: myExtents.ascent - originOffset.y]];
myBox ← MathBox.Scale[myRelBox, [myBox.Width[], myBox.Height[]]];
RETURN[myBox, relBoxes];
};
ComposeMatrix:
PUBLIC
PROC[nRows, nCols:
NAT, boxes:
LIST
OF
BOX, spaceBox, openSymBox, closeSymBox:
BOX]
RETURNS[
BOX,
LIST
OF
BOX,
BOX,
BOX] ~ {
effects: Performs the layout composition for a generalized matrix.
Returns information expected by Format, preserving input row&col orderings.
type declarations
NatSeq: TYPE ~ REF NatSeqRec;
NatSeqRec:
TYPE ~
RECORD[
elements: SEQUENCE size: NAT OF NAT
];
Element:
TYPE ~
RECORD [
box: BOX ← NIL
];
Row: TYPE ~ REF RowRec;
RowRec:
TYPE ~
RECORD [
elements: SEQUENCE numberOfCols: NAT OF Element
];
Matrix: TYPE ~ REF MatrixRec;
MatrixRec:
TYPE ~
RECORD[
rows: SEQUENCE numberOfRows: NAT OF Row
];
local declatations
tallest: NatSeq ← NEW[NatSeqRec[nRows]];
widest: NatSeq ← NEW[NatSeqRec[nCols]];
a: Matrix;
myBox, myRelBox: BOX;
relBoxes: LIST OF BOX ← NIL;
maxHeight, maxWidth, scaleFactor, yOffset: REAL ← 0.0;
currentRow, currentCol: NAT ← 0;
tempElt: Element;
farLeft, farRight, farUp, farDown: REAL ← 0.0;
scaleVec, originOffset: VEC ← [0.0, 0.0];
myExtents: ImagerFont.Extents;
closeBox, openBox: BOX;
horizSpace: BOX ← spaceBox; -- inter-column spacing
vertSpace: BOX ← spaceBox; -- inter-row spacing
convert boxes into a SEQUENCE (ugh!) matrix type
instantiate matrix "a" (isn't CLU's array[]$create() much nicer, really?)
a ← NEW[MatrixRec[nRows]];
FOR row:
NAT
IN [0..nRows)
DO
a[row] ← NEW[RowRec[nCols]];
ENDLOOP;
FOR elements:
LIST
OF
BOX ← boxes, elements.rest
UNTIL elements =
NIL
DO
[currentRow, currentCol] ← RowColFromAtom[elements.first.Tag[]];
a[currentRow - 1][currentCol - 1].box ← elements.first;
ENDLOOP;
mark tallest elt in each row
FOR r:
NAT
IN [0..nRows)
DO
tallest[r] ← 0;
maxHeight ← 0.0;
FOR c:
NAT
IN [0..nCols)
DO
IF a[r][c].box = NIL THEN ERROR unable[$missingElement]; -- missing r,c element
IF a[r][c].box.Height[] > maxHeight
THEN {
tallest[r] ← c;
maxHeight ← a[r][c].box.Height[];
};
ENDLOOP;
ENDLOOP;
mark widest elt in each row
FOR c:
NAT
IN [0..nCols)
DO
widest[c] ← 0;
maxWidth ← 0.0;
FOR r:
NAT
IN [0..nRows)
DO
IF a[r][c].box.Width[] > maxWidth
THEN {
widest[c] ← r;
maxWidth ← a[r][c].box.Width[];
};
ENDLOOP;
ENDLOOP;
vertically align tallest elts, row by row
a[0][tallest[0]].box ← MathBox.ChangeOffset[a[0][tallest[0]].box, [0.0, 0.0]]; -- set vOrigin
FOR r:
NAT
IN [1..nRows)
DO
tempElt ← a[r][tallest[r]]; -- just shorthand
align a vertical space between each row
vertSpace ← MathBox.ChangeOffset[vertSpace, [0.0, AlignVertical[vertSpace.Extents[], [top], a[r-1][tallest[r-1]].box, [bottom]]]];
a[r][tallest[r]].box ← MathBox.ChangeOffset[tempElt.box, [0.0, AlignVertical[tempElt.box.Extents[], [top], vertSpace, [bottom]]]];
ENDLOOP;
vertically align all other (shorter) elts row by row
FOR r:
NAT
IN [0..nRows)
DO
FOR c:
NAT
IN [0..nCols)
DO
IF c # tallest[r]
THEN
a[r][c].box ← MathBox.ChangeOffset[a[r][c].box, [0.0, AlignVertical[a[r][c].box.Extents[], [origin], a[r][tallest[r]].box, [origin]]]];
ENDLOOP;
ENDLOOP;
horizontally align widest elts, col by col
a[widest[0]][0].box ← MathBox.ChangeOffset[a[widest[0]][0].box, [0.0, a[widest[0]][0].box.Offset[].y]]; -- set horizontal origin
FOR c:
NAT
IN [1..nCols)
DO
tempElt ← a[widest[c]][c]; -- just shorthand
align a horizSpace between each column
horizSpace ← MathBox.ChangeOffset[horizSpace, [AlignHorizontal[horizSpace.Extents[], [left], a[widest[c-1]][c-1].box, [right]], 0.0]]; -- punt vertical offset
a[widest[c]][c].box ← MathBox.ChangeOffset[tempElt.box, [AlignHorizontal[tempElt.box.Extents[], [left], horizSpace, [right]], tempElt.box.Offset[].y]];
ENDLOOP;
horizontally align all other (narrower) elts col by col
FOR c:
NAT
IN [0..nCols)
DO
FOR r:
NAT
IN [0..nRows)
DO
IF r # widest[c]
THEN
a[r][c].box ← MathBox.ChangeOffset[a[r][c].box, [AlignHorizontal[a[r][c].box.Extents[], [center], a[widest[c]][c].box, [center]], a[r][c].box.Offset[].y]];
ENDLOOP;
ENDLOOP;
now find extreme edge points to determine myBox size
FOR r:
NAT
IN [0..nRows)
DO
FOR c:
NAT
IN [0..nCols)
DO
farLeft ← MIN[farLeft, a[r][c].box.Offset[].x - a[r][c].box.Extents[].leftExtent];
farRight ← MAX[farRight, a[r][c].box.Offset[].x + a[r][c].box.Extents[].rightExtent];
farDown ← MIN[farDown, a[r][c].box.Offset[].y - a[r][c].box.Extents[].descent];
farUp ← MAX[farUp, a[r][c].box.Offset[].y + a[r][c].box.Extents[].ascent];
ENDLOOP;
ENDLOOP;
preliminary "myBox" for aligning open and close symbols
myBox ← MathBox.MakeBox[$composeMatrixBox, NIL, other, absolute, [leftExtent: -farLeft, rightExtent: farRight, descent: -farDown, ascent: farUp]];
magnify (don't allow too much shrinkage - using hardcoded #s here is a kludge,
but I have a plane to catch...) openSymBox and closeSymBox boxes (absolute boxes)
scaleFactor ← MAX[0.25, ((1 + medGap) * (-farDown + farUp)) / openSymBox.Height[]];
openBox ← MathBox.Scale[openSymBox, [scaleFactor, scaleFactor]];
scaleFactor ← MAX[0.25, ((1 + medGap) * (-farDown + farUp)) / closeSymBox.Height[]];
closeBox ← MathBox.Scale[closeSymBox, [scaleFactor, scaleFactor]];
align openBox and closeBox
align using myBox info
openBox ← MathBox.ChangeOffset[openBox, [
AlignHorizontal[openBox.Extents[], [right, medGap], myBox, [left]],
AlignVertical[openBox.Extents[], [center], myBox, [center]]]];
closeBox ← MathBox.ChangeOffset[closeBox, [
AlignHorizontal[closeBox.Extents[], [left, -medGap], myBox, [right]],
AlignVertical[closeBox.Extents[], [center], myBox, [center]]]];
recompute extreme points
farLeft ← openBox.Offset[].x - openBox.Extents[].leftExtent;
farRight ← closeBox.Offset[].x + closeBox.Extents[].rightExtent;
farUp ← openBox.Offset[].y + openBox.Extents[].ascent;
farDown ← openBox.Offset[].y - openBox.Extents[].descent;
myBox ← MathBox.MakeBox[$composeMatrixBox, NIL, other, absolute, [leftExtent: -farLeft, rightExtent: farRight, descent: -farDown, ascent: farUp]];
scale the absolute boxes => relative boxes (extents & offsets)
scaleVec ← [1 / myBox.Width[], 1/ myBox.Height[]];
FOR r:
NAT
IN [0..nRows)
DO
FOR c:
NAT
IN [0..nCols)
DO
scaledBox: BOX ← MathBox.Scale[a[r][c].box, scaleVec];
a[r][c].box ← MathBox.ChangeType[scaledBox, relative];
ENDLOOP;
ENDLOOP;
myRelBox ← MathBox.Scale[myBox, scaleVec];
openBox ← MathBox.Scale[openBox, scaleVec];
openBox ← MathBox.ChangeType[openBox, relative];
closeBox ← MathBox.Scale[closeBox, scaleVec];
closeBox ← MathBox.ChangeType[closeBox, relative];
compute relative origin location
let origin be thru center; find center using dummy alignment extents
yOffset ← AlignVertical[[leftExtent: 0.0, rightExtent: 0.1, descent: 0.0, ascent: 0.1], [origin], openBox, [center]];
originOffset ← [0.0, yOffset];
adjust all relative boxes by origin offset amount
FOR r:
NAT
IN [0..nRows)
DO
FOR c:
NAT
IN [0..nCols)
DO
a[r][c].box ← MathBox.ChangeOffset[a[r][c].box, Vector.Sub[a[r][c].box.Offset[], originOffset]];
ENDLOOP;
ENDLOOP;
openBox ← MathBox.ChangeOffset[openBox, Vector.Sub[openBox.Offset[], originOffset]];
closeBox ← MathBox.ChangeOffset[closeBox, Vector.Sub[closeBox.Offset[], originOffset]];
adjust myRelBox w.r.t new origin
myExtents ← myRelBox.Extents[];
myRelBox ← MathBox.ChangeExtents[myRelBox, [leftExtent: myExtents.leftExtent + originOffset.x, rightExtent: myExtents.rightExtent - originOffset.x, descent: myExtents.descent + originOffset.y, ascent: myExtents.ascent - originOffset.y]];
myBox ← MathBox.Scale[myRelBox, [myBox.Width[], myBox.Height[]]];
reconstruct LIST OF BOX for return value (from MATRIX sequence structure)
relBoxes ← NIL; -- cons up new list from matrix a
FOR r:
NAT
IN [0..nRows)
DO
FOR c:
NAT
IN [0..nCols)
DO
relBoxes ← CONS[a[r][c].box, relBoxes];
ENDLOOP;
ENDLOOP;
RETURN[myBox, relBoxes, openBox, closeBox];
};