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