home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / sun / volume1 / sunclock / sunclock.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-10-26  |  19.4 KB  |  827 lines

  1. /*
  2.  
  3.     Sun clock
  4.  
  5.     Designed and implemented by John Walker in November of 1988.
  6.  
  7.     Version for the Sun Workstation.
  8.  
  9.     The algorithm used to calculate the position of the Sun is given in
  10.     Chapter 18 of:
  11.  
  12.     "Astronomical  Formulae for Calculators" by Jean Meeus, Third Edition,
  13.     Richmond: Willmann-Bell, 1985.  This book can be obtained from:
  14.  
  15.        Willmann-Bell
  16.        P.O. Box 35025
  17.        Richmond, VA  23235
  18.        USA
  19.        Phone: (804) 320-7016
  20.  
  21.     This program was written by:
  22.  
  23.        John Walker
  24.        Autodesk, Inc.
  25.        2320 Marinship Way
  26.        Sausalito, CA  94965
  27.        USA
  28.        Fax:   (415) 389-9418
  29.        Voice: (415) 332-2344 Ext. 2829
  30.        Usenet: {sun,well,uunet}!acad!kelvin
  31.        or: kelvin@acad.uu.net
  32.  
  33.     This  program is in the public domain: "Do what thou wilt shall be the
  34.     whole of the law".  I'd appreciate  receiving  any  bug  fixes  and/or
  35.     enhancements,  which  I'll  incorporate  in  future  versions  of  the
  36.     program.  Please leave the original attribution information intact    so
  37.     that credit and blame may be properly apportioned.
  38.  
  39.     Revision history:
  40.  
  41.     1.0  12/21/89  Initial version.
  42.           8/24/89  Finally got around to submitting.
  43.  
  44. */
  45.  
  46. #include <stdio.h>
  47. #include <ctype.h>
  48. #include <math.h>
  49. #include <sys/types.h>
  50. #include <sys/timeb.h>
  51. #include <assert.h>
  52.  
  53. #include <suntool/sunview.h>
  54. #include <suntool/canvas.h>
  55. #include <suntool/scrollbar.h>
  56. #include <pixrect/pixrect_hs.h>
  57.  
  58. #define abs(x) ((x) < 0 ? (-(x)) : x)              /* Absolute value */
  59. #define sgn(x) (((x) < 0) ? -1 : ((x) > 0 ? 1 : 0))      /* Extract sign */
  60. #define dtr(x) ((x) * (PI / 180.0))              /* Degree->Radian */
  61. #define rtd(x) ((x) / (PI / 180.0))              /* Radian->Degree */
  62. #define fixangle(a) ((a) - 360.0 * (floor((a) / 360.0)))  /* Fix angle      */
  63.  
  64. #define V      (void)
  65.  
  66. #define PI 3.14159265358979323846
  67.  
  68. #define TERMINC  100           /* Circle segments for terminator */
  69.  
  70. #define PROJINT  (60 * 10)       /* Frequency of seasonal recalculation
  71.                       in seconds. */
  72.  
  73. #define CXDOTS     126           /* Closed window width */
  74. #define CYDOTS     63           /* Closed window height */
  75.  
  76. #define IXDOTS     128           /* Total icon width */
  77. #define IYDOTS     74           /* Total icon height */
  78.  
  79. #define OXDOTS     640           /* Open window width */
  80. #define OYDOTS     320           /* Open window height */
  81.  
  82. /*  Globals imported  */
  83.  
  84. extern char *getenv(), *timezone(), *malloc(), *sprintf();
  85. extern time_t time();
  86. #ifdef lint
  87. extern void pw_batch();
  88. #endif
  89.  
  90. /*  Local variables  */
  91.  
  92. static int xdots, ydots;       /* Screen size */
  93. #include "sunclock.h"              /* Icon and open window bitmaps */
  94. static char *wdname[] = {       /* Week day names */
  95.         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  96. };
  97. static    Frame    bf;
  98. static    Pixwin    *fpw, *cpw, *apw;
  99. static    Pixfont *tinyfont;
  100. static    Pixfont *regfont;
  101.  
  102. static int onoon = -1;
  103. static short *wtab, *wtab1, *wtabs;
  104. static struct timeb tbtp;
  105.  
  106. static struct itimerval notif_timer = {{1, 0}, {1, 0}};
  107. static struct itimerval quick_timer = {{0, 500000}, {0, 500000}};
  108. static Menu actmenu;
  109.  
  110. static int fdate = FALSE, idir = 1, animate = FALSE;
  111. static long lincr = 60;
  112. static long cctime;
  113.  
  114. /*  Forward procedures    */
  115.  
  116. double jtime(), gmst();
  117. void drawterm(), sunpos(), projillum(), moveterm(), outdiff(), usage();
  118.  
  119. /*  UPDIMAGE  --  Update current displayed image.  */
  120.  
  121. static void updimage(istimer)
  122. int istimer;
  123. {
  124.     int i, xl, isclosed;
  125.     struct tm *ct;
  126.     char tbuf[80];
  127.     double jt, sunra, sundec, sunrv, sunlong, gt;
  128.     struct tm lt;
  129.     static int lisec = 61;       /* Last iconic seconds */
  130.     static long lctime = 0;    /* Last full calculation time */
  131.  
  132.     isclosed = (int) window_get(bf, FRAME_CLOSED);
  133.  
  134.     if (!istimer) {
  135.        if (isclosed) {
  136.           xdots = CXDOTS;
  137.           ydots = CYDOTS;
  138.  
  139.           V pw_writebackground(fpw, 0, 0, IXDOTS, IYDOTS, PIX_SRC);
  140.           V pw_vector(fpw, 0, 0, IXDOTS - 1, 0, PIX_SRC, 1);
  141.           V pw_vector(fpw, IXDOTS - 1, 0, IXDOTS - 1,
  142.                    IYDOTS - 1, PIX_SRC, 1);
  143.           V pw_vector(fpw, IXDOTS - 1, IYDOTS - 1, 0,
  144.                    IYDOTS - 1, PIX_SRC, 1);
  145.           V pw_vector(fpw, 0, IYDOTS - 1, 0, 0, PIX_SRC, 1);
  146.           V pw_rop(fpw, 1, 1, micon.pr_size.x, micon.pr_size.y,
  147.             PIX_NOT(PIX_SRC),
  148.             &micon, 0, 0);
  149.        } else {
  150.           xdots = OXDOTS;
  151.           ydots = OYDOTS;
  152.  
  153.           V pw_rop(cpw, 0, 0, bimage.pr_size.x, bimage.pr_size.y,
  154.             PIX_NOT(PIX_SRC),
  155.             &bimage, 0, 0);
  156.        }
  157.     }
  158.  
  159.     /* If this is a full repaint of the window, force complete
  160.        recalculation. */
  161.  
  162.     if (!istimer) {
  163.        lctime = 0;
  164.        onoon = -1;
  165.        lisec = 61;
  166.        for (i = 0; i < OYDOTS; i++) {
  167.           wtab1[i] = -1;
  168.        }
  169.     }
  170.  
  171.     if (fdate) {
  172.        if (animate)
  173.           cctime += lincr;
  174.        if (cctime < 0)
  175.           cctime = 0;
  176.     } else {
  177.        V time(&cctime);
  178.     }
  179.     lt = *localtime(&cctime);
  180.  
  181.         /* Special  case  to  reduce overhead  whilst iconic: if we're
  182.        only showing the icon, update the  display  only  once  per
  183.        minute,  detected  by  the  fact  that  the current seconds
  184.        reading is less than that of the  last  update.   The  icon
  185.        shows  only    hours  and  minutes, and is sufficiently small
  186.        that once-a-minute updates are plenty to keep  the  picture
  187.        in sync.  */
  188.  
  189.     if (isclosed && !fdate && (lt.tm_sec > lisec))
  190.        return;
  191.  
  192.     ct = gmtime(&cctime);
  193.  
  194.     jt = jtime(ct);
  195.     sunpos(jt, FALSE, &sunra, &sundec, &sunrv, &sunlong);
  196.     gt = gmst(jt);
  197.  
  198.     /* Projecting the illumination curve  for the current seasonal
  199.            instant is costly.  If we're running in real time, only  do
  200.        it every PROJINT seconds.  */
  201.  
  202.     if (fdate || !istimer || ((cctime - lctime) > PROJINT)) {
  203.        projillum(wtab, xdots, ydots, sundec);
  204.        wtabs = wtab;
  205.        wtab = wtab1;
  206.        wtab1 = wtabs;
  207.        lctime = cctime;
  208.     }
  209.  
  210.     sunlong = fixangle(180.0 + (sunra - (gt * 15)));
  211.     xl = sunlong * (xdots / 360.0);
  212.  
  213.     /* If the subsolar point has moved at least one pixel, update
  214.        the illuminated area on the screen.    */
  215.  
  216.     if (fdate || !istimer || (onoon != xl)) {
  217.        apw = isclosed ? fpw : cpw;
  218.        pw_batch_on(apw);
  219.        moveterm(wtab1, xl, wtab, onoon, xdots, ydots);
  220.        pw_batch_off(apw);
  221.        onoon = xl;
  222.     }
  223.  
  224.     if (isclosed) {
  225.  
  226.        /* Display time in closed window */
  227.  
  228.            V sprintf(tbuf, "%02d:%02d %s %s %02d/%02d",
  229.           lt.tm_hour, lt.tm_min,
  230.           timezone(tbtp.timezone, lt.tm_isdst),
  231.           wdname[lt.tm_wday],
  232.           lt.tm_mon + 1, lt.tm_mday);
  233.        V pw_text(fpw, 8, IYDOTS - 3, PIX_SRC, tinyfont, tbuf);
  234.        lisec = lt.tm_sec;
  235.  
  236.     } else {
  237.  
  238.        /* Display time in open window */
  239.  
  240.        V sprintf(tbuf,
  241.   " %02d:%02d:%02d %s %s %02d/%02d/%02d    %02d:%02d:%02d UTC %02d/%02d/%02d ",
  242.           lt.tm_hour, lt.tm_min, lt.tm_sec,
  243.           timezone(tbtp.timezone, lt.tm_isdst),
  244.           wdname[lt.tm_wday],
  245.           lt.tm_mon + 1, lt.tm_mday, (lt.tm_year % 100),
  246.           ct->tm_hour, ct->tm_min, ct->tm_sec,
  247.           ct->tm_mon + 1, ct->tm_mday, (ct->tm_year % 100));
  248.        V pw_text(cpw, 85, ydots - 6, PIX_SRC, regfont, tbuf);
  249.     }
  250. }
  251.  
  252. /*  Frame event processor  */
  253.  
  254. static frame_event_proc(frame, event, arg, type)
  255. Frame frame;
  256. Event *event;
  257. Notify_arg arg;
  258. Notify_event_type type;
  259. {
  260.     switch (event_id(event)) {
  261.  
  262.        case WIN_REPAINT:
  263.           if (window_get(bf, FRAME_CLOSED)) {
  264.          updimage(FALSE);
  265.           } else {
  266.          xdots = OXDOTS;
  267.          ydots = OYDOTS;
  268.  
  269.          updimage(FALSE);
  270.           }
  271.           break;
  272.  
  273.        default:
  274.           window_default_event_func(frame, event, arg, type);
  275.           break;
  276.     }
  277. }
  278.  
  279. /*  Timer notification procedure.  */
  280.  
  281. /*ARGSUSED*/
  282. static Notify_value timer_proc(frame, which)
  283. Notify_client frame;
  284. int which;
  285. {
  286.     updimage(TRUE);
  287.     return(NOTIFY_DONE);
  288. }
  289.  
  290. /*  CEVENT  --    Canvas event handler  */
  291.  
  292. /*ARGSUSED*/
  293. static void cevent(window, event, arg)
  294. Window window;
  295. Event *event;
  296. caddr_t arg;
  297. {
  298. #define mdis(x,y) V menu_set(menu_get(actmenu,MENU_NTH_ITEM,x),\
  299.                  MENU_INACTIVE,y,0)
  300.  
  301.     mdis(1, idir > 0);
  302.     mdis(2, idir < 0);
  303.     mdis(11, !fdate);
  304.  
  305.     switch (event_id(event)) {
  306.  
  307.        case MS_RIGHT:
  308.           switch (menu_show(actmenu, window, event, 0)) {
  309.  
  310.          case 1:       /* Forward */
  311.             if (idir < 0)
  312.                lincr = -lincr;
  313.             idir = 1;
  314.             break;
  315.  
  316.          case 2:       /* Backward */
  317.             if (idir > 0)
  318.                lincr = -lincr;
  319.             idir = -1;
  320.             break;
  321.  
  322.          case 4:       /* Hour */
  323.             cctime += (lincr = 3600L * idir);
  324.             fdate = TRUE;
  325.             break;
  326.  
  327.          case 5:       /* Day */
  328.             cctime += (lincr = 86400L * idir);
  329.             fdate = TRUE;
  330.             break;
  331.  
  332.          case 6:       /* Week */
  333.             cctime += (lincr = 86400L * 7 * idir);
  334.             fdate = TRUE;
  335.             break;
  336.  
  337.          case 7:       /* Month */
  338.             cctime += (lincr = 86400L * 30 * idir);
  339.             fdate = TRUE;
  340.             break;
  341.  
  342.          case 8:       /* Year */
  343.             cctime += (lincr = 86400L * 365L * idir);
  344.             fdate = TRUE;
  345.             break;
  346.  
  347.          case 10:       /* Animate */
  348.             animate = fdate = TRUE;
  349.             V notify_set_itimer_func(bf, timer_proc, ITIMER_REAL,
  350.               &quick_timer, (struct itimerval *) NULL);
  351.             break;
  352.  
  353.          case 11:       /* Real time */
  354.             animate = fdate = FALSE;
  355.             V notify_set_itimer_func(bf, timer_proc, ITIMER_REAL,
  356.               ¬if_timer, (struct itimerval *) NULL);
  357.             updimage(FALSE);
  358.             break;
  359.  
  360.          case 13:       /* Quit */
  361.             V window_done(window);
  362.             break;
  363.           }
  364.     }
  365. }
  366.  
  367. /*  MAIN  --  Main program  */
  368.  
  369. void main(argc, argv)
  370. int argc;
  371. char *argv[];
  372. {
  373.     int i;
  374.     char *op, opt;
  375.     Icon icon;
  376.     Canvas canvas;
  377.  
  378.         if (getenv("WINDOW_PARENT") == NULL) {
  379.            V fprintf(stderr, "%s must be run from within Suntools\n", argv[0]);
  380.        exit(1);
  381.     }
  382.  
  383.     /*  Process command line options.  */
  384.  
  385.     for (i = 1; i < argc; i++) {
  386.        op = argv[i];
  387.            if (*op == '-') {
  388.           opt = *(++op);
  389.           if (islower(opt))
  390.          opt = toupper(opt);
  391.           switch (opt) {
  392.                  case 'U':
  393.                  case '?':
  394.             usage();
  395.             exit(0);
  396.           }
  397.        }
  398.     }
  399.  
  400.     icon = icon_create(ICON_WIDTH, IXDOTS,
  401.                   ICON_HEIGHT, IYDOTS, ICON_LABEL, "", 0);
  402.     bf = window_create((Window) NULL, FRAME,
  403.                   FRAME_LABEL,
  404.            "Sun Clock          by John Walker, Autodesk, Inc.       v1.0",
  405.                   FRAME_NO_CONFIRM, TRUE,
  406.                   FRAME_ARGC_PTR_ARGV, &argc, argv,
  407.                   FRAME_ICON, icon,
  408.                   FRAME_CLOSED, TRUE,
  409.                   WIN_EVENT_PROC, frame_event_proc,
  410.                0);
  411.     canvas = window_create(bf, CANVAS,
  412.                    WIN_EVENT_PROC, cevent,
  413.                    0);
  414.  
  415.     V window_set(bf,
  416.        WIN_CONSUME_PICK_EVENTS,
  417.           WIN_NO_EVENTS, WIN_MOUSE_BUTTONS, LOC_DRAG, 0,
  418.        WIN_CONSUME_KBD_EVENT, WIN_LEFT_KEYS,
  419.        0);
  420.  
  421.     V window_set(canvas,
  422.        CANVAS_AUTO_EXPAND, FALSE,
  423.        CANVAS_AUTO_SHRINK, FALSE,
  424.        CANVAS_HEIGHT, OYDOTS,
  425.        CANVAS_WIDTH, OXDOTS,
  426.        WIN_HEIGHT, OYDOTS,
  427.        WIN_WIDTH, OXDOTS,
  428.        0);
  429.  
  430.     window_fit(canvas);
  431.  
  432.     window_fit(bf);
  433.     fpw = (Pixwin *) window_get(bf, WIN_PIXWIN);
  434.     cpw = canvas_pixwin(canvas);
  435.  
  436.         tinyfont = pf_open("/usr/lib/fonts/fixedwidthfonts/screen.r.7");
  437.         regfont = pf_open("/usr/lib/fonts/fixedwidthfonts/screen.b.14");
  438.  
  439.     actmenu = menu_create(MENU_STRINGS,
  440.            "Forward",
  441.            "Backward",
  442.            "",
  443.            "Hour",
  444.            "Day",
  445.            "Week",
  446.            "Month",
  447.            "Year",
  448.            "",
  449.            "Animate",
  450.            "Real time",
  451.            "",
  452.            "Quit",
  453.        0, 0);
  454.  
  455.     mdis(3, TRUE);           /* Disable blank slots in menu */
  456.     mdis(9, TRUE);
  457.     mdis(12, TRUE);
  458.  
  459.     xdots = OXDOTS;
  460.     ydots = OYDOTS;
  461.  
  462.     wtab = (short *) malloc((unsigned int) ydots * sizeof(short));
  463.     wtab1 = (short *) malloc((unsigned int) ydots * sizeof(short));
  464.  
  465.  
  466.     ftime(&tbtp);
  467.  
  468.     V notify_set_itimer_func(bf, timer_proc, ITIMER_REAL,
  469.           ¬if_timer, (struct itimerval *) NULL);
  470.     window_main_loop(bf);
  471.  
  472.     exit(0);
  473. }
  474.  
  475. /*  JDATE  --  Convert internal GMT date and time to Julian day
  476.            and fraction.  */
  477.  
  478. static long jdate(t)
  479. struct tm *t;
  480. {
  481.     long c, m, y;
  482.  
  483.     y = t->tm_year + 1900;
  484.     m = t->tm_mon + 1;
  485.     if (m > 2)
  486.        m = m - 3;
  487.     else {
  488.        m = m + 9;
  489.        y--;
  490.     }
  491.     c = y / 100L;           /* Compute century */
  492.     y -= 100L * c;
  493.     return t->tm_mday + (c * 146097L) / 4 + (y * 1461L) / 4 +
  494.         (m * 153L + 2) / 5 + 1721119L;
  495. }
  496.  
  497. /* JTIME --    Convert internal GMT  date  and    time  to  astronomical
  498.            Julian  time  (i.e.   Julian  date  plus  day fraction,
  499.            expressed as a double).    */
  500.  
  501. static double jtime(t)
  502. struct tm *t;
  503. {
  504.     return (jdate(t) - 0.5) + 
  505.        (((long) t->tm_sec) +
  506.          60L * (t->tm_min + 60L * t->tm_hour)) / 86400.0;
  507. }
  508.  
  509. /*  KEPLER  --    Solve the equation of Kepler.  */
  510.  
  511. static double kepler(m, ecc)
  512. double m, ecc;
  513. {
  514.     double e, delta;
  515. #define EPSILON 1E-6
  516.  
  517.     e = m = dtr(m);
  518.     do {
  519.        delta = e - ecc * sin(e) - m;
  520.        e -= delta / (1 - ecc * cos(e));
  521.     } while (abs(delta) > EPSILON);
  522.     return e;
  523. }
  524.  
  525. /*  SUNPOS  --    Calculate position of the Sun.    JD is the Julian  date
  526.         of  the  instant for which the position is desired and
  527.         APPARENT should be nonzero if  the  apparent  position
  528.         (corrected  for  nutation  and aberration) is desired.
  529.                 The Sun's co-ordinates are returned  in  RA  and  DEC,
  530.         both  specified  in degrees (divide RA by 15 to obtain
  531.         hours).  The radius vector to the Sun in  astronomical
  532.                 units  is returned in RV and the Sun's longitude (true
  533.         or apparent, as desired) is  returned  as  degrees  in
  534.         SLONG.    */
  535.  
  536. static void sunpos(jd, apparent, ra, dec, rv, slong)
  537. double jd;
  538. int apparent;
  539. double *ra, *dec, *rv, *slong;
  540. {
  541.     double t, t2, t3, l, m, e, ea, v, theta, omega,
  542.            eps;
  543.  
  544.     /* Time, in Julian centuries of 36525 ephemeris days,
  545.        measured from the epoch 1900 January 0.5 ET. */
  546.  
  547.     t = (jd - 2415020.0) / 36525.0;
  548.     t2 = t * t;
  549.     t3 = t2 * t;
  550.  
  551.     /* Geometric mean longitude of the Sun, referred to the
  552.        mean equinox of the date. */
  553.  
  554.     l = fixangle(279.69668 + 36000.76892 * t + 0.0003025 * t2);
  555.  
  556.         /* Sun's mean anomaly. */
  557.  
  558.     m = fixangle(358.47583 + 35999.04975*t - 0.000150*t2 - 0.0000033*t3);
  559.  
  560.         /* Eccentricity of the Earth's orbit. */
  561.  
  562.     e = 0.01675104 - 0.0000418 * t - 0.000000126 * t2;
  563.  
  564.     /* Eccentric anomaly. */
  565.  
  566.     ea = kepler(m, e);
  567.  
  568.     /* True anomaly */
  569.  
  570.     v = fixangle(2 * rtd(atan(sqrt((1 + e) / (1 - e))  * tan(ea / 2))));
  571.  
  572.         /* Sun's true longitude. */
  573.  
  574.     theta = l + v - m;
  575.  
  576.     /* Obliquity of the ecliptic. */
  577.  
  578.     eps = 23.452294 - 0.0130125 * t - 0.00000164 * t2 + 0.000000503 * t3;
  579.  
  580.         /* Corrections for Sun's apparent longitude, if desired. */
  581.  
  582.     if (apparent) {
  583.        omega = fixangle(259.18 - 1934.142 * t);
  584.        theta = theta - 0.00569 - 0.00479 * sin(dtr(omega));
  585.        eps += 0.00256 * cos(dtr(omega));
  586.     }
  587.  
  588.         /* Return Sun's longitude and radius vector */
  589.  
  590.     *slong = theta;
  591.     *rv = (1.0000002 * (1 - e * e)) / (1 + e * cos(dtr(v)));
  592.  
  593.     /* Determine solar co-ordinates. */
  594.  
  595.     *ra =
  596.     fixangle(rtd(atan2(cos(dtr(eps)) * sin(dtr(theta)), cos(dtr(theta)))));
  597.     *dec = rtd(asin(sin(dtr(eps)) * sin(dtr(theta))));
  598. }
  599.  
  600. /*  GMST  --  Calculate Greenwich Mean Siderial Time for a given
  601.           instant expressed as a Julian date and fraction.    */
  602.  
  603. static double gmst(jd)
  604. double jd;
  605. {
  606.     double t, theta0;
  607.  
  608.  
  609.     /* Time, in Julian centuries of 36525 ephemeris days,
  610.        measured from the epoch 1900 January 0.5 ET. */
  611.  
  612.     t = ((floor(jd + 0.5) - 0.5) - 2415020.0) / 36525.0;
  613.  
  614.     theta0 = 6.6460656 + 2400.051262 * t + 0.00002581 * t * t;
  615.  
  616.     t = (jd + 0.5) - (floor(jd + 0.5));
  617.  
  618.     theta0 += (t * 24.0) * 1.002737908;
  619.  
  620.     theta0 = (theta0 - 24.0 * (floor(theta0 / 24.0)));
  621.  
  622.     return theta0;
  623. }
  624.  
  625. /*  PROJILLUM  --  Project illuminated area on the map.  */
  626.  
  627. static void projillum(wtab, xdots, ydots, dec)
  628. short *wtab;
  629. int xdots, ydots;
  630. double dec;
  631. {
  632.     int i, ftf = TRUE, ilon, ilat, lilon, lilat, xt;
  633.     double m, x, y, z, th, lon, lat, s, c;
  634.  
  635.     /* Clear unoccupied cells in width table */
  636.  
  637.     for (i = 0; i < ydots; i++)
  638.        wtab[i] = -1;
  639.  
  640.     /* Build transformation for declination */
  641.  
  642.     s = sin(-dtr(dec));
  643.     c = cos(-dtr(dec));
  644.  
  645.     /* Increment over a semicircle of illumination */
  646.  
  647.     for (th = -(PI / 2); th <= PI / 2 + 0.001;
  648.          th += PI / TERMINC) {
  649.  
  650.        /* Transform the point through the declination rotation. */
  651.  
  652.        x = -s * sin(th);
  653.        y = cos(th);
  654.        z = c * sin(th);
  655.  
  656.        /* Transform the resulting co-ordinate through the
  657.           map projection to obtain screen co-ordinates. */
  658.  
  659.        lon = (y == 0 && x == 0) ? 0.0 : rtd(atan2(y, x));
  660.        lat = rtd(asin(z));
  661.  
  662.        ilat = ydots - (lat + 90) * (ydots / 180.0);
  663.        ilon = lon * (xdots / 360.0);
  664.  
  665.        if (ftf) {
  666.  
  667.           /* First time.  Just save start co-ordinate. */
  668.  
  669.           lilon = ilon;
  670.           lilat = ilat;
  671.           ftf = FALSE;
  672.        } else {
  673.  
  674.           /* Trace out the line and set the width table. */
  675.  
  676.           if (lilat == ilat) {
  677.          wtab[(ydots - 1) - ilat] = ilon == 0 ? 1 : ilon;
  678.           } else {
  679.          m = ((double) (ilon - lilon)) / (ilat - lilat);
  680.          for (i = lilat; i != ilat; i += sgn(ilat - lilat)) {
  681.             xt = lilon + floor((m * (i - lilat)) + 0.5);
  682.             wtab[(ydots - 1) - i] = xt == 0 ? 1 : xt;
  683.          }
  684.           }
  685.           lilon = ilon;
  686.           lilat = ilat;
  687.        }
  688.     }
  689.  
  690.     /* Now tweak the widths to generate full illumination for
  691.        the correct pole. */
  692.  
  693.     if (dec < 0.0) {
  694.        ilat = ydots - 1;
  695.        lilat = -1;
  696.     } else {
  697.        ilat = 0;
  698.        lilat = 1;
  699.     }
  700.  
  701.     for (i = ilat; i != ydots / 2; i += lilat) {
  702.        if (wtab[i] != -1) {
  703.           while (TRUE) {
  704.          wtab[i] = xdots / 2;
  705.          if (i == ilat)
  706.             break;
  707.          i -= lilat;
  708.           }
  709.           break;
  710.        }
  711.     }
  712. }
  713.  
  714. /*  XSPAN  --  Complement a span of pixels.  Called with line in which
  715.            pixels are contained, leftmost pixel in the  line,  and
  716.            the   number   of   pixels   to     complement.   Handles
  717.            wrap-around at the right edge of the screen.  */
  718.  
  719. static void xspan(pline, leftp, npix)
  720. int pline, leftp, npix;
  721. {
  722.     int xo, yo;
  723.  
  724.     if (window_get(bf, FRAME_CLOSED)) {
  725.        xo = 1;
  726.        yo = 1;
  727.     } else {
  728.        xo = yo = 0;
  729.     }
  730.  
  731.     leftp = leftp % xdots;
  732.     pline += yo;
  733.  
  734.     if ((leftp + npix) > xdots) {
  735.        V pw_vector(apw, leftp + xo, pline, xo + (xdots - 1), pline,
  736.          PIX_SRC ^ PIX_DST, 1);
  737.        V pw_vector(apw, xo, pline, xo + ((leftp + npix) - (xdots + 1)),
  738.          pline, PIX_SRC ^ PIX_DST, 1);
  739.     } else {
  740.        V pw_vector(apw, xo + leftp, pline, xo + leftp + (npix - 1), pline,
  741.          PIX_SRC ^ PIX_DST, 1);
  742.     }
  743. }
  744.  
  745. /*  MOVETERM  --  Update illuminated portion of the globe.  */
  746.  
  747. static void moveterm(wtab, noon, otab, onoon, xdots, ydots)
  748. short *wtab, *otab;
  749. int noon, onoon, xdots, ydots;
  750. {
  751.     int i, ol, oh, nl, nh;
  752.  
  753.     for (i = 0; i < ydots; i++) {
  754.  
  755.        /* If line is off in new width table but is set in
  756.           the old table, clear it. */
  757.  
  758.        if (wtab[i] < 0) {
  759.           if (otab[i] >= 0) {
  760.          xspan(i, ((onoon - otab[i]) + xdots) % xdots,
  761.             otab[i] * 2);
  762.           }
  763.        } else {
  764.  
  765.           /* Line is on in new width table.  If it was off in
  766.          the old width table, just draw it. */
  767.  
  768.           if (otab[i] < 0) {
  769.          xspan(i, ((noon - wtab[i]) + xdots) % xdots,
  770.             wtab[i] * 2);
  771.           } else {
  772.  
  773.          /* If both the old and new spans were the entire
  774.                     screen, they're equivalent. */
  775.  
  776.          if ((otab[i] == wtab[i]) && (wtab[i] == (xdots / 2)))
  777.             continue;
  778.  
  779.          /* The line was on in both the old and new width
  780.             tables.  We must adjust the difference in the
  781.             span.  */
  782.  
  783.          ol =  ((onoon - otab[i]) + xdots) % xdots;
  784.          oh = (ol + otab[i] * 2) - 1;
  785.          nl =  ((noon - wtab[i]) + xdots) % xdots;
  786.          nh = (nl + wtab[i] * 2) - 1;
  787.  
  788.          /* If spans are disjoint, erase old span and set
  789.             new span. */
  790.  
  791.          if (oh < nl || nh < ol) {
  792.             xspan(i, ol, (oh - ol) + 1);
  793.             xspan(i, nl, (nh - nl) + 1);
  794.          } else {
  795.             /* Clear portion(s) of old span that extend
  796.                beyond end of new span. */
  797.             if (ol < nl) {
  798.                xspan(i, ol, nl - ol);
  799.                ol = nl;
  800.             }
  801.             if (oh > nh) {
  802.                xspan(i, nh + 1, oh - nh);
  803.                oh = nh;
  804.             }
  805.             /* Extend existing (possibly trimmed) span to
  806.                correct new length. */
  807.             if (nl < ol) {
  808.                xspan(i, nl, ol - nl);
  809.             }
  810.             if (nh > oh) {
  811.                xspan(i, oh + 1, nh - oh);
  812.             }
  813.          }
  814.           }
  815.        }
  816.        otab[i] = wtab[i];
  817.     }
  818. }
  819.  
  820. /*  USAGE  --  Print how-to-call information.  */
  821.  
  822. static void usage()
  823. {
  824.     V fprintf(stderr,
  825.            "sunclock  --  The Sun clock.  By John Walker, Autodesk, Inc.\n");
  826. }
  827.