diff options
Diffstat (limited to 'app/display/gimptooltransform3dgrid.c')
-rw-r--r-- | app/display/gimptooltransform3dgrid.c | 1162 |
1 files changed, 1162 insertions, 0 deletions
diff --git a/app/display/gimptooltransform3dgrid.c b/app/display/gimptooltransform3dgrid.c new file mode 100644 index 0000000..06cd955 --- /dev/null +++ b/app/display/gimptooltransform3dgrid.c @@ -0,0 +1,1162 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptool3dtransformgrid.c + * Copyright (C) 2019 Ell + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <gegl.h> +#include <gtk/gtk.h> + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "core/gimp-transform-3d-utils.h" +#include "core/gimp-utils.h" + +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-transform.h" +#include "gimptooltransform3dgrid.h" + +#include "gimp-intl.h" + + +#define CONSTRAINT_MIN_DIST 8.0 +#define PIXELS_PER_REVOLUTION 1000 + + +enum +{ + PROP_0, + PROP_MODE, + PROP_UNIFIED, + PROP_CONSTRAIN_AXIS, + PROP_Z_AXIS, + PROP_LOCAL_FRAME, + PROP_CAMERA_X, + PROP_CAMERA_Y, + PROP_CAMERA_Z, + PROP_OFFSET_X, + PROP_OFFSET_Y, + PROP_OFFSET_Z, + PROP_ROTATION_ORDER, + PROP_ANGLE_X, + PROP_ANGLE_Y, + PROP_ANGLE_Z, + PROP_PIVOT_3D_X, + PROP_PIVOT_3D_Y, + PROP_PIVOT_3D_Z +}; + +typedef enum +{ + AXIS_NONE, + AXIS_X, + AXIS_Y +} Axis; + +struct _GimpToolTransform3DGridPrivate +{ + GimpTransform3DMode mode; + gboolean unified; + + gboolean constrain_axis; + gboolean z_axis; + gboolean local_frame; + + gdouble camera_x; + gdouble camera_y; + gdouble camera_z; + + gdouble offset_x; + gdouble offset_y; + gdouble offset_z; + + gint rotation_order; + gdouble angle_x; + gdouble angle_y; + gdouble angle_z; + + gdouble pivot_x; + gdouble pivot_y; + gdouble pivot_z; + + GimpTransformHandle handle; + + gdouble orig_x; + gdouble orig_y; + gdouble orig_offset_x; + gdouble orig_offset_y; + gdouble orig_offset_z; + GimpMatrix3 orig_transform; + + gdouble last_x; + gdouble last_y; + + Axis constrained_axis; +}; + + +/* local function prototypes */ + +static void gimp_tool_transform_3d_grid_constructed (GObject *object); +static void gimp_tool_transform_3d_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_transform_3d_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint gimp_tool_transform_3d_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_transform_3d_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static void gimp_tool_transform_3d_grid_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_transform_3d_grid_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_transform_3d_grid_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static void gimp_tool_transform_3d_grid_update_mode (GimpToolTransform3DGrid *grid); +static void gimp_tool_transform_3d_grid_reset_motion (GimpToolTransform3DGrid *grid); +static gboolean gimp_tool_transform_3d_grid_constrain (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y, + gdouble ox, + gdouble oy, + gdouble *tx, + gdouble *ty); + +static gboolean gimp_tool_transform_3d_grid_motion_vanishing_point (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y); +static gboolean gimp_tool_transform_3d_grid_motion_move (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y); +static gboolean gimp_tool_transform_3d_grid_motion_rotate (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolTransform3DGrid, gimp_tool_transform_3d_grid, + GIMP_TYPE_TOOL_TRANSFORM_GRID) + +#define parent_class gimp_tool_transform_3d_grid_parent_class + + +static void +gimp_tool_transform_3d_grid_class_init (GimpToolTransform3DGridClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_transform_3d_grid_constructed; + object_class->set_property = gimp_tool_transform_3d_grid_set_property; + object_class->get_property = gimp_tool_transform_3d_grid_get_property; + + widget_class->button_press = gimp_tool_transform_3d_grid_button_press; + widget_class->motion = gimp_tool_transform_3d_grid_motion; + widget_class->hover = gimp_tool_transform_3d_grid_hover; + widget_class->hover_modifier = gimp_tool_transform_3d_grid_hover_modifier; + widget_class->get_cursor = gimp_tool_transform_3d_grid_get_cursor; + + g_object_class_install_property (object_class, PROP_MODE, + g_param_spec_enum ("mode", + NULL, NULL, + GIMP_TYPE_TRANSFORM_3D_MODE, + GIMP_TRANSFORM_3D_MODE_CAMERA, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_UNIFIED, + g_param_spec_boolean ("unified", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CONSTRAIN_AXIS, + g_param_spec_boolean ("constrain-axis", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Z_AXIS, + g_param_spec_boolean ("z-axis", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_LOCAL_FRAME, + g_param_spec_boolean ("local-frame", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CAMERA_X, + g_param_spec_double ("camera-x", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CAMERA_Y, + g_param_spec_double ("camera-y", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CAMERA_Z, + g_param_spec_double ("camera-z", + NULL, NULL, + -(1.0 / 0.0), + 1.0 / 0.0, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_OFFSET_X, + g_param_spec_double ("offset-x", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_OFFSET_Y, + g_param_spec_double ("offset-y", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_OFFSET_Z, + g_param_spec_double ("offset-z", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ROTATION_ORDER, + g_param_spec_int ("rotation-order", + NULL, NULL, + 0, 6, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ANGLE_X, + g_param_spec_double ("angle-x", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ANGLE_Y, + g_param_spec_double ("angle-y", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ANGLE_Z, + g_param_spec_double ("angle-z", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIVOT_3D_X, + g_param_spec_double ("pivot-3d-x", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIVOT_3D_Y, + g_param_spec_double ("pivot-3d-y", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIVOT_3D_Z, + g_param_spec_double ("pivot-3d-z", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tool_transform_3d_grid_init (GimpToolTransform3DGrid *grid) +{ + grid->priv = gimp_tool_transform_3d_grid_get_instance_private (grid); +} + +static void +gimp_tool_transform_3d_grid_constructed (GObject *object) +{ + G_OBJECT_CLASS (parent_class)->constructed (object); + + g_object_set (object, + "clip-guides", TRUE, + "dynamic-handle-size", FALSE, + NULL); +} + +static void +gimp_tool_transform_3d_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolTransform3DGrid *grid = GIMP_TOOL_TRANSFORM_3D_GRID (object); + GimpToolTransform3DGridPrivate *priv = grid->priv; + + switch (property_id) + { + case PROP_MODE: + priv->mode = g_value_get_enum (value); + gimp_tool_transform_3d_grid_update_mode (grid); + break; + + case PROP_UNIFIED: + priv->unified = g_value_get_boolean (value); + gimp_tool_transform_3d_grid_update_mode (grid); + break; + + case PROP_CONSTRAIN_AXIS: + priv->constrain_axis = g_value_get_boolean (value); + gimp_tool_transform_3d_grid_reset_motion (grid); + break; + case PROP_Z_AXIS: + priv->z_axis = g_value_get_boolean (value); + gimp_tool_transform_3d_grid_reset_motion (grid); + break; + case PROP_LOCAL_FRAME: + priv->local_frame = g_value_get_boolean (value); + gimp_tool_transform_3d_grid_reset_motion (grid); + break; + + case PROP_CAMERA_X: + priv->camera_x = g_value_get_double (value); + g_object_set (grid, + "pivot-x", priv->camera_x, + NULL); + break; + case PROP_CAMERA_Y: + priv->camera_y = g_value_get_double (value); + g_object_set (grid, + "pivot-y", priv->camera_y, + NULL); + break; + case PROP_CAMERA_Z: + priv->camera_z = g_value_get_double (value); + break; + + case PROP_OFFSET_X: + priv->offset_x = g_value_get_double (value); + break; + case PROP_OFFSET_Y: + priv->offset_y = g_value_get_double (value); + break; + case PROP_OFFSET_Z: + priv->offset_z = g_value_get_double (value); + break; + + case PROP_ROTATION_ORDER: + priv->rotation_order = g_value_get_int (value); + break; + case PROP_ANGLE_X: + priv->angle_x = g_value_get_double (value); + break; + case PROP_ANGLE_Y: + priv->angle_y = g_value_get_double (value); + break; + case PROP_ANGLE_Z: + priv->angle_z = g_value_get_double (value); + break; + + case PROP_PIVOT_3D_X: + priv->pivot_x = g_value_get_double (value); + break; + case PROP_PIVOT_3D_Y: + priv->pivot_y = g_value_get_double (value); + break; + case PROP_PIVOT_3D_Z: + priv->pivot_z = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_transform_3d_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolTransform3DGrid *grid = GIMP_TOOL_TRANSFORM_3D_GRID (object); + GimpToolTransform3DGridPrivate *priv = grid->priv; + + switch (property_id) + { + case PROP_MODE: + g_value_set_enum (value, priv->mode); + break; + case PROP_UNIFIED: + g_value_set_boolean (value, priv->unified); + break; + + case PROP_CONSTRAIN_AXIS: + g_value_set_boolean (value, priv->constrain_axis); + break; + case PROP_Z_AXIS: + g_value_set_boolean (value, priv->z_axis); + break; + case PROP_LOCAL_FRAME: + g_value_set_boolean (value, priv->local_frame); + break; + + case PROP_CAMERA_X: + g_value_set_double (value, priv->camera_x); + break; + case PROP_CAMERA_Y: + g_value_set_double (value, priv->camera_y); + break; + case PROP_CAMERA_Z: + g_value_set_double (value, priv->camera_z); + break; + + case PROP_OFFSET_X: + g_value_set_double (value, priv->offset_x); + break; + case PROP_OFFSET_Y: + g_value_set_double (value, priv->offset_y); + break; + case PROP_OFFSET_Z: + g_value_set_double (value, priv->offset_z); + break; + + case PROP_ROTATION_ORDER: + g_value_set_int (value, priv->rotation_order); + break; + case PROP_ANGLE_X: + g_value_set_double (value, priv->angle_x); + break; + case PROP_ANGLE_Y: + g_value_set_double (value, priv->angle_y); + break; + case PROP_ANGLE_Z: + g_value_set_double (value, priv->angle_z); + break; + + case PROP_PIVOT_3D_X: + g_value_set_double (value, priv->pivot_x); + break; + case PROP_PIVOT_3D_Y: + g_value_set_double (value, priv->pivot_y); + break; + case PROP_PIVOT_3D_Z: + g_value_set_double (value, priv->pivot_z); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint +gimp_tool_transform_3d_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolTransform3DGrid *grid = GIMP_TOOL_TRANSFORM_3D_GRID (widget); + GimpToolTransform3DGridPrivate *priv = grid->priv; + + priv->handle = GIMP_TOOL_WIDGET_CLASS (parent_class)->button_press ( + widget, coords, time, state, press_type); + + priv->orig_x = coords->x; + priv->orig_y = coords->y; + priv->orig_offset_x = priv->offset_x; + priv->orig_offset_y = priv->offset_y; + priv->orig_offset_z = priv->offset_z; + priv->last_x = coords->x; + priv->last_y = coords->y; + + gimp_tool_transform_3d_grid_reset_motion (grid); + + return priv->handle; +} + +void +gimp_tool_transform_3d_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolTransform3DGrid *grid = GIMP_TOOL_TRANSFORM_3D_GRID (widget); + GimpToolTransform3DGridPrivate *priv = grid->priv; + GimpMatrix3 transform; + gboolean update = TRUE; + + switch (priv->handle) + { + case GIMP_TRANSFORM_HANDLE_PIVOT: + update = gimp_tool_transform_3d_grid_motion_vanishing_point ( + grid, coords->x, coords->y); + break; + + case GIMP_TRANSFORM_HANDLE_CENTER: + update = gimp_tool_transform_3d_grid_motion_move ( + grid, coords->x, coords->y); + break; + + case GIMP_TRANSFORM_HANDLE_ROTATION: + update = gimp_tool_transform_3d_grid_motion_rotate ( + grid, coords->x, coords->y); + break; + + default: + g_return_if_reached (); + } + + if (update) + { + gimp_transform_3d_matrix (&transform, + + priv->camera_x, + priv->camera_y, + priv->camera_z, + + priv->offset_x, + priv->offset_y, + priv->offset_z, + + priv->rotation_order, + priv->angle_x, + priv->angle_y, + priv->angle_z, + + priv->pivot_x, + priv->pivot_y, + priv->pivot_z); + + g_object_set (widget, + "transform", &transform, + NULL); + } +} + +static void +gimp_tool_transform_3d_grid_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GIMP_TOOL_WIDGET_CLASS (parent_class)->hover (widget, + coords, state, proximity); + + if (proximity && + gimp_tool_transform_grid_get_handle (GIMP_TOOL_TRANSFORM_GRID (widget)) == + GIMP_TRANSFORM_HANDLE_PIVOT) + { + gimp_tool_widget_set_status (widget, + _("Click-Drag to move the vanishing point")); + } +} + +static void +gimp_tool_transform_3d_grid_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolTransform3DGrid *grid = GIMP_TOOL_TRANSFORM_3D_GRID (widget); + GimpToolTransform3DGridPrivate *priv = grid->priv; + + GIMP_TOOL_WIDGET_CLASS (parent_class)->hover_modifier (widget, + key, press, state); + + priv->local_frame = (state & gimp_get_extend_selection_mask ()) != 0; +} + +static gboolean +gimp_tool_transform_3d_grid_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + if (! GIMP_TOOL_WIDGET_CLASS (parent_class)->get_cursor (widget, + coords, + state, + cursor, + tool_cursor, + modifier)) + { + return FALSE; + } + + if (gimp_tool_transform_grid_get_handle (GIMP_TOOL_TRANSFORM_GRID (widget)) == + GIMP_TRANSFORM_HANDLE_PIVOT) + { + *tool_cursor = GIMP_TOOL_CURSOR_TRANSFORM_3D_CAMERA; + } + + return TRUE; +} + +static void +gimp_tool_transform_3d_grid_update_mode (GimpToolTransform3DGrid *grid) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + + if (priv->unified) + { + g_object_set (grid, + "inside-function", GIMP_TRANSFORM_FUNCTION_MOVE, + "outside-function", GIMP_TRANSFORM_FUNCTION_ROTATE, + "use-pivot-handle", TRUE, + NULL); + } + else + { + switch (priv->mode) + { + case GIMP_TRANSFORM_3D_MODE_CAMERA: + g_object_set (grid, + "inside-function", GIMP_TRANSFORM_FUNCTION_NONE, + "outside-function", GIMP_TRANSFORM_FUNCTION_NONE, + "use-pivot-handle", TRUE, + NULL); + break; + + case GIMP_TRANSFORM_3D_MODE_MOVE: + g_object_set (grid, + "inside-function", GIMP_TRANSFORM_FUNCTION_MOVE, + "outside-function", GIMP_TRANSFORM_FUNCTION_MOVE, + "use-pivot-handle", FALSE, + NULL); + break; + + case GIMP_TRANSFORM_3D_MODE_ROTATE: + g_object_set (grid, + "inside-function", GIMP_TRANSFORM_FUNCTION_ROTATE, + "outside-function", GIMP_TRANSFORM_FUNCTION_ROTATE, + "use-pivot-handle", FALSE, + NULL); + break; + } + } +} + +static void +gimp_tool_transform_3d_grid_reset_motion (GimpToolTransform3DGrid *grid) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + GimpMatrix3 *transform; + + priv->constrained_axis = AXIS_NONE; + + g_object_get (grid, + "transform", &transform, + NULL); + + priv->orig_transform = *transform; + + g_free (transform); +} + +static gboolean +gimp_tool_transform_3d_grid_constrain (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y, + gdouble ox, + gdouble oy, + gdouble *tx, + gdouble *ty) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + + if (! priv->constrain_axis) + return TRUE; + + if (priv->constrained_axis == AXIS_NONE) + { + GimpDisplayShell *shell; + gdouble x1, y1; + gdouble x2, y2; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (grid)); + + gimp_display_shell_transform_xy_f (shell, + priv->last_x, priv->last_y, + &x1, &y1); + gimp_display_shell_transform_xy_f (shell, + x, y, + &x2, &y2); + + if (hypot (x2 - x1, y2 - y1) < CONSTRAINT_MIN_DIST) + return FALSE; + + if (fabs (*tx - ox) >= fabs (*ty - oy)) + priv->constrained_axis = AXIS_X; + else + priv->constrained_axis = AXIS_Y; + } + + if (priv->constrained_axis == AXIS_X) + *ty = oy; + else + *tx = ox; + + return TRUE; +} + +static gboolean +gimp_tool_transform_3d_grid_motion_vanishing_point (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + GimpCoords c = {}; + gdouble pivot_x; + gdouble pivot_y; + + if (! gimp_tool_transform_3d_grid_constrain (grid, + x, y, + priv->last_x, priv->last_y, + &x, &y)) + { + return FALSE; + } + + c.x = x; + c.y = y; + + GIMP_TOOL_WIDGET_CLASS (parent_class)->motion (GIMP_TOOL_WIDGET (grid), + &c, 0, 0); + + g_object_get (grid, + "pivot-x", &pivot_x, + "pivot-y", &pivot_y, + NULL); + + g_object_set (grid, + "camera-x", pivot_x, + "camera-y", pivot_y, + NULL); + + priv->last_x = c.x; + priv->last_y = c.y; + + return TRUE; +} + +static gboolean +gimp_tool_transform_3d_grid_motion_move (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + GimpMatrix4 matrix; + + if (! priv->z_axis) + { + gdouble x1, y1, z1, w1; + gdouble x2, y2, z2, w2; + + if (! priv->local_frame) + { + gimp_matrix4_identity (&matrix); + } + else + { + GimpMatrix3 transform_inv = priv->orig_transform;; + + gimp_matrix3_invert (&transform_inv); + + gimp_transform_3d_matrix3_to_matrix4 (&transform_inv, &matrix, 2); + } + + w1 = gimp_matrix4_transform_point (&matrix, + priv->last_x, priv->last_y, 0.0, + &x1, &y1, &z1); + w2 = gimp_matrix4_transform_point (&matrix, + x, y, 0.0, + &x2, &y2, &z2); + + if (w1 <= 0.0) + return FALSE; + + if (! gimp_tool_transform_3d_grid_constrain (grid, + x, y, + x1, y1, + &x2, &y2)) + { + return FALSE; + } + + if (priv->local_frame) + { + gimp_matrix4_identity (&matrix); + + gimp_transform_3d_matrix4_rotate_euler (&matrix, + priv->rotation_order, + priv->angle_x, + priv->angle_y, + priv->angle_z, + 0.0, 0.0, 0.0); + + gimp_matrix4_transform_point (&matrix, + x1, y1, z1, + &x1, &y1, &z1); + gimp_matrix4_transform_point (&matrix, + x2, y2, z2, + &x2, &y2, &z2); + } + + if (w2 > 0.0) + { + g_object_set (grid, + "offset-x", priv->offset_x + (x2 - x1), + "offset-y", priv->offset_y + (y2 - y1), + "offset-z", priv->offset_z + (z2 - z1), + NULL); + + priv->last_x = x; + priv->last_y = y; + } + else + { + g_object_set (grid, + "offset-x", priv->orig_offset_x, + "offset-y", priv->orig_offset_y, + "offset-z", priv->orig_offset_z, + NULL); + + priv->last_x = priv->orig_x; + priv->last_y = priv->orig_y; + } + } + else + { + GimpVector3 axis; + gdouble amount; + + if (! priv->local_frame) + { + axis.x = 0.0; + axis.y = 0.0; + axis.z = 1.0; + } + else + { + gimp_matrix4_identity (&matrix); + + gimp_transform_3d_matrix4_rotate_euler (&matrix, + priv->rotation_order, + priv->angle_x, + priv->angle_y, + priv->angle_z, + 0.0, 0.0, 0.0); + + axis.x = matrix.coeff[0][2]; + axis.y = matrix.coeff[1][2]; + axis.z = matrix.coeff[2][2]; + + if (axis.x < 0.0) + gimp_vector3_neg (&axis); + } + + amount = x - priv->last_x; + + g_object_set (grid, + "offset-x", priv->offset_x + axis.x * amount, + "offset-y", priv->offset_y + axis.y * amount, + "offset-z", priv->offset_z + axis.z * amount, + NULL); + + priv->last_x = x; + priv->last_y = y; + } + + return TRUE; +} + +static gboolean +gimp_tool_transform_3d_grid_motion_rotate (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + GimpDisplayShell *shell; + GimpMatrix4 matrix; + GimpMatrix2 basis_inv; + GimpVector3 omega; + gdouble z_sign; + gboolean local_frame; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (grid)); + + local_frame = priv->local_frame && (priv->constrain_axis || priv->z_axis); + + if (! local_frame) + { + gimp_matrix2_identity (&basis_inv); + z_sign = 1.0; + } + else + { + { + GimpVector3 o, n, c; + + gimp_matrix4_identity (&matrix); + + gimp_transform_3d_matrix4_rotate_euler (&matrix, + priv->rotation_order, + priv->angle_x, + priv->angle_y, + priv->angle_z, + priv->pivot_x, + priv->pivot_y, + priv->pivot_z); + + gimp_transform_3d_matrix4_translate (&matrix, + priv->offset_x, + priv->offset_y, + priv->offset_z); + + gimp_matrix4_transform_point (&matrix, + 0.0, 0.0, 0.0, + &o.x, &o.y, &o.z); + gimp_matrix4_transform_point (&matrix, + 0.0, 0.0, 1.0, + &n.x, &n.y, &n.z); + + c.x = priv->camera_x; + c.y = priv->camera_y; + c.z = priv->camera_z; + + gimp_vector3_sub (&n, &n, &o); + gimp_vector3_sub (&c, &c, &o); + + z_sign = gimp_vector3_inner_product (&c, &n) <= 0.0 ? +1.0 : -1.0; + } + + { + GimpVector2 o, u, v; + + gimp_matrix3_transform_point (&priv->orig_transform, + priv->pivot_x, priv->pivot_y, + &o.x, &o.y); + gimp_matrix3_transform_point (&priv->orig_transform, + priv->pivot_x + 1.0, priv->pivot_y, + &u.x, &u.y); + gimp_matrix3_transform_point (&priv->orig_transform, + priv->pivot_x, priv->pivot_y + 1.0, + &v.x, &v.y); + + gimp_vector2_sub (&u, &u, &o); + gimp_vector2_sub (&v, &v, &o); + + gimp_vector2_normalize (&u); + gimp_vector2_normalize (&v); + + basis_inv.coeff[0][0] = u.x; + basis_inv.coeff[1][0] = u.y; + basis_inv.coeff[0][1] = v.x; + basis_inv.coeff[1][1] = v.y; + + gimp_matrix2_invert (&basis_inv); + } + } + + if (! priv->z_axis) + { + GimpVector2 scale; + gdouble norm; + + gimp_matrix2_transform_point (&basis_inv, + -(y - priv->last_y), + x - priv->last_x, + &omega.x, &omega.y); + + omega.z = 0.0; + + if (! gimp_tool_transform_3d_grid_constrain (grid, + x, y, + 0.0, 0.0, + &omega.x, &omega.y)) + { + return FALSE; + } + + norm = gimp_vector3_length (&omega); + + if (norm > 0.0) + { + scale.x = shell->scale_x * omega.y / norm; + scale.y = shell->scale_y * omega.x / norm; + + gimp_vector3_mul (&omega, gimp_vector2_length (&scale)); + gimp_vector3_mul (&omega, 2.0 * G_PI / PIXELS_PER_REVOLUTION); + } + } + else + { + GimpVector2 o; + GimpVector2 v1 = {priv->last_x, priv->last_y}; + GimpVector2 v2 = {x, y}; + + g_warn_if_fail (priv->pivot_z == 0.0); + + gimp_matrix3_transform_point (&priv->orig_transform, + priv->pivot_x, priv->pivot_y, + &o.x, &o.y); + + gimp_vector2_sub (&v1, &v1, &o); + gimp_vector2_sub (&v2, &v2, &o); + + gimp_vector2_normalize (&v1); + gimp_vector2_normalize (&v2); + + omega.x = 0.0; + omega.y = 0.0; + omega.z = atan2 (gimp_vector2_cross_product (&v1, &v2).y, + gimp_vector2_inner_product (&v1, &v2)); + + omega.z *= z_sign; + } + + gimp_matrix4_identity (&matrix); + + if (local_frame) + gimp_transform_3d_matrix4_rotate (&matrix, &omega); + + gimp_transform_3d_matrix4_rotate_euler (&matrix, + priv->rotation_order, + priv->angle_x, + priv->angle_y, + priv->angle_z, + 0.0, 0.0, 0.0); + + if (! local_frame) + gimp_transform_3d_matrix4_rotate (&matrix, &omega); + + gimp_transform_3d_matrix4_rotate_euler_decompose (&matrix, + priv->rotation_order, + &priv->angle_x, + &priv->angle_y, + &priv->angle_z); + + priv->last_x = x; + priv->last_y = y; + + g_object_set (grid, + "angle-x", priv->angle_x, + "angle-y", priv->angle_y, + "angle-z", priv->angle_z, + NULL); + + return TRUE; +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_transform_3d_grid_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble camera_x, + gdouble camera_y, + gdouble camera_z) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_TRANSFORM_3D_GRID, + "shell", shell, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + "camera-x", camera_x, + "camera-y", camera_y, + "camera-z", camera_z, + "pivot-3d-x", (x1 + x2) / 2.0, + "pivot-3d-y", (y1 + y2) / 2.0, + "pivot-3d-z", 0.0, + NULL); +} |