|
|
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 d
Length: 9993 (0x2709)
Types: TextFile
Names: »draw_sun.c«
└─⟦52210d11f⟧ Bits:30007239 EUUGD2: TeX 3 1992-12
└─⟦af5ba6c8e⟧ »unix3.0/DVIWARE.tar.Z«
└─⟦ca79c7339⟧
└─⟦this⟧ »DVIware/crt-viewers/sunview/dvisun/draw_sun.c«
/*
* Support drawing routines for dvisun
*
* Tim Morgan, UC Irvine
*
* At the time these routines are called, the values of hh and vv 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 and the SUN normally have:
*
* 0,0
* +-----------> +x
* |
* |
* |
* \ /
* +y
*
* Angles are measured in the conventional way, from +x towards +y.
* Unfortunately, that reverses the meaning of "counterclockwise"
* from what it's normally thought of.
*
* A lot of floating point arithmetic has been converted to integer
* arithmetic for speed. In some places, this is kind-of kludgy, but
* it's worth it.
*/
#include <suntool/tool_hs.h>
#include <math.h>
#define MAXPOINTS 300 /* Max points in a path */
#define TWOPI (3.14159265359*2.0)
#define MAX_PEN_SIZE 7 /* Max pixels of pen width */
/* Unfortunately, these values also appear in dvisun.c */
#define xRESOLUTION 118 /* Dots per inch in x-direction */
#define yRESOLUTION 110 /* and in the y-direction */
#undef USEGLOBALMAG
#ifdef USEGLOBALMAG
extern int mag; /* Magnification from preamble */
extern float ActualFactor(); /* Converts magnification to a fraction */
#endif USEGLOBALMAG
extern void Warning(), Fatal(); /* Error-handling routines */
extern void sun_draw_ellipse(); /* Fast ellipse-drawing routine */
extern int hh, vv; /* Current x,y position on screen */
extern int xscreen, yscreen; /* x,y compensation for page on screen */
extern int draw_mode; /* Mode to draw with */
extern struct pixrect *display; /* Pixrect we're going to draw on */
static int xx[MAXPOINTS], yy[MAXPOINTS]; /* Path in milli-inches */
static int path_len = 0; /* # points in current path */
int pen_size = 1; /* Pixel width of lines drawn */
static bool whiten = FALSE, shade = FALSE; /* Attributes for last object */
/*
* These two routines scale from milli-inches to pixel coords, but do
* not apply the necessary offsets for absolute display positioning.
*/
#ifdef USEGLOBALMAG
#define xsc(x) (((x)*xRESOLUTION*ActualFactor(mag) + 500) / 1000)
#define ysc(y) (((y)*yRESOLUTION*ActualFactor(mag) + 500) / 1000)
#else
#define xsc(x) (((x)*xRESOLUTION + 500) / 1000)
#define ysc(y) (((y)*yRESOLUTION + 500) / 1000)
#endif
/*
* These two macros scale from milli-inches to pixel coords, and add
* the necessary offsets for absolute screen positions.
*/
#define xconv(x) (xsc(x) + hh - xscreen)
#define yconv(y) (ysc(y) + vv - yscreen)
/*
* Set the size of the virtual pen used to draw in milli-inches
*/
/* ARGSUSED */
void set_pen_size(cp)
char *cp;
{
int ps;
if (sscanf(cp, " %d ", &ps) != 1) {
Warning("Illegal .ps command format: %s", cp);
return;
}
#ifdef USEGLOBALMAG
pen_size = (ps*(xRESOLUTION+yRESOLUTION)*ActualFactor(mag) + 1000) / 2000;
#else
pen_size = (ps*(xRESOLUTION+yRESOLUTION) + 1000) / 2000;
#endif USEGLOBALMAG
if (pen_size < 1) pen_size = 1;
else if (pen_size > MAX_PEN_SIZE) pen_size = MAX_PEN_SIZE;
}
/*
* Apply the requested attributes to the last path (box) drawn.
* Attributes are reset.
*/
static void do_attribute_path(last_min_x, last_max_x, last_min_y, last_max_y)
int last_min_x, last_max_x, last_min_y, last_max_y;
{
static struct pixrect *shade_pr;
if (last_min_x < last_max_x && last_min_y < last_max_y) {
if (whiten) {
pr_rop(display, last_min_x, last_min_y, last_max_x-last_min_x,
last_max_y-last_min_y, PIX_SRC, (struct pixrect *) 0, 0, 0);
}
else if (shade) {
if (!shade_pr) {
shade_pr = mem_create(3, 3, 1);
if (!shade_pr) Fatal("Out of memory -- cannot create pixrect");
pr_put(shade_pr, 0, 0, 1);
}
pr_replrop(display, last_min_x, last_min_y, last_max_x-last_min_x,
last_max_y-last_min_y, (PIX_SRC|PIX_DST), shade_pr, 0, 0);
}
}
shade = whiten = FALSE;
}
/* Put a dot at the indicated position. Args in v-p milli-inches */
#define dot_at(x, y) pr_put(display, xconv(x), yconv(y), 1)
/* Draw a line on the screen. Arguments are in virtual-page milli-inches */
static void line_btw(x0, y0, x1, y1)
int x0, y0, x1, y1;
{
int i, xstart, xend, ystart, yend;
xstart = xconv(x0); ystart = yconv(y0);
xend = xconv(x1); yend = yconv(y1);
for (i=0; i<pen_size; i++) {
pr_vector(display, xstart, ystart, xend, yend, draw_mode, 1);
if (abs(yend-ystart) > abs(xend-xstart)) {
++xstart;
++xend;
}
else {
++ystart;
++yend;
}
}
}
/*
* Print the line defined by previous path commands
*/
void flush_path()
{
register int i;
int last_min_x, last_max_x, last_min_y, last_max_y;
last_min_x = 30000; last_min_y = 30000;
last_max_x = -30000; last_max_y = -30000;
for (i=1; i<path_len; i++) {
if (xx[i] > last_max_x) last_max_x = xx[i];
if (xx[i] < last_min_x) last_min_x = xx[i];
if (yy[i] > last_max_y) last_max_y = yy[i];
if (yy[i] < last_min_y) last_min_y = yy[i];
line_btw(xx[i], yy[i], xx[i+1], yy[i+1]);
}
if (xx[path_len] > last_max_x) last_max_x = xx[path_len];
if (xx[path_len] < last_min_x) last_min_x = xx[path_len];
if (yy[path_len] > last_max_y) last_max_y = yy[path_len];
if (yy[path_len] < last_min_y) last_min_y = yy[path_len];
path_len = 0;
do_attribute_path(xconv(last_min_x), xconv(last_max_x),
yconv(last_min_y), yconv(last_max_y));
}
/*
* Print a dashed line along the previously defined path, with
* the dashes/inch defined.
*/
void flush_dashed(cp, dotted)
char *cp;
int dotted;
{
int i, numdots, x0, y0, x1, y1;
int cx0, cy0, cx1, cy1;
float inchesperdash;
double d, spacesize, a, b, dx, dy, milliperdash;
if (sscanf(cp, " %f ", &inchesperdash) != 1) {
Warning("Illegal format for dotted/dashed line: %s", cp);
return;
}
if (path_len <= 1 || inchesperdash <= 0.0) {
Warning("Illegal conditions for dotted/dashed line");
return;
}
milliperdash = inchesperdash * 1000.0;
x0 = xx[1]; y0 = yy[1];
x1 = xx[2]; y1 = yy[2];
dx = x1 - x0;
dy = y1 - y0;
if (dotted) {
numdots = sqrt(dx*dx + dy*dy) / milliperdash + 0.5;
for (i=0; i <= numdots; i++) {
a = (float) i / (float) numdots;
cx0 = x0 + a*dx + 0.5;
cy0 = y0 + a*dy + 0.5;
dot_at(cx0, cy0);
}
}
else {
d = sqrt(dx*dx + dy*dy);
if (d <= 2.0*milliperdash)
line_btw(x0, y0, x1, y1);
else {
numdots = d / (2.0*milliperdash) + 1.0;
spacesize = (d - numdots * milliperdash) / (numdots - 1);
for (i=0; i<numdots-1; i++) {
a = i * (milliperdash + spacesize) / d;
b = a + milliperdash / d;
cx0 = x0 + a*dx + 0.5;
cy0 = y0 + a*dy + 0.5;
cx1 = x0 + b*dx + 0.5;
cy1 = y0 + b*dy + 0.5;
line_btw(cx0, cy0, cx1, cy1);
b += spacesize / d;
}
cx0 = x0 + b*dx + 0.5;
cy0 = y0 + b*dy + 0.5;
line_btw(cx0, cy0, x1, y1);
}
}
path_len = 0;
}
/*
* Add a point to the current path
*/
void add_path(cp)
char *cp;
{
int pathx, pathy;
if (++path_len >= MAXPOINTS) Fatal("Too many points");
if (sscanf(cp, " %d %d ", &pathx, &pathy) != 2)
Fatal("Malformed path command");
xx[path_len] = pathx;
yy[path_len] = pathy;
}
/*
* Draw to a floating point position
*/
static void im_fdraw(x, y)
float x,y;
{
if (++path_len >= MAXPOINTS) Fatal("Too many arc points");
xx[path_len] = x + 0.5;
yy[path_len] = y + 0.5;
}
/*
* Draw an arc
*/
void arc(cp)
char *cp;
{
int xc, yc, xrad, yrad, n;
float start_angle, end_angle, angle, theta, r;
double xradius, yradius, xcenter, ycenter;
if (sscanf(cp, " %d %d %d %d %f %f ", &xc, &yc, &xrad, &yrad, &start_angle,
&end_angle) != 6) {
Warning("Illegal arc specification: %s", cp);
return;
}
/* We have a specialized fast way to draw closed circles/ellipses */
if (start_angle <= 0.0 && end_angle >= 6.282) {
sun_draw_ellipse(xconv(xc), yconv(yc), xsc(xrad), ysc(yrad));
return;
}
xcenter = xc;
ycenter = yc;
xradius = xrad;
yradius = yrad;
r = (xradius + yradius) / 2.0;
theta = sqrt(1.0 / r);
n = 0.3 * TWOPI / theta + 0.5;
if (n < 6) n = 6;
else if (n > 80) n = 80;
theta = TWOPI / n;
flush_path();
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) );
flush_path();
}
/*
* APPROXIMATE integer distance between two points
*/
#define dist(x0, y0, x1, y1) (abs(x0-x1)+abs(y0-y1))
/*
* Draw a spline along the previously defined path
*/
void flush_spline()
{
int xp, yp, N, lastx=(-1), lasty;
int t1, t2, t3, steps, j;
register int i, w;
N = path_len + 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])) / 80;
for (j=0; j<steps; j++) { /* points within */
w = (j*1000 + 500) / steps;
t1 = w * w / 20;
w -= 500;
t2 = (750000 - w * w) / 10;
w -= 500;
t3 = w * w / 20;
xp = (t1*xx[i+2] + t2*xx[i+1] + t3*xx[i] + 50000) / 100000;
yp = (t1*yy[i+2] + t2*yy[i+1] + t3*yy[i] + 50000) / 100000;
if (lastx > -1) line_btw(lastx, lasty, xp, yp);
lastx = xp;
lasty = yp;
}
}
path_len = 0;
}
/*
* Shade the last box, circle, or ellipse
*/
void shade_last()
{
whiten = FALSE;
shade = TRUE;
}
/*
* Make the last box, circle, or ellipse, white inside (shade with white)
*/
void whiten_last()
{
whiten = TRUE;
shade = FALSE;
}