pathmeasure: Implement support for beziers
Instead of treating bezier curves as lines, we properly decompose them into line segments now so that we can treat those as lines.
This commit is contained in:
committed by
Matthias Clasen
parent
19111abcdd
commit
f1d8b96fd3
281
gsk/gskpath.c
281
gsk/gskpath.c
@@ -21,6 +21,8 @@
|
||||
|
||||
#include "gskpathprivate.h"
|
||||
|
||||
#include "gsksplineprivate.h"
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@@ -52,6 +54,7 @@ struct _GskContourClass
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
gpointer (* init_measure) (const GskContour *contour,
|
||||
float tolerance,
|
||||
float *out_length);
|
||||
void (* free_measure) (const GskContour *contour,
|
||||
gpointer measure_data);
|
||||
@@ -191,6 +194,7 @@ gsk_rect_contour_foreach (const GskContour *contour,
|
||||
|
||||
static gpointer
|
||||
gsk_rect_contour_init_measure (const GskContour *contour,
|
||||
float tolerance,
|
||||
float *out_length)
|
||||
{
|
||||
const GskRectContour *self = (const GskRectContour *) contour;
|
||||
@@ -473,14 +477,54 @@ gsk_standard_contour_get_bounds (const GskContour *contour,
|
||||
return bounds->size.width > 0 && bounds->size.height > 0;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float start;
|
||||
float end;
|
||||
float start_progress;
|
||||
float end_progress;
|
||||
graphene_point_t end_point;
|
||||
gsize op;
|
||||
} GskStandardContourMeasure;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GArray *array;
|
||||
GskStandardContourMeasure measure;
|
||||
} LengthDecompose;
|
||||
|
||||
static void
|
||||
gsk_standard_contour_measure_add_point (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_progress,
|
||||
float to_progress,
|
||||
gpointer user_data)
|
||||
{
|
||||
LengthDecompose *decomp = user_data;
|
||||
float seg_length;
|
||||
|
||||
seg_length = graphene_point_distance (from, to, NULL, NULL);
|
||||
decomp->measure.end += seg_length;
|
||||
decomp->measure.start_progress = from_progress;
|
||||
decomp->measure.end_progress = to_progress;
|
||||
decomp->measure.end_point = *to;
|
||||
|
||||
g_array_append_val (decomp->array, decomp->measure);
|
||||
|
||||
decomp->measure.start += seg_length;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gsk_standard_contour_init_measure (const GskContour *contour,
|
||||
float tolerance,
|
||||
float *out_length)
|
||||
{
|
||||
const GskStandardContour *self = (const GskStandardContour *) contour;
|
||||
gsize i;
|
||||
float length;
|
||||
float length, seg_length;
|
||||
GArray *array;
|
||||
|
||||
array = g_array_new (FALSE, FALSE, sizeof (GskStandardContourMeasure));
|
||||
length = 0;
|
||||
|
||||
for (i = 1; i < self->n_ops; i ++)
|
||||
@@ -494,12 +538,27 @@ gsk_standard_contour_init_measure (const GskContour *contour,
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
length += graphene_point_distance (&pt[0], &pt[1], NULL, NULL);
|
||||
seg_length = graphene_point_distance (&pt[0], &pt[1], NULL, NULL);
|
||||
if (seg_length > 0)
|
||||
{
|
||||
g_array_append_vals (array,
|
||||
&(GskStandardContourMeasure) {
|
||||
length,
|
||||
length + seg_length,
|
||||
0, 1,
|
||||
pt[1],
|
||||
i,
|
||||
}, 1);
|
||||
length += seg_length;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
g_warning ("i'm not fat!");
|
||||
length += graphene_point_distance (&pt[0], &pt[3], NULL, NULL);
|
||||
{
|
||||
LengthDecompose decomp = { array, { length, length, 0, 0, pt[0], i } };
|
||||
gsk_spline_decompose_cubic (pt, tolerance, gsk_standard_contour_measure_add_point, &decomp);
|
||||
length = decomp.measure.start;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -510,13 +569,14 @@ gsk_standard_contour_init_measure (const GskContour *contour,
|
||||
|
||||
*out_length = length;
|
||||
|
||||
return NULL;
|
||||
return array;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_free_measure (const GskContour *contour,
|
||||
gpointer data)
|
||||
{
|
||||
g_array_free (data, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -536,6 +596,21 @@ 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,
|
||||
@@ -544,85 +619,110 @@ gsk_standard_contour_add_segment (const GskContour *contour,
|
||||
float end)
|
||||
{
|
||||
GskStandardContour *self = (GskStandardContour *) contour;
|
||||
GArray *array = measure_data;
|
||||
guint start_index, end_index;
|
||||
float start_progress, end_progress;
|
||||
GskStandardContourMeasure *start_measure, *end_measure;
|
||||
gsize i;
|
||||
float length;
|
||||
|
||||
for (i = 0; end > 0 && i < self->n_ops; i ++)
|
||||
if (start > 0)
|
||||
{
|
||||
if (!g_array_binary_search (array, (float[1]) { start }, gsk_standard_contour_find_measure, &start_index))
|
||||
start_index = array->len - 1;
|
||||
start_measure = &g_array_index (array, GskStandardContourMeasure, start_index);
|
||||
start_progress = (start - start_measure->start) / (start_measure->end - start_measure->start);
|
||||
start_progress = start_measure->start_progress + (start_measure->end_progress - start_measure->start_progress) * start_progress;
|
||||
g_assert (start_progress >= 0 && start_progress <= 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
start_measure = NULL;
|
||||
start_progress = 0.0;
|
||||
}
|
||||
|
||||
if (g_array_binary_search (array, (float[1]) { end }, gsk_standard_contour_find_measure, &end_index))
|
||||
{
|
||||
end_measure = &g_array_index (array, GskStandardContourMeasure, end_index);
|
||||
end_progress = (end - end_measure->start) / (end_measure->end - end_measure->start);
|
||||
end_progress = end_measure->start_progress + (end_measure->end_progress - end_measure->start_progress) * end_progress;
|
||||
g_assert (end_progress >= 0 && end_progress <= 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
end_measure = NULL;
|
||||
end_progress = 1.0;
|
||||
}
|
||||
|
||||
/* Add the first partial operation,
|
||||
* taking care that first and last operation might be identical */
|
||||
if (start_measure)
|
||||
{
|
||||
switch (self->ops[start_measure->op].op)
|
||||
{
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
{
|
||||
graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
|
||||
graphene_point_t point;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[1], start_progress, &point);
|
||||
gsk_path_builder_move_to (builder, point.x, point.y);
|
||||
if (end_measure && end_measure->op == start_measure->op)
|
||||
{
|
||||
graphene_point_interpolate (&pts[0], &pts[1], end_progress, &point);
|
||||
gsk_path_builder_line_to (builder, point.x, point.y);
|
||||
return;
|
||||
}
|
||||
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
{
|
||||
graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
|
||||
graphene_point_t curve[4], discard[4];
|
||||
|
||||
gsk_spline_split_cubic (pts, discard, curve, start_progress);
|
||||
if (end_measure && end_measure->op == start_measure->op)
|
||||
{
|
||||
graphene_point_t tiny[4];
|
||||
gsk_spline_split_cubic (curve, tiny, discard, (end_progress - start_progress) / (1 - start_progress));
|
||||
gsk_path_builder_move_to (builder, tiny[0].x, tiny[0].y);
|
||||
gsk_path_builder_curve_to (builder, tiny[1].x, tiny[1].y, tiny[2].x, tiny[2].y, tiny[3].x, tiny[3].y);
|
||||
return;
|
||||
}
|
||||
gsk_path_builder_move_to (builder, curve[0].x, curve[0].y);
|
||||
gsk_path_builder_curve_to (builder, curve[1].x, curve[1].y, curve[2].x, curve[2].y, curve[3].x, curve[3].y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
}
|
||||
i = start_measure->op + 1;
|
||||
}
|
||||
else
|
||||
i = 0;
|
||||
|
||||
for (; i < (end_measure ? end_measure->op : self->n_ops); i++)
|
||||
{
|
||||
graphene_point_t *pt = &self->points[self->ops[i].point];
|
||||
|
||||
switch (self->ops[i].op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
if (start <= 0.0)
|
||||
{
|
||||
gsk_path_builder_move_to (builder, pt[0].x, pt[0].y);
|
||||
start = -1;
|
||||
}
|
||||
gsk_path_builder_move_to (builder, pt[0].x, pt[0].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
length = graphene_point_distance (&pt[0], &pt[1], NULL, NULL);
|
||||
if (length <= start)
|
||||
{
|
||||
start -= length;
|
||||
end -= length;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (start >= 0)
|
||||
{
|
||||
graphene_point_t start_pt;
|
||||
graphene_point_interpolate (&pt[0], &pt[1], start / length, &start_pt);
|
||||
gsk_path_builder_move_to (builder, start_pt.x, start_pt.y);
|
||||
start = -1;
|
||||
}
|
||||
if (length <= end)
|
||||
{
|
||||
gsk_path_builder_line_to (builder, pt[1].x, pt[1].y);
|
||||
end -= length;
|
||||
}
|
||||
else
|
||||
{
|
||||
graphene_point_t end_pt;
|
||||
graphene_point_interpolate (&pt[0], &pt[1], end / length, &end_pt);
|
||||
gsk_path_builder_line_to (builder, end_pt.x, end_pt.y);
|
||||
return;
|
||||
}
|
||||
}
|
||||
case GSK_PATH_CLOSE:
|
||||
gsk_path_builder_line_to (builder, pt[1].x, pt[1].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
g_warning ("i'm not fat!");
|
||||
length = graphene_point_distance (&pt[0], &pt[3], NULL, NULL);
|
||||
if (length <= start)
|
||||
{
|
||||
start -= length;
|
||||
end -= length;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (start >= 0)
|
||||
{
|
||||
graphene_point_t start_pt;
|
||||
graphene_point_interpolate (&pt[0], &pt[3], start / length, &start_pt);
|
||||
gsk_path_builder_move_to (builder, start_pt.x, start_pt.y);
|
||||
start = -1;
|
||||
}
|
||||
if (length <= end)
|
||||
{
|
||||
gsk_path_builder_line_to (builder, pt[3].x, pt[3].y);
|
||||
end -= length;
|
||||
}
|
||||
else
|
||||
{
|
||||
graphene_point_t end_pt;
|
||||
graphene_point_interpolate (&pt[0], &pt[3], end / length, &end_pt);
|
||||
gsk_path_builder_line_to (builder, end_pt.x, end_pt.y);
|
||||
return;
|
||||
}
|
||||
}
|
||||
gsk_path_builder_curve_to (builder, pt[1].x, pt[1].y, pt[2].x, pt[2].y, pt[3].x, pt[3].y);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -630,6 +730,39 @@ gsk_standard_contour_add_segment (const GskContour *contour,
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the last partial operation */
|
||||
if (end_measure)
|
||||
{
|
||||
switch (self->ops[end_measure->op].op)
|
||||
{
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
{
|
||||
graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
|
||||
graphene_point_t point;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[1], end_progress, &point);
|
||||
gsk_path_builder_line_to (builder, point.x, point.y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
{
|
||||
graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
|
||||
graphene_point_t curve[4], discard[4];
|
||||
|
||||
gsk_spline_split_cubic (pts, curve, discard, end_progress);
|
||||
gsk_path_builder_curve_to (builder, curve[1].x, curve[1].y, curve[2].x, curve[2].y, curve[3].x, curve[3].y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
|
||||
@@ -708,11 +841,12 @@ gsk_contour_foreach (const GskContour *contour,
|
||||
gpointer
|
||||
gsk_contour_init_measure (GskPath *path,
|
||||
gsize i,
|
||||
float tolerance,
|
||||
float *out_length)
|
||||
{
|
||||
GskContour *self = path->contours[i];
|
||||
|
||||
return self->klass->init_measure (self, out_length);
|
||||
return self->klass->init_measure (self, tolerance, out_length);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1420,6 +1554,11 @@ gsk_path_builder_line_to (GskPathBuilder *builder,
|
||||
return;
|
||||
}
|
||||
|
||||
/* skip the line if it goes to the same point */
|
||||
if (graphene_point_equal (&g_array_index (builder->points, graphene_point_t, builder->points->len - 1),
|
||||
&GRAPHENE_POINT_INIT (x, y)))
|
||||
return;
|
||||
|
||||
gsk_path_builder_append_current (builder,
|
||||
GSK_PATH_LINE,
|
||||
1, (graphene_point_t[1]) {
|
||||
|
||||
@@ -47,6 +47,7 @@ struct _GskPathMeasure
|
||||
guint ref_count;
|
||||
|
||||
GskPath *path;
|
||||
float tolerance;
|
||||
|
||||
float length;
|
||||
gsize n_contours;
|
||||
@@ -61,17 +62,35 @@ G_DEFINE_BOXED_TYPE (GskPathMeasure, gsk_path_measure,
|
||||
* gsk_path_measure_new:
|
||||
* @path: the path to measure
|
||||
*
|
||||
* Creates a measure object for the given @path.
|
||||
* Creates a measure object for the given @path with a
|
||||
* default tolerance.
|
||||
*
|
||||
* Returns: a new `GskPathMeasure` representing @path
|
||||
**/
|
||||
GskPathMeasure *
|
||||
gsk_path_measure_new (GskPath *path)
|
||||
{
|
||||
return gsk_path_measure_new_with_tolerance (path, GSK_PATH_TOLERANCE_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_new_with_tolerance:
|
||||
* @path: the path to measure
|
||||
* @tolerance: the tolerance for measuring operations
|
||||
*
|
||||
* Creates a measure object for the given @path and @tolerance.
|
||||
*
|
||||
* Returns: a new `GskPathMeasure` representing @path
|
||||
**/
|
||||
GskPathMeasure *
|
||||
gsk_path_measure_new_with_tolerance (GskPath *path,
|
||||
float tolerance)
|
||||
{
|
||||
GskPathMeasure *self;
|
||||
gsize i, n_contours;
|
||||
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
g_return_val_if_fail (tolerance > 0, NULL);
|
||||
|
||||
n_contours = gsk_path_get_n_contours (path);
|
||||
|
||||
@@ -79,11 +98,14 @@ gsk_path_measure_new (GskPath *path)
|
||||
|
||||
self->ref_count = 1;
|
||||
self->path = gsk_path_ref (path);
|
||||
self->tolerance = tolerance;
|
||||
self->n_contours = n_contours;
|
||||
|
||||
for (i = 0; i < n_contours; i++)
|
||||
{
|
||||
self->measures[i].contour_data = gsk_contour_init_measure (path, i, &self->measures[i].length);
|
||||
self->measures[i].contour_data = gsk_contour_init_measure (path, i,
|
||||
self->tolerance,
|
||||
&self->measures[i].length);
|
||||
self->length += self->measures[i].length;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_path_measure_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathMeasure * gsk_path_measure_new (GskPath *path);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathMeasure * gsk_path_measure_new_with_tolerance (GskPath *path,
|
||||
float tolerance);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathMeasure * gsk_path_measure_ref (GskPathMeasure *self);
|
||||
|
||||
@@ -36,6 +36,7 @@ gboolean gsk_path_foreach_with_tolerance (GskPath
|
||||
|
||||
gpointer gsk_contour_init_measure (GskPath *path,
|
||||
gsize i,
|
||||
float tolerance,
|
||||
float *out_length);
|
||||
void gsk_contour_free_measure (GskPath *path,
|
||||
gsize i,
|
||||
|
||||
179
gsk/gskspline.c
Normal file
179
gsk/gskspline.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright © 2002 University of Southern California
|
||||
* 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
* Carl D. Worth <cworth@cworth.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gsksplineprivate.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
graphene_point_t last_point;
|
||||
float last_progress;
|
||||
float tolerance_squared;
|
||||
GskSplineAddPointFunc func;
|
||||
gpointer user_data;
|
||||
} GskCubicDecomposition;
|
||||
|
||||
static void
|
||||
gsk_spline_decompose_add_point (GskCubicDecomposition *decomp,
|
||||
const graphene_point_t *pt,
|
||||
float progress)
|
||||
{
|
||||
if (graphene_point_equal (&decomp->last_point, pt))
|
||||
return;
|
||||
|
||||
decomp->func (&decomp->last_point, pt, decomp->last_progress, decomp->last_progress + progress, decomp->user_data);
|
||||
decomp->last_point = *pt;
|
||||
decomp->last_progress += progress;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_split_cubic (const graphene_point_t pts[4],
|
||||
graphene_point_t result1[4],
|
||||
graphene_point_t result2[4],
|
||||
float progress)
|
||||
{
|
||||
graphene_point_t ab, bc, cd;
|
||||
graphene_point_t abbc, bccd;
|
||||
graphene_point_t final;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[1], progress, &ab);
|
||||
graphene_point_interpolate (&pts[1], &pts[2], progress, &bc);
|
||||
graphene_point_interpolate (&pts[2], &pts[3], progress, &cd);
|
||||
graphene_point_interpolate (&ab, &bc, progress, &abbc);
|
||||
graphene_point_interpolate (&bc, &cd, progress, &bccd);
|
||||
graphene_point_interpolate (&abbc, &bccd, progress, &final);
|
||||
|
||||
memcpy (result1, (graphene_point_t[4]) { pts[0], ab, abbc, final }, sizeof (graphene_point_t[4]));
|
||||
memcpy (result2, (graphene_point_t[4]) { final, bccd, cd, pts[3] }, sizeof (graphene_point_t[4]));
|
||||
}
|
||||
|
||||
/* Return an upper bound on the error (squared) that could result from
|
||||
* approximating a spline as a line segment connecting the two endpoints. */
|
||||
static float
|
||||
gsk_spline_error_squared (const graphene_point_t pts[4])
|
||||
{
|
||||
float bdx, bdy, berr;
|
||||
float cdx, cdy, cerr;
|
||||
|
||||
/* We are going to compute the distance (squared) between each of the the b
|
||||
* and c control points and the segment a-b. The maximum of these two
|
||||
* distances will be our approximation error. */
|
||||
|
||||
bdx = pts[1].x - pts[0].x;
|
||||
bdy = pts[1].y - pts[0].y;
|
||||
|
||||
cdx = pts[2].x - pts[0].x;
|
||||
cdy = pts[2].y - pts[0].y;
|
||||
|
||||
if (!graphene_point_equal (&pts[0], &pts[3]))
|
||||
{
|
||||
float dx, dy, u, v;
|
||||
|
||||
/* Intersection point (px):
|
||||
* px = p1 + u(p2 - p1)
|
||||
* (p - px) ∙ (p2 - p1) = 0
|
||||
* Thus:
|
||||
* u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
|
||||
*/
|
||||
|
||||
dx = pts[3].x - pts[0].x;
|
||||
dy = pts[3].y - pts[0].y;
|
||||
v = dx * dx + dy * dy;
|
||||
|
||||
u = bdx * dx + bdy * dy;
|
||||
if (u <= 0)
|
||||
{
|
||||
/* bdx -= 0;
|
||||
* bdy -= 0;
|
||||
*/
|
||||
}
|
||||
else if (u >= v)
|
||||
{
|
||||
bdx -= dx;
|
||||
bdy -= dy;
|
||||
}
|
||||
else
|
||||
{
|
||||
bdx -= u/v * dx;
|
||||
bdy -= u/v * dy;
|
||||
}
|
||||
|
||||
u = cdx * dx + cdy * dy;
|
||||
if (u <= 0)
|
||||
{
|
||||
/* cdx -= 0;
|
||||
* cdy -= 0;
|
||||
*/
|
||||
}
|
||||
else if (u >= v)
|
||||
{
|
||||
cdx -= dx;
|
||||
cdy -= dy;
|
||||
}
|
||||
else
|
||||
{
|
||||
cdx -= u/v * dx;
|
||||
cdy -= u/v * dy;
|
||||
}
|
||||
}
|
||||
|
||||
berr = bdx * bdx + bdy * bdy;
|
||||
cerr = cdx * cdx + cdy * cdy;
|
||||
if (berr > cerr)
|
||||
return berr;
|
||||
else
|
||||
return cerr;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_spline_decompose_into (GskCubicDecomposition *decomp,
|
||||
const graphene_point_t pts[4],
|
||||
float progress)
|
||||
{
|
||||
graphene_point_t left[4], right[4];
|
||||
|
||||
if (gsk_spline_error_squared (pts) < decomp->tolerance_squared)
|
||||
{
|
||||
gsk_spline_decompose_add_point (decomp, &pts[3], progress);
|
||||
return;
|
||||
}
|
||||
|
||||
gsk_spline_split_cubic (pts, left, right, 0.5);
|
||||
|
||||
gsk_spline_decompose_into (decomp, left, progress / 2);
|
||||
gsk_spline_decompose_into (decomp, right, progress / 2);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_decompose_cubic (const graphene_point_t pts[4],
|
||||
float tolerance,
|
||||
GskSplineAddPointFunc add_point_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskCubicDecomposition decomp = { pts[0], 0.0f, tolerance * tolerance, add_point_func, user_data };
|
||||
|
||||
gsk_spline_decompose_into (&decomp, pts, 1.0f);
|
||||
|
||||
g_assert (graphene_point_equal (&decomp.last_point, &pts[3]));
|
||||
g_assert (decomp.last_progress == 1.0f || decomp.last_progress == 0.0f);
|
||||
}
|
||||
|
||||
46
gsk/gsksplineprivate.h
Normal file
46
gsk/gsksplineprivate.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GSK_SPLINE_PRIVATE_H__
|
||||
#define __GSK_SPLINE_PRIVATE_H__
|
||||
|
||||
#include "gskpath.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef void (* GskSplineAddPointFunc) (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_progress,
|
||||
float to_progress,
|
||||
gpointer user_data);
|
||||
|
||||
void gsk_spline_split_cubic (const graphene_point_t pts[4],
|
||||
graphene_point_t result1[4],
|
||||
graphene_point_t result2[4],
|
||||
float progress);
|
||||
void gsk_spline_decompose_cubic (const graphene_point_t pts[4],
|
||||
float tolerance,
|
||||
GskSplineAddPointFunc add_point_func,
|
||||
gpointer user_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_SPLINE_PRIVATE_H__ */
|
||||
|
||||
@@ -42,10 +42,12 @@ gsk_private_sources = files([
|
||||
'gskdebug.c',
|
||||
'gskprivate.c',
|
||||
'gskprofiler.c',
|
||||
'gskspline.c',
|
||||
'gl/gskglattachmentstate.c',
|
||||
'gl/gskglbuffer.c',
|
||||
'gl/gskglcommandqueue.c',
|
||||
'gl/gskglcompiler.c',
|
||||
'gl/gskglprofiler.c',
|
||||
'gl/gskgldriver.c',
|
||||
'gl/gskglglyphlibrary.c',
|
||||
'gl/gskgliconlibrary.c',
|
||||
|
||||
Reference in New Issue
Block a user