The Cairo graphics tutorial -------Transformations

来源:互联网 发布:基金产品经理 知乎 编辑:程序博客网 时间:2024/05/19 04:51

In this part of the Cairo graphics programming tutorial, we will talk about transformations.


An affine transform is composed of zero or more linear transformations (rotation, scaling or shear) and translation (shift). Several linear transformations can be combined into a single matrix.Arotation is a transformation that moves a rigid body around a fixed point.Ascaling is a transformation that enlarges or diminishes objects. The scale factor is the same in all directions.Atranslation is a transformation that moves every point a constant distance in a specified direction.Ashear is a transformation that moves an object perpendicular to agiven axis, with greater value on one side of the axis than the other.

sources: (wikipedia.org, freedictionary.com)

Translation

The following example describes a simple translation.

#include <cairo.h>#include <gtk/gtk.h>static gbooleanon_expose_event(GtkWidget *widget,    GdkEventExpose *event,    gpointer data){  cairo_t *cr;  cr = gdk_cairo_create (widget->window);  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  cairo_rectangle(cr, 20, 20, 80, 50);  cairo_stroke_preserve(cr);  cairo_set_source_rgb(cr, 1, 1, 1);  cairo_fill(cr);  cairo_translate(cr, 100, 100);  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  cairo_rectangle(cr, 20, 20, 80, 50);  cairo_stroke_preserve(cr);  cairo_set_source_rgb(cr, 1, 1, 1);  cairo_fill(cr);  cairo_destroy(cr);  return FALSE;}int main(int argc, char *argv[]){  GtkWidget *window;  gtk_init(&argc, &argv);  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  g_signal_connect(window, "expose-event",      G_CALLBACK (on_expose_event), NULL);  g_signal_connect(window, "destroy",      G_CALLBACK (gtk_main_quit), NULL);  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  gtk_window_set_default_size(GTK_WINDOW(window), 300, 230);   gtk_widget_set_app_paintable(window, TRUE);  gtk_widget_show_all(window);  gtk_main();  return 0;}

The examle draws a rectangle. Then we do a translation and draw the same rectangle again.

 cairo_translate(cr, 100, 100);

The cairo_translate() function modifies the current transormation matrix by tranlating the user space origin.In our case we shift the origin by 100 units in both directions.


Translate
Figure: Translate

Rotation

The next example demonstrates a rotation.

#include <cairo.h>#include <gtk/gtk.h>#include <math.h>static gbooleanon_expose_event(GtkWidget *widget,    GdkEventExpose *event,    gpointer data){  cairo_t *cr;  cr = gdk_cairo_create (widget->window);  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  cairo_rectangle(cr, 20, 20, 80, 50);  cairo_stroke_preserve(cr);  cairo_set_source_rgb(cr, 1, 1, 1);  cairo_fill(cr);  cairo_translate(cr, 150, 100);  cairo_rotate(cr, M_PI/2);  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  cairo_rectangle(cr, 20, 20, 80, 50);  cairo_stroke_preserve(cr);  cairo_set_source_rgb(cr, 1, 1, 1);  cairo_fill(cr);  cairo_destroy(cr);  return FALSE;}int main(int argc, char *argv[]){  GtkWidget *window;  gtk_init(&argc, &argv);  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  g_signal_connect(window, "expose-event",      G_CALLBACK (on_expose_event), NULL);  g_signal_connect(window, "destroy",      G_CALLBACK (gtk_main_quit), NULL);  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  gtk_window_set_default_size(GTK_WINDOW(window), 300, 230);   gtk_widget_set_app_paintable(window, TRUE);  gtk_widget_show_all(window);  gtk_main();  return 0;}

The example draws a rectangle, performs a translation and a rotation and draws the same rectangle again.

 cairo_translate(cr, 150, 100); cairo_rotate(cr, M_PI/2);

First we shift the user space origin. Then we rotate it by 180°. Notice that we specify the angle in radians, not degrees.2M_PI = 360°.


Rotate
Figure: Rotate

Scale

The next example demonstrates a scaling of an object.

