Compare commits
2 Commits
wip/matthi
...
editor-exp
Author | SHA1 | Date | |
---|---|---|---|
|
dd6f5597c1 | ||
|
5dfeaf3a6f |
1882
demos/gtk-demo/cepath.c
Normal file
1882
demos/gtk-demo/cepath.c
Normal file
File diff suppressed because it is too large
Load Diff
90
demos/gtk-demo/cepath.h
Normal file
90
demos/gtk-demo/cepath.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define CE_TYPE_PATH (ce_path_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (CEPath, ce_path, CE, PATH, GObject)
|
||||
|
||||
typedef struct _CEPathCurve CEPathCurve;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CE_PATH_CUSP,
|
||||
CE_PATH_SMOOTH,
|
||||
CE_PATH_SYMMETRIC,
|
||||
CE_PATH_AUTOMATIC
|
||||
} CEPathConstraint;
|
||||
|
||||
CEPath * ce_path_new (void);
|
||||
|
||||
int ce_path_get_n_curves (CEPath *self);
|
||||
|
||||
CEPathCurve * ce_path_get_curve (CEPath *self,
|
||||
int idx);
|
||||
|
||||
CEPathCurve * ce_path_previous_curve (CEPath *self,
|
||||
CEPathCurve *seg);
|
||||
|
||||
CEPathCurve * ce_path_next_curve (CEPath *self,
|
||||
CEPathCurve *seg);
|
||||
|
||||
void ce_path_set_gsk_path (CEPath *self,
|
||||
GskPath *path);
|
||||
|
||||
GskPath * ce_path_get_gsk_path (CEPath *self);
|
||||
|
||||
void ce_path_split_curve (CEPath *self,
|
||||
CEPathCurve *seg,
|
||||
float pos);
|
||||
|
||||
void ce_path_remove_curve (CEPath *self,
|
||||
CEPathCurve *seg);
|
||||
|
||||
void ce_path_drag_curve (CEPath *self,
|
||||
CEPathCurve *seg,
|
||||
const graphene_point_t *pos);
|
||||
|
||||
CEPathCurve * ce_path_find_closest_curve (CEPath *self,
|
||||
graphene_point_t *point,
|
||||
float threshold,
|
||||
graphene_point_t *p,
|
||||
float *pos);
|
||||
|
||||
void ce_path_get_point (CEPath *self,
|
||||
CEPathCurve *seg,
|
||||
int c,
|
||||
graphene_point_t *point);
|
||||
|
||||
void ce_path_set_point (CEPath *self,
|
||||
CEPathCurve *seg,
|
||||
int point,
|
||||
const graphene_point_t *pos);
|
||||
|
||||
GskPathOperation
|
||||
ce_path_get_operation (CEPath *self,
|
||||
CEPathCurve *seg);
|
||||
|
||||
void ce_path_set_operation (CEPath *self,
|
||||
CEPathCurve *seg,
|
||||
GskPathOperation op);
|
||||
|
||||
float ce_path_get_weight (CEPath *self,
|
||||
CEPathCurve *seg);
|
||||
|
||||
void ce_path_set_weight (CEPath *self,
|
||||
CEPathCurve *seg,
|
||||
float weight);
|
||||
|
||||
CEPathConstraint
|
||||
ce_path_get_constraint (CEPath *self,
|
||||
CEPathCurve *seg);
|
||||
|
||||
void ce_path_set_constraint (CEPath *self,
|
||||
CEPathCurve *seg,
|
||||
CEPathConstraint constraint);
|
||||
|
||||
|
||||
|
||||
G_END_DECLS
|
File diff suppressed because it is too large
Load Diff
@@ -33,10 +33,12 @@ make_circle_path (void)
|
||||
gsk_path_builder_cubic_to (builder, cx - kr, h - pad,
|
||||
pad, cy + kr,
|
||||
pad, cy);
|
||||
#if 0
|
||||
gsk_path_builder_cubic_to (builder, pad, cy - kr,
|
||||
cx - kr, pad,
|
||||
cx, pad);
|
||||
gsk_path_builder_close (builder);
|
||||
#endif
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
@@ -144,6 +144,7 @@ extra_demo_sources = files([
|
||||
'nodewidget.c',
|
||||
'graphwidget.c',
|
||||
'curve-editor.c',
|
||||
'cepath.c',
|
||||
])
|
||||
|
||||
if os_unix
|
||||
|
645
gtk/gtkcontour.c
Normal file
645
gtk/gtkcontour.c
Normal file
@@ -0,0 +1,645 @@
|
||||
#include "gtkcontour.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include "gsk/gskcurveprivate.h"
|
||||
|
||||
typedef struct _GtkCurve GtkCurve;
|
||||
struct _GtkCurve
|
||||
{
|
||||
GskPathOperation op;
|
||||
graphene_point_t p[4];
|
||||
float weight;
|
||||
};
|
||||
|
||||
struct _GtkContour
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GPtrArray *curves;
|
||||
graphene_point_t start;
|
||||
gboolean closed;
|
||||
};
|
||||
|
||||
struct _GtkContourClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkContour, gtk_contour, G_TYPE_OBJECT)
|
||||
|
||||
|
||||
static void
|
||||
gtk_contour_init (GtkContour *self)
|
||||
{
|
||||
self->curves = g_ptr_array_new_with_free_func (g_free);
|
||||
self->closed = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_contour_finalize (GObject *object)
|
||||
{
|
||||
GtkContour *self = GTK_CONTOUR (object);
|
||||
|
||||
g_ptr_array_unref (self->curves);
|
||||
|
||||
G_OBJECT_CLASS (gtk_contour_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_contour_class_init (GtkContourClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = gtk_contour_finalize;
|
||||
}
|
||||
|
||||
GtkContour *
|
||||
gtk_contour_new (const graphene_point_t *start)
|
||||
{
|
||||
GtkContour *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_CONTOUR, NULL);
|
||||
|
||||
self->start = *start;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
gtk_contour_get_n_curves (GtkContour *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CONTOUR (self), 0);
|
||||
|
||||
return self->curves->len;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
gtk_contour_get_n_points (GtkContour *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CONTOUR (self), 0);
|
||||
|
||||
return self->curves->len + (1 - self->closed);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_set_point (GtkContour *self,
|
||||
unsigned int pos,
|
||||
const graphene_point_t *point)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (pos < self->curves->len);
|
||||
g_return_if_fail (point != NULL);
|
||||
|
||||
if (pos < self->curves->len)
|
||||
{
|
||||
curve = g_ptr_array_index (self->curves, pos);
|
||||
curve->p[0] = *point;
|
||||
}
|
||||
|
||||
if (pos == 0 && self->closed)
|
||||
pos = self->curves->len - 1;
|
||||
|
||||
if (pos > 0)
|
||||
{
|
||||
curve = g_ptr_array_index (self->curves, pos - 1);
|
||||
curve->p[3] = *point;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_get_point (GtkContour *self,
|
||||
unsigned int pos,
|
||||
graphene_point_t *point)
|
||||
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (pos < self->curves->len + (1 - self->closed));
|
||||
g_return_if_fail (point != NULL);
|
||||
|
||||
if (pos < self->curves->len)
|
||||
{
|
||||
curve = g_ptr_array_index (self->curves, pos);
|
||||
*point = curve->p[0];
|
||||
}
|
||||
else if (pos == self->curves->len)
|
||||
{
|
||||
curve = g_ptr_array_index (self->curves, pos - 1);
|
||||
*point = curve->p[3];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_get_curve (GtkContour *self,
|
||||
unsigned int pos,
|
||||
GskPathOperation *op,
|
||||
graphene_point_t pts[4],
|
||||
float *weight)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (pos < self->curves->len);
|
||||
|
||||
curve = g_ptr_array_index (self->curves, pos);
|
||||
if (op)
|
||||
*op = curve->op;
|
||||
if (pts)
|
||||
memcpy (pts, curve->p, sizeof (graphene_point_t) * 4);
|
||||
if (weight)
|
||||
*weight = curve->weight;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_set_curve (GtkContour *self,
|
||||
unsigned int pos,
|
||||
GskPathOperation op,
|
||||
graphene_point_t p[4],
|
||||
float weight)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (pos < self->curves->len);
|
||||
|
||||
curve = g_ptr_array_index (self->curves, pos);
|
||||
curve->op = op;
|
||||
memcpy (curve->p, p, sizeof (graphene_point_t) * 4);
|
||||
curve->weight = weight;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_set_line (GtkContour *self,
|
||||
unsigned int pos)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (pos < self->curves->len);
|
||||
|
||||
curve = g_ptr_array_index (self->curves, pos);
|
||||
curve->op = GSK_PATH_LINE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_set_quad (GtkContour *self,
|
||||
unsigned int pos,
|
||||
const graphene_point_t *cp)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (pos < self->curves->len);
|
||||
g_return_if_fail (cp != NULL);
|
||||
|
||||
curve = g_ptr_array_index (self->curves, pos);
|
||||
curve->op = GSK_PATH_QUAD;
|
||||
curve->p[1] = *cp;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_set_cubic (GtkContour *self,
|
||||
unsigned int pos,
|
||||
const graphene_point_t *cp1,
|
||||
const graphene_point_t *cp2)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (pos < self->curves->len);
|
||||
g_return_if_fail (cp1 != NULL);
|
||||
g_return_if_fail (cp2 != NULL);
|
||||
|
||||
curve = g_ptr_array_index (self->curves, pos);
|
||||
curve->op = GSK_PATH_CUBIC;
|
||||
curve->p[1] = *cp1;
|
||||
curve->p[2] = *cp2;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_set_conic (GtkContour *self,
|
||||
unsigned int pos,
|
||||
const graphene_point_t *cp,
|
||||
float weight)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (pos < self->curves->len);
|
||||
g_return_if_fail (cp != NULL);
|
||||
g_return_if_fail (0 < weight);
|
||||
|
||||
curve = g_ptr_array_index (self->curves, pos);
|
||||
curve->op = GSK_PATH_CUBIC;
|
||||
curve->p[1] = *cp;
|
||||
curve->weight = weight;
|
||||
}
|
||||
|
||||
static inline const graphene_point_t *
|
||||
get_start_point (GtkContour *self)
|
||||
{
|
||||
if (self->curves->len > 0)
|
||||
{
|
||||
GtkCurve *first = g_ptr_array_index (self->curves, 0);
|
||||
return &first->p[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return &self->start;
|
||||
}
|
||||
}
|
||||
|
||||
static inline const graphene_point_t *
|
||||
get_end_point (GtkContour *self)
|
||||
{
|
||||
if (self->curves->len > 0)
|
||||
{
|
||||
GtkCurve *last = g_ptr_array_index (self->curves, self->curves->len - 1);
|
||||
return &last->p[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
return &self->start;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_line_to (GtkContour *self,
|
||||
const graphene_point_t *end)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (!self->closed);
|
||||
g_return_if_fail (end != NULL);
|
||||
|
||||
curve = g_new0 (GtkCurve, 1);
|
||||
curve->op = GSK_PATH_LINE;
|
||||
curve->p[0] = *get_end_point (self);
|
||||
curve->p[3] = *end;
|
||||
|
||||
g_ptr_array_add (self->curves, curve);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_quad_to (GtkContour *self,
|
||||
const graphene_point_t *cp,
|
||||
const graphene_point_t *end)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (!self->closed);
|
||||
g_return_if_fail (cp != NULL);
|
||||
g_return_if_fail (end != NULL);
|
||||
|
||||
curve = g_new0 (GtkCurve, 1);
|
||||
curve->op = GSK_PATH_QUAD;
|
||||
curve->p[0] = *get_end_point (self);
|
||||
curve->p[1] = *cp;
|
||||
curve->p[3] = *end;
|
||||
|
||||
g_ptr_array_add (self->curves, curve);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_cubic_to (GtkContour *self,
|
||||
const graphene_point_t *cp1,
|
||||
const graphene_point_t *cp2,
|
||||
const graphene_point_t *end)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (!self->closed);
|
||||
g_return_if_fail (cp1 != NULL);
|
||||
g_return_if_fail (cp2 != NULL);
|
||||
g_return_if_fail (end != NULL);
|
||||
|
||||
curve = g_new0 (GtkCurve, 1);
|
||||
curve->op = GSK_PATH_CUBIC;
|
||||
curve->p[0] = *get_end_point (self);
|
||||
curve->p[1] = *cp1;
|
||||
curve->p[2] = *cp2;
|
||||
curve->p[3] = *end;
|
||||
|
||||
g_ptr_array_add (self->curves, curve);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_conic_to (GtkContour *self,
|
||||
const graphene_point_t *cp,
|
||||
float weight,
|
||||
const graphene_point_t *end)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (!self->closed);
|
||||
g_return_if_fail (cp != NULL);
|
||||
g_return_if_fail (weight > 0);
|
||||
g_return_if_fail (end != NULL);
|
||||
|
||||
curve = g_new0 (GtkCurve, 1);
|
||||
curve->op = GSK_PATH_CONIC;
|
||||
curve->p[0] = *get_end_point (self);
|
||||
curve->p[1] = *cp;
|
||||
curve->p[3] = *end;
|
||||
curve->weight = weight;
|
||||
|
||||
g_ptr_array_add (self->curves, curve);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_contour_is_closed (GtkContour *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CONTOUR (self), FALSE);
|
||||
|
||||
return self->closed;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_close (GtkContour *self)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
|
||||
if (self->curves->len == 0 || self->closed)
|
||||
return;
|
||||
|
||||
if (!graphene_point_near (get_start_point (self), get_end_point (self), 0.01))
|
||||
gtk_contour_line_to (self, get_start_point (self));
|
||||
|
||||
self->closed = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_append (GtkContour *self,
|
||||
GtkContour *contour)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (GTK_IS_CONTOUR (contour));
|
||||
g_return_if_fail (!self->closed);
|
||||
g_return_if_fail (!contour->closed);
|
||||
|
||||
if (contour->curves->len > 0)
|
||||
{
|
||||
if (!graphene_point_near (get_end_point (self), get_start_point (contour), 0.01))
|
||||
gtk_contour_line_to (self, get_start_point (contour));
|
||||
|
||||
g_ptr_array_extend_and_steal (self->curves, g_ptr_array_ref (contour->curves));
|
||||
/* now contour has zero curves left */
|
||||
}
|
||||
|
||||
g_object_unref (contour);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_split (GtkContour *self,
|
||||
unsigned int pos,
|
||||
GtkContour **contour1,
|
||||
GtkContour **contour2)
|
||||
{
|
||||
GtkCurve *curve, *curve1;
|
||||
|
||||
GtkContour *c1, *c2;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (pos < self->curves->len);
|
||||
g_return_if_fail (contour1 != NULL);
|
||||
g_return_if_fail (contour2 != NULL);
|
||||
|
||||
curve = g_ptr_array_index (self->curves, pos);
|
||||
|
||||
c1 = gtk_contour_new (get_start_point (self));
|
||||
c2 = gtk_contour_new (&curve->p[3]);
|
||||
|
||||
for (unsigned int i = 0; i < self->curves->len; i++)
|
||||
{
|
||||
curve1 = g_ptr_array_index (self->curves, i);
|
||||
|
||||
if (i < pos)
|
||||
g_ptr_array_add (c1->curves, curve1);
|
||||
else if (i > pos)
|
||||
g_ptr_array_add (c2->curves, curve1);
|
||||
}
|
||||
|
||||
if (self->closed)
|
||||
{
|
||||
/* Reconnect the pieces */
|
||||
gtk_contour_append (c2, c1);
|
||||
|
||||
c1 = c2;
|
||||
c2 = NULL;
|
||||
}
|
||||
|
||||
*contour1 = c1;
|
||||
*contour2 = c2;
|
||||
|
||||
g_ptr_array_set_free_func (self->curves, NULL);
|
||||
g_free (curve);
|
||||
g_object_unref (self);
|
||||
}
|
||||
|
||||
static inline int
|
||||
n_points (GskPathOperation op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_LINE: return 2;
|
||||
case GSK_PATH_QUAD: return 3;
|
||||
case GSK_PATH_CUBIC: return 4;
|
||||
case GSK_PATH_CONIC: return 3;
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CLOSE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
gtk_curve_init_from_gsk (GtkCurve *curve,
|
||||
const GskCurve *c)
|
||||
{
|
||||
memset (curve, 0, sizeof (GtkCurve));
|
||||
curve->op = c->op;
|
||||
curve->weight = 1.f;
|
||||
|
||||
switch (c->op)
|
||||
{
|
||||
case GSK_PATH_LINE:
|
||||
curve->p[0] = c->line.points[0];
|
||||
curve->p[1] = c->line.points[1];
|
||||
break;
|
||||
case GSK_PATH_QUAD:
|
||||
curve->p[0] = c->quad.points[0];
|
||||
curve->p[1] = c->quad.points[1];
|
||||
curve->p[2] = c->quad.points[2];
|
||||
break;
|
||||
case GSK_PATH_CUBIC:
|
||||
curve->p[0] = c->cubic.points[0];
|
||||
curve->p[1] = c->cubic.points[1];
|
||||
curve->p[2] = c->cubic.points[2];
|
||||
curve->p[3] = c->cubic.points[3];
|
||||
break;
|
||||
case GSK_PATH_CONIC:
|
||||
curve->p[0] = c->conic.points[0];
|
||||
curve->p[1] = c->conic.points[1];
|
||||
curve->p[2] = c->conic.points[3];
|
||||
curve->weight = c->conic.points[2].x;
|
||||
break;
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CLOSE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
split_curve (const GtkCurve *curve,
|
||||
float t,
|
||||
GtkCurve *curve1,
|
||||
GtkCurve *curve2)
|
||||
{
|
||||
GskCurve c, c1, c2;
|
||||
|
||||
gsk_curve_init_foreach (&c, curve->op, curve->p, n_points (curve->op), curve->weight);
|
||||
gsk_curve_split (&c, t, &c1, &c2);
|
||||
gtk_curve_init_from_gsk (curve1, &c1);
|
||||
gtk_curve_init_from_gsk (curve2, &c2);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_insert_point (GtkContour *self,
|
||||
unsigned int pos,
|
||||
float t)
|
||||
{
|
||||
GtkCurve *curve, *curve2;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTOUR (self));
|
||||
g_return_if_fail (pos < self->curves->len);
|
||||
|
||||
curve = g_ptr_array_index (self->curves, pos);
|
||||
|
||||
curve2 = g_new0 (GtkCurve, 1);
|
||||
split_curve (curve, t, curve, curve2);
|
||||
g_ptr_array_insert (self->curves, pos + 1, curve2);
|
||||
}
|
||||
|
||||
static void
|
||||
path_builder_add_curve (GskPathBuilder *builder,
|
||||
GtkCurve *curve)
|
||||
{
|
||||
switch (curve->op)
|
||||
{
|
||||
case GSK_PATH_LINE:
|
||||
gsk_path_builder_line_to (builder, curve->p[3].x, curve->p[3].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
gsk_path_builder_quad_to (builder, curve->p[1].x, curve->p[1].y,
|
||||
curve->p[3].x, curve->p[3].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
gsk_path_builder_cubic_to (builder, curve->p[1].x, curve->p[1].y,
|
||||
curve->p[2].x, curve->p[2].y,
|
||||
curve->p[3].x, curve->p[3].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_path_builder_conic_to (builder, curve->p[1].x, curve->p[1].y,
|
||||
curve->p[3].x, curve->p[3].y,
|
||||
curve->weight);
|
||||
break;
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CLOSE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_contour_find_closest_curve (GtkContour *self,
|
||||
const graphene_point_t *point,
|
||||
float threshold,
|
||||
unsigned int *idx,
|
||||
graphene_point_t *p,
|
||||
float *pos)
|
||||
{
|
||||
graphene_point_t pp;
|
||||
float t;
|
||||
unsigned int best;
|
||||
|
||||
best = G_MAXUINT;
|
||||
for (unsigned int i = 0; i < self->curves->len; i++)
|
||||
{
|
||||
GtkCurve *curve = g_ptr_array_index (self->curves, i);
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
GskPathMeasure *measure;
|
||||
GskPathPoint *cp;
|
||||
float t1;
|
||||
graphene_point_t pp1;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_move_to (builder, curve->p[0].x, curve->p[0].y);
|
||||
path_builder_add_curve (builder, curve);
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
measure = gsk_path_measure_new (path);
|
||||
|
||||
cp = gsk_path_measure_get_closest_point (measure, point, threshold);
|
||||
if (cp)
|
||||
{
|
||||
gsk_path_point_get_position (cp, &pp1);
|
||||
t1 = gsk_path_point_get_distance (cp);
|
||||
threshold = graphene_point_distance (&pp1, point, NULL, NULL);
|
||||
gsk_path_point_unref (cp);
|
||||
|
||||
best = i;
|
||||
t = t1 / gsk_path_measure_get_length (measure);
|
||||
pp = pp1;
|
||||
}
|
||||
|
||||
gsk_path_measure_unref (measure);
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
|
||||
if (best != G_MAXUINT)
|
||||
{
|
||||
if (idx)
|
||||
*idx = best;
|
||||
if (pos)
|
||||
*pos = t;
|
||||
if (p)
|
||||
*p = pp;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_contour_add_to_path_builder (GtkContour *self,
|
||||
GskPathBuilder *builder)
|
||||
{
|
||||
GtkCurve *curve;
|
||||
|
||||
if (self->curves->len == 0)
|
||||
return;
|
||||
|
||||
curve = g_ptr_array_index (self->curves, 0);
|
||||
gsk_path_builder_move_to (builder, curve->p[0].x, curve->p[0].y);
|
||||
|
||||
for (unsigned int i = 0; i < self->curves->len; i++)
|
||||
{
|
||||
curve = g_ptr_array_index (self->curves, i);
|
||||
path_builder_add_curve (builder, curve);
|
||||
}
|
||||
|
||||
if (self->closed)
|
||||
gsk_path_builder_close (builder);
|
||||
}
|
118
gtk/gtkcontour.h
Normal file
118
gtk/gtkcontour.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CONTOUR (gtk_contour_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkContour, gtk_contour, GTK, CONTOUR, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkContour * gtk_contour_new (const graphene_point_t *start);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
unsigned int gtk_contour_get_n_curves (GtkContour *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
unsigned int gtk_contour_get_n_points (GtkContour *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_contour_is_closed (GtkContour *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_close (GtkContour *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_set_point (GtkContour *self,
|
||||
unsigned int pos,
|
||||
const graphene_point_t *point);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_get_point (GtkContour *self,
|
||||
unsigned int pos,
|
||||
graphene_point_t *point);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_get_curve (GtkContour *self,
|
||||
unsigned int pos,
|
||||
GskPathOperation *op,
|
||||
graphene_point_t pts[4],
|
||||
float *weight);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_set_curve (GtkContour *self,
|
||||
unsigned int pos,
|
||||
GskPathOperation op,
|
||||
graphene_point_t pts[4],
|
||||
float weight);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_set_line (GtkContour *self,
|
||||
unsigned int pos);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_set_quad (GtkContour *self,
|
||||
unsigned int pos,
|
||||
const graphene_point_t *cp);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_set_cubic (GtkContour *self,
|
||||
unsigned int pos,
|
||||
const graphene_point_t *cp1,
|
||||
const graphene_point_t *cp2);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_set_conic (GtkContour *self,
|
||||
unsigned int pos,
|
||||
const graphene_point_t *cp,
|
||||
float weight);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_line_to (GtkContour *self,
|
||||
const graphene_point_t *end);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_quad_to (GtkContour *self,
|
||||
const graphene_point_t *cp,
|
||||
const graphene_point_t *end);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_cubic_to (GtkContour *self,
|
||||
const graphene_point_t *cp1,
|
||||
const graphene_point_t *cp2,
|
||||
const graphene_point_t *end);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_conic_to (GtkContour *self,
|
||||
const graphene_point_t *cp,
|
||||
float weight,
|
||||
const graphene_point_t *end);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_append (GtkContour *self,
|
||||
GtkContour *contour);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_split (GtkContour *self,
|
||||
unsigned int pos,
|
||||
GtkContour **contour1,
|
||||
GtkContour **contour2);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_insert_point (GtkContour *self,
|
||||
unsigned int idx,
|
||||
float t);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_contour_find_closest_curve (GtkContour *self,
|
||||
const graphene_point_t *point,
|
||||
float threshold,
|
||||
unsigned int *idx,
|
||||
graphene_point_t *p,
|
||||
float *pos);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_contour_add_to_path_builder (GtkContour *self,
|
||||
GskPathBuilder *builder);
|
||||
|
||||
G_END_DECLS
|
714
gtk/gtkpath.c
Normal file
714
gtk/gtkpath.c
Normal file
@@ -0,0 +1,714 @@
|
||||
#include "config.h"
|
||||
#include "gtkpath.h"
|
||||
#include "gskcurveprivate.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
graphene_point_t point;
|
||||
float weight;
|
||||
} PointWeight;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskPathOperation op;
|
||||
gsize idx;
|
||||
} PathOp;
|
||||
|
||||
struct _GtkPath
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GArray *ops;
|
||||
GArray *points;
|
||||
};
|
||||
|
||||
struct _GtkPathClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkPath, gtk_path, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gtk_path_init (GtkPath *self)
|
||||
{
|
||||
self->ops = g_array_new (FALSE, FALSE, sizeof (PathOp));
|
||||
self->points = g_array_new (FALSE, FALSE, sizeof (PointWeight));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_finalize (GObject *object)
|
||||
{
|
||||
GtkPath *self = GTK_PATH (object);
|
||||
|
||||
g_array_unref (self->ops);
|
||||
g_array_unref (self->points);
|
||||
|
||||
G_OBJECT_GET_CLASS (object)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_class_init (GtkPathClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = gtk_path_finalize;
|
||||
}
|
||||
|
||||
GtkPath *
|
||||
gtk_path_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_PATH, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
add_curve (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkPath *self = GTK_PATH (user_data);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
g_array_append_val (self->ops, ((PathOp) { op, self->points->len }));
|
||||
g_array_append_val (self->points, ((PointWeight) { pts[0], 1.f } ));
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
g_array_append_val (self->ops, ((PathOp) { op, self->points->len - 1 }));
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
g_array_append_val (self->ops, ((PathOp) { op, self->points->len - 1 }));
|
||||
g_array_append_val (self->points, ((PointWeight) { pts[1], 1.f } ));
|
||||
break;
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
g_array_append_val (self->ops, ((PathOp) { op, self->points->len - 1 }));
|
||||
g_array_append_val (self->points, ((PointWeight) { pts[1], 1.f } ));
|
||||
g_array_append_val (self->points, ((PointWeight) { pts[2], 1.f } ));
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
g_array_append_val (self->ops, ((PathOp) { op, self->points->len - 1 }));
|
||||
g_array_append_val (self->points, ((PointWeight) { pts[1], 1.f } ));
|
||||
g_array_append_val (self->points, ((PointWeight) { pts[2], 1.f } ));
|
||||
g_array_append_val (self->points, ((PointWeight) { pts[3], 1.f } ));
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
g_array_append_val (self->ops, ((PathOp) { op, self->points->len - 1 }));
|
||||
g_array_append_val (self->points, ((PointWeight) { pts[1], weight } ));
|
||||
g_array_append_val (self->points, ((PointWeight) { pts[2], 1.f } ));
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_clear (GtkPath *self)
|
||||
{
|
||||
g_array_set_size (self->ops, 0);
|
||||
g_array_set_size (self->points, 0);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_path_set_gsk_path (GtkPath *self,
|
||||
GskPath *path)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_PATH (self));
|
||||
g_return_if_fail (path != NULL);
|
||||
|
||||
gtk_path_clear (self);
|
||||
gsk_path_foreach (path, -1, add_curve, self);
|
||||
}
|
||||
|
||||
GskPath *
|
||||
gtk_path_get_gsk_path (GtkPath *self)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_PATH (self), NULL);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
for (gsize i = 0; i < self->ops->len; i++)
|
||||
{
|
||||
PathOp op = g_array_index (self->ops, PathOp, i);
|
||||
PointWeight *p0, *p1, *p2;
|
||||
switch (op.op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
p0 = &g_array_index (self->points, PointWeight, op.idx);
|
||||
gsk_path_builder_move_to (builder, p0->point.x, p0->point.y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
gsk_path_builder_close (builder);
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
p0 = &g_array_index (self->points, PointWeight, op.idx);
|
||||
gsk_path_builder_line_to (builder, p0->point.x, p0->point.y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
p0 = &g_array_index (self->points, PointWeight, op.idx);
|
||||
p1 = &g_array_index (self->points, PointWeight, op.idx + 1);
|
||||
gsk_path_builder_quad_to (builder,
|
||||
p0->point.x, p0->point.y,
|
||||
p1->point.x, p1->point.y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
p0 = &g_array_index (self->points, PointWeight, op.idx);
|
||||
p1 = &g_array_index (self->points, PointWeight, op.idx + 1);
|
||||
p2 = &g_array_index (self->points, PointWeight, op.idx + 2);
|
||||
gsk_path_builder_cubic_to (builder,
|
||||
p0->point.x, p0->point.y,
|
||||
p1->point.x, p1->point.y,
|
||||
p2->point.x, p2->point.y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
p0 = &g_array_index (self->points, PointWeight, op.idx);
|
||||
p1 = &g_array_index (self->points, PointWeight, op.idx + 1);
|
||||
gsk_path_builder_conic_to (builder,
|
||||
p0->point.x, p0->point.y,
|
||||
p1->point.x, p1->point.y,
|
||||
p0->weight);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
gsize
|
||||
gtk_path_get_n_operations (GtkPath *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_PATH (self), 0);
|
||||
|
||||
return self->ops->len;
|
||||
}
|
||||
|
||||
GskPathOperation
|
||||
gtk_path_get_operation (GtkPath *self,
|
||||
gsize idx)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_PATH (self), GSK_PATH_MOVE);
|
||||
g_return_val_if_fail (idx < self->ops->len, GSK_PATH_MOVE);
|
||||
|
||||
return g_array_index (self->ops, GskPathOperation, idx);
|
||||
}
|
||||
|
||||
#define OP_CHANGE(op1,op2) ((op1) | ((op2) << 16))
|
||||
|
||||
static void
|
||||
shift_indices (GtkPath *self,
|
||||
gsize from,
|
||||
int shift)
|
||||
{
|
||||
for (gsize i = from; i < self->ops->len; i++)
|
||||
{
|
||||
PathOp *op = &g_array_index (self->ops, PathOp, i);
|
||||
op->idx += shift;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
line_intersection (const graphene_point_t *a,
|
||||
const graphene_point_t *b,
|
||||
const graphene_point_t *c,
|
||||
const graphene_point_t *d,
|
||||
graphene_point_t *p)
|
||||
{
|
||||
float a1 = b->y - a->y;
|
||||
float b1 = a->x - b->x;
|
||||
float c1 = a1*a->x + b1*a->y;
|
||||
|
||||
float a2 = d->y - c->y;
|
||||
float b2 = c->x - d->x;
|
||||
float c2 = a2*c->x+ b2*c->y;
|
||||
|
||||
float det = a1*b2 - a2*b1;
|
||||
|
||||
if (fabs (det) < 0.001)
|
||||
{
|
||||
p->x = NAN;
|
||||
p->y = NAN;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
p->x = (b2*c1 - b1*c2) / det;
|
||||
p->y = (a1*c2 - a2*c1) / det;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_curve (GtkPath *self, gsize idx, GskCurve *curve)
|
||||
{
|
||||
PathOp op = g_array_index (self->ops, PathOp, idx);
|
||||
PointWeight *pw = &g_array_index (self->points, PointWeight, op.idx);
|
||||
|
||||
switch (op.op)
|
||||
{
|
||||
case GSK_PATH_LINE:
|
||||
gsk_curve_init_foreach (curve,
|
||||
op.op,
|
||||
(graphene_point_t[]) { pw[0].point, pw[1].point },
|
||||
2,
|
||||
1.f);
|
||||
break;
|
||||
case GSK_PATH_QUAD:
|
||||
gsk_curve_init_foreach (curve,
|
||||
op.op,
|
||||
(graphene_point_t[]) { pw[0].point, pw[1].point, pw[2].point },
|
||||
3,
|
||||
1.f);
|
||||
break;
|
||||
case GSK_PATH_CUBIC:
|
||||
gsk_curve_init_foreach (curve,
|
||||
op.op,
|
||||
(graphene_point_t[]) { pw[0].point, pw[1].point, pw[2].point, pw[3].point },
|
||||
4,
|
||||
1.f);
|
||||
break;
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_curve_init_foreach (curve,
|
||||
op.op,
|
||||
(graphene_point_t[]) { pw[0].point, pw[1].point, pw[2].point },
|
||||
3,
|
||||
pw[1].weight);
|
||||
break;
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CLOSE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_path_set_operation (GtkPath *self,
|
||||
gsize idx,
|
||||
GskPathOperation op)
|
||||
{
|
||||
PathOp *old;
|
||||
const graphene_point_t *p0, *p1, *p2, *p3;
|
||||
|
||||
g_return_if_fail (GTK_IS_PATH (self));
|
||||
g_return_if_fail (idx < self->ops->len);
|
||||
|
||||
old = &g_array_index (self->ops, PathOp, idx);
|
||||
|
||||
if (old->op == op)
|
||||
return;
|
||||
|
||||
/* Can't change the move, for now */
|
||||
if (old->op == GSK_PATH_MOVE)
|
||||
return;
|
||||
|
||||
switch (old->op)
|
||||
{
|
||||
case GSK_PATH_CLOSE:
|
||||
p0 = p1 = &(g_array_index (self->points, PointWeight, old->idx).point);
|
||||
for (gsize idx2 = idx; idx2 > 0; idx2--)
|
||||
{
|
||||
PathOp op2 = g_array_index (self->ops, PathOp, idx2 - 1);
|
||||
if (op2.op == GSK_PATH_MOVE)
|
||||
{
|
||||
p1 = &(g_array_index (self->points, PointWeight, op2.idx).point);
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_LINE:
|
||||
{
|
||||
graphene_point_t q;
|
||||
|
||||
/* Not putting the point quite on top */
|
||||
graphene_point_interpolate (p0, p1, 0.9, &q);
|
||||
g_array_insert_val (self->points, old->idx + 1, ((PointWeight) { q, 1.f }));
|
||||
shift_indices (self, idx + 1, 1);
|
||||
old->op = op;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
graphene_point_t q1, q2;
|
||||
|
||||
/* Not putting the point quite on top */
|
||||
graphene_point_interpolate (p0, p1, 0.9, &q2);
|
||||
graphene_point_interpolate (p0, &q1, 0.5, &q1);
|
||||
g_array_insert_vals (self->points, old->idx + 1,
|
||||
(PointWeight[]) { { q1, 1.f }, { q2, 1.f } },
|
||||
2);
|
||||
shift_indices (self, idx + 1, 2);
|
||||
old->op = op;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
{
|
||||
graphene_point_t q1, q2, q3;
|
||||
|
||||
/* Not putting the point quite on top */
|
||||
graphene_point_interpolate (p0, p1, 0.9, &q3);
|
||||
graphene_point_interpolate (p0, &q3, 0.333, &q1);
|
||||
graphene_point_interpolate (p0, &q3, 0.667, &q2);
|
||||
g_array_insert_vals (self->points, old->idx + 1,
|
||||
(PointWeight[]) { { q1, 1.f }, { q2, 1.f }, { q3, 1.f } },
|
||||
3);
|
||||
shift_indices (self, idx + 1, 3);
|
||||
old->op = op;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CLOSE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
p0 = &(g_array_index (self->points, PointWeight, old->idx).point);
|
||||
p1 = &(g_array_index (self->points, PointWeight, old->idx + 1).point);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_CLOSE:
|
||||
break;
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
graphene_point_t q;
|
||||
|
||||
graphene_point_interpolate (p0, p1, 0.5, &q);
|
||||
g_array_insert_val (self->points, old->idx + 1, ((PointWeight) { q, 1.f }));
|
||||
shift_indices (self, idx + 1, 1);
|
||||
old->op = op;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
{
|
||||
graphene_point_t q1, q2;
|
||||
|
||||
graphene_point_interpolate (p0, p1, 0.333, &q1);
|
||||
graphene_point_interpolate (p0, p1, 0.667, &q2);
|
||||
|
||||
g_array_insert_vals (self->points, old->idx + 1,
|
||||
(PointWeight[]) { { q1, 1.f }, { q2, 1.f } },
|
||||
2);
|
||||
shift_indices (self, idx + 1, 2);
|
||||
old->op = op;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
case GSK_PATH_MOVE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
case GSK_PATH_CONIC:
|
||||
p0 = &(g_array_index (self->points, PointWeight, old->idx)).point;
|
||||
p1 = &(g_array_index (self->points, PointWeight, old->idx + 1)).point;
|
||||
p2 = &(g_array_index (self->points, PointWeight, old->idx + 2)).point;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_QUAD:
|
||||
case GSK_PATH_CONIC:
|
||||
old->op = op;
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
{
|
||||
graphene_point_t q1, q2;
|
||||
|
||||
graphene_point_interpolate (p0, p1, 0.667, &q1);
|
||||
graphene_point_interpolate (p1, p2, 0.333, &q2);
|
||||
|
||||
g_array_insert_vals (self->points, old->idx + 1,
|
||||
(PointWeight[]) { { q1, 1.f }, { q2, 1.f } },
|
||||
2);
|
||||
shift_indices (self, idx + 1, 2);
|
||||
old->op = op;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
g_array_remove_index (self->points, old->idx + 1);
|
||||
shift_indices (self, idx + 1, -1);
|
||||
old->op = op;
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CLOSE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
p0 = &(g_array_index (self->points, PointWeight, old->idx)).point;
|
||||
p1 = &(g_array_index (self->points, PointWeight, old->idx + 1)).point;
|
||||
p2 = &(g_array_index (self->points, PointWeight, old->idx + 2)).point;
|
||||
p3 = &(g_array_index (self->points, PointWeight, old->idx + 3)).point;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_CLOSE:
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
g_array_remove_range (self->points, old->idx + 1, 2);
|
||||
shift_indices (self, idx + 1, -2);
|
||||
old->op = op;
|
||||
break;
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
graphene_point_t q;
|
||||
PointWeight *pw;
|
||||
|
||||
if (!line_intersection (p0, p1, p2, p3, &q))
|
||||
graphene_point_interpolate (p0, p3, 0.5, &q);
|
||||
g_array_remove_index (self->points, old->idx + 2);
|
||||
pw = &g_array_index (self->points, PointWeight, old->idx + 1);
|
||||
pw->point = q;
|
||||
shift_indices (self, idx + 1, -1);
|
||||
old->op = op;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CUBIC:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_path_split_operation (GtkPath *self,
|
||||
gsize idx,
|
||||
float t)
|
||||
{
|
||||
PathOp op;
|
||||
GskCurve curve, c1, c2;
|
||||
|
||||
g_return_if_fail (GTK_IS_PATH (self));
|
||||
g_return_if_fail (idx < self->ops->len);
|
||||
g_return_if_fail (0 <= t && t <= 1);
|
||||
|
||||
op = g_array_index (self->ops, PathOp, idx);
|
||||
|
||||
if (op.op == GSK_PATH_MOVE || op.op == GSK_PATH_CLOSE)
|
||||
return; /* Can't do these for now */
|
||||
|
||||
get_curve (self, idx, &curve);
|
||||
gsk_curve_split (&curve, t, &c1, &c2);
|
||||
|
||||
switch (op.op)
|
||||
{
|
||||
case GSK_PATH_LINE:
|
||||
g_array_insert_val (self->points, op.idx + 1,
|
||||
((PointWeight) { c1.line.points[1], 1.f }));
|
||||
shift_indices (self, idx + 1, 1);
|
||||
g_array_insert_val (self->ops, idx + 1, ((PathOp) { op.op, op.idx + 1 }));
|
||||
break;
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
g_array_remove_index (self->points, op.idx + 1);
|
||||
g_array_insert_vals (self->points, op.idx + 1,
|
||||
(PointWeight []) { { c1.quad.points[1], 1.f },
|
||||
{ c1.quad.points[2], 1.f },
|
||||
{ c2.quad.points[1], 1.f } },
|
||||
3);
|
||||
shift_indices (self, idx + 1, 2);
|
||||
g_array_insert_val (self->ops, idx + 1, ((PathOp) { op.op, op.idx + 2 }));
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
g_array_remove_range (self->points, op.idx + 1, 2);
|
||||
g_array_insert_vals (self->points, op.idx + 1,
|
||||
(PointWeight []) { { c1.cubic.points[1], 1.f },
|
||||
{ c1.cubic.points[2], 1.f },
|
||||
{ c1.cubic.points[3], 1.f },
|
||||
{ c2.cubic.points[1], 1.f },
|
||||
{ c2.cubic.points[2], 1.f } },
|
||||
5);
|
||||
shift_indices (self, idx + 1, 3);
|
||||
g_array_insert_val (self->ops, idx + 1, ((PathOp) { op.op, op.idx + 3 }));
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
g_array_remove_index (self->points, op.idx + 1);
|
||||
g_array_insert_vals (self->points, op.idx + 1,
|
||||
(PointWeight []) { { c1.conic.points[1], c1.conic.points[2].x },
|
||||
{ c1.conic.points[3], 1.f },
|
||||
{ c2.conic.points[1], c2.conic.points[2].x } },
|
||||
3);
|
||||
shift_indices (self, idx + 1, 2);
|
||||
g_array_insert_val (self->ops, idx + 1, ((PathOp) { op.op, op.idx + 2 }));
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CLOSE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_path_remove_operation (GtkPath *self,
|
||||
gsize idx)
|
||||
{
|
||||
PathOp op;
|
||||
|
||||
g_return_if_fail (GTK_IS_PATH (self));
|
||||
g_return_if_fail (idx < self->ops->len);
|
||||
|
||||
op = g_array_index (self->ops, PathOp, idx);
|
||||
|
||||
switch (op.op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
/* Removing a MOVE is a no-op */
|
||||
return;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
/* No point point shuffling needed */
|
||||
g_array_remove_index (self->ops, idx);
|
||||
return;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
g_array_remove_index (self->ops, idx);
|
||||
g_array_remove_range (self->points, op.idx, 1);
|
||||
shift_indices (self, idx, -1);
|
||||
break;
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
case GSK_PATH_CONIC:
|
||||
g_array_remove_index (self->ops, idx);
|
||||
g_array_remove_range (self->points, op.idx, 2);
|
||||
shift_indices (self, idx, -2);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
g_array_remove_index (self->ops, idx);
|
||||
g_array_remove_range (self->points, op.idx, 3);
|
||||
shift_indices (self, idx, -3);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
gsize
|
||||
gtk_path_get_points_for_operation (GtkPath *self,
|
||||
gsize idx)
|
||||
{
|
||||
PathOp op;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_PATH (self), 0);
|
||||
g_return_val_if_fail (idx < self->ops->len, 0);
|
||||
|
||||
op = g_array_index (self->ops, PathOp, idx);
|
||||
|
||||
return op.idx;
|
||||
}
|
||||
|
||||
gsize
|
||||
gtk_path_get_n_points (GtkPath *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_PATH (self), 0);
|
||||
|
||||
return self->points->len;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_path_get_point (GtkPath *self,
|
||||
gsize idx,
|
||||
graphene_point_t *point)
|
||||
{
|
||||
PointWeight *p;
|
||||
|
||||
g_return_if_fail (GTK_IS_PATH (self));
|
||||
g_return_if_fail (idx < self->points->len);
|
||||
g_return_if_fail (point != NULL);
|
||||
|
||||
p = &g_array_index (self->points, PointWeight, idx);
|
||||
*point = p->point;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_path_set_point (GtkPath *self,
|
||||
gsize idx,
|
||||
const graphene_point_t *point)
|
||||
{
|
||||
PointWeight *p;
|
||||
|
||||
g_return_if_fail (GTK_IS_PATH (self));
|
||||
g_return_if_fail (idx < self->points->len);
|
||||
g_return_if_fail (point != NULL);
|
||||
|
||||
p = &g_array_index (self->points, PointWeight, idx);
|
||||
p->point = *point;
|
||||
}
|
||||
|
||||
float
|
||||
gtk_path_get_conic_weight (GtkPath *self,
|
||||
gsize idx)
|
||||
{
|
||||
PointWeight *p;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_PATH (self), 1.f);
|
||||
g_return_val_if_fail (idx < self->points->len, 1.f);
|
||||
|
||||
p = &g_array_index (self->points, PointWeight, idx);
|
||||
return p->weight;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_path_set_conic_weight (GtkPath *self,
|
||||
gsize idx,
|
||||
float weight)
|
||||
{
|
||||
PointWeight *p;
|
||||
|
||||
g_return_if_fail (GTK_IS_PATH (self));
|
||||
g_return_if_fail (idx < self->points->len);
|
||||
g_return_if_fail (weight > 0);
|
||||
|
||||
p = &g_array_index (self->points, PointWeight, idx);
|
||||
p->weight = weight;
|
||||
}
|
73
gtk/gtkpath.h
Normal file
73
gtk/gtkpath.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_PATH (gtk_path_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkPath, gtk_path, GTK, PATH, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkPath * gtk_path_new (void);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_path_set_gsk_path (GtkPath *self,
|
||||
GskPath *gsk_path);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gtk_path_get_gsk_path (GtkPath *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gsize gtk_path_get_n_operations (GtkPath *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathOperation gtk_path_get_operation (GtkPath *self,
|
||||
gsize idx);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_path_set_operation (GtkPath *self,
|
||||
gsize idx,
|
||||
GskPathOperation op);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_path_split_operation (GtkPath *self,
|
||||
gsize idx,
|
||||
float t);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_path_remove_operation (GtkPath *self,
|
||||
gsize idx);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gsize gtk_path_get_points_for_operation (GtkPath *self,
|
||||
gsize idx);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gsize gtk_path_get_n_points (GtkPath *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_path_get_point (GtkPath *self,
|
||||
gsize idx,
|
||||
graphene_point_t *point);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_path_set_point (GtkPath *self,
|
||||
gsize idx,
|
||||
const graphene_point_t *point);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gsize gtk_path_insert_point (GtkPath *self,
|
||||
gsize idx,
|
||||
const graphene_point_t *point);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gtk_path_get_conic_weight (GtkPath *self,
|
||||
gsize idx);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_path_set_conic_weight (GtkPath *self,
|
||||
gsize idx,
|
||||
float weight);
|
||||
|
||||
G_END_DECLS
|
@@ -15,6 +15,7 @@ gtk_cargs = [
|
||||
# List of sources that do not contain public API, and should not be
|
||||
# introspected
|
||||
gtk_private_sources = files([
|
||||
'gtkpath.c',
|
||||
'fnmatch.c',
|
||||
'gdktextureutils.c',
|
||||
'gsettings-mapping.c',
|
||||
@@ -151,6 +152,7 @@ gtk_private_sources = files([
|
||||
|
||||
# List of files that contain public API, and should be introspected
|
||||
gtk_public_sources = files([
|
||||
'gtkcontour.c',
|
||||
'gtkaboutdialog.c',
|
||||
'gtkaccelgroup.c',
|
||||
'gtkaccessible.c',
|
||||
|
Reference in New Issue
Block a user