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