mkg@lzaz.UUCP (Marsh Gosnell) (12/21/85)
Here's a new version of earthplot. For those who missed it, it is a program that plots an outline of the earth given the latitute, longitude, and altitude. I really liked the program but had problems with it's performance (5-6 minutes to draw the earth). The new version is vastly improved. The plot is completed in about 35 seconds. How did I do it? I got rid of the unnecessary use of floating point and substituted integer arithmetic with implied decimal places (1.0 is represented as 10000). Accuracy is good enough so that out of 7000 data points, only 400 are off by no more than one pixel. I also incorporated the data file into a code segment so the disk i/o involved in reading the data is also eliminated. A binhex'ed version of the new program is posted as a separate article so you won't have to compile this to enjoy it. Enjoy!! Marsh Gosnell ihnp4!lzma!mkg /** ** EarthPlot -- a program to draw the earth as viewed from space. ** ** Written by: Michael Peirce ** 1258 Manet Drive ** Sunnyvale, CA 94087 ** ** Significant performance improvements by: ** Marsh Gosnell ** 35 Godfrey Road ** Montclair, NJ 07043 ** ** ...in Megamax C (version 2.1) w/ the SimpleTools package ** ** Based on Microplot program written in Fortran by Richard Heurtley ** (This can be found in file B900:MICROPLOT.FTN on the MTS system ** at Rensselear Polytechnic Institute in Troy, New York) ** ** Who When What ** === =========== ==================================== ** mrp 01-dec-1985 Final work done for version 1.0 ** mkg 20-dec-1985 Performance improvements **/ /** ** The performance improvments are: ** ** - use of integer arithmetic where possible. All values are small ** enough so that using integer arithmetic with 4 implied decimal ** places (e.g., multiplying by 10000) yields sufficient accuracy. ** (Out of 7000 displayed points, 400 are off by 1 pixel). ** ** - data is kept in a separate code segment. avoids disk i/o ** ** - new lmul routine. The Megamax lmul routine always does things the ** hard way even if the two long values are shorts. The new lmul will ** lmul will run really fast if the two values are shorts (which they ** usually are in this case). **/ /** ** Because Earthplot is in such a state (and because I did it ** primarily as a learning experience) both source and executable ** are being placed into the public domain. I encourage anyone to ** pick up on this start and expand on it's theme. All I ask is ** that if anyone does improve on this program them send the ** results back to me (either via the net or other means). ** ** The following is a list of possible extensions: ** ** - support cut & paste to the clipboard ** ** - support resizing the drawing window to allow various sizes ** of earth plots ** ** - save plots directly into a file (a save option) ** ** - either a scripting facility or a batch runable version so ** that a large number of plots can be generated without human ** interaction. This would allow one to either feed these plots ** into something like Videoworks to create a "real-time" rotation ** of the earth or a special "player" program that would do this. ** (Note: this has already been done for my roommate's IBM-PC clone. ** Come on MacFolks, we can do it better than THEM). ** ** - compress the data (from ASCII into binary data) ** ** - put the data into (a) the data fork of the executable or (b) a ** a resource (chunked to work on 128k machines?) ** ** - improve on hidden line and/or ploting algorithm... ** ** - add a floating point chip to the Mac!!!!! **/ #include <qd.h> #include <font.h> #include <qdvars.h> #include <misc.h> #include <win.h> #include <string.h> #include <stdio.h> #include <fmath.h> #include <control.h> #include <event.h> #include <res.h> /* * Defines for simpletools */ #define itemdisable 0L #define itemenable 1L #define itemcheck 2L #define itemuncheck 3L #define watchcursor 4 #define inbutton 10 #define inupbutton 20 #define indownbutton 21 #define inpageup 22 #define inpagedown 23 #define inthumb 129 /* * Define the coords for all my rectangles here */ #define info_1 12 #define info_2 25 #define info_3 175 #define info_4 329 #define icon_1 10 #define icon_2 15 #define icon_3 42 #define icon_4 47 #define north_1 5 #define north_2 104 #define north_3 74 #define north_4 124 #define south_1 75 #define south_2 104 #define south_3 145 #define south_4 124 #define east_1 5 #define east_2 184 #define east_3 74 #define east_4 204 #define west_1 75 #define west_2 184 #define west_3 145 #define west_4 204 #define mi_1 5 #define mi_2 264 #define mi_3 65 #define mi_4 284 #define km_1 66 #define km_2 264 #define km_3 175 #define km_4 284 #define lat_sb_1 -1 #define lat_sb_2 128 #define lat_sb_3 164 #define lat_sb_4 144 #define lon_sb_1 -1 #define lon_sb_2 208 #define lon_sb_3 164 #define lon_sb_4 224 #define alt_sb_1 -1 #define alt_sb_2 289 #define alt_sb_3 164 #define alt_sb_4 305 #define earth_1 195 #define earth_2 25 #define earth_3 499 #define earth_4 329 #define alt_scale 1000 /* * The resources I use */ #define icon_id 512 /* * Some usefull constants */ #define diam 7926.0 #define pi 3.141592654 #define pi100 0.03141592654 /* pi / 100 */ #define conv 0.017453293 /* pi / 180 */ #define xsize 300 /* size of window */ #define ysize 300 #define half_xsize 150 #define half_ysize 150 #define fudge 10000 /* real fudge so we can shift to divide rather than use expensive ldiv */ #define fudge2 20078 #define bitmap_size xsize * ysize / 8 extern char applestring[]; /* defined in simpletools */ extern int wprocid; extern windowptr windowpointer(); double alatd, alond, height; double cos1, cos2, sin1, sin2, xpos, xmax, scaler; long icos1, icos2, isin1, isin2, ixmax, ixpos, iscaler; double alat, alon; int lat, lon, alt; int latnlong; curshandle watch_cursor; rect erect; rect lat_data, lon_data, alt_data; rect icon_rect; handle icon_handle; controlhandle alt_sb; controlhandle lat_sb; controlhandle lon_sb; controlhandle north_check; controlhandle south_check; controlhandle east_check; controlhandle west_check; controlhandle mi_check; controlhandle km_check; extern unsigned char getch(); /* get next data point from data segment */ windowptr wind; int storage[12000]; int draw; /* != 0 if we want to draw the line. ==0 if move to point */ int over; /* != 0 when point is outside visible area */ bitmap saved_bitmap; char controls[] = "Controls"; char earthwin[] = "Earth"; /* * beep - make a noise! */ beep () { sysbeep (5); /* useful in debugging */ } leave() /* Attached to the Quit menu */ { exit(0); } nop() { } xxx(xabs, yabs, zabs) /* plot a line or move current position */ long xabs, yabs, zabs; { int x, y; long ifactor, ixtmp, ixrel, iyrel, izrel; /* * draw line if we want to draw (as opposed to move) * and the previous point was drawn too. */ draw = draw && !over; ixtmp = ((xabs * icos2) + (yabs * isin2)) / fudge; ixrel = ((ixtmp * icos1) + (zabs * isin1)) / fudge; over = (ixrel < ixmax); if (over) return; iyrel = ((yabs * icos2) - (xabs * isin2)) / fudge; izrel = ((zabs * icos1) - (ixtmp * isin1)) / fudge; ifactor = ixpos * iscaler / (ixpos - ixrel); /* you'll want this if the earth window size becomes variable * x = ((((iyrel * ifactor) / fudge) + fudge) * half_xsize) / fudge; * y = ((((-izrel * ifactor) / fudge) + fudge) * half_ysize) / fudge; * * another hack to get rid of two ldiv's. * approximation is close enough */ x = ((((iyrel * ifactor) / fudge) + fudge) * 123) >> 13; y = ((((-izrel * ifactor) / fudge) + fudge) * 123) >> 13; if (draw) lineto (x, y); else moveto (x, y); } /* * brag - put up our ABOUT BOX */ brag () { char mess[255]; /* string to form a message */ strcpy (mess, "\265EarthPlot -- Version 2.0 -- December 1985\r"); strcat (mess, "Public domain program by Michael Peirce\r"); strcat (mess, "Version 2 improvements by Marsh Gosnell"); if (message (mess)) nop (); } sorry () { char mess[255]; /* string to form a message */ strcpy (mess, "You'll have to use shift-option-3."); if (message (mess)) nop (); } /* * eraseearth - erase earth window */ eraseearth () { withwindow(earthwin); fillrect(&erect, qdvars.qdwhite); frameoval(&erect); } /* * drawearth - actually draw the picture */ drawearth () { int temp, i, j, first; eventrecord theevent; double coslat, coslon, sinlon, x, y, z; long ix, iy, iz; setcursor(*watch_cursor); eraseearth(); alatd = getctlvalue(lat_sb); alond = getctlvalue(lon_sb); height = getctlvalue(alt_sb) * (long)alt_scale; if (getctlvalue(mi_check) == 0) height = height * 1.61; alat = alatd * conv; alon = alond * conv; cos1 = cos(alat); cos2 = cos(alon); sin1 = sin(alat); sin2 = sin(alon); if (getctlvalue(south_check) == 1) sin1 = -sin1; if (getctlvalue(west_check) == 1) sin2 = -sin2; xpos = (height + diam) / diam; xmax = 1.0 / xpos; scaler = sqrt (1.0 - (xmax * xmax)); icos1 = (long)(cos1 * fudge); icos2 = (long)(cos2 * fudge); isin1 = (long)(sin1 * fudge); isin2 = (long)(sin2 * fudge); ixmax = (long)(xmax * fudge); ixpos = (long)(xpos * fudge); iscaler = (long)(scaler * fudge); moveto((int)xsize, (int)half_ysize); if (latnlong) { for (j = 1; j < 11; j++) { alat = (double)((j - 6) * 15) * conv; coslat = cos(alat) * fudge; iz = (long)(sin(alat) * fudge); draw = FALSE; over = FALSE; for (i = 0; i < 204; i += 2) { alon = (double)(i) * pi100; ix = (long)(coslat * cos(alon)); iy = (long)(coslat * sin(alon)); xxx(ix, iy, iz); draw = TRUE; } if (getnextevent(everyevent, &theevent) != 0) if (theevent.what == mousedown) goto abort_drawing; } for (j = 0; j < 24; j++) { alon = (double)((j * 15)) * conv; coslon = cos(alon) * fudge; sinlon = sin(alon) * fudge; draw = FALSE; over = FALSE; for (i = 0; i < 104; i += 2) { alat = ((double)(i) / 100.0 - 0.5) * pi; coslat = cos(alat); ix = (long)(coslat * coslon); iy = (long)(coslat * sinlon); iz = (long)(sin(alat) * fudge); xxx(ix, iy, iz); draw = TRUE; } if (getnextevent(everyevent, &theevent) != 0) if (theevent.what == mousedown) goto abort_drawing; } } getch(1); /* reset data pointer */ over = FALSE; while ((ix = getch(0)) != 0xff) { iy = getch(0); iz = getch(0); draw = ix || iy || iz; if (!draw) { ix = getch(0); iy = getch(0); iz = getch(0); } /* x>>8 == x/265 */ ix = ((ix * fudge2) >> 8) - fudge; iy = ((iy * fudge2) >> 8) - fudge; iz = ((iz * fudge2) >> 8) - fudge; xxx(ix, iy, iz); } abort_drawing: initcursor(); wind = windowpoint(earthwin); copybits(&(wind->portbits), &(saved_bitmap), &erect, &erect, srccopy, NULL); } /* * dolines - toggles lat & long lines */ dolines() { latnlong = !latnlong; if (latnlong) menu("\265Plot", "Grid on/G", itemcheck); else menu("\265Plot", "Grid on/G", itemuncheck); } /* * restore_earth - keeps earth drawn */ restore_earth() { wind = windowpoint(earthwin); copybits(&(saved_bitmap), &(wind->portbits), &erect, &erect, srccopy, NULL); } copy_earth() { /* long temp; pichandle pic; restype pict_res; rect xrect; pic = openpicture(&erect); hlock(pic); restore_earth(); closepicture(); pict_res[0] = 'P'; pict_res[1] = 'I'; pict_res[2] = 'C'; pict_res[3] = 'T'; zeroscrap(); temp = gethandlesize(pic); temp = putscrap(temp,&pict_res,*pic); killpicture(pic);beep(); */ } TextUpdate() { windowptr wind; wind = windowpoint(controls); drawcontrols(wind); textface(9); moveto(60, 36); textface(0); printf("\265EarthPlot V2\n"); moveto(9, 92); printf("Latitude:\n"); moveto(9, 172); printf("Longitude:\n"); moveto(9, 252); printf("Altitude:\n"); } DataUpdate() { windowptr wind; wind = windowpoint(controls); fillrect(&lat_data, white); moveto(75, 92); printf("%d\241\n", getctlvalue(lat_sb)); fillrect(&lon_data, white); moveto(82, 172); printf("%d\241\n", getctlvalue(lon_sb)); fillrect(&alt_data, white); moveto(73, 252); printf("%ld\n", getctlvalue(alt_sb) * (long)alt_scale); } doupdate() { windowptr wind; wind = windowpoint(controls); ploticon(&icon_rect, icon_handle); TextUpdate(); DataUpdate(); drawcontrols(wind); moveto(0, 61); lineto(175, 61); moveto(0, 64); lineto(175, 64); } pascal track(control, partcode) controlhandle control; int partcode; { int i, step; if (partcode == 0) return; switch (partcode) { case inupbutton: step = -1; break; case indownbutton: step = 1; break; case inpageup: step = -10; break; case inpagedown: step = 10; break; default: return; } i = getctlvalue(control) + step; if (*control == *lat_sb) { if (i > 90) i = 90; if (i < 0) i = 0; } if (*control == *lon_sb) { if (i > 180) i = 180; if (i < 0) i = 0; } if (*control == *alt_sb) { if (i > 160) i = 1600; if (i < 0) i = 0; } setctlvalue(control, i); DataUpdate(); } doincontent(x, y, thewindow, theevent) int x, y; windowptr thewindow; eventrecord *theevent; { controlhandle thecontrol; int partcode; globaltolocal(&theevent->where); partcode = findcontrol(&theevent->where, thewindow, &thecontrol); if (partcode) { switch (partcode) { case incheckbox: partcode = trackcontrol(thecontrol, &theevent->where, NULL); if (*thecontrol == *north_check) { setctlvalue(north_check, 1); setctlvalue(south_check, 0); } if (*thecontrol == *south_check) { setctlvalue(north_check, 0); setctlvalue(south_check, 1); } if (*thecontrol == *east_check) { setctlvalue(east_check, 1); setctlvalue(west_check, 0); } if (*thecontrol == *west_check) { setctlvalue(east_check, 0); setctlvalue(west_check, 1); } if (*thecontrol == *mi_check) { setctlvalue(mi_check, 1); setctlvalue(km_check, 0); } if (*thecontrol == *km_check) { setctlvalue(mi_check, 0); setctlvalue(km_check, 1); } break; case inupbutton: case indownbutton: case inpageup: case inpagedown: partcode = trackcontrol(thecontrol, &theevent->where, track); break; case inthumb: partcode = trackcontrol(thecontrol, &theevent->where, NULL); break; } } DataUpdate(); } /* * setup - do our initial housekeeping */ setup () { int temp, i; rect alt_rect, lat_rect, lon_rect, mi_rect, km_rect; rect north_rect, south_rect, east_rect, west_rect; watch_cursor = getcursor(watchcursor); hlock(watch_cursor); latnlong = FALSE; setrect(&erect, -1, -1, (int)xsize + 1, (int)ysize + 1); menu(applestring, "About \265Plot", brag); menu(applestring, "About \265Plot", itemenable); menu("Edit", "Undo", nop); menu("Edit", "Undo", itemdisable); menu("Edit", "Cut/X", sorry); menu("Edit", "Cut/X", itemenable); menu("Edit", "Copy/C", nop); menu("Edit", "Copy/C", itemdisable); menu("Edit", "Paste/V", nop); menu("Edit", "Paste/V", itemdisable); menu("Edit", "Clear", eraseearth); menu("File", "Quit/Q", leave); menu("File", "Quit/Q", itemenable); menu("\265Plot", "Grid on/G", dolines); menu("\265Plot", "Erase Earth/B", eraseearth); menu("\265Plot", "Draw Earth/D", drawearth); temp = wprocid; wprocid = 3; window (earthwin, earth_1, earth_2, earth_3, earth_4, nop, nop, restore_earth, nop); setorigin(-2, -2); wprocid = temp; temp = wprocid; wprocid = 3; window (controls, info_1, info_2, info_3, info_4, nop, nop, doupdate, doincontent); wprocid = temp; wind = windowpoint(controls); saved_bitmap.baseaddr = (qdptr) & storage; saved_bitmap.rowbytes = wind->portbits.rowbytes; saved_bitmap.bounds = wind->portbits.bounds; setrect(&lat_rect, lat_sb_1, lat_sb_2, lat_sb_3, lat_sb_4); setrect(&lon_rect, lon_sb_1, lon_sb_2, lon_sb_3, lon_sb_4); setrect(&alt_rect, alt_sb_1, alt_sb_2, alt_sb_3, alt_sb_4); setrect(&lat_data, 75, 72, 175, 92); setrect(&lon_data, 82, 152, 175, 172); setrect(&alt_data, 73, 232, 175, 252); setrect(&north_rect, north_1, north_2, north_3, north_4); setrect(&south_rect, south_1, south_2, south_3, south_4); setrect(&east_rect, east_1, east_2, east_3, east_4); setrect(&west_rect, west_1, west_2, west_3, west_4); setrect(&mi_rect, mi_1, mi_2, mi_3, mi_4); setrect(&km_rect, km_1, km_2, km_3, km_4); setrect(&icon_rect, icon_1, icon_2, icon_3, icon_4); lat = 0; lon = 0; alt = 160; /* 160 * 1000 = 160,000 */ lat_sb = newcontrol(wind, &lat_rect, "", TRUE, lat, 0, 90, 16, (long)0); lon_sb = newcontrol(wind, &lon_rect, "", TRUE, lon, 0, 180, 16, (long)0); alt_sb = newcontrol(wind, &alt_rect, "", TRUE, alt, 1, 160, 16, (long)0); north_check = newcontrol(wind, &north_rect, "North", TRUE, 1, 0, 1, 2, (long)0); south_check = newcontrol(wind, &south_rect, "South", TRUE, 0, 0, 1, 2, (long)0); east_check = newcontrol(wind, &east_rect, "East", TRUE, 1, 0, 1, 2, (long)0); west_check = newcontrol(wind, &west_rect, "West", TRUE, 0, 0, 1, 2, (long)0); mi_check = newcontrol(wind, &mi_rect, "Miles", TRUE, 1, 0, 1, 2, (long)0); km_check = newcontrol(wind, &km_rect, "Kilometers", TRUE, 0, 0, 1, 2, (long)0); icon_handle = geticon(icon_id); textfont(0); eraseearth(); wind = windowpoint(earthwin); copybits(&(wind->portbits), &(saved_bitmap), &erect, &erect, srccopy, NULL); getch(1); /* call here to get disk activity done up front */ } main () { simpletools("About \265Plot"); setup(); for (; ; ) simpleevents (); }