|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.