Annotation of Examples/AppKit/Backspace/BoinkViewPart.m, revision 1.1.1.1

1.1       root        1: //  BoinkViewPart.m
                      2: //
                      3: //  implements a bouncing ball 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 "BoinkViewPart.h"
                     11: #import "SpaceView.h"
                     12: #import "Thinker.h"
                     13: #import "BoinkWraps.h"
                     14: #import <appkit/NXImage.h>
                     15: #import <math.h>
                     16: #import <libc.h>
                     17: #import <dpsclient/wraps.h>
                     18: 
                     19: @implementation BoinkView
                     20: 
                     21: // assumed interval in milliseconds
                     22: #define ASSUMED_INTERVAL 35
                     23: 
                     24: #define WIDTH 100
                     25: #define HEIGHT 100
                     26: #define GAP 4
                     27: #define COUNT 10
                     28: #define ACCEL (-2)
                     29: #define REBOUND (-1.3)
                     30: 
                     31: // This screen height value is not critical, though it will be used
                     32: // to determine how high the ball can go
                     33: #define SCREEN_HEIGHT 832
                     34: #define LAUNCH_SPEED (sqrt(fabs(2*ACCEL*(SCREEN_HEIGHT - HEIGHT))))
                     35: #define REAL_LAUNCH_SPEEd (sqrt(fabs(2*accel*(viewHeight - HEIGHT))))
                     36: 
                     37: #define MIN_X_SPEED (3)
                     38: #define MAX_X_SPEED (6)
                     39: #define ABS_MAX_X_SPEED (6)
                     40: #define MAX_Y_SPEED (LAUNCH_SPEED + 20)
                     41: 
                     42: #define BUFFER_WIDTH (WIDTH + ABS_MAX_X_SPEED + 1)
                     43: #define BUFFER_HEIGHT (HEIGHT + MAX_Y_SPEED + 1)
                     44: 
                     45: 
                     46: 
                     47: /* move the ball to its new bounce position */
                     48: - oneStep
                     49: {
                     50:        NXRect black = {0,0,0,0};
                     51:        NXRect ballRect;
                     52:        BRECT new;
                     53:        float scaledTime, calcYpos;
                     54:        
                     55:        then = now;
                     56:        now = currentTimeInMs();
                     57: 
                     58:        /* calculate new ball x position */
                     59:        xpos += [self timeCorrectedXSpeed];
                     60: 
                     61:        if (xpos < 0)                           /* ball hit left edge */
                     62:        {       xspeed = -xspeed;
                     63:                if (viewWidth > WIDTH)
                     64:                {       spinDir = -spinDir;
                     65:                }
                     66:                xpos = 0;
                     67:        }
                     68:        else if (xpos > (viewWidth - WIDTH))            /* ball hit right edge */
                     69:        {       if (viewWidth > WIDTH)
                     70:                {
                     71:                        xspeed = -[self getRandomXspeed];
                     72:                        [self checkXspeed:&xspeed];
                     73:                        xpos = (viewWidth - WIDTH);
                     74:                }
                     75:                else
                     76:                {       xspeed = xpos = 0;
                     77:                }
                     78:        }
                     79: 
                     80: 
                     81:        scaledTime = ((float)(now - then) / ASSUMED_INTERVAL);
                     82:        if (scaledTime > 1) scaledTime = 1;
                     83:        
                     84:        // calculate new ball vertical position
                     85:        calcYpos = ypos + (scaledTime*yspeed) + ((accel * scaledTime * scaledTime)/2);
                     86: 
                     87:        // change vertical ball speed to simulate gravity
                     88:        yspeed += (accel * scaledTime);
                     89:        
                     90:        if (calcYpos < (ypos - MAX_Y_SPEED)) calcYpos = ypos - MAX_Y_SPEED;
                     91:        else if (calcYpos > (ypos + MAX_Y_SPEED)) calcYpos = ypos + MAX_Y_SPEED;
                     92:        
                     93:        ypos = calcYpos;
                     94:        
                     95:        if (yspeed < -MAX_Y_SPEED) yspeed = -MAX_Y_SPEED;
                     96:        
                     97: 
                     98:        if (ypos <= 0)                          /* ball hit bottom of window */
                     99:        {
                    100:                ypos = 0;
                    101:                
                    102:                if (viewHeight > HEIGHT)
                    103:                {
                    104:                        if (reboundMode == DECREASING)
                    105:                        {
                    106:                                yspeed = lastLaunchSpeed = lastLaunchSpeed + rebound;
                    107:                        }
                    108:                        else
                    109:                        {
                    110:                                yspeed = lastLaunchSpeed = lastLaunchSpeed - (2*rebound);
                    111:                        }
                    112: 
                    113:                        if (yspeed <= 0)
                    114:                        {
                    115:                                yspeed = 0;
                    116:                                reboundMode = INCREASING;       /* bounce height increases every bounce */
                    117:                        }
                    118:                        else if (yspeed > MAX_Y_SPEED) yspeed = MAX_Y_SPEED - (3*accel);
                    119:                }
                    120:                else yspeed = 0;
                    121:        
                    122:        }
                    123:        else if (ypos >= (viewHeight - HEIGHT)) /* ball hit top of window */
                    124:        {       if (viewHeight > HEIGHT)
                    125:                {
                    126:                        yspeed = accel;
                    127:                        ypos = (viewHeight - HEIGHT);
                    128:                        reboundMode = DECREASING;       /* bounce height decreases every bounce */
                    129:                        spinDir = -spinDir;
                    130:                }
                    131:                else
                    132:                {       yspeed = ypos = 0;
                    133:                }
                    134:        }
                    135: 
                    136: 
                    137:        /* rotate the ball by selecting a new ball image to blit */
                    138:        /* we have an image of the ball in 10 different stages of rotation */
                    139: 
                    140:        [self incrementBallNumber];
                    141:        
                    142:        new.l = floor(xpos);
                    143:        new.b = floor(ypos);
                    144:        new.r = new.l + WIDTH;
                    145:        new.t = new.b + HEIGHT;
                    146:        
                    147:        ballRect.origin.x = (WIDTH+GAP) * ballNum;
                    148:        ballRect.origin.y = 0;
                    149:        ballRect.size.width = WIDTH;
                    150:        ballRect.size.height = HEIGHT;
                    151:        
                    152:        redrawTo.x = MIN(new.l, old.l);
                    153:        redrawTo.y = MIN(new.b, old.b);
                    154: 
                    155:        redraw.origin.x = 0;
                    156:        redraw.origin.y = 0;
                    157:        redraw.size.width = (MAX(new.r, old.r)) - redrawTo.x + 1;
                    158:        redraw.size.height = (MAX(new.t, old.t)) - redrawTo.y + 1;
                    159:        
                    160:        black.size= redraw.size;
                    161: 
                    162:        [self updateGrid];
                    163: 
                    164:        [buffer lockFocus];
                    165:        PSsetgray(0);
                    166:        NXRectFill(&black);
                    167:        
                    168:        ballTo.x = new.l - redrawTo.x;
                    169:        ballTo.y = new.b - redrawTo.y;
                    170: 
                    171:        [self drawLinesInBuffer];
                    172:        
                    173:        [balls composite:NX_SOVER fromRect:&ballRect toPoint:&ballTo];
                    174:        [buffer unlockFocus];
                    175:        
                    176:        
                    177:        // Now bring it onto the screen
                    178:        
                    179:        [buffer composite:NX_COPY fromRect:&redraw toPoint:&redrawTo];
                    180: 
                    181:        old = new;
                    182: 
                    183:        return self;
                    184: }
                    185: 
                    186: 
                    187: 
                    188: /* calculate a vertical launch speed which will get the ball almost to */
                    189: /* the top of the window before gravity pulls it back down. Little bit */
                    190: /* of physics lesson here...                                                                                   */
                    191: 
                    192: - newSpeed
                    193: {
                    194:        lastLaunchSpeed = yspeed = REAL_LAUNCH_SPEEd;
                    195:        if (yspeed > MAX_Y_SPEED) yspeed = MAX_Y_SPEED;
                    196:        xpos = 0;
                    197:        ypos = 0;
                    198: 
                    199:        if (viewWidth <= WIDTH) xspeed = 0;
                    200:        else xspeed = [self getRandomXspeed];
                    201: 
                    202:        [self checkXspeed:&xspeed];
                    203:        rebound = REBOUND;
                    204:        return self;
                    205: }
                    206: 
                    207: 
                    208: - initFrame:(const NXRect *)frameRect
                    209: {
                    210:        NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };
                    211: 
                    212:        [super initFrame:frameRect];
                    213:        [self allocateGState];          // For faster lock/unlockFocus
                    214:        [self setClipping:NO];          // even faster...
                    215: 
                    216:        accel = ACCEL;
                    217:        spinDir = 1;
                    218: 
                    219:        //in this case, I only need one buffer for several Views
                    220:        if (!(buffer = [NXImage findImageNamed:"boinkBuffer"]))
                    221:        {
                    222:                buffer = [[NXImage alloc] initSize:&black.size];
                    223:                [buffer setName:"boinkBuffer"];
                    224:        }
                    225:        
                    226:        if ([buffer lockFocus])
                    227:        {
                    228:                PSsetgray(0);
                    229:                NXRectFill(&black);
                    230:                [buffer unlockFocus];
                    231:        }
                    232: 
                    233:        balls = [NXImage findImageNamed:"balls"];
                    234: 
                    235:        [self newViewSize];
                    236: 
                    237:        return self;
                    238: }
                    239: 
                    240: - setAccel:(float)val
                    241: {
                    242:        accel = val;
                    243:        return self;
                    244: }
                    245: 
                    246: - sizeTo:(NXCoord)width :(NXCoord)height
                    247: {
                    248:        [super sizeTo:width :height];
                    249:        [self newViewSize];
                    250:        return self;
                    251: }
                    252: 
                    253: - drawSelf:(const NXRect *)rects :(int)rectCount
                    254: {
                    255:        if (!rects || !rectCount) return self;
                    256:        
                    257:        //PSsetgray(0);
                    258:        //NXRectFill(rects);
                    259:        
                    260:        NXRectClip(rects);
                    261:        [self drawGrid];
                    262: 
                    263:        return self;
                    264: }
                    265: 
                    266: - newViewSize
                    267: {
                    268:        int i;
                    269:        //this is called every time View size changes
                    270:        NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };
                    271: 
                    272:        then = now = currentTimeInMs();
                    273: 
                    274:        if (oldSize.width == bounds.size.width &&
                    275:                        oldSize.height == bounds.size.height)
                    276:                return self;
                    277:        else
                    278:        {
                    279:                oldSize.width = bounds.size.width;
                    280:                oldSize.height = bounds.size.height;
                    281:        }
                    282:        
                    283:        old.l = old.r = old.b = old.t = ballTo.x = ballTo.y = 0;
                    284: 
                    285:        viewWidth = bounds.size.width;
                    286:        viewHeight = bounds.size.height;
                    287:        if (viewHeight > SCREEN_HEIGHT) viewHeight = SCREEN_HEIGHT;
                    288:        
                    289:        nvert = viewWidth/130;
                    290:        if (nvert > NVERT) nvert = NVERT;
                    291:        nhoriz = viewHeight/130;
                    292:        if (nhoriz > NHORIZ) nhoriz = NHORIZ;
                    293:        
                    294:        if (viewWidth < WIDTH) nvert = 0;
                    295:        if (viewHeight < HEIGHT) nhoriz= 0;
                    296:        vcount = hcount = 0;
                    297:        
                    298:        for (i=0; i<nvert; i++)
                    299:        {
                    300:                vertLines[i].hue = i * 0.17;
                    301:                while (vertLines[i].hue > 1) vertLines[i].hue -= 1;
                    302:                vertLines[i].pos = floor(i * (viewWidth/nvert));
                    303:        }
                    304:        
                    305:        for (i=0; i<nhoriz; i++)
                    306:        {
                    307:                horizLines[i].hue = i * 0.17 + 0.1;
                    308:                while (horizLines[i].hue > 1) horizLines[i].hue -= 1;
                    309:                horizLines[i].pos = i * floor((viewHeight/nhoriz)) + 1;
                    310:        }
                    311:        
                    312:        if ([buffer lockFocus])
                    313:        {
                    314:                PSsetgray(0);
                    315:                NXRectFill(&black);
                    316:                [buffer unlockFocus];
                    317:        }
                    318: 
                    319:        [self newSpeed];
                    320:        return self;
                    321: }
                    322: 
                    323: - incrementBallNumber
                    324: {
                    325:        if (now > nextRotationTime)
                    326:        {
                    327:                ballNum += spinDir;
                    328: 
                    329:                if (ballNum >= COUNT) ballNum = 0;
                    330:                else if (ballNum < 0) ballNum = COUNT-1;
                    331:                nextRotationTime = now + 24;
                    332:        }
                    333: 
                    334:        return self;
                    335: }
                    336: 
                    337: - (float) getRandomXspeed
                    338: {
                    339:        return randBetween(MIN_X_SPEED, MAX_X_SPEED);
                    340: }
                    341: 
                    342: - (float) timeCorrectedXSpeed
                    343: {
                    344:        float ret = xspeed * ((float)(now - then) / ASSUMED_INTERVAL);
                    345:        [self checkXspeed:&ret];
                    346:        return ret;
                    347: }
                    348: 
                    349: - checkXspeed:(float *)speed
                    350: {
                    351:        if (*speed > MAX_X_SPEED) *speed = MAX_X_SPEED;
                    352:        else if (*speed < -MAX_X_SPEED) *speed = -MAX_X_SPEED;
                    353:        return self;
                    354: }
                    355: 
                    356: - (const char *)windowTitle
                    357: {
                    358:        return "Boink!";
                    359: }
                    360: 
                    361: 
                    362: - drawGrid
                    363: {
                    364:        int i;
                    365:        float *fp;
                    366:                
                    367:        for (i=0; i<nvert; i++)
                    368:        {
                    369:                fp = &vertLines[i].pos;
                    370:                colorLine(*fp, 0, *fp, viewHeight, vertLines[i].hue, 1);
                    371:        }
                    372:        
                    373:        for (i=0; i<nhoriz; i++)
                    374:        {
                    375:                fp = &horizLines[i].pos;
                    376:                colorLine(0, *fp, viewWidth, *fp, horizLines[i].hue, 1);
                    377:        }
                    378:        
                    379:        return self;
                    380: }
                    381: 
                    382: - updateGrid
                    383: {
                    384:        NXRect avoid;
                    385:        float oldPos;
                    386:        float *fp;
                    387:        
                    388:        if (!nvert && !nhoriz) return self;
                    389: 
                    390:        if (now < nextLineDrawTime) return self;
                    391: 
                    392:        nextLineDrawTime = now + 3300;
                    393:        
                    394:        avoid.origin = redrawTo;
                    395:        avoid.size = redraw.size;
                    396: 
                    397:        if (++toggle & 1)
                    398:        {
                    399:                //advance vertical line
                    400:                
                    401:                if (!nvert) return self;
                    402:                
                    403:                fp = &vertLines[vcount].pos;
                    404:                oldPos = *fp;
                    405:                *fp += 1;
                    406:                if (*fp > viewWidth) *fp = 0;
                    407:                vertLines[vcount].hue += 0.005;
                    408:                if (vertLines[vcount].hue > 1) vertLines[vcount].hue -= 1;
                    409:                
                    410: 
                    411:                verticalLineWithAvoidance(*fp, 0, *fp, viewHeight, vertLines[vcount].hue, 1, &avoid);
                    412:                verticalLineWithAvoidance(oldPos, 0, oldPos, viewHeight, 0, 0, &avoid);
                    413: 
                    414:                if (++vcount >= nvert) vcount = 0;
                    415:        }
                    416:        else
                    417:        {
                    418:                //advance horiz line
                    419:                
                    420:                if (!nhoriz) return self;
                    421:                
                    422:                fp = &horizLines[hcount].pos;
                    423:                oldPos = *fp;
                    424:                *fp += 1;
                    425:                if (*fp > viewHeight) *fp = 0;
                    426:                horizLines[hcount].hue += 0.005;
                    427:                if (horizLines[hcount].hue > 1) horizLines[hcount].hue -= 1;
                    428:                
                    429: 
                    430:                horizLineWithAvoidance(0, *fp, viewWidth, *fp, horizLines[hcount].hue, 1, &avoid);
                    431:                horizLineWithAvoidance(0, oldPos, viewWidth, oldPos, 0, 0, &avoid);
                    432: 
                    433:                if (++hcount >= nhoriz) hcount = 0;
                    434:        }
                    435:        
                    436:        return self;
                    437: }
                    438: 
                    439: void horizLineWithAvoidance(float x1, float y1, float x2,float y2,
                    440:                        float hue,float brightness, const NXRect *r)
                    441: {
                    442:        if (y1 <= r->origin.y || y1 >= r->origin.y+r->size.height)
                    443:                colorLine(x1, y1, x2, y2, hue, brightness);
                    444:        else
                    445:        {
                    446:                colorLine(x1, y1, r->origin.x, y2, hue, brightness);
                    447:                colorLine(r->origin.x+r->size.width, y1, x2, y2, hue, brightness);
                    448:        }
                    449: }
                    450: 
                    451: void verticalLineWithAvoidance(float x1, float y1, float x2,float y2,
                    452:                        float hue,float brightness, const NXRect *r)
                    453: {
                    454:        if (x1 <= r->origin.x || x1 >= r->origin.x+r->size.width)
                    455:                colorLine(x1, y1, x2, y2, hue, brightness);
                    456:        else
                    457:        {
                    458:                colorLine(x1, y1, x2, r->origin.y, hue, brightness);
                    459:                colorLine(x1, r->origin.y+r->size.height, x2, y2, hue, brightness);
                    460:        }
                    461: }
                    462: 
                    463: - drawLinesInBuffer
                    464: {
                    465:        NXRect avoid;
                    466:        int i;
                    467:                
                    468:        avoid.origin = redrawTo;
                    469:        avoid.size = redraw.size;
                    470: 
                    471:        for (i=0; i<nvert; i++)
                    472:        {
                    473:                float x = vertLines[i].pos;
                    474: 
                    475:                if (x >= avoid.origin.x && x <= avoid.origin.x + avoid.size.width)
                    476:                {
                    477:                        colorLine(x-redrawTo.x, 0, x-redrawTo.x, avoid.size.height, vertLines[i].hue, 1);
                    478:                }
                    479:        }
                    480:        
                    481:        for (i=0; i<nhoriz; i++)
                    482:        {
                    483:                float y = horizLines[i].pos;
                    484: 
                    485:                if (y >= avoid.origin.y && y <= avoid.origin.y + avoid.size.height)
                    486:                {
                    487:                        colorLine(0, y-redrawTo.y, avoid.size.width, y-redrawTo.y, horizLines[i].hue, 1);
                    488:                }
                    489:        }
                    490:        
                    491:        return self;
                    492: }
                    493: 
                    494: - inspector:sender
                    495: {
                    496:     return [sender boinkInspector];
                    497: }
                    498: 
                    499: 
                    500: 
                    501: @end
                    502: 
                    503: 
                    504: 
                    505: 
                    506: 
                    507: 
                    508: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.