<> <> DIRECTORY MathBox, MathTypes USING [Style], Imager USING [Context], ImagerFont USING [Extents], Vector USING [VEC, Sub], Rope USING [ROPE, Find, Substr, Cat], Convert USING [RopeFromAtom, AtomFromRope, CardFromRope, RopeFromCard], MathRules; MathRulesImpl: CEDAR PROGRAM IMPORTS MathBox, Vector, Convert, Rope EXPORTS MathRules ~ BEGIN <> <<>> VEC: TYPE ~ Vector.VEC; ROPE: TYPE ~ Rope.ROPE; BOX: TYPE ~ MathBox.BOX; Style: TYPE ~ MathTypes.Style; AtomBoxProc: TYPE ~ MathRules.AtomBoxProc; AtomPaintProc: TYPE ~ MathRules.AtomPaintProc; CompoundBoxProc: TYPE ~ MathRules.CompoundBoxProc; CompositionProc: TYPE ~ MathRules.CompositionProc; Alignment2D: TYPE ~ MathRules.Alignment2D; Alignment: TYPE ~ MathRules.Alignment; Offset: TYPE ~ MathRules.Offset; TaggedOffset: TYPE ~ MathRules.TaggedOffset; Size: TYPE ~ MathRules.Size; <> smallGap: REAL = 0.05; medGap: REAL = 0.1; bigGap: REAL = 0.25; matrixGap: REAL = 0.4; <> AlignHorizontal: PUBLIC PROC[box1: ImagerFont.Extents, offset1: Offset, box2: BOX, offset2: Offset] RETURNS[REAL] ~ { <> << Returns x offset for box1.>> displacement1, displacement2: REAL _ 0.0; SELECT offset1.wrt FROM left, bottom => displacement1 _ offset1.pos; right, top => displacement1 _ offset1.pos + 1.0; origin => displacement1 _ offset1.pos + (box1.leftExtent / (box1.leftExtent + box1.rightExtent)); center => displacement1 _ offset1.pos + 0.5; ENDCASE => ERROR; SELECT offset2.wrt FROM left, bottom => displacement2 _ offset2.pos; right, top => displacement2 _ offset2.pos + 1.0; origin => displacement2 _ offset2.pos + (box2.Extents[].leftExtent / box2.Width[]); center => displacement2 _ offset2.pos + 0.5; ENDCASE => ERROR; RETURN[MathBox.AlignHorizontal[box1, displacement1, box2, displacement2]]; }; <<>> AlignVertical: PUBLIC PROC[box1: ImagerFont.Extents, offset1: Offset, box2: BOX, offset2: Offset] RETURNS[REAL] ~ { <> << Returns y offset for box1.>> displacement1, displacement2: REAL; SELECT offset1.wrt FROM left, bottom => displacement1 _ offset1.pos; right, top => displacement1 _ offset1.pos + 1.0; origin => displacement1 _ offset1.pos + (box1.descent / (box1.descent + box1.ascent)); center => displacement1 _ offset1.pos + 0.5; ENDCASE => ERROR; SELECT offset2.wrt FROM left, bottom => displacement2 _ offset2.pos; right, top => displacement2 _ offset2.pos + 1.0; origin => displacement2 _ offset2.pos + (box2.Extents[].descent / box2.Height[]); center => displacement2 _ offset2.pos + 0.5; ENDCASE => ERROR; RETURN[MathBox.AlignVertical[box1, displacement1, box2, displacement2]]; }; <> Compose: PUBLIC PROC[boxes: LIST OF BOX, alignments: LIST OF Alignment2D, hOrigin, vOrigin: TaggedOffset] RETURNS[BOX, LIST OF BOX] ~ { <> << Returns information expected from a CompositionProc.>> <<>> <> 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 < ERROR unable>> <> 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]; <> <> <> tempBox _ MathBox.GetBox[alignments.first.hAttach.tag, boxes]; relBoxes _ CONS[MathBox.ChangeOffset[tempBox, [0.0, 0.0]], relBoxes]; <> 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]; <> 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; <> 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; <> myBox _ MathBox.MakeBox[$composeMyBox, NIL, other, absolute, [leftExtent: -farLeft, rightExtent: farRight, descent: -farDown, ascent: farUp]]; < relative boxes (extents & offsets)>> scaleVec _ [1 / myBox.Width[], 1/ myBox.Height[]]; relBoxes _ MathBox.MapBoxList[relBoxes, LocalScale]; myRelBox _ MathBox.Scale[myBox, scaleVec]; <> 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] ]; <> relBoxes _ MathBox.MapBoxList[relBoxes, LocalFixOffset]; <> 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]; }; AtomFromRowCol: PUBLIC PROC[row, col: NAT] RETURNS[ATOM] ~ { <> <<>> rowRope: ROPE _ Convert.RopeFromCard[row]; colRope: ROPE _ Convert.RopeFromCard[col]; RETURN[Convert.AtomFromRope[Rope.Cat["r", rowRope, "c", colRope]]]; }; RowColFromAtom: PUBLIC PROC[rc: ATOM] RETURNS[NAT, NAT] ~ { <> <> << SIGNALS badFormat if rc does not conform to the format $r#c#>> << >> <> rcRope: ROPE _ Convert.RopeFromAtom[from: rc, quote: FALSE]; rPos: INT _ Rope.Find[rcRope, "r"]; cPos: INT _ Rope.Find[rcRope, "c"]; row, col: NAT _ 0; IF (cPos = -1) OR (rPos = -1) OR (cPos < rPos) THEN ERROR badFormat; row _ Convert.CardFromRope[Rope.Substr[rcRope, rPos+1, (cPos - (rPos+1))]]; col _ Convert.CardFromRope[Rope.Substr[rcRope, cPos+1]]; RETURN[row, col]; }; ComposeMatrix: PUBLIC PROC[nRows, nCols: NAT, boxes: LIST OF BOX, spaceBox, openSymBox, closeSymBox: BOX] RETURNS[BOX, LIST OF BOX, BOX, BOX] ~ { <> << Returns information expected by Format, preserving input row&col orderings.>> <<>> <> <<>> 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 ]; <> 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 <<>> <> <> 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; <> 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; <> 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; <> 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 <> 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; <> 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; <> 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 <> 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; <> 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; <> 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; <> myBox _ MathBox.MakeBox[$composeMatrixBox, NIL, other, absolute, [leftExtent: -farLeft, rightExtent: farRight, descent: -farDown, ascent: farUp]]; <<>> <> <> 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]]; <> <<>> <> 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]]]]; <> 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]]; < 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]; <> <> yOffset _ AlignVertical[[leftExtent: 0.0, rightExtent: 0.1, descent: 0.0, ascent: 0.1], [origin], openBox, [center]]; originOffset _ [0.0, yOffset]; <> 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]]; <> 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[]]]; <> 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]; }; <> ComputeSize: PUBLIC PROC[base, adjustment: Size] RETURNS[Size] ~ { <> <<>> SELECT base FROM normal => RETURN[adjustment]; script => { SELECT adjustment FROM normal => RETURN[script]; script, scriptscript => RETURN[scriptscript]; big => RETURN[normal]; ENDCASE => ERROR; }; scriptscript => { SELECT adjustment FROM normal, script, scriptscript => RETURN[scriptscript]; big => RETURN[script]; ENDCASE => ERROR; }; big => { SELECT adjustment FROM normal, big => RETURN[big]; script => RETURN[normal]; scriptscript => RETURN[script]; ENDCASE => ERROR; }; ENDCASE => ERROR; }; VecFromSize: PUBLIC PROC[size: Size] RETURNS[VEC] ~ { <> <<>> SELECT size FROM normal => RETURN[[1.0, 1.0]]; script => RETURN[[0.6, 0.6]]; scriptscript => RETURN[[0.36, 0.36]]; big => RETURN[[1.5, 1.5]]; ENDCASE => ERROR; }; <> unable: PUBLIC ERROR[reason: ATOM] = CODE; badFormat: PUBLIC ERROR = CODE; END.