EditorImplementation.tioga
Dennis Arnon, April 13, 1987 12:49:23 pm PDT
The CaminoReal Expression Editor: Implementation
Carl and Dennis
© Copyright 1987 Xerox Corporation. All rights reserved.
1. Imager Boxes, Rectangles and Extents
ImagerBox.Box is RECORD [xmin, ymin, xmax, ymax: REAL]; This seems to assume that the origin is in the (closure of the) interior of the box, i.e. that xmin and ymin are nonpositive numbers. Then ImagerBox.ExtentsFromBox can just do [leftExtent: -b.xmin, descent: -b.ymin, rightExtent: b.xmax, ascent: b.ymax]. Thus all components of an Extent are nonnegative numbers.
Presumably this convention is preserved in Meddle Extents.
Imager.Rectangle is RECORD [x, y, w, h: REAL], where [x, y] is lower left hand corner, w = width, h = height. I.e. rectangle is [x, y], [x+w, y], [x+w, y+h], [x, y+h]. Appears to be the case that w, h can be negative or positive.
2. Meddle Boxes
The offset of a Meddle Box is the vector from the origin of whatever the current coordinate system is, to the lower left hand corner of the Box. Thus (the position and size of) a Box is (are) precisely specified, in a given coordinate system, by giving its offset, origin, and extents.
Relative boxes seem to always adhere to the convention of being located inside a 1 x 1 square.
Suppose given an absolute box A and a relative box R. Then we can map R to an absolute Box B that has the same relative position inside of A as R has inside the 1 x 1 square, as follows: we (independently) scale the width and height (i.e. the Extents) of the relative box according to the width and height of the given absolute box (this is just the mapping of the 1 x 1 square to A), which gives us B, and then position B inside of A by giving it an offset equal to the offset of A plus the scaled offset of R.
3. Origins of Meddle Boxes
In general, origins seem to be set so that if the origins of two boxes that each contain a single character are made to lie on the same horizontal line, the characters will appear to be properly aligned on the same horizontal line of text. This takes account of the possibly different ascent and descent. I.e. origins seems to lie on what Knuth ("TeX, a system for Technical Text", p. 42) calls the baseline. Perhaps origins per se are arrived at by applying the same principal in the vertical direction - i.e. if the origins of two boxes that each contain a single character (as e.g. in a fraction) are made to lie on the same vertical line, then the characters will appear to be properly aligned one above the other.
Presumably origins of general expressions in boxes are arrived at by application of the same principles. So perhaps in general, unless you know you want to do differently, you should align by origin.
4. Meddle`s constraint language for aligning boxes
Each (Argument and Symbol) Box of the notation must be mentioned exactly once in the LIST OF Alignment2D passed to MathRules.Compose, or (the contents of) that Box will not get painted when the notation is painted.
In a MathRules.Alignment2D, we may distinguish the Box "to be aligned" and the Box "to align with"; e.g. for the first Alignment2D in:
alignments: LIST OF Alignment2D ← LIST[
[$topSpace,
[$fractionBar, [center], [center]],
[$fractionBar, [bottom], [top]]],
[$bottomSpace ...
$topSpace is the "to be aligned" Box, and $fractionBar is the "to align with" Box.
Obviously the MathRules.Offset values of left and right in an Alignment2D only make sense for x-alignment, top and bottom only make sense for y-alignment, and center and origin make sense for both.
The "to align with" Box in the first Alignment2D in the list, e.g. $fractionBar above, is taken by MathRules.Compose to be the "origin" Box, i.e. the Box with respect to which all others are "to be aligned" Boxes (in the sense of transitive closure).
Rule: in the LIST OF Alignment2D in a CompositionProc, with the exception of the "origin" Box, a Box must first appear as a "to be aligned" Box in one of the Alignment2D's in the list before it can appear as a "to align with" Box.
Hence if we define the (irreflexive, antisymmetric, transitive) relation of "is aligned with respect to", then a correctly formed alignments: LIST OF Alignment2D is a directed acyclic graph whose nodes are in one-one correspondence with the Boxes of the notation, whose root is the "origin" Box, whose arcs point towards the root, with the property that the closer a node is to the root, the sooner it occurs as a "to be aligned" Box in alignments: LIST OF Alignment2D.
The graph is not a tree because a Box can be x-aligned with one Box and y-aligned with a different one.
The prohibition of cycles can cause problems: e.g. you cannot simultaneously x-align B2 with respect to B1 (which may be necessary e.g. if left of B1 needs to be aligned with respect to left of B0, and left of B2 needs to be aligned with respect to right of B1) and y-align B1 with respect to B2 (which may be necessary e.g. if y-origin of B1 needs to be aligned with respect to y-origin of B2).
Note: there is no enforcement of disjointness of aligned boxes.
5. Exprs vs. DisplayExprs
A Class of CompoundExprs is defined by an operator, e.g. PLUS. Thus this class of expressions is the set of all sums.
The database (MathDB) pertains to Expr Classes.
DisplayExprs are Exprs which have relativeBoxes and absoluteBoxes attached to them, and have parent pointers. A DisplayExpr can be attached to a Viewer; an Expr cannot.
(1) DisplayExprs are mutable, i.e. you can go in and change a substructure of a DisplayExpr. Thus, when you have a pointer to a DisplayExprRep, some recursive part of the expression may change "without you're knowing". This is why you need the parent pointers in DisplayExpr rep.
(2) Only DisplayExprs can be formatted and painted.
However both DisplayExprs and Exprs directly use the Expression classes database, i.e. both Exprs and DisplayObj's have a class: MathExpr.Class field.
General point: we have a ROPE representation for Exprs, not for DisplayExprs. Thus there seems to be the philosophy - when we represent something externally, we don't try to save formatting information with it.
However, it is DisplayExprs that we convert to ASRopes. This is a piece of cruftiness.
ToFix: We have DisplayExpr = TaggedMathExpr (i.e. tagged), Expr = Displayobj (i.e. untagged). Clean up and impose better terminology!
6. Managing the DisplayExpr form of an Expr
At what points in time is a new DisplayExpr constructed, i.e. is Formatting done? i.e. what tree actions cause computation of new DisplayExpr?
6. Atomic, Compound, and Matrix Expression types
Atomics are a Type and a Value (and a style?) Compounds have a operator and a fixed number of arguments. Matrices are a type, a 2dimensional specification of number of args, and the args (note that Matrices are always 2d; to get 1d, just set one index to 1). Matrix type can be used for any n-ary kind of construct, i.e. a CARDINAL-parametrized family of expressions, which all have the same formatting.
Apparently a characteristic feature of Compound expression classes is having a fixed number of arguments, and for this reason, vectors and matrices could not be done as Compound expressions.
6. More on Compound Expressions
1. Arguments and Symbols
Note that Arguments and Symbols are only used in CompoundExprs, indeed they are a part of the definition of any Class of CompoundExprs.
Meddle's view of the displayed form of a CompoundExpr is that it can be thought of as consisting of Arguments and Symbols, where Symbols are fixed glyphs (e.g. an integral sign, a radical sign), and Arguments are, recursively, arbitrary Expressions. The implementation of this is, when we need the Expr corresponding to an Argument, to do a lookup in the subExprs list of the CompoundExpr using the tag for that argument given in the Argument list of the CompoundClass definition. On the one hand, this scheme of classifying the components of a notation as either Arguments or Symbol permits too much (e.g. in a function notation f(x), we can replace f by a/b to get nonsense, since f is an Argument in the function Expr class); on the other, since the hit detection mechanism does not allow for selection of Symbols of an Expression (its algorithm is: see if the hit occurs inside any Argument Expr, if so, return result of recursive call HitDetect[argumentExpr, position], else return thisExpr), which apparently people want to do.
Note that if the same symbol occurs multiple times, e.g. comma in f(a, b, c), then those have to be different declared symbols. The point is that each symbol and argument comprising a notation must be a distinct, nameable, box, which we will constrain appropriately to do the layout.
There are two extensions to the current scheme that might be helpful. First, allow initial assignment of Exprs other than Placeholder to Arguments. Then if there are obvious cases of current Symbols that one would like to be able to select, e.g. integral sign, make them Arguments with appropriate initial (Atomic) Exprs. Of course, this opens the way to new kinds of garbage, e.g. an Expr that CaminoReal thinks internally is an integral, but whose display form doesn't have an Integral sign anywhere because that Argument Expr has been replaced by some other Expr.
Would really be nice if the initial Exprs assigned to Arguments could be the results of procedure evaluations. This would allow the definition of "composed" Expr classes, e.g. matrix of partial derivatives, i.e. much richer templates. Ideally, user interface would allow users to just create the template using the Editor, then say "install it", and have CaminoReal do the necessary bookkeeping. (This opens issues of what "siblings" would mean in such templates, however; presumably the answer is: wrt order of leaves in a preorder tree traversal.)
A second extension might help with both current and future problems of introducing garbage. This would be, for Arguments of an Expression class (whether initially placeholder, or whether intially something more elaborate), to allow for "type" specification, i.e. imposition of rules that restrict the kinds of Expressions that a given Argument can be given as values. Note that the Domain-driven editing mode (i.e. you eval the current expr, put up the ops of its Domain, and specify Expression creation rather than Evaluation with the selected operator) accomplishes this.
2. Hot Arguments
At most one of the Arguments of a CompoundExpr can have the alias $hot? Is the notion of "selectable sibling of the hot arg" equivalent to the notions of "hottest" and "hot" that we want for the semantics of wrap: "the Expr being wrapped around replaces the hottest Arg, and the hot arg becomes the primary selection"?
3. The issue of grammars
We don't actuall have a grammar in our database. i.e. our (Compound) templates consist of ordered arglists. There is no built-in assumption that we will "see" certain characters in an (ordered) "input stream".
7. General notes on Atom Expr Classes.
The values of atoms of class space are (the ROPEs) $null, $thin, and other such values seen in the SpaceBox proc.
Suggests that we should have an atom class for special symbols. This is the one that should allow you to type in octal code for any symbol, and a name (e.g. $infinity), and have that added.
8. Atom Box Procs
AtomBoxProc: TYPE ~ PROC[value: ROPE, style: Style] RETURNS[ImagerFont.Extents];
Their function is to compute a bounding box of something. For example, MathRules.AtomBoxProc Computes the bounding box for an atom, given its value & style. Note that an AtomBoxProc is an arg to InstallExprClassesA.MakeAtomClass.
9. Box and Composition Procs for Compound Classes
CompoundBoxProc: TYPE ~ PROC[boxes: LIST OF BOX] RETURNS[LIST OF BOX];
A MathRules.CompoundBoxProc does not compute an overall bounding box; instead it adjusts the bounding boxes of the arguments & symbols (of a compound expr) to satisfy desired constraints. E.g. FractionBoxRule scales the fraction bar to be as wide as numerator and denominator.
Thus in CompoundBoxProcs, we make the various components (arguments & symbols) of the display form of a CompoundExpr have the right size; it is then the Class CompositionProc that actually lays them out.
CompositionProc: TYPE ~ PROC[boxes: LIST OF BOX] RETURNS[BOX, LIST OF BOX];
The BOX returned will be an absolute Box, the LIST OF BOX returned will be relative Boxes.
The CompositionProc of a CompoundExpr class appears to be invoked in only one place - in MathDisplayExpr.Format. The only other place that CompositionProcs are mentioned is MathExpr.MakeCompoundClass.
10. MathDisplayExpr.Format
MathDisplayExpr.Format is called by SetDisplayExpr, WriteArtwork, and CreateExprViewer in ViewExprImpl. In both cases, function seems to be to get a bounding box for the DisplayExpr. What is curious is that in none of these instances, nor in MathDisplayExpr.Format itself, do we seem to reset the relativeBox of the DisplayExpr arg to the new overall bounding box we compute for it. Perhaps this is related to the fact that that field seems to be set from "outside", e.g. for a CompoundExpr, we recursively Format its subExprs, CONS the boxes we get back into a list, apply the CompoundExprClass BoxProc to possibly rescale the boxes, then call the CompoundExprClass CompositionProc to compute the overall bounding box, and return that. These CompositionProc's will call MathRules.Compose, which will make the subExpr boxes relative boxes, and leave the overall bounding box as an absolute box. Thus the overall bounding box returned by MathDisplayExpr.Format is an absolute box.
The explanation is that it is the displayBox field of the viewer off of which a DisplayExpr is hung that maintains its overall bounding box.
MathDisplayExpr.Format updates its DisplayExpr arg in the sense that it sets new bounding boxes for all subexpressions.
11. Painting
When we go to paint an ExprViewer, we first newly set its physicalBox by applying the current user-specified scale factor (which is also hung off this ExprViewer) to its current displayBox, and then call MathDisplayExpr.Paint with this new physicalBox as its absBox argument. It then calls Paint recursively on each subExpr, which calls MathBox.RelToAbsBox on the subExpr's relativeBox and the given absBox, to get a new absBox argument for the recursive call to Paint. At bottom, we paint atoms into the specified absBoxes.
Question that arises from looking in detail at Atom Paint Procs - when we paint, we scale what is to be painted to fit into the given absBox. But the actual painting is done with an Imager.SetXY[context, absBox.Offset[]], followed e.g. by an Imager.ShowRope. How do we know that the origin inside the absBox is in the same relative position as the origin inside the relBox, i.e. how do we know that we spray the bits down, they will actually lie inside the absBox? Answer seems to be that consistency depends on consistency of the Imager.Extents returned by ImagerFont.RopeBoundingBox, i.e. the origin returned by ImagerFont.RopeBoundingBox, and the painting action of Imager.SetXY to that origin followed by Imager.ShowRope.
Another question about Atom Paint Procs - why do they call ImagerFont.RopeBoundingBox to get new Extents? This is already done once by the call to the Box Proc when the Atom is Formatted.
12. Matrix Box Procs
Matrix expression classes appear not to have Box Procs. Perhaps this is because all the relevant knowledge is imbedded in MathRules.ComposeMatrix.
13. Painting viewers
When should we use the paint queue, and when should ViewerTools.PaintViewer be called directly?
Possible answer: we use the queue when we want to "stack" two or more viewers before painting any, as in a copy or swap.
Claim: All painting should be done through the queue (for consistency).
13. Selections
General rules
Note that selections are specified in terms of decorated parse trees, i.e. DisplayExprs. I.e. they are independent of their display in specific viewers.
The general idea is to maintain a global, across all the different ExprViewers that may exist at any one time, list of currently active selections. Besides its flavor, with each we store the Viewer containing it, and the selected DisplayExpr within that Viewer. Painting of the selection depends on finding exactly that DisplayExpr, i.e. by equality of pointers, in a walk down the Viewer's (overall) DisplayExpr.
The concept of whether or not a selection of a certain type is "active" or not?
ViewExprOpsImpl.GlobalSelections is a list of the currently active selections. Just because a flavor has an entry here, doesn't mean active; that true iff its value (of type Selection) is non—NIL and if active field TRUE.
Keyboard selections
A KB selection is really just a certain kind of "incremental" primary selection, i.e. "string of characters being typed in from the keyboard". When the screen shows a KB selection, it means that if the right sort of character is typed next, it will be concatenated to the current KB selection. When a "terminating action" occurs, the KB selection becomes the primary selection, which determines the semantics of the action.
At any point at which we have an active primary selection, a letter, digit, ?, or % causes us to start on a KB selection.
Wraps, whether SingleChar or MultiChar, always happen around the current Primary selection. If I have some a KB selection of type variable, integer, real, or symbol, then I have to explictly make it the Primary selection (^M) before I can do a MultiChar wrap around it. An exception to this is that a SingleChar wrap when there is a KB selection will treat it as though it were the Primary selection. If I have a KB selection and I try to do a MultiChar wrap without explicitly making the KB primary, the parser will just keep listening to the chars of my MultiChar template as though they were to be extending the current KB selection, e.g. the ?'s will get tossed.
The way MultiChar wraps work is: when you start it, you save the current active KB selection, if any, else the current active primary selection. Then you start building a new KB selection on top of whichever one you saved. When the template name is terminated, you replace the current KB with what was saved and make it Primary, then wrap the template around it.
Types:
Primary selection:
Selected Expression is highlighted by rendering it white on black, which is
inverted from the normal black on white (just like Tioga).
Copy selection:
Selected Expression is highlighted in dark gray.
Move selection:
Selected Expression is highlighted in light gray.
Active keyboard selection (KB Selection):
This selection type cannot be applied by the user. The selected Expression
highlighted using horizontal gray lines. This selection type is automatically
invoked when there is an active keyboard entry for an atom (e.g. a number or
variable). Its purpose is mostly as an indicator.
Change: when template wrapping is invoked either from keyboard or menu, or when Ctrl-P typed, active keyboard selection is used like a primary selection. I.e. the semantics is now that KB active means that letters and digits
Indication:
Single Clicks:
Red => Primary Select
Shift Red => Copy Select
Ctrl Red => Move Select
Blue => Primary Sibling Select
Shift Blue => Primary Child Select
Double Clicks:
Red => Extend Primary Selection to Parent
Shift Red => Extend Copy Selection to Parent
Ctrl Red => Extend Move Selection to Parent
Keyboard:
DEL => Delete Primary Selection
Ctrl-I => Primary select entire current viewer contents
Ctrl-P, '), '], '} => Primary Select Parent of current Primary or KB selection
Ctrl-K => Primary Select Child (das Kind) of current Primary or KB selection
Ctrl-L, ', => Primary Select Sibling (Lateral movement) of current Primary or KB selection
Ctrl-M => convert KB selection to Primary
14. Keyboard and Menu-driven "Parsing"
Semantics of Replacement
We are always replacing the Primary or Keyboard Selection by some Expr, i.e. it makes no sense to speak of replacing the Copy or Move selections.
If the Expr that we are replacing by is a template, we primary select its hot arg.
15. Interfaces annotated
MathTypes
Defines types common to all modules. In the latest version, only two types are
defined: Style (font info), and
FormatClass: TYPE ~ {atom, paren, binaryOp, unaryOp, op, relation, over, matrix, radical, other};
This is a classification of user-defined operations into categories for use by formatting procedures.
Note that a FormatClass is an arg to MakeAtomClass (all the atom classes supply a value of "atom".)
Note also that MathDisplayExpr.Format sets the FormatClass of outer bounding RelBox to a new value.
MathBox
Data abstraction for a BOX. A BOX is an immutable object.
A BOX is the basic building block for formatting expressions.
(Similar to TeX's concept of boxes and glue.)
Provides BOX constructors and selectors.
Provides operations for determining metrics about a box.
Provides some LIST operations for LIST OF BOX.
MathRules
Defines procedure types for formatting, positioning, painting, and data conversion rules.
Defines types associated with alignment, positioning, and sizing.
(MEDDLE currently has four fixed MathRules.Size values: normal, big, script, and scriptscript)
Provides primitive procedures to align boxes: AlignHorizontal and AlignVertical.
Provides high-level procedures for automated positioning & composition:
Compose and ComposeMatrix.
Provides procedures for computing relative sizes: ComputeSize and VecFromSize.
MathExpr
Data abstraction for a mathematical expression, EXPR. An EXPR is an immutable
object which contains information about its subexpressions and their classes.
Provides EXPR constructors and selectors.
Provides parse/unparse operations for EXPR.
Provides LIST selector operation for LIST OF EXPR.
Also defines class types for atoms, compound exprs, and matrix exprs.
Classes contain information such as name and rules for formatting, painting, etc.
Provides constructors and selectors for these classes.
MathDisplayExpr
Data abstraction for a displayed mathematical expression, DisplayExpr. A DisplayExpr
is a mutable object which is an EXPR mapped to a viewer. It contains mutable
information about absolute box locations, etc.
Provides DisplayExpr constructors and selectors.
Provides conversion routines between EXPR <=> DisplayExpr.
Provides Copy, Format, Paint, Replace, Selection operations.
Provides an unparse operation to AlgebraStrutures/CaminoReal format.
MathDB
The database of known types of mathematical expressions.
Loaded up by the InstallExprClasses modules.
Currently accessed only by the "Make Expr" procs in MathExpr (e.g. MakeCompoundExpr), and by the editing action procs (e.g. ReplaceWithOperator in ViewExprImpl).
MathConstructors
Defines and registers classes and rules for many common math operations.
Provides high-level constuctors to combine EXPR's using these operations.
This is the right module to look at if you are defining new operations (notations).
InstallExprClassesA, InstallExprClassesB
Loads up the database.
ViewExprOps
Provides procedural abstractions called by ViewExpr to deal with selections and
paint "queue" operations.
ViewExpr
This is the core of the MEDDLE user interface.
Defines and registers a new viewer class, $expr.
Parses keyboard and mouse events.
Handles menu events.
Handles copy/move/select events.
Handles all viewer trivia like PaintProc, NotifyProc, bi-scroller stuff, etc.
Provides high-level operations to create a MEDDLE viewer and set/get its contents.
MathToTioga
Interface between MEDDLE and Tioga.
Defines and registers the Tioga CharacterArtwork class $MeddleExpr.
Registers (Commander) command "MeddleArtwork {on|off}" to enable/disable this feature.
16. Obsolete interfaces
MathCommunication
Interface between MEDDLE and CaminoReal.
Provides high-level operations to create a MEDDLE viewer and set/get its contents.
MakeMeddleViewer
Defines and registers (Commander) command "MeddleOpen {name}" to open a
new MEDDLE viewer from the CommandTool.