applied a patch from Simon.

--Sven
This commit is contained in:
Sven Neumann 1999-09-15 10:54:25 +00:00
parent e422b59e59
commit e56ff58b04
8 changed files with 1142 additions and 158 deletions

View File

@ -1,3 +1,16 @@
Wed Sep 15 12:43:39 MEST 1999 Simon Budig <Simon.Budig@unix-ag.org>
* app/tools.c
* app/pixmaps2.h
* app/path_tool.c
* app/path_toolP.h
Own pixmap for the Path Tool. It is now possible to connect
open ends of curves. Simply activate one end and shift click
on the other. Control-Click deletes points.
Still no bezier/painting functions...
1999-09-14 Tor Lillqvist <tml@iki.fi>
* app/brush_select.c: Include config.h, guard inclusion of

View File

@ -29,6 +29,8 @@
* segments between two anchors.
*/
#undef PATH_TOOL_DEBUG
#include <math.h>
/* #include "appenv.h"
*/
@ -38,7 +40,9 @@
#include "path_tool.h"
#include "path_toolP.h"
#ifdef PATH_TOOL_DEBUG
#include <stdio.h>
#endif PATH_TOOL_DEBUG
#include "libgimp/gimpintl.h"
@ -68,15 +72,18 @@ static gdouble path_locate_point (Path *, PathCurve **, PathSegment *
/* Tools to manipulate paths, curves, segments */
static PathCurve * path_add_curve (Path *, gint, gint);
static inline PathSegment * path_append_segment (Path *, PathCurve *, SegmentType, gint, gint);
static PathSegment * path_append_segment (Path *, PathCurve *, SegmentType, gint, gint);
static PathSegment * path_prepend_segment (Path *, PathCurve *, SegmentType, gint, gint);
static PathSegment * path_split_segment (PathSegment *, gdouble);
static void path_join_curves (PathSegment *, PathSegment *);
static void path_flip_curve (PathCurve *);
static void path_free_path (Path *);
static void path_free_curve (PathCurve *);
static void path_free_segment (PathSegment *);
static void path_delete_segment (PathSegment *);
static void path_print (Path *);
static void path_offset_active (Path *, gdouble, gdouble);
static void path_clear_active (Path *, PathCurve *, PathSegment *);
static void path_set_flags (PathTool *, Path *, PathCurve *, PathSegment *, guint32, guint32);
/* High level image-manipulation functions */
@ -116,8 +123,9 @@ static ToolOptions *path_options = NULL;
*/
/*
* 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
* 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
@ -176,7 +184,9 @@ path_traverse_curve (Path *path, PathCurve *curve, CurveTraverseFunc curvefunc,
static void
path_traverse_segment (Path *path, PathCurve *curve, PathSegment *segment, SegmentTraverseFunc function, gpointer data)
{
#ifdef PATH_TOOL_DEBUG
fprintf(stderr, "path_traverse_segment\n");
#endif PATH_TOOL_DEBUG
/* Something like:
* for i = 1 to subsamples {
@ -190,34 +200,48 @@ path_traverse_segment (Path *path, PathCurve *curve, PathSegment *segment, Segme
* Helper functions for manipulating the data-structures:
*/
static PathCurve * path_add_curve (Path * cur_path, gint x, gint y)
static PathCurve *path_add_curve (Path * cur_path, gint x, gint y)
{
PathCurve * tmp = cur_path->curves;
PathCurve * new_curve;
PathCurve * new_curve = NULL;
#ifdef PATH_TOOL_DEBUG
fprintf(stderr, "path_add_curve\n");
#endif PATH_TOOL_DEBUG
new_curve = g_new (PathCurve, 1);
if (cur_path) {
new_curve = g_new (PathCurve, 1);
new_curve->next = tmp;
new_curve->prev = NULL;
new_curve->cur_segment = NULL;
new_curve->segments = NULL;
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;
if (tmp) tmp->prev = new_curve;
cur_path->curves = cur_path->cur_curve = new_curve;
cur_path->curves = cur_path->cur_curve = new_curve;
new_curve->segments = path_append_segment (cur_path, new_curve, SEGMENT_LINE, x, y);
new_curve->segments = path_prepend_segment (cur_path, new_curve, SEGMENT_LINE, 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;
}
static inline PathSegment * path_append_segment (Path * cur_path, PathCurve * cur_curve, SegmentType type, gint x, gint y)
static PathSegment * path_append_segment (Path * 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) {
@ -230,32 +254,239 @@ static inline PathSegment * path_append_segment (Path * cur_path, PathCurve * c
new_segment->type = type;
new_segment->x = x;
new_segment->y = y;
new_segment->flags = SEGMENT_ACTIVE;
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;
}
#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;
}
/* static PathSegment * path_prepend_segment (Path *, PathCurve *, gint, gint);
* static PathSegment * path_split_segment (PathSegment *, gdouble);
static PathSegment * path_prepend_segment (Path * 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;
}
#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;
}
/* static PathSegment * path_split_segment (PathSegment *, gdouble);
*/
/*
* Join two arbitrary endpoints and free the parent from the second
* segment, if it differs from the first parents.
*/
static 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;
}
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;
}
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;
}
#ifdef PATH_TOOL_DEBUG
fprintf (stderr, "Cant join these curves yet...\n");
return;
#endif PATH_TOOL_DEBUG
}
/*
* This function reverses the order of the anchors. This is
* necessary for some joining operations.
*/
static 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;
}
tmp = tmp->next;
}
}
static void
path_free_path (Path * path)
{
@ -283,13 +514,17 @@ path_free_curve (PathCurve *curve)
if (curve)
{
tmp2 = curve->segments;
g_free(curve);
/* 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);
}
}
@ -298,12 +533,62 @@ path_free_segment (PathSegment *segment)
{
if (segment)
{
/* Clear the active flag to keep path_tool->single_active_segment consistent */
path_set_flags (segment->parent->parent->path_tool, segment->parent->parent,
segment->parent, segment, 0, SEGMENT_ACTIVE);
if (segment->data)
g_free(segment->data);
g_free (segment);
}
}
static 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);
}
}
static 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
*/
}
}
/*
@ -336,7 +621,9 @@ path_tool_button_press (Tool *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
gdisp = (GDisplay *) gdisp_ptr;
path_tool = (PathTool *) tool->private;
@ -344,7 +631,9 @@ path_tool_button_press (Tool *tool,
/* 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;
@ -395,20 +684,42 @@ 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;
Path * 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:
@ -416,15 +727,75 @@ path_tool_button_press_anchor (Tool *tool,
* + 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!).
*/
if (path_tool->click_modifier & GDK_SHIFT_MASK)
path_tool->click_segment->flags ^= SEGMENT_ACTIVE;
else {
if (!(path_tool->click_segment->flags & SEGMENT_ACTIVE))
path_clear_active (cur_path, NULL, NULL);
path_tool->click_segment->flags |= SEGMENT_ACTIVE;
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;
}
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);
}
/* Action goes here */
@ -445,35 +816,47 @@ path_tool_button_press_canvas (Tool *tool,
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);
path_clear_active (cur_path, NULL, NULL);
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;
if (!(cur_curve = cur_path->cur_curve)) {
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_LINE, path_tool->click_x, path_tool->click_y);
else
cur_curve->cur_segment = path_prepend_segment(cur_path, cur_curve, SEGMENT_LINE, 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 {
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);
draw_core_resume(path_tool->core, tool);
return 0;
path_set_flags (path_tool, cur_path, cur_path->cur_curve, cur_path->cur_curve->segments, SEGMENT_ACTIVE, 0);
}
cur_curve->cur_segment = path_append_segment(cur_path, cur_curve, SEGMENT_LINE, path_tool->click_x, path_tool->click_y);
draw_core_resume(path_tool->core, tool);
return 0;
}
static void
path_tool_button_release (Tool *tool,
GdkEventButton *bevent,
@ -482,13 +865,14 @@ path_tool_button_release (Tool *tool,
GDisplay * gdisp;
PathTool * path_tool;
#ifdef PATH_TOOL_DEBUG
fprintf (stderr, "path_tool_button_release\n");
#endif PATH_TOOL_DEBUG
gdisp = (GDisplay *) gdisp_ptr;
path_tool = (PathTool *) tool->private;
path_tool->state &= ~PATH_TOOL_DRAG;
path_tool->state = 0;
gdk_pointer_ungrab (bevent->time);
gdk_flush ();
@ -507,6 +891,8 @@ path_tool_motion (Tool *tool,
GDisplay * gdisp;
PathTool * path_tool;
if (gtk_events_pending()) return;
gdisp = (GDisplay *) gdisp_ptr;
path_tool = (PathTool *) tool->private;
@ -526,39 +912,68 @@ path_tool_motion_anchor (Tool *tool,
GDisplay *gdisp)
{
PathTool * path_tool;
gdouble dx, dy;
gdouble dx, dy, d;
gint x,y;
static gint lastx = 0;
static gint lasty = 0;
if (gtk_events_pending()) return; /* Is this OK? I want to ignore just the motion-events... */
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;
lastx = path_tool->click_x;
lasty = path_tool->click_y;
dxsum = 0;
dysum = 0;
}
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, TRUE, 0);
dx = x - lastx;
dy = y - lasty;
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;
lastx = x;
lasty = y;
}
@ -571,8 +986,10 @@ path_tool_cursor_update (Tool *tool,
GDisplay *gdisp;
gint x, y, halfwidth, dummy, cursor_location;
#ifdef PATH_TOOL_DEBUG
/* fprintf (stderr, "path_tool_cursor_update\n");
*/
#endif PATH_TOOL_DEBUG
gdisp = (GDisplay *) gdisp_ptr;
path_tool = (PathTool *) tool->private;
@ -609,7 +1026,9 @@ path_tool_control (Tool *tool,
GDisplay * gdisp;
PathTool * path_tool;
#ifdef PATH_TOOL_DEBUG
fprintf (stderr, "path_tool_control\n");
#endif PATH_TOOL_DEBUG
gdisp = (GDisplay *) tool->gdisp_ptr;
path_tool = (PathTool *) tool->private;
@ -655,6 +1074,10 @@ tools_new_path_tool (void)
private->click_y = 0;
private->click_halfwidth = 0;
private->click_modifier = 0;
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);
@ -674,6 +1097,7 @@ tools_new_path_tool (void)
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;
}
@ -701,7 +1125,7 @@ tools_free_path_tool (Tool *tool)
/**************************************************************
* Set of tools to determine, if the click was on an anchor
* Set of function to determine, if the click was on an anchor
*/
typedef struct {
@ -767,7 +1191,7 @@ path_tool_on_anchors (Tool *tool, gint x, gint y, gint halfwidth, Path **ret_pat
/**************************************************************
* Set of tools to offset all active anchors
* Set of function to offset all active anchors
*/
typedef struct {
@ -802,34 +1226,70 @@ path_offset_active (Path *path, gdouble dx, gdouble dy)
/**************************************************************
* Set of tools to set the state of all anchors to inactive
* Set of function to set the state of all anchors to inactive
*/
typedef struct {
guint32 bits_set;
guint32 bits_clear;
PathTool *path_tool;
} Path_set_flags_type;
/* This is a CurveTraverseFunc */
static void
path_clear_active_helper (Path *path, PathCurve *curve, PathSegment *segment, gpointer ptr)
path_set_flags_helper (Path *path, PathCurve *curve, PathSegment *segment, gpointer ptr)
{
gint distance;
Path_set_flags_type *tmp = (Path_set_flags_type *) ptr;
guint32 oldflags;
if (segment) {
oldflags = segment->flags;
segment->flags &= ~(tmp->bits_clear);
segment->flags |= tmp->bits_set;
if (segment)
segment->flags &= ~SEGMENT_ACTIVE;
/*
* 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? */
GPOINTER_TO_UINT(tmp->path_tool->single_active_segment) ^= GPOINTER_TO_UINT(segment);
}
}
}
static void
path_clear_active (Path *path, PathCurve *curve, PathSegment *segment)
path_set_flags (PathTool *path_tool, Path *path, PathCurve *curve, PathSegment *segment, guint32 bits_set, guint32 bits_clear)
{
fprintf (stderr, "path_clear_active\n");
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_clear_active_helper (path, curve, segment, NULL);
path_set_flags_helper (path, curve, segment, tmp);
else if (curve)
path_traverse_curve (path, curve, path_clear_active_helper, NULL, NULL);
path_traverse_curve (path, curve, path_set_flags_helper, NULL, tmp);
else if (path)
path_traverse_path (path, NULL, path_clear_active_helper, NULL, NULL);
path_traverse_path (path, NULL, path_set_flags_helper, NULL, tmp);
g_free (tmp);
}
/**************************************************************
* Set of tools to draw the segments to the window
* Set of functions to draw the segments to the window
*/
/* This is a CurveTraverseFunc */
@ -840,10 +1300,13 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi
GDisplay * gdisp;
PathTool * path_tool;
DrawCore * core;
gint x1, y1, x2, y2, draw;
gint x1, y1, x2, y2;
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;
}
@ -852,15 +1315,8 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi
path_tool = (PathTool *) tool->private;
core = path_tool->core;
draw = 1;
if (path_tool->draw & PATH_TOOL_REDRAW_ACTIVE)
{
if (segment->flags & SEGMENT_ACTIVE || (segment->next && segment->next->flags & SEGMENT_ACTIVE))
draw=1;
else
draw=0;
}
draw = (segment->flags & SEGMENT_ACTIVE || (segment->next && segment->next->flags & SEGMENT_ACTIVE));
if (segment && draw)
{
@ -880,8 +1336,10 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi
gdk_draw_line (core->win, core->gc, x1, y1, x2, y2);
}
}
#ifdef PATH_TOOL_DEBUG
else if (!segment)
fprintf(stderr, "path_tool_draw_segment: no segment to draw\n");
#endif PATH_TOOL_DEBUG
}
static void
@ -892,11 +1350,14 @@ path_tool_draw (Tool *tool)
PathTool * path_tool;
PathCurve * cur_curve;
#ifdef PATH_TOOL_DEBUG
fprintf (stderr, "path_tool_draw\n");
#endif PATH_TOOL_DEBUG
gdisp = tool->gdisp_ptr;
path_tool = tool->private;
cur_path = path_tool->cur_path;
path_traverse_path (cur_path, NULL, path_tool_draw_helper, NULL, tool);
}

