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
DBInit()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
DBCreateElt(type,pointlist,brush,size,text,db)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
DBGravitate(x1,y1,x2,y2,point,element,db)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
DBSetGravitate(x1,y1,x2,y2,point,element,db)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
DBClearElt(elt)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
DBRead(filename,orient,pos)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 */
DBGetType(s)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
DBBounded(elt,x1,y1,x2,y2)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