diff options
Diffstat (limited to 'plug-ins/common/cml-explorer.c')
-rw-r--r-- | plug-ins/common/cml-explorer.c | 2569 |
1 files changed, 2569 insertions, 0 deletions
diff --git a/plug-ins/common/cml-explorer.c b/plug-ins/common/cml-explorer.c new file mode 100644 index 0000000..b09fc66 --- /dev/null +++ b/plug-ins/common/cml-explorer.c @@ -0,0 +1,2569 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * CML_explorer.c + * Time-stamp: <2000-02-13 18:18:37 yasuhiro> + * Copyright (C) 1997 Shuji Narazaki <narazaki@InetQ.or.jp> + * Version: 1.0.11 + * URL: http://www.inetq.or.jp/~narazaki/TheGIMP/ + * + * 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/>. + * + * Comment: + * CML is the abbreviation of Coupled-Map Lattice that is a model of + * complex systems, proposed by a physicist[1,2]. + * + * Similar models are summaried as follows: + * + * Value Time Space + * Coupled-Map Lattice cont. discrete discrete + * Celluar Automata discrete discrete discrete + * Differential Eq. cont. cont. cont. + * + * (But this program uses a parameter: hold-rate to avoid very fast changes. + * Thus time is rather continuous than discrete. + * Yes, this change to model changes the output completely.) + * + * References: + * 1. Kunihiko Kaneko, Period-doubling of kind-antikink patterns, + * quasi-periodicity in antiferro-like structures and spatial + * intermittency in coupled map lattices -- Toward a prelude to a + * "field theory of chaos", Prog. Theor. Phys. 72 (1984) 480. + * + * 2. Kunihiko Kaneko ed., Theory and Applications of Coupled Map + * Lattices (Wiley, 1993). + * + * About Parameter File: + * I assume that the possible longest line in CMP parameter file is 1023. + * Please read CML_save_to_file_callback if you want know details of syntax. + * + * Format version 1.0 starts with: + * ; This is a parameter file for CML_explorer + * ; File format version: 1.0 + * ; + * Hue + * + * The old format for CML_explorer included in gimp-0.99.[89] is: + * ; CML parameter file (version: 1.0) + * ; Hue + * + * (This file format is interpreted as format version 0.99 now.) + * + * Thanks: + * This version contains patches from: + * Tim Mooney <mooney@dogbert.cc.ndsu.NoDak.edu> + * Sean P Cier <scier@andrew.cmu.edu> + * David Mosberger-Tang <davidm@azstarnet.com> + * Michael Sweet <mike@easysw.com> + * + */ +#include "config.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <glib/gstdio.h> + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#include "libgimp/stdplugins-intl.h" + +#define PARAM_FILE_FORMAT_VERSION 1.0 +#define PLUG_IN_PROC "plug-in-cml-explorer" +#define PLUG_IN_BINARY "cml-explorer" +#define PLUG_IN_ROLE "gimp-cml-explorer" +#define VALS CML_explorer_vals +#define PROGRESS_UPDATE_NUM 100 +#define CML_LINE_SIZE 1024 +#define TILE_CACHE_SIZE 32 +#define SCALE_WIDTH 130 +#define PREVIEW_WIDTH 64 +#define PREVIEW_HEIGHT 220 + +#define CANNONIZE(p, x) (255*(((p).range_h - (p).range_l)*(x) + (p).range_l)) +#define HCANNONIZE(p, x) (254*(((p).range_h - (p).range_l)*(x) + (p).range_l)) +#define POS_IN_TORUS(i,size) ((i < 0) ? size + i : ((size <= i) ? i - size : i)) + +typedef struct _WidgetEntry WidgetEntry; + +struct _WidgetEntry +{ + GtkWidget *widget; + gpointer value; + void (*updater) (WidgetEntry *); +}; + +enum +{ + CML_KEEP_VALUES, + CML_KEEP_FIRST, + CML_FILL, + CML_LOGIST, + CML_LOGIST_STEP, + CML_POWER, + CML_POWER_STEP, + CML_REV_POWER, + CML_REV_POWER_STEP, + CML_DELTA, + CML_DELTA_STEP, + CML_SIN_CURVE, + CML_SIN_CURVE_STEP, + CML_NUM_VALUES +}; + +static const gchar *function_names[CML_NUM_VALUES] = +{ + N_("Keep image's values"), + N_("Keep the first value"), + N_("Fill with parameter k"), + N_("k{x(1-x)}^p"), + N_("k{x(1-x)}^p stepped"), + N_("kx^p"), + N_("kx^p stepped"), + N_("k(1-x^p)"), + N_("k(1-x^p) stepped"), + N_("Delta function"), + N_("Delta function stepped"), + N_("sin^p-based function"), + N_("sin^p, stepped") +}; + +enum +{ + COMP_NONE, + COMP_MAX_LINEAR, + COMP_MAX_LINEAR_P1, + COMP_MAX_LINEAR_M1, + COMP_MIN_LINEAR, + COMP_MIN_LINEAR_P1, + COMP_MIN_LINEAR_M1, + COMP_MAX_LINEAR_P1L, + COMP_MAX_LINEAR_P1U, + COMP_MAX_LINEAR_M1L, + COMP_MAX_LINEAR_M1U, + COMP_MIN_LINEAR_P1L, + COMP_MIN_LINEAR_P1U, + COMP_MIN_LINEAR_M1L, + COMP_MIN_LINEAR_M1U, + COMP_NUM_VALUES +}; + +static const gchar *composition_names[COMP_NUM_VALUES] = +{ + NC_("cml-composition", "None"), + N_("Max (x, -)"), + N_("Max (x+d, -)"), + N_("Max (x-d, -)"), + N_("Min (x, -)"), + N_("Min (x+d, -)"), + N_("Min (x-d, -)"), + N_("Max (x+d, -), (x < 0.5)"), + N_("Max (x+d, -), (0.5 < x)"), + N_("Max (x-d, -), (x < 0.5)"), + N_("Max (x-d, -), (0.5 < x)"), + N_("Min (x+d, -), (x < 0.5)"), + N_("Min (x+d, -), (0.5 < x)"), + N_("Min (x-d, -), (x < 0.5)"), + N_("Min (x-d, -), (0.5 < x)") +}; + +enum +{ + STANDARD, + AVERAGE, + ANTILOG, + RAND_POWER0, + RAND_POWER1, + RAND_POWER2, + MULTIPLY_RANDOM0, + MULTIPLY_RANDOM1, + MULTIPLY_GRADIENT, + RAND_AND_P, + ARRANGE_NUM_VALUES +}; + +static const gchar *arrange_names[ARRANGE_NUM_VALUES] = +{ + N_("Standard"), + N_("Use average value"), + N_("Use reverse value"), + N_("With random power (0,10)"), + N_("With random power (0,1)"), + N_("With gradient power (0,1)"), + N_("Multiply rand. value (0,1)"), + N_("Multiply rand. value (0,2)"), + N_("Multiply gradient (0,1)"), + N_("With p and random (0,1)"), +}; + +enum +{ + CML_INITIAL_RANDOM_INDEPENDENT = 6, + CML_INITIAL_RANDOM_SHARED, + CML_INITIAL_RANDOM_FROM_SEED, + CML_INITIAL_RANDOM_FROM_SEED_SHARED, + CML_INITIAL_NUM_VALUES +}; + +static const gchar *initial_value_names[CML_INITIAL_NUM_VALUES] = +{ + N_("All black"), + N_("All gray"), + N_("All white"), + N_("The first row of the image"), + N_("Continuous gradient"), + N_("Continuous grad. w/o gap"), + N_("Random, ch. independent"), + N_("Random shared"), + N_("Randoms from seed"), + N_("Randoms from seed (shared)") +}; + +#define CML_PARAM_NUM 15 + +typedef struct +{ + gint function; + gint composition; + gint arrange; + gint cyclic_range; + gdouble mod_rate; /* diff / old-value */ + gdouble env_sensitivity; /* self-diff : env-diff */ + gint diffusion_dist; + gdouble ch_sensitivity; + gint range_num; + gdouble power; + gdouble parameter_k; + gdouble range_l; + gdouble range_h; + gdouble mutation_rate; + gdouble mutation_dist; +} CML_PARAM; + +typedef struct +{ + CML_PARAM hue; + CML_PARAM sat; + CML_PARAM val; + gint initial_value; + gint scale; + gint start_offset; + gint seed; + gchar last_file_name[256]; +} ValueType; + +static ValueType VALS = +{ + /* function composition arra + cyc chng sens diff cor n pow k (l,h) rnd dist */ + { + CML_SIN_CURVE, COMP_NONE, STANDARD, + 1, 0.5, 0.7, 2, 0.0, 1, 1.0, 1.0, 0, 1, 0.0, 0.1 + }, + { + CML_FILL, COMP_NONE, STANDARD, + 0, 0.6, 0.1, 2, 0.0, 1, 1.4, 0.9, 0, 0.9, 0.0, 0.1 + }, + { + CML_FILL, COMP_NONE, STANDARD, + 0, 0.5, 0.2, 2, 0.0, 1, 2.0, 1.0, 0, 0.9, 0.0, 0.1 + }, + 6, /* random value 1 */ + 1, /* scale */ + 0, /* start_offset */ + 0, /* seed */ + "" /* last filename */ +}; + +static CML_PARAM *channel_params[] = +{ + &VALS.hue, + &VALS.sat, + &VALS.val +}; + +static const gchar *channel_names[] = +{ + N_("Hue"), + N_("Saturation"), + N_("Value") +}; + +static const gchar *load_channel_names[] = +{ + N_("(None)"), + N_("Hue"), + N_("Saturation"), + N_("Value") +}; + +static void query (void); +static void run (const gchar *name, + gint nparams, + const GimpParam *param, + gint *nreturn_vals, + GimpParam **return_vals); + +static GimpPDBStatusType CML_main_function (gboolean preview_p); +static void CML_compute_next_step (gint size, + gdouble **h, + gdouble **s, + gdouble **v, + gdouble **hn, + gdouble **sn, + gdouble **vn, + gdouble **haux, + gdouble **saux, + gdouble **vaux); +static gdouble CML_next_value (gdouble *vec, + gint pos, + gint size, + gdouble c1, + gdouble c2, + CML_PARAM *param, + gdouble aux); +static gdouble logistic_function (CML_PARAM *param, + gdouble x, + gdouble power); + + +static gint CML_explorer_dialog (void); +static GtkWidget * CML_dialog_channel_panel_new (CML_PARAM *param, + gint channel_id); +static GtkWidget * CML_dialog_advanced_panel_new (void); + +static void CML_explorer_toggle_entry_init (WidgetEntry *widget_entry, + GtkWidget *widget, + gpointer value_ptr); + +static void CML_explorer_int_entry_init (WidgetEntry *widget_entry, + GtkObject *object, + gpointer value_ptr); + +static void CML_explorer_double_entry_init (WidgetEntry *widget_entry, + GtkObject *object, + gpointer value_ptr); + +static void CML_explorer_menu_update (GtkWidget *widget, + gpointer data); +static void CML_initial_value_menu_update (GtkWidget *widget, + gpointer data); +static void CML_explorer_menu_entry_init (WidgetEntry *widget_entry, + GtkWidget *widget, + gpointer value_ptr); + +static void preview_update (void); +static void function_graph_new (GtkWidget *widget, + gpointer *data); +static void CML_set_or_randomize_seed_callback (GtkWidget *widget, + gpointer data); +static void CML_copy_parameters_callback (GtkWidget *widget, + gpointer data); +static void CML_initial_value_sensitives_update (void); + +static void CML_save_to_file_callback (GtkWidget *widget, + gpointer data); +static void CML_save_to_file_response (GtkWidget *dialog, + gint response_id, + gpointer data); + +static void CML_preview_update_callback (GtkWidget *widget, + gpointer data); +static void CML_load_from_file_callback (GtkWidget *widget, + gpointer data); +static gboolean CML_load_parameter_file (const gchar *filename, + gboolean interactive_mode); +static void CML_load_from_file_response (GtkWidget *dialog, + gint response_id, + gpointer data); +static gint parse_line_to_gint (FILE *file, + gboolean *flag); +static gdouble parse_line_to_gdouble (FILE *file, + gboolean *flag); + + +const GimpPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +static GtkWidget *preview; +static WidgetEntry widget_pointers[4][CML_PARAM_NUM]; + +static guchar *img; +static gint img_stride; +static cairo_surface_t *buffer; + +typedef struct +{ + GtkWidget *widget; + gint logic; +} CML_sensitive_widget_table; + +#define RANDOM_SENSITIVES_NUM 5 +#define GRAPHSIZE 256 + +static CML_sensitive_widget_table random_sensitives[RANDOM_SENSITIVES_NUM] = +{ + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 } +}; + +static GRand *gr; +static gint drawable_id = 0; +static gint copy_source = 0; +static gint copy_destination = 0; +static gint selective_load_source = 0; +static gint selective_load_destination = 0; +static gboolean CML_preview_defer = FALSE; + +static gdouble *mem_chank0 = NULL; +static gint mem_chank0_size = 0; +static guchar *mem_chank1 = NULL; +static gint mem_chank1_size = 0; +static guchar *mem_chank2 = NULL; +static gint mem_chank2_size = 0; + +MAIN () + +static void +query (void) +{ + static const GimpParamDef args [] = + { + { GIMP_PDB_INT32, "ru-_mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }, + { GIMP_PDB_IMAGE, "image", "Input image (not used)" }, + { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }, + { GIMP_PDB_STRING, "parameter-filename", "The name of parameter file. CML_explorer makes an image with its settings." } + }; + + gimp_install_procedure (PLUG_IN_PROC, + N_("Create abstract Coupled-Map Lattice patterns"), + "Make an image of Coupled-Map Lattice (CML). CML is " + "a kind of Cellula Automata on continuous (value) " + "domain. In GIMP_RUN_NONINTERACTIVE, the name of a " + "parameter file is passed as the 4th arg. You can " + "control CML_explorer via parameter file.", + /* Or do you want to call me with over 50 args? */ + "Shuji Narazaki (narazaki@InetQ.or.jp); " + "http://www.inetq.or.jp/~narazaki/TheGIMP/", + "Shuji Narazaki", + "1997", + N_("CML _Explorer..."), + "RGB*, GRAY*", + GIMP_PLUGIN, + G_N_ELEMENTS (args), 0, + args, NULL); + + gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render/Pattern"); +} + +static void +run (const gchar *name, + gint nparams, + const GimpParam *param, + gint *nreturn_vals, + GimpParam **return_vals) +{ + static GimpParam values[1]; + GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR; + GimpRunMode run_mode; + + run_mode = param[0].data.d_int32; + drawable_id = param[2].data.d_drawable; + + INIT_I18N (); + gegl_init (NULL, NULL); + + *nreturn_vals = 1; + *return_vals = values; + + values[0].type = GIMP_PDB_STATUS; + values[0].data.d_status = status; + + switch (run_mode) + { + case GIMP_RUN_INTERACTIVE: + gimp_get_data (PLUG_IN_PROC, &VALS); + if (! CML_explorer_dialog ()) + return; + break; + case GIMP_RUN_NONINTERACTIVE: + { + gchar *filename = param[3].data.d_string; + + if (! CML_load_parameter_file (filename, FALSE)) + return; + break; + } + case GIMP_RUN_WITH_LAST_VALS: + gimp_get_data (PLUG_IN_PROC, &VALS); + break; + } + + status = CML_main_function (FALSE); + + if (run_mode != GIMP_RUN_NONINTERACTIVE) + gimp_displays_flush(); + if (run_mode == GIMP_RUN_INTERACTIVE && status == GIMP_PDB_SUCCESS) + gimp_set_data (PLUG_IN_PROC, &VALS, sizeof (ValueType)); + + g_free (mem_chank0); + g_free (mem_chank1); + g_free (mem_chank2); + + values[0].type = GIMP_PDB_STATUS; + values[0].data.d_status = status; +} + +static GimpPDBStatusType +CML_main_function (gboolean preview_p) +{ + GeglBuffer *src_buffer; + GeglBuffer *dest_buffer; + const Babl *src_format; + const Babl *dest_format; + guchar *dest_buf = NULL; + guchar *src_buf = NULL; + gint x, y; + gint dx, dy; + gboolean dest_has_alpha = FALSE; + gboolean dest_is_gray = FALSE; + gboolean src_has_alpha = FALSE; + gboolean src_is_gray = FALSE; + gint total, processed = 0; + gint keep_height = 1; + gint cell_num, width_by_pixel, height_by_pixel; + gint index; + gint src_bpp, src_bpl; + gint dest_bpp, dest_bpl; + gdouble *hues, *sats, *vals; + gdouble *newh, *news, *newv; + gdouble *haux, *saux, *vaux; + + if (! gimp_drawable_mask_intersect (drawable_id, + &x, &y, + &width_by_pixel, &height_by_pixel)) + return GIMP_PDB_SUCCESS; + + src_has_alpha = dest_has_alpha = gimp_drawable_has_alpha (drawable_id); + src_is_gray = dest_is_gray = gimp_drawable_is_gray (drawable_id); + + if (src_is_gray) + { + if (src_has_alpha) + src_format = babl_format ("Y'A u8"); + else + src_format = babl_format ("Y' u8"); + } + else + { + if (src_has_alpha) + src_format = babl_format ("R'G'B'A u8"); + else + src_format = babl_format ("R'G'B' u8"); + } + + dest_format = src_format; + + src_bpp = dest_bpp = babl_format_get_bytes_per_pixel (src_format); + + if (preview_p) + { + dest_format = babl_format ("R'G'B' u8"); + + dest_has_alpha = FALSE; + dest_bpp = 3; + + if (width_by_pixel > PREVIEW_WIDTH) /* preview < drawable (selection) */ + width_by_pixel = PREVIEW_WIDTH; + if (height_by_pixel > PREVIEW_HEIGHT) + height_by_pixel = PREVIEW_HEIGHT; + } + + dest_bpl = width_by_pixel * dest_bpp; + src_bpl = width_by_pixel * src_bpp; + cell_num = (width_by_pixel - 1)/ VALS.scale + 1; + total = height_by_pixel * width_by_pixel; + + if (total < 1) + return GIMP_PDB_EXECUTION_ERROR; + + keep_height = VALS.scale; + + /* configure reusable memories */ + if (mem_chank0_size < 9 * cell_num * sizeof (gdouble)) + { + g_free (mem_chank0); + mem_chank0_size = 9 * cell_num * sizeof (gdouble); + mem_chank0 = (gdouble *) g_malloc (mem_chank0_size); + } + + hues = mem_chank0; + sats = mem_chank0 + cell_num; + vals = mem_chank0 + 2 * cell_num; + newh = mem_chank0 + 3 * cell_num; + news = mem_chank0 + 4 * cell_num; + newv = mem_chank0 + 5 * cell_num; + haux = mem_chank0 + 6 * cell_num; + saux = mem_chank0 + 7 * cell_num; + vaux = mem_chank0 + 8 * cell_num; + + if (mem_chank1_size < src_bpl * keep_height) + { + g_free (mem_chank1); + mem_chank1_size = src_bpl * keep_height; + mem_chank1 = (guchar *) g_malloc (mem_chank1_size); + } + src_buf = mem_chank1; + + if (mem_chank2_size < dest_bpl * keep_height) + { + g_free (mem_chank2); + mem_chank2_size = dest_bpl * keep_height; + mem_chank2 = (guchar *) g_malloc (mem_chank2_size); + } + dest_buf = mem_chank2; + + if (! preview_p) + dest_buffer = gimp_drawable_get_shadow_buffer (drawable_id); + + src_buffer = gimp_drawable_get_buffer (drawable_id); + + gr = g_rand_new (); + if (VALS.initial_value == CML_INITIAL_RANDOM_FROM_SEED) + g_rand_set_seed (gr, VALS.seed); + + for (index = 0; index < cell_num; index++) + { + switch (VALS.hue.arrange) + { + case RAND_POWER0: + haux [index] = g_rand_double_range (gr, 0, 10); + break; + case RAND_POWER2: + case MULTIPLY_GRADIENT: + haux [index] = (gdouble) abs ((index % 511) - 255) / (gdouble) 256; + break; + case RAND_POWER1: + case MULTIPLY_RANDOM0: + haux [index] = g_rand_double (gr); + break; + case MULTIPLY_RANDOM1: + haux [index] = g_rand_double_range (gr, 0, 2); + break; + case RAND_AND_P: + haux [index] = ((index % (2 * VALS.hue.diffusion_dist) == 0) ? + g_rand_double (gr) : VALS.hue.power); + break; + default: + haux [index] = VALS.hue.power; + break; + } + + switch (VALS.sat.arrange) + { + case RAND_POWER0: + saux [index] = g_rand_double_range (gr, 0, 10); + break; + case RAND_POWER2: + case MULTIPLY_GRADIENT: + saux [index] = (gdouble) abs ((index % 511) - 255) / (gdouble) 256; + break; + case RAND_POWER1: + case MULTIPLY_RANDOM0: + saux [index] = g_rand_double (gr); + break; + case MULTIPLY_RANDOM1: + saux [index] = g_rand_double_range (gr, 0, 2); + break; + case RAND_AND_P: + saux [index] = ((index % (2 * VALS.sat.diffusion_dist) == 0) ? + g_rand_double (gr) : VALS.sat.power); + break; + default: + saux [index] = VALS.sat.power; + break; + } + + switch (VALS.val.arrange) + { + case RAND_POWER0: + vaux [index] = g_rand_double_range (gr, 0, 10); + break; + case RAND_POWER2: + case MULTIPLY_GRADIENT: + vaux [index] = (gdouble) abs ((index % 511) - 255) / (gdouble) 256; + break; + case RAND_POWER1: + case MULTIPLY_RANDOM0: + vaux [index] = g_rand_double (gr); + break; + case MULTIPLY_RANDOM1: + vaux [index] = g_rand_double_range (gr, 0, 2); + break; + case RAND_AND_P: + vaux [index] = ((index % (2 * VALS.val.diffusion_dist) == 0) ? + g_rand_double (gr) : VALS.val.power); + break; + default: + vaux [index] = VALS.val.power; + break; + } + + switch (VALS.initial_value) + { + case 0: + case 1: + case 2: + hues[index] = sats[index] = vals[index] = 0.5 * (VALS.initial_value); + break; + case 3: /* use the values of the image (drawable) */ + break; /* copy from the drawable after this loop */ + case 4: /* grandient 1 */ + hues[index] = sats[index] = vals[index] + = (gdouble) (index % 256) / (gdouble) 256; + break; /* gradinet 2 */ + case 5: + hues[index] = sats[index] = vals[index] + = (gdouble) abs ((index % 511) - 255) / (gdouble) 256; + break; + case CML_INITIAL_RANDOM_INDEPENDENT: + case CML_INITIAL_RANDOM_FROM_SEED: + hues[index] = g_rand_double (gr); + sats[index] = g_rand_double (gr); + vals[index] = g_rand_double (gr); + break; + case CML_INITIAL_RANDOM_SHARED: + case CML_INITIAL_RANDOM_FROM_SEED_SHARED: + hues[index] = sats[index] = vals[index] = g_rand_double (gr); + break; + } + } + + if (VALS.initial_value == 3) + { + int index; + + for (index = 0; + index < MIN (cell_num, width_by_pixel / VALS.scale); + index++) + { + guchar buffer[4]; + int rgbi[3]; + int i; + + gegl_buffer_sample (src_buffer, x + (index * VALS.scale), y, NULL, + buffer, src_format, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + + for (i = 0; i < 3; i++) rgbi[i] = buffer[i]; + gimp_rgb_to_hsv_int (rgbi, rgbi + 1, rgbi + 2); + hues[index] = (gdouble) rgbi[0] / (gdouble) 255; + sats[index] = (gdouble) rgbi[1] / (gdouble) 255; + vals[index] = (gdouble) rgbi[2] / (gdouble) 255; + } + } + + if (! preview_p) + gimp_progress_init (_("CML Explorer: evoluting")); + + /* rolling start */ + for (index = 0; index < VALS.start_offset; index++) + CML_compute_next_step (cell_num, &hues, &sats, &vals, &newh, &news, &newv, + &haux, &saux, &vaux); + + /* rendering */ + for (dy = 0; dy < height_by_pixel; dy += VALS.scale) + { + gint r, g, b, h, s, v; + gint offset_x, offset_y, dest_offset; + + if (height_by_pixel < dy + keep_height) + keep_height = height_by_pixel - dy; + + if ((VALS.hue.function == CML_KEEP_VALUES) || + (VALS.sat.function == CML_KEEP_VALUES) || + (VALS.val.function == CML_KEEP_VALUES)) + { + gegl_buffer_get (src_buffer, + GEGL_RECTANGLE (x, y + dy, + width_by_pixel, keep_height), 1.0, + src_format, src_buf, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + + CML_compute_next_step (cell_num, + &hues, &sats, &vals, + &newh, &news, &newv, + &haux, &saux, &vaux); + + for (dx = 0; dx < cell_num; dx++) + { + h = r = HCANNONIZE (VALS.hue, hues[dx]); + s = g = CANNONIZE (VALS.sat, sats[dx]); + v = b = CANNONIZE (VALS.val, vals[dx]); + + if (! dest_is_gray) + gimp_hsv_to_rgb_int (&r, &g, &b); + + /* render destination */ + for (offset_y = 0; + (offset_y < VALS.scale) && (dy + offset_y < height_by_pixel); + offset_y++) + for (offset_x = 0; + (offset_x < VALS.scale) && (dx * VALS.scale + offset_x < width_by_pixel); + offset_x++) + { + if ((VALS.hue.function == CML_KEEP_VALUES) || + (VALS.sat.function == CML_KEEP_VALUES) || + (VALS.val.function == CML_KEEP_VALUES)) + { + int rgbi[3]; + int i; + + for (i = 0; i < src_bpp; i++) + rgbi[i] = src_buf[offset_y * src_bpl + + (dx * VALS.scale + offset_x) * src_bpp + i]; + if (src_is_gray && (VALS.val.function == CML_KEEP_VALUES)) + { + b = rgbi[0]; + } + else + { + gimp_rgb_to_hsv_int (rgbi, rgbi + 1, rgbi + 2); + + r = (VALS.hue.function == CML_KEEP_VALUES) ? rgbi[0] : h; + g = (VALS.sat.function == CML_KEEP_VALUES) ? rgbi[1] : s; + b = (VALS.val.function == CML_KEEP_VALUES) ? rgbi[2] : v; + gimp_hsv_to_rgb_int (&r, &g, &b); + } + } + + dest_offset = (offset_y * dest_bpl + + (dx * VALS.scale + offset_x) * dest_bpp); + + if (dest_is_gray) + { + dest_buf[dest_offset++] = b; + if (preview_p) + { + dest_buf[dest_offset++] = b; + dest_buf[dest_offset++] = b; + } + } + else + { + dest_buf[dest_offset++] = r; + dest_buf[dest_offset++] = g; + dest_buf[dest_offset++] = b; + } + if (dest_has_alpha) + dest_buf[dest_offset] = 255; + + if ((!preview_p) && + (++processed % (total / PROGRESS_UPDATE_NUM + 1)) == 0) + gimp_progress_update ((gdouble) processed / (gdouble) total); + } + } + + if (preview_p) + { + gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview), + 0, dy, + width_by_pixel, keep_height, + GIMP_RGB_IMAGE, + dest_buf, + dest_bpl); + } + else + { + gegl_buffer_set (dest_buffer, + GEGL_RECTANGLE (x, y + dy, + width_by_pixel, keep_height), 0, + dest_format, dest_buf, + GEGL_AUTO_ROWSTRIDE); + } + } + + g_object_unref (src_buffer); + + if (preview_p) + { + gtk_widget_queue_draw (preview); + } + else + { + gimp_progress_update (1.0); + + g_object_unref (dest_buffer); + + gimp_drawable_merge_shadow (drawable_id, TRUE); + gimp_drawable_update (drawable_id, + x, y, width_by_pixel, height_by_pixel); + } + + g_rand_free (gr); + + return GIMP_PDB_SUCCESS; +} + +static void +CML_compute_next_step (gint size, + gdouble **h, + gdouble **s, + gdouble **v, + gdouble **hn, + gdouble **sn, + gdouble **vn, + gdouble **haux, + gdouble **saux, + gdouble **vaux) +{ + gint index; + + for (index = 0; index < size; index++) + (*hn)[index] = CML_next_value (*h, index, size, + (*s)[POS_IN_TORUS (index, size)], + (*v)[POS_IN_TORUS (index, size)], + &VALS.hue, + (*haux)[POS_IN_TORUS (index , size)]); + for (index = 0; index < size; index++) + (*sn)[index] = CML_next_value (*s, index, size, + (*v)[POS_IN_TORUS (index , size)], + (*h)[POS_IN_TORUS (index , size)], + &VALS.sat, + (*saux)[POS_IN_TORUS (index , size)]); + for (index = 0; index < size; index++) + (*vn)[index] = CML_next_value (*v, index, size, + (*h)[POS_IN_TORUS (index , size)], + (*s)[POS_IN_TORUS (index , size)], + &VALS.val, + (*vaux)[POS_IN_TORUS (index , size)]); + +#define GD_SWAP(x, y) { gdouble *tmp = *x; *x = *y; *y = tmp; } + GD_SWAP (h, hn); + GD_SWAP (s, sn); + GD_SWAP (v, vn); +#undef SWAP +} + +#define LOGISTICS(x) logistic_function (param, x, power) +#define ENV_FACTOR(x) (param->env_sensitivity * LOGISTICS (x)) +#define CHN_FACTOR(x) (param->ch_sensitivity * LOGISTICS (x)) + +static gdouble +CML_next_value (gdouble *vec, + gint pos, + gint size, + gdouble c1, + gdouble c2, + CML_PARAM *param, + gdouble power) +{ + gdouble val = vec[pos]; + gdouble diff = 0; + gdouble self_diff = 0; + gdouble by_env = 0; + gdouble self_mod_rate = 0; + gdouble hold_rate = 1 - param->mod_rate; + gdouble env_factor = 0; + gint index; + + self_mod_rate = (1 - param->env_sensitivity - param->ch_sensitivity); + + switch (param->arrange) + { + case ANTILOG: + self_diff = self_mod_rate * LOGISTICS (1 - vec[pos]); + for (index = 1; index <= param->diffusion_dist / 2; index++) + env_factor += ENV_FACTOR (1 - vec[POS_IN_TORUS (pos + index, size)]) + + ENV_FACTOR (1 - vec[POS_IN_TORUS (pos - index, size)]); + if ((param->diffusion_dist % 2) == 1) + env_factor += (ENV_FACTOR (1 - vec[POS_IN_TORUS (pos + index, size)]) + + ENV_FACTOR (1 - vec[POS_IN_TORUS (pos - index, size)])) / 2; + env_factor /= (gdouble) param->diffusion_dist; + by_env = env_factor + (CHN_FACTOR (1 - c1) + CHN_FACTOR (1 - c2)) / 2; + diff = param->mod_rate * (self_diff + by_env); + val = hold_rate * vec[pos] + diff; + break; + case AVERAGE: + self_diff = self_mod_rate * LOGISTICS (vec[pos]); + for (index = 1; index <= param->diffusion_dist / 2; index++) + env_factor += vec[POS_IN_TORUS (pos + index, size)] + vec[POS_IN_TORUS (pos - index, size)]; + if ((param->diffusion_dist % 2) == 1) + env_factor += (vec[POS_IN_TORUS (pos + index, size)] + vec[POS_IN_TORUS (pos - index, size)]) / 2; + env_factor /= (gdouble) param->diffusion_dist; + by_env = ENV_FACTOR (env_factor) + (CHN_FACTOR (c1) + CHN_FACTOR (c2)) / 2; + diff = param->mod_rate * (self_diff + by_env); + val = hold_rate * vec[pos] + diff; + break; + case MULTIPLY_RANDOM0: + case MULTIPLY_RANDOM1: + case MULTIPLY_GRADIENT: + { + gdouble tmp; + + tmp = power; + power = param->power; + self_diff = self_mod_rate * LOGISTICS (vec[pos]); + for (index = 1; index <= param->diffusion_dist / 2; index++) + env_factor += ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)]) + + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)]); + if ((param->diffusion_dist % 2) == 1) + env_factor += (ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)]) + + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)])) / 2; + env_factor /= (gdouble) param->diffusion_dist; + by_env = (env_factor + CHN_FACTOR (c1) + CHN_FACTOR (c2)) / 2; + diff = pow (param->mod_rate * (self_diff + by_env), tmp); + val = hold_rate * vec[pos] + diff; + break; + } + case STANDARD: + case RAND_POWER0: + case RAND_POWER1: + case RAND_POWER2: + case RAND_AND_P: + default: + self_diff = self_mod_rate * LOGISTICS (vec[pos]); + + for (index = 1; index <= param->diffusion_dist / 2; index++) + env_factor += ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)]) + + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)]); + if ((param->diffusion_dist % 2) == 1) + env_factor += (ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)]) + + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)])) / 2; + env_factor /= (gdouble) param->diffusion_dist; + by_env = env_factor + (CHN_FACTOR (c1) + CHN_FACTOR (c2)) / 2; + diff = param->mod_rate * (self_diff + by_env); + val = hold_rate * vec[pos] + diff; + break; + } + /* finalize */ + if (g_rand_double (gr) < param->mutation_rate) + { + val += ((g_rand_double (gr) < 0.5) ? -1.0 : 1.0) * param->mutation_dist * g_rand_double (gr); + } + if (param->cyclic_range) + { + if (1.0 < val) + val = val - (int) val; + else if (val < 0.0) + val = val - floor (val); + } + else + /* The range of val should be [0,1], not [0,1). + Cannonization shuold be done in color mapping phase. */ + val = CLAMP (val, 0.0, 1); + + return val; +} +#undef LOGISTICS +#undef ENV_FACTOR +#undef CHN_FACTOR + + +static gdouble +logistic_function (CML_PARAM *param, + gdouble x, + gdouble power) +{ + gdouble x1 = x; + gdouble result = 0; + gint n = param->range_num; + gint step; + + step = (int) (x * (gdouble) n); + x1 = (x - ((gdouble) step / (gdouble) n)) * n; + switch (param->function) + { + case CML_KEEP_VALUES: + case CML_KEEP_FIRST: + result = x; + return result; + break; + case CML_FILL: + result = CLAMP (param->parameter_k, 0.0, 1.0); + return result; + break; + case CML_LOGIST: + result = param->parameter_k * pow (4 * x1 * (1.0 - x1), power); + break; + case CML_LOGIST_STEP: + result = param->parameter_k * pow (4 * x1 * (1.0 - x1), power); + result = (result + step) / (gdouble) n; + break; + case CML_POWER: + result = param->parameter_k * pow (x1, power); + break; + case CML_POWER_STEP: + result = param->parameter_k * pow (x1, power); + result = (result + step) / (gdouble) n; + break; + case CML_REV_POWER: + result = param->parameter_k * (1 - pow (x1, power)); + break; + case CML_REV_POWER_STEP: + result = param->parameter_k * (1 - pow (x1, power)); + result = (result + step) / (gdouble) n; + break; + case CML_DELTA: + result = param->parameter_k * 2 * ((x1 < 0.5) ? x1 : (1.0 - x1)); + break; + case CML_DELTA_STEP: + result = param->parameter_k * 2 * ((x1 < 0.5) ? x1 : (1.0 - x1)); + result = (result + step) / (gdouble) n; + break; + case CML_SIN_CURVE: + if (1.0 < power) + result = 0.5 * (sin (G_PI * ABS (x1 - 0.5) / power) / sin (G_PI * 0.5 / power) + 1); + else + result = 0.5 * (pow (sin (G_PI * ABS (x1 - 0.5)), power) + 1); + if (x1 < 0.5) result = 1 - result; + break; + case CML_SIN_CURVE_STEP: + if (1.0 < power) + result = 0.5 * (sin (G_PI * ABS (x1 - 0.5) / power) / sin (G_PI * 0.5 / power) + 1); + else + result = 0.5 * (pow (sin (G_PI * ABS (x1 - 0.5)), power) + 1); + if (x1 < 0.5) result = 1 - result; + result = (result + step) / (gdouble) n; + break; + } + switch (param->composition) + { + case COMP_NONE: + break; + case COMP_MAX_LINEAR: + result = MAX ((gdouble) x, (gdouble) result); + break; + case COMP_MAX_LINEAR_P1: + result = MAX ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + case COMP_MAX_LINEAR_P1L: + if (x < 0.5) + result = MAX ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + case COMP_MAX_LINEAR_P1U: + if (0.5 < x) + result = MAX ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + case COMP_MAX_LINEAR_M1: + result = MAX ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + case COMP_MAX_LINEAR_M1L: + if (x < 0.5) + result = MAX ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + case COMP_MAX_LINEAR_M1U: + if (0.5 < x) + result = MAX ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + case COMP_MIN_LINEAR: + result = MIN ((gdouble) x, (gdouble) result); + break; + case COMP_MIN_LINEAR_P1: + result = MIN ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + case COMP_MIN_LINEAR_P1L: + if (x < 0.5) + result = MIN ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + case COMP_MIN_LINEAR_P1U: + if (0.5 < x) + result = MIN ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + case COMP_MIN_LINEAR_M1: + result = MIN ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + case COMP_MIN_LINEAR_M1L: + if (x < 0.5) + result = MIN ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + case COMP_MIN_LINEAR_M1U: + if (0.5 < x) + result = MIN ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result); + break; + } + return result; +} + +/* dialog stuff */ +static gint +CML_explorer_dialog (void) +{ + GtkWidget *dialog; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *frame; + GtkWidget *abox; + GtkWidget *bbox; + GtkWidget *button; + gboolean run; + + gimp_ui_init (PLUG_IN_BINARY, TRUE); + + dialog = gimp_dialog_new (_("Coupled-Map-Lattice Explorer"), PLUG_IN_ROLE, + NULL, 0, + gimp_standard_help_func, PLUG_IN_PROC, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_OK"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gimp_window_set_transient (GTK_WINDOW (dialog)); + + CML_preview_defer = TRUE; + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); + gtk_widget_show (vbox); + + abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0); + gtk_widget_show (abox); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (abox), frame); + gtk_widget_show (frame); + + preview = gimp_preview_area_new (); + gtk_widget_set_size_request (preview, + PREVIEW_WIDTH, PREVIEW_HEIGHT); + gtk_container_add (GTK_CONTAINER (frame), preview); + gtk_widget_show (preview); + + bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL); + gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0); + gtk_widget_show (bbox); + + button = gtk_button_new_with_label (_("New Seed")); + gtk_container_add (GTK_CONTAINER (bbox), button); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (CML_preview_update_callback), + &VALS); + + random_sensitives[0].widget = button; + random_sensitives[0].logic = TRUE; + + button = gtk_button_new_with_label (_("Fix Seed")); + gtk_container_add (GTK_CONTAINER (bbox), button); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (CML_set_or_randomize_seed_callback), + &VALS); + + random_sensitives[1].widget = button; + random_sensitives[1].logic = TRUE; + + button = gtk_button_new_with_label (_("Random Seed")); + gtk_container_add (GTK_CONTAINER (bbox), button); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (CML_set_or_randomize_seed_callback), + &VALS); + + random_sensitives[2].widget = button; + random_sensitives[2].logic = FALSE; + + bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL); + gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0); + gtk_widget_show (bbox); + + button = gtk_button_new_with_mnemonic (_("_Open")); + gtk_container_add (GTK_CONTAINER (bbox), button); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (CML_load_from_file_callback), + &VALS); + + button = gtk_button_new_with_mnemonic (_("_Save")); + gtk_container_add (GTK_CONTAINER (bbox), button); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (CML_save_to_file_callback), + &VALS); + + { + GtkWidget *notebook; + GtkWidget *page; + + notebook = gtk_notebook_new (); + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP); + gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0); + gtk_widget_show (notebook); + + page = CML_dialog_channel_panel_new (&VALS.hue, 0); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, + gtk_label_new_with_mnemonic (_("_Hue"))); + + page = CML_dialog_channel_panel_new (&VALS.sat, 1); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, + gtk_label_new_with_mnemonic (_("Sat_uration"))); + + page = CML_dialog_channel_panel_new (&VALS.val, 2); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, + gtk_label_new_with_mnemonic (_("_Value"))); + + page = CML_dialog_advanced_panel_new (); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, + gtk_label_new_with_mnemonic (_("_Advanced"))); + + { + GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + GtkWidget *table; + GtkWidget *label; + GtkWidget *combo; + GtkWidget *frame; + GtkWidget *vbox; + GtkObject *adj; + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + gtk_widget_show (vbox); + + frame = gimp_frame_new (_("Channel Independent Parameters")); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + table = gtk_table_new (3, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + combo = gimp_int_combo_box_new_array (CML_INITIAL_NUM_VALUES, + initial_value_names); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), + VALS.initial_value); + + g_signal_connect (combo, "changed", + G_CALLBACK (CML_initial_value_menu_update), + &VALS.initial_value); + + CML_explorer_menu_entry_init (&widget_pointers[3][0], + combo, &VALS.initial_value); + label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("Initial value:"), 0.0, 0.5, + combo, 2, FALSE); + gtk_size_group_add_widget (group, label); + g_object_unref (group); + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1, + _("Zoom scale:"), SCALE_WIDTH, 3, + VALS.scale, 1, 10, 1, 2, 0, + TRUE, 0, 0, + NULL, NULL); + gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj)); + CML_explorer_int_entry_init (&widget_pointers[3][1], + adj, &VALS.scale); + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2, + _("Start offset:"), SCALE_WIDTH, 3, + VALS.start_offset, 0, 100, 1, 10, 0, + TRUE, 0, 0, + NULL, NULL); + gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj)); + CML_explorer_int_entry_init (&widget_pointers[3][2], + adj, &VALS.start_offset); + + frame = + gimp_frame_new (_("Seed of Random (only for \"From Seed\" Modes)")); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + table = gtk_table_new (2, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0, + _("Seed:"), SCALE_WIDTH, 0, + VALS.seed, 0, (guint32) -1, 1, 10, 0, + TRUE, 0, 0, + NULL, NULL); + gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj)); + CML_explorer_int_entry_init (&widget_pointers[3][3], + adj, &VALS.seed); + + random_sensitives[3].widget = table; + random_sensitives[3].logic = FALSE; + + button = + gtk_button_new_with_label + (_("Switch to \"From seed\" With the Last Seed")); + gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, 1, 2); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (CML_set_or_randomize_seed_callback), + &VALS); + + random_sensitives[4].widget = button; + random_sensitives[4].logic = TRUE; + + gimp_help_set_help_data (button, + _("\"Fix seed\" button is an alias of me.\n" + "The same seed produces the same image, " + "if (1) the widths of images are same " + "(this is the reason why image on drawable " + "is different from preview), and (2) all " + "mutation rates equal to zero."), NULL); + + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, + gtk_label_new_with_mnemonic (_("O_thers"))); + } + + { + GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + GtkWidget *table; + GtkWidget *frame; + GtkWidget *label; + GtkWidget *combo; + GtkWidget *vbox; + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + gtk_widget_show (vbox); + + frame = gimp_frame_new (_("Copy Settings")); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + table = gtk_table_new (3, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (channel_names), + channel_names); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), copy_source); + + g_signal_connect (combo, "changed", + G_CALLBACK (gimp_int_combo_box_get_active), + ©_source); + + label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("Source channel:"), 0.0, 0.5, + combo, 1, FALSE); + gtk_size_group_add_widget (group, label); + g_object_unref (group); + + combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (channel_names), + channel_names); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), + copy_destination); + + g_signal_connect (combo, "changed", + G_CALLBACK (gimp_int_combo_box_get_active), + ©_destination); + + label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, + _("Destination channel:"), 0.0, 0.5, + combo, 1, FALSE); + gtk_size_group_add_widget (group, label); + + button = gtk_button_new_with_label (_("Copy Parameters")); + gtk_table_attach (GTK_TABLE (table), button, 0, 2, 2, 3, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (CML_copy_parameters_callback), + &VALS); + + frame = gimp_frame_new (_("Selective Load Settings")); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + table = gtk_table_new (2, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (load_channel_names), + load_channel_names); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), + selective_load_source); + + g_signal_connect (combo, "changed", + G_CALLBACK (gimp_int_combo_box_get_active), + &selective_load_source); + + label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("Source channel in file:"), + 0.0, 0.5, + combo, 1, FALSE); + gtk_size_group_add_widget (group, label); + + combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (load_channel_names), + load_channel_names); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), + selective_load_destination); + + g_signal_connect (combo, "changed", + G_CALLBACK (gimp_int_combo_box_get_active), + &selective_load_destination); + + label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, + _("Destination channel:"), + 0.0, 0.5, + combo, 1, FALSE); + gtk_size_group_add_widget (group, label); + + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, + gtk_label_new_with_mnemonic (_("_Misc Ops."))); + } + } + + CML_initial_value_sensitives_update (); + + gtk_widget_show (dialog); + + img_stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, GRAPHSIZE); + img = g_malloc0 (img_stride * GRAPHSIZE); + + buffer = cairo_image_surface_create_for_data (img, CAIRO_FORMAT_RGB24, + GRAPHSIZE, + GRAPHSIZE, + img_stride); + + /* Displaying preview might takes a long time. Thus, first, dialog itself + * should be shown before making preview in it. + */ + CML_preview_defer = FALSE; + preview_update (); + + run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK); + + gtk_widget_destroy (dialog); + g_free (img); + cairo_surface_destroy (buffer); + + return run; +} + +static GtkWidget * +CML_dialog_channel_panel_new (CML_PARAM *param, + gint channel_id) +{ + GtkWidget *table; + GtkWidget *combo; + GtkWidget *toggle; + GtkWidget *button; + GtkObject *adj; + gpointer *chank; + gint index = 0; + + table = gtk_table_new (13, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_container_set_border_width (GTK_CONTAINER (table), 12); + gtk_widget_show (table); + + combo = gimp_int_combo_box_new_array (CML_NUM_VALUES, function_names); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), param->function); + + g_signal_connect (combo, "changed", + G_CALLBACK (CML_explorer_menu_update), + ¶m->function); + + CML_explorer_menu_entry_init (&widget_pointers[channel_id][index], + combo, ¶m->function); + gimp_table_attach_aligned (GTK_TABLE (table), 0, index, + _("Function type:"), 0.0, 0.5, + combo, 2, FALSE); + index++; + + combo = gimp_int_combo_box_new_array (COMP_NUM_VALUES, composition_names); + + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), + param->composition); + + g_signal_connect (combo, "changed", + G_CALLBACK (CML_explorer_menu_update), + ¶m->composition); + + CML_explorer_menu_entry_init (&widget_pointers[channel_id][index], + combo, ¶m->composition); + gimp_table_attach_aligned (GTK_TABLE (table), 0, index, + _("Composition:"), 0.0, 0.5, + combo, 2, FALSE); + index++; + + combo = gimp_int_combo_box_new_array (ARRANGE_NUM_VALUES, arrange_names); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), param->arrange); + + g_signal_connect (combo, "changed", + G_CALLBACK (CML_explorer_menu_update), + ¶m->arrange); + + CML_explorer_menu_entry_init (&widget_pointers[channel_id][index], + combo, ¶m->arrange); + gimp_table_attach_aligned (GTK_TABLE (table), 0, index, + _("Misc arrange:"), 0.0, 0.5, + combo, 2, FALSE); + index++; + + toggle = gtk_check_button_new_with_label (_("Use cyclic range")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + param->cyclic_range); + gtk_table_attach_defaults (GTK_TABLE (table), toggle, 0, 3, index, index + 1); + CML_explorer_toggle_entry_init (&widget_pointers[channel_id][index], + toggle, ¶m->cyclic_range); + gtk_widget_show (toggle); + index++; + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index, + _("Mod. rate:"), SCALE_WIDTH, 5, + param->mod_rate, 0.0, 1.0, 0.01, 0.1, 2, + TRUE, 0, 0, + NULL, NULL); + CML_explorer_double_entry_init (&widget_pointers[channel_id][index], + adj, ¶m->mod_rate); + index++; + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index, + _("Env. sensitivity:"), SCALE_WIDTH, 5, + param->env_sensitivity, 0.0, 1.0, 0.01, 0.1, 2, + TRUE, 0, 0, + NULL, NULL); + CML_explorer_double_entry_init (&widget_pointers[channel_id][index], + adj, ¶m->env_sensitivity); + index++; + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index, + _("Diffusion dist.:"), SCALE_WIDTH, 5, + param->diffusion_dist, 2, 10, 1, 2, 0, + TRUE, 0, 0, + NULL, NULL); + CML_explorer_int_entry_init (&widget_pointers[channel_id][index], + adj, ¶m->diffusion_dist); + index++; + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index, + _("# of subranges:"), SCALE_WIDTH, 5, + param->range_num, 1, 10, 1, 2, 0, + TRUE, 0, 0, + NULL, NULL); + CML_explorer_int_entry_init (&widget_pointers[channel_id][index], + adj, ¶m->range_num); + index++; + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index, + _("P(ower factor):"), SCALE_WIDTH, 5, + param->power, 0.0, 10.0, 0.1, 1.0, 2, + TRUE, 0, 0, + NULL, NULL); + CML_explorer_double_entry_init (&widget_pointers[channel_id][index], + adj, ¶m->power); + index++; + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index, + _("Parameter k:"), SCALE_WIDTH, 5, + param->parameter_k, 0.0, 10.0, 0.1, 1.0, 2, + TRUE, 0, 0, + NULL, NULL); + CML_explorer_double_entry_init (&widget_pointers[channel_id][index], + adj, ¶m->parameter_k); + index++; + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index, + _("Range low:"), SCALE_WIDTH, 5, + param->range_l, 0.0, 1.0, 0.01, 0.1, 2, + TRUE, 0, 0, + NULL, NULL); + CML_explorer_double_entry_init (&widget_pointers[channel_id][index], + adj, ¶m->range_l); + index++; + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index, + _("Range high:"), SCALE_WIDTH, 5, + param->range_h, 0.0, 1.0, 0.01, 0.1, 2, + TRUE, 0, 0, + NULL, NULL); + CML_explorer_double_entry_init (&widget_pointers[channel_id][index], + adj, ¶m->range_h); + index++; + + chank = g_new (gpointer, 2); + chank[0] = GINT_TO_POINTER (channel_id); + chank[1] = param; + + button = gtk_button_new_with_label (_("Plot a Graph of the Settings")); + gtk_table_attach_defaults (GTK_TABLE (table), button, + 0, 3, index, index + 1); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (function_graph_new), + chank); + return table; +} + +static GtkWidget * +CML_dialog_advanced_panel_new (void) +{ + GtkWidget *vbox; + GtkWidget *subframe; + GtkWidget *table; + GtkObject *adj; + + gint index = 0; + gint widget_offset = 12; + gint channel_id; + CML_PARAM *param; + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + gtk_widget_show (vbox); + + for (channel_id = 0; channel_id < 3; channel_id++) + { + param = (CML_PARAM *)&VALS + channel_id; + + subframe = gimp_frame_new (gettext (channel_names[channel_id])); + gtk_box_pack_start (GTK_BOX (vbox), subframe, FALSE, FALSE, 0); + gtk_widget_show (subframe); + + table = gtk_table_new (3, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_container_add (GTK_CONTAINER (subframe), table); + gtk_widget_show (table); + + index = 0; + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index, + _("Ch. sensitivity:"), SCALE_WIDTH, 0, + param->ch_sensitivity, 0.0, 1.0, 0.01, 0.1, 2, + TRUE, 0, 0, + NULL, NULL); + CML_explorer_double_entry_init (&widget_pointers[channel_id][index + + widget_offset], + adj, ¶m->ch_sensitivity); + index++; + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index, + _("Mutation rate:"), SCALE_WIDTH, 0, + param->mutation_rate, 0.0, 1.0, 0.01, 0.1, 2, + TRUE, 0, 0, + NULL, NULL); + CML_explorer_double_entry_init (&widget_pointers[channel_id][index + + widget_offset], + adj, ¶m->mutation_rate); + index++; + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index, + _("Mutation dist.:"), SCALE_WIDTH, 0, + param->mutation_dist, 0.0, 1.0, 0.01, 0.1, 2, + TRUE, 0, 0, + NULL, NULL); + CML_explorer_double_entry_init (&widget_pointers[channel_id][index + + widget_offset], + adj, ¶m->mutation_dist); + } + return vbox; +} + +static void +preview_update (void) +{ + if (! CML_preview_defer) + CML_main_function (TRUE); +} + +static gboolean +function_graph_expose (GtkWidget *widget, + GdkEventExpose *event, + gpointer *data) +{ + GtkStyle *style = gtk_widget_get_style (widget); + gint x, y; + gint rgbi[3]; + gint channel_id = GPOINTER_TO_INT (data[0]); + CML_PARAM *param = data[1]; + cairo_t *cr; + + cr = gdk_cairo_create (gtk_widget_get_window (widget)); + + gdk_cairo_region (cr, event->region); + cairo_clip (cr); + + cairo_set_line_width (cr, 1.0); + + cairo_surface_flush (buffer); + + for (x = 0; x < GRAPHSIZE; x++) + { + /* hue */ + rgbi[0] = rgbi[1] = rgbi[2] = 127; + if ((0 <= channel_id) && (channel_id <= 2)) + rgbi[channel_id] = CANNONIZE ((*param), ((gdouble) x / (gdouble) 255)); + gimp_hsv_to_rgb_int (rgbi, rgbi+1, rgbi+2); + for (y = 0; y < GRAPHSIZE; y++) + { + GIMP_CAIRO_RGB24_SET_PIXEL((img+(y*img_stride+x*4)), + rgbi[0], + rgbi[1], + rgbi[2]); + } + } + + cairo_surface_mark_dirty (buffer); + + cairo_set_source_surface (cr, buffer, 0.0, 0.0); + + cairo_paint (cr); + cairo_translate (cr, 0.5, 0.5); + + cairo_move_to (cr, 0, 255); + cairo_line_to (cr, 255, 0); + gdk_cairo_set_source_color (cr, &style->white); + cairo_stroke (cr); + + y = 255 * CLAMP (logistic_function (param, 0, param->power), + 0, 1.0); + cairo_move_to (cr, 0, 255-y); + for (x = 0; x < GRAPHSIZE; x++) + { + /* curve */ + y = 255 * CLAMP (logistic_function (param, x/(gdouble)255, param->power), + 0, 1.0); + cairo_line_to (cr, x, 255-y); + } + + gdk_cairo_set_source_color (cr, &style->black); + cairo_stroke (cr); + cairo_destroy (cr); + + return TRUE; +} + +static void +function_graph_new (GtkWidget *widget, + gpointer *data) +{ + GtkWidget *dialog; + GtkWidget *frame; + GtkWidget *preview; + + dialog = gimp_dialog_new (_("Graph of the Current Settings"), PLUG_IN_ROLE, + gtk_widget_get_toplevel (widget), 0, + gimp_standard_help_func, PLUG_IN_PROC, + + _("_Close"), GTK_RESPONSE_CLOSE, + + NULL); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_container_set_border_width (GTK_CONTAINER (frame), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + preview = gtk_drawing_area_new (); + gtk_widget_set_size_request (preview, GRAPHSIZE, GRAPHSIZE); + gtk_container_add (GTK_CONTAINER (frame), preview); + gtk_widget_show (preview); + g_signal_connect (preview, "expose-event", + G_CALLBACK (function_graph_expose), data); + + gtk_widget_show (dialog); + + gimp_dialog_run (GIMP_DIALOG (dialog)); + + gtk_widget_destroy (dialog); +} + +static void +CML_set_or_randomize_seed_callback (GtkWidget *widget, + gpointer data) +{ + CML_preview_defer = TRUE; + + switch (VALS.initial_value) + { + case CML_INITIAL_RANDOM_INDEPENDENT: + VALS.initial_value = CML_INITIAL_RANDOM_FROM_SEED; + break; + case CML_INITIAL_RANDOM_SHARED: + VALS.initial_value = CML_INITIAL_RANDOM_FROM_SEED_SHARED; + break; + case CML_INITIAL_RANDOM_FROM_SEED: + VALS.initial_value = CML_INITIAL_RANDOM_INDEPENDENT; + break; + case CML_INITIAL_RANDOM_FROM_SEED_SHARED: + VALS.initial_value = CML_INITIAL_RANDOM_SHARED; + break; + default: + break; + } + if (widget_pointers[3][3].widget && widget_pointers[3][3].updater) + (widget_pointers[3][3].updater) (widget_pointers[3]+3); + if (widget_pointers[3][0].widget && widget_pointers[3][0].updater) + (widget_pointers[3][0].updater) (widget_pointers[3]); + + CML_initial_value_sensitives_update (); + + CML_preview_defer = FALSE; +} + +static void +CML_copy_parameters_callback (GtkWidget *widget, + gpointer data) +{ + gint index; + WidgetEntry *widgets; + + if (copy_source == copy_destination) + { + g_message (_("Warning: the source and the destination are the same channel.")); + return; + } + *channel_params[copy_destination] = *channel_params[copy_source]; + CML_preview_defer = TRUE; + widgets = widget_pointers[copy_destination]; + + for (index = 0; index < CML_PARAM_NUM; index++) + if (widgets[index].widget && widgets[index].updater) + (widgets[index].updater) (widgets + index); + + CML_preview_defer = FALSE; + preview_update (); +} + +static void +CML_initial_value_sensitives_update (void) +{ + gint i = 0; + gint flag1, flag2; + + flag1 = (CML_INITIAL_RANDOM_INDEPENDENT <= VALS.initial_value) + & (VALS.initial_value <= CML_INITIAL_RANDOM_FROM_SEED_SHARED); + flag2 = (CML_INITIAL_RANDOM_INDEPENDENT <= VALS.initial_value) + & (VALS.initial_value <= CML_INITIAL_RANDOM_SHARED); + + for (; i < G_N_ELEMENTS (random_sensitives) ; i++) + if (random_sensitives[i].widget) + gtk_widget_set_sensitive (random_sensitives[i].widget, + flag1 & (random_sensitives[i].logic == flag2)); +} + +static void +CML_preview_update_callback (GtkWidget *widget, + gpointer data) +{ + WidgetEntry seed_widget = widget_pointers[3][3]; + + preview_update (); + + CML_preview_defer = TRUE; + + if (seed_widget.widget && seed_widget.updater) + seed_widget.updater (&seed_widget); + + CML_preview_defer = FALSE; +} + +/* parameter file saving functions */ + +static void +CML_save_to_file_callback (GtkWidget *widget, + gpointer data) +{ + static GtkWidget *dialog = NULL; + + if (! dialog) + { + dialog = + gtk_file_chooser_dialog_new (_("Save CML Explorer Parameters"), + GTK_WINDOW (gtk_widget_get_toplevel (widget)), + GTK_FILE_CHOOSER_ACTION_SAVE, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Save"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), + TRUE); + + g_signal_connect (dialog, "response", + G_CALLBACK (CML_save_to_file_response), + NULL); + g_signal_connect (dialog, "delete-event", + G_CALLBACK (gtk_true), + NULL); + } + + if (strlen (VALS.last_file_name)) + gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), + VALS.last_file_name); + + gtk_window_present (GTK_WINDOW (dialog)); +} + +static void +CML_save_to_file_response (GtkWidget *dialog, + gint response_id, + gpointer data) +{ + gchar *filename; + FILE *file; + gint channel_id; + + if (response_id != GTK_RESPONSE_OK) + { + gtk_widget_hide (dialog); + return; + } + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + if (! filename) + return; + + file = g_fopen (filename, "wb"); + + if (! file) + { + g_message (_("Could not open '%s' for writing: %s"), + gimp_filename_to_utf8 (filename), g_strerror (errno)); + g_free (filename); + return; + } + + fprintf (file, "; This is a parameter file for CML_explorer\n"); + fprintf (file, "; File format version: %1.1f\n", PARAM_FILE_FORMAT_VERSION); + fprintf (file, ";\n"); + + for (channel_id = 0; channel_id < 3; channel_id++) + { + gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; + + CML_PARAM param = *(CML_PARAM *)(channel_params[channel_id]); + + fprintf (file, "\t%s\n", channel_names[channel_id]); + fprintf (file, "Function_type : %d (%s)\n", + param.function, function_names[param.function]); + fprintf (file, "Compostion_type : %d (%s)\n", + param.composition, composition_names[param.composition]); + fprintf (file, "Arrange : %d (%s)\n", + param.arrange, arrange_names[param.arrange]); + fprintf (file, "Cyclic_range : %d (%s)\n", + param.cyclic_range, (param.cyclic_range ? "TRUE" : "FALSE")); + fprintf (file, "Mod. rate : %s\n", + g_ascii_dtostr (buf, sizeof (buf), param.mod_rate)); + fprintf (file, "Env_sensitivtiy : %s\n", + g_ascii_dtostr (buf, sizeof (buf), param.env_sensitivity)); + fprintf (file, "Diffusion dist. : %d\n", param.diffusion_dist); + fprintf (file, "Ch. sensitivity : %s\n", + g_ascii_dtostr (buf, sizeof (buf), param.ch_sensitivity)); + fprintf (file, "Num. of Subranges: %d\n", param.range_num); + fprintf (file, "Power_factor : %s\n", + g_ascii_dtostr (buf, sizeof (buf), param.power)); + fprintf (file, "Parameter_k : %s\n", + g_ascii_dtostr (buf, sizeof (buf), param.parameter_k)); + fprintf (file, "Range_low : %s\n", + g_ascii_dtostr (buf, sizeof (buf), param.range_l)); + fprintf (file, "Range_high : %s\n", + g_ascii_dtostr (buf, sizeof (buf), param.range_h)); + fprintf (file, "Mutation_rate : %s\n", + g_ascii_dtostr (buf, sizeof (buf), param.mutation_rate)); + fprintf (file, "Mutation_distance: %s\n", + g_ascii_dtostr (buf, sizeof (buf), param.mutation_dist)); + } + + fprintf (file, "\n"); + fprintf (file, "Initial value : %d (%s)\n", + VALS.initial_value, initial_value_names[VALS.initial_value]); + fprintf (file, "Zoom scale : %d\n", VALS.scale); + fprintf (file, "Start offset : %d\n", VALS.start_offset); + fprintf (file, "Random seed : %d\n", VALS.seed); + fclose(file); + + g_message (_("Parameters were saved to '%s'"), + gimp_filename_to_utf8 (filename)); + + strncpy (VALS.last_file_name, filename, + sizeof (VALS.last_file_name) - 1); + + g_free (filename); + + gtk_widget_hide (dialog); +} + +/* parameter file loading functions */ + +static void +CML_load_from_file_callback (GtkWidget *widget, + gpointer data) +{ + static GtkWidget *dialog = NULL; + + if (! dialog) + { + dialog = + gtk_file_chooser_dialog_new (_("Load CML Explorer Parameters"), + GTK_WINDOW (gtk_widget_get_toplevel (widget)), + GTK_FILE_CHOOSER_ACTION_OPEN, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Open"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + g_signal_connect (dialog, "response", + G_CALLBACK (CML_load_from_file_response), + NULL); + g_signal_connect (dialog, "delete-event", + G_CALLBACK (gtk_true), + NULL); + } + + if (strlen (VALS.last_file_name) > 0) + gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), + VALS.last_file_name); + + gtk_window_present (GTK_WINDOW (dialog)); +} + +static void +CML_load_from_file_response (GtkWidget *dialog, + gint response_id, + gpointer data) +{ + if (response_id == GTK_RESPONSE_OK) + { + gchar *filename; + gint channel_id; + gboolean flag = TRUE; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + + gtk_widget_set_sensitive (dialog, FALSE); + flag = CML_load_parameter_file (filename, TRUE); + + g_free (filename); + + if (flag) + { + WidgetEntry *widgets; + gint index; + + CML_preview_defer = TRUE; + + for (channel_id = 0; channel_id < 3; channel_id++) + { + widgets = widget_pointers[channel_id]; + for (index = 0; index < CML_PARAM_NUM; index++) + if (widgets[index].widget && widgets[index].updater) + (widgets[index].updater) (widgets + index); + } + /* channel independent parameters */ + widgets = widget_pointers[3]; + for (index = 0; index < 4; index++) + if (widgets[index].widget && widgets[index].updater) + (widgets[index].updater) (widgets + index); + + CML_preview_defer = FALSE; + + preview_update (); + } + } + + gtk_widget_hide (GTK_WIDGET (dialog)); +} + +static gboolean +CML_load_parameter_file (const gchar *filename, + gboolean interactive_mode) +{ + FILE *file; + gint channel_id; + gboolean flag = TRUE; + CML_PARAM ch[3]; + gint initial_value = 0; + gint scale = 1; + gint start_offset = 0; + gint seed = 0; + gint old2new_function_id[] = { 3, 4, 5, 6, 7, 9, 10, 11, 1, 2 }; + + file = g_fopen (filename, "rb"); + + if (!file) + { + g_message (_("Could not open '%s' for reading: %s"), + gimp_filename_to_utf8 (filename), g_strerror (errno)); + return FALSE; + } + else + { + gchar line[CML_LINE_SIZE]; + gdouble version = 0.99; + + version = parse_line_to_gdouble (file, &flag); /* old format returns 1 */ + if (version == 1.0) + version = 0.99; + else if (! flag) + { + flag = TRUE; + version = parse_line_to_gdouble (file, &flag); /* maybe new format */ + if (flag) + fgets (line, CML_LINE_SIZE - 1, file); /* one more comment line */ + } + if (version == 0) + { + if (interactive_mode) + gimp_message (_("Error: it's not CML parameter file.")); + fclose(file); + return FALSE; + } + if (interactive_mode) + { + if (version < PARAM_FILE_FORMAT_VERSION) + g_message (_("Warning: '%s' is an old format file."), + gimp_filename_to_utf8 (filename)); + + if (PARAM_FILE_FORMAT_VERSION < version) + g_message (_("Warning: '%s' is a parameter file for a newer " + "version of CML Explorer."), + gimp_filename_to_utf8 (filename)); + } + for (channel_id = 0; flag && (channel_id < 3); channel_id++) + { + /* patched by Tim Mooney <mooney@dogbert.cc.ndsu.NoDak.edu> */ + if (fgets (line, CML_LINE_SIZE - 1, file) == NULL) /* skip channel name */ + { + flag = FALSE; + break; + } + ch[channel_id].function = parse_line_to_gint (file, &flag); + if (version < 1.0) + ch[channel_id].function = old2new_function_id [ch[channel_id].function]; + if (1.0 <= version) + ch[channel_id].composition = parse_line_to_gint (file, &flag); + else + ch[channel_id].composition = COMP_NONE; + ch[channel_id].arrange = parse_line_to_gint (file, &flag); + ch[channel_id].cyclic_range = parse_line_to_gint (file, &flag); + ch[channel_id].mod_rate = parse_line_to_gdouble (file, &flag); + ch[channel_id].env_sensitivity = parse_line_to_gdouble (file, &flag); + if (1.0 <= version) + ch[channel_id].diffusion_dist = parse_line_to_gint (file, &flag); + else + ch[channel_id].diffusion_dist = 2; + ch[channel_id].ch_sensitivity = parse_line_to_gdouble (file, &flag); + ch[channel_id].range_num = parse_line_to_gint (file, &flag); + ch[channel_id].power = parse_line_to_gdouble (file, &flag); + ch[channel_id].parameter_k = parse_line_to_gdouble (file, &flag); + ch[channel_id].range_l = parse_line_to_gdouble (file, &flag); + ch[channel_id].range_h = parse_line_to_gdouble (file, &flag); + ch[channel_id].mutation_rate = parse_line_to_gdouble (file, &flag); + ch[channel_id].mutation_dist = parse_line_to_gdouble (file, &flag); + } + if (flag) + { + gint dummy; + + if (fgets (line, CML_LINE_SIZE - 1, file) == NULL) /* skip a line */ + dummy = 1; + else + { + initial_value = parse_line_to_gint (file, &dummy); + scale = parse_line_to_gint (file, &dummy); + start_offset = parse_line_to_gint (file, &dummy); + seed = parse_line_to_gint (file, &dummy); + } + if (! dummy) + { + initial_value = 0; + scale = 1; + start_offset = 0; + seed = 0; + } + } + fclose (file); + } + + if (! flag) + { + if (interactive_mode) + gimp_message (_("Error: failed to load parameters")); + } + else + { + if ((selective_load_source == 0) || (selective_load_destination == 0)) + { + VALS.hue = ch[0]; + VALS.sat = ch[1]; + VALS.val = ch[2]; + + VALS.initial_value = initial_value; + VALS.scale = scale; + VALS.start_offset = start_offset; + VALS.seed = seed; + } + else + { + memcpy ((CML_PARAM *)&VALS + (selective_load_destination - 1), + (void *)&ch[selective_load_source - 1], + sizeof (CML_PARAM)); + } + + strncpy (VALS.last_file_name, filename, + sizeof (VALS.last_file_name) - 1); + } + return flag; +} + +static gint +parse_line_to_gint (FILE *file, + gboolean *flag) +{ + gchar line[CML_LINE_SIZE]; + gchar *str; + + if (! *flag) + return 0; + if (fgets (line, CML_LINE_SIZE - 1, file) == NULL) + { + *flag = FALSE; /* set FALSE if fail to parse */ + return 0; + } + str = &line[0]; + while (*str != ':') + if (*str == '\000') + { + *flag = FALSE; + return 0; + } + else + { + str++; + } + + return atoi (str + 1); +} + +static gdouble +parse_line_to_gdouble (FILE *file, + gboolean *flag) +{ + gchar line[CML_LINE_SIZE]; + gchar *str; + + if (! *flag) + return 0.0; + + if (fgets (line, CML_LINE_SIZE - 1, file) == NULL) + { + *flag = FALSE; /* set FALSE if fail to parse */ + return 0.0; + } + str = &line[0]; + while (*str != ':') + if (*str == '\000') + { + *flag = FALSE; + return 0.0; + } + else + { + str++; + } + + return g_ascii_strtod (str + 1, NULL); +} + + +/* toggle button functions */ + +static void +CML_explorer_toggle_update (GtkWidget *widget, + gpointer data) +{ + gimp_toggle_button_update (widget, data); + + preview_update (); +} + +static void +CML_explorer_toggle_entry_change_value (WidgetEntry *widget_entry) +{ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget_entry->widget), + *(gint *) (widget_entry->value)); +} + +static void +CML_explorer_toggle_entry_init (WidgetEntry *widget_entry, + GtkWidget *widget, + gpointer value_ptr) +{ + g_signal_connect (widget, "toggled", + G_CALLBACK (CML_explorer_toggle_update), + value_ptr); + + widget_entry->widget = widget; + widget_entry->value = value_ptr; + widget_entry->updater = CML_explorer_toggle_entry_change_value; +} + +/* int adjustment functions */ + +static void +CML_explorer_int_adjustment_update (GtkAdjustment *adjustment, + gpointer data) +{ + gimp_int_adjustment_update (adjustment, data); + + preview_update (); +} + +static void +CML_explorer_int_entry_change_value (WidgetEntry *widget_entry) +{ + GtkAdjustment *adjustment = (GtkAdjustment *) (widget_entry->widget); + + gtk_adjustment_set_value (adjustment, *(gint *) (widget_entry->value)); +} + +static void +CML_explorer_int_entry_init (WidgetEntry *widget_entry, + GtkObject *adjustment, + gpointer value_ptr) +{ + g_signal_connect (adjustment, "value-changed", + G_CALLBACK (CML_explorer_int_adjustment_update), + value_ptr); + + widget_entry->widget = (GtkWidget *) adjustment; + widget_entry->value = value_ptr; + widget_entry->updater = CML_explorer_int_entry_change_value; +} + +/* double adjustment functions */ + +static void +CML_explorer_double_adjustment_update (GtkAdjustment *adjustment, + gpointer data) +{ + gimp_double_adjustment_update (adjustment, data); + + preview_update (); +} + +static void +CML_explorer_double_entry_change_value (WidgetEntry *widget_entry) +{ + GtkAdjustment *adjustment = (GtkAdjustment *) (widget_entry->widget); + + gtk_adjustment_set_value (adjustment, *(gdouble *) (widget_entry->value)); +} + +static void +CML_explorer_double_entry_init (WidgetEntry *widget_entry, + GtkObject *adjustment, + gpointer value_ptr) +{ + g_signal_connect (adjustment, "value-changed", + G_CALLBACK (CML_explorer_double_adjustment_update), + value_ptr); + + widget_entry->widget = (GtkWidget *) adjustment; + widget_entry->value = value_ptr; + widget_entry->updater = CML_explorer_double_entry_change_value; +} + +/* menu functions */ + +static void +CML_explorer_menu_update (GtkWidget *widget, + gpointer data) +{ + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) data); + + preview_update (); +} + +static void +CML_initial_value_menu_update (GtkWidget *widget, + gpointer data) +{ + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) data); + + CML_initial_value_sensitives_update (); + preview_update (); +} + +static void +CML_explorer_menu_entry_change_value (WidgetEntry *widget_entry) +{ + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (widget_entry->widget), + *(gint *) (widget_entry->value)); +} + +static void +CML_explorer_menu_entry_init (WidgetEntry *widget_entry, + GtkWidget *widget, + gpointer value_ptr) +{ + widget_entry->widget = widget; + widget_entry->value = value_ptr; + widget_entry->updater = CML_explorer_menu_entry_change_value; +} |