|
|
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);
}