/* 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): * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis * * 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 . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "libpikamath/pikamath.h" #include "libpikaconfig/pikaconfig.h" #include "core-types.h" #include "pikacurve.h" #include "pikadynamics.h" #include "pikadynamics-load.h" #include "pikadynamics-save.h" #include "pikadynamicsoutput.h" #include "pika-intl.h" #define DEFAULT_NAME "Nameless dynamics" enum { PROP_0, PROP_NAME, PROP_OPACITY_OUTPUT, PROP_SIZE_OUTPUT, PROP_ANGLE_OUTPUT, PROP_COLOR_OUTPUT, PROP_FORCE_OUTPUT, PROP_HARDNESS_OUTPUT, PROP_ASPECT_RATIO_OUTPUT, PROP_SPACING_OUTPUT, PROP_RATE_OUTPUT, PROP_FLOW_OUTPUT, PROP_JITTER_OUTPUT }; typedef struct _PikaDynamicsPrivate PikaDynamicsPrivate; struct _PikaDynamicsPrivate { PikaDynamicsOutput *opacity_output; PikaDynamicsOutput *hardness_output; PikaDynamicsOutput *force_output; PikaDynamicsOutput *rate_output; PikaDynamicsOutput *flow_output; PikaDynamicsOutput *size_output; PikaDynamicsOutput *aspect_ratio_output; PikaDynamicsOutput *color_output; PikaDynamicsOutput *angle_output; PikaDynamicsOutput *jitter_output; PikaDynamicsOutput *spacing_output; }; #define GET_PRIVATE(output) \ ((PikaDynamicsPrivate *) pika_dynamics_get_instance_private ((PikaDynamics *) (output))) static void pika_dynamics_finalize (GObject *object); static void pika_dynamics_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_dynamics_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void pika_dynamics_dispatch_properties_changed (GObject *object, guint n_pspecs, GParamSpec **pspecs); static const gchar * pika_dynamics_get_extension (PikaData *data); static void pika_dynamics_copy (PikaData *data, PikaData *src_data); static PikaDynamicsOutput * pika_dynamics_create_output (PikaDynamics *dynamics, const gchar *name, PikaDynamicsOutputType type); static void pika_dynamics_output_notify (GObject *output, const GParamSpec *pspec, PikaDynamics *dynamics); G_DEFINE_TYPE_WITH_PRIVATE (PikaDynamics, pika_dynamics, PIKA_TYPE_DATA) #define parent_class pika_dynamics_parent_class static void pika_dynamics_class_init (PikaDynamicsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); PikaDataClass *data_class = PIKA_DATA_CLASS (klass); PikaViewableClass *viewable_class = PIKA_VIEWABLE_CLASS (klass); object_class->finalize = pika_dynamics_finalize; object_class->set_property = pika_dynamics_set_property; object_class->get_property = pika_dynamics_get_property; object_class->dispatch_properties_changed = pika_dynamics_dispatch_properties_changed; viewable_class->default_icon_name = "pika-dynamics"; data_class->save = pika_dynamics_save; data_class->get_extension = pika_dynamics_get_extension; data_class->copy = pika_dynamics_copy; PIKA_CONFIG_PROP_STRING (object_class, PROP_NAME, "name", NULL, NULL, DEFAULT_NAME, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_OBJECT (object_class, PROP_OPACITY_OUTPUT, "opacity-output", NULL, NULL, PIKA_TYPE_DYNAMICS_OUTPUT, PIKA_CONFIG_PARAM_AGGREGATE); PIKA_CONFIG_PROP_OBJECT (object_class, PROP_FORCE_OUTPUT, "force-output", NULL, NULL, PIKA_TYPE_DYNAMICS_OUTPUT, PIKA_CONFIG_PARAM_AGGREGATE); PIKA_CONFIG_PROP_OBJECT (object_class, PROP_HARDNESS_OUTPUT, "hardness-output", NULL, NULL, PIKA_TYPE_DYNAMICS_OUTPUT, PIKA_CONFIG_PARAM_AGGREGATE); PIKA_CONFIG_PROP_OBJECT (object_class, PROP_RATE_OUTPUT, "rate-output", NULL, NULL, PIKA_TYPE_DYNAMICS_OUTPUT, PIKA_CONFIG_PARAM_AGGREGATE); PIKA_CONFIG_PROP_OBJECT (object_class, PROP_FLOW_OUTPUT, "flow-output", NULL, NULL, PIKA_TYPE_DYNAMICS_OUTPUT, PIKA_CONFIG_PARAM_AGGREGATE); PIKA_CONFIG_PROP_OBJECT (object_class, PROP_SIZE_OUTPUT, "size-output", NULL, NULL, PIKA_TYPE_DYNAMICS_OUTPUT, PIKA_CONFIG_PARAM_AGGREGATE); PIKA_CONFIG_PROP_OBJECT (object_class, PROP_ASPECT_RATIO_OUTPUT, "aspect-ratio-output", NULL, NULL, PIKA_TYPE_DYNAMICS_OUTPUT, PIKA_CONFIG_PARAM_AGGREGATE); PIKA_CONFIG_PROP_OBJECT (object_class, PROP_COLOR_OUTPUT, "color-output", NULL, NULL, PIKA_TYPE_DYNAMICS_OUTPUT, PIKA_CONFIG_PARAM_AGGREGATE); PIKA_CONFIG_PROP_OBJECT (object_class, PROP_ANGLE_OUTPUT, "angle-output", NULL, NULL, PIKA_TYPE_DYNAMICS_OUTPUT, PIKA_CONFIG_PARAM_AGGREGATE); PIKA_CONFIG_PROP_OBJECT (object_class, PROP_JITTER_OUTPUT, "jitter-output", NULL, NULL, PIKA_TYPE_DYNAMICS_OUTPUT, PIKA_CONFIG_PARAM_AGGREGATE); PIKA_CONFIG_PROP_OBJECT (object_class, PROP_SPACING_OUTPUT, "spacing-output", NULL, NULL, PIKA_TYPE_DYNAMICS_OUTPUT, PIKA_CONFIG_PARAM_AGGREGATE); } static void pika_dynamics_init (PikaDynamics *dynamics) { PikaDynamicsPrivate *private = GET_PRIVATE (dynamics); private->opacity_output = pika_dynamics_create_output (dynamics, "opacity-output", PIKA_DYNAMICS_OUTPUT_OPACITY); private->force_output = pika_dynamics_create_output (dynamics, "force-output", PIKA_DYNAMICS_OUTPUT_FORCE); private->hardness_output = pika_dynamics_create_output (dynamics, "hardness-output", PIKA_DYNAMICS_OUTPUT_HARDNESS); private->rate_output = pika_dynamics_create_output (dynamics, "rate-output", PIKA_DYNAMICS_OUTPUT_RATE); private->flow_output = pika_dynamics_create_output (dynamics, "flow-output", PIKA_DYNAMICS_OUTPUT_FLOW); private->size_output = pika_dynamics_create_output (dynamics, "size-output", PIKA_DYNAMICS_OUTPUT_SIZE); private->aspect_ratio_output = pika_dynamics_create_output (dynamics, "aspect-ratio-output", PIKA_DYNAMICS_OUTPUT_ASPECT_RATIO); private->color_output = pika_dynamics_create_output (dynamics, "color-output", PIKA_DYNAMICS_OUTPUT_COLOR); private->angle_output = pika_dynamics_create_output (dynamics, "angle-output", PIKA_DYNAMICS_OUTPUT_ANGLE); private->jitter_output = pika_dynamics_create_output (dynamics, "jitter-output", PIKA_DYNAMICS_OUTPUT_JITTER); private->spacing_output = pika_dynamics_create_output (dynamics, "spacing-output", PIKA_DYNAMICS_OUTPUT_SPACING); } static void pika_dynamics_finalize (GObject *object) { PikaDynamicsPrivate *private = GET_PRIVATE (object); g_clear_object (&private->opacity_output); g_clear_object (&private->force_output); g_clear_object (&private->hardness_output); g_clear_object (&private->rate_output); g_clear_object (&private->flow_output); g_clear_object (&private->size_output); g_clear_object (&private->aspect_ratio_output); g_clear_object (&private->color_output); g_clear_object (&private->angle_output); g_clear_object (&private->jitter_output); g_clear_object (&private->spacing_output); G_OBJECT_CLASS (parent_class)->finalize (object); } static void pika_dynamics_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaDynamicsPrivate *private = GET_PRIVATE (object); PikaDynamicsOutput *src_output = NULL; PikaDynamicsOutput *dest_output = NULL; switch (property_id) { case PROP_NAME: pika_object_set_name (PIKA_OBJECT (object), g_value_get_string (value)); break; case PROP_OPACITY_OUTPUT: src_output = g_value_get_object (value); dest_output = private->opacity_output; break; case PROP_FORCE_OUTPUT: src_output = g_value_get_object (value); dest_output = private->force_output; break; case PROP_HARDNESS_OUTPUT: src_output = g_value_get_object (value); dest_output = private->hardness_output; break; case PROP_RATE_OUTPUT: src_output = g_value_get_object (value); dest_output = private->rate_output; break; case PROP_FLOW_OUTPUT: src_output = g_value_get_object (value); dest_output = private->flow_output; break; case PROP_SIZE_OUTPUT: src_output = g_value_get_object (value); dest_output = private->size_output; break; case PROP_ASPECT_RATIO_OUTPUT: src_output = g_value_get_object (value); dest_output = private->aspect_ratio_output; break; case PROP_COLOR_OUTPUT: src_output = g_value_get_object (value); dest_output = private->color_output; break; case PROP_ANGLE_OUTPUT: src_output = g_value_get_object (value); dest_output = private->angle_output; break; case PROP_JITTER_OUTPUT: src_output = g_value_get_object (value); dest_output = private->jitter_output; break; case PROP_SPACING_OUTPUT: src_output = g_value_get_object (value); dest_output = private->spacing_output; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } if (src_output && dest_output) { pika_config_copy (PIKA_CONFIG (src_output), PIKA_CONFIG (dest_output), PIKA_CONFIG_PARAM_SERIALIZE); } } static void pika_dynamics_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaDynamicsPrivate *private = GET_PRIVATE (object); switch (property_id) { case PROP_NAME: g_value_set_string (value, pika_object_get_name (object)); break; case PROP_OPACITY_OUTPUT: g_value_set_object (value, private->opacity_output); break; case PROP_FORCE_OUTPUT: g_value_set_object (value, private->force_output); break; case PROP_HARDNESS_OUTPUT: g_value_set_object (value, private->hardness_output); break; case PROP_RATE_OUTPUT: g_value_set_object (value, private->rate_output); break; case PROP_FLOW_OUTPUT: g_value_set_object (value, private->flow_output); break; case PROP_SIZE_OUTPUT: g_value_set_object (value, private->size_output); break; case PROP_ASPECT_RATIO_OUTPUT: g_value_set_object (value, private->aspect_ratio_output); break; case PROP_COLOR_OUTPUT: g_value_set_object (value, private->color_output); break; case PROP_ANGLE_OUTPUT: g_value_set_object (value, private->angle_output); break; case PROP_JITTER_OUTPUT: g_value_set_object (value, private->jitter_output); break; case PROP_SPACING_OUTPUT: g_value_set_object (value, private->spacing_output); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_dynamics_dispatch_properties_changed (GObject *object, guint n_pspecs, GParamSpec **pspecs) { gint i; G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object, n_pspecs, pspecs); for (i = 0; i < n_pspecs; i++) { if (pspecs[i]->flags & PIKA_CONFIG_PARAM_SERIALIZE) { pika_data_dirty (PIKA_DATA (object)); break; } } } static const gchar * pika_dynamics_get_extension (PikaData *data) { return PIKA_DYNAMICS_FILE_EXTENSION; } static void pika_dynamics_copy (PikaData *data, PikaData *src_data) { pika_data_freeze (data); pika_config_copy (PIKA_CONFIG (src_data), PIKA_CONFIG (data), 0); pika_data_thaw (data); } /* public functions */ PikaData * pika_dynamics_new (PikaContext *context, const gchar *name) { g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (name[0] != '\0', NULL); return g_object_new (PIKA_TYPE_DYNAMICS, "name", name, NULL); } PikaData * pika_dynamics_get_standard (PikaContext *context) { static PikaData *standard_dynamics = NULL; if (! standard_dynamics) { g_set_weak_pointer (&standard_dynamics, pika_dynamics_new (context, "Standard dynamics")); pika_data_clean (standard_dynamics); pika_data_make_internal (standard_dynamics, "pika-dynamics-standard"); } return standard_dynamics; } PikaDynamicsOutput * pika_dynamics_get_output (PikaDynamics *dynamics, PikaDynamicsOutputType type_id) { PikaDynamicsPrivate *private; g_return_val_if_fail (PIKA_IS_DYNAMICS (dynamics), NULL); private = GET_PRIVATE (dynamics); switch (type_id) { case PIKA_DYNAMICS_OUTPUT_OPACITY: return private->opacity_output; break; case PIKA_DYNAMICS_OUTPUT_FORCE: return private->force_output; break; case PIKA_DYNAMICS_OUTPUT_HARDNESS: return private->hardness_output; break; case PIKA_DYNAMICS_OUTPUT_RATE: return private->rate_output; break; case PIKA_DYNAMICS_OUTPUT_FLOW: return private->flow_output; break; case PIKA_DYNAMICS_OUTPUT_SIZE: return private->size_output; break; case PIKA_DYNAMICS_OUTPUT_ASPECT_RATIO: return private->aspect_ratio_output; break; case PIKA_DYNAMICS_OUTPUT_COLOR: return private->color_output; break; case PIKA_DYNAMICS_OUTPUT_ANGLE: return private->angle_output; break; case PIKA_DYNAMICS_OUTPUT_JITTER: return private->jitter_output; break; case PIKA_DYNAMICS_OUTPUT_SPACING: return private->spacing_output; break; default: g_return_val_if_reached (NULL); break; } } gboolean pika_dynamics_is_output_enabled (PikaDynamics *dynamics, PikaDynamicsOutputType type) { PikaDynamicsOutput *output; g_return_val_if_fail (PIKA_IS_DYNAMICS (dynamics), FALSE); output = pika_dynamics_get_output (dynamics, type); return pika_dynamics_output_is_enabled (output); } gdouble pika_dynamics_get_linear_value (PikaDynamics *dynamics, PikaDynamicsOutputType type, const PikaCoords *coords, PikaPaintOptions *options, gdouble fade_point) { PikaDynamicsOutput *output; g_return_val_if_fail (PIKA_IS_DYNAMICS (dynamics), 0.0); output = pika_dynamics_get_output (dynamics, type); return pika_dynamics_output_get_linear_value (output, coords, options, fade_point); } gdouble pika_dynamics_get_angular_value (PikaDynamics *dynamics, PikaDynamicsOutputType type, const PikaCoords *coords, PikaPaintOptions *options, gdouble fade_point) { PikaDynamicsOutput *output; g_return_val_if_fail (PIKA_IS_DYNAMICS (dynamics), 0.0); output = pika_dynamics_get_output (dynamics, type); return pika_dynamics_output_get_angular_value (output, coords, options, fade_point); } gdouble pika_dynamics_get_aspect_value (PikaDynamics *dynamics, PikaDynamicsOutputType type, const PikaCoords *coords, PikaPaintOptions *options, gdouble fade_point) { PikaDynamicsOutput *output; g_return_val_if_fail (PIKA_IS_DYNAMICS (dynamics), 0.0); output = pika_dynamics_get_output (dynamics, type); return pika_dynamics_output_get_aspect_value (output, coords, options, fade_point); } /* private functions */ static PikaDynamicsOutput * pika_dynamics_create_output (PikaDynamics *dynamics, const gchar *name, PikaDynamicsOutputType type) { PikaDynamicsOutput *output = pika_dynamics_output_new (name, type); g_signal_connect (output, "notify", G_CALLBACK (pika_dynamics_output_notify), dynamics); return output; } static void pika_dynamics_output_notify (GObject *output, const GParamSpec *pspec, PikaDynamics *dynamics) { g_object_notify (G_OBJECT (dynamics), pika_object_get_name (output)); }