Added small utility function for the manhattan metric.

2005-10-01  Simon Budig  <simon@gimp.org>

	* app/core/gimpcoords.[ch]: Added small utility function for the
	manhattan metric.

	* app/vectors/gimpstroke.[ch]: virtualized stubs provided by mitch

	* app/vectors/gimpbezierstroke.c: Reworked some internal functions
	to make it easier to access the parameters for the coordinates of an
	stroke interpolation. Simplified the straight line test. Added an
	implementation of the _nearest_tangent_get () function that does
	not yet seem to work properly. Not sure where the problem is.
This commit is contained in:
Simon Budig 2005-10-01 00:49:44 +00:00 committed by Simon Budig
parent e935273a19
commit 3c2e94a2fb
6 changed files with 466 additions and 211 deletions

View File

@ -1,3 +1,16 @@
2005-10-01 Simon Budig <simon@gimp.org>
* app/core/gimpcoords.[ch]: Added small utility function for the
manhattan metric.
* app/vectors/gimpstroke.[ch]: virtualized stubs provided by mitch
* app/vectors/gimpbezierstroke.c: Reworked some internal functions
to make it easier to access the parameters for the coordinates of an
stroke interpolation. Simplified the straight line test. Added an
implementation of the _nearest_tangent_get () function that does
not yet seem to work properly. Not sure where the problem is.
2005-09-30 Michael Natterer <mitch@gimp.org>
* app/actions/image-commands.c

View File

@ -148,6 +148,30 @@ gimp_coords_length (const GimpCoords *a)
return sqrt (gimp_coords_length_squared (a));
}
/*
* Distance via manhattan metric, an upper bound for the eucledian metric.
* used for e.g. bezier approximation
*/
gdouble
gimp_coords_manhattan_dist (const GimpCoords *a,
const GimpCoords *b)
{
gdouble dist = 0;
dist += fabs (a->pressure - b->pressure);
dist += fabs (a->xtilt - b->xtilt);
dist += fabs (a->ytilt - b->ytilt);
dist += fabs (a->wheel - b->wheel);
dist *= INPUT_RESOLUTION;
dist += fabs (a->x - b->x);
dist += fabs (a->y - b->y);
return dist;
}
gboolean
gimp_coords_equal (const GimpCoords *a,
const GimpCoords *b)

View File

@ -45,6 +45,8 @@ gdouble gimp_coords_scalarprod (const GimpCoords *a,
const GimpCoords *b);
gdouble gimp_coords_length (const GimpCoords *a);
gdouble gimp_coords_length_squared (const GimpCoords *a);
gdouble gimp_coords_manhattan_dist (const GimpCoords *a,
const GimpCoords *b);
gboolean gimp_coords_equal (const GimpCoords *a,
const GimpCoords *b);

View File

