1 /*
2 FLAM3 - cosmic recursive fractal flames
3 Copyright (C) 1992-2009 Spotworks LLC
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "private.h"
20 #include "isaacs.h"
21 #include "config.h"
22
23 int verbose;
24
get_extras()25 char *get_extras() {
26 char *e = getenv("extras");
27 return e;
28 }
29
gprint(flam3_genome * cp,int extras)30 void gprint(flam3_genome *cp, int extras) {
31 if (getenv("noedits"))
32 flam3_print(stdout, cp, extras ? get_extras() : NULL, flam3_dont_print_edits);
33 else
34 flam3_print(stdout, cp, extras ? get_extras() : NULL, flam3_print_edits);
35 }
36
37
test_cp(flam3_genome * cp)38 void test_cp(flam3_genome *cp) {
39 cp->time = 0.0;
40 cp->interpolation = flam3_interpolation_linear;
41 cp->palette_interpolation = flam3_palette_interpolation_hsv;
42 cp->hsv_rgb_palette_blend = 0.0;
43 cp->background[0] = 0.0;
44 cp->background[1] = 0.0;
45 cp->background[2] = 0.0;
46 cp->center[0] = 0.0;
47 cp->center[1] = 0.0;
48 cp->rotate = 0.0;
49 cp->pixels_per_unit = 64;
50 cp->width = 128;
51 cp->height = 128;
52 cp->spatial_oversample = 1;
53 cp->spatial_filter_radius = 0.5;
54 cp->spatial_filter_select = 0;
55 cp->highlight_power= 1.0;
56 cp->zoom = 0.0;
57 cp->sample_density = 1;
58 cp->nbatches = 1;
59 cp->ntemporal_samples = 1;
60 cp->estimator = 0.0;
61 cp->estimator_minimum = 0.0;
62 cp->estimator_curve = 0.6;
63 }
64
string_to_cp(char * s,int * n,int defaults)65 flam3_genome *string_to_cp(char *s, int *n, int defaults) {
66 flam3_genome *cp;
67 FILE *fp;
68
69 fp = fopen(s, "rb");
70 if (NULL == fp) {
71 perror(s);
72 exit(1);
73 }
74 cp = flam3_parse_from_file(fp, s, defaults, n);
75 if (NULL == cp) {
76 fprintf(stderr, "could not read genome from %s.\n", s);
77 exit(1);
78 }
79 return cp;
80 }
81
create_new_editdoc(char * action,flam3_genome * parent0,flam3_genome * parent1)82 xmlDocPtr create_new_editdoc(char *action, flam3_genome *parent0, flam3_genome *parent1) {
83
84 xmlDocPtr doc = NULL, comment_doc = NULL;
85 xmlNodePtr root_node = NULL, node = NULL, nodecopy = NULL;
86 xmlNodePtr root_comment = NULL;
87 struct tm *localt;
88 time_t mytime;
89 char *ai;
90 char timestring[100];
91 char *nick = getenv("nick");
92 char *url = getenv("url");
93 char *id = getenv("id");
94 char *gen = getenv("gen");
95 char *comment = getenv("comment");
96 int sheep_gen = argi("sheep_gen",-1);
97 int sheep_id = argi("sheep_id",-1);
98 char buffer[100];
99 char comment_string[100];
100
101 doc = xmlNewDoc( (const xmlChar *)"1.0");
102
103 /* Create the root node, called "edit" */
104 root_node = xmlNewNode(NULL, (const xmlChar *)"edit");
105 xmlDocSetRootElement(doc,root_node);
106 /* Add the edit attributes */
107
108 /* date */
109 mytime = time(NULL);
110 localt = localtime(&mytime);
111 /* XXX use standard time format including timezone */
112 strftime(timestring, 100, "%a %b %e %H:%M:%S %z %Y", localt);
113 xmlNewProp(root_node, (const xmlChar *)"date", (const xmlChar *)timestring);
114
115 /* nick */
116 if (nick) {
117 xmlNewProp(root_node, (const xmlChar *)"nick", (const xmlChar *)nick);
118 }
119
120 /* url */
121 if (url) {
122 xmlNewProp(root_node, (const xmlChar *)"url", (const xmlChar *)url);
123 }
124
125 if (id) {
126 xmlNewProp(root_node, (const xmlChar *)"id", (const xmlChar *)id);
127 }
128
129 if (gen) {
130 xmlNewProp(root_node, (const xmlChar *)"gen", (const xmlChar *)gen);
131 }
132
133 /* action */
134 xmlNewProp(root_node, (const xmlChar *)"action", (const xmlChar *)action);
135
136 /* sheep info */
137 if (sheep_gen > 0 && sheep_id > 0) {
138 /* Create a child node of the root node called sheep */
139 node = xmlNewChild(root_node, NULL, (const xmlChar *)"sheep", NULL);
140
141 /* Create the sheep attributes */
142 sprintf(buffer, "%d", sheep_gen);
143 xmlNewProp(node, (const xmlChar *)"generation", (const xmlChar *)buffer);
144
145 sprintf(buffer, "%d", sheep_id);
146 xmlNewProp(node, (const xmlChar *)"id", (const xmlChar *)buffer);
147 }
148
149 /* Check for the parents */
150 /* If Parent 0 not specified, this is a randomly generated genome. */
151 if (parent0) {
152 if (parent0->edits) {
153 /* Copy the node from the parent */
154 node = xmlDocGetRootElement(parent0->edits);
155 nodecopy = xmlCopyNode(node, 1);
156 xmlNewProp(nodecopy,(const xmlChar *)"filename", (const xmlChar *)parent0->parent_fname);
157 sprintf(buffer,"%d",parent0->genome_index);
158 xmlNewProp(nodecopy,(const xmlChar *)"index", (const xmlChar *)buffer);
159 xmlAddChild(root_node, nodecopy);
160 } else {
161 /* Insert a (parent has no edit) message */
162 nodecopy = xmlNewChild(root_node, NULL, (const xmlChar *)"edit",NULL);
163 xmlNewProp(nodecopy,(const xmlChar *)"filename", (const xmlChar *)parent0->parent_fname);
164 sprintf(buffer,"%d",parent0->genome_index);
165 xmlNewProp(nodecopy,(const xmlChar *)"index", (const xmlChar *)buffer);
166
167 }
168 }
169
170 if (parent1) {
171
172 if (parent1->edits) {
173 /* Copy the node from the parent */
174 node = xmlDocGetRootElement(parent1->edits);
175 nodecopy = xmlCopyNode(node, 1);
176 xmlNewProp(nodecopy,(const xmlChar *)"filename", (const xmlChar *)parent1->parent_fname);
177 sprintf(buffer,"%d",parent1->genome_index);
178 xmlNewProp(nodecopy,(const xmlChar *)"index", (const xmlChar *)buffer);
179 xmlAddChild(root_node, nodecopy);
180 } else {
181 /* Insert a (parent has no edit) message */
182 nodecopy = xmlNewChild(root_node, NULL, (const xmlChar *)"edit",NULL);
183 xmlNewProp(nodecopy,(const xmlChar *)"filename", (const xmlChar *)parent1->parent_fname);
184 sprintf(buffer,"%d",parent1->genome_index);
185 xmlNewProp(nodecopy,(const xmlChar *)"index", (const xmlChar *)buffer);
186 }
187 }
188
189 /* Comment string */
190 /* This one's hard, since we have to treat the comment string as */
191 /* a valid XML document. Create a new document using the comment */
192 /* string as the in-memory document, and then copy all children of */
193 /* the root node into the edit structure */
194 /* Parsing the comment string should be done once and then copied */
195 /* for each call to create_new_editdoc, but that's for later. */
196 if (comment) {
197
198 sprintf(comment_string,"<comm>%s</comm>",comment);
199
200 comment_doc = xmlReadMemory(comment_string, strlen(comment_string), "comment.env", NULL, XML_PARSE_NONET);
201
202 /* Check for errors */
203 if (comment_doc==NULL) {
204 fprintf(stderr, "Failed to parse comment into XML!\n");
205 exit(1);
206 }
207
208 /* Loop through the children of the new document and copy */
209 /* them into the root_node */
210 root_comment = xmlDocGetRootElement(comment_doc);
211
212 for (node=root_comment->children; node; node = node->next) {
213
214 nodecopy = xmlCopyNode(node,1);
215 xmlAddChild(root_node, nodecopy);
216 }
217
218 /* Free the created document */
219 xmlFreeDoc(comment_doc);
220 }
221
222
223 /* return the xml doc */
224 return(doc);
225 }
226
offset(flam3_genome * g)227 void offset(flam3_genome *g) {
228 char *os = getenv("offset");
229 double ox, oy;
230 if (NULL == os) return;
231 sscanf(os, "%lf:%lf", &ox, &oy);
232 g->center[0] += ox / (g->pixels_per_unit * g->spatial_oversample);
233 g->center[1] += oy / (g->pixels_per_unit * g->spatial_oversample);
234 }
235
spin(int frame,double blend,flam3_genome * parent,flam3_genome * templ)236 void spin(int frame, double blend, flam3_genome *parent, flam3_genome *templ)
237 {
238 flam3_genome *result;
239 char action[50];
240 xmlDocPtr doc;
241
242 /* Spin the parent blend*360 degrees */
243 result = sheep_loop(parent,blend);
244
245 /* Apply the template if necessary */
246 if (templ)
247 flam3_apply_template(result, templ);
248
249 /* Set genome parameters accordingly */
250 result->time = (double)frame;
251 result->interpolation = flam3_interpolation_linear;
252 result->palette_interpolation = flam3_palette_interpolation_hsv_circular;
253 result->hsv_rgb_palette_blend = 0.0;
254
255 /* Force linear interpolation - unsure if this is still necessary */
256 /* I believe we put this in so that older clients could render frames */
257 // result->interpolation_type = flam3_inttype_linear;
258
259 /* Create the edit doc xml */
260 sprintf(action,"rotate %g",blend*360.0);
261 doc = create_new_editdoc(action, parent, (flam3_genome *)NULL);
262 result->edits = doc;
263
264 /* Subpixel jitter */
265 offset(result);
266
267 /* Make the name of the flame the time */
268 sprintf(result->flame_name,"%f",result->time);
269
270 /* Print the resulting xml */
271 gprint(result, 1);
272
273 /* Clear out the xml doc */
274 xmlFreeDoc(result->edits);
275
276 /* Clear the result cp */
277 clear_cp(result,flam3_defaults_on);
278
279 /* Free the cp allocated in flam3_sheep_loop */
280 free(result);
281 }
282
spin_inter(int frame,double blend,int seqflag,flam3_genome * parents,flam3_genome * templ)283 void spin_inter(int frame, double blend, int seqflag, flam3_genome *parents, flam3_genome *templ) {
284
285 flam3_genome *result;
286 char action[50];
287 xmlDocPtr doc;
288 char *ai;
289 double stagger = argf("stagger", 0.0);
290
291 /* Interpolate between spun parents */
292 result = sheep_edge(parents, blend, seqflag, stagger);
293
294 /* Unsure why we check for random palettes on both ends... */
295 if ((parents[0].palette_index != flam3_palette_random) &&
296 (parents[1].palette_index != flam3_palette_random)) {
297
298 result->palette_index = flam3_palette_interpolated;
299 result->palette_index0 = parents[0].palette_index;
300 result->hue_rotation0 = parents[0].hue_rotation;
301 result->palette_index1 = parents[1].palette_index;
302 result->hue_rotation1 = parents[1].hue_rotation;
303 result->palette_blend = blend;
304 }
305
306 /* Apply template if necessary */
307 if (templ)
308 flam3_apply_template(result, templ);
309
310 /* Set genome attributes */
311 result->time = (double)frame;
312 // result->interpolation_type = flam3_inttype_linear;
313
314 /* Create the edit doc xml */
315 sprintf(action,"interpolate %g",blend*360.0);
316 doc = create_new_editdoc(action, &parents[0], &parents[1]);
317 result->edits = doc;
318
319 /* Subpixel jitter */
320 offset(result);
321
322 /* Make the name of the flame the time */
323 sprintf(result->flame_name,"%f",result->time);
324
325 /* Print the genome */
326 gprint(result, 1);
327
328 /* Clean up */
329 xmlFreeDoc(result->edits);
330
331 /* Free genome storage */
332 clear_cp(result,flam3_defaults_on);
333 free(result);
334 }
335
truncate_variations(flam3_genome * g,int max_vars,char * action)336 void truncate_variations(flam3_genome *g, int max_vars, char *action) {
337 int i, j, nvars, smallest;
338 double sv=0;
339 char trunc_note[30];
340
341 for (i = 0; i < g->num_xforms; i++) {
342 double d = g->xform[i].density;
343
344 /* if (0.0 < d && d < 0.001) */
345
346 if (d < 0.001 && (g->final_xform_index != i)) {
347 sprintf(trunc_note," trunc_density %d",i);
348 //strcat(action,trunc_note);
349 add_to_action(action,trunc_note);
350 flam3_delete_xform(g, i);
351
352 /* g->xform[i].density = 0.0;
353 } else if (d > 0.0) {
354 */
355 } else {
356 do {
357 nvars = 0;
358 smallest = -1;
359 for (j = 0; j < flam3_nvariations; j++) {
360 double v = g->xform[i].var[j];
361 if (v != 0.0) {
362 nvars++;
363 if (-1 == smallest || fabs(v) < sv) {
364 smallest = j;
365 sv = fabs(v);
366 }
367 }
368 }
369 if (nvars > max_vars) {
370 sprintf(trunc_note," trunc %d %d",i,smallest);
371 //strcat(action,trunc_note);
372 add_to_action(action,trunc_note);
373 g->xform[i].var[smallest] = 0.0;
374 }
375 } while (nvars > max_vars);
376 }
377 }
378 }
379
golden_bit(randctx * rc)380 static double golden_bit(randctx *rc) {
381 return flam3_random_isaac_bit(rc)?0.38196:0.61804;
382 }
383
print_find_parents(xmlNode * node,int last,int level)384 static void print_find_parents(xmlNode *node, int last, int level) {
385 xmlAttrPtr att_ptr, cur_att;
386 xmlNodePtr chld_ptr=NULL, cur_chld=NULL;
387 xmlNode *this_node;
388 int next_last;
389 //for (i = 0; i < level; i++)
390 // fprintf(stdout, "+");
391 // fprintf(stdout, "pfp %d ", last);
392 for (this_node=node; this_node; this_node = this_node->next) {
393 if (this_node->type == XML_ELEMENT_NODE) {
394 // fprintf(stdout, "nname=%s ", this_node->name);
395 if (!xmlStrcmp(this_node->name, (const xmlChar *)"edit")) {
396 att_ptr = node->properties;
397 next_last = 0;
398 if (last) {
399 char *pgen = NULL, *pid = NULL;
400 for (cur_att = att_ptr; cur_att; cur_att = cur_att->next) {
401 if (!xmlStrcmp(cur_att->name, (const xmlChar *)"gen")) {
402 char *att_str = (char *) xmlGetProp(node,cur_att->name);
403 pgen = att_str;
404 }
405 if (!xmlStrcmp(cur_att->name, (const xmlChar *)"id")) {
406 char *att_str = (char *) xmlGetProp(node,cur_att->name);
407 pid = att_str;
408 }
409 }
410 if (pgen) printf("GEN=%s ", pgen);
411 if (pid) printf("ID=%s", pid);
412 if (pid || pgen)
413 printf("\n");
414 } else {
415 for (cur_att = att_ptr; cur_att; cur_att = cur_att->next) {
416 char *att_str = (char *) xmlGetProp(node,cur_att->name);
417 // fprintf(stdout, "name=%s val=%s ", cur_att->name, att_str);
418 if (!xmlStrcmp(cur_att->name, (const xmlChar *)"action")) {
419 if (!strncmp(att_str, "cross", 5) || !strncmp(att_str, "mutate", 6)) {
420 next_last = 1;
421 }
422 }
423 }
424 // fprintf(stdout, "\n");
425 chld_ptr = node->children;
426 for (cur_chld=chld_ptr; cur_chld; cur_chld = cur_chld->next)
427 print_find_parents(cur_chld, next_last, level + 1);
428 }
429 }
430 }
431 return;
432 }
433 }
434
435 int
main(argc,argv)436 main(argc, argv)
437 int argc;
438 char **argv;
439 {
440 int debug = 0;
441 int count;
442 char *ai;
443 unsigned char *image;
444 flam3_genome *templ = NULL;
445 flam3_genome cp_orig, cp_save;
446 int i, j;
447 double avg_pix, fraction_black, fraction_white;
448 double avg_thresh = argf("avg", 20.0);
449 double black_thresh = argf("black", 0.01);
450 double white_limit = argf("white", 0.05);
451 int nframes = argi("nframes", 100);
452 int sym = argi("symmetry", 0);
453 int enclosed = argi("enclosed", 1);
454 char *clone = getenv("clone");
455 char *clone_all = getenv("clone_all");
456 char *animate = getenv("animate");
457 char *mutate = getenv("mutate");
458 char *cross0 = getenv("cross0");
459 char *cross1 = getenv("cross1");
460 char *method = getenv("method");
461 char *inter = getenv("inter");
462 char *find_parents = getenv("find_parents");
463 char *rotate = getenv("rotate");
464 char *strip = getenv("strip");
465 char *sequence = getenv("sequence");
466 int loops = argi("loops", 1);
467 int frame = argi("frame", 0);
468 int rep, repeat = argi("repeat", 1);
469 double speed = argf("speed", 0.1);
470 int bits = argi("bits", 33);
471 int ntries = argi("tries", 10);
472 char *use_vars = getenv("use_vars");
473 char *dont_use_vars = getenv("dont_use_vars");
474 flam3_genome *parent0=NULL, *parent1=NULL;
475 flam3_genome selp0, selp1;
476 flam3_genome *aselp0, *aselp1;
477 int parent0_n, parent1_n;
478 int num_threads = 1;
479 flam3_genome *cp;
480 int ncp;
481
482 int ivars[max_specified_vars];
483 int novars[max_specified_vars];
484 int num_ivars = 0;
485 int num_novars = 0;
486 char *var_tok;
487
488 flam3_frame f;
489 char action[flam3_max_action_length];
490
491 stat_struct stats;
492
493
494
495 #ifdef WIN32
496
497 char *slashloc;
498 char exepath[256];
499 char palpath[256];
500 memset(exepath,0,256);
501 memset(palpath,0,256);
502 slashloc = strrchr(argv[0],'\\');
503 if (NULL==slashloc) {
504 sprintf(palpath,"flam3_palettes=flam3-palettes.xml");
505 } else {
506 strncpy(exepath,argv[0],slashloc-argv[0]+1);
507 sprintf(palpath,"flam3_palettes=%sflam3-palettes.xml",exepath);
508 }
509 putenv(palpath);
510
511 #endif
512
513 if (argc>1) {
514 if (strcmp("--version",argv[1])==0) {
515 printf("FLAM3-%s\n",flam3_version());
516 exit(0);
517 } else {
518 printf("unrecognized option %s, aborting.\n",argv[1]);
519 exit(-1);
520 }
521 }
522
523 verbose = argi("verbose", 0);
524
525 memset(&cp_orig, 0, sizeof(flam3_genome));
526 memset(&cp_save, 0, sizeof(flam3_genome));
527 memset(&selp0, 0, sizeof(flam3_genome));
528 memset(&selp1, 0, sizeof(flam3_genome));
529
530 if (1 != argc) {
531 docstring();
532 exit(0);
533 }
534
535 /* Init random number generators */
536 flam3_init_frame(&f);
537 flam3_srandom();
538
539 //f.temporal_filter_radius = 0.0;
540 f.bits = bits;
541 f.bytes_per_channel = 1;
542 f.earlyclip = 1;
543 f.verbose = 0;
544 f.genomes = &cp_orig;
545 f.ngenomes = 1;
546 f.pixel_aspect_ratio = 1.0;
547 f.progress = 0;
548 f.nthreads = num_threads;
549 f.sub_batch_size = 10000;
550 test_cp(&cp_orig); // just for the width & height
551
552 /* Are the variations to be used specified? */
553 if (use_vars && dont_use_vars) {
554 fprintf(stderr,"use_vars and dont_use_vars cannot both be specified. Terminating.\n");
555 exit(-1);
556 }
557
558 /* Specify reasonable defaults if nothing is specified */
559 if (!use_vars && !dont_use_vars) {
560 novars[num_novars++] = VAR_NOISE;
561 novars[num_novars++] = VAR_BLUR;
562 novars[num_novars++] = VAR_GAUSSIAN_BLUR;
563 novars[num_novars++] = VAR_RADIAL_BLUR;
564 novars[num_novars++] = VAR_NGON;
565 novars[num_novars++] = VAR_SQUARE;
566 novars[num_novars++] = VAR_RAYS;
567 novars[num_novars++] = VAR_CROSS;
568 novars[num_novars++] = VAR_PRE_BLUR;
569 novars[num_novars++] = VAR_SEPARATION;
570 novars[num_novars++] = VAR_SPLIT;
571 novars[num_novars++] = VAR_SPLITS;
572
573
574
575 /* Loop over the novars and set ivars to the complement */
576 for (i=0;i<flam3_nvariations;i++) {
577 for (j=0;j<num_novars;j++) {
578 if (novars[j] == i)
579 break;
580 }
581 if (j==num_novars)
582 ivars[num_ivars++] = i;
583 }
584
585 } else {
586
587 if (use_vars) {
588 /* Parse comma-separated list of variations to use */
589 var_tok = strtok(use_vars,",");
590 ivars[num_ivars++] = atoi(var_tok);
591 while(1) {
592 var_tok = strtok(NULL,",");
593
594 if (var_tok==NULL)
595 break;
596
597 ivars[num_ivars++] = atoi(var_tok);
598
599 if (num_ivars==max_specified_vars) {
600 fprintf(stderr,"Maximum number of user-specified variations exceeded. Truncating.\n");
601 break;
602 }
603 }
604
605 /* Error checking */
606 for (i=0;i<num_ivars;i++) {
607 if (ivars[i]<0 || ivars[i]>=flam3_nvariations) {
608 fprintf(stderr,"specified variation list includes bad value. (%d)\n",ivars[i]);
609 exit(1);
610 }
611 }
612 } else if (dont_use_vars) {
613 /* Parse comma-separated list of variations NOT to use */
614 var_tok = strtok(dont_use_vars,",");
615 novars[num_novars++] = atoi(var_tok);
616 while(1) {
617 var_tok = strtok(NULL,",");
618
619 if (var_tok==NULL)
620 break;
621
622 novars[num_novars++] = atoi(var_tok);
623
624 if (num_novars==max_specified_vars) {
625 fprintf(stderr,"Maximum number of user-specified variations exceeded. Truncating.\n");
626 break;
627 }
628 }
629
630 /* Loop over the novars and set ivars to the complement */
631 for (i=0;i<flam3_nvariations;i++) {
632 for (j=0;j<num_novars;j++) {
633 if (novars[j] == i)
634 break;
635 }
636 if (j==num_novars)
637 ivars[num_ivars++] = i;
638 }
639 }
640 }
641
642 if (1 < (!!mutate + !!(cross0 || cross1) +
643 !!inter + !!rotate + !!clone + !!strip )) {
644 fprintf(stderr,
645 "can only specify one of mutate, clone, cross, rotate, strip, or inter.\n");
646 exit(1);
647 }
648
649 if ( (!cross0) ^ (!cross1) ) {
650 fprintf(stderr, "must specify both crossover arguments.\n");
651 exit(1);
652 }
653
654 if (method && (!cross0 && !mutate)) {
655 fprintf(stderr, "cannot specify method unless doing crossover or mutate.\n");
656 exit(1);
657 }
658
659 if (getenv("template")) {
660 char *tf = getenv("template");
661
662 templ = string_to_cp(tf, &ncp, flam3_defaults_off);
663 if (1 < ncp) {
664 fprintf(stderr, "more than one control point in template, "
665 "ignoring all but first.\n");
666 } else if (0 == ncp) {
667 fprintf(stderr, "no control points in template.\n");
668 exit(1);
669 }
670
671 }
672
673 /* Methods for genetic manipulation begin here */
674
675 if (clone_all) {
676
677 cp = string_to_cp(clone_all, &ncp, flam3_defaults_on);
678
679 printf("<clone_all version=\"FLAM3-%s\">\n", flam3_version());
680 for (i = 0; i < ncp; i++) {
681 if (templ) flam3_apply_template(&cp[i], templ);
682 offset(&cp[i]);
683 gprint(&cp[i], 1);
684 }
685 printf("</clone_all>\n");
686
687 exit(0);
688 }
689
690 if (animate) {
691 flam3_genome interpolated;
692 int first_frame,last_frame;
693 int ftime,iscp;
694 double stagger = argf("stagger", 0.0);
695 cp = string_to_cp(animate, &ncp, flam3_defaults_on);
696
697
698 for (i = 0; i < ncp; i++) {
699 if (i > 0 && cp[i].time <= cp[i-1].time) {
700 fprintf(stderr, "error: control points must be sorted by time, but %g <= %g, index %d.\n",
701 cp[i].time, cp[i-1].time, i);
702 exit(1);
703 }
704 /* Strip out all motion elements here */
705 for (j=0;j<cp[i].num_xforms;j++)
706 flam3_delete_motion_elements(&cp[i].xform[j]);
707
708 }
709
710 if (!getenv("begin"))
711 first_frame = (int) cp[0].time;
712 else
713 first_frame = argi("begin",0);
714
715 if (!getenv("end"))
716 last_frame = (int) cp[ncp-1].time;
717 else
718 last_frame = argi("end",0);
719
720 if (last_frame < first_frame) last_frame = first_frame;
721
722 printf("<animate version=\"FLAM3-%s\">\n", flam3_version());
723
724 for (ftime = first_frame; ftime <= last_frame; ftime += 1) {
725 iscp=0;
726 for (i=0;i<ncp;i++) {
727 if ( ftime==cp[i].time ) {
728 flam3_copy(&interpolated, &(cp[i]) );
729 iscp=1;
730 }
731 }
732 if (iscp==0) {
733 flam3_interpolate(cp, ncp, (double)ftime, stagger, &interpolated);
734 for (i=0;i<ncp;i++) {
735 if ( ftime==cp[i].time-1 ) {
736 iscp=1;
737 }
738 }
739 if (iscp==0)
740 interpolated.interpolation_type = flam3_inttype_linear;
741 }
742
743 if (templ) flam3_apply_template(&interpolated, templ);
744 gprint(&interpolated, 1);
745 }
746 printf("</animate>\n");
747 exit(0);
748 }
749
750
751 if (sequence) {
752 double blend;
753 int seqflag;
754 int framecount;
755
756 if (nframes <= 0) {
757 fprintf(stderr, "nframes must be positive, not %d.\n", nframes);
758 exit(1);
759 }
760
761 cp = string_to_cp(sequence, &ncp, flam3_defaults_on);
762
763 if (enclosed) printf("<sequence version=\"FLAM3-%s\">\n", flam3_version());
764 framecount = 0;
765 #if 1
766 for (i = 0; i < ncp; i++) {
767 if (loops) {
768 for (frame = 0; frame < nframes; frame++) {
769 blend = frame/(double)nframes;
770 spin(framecount++, blend, &cp[i], templ);
771 }
772 }
773 if (i < ncp-1)
774 for (frame = 0; frame < nframes; frame++) {
775 if (0==frame || nframes-1==frame)
776 seqflag=1;
777 else
778 seqflag=0;
779 blend = frame/(double)nframes;
780 spin_inter(framecount++, blend, seqflag, &cp[i], templ);
781 }
782 }
783 spin(framecount, 0.0, &cp[ncp-1], templ);
784 #else
785 if (1) {
786 flam3_genome res;
787 memset(&res, 0, sizeof(flam3_genome));
788 res.final_xform_index = -1;
789 flam3_add_xforms(&res, cp[0].num_xforms, 0);
790
791 if (ncp < 4) {
792 fprintf(stderr, "catmull-rom requires 4 or more control points.\n");
793 exit(1);
794 }
795 for (i = 0; i < ncp - 3; i++) {
796 for (frame = 0; frame < nframes; frame++) {
797 blend = frame/(double)nframes;
798 interpolate_catmull_rom(cp+i, blend, &res);
799 res.time = frame + i * nframes;
800 gprint(&res, 0);
801 fflush(stdout);
802 }
803 }
804 }
805 #endif
806 if (enclosed) printf("</sequence>\n");
807 exit(0);
808 }
809
810 if (inter || rotate) {
811
812 double blend, spread;
813 char *fname = inter?inter:rotate;
814 int ni;
815
816 if (nframes <= 0) {
817 fprintf(stderr, "nframes must be positive, not %d.\n", nframes);
818 exit(1);
819 }
820
821 blend = frame/(double)nframes;
822 spread = 1.0/nframes;
823
824 cp = string_to_cp(fname, &ncp, flam3_defaults_on);
825
826 if (enclosed) printf("<pick version=\"FLAM3-%s\">\n", flam3_version());
827 if (rotate) {
828 if (1 != ncp) {
829 fprintf(stderr, "rotation requires one control point, not %d.\n", ncp);
830 exit(1);
831 }
832 spin(frame - 1, blend - spread, cp, templ);
833 spin(frame , blend , cp, templ);
834 spin(frame + 1, blend + spread, cp, templ);
835 } else {
836 if (2 != ncp) {
837 fprintf(stderr, "interpolation requires two control points, not %d.\n", ncp);
838 exit(1);
839 }
840 spin_inter(frame - 1, blend - spread, 0, cp, templ);
841 spin_inter(frame , blend , 0, cp, templ);
842 spin_inter(frame + 1, blend + spread, 0, cp, templ);
843 }
844 if (enclosed) printf("</pick>\n");
845
846 for (ni=0;ni<ncp;ni++) {
847 xmlFreeDoc(cp[ni].edits);
848 clear_cp(&cp[ni],flam3_defaults_on);
849 }
850 free(cp);
851
852 exit(0);
853 }
854
855 if (strip) {
856
857 cp = string_to_cp(strip, &ncp, flam3_defaults_on);
858
859 if (enclosed) printf("<pick version=\"FLAM3-%s\">\n", flam3_version());
860
861 for (i = 0; i < ncp; i++) {
862 double old_center[2];
863
864 /* Strip out motion elements */
865 for (j=0;j<cp[i].num_xforms;j++)
866 flam3_delete_motion_elements(&cp[i].xform[j]);
867
868 old_center[0] = cp[i].center[0];
869 old_center[1] = cp[i].center[1];
870 cp[i].height /= nframes;
871 cp[i].center[1] = cp[i].center[1] - ((nframes - 1) * cp[i].height) /
872 (2 * cp[i].pixels_per_unit * pow(2.0, cp[i].zoom));
873 cp[i].center[1] += cp[i].height * frame / ( cp[i].pixels_per_unit * pow(2.0,cp[i].zoom) );
874 rotate_by(cp[i].center, old_center, cp[i].rotate);
875
876 if (templ) flam3_apply_template(&cp[i], templ);
877 offset(&cp[i]);
878 gprint(&cp[i], 1);
879 }
880
881 if (enclosed) printf("</pick>\n");
882 exit(0);
883 }
884
885 if (find_parents) {
886 cp = string_to_cp(find_parents, &ncp, flam3_defaults_on);
887 if (1 != ncp) {
888 fprintf(stderr, "can only find parents of one genome\n");
889 exit(1);
890 }
891 xmlDocPtr edits = cp[0].edits;
892 xmlNode *rootnode = xmlDocGetRootElement(edits);
893 print_find_parents(rootnode, 0, 0);
894 exit(0);
895 }
896
897 /* pick a control point until it looks good enough */
898 if (repeat <= 0) {
899 fprintf(stderr, "repeat must be positive, not %d.\n", repeat);
900 exit(1);
901 }
902
903 if (enclosed) printf("<pick version=\"FLAM3-%s\">\n", flam3_version());
904 image = (unsigned char *) malloc(3 * cp_orig.width * cp_orig.height);
905
906 for (rep = 0; rep < repeat; rep++) {
907
908 if (verbose)
909 fprintf(stderr, "flame = %d/%d..", rep+1, repeat);
910
911 count = 0;
912
913 if (clone) {
914
915 parent0 = string_to_cp(clone, &parent0_n, flam3_defaults_on);
916 /* Action is 'clone' with trunc_vars concat */
917 sprintf(action,"clone");
918
919 if (getenv("clone_action"))
920 sprintf(action,"clone %s", getenv("clone_action"));
921
922 flam3_copy(&selp0, &(parent0[random()%parent0_n]));
923 flam3_copy(&cp_save, &selp0);
924 aselp0 = &selp0;
925 aselp1 = NULL;
926 truncate_variations(&cp_save, 5, action);
927
928 cp_save.edits = create_new_editdoc(action, aselp0, aselp1);
929
930 } else {
931 int did_color;
932
933 do {
934
935 int random_mode=0;
936
937 if (verbose) fprintf(stderr, ".");
938 did_color = 0;
939 f.time = (double) 0.0;
940 action[0] = 0;
941
942 if (mutate) {
943 int mutmeth;
944
945 parent0 = string_to_cp(mutate, &parent0_n, flam3_defaults_on);
946 flam3_copy(&selp0, &(parent0[((unsigned)irand(&f.rc))%parent0_n]));
947 flam3_copy(&cp_orig, &selp0);
948 aselp0 = &selp0;
949 aselp1 = NULL;
950
951 if (NULL == getenv("method"))
952 mutmeth = MUTATE_NOT_SPECIFIED;
953 else if (!strcmp(method,"all_vars"))
954 mutmeth = MUTATE_ALL_VARIATIONS;
955 else if (!strcmp(method,"one_xform"))
956 mutmeth = MUTATE_ONE_XFORM_COEFS;
957 else if (!strcmp(method,"add_symmetry"))
958 mutmeth = MUTATE_ADD_SYMMETRY;
959 else if (!strcmp(method,"post_xforms"))
960 mutmeth = MUTATE_POST_XFORMS;
961 else if (!strcmp(method,"color_palette"))
962 mutmeth = MUTATE_COLOR_PALETTE;
963 else if (!strcmp(method,"delete_xform"))
964 mutmeth = MUTATE_DELETE_XFORM;
965 else if (!strcmp(method,"all_coefs"))
966 mutmeth = MUTATE_ALL_COEFS;
967 else {
968 fprintf(stderr,"method '%s' not defined for mutate. defaulting to random.\n",method);
969 mutmeth = MUTATE_NOT_SPECIFIED;
970 }
971
972 flam3_mutate(&cp_orig, mutmeth, ivars, num_ivars, sym, speed, &f.rc, action);
973
974 /* Scan string returned for 'mutate color' */
975 if ( strstr(action,"mutate color") )
976 did_color = 1;
977
978 if (cp_orig.flame_name[0]) {
979 char tm[flam3_name_len+1];
980 strncpy(tm, cp_orig.flame_name, flam3_name_len);
981 snprintf(cp_orig.flame_name, flam3_name_len, "mutation %d of %s", rep, tm);
982 }
983
984 } else if (cross0) {
985 int i0, i1;
986 int crossmeth;
987
988 parent0 = string_to_cp(cross0, &parent0_n, flam3_defaults_on);
989 parent1 = string_to_cp(cross1, &parent1_n, flam3_defaults_on);
990
991 i0 = ((unsigned)irand(&f.rc))%parent0_n;
992 i1 = ((unsigned)irand(&f.rc))%parent1_n;
993
994 flam3_copy(&selp0, &(parent0[i0]));
995 flam3_copy(&selp1, &(parent1[i1]));
996
997 aselp0 = &selp0;
998 aselp1 = &selp1;
999
1000 if (NULL == getenv("method"))
1001 crossmeth = CROSS_NOT_SPECIFIED;
1002 else if (!strcmp(method,"union"))
1003 crossmeth = CROSS_UNION;
1004 else if (!strcmp(method,"interpolate"))
1005 crossmeth = CROSS_INTERPOLATE;
1006 else if (!strcmp(method,"alternate"))
1007 crossmeth = CROSS_ALTERNATE;
1008 else {
1009 fprintf(stderr,"method '%s' not defined for cross. defaulting to random.\n",method);
1010 crossmeth = CROSS_NOT_SPECIFIED;
1011 }
1012
1013 flam3_cross(&parent0[i0], &parent1[i1], &cp_orig, crossmeth, &f.rc, action);
1014
1015 if (parent0[i0].flame_name[0] || parent1[i1].flame_name[0]) {
1016 snprintf(cp_orig.flame_name, flam3_name_len, "%d of %s x %s",
1017 rep, parent0[i0].flame_name, parent1[i1].flame_name);
1018 }
1019
1020 } else {
1021 sprintf(action,"random");
1022 random_mode=1;
1023 flam3_random(&cp_orig, ivars, num_ivars, sym, 0);
1024
1025
1026 aselp0 = NULL;
1027 aselp1 = NULL;
1028 }
1029
1030 /* Adjust bounding box half the time */
1031 if (flam3_random_bit(&f.rc) || random_mode) {
1032 double bmin[2], bmax[2];
1033 flam3_estimate_bounding_box(&cp_orig, 0.01, 100000, bmin, bmax, &f.rc);
1034 if (flam3_random_isaac_01(&f.rc) < 0.3) {
1035 cp_orig.center[0] = (bmin[0] + bmax[0]) / 2.0;
1036 cp_orig.center[1] = (bmin[1] + bmax[1]) / 2.0;
1037 add_to_action(action," recentered");
1038 } else {
1039 double mix0, mix1;
1040 if (flam3_random_isaac_bit(&f.rc)) {
1041 mix0 = golden_bit(&f.rc) + flam3_random_isaac_11(&f.rc)/5;
1042 mix1 = golden_bit(&f.rc);
1043 add_to_action(action," reframed0");
1044 } else if (flam3_random_isaac_bit(&f.rc)) {
1045 mix0 = golden_bit(&f.rc);
1046 mix1 = golden_bit(&f.rc) + flam3_random_isaac_11(&f.rc)/5;
1047 add_to_action(action," reframed1");
1048 } else {
1049 mix0 = golden_bit(&f.rc) + flam3_random_isaac_11(&f.rc)/5;
1050 mix1 = golden_bit(&f.rc) + flam3_random_isaac_11(&f.rc)/5;
1051 add_to_action(action," reframed2");
1052 }
1053 cp_orig.center[0] = mix0 * bmin[0] + (1-mix0)*bmax[0];
1054 cp_orig.center[1] = mix1 * bmin[1] + (1-mix1)*bmax[1];
1055 }
1056 cp_orig.rot_center[0] = cp_orig.center[0];
1057 cp_orig.rot_center[1] = cp_orig.center[1];
1058 cp_orig.pixels_per_unit = cp_orig.width / (bmax[0] - bmin[0]);
1059 }
1060
1061 truncate_variations(&cp_orig, 5, action);
1062
1063 if (!did_color && random()&1) {
1064 if (debug)
1065 fprintf(stderr,"improving colors...\n");
1066
1067 flam3_improve_colors(&cp_orig, 100, 0, 10);
1068 //strcat(action," improved colors");
1069 add_to_action(action," improved colors");
1070 }
1071
1072 cp_orig.edits = create_new_editdoc(action, aselp0, aselp1);
1073 flam3_copy(&cp_save, &cp_orig);
1074 test_cp(&cp_orig);
1075 if (flam3_render(&f, image, flam3_field_both, 3, 0, &stats)) {
1076 fprintf(stderr,"error rendering test image: aborting.\n");
1077 exit(1);
1078 }
1079
1080 if (1) {
1081 int n, tot, totb, totw;
1082 n = cp_orig.width * cp_orig.height;
1083 tot = 0;
1084 totb = 0;
1085 totw = 0;
1086 for (i = 0; i < 3*n; i+=3) {
1087
1088 tot += (image[i]+image[i+1]+image[i+2]);
1089 if (0 == image[i] && 0 == image[i+1] && 0 == image[i+2]) totb++;
1090 if (255 == image[i] && 255 == image[i+1] && 255 == image[i+2]) totw++;
1091
1092 // printf("%d ", image[i]);
1093 }
1094
1095 avg_pix = (tot / (double)(3*n));
1096 fraction_black = totb / (double)n;
1097 fraction_white = totw / (double)n;
1098
1099 if (debug)
1100 fprintf(stderr,
1101 "avg_pix=%g fraction_black=%g fraction_white=%g n=%g\n",
1102 avg_pix, fraction_black, fraction_white, (double)n);
1103
1104 } else {
1105 avg_pix = avg_thresh + 1.0;
1106 fraction_black = black_thresh + 1.0;
1107 fraction_white = white_limit - 1.0;
1108 }
1109
1110 clear_cp(&cp_orig,flam3_defaults_on);
1111
1112 count++;
1113 } while ((avg_pix < avg_thresh ||
1114 fraction_black < black_thresh ||
1115 fraction_white > white_limit) &&
1116 count < ntries);
1117
1118 if (ntries == count) {
1119 fprintf(stderr, "warning: reached maximum attempts, giving up.\n");
1120 }
1121
1122 }
1123
1124 if (templ)
1125 flam3_apply_template(&cp_save,templ);
1126
1127 cp_save.time = (double)rep;
1128
1129 if (1) {
1130 char *maxforms = getenv("maxforms");
1131 if (maxforms && atoi(maxforms)) {
1132 cp_save.symmetry = 0;
1133 while (cp_save.num_xforms > atoi(maxforms))
1134 flam3_delete_xform(&cp_save, cp_save.num_xforms - 1);
1135 }
1136 }
1137
1138 gprint(&cp_save, 1);
1139 fflush(stdout);
1140
1141 /* Free created documents */
1142 /* (Only free once, since the copy is a ptr to the original) */
1143 xmlFreeDoc(cp_save.edits);
1144 clear_cp(&cp_save,0);
1145
1146 if (verbose) {
1147 fprintf(stderr, "\ndone. action = %s\n", action);
1148 }
1149
1150 }
1151 if (enclosed) printf("</pick>\n");
1152 free(image);
1153
1154 return 0;
1155 }
1156