/* FLAM3 - cosmic recursive fractal flames 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 "isaacs.h" #include "config.h" int verbose; char *get_extras() { char *e = getenv("extras"); return e; } void gprint(flam3_genome *cp, int extras) { if (getenv("noedits")) flam3_print(stdout, cp, extras ? get_extras() : NULL, flam3_dont_print_edits); else flam3_print(stdout, cp, extras ? get_extras() : NULL, flam3_print_edits); } void test_cp(flam3_genome *cp) { cp->time = 0.0; cp->interpolation = flam3_interpolation_linear; cp->palette_interpolation = flam3_palette_interpolation_hsv; cp->background[0] = 0.0; cp->background[1] = 0.0; cp->background[2] = 0.0; cp->center[0] = 0.0; cp->center[1] = 0.0; cp->rotate = 0.0; cp->pixels_per_unit = 64; cp->width = 128; cp->height = 128; cp->spatial_oversample = 1; cp->spatial_filter_radius = 0.5; cp->spatial_filter_select = 0; cp->zoom = 0.0; cp->sample_density = 1; cp->nbatches = 1; cp->ntemporal_samples = 1; cp->estimator = 0.0; cp->estimator_minimum = 0.0; cp->estimator_curve = 0.6; } flam3_genome *string_to_cp(char *s, int *n) { flam3_genome *cp; FILE *fp; fp = fopen(s, "rb"); if (NULL == fp) { perror(s); exit(1); } cp = flam3_parse_from_file(fp, s, flam3_defaults_on, n); if (NULL == cp) { fprintf(stderr, "could not read genome from %s.\n", s); exit(1); } return cp; } xmlDocPtr create_new_editdoc(char *action, flam3_genome *parent0, flam3_genome *parent1) { xmlDocPtr doc = NULL, comment_doc = NULL; xmlNodePtr root_node = NULL, node = NULL, nodecopy = NULL; xmlNodePtr root_comment = NULL; struct tm *localt; time_t mytime; char *ai; char timestring[100]; char *nick = getenv("nick"); char *url = getenv("url"); char *id = getenv("id"); char *comment = getenv("comment"); int sheep_gen = argi("sheep_gen",-1); int sheep_id = argi("sheep_id",-1); char buffer[100]; char comment_string[100]; doc = xmlNewDoc( (const xmlChar *)"1.0"); /* Create the root node, called "edit" */ root_node = xmlNewNode(NULL, (const xmlChar *)"edit"); xmlDocSetRootElement(doc,root_node); /* Add the edit attributes */ /* date */ mytime = time(NULL); localt = localtime(&mytime); /* XXX use standard time format including timezone */ strftime(timestring, 100, "%a %b %e %H:%M:%S %z %Y", localt); xmlNewProp(root_node, (const xmlChar *)"date", (const xmlChar *)timestring); /* nick */ if (nick) { xmlNewProp(root_node, (const xmlChar *)"nick", (const xmlChar *)nick); } /* url */ if (url) { xmlNewProp(root_node, (const xmlChar *)"url", (const xmlChar *)url); } if (id) { xmlNewProp(root_node, (const xmlChar *)"id", (const xmlChar *)id); } /* action */ xmlNewProp(root_node, (const xmlChar *)"action", (const xmlChar *)action); /* sheep info */ if (sheep_gen > 0 && sheep_id > 0) { /* Create a child node of the root node called sheep */ node = xmlNewChild(root_node, NULL, (const xmlChar *)"sheep", NULL); /* Create the sheep attributes */ sprintf(buffer, "%d", sheep_gen); xmlNewProp(node, (const xmlChar *)"generation", (const xmlChar *)buffer); sprintf(buffer, "%d", sheep_id); xmlNewProp(node, (const xmlChar *)"id", (const xmlChar *)buffer); } /* Check for the parents */ /* If Parent 0 not specified, this is a randomly generated genome. */ if (parent0) { if (parent0->edits) { /* Copy the node from the parent */ node = xmlDocGetRootElement(parent0->edits); nodecopy = xmlCopyNode(node, 1); xmlNewProp(nodecopy,(const xmlChar *)"filename", (const xmlChar *)parent0->parent_fname); sprintf(buffer,"%d",parent0->genome_index); xmlNewProp(nodecopy,(const xmlChar *)"index", (const xmlChar *)buffer); xmlAddChild(root_node, nodecopy); } else { /* Insert a (parent has no edit) message */ nodecopy = xmlNewChild(root_node, NULL, (const xmlChar *)"edit",NULL); xmlNewProp(nodecopy,(const xmlChar *)"filename", (const xmlChar *)parent0->parent_fname); sprintf(buffer,"%d",parent0->genome_index); xmlNewProp(nodecopy,(const xmlChar *)"index", (const xmlChar *)buffer); } } if (parent1) { if (parent1->edits) { /* Copy the node from the parent */ node = xmlDocGetRootElement(parent1->edits); nodecopy = xmlCopyNode(node, 1); xmlNewProp(nodecopy,(const xmlChar *)"filename", (const xmlChar *)parent1->parent_fname); sprintf(buffer,"%d",parent1->genome_index); xmlNewProp(nodecopy,(const xmlChar *)"index", (const xmlChar *)buffer); xmlAddChild(root_node, nodecopy); } else { /* Insert a (parent has no edit) message */ nodecopy = xmlNewChild(root_node, NULL, (const xmlChar *)"edit",NULL); xmlNewProp(nodecopy,(const xmlChar *)"filename", (const xmlChar *)parent1->parent_fname); sprintf(buffer,"%d",parent1->genome_index); xmlNewProp(nodecopy,(const xmlChar *)"index", (const xmlChar *)buffer); } } /* Comment string */ /* This one's hard, since we have to treat the comment string as */ /* a valid XML document. Create a new document using the comment */ /* string as the in-memory document, and then copy all children of */ /* the root node into the edit structure */ /* Parsing the comment string should be done once and then copied */ /* for each call to create_new_editdoc, but that's for later. */ if (comment) { sprintf(comment_string,"%s",comment); comment_doc = xmlReadMemory(comment_string, strlen(comment_string), "comment.env", NULL, XML_PARSE_NONET); /* Check for errors */ if (comment_doc==NULL) { fprintf(stderr, "Failed to parse comment into XML!\n"); exit(1); } /* Loop through the children of the new document and copy */ /* them into the root_node */ root_comment = xmlDocGetRootElement(comment_doc); for (node=root_comment->children; node; node = node->next) { nodecopy = xmlCopyNode(node,1); xmlAddChild(root_node, nodecopy); } /* Free the created document */ xmlFreeDoc(comment_doc); } /* return the xml doc */ return(doc); } void offset(flam3_genome *g) { char *os = getenv("offset"); double ox, oy; if (NULL == os) return; sscanf(os, "%lf:%lf", &ox, &oy); g->center[0] += ox / (g->pixels_per_unit * g->spatial_oversample); g->center[1] += oy / (g->pixels_per_unit * g->spatial_oversample); } void spin(int frame, double blend, flam3_genome *parent, flam3_genome *templ) { flam3_genome *result; char action[50]; xmlDocPtr doc; /* Spin the parent blend*360 degrees */ result = sheep_loop(parent,blend); /* Apply the template if necessary */ if (templ) flam3_apply_template(result, templ); /* Set genome parameters accordingly */ result->time = (double)frame; result->interpolation = flam3_interpolation_linear; result->palette_interpolation = flam3_palette_interpolation_hsv; /* Force linear interpolation - unsure if this is still necessary */ /* I believe we put this in so that older clients could render frames */ // result->interpolation_type = flam3_inttype_linear; /* Create the edit doc xml */ sprintf(action,"rotate %g",blend*360.0); doc = create_new_editdoc(action, parent, (flam3_genome *)NULL); result->edits = doc; /* Subpixel jitter */ offset(result); /* Make the name of the flame the time */ sprintf(result->flame_name,"%f",result->time); /* Print the resulting xml */ gprint(result, 1); /* Clear out the xml doc */ xmlFreeDoc(result->edits); /* Clear the result cp */ clear_cp(result,flam3_defaults_on); /* Free the cp allocated in flam3_sheep_loop */ free(result); } void spin_inter(int frame, double blend, int seqflag, flam3_genome *parents, flam3_genome *templ) { flam3_genome *result; char action[50]; xmlDocPtr doc; char *ai; double stagger = argf("stagger", 0.0); /* Interpolate between spun parents */ result = sheep_edge(parents, blend, seqflag, stagger); /* Unsure why we check for random palettes on both ends... */ if ((parents[0].palette_index != flam3_palette_random) && (parents[1].palette_index != flam3_palette_random)) { result->palette_index = flam3_palette_interpolated; result->palette_index0 = parents[0].palette_index; result->hue_rotation0 = parents[0].hue_rotation; result->palette_index1 = parents[1].palette_index; result->hue_rotation1 = parents[1].hue_rotation; result->palette_blend = blend; } /* Apply template if necessary */ if (templ) flam3_apply_template(result, templ); /* Set genome attributes */ result->time = (double)frame; // result->interpolation_type = flam3_inttype_linear; /* Create the edit doc xml */ sprintf(action,"interpolate %g",blend*360.0); doc = create_new_editdoc(action, &parents[0], &parents[1]); result->edits = doc; /* Subpixel jitter */ offset(result); /* Make the name of the flame the time */ sprintf(result->flame_name,"%f",result->time); /* Print the genome */ gprint(result, 1); /* Clean up */ xmlFreeDoc(result->edits); /* Free genome storage */ clear_cp(result,flam3_defaults_on); free(result); } void truncate_variations(flam3_genome *g, int max_vars, char *action) { int i, j, nvars, smallest; double sv=0; char trunc_note[30]; for (i = 0; i < g->num_xforms; i++) { double d = g->xform[i].density; /* if (0.0 < d && d < 0.001) */ if (d < 0.001 && (g->final_xform_index != i)) { sprintf(trunc_note," trunc_density %d",i); //strcat(action,trunc_note); add_to_action(action,trunc_note); flam3_delete_xform(g, i); /* g->xform[i].density = 0.0; } else if (d > 0.0) { */ } else { do { nvars = 0; smallest = -1; for (j = 0; j < flam3_nvariations; j++) { double v = g->xform[i].var[j]; if (v != 0.0) { nvars++; if (-1 == smallest || fabs(v) < sv) { smallest = j; sv = fabs(v); } } } if (nvars > max_vars) { sprintf(trunc_note," trunc %d %d",i,smallest); //strcat(action,trunc_note); add_to_action(action,trunc_note); g->xform[i].var[smallest] = 0.0; } } while (nvars > max_vars); } } } static double golden_bit(randctx *rc) { return flam3_random_isaac_bit(rc)?0.38196:0.61804; } int main(argc, argv) int argc; char **argv; { int debug = 0; int count; char *ai; unsigned char *image; flam3_genome *templ = NULL; flam3_genome cp_orig, cp_save; int i, j; double avg_pix, fraction_black, fraction_white; double avg_thresh = argf("avg", 20.0); double black_thresh = argf("black", 0.01); double white_limit = argf("white", 0.05); int nframes = argi("nframes", 100); int sym = argi("symmetry", 0); int enclosed = argi("enclosed", 1); char *clone = getenv("clone"); char *clone_all = getenv("clone_all"); char *animate = getenv("animate"); char *mutate = getenv("mutate"); char *cross0 = getenv("cross0"); char *cross1 = getenv("cross1"); char *method = getenv("method"); char *inter = getenv("inter"); char *rotate = getenv("rotate"); char *strip = getenv("strip"); char *sequence = getenv("sequence"); int loops = argi("loops", 1); int frame = argi("frame", 0); int rep, repeat = argi("repeat", 1); double speed = argf("speed", 0.1); int bits = argi("bits", 33); int ntries = argi("tries", 10); char *use_vars = getenv("use_vars"); char *dont_use_vars = getenv("dont_use_vars"); flam3_genome *parent0=NULL, *parent1=NULL; flam3_genome selp0, selp1; flam3_genome *aselp0, *aselp1; int parent0_n, parent1_n; int num_threads = 1; flam3_genome *cp; int ncp; int ivars[max_specified_vars]; int novars[max_specified_vars]; int num_ivars = 0; int num_novars = 0; char *var_tok; flam3_frame f; char action[flam3_max_action_length]; stat_struct stats; #ifdef WIN32 char *slashloc; char exepath[256]; char palpath[256]; memset(exepath,0,256); memset(palpath,0,256); slashloc = strrchr(argv[0],'\\'); if (NULL==slashloc) { sprintf(palpath,"flam3_palettes=flam3-palettes.xml"); } else { strncpy(exepath,argv[0],slashloc-argv[0]+1); sprintf(palpath,"flam3_palettes=%sflam3-palettes.xml",exepath); } putenv(palpath); #endif if (argc>1) { if (strcmp("--version",argv[1])==0) { printf("FLAM3-%s\n",flam3_version()); exit(0); } else { printf("unrecognized option %s, aborting.\n",argv[1]); exit(-1); } } verbose = argi("verbose", 0); memset(&cp_orig, 0, sizeof(flam3_genome)); memset(&cp_save, 0, sizeof(flam3_genome)); memset(&selp0, 0, sizeof(flam3_genome)); memset(&selp1, 0, sizeof(flam3_genome)); if (1 != argc) { docstring(); exit(0); } /* Init random number generators */ flam3_init_frame(&f); flam3_srandom(); //f.temporal_filter_radius = 0.0; f.bits = bits; f.bytes_per_channel = 1; f.earlyclip = 1; f.verbose = 0; f.genomes = &cp_orig; f.ngenomes = 1; f.pixel_aspect_ratio = 1.0; f.progress = 0; f.nthreads = num_threads; f.sub_batch_size = 10000; test_cp(&cp_orig); // just for the width & height /* Are the variations to be used specified? */ if (use_vars && dont_use_vars) { fprintf(stderr,"use_vars and dont_use_vars cannot both be specified. Terminating.\n"); exit(-1); } /* Specify reasonable defaults if nothing is specified */ if (!use_vars && !dont_use_vars) { novars[num_novars++] = VAR_NOISE; novars[num_novars++] = VAR_BLUR; novars[num_novars++] = VAR_GAUSSIAN_BLUR; novars[num_novars++] = VAR_RADIAL_BLUR; novars[num_novars++] = VAR_NGON; novars[num_novars++] = VAR_SQUARE; novars[num_novars++] = VAR_RAYS; novars[num_novars++] = VAR_CROSS; novars[num_novars++] = VAR_PRE_BLUR; novars[num_novars++] = VAR_SEPARATION; novars[num_novars++] = VAR_SPLIT; novars[num_novars++] = VAR_SPLITS; /* Loop over the novars and set ivars to the complement */ for (i=0;i=flam3_nvariations) { fprintf(stderr,"specified variation list includes bad value. (%d)\n",ivars[i]); exit(1); } } } else if (dont_use_vars) { /* Parse comma-separated list of variations NOT to use */ var_tok = strtok(dont_use_vars,","); novars[num_novars++] = atoi(var_tok); while(1) { var_tok = strtok(NULL,","); if (var_tok==NULL) break; novars[num_novars++] = atoi(var_tok); if (num_novars==max_specified_vars) { fprintf(stderr,"Maximum number of user-specified variations exceeded. Truncating.\n"); break; } } /* Loop over the novars and set ivars to the complement */ for (i=0;i\n", flam3_version()); for (i = 0; i < ncp; i++) { if (templ) flam3_apply_template(&cp[i], templ); offset(&cp[i]); gprint(&cp[i], 1); } printf("\n"); exit(0); } if (animate) { flam3_genome interpolated; int first_frame,last_frame; int ftime,iscp; double stagger = argf("stagger", 0.0); cp = string_to_cp(animate, &ncp); for (i = 0; i < ncp; i++) { if (i > 0 && cp[i].time <= cp[i-1].time) { fprintf(stderr, "error: control points must be sorted by time, but %g <= %g, index %d.\n", cp[i].time, cp[i-1].time, i); exit(1); } /* Strip out all motion elements here */ for (j=0;j\n", flam3_version()); for (ftime = first_frame; ftime <= last_frame; ftime += 1) { iscp=0; for (i=0;i\n"); exit(0); } if (sequence) { double blend, spread; int seqflag; int framecount; if (nframes <= 0) { fprintf(stderr, "nframes must be positive, not %d.\n", nframes); exit(1); } cp = string_to_cp(sequence, &ncp); if (enclosed) printf("\n", flam3_version()); spread = 1.0/nframes; framecount = 0; #if 1 for (i = 0; i < ncp; i++) { if (loops) { for (frame = 0; frame < nframes; frame++) { blend = frame/(double)nframes; spin(framecount++, blend, &cp[i], templ); } } if (i < ncp-1) for (frame = 0; frame < nframes; frame++) { if (0==frame || nframes-1==frame) seqflag=1; else seqflag=0; blend = frame/(double)nframes; spin_inter(framecount++, blend, seqflag, &cp[i], templ); } } spin(framecount, 0.0, &cp[ncp-1], templ); #else if (1) { flam3_genome res; memset(&res, 0, sizeof(flam3_genome)); res.final_xform_index = -1; flam3_add_xforms(&res, cp[0].num_xforms, 0); if (ncp < 4) { fprintf(stderr, "catmull-rom requires 4 or more control points.\n"); exit(1); } for (i = 0; i < ncp - 3; i++) { for (frame = 0; frame < nframes; frame++) { blend = frame/(double)nframes; interpolate_catmull_rom(cp+i, blend, &res); res.time = frame + i * nframes; gprint(&res, 0); fflush(stdout); } } } #endif if (enclosed) printf("\n"); exit(0); } if (inter || rotate) { double blend, spread; char *fname = inter?inter:rotate; int ni; if (nframes <= 0) { fprintf(stderr, "nframes must be positive, not %d.\n", nframes); exit(1); } blend = frame/(double)nframes; spread = 1.0/nframes; cp = string_to_cp(fname, &ncp); if (enclosed) printf("\n", flam3_version()); if (rotate) { if (1 != ncp) { fprintf(stderr, "rotation requires one control point, not %d.\n", ncp); exit(1); } spin(frame - 1, blend - spread, cp, templ); spin(frame , blend , cp, templ); spin(frame + 1, blend + spread, cp, templ); } else { if (2 != ncp) { fprintf(stderr, "interpolation requires two control points, not %d.\n", ncp); exit(1); } spin_inter(frame - 1, blend - spread, 0, cp, templ); spin_inter(frame , blend , 0, cp, templ); spin_inter(frame + 1, blend + spread, 0, cp, templ); } if (enclosed) printf("\n"); for (ni=0;ni\n", flam3_version()); for (i = 0; i < ncp; i++) { double old_center[2]; /* Strip out motion elements */ for (j=0;j\n"); exit(0); } /* pick a control point until it looks good enough */ if (repeat <= 0) { fprintf(stderr, "repeat must be positive, not %d.\n", repeat); exit(1); } if (enclosed) printf("\n", flam3_version()); image = (unsigned char *) malloc(3 * cp_orig.width * cp_orig.height); for (rep = 0; rep < repeat; rep++) { if (verbose) fprintf(stderr, "flame = %d/%d..", rep+1, repeat); count = 0; if (clone) { parent0 = string_to_cp(clone, &parent0_n); /* Action is 'clone' with trunc_vars concat */ sprintf(action,"clone"); if (getenv("clone_action")) sprintf(action,"clone %s", getenv("clone_action")); flam3_copy(&selp0, &(parent0[random()%parent0_n])); flam3_copy(&cp_save, &selp0); aselp0 = &selp0; aselp1 = NULL; truncate_variations(&cp_save, 5, action); cp_save.edits = create_new_editdoc(action, aselp0, aselp1); } else { int did_color; do { int random_mode=0; if (verbose) fprintf(stderr, "."); did_color = 0; f.time = (double) 0.0; action[0] = 0; if (mutate) { int mutmeth; parent0 = string_to_cp(mutate, &parent0_n); flam3_copy(&selp0, &(parent0[(xorshift_step(&f.rc))%parent0_n])); flam3_copy(&cp_orig, &selp0); aselp0 = &selp0; aselp1 = NULL; if (NULL == getenv("method")) mutmeth = MUTATE_NOT_SPECIFIED; else if (!strcmp(method,"all_vars")) mutmeth = MUTATE_ALL_VARIATIONS; else if (!strcmp(method,"one_xform")) mutmeth = MUTATE_ONE_XFORM_COEFS; else if (!strcmp(method,"add_symmetry")) mutmeth = MUTATE_ADD_SYMMETRY; else if (!strcmp(method,"post_xforms")) mutmeth = MUTATE_POST_XFORMS; else if (!strcmp(method,"color_palette")) mutmeth = MUTATE_COLOR_PALETTE; else if (!strcmp(method,"delete_xform")) mutmeth = MUTATE_DELETE_XFORM; else if (!strcmp(method,"all_coefs")) mutmeth = MUTATE_ALL_COEFS; else { fprintf(stderr,"method '%s' not defined for mutate. defaulting to random.\n",method); mutmeth = MUTATE_NOT_SPECIFIED; } flam3_mutate(&cp_orig, mutmeth, ivars, num_ivars, sym, speed, &f.rc, action); /* Scan string returned for 'mutate color' */ if ( strstr(action,"mutate color") ) did_color = 1; if (cp_orig.flame_name[0]) { char tm[flam3_name_len+1]; strncpy(tm, cp_orig.flame_name, flam3_name_len); snprintf(cp_orig.flame_name, flam3_name_len, "mutation %d of %s", rep, tm); } } else if (cross0) { int i0, i1; int crossmeth; parent0 = string_to_cp(cross0, &parent0_n); parent1 = string_to_cp(cross1, &parent1_n); i0 = (xorshift_step(&f.rc))%parent0_n; i1 = (xorshift_step(&f.rc))%parent1_n; flam3_copy(&selp0, &(parent0[i0])); flam3_copy(&selp1, &(parent1[i1])); aselp0 = &selp0; aselp1 = &selp1; if (NULL == getenv("method")) crossmeth = CROSS_NOT_SPECIFIED; else if (!strcmp(method,"union")) crossmeth = CROSS_UNION; else if (!strcmp(method,"interpolate")) crossmeth = CROSS_INTERPOLATE; else if (!strcmp(method,"alternate")) crossmeth = CROSS_ALTERNATE; else { fprintf(stderr,"method '%s' not defined for cross. defaulting to random.\n",method); crossmeth = CROSS_NOT_SPECIFIED; } flam3_cross(&parent0[i0], &parent1[i1], &cp_orig, crossmeth, &f.rc, action); if (parent0[i0].flame_name[0] || parent1[i1].flame_name[0]) { snprintf(cp_orig.flame_name, flam3_name_len, "%d of %s x %s", rep, parent0[i0].flame_name, parent1[i1].flame_name); } } else { sprintf(action,"random"); random_mode=1; flam3_random(&cp_orig, ivars, num_ivars, sym, 0); aselp0 = NULL; aselp1 = NULL; } /* Adjust bounding box half the time */ if (flam3_random_bit(&f.rc) || random_mode) { double bmin[2], bmax[2]; flam3_estimate_bounding_box(&cp_orig, 0.01, 100000, bmin, bmax, &f.rc); if (flam3_random_isaac_01(&f.rc) < 0.3) { cp_orig.center[0] = (bmin[0] + bmax[0]) / 2.0; cp_orig.center[1] = (bmin[1] + bmax[1]) / 2.0; add_to_action(action," recentered"); } else { double mix0, mix1; if (flam3_random_isaac_bit(&f.rc)) { mix0 = golden_bit(&f.rc) + flam3_random_isaac_11(&f.rc)/5; mix1 = golden_bit(&f.rc); add_to_action(action," reframed0"); } else if (flam3_random_isaac_bit(&f.rc)) { mix0 = golden_bit(&f.rc); mix1 = golden_bit(&f.rc) + flam3_random_isaac_11(&f.rc)/5; add_to_action(action," reframed1"); } else { mix0 = golden_bit(&f.rc) + flam3_random_isaac_11(&f.rc)/5; mix1 = golden_bit(&f.rc) + flam3_random_isaac_11(&f.rc)/5; add_to_action(action," reframed2"); } cp_orig.center[0] = mix0 * bmin[0] + (1-mix0)*bmax[0]; cp_orig.center[1] = mix1 * bmin[1] + (1-mix1)*bmax[1]; } cp_orig.rot_center[0] = cp_orig.center[0]; cp_orig.rot_center[1] = cp_orig.center[1]; cp_orig.pixels_per_unit = cp_orig.width / (bmax[0] - bmin[0]); } truncate_variations(&cp_orig, 5, action); if (!did_color && random()&1) { if (debug) fprintf(stderr,"improving colors...\n"); flam3_improve_colors(&cp_orig, 100, 0, 10); //strcat(action," improved colors"); add_to_action(action," improved colors"); } cp_orig.edits = create_new_editdoc(action, aselp0, aselp1); flam3_copy(&cp_save, &cp_orig); test_cp(&cp_orig); if (flam3_render(&f, image, flam3_field_both, 3, 0, &stats)) { fprintf(stderr,"error rendering test image: aborting.\n"); exit(1); } if (1) { int n, tot, totb, totw; n = cp_orig.width * cp_orig.height; tot = 0; totb = 0; totw = 0; for (i = 0; i < 3*n; i+=3) { tot += (image[i]+image[i+1]+image[i+2]); if (0 == image[i] && 0 == image[i+1] && 0 == image[i+2]) totb++; if (255 == image[i] && 255 == image[i+1] && 255 == image[i+2]) totw++; // printf("%d ", image[i]); } avg_pix = (tot / (double)(3*n)); fraction_black = totb / (double)n; fraction_white = totw / (double)n; if (debug) fprintf(stderr, "avg_pix=%g fraction_black=%g fraction_white=%g n=%g\n", avg_pix, fraction_black, fraction_white, (double)n); } else { avg_pix = avg_thresh + 1.0; fraction_black = black_thresh + 1.0; fraction_white = white_limit - 1.0; } clear_cp(&cp_orig,flam3_defaults_on); count++; } while ((avg_pix < avg_thresh || fraction_black < black_thresh || fraction_white > white_limit) && count < ntries); if (ntries == count) { fprintf(stderr, "warning: reached maximum attempts, giving up.\n"); } } if (templ) flam3_apply_template(&cp_save,templ); cp_save.time = (double)rep; if (1) { char *maxforms = getenv("maxforms"); if (maxforms && atoi(maxforms)) { cp_save.symmetry = 0; while (cp_save.num_xforms > atoi(maxforms)) flam3_delete_xform(&cp_save, cp_save.num_xforms - 1); } } gprint(&cp_save, 1); fflush(stdout); /* Free created documents */ /* (Only free once, since the copy is a ptr to the original) */ xmlFreeDoc(cp_save.edits); clear_cp(&cp_save,0); if (verbose) { fprintf(stderr, "\ndone. action = %s\n", action); } } if (enclosed) printf("\n"); free(image); return 0; }