#include <cairo.h>#include <gtk/gtk.h>static gbooleanon_expose_event(GtkWidget *widget,    GdkEventExpose *event,    gpointer data){  cairo_t *cr;  cr = gdk_cairo_create (widget->window);  cairo_save(cr);  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  cairo_rectangle(cr, 20, 30, 80, 50);  cairo_stroke_preserve(cr);  cairo_set_source_rgb(cr, 1, 1, 1);  cairo_fill(cr);  cairo_restore(cr);  cairo_save(cr);  cairo_translate(cr, 130, 30);  cairo_scale(cr, 0.7, 0.7);  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  cairo_rectangle(cr, 0, 0, 80, 50);  cairo_stroke_preserve(cr);  cairo_set_source_rgb(cr, 1, 1, 1);  cairo_fill(cr);  cairo_restore(cr);  cairo_save(cr);  cairo_translate(cr, 220, 30);  cairo_scale(cr, 1.5, 1.5);  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  cairo_rectangle(cr, 0, 0, 80, 50);  cairo_stroke_preserve(cr);  cairo_set_source_rgb(cr, 1, 1, 1);  cairo_fill(cr);  cairo_restore(cr);  cairo_destroy(cr);  return FALSE;}int main(int argc, char *argv[]){  GtkWidget *window;  gtk_init(&argc, &argv);  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  g_signal_connect(window, "expose-event",      G_CALLBACK (on_expose_event), NULL);  g_signal_connect(window, "destroy",      G_CALLBACK (gtk_main_quit), NULL);  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  gtk_window_set_default_size(GTK_WINDOW(window), 360, 140);   gtk_widget_set_app_paintable(window, TRUE);  gtk_widget_show_all(window);  gtk_main();  return 0;}

This time the example makes the initial rectangle smaller and then bigger by a specific scale factor.

 cairo_save(cr); ... cairo_restore(cr);

We want to perform two scaling operations on the initial rectangle. Therefore, we need to save the initial transformation matrix. This is done by a pair ofcairo_save() and cairo_restore() functions.

 cairo_translate(cr, 130, 30); cairo_scale(cr, 0.7, 0.7);

Here we shift the user space origin an scale it by a factor of 0.7.


Scale
Figure: Scale

Shear

In the following example we perform shearing.

#include <cairo.h>#include <gtk/gtk.h>static gbooleanon_expose_event(GtkWidget *widget,    GdkEventExpose *event,    gpointer data){  cairo_t *cr;  cairo_matrix_t matrix;  cr = gdk_cairo_create (widget->window);  cairo_save(cr);  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  cairo_rectangle(cr, 20, 30, 80, 50);  cairo_stroke_preserve(cr);  cairo_set_source_rgb(cr, 1, 1, 1);  cairo_fill(cr);  cairo_restore(cr);  cairo_save(cr);  cairo_translate(cr, 130, 30);  cairo_matrix_init(&matrix,      1.0, 0.5,      0.0, 1.0,      0.0, 0.0);  cairo_transform (cr, &matrix);  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  cairo_rectangle(cr, 0, 0, 80, 50);  cairo_stroke_preserve(cr);  cairo_set_source_rgb(cr, 1, 1, 1);  cairo_fill(cr);  cairo_restore(cr);  cairo_save(cr);  cairo_translate(cr, 220, 30);  cairo_matrix_init(&matrix,      1.0, 0.0,      0.7, 1.0,      0.0, 0.0);  cairo_transform(cr, &matrix);  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  cairo_rectangle(cr, 0, 0, 80, 50);  cairo_stroke_preserve(cr);  cairo_set_source_rgb(cr, 1, 1, 1);  cairo_fill(cr);  cairo_restore(cr);  cairo_destroy(cr);  return FALSE;}int main(int argc, char *argv[]){  GtkWidget *window;  gtk_init(&argc, &argv);  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  g_signal_connect(window, "expose-event",      G_CALLBACK(on_expose_event), NULL);  g_signal_connect(window, "destroy",      G_CALLBACK(gtk_main_quit), NULL);  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  gtk_window_set_default_size(GTK_WINDOW(window), 360, 140);   gtk_widget_set_app_paintable(window, TRUE);  gtk_widget_show_all(window);  gtk_main();  return 0;}

In this code example, we perform two shear transformations. For a shear transformation, we do not have a special function. We must use matrices.

 cairo_matrix_t matrix;

The cairo_matrix_t is a structure that holds an affine transformation.

 cairo_matrix_init(&matrix,    1.0, 0.5,    0.0, 1.0,    0.0, 0.0); cairo_transform (cr, &matrix);

This transformation shears y values by 0.5 of the x values.

 cairo_matrix_init(&matrix,     1.0, 0.0,     0.7, 1.0,     0.0, 0.0); cairo_transform(cr, &matrix);

And this transformation multiplies the x value of each coordinate by 0.7 of the y.


Shear
Figure: Shear

Ellipses

In the following example we create an complex shape by rotating a bunch of ellipses.

