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 "parser.h"
20 #include "interpolation.h"
21 #include "filters.h"
22 #include <errno.h>
23 
24 static int flam3_conversion_failed;
25 
flam3_atoi(char * nstr)26 int flam3_atoi(char *nstr) {
27 
28    /* Note that this is NOT thread-safe, but simplifies things significantly. */
29    int res;
30    char *endp;
31 
32    /* Reset errno */
33    errno=0;
34 
35    /* Convert the string using strtol */
36    res = strtol(nstr, &endp, 10);
37 
38    /* Check errno & return string */
39    if (endp!=nstr+strlen(nstr)) {
40       flam3_conversion_failed = 1;
41       fprintf(stderr,"flam3_atoi : Error converting :%s: extra chars\n",nstr);
42    }
43    if (errno) {
44       flam3_conversion_failed = 1;
45       fprintf(stderr,"flam3_atoi : Error converting :%s:\n",nstr);
46    }
47    return(res);
48 }
49 
flam3_atof(char * nstr)50 double flam3_atof(char *nstr) {
51 
52    /* Note that this is NOT thread-safe, but simplifies things significantly. */
53    double res;
54    char *endp;
55 
56    /* Reset errno */
57    errno=0;
58 
59    /* Convert the string using strtod */
60    res = strtod(nstr, &endp);
61 
62    /* Check errno & return string */
63    if (endp!=nstr+strlen(nstr)) {
64       flam3_conversion_failed = 1;
65       fprintf(stderr,"flam3_atof: Error converting :%s: extra chars\n",nstr);
66    }
67    if (errno) {
68       flam3_conversion_failed = 1;
69       fprintf(stderr,"flam3_atof: Error converting :%s:\n",nstr);
70    }
71    return(res);
72 }
73 
var2n(const char * s)74 int var2n(const char *s) {
75    int i;
76 
77    for (i = 0; i < flam3_nvariations; i++)
78       if (!strcmp(s, flam3_variation_names[i])) return i;
79 
80    return flam3_variation_none;
81 }
82 
flam3_parse_hexformat_colors(char * colstr,flam3_genome * cp,int numcolors,int chan)83 int flam3_parse_hexformat_colors(char *colstr, flam3_genome *cp, int numcolors, int chan) {
84 
85    int c_idx=0;
86    int col_count=0;
87    int r,g,b,a;
88    int sscanf_ret;
89    char tmps[2];
90    int skip = (int)fabs(chan);
91 
92    /* Strip whitespace prior to first color */
93    while (isspace( (int)colstr[c_idx]))
94       c_idx++;
95 
96    do {
97 
98       /* Parse an RGB triplet at a time... */
99       a = 255;
100       if (chan==3)
101          sscanf_ret = sscanf(&(colstr[c_idx]),"%2x%2x%2x",&r,&g,&b);
102       else if (chan==-4)
103          sscanf_ret = sscanf(&(colstr[c_idx]),"00%2x%2x%2x",&r,&g,&b);
104       else // chan==4
105          sscanf_ret = sscanf(&(colstr[c_idx]),"%2x%2x%2x%2x",&r,&g,&b,&a);
106 
107       if ((chan!=4 && sscanf_ret != 3) || (chan==4 && sscanf_ret != 4)) {
108          fprintf(stderr, "Error:  Problem reading hexadecimal color data.\n");
109          return(1);
110       }
111 
112       c_idx += 2*skip;
113 
114       while (isspace( (int)colstr[c_idx]))
115          c_idx++;
116 
117       cp->palette[col_count].color[0] = r / 255.0;
118       cp->palette[col_count].color[1] = g / 255.0;
119       cp->palette[col_count].color[2] = b / 255.0;
120       cp->palette[col_count].color[3] = a / 255.0;
121       cp->palette[col_count].index = col_count;
122 
123       col_count++;
124 
125    } while (col_count<numcolors);
126 
127    if (sscanf(&(colstr[c_idx]),"%1s",tmps)>0) {
128       fprintf(stderr,"error: extra data at end of hex color data '%s'\n",&(colstr[c_idx]));
129       return(1);
130    }
131 
132    return(0);
133 }
134 
flam3_interp_missing_colors(flam3_genome * cp)135 int flam3_interp_missing_colors(flam3_genome *cp) {
136 
137     /* Check for a non-full palette */
138     int minix,maxix;
139     int colorli,colorri;
140     int wrapmin,wrapmax;
141     int intl, intr;
142     int str,enr;
143     int i,j,k;
144     double prcr;
145 
146     minix = 0;
147     for (i=0; i<256; i++) {
148         if (cp->palette[i].index >= 0) {
149             minix = i;
150             break;
151         }
152     }
153 
154     if (i==256) {
155         /* No colors.  Set all indices properly. */
156         for (i=0;i<256;i++)
157             cp->palette[i].index = i;
158         return(1);
159     }
160 
161     wrapmin = minix + 256;
162 
163     maxix = 255;
164     for (i=255;i>=0;i--) {
165         if (cp->palette[i].index >= 0) {
166             maxix = i;
167             break;
168         }
169     }
170 
171     wrapmax = maxix - 256;
172 
173     /* Loop over the indices looking for negs */
174     i = 0;
175     while(i<256) {
176 
177         if (cp->palette[i].index < 0) {
178             /* Start of a range of negs */
179             str = i;
180             intl = i-1;
181             intr = i+1;
182             colorli = intl;
183             colorri = intr;
184             while (cp->palette[i].index<0 && i<256) {
185                 enr = i;
186                 intr = i+1;
187                 colorri = intr;
188                 i++;
189             }
190 
191             if (intl==-1) {
192                 intl = wrapmax;
193                 colorli = maxix;
194             }
195 
196             if (intr==256) {
197                 intr = wrapmin;
198                 colorri = minix;
199             }
200 
201             for (j=str;j<=enr;j++) {
202 
203                 prcr = (j-intl)/(double)(intr-intl);
204 
205                 for (k=0;k<=3;k++)
206                     cp->palette[j].color[k] = cp->palette[colorli].color[k] * (1.0-prcr) + cp->palette[colorri].color[k] * prcr;
207 
208                 cp->palette[j].index = j;
209             }
210 
211             i = colorri+1;
212         } else
213             i ++;
214     }
215 
216     return(0);
217 }
218 
219 
scan_for_flame_nodes(xmlNode * cur_node,char * parent_file,int default_flag,flam3_genome ** all_cps,int * all_ncps)220 void scan_for_flame_nodes(xmlNode *cur_node, char *parent_file, int default_flag, flam3_genome **all_cps, int *all_ncps) {
221 
222    xmlNode *this_node = NULL;
223    flam3_genome loc_current_cp;
224    flam3_genome *genome_storage = *all_cps; /* To simplify semantics */
225    size_t f3_storage;
226    int pfe_success;
227    int col_success;
228 
229    memset(&loc_current_cp,0,sizeof(flam3_genome));
230 
231    /* Loop over this level of elements */
232    for (this_node=cur_node; this_node; this_node = this_node->next) {
233 
234       /* Check to see if this element is a <flame> element */
235       if (this_node->type == XML_ELEMENT_NODE && !xmlStrcmp(this_node->name, (const xmlChar *)"flame")) {
236 
237          /* This is a flame element.  Parse it. */
238          clear_cp(&loc_current_cp, default_flag);
239 
240          pfe_success = parse_flame_element(this_node,&loc_current_cp);
241 
242          if (pfe_success>0) {
243             fprintf(stderr,"error parsing flame element\n");
244             all_cps = NULL; /* leaks memory but terminates */
245             /* !!! free all_cp properly !!! */
246             *all_ncps = 0;
247             return;
248          }
249 
250          /* Copy this cp into the array */
251          f3_storage = (1+*all_ncps)*sizeof(flam3_genome);
252          genome_storage = realloc(genome_storage, f3_storage);
253 
254          /* Must set value of pointer to new storage location */
255          *all_cps = genome_storage;
256 
257          /* Clear out the realloc'd memory */
258          memset(&(genome_storage[*all_ncps]),0,sizeof(flam3_genome));
259 
260          if (loc_current_cp.palette_index != -1) {
261             col_success = flam3_get_palette(loc_current_cp.palette_index, loc_current_cp.palette,
262                loc_current_cp.hue_rotation);
263             if (col_success < 0)
264                fprintf(stderr,"error retrieving palette %d, setting to all white\n",loc_current_cp.palette_index);
265          }
266 
267          col_success = flam3_interp_missing_colors(&loc_current_cp);
268 
269          loc_current_cp.genome_index = *all_ncps;
270          memset(loc_current_cp.parent_fname, 0, flam3_parent_fn_len);
271          strncpy(loc_current_cp.parent_fname,parent_file,flam3_parent_fn_len-1);
272 
273          flam3_copy(&(genome_storage[*all_ncps]), &loc_current_cp);
274          (*all_ncps) ++;
275 
276       } else {
277          /* Check all of the children of this element */
278          scan_for_flame_nodes(this_node->children, parent_file, default_flag, all_cps, all_ncps);
279       }
280    }
281 
282    /* Clear the cp (frees allocated memory) */
283    clear_cp(&loc_current_cp, default_flag);
284 
285 }
286 
287 
parse_flame_element(xmlNode * flame_node,flam3_genome * loc_current_cp)288 int parse_flame_element(xmlNode *flame_node, flam3_genome *loc_current_cp) {
289    flam3_genome *cp = loc_current_cp;
290    xmlNode *chld_node, *motion_node;
291    xmlNodePtr edit_node;
292    xmlAttrPtr att_ptr, cur_att;
293    int solo_xform=-1;
294    char *att_str;
295    int num_std_xforms=-1;
296    char tmps[2];
297    int i;
298    flam3_xform tmpcpy;
299    flam3_chaos_entry *xaos=NULL;
300    int num_xaos=0;
301 
302    /* Reset the conversion error flag */
303    /* NOT threadsafe                  */
304    flam3_conversion_failed=0;
305 
306    /* Store this flame element in the current cp */
307 
308    /* Wipe out the current palette, replace with -1 for each element */
309    for (i=0;i<256;i++) {
310       cp->palette[i].color[0] = 0;
311       cp->palette[i].color[1] = 0;
312       cp->palette[i].color[2] = 0;
313       cp->palette[i].color[3] = 0;
314       cp->palette[i].index = -1;
315    }
316 
317    /* The top level element is a flame element. */
318    /* Read the attributes of it and store them. */
319    att_ptr = flame_node->properties;
320 
321    if (att_ptr==NULL) {
322       fprintf(stderr, "Error : <flame> element has no attributes.\n");
323       return(1);
324    }
325 
326    memset(cp->flame_name,0,flam3_name_len+1);
327 
328    for (cur_att = att_ptr; cur_att; cur_att = cur_att->next) {
329 
330        att_str = (char *) xmlGetProp(flame_node,cur_att->name);
331 
332       /* Compare attribute names */
333       if (!xmlStrcmp(cur_att->name, (const xmlChar *)"time")) {
334          cp->time = flam3_atof(att_str);
335       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"hsv_rgb_palette_blend")) {
336          cp->hsv_rgb_palette_blend = flam3_atof(att_str);
337       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"interpolation")) {
338      if (!strcmp("linear", att_str)) {
339          cp->interpolation = flam3_interpolation_linear;
340      } else if  (!strcmp("smooth", att_str)) {
341          cp->interpolation = flam3_interpolation_smooth;
342      } else {
343          fprintf(stderr, "warning: unrecognized interpolation type %s.\n", att_str);
344      }
345       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"palette_interpolation")) {
346      if (!strcmp("hsv", att_str)) {
347          cp->palette_interpolation = flam3_palette_interpolation_hsv;
348      } else if  (!strcmp("sweep", att_str)) {
349          cp->palette_interpolation = flam3_palette_interpolation_sweep;
350      } else if  (!strcmp("hsv_circular", att_str)) {
351          cp->palette_interpolation = flam3_palette_interpolation_hsv_circular;
352      } else if  (!strcmp("rgb", att_str)) {
353          cp->palette_interpolation = flam3_palette_interpolation_rgb;
354      } else {
355          fprintf(stderr, "warning: unrecognized palette interpolation type %s.\n", att_str);
356      }
357       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"interpolation_space") ||
358                  !xmlStrcmp(cur_att->name, (const xmlChar *)"interpolation_type")) {
359 
360          if (!strcmp("linear", att_str))
361             cp->interpolation_type = flam3_inttype_linear;
362          else if (!strcmp("log", att_str))
363             cp->interpolation_type = flam3_inttype_log;
364          else if (!strcmp("old", att_str))
365             cp->interpolation_type = flam3_inttype_compat;
366          else if (!strcmp("older", att_str))
367             cp->interpolation_type = flam3_inttype_older;
368          else
369             fprintf(stderr,"warning: unrecognized interpolation_type %s.\n",att_str);
370 
371       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"name")) {
372          strncpy(cp->flame_name, att_str, flam3_name_len);
373          i = (int)strlen(cp->flame_name)-1;
374          while(i-->0) {
375             if (isspace(cp->flame_name[i]))
376                cp->flame_name[i] = '_';
377          }
378 
379       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"palette")) {
380          cp->palette_index = flam3_atoi(att_str);
381       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"size")) {
382          if (sscanf(att_str, "%d %d%1s", &cp->width, &cp->height, tmps) != 2) {
383             fprintf(stderr,"error: invalid size attribute '%s'\n",att_str);
384             xmlFree(att_str);
385             return(1);
386          }
387       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"center")) {
388          if (sscanf(att_str, "%lf %lf%1s", &cp->center[0], &cp->center[1], tmps) != 2) {
389             fprintf(stderr,"error: invalid center attribute '%s'\n",att_str);
390             xmlFree(att_str);
391             return(1);
392          }
393          cp->rot_center[0] = cp->center[0];
394          cp->rot_center[1] = cp->center[1];
395       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"scale")) {
396          cp->pixels_per_unit = flam3_atof(att_str);
397       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"rotate")) {
398          cp->rotate = flam3_atof(att_str);
399       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"zoom")) {
400          cp->zoom = flam3_atof(att_str);
401       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"oversample")) {
402          cp->spatial_oversample = flam3_atoi(att_str);
403       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"supersample")) {
404          cp->spatial_oversample = flam3_atoi(att_str);
405       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"filter")) {
406          cp->spatial_filter_radius = flam3_atof(att_str);
407       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"filter_shape")) {
408          if (!strcmp("gaussian", att_str))
409             cp->spatial_filter_select = flam3_gaussian_kernel;
410          else if (!strcmp("hermite", att_str))
411             cp->spatial_filter_select = flam3_hermite_kernel;
412          else if (!strcmp("box", att_str))
413             cp->spatial_filter_select = flam3_box_kernel;
414          else if (!strcmp("triangle", att_str))
415             cp->spatial_filter_select = flam3_triangle_kernel;
416          else if (!strcmp("bell", att_str))
417             cp->spatial_filter_select = flam3_bell_kernel;
418          else if (!strcmp("bspline", att_str))
419             cp->spatial_filter_select = flam3_b_spline_kernel;
420          else if (!strcmp("mitchell", att_str))
421             cp->spatial_filter_select = flam3_mitchell_kernel;
422          else if (!strcmp("blackman", att_str))
423             cp->spatial_filter_select = flam3_blackman_kernel;
424          else if (!strcmp("catrom", att_str))
425             cp->spatial_filter_select = flam3_catrom_kernel;
426          else if (!strcmp("hanning", att_str))
427             cp->spatial_filter_select = flam3_hanning_kernel;
428          else if (!strcmp("hamming", att_str))
429             cp->spatial_filter_select = flam3_hamming_kernel;
430          else if (!strcmp("lanczos3", att_str))
431             cp->spatial_filter_select = flam3_lanczos3_kernel;
432          else if (!strcmp("lanczos2", att_str))
433             cp->spatial_filter_select = flam3_lanczos2_kernel;
434          else if (!strcmp("quadratic", att_str))
435             cp->spatial_filter_select = flam3_quadratic_kernel;
436          else
437             fprintf(stderr, "warning: unrecognized kernel shape %s.  Using gaussian.\n", att_str);
438 
439       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"temporal_filter_type")) {
440          if (!strcmp("box", att_str))
441             cp->temporal_filter_type = flam3_temporal_box;
442          else if (!strcmp("gaussian", att_str))
443             cp->temporal_filter_type = flam3_temporal_gaussian;
444          else if (!strcmp("exp",att_str))
445             cp->temporal_filter_type = flam3_temporal_exp;
446          else
447             fprintf(stderr, "warning: unrecognized temporal filter %s.  Using box.\n",att_str);
448       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"temporal_filter_width")) {
449          cp->temporal_filter_width = flam3_atof(att_str);
450       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"temporal_filter_exp")) {
451          cp->temporal_filter_exp = flam3_atof(att_str);
452       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"palette_mode")) {
453          if (!strcmp("step", att_str))
454             cp->palette_mode = flam3_palette_mode_step;
455          else if (!strcmp("linear", att_str))
456             cp->palette_mode = flam3_palette_mode_linear;
457          else
458             fprintf(stderr,"warning: unrecognized palette mode %s.  Using step.\n",att_str);
459       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"quality")) {
460          cp->sample_density = flam3_atof(att_str);
461       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"passes")) {
462          cp->nbatches = flam3_atoi(att_str);
463       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"temporal_samples")) {
464          cp->ntemporal_samples = flam3_atoi(att_str);
465       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"background")) {
466          if (sscanf(att_str, "%lf %lf %lf%1s", &cp->background[0], &cp->background[1], &cp->background[2], tmps) != 3) {
467             fprintf(stderr,"error: invalid background attribute '%s'\n",att_str);
468             xmlFree(att_str);
469             return(1);
470          }
471       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"brightness")) {
472          cp->brightness = flam3_atof(att_str);
473 /*      } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"contrast")) {
474          cp->contrast = flam3_atof(att_str);*/
475       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"gamma")) {
476          cp->gamma = flam3_atof(att_str);
477       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"highlight_power")) {
478          cp->highlight_power = atof(att_str);
479       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"vibrancy")) {
480          cp->vibrancy = flam3_atof(att_str);
481       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"hue")) {
482          cp->hue_rotation = fmod(flam3_atof(att_str), 1.0);
483       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"estimator_radius")) {
484          cp->estimator = flam3_atof(att_str);
485       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"estimator_minimum")) {
486          cp->estimator_minimum = flam3_atof(att_str);
487       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"estimator_curve")) {
488          cp->estimator_curve = flam3_atof(att_str);
489       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"gamma_threshold")) {
490          cp->gam_lin_thresh = flam3_atof(att_str);
491       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"soloxform")) {
492          solo_xform = flam3_atof(att_str);
493       }
494 
495       xmlFree(att_str);
496 
497    }
498 
499    /* Finished with flame attributes.  Now look at children of flame element. */
500    for (chld_node=flame_node->children; chld_node; chld_node = chld_node->next) {
501 
502       /* Is this a color node? */
503       if (!xmlStrcmp(chld_node->name, (const xmlChar *)"color")) {
504          int index = -1;
505          double r=0.0,g=0.0,b=0.0,a=0.0;
506 
507          /* Loop through the attributes of the color element */
508          att_ptr = chld_node->properties;
509 
510          if (att_ptr==NULL) {
511             fprintf(stderr,"Error:  No attributes for color element.\n");
512             return(1);
513          }
514 
515          for (cur_att=att_ptr; cur_att; cur_att = cur_att->next) {
516 
517             att_str = (char *) xmlGetProp(chld_node,cur_att->name);
518 
519             a = 255.0;
520 
521             if (!xmlStrcmp(cur_att->name, (const xmlChar *)"index")) {
522                index = flam3_atoi(att_str);
523             } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"rgb")) {
524                if (sscanf(att_str, "%lf %lf %lf%1s", &r, &g, &b, tmps) != 3) {
525                   fprintf(stderr,"error: invalid rgb attribute '%s'\n",att_str);
526                   xmlFree(att_str);
527                   return(1);
528                }
529             } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"rgba")) {
530                if (sscanf(att_str, "%lf %lf %lf %lf%1s", &r, &g, &b, &a, tmps) != 4) {
531                   fprintf(stderr,"error: invalid rgba attribute '%s'\n",att_str);
532                   xmlFree(att_str);
533                   return(1);
534                }
535             } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"a")) {
536                if (sscanf(att_str, "%lf%1s", &a, tmps) != 1) {
537                   fprintf(stderr,"error: invalid a attribute '%s'\n",att_str);
538                   xmlFree(att_str);
539                   return(1);
540                }
541             } else {
542                fprintf(stderr,"Error:  Unknown color attribute '%s'\n",cur_att->name);
543                xmlFree(att_str);
544                return(1);
545             }
546 
547             xmlFree(att_str);
548          }
549 
550          if (index >= 0 && index <= 255) {
551             cp->palette[index].color[3] = a / 255.0;
552             /* Don't forget to premultiply the palette... */
553             cp->palette[index].color[0] = cp->palette[index].color[3] * r / 255.0;
554             cp->palette[index].color[1] = cp->palette[index].color[3] * g / 255.0;
555             cp->palette[index].color[2] = cp->palette[index].color[3] * b / 255.0;
556             cp->palette[index].index = index;
557          } else {
558             fprintf(stderr,"Error:  Color element with bad/missing index attribute (%d)\n",index);
559             return(1);
560          }
561       } else if (!xmlStrcmp(chld_node->name, (const xmlChar *)"colors")) {
562 
563          int count = 0;
564 
565          /* Loop through the attributes of the colors element */
566          att_ptr = chld_node->properties;
567 
568          if (att_ptr==NULL) {
569             fprintf(stderr,"Error: No attributes for colors element.\n");
570             return(1);
571          }
572 
573          for (cur_att=att_ptr; cur_att; cur_att = cur_att->next) {
574 
575             att_str = (char *) xmlGetProp(chld_node,cur_att->name);
576 
577             if (!xmlStrcmp(cur_att->name, (const xmlChar *)"count")) {
578                count = flam3_atoi(att_str);
579             } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"data")) {
580                if (flam3_parse_hexformat_colors(att_str, cp, count, -4) > 0) {
581                   fprintf(stderr,"error parsing hexformatted colors\n");
582                   xmlFree(att_str);
583                   return(1);
584                }
585             } else {
586                fprintf(stderr,"Error:  Unknown color attribute '%s'\n",cur_att->name);
587                xmlFree(att_str);
588                return(1);
589             }
590 
591             xmlFree(att_str);
592          }
593 
594 
595       } else if (!xmlStrcmp(chld_node->name, (const xmlChar *)"palette")) {
596 
597          /* This could be either the old form of palette or the new form */
598          /* Make sure BOTH are not specified, otherwise either are ok    */
599          int numcolors=0;
600          int numbytes=0;
601          int old_format=0;
602          int new_format=0;
603          int index0, index1;
604          double hue0, hue1;
605          double blend = 0.5;
606          index0 = index1 = flam3_palette_random;
607          hue0 = hue1 = 0.0;
608 
609          /* Loop through the attributes of the palette element */
610          att_ptr = chld_node->properties;
611 
612          if (att_ptr==NULL) {
613             fprintf(stderr,"Error:  No attributes for palette element.\n");
614             return(1);
615          }
616 
617          for (cur_att=att_ptr; cur_att; cur_att = cur_att->next) {
618 
619             att_str = (char *) xmlGetProp(chld_node,cur_att->name);
620 
621             if (!xmlStrcmp(cur_att->name, (const xmlChar *)"index0")) {
622                old_format++;
623                index0 = flam3_atoi(att_str);
624             } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"index1")) {
625                old_format++;
626                index1 = flam3_atoi(att_str);
627             } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"hue0")) {
628                old_format++;
629                hue0 = flam3_atof(att_str);
630             } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"hue1")) {
631                old_format++;
632                hue1 = flam3_atof(att_str);
633             } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"blend")) {
634                old_format++;
635                blend = flam3_atof(att_str);
636             } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"count")) {
637                new_format++;
638                numcolors = flam3_atoi(att_str);
639             } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"format")) {
640                new_format++;
641                if (!strcmp(att_str,"RGB"))
642                   numbytes=3;
643                else if (!strcmp(att_str,"RGBA"))
644                   numbytes=4;
645                else {
646                   fprintf(stderr,"Error: Unrecognized palette format string (%s)\n",att_str);
647                   xmlFree(att_str);
648                   return(1);
649                }
650             } else {
651                fprintf(stderr,"Error:  Unknown palette attribute '%s'\n",cur_att->name);
652                xmlFree(att_str);
653                return(1);
654             }
655 
656             xmlFree(att_str);
657          }
658 
659          /* Old or new format? */
660          if (new_format>0 && old_format>0) {
661             fprintf(stderr,"Error: mixing of old and new palette tag syntax not allowed.\n");
662             return(1);
663          }
664 
665          if (old_format>0)
666             interpolate_cmap(cp->palette, blend, index0, hue0, index1, hue1);
667          else {
668 
669             char *pal_str;
670 
671             /* Read formatted string from contents of tag */
672 
673             pal_str = (char *) xmlNodeGetContent(chld_node);
674 
675             if (flam3_parse_hexformat_colors(pal_str, cp, numcolors, numbytes) > 0) {
676                fprintf(stderr,"error reading hexformatted colors\n");
677                xmlFree(pal_str);
678                return(1);
679             }
680 
681             xmlFree(pal_str);
682          }
683       } else if (!xmlStrcmp(chld_node->name, (const xmlChar *)"symmetry")) {
684 
685          int kind=0;
686          int bef,aft;
687 
688          /* Loop through the attributes of the symmetry element */
689          att_ptr = chld_node->properties;
690 
691          if (att_ptr==NULL) {
692             fprintf(stderr,"Error:  No attributes for symmetry element.\n");
693             return(1);
694          }
695 
696          for (cur_att=att_ptr; cur_att; cur_att = cur_att->next) {
697 
698             att_str = (char *) xmlGetProp(chld_node,cur_att->name);
699 
700             if (!xmlStrcmp(cur_att->name, (const xmlChar *)"kind")) {
701                kind = flam3_atoi(att_str);
702             } else {
703                fprintf(stderr,"Error:  Unknown symmetry attribute '%s'\n",cur_att->name);
704                xmlFree(att_str);
705                return(1);
706             }
707 
708             xmlFree(att_str);
709          }
710 
711          bef = cp->num_xforms;
712          flam3_add_symmetry(cp,kind);
713          aft = cp->num_xforms;
714          num_std_xforms += (aft-bef);
715 
716 
717       } else if (!xmlStrcmp(chld_node->name, (const xmlChar *)"xform") ||
718                   !xmlStrcmp(chld_node->name, (const xmlChar *)"finalxform")) {
719 
720          int xf = cp->num_xforms;
721 
722          if (!xmlStrcmp(chld_node->name, (const xmlChar *)"finalxform")) {
723 
724             if (cp->final_xform_index >=0) {
725                fprintf(stderr,"Error:  Cannot specify more than one final xform.\n");
726                return(1);
727             }
728 
729             flam3_add_xforms(cp, 1, 0, 1);
730             cp->xform[xf].var[0]=0.0;
731             cp->final_xform_index = xf;
732             /* Now, if present, the xform enable defaults to on */
733             cp->final_xform_enable = 1;
734 
735          } else {
736 
737             /* Add one to the counter */
738             flam3_add_xforms(cp, 1, 0, 0);
739 
740             /* If there was already a final xform, we have to change xf to point to the second to last xform */
741             if (cp->final_xform_index>=0)
742                xf--;
743 
744             cp->xform[xf].var[0]=0.0;
745             num_std_xforms++;
746 
747          }
748 
749          if (parse_xform_xml(chld_node, &(cp->xform[xf]), &num_xaos, &xaos, num_std_xforms, 0) != 0)
750             return(1);
751 
752          if (cp->final_xform_index == xf && cp->xform[xf].density != 0.0) {
753             fprintf(stderr,"Error: Final xforms should not have weight specified.\n");
754             return(1);
755          }
756 
757          /* Check for non-zero motion_* params */
758          if (cp->xform[xf].motion_freq != 0 || cp->xform[xf].motion_func != 0) {
759             fprintf(stderr,"Error: Motion parameters should not be specified in xforms.\n");
760             return(1);
761          }
762 
763 
764          /* Motion Language:  Check the xform element for children - should be named 'motion'. */
765          for (motion_node=chld_node->children; motion_node; motion_node = motion_node->next) {
766 
767             if (!xmlStrcmp(motion_node->name, (const xmlChar *)"motion")) {
768 
769                int nm = cp->xform[xf].num_motion;
770 
771                /* Add motion element to xform */
772                flam3_add_motion_element( &cp->xform[xf] );
773 
774                /* Read motion xml */
775                if (parse_xform_xml(motion_node, &(cp->xform[xf].motion[nm]), NULL, NULL, 0, 1) != 0)
776                   return(1);
777 
778             }
779 
780          }
781 
782       } else if (!xmlStrcmp(chld_node->name, (const xmlChar *)"edit")) {
783 
784          /* Create a new XML document with this edit node as the root node */
785          cp->edits = xmlNewDoc( (const xmlChar *)"1.0");
786          edit_node = xmlCopyNode( chld_node, 1 );
787          xmlDocSetRootElement(cp->edits, edit_node);
788 
789       }
790    } /* Done parsing flame element. */
791 
792    num_std_xforms++;
793 
794    for (i=0;i<num_std_xforms;i++) {
795 
796       /* Adjust opacity with solo xform setting */
797       if (solo_xform>=0 && i!=solo_xform)
798          cp->xform[i].opacity = 0.0;
799 
800    }
801 
802    /* Set the chaos array entries with the values in the xaos list */
803    for (i=0;i<num_xaos;i++)
804       cp->chaos[xaos[i].from][xaos[i].to] = xaos[i].scalar;
805 
806    free(xaos);
807 
808    /* If there is a final xform in this cp, move it to the end of the list */
809    if (cp->final_xform_index >=0 && cp->final_xform_index != (cp->num_xforms-1)) {
810       /* Make a copy of the final xform */
811       tmpcpy = cp->xform[cp->final_xform_index];
812 
813       /* Move each other xform up one */
814       for (i=cp->final_xform_index+1;i<cp->num_xforms;i++)
815          cp->xform[i-1] = cp->xform[i];
816 
817       /* Put the final at the end */
818       cp->xform[cp->num_xforms-1] = tmpcpy;
819 
820       cp->final_xform_index = cp->num_xforms - 1;
821    }
822 
823    /* Check for bad parse */
824    if (flam3_conversion_failed) {
825       fprintf(stderr,"error: parsing a double or int attribute's value.\n");
826       return(1);
827    }
828 
829    return(0);
830 
831 }
832 
parse_xform_xml(xmlNode * chld_node,flam3_xform * this_xform,int * num_xaos,flam3_chaos_entry ** xaos,int numstd,int motionxf)833 int parse_xform_xml(xmlNode *chld_node,flam3_xform *this_xform, int *num_xaos,
834                     flam3_chaos_entry **xaos, int numstd, int motionxf) {
835 
836    xmlAttrPtr att_ptr, cur_att;
837    char *att_str, *cpy;
838    char tmps[2];
839    int j,k;
840 
841    /* Loop through the attributes of the xform element */
842    att_ptr = chld_node->properties;
843 
844    if (att_ptr==NULL) {
845       fprintf(stderr,"Error: No attributes for element.\n");
846       return(1);
847    }
848 
849    for (cur_att=att_ptr; cur_att; cur_att = cur_att->next) {
850 
851       att_str = (char *) xmlGetProp(chld_node,cur_att->name);
852 
853       cpy = att_str;
854       if (!xmlStrcmp(cur_att->name, (const xmlChar *)"weight")) {
855          this_xform->density = flam3_atof(att_str);
856       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"symmetry")) {
857          /* Deprecated.  Set both color_speed and animate to this value. */
858          this_xform->color_speed = (1.0-flam3_atof(att_str))/2.0;
859          this_xform->animate = flam3_atof(att_str)>0 ? 0 : 1;
860       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"color_speed")) {
861          this_xform->color_speed = flam3_atof(att_str);
862       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"animate")) {
863          this_xform->animate = flam3_atof(att_str);
864       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"motion_frequency")) {
865          this_xform->motion_freq = flam3_atoi(att_str);
866       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"motion_function")) {
867          if (!strcmp("sin", att_str)) {
868             this_xform->motion_func = MOTION_SIN;
869          } else if (!strcmp("triangle",att_str)) {
870             this_xform->motion_func = MOTION_TRIANGLE;
871          } else if (!strcmp("hill",att_str)) {
872             this_xform->motion_func = MOTION_HILL;
873          } else {
874             fprintf(stderr,"Error: unknown motion function '%s'\n",att_str);
875             xmlFree(att_str);
876             return(1);
877          }
878 
879       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"color")) {
880          double tmpc1;
881          this_xform->color = 0.0;
882          /* Try two coords first */
883          if (sscanf(att_str, "%lf %lf%1s", &this_xform->color, &tmpc1, tmps) != 2) {
884             /* Try one color */
885             if (sscanf(att_str, "%lf%1s", &this_xform->color,tmps) != 1) {
886                fprintf(stderr,"Error: malformed xform color attribute '%s'\n",att_str);
887                xmlFree(att_str);
888                return(1);
889             }
890          }
891       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"var1")) {
892          for (j=0; j < flam3_nvariations; j++) {
893             this_xform->var[j] = 0.0;
894          }
895          j = flam3_atoi(att_str);
896 
897          if (j < 0 || j >= flam3_nvariations) {
898             fprintf(stderr,"Error:  Bad variation (%d)\n",j);
899             xmlFree(att_str);
900             return(1);
901          }
902 
903          this_xform->var[j] = 1.0;
904       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"var")) {
905          for (j=0; j < flam3_nvariations; j++) {
906             char *cpy2;
907             errno=0;
908             this_xform->var[j] = strtod(cpy, &cpy2);
909             if (errno != 0 || cpy==cpy2) {
910                fprintf(stderr,"error: bad value in var attribute '%s'\n",att_str);
911                xmlFree(att_str);
912                return(1);
913             }
914             cpy=cpy2;
915          }
916 
917          if (cpy != att_str+strlen(att_str)) {
918             fprintf(stderr,"error: extra chars at the end of var attribute '%s'\n",att_str);
919             xmlFree(att_str);
920             return(1);
921          }
922       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"chaos")) {
923          /* Chaos scalars */
924 
925          char *tok;
926          double scal;
927          int toi=0;
928 
929          if (motionxf==1) {
930             fprintf(stderr,"error: motion element cannot have a chaos attribute.\n");
931             xmlFree(att_str);
932             return(1);
933          }
934 
935          /* The att string contains at least one value, delimited by a space */
936          tok = strtok(cpy," ");
937          while (tok!=NULL) {
938             scal = flam3_atof(tok);
939 
940             /* Skip 1.0 entries */
941             if (scal==1.0) {
942                toi++;
943                tok = strtok(NULL," ");
944                continue;
945             }
946 
947             /* Realloc the xaos list */
948             *xaos = realloc((*xaos),(*num_xaos+1) * sizeof(flam3_chaos_entry));
949 
950             /* Populate the xaos list */
951             (*xaos)[*num_xaos].from = numstd;
952             (*xaos)[*num_xaos].to = toi;
953             (*xaos)[*num_xaos].scalar = scal;
954             toi++;
955             (*num_xaos)++;
956 
957             /* Get the next token */
958             tok = strtok(NULL," ");
959          }
960       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"plotmode")) {
961 
962          if (motionxf==1) {
963             fprintf(stderr,"error: motion element cannot have a plotmode attribute.\n");
964             xmlFree(att_str);
965             return(1);
966          }
967 
968          if (!strcmp("off", att_str))
969             this_xform->opacity = 0.0;
970       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"opacity")) {
971          this_xform->opacity = flam3_atof(att_str);
972 
973       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"coefs")) {
974          for (k=0; k<3; k++) {
975             for (j=0; j<2; j++) {
976                char *cpy2;
977                errno = 0;
978                this_xform->c[k][j] = strtod(cpy, &cpy2);
979                if (errno != 0 || cpy==cpy2) {
980                   fprintf(stderr,"error: bad value in coefs attribute '%s'\n",att_str);
981                   xmlFree(att_str);
982                   return(1);
983                }
984                cpy=cpy2;
985             }
986          }
987          if (cpy != att_str+strlen(att_str)) {
988             fprintf(stderr,"error: extra chars at the end of coefs attribute '%s'\n",att_str);
989             xmlFree(att_str);
990             return(1);
991          }
992       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"post")) {
993          for (k = 0; k < 3; k++) {
994             for (j = 0; j < 2; j++) {
995                char *cpy2;
996                errno = 0;
997                this_xform->post[k][j] = strtod(cpy, &cpy2);
998                if (errno != 0 || cpy==cpy2) {
999                   fprintf(stderr,"error: bad value in post attribute '%s'\n",att_str);
1000                   xmlFree(att_str);
1001                   return(1);
1002                }
1003                cpy=cpy2;
1004             }
1005          }
1006          if (cpy != att_str+strlen(att_str)) {
1007             fprintf(stderr,"error: extra chars at end of post attribute '%s'\n",att_str);
1008             xmlFree(att_str);
1009             return(1);
1010          }
1011       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"blob_low")) {
1012          this_xform->blob_low = flam3_atof(att_str);
1013       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"blob_high")) {
1014          this_xform->blob_high = flam3_atof(att_str);
1015       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"blob_waves")) {
1016          this_xform->blob_waves = flam3_atof(att_str);
1017       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"pdj_a")) {
1018          this_xform->pdj_a = flam3_atof(att_str);
1019       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"pdj_b")) {
1020          this_xform->pdj_b = flam3_atof(att_str);
1021       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"pdj_c")) {
1022          this_xform->pdj_c = flam3_atof(att_str);
1023       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"pdj_d")) {
1024          this_xform->pdj_d = flam3_atof(att_str);
1025       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"fan2_x")) {
1026          this_xform->fan2_x = flam3_atof(att_str);
1027       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"fan2_y")) {
1028          this_xform->fan2_y = flam3_atof(att_str);
1029       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"rings2_val")) {
1030          this_xform->rings2_val = flam3_atof(att_str);
1031       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"perspective_angle")) {
1032          this_xform->perspective_angle = flam3_atof(att_str);
1033       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"perspective_dist")) {
1034          this_xform->perspective_dist = flam3_atof(att_str);
1035       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"julian_power")) {
1036          this_xform->julian_power = flam3_atof(att_str);
1037       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"julian_dist")) {
1038          this_xform->julian_dist = flam3_atof(att_str);
1039       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"juliascope_power")) {
1040          this_xform->juliascope_power = flam3_atof(att_str);
1041       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"juliascope_dist")) {
1042          this_xform->juliascope_dist = flam3_atof(att_str);
1043       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"radial_blur_angle")) {
1044          this_xform->radial_blur_angle = flam3_atof(att_str);
1045       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"pie_slices")) {
1046          this_xform->pie_slices = flam3_atof(att_str);
1047       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"pie_rotation")) {
1048          this_xform->pie_rotation = flam3_atof(att_str);
1049       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"pie_thickness")) {
1050          this_xform->pie_thickness = flam3_atof(att_str);
1051       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"ngon_sides")) {
1052          this_xform->ngon_sides = flam3_atof(att_str);
1053       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"ngon_power")) {
1054          this_xform->ngon_power = flam3_atof(att_str);
1055       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"ngon_circle")) {
1056          this_xform->ngon_circle = flam3_atof(att_str);
1057       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"ngon_corners")) {
1058          this_xform->ngon_corners = flam3_atof(att_str);
1059       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"curl_c1")) {
1060          this_xform->curl_c1 = flam3_atof(att_str);
1061       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"curl_c2")) {
1062          this_xform->curl_c2 = flam3_atof(att_str);
1063       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"rectangles_x")) {
1064          this_xform->rectangles_x = flam3_atof(att_str);
1065       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"rectangles_y")) {
1066          this_xform->rectangles_y = flam3_atof(att_str);
1067       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"amw_amp")) {
1068          this_xform->amw_amp = flam3_atof(att_str);
1069       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"disc2_rot")) {
1070          this_xform->disc2_rot = flam3_atof(att_str);
1071       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"disc2_twist")) {
1072          this_xform->disc2_twist = flam3_atof(att_str);
1073       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"super_shape_rnd")) {
1074          this_xform->super_shape_rnd = flam3_atof(att_str);
1075          /* Limit to [0,1] */
1076          if (this_xform->super_shape_rnd<0)
1077             this_xform->super_shape_rnd=0;
1078          else if (this_xform->super_shape_rnd>1)
1079             this_xform->super_shape_rnd=1;
1080       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"super_shape_m")) {
1081          this_xform->super_shape_m = flam3_atof(att_str);
1082       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"super_shape_n1")) {
1083          this_xform->super_shape_n1 = flam3_atof(att_str);
1084       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"super_shape_n2")) {
1085          this_xform->super_shape_n2 = flam3_atof(att_str);
1086       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"super_shape_n3")) {
1087          this_xform->super_shape_n3 = flam3_atof(att_str);
1088       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"super_shape_holes")) {
1089          this_xform->super_shape_holes = flam3_atof(att_str);
1090       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"flower_petals")) {
1091          this_xform->flower_petals = flam3_atof(att_str);
1092       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"flower_holes")) {
1093          this_xform->flower_holes = flam3_atof(att_str);
1094       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"conic_eccentricity")) {
1095          this_xform->conic_eccentricity = flam3_atof(att_str);
1096       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"conic_holes")) {
1097          this_xform->conic_holes = flam3_atof(att_str);
1098       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"parabola_height")) {
1099          this_xform->parabola_height = flam3_atof(att_str);
1100       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"parabola_width")) {
1101          this_xform->parabola_width = flam3_atof(att_str);
1102       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"bent2_x")) {
1103          this_xform->bent2_x = flam3_atof(att_str);
1104       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"bent2_y")) {
1105          this_xform->bent2_y = flam3_atof(att_str);
1106       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"bipolar_shift")) {
1107          this_xform->bipolar_shift = flam3_atof(att_str);
1108       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"cell_size")) {
1109          this_xform->cell_size = flam3_atof(att_str);
1110       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"cpow_i")) {
1111          this_xform->cpow_i = flam3_atof(att_str);
1112       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"cpow_r")) {
1113          this_xform->cpow_r = flam3_atof(att_str);
1114       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"cpow_power")) {
1115          this_xform->cpow_power = flam3_atof(att_str);
1116       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"curve_xamp")) {
1117          this_xform->curve_xamp = flam3_atof(att_str);
1118       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"curve_yamp")) {
1119          this_xform->curve_yamp = flam3_atof(att_str);
1120       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"curve_xlength")) {
1121          this_xform->curve_xlength = flam3_atof(att_str);
1122       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"curve_ylength")) {
1123          this_xform->curve_ylength = flam3_atof(att_str);
1124       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"escher_beta")) {
1125          this_xform->escher_beta = flam3_atof(att_str);
1126       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"lazysusan_x")) {
1127          this_xform->lazysusan_x = flam3_atof(att_str);
1128       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"lazysusan_y")) {
1129          this_xform->lazysusan_y = flam3_atof(att_str);
1130       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"lazysusan_spin")) {
1131          this_xform->lazysusan_spin = flam3_atof(att_str);
1132       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"lazysusan_space")) {
1133          this_xform->lazysusan_space = flam3_atof(att_str);
1134       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"lazysusan_twist")) {
1135          this_xform->lazysusan_twist = flam3_atof(att_str);
1136       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"modulus_x")) {
1137          this_xform->modulus_x = flam3_atof(att_str);
1138       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"modulus_y")) {
1139          this_xform->modulus_y = flam3_atof(att_str);
1140       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"oscilloscope_separation")) {
1141          this_xform->oscope_separation = flam3_atof(att_str);
1142       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"oscilloscope_frequency")) {
1143          this_xform->oscope_frequency = flam3_atof(att_str);
1144       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"oscilloscope_amplitude")) {
1145          this_xform->oscope_amplitude = flam3_atof(att_str);
1146       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"oscilloscope_damping")) {
1147          this_xform->oscope_damping = flam3_atof(att_str);
1148       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"oscope_separation")) {
1149          this_xform->oscope_separation = flam3_atof(att_str);
1150       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"oscope_frequency")) {
1151          this_xform->oscope_frequency = flam3_atof(att_str);
1152       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"oscope_amplitude")) {
1153          this_xform->oscope_amplitude = flam3_atof(att_str);
1154       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"oscope_damping")) {
1155          this_xform->oscope_damping = flam3_atof(att_str);
1156       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"popcorn2_x")) {
1157          this_xform->popcorn2_x = flam3_atof(att_str);
1158       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"popcorn2_y")) {
1159          this_xform->popcorn2_y = flam3_atof(att_str);
1160       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"popcorn2_c")) {
1161          this_xform->popcorn2_c = flam3_atof(att_str);
1162       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"separation_x")) {
1163          this_xform->separation_x = flam3_atof(att_str);
1164       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"separation_xinside")) {
1165          this_xform->separation_xinside = flam3_atof(att_str);
1166       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"separation_y")) {
1167          this_xform->separation_y = flam3_atof(att_str);
1168       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"separation_yinside")) {
1169          this_xform->separation_yinside = flam3_atof(att_str);
1170       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"split_xsize")) {
1171          this_xform->split_xsize = flam3_atof(att_str);
1172       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"split_ysize")) {
1173          this_xform->split_ysize = flam3_atof(att_str);
1174       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"splits_x")) {
1175          this_xform->splits_x = flam3_atof(att_str);
1176       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"splits_y")) {
1177          this_xform->splits_y = flam3_atof(att_str);
1178       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"stripes_space")) {
1179          this_xform->stripes_space = flam3_atof(att_str);
1180       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"stripes_warp")) {
1181          this_xform->stripes_warp = flam3_atof(att_str);
1182       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_angle")) {
1183          this_xform->wedge_angle = flam3_atof(att_str);
1184       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_hole")) {
1185          this_xform->wedge_hole = flam3_atof(att_str);
1186       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_count")) {
1187          this_xform->wedge_count = flam3_atof(att_str);
1188       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_swirl")) {
1189          this_xform->wedge_swirl = flam3_atof(att_str);
1190       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_julia_angle")) {
1191          this_xform->wedge_julia_angle = flam3_atof(att_str);
1192       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_julia_count")) {
1193          this_xform->wedge_julia_count = flam3_atof(att_str);
1194       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_julia_power")) {
1195          this_xform->wedge_julia_power = flam3_atof(att_str);
1196       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_julia_dist")) {
1197          this_xform->wedge_julia_dist = flam3_atof(att_str);
1198       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_sph_angle")) {
1199          this_xform->wedge_sph_angle = flam3_atof(att_str);
1200       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_sph_hole")) {
1201          this_xform->wedge_sph_hole = flam3_atof(att_str);
1202       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_sph_count")) {
1203          this_xform->wedge_sph_count = flam3_atof(att_str);
1204       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"wedge_sph_swirl")) {
1205          this_xform->wedge_sph_swirl = flam3_atof(att_str);
1206       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"whorl_inside")) {
1207          this_xform->whorl_inside = flam3_atof(att_str);
1208       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"whorl_outside")) {
1209          this_xform->whorl_outside = flam3_atof(att_str);
1210       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"waves2_scalex")) {
1211          this_xform->waves2_scalex = flam3_atof(att_str);
1212       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"waves2_scaley")) {
1213          this_xform->waves2_scaley = flam3_atof(att_str);
1214       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"waves2_freqx")) {
1215          this_xform->waves2_freqx = flam3_atof(att_str);
1216       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"waves2_freqy")) {
1217          this_xform->waves2_freqy = flam3_atof(att_str);
1218       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"auger_freq")) {
1219          this_xform->auger_freq = flam3_atof(att_str);
1220       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"auger_weight")) {
1221          this_xform->auger_weight = flam3_atof(att_str);
1222       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"auger_sym")) {
1223          this_xform->auger_sym = flam3_atof(att_str);
1224       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"auger_scale")) {
1225          this_xform->auger_scale = flam3_atof(att_str);
1226       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"flux_spread")) {
1227          this_xform->flux_spread = flam3_atof(att_str);
1228       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"Re_A") || !xmlStrcmp(cur_att->name, (const xmlChar *)"mobius_re_a")) {
1229          this_xform->mobius_re_a = flam3_atof(att_str);
1230       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"Re_B") || !xmlStrcmp(cur_att->name, (const xmlChar *)"mobius_re_b")) {
1231          this_xform->mobius_re_b = flam3_atof(att_str);
1232       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"Re_C") || !xmlStrcmp(cur_att->name, (const xmlChar *)"mobius_re_c")) {
1233          this_xform->mobius_re_c = flam3_atof(att_str);
1234       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"Re_D") || !xmlStrcmp(cur_att->name, (const xmlChar *)"mobius_re_d")) {
1235          this_xform->mobius_re_d = flam3_atof(att_str);
1236       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"Im_A") || !xmlStrcmp(cur_att->name, (const xmlChar *)"mobius_im_a")) {
1237          this_xform->mobius_im_a = flam3_atof(att_str);
1238       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"Im_B") || !xmlStrcmp(cur_att->name, (const xmlChar *)"mobius_im_b")) {
1239          this_xform->mobius_im_b = flam3_atof(att_str);
1240       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"Im_C") || !xmlStrcmp(cur_att->name, (const xmlChar *)"mobius_im_c")) {
1241          this_xform->mobius_im_c = flam3_atof(att_str);
1242       } else if (!xmlStrcmp(cur_att->name, (const xmlChar *)"Im_D") || !xmlStrcmp(cur_att->name, (const xmlChar *)"mobius_im_d")) {
1243          this_xform->mobius_im_d = flam3_atof(att_str);
1244       } else {
1245          int v = var2n((char *) cur_att->name);
1246          if (v != flam3_variation_none)
1247             this_xform->var[v] = flam3_atof(att_str);
1248          else
1249             fprintf(stderr,"Warning: unrecognized variation %s.  Ignoring.\n",(char *)cur_att->name);
1250       }
1251 
1252 
1253       xmlFree(att_str);
1254    }
1255    return(0);
1256 }
1257 
flam3_edit_print(FILE * f,xmlNodePtr editNode,int tabs,int formatting)1258 void flam3_edit_print(FILE *f, xmlNodePtr editNode, int tabs, int formatting) {
1259 
1260    char *tab_string = "   ";
1261    int ti,strl;
1262    xmlAttrPtr att_ptr=NULL,cur_att=NULL;
1263    xmlNodePtr chld_ptr=NULL, cur_chld=NULL;
1264    int indent_printed = 0;
1265    char *ai;
1266    int tablim = argi("print_edit_depth",0);
1267 
1268    char *att_str,*cont_str,*cpy_string;
1269 
1270    if (tablim>0 && tabs>tablim)
1271       return;
1272 
1273    /* If this node is an XML_ELEMENT_NODE, print it and it's attributes */
1274    if (editNode->type==XML_ELEMENT_NODE) {
1275 
1276       /* Print the node at the tab specified */
1277       if (formatting) {
1278          for (ti=0;ti<tabs;ti++)
1279             fprintf(f,"%s",tab_string);
1280       }
1281 
1282       fprintf(f,"<%s",editNode->name);
1283 
1284       /* This can either be an edit node or a sheep node */
1285       /* If it's an edit node, add one to the tab        */
1286       if (!xmlStrcmp(editNode->name, (const xmlChar *)"edit")) {
1287          tabs ++;
1288       }
1289 
1290       /* Print the attributes */
1291       att_ptr = editNode->properties;
1292 
1293       for (cur_att = att_ptr; cur_att; cur_att = cur_att->next) {
1294 
1295          att_str = (char *) xmlGetProp(editNode,cur_att->name);
1296          fprintf(f," %s=\"%s\"",cur_att->name,att_str);
1297          xmlFree(att_str);
1298       }
1299 
1300       /* Does this node have children? */
1301       if (!editNode->children || (tablim>0 && tabs>tablim)) {
1302          /* Close the tag and subtract the tab */
1303          fprintf(f,"/>");
1304          if (formatting)
1305             fprintf(f,"\n");
1306          tabs--;
1307       } else {
1308 
1309          /* Close the tag */
1310          fprintf(f,">");
1311 
1312          if (formatting)
1313             fprintf(f,"\n");
1314 
1315          /* Loop through the children and print them */
1316          chld_ptr = editNode->children;
1317 
1318          indent_printed = 0;
1319 
1320          for (cur_chld=chld_ptr; cur_chld; cur_chld = cur_chld->next) {
1321 
1322             /* If child is an element, indent first and then print it. */
1323             if (cur_chld->type==XML_ELEMENT_NODE &&
1324                (!xmlStrcmp(cur_chld->name, (const xmlChar *)"edit") ||
1325       (!xmlStrcmp(cur_chld->name, (const xmlChar *)"sheep")))) {
1326 
1327                if (indent_printed) {
1328                   indent_printed = 0;
1329                   fprintf(f,"\n");
1330                }
1331 
1332                flam3_edit_print(f, cur_chld, tabs, 1);
1333 
1334             } else {
1335 
1336                /* Child is a text node.  We don't want to indent more than once. */
1337                if (xmlIsBlankNode(cur_chld))
1338                   continue;
1339 
1340                if (indent_printed==0 && formatting==1) {
1341                   for (ti=0;ti<tabs;ti++)
1342                      fprintf(f,"%s",tab_string);
1343                   indent_printed = 1;
1344                }
1345 
1346                /* Print nodes without formatting. */
1347                flam3_edit_print(f, cur_chld, tabs, 0);
1348 
1349             }
1350          }
1351 
1352          if (indent_printed && formatting)
1353             fprintf(f,"\n");
1354 
1355          /* Tab out. */
1356          tabs --;
1357          if (formatting) {
1358             for (ti=0;ti<tabs;ti++)
1359                fprintf(f,"%s",tab_string);
1360          }
1361 
1362          /* Close the tag */
1363          fprintf(f,"</%s>",editNode->name);
1364 
1365          if (formatting) {
1366             fprintf(f,"\n");
1367          }
1368       }
1369 
1370    } else if (editNode->type==XML_TEXT_NODE) {
1371 
1372       /* Print text node */
1373       cont_str = (char *) xmlNodeGetContent(editNode);
1374       cpy_string = &(cont_str[0]);
1375       while (isspace(*cpy_string))
1376          cpy_string++;
1377 
1378       strl = (int)strlen(cont_str)-1;
1379 
1380       while (isspace(cont_str[strl]))
1381          strl--;
1382 
1383       cont_str[strl+1] = 0;
1384 
1385       fprintf(f,"%s",cpy_string);
1386 
1387    }
1388 }
1389 
1390