/* * mpack.c -- * * Mpack is just like Tpack except that it generates Magic files instead * of Caesar files. * * Routines for module generation. Used by Mpla, MQuilt, MPanda, and others. * For info see paper called 'Pictures with Parenthesis' in 20th Design * Automation conference, as well as UC Berkeley Computer Science Report number * UCB/CSD 84/166, January, 1984. * * Copyright (C) 1985 Regents of the University of California * All rights reserved. */ #ifndef lint static char sccsid[] = "@(#)mpack.c 1.32 MAGIC-MPACK (Berkeley) 11/2/85"; #endif #include #include "magic.h" #include "hash.h" #include "geometry.h" #include "tile.h" #include "database.h" #include "mpack.h" #include "mpackint.h" #include "utils.h" #include "tech.h" #include "cif.h" #include "textio.h" #include "malloc.h" /* library routines */ extern char *strcat(); extern char *strcpy(); /* predefined locations */ POINT origin_point = {0, 0}; RECTANGLE origin_rect = {0, 0, 0, 0}; #define STRLEN 200 #define TECH_DEFAULT "cmos" static char *prog_name; static char out_base_name[STRLEN]; static char cif_style_name[STRLEN]; static char template_name[STRLEN]; static int write_cif = FALSE; static int verbose = FALSE; static int tile_number = 0; TILE OutTile = NULL; int tot_rects = 0; /* a count of the number of rectangles placed */ /* info for the debugging option '-D' */ int tilesToDo = BIG_NUM; int tilesToOutline = BIG_NUM; TILE lastOutTile = NULL; int normalTile = TRUE; #ifdef DEBUGGING /*--------------------------------------------------------- * The routines on this page are useful for debugging * * Results: None. * * Side Effects: Text is printed. *--------------------------------------------------------- */ trace(msg) char *msg; { (void) fprintf(stderr,"---> %s\n",msg); (void) fflush(stderr); } traceint(msg,i) char *msg; int i; { (void) fprintf(stderr,"---> %s = %d\n",msg,i); (void) fflush(stderr); } #endif /* * ---------------------------------------------------------------------------- * * tpMakeDef -- * * Return a new def with the given name if possible, otherwise make * up a new name if it already exists. If that was the case, print * out an error message. If name is NULL then make one up. * * Results: * A pointer to a new malloc'ed def. * * Side Effects: * none. * * ---------------------------------------------------------------------------- */ CellDef * tpMakeDef(name, file) char *name; /* The name of the cell. If NULL, make one up. */ char *file; /* The file where the cell is stored. If NULL, * then the file is not stored on disk -- it is * for internal use. */ { static int newtileID = 0; static char id[100]; CellDef *def; bool madeup; def = NULL; madeup = FALSE; if (name != NULL) def = DBCellNewDef(name, file); while (def == NULL) { newtileID++; (void) sprintf(id, "_tile%d", newtileID); def = DBCellNewDef(id, file); madeup = TRUE; } if (madeup && name != NULL) TxError("Tile '%s' already exists, using '%s' as the name of the new tile instead.\n", name, id); if (file == NULL) DBCellSetAvail(def); return def; } /* *----------------------------------------------------------------------------- * * tpCellCopyLabels -- * * Copy labels from sourceUse to targetUse, transforming according to * the supplied transform. Ommit any label that is the size of the area * being copied and that has the specified name. Also omit any labels that * stick out of the area. * * Results: * None. * * Side effects: * Updates the labels in targetUse. * *----------------------------------------------------------------------------- */ tpCellCopyLabels(sourceUse, targetUse, rect, ommitName, transform) CellUse *sourceUse; /* Cell from which labels are to be yanked */ CellUse *targetUse; /* Cell into which labels are to be stuffed */ register Rect *rect; /* Area to copy (in sourceUse coordinates) */ char *ommitName; /* Label to ommit if it is the same size of * the above rectangle. */ Transform *transform; /* Transform sourceUse to targetUse coords */ { register Label *lab; CellDef *def = targetUse->cu_def; Rect labTargetRect; int targetPos; for (lab = sourceUse->cu_def->cd_labels; lab; lab = lab->lab_next) if (GEO_SURROUND(rect, &lab->lab_rect) && ((lab->lab_rect.r_xbot != rect->r_xbot) || (lab->lab_rect.r_ybot != rect->r_ybot) || (lab->lab_rect.r_xtop != rect->r_xtop) || (lab->lab_rect.r_ytop != rect->r_ytop) || (ommitName == NULL) || (strcmp(lab->lab_text, ommitName) != 0)) ) { GeoTransRect(transform, &lab->lab_rect, &labTargetRect); targetPos = GeoTransPos(transform, lab->lab_pos); (void) DBPutLabel(def, &labTargetRect, targetPos, lab->lab_text, lab->lab_type); } } /*--------------------------------------------------------- * TPerror is Tpack's error handler * * Results: None. * * Side Effects: * A message is printed, and Tpack may exit. *--------------------------------------------------------- */ TPerror(errcd,msg) int errcd; /* an exit code, or 0 for no exit */ char *msg; /* a message to be printed on stderr */ { (void) fprintf(stderr,"%s (mpack): %s.\n", prog_name, msg); (void) fflush(stderr); if (errcd != 0) MainExit(errcd); } /*--------------------------------------------------------- * Do_args handles the command line arguments * * Results: None. * * Side Effects: * Global flags and file names are set. * The input file is opened as stdin. * Arguments that are of zero length are ignored, * as is the flag character ' '. This is so that * the caller of this routine may define new flags which * he processes and then nulls out. *--------------------------------------------------------- */ do_args(argc, argv) int argc; /* the number of arguments */ char *argv[]; /* an array of string pointers */ { int i, j; int junk; int p_flag = FALSE; char *arg, msg[STRLEN], style_name[STRLEN], in_name[STRLEN]; FILE *fopen(); (void) strcpy(out_base_name, ""); (void) strcpy(cif_style_name, ""); (void) strcpy(in_name,""); (void) strcpy(style_name,""); i = 1; while (i < argc) { arg = argv[i]; if (arg[0] == '-') { for (j = 1; arg[j] != '\0'; j++) { switch (arg[j]) { case 'a': write_cif = FALSE; continue; case 'c': write_cif = TRUE; continue; case 'p': p_flag = TRUE; continue; case 'v': verbose = TRUE; continue; case 's': i++; if (i >= argc) TPerror(1,"Style name expected after -s option"); (void) strcpy(style_name, argv[i]); continue; case 't': i++; if (i >= argc) TPerror(1,"Template expected after -t option"); (void) strcpy(template_name, argv[i]); continue; case 'o': i++; if (i >= argc) TPerror(1,"Output file expected after -o option"); (void) strcpy(out_base_name, argv[i]); continue; case 'l': i++; if (i >= argc) TPerror(1,"Cif style expected after after -l option"); (void) strcpy(cif_style_name, argv[i]); continue; case 'M': i++; if (i >= argc) TPerror(1,"Integer expected after -M option"); if (sscanf(argv[i], "%d", &junk) != 1) TPerror(1,"integer expected after -M option"); continue; case 'D': i++; if (i >= argc) TPerror(1,"2 integers expected after -D option"); if (sscanf(argv[i], "%d", &tilesToDo) != 1) TPerror(1,"2 integers expected after -D option"); i++; if (i >= argc) TPerror(1,"2 integers expected after -D option"); if (sscanf(argv[i], "%d", &tilesToOutline) != 1) TPerror(1,"2 integers expected after -D option"); tilesToOutline = tilesToDo - tilesToOutline + 1; continue; case ' ': continue; default: (void) sprintf(msg, "Unknown option '%c'", arg[j]); TPerror(1,msg); continue; } } } else { if (arg[0] != '\0') (void) strcpy(in_name, arg); } i++; } /* while */ if (strcmp(in_name, "") != 0) { FILE *err; err = freopen(in_name, "r", stdin); if (err == NULL) { (void) sprintf(msg, "Could not open input file \"%s\"", in_name); TPerror(1, msg); } } if (p_flag) { (void) strcpy(out_base_name, ""); } else { if ((strcmp(in_name, "") != 0) && (strcmp(out_base_name, "") == 0)) { int k; (void) strcpy(out_base_name, in_name); for (k=0; (out_base_name[k] != '.') && (out_base_name[k] != '\0'); k++) ; out_base_name[k] = '\0'; } } if (strcmp(template_name, "") != 0) { if (strcmp(style_name, "") != 0) { (void) strcat(template_name, "-"); (void) strcat(template_name, style_name); } } } /*--------------------------------------------------------- * TPinitialize initializes the Tpack system * * Results: None. * * Side Effects: * Various global variables are set, command * line arguments are processed, and an initial * set of tiles are loaded. *--------------------------------------------------------- */ TPinitialize(argc, argv, tname) int argc; /* the number of command line args */ char *argv[]; /* pointers to the args */ char *tname; /* The base name of the file that */ /* contains the initial tiles. */ /* Extensions are added if needed, */ /* and the '-s' option may tack on */ /* some characters. A zero length */ /* file name suppresses the loading*/ /* of the tiles. */ { char *tech; if ( (argc < 0) || (argc > 10000) ) TPerror(1,"Unusual arguments passed to TPinit"); prog_name = argv[0]; (void) strcpy(template_name, tname); do_args(argc, argv); tech = DBGetTech(template_name); if (tech != NULL) TechDefault = tech; if (TechDefault == NULL) TechDefault = TECH_DEFAULT; magicMainInit(); TPload_tiles(template_name); if (write_cif && cif_style_name[0] != '\0') CIFSetStyle(cif_style_name); } /*--------------------------------------------------------- * TPload_tiles loads in a set of tiles from a Magic file. * * Results: None. * * Side Effects: * Each rectangular label in the file defines a tile. * These tiles are placed on a list for future reference. *--------------------------------------------------------- */ TPload_tiles(filename) char *filename; /* the filename containing the tiles */ { CellDef *cd; CellDef *tile; Label *l; Rect *rlab; CellUse *u1, *u2; SearchContext scx; if (strcmp(filename, "") == 0) return; cd = (CellDef *) TPread_tile(filename); /* create a dummy CellUse for the template */ u1 = DBCellNewUse((CellDef *) cd, (char*)NULL); u1->cu_expandMask = 1; /* scan through looking for rectangular labels */ for (l = cd->cd_labels; l != NULL; l = l->lab_next) { rlab = &l->lab_rect; if ((rlab->r_xtop != rlab->r_xbot) && (rlab->r_ybot != rlab->r_ytop)) { /* create a tile for this label */ tile = (CellDef *) TPcreate_tile(l->lab_text); /* create a dummy CellUse for the new tile */ u2 = DBCellNewUse((CellDef *) tile, (char*)NULL); u2->cu_expandMask = 1; /* copy the paint from the template into the tile */ scx.scx_use = u1; scx.scx_x = scx.scx_y = 0; scx.scx_area = *rlab; scx.scx_trans = GeoIdentityTransform; (void) DBCellCopyPaint(&scx, &DBAllTypeBits, 1, u2); /* Copy the labels from the template into the tile. * Copy the surrounding label too, so that the tile's bbox will * be correct. Don't copy any labels that stick out, though. */ (void) tpCellCopyLabels(u1, u2, rlab, (char *) NULL, &GeoIdentityTransform); /* copy all subcells */ scx.scx_use = u1; scx.scx_x = scx.scx_y = 0; scx.scx_area = *rlab; scx.scx_trans = GeoIdentityTransform; (void) DBCellCopyAllCells(&scx, 0, u2, (Rect *) NULL); DBReComputeBbox((CellDef *) tile); if (!DBCellDeleteUse(u2)) TPerror(1, "Could not free CellUse\n"); } /* if */ } if (!DBCellDeleteUse(u1)) TPerror(1, "Could not free CellUse\n"); TPdelete_tile( (TILE) cd); } /*--------------------------------------------------------- * Create and initialize a new, empty tile. * * Results: A unique ID for the new tile is returned. * * Side Effects: A new tile is in the Magic database. *--------------------------------------------------------- */ TILE TPcreate_tile(name) char *name; /* a name for the tile */ { CellDef *def; /* create the tile */ def = tpMakeDef(name, (char *)NULL); return( (TILE) def); } /*--------------------------------------------------------- * Delete a tile from the Magic database * * Results: None. * * Side Effects: A tile is deleted from the Magic database *--------------------------------------------------------- */ TPdelete_tile(t) TILE t; /* the tile to be deleted */ { if (!DBCellDeleteDef((CellDef *)t)) TPerror(1, "Could not free CellDef\n"); } /*--------------------------------------------------------- * TPread_tile reads a Magic file into a tile. * * Results: An ID for the tile is returned. * * Side Effects: A cell is added to the Magic database. *--------------------------------------------------------- */ TILE TPread_tile(file) char *file; /* the file name */ { CellDef *def; /* First try to find it in main memory, then try to * read it from disk. */ def = tpMakeDef(file, file); if (!DBCellRead(def, file, TRUE)) { TPerror(1, "Mpack exiting.\n"); } else { /* DBCellRead doesn't dare to change bounding boxes, so * we have to call DBReComputeBbox here (we know that it's * safe). */ DBReComputeBbox(def); } return((TILE) def); } /*--------------------------------------------------------- * Returns the tile that has the given name * * Results: A TILE id, or NULL if not found. * * Side Effects: * An error message may be printed. *--------------------------------------------------------- */ TILE TPname_to_tile(tname) char *tname; /* the name of the tile */ { TILE t; t = (TILE) DBCellLookDef(tname); if (t == NULL) TxError("Tile \"%s\" not found.\n", tname); return t; } /*--------------------------------------------------------- * Returns true iff the tile exists * * Results: A boolean * * Side Effects: none *--------------------------------------------------------- */ int TPtile_exists(tname) char *tname; /* the name of the tile */ { return (DBCellLookDef(tname) != NULL); } /*--------------------------------------------------------- * Write a tile out into a Magic file * * Results: None. * * Side Effects: * A file is created, and the file name is * associated with the tile for future use. * Various error messages and other information * may be produced. *--------------------------------------------------------- */ TPwrite_tile(out_tile, filename) TILE out_tile; /* the tile to be written out */ char *filename; /* the name of the file */ { char out_name[STRLEN]; CellDef *cd; Rect bbox_1; cd = (CellDef *) out_tile; /* * Take care of timestamps. */ /* This is a new cell, make sure that it is DRCed when magic reads it in! */ bbox_1 = cd->cd_bbox; bbox_1.r_xbot -= 1; bbox_1.r_ybot -= 1; bbox_1.r_xtop += 1; bbox_1.r_ytop += 1; DBPaint(cd, &bbox_1, TT_CHECKPAINT); /* Generate real timestamps. */ DBUpdateStamps(); if (strcmp(filename, "") == 0) { (void) strcpy(out_name, out_base_name); } else { (void) strcpy(out_name, filename); }; if (verbose) { TxError("%d tiles placed, %d rectangles.\n", tile_number, tot_rects); } if (write_cif) { FILE *fp; if (strcmp(out_name,"") == 0) fp = stdout; else { char new_out_name[200]; strncpy(new_out_name, out_name, 190); new_out_name[190] = '\0'; strcat(new_out_name, ".cif"); fp = fopen(new_out_name, "w"); if (fp == NULL) { TxError("Could not create output file '%s'\n", new_out_name); MainExit(1); } } if (!CIFWrite(cd, fp)) { perror("Mpack could not write file."); TxError("Filename was '%s'\n", out_name); }; } else { if (strcmp(out_name,"") == 0) { if (!DBCellWriteFile(cd, stdout)) { perror("Mpack could not write to stdout"); MainExit(1); } } else { if (!DBCellWrite(cd, out_name)) { perror("Mpack could not write file"); TxError("Filename was '%s.mag'\n", out_name); MainExit(1); } }; } } /*--------------------------------------------------------- * A rectangle the size of a tile is produced, such that the * lower left corner of the rectangle is at the specified point * * Results: a rectangle * * Side Effects: A NULL tile produces an error message. *--------------------------------------------------------- */ RECTANGLE TPdisp_tile(from_tile, ll_corner) TILE from_tile; POINT ll_corner; { Rect from_box; RECTANGLE return_rect; if (from_tile == NULL) { TPerror(0,"Null tile passed to TPdisp_tile"); return_rect.x_left = ll_corner.x; return_rect.x_right = ll_corner.x; return_rect.y_top = ll_corner.y; return_rect.y_bot = ll_corner.y; return return_rect; } from_box = ((CellDef *)from_tile)->cd_bbox; return_rect.x_left = ll_corner.x; return_rect.x_right = ll_corner.x + (from_box.r_xtop - from_box.r_xbot); return_rect.y_bot = ll_corner.y; return_rect.y_top = ll_corner.y + (from_box.r_ytop - from_box.r_ybot); return return_rect; } /*--------------------------------------------------------- * A tile is painted into another tile at the given point, * and a rectangle that defines the area that was painted * is returned. * * Results: a rectangle * * Side Effects: * A cell in the Magic database is modified. * Error messages may be produced. *--------------------------------------------------------- */ RECTANGLE TPpaint_tile(from_tile, to_tile, ll_corner) TILE from_tile; /* the tile which is to be copied */ TILE to_tile; /* The destination for the copy operation */ POINT ll_corner; /* a point within 'to_tile' */ { char sname[STRLEN]; Rect from_box; RECTANGLE return_rect; Transform t; CellUse *to_use, *from_use; int x_offset, y_offset; SearchContext scx; if ((from_tile == NULL) || (to_tile == NULL)) { TPerror(0,"Null tile passed to TPpaint_tile"); return_rect.x_left = ll_corner.x; return_rect.x_right = ll_corner.x; return_rect.y_top = ll_corner.y; return_rect.y_bot = ll_corner.y; return return_rect; } /* create dummy CellUses */ from_use = DBCellNewUse((CellDef *) from_tile, (char*)NULL); from_use->cu_expandMask = 1; to_use = DBCellNewUse((CellDef *) to_tile, (char*)NULL); to_use->cu_expandMask = 1; from_box = ((CellDef *)from_tile)->cd_bbox; x_offset = ll_corner.x - from_box.r_xbot; y_offset = ll_corner.y - from_box.r_ybot; return_rect.x_left = ll_corner.x; return_rect.x_right = ll_corner.x + (from_box.r_xtop - from_box.r_xbot); return_rect.y_bot = ll_corner.y; return_rect.y_top = ll_corner.y + (from_box.r_ytop - from_box.r_ybot); t = GeoIdentityTransform; t.t_c = x_offset; t.t_f = y_offset; if (normalTile) tile_number++; if (tile_number <= tilesToDo) { lastOutTile = to_tile; /* Copy the paint in the tile */ scx.scx_use = from_use; scx.scx_x = scx.scx_y = 0; scx.scx_area = TiPlaneRect; scx.scx_trans = t; (void) DBCellCopyPaint(&scx, &DBAllTypeBits, 1, to_use); /* Grab any labels in the tile */ /* NOTE: don't copy the outermost label that defines the tile */ (void) tpCellCopyLabels(from_use, to_use, &from_box, ((CellDef *)from_tile)->cd_name, &t); /* Copy the subcells in the tile */ scx.scx_use = from_use; scx.scx_x = scx.scx_y = 0; scx.scx_area = TiPlaneRect; scx.scx_trans = t; (void) DBCellCopyAllCells(&scx, 0, to_use, (Rect *) NULL); /* label the tile in the output */ if ( (verbose || (tile_number >= tilesToOutline) ) && normalTile ) { (void) sprintf(sname, "Tile_%d", tile_number); TPplace_label(to_tile, return_rect, sname); } } DBReComputeBbox((CellDef *) to_tile); return(return_rect); } /*--------------------------------------------------------- * create a label in a tile * * Results: None. * * Side Effects: A Magic cell is modified. *--------------------------------------------------------- */ TPplace_label(to_tile, rect, label_name) TILE to_tile; /* the tile that is to recieve the label */ RECTANGLE rect; /* the rectangle that defines the label */ char *label_name; /* the string to be assocaited with the label */ { CellDef *cd; Rect r; if (to_tile == NULL) { TPerror(0,"NULL tile passed to TPplace_label, it was ignored\n"); return; } cd = (CellDef *)to_tile; r.r_xbot = rect.x_left; r.r_ybot = rect.y_bot; r.r_xtop = rect.x_right; r.r_ytop = rect.y_top; (void) DBPutLabel(cd, &r, -1, label_name, TT_SPACE); DBAdjustLabels(cd, &r); DBReComputeBbox(cd); } /*--------------------------------------------------------- * Compute an offset between two points. * * Results: a point. * * Side Effects: none *--------------------------------------------------------- */ POINT align(p1,p2) POINT p1,p2; { POINT p; p.x = p1.x - p2.x; p.y = p1.y - p2.y; /* paint an alignment blotch for demo purposes */ if ((tile_number >= tilesToOutline) && (tile_number < tilesToDo) && TPtile_exists("blotch") && (lastOutTile != NULL) ) { POINT pb; RECTANGLE rSize; TILE blotchTile; blotchTile = TPname_to_tile("blotch"); rSize = TPsize_of_tile(blotchTile); pb.x = p1.x - rSize.x_right / 2; pb.y = p1.y - rSize.y_top / 2; normalTile = FALSE; TPpaint_tile(blotchTile, lastOutTile, pb); normalTile = TRUE; } return(p); } /*--------------------------------------------------------- * These 4 routines each return a corner of a rectangle * * Results: A point. * * Side Effects: None. *--------------------------------------------------------- */ POINT rLL(r) /* the lower left corner */ RECTANGLE r; { POINT p; p.x = r.x_left; p.y = r.y_bot; return(p); } POINT rLR(r) /* the lower right corner */ RECTANGLE r; { POINT p; p.x = r.x_right; p.y = r.y_bot; return(p); } POINT rUL(r) /* the upper left corner */ RECTANGLE r; { POINT p; p.x = r.x_left; p.y = r.y_top; return(p); } POINT rUR(r) /* the upper right corner */ RECTANGLE r; { POINT p; p.x = r.x_right; p.y = r.y_top; return(p); } /*--------------------------------------------------------- * Return a corner of a tile, relative to the lower left * corner of that tile. * * Results: A point. * * Side Effects: None. *--------------------------------------------------------- */ POINT tLL(t) /* the lower left corner */ TILE t; { POINT p; t = t; /* for lint */ p.x = 0; p.y = 0; return(p); } POINT tLR(t) /* the lower right corner */ TILE t; { POINT p; if (t == NULL) p.x = 0; else p.x = ((CellDef *)t)->cd_bbox.r_xtop - ((CellDef *)t)->cd_bbox.r_xbot; p.y = 0; return(p); } POINT tUL(t) /* the upper left corner */ TILE t; { POINT p; p.x = 0; if (t == NULL) p.y = 0; else p.y = ((CellDef *)t)->cd_bbox.r_ytop - ((CellDef *)t)->cd_bbox.r_ybot; return(p); } POINT tUR(t) /* the upper right corner */ TILE t; { POINT p; if (t == NULL) p.x = p.y = 0; else { p.x = ((CellDef *)t)->cd_bbox.r_xtop - ((CellDef *)t)->cd_bbox.r_xbot; p.y = ((CellDef *)t)->cd_bbox.r_ytop - ((CellDef *)t)->cd_bbox.r_ybot; } return(p); } /*--------------------------------------------------------- * Return a rectangle the size of the tile, and located at * the origin. * * Results: A rectangle. * * Side Effects: None. *--------------------------------------------------------- */ RECTANGLE TPsize_of_tile(t) TILE t; { RECTANGLE rect; Rect from_box; rect.x_left = 0; rect.y_bot = 0; if (t == NULL) rect.x_right = rect.y_top = 0; else { from_box = ((CellDef *)t)->cd_bbox; rect.x_right = from_box.r_xtop - from_box.r_xbot; rect.y_top = from_box.r_ytop - from_box.r_ybot; } return(rect); } /*--------------------------------------------------------- * Remove from a tile all labels that begin with a * certain character. * * This is useful for removing labels that where stretch lines * or for alignment purposes. * * Results: None. * * Side Effects: Labels are removed from a cell in the * Magic database. * * Notes: similar to DBKillLabel in Magic *--------------------------------------------------------- */ static char killchar; bool TPstripfunc(lab) Label *lab; { return ((lab->lab_text != NULL) && (lab->lab_text[0] == killchar)); } TPstrip_labels(t,c) TILE t; /* the tile to remove labels from */ char c; /* the character that the labels start with */ { CellDef *def; if (t == NULL) return; def = (CellDef *) t; killchar = c; DBEraseLabelsByFunction(def, TPstripfunc); DBReComputeBbox((CellDef *) def); } /*--------------------------------------------------------- * Remove labels from a given area of a tile. * * This is useful for removing labels that are point things out to * the module generator but aren't supposed to appear in the final * layout. * * Results: None. * * Side Effects: Labels are removed from a cell in the * Magic database. * * Notes: similar to DBKillLabel in Magic *--------------------------------------------------------- */ static Rect removearea; static char *removename; bool TPremovefunc(lab) Label *lab; { return ((GEO_SURROUND(&removearea, &(lab->lab_rect)) && ((removename == NULL) || (strcmp(removename, lab->lab_text) == 0)))); } TPremove_labels(t, name, r) TILE t; /* The tile to remove labels from. */ char *name; /* If non-NULL, remove only labels with this name. */ RECTANGLE r; /* Only labels completely contained in this area will * be removed. */ { CellDef *def; if (t == NULL) return; def = (CellDef *) t; removename = name; removearea.r_xbot = r.x_left; removearea.r_ybot = r.y_bot; removearea.r_xtop = r.x_right; removearea.r_ytop = r.y_top; DBEraseLabelsByFunction(def, TPremovefunc); DBReComputeBbox((CellDef *) def); } /*--------------------------------------------------------- * Move paint and subcells from one cell to another. Don't touch labels. * * Results: None. * * Side Effects: * Modifies both from cell and to cell. *--------------------------------------------------------- */ tpMovePaint(u1, u2, r, t) CellUse *u1, *u2; /* Move everything from u1->cu_def to * u2->cu_def. The transforms in the * CellUses must be identity transforms. */ Rect *r; /* Only move stuff in this rectangle. */ Transform *t; /* Apply this transform during the move. */ { SearchContext scx; /* Copy the paint. */ scx.scx_use = u1; scx.scx_x = scx.scx_y = 0; scx.scx_area = *r; scx.scx_trans = *t; (void) DBCellCopyPaint(&scx, &DBAllTypeBits, 1, u2); /* Copy all subcells */ scx.scx_use = u1; scx.scx_x = scx.scx_y = 0; scx.scx_area = *r; scx.scx_trans = *t; (void) DBCellCopyAllCells(&scx, 0, u2, (Rect *) NULL); /* Delete source area */ DBErase(u1->cu_def, r, TT_SPACE); /* BOGUS: Need to erase cells too! */ } /*--------------------------------------------------------- * Given two tiles on the same type of tile plane, determine * what sort of material should bridge that gap. * * Results: A TileType. * * Side Effects: None. *--------------------------------------------------------- */ TileType tpGapMaterial(t1, t2) Tile *t1, *t2; { TileType type, type1, type2; type1 = TiGetType(t1); type2 = TiGetType(t2); if ((type1 == TT_SPACE) || (type2 == TT_SPACE)) return TT_SPACE; /* Using MIN is arbitrary, but symmetric and that's important. * We could be more clever and check to see which type is sretchable, * etc, but this seems to work OK. */ type = MIN(type1, type2); return type; } /*--------------------------------------------------------- * Fill in a gap in a cell def. Do this by looking at the * paint on either side of the gap. * * Results: None. * * Side Effects: None. *--------------------------------------------------------- */ void tpFillGapX(def, left, right) CellDef *def; int left, right; /* left and right (top and bottom) edges of gap */ { Point pl, pr; Tile *tl, *tr; Rect r; Plane *plane; TileType type; int pNum; if (left >= right) return; r.r_xbot = left; r.r_xtop = right; pl.p_x = left - 1; pr.p_x = right; /* loop through all paint planes in def */ for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++) { pl.p_y = pr.p_y = TiPlaneRect.r_ytop; plane = def->cd_planes[pNum]; tl = TiSrPoint((Tile *)NULL, plane, &pl); tr = TiSrPoint((Tile *)NULL, plane, &pr); do { if ((tl == NULL) || (tr == NULL)) break; r.r_ytop = MIN(TOP(tl), TOP(tr)); r.r_ybot = MAX(BOTTOM(tl), BOTTOM(tr)); if (r.r_ybot < r.r_ytop) { type = tpGapMaterial(tl, tr); if (type != TT_SPACE) DBPaint(def, &r, type); } if (BOTTOM(tl) < BOTTOM(tr)) { /* move right point down */ pr.p_y = BOTTOM(tr) - 1; tr = TiSrPoint((Tile *)NULL, plane, &pr); } else { /* move left point down */ pl.p_y = BOTTOM(tl) - 1; tl = TiSrPoint((Tile *)NULL, plane, &pl); } } while ((BOTTOM(tl) > TiPlaneRect.r_ybot) && (BOTTOM(tr) > TiPlaneRect.r_ybot)); } } void tpFillGapY(def, bottom, top) CellDef *def; int bottom, top; /* top and bottom edges of gap */ { Point pl, pr; Tile *tl, *tr; Rect r; Plane *plane; TileType type; int pNum; if (bottom >= top) return; r.r_ybot = bottom; r.r_ytop = top; pl.p_y = bottom - 1; pr.p_y = top; /* loop through all paint planes in def */ for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++) { pl.p_x = pr.p_x = TiPlaneRect.r_xtop; plane = def->cd_planes[pNum]; tl = TiSrPoint((Tile *)NULL, plane, &pl); tr = TiSrPoint((Tile *)NULL, plane, &pr); do { if ((tl == NULL) || (tr == NULL)) break; r.r_xtop = MIN(RIGHT(tl), RIGHT(tr)); r.r_xbot = MAX(LEFT(tl), LEFT(tr)); if (r.r_xbot < r.r_xtop) { type = tpGapMaterial(tl, tr); if (type != TT_SPACE) DBPaint(def, &r, type); } if (LEFT(tl) < LEFT(tr)) { /* move top point down */ pr.p_x = LEFT(tr) - 1; tr = TiSrPoint((Tile *)NULL, plane, &pr); } else { /* move bottom point down */ pl.p_x = LEFT(tl) - 1; tl = TiSrPoint((Tile *)NULL, plane, &pl); } } while ((LEFT(tl) > TiPlaneRect.r_xbot) && (LEFT(tr) > TiPlaneRect.r_xbot)); } } /*--------------------------------------------------------- * Stretch a cell along stretch lines. Stretch lines * are 1-dimensional labels, i.e. collapsed labels with * no area. The amount of stretch is specified in HALF-lambda * simply for compatability with previous versions of this system. * We round odd numbers of half-lambdas up to the nearest lambda. * * Results: None. * * Side Effects: * A cell in the Magic database is modified. *--------------------------------------------------------- */ TPstretch_tile(t,s,num) TILE t; /* the tile to be stretched */ char *s; /* the name of the stretch lines */ int num; /* the number of HALF-lambda to stretch */ { CellDef *def; CellUse *use; Label *lab, *labPrev; bool moreToDo, anyStretch; bool stretchx, stretchy; Point ll; static CellDef *yankDef = NULL; static CellUse *yankUse = NULL; if (t == NULL) { TPerror(0,"Null tile passed to TPstretch_tile"); return; }; num = (num + 1)/2; /* Magic only deals with whole lambdas */ def = (CellDef *) t; anyStretch = FALSE; /* create a dummy CellUse for the yank cell */ use = DBCellNewUse((CellDef *) def, (char *)NULL); use->cu_expandMask = 1; if (yankDef == NULL) { yankDef = DBCellLookDef("__YANK_MPACK__"); if (yankDef == (CellDef *) NULL) { /* create the CellDef */ yankDef = DBCellNewDef("__YANK_MPACK__", (char *) NULL); DBCellSetAvail(yankDef); yankDef->cd_flags |= CDINTERNAL; /* create a dummy CellUse for the yank cell */ yankUse = DBCellNewUse((CellDef *) yankDef, (char *)NULL); yankUse->cu_expandMask = 1; } else { TxError("Could not create internal cell '__YANK_MPACK__'\n"); MainExit(1); } } moreToDo = TRUE; while (moreToDo) { moreToDo = FALSE; for (labPrev = NULL, lab = def->cd_labels; lab != NULL; labPrev = lab, lab = lab->lab_next) { stretchx = (lab->lab_rect.r_xbot == lab->lab_rect.r_xtop); stretchy = (lab->lab_rect.r_ybot == lab->lab_rect.r_ytop); /* Is it a line, and is it of the right name? */ if ((stretchx != stretchy) && (strcmp(lab->lab_text, s) == 0)){ anyStretch = TRUE; ll = lab->lab_rect.r_ll; /* Delete the stretch label */ if (labPrev == NULL) def->cd_labels = lab->lab_next; else labPrev->lab_next = lab->lab_next; FREE((char *) lab); /* Do the stretch */ if (num > 0) { Rect r; Transform t; r = TiPlaneRect; t = GeoIdentityTransform; if (stretchx) { r.r_xbot = ll.p_x; tpMovePaint(use, yankUse, &r, &t); GeoTranslateTrans(&t, num, 0, &t); tpMovePaint(yankUse, use, &r, &t); tpFillGapX(def, ll.p_x, ll.p_x + num); for (labPrev = NULL, lab = def->cd_labels; lab != NULL; labPrev = lab, lab = lab->lab_next) { if (lab->lab_rect.r_xbot >= ll.p_x) lab->lab_rect.r_xbot += num; if (lab->lab_rect.r_xtop > ll.p_x) lab->lab_rect.r_xtop += num; } } else { r.r_ybot = ll.p_y; tpMovePaint(use, yankUse, &r, &t); GeoTranslateTrans(&t, 0, num, &t); tpMovePaint(yankUse, use, &r, &t); tpFillGapY(def, ll.p_y, ll.p_y + num); for (labPrev = NULL, lab = def->cd_labels; lab != NULL; labPrev = lab, lab = lab->lab_next) { if (lab->lab_rect.r_ybot >= ll.p_y) lab->lab_rect.r_ybot += num; if (lab->lab_rect.r_ytop > ll.p_y) lab->lab_rect.r_ytop += num; } }; }; /* Jump out to while loop, as we are scanning something which * may have just changed. */ moreToDo = TRUE; break; } } } if (anyStretch) { DBReComputeBbox(def); DBAdjustLabels(def, &TiPlaneRect); }; if (!DBCellDeleteUse(use)) TPerror(1, "Could not free CellUse\n"); } /*--------------------------------------------------------- * TPpaint_cell will place the 'from' tile in the 'to' tile * as a subcell. * * Results: The location of the tile in its parent. * * Side Effects: * This routine adds a new child to the specified tile * and places that cell at the specified point. * The from tile must be a tile that exists on disk -- i. e. it * was read from disk or was written from disk. *--------------------------------------------------------- */ RECTANGLE TPpaint_cell(from, to, p) TILE from; /* the tile to be placed as a cell */ TILE to; /* The tile that will get the new cell */ POINT p; /* a point within 'to' that indicates where the */ /* lower left corner of 'from' should go */ { CellDef *fromDef, *toDef; CellUse *newUse; Transform transform; RECTANGLE return_rect; return_rect.x_left = p.x; return_rect.x_right = p.x; return_rect.y_top = p.y; return_rect.y_bot = p.y; if ((from == NULL) || (to == NULL)) { TPerror(0,"Null tile passed to TPpaint_cell"); return return_rect; } fromDef = (CellDef *) from; toDef = (CellDef *) to; if (DBIsAncestor(fromDef, toDef)) { TPerror(0, "TPpaint_cell call creates circular structure -- ignored\n"); return return_rect; } newUse = DBCellNewUse(fromDef, (char *) NULL); if (!DBLinkCell(newUse, toDef)) { if (!DBCellDeleteUse(newUse)) TPerror(1, "Could not free CellUse\n"); TPerror(0, "TPpaint_cell could not link in new cell\n"); return return_rect; } transform = GeoIdentityTransform; transform.t_c += p.x; transform.t_f += p.y; DBSetTrans(newUse, &transform); DBPlaceCell(newUse, toDef); DBReComputeBbox(toDef); return_rect.x_left = p.x; return_rect.x_right = p.x + (fromDef->cd_bbox.r_xtop - fromDef->cd_bbox.r_xbot); return_rect.y_bot = p.y; return_rect.y_top = p.y + (fromDef->cd_bbox.r_ytop - fromDef->cd_bbox.r_ybot); return return_rect; } /* add two points */ POINT TPadd_pp(p1,p2) POINT p1, p2; { POINT r; r.x = p1.x + p2.x; r.y = p1.y + p2.y; return r; } /* subtract two points */ POINT TPsub_pp(p1,p2) POINT p1, p2; { POINT r; r.x = p1.x - p2.x; r.y = p1.y - p2.y; return r; } /* displace a rectangle by a point (vector) */ RECTANGLE TPadd_rp(r,p) RECTANGLE r; POINT p; { RECTANGLE ret; ret.x_left = r.x_left + p.x; ret.x_right = r.x_right + p.x; ret.y_bot = r.y_bot + p.y; ret.y_top = r.y_top + p.y; return ret; } /* displace a rectangle by a point (vector) */ RECTANGLE TPsub_rp(r,p) RECTANGLE r; POINT p; { RECTANGLE ret; ret.x_left = r.x_left - p.x; ret.x_right = r.x_right - p.x; ret.y_bot = r.y_bot - p.y; ret.y_top = r.y_top - p.y; return ret; } /*--------------------------------------------------------- * TPfind_label will look within an area of a tile for a label * that matches a string. * * Results: A boolean that says if the label was found. * * Side Effects: * A the second rectangle passed becomes the location * of the label found. *--------------------------------------------------------- */ int TPfind_label(tile, rec, str, ret) TILE tile; /* the tile in which to look */ RECTANGLE *rec; /* a pointer to a rectangle that constrains the area in which we are looking, or NULL */ char str[STRLEN]; /* the label name to match */ RECTANGLE *ret; /* a pointer to a rectangle that should be filled in with the label position */ { Label *lab; CellDef *def; Rect box; if (tile == NULL) return FALSE; def = (CellDef *) tile; for (lab = def->cd_labels; lab != NULL; lab = lab->lab_next) { box = lab->lab_rect; if (strcmp(str, lab->lab_text) == 0) { if ( (rec == NULL) || ( (box.r_xtop <= rec->x_right) && (box.r_xbot >= rec->x_left) && (box.r_ytop <= rec->y_top) && (box.r_ybot >= rec->y_bot) ) ) { ret->x_right = box.r_xtop; ret->x_left = box.r_xbot; ret->y_top = box.r_ytop; ret->y_bot = box.r_ybot; return TRUE; } } } return FALSE; } /*--------------------------------------------------------- * Find out how big lambda is, in centi-microns. * * Results: An integer giving the number of centi-microns per lambda * * Side Effects: * None. *--------------------------------------------------------- */ int TPget_lambda() { extern int CIFOutputScaleFactor(); return CIFOutputScaleFactor(); }