/****************************************************************/
/* ifsspell.c - generates ifs matrix entry readable by fractint */
/****************************************************************/
/* Written by Wesley B. Loewer, Copyright 1994.                 */
/****************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

#define PI                      3.141592653589793
#define MAX_AFFINES_PER_LETTER  5
#define MAX_STRING_LENGTH       40
#define SPELL_IFS_FILENAME      "spell.ifs"
#define IFS_ENTRY_FORMAT        "% -9.5f"
#define FRACTINT_SCALING_FACTOR  10
#define NO_MATCH                 -1

char *string;
double spacing=2;
double total_area, total_width, total_height=10;
FILE *spell_ifs;

struct affine_struct
    {
    double width, height;
    double rot_angle, skew_angle; /* in degrees */
    double horiz_shift, vert_shift; /* after rotations */
    };

struct letter_struct
    {
    char letter;
    int affine_num;
    double width;
    struct affine_struct affines[MAX_AFFINES_PER_LETTER];
    /* calculated */
    double area;
    };

struct letter_struct letters[] =
    {
        {'A', 3, 8, {{10, 2, 78.7, 11.3, 2, 0},
                     {10, 2, -78.7, -11.3, 4, 10},
                     {2, 1.5, 0, 0, 3, 4.5},
               }
        },

        {'B', 5, 6, {{10, 2, 90, 0, 2, 0},
                     {3.2, 2, -51.3, -38.7, 2, 10},
                     {3.2, 2, -128.7, 38.7, 4, 7.5},
                     {3.2, 2, -51.3, -38.7, 2, 5},
                     {3.2, 2, -128.7, 38.7, 4, 2.5},
                    }
        },

        {'C', 3, 6, {{6, 2, 0, 0, 0, 8},
                     {6, 2, 180, 0, 6, 2},
                     {6, 2, 90, 0, 2, 2},
                    }
        },

        {'D', 4, 6, {{10, 2, 90, 0, 2, 0},
                     {4, 2, 0, 45, 2, 8},
                     {6, 2, -90, 0, 4, 8},
                     {4, 2, 0, -45, 0, 0},
                    }
        },     

        {'E', 4, 6, {{6, 2, 0, 0, 0, 8},
                     {6, 2, 0, 0, 0, 0},
                     {4, 2, 0, 0, 2, 4},
                     {6, 2, 90, 0, 2, 2},
                    }
        },

        {'F', 3, 6, {{6, 2, 0, 0, 0, 8},
                     {4, 2, 0, 0, 2, 4},
                     {8, 2, 90, 0, 2, 0},
                    }
        },

        {'G', 5, 7, {{7, 2, 0, 0, 0, 8},
                     {6, 2, 90, 0, 2, 2},
                     {5, 2, 0, 0, 0, 0},
                     {3, 2, 90, 0, 7, 0},
                     {4, 2, 0, 0, 3, 3},
                    }
        },

        {'H', 5, 6, {{4, 2, 90, 0, 2, 0},
                     {4, 2, 90, 0, 2, 6},
                     {6, 2, 0, 0, 0, 4},
                     {4, 2, -90, 0, 4, 10},
                     {4, 2, -90, 0, 4, 4},
                    }
        },

        {'I', 1, 2, {{10, 2, 90, 0, 2, 0},
                    }
        },

        {'J', 3, 6, {{8, 2, -90, 0, 4, 10},
                     {4, 2, 180, 0, 6, 2},
                     {4, 2, 90, 0, 2, 0},
                    }
        },

        {'K', 3, 6, {{10, 2, 90, 0, 2, 0},
                     {5.4, 2, 68.2, 21.8, 4, 5},
                     {5.4, 2, 111.8, -21.8, 6, 0},
                    }
        },

        {'L', 2, 6, {{8, 2, 90, 0, 2, 2},
                     {6, 2, 0, 0, 0, 0},
                    }
        },

        {'M', 4, 10, {{10, 2, 90, 0, 2, 0},
                      {10.2, 2, -68.2, -21.8, 0, 10},
                      {10.2, 2, 68.2, 21.8, 6, 0},
                      {10, 2, 90, 0, 10, 0},
                     }
        },

        {'N', 3, 8, {{10, 2, 90, 0, 2, 0},
                      {10.2, 2, -68.2, -21.8, 1, 10},
                      {10, 2, 90, 0, 8, 0},
                     }
        },

        {'O', 4, 6, {{6, 2, 0, 0, 0, 8},
                     {6, 2, 180, 0, 6, 2},
                     {6, 2, 90, 0, 2, 2},
                     {6, 2, -90, 0, 4, 8},
                    }
        },

        {'P', 4, 6, {{8.5, 2, 90, 0, 2, 0},
                     {4.5, 1.5, 0, 0, 0, 8.5},
                     {3.5, 1.5, -90, 0, 4.5, 10},
                     {4, 1.5, 0, 0, 2, 5},
                    }
        },

        {'Q', 5, 7.5, {{6, 2, 0, 0, 0, 8},
                       {6, 2, 180, 0, 6, 2},
                       {6, 2, 90, 0, 2, 2},
                       {6, 2, -90, 0, 4, 8},
                       {4, 2, -45, 0, 2.5, 1.5},
                      }
        },

        {'R', 5, 6, {{8.5, 2, 90, 0, 2, 0},
                     {4.5, 1.5, 0, 0, 0, 8.5},
                     {3.5, 1.5, -90, 0, 4.5, 10},
                     {4, 1.5, 0, 0, 2, 5},
                     {5.47, 2, -66, -24, 2, 5},
                    }
        },

        {'S', 5, 6, {{6, 2, 0, 0, 0, 0},
                     {4, 2, 90, 0, 6, 2},
                     {4, 2 ,180, 0, 4, 6},
                     {4, 2, 90, 0, 2, 6},
                     {4, 2, 0, 0, 2, 8},
                    }
        },

        {'T', 2, 6, {{8, 2, 90, 0, 4, 0},
                     {6, 2, 0, 0, 0, 8},
                    }
        },

        {'U', 3, 6, {{8, 2, -90, 0, 4, 10},
                     {6, 2, 180, 0, 6, 2},
                     {8, 2, 90, 0, 2, 2},
                    }
        },

        {'V', 2, 8, {{10.4, 2, -73.3, -16.7, 0, 10},
                     {10.4, 2, 73.3, 16.7, 5, 0},
                    }
        },

        {'W', 4, 12, {{10.2, 2, -78.7, -11.3, 0, 10},
                      {6.7, 2, 63.4, 26.6, 4, 0},
                      {6.7, 2, -63.4, -26.6, 5, 6},
                      {10.2, 2, 78.7, 11.3, 10, 0},
                     }
        },

        {'X', 2, 7, {{11.2, 2, 63.4, 26.6, 2, 0},
                     {11.2, 2, -63.4, -26.6, 0, 10},
                    }
        },

        {'Y', 3, 8, {{5, 2, 90, 0, 5, 0},
                     {5.83, 2, 59, 31, 5, 5},
                     {5.83, 2, -59, -31, 0, 10},
                    }
        },

        {'Z', 3, 6, {{6, 2, 0, 0, 0, 0},
                     {6, 2, 0, 0, 0, 8},
                     {7.2, 2, 56.3, 33.7, 2, 2},
                    }
        },

        {'-', 1, 6, {{6, 2, 0, 0, 0, 4},
                    }
        },

        {'+', 2, 6, {{6, 2, 0, 0, 0, 4},
                     {6, 2, 90, 0, 4, 2},
                    }
        },

        {'=', 2, 6, {{6, 2, 0, 0, 0, 6},
                     {6, 2, 0, 0, 0, 2},
                    }
        },

        {'/', 1, 7, {{11.2, 2, 63.4, 26.6, 2, 0},
                    }
        },

        {'\\', 1, 7, {{11.2, 2, -63.4, -26.6, 0, 10},
                     }
        },

        {'!', 2, 2, {{6, 2, 90, 0, 2, 4},
                     {2, 2, 0, 0, 0, 0},
                    }
        },

        {'?', 5, 6, {{2, 2, 90, 0, 2, 6},
                     {4, 2, 0, 0, 0, 8},
                     {4, 2, -90, 0, 4, 10},
                     {3.6, 2, -123.7, 33.7, 4, 6},
                     {2, 2, 0, 0, 2, 0},
                    }
        },

        {'_', 0, 4}, /* to be used as a space character */

        {'\0' /* terminating character */
        }
    };


