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 * 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 */ 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 */ 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 * 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 */ 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 */ 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 * 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 */ 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 */ 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 */ 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 */ 626 DBClearSet() 627 { 628 while (!DBNullelt(cset)) 629 cset = DBNextofSet(cset); 630 } /* end DBClearSet */ 631