#include <cairo.h>#include <gtk/gtk.h>static gbooleanon_expose_event(GtkWidget *widget,    GdkEventExpose *event,    gpointer data){  cairo_t *cr;  cr = gdk_cairo_create(widget->window);  gint width, height;  gtk_window_get_size(GTK_WINDOW(widget), &width, &height);   cairo_set_line_width(cr, 0.5);  cairo_translate(cr, width/2, height/2);  cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);  cairo_stroke(cr);  gint i;  cairo_save(cr);  for ( i = 0; i < 36; i++) {      cairo_rotate(cr, i*M_PI/36);      cairo_scale(cr, 0.3, 1);      cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);      cairo_restore(cr);      cairo_stroke(cr);      cairo_save(cr);  }  cairo_destroy(cr);  return FALSE;}int main(int argc, char *argv[]){  GtkWidget *window;  gtk_init(&argc, &argv);  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  g_signal_connect(G_OBJECT(window), "expose-event",      G_CALLBACK(on_expose_event), NULL);  g_signal_connect(G_OBJECT(window), "destroy",      G_CALLBACK(gtk_main_quit), NULL);  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  gtk_window_set_default_size(GTK_WINDOW(window), 350, 250);   gtk_widget_set_app_paintable(window, TRUE);  gtk_widget_show_all(window);  gtk_main();  return 0;}
 cairo_translate(cr, width/2, height/2); cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI); cairo_stroke(cr);

In the middle of the GTK+ window, we create a circle. This will be a bounding circle for our ellipses.

 cairo_save(cr); for ( i = 0; i < 36; i++) {     cairo_rotate(cr, i*M_PI/36);     cairo_scale(cr, 0.3, 1);     cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);     cairo_restore(cr);     cairo_stroke(cr);     cairo_save(cr); }

We create 36 ellipses along the path of our bounding circle. An ellipse is a scaled circle. We rotate the ellipses. Thus we create an interesting shape.


Ellipse rotate
Figure: Ellipse rotate

Star

The next example shows a rotating and scaling star.

#include <cairo.h>#include <gtk/gtk.h>#include <math.h>int points[11][2] = {     { 0, 85 },     { 75, 75 },     { 100, 10 },     { 125, 75 },     { 200, 85 },    { 150, 125 },     { 160, 190 },    { 100, 150 },     { 40, 190 },    { 50, 125 },    { 0, 85 } };static gbooleanon_expose_event(GtkWidget *widget,    GdkEventExpose *event,    gpointer data){  cairo_t *cr;  static gdouble angle = 0;  static gdouble scale = 1;  static gdouble delta = 0.01;  gint width, height;  gtk_window_get_size(GTK_WINDOW(widget), &width, &height);  cr = gdk_cairo_create(widget->window);  cairo_set_source_rgb(cr, 0, 0.44, 0.7);  cairo_set_line_width(cr, 1);  cairo_translate(cr, width / 2, height / 2 );  cairo_rotate(cr, angle);  cairo_scale(cr, scale, scale);  gint i;  for ( i = 0; i < 10; i++ ) {      cairo_line_to(cr, points[i][0], points[i][1]);  }  cairo_close_path(cr);  cairo_fill(cr);  cairo_stroke(cr);  if ( scale < 0.01 ) {      delta = -delta;  } else if (scale > 0.99) {      delta = -delta;  }  scale += delta;  angle += 0.01;  cairo_destroy(cr);  return FALSE;}static gbooleantime_handler (GtkWidget *widget){  if (widget->window == NULL) return FALSE;  gtk_widget_queue_draw(widget);  return TRUE;}int main(int argc, char *argv[]){  GtkWidget *window;  gtk_init(&argc, &argv);  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);  g_signal_connect(window, "expose-event",      G_CALLBACK(on_expose_event), NULL);  g_signal_connect(window, "destroy",      G_CALLBACK(gtk_main_quit), NULL);   gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  gtk_window_set_title(GTK_WINDOW(window), "star");  gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);   gtk_widget_set_app_paintable(window, TRUE);  g_timeout_add(10, (GSourceFunc) time_handler, (gpointer) window);    gtk_widget_show_all(window);  gtk_main();  return 0;}

In this example, we create a star object. We will translate it, rotate it and scale it.

 cairo_translate(cr, width / 2, height / 2 ); cairo_rotate(cr, angle); cairo_scale(cr, scale, scale 

We shift the star into the middle of the window. Rotate it and scale it.

  for ( i = 0; i < 10; i++ ) {      cairo_line_to(cr, points[i][0], points[i][1]);  }  cairo_close_path(cr);  cairo_fill(cr);  cairo_stroke(cr);

Here we draw the star object.

 if ( scale < 0.01 ) {     delta = -delta; } else if (scale > 0.99) {     delta = -delta; }

These line control the growing or shrinking of the star object.


Star
Figure: Star

In this part of the Cairo graphics tutorial, we talked about transformations.