DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T t

⟦768743734⟧ TextFile

    Length: 15060 (0x3ad4)
    Types: TextFile
    Names: »tpic_support.c«

Derivation

└─⟦52210d11f⟧ Bits:30007239 EUUGD2: TeX 3 1992-12
    └─⟦af5ba6c8e⟧ »unix3.0/DVIWARE.tar.Z« 
        └─⟦ca79c7339⟧ 
            └─⟦this⟧ »DVIware/laser-setters/dvi-to-ps/TeXPS/dvitps/src/tpic_support.c« 

TextFile

/* Copyright 1988 Stephan v. Bechtolsheim */

/* This file is part of the TeXPS Software Package.

The TeXPS Software Package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.  No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing.  Refer to the TeXPS Software Package
General Public License for full details.

Everyone is granted permission to copy, modify and redistribute
the TeXPS Software Package, but only under the conditions described in the
TeXPS Software Package General Public License.   A copy of this license is
supposed to have been given to you along with TeXPS Software Package so you
can know your rights and responsibilities.  It should be in a
file named CopyrightLong.  Among other things, the copyright notice
and this notice must be preserved on all copies.  */


/*
 * Tpic support routines
 *
 *      Tim Morgan, UC Irvine, morgan@ics.uci.edu
 *	Fletcher Mattox, fletcher@sally.utexas.edu, (adapted to dvitps)
 *      Stephan Bechtolsheim made some changes (more to conform to
 *      the conventions in this driver than anything else).
 * \special{pn pensize}			Tpic_set_pen_size(pensize)
 * \special{tx count pattern}		not implemented
 * \special{pa x y}			Tpic_add_path(x, y)
 * \special{fp}				Tpic_flush_path_fp()
 * \special{ip}				Tpic_flush_path_ip()
 * \special{dt length}			Tpic_flush_dashed(length, 1)
 * \special{da length}			Tpic_flush_dashed(length, 0)
 * \special{ar x y xrad yrad start end} arc(x, y, xrad, yrad, start, end)
 * \special{sh}				Tpic_shade_last(grey)
 * \special{wh}				Tpic_shade_last(white)
 * \special{bl}				Tpic_shade_last(black)
 *
 * At the time these routines are called, the values of hhh and vvv 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:
 *
 *                      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.
 */

#include <stdio.h>
#include <math.h>
#include "defs.h"
#include "dvitps.h"
#include "emit.h"
#include "special-tokenlex.h"

/* External declarations. */
extern int IntFromLex();
extern int LexInt;
extern double LexDouble;
extern double DoubleFromLex();
extern double DoubleOrIntFromLex();
extern int PssColumn;
extern void PssSendInteger();
extern void PssSendCommand();

extern int Verbose;
extern int DriverMag; /* Magnification used for this dvi file */
extern int Resolution;/* Pixels per Inch */
extern int hhh, vvv;	/* device coordinates in pixels */

/* Forward declarations. */
void TpicOutputLinetoPath();
void TpicSaveShadingColor();

#define	MAXPOINTS	300	/* Max points in a path. */

static int xx[MAXPOINTS], yy[MAXPOINTS]; /* Path in milli-inches,
					    absolute coordinates. */
static int TpicPathLength = 0;	/* # points in current path */
int pen_size = 1;		/* Pixel width of lines drawn */


/*
 * These two macros scale from milli-inches to pixel coords, but do
 * not apply the necessary offsets for absolute display positioning.
 * Because of overflow problems has to be done with "double".
 */
#define	xsc(x)	(int) (((x)*(double)Resolution*(double)DriverMag/1000.0\
			+ 500.0) / 1000)
#define	ysc(y)	(int) (((y)*(double)Resolution*(double)DriverMag/1000.0\
			+ 500.0) / 1000)

/*
 *  These two macros scale from milli-inches to pixel coords, and add
 *  the necessary offsets for absolute device positions.
 */
#define	XCONV(x)	(xsc(x) + hhh)
#define	YCONV(y)	(ysc(y) + vvv)

/* The following variable, if set to TRUE, means the next
   object is shaded, and if set to FALSE, it means it's "stroked",
   that is a line is drawn. After shading, it's always reset to
   stroke. */
int ShadeNextObject = FALSE;

