[gtk/matthiasc/lottie] Rename to CurveEditor
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/matthiasc/lottie] Rename to CurveEditor
- Date: Sat, 21 Nov 2020 01:38:30 +0000 (UTC)
commit 971f637b0264963173305db93b085f6085f28fa7
Author: Matthias Clasen <mclasen redhat com>
Date: Fri Nov 20 19:12:04 2020 -0500
Rename to CurveEditor
And start separating the widget from the demo
with an api.
tests/curve.c | 351 ++++++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 267 insertions(+), 84 deletions(-)
---
diff --git a/tests/curve.c b/tests/curve.c
index 7ec1a5834d..d143e4eac7 100644
--- a/tests/curve.c
+++ b/tests/curve.c
@@ -1,7 +1,5 @@
/* TODO
* - point insert/remove
- * - rename to CurveEditor
- * - add properties
*/
#include <gtk/gtk.h>
@@ -26,6 +24,19 @@ closest_point (const graphene_point_t *p,
q->y = a->y + t * (b->y - a->y);
}
+/* Determine if p is on the line through a and b */
+static gboolean
+collinear (const graphene_point_t *p,
+ const graphene_point_t *a,
+ const graphene_point_t *b)
+{
+ graphene_point_t q;
+
+ closest_point (p, a, b, &q);
+
+ return graphene_point_near (p, &q, 0.0001);
+}
+
/* Set q to the point on the line through p and a that is
* at a distance of d from p, on the opposite side
*/
@@ -48,7 +59,7 @@ opposite_point (const graphene_point_t *p,
#define RADIUS 5
-G_DECLARE_FINAL_TYPE (DemoWidget, demo_widget, DEMO, WIDGET, GtkWidget)
+G_DECLARE_FINAL_TYPE (CurveEditor, curve_editor, CURVE, EDITOR, GtkWidget)
typedef enum
{
@@ -93,7 +104,7 @@ typedef struct
gboolean smooth;
} PointData;
-struct _DemoWidget
+struct _CurveEditor
{
GtkWidget parent_instance;
graphene_point_t *points;
@@ -108,12 +119,12 @@ struct _DemoWidget
GActionMap *actions;
};
-struct _DemoWidgetClass
+struct _CurveEditorClass
{
GtkWidgetClass parent_class;
};
-G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
+G_DEFINE_TYPE (CurveEditor, curve_editor, GTK_TYPE_WIDGET)
static float
dist (graphene_point_t *a, graphene_point_t *b)
@@ -128,7 +139,7 @@ static void
drag_begin (GtkGestureDrag *gesture,
double start_x,
double start_y,
- DemoWidget *self)
+ CurveEditor *self)
{
int i;
graphene_point_t p = GRAPHENE_POINT_INIT (start_x, start_y);
@@ -153,7 +164,7 @@ static void
drag_update (GtkGestureDrag *gesture,
double offset_x,
double offset_y,
- DemoWidget *self)
+ CurveEditor *self)
{
double x, y;
double dx, dy;
@@ -348,7 +359,7 @@ static void
drag_end (GtkGestureDrag *gesture,
double offset_x,
double offset_y,
- DemoWidget *self)
+ CurveEditor *self)
{
drag_update (gesture, offset_x, offset_y, self);
self->dragged = -1;
@@ -356,7 +367,7 @@ drag_end (GtkGestureDrag *gesture,
}
static void
-maintain_smoothness (DemoWidget *self,
+maintain_smoothness (CurveEditor *self,
int point)
{
gboolean smooth;
@@ -414,7 +425,7 @@ toggle_smooth (GSimpleAction *action,
GVariant *value,
gpointer data)
{
- DemoWidget *self = DEMO_WIDGET (data);
+ CurveEditor *self = CURVE_EDITOR (data);
self->point_data[self->context / 3].smooth = g_variant_get_boolean (value);
@@ -428,7 +439,7 @@ set_operation (GSimpleAction *action,
GVariant *value,
gpointer data)
{
- DemoWidget *self = DEMO_WIDGET (data);
+ CurveEditor *self = CURVE_EDITOR (data);
self->point_data[self->context / 3].op = op_from_string (g_variant_get_string (value, NULL));
@@ -443,7 +454,7 @@ pressed (GtkGestureClick *gesture,
int n_press,
double x,
double y,
- DemoWidget *self)
+ CurveEditor *self)
{
graphene_point_t m = GRAPHENE_POINT_INIT (x, y);
int i;
@@ -486,7 +497,7 @@ released (GtkGestureClick *gesture,
int n_press,
double x,
double y,
- DemoWidget *self)
+ CurveEditor *self)
{
graphene_point_t m = GRAPHENE_POINT_INIT (x, y);
int i;
@@ -528,52 +539,7 @@ released (GtkGestureClick *gesture,
}
static void
-init_points (DemoWidget *self)
-{
- float w = 200;
- float h = 200;
- float cx = w / 2;
- float cy = h / 2;
- float pad = 20;
- float r = (w - 2 * pad) / 2;
- float k = 0.55228;
- float kr = k * r;
- int i;
-
- g_free (self->points);
- g_free (self->point_data);
-
- self->n_points = 12;
- self->points = g_new (graphene_point_t, self->n_points);
- self->point_data = g_new (PointData, self->n_points / 3);
-
-
- self->points[0] = GRAPHENE_POINT_INIT (cx, pad);
- self->points[1] = GRAPHENE_POINT_INIT (cx + kr, pad);
- self->points[2] = GRAPHENE_POINT_INIT (w - pad, cy - kr);
-
- self->points[3] = GRAPHENE_POINT_INIT (w - pad, cy);
- self->points[4] = GRAPHENE_POINT_INIT (w - pad, cy + kr);
- self->points[5] = GRAPHENE_POINT_INIT (cx + kr, h - pad);
-
- self->points[6] = GRAPHENE_POINT_INIT (cx, h - pad);
- self->points[7] = GRAPHENE_POINT_INIT (cx - kr, h - pad);
- self->points[8] = GRAPHENE_POINT_INIT (pad, cy + kr);
-
- self->points[9] = GRAPHENE_POINT_INIT (pad, cy);
- self->points[10] = GRAPHENE_POINT_INIT (pad, cy - kr);
- self->points[11] = GRAPHENE_POINT_INIT (cx - kr, pad);
-
- for (i = 0; i < self->n_points / 3; i++)
- {
- self->point_data[i].edit = FALSE;
- self->point_data[i].smooth = TRUE;
- self->point_data[i].op = CURVE;
- }
-}
-
-static void
-demo_widget_init (DemoWidget *self)
+curve_editor_init (CurveEditor *self)
{
GtkGesture *gesture;
GMenu *menu;
@@ -597,7 +563,9 @@ demo_widget_init (DemoWidget *self)
g_signal_connect (gesture, "released", G_CALLBACK (released), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
- init_points (self);
+ self->points = NULL;
+ self->point_data = NULL;
+ self->n_points = 0;
self->actions = G_ACTION_MAP (g_simple_action_group_new ());
@@ -642,10 +610,10 @@ demo_widget_init (DemoWidget *self)
}
static void
-demo_widget_snapshot (GtkWidget *widget,
+curve_editor_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
- DemoWidget *self = (DemoWidget *)widget;
+ CurveEditor *self = (CurveEditor *)widget;
GskPathBuilder *builder;
GskPath *path;
GskStroke *stroke;
@@ -653,6 +621,9 @@ demo_widget_snapshot (GtkWidget *widget,
float width;
float height;
+ if (self->n_points == 0)
+ return;
+
width = gtk_widget_get_width (widget);
height = gtk_widget_get_width (widget);
@@ -808,7 +779,7 @@ demo_widget_snapshot (GtkWidget *widget,
}
static void
-demo_widget_measure (GtkWidget *widget,
+curve_editor_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum_size,
@@ -821,71 +792,281 @@ demo_widget_measure (GtkWidget *widget,
}
static void
-demo_widget_size_allocate (GtkWidget *widget,
+curve_editor_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
- DemoWidget *self = DEMO_WIDGET (widget);
+ CurveEditor *self = CURVE_EDITOR (widget);
gtk_native_check_resize (GTK_NATIVE (self->menu));
}
static void
-demo_widget_dispose (GObject *object)
+curve_editor_dispose (GObject *object)
{
- DemoWidget *self = DEMO_WIDGET (object);
+ CurveEditor *self = CURVE_EDITOR (object);
g_clear_pointer (&self->points, g_free);
g_clear_pointer (&self->point_data, g_free);
g_clear_pointer (&self->menu, gtk_widget_unparent);
- G_OBJECT_CLASS (demo_widget_parent_class)->dispose (object);
+ G_OBJECT_CLASS (curve_editor_parent_class)->dispose (object);
}
static void
-demo_widget_class_init (DemoWidgetClass *class)
+curve_editor_class_init (CurveEditorClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
- object_class->dispose = demo_widget_dispose;
+ object_class->dispose = curve_editor_dispose;
- widget_class->snapshot = demo_widget_snapshot;
- widget_class->measure = demo_widget_measure;
- widget_class->size_allocate = demo_widget_size_allocate;
+ widget_class->snapshot = curve_editor_snapshot;
+ widget_class->measure = curve_editor_measure;
+ widget_class->size_allocate = curve_editor_size_allocate;
}
static GtkWidget *
-demo_widget_new (void)
+curve_editor_new (void)
{
- return g_object_new (demo_widget_get_type (), NULL);
+ return g_object_new (curve_editor_get_type (), NULL);
}
static void
-edit_changed (GtkToggleButton *button,
- GParamSpec *pspec,
- DemoWidget *self)
+curve_editor_set_edit (CurveEditor *self,
+ gboolean edit)
{
int i;
- self->edit = gtk_toggle_button_get_active (button);
+ self->edit = edit;
if (!self->edit)
{
for (i = 0; i < self->n_points / 3; i++)
self->point_data[i].edit = FALSE;
}
+
gtk_widget_queue_draw (GTK_WIDGET (self));
}
+typedef struct
+{
+ int count;
+ graphene_point_t first;
+ graphene_point_t last;
+ gboolean has_close;
+ gboolean has_initial_move;
+} CountSegmentData;
+
+static gboolean
+count_segments (GskPathOperation op,
+ const graphene_point_t *pts,
+ gsize n_pts,
+ gpointer data)
+{
+ CountSegmentData *d = data;
+
+ if (d->count == 0)
+ {
+ d->first = pts[0];
+ if (op == GSK_PATH_MOVE)
+ d->has_initial_move = TRUE;
+ }
+
+ d->last = pts[n_pts - 1];
+ d->count++;
+
+ if (op == GSK_PATH_CLOSE)
+ d->has_close = TRUE;
+
+ return TRUE;
+}
+
+typedef struct
+{
+ CurveEditor *editor;
+ int pos;
+} CopySegmentData;
+
+static gboolean
+copy_segments (GskPathOperation op,
+ const graphene_point_t *pts,
+ gsize n_pts,
+ gpointer data)
+{
+ CopySegmentData *d = data;
+ int i;
+
+ switch (op)
+ {
+ case GSK_PATH_MOVE:
+ if (d->pos == 0)
+ {
+ d->editor->points[d->pos++] = pts[0];
+ }
+ else
+ {
+ d->editor->point_data[d->pos / 3].op = MOVE;
+ d->editor->point_data[d->pos / 3].smooth = FALSE;
+
+ d->editor->points[d->pos++] = pts[0];
+ d->editor->points[d->pos++] = pts[0];
+ d->editor->points[d->pos++] = pts[0];
+ }
+ break;
+ case GSK_PATH_CLOSE:
+ break;
+ case GSK_PATH_LINE:
+ d->editor->point_data[d->pos / 3].op = LINE;
+ d->editor->point_data[d->pos / 3].smooth = FALSE;
+
+ if (d->pos == 0)
+ d->editor->points[d->pos++] = pts[0];
+
+ d->editor->points[d->pos++] = pts[1];
+ d->editor->points[d->pos++] = pts[1];
+ d->editor->points[d->pos++] = pts[1];
+ break;
+ case GSK_PATH_CURVE:
+ d->editor->point_data[d->pos / 3].op = CURVE;
+ d->editor->point_data[d->pos / 3].smooth = FALSE;
+
+ if (d->pos == 0)
+ d->editor->points[d->pos++] = pts[0];
+
+ for (i = 1; i < n_pts; i++)
+ d->editor->points[d->pos++] = pts[i];
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return TRUE;
+}
+
+/* Check if the points arount point currently satisy
+ * smoothness conditions. Set PointData.smooth accordingly.
+ */
+static void
+update_smoothness (CurveEditor *self,
+ int point)
+{
+ Operation op, op1;
+ graphene_point_t *p, *p2, *p1;
+
+ p = &self->points[point];
+ op = self->point_data[point / 3].op;
+ op1 = self->point_data[((point - 1 + self->n_points) % self->n_points) / 3].op;
+
+ if (op == CURVE)
+ p2 = &self->points[(point + 1) % self->n_points];
+ else if (op == LINE)
+ p2 = &self->points[(point + 3) % self->n_points];
+ else
+ p2 = NULL;
+
+ if (op1 == CURVE)
+ p1 = &self->points[(point - 1 + self->n_points) % self->n_points];
+ else if (op1 == LINE)
+ p1 = &self->points[(point - 3 + self->n_points) % self->n_points];
+ else
+ p1 = NULL;
+
+ if (p1 && p2)
+ self->point_data[point / 3].smooth = collinear (p, p1, p2);
+ else
+ self->point_data[point / 3].smooth = TRUE;
+}
+
static void
-reset (GtkButton *button,
- DemoWidget *self)
+curve_editor_set_path (CurveEditor *self,
+ GskPath *path)
{
- init_points (self);
+ CountSegmentData data;
+ CopySegmentData data2;
+ int i;
+
+ g_clear_pointer (&self->points, g_free);
+ g_clear_pointer (&self->point_data, g_free);
+ self->n_points = 0;
+
+ data.count = 0;
+ data.has_close = FALSE;
+ gsk_path_foreach (path, count_segments, &data);
+
+ if (data.has_initial_move)
+ data.count--;
+
+ if (!graphene_point_near (&data.first, &data.last, 0.0001) && !data.has_close)
+ data.count++;
+
+ self->n_points = data.count * 3;
+ self->points = g_new0 (graphene_point_t, self->n_points);
+ self->point_data = g_new0 (PointData, data.count);
+
+ data2.editor = self;
+ data2.pos = 0;
+ gsk_path_foreach (path, copy_segments, &data2);
+
+ for (i = 0; i < self->n_points; i += 3)
+ update_smoothness (self, i);
+
gtk_widget_queue_draw (GTK_WIDGET (self));
}
+/* -------------------- */
+
+static GskPath *
+make_circle_path (void)
+{
+ float w = 200;
+ float h = 200;
+ float cx = w / 2;
+ float cy = h / 2;
+ float pad = 20;
+ float r = (w - 2 * pad) / 2;
+ float k = 0.55228;
+ float kr = k * r;
+ GskPathBuilder *builder;
+
+ builder = gsk_path_builder_new ();
+
+ gsk_path_builder_move_to (builder, cx, pad);
+ gsk_path_builder_curve_to (builder, cx + kr, pad,
+ w - pad, cy - kr,
+ w - pad, cy);
+ gsk_path_builder_curve_to (builder, w - pad, cy + kr,
+ cx + kr, h - pad,
+ cx, h - pad);
+ gsk_path_builder_curve_to (builder, cx - kr, h - pad,
+ pad, cy + kr,
+ pad, cy);
+ gsk_path_builder_curve_to (builder, pad, cy - kr,
+ cx - kr, pad,
+ cx, pad);
+
+ return gsk_path_builder_free_to_path (builder);
+}
+
+static void
+edit_changed (GtkToggleButton *button,
+ GParamSpec *pspec,
+ CurveEditor *editor)
+{
+ curve_editor_set_edit (editor, gtk_toggle_button_get_active (button));
+}
+
+static void
+reset (GtkButton *button,
+ CurveEditor *editor)
+{
+ GskPath *path;
+
+ path = make_circle_path ();
+ curve_editor_set_path (editor, path);
+ gsk_path_unref (path);
+}
+
int
main (int argc, char *argv[])
{
@@ -911,11 +1092,13 @@ main (int argc, char *argv[])
gtk_window_set_titlebar (GTK_WINDOW (window), titlebar);
- demo = demo_widget_new ();
+ demo = curve_editor_new ();
g_signal_connect (edit_toggle, "notify::active", G_CALLBACK (edit_changed), demo);
g_signal_connect (reset_button, "clicked", G_CALLBACK (reset), demo);
+ reset (NULL, CURVE_EDITOR (demo));
+
gtk_window_set_child (window, demo);
gtk_window_present (window);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]