Example #1
0
/* Returns the length of path. path must be flattened
 * (i.e. only consist of line segments)
 */
static double _rsvg_path_length(cairo_path_t *path) {
    int i;
    double distance = 0.0;
    cairo_path_data_t *last_point, *cur_point, *last_move_to;

    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:
            last_move_to = data;
            last_point = &data[1];
            break;
        case CAIRO_PATH_CLOSE_PATH:
            // Make it look like it's a line_to to last_move_to
            data = last_move_to;
            // fall through
        case CAIRO_PATH_LINE_TO:
            cur_point = &data[1];
            distance += two_points_distance(last_point, cur_point);
            last_point = cur_point;
            break;
        default:
            g_assert_not_reached ();
        }
    }

    return distance;
}
/* Returns length of a Bezier curve. Seems like computing that analytically is not easy. The
 * code just flattens the curve using cairo and adds the length of segments. */
static double curve_length(
	double x0,
	double y0,
	double x1,
	double y1,
	double x2,
	double y2,
	double x3,
	double y3
) {
	cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_A8, 0, 0);
	cairo_t* cr = cairo_create(surface);
	double length = 0;
	cairo_path_t* path;
	cairo_path_data_t* data;
	cairo_path_data_t current_point;
	int i;

	current_point.point.x = 0.0;
	current_point.point.y = 0.0;

	cairo_surface_destroy(surface);
	cairo_move_to(cr, x0, y0);
	cairo_curve_to(cr, x1, y1, x2, y2, x3, y3);

	path = cairo_copy_path_flat(cr);

	for(i = 0; i < path->num_data; i += path->data[i].header.length) {
		data = &path->data[i];

		switch (data->header.type) {
		case CAIRO_PATH_MOVE_TO:
			current_point = data[1];

			break;

		case CAIRO_PATH_LINE_TO:
			length += two_points_distance(&current_point, &data[1]);
			current_point = data[1];

			break;

		default:
			break;
		}
	}

	cairo_path_destroy(path);
	cairo_destroy(cr);

	return length;
}
/* Compute parametrization info. That is, for each part of the cairo path, tags it with
 * its length. */
static parametrization_t* parametrize_path(cairo_path_t* path) {
	parametrization_t* parametrization = 0;
	cairo_path_data_t* data = 0;
	cairo_path_data_t last_move_to;
	cairo_path_data_t current_point;
	int i;

	current_point.point.x = 0.0;
	current_point.point.y = 0.0;

	parametrization = (parametrization_t*)malloc(path->num_data * sizeof(parametrization[0]));

	for(i = 0; i < path->num_data; i += path->data[i].header.length) {
		data = &path->data[i];
		parametrization[i] = 0.0;

		switch(data->header.type) {
		case CAIRO_PATH_MOVE_TO:
			last_move_to = data[1];
			current_point = data[1];

			break;

		case CAIRO_PATH_CLOSE_PATH:
			/* Make it look like it's a line_to to last_move_to. */
			data = (&last_move_to) - 1;

		case CAIRO_PATH_LINE_TO:
			parametrization[i] = two_points_distance(&current_point, &data[1]);
			current_point = data[1];

			break;

		case CAIRO_PATH_CURVE_TO:
			parametrization[i] = curve_length(
				current_point.point.x, current_point.point.x,
				data[1].point.x, data[1].point.y,
				data[2].point.x, data[2].point.y,
				data[3].point.x, data[3].point.y
			);

			current_point = data[3];

			break;
		}
	}

	return parametrization;
}
Example #4
0
/* Compute parametrization info.  That is, for each part of the 
 * cairo path, tags it with its length.
 *
 * Free returned value with g_free().
 */
static parametrization_t *
parametrize_path (cairo_path_t *path)
{
    int i;
    cairo_path_data_t *data, last_move_to, current_point;
    parametrization_t *parametrization;

    parametrization = g_malloc (path->num_data * sizeof (parametrization[0]));

    for (i=0; i < path->num_data; i += path->data[i].header.length) {
        data = &path->data[i];
        parametrization[i] = 0.0;
        switch (data->header.type) {
        case CAIRO_PATH_MOVE_TO:
            last_move_to = data[1];
            current_point = data[1];
            break;
        case CAIRO_PATH_CLOSE_PATH:
            /* Make it look like it's a line_to to last_move_to */
            data = (&last_move_to) - 1;
            /* fall through */
        case CAIRO_PATH_LINE_TO:
            parametrization[i] = two_points_distance (&current_point, &data[1]);
            current_point = data[1];
            break;
        case CAIRO_PATH_CURVE_TO:
            /* naive curve-length, treating bezier as three line segments:
               parametrization[i] = two_points_distance (&current_point, &data[1])
               + two_points_distance (&data[1], &data[2])
               + two_points_distance (&data[2], &data[3]);
            */
            parametrization[i] = curve_length (current_point.point.x, current_point.point.x,
                                               data[1].point.x, data[1].point.y,
                                               data[2].point.x, data[2].point.y,
                                               data[3].point.x, data[3].point.y);

            current_point = data[3];
            break;
        default:
            g_assert_not_reached ();
        }
    }

    return parametrization;
}
Example #5
0
/* Returns the x, y, and tangent vector angle of a point distance
 * d down a path. path must be flattened.
 */
static void _rsvg_path_point_on_path(cairo_path_t *path, double d,
                                     double *x, double *y, double *theta)
{
    int i;
    double dist_so_far = 0.0;
    cairo_path_data_t *last_point, *cur_point, *last_move_to;

    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:
            last_move_to = data;
            last_point = &data[1];
            break;
        case CAIRO_PATH_CLOSE_PATH:
            // Make it look like it's a line_to to last_move_to
            data = last_move_to;
            // fall through
        case CAIRO_PATH_LINE_TO:
            cur_point = &data[1];
            dist_so_far += two_points_distance(last_point, cur_point);

            // Reached distance d along the path
            if (dist_so_far > d) {
                double dlength = dist_so_far - d;
                double angle = atan2(cur_point->point.y - last_point->point.y,
                                     cur_point->point.x - last_point->point.x);
                *x = dlength * cos(angle) + last_point->point.x;
                *y = dlength * sin(angle) + last_point->point.y;
                // Find the normal vector and normalize to [-pi,pi]
                angle -= G_PI/2.0;
                if (angle < -G_PI)
                    angle += 2*G_PI;
                *theta = angle;
            } else {
                last_point = cur_point;                
            }
            break;
        default:
            g_assert_not_reached ();
        }
    }
}