This file describes the first Visualizer project
==========
Steps in the project
It looks like a good way to go is to start with some simple examples, then work toward harder ones. This will be easier for you and at the same time let me develop the data structures over time, as the project proceeds.
A good way to organize the project is to do a number of smaller, mini-projects. The first is just visualizing a syntax graph for "A + B", and having it paint in the Display.
Please be aware that the syntax-graph data structures and the custom properties for EQNLang will change as more experience is gained in using them and it becomes clear that they have to be modified. This will cause the code you write to be modified, even after it is already working.
==========
Theory behind the syntax graph
The theory behind the way I want to do the syntax graph will probably seem a bit "strange" to you at first, especially if you have any experience with compilers. It's a bit abstract; the reason is to allow the same data structures to be re-used for any CTOS language that can be designed.
The best file to look at first is the "SyntacticElement.java" file, which has a very long comment that talks a bit about the thinking behind the data structures.
To summarize the idea behind the syntax data structures: Syntax is a pattern that correlates to the visual pattern that humans look at. Each element of the visual pattern has a corresponding element in the syntactic pattern.
So, that is the only thing that the data structures need to support: an element in the syntax for each visual cue. Visual cues are shapes and physical position. Physical position determines which visual pattern a given shape belongs to (more on this in the comment).
So, there are only three kinds of thing in the syntax graph:
Syntactic element
Syntactic link
Syntactic property
plus, a set of property-types, and a set of property values, for links and elements
To summarize the data structures.. I have only two main syntactic data types: elements and links. Each of these has a list of properties attached to it.
The element properties state what kind of element it is (for EQNLang), such as a root of a syntactic pattern, and/or a structural element, and/or a shape containing element.
The link properties state what kind of link it is (for EQNLang) such as a "back" link to the root of a syntactic pattern, or a link that indicates that data flows from one syntactic pattern to another..
===========
Property Names and Property Values
I have decided to make all property names and all property values be integers. I have included, in "EQNLangSyntaxSpecialization" a bunch of files that have static constants in them. These constants define the property names and property values. I also have a skeleton of how to use the properties in EQNLangSrcVisualizer.java
That file shows how to use the constants inside switch statements. The switch statements steer, to code that takes some action on the DisplayList. Each piece of code is only invoked if some set of things is true. For example, the method "foobar" is called when "Currently processing a SyntacticElement, and it is a command-type" is true, and only when that is true.
If you want to use a different code structure, write me an e-mail that makes a case for why you view the alternative structure as better.
You will find in EQNLangSrcHolder a test syntax graph that has been built in the code. This is the first syntax graph that you will visualize.
The next project after this one will add multiple syntactic patterns, positioning of them, and scaling of them. For now, just a single command with two variables.
============
What the Visualizer does
Each command in EQNLang has not only a shape but "ports". Each port is either an input or an output. They are placed visually around the shape.
The Visualizer must place an expression in the position of each port. If the input position in the syntax graph is empty, then the visualizer places an empty box in that input port's position. If the input position links to another command-rooted syntax pattern, then that entire syntax-pattern goes in the input-port's position.
The plus command in "A + B" has both input ports filled, and the command has no explicit output port. By "no explicit output port" I mean that, upon evaluation, the expression will be replaced by whatever it evaluates to. An expression's visual placement implies where it's output goes to. Syntax patterns with implied output ports are placed inside the input ports of other syntax patterns.
In the expression "A + B", the input ports of the "+" are filled by the "A" and "B". These are both "Name" patterns of memProcessor type. Name patterns have no visual input ports, and implied output ports.
What this all means is that information is needed about the position of input ports for each command. This additional information is what the Visualizer will use to place the syntax-patterns.
==========
Details of the Visualizer project
Here is what I would like (most of this is also in comments in the code):
-- The Visualizer will generate a DisplayList, which is a linked list of DisplayElements.
-- It first places a virtual shape for each syntactic element on a virtual grid, then generates a DisplayElement for each virtual shape.
-- The Visualizer doesn't know what the actual shapes are, it only knows the name of the shape and a bounding box for it. The Visualizer works with the bounding boxes.
-- The Visualizer places shapes onto a virtual grid. The grid is continuous, so each position is a float value.
-- the Display will create its own version of the virtual grid, and set "0,0" of its virtual grid to the lower left corner of its window (to start with.. the User can then pan and zoom).
-- Each syntactic element has associated layout information that is looked up via some mechanism.
-- The look-up info is loaded from disk during initialization of the Visualizer.
-- The layout information is a shape plus a list of ports.
-- The shape is a shape-name plus a bounding-box.
-- a bounding box consists of an origin x,y value and width plus height value.
-- The bounding box for a shape is the "minimum enclosing bounding box". The origin of the shape, when looked up, is always 0,0
-- a port has a bounding box too, but it has no shape.. instead, it's bounding box represents a constraint on how big the collection of shapes inside that port can grow.
-- The origin of a port bounding box is relative to the origin of the shape's BB (bounding box). It's as if the shape defined its own mini-grid, and the ports are placed on the shape's mini-grid. Thus, the origins of the ports are actually offsets from the origin of the shape.
-- The layout information for a "variable" in the syntax-graph comes from two sources: the type of variable is used to look up a font plus font-size, while the syntax-graph node has an attached text-string as a property-value.
-- Generate the shape's bounding box by calculating it. First look up the bounding box for each character in the string (from information about the font). Then calculate the smallest bounding box that encloses the entire string. Make the origin of the calculated enclosing bounding box be 0,0. A variable has no ports, so done.
-- do two passes when placing shapes. The first pass generates a tree of bounding boxes, the second pass scales and translates the bounding boxes, placing them onto the virtual grid.
First pass:
-- walk the syntax-graph (in a spanning tree fashion) and build up a tree of BoundingBoxTreeNode.
-- start with a root BBChildLink. Set it to be the current BBChildLink.
-- set the root of the syntax-graph to be the current syntactic element.
-- Generate a BoundingBoxTreeNode for the current syntactic element.
-- Set the link in the current parent BBChildLink to the newly created BoundingBoxTreeNode.
-- look up the type of syntactic element to get its shape info and port list.
-- If the syntactic element has ports, then make a BBChildLink for each port on the port-list. Make the BBChildLink's BB be the bounding box information that was in the port-info. This is a constraint bounding box.
-- Each port corresponds to a child in the syntax graph, go to each child and make a BoundingBoxTreeNode, connect the corresponding BBChildLink to it, and repeat the above process.
-- There are two kinds of bounding box here. One kind is the minimum size box that completely encloses a shape. The other kind is a constraint. When the second pass is performed, the shapes will be scaled, such that the shape bounding box gets as big as possible while still completely enclosed by some constraining bounding box.
-- See the comments in the skeleton code for BoundingBoxTreeNode and BBChildLink.
-- Once all the syntactic elements have been added to the bounding-box tree, go to the second pass, which performs scaling and translation of bounding boxes, which places them onto the virtual grid.
Second pass:
-- start with a default root bounding box that represents the entire graph. This is the "root" bounding box. It's origin is at 0,0 on the virtual grid.
-- this root BB is made the parent constraining BB
-- set the root node of the bounding-box tree as the current BoundingBoxTreeNode and begin:
-- <loop>
-- generate the minimum enclosing bounding box for the BoundingBoxTreeNode's shape together with all of its child nodes. Leave the BoundingBoxTreeNode's shape where it is, so the generated enclosing BB will have its origin placed relative to the shape's origin, but possibly shifted due to the ports around the shape.
-- calculate the scaling factor that will make the enclosing bounding box as large as possible while still being enclosed by the parent constraining bounding box.
-- apply the scaling factor (which moves the origins, as well as changes the sizes of the BBs)
-- calculate the translation to apply to the resized minimum enclosing BB to shift its origin to match the origin of the parent constraining BB
-- apply that translation to the shape's enclosing bounding box and each of its children's constraining bounding boxes. Those bounding boxes are now at their final placement on the virtual grid, at their final size.
-- generate the DisplayElement for the BoundingBoxTreeNode's shape, and add it to the DisplayList.
-- now, repeat the process for the contents of each child constraining BB:
In turn, set the child constraining BB to be the parent constraining BB.. and
set the BBChildLink's node as the current BoundingBoxTreeNode.. and go to the
-- (Note, out of interest, that as one descends the bounding-box tree, the scaling factors multiply, and translations add..)
-- When this process is complete for the entire syntax-graph, send the DisplayList to the Display.