MathBoxTreesImpl.mesa
Copyright Ó 1989 by Xerox Corporation. All rights reserved.
Arnon, September 7, 1989 10:21:56 am PDT
DIRECTORY
MathBoxTree,
MathExpr,
MathBox,
MathConstructors,
MathDB,
MathTypes,
MathRules,
Convert USING [RopeFromAtom],
Rope USING [ROPE, Length, Find, Replace, Cat],
Imager USING [Context, DoSaveAll, MaskRectangle, MaskStrokeTrajectory, SetColor, SetStrokeWidth],
ImagerBackdoor USING [invert],
ImagerFont USING [Extents, Find, Font, RopeBoundingBox, Scale],
ImagerPath USING [LineToX, LineToY, MoveTo],
Vector2 USING [InlineAdd],
XRope;
MathBoxTreesImpl: CEDAR PROGRAM
IMPORTS MathBox, MathExpr, MathDB, MathConstructors, Imager, ImagerBackdoor, ImagerFont, ImagerPath, MathRules, Rope, Convert, Vector2, XRope
EXPORTS MathBoxTree ~
BEGIN
Type Abbreviations from Imported Interfaces
ROPE: TYPE ~ Rope.ROPE;
BOX: TYPE ~ MathBox.BOX;
EXPR: TYPE ~ MathExpr.EXPR;
AtomEXPR: TYPE ~ MathExpr.AtomEXPR;
CompoundEXPR: TYPE ~ MathExpr.CompoundEXPR;
MatrixEXPR: TYPE ~ MathExpr.MatrixEXPR;
Style: TYPE ~ MathTypes.Style;
Argument: TYPE ~ MathExpr.Argument;
Symbol: TYPE ~ MathExpr.Symbol;
AtomClass: TYPE ~ MathExpr.AtomClass;
AtomFlavor: TYPE ~ MathExpr.AtomFlavor;
CompoundClass: TYPE ~ MathExpr.CompoundClass;
MatrixClass: TYPE ~ MathExpr.MatrixClass;
Size: TYPE ~ MathRules.Size;
Selection: TYPE ~ MathBoxTree.Selection;
ReplacePair: TYPE ~ MathBoxTree.ReplacePair;
Private Type Rep
BoxTreeDataRep: TYPE ~ RECORD [
SELECT type:* FROM
pureBoxTree => [ -- display subtree of a leafObject; "pure" since just Boxes, no Objects.
No subtree is selectable (only Objects are selectable)
root: Box, -- cannot be NIL since visibility/non-visibility is a property of Objects (i.e. only of ancestors of pureBoxTrees)
parent: BoxTreeData, -- cannot be NIL since (root of) a pureBoxTree is always a child either of some pureBoxTree, or of some leafObject.
children: LIST OF BoxTreeData ← NIL -- All children of a pureBoxTree are pureBoxTrees.
Leaf Box iff children = NIL
],
leafObject => [ -- i.e. a "packed Object", i.e. no subcomponent selectable
root: Box ← NIL, -- object visible iff root # NIL
parent: BoxTreeData ← NIL, -- root is an absolute Box iff parent = NIL (otherwise relative)
children: LIST OF BoxTreeData ← NIL -- All children of a leafObject are pureBoxTrees.
Root is a leaf Box iff children = NIL
object: Object,
],
This Object (self.object) selectable iff visible
Nothing inside self.root, e.g. no subBoxTree or subObject, is selectable
self.root and self.children created by the EltDisplay proc of (the domain) self.object.class, with either no "unpacking" possible (e.g. ground domain), or a passed-in environment specifying "no unpacking".
interiorObject => [ -- "unpacked" (to at least one level) Object display
root: Box ← NIL, -- bounding, i.e. outermost, box for the display of self. All subBoxes assumed to be contained in self.args and self.opDisplay.
Object visible iff root # NIL
parent: BoxTreeData ← NIL, -- root is an absolute Box iff parent = NIL (otherwise relative)
op: ATOM,
domain: Object, -- Domain or View
args: LIST OF BoxTreeData, -- cannot be NIL since this is display of an unpacked Object
assert[ elementOf[op, domain.methods] ]
assert[ resolvableTo[args, opMethod.dynamicSignature] ]
assert[ Object[self] = opMethod[args] ]
opDisplay: LIST OF BoxTreeData ← NIL
selecting one of these BoxTreeDatas implies that (logically) all are selected (user feedback should so indicate?). Editing will take place on just the one actually selected, however.
All of these BoxTreeDatas should belong to domain "OperatorGlyphs". They should only be replacable by an element of the same domain. If that is done, this determines a new candidate self.op. We then proceed top-down to check the assertions above, and reformat self. I.e. we check elementOf[op, domain.methods
It is not assumed that the value of (the data of) each arg here is the same BoxTreeData one would get by application of the EltDisplay proc of the domain of the Object[] of that arg (with the same environment, e.g. unpacking), although at some point in the bottom-up creation of self that BoxTreeData was created; it may be modified by "postprocessing" that we as part of the formatting of this interiorObject
ENDCASE
The basic editing cycle is: select some (selectable, i.e. Object) BoxTree, possibly change its domain, possibly change its opDisplay (and so its op), possibly replace it with a new Object, possibly copy it somewhere, possibly do nothing; update Object of which selection Object is a part; identify new selection. Replacement with a new Object overrides other changes. The update process first updates the selection Object. If this is a newly inserted Object, nothing to do. If domain and/or op changed, then verify the assertions:
assert[ elementOf[op, domain.methods] ]
assert[ resolvableTo[args, opMethod.dynamicSignature] ]
assert[ Object[self] = opMethod[args] ]
and thereby obtain a new BoxTree to replace the originally selected BoxTree. If failure occurs in here, then user is asked what to do - he can either undo the new domain, or delete self. If the latter, then before the selection can possibly be released, some specification of a new Object to replace self must be made. If no such Object is provided, then either a default Object of the new domain, or an Exprs placeholder, is used.
We now have a new BoxTree, i.e. a newSelf. We format it.
If its domain is the same as old self, and if its bounding box fits inside that of old self, then we paint it (and nothing else) and done.
If domain is the same as old self, but newSelf's bounding box doesn't fit inside that of old self (and we decide not to simply rescale it to fit), then we resume an EltDisplay[Object[root]] computation at this point (i.e. so far we can use the existing formattings of siblings, but we invoke a fresh format command ont the parent).
If the domain of newSelf is different from old domain, we startup ("continue") an Object[root] computation from this point.
First, we check whether newSelf is compatible with the appropriate element of parent.opMethod.dynamicSignature. If so, we (possibly) convert newSelf, format it, and proceed as above, depending on whether its bounding box fits inside the old one.
If newSelf's domain is not compatible with the appropriate element of parent.opMethod.dynamicSignature, then we resume an Object[root] computation at this point. E.g. we may lookup parent.op in some other domain, or try to resolve the domains of newSelf and its siblings, etc. If the Object[root] computation succeeds, then the (possibly necessary) reformatting of BoxTrees will occur all the way up to the root, and when we get there and finish, we repaint the entire root Object. A new selection is set (probably at newSelf).
If the Object[root] computation fails at some point, we report the failure and do an automatic undo of this editing cycle.
Note: EltDisplay methods are assumed to produce a formatted result. I.e. the old Meddle Format step is part of any EltDisplay method.
];
BoxTree: TYPE ~ REF BoxTreeRep; -- inside impl module, use rep
Display Expression Constructors
MakeAtomicBoxTree: PUBLIC PROC[tag: ATOM, class: AtomClass, value: ROPE, relBox, absBox: BOX, parent: BoxTree ← NIL] RETURNS[BoxTree] ~ {
effects: Constructs and returns a new atomic display expression.
RETURN[NEW[BoxTreeRep ← [tag: tag, relativeBox: relBox, absoluteBox: absBox, parent: parent, expression: NEW[AtomDisplayObjRep ← [atom[class: class, value: value]]]]]];
};
MakeCompoundBoxTree: PUBLIC PROC[tag: ATOM, class: CompoundClass, subExprs: LIST OF BoxTree, relBox, absBox: BOX, parent: BoxTree ← NIL] RETURNS[BoxTree] ~ {
effects: Constructs and returns a new compound display expression.
RETURN[NEW[BoxTreeRep ← [tag: tag, relativeBox: relBox, absoluteBox: absBox, parent: parent, expression: NEW[CompoundDisplayObjRep ← [compound[class: class, subExprs: subExprs]]]]]];
};
MakeMatrixBoxTree: PUBLIC PROC[tag: ATOM, class: MatrixClass, nRows, nCols: INT, elements: LIST OF BoxTree, relBox, absBox: BOX, openSym, closeSym, space: BoxTree, parent: BoxTree ← NIL]
RETURNS[BoxTree] ~ {
effects: Constructs and returns a new matrix display expression.
RETURN[NEW[BoxTreeRep ← [tag: tag, relativeBox: relBox, absoluteBox: absBox, parent: parent, expression: NEW[MatrixDisplayObjRep ← [matrix[class: class, nRows: nRows, nCols: nCols, elements: elements, openSym: openSym, closeSym: closeSym, space: space]]]]]];
};
Parse/UnParse Routines
ASRopeFromBoxTree: PUBLIC PROC[expr: BoxTree, flavor: ATOM ← $AS] RETURNS[ROPE] ~ {
effects: Returns a ROPE in selected format
WITH expr.expression SELECT FROM
a: AtomDisplayObj => SELECT flavor FROM
$Reduce => IF a.class.cvtReduceRope # NIL THEN RETURN[a.class.cvtReduceRope[a.value]] ELSE RETURN[a.value];
$SMP => IF a.class.cvtSMPRope # NIL THEN RETURN[a.class.cvtSMPRope[a.value]] ELSE RETURN[a.value];
ENDCASE => IF a.class.cvtASRope # NIL THEN RETURN[a.class.cvtASRope[a.value]] ELSE RETURN[a.value];
c: CompoundDisplayObj => {
template: ROPESELECT flavor FROM
$Reduce => c.class.cvtReduce, -- template for return value
$SMP => c.class.cvtSMP, -- template for return value
ENDCASE => c.class.cvtAS; -- template for return value
argName: ROPENIL;
position: INT ← -1;
IF Rope.Length[template] < 1 THEN RETURN[""];
recursively copy subexpressions
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
argName ← Convert.RopeFromAtom[l.first.name];
position ← Rope.Find[template, argName];
IF position # -1 THEN template ← Rope.Replace[base: template, start: position, len: Rope.Length[argName], with: ASRopeFromBoxTree[GetBoxTree[l.first.name, c.subExprs], flavor] ];
ENDLOOP;
Hack for complexes with negative imaginary part. December 11, 1986 11:24:18 am PST
IF c.class.name = $complex THEN {
position ← Rope.Find[template, "+ -"];
IF position # -1 THEN template ← Rope.Replace[base: template, start: position, len: 3, with: "- "];
};
RETURN[template];
};
m: MatrixDisplayObj => {
Element: TYPE ~ RECORD [
expr: BoxTree ← 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
];
a: Matrix;
currentRow, currentCol: NAT;
result: ROPENIL; -- return value
convert boxes into a SEQUENCE (ugh!) matrix type
instantiate matrix "a" (isn't CLU's array[]$create() much nicer, really?)
a ← NEW[MatrixRec[m.nRows]];
FOR row:INT IN [0..m.nRows) DO
a[row] ← NEW[RowRec[m.nCols]];
ENDLOOP;
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO
[currentRow, currentCol] ← MathRules.RowColFromAtom[l.first.tag];
a[currentRow - 1][currentCol - 1].expr ← l.first;
ENDLOOP;
SELECT m.class.name FROM
$matrix => {
result ← "List[ ";
FOR r:INT IN [0..m.nRows) DO
result ← Rope.Cat[result, "List[ "]; -- row start char
FOR c:INT IN [0..m.nCols) DO
result ← Rope.Cat[result, ASRopeFromBoxTree[a[r][c].expr, flavor]];
add a comma if not last col in row
IF c # (m.nCols - 1) THEN result ← Rope.Cat[result, ", "]; -- col separator
ENDLOOP;
result ← Rope.Cat[result, " ]"]; -- row end char
add a comma if not last row in matrix
IF r # (m.nRows - 1) THEN result ← Rope.Cat[result, ", "]; -- row separator
ENDLOOP;
result ← Rope.Cat[result, " ]"]; -- end with matrix end char
};
$vector, $sequence, $set, $point => {
result ← "List[ "; -- should select set, sequence, etc.
IF m.nRows = 1 THEN { -- row vector
FOR c: INT IN [0..m.nCols) DO
result ← Rope.Cat[result, ASRopeFromBoxTree[a[0][c].expr, flavor]];
add a comma if not last col in vector
IF c # (m.nCols - 1) THEN result ← Rope.Cat[result, ", "]; -- col separator
ENDLOOP;
}
ELSE { -- column vector
FOR r: INT IN [0..m.nRows) DO
result ← Rope.Cat[result, ASRopeFromBoxTree[a[r][0].expr, flavor]];
add a comma if not last row in vector
IF r # (m.nRows - 1) THEN result ← Rope.Cat[result, ", "]; -- row separator
ENDLOOP;
};
result ← Rope.Cat[result, " ]"];
};
ENDCASE => result ← "unknownMatrixType";
RETURN[result];
};
ENDCASE => ERROR;
};
BoxTreeFromExpr: PUBLIC PROC[expr: EXPR] RETURNS[BoxTree] ~ {
effects: Constructs and returns a new display expression from expr.
All Display formatting information is set to default values.
local declarations
myBoxTree, tempBoxTree: BoxTree;
emptyAtom: ATOM ← $expr; -- generic identifier
emptyRelBox: BOX ← MathBox.MakeBox[emptyAtom, NIL, other, relative];
emptyAbsBox: BOX ← MathBox.MakeBox[emptyAtom, NIL, other, absolute];
SELECT expr.GetType[] FROM
atom => {
a: AtomEXPR ← expr.GetAtomExpr[];
should really combine passed in style with local atomic style
RETURN[MakeAtomicBoxTree[emptyAtom, MathExpr.GetAtomClass[a], MathExpr.GetValue[a], emptyRelBox, emptyAbsBox]];
};
compound => {
c: CompoundEXPR ← expr.GetCompoundExpr[];
compClass: CompoundClass ← MathExpr.GetCompoundClass[c];
recursively generate displayexprs for all subexpressions (arguments & symbols)
displaySubExprs: LIST OF BoxTree ← NIL; -- cons up list of display expressions
generate all argument subexpressions
FOR l: LIST OF Argument ← compClass.arguments, l.rest UNTIL l = NIL DO
tempBoxTree ← BoxTreeFromExpr[MathExpr.GetTaggedExpr[l.first.name, MathExpr.GetSubExprs[c] ! exprNotFound => {ERROR unable[$missingArg]}].expression];
tempBoxTree.tag ← l.first.name; -- set name tag for display expr
displaySubExprs ← CONS[tempBoxTree, displaySubExprs];
ENDLOOP;
generate all symbol subexpressions
FOR l: LIST OF Symbol ← compClass.symbols, l.rest UNTIL l = NIL DO
tempBoxTree ← BoxTreeFromExpr[MathExpr.GetTaggedExpr[l.first.name, MathExpr.GetSubExprs[c] ! exprNotFound => {ERROR unable[$missingSym]}].expression];
tempBoxTree.tag ← l.first.name; -- set name tag for display expr
displaySubExprs ← CONS[tempBoxTree, displaySubExprs];
ENDLOOP;
construct return value now so can set "parent" component of subexpressions
myBoxTree ← MakeCompoundBoxTree[emptyAtom, MathExpr.GetCompoundClass[c], displaySubExprs, emptyRelBox, emptyAbsBox];
set parents of display subexpressions
FOR l: LIST OF BoxTree ← displaySubExprs, l.rest UNTIL l = NIL DO
l.first.parent ← myBoxTree; -- set parent
ENDLOOP;
RETURN[myBoxTree];
};
matrix => {
m: MatrixEXPR ← expr.GetMatrixExpr[];
elts: LIST OF MathExpr.TaggedMathExpr ← MathExpr.GetMatrixElements[m];
class: MatrixClass ← MathExpr.GetMatrixClass[m];
displayElts, displayEltsPointer: LIST OF BoxTree ← NIL;
nRows, nCols: NAT;
openSym, closeSym, space: BoxTree;
[nRows, nCols] ← MathExpr.GetMatrixSize[m];
convert elements from expr to displayexpr
FOR l: LIST OF MathExpr.TaggedMathExpr ← elts, l.rest UNTIL l = NIL DO
tempBoxTree ← BoxTreeFromExpr[l.first.expression];
tempBoxTree.tag ← l.first.id; -- set name tag for display expr
displayElts ← CONS[tempBoxTree, displayElts];
ENDLOOP;
convert elements from expr to displayexpr -- changed 3/4/87
FOR l: LIST OF MathExpr.TaggedMathExpr ← elts, l.rest UNTIL l = NIL DO
tempBoxTree ← BoxTreeFromExpr[l.first.expression];
tempBoxTree.tag ← l.first.id; -- set name tag for display expr
IF displayElts = NIL THEN displayElts ← displayEltsPointer ← LIST[tempBoxTree] ELSE displayEltsPointer ← displayEltsPointer.rest ← LIST[tempBoxTree];
ENDLOOP;
openSym ← BoxTreeFromExpr[class.openSym];
closeSym ← BoxTreeFromExpr[class.closeSym];
space ← BoxTreeFromExpr[class.space];
construct return value now so can set "parent" component of subexpressions
myBoxTree ← MakeMatrixBoxTree[emptyAtom, class, nRows, nCols, displayElts, emptyRelBox, emptyAbsBox, openSym, closeSym, space];
FOR l: LIST OF BoxTree ← displayElts, l.rest UNTIL l = NIL DO
l.first.parent ← myBoxTree; -- set parent
ENDLOOP;
RETURN[myBoxTree];
};
ENDCASE => ERROR;
};
ExprFromBoxTree: PUBLIC PROC[expr: BoxTree] RETURNS[EXPR] ~ {
effects: Constructs and returns a new EXPR from expr.
2/87 - amounts to just stripping away the formatting info from the BoxTree, i.e. stripping relativeBox, absoluteBox, and parent pointers.
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
RETURN[MathExpr.MakeAtomicExpr[a.class.name, a.value]];
};
c: CompoundDisplayObj => {
local declarations
args: LIST OF MathExpr.TaggedMathExpr ← NIL;
recursively build a list of tagged arg exprs
FOR l: LIST OF BoxTree ← c.subExprs, l.rest UNTIL l = NIL DO
args ← CONS[[l.first.tag, ExprFromBoxTree[l.first]], args];
ENDLOOP;
RETURN[MathExpr.MakeCompoundExpr[c.class.name, args]];
};
m: MatrixDisplayObj => {
local declarations
elts, eltsPointer: LIST OF MathExpr.TaggedMathExpr ← NIL;
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO
elts ← CONS[[l.first.tag, ExprFromBoxTree[l.first]], elts];
ENDLOOP;
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO -- changed 3/4/87
taggedMathExpr: MathExpr.TaggedMathExpr ← [l.first.tag, ExprFromBoxTree[l.first]];
IF elts = NIL THEN elts ← eltsPointer ← LIST[taggedMathExpr] ELSE eltsPointer ← eltsPointer.rest ← LIST[taggedMathExpr];
ENDLOOP;
RETURN[MathExpr.MakeMatrixExpr[m.class.name, m.nRows, m.nCols, elts]];
};
ENDCASE => ERROR;
};
Copy: PUBLIC PROC[expr: BoxTree] RETURNS[BoxTree] ~ {
effects: Returns a new, distinct copy of expr.
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
RETURN[MakeAtomicBoxTree[expr.tag, a.class, a.value, expr.relativeBox, expr.absoluteBox]];
};
c: CompoundDisplayObj => {
local declarations
exprCopy: BoxTree; -- return value
copiedSubExprs: LIST OF BoxTree ← NIL; -- cons up new list of copies
recursively copy subexpressions
FOR l: LIST OF BoxTree ← c.subExprs, l.rest UNTIL l = NIL DO
copiedSubExprs ← CONS[Copy[l.first], copiedSubExprs];
ENDLOOP;
construct return value now so we can set parent pointers for subexprs
exprCopy ← MakeCompoundBoxTree[expr.tag, c.class, copiedSubExprs, expr.relativeBox, expr.absoluteBox];
FOR l: LIST OF BoxTree ← copiedSubExprs, l.rest UNTIL l = NIL DO
l.first.parent ← exprCopy; -- set parent pointers appropriately
ENDLOOP;
RETURN[exprCopy];
};
m: MatrixDisplayObj => {
local declarations
exprCopy: BoxTree; -- return value
copiedElts, copiedEltsPtr: LIST OF BoxTree ← NIL; -- cons up a new list of copied elements
recursively copy elements
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO
copiedElts ← CONS[Copy[l.first], copiedElts];
ENDLOOP;
recursively copy elements -- changed 3/4/87
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO
IF copiedElts = NIL THEN copiedElts ← copiedEltsPtr ← LIST[Copy[l.first] ] ELSE copiedEltsPtr ← copiedEltsPtr.rest ← LIST[Copy[l.first] ];
ENDLOOP;
construct return value now so we can set parent pointers for subexprs
exprCopy ← MakeMatrixBoxTree[expr.tag, m.class, m.nRows, m.nCols, copiedElts, expr.relativeBox, expr.absoluteBox, Copy[m.openSym], Copy[m.closeSym], Copy[m.space]];
FOR l: LIST OF BoxTree ← copiedElts, l.rest UNTIL l = NIL DO
l.first.parent ← exprCopy; -- set parent pointers appropriately
ENDLOOP;
RETURN[exprCopy];
};
ENDCASE => ERROR;
};
Selectors
Tag: PUBLIC PROC[expr: BoxTree] RETURNS[ATOM] ~ {
effects: Returns tag associated with expr.
RETURN[expr.tag];
};
Class: PUBLIC PROC[expr: BoxTree] RETURNS[ATOM] ~ {
effects: Returns class of expr.
WITH expr.expression SELECT FROM
a: AtomDisplayObj => RETURN[a.class.name];
c: CompoundDisplayObj => RETURN[c.class.name];
m: MatrixDisplayObj => RETURN[m.class.name];
ENDCASE => ERROR;
};
GetSubExprs: PUBLIC PROC[expr: BoxTree] RETURNS[LIST OF BoxTree] ~ {
effects: Returns subexpressions for expr.
Returns NIL if no subexpressions exist
WITH expr.expression SELECT FROM
a: AtomDisplayObj => RETURN[NIL]; -- atoms don't have any children
c: CompoundDisplayObj => RETURN[c.subExprs];
m: MatrixDisplayObj => RETURN[m.elements];
ENDCASE => ERROR;
};
AbsBox: PUBLIC PROC[expr: BoxTree] RETURNS[BOX] ~ {
effects: Returns absoluteBox for expr.
RETURN[expr.absoluteBox];
};
Format and Paint
Format: PUBLIC PROC[expr: BoxTree, size: Size] RETURNS[BOX] ~ {
fontName: ROPE ~ "Xerox/XC1-2-2/TimesRoman-italic";
scale: REAL ~ 1.0;
font: ImagerFont.Font ~ ImagerFont.Scale[ImagerFont.Find[fontName], scale];
a: ImagerFont.Extents ~ ImagerFont.RopeBoundingBox[font, "="]; -- for ``axis'' height
axisHeight: REAL ~ (a.ascent-a.descent)/2;
innerBox: BOX ← InnerFormat[expr, size];
extents: ImagerFont.Extents ← innerBox.Extents;
Were using axis as baseline, so must shift up here
extents.ascent ← extents.ascent + axisHeight;
extents.descent ← extents.descent - axisHeight;
innerBox ← MathBox.ChangeExtents[innerBox, extents];
innerBox ← MathBox.ChangeOffset[innerBox, innerBox.Offset[].InlineAdd[[0.0, axisHeight]]];
RETURN[innerBox];
};
InnerFormat: PROC[expr: BoxTree, size: Size] RETURNS[BOX] ~ {
modifies: expr
effects: expr is updated into a "paintable" form (dsa - perhaps in the sense that all subExprs have been given correct relative bounding boxes)
Returns a bounding box for expr.
SIGNALS unable[reason: ATOM] if InnerFormatting can not be completed.
local declarations
subExprBoxes: LIST OF BOX;
myBox, tempBox: BOX;
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
RETURN[MathBox.Scale[MathBox.MakeBox[tag: a.class.name, aliases: NIL, formatClass: a.class.formatClass, type: absolute, extents: a.class.boxRule[a.value, a.class.style]], MathRules.VecFromSize[size]]];
};
c: CompoundDisplayObj => {
recursively format all subexpressions (arguments & symbols)
subExprBoxes ← NIL; -- cons up list of subexpression boxes
format all argument subexpressions
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
tempBox ← InnerFormat[GetBoxTree[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingArg]}], MathRules.ComputeSize[base: size, adjustment: l.first.size]];
subExprBoxes ← CONS[MathBox.MakeBox[l.first.name, l.first.aliases, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[]], subExprBoxes];
ENDLOOP;
format all symbol subexpressions
FOR l: LIST OF Symbol ← c.class.symbols, l.rest UNTIL l = NIL DO
tempBox ← InnerFormat[GetBoxTree[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingSym]}], MathRules.ComputeSize[base: size, adjustment: l.first.size]];
subExprBoxes ← CONS[MathBox.MakeBox[l.first.name, l.first.aliases, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[]], subExprBoxes];
ENDLOOP;
compute corrected boxes using sizing constraints
subExprBoxes ← c.class.boxRule[subExprBoxes];
compose layout for boxes using alignment constraints
[myBox, subExprBoxes] ← c.class.compBox[subExprBoxes];
set format class for mybox
myBox ← MathBox.ChangeFormatClass[myBox, c.class.formatClass];
set relative boxes of display subexpressions
FOR l: LIST OF BoxTree ← c.subExprs, l.rest UNTIL l = NIL DO
IF l.first.relativeBox.Type # relative THEN ERROR unable[$wrongBoxType];
l.first.relativeBox ← MathBox.GetBox[l.first.tag, subExprBoxes ! MathBox.boxNotFound => {ERROR unable[$boxNotFound]}]; -- set box
ENDLOOP;
RETURN[myBox];
};
m: MatrixDisplayObj => {
local declarations
elementBoxes, elementBoxesPtr: LIST OF BOXNIL;
tempBox, spaceBox, openSymBox, closeSymBox: BOX;
recursively format all matrix elements
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO
tempBox ← InnerFormat[l.first, MathRules.ComputeSize[base: size, adjustment: script]];
elementBoxes ← CONS[MathBox.MakeBox[l.first.tag, NIL, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[]], elementBoxes];
ENDLOOP;
recursively format all matrix elements
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO
tempBox ← InnerFormat[l.first, size];
IF elementBoxes = NIL THEN
elementBoxes ← elementBoxesPtr ← LIST[ MathBox.MakeBox[l.first.tag, NIL, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[] ] ]
ELSE
elementBoxesPtr ← elementBoxesPtr.rest ← LIST[ MathBox.MakeBox[l.first.tag, NIL, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[] ] ];
ENDLOOP;
format openSym, closeSym, and space
spaceBox ← InnerFormat[m.space, MathRules.ComputeSize[base: size, adjustment: script]];
openSymBox ← InnerFormat[m.openSym, normal];
closeSymBox ← InnerFormat[m.closeSym, normal];
compose layout for matrix by using alignment constraints
[myBox, elementBoxes, m.openSym.relativeBox, m.closeSym.relativeBox] ← MathRules.ComposeMatrix[m.nRows, m.nCols, elementBoxes, spaceBox, openSymBox, closeSymBox];
set format class for mybox
myBox ← MathBox.ChangeFormatClass[myBox, m.class.formatClass];
set relative boxes of matrix elements
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO
IF l.first.relativeBox.Type # relative THEN ERROR unable[$wrongBoxType];
l.first.relativeBox ← MathBox.GetBox[l.first.tag, elementBoxes ! MathBox.boxNotFound => {ERROR unable[$boxNotFound]}]; -- set box
ENDLOOP;
RETURN[myBox];
};
ENDCASE => ERROR;
};
Paint: PUBLIC PROC[expr: BoxTree, context: Imager.Context, absBox: BOX,
selections: LIST OF Selection] ~ {
requires: absBox is an absolute BOX
modifies: expr
effects: Displays expr in context, bounded by absBox.
Updates absolute bounding boxes (viewer coords).
SIGNALS unable[reason: ATOM] if Painting cannot be completed.
local declarations
currentBoxTree: BoxTree;
update absolute coordinates
IF absBox.Type # absolute THEN ERROR unable[$wrongBoxType];
expr.absoluteBox ← absBox;
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
a.class.paintRule[a.value, a.class.style, context, absBox];
};
c: CompoundDisplayObj => {
paint all subexpressions
paint arguments
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
currentBoxTree ← GetBoxTree[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingArg]}];
Paint[currentBoxTree, context, MathBox.RelToAbsBox[currentBoxTree.relativeBox, absBox], selections];
ENDLOOP;
paint symbols
FOR l: LIST OF Symbol ← c.class.symbols, l.rest UNTIL l = NIL DO
currentBoxTree ← GetBoxTree[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingArg]}];
Paint[currentBoxTree, context, MathBox.RelToAbsBox[currentBoxTree.relativeBox, absBox], selections];
ENDLOOP;
};
m: MatrixDisplayObj => {
paint all matrix elements
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO
Paint[l.first, context, MathBox.RelToAbsBox[l.first.relativeBox, absBox], selections];
ENDLOOP;
paint open & close symbols
Paint[m.openSym, context, MathBox.RelToAbsBox[m.openSym.relativeBox, absBox], selections];
Paint[m.closeSym, context, MathBox.RelToAbsBox[m.closeSym.relativeBox, absBox], selections];
};
ENDCASE => ERROR;
if selected, highlight region appropriately, using a border and optional fill
FOR l: LIST OF Selection ← selections, l.rest UNTIL l = NIL DO
IF (expr = l.first.expr) THEN {
HighlightProc: PROC ~ {
fat: REAL ← 3;
set proper color depending on selection
context.SetColor[l.first.color];
IF NOT l.first.borderOnly
THEN {
highlight whole region
Imager.MaskRectangle[context, [
x: absBox.Offset[].x - absBox.Extents[].leftExtent,
y: absBox.Offset[].y - absBox.Extents[].descent,
w: absBox.Width[],
h: absBox.Height[]]];
put thin border around filled region
fat ← 1;
context.SetColor[ImagerBackdoor.invert];
};
highlight border
Imager.SetStrokeWidth[context, 2.0*fat];
Imager.MaskStrokeTrajectory[context,
ImagerPath.LineToX[
ImagerPath.LineToY[
ImagerPath.LineToX[
ImagerPath.LineToY[
ImagerPath.MoveTo[
[ absBox.Offset[].x - (absBox.Extents[].leftExtent + fat),
absBox.Offset[].y - (absBox.Extents[].descent + fat)]],
absBox.Offset[].y + (absBox.Extents[].ascent + fat)],
absBox.Offset[].x + (absBox.Extents[].rightExtent + fat)],
absBox.Offset[].y - (absBox.Extents[].descent + fat)],
absBox.Offset[].x - (absBox.Extents[].leftExtent + fat)],
TRUE];
};
Imager.DoSaveAll[context, HighlightProc];
};
ENDLOOP;
};
List Operations
GetBoxTree: PUBLIC PROC[tag: ATOM, exprs: LIST OF BoxTree] RETURNS[BoxTree] ~ {
effects: Returns the BoxTree in exprs associated with tag.
SIGNALS exprNotFound if no association exists.
cdr down list looking for tag
FOR l: LIST OF BoxTree ← exprs, l.rest UNTIL l = NIL DO
IF l.first.tag = tag THEN RETURN[l.first];
ENDLOOP;
not found, so signal error
ERROR exprNotFound;
};
Selection Operations
BoxTreeFromCoords: PUBLIC PROC[expr: BoxTree, x, y: REAL] RETURNS[BoxTree] ~ {
effects: Returns the subexpression associated with coordinates [x, y] in expression expr.
SIGNALS noSelection if no association exists.
IF expr = NIL THEN ERROR noSelection;
IF ~MathBox.Inside[expr.absoluteBox, x, y] THEN ERROR noSelection;
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
IF Selectable[expr] THEN RETURN[expr] ELSE ERROR noSelection;
};
c: CompoundDisplayObj => {
see if selection can be further narrowed
FOR l: LIST OF BoxTree ← c.subExprs, l.rest UNTIL l = NIL DO
IF MathBox.Inside[l.first.absoluteBox, x, y] THEN
RETURN[BoxTreeFromCoords[l.first, x, y ! noSelection => CONTINUE]];
ENDLOOP;
if above loop does not return, then can't narrow selection any more
IF Selectable[expr] THEN RETURN[expr] ELSE ERROR noSelection;
};
m: MatrixDisplayObj => {
see if selection can be further narrowed
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO
IF MathBox.Inside[l.first.absoluteBox, x, y] THEN
RETURN[BoxTreeFromCoords[l.first, x, y ! noSelection => CONTINUE]];
ENDLOOP;
if above loop does not return, then can't narrow selection any more
IF Selectable[expr] THEN RETURN[expr] ELSE ERROR noSelection;
};
ENDCASE => ERROR;
};
Selectable: PUBLIC PROC[expr: BoxTree] RETURNS[BOOL] ~ {
effects: Returns TRUE iff expr is selectable.
IF expr = NIL THEN RETURN[FALSE];
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
IF a.class.flavor = symbol THEN RETURN[FALSE] ELSE RETURN[TRUE];
};
c: CompoundDisplayObj => RETURN[TRUE];
m: MatrixDisplayObj => RETURN[TRUE];
ENDCASE => ERROR;
};
SelectableParent: PUBLIC PROC[expr: BoxTree] RETURNS[BoxTree] ~ {
effects: Returns the parent (enclosing expression) of expr.
SIGNALS noParent if no selectable parent exists.
complain if no expr or no parent
IF (expr = NIL) OR (expr.parent) = NIL THEN ERROR noSelection;
RETURN[expr.parent];
};
SelectableChild: PUBLIC PROC[expr: BoxTree] RETURNS[BoxTree] ~ {
effects: Returns a child expressions (subexpression) for expr.
SIGNALS noSelection if no selectable child exists.
local declarations
childExpr: BoxTree ← NIL;
IF expr = NIL THEN ERROR noSelection;
WITH expr.expression SELECT FROM
a: AtomDisplayObj => ERROR noSelection; -- atom has no children
c: CompoundDisplayObj => {
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
keep looking for selectable child
childExpr ← GetBoxTree[l.first.name, c.subExprs];
IF Selectable[childExpr] THEN RETURN[childExpr]; -- return selected expression
ENDLOOP;
if there are no selectable children, signal noSelection
ERROR noSelection;
};
m: MatrixDisplayObj => {
** FOO FIX THIS **
ERROR noSelection;
};
ENDCASE => ERROR;
};
ExtendFunctionSelectSibling: PROC [expr: BoxTree] RETURNS [BoxTree] ~ {
Hack 7/10/87: Assuming expr is the last arg of a function, change its parent to be larger function class and select the new last arg.
emptyAtom: ATOM ← $expr; -- generic identifier
emptyRelBox: BOX ← MathBox.MakeBox[emptyAtom, NIL, other, relative];
emptyAbsBox: BOX ← MathBox.MakeBox[emptyAtom, NIL, other, absolute];
arg12Space: BoxTree ← MakeAtomicBoxTree[$arg12Space, MathDB.LookupAtomClass[$space], Convert.RopeFromAtom[$medium, FALSE], emptyRelBox, emptyAbsBox];
arg23Space: BoxTree ← MakeAtomicBoxTree[$arg23Space, MathDB.LookupAtomClass[$space], Convert.RopeFromAtom[$medium, FALSE], emptyRelBox, emptyAbsBox];
arg34Space: BoxTree ← MakeAtomicBoxTree[$arg34Space, MathDB.LookupAtomClass[$space], Convert.RopeFromAtom[$medium, FALSE], emptyRelBox, emptyAbsBox];
comma12: BoxTree ← MakeAtomicBoxTree[$comma12, MathDB.LookupAtomClass[$italicSym], XRope.FromChar[',] , emptyRelBox, emptyAbsBox];
comma23: BoxTree ← MakeAtomicBoxTree[$comma23, MathDB.LookupAtomClass[$italicSym], XRope.FromChar[',] , emptyRelBox, emptyAbsBox];
comma34: BoxTree ← MakeAtomicBoxTree[$comma34, MathDB.LookupAtomClass[$italicSym], XRope.FromChar[',] , emptyRelBox, emptyAbsBox];
newExprs: LIST OF BoxTree;
sibling: BoxTree ← BoxTreeFromExpr[MathConstructors.MakePlaceHolder[] ];
sibling.parent ← expr.parent;
WITH expr.parent.expression SELECT FROM
c: CompoundDisplayObj => SELECT c.class.name FROM
$nullaryFunction, $unaryFunction, $binaryFunction, $ternaryFunction => {
SELECT c.class.name FROM
$nullaryFunction => {
c.class ← MathDB.LookupCompoundClass[$unaryFunction];
sibling.tag ← $arg1;
newExprs ← LIST[sibling]
};
$unaryFunction => {
c.class ← MathDB.LookupCompoundClass[$binaryFunction];
sibling.tag ← $arg2;
newExprs ← LIST[sibling, arg12Space, comma12]
};
$binaryFunction => {
c.class ← MathDB.LookupCompoundClass[$ternaryFunction];
sibling.tag ← $arg3;
newExprs ← LIST[sibling, arg23Space, comma23]
};
$ternaryFunction => {
c.class ← MathDB.LookupCompoundClass[$quaternaryFunction];
sibling.tag ← $arg4;
newExprs ← LIST[sibling, arg34Space, comma34]
};
ENDCASE => ERROR; -- can't extend quaternaryFunction
IF c.subExprs = NIL THEN c.subExprs ← newExprs ELSE {
p: LIST OF BoxTree ← c.subExprs;
WHILE p.rest#NIL DO p ← p.rest ENDLOOP;
p.rest ← newExprs;
};
};
ENDCASE => ERROR noSelection;
ENDCASE => ERROR noSelection;
RETURN[sibling];
};
SelectableSibling: PUBLIC PROC[expr: BoxTree] RETURNS[BoxTree] ~ {
effects: Returns the "next" sibling expression from expr.
SIGNALS noSelection if no selectable sibling exists.
local declarations
afterExpr: LIST OF Argument ← NIL; -- sibling after expr in parent's subexpr list
siblingExpr: BoxTree ← NIL;
IF (expr = NIL) OR (expr.parent = NIL) THEN ERROR noSelection;
"cycle" thru parent's children and return "next" selectable sibling in list
WITH expr.parent.expression SELECT FROM
a: AtomDisplayObj => ERROR noSelection;
c: CompoundDisplayObj => {
find starting point to begin looking for next selectable sibling expr
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
IF l.first.name = expr.tag THEN {afterExpr ← l.rest; EXIT};
ENDLOOP;
look for next selectable sibling in exprs after expr in list (i.e. in afterExpr)
FOR l: LIST OF Argument ← afterExpr, l.rest UNTIL l = NIL DO
siblingExpr ← GetBoxTree[l.first.name, c.subExprs];
IF Selectable[siblingExpr] THEN RETURN[siblingExpr]; -- return selected expression
ENDLOOP;
SELECT c.class.name FROM
$nullaryFunction, $unaryFunction, $binaryFunction, $ternaryFunction => RETURN[ExtendFunctionSelectSibling[expr]]; -- hack
ENDCASE;
*** hack 7/16/87 to handle extra inserted parentheses
IF c.class.name=$paren THEN RETURN[SelectableSibling[expr.parent] ];
*** end hack
if no selectable sibling exists after expr, then wraparound list to before expr
FOR l: LIST OF Argument ← c.class.arguments, l.rest UNTIL l = NIL DO
IF l.first.name = expr.tag THEN EXIT; -- already looked thru entire list
siblingExpr ← GetBoxTree[l.first.name, c.subExprs];
IF Selectable[siblingExpr] THEN RETURN[siblingExpr]; -- return selected expression
ENDLOOP;
if we haven't RETURNed yet, then there are no selectable siblings
ERROR noSelection;
};
m: MatrixDisplayObj => {
** FOO FIX THIS **
ERROR noSelection;
};
ENDCASE => ERROR;
};
Replace: PUBLIC PROC[expr, old, new: BoxTree] RETURNS[BoxTree] ~ {
effects: Returns a copy of display expression expr with new substituted for old.
caveats: Return value must be reformatted before Paint[]'ing.
Note that new is used (not a copy). Hence if expr = old, then new returned, i.e. a "copy" returned iff old is a proper subexpression of expr.
RETURN[ReplaceN[expr, LIST[[old: old, new: new]]]];
};
ReplaceN: PUBLIC PROC[expr: BoxTree, replacements: LIST OF ReplacePair] RETURNS[BoxTree] ~ {
effects: Returns a copy of display expression expr with new substituted for old for each pair in replacements [old, new].
If expr = old←i, then new←i i.e. a "copy" returned iff each old←i is a proper subexpression of expr.
Note that replacements can "interfere" with each other; outcome depends on their ordering.
caveats: Return value must be reformatted before Paint[]'ing.
Note that "old" components of replace pairs must be the actual pointers to the exprs we wish to replace, i.e. we only replace that actual expr, and not any other expr which is equivalent but physically distinct from it.
Note that the "new" components of replace pairs are used (not copies). Hence anyone who holds the pointer to a "new" component can e.g. later ask to have it replaced by something.
Note that recursion terminates as soon as we find a match, i.e. if expr = one of the olds (i.e. expr is one of the things to be replaced), we won't examine any subexprs of expr for possible additional matches
FOR l: LIST OF ReplacePair ← replacements, l.rest UNTIL l = NIL DO
IF expr = l.first.old THEN {
parent: BoxTree ← l.first.old.parent;
new: BoxTree ← l.first.new;
Parenthesize replacement if its op has lower precedence than parent
IF parent #NIL AND MathDB.CompareOps[Class[new], Class[parent] ] = less THEN {
-- 7/10/87 -- turn off op precedence for functional notation parsing
IF FALSE THEN {
parenArgTag: ATOM = $a;
argBoxTree: BoxTree;
Create skeleton for parenthesized expression (i.e. dummy arg)
parenExpr: BoxTree ← BoxTreeFromExpr[MathExpr.MakeCompoundExpr[$paren, LIST[[parenArgTag, MathConstructors.MakePlaceHolder[] ]] ] ];
Grab onto dummy arg
FOR l: LIST OF BoxTree ← GetSubExprs[parenExpr], l.rest UNTIL l = NIL DO
IF Tag[l.first] = parenArgTag THEN { argBoxTree ← l.first; EXIT };
ENDLOOP;
Stuff actual pointer to new into parenExpr
parenExpr ← Replace[parenExpr, argBoxTree, new];
Update pointers
new.tag ← $a; new.parent ← parenExpr;
parenExpr.tag ← l.first.old.tag; -- replacement has same tag (e.g. $integrand, etc.)
parenExpr.parent ← parent; -- replacement has same parent as before
RETURN[parenExpr]
}
ELSE {
new.tag ← l.first.old.tag; -- replacement has same tag (e.g. $integrand, etc.)
new.parent ← parent; -- replacement has same parent as before
RETURN[new]
};
};
ENDLOOP;
WITH expr.expression SELECT FROM
a: AtomDisplayObj => {
RETURN[Copy[expr]];
};
c: CompoundDisplayObj => {
local declarations
exprCopy: BoxTree; -- return value
copiedSubExprs: LIST OF BoxTree ← NIL; -- cons up new list of copies
recursively copy subexpressions
FOR l: LIST OF BoxTree ← c.subExprs, l.rest UNTIL l = NIL DO
copiedSubExprs ← CONS[ReplaceN[l.first, replacements], copiedSubExprs];
ENDLOOP;
construct return value now so we can set parent pointers for subexprs
exprCopy ← MakeCompoundBoxTree[expr.tag, c.class, copiedSubExprs, expr.relativeBox, expr.absoluteBox];
FOR l: LIST OF BoxTree ← copiedSubExprs, l.rest UNTIL l = NIL DO
l.first.parent ← exprCopy; -- set parent pointers appropriately
ENDLOOP;
RETURN[exprCopy];
};
m: MatrixDisplayObj => {
local declarations
exprCopy: BoxTree; -- return value
copiedElts, copiedEltsPtr: LIST OF BoxTree ← NIL; -- cons up a new list of copied elements
recursively copy elements
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO
copiedElts ← CONS[ReplaceN[l.first, replacements], copiedElts];
ENDLOOP;
recursively copy elements - changed 3/4/87
FOR l: LIST OF BoxTree ← m.elements, l.rest UNTIL l = NIL DO
IF copiedElts = NIL THEN copiedElts ← copiedEltsPtr ← LIST[ReplaceN[l.first, replacements] ] ELSE copiedEltsPtr ← copiedEltsPtr.rest ← LIST[ReplaceN[l.first, replacements] ];
ENDLOOP;
construct return value now so we can set parent pointers for subexprs
exprCopy ← MakeMatrixBoxTree[expr.tag, m.class, m.nRows, m.nCols, copiedElts, expr.relativeBox, expr.absoluteBox, Copy[m.openSym], Copy[m.closeSym], Copy[m.space]];
FOR l: LIST OF BoxTree ← copiedElts, l.rest UNTIL l = NIL DO
l.first.parent ← exprCopy; -- set parent pointers appropriately
ENDLOOP;
RETURN[exprCopy];
};
ENDCASE => ERROR;
};
Signals & Errors
unable: PUBLIC ERROR[reason: ATOM] = CODE;
noSelection: PUBLIC ERROR = CODE;
exprNotFound: PUBLIC ERROR = CODE;
END.