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.