1 /* Copyright (C) 1992-1998 The Geometry Center
2  * Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips
3  *
4  * This file is part of Geomview.
5  *
6  * Geomview is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * Geomview is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with Geomview; see the file COPYING.  If not, write
18  * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
19  * USA, or visit http://www.gnu.org.
20  */
21 
22 #if 0
23 static char copyright[] = "Copyright (C) 1992-1998 The Geometry Center\n\
24 Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips";
25 #endif
26 
27 # include "config.h"
28 
29 /* math2oogl: convert Mathematica graphics object to OOGL format.
30    SurfaceGraphics and MeshGraphics => MESH, Graphics3D => OFF,
31    BezierPatch => BEZuvn.
32 
33    Note that we expect the graphics objects to have been processed as
34    in OOGL.m, i.e. provide dimension and meshrange information and
35    print out the colors before the points for SurfaceGraphics objects,
36    and convert the characters "(){}, " to a newline.
37 
38    Original converter by Nils McCarthy,
39    Geomview pipefitting by Stuart Levy
40    SurfaceGraphics converter additions by Tamara Munzner.
41    MeshGraphics and BezierPatch converter additions by Silvio Levy
42 */
43 
44 
45 
46 /*
47  * Pipe fitting for linking Mathematica to geomview.
48  * Starts geomview if not already running.
49  */
50 
51 
52 #include <stdio.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #if defined(unix) || defined(__unix) || defined(__unix__)
56 #include <signal.h>
57 #include <sys/file.h>
58 #include <sys/socket.h>
59 #include <sys/un.h>
60 #endif
61 #include <sys/errno.h>
62 #include <string.h>
63 #include <stdlib.h>
64 #include <ctype.h>
65 
66 #ifdef AIX
67 #define _BSD 1		/* Get FNDELAY from <fcntl.h> */
68 #endif
69 #include <fcntl.h>
70 
71 #ifdef NeXT
72 #include <bsd/libc.h>
73 #else  /* any other reasonable unix */
74 #include <unistd.h>
75 #endif
76 
77 #ifdef SVR4	/* What should this be called? */
78 #include <sys/systeminfo.h>
79 #endif
80 
81 #ifndef FNDELAY
82 # define FNDELAY  O_NDELAY
83 #endif
84 
85 #ifndef FNONBLK		/* Next 3.0 lacks these in <sys/fcntl.h> */
86 # define FNONBLK  FNDELAY
87 #endif
88 
89 #ifndef O_NONBLOCK
90 # define O_NONBLOCK FNONBLK
91 #endif
92 
93 #ifndef FD_CLOEXEC
94 #define FD_CLOEXEC 1
95 #endif
96 
97 extern int errno;
98 
99 char *todir = "/tmp/geomview";
100 char *toname = "Mathematica";
101 
102 char giveup[] = "Geomview graphics: math2oogl: Couldn't start geomview on ";
103 
104 /* Fool some Linux distros which are too eager to improve the quality
105  * of source code and turn on FORTIFY_SOURCE by default for the
106  * C-compiler gcc.
107  */
ign_write(int fd,void * buffer,size_t size)108 static int ign_write(int fd, void *buffer, size_t size)
109 {
110   return write(fd, buffer, size);
111 }
112 
ign_dup(int fd)113 static int ign_dup(int fd)
114 {
115   return dup(fd);
116 }
117 
interrupt(int sig)118 static void interrupt(int sig) {
119     char myname[1024];
120     ign_write(2, giveup, sizeof(giveup));
121 
122 #ifdef SVR4
123 	/* jkirk@keck.tamu.edu reports no gethostname() in Solaris (SVR4).
124 	 * Use sysinfo() instead.
125 	 */
126     sysinfo(SI_HOSTNAME, myname, sizeof(myname));
127 #else
128     gethostname(myname, sizeof(myname));
129 #endif
130 
131     ign_write(2, myname, strlen(myname));
132     ign_write(2, "\n", 1);
133     exit(1);
134 }
135 
start_gv(char ** gvpath,char * pipename,char * toname)136 void start_gv(char **gvpath, char *pipename, char *toname)
137 {
138     char *args[1024];
139     int i = 0;
140     char **gvp;
141 
142     signal(SIGALRM, interrupt);
143     args[i++] = gvpath[0];
144 
145 #ifdef NeXT
146     args[i++] = "-Mc";  args[i++] = toname;
147 #else /* sgi */
148     args[i++] = "-c";   args[i++] = pipename;
149 #endif
150 	/* Copy remaining args through trailing NULL */
151     for(gvp = gvpath; (args[i++] = *++gvp); )
152 	;
153 
154     if(fork() == 0) {
155 	int savederr = dup(2);
156 	static char whynot[] = "Geomview graphics: math2oogl: Couldn't find ";
157 
158 	fcntl(savederr, F_SETFD, FD_CLOEXEC);	/* close this on exec */
159 	close(0); close(1); close(2);
160 
161 
162 	open("/dev/null", O_RDWR);	/* Open /dev/null as file descriptors */
163 	ign_dup(0);  ign_dup(0);	/* 0(stdin), 1(stdout) and 2(stderr) */
164 		/* (Could just close them, but that seems to poison geomview
165 		 * if it tries to report an error.)
166 		 */
167 
168 #if SETPGRP_VOID
169 	setpgrp();
170 #else
171 	setpgrp(0,getpid());
172 #endif
173 	execvp(gvpath[0], &args[0]);
174 	ign_write(savederr, whynot, sizeof(whynot));
175 	ign_write(savederr, gvpath[0], strlen(gvpath[0]));
176 	ign_write(savederr, "\n", 1);
177 	execvp("geomview", &args[0]);
178 	execvp("gv", &args[0]);
179 
180 	dup2(savederr, 2);
181 	kill(getppid(), SIGALRM);
182 	interrupt(0);
183 	_exit(1);
184     }
185 }
186 
startgv(char ** gvpath)187 void startgv(char **gvpath)
188 {
189     int usesock;
190     int n, fd = -1;
191     char pipename[BUFSIZ];
192     struct sockaddr_un un;
193 
194     if(access(todir, W_OK) < 0) {
195 	mkdir(todir, 0777);
196 	chmod(todir, 0777);
197     }
198     sprintf(pipename, "%s/%s", todir, toname);
199 
200 #ifdef NeXT
201     usesock = 1;
202 #else
203     usesock = 0;
204 #endif
205 
206     if(usesock) {
207 	strncpy(un.sun_path, pipename, sizeof(un.sun_path)-1);
208 	un.sun_family = AF_UNIX;
209 	fd = socket(PF_UNIX, SOCK_STREAM, 0);
210 	if(connect(fd, (struct sockaddr *)(&un), sizeof(un)) < 0) {
211 	    if(errno != ECONNREFUSED && errno != ENOENT) {
212 		fprintf(stderr, "togeomview: Can't connect to ");
213 		perror(pipename);
214 		exit(1);
215 	    }
216 
217 	    start_gv(gvpath, pipename, toname);
218 	    for(n = 0; connect(fd, (struct sockaddr *)(&un), sizeof(un)) < 0; n++) {
219 		if(n == 60)
220 		    interrupt(0);
221 		sleep(1);
222 	    }
223 	}
224     } else {
225 	/* Use named pipe */
226 	if(access(pipename, 0) < 0) {
227 	    mknod(pipename, S_IFIFO, 0);
228 	    chmod(pipename, 0666);
229 	}
230 	fd = open(pipename, O_WRONLY|O_NONBLOCK);
231 	if(fd >= 0) {
232 	    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~(FNDELAY|FNONBLK|O_NONBLOCK));
233 	} else if(errno == ENXIO) {
234 	    start_gv(gvpath, pipename, toname);
235 	    alarm(60);
236 	    fd = open(pipename, O_WRONLY);
237 	    alarm(0);
238 	}
239     }
240     if(fd < 0) {
241 	fprintf(stderr, "Can't open pipe to geomview: ");
242 	perror(pipename);
243 	exit(1);
244     }
245 
246     /* Now stdout writes to pipe. */
247     dup2(fd, 1);
248 }
249 
250 
251 /*
252  * Mathematica->OOGL conversion
253  */
254 
255 typedef float Color[3];
256 
257 #define ismajor isupper
258 
259 /*
260  * The following table should always obey the convention that "major"
261  * tokens (corresponding to the cases in the big switch below)
262  *  are represented by capitals
263  */
264 
265 enum st {
266   IGNORE='i',
267   POLYGON='p',
268   LINE='l',
269   COLOR='c',
270   NUMBER='n',
271   MESH='m',
272   MESHRANGE='k',
273   DIMENSIONS='d',
274   SG='S',
275   MG='M',
276   G3='G',
277   BG='B'
278 };
279 
280 struct line {
281   char *data;
282   enum st token;
283   struct line *next;
284 };
285 
286 struct line *lines=NULL;
287 int size[2];
288 float range[4];
289 
usage()290 void usage()
291 {
292   fprintf(stderr,"Usage:  math2oogl [-togeomview <objectname>]");
293   exit(1);
294 }
295 
main(int ac,char ** av)296 int main(int ac,char **av)
297 {
298   int togv = 0;
299   struct line *lastline=NULL;
300   struct line *prev=NULL;
301   char buf[1024];
302   int npolypoints=0, npolys=0, npolycolors=0;
303   int nvectpoints=0, nvects=0, nvectcolors=0;
304   struct line *curline, *globline;
305   int numnums=0;
306   int numcols=0;
307   enum st state=IGNORE;
308   Color **colors = NULL;
309   int i,j,q,ok;
310   float xincr, yincr;
311   int complex = 0, toss = 0;
312 
313   if (ac >= 4 && (!strcmp(av[1],"-togeomview"))) {
314     startgv(&av[3]);
315     printf("(geometry %s\n", av[2]);
316     togv=1;
317   } else if (ac > 1) usage();
318 
319   for(;;) {
320     int c;
321     char *k;
322 	/* Swallow all delimiters, " (){},\n\r".
323 	 * It's not strictly correct to ignore {} nesting, but...
324 	 */
325     for(k = buf; (c = getchar()) > ' ' && c != ',' && c != '(' && c != ')'
326 			    && c != '{' && c != '}' && k < buf+sizeof(buf)-1;
327 			k++) {
328 	/* Handle escaped newlines, which Mathematica inserts just rarely
329 	 * enough to be hard to notice!
330 	 */
331 	if(c == '\\') {
332 	    if((c = getchar()) == '\n') {
333 		while((c = getchar()) == ' ' || c == '\t')
334 		    ;
335 	    }
336 	}
337 	*k = c;
338     }
339     if(c == EOF)
340 	break;
341     *k = '\0';
342     if (k == buf || !strcmp(buf,"List"))
343       continue;
344 
345     if (!lastline)
346       lastline = lines = malloc(sizeof(struct line));
347     else {
348       if (toss) lastline = prev;
349       toss = 0;
350       prev = lastline;
351       lastline = lastline->next = malloc (sizeof(struct line));
352     }
353     lastline->next = NULL;
354     lastline->data = malloc(strlen(buf)+1);
355 
356     strcpy (lastline->data, buf);
357     if (isalpha(*lastline->data)) {
358       if (!strcmp(lastline->data, "Graphics3D"))
359 	lastline->token = G3;
360       else if (!strcmp(lastline->data, "SurfaceGraphics"))
361 	lastline->token = SG;
362       else if (!strcmp(lastline->data, "MeshGraphics"))
363 	lastline->token = MG;
364       else if (!strcmp(lastline->data, "BezierPatch")||
365           !strcmp(lastline->data, "BezierGraphics`BezierPatch"))
366 	lastline->token = BG;
367       else if (!strcmp(lastline->data, "Polygon"))
368 	lastline->token = POLYGON;
369       else if (!strcmp(lastline->data, "Point"))
370 	lastline->token = LINE; /* degenerate vector */
371       else if (!strcmp(lastline->data, "Line"))
372 	lastline->token = LINE;
373       else if (!strcmp(lastline->data, "RGBColor"))
374 	lastline->token = COLOR;
375       else if (!strcmp(lastline->data, "MeshRange"))
376 	lastline->token = MESHRANGE;
377       else if (!strcmp(lastline->data, "Dimensions"))
378 	lastline->token = DIMENSIONS;
379       else if (!strcmp(lastline->data, "Complex")) {
380 	lastline->token = IGNORE;
381 	complex = 1;
382 	toss = 1;
383       } else {
384 	lastline->token = IGNORE;
385       }
386     } else if (isdigit(*lastline->data) || '-'==*lastline->data) {
387       lastline->token = NUMBER;
388       /* grab real part, toss imaginary part */
389       if (complex) {
390 	if (complex == 1) {
391 	  complex++;
392 	} else if (complex == 2) {
393 	  lastline->token = IGNORE;
394 	  complex = 0;
395 	  toss = 1;
396 	}
397       }
398     }
399   }
400 
401   /* we might have multiple graphics objects */
402   printf ("{ LIST\n\n");
403 
404   ok = 1;
405   globline = lines;
406   for (globline = lines;globline;) {
407     switch (globline->token) {
408     case SG: /* SurfaceGraphics */
409       globline=globline->next;
410       if (globline->token == DIMENSIONS) {
411 	globline=globline->next;
412 	for (i = 0; i < 2 && ok; i++, globline = globline->next)
413 	  if (globline->token == NUMBER)
414 	    size[i] = atoi(globline->data);
415 	  else ok = 0;
416       } else
417 	ok = 0;
418       if (!ok) {
419 	fprintf(stderr, "can't read mesh dimensions!\n");
420 	return 1;
421       }
422       if (globline->token == MESHRANGE) {
423 	globline=globline->next;
424 	for (i = 0; i < 4 && ok; i++, globline = globline->next) {
425 	  if (globline->token == NUMBER)
426 	    range[i] = atof(globline->data);
427 	  else ok = 0;
428 	}
429       } else ok = 0;
430       if (!ok) {
431 	fprintf(stderr, "can't read mesh range!\n");
432 	return 1;
433       }
434 
435       /*
436 	in SurfaceGraphics object, first we get all the colors,
437 	then all the points: they're not interleaved as in
438 	the Graphics3D object. so we need to store them.
439 
440 	points array is size[0] x size[1],
441 	color array is size[0]-1 x size[1]-1 (per face not per vertex)
442 	so repeat the face color for the extra vertices.
443 
444 	*/
445 
446       /* if there are colors, store them off */
447       if (globline->token == COLOR) {
448 	numcols = (size[0]-1) * (size[1]-1);
449 	colors = malloc((size[0]-1)*sizeof(Color*));
450 	for (i = 0; i < size[0]-1;i++)
451 	  colors[i] = malloc((size[1]-1)*sizeof(Color));
452 	ok = 1;
453 	for (i = 0; i < size[0]-1; i++) {
454 	  for (j=0; j < size[1]-1; j++) {
455 	    while (globline->token == COLOR && ok)
456 	      for (q=0; q < 3 && ok; q++) {
457 		globline = globline->next;
458 		if (globline->token != NUMBER) {
459 		  ok = 0;
460 		  break;
461 		} else {
462 		  colors[i][j][q]=atof(globline->data);
463 		}
464 	      }
465 	    globline=globline->next;
466 	  }
467 	}
468 	if (!ok) {
469 	  fprintf(stderr, "can't read mesh color array!\n");
470 	  return 1;
471 	}
472       }
473       /* Each number we get is just the z coordinate.
474 	 Figure out x and y values based on meshrange and size.
475 	 We don't use a ZMESH because the grid isn't necessarily
476 	 an array starting at 0,0: it depends on meshrange
477        */
478       xincr = (range[1] - range[0]) / (size[0]-1);
479       yincr = (range[3] - range[2]) / (size[1]-1);
480 
481       if (numcols)
482 	printf("\n\n{ CMESH \n");
483       else
484 	printf("\n\n{ MESH \n");
485       printf("\n%d %d\n",size[0], size[1]);
486       ok = 1;
487       for (i = 0; i < size[0]; i++){
488 	for (j = 0; j < size[1]; j++) {
489 	  if (globline->token!=NUMBER) {
490 	    ok = 0; break;
491 	  }
492 	  printf(" %f ", range[0] + xincr*j); /* x */
493 	  printf(" %f ", range[2] + yincr*i); /* y */
494 	  printf(" %s ", globline->data);      /* z */
495 	  /* MESH vertices need RGBA colors
496 	     since OOGL MESHes have per-vertex colors, repeat some.
497 	     repeat last i index, first j index.
498 	   */
499 	  if (numcols) {
500 	    printf(" %f %f %f 1\n", colors[i<size[0]-1?i:i-1][j?j-1:0][0],
501 		   colors[i<size[0]-1?i:i-1][j?j-1:0][1],
502 		   colors[i<size[0]-1?i:i-1][j?j-1:0][2]);
503 	  } else printf("\n");
504 	  globline=globline->next;
505 	}
506       }
507       if (!ok) {
508 	fprintf(stderr, "can't read mesh points array!\n");
509 	return 1;
510       } else
511 	printf("} #end of MESH\n");
512       break;
513     case MG:  /* MeshGraphics */
514       globline=globline->next;
515       if (globline->token == DIMENSIONS) {
516 	globline=globline->next;
517 	for (i = 0; i < 2 && ok; i++, globline = globline->next)
518 	  if (globline->token == NUMBER)
519 	    size[i] = atoi(globline->data);
520 	  else ok = 0;
521       } else
522 	ok = 0;
523       if (!ok) {
524 	fprintf(stderr, "can't read mesh dimensions!\n");
525 	return 1;
526       }
527 
528       /*
529 	in MeshGraphics object, first we get all the colors,
530 	then all the points: they're not interleaved as in
531 	the Graphics3D object. so we need to store them.
532 
533 	points array is size[0] x size[1],
534 	color array is size[0]-1 x size[1]-1 (per face not per vertex)
535 	so repeat the face color for the extra vertices.
536 
537 	*/
538 
539       /* if there are colors, store them off */
540       if (globline->token == COLOR) {
541 	numcols = (size[0]-1) * (size[1]-1);
542 	colors = malloc((size[0]-1)*sizeof(Color*));
543 	for (i = 0; i < size[0]-1;i++)
544 	  colors[i] = malloc((size[1]-1)*sizeof(Color));
545 	ok = 1;
546 	for (i = 0; i < size[0]-1; i++) {
547 	  for (j=0; j < size[1]-1; j++) {
548 	    while (globline->token == COLOR && ok)
549 	      for (q=0; q < 3 && ok; q++) {
550 		globline = globline->next;
551 		if (globline->token != NUMBER) {
552 		  ok = 0;
553 		  break;
554 		} else {
555 		  colors[i][j][q]=atof(globline->data);
556 		}
557 	      }
558 	    globline=globline->next;
559 	  }
560 	}
561 	if (!ok) {
562 	  fprintf(stderr, "can't read mesh color array!\n");
563 	  return 1;
564 	}
565       }
566       /* Each number we get is just the x,y or z coordinate.
567 	 We don't use a ZMESH because the grid isn't necessarily
568 	 an array starting at 0,0: it depends on meshrange
569        */
570 
571       if (numcols)
572 	printf("\n\n{ CMESH \n");
573       else
574 	printf("\n\n{ MESH \n");
575       printf("\n%d %d\n",size[0], size[1]);
576       ok = 1;
577       for (i = 0; i < size[0]; i++){
578 	for (j = 0; j < size[1]; j++) {
579 	  if (globline->token!=NUMBER) {
580 	    ok = 0; break;
581 	  }
582 	  printf(" %s ", globline->data);      /* x */
583           globline=globline->next;
584 	  if (globline->token!=NUMBER) {
585 	    ok = 0; break;
586 	  }
587 	  printf(" %s ", globline->data);      /* y */
588           globline=globline->next;
589 	  if (globline->token!=NUMBER) {
590 	    ok = 0; break;
591 	  }
592 	  printf(" %s ", globline->data);      /* z */
593 	  /* MESH vertices need RGBA colors
594 	     since OOGL MESHes have per-vertex colors, repeat some.
595 	     repeat last i index, first j index.
596 	   */
597 	  if (numcols) {
598 	    printf(" %f %f %f 1\n", colors[i<size[0]-1?i:i-1][j?j-1:0][0],
599 		   colors[i<size[0]-1?i:i-1][j?j-1:0][1],
600 		   colors[i<size[0]-1?i:i-1][j?j-1:0][2]);
601 	  } else printf("\n");
602 	  globline=globline->next;
603 	}
604       }
605       if (!ok) {
606 	fprintf(stderr, "can't read mesh points array!\n");
607 	return 1;
608       } else
609 	printf("} #end of MESH\n");
610     break;
611     case BG:  /* BezierGraphics */
612       globline=globline->next;
613       if (globline->token == DIMENSIONS) {
614 	globline=globline->next;
615 	for (i = 0; i < 2 && ok; i++, globline = globline->next)
616 	  if (globline->token == NUMBER)
617 	    size[i] = atoi(globline->data);
618 	  else ok = 0;
619       } else
620 	ok = 0;
621       if (!ok) {
622 	fprintf(stderr, "can't read Bezier patch dimensions!\n");
623 	return 1;
624       }
625 
626       /*
627 	in BezierGraphics object, first we get all the colors,
628 	then all the points.
629 
630 	points array is size[0] x size[1],
631 	color array is 2x2.
632 
633 	*/
634 
635       /* if there are colors, store them off */
636       if (globline->token == COLOR) {
637 	numcols = 2*2;
638 	colors = malloc(2*sizeof(Color*));
639 	for (i = 0; i < 2;i++)
640 	  colors[i] = malloc(2*sizeof(Color));
641 	ok = 1;
642 	for (i = 0; i < 2; i++) {
643 	  for (j=0; j < 2; j++) {
644 	    while (globline->token == COLOR && ok)
645 	      for (q=0; q < 3 && ok; q++) {
646 		globline = globline->next;
647 		if (globline->token != NUMBER) {
648 		  ok = 0;
649 		  break;
650 		} else {
651 		  colors[i][j][q]=atof(globline->data);
652 		}
653 	      }
654 	    globline=globline->next;
655 	  }
656 	}
657 	if (!ok) {
658 	  fprintf(stderr, "can't read mesh color array!\n");
659 	  return 1;
660 	}
661       }
662       /* Each number we get is just the x,y or z coordinate.  */
663 
664       if (numcols)
665 	printf("\n\n{ CBEZ");
666       else
667 	printf("\n\n{ BEZ");
668       printf("%d%d3\n",size[1]-1, size[0]-1); /* reverse order */
669       ok = 1;
670       for (i = 0; i < size[0]; i++){
671 	for (j = 0; j < size[1]; j++) {
672 	  if (globline->token!=NUMBER) {
673 	    ok = 0; break;
674 	  }
675 	  printf(" %s ", globline->data);      /* x */
676           globline=globline->next;
677 	  if (globline->token!=NUMBER) {
678 	    ok = 0; break;
679 	  }
680 	  printf(" %s ", globline->data);      /* y */
681           globline=globline->next;
682 	  if (globline->token!=NUMBER) {
683 	    ok = 0; break;
684 	  }
685 	  printf(" %s ", globline->data);      /* z */
686 	  /* print colors */
687 	  if (numcols) {
688 	    printf(" %f %f %f 1\n", colors[i][j][0], colors[i][j][1],
689 		   colors[i][j][2]);
690 	  } else printf("\n");
691 	  globline=globline->next;
692 	}
693       }
694       if (!ok) {
695 	fprintf(stderr, "can't read control points array!\n");
696 	return 1;
697       } else
698 	printf("} #end of BEZ\n");
699     break;
700     case G3: /* Graphics3D */
701       globline=globline->next;
702       npolypoints=npolys=npolycolors=0;
703       nvectpoints=nvects=nvectcolors=0;
704       numnums=numcols=0;
705       state = IGNORE;
706       for (curline=globline;;curline=curline->next) {
707 	if (!curline || (curline->token != NUMBER)) {
708 
709 	  if (state == LINE) {
710 	    nvects++;
711 	    nvectpoints += numnums/3;
712 	    if (numcols)
713 	      nvectcolors++;
714 	    numcols = 0;
715 	  } else if (state == POLYGON) {
716 	    npolys++;
717 	    npolypoints += numnums/3;
718 	    if (numcols)
719 	      npolycolors++;
720 	  }
721 	  if (!curline || ismajor(curline->token))
722 	    break; /* go back to main loop */
723 
724 	  if (curline->token == COLOR)
725 	    numcols++;
726 
727 	  state=curline->token;
728 	  numnums=0;
729 	} else			/* it's a number */
730 	  numnums++;
731       }
732       if (npolys) {
733 
734 	printf ("\n{ = OFF\n");
735 	printf ("%d %d %d\n",npolypoints, npolys, 0);
736 
737 	{ /* vertex list */
738 	  struct line *curline;
739 	  enum st state=IGNORE;
740 	  char coordnum=0;
741 
742 	  for (curline=globline; curline && !ismajor(curline->token);
743 	       curline=curline->next) {
744 	    if (curline->token != NUMBER) {
745 	      state = curline->token;
746 	      coordnum=0;
747 	      printf("\n");
748 	    } else
749 	      if (state == POLYGON) {
750 		printf("%s",curline->data);
751 		if(coordnum++%3 == 2)
752 		  printf("\n");
753 		else
754 		  printf(" ");
755 	      }
756 	  }
757 	}
758 	{ /* face list (maybe includes color) */
759 	  Color c;
760 	  char hascolor = 0;	/* so far. */
761 	  char thiscolor=1;	/* if this polygon has had its color
762 				 * tacked on. */
763 	  char cnum=3;		/* 3 == not doing color now. */
764 	  struct line *curline;
765 	  enum st state=IGNORE;
766 	  int pointnum=0;
767 
768 	  c[0] = c[1] = c[2] = 0;
769 	  for (curline=globline;;curline=curline->next) {
770 	    if (!thiscolor && hascolor &&
771 		(!curline || curline->token != NUMBER)) {
772 	      /* OFF faces need just RGB colors, no alpha! */
773 	      printf(" %f %f %f",c[0],c[1],c[2]);
774 	      thiscolor = 1;
775 	    }
776 	    if (!curline || ismajor(curline->token))
777 	      break; /* back to main loop */
778 	    switch (curline->token) {
779 	    case COLOR:
780 	      cnum = 0;
781 	      state = COLOR;
782 	      hascolor = 1;
783 	      break;
784 	    case POLYGON:
785 	      {
786 		struct line *countline;
787 		int numvert=0;
788 
789 		thiscolor = !hascolor;
790 
791 		cnum = 3;
792 		state = POLYGON;
793 		for (countline = curline->next; countline; countline=countline->next) {
794 		  if (countline->token != NUMBER)
795 		    break;
796 		  numvert++;
797 		}
798 
799 		printf("\n%d",numvert/3);
800 	      }
801 	      break;
802 	    case NUMBER:
803 	      if (state == POLYGON) {
804 		if (pointnum++%3 == 2)
805 		  printf(" %d",pointnum/3-1);
806 	      } else if (state==COLOR) {
807 		if (cnum<3)
808 		  c[(int)cnum++] = atof(curline->data);
809 	      }
810 	      break;
811 	    default:
812 	      state = curline->token;
813 	      break;
814 	    }
815 	  }
816 	}
817 	printf("\n\n} #end of OFF\n");
818       }
819       if (nvects) {
820 	printf("\n{ = VECT\n");
821 	printf("%d %d %d\n",nvects, nvectpoints, nvectcolors);
822 	{
823 	  struct line *curline;
824 	  enum st state=IGNORE;
825 	  int numvert=0, numcol=0;
826 	  for (curline=globline;;curline=curline->next) {
827 	    if (numvert && (!curline || curline->token != NUMBER)) {
828 	      printf("%d ",numvert/3);
829 	      numvert = 0;
830 	    }
831 	    if (!curline || ismajor(curline->token))
832 	      break; /* back to main loop */
833 	    if(curline->token == NUMBER && state==LINE)
834 	      numvert++;
835 	    else
836 	      state=curline->token;
837 	  }
838 	  printf("\n\n");
839 	  for (curline=globline; curline && !ismajor(curline->token);
840 	       curline=curline->next) {
841 	    if (curline->token == LINE) {
842 	      printf("%d ",numcol?1:0);
843 	      numcol = 0;
844 	    }
845 	    if(curline->token == COLOR)
846 	      numcol++;
847 	  }
848 	  printf("\n\n");
849 	}
850 	{
851 	  struct line *curline;
852 	  enum st state=IGNORE;
853 	  int coordno=0;
854 	  for (curline=globline; curline && !ismajor(curline->token);
855 	       curline=curline->next) {
856 	    if (curline->token != NUMBER)
857 	      state = curline->token;
858 	    else
859 	      if (state == LINE) {
860 		printf("%s%c",curline->data,((++coordno)%3)?' ':'\n');
861 	      }
862 	  }
863 	}
864 	{
865 	  struct line *curline;
866 	  Color c;
867 	  char hascolor=0;
868 	  char cnum=3;
869 
870 	  c[0] = c[1] = c[2] = 0;
871 
872 	  for (curline=globline; curline && !ismajor(curline->token);
873 	       curline=curline->next) {
874 	    if (curline->token == NUMBER) {
875 	      if (cnum<3) {
876 		c[(int)cnum++]=atof(curline->data);
877 		hascolor = 1;
878 	      }
879 	    } else if (curline->token == LINE) {
880 	      /* note VECTs need RGBA colors */
881 	      if(hascolor)
882 		printf("%f %f %f 1\n",c[0],c[1],c[2]);
883 	      hascolor = 0;
884 	    } else if (curline->token == COLOR)
885 	      cnum = 0;
886 
887 	  }
888 	}
889 	printf("\n} #end of VECT\n");
890       }
891       globline = curline;
892     break;
893     default:
894 	    fprintf(stderr, "math2oogl: unexpected data: %s\n", globline->data);
895 	    return 1;
896     } /* end switch (globline->token) */
897  }
898   printf ("\n} #end of LIST\n");
899   if (togv) {		/* end our (geometry */
900     printf("\n)\n");
901   }
902   return 0;
903 }
904