An item, be it simple like a rectangle or more complex like a group, is always created relative to a group which is known as its parent, the group’s items are its children. The items form a tree whose nodes are the group items. The top-most node is known as the root group, of id 1, which is automatically created with TkZinc. By convention, the root group is its own parent. It is not possible to change the parent of the root group and it is not possible to delete it. However, it is possible to change the group of all other items after creation, and thus modify the item tree at any time. This is the use of the chggroup command.
The following attributes are composed down the item tree to form the resulting attribute value in the leaf items:
It may seem at first that there is a contradiction in this title, but there is not. It is possible to built complex objects from simple items simply by assembling those items together in a group (using other intervening groups if the need arise). Once this is done, it would be convenient if the whole acted as a single item, the top assembling group. It is already so for many commands that act on a group, it is possible to move, resize, rotate, restack, clone, hide, change the transparency, delete the group as a whole without knowing anything of its children. But when it comes to event dispatching, the group is completly transparent so far. So the event dispatch mecanism will try to locate the smallest most visible item containing the pointer and will trigger the associated bindings. Not exactly what we meant. So groups have a feature, the -atomic attribute, that is used to seal a group so that events cannot propagate past it downward. If an item part of an atomic group is under the pointer, TkZinc will try to trigger bindings associated with the atomic group not with the item under the pointer. This improves greatly the metaphor of an indivisible item.
It must also be noted that commands such as find 'enclosed'/'overlapping' or addtag 'enclosed'/'overlapping' act differently on an atomic group. Such search command will not traverse an atomic group. So if a part of the atomic group is enclosed or overlapping, the search command will return the atomic group and not its part.
A small program, Atomic groups is available as part of zinc-demos to demonstrate the atomic groups behaviour.
The items are displayed in a specific order which determines how they stack. This order is also important for associating events with items. The items are arranged in a display list for each group. The display list imposes a total ordering among its items. The group display lists are connected in a tree identical to the group tree and form a hierarchical display list. The items are drawn by traversing the display list from the least visible item to the most visible one. Each time a group is encoutered the traversal proceed with this group display list before resuming the upper display list traversal. The search to find the item that should receive an event is done in the opposite direction. In this way, items are drawn according to their relative stacking order and events are dispatched to the top-most item at a given location.
It is important to note as a consequence of this structuring, that items of a group are stacked between the items that are under the group and the items that are on top of the group. Thus, items of two groups cannot be intertwinned, they stack exactly as their groups stack, that is items of the underneath group a drawn then the items of the other group are drawn on top.
The item ordering imposed by the display lists can be ajusted in three ways. The two first are local to a group’s display list. The third can be used to rearrange between groups.
An item will catch an event if all the following conditions are satisfied:
An item satisfying all the above conditions can have its -visible set to false, or can be fully transparent (when using openGL). It will still catch the events.
In TkZinc each item is geometrically defined in its own coordinate space. So each time a new item is created, a new coordinate system is attached to it. This coordinate system must be related to the coordinate systems of the other items to place the items with respect to each other. This relationship is defined by an affine transformation associated with the item. This transformation establishes the relationship between an item and its group. The items being arranged in a tree by their groups, its possible via the transformations to place all the items in an absolute coordinate system known as the window space.
Just after item creation, the item transformation is set to identity, i.e the item coordinate system maps exactly on the system of its group. The commands translate , scale , rotate , skew can be used to modify this relationship to the effect of translating, enlarging, shrinking, rotating or skewing the item. It must be emphasized that those commands act on the relation between two coordinate spaces, not on the item geometry itself. If the goal is to change the item (except for groups, see next paragraph) geometry, the command coords may be more appropriate (but see below the command tapply ).
As it should be clear, groups are like any other items, they are defined in their own coordinate space and are assembled with their parents by transformations. This is a very powerful tool to manage the geometry of clusters of items. One must not refrain from using groups only to assign them a transformation task such as panning a whole set of items or scaling a set while another is kept in place in another group. For the developper convenience, the coords method on a group change its transformation. It defines the absolute translation applied to the group.
Another very interesting use of a group as a transformation tool is to manage a window coordinate space where the origin is not in the top left corner and where the Y axis goes from bottom to top. It is quite simple to write a function that is triggerred on the window resize event whose only goal is to compute a new transformation for the group. Other parts of the application and the other items are not aware of this happening. A good factorization example.
In fact, transformation are so useful that a whole set of functions are available to help use them in full. Apart from the already mentioned translate , scale , rotate , and skew commands, it is also possible to restore a transformation to its initial state, identity, with the treset command. It is also possible to compose a transformation with another name transform with the tcompose command.
An item transformation can be saved under a name, in fact creating a named transformation which can be manipulated just as an item transformation (i.e using translate, scale, rotate, treset). Once a transformation has been named it can be used to set the transformation of any item using with the command trestore . And it can be disposed of with the command tdelete .
An item can be physically modified by applying its own transformation to itself. This is the goal of the tapply command. It applies the item transformation to its own coordinates an then reset the item transformation. Visually nothing has changed but in fact the item is irrevocably modified. Be aware that if it is quite easy to undo a change in a transformation by using treset or by saving and then restoring a transformation, it is not so easy to revert a physical modification on an item. The exact order of the operations must be recorded and even then there is no shield against round off errors that will probably occur. This command may be used together with the translate, scale and rotate commands if someone really want, even after reading this paragraph, to implement the canvas move, scale and even rotate commands.
A predefined named transformation exists. Its name is identity and refers to the identity transform. it can not be modified by the user.
When dealing with mouse events and other sources of window coordinates, it is often useful to map the window coordinates to an appropriate coordinate space. The command transform is just what is needed to do so. It is powerful enough to be able to convert coordinates from any coordinate space to any other. A special provision has been made to facilitate conversion from window space to another space. The opposite is not impossible but rely on a small trick: the root group transformation must be left as identity (the default at creation time). In this way, it is possible to use the root group space, which is then the same as the window space, as the target space of the transform command.
If you need to manage many different transformations independently, it is a good practice to apply these transformations to different groups. For example, a group can be used for translation and an other group (father or son) for scaling.
When a rotation or a scale appear in a transformation, all items do not behave exactly in the same manner. For example text items do not scale or rotate. Only their position moves according to the rotation or the scaling factor. Here is how items react to the scale and rotation factors of the transformation.
However, every item has a couple of attributes -composescale and -composerotation that can be used to control how the scale and rotation factors are inherited from the parents’ transformations. These attributes default to true (i.e. rotation and scale from parents are meaningful, except for icon where these attributes defaulted to false). When one of these attributes is set to false the corresponding factor is reset from the inherited transformation. Scale factors are reset to 1.0 and rotation is reset to 0. Be careful that this applies to the inherited transformation, not to the item transformation itself which is composed after taking into account the composition attributes.
As you can see, the transformation process is quite powerful but complex. A small program, Tranformation testbed is available as part of zinc-demos to demonstrate the transformation capabilities of TkZinc. This is also a great resource to understand how it works and to tame its complexity. It is possible to use this program to test one’s idea on a given transformation problem before coding it as part of a complex application.
Groups can set a clip boundary before drawing their children. Thought of this feature as if a group can be made to act as a window on its children. Except that the window can have any shape you like to give it. Each group has a -clip attribute which can be set to an item of the group. This item, known as the clipper of the group, defines the shape of the clipping. All item types except group , track , waypoint , reticle and map can be used as clippers but the clipper must be a direct child of the clipped group. The clipper defines the shape of the clipping but is also drawn as a regular group item. It is typical to either mask explicitly the clipper by turning off its -visible attribute or to fill and lower it so it can act as the background. Of course, other creative uses can be found but be warned that the clipper outline will never be easthetically drawn due to round off or quantization errors, it is better to turn off borders or outlines in this case.
It is also possible to clip the root group (only on X11 system. Does not work on windows systems). Depending on the value of TkZinc options -reshape and -fullreshape , the clipping form can be used either to clip all items in the TkZinc widget, or reshape the TkZinc widget, or to propagate the TkZinc widget shape to the parent windows. In the latter case, this allows to build non-rectangulaire applications. This requires both the SHAPE X11 extension and a compliant Window Manager (fvwm is known to support non rectangulaire top windows). The clipping form should have a bounding box with the same ratio as the topwindow or some normalisation will occur. Example:
my $mw = MainWindow->new();
my $zinc = $mw->Zinc(-reshape => 1, -fullreshape => 1)->pack;
# creating a triangulaire curve
my $triangle= $zinc->add('curve',1, [ [0,0], [100,0], [50,100] ], -closed => 1);
# using the triangulaire curve to reshape both TkZinc and Mainwindow widgets
$zinc->itemconfigure(1, -clip => $triangle);
$zinc->add('arc',1, [ [0,0], [100,100] ], -filled => 1, -fillcolor => 'darkblue');