diff options
Diffstat (limited to 'app/core/gimpsymmetry-mandala.c')
-rw-r--r-- | app/core/gimpsymmetry-mandala.c | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/app/core/gimpsymmetry-mandala.c b/app/core/gimpsymmetry-mandala.c new file mode 100644 index 0000000..668819a --- /dev/null +++ b/app/core/gimpsymmetry-mandala.c @@ -0,0 +1,574 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsymmetry-mandala.c + * Copyright (C) 2015 Jehan <jehan@gimp.org> + * + * 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 <string.h> + +#include <cairo.h> +#include <gegl.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-cairo.h" +#include "gimpdrawable.h" +#include "gimpguide.h" +#include "gimpimage.h" +#include "gimpimage-guides.h" +#include "gimpimage-symmetry.h" +#include "gimpitem.h" +#include "gimpsymmetry-mandala.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_CENTER_X, + PROP_CENTER_Y, + PROP_SIZE, + PROP_DISABLE_TRANSFORMATION, + PROP_ENABLE_REFLECTION, +}; + + +/* Local function prototypes */ + +static void gimp_mandala_constructed (GObject *object); +static void gimp_mandala_finalize (GObject *object); +static void gimp_mandala_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_mandala_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_mandala_active_changed (GimpSymmetry *sym); + +static void gimp_mandala_add_guide (GimpMandala *mandala, + GimpOrientationType orientation); +static void gimp_mandala_remove_guide (GimpMandala *mandala, + GimpOrientationType orientation); +static void gimp_mandala_guide_removed_cb (GObject *object, + GimpMandala *mandala); +static void gimp_mandala_guide_position_cb (GObject *object, + GParamSpec *pspec, + GimpMandala *mandala); + +static void gimp_mandala_update_strokes (GimpSymmetry *mandala, + GimpDrawable *drawable, + GimpCoords *origin); +static void gimp_mandala_get_transform (GimpSymmetry *mandala, + gint stroke, + gdouble *angle, + gboolean *reflect); +static void gimp_mandala_image_size_changed_cb (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height, + GimpSymmetry *sym); + + +G_DEFINE_TYPE (GimpMandala, gimp_mandala, GIMP_TYPE_SYMMETRY) + +#define parent_class gimp_mandala_parent_class + + +static void +gimp_mandala_class_init (GimpMandalaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpSymmetryClass *symmetry_class = GIMP_SYMMETRY_CLASS (klass); + GParamSpec *pspec; + + object_class->constructed = gimp_mandala_constructed; + object_class->finalize = gimp_mandala_finalize; + object_class->set_property = gimp_mandala_set_property; + object_class->get_property = gimp_mandala_get_property; + + symmetry_class->label = _("Mandala"); + symmetry_class->update_strokes = gimp_mandala_update_strokes; + symmetry_class->get_transform = gimp_mandala_get_transform; + symmetry_class->active_changed = gimp_mandala_active_changed; + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_CENTER_X, + "center-x", + _("Center abscissa"), + NULL, + 0.0, G_MAXDOUBLE, 0.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + pspec = g_object_class_find_property (object_class, "center-x"); + gegl_param_spec_set_property_key (pspec, "unit", "pixel-coordinate"); + gegl_param_spec_set_property_key (pspec, "axis", "x"); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_CENTER_Y, + "center-y", + _("Center ordinate"), + NULL, + 0.0, G_MAXDOUBLE, 0.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + pspec = g_object_class_find_property (object_class, "center-y"); + gegl_param_spec_set_property_key (pspec, "unit", "pixel-coordinate"); + gegl_param_spec_set_property_key (pspec, "axis", "y"); + + GIMP_CONFIG_PROP_INT (object_class, PROP_SIZE, + "size", + _("Number of points"), + NULL, + 1, 100, 6.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DISABLE_TRANSFORMATION, + "disable-transformation", + _("Disable brush transform"), + _("Disable brush rotation"), + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_ENABLE_REFLECTION, + "enable-reflection", + _("Kaleidoscope"), + _("Reflect consecutive strokes"), + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); +} + +static void +gimp_mandala_init (GimpMandala *mandala) +{ +} + +static void +gimp_mandala_constructed (GObject *object) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (object); + + g_signal_connect_object (sym->image, "size-changed-detailed", + G_CALLBACK (gimp_mandala_image_size_changed_cb), + sym, 0); +} + +static void +gimp_mandala_finalize (GObject *object) +{ + GimpMandala *mandala = GIMP_MANDALA (object); + + g_clear_object (&mandala->horizontal_guide); + g_clear_object (&mandala->vertical_guide); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_mandala_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpMandala *mandala = GIMP_MANDALA (object); + GimpImage *image = GIMP_SYMMETRY (mandala)->image; + + switch (property_id) + { + case PROP_CENTER_X: + if (g_value_get_double (value) > 0.0 && + g_value_get_double (value) < (gdouble) gimp_image_get_width (image)) + { + mandala->center_x = g_value_get_double (value); + + if (mandala->vertical_guide) + { + g_signal_handlers_block_by_func (mandala->vertical_guide, + gimp_mandala_guide_position_cb, + mandala); + gimp_image_move_guide (image, mandala->vertical_guide, + mandala->center_x, + FALSE); + g_signal_handlers_unblock_by_func (mandala->vertical_guide, + gimp_mandala_guide_position_cb, + mandala); + } + } + break; + + case PROP_CENTER_Y: + if (g_value_get_double (value) > 0.0 && + g_value_get_double (value) < (gdouble) gimp_image_get_height (image)) + { + mandala->center_y = g_value_get_double (value); + + if (mandala->horizontal_guide) + { + g_signal_handlers_block_by_func (mandala->horizontal_guide, + gimp_mandala_guide_position_cb, + mandala); + gimp_image_move_guide (image, mandala->horizontal_guide, + mandala->center_y, + FALSE); + g_signal_handlers_unblock_by_func (mandala->horizontal_guide, + gimp_mandala_guide_position_cb, + mandala); + } + } + break; + + case PROP_SIZE: + mandala->size = g_value_get_int (value); + break; + + case PROP_DISABLE_TRANSFORMATION: + mandala->disable_transformation = g_value_get_boolean (value); + break; + + case PROP_ENABLE_REFLECTION: + mandala->enable_reflection = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_mandala_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpMandala *mandala = GIMP_MANDALA (object); + + switch (property_id) + { + case PROP_CENTER_X: + g_value_set_double (value, mandala->center_x); + break; + case PROP_CENTER_Y: + g_value_set_double (value, mandala->center_y); + break; + case PROP_SIZE: + g_value_set_int (value, mandala->size); + break; + case PROP_DISABLE_TRANSFORMATION: + g_value_set_boolean (value, mandala->disable_transformation); + break; + case PROP_ENABLE_REFLECTION: + g_value_set_boolean (value, mandala->enable_reflection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_mandala_active_changed (GimpSymmetry *sym) +{ + GimpMandala *mandala = GIMP_MANDALA (sym); + + if (sym->active) + { + if (! mandala->horizontal_guide) + gimp_mandala_add_guide (mandala, GIMP_ORIENTATION_HORIZONTAL); + + if (! mandala->vertical_guide) + gimp_mandala_add_guide (mandala, GIMP_ORIENTATION_VERTICAL); + } + else + { + if (mandala->horizontal_guide) + gimp_mandala_remove_guide (mandala, GIMP_ORIENTATION_HORIZONTAL); + + if (mandala->vertical_guide) + gimp_mandala_remove_guide (mandala, GIMP_ORIENTATION_VERTICAL); + } +} + +static void +gimp_mandala_add_guide (GimpMandala *mandala, + GimpOrientationType orientation) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (mandala); + GimpImage *image; + Gimp *gimp; + GimpGuide *guide; + gint position; + + image = sym->image; + gimp = image->gimp; + + guide = gimp_guide_custom_new (orientation, + gimp->next_guide_ID++, + GIMP_GUIDE_STYLE_MANDALA); + + if (orientation == GIMP_ORIENTATION_HORIZONTAL) + { + mandala->horizontal_guide = guide; + + /* Mandala guide position at first activation is at canvas middle. */ + if (mandala->center_y < 1.0) + mandala->center_y = (gdouble) gimp_image_get_height (image) / 2.0; + + position = (gint) mandala->center_y; + } + else + { + mandala->vertical_guide = guide; + + /* Mandala guide position at first activation is at canvas middle. */ + if (mandala->center_x < 1.0) + mandala->center_x = (gdouble) gimp_image_get_width (image) / 2.0; + + position = (gint) mandala->center_x; + } + + g_signal_connect (guide, "removed", + G_CALLBACK (gimp_mandala_guide_removed_cb), + mandala); + + gimp_image_add_guide (image, guide, position); + + g_signal_connect (guide, "notify::position", + G_CALLBACK (gimp_mandala_guide_position_cb), + mandala); +} + +static void +gimp_mandala_remove_guide (GimpMandala *mandala, + GimpOrientationType orientation) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (mandala); + GimpImage *image; + GimpGuide *guide; + + image = sym->image; + guide = (orientation == GIMP_ORIENTATION_HORIZONTAL) ? + mandala->horizontal_guide : mandala->vertical_guide; + + g_signal_handlers_disconnect_by_func (guide, + gimp_mandala_guide_removed_cb, + mandala); + g_signal_handlers_disconnect_by_func (guide, + gimp_mandala_guide_position_cb, + mandala); + + gimp_image_remove_guide (image, guide, FALSE); + g_object_unref (guide); + + if (orientation == GIMP_ORIENTATION_HORIZONTAL) + mandala->horizontal_guide = NULL; + else + mandala->vertical_guide = NULL; +} + +static void +gimp_mandala_guide_removed_cb (GObject *object, + GimpMandala *mandala) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (mandala); + + g_signal_handlers_disconnect_by_func (object, + gimp_mandala_guide_removed_cb, + mandala); + g_signal_handlers_disconnect_by_func (object, + gimp_mandala_guide_position_cb, + mandala); + + if (GIMP_GUIDE (object) == mandala->horizontal_guide) + { + g_object_unref (mandala->horizontal_guide); + + mandala->horizontal_guide = NULL; + mandala->center_y = 0.0; + + gimp_mandala_remove_guide (mandala, GIMP_ORIENTATION_VERTICAL); + } + else if (GIMP_GUIDE (object) == mandala->vertical_guide) + { + g_object_unref (mandala->vertical_guide); + mandala->vertical_guide = NULL; + mandala->center_x = 0.0; + + gimp_mandala_remove_guide (mandala, GIMP_ORIENTATION_HORIZONTAL); + } + + gimp_image_symmetry_remove (sym->image, sym); +} + +static void +gimp_mandala_guide_position_cb (GObject *object, + GParamSpec *pspec, + GimpMandala *mandala) +{ + GimpGuide *guide = GIMP_GUIDE (object); + + if (guide == mandala->horizontal_guide) + { + g_object_set (G_OBJECT (mandala), + "center-y", (gdouble) gimp_guide_get_position (guide), + NULL); + } + else if (guide == mandala->vertical_guide) + { + g_object_set (G_OBJECT (mandala), + "center-x", (gdouble) gimp_guide_get_position (guide), + NULL); + } +} + +static void +gimp_mandala_update_strokes (GimpSymmetry *sym, + GimpDrawable *drawable, + GimpCoords *origin) +{ + GimpMandala *mandala = GIMP_MANDALA (sym); + GimpCoords *coords; + GimpMatrix3 matrix; + gdouble slice_angle; + gdouble mid_slice_angle = 0.0; + gdouble center_x, center_y; + gint offset_x, offset_y; + gint i; + + gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y); + + center_x = mandala->center_x - offset_x; + center_y = mandala->center_y - offset_y; + + g_list_free_full (sym->strokes, g_free); + sym->strokes = NULL; + + coords = g_memdup (sym->origin, sizeof (GimpCoords)); + sym->strokes = g_list_prepend (sym->strokes, coords); + + /* The angle of each slice, in radians. */ + slice_angle = 2.0 * G_PI / mandala->size; + + if (mandala->enable_reflection) + { + /* Find out in which slice the user is currently drawing. */ + gdouble angle = atan2 (sym->origin->y - center_y, + sym->origin->x - center_x); + gint slice_no = (int) floor(angle/slice_angle); + + /* Angle where the middle of that slice is. */ + mid_slice_angle = slice_no * slice_angle + slice_angle / 2.0; + } + + for (i = 1; i < mandala->size; i++) + { + gdouble new_x, new_y; + + coords = g_memdup (sym->origin, sizeof (GimpCoords)); + gimp_matrix3_identity (&matrix); + gimp_matrix3_translate (&matrix, + -center_x, + -center_y); + if (mandala->enable_reflection && i % 2 == 1) + { + /* Reflecting over the mid_slice_angle axis, reflects slice without changing position. */ + gimp_matrix3_rotate(&matrix, -mid_slice_angle); + gimp_matrix3_scale (&matrix, 1, -1); + gimp_matrix3_rotate(&matrix, mid_slice_angle - i * slice_angle); + } + else + { + gimp_matrix3_rotate (&matrix, - i * slice_angle); + } + gimp_matrix3_translate (&matrix, + +center_x, + +center_y); + gimp_matrix3_transform_point (&matrix, + coords->x, + coords->y, + &new_x, + &new_y); + coords->x = new_x; + coords->y = new_y; + sym->strokes = g_list_prepend (sym->strokes, coords); + } + + sym->strokes = g_list_reverse (sym->strokes); + + g_signal_emit_by_name (sym, "strokes-updated", sym->image); +} + +static void +gimp_mandala_get_transform (GimpSymmetry *sym, + gint stroke, + gdouble *angle, + gboolean *reflect) +{ + GimpMandala *mandala = GIMP_MANDALA (sym); + gdouble slice_angle; + + if (mandala->disable_transformation) + return; + + slice_angle = 360.0 / mandala->size; + + if (mandala->enable_reflection && stroke % 2 == 1) + { + /* Find out in which slice the user is currently drawing. */ + gdouble origin_angle = gimp_rad_to_deg (atan2 (sym->origin->y - mandala->center_y, + sym->origin->x - mandala->center_x)); + gint slice_no = (int) floor(origin_angle/slice_angle); + + /* Angle where the middle of that slice is. */ + gdouble mid_slice_angle = slice_no * slice_angle + slice_angle / 2.0; + + *angle = 180.0 - (-2 * mid_slice_angle + stroke * slice_angle) ; + *reflect = TRUE; + } + else + { + *angle = stroke * slice_angle; + } +} + +static void +gimp_mandala_image_size_changed_cb (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height, + GimpSymmetry *sym) +{ + if (previous_width != gimp_image_get_width (image) || + previous_height != gimp_image_get_height (image)) + { + g_signal_emit_by_name (sym, "gui-param-changed", sym->image); + } +} |