Implement adding points

This commit is contained in:
Matthias Clasen
2020-11-20 23:47:12 -05:00
parent 4f5d05634a
commit 95867222b8

View File

@@ -1,6 +1,3 @@
/* TODO: point insert/remove
*/
#include "curve-editor.h" #include "curve-editor.h"
#include <gtk/gtk.h> #include <gtk/gtk.h>
@@ -463,6 +460,304 @@ remove_point (GSimpleAction *action,
self->n_points -= 3; self->n_points -= 3;
} }
static void
find_line_point (graphene_point_t *a,
graphene_point_t *b,
graphene_point_t *p,
double *t,
graphene_point_t *pp,
double *d)
{
graphene_vec2_t n;
graphene_vec2_t ap;
float tt;
graphene_vec2_init (&n, b->x - a->x, b->y - a->y);
graphene_vec2_init (&ap, p->x - a->x, p->y - a->y);
tt = graphene_vec2_dot (&ap, &n) / graphene_vec2_dot (&n, &n);
if (tt < 0)
{
*pp = *a;
*t = 0;
*d = graphene_point_distance (a, p, NULL, NULL);
}
else if (tt > 1)
{
*pp = *b;
*t = 1;
*d = graphene_point_distance (b, p, NULL, NULL);
}
else
{
pp->x = a->x + tt * (b->x - a->x);
pp->y = a->y + tt * (b->y - a->y);
*t = tt;
*d = graphene_point_distance (pp, p, NULL, NULL);
}
}
static void
gsk_split_get_coefficients (graphene_point_t coeffs[4],
const graphene_point_t pts[4])
{
coeffs[0] = GRAPHENE_POINT_INIT (pts[3].x - 3.0f * pts[2].x + 3.0f * pts[1].x - pts[0].x,
pts[3].y - 3.0f * pts[2].y + 3.0f * pts[1].y - pts[0].y);
coeffs[1] = GRAPHENE_POINT_INIT (3.0f * pts[2].x - 6.0f * pts[1].x + 3.0f * pts[0].x,
3.0f * pts[2].y - 6.0f * pts[1].y + 3.0f * pts[0].y);
coeffs[2] = GRAPHENE_POINT_INIT (3.0f * pts[1].x - 3.0f * pts[0].x,
3.0f * pts[1].y - 3.0f * pts[0].y);
coeffs[3] = pts[0];
}
static void
gsk_spline_get_point_cubic (const graphene_point_t pts[4],
float progress,
graphene_point_t *pos,
graphene_vec2_t *tangent)
{
graphene_point_t c[4];
gsk_split_get_coefficients (c, pts);
if (pos)
*pos = GRAPHENE_POINT_INIT (((c[0].x * progress + c[1].x) * progress +c[2].x) * progress + c[3].x,
((c[0].y * progress + c[1].y) * progress +c[2].y) * progress + c[3].y);
if (tangent)
{
graphene_vec2_init (tangent,
(3.0f * c[0].x * progress + 2.0f * c[1].x) * progress + c[2].x,
(3.0f * c[0].y * progress + 2.0f * c[1].y) * progress + c[2].y);
graphene_vec2_normalize (tangent, tangent);
}
}
static void
find_curve_point (graphene_point_t *points,
graphene_point_t *p,
double *t,
graphene_point_t *pp,
double *d)
{
graphene_point_t q;
graphene_point_t best_p;
double best_d;
double best_t;
double dd;
double tt;
int i;
best_d = G_MAXDOUBLE;
best_t = 0;
for (i = 0; i < 20; i++)
{
tt = i / 20.0;
gsk_spline_get_point_cubic (points, tt, &q, NULL);
dd = graphene_point_distance (&q, p, NULL, NULL);
if (dd < best_d)
{
best_d = dd;
best_t = tt;
best_p = q;
}
}
/* TODO: bisect from here */
*t = best_t;
*pp = best_p;
*d = best_d;
}
static void
find_closest_point (CurveEditor *self,
graphene_point_t *p,
int *point,
double *t,
double *d)
{
int i;
int best_i;
double best_d;
double best_t;
double tt;
double dd;
graphene_point_t pp;
best_i = -1;
best_d = G_MAXDOUBLE;
best_t = 0;
for (i = 0; i < self->n_points; i++)
{
if (i % 3 != 0)
continue;
switch (self->point_data[i / 3].op)
{
case MOVE:
continue;
case LINE:
find_line_point (&self->points[i], &self->points[(i + 3) % self->n_points], p, &tt, &pp, &dd);
if (dd < best_d)
{
best_i = i;
best_d = dd;
best_t = tt;
}
break;
case CURVE:
{
graphene_point_t points[4];
int k;
for (k = 0; k < 4; k++)
points[k] = self->points[(i + k) % self->n_points];
find_curve_point (points, p, &tt, &pp, &dd);
if (dd < best_d)
{
best_i = i;
best_d = dd;
best_t = tt;
}
}
break;
default:
g_assert_not_reached ();
}
}
*point = best_i;
*t = best_t;
*d = best_d;
}
static void
split_bezier (graphene_point_t *points,
int length,
float t,
graphene_point_t *left,
int *left_pos,
graphene_point_t *right,
int *right_pos)
{
if (length == 1)
{
left[*left_pos] = points[0];
(*left_pos)++;
right[*right_pos] = points[0];
(*right_pos)++;
}
else
{
graphene_point_t *newpoints;
int i;
newpoints = g_alloca (sizeof (graphene_point_t) * (length - 1));
for (i = 0; i < length - 1; i++)
{
if (i == 0)
{
left[*left_pos] = points[i];
(*left_pos)++;
}
if (i == length - 2)
{
right[*right_pos] = points[i + 1];
(*right_pos)++;
}
graphene_point_interpolate (&points[i], &points[i+1], t, &newpoints[i]);
}
split_bezier (newpoints, length - 1, t, left, left_pos, right, right_pos);
}
}
static void
insert_point (CurveEditor *self,
int point,
double pos)
{
Operation op = self->point_data[point / 3].op;
int i;
graphene_point_t points[4];
int k;
if (op == MOVE)
return;
for (k = 0; k < 4; k++)
points[k] = self->points[(point + k) % self->n_points];
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 (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 += 3;
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;
}
else if (op == CURVE)
{
graphene_point_t left[4];
graphene_point_t right[4];
int left_pos = 0;
int right_pos = 0;
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->point_data[point / 3 + 1].smooth = TRUE;
self->point_data[point / 3 + 1].op = CURVE;
}
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
maybe_insert_point (CurveEditor *self,
double x,
double y)
{
graphene_point_t m = GRAPHENE_POINT_INIT (x, y);
int point;
double t;
double d;
find_closest_point (self, &m, &point, &t, &d);
if (d > CLICK_RADIUS)
return;
insert_point (self, point, t);
}
static void static void
pressed (GtkGestureClick *gesture, pressed (GtkGestureClick *gesture,
int n_press, int n_press,
@@ -514,6 +809,7 @@ released (GtkGestureClick *gesture,
CurveEditor *self) CurveEditor *self)
{ {
graphene_point_t m = GRAPHENE_POINT_INIT (x, y); graphene_point_t m = GRAPHENE_POINT_INIT (x, y);
int button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
int i; int i;
if (!self->edit) if (!self->edit)
@@ -525,10 +821,10 @@ released (GtkGestureClick *gesture,
{ {
if (i % 3 == 0) if (i % 3 == 0)
{ {
int button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
if (button == GDK_BUTTON_PRIMARY) if (button == GDK_BUTTON_PRIMARY)
{ {
self->point_data[i / 3].edit = !self->point_data[i / 3].edit; self->point_data[i / 3].edit = !self->point_data[i / 3].edit;
return;
} }
else if (button == GDK_BUTTON_SECONDARY) else if (button == GDK_BUTTON_SECONDARY)
{ {
@@ -546,10 +842,15 @@ released (GtkGestureClick *gesture,
d = graphene_point_distance (c, p, NULL, NULL); d = graphene_point_distance (c, p, NULL, NULL);
opposite_point (p, c2, d, c); opposite_point (p, c2, d, c);
} }
return;
} }
} }
} }
} }
if (button == GDK_BUTTON_PRIMARY)
maybe_insert_point (self, x, y);
} }
static void static void
@@ -926,23 +1227,19 @@ copy_segments (GskPathOperation op,
gpointer data) gpointer data)
{ {
CopySegmentData *d = data; CopySegmentData *d = data;
int i;
switch (op) switch (op)
{ {
case GSK_PATH_MOVE: case GSK_PATH_MOVE:
if (d->pos == 0) 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].op = MOVE;
d->editor->point_data[d->pos / 3].smooth = FALSE; 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]; 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];
} }
break; break;
case GSK_PATH_CLOSE: case GSK_PATH_CLOSE:
@@ -956,7 +1253,8 @@ copy_segments (GskPathOperation op,
d->editor->points[d->pos++] = pts[1]; d->editor->points[d->pos++] = pts[1];
d->editor->points[d->pos++] = pts[1]; 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];
break; break;
case GSK_PATH_CURVE: case GSK_PATH_CURVE:
d->editor->point_data[d->pos / 3].op = CURVE; d->editor->point_data[d->pos / 3].op = CURVE;
@@ -965,8 +1263,11 @@ copy_segments (GskPathOperation op,
if (d->pos == 0) if (d->pos == 0)
d->editor->points[d->pos++] = pts[0]; d->editor->points[d->pos++] = pts[0];
for (i = 1; i < n_pts; i++) d->editor->points[d->pos++] = pts[1];
d->editor->points[d->pos++] = pts[i]; d->editor->points[d->pos++] = pts[2];
if (d->pos < d->editor->n_points)
d->editor->points[d->pos++] = pts[3];
break; break;
default: default:
g_assert_not_reached (); g_assert_not_reached ();