/* these could be all calculated at compile time, but I'm much too lazy. */
void init_letters(void)
    {
    int l, a;

    l=0;
    while (letters[l].letter != '\0')
        {
        letters[l].area = 0;
        for (a=0; a<letters[l].affine_num; a++)
            {
            letters[l].affines[a].width /= FRACTINT_SCALING_FACTOR;
            letters[l].affines[a].height /= FRACTINT_SCALING_FACTOR;
            letters[l].affines[a].horiz_shift /= FRACTINT_SCALING_FACTOR;
            letters[l].affines[a].vert_shift /= FRACTINT_SCALING_FACTOR;
            letters[l].area += letters[l].affines[a].width * letters[l].affines[a].height;
            }
        letters[l].width /= FRACTINT_SCALING_FACTOR;
        l++;
        }
    spacing /= FRACTINT_SCALING_FACTOR;
    total_height /= FRACTINT_SCALING_FACTOR;
    }

int get_letter(int c)
    {
    int l=0;

    while (letters[l].letter != (char)c && letters[l].letter != '\0')
        l++;
    return letters[l].letter != '\0' ? l : NO_MATCH;
    }

void get_totals(void)
    {
    int c;
    int i, l;

    total_area = 0;
    total_width = 0;
    i = 0;
    while ((c=string[i]) != '\0')
        {
        l = get_letter(toupper(c));
        if (l != NO_MATCH)
            {
            total_area += letters[l].area;
            total_width += letters[l].width + spacing;
            }
        i++;
        }
    }

