1 /*
2 * @(#)db.c 1.2 01/03/85
3 *
4 * This file contains routines for database manipulation for the
5 * SUN Gremlin picture editor.
6 *
7 * Mark Opperman (opcode@monet.BERKELEY)
8 *
9 */
10
11 #include "gremlin.h"
12 #include <ctype.h>
13
14 /* imports from undodb */
15
16 extern UNRembAdd();
17 extern UNRembDelete();
18 extern UNRembMod();
19
20 /* imports from C */
21
22 extern char *malloc();
23
24 /* imports from point.c */
25
26 extern PTModifyTextPoints();
27 extern POINT *PTMakePoint();
28
29 /* imports from text.c */
30
31 extern TxPutMsg();
32 extern TxMsgOK();
33
34 /* imports from main */
35
36 extern SEARCH; /* Search the path for filename */
37 extern TOOLINSTALLED;
38
39 /* cset is a pointer to the current set available to the outside world. */
40
41 ELT *cset;
42
43 /*
44 * This routine creates a new element with the specified attributes and
45 * links it into database db.
46 */
47 ELT *
DBCreateElt(type,pointlist,brush,size,text,db)48 DBCreateElt(type, pointlist, brush, size, text, db)
49 int type, brush, size;
50 POINT *pointlist;
51 char *text;
52 ELT *(*db);
53 {
54 register ELT *temp;
55
56 temp = (ELT *) malloc(sizeof(ELT));
57 temp->nextelt = *db;
58 temp->type = type;
59 temp->ptlist = pointlist;
60 temp->brushf = brush;
61 temp->size = size;
62 temp->textpt = text;
63 *db = temp;
64 UNRembAdd(temp, db);
65 return(temp);
66 } /* end DBCreateElt */
67
68
69 /*
70 * This routine deletes the specified element by searching the database
71 * for its predecessor and deleting the pointer to the element.
72 * Flag indicates whether or not the element was in the current set
73 * and is passed along for use by the undo routines.
74 */
75 DBDelete(elt, db)
76 register ELT *elt, *(*db);
77 {
78 register ELT *(*temp);
79
80 temp = db;
81
82 while (*temp != elt) {
83 if (DBNullelt(*temp)) {
84 error("no such element");
85 return;
86 }
87 temp = &(DBNextElt((*temp)));
88 }
89
90 UNRembDelete(*temp, db);
91 *temp = DBNextElt(elt);
92 } /* end DBDelete */
93
94
95 #define highval 100000 /* arbitrary value greater than any
96 * expected distance */
97
98
99 /*
100 * This routine searches the database for the point closest to
101 * (Euclidean distance) point1. This point is returned as point2
102 * and the element which contained the point is also returned.
103 * The point must be closer than some predefined maximum distance
104 * in order to be gravitated.
105 * If setonly == TRUE the element's "setnext" pointer is used to
106 * find the elements in the list, otherwise "nextelt" is used.
107 */
DBGravitate(x1,y1,x2,y2,point,elt,db,setonly)108 DBGravitate(x1, y1, x2, y2, point, elt, db, setonly)
109 float x1, y1, *x2, *y2;
110 POINT *(*point);
111 ELT *(*elt), *db;
112 int setonly;
113 {
114 register POINT *holdpt;
115 register ELT *temp;
116 register t, t1, t2;
117 register distance = highval;
118
119 temp = db;
120 *elt = DBInit();
121 *x2 = x1;
122 *y2 = y1;
123 while (!DBNullelt(temp)) {
124 holdpt = temp->ptlist;
125 while (!Nullpoint(holdpt)) {
126 /* Calculate the distance between the point in the data
127 base and the specified point. Use Euclidean distance
128 except that, since we only need relative distance and
129 not an absolute number, it is not necessary to take the
130 square root. The equation for the distance was broken up
131 as below in order to allow integer arithmetic wherever
132 possible to increase efficiency when it was discovered
133 that this routine was too slow. */
134 t1 = holdpt->x - x1;
135 t1 *= t1;
136 t2 = holdpt->y - y1;
137 t2 *= t2;
138 t = t1 + t2;
139
140 if ((t < distance) && (t < MAXGDIST)) {
141 distance = t;
142 *x2 = holdpt->x;
143 *y2 = holdpt->y;
144 *point = holdpt;
145 *elt = temp;
146 }
147
148 holdpt = holdpt->nextpt;
149 }
150 temp = setonly ? DBNextofSet(temp) : DBNextElt(temp);
151 }
152 } /* end DBGravitate */
153
154
155 /*
156 * This routine returns all storage associated with the element to
157 * free storage.
158 */
DBClearElt(elt)159 DBClearElt(elt)
160 register ELT *elt;
161 {
162 register POINT *pt, *pt2;
163
164 pt = elt->ptlist;
165
166 while (!Nullpoint(pt)) {
167 pt2 = PTNextPoint(pt);
168 free ((char *) pt);
169 pt = pt2;
170 }
171
172 free(elt->textpt);
173 free((char *) elt);
174 } /* end DBClearElt */
175
176
177 /*
178 * This routine reads the specified file into a database and
179 * returns a pointer to that database. Orient and pos are also set
180 * from the file.
181 *
182 * The format of a file written by gremlin is:
183 * the string: "gremlinfile" followed by a carriage return.
184 * the orientation (integer) and the x and y coordinates of a positioning
185 * point (float) followed by another carriage return.
186 * The output of 0 or more elements (see below).
187 * a -1 (integer) indicating end of data.
188 *
189 * The format of each element is:
190 * The element type (integer) followed by a carriage return.
191 * a list of 0 or more pairs of point coordinates (float) each on separate
192 * lines and terminated by the coordinates -1.0 -1.0.
193 * the brush (font) and size (integer) the element was defined with then <cr>
194 * the length (integer) of the string followed by the string terminated with
195 * a carriage return.
196 *
197 * All numbers are printed using standard C output conversion (ascii).
198 *
199 * +++ NEW FORMAT FOR SUN +++
200 *
201 * "sungremlinfile" is keyword in place of "gremlinfile"
202 *
203 * Point lists are terminated by a line containing a single asterik ('*')
204 * to allow the legal point (-1.00 -1.00) in the point list. All negative
205 * coordinates are now legal. Element types are indicated by ascii text,
206 * eg, POLYGON, VECTOR, ARC, BOTLEFT, TOPCENT, etc.
207 */
208 ELT *
DBRead(filename,orient,pos)209 DBRead(filename, orient, pos)
210 char *filename;
211 int *orient;
212 POINT *pos;
213 {
214 FILE *fp, *POpen();
215 ELT *elt, *elist;
216 POINT *plist;
217 char string[128], *txt, *prealname;
218 float x, y;
219 int len, type, i, brush, size, done, lastpoint, sunfile;
220
221 sunfile = FALSE;
222 elist = DBInit();
223 fp = POpen(filename, &prealname, SEARCH);
224
225 if (fp == NULL) {
226 (void) sprintf(string, "can't open %s",filename);
227 error(string);
228 return(elist);
229 }
230
231 if (TOOLINSTALLED) /* no message if reading startup edit file */
232 TxPutMsg("reading file...");
233 (void) fscanf(fp, "%s\n", string);
234
235 if (strcmp(string, "gremlinfile")) {
236 if (strcmp(string, "sungremlinfile")) {
237 error("not gremlin file");
238 return(elist);
239 }
240 sunfile = TRUE;
241 }
242
243 (void) fscanf(fp, "%d%f%f\n", orient, &x, &y);
244 pos->x = x;
245 pos->y = y;
246
247 done = FALSE;
248 while (!done) {
249 if (fscanf(fp,"%s\n", string) == EOF) { /* element type */
250 error("error in file format");
251 fclose(fp);
252 return(elist);
253 }
254
255 if ((type = DBGetType(string)) < 0) { /* no more data */
256 done = TRUE;
257 }
258 else {
259 plist = PTInit();
260 (void) fscanf(fp, "%f%f\n", &x, &y); /* read first point */
261
262 /* Files created on the SUN have point lists terminated
263 * by a line containing only an asterik ('*'). Files
264 * created on the AED have point lists terminated by the
265 * coordinate pair (-1.00 -1.00).
266 */
267 lastpoint = FALSE;
268 do {
269 (void) PTMakePoint(x, y, &plist);
270 fgets(string, 127, fp);
271 if (string[0] == '*') { /* SUN gremlin file */
272 lastpoint = TRUE;
273 }
274 else {
275 (void) sscanf(string, "%f%f", &x, &y);
276 if ((x == -1.00 && y == -1.00) && (!sunfile))
277 lastpoint = TRUE;
278 }
279 } while (!lastpoint);
280 #ifdef oldway
281 while ((x != -1) && (y != -1)) { /* plist terminated by -1, -1 */
282 (void) PTMakePoint(x, y, &plist);
283 (void) fscanf(fp, "%f%f\n", &x, &y);
284 }
285 #endif
286
287 (void) fscanf(fp, "%d%d\n", &brush, &size);
288 (void) fscanf(fp, "%d", &len);
289 (void) getc(fp); /* eat blank */
290 txt = malloc((unsigned) len + 1);
291 for (i=0; i<len; ++i)
292 txt[i] = getc(fp);
293 txt[len] = '\0';
294 elt = DBCreateElt(type, plist, brush, size, txt, &elist);
295 if (TEXT(elt->type)) /* recompute text reference points */
296 PTModifyTextPoints(elt);
297 }
298 }
299
300 TxMsgOK();
301 fclose(fp);
302 return(elist);
303 } /* end DBRead */
304
305
306 /*
307 * Interpret element type in string s.
308 * Old file format consisted of integer element types.
309 * New file format has literal names for element types.
310 */
DBGetType(s)311 DBGetType(s)
312 register char *s;
313 {
314 if (isdigit(s[0]) || (s[0] == '-')) /* old element format or EOF */
315 return(atoi(s));
316
317 switch (s[0]) {
318 case 'P':
319 return(POLYGON);
320 case 'V':
321 return(VECTOR);
322 case 'A':
323 return(ARC);
324 case 'C':
325 if (s[1] == 'U')
326 return(CURVE);
327 switch (s[4]) {
328 case 'L':
329 return(CENTLEFT);
330 case 'C':
331 return(CENTCENT);
332 case 'R':
333 return(CENTRIGHT);
334 default:
335 error("unknown element type");
336 return(-1);
337 }
338 case 'B':
339 switch (s[3]) {
340 case 'L':
341 return(BOTLEFT);
342 case 'C':
343 return(BOTCENT);
344 case 'R':
345 return(BOTRIGHT);
346 default:
347 error("unknown element type");
348 return(-1);
349 }
350 case 'T':
351 switch (s[3]) {
352 case 'L':
353 return(TOPLEFT);
354 case 'C':
355 return(TOPCENT);
356 case 'R':
357 return(TOPRIGHT);
358 default:
359 error("unknown element type");
360 return(-1);
361 }
362 default:
363 error("unknown element type");
364 return(-1);
365 }
366 } /* end DBGetType */
367
368
369 /*
370 * This routine returns true if all points in elt are bounded by
371 * the rectangle who diagonal is formed by (x1, y1) and (x2, y2).
372 */
DBBounded(elt,x1,y1,x2,y2)373 DBBounded(elt, x1, y1, x2, y2)
374 register ELT *elt;
375 register float x1, y1, x2, y2;
376 {
377 register POINT *p1;
378 register float lox, loy, hix, hiy; /* OK to compare register floats */
379
380 lox = (x1 < x2) ? x1 : x2;
381 loy = (y1 < y2) ? y1 : y2;
382 hix = (x1 > x2) ? x1 : x2;
383 hiy = (y1 > y2) ? y1 : y2;
384 p1 = elt->ptlist;
385
386 while (!Nullpoint(p1)) {
387 if ((p1->x < lox) || (p1->x > hix) || (p1->y < loy) || (p1->y > hiy))
388 return(FALSE);
389 p1 = PTNextPoint(p1);
390 }
391
392 return(TRUE);
393 } /* end DBBounded */
394
395
396 /*
397 * This routine creates a copy of the the element transformed by
398 * the transformation matrix and adds the new copy to the database.
399 */
400 ELT *
DBCopy(elt,transform,db)401 DBCopy(elt, transform, db)
402 register ELT *elt;
403 ELT *(*db);
404 float transform[3][2];
405 {
406 register POINT *pt;
407 POINT *newlist;
408 char *newtext;
409
410 newlist = PTInit();
411 pt = elt->ptlist;
412
413 while (!Nullpoint(pt)) { /* matrix multiply */
414 (void) PTMakePoint((((pt->x) * transform[0][0]) +
415 ((pt->y) * transform[1][0]) +
416 transform[2][0]),
417 (((pt->x) * transform[0][1]) +
418 ((pt->y) * transform[1][1]) +
419 transform[2][1]), &newlist);
420 pt = pt->nextpt;
421 }
422
423 newtext = malloc((unsigned) strlen(elt->textpt) + 1);
424 (void) strcpy(newtext, elt->textpt);
425 return( DBCreateElt(elt->type, newlist, elt->brushf,
426 elt->size, newtext, db) );
427 } /* end DBCopy */
428
429
430 /*
431 * This routine transforms the element by multiplying the
432 * coordinates of each of the points in the element by the
433 * transformation matrix.
434 */
DBXform(elt,transform,db)435 DBXform(elt, transform, db)
436 register ELT *elt;
437 float transform[3][2];
438 ELT *(*db);
439 {
440 register POINT *pt;
441 float px, py;
442
443 UNRembMod(elt, db);
444 pt = elt->ptlist;
445
446 while (!Nullpoint(pt)) {
447 px = ((pt->x) * transform[0][0]) +
448 ((pt->y) * transform[1][0]) + transform[2][0];
449 py = ((pt->x) * transform[0][1]) +
450 ((pt->y) * transform[1][1]) + transform[2][1];
451 pt->x = px;
452 pt->y = py;
453 pt = pt->nextpt;
454 }
455 } /* end DBXform */
456
457
458 /*
459 * This routine changes the brush attribute of the element.
460 */
461 DBChangeBrush(elt, brush, db)
462 ELT *elt, *(*db);
463 int brush;
464 {
465 UNRembMod(elt, db);
466 elt->brushf = brush;
467 } /* end DBChangeBrush */
468
469
470 /*
471 * This routine changes the justify attribute of the element.
472 */
473 DBChangeJustify(elt, justmode, db)
474 ELT *elt, *(*db);
475 int justmode;
476 {
477 register length;
478 register POINT *pos, *point;
479
480 UNRembMod(elt, db);
481 elt->type = justmode;
482 PTModifyTextPoints(elt);
483 } /* end DBChangeJustify */
484
485
486 /*
487 * This routine changes the font attribute of the given element.
488 */
489 DBChangeFont(elt, font, db)
490 ELT *elt, *(*db);
491 int font;
492 {
493 UNRembMod(elt, db);
494 elt->brushf = font;
495 PTModifyTextPoints(elt);
496 } /* end DBChangeFont */
497
498
499 /*
500 * This routine changes the size attribute of the given element.
501 */
502 DBChangeSize(elt, size, db)
503 ELT *elt, *(*db);
504 int size;
505 {
506 UNRembMod(elt, db);
507 elt->size = size;
508 PTModifyTextPoints(elt);
509 } /* end DBChangeSize */
510
511
512 /*
513 * This routine changes the stipple attribute of the given element.
514 */
515 DBChangeStipple(elt, stipple, db)
516 ELT *elt, *(*db);
517 int stipple;
518 {
519 UNRembMod(elt, db);
520 elt->size = stipple;
521 } /* end DBChangeStipple */
522
523
524 /*
525 * This routine changes the text attribute of the given element.
526 */
527 DBChangeText(elt, text, db)
528 ELT *elt, *(*db);
529 char *text;
530 {
531 char *new;
532
533 UNRembMod(elt, db);
534 free(elt->textpt);
535 new = malloc((unsigned) strlen(text) + 1);
536 (void) strcpy(new, text);
537 elt->textpt = new;
538 PTModifyTextPoints(elt);
539 } /* end DBChangeText */
540
541
542 /*
543 * This routine changes the type attribute of the given element.
544 */
545 DBChangeType(elt, newtype, db)
546 ELT *elt, *(*db);
547 int newtype;
548 {
549 UNRembMod(elt, db);
550 elt->type = newtype;
551 } /* end DBChangeType */
552
553
554 /*
555 * This routine changes the type and stipple attributes of the given element.
556 */
557 DBChangeTypeStipple(elt, newtype, newstipple, db)
558 ELT *elt, *(*db);
559 int newtype, newstipple;
560 {
561 UNRembMod(elt, db);
562 elt->type = newtype;
563 elt->size = newstipple;
564 } /* end DBChangeType */
565
566
567 /*
568 * This routine changes the type, brush and stipple attributes
569 * of the given element.
570 */
571 DBChangeTypeBrushStipple(elt, newtype, newbrush, newstipple, db)
572 ELT *elt, *(*db);
573 int newtype, newbrush, newstipple;
574 {
575 UNRembMod(elt, db);
576 elt->type = newtype;
577 elt->brushf = newbrush;
578 elt->size = newstipple;
579 } /* end DBChangeType */
580
581
582 /*
583 * This routine adds the element to the current set database.
584 */
DBAddSet(elt)585 DBAddSet(elt)
586 register ELT *elt;
587 {
588 register ELT *elist;
589
590 elist = cset;
591
592 while (!DBNullelt(elist)) { /* makes sure element not already in list */
593 if (elist == elt)
594 return;
595 elist = DBNextofSet(elist);
596 }
597
598 elt->setnext = cset;
599 cset = elt;
600 } /* end DBAddSet */
601
602
603 /*
604 * Return TRUE if element in current set, else FALSE.
605 */
DBInCset(elt)606 DBInCset(elt)
607 register ELT *elt;
608 {
609 register ELT *elist;
610
611 elist = cset;
612
613 while (!DBNullelt(elist)) { /* makes sure element not already in list */
614 if (elist == elt)
615 return(TRUE);
616 elist = DBNextofSet(elist);
617 }
618 return(FALSE);
619 } /* end DBInCset */
620
621
622 /*
623 * This routine clears the current set by setting the pointer
624 * to a null element.
625 */
DBClearSet()626 DBClearSet()
627 {
628 while (!DBNullelt(cset))
629 cset = DBNextofSet(cset);
630 } /* end DBClearSet */
631