/* Copyright (C) 1992-2009 Spotworks LLC This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "private.h" #include "rect.h" #include "img.h" #include "build/config.h" #include "variations.h" #include "interpolation.h" #include "parser.h" #include "palettes.h" #include "random.h" #include #include #include #ifdef HAVE_STDINT_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #define CHOOSE_XFORM_GRAIN 16384 #define CHOOSE_XFORM_GRAIN_M1 16383 unsigned short *flam3_create_xform_distrib(flam3_genome *cp) { /* Xform distrib is created in this function */ int numrows; int dist_row,i; unsigned short *xform_distrib; numrows = cp->num_xforms - (cp->final_xform_index>=0) + 1; xform_distrib = calloc(numrows*CHOOSE_XFORM_GRAIN,sizeof(unsigned short)); /* First, set up the first row of the xform_distrib (raw weights) */ flam3_create_chaos_distrib(cp, -1, xform_distrib); /* Check for non-unity chaos */ cp->chaos_enable = 1 - flam3_check_unity_chaos(cp); if (cp->chaos_enable) { /* Now set up a row for each of the xforms */ dist_row = 0; for (i=0;inum_xforms;i++) { if (cp->final_xform_index == i) continue; else dist_row++; if (flam3_create_chaos_distrib(cp, i, &(xform_distrib[CHOOSE_XFORM_GRAIN*(dist_row)]))) { free(xform_distrib); return(NULL); } } } return(xform_distrib); } void rotate_by(double *p, double *center, double by) { double r[2]; double th = by * 2 * M_PI / 360.0; double c = cos(th); double s = -sin(th); p[0] -= center[0]; p[1] -= center[1]; r[0] = c * p[0] - s * p[1]; r[1] = s * p[0] + c * p[1]; p[0] = r[0] + center[0]; p[1] = r[1] + center[1]; } int flam3_check_unity_chaos(flam3_genome *cp) { int i,j; int num_std; int unity=1; num_std = cp->num_xforms - (cp->final_xform_index >= 0); for (i=0;ichaos[i][j]-1.0) > EPS) unity=0; } } return(unity); } int flam3_create_chaos_distrib(flam3_genome *cp, int xi, unsigned short *xform_distrib) { /* Xform distrib is a preallocated array of CHOOSE_XFORM_GRAIN chars */ /* address of array is passed in, contents are modified */ double t,r,dr; int i,j; int num_std; //fprintf(stdout,"storing at %ld\n",xform_distrib); num_std = cp->num_xforms - (cp->final_xform_index >= 0); dr = 0.0; for (i = 0; i < num_std; i++) { double d = cp->xform[i].density; if (xi>=0) d *= cp->chaos[xi][i]; //fprintf(stdout,"%f ",d); if (d < 0.0) { fprintf(stderr, "xform weight must be non-negative, not %g.\n", d); return(1); } dr += d; } //fprintf(stdout,"dr=%f\n",dr); if (dr == 0.0) { fprintf(stderr, "cannot iterate empty flame.\n"); return(1); } dr = dr / CHOOSE_XFORM_GRAIN; j = 0; t = cp->xform[0].density; if (xi>=0) t *= cp->chaos[xi][0]; r = 0.0; for (i = 0; i < CHOOSE_XFORM_GRAIN; i++) { while (r >= t) { j++; if (xi>=0) t += cp->xform[j].density*cp->chaos[xi][j]; else t += cp->xform[j].density; } //fprintf(stdout,"%d ",j); xform_distrib[i] = j; r += dr; } //fprintf(stdout,"\n---\n"); return(0); } /* * run the function system described by CP forward N generations. store * the N resulting 4-vectors in SAMPLES. the initial point is passed in * SAMPLES[0..3]. ignore the first FUSE iterations. */ int flam3_iterate(flam3_genome *cp, int n, int fuse, const double4 in, double4 *samples, const unsigned short *xform_distrib, randctx *rc) { int i; double4 p, q; int consec = 0; int badvals = 0; int lastxf=0; int fn; p = in; /* Perform precalculations */ for (i=0;inum_xforms;i++) xform_precalc(cp,i); for (i = -fuse; i < n; i++) { // fn = xform_distrib[ lastxf*CHOOSE_XFORM_GRAIN + (((unsigned)irand(rc)) % CHOOSE_XFORM_GRAIN)]; if (cp->chaos_enable) fn = xform_distrib[ lastxf*CHOOSE_XFORM_GRAIN + (rand_u64(rc) & CHOOSE_XFORM_GRAIN_M1)]; else fn = xform_distrib[ rand_u64(rc) & CHOOSE_XFORM_GRAIN_M1 ]; if (apply_xform(cp, fn, p, &q, rc)>0) { consec ++; badvals ++; if (consec<5) { p = q; --i; continue; } else consec = 0; } else consec = 0; /* Store the last used transform */ lastxf = fn+1; p = q; if (cp->final_xform_enable == 1) { if (cp->xform[cp->final_xform_index].opacity==1 || rand_d01(rc)xform[cp->final_xform_index].opacity) { apply_xform(cp, cp->final_xform_index, p, &q, rc); /* Keep the opacity from the original xform */ q = (double4) { q[0], q[1], q[2], p[3] }; } } /* if fuse over, store it */ if (i >= 0) { samples[i] = q; } } return(badvals); } flam3_genome *sheep_loop(flam3_genome *cp, double blend) { flam3_genome *result; int i; /* Allocate the genome - this must be freed by calling function */ result = calloc(1,sizeof(flam3_genome)); /* Clear it */ clear_cp(result,flam3_defaults_on); /* Copy the original */ flam3_copy(result,cp); /* * Insert motion magic here : * if there are motion elements, we will modify the contents of * the result genome before flam3_rotate is called. */ for (i=0;inum_xforms;i++) { if (cp->xform[i].num_motion>0) { /* Apply motion parameters to result.xform[i] using blend parameter */ apply_motion_parameters(&cp->xform[i], &result->xform[i], blend); } /* Delete the motion parameters from the result */ flam3_delete_motion_elements(&result->xform[i]); } /* Rotate the affines */ flam3_rotate(result, blend*360.0,result->interpolation_type); return(result); } /* BY is angle in degrees */ void flam3_rotate(flam3_genome *cp, double by, int interpolation_type) { int i; for (i = 0; i < cp->num_xforms; i++) { double r[2][2]; double T[2][2]; double U[2][2]; double dtheta = by * 2.0 * M_PI / 360.0; /* Don't rotate xforms with > 0 animate values */ if (cp->xform[i].animate == 0.0) continue; if (cp->xform[i].padding == 1) { if (interpolation_type == flam3_inttype_compat) { /* gen 202 era flam3 did not rotate padded xforms */ continue; } else if (interpolation_type == flam3_inttype_older) { /* not sure if 198 era flam3 rotated padded xforms */ continue; } else if (interpolation_type == flam3_inttype_linear) { /* don't rotate for prettier symsings */ continue; } else if (interpolation_type == flam3_inttype_log) { /* Current flam3: what do we prefer? */ //continue; } } /* Do NOT rotate final xforms */ if (cp->final_xform_enable==1 && cp->final_xform_index==i) continue; r[1][1] = r[0][0] = cos(dtheta); r[0][1] = sin(dtheta); r[1][0] = -r[0][1]; T[0][0] = cp->xform[i].c[0][0]; T[1][0] = cp->xform[i].c[1][0]; T[0][1] = cp->xform[i].c[0][1]; T[1][1] = cp->xform[i].c[1][1]; mult_matrix(r, T, U); cp->xform[i].c[0][0] = U[0][0]; cp->xform[i].c[1][0] = U[1][0]; cp->xform[i].c[0][1] = U[0][1]; cp->xform[i].c[1][1] = U[1][1]; } } #define APPMOT(x) do { addto->x += mot[i].x * motion_funcs(func,freq*blend); } while (0); void apply_motion_parameters(flam3_xform *xf, flam3_xform *addto, double blend) { int i,j,k; int freq; int func; flam3_xform* mot; mot = xf->motion; /* Loop over the motion elements and add their contribution to the original vals */ for (i=0; inum_motion; i++) { freq = mot->motion_freq; func = mot->motion_func; APPMOT(density); /* Must ensure > 0 after all is applied */ APPMOT(color); /* Must ensure [0,1] after all is applied */ APPMOT(opacity); APPMOT(color_speed); APPMOT(animate); APPMOT(blob_low); APPMOT(blob_high); APPMOT(blob_waves); APPMOT(pdj_a); APPMOT(pdj_b); APPMOT(pdj_c); APPMOT(pdj_d); APPMOT(fan2_x); APPMOT(fan2_y); APPMOT(rings2_val); APPMOT(perspective_angle); APPMOT(perspective_dist); APPMOT(julian_power); APPMOT(julian_dist); APPMOT(juliascope_power); APPMOT(juliascope_dist); APPMOT(radial_blur_angle); APPMOT(pie_slices); APPMOT(pie_rotation); APPMOT(pie_thickness); APPMOT(ngon_sides); APPMOT(ngon_power); APPMOT(ngon_circle); APPMOT(ngon_corners); APPMOT(curl_c1); APPMOT(curl_c2); APPMOT(rectangles_x); APPMOT(rectangles_y); APPMOT(amw_amp); APPMOT(disc2_rot); APPMOT(disc2_twist); APPMOT(super_shape_rnd); APPMOT(super_shape_m); APPMOT(super_shape_n1); APPMOT(super_shape_n2); APPMOT(super_shape_n3); APPMOT(super_shape_holes); APPMOT(flower_petals); APPMOT(flower_holes); APPMOT(conic_eccentricity); APPMOT(conic_holes); APPMOT(parabola_height); APPMOT(parabola_width); APPMOT(bent2_x); APPMOT(bent2_y); APPMOT(bipolar_shift); APPMOT(cell_size); APPMOT(cpow_r); APPMOT(cpow_i); APPMOT(cpow_power); APPMOT(curve_xamp); APPMOT(curve_yamp); APPMOT(curve_xlength); APPMOT(curve_ylength); APPMOT(escher_beta); APPMOT(lazysusan_x); APPMOT(lazysusan_y); APPMOT(lazysusan_twist); APPMOT(lazysusan_space); APPMOT(lazysusan_spin); APPMOT(modulus_x); APPMOT(modulus_y); APPMOT(oscope_separation); APPMOT(oscope_frequency); APPMOT(oscope_amplitude); APPMOT(oscope_damping); APPMOT(popcorn2_x); APPMOT(popcorn2_y); APPMOT(popcorn2_c); APPMOT(separation_x); APPMOT(separation_xinside); APPMOT(separation_y); APPMOT(separation_yinside); APPMOT(split_xsize); APPMOT(split_ysize); APPMOT(splits_x); APPMOT(splits_y); APPMOT(stripes_space); APPMOT(stripes_warp); APPMOT(wedge_angle); APPMOT(wedge_hole); APPMOT(wedge_count); APPMOT(wedge_swirl); APPMOT(wedge_julia_angle); APPMOT(wedge_julia_count); APPMOT(wedge_julia_power); APPMOT(wedge_julia_dist); APPMOT(wedge_sph_angle); APPMOT(wedge_sph_hole); APPMOT(wedge_sph_count); APPMOT(wedge_sph_swirl); APPMOT(whorl_inside); APPMOT(whorl_outside); APPMOT(waves2_scalex); APPMOT(waves2_scaley); APPMOT(waves2_freqx); APPMOT(waves2_freqy); APPMOT(auger_sym); APPMOT(auger_weight); APPMOT(auger_freq); APPMOT(auger_scale); APPMOT(flux_spread); APPMOT(mobius_re_a); APPMOT(mobius_re_b); APPMOT(mobius_re_c); APPMOT(mobius_re_d); APPMOT(mobius_im_a); APPMOT(mobius_im_b); APPMOT(mobius_im_c); APPMOT(mobius_im_d); for (j = 0; j < flam3_nvariations; j++) APPMOT(var[j]); for (j=0; j<3; j++) { for (k=0; k<2; k++) { APPMOT(c[j][k]); APPMOT(post[j][k]); } } } /* Make sure certain params are within reasonable bounds */ if (addto->color<0) addto->color=0; if (addto->color>1) addto->color=1; if (addto->density<0) addto->density=0; } /* * create a control point that interpolates between the control points * passed in CPS. CPS must be sorted by time. */ void flam3_interpolate(flam3_genome cps[], int ncps, double time, double stagger, flam3_genome *result) { int i1, i2; double c[2]; flam3_genome cpi[4]; int smoothflag = 0; if (1 == ncps) { flam3_copy(result, &(cps[0])); return; } if (cps[0].time >= time) { i1 = 0; i2 = 1; } else if (cps[ncps - 1].time <= time) { i1 = ncps - 2; i2 = ncps - 1; } else { i1 = 0; while (cps[i1].time < time) i1++; i1--; i2 = i1 + 1; } c[0] = (cps[i2].time - time) / (cps[i2].time - cps[i1].time); c[1] = 1.0 - c[0]; memset(cpi, 0, 4*sizeof(flam3_genome)); /* To interpolate the xforms, we will make copies of the source cps */ /* and ensure that they both have the same number before progressing */ if (flam3_interpolation_linear == cps[i1].interpolation) { flam3_align(&cpi[0], &cps[i1], 2); smoothflag = 0; } else { if (0 == i1) { fprintf(stderr, "error: cannot use smooth interpolation on first segment.\n"); fprintf(stderr, "reverting to linear interpolation.\n"); flam3_align(&cpi[0], &cps[i1], 2); smoothflag = 0; } if (ncps-1 == i2) { fprintf(stderr, "error: cannot use smooth interpolation on last segment.\n"); fprintf(stderr, "reverting to linear interpolation.\n"); flam3_align(&cpi[0], &cps[i1], 2); smoothflag = 0; } flam3_align(&cpi[0], &cps[i1-1], 4); smoothflag = 1; } /* Clear the destination cp */ clear_cp(result, 1); if (cpi[0].final_xform_index >= 0) { flam3_add_xforms(result, cpi[0].num_xforms-1, 0, 0); flam3_add_xforms(result, 1, 0, 1); } else flam3_add_xforms(result, cpi[0].num_xforms, 0, 0); result->time = time; result->interpolation = flam3_interpolation_linear; result->interpolation_type = cpi[0].interpolation_type; result->palette_interpolation = flam3_palette_interpolation_hsv; if (!smoothflag) { flam3_interpolate_n(result, 2, cpi, c, stagger); } else { interpolate_catmull_rom(cpi, c[1], result); clear_cp(&(cpi[2]),0); clear_cp(&(cpi[3]),0); } clear_cp(&(cpi[0]),0); clear_cp(&(cpi[1]),0); } void flam3_copy_params(flam3_xform *dest, flam3_xform *src, int varn) { /* We only want to copy param var coefs for this one */ if (varn==VAR_BLOB) { /* Blob */ dest->blob_low = src->blob_low; dest->blob_high = src->blob_high; dest->blob_waves = src->blob_waves; } else if (varn==VAR_PDJ) { /* PDJ */ dest->pdj_a = src->pdj_a; dest->pdj_b = src->pdj_b; dest->pdj_c = src->pdj_c; dest->pdj_d = src->pdj_d; } else if (varn==VAR_FAN2) { /* Fan2 */ dest->fan2_x = src->fan2_x; dest->fan2_y = src->fan2_y; } else if (varn==VAR_RINGS2) { /* Rings2 */ dest->rings2_val = src->rings2_val; } else if (varn==VAR_PERSPECTIVE) { /* Perspective */ dest->perspective_angle = src->perspective_angle; dest->perspective_dist = src->perspective_dist; dest->persp_vsin = src->persp_vsin; dest->persp_vfcos = src->persp_vfcos; } else if (varn==VAR_JULIAN) { /* Julia_N */ dest->julian_power = src->julian_power; dest->julian_dist = src->julian_dist; dest->julian_rN = src->julian_rN; dest->julian_cn = src->julian_cn; } else if (varn==VAR_JULIASCOPE) { /* Julia_Scope */ dest->juliascope_power = src->juliascope_power; dest->juliascope_dist = src->juliascope_dist; dest->juliascope_rN = src->juliascope_rN; dest->juliascope_cn = src->juliascope_cn; } else if (varn==VAR_RADIAL_BLUR) { /* Radial Blur */ dest->radial_blur_angle = src->radial_blur_angle; } else if (varn==VAR_PIE) { /* Pie */ dest->pie_slices = src->pie_slices; dest->pie_rotation = src->pie_rotation; dest->pie_thickness = src->pie_thickness; } else if (varn==VAR_NGON) { /* Ngon */ dest->ngon_sides = src->ngon_sides; dest->ngon_power = src->ngon_power; dest->ngon_corners = src->ngon_corners; dest->ngon_circle = src->ngon_circle; } else if (varn==VAR_CURL) { /* Curl */ dest->curl_c1 = src->curl_c1; dest->curl_c2 = src->curl_c2; } else if (varn==VAR_RECTANGLES) { /* Rect */ dest->rectangles_x = src->rectangles_x; dest->rectangles_y = src->rectangles_y; } else if (varn==VAR_DISC2) { /* Disc2 */ dest->disc2_rot = src->disc2_rot; dest->disc2_twist = src->disc2_twist; } else if (varn==VAR_SUPER_SHAPE) { /* Supershape */ dest->super_shape_rnd = src->super_shape_rnd; dest->super_shape_m = src->super_shape_m; dest->super_shape_n1 = src->super_shape_n1; dest->super_shape_n2 = src->super_shape_n2; dest->super_shape_n3 = src->super_shape_n3; dest->super_shape_holes = src->super_shape_holes; } else if (varn==VAR_FLOWER) { /* Flower */ dest->flower_petals = src->flower_petals; dest->flower_petals = src->flower_petals; } else if (varn==VAR_CONIC) { /* Conic */ dest->conic_eccentricity = src->conic_eccentricity; dest->conic_holes = src->conic_holes; } else if (varn==VAR_PARABOLA) { /* Parabola */ dest->parabola_height = src->parabola_height; dest->parabola_width = src->parabola_width; } else if (varn==VAR_BENT2) { /* Bent2 */ dest->bent2_x = src->bent2_x; dest->bent2_y = src->bent2_y; } else if (varn==VAR_BIPOLAR) { /* Bipolar */ dest->bipolar_shift = src->bipolar_shift; } else if (varn==VAR_CELL) { /* Cell */ dest->cell_size = src->cell_size; } else if (varn==VAR_CPOW) { /* Cpow */ dest->cpow_i = src->cpow_i; dest->cpow_r = src->cpow_r; dest->cpow_power = src->cpow_power; } else if (varn==VAR_CURVE) { /* Curve */ dest->curve_xamp = src->curve_xamp; dest->curve_yamp = src->curve_yamp; dest->curve_xlength = src->curve_xlength; dest->curve_ylength = src->curve_ylength; } else if (varn==VAR_ESCHER) { /* Escher */ dest->escher_beta = src->escher_beta; } else if (varn==VAR_LAZYSUSAN) { /* Lazysusan */ dest->lazysusan_x = src->lazysusan_x; dest->lazysusan_y = src->lazysusan_y; dest->lazysusan_spin = src->lazysusan_spin; dest->lazysusan_space = src->lazysusan_space; dest->lazysusan_twist = src->lazysusan_twist; } else if (varn==VAR_MODULUS) { /* Modulus */ dest->modulus_x = src->modulus_x; dest->modulus_y = src->modulus_y; } else if (varn==VAR_OSCILLOSCOPE) { /* Oscope */ dest->oscope_separation = src->oscope_separation; dest->oscope_frequency = src->oscope_frequency; dest->oscope_amplitude = src->oscope_amplitude; dest->oscope_damping = src->oscope_damping; } else if (varn==VAR_POPCORN2) { /* Popcorn2 */ dest->popcorn2_x = src->popcorn2_x; dest->popcorn2_y = src->popcorn2_y; dest->popcorn2_c = src->popcorn2_c; } else if (varn==VAR_SEPARATION) { /* Separation */ dest->separation_x = src->separation_x; dest->separation_y = src->separation_y; dest->separation_xinside = src->separation_xinside; dest->separation_yinside = src->separation_yinside; } else if (varn==VAR_SPLIT) { /* Split */ dest->split_xsize = src->split_xsize; dest->split_ysize = src->split_ysize; } else if (varn==VAR_SPLITS) { /* Splits */ dest->splits_x = src->splits_x; dest->splits_y = src->splits_y; } else if (varn==VAR_STRIPES) { /* Stripes */ dest->stripes_space = src->stripes_space; dest->stripes_warp = src->stripes_warp; } else if (varn==VAR_WEDGE) { /* Wedge */ dest->wedge_angle = src->wedge_angle; dest->wedge_hole = src->wedge_hole; dest->wedge_count = src->wedge_count; dest->wedge_swirl = src->wedge_swirl; } else if (varn==VAR_WEDGE_JULIA) { /* Wedge_Julia */ dest->wedge_julia_angle = src->wedge_julia_angle; dest->wedge_julia_count = src->wedge_julia_count; dest->wedge_julia_power = src->wedge_julia_power; dest->wedge_julia_dist = src->wedge_julia_dist; dest->wedgeJulia_cf = src->wedgeJulia_cf; dest->wedgeJulia_cn = src->wedgeJulia_cn; dest->wedgeJulia_rN = src->wedgeJulia_rN; } else if (varn==VAR_WEDGE_SPH) { /* Wedge_sph */ dest->wedge_sph_angle = src->wedge_sph_angle; dest->wedge_sph_hole = src->wedge_sph_hole; dest->wedge_sph_count = src->wedge_sph_count; dest->wedge_sph_swirl = src->wedge_sph_swirl; } else if (varn==VAR_WHORL) { /* whorl */ dest->whorl_inside = src->whorl_inside; dest->whorl_outside = src->whorl_outside; } else if (varn==VAR_WAVES2) { /* waves2 */ dest->waves2_scalex = src->waves2_scalex; dest->waves2_scaley = src->waves2_scaley; dest->waves2_freqx = src->waves2_freqx; dest->waves2_freqy = src->waves2_freqy; } else if (varn==VAR_AUGER) { /* auger */ dest->auger_sym = src->auger_sym; dest->auger_weight = src->auger_weight; dest->auger_freq = src->auger_freq; dest->auger_scale = src->auger_scale; } else if (varn==VAR_FLUX) { /* flux */ dest->flux_spread = src->flux_spread; } else if (varn==VAR_MOBIUS) { /* mobius */ dest->mobius_re_a = src->mobius_re_a; dest->mobius_re_b = src->mobius_re_b; dest->mobius_re_c = src->mobius_re_c; dest->mobius_re_d = src->mobius_re_d; dest->mobius_im_a = src->mobius_im_a; dest->mobius_im_b = src->mobius_im_b; dest->mobius_im_c = src->mobius_im_c; dest->mobius_im_d = src->mobius_im_d; } } /* Motion support functions */ void flam3_add_motion_element(flam3_xform *xf) { /* Add one to the xform's count of motion elements */ xf->num_motion++; /* Reallocate the motion storage to include the empty space */ xf->motion = (struct xform *)realloc(xf->motion, xf->num_motion * sizeof(struct xform)); /* Initialize the motion element */ /* In this case, all elements should be set to 0 */ memset( &(xf->motion[xf->num_motion-1]), 0, sizeof(struct xform)); } /* Motion support functions */ void flam3_delete_motion_elements(flam3_xform *xf) { /* Free the motion elements */ if (xf->num_motion>0) { free(xf->motion); xf->num_motion = 0; } } /* Xform support functions */ void flam3_add_xforms(flam3_genome *thiscp, int num_to_add, int interp_padding, int final_flag) { int i,j; int old_num = thiscp->num_xforms; int oldstd,numstd; flam3_xform tmp; oldstd = thiscp->num_xforms - (thiscp->final_xform_index >= 0); /* !!! must make sure that if final_flag is specified, we don't already have a final xform! !!! */ // if (thiscp->num_xforms > 0) thiscp->xform = (flam3_xform *)realloc(thiscp->xform, (thiscp->num_xforms + num_to_add) * sizeof(flam3_xform)); // else // thiscp->xform = (flam3_xform *)malloc(num_to_add * sizeof(flam3_xform)); thiscp->num_xforms += num_to_add; /* Initialize all the new xforms */ initialize_xforms(thiscp, old_num); /* Set the padding flag for the new xforms */ if (interp_padding) { for (i = old_num ; i < thiscp->num_xforms ; i++) thiscp->xform[i].padding=1; } /* If the final xform is not the last xform in the list, make it so */ if (thiscp->final_xform_index >= 0 && thiscp->final_xform_index != thiscp->num_xforms-1) { tmp = thiscp->xform[thiscp->final_xform_index]; for (i=thiscp->final_xform_index; i < thiscp->num_xforms-1; i++) thiscp->xform[i] = thiscp->xform[i+1]; thiscp->final_xform_index = thiscp->num_xforms-1; thiscp->xform[thiscp->final_xform_index] = tmp; } if (final_flag) { /* Set the final xform index */ thiscp->final_xform_enable = 1; thiscp->final_xform_index = thiscp->num_xforms-1; } else { /* Handle the chaos array */ numstd = thiscp->num_xforms - (thiscp->final_xform_index>=0); /* Pad existing rows */ for (i=0;ichaos[i] = realloc(thiscp->chaos[i], numstd * sizeof(double)); for (j=oldstd; jchaos[i][j] = 1.0; } /* Add new rows */ thiscp->chaos = realloc(thiscp->chaos,numstd * sizeof(double *)); for (i=oldstd; ichaos[i] = malloc(numstd * sizeof(double)); for (j=0;jchaos[i][j] = 1.0; } } } void flam3_delete_xform(flam3_genome *thiscp, int idx_to_delete) { int i,j; int num_std = thiscp->num_xforms - (thiscp->final_xform_index >= 0); if (thiscp->final_xform_index != idx_to_delete) { /* We're going to delete the nth std xform. */ /* Delete the nth_std row of the chaos array */ free(thiscp->chaos[idx_to_delete]); /* Shift the pointers down one */ for (i=idx_to_delete+1;ichaos[i-1] = thiscp->chaos[i]; /* Realloc the pointer array */ thiscp->chaos = realloc(thiscp->chaos,(num_std-1)*sizeof(double *)); num_std--; /* Loop over all of the rows and remove the nth_std element from them */ for (i=0;ichaos[i][j-1] = thiscp->chaos[i][j]; } /* Realloc the vector to have one less element */ thiscp->chaos[i] = realloc(thiscp->chaos[i],num_std*sizeof(double)); } } /* Handle the final xform index */ if (thiscp->final_xform_index == idx_to_delete) { thiscp->final_xform_index = -1; thiscp->final_xform_enable = 0; } else if (thiscp->final_xform_index > idx_to_delete) { thiscp->final_xform_index--; } /* Delete the motion elements of the banished xform */ flam3_delete_motion_elements(&(thiscp->xform[idx_to_delete])); /* Move all of the xforms down one - this does not require manual motion xform adjustment */ for (i=idx_to_delete; inum_xforms-1; i++) thiscp->xform[i] = thiscp->xform[i+1]; thiscp->num_xforms--; /* Reduce the memory storage by one xform */ thiscp->xform = (flam3_xform *)realloc(thiscp->xform, sizeof(flam3_xform) * thiscp->num_xforms); } void flam3_copy_xform(flam3_xform *dest, flam3_xform *src) { int j; /* Make sure the dest doesn't have motion already */ if (dest->num_motion>0) flam3_delete_motion_elements(dest); /* Copy everything */ *dest = *src; /* Reset motion in dest and copy it */ dest->num_motion=0; dest->motion=NULL; if (src->num_motion>0) { for (j=0;jnum_motion;j++) flam3_add_motion_element(dest); memcpy(dest->motion,src->motion,src->num_motion*sizeof(flam3_xform)); } } /* Copy one control point to another */ void flam3_copy(flam3_genome *dest, const flam3_genome * const src) { int i,ii; int numstd; /* If there are any xforms in dest before the copy, clean them up */ clear_cp(dest, 1); /* Copy main contents of genome */ memcpy(dest, src, sizeof(flam3_genome)); /* Only the pointer to the xform was copied, not the actual xforms. */ /* We need to create new xform memory storage for this new cp */ /* This goes for chaos, too. */ dest->num_xforms = 0; dest->final_xform_index = -1; dest->xform = NULL; dest->chaos = NULL; /* Add the standard xforms first */ numstd = src->num_xforms-(src->final_xform_index>=0); flam3_add_xforms(dest, numstd, 0, 0); for (i=0;ixform[i], &src->xform[i]); /* Add the final x if it's present */ if (src->final_xform_index>=0) { i = src->final_xform_index; flam3_add_xforms(dest, 1, 0, 1); ii = dest->final_xform_index; flam3_copy_xform(&dest->xform[ii],&src->xform[i]); } /* Also, only the pointer to the chaos array was copied. * We have to take care of that as well. */ for (i=0;ichaos[i],src->chaos[i], numstd * sizeof(double)); palette_copy (&src->palette, &dest->palette); } void flam3_copyx(flam3_genome *dest, flam3_genome *src, int dest_std_xforms, int dest_final_xform) { int i,numsrcstd; /* If there are any xforms in dest before the copy, clean them up */ clear_cp(dest, 1); /* Copy main contents of genome */ memcpy(dest, src, sizeof(flam3_genome)); /* Only the pointer to the xform was copied, not the actual xforms. */ /* We need to create new xform memory storage for this new cp */ /* This goes for chaos, too. */ dest->num_xforms = 0; dest->xform = NULL; dest->chaos = NULL; dest->final_xform_index = -1; /* Add the padded standard xform list */ /* Set the pad to 1 for these */ flam3_add_xforms(dest, dest_std_xforms, 1, 0); numsrcstd = src->num_xforms - (src->final_xform_index >= 0); for(i=0;ixform[i],&src->xform[i]); /* Copy the initial chaos from the src - the rest are already 1 */ memcpy(dest->chaos[i], src->chaos[i], numsrcstd*sizeof(double)); } /* Add the final xform if necessary */ if (dest_final_xform > 0) { flam3_add_xforms(dest, dest_final_xform, 1, 1); if (src->final_xform_enable > 0) { i = src->final_xform_index; flam3_copy_xform(&dest->xform[dest->num_xforms-1],&src->xform[i]); } else { /* Interpolated-against final xforms need animate & color_speed set to 0.0 */ dest->xform[dest->num_xforms-1].num_motion = 0; dest->xform[dest->num_xforms-1].motion=NULL; dest->xform[dest->num_xforms-1].animate=0.0; dest->xform[dest->num_xforms-1].color_speed=0.0; } } else { dest->final_xform_index = -1; dest->final_xform_enable = 0; } } void clear_cp(flam3_genome *cp, int default_flag) { memset (cp, 0, sizeof (*cp)); cp->gamma = 4.0; cp->vibrancy = 1.0; cp->contrast = 1.0; cp->brightness = 4.0; cp->pixels_per_unit = 50; cp->interpolation = flam3_interpolation_linear; cp->palette_interpolation = flam3_palette_interpolation_hsv; if (default_flag==flam3_defaults_on) { /* If defaults are on, set to reasonable values */ cp->highlight_power = -1.0; cp->width = 100; cp->height = 100; cp->gam_lin_thresh = 0.01; cp->interpolation_type = flam3_inttype_log; cp->palette_mode = PALETTE_MODE_STEP; } else { /* Defaults are off, so set to UN-reasonable values. */ cp->highlight_power = -1.0; cp->zoom = 999999999; cp->width = -1; cp->height = -1; cp->gam_lin_thresh = -1; // cp->motion_exp = -999; cp->interpolation_type = -1; cp->palette_mode = -1; } cp->final_xform_index = -1; } flam3_genome *flam3_parse_xml2(char *xmldata, char *xmlfilename, int default_flag, int *ncps, randctx * const rc) { xmlDocPtr doc; /* Parsed XML document tree */ xmlNode *rootnode; char *bn; int i; int loc_all_ncps=0; flam3_genome *loc_all_cp=NULL; char* locale = NULL; char* lorig = setlocale(LC_NUMERIC, NULL); /* Parse XML string into internal document */ /* Forbid network access during read */ doc = xmlReadMemory(xmldata, (int)strlen(xmldata), xmlfilename, NULL, XML_PARSE_NONET); /* Check for errors */ if (doc==NULL) { fprintf(stderr, "Failed to parse %s\n", xmlfilename); return NULL; } /* What is the root node of the document? */ rootnode = xmlDocGetRootElement(doc); // force use of "C" locale when writing reals. // first save away the current settings. if (lorig == NULL) fprintf(stderr, "error: couldn't get current locale\n"); else { int slen = strlen(lorig) + 1; locale = (char*)malloc(slen); if (locale != NULL) memcpy(locale, lorig, slen); } if (setlocale(LC_NUMERIC, "C") == NULL) fprintf(stderr, "error: couldn't set C locale\n"); /* Scan for nodes, starting with this node */ bn = basename(xmlfilename); /* Have to use &loc_all_cp since the memory gets allocated in scan_for_flame_nodes */ scan_for_flame_nodes(rootnode, bn, default_flag,&loc_all_cp,&loc_all_ncps, rc); // restore locale if (locale != NULL) { if (setlocale(LC_NUMERIC, locale) == NULL) fprintf(stderr, "error: couldn't replace locale settings\n"); free(locale); } xmlFreeDoc(doc); *ncps = loc_all_ncps; /* Check to see if the first control point or the second-to-last */ /* control point has interpolation="smooth". This is invalid */ /* and should be reset to linear (with a warning). */ if (loc_all_ncps>=1) { if (loc_all_cp[0].interpolation == flam3_interpolation_smooth) { fprintf(stderr,"Warning: smooth interpolation cannot be used for first segment.\n" " switching to linear.\n"); loc_all_cp[0].interpolation = flam3_interpolation_linear; } } if (loc_all_ncps>=2) { if (loc_all_cp[(loc_all_ncps)-2].interpolation == flam3_interpolation_smooth) { fprintf(stderr,"Warning: smooth interpolation cannot be used for last segment.\n" " switching to linear.\n"); loc_all_cp[loc_all_ncps-2].interpolation = flam3_interpolation_linear; } } /* Finally, ensure that consecutive 'rotate' parameters never exceed */ /* a difference of more than 180 degrees (+/-) for interpolation. */ /* An adjustment of +/- 360 degrees is made until this is true. */ if (*ncps>1) { for (i=1;i<*ncps;i++) { /* Only do this adjustment if we're not in compat mode */ if (flam3_inttype_compat != loc_all_cp[i-1].interpolation_type && flam3_inttype_older != loc_all_cp[i-1].interpolation_type) { while (loc_all_cp[i].rotate < loc_all_cp[i-1].rotate-180) loc_all_cp[i].rotate += 360; while (loc_all_cp[i].rotate > loc_all_cp[i-1].rotate+180) loc_all_cp[i].rotate -= 360; } } } //Note that concurrent calls to flam3, if in parallel, potentially segfault //if this function is called. technically it's required but it doesn't //leak memory continuously. //xmlCleanupParser(); return loc_all_cp; } flam3_genome * flam3_parse_from_file(FILE *f, char *fname, int default_flag, int *ncps, randctx * const rc) { int i, c, slen = 5000; char *s, *snew; flam3_genome *ret; /* Incrementally read XML file into a string */ s = malloc(slen); i = 0; do { c = getc(f); if (EOF == c) break; s[i++] = c; if (i == slen-1) { slen *= 2; snew = realloc(s, slen); if (snew==NULL) { fprintf(stderr,"XML file too large to be read. continuing with partial file.\n"); break; } else s = snew; } } while (1); /* Null-terminate the read XML data */ s[i] = 0; /* Parse the XML string */ if (fname) ret = flam3_parse_xml2(s, fname, default_flag, ncps, rc); else ret = flam3_parse_xml2(s, "stdin", default_flag, ncps, rc); free(s); return(ret); } void flam3_apply_template(flam3_genome *cp, flam3_genome *templ) { /* Check for invalid values - only replace those with valid ones */ if (templ->zoom < 999999998) cp->zoom = templ->zoom; if (templ->width > 0) { /* preserving scale should be an option */ cp->pixels_per_unit = cp->pixels_per_unit * templ->width / cp->width; cp->width = templ->width; } if (templ->height > 0) cp->height = templ->height; if (templ->gam_lin_thresh >= 0) cp->gam_lin_thresh = templ->gam_lin_thresh; if (templ->interpolation >= 0) cp->interpolation = templ->interpolation; if (templ->interpolation_type >= 0) cp->interpolation_type = templ->interpolation_type; if (templ->highlight_power >=0) cp->highlight_power = templ->highlight_power; if (templ->palette_mode >= 0) cp->palette_mode = templ->palette_mode; } char *flam3_print_to_string(flam3_genome *cp) { FILE *tmpflame; long stringbytes; char *genome_string; int using_tmpdir = 0; char tmpnam[256]; tmpflame = tmpfile(); if (NULL==tmpflame) { if (using_tmpdir == 0) { perror("opening temporary file"); return (NULL); } } flam3_print(tmpflame,cp,NULL,flam3_dont_print_edits); stringbytes = ftell(tmpflame); fseek(tmpflame,0L, SEEK_SET); genome_string = (char *)calloc(stringbytes+1,1); if (stringbytes != fread(genome_string, 1, stringbytes, tmpflame)) { perror("reading string from temp file"); } fclose(tmpflame); if (using_tmpdir) unlink(tmpnam); return(genome_string); } void flam3_print(FILE *f, flam3_genome *cp, char *extra_attributes, int print_edits) { int i,numstd; // force use of "C" locale when writing reals. // first save away the current settings. char* locale = NULL; char* lorig = setlocale(LC_NUMERIC, NULL); if (lorig == NULL) fprintf(stderr, "error: couldn't get current locale\n"); else { int slen = strlen(lorig) + 1; locale = (char*)malloc(slen); if (locale != NULL) memcpy(locale, lorig, slen); } if (setlocale(LC_NUMERIC, "C") == NULL) fprintf(stderr, "error: couldn't set C locale\n"); fprintf(f, "time); if (cp->flame_name[0]!=0) fprintf(f, " name=\"%s\"",cp->flame_name); fprintf(f, " size=\"%d %d\"", cp->width, cp->height); fprintf(f, " center=\"%g %g\"", cp->center[0], cp->center[1]); fprintf(f, " scale=\"%g\"", cp->pixels_per_unit); if (cp->zoom != 0.0) fprintf(f, " zoom=\"%g\"", cp->zoom); fprintf(f, " rotate=\"%g\"", cp->rotate); fprintf(f, " brightness=\"%g\"", cp->brightness); fprintf(f, " gamma=\"%g\"", cp->gamma); fprintf(f, " highlight_power=\"%g\"", cp->highlight_power); fprintf(f, " vibrancy=\"%g\"", cp->vibrancy); fprintf(f, " gamma_threshold=\"%g\"", cp->gam_lin_thresh); if (PALETTE_MODE_STEP == cp->palette_mode) fprintf(f, " palette_mode=\"step\""); else if (PALETTE_MODE_LINEAR == cp->palette_mode) fprintf(f, " palette_mode=\"linear\""); if (flam3_interpolation_linear != cp->interpolation) fprintf(f, " interpolation=\"smooth\""); if (flam3_inttype_linear == cp->interpolation_type) fprintf(f, " interpolation_type=\"linear\""); else if (flam3_inttype_log == cp->interpolation_type) fprintf(f, " interpolation_type=\"log\""); else if (flam3_inttype_compat == cp->interpolation_type) fprintf(f, " interpolation_type=\"old\""); else if (flam3_inttype_older == cp->interpolation_type) fprintf(f, " interpolation_type=\"older\""); if (flam3_palette_interpolation_hsv != cp->palette_interpolation) fprintf(f, " palette_interpolation=\"sweep\""); if (extra_attributes) fprintf(f, " %s", extra_attributes); fprintf(f, ">\n"); if (cp->symmetry) fprintf(f, " \n", cp->symmetry); numstd = cp->num_xforms - (cp->final_xform_index>=0); for (i = 0; i < cp->num_xforms; i++) { if (i==cp->final_xform_index) flam3_print_xform(f, &cp->xform[i], 1, numstd, NULL, 0); else flam3_print_xform(f, &cp->xform[i], 0, numstd, cp->chaos[i], 0); } for (i = 0; i < cp->palette.count; i++) { double4 rgba = cp->palette.color[i] * 255.0; fprintf(f, " "); if (rgba[3] ==255.0) { fprintf(f, "", i, rgba[0], rgba[1], rgba[2]); } else { fprintf(f, " ", i, rgba[0], rgba[1], rgba[2], rgba[3]); } // if (i%4 == 3) fprintf(f, "\n"); } if (cp->edits != NULL && print_edits==flam3_print_edits) { /* We need a custom script for printing these */ /* and it needs to be recursive */ xmlNodePtr elem_node = xmlDocGetRootElement(cp->edits); flam3_edit_print(f,elem_node, 1, 1); } fprintf(f, "\n"); if (locale != NULL) { if (setlocale(LC_NUMERIC, locale) == NULL) fprintf(stderr, "error: couldn't restore locale settings\n"); free(locale); } } #define PRINTNON(p) do { if (x->p != 0.0) fprintf(f, #p "=\"%f\" ",x->p); } while(0) void flam3_print_xform(FILE *f, flam3_xform *x, int final_flag, int numstd, double *chaos_row, int motion_flag) { int blob_var=0,pdj_var=0,fan2_var=0,rings2_var=0,perspective_var=0; int juliaN_var=0,juliaScope_var=0,radialBlur_var=0,pie_var=0,disc2_var=0; int ngon_var=0,curl_var=0,rectangles_var=0,supershape_var=0; int flower_var=0,conic_var=0,parabola_var=0,bent2_var=0,bipolar_var=0; int cell_var=0,cpow_var=0,curve_var=0,escher_var=0,lazys_var=0; int modulus_var=0,oscope_var=0,popcorn2_var=0,separation_var=0; int split_var=0,splits_var=0,stripes_var=0,wedge_var=0,wedgeJ_var=0; int wedgeS_var=0,whorl_var=0,waves2_var=0,auger_var=0,flux_var=0; int mobius_var=0; int j; int lnv; if (motion_flag) { fprintf(f, " motion_freq); if (x->motion_func == MOTION_SIN) fprintf(f, "motion_function=\"sin\" "); else if (x->motion_func == MOTION_TRIANGLE) fprintf(f, "motion_function=\"triangle\" "); else if (x->motion_func == MOTION_HILL) fprintf(f, "motion_function=\"hill\" "); } else { if (final_flag) fprintf(f, " density); } if (!motion_flag || x->color != 0.0) fprintf(f, "color=\"%g\" ", x->color); if (!motion_flag) fprintf(f, "color_speed=\"%g\" ", x->color_speed); if (!final_flag && !motion_flag) fprintf(f, "animate=\"%g\" ", x->animate); lnv = flam3_nvariations; for (j = 0; j < lnv; j++) { double v = x->var[j]; if (0.0 != v) { fprintf(f, "%s=\"%g\" ", flam3_variation_names[j], v); if (j==VAR_BLOB) blob_var=1; else if (j==VAR_PDJ) pdj_var=1; else if (j==VAR_FAN2) fan2_var=1; else if (j==VAR_RINGS2) rings2_var=1; else if (j==VAR_PERSPECTIVE) perspective_var=1; else if (j==VAR_JULIAN) juliaN_var=1; else if (j==VAR_JULIASCOPE) juliaScope_var=1; else if (j==VAR_RADIAL_BLUR) radialBlur_var=1; else if (j==VAR_PIE) pie_var=1; else if (j==VAR_NGON) ngon_var=1; else if (j==VAR_CURL) curl_var=1; else if (j==VAR_RECTANGLES) rectangles_var=1; else if (j==VAR_DISC2) disc2_var=1; else if (j==VAR_SUPER_SHAPE) supershape_var=1; else if (j==VAR_FLOWER) flower_var=1; else if (j==VAR_CONIC) conic_var=1; else if (j==VAR_PARABOLA) parabola_var=1; else if (j==VAR_BENT2) bent2_var=1; else if (j==VAR_BIPOLAR) bipolar_var=1; else if (j==VAR_CELL) cell_var=1; else if (j==VAR_CPOW) cpow_var=1; else if (j==VAR_CURVE) curve_var=1; else if (j==VAR_ESCHER) escher_var=1; else if (j==VAR_LAZYSUSAN) lazys_var=1; else if (j==VAR_MODULUS) modulus_var=1; else if (j==VAR_OSCILLOSCOPE) oscope_var=1; else if (j==VAR_POPCORN2) popcorn2_var=1; else if (j==VAR_SPLIT) split_var=1; else if (j==VAR_SPLITS) splits_var=1; else if (j==VAR_STRIPES) stripes_var=1; else if (j==VAR_WEDGE) wedge_var=1; else if (j==VAR_WEDGE_JULIA) wedgeJ_var=1; else if (j==VAR_WEDGE_SPH) wedgeS_var=1; else if (j==VAR_WHORL) whorl_var=1; else if (j==VAR_WAVES2) waves2_var=1; else if (j==VAR_AUGER) auger_var=1; else if (j==VAR_FLUX) flux_var=1; else if (j==VAR_MOBIUS) mobius_var=1; } } if (!motion_flag) { if (blob_var==1) { fprintf(f, "blob_low=\"%g\" ", x->blob_low); fprintf(f, "blob_high=\"%g\" ", x->blob_high); fprintf(f, "blob_waves=\"%g\" ", x->blob_waves); } if (pdj_var==1) { fprintf(f, "pdj_a=\"%g\" ", x->pdj_a); fprintf(f, "pdj_b=\"%g\" ", x->pdj_b); fprintf(f, "pdj_c=\"%g\" ", x->pdj_c); fprintf(f, "pdj_d=\"%g\" ", x->pdj_d); } if (fan2_var==1) { fprintf(f, "fan2_x=\"%g\" ", x->fan2_x); fprintf(f, "fan2_y=\"%g\" ", x->fan2_y); } if (rings2_var==1) { fprintf(f, "rings2_val=\"%g\" ", x->rings2_val); } if (perspective_var==1) { fprintf(f, "perspective_angle=\"%g\" ", x->perspective_angle); fprintf(f, "perspective_dist=\"%g\" ", x->perspective_dist); } if (juliaN_var==1) { fprintf(f, "julian_power=\"%g\" ", x->julian_power); fprintf(f, "julian_dist=\"%g\" ", x->julian_dist); } if (juliaScope_var==1) { fprintf(f, "juliascope_power=\"%g\" ", x->juliascope_power); fprintf(f, "juliascope_dist=\"%g\" ", x->juliascope_dist); } if (radialBlur_var==1) { fprintf(f, "radial_blur_angle=\"%g\" ", x->radial_blur_angle); } if (pie_var==1) { fprintf(f, "pie_slices=\"%g\" ", x->pie_slices); fprintf(f, "pie_rotation=\"%g\" ", x->pie_rotation); fprintf(f, "pie_thickness=\"%g\" ", x->pie_thickness); } if (ngon_var==1) { fprintf(f, "ngon_sides=\"%g\" ", x->ngon_sides); fprintf(f, "ngon_power=\"%g\" ", x->ngon_power); fprintf(f, "ngon_corners=\"%g\" ", x->ngon_corners); fprintf(f, "ngon_circle=\"%g\" ", x->ngon_circle); } if (curl_var==1) { fprintf(f, "curl_c1=\"%g\" ", x->curl_c1); fprintf(f, "curl_c2=\"%g\" ", x->curl_c2); } if (rectangles_var==1) { fprintf(f, "rectangles_x=\"%g\" ", x->rectangles_x); fprintf(f, "rectangles_y=\"%g\" ", x->rectangles_y); } if (disc2_var==1) { fprintf(f, "disc2_rot=\"%g\" ", x->disc2_rot); fprintf(f, "disc2_twist=\"%g\" ", x->disc2_twist); } if (supershape_var==1) { fprintf(f, "super_shape_rnd=\"%g\" ", x->super_shape_rnd); fprintf(f, "super_shape_m=\"%g\" ", x->super_shape_m); fprintf(f, "super_shape_n1=\"%g\" ", x->super_shape_n1); fprintf(f, "super_shape_n2=\"%g\" ", x->super_shape_n2); fprintf(f, "super_shape_n3=\"%g\" ", x->super_shape_n3); fprintf(f, "super_shape_holes=\"%g\" ", x->super_shape_holes); } if (flower_var==1) { fprintf(f, "flower_petals=\"%g\" ", x->flower_petals); fprintf(f, "flower_holes=\"%g\" ", x->flower_holes); } if (conic_var==1) { fprintf(f, "conic_eccentricity=\"%g\" ", x->conic_eccentricity); fprintf(f, "conic_holes=\"%g\" ", x->conic_holes); } if (parabola_var==1) { fprintf(f, "parabola_height=\"%g\" ", x->parabola_height); fprintf(f, "parabola_width=\"%g\" ", x->parabola_width); } if (bent2_var==1) { fprintf(f, "bent2_x=\"%g\" ", x->bent2_x); fprintf(f, "bent2_y=\"%g\" ", x->bent2_y); } if (bipolar_var==1) { fprintf(f, "bipolar_shift=\"%g\" ", x->bipolar_shift); } if (cell_var==1) { fprintf(f, "cell_size=\"%g\" ", x->cell_size); } if (cpow_var==1) { fprintf(f, "cpow_i=\"%g\" ", x->cpow_i); fprintf(f, "cpow_r=\"%g\" ", x->cpow_r); fprintf(f, "cpow_power=\"%g\" ", x->cpow_power); } if (curve_var==1) { fprintf(f, "curve_xamp=\"%g\" ", x->curve_xamp); fprintf(f, "curve_yamp=\"%g\" ", x->curve_yamp); fprintf(f, "curve_xlength=\"%g\" ", x->curve_xlength); fprintf(f, "curve_ylength=\"%g\" ", x->curve_ylength); } if (escher_var==1) { fprintf(f, "escher_beta=\"%g\" ", x->escher_beta); } if (lazys_var==1) { fprintf(f, "lazysusan_x=\"%g\" ", x->lazysusan_x); fprintf(f, "lazysusan_y=\"%g\" ", x->lazysusan_y); fprintf(f, "lazysusan_spin=\"%g\" ", x->lazysusan_spin); fprintf(f, "lazysusan_space=\"%g\" ", x->lazysusan_space); fprintf(f, "lazysusan_twist=\"%g\" ", x->lazysusan_twist); } if (modulus_var==1) { fprintf(f, "modulus_x=\"%g\" ", x->modulus_x); fprintf(f, "modulus_y=\"%g\" ", x->modulus_y); } if (oscope_var==1) { fprintf(f, "oscilloscope_separation=\"%g\" ", x->oscope_separation); fprintf(f, "oscilloscope_frequency=\"%g\" ", x->oscope_frequency); fprintf(f, "oscilloscope_amplitude=\"%g\" ", x->oscope_amplitude); fprintf(f, "oscilloscope_damping=\"%g\" ", x->oscope_damping); } if (popcorn2_var==1) { fprintf(f, "popcorn2_x=\"%g\" ", x->popcorn2_x); fprintf(f, "popcorn2_y=\"%g\" ", x->popcorn2_y); fprintf(f, "popcorn2_c=\"%g\" ", x->popcorn2_c); } if (separation_var==1) { fprintf(f, "separation_x=\"%g\" ", x->separation_x); fprintf(f, "separation_y=\"%g\" ", x->separation_y); fprintf(f, "separation_xinside=\"%g\" ", x->separation_xinside); fprintf(f, "separation_yinside=\"%g\" ", x->separation_yinside); } if (split_var==1) { fprintf(f, "split_xsize=\"%g\" ", x->split_xsize); fprintf(f, "split_ysize=\"%g\" ", x->split_ysize); } if (splits_var==1) { fprintf(f, "splits_x=\"%g\" ", x->splits_x); fprintf(f, "splits_y=\"%g\" ", x->splits_y); } if (stripes_var==1) { fprintf(f, "stripes_space=\"%g\" ", x->stripes_space); fprintf(f, "stripes_warp=\"%g\" ", x->stripes_warp); } if (wedge_var==1) { fprintf(f, "wedge_angle=\"%g\" ", x->wedge_angle); fprintf(f, "wedge_hole=\"%g\" ", x->wedge_hole); fprintf(f, "wedge_count=\"%g\" ", x->wedge_count); fprintf(f, "wedge_swirl=\"%g\" ", x->wedge_swirl); } if (wedgeJ_var==1) { fprintf(f, "wedge_julia_angle=\"%g\" ", x->wedge_julia_angle); fprintf(f, "wedge_julia_count=\"%g\" ", x->wedge_julia_count); fprintf(f, "wedge_julia_power=\"%g\" ", x->wedge_julia_power); fprintf(f, "wedge_julia_dist=\"%g\" ", x->wedge_julia_dist); } if (wedgeS_var==1) { fprintf(f, "wedge_sph_angle=\"%g\" ", x->wedge_sph_angle); fprintf(f, "wedge_sph_hole=\"%g\" ", x->wedge_sph_hole); fprintf(f, "wedge_sph_count=\"%g\" ", x->wedge_sph_count); fprintf(f, "wedge_sph_swirl=\"%g\" ", x->wedge_sph_swirl); } if (whorl_var==1) { fprintf(f, "whorl_inside=\"%g\" ", x->whorl_inside); fprintf(f, "whorl_outside=\"%g\" ", x->whorl_outside); } if (waves2_var==1) { fprintf(f, "waves2_scalex=\"%g\" ", x->waves2_scalex); fprintf(f, "waves2_scaley=\"%g\" ", x->waves2_scaley); fprintf(f, "waves2_freqx=\"%g\" ", x->waves2_freqx); fprintf(f, "waves2_freqy=\"%g\" ", x->waves2_freqy); } if (auger_var==1) { fprintf(f, "auger_sym=\"%g\" ", x->auger_sym); fprintf(f, "auger_weight=\"%g\" ", x->auger_weight); fprintf(f, "auger_freq=\"%g\" ", x->auger_freq); fprintf(f, "auger_scale=\"%g\" ", x->auger_scale); } if (flux_var==1) fprintf(f, "flux_spread=\"%g\" ", x->flux_spread); if (mobius_var==1) { fprintf(f, "mobius_re_a=\"%g\" ", x->mobius_re_a); fprintf(f, "mobius_im_a=\"%g\" ", x->mobius_im_a); fprintf(f, "mobius_re_b=\"%g\" ", x->mobius_re_b); fprintf(f, "mobius_im_b=\"%g\" ", x->mobius_im_b); fprintf(f, "mobius_re_c=\"%g\" ", x->mobius_re_c); fprintf(f, "mobius_im_c=\"%g\" ", x->mobius_im_c); fprintf(f, "mobius_re_d=\"%g\" ", x->mobius_re_d); fprintf(f, "mobius_im_d=\"%g\" ", x->mobius_im_d); } fprintf(f, "coefs=\""); for (j = 0; j < 3; j++) { if (j) fprintf(f, " "); fprintf(f, "%g %g", x->c[j][0], x->c[j][1]); } fprintf(f, "\""); if (!id_matrix(x->post)) { fprintf(f, " post=\""); for (j = 0; j < 3; j++) { if (j) fprintf(f, " "); fprintf(f, "%g %g", x->post[j][0], x->post[j][1]); } fprintf(f, "\""); } } else { /* For motion, print any parameter if it's nonzero */ PRINTNON(blob_low); PRINTNON(blob_high); PRINTNON(blob_waves); PRINTNON(pdj_a); PRINTNON(pdj_b); PRINTNON(pdj_c); PRINTNON(pdj_d); PRINTNON(fan2_x); PRINTNON(fan2_y); PRINTNON(rings2_val); PRINTNON(perspective_angle); PRINTNON(perspective_dist); PRINTNON(julian_power); PRINTNON(julian_dist); PRINTNON(juliascope_power); PRINTNON(juliascope_dist); PRINTNON(radial_blur_angle); PRINTNON(pie_slices); PRINTNON(pie_rotation); PRINTNON(pie_thickness); PRINTNON(ngon_sides); PRINTNON(ngon_power); PRINTNON(ngon_corners); PRINTNON(ngon_circle); PRINTNON(curl_c1); PRINTNON(curl_c2); PRINTNON(rectangles_x); PRINTNON(rectangles_y); PRINTNON(disc2_rot); PRINTNON(disc2_twist); PRINTNON(super_shape_rnd); PRINTNON(super_shape_m); PRINTNON(super_shape_n1); PRINTNON(super_shape_n2); PRINTNON(super_shape_n3); PRINTNON(super_shape_holes); PRINTNON(flower_petals); PRINTNON(flower_holes); PRINTNON(conic_eccentricity); PRINTNON(conic_holes); PRINTNON(parabola_height); PRINTNON(parabola_width); PRINTNON(bent2_x); PRINTNON(bent2_y); PRINTNON(bipolar_shift); PRINTNON(cell_size); PRINTNON(cpow_i); PRINTNON(cpow_r); PRINTNON(cpow_power); PRINTNON(curve_xamp); PRINTNON(curve_yamp); PRINTNON(curve_xlength); PRINTNON(curve_ylength); PRINTNON(escher_beta); PRINTNON(lazysusan_x); PRINTNON(lazysusan_y); PRINTNON(lazysusan_spin); PRINTNON(lazysusan_space); PRINTNON(lazysusan_twist); PRINTNON(modulus_x); PRINTNON(modulus_y); PRINTNON(oscope_separation); PRINTNON(oscope_frequency); PRINTNON(oscope_amplitude); PRINTNON(oscope_damping); PRINTNON(popcorn2_x); PRINTNON(popcorn2_y); PRINTNON(popcorn2_c); PRINTNON(separation_x); PRINTNON(separation_y); PRINTNON(separation_xinside); PRINTNON(separation_yinside); PRINTNON(split_xsize); PRINTNON(split_ysize); PRINTNON(splits_x); PRINTNON(splits_y); PRINTNON(stripes_space); PRINTNON(stripes_warp); PRINTNON(wedge_angle); PRINTNON(wedge_hole); PRINTNON(wedge_count); PRINTNON(wedge_swirl); PRINTNON(wedge_julia_angle); PRINTNON(wedge_julia_count); PRINTNON(wedge_julia_power); PRINTNON(wedge_julia_dist); PRINTNON(wedge_sph_angle); PRINTNON(wedge_sph_hole); PRINTNON(wedge_sph_count); PRINTNON(wedge_sph_swirl); PRINTNON(whorl_inside); PRINTNON(whorl_outside); PRINTNON(waves2_scalex); PRINTNON(waves2_scaley); PRINTNON(waves2_freqx); PRINTNON(waves2_freqy); PRINTNON(auger_sym); PRINTNON(auger_weight); PRINTNON(auger_freq); PRINTNON(auger_scale); PRINTNON(flux_spread); PRINTNON(mobius_re_a); PRINTNON(mobius_im_a); PRINTNON(mobius_re_b); PRINTNON(mobius_im_b); PRINTNON(mobius_re_c); PRINTNON(mobius_im_c); PRINTNON(mobius_re_d); PRINTNON(mobius_im_d); if (!zero_matrix(x->c)) { fprintf(f, "coefs=\""); for (j = 0; j < 3; j++) { if (j) fprintf(f, " "); fprintf(f, "%g %g", x->c[j][0], x->c[j][1]); } fprintf(f, "\""); } if (!zero_matrix(x->post)) { fprintf(f, " post=\""); for (j = 0; j < 3; j++) { if (j) fprintf(f, " "); fprintf(f, "%g %g", x->post[j][0], x->post[j][1]); } fprintf(f, "\""); } } if (!final_flag && !motion_flag) { /* Print out the chaos row for this xform */ int numcols = numstd; while (numcols > 0 && chaos_row[numcols-1]==1.0) numcols--; if (numcols>0) { fprintf(f, " chaos=\""); for (j=0;jopacity); } if (!motion_flag && x->num_motion>0) { int nm; fprintf(f,">\n"); for (nm=0; nmnum_motion; nm++) flam3_print_xform(f, &(x->motion[nm]), 0, 0, NULL, 1); if (final_flag) fprintf(f," \n"); else fprintf(f," \n"); } else fprintf(f, "/>\n"); } static double round6(double x) { x *= 1e6; if (x < 0) x -= 1.0; return 1e-6*(int)(x+0.5); } /* sym=2 or more means rotational sym=1 means identity, ie no symmetry sym=0 means pick a random symmetry (maybe none) sym=-1 means bilateral (reflection) sym=-2 or less means rotational and reflective */ void flam3_add_symmetry(flam3_genome *cp, int sym, randctx * const rc) { int i, j, k; double a; int result = 0; if (0 == sym) { static int sym_distrib[] = { -4, -3, -2, -2, -2, -1, -1, -1, 2, 2, 2, 3, 3, 4, 4, }; if (rand_bool(rc)) { sym = rand_distrib(rc, sym_distrib); } else if (rand_mod(rc, 32)) { sym = rand_mod(rc, 13)-6; } else { sym = rand_mod(rc, 51)-25; } } if (1 == sym || 0 == sym) return; cp->symmetry = sym; if (sym < 0) { i = cp->num_xforms; if (cp->final_xform_enable) i -= 1; flam3_add_xforms(cp,1,0,0); cp->xform[i].density = 1.0; cp->xform[i].color_speed = 0.0; cp->xform[i].animate = 0.0; cp->xform[i].var[0] = 1.0; for (j = 1; j < flam3_nvariations; j++) cp->xform[i].var[j] = 0; cp->xform[i].color = 1.0; cp->xform[i].c[0][0] = -1.0; cp->xform[i].c[0][1] = 0.0; cp->xform[i].c[1][0] = 0.0; cp->xform[i].c[1][1] = 1.0; cp->xform[i].c[2][0] = 0.0; cp->xform[i].c[2][1] = 0.0; result++; sym = -sym; } a = 2*M_PI/sym; for (k = 1; k < sym; k++) { i = cp->num_xforms; if (cp->final_xform_enable) i -= 1; flam3_add_xforms(cp, 1, 0,0); cp->xform[i].density = 1.0; cp->xform[i].color_speed = 0.0; cp->xform[i].animate = 0.0; cp->xform[i].var[0] = 1.0; for (j = 1; j < flam3_nvariations; j++) cp->xform[i].var[j] = 0; cp->xform[i].color = (sym<3) ? 0.0 : ((k-1.0)/(sym-2.0)); cp->xform[i].c[0][0] = round6(cos(k*a)); cp->xform[i].c[0][1] = round6(sin(k*a)); cp->xform[i].c[1][0] = round6(-cp->xform[i].c[0][1]); cp->xform[i].c[1][1] = cp->xform[i].c[0][0]; cp->xform[i].c[2][0] = 0.0; cp->xform[i].c[2][1] = 0.0; result++; } qsort((char *) &cp->xform[cp->num_xforms-result], result, sizeof(flam3_xform), compare_xforms); } void add_to_action(char *action, char *addtoaction) { /* action is a flam3_max_action_length array */ if (action != NULL) { int alen = strlen(action); int addlen = strlen(addtoaction); if (alen+addlen < flam3_max_action_length) strcat(action,addtoaction); else fprintf(stderr,"action string too long, truncating...\n"); } } void flam3_cross(flam3_genome *cp0, flam3_genome *cp1, flam3_genome *out, int cross_mode, randctx *rc) { int i,j, rb; char ministr[10]; if (cross_mode == CROSS_NOT_SPECIFIED) { double s = rand_d01(rc); if (s < 0.1) cross_mode = CROSS_UNION; else if (s < 0.2) cross_mode = CROSS_INTERPOLATE; else cross_mode = CROSS_ALTERNATE; } if (cross_mode == CROSS_UNION) { flam3_xform mycopy; /* Make a copy of cp0 */ flam3_copy(out, cp0); for (j=0;jnum_xforms;j++) { /* Skip over the final xform, if it's present. */ /* Default behavior keeps the final from parent0. */ if (cp1->final_xform_index == j) continue; i = out->num_xforms; if (out->final_xform_enable) i -= 1; flam3_add_xforms(out, 1, 0, 0); flam3_copy_xform(&out->xform[i],&cp1->xform[j]); } /* Put the final xform last (if there is one) */ /* We do not need to do complicated xform copies here since we're just moving them around */ if (out->final_xform_index >= 0) { mycopy = out->xform[out->final_xform_index]; out->xform[out->final_xform_index] = out->xform[out->num_xforms-1]; out->xform[out->num_xforms-1] = mycopy; out->final_xform_index = out->num_xforms-1; } } else if (cross_mode == CROSS_INTERPOLATE) { /* linearly interpolate somewhere between the two */ flam3_genome parents[2]; double t = rand_d01(rc); memset(parents, 0, 2*sizeof(flam3_genome)); flam3_copy(&(parents[0]), cp0); flam3_copy(&(parents[1]), cp1); parents[0].time = 0.0; parents[1].time = 1.0; flam3_interpolate(parents, 2, t, 0, out); for (i=0;inum_xforms;i++) flam3_delete_motion_elements(&out->xform[i]); clear_cp(&parents[0],flam3_defaults_on); clear_cp(&parents[1],flam3_defaults_on); sprintf(ministr,"%7.5g",t); } else { /* alternate mode */ int got0, got1, used_parent; char *trystr; trystr = calloc(4 * (cp0->num_xforms + cp1->num_xforms), sizeof(char)); /* each xform comes from a random parent, possible for an entire parent to be excluded */ do { trystr[0] = 0; got0 = got1 = 0; rb = rand_bool(rc); sprintf(ministr,"%d:",rb); strcat(trystr,ministr); /* Copy the parent, sorting the final xform to the end if it's present. */ if (rb) flam3_copyx(out, cp1, cp1->num_xforms - (cp1->final_xform_index > 0), cp1->final_xform_enable); else flam3_copyx(out, cp0, cp0->num_xforms - (cp0->final_xform_index > 0), cp0->final_xform_enable); used_parent = rb; /* Only replace non-final xforms */ for (i = 0; i < out->num_xforms - out->final_xform_enable; i++) { rb = rand_bool(rc); /* Replace xform if bit is 1 */ if (rb==1) { if (used_parent==0) { if (i < cp1->num_xforms && cp1->xform[i].density > 0) { flam3_copy_xform(&out->xform[i],&cp1->xform[i]); sprintf(ministr," 1"); got1 = 1; } else { sprintf(ministr," 0"); got0 = 1; } } else { if (i < cp0->num_xforms && cp0->xform[i].density > 0) { flam3_copy_xform(&out->xform[i],&cp0->xform[i]); sprintf(ministr," 0"); got0 = 1; } else { sprintf(ministr," 1"); got1 = 1; } } } else { sprintf(ministr," %d",used_parent); if (used_parent) got1 = 1; else got0 = 1; } strcat(trystr,ministr); } if (used_parent==0 && cp0->final_xform_enable) got0 = 1; else if (used_parent==1 && cp1->final_xform_enable) got1 = 1; } while ((i > 1) && !(got0 && got1)); free(trystr); } /* reset color coords */ for (i = 0; i < out->num_xforms; i++) { out->xform[i].color = i&1; } /* Potentially genetically cross the two colormaps together */ if (rand_d01(rc) < 0.4) { /* Select the starting parent */ int startParent=rand_bool(rc); int ci; sprintf(ministr," %d:",startParent); /* Loop over the entries, switching to the other parent 1% of the time */ for (ci=0;cipalette.count;ci++) { if (rand_d01(rc)<.01) { startParent = 1-startParent; sprintf(ministr," %d",ci); } out->palette.color[ci] = startParent ? cp1->palette.color[ci] : cp0->palette.color[ci]; } } } void flam3_mutate(flam3_genome *cp, int mutate_mode, int *ivars, int ivars_n, int sym, double speed, const palette_collection * const pc, randctx *rc) { double randselect; flam3_genome mutation; int i,j,done; char ministr[30]; /* If mutate_mode = -1, choose a random mutation mode */ if (mutate_mode == MUTATE_NOT_SPECIFIED) { randselect = rand_d01(rc); if (randselect < 0.1) mutate_mode = MUTATE_ALL_VARIATIONS; else if (randselect < 0.3) mutate_mode = MUTATE_ONE_XFORM_COEFS; else if (randselect < 0.5) mutate_mode = MUTATE_ADD_SYMMETRY; else if (randselect < 0.6) mutate_mode = MUTATE_POST_XFORMS; else if (randselect < 0.7) mutate_mode = MUTATE_COLOR_PALETTE; else if (randselect < 0.8) mutate_mode = MUTATE_DELETE_XFORM; else mutate_mode = MUTATE_ALL_COEFS; } memset(&mutation, 0, sizeof(flam3_genome)); if (mutate_mode == MUTATE_ALL_VARIATIONS) { do { /* Create a random flame, and use the variations */ /* to replace those in the original */ flam3_random(&mutation, ivars, ivars_n, sym, cp->num_xforms, pc, rc); for (i = 0; i < cp->num_xforms; i++) { for (j = 0; j < flam3_nvariations; j++) { if (cp->xform[i].var[j] != mutation.xform[i].var[j]) { /* Copy the new var weights */ cp->xform[i].var[j] = mutation.xform[i].var[j]; /* Copy parameters for this variation only */ flam3_copy_params(&(cp->xform[i]),&(mutation.xform[i]),j); done = 1; } } } } while (!done); } else if (mutate_mode == MUTATE_ONE_XFORM_COEFS) { int modxf; /* Generate a 2-xform random */ flam3_random(&mutation, ivars, ivars_n, sym, 2, pc, rc); /* Which xform do we mutate? */ modxf = rand_mod(rc, cp->num_xforms); sprintf(ministr,"%d coefs",modxf); /* if less than 3 xforms, then change only the translation part */ if (2 >= cp->num_xforms) { for (j = 0; j < 2; j++) cp->xform[modxf].c[2][j] = mutation.xform[0].c[2][j]; } else { for (i = 0; i < 3; i++) for (j = 0; j < 2; j++) cp->xform[modxf].c[i][j] = mutation.xform[0].c[i][j]; } } else if (mutate_mode == MUTATE_ADD_SYMMETRY) { flam3_add_symmetry(cp, 0, rc); } else if (mutate_mode == MUTATE_POST_XFORMS) { int b = 1 + rand_mod(rc, 6); int same = rand_mod(rc, 4); /* 25% chance of using the same post for all of them */ sprintf(ministr,"(%d%s)",b,(same>0) ? " same" : ""); for (i = 0; i < cp->num_xforms; i++) { int copy = (i > 0) && same; if (copy) { /* Copy the post from the first xform to the rest of them */ for (j = 0; j < 3; j++) { cp->xform[i].post[j][0] = cp->xform[0].post[j][0]; cp->xform[i].post[j][1] = cp->xform[0].post[j][1]; } } else { if (b&1) { /* 50% chance */ double f = M_PI * rand_d11(rc); double t[2][2]; t[0][0] = (cp->xform[i].c[0][0] * cos(f) + cp->xform[i].c[0][1] * -sin(f)); t[0][1] = (cp->xform[i].c[0][0] * sin(f) + cp->xform[i].c[0][1] * cos(f)); t[1][0] = (cp->xform[i].c[1][0] * cos(f) + cp->xform[i].c[1][1] * -sin(f)); t[1][1] = (cp->xform[i].c[1][0] * sin(f) + cp->xform[i].c[1][1] * cos(f)); cp->xform[i].c[0][0] = t[0][0]; cp->xform[i].c[0][1] = t[0][1]; cp->xform[i].c[1][0] = t[1][0]; cp->xform[i].c[1][1] = t[1][1]; f *= -1.0; t[0][0] = (cp->xform[i].post[0][0] * cos(f) + cp->xform[i].post[0][1] * -sin(f)); t[0][1] = (cp->xform[i].post[0][0] * sin(f) + cp->xform[i].post[0][1] * cos(f)); t[1][0] = (cp->xform[i].post[1][0] * cos(f) + cp->xform[i].post[1][1] * -sin(f)); t[1][1] = (cp->xform[i].post[1][0] * sin(f) + cp->xform[i].post[1][1] * cos(f)); cp->xform[i].post[0][0] = t[0][0]; cp->xform[i].post[0][1] = t[0][1]; cp->xform[i].post[1][0] = t[1][0]; cp->xform[i].post[1][1] = t[1][1]; } if (b&2) { /* 33% chance */ double f = 0.2 + rand_d01(rc); double g = 0.2 + rand_d01(rc); if (rand_bool(rc)) f = 1.0 / f; if (rand_bool(rc)) g = f; else { if (rand_bool(rc)) g = 1.0 / g; } cp->xform[i].c[0][0] /= f; cp->xform[i].c[0][1] /= f; cp->xform[i].c[1][1] /= g; cp->xform[i].c[1][0] /= g; cp->xform[i].post[0][0] *= f; cp->xform[i].post[1][0] *= f; cp->xform[i].post[0][1] *= g; cp->xform[i].post[1][1] *= g; } if (b&4) { /* 16% chance */ double f = rand_d11(rc); double g = rand_d11(rc); cp->xform[i].c[2][0] -= f; cp->xform[i].c[2][1] -= g; cp->xform[i].post[2][0] += f; cp->xform[i].post[2][1] += g; } } } } else if (mutate_mode == MUTATE_COLOR_PALETTE) { double s = rand_d01(rc); if (s < 0.4) { /* randomize xform color coords */ flam3_improve_colors(cp, 100, 0, 10, pc, rc); } else if (s < 0.8) { /* randomize xform color coords and palette */ flam3_improve_colors(cp, 25, 1, 10, pc, rc); } else { /* randomize palette only */ const palette * const p = palette_random (pc, rc); assert (p != NULL); palette_copy (p, &cp->palette); palette_rotate_hue (&cp->palette, cp->hue_rotation); } } else if (mutate_mode == MUTATE_DELETE_XFORM) { int nx = rand_mod(rc, cp->num_xforms); sprintf(ministr,"%d",nx); if (cp->num_xforms > 1) flam3_delete_xform(cp,nx); } else { /* MUTATE_ALL_COEFS */ int x; flam3_random(&mutation, ivars, ivars_n, sym, cp->num_xforms, pc, rc); /* change all the coefs by a fraction of the random */ for (x = 0; x < cp->num_xforms; x++) { for (i = 0; i < 3; i++) { for (j = 0; j < 2; j++) { cp->xform[x].c[i][j] += speed * mutation.xform[x].c[i][j]; } } /* Eventually, we can mutate the parametric variation coefs here. */ } } clear_cp(&mutation,flam3_defaults_on); } void flam3_random(flam3_genome *cp, int *ivars, int ivars_n, int sym, int spec_xforms, const palette_collection * const pc, randctx * const rc) { int i, nxforms, var, samed, multid, samepost, postid, addfinal=0; int finum = -1; int n; int mvar = flam3_nvariations; double sum; static int xform_distrib[] = { 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6 }; clear_cp(cp,flam3_defaults_on); cp->hue_rotation = rand_mod(rc, 8) ? 0.0 : rand_d01(rc); const palette * const p = palette_random (pc, rc); assert (p != NULL); palette_copy (p, &cp->palette); palette_rotate_hue (&cp->palette, cp->hue_rotation); cp->time = 0.0; cp->interpolation = flam3_interpolation_linear; cp->palette_interpolation = flam3_palette_interpolation_hsv; /* Choose the number of xforms */ if (spec_xforms>0) { nxforms = spec_xforms; flam3_add_xforms(cp,nxforms,0,0); } else { nxforms = rand_distrib(rc, xform_distrib); flam3_add_xforms(cp,nxforms,0,0); /* Add a final xform 15% of the time */ addfinal = rand_d01(rc) < 0.15; if (addfinal) { flam3_add_xforms(cp,1,0,1); nxforms = nxforms + addfinal; finum = nxforms-1; } } /* If first input variation is 'flam3_variation_random' */ /* choose one to use or decide to use multiple */ if (flam3_variation_random == ivars[0]) { if (rand_bool(rc)) { var = rand_mod(rc, mvar); } else { var = flam3_variation_random; } } else { var = flam3_variation_random_fromspecified; } samed = rand_bool(rc); multid = rand_bool(rc); postid = rand_d01(rc) < 0.6; samepost = rand_bool(rc); /* Loop over xforms */ for (i = 0; i < nxforms; i++) { int j, k; cp->xform[i].density = 1.0 / nxforms; cp->xform[i].color = i&1; cp->xform[i].color_speed = 0.5; cp->xform[i].animate = 1.0; for (j = 0; j < 3; j++) { for (k = 0; k < 2; k++) { cp->xform[i].c[j][k] = rand_d11(rc); cp->xform[i].post[j][k] = (double)(k==j); } } if ( i != finum ) { if (!postid) { for (j = 0; j < 3; j++) for (k = 0; k < 2; k++) { if (samepost || (i==0)) cp->xform[i].post[j][k] = rand_d11(rc); else cp->xform[i].post[j][k] = cp->xform[0].post[j][k]; } } /* Clear all variation coefs */ for (j = 0; j < flam3_nvariations; j++) cp->xform[i].var[j] = 0.0; if (flam3_variation_random != var && flam3_variation_random_fromspecified != var) { /* Use only one variation specified for all xforms */ cp->xform[i].var[var] = 1.0; } else if (multid && flam3_variation_random == var) { /* Choose a random var for this xform */ cp->xform[i].var[rand_mod(rc, mvar)] = 1.0; } else { if (samed && i > 0) { /* Copy the same variations from the previous xform */ for (j = 0; j < flam3_nvariations; j++) { cp->xform[i].var[j] = cp->xform[i-1].var[j]; flam3_copy_params(&(cp->xform[i]),&(cp->xform[i-1]),j); } } else { /* Choose a random number of vars to use, at least 2 */ /* but less than flam3_nvariations.Probability leans */ /* towards fewer variations. */ n = 2; while ((rand_bool(rc)) && (nxform[i].var[rand_mod(rc, mvar)] = rand_d01(rc); else cp->xform[i].var[ivars[rand_mod(rc, ivars_n)]] = rand_d01(rc); } /* Normalize weights to 1.0 total. */ sum = 0.0; for (j = 0; j < flam3_nvariations; j++) sum += cp->xform[i].var[j]; if (sum == 0.0) cp->xform[i].var[rand_mod(rc, flam3_nvariations)] = 1.0; else { for (j = 0; j < flam3_nvariations; j++) cp->xform[i].var[j] /= sum; } } } } else { /* Handle final xform randomness. */ n = 1; if (rand_bool(rc)) n++; /* Randomly choose n variations, and change their weights. */ /* A var can be selected more than once, further reducing */ /* the probability that multiple vars are used. */ for (j = 0; j < n; j++) { if (flam3_variation_random_fromspecified != var) cp->xform[i].var[rand_mod(rc, mvar)] = rand_d01(rc); else cp->xform[i].var[ivars[rand_mod(rc, ivars_n)]] = rand_d01(rc); } /* Normalize weights to 1.0 total. */ sum = 0.0; for (j = 0; j < flam3_nvariations; j++) sum += cp->xform[i].var[j]; if (sum == 0.0) cp->xform[i].var[rand_mod(rc, flam3_nvariations)] = 1.0; else { for (j = 0; j < flam3_nvariations; j++) cp->xform[i].var[j] /= sum; } } /* Generate random params for parametric variations, if selected. */ if (cp->xform[i].var[VAR_BLOB] > 0) { /* Create random params for blob */ cp->xform[i].blob_low = 0.2 + 0.5 * rand_d01(rc); cp->xform[i].blob_high = 0.8 + 0.4 * rand_d01(rc); cp->xform[i].blob_waves = (int)(2 + 5 * rand_d01(rc)); } if (cp->xform[i].var[VAR_PDJ] > 0) { /* Create random params for PDJ */ cp->xform[i].pdj_a = 3.0 * rand_d11(rc); cp->xform[i].pdj_b = 3.0 * rand_d11(rc); cp->xform[i].pdj_c = 3.0 * rand_d11(rc); cp->xform[i].pdj_d = 3.0 * rand_d11(rc); } if (cp->xform[i].var[VAR_FAN2] > 0) { /* Create random params for fan2 */ cp->xform[i].fan2_x = rand_d11(rc); cp->xform[i].fan2_y = rand_d11(rc); } if (cp->xform[i].var[VAR_RINGS2] > 0) { /* Create random params for rings2 */ cp->xform[i].rings2_val = 2*rand_d01(rc); } if (cp->xform[i].var[VAR_PERSPECTIVE] > 0) { /* Create random params for perspective */ cp->xform[i].perspective_angle = rand_d01(rc); cp->xform[i].perspective_dist = 2*rand_d01(rc) + 1.0; } if (cp->xform[i].var[VAR_JULIAN] > 0) { /* Create random params for julian */ cp->xform[i].julian_power = (int)(5*rand_d01(rc) + 2); cp->xform[i].julian_dist = 1.0; } if (cp->xform[i].var[VAR_JULIASCOPE] > 0) { /* Create random params for juliaScope */ cp->xform[i].juliascope_power = (int)(5*rand_d01(rc) + 2); cp->xform[i].juliascope_dist = 1.0; } if (cp->xform[i].var[VAR_RADIAL_BLUR] > 0) { /* Create random params for radialBlur */ cp->xform[i].radial_blur_angle = (2 * rand_d01(rc) - 1); } if (cp->xform[i].var[VAR_PIE] > 0) { /* Create random params for pie */ cp->xform[i].pie_slices = (int) 10.0*rand_d01(rc); cp->xform[i].pie_thickness = rand_d01(rc); cp->xform[i].pie_rotation = 2.0 * M_PI * rand_d11(rc); } if (cp->xform[i].var[VAR_NGON] > 0) { /* Create random params for ngon */ cp->xform[i].ngon_sides = (int) rand_d01(rc)* 10 + 3; cp->xform[i].ngon_power = 3*rand_d01(rc) + 1; cp->xform[i].ngon_circle = 3*rand_d01(rc); cp->xform[i].ngon_corners = 2*rand_d01(rc)*cp->xform[i].ngon_circle; } if (cp->xform[i].var[VAR_CURL] > 0) { /* Create random params for curl */ cp->xform[i].curl_c1 = rand_d01(rc); cp->xform[i].curl_c2 = rand_d01(rc); } if (cp->xform[i].var[VAR_RECTANGLES] > 0) { /* Create random params for rectangles */ cp->xform[i].rectangles_x = rand_d01(rc); cp->xform[i].rectangles_y = rand_d01(rc); } if (cp->xform[i].var[VAR_DISC2] > 0) { /* Create random params for disc2 */ cp->xform[i].disc2_rot = 0.5 * rand_d01(rc); cp->xform[i].disc2_twist = 0.5 * rand_d01(rc); } if (cp->xform[i].var[VAR_SUPER_SHAPE] > 0) { /* Create random params for supershape */ cp->xform[i].super_shape_rnd = rand_d01(rc); cp->xform[i].super_shape_m = (int) rand_d01(rc)*6; cp->xform[i].super_shape_n1 = rand_d01(rc)*40; cp->xform[i].super_shape_n2 = rand_d01(rc)*20; cp->xform[i].super_shape_n3 = cp->xform[i].super_shape_n2; cp->xform[i].super_shape_holes = 0.0; } if (cp->xform[i].var[VAR_FLOWER] > 0) { /* Create random params for flower */ cp->xform[i].flower_petals = 4 * rand_d01(rc); cp->xform[i].flower_holes = rand_d01(rc); } if (cp->xform[i].var[VAR_CONIC] > 0) { /* Create random params for conic */ cp->xform[i].conic_eccentricity = rand_d01(rc); cp->xform[i].conic_holes = rand_d01(rc); } if (cp->xform[i].var[VAR_PARABOLA] > 0) { /* Create random params for parabola */ cp->xform[i].parabola_height = 0.5 + rand_d01(rc); cp->xform[i].parabola_width = 0.5 + rand_d01(rc); } if (cp->xform[i].var[VAR_BENT2] > 0) { /* Create random params for bent2 */ cp->xform[i].bent2_x = 3*(-0.5 + rand_d01(rc)); cp->xform[i].bent2_y = 3*(-0.5 + rand_d01(rc)); } if (cp->xform[i].var[VAR_BIPOLAR] > 0) { /* Create random params for bipolar */ cp->xform[i].bipolar_shift = 2.0 * rand_d01(rc) - 1; } if (cp->xform[i].var[VAR_CELL] > 0) { /* Create random params for cell */ cp->xform[i].cell_size = 2.0 * rand_d01(rc) + 0.5; } if (cp->xform[i].var[VAR_CPOW] > 0) { /* Create random params for cpow */ cp->xform[i].cpow_r = 3.0 * rand_d01(rc); cp->xform[i].cpow_i = rand_d01(rc) - 0.5; cp->xform[i].cpow_power = (int)(5.0 * rand_d01(rc)); } if (cp->xform[i].var[VAR_CURVE] > 0) { /* Create random params for curve */ cp->xform[i].curve_xamp = 5 * (rand_d01(rc)-.5); cp->xform[i].curve_yamp = 4 * (rand_d01(rc)-.5); cp->xform[i].curve_xlength = 2 * (rand_d01(rc)+.5); cp->xform[i].curve_ylength = 2 * (rand_d01(rc)+.5); } if (cp->xform[i].var[VAR_ESCHER] > 0) { /* Create random params for escher */ cp->xform[i].escher_beta = M_PI * rand_d11(rc); } if (cp->xform[i].var[VAR_LAZYSUSAN] > 0) { /* Create random params for lazysusan */ cp->xform[i].lazysusan_x = 2.0*rand_d11(rc); cp->xform[i].lazysusan_y = 2.0*rand_d11(rc); cp->xform[i].lazysusan_spin = M_PI*rand_d11(rc); cp->xform[i].lazysusan_space = 2.0*rand_d11(rc); cp->xform[i].lazysusan_twist = 2.0*rand_d11(rc); } if (cp->xform[i].var[VAR_MODULUS] > 0) { /* Create random params for modulus */ cp->xform[i].modulus_x = rand_d11(rc); cp->xform[i].modulus_y = rand_d11(rc); } if (cp->xform[i].var[VAR_OSCILLOSCOPE] > 0) { /* Create random params for oscope */ cp->xform[i].oscope_separation = 1.0 + rand_d11(rc); cp->xform[i].oscope_frequency = M_PI * rand_d11(rc); cp->xform[i].oscope_amplitude = 1.0 + 2 * rand_d01(rc); cp->xform[i].oscope_damping = rand_d01(rc); } if (cp->xform[i].var[VAR_POPCORN2] > 0) { /* Create random params for popcorn2 */ cp->xform[i].popcorn2_x = 0.2 * rand_d01(rc); cp->xform[i].popcorn2_y = 0.2 * rand_d01(rc); cp->xform[i].popcorn2_c = 5 * rand_d01(rc); } if (cp->xform[i].var[VAR_SEPARATION] > 0) { /* Create random params for separation */ cp->xform[i].separation_x = 1 + rand_d11(rc); cp->xform[i].separation_y = 1 + rand_d11(rc); cp->xform[i].separation_xinside = rand_d11(rc); cp->xform[i].separation_yinside = rand_d11(rc); } if (cp->xform[i].var[VAR_SPLIT] > 0) { /* Create random params for split */ cp->xform[i].split_xsize = rand_d11(rc); cp->xform[i].split_ysize = rand_d11(rc); } if (cp->xform[i].var[VAR_SPLITS] > 0) { /* Create random params for splits */ cp->xform[i].splits_x = rand_d11(rc); cp->xform[i].splits_y = rand_d11(rc); } if (cp->xform[i].var[VAR_STRIPES] > 0) { /* Create random params for stripes */ cp->xform[i].stripes_space = rand_d01(rc); cp->xform[i].stripes_warp = 5*rand_d01(rc); } if (cp->xform[i].var[VAR_WEDGE] > 0) { /* Create random params for wedge */ cp->xform[i].wedge_angle = M_PI*rand_d01(rc); cp->xform[i].wedge_hole = 0.5*rand_d11(rc); cp->xform[i].wedge_count = floor(5*rand_d01(rc))+1; cp->xform[i].wedge_swirl = rand_d01(rc); } if (cp->xform[i].var[VAR_WEDGE_JULIA] > 0) { /* Create random params for wedge_julia */ cp->xform[i].wedge_julia_power = (int)(5*rand_d01(rc) + 2); cp->xform[i].wedge_julia_dist = 1.0; cp->xform[i].wedge_julia_count = (int)(3*rand_d01(rc) + 1); cp->xform[i].wedge_julia_angle = M_PI * rand_d01(rc); } if (cp->xform[i].var[VAR_WEDGE_SPH] > 0) { /* Create random params for wedge_sph */ cp->xform[i].wedge_sph_angle = M_PI*rand_d01(rc); cp->xform[i].wedge_sph_hole = 0.5*rand_d11(rc); cp->xform[i].wedge_sph_count = floor(5*rand_d01(rc))+1; cp->xform[i].wedge_sph_swirl = rand_d01(rc); } if (cp->xform[i].var[VAR_WHORL] > 0) { /* Create random params for whorl */ cp->xform[i].whorl_inside = rand_d01(rc); cp->xform[i].whorl_outside = rand_d01(rc); } if (cp->xform[i].var[VAR_WAVES2] > 0) { /* Create random params for waves2 */ cp->xform[i].waves2_scalex = 0.5 + rand_d01(rc); cp->xform[i].waves2_scaley = 0.5 + rand_d01(rc); cp->xform[i].waves2_freqx = 4 * rand_d01(rc); cp->xform[i].waves2_freqy = 4 * rand_d01(rc); } if (cp->xform[i].var[VAR_AUGER] > 0) { /* Create random params for auger */ cp->xform[i].auger_sym = 0; cp->xform[i].auger_weight = 0.5 + rand_d01(rc)/2.0; cp->xform[i].auger_freq = floor(5*rand_d01(rc))+1; cp->xform[i].auger_scale = rand_d01(rc); } if (cp->xform[i].var[VAR_FLUX] > 0) { /* Create random params for flux */ cp->xform[i].flux_spread = 0.5 + rand_d01(rc)/2.0; } if (cp->xform[i].var[VAR_MOBIUS] > 0) { /* Create random params for mobius */ cp->xform[i].mobius_re_a = rand_d11(rc); cp->xform[i].mobius_im_a = rand_d11(rc); cp->xform[i].mobius_re_b = rand_d11(rc); cp->xform[i].mobius_im_b = rand_d11(rc); cp->xform[i].mobius_re_c = rand_d11(rc); cp->xform[i].mobius_im_c = rand_d11(rc); cp->xform[i].mobius_re_d = rand_d11(rc); cp->xform[i].mobius_im_d = rand_d11(rc); } } /* Randomly add symmetry (but not if we've already added a final xform) */ if (sym || (!rand_mod(rc, 4) && !addfinal)) flam3_add_symmetry(cp, sym, rc); else cp->symmetry = 0; //qsort((char *) cp->xform, (cp->num_xforms-addfinal), sizeof(flam3_xform), compare_xforms); } static int sort_by_x(const void *av, const void *bv) { double4 a = *((double4 *) av); double4 b = *((double4 *) bv); if (a[0] < b[0]) return -1; if (a[0] > b[0]) return 1; return 0; } static int sort_by_y(const void *av, const void *bv) { double4 a = *((double4 *) av); double4 b = *((double4 *) bv); if (a[1] < b[1]) return -1; if (a[1] > b[1]) return 1; return 0; } /* * find a 2d bounding box that does not enclose eps of the fractal density * in each compass direction. */ int flam3_estimate_bounding_box(flam3_genome *cp, double eps, int nsamples, double *bmin, double *bmax, randctx *rc) { int i; int low_target, high_target; double min[2], max[2]; double4 *points; int bv; unsigned short *xform_distrib; if (nsamples <= 0) nsamples = 10000; int ret = posix_memalign ((void **) &points, sizeof (*points), sizeof(*points) * nsamples); assert (ret == 0 && points != NULL); const double4 start = (double4) { rand_d11(rc), rand_d11(rc), 0.0, 0.0 }; if (prepare_precalc_flags(cp)) return(-1); xform_distrib = flam3_create_xform_distrib(cp); if (xform_distrib==NULL) return(-1); bv=flam3_iterate(cp, nsamples, 20, start, points, xform_distrib, rc); free(xform_distrib); if ( bv/(double)nsamples > eps ) eps = 3*bv/(double)nsamples; if ( eps > 0.3 ) eps = 0.3; low_target = (int)(nsamples * eps); high_target = nsamples - low_target; min[0] = min[1] = 1e10; max[0] = max[1] = -1e10; for (i = 0; i < nsamples; i++) { const double4 p = points[i]; if (p[0] < min[0]) min[0] = p[0]; if (p[1] < min[1]) min[1] = p[1]; if (p[0] > max[0]) max[0] = p[0]; if (p[1] > max[1]) max[1] = p[1]; } if (low_target == 0) { bmin[0] = min[0]; bmin[1] = min[1]; bmax[0] = max[0]; bmax[1] = max[1]; free(points); return(bv); } qsort(points, nsamples, sizeof(double4), sort_by_x); bmin[0] = points[low_target][0]; bmax[0] = points[high_target][0]; qsort(points, nsamples, sizeof(double4), sort_by_y); bmin[1] = points[low_target][1]; bmax[1] = points[high_target][1]; free(points); return(bv); }