home *** CD-ROM | disk | FTP | other *** search
/ Resource Library: Graphics / graphics-16000.iso / msdos / animutil / anim13 / grafsupp.c < prev    next >
Text File  |  1990-02-07  |  14KB  |  401 lines

  1. /* grafsupp.c
  2.  
  3. This file contains functions scale(), load_array(), draw_perimeter(),
  4. label_plot(), and plot_curve() which supplement the graphics functions
  5. in Borland's Turbo C.
  6. In particular, they make possible device independent graphics programming,
  7. i.e. you don't need to use pixels for graphics coordinates.
  8. You are free to use whatever coordinates are natural for your problem.
  9. The routines in this file are modeled after routines
  10. in the graphics package written at the National Center for
  11. Atmospheric Research (NCAR) in Boulder, Colorado;
  12. I have not seen the source code for the NCAR (Fortran) subroutines,
  13. but I have read their documentation.
  14.  
  15. Copyright 1990 by Jon Ahlquist, Department of Meteorology B-161,
  16. Florida State University, Tallahassee, Florida 32306-3034, USA.
  17. Telephone: (904) 644-1558.
  18. Telnet address: ahlquist@metsat.met.fsu.edu (ahlquist@128.186.5.2)
  19.  
  20. This software may be freely copied without charge.
  21. Copyright is made to prevent anyone from trying to impose restrictions
  22. on this software.
  23. All software and documentation is provided "as is" without warranty
  24. of any kind.
  25.  
  26. Development of this material was sponsored by NSF grant ATM-8714674.
  27. */
  28.  
  29.                       /* Prototypes for:  */
  30. #include <graphics.h> /* Borland graphics */
  31. #include <grafsupp.h> /* Functions here   */
  32. #include <stdlib.h>   /* min() and max()  */
  33.  
  34. /* Declare global variables set by scale(). */
  35.  
  36. float _xscale,     _xoffset,     _yscale,       _yoffset,
  37.       _xscale_gen, _xoffset_gen, _yscale_gen,   _yoffset_gen,
  38.       _X_left_gen, _X_right_gen, _Y_bottom_gen, _Y_top_gen,
  39.       pixel_aspect_ratio;
  40.  
  41. int   XX_max, YY_max, XX_width;
  42.  
  43. /*********************************************************************/
  44.  
  45. void scale(float X_left_gen,    float X_right_gen,
  46.            float Y_bottom_gen,  float Y_top_gen,
  47.  
  48.            float x_left_user,   float x_right_user,
  49.            float y_bottom_user, float y_top_user)
  50.  
  51. /*
  52. After initgraph() has been called to switch the screen display
  53. to graphics mode, you can call scale() to compute constants needed for
  54. coordinate transformations from "generic" and "user" coordinates
  55. to the pixel coordinates needed by Borland graphics functions.
  56. The actual transformations are carried out by the following macros
  57. defined in grafsupp.h.
  58.  
  59. Macro              Convert        coordinate to           pixel coordinate.
  60. int XX(float x)            user x               horizontal
  61. int YY(float y)            user y               vertical
  62. int XG(float xg)        generic xg              horizontal
  63. int YG(float yg)        generic yg              vertical
  64.  
  65. "Generic" and "user" coordinates and macros XX(), etc. are explained below.
  66. An example program is contained in file grafdemo.c.
  67. Also, see the other functions in this file, which complement scale().
  68.  
  69. Imagine the largest square that will fit on the screen
  70. of your computer monitor, and center it on your monitor screen.
  71. Let (0,0) denote the lower left corner and (1,1) the upper right corner
  72. of this square.  These reference points define what
  73. we shall call "generic" coordinates.  Since a computer monitor is actually
  74. about 1/3 wider than it is tall, the lower left and upper right corners
  75. of the monitor screen are approximately at generic coordinates
  76. (-1/6, 0) and (7/6, 1).
  77.  
  78. The first four arguments to scale() define the x and y edges, in terms
  79. of generic coordinates, of the window that you want to use for plotting.
  80. For example,   scale(-1./6., 7./6., 0.5, 1.0, ...)
  81. would define the upper half of the screen.
  82.  
  83. The last four arguments to scale() define "user" coordinates
  84. that will refer to the same locations on the screen
  85. as were defined by the generic coordinates.
  86. "User" coordinates are whatever coordinates are natural
  87. for the plot that you are creating.  For example, let's suppose
  88. that you want to plot temperature as a function of time of day.
  89. You have measured time in hours, starting with 0 at midnight,
  90. passing through 12 at noon, and ending with 24 at the next midnight.
  91. During that time, temperature varied between 50 and 70 degrees
  92. Fahrenheit.  Therefore, you want to create a graph whose x-axis
  93. runs from 0 to 24 and whose y-axis runs from 50 to 70.
  94. That would define the user coordinates for our problem.
  95. Suppose your program says
  96. scale(-1./6., 7./6., 0.5, 1.0,  0., 24., 50., 70.);
  97. That would mean that you want time = 0 to appear at horizontal generic
  98. coordinate -1/6, which is the left edge of the monitor screen.
  99. Time = 24 would be associated with horizontal generic coordinate 7/6,
  100. which is the right edge of the monitor screen.
  101. Temperature = 50 would be associated with vertical generic coordinate 0.5,
  102. which is halfway up the screen.
  103. Temperature = 70 would be associated with vertical generic coordinate 1.0,
  104. which is at the top of the screen.
  105.  
  106. Once you have called scale(), you can use macros XX(), YY(), XG(), and YG()
  107. defined in grafsupp.h which are explained as follows.
  108. Let (x,y)   be an arbitrary user coordinate, and
  109. let (xg,yg) be an arbitrary generic coordinate.
  110. XX(x) and YY(y) will be integers denoting the horizontal and vertical
  111. pixel coordinates of (x,y).
  112. XG(xg) and YG(yg) will be integers denoting the horizontal and
  113. vertical pixel coordinates of (xg,yg).
  114.  
  115. Thus, with scale() and the four macros XX(), etc.,
  116. you can think in terms of user coordinates (x,y) and/or generic
  117. cooridnates (xg,yg), which are totally independent of the graphics mode
  118. that you are using.  You can rely on XX(), YY(), XG(), and YG()
  119. to compute the pixel addresses that you need for any of the Borland
  120. graphics functions.
  121.  
  122.  
  123. Dr. Jon Ahlquist
  124. Dept. of Meteorology
  125. Florida State University
  126. Tallahassee, FL 32306-3034
  127. Telephone (904) 644-1558
  128.  
  129. 26 June 1988, 25 Jul 1989.
  130. */
  131.  
  132. {
  133. int  x_asp, y_asp;
  134.  
  135. /* Make global variable copies of the user's "generic" coordinates. */
  136.  
  137.               _Y_top_gen = Y_top_gen;
  138. _X_left_gen = X_left_gen; _X_right_gen = X_right_gen;
  139.            _Y_bottom_gen = Y_bottom_gen;
  140.  
  141. /*
  142. First we cite the equations for converting between
  143. the user's coordinates and "generic" coordinates.
  144. "Generic" coordinates lie in a square extending
  145. from (0,0) in the lower left corner of a square to (1,1) in the
  146. upper right corner.
  147. Let (x,y) refer to a coordinate in the user's system,
  148. and (X,Y) be the corresponding point in "generic"
  149. coordinates.
  150. Using floating point arithmetic, the user's coordinates
  151. are related to generic coordinates through the formulae:
  152.  
  153.    X = (x            - x_left_user)   * (X_right_gen - X_left_gen)
  154.      / (x_right_user - x_left_user)
  155.      +  X_left_gen
  156.  
  157. and
  158.  
  159.    Y = (y            - y_bottom_user) * (Y_top_gen - Y_bottom_gen)
  160.      / (y_top_user   - y_bottom_user)
  161.      +  Y_bottom_gen
  162.  
  163. We may rewrite these equations as
  164.    X = _xscale * x  +  _xoffset, and
  165.    Y = _yscale * y  +  _yoffset,
  166. where the constants are given by:
  167. */
  168.  
  169. _xscale  = (X_right_gen  - X_left_gen  ) / (x_right_user  - x_left_user);
  170. _yscale  = (Y_top_gen    - Y_bottom_gen) / (y_top_user    - y_bottom_user);
  171. _xoffset = (x_right_user * X_left_gen    -  x_left_user   * X_right_gen)
  172.          / (x_right_user -  x_left_user);
  173. _yoffset = (y_top_user   * Y_bottom_gen  -  y_bottom_user * Y_top_gen)
  174.          / (y_top_user   -  y_bottom_user);
  175.  
  176.  
  177. /*
  178. Next, we turn to converting from "generic" coordinates to
  179. graphics mode coordinates.
  180. Let (XX,YY) denote a Borland graphics mode coordinate where (0,0) is
  181. in the upper left corner of the screen and (XX_max, YY_max) is
  182. in the lower right corner.  XX_max and YY_max are given by:
  183. */
  184.  
  185. XX_max = getmaxx(); /* No. of columns of pixels, less 1. */
  186. YY_max = getmaxy(); /* No. of rows    of pixels, less 1. */
  187.  
  188. /*
  189. Let pixel_aspect_ratio denote the ratio of width to height of a
  190. pixel.  Specifically:
  191. */
  192.  
  193. getaspectratio(&x_asp, &y_asp);
  194. pixel_aspect_ratio = (float) x_asp / (float) y_asp;
  195.  
  196. /*
  197. We note that a TV monitor screen is always wider than it is tall.
  198. Thus, we will place Y=0 at YY=YY_max and Y=1 at YY=0.
  199. X=0 and X=1 will lie equidistant to the left and right of the
  200. center of the screen.  The distance from X=0 to X=1 on the monitor
  201. screen should be the same as the distance from Y=0 to Y=1, i.e.
  202. XX_width * x_asp = YY_max * y_asp,
  203. where XX_width is the width in pixels of the X=0 to X=1 region, so */
  204.  
  205. XX_width = (int) ( ((float)YY_max) / pixel_aspect_ratio + 0.5 );
  206.  
  207. /* Thus, the transformation betweeen "generic" coordinates
  208. and graphics mode coordinates is:
  209.  
  210. XX = X * XX_width  +  0.5 * (XX_max - XX_width)  +  0.5
  211.  
  212. and
  213.  
  214. YY = (1. - Y) * YY_max  +  0.5
  215.  
  216. The additive factors of 0.5 are to guarantee that
  217. the remainder of the right hand side of each equation will be
  218. rounded to the nearest integer when computing XX and YY,
  219. which are always integer values.
  220. */
  221.  
  222. /* Now write the conversion from (X,Y) to (XX,YY) in the form
  223.  
  224. XX = _xscale_gen * X + _xoffset_gen    and
  225. YY = _yscale_gen * Y + _yoffset_gen
  226.  
  227. where
  228. */
  229.  
  230. _xscale_gen  =  XX_width;
  231. _xoffset_gen =  0.5 * (XX_max - XX_width) + 0.5;
  232. _yscale_gen  = -1.0 * YY_max;
  233. _yoffset_gen =  YY_max + 0.5;
  234.  
  235. /*
  236. Combining the formulae to convert from (x,y) to (X,Y)
  237. and thence to (XX,YY), we may write
  238.  
  239. XX = _xscale * x  + _xoffset, and
  240. YY = _yscale * y  + _yoffset,
  241.  
  242. where
  243. */
  244.  
  245. _xscale  = XX_width * _xscale;
  246. _xoffset = XX_width * _xoffset  +  0.5 * (XX_max - XX_width) + 0.5;
  247. _yscale  =-YY_max   * _yscale;
  248. _yoffset = YY_max   * (1.0 - _yoffset) + 0.5;
  249.  
  250. } /*End of function scale(); */
  251.  
  252. /***********************************************************/
  253.  
  254. void load_array(float *x, int n, float xmin, float xmax)
  255. /* This function loads floating point array x with uniformly
  256. spaced values ranging from x[0]=xmin to x[n-1]=xmax. */
  257. {
  258. int i;
  259. float dx;
  260. dx = (xmax-xmin)/(n-1);
  261. for (i=0; i<(n-1); i++) *(x+i) = xmin + i*dx;
  262. *(x+n-1) = xmax;
  263. /* We assign the last element of x separately to guarantee that it is
  264. exactly equal to xmax without round-off errror. */
  265. }
  266.  
  267. /***********************************************************/
  268.  
  269. void draw_perimeter(int n_major_x, int n_minor_x,
  270.                     int n_major_y, int n_minor_y)
  271. {
  272. /* Perimeter draws a box around the plotting page region specified
  273. in the call to scale();
  274. It also draws tick marks on the inside of the box.
  275. A "major" tick mark is a little longer than a "minor" tick mark.
  276.  
  277. n_major_x is the number of pieces into which the x axis will
  278. be cut by major tick marks.  For example, if n_major_x=2,
  279. then one major tick mark will be drawn on the x-axis.
  280.  
  281. n_minor_x is the number of pieces into which one of the pieces
  282. created by n_major_x is cut.
  283.  
  284. n_major_y and n_minor_y have analogous meanings for the y axis.
  285.  
  286.  
  287. Jon Ahlquist, 13 January 1990.
  288. */
  289. int    i, j;
  290. float x, x1, x2, Dx, dx,
  291.       y, y1, y2, Dy, dy;
  292.  
  293. /* Draw a box around the window. */
  294. moveto (XG(_X_left_gen),  YG(_Y_bottom_gen));
  295. lineto (XG(_X_right_gen), YG(_Y_bottom_gen));
  296. lineto (XG(_X_right_gen), YG(_Y_top_gen));
  297. lineto (XG(_X_left_gen),  YG(_Y_top_gen));
  298. lineto (XG(_X_left_gen),  YG(_Y_bottom_gen));
  299.  
  300. /* Make sure that values are proper. */
  301. n_major_x = (int) max(n_major_x, 1);
  302. n_minor_x = (int) max(n_minor_x, 1);
  303. n_major_y = (int) max(n_major_y, 1);
  304. n_minor_y = (int) max(n_minor_y, 1);
  305.  
  306. /* Draw tick marks on the x axis. */
  307. Dx = (_X_right_gen - _X_left_gen)/n_major_x;
  308. dx = Dx/n_minor_x;
  309. x2 = _X_left_gen;
  310. for (i=1; i<=n_major_x; i++)
  311.    {
  312.    /* Major tick marks. */
  313.    x1  = x2;
  314.    x2 += Dx;
  315.    moveto(XG(x2), YG(_Y_top_gen)),
  316.    lineto(XG(x2), YG(_Y_top_gen-0.02));
  317.    moveto(XG(x2), YG(_Y_bottom_gen));
  318.    lineto(XG(x2), YG(_Y_bottom_gen+0.02));
  319.  
  320.    /* Minor tick marks. */
  321.    x = x1;
  322.    for (j=1; j<n_minor_x; j++)
  323.       {
  324.       x += dx;
  325.       moveto(XG(x), YG(_Y_top_gen)),
  326.       lineto(XG(x), YG(_Y_top_gen-0.01));
  327.       moveto(XG(x), YG(_Y_bottom_gen));
  328.       lineto(XG(x), YG(_Y_bottom_gen+0.01));
  329.       }
  330.    }
  331.  
  332. /* Draw major tick marks on the y axis. */
  333. Dy = (_Y_top_gen - _Y_bottom_gen)/n_major_y;
  334. dy = Dy/n_minor_y;
  335. y2 = _Y_bottom_gen;
  336. for (i=1; i<=n_major_y; i++)
  337.    {
  338.    /* Major tick marks. */
  339.    y1  = y2;
  340.    y2 += Dy;
  341.    moveto(XG(_X_left_gen),       YG(y1));
  342.    lineto(XG(_X_left_gen +0.02), YG(y1));
  343.    moveto(XG(_X_right_gen),      YG(y1));
  344.    lineto(XG(_X_right_gen-0.02), YG(y1));
  345.  
  346.    /* Minor tick marks. */
  347.    y = y1;
  348.    for (j=1; j<n_minor_y; j++)
  349.       {
  350.       y += dy;
  351.       moveto(XG(_X_left_gen),       YG(y));
  352.       lineto(XG(_X_left_gen +0.01), YG(y));
  353.       moveto(XG(_X_right_gen),      YG(y));
  354.       lineto(XG(_X_right_gen-0.01), YG(y));
  355.       }
  356.    }
  357. }
  358.  
  359. /***********************************************************/
  360.  
  361. void label_plot(char *title, char *xlabel, char *ylabel)
  362. /* This function will write a title just above a graph
  363. created using scale() and will label the x and y axes along
  364. the bottom and left sides of the graph.
  365.  
  366. Jon Ahlquist, 13 Jan 1990. */
  367. {
  368. settextjustify(CENTER_TEXT, BOTTOM_TEXT);
  369.  
  370. settextstyle(DEFAULT_FONT, HORIZ_DIR, 1);
  371. outtextxy(XG(0.5*(_X_left_gen+_X_right_gen)),
  372.           YG(_Y_top_gen    + 0.05), title);
  373.  
  374. settextstyle(DEFAULT_FONT,VERT_DIR, 1);
  375. outtextxy(XG(_X_left_gen-0.05), YG(_Y_bottom_gen), ylabel);
  376.  
  377. /* Reset settings to default values. */
  378. settextjustify(LEFT_TEXT, TOP_TEXT);
  379. settextstyle(DEFAULT_FONT, HORIZ_DIR, 1);
  380. outtextxy(XG(_X_left_gen), YG(_Y_bottom_gen - 0.05), xlabel);
  381. }
  382.  
  383. /***********************************************************/
  384.  
  385. void plot_curve(float *x, float *y, int n)
  386. /* Let x and y be  floating point arrays holding "n" values
  387. of "user" coordinates.
  388. "User" coordinates are explained in the documentation for scale().
  389. This routine plots the "n" values of (x,y) by connecting
  390. the (x,y) values with straight line segments.
  391. Before calling plot_curve(), you MUST call initgraph() and scale().
  392. The prototype for plot_curve() is declared in header file grafsupp.h.
  393.  
  394. Jon Ahlquist, 12 Jan 1990. */
  395. {
  396. int  i;
  397. moveto (XX(*x), YY(*y));
  398. for (i=1; i<n; i++) lineto (XX(*(x+i)), YY(*(y+i)));
  399. }
  400.  
  401.