Discussion:
[cairo] Transformations usage
Daniele Dario
2017-12-06 10:46:26 UTC
Permalink
Hello everyone,
I'm trying to create my own widget on GTK+ to display a clip of a 2D
map where I placed some polygons. The point of view can be moved inside
the map and may rotate.

The main idea is that I draw the map on a cairo_surface_t once and than
on expose event I use coordinates of POV to center the device surface
to paint on the map, rotate the map, scale, clip and paint.

To start I tried to use a transformation matrix to be able to draw
using native coordinates on a surface but I'm getting confused on how
transformation affects drawing.

At the end of the code below I expect to have a map painted on the
surface. In order to paint it on the widget I'd need another
transformation because: 
- map size in the example is 165x165 and scale is 1 pixel = 1 [m]
- widget will have a different ratio

Can someone point me out on some examples or with some hint on how to
achieve what I'm looking for?

Thanks in advance,
Daniele.

/*
 * Create a surface for the map:
 *  - 1 pixel = 1 [m]
 *  - assume map width >= height
 *  - coordinates of polygons are relative to an
 *    arbitrary origin placed at
 *    O = (x = height/2, y = height/2)
 */
gint site_width = 165;
gint site_height = 165;
cairo_surface_t *map = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
site_width, site_height);

/* Origin coordinates are relative */
gdouble offset = site_height / 2.;

/* Create a context to draw on the surface */
cr = cairo_create (map);

/*
 * FIXME: Need a transformation in order to use
 * coordinates relative to O:
 *  - x axis is correct just needs translation of offset
 *  - y axis is inverse and needs translation of offset
 */
cairo_matrix_t mat;
cairo_matrix_init (&mat, 1.0, 0.0, 0.0, -1.0, offset, offset);
cairo_transform (cr, &mat);

/* TODO: draw all objects on the map */
/* Array of (x,y) coordinates to represent a polygon */
GtkScuiPoint v1 [4] = {
{ -20., -40.},
{ -20.,   0.},
{ -50.,   0.},
{ -50., -40.}
};

//cairo_set_line_width (cr, 0.1);

cairo_set_source_rgba (cr, 1., 0., 0., .7);

/* Wrapper to draw and fill polygon  */
scui_cairo_draw_polygon (cr, TRUE, v1, 4);

GtkScuiPoint v2 [4] = {
{ 40.,  0.},
{ 40., 20.},
{ 20., 20.},
{ 20.,  0.}
};

cairo_set_source_rgba (cr, 0., 0., 1., .7);

/* Wrapper to draw an empty polygon  */
scui_cairo_draw_polygon (cr, FALSE, v2, 4);

/* TODO: Store surface to avoid re-painting it */

/* Destroy context */
cairo_destroy (cr);
--
cairo mailing list
***@cairographics.org
https://lists.cairographics.org/mailman/listinfo/
Bill Spitzak
2017-12-06 18:29:33 UTC
Permalink
It is probably easier to use a series of cairo transforms. But I
think the main problem is that the y in the matrix needs to be
height-offset.

If you have a point XY in your map that you want to be in the center
of the window, I would do this:

