Back to index

tetex-bin  3.0
drawPS.c
Go to the documentation of this file.
00001 /*
00002  *                      D R A W P S . C
00003  *
00004  *  Changed to take magnification into account, 27 August 1990.
00005  *
00006  * (minor mods by don on 5 Jan 90 to accommodate highres PostScript)
00007  *
00008  * $Revision: 1.1 $
00009  *
00010  * $Log:      drawPS.c,v $
00011  * Revision 1.1  90/03/10  20:32:48  grunwald
00012  * Initial revision
00013  *
00014  *
00015  * 89/04/24: decouty@irisa.fr, priol@irisa.fr
00016  *              added three shading levels for fig,
00017  *              removed '\n' in cmdout() calls
00018  *
00019  * Revision 1.4  87/05/07  15:06:24  dorab
00020  * relinked back hh to h and vv to v undoing a previous change.
00021  * this was causing subtle quantization problems. the main change
00022  * is in the definition of hconvPS and vconvPS.
00023  *
00024  * changed the handling of the UC seal. the PS file should now be
00025  * sent via the -i option.
00026  *
00027  * Revision 1.3  86/04/29  23:20:55  dorab
00028  * first general release
00029  *
00030  * Revision 1.3  86/04/29  22:59:21  dorab
00031  * first general release
00032  *
00033  * Revision 1.2  86/04/29  13:23:40  dorab
00034  * Added distinctive RCS header
00035  *
00036  */
00037 #ifndef lint
00038 char RCSid[] =
00039   "@(#)$Header: /usr/local/src/TeX/Dvips-5.0.2/RCS/drawPS.c,v 1.1 90/03/10 20:32:48 grunwald Exp $ (UCLA)";
00040 #endif
00041 
00042 /*
00043  the driver for handling the \special commands put out by
00044  the tpic program as modified by Tim Morgan <morgan@uci.edu>
00045  the co-ordinate system is with the origin at the top left
00046  and the x-axis is to the right, and the y-axis is to the bottom.
00047  when these routines are called, the origin is set at the last dvi position,
00048  which has to be gotten from the dvi-driver (in this case, dvips) and will
00049  have to be converted to device co-ordinates (in this case, by [hv]convPS).
00050  all dimensions are given in milli-inches and must be converted to what
00051  dvips has set up (i.e. there are convRESOLUTION units per inch).
00052 
00053  it handles the following \special commands
00054     pn n                        set pen size to n
00055     pa x y                      add path segment to (x,y)
00056     fp                          flush path
00057     ip                          flush invisible path, i.e. do shading only
00058     da l                        flush dashed - each dash is l (inches)
00059     dt l                        flush dotted - one dot per l (inches)
00060     sp [l]                      flush spline - optional l is dot/dash length
00061     ar x y xr yr sa ea          arc centered at (x,y) with x-radius xr
00062                                 and y-radius yr, start angle sa (in rads),
00063                                 end angle ea (in rads)
00064     ia x y xr yr sa ea          invisible arc with same parameters as above
00065     sh [s]                      shade last path (box, circle, ellipse)
00066                             s = gray level (0 = white, 1 = black)
00067     wh                          whiten last path (box, circle, ellipse)
00068     bk                          blacken last path (box,circle, ellipse)
00069     tx                          texture command - Added by T. Priol
00070                             (priol@irisa.fr), enhanced by M. Jourdan:
00071                             textures are translated into uniform gray levels
00072 
00073   this code is in the public domain
00074 
00075   written by dorab patel <dorab@cs.ucla.edu>
00076   december 1985
00077   released feb 1986
00078   changes for dvips july 1987
00079 
00080   */
00081 
00082 #include "dvips.h"
00083 
00084 #ifdef TPIC                     /* rest of the file !! */
00085 
00086 #include <math.h>           /* used in function "arc" */
00087 
00088 #ifdef DEBUG
00089 extern integer debug_flag;
00090 #endif  /* DEBUG */
00091 
00092 /*
00093  * external functions used here
00094  */
00095 #include "protos.h"
00096 /*
00097  * external variables used here
00098  */
00099 extern integer hh,vv;           /* the current x,y position in pixel units */
00100 extern int actualdpi ;
00101 extern int vactualdpi ;
00102 extern double mag ;
00103 
00104 #define convRESOLUTION DPI
00105 #define convVRESOLUTION VDPI
00106 #define tpicRESOLUTION 1000     /* number of tpic units per inch */
00107 #ifndef TRUE
00108 #define TRUE 1
00109 #define FALSE 0
00110 #endif
00111 
00112 /* convert from tpic units to PS units */
00113 #define PixRound(a,b) zPixRound((a),(b))
00114 #define convPS(x) PixRound((x),convRESOLUTION)
00115 #define convVPS(x) PixRound((x),convVRESOLUTION)
00116 
00117 /* convert from tpic locn to abs horiz PS locn */
00118 #define hconvPS(x) (hh + convPS(x))
00119 /* convert from tpic locn to abs vert PS locn */
00120 #define vconvPS(x) (vv + convVPS(x))
00121 /* convert to degrees */
00122 #define convDeg(x) (360*(x)/(2*3.14159265358979))
00123 
00124 /* if PostScript had splines, i wouldnt need to store the path */
00125 #define MAXPATHS 600            /* maximum number of path segments */
00126 #define NONE 0                  /* for shading */
00127 #define BLACK           1       /* for shading */
00128 #define GRAY         2       /* MJ; for shading */
00129 #define WHITE           3       /* for shading */
00130 
00131 /* the following defines are used to make the PostScript shorter;
00132   the corresponding defines are in the PostScript prolog special.lpro */
00133 #define MOVETO "a"
00134 #define LINETO "li"
00135 #define RCURVETO "rc"
00136 #define RLINETO "rl"
00137 #define STROKE "st"
00138 #define FILL "fil"
00139 #define NEWPATH "np"
00140 #define CLOSEPATH "closepath"
00141 /*
00142  * STROKE and FILL must restore the current point to that
00143  * saved by NEWPATH
00144  */
00145 
00146 static double xx[MAXPATHS], yy[MAXPATHS]; /* the current path in milli-inches */
00147 static integer pathLen = 0;             /* the current path length */
00148 static integer shading = NONE;  /* what to shade the last figure */
00149 static integer penSize = 2;             /* pen size in PS units */
00150 
00151 /* forward declarations */
00152 /* static void doShading(); */
00153 static double zPixRound P2H(double, double);             /* (x/y)PixRound(x,y) */
00154 static double  shadetp = 0.5;
00155              /* shading level, initialized as requested by tpic 2.0 -- MJ */
00156 
00157 void
00158 setPenSize P1C(char *, cp)
00159 {
00160   long ps;
00161 
00162   if (sscanf(cp, " %ld ", &ps) != 1)
00163     {
00164       error("Illegal .ps command format");
00165       return;
00166     }
00167 
00168   penSize = convPS(ps);
00169   doubleout((integer)penSize);
00170   cmdout("setlinewidth");
00171 }                               /* end setPenSize */
00172 
00173 void
00174 addPath P1C(char *, cp)
00175 {
00176   double x,y;
00177 
00178   if (++pathLen >= MAXPATHS) error("! Too many points");
00179   if (sscanf(cp, " %lg %lg ", &x, &y) != 2)
00180     error("! Malformed path expression");
00181   xx[pathLen] = x;
00182   yy[pathLen] = y;
00183 }                               /* end of addPath */
00184 
00185 void
00186 arc P2C(char *, cp, int, invis)
00187 {
00188   double xc, yc, xrad, yrad;
00189   double startAngle, endAngle;
00190 
00191   if (sscanf(cp, " %lg %lg %lg %lg %lg %lg ", &xc, &yc, &xrad, &yrad,
00192              &startAngle, &endAngle) != 6)
00193     {
00194       error("Illegal arc specification");
00195       return;
00196     }
00197 
00198 /* To have filled ellipses/circles also have borders, we duplicate the object
00199    specification; first time we emit a FILL command, second time we emit a
00200    STROKE command.
00201    There certainly exists a better way to do that in PostScript, by
00202    remembering the object specification (path) across the fill command.
00203    However I (MJ) am completely unproficient at PostScript...
00204 */
00205 
00206 /* we need the newpath since STROKE doesnt do a newpath */
00207 
00208   if (shading) {
00209       /* first time for shading */
00210       cmdout(NEWPATH);
00211       doubleout(hconvPS(xc));
00212       doubleout(vconvPS(yc));
00213       doubleout(convPS(xrad));
00214       if (xrad != yrad && VDPI == DPI)
00215        doubleout(convPS(yrad));
00216       doubleout(convDeg(startAngle));
00217       doubleout(convDeg(endAngle));
00218 
00219       if (xrad == yrad && VDPI == DPI)           /* for arcs and circles */
00220        cmdout("arc");
00221       else
00222        cmdout("ellipse");
00223 
00224       cmdout(FILL);
00225       shading = NONE;
00226       cmdout("0 setgray");  /* default of black */
00227   }
00228 
00229   if (!invis) {
00230       /* There is a problem with tpic 2.0's handling of dotted ellipses.
00231         In most (all?) cases, after conversion to degrees and rounding
00232         to integers, startAngle and endAngle become the same, so nothing,
00233         not even a dot (as requested), is printed in the output (at least
00234         on my Apple LaserWriter).
00235         So I (MJ) singled out this case.
00236        */
00237       double degStAng = convDeg(startAngle),
00238              degEnAng = convDeg(endAngle);
00239 
00240       cmdout(NEWPATH);             /* save current point */
00241 
00242       if (degStAng != degEnAng) { /* normal case */
00243          doubleout(hconvPS(xc));
00244          doubleout(vconvPS(yc));
00245          doubleout(convPS(xrad));
00246          if (xrad != yrad)
00247              doubleout(convPS(yrad));
00248          doubleout(degStAng);
00249          doubleout(degEnAng);
00250 
00251          if (xrad == yrad)  /* for arcs and circles */
00252              cmdout("arc");
00253          else
00254              cmdout("ellipse");
00255       }
00256       else {                /* draw a single dot */
00257          double xdot, ydot;
00258          xdot = ((double)xc + (double)xrad * cos ((startAngle + endAngle) / 2.0));
00259          ydot = ((double)yc + (double)yrad * sin ((startAngle + endAngle) / 2.0));
00260          doubleout(hconvPS(xdot));
00261          doubleout(vconvPS(ydot));
00262          cmdout(MOVETO);
00263          /* +1 to make sure the dot is printed */
00264          doubleout(hconvPS(xdot)+1);
00265          doubleout(vconvPS(ydot)+1);
00266          cmdout(LINETO);
00267       }
00268 
00269       cmdout(STROKE);
00270   }
00271 }                               /* end of arc */
00272 
00273 /*
00274  * In pic dashed lines are created in a manner which makes boths ends
00275  * of a line solid.
00276  * This means that the corner of a dotted box is a dot and the corner
00277  * of a dashed box is solid.
00278  * The number of inches/dash must be adjusted accordingly.
00279  */
00280 void
00281 flushDashedPath P2C(int, dotted, double, inchesPerDash)
00282 {
00283   register int i;
00284   int nipd = (integer) convPS((int) inchesPerDash);
00285 
00286   if (nipd == 0)
00287     nipd = 1 ;
00288   if (pathLen < 2)
00289     {
00290       error("Path less than 2 points - ignored");
00291       pathLen = 0 ;
00292       return;
00293     }
00294 
00295   cmdout(NEWPATH);          /* to save the current point */
00296   for (i=2; i <= pathLen; i++) {
00297       integer dx = hconvPS(xx[i-1]) - hconvPS(xx[i]);
00298       integer dy = vconvPS(yy[i-1]) - vconvPS(yy[i]);
00299       double delta = sqrt((double) (dx * dx + dy * dy));
00300       double ipd;
00301       int ndashes;
00302 
00303       /*
00304        * If a line is dashed there must be an odd number of dashes.
00305        * This makes the first and last dash of the line solid.
00306        * If a line is dotted the length of a dot + space sequence must be
00307        * chosen such that (n+1) * dotlength + n space length = linelength
00308        * => (dot + space) = (line - dot) / n
00309        */
00310       if (dotted)
00311          delta -= penSize;
00312       ndashes = (integer) (delta / nipd + 0.5); /* number of dashes on line */
00313       /* only odd when dashed, choose odd number with smallest deviation */
00314       if (ndashes > 2 && (ndashes & 0x1) == 0 && !dotted) {
00315        if (fabs(nipd - delta/(ndashes - 1)) <
00316            (fabs(nipd - delta/(ndashes + 1))))
00317            ndashes -= 1;
00318        else
00319            ndashes += 1;
00320       } else if (ndashes < 1)
00321          ndashes = 1 ;
00322       ipd = delta / ndashes;
00323       if (ipd <= 0.0)
00324          ipd = 1.0 ;
00325       cmdout("[");
00326       if (dotted) {
00327        doubleout(penSize);
00328        doubleout(fabs(ipd - penSize));
00329       } else                               /* if dashed */
00330        doubleout(ipd);
00331 
00332       cmdout("]");
00333       doubleout(0);
00334       cmdout("setdash");
00335       doubleout(hconvPS(xx[i-1]));
00336       doubleout(vconvPS(yy[i-1]));
00337       cmdout(MOVETO);
00338       doubleout(hconvPS(xx[i]));
00339       doubleout(vconvPS(yy[i]));
00340       cmdout(LINETO);
00341       cmdout(STROKE);
00342   }
00343   cmdout("[] 0 setdash");
00344   pathLen = 0;
00345 }
00346 
00347 void
00348 flushPath P1C(int, invis)
00349 {
00350   register int i;
00351 
00352   if (pathLen < 2)
00353     {
00354       error("Path less than 2 points - ignored");
00355       pathLen = 0 ;
00356       return;
00357     }
00358 
00359 /* To have filled boxes also have borders, we duplicate the object
00360    specification; first time we emit a FILL command, second time we emit a
00361    STROKE command.
00362    There certainly exists a better way to do that in PostScript, by
00363    remembering the object specification (path) across the fill command.
00364    However I (MJ) am completely unproficient at PostScript...
00365 */
00366 
00367 #ifdef DEBUG
00368     if (dd(D_SPECIAL))
00369         (void)fprintf(stderr,
00370 #ifdef SHORTINT
00371             "flushpath(1): hh=%ld, vv=%ld, x=%lg, y=%lg, xPS=%lg, yPS=%lg\n",
00372 #else   /* ~SHORTINT */
00373             "flushpath(1): hh=%d, vv=%d, x=%lg, y=%lg, xPS=%lg, yPS=%lg\n",
00374 #endif  /* ~SHORTINT */
00375                     hh, vv, xx[1], yy[1], hconvPS(xx[1]), vconvPS(yy[1]));
00376 #endif /* DEBUG */
00377   if (shading) {
00378       /* first time for shading */
00379       cmdout(NEWPATH); /* to save the current point */
00380       doubleout(hconvPS(xx[1]));
00381       doubleout(vconvPS(yy[1]));
00382       cmdout(MOVETO);
00383       for (i=2; i < pathLen; i++) {
00384 #ifdef DEBUG
00385        if (dd(D_SPECIAL))
00386            (void)fprintf(stderr,
00387 #ifdef SHORTINT
00388               "flushpath(%ld): hh=%ld, vv=%ld, x=%lg, y=%lg, xPS=%lg, yPS=%lg\n",
00389 #else   /* ~SHORTINT */
00390               "flushpath(%d): hh=%d, vv=%d, x=%lg, y=%lg, xPS=%lg, yPS=%lg\n",
00391 #endif  /* ~SHORTINT */
00392                      i, hh, vv, xx[i], yy[i], hconvPS(xx[i]), vconvPS(yy[i]));
00393 #endif /* DEBUG */
00394        doubleout(hconvPS(xx[i]));
00395        doubleout(vconvPS(yy[i]));
00396        cmdout(LINETO);
00397        }
00398       /* Shading should always apply to a closed path
00399         force it and complain if necessary */
00400       if (xx[1] == xx[pathLen] && yy[1] == yy[pathLen])
00401        cmdout(CLOSEPATH);
00402       else {
00403        doubleout(hconvPS(xx[pathLen]));
00404        doubleout(vconvPS(yy[pathLen]));
00405        cmdout(LINETO);
00406        cmdout(CLOSEPATH);
00407        error("Attempt to fill a non-closed path");
00408        fprintf(stderr,
00409 #ifdef SHORTINT
00410               "\tfirst point: x=%lg, y=%lg; last point: x=%lg, y=%lg\n",
00411 #else   /* ~SHORTINT */
00412               "\tfirst point: x=%lg, y=%lg; last point: x=%lg, y=%lg\n",
00413 #endif  /* ~SHORTINT */
00414                      xx[1], yy[1], xx[pathLen], yy[pathLen]);
00415 
00416       }
00417       cmdout(FILL);
00418       shading = NONE;
00419       cmdout("0 setgray");  /* default of black */
00420   }
00421 
00422   if (!invis) {
00423       cmdout(NEWPATH);             /* to save the current point */
00424       doubleout(hconvPS(xx[1]));
00425       doubleout(vconvPS(yy[1]));
00426       cmdout(MOVETO);
00427       for (i=2; i < pathLen; i++) {
00428          doubleout(hconvPS(xx[i]));
00429          doubleout(vconvPS(yy[i]));
00430          cmdout(LINETO);
00431       }
00432       if (xx[1] == xx[pathLen] && yy[1] == yy[pathLen])
00433          cmdout(CLOSEPATH);
00434       else {
00435          doubleout(hconvPS(xx[pathLen]));
00436          doubleout(vconvPS(yy[pathLen]));
00437          cmdout(LINETO);
00438       }
00439       cmdout(STROKE);
00440   }
00441 
00442   pathLen = 0;
00443 }                               /* end of flushPath */
00444 
00445 void
00446 flushDashed P2C(char *, cp, int, dotted)
00447 {
00448   double inchesPerDash;
00449   int savelen = pathLen;
00450 
00451   if (sscanf(cp, " %lg ", &inchesPerDash) != 1)
00452     {
00453       error ("Illegal format for dotted/dashed line");
00454       return;
00455     }
00456 
00457   if (inchesPerDash <= 0.0)
00458     {
00459       error ("Length of dash/dot cannot be negative");
00460       return;
00461     }
00462 
00463   inchesPerDash = 1000 * inchesPerDash; /* to get milli-inches */
00464   flushPath(1);/* print filled, always invisible bounds */
00465   pathLen = savelen;
00466   flushDashedPath(dotted,inchesPerDash);
00467 
00468   cmdout("[] 0 setdash");
00469 }                               /* end of flushDashed */
00470 
00471 void
00472 flushSpline P1C(char *, cp)
00473 {                               /* as exact as psdit!!! */
00474   register long i ;
00475   register double dxi, dyi, dxi1, dyi1;
00476 
00477   if (*cp) {
00478       double inchesPerDash;
00479       int ipd ;
00480 
00481       if (sscanf(cp, "%lg ", &inchesPerDash) != 1) {
00482          error ("Illegal format for dotted/dashed spline");
00483          return;
00484       }
00485 
00486       ipd = (int)(1000.0 * inchesPerDash); /* to get milli-inches */
00487 
00488       if (ipd != 0) {
00489          cmdout("[");
00490          if (inchesPerDash < 0.0) /* dotted */ {
00491              doubleout(penSize);
00492              doubleout(fabs(convPS((int)-ipd) - penSize));
00493          } else             /* dashed */
00494              doubleout(convPS((int)ipd));
00495 
00496          cmdout("]");
00497          doubleout(0);
00498          cmdout("setdash");
00499       }
00500   }
00501 
00502   if (pathLen < 2)
00503     {
00504       error("Spline less than two points - ignored");
00505       return;
00506     }
00507 
00508   cmdout(NEWPATH);      /* to save the current point */
00509   doubleout(hconvPS(xx[1]));
00510   doubleout(vconvPS(yy[1]));
00511   cmdout(MOVETO);
00512   doubleout(convPS((xx[2]-xx[1])/2));
00513   doubleout(convPS((yy[2]-yy[1])/2));
00514   cmdout(RLINETO);
00515 
00516   for (i=2; i < pathLen; i++)
00517     {
00518       dxi = convPS(xx[i] - xx[i-1]);
00519       dyi = convVPS(yy[i] - yy[i-1]);
00520       dxi1 = convPS(xx[i+1] - xx[i]);
00521       dyi1 = convVPS(yy[i+1] - yy[i]);
00522 
00523       doubleout(dxi/3);
00524       doubleout(dyi/3);
00525       doubleout((3*dxi+dxi1)/6);
00526       doubleout((3*dyi+dyi1)/6);
00527       doubleout((dxi+dxi1)/2);
00528       doubleout((dyi+dyi1)/2);
00529       cmdout(RCURVETO);
00530     }
00531 
00532   doubleout(hconvPS(xx[pathLen]));
00533   doubleout(vconvPS(yy[pathLen]));
00534   cmdout(LINETO);
00535 
00536 /*  doShading(); */
00537   cmdout(STROKE);           /* assume no shaded splines */
00538   pathLen = 0;
00539 
00540   if (*cp)
00541       cmdout("[] 0 setdash");
00542 
00543 }                               /* end of flushSpline */
00544 
00545 /* set shading level (used with  Fig 1.4-TFX). priol@irisa.fr, 89/04 */
00546 /* A better trial at setting shading level -- jourdan@minos.inria.fr */
00547 /* Count number of black bits in the pattern together with total number, */
00548 /* compute the average and use this as the PostScript gray level */
00549 void
00550 SetShade P1C(register char *, cp)
00551 {
00552     int blackbits = 0, totalbits = 0;
00553 
00554     while (*cp) {
00555        switch (*cp) {
00556        case '0':
00557            totalbits += 4;
00558            break;
00559        case '1':
00560        case '2':
00561        case '4':
00562        case '8':
00563            blackbits += 1;
00564            totalbits += 4;
00565            break;
00566        case '3':
00567        case '5':
00568        case '6':
00569        case '9':
00570        case 'a':
00571        case 'A':
00572        case 'c':
00573        case 'C':
00574            blackbits += 2;
00575            totalbits += 4;
00576            break;
00577        case '7':
00578        case 'b':
00579        case 'B':
00580        case 'd':
00581        case 'D':
00582        case 'e':
00583        case 'E':
00584            blackbits += 3;
00585            totalbits += 4;
00586            break;
00587        case 'f':
00588        case 'F':
00589            blackbits += 4;
00590            totalbits += 4;
00591            break;
00592        case ' ':
00593            break;
00594        default:
00595            error("Invalid character in .tx pattern");
00596            break;
00597        }
00598        cp++;
00599     }
00600     shadetp = 1.0 - ((double) blackbits / (double) totalbits);
00601     shading = GRAY;
00602 }                               /* end of SetShade       */
00603 
00604 void
00605 shadeLast P1C(char *, cp)
00606 {
00607   char tpout[20];
00608 
00609   if (*cp) {
00610       double tempShadetp;
00611 
00612       if (sscanf(cp, "%lg ", &tempShadetp) != 1)
00613          error ("Illegal format for shade level");
00614       else if (tempShadetp < 0.0 || tempShadetp > 1.0)
00615          error ("Invalid shade level");
00616       else
00617          /* if "sh" has an argument we can safely assume that tpic 2.0 is used
00618             so that all subsequent "sh" commands will come with an explicit
00619             argument.  Hence we may overwrite shadetp's old value */
00620          /* Also note the inversion of gray levels for tpic 2.0 (0 = white,
00621             1 = black) w.r.t. PostScript (0 = black, 1 = white) */
00622          shadetp = 1.0 - tempShadetp;
00623   }
00624 
00625   shading = GRAY;
00626   sprintf(tpout,"%1.3f setgray",shadetp); /* priol@irisa.fr, MJ */
00627   cmdout(tpout);
00628 }                               /* end of shadeLast */
00629 
00630 void
00631 whitenLast P1H(void)
00632 {
00633   shading = WHITE;
00634   cmdout("1 setgray");
00635 }                               /* end of whitenLast */
00636 
00637 void
00638 blackenLast P1H(void)
00639 {
00640   shading = BLACK;
00641   cmdout("0 setgray");          /* actually this aint needed */
00642 }                               /* end of whitenLast */
00643 
00644 /*
00645 static void
00646 doShading P1H(void)
00647 {
00648   if (shading)
00649     {
00650       cmdout(FILL);
00651       shading = NONE;
00652       cmdout("0 setgray");      !* default of black *!
00653     }
00654   else
00655     cmdout(STROKE);
00656 }                               !* end of doShading *!
00657 */
00658 
00659 /*
00660  *   We need to calculate (x * convDPI * mag) / (tpicResolution * 1000)
00661  *   So we use doubleing point.  (This should have a very small impact
00662  *   on the speed of the program.)
00663  */
00664 static double
00665 zPixRound P2C(register double, x, /* in DVI units */
00666              register double, convDPI     /* dots per inch */
00667              )      /* return rounded number of pixels */
00668 {
00669    return ((x * mag * (double)convDPI /
00670                     (1000.0 * tpicRESOLUTION))) ;
00671 }
00672 
00673 #endif /* TPIC */
00674