[gtk/matthiasc/lottie] Redo the data structure
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/matthiasc/lottie] Redo the data structure
- Date: Sun, 22 Nov 2020 03:56:20 +0000 (UTC)
commit 17ef4a77fee8989141c958b7d74d3c09894c2565
Author: Matthias Clasen <mclasen redhat com>
Date: Sat Nov 21 19:57:37 2020 -0500
Redo the data structure
Change things around a bit and keep an array of
points. Each point comes with its two neighboring
control points, and each Bezier segment is made up
from points from two neighboring points.
This is much closer to what the ui shows.
tests/curve-editor.c | 687 ++++++++++++++++++++++++++-------------------------
1 file changed, 348 insertions(+), 339 deletions(-)
---
diff --git a/tests/curve-editor.c b/tests/curve-editor.c
index 1e3040e7a5..172966e0ed 100644
--- a/tests/curve-editor.c
+++ b/tests/curve-editor.c
@@ -1,5 +1,3 @@
-/* TODO: redo data structure, use gskpath for math */
-
#include "curve-editor.h"
#include <gtk/gtk.h>
@@ -44,22 +42,32 @@ op_from_string (const char *s)
g_assert_not_reached ();
}
+/* We don't store Bezier segments, but an array of points on
+ * the line. Each point comes with its two neighboring control
+ * points, so each Bezier segment contains p[1] and p[2] from
+ * one point, and p[0] and p[1] from the next.
+ *
+ * The control points are irrelevant for MOVE and LINE segments.
+ */
typedef struct
{
- Operation op;
+ /* 0 and 2 are control points, 1 is the point on the line */
+ graphene_point_t p[3];
gboolean edit;
gboolean smooth;
gboolean symmetric;
+ int dragged;
+ int hovered;
+ /* refers to the segment following the point */
+ Operation op;
} PointData;
struct _CurveEditor
{
GtkWidget parent_instance;
- graphene_point_t *points;
+ PointData *points;
int n_points;
- PointData *point_data; /* length is n_points / 3 */
int dragged;
- int hovered;
int context;
gboolean edit;
@@ -260,15 +268,15 @@ find_closest_point (CurveEditor *self,
for (i = 0; i < self->n_points; i++)
{
- if (i % 3 != 0)
- continue;
+ PointData *pd = &self->points[i];
+ PointData *pd1 = &self->points[(i + 1) % self->n_points];
- switch (self->point_data[i / 3].op)
+ switch (pd->op)
{
case MOVE:
continue;
case LINE:
- find_line_point (&self->points[i], &self->points[(i + 3) % self->n_points], p, &tt, &pp, &dd);
+ find_line_point (&pd->p[1], &pd1->p[1], p, &tt, &pp, &dd);
if (dd < best_d)
{
best_i = i;
@@ -279,10 +287,11 @@ find_closest_point (CurveEditor *self,
case CURVE:
{
graphene_point_t points[4];
- int k;
- for (k = 0; k < 4; k++)
- points[k] = self->points[(i + k) % self->n_points];
+ points[0] = pd->p[1];
+ points[1] = pd->p[2];
+ points[2] = pd1->p[0];
+ points[3] = pd1->p[1];
find_curve_point (points, p, &tt, &pp, &dd);
if (dd < best_d)
@@ -346,27 +355,28 @@ split_bezier (graphene_point_t *points,
/* {{{ Utilities */
static gboolean
point_is_visible (CurveEditor *self,
- int point)
+ int point,
+ int point1)
{
g_assert (0 <= point && point < self->n_points);
if (!self->edit)
return FALSE;
- switch (point % 3)
+ switch (point1)
{
- case 0: /* point on curve */
- return TRUE;
- case 1:
- if (!self->point_data[point / 3].edit)
+ case 0:
+ if (!self->points[point].edit)
return FALSE;
else
- return self->point_data[point / 3].op == CURVE;
+ return self->points[(point - 1 + self->n_points) % self->n_points].op == CURVE;
+ case 1: /* point on curve */
+ return TRUE;
case 2:
- if (!self->point_data[((point + 1) % self->n_points) / 3].edit)
+ if (!self->points[point].edit)
return FALSE;
else
- return self->point_data[point / 3].op == CURVE;
+ return self->points[point].op == CURVE;
default:
g_assert_not_reached ();
}
@@ -374,55 +384,54 @@ point_is_visible (CurveEditor *self,
static void
maintain_smoothness (CurveEditor *self,
- int point)
+ int point)
{
- gboolean smooth;
+ PointData *pd;
Operation op, op1;
+ graphene_point_t *p;
- smooth = self->point_data[point / 3].smooth;
+ pd = &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 (!pd->smooth)
+ return;
- if (smooth)
- {
- graphene_point_t *p;
+ op = pd->op;
+ op1 = self->points[(point - 1 + self->n_points) % self->n_points].op;
- p = &self->points[point];
+ p = &pd->p[1];
- if (op == CURVE && op1 == CURVE)
- {
- graphene_point_t *c, *c2;
- float d;
+ if (op == CURVE && op1 == CURVE)
+ {
+ graphene_point_t *c, *c2;
+ float d;
- c = &self->points[(point - 1 + self->n_points) % self->n_points];
- c2 = &self->points[(point + 1) % self->n_points];
+ c = &pd->p[0];
+ c2 = &pd->p[2];
- d = graphene_point_distance (c, p, NULL, NULL);
- opposite_point (p, c2, d, c);
- }
- else if (op == CURVE && op1 == LINE)
- {
- graphene_point_t *c, *p2;
- float d;
+ d = graphene_point_distance (c, p, NULL, NULL);
+ opposite_point (p, c2, d, c);
+ }
+ else if (op == CURVE && op1 == LINE)
+ {
+ graphene_point_t *c, *p2;
+ float d;
- c = &self->points[(point + 1) % self->n_points];
- p2 = &self->points[(point - 3 + self->n_points) % self->n_points];
+ c = &pd->p[2];
+ p2 = &self->points[(point - 1 + self->n_points) % self->n_points].p[1];
- d = graphene_point_distance (c, p, NULL, NULL);
- opposite_point (p, p2, d, c);
- }
- else if (op == LINE && op1 == CURVE)
- {
- graphene_point_t *c, *p2;
- float d;
+ d = graphene_point_distance (c, p, NULL, NULL);
+ opposite_point (p, p2, d, c);
+ }
+ else if (op == LINE && op1 == CURVE)
+ {
+ graphene_point_t *c, *p2;
+ float d;
- c = &self->points[(point - 1 + self->n_points) % self->n_points];
- p2 = &self->points[(point + 3) % self->n_points];
+ c = &pd->p[0];
+ p2 = &self->points[(point + 1) % self->n_points].p[1];
- d = graphene_point_distance (c, p, NULL, NULL);
- opposite_point (p, p2, d, c);
- }
+ d = graphene_point_distance (c, p, NULL, NULL);
+ opposite_point (p, p2, d, c);
}
}
@@ -430,34 +439,35 @@ maintain_smoothness (CurveEditor *self,
* smoothness conditions. Set PointData.smooth accordingly.
*/
static void
-update_smoothness (CurveEditor *self,
- int point)
+check_smoothness (CurveEditor *self,
+ int point)
{
Operation op, op1;
- graphene_point_t *p, *p2, *p1;
+ graphene_point_t *p1, *p2;
+ PointData *pd;
- 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;
+ pd = &self->points[point];
+ op = pd->op;
+ op1 = self->points[(point - 1 + self->n_points) % self->n_points].op;
if (op == CURVE)
- p2 = &self->points[(point + 1) % self->n_points];
+ p2 = &pd->p[2];
else if (op == LINE)
- p2 = &self->points[(point + 3) % self->n_points];
+ p2 = &self->points[(point + 1) % self->n_points].p[1];
else
p2 = NULL;
if (op1 == CURVE)
- p1 = &self->points[(point - 1 + self->n_points) % self->n_points];
+ p1 = &pd->p[0];
else if (op1 == LINE)
- p1 = &self->points[(point - 3 + self->n_points) % self->n_points];
+ p1 = &self->points[(point - 1 + self->n_points) % self->n_points].p[1];
else
p1 = NULL;
if (p1 && p2)
- self->point_data[point / 3].smooth = collinear (p, p1, p2);
+ pd->smooth = collinear (&pd->p[1], p1, p2);
else
- self->point_data[point / 3].smooth = TRUE;
+ pd->smooth = TRUE;
}
static void
@@ -465,44 +475,37 @@ insert_point (CurveEditor *self,
int point,
double pos)
{
- Operation op = self->point_data[point / 3].op;
+ Operation op = self->points[point].op;
int i;
graphene_point_t points[4];
- int k;
+ int point1, point2;
if (op == MOVE)
return;
- for (k = 0; k < 4; k++)
- points[k] = self->points[(point + k) % self->n_points];
+ point1 = (point + 1) % self->n_points;
+ points[0] = self->points[point].p[1];
+ points[1] = self->points[point].p[2];
+ points[2] = self->points[point1].p[0];
+ points[3] = self->points[point1].p[1];
- self->point_data = g_realloc (self->point_data, sizeof (PointData) * (self->n_points / 3 + 1));
- for (i = self->n_points / 3; i > point / 3; i--)
- self->point_data[i] = self->point_data[i - 1];
+ self->points = g_realloc (self->points, sizeof (PointData) * (self->n_points + 1));
+ for (i = self->n_points; i > point; i--)
+ self->points[i] = self->points[i - 1];
- self->points = g_realloc (self->points, sizeof (graphene_point_t) * (self->n_points + 3));
- for (i = self->n_points + 2; i > point + 4; i--)
- self->points[i] = self->points[i - 3];
+ self->n_points += 1;
- self->n_points += 3;
+ point1 = (point + 1) % self->n_points;
+ point2 = (point + 2) % self->n_points;
if (op == LINE)
{
graphene_point_t p;
- graphene_point_t q;
-
- graphene_point_interpolate (&self->points[point], &self->points[(point + 6) % self->n_points], pos,
&p);
- self->points[point + 3] = p;
- graphene_point_interpolate (&p, &self->points[(point + 6) % self->n_points], 0.33, &q);
-
- self->points[point + 4] = q;
-
- graphene_point_interpolate (&p, &self->points[(point + 6) % self->n_points], 0.66, &q);
-
- self->points[point + 5] = q;
- self->point_data[point / 3 + 1].smooth = TRUE;
- self->point_data[point / 3 + 1].op = LINE;
+ graphene_point_interpolate (&points[0], &points[3], pos, &p);
+ self->points[point1].p[1] = p;
+ self->points[point1].smooth = TRUE;
+ self->points[point1].op = LINE;
}
else if (op == CURVE)
{
@@ -513,14 +516,16 @@ insert_point (CurveEditor *self,
split_bezier (points, 4, pos, left, &left_pos, right, &right_pos);
- for (k = 0; k < 4; k++)
- {
- self->points[(point + k) % self->n_points] = left[k];
- self->points[(point + 3 + k) % self->n_points] = right[3 - k];
- }
+ self->points[point].p[1] = left[0];
+ self->points[point].p[2] = left[1];
+ self->points[point1].p[0] = left[2];
+ self->points[point1].p[1] = left[3];
+ self->points[point1].p[2] = right[2];
+ self->points[point2].p[0] = right[1];
+ self->points[point2].p[1] = right[0];
- self->point_data[point / 3 + 1].smooth = TRUE;
- self->point_data[point / 3 + 1].op = CURVE;
+ self->points[point1].smooth = TRUE;
+ self->points[point1].op = CURVE;
}
gtk_widget_queue_draw (GTK_WIDGET (self));
@@ -533,26 +538,31 @@ curve_editor_add_path (CurveEditor *self,
{
int i;
- gsk_path_builder_move_to (builder, self->points[0].x, self->points[0].y);
- for (i = 1; i < self->n_points; i += 3)
+ if (self->n_points > 0)
+ gsk_path_builder_move_to (builder, self->points[0].p[1].x, self->points[0].p[1].y);
+
+ for (i = 0; i < self->n_points; i++)
{
- switch (self->point_data[i / 3].op)
+ PointData *pd1, *pd;
+
+ pd1 = &self->points[i];
+ pd = &self->points[(i + 1) % self->n_points];
+
+ switch (pd1->op)
{
case MOVE:
- gsk_path_builder_move_to (builder,
- self->points[(i + 2) % self->n_points].x, self->points[(i + 2) %
self->n_points].y);
+ gsk_path_builder_move_to (builder, pd->p[1].x, pd->p[1].y);
break;
case LINE:
- gsk_path_builder_line_to (builder,
- self->points[(i + 2) % self->n_points].x, self->points[(i + 2) %
self->n_points].y);
+ gsk_path_builder_line_to (builder, pd->p[1].x, pd->p[1].y);
break;
case CURVE:
gsk_path_builder_curve_to (builder,
- self->points[i].x, self->points[i].y,
- self->points[(i + 1) % self->n_points].x, self->points[(i + 1) %
self->n_points].y,
- self->points[(i + 2) % self->n_points].x, self->points[(i + 2) %
self->n_points].y);
+ pd1->p[2].x, pd1->p[2].y,
+ pd->p[0].x, pd->p[0].y,
+ pd->p[1].x, pd->p[1].y);
break;
default:
g_assert_not_reached ();
@@ -606,47 +616,37 @@ copy_segments (GskPathOperation op,
gpointer data)
{
CopySegmentData *d = data;
+ CurveEditor *self = d->editor;
switch (op)
{
case GSK_PATH_MOVE:
if (d->pos != 0)
{
- 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];
- if (d->pos < d->editor->n_points)
- d->editor->points[d->pos++] = pts[0];
+ d->editor->points[d->pos - 1].op = MOVE;
+ d->editor->points[d->pos % self->n_points].p[1] = pts[0];
+ d->pos++;
}
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];
- if (d->pos < d->editor->n_points)
- d->editor->points[d->pos++] = pts[1];
+ d->editor->points[d->pos - 1].op = LINE;
+ d->editor->points[d->pos % self->n_points].p[1] = 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];
+ {
+ d->editor->points[d->pos].p[1] = pts[0];
+ d->pos++;
+ }
- d->editor->points[d->pos++] = pts[1];
- d->editor->points[d->pos++] = pts[2];
+ d->editor->points[d->pos - 1].op = CURVE;
- if (d->pos < d->editor->n_points)
- d->editor->points[d->pos++] = pts[3];
+ d->editor->points[d->pos - 1].p[2] = pts[1];
+ d->editor->points[d->pos % self->n_points].p[0] = pts[2];
+ d->editor->points[d->pos % self->n_points].p[1] = pts[3];
+ d->pos++;
break;
default:
g_assert_not_reached ();
@@ -662,20 +662,24 @@ drag_begin (GtkGestureDrag *gesture,
double start_y,
CurveEditor *self)
{
- int i;
+ int i, j;
graphene_point_t p = GRAPHENE_POINT_INIT (start_x, start_y);
if (self->edit)
for (i = 0; i < self->n_points; i++)
{
- if (graphene_point_distance (&self->points[i], &p, NULL, NULL) < CLICK_RADIUS)
+ for (j = 0; j < 3; j++)
{
- if (point_is_visible (self, i))
+ if (graphene_point_distance (&self->points[i].p[j], &p, NULL, NULL) < CLICK_RADIUS)
{
- self->dragged = i;
- gtk_widget_queue_draw (GTK_WIDGET (self));
+ if (point_is_visible (self, i, j))
+ {
+ self->dragged = i;
+ self->points[i].dragged = j;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+ return;
}
- return;
}
}
@@ -692,6 +696,7 @@ drag_update (GtkGestureDrag *gesture,
double dx, dy;
graphene_point_t *c, *p, *d;
double l1, l2;
+ PointData *pd;
if (self->dragged == -1)
return;
@@ -703,142 +708,134 @@ drag_update (GtkGestureDrag *gesture,
x += offset_x;
y += offset_y;
- d = &self->points[self->dragged];
+ pd = &self->points[self->dragged];
+ d = &pd->p[pd->dragged];
/* before moving the point, record the distances to its neighbors, since
* we may want to preserve those
*/
- c = &self->points[(self->dragged - 1 + self->n_points) % self->n_points];
- l1 = graphene_point_distance (d, c, NULL, NULL);
- c = &self->points[(self->dragged + 1) % self->n_points];
- l2 = graphene_point_distance (d, c, NULL, NULL);
+ l1 = graphene_point_distance (&pd->p[1], &pd->p[0], NULL, NULL);
+ l2 = graphene_point_distance (&pd->p[1], &pd->p[2], NULL, NULL);
dx = x - d->x;
dy = y - d->y;
- if (self->dragged % 3 == 0)
+ if (pd->dragged == 1)
{
/* dragged point is on curve */
- Operation op, op1, op2;
+ Operation op, op1, op11, op2;
+ PointData *pd1, *pd2;
/* first move the point itself */
d->x = x;
d->y = y;
/* adjust control points as needed */
- op = self->point_data[self->dragged / 3].op;
- op1 = self->point_data[((self->dragged - 1 + self->n_points) % self->n_points) / 3].op;
+ pd1 = &self->points[((self->dragged - 1 + self->n_points) % self->n_points)];
+ pd2 = &self->points[((self->dragged + 1) % self->n_points)];
+
+ op = pd->op;
+ op1 = pd1->op;
+ op2 = pd2->op;
if (op1 == LINE)
{
/* the other endpoint of the line */
- p = &self->points[(self->dragged - 3 + self->n_points) % self->n_points];
+ p = &pd1->p[1];
- if (op == CURVE && self->point_data[self->dragged / 3].smooth)
+ if (op == CURVE && pd->smooth)
{
/* adjust the control point after the line segment */
- c = &self->points[(self->dragged + 1) % self->n_points];
- opposite_point (d, p, l2, c);
+ opposite_point (d, p, l2, &pd->p[2]);
}
else
{
- c = &self->points[(self->dragged + 1) % self->n_points];
- c->x += dx;
- c->y += dy;
+ pd->p[2].x += dx;
+ pd->p[2].y += dy;
}
- c = &self->points[(self->dragged - 1 + self->n_points) % self->n_points];
- c->x += dx;
- c->y += dy;
+ pd->p[0].x += dx;
+ pd->p[0].y += dy;
- op2 = self->point_data[((self->dragged - 4 + self->n_points) % self->n_points) / 3].op;
- if (op2 == CURVE && self->point_data[((self->dragged - 3 + self->n_points) % self->n_points) /
3].smooth)
+ op11 = self->points[((self->dragged - 2 + self->n_points) % self->n_points)].op;
+
+ if (op11 == CURVE && pd1->smooth)
{
double l;
/* adjust the control point before the line segment */
- c = &self->points[((self->dragged - 4 + self->n_points) % self->n_points)];
-
- l = graphene_point_distance (c, p, NULL, NULL);
- opposite_point (p, d, l, c);
+ l = graphene_point_distance (&pd1->p[0], p, NULL, NULL);
+ opposite_point (p, d, l, &pd1->p[0]);
}
}
+
if (op == LINE)
{
/* the other endpoint of the line */
- p = &self->points[(self->dragged + 3) % self->n_points];
+ p = &pd2->p[1];
- if (op1 == CURVE && self->point_data[self->dragged / 3].smooth)
+ if (op1 == CURVE && pd->smooth)
{
/* adjust the control point before the line segment */
- c = &self->points[(self->dragged - 1 + self->n_points) % self->n_points];
- opposite_point (d, p, l1, c);
+ opposite_point (d, p, l1, &pd->p[0]);
}
else
{
- c = &self->points[(self->dragged -1 + self->n_points) % self->n_points];
- c->x += dx;
- c->y += dy;
+ pd->p[0].x += dx;
+ pd->p[0].y += dy;
}
- c = &self->points[(self->dragged + 1) % self->n_points];
- c->x += dx;
- c->y += dy;
+ pd->p[2].x += dx;
+ pd->p[2].y += dy;
- op2 = self->point_data[((self->dragged + 3) % self->n_points) / 3].op;
- if (op2 == CURVE && self->point_data[((self->dragged + 3) % self->n_points) / 3].smooth)
+ if (op2 == CURVE && pd2->smooth)
{
double l;
/* adjust the control point after the line segment */
- c = &self->points[((self->dragged + 4) % self->n_points)];
-
- l = graphene_point_distance (c, p, NULL, NULL);
- opposite_point (p, d, l, c);
+ l = graphene_point_distance (&pd2->p[2], p, NULL, NULL);
+ opposite_point (p, d, l, &pd2->p[2]);
}
}
if (op1 != LINE && op != LINE)
{
- self->points[(self->dragged - 1 + self->n_points) % self->n_points].x += dx;
- self->points[(self->dragged - 1 + self->n_points) % self->n_points].y += dy;
-
- self->points[(self->dragged + 1) % self->n_points].x += dx;
- self->points[(self->dragged + 1) % self->n_points].y += dy;
+ pd->p[0].x += dx;
+ pd->p[0].y += dy;
+ pd->p[2].x += dx;
+ pd->p[2].y += dy;
}
}
else
{
/* dragged point is a control point */
- int point;
graphene_point_t *p1;
Operation op, op1;
- if (self->dragged % 3 == 1)
+ if (pd->dragged == 0)
{
- point = (self->dragged - 1 + self->n_points) % self->n_points;
- c = &self->points[(self->dragged - 2 + self->n_points) % self->n_points];
- p = &self->points[point];
+ c = &pd->p[2];
+ p = &pd->p[1];
+
+ op = self->points[(self->dragged - 1 + self->n_points) % self->n_points].op;
+ op1 = self->points[self->dragged].op;
- op = self->point_data[point / 3].op;
- op1 = self->point_data[((self->dragged - 4 + self->n_points) % self->n_points) / 3].op;
- p1 = &self->points[(self->dragged - 4 + self->n_points) % self->n_points];
+ p1 = &self->points[(self->dragged + 1) % self->n_points].p[1];
}
- else if (self->dragged % 3 == 2)
+ else if (pd->dragged == 2)
{
- point = (self->dragged + 1) % self->n_points;
- c = &self->points[(self->dragged + 2) % self->n_points];
- p = &self->points[point];
+ c = &pd->p[0];
+ p = &pd->p[1];
- op = self->point_data[self->dragged / 3].op;
- op1 = self->point_data[point / 3].op;
- p1 = &self->points[(self->dragged + 4) % self->n_points];
+ op = self->points[self->dragged].op;
+ op1 = self->points[(self->dragged - 1 + self->n_points) % self->n_points].op;
+ p1 = &self->points[(self->dragged - 1 + self->n_points) % self->n_points].p[1];
}
else
g_assert_not_reached ();
- if (op == CURVE && self->point_data[point / 3].smooth)
+ if (op == CURVE && pd->smooth)
{
if (op1 == CURVE)
{
@@ -849,7 +846,7 @@ drag_update (GtkGestureDrag *gesture,
d->y = y;
/* then adjust the other control point */
- if (self->point_data[point / 3].symmetric)
+ if (pd->symmetric)
l = graphene_point_distance (d, p, NULL, NULL);
else
l = graphene_point_distance (c, p, NULL, NULL);
@@ -895,7 +892,7 @@ set_smooth (GSimpleAction *action,
{
CurveEditor *self = CURVE_EDITOR (data);
- self->point_data[self->context / 3].smooth = g_variant_get_boolean (value);
+ self->points[self->context].smooth = g_variant_get_boolean (value);
maintain_smoothness (self, self->context);
@@ -909,7 +906,7 @@ set_symmetric (GSimpleAction *action,
{
CurveEditor *self = CURVE_EDITOR (data);
- self->point_data[self->context / 3].symmetric = g_variant_get_boolean (value);
+ self->points[self->context].symmetric = g_variant_get_boolean (value);
maintain_smoothness (self, self->context);
@@ -923,10 +920,10 @@ set_operation (GSimpleAction *action,
{
CurveEditor *self = CURVE_EDITOR (data);
- self->point_data[self->context / 3].op = op_from_string (g_variant_get_string (value, NULL));
+ self->points[self->context].op = op_from_string (g_variant_get_string (value, NULL));
maintain_smoothness (self, self->context);
- maintain_smoothness (self, (self->context + 3) % self->n_points);
+ maintain_smoothness (self, (self->context + 1) % self->n_points);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
@@ -939,19 +936,12 @@ remove_point (GSimpleAction *action,
CurveEditor *self = CURVE_EDITOR (data);
int i;
- for (i = self->context / 3; i + 1 < self->n_points / 3; i++)
- self->point_data[i] = self->point_data[i + 1];
-
- self->point_data = g_realloc (self->point_data, sizeof (PointData) * (self->n_points / 3 - 1));
-
- self->points[(self->context - 1 + self->n_points) % self->n_points] = self->points[self->context + 2];
+ for (i = self->context; i + 1 < self->n_points; i++)
+ self->points[i] = self->points[i + 1];
- for (i = self->context; i + 3 < self->n_points; i++)
- self->points[i] = self->points[i + 3];
+ self->points = g_realloc (self->points, sizeof (PointData) * (self->n_points - 1));
- self->points = g_realloc (self->points, sizeof (graphene_point_t) * (self->n_points - 3));
-
- self->n_points -= 3;
+ self->n_points -= 1;
}
/* }}} */
/* {{{ Event handlers */
@@ -970,42 +960,43 @@ pressed (GtkGestureClick *gesture,
return;
if (button == GDK_BUTTON_SECONDARY)
- for (i = 0; i < self->n_points; i++)
- {
- if (i % 3 != 0 || !point_is_visible (self, i))
- continue;
+ {
+ for (i = 0; i < self->n_points; i++)
+ {
+ PointData *pd = &self->points[i];
- if (graphene_point_distance (&self->points[i], &m, NULL, NULL) < CLICK_RADIUS)
- {
- GAction *action;
+ if (graphene_point_distance (&pd->p[1], &m, NULL, NULL) < CLICK_RADIUS)
+ {
+ GAction *action;
- self->context = i;
+ self->context = i;
- action = g_action_map_lookup_action (self->actions, "smooth");
- g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (self->point_data[i /
3].smooth));
+ action = g_action_map_lookup_action (self->actions, "smooth");
+ g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (pd->smooth));
- action = g_action_map_lookup_action (self->actions, "symmetric");
- if (self->point_data[i / 3].smooth)
- {
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
- g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean
(self->point_data[i / 3].symmetric));
- }
- else
- {
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
- g_simple_action_set_state (G_SIMPLE_ACTION (action), FALSE);
- }
+ action = g_action_map_lookup_action (self->actions, "symmetric");
+ if (pd->smooth)
+ {
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+ g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean
(pd->symmetric));
+ }
+ else
+ {
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+ g_simple_action_set_state (G_SIMPLE_ACTION (action), FALSE);
+ }
- action = g_action_map_lookup_action (self->actions, "operation");
+ action = g_action_map_lookup_action (self->actions, "operation");
- g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_string (op_to_string
(self->point_data[i / 3].op)));
+ g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_string (op_to_string
(pd->op)));
- gtk_popover_set_pointing_to (GTK_POPOVER (self->menu),
- &(const GdkRectangle){ x, y, 1, 1 });
- gtk_popover_popup (GTK_POPOVER (self->menu));
- return;
- }
- }
+ gtk_popover_set_pointing_to (GTK_POPOVER (self->menu),
+ &(const GdkRectangle){ x, y, 1, 1 });
+ gtk_popover_popup (GTK_POPOVER (self->menu));
+ return;
+ }
+ }
+ }
}
static void
@@ -1024,18 +1015,15 @@ released (GtkGestureClick *gesture,
for (i = 0; i < self->n_points; i++)
{
- if (!point_is_visible (self, i))
- continue;
+ PointData *pd = &self->points[i];
- if (graphene_point_distance (&self->points[i], &m, NULL, NULL) < CLICK_RADIUS)
+ if (graphene_point_distance (&pd->p[1], &m, NULL, NULL) < CLICK_RADIUS)
{
- if (i % 3 == 0)
+ if (button == GDK_BUTTON_PRIMARY)
{
- if (button == GDK_BUTTON_PRIMARY)
- {
- self->point_data[i / 3].edit = !self->point_data[i / 3].edit;
- return;
- }
+ pd->edit = !pd->edit;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ return;
}
}
}
@@ -1060,26 +1048,36 @@ motion (GtkEventControllerMotion *controller,
CurveEditor *self)
{
graphene_point_t m = GRAPHENE_POINT_INIT (x, y);
- int i;
- int was_hovered = self->hovered;
-
- self->hovered = -1;
+ int i, j;
+ gboolean changed = FALSE;
if (self->edit)
- for (i = 0; i < self->n_points; i++)
- {
- if (!point_is_visible (self, i))
- continue;
+ {
+ for (i = 0; i < self->n_points; i++)
+ {
+ PointData *pd = &self->points[i];
+ int hovered = -1;
- if (graphene_point_distance (&self->points[i], &m, NULL, NULL) < CLICK_RADIUS)
- {
- if (self->hovered != i)
- self->hovered = i;
- break;
- }
+ for (j = 0; j < 3; j++)
+ {
+ if (!point_is_visible (self, i, j))
+ continue;
+
+ if (graphene_point_distance (&pd->p[j], &m, NULL, NULL) < CLICK_RADIUS)
+ {
+ hovered = j;
+ break;
+ }
+ }
+ if (pd->hovered != hovered)
+ {
+ pd->hovered = hovered;
+ changed = TRUE;
+ }
+ }
}
- if (self->hovered != was_hovered)
+ if (changed)
gtk_widget_queue_draw (GTK_WIDGET (self));
}
@@ -1087,11 +1085,21 @@ static void
leave (GtkEventController *controller,
CurveEditor *self)
{
- if (self->hovered != -1)
+ int i;
+ gboolean changed = FALSE;
+
+ for (i = 0; i < self->n_points; i++)
{
- self->hovered = -1;
- gtk_widget_queue_draw (GTK_WIDGET (self));
+ PointData *pd = &self->points[i];
+ if (pd->hovered != -1)
+ {
+ pd->hovered = -1;
+ changed = TRUE;
+ }
}
+
+ if (changed)
+ gtk_widget_queue_draw (GTK_WIDGET (self));
}
/* }}} */
/* {{{ Snapshot */
@@ -1103,7 +1111,7 @@ curve_editor_snapshot (GtkWidget *widget,
GskPathBuilder *builder;
GskPath *path;
GskStroke *stroke;
- int i, j;
+ int i, j, k;
float width;
float height;
@@ -1118,19 +1126,23 @@ curve_editor_snapshot (GtkWidget *widget,
if (self->edit)
{
/* Add the skeleton */
- gsk_path_builder_move_to (builder, self->points[0].x, self->points[0].y);
- for (i = 1; i < self->n_points; i++)
+ for (i = 0; i < self->n_points; i++)
{
- if (i % 3 != 2 &&
- point_is_visible (self, i) &&
- point_is_visible (self, (i - 1 + self->n_points) % self->n_points))
- gsk_path_builder_line_to (builder, self->points[i].x, self->points[i].y);
- else
- gsk_path_builder_move_to (builder, self->points[i].x, self->points[i].y);
+ PointData *pd = &self->points[i];
+ gboolean need_move = TRUE;
+ if (point_is_visible (self, i, 0))
+ {
+ gsk_path_builder_move_to (builder, pd->p[0].x, pd->p[0].y);
+ gsk_path_builder_line_to (builder, pd->p[1].x, pd->p[1].y);
+ need_move = FALSE;
+ }
+ if (point_is_visible (self, i, 2))
+ {
+ if (need_move)
+ gsk_path_builder_move_to (builder, pd->p[1].x, pd->p[1].y);
+ gsk_path_builder_line_to (builder, pd->p[2].x, pd->p[2].y);
+ }
}
- if (point_is_visible (self, 0) &&
- point_is_visible (self, self->n_points - 1))
- gsk_path_builder_line_to (builder, self->points[0].x, self->points[0].y);
}
/* Add the curve itself */
@@ -1163,68 +1175,62 @@ curve_editor_snapshot (GtkWidget *widget,
};
GdkRGBA color;
- for (j = 0; j < 4; j++)
+ for (k = 0; k < 4; k++)
{
builder = gsk_path_builder_new ();
for (i = 0; i < self->n_points; i++)
{
- switch (j)
+ PointData *pd = &self->points[i];
+
+ for (j = 0; j < 3; j++)
{
- case 0:
- if (i != self->hovered)
- continue;
- break;
+ switch (k)
+ {
+ case 0:
+ if (j != pd->hovered)
+ continue;
+ break;
- case 1:
- if (i == self->hovered)
- continue;
+ case 1:
+ if (j == pd->hovered)
+ continue;
- if (!(i % 3 == 0 &&
- self->point_data[i / 3].smooth))
- continue;
- break;
+ if (!(j == 1 && pd->smooth))
+ continue;
+ break;
- case 2:
- if (i == self->hovered)
- continue;
+ case 2:
+ if (j == pd->hovered)
+ continue;
- if (!(i % 3 == 0 &&
- !self->point_data[i / 3].smooth))
- continue;
- break;
+ if (!(j == 1 && !pd->smooth))
+ continue;
+ break;
- case 3:
- if (i == self->hovered)
- continue;
+ case 3:
+ if (j == pd->hovered)
+ continue;
- if (i % 3 == 1)
- {
- if (!(self->point_data[i / 3].edit &&
- self->point_data[i / 3].op == CURVE))
+ if (j == 1)
continue;
- }
- else if (i % 3 == 2)
- {
- if (!(self->point_data[((i + 3) % self->n_points) / 3].edit &&
- self->point_data[i / 3].op == CURVE))
+
+ if (!point_is_visible (self, i, j))
continue;
+ break;
+
+ default:
+ g_assert_not_reached ();
}
- else
- continue;
- break;
- default:
- g_assert_not_reached ();
+ gsk_path_builder_add_circle (builder, &pd->p[j], DRAW_RADIUS);
}
-
- gsk_path_builder_add_circle (builder, &self->points[i], DRAW_RADIUS);
}
path = gsk_path_builder_free_to_path (builder);
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
- gdk_rgba_parse (&color, colors[j]);
+ gdk_rgba_parse (&color, colors[k]);
gtk_snapshot_append_color (snapshot, &color, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
@@ -1273,7 +1279,6 @@ curve_editor_dispose (GObject *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 (curve_editor_parent_class)->dispose (object);
@@ -1324,7 +1329,6 @@ curve_editor_init (CurveEditor *self)
gtk_widget_add_controller (GTK_WIDGET (self), controller);
self->points = NULL;
- self->point_data = NULL;
self->n_points = 0;
self->actions = G_ACTION_MAP (g_simple_action_group_new ());
@@ -1403,9 +1407,11 @@ curve_editor_set_edit (CurveEditor *self,
self->edit = edit;
if (!self->edit)
{
- self->hovered = -1;
- for (i = 0; i < self->n_points / 3; i++)
- self->point_data[i].edit = FALSE;
+ for (i = 0; i < self->n_points; i++)
+ {
+ self->points[i].edit = FALSE;
+ self->points[i].hovered = -1;
+ }
}
gtk_widget_queue_draw (GTK_WIDGET (self));
@@ -1420,7 +1426,6 @@ curve_editor_set_path (CurveEditor *self,
int i;
g_clear_pointer (&self->points, g_free);
- g_clear_pointer (&self->point_data, g_free);
self->n_points = 0;
data.count = 0;
@@ -1433,16 +1438,20 @@ curve_editor_set_path (CurveEditor *self,
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);
+ self->n_points = data.count;
+ self->points = g_new0 (PointData, self->n_points);
+ for (i = 0; i < self->n_points; i++)
+ {
+ self->points[i].smooth = FALSE;
+ self->points[i].hovered = -1;
+ }
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);
+ for (i = 0; i < self->n_points; i++)
+ check_smoothness (self, i);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]