Basic Canvas Architecture

This section introduces the architecture of the canvas, including the arrangement of items into hierarchical groups, and the many coordinate systems involved in using the canvas.

GnomeCanvasGroup

Canvas items are arranged in a tree structure. You can group items together, to be moved as a unit; canvas architect Federico Mena Quintero likes to use a circuit diagram editor as an example. You might group together the shapes representing each logic gate, so you could manipulate the logic gate as a unit. You could also collect several logic gates into a single component; that is, groups can contain subgroups. Within each group, the canvas maintains a stacking order; objects higher in the stacking order obscure objects lower in the stacking order.

To implement this, the canvas comes with a special kind of canvas item called GnomeCanvasGroup. As the name suggests, a canvas group groups a number canvas items together so you can manipulate the child items as a single item. A GnomeCanvasGroup is invisible; to render itself, it simply recurses its children, rendering each of them in turn. When you create a new GnomeCanvas, a default group called the "root" is created for you. All canvas items are added somewhere below the root group. The canvas widget only deals with the root canvas item directly; all other canvas items are managed by their parent group.

An accessor function is provided to access the root canvas group, shown in Figure 1.

       #include <libgnomeui/gnome-canvas.h>
      

GnomeCanvasGroup* gnome_canvas_root(GnomeCanvas* canvas);

Figure 1. Root Group Accessor

Items must always be part of a group; there is no such thing as an "orphan" canvas item. When you create an item, you must specify its canvas group. It is also possible to reparent items after creation. However, items are permanently bound to the GnomeCanvas they were created on; it is not permitted to reparent an item to a group on a different canvas.

Coordinates

Many of the features of the canvas are implemented via translations between different coordinate systems. Canvas items can be moved, rotated, or scaled via affine transformations, described in more detail below. (Short version: an affine transformation is a way to convert from one coordinate system to another.) Here are the important coordinate systems which come up when using the canvas and writing custom canvas items:

When using preexisting canvas items, you will mostly be interested in world and item coordinates. When writing your own items, you will also need to use canvas and buffer coordinates.

There are two ways to convert between the various coordinate systems; one way is to obtain and use affines directly---this is described in the next section. The easy way is to use one of the convenience functions provided for the purpose, shown in Figure 2. Conversion between canvas and item coordinates requires you to convert to world coordinates first as an intermediate step. There is no function to convert to or from buffer coordinates, because this is a simple matter of subtracting the buffer offsets from the canvas coordinates (canvas to buffer), or adding the buffer offsets to the buffer coordinates (buffer to canvas).

       #include <libgnomeui/gnome-canvas.h>
      

void gnome_canvas_w2c(GnomeCanvas* canvas, double wx, double wy, int* cx, int* cy);

void gnome_canvas_w2c_d(GnomeCanvas* canvas, double wx, double wy, double* cx, double* cy);

void gnome_canvas_c2w(GnomeCanvas* canvas, int cx, int cy, double* wx, double* wy);

void gnome_canvas_item_w2i(GnomeCanvasItem* item, double* x, double* y);

void gnome_canvas_item_i2w(GnomeCanvasItem* item, double* x, double* y);

void gnome_canvas_window_to_world(GnomeCanvas* canvas, double winx, double winy, double* worldx, double* worldy);

void gnome_canvas_world_to_window(GnomeCanvas* canvas, double worldx, double worldy, double* winx, double* winy);

Figure 2. Coordinate Conversions

Affine Transformations

An affine is a transformation matrix made up of six real numbers that can be applied to an ordered pair. Depending on the contents of the affine, the point it is applied to can be:

Conceptually, an affine defines a relationship between points on a plane. For any point (A,B), the affine defines a single corresponding transformed point; the mapping is one-to-one, so given the transformed point you can determine the original point.

Affines have interesting properties that make them useful in computer graphics. Most importantly, they can be composed, concatenated, or multiplied (the three terms are synonymous). You can compose any number of affines to create a single affine; applying the single affine has the same effect as applying each of the original affines in order. Note that the order of composition is important! Unlike multiplication, affine composition is not commutative (which is a reason to avoid the term "multiply" in this context).

libart_lgpl contains a module for affine manipulation. It represents affines as an array of six doubles. Its affine functions are shown in Figure 3.

       #include <libart_lgpl/art_affine.h>
      

void art_affine_point(ArtPoint* dst, const ArtPoint* src, const double affine[6]);

void art_affine_invert(double dst_affine[6], const double src_affine[6]);

void art_affine_multiply(double dst[6], const double src1[6], const double src2[6]);

void art_affine_identity(double dst[6]);

void art_affine_scale(double dst[6], double sx, double sy);

void art_affine_rotate(double dst[6], double theta);

void art_affine_translate(double dst[6], double tx, double ty);

int art_affine_rectilinear(const double src[6]);

Figure 3. Affine Manipulation

art_affine_point() applies an affine to a point. The affine is applied to the second argument (src) and the result is copied into the first argument (dst). An ArtPoint is simply:


typedef struct _ArtPoint ArtPoint;

struct _ArtPoint {
  double x, y;
};

      

Affines can be inverted. If an affine converts points in coordinate system A into points in coordinate system B, its inverse converts points in coordinate system B into points in coordinate system A. art_affine_invert() fills its first argument with the inverse of its second.

art_affine_multiply() composes two affines as described earlier in this section, placing the result in its first argument.

Four functions are provided to create affines with particular properties.

art_affine_rectilinear() returns TRUE if the affine rotates rectangles aligned to the axes in such a way that they remain aligned to the axes. That is, it returns TRUE if the rotation is 0, 90, 180, or 270 degrees.

You can ask the canvas widget to compute affines which convert between its various coordinate systems. These functions are shown in Figure 4; each of them fills an array you pass in with the affine being requested.

       #include <libgnomeui/gnome-canvas.h>
      

void gnome_canvas_item_i2w_affine(GnomeCanvasItem* item, double affine[6]);

void gnome_canvas_item_i2c_affine(GnomeCanvasItem* item, double affine[6]);

void gnome_canvas_w2c_affine(GnomeCanvas* canvas, double affine[6]);

Figure 4. Canvas Affines