void process_string(void)
    {
    int c;
    int i, l, a;
    double pos, xscale, yscale, tan_phi, sin_theta, cos_theta, xshift, yshift, prob;
    struct affine_struct af;

    get_totals();
    if (total_area == 0 || total_width == 0)
        {
        printf("Error: nothing to print\n");
        exit (3);
        }

    pos = spacing/2;
    fprintf(spell_ifs,"%s  { ; generated by ifsspell\n",string);
    i = 0;
    while ((c=string[i]) !=0)
        {
        l = get_letter(toupper(c));
        if (l != NO_MATCH)
            {
            for (a=0; a<letters[l].affine_num; a++)
                {
                af = letters[l].affines[a]; /* just to make life easier */

                xscale = af.width / total_width;
                yscale = af.height / total_height;

                tan_phi = -tan(PI/180*af.skew_angle);
                sin_theta = sin(PI/180*af.rot_angle);
                cos_theta = cos(PI/180*af.rot_angle);
                prob = af.width * af.height / total_area;
                xshift = af.horiz_shift+pos;
                yshift = af.vert_shift;

                /* rotation_matrix * skew_matrix * scale_matrix * [x,y] + trans_matrix */

                /* a term */
                fprintf(spell_ifs,IFS_ENTRY_FORMAT, xscale * cos_theta);

                /* b term */
                fprintf(spell_ifs,IFS_ENTRY_FORMAT, yscale * (cos_theta*tan_phi - sin_theta));

                /* c term */
                fprintf(spell_ifs,IFS_ENTRY_FORMAT, xscale * sin_theta);

                /* d term */
                fprintf(spell_ifs,IFS_ENTRY_FORMAT, yscale * (sin_theta*tan_phi + cos_theta));

                /* e term */
                fprintf(spell_ifs,IFS_ENTRY_FORMAT, xshift);

                /* f term */
                fprintf(spell_ifs,IFS_ENTRY_FORMAT, yshift);

                /* p term */
                fprintf(spell_ifs,IFS_ENTRY_FORMAT"\n", prob);
                }
            pos += letters[l].width + spacing;
            }
        fprintf(spell_ifs,"\n"); /* separate letters with an extra line */
        i++;
        }
    fprintf(spell_ifs,"}\n\n");
    }



void init_spell_ifs_file(void)
    {
#ifdef DEBUG
    spell_ifs = fopen(SPELL_IFS_FILENAME, "wt");
#else
    spell_ifs = fopen(SPELL_IFS_FILENAME, "at");
#endif
    if (spell_ifs == NULL)
        {
        printf("Error opening %s\n", SPELL_IFS_FILENAME);
        exit (2);
        }
    }

void close_spell_ifs_file(void)
    {
    fclose(spell_ifs);
    }

main(int argc, char *argv[])
    {
    int c;
    int l;

    if (argc < 2)
        {
        printf("Usage: ifsspell string\n"
               "available characters: ");
        l = 0;
        while ((c=letters[l++].letter) != '\0')
            putchar(c);
        putchar('\n');
        return 1;
        }

    init_letters();
    string = argv[1];
    init_spell_ifs_file();
    process_string();
    close_spell_ifs_file();
    return 0;
    }
