summaryrefslogtreecommitdiff
path: root/graphics.c
diff options
context:
space:
mode:
authorTim Edwards <tim@opencircuitdesign.com>2014-09-16 10:20:18 -0400
committerTim Edwards <tim@opencircuitdesign.com>2014-09-16 10:20:18 -0400
commit5dc5691ea246a85d546f3c1ff01fd13279731bd2 (patch)
tree233bf593f254f5525eaa4249d094b6dac4440004 /graphics.c
Initial commit at Tue Sep 16 10:20:18 EDT 2014 by tim on stravinsky.tim.linglan.net
Diffstat (limited to 'graphics.c')
-rw-r--r--graphics.c692
1 files changed, 692 insertions, 0 deletions
diff --git a/graphics.c b/graphics.c
new file mode 100644
index 0000000..4eab75d
--- /dev/null
+++ b/graphics.c
@@ -0,0 +1,692 @@
+/*------------------------------------------------------*/
+/* Graphics routines for qrouter */
+/*------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <tk.h>
+
+#include "qrouter.h"
+#include "qconfig.h"
+#include "node.h"
+#include "maze.h"
+#include "lef.h"
+
+/*------------------------------*/
+/* Type declarations */
+/*------------------------------*/
+
+void load_font(XFontStruct **);
+void createGC(Window, GC *, XFontStruct *);
+
+/*----------------------------------*/
+/* Global variables for X11 drawing */
+/*----------------------------------*/
+
+XFontStruct *font_info;
+Pixmap buffer = (Pixmap)0;
+Display *dpy;
+Window win;
+Colormap cmap;
+GC gc;
+Dimension width, height;
+
+#define SHORTSPAN 10
+#define LONGSPAN 127
+
+int spacing;
+int bluepix, greenpix, redpix, cyanpix, orangepix, goldpix;
+int blackpix, whitepix, graypix, ltgraypix, yellowpix;
+int magentapix, purplepix, greenyellowpix;
+int brownvector[SHORTSPAN];
+int bluevector[LONGSPAN];
+
+/*--------------------------------------------------------------*/
+/* Highlight a position on the graph. Do this on the actual */
+/* screen, not the buffer. */
+/*--------------------------------------------------------------*/
+
+void highlight(int x, int y) {
+
+ int i, xspc, yspc, hspc;
+ PROUTE *Pr;
+
+ // If Obs2[] at x, y is a source or dest, don't highlight
+ // Do this only for layer 0; it doesn't have to be rigorous.
+ for (i = 0; i < Num_layers; i++) {
+ Pr = &Obs2[i][OGRID(x, y, i)];
+ if (Pr->flags & (PR_SOURCE | PR_TARGET)) return;
+ }
+
+ hspc = spacing >> 1;
+ if (hspc == 0) hspc = 1;
+
+ xspc = (x + 1) * spacing - hspc;
+ yspc = height - (y + 1) * spacing - hspc;
+
+ XSetForeground(dpy, gc, yellowpix);
+ XFillRectangle(dpy, win, gc, xspc, yspc, spacing, spacing);
+ XFlush(dpy);
+}
+
+/*--------------------------------------*/
+/* Highlight source (in magenta) */
+/*--------------------------------------*/
+
+void highlight_source() {
+
+ int xspc, yspc, hspc;
+ int i, x, y;
+ PROUTE *Pr;
+
+ if (Obs2[0] == NULL) return;
+
+ // Determine the number of routes per width and height, if
+ // it has not yet been computed
+
+ hspc = spacing >> 1;
+ if (hspc == 0) hspc = 1;
+
+ // Draw source pins as magenta squares
+ XSetForeground(dpy, gc, magentapix);
+ for (i = 0; i < Num_layers; i++) {
+ for (x = 0; x < NumChannelsX[i]; x++) {
+ xspc = (x + 1) * spacing - hspc;
+ for (y = 0; y < NumChannelsY[i]; y++) {
+ Pr = &Obs2[i][OGRID(x, y, i)];
+ if (Pr->flags & PR_SOURCE) {
+ yspc = height - (y + 1) * spacing - hspc;
+ XFillRectangle(dpy, win, gc, xspc, yspc,
+ spacing, spacing);
+ }
+ }
+ }
+ }
+ XFlush(dpy);
+}
+
+/*--------------------------------------*/
+/* Highlight destination (in purple) */
+/*--------------------------------------*/
+
+void highlight_dest() {
+
+ int xspc, yspc, hspc, dspc;
+ int i, x, y;
+ PROUTE *Pr;
+
+ if (Obs2[0] == NULL) return;
+
+ // Determine the number of routes per width and height, if
+ // it has not yet been computed
+
+ dspc = spacing + 4; // Make target more visible
+ hspc = dspc >> 1;
+
+ // Draw destination pins as purple squares
+ XSetForeground(dpy, gc, purplepix);
+ for (i = 0; i < Num_layers; i++) {
+ for (x = 0; x < NumChannelsX[i]; x++) {
+ xspc = (x + 1) * spacing - hspc;
+ for (y = 0; y < NumChannelsY[i]; y++) {
+ Pr = &Obs2[i][OGRID(x, y, i)];
+ if (Pr->flags & PR_TARGET) {
+ yspc = height - (y + 1) * spacing - hspc;
+ XFillRectangle(dpy, win, gc, xspc, yspc,
+ dspc, dspc);
+ }
+ }
+ }
+ }
+ XFlush(dpy);
+}
+
+/*----------------------------------------------*/
+/* Highlight all the search starting points */
+/*----------------------------------------------*/
+
+void highlight_starts(POINT glist) {
+
+ int xspc, yspc, hspc;
+ POINT gpoint;
+
+ // Determine the number of routes per width and height, if
+ // it has not yet been computed
+
+ hspc = spacing >> 1;
+
+ XSetForeground(dpy, gc, greenyellowpix);
+ for (gpoint = glist; gpoint; gpoint = gpoint->next) {
+ xspc = (gpoint->x1 + 1) * spacing - hspc;
+ yspc = height - (gpoint->y1 + 1) * spacing - hspc;
+ XFillRectangle(dpy, win, gc, xspc, yspc, spacing, spacing);
+ }
+ XFlush(dpy);
+}
+
+/*--------------------------------------*/
+/* Highlight mask (in tan) */
+/*--------------------------------------*/
+
+void highlight_mask() {
+
+ int xspc, yspc, hspc;
+ int x, y;
+ u_char value;
+
+ if (RMask == NULL) return;
+
+ // Determine the number of routes per width and height, if
+ // it has not yet been computed
+
+ hspc = spacing >> 1;
+
+ // Draw destination pins as tan squares
+ for (x = 0; x < NumChannelsX[0]; x++) {
+ xspc = (x + 1) * spacing - hspc;
+ for (y = 0; y < NumChannelsY[0]; y++) {
+ XSetForeground(dpy, gc, brownvector[RMask[OGRID(x, y, 0)]]);
+ yspc = height - (y + 1) * spacing - hspc;
+ XFillRectangle(dpy, win, gc, xspc, yspc, spacing, spacing);
+ }
+ }
+ XFlush(dpy);
+}
+
+/*----------------------------------------------*/
+/* Draw a map of obstructions and pins */
+/*----------------------------------------------*/
+
+void
+map_obstruction()
+{
+ int xspc, yspc, hspc;
+ int i, x, y, n, norm;
+ u_char *Congestion;
+ u_char value, maxval;
+
+ hspc = spacing >> 1;
+
+ // Draw obstructions as light gray squares
+ XSetForeground(dpy, gc, ltgraypix);
+ for (i = 0; i < Num_layers; i++) {
+ for (x = 0; x < NumChannelsX[i]; x++) {
+ xspc = (x + 1) * spacing - hspc;
+ for (y = 0; y < NumChannelsY[i]; y++) {
+ if (Obs[i][OGRID(x, y, i)] & NO_NET) {
+ yspc = height - (y + 1) * spacing - hspc;
+ XFillRectangle(dpy, buffer, gc, xspc, yspc,
+ spacing, spacing);
+ }
+ }
+ }
+ }
+
+ // Draw pins as gray squares
+ XSetForeground(dpy, gc, graypix);
+ for (i = 0; i < Num_layers; i++) {
+ for (x = 0; x < NumChannelsX[i]; x++) {
+ xspc = (x + 1) * spacing - hspc;
+ for (y = 0; y < NumChannelsY[i]; y++) {
+ if (Nodesav[i][OGRID(x, y, i)] != NULL) {
+ yspc = height - (y + 1) * spacing - hspc;
+ XFillRectangle(dpy, buffer, gc, xspc, yspc,
+ spacing, spacing);
+ }
+ }
+ }
+ }
+}
+
+/*----------------------------------------------*/
+/* Draw a map of actual route congestion */
+/*----------------------------------------------*/
+
+void
+map_congestion()
+{
+ int xspc, yspc, hspc;
+ int i, x, y, n, norm;
+ u_char *Congestion;
+ u_char value, maxval;
+
+ hspc = spacing >> 1;
+
+ Congestion = (u_char *)calloc(NumChannelsX[0] * NumChannelsY[0],
+ sizeof(u_char));
+
+ // Analyze Obs[] array for congestion
+ for (i = 0; i < Num_layers; i++) {
+ for (x = 0; x < NumChannelsX[i]; x++) {
+ for (y = 0; y < NumChannelsY[i]; y++) {
+ value = (u_char)0;
+ n = Obs[i][OGRID(x, y, i)];
+ if (n & ROUTED_NET) value++;
+ if (n & BLOCKED_MASK) value++;
+ if (n & NO_NET) value++;
+ if (n & PINOBSTRUCTMASK) value++;
+ Congestion[OGRID(x, y, 0)] += value;
+ }
+ }
+ }
+
+ maxval = 0;
+ for (x = 0; x < NumChannelsX[0]; x++) {
+ for (y = 0; y < NumChannelsY[0]; y++) {
+ value = Congestion[OGRID(x, y, 0)];
+ if (value > maxval) maxval = value;
+ }
+ }
+ norm = (LONGSPAN - 1) / maxval;
+
+ // Draw destination pins as blue squares
+ for (x = 0; x < NumChannelsX[0]; x++) {
+ xspc = (x + 1) * spacing - hspc;
+ for (y = 0; y < NumChannelsY[0]; y++) {
+ XSetForeground(dpy, gc, bluevector[norm * Congestion[OGRID(x, y, 0)]]);
+ yspc = height - (y + 1) * spacing - hspc;
+ XFillRectangle(dpy, buffer, gc, xspc, yspc, spacing, spacing);
+ }
+ }
+
+ // Cleanup
+ free(Congestion);
+}
+
+/*----------------------------------------------------------------------*/
+/* Draw a map of route congestion estimated from net bounding boxes */
+/*----------------------------------------------------------------------*/
+
+void
+map_estimate()
+{
+ NET net;
+ int xspc, yspc, hspc;
+ int i, x, y, nwidth, nheight, area, length, value;
+ float density, *Congestion, norm, maxval;
+
+ hspc = spacing >> 1;
+
+ Congestion = (float *)calloc(NumChannelsX[0] * NumChannelsY[0],
+ sizeof(float));
+
+ // Use net bounding boxes to estimate congestion
+
+ for (i = 0; i < Numnets; i++) {
+ net = Nlnets[i];
+ nwidth = (net->xmax - net->xmin + 1);
+ nheight = (net->ymax - net->ymin + 1);
+ area = nwidth * nheight;
+ if (nwidth > nheight) {
+ length = nwidth + (nheight >> 1) * net->numnodes;
+ }
+ else {
+ length = nheight + (nwidth >> 1) * net->numnodes;
+ }
+ density = (float)length / (float)area;
+
+ for (x = net->xmin; x < net->xmax; x++)
+ for (y = net->ymin; y < net->ymax; y++)
+ Congestion[OGRID(x, y, 0)] += density;
+ }
+
+ maxval = 0.0;
+ for (x = 0; x < NumChannelsX[0]; x++) {
+ for (y = 0; y < NumChannelsY[0]; y++) {
+ density = Congestion[OGRID(x, y, 0)];
+ if (density > maxval) maxval = density;
+ }
+ }
+ norm = (float)(LONGSPAN - 1) / maxval;
+
+ // Draw destination pins as blue squares
+ for (x = 0; x < NumChannelsX[0]; x++) {
+ xspc = (x + 1) * spacing - hspc;
+ for (y = 0; y < NumChannelsY[0]; y++) {
+ value = (int)(norm * Congestion[OGRID(x, y, 0)]);
+ XSetForeground(dpy, gc, bluevector[value]);
+ yspc = height - (y + 1) * spacing - hspc;
+ XFillRectangle(dpy, buffer, gc, xspc, yspc, spacing, spacing);
+ }
+ }
+
+ // Cleanup
+ free(Congestion);
+}
+
+/*--------------------------------------*/
+/* Draw one net on the display */
+/*--------------------------------------*/
+
+void draw_net(NET net, u_char single, int *lastlayer) {
+
+ int i, xspc, yspc;
+ int layer;
+ SEG seg;
+ ROUTE rt;
+
+ if (dpy == NULL) return;
+
+ // Draw all nets, much like "emit_routes" does when writing
+ // routes to the DEF file.
+
+ rt = net->routes;
+ if (single && rt)
+ for (rt = net->routes; rt->next; rt = rt->next);
+
+ for (; rt; rt = rt->next) {
+ for (seg = rt->segments; seg; seg = seg->next) {
+ layer = seg->layer;
+ if (layer != *lastlayer) {
+ *lastlayer = layer;
+ switch(layer) {
+ case 0:
+ XSetForeground(dpy, gc, bluepix);
+ break;
+ case 1:
+ XSetForeground(dpy, gc, redpix);
+ break;
+ case 2:
+ XSetForeground(dpy, gc, cyanpix);
+ break;
+ case 3:
+ XSetForeground(dpy, gc, goldpix);
+ break;
+ default:
+ XSetForeground(dpy, gc, greenpix);
+ break;
+ }
+ }
+ XDrawLine(dpy, buffer, gc, spacing * (seg->x1 + 1),
+ height - spacing * (seg->y1 + 1),
+ spacing * (seg->x2 + 1),
+ height - spacing * (seg->y2 + 1));
+ if (single)
+ XDrawLine(dpy, win, gc, spacing * (seg->x1 + 1),
+ height - spacing * (seg->y1 + 1),
+ spacing * (seg->x2 + 1),
+ height - spacing * (seg->y2 + 1));
+ }
+ }
+ if (single) {
+ // The following line to be removed
+ XCopyArea(dpy, buffer, win, gc, 0, 0, width, height, 0, 0);
+ XFlush(dpy);
+ }
+}
+
+/*--------------------------------------*/
+/* Graphical display of the layout */
+/*--------------------------------------*/
+
+void draw_layout() {
+
+ int lastlayer, xspc, yspc, hspc;
+ int i, x, y;
+ NET net;
+ NETLIST nlist;
+
+ if (dpy == NULL) return;
+ else if (buffer == (Pixmap)NULL) return;
+
+ XSetForeground(dpy, gc, whitepix);
+ XFillRectangle(dpy, buffer, gc, 0, 0, width, height);
+
+ // Check if a netlist even exists
+ if (Obs[0] == NULL) {
+ XCopyArea(dpy, buffer, win, gc, 0, 0, width, height, 0, 0);
+ return;
+ }
+
+ switch (mapType & MAP_MASK) {
+ case MAP_OBSTRUCT:
+ map_obstruction();
+ break;
+ case MAP_CONGEST:
+ map_congestion();
+ break;
+ case MAP_ESTIMATE:
+ map_estimate();
+ break;
+ }
+
+ // Draw all nets, much like "emit_routes" does when writing
+ // routes to the DEF file.
+
+ if ((mapType & DRAW_MASK) == DRAW_ROUTES) {
+ lastlayer = -1;
+ for (i = 0; i < Numnets; i++) {
+ net = Nlnets[i];
+ draw_net(net, FALSE, &lastlayer);
+ }
+ }
+
+ /* Copy double-buffer onto display window */
+ XCopyArea(dpy, buffer, win, gc, 0, 0, width, height, 0, 0);
+}
+
+/*--------------------------------------*/
+/* GUI initialization */
+/*--------------------------------------*/
+
+int GUI_init(Tcl_Interp *interp)
+{
+ Tk_Window tkwind, tktop;
+ static char *qrouterdrawdefault = ".qrouter";
+ char *qrouterdrawwin;
+ XColor cvcolor, cvexact;
+ int i;
+ float frac;
+
+ tktop = Tk_MainWindow(interp);
+ if (tktop == NULL) {
+ tcl_printf(stderr, "No Top-level Tk window available. . .\n");
+ return;
+ }
+
+ qrouterdrawwin = (char *)Tcl_GetVar(interp, "drawwindow", TCL_GLOBAL_ONLY);
+ if (qrouterdrawwin == NULL)
+ qrouterdrawwin = qrouterdrawdefault;
+
+ tkwind = Tk_NameToWindow(interp, qrouterdrawwin, tktop);
+
+ if (tkwind == NULL) {
+ tcl_printf(stderr, "The Tk window hierarchy must be rooted at"
+ ".qrouter or $drawwindow must point to the drawing window\n");
+ return TCL_ERROR;
+ }
+
+ Tk_MapWindow(tkwind);
+ dpy = Tk_Display(tkwind);
+ win = Tk_WindowId(tkwind);
+ cmap = DefaultColormap (dpy, Tk_ScreenNumber(tkwind));
+
+ load_font(&font_info);
+
+ /* create GC for text and drawing */
+ createGC(win, &gc, font_info);
+
+ /* Initialize colors */
+
+ XAllocNamedColor(dpy, cmap, "blue", &cvcolor, &cvexact);
+ bluepix = cvcolor.pixel;
+ XAllocNamedColor(dpy, cmap, "cyan", &cvcolor, &cvexact);
+ cyanpix = cvcolor.pixel;
+ XAllocNamedColor(dpy, cmap, "green", &cvcolor, &cvexact);
+ greenpix = cvcolor.pixel;
+ XAllocNamedColor(dpy, cmap, "red", &cvcolor, &cvexact);
+ redpix = cvcolor.pixel;
+ XAllocNamedColor(dpy, cmap, "orange", &cvcolor, &cvexact);
+ orangepix = cvcolor.pixel;
+ XAllocNamedColor(dpy, cmap, "gold", &cvcolor, &cvexact);
+ goldpix = cvcolor.pixel;
+ XAllocNamedColor(dpy, cmap, "gray70", &cvcolor, &cvexact);
+ ltgraypix = cvcolor.pixel;
+ XAllocNamedColor(dpy, cmap, "gray50", &cvcolor, &cvexact);
+ graypix = cvcolor.pixel;
+ XAllocNamedColor(dpy, cmap, "yellow", &cvcolor, &cvexact);
+ yellowpix = cvcolor.pixel;
+ XAllocNamedColor(dpy, cmap, "purple", &cvcolor, &cvexact);
+ purplepix = cvcolor.pixel;
+ XAllocNamedColor(dpy, cmap, "magenta", &cvcolor, &cvexact);
+ magentapix = cvcolor.pixel;
+ XAllocNamedColor(dpy, cmap, "GreenYellow", &cvcolor, &cvexact);
+ greenyellowpix = cvcolor.pixel;
+ blackpix = BlackPixel(dpy,DefaultScreen(dpy));
+ whitepix = WhitePixel(dpy,DefaultScreen(dpy));
+
+ cvcolor.flags = DoRed | DoGreen | DoBlue;
+ for (i = 0; i < SHORTSPAN; i++) {
+ frac = (float)i / (float)(SHORTSPAN - 1);
+ /* gamma correction */
+ frac = pow(frac, 0.5);
+
+ cvcolor.green = (int)(53970 * frac);
+ cvcolor.blue = (int)(46260 * frac);
+ cvcolor.red = (int)(35980 * frac);
+ XAllocColor(dpy, cmap, &cvcolor);
+ brownvector[i] = cvcolor.pixel;
+ }
+
+ cvcolor.green = 0;
+ cvcolor.red = 0;
+
+ for (i = 0; i < LONGSPAN; i++) {
+ frac = (float)i / (float)(LONGSPAN - 1);
+ /* gamma correction */
+ frac = pow(frac, 0.5);
+
+ cvcolor.blue = (int)(65535 * frac);
+ XAllocColor(dpy, cmap, &cvcolor);
+ bluevector[i] = cvcolor.pixel;
+ }
+ return TCL_OK; /* proceed to interpreter */
+}
+
+/*----------------------------------------------------------------*/
+
+int QuitCallback(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *objv[])
+{
+ exit(0);
+ return TCL_OK; // Statement not reached
+}
+
+/*----------------------------------------------------------------*/
+
+void load_font(XFontStruct **font_info)
+{
+ char *fontname = "9x15";
+
+ /* Load font and get font information structure. */
+
+ if ((*font_info = XLoadQueryFont (dpy,fontname)) == NULL) {
+ (void) Fprintf (stderr, "Cannot open 9x15 font\n");
+ // exit(1);
+ }
+}
+
+/*----------------------------------------------------------------*/
+
+void createGC(Window win, GC *gc, XFontStruct *font_info)
+{
+ unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
+ XGCValues values;
+ unsigned int line_width = 1;
+ int line_style = LineSolid;
+ int cap_style = CapRound;
+ int join_style = JoinRound;
+
+ /* Create default Graphics Context */
+
+ *gc = XCreateGC(dpy, win, valuemask, &values);
+
+ /* specify font */
+
+ if (font_info != NULL)
+ XSetFont(dpy, *gc, font_info->fid);
+
+ /* specify black foreground since default window background is
+ * white and default foreground is undefined. */
+
+ XSetForeground(dpy, *gc, blackpix);
+
+ /* set line, fill attributes */
+
+ XSetLineAttributes(dpy, *gc, line_width, line_style,
+ cap_style, join_style);
+ XSetFillStyle(dpy, *gc, FillSolid);
+ XSetArcMode(dpy, *gc, ArcPieSlice);
+}
+
+/*----------------------------------------------------------------*/
+
+void expose(Tk_Window tkwind)
+{
+ if (Tk_WindowId(tkwind) == 0) return;
+ if (dpy == NULL) return;
+ draw_layout();
+}
+
+/*----------------------------------------------------------------*/
+
+int redraw(ClientData clientData, Tcl_Interp *interp, int objc,
+ Tcl_Obj *objv[])
+{
+ draw_layout();
+ return TCL_OK;
+}
+
+/*------------------------------------------------------*/
+/* Call to recalculate the spacing if NumChannelsX[0] */
+/* or NumChannelsY[0] changes. */
+/* */
+/* Return 1 if the spacing changed, 0 otherwise. */
+/*------------------------------------------------------*/
+
+int recalc_spacing()
+{
+ int xspc, yspc;
+ int oldspacing = spacing;
+
+ xspc = width / (NumChannelsX[0] + 1);
+ yspc = height / (NumChannelsY[0] + 1);
+ spacing = (xspc < yspc) ? xspc : yspc;
+ if (spacing == 0) spacing = 1;
+
+ return (spacing == oldspacing) ? 0 : 1;
+}
+
+/*----------------------------------------------------------------*/
+
+void resize(Tk_Window tkwind, int locwidth, int locheight)
+{
+ Window window;
+
+ if ((locwidth == 0) || (locheight == 0)) return;
+
+ if (buffer != (Pixmap)0)
+ XFreePixmap (Tk_Display(tkwind), buffer);
+
+ if (Tk_WindowId(tkwind) == 0)
+ Tk_MapWindow(tkwind);
+
+ buffer = XCreatePixmap (Tk_Display(tkwind), Tk_WindowId(tkwind),
+ locwidth, locheight, DefaultDepthOfScreen(Tk_Screen(tkwind)));
+
+ width = locwidth;
+ height = locheight;
+
+ recalc_spacing();
+
+ if (dpy) draw_layout();
+}
+
+/*----------------------------------------------------------------*/