/* 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 * 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 . * * 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 * Sean P Cier * David Mosberger-Tang * Michael Sweet * */ #include "config.h" #include #include #include #include #include #include #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, "/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 */ 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; }