
/* Generated by Interface Builder */

#import "Henon.h"

@implementation Henon

// This function is called by the timedEntry, every n seconds. The argument
// senderObject contains the oject that initiated the timed entry (Henon)

void iterate(DPSTimedEntry iter, double now, void *senderObject)
{
id henonObject;
    henonObject = (id) senderObject;	// Retrieve the Henon object
    [henonObject iteration];		// And call its method iteration.
}

- setHenonView:anObject
{
    henonView = anObject;
    A = 1.4;
    B = 0.3;
    speed = 0.25;
    [[henonView window] setBackgroundGray:0.];	// Sets the background to black
    return self;
}

- setMidiControl:anObject
{
    midiControl = anObject;
    return self;
}

- setParaCelA:anObject
{
    paraCelA = anObject;
    [paraCelA setFloatValue:1.4 at:0];
    return self;
}

- setParaCelB:anObject
{
    paraCelB = anObject;
    [paraCelB setFloatValue:0.3 at:0];
    return self;
}

- setParaSlidA:anObject
{
    paraSlidA = anObject;
    return self;
}

- setParaSlidB:anObject
{
    paraSlidB = anObject;
    return self;
}

- setA:sender
{
    A = [sender floatValue];		// This method work for both the cell
    [paraCelA setFloatValue:A at:0];	// and the slider objects. Cool!
    [paraSlidA setFloatValue:A];	// Make sure the slider and the cell
    return self;			// show the same values!
}

- setB:sender
{
    B = [sender floatValue];
    [paraCelB setFloatValue:B at:0];		// Idem!
    [paraSlidB setFloatValue:B];
    return self;
}

- start:sender
{
// This is to adjust the display view, in case we resized the window...
    [[[henonView window] contentView] getBounds:&viewBounds];
    [henonView setFrame:&viewBounds];
    if(start)
    {
	DPSRemoveTimedEntry(iter);	// Stops the timed entry
	start = 0;
	return self;
    }
    if(display == 0)
    {
	iter = DPSAddTimedEntry(speed,iterate,self,20); // Start timed Entry
	start = 1;		// Every speed seconds, iterate will be called
	if(initSet)		// with self as argument (self = Henon object)
	{			// "20" is the priority level
	    initSet = 0;
	    X = x_init;
	    Y = y_init;
	}
	[[henonView window] setTitle:"Henon Map; X/Y Plan"];
	return self;
    }
    if(display)			// If we want bifurcation.
    {
	[self clear:self];
	[[henonView window] setTitle:"Bifurcation Diagram; X/A Plan"];
	[self bifurcation];
    }
    return self;
}

- setInitial:sender
{
    switch([sender tag])
    {
	case 0 : x_init = [sender floatValueAt:0]; break ;
	case 1 : x_init = [sender floatValueAt:1]; break ;
	default : printf("Error during initial values setup\n"); break;
    }
    initSet = 1;
    return self;
}

- setSpeed:sender
{
    speed = [sender floatValue];
    if(start)
    {
	DPSRemoveTimedEntry(iter);	// When you change the speed, you must
					// replace the timed entry...
	iter = DPSAddTimedEntry(speed,iterate,self,20);
    }
    
    return self;
}


- setDisplay:sender
{
    display = [[sender selectedCell] tag];
    return self;	// if tag==0, Henon map, if tag==1 bifurcation.
}

- clear:sender
{

    [[[henonView window] contentView] getBounds:&viewBounds];
    [henonView setFrame:&viewBounds];		// Readjust the View size...
    [henonView lockFocus];		// Before any Drawing!
    PSsetgray(0.) ;			// Color = black
    NXRectFill(&viewBounds);		// Fill the view
    [henonView unlockFocus];		// After the drawing
    return self;
// The window is retained only (see its attributes in Intervace Builder).
// If it was buffered, we would need to flush it to actually see what we draw.
// This could be done by: [[henonView window] flushWindow]
}

- stableOnly:sender
{
    stable = [[sender selectedCell] tag];
    return self;
}

