mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 13:41:07 +00:00
Merge branch 'circle-contour' into 'main'
path: Add a circle contour See merge request GNOME/gtk!6345
This commit is contained in:
commit
2297a353b8
601
gsk/gskcontour.c
601
gsk/gskcontour.c
@ -103,23 +103,141 @@ struct _GskContourClass
|
||||
|
||||
static void
|
||||
_g_string_append_double (GString *string,
|
||||
const char *prefix,
|
||||
double d)
|
||||
{
|
||||
char buf[G_ASCII_DTOSTR_BUF_SIZE];
|
||||
|
||||
g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, d);
|
||||
g_string_append (string, prefix);
|
||||
g_string_append (string, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
_g_string_append_point (GString *string,
|
||||
const char *prefix,
|
||||
const graphene_point_t *pt)
|
||||
{
|
||||
_g_string_append_double (string, pt->x);
|
||||
g_string_append_c (string, ' ');
|
||||
_g_string_append_double (string, pt->y);
|
||||
_g_string_append_double (string, prefix, pt->x);
|
||||
_g_string_append_double (string, " ", pt->y);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
add_segment (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskPathBuilder *builder = user_data;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
|
||||
break;
|
||||
case GSK_PATH_LINE:
|
||||
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
|
||||
break;
|
||||
case GSK_PATH_QUAD:
|
||||
gsk_path_builder_quad_to (builder,
|
||||
pts[1].x, pts[1].y,
|
||||
pts[2].x, pts[2].y);
|
||||
break;
|
||||
case GSK_PATH_CUBIC:
|
||||
gsk_path_builder_cubic_to (builder,
|
||||
pts[1].x, pts[1].y,
|
||||
pts[2].x, pts[2].y,
|
||||
pts[3].x, pts[3].y);
|
||||
break;
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_path_builder_conic_to (builder,
|
||||
pts[1].x, pts[1].y,
|
||||
pts[2].x, pts[2].y,
|
||||
weight);
|
||||
break;
|
||||
case GSK_PATH_CLOSE:
|
||||
gsk_path_builder_close (builder);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
convert_to_standard_contour (const GskContour *contour)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_contour_foreach (contour, 0.5, add_segment, builder);
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Default implementations */
|
||||
|
||||
static gsize
|
||||
gsk_contour_get_size_default (const GskContour *contour)
|
||||
{
|
||||
return contour->klass->struct_size;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
foreach_print (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
GString *string = data;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
_g_string_append_point (string, "M ", &pts[0]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
g_string_append (string, " Z");
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
_g_string_append_point (string, " L ", &pts[1]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
_g_string_append_point (string, " Q ", &pts[1]);
|
||||
_g_string_append_point (string, ", ", &pts[2]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
_g_string_append_point (string, " C ", &pts[1]);
|
||||
_g_string_append_point (string, ", ", &pts[2]);
|
||||
_g_string_append_point (string, ", ", &pts[3]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
_g_string_append_point (string, " O ", &pts[1]);
|
||||
_g_string_append_point (string, ", ", &pts[2]);
|
||||
_g_string_append_double (string, ", ", weight);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_contour_print_default (const GskContour *contour,
|
||||
GString *string)
|
||||
{
|
||||
gsk_contour_foreach (contour, 0.5, foreach_print, string);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Standard */
|
||||
@ -257,65 +375,6 @@ gsk_standard_contour_get_flags (const GskContour *contour)
|
||||
return self->flags;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_print (const GskContour *contour,
|
||||
GString *string)
|
||||
{
|
||||
const GskStandardContour *self = (const GskStandardContour *) contour;
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < self->n_ops; i ++)
|
||||
{
|
||||
const graphene_point_t *pt = gsk_pathop_points (self->ops[i]);
|
||||
|
||||
switch (gsk_pathop_op (self->ops[i]))
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
g_string_append (string, "M ");
|
||||
_g_string_append_point (string, &pt[0]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
g_string_append (string, " Z");
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
g_string_append (string, " L ");
|
||||
_g_string_append_point (string, &pt[1]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
g_string_append (string, " Q ");
|
||||
_g_string_append_point (string, &pt[1]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_point (string, &pt[2]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
g_string_append (string, " C ");
|
||||
_g_string_append_point (string, &pt[1]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_point (string, &pt[2]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_point (string, &pt[3]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
g_string_append (string, " O ");
|
||||
_g_string_append_point (string, &pt[1]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_point (string, &pt[3]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_double (string, pt[2].x);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_standard_contour_get_bounds (const GskContour *contour,
|
||||
GskBoundingBox *bounds)
|
||||
@ -956,7 +1015,7 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
|
||||
gsk_standard_contour_copy,
|
||||
gsk_standard_contour_get_size,
|
||||
gsk_standard_contour_get_flags,
|
||||
gsk_standard_contour_print,
|
||||
gsk_contour_print_default,
|
||||
gsk_standard_contour_get_bounds,
|
||||
gsk_standard_contour_get_stroke_bounds,
|
||||
gsk_standard_contour_get_start_end,
|
||||
@ -1025,6 +1084,422 @@ gsk_standard_contour_new (GskPathFlags flags,
|
||||
return contour;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Circle */
|
||||
|
||||
typedef struct _GskCircleContour GskCircleContour;
|
||||
struct _GskCircleContour
|
||||
{
|
||||
GskContour contour;
|
||||
|
||||
graphene_point_t center;
|
||||
float radius;
|
||||
gboolean ccw;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_circle_contour_copy (const GskContour *contour,
|
||||
GskContour *dest)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
GskCircleContour *target = (GskCircleContour *) dest;
|
||||
|
||||
*target = *self;
|
||||
}
|
||||
|
||||
static GskPathFlags
|
||||
gsk_circle_contour_get_flags (const GskContour *contour)
|
||||
{
|
||||
return GSK_PATH_CLOSED;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_circle_contour_get_bounds (const GskContour *contour,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
|
||||
gsk_bounding_box_init (bounds,
|
||||
&GRAPHENE_POINT_INIT (self->center.x - self->radius,
|
||||
self->center.y - self->radius),
|
||||
&GRAPHENE_POINT_INIT (self->center.x + self->radius,
|
||||
self->center.y + self->radius));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_circle_contour_get_stroke_bounds (const GskContour *contour,
|
||||
const GskStroke *stroke,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
|
||||
gsk_bounding_box_init (bounds,
|
||||
&GRAPHENE_POINT_INIT (self->center.x - self->radius - stroke->line_width/2,
|
||||
self->center.y - self->radius - stroke->line_width/2),
|
||||
&GRAPHENE_POINT_INIT (self->center.x + self->radius + stroke->line_width/2,
|
||||
self->center.y + self->radius + stroke->line_width/2));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_circle_contour_get_start_end (const GskContour *contour,
|
||||
graphene_point_t *start,
|
||||
graphene_point_t *end)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
|
||||
if (start)
|
||||
*start = GRAPHENE_POINT_INIT (self->center.x + self->radius, self->center.y);
|
||||
|
||||
if (end)
|
||||
*end = GRAPHENE_POINT_INIT (self->center.x + self->radius, self->center.y);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_circle_contour_foreach (const GskContour *contour,
|
||||
float tolerance,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
float rx, ry;
|
||||
|
||||
rx = ry = self->radius;
|
||||
if (self->ccw)
|
||||
ry = - self->radius;
|
||||
|
||||
if (!func (GSK_PATH_MOVE,
|
||||
(const graphene_point_t[1]) {
|
||||
GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y),
|
||||
},
|
||||
1, 0.f, user_data))
|
||||
return FALSE;
|
||||
|
||||
if (!func (GSK_PATH_CONIC,
|
||||
(const graphene_point_t[3]) {
|
||||
GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y),
|
||||
GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y + ry),
|
||||
GRAPHENE_POINT_INIT (self->center.x, self->center.y + ry),
|
||||
},
|
||||
3, M_SQRT1_2, user_data))
|
||||
return FALSE;
|
||||
|
||||
if (!func (GSK_PATH_CONIC,
|
||||
(const graphene_point_t[3]) {
|
||||
GRAPHENE_POINT_INIT (self->center.x, self->center.y + ry),
|
||||
GRAPHENE_POINT_INIT (self->center.x - rx, self->center.y + ry),
|
||||
GRAPHENE_POINT_INIT (self->center.x - rx, self->center.y),
|
||||
},
|
||||
3, M_SQRT1_2, user_data))
|
||||
return FALSE;
|
||||
|
||||
if (!func (GSK_PATH_CONIC,
|
||||
(const graphene_point_t[3]) {
|
||||
GRAPHENE_POINT_INIT (self->center.x - rx, self->center.y),
|
||||
GRAPHENE_POINT_INIT (self->center.x - rx, self->center.y - ry),
|
||||
GRAPHENE_POINT_INIT (self->center.x, self->center.y - ry),
|
||||
},
|
||||
3, M_SQRT1_2, user_data))
|
||||
return FALSE;
|
||||
|
||||
if (!func (GSK_PATH_CONIC,
|
||||
(const graphene_point_t[3]) {
|
||||
GRAPHENE_POINT_INIT (self->center.x, self->center.y - ry),
|
||||
GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y - ry),
|
||||
GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y),
|
||||
},
|
||||
3, M_SQRT1_2, user_data))
|
||||
return FALSE;
|
||||
|
||||
if (!func (GSK_PATH_CLOSE,
|
||||
(const graphene_point_t[2]) {
|
||||
GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y),
|
||||
GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y),
|
||||
},
|
||||
2, 0.f, user_data))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GskContour *
|
||||
gsk_circle_contour_reverse (const GskContour *contour)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
GskCircleContour *copy;
|
||||
|
||||
copy = g_new0 (GskCircleContour, 1);
|
||||
gsk_circle_contour_copy (contour, (GskContour *)copy);
|
||||
copy->ccw = !self->ccw;
|
||||
|
||||
return (GskContour *)copy;
|
||||
}
|
||||
|
||||
static int
|
||||
gsk_circle_contour_get_winding (const GskContour *contour,
|
||||
const graphene_point_t *point)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
|
||||
if (graphene_point_distance (point, &self->center, NULL, NULL) >= self->radius)
|
||||
return 0;
|
||||
|
||||
if (self->ccw)
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static gsize
|
||||
gsk_circle_contour_get_n_ops (const GskContour *contour)
|
||||
{
|
||||
/* Not related to how many curves foreach produces.
|
||||
* GskPath assumes that the start- and endpoints
|
||||
* of a contour are { x, 1, 0 } and { x, n_ops - 1, 1 }.
|
||||
*
|
||||
* The circle contour uses a single 'segment' in path
|
||||
* points, with a t that ranges from 0 to 1 to cover
|
||||
* the angles from 0 to 360 (or 360 to 0 in the ccw
|
||||
* case).
|
||||
*/
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_circle_contour_get_closest_point (const GskContour *contour,
|
||||
const graphene_point_t *point,
|
||||
float threshold,
|
||||
GskRealPathPoint *result,
|
||||
float *out_dist)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
float dist, angle, t;
|
||||
|
||||
dist = fabsf (graphene_point_distance (&self->center, point, NULL, NULL) - self->radius);
|
||||
|
||||
if (dist > threshold)
|
||||
return FALSE;
|
||||
|
||||
angle = RAD_TO_DEG (atan2f (point->y - self->center.y, point->x - self->center.x));
|
||||
|
||||
if (angle < 0)
|
||||
angle = 360 - angle;
|
||||
|
||||
t = CLAMP (angle / 360, 0, 1);
|
||||
|
||||
if (self->ccw)
|
||||
t = 1 - t;
|
||||
|
||||
result->idx = 1;
|
||||
result->t = t;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define GSK_CIRCLE_POINT_INIT(self, angle) \
|
||||
GRAPHENE_POINT_INIT ((self)->center.x + cosf (DEG_TO_RAD (angle)) * self->radius, \
|
||||
(self)->center.y + sinf (DEG_TO_RAD (angle)) * self->radius)
|
||||
|
||||
static void
|
||||
gsk_circle_contour_get_position (const GskContour *contour,
|
||||
GskRealPathPoint *point,
|
||||
graphene_point_t *position)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
float t;
|
||||
|
||||
t = point->t;
|
||||
|
||||
if (self->ccw)
|
||||
t = 1 - t;
|
||||
|
||||
if (t == 0 || t == 1)
|
||||
*position = GRAPHENE_POINT_INIT (self->center.x + self->radius, self->center.y);
|
||||
else
|
||||
*position = GSK_CIRCLE_POINT_INIT (self, t * 360);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_circle_contour_get_tangent (const GskContour *contour,
|
||||
GskRealPathPoint *point,
|
||||
GskPathDirection direction,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
graphene_point_t p;
|
||||
|
||||
gsk_circle_contour_get_position (contour, point, &p);
|
||||
|
||||
graphene_vec2_init (tangent, p.y - self->center.y, - p.x + self->center.x);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
|
||||
static float
|
||||
gsk_circle_contour_get_curvature (const GskContour *contour,
|
||||
GskRealPathPoint *point,
|
||||
GskPathDirection direction,
|
||||
graphene_point_t *center)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
|
||||
if (center)
|
||||
*center = self->center;
|
||||
|
||||
return 1 / self->radius;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_circle_contour_add_segment (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
gboolean emit_move_to,
|
||||
GskRealPathPoint *start,
|
||||
GskRealPathPoint *end)
|
||||
{
|
||||
GskPath *path;
|
||||
graphene_point_t p;
|
||||
GskRealPathPoint start2, end2;
|
||||
const GskContour *std;
|
||||
float dist;
|
||||
|
||||
/* This is a cheesy way of doing things: convert to a standard contour,
|
||||
* and translate the path points from circle to standard. We just have
|
||||
* to be careful to tell start- and endpoint apart.
|
||||
*/
|
||||
|
||||
path = convert_to_standard_contour (contour);
|
||||
std = gsk_path_get_contour (path, 0);
|
||||
|
||||
start2.contour = 0;
|
||||
|
||||
if (start->idx == 1 && start->t == 0)
|
||||
{
|
||||
start2.idx = 1;
|
||||
start2.t = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gsk_circle_contour_get_position (contour, start, &p);
|
||||
gsk_standard_contour_get_closest_point (std, &p, INFINITY, &start2, &dist);
|
||||
}
|
||||
|
||||
end2.contour = 0;
|
||||
|
||||
if (end->idx == 1 && end->t == 1)
|
||||
{
|
||||
end2.idx = 4;
|
||||
end2.t = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
gsk_circle_contour_get_position (contour, end, &p);
|
||||
gsk_standard_contour_get_closest_point (std, &p, INFINITY, &end2, &dist);
|
||||
}
|
||||
|
||||
gsk_standard_contour_add_segment (std, builder, emit_move_to, &start2, &end2);
|
||||
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gsk_circle_contour_init_measure (const GskContour *contour,
|
||||
float tolerance,
|
||||
float *out_length)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
|
||||
*out_length = 2 * M_PI * self->radius;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_circle_contour_free_measure (const GskContour *contour,
|
||||
gpointer data)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_circle_contour_get_point (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
GskRealPathPoint *result)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
float t;
|
||||
|
||||
t = distance / (2 * M_PI * self->radius);
|
||||
|
||||
if (self->ccw)
|
||||
t = 1 - t;
|
||||
|
||||
result->idx = 1;
|
||||
result->t = t;
|
||||
}
|
||||
|
||||
static float
|
||||
gsk_circle_contour_get_distance (const GskContour *contour,
|
||||
GskRealPathPoint *point,
|
||||
gpointer measure_data)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
float t;
|
||||
|
||||
t = point->t;
|
||||
|
||||
if (self->ccw)
|
||||
t = 1 - t;
|
||||
|
||||
return 2 * M_PI * self->radius * t;
|
||||
}
|
||||
|
||||
static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =
|
||||
{
|
||||
sizeof (GskCircleContour),
|
||||
"GskCircleContour",
|
||||
gsk_circle_contour_copy,
|
||||
gsk_contour_get_size_default,
|
||||
gsk_circle_contour_get_flags,
|
||||
gsk_contour_print_default,
|
||||
gsk_circle_contour_get_bounds,
|
||||
gsk_circle_contour_get_stroke_bounds,
|
||||
gsk_circle_contour_get_start_end,
|
||||
gsk_circle_contour_foreach,
|
||||
gsk_circle_contour_reverse,
|
||||
gsk_circle_contour_get_winding,
|
||||
gsk_circle_contour_get_n_ops,
|
||||
gsk_circle_contour_get_closest_point,
|
||||
gsk_circle_contour_get_position,
|
||||
gsk_circle_contour_get_tangent,
|
||||
gsk_circle_contour_get_curvature,
|
||||
gsk_circle_contour_add_segment,
|
||||
gsk_circle_contour_init_measure,
|
||||
gsk_circle_contour_free_measure,
|
||||
gsk_circle_contour_get_point,
|
||||
gsk_circle_contour_get_distance,
|
||||
};
|
||||
|
||||
GskContour *
|
||||
gsk_circle_contour_new (const graphene_point_t *center,
|
||||
float radius)
|
||||
{
|
||||
GskCircleContour *self;
|
||||
|
||||
self = g_new0 (GskCircleContour, 1);
|
||||
|
||||
self->contour.klass = &GSK_CIRCLE_CONTOUR_CLASS;
|
||||
|
||||
self->contour.klass = &GSK_CIRCLE_CONTOUR_CLASS;
|
||||
self->center = *center;
|
||||
self->radius = radius;
|
||||
self->ccw = FALSE;
|
||||
|
||||
return (GskContour *) self;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ API */
|
||||
|
||||
|
@ -34,6 +34,9 @@ GskContour * gsk_standard_contour_new (GskPathFlags
|
||||
gsize n_ops,
|
||||
gssize offset);
|
||||
|
||||
GskContour * gsk_circle_contour_new (const graphene_point_t *center,
|
||||
float radius);
|
||||
|
||||
void gsk_contour_copy (GskContour * dest,
|
||||
const GskContour *src);
|
||||
GskContour * gsk_contour_dup (const GskContour *src);
|
||||
|
@ -953,6 +953,78 @@ parse_command (const char **p,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_string (const char **p,
|
||||
const char *s)
|
||||
{
|
||||
int len = strlen (s);
|
||||
if (strncmp (*p, s, len) != 0)
|
||||
return FALSE;
|
||||
(*p) += len;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_circle (const char **p,
|
||||
double *cx,
|
||||
double *cy,
|
||||
double *r)
|
||||
{
|
||||
const char *o = *p;
|
||||
double x0, y0, x1, y1, x2, y2, x3, y3;
|
||||
double x4, y4, x5, y5, x6, y6, x7, y7;
|
||||
double x8, y8, w0, w1, w2, w3;
|
||||
double xx, yy;
|
||||
|
||||
if (parse_coordinate_pair (p, &x0, &y0) &&
|
||||
parse_string (p, "O") &&
|
||||
parse_coordinate_pair (p, &x1, &y1) &&
|
||||
parse_coordinate_pair (p, &x2, &y2) &&
|
||||
parse_nonnegative_number (p, &w0) &&
|
||||
parse_string (p, "O") &&
|
||||
parse_coordinate_pair (p, &x3, &y3) &&
|
||||
parse_coordinate_pair (p, &x4, &y4) &&
|
||||
parse_nonnegative_number (p, &w1) &&
|
||||
parse_string (p, "O") &&
|
||||
parse_coordinate_pair (p, &x5, &y5) &&
|
||||
parse_coordinate_pair (p, &x6, &y6) &&
|
||||
parse_nonnegative_number (p, &w2) &&
|
||||
parse_string (p, "O") &&
|
||||
parse_coordinate_pair (p, &x7, &y7) &&
|
||||
parse_coordinate_pair (p, &x8, &y8) &&
|
||||
parse_nonnegative_number (p, &w3) &&
|
||||
parse_string (p, "Z"))
|
||||
{
|
||||
xx = (x0 + x4) / 2;
|
||||
yy = (y2 + y6) / 2;
|
||||
|
||||
#define NEAR(x, y) (fabs ((x) - (y)) < 0.001)
|
||||
|
||||
if (NEAR (x0, x1) && NEAR (x0, x8) && NEAR (x0, x7) &&
|
||||
NEAR (x2, x6) && NEAR (x3, x4) && NEAR (x4, x5) &&
|
||||
NEAR (y5, y6) && NEAR (y6, y7) && NEAR (y4, y8) &&
|
||||
NEAR (y8, y0) && NEAR (y3, y2) && NEAR (y2, y1) &&
|
||||
NEAR (x2, xx) && NEAR (yy, y4) &&
|
||||
NEAR (w0, M_SQRT1_2) && NEAR (w1, M_SQRT1_2) &&
|
||||
NEAR (w2, M_SQRT1_2) && NEAR (w3, M_SQRT1_2) &&
|
||||
x1 > x2 && x2 > x3 && y3 > y4 && y4 > y5)
|
||||
{
|
||||
*cx = xx;
|
||||
*cy = yy;
|
||||
*r = x0 - xx;
|
||||
|
||||
skip_whitespace (p);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#undef NEAR
|
||||
}
|
||||
|
||||
*p = o;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_parse:
|
||||
* @string: a string
|
||||
@ -1037,9 +1109,20 @@ gsk_path_parse (const char *string)
|
||||
case 'M':
|
||||
case 'm':
|
||||
{
|
||||
double x1, y1;
|
||||
double x1, y1, r;
|
||||
|
||||
if (parse_coordinate_pair (&p, &x1, &y1))
|
||||
if (parse_circle (&p, &x1, &y1, &r))
|
||||
{
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (x1, y1), r);
|
||||
if (_strchr ("zZX", prev_cmd))
|
||||
{
|
||||
path_x = x1 + r;
|
||||
path_y = y1;
|
||||
}
|
||||
x = x1 + r;
|
||||
y = y1;
|
||||
}
|
||||
else if (parse_coordinate_pair (&p, &x1, &y1))
|
||||
{
|
||||
if (cmd == 'm')
|
||||
{
|
||||
|
@ -560,34 +560,11 @@ gsk_path_builder_add_circle (GskPathBuilder *self,
|
||||
const graphene_point_t *center,
|
||||
float radius)
|
||||
{
|
||||
graphene_point_t current;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (center != NULL);
|
||||
g_return_if_fail (radius > 0);
|
||||
|
||||
current = self->current_point;
|
||||
|
||||
gsk_path_builder_move_to (self, center->x + radius, center->y);
|
||||
// bottom right quarter
|
||||
gsk_path_builder_arc_to (self,
|
||||
center->x + radius, center->y + radius,
|
||||
center->x, center->y + radius);
|
||||
// bottom left quarter
|
||||
gsk_path_builder_arc_to (self,
|
||||
center->x - radius, center->y + radius,
|
||||
center->x - radius, center->y);
|
||||
// top left quarter
|
||||
gsk_path_builder_arc_to (self,
|
||||
center->x - radius, center->y - radius,
|
||||
center->x, center->y - radius);
|
||||
// top right quarter
|
||||
gsk_path_builder_arc_to (self,
|
||||
center->x + radius, center->y - radius,
|
||||
center->x + radius, center->y);
|
||||
// done
|
||||
gsk_path_builder_close (self);
|
||||
self->current_point = current;
|
||||
gsk_path_builder_add_contour (self, gsk_circle_contour_new (center, radius));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -813,9 +813,10 @@ static void
|
||||
test_circle (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
GskPathMeasure *measure;
|
||||
float length;
|
||||
GskPath *path, *path1, *path2, *path3, *path4, *path5, *path6;
|
||||
GskPathMeasure *measure, *measure1, *measure2, *measure3;
|
||||
float length, length1, length2, length3;
|
||||
GskPathPoint point0, point1;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 0), 1);
|
||||
@ -826,8 +827,70 @@ test_circle (void)
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (length, 2 * M_PI, 0.001);
|
||||
|
||||
gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (1, 1), INFINITY, &point0);
|
||||
gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (-1, 1), INFINITY, &point1);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, path, &point0, &point1);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
measure1 = gsk_path_measure_new (path1);
|
||||
length1 = gsk_path_measure_get_length (measure1);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (length1, 2 * M_PI * 0.25, 0.001);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, path, &point1, &point0);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
measure2 = gsk_path_measure_new (path2);
|
||||
length2 = gsk_path_measure_get_length (measure2);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (length2, 2 * M_PI * 0.75, 0.001);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_reverse_path (builder, path);
|
||||
path3 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
measure3 = gsk_path_measure_new (path3);
|
||||
length3 = gsk_path_measure_get_length (measure3);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (length3, 2 * M_PI, 0.001);
|
||||
|
||||
g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_WINDING));
|
||||
g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_EVEN_ODD));
|
||||
g_assert_true (gsk_path_in_fill (path3, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_WINDING));
|
||||
g_assert_true (gsk_path_in_fill (path3, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_EVEN_ODD));
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_path (builder, path);
|
||||
gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (-2, -2, 4, 4));
|
||||
path4 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
g_assert_true (gsk_path_in_fill (path4, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_WINDING));
|
||||
g_assert_false (gsk_path_in_fill (path4, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_EVEN_ODD));
|
||||
|
||||
path5 = gsk_path_parse ("M 2 0 O 2 2 0 2 0.707 O -2 2 -2 0 0.707 O -2 -2 0 -2 0.707 O 2 -2 2 0 0.707 Z");
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_path (builder, path);
|
||||
gsk_path_builder_add_path (builder, path5);
|
||||
path6 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
g_assert_true (gsk_path_in_fill (path6, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_WINDING));
|
||||
g_assert_false (gsk_path_in_fill (path6, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_EVEN_ODD));
|
||||
|
||||
|
||||
gsk_path_measure_unref (measure);
|
||||
gsk_path_measure_unref (measure1);
|
||||
gsk_path_measure_unref (measure2);
|
||||
gsk_path_measure_unref (measure3);
|
||||
gsk_path_unref (path);
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
gsk_path_unref (path3);
|
||||
gsk_path_unref (path4);
|
||||
gsk_path_unref (path5);
|
||||
gsk_path_unref (path6);
|
||||
}
|
||||
|
||||
static void
|
||||
|
Loading…
Reference in New Issue
Block a user