"Fossies" - the Fresh Open Source Software Archive

Member "xearth-1.1/render.c" (7 Nov 1999, 16701 Bytes) of package /linux/misc/old/xearth-1.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  * render.c
    3  * kirk johnson
    4  * october 1993
    5  *
    6  * Copyright (C) 1989, 1990, 1993-1995, 1999 Kirk Lauritz Johnson
    7  *
    8  * Parts of the source code (as marked) are:
    9  *   Copyright (C) 1989, 1990, 1991 by Jim Frost
   10  *   Copyright (C) 1992 by Jamie Zawinski <jwz@lucid.com>
   11  *
   12  * Permission to use, copy, modify and freely distribute xearth for
   13  * non-commercial and not-for-profit purposes is hereby granted
   14  * without fee, provided that both the above copyright notice and this
   15  * permission notice appear in all copies and in supporting
   16  * documentation.
   17  *
   18  * Unisys Corporation holds worldwide patent rights on the Lempel Zev
   19  * Welch (LZW) compression technique employed in the CompuServe GIF
   20  * image file format as well as in other formats. Unisys has made it
   21  * clear, however, that it does not require licensing or fees to be
   22  * paid for freely distributed, non-commercial applications (such as
   23  * xearth) that employ LZW/GIF technology. Those wishing further
   24  * information about licensing the LZW patent should contact Unisys
   25  * directly at (lzw_info@unisys.com) or by writing to
   26  *
   27  *   Unisys Corporation
   28  *   Welch Licensing Department
   29  *   M/S-C1SW19
   30  *   P.O. Box 500
   31  *   Blue Bell, PA 19424
   32  *
   33  * The author makes no representations about the suitability of this
   34  * software for any purpose. It is provided "as is" without express or
   35  * implied warranty.
   36  *
   37  * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
   38  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
   39  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT
   40  * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
   41  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
   42  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
   43  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   44  */
   45 
   46 #include "xearth.h"
   47 #include "kljcpyrt.h"
   48 
   49 static void new_stars _P((double));
   50 static void new_grid _P((int, int));
   51 static void new_grid_dot _P((double *, double *));
   52 static int dot_comp _P((const void *, const void *));
   53 static void render_rows_setup _P((void));
   54 static void render_next_row _P((s8or32 *, int));
   55 static void no_shade_row _P((s8or32 *, u_char *));
   56 static void compute_sun_vector _P((double *));
   57 static void orth_compute_inv_x _P((double *));
   58 static void orth_shade_row _P((int, s8or32 *, double *, double *, u_char *));
   59 static void merc_shade_row _P((int, s8or32 *, double *, u_char *));
   60 static void cyl_shade_row _P((int, s8or32 *, double *, u_char *));
   61 
   62 static int      scanbitcnt;
   63 static ScanBit *scanbit;
   64 static s8or32   scan_to_pix[256];
   65 
   66 static int    night_val;
   67 static int    day_val_base;
   68 static double day_val_delta;
   69 
   70 static ExtArr   dots = NULL;
   71 static int      dotcnt;
   72 static ScanDot *dot;
   73 
   74 
   75 static int dot_comp(a, b)
   76      const void *a;
   77      const void *b;
   78 {
   79   return (((const ScanDot *) a)->y - ((const ScanDot *) b)->y);
   80 }
   81 
   82 
   83 static void render_rows_setup()
   84 {
   85   int i;
   86 
   87   scanbitcnt = scanbits->count;
   88   scanbit    = (ScanBit *) scanbits->body;
   89   dotcnt     = dots->count;
   90   dot        = (ScanDot *) dots->body;
   91 
   92   /* precompute table for translating between
   93    * scan buffer values and pixel types
   94    */
   95   for (i=0; i<256; i++)
   96     if (i == 0)
   97       scan_to_pix[i] = PixTypeSpace;
   98     else if (i > 64)
   99       scan_to_pix[i] = PixTypeLand;
  100     else
  101       scan_to_pix[i] = PixTypeWater;
  102 }
  103 
  104 
  105 static void render_next_row(buf, idx)
  106      s8or32 *buf;
  107      int     idx;
  108 {
  109   int      i, i_lim;
  110   int      tmp;
  111   int      _scanbitcnt;
  112   ScanBit *_scanbit;
  113 
  114   xearth_bzero((char *) buf, (unsigned) (sizeof(s8or32) * wdth));
  115 
  116   /* explicitly copy scanbitcnt and scanbit to local variables
  117    * to help compilers figure out that they can be registered
  118    */
  119   _scanbitcnt = scanbitcnt;
  120   _scanbit    = scanbit;
  121 
  122   while ((_scanbitcnt > 0) && (_scanbit->y == idx))
  123   {
  124     /* use i_lim to encourage compilers to register loop limit
  125      */
  126     i_lim = _scanbit->hi_x;
  127     tmp   = _scanbit->val;
  128     for (i=_scanbit->lo_x; i<=i_lim; i++)
  129       buf[i] += tmp;
  130 
  131     _scanbit    += 1;
  132     _scanbitcnt -= 1;
  133   }
  134 
  135   /* copy changes to scanbitcnt and scanbit out to memory
  136    */
  137   scanbitcnt = _scanbitcnt;
  138   scanbit    = _scanbit;
  139 
  140   /* use i_lim to encourage compilers to register loop limit
  141    */
  142   i_lim = wdth;
  143   for (i=0; i<i_lim; i++)
  144     buf[i] = scan_to_pix[(int) (buf[i] & 0xff)];
  145 
  146   while ((dotcnt > 0) && (dot->y == idx))
  147   {
  148     tmp = dot->x;
  149 
  150     if (dot->type == DotTypeStar)
  151     {
  152       if (buf[tmp] == PixTypeSpace)
  153         buf[tmp] = PixTypeStar;
  154     }
  155     else
  156     {
  157       switch (buf[tmp])
  158       {
  159       case PixTypeLand:
  160         buf[tmp] = PixTypeGridLand;
  161         break;
  162 
  163       case PixTypeWater:
  164         buf[tmp] = PixTypeGridWater;
  165         break;
  166       }
  167     }
  168 
  169     dot    += 1;
  170     dotcnt -= 1;
  171   }
  172 }
  173 
  174 
  175 static void no_shade_row(scanbuf, rslt)
  176      s8or32 *scanbuf;
  177      u_char *rslt;
  178 {
  179   int i, i_lim;
  180 
  181   /* use i_lim to encourage compilers to register loop limit
  182    */
  183   i_lim = wdth;
  184   for (i=0; i<i_lim; i++)
  185   {
  186     switch (scanbuf[i])
  187     {
  188     case PixTypeSpace:        /* black */
  189       rslt[0] = 0;
  190       rslt[1] = 0;
  191       rslt[2] = 0;
  192       break;
  193 
  194     case PixTypeStar:         /* white */
  195     case PixTypeGridLand:
  196     case PixTypeGridWater:
  197       rslt[0] = 255;
  198       rslt[1] = 255;
  199       rslt[2] = 255;
  200       break;
  201 
  202     case PixTypeLand:         /* green */
  203       rslt[0] = 0;
  204       rslt[1] = 255;
  205       rslt[2] = 0;
  206       break;
  207 
  208     case PixTypeWater:        /* blue */
  209       rslt[0] = 0;
  210       rslt[1] = 0;
  211       rslt[2] = 255;
  212       break;
  213 
  214     default:
  215       assert(0);
  216     }
  217 
  218     rslt += 3;
  219   }
  220 }
  221 
  222 
  223 static void compute_sun_vector(rslt)
  224      double *rslt;
  225 {
  226   rslt[0] = sin(sun_lon * (M_PI/180)) * cos(sun_lat * (M_PI/180));
  227   rslt[1] = sin(sun_lat * (M_PI/180));
  228   rslt[2] = cos(sun_lon * (M_PI/180)) * cos(sun_lat * (M_PI/180));
  229 
  230   XFORM_ROTATE(rslt, view_pos_info);
  231 }
  232 
  233 
  234 static void orth_compute_inv_x(inv_x)
  235      double *inv_x;
  236 {
  237   int i, i_lim;
  238 
  239   i_lim = wdth;
  240   for (i=0; i<i_lim; i++)
  241     inv_x[i] = INV_XPROJECT(i);
  242 }
  243 
  244 
  245 static void orth_shade_row(idx, scanbuf, sol, inv_x, rslt)
  246      int     idx;
  247      s8or32 *scanbuf;
  248      double *sol;
  249      double *inv_x;
  250      u_char *rslt;
  251 {
  252   int    i, i_lim;
  253   int    scanbuf_val;
  254   int    val;
  255   double x, y, z;
  256   double scale;
  257   double tmp;
  258   double y_sol_1;
  259 
  260   y = INV_YPROJECT(idx);
  261 
  262   /* save a little computation in the inner loop
  263    */
  264   tmp     = 1 - (y*y);
  265   y_sol_1 = y * sol[1];
  266 
  267   /* use i_lim to encourage compilers to register loop limit
  268    */
  269   i_lim = wdth;
  270   for (i=0; i<i_lim; i++)
  271   {
  272     scanbuf_val = scanbuf[i];
  273 
  274     switch (scanbuf_val)
  275     {
  276     case PixTypeSpace:        /* black */
  277       rslt[0] = 0;
  278       rslt[1] = 0;
  279       rslt[2] = 0;
  280       break;
  281 
  282     case PixTypeStar:         /* white */
  283     case PixTypeGridLand:
  284     case PixTypeGridWater:
  285       rslt[0] = 255;
  286       rslt[1] = 255;
  287       rslt[2] = 255;
  288       break;
  289 
  290     case PixTypeLand:         /* green, blue */
  291     case PixTypeWater:
  292       x = inv_x[i];
  293       z = tmp - (x*x);
  294       z = SQRT(z);
  295       scale = (x * sol[0]) + y_sol_1 + (z * sol[2]);
  296       if (scale < 0)
  297       {
  298     val = night_val;
  299       }
  300       else
  301       {
  302     val = day_val_base + (scale * day_val_delta);
  303     if (val > 255)
  304       val = 255;
  305     else
  306       assert(val >= 0);
  307       }
  308 
  309       if (scanbuf_val == PixTypeLand)
  310       {
  311     /* land (green) */
  312     rslt[0] = 0;
  313     rslt[1] = val;
  314     rslt[2] = 0;
  315       }
  316       else
  317       {
  318     /* water (blue) */
  319     rslt[0] = 0;
  320     rslt[1] = 0;
  321     rslt[2] = val;
  322       }
  323       break;
  324 
  325     default:
  326       assert(0);
  327     }
  328 
  329     rslt += 3;
  330   }
  331 }
  332 
  333 
  334 static void merc_shade_row(idx, scanbuf, sol, rslt)
  335      int     idx;
  336      s8or32 *scanbuf;
  337      double *sol;
  338      u_char *rslt;
  339 {
  340   int    i, i_lim;
  341   int    scanbuf_val;
  342   int    val;
  343   double x, y, z;
  344   double sin_theta;
  345   double cos_theta;
  346   double scale;
  347   double tmp;
  348   double y_sol_1;
  349 
  350   y = INV_YPROJECT(idx);
  351   y = INV_MERCATOR_Y(y);
  352 
  353   /* conceptually, on each iteration of the i loop, we want:
  354    *
  355    *   x = sin(INV_XPROJECT(i)) * sqrt(1 - (y*y));
  356    *   z = cos(INV_XPROJECT(i)) * sqrt(1 - (y*y));
  357    *
  358    * computing this directly is rather expensive, however, so we only
  359    * compute the first (i=0) pair of values directly; all other pairs
  360    * (i>0) are obtained through successive rotations of the original
  361    * pair (by inv_proj_scale radians).
  362    */
  363 
  364   /* compute initial (x, z) values
  365    */
  366   tmp = sqrt(1 - (y*y));
  367   x   = sin(INV_XPROJECT(0)) * tmp;
  368   z   = cos(INV_XPROJECT(0)) * tmp;
  369 
  370   /* compute rotation coefficients used
  371    * to find subsequent (x, z) values
  372    */
  373   tmp = proj_info.inv_proj_scale;
  374   sin_theta = sin(tmp);
  375   cos_theta = cos(tmp);
  376 
  377   /* save a little computation in the inner loop
  378    */
  379   y_sol_1 = y * sol[1];
  380 
  381   /* use i_lim to encourage compilers to register loop limit
  382    */
  383   i_lim = wdth;
  384   for (i=0; i<i_lim; i++)
  385   {
  386     scanbuf_val = scanbuf[i];
  387 
  388     switch (scanbuf_val)
  389     {
  390     case PixTypeSpace:        /* black */
  391       rslt[0] = 0;
  392       rslt[1] = 0;
  393       rslt[2] = 0;
  394       break;
  395 
  396     case PixTypeStar:         /* white */
  397     case PixTypeGridLand:
  398     case PixTypeGridWater:
  399       rslt[0] = 255;
  400       rslt[1] = 255;
  401       rslt[2] = 255;
  402       break;
  403 
  404     case PixTypeLand:         /* green, blue */
  405     case PixTypeWater:
  406       scale = (x * sol[0]) + y_sol_1 + (z * sol[2]);
  407       if (scale < 0)
  408       {
  409     val = night_val;
  410       }
  411       else
  412       {
  413     val = day_val_base + (scale * day_val_delta);
  414     if (val > 255)
  415       val = 255;
  416     else
  417       assert(val >= 0);
  418       }
  419 
  420       if (scanbuf_val == PixTypeLand)
  421       {
  422     /* land (green) */
  423     rslt[0] = 0;
  424     rslt[1] = val;
  425     rslt[2] = 0;
  426       }
  427       else
  428       {
  429     /* water (blue) */
  430     rslt[0] = 0;
  431     rslt[1] = 0;
  432     rslt[2] = val;
  433       }
  434       break;
  435 
  436     default:
  437       assert(0);
  438     }
  439 
  440     /* compute next (x, z) values via 2-d rotation
  441      */
  442     tmp = (cos_theta * z) - (sin_theta * x);
  443     x   = (sin_theta * z) + (cos_theta * x);
  444     z   = tmp;
  445 
  446     rslt += 3;
  447   }
  448 }
  449 
  450 
  451 static void cyl_shade_row(idx, scanbuf, sol, rslt)
  452      int     idx;
  453      s8or32 *scanbuf;
  454      double *sol;
  455      u_char *rslt;
  456 {
  457   int    i, i_lim;
  458   int    scanbuf_val;
  459   int    val;
  460   double x, y, z;
  461   double sin_theta;
  462   double cos_theta;
  463   double scale;
  464   double tmp;
  465   double y_sol_1;
  466 
  467   y = INV_YPROJECT(idx);
  468   y = INV_CYLINDRICAL_Y(y);
  469 
  470   /* conceptually, on each iteration of the i loop, we want:
  471    *
  472    *   x = sin(INV_XPROJECT(i)) * sqrt(1 - (y*y));
  473    *   z = cos(INV_XPROJECT(i)) * sqrt(1 - (y*y));
  474    *
  475    * computing this directly is rather expensive, however, so we only
  476    * compute the first (i=0) pair of values directly; all other pairs
  477    * (i>0) are obtained through successive rotations of the original
  478    * pair (by inv_proj_scale radians).
  479    */
  480 
  481   /* compute initial (x, z) values
  482    */
  483   tmp = sqrt(1 - (y*y));
  484   x   = sin(INV_XPROJECT(0)) * tmp;
  485   z   = cos(INV_XPROJECT(0)) * tmp;
  486 
  487   /* compute rotation coefficients used
  488    * to find subsequent (x, z) values
  489    */
  490   tmp = proj_info.inv_proj_scale;
  491   sin_theta = sin(tmp);
  492   cos_theta = cos(tmp);
  493 
  494   /* save a little computation in the inner loop
  495    */
  496   y_sol_1 = y * sol[1];
  497 
  498   /* use i_lim to encourage compilers to register loop limit
  499    */
  500   i_lim = wdth;
  501   for (i=0; i<i_lim; i++)
  502   {
  503     scanbuf_val = scanbuf[i];
  504 
  505     switch (scanbuf_val)
  506     {
  507     case PixTypeSpace:        /* black */
  508       rslt[0] = 0;
  509       rslt[1] = 0;
  510       rslt[2] = 0;
  511       break;
  512 
  513     case PixTypeStar:         /* white */
  514     case PixTypeGridLand:
  515     case PixTypeGridWater:
  516       rslt[0] = 255;
  517       rslt[1] = 255;
  518       rslt[2] = 255;
  519       break;
  520 
  521     case PixTypeLand:         /* green, blue */
  522     case PixTypeWater:
  523       scale = (x * sol[0]) + y_sol_1 + (z * sol[2]);
  524       if (scale < 0)
  525       {
  526     val = night_val;
  527       }
  528       else
  529       {
  530     val = day_val_base + (scale * day_val_delta);
  531     if (val > 255)
  532       val = 255;
  533     else
  534       assert(val >= 0);
  535       }
  536 
  537       if (scanbuf_val == PixTypeLand)
  538       {
  539     /* land (green) */
  540     rslt[0] = 0;
  541     rslt[1] = val;
  542     rslt[2] = 0;
  543       }
  544       else
  545       {
  546     /* water (blue) */
  547     rslt[0] = 0;
  548     rslt[1] = 0;
  549     rslt[2] = val;
  550       }
  551       break;
  552 
  553     default:
  554       assert(0);
  555     }
  556 
  557     /* compute next (x, z) values via 2-d rotation
  558      */
  559     tmp = (cos_theta * z) - (sin_theta * x);
  560     x   = (sin_theta * z) + (cos_theta * x);
  561     z   = tmp;
  562 
  563     rslt += 3;
  564   }
  565 }
  566 
  567 
  568 void render(rowfunc)
  569      int (*rowfunc) _P((u_char *));
  570 {
  571   int     i, i_lim;
  572   s8or32 *scanbuf;
  573   u_char *row;
  574   double *inv_x;
  575   double  sol[3];
  576   double  tmp;
  577 
  578   scanbuf = (s8or32 *) malloc((unsigned) (sizeof(s8or32) * wdth));
  579   row = (u_char *) malloc((unsigned) wdth*3);
  580   assert((scanbuf != NULL) && (row != NULL));
  581 
  582   inv_x = NULL;
  583   render_rows_setup();
  584 
  585   if (do_shade)
  586   {
  587     /* inv_x[] only gets used with orthographic projection
  588      */
  589     if (proj_type == ProjTypeOrthographic)
  590     {
  591       inv_x = (double *) malloc((unsigned) sizeof(double) * wdth);
  592       assert(inv_x != NULL);
  593       orth_compute_inv_x(inv_x);
  594     }
  595 
  596     compute_sun_vector(sol);
  597 
  598     /* precompute shading parameters
  599      */
  600     night_val     = night * (255.99/100.0);
  601     tmp           = terminator / 100.0;
  602     day_val_base  = ((tmp * day) + ((1-tmp) * night))  * (255.99/100.0);
  603     day_val_delta = (day * (255.99/100.0)) - day_val_base;
  604   }
  605 
  606   /* main render loop
  607    * (use i_lim to encourage compilers to register loop limit)
  608    */
  609   i_lim = hght;
  610   for (i=0; i<i_lim; i++)
  611   {
  612     render_next_row(scanbuf, i);
  613 
  614     if (!do_shade)
  615       no_shade_row(scanbuf, row);
  616     else if (proj_type == ProjTypeOrthographic)
  617       orth_shade_row(i, scanbuf, sol, inv_x, row);
  618     else if (proj_type == ProjTypeMercator)
  619       merc_shade_row(i, scanbuf, sol, row);
  620     else /* (proj_type == ProjTypeCylindrical) */
  621       cyl_shade_row(i, scanbuf, sol, row);
  622 
  623     rowfunc(row);
  624   }
  625 
  626   free(scanbuf);
  627   free(row);
  628 
  629   if (inv_x != NULL) free(inv_x);
  630 }
  631 
  632 
  633 void do_dots()
  634 {
  635   if (dots == NULL)
  636     dots = extarr_alloc(sizeof(ScanDot));
  637   else
  638     dots->count = 0;
  639 
  640   if (do_stars) new_stars(star_freq);
  641   if (do_grid) new_grid(grid_big, grid_small);
  642 
  643   qsort(dots->body, dots->count, sizeof(ScanDot), dot_comp);
  644 }
  645 
  646 
  647 static void new_stars(freq)
  648      double freq;
  649 {
  650   int      i;
  651   int      x, y;
  652   int      max_stars;
  653   ScanDot *newdot;
  654 
  655   max_stars = wdth * hght * freq;
  656 
  657   for (i=0; i<max_stars; i++)
  658   {
  659     x = random() % wdth;
  660     y = random() % hght;
  661 
  662     newdot = (ScanDot *) extarr_next(dots);
  663     newdot->x    = x;
  664     newdot->y    = y;
  665     newdot->type = DotTypeStar;
  666 
  667     if ((big_stars) && (x+1 < wdth) && ((random() % 100) < big_stars))
  668     {
  669       newdot = (ScanDot *) extarr_next(dots);
  670       newdot->x    = x+1;
  671       newdot->y    = y;
  672       newdot->type = DotTypeStar;
  673     }
  674   }
  675 }
  676 
  677 
  678 static void new_grid(big, small)
  679      int big;
  680      int small;
  681 {
  682   int    i, j;
  683   int    cnt;
  684   double lat, lon;
  685   double lat_scale, lon_scale;
  686   double cs_lat[2];
  687   double cs_lon[2];
  688 
  689   /* lines of longitude
  690    */
  691   lon_scale = M_PI / (2 * big);
  692   lat_scale = M_PI / (2 * big * small);
  693   for (i=(-2*big); i<(2*big); i++)
  694   {
  695     lon       = i * lon_scale;
  696     cs_lon[0] = cos(lon);
  697     cs_lon[1] = sin(lon);
  698 
  699     for (j=(-(big*small)+1); j<(big*small); j++)
  700     {
  701       lat       = j * lat_scale;
  702       cs_lat[0] = cos(lat);
  703       cs_lat[1] = sin(lat);
  704 
  705       new_grid_dot(cs_lat, cs_lon);
  706     }
  707   }
  708 
  709   /* lines of latitude
  710    */
  711   lat_scale = M_PI / (2 * big);
  712   for (i=(1-big); i<big; i++)
  713   {
  714     lat       = i * lat_scale;
  715     cs_lat[0] = cos(lat);
  716     cs_lat[1] = sin(lat);
  717     cnt       = 2 * ((int) ((cs_lat[0] * small) + 0.5)) * big;
  718     lon_scale = M_PI / cnt;
  719 
  720     for (j=(-cnt); j<cnt; j++)
  721     {
  722       lon       = j * lon_scale;
  723       cs_lon[0] = cos(lon);
  724       cs_lon[1] = sin(lon);
  725 
  726       new_grid_dot(cs_lat, cs_lon);
  727     }
  728   }
  729 }
  730 
  731 
  732 static void new_grid_dot(cs_lat, cs_lon)
  733      double *cs_lat;
  734      double *cs_lon;
  735 {
  736   int      x, y;
  737   double   pos[3];
  738   ScanDot *new;
  739 
  740   pos[0] = cs_lon[1] * cs_lat[0];
  741   pos[1] = cs_lat[1];
  742   pos[2] = cs_lon[0] * cs_lat[0];
  743 
  744   XFORM_ROTATE(pos, view_pos_info);
  745 
  746   if (proj_type == ProjTypeOrthographic)
  747   {
  748     /* if the grid dot isn't visible, return immediately
  749      */
  750     if (pos[2] <= 0) return;
  751   }
  752   else if (proj_type == ProjTypeMercator)
  753   {
  754     /* apply mercator projection
  755      */
  756     pos[0] = MERCATOR_X(pos[0], pos[2]);
  757     pos[1] = MERCATOR_Y(pos[1]);
  758   }
  759   else /* (proj_type == ProjTypeCylindrical) */
  760   {
  761     /* apply cylindrical projection
  762      */
  763     pos[0] = CYLINDRICAL_X(pos[0], pos[2]);
  764     pos[1] = CYLINDRICAL_Y(pos[1]);
  765   }
  766 
  767   x = XPROJECT(pos[0]);
  768   y = YPROJECT(pos[1]);
  769 
  770   if ((x >= 0) && (x < wdth) && (y >= 0) && (y < hght))
  771   {
  772     new = (ScanDot *) extarr_next(dots);
  773     new->x    = x;
  774     new->y    = y;
  775     new->type = DotTypeGrid;
  776   }
  777 }