- iteration
{
float aux_x, aux_y;
int queueSize;

    queueSize = [midiControl getSizeOfQueue];	// Retrieve the size of the
    if(queueSize > 20) return self;		// Midi queue.
    aux_x = Y - A * X * X + 1;
    aux_y = B * X;
    X = aux_x;
    Y = aux_y;
    [henonView lockFocus];  	//Must lockFocus on the view BEFORE any drawing
    PSsetgray(1.0) ;		// or drawing set-up!
    PSsetlinewidth (1.);
    PSmoveto(viewBounds.size.width * (X+1.5)/3.,viewBounds.size.height * (Y+.5));
    PSlineto(viewBounds.size.width * (X+1.5)/3,viewBounds.size.height * (Y+.5));
    PSstroke();
    // These are common PS functions, easy to use, but slow. Don't forget to
    // PSstroke() after you draw!
    [henonView unlockFocus];
    [midiControl send:X:Y:speed];
}

-bifurcation
{
int i,j,N;
float aux_x, aux_y;
float A_aux,Height;
short *data;
char *ops;
short boundingBox[4] = {0,0,10000,10000};
short dot_loc;
NXEvent *stopEvent;

// We use the DPSDoUserPath() function to make the drawing really fast.
// This encapsulates several PS operations into one, much faster, shot.
// We calculate 150 points, put them in the data array, and draw all of them
// at the same time.
// By the same token, the event record is checked for "mouse down" events for 
// fast abort... 

data = (short*)calloc(600,sizeof(short));	// The coordinates array
ops = (char*)calloc(300,sizeof(short));		// The PS operators array

for(i=0;i<150;i++)
{
    ops[2*i] = dps_moveto;		// The PS operator array is simply
    ops[2*i+1] = dps_lineto;		// moveto,lineto,moveto etc...
}

N = (int) viewBounds.size.width;
Height = viewBounds.size.height/3.;

[henonView lockFocus];			// Don't forget this!
PSsetgray(1.0) ;
PSsetlinewidth (1.);
if(stable == 0)
for(i=0,A_aux=0;i<N;i++,A_aux += 1.4/N)
{
    for(j=0,X=Y=0;j<150;j++)
    {
	aux_x = Y - A_aux * X * X + 1;
	aux_y = B * X;
	X = aux_x;
	Y = aux_y;
	dot_loc = Height * (X+1.5);
	data[4*j] = data[4*j+2] = i;
	data[4*j+1] = data[4*j+3] = dot_loc;
	// The data array contains: x0,y0,x0,y0,x1,y1,x1,y1,etc...
	// The first couple is for moveto, the second for lineto. We get
	// then a simple point...
    }
    DPSDoUserPath(data,600,dps_short,ops,300,boundingBox,dps_ustroke);
    stopEvent = [NXApp getNextEvent:(NX_LMOUSEDOWNMASK) waitFor:0. threshold:NX_MODALRESPTHRESHOLD];		// To abort, detect mouse down
    if(stopEvent != 0) break;	    		// Events and break!
}
else						// Stable display
for(i=0,A_aux=0;i<N;i++,A_aux += 1.4/N)
{
    for(j=0,X=Y=0;j<150;j++)
    {
	aux_x = Y - A_aux * X * X + 1;
	aux_y = B * X;
	X = aux_x;
	Y = aux_y;
    }						// calculate 150 first points
    for(j=0;j<150;j++)
    {
	aux_x = Y - A_aux * X * X + 1;
	aux_y = B * X;
	X = aux_x;
	Y = aux_y;
	dot_loc = Height * (X+1.5);
	data[4*j] = data[4*j+2] = i;		// then display the next 150
	data[4*j+1] = data[4*j+3] = dot_loc;
    }
    DPSDoUserPath(data,600,dps_short,ops,300,boundingBox,dps_ustroke);
    stopEvent = [NXApp getNextEvent:(NX_LMOUSEDOWNMASK) waitFor:0. threshold:NX_MODALRESPTHRESHOLD];		// To abort, detect mouse down
    if(stopEvent != 0) break;	    		// Events and break!
}

[henonView unlockFocus];		// Don't forget that either!
return self;
}






@end
