Generalized to allow a variable number of lines for snapping, and

2008-01-14  Martin Nordholts  <martinn@svn.gnome.org>

	* app/tools/tools-utils.[ch]
	(gimp_tool_motion_constrain): Generalized to allow a variable
	number of lines for snapping, and rewritten to make snapping
	happen more intuitively; snap the shortest distance rather than
	only horizontally or vertically.
	(gimp_tool_utils_point_to_line_distance): New helper function.

	* app/tools/gimpblendtool.c
	* app/tools/gimpmeasuretool.c
	* app/tools/gimppainttool.c: Adjust to the new function signature.

svn path=/trunk/; revision=24609
This commit is contained in:
Martin Nordholts 2008-01-14 21:23:02 +00:00 committed by Martin Nordholts
parent f8808d8afc
commit 4ef8797faa
6 changed files with 98 additions and 48 deletions

View File

@ -1,3 +1,16 @@
2008-01-14 Martin Nordholts <martinn@svn.gnome.org>
* app/tools/tools-utils.[ch]
(gimp_tool_motion_constrain): Generalized to allow a variable
number of lines for snapping, and rewritten to make snapping
happen more intuitively; snap the shortest distance rather than
only horizontally or vertically.
(gimp_tool_utils_point_to_line_distance): New helper function.
* app/tools/gimpblendtool.c
* app/tools/gimpmeasuretool.c
* app/tools/gimppainttool.c: Adjust to the new function signature.
2008-01-14 Michael Natterer <mitch@gimp.org>
* app/gegl/Makefile.am

View File

@ -46,7 +46,8 @@
#include "gimp-intl.h"
#define TARGET_SIZE 15
#define TARGET_SIZE 15
#define N_SNAP_LINES 12
/* local function prototypes */
@ -287,7 +288,8 @@ gimp_blend_tool_motion (GimpTool *tool,
if (state & GDK_CONTROL_MASK)
{
gimp_tool_motion_constrain (blend_tool->start_x, blend_tool->start_y,
&blend_tool->end_x, &blend_tool->end_y);
&blend_tool->end_x, &blend_tool->end_y,
N_SNAP_LINES);
}
gimp_tool_pop_status (tool, display);
@ -319,7 +321,8 @@ gimp_blend_tool_active_modifier_key (GimpTool *tool,
if (press)
{
gimp_tool_motion_constrain (blend_tool->start_x, blend_tool->start_y,
&blend_tool->end_x, &blend_tool->end_y);
&blend_tool->end_x, &blend_tool->end_y,
N_SNAP_LINES);
}
gimp_tool_pop_status (tool, display);

View File

