home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / ScreenSavers / SpaceSaver / Source / SpaceView.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  7.0 KB  |  302 lines

  1. // SpaceView.m
  2. //
  3. // This class implements the flying starfield screen saver view.
  4. //
  5. // You may freely copy, distribute, and reuse the code in this example.
  6. // NeXT disclaims any warranty of any kind, expressed or implied, as
  7. // to its fitness for any particular use.
  8.  
  9. #import "SpaceView.h"
  10. #import "Thinker.h"
  11. #import "psfuncts.h"
  12.  
  13. #import <dpsclient/wraps.h>
  14. #import <appkit/NXImage.h>
  15. #import <objc/zone.h>
  16. #import <mach/mach.h>
  17. #import <c.h>
  18. #import <libc.h>
  19. #import <math.h>
  20.  
  21. #define PI (3.141592653589)
  22.  
  23. @implementation SpaceView
  24.  
  25. // takes theta and distance and stuffs it into x &y for *p
  26. - convertToXY:(STAR *) p
  27. {
  28.     p->draw->x = floor(bounds.size.width / 2 + (p->distance * cos(p->theta)));
  29.     p->draw->y = floor(bounds.size.height / 2 + (p->distance * sin(p->theta)));
  30.  
  31.     return self;
  32. }
  33.  
  34. - oneStep
  35. {
  36.     int i, count, starsInArray = 0;
  37.     STAR *p;
  38.     NXPoint *t;
  39.  
  40.     if (nstars < NSTARS) [self addStar];
  41.  
  42.     for (i = 0; i < nstars; i++) {
  43.         p = &stars[i];
  44.         p->distance += p->delta;
  45.         p->delta *= p->ddelta;
  46.  
  47.         [self convertToXY:p];
  48.  
  49.         // only draw the star if it moved > 1 pixel
  50.         if (p->draw->x != p->erase->x || p->draw->y != p->erase->y) {
  51.             BOOL mustErase = NO;
  52.             // add star to the erasure array
  53.             b[starsInArray] = *p->erase;
  54.             bc[starsInArray] = p->c;
  55.  
  56.             if (p->distance > p->changepoint[p->changemode]) {
  57.                 (p->c)++;    // increment character for next star size
  58.                 (p->changemode)++;
  59.                 }
  60.  
  61.             // clipping is off, so we must not draw outside view.
  62.             // replace stars that go too far...
  63.             if (p->draw->x < 0 ||
  64.                 p->draw->y < 0 ||
  65.                 p->draw->x + 7 > bounds.size.width ||
  66.                 p->draw->y + 7 > bounds.size.height)
  67.                 {
  68.                 [self replaceStarAt:i];
  69.                 mustErase = YES;
  70.                 }
  71.  
  72.             w[starsInArray] = *p->draw;
  73.             wc[starsInArray] = p->c;
  74.  
  75.             if (mustErase || [self allowStars:p]) starsInArray++;
  76.  
  77.             t = p->draw; p->draw = p->erase; p->erase = t;
  78.             }
  79.         }
  80.  
  81.     bc[starsInArray] = wc[starsInArray] = 0; //null terminate string
  82.     if (starsInArray) {
  83.         for (i = 0; i < (starsInArray - 1); i++) {
  84.             bOffsets[i].x = b[i+1].x - b[i].x;
  85.             bOffsets[i].y = b[i+1].y - b[i].y;
  86.             wOffsets[i].x = w[i+1].x - w[i].x;
  87.             wOffsets[i].y = w[i+1].y - w[i].y;
  88.             }
  89.         bOffsets[i].x = bOffsets[i].y = wOffsets[i].x = wOffsets[i].y = 0;
  90.  
  91.         count = 0;
  92.         while (count < starsInArray) {
  93.             char tc;
  94.             int j, t = (starsInArray - count);
  95.  
  96.             // You get the best performance if you put out all the stars
  97.             // at once.  This causes noticable flicker, so I put out
  98.             // 100 of the stars per iteration.  This gives reasonable speed
  99.             // and flicker is hardly noticable.  Besides, stars
  100.             // _should_ flicker a little...
  101.  
  102.             i = (t < STARSPERIT) ? t : STARSPERIT;
  103.             j = i + count;
  104.  
  105.             PSsetgray(NX_BLACK);
  106.             tc = bc[j]; bc[j] = 0;
  107.             PSWXYShow(b[count].x, b[count].y, &bc[count], (float *) (&bOffsets[count].x), i * 2);
  108.             bc[j] = tc;
  109.  
  110.             PSsetgray(NX_WHITE);
  111.             tc = wc[j]; wc[j] = 0;
  112.             PSWXYShow(w[count].x, w[count].y, &wc[count], (float *)(&wOffsets[count].x), i * 2);
  113.             wc[j] = tc;
  114.  
  115.             count += STARSPERIT;
  116.             }
  117.         }
  118.  
  119.     return self;
  120. }
  121.  
  122. // returns yes if the star is outside the avoidance rectangle
  123. // this is really fast and loose but it works acceptibly well
  124. // ps I could just use NXIntersectsRect() but I want to avoid
  125. // trap overhead.  Call me paranoid...
  126.  
  127. - (BOOL) allowStars:(const STAR *) p
  128. {
  129.     // just return if voidRect not set
  130.     if ((!voidRect.size.width) ||
  131.         p->draw->x < voidRect.origin.x ||
  132.         p->draw->y < voidRect.origin.y ||
  133.         p->draw->x+7 > voidRect.origin.x+voidRect.size.width ||
  134.         p->draw->y+7 > voidRect.origin.y+voidRect.size.height ||
  135.  
  136.         p->erase->x < voidRect.origin. x ||
  137.         p->erase->y < voidRect.origin. y ||
  138.         p->erase->x+7 > voidRect.origin.x+voidRect.size.width ||
  139.         p->erase->y+7 > voidRect.origin.y+voidRect.size.height) return YES;
  140.  
  141.     return NO;
  142. }
  143.  
  144. - initFrame:(const NXRect *) frameRect
  145. {
  146.     [super initFrame:frameRect];
  147.     [self allocateGState]; // For faster lock/unlockFocus
  148.     [self setClipping:NO]; // even faster...
  149.     [self setRadius];
  150.     loadPSProcedures();
  151.     PSWDefineFont("StarFont");
  152.  
  153.     return self;
  154. }
  155.  
  156. - drawSelf:(const NXRect *) rects :(int) rectCount
  157. {
  158.     // this drawself doesn't really draw the view at all.
  159.     // in fact it just promotes the window to screen depth...
  160.  
  161.     NXRect t = {0, 0, 1, 1};
  162.  
  163.     PSsetrgbcolor(1, 0, 0);
  164.     NXRectFill(&t); // yucky trick for window depth promotion!
  165.     PSsetgray(NX_BLACK);
  166.     NXRectFill(&t);
  167.  
  168.     PSselectfont("StarFont", 1.0);
  169.  
  170.     return self;
  171. }
  172.  
  173. - sizeTo:(NXCoord) width :(NXCoord) height
  174. {
  175.     [super sizeTo:width :height];
  176.  
  177.     if (oldSize.width != bounds.size.width || oldSize.height != bounds.size.height) {
  178.         oldSize.width = bounds.size.width;
  179.         oldSize.height = bounds.size.height;
  180.         [self setRadius];
  181.         nstars = 0;
  182.         [self display];
  183.         }
  184.  
  185.     return self;
  186. }
  187.  
  188. - addStar // only call addStar if there is room in the stars array!
  189. {
  190.     return [self replaceStarAt:nstars++];
  191. }
  192.  
  193. - replaceStarAt:(int) index
  194. {
  195.     float dist, t;
  196.     int tries = 0;
  197.     STAR *p = &stars[index];
  198.     BOOL inBounds;
  199.  
  200.     p->draw = &p->r1;
  201.     p->erase = &p->r2;
  202.  
  203.     do {
  204.         p->theta = randBetween(0, (2 * PI));
  205.  
  206.         if (tries++ < 3) p->distance = randBetween(1, radius);
  207.         else p->distance = randBetween(1, p->distance);
  208.  
  209.         inBounds = YES;
  210.         [self convertToXY:p];
  211.  
  212.         if (p->draw->x < 0 || p->draw->y < 0 ||
  213.             p->draw->x + 7 > bounds.size.width ||
  214.             p->draw->y + 7 > bounds.size.height)
  215.         {
  216.             inBounds = NO;
  217.         }
  218.     } while (!inBounds);
  219.  
  220.     p->delta = (0.2);
  221.  
  222.     p->ddelta = randBetween(1.0, 1.1);
  223.  
  224.     t = randBetween(0, (0.42 * radius));
  225.     dist = MAX(20, t);
  226.     p->changepoint[0] = p->distance + 5; // to b
  227.     p->changepoint[1] = p->changepoint[0] - 5 + dist + dist; // to c
  228.  
  229.     p->changepoint[2] = p->changepoint[1] + dist; // to d
  230.     p->changepoint[3] = p->changepoint[2] + dist; // to e
  231.     p->changepoint[4] = p->changepoint[3] + dist; // to f
  232.     p->changepoint[5] = 100000; // never change to g
  233.  
  234.     p->changemode = 0;
  235.  
  236.     if ((++toggle) & 1) p->c = 'a';
  237.     else p->c = 'g';
  238.  
  239.     p->r2 = p->r1;
  240.  
  241.     return self;
  242. }
  243.  
  244. - setRadius
  245. {
  246.     float x = bounds.size.width;
  247.     float y = bounds.size.height;
  248.  
  249.     radius = (sqrt(x*x + y*y)) / 2;
  250.  
  251.     return self;
  252. }
  253.  
  254. - (const char *) windowTitle { return "The Final Frontier"; }
  255.  
  256. - setVoidRect:(const NXRect *) r
  257. {
  258.     voidRect = *r;
  259.  
  260.     return self;
  261. }
  262.  
  263. - didLockFocus
  264. {
  265.     PSselectfont("StarFont", 1.0);
  266.  
  267.     return self;
  268. }
  269.  
  270. - (BOOL)useBufferedWindow {    return NO; }
  271.  
  272. - (BOOL)ignoreMouseMovement { return NO; }
  273.  
  274. @end
  275.  
  276. @implementation View(nonretainedFillMethod)
  277.  
  278. // I add this method as a category of View to be sure that all
  279. // my views implement it.  I really want to use nonretained windows
  280. // but they are drawn via drawSelf at all kinds of goofy times.  It
  281. // seems like the kit kind of throws up its hands when it doesn't have
  282. // a buffer to draw from, so you get a lot more drawSelfs than you need,
  283. // and sometimes you don't get them when you really want them.  I know
  284. // when I need the background filled in black, so I factor that out of
  285. // my drawSelf and then drawself only draws things that are already on
  286. // screen so you don't see it happen.  I will only call this method on
  287. // a nonretained (and full screen) window.
  288.  
  289. - fillBoundsWithBlack
  290. {
  291.     if ([self canDraw]) {
  292.         [self lockFocus]; {
  293.             PSsetgray(NX_BLACK);
  294.             NXRectFill(&bounds);
  295.             } [self unlockFocus];
  296.         }
  297.  
  298.     return self;
  299. }
  300.  
  301. @end
  302.