
CHAPTER 4 Structured Graphics

The painter object maintains a stack of cumulative transforms for each level of the traversal. You can see the stack being pushed and popped in lines 5 and 25 of figure 4-6. This is needed because parents who traverse multiple children depend on each child restoring the painter's transform to its original state before that child was traversed.
Note in figure 4-17 that layout glyphs--even though they do not store transformations--can also affect the painter's transformation during traversal. A box, for example, will pre-multiply a matrix representing the translation it allocates to the child being traversed.
Fresco's Transform interface, shown in figure 4-18, defines the data structure for the transformation matrix as well as operations for manipulating the matrix.
It's useful to examine the meaning of some of these operations. The operation transform_vertex() results in a pre-multiply of the transform by the given vertex. The result is stored in the same vertex. For example, the code
tx->transform_vertex(v);results in the multiplication shown in figure 4-19. (The transform object actually uses a 4x4 matrix. We use 3x3 in diagrams for simplicity.)

The inverse_transform_vertex() operation is similar except that the matrix is inverted before the multiply.
Calling translate() with a vertex v is effectively the same as post-multiplying by a matrix corresponding to the translation vertex v. For example, the code
Vertex v = { x,y,0 };
tx->translate(v);
results in the multiplication shown in figure 4-20.
The scale and rotate operations are similar. That is, both result in post-multiplies.
Figure 4-17 above showed how transforms are accumulated during traversal. We've also seen how the PickTraversal object can act as a snapshot of traversal state to a given hit glyph. Let's put these two concepts together in order to understand the geometry needed to initialize new circles and squares created when you click and release your mouse over white space.
When a mouse click occurs over white space, FiggyViewer's release() operation calls the add() function shown in figure 4-21 below. The x,y arguments passed in are the window-relative coordinates of the mouse click.
In the case of the circle, its center position is set to the mouse click coordinates x,y in line 6. The new circle's transformation is initialized with a copy of the transformation target_tx_ in line 15. The transform target_tx_ was initialized to be the cumulative transformation down to and including the root PolyFigure. (This can be seen in figure 4-14, page 45.) The next line in figure 4-21, line 16, takes this cumulative transformation and inverts it.
Thus (to state this more succinctly) the new circle's transform is initialized with the inverse of the cumulative transformation to the root PolyFigure. To understand why this is so, consider the matrix equation
shown in figure 4-22 below.
When draw traversal reaches the circle, the painter's matrix will equal the product [Tc x Tb x Ta]. The point Pc on the right side of the equal sign is the circle's center, which was set to the mouse click coordinates on line 6 of figure 4-21. The point on the left side of the equal sign represents the window coordinates where the circle's center will be drawn via the painter. Note that the point Pc also appears on the left side. This means that this equation describes the case where the circle's center always appears at the mouse click (just as it does with the Figgy program.)
Solving the equation yields the circle's transformation
which, again, is the inverse of the cumulative transformation to the root PolyFigure.
Using the inverse of a cumulative transformation is common when window-relative coordinates are used as input. One example can be seen in FiggyViewer's drag operation (shown in figure 4-7 on page 40). This is the code used to translate a circle when you click and drag on it. Referring to figure 4-7, the start and end vertices are set to window-relative coordinates on lines 6-9. These are used later (line 18) to translate the figure as follows:
tx->translate(v);We mentioned above that calling translate() is equivalent to post-multiplying by a matrix corresponding to the translation vertex v. This is represented by matrix Tv in figure 4-23.

But, for the correct translation to take place, it is important that Tv be in proper coordinate system. The reason for this can be understood intuitively: If, before dragging the circle, we had zoomed way in on the drawing, then a given window-relative distance would be small compared to our circle--Tv would contain too little translation. If, on the other hand, we had zoomed way out on our drawing, then the same window relative distance would be very big to our circle--and Tv would contain too much translation.
To rectify this, the window-relative distance is first inverse transformed by the cumulative transformation to the root PolyFigure. This is done in lines 11 and 12 of figure 4-7 on page 40. Now Tv's translation is in the same coordinate system as that of the circle.
Generated with CERN WebMaker