hitchens@ut-sally.UUCP (Ron Hitchens, Vogon poetry critic for the Times) (05/31/85)
# This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Wrapped by ut-sally!hitchens on Fri May 31 02:58:21 CDT 1985 # Contents: README stringart.c gennums.c echo x - README sed 's/^@//' > "README" <<'@//E*O*F README//' Here's the source to Stringart which I promised a few weeks ago. I've been swamped doing real work and just haven't gotten around to it until now. I received quite a few requests for this so I'm posting it rather than sending individual copies to people. I spent quite a bit of time adding profuse comments so that MacBeginners can see what's necessary for a minimal application in C on the Mac. This code is functionally the same as the object code I posted a few weeks ago, except that a few things have been robustified and it will now use the entire screen if running on a Lisa. If anyone wants this new version to run on a Lisa but doesn't have MegaMax to compile it with I'll be happy to send them a binhex'd copy. To help relieve net congestion I won't repost the object code. This code was prepared with Megamax C version 2. As far as I know there is no reason why it shouldn't work with version 1 also, but I haven't tried it. There are two programs here. One generates a third C source file which declares the tables of numbers used to draw the vectors. This file and the stringart program itself are linked together to produce the application. Compilation instructions are in the comments in the stringart.c source. Feel free to distribute this thing far and wide. Perhaps someone could post it on CompuServe. I'd be glad to hear any comments about StringArt. Ron Hitchens hitchens@ut-sally.ARPA ...!ihnp4!ut-sally!hitchens.UUCP @//E*O*F README// chmod u=rw,g=r,o=r README echo x - stringart.c sed 's/^@//' > "stringart.c" <<'@//E*O*F stringart.c//' /* * StringArt May 1985 Ron Hitchens * * hitchens@ut-sally.ARPA ihnp4!ut-sally!hitchens.UUCP * * This program was adapted from a demo for the Sun Microsystems * Unix workstation * Ported to the Mac May 10, 1985 by Ron Hitchens * * It runs a lot faster on the Sun, even though it's also 68000 based * and is running multituser Unix 4.2BSD, has more layers of software * between you and the screen, and is computing coordinates using * floating point. It's clock rate is faster, and it's vector drawing * routines are coded for 1-bit-wide lines, but even so... * * This program is public domain. It may be be freely distributed, * copied and used. All I ask is that my name and this notice remain * on it. And that modifications be noted in this source listing and * returned to me if possible at the above email address. * * History: * May 85 Hitchens Created. * May 28 Hitchens Look at screen size vars, add comments * * * Compilation instructions with Megamax C: * You need the C source files stringart.c and gennums.c * 1. compile and link gennums.c * 2. run gennums, it creates a C source file named stringnums.c * This name is hard-coded in gennums.c * 3. Compile stringnums.c, but don't link it. Keep stringnums.o * 4. Compile stringart.c * 5. Link stringart.o and stringnums.o together to make stringart * 6. Run StringArt. * 7. Sit back and enjoy the show. * If you make changes to stringart, after doing the above, you can skip * to 4. Stringnums doesn't need to be re-compiled. * If you want to port this to another flavor of C you're on your own. * * The icon was created manually with the resource editor, to retain * the icon when recompiling just specify the name of the old * StringArt application file as the output of the linker. It will * overwrite the code but won't disturb the icon resources. */ /* This demo creates random vector designs. This is accomplished by randomly choosing a function for each coordinate halve of the two points describing a vector that moves through two dimensional space. Both x coordinate halves cannot be the same since the design would simply be a collection of vertical lines. Similarly both y coordinate halves cannot be the same. The functions are: function[0][x] = sin( 2*PI*x/NUMLINES ) function[1][x] = -sin( 2*PI*x/NUMLINES ) function[2][x] = cos( 2*PI*x/NUMLINES ) function[3][x] = -cos( 2*PI*x/NUMLINES ) function[4][x] = sin( 4*PI*x/NUMLINES ) function[5][x] = -sin( 4*PI*x/NUMLINES ) function[6][x] = cos( 4*PI*x/NUMLINES ) function[7][x] = -cos( 4*PI*x/NUMLINES ) function[8][x] = sin( 6*PI*x/NUMLINES ) function[9][x] = -sin( 6*PI*x/NUMLINES ) function[10][x] = cos( 6*PI*x/NUMLINES ) function[11][x] = -cos( 6*PI*x/NUMLINES ) function[12][x] = 2 * abs( x - (NUMLINES/2) - 1 ) *not used* The values of the functions were pre-computed to have the demo run as fast as possible. The program runs in an endless loop, it will terminate if the "Quit" menu item is selected or a 'Q' is typed. */ #include <qd.h> /* quikdraw */ #include <qdvars.h> /* quickdraw globals */ #include <win.h> /* window manager */ #include <menu.h> /* menu manager */ #include <event.h> /* event manager */ #define NUMLINES 343 /* number of vectors in a design */ #define NUM_FUNCTIONS 12 /* number of functions */ #define VOFFSET 19 /* offset from top, drag is under menu bar */ #define BORDER 2 /* surrounding blank border */ /* get real size of screen, in case of Lisa or something new */ #define HSIZE (qdvars.screenbits.bounds.a.right) #define VSIZE (qdvars.screenbits.bounds.a.bottom) windowrecord myrecord; /* if you can't figure out what these are */ windowptr mywindow; /* you'd better give up right now */ menuhandle mymenu; int holding = 0; /* are we currently holding? */ int step = 0; /* single step? */ int q [4][NUMLINES]; /* temp array for scaled coordinates */ extern int p [NUM_FUNCTIONS][NUMLINES]; /* pre-computed points, scaled by 1024 */ main() { int i, j, k, l, m; long secs; init (); /* do housekeeping */ showinfo (); /* display bragging info */ getdatetime (&qdvars.randseed); /* randomize the random() function */ while (1) { /* main loop, go round and round forever */ /* pick the functions */ while ((i = random() % NUM_FUNCTIONS) < 0) /* nothing */ ; while ((j = random() % NUM_FUNCTIONS) == i || (j < 0)); while ((k = random() % NUM_FUNCTIONS) < 0); while ((l = random() % NUM_FUNCTIONS) == k || (l < 0)); /* scalepoints was a hack to speed up the line drawing, by avoiding the scaling computations for each coordinate while the lines are being drawn. It didn't help as much as I'd hoped, only about a second is saved, maybe less. Apparently I'm pushing QuickDraw as fast as it will go. The same calculations using floating point took about 45 seconds, YOW! */ scalepoints (i, k, j, l); /* scale to the window */ while (holding && !step) /* loop here if holding */ checkmenu (); /* watch events when holding */ blankwindow (mywindow); /* clear to black or white */ setport (mywindow); /* point QD at my window */ /* the above statement shouldn't be necessary, but something sneaky, like the screensaver DA, could be lurking in the darkness and may change the grafport out from under us. Grrr... */ obscurecursor (); /* get rid of the cursor */ for (m = 0; m < NUMLINES; m++) { /* draw the vectors */ moveto (q [0][m], q [1][m]); lineto (q [2][m], q [3][m]); } systemtask (); /* in case of DAs */ if (!holding) sleep (6); /* pause for ~6 secs */ step = 0; /* only good for one shot */ } } /* the numbers in the array p were pre-computed using floating point arithmetic, they were all sin or cos functions and so yielded numbers [-1, 1]. These numbers were then scaled up by 1024 so they could be stored as integers. 1024 was used so they could be scaled down again with a shift-right-10 rather than a divide operation. I use two statements to compute each number to be sure the intermediate values are calculated as longs, the values involved far exceed the magnitude possible in a 16-bit int. */ scalepoints (x1, y1, x2, y2) int x1, y1, x2, y2; { long l, h, v; int i; h = (HSIZE) / 2; v = (VSIZE - VOFFSET) / 2; for (i = 0; i < NUMLINES; i++) { l = (((long)p [x1][i] * (h - BORDER)) >> 10) + h; q [0][i] = (int)l; l = (((long)p [y1][i] * (v - BORDER)) >> 10) + v; q [1][i] = (int)l; l = (((long)p [x2][i] * (h - BORDER)) >> 10) + h; q [2][i] = (int)l; l = (((long)p [y2][i] * (v - BORDER)) >> 10) + v; q [3][i] = (int)l; checkmenu (); /* see if there are any events to be handled */ } } checkmenu () /* test for and handle any events, such as keyboard and mouse */ { eventrecord theevent; windowptr tmpptr; /* loop as long as there are events in the queue */ while (getnextevent (everyevent, &theevent)) { switch (theevent.what) { case abortevt: /* something is telling us to stop */ exit (0); case keydown: /* got a key */ /* does this key belong to a menu? */ if (menukey ((char) (theevent.message % 256))) { /* yes */ do_menu (menukey ((char) (theevent.message % 256))); } else { step = 1; /* step once (undoc feature) */ return; } break; case mousedown: /* mouse clicked */ if (findwindow(&theevent.where, &tmpptr) == inmenubar){ do_menu (menuselect (&theevent.where)); } else { step = 1; /* cycle once */ } break; case updateevt: /* always get one of these at startup */ beginupdate (mywindow); endupdate (mywindow); /* only way to get rid of it */ break; case nullevent: /* shouldn't happen, filtered above */ return; default: break; /* not interested in anything else */ } } } do_menu (item) long item; /* handle a menu selection */ { hilitemenu (1); switch (item) { case 0x00010001: /* menu 1, item 1 (hold) */ toggle_hold (); break; case 0x00010002: /* menu 1, item 2 (invert) */ invert (); break; case 0x00010003: /* menu 1, item 3 (author info) */ showinfo (); break; case 0x00010004: /* menu1, item 4 (quit) */ exit (0); } hilitemenu (0); } toggle_hold () { holding = !holding; /* toggle the flag */ checkitem (mymenu, 1, holding); /* place or remove checkmark, depending on holding */ } invert () { setport (mywindow); /* on the off chance... */ if (mywindow->pnmode == patbic) { penmode (patcopy); /* draw black */ textmode (srccopy); } else { penmode (patbic); /* draw white */ textmode (srcbic); } invertrect (&mywindow->portrect); /* flip what's onscreen */ } init () /* do initial housekeeping chores */ { managerinits (); windowinits (); menuinits (); } managerinits() { initgraf(&theport); /* initialize qd (theport in qdvars) */ initfonts(); /* initialize font manager */ initwindows(); /* initialize window manager */ initmenus(); /* initialize menus */ initcursor(); /* set cursor to arrow */ flushevents(everyevent, 0); /* lose any outstanding events */ } windowinits() { rect temprect; setrect(&temprect, 0, VOFFSET, HSIZE, VSIZE); mywindow = newwindow(&myrecord, &temprect, "Stringart", 1, 0, (long) -1, 0, (long) 0); setport(mywindow); /* draw in my window */ selectwindow(mywindow); /* make mine active */ pennormal (); /* make sure pen has defaults */ penmode (patbic); /* make it draw white */ textmode (srcbic); /* make text draw white */ textfont (0); /* use system font */ textface ((short) bold); /* use a fancy text style */ } menuinits () { mymenu = newmenu (1, "StringArt"); /* allocate a menu struct */ appendmenu (mymenu, "Hold/H"); /* load the item values */ appendmenu (mymenu, "Invert/I"); appendmenu (mymenu, "Author Info/A"); appendmenu (mymenu, "Quit/Q"); insertmenu (mymenu, 0); /* let the OS know about it */ drawmenubar (); /* make it show up */ } sleep (s) int s; /* sleep for approx s seconds, could vary from s-1 to s seconds */ { long s1, s2; getdatetime (&s1); /* what time is it now? */ s1 += s; /* looking for now + s seconds */ while (1) { getdatetime (&s2); if (s1 <= s2) break; /* ok, the time has come */ checkmenu (); /* keep an eye on events around you */ } } blankwindow (w) windowptr w; { if (w->pnmode == patbic) { /* drawing white? */ fillrect (&w->portrect, qdvars.black); /* yes, paint window black */ } else { fillrect (&w->portrect, qdvars.white); /* no, paint it white */ } } showinfo () /* display bragging info */ { static char *s [] = { "StringArt by Ron Hitchens", "hitchens@ut-sally.ARPA or ...!ihnp4!ut-sally!hitchens.UUCP", "", "Written in MegaMax C", "This program is public domain", "", "While in Hold, click mouse to single step", "To save an image, type: SHIFT/CMD/3" , "To print an image, type: SHIFT/CMD/4" }; int i, n; static int busy = 0; hilitemenu (1); /* turn on the menu */ if (busy) return; /* info screen already up */ busy = 1; /* to prevent recursive displays */ setport (mywindow); /* just in case */ obscurecursor (); /* get rid of the cursor */ blankwindow (mywindow); n = sizeof (s) / sizeof (s [0]); /* how many lines of text? */ textsize (24); /* get fancy for first line */ for (i = 0; i < n; i++) { /* display all the text */ moveto ((HSIZE / 2) - (stringwidth (s [i]) / 2), (VSIZE / (n + 1)) + (i * (VSIZE / (n + 1)))); drawstring (s [i]); /* draw one of the text lines */ textsize (12); /* back to normal for the rest */ } if (!holding) sleep (10); /* wait a bit */ hilitemenu (0); /* turn off the menu */ busy = 0; /* ok, I'm finished */ } @//E*O*F stringart.c// chmod u=rw,g=r,o=r stringart.c echo x - gennums.c sed 's/^@//' > "gennums.c" <<'@//E*O*F gennums.c//' /* gennums.c generate number tables for stringart */ #include <stdio.h> /* standard I/O */ #include <fmath.h> /* floating point math */ /* This demo creates random vector designs. This is accomplished by randomly choosing a function for each coordinate halve of the two points describing a vector that moves through two dimensional space. Both x coordinate halves cannot be the same since the design would simply be a collection of vertical lines. Similarly both y coordinate halves cannot be the same. The functions are: function[0][x] = sin( 2*PI*x/NUMLINES ) function[1][x] = -sin( 2*PI*x/NUMLINES ) function[2][x] = cos( 2*PI*x/NUMLINES ) function[3][x] = -cos( 2*PI*x/NUMLINES ) function[4][x] = sin( 4*PI*x/NUMLINES ) function[5][x] = -sin( 4*PI*x/NUMLINES ) function[6][x] = cos( 4*PI*x/NUMLINES ) function[7][x] = -cos( 4*PI*x/NUMLINES ) function[8][x] = sin( 6*PI*x/NUMLINES ) function[9][x] = -sin( 6*PI*x/NUMLINES ) function[10][x] = cos( 6*PI*x/NUMLINES ) function[11][x] = -cos( 6*PI*x/NUMLINES ) function[12][x] = 2 * abs( x - (NUMLINES/2) - 1 ) The values of the functions were pre computed to have the demo run as fast as possible. The program will only terminate on interrupt since it is in an endless loop. */ /* these should be in a common header file, but I'm lazy */ #define NUMLINES 343 /* number of vectors in a design */ #define NUM_FUNCTIONS 12 /* number of functions */ int p [NUMLINES]; main() { int i, j, k, l, m; FILE *f; f = fopen ("stringnums.c", "w"); fprintf (f, "\n\t/* This source file generated by gennums.c */\n\n\n"); fprintf (f, "#define NUM_FUNCTIONS %d\n", NUM_FUNCTIONS); fprintf (f, "#define NUMLINES %d\n\n", NUMLINES); fprintf (f, "int p [NUM_FUNCTIONS][NUMLINES] = {\n\n"); for (i = 0; i < NUM_FUNCTIONS; i++) { cifer (i, 1024); /* scaling down is faster with a power of 2 */ dumparray (f, i); } fprintf (f, "\t}\n};\n\n"); fclose (f); } dumparray (f, n) FILE *f; int n; { int i; if (n != 0) fprintf (f, "\t},\n\n"); fprintf (f, "\t{\n"); for (i = 0; i < NUMLINES; i++) { fprintf (f, "\t%d", p[i]); if (i != NUMLINES - 1) fprintf (f, ","); if (i % 7 == 6) fprintf (f, "\n"); } } #define PI 3.1415927 int cifer (func, scale) int func, scale; /* Hey Uncle Jed... */ { float tmp; int x; for (x = 0; x < NUMLINES; x++) { /* clumsy, but doesn't need to be efficient */ switch (func) { case 0: tmp = sin( 2*PI*x/NUMLINES); break; case 1: tmp = -sin( 2*PI*x/NUMLINES); break; case 2: tmp = cos( 2*PI*x/NUMLINES); break; case 3: tmp = -cos( 2*PI*x/NUMLINES); break; case 4: tmp = sin( 4*PI*x/NUMLINES); break; case 5: tmp = -sin( 4*PI*x/NUMLINES); break; case 6: tmp = cos( 4*PI*x/NUMLINES); break; case 7: tmp = -cos( 4*PI*x/NUMLINES); break; case 8: tmp = sin( 6*PI*x/NUMLINES); break; case 9: tmp = -sin( 6*PI*x/NUMLINES); break; case 10: tmp = cos( 6*PI*x/NUMLINES); break; case 11: tmp = -cos( 6*PI*x/NUMLINES); break; case 12: tmp = 2 * abs( x - (NUMLINES/2) - 1); /* not used */ break; default: tmp = x; /* shouldn't happen */ break; } p [x] = (int)(tmp * scale); } } @//E*O*F gennums.c// chmod u=rw,g=r,o=r gennums.c exit 0