WIP: path: Add conic curves

So far this just adds the API, if you use it, you'll get lots of
g_warnings().
This commit is contained in:
Benjamin Otte
2020-11-28 07:24:05 +01:00
parent 91923c29da
commit c82080ccea
7 changed files with 158 additions and 11 deletions

View File

@@ -103,6 +103,7 @@ static gboolean
gtk_path_transform_op (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer data)
{
GtkPathTransform *transform = data;
@@ -135,6 +136,15 @@ gtk_path_transform_op (GskPathOperation op,
}
break;
case GSK_PATH_CONIC:
{
graphene_point_t res[2];
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
gsk_path_builder_conic_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, weight);
}
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (transform->builder);
break;

View File

@@ -296,6 +296,7 @@ gsk_path_builder_add_rect
gsk_path_builder_move_to
gsk_path_builder_line_to
gsk_path_builder_curve_to
gsk_path_builder_conic_to
gsk_path_builder_close
<SUBSECTION Private>
GSK_TYPE_PATH_BUILDER

View File

@@ -244,6 +244,9 @@ typedef enum {
* @GSK_PATH_CURVE: A curve-to operation describing a cubic bezier curve
* with 4 points describing the start point, the two control points
* and the end point of the curve.
* @GSK_PATH_CONIC: A weighted quadratic bezier curve with 3 points
* describing the start point, control point and end point of the
* curve. A weight for the curve will be passed, too.
*
* Path operations can be used to approximate a #GskPath.
**/
@@ -252,6 +255,7 @@ typedef enum {
GSK_PATH_CLOSE,
GSK_PATH_LINE,
GSK_PATH_CURVE,
GSK_PATH_CONIC,
} GskPathOperation;
/**

View File

@@ -246,11 +246,11 @@ gsk_rect_contour_foreach (const GskContour *contour,
GRAPHENE_POINT_INIT (self->x, self->y)
};
return func (GSK_PATH_MOVE, &pts[0], 1, user_data)
&& func (GSK_PATH_LINE, &pts[0], 2, user_data)
&& func (GSK_PATH_LINE, &pts[1], 2, user_data)
&& func (GSK_PATH_LINE, &pts[2], 2, user_data)
&& func (GSK_PATH_CLOSE, &pts[3], 2, user_data);
return func (GSK_PATH_MOVE, &pts[0], 1, 0, user_data)
&& func (GSK_PATH_LINE, &pts[0], 2, 0, user_data)
&& func (GSK_PATH_LINE, &pts[1], 2, 0, user_data)
&& func (GSK_PATH_LINE, &pts[2], 2, 0, user_data)
&& func (GSK_PATH_CLOSE, &pts[3], 2, 0, user_data);
}
static gpointer
@@ -605,7 +605,7 @@ gsk_circle_contour_curve (const graphene_point_t curve[4],
{
ForeachWrapper *wrapper = data;
return wrapper->func (GSK_PATH_CURVE, curve, 4, wrapper->user_data);
return wrapper->func (GSK_PATH_CURVE, curve, 4, 0, wrapper->user_data);
}
static gboolean
@@ -617,7 +617,7 @@ gsk_circle_contour_foreach (const GskContour *contour,
const GskCircleContour *self = (const GskCircleContour *) contour;
graphene_point_t start = GSK_CIRCLE_POINT_INIT (self, self->start_angle);
if (!func (GSK_PATH_MOVE, &start, 1, user_data))
if (!func (GSK_PATH_MOVE, &start, 1, 0, user_data))
return FALSE;
if (!gsk_spline_decompose_arc (&self->center,
@@ -631,7 +631,7 @@ gsk_circle_contour_foreach (const GskContour *contour,
if (fabs (self->start_angle - self->end_angle) >= 360)
{
if (!func (GSK_PATH_CLOSE, (graphene_point_t[2]) { start, start }, 2, user_data))
if (!func (GSK_PATH_CLOSE, (graphene_point_t[2]) { start, start }, 2, 0, user_data))
return FALSE;
}
@@ -862,13 +862,25 @@ gsk_standard_contour_foreach (const GskContour *contour,
[GSK_PATH_MOVE] = 1,
[GSK_PATH_CLOSE] = 2,
[GSK_PATH_LINE] = 2,
[GSK_PATH_CURVE] = 4
[GSK_PATH_CURVE] = 4,
};
for (i = 0; i < self->n_ops; i ++)
{
if (!func (self->ops[i].op, &self->points[self->ops[i].point], n_points[self->ops[i].op], user_data))
return FALSE;
if (self->ops[i].op == GSK_PATH_CONIC)
{
graphene_point_t pts[2] = { self->points[self->ops[i].point],
self->points[self->ops[i].point + 2] };
float weight = self->points[self->ops[i].point + 1].x;
if (!func (GSK_PATH_CONIC, pts, 2, weight, user_data))
return FALSE;
}
else
{
if (!func (self->ops[i].op, &self->points[self->ops[i].point], n_points[self->ops[i].op], 0, user_data))
return FALSE;
}
}
return TRUE;
@@ -918,6 +930,16 @@ gsk_standard_contour_print (const GskContour *contour,
_g_string_append_point (string, &pt[3]);
break;
case GSK_PATH_CONIC:
/* This is not valid SVG */
g_string_append (string, " O ");
_g_string_append_point (string, &pt[1]);
g_string_append (string, ", ");
_g_string_append_double (string, pt[2].x);
g_string_append (string, ", ");
_g_string_append_point (string, &pt[3]);
break;
default:
g_assert_not_reached();
return;
@@ -1070,6 +1092,23 @@ gsk_standard_contour_init_measure (const GskContour *contour,
}
break;
case GSK_PATH_CONIC:
g_warning ("FIXME: Stop treating conics as lines");
seg_length = graphene_point_distance (&pt[0], &pt[3], 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;
default:
g_assert_not_reached();
return NULL;
@@ -1130,6 +1169,16 @@ gsk_standard_contour_measure_get_point (GskStandardContour *self,
gsk_spline_get_point_cubic (pts, progress, pos, tangent);
break;
case GSK_PATH_CONIC:
g_warning ("FIXME: Stop treating conics as lines");
if (pos)
graphene_point_interpolate (&pts[0], &pts[3], progress, pos);
if (tangent)
{
graphene_vec2_init (tangent, pts[3].x - pts[0].x, pts[3].y - pts[0].y);
graphene_vec2_normalize (tangent, tangent);
}
break;
case GSK_PATH_MOVE:
default:
g_assert_not_reached ();
@@ -1363,6 +1412,24 @@ gsk_standard_contour_add_segment (const GskContour *contour,
}
break;
case GSK_PATH_CONIC:
g_warning ("FIXME: Stop treating conics as lines");
{
graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
graphene_point_t point;
graphene_point_interpolate (&pts[0], &pts[3], 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[3], end_progress, &point);
gsk_path_builder_line_to (builder, point.x, point.y);
return;
}
gsk_path_builder_line_to (builder, pts[3].x, pts[3].y);
}
break;
case GSK_PATH_MOVE:
default:
g_assert_not_reached();
@@ -1392,6 +1459,10 @@ gsk_standard_contour_add_segment (const GskContour *contour,
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;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pt[1].x, pt[1].y, pt[3].x, pt[3].y, pt[2].x);
break;
default:
g_assert_not_reached();
return;
@@ -1424,6 +1495,17 @@ gsk_standard_contour_add_segment (const GskContour *contour,
}
break;
case GSK_PATH_CONIC:
g_warning ("FIXME: Stop treating conics as lines");
{
graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
graphene_point_t point;
graphene_point_interpolate (&pts[0], &pts[3], end_progress, &point);
gsk_path_builder_line_to (builder, point.x, point.y);
}
break;
case GSK_PATH_MOVE:
default:
g_assert_not_reached();
@@ -1804,6 +1886,7 @@ static gboolean
gsk_path_to_cairo_add_op (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer cr)
{
switch (op)
@@ -1824,6 +1907,11 @@ gsk_path_to_cairo_add_op (GskPathOperation op,
cairo_curve_to (cr, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
break;
case GSK_PATH_CONIC:
g_warning ("FIXME: Stop treating conics as lines");
cairo_line_to (cr, pts[3].x, pts[3].y);
break;
default:
g_assert_not_reached ();
return FALSE;

View File

@@ -34,6 +34,7 @@ G_BEGIN_DECLS
* @op: The operation to perform
* @pts: The points of the operation
* @n_pts: The number of points
* @weight: The weight for conic curves, or unused if not a conic curve.
* @user_data: The user data provided with the function
*
* Prototype of the callback to iterate throught the operations of
@@ -45,6 +46,7 @@ G_BEGIN_DECLS
typedef gboolean (* GskPathForeachFunc) (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data);
#define GSK_TYPE_PATH (gsk_path_get_type ())

View File

@@ -446,6 +446,41 @@ gsk_path_builder_curve_to (GskPathBuilder *builder,
});
}
/**
* gsk_path_builder_conic_to:
* @builder: a #GskPathBuilder
* @x1: x coordinate of control point
* @y1: y coordinate of control point
* @x2: x coordinate of the end of the curve
* @y2: y coordinate of the end of the curve
* @weight: weight of the curve
*
* Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
* from the current point to @x2, @y2 with the given
* @weight and @x1, @y1 as the single control point.
*
* Conic curves can be used to draw ellipses and circles.
**/
void
gsk_path_builder_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight)
{
g_return_if_fail (builder != NULL);
builder->flags ^= ~GSK_PATH_FLAT;
gsk_path_builder_append_current (builder,
GSK_PATH_CONIC,
3, (graphene_point_t[3]) {
GRAPHENE_POINT_INIT (x1, y1),
GRAPHENE_POINT_INIT (weight, 0),
GRAPHENE_POINT_INIT (x2, y2)
});
}
/**
* gsk_path_builder_close:
* @builder: a #GskPathBuilder

View File

@@ -73,6 +73,13 @@ void gsk_path_builder_curve_to (GskPathBuilder
float x3,
float y3);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_close (GskPathBuilder *builder);
G_END_DECLS