transformtool: rotate handles along with frame

This commit is contained in:
Mikael Magnusson 2012-08-22 22:49:56 +02:00
parent e83b40982f
commit b11a705203
2 changed files with 284 additions and 238 deletions

View File

@ -300,22 +300,50 @@ gimp_canvas_handle_draw (GimpCanvasItem *item,
cairo_t *cr)
{
GimpCanvasHandlePrivate *private = GET_PRIVATE (item);
gdouble x, y;
gdouble x, y, tx, ty;
gimp_canvas_handle_transform (item, shell, &x, &y);
gimp_display_shell_transform_xy_f (shell,
private->x, private->y,
&tx, &ty);
switch (private->type)
{
case GIMP_HANDLE_SQUARE:
cairo_rectangle (cr, x, y, private->width - 1.0, private->height - 1.0);
_gimp_canvas_item_stroke (item, cr);
break;
case GIMP_HANDLE_FILLED_SQUARE:
cairo_rectangle (cr, x - 0.5, y - 0.5, private->width, private->height);
case GIMP_HANDLE_DIAMOND:
case GIMP_HANDLE_FILLED_DIAMOND:
cairo_save (cr);
cairo_translate (cr, tx, ty);
cairo_rotate (cr, private->start_angle);
cairo_translate (cr, -tx, -ty);
_gimp_canvas_item_fill (item, cr);
switch (private->type)
{
case GIMP_HANDLE_SQUARE:
cairo_rectangle (cr, x, y, private->width - 1.0, private->height - 1.0);
_gimp_canvas_item_stroke (item, cr);
break;
case GIMP_HANDLE_FILLED_SQUARE:
cairo_rectangle (cr, x - 0.5, y - 0.5, private->width, private->height);
_gimp_canvas_item_fill (item, cr);
break;
case GIMP_HANDLE_DIAMOND:
case GIMP_HANDLE_FILLED_DIAMOND:
cairo_move_to (cr, x, y - (gdouble) private->height / 2.0);
cairo_line_to (cr, x + (gdouble) private->width / 2.0, y);
cairo_line_to (cr, x, y + (gdouble) private->height / 2.0);
cairo_line_to (cr, x - (gdouble) private->width / 2.0, y);
cairo_line_to (cr, x, y - (gdouble) private->height / 2.0);
if (private->type == GIMP_HANDLE_DIAMOND)
_gimp_canvas_item_stroke (item, cr);
else
_gimp_canvas_item_fill (item, cr);
break;
default:
g_assert_not_reached ();
}
cairo_restore (cr);
break;
case GIMP_HANDLE_CIRCLE:
@ -346,20 +374,6 @@ gimp_canvas_handle_draw (GimpCanvasItem *item,
_gimp_canvas_item_stroke (item, cr);
break;
case GIMP_HANDLE_DIAMOND:
case GIMP_HANDLE_FILLED_DIAMOND:
cairo_move_to (cr, x, y - (gdouble) private->height / 2.0);
cairo_line_to (cr, x + (gdouble) private->width / 2.0, y);
cairo_line_to (cr, x, y + (gdouble) private->height / 2.0);
cairo_line_to (cr, x - (gdouble) private->width / 2.0, y);
cairo_line_to (cr, x, y - (gdouble) private->height / 2.0);
if (private->type == GIMP_HANDLE_DIAMOND)
_gimp_canvas_item_stroke (item, cr);
else
_gimp_canvas_item_fill (item, cr);
break;
default:
break;
}
@ -372,6 +386,7 @@ gimp_canvas_handle_get_extents (GimpCanvasItem *item,
GimpCanvasHandlePrivate *private = GET_PRIVATE (item);
cairo_rectangle_int_t rectangle;
gdouble x, y;
gdouble w, h;
gimp_canvas_handle_transform (item, shell, &x, &y);
@ -379,10 +394,12 @@ gimp_canvas_handle_get_extents (GimpCanvasItem *item,
{
case GIMP_HANDLE_SQUARE:
case GIMP_HANDLE_FILLED_SQUARE:
rectangle.x = x - 1.5;
rectangle.y = y - 1.5;
rectangle.width = private->width + 3.0;
rectangle.height = private->height + 3.0;
w = private->width * (sqrt(2) - 1) / 2;
h = private->height * (sqrt(2) - 1) / 2;
rectangle.x = x - 1.5 - w;
rectangle.y = y - 1.5 - h;
rectangle.width = private->width + 3.0 + w * 2;
rectangle.height = private->height + 3.0 + h * 2;
break;
case GIMP_HANDLE_CIRCLE:
@ -411,20 +428,33 @@ gimp_canvas_handle_hit (GimpCanvasItem *item,
{
GimpCanvasHandlePrivate *private = GET_PRIVATE (item);
gdouble handle_tx, handle_ty;
gdouble tx, ty;
gdouble mx, my, tx, ty, mmx, mmy;
gdouble diamond_offset_x = 0.0, diamond_offset_y = 0.0;
gdouble angle = -private->start_angle;
gimp_canvas_handle_transform (item, shell, &handle_tx, &handle_ty);
gimp_display_shell_transform_xy_f (shell,
x, y,
&tx, &ty);
&mx, &my);
switch (private->type)
{
case GIMP_HANDLE_DIAMOND:
case GIMP_HANDLE_FILLED_DIAMOND:
angle -= G_PI / 4.0;
diamond_offset_x = private->width / 2.0;
diamond_offset_y = private->height / 2.0;
case GIMP_HANDLE_SQUARE:
case GIMP_HANDLE_FILLED_SQUARE:
return (tx == CLAMP (tx, handle_tx, handle_tx + private->width) &&
ty == CLAMP (ty, handle_ty, handle_ty + private->height));
gimp_display_shell_transform_xy_f (shell,
private->x, private->y,
&tx, &ty);
mmx = mx - tx; mmy = my - ty;
mx = cos (angle) * mmx - sin (angle) * mmy + tx + diamond_offset_x;
my = sin (angle) * mmx + cos (angle) * mmy + ty + diamond_offset_y;
return mx > handle_tx && mx < handle_tx + private->width &&
my > handle_ty && my < handle_ty + private->height;
case GIMP_HANDLE_CIRCLE:
case GIMP_HANDLE_FILLED_CIRCLE:
@ -437,14 +467,9 @@ gimp_canvas_handle_hit (GimpCanvasItem *item,
width /= 2;
return ((SQR (handle_tx - tx) + SQR (handle_ty - ty)) < SQR (width));
return ((SQR (handle_tx - mx) + SQR (handle_ty - my)) < SQR (width));
}
case GIMP_HANDLE_DIAMOND:
case GIMP_HANDLE_FILLED_DIAMOND:
return ((ABS (handle_tx - tx) + ABS (handle_ty - ty)) < ABS (private->width));
break;
default:
break;
}

View File

@ -39,6 +39,7 @@
#include "widgets/gimphelp-ids.h"
#include "display/gimpcanvasgroup.h"
#include "display/gimpcanvashandle.h"
#include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-transform.h"
@ -136,6 +137,164 @@ gimp_unified_transform_tool_init (GimpUnifiedTransformTool *unified_tool)
tr_tool->use_handles = TRUE;
}
static gboolean
transform_is_convex (GimpVector2 *pos)
{
return gimp_transform_polygon_is_convex (pos[0].x, pos[0].y,
pos[1].x, pos[1].y,
pos[2].x, pos[2].y,
pos[3].x, pos[3].y);
}
static inline gdouble
dotprod (GimpVector2 a,
GimpVector2 b)
{
return a.x * b.x + a.y * b.y;
}
static inline gdouble norm (GimpVector2 a)
{
return sqrt (dotprod (a, a));
}
static inline GimpVector2
vectorsubtract (GimpVector2 a,
GimpVector2 b)
{
GimpVector2 c;
c.x = a.x - b.x;
c.y = a.y - b.y;
return c;
}
static inline GimpVector2
vectoradd (GimpVector2 a,
GimpVector2 b)
{
GimpVector2 c;
c.x = a.x + b.x;
c.y = a.y + b.y;
return c;
}
static inline GimpVector2
scalemult (GimpVector2 a,
gdouble b)
{
GimpVector2 c;
c.x = a.x * b;
c.y = a.y * b;
return c;
}
static inline GimpVector2
vectorproject (GimpVector2 a,
GimpVector2 b)
{
return scalemult (b, dotprod (a, b) / dotprod (b, b));
}
/* finds the clockwise angle between the vectors given, 0-2π */
static inline gdouble
calcangle (GimpVector2 a,
GimpVector2 b)
{
gdouble angle, angle2;
gdouble length = norm (a) * norm (b);
angle = acos (dotprod (a, b)/length);
angle2 = b.y;
b.y = -b.x;
b.x = angle2;
angle2 = acos (dotprod (a, b)/length);
return ((angle2 > G_PI/2.) ? angle : 2*G_PI-angle);
}
static inline GimpVector2
rotate2d (GimpVector2 p,
gdouble angle)
{
GimpVector2 ret;
ret.x = cos (angle) * p.x-sin (angle) * p.y;
ret.y = sin (angle) * p.x+cos (angle) * p.y;
return ret;
}
static inline GimpVector2
lineintersect (GimpVector2 p1, GimpVector2 p2,
GimpVector2 q1, GimpVector2 q2)
{
gdouble denom, u;
GimpVector2 p;
denom = (q2.y - q1.y) * (p2.x - p1.x) - (q2.x - q1.x) * (p2.y - p1.y);
if (denom == 0.0)
{
p.x = (p1.x + p2.x + q1.x + q2.x) / 4;
p.y = (p1.y + p2.y + q1.y + q2.y) / 4;
}
else
{
u = (q2.x - q1.x) * (p1.y - q1.y) - (q2.y - q1.y) * (p1.x - q1.x);
u /= denom;
p.x = p1.x + u * (p2.x - p1.x);
p.y = p1.y + u * (p2.y - p1.y);
}
return p;
}
static inline GimpVector2
getpivotdelta (GimpTransformTool *tr_tool,
GimpVector2 *oldpos,
GimpVector2 *newpos,
GimpVector2 pivot)
{
GimpMatrix3 transform_before, transform_after;
GimpVector2 delta;
gimp_matrix3_identity (&transform_before);
gimp_matrix3_identity (&transform_after);
gimp_transform_matrix_perspective (&transform_before,
tr_tool->x1,
tr_tool->y1,
tr_tool->x2 - tr_tool->x1,
tr_tool->y2 - tr_tool->y1,
oldpos[0].x, oldpos[0].y,
oldpos[1].x, oldpos[1].y,
oldpos[2].x, oldpos[2].y,
oldpos[3].x, oldpos[3].y);
gimp_transform_matrix_perspective (&transform_after,
tr_tool->x1,
tr_tool->y1,
tr_tool->x2 - tr_tool->x1,
tr_tool->y2 - tr_tool->y1,
newpos[0].x, newpos[0].y,
newpos[1].x, newpos[1].y,
newpos[2].x, newpos[2].y,
newpos[3].x, newpos[3].y);
gimp_matrix3_invert (&transform_before);
gimp_matrix3_mult (&transform_after, &transform_before);
gimp_matrix3_transform_point (&transform_before,
pivot.x, pivot.y, &delta.x, &delta.y);
delta = vectorsubtract (delta, pivot);
return delta;
}
static gboolean
point_is_inside_polygon (gint n, gdouble *x, gdouble *y, gdouble px, gdouble py)
{
@ -309,65 +468,85 @@ gimp_unified_transform_tool_draw_gui (GimpTransformTool *tr_tool,
GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tr_tool);
GimpCanvasGroup *stroke_group;
gint d, i;
gdouble tx[] = { tr_tool->tx1, tr_tool->tx2,
tr_tool->tx3, tr_tool->tx4 },
ty[] = { tr_tool->ty1, tr_tool->ty2,
tr_tool->ty3, tr_tool->ty4 };
gdouble angle[8];
GimpVector2 o[] = { { .x = tr_tool->tx1, .y = tr_tool->ty1 },
{ .x = tr_tool->tx2, .y = tr_tool->ty2 },
{ .x = tr_tool->tx3, .y = tr_tool->ty3 },
{ .x = tr_tool->tx4, .y = tr_tool->ty4 } },
t[] = { o[0], o[1], o[2], o[3] },
right = { .x = 1.0, .y = 0.0 },
up = { .x = 0.0, .y = 1.0 };
angle[0] = calcangle (vectorsubtract (o[0], o[1]), right);
angle[1] = calcangle (vectorsubtract (o[2], o[3]), right);
angle[2] = calcangle (vectorsubtract (o[3], o[1]), up);
angle[3] = calcangle (vectorsubtract (o[2], o[0]), up);
angle[4] = angle[0] + angle[3];
angle[5] = angle[0] + angle[2];
angle[6] = angle[1] + angle[3];
angle[7] = angle[1] + angle[2];
for (i = 0; i < 4; i++)
{
GimpCanvasItem *h;
/* draw the scale handles */
tr_tool->handles[TRANSFORM_HANDLE_NW + i] =
gimp_draw_tool_add_handle (draw_tool,
GIMP_HANDLE_SQUARE,
tx[i], ty[i],
handle_w * 1.5, handle_h * 1.5,
GIMP_HANDLE_ANCHOR_CENTER);
h = gimp_draw_tool_add_handle (draw_tool,
GIMP_HANDLE_SQUARE,
t[i].x, t[i].y,
handle_w * 1.5, handle_h * 1.5,
GIMP_HANDLE_ANCHOR_CENTER);
gimp_canvas_handle_set_angles (h, angle[i + 4] / 2.0, 0.0);
tr_tool->handles[TRANSFORM_HANDLE_NW + i] = h;
/* draw the perspective handles */
tr_tool->handles[TRANSFORM_HANDLE_NW_P + i] =
gimp_draw_tool_add_handle (draw_tool,
GIMP_HANDLE_SQUARE,
tx[i], ty[i],
handle_w * 0.8, handle_h * 0.8,
GIMP_HANDLE_ANCHOR_CENTER);
h = gimp_draw_tool_add_handle (draw_tool,
GIMP_HANDLE_SQUARE,
t[i].x, t[i].y,
handle_w * 0.8, handle_h * 0.8,
GIMP_HANDLE_ANCHOR_CENTER);
gimp_canvas_handle_set_angles (h, angle[i + 4] / 2.0, 0.0);
tr_tool->handles[TRANSFORM_HANDLE_NW_P + i] = h;
}
/* draw the side handles */
tx[0] = (tr_tool->tx1 + tr_tool->tx2) / 2.0;
ty[0] = (tr_tool->ty1 + tr_tool->ty2) / 2.0;
tx[1] = (tr_tool->tx3 + tr_tool->tx4) / 2.0;
ty[1] = (tr_tool->ty3 + tr_tool->ty4) / 2.0;
tx[2] = (tr_tool->tx2 + tr_tool->tx4) / 2.0;
ty[2] = (tr_tool->ty2 + tr_tool->ty4) / 2.0;
tx[3] = (tr_tool->tx3 + tr_tool->tx1) / 2.0;
ty[3] = (tr_tool->ty3 + tr_tool->ty1) / 2.0;
t[0] = scalemult (vectoradd (o[0], o[1]), 0.5);
t[1] = scalemult (vectoradd (o[2], o[3]), 0.5);
t[2] = scalemult (vectoradd (o[1], o[3]), 0.5);
t[3] = scalemult (vectoradd (o[2], o[0]), 0.5);
for (i = 0; i < 4; i++)
tr_tool->handles[TRANSFORM_HANDLE_N + i] =
gimp_draw_tool_add_handle (draw_tool,
GIMP_HANDLE_SQUARE,
tx[i], ty[i],
handle_w, handle_h,
GIMP_HANDLE_ANCHOR_CENTER);
{
GimpCanvasItem *h;
h = gimp_draw_tool_add_handle (draw_tool,
GIMP_HANDLE_SQUARE,
t[i].x, t[i].y,
handle_w, handle_h,
GIMP_HANDLE_ANCHOR_CENTER);
gimp_canvas_handle_set_angles (h, angle[i], 0.0);
tr_tool->handles[TRANSFORM_HANDLE_N + i] = h;
}
/* draw the shear handles */
tx[0] = (tr_tool->tx1 * 1.0 + tr_tool->tx2 * 3.0) / 4.0;
ty[0] = (tr_tool->ty1 * 1.0 + tr_tool->ty2 * 3.0) / 4.0;
tx[1] = (tr_tool->tx3 * 3.0 + tr_tool->tx4 * 1.0) / 4.0;
ty[1] = (tr_tool->ty3 * 3.0 + tr_tool->ty4 * 1.0) / 4.0;
tx[2] = (tr_tool->tx2 * 1.0 + tr_tool->tx4 * 3.0) / 4.0;
ty[2] = (tr_tool->ty2 * 1.0 + tr_tool->ty4 * 3.0) / 4.0;
tx[3] = (tr_tool->tx3 * 3.0 + tr_tool->tx1 * 1.0) / 4.0;
ty[3] = (tr_tool->ty3 * 3.0 + tr_tool->ty1 * 1.0) / 4.0;
t[0] = scalemult (vectoradd ( o[0] , scalemult (o[1], 3.0)), 0.25);
t[1] = scalemult (vectoradd (scalemult (o[2], 3.0), o[3] ), 0.25);
t[2] = scalemult (vectoradd ( o[1] , scalemult (o[3], 3.0)), 0.25);
t[3] = scalemult (vectoradd (scalemult (o[2], 3.0), o[0] ), 0.25);
for (i = 0; i < 4; i++)
tr_tool->handles[TRANSFORM_HANDLE_N_S + i] =
gimp_draw_tool_add_handle (draw_tool,
GIMP_HANDLE_FILLED_DIAMOND,
tx[i], ty[i],
handle_w, handle_h,
GIMP_HANDLE_ANCHOR_CENTER);
{
GimpCanvasItem *h;
h = gimp_draw_tool_add_handle (draw_tool,
GIMP_HANDLE_FILLED_DIAMOND,
t[i].x, t[i].y,
handle_w, handle_h,
GIMP_HANDLE_ANCHOR_CENTER);
gimp_canvas_handle_set_angles (h, angle[i], 0.0);
tr_tool->handles[TRANSFORM_HANDLE_N_S + i] = h;
}
/* draw the rotation center axis handle */
d = MIN (handle_w, handle_h);
@ -473,164 +652,6 @@ gimp_unified_transform_tool_prepare (GimpTransformTool *tr_tool)
tr_tool->trans_info[Y3] = (gdouble) tr_tool->y2;
}
static gboolean
transform_is_convex (GimpVector2 *pos)
{
return gimp_transform_polygon_is_convex (pos[0].x, pos[0].y,
pos[1].x, pos[1].y,
pos[2].x, pos[2].y,
pos[3].x, pos[3].y);
}
static inline gdouble
dotprod (GimpVector2 a,
GimpVector2 b)
{
return a.x * b.x + a.y * b.y;
}
static inline gdouble norm (GimpVector2 a)
{
return sqrt (dotprod (a, a));
}
static inline GimpVector2
vectorsubtract (GimpVector2 a,
GimpVector2 b)
{
GimpVector2 c;
c.x = a.x - b.x;
c.y = a.y - b.y;
return c;
}
static inline GimpVector2
vectoradd (GimpVector2 a,
GimpVector2 b)
{
GimpVector2 c;
c.x = a.x + b.x;
c.y = a.y + b.y;
return c;
}
static inline GimpVector2
scalemult (GimpVector2 a,
gdouble b)
{
GimpVector2 c;
c.x = a.x * b;
c.y = a.y * b;
return c;
}
static inline GimpVector2
vectorproject (GimpVector2 a,
GimpVector2 b)
{
return scalemult (b, dotprod (a, b) / dotprod (b, b));
}
/* finds the clockwise angle between the vectors given, 0-2π */
static inline gdouble
calcangle (GimpVector2 a,
GimpVector2 b)
{
gdouble angle, angle2;
gdouble length = norm (a) * norm (b);
angle = acos (dotprod (a, b)/length);
angle2 = b.y;
b.y = -b.x;
b.x = angle2;
angle2 = acos (dotprod (a, b)/length);
return ((angle2 > G_PI/2.) ? angle : 2*G_PI-angle);
}
static inline GimpVector2
rotate2d (GimpVector2 p,
gdouble angle)
{
GimpVector2 ret;
ret.x = cos (angle) * p.x-sin (angle) * p.y;
ret.y = sin (angle) * p.x+cos (angle) * p.y;
return ret;
}
static inline GimpVector2
lineintersect (GimpVector2 p1, GimpVector2 p2,
GimpVector2 q1, GimpVector2 q2)
{
gdouble denom, u;
GimpVector2 p;
denom = (q2.y - q1.y) * (p2.x - p1.x) - (q2.x - q1.x) * (p2.y - p1.y);
if (denom == 0.0)
{
p.x = (p1.x + p2.x + q1.x + q2.x) / 4;
p.y = (p1.y + p2.y + q1.y + q2.y) / 4;
}
else
{
u = (q2.x - q1.x) * (p1.y - q1.y) - (q2.y - q1.y) * (p1.x - q1.x);
u /= denom;
p.x = p1.x + u * (p2.x - p1.x);
p.y = p1.y + u * (p2.y - p1.y);
}
return p;
}
static inline GimpVector2
getpivotdelta (GimpTransformTool *tr_tool,
GimpVector2 *oldpos,
GimpVector2 *newpos,
GimpVector2 pivot)
{
GimpMatrix3 transform_before, transform_after;
GimpVector2 delta;
gimp_matrix3_identity (&transform_before);
gimp_matrix3_identity (&transform_after);
gimp_transform_matrix_perspective (&transform_before,
tr_tool->x1,
tr_tool->y1,
tr_tool->x2 - tr_tool->x1,
tr_tool->y2 - tr_tool->y1,
oldpos[0].x, oldpos[0].y,
oldpos[1].x, oldpos[1].y,
oldpos[2].x, oldpos[2].y,
oldpos[3].x, oldpos[3].y);
gimp_transform_matrix_perspective (&transform_after,
tr_tool->x1,
tr_tool->y1,
tr_tool->x2 - tr_tool->x1,
tr_tool->y2 - tr_tool->y1,
newpos[0].x, newpos[0].y,
newpos[1].x, newpos[1].y,
newpos[2].x, newpos[2].y,
newpos[3].x, newpos[3].y);
gimp_matrix3_invert (&transform_before);
gimp_matrix3_mult (&transform_after, &transform_before);
gimp_matrix3_transform_point (&transform_before,
pivot.x, pivot.y, &delta.x, &delta.y);
delta = vectorsubtract (delta, pivot);
return delta;
}
static void
gimp_unified_transform_tool_motion (GimpTransformTool *transform_tool)
{