/*------------------------------------------------------------------------*/
/* */
/* CONVERT.CPP */
/* */
/* Scott Derrick */
/* cmp140 spring quarter ucsc */
/* */
/* convert character representation */
/* */
/*------------------------------------------------------------------------*/
/*
I wrote this utilty to convert the artificial character dataset's
representation from a list of line segments to a 12x8 array of 1's and 0's.
You may copy, use ,distribute or modify this code.
I compiled this with Borland's NT 4.0 & OS/2 1.5 compilers
use > bcc convert.cpp
I compiled this with gcc
use % g++ convert.cc -o convert -ml
outputs two files: pattern, this is the input test patterns
: target, this is the target patterns
outputs one file: pattern, input pattern followed by target pattern
outputs the pattern file in two formats,
both formats represent the character as a 12x8 grid of pixels, a 0 is
off pixel, a 1 is a on pixel.
the input file represents the character as a collection of line segments
specified as two pairs of x,y coords and a diagonal ratio indicating the
minimal rectagle that would contain the character. The output character
is sized to fit the grid if needed.
This is the input file, the first number represents the character, 1 == a.
The second number is the line segment number. The string 'line' is
extraneous. The next four digits represent the two xy pairs, x1 y1 x2 y2.
The next float is the line segment length, The next float is the diagonal
size of the rectangle that would contain the character.
1 0 line 0 0 0 13 13.00 45.28
1 1 line 20 0 22 15 15.13 45.28
1 2 line 0 13 22 15 22.09 45.28
1 3 line 0 13 0 27 14.00 45.28
1 4 line 22 15 23 39 24.02 45.28
1 5 line 0 27 23 39 25.94 45.28
This is the array representation of the output pattern file.
1 2 3 4 5 6 7 8 columns
1 0 0 0 0 0 0 1 1
2 0 0 0 0 1 1 0 1
3 0 0 1 1 0 0 0 1
4 1 1 0 0 0 0 0 1
5 1 0 0 0 0 0 1 0
6 1 0 0 0 0 0 1 0
7 1 0 0 0 0 0 1 0
8 1 1 1 1 1 1 1 0
9 1 0 0 0 0 0 1 0
10 1 0 0 0 0 0 1 0
11 1 0 0 0 0 0 1 0
12 1 0 0 0 0 0 1 0
r
o
w
s
Two different pattern files may be generated.
The first presents the pattern to the network as a one large pattern. The
rows are concated together in one string -> 123456789101112
The second presents the pattern to the network as 6 grids. The upper left
quadrant, followed by the upper right, followed by the middle left......
The pixels would be in row column representation
-> 11 12 13 14 21 22 23 24 31 32 33 34 41 42 43 44
for the first quadrant, then followed by the pixels for the next quadrant.
The target file is a 1x10 array, each slot represents a character. All slots
are zero except for the correct character which is set to 1.
1 0 0 0 0 0 0 0 0 0
This would be the target for the character 'A'.
*/
#define NODEBUG 1 // comment out for debug output
#define MAXPATH 167 // added for bug in dir.h gcc version
#ifndef __DIR_H
// #include
#endif
#ifndef __STRING_H
#include
#endif
#ifndef __STDLIB_H
#include
#endif
#ifndef __STDIO_H
#include
#endif
#ifndef __MATH_H
#include
#endif
#ifndef __FSTREAM_H
#include
#endif
#ifndef __IOSTREAM_H
#include
#endif
// missing prototype from gcc headers
char *strlwr(char *);
enum { ONEARRAY, SIXARRAY, ONEFILE, TWOFILE };
#define LINELENGTH 50
// input a float, return an int rounded up if > xx.5 , rounded
// down if <= xx.5
int round(float num)
{
int stub = (int)floor(num);
float rem = num - stub;
if(rem > .5)
stub++;
return(stub);
}
// convert class, to convert a character reprensented as line segments
// to a 12x8 pixel array, the character is sized to fill the grid if needed
class Convert
{
public:
Convert( char *, int, int, int, int, const char *);
~Convert(void);
void Show(void);
void scanSource(void);
void fillGrid(const char *line);
void output(void);
void targetOutput(void);
private:
fstream *target, *pattern, source; // input & output streams
int low, high; // range of characters
int otype; // output type
int ftype; // one or two output files
short grid[12][8]; // the pattern
char *inputFile; // input file
char currentChar;
};
// destructor
Convert::~Convert(void)
{
if(inputFile)
delete(inputFile);
}
// converts constructor, pass in the input file specs and the output
// representation type
Convert::Convert( char *characters, int rlow, int rhigh, int arrayType,
int fileType, const char *path)
{
fstream pat, tar;
low = rlow;
high = rhigh;
otype = arrayType;
ftype = fileType;
inputFile = new char[MAXPATH];
char *inputPtr;
char currNum[4];
int charCount = 0;
int numCount;
// figure out the path if the source files are in a different dir
if(path)
{
strcpy(inputFile,path);
int len = strlen(inputFile);
inputPtr = inputFile + len;
}
else
inputPtr = inputFile;
// open the output streams
pat.open("pattern", ios::out);
pattern = &pat;
if(ftype == TWOFILE)
{
tar.open("target", ios::out);
target = &tar;
}
else
target = &pat;
for(numCount = low; numCount <= high; numCount++)
{
charCount = 0;
while(characters[charCount])
{
// build input file name
inputPtr[0] = currentChar = characters[charCount++];
inputPtr[1] = 0;
sprintf(currNum, "%d", numCount);
strcat(inputFile, currNum);
source.open(inputFile, ios::in);
if(source.fail())
{
cerr << "Error opening " << inputFile << '\n';
exit(1);
}
// scan & generate the output
scanSource();
source.close();
output();
}
}
tar.close();
pat.close();
}
// output target file
void Convert::targetOutput(void)
{
if(target->good())
{
switch(currentChar)
{
case 'a':
*target << "1 0 0 0 0 0 0 0 0 0" << endl;
break;
case 'c':
*target << "0 1 0 0 0 0 0 0 0 0" << endl;
break;
case 'd':
*target << "0 0 1 0 0 0 0 0 0 0" << endl;
break;
case 'e':
*target << "0 0 0 1 0 0 0 0 0 0" << endl;
break;
case 'f':
*target << "0 0 0 0 1 0 0 0 0 0" << endl;
break;
case 'g':
*target << "0 0 0 0 0 1 0 0 0 0" << endl;
break;
case 'h':
*target << "0 0 0 0 0 0 1 0 0 0" << endl;
break;
case 'l':
*target << "0 0 0 0 0 0 0 1 0 0" << endl;
break;
case 'p':
*target << "0 0 0 0 0 0 0 0 1 0" << endl;
break;
case 'r':
*target << "0 0 0 0 0 0 0 0 0 1" << endl;
break;
}
}
}
// output grid to pattern & target files
void Convert::output(void)
{
if(pattern->good())
{
int k, l;
// output as one big picture line by line
if(otype == ONEARRAY)
{
for(k = 11; k >= 0; k--)
{
for(l = 0; l < 8; l++)
*pattern << grid[k][l] << ' ';
}
}
// output as 6 quadrants each quadrant line by line
else
{
for(k = 11; k >= 8; k--)
{
for(l = 0; l < 4; l++)
*pattern << grid[k][l] << ' ';
}
for(k = 11; k >= 8; k--)
{
for(l = 4; l < 8; l++)
*pattern << grid[k][l] << ' ';
}
for(k = 7; k >= 4; k--)
{
for(l = 0; l < 4; l++)
*pattern << grid[k][l] << ' ';
}
for(k = 7; k >= 4; k--)
{
for(l = 4; l < 8; l++)
*pattern << grid[k][l] << ' ';
}
for(k = 3; k >= 0; k--)
{
for(l = 0; l < 4; l++)
*pattern << grid[k][l] << ' ';
}
for(k = 3; k >= 0; k--)
{
for(l = 4; l < 8; l++)
*pattern << grid[k][l] << ' ';
}
}
if(ftype == TWOFILE)
*pattern << endl;
else
*pattern << " ";
// output target vector
targetOutput();
}
}
// reads one line at a time and calls fillgrid to store *pattern
void Convert::scanSource(void)
{
#ifndef NODEBUG
cout << inputFile << endl;
#endif
char line[LINELENGTH];
for(int i = 0;i < 12; i++)
for(int j = 0; j < 8;j++)
grid[i][j] = 0;
while(!source.eof())
{
source.getline(line, LINELENGTH);
#ifndef NODEBUG
cout << line << endl;
#endif
if(line[0])
fillGrid(line);
}
if(source.fail())
source.clear();
// for debug output
#ifndef NODEBUG
for(int k = 11; k >= 0; k--)
{
for(int l = 0; l < 8; l++)
cout << grid[k][l] << ' ';
cout << endl;
}
#endif
}
void Convert::fillGrid(const char *line)
{
int xy[4];
int count;
float size, val, slope, xflt, yflt;
char tline[LINELENGTH];
strcpy(tline, line);
char *token = strstr(tline, "line");
token = strtok(token," ");
// retrieve the x & y values & the size value
for(int i = 0; i < 4; i++)
{
token = strtok(NULL," ");
xy[i] = atoi(token);
}
strtok(NULL," ");
token = strtok(NULL," ");
size = atof(token);
val = size/14;
// adjust the x & y coords to fit into a 12x8 grid
for(int j = 0; j < 4; j++)
{
xy[j] = (int)(xy[j] / val);
if((xy[j] > 11) && ((j==1)||(j==3)))
xy[j] = 11;
if((xy[j] > 7) && ((j==0)||(j==2)))
xy[j] = 7;
}
float den = xy[2] - xy[0];
if(den < 0) // the line is drawing from right to left, switch it back
{
int tempval = xy[0];
xy[0] = xy[2];
xy[2] = tempval;
tempval = xy[1];
xy[1] = xy[3];
xy[3] = tempval;
den = xy[2] - xy[0];
}
// don't devide by 0!!
if(den)
slope = ((float)(xy[3] - xy[1]))/den; // this is x/y
else
slope = 999999999999.0;
if((slope > 1) || (slope < -1)) // index off of y axis
{
xflt = xy[0];
if(slope > 1)
{
for(count = xy[1]; count <= xy[3]; count++)
{
grid[count][round(xflt)] = 1;
xflt += 1/slope;
}
}
else // negative slope
{
for(count = xy[1]; count >= xy[3]; count--)
{
grid[count][round(xflt)] = 1;
xflt -= 1/slope;
}
}
}
else // index off of x axis
{
yflt = xy[1];
for(count = xy[0]; count <= xy[2]; count++)
{
grid[round(yflt)][count] = 1;
yflt += slope;
}
}
}
int main( int argc, char *argv[] )
{
int atype = ONEARRAY;
int ftype = TWOFILE;
int low = 1;
int high = 10;
char chars[] = "acdefghlpr";
char *thepath = 0;
if( argc < 5 )
{
cerr << "Usage: convert [-output] [acdefghlpr] x1 x2 [path]" << endl << endl;
cerr << "output:" << endl;
cerr << "\t-1\toutput *pattern & *target in one file" << endl;
cerr << "\t-2\toutput *pattern & *target in two files" << endl;
cerr << "\t-c\trepresent as 12x8 array" << endl;
cerr << "\t-d\trepresent as 6 4x4 arrays" << endl;
cerr << "\nacdefghlpr: specify characters to convert" << endl;
cerr << "\nx1 x2: specify range to convert, x1 < x2, and 1 to 500" << endl;
cerr << "\npath: specify optional path to source files" << endl;
cerr << "\n\nexample: >convert -2c acdefgh 1 25 source" << endl;
exit(1);
}
else
{
if( strchr( argv[1], 'd' ) )
atype = SIXARRAY;
if( strchr( argv[1], '1' ) )
ftype = ONEFILE;
strncpy(chars, argv[2], 10);
// strlwr(chars); /****this function missing in gcc ******/
low = atoi(argv[3]);
high = atoi(argv[4]);
if( !low || !high || (low > high))
{
cerr << "ERROR: incorrect values for x1 or x2" << endl;
exit(1);
}
if(argc > 5)
thepath = argv[5];
Convert(chars, low, high, atype, ftype, thepath);
}
return 0;
}