/* Color values in general:
   Tpic's language defines color such that 0 is white, 1 is black and that's
   the way colors are stored in the following variable.
   Postscript needs 1 white and 0 black, just the reverse. It's reversed when
   it's sent out. */
double ColorOfNextShade;

/*
 * TpicShadeCommandGeneral
 * ***********************
 * This procedure deals with the "sh" command. This command can be
 * one of the following:
 * sh s (where s indicated the color, 0 ... 1
 * sh   (default to gray in this case)
 */
void
TpicShadeCommandGeneral()
{
  int tok;

  tok = yylex();
  switch (tok) {
    case 0:
      TpicSaveShadingColor(0.7);
      break;
    case T_INT:
      TpicSaveShadingColor((double)LexInt);
      break;
    case T_DOUBLE:
      TpicSaveShadingColor(LexDouble);
      break;
    default:
      Fatal2 ("TpicShadeCommandGeneral(): default, code = %d\n", tok);
    }
}

TpicShadeCommandWhite() {TpicSaveShadingColor(0.0);}
TpicShadeCommandBlack() {TpicSaveShadingColor(1.0);}

/*
 * TpicSaveShadingColor
 * ********************
 * Expects a `color' between 0 (white) and 1 (black).
 * It saves that color.
 *
 * color: the new incoming color.
 */
void
TpicSaveShadingColor(color)
     double color;
{
  if (color < 0.0 || color > 1.0) {
    Warning2 ("Tpic_shade_last(): Illegal shade %f", color);
    return;
  }
  ShadeNextObject = TRUE;
  ColorOfNextShade = color;

#ifdef DEBUG
  fprintf (stderr, "TpicSaveShadingColor(): gray value = %4.2lf\n", color);
#endif
}

/*
 * TpicShadeOrStroke
 * *****************
 * This is where the shading and stroking actually happens.
 * See comment in the beginning for an explanation about shading
 * colors here and in PostScript.
 */
void
TpicShadeOrStroke()
{
  char buffer[256];

  if (ShadeNextObject) {
    PssSendCommand ("currentgray"); /* Save current color. */
    sprintf (buffer, "%4.2lf ", 1.0 - ColorOfNextShade); /* Send new color. */
    PssSendCommand (buffer);
    PssSendCommand ("setgray"); /* Set new color. */
    PssSendCommand("fill"); /* fill the area. */
    /* The following setgray matches the currentgray from
       the preceding procedure. */
    PssSendCommand("setgray"); /* Restore old color. */
    ShadeNextObject = FALSE; /* No more shading. */
  } else {
    PssSendCommand("stroke");
  }
}

/*
 * TpicSaveCurrentPoint
 * ********************
 *
 *  Tpic \specials must be careful not to forget what the current state
 *  is, so that it can restore it later on.  I think `current state'
 *  == `current point'.  The driver expects an rmoveto to work after
 *  a \special, so the current point must be saved (at least).
 */
void
TpicSaveCurrentPoint()
{
  PssSendCommand("currentpoint");
}

/*
 *  Pop the currentpoint off the stack.  Note that TpicSaveCurrentPoint()
 *  and TpicRestoreCurrentPoint() must come in pairs, or we are in big trouble.
 */
void
TpicRestoreCurrentPoint()
{
  PssSendCommand("moveto");
}

/*
 *  Set the size of the virtual pen used to draw in milli-inches
 */
void 
Tpic_set_pen_size()
{
  int ps;
  
  ps = IntFromLex();
  pen_size = xsc(ps);
  PssSendInteger (pen_size);
  PssSendCommand ("setlinewidth");
}

/*
 * Tpic_flush_path_ip
 * ******************
 * The "ip", if shading was turned on before, does shading.
 * If shading was not turned on, it does nothing, but does clear the path.
 */
void 
Tpic_flush_path_ip()
{
  if (ShadeNextObject) {
    TpicSaveCurrentPoint();
    TpicOutputLinetoPath();
    TpicShadeOrStroke();
    TpicRestoreCurrentPoint();
  } else {
    TpicPathLength = 0;
  }
}

/*
 * Tpic_flush_path_fp
 * ******************
 * The "fp" flush path definition was originally to
 * simply shade the figure if shading was in effect, or stroke
 * it otherwise (= default).
 * New function is as follows:
 *
 * 1. always draw a line
 * 2. If shading in effect also shade.
 */
