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

转载 2012年03月30日 08:22:56

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.A rotation is a transformation that moves a rigid body around a fixed point.A scaling is a transformation that enlarges or diminishes objects. The scale factor is the same in all directions.A translation is a transformation that moves every point a constant distance in a specified direction.A shear 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 gboolean
on_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 gboolean
on_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 gboolean
on_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 of cairo_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 gboolean
on_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 gboolean
on_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 gboolean
on_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 gboolean
time_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.



The Cairo graphics tutorial -------Clipping and masking

In this part of the Cairo tutorial, we will talk about clipping and masking. Clipping ...

The Cairo graphics tutorial -------Transparency

In this part of the Cairo C API tutorial, we will talk about transparency. We will provide some basi...

The Cairo graphics tutorial -------Custom GTK widget

In this part of the Cairo graphics tutorial, we will create a custom GTK widget, where we will use t...

The Cairo graphics tutorial -------Shapes and fills in Cairo

In this part of the Cairo tutorial, we will create some basic and more advanced shapes. We will fill...

The Cairo graphics tutorial -------Cairo backends

The Cairo library supports various backends. In this section of the Cairo graphics tutorial, we will...

The Cairo graphics tutorial -------Basic drawing in Cairo

In this part of the Cairo graphics tutorial, we will draw some basic primitives. We will draw simple...

The Cairo graphics tutorial -------Text in Cairo

In this part of the Cairo graphics tutorial, we will work with text. Soulmate In the first...

The Cairo graphics tutorial -------Cairo definitions

Cairo definitions In this part of the Cairo graphics tutorial, we will provide some useful definiti...

OpenGL ES Tutorial for Android – Part III – Transformations

这篇文章里面介绍如何知道rotatef非常有用。在一个就是pushmatrix和popmatrix Last tutorial was about building your polygons. Th...

Core Graphics Tutorial Part 3: Patterns and Playgrounds

原文链接:Core Graphics Tutorial Part 3: Patterns and Playgrounds 原文作者: Caroline Begbie 译文出自:开发者前线 www.d...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:The Cairo graphics tutorial -------Transformations
举报原因:
原因补充:

(最多只允许输入30个字)