View File

@ -40,45 +40,46 @@ typedef enum { SEGMENT_LINE, SEGMENT_BEZIER } SegmentType;
typedef struct _path_segment PathSegment;
typedef struct _path_curve PathCurve;
typedef struct _path Path;
typedef struct _path_tool PathTool;
struct _path_segment
{
SegmentType type; /* What type of segment */
gdouble x, y; /* location of starting-point in image space */
gpointer data; /* Additional data, dependant of segment-type */
guint32 flags; /* Various Flags: Is the Segment active? */
PathCurve *parent; /* the parent Curve */
PathSegment *next; /* Next Segment or NULL */
PathSegment *prev; /* Previous Segment or NULL */
gpointer data; /* Additional data, dependant of segment-type */
};
typedef struct _path_curve PathCurve;
struct _path_curve
{
PathSegment * segments; /* The segments of the curve */
PathSegment * cur_segment; /* the current segment */
Path * parent; /* the parent Path */
PathCurve * next; /* Next Curve or NULL */
PathCurve * prev; /* Previous Curve or NULL */
};
typedef struct _path Path;
struct _path
{
PathCurve * curves; /* the curves */
PathCurve * cur_curve; /* the current curve */
GString * name; /* the name of the path */
guint32 state; /* is the path locked? */
PathTool * path_tool; /* The parent Path Tool */
};
typedef struct _path_tool PathTool;
struct _path_tool
{
gint click_pos; /* where did the user click? */
@ -90,6 +91,14 @@ struct _path_tool
PathCurve *click_curve; /* was the click? */
PathSegment *click_segment;
gint active_count; /* How many segments are active? */
/*
* WARNING: single_active_segment may contain non NULL Values
* which point to the nirvana. But they are important!
* The pointer is garantueed to be valid, when active_count==1
*/
PathSegment *single_active_segment; /* The only active segment */
gint state; /* state of tool */
gint draw; /* all or part */
DrawCore *core; /* Core drawing object */

View File

@ -1004,3 +1004,34 @@ static char *xinput_airbrush_bits [] =
"......................"
};
/* GIMP icon image format -- S. Kimball, P. Mattis */
/* Image name: path_tool */
#define path_tool_width 22
#define path_tool_height 22
static char *path_tool_bits [] =
{
"......................",
"......aaa.............",
"......ahae............",
"......aaae............",
".......ee..aaae.......",
"......a..aae..aae.....",
"......e.a.......ae....",
".....a.a.........aa...",
"......a.........aaaa..",
"....aa..........aaaae.",
".....a..a........aaee.",
"...aa....aa.......ee..",
"..aae....aaaa.........",
".ahha.....aaaa........",
".ahhae....aaa.........",
"..aaee.....a.a........",
"..aee.........a.......",
"..a...................",
"..a...................",
"..a...................",
"..a...................",
"......................"
};

