837 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			837 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* PIKA - Photo and Image Kooker Application
 | |
|  * a rebranding of The GNU Image Manipulation Program (created with heckimp)
 | |
|  * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
 | |
|  *
 | |
|  * Original copyright, applying to most contents (license remains unchanged): 
 | |
|  *
 | |
|  * pikacageconfig.c
 | |
|  * Copyright (C) 2010 Michael Muré <batolettre@gmail.com>
 | |
|  *
 | |
|  * 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 <gdk-pixbuf/gdk-pixbuf.h>
 | |
| #include <gegl.h>
 | |
| 
 | |
| #include "libpikaconfig/pikaconfig.h"
 | |
| #include "libpikamath/pikamath.h"
 | |
| #include "libpikabase/pikabase.h"
 | |
| 
 | |
| #include "operations-types.h"
 | |
| 
 | |
| #include "pikacageconfig.h"
 | |
| 
 | |
| 
 | |
| /*#define DEBUG_CAGE */
 | |
| 
 | |
| /* This DELTA is aimed to not have handle on exact pixel during computation,
 | |
|  * to avoid particular case. It shouldn't be so useful, but it's a double
 | |
|  * safety. */
 | |
| #define DELTA             0.010309278351
 | |
| 
 | |
| 
 | |
| static void   pika_cage_config_finalize               (GObject        *object);
 | |
| static void   pika_cage_config_get_property           (GObject        *object,
 | |
|                                                        guint           property_id,
 | |
|                                                        GValue         *value,
 | |
|                                                        GParamSpec     *pspec);
 | |
| static void   pika_cage_config_set_property           (GObject        *object,
 | |
|                                                        guint           property_id,
 | |
|                                                        const GValue   *value,
 | |
|                                                        GParamSpec     *pspec);
 | |
| 
 | |
| static void   pika_cage_config_compute_scaling_factor (PikaCageConfig *gcc);
 | |
| static void   pika_cage_config_compute_edges_normal   (PikaCageConfig *gcc);
 | |
| 
 | |
| 
 | |
| G_DEFINE_TYPE_WITH_CODE (PikaCageConfig, pika_cage_config,
 | |
|                          PIKA_TYPE_OPERATION_SETTINGS,
 | |
|                          G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONFIG,
 | |
|                                                 NULL))
 | |
| 
 | |
| #define parent_class pika_cage_config_parent_class
 | |
| 
 | |
| #ifdef DEBUG_CAGE
 | |
| static void
 | |
| print_cage (PikaCageConfig *gcc)
 | |
