From 4ef8797faa4324b5bf3a0079f3e21556a7cf0231 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 14 Jan 2008 21:23:02 +0000 Subject: [PATCH] Generalized to allow a variable number of lines for snapping, and 2008-01-14 Martin Nordholts * 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 --- ChangeLog | 13 +++++ app/tools/gimpblendtool.c | 9 ++- app/tools/gimpmeasuretool.c | 9 ++- app/tools/gimppainttool.c | 4 +- app/tools/tools-utils.c | 108 ++++++++++++++++++++++-------------- app/tools/tools-utils.h | 3 +- 6 files changed, 98 insertions(+), 48 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4fba6eae5e..d87d649355 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2008-01-14 Martin Nordholts + + * 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 * app/gegl/Makefile.am diff --git a/app/tools/gimpblendtool.c b/app/tools/gimpblendtool.c index 3c9b21cdcd..7a3920d4b1 100644 --- a/app/tools/gimpblendtool.c +++ b/app/tools/gimpblendtool.c @@ -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); diff --git a/app/tools/gimpmeasuretool.c b/app/tools/gimpmeasuretool.c index a7759ef66f..002b7dd6cf 100644 --- a/app/tools/gimpmeasuretool.c +++ b/app/tools/gimpmeasuretool.c @@ -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); diff --git a/app/tools/gimppainttool.c b/app/tools/gimppainttool.c index 98bdda722e..f92c89b290 100644 --- a/app/tools/gimppainttool.c +++ b/app/tools/gimppainttool.c @@ -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 diff --git a/app/tools/tools-utils.c b/app/tools/tools-utils.c index e302e3c609..f4cde8840d 100644 --- a/app/tools/tools-utils.c +++ b/app/tools/tools-utils.c @@ -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; + } + } } - diff --git a/app/tools/tools-utils.h b/app/tools/tools-utils.h index 4025f9f8d4..1266cbf95e 100644 --- a/app/tools/tools-utils.h +++ b/app/tools/tools-utils.h @@ -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__ */