/*
Copyright (C) 1992-2009 Spotworks LLC
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include "random.h"
#include "private.h"
#include "img.h"
#include "rect.h"
#include "math.h"
#include "genome.h"
#define streq(a,b) (strcmp (a, b) == 0)
const char *argp_program_version = PACKAGE "-" VERSION;
typedef struct {
unsigned int bpc;
float scale, time;
char *cache;
} render_arguments;
static error_t parse_render_opt (int key, char *arg,
struct argp_state * const state) {
render_arguments * const arguments = state->input;
switch (key) {
case 'b': {
int i = atoi (arg);
if (i == 8 || i == 16) {
arguments->bpc = i;
} else {
argp_error (state, "Bits per channel must be 8 or 16");
}
break;
}
case 't': {
float i = atof (arg);
if (i <= 0) {
argp_error (state, "Time must be > 0");
} else {
arguments->time = i;
}
break;
}
case 's':
arguments->scale = atof (arg);
if (arguments->scale <= 0.0) {
argp_error (state, "Scale must be > 0");
}
break;
case 'c':
arguments->cache = strdup (arg);
break;
case ARGP_KEY_ARG:
if (state->arg_num > 0) {
return ARGP_ERR_UNKNOWN;
}
break;
case ARGP_KEY_END:
break;
default:
return ARGP_ERR_UNKNOWN;
break;
}
return 0;
}
static void do_render (const render_arguments * const arguments) {
randctx rc;
rand_seed(&rc);
int ncps;
flam3_genome * const cps = flam3_parse_xml2 (STDIN_FILENO,
flam3_defaults_on, &ncps, &rc);
if (cps == NULL) {
fprintf(stderr,"error reading genomes from file\n");
exit(1);
}
assert (ncps == 1);
flam3_genome * const genome = &cps[0];
genome->height *= arguments->scale;
genome->width *= arguments->scale;
genome->pixels_per_unit *= arguments->scale;
const unsigned int bytes_per_channel = arguments->bpc/8;
const unsigned int channels = 4;
const size_t this_size = channels * genome->width * genome->height *
bytes_per_channel;
void *image = (void *) calloc(this_size, sizeof(char));
bucket bucket;
bucket_init (&bucket, (uint2) { genome->width, genome->height });
if (arguments->cache != NULL) {
bucket_deserialize (&bucket, arguments->cache);
}
render_bucket (genome, &bucket, arguments->time);
if (arguments->cache != NULL) {
bucket_serialize (&bucket, arguments->cache);
}
fprintf (stderr, "%lu samples, %lu bad\n",
bucket.samples, bucket.badvals);
render_image (genome, &bucket, image, bytes_per_channel);
flam3_img_comments fpc;
write_png (stdout, image, genome->width, genome->height, &fpc,
bytes_per_channel);
}
static void print_genome (flam3_genome * const genome) {
printf("\n");
flam3_print (stdout, genome, NULL);
printf("\n");
}
typedef struct {
const char *palette;
unsigned int width, height, max_xforms, max_var;
double post_likelihood, final_likelihood, symmetry_likelihood;
} random_arguments;
static error_t parse_random_opt (int key, char *arg,
struct argp_state * const state) {
random_arguments * const arguments = state->input;
switch (key) {
case 'h': {
int i = atoi (arg);
if (i <= 0) {
argp_error (state, "Height must be > 0");
} else {
arguments->height = i;
}
break;
}
case 'w': {
int i = atoi (arg);
if (i <= 0) {
argp_error (state, "Width must be > 0");
} else {
arguments->width = i;
}
break;
}
case 'x': {
int i = atoi (arg);
if (i <= 0) {
argp_error (state, "Max xforms must be > 0");
} else {
arguments->max_xforms = i;
}
break;
}
case ARGP_KEY_ARG:
if (state->arg_num > 0) {
return ARGP_ERR_UNKNOWN;
}
break;
case ARGP_KEY_END:
break;
default:
return ARGP_ERR_UNKNOWN;
break;
}
return 0;
}
#define GOLDEN_RATIO (1.618033988749894848204586834)
#define GOLDEN_RATIO_M1 (GOLDEN_RATIO-1.0)
#define GOLDEN_RATIO_DIV (GOLDEN_RATIO_M1/GOLDEN_RATIO)
static double golden_bit (randctx * const rc) {
return rand_bool (rc) ? GOLDEN_RATIO_DIV : GOLDEN_RATIO_M1;
}
static void adjust_bounding_box (flam3_genome * const genome, randctx * const rc) {
double bmin[2], bmax[2];
flam3_estimate_bounding_box(genome, 0.01, 100000, bmin, bmax, rc);
if (rand_d01(rc) < 0.3) {
genome->center[0] = (bmin[0] + bmax[0]) / 2.0;
genome->center[1] = (bmin[1] + bmax[1]) / 2.0;
} else {
double mix0, mix1;
if (rand_bool(rc)) {
mix0 = golden_bit(rc) + rand_d11(rc)/5;
mix1 = golden_bit(rc);
} else if (rand_bool(rc)) {
mix0 = golden_bit(rc);
mix1 = golden_bit(rc) + rand_d11(rc)/5;
} else {
mix0 = golden_bit(rc) + rand_d11(rc)/5;
mix1 = golden_bit(rc) + rand_d11(rc)/5;
}
genome->center[0] = mix0 * bmin[0] + (1-mix0)*bmax[0];
genome->center[1] = mix1 * bmin[1] + (1-mix1)*bmax[1];
}
genome->rot_center[0] = genome->center[0];
genome->rot_center[1] = genome->center[1];
genome->pixels_per_unit = genome->width / (bmax[0] - bmin[0]);
}
static void do_random (const random_arguments * const arguments) {
randctx rc;
rand_seed(&rc);
palette_collection pc;
bool bret = palette_read_collection ("flam3-palettes.xml", &pc);
assert (bret);
flam3_genome genome;
clear_cp (&genome,flam3_defaults_on);
genome.hue_rotation = rand_mod(&rc, 8) ? 0.0 : rand_d01(&rc);
const palette * const p = palette_random (&pc, &rc);
assert (p != NULL);
palette_copy (p, &genome.palette);
palette_rotate_hue (&genome.palette, genome.hue_rotation);
genome.interpolation = flam3_interpolation_linear;
genome.palette_interpolation = flam3_palette_interpolation_hsv;
genome.rotate = rand_d01 (&rc) * 360.0;
unsigned int nxforms = rand_mod (&rc, arguments->max_xforms) + 1;
flam3_add_xforms(&genome,nxforms,0,0);
/* Add a final xform 15% of the time */
const bool add_final = rand_d01(&rc) < arguments->final_likelihood;
if (add_final) {
flam3_add_xforms(&genome,1,0,1);
++nxforms;
}
/* Loop over xforms */
assert (nxforms > 0);
for (unsigned int i = 0; i < nxforms; i++) {
flam3_xform * const xform = &genome.xform[i];
const bool add_post = rand_d01 (&rc) < arguments->post_likelihood;
xform_rand (xform, add_post, arguments->max_var, &rc);
xform->density = 1.0 / nxforms;
xform->color_speed = 0.5;
xform->animate = 1.0;
}
/* Randomly add symmetry (but not if we've already added a final xform) */
if (rand_d01(&rc) < arguments->symmetry_likelihood && !add_final) {
flam3_add_symmetry(&genome, 0, &rc);
}
/* random resets genome, adjust before finding appropriate bbox */
genome.width = arguments->width;
genome.height = arguments->height;
adjust_bounding_box (&genome, &rc);
print_genome (&genome);
}
typedef struct {
int method;
unsigned int symmetry;
} mutate_arguments;
static error_t parse_mutate_opt (int key, char *arg,
struct argp_state * const state) {
mutate_arguments * const arguments = state->input;
switch (key) {
case 'm':
if (arg == NULL) {
arguments->method = MUTATE_NOT_SPECIFIED;
} else if (streq (arg, "all-vars")) {
arguments->method = MUTATE_ALL_VARIATIONS;
} else if (streq(arg,"one-xform")) {
arguments->method = MUTATE_ONE_XFORM_COEFS;
} else if (streq(arg,"add-symmetry")) {
arguments->method = MUTATE_ADD_SYMMETRY;
} else if (streq(arg,"post-xforms")) {
arguments->method = MUTATE_POST_XFORMS;
} else if (streq(arg,"color-palette")) {
arguments->method = MUTATE_COLOR_PALETTE;
} else if (streq(arg,"delete-xform")) {
arguments->method = MUTATE_DELETE_XFORM;
} else if (streq(arg,"all-coefs")) {
arguments->method = MUTATE_ALL_COEFS;
} else {
argp_error (state, "Unknown method %s", arg);
}
break;
case ARGP_KEY_ARG:
if (state->arg_num > 0) {
return ARGP_ERR_UNKNOWN;
}
break;
case ARGP_KEY_END:
break;
default:
return ARGP_ERR_UNKNOWN;
break;
}
return 0;
}
static void do_mutate (const mutate_arguments * const arguments) {
randctx rc;
rand_seed(&rc);
int ncps;
flam3_genome * const cps = flam3_parse_xml2 (STDIN_FILENO,
flam3_defaults_on, &ncps, &rc);
if (cps == NULL) {
fprintf(stderr,"error reading genomes from file\n");
exit(1);
}
assert (ncps == 1);
palette_collection pc;
bool bret = palette_read_collection ("flam3-palettes.xml", &pc);
assert (bret);
flam3_genome * const genome = &cps[0];
int ivars = 0;
const double speed = 1.0;
flam3_mutate (genome, arguments->method, &ivars, 1, arguments->symmetry,
speed, &pc, &rc);
print_genome (genome);
}
typedef struct {
int method;
} cross_arguments;
static error_t parse_cross_opt (int key, char *arg,
struct argp_state * const state) {
mutate_arguments * const arguments = state->input;
switch (key) {
case 'm':
if (arg == NULL) {
arguments->method = CROSS_NOT_SPECIFIED;
} else if (streq(arg,"union")) {
arguments->method = CROSS_UNION;
} else if (streq(arg,"interpolate")) {
arguments->method = CROSS_INTERPOLATE;
} else if (streq(arg,"alternate")) {
arguments->method = CROSS_ALTERNATE;
} else {
argp_error (state, "Unknown method %s", arg);
}
break;
case ARGP_KEY_ARG:
if (state->arg_num > 0) {
return ARGP_ERR_UNKNOWN;
}
break;
case ARGP_KEY_END:
break;
default:
return ARGP_ERR_UNKNOWN;
break;
}
return 0;
}
static void do_cross (const cross_arguments * const arguments) {
randctx rc;
rand_seed(&rc);
int ncps;
flam3_genome * const cps = flam3_parse_xml2 (STDIN_FILENO,
flam3_defaults_on, &ncps, &rc);
if (cps == NULL) {
fprintf(stderr,"error reading genomes from file\n");
exit(1);
}
assert (ncps == 2);
flam3_genome * const genome_a = &cps[0], * const genome_b = &cps[1];
flam3_genome genome_out;
flam3_cross (genome_a, genome_b, &genome_out, arguments->method, &rc);
print_genome (&genome_out);
}
#if 0
static void do_improvecolors () {
flam3_improve_colors(&cp_orig, 100, 0, 10, &rc);
}
static void do_interpolate () {
for (ftime = first_frame; ftime <= last_frame; ftime += 1) {
iscp=0;
for (i=0;i