| {
 | |
|   gint i;
 | |
|   GeglRectangle bounding_box;
 | |
|   PikaCagePoint *point;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
| 
 | |
|   bounding_box = pika_cage_config_get_bounding_box (gcc);
 | |
| 
 | |
|   for (i = 0; i < gcc->cage_points->len; i++)
 | |
|     {
 | |
|       point = &g_array_index (gcc->cage_points, PikaCagePoint, i);
 | |
|       g_printerr ("cgx: %.0f    cgy: %.0f    cvdx: %.0f    cvdy: %.0f  sf: %.2f  normx: %.2f  normy: %.2f %s\n",
 | |
|                   point->src_point.x + ((gcc->cage_mode==PIKA_CAGE_MODE_CAGE_CHANGE)?gcc->displacement_x:0),
 | |
|                   point->src_point.y + ((gcc->cage_mode==PIKA_CAGE_MODE_CAGE_CHANGE)?gcc->displacement_y:0),
 | |
|                   point->dest_point.x + ((gcc->cage_mode==PIKA_CAGE_MODE_DEFORM)?gcc->displacement_x:0),
 | |
|                   point->dest_point.y + ((gcc->cage_mode==PIKA_CAGE_MODE_DEFORM)?gcc->displacement_y:0),
 | |
|                   point->edge_scaling_factor,
 | |
|                   point->edge_normal.x,
 | |
|                   point->edge_normal.y,
 | |
|                   ((point->selected) ? "S" : "NS"));
 | |
|     }
 | |
| 
 | |
|   g_printerr ("bounding box: x: %d  y: %d  width: %d  height: %d\n", bounding_box.x, bounding_box.y, bounding_box.width, bounding_box.height);
 | |
|   g_printerr ("disp x: %f  disp y: %f\n", gcc->displacement_x, gcc->displacement_y);
 | |
|   g_printerr ("done\n");
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static void
 | |
| pika_cage_config_class_init (PikaCageConfigClass *klass)
 | |
| {
 | |
|   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 | |
| 
 | |
|   object_class->set_property = pika_cage_config_set_property;
 | |
|   object_class->get_property = pika_cage_config_get_property;
 | |
| 
 | |
|   object_class->finalize     = pika_cage_config_finalize;
 | |
| }
 | |
| 
 | |
| static void
 | |
| pika_cage_config_init (PikaCageConfig *self)
 | |
| {
 | |
|   /*pre-allocation for 50 vertices for the cage.*/
 | |
|   self->cage_points = g_array_sized_new (FALSE, FALSE, sizeof(PikaCagePoint), 50);
 | |
| }
 | |
| 
 | |
| static void
 | |
| pika_cage_config_finalize (GObject *object)
 | |
| {
 | |
|   PikaCageConfig *gcc = PIKA_CAGE_CONFIG (object);
 | |
| 
 | |
|   g_array_free (gcc->cage_points, TRUE);
 | |
| 
 | |
|   G_OBJECT_CLASS (parent_class)->finalize (object);
 | |
| }
 | |
| 
 | |
| static void
 | |
| pika_cage_config_get_property (GObject    *object,
 | |
|                                guint       property_id,
 | |
|                                GValue     *value,
 | |
|                                GParamSpec *pspec)
 | |
| {
 | |
|   switch (property_id)
 | |
|     {
 | |
|     default:
 | |
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 | |
|       break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| pika_cage_config_set_property (GObject      *object,
 | |
|                                guint         property_id,
 | |
|                                const GValue *value,
 | |
|                                GParamSpec   *pspec)
 | |
| {
 | |
|   switch (property_id)
 | |
|     {
 | |
|     default:
 | |
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 | |
|       break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_get_n_points:
 | |
|  * @gcc: the cage config
 | |
|  *
 | |
|  * Returns: the number of points of the cage
 | |
|  */
 | |
| guint
 | |
| pika_cage_config_get_n_points (PikaCageConfig *gcc)
 | |
| {
 | |
|   return gcc->cage_points->len;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_add_cage_point:
 | |
|  * @gcc: the cage config
 | |
|  * @x: x value of the new point
 | |
|  * @y: y value of the new point
 | |
|  *
 | |
|  * Add a new point in the last index of the polygon of the cage.
 | |
|  * Point is added in both source and destination cage
 | |
|  */
 | |
| void
 | |
| pika_cage_config_add_cage_point (PikaCageConfig  *gcc,
 | |
|                                  gdouble          x,
 | |
|                                  gdouble          y)
 | |
| {
 | |
|   pika_cage_config_insert_cage_point (gcc, gcc->cage_points->len, x, y);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_insert_cage_point:
 | |
|  * @gcc: the cage config
 | |
|  * @point_number: index where the point will be inserted
 | |
|  * @x: x value of the new point
 | |
|  * @y: y value of the new point
 | |
|  *
 | |
|  * Insert a new point in the polygon of the cage at the given index.
 | |
|  * Point is added in both source and destination cage
 | |
|  */
 | |
| void
 | |
| pika_cage_config_insert_cage_point (PikaCageConfig  *gcc,
 | |
|                                     gint             point_number,
 | |
|                                     gdouble          x,
 | |
|                                     gdouble          y)
 | |
| {
 | |
|   PikaCagePoint point;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
|   g_return_if_fail (point_number <= gcc->cage_points->len);
 | |
|   g_return_if_fail (point_number >= 0);
 | |
| 
 | |
|   point.src_point.x = x + DELTA;
 | |
|   point.src_point.y = y + DELTA;
 | |
| 
 | |
|   point.dest_point.x = x + DELTA;
 | |
|   point.dest_point.y = y + DELTA;
 | |
| 
 | |
|   g_array_insert_val (gcc->cage_points, point_number, point);
 | |
| 
 | |
|   pika_cage_config_compute_scaling_factor (gcc);
 | |
|   pika_cage_config_compute_edges_normal (gcc);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_remove_last_cage_point:
 | |
|  * @gcc: the cage config
 | |
|  *
 | |
|  * Remove the last point of the cage, in both source and destination cage
 | |
|  */
 | |
| void
 | |
| pika_cage_config_remove_last_cage_point (PikaCageConfig  *gcc)
 | |
| {
 | |
|   pika_cage_config_remove_cage_point (gcc, gcc->cage_points->len - 1);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_remove_cage_point:
 | |
|  * @gcc: the cage config
 | |
|  * @point_number: the index of the point to remove
 | |
|  *
 | |
|  * Remove the given point from the cage
 | |
|  */
 | |
| void
 | |
| pika_cage_config_remove_cage_point (PikaCageConfig *gcc,
 | |
|                                     gint            point_number)
 | |
| {
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
|   g_return_if_fail (point_number < gcc->cage_points->len);
 | |
|   g_return_if_fail (point_number >= 0);
 | |
| 
 | |
|   if (gcc->cage_points->len > 0)
 | |
|     g_array_remove_index (gcc->cage_points, gcc->cage_points->len - 1);
 | |
| 
 | |
|   pika_cage_config_compute_scaling_factor (gcc);
 | |
|   pika_cage_config_compute_edges_normal (gcc);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_remove_selected_points:
 | |
|  * @gcc: the cage config
 | |
|  *
 | |
|  * Remove all the selected points from the cage
 | |
|  */
 | |
| void
 | |
| pika_cage_config_remove_selected_points (PikaCageConfig  *gcc)
 | |
| {
 | |
|   gint           i;
 | |
|   PikaCagePoint *point;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
| 
 | |
|   for (i = 0; i < gcc->cage_points->len; i++)
 | |
|     {
 | |
|       point = &g_array_index (gcc->cage_points, PikaCagePoint, i);
 | |
| 
 | |
|       if (point->selected)
 | |
|         {
 | |
|           g_array_remove_index (gcc->cage_points, i);
 | |
|           i--;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   pika_cage_config_compute_scaling_factor (gcc);
 | |
|   pika_cage_config_compute_edges_normal (gcc);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_get_point_coordinate:
 | |
|  * @gcc: the cage config
 | |
|  * @mode: the actual mode of the cage, PIKA_CAGE_MODE_CAGE_CHANGE or PIKA_CAGE_MODE_DEFORM
 | |
|  * @point_number: the index of the point to return
 | |
|  *
 | |
|  * Returns: the real position of the given point, as a PikaVector2
 | |
|  */
 | |
| PikaVector2
 | |
| pika_cage_config_get_point_coordinate (PikaCageConfig *gcc,
 | |
|                                        PikaCageMode    mode,
 | |
|                                        gint            point_number)
 | |
| {
 | |
|   PikaVector2     result = { 0.0, 0.0 };
 | |
|   PikaCagePoint  *point;
 | |
| 
 | |
|   g_return_val_if_fail (PIKA_IS_CAGE_CONFIG (gcc), result);
 | |
|   g_return_val_if_fail (point_number < gcc->cage_points->len, result);
 | |
|   g_return_val_if_fail (point_number >= 0, result);
 | |
| 
 | |
|   point = &g_array_index (gcc->cage_points, PikaCagePoint, point_number);
 | |
| 
 | |
|   if (point->selected)
 | |
|     {
 | |
|       if (mode == PIKA_CAGE_MODE_CAGE_CHANGE)
 | |
|         {
 | |
|           result.x = point->src_point.x + gcc->displacement_x;
 | |
|           result.y = point->src_point.y + gcc->displacement_y;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           result.x = point->dest_point.x + gcc->displacement_x;
 | |
|           result.y = point->dest_point.y + gcc->displacement_y;
 | |
|         }
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       if (mode == PIKA_CAGE_MODE_CAGE_CHANGE)
 | |
|         {
 | |
|           result.x = point->src_point.x;
 | |
|           result.y = point->src_point.y;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           result.x = point->dest_point.x;
 | |
|           result.y = point->dest_point.y;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_add_displacement:
 | |
|  * @gcc: the cage config
 | |
|  * @mode: the actual mode of the cage, PIKA_CAGE_MODE_CAGE_CHANGE or PIKA_CAGE_MODE_DEFORM
 | |
|  * @point_number: the point of the cage to move
 | |
|  * @x: x displacement value
 | |
|  * @y: y displacement value
 | |
|  *
 | |
|  * Add a displacement for all selected points of the cage.
 | |
|  * This displacement need to be committed to become effective.
 | |
|  */
 | |
| void
 | |
| pika_cage_config_add_displacement (PikaCageConfig *gcc,
 | |
|                                    PikaCageMode    mode,
 | |
|                                    gdouble         x,
 | |
|                                    gdouble         y)
 | |
| {
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
| 
 | |
|   gcc->cage_mode = mode;
 | |
|   gcc->displacement_x = x;
 | |
|   gcc->displacement_y = y;
 | |
| 
 | |
|   #ifdef DEBUG_CAGE
 | |
|     print_cage (gcc);
 | |
|   #endif
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_commit_displacement:
 | |
|  * @gcc: the cage config
 | |
|  *
 | |
|  * Apply the displacement to the cage
 | |
|  */
 | |
| void
 | |
| pika_cage_config_commit_displacement (PikaCageConfig *gcc)
 | |
| {
 | |
|   gint  i;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
| 
 | |
|   for (i = 0; i < gcc->cage_points->len; i++)
 | |
|     {
 | |
|       PikaCagePoint *point;
 | |
|       point = &g_array_index (gcc->cage_points, PikaCagePoint, i);
 | |
| 
 | |
|       if (point->selected)
 | |
|         {
 | |
|           if (gcc->cage_mode == PIKA_CAGE_MODE_CAGE_CHANGE)
 | |
|             {
 | |
|               point->src_point.x += gcc->displacement_x;
 | |
|               point->src_point.y += gcc->displacement_y;
 | |
|               point->dest_point.x += gcc->displacement_x;
 | |
|               point->dest_point.y += gcc->displacement_y;
 | |
|             }
 | |
|           else
 | |
|             {
 | |
|               point->dest_point.x += gcc->displacement_x;
 | |
|               point->dest_point.y += gcc->displacement_y;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   pika_cage_config_compute_scaling_factor (gcc);
 | |
|   pika_cage_config_compute_edges_normal (gcc);
 | |
|   pika_cage_config_reset_displacement (gcc);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_reset_displacement:
 | |
|  * @gcc: the cage config
 | |
|  *
 | |
|  * Set the displacement to zero.
 | |
|  */
 | |
| void
 | |
| pika_cage_config_reset_displacement (PikaCageConfig *gcc)
 | |
| {
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
| 
 | |
|   gcc->displacement_x = 0.0;
 | |
|   gcc->displacement_y = 0.0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_get_bounding_box:
 | |
|  * @gcc: the cage config
 | |
|  *
 | |
|  * Compute the bounding box of the source cage
 | |
|  *
 | |
|  * Returns: the bounding box of the source cage, as a GeglRectangle
 | |
|  */
 | |
| GeglRectangle
 | |
| pika_cage_config_get_bounding_box (PikaCageConfig *gcc)
 | |
| {
 | |
|   GeglRectangle  bounding_box = { 0, 0, 0, 0};
 | |
|   gint           i;
 | |
|   PikaCagePoint *point;
 | |
| 
 | |
|   g_return_val_if_fail (PIKA_IS_CAGE_CONFIG (gcc), bounding_box);
 | |
| 
 | |
|   if (gcc->cage_points->len == 0)
 | |
|     return bounding_box;
 | |
| 
 | |
|   point = &g_array_index (gcc->cage_points, PikaCagePoint, 0);
 | |
| 
 | |
|   if (point->selected)
 | |
|     {
 | |
|       bounding_box.x = point->src_point.x + gcc->displacement_x;
 | |
|       bounding_box.y = point->src_point.y + gcc->displacement_y;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       bounding_box.x = point->src_point.x;
 | |
|       bounding_box.y = point->src_point.y;
 | |
|     }
 | |
| 
 | |
|   for (i = 1; i < gcc->cage_points->len; i++)
 | |
|     {
 | |
|       gdouble x,y;
 | |
|       point = &g_array_index (gcc->cage_points, PikaCagePoint, i);
 | |
| 
 | |
|       if (point->selected)
 | |
|         {
 | |
|           x = point->src_point.x + gcc->displacement_x;
 | |
|           y = point->src_point.y + gcc->displacement_y;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           x = point->src_point.x;
 | |
|           y = point->src_point.y;
 | |
|         }
 | |
| 
 | |
|       if (x < bounding_box.x)
 | |
|         {
 | |
|           bounding_box.width += bounding_box.x - x;
 | |
|           bounding_box.x = x;
 | |
|         }
 | |
| 
 | |
|       if (y < bounding_box.y)
 | |
|         {
 | |
|           bounding_box.height += bounding_box.y - y;
 | |
|           bounding_box.y = y;
 | |
|         }
 | |
| 
 | |
|       if (x > bounding_box.x + bounding_box.width)
 | |
|         {
 | |
|           bounding_box.width = x - bounding_box.x;
 | |
|         }
 | |
| 
 | |
|       if (y > bounding_box.y + bounding_box.height)
 | |
|         {
 | |
|           bounding_box.height = y - bounding_box.y;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   return bounding_box;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_reverse_cage:
 | |
|  * @gcc: the cage config
 | |
|  *
 | |
|  * When using non-simple cage (like a cage in 8), user may want to
 | |
|  * manually inverse inside and outside of the cage. This function
 | |
|  * reverse the cage
 | |
|  */
 | |
| void
 | |
| pika_cage_config_reverse_cage (PikaCageConfig *gcc)
 | |
| {
 | |
|   PikaCagePoint temp;
 | |
|   gint          i;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
| 
 | |
|   for (i = 0; i < gcc->cage_points->len / 2; i++)
 | |
|     {
 | |
|       temp = g_array_index (gcc->cage_points, PikaCagePoint, i);
 | |
| 
 | |
|       g_array_index (gcc->cage_points, PikaCagePoint, i) =
 | |
|         g_array_index (gcc->cage_points, PikaCagePoint, gcc->cage_points->len - i - 1);
 | |
| 
 | |
|       g_array_index (gcc->cage_points, PikaCagePoint, gcc->cage_points->len - i - 1) = temp;
 | |
|     }
 | |
| 
 | |
|   pika_cage_config_compute_scaling_factor (gcc);
 | |
|   pika_cage_config_compute_edges_normal (gcc);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_reverse_cage_if_needed:
 | |
|  * @gcc: the cage config
 | |
|  *
 | |
|  * Since the cage need to be defined counter-clockwise to have the
 | |
|  * topological inside in the actual 'physical' inside of the cage,
 | |
|  * this function compute if the cage is clockwise or not, and reverse
 | |
|  * the cage if needed.
 | |
|  *
 | |
|  * This function does not take into account an eventual displacement
 | |
|  */
 | |
| void
 | |
| pika_cage_config_reverse_cage_if_needed (PikaCageConfig *gcc)
 | |
| {
 | |
|   gint    i;
 | |
|   gdouble sum;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
| 
 | |
|   sum = 0.0;
 | |
| 
 | |
|   /* this is a bit crappy, but should works most of the case */
 | |
|   /* we do the sum of the projection of each point to the previous
 | |
|      segment, and see the final sign */
 | |
|   for (i = 0; i < gcc->cage_points->len ; i++)
 | |
|     {
 | |
|       PikaVector2 P1, P2, P3;
 | |
|       gdouble     z;
 | |
| 
 | |
|       P1 = (g_array_index (gcc->cage_points, PikaCagePoint, i)).src_point;
 | |
|       P2 = (g_array_index (gcc->cage_points, PikaCagePoint, (i+1) % gcc->cage_points->len)).src_point;
 | |
|       P3 = (g_array_index (gcc->cage_points, PikaCagePoint, (i+2) % gcc->cage_points->len)).src_point;
 | |
| 
 | |
|       z = P1.x * (P2.y - P3.y) + P2.x * (P3.y - P1.y) + P3.x * (P1.y - P2.y);
 | |
| 
 | |
|       sum += z;
 | |
|     }
 | |
| 
 | |
|   /* sum > 0 mean a cage defined counter-clockwise, so we reverse it */
 | |
|   if (sum > 0)
 | |
|     {
 | |
|       pika_cage_config_reverse_cage (gcc);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_compute_scaling_factor:
 | |
|  * @gcc: the cage config
 | |
|  *
 | |
|  * Update Green Coordinate scaling factor for the destination cage.
 | |
|  * This function does not take into account an eventual displacement.
 | |
|  */
 | |
| static void
 | |
| pika_cage_config_compute_scaling_factor (PikaCageConfig *gcc)
 | |
| {
 | |
|   PikaVector2    edge;
 | |
|   gdouble        length, length_d;
 | |
|   gint           i;
 | |
|   PikaCagePoint *current, *last;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
|   if (gcc->cage_points->len < 2)
 | |
|     return;
 | |
| 
 | |
|   last = &g_array_index (gcc->cage_points, PikaCagePoint, 0);
 | |
| 
 | |
|   for (i = 1; i <= gcc->cage_points->len; i++)
 | |
|     {
 | |
|       current = &g_array_index (gcc->cage_points, PikaCagePoint, i % gcc->cage_points->len);
 | |
| 
 | |
|       pika_vector2_sub (&edge,
 | |
|                         &(last->src_point),
 | |
|                         &(current->src_point));
 | |
|       length = pika_vector2_length (&edge);
 | |
| 
 | |
|       pika_vector2_sub (&edge,
 | |
|                         &(last->dest_point),
 | |
|                         &(current->dest_point));
 | |
|       length_d = pika_vector2_length (&edge);
 | |
| 
 | |
|       last->edge_scaling_factor = length_d / length;
 | |
|       last = current;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_compute_edges_normal:
 | |
|  * @gcc: the cage config
 | |
|  *
 | |
|  * Update edges normal for the destination cage.
 | |
|  * This function does not take into account an eventual displacement.
 | |
|  */
 | |
| static void
 | |
| pika_cage_config_compute_edges_normal (PikaCageConfig *gcc)
 | |
| {
 | |
|   PikaVector2    normal;
 | |
|   gint           i;
 | |
|   PikaCagePoint *current, *last;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
| 
 | |
|   last = &g_array_index (gcc->cage_points, PikaCagePoint, 0);
 | |
| 
 | |
|   for (i = 1; i <= gcc->cage_points->len; i++)
 | |
|     {
 | |
|       current = &g_array_index (gcc->cage_points, PikaCagePoint, i % gcc->cage_points->len);
 | |
| 
 | |
|       pika_vector2_sub (&normal,
 | |
|                         &(current->dest_point),
 | |
|                         &(last->dest_point));
 | |
| 
 | |
|       last->edge_normal = pika_vector2_normal (&normal);
 | |
|       last = current;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_point_inside:
 | |
|  * @gcc: the cage config
 | |
|  * @x: x coordinate of the point to test
 | |
|  * @y: y coordinate of the point to test
 | |
|  *
 | |
|  * Check if the given point is inside the cage. This test is done in
 | |
|  * the regard of the topological inside of the source cage.
 | |
|  *
 | |
|  * Returns: TRUE if the point is inside, FALSE if not.
 | |
|  * This function does not take into account an eventual displacement.
 | |
|  */
 | |
| gboolean
 | |
| pika_cage_config_point_inside (PikaCageConfig *gcc,
 | |
|                                gfloat          x,
 | |
|                                gfloat          y)
 | |
| {
 | |
|   PikaVector2   *last, *current;
 | |
|   gboolean       inside = FALSE;
 | |
|   gint           i;
 | |
| 
 | |
|   g_return_val_if_fail (PIKA_IS_CAGE_CONFIG (gcc), FALSE);
 | |
| 
 | |
|   last = &((g_array_index (gcc->cage_points, PikaCagePoint, gcc->cage_points->len - 1)).src_point);
 | |
| 
 | |
|   for (i = 0; i < gcc->cage_points->len; i++)
 | |
|     {
 | |
|       current = &((g_array_index (gcc->cage_points, PikaCagePoint, i)).src_point);
 | |
| 
 | |
|       if ((((current->y <= y) && (y < last->y))
 | |
|            || ((last->y <= y) && (y < current->y)))
 | |
|           && (x < (last->x - current->x) * (y - current->y) / (last->y - current->y) + current->x))
 | |
|         {
 | |
|           inside = !inside;
 | |
|         }
 | |
| 
 | |
|       last = current;
 | |
|     }
 | |
| 
 | |
|   return inside;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_select_point:
 | |
|  * @gcc: the cage config
 | |
|  * @point_number: the index of the point to select
 | |
|  *
 | |
|  * Select the given point of the cage, and deselect the others.
 | |
|  */
 | |
| void
 | |
| pika_cage_config_select_point (PikaCageConfig  *gcc,
 | |
|                                gint             point_number)
 | |
| {
 | |
|   gint           i;
 | |
|   PikaCagePoint *point;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
|   g_return_if_fail (point_number < gcc->cage_points->len);
 | |
|   g_return_if_fail (point_number >= 0);
 | |
| 
 | |
|   for (i = 0; i < gcc->cage_points->len; i++)
 | |
|     {
 | |
|       point = &g_array_index (gcc->cage_points, PikaCagePoint, i);
 | |
| 
 | |
|       if (i == point_number)
 | |
|         {
 | |
|           point->selected = TRUE;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           point->selected = FALSE;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_select_area:
 | |
|  * @gcc: the cage config
 | |
|  * @mode: the actual mode of the cage, PIKA_CAGE_MODE_CAGE_CHANGE or PIKA_CAGE_MODE_DEFORM
 | |
|  * @area: the area to select
 | |
|  *
 | |
|  * Select cage's point inside the given area and deselect others
 | |
|  */
 | |
| void
 | |
| pika_cage_config_select_area  (PikaCageConfig  *gcc,
 | |
|                                PikaCageMode     mode,
 | |
|                                GeglRectangle    area)
 | |
| {
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
| 
 | |
|   pika_cage_config_deselect_points (gcc);
 | |
|   pika_cage_config_select_add_area (gcc, mode, area);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_select_add_area:
 | |
|  * @gcc: the cage config
 | |
|  * @mode: the actual mode of the cage, PIKA_CAGE_MODE_CAGE_CHANGE or PIKA_CAGE_MODE_DEFORM
 | |
|  * @area: the area to select
 | |
|  *
 | |
|  * Select cage's point inside the given area. Already selected point stay selected.
 | |
|  */
 | |
| void
 | |
| pika_cage_config_select_add_area (PikaCageConfig *gcc,
 | |
|                                   PikaCageMode    mode,
 | |
|                                   GeglRectangle   area)
 | |
| {
 | |
|   gint           i;
 | |
|   PikaCagePoint *point;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
| 
 | |
|   for (i = 0; i < gcc->cage_points->len; i++)
 | |
|     {
 | |
|       point = &g_array_index (gcc->cage_points, PikaCagePoint, i);
 | |
| 
 | |
|       if (mode == PIKA_CAGE_MODE_CAGE_CHANGE)
 | |
|         {
 | |
|           if (point->src_point.x >= area.x &&
 | |
|               point->src_point.x <= area.x + area.width &&
 | |
|               point->src_point.y >= area.y &&
 | |
|               point->src_point.y <= area.y + area.height)
 | |
|             {
 | |
|               point->selected = TRUE;
 | |
|             }
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           if (point->dest_point.x >= area.x &&
 | |
|               point->dest_point.x <= area.x + area.width &&
 | |
|               point->dest_point.y >= area.y &&
 | |
|               point->dest_point.y <= area.y + area.height)
 | |
|             {
 | |
|               point->selected = TRUE;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_toggle_point_selection:
 | |
|  * @gcc: the cage config
 | |
|  * @point_number: the index of the point to toggle selection
 | |
|  *
 | |
|  * Toggle the selection of the given cage point
 | |
|  */
 | |
| void
 | |
| pika_cage_config_toggle_point_selection (PikaCageConfig *gcc,
 | |
|                                          gint            point_number)
 | |
| {
 | |
|   PikaCagePoint *point;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
|   g_return_if_fail (point_number < gcc->cage_points->len);
 | |
|   g_return_if_fail (point_number >= 0);
 | |
| 
 | |
|   point = &g_array_index (gcc->cage_points, PikaCagePoint, point_number);
 | |
|   point->selected = ! point->selected;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_deselect_points:
 | |
|  * @gcc: the cage config
 | |
|  *
 | |
|  * Deselect all cage points.
 | |
|  */
 | |
| void
 | |
| pika_cage_config_deselect_points (PikaCageConfig *gcc)
 | |
| {
 | |
|   gint  i;
 | |
| 
 | |
|   g_return_if_fail (PIKA_IS_CAGE_CONFIG (gcc));
 | |
| 
 | |
|   for (i = 0; i < gcc->cage_points->len; i++)
 | |
|     {
 | |
|       (g_array_index (gcc->cage_points, PikaCagePoint, i)).selected = FALSE;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pika_cage_config_point_is_selected:
 | |
|  * @gcc: the cage config
 | |
|  * @point_number: the index of the point to test
 | |
|  *
 | |
|  * Returns: TRUE if the point is selected, FALSE otherwise.
 | |
|  */
 | |
| gboolean
 | |
| pika_cage_config_point_is_selected (PikaCageConfig  *gcc,
 | |
|                                     gint             point_number)
 | |
| {
 | |
|   PikaCagePoint *point;
 | |
| 
 | |
|   g_return_val_if_fail (PIKA_IS_CAGE_CONFIG (gcc), FALSE);
 | |
|   g_return_val_if_fail (point_number < gcc->cage_points->len, FALSE);
 | |
|   g_return_val_if_fail (point_number >= 0, FALSE);
 | |
| 
 | |
|   point = &(g_array_index (gcc->cage_points, PikaCagePoint, point_number));
 | |
| 
 | |
|   return point->selected;
 | |
| }
 |