@ -37,7 +37,8 @@
static void gimp_bezier_stroke_class_init (GimpBezierStrokeClass *klass);
static void gimp_bezier_stroke_init (GimpBezierStroke *bezier_stroke);
static gdouble gimp_bezier_stroke_nearest_point_get (const GimpStroke *stroke,
static gdouble
gimp_bezier_stroke_nearest_point_get (const GimpStroke *stroke,
const GimpCoords *coord,
const gdouble precision,
GimpCoords *ret_point,
@ -45,37 +46,61 @@ static gdouble gimp_bezier_stroke_nearest_point_get (const GimpStroke *stroke,
GimpAnchor **ret_segment_end,
gdouble *ret_pos);
static gdouble
gimp_bezier_stroke_segment_nearest_point_get (const GimpCoords *beziercoords,
gimp_bezier_stroke_segment_nearest_point_get
(const GimpCoords *beziercoords,
const GimpCoords *coord,
const gdouble precision,
GimpCoords *ret_point,
gdouble *ret_pos,
gint depth);
static void gimp_bezier_stroke_anchor_move_relative (GimpStroke *stroke,
static gdouble
gimp_bezier_stroke_nearest_tangent_get (const GimpStroke *stroke,
const GimpCoords *coord1,
const GimpCoords *coord2,
const gdouble precision,
GimpCoords *nearest,
GimpAnchor **ret_segment_start,
GimpAnchor **ret_segment_end,
gdouble *ret_pos);
static gdouble
gimp_bezier_stroke_segment_nearest_tangent_get
(const GimpCoords *beziercoords,
const GimpCoords *coord1,
const GimpCoords *coord2,
const gdouble precision,
GimpCoords *ret_point,
gdouble *ret_pos);
static void
gimp_bezier_stroke_anchor_move_relative
(GimpStroke *stroke,
GimpAnchor *anchor,
const GimpCoords *deltacoord,
GimpAnchorFeatureType feature);
static void gimp_bezier_stroke_anchor_move_absolute (GimpStroke *stroke,
static void
gimp_bezier_stroke_anchor_move_absolute
(GimpStroke *stroke,
GimpAnchor *anchor,
const GimpCoords *coord,
GimpAnchorFeatureType feature);
static void gimp_bezier_stroke_anchor_convert (GimpStroke *stroke,
static void
gimp_bezier_stroke_anchor_convert (GimpStroke *stroke,
GimpAnchor *anchor,
GimpAnchorFeatureType feature);
static void gimp_bezier_stroke_anchor_delete (GimpStroke *stroke,
static void
gimp_bezier_stroke_anchor_delete (GimpStroke *stroke,
GimpAnchor *anchor);
static gboolean gimp_bezier_stroke_point_is_movable
(GimpStroke *stroke,
static gboolean
gimp_bezier_stroke_point_is_movable (GimpStroke *stroke,
GimpAnchor *predec,
gdouble position);
static void gimp_bezier_stroke_point_move_relative
(GimpStroke *stroke,
static void
gimp_bezier_stroke_point_move_relative (GimpStroke *stroke,
GimpAnchor *predec,
gdouble position,
const GimpCoords *deltacoord,
GimpAnchorFeatureType feature);
static void gimp_bezier_stroke_point_move_absolute
(GimpStroke *stroke,
static void
gimp_bezier_stroke_point_move_absolute (GimpStroke *stroke,
GimpAnchor *predec,
gdouble position,
const GimpCoords *coord,
@ -83,39 +108,51 @@ static void gimp_bezier_stroke_point_move_absolute
static void gimp_bezier_stroke_close (GimpStroke *stroke);
static GimpStroke * gimp_bezier_stroke_open (GimpStroke *stroke,
static GimpStroke *
gimp_bezier_stroke_open (GimpStroke *stroke,
GimpAnchor *end_anchor);
static gboolean gimp_bezier_stroke_anchor_is_insertable
static gboolean
gimp_bezier_stroke_anchor_is_insertable
(GimpStroke *stroke,
GimpAnchor *predec,
gdouble position);
static GimpAnchor * gimp_bezier_stroke_anchor_insert (GimpStroke *stroke,
static GimpAnchor *
gimp_bezier_stroke_anchor_insert (GimpStroke *stroke,
GimpAnchor *predec,
gdouble position);
static gboolean gimp_bezier_stroke_is_extendable (GimpStroke *stroke,
static gboolean
gimp_bezier_stroke_is_extendable (GimpStroke *stroke,
GimpAnchor *neighbor);
static gboolean gimp_bezier_stroke_connect_stroke (GimpStroke *stroke,
static gboolean
gimp_bezier_stroke_connect_stroke (GimpStroke *stroke,
GimpAnchor *anchor,
GimpStroke *extension,
GimpAnchor *neighbor);
static GArray * gimp_bezier_stroke_interpolate (const GimpStroke *stroke,
static GArray *
gimp_bezier_stroke_interpolate (const GimpStroke *stroke,
const gdouble precision,
gboolean *closed);
static void gimp_bezier_stroke_finalize (GObject *object);
static GList * gimp_bezier_stroke_get_anchor_listitem (GList *list);
static GList * gimp_bezier_stroke_get_anchor_listitem
(GList *list);
static void gimp_bezier_coords_subdivide (const GimpCoords *beziercoords,
const gdouble precision,
GArray **ret_coords);
GArray **ret_coords,
GArray **ret_params);
static void gimp_bezier_coords_subdivide2 (const GimpCoords *beziercoords,
const gdouble start_t,
const gdouble end_t,
const gdouble precision,
GArray **ret_coords,
GArray **ret_params,
gint depth);
static gboolean gimp_bezier_coords_is_straight (const GimpCoords *beziercoords,
static gboolean gimp_bezier_coords_is_straight
(const GimpCoords *beziercoords,
const gdouble precision);
@ -166,6 +203,7 @@ gimp_bezier_stroke_class_init (GimpBezierStrokeClass *klass)
object_class->finalize = gimp_bezier_stroke_finalize;
stroke_class->nearest_point_get = gimp_bezier_stroke_nearest_point_get;
stroke_class->nearest_tangent_get = gimp_bezier_stroke_nearest_tangent_get;
stroke_class->anchor_move_relative = gimp_bezier_stroke_anchor_move_relative;
stroke_class->anchor_move_absolute = gimp_bezier_stroke_anchor_move_absolute;
stroke_class->anchor_convert = gimp_bezier_stroke_anchor_convert;
@ -586,7 +624,7 @@ gimp_bezier_stroke_nearest_point_get (const GimpStroke *stroke,
GimpAnchor *anchor;
gint count;
g_return_val_if_fail (GIMP_IS_BEZIER_STROKE (stroke), - 1.0);
g_return_val_if_fail (GIMP_IS_BEZIER_STROKE (stroke), -1.0);
if (!stroke->anchors)
return -1.0;
@ -745,7 +783,7 @@ gimp_bezier_stroke_segment_nearest_point_get (const GimpCoords *beziercoords,
subdivided[0] = beziercoords[0];
subdivided[6] = beziercoords[3];
/* if (!depth) g_printerr ("Hit rekursion depth limit!\n"); */
/* if (!depth) g_printerr ("Hit recursion depth limit!\n"); */
gimp_coords_average (&(beziercoords[0]), &(beziercoords[1]),
&(subdivided[1]));
@ -795,6 +833,183 @@ gimp_bezier_stroke_segment_nearest_point_get (const GimpCoords *beziercoords,
}
static gdouble
gimp_bezier_stroke_nearest_tangent_get (const GimpStroke *stroke,
const GimpCoords *coord1,
const GimpCoords *coord2,
const gdouble precision,
GimpCoords *nearest,
GimpAnchor **ret_segment_start,
GimpAnchor **ret_segment_end,
gdouble *ret_pos)
{
gdouble min_dist, dist, pos;
GimpCoords point;
GimpCoords segmentcoords[4];
GList *anchorlist;
GimpAnchor *segment_start, *segment_end = NULL;
GimpAnchor *anchor;
gint count;
g_return_val_if_fail (GIMP_IS_BEZIER_STROKE (stroke), -1.0);
if (!stroke->anchors)
return -1.0;
count = 0;
min_dist = -1;
for (anchorlist = stroke->anchors;
anchorlist && GIMP_ANCHOR (anchorlist->data)->type != GIMP_ANCHOR_ANCHOR;
anchorlist = g_list_next (anchorlist));
segment_start = anchorlist->data;
for ( ; anchorlist; anchorlist = g_list_next (anchorlist))
{
anchor = anchorlist->data;
segmentcoords[count] = anchor->position;
count++;
if (count == 4)
{
segment_end = anchorlist->data;
dist = gimp_bezier_stroke_segment_nearest_tangent_get (segmentcoords,
coord1, coord2,
precision,
&point, &pos);
if (dist < min_dist || min_dist < 0)
{
min_dist = dist;
if (ret_pos)
*ret_pos = pos;
if (ret_segment_start)
*ret_segment_start = segment_start;
if (ret_segment_end)
*ret_segment_end = segment_end;
}
segment_start = anchorlist->data;
segmentcoords[0] = segmentcoords[3];
count = 1;
}
}
if (stroke->closed && stroke->anchors)
{
anchorlist = stroke->anchors;
while (count < 3)
{
segmentcoords[count] = GIMP_ANCHOR (anchorlist->data)->position;
count++;
}
anchorlist = g_list_next (anchorlist);
if (anchorlist)
{
segment_end = GIMP_ANCHOR (anchorlist->data);
segmentcoords[3] = segment_end->position;
}
dist = gimp_bezier_stroke_segment_nearest_tangent_get (segmentcoords,
coord1, coord2,
precision,
&point, &pos);
if (dist < min_dist || min_dist < 0)
{
min_dist = dist;
if (ret_pos)
*ret_pos = pos;
if (ret_segment_start)
*ret_segment_start = segment_start;
if (ret_segment_end)
*ret_segment_end = segment_end;
}
}
return min_dist;
}
static gdouble
gimp_bezier_stroke_segment_nearest_tangent_get (const GimpCoords *beziercoords,
const GimpCoords *coord1,
const GimpCoords *coord2,
const gdouble precision,
GimpCoords *ret_point,
gdouble *ret_pos)
{
GArray *ret_coords, *ret_params;
GimpCoords dir, line, dcoord, min_point;
gdouble min_dist = -1, dist, length2, scalar, ori, ori2;
gint i;
gimp_coords_difference (coord2, coord1, &line);
ret_coords = g_array_new (FALSE, FALSE, sizeof (GimpCoords));
ret_params = g_array_new (FALSE, FALSE, sizeof (gdouble));
gimp_bezier_coords_subdivide (beziercoords, precision,
&ret_coords, &ret_params);
g_return_val_if_fail (ret_coords->len == ret_params->len, -1.0);
if (ret_coords->len < 2)
return -1;
gimp_coords_difference (&g_array_index (ret_coords, GimpCoords, 1),
&g_array_index (ret_coords, GimpCoords, 0),
&dir);
ori = dir.x * line.y - dir.y * line.x;
for (i = 1; i < ret_coords->len; i++)
{
gimp_coords_difference (&g_array_index (ret_coords, GimpCoords, i+1),
&g_array_index (ret_coords, GimpCoords, i),
&dir);
ori2 = dir.x * line.y - dir.y * line.x;
if (ori * ori2 <= 0)
{
#if 0
if (ori2 == 0)
/* Kandidat finden */;
else
/* ret_coords[i] ist der Kandidat */;
#endif
gimp_coords_difference (&g_array_index (ret_coords, GimpCoords, i),
coord1,
&dcoord);
length2 = gimp_coords_scalarprod (&line, &line);
scalar = gimp_coords_scalarprod (&line, &dcoord) / length2;
if (scalar >= 0 && scalar <= 1)
{
gimp_coords_mix (1.0, coord1,
scalar, &line,
&min_point);
gimp_coords_difference (&min_point,
&g_array_index (ret_coords, GimpCoords, i),
&dcoord);
dist = gimp_coords_length (&dcoord);
if (dist < min_dist || min_dist < 0)
{
min_dist = dist;
*ret_point = min_point;
*ret_pos = g_array_index (ret_params, gdouble, i);
}
}
}
ori = ori2;
}
return min_dist;
}
static gboolean
gimp_bezier_stroke_is_extendable (GimpStroke *stroke,
GimpAnchor *neighbor)
@ -1311,7 +1526,8 @@ gimp_bezier_stroke_interpolate (const GimpStroke *stroke,
if (count == 4)
{
gimp_bezier_coords_subdivide (segmentcoords, precision, &ret_coords);
gimp_bezier_coords_subdivide (segmentcoords, precision,
&ret_coords, NULL);
segmentcoords[0] = segmentcoords[3];
count = 1;
need_endpoint = TRUE;
@ -1331,7 +1547,8 @@ gimp_bezier_stroke_interpolate (const GimpStroke *stroke,
if (anchorlist)
segmentcoords[3] = GIMP_ANCHOR (anchorlist->data)->position;
gimp_bezier_coords_subdivide (segmentcoords, precision, &ret_coords);
gimp_bezier_coords_subdivide (segmentcoords, precision,
&ret_coords, NULL);
need_endpoint = TRUE;
}
@ -1842,6 +2059,10 @@ gimp_bezier_stroke_get_anchor_listitem (GList *list)
* a helper function that determines if a bezier segment is "straight
* enough" to be approximated by a line.
*
* To be more exact, it also checks for the control points to be distributed
* evenly along the line. This makes it easier to reconstruct parameters for
* a given point along the segment.
*
* Needs four GimpCoords in an array.
*/
@ -1849,63 +2070,21 @@ static gboolean
gimp_bezier_coords_is_straight (const GimpCoords *beziercoords,
gdouble precision)
{
GimpCoords line, tan1, tan2, d1, d2;
gdouble l2, s1, s2;
GimpCoords pt1, pt2;
gimp_coords_difference (&(beziercoords[3]),
&(beziercoords[0]),
&line);
/* calculate the "ideal" positions for the control points */
if (gimp_coords_length_squared (&line) < precision * precision)
{
gimp_coords_difference (&(beziercoords[1]),
&(beziercoords[0]),
&tan1);
gimp_coords_difference (&(beziercoords[2]),
&(beziercoords[3]),
&tan2);
if ((gimp_coords_length_squared (&tan1) < precision * precision) &&
(gimp_coords_length_squared (&tan2) < precision * precision))
{
return 1;
}
else
{
/* Tangents are too big for the small baseline */
return 0;
}
}
else
{
gimp_coords_difference (&(beziercoords[1]),
&(beziercoords[0]),
&tan1);
gimp_coords_difference (&(beziercoords[2]),
&(beziercoords[0]),
&tan2);
gimp_coords_mix (2.0 / 3.0, &(beziercoords[0]),
1.0 / 3.0, &(beziercoords[3]),
&pt1);
gimp_coords_mix (1.0 / 3.0, &(beziercoords[0]),
2.0 / 3.0, &(beziercoords[3]),
&pt2);
l2 = gimp_coords_scalarprod (&line, &line);
s1 = gimp_coords_scalarprod (&line, &tan1) / l2;
s2 = gimp_coords_scalarprod (&line, &tan2) / l2;
/* calculate the deviation of the actual control points */
if (s1 < 0 || s1 > 1 || s2 < 0 || s2 > 1 || s2 < s1)
{
/* The tangents get projected outside the baseline */
return 0;
}
gimp_coords_mix (1.0, &tan1, - s1, &line, &d1);
gimp_coords_mix (1.0, &tan2, - s2, &line, &d2);
if ((gimp_coords_length_squared (&d1) > precision * precision) ||
(gimp_coords_length_squared (&d2) > precision * precision))
{
/* The control points are too far away from the baseline */
return 0;
}
return 1;
}
return (gimp_coords_manhattan_dist (&(beziercoords[1]), &pt1) < precision &&
gimp_coords_manhattan_dist (&(beziercoords[2]), &pt2) < precision);
}
@ -1914,16 +2093,21 @@ gimp_bezier_coords_is_straight (const GimpCoords *beziercoords,
static void
gimp_bezier_coords_subdivide (const GimpCoords *beziercoords,
const gdouble precision,
GArray **ret_coords)
GArray **ret_coords,
GArray **ret_params)
{
gimp_bezier_coords_subdivide2 (beziercoords, precision, ret_coords, 10);
gimp_bezier_coords_subdivide2 (beziercoords, 0.0, 1.0,
precision, ret_coords, ret_params, 10);
}
static void
gimp_bezier_coords_subdivide2 (const GimpCoords *beziercoords,
const gdouble start_t,
const gdouble end_t,
const gdouble precision,
GArray **ret_coords,
GArray **ret_params,
gint depth)
{
/*
@ -1932,11 +2116,12 @@ gimp_bezier_coords_subdivide2 (const GimpCoords *beziercoords,
*/
GimpCoords subdivided[8];
gdouble middle_t = (start_t + end_t) / 2;
subdivided[0] = beziercoords[0];
subdivided[6] = beziercoords[3];
/* if (!depth) g_printerr ("Hit rekursion depth limit!\n"); */
/* if (!depth) g_printerr ("Hit recursion depth limit!\n"); */
gimp_coords_average (&(beziercoords[0]), &(beziercoords[1]),
&(subdivided[1]));
@ -1970,22 +2155,42 @@ gimp_bezier_coords_subdivide2 (const GimpCoords *beziercoords,
precision)) /* 1st half */
{
*ret_coords = g_array_append_vals (*ret_coords, &(subdivided[0]), 3);
if (ret_params)
{
gdouble params[3];
params[0] = start_t;
params[1] = (2 * start_t + middle_t) / 3;
params[2] = (start_t + 2 * middle_t) / 3;
*ret_params = g_array_append_vals (*ret_params, &(params[0]), 3);
}
}
else
{
gimp_bezier_coords_subdivide2 (&(subdivided[0]), precision,
ret_coords, depth-1);
gimp_bezier_coords_subdivide2 (&(subdivided[0]),
start_t, (start_t + end_t) / 2,
precision,
ret_coords, ret_params, depth-1);
}
if (!depth || gimp_bezier_coords_is_straight (&(subdivided[3]),
precision)) /* 2nd half */
{
*ret_coords = g_array_append_vals (*ret_coords, &(subdivided[3]), 3);
if (ret_params)
{
gdouble params[3];
params[0] = middle_t;
params[1] = (2 * middle_t + end_t) / 3;
params[2] = (middle_t + 2 * end_t) / 3;
*ret_params = g_array_append_vals (*ret_params, &(params[0]), 3);
}
}
else
{
gimp_bezier_coords_subdivide2 (&(subdivided[3]), precision,
ret_coords, depth-1);
gimp_bezier_coords_subdivide2 (&(subdivided[3]),
(start_t + end_t) / 2, end_t,
precision,
ret_coords, ret_params, depth-1);
}
}

View File

@ -55,13 +55,6 @@ static void gimp_stroke_finalize (GObject *object);
static gint64 gimp_stroke_get_memsize (GimpObject *object,
gint64 *gui_size);
static gdouble gimp_stroke_real_nearest_point_get (const GimpStroke *stroke,
const GimpCoords *coord,
const gdouble precision,
GimpCoords *ret_point,
GimpAnchor **ret_segment_start,
GimpAnchor **ret_segment_end,
gdouble *ret_pos);
static GimpAnchor * gimp_stroke_real_anchor_get (const GimpStroke *stroke,
const GimpCoords *coord);
static GimpAnchor * gimp_stroke_real_anchor_get_next (const GimpStroke *stroke,
@ -225,7 +218,9 @@ gimp_stroke_class_init (GimpStrokeClass *klass)
klass->point_move_relative = gimp_stroke_real_point_move_relative;
klass->point_move_absolute = gimp_stroke_real_point_move_absolute;
klass->nearest_point_get = gimp_stroke_real_nearest_point_get;
klass->nearest_point_get = NULL;
klass->nearest_tangent_get = NULL;
klass->nearest_intersection_get = NULL;
klass->close = gimp_stroke_real_close;
klass->open = gimp_stroke_real_open;
klass->anchor_is_insertable = gimp_stroke_real_anchor_is_insertable;
@ -423,6 +418,7 @@ gimp_stroke_nearest_point_get (const GimpStroke *stroke,
g_return_val_if_fail (GIMP_IS_STROKE (stroke), FALSE);
g_return_val_if_fail (coord != NULL, FALSE);
if (GIMP_STROKE_GET_CLASS (stroke)->nearest_point_get)
return GIMP_STROKE_GET_CLASS (stroke)->nearest_point_get (stroke,
coord,
precision,
@ -430,6 +426,7 @@ gimp_stroke_nearest_point_get (const GimpStroke *stroke,
ret_segment_start,
ret_segment_end,
ret_pos);
return -1;
}
gdouble
@ -446,10 +443,15 @@ gimp_stroke_nearest_tangent_get (const GimpStroke *stroke,
g_return_val_if_fail (coords1 != NULL, FALSE);
g_return_val_if_fail (coords2 != NULL, FALSE);
#ifdef __GNUC__
#warning please implement me, nomis!
#endif
if (GIMP_STROKE_GET_CLASS (stroke)->nearest_tangent_get)
return GIMP_STROKE_GET_CLASS (stroke)->nearest_tangent_get (stroke,
coords1,
coords2,
precision,
nearest,
ret_segment_start,
ret_segment_end,
ret_pos);
return -1;
}
@ -467,23 +469,15 @@ gimp_stroke_nearest_intersection_get (const GimpStroke *stroke,
g_return_val_if_fail (coords1 != NULL, FALSE);
g_return_val_if_fail (direction != NULL, FALSE);
#ifdef __GNUC__
#warning please implement me, nomis!
#endif
return -1;
}
static gdouble
gimp_stroke_real_nearest_point_get (const GimpStroke *stroke,
const GimpCoords *coord,
const gdouble precision,
GimpCoords *ret_point,
GimpAnchor **ret_segment_start,
GimpAnchor **ret_segment_end,
gdouble *ret_pos)
{
g_printerr ("gimp_stroke_nearest_point_get: default implementation\n");
if (GIMP_STROKE_GET_CLASS (stroke)->nearest_tangent_get)
return GIMP_STROKE_GET_CLASS (stroke)->nearest_tangent_get (stroke,
coords1,
direction,
precision,
nearest,
ret_segment_start,
ret_segment_end,
ret_pos);
return -1;
}

View File

@ -61,6 +61,23 @@ struct _GimpStrokeClass
GimpAnchor **ret_segment_start,
GimpAnchor **ret_segment_end,
gdouble *ret_pos);
gdouble (* nearest_tangent_get) (const GimpStroke *stroke,
const GimpCoords *coord1,
const GimpCoords *coord2,
const gdouble precision,
GimpCoords *nearest,
GimpAnchor **ret_segment_start,
GimpAnchor **ret_segment_end,
gdouble *ret_pos);
gdouble (* nearest_intersection_get)
(const GimpStroke *stroke,
const GimpCoords *coord1,
const GimpCoords *direction,
const gdouble precision,
GimpCoords *nearest,
GimpAnchor **ret_segment_start,
GimpAnchor **ret_segment_end,
gdouble *ret_pos);
GimpAnchor * (* anchor_get_next) (const GimpStroke *stroke,
const GimpAnchor *prev);
void (* anchor_select) (GimpStroke *stroke,