home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Virtual Reality Zone
/
VRZONE.ISO
/
mac
/
PC
/
MISC3D
/
3DKIT1
/
SHRINK.C
< prev
next >
Wrap
Text File
|
1992-09-08
|
10KB
|
339 lines
/* shrink.c - minimize 3dv file size */
/* Oscar Garcia <garciao@mof.govt.nz>, May 1992 */
#define NDEBUG
/* uses getopt */
int getopt(int argc, char *argv[], char *options);
extern int optind;
extern char *optarg;
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <mem.h>
#include <alloc.h>
#define USAGE "usage: shrink [/on] [/tx] [/q] [input_file [output_file]]\n\
\tIf file names are omitted, the standard i/o streams are used.\n\
\tOptions (with defaults):\n\
\t\t/o3 - Optimization level (1, 2, or 3)\n\
\t\t/t1e6 - Points closer than (range / xx) considered equal\n\
\t\t/q - Quiet, no progress info displayed\n"
#define ERROR(msg) {fputs(msg,stderr),exit(1);}
#define PERROR(msg) {perror(msg),exit(1);}
#define BIG 3e33
#define ODEFAULT 3
#define TDEFAULT 1e6
typedef float POINT[3];
typedef struct
{ int from, to, colour;
}LINESTR;
/* test if two points are close enough */
int samepoint(POINT p, POINT q, double tol)
{ int i = 3;
while (i-- > 0)
if (fabs(*p++ - *q++) > tol)
return 0;
return 1;
}
void main(int argc, char* argv[])
{
int i, j, k, n, m, p, col, first, last, *newi,
oplevel = ODEFAULT, quiet = 0;
double u, tolerance = TDEFAULT;
unsigned long memory;
char options[] = "o:t:q";
FILE *infile = stdin, *outfile = stdout;
POINT min, max, *x, *this, *seen;
LINESTR *line;
/* get options */
while ((n = getopt(argc, argv, options)) != EOF)
if (n == '?')
ERROR(USAGE)
else
switch (n)
{ case 'o': oplevel = atoi(optarg); break;
case 't': tolerance = atof(optarg); break;
case 'q': quiet = 1; break;
}
if (oplevel < 1 || oplevel > 3 || tolerance <= 0)
ERROR(USAGE);
if (!quiet)
fprintf(stderr, "SHRINK: 3dv file optimizer starting...\n");
/* open files */
if (optind < argc - 2)
ERROR(USAGE); /* too many */
if (optind < argc)
{ infile = fopen(argv[optind], "rt");
if (infile == NULL)
ERROR("Can't open input file");
if (++optind < argc)
{ outfile = fopen(argv[optind], "wt");
if (outfile == NULL)
ERROR("Can't open output file");
}
}
/* read points */
i = fscanf(infile, "%d", &n);
if (i != 1 || ferror(infile))
PERROR("Bad input file");
newi = calloc(n + 1, sizeof(int)); /* space for new indices */
#ifdef __MSDOS__
if ((unsigned)n * sizeof(POINT) > 0xfff0U)
ERROR("64k memory block limit exceeded");
#endif
x = malloc((unsigned)n * sizeof(POINT));
if (x == NULL)
ERROR("Out of memory");
for (j = 0; j < 3; j++)
{ min[j] = BIG;
max[j] = -BIG;
}
if (!quiet)
fprintf(stderr, "SHRINK: Reading %d points...\n", n);
for (i = 0; i < n; i++)
{ fscanf(infile, "%f %f %f", &x[i][0], &x[i][1], &x[i][2]);
for (j = 0; j < 3; j++)
{ if (x[i][j] < min[j])
min[j] = x[i][j];
else if (x[i][j] > max[j])
max[j] = x[i][j];
}
}
if (ferror(infile))
PERROR("Error reading input");
/* check for duplicated points */
if (!quiet)
fprintf(stderr, "SHRINK: OK, checking for duplicates...\n");
for (j = 0, u = -BIG; j < 3; j++)
if (u < max[j] - min[j])
u = max[j] - min[j];
tolerance = u / tolerance; /* comparison tolerance */
for (this = x, i = j = 0; i++ < n; this++)
{ for (seen = this; seen-- > x; )
if (samepoint(*this, *seen, tolerance))
{ newi[i] = -abs(newi[(unsigned)(seen-x)+1]); /* flag to del.*/
break;
}
if (newi[i] == 0)
newi[i] = ++j;
}
/* output points, without duplicates */
if (!quiet)
fprintf(stderr, "SHRINK: Writing coordinates for %d points...\n", j);
fprintf(outfile, "%d\n", j); /* number of points */
for (i = 0; i < n; i++)
{ if (newi[i + 1] < 0)
newi[i + 1] = - newi[i + 1];
else
fprintf(outfile, "%g %g %g\n", x[i][0], x[i][1], x[i][2]);
}
n = j; /* new number of points */
free(x);
/* read lines, adjusting indices */
fscanf(infile, "%d", &m); /* number of lines */
if (!quiet)
fprintf(stderr, "SHRINK: Reading %d line move/draw commands...\n", m);
memory = coreleft();
#ifdef __MSDOS__
if (memory > 0xfff0U)
memory = 0xfff0U; /* avoid segment wraparound */
#endif
memory = memory / sizeof(LINESTR);
if (memory > m + 2)
memory = m + 2; /* upper bound for number of segments */
line = malloc(memory * sizeof(LINESTR));
if (line == NULL)
ERROR("Memory allocation failure"); /* should not happen */
for (i = 1; m > 0 && i < memory; m--)
{ fscanf(infile, "%d %d", &j, &col);
j = newi[j];
if (col == 0)
line[i].from = j;
else
{ line[i].to = j;
line[i].colour = col;
line[++i].from = j;
}
}
if (i >= memory)
ERROR("Out of memory");
line[i].from = -1; /* sentinel */
m = i - 1; /* number of line segments */
if (ferror(infile))
PERROR("Error reading input");
if (feof(infile))
ERROR("Unexpected end of file on input");
fclose(infile);
if (oplevel >1)
{ /* check for duplicated lines */
if (!quiet)
fprintf(stderr,
"SHRINK: Checking %d line segments for duplicates...\n", m);
k = m; /* count */
for (i = 1; i <= m; i++)
for (j = 1; j < i; j++)
if ((line[i].from == line[j].from && line[i].to == line[j].to)
|| (line[i].from == line[j].to && line[i].to == line[j].from))
{ line[j].colour |= line[i].colour;
line[i].from = -1; /* flag for deletion */
k--; /* count */
break;
}
if (!quiet)
fprintf(stderr, "SHRINK: %d segments left.\n", k);
}
if (oplevel > 2)
{
/* minimize number of moves */
if (!quiet)
fprintf(stderr, "SHRINK: Minimizing moves...\n");
/* Chains of line segments built as linked lists, using line[].to as
pointers to next segment. Last line[].to in chain in the last point
number, flagged with a minus sign. Re-use newi[p] to point to chain
starting (positive) or ending (negative) with point p. Actually,
use array indices instead of real pointers.
*/
memset(newi, 0, (n+1) * sizeof(int)); /* zero chain pointers array */
for (i = 1; i <= m; i++)
if ((first = line[i].from) > 0) /* valid segment */
{ if ((j = newi[first]) == 0) /* other chains start/end at first? */
newi[first] = i; /* no, flag this one */
else if (j < 0)
{ /* chain at -j ends with first, link it to here */
newi[first] = 0; /* not end of chain anymore */
first = line[-j].from; /* new start of chain */
for (j = -j; line[j].to > 0; j = line[j].to)
; /* follow chain, last point flagged with minus */
assert(-line[j].to == line[i].from); /* debugging check */
line[j].to = i; /* point to current segment */
}
else
{ /* chain at j starts with first, reverse it and link */
assert(line[i].from == line[j].from);
newi[first] = 0; /* not start of chain anymore */
p = i; /* point to here */
while ((first = -line[j].to) < 0) /* reverse */
{ line[j].to = p; /* point to previous segment */
p = j; /* next one should point to here */
j = -first; /* next segment index */
line[p].from = line[j].from;
}
line[j].from = first; /* new first segment */
line[j].to = p;
newi[first] = j; /* point to it */
}
/* now link consecutive segments */
while (line[i+1].from == line[i].to)
{ line[i].to = i + 1;
i++;
}
last = line[i].to; /* last point */
line[i].to = -last; /* flag end of chain */
/* see if other chains start/end at last */
if (last == first)
; /* but avoid chasing your tail! */
else if ((j = newi[last]) == 0)
newi[last] = -newi[first]; /* flag this end */
else if (j > 0)
{ /* chain at j starts with last, link to it */
newi[last] = 0; /* not start of chain anymore */
assert(-line[i].to == line[j].from); /* debugging check */
line[i].to = j; /* point to it */
while (line[j].to > 0)
j = line[j].to; /* follow chain to last point */
newi[-line[j].to] = -newi[first]; /* end pointer */
}
else
{ /* chain at -j ends with last, reverse and link to it */
newi[last] = 0; /* not end of chain anymore */
p = -line[j = -j].from; /* new end of chain */
newi[-p] = - newi[first]; /* end pointer */
while ((last = -line[j].to) < 0) /* reverse */
{ line[j].to = p; /* point to previous segment */
p = j; /* next one should point to here */
j = -last; /* next segment index */
line[p].from = line[j].from;
}
assert(-line[i].to == last); /* check */
line[j].from = last; /* new first segment */
line[j].to = p;
line[i].to = j; /* point to it */
}
}
/* output lines */
/* first count the moves/draws */
for (m = 0, i = 1; i <= n; i++)
if (newi[i] > 0)
{ m++; /* move */
for (j = newi[i]; j > 0; j = line[j].to)
m++; /* draw */
}
fprintf(outfile, "%d\n", m); /* output the count */
/* now output the moves/draws */
if (!quiet)
fprintf(stderr,
"SHRINK: OK, Writing %d line move/draw commands...\n", m);
for (i = 1; i <= n; i++)
if (newi[i] > 0)
{ for (j = newi[i], col = 0; j > 0; j = line[j].to)
{ fprintf(outfile, "%d %d\n", line[j].from, col);
col = line[j].colour;
}
fprintf(outfile, "%d %d\n", -j, col);
}
}
else /* optlevel < 3, no lines optimization */
{
/* just output the lines */
for (i = 1, j = -1, n = 0; i <= m; i++) /* count moves/draws */
if (line[i].from > 0)
{ if (line[i].from != j)
n++; /* move */
j = line[i].to;
n++; /* draw */
}
fprintf(outfile, "%d\n", n);
if (!quiet)
fprintf(stderr,
"SHRINK: Writing %d line move/draw commands...\n", n);
for (i = 1, j = -1; i <= m; i++) /* output moves/draws */
if (line[i].from > 0)
{ if (line[i].from != j)
fprintf(outfile, "%d 0\n", line[i].from); /* move */
j = line[i].to;
fprintf(outfile, "%d %d\n", j, line[i].colour); /* draw */
}
}
if (ferror(outfile))
PERROR("Error writing output");
fclose(outfile);
if (!quiet)
fprintf(stderr, "SHRINK: Done.\n");
}