/*
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"
#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_from_file (stdin, NULL,
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, flam3_dont_print_edits);
printf("\n");
}
typedef struct {
int symmetry;
const char *palette;
unsigned int width, height;
} 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 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);
flam3_genome genome = { .edits = NULL };
int ivars = flam3_variation_random;
flam3_random (&genome, &ivars, 1, arguments->symmetry, 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_from_file (stdin, NULL,
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];
int ivars = flam3_variation_random;
const double speed = 1.0;
flam3_mutate (genome, arguments->method, &ivars, 1, arguments->symmetry,
speed, &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_from_file (stdin, NULL,
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