/* * Support drawing routines for Chris Torek's DVI->ImPress program. * * Requires v1.7 or later ImPress to handle paths. * Better if v1.9 or later for arc, circle, and ellipse primitives * (define USEGRAPHICS). * * Tim Morgan, UC Irvine, 11/17/85 * * * At the time these routines are called, the position of the Imagen should * have been updated to the upper left corner of the graph (the position * the \special appears at in the dvi file). Then the coordinates in the * graphics commands are in terms of a virtual page with axes oriented the * same as the Imagen normally has: * * 0,0 * +-----------> +x * | * | * | * \ / * +y * * Angles are measured in the conventional way, from +x towards +y. * Unfortunately, that reverses the meaning of "counterclockwise" from * what you see in the output. * * Unfortunately, some 8/300's don't have an aspect ratio which is 1:1. * One of ours appears to be 295 dpi in the horizontal direction and 300dpi * vertically. If ASPECT is defined, then dviimp/imagen1 and draw_imp/special * use two different variables for the horizontal and vertical resolution, and * otherwise, just one. Because the drawing routines which are defined * in ImPress for circles, arcs, and ellipses in V1.9 and later assume that * the output device is 1:1, they can't be used if ASPECT is defined, and * because I don't want to hack up imagen1 to understand different horizontal * and vertical resolutions, we're currently ignoring this problem, and not * defining ASPECT. */ #define USEGRAPHICS /* Only if v1.9 or later imPRESS */ #undef ASPECT #ifdef ASPECT /* Can't have both! */ #undef USEGRAPHICS #endif #include #include #include "types.h" #include "imPcodes.h" /* Put a two-byte (word) value to the Imagen */ #define putword(w) (putchar((w) >> 8), putchar(w)) extern char *malloc(); #define TRUE 1 #define FALSE 0 #define TWOPI (3.14157926536*2.0) #define MAXPOINTS 300 /* Most number of points in a path */ #define SPLINEPOINTS 900 /* Most points in a spline */ #define RADTOPXL 2607.435436 /* (16383 / (2pi)). This converts from radians to the angle units used by ImPress */ /* Convert radian angles to 2**-14 angle units used by ImPress */ #define RadToImpUnits(a) ((short) ((a)*RADTOPXL + 0.5)) /* Graphics operations */ #define WHITE 0 #define SHADE 3 #define OR 7 #define BLACK 15 extern double cos(), sin(), sqrt(); #ifndef ASPECT extern int DPI; /* Resolution of device */ #define PixPerInX DPI #define PixPerInY DPI #else extern int DPIx, DPIy; /* x,y resolution of device */ #define PixPerInX DPIx #define PixPerInY DPIy #endif extern int UserMag; #define conv(x, f)\ ((int) ((((double)(x)/1000.0) * ((double)(f)) * ((double)UserMag/1000.0)) + 0.5)) #define xconv(x) conv(x, PixPerInX) #define yconv(y) conv(y, PixPerInY) extern int ImHH; /* Imagen horizontal position */ extern int ImVV; /* Imagen vertical position */ extern int hh; /* current horizontal position, in DEVs */ extern int vv; /* current vertical position, in DEVs */ extern int NextFamilyNumber; /* Number of next ImPress family to use */ #define fnum NextFamilyNumber static int xx[MAXPOINTS], yy[MAXPOINTS], pathlen, pensize = 2; /* Size we want Imagen to draw at, default 2 pixels */ #define MAXPENSIZE 20 /* Imagen restriction */ static int family_defined = FALSE, /* Have we chosen family yet? */ texture_defined = FALSE,/* Have we done a set_texture yet? */ whiten_next = FALSE, /* Should next object be whitened? */ blacken_next = FALSE, /* Should next object be blackened? */ shade_next = FALSE; /* Should next object be shaded? */ /* Predefined shading (texture) glyph */ /* First, define size of glyph */ #define THEIGHT 32 /* bits high */ #define TWIDTH 4 /* bytes wide */ /* Next, declare the bit map for the glyph */ static char stexture[THEIGHT][TWIDTH]={ {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}}; /* * Copy a default texture into the stexture array */ static void glyph_init() { static char btexture[THEIGHT][TWIDTH]={ {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0xcc, 0xcc, 0xcc, 0xcc}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00}}; int i; for (i=0; i MAXPENSIZE) pensize = MAXPENSIZE; } /* * Make sure the pen size is set. Since we push and pop the state, * this has to be sent for each different object (I think). */ static void set_pen_size() { putchar(imP_SetPen); putchar(pensize); } /* * Actually apply the attributes (shade, whiten, or blacken) to the currently * defined path/figure. */ static void do_attributes() { static int family; /* Family of downloaded texture glyph */ static int member; /* Member of family */ int i,j; /* Loop through glyph array */ if (shade_next) { shade_next = FALSE; if (!family_defined) { family_defined = TRUE; family = fnum++; member = -1; } if (!texture_defined) { texture_defined = TRUE; member++; putchar(imP_DefGlyph); putchar((family & 0x7e) >> 1); putchar((family & 0x01) << 7 | (member & 0x7f)); /*putword(0); */ /* Advance width */ putword(32); putword(TWIDTH*8); /* pixel width (8 x number of bytes) */ /*putword(0); */ /* left offset */ putword(32); putword(THEIGHT); /* and height of glyph */ /*putword(0); */ /* top offset */ putword(32); for (i=0; i> 1); putchar((family & 0x01) << 7 | (member & 0x7f)); putchar(imP_FillPath); putchar(SHADE); glyph_init(); /* reinitialize the array */ } else if (whiten_next) { whiten_next = FALSE; putchar(imP_FillPath); putchar(WHITE); } else if (blacken_next) { blacken_next = FALSE; putchar(imP_FillPath); putchar(BLACK); } } /* * Flush the path that we've built up with im_drawto() * Called as \special{fp} */ static void im_flushpath() { register int i; push_location(); if (pathlen <= 0) return; set_pen_size(); putchar(imP_CreatePath); putword(pathlen); for (i=1; i<=pathlen; i++) { putword(xx[i]); putword(yy[i]); } pathlen = 0; putchar(imP_DrawPath); putchar(BLACK); do_attributes(); pop_location(); } /* Helper routine for dashed_line() */ static void connect(x0, y0, x1, y1) int x0, y0, x1, y1; { set_pen_size(); putchar(imP_CreatePath); putword(2); /* Path length */ putword(x0); putword(y0);/* The path */ putword(x1); putword(y1); putchar(imP_DrawPath); putchar(BLACK); } /* Another helper. Draw a dot at the indicated point */ static void dot_at(x, y) int x,y; { set_pen_size(); putchar(imP_CreatePath); putword(1); /* Path length */ putword(x); putword(y); /* The path */ putchar(imP_DrawPath); putchar(BLACK); } /* * Draw a dashed or dotted line between the first pair of points in the array * Called as \special{da } (dashed line) * or \special{dt } (dotted line) * eg: \special{da 0.05} */ static void dashed_line(cp, dotted) char *cp; int dotted; /* boolean */ { int i, numdots, x0, y0, x1, y1; double cx0, cy0, cx1, cy1; double d, spacesize, a, b, dx, dy, pixperdash; float inchesperdash; if (sscanf(cp, " %f ", &inchesperdash) != 1) return; if (pathlen <= 1) return; pixperdash = inchesperdash * ((float) PixPerInY); x0 = xx[1]; x1 = xx[2]; y0 = yy[1]; y1 = yy[2]; dx = x1 - x0; dy = y1 - y0; push_location(); if (dotted) { numdots = sqrt(dx*dx + dy*dy) / pixperdash + 0.5; if (numdots > 0) for (i = 0; i <= numdots; i++) { a = (float) i / (float) numdots; cx0 = ((float) x0) + (a*dx) + 0.5; cy0 = ((float) y0) + (a*dy) + 0.5; dot_at((int) cx0, (int) cy0); } } else { d = sqrt(dx*dx + dy*dy); if (d <= 2 * pixperdash) { connect(x0, y0, x1, y1); pathlen = 0; pop_location(); return; } numdots = d / (2 * pixperdash) + 1; spacesize = (d - numdots * pixperdash) / (numdots - 1); for (i=0; i } * eg: \special{pa 0 1200} */ static void im_drawto(cp) char *cp; { int x,y; if (sscanf(cp, " %d %d ", &x, &y) != 2) return; if (++pathlen >= MAXPOINTS) error(1, 0, "Too many points specified"); xx[pathlen] = xconv(x); yy[pathlen] = yconv(y); } #ifndef USEGRAPHICS /* * Helper routine for im_arc(). * Convert x and y to integers, then call im_drawto() normally. */ static void im_fdraw(x, y) float x, y; { int ix,iy; ix = (int) x + 0.5; iy = (int) y + 0.5; im_drawto(ix, iy); } /* * Draw the indicated arc on the virtual page and flush it. * The arc is always drawn counter clockwise from start_angle to end_angle on * the virtual page. That is, clockwise in the real world (since +y is down). * It is assumed that start_angle has been adjusted to be in the range * 0.0 <= start_angle < 2*PI * and that end_angle is the smallest suitable angle >= start_angle. Thus * end_angle MAY be >= 2*PI. * * Called as \special{ar * } * * ,,, are in 1/1000's of an inch. * and are in radians. * * eg: \special{ar 240 240 30 30 0.000 6.283} */ static void im_arc(cp) char *cp; { int xc, yc, xrad, yrad; float start_angle, end_angle; double angle, theta, r, xcenter, ycenter, xradius, yradius; int n; if (sscanf(cp, " %d %d %d %d %f %f ", &xc, &yc, &xrad, &yrad, &start_angle, &end_angle) != 6) return; xcenter = xc; /* Convert to floating point */ ycenter = yc; xradius = xrad; yradius = yrad; r = (xradius+yradius)/2.0; theta = sqrt(1.0 / r); n = TWOPI / theta + 0.5; if (n<6) n = 6; if (n>80) n = 80; theta = TWOPI / n; im_fdraw( xcenter + xradius*cos(start_angle), ycenter + yradius*sin(start_angle) ); angle = start_angle + theta; while (angle < end_angle) { im_fdraw(xcenter + xradius*cos(angle), ycenter + yradius*sin(angle) ); angle += theta; } im_fdraw( xcenter + xradius*cos(end_angle), ycenter + yradius*sin(end_angle) ); im_flushpath(); } #else USEGRAPHICS /* Same routine as above, but it uses the special graphics primitives */ static void im_arc(cp) char *cp; { int xc, yc, xrad, yrad; float start_angle, end_angle; short alpha0, alpha1; if (sscanf(cp, " %d %d %d %d %f %f ", &xc, &yc, &xrad, &yrad, &start_angle, &end_angle) != 6) return; push_location(); set_pen_size(); putchar(imP_SetHAbs); putword(xconv(xc)); putchar(imP_SetVAbs); putword(yconv(yc)); /* * If end_angle > TWOPI, we can't simply use it, since it will be > 16383, * and thus an illegal angle. Simply subtracting TWOPI will make it < * start_angle, which will reverse the direction the arc is drawn, resulting * in the wrong arc. So we also exchange start_angle and end_angle. But then * start_angle < end_angle, so the arc goes back to its original direction! * So we also subtract TWOPI from the original start_angle, foring end_angle * to be negative (since 0<=start_angle TWOPI) { double temp; temp = end_angle - TWOPI; end_angle = start_angle - TWOPI; start_angle = temp; } if (xrad >= yrad-1 && xrad <= yrad+1) { /* Circle or arc */ alpha0 = RadToImpUnits(start_angle); alpha1 = RadToImpUnits(end_angle); putchar(imP_CircleArc); putword(xconv((double) xrad)); putword(alpha0); putword(alpha1); } else { /* Ellipse */ putchar(imP_EllipseArc); putword(xconv((double) xrad)); putword(yconv((double) yrad)); putword(0); /* alphaoff */ putword(0); /* zero start angle */ putword(16383); /* two pi end angle */ } putchar(imP_DrawPath); putchar(BLACK); do_attributes(); pop_location(); } #endif USEGRAPHICS /* * Create a spline through the points in the array. * Called like flush path (fp) command, after points * have been defined via pa command(s). * * eg: \special{sp} */ static void flush_spline() { int xp, yp, N; float t1, t2, t3, w; int i, j, steps; int splinex[SPLINEPOINTS], spliney[SPLINEPOINTS], splinelen; push_location(); set_pen_size(); putchar(imP_CreatePath); splinelen = 0; N = pathlen + 1; xx[0] = xx[1]; yy[0] = yy[1]; xx[N] = xx[N-1]; yy[N] = yy[N-1]; for (i = 0; i < N-1; i++) { /* interval */ steps = (dist(xx[i],yy[i], xx[i+1],yy[i+1]) + dist(xx[i+1],yy[i+1], xx[i+2],yy[i+2])) / 20; for (j = 0; j < steps; j++) { /* points within */ w = ((float) j) / ((float) steps); t1 = 0.5 * w * w; w -= 0.5; t2 = 0.75 - w * w; w -= 0.5; t3 = 0.5 * w * w; xp = t1 * xx[i+2] + t2 * xx[i+1] + t3 * xx[i] + 0.5; yp = t1 * yy[i+2] + t2 * yy[i+1] + t3 * yy[i] + 0.5; if (splinelen >= SPLINEPOINTS) error(1, 0, "Too many points in spline"); splinex[splinelen] = xp; spliney[splinelen++] = yp; } } putword(splinelen); for (i=0; i