|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T i
Length: 19452 (0x4bfc) Types: TextFile Names: »imagen1-special.c«
└─⟦060c9c824⟧ Bits:30007080 DKUUG TeX 2/12/89 └─⟦this⟧ »./DVIware/laser-setters/umd-dvi/dev/imagen1-special.c«
/* * 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 <stdio.h> #include <ctype.h> #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<THEIGHT; i++) bcopy(btexture[i],stexture[i],TWIDTH); } /* * Push the state of the Imagen and set up a new virtual coord system */ static void push_location() { putchar(imP_Push); putchar(imP_SetHVSystem); putchar(0140); } /* * Create the pushed virtual page, and pop the state of the printer */ static void pop_location() { putchar(imP_Pop); } /* * Set the pen size * Called as \special{pn size} * eg: \special{pn 8} * The size is the number of milli-inches for the diameter of the pen. * This routine converts that value to device-dependent pixels, and makes * sure that the resulting value is within legal bounds. */ static void im_pensize(cp) char *cp; { int size; if (sscanf(cp, " %d ", &size) != 1) return; pensize = yconv(size); if (pensize < 1) pensize = 1; else if (pensize > 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<THEIGHT; i++)/* Do rows */ for (j=0; j<TWIDTH; j++) putchar(stexture[i][j]); } putchar(imP_SetTexture); putchar((family & 0x7e) >> 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 <inchesperdash>} (dashed line) * or \special{dt <inchesperdot>} (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<numdots-1; i++) { a = i * (pixperdash + spacesize) / d; b = a + pixperdash / d; cx0 = ((float) x0) + (a*dx) + 0.5; cy0 = ((float) y0) + (a*dy) + 0.5; cx1 = ((float) x0) + (b*dx) + 0.5; cy1 = ((float) y0) + (b*dy) + 0.5; connect((int) cx0, (int) cy0, (int) cx1, (int) cy1); b += spacesize / d; } cx0 = ((float) x0) + (b*dx) + 0.5; cy0 = ((float) y0) + (b*dy) + 0.5; connect((int) cx0, (int) cy0, x1, y1); } pathlen = 0; pop_location(); } /* * Virtually draw to a given x,y position on the virtual page. * X and Y are expressed in thousandths of an inch, and this * routine converts them to pixels. * * Called as \special{pa <x> <y>} * 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 <xcenter> <ycenter> <xradius> <yradius> * <startangle> <endangle>} * * <xcenter>,<ycenter>,<xradius>,<yradius> are in 1/1000's of an inch. * <startangle> and <endangle> 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 originally), which will cause * the Imagen to draw in a true CCW direction (opposite of normal). */ if (end_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<splinelen; i++) { putword(splinex[i]); putword(spliney[i]); } pathlen = 0; putchar(imP_DrawPath); putchar(BLACK); pop_location(); } static int dist(x1, y1, x2, y2) /* integer distance from x1,y1 to x2,y2 */ { float dx, dy; dx = x2 - x1; dy = y2 - y1; return sqrt(dx*dx + dy*dy) + 0.5; } /* * Whiten the interior of the next figure (path). Command is: * \special{wh} */ static void im_whiten() { whiten_next = TRUE; } /* * Blacken the interior of the next figure (path). Command is: * \special{bk} */ static void im_blacken() { blacken_next = TRUE; } /* * Shade the interior of the next figure (path) with the predefined * texture. Command is: * \special{sh} */ static void im_shade() { shade_next = TRUE; } /* * Define the texture array. Command is: * \special{tx 32bits 32bits ....} */ static void im_texture(pcount,bitpattern) int pcount, bitpattern[32]; { int i,j,k; unsigned long ul_one; #ifdef DEBUG if (sizeof ul_one != TWIDTH) error(1, 0, "pointer/size mismatch"); #endif j = 0; for (k=0; k < THEIGHT/pcount; k++) { for (i=0; i<pcount; i++) { ul_one = htonl((unsigned long) bitpattern[i]); bcopy((char *) &ul_one, stexture[j++], TWIDTH); } } texture_defined = FALSE; } /* * This routine takes the string argument for a tx command and * parses out the separate bitpatterns to call im_texture with. * Written by Tinh Tang */ static void do_texture(t) char *t; { int bitpattern[32]; int pcount = 0; while (isspace (*t)) t++; while (*t) { if (sscanf(t, "%x", &bitpattern[pcount++]) != 1) { error(0, 0, "malformed tx command"); return; } while (*t && !isspace(*t)) t++;/* Skip to space */ while (*t && isspace(*t)) t++;/* Skip to nonspace */ } if (pcount != 4 && pcount != 8 && pcount != 16 && pcount != 32) { error(0, 0, "malformed tx command"); return; } im_texture(pcount, bitpattern); } #define COMLEN 3 /* Length of a tpic command plus one */ DoSpecial(k) i32 k; { char *spstring, *cp, command[COMLEN]; register int len; spstring = malloc((unsigned) (k+1)); if (spstring == NULL) error(2, 0, "Out of memory"); len = 0; while (k--) spstring[len++] = GetByte(stdin); spstring[len] = '\0'; cp = spstring; while (isspace(*cp)) ++cp; len = 0; while (!isspace(*cp) && *cp && len < COMLEN-1) command[len++] = *cp++; command[len] = '\0'; if (ImHH != hh || ImVV != vv) ImSetPosition(hh, vv); if (strcmp(command, "pn") == 0) im_pensize(cp); else if (strcmp(command, "fp") == 0) im_flushpath(); else if (strcmp(command, "da") == 0) dashed_line(cp, 0); else if (strcmp(command, "dt") == 0) dashed_line(cp, 1); else if (strcmp(command, "pa") == 0) im_drawto(cp); else if (strcmp(command, "ar") == 0) im_arc(cp); else if (strcmp(command, "sp") == 0) flush_spline(); else if (strcmp(command, "sh") == 0) im_shade(); else if (strcmp(command, "wh") == 0) im_whiten(); else if (strcmp(command, "bk") == 0) im_blacken(); else if (strcmp(command, "tx") == 0) im_texture(cp); else error(0, 0, "warning: ignoring \\special"); free(spstring); }