View File

@ -520,7 +520,7 @@ ToolInfo tool_info[] =
26,
N_("/Tools/Path"),
"",
(char **) measure_bits,
(char **) path_tool_bits,
N_("Manipulate paths"),
"ContextHelp/path",
PATH_TOOL,

View File

@ -29,6 +29,8 @@
* segments between two anchors.
*/
#undef PATH_TOOL_DEBUG
#include <math.h>
/* #include "appenv.h"
*/
@ -38,7 +40,9 @@
#include "path_tool.h"
#include "path_toolP.h"
#ifdef PATH_TOOL_DEBUG
#include <stdio.h>
#endif PATH_TOOL_DEBUG
#include "libgimp/gimpintl.h"
@ -68,15 +72,18 @@ static gdouble path_locate_point (Path *, PathCurve **, PathSegment *
/* Tools to manipulate paths, curves, segments */
static PathCurve * path_add_curve (Path *, gint, gint);
static inline PathSegment * path_append_segment (Path *, PathCurve *, SegmentType, gint, gint);
static PathSegment * path_append_segment (Path *, PathCurve *, SegmentType, gint, gint);
static PathSegment * path_prepend_segment (Path *, PathCurve *, SegmentType, gint, gint);
static PathSegment * path_split_segment (PathSegment *, gdouble);
static void path_join_curves (PathSegment *, PathSegment *);
static void path_flip_curve (PathCurve *);
static void path_free_path (Path *);
static void path_free_curve (PathCurve *);
static void path_free_segment (PathSegment *);
static void path_delete_segment (PathSegment *);
static void path_print (Path *);
static void path_offset_active (Path *, gdouble, gdouble);
static void path_clear_active (Path *, PathCurve *, PathSegment *);
static void path_set_flags (PathTool *, Path *, PathCurve *, PathSegment *, guint32, guint32);
/* High level image-manipulation functions */
@ -116,8 +123,9 @@ static ToolOptions *path_options = NULL;
*/
/*
* 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
* 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
@ -176,7 +184,9 @@ path_traverse_curve (Path *path, PathCurve *curve, CurveTraverseFunc curvefunc,
static void
path_traverse_segment (Path *path, PathCurve *curve, PathSegment *segment, SegmentTraverseFunc function, gpointer data)
{
#ifdef PATH_TOOL_DEBUG
fprintf(stderr, "path_traverse_segment\n");
#endif PATH_TOOL_DEBUG
/* Something like:
* for i = 1 to subsamples {
@ -190,34 +200,48 @@ path_traverse_segment (Path *path, PathCurve *curve, PathSegment *segment, Segme
* Helper functions for manipulating the data-structures:
*/
static PathCurve * path_add_curve (Path * cur_path, gint x, gint y)
static PathCurve *path_add_curve (Path * cur_path, gint x, gint y)
{
PathCurve * tmp = cur_path->curves;
PathCurve * new_curve;
PathCurve * new_curve = NULL;
#ifdef PATH_TOOL_DEBUG
fprintf(stderr, "path_add_curve\n");
#endif PATH_TOOL_DEBUG
new_curve = g_new (PathCurve, 1);
if (cur_path) {
new_curve = g_new (PathCurve, 1);
new_curve->next = tmp;
new_curve->prev = NULL;
new_curve->cur_segment = NULL;
new_curve->segments = NULL;
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;
if (tmp) tmp->prev = new_curve;
cur_path->curves = cur_path->cur_curve = new_curve;
cur_path->curves = cur_path->cur_curve = new_curve;
new_curve->segments = path_append_segment (cur_path, new_curve, SEGMENT_LINE, x, y);
new_curve->segments = path_prepend_segment (cur_path, new_curve, SEGMENT_LINE, 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;
}
static inline PathSegment * path_append_segment (Path * cur_path, PathCurve * cur_curve, SegmentType type, gint x, gint y)
static PathSegment * path_append_segment (Path * 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) {
@ -230,32 +254,239 @@ static inline PathSegment * path_append_segment (Path * cur_path, PathCurve * c
new_segment->type = type;
new_segment->x = x;
new_segment->y = y;
new_segment->flags = SEGMENT_ACTIVE;
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;
}
#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;
}
/* static PathSegment * path_prepend_segment (Path *, PathCurve *, gint, gint);
* static PathSegment * path_split_segment (PathSegment *, gdouble);
static PathSegment * path_prepend_segment (Path * 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;
}
#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;
}
/* static PathSegment * path_split_segment (PathSegment *, gdouble);
*/
/*
* Join two arbitrary endpoints and free the parent from the second
* segment, if it differs from the first parents.
*/
static 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;
}
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;
}
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;
}
#ifdef PATH_TOOL_DEBUG
fprintf (stderr, "Cant join these curves yet...\n");
return;
#endif PATH_TOOL_DEBUG
}
/*
* This function reverses the order of the anchors. This is
* necessary for some joining operations.
*/
static 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;
}
tmp = tmp->next;
}
}
static void
path_free_path (Path * path)
{
@ -283,13 +514,17 @@ path_free_curve (PathCurve *curve)
if (curve)
{
tmp2 = curve->segments;
g_free(curve);
/* 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);
}
}
@ -298,12 +533,62 @@ path_free_segment (PathSegment *segment)
{
if (segment)
{
/* Clear the active flag to keep path_tool->single_active_segment consistent */
path_set_flags (segment->parent->parent->path_tool, segment->parent->parent,
segment->parent, segment, 0, SEGMENT_ACTIVE);
if (segment->data)
g_free(segment->data);
g_free (segment);
}
}
static 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);
}
}
static 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
*/
}
}
/*
@ -336,7 +621,9 @@ path_tool_button_press (Tool *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
gdisp = (GDisplay *) gdisp_ptr;
path_tool = (PathTool *) tool->private;
@ -344,7 +631,9 @@ path_tool_button_press (Tool *tool,
/* 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;
@ -395,20 +684,42 @@ 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;
Path * 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:
@ -416,15 +727,75 @@ path_tool_button_press_anchor (Tool *tool,
* + 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!).
*/
if (path_tool->click_modifier & GDK_SHIFT_MASK)
path_tool->click_segment->flags ^= SEGMENT_ACTIVE;
else {
if (!(path_tool->click_segment->flags & SEGMENT_ACTIVE))
path_clear_active (cur_path, NULL, NULL);
path_tool->click_segment->flags |= SEGMENT_ACTIVE;
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;
}
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);
}
/* Action goes here */
@ -445,35 +816,47 @@ path_tool_button_press_canvas (Tool *tool,
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);
path_clear_active (cur_path, NULL, NULL);
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;
if (!(cur_curve = cur_path->cur_curve)) {
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_LINE, path_tool->click_x, path_tool->click_y);
else
cur_curve->cur_segment = path_prepend_segment(cur_path, cur_curve, SEGMENT_LINE, 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 {
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);
draw_core_resume(path_tool->core, tool);
return 0;
path_set_flags (path_tool, cur_path, cur_path->cur_curve, cur_path->cur_curve->segments, SEGMENT_ACTIVE, 0);
}
cur_curve->cur_segment = path_append_segment(cur_path, cur_curve, SEGMENT_LINE, path_tool->click_x, path_tool->click_y);
draw_core_resume(path_tool->core, tool);
return 0;
}
static void
path_tool_button_release (Tool *tool,
GdkEventButton *bevent,
@ -482,13 +865,14 @@ path_tool_button_release (Tool *tool,
GDisplay * gdisp;
PathTool * path_tool;
#ifdef PATH_TOOL_DEBUG
fprintf (stderr, "path_tool_button_release\n");
#endif PATH_TOOL_DEBUG
gdisp = (GDisplay *) gdisp_ptr;
path_tool = (PathTool *) tool->private;
path_tool->state &= ~PATH_TOOL_DRAG;
path_tool->state = 0;
gdk_pointer_ungrab (bevent->time);
gdk_flush ();
@ -507,6 +891,8 @@ path_tool_motion (Tool *tool,
GDisplay * gdisp;
PathTool * path_tool;
if (gtk_events_pending()) return;
gdisp = (GDisplay *) gdisp_ptr;
path_tool = (PathTool *) tool->private;
@ -526,39 +912,68 @@ path_tool_motion_anchor (Tool *tool,
GDisplay *gdisp)
{
PathTool * path_tool;
gdouble dx, dy;
gdouble dx, dy, d;
gint x,y;
static gint lastx = 0;
static gint lasty = 0;
if (gtk_events_pending()) return; /* Is this OK? I want to ignore just the motion-events... */
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;
lastx = path_tool->click_x;
lasty = path_tool->click_y;
dxsum = 0;
dysum = 0;
}
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, TRUE, 0);
dx = x - lastx;
dy = y - lasty;
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;
lastx = x;
lasty = y;
}
@ -571,8 +986,10 @@ path_tool_cursor_update (Tool *tool,
GDisplay *gdisp;
gint x, y, halfwidth, dummy, cursor_location;
#ifdef PATH_TOOL_DEBUG
/* fprintf (stderr, "path_tool_cursor_update\n");
*/
#endif PATH_TOOL_DEBUG
gdisp = (GDisplay *) gdisp_ptr;
path_tool = (PathTool *) tool->private;
@ -609,7 +1026,9 @@ path_tool_control (Tool *tool,
GDisplay * gdisp;
PathTool * path_tool;
#ifdef PATH_TOOL_DEBUG
fprintf (stderr, "path_tool_control\n");
#endif PATH_TOOL_DEBUG
gdisp = (GDisplay *) tool->gdisp_ptr;
path_tool = (PathTool *) tool->private;
@ -655,6 +1074,10 @@ tools_new_path_tool (void)
private->click_y = 0;
private->click_halfwidth = 0;
private->click_modifier = 0;
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);
@ -674,6 +1097,7 @@ tools_new_path_tool (void)
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;
}
@ -701,7 +1125,7 @@ tools_free_path_tool (Tool *tool)
/**************************************************************
* Set of tools to determine, if the click was on an anchor
* Set of function to determine, if the click was on an anchor
*/
typedef struct {
@ -767,7 +1191,7 @@ path_tool_on_anchors (Tool *tool, gint x, gint y, gint halfwidth, Path **ret_pat
/**************************************************************
* Set of tools to offset all active anchors
* Set of function to offset all active anchors
*/
typedef struct {
@ -802,34 +1226,70 @@ path_offset_active (Path *path, gdouble dx, gdouble dy)
/**************************************************************
* Set of tools to set the state of all anchors to inactive
* Set of function to set the state of all anchors to inactive
*/
typedef struct {
guint32 bits_set;
guint32 bits_clear;
PathTool *path_tool;
} Path_set_flags_type;
/* This is a CurveTraverseFunc */
static void
path_clear_active_helper (Path *path, PathCurve *curve, PathSegment *segment, gpointer ptr)
path_set_flags_helper (Path *path, PathCurve *curve, PathSegment *segment, gpointer ptr)
{
gint distance;
Path_set_flags_type *tmp = (Path_set_flags_type *) ptr;
guint32 oldflags;
if (segment) {
oldflags = segment->flags;
segment->flags &= ~(tmp->bits_clear);
segment->flags |= tmp->bits_set;
if (segment)
segment->flags &= ~SEGMENT_ACTIVE;
/*
* 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? */
GPOINTER_TO_UINT(tmp->path_tool->single_active_segment) ^= GPOINTER_TO_UINT(segment);
}
}
}
static void
path_clear_active (Path *path, PathCurve *curve, PathSegment *segment)
path_set_flags (PathTool *path_tool, Path *path, PathCurve *curve, PathSegment *segment, guint32 bits_set, guint32 bits_clear)
{
fprintf (stderr, "path_clear_active\n");
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_clear_active_helper (path, curve, segment, NULL);
path_set_flags_helper (path, curve, segment, tmp);
else if (curve)
path_traverse_curve (path, curve, path_clear_active_helper, NULL, NULL);
path_traverse_curve (path, curve, path_set_flags_helper, NULL, tmp);
else if (path)
path_traverse_path (path, NULL, path_clear_active_helper, NULL, NULL);
path_traverse_path (path, NULL, path_set_flags_helper, NULL, tmp);
g_free (tmp);
}
/**************************************************************
* Set of tools to draw the segments to the window
* Set of functions to draw the segments to the window
*/
/* This is a CurveTraverseFunc */
@ -840,10 +1300,13 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi
GDisplay * gdisp;
PathTool * path_tool;
DrawCore * core;
gint x1, y1, x2, y2, draw;
gint x1, y1, x2, y2;
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;
}
@ -852,15 +1315,8 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi
path_tool = (PathTool *) tool->private;
core = path_tool->core;
draw = 1;
if (path_tool->draw & PATH_TOOL_REDRAW_ACTIVE)
{
if (segment->flags & SEGMENT_ACTIVE || (segment->next && segment->next->flags & SEGMENT_ACTIVE))
draw=1;
else
draw=0;
}
draw = (segment->flags & SEGMENT_ACTIVE || (segment->next && segment->next->flags & SEGMENT_ACTIVE));
if (segment && draw)
{
@ -880,8 +1336,10 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi
gdk_draw_line (core->win, core->gc, x1, y1, x2, y2);
}
}
#ifdef PATH_TOOL_DEBUG
else if (!segment)
fprintf(stderr, "path_tool_draw_segment: no segment to draw\n");
#endif PATH_TOOL_DEBUG
}
static void
@ -892,11 +1350,14 @@ path_tool_draw (Tool *tool)
PathTool * path_tool;
PathCurve * cur_curve;
#ifdef PATH_TOOL_DEBUG
fprintf (stderr, "path_tool_draw\n");
#endif PATH_TOOL_DEBUG
gdisp = tool->gdisp_ptr;
path_tool = tool->private;
cur_path = path_tool->cur_path;
path_traverse_path (cur_path, NULL, path_tool_draw_helper, NULL, tool);
}

