1 /* @(#)db1.c	1.3	10/21/84
2  *
3  * Copyright -C- 1982 Barry S. Roitblat
4  *
5  *      This file contains routines for database manipulation for the
6  * gremlin picture editor.
7  */
8 
9 #include "gremlin.h"
10 #include "grem2.h"
11 #include <ctype.h>
12 
13 /* imports from undodb */
14 
15 extern UNRembAdd(), UNRembDelete();
16 
17 /* imports from c */
18 
19 extern char *malloc();
20 extern char *strcpy();
21 extern char *sprintf();
22 
23 /* imports from point.c */
24 
25 extern POINT *PTInit();
26 extern POINT *PTMakePoint();
27 
28 /* imports from textio.c  */
29 
30 extern TxPutMsg(), TxMsgOK();
31 
32 /* imports from main */
33 
34 extern int SEARCH;        /* Search the path for filename ?? */
35 
36 ELT *DBInit()
37 /*
38  *      This routine returns a pointer to an initialized database element
39  * which would be the only element in an empty list.
40  */
41 
42 {
43     return(NULL);
44 }  /* end DBInit */
45 
46 ELT *DBCreateElt(type, pointlist, brush, size, text, db)
47 int type, brush, size;
48 POINT *pointlist;
49 char *text;
50 ELT *(*db) ;
51 /*
52  *      This routine creates a new element with the specified attributes and
53  * links it into database.
54  */
55 
56 {
57     ELT *temp;
58 
59     temp = (ELT *) malloc(sizeof(ELT));
60     temp->nextelt = *db;
61     temp->type = type;
62     temp->ptlist = pointlist;
63     temp->brushf = brush;
64     temp->size = size;
65     temp->textpt = text;
66     *db = temp;
67     UNRembAdd(temp, db);
68     return(temp);
69 } /* end CreateElt */
70 
71 
72 DBDelete(element, db)
73 ELT *element, *(*db);
74 /*
75  *      This routine deletes the specified element by searching the database
76  * for its predecessor and deleting the pointer to the element.
77  * Flag indicates whether or not the element was in the current set
78  * and is passed along for use by the undo routines.
79  */
80 
81 {
82     ELT *(*temp);
83 
84     temp = db;
85     while (*temp != element)
86     {
87         if (DBNullelt(*temp))
88         {
89             error("no such element");
90             return;
91         };
92         temp = &(DBNextElt((*temp)));
93     };
94     UNRembDelete(*temp, db);
95     *temp = DBNextElt(element);
96 }  /* end DBDelete */
97 
98 
99 #define highval 10000               /* arbitrary value greater than any
100                                      * expected distance                */
101 
102 DBGravitate(x1, y1, x2, y2, point, element, db)
103 float x1, y1, *x2, *y2;
104 POINT *(*point);
105 ELT *(*element), *db;
106 /*
107  *      This routine searches the database for the point closest to
108  * (Euclidean distance) point1.  This point is returned as point2
109  * and the element which contained the point is also returned.
110  * The point must be closer than some predefined maximum distance
111  * in order to be gravitated.
112  */
113 
114 {
115     POINT *holdpt;
116     ELT *temp;
117     long int t, t1, t2, distance = highval;
118 
119     temp = db;
120     *element = DBInit();
121     *x2 = x1;
122     *y2 = y1;
123     while ( !DBNullelt(temp) )
124     {
125         holdpt = temp->ptlist;
126         while ( !Nullpoint(holdpt) )
127         {
128 
129                 /*  Calculate the distance between the point in the data
130                  * base and the specified point.  Use Euclidean distance
131                  * except that, since we only need relative distance and
132                  * not an absolute number, it is not necessary to take the
133                  * square root.  The equation for the distance was broken up
134                  * as below in order to allow integer arithmetic wherever
135                  * possible to increase efficiency when it was discovered
136                  * that this routine was too slow.
137                  */
138             t1 = holdpt->x - x1;
139             t1 *= t1;
140             t2 = holdpt->y - y1;
141             t2 *= t2;
142             t = t1 + t2;
143             if ((t < distance) && (t < MAXGDIST))
144             {
145                 distance = t;
146                 *x2 = holdpt->x;
147                 *y2 = holdpt->y;
148     		*point = holdpt;
149                 *element = temp;
150             }  /* end if */;
151             holdpt = holdpt->nextpt;
152         }  /* end while holdpt */;
153         temp = temp->nextelt;
154     }  /* end while temp */;
155 }  /* end Gravitate */
156 
157 
158 DBSetGravitate(x1, y1, x2, y2, point, element, db)
159 float x1, y1, *x2, *y2;
160 POINT *(*point);
161 ELT *(*element), *db;
162 /*
163  *      This routine searches the database for the point closest to
164  * (Euclidean distance) point1.  This point is returned as point2
165  * and the element which contained the point is also returned.
166  * The point must be closer than some predefined maximum distance
167  * in order to be gravitated.
168  */
169 
170 {
171     POINT *holdpt;
172     ELT *temp;
173     long int t, t1, t2, distance = highval;
174 
175     temp = db;
176     *element = DBInit();
177     *x2 = x1;
178     *y2 = y1;
179     while ( !DBNullelt(temp) )
180     {
181         holdpt = temp->ptlist;
182         while ( !Nullpoint(holdpt) )
183         {
184 
185                 /*  Calculate the distance between the point in the data
186                  * base and the specified point.  Use Euclidean distance
187                  * except that, since we only need relative distance and
188                  * not an absolute number, it is not necessary to take the
189                  * square root.  The equation for the distance was broken up
190                  * as below in order to allow integer arithmetic wherever
191                  * possible to increase efficiency when it was discovered
192                  * that this routine was too slow.
193                  */
194             t1 = holdpt->x - x1;
195             t1 *= t1;
196             t2 = holdpt->y - y1;
197             t2 *= t2;
198             t = t1 + t2;
199             if ((t < distance) && (t < MAXGDIST))
200             {
201                 distance = t;
202                 *x2 = holdpt->x;
203                 *y2 = holdpt->y;
204     		*point = holdpt;
205                 *element = temp;
206             }  /* end if */;
207             holdpt = holdpt->nextpt;
208         }  /* end while holdpt */;
209         temp = temp->setnext;
210     }  /* end while temp */;
211 }  /* end Gravitate */
212 
213 
214 
215 DBClearElt(elt)
216 ELT *elt;
217 /*
218  *      This routine returns all storage associated with the element to
219  * free storage
220  */
221 
222 {
223     POINT *pt, *pt2;
224 
225     pt = elt->ptlist;
226     while ( !Nullpoint(pt) )
227     {
228         pt2 = PTNextPoint(pt);
229         free ((char *) pt);
230         pt = pt2;
231     }  /* end while */;
232     free(elt->textpt);
233     free((char *) elt);
234 }  /* end DBClearElt */
235 
236 
237 ELT *DBRead(filename, orient, pos)
238 char *filename;
239 int *orient;
240 POINT *pos;
241 /*
242  *      This routine reads the specified file into a database and
243  * returns a pointer to that database.  Orient and pos are also set
244  * from the file.
245  *
246  * The format of a file written by gremlin is:
247  * the string: "gremlinfile" followed by a carriage return.
248  * the orientation (integer) and the x and y coordinates of a positioning
249  * point (float) followed by another carriage return.
250  * The output of 0 or more elements (see below).
251  * a -1 (integer) indicating end of data.
252  *
253  * The format of each element is:
254  * The element type (integer) followed by a carriage return.
255  * a list of 0 or more pairs of point coordinates (float) each on separate
256  * lines and terminated by the coordinates -1.0 -1.0.
257  * the brush (font) and size (integer) the element was defined with then <cr>
258  * the length (integer) of the string followed by the string terminated with
259  * a carriage return.
260  *
261  * All numbers are printed using standard c output conversion (ascii).
262  *
263  * +++ NEW FORMAT FOR SUN +++
264  *
265  * Installed 10/21/84 by Mark Opperman
266  * This modification allows reading of either SUN or AED formats
267  *
268  * "sungremlinfile" is keyword in place of "gremlinfile"
269  *
270  * Point lists are terminated by a line containing a single asterik ('*')
271  * to allow the legal point (-1.00 -1.00) in the point list.  All negative
272  * coordinates are now legal.  Element types are indicated by ascii text,
273  * eg, POLYGON, VECTOR, ARC, BOTLEFT, TOPCENT, etc.
274  */
275 
276 {
277     FILE *fp, *POpen();
278     ELT *elist;
279     POINT *plist;
280     char string[128], *txt;
281     float x, y;
282     int len, type, i, brush, size, done, lastpoint, sunfile;
283 
284     sunfile = FALSE;
285     elist = DBInit();
286     fp = POpen(filename,(char **) NULL,SEARCH);
287     if (fp == NULL)
288     {
289         (void) sprintf(string, "can't open %s",filename);
290         error(string);
291         return(elist);
292     }
293     TxPutMsg("reading file...");
294     (void) fscanf(fp,"%s\n",string);
295     if ( strcmp(string, "gremlinfile") )
296     {
297 	if ( strcmp(string, "sungremlinfile") )
298 	{
299 	    error("not gremlin file");
300 	    return(elist);
301 	}
302 	sunfile = TRUE;
303     }
304     (void) fscanf(fp, "%d%f%f\n", orient, &x, &y);
305     pos->x = x;
306     pos->y = y;
307 
308     done = FALSE;
309     while (!done)
310     {
311         if ( fscanf(fp,"%s\n", string) == EOF )		/* element type */
312         {
313             error("error in file format");
314 	    fclose(fp);
315             return(elist);
316         }
317         if ( (type = DBGetType(string))  < 0)         /* no more data */
318         {
319             done = TRUE;
320         }
321         else
322         {
323             (void) fscanf(fp, "%f%f\n", &x, &y);	/* read first point */
324             plist = PTInit();
325 
326 	    /* Files created on the SUN have point lists terminated
327 	     * by a line containing only an asterik ('*').  Files
328 	     * created on the AED have point lists terminated by the
329 	     * coordinate pair (-1.00 -1.00).
330 	     */
331 	    lastpoint = FALSE;
332 	    do {
333 		(void) PTMakePoint(x, y, &plist);
334 		fgets(string, 127, fp);
335 		if (string[0] == '*') {     /* SUN gremlin file */
336 		    lastpoint = TRUE;
337 		}
338 		else {
339 		    (void) sscanf(string, "%f%f", &x, &y);
340 		    if ((x == -1.00 && y == -1.00) && (!sunfile))
341 			lastpoint = TRUE;
342 		}
343 	    } while (!lastpoint);
344 
345 #ifdef oldway
346             while ((x != -1) && (y != -1)) /* pointlist terminated by -1, -1 */
347             {
348                 (void) PTMakePoint(x, y, &plist);
349                 (void) fscanf(fp, "%f%f", &x, &y);
350             }
351 #endif
352             (void) fscanf(fp, "%d%d\n", &brush, &size);
353             (void) fscanf(fp, "%d", &len);
354             txt = malloc((unsigned) len + 1);
355             (void) getc(fp);        /* throw away space character */
356             for (i=0; i<len; ++i)
357                 txt[i] = getc(fp);
358             txt[len] = '\0';
359             (void) DBCreateElt(type, plist, brush, size, txt, &elist);
360         }  /* end else */
361     }  /* end while not done */;
362     TxMsgOK();
363     fclose(fp);
364     return(elist);
365 } /* end DBRead */
366 
367 
368 /*
369  * Interpret element type in string s.
370  * Old file format consisted of integer element types.
371  * New file format has literal names for element types.
372  */
373 DBGetType(s)
374 register char *s;
375 {
376     if (isdigit(s[0]) || (s[0] == '-'))         /* old element format or EOF */
377         return(atoi(s));
378 
379     switch (s[0]) {
380         case 'P':
381             return(POLYGON);
382         case 'V':
383             return(VECTOR);
384         case 'A':
385             return(ARC);
386         case 'C':
387             if (s[1] == 'U')
388                 return(CURVE);
389             switch (s[4]) {
390                 case 'L':
391                     return(CENTLEFT);
392                 case 'C':
393                     return(CENTCENT);
394                 case 'R':
395                     return(CENTRIGHT);
396                 default:
397                     error("unknown element type");
398                     return(-1);
399             }
400         case 'B':
401             switch (s[3]) {
402                 case 'L':
403                     return(BOTLEFT);
404                 case 'C':
405                     return(BOTCENT);
406                 case 'R':
407                     return(BOTRIGHT);
408                 default:
409                     error("unknown element type");
410                     return(-1);
411             }
412         case 'T':
413             switch (s[3]) {
414                 case 'L':
415                     return(TOPLEFT);
416                 case 'C':
417                     return(TOPCENT);
418                 case 'R':
419                     return(TOPRIGHT);
420                 default:
421                     error("unknown element type");
422                     return(-1);
423             }
424         default:
425             error("unknown element type");
426             return(-1);
427     }
428 }  /* end DBGetType */
429 
430 
431 DBBounded(elt, x1, y1, x2, y2)
432 ELT *elt;
433 float x1, y1, x2, y2;
434 /*
435  *      This routine returns true if all points in elt are bounded by
436  * the rectangle who diagonal is formed by (x1, y1) and (x2, y2).
437  */
438 
439 {
440 	POINT *p1;
441 	float lox, loy, hix, hiy;
442 
443 	lox = (x1 < x2) ? x1 : x2;
444 	loy = (y1 < y2) ? y1 : y2;
445 	hix = (x1 > x2) ? x1 : x2;
446 	hiy = (y1 > y2) ? y1 : y2;
447 	p1 = elt->ptlist;
448 	while ( !Nullpoint(p1) )
449 	{
450 		if (p1->x < lox) return(FALSE);
451 		if (p1->x > hix) return(FALSE);
452 		if (p1->y < loy) return(FALSE);
453 		if (p1->y > hiy) return(FALSE);
454 		p1 = PTNextPoint(p1);
455 	}  /* end while */;
456 	return(TRUE);
457 }  /* end DBBounded */
458