1 /*
2  * plotPS.c --
3  *
4  * This file contains procedures that generate PS-format files
5  * to describe a section of layout.
6  *
7  *     *********************************************************************
8  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
9  *     * Permission to use, copy, modify, and distribute this              *
10  *     * software and its documentation for any purpose and without        *
11  *     * fee is hereby granted, provided that the above copyright          *
12  *     * notice appear in all copies.  The University of California        *
13  *     * makes no representations about the suitability of this            *
14  *     * software for any purpose.  It is provided "as is" without         *
15  *     * express or implied warranty.  Export of this software outside     *
16  *     * of the United States of America may require an export license.    *
17  *     *********************************************************************
18  */
19 
20 #ifndef lint
21 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/plot/plotPS.c,v 1.3 2010/06/24 12:37:25 tim Exp $";
22 #endif  /* not lint */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "utils/magic.h"
29 #include "utils/geometry.h"
30 #include "tiles/tile.h"
31 #include "utils/hash.h"
32 #include "database/database.h"
33 #include "utils/tech.h"
34 #include "utils/malloc.h"
35 #include "utils/utils.h"
36 #include "windows/windows.h"
37 #include "commands/commands.h"
38 #include "dbwind/dbwind.h"
39 #include "textio/textio.h"
40 
41 /* Records of the following type are used to describe how to generate
42  * PS output for a particular set of mask layers.  Each style
43  * describes the PS figures to draw for a particular set of
44  * layers.  A single layer may participate in several ps styles.
45  */
46 
47 typedef struct psstyle
48 {
49     TileTypeBitMask grs_layers;		/* Layers to plot in this style. */
50     int grs_stipple;			/* Index of fill to use. */
51     int grs_color;			/* Index of color to use. */
52     struct psstyle *grs_next;	/* Next style in chain. */
53 } PSStyle;
54 
55 typedef struct pspattern
56 {
57     int 		index;
58     unsigned long	stipple[8];
59     struct pspattern	*pat_next;
60 } PSPattern;
61 
62 typedef struct pscolor
63 {
64     int			index;
65     unsigned char	color[4];
66     struct pscolor	*col_next;
67 } PSColor;
68 
69 
70 static PSStyle *plotPSStyles = NULL;
71 static PSPattern *plotPSPatterns = NULL;
72 static PSColor	*plotPSColors = NULL;
73 
74 int delta, xnmargin, ynmargin, xpmargin, ypmargin;
75 float fscale;
76 
77 /* Most of the grs_stipple values are PS stipple numbers.  However,
78  * if a grs_stipple value is less than zero, it means something special.
79  * The definitions below give the possible alternatives:
80  *
81  * CROSS:		Draw a thick outline around the tile with
82  *			a cross through it (used for contacts).
83  * BORDER:		Same as CROSS, except draw the outline with
84  *			no cross through it.
85  * SOLID:		This is the same as a solid stipple but renders
86  *			much faster.
87  */
88 
89 #define CROSS  -1
90 #define BORDER -2
91 #define SOLID  -3
92 
93 /* The definitions below give the integers used for various PS
94  * line drawing styles (brushes).
95  */
96 
97 #define PS_THIN		1
98 #define PS_MEDIUM	2
99 #define PS_THICK	3
100 
101 /* The variables below are used to pass information from the top-level
102  * procedure PlotPS down to the lower-level search functions
103  * that are invoked for pieces of the layout.
104  */
105 
106 static FILE *file;		/* File to use for output. */
107 static PSStyle *curStyle;	/* Current style being output. */
108 static PSColor *curColor;	/* Current color being output. */
109 static PSPattern *curPattern;	/* Current pattern being output. */
110 static int curLineWidth;	/* Current line width */
111 static int curFont;		/* Current font */
112 static TileTypeBitMask curMask;	/* Layers currently being searched:  this
113 				 * is the AND of the mask from curStyle and
114 				 * the layers that the user specified.
115 				 */
116 static Rect bbox;		/* Bounding box, in root coordinates, of
117 				 * area being plotted.
118 				 */
119 
120 /* Parameters passed to the plotting process */
121 
122 static char *defaultBoldFont = "/HelveticaBold";
123 static char *defaultFont = "/Helvetica";
124 char *PlotPSIdFont = NULL;
125 char *PlotPSNameFont = NULL;
126 char *PlotPSLabelFont = NULL;
127 int PlotPSIdSize = 8;
128 int PlotPSNameSize = 12;
129 int PlotPSLabelSize = 12;
130 int PlotPSBoundary = 1;	 /* Print boundaries around all layers */
131 int PlotPSHeight = 792;  /* 11 inches * 72 PS units/inch */
132 int PlotPSWidth = 612;   /* 8.5 inches */
133 int PlotPSMargin = 72;   /* 1 inch */
134 
135 int curx1, curx2, cury1, cury2;		    /* Last encountered line */
136 int curxbot, curybot, curwidth, curheight;  /* Last encountered rectangle */
137 
138 /*
139  * ----------------------------------------------------------------------------
140  *
141  * PSReset()
142  *
143  *	Plot optimization:   Reset buffered line and rectangle to default values
144  *
145  * ----------------------------------------------------------------------------
146  */
147 
148 void
PSReset()149 PSReset()
150 {
151     curxbot = curybot = curwidth = curheight = -2;
152     curx1 = curx2 = cury1 = cury2 = -2;
153 }
154 
155 
156 /*
157  * ----------------------------------------------------------------------------
158  *	PlotPSTechInit --
159  *
160  * 	Called once at beginning of technology file read-in to initialize
161  *	data structures.
162  *
163  * Results:
164  *	None.
165  *
166  * Side effects:
167  *	Clears out the list of things to plot.
168  * ----------------------------------------------------------------------------
169  */
170 
171 void
PlotPSTechInit()172 PlotPSTechInit()
173 {
174     int i, j;
175     PSStyle *style;
176     PSColor *color;
177     PSPattern *pattern;
178 
179     /* Clear out any old information */
180 
181     for (style = plotPSStyles; style != NULL; style = style->grs_next)
182     {
183 	freeMagic((char *) style);
184     }
185     plotPSStyles = NULL;
186 
187     for (pattern = plotPSPatterns; pattern != NULL; pattern = pattern->pat_next)
188     {
189 	freeMagic((char *) pattern);
190     }
191     plotPSPatterns = NULL;
192 
193     for (color = plotPSColors; color != NULL; color = color->col_next)
194     {
195 	freeMagic((char *) color);
196     }
197     plotPSColors = NULL;
198 
199     if (!PlotPSIdFont)
200 	StrDup(&PlotPSIdFont, defaultFont);
201     if (!PlotPSNameFont)
202 	StrDup(&PlotPSNameFont, defaultBoldFont);
203     if (!PlotPSLabelFont)
204 	StrDup(&PlotPSLabelFont, defaultFont);
205 }
206 
207 /*
208  * ----------------------------------------------------------------------------
209  *	PlotPSTechLine --
210  *
211  * 	This procedure is invoked by the technology module once for
212  *	each line in the "ps" subsection of the "plot" section
213  *	of the technology file.
214  *
215  * Results:
216  *	Always returns TRUE (otherwise the technology module would
217  *	abort Magic with a fatal error).
218  *
219  * Side effects:
220  *	Builds up the table of PS styles.
221  * ----------------------------------------------------------------------------
222  */
223 
224 bool
PlotPSTechLine(sectionName,argc,argv)225 PlotPSTechLine(sectionName, argc, argv)
226     char *sectionName;		/* Name of this section (unused). */
227     int argc;			/* Number of arguments on line. */
228     char *argv[];		/* Pointers to fields of line. */
229 {
230     PSStyle *newstyle;
231     PSColor *newcolor;
232     PSPattern *newpattern;
233     int i, color, stipple;
234 
235     if (argc != 9 && argc != 5 && argc != 3)
236     {
237 	TechError("\"ps\" lines must have either 9, 5, or 3 arguments.\n");
238 	return TRUE;
239     }
240 
241     if (argc == 9)	/* pattern definition */
242     {
243 	newpattern = (PSPattern *) mallocMagic(sizeof(PSPattern));
244 	sscanf(argv[0], "%d", &(newpattern->index));
245 	for(i = 0; i < 8; i++)
246 	{
247 	    sscanf(argv[1 + i], "%08lx", &(newpattern->stipple[i]));
248 	}
249 	newpattern->pat_next = plotPSPatterns;
250 	plotPSPatterns = newpattern;
251     }
252     else if (argc == 5)	/* color definition */
253     {
254 	int tmpint;
255 	newcolor = (PSColor *) mallocMagic(sizeof(PSColor));
256 	sscanf(argv[0], "%d", &(newcolor->index));
257 	for(i = 0; i < 4; i++)
258 	{
259 	    sscanf(argv[1 + i], "%d", &tmpint);
260 	    newcolor->color[i] = (unsigned char)(tmpint & 0xff);
261 	}
262 	newcolor->col_next = plotPSColors;
263 	plotPSColors = newcolor;
264     }
265     else {   /* 3 args: layer definition */
266 	if (!StrIsInt(argv[1]))
267 	{
268 	    TechError("2nd field must be an integer\n");
269 	    return TRUE;
270 	}
271 	color = atoi(argv[1]);
272 
273 	if (strcmp(argv[2], "X") == 0)
274 	    stipple = CROSS;
275 	else if (strcmp(argv[2], "B") == 0)
276 	    stipple = BORDER;
277 	else if (strcmp(argv[2], "S") == 0)
278 	    stipple = SOLID;
279 	else
280 	{
281 	    if (!StrIsInt(argv[2]))
282 	    {
283 		TechError("3rd field must be an integer or \"S\", \"X\", or \"B\".\n");
284 		return TRUE;
285 	    }
286 	    stipple = atoi(argv[2]);
287         }
288 
289 	newstyle = (PSStyle *) mallocMagic(sizeof(PSStyle));
290 
291 	DBTechNoisyNameMask(argv[0], &newstyle->grs_layers);
292 
293 	/* Replace non-primary contact images with primary images. */
294 
295 	for (i = TT_TECHDEPBASE; i < DBNumTypes; i++)
296 	{
297 	    if TTMaskHasType(&newstyle->grs_layers, i)
298 		TTMaskSetMask(&newstyle->grs_layers, &DBLayerTypeMaskTbl[i]);
299 	}
300 	TTMaskAndMask(&newstyle->grs_layers, &DBUserLayerBits);
301 	newstyle->grs_stipple = stipple;
302 	newstyle->grs_color = color;
303 	newstyle->grs_next = plotPSStyles;
304 	plotPSStyles = newstyle;
305     }
306 
307     return TRUE;
308 }
309 
310 /*
311  * ----------------------------------------------------------------------------
312  *
313  * plotPSFlushRect()
314  *
315  *	Plot optimization:   Draw last buffered rectangle.
316  *
317  * ----------------------------------------------------------------------------
318  */
319 
320 void
plotPSFlushRect(style)321 plotPSFlushRect(style)
322     int style;
323 {
324     if (curwidth > 0)
325     {
326 	if (style == SOLID)
327 	    fprintf(file, "%d %d %d %d ms\n", curxbot, curybot,
328 			curwidth, curheight);
329 	else
330 	    fprintf(file, "%d %d %d %d fb\n", curxbot, curybot,
331 			curxbot + curwidth, curybot + curheight);
332     }
333 }
334 
335 /*
336  * ----------------------------------------------------------------------------
337  *
338  * plotPSFlushLine()
339  *
340  *	Plot optimization:   Draw last buffered line.
341  *
342  * ----------------------------------------------------------------------------
343  */
344 
345 void
plotPSFlushLine()346 plotPSFlushLine()
347 {
348     if (cury1 == cury2)
349     {
350 	if (curx1 != curx2)	/* true only if nothing is buffered */
351 	    fprintf(file, "%d %d %d hl\n", curx2 - curx1, curx1, cury1);
352     }
353     else if (curx1 == curx2)
354 	fprintf(file, "%d %d %d vl\n", cury2 - cury1, curx1, cury1);
355     else
356 	fprintf(file, "%d %d %d %d ml\n", curx1, cury1, curx2, cury2);
357 }
358 
359 
360 /*
361  * ----------------------------------------------------------------------------
362  *
363  * plotPSLine --
364  *
365  * 	Outputs a line into the current PS file.
366  *
367  * Results:
368  *	None.
369  *
370  * Side effects:
371  *	I/O.
372  *
373  * ----------------------------------------------------------------------------
374  */
375 
376 void
plotPSLine(p1,p2)377 plotPSLine(p1, p2)
378     Point *p1, *p2;		/* Endpoints of line, given in root
379 				 * coordinates.
380 				 */
381 {
382     int x1, x2, y1, y2, limit, diff;
383     bool tmptf;
384 
385     /* Clip the line to the rectangular area being output.  First,
386      * arrange for the first x-coordinate to be the smaller, then
387      * clip against vertical lines at the x-boundaries.
388      */
389 
390     if (p1->p_x <= p2->p_x)
391     {
392 	x1 = p1->p_x - bbox.r_xbot;
393 	x2 = p2->p_x - bbox.r_xbot;
394 	y1 = p1->p_y - bbox.r_ybot;
395 	y2 = p2->p_y - bbox.r_ybot;
396     }
397     else
398     {
399 	x1 = p2->p_x - bbox.r_xbot;
400 	x2 = p1->p_x - bbox.r_xbot;
401 	y1 = p2->p_y - bbox.r_ybot;
402 	y2 = p1->p_y - bbox.r_ybot;
403     }
404     limit = bbox.r_xtop - bbox.r_xbot;
405     if ((x1 > limit) || (x2 < 0)) return;
406 
407     /* Now clip against horizontal lines at the y-boundaries. */
408 
409     if (y2 < y1)
410     {
411 	float tmp;
412 	tmp = y2; y2 = y1; y1 = tmp;
413 	tmp = x2; x2 = x1; x1 = tmp;
414     }
415     limit = bbox.r_ytop - bbox.r_ybot;
416     if ((y1 > limit) || (y2 < 0)) return;
417 
418     /* compare against last output line and merge if possible */
419     if (((x1 == x2) && (x1 == curx1) && (x2 == curx2)
420 		&& ((tmptf = (y1 == cury2)) || (y2 == cury1))))
421     {
422 	if (tmptf) cury2 = y2;
423 	else cury1 = y1;
424     }
425     else if (((y1 == y2) && (y1 == cury1) && (y2 == cury2)
426 		&& ((tmptf = (x1 == curx2)) || (x2 == curx1))))
427     {
428 	if (tmptf) curx2 = x2;
429 	else curx1 = x1;
430     }
431     else
432     {
433 	plotPSFlushLine();
434 	curx1 = x1;
435 	curx2 = x2;
436 	cury1 = y1;
437 	cury2 = y2;
438     }
439 }
440 
441 
442 /*
443  * ----------------------------------------------------------------------------
444  *
445  * plotPSRect --
446  *
447  * 	Outputs PS statements to draw a rectangular area as
448  *	an outline with a given line style.
449  *
450  * Results:
451  *	None.
452  *
453  * Side effects:
454  *	Adds information to the current PS file.
455  *
456  * ----------------------------------------------------------------------------
457  */
458 
459 void
plotPSRect(rect,style)460 plotPSRect(rect, style)
461     Rect *rect;	/* Rectangle to be drawn, in root coords. */
462     int style;
463 {
464     int x, y, w, h;
465 
466     /* Output all boxes with any part visible.  Depend on PostScript to */
467     /* do the clipping of any boxes crossing the plot boundary.		*/
468 
469     x = rect->r_xbot - bbox.r_xbot;
470     if ((x < 0) || (rect->r_xbot > bbox.r_xtop)) return;
471     w = rect->r_xtop - rect->r_xbot;
472     y = rect->r_ybot - bbox.r_ybot;
473     if ((y < 0) || (rect->r_ybot > bbox.r_ytop)) return;
474     h = rect->r_ytop - rect->r_ybot;
475 
476     fprintf(file, "%d %d %d %d m%c\n", x, y, w, h, (style == CROSS) ? 'x' :
477 		(style == SOLID) ? 's' : 'r');
478 }
479 
480 
481 /*
482  * ----------------------------------------------------------------------------
483  *
484  * plotPSPaint --
485  *
486  * 	This procedure is invoked once for each paint rectangle in
487  *	the area being plotted.
488  *
489  * Results:
490  *	Always returns 0 to keep the search alive.
491  *
492  * Side effects:
493  *	Outputs information for the tile, including stipple for its
494  *	interior, and a solid line for any portion of the boundary
495  *	of the tile that is adjacent to a tile NOT in this style.
496  *
497  * ----------------------------------------------------------------------------
498  */
499 
500 int
plotPSPaint(tile,cxp)501 plotPSPaint(tile, cxp)
502     Tile *tile;			/* Tile that's of type to be output. */
503     TreeContext *cxp;		/* Describes search in progress. */
504 {
505     Rect tileArea, edge, rootArea;
506     int xbot, width, ybot, height;
507     Tile *neighbor;
508     SearchContext *scx = cxp->tc_scx;
509     bool tmptf;
510     TileType ntype;
511 
512     /* First transform tile coords to root coords */
513 
514     TiToRect(tile, &tileArea);
515     GeoTransRect(&scx->scx_trans, &tileArea, &rootArea);
516 
517     /* See if this tile gets special handling. */
518 
519     if ((curStyle->grs_stipple == CROSS) || (curStyle->grs_stipple == BORDER))
520     {
521 	/* Draw tile as a thick outline with a cross from corner
522 	 * to corner, and skip the rest of this procedure.
523 	 */
524 
525 	Point ul, lr;
526 
527 	if (curLineWidth != PS_MEDIUM) {
528 	    fprintf(file, "l2\n");
529 	    curLineWidth = PS_MEDIUM;
530 	}
531 
532 	plotPSRect(&rootArea, curStyle->grs_stipple);
533 	return 0;
534     }
535 
536     /* If this is a triangle, output the last rect and deal with this one */
537     /* individually.							  */
538 
539     if (IsSplit(tile))
540     {
541 	int np, i, j;
542 	TileType dinfo;
543 	Point polyp[5];
544 
545 	plotPSFlushRect(curStyle->grs_stipple);
546 	plotPSFlushLine();
547 	PSReset();
548 
549 	/* Side and direction are altered by geometric transformations */
550 
551 	dinfo = DBTransformDiagonal(TiGetTypeExact(tile), &scx->scx_trans);
552 
553 	/* Use GrClipTriangle() routine to get the n-sided polygon that */
554 	/* results from clipping a triangle to the clip region.		*/
555 
556 	GrClipTriangle(&rootArea, &bbox, TRUE, dinfo, polyp, &np);
557 	for (i = 0; i < np; i++)
558 	{
559 	   polyp[i].p_x -= bbox.r_xbot;
560 	   polyp[i].p_y -= bbox.r_ybot;
561 	   fprintf(file, "%d %d ", polyp[i].p_x, polyp[i].p_y);
562 	}
563 	fprintf(file, "%d tb\n", np);
564 
565 	if (PlotPSBoundary)
566 	{
567 	    if (curLineWidth != PS_THIN) {
568 		fprintf(file, "l1\n");
569 		curLineWidth = PS_THIN;
570 	    }
571 
572 	    /* Diagonal is always drawn */
573 
574 	    for (i = 0; i < np; i++)
575 	    {
576 		j = (i + 1) % np;
577 		if (polyp[i].p_x != polyp[j].p_x &&
578 			polyp[i].p_y != polyp[j].p_y)
579 		{
580 		    fprintf(file, "%d %d %d %d ml\n", polyp[i].p_x, polyp[i].p_y,
581 			polyp[j].p_x, polyp[j].p_y);
582 		    break;
583 		}
584 	    }
585 	}
586     }
587     else
588     {
589 
590 	/* This tile gets "normal" processing (i.e. stippling and outlining).
591 	 * Clip it to the plotting area and output.
592 	 */
593 
594 	GeoClip(&rootArea, &bbox);
595 	xbot = rootArea.r_xbot - bbox.r_xbot;
596 	width = rootArea.r_xtop - rootArea.r_xbot;
597 	ybot = rootArea.r_ybot - bbox.r_ybot;
598 	height = rootArea.r_ytop - rootArea.r_ybot;
599 
600 	/* compare against last output rectangle and merge if possible */
601 	if ((width == curwidth) && (xbot == curxbot) && ((tmptf = (ybot == curybot
602 			+ curheight)) || (ybot + height == curybot)))
603 	{
604 	    curheight += height;
605 	    if (!tmptf) curybot = ybot;
606 	}
607 	else if ((height == curheight) && (ybot == curybot) && ((tmptf = (xbot
608 		== curxbot + curwidth)) || (xbot + width == curxbot)))
609 	{
610 	    curwidth += width;
611 	    if (!tmptf) curxbot = xbot;
612 	}
613 	else
614 	{
615 	    plotPSFlushRect(curStyle->grs_stipple);
616 	    curheight = height;
617 	    curwidth = width;
618 	    curxbot = xbot;
619 	    curybot = ybot;
620 	}
621 
622 	if (PlotPSBoundary && (curLineWidth != PS_THIN)) {
623 	    fprintf(file, "l1\n");
624 	    curLineWidth = PS_THIN;
625 	}
626     }
627 
628     if (!PlotPSBoundary) return 0;	/* No borders */
629 
630     /* Now output lines for any edges between material of the type
631      * currently being drawn and material of other types.  This is
632      * done by searching along the tile's borders for neighbors that
633      * have the wrong types.  First, search the tile's bottom border
634      * (unless it is at infinity).
635      *
636      * (This code is essentially a duplicate of selRedisplayFunc())
637      */
638 
639     if (IsSplit(tile) && (!(SplitSide(tile) ^ SplitDirection(tile))))
640         goto searchleft;        /* nothing on bottom of split */
641 
642     if (tileArea.r_ybot > TiPlaneRect.r_ybot)
643     {
644 	edge.r_ybot = edge.r_ytop = tileArea.r_ybot;
645 	for (neighbor = LB(tile); LEFT(neighbor) < tileArea.r_xtop;
646 		neighbor = TR(neighbor))
647 	{
648 	    ntype = TiGetTopType(neighbor);
649 	    if (TTMaskHasType(&curMask, ntype)) continue;
650 	    edge.r_xbot = LEFT(neighbor);
651 	    edge.r_xtop = RIGHT(neighbor);
652 	    if (edge.r_xbot < tileArea.r_xbot) edge.r_xbot = tileArea.r_xbot;
653 	    if (edge.r_xtop > tileArea.r_xtop) edge.r_xtop = tileArea.r_xtop;
654 	    GeoTransRect(&scx->scx_trans, &edge, &rootArea);
655 	    plotPSLine(&rootArea.r_ll, &rootArea.r_ur);
656 	}
657     }
658 
659     /* Now go along the tile's left border, doing the same thing.   Ignore
660      * edges that are at infinity.
661      */
662 
663 searchleft:
664     if (IsSplit(tile) && SplitSide(tile))
665 	goto searchtop;		/* Nothing on left side of tile */
666 
667     if (tileArea.r_xbot > TiPlaneRect.r_xbot)
668     {
669 	edge.r_xbot = edge.r_xtop = tileArea.r_xbot;
670 	for (neighbor = BL(tile); BOTTOM(neighbor) < tileArea.r_ytop;
671 		neighbor = RT(neighbor))
672 	{
673  	    ntype = TiGetRightType(neighbor);
674 	    if (TTMaskHasType(&curMask, ntype)) continue;
675 	    edge.r_ybot = BOTTOM(neighbor);
676 	    edge.r_ytop = TOP(neighbor);
677 	    if (edge.r_ybot < tileArea.r_ybot) edge.r_ybot = tileArea.r_ybot;
678 	    if (edge.r_ytop > tileArea.r_ytop) edge.r_ytop = tileArea.r_ytop;
679 	    GeoTransRect(&scx->scx_trans, &edge, &rootArea);
680 	    plotPSLine(&rootArea.r_ll, &rootArea.r_ur);
681 	}
682     }
683 
684     /* Same thing for the tile's top border. */
685 
686 searchtop:
687     if (IsSplit(tile) && (SplitSide(tile) ^ SplitDirection(tile)))
688 	goto searchright;		/* Nothing on top side of tile */
689 
690     if (tileArea.r_ytop < TiPlaneRect.r_ytop)
691     {
692 	edge.r_ybot = edge.r_ytop = tileArea.r_ytop;
693 	for (neighbor = RT(tile); RIGHT(neighbor) > tileArea.r_xbot;
694 		neighbor = BL(neighbor))
695 	{
696 	    ntype = TiGetBottomType(neighbor);
697 	    if (TTMaskHasType(&curMask, ntype)) continue;
698 	    edge.r_xbot = LEFT(neighbor);
699 	    edge.r_xtop = RIGHT(neighbor);
700 	    if (edge.r_xbot < tileArea.r_xbot) edge.r_xbot = tileArea.r_xbot;
701 	    if (edge.r_xtop > tileArea.r_xtop) edge.r_xtop = tileArea.r_xtop;
702 	    GeoTransRect(&scx->scx_trans, &edge, &rootArea);
703 	    plotPSLine(&rootArea.r_ll, &rootArea.r_ur);
704 	}
705     }
706 
707     /* Finally, the right border. */
708 
709 searchright:
710     if (IsSplit(tile) && !SplitSide(tile))
711 	return 0;		/* Nothing on right side of tile */
712 
713     if (tileArea.r_xtop < TiPlaneRect.r_xtop)
714     {
715 	edge.r_xbot = edge.r_xtop = tileArea.r_xtop;
716 	for (neighbor = TR(tile); TOP(neighbor) > tileArea.r_ybot;
717 		neighbor = LB(neighbor))
718 	{
719 	    ntype = TiGetLeftType(neighbor);
720 	    if (TTMaskHasType(&curMask, ntype)) continue;
721 	    edge.r_ybot = BOTTOM(neighbor);
722 	    edge.r_ytop = TOP(neighbor);
723 	    if (edge.r_ybot < tileArea.r_ybot) edge.r_ybot = tileArea.r_ybot;
724 	    if (edge.r_ytop > tileArea.r_ytop) edge.r_ytop = tileArea.r_ytop;
725 	    GeoTransRect(&scx->scx_trans, &edge, &rootArea);
726 	    plotPSLine(&rootArea.r_ll, &rootArea.r_ur);
727 	}
728     }
729 
730     return 0;
731 }
732 
733 
734 /*
735  * ----------------------------------------------------------------------------
736  *
737  * plotPSLabelPosition --
738  *
739  *	Determine the label position, orientation, and approximate bounding box
740  *
741  * ----------------------------------------------------------------------------
742  */
743 
744 int
plotPSLabelPosition(scx,label,x,y,p)745 plotPSLabelPosition(scx, label, x, y, p)
746     SearchContext *scx;		/* Describes state of search when label
747 				 * was found.
748 				 */
749     Label *label;		/* Label that was found. */
750     int *x;			/* returned x position */
751     int *y;			/* returned y position */
752     int *p;			/* returned orientation */
753 {
754     Rect rootArea;
755     int pos;
756 
757     /* Mapping from our GEO_xxx positions to PS object types: */
758 
759     static int psPosition[] =
760     {
761 	 5, /* CENTCENT */
762 	 1, /* TOPCENT */
763 	 0, /* TOPRIGHT */
764 	 4, /* CENTRIGHT */
765 	12, /* BOTRIGHT */
766 	13, /* BOTCENT */
767 	15, /* BOTLEFT */
768 	 7, /* CENTLEFT */
769 	 3  /* TOPLEFT */
770     };
771 
772     GeoTransRect(&scx->scx_trans, &label->lab_rect, &rootArea);
773     pos = GeoTransPos(&scx->scx_trans, label->lab_just);
774     switch (pos)
775     {
776 	case GEO_NORTH:
777 	case GEO_NORTHEAST:
778 	case GEO_NORTHWEST:
779 	    *y = (rootArea.r_ytop - bbox.r_ybot);
780 	    *y += delta;
781 	    break;
782 
783 	case GEO_CENTER:
784 	case GEO_WEST:
785 	case GEO_EAST:
786 	    *y = (rootArea.r_ytop + rootArea.r_ybot) / 2 - bbox.r_ybot;
787 	    break;
788 
789 	case GEO_SOUTH:
790 	case GEO_SOUTHEAST:
791 	case GEO_SOUTHWEST:
792 	    *y = (rootArea.r_ybot - bbox.r_ybot);
793 	    *y -= delta;
794 	    break;
795     }
796     switch (pos)
797     {
798 	case GEO_WEST:
799 	case GEO_NORTHWEST:
800 	case GEO_SOUTHWEST:
801 	    *x = (rootArea.r_xbot - bbox.r_xbot);
802 	    *x -= delta;
803 	    break;
804 
805 	case GEO_CENTER:
806 	case GEO_NORTH:
807 	case GEO_SOUTH:
808 	    *x = (rootArea.r_xtop + rootArea.r_xbot) / 2 - bbox.r_xbot;
809 	    break;
810 
811 	case GEO_EAST:
812 	case GEO_NORTHEAST:
813 	case GEO_SOUTHEAST:
814 	    *x = (rootArea.r_xtop - bbox.r_xbot);
815 	    *x += delta;
816 	    break;
817     }
818     *p = psPosition[pos];
819     return 0;
820 }
821 
822 
823 /*
824  * ----------------------------------------------------------------------------
825  *
826  * plotPSLabelBounds --
827  *
828  *	Estimate the bounding box extension based on label strings.
829  *	In reality, we need to know label sizes to compute the scale,
830  *	and we need to know the scale to compute label sizes.  However,
831  *	in practice, we can only estimate the label size anyway, so we
832  *	allow for some slop and just wing it.
833  *
834  * ----------------------------------------------------------------------------
835  */
836 
837 #define AVGCHARWIDTH 0.7
838 #define CHARHEIGHT 1.4
839 
840 int
plotPSLabelBounds(scx,label)841 plotPSLabelBounds(scx, label)
842     SearchContext *scx;		/* Describes state of search when label
843 				 * was found.
844 				 */
845     Label *label;		/* Label that was found. */
846 {
847     int pspos;
848     int ls, psxsize, psysize;
849     int llx, lly, urx, ury;
850     int psdelta = (int)((float)delta / fscale);
851 
852     plotPSLabelPosition(scx, label, &llx, &lly, &pspos);
853     urx = (int)((float)(llx - bbox.r_xtop) / fscale);
854     ury = (int)((float)(lly - bbox.r_ytop) / fscale);
855     llx = (int)((float)(bbox.r_xbot - llx) / fscale);
856     lly = (int)((float)(bbox.r_ybot - lly) / fscale);
857     ls = strlen(label->lab_text);
858 
859     psxsize = ls * (int)((float)PlotPSLabelSize * AVGCHARWIDTH);
860     psysize = (int)((float)PlotPSLabelSize * CHARHEIGHT);
861 
862     switch (pspos) {
863 	case 0:
864 	    ury += psysize + psdelta;
865 	    urx += psxsize + psdelta;
866 	    break;
867 	case 4:
868 	    ury += psysize / 2;
869 	    lly += psysize / 2;
870 	    urx += psxsize + psdelta;
871 	    break;
872 	case 12:
873 	    lly += psysize + psdelta;
874 	    urx += psxsize + psdelta;
875 	    break;
876 	case 13:
877 	    lly += psysize + psdelta;
878 	    urx += psxsize / 2;
879 	    llx += psxsize / 2;
880 	    break;
881 	case 15:
882 	    lly += psysize + psdelta;
883 	    llx += psxsize + psdelta;
884 	    break;
885 	case 7:
886 	    ury += psysize / 2;
887 	    lly += psysize / 2;
888 	    llx += psxsize + psdelta;
889 	    break;
890 	case 3:
891 	    ury += psysize + psdelta;
892 	    llx += psxsize + psdelta;
893 	    break;
894 	case 1:
895 	    ury += psysize + psdelta;
896 	    urx += psxsize / 2;
897 	    llx += psxsize / 2;
898 	    break;
899 	case 5:
900 	    ury += psysize / 2;
901 	    lly += psysize / 2;
902 	    urx += psxsize / 2;
903 	    llx += psxsize / 2;
904 	    break;
905     }
906 
907     if (xpmargin < urx) xpmargin = urx;
908     if (ypmargin < ury) ypmargin = ury;
909     if (xnmargin < llx) xnmargin = llx;
910     if (ynmargin < lly) ynmargin = lly;
911 
912     return 0;
913 }
914 
915 /*
916  * ----------------------------------------------------------------------------
917  *
918  * plotPSLabelBox --
919  *
920  *	Output the box connected to a label
921  *
922  * ----------------------------------------------------------------------------
923  */
924 
925 int
plotPSLabelBox(scx,label)926 plotPSLabelBox(scx, label)
927     SearchContext *scx;		/* Describes state of search when label
928 				 * was found.
929 				 */
930     Label *label;		/* Label that was found. */
931 {
932     Rect rootArea;
933     int x, y;
934 
935     GeoTransRect(&scx->scx_trans, &label->lab_rect, &rootArea);
936 
937     /* Output lines marking the label's area.  Different things are
938      * done depending on whether the label is a point, a line, or an
939      * area.
940      */
941 
942     if (curLineWidth != PS_MEDIUM) {
943 	fprintf(file, "l2\n");
944 	curLineWidth = PS_MEDIUM;
945     }
946 
947     if ((rootArea.r_xbot == rootArea.r_xtop) &&
948 	    (rootArea.r_ybot == rootArea.r_ytop))
949     {
950 	/* Point label.  Output a cross. */
951 
952 	x = (rootArea.r_xbot - bbox.r_xbot);
953 	y = (rootArea.r_ybot - bbox.r_ybot);
954 	fprintf(file, "%d %d %d pl\n", delta, x, y);
955     }
956     else if ((rootArea.r_xbot == rootArea.r_xtop) ||
957 	     (rootArea.r_ybot == rootArea.r_ytop))
958     {
959 	/* Line label.  Just draw a medium-thickness line. */
960 
961 	plotPSLine(&rootArea.r_ll, &rootArea.r_ur);
962     }
963     else
964     {
965 	/* Rectangular.  Draw lines around the boundary. */
966 
967 	plotPSRect(&rootArea, 0);
968     }
969     return 0;
970 }
971 
972 /*
973  * ----------------------------------------------------------------------------
974  *
975  * plotPSLabel --
976  *
977  * 	This procedure is invoked once for each label overlapping the
978  *	area being plotted.  It generates PS output to describe
979  *	the label.
980  *
981  * Results:
982  *	Always returns 0 to keep the search from aborting.
983  *
984  * Side effects:
985  *	PS information is output.
986  *
987  * ----------------------------------------------------------------------------
988  */
989 
990 int
plotPSLabel(scx,label)991 plotPSLabel(scx, label)
992     SearchContext *scx;		/* Describes state of search when label
993 				 * was found.
994 				 */
995     Label *label;		/* Label that was found. */
996 {
997     int x, y;
998     int pspos;
999 
1000     plotPSLabelPosition(scx, label, &x, &y, &pspos);
1001 
1002     /* Output the text for the label, if the label is within delta
1003      * of the area we're plotting (a large label could overlap a
1004      * bit of the area but stick out way off-screen too).
1005      */
1006 
1007     if ((x >= -delta) && (y >= -delta) &&
1008 	    (x <= (bbox.r_xtop - bbox.r_xbot) + delta) &&
1009 	    (y <= (bbox.r_ytop - bbox.r_ybot) + delta))
1010     {
1011 	fprintf(file, "(%s) %d %d %d lb\n", label->lab_text, pspos, x, y);
1012     }
1013     return 0;
1014 }
1015 
1016 /*
1017  * ----------------------------------------------------------------------------
1018  *
1019  * plotPSCell --
1020  *
1021  * 	This procedure is invoked once for each unexpanded cell that
1022  *	overlaps the area being plotted.
1023  *
1024  * Results:
1025  *	Always returns 0 to keep the search from aborting.
1026  *
1027  * Side effects:
1028  *	PS information is output to describe the cell.
1029  *
1030  * ----------------------------------------------------------------------------
1031  */
1032 
1033 int
plotPSCell(scx)1034 plotPSCell(scx)
1035     SearchContext *scx;		/* Describes cell whose bbox is to
1036 				 * be plotted.
1037 				 */
1038 {
1039     extern bool PlotShowCellNames;
1040     char idName[100];
1041     Rect rootArea;
1042     CellDef *def;
1043     int x, y;
1044 
1045     /* Convert the cell's bounding box to root coordinates and then
1046      * draw as a thick outline.
1047      */
1048 
1049     def = scx->scx_use->cu_def;
1050     GeoTransRect(&scx->scx_trans, &def->cd_bbox, &rootArea);
1051     if (curLineWidth != PS_THICK) {
1052 	fprintf(file, "l3\n");
1053 	curLineWidth = PS_THICK;
1054     }
1055     plotPSRect(&rootArea, 0);
1056 
1057     if (!PlotShowCellNames)
1058 	return 0;
1059 
1060     /* Output the cell definition's name in the top of the bounding box.
1061      * Use a bold font (#3), in a medium size (#2).  Make sure that the
1062      * name's positioning point is within the area we're plotting.
1063      */
1064 
1065     x = (rootArea.r_xtop + rootArea.r_xbot - 2*bbox.r_xbot)/2;
1066     y = (2*rootArea.r_ytop + rootArea.r_ybot - 3*bbox.r_ybot)/3;
1067     if ((x >= 0) && (y >= 0) &&
1068 	    (x <= (bbox.r_xtop - bbox.r_xbot)) &&
1069 	    (y <= (bbox.r_ytop - bbox.r_ybot)))
1070     {
1071 	fprintf(file, "f2 (%s) 5 %d %d lb\n", def->cd_name, x, y);
1072     }
1073 
1074     /* Output the cell id in the bottom of the bounding box.
1075      * Use an italic font (#2) in a medium size (#2).
1076      */
1077 
1078     x = (rootArea.r_xtop + rootArea.r_xbot - 2*bbox.r_xbot)/2;
1079     y = (rootArea.r_ytop + 2*rootArea.r_ybot - 3*bbox.r_ybot)/3;
1080     if ((x >= 0) && (y >= 0) &&
1081 	    (x <= (bbox.r_xtop - bbox.r_xbot)) &&
1082 	    (y <= (bbox.r_ytop - bbox.r_ybot)))
1083     {
1084 	(void) DBPrintUseId(scx, idName, 100, TRUE);
1085 	fprintf(file, "f3 (%s) 5 %d %d lb\n", idName, x, y);
1086     }
1087 
1088     return 0;
1089 }
1090 
1091 
1092 /*
1093  * ----------------------------------------------------------------------------
1094  *
1095  * PlotPS --
1096  *
1097  * 	This procedure generates a PS file to describe an area of
1098  *	a layout.
1099  *
1100  * Results:
1101  *	None.
1102  *
1103  * Side effects:
1104  *	None.
1105  *
1106  * ----------------------------------------------------------------------------
1107  */
1108 
1109 void
PlotPS(fileName,scx,layers,xMask)1110 PlotPS(fileName, scx, layers, xMask)
1111     char *fileName;			/* Name of PS file to write. */
1112     SearchContext *scx;			/* The use and area and transformation
1113 					 * in this describe what to plot.
1114 					 */
1115     TileTypeBitMask *layers;		/* Tells what layers to plot.  Only
1116 					 * paint layers in this mask, and also
1117 					 * expanded according to xMask, are
1118 					 * plotted.  If L_LABELS is set, then
1119 					 * labels on the layers are also
1120 					 * plotted, if expanded according to
1121 					 * xMask.  If L_CELL is set, then
1122 					 * subcells that are unexpanded
1123 					 * according to xMask are plotted as
1124 					 * bounding boxes.
1125 					 */
1126     int xMask;				/* An expansion mask, used to indicate
1127 					 * the window whose expansion status
1128 					 * will be used to determine
1129 					 * visibility.  Zero means treat
1130 					 * everything as expanded.
1131 					 */
1132 {
1133     int xsize, ysize;
1134     float yscale;
1135     FILE *infile;
1136     int i, j;
1137     int twidth, theight;
1138     char *fontptr, *fptr2, *fptr3;
1139     char line_in[100];
1140 
1141     PSReset();
1142 
1143     /* Compute a scale factor between our coordinates and PS
1144      * coordinates.
1145      */
1146 
1147     GeoTransRect(&scx->scx_trans, &scx->scx_area, &bbox);
1148     xsize = bbox.r_xtop - bbox.r_xbot;
1149     ysize = bbox.r_ytop - bbox.r_ybot;
1150     fscale = (float)(PlotPSWidth - 2 * PlotPSMargin) / (float)xsize;
1151     yscale = (float)(PlotPSHeight - 2 * PlotPSMargin) / (float)ysize;
1152     if (yscale < fscale) fscale = yscale;
1153 
1154     /* Compute a distance equal to 1/8th the size of a typical wire
1155      * (max of thicknesses of routing layers).  This is used to offset
1156      * text from labels and to compute cross size for point labels.
1157      */
1158 
1159     if (RtrMetalWidth > RtrPolyWidth)
1160 	delta = RtrMetalWidth / 8;
1161     else delta = RtrPolyWidth / 8;
1162     if (delta == 0) delta = 1;
1163 
1164     /* Go through labels once to estimate the bounding box, including labels */
1165 
1166     xnmargin = ynmargin = xpmargin = ypmargin = 0;
1167     if (TTMaskHasType(layers, L_LABEL))
1168     {
1169 	curMask = *layers;
1170 	TTMaskSetType(&curMask, TT_SPACE);
1171 	(void) DBTreeSrLabels(scx, &curMask, xMask, (TerminalPath *) NULL,
1172 		TF_LABEL_ATTACH, plotPSLabelBounds, (ClientData) NULL);
1173 	fscale = (float)(PlotPSWidth - 2 * PlotPSMargin - xnmargin - xpmargin)
1174 		/ (float)(xsize);
1175 	yscale = (float)(PlotPSHeight - 2 * PlotPSMargin - ynmargin - ypmargin)
1176 		/ (float)(ysize);
1177         if (yscale < fscale) fscale = yscale;
1178     }
1179     twidth = (xsize * fscale) + xnmargin + xpmargin;
1180     theight = (ysize * fscale) + ynmargin + ypmargin;
1181 
1182     /* Open the PS file and output header information. */
1183 
1184     file = PaOpen(fileName, "w", (char *) NULL, ".", (char *) NULL,
1185 	    (char **) NULL);
1186     if (file == NULL)
1187     {
1188 	TxError("Couldn't write PS file \"%s\".\n", fileName);
1189 	return;
1190     }
1191     fprintf(file, "%%!PS-Adobe-3.0 EPSF-3.0\n");
1192     fprintf(file, "%%%%BoundingBox: %d %d %d %d\n",
1193 		PlotPSMargin, PlotPSMargin, twidth + PlotPSMargin,
1194 		theight + PlotPSMargin);
1195     fontptr = PlotPSIdFont;
1196     fprintf(file, "%%%%DocumentNeededResources: font %s", fontptr);
1197     if (!Match(fptr2 = PlotPSNameFont, fontptr));
1198         fprintf(file, " font %s", fptr2);
1199     if (!Match(fptr3 = PlotPSLabelFont, fontptr))
1200 	if (!Match(fptr3, fptr2))
1201             fprintf(file, " font %s", fptr3);
1202     fprintf(file, "\n");
1203     fprintf(file, "%%%%EndComments\n");
1204 
1205     /* Insert the prolog here */
1206 
1207     infile = PaOpen("magicps", "r", ".pro", ".", SysLibPath, NULL);
1208     if (infile != NULL)
1209 	while(fgets(line_in, 99, infile) != NULL)
1210 	    fputs(line_in, file);
1211     else
1212 	fprintf(file, "\npostscript_prolog_is_missing\n\n");
1213 
1214     /* Insert the font definitions here. */
1215 
1216     fprintf(file, "/f1 { %.3f %s sf } def\n", (float)PlotPSLabelSize / fscale,
1217 		PlotPSLabelFont);
1218     fprintf(file, "/f2 { %.3f %s sf } def\n", (float)PlotPSNameSize / fscale,
1219 		PlotPSNameFont);
1220     fprintf(file, "/f3 { %.3f %s sf } def\n", (float)PlotPSIdSize / fscale,
1221 		PlotPSIdFont);
1222 
1223     /* Insert the color and stipple definitions here. */
1224 
1225     for (curColor = plotPSColors; curColor != NULL;
1226 	 curColor = curColor->col_next)
1227     {
1228 	fprintf(file, "/col%d {%.3f %.3f %.3f %.3f sc} bind def\n",
1229 		curColor->index,
1230 		(float)curColor->color[0] / 255.0,
1231 		(float)curColor->color[1] / 255.0,
1232 		(float)curColor->color[2] / 255.0,
1233 		(float)curColor->color[3] / 255.0);
1234     }
1235 
1236     for (curPattern = plotPSPatterns; curPattern != NULL;
1237 	curPattern = curPattern->pat_next)
1238     {
1239 	fprintf(file, "{<");
1240 	for (j = 0; j < 8; j++)
1241 	    fprintf(file, "%08lx%08lx", curPattern->stipple[j], curPattern->stipple[j]);
1242 	fprintf(file, ">} %d dp\n", curPattern->index);
1243     }
1244 
1245     fprintf(file, "%%%%EndResource\n%%%%EndProlog\n\n");
1246     fprintf(file, "%%%%Page: 1 1\n");
1247     fprintf(file, "/pgsave save def bop\n");
1248     fprintf(file, "%% 0 0 offsets\nninit\n");
1249     fprintf(file, "%d %d translate\n", PlotPSMargin + xnmargin, PlotPSMargin
1250 		+ ynmargin);
1251     fprintf(file, "%.3f %.3f scale\nminit\n", fscale, fscale);
1252     fprintf(file, "0 0 %d %d gsave rectclip\n", xsize, ysize);
1253     fprintf(file, "l2\nsp\n\n");
1254 
1255     curLineWidth = PS_MEDIUM;
1256 
1257     /* For each PS style, find all the paint layers that belong
1258      * to that style and put plot information into the file.
1259      */
1260 
1261     for (curStyle = plotPSStyles; curStyle != NULL;
1262 	 curStyle = curStyle->grs_next)
1263     {
1264 	fprintf(file, "col%d\n", curStyle->grs_color);
1265 	if (curStyle->grs_stipple >= 0)
1266 	    fprintf(file, "%d sl\n", curStyle->grs_stipple);
1267 	TTMaskAndMask3(&curMask, layers, &curStyle->grs_layers);
1268 	(void) DBTreeSrTiles(scx, &curMask, xMask, plotPSPaint,
1269 		(ClientData) NULL);
1270 	plotPSFlushRect(curStyle->grs_stipple);
1271 	plotPSFlushLine();
1272 	PSReset();
1273     }
1274 
1275     /* Output subcell bounding boxes, if they are wanted. */
1276 
1277     if (TTMaskHasType(layers, L_CELL))
1278     {
1279 	(void) DBTreeSrCells(scx, xMask, plotPSCell, (ClientData) NULL);
1280 	plotPSFlushRect(BORDER);
1281 	plotPSFlushLine();
1282     }
1283 
1284     /* Output label boxes followed by labels */
1285 
1286     if (TTMaskHasType(layers, L_LABEL))
1287     {
1288 	curMask = *layers;
1289 	TTMaskSetType(&curMask, TT_SPACE);
1290 	(void) DBTreeSrLabels(scx, &curMask, xMask, (TerminalPath *) NULL,
1291 		TF_LABEL_ATTACH, plotPSLabelBox, (ClientData) NULL);
1292 	plotPSFlushRect(BORDER);
1293 	plotPSFlushLine();
1294 	PSReset();
1295         fprintf(file, "grestore\n");	/* end clipping rectangle */
1296         fprintf(file, "f1 0 setgray\n");  /* set font, set color to black */
1297 
1298 	curMask = *layers;
1299 	TTMaskSetType(&curMask, TT_SPACE);
1300 	(void) DBTreeSrLabels(scx, &curMask, xMask, (TerminalPath *) NULL,
1301 		TF_LABEL_ATTACH, plotPSLabel, (ClientData) NULL);
1302     }
1303     else
1304     {
1305         fprintf(file, "grestore\n");	/* end clipping rectangle */
1306     }
1307 
1308     /* Output trailer information into the file, and close it. */
1309 
1310     fprintf(file, "pgsave restore showpage\n\n");
1311     fprintf(file, "%%%%Trailer\nMAGICsave restore\n%%%%EOF\n");
1312     fclose(file);
1313     return;
1314 }
1315