
2001-04-07 Simon Budig <simon@gimp.org> * app/Makefile.am * app/apptypes.h * app/path_bezier.[ch] * app/path_curves.[ch] * app/pixmaps2.h * app/tools/Makefile.am * app/tools/gimpdrawtool.[ch] * app/tools/path_tool.[ch] * app/tools/path_toolP.h * app/tools/tools.c new files: * app/tools/gimppathtool.c * app/tools/gimppathtool.h Reactivated (at least partially) the old new path tool. It will undergo major restructuring. Especially the path data will become proper objects. This definitely is work in progress and totally unuseable now.
1921 lines
48 KiB
C
1921 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 <math.h>
|
|
|
|
|
|
#include "path_curves.h"
|
|
#include "config.h"
|
|
#include "libgimp/gimpintl.h"
|
|
*/
|
|
|
|
#include <glib.h>
|
|
#include <gdk/gdk.h>
|
|
#include "apptypes.h"
|
|
#include "path_curves.h"
|
|
#include "path_tool.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 *,
|
|
gint,
|
|
gint);
|
|
PathSegment * path_append_segment (NPath *,
|
|
PathCurve *,
|
|
SegmentType,
|
|
gint,
|
|
gint);
|
|
PathSegment * path_prepend_segment (NPath *,
|
|
PathCurve *,
|
|
SegmentType,
|
|
gint,
|
|
gint);
|
|
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 */
|
|
|
|
#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**);
|
|
gboolean path_tool_on_anchors (Tool *, gint, gint, gint,
|
|
NPath**, PathCurve**, PathSegment**);
|
|
gint path_tool_on_handles (Tool *, gint, gint, gint,
|
|
NPath **, PathCurve **, PathSegment **);
|
|
|
|
gint path_tool_button_press_canvas (Tool *, GdkEventButton *, GDisplay *);
|
|
gint path_tool_button_press_anchor (Tool *, GdkEventButton *, GDisplay *);
|
|
gint path_tool_button_press_handle (Tool *, GdkEventButton *, GDisplay *);
|
|
gint path_tool_button_press_curve (Tool *, GdkEventButton *, GDisplay *);
|
|
void path_tool_motion_anchor (Tool *, GdkEventMotion *, GDisplay *);
|
|
void path_tool_motion_handle (Tool *, GdkEventMotion *, GDisplay *);
|
|
void path_tool_motion_curve (Tool *, GdkEventMotion *, GDisplay *);
|
|
|
|
#endif
|
|
|
|
/* the path tool options */
|
|
static ToolOptions *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
|
|
fprintf(stderr, "path_traverse_segment\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
/* 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,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
PathCurve * tmp = cur_path->curves;
|
|
PathCurve * new_curve = NULL;
|
|
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf(stderr, "path_add_curve\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
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
|
|
fprintf (stderr, "Fatal Error: path_add_curve called without valid path\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
return new_curve;
|
|
}
|
|
|
|
|
|
PathSegment *
|
|
path_append_segment (NPath * cur_path,
|
|
PathCurve * cur_curve,
|
|
SegmentType type,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
PathSegment * tmp = cur_curve->segments;
|
|
PathSegment * new_segment = NULL;
|
|
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf(stderr, "path_append_segment\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
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
|
|
fprintf(stderr, "Fatal Error: path_append_segment called with a closed curve\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
}
|
|
#ifdef PATH_TOOL_DEBUG
|
|
else
|
|
fprintf(stderr, "Fatal Error: path_append_segment called without valid curve\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
return new_segment;
|
|
}
|
|
|
|
|
|
PathSegment *
|
|
path_prepend_segment (NPath * cur_path,
|
|
PathCurve * cur_curve,
|
|
SegmentType type,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
PathSegment * tmp = cur_curve->segments;
|
|
PathSegment * new_segment = NULL;
|
|
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf(stderr, "path_prepend_segment\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
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
|
|
fprintf(stderr, "Fatal Error: path_prepend_segment called with a closed curve\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
}
|
|
#ifdef PATH_TOOL_DEBUG
|
|
else
|
|
fprintf(stderr, "Fatal Error: path_prepend_segment called without valid curve\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
return new_segment;
|
|
}
|
|
|
|
PathSegment *
|
|
path_split_segment (PathSegment *segment,
|
|
gdouble position)
|
|
{
|
|
PathSegment * new_segment = NULL;
|
|
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf(stderr, "path_split_segment\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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
|
|
fprintf(stderr, "path_split_segment without valid segment\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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
|
|
fprintf(stderr, "Fatal Error: path_join_curves called with a closed segment\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
return;
|
|
}
|
|
if (segment1->parent == segment2->parent) {
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "Joining beginning and end of the same curve...\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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
|
|
fprintf (stderr, "Flipping second curve (next, next)...\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
path_flip_curve (segment2->parent);
|
|
/* segment2 = segment2->parent->segments;
|
|
*/
|
|
}
|
|
|
|
if (segment1->prev == NULL && segment2->prev == NULL) {
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "Flipping second curve (prev, prev)...\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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
|
|
fprintf (stderr, "Appending second to first curve...\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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
|
|
fprintf (stderr, "Prepending second to first curve...\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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
|
|
fprintf (stderr, "Cant join these curves yet...\nThis should not happen.");
|
|
return;
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
fprintf (stderr, "path_flip_curve: No curve o no segments to flip!\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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,
|
|
gint x,
|
|
gint y,
|
|
gint halfwidth,
|
|
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,
|
|
GDisplay *gdisp)
|
|
{
|
|
PathTool * path_tool;
|
|
gint grab_pointer=0;
|
|
gint x, y, halfwidth, dummy;
|
|
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "path_tool_button_press\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
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
|
|
fprintf(stderr, "Clickcoordinates %d, %d\n",x,y);
|
|
#endif PATH_TOOL_DEBUG
|
|
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,
|
|
GDisplay *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
|
|
fprintf(stderr, "path_tool_button_press_anchor:\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
grab_pointer = 1;
|
|
|
|
if (!cur_path) {
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "Fatal error: No current Path\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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
|
|
fprintf (stderr, "Doppelclick!\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
} 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
|
|
fprintf (stderr, "p_sas: %p\n", p_sas);
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
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,
|
|
GDisplay *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
|
|
fprintf(stderr, "path_tool_button_press_handle:\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
grab_pointer = 1;
|
|
|
|
if (!cur_path) {
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "Fatal error: No current Path\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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
|
|
fprintf (stderr, "Doppelclick!\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
} else
|
|
doubleclick=FALSE;
|
|
last_click_time = bevent->time;
|
|
|
|
return grab_pointer;
|
|
}
|
|
|
|
gint
|
|
path_tool_button_press_canvas (Tool *tool,
|
|
GdkEventButton *bevent,
|
|
GDisplay *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
|
|
fprintf(stderr, "path_tool_button_press_canvas:\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
grab_pointer = 1;
|
|
|
|
if (!cur_path) {
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "Fatal error: No current Path\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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,
|
|
GDisplay *gdisp)
|
|
{
|
|
PathTool *path_tool = tool->private;
|
|
|
|
NPath * cur_path = path_tool->cur_path;
|
|
PathSegment * cur_segment;
|
|
gint grab_pointer;
|
|
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf(stderr, "path_tool_button_press_curve:\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
grab_pointer = 1;
|
|
|
|
if (!cur_path) {
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "Fatal error: No current NPath\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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,
|
|
GDisplay *gdisp)
|
|
{
|
|
PathTool * path_tool;
|
|
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "path_tool_button_release\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
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,
|
|
GDisplay *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,
|
|
GDisplay *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,
|
|
GDisplay *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,
|
|
GDisplay *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,
|
|
GDisplay *gdisp)
|
|
{
|
|
PathTool *path_tool;
|
|
gint x, y, halfwidth, dummy, cursor_location;
|
|
|
|
#ifdef PATH_TOOL_DEBUG
|
|
/* fprintf (stderr, "path_tool_cursor_update\n");
|
|
*/
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
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,
|
|
GDisplay *gdisp)
|
|
{
|
|
PathTool * path_tool;
|
|
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "path_tool_control\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
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
|
|
fprintf (stderr, "path_tool_control: HALT\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
draw_core_stop (path_tool->core, tool);
|
|
tool->state = INACTIVE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "path_tool_control: end\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
}
|
|
|
|
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, (ToolOptions *) 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)
|
|
{
|
|
GDisplay * gdisp;
|
|
PathTool * path_tool;
|
|
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "tools_free_path_tool start\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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
|
|
fprintf (stderr, "tools_free_path_tool end\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
}
|
|
|
|
#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;
|
|
gint testx;
|
|
gint 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,
|
|
gint x,
|
|
gint 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;
|
|
gint testx;
|
|
gint 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,
|
|
gint x,
|
|
gint 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 0
|
|
/* 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);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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;
|
|
GDisplay * gdisp;
|
|
PathTool * path_tool;
|
|
DrawCore * core;
|
|
gint x1, y1;
|
|
gboolean draw = TRUE;
|
|
|
|
if (!tool) {
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "Fatal Error: path_tool_draw_segment called without valid tool *\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
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)
|
|
fprintf(stderr, "path_tool_draw_segment: no segment to draw\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
#endif
|
|
}
|
|
|
|
void
|
|
path_tool_draw (Tool *tool)
|
|
{
|
|
#if 0
|
|
GDisplay * gdisp;
|
|
NPath * cur_path;
|
|
PathTool * path_tool;
|
|
|
|
#ifdef PATH_TOOL_DEBUG
|
|
fprintf (stderr, "path_tool_draw\n");
|
|
#endif PATH_TOOL_DEBUG
|
|
|
|
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
|
|
/* fprintf (stderr, "path_tool_draw end.\n");
|
|
*/
|
|
#endif PATH_TOOL_DEBUG
|
|
#endif
|
|
|
|
}
|
|
|
|
|