Cairo FAQ Transformations

Материал из Wiki.crossplatform.ru

(Различия между версиями)
Перейти к: навигация, поиск
(Новая: In this part of the Cairo graphics programming tutorial, we will talk about transformations. <b>An affine transform</b> is composed of zero or more linear transformations (rotation, sca...)
(Удалено по требованию автора...)
 
Строка 1: Строка 1:
-
In this part of the Cairo graphics programming tutorial, we will talk about transformations.
 
-
<b>An affine transform</b> 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 <b>rotation</b> is a transformation that moves a rigid body around a fixed point.
 
-
A <b>scaling</b> is a transformation that enlarges or diminishes objects. The scale factor is the same in all directions.
 
-
A <b>translation</b> is a transformation that moves every point a constant distance in a specified direction.
 
-
A <b>shear</b> is a transformation that moves an object perpendicular to a given 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.
 
-
 
-
<source lang="cpp">
 
-
#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;
 
-
}
 
-
</source>
 
-
 
-
The examle draws a rectangle. Then we do a translation and draw the same rectangle again.
 
-
 
-
<source lang="cpp">
 
-
cairo_translate(cr, 100, 100);
 
-
 
-
</source>
 
-
 
-
The <b>cairo_translate()</b> 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.
 
-
 
-
[[image: cairo_faq_translate.png | center]]
 
-
 
-
== Rotation ==
 
-
 
-
The next example demonstrates a rotation.
 
-
 
-
<source lang="cpp">
 
-
#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;
 
-
}
 
-
</source>
 
-
 
-
The example draws a rectangle, performs a translation and a rotation and draws the same rectangle again.
 
-
 
-
<source lang="cpp">
 
-
cairo_translate(cr, 150, 100);
 
-
cairo_rotate(cr, M_PI/2);
 
-
</source>
 
-
 
-
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°.
 
-
 
-
[[image: cairo_faq_rotate.png | center]]
 
-
 
-
== Scale ==
 
-
 
-
The next example demonstrates a scaling of an object.
 
-
 
-
<source lang="cpp">
 
-
#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;
 
-
}
 
-
</source>
 
-
 
-
This time the example makes the initial rectangle smaller and then bigger by a specific scale factor.
 
-
 
-
<source lang="cpp">
 
-
cairo_save(cr);
 
-
...
 
-
cairo_restore(cr);
 
-
</source>
 
-
 
-
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 <b>cairo_restore()</b> functions.
 
-
 
-
<source lang="cpp">
 
-
cairo_translate(cr, 130, 30);
 
-
cairo_scale(cr, 0.7, 0.7);
 
-
 
-
</source>
 
-
 
-
Here we shift the user space origin an scale it by a factor of 0.7.
 
-
 
-
[[image: cairo_faq_scale.png | center]]
 
-
 
-
== Shear ==
 
-
 
-
In the following example we perform shearing.
 
-
 
-
<source lang="cpp">
 
-
#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;
 
-
}
 
-
 
-
</source>
 
-
 
-
In this code example, we perform two shear transformations. For a shear transformation, we do not have a special  function. We must use matrices.
 
-
 
-
<source lang="cpp">
 
-
cairo_matrix_t matrix;
 
-
</source>
 
-
 
-
The <b>cairo_matrix_t</b> is a structure that holds an affine transformation.
 
-
 
-
<source lang="cpp">
 
-
cairo_matrix_init(&matrix,
 
-
    1.0, 0.5,
 
-
    0.0, 1.0,
 
-
    0.0, 0.0);
 
-
 
-
cairo_transform (cr, &matrix);
 
-
</source>
 
-
 
-
This transformation  shears y values by 0.5 of the x values.
 
-
 
-
<source lang="cpp">
 
-
cairo_matrix_init(&matrix,
 
-
    1.0, 0.0,
 
-
    0.7, 1.0,
 
-
    0.0, 0.0);
 
-
 
-
cairo_transform(cr, &matrix);
 
-
 
-
</source>
 
-
 
-
And this transformation multiplies the x value of each coordinate by 0.7 of the y.
 
-
 
-
[[image: cairo_faq_shear.png | center]]
 
-
 
-
== Ellipses ==
 
-
 
-
In the following example we create an complex shape by rotating a bunch of ellipses.
 
-
 
-
<source lang="cpp">
 
-
#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;
 
-
}
 
-
 
-
</source>
 
-
 
-
<source lang="cpp">
 
-
cairo_translate(cr, width/2, height/2);
 
-
cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);
 
-
cairo_stroke(cr);
 
-
</source>
 
-
 
-
In the middle of the GTK+ window, we create a circle. This will be a bounding circle for our ellipses.
 
-
 
-
<source lang="cpp">
 
-
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);
 
-
}
 
-
</source>
 
-
 
-
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.
 
-
 
-
[[image: cairo_faq_ellipserotate.png | center]]
 
-
 
-
== Star ==
 
-
 
-
The next example shows a rotating and scaling star.
 
-
 
-
<source lang="cpp">
 
-
#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;
 
-
}
 
-
 
-
</source>
 
-
 
-
In this example, we create a star object. We will translate it, rotate it and scale it.
 
-
 
-
<source lang="cpp">
 
-
cairo_translate(cr, width / 2, height / 2 );
 
-
cairo_rotate(cr, angle);
 
-
cairo_scale(cr, scale, scale
 
-
</source>
 
-
 
-
We shift the star into the middle of the window. Rotate it and scale it.
 
-
 
-
<source lang="cpp">
 
-
  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);
 
-
 
-
</source>
 
-
 
-
Here we draw the star object.
 
-
 
-
<source lang="cpp">
 
-
if ( scale < 0.01 ) {
 
-
    delta = -delta;
 
-
} else if (scale > 0.99) {
 
-
    delta = -delta;
 
-
}
 
-
</source>
 
-
 
-
These line control the growing or shrinking of the star object.
 
-
 
-
[[image: cairo_faq_star.gif | center]]
 
-
 
-
 
-
[[Категория:GTK+]]
 
-
[[Категория:Cairo]]
 

Текущая версия на 11:30, 7 апреля 2009