cairo_translate(cr, -X, -Y); // put center at 0,0
cairo_scale(cr, 1, -1); // flip from y-up to y-down
cairo_rotate(cr, r); // rotate ccw by r degrees
cairo_scale(cr, s, s); // scale where s is pixels/meter
cairo_translate(cr, w/2, h/2); // move center back to center of
window of size w×h
Post by Daniele Dario
Hello everyone,
I'm trying to create my own widget on GTK+ to display a clip of a 2D
map where I placed some polygons. The point of view can be moved inside
the map and may rotate.
The main idea is that I draw the map on a cairo_surface_t once and than
on expose event I use coordinates of POV to center the device surface
to paint on the map, rotate the map, scale, clip and paint.
To start I tried to use a transformation matrix to be able to draw
using native coordinates on a surface but I'm getting confused on how
transformation affects drawing.
At the end of the code below I expect to have a map painted on the
surface. In order to paint it on the widget I'd need another
- map size in the example is 165x165 and scale is 1 pixel = 1 [m]
- widget will have a different ratio
Can someone point me out on some examples or with some hint on how to
achieve what I'm looking for?
Thanks in advance,
Daniele.
/*
* - 1 pixel = 1 [m]
* - assume map width >= height
* - coordinates of polygons are relative to an
* arbitrary origin placed at
* O = (x = height/2, y = height/2)
*/
gint site_width = 165;
gint site_height = 165;
cairo_surface_t *map = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
site_width, site_height);
/* Origin coordinates are relative */
gdouble offset = site_height / 2.;
/* Create a context to draw on the surface */
cr = cairo_create (map);
/*
* FIXME: Need a transformation in order to use
* - x axis is correct just needs translation of offset
* - y axis is inverse and needs translation of offset
*/
cairo_matrix_t mat;
cairo_matrix_init (&mat, 1.0, 0.0, 0.0, -1.0, offset, offset);
cairo_transform (cr, &mat);
/* TODO: draw all objects on the map */
/* Array of (x,y) coordinates to represent a polygon */
GtkScuiPoint v1 [4] = {
{ -20., -40.},
{ -20., 0.},
{ -50., 0.},
{ -50., -40.}
};
//cairo_set_line_width (cr, 0.1);
cairo_set_source_rgba (cr, 1., 0., 0., .7);
/* Wrapper to draw and fill polygon */
scui_cairo_draw_polygon (cr, TRUE, v1, 4);
GtkScuiPoint v2 [4] = {
{ 40., 0.},
{ 40., 20.},
{ 20., 20.},
{ 20., 0.}
};
cairo_set_source_rgba (cr, 0., 0., 1., .7);
/* Wrapper to draw an empty polygon */
scui_cairo_draw_polygon (cr, FALSE, v2, 4);
/* TODO: Store surface to avoid re-painting it */
/* Destroy context */
cairo_destroy (cr);
--
cairo mailing list
https://lists.cairographics.org/mailman/listinfo/cairo
--
cairo mailing list
***@cairographics.org
https:/
Daniele Dario
2017-12-11 12:23:55 UTC
Permalink
Hi Bill,
thanks for the hints.

Spent some time to figure out how it works but finally I got something
working :-)

The first issue I had was that cairo_translate (cr, -X, -Y) seems
wrong: if I use X, Y I get it working properly.

Daniele.
It is  probably easier to use a series of cairo transforms. But I
think the main problem is that the y in the matrix needs to be
height-offset.
If you have a point XY in your map that you want to be in the center
 cairo_translate(cr, -X, -Y); // put center at 0,0
 cairo_scale(cr, 1, -1); // flip from y-up to y-down
 cairo_rotate(cr, r); // rotate ccw by r degrees
 cairo_scale(cr, s, s); // scale where s is pixels/meter
 cairo_translate(cr, w/2, h/2); // move center back to center of
window of size w×h
Post by Daniele Dario
Hello everyone,
I'm trying to create my own widget on GTK+ to display a clip of a 2D
map where I placed some polygons. The point of view can be moved inside
the map and may rotate.
The main idea is that I draw the map on a cairo_surface_t once and than
on expose event I use coordinates of POV to center the device surface
to paint on the map, rotate the map, scale, clip and paint.
To start I tried to use a transformation matrix to be able to draw
using native coordinates on a surface but I'm getting confused on how
transformation affects drawing.
At the end of the code below I expect to have a map painted on the
surface. In order to paint it on the widget I'd need another
- map size in the example is 165x165 and scale is 1 pixel = 1 [m]
- widget will have a different ratio
Can someone point me out on some examples or with some hint on how to
achieve what I'm looking for?
Thanks in advance,
Daniele.
/*
 *  - 1 pixel = 1 [m]
 *  - assume map width >= height
 *  - coordinates of polygons are relative to an
 *    arbitrary origin placed at
 *    O = (x = height/2, y = height/2)
 */
