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