This is MandelView.m in view mode; [Download] [Up]
/* * MandelView.m, subclass of View, Jan.1992 * written by R. Pajarola * E-mail: rpajarol@iiic.ethz.ch */ #import "MandelView.h" #import <appkit/Application.h> #import <dpsclient/wraps.h> #import <appkit/appkit.h> #import <appkit/Cell.h> #import <appkit/TextField.h> #import <appkit/tiff.h> #import <math.h> #import <stdlib.h> #define MAXCOLOR 256 #define ESC '\x1B' @implementation MandelView double last_x, last_y, last_s = -1, xmin, ymin, ymax, s, delta; int iter, width, height, count; id data, myWindow; float c = 3.14159265359/MAXCOLOR; unsigned char color, *pixels, palette[MAXCOLOR]; NXRect bbox; BOOL stop; NXEvent *action; void fillRect(int x1,int y1,int x2,int y2) /* Filling a rectangle with "color". */ { int i; unsigned char *p, *p1, *p2; p1 = pixels + y1*width + x1; for(i=y1; i<y2; i++) { p2 = p1 + x2 - x1; for(p=p1; p<p2; *p++ = color); p1 += width; } } void setPixel(register int x, register int y, unsigned char *p) /* Calculating the Mandelbrotset for the komplex point z=(zx, i*zy) * by the formula z = z^2 +c. * The color depends on the iterations done for this pixel. */ { register double cx, cy, zx, zy, qx, qy; register int t; zx = cx = xmin + x*delta; zy = cy = ymax - y*delta; qx = zx*zx; qy = zy*zy; for (t=1; t<iter && qx+qy<4.0; t++) { zy = 2.0*zx*zy + cy; zx = qx - qy + cx; qx = zx*zx; qy = zy*zy; } if (t>=iter) color = 0; else { color = palette[t % MAXCOLOR]; } *p = color; } void calcRect(int x1, int y1, int x2, int y2) /* Calculating a rectangle with the corners x1,y1 (lower, left) * and x2,y2 (upper, right). */ { unsigned char lastcolor, *p1, *p2; int i,j,x,y; BOOL equal = YES; /* Testing the keyboard for a <Esc>-key input. */ action = [NXApp getNextEvent:NX_KEYDOWNMASK waitFor:0.0000001 threshold:NX_MODALRESPTHRESHOLD]; if (action != NULL) stop = (action->data.key.charSet == NX_ASCIISET) && (action->data.key.charCode == ESC); /* If there was no <Esc>-key pressed, continue the calculations. */ if (!stop) { count++; /* First the edges of the rectangle are calculated. * If all points have the same color, the rectangle will * be filled by this color. * Otherwise the rectangle will be split into four other * rectangles and this procedure is called again. * If the rectangle is too small it won't be split. */ p1 = pixels + y1*width + x1; setPixel(x1, y1, p1); lastcolor = color; p2 = pixels + (y2-1)*width + x1; setPixel(x1, y2-1, p2); equal = equal && (color == lastcolor); for (i = x1+1; i < x2; i++) { p1++; setPixel(i, y1, p1); equal = equal && (color == lastcolor); p2++; setPixel(i, y2-1, p2); equal = equal && (color == lastcolor); } p1 = pixels + y1*width + x1; p2 = pixels + y1*width + x2-1; for (j = y1+1; j < y2-1; j++) { p1 += width; setPixel(x1, j, p1); equal = equal && (color == lastcolor); p2 += width; setPixel(x2-1, j, p2); equal = equal && (color == lastcolor); } if (equal) { if (color) fillRect(x1+1, y1+1, x2-1, y2-1); } else { if (y2-y1 < 8) { p1 = pixels + y1*width + x1; for (j = y1+1; j < y2-1; j++) { p2 = p1 += width; for (i= x1+1; i < x2-1; i++) { p2++; setPixel(i, j, p2); } } } else { x = (x1+x2)/2; y = (y1+y2)/2; calcRect(x1+1, y1+1, x, y); if (!stop) calcRect(x, y1+1, x2-1, y); if (!stop) calcRect(x1+1, y, x, y2-1); if (!stop) calcRect(x, y, x2-1, y2-1); } } if (count == 3) { /* Rendering the calculated image on the screen. */ NXImageBitmap(&bbox, width, height, 8, 1, NX_MESHED, NX_MONOTONICMASK, pixels, NULL, NULL, NULL, NULL); [myWindow flushWindow]; } count--; } } - initFrame:(NXRect *)rect { int i; [super initFrame:rect]; [window useOptimizedDrawing:YES]; [self setOpaque:YES]; xmin = -2.1; ymin = -1.5; s = 3.0; /* Set colortable. */ for (i=0; i<MAXCOLOR; i++) palette[i] = sin(i*c)*254+1; return self; } - run:sender { stop = NO; [self lockFocus]; bbox = bounds; width = bbox.size.width; height = bbox.size.height; /* Getting or setting the values of the area that * has to be calculated. */ iter = [depth intValue]; if (iter==0) { [self setParameters: -2.1: -1.5: 3.0]; [depth setIntValue: 0]; [pixels_x setIntValue: width]; [pixels_y setIntValue: height]; [self unlockFocus]; return self; } last_x = xmin; last_y = ymin; last_s = s; [self getParameters: &xmin: &ymin: &s]; [pixels_x setIntValue: width]; [pixels_y setIntValue: height]; if (width <= height) delta = s/width; else delta = s/height; data = self; myWindow = window; /* Freeing and then newly allocating the memory for the bitmap data. */ free(pixels); pixels = (unsigned char *)calloc(width * height, sizeof(unsigned char)); if (pixels) { /* Clear the wondow */ NXImageBitmap(&bbox, width, height, 8, 1, NX_MESHED, NX_MONOTONICMASK, pixels, NULL, NULL, NULL, NULL); [window flushWindow]; /* We need ymax instead of ymin because of the flippedness of the * bitmap. */ ymax = ymin + height*delta; count = 0; calcRect(0.0, 0.0, width, height); /* Rendering the calculated image on the screen. */ NXImageBitmap(&bbox, width, height, 8, 1, NX_MESHED, NX_MONOTONICMASK, pixels, NULL, NULL, NULL, NULL); [window flushWindow]; } [self unlockFocus]; /* Signaling the user that the picture is completed. */ NXBeep(); return self; } - setParameters: (float)x: (float)y: (float)s { [x_min setDoubleValue: x]; [y_min setDoubleValue: y]; [scale setDoubleValue: s]; return self; } - getParameters: (double *)x: (double *)y: (double *)s { *x = [x_min doubleValue]; *y = [y_min doubleValue]; *s = [scale doubleValue]; return self; } - setlastParameters:sender { if (last_s >= 0) { [x_min setDoubleValue: last_x]; [y_min setDoubleValue: last_y]; [scale setDoubleValue: last_s]; } return self; } - drawSelf :(const NXRect *)rects :(int)rectCount { if (pixels) NXImageBitmap(&bbox, width, height, 8, 1, NX_MESHED, NX_MONOTONICMASK, pixels, NULL, NULL, NULL, NULL); return self; } - mouseDown:(NXEvent *)event /* * Imported method from the Mandelbrot demonstrationprogramm. */ { int looping = YES; int oldMask; NXRect bbox; NXPoint startPoint, currPoint; float newX, newY, newDX; NXCoord aspectRatio; aspectRatio = bounds.size.height / bounds.size.width; oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK]; startPoint = event->location; [self convertPoint:&startPoint fromView:nil]; NXSetRect(&bbox,startPoint.x,startPoint.y,0.0,0.0); [self lockFocus]; while (looping) { event=[NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK]; currPoint = event->location; [self convertPoint:&currPoint fromView:nil]; bbox.size.width = 2*(currPoint.x - startPoint.x); bbox.size.height = 2*(currPoint.y - startPoint.y); /* Normalize bbox to always have positive width and height */ if (bbox.size.width < 0) bbox.size.width = -bbox.size.width; if (bbox.size.height < 0) bbox.size.height = -bbox.size.height; /* * constrain the box to have the aspect ratio of the view. Choose * whichever dimension is closer to the desired result. */ if ((bbox.size.height/bbox.size.width) > aspectRatio) { bbox.size.height = bbox.size.width * aspectRatio; } else { bbox.size.width = bbox.size.height / aspectRatio; } /* The startPoint is always at the center of the bbox */ bbox.origin.x = startPoint.x - 0.5 * bbox.size.width; bbox.origin.y = startPoint.y - 0.5 * bbox.size.height; PSnewinstance(); if (looping = (event->type == NX_MOUSEDRAGGED)) { PSsetinstance(YES); PSsetgray(NX_WHITE); NXFrameRect(&bbox); PSsetinstance(NO); } } [self unlockFocus]; [window setEventMask:oldMask]; if ((bbox.size.width > 0) && (bbox.size.height > 0)) { /* * At this point, bbox is in window coordinates. Set new controller * parameters based on this view's coordinates and the bounding box. */ newX = bbox.origin.x; newX = newX * delta + xmin - delta/2; newY = bbox.origin.y; newY = newY * delta + ymin - delta/2; if (bbox.size.width <= bbox.size.height) newDX = (bbox.size.width*delta); else newDX = (bbox.size.height*delta); /* * Note that we only update the text fields in the mandelView -- we * don't update the internal variables. If we were to update * the internal vs, then the user would only get one chance with the * mouse down method because subsequent mouse-downs would work in terms * of the new coordinate system. */ [self setParameters:newX :newY :newDX]; } return self; } - (int)getValues:(unsigned char **)d :(int *)w :(int *)h :(int *)bps :(int *)spp :(BOOL *)alpha :(BOOL *)config :(NXColorSpace *)space /* Method for getting the bitmap and it's associated values. */ { *d = pixels; *w = width; *h = height; *bps = 8; *spp = 1; *alpha = NO; *config = NO; *space = NX_OneIsWhiteColorSpace; return (int)pixels; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.