1
/* -*- mode: c; c-basic-offset: 4; -*-
3
* bifurcation-diagram.c - A HistogramImager that renders a bifurcation diagram
4
* in which one axis interpolates across de Jong parameters
5
* and the other axis shows a 1 dimensional projection of the
6
* image at those parameters.
8
* Fyre - rendering and interactive exploration of chaotic functions
9
* Copyright (C) 2004-2005 David Trowbridge and Micah Dowty
11
* This program is free software; you can redistribute it and/or
12
* modify it under the terms of the GNU General Public License
13
* as published by the Free Software Foundation; either version 2
14
* of the License, or (at your option) any later version.
16
* This program is distributed in the hope that it will be useful,
17
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
* GNU General Public License for more details.
21
* You should have received a copy of the GNU General Public License
22
* along with this program; if not, write to the Free Software
23
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29
#include "bifurcation-diagram.h"
30
#include "math-util.h"
32
static void bifurcation_diagram_class_init (BifurcationDiagramClass *klass);
33
static void bifurcation_diagram_init (BifurcationDiagram *self);
34
static void bifurcation_diagram_dispose (GObject *gobject);
36
static void bifurcation_diagram_init_columns (BifurcationDiagram *self);
37
static BifurcationColumn* bifurcation_diagram_next_column (BifurcationDiagram *self);
38
static void bifurcation_diagram_get_column_params (BifurcationDiagram *self,
39
BifurcationColumn *column,
42
static gpointer parent_class = NULL;
45
/************************************************************************************/
46
/**************************************************** Initialization / Finalization */
47
/************************************************************************************/
49
GType bifurcation_diagram_get_type(void) {
50
static GType dj_type = 0;
53
static const GTypeInfo dj_info = {
54
sizeof(BifurcationDiagramClass),
56
NULL, /* base_finalize */
57
(GClassInitFunc) bifurcation_diagram_class_init,
58
NULL, /* class_finalize */
59
NULL, /* class_data */
60
sizeof(BifurcationDiagram),
62
(GInstanceInitFunc) bifurcation_diagram_init,
65
dj_type = g_type_register_static(HISTOGRAM_IMAGER_TYPE, "BifurcationDiagram", &dj_info, 0);
71
static void bifurcation_diagram_class_init(BifurcationDiagramClass *klass) {
72
GObjectClass *object_class;
73
parent_class = g_type_class_ref(G_TYPE_OBJECT);
74
object_class = (GObjectClass*) klass;
76
object_class->dispose = bifurcation_diagram_dispose;
79
static void bifurcation_diagram_init(BifurcationDiagram *self) {
82
static void bifurcation_diagram_dispose(GObject *gobject) {
83
BifurcationDiagram *self = BIFURCATION_DIAGRAM(gobject);
86
g_free(self->columns);
90
if (self->interpolant) {
91
g_object_unref(self->interpolant);
92
self->interpolant = NULL;
95
if (self->interp_data && self->interp_data_free)
96
self->interp_data_free(self->interp_data);
97
self->interp_data = NULL;
98
self->interp_data_free = NULL;
100
G_OBJECT_CLASS(parent_class)->dispose(gobject);
103
BifurcationDiagram* bifurcation_diagram_new() {
104
return BIFURCATION_DIAGRAM(g_object_new(bifurcation_diagram_get_type(), NULL));
108
/************************************************************************************/
109
/************************************************************************* Settings */
110
/************************************************************************************/
112
void bifurcation_diagram_set_interpolator (BifurcationDiagram *self,
113
ParameterInterpolator *interp,
114
gpointer interp_data,
115
GFreeFunc interp_data_free) {
116
/* Free the old interpolator data */
117
if (self->interp_data && self->interp_data_free)
118
self->interp_data_free(self->interp_data);
120
self->interp = interp;
121
self->interp_data = interp_data;
122
self->interp_data_free = interp_data_free;
124
self->calc_dirty_flag = TRUE;
127
void bifurcation_diagram_set_linear_endpoints (BifurcationDiagram *self,
130
ParameterHolderPair *pair, *oldpair;
132
/* If the old interpolator was also linear, see if we can avoid this update... */
133
if (self->interp == PARAMETER_INTERPOLATOR(parameter_holder_interpolate_linear)) {
134
oldpair = (ParameterHolderPair*) self->interp_data;
136
if ((!memcmp(&DE_JONG(oldpair->a)->param, &first->param, sizeof(first->param))) &&
137
(!memcmp(&DE_JONG(oldpair->b)->param, &second->param, sizeof(second->param))))
141
pair = g_new(ParameterHolderPair, 1);
142
pair->a = g_object_ref(first);
143
pair->b = g_object_ref(second);
145
bifurcation_diagram_set_interpolator(self, PARAMETER_INTERPOLATOR(parameter_holder_interpolate_linear),
146
pair, (GFreeFunc) parameter_holder_pair_free);
150
/************************************************************************************/
151
/************************************************************************** Columns */
152
/************************************************************************************/
154
static void bifurcation_diagram_init_columns (BifurcationDiagram *self) {
158
/* Do we need to resize the column array? */
159
histogram_imager_get_hist_size(HISTOGRAM_IMAGER(self), &hist_width, NULL);
160
if (hist_width != self->num_columns) {
163
g_free(self->columns);
165
/* Create columns and number them */
166
self->num_columns = hist_width;
167
self->current_column = 0;
168
self->columns = g_new0(BifurcationColumn, self->num_columns);
169
for (i=0; i<self->num_columns; i++)
170
self->columns[i].ix = i;
172
/* Shuffle them, so we render in a seemingly-random order */
173
for (i=self->num_columns-1; i>=0; i--) {
174
j = g_random_int_range(0, i+1);
175
tmp = self->columns[i].ix;
176
self->columns[i].ix = self->columns[j].ix;
177
self->columns[j].ix = tmp;
180
self->calc_dirty_flag = TRUE;
183
/* Should we be resetting the columns, either due to
184
* a change in interpolants or a column array resize?
186
if (self->calc_dirty_flag || HISTOGRAM_IMAGER(self)->histogram_clear_flag) {
187
/* Clear the histogram if it isn't already */
188
if (!HISTOGRAM_IMAGER(self)->histogram_clear_flag)
189
histogram_imager_clear(HISTOGRAM_IMAGER(self));
191
for (i=0; i<self->num_columns; i++) {
192
/* Invalidate each column and each interpolated parameter set */
193
self->columns[i].point.valid = FALSE;
194
for (j=0; j<(sizeof(self->columns[0].interpolated)/
195
sizeof(self->columns[0].interpolated[0])); j++) {
196
self->columns[i].interpolated[j].valid = FALSE;
200
HISTOGRAM_IMAGER(self)->histogram_clear_flag = FALSE;
201
self->calc_dirty_flag = FALSE;
205
static BifurcationColumn* bifurcation_diagram_next_column (BifurcationDiagram *self) {
206
/* Get the next column, wrapping around when we hit the end */
207
BifurcationColumn *column = &self->columns[self->current_column];
208
if (++self->current_column >= self->num_columns)
209
self->current_column = 0;
211
/* Initialize this column's point if it isn't yet */
212
if (!column->point.valid) {
213
column->point.x = uniform_variate();
214
column->point.y = uniform_variate();
215
column->point.valid = TRUE;
221
static void bifurcation_diagram_get_column_params (BifurcationDiagram *self,
222
BifurcationColumn *column,
223
DeJongParams *param) {
224
/* Get a random parameter set from the given column, creating it if necessary */
225
int interpIndex = g_random_int_range(0, sizeof(self->columns[0].interpolated)/
226
sizeof(self->columns[0].interpolated[0]));
228
if (!column->interpolated[interpIndex].valid) {
230
/* Create an interpolant if we don't have one yet */
231
if (!self->interpolant)
232
self->interpolant = de_jong_new();
234
/* Pick a random place within the column to perform the interpolation */
235
self->interp(PARAMETER_HOLDER(self->interpolant),
236
(column->ix + uniform_variate()) / (self->num_columns - 1),
238
column->interpolated[interpIndex].param = self->interpolant->param;
240
column->interpolated[interpIndex].valid = TRUE;
243
*param = column->interpolated[interpIndex].param;
247
/************************************************************************************/
248
/********************************************************************** Calculation */
249
/************************************************************************************/
251
void bifurcation_diagram_calculate (BifurcationDiagram *self,
252
guint iterations_total,
253
guint iterations_per_column) {
255
BifurcationColumn *column;
257
int hist_width, hist_height;
258
double x, y, point_x, point_y;
259
int i, col_i, ix, iy;
261
const float y_min = -3;
262
const float y_max = 3;
264
bifurcation_diagram_init_columns(self);
265
histogram_imager_prepare_plots(HISTOGRAM_IMAGER(self), &plot);
266
histogram_imager_get_hist_size(HISTOGRAM_IMAGER(self), &hist_width, &hist_height);
268
for (i=iterations_total; i;) {
270
column = bifurcation_diagram_next_column(self);
271
bifurcation_diagram_get_column_params(self, column, ¶m);
273
point_x = column->point.x;
274
point_y = column->point.y;
276
for(col_i=iterations_per_column; i && col_i; --i, --col_i) {
277
/* These are the actual Peter de Jong map equations. The new point value
278
* gets stored into 'point', then we go on and mess with x and y before plotting.
280
x = sin(param.a * point_y) - cos(param.b * point_x);
281
y = sin(param.c * point_x) - cos(param.d * point_y);
285
if (y >= y_min && y < y_max) {
286
iy = (int)( (y - y_min) / (y_max - y_min) * hist_height );
288
HISTOGRAM_IMAGER_PLOT(plot, ix, iy);
292
column->point.x = point_x;
293
column->point.y = point_y;
296
histogram_imager_finish_plots(HISTOGRAM_IMAGER(self), &plot);