Re: Extents under rotation

Uli Schlachter <[email protected]>
Newsgroups gmane.comp.lib.cairo
Message-ID <[email protected]>
Hi again,

On 07.12.2013 08:31, Donn wrote:
> On 06/12/2013 17:13, Bill Spitzak wrote:
>> The matrices are multiplied before this is done, so this error does not
>> accumulate. For instance if you rotate by 45 degrees and then back by 45
>> degrees, it will act like a rotation of 0 and produce your expected
>> bounding box, rather than enlarging the bounding box twice.
>
> I'm sure I will face these things soon when I institute picking beyond a
> basic bbox hit approach.
>
>
>
> Bill,
> Do you have a way to draw a tight bbox in user-space around a path?
[...]

I was bored, so:

See the attached test program (written in C since I don't know vala). It uses 
three different ways for getting the extents of a circle.

1) cairo_path_extents()
2) cairo_path_extents() after cairo_identity_matrix()
3) Some slow calculations with cairo_copy_path_flat(). See get_path_extents().

The first row is painted without any rotation (I needed this since I made a typo 
at first with x1/y1/x2/y2 that I took a while to find) with the calculations as 
mentioned above.

The second row is rotated by 67° and the individual rectangles are again 
calculated as above.

Hope this helps,
Uli

-- 
cairo mailing list
[email protected]
http://lists.cairographics.org/mailman/listinfo/cairo
img.png (image/png, 8.8 KB) - not displayed
test.c (text/x-csrc, 2.3 KB)
#include <cairo.h>
#include <math.h>
#include <assert.h>

static cairo_t *get_surface(int width, int height)
{
	cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
	cairo_t *cr = cairo_create (surface);
	cairo_surface_destroy (surface);
	return cr;
}

static void get_path_extents(cairo_t *cr, double *x1, double *y1, double *x2, double *y2)
{
	int i, had_point = 0;
	cairo_path_t *path = cairo_copy_path_flat (cr);

	*x1 = *y1 = *x2 = *y2 = 0;

	for (i=0; i < path->num_data; i += path->data[i].header.length) {
		cairo_path_data_t *data = &path->data[i];
		switch (data->header.type) {
		case CAIRO_PATH_MOVE_TO:
		case CAIRO_PATH_LINE_TO:
			if (!had_point) {
				*x1 = *x2 = data[1].point.x;
				*y1 = *y2 = data[1].point.y;
				had_point = 1;
			} else {
				*x1 = fmin(*x1, data[1].point.x);
				*y1 = fmin(*y1, data[1].point.y);
				*x2 = fmax(*x2, data[1].point.x);
				*y2 = fmax(*y2, data[1].point.y);
			}
			break;
		case CAIRO_PATH_CLOSE_PATH:
			/* Nothing to do here */
			break;
		case CAIRO_PATH_CURVE_TO:
		default:
			assert (0);
		}
	}

	cairo_path_destroy (path);
}

static void do_row(cairo_t *cr)
{
	double x1, y1, x2, y2;
	double step_x, step_y;

	cairo_save (cr);
	step_x = 100;
	step_y = 0;
	cairo_device_to_user_distance (cr, &step_x, &step_y);

	cairo_arc (cr, 50, 50, 30, 0, 2 * M_PI);
	cairo_path_extents (cr, &x1, &y1, &x2, &y2);
	cairo_rectangle (cr, x1, y1, x2-x1, y2-y1);
	cairo_stroke (cr);
	cairo_translate (cr, step_x, step_y);

	cairo_arc (cr, 50, 50, 30, 0, 2 * M_PI);
	cairo_save (cr);
	cairo_identity_matrix (cr);
	cairo_path_extents (cr, &x1, &y1, &x2, &y2);
	cairo_rectangle (cr, x1, y1, x2-x1, y2-y1);
	cairo_stroke (cr);
	cairo_restore (cr);
	cairo_translate (cr, step_x, step_y);

	cairo_arc (cr, 50, 50, 30, 0, 2 * M_PI);
	get_path_extents (cr, &x1, &y1, &x2, &y2);
	cairo_rectangle (cr, x1, y1, x2-x1, y2-y1);
	cairo_stroke (cr);
	cairo_translate (cr, step_x, step_y);

	cairo_restore (cr);
}

int main(void)
{
	cairo_t *cr = get_surface(100 * 3, 100 * 2);

	cairo_set_source_rgb (cr, 0, 0, 0);
	cairo_paint (cr);

	cairo_set_source_rgb (cr, 1, 1, 1);
	do_row (cr);

	cairo_translate (cr, 80, 100);
	cairo_rotate (cr, 67*M_PI/180);
	do_row (cr);

	cairo_surface_write_to_png (cairo_get_target (cr), "img.png");
	cairo_destroy (cr);
	return 0;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.