void 
Tpic_flush_path_fp()
{
  register int i;

#ifdef ABC
  for (;;) {
    printf ("Give x: ");
    scanf ("%d", &k);
    printf (":%d:\n", XCONV(k));
  }
#endif

#ifdef TESTING
  EMIT_NEW_LINE;
#endif

  /* If Shading is in effect, do it now (that is first). Color is not
     deal with here but in TpicShadeOrStroke(). */
  if (ShadeNextObject) {
    TpicSaveCurrentPoint();
    TpicOutputLinetoPath();
    TpicShadeOrStroke();
    TpicRestoreCurrentPoint();
  }

  /* Now comes the stroking part. */
  if (ShadeNextObject)
    Fatal ("Tpic_flush_path_fp(): shading still on.");
  TpicSaveCurrentPoint();
  TpicOutputLinetoPath();
  TpicShadeOrStroke();
  TpicRestoreCurrentPoint();

#ifdef TESTING
  EMIT_NEW_LINE;
#endif
  TpicPathLength = 0;
}

/*
 * TpicOutputLinetoPath
 * ********************
 */
void
TpicOutputLinetoPath()
{
  int i;

  PssSendCommand("newpath");
  PssSendInteger (XCONV(xx[1]));
  PssSendInteger (YCONV(yy[1]));
  PssSendCommand ("moveto");

  for (i=1; i<TpicPathLength; i++)  {
#ifdef TESTING
    EMIT_NEW_LINE;
#endif
    PssSendInteger(XCONV(xx[i+1]));
    PssSendInteger(YCONV(yy[i+1]));
    PssSendCommand("lineto");
  }
}

/*
 *  Draw a dot -- the call to Tpic_dot_at() must be protected
 *  by a TpicSaveCurrentPoint().
 */
Tpic_dot_at(x, y)
     int x,y;
{
  int radius = 1;		/* in pixels */

  PssSendCommand ("newpath");
  PssSendInteger(XCONV(x));
  PssSendInteger(YCONV(y));
  PssSendInteger(radius);
  PssSendInteger(0);
  PssSendInteger(360);
  PssSendCommand("arc");
  PssSendCommand("fill");
}

/*
 * Print a dashed line along the previously defined path, with
 * the dashes/inch defined.
 */
void 
Tpic_flush_dashed(dotted)
     int dotted;
{
  int i, numdots, x0, y0, x1, y1;
  int cx0, cy0;
  double inchesperdash;
  double d, spacesize, a, dx, dy, milliperdash;

  inchesperdash = DoubleOrIntFromLex();
  if (TpicPathLength <= 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;
    TpicSaveCurrentPoint();
    for (i=0; i <= numdots; i++) {
      a = (double) i / (double) numdots;
      cx0 = x0 + a*dx + 0.5;
      cy0 = y0 + a*dy + 0.5;
      Tpic_dot_at(cx0, cy0);
    }
    TpicRestoreCurrentPoint();
  }
  else {
    d = sqrt(dx*dx + dy*dy);
    numdots = d / (2.0*milliperdash) + 1.0;
    if (numdots == 1)
      spacesize = 0;
    else
      spacesize = (d - numdots * milliperdash) / (numdots - 1);
    PssSendCommand ("[");
    PssSendInteger (xsc(milliperdash));
    PssSendInteger (xsc(spacesize));
    PssSendCommand ("] 0 setdash");
    Tpic_flush_path_fp(); /* OOOPS ?!?!? */
    PssSendCommand("[] 0 setdash");
  }
  TpicPathLength = 0;
}

/*
 *  Add a point to the current path
 */
void 
Tpic_add_path()
{
  if (++TpicPathLength >= MAXPOINTS)
    Fatal("Tpic_add_path(): Too many points.");
  xx[TpicPathLength] = IntFromLex();
  yy[TpicPathLength] = IntFromLex();
}

/*
 * TpicArcBusiness
 * ***************
 * Draw an arc. This function is called by either K_ar or K_ia.
 *
 * control: TRUE: original command is K_ar
 *          FALSE: original command is K_ia
 */
