path: Add gsk_path_measure_get_point()
Allows querying the coordinates and direction of any specific point on a path.
This commit is contained in:
181
gsk/gskpath.c
181
gsk/gskpath.c
@@ -71,6 +71,11 @@ struct _GskContourClass
|
||||
float *out_length);
|
||||
void (* free_measure) (const GskContour *contour,
|
||||
gpointer measure_data);
|
||||
void (* get_point) (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
void (* copy) (const GskContour *contour,
|
||||
GskContour *dest);
|
||||
void (* add_segment) (const GskContour *contour,
|
||||
@@ -196,6 +201,51 @@ gsk_rect_contour_free_measure (const GskContour *contour,
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_rect_contour_get_point (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskRectContour *self = (const GskRectContour *) contour;
|
||||
|
||||
if (distance < fabsf (self->width))
|
||||
{
|
||||
if (pos)
|
||||
*pos = GRAPHENE_POINT_INIT (self->x + copysignf (distance, self->width), self->y);
|
||||
if (tangent)
|
||||
graphene_vec2_init (tangent, copysignf (1.0f, self->width), 0.0f);
|
||||
return;
|
||||
}
|
||||
distance -= fabsf (self->width);
|
||||
|
||||
if (distance < fabsf (self->height))
|
||||
{
|
||||
if (pos)
|
||||
*pos = GRAPHENE_POINT_INIT (self->x + self->width, self->y + copysignf (distance, self->height));
|
||||
if (tangent)
|
||||
graphene_vec2_init (tangent, 0.0f, copysignf (self->height, 1.0f));
|
||||
return;
|
||||
}
|
||||
distance -= fabs (self->height);
|
||||
|
||||
if (distance < fabsf (self->width))
|
||||
{
|
||||
if (pos)
|
||||
*pos = GRAPHENE_POINT_INIT (self->x + self->width - copysignf (distance, self->width), self->y + self->height);
|
||||
if (tangent)
|
||||
graphene_vec2_init (tangent, - copysignf (1.0f, self->width), 0.0f);
|
||||
return;
|
||||
}
|
||||
distance -= fabsf (self->width);
|
||||
|
||||
if (pos)
|
||||
*pos = GRAPHENE_POINT_INIT (self->x, self->y + self->height - copysignf (distance, self->height));
|
||||
if (tangent)
|
||||
graphene_vec2_init (tangent, 0.0f, - copysignf (self->height, 1.0f));
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_rect_contour_copy (const GskContour *contour,
|
||||
GskContour *dest)
|
||||
@@ -282,6 +332,7 @@ static const GskContourClass GSK_RECT_CONTOUR_CLASS =
|
||||
gsk_rect_contour_foreach,
|
||||
gsk_rect_contour_init_measure,
|
||||
gsk_rect_contour_free_measure,
|
||||
gsk_rect_contour_get_point,
|
||||
gsk_rect_contour_copy,
|
||||
gsk_rect_contour_add_segment
|
||||
};
|
||||
@@ -427,6 +478,34 @@ gsk_circle_contour_free_measure (const GskContour *contour,
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_circle_contour_get_point (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
float delta = self->end_angle - self->start_angle;
|
||||
float length = self->radius * DEG_TO_RAD (delta);
|
||||
float angle = self->start_angle + distance/length * delta;
|
||||
graphene_point_t p;
|
||||
|
||||
p = GRAPHENE_POINT_INIT (self->center.x + cos (DEG_TO_RAD (angle)) * self->radius,
|
||||
self->center.y + sin (DEG_TO_RAD (angle)) * self->radius);
|
||||
|
||||
if (pos)
|
||||
*pos = p;
|
||||
|
||||
if (tangent)
|
||||
{
|
||||
graphene_vec2_init (tangent,
|
||||
p.y - self->center.y,
|
||||
- p.x + self->center.x);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_circle_contour_copy (const GskContour *contour,
|
||||
GskContour *dest)
|
||||
@@ -475,6 +554,7 @@ static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =
|
||||
gsk_circle_contour_foreach,
|
||||
gsk_circle_contour_init_measure,
|
||||
gsk_circle_contour_free_measure,
|
||||
gsk_circle_contour_get_point,
|
||||
gsk_circle_contour_copy,
|
||||
gsk_circle_contour_add_segment
|
||||
};
|
||||
@@ -756,6 +836,78 @@ gsk_standard_contour_free_measure (const GskContour *contour,
|
||||
g_array_free (data, TRUE);
|
||||
}
|
||||
|
||||
static int
|
||||
gsk_standard_contour_find_measure (gconstpointer m,
|
||||
gconstpointer l)
|
||||
{
|
||||
const GskStandardContourMeasure *measure = m;
|
||||
float length = *(const float *) l;
|
||||
|
||||
if (measure->start > length)
|
||||
return 1;
|
||||
else if (measure->end <= length)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_measure_get_point (GskStandardContour *self,
|
||||
gsize op,
|
||||
float progress,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const graphene_point_t *pts;
|
||||
|
||||
pts = &self->points[self->ops[op].point];
|
||||
switch (self->ops[op].op)
|
||||
{
|
||||
case GSK_PATH_LINE:
|
||||
case GSK_PATH_CLOSE:
|
||||
if (pos)
|
||||
graphene_point_interpolate (&pts[0], &pts[1], progress, pos);
|
||||
if (tangent)
|
||||
{
|
||||
graphene_vec2_init (tangent, pts[1].x - pts[0].x, pts[1].y - pts[0].y);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
gsk_spline_get_point_cubic (pts, progress, pos, tangent);
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_get_point (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
GskStandardContour *self = (GskStandardContour *) contour;
|
||||
GArray *array = measure_data;
|
||||
guint index;
|
||||
float progress;
|
||||
GskStandardContourMeasure *measure;
|
||||
|
||||
if (!g_array_binary_search (array, &distance, gsk_standard_contour_find_measure, &index))
|
||||
index = array->len - 1;
|
||||
measure = &g_array_index (array, GskStandardContourMeasure, index);
|
||||
progress = (distance - measure->start) / (measure->end - measure->start);
|
||||
progress = measure->start_progress + (measure->end_progress - measure->start_progress) * progress;
|
||||
g_assert (progress >= 0 && progress <= 1);
|
||||
|
||||
gsk_standard_contour_measure_get_point (self, measure->op, progress, pos, tangent);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_init (GskContour *contour,
|
||||
GskPathFlags flags,
|
||||
@@ -773,21 +925,6 @@ gsk_standard_contour_copy (const GskContour *contour,
|
||||
gsk_standard_contour_init (dest, self->flags, self->ops, self->n_ops, self->points, self->n_points);
|
||||
}
|
||||
|
||||
static int
|
||||
gsk_standard_contour_find_measure (gconstpointer m,
|
||||
gconstpointer l)
|
||||
{
|
||||
const GskStandardContourMeasure *measure = m;
|
||||
float length = *(const float *) l;
|
||||
|
||||
if (measure->start > length)
|
||||
return 1;
|
||||
else if (measure->end <= length)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_add_segment (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
@@ -953,6 +1090,7 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
|
||||
gsk_standard_contour_foreach,
|
||||
gsk_standard_contour_init_measure,
|
||||
gsk_standard_contour_free_measure,
|
||||
gsk_standard_contour_get_point,
|
||||
gsk_standard_contour_copy,
|
||||
gsk_standard_contour_add_segment
|
||||
};
|
||||
@@ -1036,6 +1174,19 @@ gsk_contour_free_measure (GskPath *path,
|
||||
self->klass->free_measure (self, data);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_contour_get_point (GskPath *path,
|
||||
gsize i,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
GskContour *self = path->contours[i];
|
||||
|
||||
self->klass->get_point (self, measure_data, distance, pos, tangent);
|
||||
}
|
||||
|
||||
/* PATH */
|
||||
|
||||
static GskPath *
|
||||
|
@@ -182,6 +182,67 @@ gsk_path_measure_get_length (GskPathMeasure *self)
|
||||
return self->length;
|
||||
}
|
||||
|
||||
static float
|
||||
gsk_path_measure_clamp_distance (GskPathMeasure *self,
|
||||
float distance)
|
||||
{
|
||||
if (isnan (distance))
|
||||
return 0;
|
||||
|
||||
return CLAMP (distance, 0, self->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_point:
|
||||
* @self: a #GskPathMeasure
|
||||
* @distance: distance into the path
|
||||
* @pos: (out) (optional) (caller-allocates): The coordinates
|
||||
* of the position at @distance
|
||||
* @tangent: (out) (optional) (caller-allocates): The tangent
|
||||
* to the position at @distance
|
||||
*
|
||||
* Calculates the coordinates and tangent of the point @distance
|
||||
* units into the path. The value will be clamped to the length
|
||||
* of the path.
|
||||
*
|
||||
* If the point is a discontinuous edge in the path, the returned
|
||||
* point and tangent will describe the line starting at that point
|
||||
* going forward.
|
||||
**/
|
||||
void
|
||||
gsk_path_measure_get_point (GskPathMeasure *self,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
if (pos == NULL && tangent == NULL)
|
||||
return;
|
||||
|
||||
distance = gsk_path_measure_clamp_distance (self, distance);
|
||||
|
||||
for (i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
if (self->measures[i].length < distance)
|
||||
{
|
||||
distance -= self->measures[i].length;
|
||||
}
|
||||
else
|
||||
{
|
||||
gsk_contour_get_point (self->path,
|
||||
i,
|
||||
self->measures[i].contour_data,
|
||||
distance,
|
||||
pos,
|
||||
tangent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_add_segment:
|
||||
* @self: a #GskPathMeasure
|
||||
@@ -209,8 +270,8 @@ gsk_path_measure_add_segment (GskPathMeasure *self,
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (builder != NULL);
|
||||
|
||||
start = CLAMP (start, 0, self->length);
|
||||
end = CLAMP (end, 0, self->length);
|
||||
start = gsk_path_measure_clamp_distance (self, start);
|
||||
end = gsk_path_measure_clamp_distance (self, end);
|
||||
if (start >= end)
|
||||
return;
|
||||
|
||||
|
@@ -46,6 +46,11 @@ void gsk_path_measure_unref (GskPathMeasure
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_path_measure_get_length (GskPathMeasure *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_measure_get_point (GskPathMeasure *self,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_measure_add_segment (GskPathMeasure *self,
|
||||
|
@@ -41,6 +41,12 @@ gpointer gsk_contour_init_measure (GskPath
|
||||
void gsk_contour_free_measure (GskPath *path,
|
||||
gsize i,
|
||||
gpointer data);
|
||||
void gsk_contour_get_point (GskPath *path,
|
||||
gsize i,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
|
||||
void gsk_path_builder_add_contour (GskPathBuilder *builder,
|
||||
GskPath *path,
|
||||
|
@@ -47,6 +47,40 @@ gsk_spline_decompose_add_point (GskCubicDecomposition *decomp,
|
||||
decomp->last_progress += progress;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_split_get_coefficients (graphene_point_t coeffs[4],
|
||||
const graphene_point_t pts[4])
|
||||
{
|
||||
coeffs[0] = GRAPHENE_POINT_INIT (pts[3].x - 3.0f * pts[2].x + 3.0f * pts[1].x - pts[0].x,
|
||||
pts[3].y - 3.0f * pts[2].y + 3.0f * pts[1].y - pts[0].y);
|
||||
coeffs[1] = GRAPHENE_POINT_INIT (3.0f * pts[2].x - 6.0f * pts[1].x + 3.0f * pts[0].x,
|
||||
3.0f * pts[2].y - 6.0f * pts[1].y + 3.0f * pts[0].y);
|
||||
coeffs[2] = GRAPHENE_POINT_INIT (3.0f * pts[1].x - 3.0f * pts[0].x,
|
||||
3.0f * pts[1].y - 3.0f * pts[0].y);
|
||||
coeffs[3] = pts[0];
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_get_point_cubic (const graphene_point_t pts[4],
|
||||
float progress,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
graphene_point_t c[4];
|
||||
|
||||
gsk_split_get_coefficients (c, pts);
|
||||
if (pos)
|
||||
*pos = GRAPHENE_POINT_INIT (((c[0].x * progress + c[1].x) * progress +c[2].x) * progress + c[3].x,
|
||||
((c[0].y * progress + c[1].y) * progress +c[2].y) * progress + c[3].y);
|
||||
if (tangent)
|
||||
{
|
||||
graphene_vec2_init (tangent,
|
||||
(3.0f * c[0].x * progress + 2.0f * c[1].x) * progress + c[2].x,
|
||||
(3.0f * c[0].y * progress + 2.0f * c[1].y) * progress + c[2].y);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_split_cubic (const graphene_point_t pts[4],
|
||||
graphene_point_t result1[4],
|
||||
|
@@ -31,6 +31,10 @@ typedef void (* GskSplineAddPointFunc) (const graphene_point_t *from,
|
||||
float to_progress,
|
||||
gpointer user_data);
|
||||
|
||||
void gsk_spline_get_point_cubic (const graphene_point_t pts[4],
|
||||
float progress,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
void gsk_spline_split_cubic (const graphene_point_t pts[4],
|
||||
graphene_point_t result1[4],
|
||||
graphene_point_t result2[4],
|
||||
|
Reference in New Issue
Block a user