gint site_width = 165;
gint site_height = 165;
cairo_surface_t *map = cairo_image_surface_create
(CAIRO_FORMAT_ARGB32,
        site_width, site_height);
/* Origin coordinates are relative */
gdouble offset = site_height / 2.;
/* Create a context to draw on the surface */
cr = cairo_create (map);
/*
 * FIXME: Need a transformation in order to use
 *  - x axis is correct just needs translation of offset
 *  - y axis is inverse and needs translation of offset
 */
cairo_matrix_t mat;
cairo_matrix_init (&mat, 1.0, 0.0, 0.0, -1.0, offset, offset);
cairo_transform (cr, &mat);
/* TODO: draw all objects on the map */
/* Array of (x,y) coordinates to represent a polygon */
GtkScuiPoint v1 [4] = {
        { -20., -40.},
        { -20.,   0.},
        { -50.,   0.},
        { -50., -40.}
};
//cairo_set_line_width (cr, 0.1);
cairo_set_source_rgba (cr, 1., 0., 0., .7);
/* Wrapper to draw and fill polygon  */
scui_cairo_draw_polygon (cr, TRUE, v1, 4);
GtkScuiPoint v2 [4] = {
        { 40.,  0.},
        { 40., 20.},
        { 20., 20.},
        { 20.,  0.}
};
cairo_set_source_rgba (cr, 0., 0., 1., .7);
/* Wrapper to draw an empty polygon  */
scui_cairo_draw_polygon (cr, FALSE, v2, 4);
/* TODO: Store surface to avoid re-painting it */
/* Destroy context */
cairo_destroy (cr);
--
cairo mailing list
https://lists.cairographics.org/mailman/listinfo/cairo
--
cairo mailing list
***@cairographics.org
https:
c***@aol.com
2017-12-06 19:03:16 UTC
Permalink
Hi Daniele,

Is the map background coming from a file or a pixbuf, or are you drawing the map entirely with Cairo?

Cairo can draw pretty fast so usually you don't have to worry about storing a surface and reusing it in GTK. A good technique though. If you draw a complicated background that doesn't change often, it can speed drawing up. There is a GTK gauge widget that uses some of those ideas.

https://github.com/cecashon/OrderedSetVelociRaptor/tree/master/Misc/AdjustableGauge

For transformations, there is a GTK drawing program that might be helpful.

https://github.com/cecashon/OrderedSetVelociRaptor/blob/master/Misc/cairo_drawings/bezier_points1.c

It will draw polygons and let you drag points around for interactive drawing along with transforms and animation. If you are using Cairo to draw everything with transforms it might give you some ideas.

Eric
Daniele Dario
2017-12-11 12:31:59 UTC
Permalink
Hi Eric,
thanks a lot for the examples :-)

After a lot of tries and with the help of Bill, I got something working
finally.

The map background doesn't change so the idea to draw it once and save
it on a surface. Than the operations will just be
translations/rotations to update view.

Again thanks,
Daniele.
Post by c***@aol.com
Hi Daniele,
Is the map background coming from a file or a pixbuf, or are you
drawing the map entirely with Cairo?
Cairo can draw pretty fast so usually you don't have to worry about
storing a surface and reusing it in GTK. A good technique though. If
you draw a complicated background that doesn't change often, it can
speed drawing up. There is a GTK gauge widget that uses some of those
ideas.
https://github.com/cecashon/OrderedSetVelociRaptor/tree/master/Misc/A
djustableGauge
For transformations, there is a GTK drawing program that might be helpful.
https://github.com/cecashon/OrderedSetVelociRaptor/blob/master/Misc/c
airo_drawings/bezier_points1.c
It will draw polygons and let you drag points around for interactive
drawing along with transforms and animation. If you are using Cairo
to draw everything with transforms it might give you some ideas.
Eric
--
cairo mailing list
***@cairographics.org
https://lists.cairogr
Continue reading on narkive:
Loading...