gimp/app/tools/path_tool.c

1918 lines
48 KiB
C

/* The GIMP -- an image manipulation program
*
* This file Copyright (C) 1999 Simon Budig
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* Complete new path-tool by Simon Budig <Simon.Budig@unix-ag.org>
*
* a path manipulation core independent of the underlying formula:
* implement bezier-curves, intelligent scissors-curves, splines...
*
* A Path is a collection of curves, which are constructed from
* segments between two anchors.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "tools-types.h"
#include "path_curves.h"
#include "path_tool.h"
#include "gimppathtool.h"
#include "libgimp/gimpintl.h"
/*
* Every new curve-type has to have a parameter between 0 and 1, and
* should go from a starting to a target point.
*/
/* Some defines... */
#define PATH_TOOL_WIDTH 8
#define PATH_TOOL_HALFWIDTH 4
/* local function prototypes */
/* Small functions to determine coordinates, iterate over path/curve/segment */
void path_segment_get_coordinates (PathSegment *,
gdouble,
gint *,
gint *);
void path_traverse_path (NPath *,
PathTraverseFunc,
CurveTraverseFunc,
SegmentTraverseFunc,
gpointer);
void path_traverse_curve (NPath *,
PathCurve *,
CurveTraverseFunc,
SegmentTraverseFunc,
gpointer);
void path_traverse_segment (NPath *,
PathCurve *,
PathSegment *,
SegmentTraverseFunc,
gpointer);
gdouble path_locate_point (NPath *,
PathCurve **,
PathSegment **,
gint,
gint,
gint,
gint,
gint);
gdouble path_tool_on_curve (NPath *path,
gint x,
gint y,
gint halfwidth,
NPath **ret_pathP,
PathCurve **ret_curveP,
PathSegment **ret_segmentP);
/* Tools to manipulate paths, curves, segments */
PathCurve * path_add_curve (NPath *,
gdouble,
gdouble);
PathSegment * path_append_segment (NPath *,
PathCurve *,
SegmentType,
gdouble,
gdouble);
PathSegment * path_prepend_segment (NPath *,
PathCurve *,
SegmentType,
gdouble,
gdouble);
PathSegment * path_split_segment (PathSegment *,
gdouble);
void path_join_curves (PathSegment *,
PathSegment *);
void path_flip_curve (PathCurve *);
void path_free_path (NPath *);
void path_free_curve (PathCurve *);
void path_free_segment (PathSegment *);
void path_delete_segment (PathSegment *);
void path_print (NPath *);
void path_offset_active (NPath *, gdouble, gdouble);
void path_set_flags (GimpPathTool *,
NPath *,
PathCurve *,
PathSegment *,
guint32,
guint32);
/* High level image-manipulation functions */
void path_stroke (GimpPathTool *,
NPath *);
void path_to_selection (GimpPathTool *,
NPath *);
/* Functions necessary for the tool */
gboolean path_tool_on_anchors (NPath *, gdouble, gdouble, gint,
NPath**, PathCurve**, PathSegment**);
gint path_tool_on_handles (NPath *, gdouble, gdouble, gint,
NPath **, PathCurve **, PathSegment **);
#if 0
void path_tool_button_press (Tool *, GdkEventButton *, gpointer);
void path_tool_button_release (Tool *, GdkEventButton *, gpointer);
void path_tool_motion (Tool *, GdkEventMotion *, gpointer);
void path_tool_cursor_update (Tool *, GdkEventMotion *, gpointer);
void path_tool_control (Tool *, ToolAction, gpointer);
void path_tool_draw (Tool *);
void path_tool_draw_curve (Tool *, PathCurve *);
void path_tool_draw_segment (Tool *, PathSegment *);
gdouble path_tool_on_curve (Tool *, gint, gint, gint,
NPath**, PathCurve**, PathSegment**);
gint path_tool_button_press_canvas (Tool *, GdkEventButton *, GimpDisplay *);
gint path_tool_button_press_anchor (Tool *, GdkEventButton *, GimpDisplay *);
gint path_tool_button_press_handle (Tool *, GdkEventButton *, GimpDisplay *);
gint path_tool_button_press_curve (Tool *, GdkEventButton *, GimpDisplay *);
void path_tool_motion_anchor (Tool *, GdkEventMotion *, GimpDisplay *);
void path_tool_motion_handle (Tool *, GdkEventMotion *, GimpDisplay *);
void path_tool_motion_curve (Tool *, GdkEventMotion *, GimpDisplay *);
#endif
/* the path tool options */
static GimpToolOptions *path_options = NULL;
/*
*
*
* Here we go!
*
*
*/
/*
* These functions are for applying a function over a complete
* path/curve/segment. They can pass information to each other
* with a arbitrary data structure
*
* The idea behind the three different functions is:
* if pathfunc != NULL
* call pathfunc for every curve
* else
* if curvefunc != NULL
* call curvefunc for every segment
* else
* call segmentfunc for every point
*
*/
void
path_traverse_path (NPath *path,
PathTraverseFunc pathfunc,
CurveTraverseFunc curvefunc,
SegmentTraverseFunc segmentfunc,
gpointer data)
{
PathCurve *cur_curve;
if (path && path->curves)
{
cur_curve = path->curves;
if (pathfunc)
do {
(* pathfunc) (path, cur_curve, data);
cur_curve = cur_curve->next;
} while (cur_curve && cur_curve != path->curves);
else
do {
path_traverse_curve (path, cur_curve, curvefunc, segmentfunc, data);
cur_curve = cur_curve->next;
} while (cur_curve && cur_curve != path->curves);
}
}
void
path_traverse_curve (NPath *path,
PathCurve *curve,
CurveTraverseFunc curvefunc,
SegmentTraverseFunc segmentfunc,
gpointer data)
{
PathSegment *cur_segment;
if (curve && curve->segments)
{
cur_segment = curve->segments;
if (curvefunc)
do {
(* curvefunc) (path, curve, cur_segment, data);
cur_segment = cur_segment->next;
} while (cur_segment && cur_segment != curve->segments);
else
do {
path_traverse_segment (path, curve, cur_segment, segmentfunc, data);
cur_segment = cur_segment->next;
} while (cur_segment && cur_segment != curve->segments);
}
}
void
path_traverse_segment (NPath *path,
PathCurve *curve,
PathSegment *segment,
SegmentTraverseFunc function,
gpointer data)
{
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_traverse_segment\n");
#endif
/* XXX: here we need path_curve_get_point(s) */
/* Something like:
* for i = 1 to subsamples {
* (x,y) = get_coordinates(i / subsamples)
* (* function) (....)
* }
*/
}
/**************************************************************
* Helper functions for manipulating the data-structures:
*/
PathCurve *
path_add_curve (NPath * cur_path,
gdouble x,
gdouble y)
{
PathCurve * tmp = cur_path->curves;
PathCurve * new_curve = NULL;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_add_curve\n");
#endif
if (cur_path) {
new_curve = g_new (PathCurve, 1);
new_curve->parent = cur_path;
new_curve->next = tmp;
new_curve->prev = NULL;
new_curve->cur_segment = NULL;
new_curve->segments = NULL;
if (tmp) tmp->prev = new_curve;
cur_path->curves = cur_path->cur_curve = new_curve;
new_curve->segments = path_prepend_segment (cur_path, new_curve, SEGMENT_BEZIER, x, y);
}
#ifdef PATH_TOOL_DEBUG
else
g_printerr ("Fatal Error: path_add_curve called without valid path\n");
#endif
return new_curve;
}
PathSegment *
path_append_segment (NPath * cur_path,
PathCurve * cur_curve,
SegmentType type,
gdouble x,
gdouble y)
{
PathSegment * tmp = cur_curve->segments;
PathSegment * new_segment = NULL;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_append_segment\n");
#endif
if (cur_curve) {
tmp = cur_curve->segments;
while (tmp && tmp->next && tmp->next != cur_curve->segments) {
tmp = tmp->next;
}
if (tmp == NULL || tmp->next == NULL) {
new_segment = g_new (PathSegment, 1);
new_segment->type = type;
new_segment->x = x;
new_segment->y = y;
new_segment->flags = 0;
new_segment->parent = cur_curve;
new_segment->next = NULL;
new_segment->prev = tmp;
new_segment->data = NULL;
if (tmp)
tmp->next = new_segment;
cur_curve->cur_segment = new_segment;
path_curve_init_segment (new_segment);
}
#ifdef PATH_TOOL_DEBUG
else
g_printerr ("Fatal Error: path_append_segment called with a closed curve\n");
#endif
}
#ifdef PATH_TOOL_DEBUG
else
g_printerr ("Fatal Error: path_append_segment called without valid curve\n");
#endif
return new_segment;
}
PathSegment *
path_prepend_segment (NPath * cur_path,
PathCurve * cur_curve,
SegmentType type,
gdouble x,
gdouble y)
{
PathSegment * tmp = cur_curve->segments;
PathSegment * new_segment = NULL;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_prepend_segment\n");
#endif
if (cur_curve) {
tmp = cur_curve->segments;
if (tmp == NULL || tmp->prev == NULL) {
new_segment = g_new (PathSegment, 1);
new_segment->type = type;
new_segment->x = x;
new_segment->y = y;
new_segment->flags = 0;
new_segment->parent = cur_curve;
new_segment->next = tmp;
new_segment->prev = NULL;
new_segment->data = NULL;
if (tmp)
tmp->prev = new_segment;
cur_curve->segments = new_segment;
cur_curve->cur_segment = new_segment;
path_curve_init_segment (new_segment);
}
#ifdef PATH_TOOL_DEBUG
else
g_printerr ("Fatal Error: path_prepend_segment called with a closed curve\n");
#endif
}
#ifdef PATH_TOOL_DEBUG
else
g_printerr ("Fatal Error: path_prepend_segment called without valid curve\n");
#endif
return new_segment;
}
PathSegment *
path_split_segment (PathSegment *segment,
gdouble position)
{
PathSegment * new_segment = NULL;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_split_segment\n");
#endif
if (segment && segment->next) {
new_segment = g_new (PathSegment, 1);
new_segment->type = segment->type;
path_curve_get_point (segment, position, &(new_segment->x), &(new_segment->y));
new_segment->flags = 0;
new_segment->parent = segment->parent;
new_segment->next = segment->next;
new_segment->prev = segment;
new_segment->data = NULL;
path_curve_init_segment (new_segment);
new_segment->next->prev = new_segment;
segment->next = new_segment;
return new_segment;
}
#ifdef PATH_TOOL_DEBUG
else
g_printerr ("path_split_segment without valid segment\n");
#endif
return NULL;
}
/*
* Join two arbitrary endpoints and free the parent from the second
* segment, if it differs from the first parents.
*/
void
path_join_curves (PathSegment *segment1,
PathSegment *segment2)
{
PathCurve *curve1, *curve2;
PathSegment *tmp;
if ((segment1->next && segment1->prev) || (segment2->next && segment2->prev)) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("Fatal Error: path_join_curves called with a closed segment\n");
#endif
return;
}
if (segment1->parent == segment2->parent) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("Joining beginning and end of the same curve...\n");
#endif
if (segment2->next == NULL) {
segment2->next = segment1;
segment1->prev = segment2;
} else {
segment2->prev = segment1;
segment1->next = segment2;
}
/* XXX: Probably some segment-updates needed */
return;
}
if (segment1->next == NULL && segment2->next == NULL) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("Flipping second curve (next, next)...\n");
#endif
path_flip_curve (segment2->parent);
/* segment2 = segment2->parent->segments;
*/
}
if (segment1->prev == NULL && segment2->prev == NULL) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("Flipping second curve (prev, prev)...\n");
#endif
path_flip_curve (segment2->parent);
/* segment2 = segment2->parent->segments;
* while (segment2->next)
* segment2 = segment2->next;
*/
}
if (segment1->next == NULL && segment2->prev == NULL) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("Appending second to first curve...\n");
#endif
curve1 = segment1->parent;
curve2 = segment2->parent;
segment1->next = segment2;
segment2->prev = segment1;
curve2->segments = NULL;
if (curve2->prev)
curve2->prev->next = curve2->next;
if (curve2->next)
curve2->next->prev = curve2->prev;
if (curve2->parent->curves == curve2)
curve2->parent->curves = curve2->next;
path_free_curve (curve2);
tmp = segment2;
while (tmp) {
tmp->parent = curve1;
tmp = tmp->next;
}
/* XXX: Probably some segment-updates needed */
return;
}
if (segment1->prev == NULL && segment2->next == NULL) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("Prepending second to first curve...\n");
#endif
curve1 = segment1->parent;
curve2 = segment2->parent;
segment1->prev = segment2;
segment2->next = segment1;
curve2->segments = NULL;
if (curve2->prev)
curve2->prev->next = curve2->next;
if (curve2->next)
curve2->next->prev = curve2->prev;
if (curve2->parent->curves == curve2)
curve2->parent->curves = curve2->next;
path_free_curve (curve2);
tmp = segment2;
while (tmp) {
tmp->parent = curve1;
curve1->segments = tmp;
tmp = tmp->prev;
}
return;
/* XXX: Probably some segment-updates needed */
}
#ifdef PATH_TOOL_DEBUG
g_printerr ("Cant join these curves yet...\nThis should not happen.");
return;
#endif
}
/*
* This function reverses the order of the anchors. This is
* necessary for some joining operations.
*/
void
path_flip_curve (PathCurve *curve)
{
gpointer *end_data;
SegmentType end_type;
PathSegment *tmp, *tmp2;
if (!curve && !curve->segments) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_flip_curve: No curve o no segments to flip!\n");
#endif
return;
}
tmp = curve->segments;
while (tmp->next)
tmp = tmp->next;
end_data = tmp->data;
end_type = tmp->type;
tmp->parent->segments = tmp;
while (tmp) {
tmp2 = tmp->next;
tmp->next = tmp->prev;
tmp->prev = tmp2;
if (tmp->next) {
tmp->type = tmp->next->type;
tmp->data = tmp->next->data;
} else {
tmp->type = end_type;
tmp->data = end_data;
}
path_curve_flip_segment (tmp);
tmp = tmp->next;
}
}
void
path_free_path (NPath * path)
{
PathCurve *tmp1, *tmp2;
if (path)
{
tmp2 = path->curves;
while ((tmp1 = tmp2) != NULL)
{
tmp2 = tmp1->next;
path_free_curve (tmp1);
}
g_string_free(path->name, TRUE);
g_free(path);
}
}
void
path_free_curve (PathCurve *curve)
{
PathSegment *tmp1, *tmp2;
if (curve)
{
tmp2 = curve->segments;
/* break closed curves */
if (tmp2 && tmp2->prev)
tmp2->prev->next = NULL;
while ((tmp1 = tmp2) != NULL)
{
tmp2 = tmp1->next;
path_free_segment (tmp1);
}
g_free(curve);
}
}
void
path_free_segment (PathSegment *segment)
{
if (segment)
{
/* Clear the active flag to keep path_tool->single_active_segment
* consistent */
path_set_flags (NULL, segment->parent->parent,
segment->parent, segment, 0, SEGMENT_ACTIVE);
path_curve_cleanup_segment(segment);
g_free (segment);
}
}
void
path_delete_curve (PathCurve *curve)
{
if (curve)
{
if (curve->next)
curve->next->prev = curve->prev;
if (curve->prev)
curve->prev->next = curve->next;
if (curve == curve->parent->curves) {
curve->parent->curves = curve->next;
}
path_free_curve (curve);
}
}
void
path_delete_segment (PathSegment *segment)
{
if (segment)
{
if (segment->next)
segment->next->prev = segment->prev;
if (segment->prev)
segment->prev->next = segment->next;
/* If the remaining curve is closed and has a
* single point only, open it.
*/
if (segment->next == segment->prev && segment->next)
segment->next->next = segment->next->prev = NULL;
if (segment == segment->parent->segments)
segment->parent->segments = segment->next;
if (segment->parent->segments == NULL)
path_delete_curve (segment->parent);
path_free_segment (segment);
/*
* here we have to update the surrounding segments
*/
/* XXX: Please add path_curve_update_segment here */
}
}
/*
* A function to determine, which object is hit by the cursor
*/
gint
path_tool_cursor_position (NPath *path,
gdouble x,
gdouble y,
gint halfwidth,
gint halfheight,
NPath **pathP,
PathCurve **curveP,
PathSegment **segmentP,
gdouble *positionP,
gint *handle_idP)
{
gdouble pos;
gint handle_id;
if (path_tool_on_anchors (path, x, y, halfwidth, pathP, curveP, segmentP))
return ON_ANCHOR;
handle_id = path_tool_on_handles (path, x, y, halfwidth, pathP, curveP, segmentP);
if (handle_id) {
if (handle_idP) (*handle_idP) = handle_id;
return ON_HANDLE;
}
pos = path_tool_on_curve (path, x, y, halfwidth, pathP, curveP, segmentP);
if (pos >= 0 && pos <= 1) {
if (positionP) (*positionP) = pos;
return ON_CURVE;
}
return ON_CANVAS;
}
#if 0
/**************************************************************
* The click-callbacks for the tool
*/
void
path_tool_button_press (Tool *tool,
GdkEventButton *bevent,
GimpDisplay *gdisp)
{
PathTool * path_tool;
gint grab_pointer=0;
gint x, y, halfwidth, dummy;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_tool_button_press\n");
#endif
path_tool = (PathTool *) tool->private;
tool->gdisp = gdisp;
/* Transform window-coordinates to canvas-coordinates */
gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y, TRUE, 0);
#ifdef PATH_TOOL_DEBUG
g_printerr ("Clickcoordinates %d, %d\n",x,y);
#endif
path_tool->click_x = x;
path_tool->click_y = y;
path_tool->click_modifier = bevent->state;
/* get halfwidth in image coord */
gdisplay_untransform_coords (gdisp, bevent->x + PATH_TOOL_HALFWIDTH, 0, &halfwidth, &dummy, TRUE, 0);
halfwidth -= x;
path_tool->click_halfwidth = halfwidth;
if (!path_tool->cur_path->curves)
draw_core_start (path_tool->core, gdisp->canvas->window, tool);
/* determine point, where clicked,
* switch accordingly.
*/
path_tool->click_type = path_tool_cursor_position (tool, x, y, halfwidth,
&(path_tool->click_path),
&(path_tool->click_curve),
&(path_tool->click_segment),
&(path_tool->click_position),
&(path_tool->click_handle_id));
switch (path_tool->click_type)
{
case ON_CANVAS:
grab_pointer = path_tool_button_press_canvas(tool, bevent, gdisp);
break;
case ON_ANCHOR:
grab_pointer = path_tool_button_press_anchor(tool, bevent, gdisp);
break;
case ON_HANDLE:
grab_pointer = path_tool_button_press_handle(tool, bevent, gdisp);
break;
case ON_CURVE:
grab_pointer = path_tool_button_press_curve(tool, bevent, gdisp);
break;
default:
g_message("Huh? Whats happening here? (button_press_*)");
}
if (grab_pointer)
gdk_pointer_grab (gdisp->canvas->window, FALSE,
GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK,
NULL, NULL, bevent->time);
tool->state = ACTIVE;
}
gint
path_tool_button_press_anchor (Tool *tool,
GdkEventButton *bevent,
GimpDisplay *gdisp)
{
static guint32 last_click_time=0;
gboolean doubleclick=FALSE;
PathTool *path_tool = tool->private;
NPath * cur_path = path_tool->cur_path;
PathSegment *p_sas;
gint grab_pointer;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_tool_button_press_anchor:\n");
#endif
grab_pointer = 1;
if (!cur_path) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("Fatal error: No current Path\n");
#endif
return 0;
}
/*
* We have to determine, if this was a doubleclick for ourself, because
* disp_callback.c ignores the GDK_[23]BUTTON_EVENT's and adding them to
* the switch statement confuses some tools.
*/
if (bevent->time - last_click_time < 250) {
doubleclick=TRUE;
#ifdef PATH_TOOL_DEBUG
g_printerr ("Doppelclick!\n");
#endif
} else
doubleclick=FALSE;
last_click_time = bevent->time;
draw_core_pause (path_tool->core, tool);
/* The user pressed on an anchor:
* normally this activates this anchor
* + SHIFT toggles the activity of an anchor.
* if this anchor is at the end of an open curve and the other
* end is active, close the curve.
*
* Doubleclick (de)activates the whole curve (not Path!).
*/
p_sas = path_tool->single_active_segment;
#ifdef PATH_TOOL_DEBUG
g_printerr ("p_sas: %p\n", p_sas);
#endif
if (path_tool->click_modifier & GDK_SHIFT_MASK) {
if (path_tool->active_count == 1 && p_sas && p_sas != path_tool->click_segment &&
(p_sas->next == NULL || p_sas->prev == NULL) &&
(path_tool->click_segment->next == NULL || path_tool->click_segment->prev == NULL)) {
/*
* if this is the end of an open curve and the single active segment was another
* open end, connect those ends.
*/
path_join_curves (path_tool->click_segment, p_sas);
path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve,
NULL, 0, SEGMENT_ACTIVE);
}
if (doubleclick)
/*
* Doubleclick set the whole curve to the same state, depending on the
* state of the clicked anchor.
*/
if (path_tool->click_segment->flags & SEGMENT_ACTIVE)
path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve,
NULL, SEGMENT_ACTIVE, 0);
else
path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve,
NULL, 0, SEGMENT_ACTIVE);
else
/*
* Toggle the state of the clicked anchor.
*/
if (path_tool->click_segment->flags & SEGMENT_ACTIVE)
path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve,
path_tool->click_segment, 0, SEGMENT_ACTIVE);
else
path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve,
path_tool->click_segment, SEGMENT_ACTIVE, 0);
}
/*
* Delete anchors, when CONTROL is pressed
*/
else if (path_tool->click_modifier & GDK_CONTROL_MASK)
{
if (path_tool->click_segment->flags & SEGMENT_ACTIVE)
{
if (path_tool->click_segment->prev)
path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve,
path_tool->click_segment->prev, SEGMENT_ACTIVE, 0);
else if (path_tool->click_segment->next)
path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve,
path_tool->click_segment->next, SEGMENT_ACTIVE, 0);
}
path_delete_segment (path_tool->click_segment);
path_tool->click_segment = NULL;
/* Maybe CTRL-ALT Click should remove the whole curve? Or the active points? */
}
else if (!(path_tool->click_segment->flags & SEGMENT_ACTIVE))
{
path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE);
path_set_flags (path_tool, cur_path, path_tool->click_curve, path_tool->click_segment, SEGMENT_ACTIVE, 0);
}
draw_core_resume(path_tool->core, tool);
return grab_pointer;
}
gint
path_tool_button_press_handle (Tool *tool,
GdkEventButton *bevent,
GimpDisplay *gdisp)
{
static guint32 last_click_time=0;
gboolean doubleclick=FALSE;
PathTool *path_tool = tool->private;
NPath * cur_path = path_tool->cur_path;
gint grab_pointer;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_tool_button_press_handle:\n");
#endif
grab_pointer = 1;
if (!cur_path) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("Fatal error: No current Path\n");
#endif
return 0;
}
/*
* We have to determine, if this was a doubleclick for ourself, because
* disp_callback.c ignores the GDK_[23]BUTTON_EVENT's and adding them to
* the switch statement confuses some tools.
*/
if (bevent->time - last_click_time < 250) {
doubleclick=TRUE;
#ifdef PATH_TOOL_DEBUG
g_printerr ("Doppelclick!\n");
#endif
} else
doubleclick=FALSE;
last_click_time = bevent->time;
return grab_pointer;
}
gint
path_tool_button_press_canvas (Tool *tool,
GdkEventButton *bevent,
GimpDisplay *gdisp)
{
PathTool *path_tool = tool->private;
NPath * cur_path = path_tool->cur_path;
PathCurve * cur_curve;
PathSegment * cur_segment;
gint grab_pointer;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_tool_button_press_canvas:\n");
#endif
grab_pointer = 1;
if (!cur_path) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("Fatal error: No current Path\n");
#endif
return 0;
}
draw_core_pause (path_tool->core, tool);
if (path_tool->active_count == 1 && path_tool->single_active_segment != NULL
&& (path_tool->single_active_segment->prev == NULL || path_tool->single_active_segment->next == NULL)) {
cur_segment = path_tool->single_active_segment;
cur_curve = cur_segment->parent;
path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE);
if (cur_segment->next == NULL)
cur_curve->cur_segment = path_append_segment(cur_path, cur_curve, SEGMENT_BEZIER, path_tool->click_x, path_tool->click_y);
else
cur_curve->cur_segment = path_prepend_segment(cur_path, cur_curve, SEGMENT_BEZIER, path_tool->click_x, path_tool->click_y);
if (cur_curve->cur_segment) {
path_set_flags (path_tool, cur_path, cur_curve, cur_curve->cur_segment, SEGMENT_ACTIVE, 0);
}
} else {
if (path_tool->active_count == 0) {
path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE);
cur_path->cur_curve = path_add_curve(cur_path, path_tool->click_x, path_tool->click_y);
path_set_flags (path_tool, cur_path, cur_path->cur_curve, cur_path->cur_curve->segments, SEGMENT_ACTIVE, 0);
} else {
path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE);
}
}
draw_core_resume(path_tool->core, tool);
return 0;
}
gint
path_tool_button_press_curve (Tool *tool,
GdkEventButton *bevent,
GimpDisplay *gdisp)
{
PathTool *path_tool = tool->private;
NPath * cur_path = path_tool->cur_path;
PathSegment * cur_segment;
gint grab_pointer;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_tool_button_press_curve:\n");
#endif
grab_pointer = 1;
if (!cur_path) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("Fatal error: No current NPath\n");
#endif
return 0;
}
draw_core_pause (path_tool->core, tool);
if (path_tool->click_modifier & GDK_SHIFT_MASK) {
cur_segment = path_curve_insert_anchor (path_tool, path_tool->click_segment, path_tool->click_position);
path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE);
path_set_flags (path_tool, cur_path, path_tool->click_curve, cur_segment, SEGMENT_ACTIVE, 0);
path_tool->click_type = ON_ANCHOR;
path_tool->click_segment = cur_segment;
} else {
path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE);
path_set_flags (path_tool, cur_path, path_tool->click_curve, path_tool->click_segment, SEGMENT_ACTIVE, 0);
path_set_flags (path_tool, cur_path, path_tool->click_curve, path_tool->click_segment->next, SEGMENT_ACTIVE, 0);
}
draw_core_resume(path_tool->core, tool);
return 0;
}
void
path_tool_button_release (Tool *tool,
GdkEventButton *bevent,
GimpDisplay *gdisp)
{
PathTool * path_tool;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_tool_button_release\n");
#endif
path_tool = (PathTool *) tool->private;
path_tool->state &= ~PATH_TOOL_DRAG;
gdk_pointer_ungrab (bevent->time);
gdk_flush ();
}
/**************************************************************
* The motion-callbacks for the tool
*/
void
path_tool_motion (Tool *tool,
GdkEventMotion *mevent,
GimpDisplay *gdisp)
{
PathTool * path_tool;
if (gtk_events_pending()) return;
path_tool = (PathTool *) tool->private;
switch (path_tool->click_type) {
case ON_ANCHOR:
path_tool_motion_anchor (tool, mevent, gdisp);
break;
case ON_HANDLE:
path_tool_motion_handle (tool, mevent, gdisp);
break;
case ON_CURVE:
path_tool_motion_curve (tool, mevent, gdisp);
break;
default:
return;
}
}
void
path_tool_motion_anchor (Tool *tool,
GdkEventMotion *mevent,
GimpDisplay *gdisp)
{
PathTool * path_tool;
gdouble dx, dy, d;
gint x,y;
static gint dxsum = 0;
static gint dysum = 0;
path_tool = (PathTool *) tool->private;
/*
* Dont do anything, if the user clicked with pressed CONTROL-Key,
* because he deleted an anchor.
*/
if (path_tool->click_modifier & GDK_CONTROL_MASK)
return;
if (!(path_tool->state & PATH_TOOL_DRAG))
{
path_tool->state |= PATH_TOOL_DRAG;
dxsum = 0;
dysum = 0;
}
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, TRUE, 0);
dx = x - path_tool->click_x - dxsum;
dy = y - path_tool->click_y - dysum;
/* restrict to horizontal/vertical lines, if modifiers are pressed
* I'm not sure, if this is intuitive for the user. Esp. When moving
* an endpoint of an curve I'd expect, that the *line* is
* horiz/vertical - not the delta to the point, where the point was
* originally...
*/
if (mevent->state & GDK_MOD1_MASK)
{
if (mevent->state & GDK_CONTROL_MASK)
{
d = (fabs(dx) + fabs(dy)) / 2;
d = (fabs(x - path_tool->click_x) + fabs(y - path_tool->click_y)) / 2;
dx = ((x < path_tool->click_x) ? -d : d ) - dxsum;
dy = ((y < path_tool->click_y) ? -d : d ) - dysum;
}
else
dx = - dxsum;
}
else if (mevent->state & GDK_CONTROL_MASK)
dy = - dysum;
path_tool->draw |= PATH_TOOL_REDRAW_ACTIVE;
draw_core_pause(path_tool->core, tool);
path_offset_active (path_tool->cur_path, dx, dy);
dxsum += dx;
dysum += dy;
draw_core_resume (path_tool->core, tool);
path_tool->draw &= ~PATH_TOOL_REDRAW_ACTIVE;
}
void
path_tool_motion_handle (Tool *tool,
GdkEventMotion *mevent,
GimpDisplay *gdisp)
{
PathTool * path_tool;
gdouble dx, dy;
gint x,y;
static gint dxsum = 0;
static gint dysum = 0;
path_tool = (PathTool *) tool->private;
/*
* Dont do anything, if the user clicked with pressed CONTROL-Key,
* because he moved the handle to the anchor an anchor.
* XXX: Not yet! :-)
*/
if (path_tool->click_modifier & GDK_CONTROL_MASK)
return;
if (!(path_tool->state & PATH_TOOL_DRAG))
{
path_tool->state |= PATH_TOOL_DRAG;
dxsum = 0;
dysum = 0;
}
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, TRUE, 0);
dx = x - path_tool->click_x - dxsum;
dy = y - path_tool->click_y - dysum;
path_tool->draw |= PATH_TOOL_REDRAW_ACTIVE;
draw_core_pause(path_tool->core, tool);
path_curve_drag_handle (path_tool, path_tool->click_segment, dx, dy, path_tool->click_handle_id);
dxsum += dx;
dysum += dy;
draw_core_resume (path_tool->core, tool);
path_tool->draw &= ~PATH_TOOL_REDRAW_ACTIVE;
}
void
path_tool_motion_curve (Tool *tool,
GdkEventMotion *mevent,
GimpDisplay *gdisp)
{
PathTool * path_tool;
gdouble dx, dy;
gint x,y;
static gint dxsum = 0;
static gint dysum = 0;
path_tool = (PathTool *) tool->private;
if (!(path_tool->state & PATH_TOOL_DRAG))
{
path_tool->state |= PATH_TOOL_DRAG;
dxsum = 0;
dysum = 0;
}
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, TRUE, 0);
dx = x - path_tool->click_x - dxsum;
dy = y - path_tool->click_y - dysum;
path_tool->draw |= PATH_TOOL_REDRAW_ACTIVE;
draw_core_pause(path_tool->core, tool);
path_curve_drag_segment (path_tool, path_tool->click_segment, path_tool->click_position, dx, dy);
dxsum += dx;
dysum += dy;
draw_core_resume (path_tool->core, tool);
path_tool->draw &= ~PATH_TOOL_REDRAW_ACTIVE;
}
void
path_tool_cursor_update (Tool *tool,
GdkEventMotion *mevent,
GimpDisplay *gdisp)
{
PathTool *path_tool;
gint x, y, halfwidth, dummy, cursor_location;
#ifdef PATH_TOOL_DEBUG
/* g_printerr ("path_tool_cursor_update\n");
*/
#endif
path_tool = (PathTool *) tool->private;
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, TRUE, 0);
/* get halfwidth in image coord */
gdisplay_untransform_coords (gdisp, mevent->x + PATH_TOOL_HALFWIDTH, 0, &halfwidth, &dummy, TRUE, 0);
halfwidth -= x;
cursor_location = path_tool_cursor_position (tool, x, y, halfwidth, NULL, NULL, NULL, NULL, NULL);
switch (cursor_location) {
case ON_CANVAS:
gdisplay_install_tool_cursor (gdisp, GIMP_MOUSE1AP_CURSOR);
break;
case ON_ANCHOR:
gdisplay_install_tool_cursor (gdisp, GDK_FLEUR);
break;
case ON_HANDLE:
gdisplay_install_tool_cursor (gdisp, GDK_CROSSHAIR);
break;
case ON_CURVE:
gdisplay_install_tool_cursor (gdisp, GDK_CROSSHAIR);
break;
default:
gdisplay_install_tool_cursor (gdisp, GDK_QUESTION_ARROW);
break;
}
}
/**************************************************************
* Tool-control functions
*/
void
path_tool_control (Tool *tool,
ToolAction action,
GimpDisplay *gdisp)
{
PathTool * path_tool;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_tool_control\n");
#endif
path_tool = (PathTool *) tool->private;
switch (action)
{
case PAUSE:
draw_core_pause (path_tool->core, tool);
break;
case RESUME:
draw_core_resume (path_tool->core, tool);
break;
case HALT:
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_tool_control: HALT\n");
#endif
draw_core_stop (path_tool->core, tool);
tool->state = INACTIVE;
break;
default:
break;
}
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_tool_control: end\n");
#endif
}
Tool *
tools_new_path_tool (void)
{
Tool * tool;
PathTool * private;
/* The tool options */
if (! path_options)
{
path_options = tool_options_new (_("Path Tool"));
tools_register (PATH_TOOL, (GimpToolOptions *) path_options);
}
tool = tools_new_tool (PATH_TOOL);
private = g_new0 (PathTool, 1);
private->click_type = ON_CANVAS;
private->click_x = 0;
private->click_y = 0;
private->click_halfwidth = 0;
private->click_modifier = 0;
private->click_path = NULL;
private->click_curve = NULL;
private->click_segment = NULL;
private->click_position = -1;
private->active_count = 0;
private->single_active_segment = NULL;
private->state = 0;
private->draw = PATH_TOOL_REDRAW_ALL;
private->core = draw_core_new (path_tool_draw);
private->cur_path = g_new0(NPath, 1);
private->scanlines = NULL;
tool->private = (void *) private;
tool->button_press_func = path_tool_button_press;
tool->button_release_func = path_tool_button_release;
tool->motion_func = path_tool_motion;
tool->cursor_update_func = path_tool_cursor_update;
tool->control_func = path_tool_control;
private->cur_path->curves = NULL;
private->cur_path->cur_curve = NULL;
private->cur_path->name = g_string_new("Path 0");
private->cur_path->state = 0;
private->cur_path->path_tool = private;
return tool;
}
void
tools_free_path_tool (Tool *tool)
{
GimpDisplay * gdisp;
PathTool * path_tool;
#ifdef PATH_TOOL_DEBUG
g_printerr ("tools_free_path_tool start\n");
#endif
path_tool = (PathTool *) tool->private;
gdisp = tool->gdisp;
if (tool->state == ACTIVE)
{
draw_core_stop (path_tool->core, tool);
}
path_free_path (path_tool->cur_path);
draw_core_free (path_tool->core);
g_free (path_tool);
#ifdef PATH_TOOL_DEBUG
g_printerr ("tools_free_path_tool end\n");
#endif
}
#endif
/**************************************************************
* Set of function to determine, if the click was on a segment
*/
typedef struct {
NPath *path;
PathCurve *curve;
PathSegment *segment;
gint testx;
gint testy;
gint halfwidth;
gint distance;
gdouble position;
gboolean found;
} Path_on_curve_type;
/* This is a CurveTraverseFunc */
void
path_tool_on_curve_helper (NPath *path,
PathCurve *curve,
PathSegment *segment,
gpointer ptr)
{
gint distance;
gdouble position;
Path_on_curve_type *data = (Path_on_curve_type *) ptr;
if (segment && segment->next && data && data->distance > 0)
{
position = path_curve_on_segment (segment, data->testx, data->testy, data->halfwidth, &distance);
if (position >= 0 && distance < data->distance )
{
data->path = path;
data->curve = curve;
data->segment = segment;
data->distance = distance;
data->position = position;
data->found = TRUE;
}
}
}
gdouble
path_tool_on_curve (NPath *path,
gint x,
gint y,
gint halfwidth,
NPath **ret_pathP,
PathCurve **ret_curveP,
PathSegment **ret_segmentP)
{
Path_on_curve_type *data = g_new (Path_on_curve_type, 1);
gdouble position;
data->path = path;
data->curve = NULL;
data->segment = NULL;
data->testx = x;
data->testy = y;
data->halfwidth = halfwidth;
data->distance = halfwidth * halfwidth + 1;
data->position = -1;
data->found = FALSE;
path_traverse_path (path, NULL, path_tool_on_curve_helper, NULL, data);
if (ret_pathP) *ret_pathP = data->path;
if (ret_curveP) *ret_curveP = data->curve;
if (ret_segmentP) *ret_segmentP = data->segment;
position = data->position;
g_free(data);
return position;
}
/**************************************************************
* Set of function to determine, if the click was on an anchor
*/
typedef struct {
NPath *path;
PathCurve *curve;
PathSegment *segment;
gdouble testx;
gdouble testy;
gint distance;
gboolean found;
} Path_on_anchors_type;
/* This is a CurveTraverseFunc */
void
path_tool_on_anchors_helper (NPath *path,
PathCurve *curve,
PathSegment *segment,
gpointer ptr)
{
gint distance;
Path_on_anchors_type *data = (Path_on_anchors_type *) ptr;
if (segment && data && data->distance > 0)
{
distance = ((data->testx - segment->x) * (data->testx - segment->x) +
(data->testy - segment->y) * (data->testy - segment->y));
if ( distance < data->distance )
{
data->path = path;
data->curve = curve;
data->segment = segment;
data->distance = distance;
data->found = TRUE;
}
}
}
gboolean
path_tool_on_anchors (NPath *path,
gdouble x,
gdouble y,
gint halfwidth,
NPath **ret_pathP,
PathCurve **ret_curveP,
PathSegment **ret_segmentP)
{
Path_on_anchors_type *data = g_new (Path_on_anchors_type, 1);
gboolean ret_found;
data->path = path;
data->curve = NULL;
data->segment = NULL;
data->testx = x;
data->testy = y;
data->distance = halfwidth * halfwidth + 1;
data->found = FALSE;
path_traverse_path (path, NULL, path_tool_on_anchors_helper, NULL, data);
if (ret_pathP) *ret_pathP = data->path;
if (ret_curveP) *ret_curveP = data->curve;
if (ret_segmentP) *ret_segmentP = data->segment;
ret_found = data->found;
g_free(data);
return ret_found;
}
/**************************************************************
* Set of function to determine, if the click was on an handle
*/
typedef struct {
NPath *path;
PathCurve *curve;
PathSegment *segment;
gdouble testx;
gdouble testy;
gint halfwidth;
gint handle_id;
gboolean found;
} Path_on_handles_type;
/* This is a CurveTraverseFunc */
void
path_tool_on_handles_helper (NPath *path,
PathCurve *curve,
PathSegment *segment,
gpointer ptr)
{
Path_on_handles_type *data = (Path_on_handles_type *) ptr;
gint handle;
if (segment && data && !data->found)
{
handle = path_curve_on_handle (segment, data->testx, data->testy,
data->halfwidth);
if (handle)
{
data->path = path;
data->curve = curve;
data->segment = segment;
data->handle_id = handle;
data->found = TRUE;
}
}
}
gint
path_tool_on_handles (NPath *path,
gdouble x,
gdouble y,
gint halfwidth,
NPath **ret_pathP,
PathCurve **ret_curveP,
PathSegment **ret_segmentP)
{
Path_on_handles_type *data = g_new (Path_on_handles_type, 1);
gint handle_ret;
data->path = path;
data->curve = NULL;
data->segment = NULL;
data->testx = x;
data->testy = y;
data->halfwidth = halfwidth;
data->handle_id = 0;
data->found = FALSE;
path_traverse_path (path, NULL, path_tool_on_handles_helper, NULL, data);
if (ret_pathP) *ret_pathP = data->path;
if (ret_curveP) *ret_curveP = data->curve;
if (ret_segmentP) *ret_segmentP = data->segment;
handle_ret = data->handle_id;
g_free(data);
return handle_ret;
}
/**************************************************************
* Set of function to offset all active anchors
*/
typedef struct {
gdouble dx;
gdouble dy;
} Path_offset_active_type;
/* This is a CurveTraverseFunc */
void
path_offset_active_helper (NPath *path,
PathCurve *curve,
PathSegment *segment,
gpointer ptr)
{
Path_offset_active_type *data = (Path_offset_active_type *) ptr;
if (segment && data && (segment->flags & SEGMENT_ACTIVE)) {
segment->x += data->dx;
segment->y += data->dy;
}
/* XXX: Do a segment_update here! */
}
void
path_offset_active (NPath *path,
gdouble dx,
gdouble dy)
{
Path_offset_active_type *data = g_new (Path_offset_active_type, 1);
data->dx = dx;
data->dy = dy;
if (path)
path_traverse_path (path, NULL, path_offset_active_helper, NULL, data);
g_free(data);
}
/**************************************************************
* Set of function to set the state of all anchors
*/
typedef struct {
guint32 bits_set;
guint32 bits_clear;
GimpPathTool *path_tool;
} Path_set_flags_type;
/* This is a CurveTraverseFunc */
void
path_set_flags_helper (NPath *path,
PathCurve *curve,
PathSegment *segment,
gpointer ptr)
{
Path_set_flags_type *tmp = (Path_set_flags_type *) ptr;
guint32 oldflags;
guint tmp_uint;
if (segment) {
oldflags = segment->flags;
segment->flags &= ~(tmp->bits_clear);
segment->flags |= tmp->bits_set;
/*
* Some black magic: We try to remember, which is the single active
* segment. We count, how many segments are active (in
* path_tool->active_count) and XOR path_tool->single_active_segment
* every time we select or deselect an anchor. So if exactly one anchor
* is active, path_tool->single_active_segment points to it.
*/
/* If SEGMENT_ACTIVE state has changed change the PathTool data
* accordingly.
*/
if (((segment->flags ^ oldflags) & SEGMENT_ACTIVE) && tmp && tmp->path_tool) {
if (segment->flags & SEGMENT_ACTIVE)
tmp->path_tool->active_count++;
else
tmp->path_tool->active_count--;
/* Does this work on all (16|32|64)-bit Machines? */
tmp_uint = GPOINTER_TO_UINT(tmp->path_tool->single_active_segment);
tmp_uint ^= GPOINTER_TO_UINT(segment);
tmp->path_tool->single_active_segment = GUINT_TO_POINTER(tmp_uint);
}
}
}
void
path_set_flags (GimpPathTool *path_tool,
NPath *path,
PathCurve *curve,
PathSegment *segment,
guint32 bits_set,
guint32 bits_clear)
{
Path_set_flags_type *tmp = g_new (Path_set_flags_type, 1);
tmp->bits_set=bits_set;
tmp->bits_clear=bits_clear;
tmp->path_tool = path_tool;
if (segment)
path_set_flags_helper (path, curve, segment, tmp);
else if (curve)
path_traverse_curve (path, curve, path_set_flags_helper, NULL, tmp);
else if (path)
path_traverse_path (path, NULL, path_set_flags_helper, NULL, tmp);
g_free (tmp);
}
/**************************************************************
* Set of functions to draw the segments to the window
*/
/* This is a CurveTraverseFunc */
void
path_tool_draw_helper (NPath *path,
PathCurve *curve,
PathSegment *segment,
gpointer tool_ptr)
{
#if 0
Tool * tool = (Tool *) tool_ptr;
GimpDisplay * gdisp;
PathTool * path_tool;
DrawCore * core;
gint x1, y1;
gboolean draw = TRUE;
if (!tool) {
#ifdef PATH_TOOL_DEBUG
g_printerr ("Fatal Error: path_tool_draw_segment called without valid tool *\n");
#endif
return;
}
gdisp = tool->gdisp;
path_tool = (PathTool *) tool->private;
core = path_tool->core;
if (path_tool->draw & PATH_TOOL_REDRAW_ACTIVE)
draw = (segment->flags & SEGMENT_ACTIVE || (segment->next && segment->next->flags & SEGMENT_ACTIVE));
if (segment && draw)
{
gdisplay_transform_coords (gdisp, (gint) segment->x, (gint) segment->y, &x1, &y1, FALSE);
if (segment->flags & SEGMENT_ACTIVE)
gdk_draw_arc (core->win, core->gc, 0,
x1 - PATH_TOOL_HALFWIDTH, y1 - PATH_TOOL_HALFWIDTH,
PATH_TOOL_WIDTH, PATH_TOOL_WIDTH, 0, 23040);
else
gdk_draw_arc (core->win, core->gc, 1,
x1 - PATH_TOOL_HALFWIDTH, y1 - PATH_TOOL_HALFWIDTH,
PATH_TOOL_WIDTH, PATH_TOOL_WIDTH, 0, 23040);
path_curve_draw_handles (tool, segment);
if (segment->next)
{
path_curve_draw_segment (tool, segment);
}
}
#ifdef PATH_TOOL_DEBUG
else if (!segment)
g_printerr ("path_tool_draw_segment: no segment to draw\n");
#endif
#endif
}
#if 0
void
path_tool_draw (Tool *tool)
{
GimpDisplay * gdisp;
NPath * cur_path;
PathTool * path_tool;
#ifdef PATH_TOOL_DEBUG
g_printerr ("path_tool_draw\n");
#endif
gdisp = tool->gdisp;
path_tool = tool->private;
cur_path = path_tool->cur_path;
path_traverse_path (cur_path, NULL, path_tool_draw_helper, NULL, tool);
#ifdef PATH_TOOL_DEBUG
/* g_printerr ("path_tool_draw end.\n");
*/
#endif
}
#endif