From fc4ca7fbcfeaecbc9d9d64d7c4936d96817e764e Mon Sep 17 00:00:00 2001 From: Ell Date: Sun, 15 Jul 2018 18:07:54 -0400 Subject: [PATCH] app: add "orientation" property to GimpToolCompass + improvements Add an "orientation" property to GimpToolCompass, which can be one of "auto", "horizontal", or "vertical", and which controls the orientation of the line against which the angle is measured, when not in 3-point mode (previously, the line would always be horizontal.) When "orientation" is "auto", the orientation is automatically set to either horizontal or vertical, such that the measured angle is <= 45 deg. Keep the line horizontal, or vertical, in display-space, rather than in image-space, so that the compass works correctly even when the canvas is rotated and/or flipped. Fix the compass's behavior when the image's horizontal and vertical resolutions are different, both with and without dot-for-dot. Add "pixel-angle" and "unit-angle" read-only properties, which return the measured angle either with or without taking the image's resolution into account, respectively. These properties will be used by the measure tool in the next commit, instead of having it implement its own angle calculation. (cherry picked from commit d2f33cf1be76d876234dc58b1adbc8574003f838) --- app/display/display-enums.c | 31 +++ app/display/display-enums.h | 12 + app/display/gimptoolcompass.c | 418 +++++++++++++++++++--------------- app/display/gimptoolcompass.h | 17 +- app/tools/gimpmeasuretool.c | 1 + 5 files changed, 291 insertions(+), 188 deletions(-) diff --git a/app/display/display-enums.c b/app/display/display-enums.c index d7dbaa8997..e54861fffb 100644 --- a/app/display/display-enums.c +++ b/app/display/display-enums.c @@ -455,6 +455,37 @@ gimp_vector_mode_get_type (void) return type; } +GType +gimp_compass_orientation_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_COMPASS_ORIENTATION_AUTO, "GIMP_COMPASS_ORIENTATION_AUTO", "auto" }, + { GIMP_COMPASS_ORIENTATION_HORIZONTAL, "GIMP_COMPASS_ORIENTATION_HORIZONTAL", "horizontal" }, + { GIMP_COMPASS_ORIENTATION_VERTICAL, "GIMP_COMPASS_ORIENTATION_VERTICAL", "vertical" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_COMPASS_ORIENTATION_AUTO, NC_("compass-orientation", "Auto"), NULL }, + { GIMP_COMPASS_ORIENTATION_HORIZONTAL, NC_("compass-orientation", "Horizontal"), NULL }, + { GIMP_COMPASS_ORIENTATION_VERTICAL, NC_("compass-orientation", "Vertical"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpCompassOrientation", values); + gimp_type_set_translation_context (type, "compass-orientation"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + GType gimp_zoom_focus_get_type (void) { diff --git a/app/display/display-enums.h b/app/display/display-enums.h index 52db053dcd..658bcbcfec 100644 --- a/app/display/display-enums.h +++ b/app/display/display-enums.h @@ -197,6 +197,18 @@ typedef enum } GimpVectorMode; +#define GIMP_TYPE_COMPASS_ORIENTATION (gimp_compass_orientation_get_type ()) + +GType gimp_compass_orientation_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_COMPASS_ORIENTATION_AUTO, /*< desc="Auto" >*/ + GIMP_COMPASS_ORIENTATION_HORIZONTAL, /*< desc="Horizontal" >*/ + GIMP_COMPASS_ORIENTATION_VERTICAL /*< desc="Vertical" >*/ +} GimpCompassOrientation; + + #define GIMP_TYPE_ZOOM_FOCUS (gimp_zoom_focus_get_type ()) GType gimp_zoom_focus_get_type (void) G_GNUC_CONST; diff --git a/app/display/gimptoolcompass.c b/app/display/gimptoolcompass.c index 34cdf339d8..1eb6095d56 100644 --- a/app/display/gimptoolcompass.c +++ b/app/display/gimptoolcompass.c @@ -42,6 +42,7 @@ #include "gimpdisplay.h" #include "gimpdisplayshell.h" #include "gimpdisplayshell-appearance.h" +#include "gimpdisplayshell-transform.h" #include "gimpdisplayshell-utils.h" #include "gimptoolcompass.h" @@ -49,6 +50,8 @@ #define ARC_RADIUS 30 +#define EPSILON 1e-6 + /* possible measure functions */ typedef enum @@ -64,13 +67,16 @@ typedef enum enum { PROP_0, + PROP_ORIENTATION, PROP_N_POINTS, PROP_X1, PROP_Y1, PROP_X2, PROP_Y2, PROP_X3, - PROP_Y3 + PROP_Y3, + PROP_PIXEL_ANGLE, + PROP_UNIT_ANGLE }; enum @@ -81,78 +87,85 @@ enum struct _GimpToolCompassPrivate { - gint n_points; - gint x[3]; - gint y[3]; + GimpCompassOrientation orientation; + gint n_points; + gint x[3]; + gint y[3]; - gdouble angle1; - gdouble angle2; + GimpVector2 radius1; + GimpVector2 radius2; + gdouble display_angle; + gdouble pixel_angle; + gdouble unit_angle; - CompassFunction function; - gdouble mouse_x; - gdouble mouse_y; - gint last_x; - gint last_y; - gint point; + CompassFunction function; + gdouble mouse_x; + gdouble mouse_y; + gint last_x; + gint last_y; + gint point; - GimpCanvasItem *line1; - GimpCanvasItem *line2; - GimpCanvasItem *angle; - GimpCanvasItem *angle_line; - GimpCanvasItem *handles[3]; + GimpCanvasItem *line1; + GimpCanvasItem *line2; + GimpCanvasItem *arc; + GimpCanvasItem *arc_line; + GimpCanvasItem *handles[3]; }; /* local function prototypes */ -static void gimp_tool_compass_constructed (GObject *object); -static void gimp_tool_compass_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec); -static void gimp_tool_compass_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec); +static void gimp_tool_compass_constructed (GObject *object); +static void gimp_tool_compass_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_compass_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); -static void gimp_tool_compass_changed (GimpToolWidget *widget); -static gint gimp_tool_compass_button_press (GimpToolWidget *widget, - const GimpCoords *coords, - guint32 time, - GdkModifierType state, - GimpButtonPressType press_type); -static void gimp_tool_compass_button_release (GimpToolWidget *widget, - const GimpCoords *coords, - guint32 time, - GdkModifierType state, - GimpButtonReleaseType release_type); -static void gimp_tool_compass_motion (GimpToolWidget *widget, - const GimpCoords *coords, - guint32 time, - GdkModifierType state); -static GimpHit gimp_tool_compass_hit (GimpToolWidget *widget, - const GimpCoords *coords, - GdkModifierType state, - gboolean proximity); -static void gimp_tool_compass_hover (GimpToolWidget *widget, - const GimpCoords *coords, - GdkModifierType state, - gboolean proximity); -static void gimp_tool_compass_leave_notify (GimpToolWidget *widget); -static void gimp_tool_compass_motion_modifier (GimpToolWidget *widget, - GdkModifierType key, - gboolean press, - GdkModifierType state); -static gboolean gimp_tool_compass_get_cursor (GimpToolWidget *widget, - const GimpCoords *coords, - GdkModifierType state, - GimpCursorType *cursor, - GimpToolCursorType *tool_cursor, - GimpCursorModifier *modifier); +static void gimp_tool_compass_changed (GimpToolWidget *widget); +static gint gimp_tool_compass_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_compass_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_compass_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_compass_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_compass_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_compass_leave_notify (GimpToolWidget *widget); +static void gimp_tool_compass_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_compass_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); -static gint gimp_tool_compass_get_point (GimpToolCompass *compass, - const GimpCoords *coords); -static void gimp_tool_compass_update_hilight (GimpToolCompass *compass); +static gint gimp_tool_compass_get_point (GimpToolCompass *compass, + const GimpCoords *coords); +static void gimp_tool_compass_update_hilight (GimpToolCompass *compass); +static void gimp_tool_compass_update_angle (GimpToolCompass *compass, + GimpCompassOrientation orientation, + gboolean flip); G_DEFINE_TYPE (GimpToolCompass, gimp_tool_compass, GIMP_TYPE_TOOL_WIDGET) @@ -195,6 +208,13 @@ gimp_tool_compass_class_init (GimpToolCompassClass *klass) G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); + g_object_class_install_property (object_class, PROP_ORIENTATION, + g_param_spec_enum ("orientation", NULL, NULL, + GIMP_TYPE_COMPASS_ORIENTATION, + GIMP_COMPASS_ORIENTATION_AUTO, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, PROP_N_POINTS, g_param_spec_int ("n-points", NULL, NULL, 1, 3, 1, @@ -243,6 +263,16 @@ gimp_tool_compass_class_init (GimpToolCompassClass *klass) GIMP_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, PROP_PIXEL_ANGLE, + g_param_spec_double ("pixel-angle", NULL, NULL, + -G_PI, G_PI, 0.0, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_UNIT_ANGLE, + g_param_spec_double ("unit-angle", NULL, NULL, + -G_PI, G_PI, 0.0, + GIMP_PARAM_READABLE)); + g_type_class_add_private (klass, sizeof (GimpToolCompassPrivate)); } @@ -256,72 +286,6 @@ gimp_tool_compass_init (GimpToolCompass *compass) compass->private->point = -1; } -static gdouble -gimp_tool_compass_get_angle (gint dx, - gint dy, - gdouble xres, - gdouble yres) -{ - gdouble angle; - - if (dx) - angle = gimp_rad_to_deg (atan (((gdouble) (dy) / yres) / - ((gdouble) (dx) / xres))); - else if (dy) - angle = dy > 0 ? 270.0 : 90.0; - else - angle = 180.0; - - if (dx > 0) - { - if (dy > 0) - angle = 360.0 - angle; - else - angle = -angle; - } - else - { - angle = 180.0 - angle; - } - - return angle; -} - -static void -gimp_tool_compass_update_angles (GimpToolCompass *compass) -{ - GimpToolWidget *widget = GIMP_TOOL_WIDGET (compass); - GimpToolCompassPrivate *private = compass->private; - GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); - GimpImage *image = gimp_display_get_image (shell->display); - gint ax, ay; - gint bx, by; - gdouble xres; - gdouble yres; - - ax = private->x[1] - private->x[0]; - ay = private->y[1] - private->y[0]; - - if (private->n_points == 3) - { - bx = private->x[2] - private->x[0]; - by = private->y[2] - private->y[0]; - } - else - { - bx = 0; - by = 0; - } - - gimp_image_get_resolution (image, &xres, &yres); - - if (private->n_points != 3) - bx = ax > 0 ? 1 : -1; - - private->angle1 = gimp_tool_compass_get_angle (ax, ay, xres, yres); - private->angle2 = gimp_tool_compass_get_angle (bx, by, xres, yres); -} - static void gimp_tool_compass_constructed (GObject *object) { @@ -349,19 +313,19 @@ gimp_tool_compass_constructed (GObject *object) private->x[2], private->y[2]); - private->angle = gimp_tool_widget_add_handle (widget, - GIMP_HANDLE_CIRCLE, - private->x[0], - private->y[0], - ARC_RADIUS * 2 + 1, - ARC_RADIUS * 2 + 1, - GIMP_HANDLE_ANCHOR_CENTER); + private->arc = gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_CIRCLE, + private->x[0], + private->y[0], + ARC_RADIUS * 2 + 1, + ARC_RADIUS * 2 + 1, + GIMP_HANDLE_ANCHOR_CENTER); - private->angle_line = gimp_tool_widget_add_line (widget, - private->x[0], - private->y[0], - private->x[0] + 10, - private->y[0]); + private->arc_line = gimp_tool_widget_add_line (widget, + private->x[0], + private->y[0], + private->x[0] + 10, + private->y[0]); gimp_tool_widget_pop_group (widget); @@ -413,6 +377,9 @@ gimp_tool_compass_set_property (GObject *object, case PROP_Y3: private->y[2] = g_value_get_int (value); break; + case PROP_ORIENTATION: + private->orientation = g_value_get_enum (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -452,6 +419,15 @@ gimp_tool_compass_get_property (GObject *object, case PROP_Y3: g_value_set_int (value, private->y[2]); break; + case PROP_ORIENTATION: + g_value_set_enum (value, private->orientation); + break; + case PROP_PIXEL_ANGLE: + g_value_set_double (value, private->pixel_angle); + break; + case PROP_UNIT_ANGLE: + g_value_set_double (value, private->unit_angle); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -468,10 +444,13 @@ gimp_tool_compass_changed (GimpToolWidget *widget) gdouble angle1; gdouble angle2; gint draw_arc = 0; - gdouble target; - gdouble arc_radius; + gdouble arc_line_length; - gimp_tool_compass_update_angles (compass); + gimp_tool_compass_update_angle (compass, private->orientation, FALSE); + + angle1 = -atan2 (private->radius1.y * shell->scale_y, + private->radius1.x * shell->scale_x); + angle2 = -private->display_angle; gimp_canvas_line_set (private->line1, private->x[0], @@ -505,36 +484,26 @@ gimp_tool_compass_changed (GimpToolWidget *widget) draw_arc++; } - angle1 = private->angle2 / 180.0 * G_PI; - angle2 = (private->angle1 - private->angle2) / 180.0 * G_PI; - - if (angle2 > G_PI) - angle2 -= 2.0 * G_PI; - - if (angle2 < -G_PI) - angle2 += 2.0 * G_PI; - - gimp_canvas_handle_set_position (private->angle, + gimp_canvas_handle_set_position (private->arc, private->x[0], private->y[0]); - gimp_canvas_handle_set_angles (private->angle, angle1, angle2); - gimp_canvas_item_set_visible (private->angle, + gimp_canvas_handle_set_angles (private->arc, angle1, angle2); + gimp_canvas_item_set_visible (private->arc, private->n_points > 1 && draw_arc == private->n_points - 1 && - angle2 != 0.0); + fabs (angle2) > EPSILON); - target = FUNSCALEX (shell, (GIMP_CANVAS_HANDLE_SIZE_CROSS >> 1)); - arc_radius = FUNSCALEX (shell, ARC_RADIUS); + arc_line_length = (ARC_RADIUS + (GIMP_CANVAS_HANDLE_SIZE_CROSS >> 1)) / + hypot (private->radius2.x * shell->scale_x, + private->radius2.y * shell->scale_y); - gimp_canvas_line_set (private->angle_line, + gimp_canvas_line_set (private->arc_line, private->x[0], private->y[0], - private->x[0] + (private->x[1] > private->x[0] ? - (arc_radius + target) : - -(arc_radius + target)), - private->y[0]); - gimp_canvas_item_set_visible (private->angle_line, + private->x[0] + private->radius2.x * arc_line_length, + private->y[0] + private->radius2.y * arc_line_length); + gimp_canvas_item_set_visible (private->arc_line, private->n_points == 2 && - angle2 != 0.0); + fabs (angle2) > EPSILON); gimp_canvas_handle_set_position (private->handles[0], private->x[0], private->y[0]); @@ -1051,27 +1020,116 @@ gimp_tool_compass_update_hilight (GimpToolCompass *compass) } } +static void +gimp_tool_compass_update_angle (GimpToolCompass *compass, + GimpCompassOrientation orientation, + gboolean flip) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (compass); + GimpToolCompassPrivate *private = compass->private; + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + GimpImage *image = gimp_display_get_image (shell->display); + GimpVector2 radius1; + GimpVector2 radius2; + gdouble xres; + gdouble yres; + + gimp_image_get_resolution (image, &xres, &yres); + + private->radius1.x = private->x[1] - private->x[0]; + private->radius1.y = private->y[1] - private->y[0]; + + if (private->n_points == 3) + { + private->radius2.x = private->x[2] - private->x[0]; + private->radius2.y = private->y[2] - private->y[0]; + } + else + { + gdouble angle = -shell->rotate_angle * G_PI / 180.0; + + if (orientation == GIMP_COMPASS_ORIENTATION_VERTICAL) + angle -= G_PI / 2.0; + + if (flip) + angle += G_PI; + + if (shell->flip_horizontally) + angle = G_PI - angle; + if (shell->flip_vertically) + angle = -angle; + + private->radius2.x = cos (angle); + private->radius2.y = sin (angle); + + if (! shell->dot_for_dot) + { + private->radius2.x *= xres; + private->radius2.y *= yres; + + gimp_vector2_normalize (&private->radius2); + } + } + + radius1 = private->radius1; + radius2 = private->radius2; + + private->pixel_angle = atan2 (gimp_vector2_cross_product (&radius1, &radius2).x, + gimp_vector2_inner_product (&radius1, &radius2)); + + radius1.x /= xres; + radius1.y /= yres; + + radius2.x /= xres; + radius2.y /= yres; + + private->unit_angle = atan2 (gimp_vector2_cross_product (&radius1, &radius2).x, + gimp_vector2_inner_product (&radius1, &radius2)); + + if (shell->dot_for_dot) + private->display_angle = private->pixel_angle; + else + private->display_angle = private->unit_angle; + + if (private->n_points == 2) + { + if (! flip && fabs (private->display_angle) > G_PI / 2.0 + EPSILON) + { + gimp_tool_compass_update_angle (compass, orientation, TRUE); + } + else if (orientation == GIMP_COMPASS_ORIENTATION_AUTO && + fabs (private->display_angle) > G_PI / 4.0 + EPSILON) + { + gimp_tool_compass_update_angle (compass, + GIMP_COMPASS_ORIENTATION_VERTICAL, + FALSE); + } + } +} + /* public functions */ GimpToolWidget * -gimp_tool_compass_new (GimpDisplayShell *shell, - gint n_points, - gint x1, - gint y1, - gint x2, - gint y2, - gint x3, - gint y3) +gimp_tool_compass_new (GimpDisplayShell *shell, + GimpCompassOrientation orientation, + gint n_points, + gint x1, + gint y1, + gint x2, + gint y2, + gint x3, + gint y3) { g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); return g_object_new (GIMP_TYPE_TOOL_COMPASS, - "shell", shell, - "n-points", n_points, - "x1", x1, - "y1", y1, - "x2", x2, - "y2", y2, + "shell", shell, + "orientation", orientation, + "n-points", n_points, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, NULL); } diff --git a/app/display/gimptoolcompass.h b/app/display/gimptoolcompass.h index 31364bcc5a..8e6fdfe06e 100644 --- a/app/display/gimptoolcompass.h +++ b/app/display/gimptoolcompass.h @@ -58,14 +58,15 @@ struct _GimpToolCompassClass GType gimp_tool_compass_get_type (void) G_GNUC_CONST; -GimpToolWidget * gimp_tool_compass_new (GimpDisplayShell *shell, - gint n_points, - gint x1, - gint y1, - gint x2, - gint y2, - gint y3, - gint x3); +GimpToolWidget * gimp_tool_compass_new (GimpDisplayShell *shell, + GimpCompassOrientation orinetation, + gint n_points, + gint x1, + gint y1, + gint x2, + gint y2, + gint y3, + gint x3); #endif /* __GIMP_TOOL_COMPASS_H__ */ diff --git a/app/tools/gimpmeasuretool.c b/app/tools/gimpmeasuretool.c index 57ccca88aa..8da8363b1a 100644 --- a/app/tools/gimpmeasuretool.c +++ b/app/tools/gimpmeasuretool.c @@ -436,6 +436,7 @@ gimp_measure_tool_start (GimpMeasureTool *measure, measure->y[2] = 0; measure->widget = gimp_tool_compass_new (shell, + GIMP_COMPASS_ORIENTATION_AUTO, measure->n_points, measure->x[0], measure->y[0],