<<>> <> <> <> 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; MathBoxTreeImpl: CEDAR PROGRAM IMPORTS MathBox, MathExpr, MathDB, MathConstructors, Imager, ImagerBackdoor, ImagerFont, ImagerPath, MathRules, Rope, Convert, Vector2, XRope EXPORTS MathBoxTree ~ BEGIN <> <<>> 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; <> BoxTreeRep: PUBLIC TYPE ~ RECORD [ method: ATOM, -- identifying tag such as $integrand expression: DisplayObj, -- atom, compound expr, or matrix relativeBox: BOX, -- bounding box in relative units absoluteBox: BOX, -- bounding box in absolute units iconic: BOOL _ FALSE, -- TRUE iff expression is currently iconic (2/87apparentlyUnused) parent: BoxTree _ NIL -- enclosing parent expression ]; BoxTreeRep: TYPE ~ RECORD [ root: Box, parent: BoxTree _ NIL, children: LIST OF BoxTree _ NIL -- leaf iff children = NIL ]; ObjectDisplayRep: TYPE ~ RECORD [ SELECT type:* FROM atomic => [ display: BoxTree _ NIL, -- object visible iff display # NIL object: Object, ], <> <> <> <<>> compound => [ -- "unpacked" (to at least one level) Object display display: Box, -- bounding, i.e. outermost, box for the display of self. We specify a Box, rather than a BoxTree, here, because we assume all subBoxes to be contained in self.args and self.opDisplay domain: Object, op: ATOM, args: LIST OF ObjectDisplay, <> <> <> opDisplay: LIST OF ObjectDisplay _ NIL <> <> <<>> <> ], ENDCASE <> <> <> <> <> <> <> <> <> <> <> <> <> ]; BoxTree: TYPE ~ REF BoxTreeRep; -- inside impl module, use rep ObjectDisplay: TYPE ~ REF ObjectDisplayRep; -- inside impl module, use rep <> MakeAtomicBoxTree: PUBLIC PROC[tag: ATOM, class: AtomClass, value: ROPE, relBox, absBox: BOX, parent: BoxTree _ NIL] RETURNS[BoxTree] ~ { <> 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] ~ { <> 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] ~ { <> 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]]]]]]; }; <> ASRopeFromBoxTree: PUBLIC PROC[expr: BoxTree, flavor: ATOM _ $AS] RETURNS[ROPE] ~ { <> <<>> 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: ROPE _ SELECT 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: ROPE _ NIL; position: INT _ -1; IF Rope.Length[template] < 1 THEN RETURN[""]; <> 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; <> 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: ROPE _ NIL; -- return value <> <> 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]]; <> IF c # (m.nCols - 1) THEN result _ Rope.Cat[result, ", "]; -- col separator ENDLOOP; result _ Rope.Cat[result, " ]"]; -- row end char <> 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]]; <> 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]]; <> 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] ~ { <> << All Display formatting information is set to default values.>> <<>> <> 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[]; <> RETURN[MakeAtomicBoxTree[emptyAtom, MathExpr.GetAtomClass[a], MathExpr.GetValue[a], emptyRelBox, emptyAbsBox]]; }; compound => { c: CompoundEXPR _ expr.GetCompoundExpr[]; compClass: CompoundClass _ MathExpr.GetCompoundClass[c]; <> displaySubExprs: LIST OF BoxTree _ NIL; -- cons up list of display expressions <> 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; <> 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; <<>> <> myBoxTree _ MakeCompoundBoxTree[emptyAtom, MathExpr.GetCompoundClass[c], displaySubExprs, emptyRelBox, emptyAbsBox]; <> 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]; <> <> <> <> <> <> <<>> <> 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]; <> 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] ~ { <> <<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 => { <> args: LIST OF MathExpr.TaggedMathExpr _ NIL; <> 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 => { <> elts, eltsPointer: LIST OF MathExpr.TaggedMathExpr _ NIL; <> <> <> <<>> 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] ~ { <> WITH expr.expression SELECT FROM a: AtomDisplayObj => { RETURN[MakeAtomicBoxTree[expr.tag, a.class, a.value, expr.relativeBox, expr.absoluteBox]]; }; c: CompoundDisplayObj => { <> exprCopy: BoxTree; -- return value copiedSubExprs: LIST OF BoxTree _ NIL; -- cons up new list of copies <> FOR l: LIST OF BoxTree _ c.subExprs, l.rest UNTIL l = NIL DO copiedSubExprs _ CONS[Copy[l.first], copiedSubExprs]; ENDLOOP; <> 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 => { <> exprCopy: BoxTree; -- return value copiedElts, copiedEltsPtr: LIST OF BoxTree _ NIL; -- cons up a new list of copied elements <> <> <> <> <> 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; <> 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; }; <> Tag: PUBLIC PROC[expr: BoxTree] RETURNS[ATOM] ~ { <> RETURN[expr.tag]; }; Class: PUBLIC PROC[expr: BoxTree] RETURNS[ATOM] ~ { <> 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] ~ { <> << 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] ~ { <> RETURN[expr.absoluteBox]; }; <> 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; <> extents.ascent _ extents.ascent + axisHeight; extents.descent _ extents.descent - axisHeight; <> innerBox _ MathBox.ChangeOffset[innerBox, innerBox.Offset[].InlineAdd[[0.0, axisHeight]]]; RETURN[innerBox]; }; InnerFormat: PROC[expr: BoxTree, size: Size] RETURNS[BOX] ~ { <> <> << Returns a bounding box for expr.>> << SIGNALS unable[reason: ATOM] if InnerFormatting can not be completed.>> <<>> <> 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 => { <> subExprBoxes _ NIL; -- cons up list of subexpression boxes <> 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; <> 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; <> subExprBoxes _ c.class.boxRule[subExprBoxes]; <> [myBox, subExprBoxes] _ c.class.compBox[subExprBoxes]; <> myBox _ MathBox.ChangeFormatClass[myBox, c.class.formatClass]; <> 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 => { <> elementBoxes, elementBoxesPtr: LIST OF BOX _ NIL; tempBox, spaceBox, openSymBox, closeSymBox: BOX; <> <> <> <> <> <> 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; <> spaceBox _ InnerFormat[m.space, MathRules.ComputeSize[base: size, adjustment: script]]; openSymBox _ InnerFormat[m.openSym, normal]; closeSymBox _ InnerFormat[m.closeSym, normal]; <> [myBox, elementBoxes, m.openSym.relativeBox, m.closeSym.relativeBox] _ MathRules.ComposeMatrix[m.nRows, m.nCols, elementBoxes, spaceBox, openSymBox, closeSymBox]; <> myBox _ MathBox.ChangeFormatClass[myBox, m.class.formatClass]; <> 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] ~ { <> <> <> << Updates absolute bounding boxes (viewer coords).>> << SIGNALS unable[reason: ATOM] if Painting cannot be completed.>> <<>> <> currentBoxTree: BoxTree; <<>> <> 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 => { <> <<>> <> 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; <> 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 => { <> 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[m.openSym, context, MathBox.RelToAbsBox[m.openSym.relativeBox, absBox], selections]; Paint[m.closeSym, context, MathBox.RelToAbsBox[m.closeSym.relativeBox, absBox], selections]; }; ENDCASE => ERROR; <> FOR l: LIST OF Selection _ selections, l.rest UNTIL l = NIL DO IF (expr = l.first.expr) THEN { HighlightProc: PROC ~ { fat: REAL _ 3; <> context.SetColor[l.first.color]; IF NOT l.first.borderOnly THEN { <> Imager.MaskRectangle[context, [ x: absBox.Offset[].x - absBox.Extents[].leftExtent, y: absBox.Offset[].y - absBox.Extents[].descent, w: absBox.Width[], h: absBox.Height[]]]; <> fat _ 1; context.SetColor[ImagerBackdoor.invert]; }; <> 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; }; <> GetBoxTree: PUBLIC PROC[tag: ATOM, exprs: LIST OF BoxTree] RETURNS[BoxTree] ~ { <> << SIGNALS exprNotFound if no association exists.>> <> FOR l: LIST OF BoxTree _ exprs, l.rest UNTIL l = NIL DO IF l.first.tag = tag THEN RETURN[l.first]; ENDLOOP; <> ERROR exprNotFound; }; <> BoxTreeFromCoords: PUBLIC PROC[expr: BoxTree, x, y: REAL] RETURNS[BoxTree] ~ { <> << 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 => { <> 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 Selectable[expr] THEN RETURN[expr] ELSE ERROR noSelection; }; m: MatrixDisplayObj => { <> 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 Selectable[expr] THEN RETURN[expr] ELSE ERROR noSelection; }; ENDCASE => ERROR; }; Selectable: PUBLIC PROC[expr: BoxTree] RETURNS[BOOL] ~ { <> <<>> 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] ~ { <> << SIGNALS noParent if no selectable parent exists.>> <<>> <> IF (expr = NIL) OR (expr.parent) = NIL THEN ERROR noSelection; RETURN[expr.parent]; }; SelectableChild: PUBLIC PROC[expr: BoxTree] RETURNS[BoxTree] ~ { <> << SIGNALS noSelection if no selectable child exists.>> <<>> <> 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 <> childExpr _ GetBoxTree[l.first.name, c.subExprs]; IF Selectable[childExpr] THEN RETURN[childExpr]; -- return selected expression ENDLOOP; <> ERROR noSelection; }; m: MatrixDisplayObj => { <<** FOO FIX THIS **>> ERROR noSelection; }; ENDCASE => ERROR; }; ExtendFunctionSelectSibling: PROC [expr: BoxTree] RETURNS [BoxTree] ~ { <> 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] ~ { <> << SIGNALS noSelection if no selectable sibling exists.>> <<>> <> 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 => { <> 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; <> 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>> <<>> <> 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; <> ERROR noSelection; }; m: MatrixDisplayObj => { <<** FOO FIX THIS **>> ERROR noSelection; }; ENDCASE => ERROR; }; Replace: PUBLIC PROC[expr, old, new: BoxTree] RETURNS[BoxTree] ~ { <> <> << 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] ~ { <> <> <> <> <<>> <> <> <> 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; <> <> <<-- 7/10/87 -- turn off op precedence for functional notation parsing>> IF FALSE THEN { parenArgTag: ATOM = $a; argBoxTree: BoxTree; <> parenExpr: BoxTree _ BoxTreeFromExpr[MathExpr.MakeCompoundExpr[$paren, LIST[[parenArgTag, MathConstructors.MakePlaceHolder[] ]] ] ]; <<>> <> FOR l: LIST OF BoxTree _ GetSubExprs[parenExpr], l.rest UNTIL l = NIL DO IF Tag[l.first] = parenArgTag THEN { argBoxTree _ l.first; EXIT }; ENDLOOP; <> parenExpr _ Replace[parenExpr, argBoxTree, new]; <> <> 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 => { <> exprCopy: BoxTree; -- return value copiedSubExprs: LIST OF BoxTree _ NIL; -- cons up new list of copies <> FOR l: LIST OF BoxTree _ c.subExprs, l.rest UNTIL l = NIL DO copiedSubExprs _ CONS[ReplaceN[l.first, replacements], copiedSubExprs]; ENDLOOP; <> 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 => { <> exprCopy: BoxTree; -- return value copiedElts, copiedEltsPtr: LIST OF BoxTree _ NIL; -- cons up a new list of copied elements <> <> <> <> <> 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; <> 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; }; <> unable: PUBLIC ERROR[reason: ATOM] = CODE; noSelection: PUBLIC ERROR = CODE; exprNotFound: PUBLIC ERROR = CODE; END.