void 
TpicArcBusiness(control)
     int control;
{
  int xc, yc, xrad, yrad;
  double start_angle, end_angle;
  char buffer[256];
  int closed_arc = FALSE; /* closed arc, that is circle or ellipsis? */
  int draw_arc_path; /* Draw the arc itself? */

  /* Read in all the arguments. */
  xc = IntFromLex();
  yc = IntFromLex();
  xrad = IntFromLex();
  yrad = IntFromLex();
  start_angle = DoubleOrIntFromLex();
  end_angle =   DoubleOrIntFromLex();

  /* Normalization of angles. */
  while (start_angle < 0.0)  start_angle += TWOPI;
  while (end_angle < 0.0)  end_angle += TWOPI;
  while (start_angle > TWOPI)  start_angle -= TWOPI;
  while (end_angle > TWOPI)  end_angle -= TWOPI;

  /* The following code fixes up the end_angle, if the difference between
     start and end_angle is very small, because the assumption is then,
     that this very small difference comes from the fact that the
     difference between the two angles is supposed to be 2*pi instead of
     zero, but the normalization of the angles above did reduce the difference
     to zero. */
  if (ABS_MACRO(start_angle - end_angle) < 0.001) {
    closed_arc = TRUE;
    end_angle += TWOPI;
  }

  /* If shading is in effect, the path must be closed. Otherwise shading
     is turned off. */
  if (ShadeNextObject && !closed_arc) {
    ShadeNextObject = FALSE;
    if (Verbose > 0) {
      fprintf (stderr, "TpicArcBusiness(): K_ia/K_ar: shading in effect, path not closed.\n");
      fprintf (stderr, "                   shading turned off.\n");
    }
  }

  /* The next question is whether the arc / circle / ellipsis should be drawn or not.
     If it's the K_ia command it should be not. But for debugging purposes we
     deemed it as better, that the path is drawn in the case of the K_ia command,
     if the path is not closed. If it's closed, the K_ia does shading only.
     K_ar always draws the path. */
  if (control)
    draw_arc_path = TRUE;
  else {
    if (closed_arc)
      draw_arc_path = FALSE;
    else {
      draw_arc_path = TRUE;
      if (Verbose > 0)
	fprintf (stderr, "TpicArcBusiness(): K_ia, path not closed: draw arc, no shading.\n");
    }
  }

  /* Angles in PostScript are in degrees. */
  start_angle *= 360.0/TWOPI;
  end_angle *= 360.0/TWOPI;

  /* Write out the PostScript code here. */
  TpicSaveCurrentPoint();
  PssSendCommand("newpath");
  PssSendInteger(XCONV(xc));
  PssSendInteger(YCONV(yc));
  PssSendInteger(xsc(xrad));
  PssSendInteger(ysc(yrad));
  /* To use PssSendCommand for the following is a little bit of
     cheating but it works. */
  sprintf (buffer, "%5.2lf", start_angle);
  PssSendCommand (buffer);
  sprintf (buffer, "%5.2lf", end_angle);
  PssSendCommand (buffer);

  /* The shading value reported here is -1, if no shading takes place,
     and otherwise it's a number in the range 0 .. 1, which is the setgray
     value for PostScript to be used. */
  sprintf (buffer, "%5.2lf", ShadeNextObject ? 1.0 - ColorOfNextShade:-1.0);
  PssSendCommand (buffer);

  /* Now convey information whether path should be drawn or not. */
  PssSendCommand (draw_arc_path ? "true":"false");

  /* Send information out here whether the path should be drawn or not. */
  PssSendCommand ("@ellipse");

  /* Resetting things. */
  if (ShadeNextObject)
    ShadeNextObject = FALSE;
  TpicRestoreCurrentPoint();
}

/*
 *  Draw a Chaikin spline along the previously defined path
 *  @ChaikinSplineDraw strokes the path, so we don't do it here.
 */
void 
Tpic_flush_spline()
{
  int i;
  
  TpicSaveCurrentPoint();
  for (i=1; i<=TpicPathLength; i++) {
    PssSendInteger(XCONV(xx[i]));
    PssSendInteger(YCONV(yy[i]));
  }
  PssSendInteger(TpicPathLength);
  PssSendCommand ("@ChaikinSplineDraw");
  TpicRestoreCurrentPoint();
  TpicPathLength = 0;
}