View File

@ -40,45 +40,46 @@ typedef enum { SEGMENT_LINE, SEGMENT_BEZIER } SegmentType;
typedef struct _path_segment PathSegment;
typedef struct _path_curve PathCurve;
typedef struct _path Path;
typedef struct _path_tool PathTool;
struct _path_segment
{
SegmentType type; /* What type of segment */
gdouble x, y; /* location of starting-point in image space */
gpointer data; /* Additional data, dependant of segment-type */
guint32 flags; /* Various Flags: Is the Segment active? */
PathCurve *parent; /* the parent Curve */
PathSegment *next; /* Next Segment or NULL */
PathSegment *prev; /* Previous Segment or NULL */
gpointer data; /* Additional data, dependant of segment-type */
};
typedef struct _path_curve PathCurve;
struct _path_curve
{
PathSegment * segments; /* The segments of the curve */
PathSegment * cur_segment; /* the current segment */
Path * parent; /* the parent Path */
PathCurve * next; /* Next Curve or NULL */
PathCurve * prev; /* Previous Curve or NULL */
};
typedef struct _path Path;
struct _path
{
PathCurve * curves; /* the curves */
PathCurve * cur_curve; /* the current curve */
GString * name; /* the name of the path */
guint32 state; /* is the path locked? */
PathTool * path_tool; /* The parent Path Tool */
};
typedef struct _path_tool PathTool;
struct _path_tool
{
gint click_pos; /* where did the user click? */
@ -90,6 +91,14 @@ struct _path_tool
PathCurve *click_curve; /* was the click? */
PathSegment *click_segment;
gint active_count; /* How many segments are active? */
/*
* WARNING: single_active_segment may contain non NULL Values
* which point to the nirvana. But they are important!
* The pointer is garantueed to be valid, when active_count==1
*/
PathSegment *single_active_segment; /* The only active segment */
gint state; /* state of tool */
gint draw; /* all or part */
DrawCore *core; /* Core drawing object */

View File

@ -520,7 +520,7 @@ ToolInfo tool_info[] =
26,
N_("/Tools/Path"),
"",
(char **) measure_bits,
(char **) path_tool_bits,
N_("Manipulate paths"),
"ContextHelp/path",
PATH_TOOL,