[gtk/wip/otte/lottie: 11/11] WIP: path: Add conic curves
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/lottie: 11/11] WIP: path: Add conic curves
- Date: Sat, 28 Nov 2020 06:36:12 +0000 (UTC)
commit c82080ccea0e8c843209fb78c8b1673d4bc73ea9
Author: Benjamin Otte <otte redhat com>
Date: Sat Nov 28 07:24:05 2020 +0100
WIP: path: Add conic curves
So far this just adds the API, if you use it, you'll get lots of
g_warnings().
demos/gtk-demo/path_text.c | 10 ++++
docs/reference/gsk/gsk4-sections.txt | 1 +
gsk/gskenums.h | 4 ++
gsk/gskpath.c | 110 +++++++++++++++++++++++++++++++----
gsk/gskpath.h | 2 +
gsk/gskpathbuilder.c | 35 +++++++++++
gsk/gskpathbuilder.h | 7 +++
7 files changed, 158 insertions(+), 11 deletions(-)
---
diff --git a/demos/gtk-demo/path_text.c b/demos/gtk-demo/path_text.c
index 22e57f3eed..144e13a58c 100644
--- a/demos/gtk-demo/path_text.c
+++ b/demos/gtk-demo/path_text.c
@@ -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;
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt
index 5ad68326d4..5d34f26e6e 100644
--- a/docs/reference/gsk/gsk4-sections.txt
+++ b/docs/reference/gsk/gsk4-sections.txt
@@ -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
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 8ca577563d..cb4fa5fe13 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -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;
/**
diff --git a/gsk/gskpath.c b/gsk/gskpath.c
index 0f6fbc8ef1..c15d457ab4 100644
--- a/gsk/gskpath.c
+++ b/gsk/gskpath.c
@@ -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;
diff --git a/gsk/gskpath.h b/gsk/gskpath.h
index 57bdc5bba2..d0116e7ba4 100644
--- a/gsk/gskpath.h
+++ b/gsk/gskpath.h
@@ -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 ())
diff --git a/gsk/gskpathbuilder.c b/gsk/gskpathbuilder.c
index 16242a306d..161046157c 100644
--- a/gsk/gskpathbuilder.c
+++ b/gsk/gskpathbuilder.c
@@ -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
diff --git a/gsk/gskpathbuilder.h b/gsk/gskpathbuilder.h
index 2bc7a3933d..cca8366430 100644
--- a/gsk/gskpathbuilder.h
+++ b/gsk/gskpathbuilder.h
@@ -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
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]