@ -54,6 +54,7 @@
#define TARGET 12
#define ARC_RADIUS 30
#define N_SNAP_LINES 12
/* local function prototypes */
@ -434,7 +435,9 @@ gimp_measure_tool_motion (GimpTool *tool,
gdouble x = measure->x[i];
gdouble y = measure->y[i];
gimp_tool_motion_constrain (measure->x[0], measure->y[0], &x, &y);
gimp_tool_motion_constrain (measure->x[0], measure->y[0],
&x, &y,
N_SNAP_LINES);
measure->x[i] = ROUND (x);
measure->y[i] = ROUND (y);
@ -505,7 +508,9 @@ gimp_measure_tool_active_modifier_key (GimpTool *tool,
y = measure->mouse_y;
if (press)
gimp_tool_motion_constrain (measure->x[0], measure->y[0], &x, &y);
gimp_tool_motion_constrain (measure->x[0], measure->y[0],
&x, &y,
N_SNAP_LINES);
measure->x[measure->point] = ROUND (x);
measure->y[measure->point] = ROUND (y);

View File

@ -51,6 +51,7 @@
#define HANDLE_SIZE 15
#define STATUSBAR_SIZE 200
#define N_SNAP_LINES 12
static GObject * gimp_paint_tool_constructor (GType type,
@ -256,7 +257,8 @@ gimp_paint_tool_round_line (GimpPaintCore *core,
/* Restrict to multiples of 15 degrees if ctrl is pressed */
if (state & GDK_CONTROL_MASK)
gimp_tool_motion_constrain (core->last_coords.x, core->last_coords.y,
&core->cur_coords.x, &core->cur_coords.y);
&core->cur_coords.x, &core->cur_coords.y,
N_SNAP_LINES);
}
static void

View File

@ -25,34 +25,46 @@
#include "tools-utils.h"
static gdouble gimp_tool_utils_point_to_line_distance (const GimpVector2 *point,
const GimpVector2 *point_on_line,
const GimpVector2 *normalized_line_direction,
GimpVector2 *closest_line_point);
/**
* gimp_tool_motion_constrain_helper:
* @dx: the (fixed) delta-x
* @dy: a suggested delta-y
* gimp_tool_utils_point_to_line_distance:
* @point: The point to calculate the distance for.
* @point_on_line: A point on the line.
* @line_direction: Normalized line direction vector.
* @closest_line_point: Gets set to the point on the line that is
* closest to @point.
*
* Returns: An adjusted dy' near dy such that the slope (dx,dy')
* is a multiple of 15 degrees.
* Returns: The shortest distance from @point to the line defined by
* @point_on_line and @normalized_line_direction.
**/
static gdouble
gimp_tool_motion_constrain_helper (gdouble dx,
gdouble dy)
gimp_tool_utils_point_to_line_distance (const GimpVector2 *point,
const GimpVector2 *point_on_line,
const GimpVector2 *line_direction,
GimpVector2 *closest_line_point)
{
static const gdouble slope[4] = { 0, 0.26795, 0.57735, 1 };
static const gdouble divider[3] = { 0.13165, 0.41421, 0.76732 };
gint i;
GimpVector2 distance_vector;
GimpVector2 tmp_a;
GimpVector2 tmp_b;
gdouble d;
if (dy < 0)
return - gimp_tool_motion_constrain_helper (dx, -dy);
gimp_vector2_sub (&tmp_a, point, point_on_line);
dx = fabs (dx);
d = gimp_vector2_inner_product (&tmp_a, line_direction);
for (i = 0; i < 3; i ++)
if (dy < dx * divider[i])
break;
tmp_b = gimp_vector2_mul_val (*line_direction, d);
dy = dx * slope[i];
*closest_line_point = gimp_vector2_add_val (*point_on_line,
tmp_b);
return dy;
gimp_vector2_sub (&distance_vector, closest_line_point, point);
return gimp_vector2_length (&distance_vector);
}
/**
@ -61,33 +73,47 @@ gimp_tool_motion_constrain_helper (gdouble dx,
* @start_y:
* @end_x:
* @end_y:
* @n_snap_lines: Number evenly disributed lines to snap to.
*
* Restricts the motion vector to 15 degree steps by changing the end
* point (if necessary).
* Projects a line onto the specified subset of evenly radially
* distributed lines. @n_lines of 2 makes the line snap horizontally
* or vertically. @n_lines of 4 snaps on 45 degree steps. @n_lines of
* 12 on 15 degree steps. etc.
**/
void
gimp_tool_motion_constrain (gdouble start_x,
gdouble start_y,
gdouble *end_x,
gdouble *end_y)
gimp_tool_motion_constrain (gdouble start_x,
gdouble start_y,
gdouble *end_x,
gdouble *end_y,
gint n_snap_lines)
{
gdouble dx = *end_x - start_x;
gdouble dy = *end_y - start_y;
GimpVector2 line_point = { start_x, start_y };
GimpVector2 point = { *end_x, *end_y };
GimpVector2 constrained_point;
GimpVector2 line_dir;
gdouble shortest_dist_moved = G_MAXDOUBLE;
gdouble dist_moved;
gdouble angle;
gint i;
/* This algorithm changes only one of dx and dy, and does not try
* to constrain the resulting dx and dy to integers. This gives
* at least two benefits:
* 1. gimp_tool_motion_constrain is idempotent, even if followed by
* a rounding operation.
* 2. For any two lines with the same starting-point and ideal
* 15-degree direction, the points plotted by
* gimp_paint_core_interpolate for the shorter line will always
* be a superset of those plotted for the longer line.
*/
for (i = 0; i < n_snap_lines; i++)
{
angle = i * G_PI / n_snap_lines;
if (fabs (dx) > fabs (dy))
*end_y = (start_y + gimp_tool_motion_constrain_helper (dx, dy));
else
*end_x = (start_x + gimp_tool_motion_constrain_helper (dy, dx));
gimp_vector2_set (&line_dir,
cos (angle),
sin (angle));
dist_moved = gimp_tool_utils_point_to_line_distance (&point,
&line_point,
&line_dir,
&constrained_point);
if (dist_moved < shortest_dist_moved)
{
shortest_dist_moved = dist_moved;
*end_x = constrained_point.x;
*end_y = constrained_point.y;
}
}
}

View File

@ -23,7 +23,8 @@
void gimp_tool_motion_constrain (gdouble start_x,
gdouble start_y,
gdouble *end_x,
gdouble *end_y);
gdouble *end_y,
gint n_snap_lines);
#endif /* __TOOLS_UTILS_H__ */