/**********************************************************************
 *<
	FILE: noizctrl.cpp

	DESCRIPTION: A simple noise controller

	CREATED BY: Rolf Berteig

	HISTORY: created 26 August 1995

 *>	Copyright (c) 1994, All Rights Reserved.
 **********************************************************************/
#include "ctrl.h"
#include "units.h"
#include "noise.h"
#include "istdplug.h"

#define FLOATNOISE_CONTROL_CNAME	GetString(IDS_RB_NOISEFLOAT)
#define POSITIONNOISE_CONTROL_CNAME	GetString(IDS_RB_NOISEPOSITION)
#define POINT3NOISE_CONTROL_CNAME	GetString(IDS_RB_NOISEPOINT3)
#define ROTATIONNOISE_CONTROL_CNAME	GetString(IDS_RB_NOISEROTATION)
#define SCALENOISE_CONTROL_CNAME	GetString(IDS_RB_NOISESCALE)



#define MAX_ELEMS	3
										   
class BaseNoiseControl : public INoiseControl {
	public:		
		//float strength[MAX_ELEMS];
		Control *cont; // make strength animatable.
		float frequency;
		float roughness;
		int seed;
		BOOL fractal;
		BOOL lim[MAX_ELEMS];
		Interval range;
		TimeValue rampin, rampout;

		BaseNoiseControl();
		BaseNoiseControl& operator=(const BaseNoiseControl& from);
		float NoiseAtTime(TimeValue t,int s,int index);
		
		virtual int Elems()=0;

		// Animatable methods		
		void DeleteThis() {delete this;}		
		int IsKeyable() {return 0;}		
		BOOL IsAnimated() {return TRUE;}

		int NumSubs();
		Animatable* SubAnim(int i);
		TSTR SubAnimName(int i);
		BOOL AssignController(Animatable *control,int subAnim);
		int SubNumToRefNum(int subNum);

		int NumRefs();
		RefTargetHandle GetReference(int i);
		void SetReference(int i, RefTargetHandle rtarg);

		void EditTrackParams(
			TimeValue t,
			ParamDimensionBase *dim,
			TCHAR *pname,
			HWND hParent,
			IObjParam *ip,
			DWORD flags);
		
		int TrackParamsType() {return TRACKPARAMS_WHOLE;}

		// Reference methods
		RefResult NotifyRefChanged(Interval, RefTargetHandle, PartID&, RefMessage) {return REF_SUCCEED;}

		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);

		// Control methods				
		void Copy(Control *from) {}
		BOOL IsLeaf() {return TRUE;}
		void CommitValue(TimeValue t) {}
		void RestoreValue(TimeValue t) {}		
		
		// From INoiseControl
		void SetSeed(int seed);		
		void SetFrequency(float f);		
		void SetFractal(BOOL f);		
		void SetRoughness(float f);		
		void SetRampIn(TimeValue in);
		void SetRampOut(TimeValue out);		
		void SetPositiveOnly(int which,BOOL onOff);		
		void SetStrengthController(Control *c);

		float GetRoughness() {return roughness;}
		int GetSeed() {return seed;}
		float GetFrequency() {return frequency;}
		BOOL GetFractal() {return fractal;}
		TimeValue GetRampIn() {return rampin;}
		TimeValue GetRampOut() {return rampout;}
		BOOL GetPositiveOnly(int which) {return lim[which];}
		Control *GetStrengthController() {return cont;}

		void HoldRange();
		Interval GetTimeRange(DWORD flags) {return range;}
		void EditTimeRange(Interval range,DWORD flags);
		void MapKeys(TimeMap *map,DWORD flags );

		void GetStrength(TimeValue t,float *strength) {
			cont->GetValue(t,strength,FOREVER);
			}
	};

//---------------------------------------------------------------------------

class FloatNoiseControl : public BaseNoiseControl {
	public:
		int Elems() {return 1;}

		FloatNoiseControl() {
			cont=NULL;
			ReplaceReference(Control::NumRefs(),NewDefaultFloatController());
			float v = 50.0f;
			cont->SetValue(0,&v);
			}

		Class_ID ClassID() { return Class_ID(FLOATNOISE_CONTROL_CLASS_ID,0); }  
		SClass_ID SuperClassID() { return CTRL_FLOAT_CLASS_ID; } 
		void GetClassName(TSTR& s) {s = FLOATNOISE_CONTROL_CNAME;}

		// Control methods
		RefTargetHandle Clone(RemapDir& remap);

		// StdControl methods
		void GetValueLocalTime(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE);
		void SetValueLocalTime(TimeValue t, void *val, int commit, GetSetMethod method) {}		
		void Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int type);		
		void *CreateTempValue() {return new float;}
		void DeleteTempValue(void *val) {delete (float*)val;}
		void ApplyValue(void *val, void *delta) {*((float*)val) += *((float*)delta);}
		void MultiplyValue(void *val, float m) {*((float*)val) *= m;}
	};


class FloatNoiseClassDesc:public ClassDesc {
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading) { return new FloatNoiseControl(); }
	const TCHAR *	ClassName() { return FLOATNOISE_CONTROL_CNAME; }
	SClass_ID		SuperClassID() { return CTRL_FLOAT_CLASS_ID; }
	Class_ID		ClassID() { return Class_ID(FLOATNOISE_CONTROL_CLASS_ID,0); }
	const TCHAR* 	Category() { return _T("");  }
	};
static FloatNoiseClassDesc floatNoiseCD;
ClassDesc* GetFloatNoiseDesc() {return &floatNoiseCD;}

//-----------------------------------------------------------------------------

class PositionNoiseControl : public BaseNoiseControl {
	public:
		int Elems() {return 3;}

		PositionNoiseControl() {
			cont=NULL;
			ReplaceReference(Control::NumRefs(),NewDefaultPoint3Controller());
			Point3 v(50.0f,50.0f,50.0f);			
			cont->SetValue(0,&v);
			}

		Class_ID ClassID() { return Class_ID(POSITIONNOISE_CONTROL_CLASS_ID,0); }  
		SClass_ID SuperClassID() { return CTRL_POSITION_CLASS_ID; } 
		void GetClassName(TSTR& s) {s = POSITIONNOISE_CONTROL_CNAME;}

		// Control methods
		RefTargetHandle Clone(RemapDir& remap);

		// StdControl methods
		void GetValueLocalTime(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE);
		void SetValueLocalTime(TimeValue t, void *val, int commit, GetSetMethod method) {}		
		void Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int type);
		void *CreateTempValue() {return new Point3;}
		void DeleteTempValue(void *val) {delete (Point3*)val;}
		void ApplyValue(void *val, void *delta) {((Matrix3*)val)->PreTranslate(*((Point3*)delta));}
		void MultiplyValue(void *val, float m) {*((Point3*)val) *= m;}
	};


class PositionNoiseClassDesc:public ClassDesc {
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading) { return new PositionNoiseControl(); }
	const TCHAR *	ClassName() { return POSITIONNOISE_CONTROL_CNAME; }
	SClass_ID		SuperClassID() { return CTRL_POSITION_CLASS_ID; }
	Class_ID		ClassID() { return Class_ID(POSITIONNOISE_CONTROL_CLASS_ID,0); }
	const TCHAR* 	Category() { return _T("");  }
	};
static PositionNoiseClassDesc positionNoiseCD;
ClassDesc* GetPositionNoiseDesc() {return &positionNoiseCD;}

//-----------------------------------------------------------------------------

class Point3NoiseControl : public BaseNoiseControl {
	public:
		int Elems() {return 3;}

		Point3NoiseControl() {
			cont=NULL;
			ReplaceReference(Control::NumRefs(),NewDefaultPoint3Controller());
			Point3 v(50.0f,50.0f,50.0f);			
			cont->SetValue(0,&v);
			}

		Class_ID ClassID() { return Class_ID(POINT3NOISE_CONTROL_CLASS_ID,0); }  
		SClass_ID SuperClassID() { return CTRL_POINT3_CLASS_ID; } 
		void GetClassName(TSTR& s) {s = POINT3NOISE_CONTROL_CNAME;}

		// Control methods
		RefTargetHandle Clone(RemapDir& remap);

		// StdControl methods
		void GetValueLocalTime(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE);
		void SetValueLocalTime(TimeValue t, void *val, int commit, GetSetMethod method) {}		
		void Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int type);
		void *CreateTempValue() {return new Point3;}
		void DeleteTempValue(void *val) {delete (Point3*)val;}
		void ApplyValue(void *val, void *delta) {*((Point3*)val) += *((Point3*)delta);}
		void MultiplyValue(void *val, float m) {*((Point3*)val) *= m;}
	};


class Point3NoiseClassDesc:public ClassDesc {
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading) { return new Point3NoiseControl(); }
	const TCHAR *	ClassName() { return POINT3NOISE_CONTROL_CNAME; }
	SClass_ID		SuperClassID() { return CTRL_POINT3_CLASS_ID; }
	Class_ID		ClassID() { return Class_ID(POINT3NOISE_CONTROL_CLASS_ID,0); }
	const TCHAR* 	Category() { return _T("");  }
	};
static Point3NoiseClassDesc point3NoiseCD;
ClassDesc* GetPoint3NoiseDesc() {return &point3NoiseCD;}

//-----------------------------------------------------------------------------


class RotationNoiseControl : public BaseNoiseControl {
	public:
		int Elems() {return 3;}
		
		RotationNoiseControl() {
			Point3 strength;
			strength[0] = strength[1] = strength[2] = DegToRad(45);
			cont=NULL;
			ReplaceReference(Control::NumRefs(),NewDefaultPoint3Controller());
			cont->SetValue(0,&strength); 
			}

		Class_ID ClassID() { return Class_ID(ROTATIONNOISE_CONTROL_CLASS_ID,0); }  
		SClass_ID SuperClassID() { return CTRL_ROTATION_CLASS_ID; } 
		void GetClassName(TSTR& s) {s = ROTATIONNOISE_CONTROL_CNAME;}

		// Control methods
		RefTargetHandle Clone(RemapDir& remap);

		// StdControl methods
		void GetValueLocalTime(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE);
		void SetValueLocalTime(TimeValue t, void *val, int commit, GetSetMethod method) {}		
		void Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int type);
		void *CreateTempValue() {return new Quat;}
		void DeleteTempValue(void *val) {delete (Quat*)val;}
		void ApplyValue(void *val, void *delta) {PreRotateMatrix( *((Matrix3*)val), *((Quat*)delta) );}
		void MultiplyValue(void *val, float m) {*((Quat*)val) *= m;}
	};


class RotationNoiseClassDesc:public ClassDesc {
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading) { return new RotationNoiseControl(); }
	const TCHAR *	ClassName() { return ROTATIONNOISE_CONTROL_CNAME; }
	SClass_ID		SuperClassID() { return CTRL_ROTATION_CLASS_ID; }
	Class_ID		ClassID() { return Class_ID(ROTATIONNOISE_CONTROL_CLASS_ID,0); }
	const TCHAR* 	Category() { return _T("");  }
	};
static RotationNoiseClassDesc rotationNoiseCD;
ClassDesc* GetRotationNoiseDesc() {return &rotationNoiseCD;}

//-----------------------------------------------------------------------------

class ScaleNoiseControl : public BaseNoiseControl {
	public:
		int Elems() {return 3;}

		ScaleNoiseControl() {
			Point3 strength;
			strength[0] = strength[1] = strength[2] = 0.5f;
			cont=NULL;
			ReplaceReference(Control::NumRefs(),NewDefaultPoint3Controller());
			cont->SetValue(0,&strength); 
			}

		Class_ID ClassID() { return Class_ID(SCALENOISE_CONTROL_CLASS_ID,0); }  
		SClass_ID SuperClassID() { return CTRL_SCALE_CLASS_ID; } 
		void GetClassName(TSTR& s) {s = SCALENOISE_CONTROL_CNAME;}

		// Control methods
		RefTargetHandle Clone(RemapDir& remap);

		// StdControl methods
		void GetValueLocalTime(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE);
		void SetValueLocalTime(TimeValue t, void *val, int commit, GetSetMethod method) {}		
		void Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int type);
		void *CreateTempValue() {return new ScaleValue;}
		void DeleteTempValue(void *val) {delete (ScaleValue*)val;}
		void ApplyValue(void *val, void *delta) {ApplyScaling( *((Matrix3*)val), *((ScaleValue*)delta) );}
		void MultiplyValue(void *val, float m) {*((ScaleValue*)val) *= m;}
	};


class ScaleNoiseClassDesc:public ClassDesc {
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading) { return new ScaleNoiseControl(); }
	const TCHAR *	ClassName() { return SCALENOISE_CONTROL_CNAME; }
	SClass_ID		SuperClassID() { return CTRL_SCALE_CLASS_ID; }
	Class_ID		ClassID() { return Class_ID(SCALENOISE_CONTROL_CLASS_ID,0);}
	const TCHAR* 	Category() { return _T("");  }
	};
static ScaleNoiseClassDesc scaleNoiseCD;
ClassDesc* GetScaleNoiseDesc() {return &scaleNoiseCD;}


//-----------------------------------------------------------------------------


class RangeRestore : public RestoreObj {
	public:
		BaseNoiseControl *cont;
		Interval ur, rr;
		RangeRestore(BaseNoiseControl *c) 
			{
			cont = c;
			ur   = cont->range;
			}   		
		void Restore(int isUndo) 
			{
			rr = cont->range;
			cont->range = ur;
			cont->NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
			}
		void Redo()
			{
			cont->range = rr;
			cont->NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
			}		
		void EndHold() 
			{ 
			cont->ClearAFlag(A_HELD);
			}
		TSTR Description() { return TSTR(_T("Noise control range")); }
	};


//-----------------------------------------------------------------
//
// UI

static LRESULT CALLBACK NoiseGraphWinProc(
		HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

static void InitNoiseGraph(HINSTANCE hInst)
	{
	static BOOL init = FALSE;
	if (init) return;
	else init = TRUE;

	WNDCLASS wc;
	wc.style         = 0;
    wc.hInstance     = hInst;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.lpfnWndProc   = NoiseGraphWinProc;
    wc.lpszClassName = _T("NoiseGraph");
		
	RegisterClass(&wc);	
	}


#define STEPSIZE			4
#define GRAPH_TIMESCALE		20

static void DrawNoiseGraph(BaseNoiseControl *cont,HWND hWnd,HDC hdc)
	{
	Rect rect, orect;
	GetClientRect(hWnd,&rect);
	orect = rect;
	float v, fy;
	int mid = rect.h()/2;
	rect.top    += 5;
	rect.bottom -= 5;
	TimeValue end = rect.right * GRAPH_TIMESCALE;

	SelectObject(hdc,CreatePen(PS_DOT,0,GetSysColor(COLOR_BTNFACE)));
	
	MoveToEx(hdc,0,mid,NULL);
	LineTo(hdc,rect.right,mid);
	MoveToEx(hdc,cont->rampin / GRAPH_TIMESCALE,0,NULL);
	LineTo(hdc,cont->rampin / GRAPH_TIMESCALE,rect.bottom);
	MoveToEx(hdc,rect.right - cont->rampout / GRAPH_TIMESCALE,0,NULL);
	LineTo(hdc,rect.right - cont->rampout / GRAPH_TIMESCALE,rect.bottom);

	DeleteObject(SelectObject(hdc,GetStockObject(BLACK_PEN)));

	for (int x = 0; x < rect.right + STEPSIZE; x += STEPSIZE) {
		int t = x*GRAPH_TIMESCALE;
		float ramp = 1.0f;
		
		if (t<cont->rampin) {
			float u = float(t)/float(cont->rampin);
			ramp *= u*u * (3.0f-2.0f*u);
			}
		if (t>end-cont->rampout) {
			float u = float(end-t)/float(cont->rampout);
			ramp *= u*u * (3.0f-2.0f*u);
			}

		v = float(t) * float(0.005) * cont->frequency + Perm(cont->seed);
		if (cont->fractal) {
			fy = (float)fBm1(v, 1.0f-cont->roughness, 2.0f, 6 /*octaves*/);
		} else {
			fy = noise1(v);
			}
		fy *= ramp;

		if (!x) {
			MoveToEx(hdc,0,mid + int((rect.h()) * fy),NULL);
		} else {
			LineTo(hdc,x,mid + int((rect.h()) * fy));
			}
		}

	WhiteRect3D(hdc,orect,TRUE);
	}

static LRESULT CALLBACK NoiseGraphWinProc(
		HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
	BaseNoiseControl *cont = (BaseNoiseControl*)GetWindowLong(hWnd,GWL_USERDATA);
	if (!cont) return DefWindowProc(hWnd,msg,wParam,lParam);

	switch (msg) {		
		case WM_PAINT: {
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint(hWnd,&ps);
			DrawNoiseGraph(cont,hWnd,hdc);
			EndPaint(hWnd,&ps);
			break;
			}

		default:
			return DefWindowProc(hWnd,msg,wParam,lParam);
		}
	
	return 0;
	}

//--------------------------------------------------------------

static BOOL CALLBACK NoiseDlgProc(
		HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

static int strengthSpinID[] = {IDC_NOISE_XSTRENGTHSPIN,IDC_NOISE_YSTRENGTHSPIN,IDC_NOISE_ZSTRENGTHSPIN};
static int strengthEditID[] = {IDC_NOISE_XSTRENGTH,IDC_NOISE_YSTRENGTH,IDC_NOISE_ZSTRENGTH};
static int strengthLabelID[] = {IDC_NOISE_XSTRENGTHLABEL,IDC_NOISE_YSTRENGTHLABEL,IDC_NOISE_ZSTRENGTHLABEL};
static int limID[] = {IDC_NOISE_XLIM0,IDC_NOISE_YLIM0,IDC_NOISE_ZLIM0};

#define NOISEDLG_CLASS_ID	0xaab659c4


class NoiseCtrlWindow {
	public:
		HWND hWnd;
		HWND hParent;
		Control *cont;
		NoiseCtrlWindow() {assert(0);}
		NoiseCtrlWindow(HWND hWnd,HWND hParent,Control *cont)
			{this->hWnd=hWnd; this->hParent=hParent; this->cont=cont;}
	};
static Tab<NoiseCtrlWindow> noiseCtrlWindows;

static void RegisterNoiseCtrlWindow(HWND hWnd, HWND hParent, Control *cont)
	{
	NoiseCtrlWindow rec(hWnd,hParent,cont);
	noiseCtrlWindows.Append(1,&rec);
	}

static void UnRegisterNoiseCtrlWindow(HWND hWnd)
	{	
	for (int i=0; i<noiseCtrlWindows.Count(); i++) {
		if (hWnd==noiseCtrlWindows[i].hWnd) {
			noiseCtrlWindows.Delete(i,1);
			return;
			}
		}	
	}

static HWND FindOpenNoiseCtrlWindow(HWND hParent,Control *cont)
	{	
	for (int i=0; i<noiseCtrlWindows.Count(); i++) {
		if (hParent == noiseCtrlWindows[i].hParent &&
			cont    == noiseCtrlWindows[i].cont) {
			return noiseCtrlWindows[i].hWnd;
			}
		}
	return NULL;
	}

class NoiseDlg : public ReferenceMaker, public TimeChangeCallback {
	public:
		BaseNoiseControl *cont;	
		ParamDimensionBase *dim;
		IObjParam *ip;
		HWND hWnd;
		BOOL valid;
		int elems;
		ISpinnerControl *iStrength[MAX_ELEMS];
		ISpinnerControl *iSeed, *iFreq, *iRough, *iRampIn, *iRampOut;

		NoiseDlg(
			BaseNoiseControl *cont,
			ParamDimensionBase *dim,
			TCHAR *pname,
			IObjParam *ip,
			HWND hParent);
		~NoiseDlg();

		Class_ID ClassID() {return Class_ID(NOISEDLG_CLASS_ID,0);}
		SClass_ID SuperClassID() {return REF_MAKER_CLASS_ID;}

		void MaybeCloseWindow();

		void TimeChanged(TimeValue t) {Invalidate();}

		void Invalidate();
		void Update();
		void SetupUI(HWND hWnd);
		void Change(BOOL redraw=FALSE);
		void WMCommand(int id, int notify, HWND hCtrl);
		void SpinnerChange(int id,BOOL drag);
		void SpinnerStart(int id);
		void SpinnerEnd(int id,BOOL cancel);

		RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, 
	         PartID& partID,  RefMessage message);
		int NumRefs() {return 1;}
		RefTargetHandle GetReference(int i) {return cont;}
		void SetReference(int i, RefTargetHandle rtarg) {cont=(BaseNoiseControl*)rtarg;}
	};


NoiseDlg::NoiseDlg(
		BaseNoiseControl *cont,
		ParamDimensionBase *dim,
		TCHAR *pname,
		IObjParam *ip,
		HWND hParent)
	{
	InitNoiseGraph(hInstance);
	this->cont = cont;
	this->ip   = ip;
	this->dim  = dim;
	valid = FALSE;
	elems = cont->Elems();

	theHold.Suspend();
	MakeRefByID(FOREVER,0,cont);
	theHold.Resume();

	hWnd = CreateDialogParam(
		hInstance,
		MAKEINTRESOURCE(IDD_NOISEPARAMS),
		hParent,
		NoiseDlgProc,
		(LPARAM)this);	
	TSTR title = TSTR(GetString(IDS_RB_NOISECONTROLTITLE)) + TSTR(pname);
	SetWindowText(hWnd,title);
	ip->RegisterTimeChangeCallback(this);
	}

NoiseDlg::~NoiseDlg()
	{
	UnRegisterNoiseCtrlWindow(hWnd);
	ip->UnRegisterTimeChangeCallback(this);

	theHold.Suspend();
	DeleteAllRefsFromMe();
	theHold.Resume();

	ReleaseISpinner(iSeed);
	ReleaseISpinner(iFreq);
	ReleaseISpinner(iRough);
	ReleaseISpinner(iRampIn);
	ReleaseISpinner(iRampOut);
	for (int i=0; i<elems; i++) {
		ReleaseISpinner(iStrength[i]);
		}
	}

void NoiseDlg::Invalidate()
	{
	valid = FALSE;
	InvalidateRect(hWnd,NULL,FALSE);	
	InvalidateRect(GetDlgItem(hWnd,IDC_NOISE_GRAPH),NULL,FALSE);
	}

void NoiseDlg::Update()
	{
	if (!valid && hWnd) {
		float strength[MAX_ELEMS];
		cont->GetStrength(ip->GetTime(),strength);

		for (int i=0; i<elems; i++) {			
			iStrength[i]->SetValue(dim->Convert(strength[i]),FALSE);
			CheckDlgButton(hWnd,limID[i],cont->lim[i]);
			}
		iSeed->SetValue(cont->seed,FALSE);
		iFreq->SetValue(cont->frequency,FALSE);
		iRough->SetValue(cont->roughness,FALSE);
		iRampIn->SetValue(cont->rampin,FALSE);
		iRampOut->SetValue(cont->rampout,FALSE);
		if (cont->fractal) {
			CheckDlgButton(hWnd,IDC_NOISE_FRACTAL,TRUE);
			EnableWindow(GetDlgItem(hWnd,IDC_NOISE_ROUGHLABEL),TRUE);
			EnableWindow(GetDlgItem(hWnd,IDC_NOISE_ROUGHSPIN),TRUE);
		} else {
			CheckDlgButton(hWnd,IDC_NOISE_FRACTAL,FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_NOISE_ROUGHLABEL),FALSE);
			EnableWindow(GetDlgItem(hWnd,IDC_NOISE_ROUGHSPIN),FALSE);
			}
		valid = TRUE;
		}
	}

void NoiseDlg::SetupUI(HWND hWnd)
	{
	this->hWnd = hWnd;

	for (int i=0; i<elems; i++) {
		iStrength[i] = GetISpinner(GetDlgItem(hWnd,strengthSpinID[i]));
		iStrength[i]->SetLimits(-9999999,9999999,FALSE);
		iStrength[i]->SetAutoScale();
		iStrength[i]->LinkToEdit(GetDlgItem(hWnd,strengthEditID[i]),EDITTYPE_FLOAT);		
		}
	for ( ; i< MAX_ELEMS; i++) {
		ShowWindow(GetDlgItem(hWnd,strengthSpinID[i]),SW_HIDE);
		ShowWindow(GetDlgItem(hWnd,strengthEditID[i]),SW_HIDE);
		ShowWindow(GetDlgItem(hWnd,strengthLabelID[i]),SW_HIDE);
		ShowWindow(GetDlgItem(hWnd,limID[i]),SW_HIDE);
		}

	iSeed = GetISpinner(GetDlgItem(hWnd,IDC_NOISE_SEEDSPIN));
	iSeed->SetLimits(0,9999999,FALSE);
	iSeed->SetScale(0.1f);
	iSeed->LinkToEdit(GetDlgItem(hWnd,IDC_NOISE_SEED),EDITTYPE_INT);	

	iFreq = GetISpinner(GetDlgItem(hWnd,IDC_NOISE_FREQSPIN));
	iFreq->SetLimits(0.001f,10.0f,FALSE);
	iFreq->SetScale(0.005f);
	iFreq->LinkToEdit(GetDlgItem(hWnd,IDC_NOISE_FREQ),EDITTYPE_FLOAT);	

	iRough = GetISpinner(GetDlgItem(hWnd,IDC_NOISE_ROUGHSPIN));
	iRough->SetLimits(0.0f,1.0f,FALSE);
	iRough->SetScale(0.005f);
	iRough->LinkToEdit(GetDlgItem(hWnd,IDC_NOISE_ROUGH),EDITTYPE_FLOAT);	
	
	iRampIn = GetISpinner(GetDlgItem(hWnd,IDC_NOISE_RAMPINSPIN));
	iRampIn->SetLimits(0,TIME_PosInfinity,FALSE);
	iRampIn->SetScale(10.0f);
	iRampIn->LinkToEdit(GetDlgItem(hWnd,IDC_NOISE_RAMPIN),EDITTYPE_TIME);	
	
	iRampOut = GetISpinner(GetDlgItem(hWnd,IDC_NOISE_RAMPOUTSPIN));
	iRampOut->SetLimits(0,TIME_PosInfinity,FALSE);
	iRampOut->SetScale(10.0f);
	iRampOut->LinkToEdit(GetDlgItem(hWnd,IDC_NOISE_RAMPOUT),EDITTYPE_TIME);	
	
	if (elems==1) {		
		SetWindowText(GetDlgItem(hWnd,IDC_NOISE_XSTRENGTHLABEL),
			GetString(IDS_RB_STRENGTH));
		}
	
	SetWindowLong(GetDlgItem(hWnd,IDC_NOISE_GRAPH),GWL_USERDATA,(LONG)cont);
	valid = FALSE;
	Update();
	}

void NoiseDlg::WMCommand(int id, int notify, HWND hCtrl)
	{
	switch (id) {
		case IDC_NOISE_FRACTAL:
			cont->fractal = IsDlgButtonChecked(hWnd,id);
			Change(TRUE);
			break;

		case IDC_NOISE_XLIM0:
			cont->lim[0] = IsDlgButtonChecked(hWnd,id);
			Change(TRUE);
			break;
		case IDC_NOISE_YLIM0:
			cont->lim[1] = IsDlgButtonChecked(hWnd,id);
			Change(TRUE);
			break;
		case IDC_NOISE_ZLIM0:
			cont->lim[2] = IsDlgButtonChecked(hWnd,id);
			Change(TRUE);
			break;
		}
	}

void NoiseDlg::SpinnerChange(int id,BOOL drag)
	{
	float strength[MAX_ELEMS];
	BOOL strengthChange = FALSE;
	
	if (!drag) {
		SpinnerStart(id);
		}

	if (id==IDC_NOISE_XSTRENGTHSPIN ||
		id==IDC_NOISE_YSTRENGTHSPIN ||
		id==IDC_NOISE_ZSTRENGTHSPIN) {
		cont->GetStrength(ip->GetTime(),strength);
		}

	switch (id) {
		case IDC_NOISE_XSTRENGTHSPIN:
			strength[0] = dim->UnConvert(iStrength[0]->GetFVal());
			strengthChange = TRUE;
			Change(1);
			break;

		case IDC_NOISE_YSTRENGTHSPIN:
			strength[1] = dim->UnConvert(iStrength[1]->GetFVal());
			strengthChange = TRUE;
			Change(1);
			break;

		case IDC_NOISE_ZSTRENGTHSPIN:
			strength[2] = dim->UnConvert(iStrength[2]->GetFVal());
			strengthChange = TRUE;
			Change(1);
			break;

		case IDC_NOISE_SEEDSPIN:
			cont->seed = iSeed->GetIVal();
			Change(1);
			break;

		case IDC_NOISE_FREQSPIN:
			cont->frequency = iFreq->GetFVal();
			Change(1);
			break;	
		
		case IDC_NOISE_ROUGHSPIN:
			cont->roughness = iRough->GetFVal();
			Change(1);
			break;			

		case IDC_NOISE_RAMPINSPIN:
			cont->rampin = iRampIn->GetIVal();
			Change(1);
			break;

		case IDC_NOISE_RAMPOUTSPIN:
			cont->rampout = iRampOut->GetIVal();
			Change(1);
			break;
		}
	
	if (strengthChange) {
		cont->cont->SetValue(ip->GetTime(),strength);
		}
	}

void NoiseDlg::SpinnerStart(int id)
	{
	switch (id) {
		case IDC_NOISE_XSTRENGTHSPIN:
		case IDC_NOISE_YSTRENGTHSPIN:
		case IDC_NOISE_ZSTRENGTHSPIN:
			theHold.Begin();
			break;
		}
	}

void NoiseDlg::SpinnerEnd(int id,BOOL cancel)
	{
	switch (id) {
		case IDC_NOISE_XSTRENGTH:
		case IDC_NOISE_YSTRENGTH:
		case IDC_NOISE_ZSTRENGTH:
		case IDC_NOISE_XSTRENGTHSPIN:
		case IDC_NOISE_YSTRENGTHSPIN:
		case IDC_NOISE_ZSTRENGTHSPIN:
			if (cancel) {
				theHold.Cancel();
			} else {
				theHold.Accept(GetString(IDS_RB_CHANGESTRENGTH));
				}
			break;
		}
	ip->RedrawViews(ip->GetTime());
	}

void NoiseDlg::Change(BOOL redraw)
	{
	InvalidateRect(GetDlgItem(hWnd,IDC_NOISE_GRAPH),NULL,TRUE);
	cont->NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
	UpdateWindow(GetDlgItem(hWnd,IDC_NOISE_GRAPH));
	UpdateWindow(GetParent(hWnd));	
	if (redraw) ip->RedrawViews(ip->GetTime());
	}


class CheckForNonNoiseDlg : public DependentEnumProc {
	public:		
		BOOL non;
		ReferenceMaker *me;
		CheckForNonNoiseDlg(ReferenceMaker *m) {non = FALSE;me = m;}
		int proc(ReferenceMaker *rmaker) {
			if (rmaker==me) return 0;
			if (rmaker->SuperClassID()!=REF_MAKER_CLASS_ID &&
				rmaker->ClassID()!=Class_ID(NOISEDLG_CLASS_ID,0)) {
				non = TRUE;
				return 1;
				}
			return 0;
			}
	};
void NoiseDlg::MaybeCloseWindow()
	{
	CheckForNonNoiseDlg check(cont);
	cont->EnumDependents(&check);
	if (!check.non) {
		PostMessage(hWnd,WM_CLOSE,0,0);
		}
	}



RefResult NoiseDlg::NotifyRefChanged(
		Interval changeInt, 
		RefTargetHandle hTarget, 
     	PartID& partID,  
     	RefMessage message)
	{
	switch (message) {
		case REFMSG_CHANGE:
			Invalidate();			
			break;
		
		case REFMSG_REF_DELETED:
			MaybeCloseWindow();
			break;
		}
	return REF_SUCCEED;
	}


static BOOL CALLBACK NoiseDlgProc(
		HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
	NoiseDlg *dlg = (NoiseDlg*)GetWindowLong(hWnd,GWL_USERDATA);

	switch (msg) {
		case WM_INITDIALOG:
			dlg = (NoiseDlg*)lParam;
			SetWindowLong(hWnd,GWL_USERDATA,lParam);
			dlg->SetupUI(hWnd);
			break;

		case CC_SPINNER_BUTTONDOWN:
			dlg->SpinnerStart(LOWORD(wParam));
			break;

		case CC_SPINNER_CHANGE:
			dlg->SpinnerChange(LOWORD(wParam),HIWORD(wParam));
			break;

		case WM_CUSTEDIT_ENTER:
		case CC_SPINNER_BUTTONUP:
			dlg->SpinnerEnd(LOWORD(wParam),!HIWORD(wParam));
			break;

		case WM_COMMAND:
			dlg->WMCommand(LOWORD(wParam),HIWORD(wParam),(HWND)lParam);						
			break;

		case WM_PAINT:
			dlg->Update();
			return 0;			
		
		case WM_CLOSE:
			DestroyWindow(hWnd);			
			break;

		case WM_DESTROY:						
			delete dlg;
			break;
		
		default:
			return FALSE;
		}
	return TRUE;
	}

void BaseNoiseControl::EditTrackParams(
		TimeValue t,
		ParamDimensionBase *dim,
		TCHAR *pname,
		HWND hParent,
		IObjParam *ip,
		DWORD flags)
	{
	HWND hCur = FindOpenNoiseCtrlWindow(hParent,this);
	if (hCur) {
		SetForegroundWindow(hCur);
		return;
		}

	NoiseDlg *dlg = new NoiseDlg(this,dim,pname,ip,hParent);
	RegisterNoiseCtrlWindow(dlg->hWnd,hParent,this);
	}

//------------------------------------------------------------

BaseNoiseControl::BaseNoiseControl() 
	{	
	for (int i=0; i<MAX_ELEMS; i++) {
		//strength[i] = 50.0f;
		lim[i] = FALSE;
		}
	fractal   = TRUE;
	roughness = 0.0f;
	frequency = 0.5f;	
	seed      = 0;	
	rampin = rampout = 0;
	range     = Interval(GetAnimStart(),GetAnimEnd());
	} 

BaseNoiseControl& BaseNoiseControl::operator=(const BaseNoiseControl& from)
	{
	ReplaceReference(Control::NumRefs(),from.cont);
	for (int i=0; i<MAX_ELEMS; i++) {
		//strength[i]  = from.strength[i];
		lim[i] = from.lim[i];
		}
	fractal   = from.fractal;
	roughness = from.roughness;
	frequency = from.frequency;
	seed      = from.seed;
	range     = from.range;
	rampin    = from.rampin;
	rampout   = from.rampout;
	return *this;
	}

int BaseNoiseControl::NumSubs() 
	{
	return Control::NumSubs()+1;
	}

Animatable* BaseNoiseControl::SubAnim(int i) 
	{
	if (i<Control::NumSubs()) return Control::SubAnim(i);
	return cont;
	}

TSTR BaseNoiseControl::SubAnimName(int i) 
	{
	if (i<Control::NumSubs()) 
		return Control::SubAnimName(i);
	return GetString(IDS_RB_NOISESTRENGTH);
	}

BOOL BaseNoiseControl::AssignController(Animatable *control,int subAnim) 
	{
	ReplaceReference(Control::NumRefs(),(Control*)control);
	return TRUE;
	}

int BaseNoiseControl::SubNumToRefNum(int subNum) 
	{	
	return Control::NumRefs();
	}

int BaseNoiseControl::NumRefs() 
	{
	return Control::NumRefs()+1;
	}

RefTargetHandle BaseNoiseControl::GetReference(int i) 
	{
	if (i<Control::NumRefs()) return Control::GetReference(i);
	return cont;
	}

void BaseNoiseControl::SetReference(int i, RefTargetHandle rtarg) 
	{
	if (i<Control::NumRefs()) Control::SetReference(i,rtarg);
	else cont = (Control*)rtarg;
	}

extern float noise1(float arg);

float BaseNoiseControl::NoiseAtTime(TimeValue t,int s,int index)
	{
	float ramp = 1.0f, res;
	
	// Limit to in range
	if (t<range.Start()) t = range.Start();
	if (t>range.End()) t = range.End();	
	
	// Compute ramping
	if (t<range.Start()+rampin) {
		float u = float(t-range.Start())/float(rampin);
		ramp *= u*u * (3.0f-2.0f*u);
		}
	if (t>range.End()-rampout) {
		float u = float(range.End()-t)/float(rampout);
		ramp *= u*u * (3.0f-2.0f*u);
		}

	// Compute noise
	float v = float(t) * float(0.005) * frequency + Perm(s);	
	if (fractal) {
		res = (float)fBm1(v, 1.0f-roughness, 2.0f, 6 /*octaves*/);
	} else {
		res = noise1(v);
		}
	if (lim[index]) {
		res = res+0.5f;
		}
	return res * ramp;
	}

void BaseNoiseControl::HoldRange()
	{
	if (theHold.Holding() && !TestAFlag(A_HELD)) {
		SetAFlag(A_HELD);
		theHold.Put(new RangeRestore(this));
		}
	}

void BaseNoiseControl::EditTimeRange(Interval range,DWORD flags)
	{
	if (!(flags&EDITRANGE_LINKTOKEYS)) {
		HoldRange();
		this->range = range;
		NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
		}
	}

void BaseNoiseControl::MapKeys(TimeMap *map,DWORD flags)
	{
	if (flags&TRACK_MAPRANGE) {
		HoldRange();
		TimeValue t0 = map->map(range.Start());
		TimeValue t1 = map->map(range.End());
		range.Set(t0,t1);
		NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
		}
	}


#define XSTRENGTH_CHUNK		0x0100
#define YSTRENGTH_CHUNK		0x0101
#define ZSTRENGTH_CHUNK		0x0102
#define FREQUENCY_CHUNK		0x0103
#define ROUGHNESS_CHUNK		0x0104
#define SEED_CHUNK			0x0105
#define FRACTAL_CHUNK		0x0106
#define RANGE_CHUNK			0x0107
#define RAMPIN_CHUNK		0x0108
#define RAMPOUT_CHUNK		0x0109
#define XLIM_CHUNK			0x0110
#define YLIM_CHUNK			0x0111
#define ZLIM_CHUNK			0x0112

IOResult BaseNoiseControl::Save(ISave *isave)
	{		
	ULONG nb;	

	for (int i=0; i<MAX_ELEMS; i++) {
		//isave->BeginChunk(XSTRENGTH_CHUNK+i);
		//isave->Write(&strength[i],sizeof(strength[i]),&nb);
		//isave->EndChunk();

		isave->BeginChunk(XLIM_CHUNK+i);
		isave->Write(&lim[i],sizeof(lim[i]),&nb);
		isave->EndChunk();
		}

	isave->BeginChunk(FREQUENCY_CHUNK);
	isave->Write(&frequency,sizeof(frequency),&nb);
	isave->EndChunk();
	
	isave->BeginChunk(ROUGHNESS_CHUNK);
	isave->Write(&roughness,sizeof(roughness),&nb);
	isave->EndChunk();
	
	isave->BeginChunk(SEED_CHUNK);
	isave->Write(&seed,sizeof(seed),&nb);
	isave->EndChunk();
	
	isave->BeginChunk(FRACTAL_CHUNK);
	isave->Write(&fractal,sizeof(fractal),&nb);
	isave->EndChunk();
	
	isave->BeginChunk(RANGE_CHUNK);
	isave->Write(&range,sizeof(range),&nb);
	isave->EndChunk();
	
	isave->BeginChunk(RAMPIN_CHUNK);
	isave->Write(&rampin,sizeof(rampin),&nb);
	isave->EndChunk();
	
	isave->BeginChunk(RAMPOUT_CHUNK);
	isave->Write(&rampout,sizeof(rampout),&nb);
	isave->EndChunk();
	
	return IO_OK;
	}

IOResult BaseNoiseControl::Load(ILoad *iload)
	{
	float strength[MAX_ELEMS];
	BOOL strengthRead=FALSE;
	ULONG nb;
	IOResult res = IO_OK;
	while (IO_OK==(res=iload->OpenChunk())) {
		switch (iload->CurChunkID()) {
			case XSTRENGTH_CHUNK:
				res=iload->Read(&strength[0],sizeof(strength[0]),&nb);
				strengthRead = TRUE;
				break;

			case YSTRENGTH_CHUNK:
				res=iload->Read(&strength[1],sizeof(strength[1]),&nb);
				strengthRead = TRUE;
				break;

			case ZSTRENGTH_CHUNK:
				res=iload->Read(&strength[2],sizeof(strength[2]),&nb);
				strengthRead = TRUE;
				break;

			case XLIM_CHUNK:
				res=iload->Read(&lim[0],sizeof(lim[0]),&nb);
				break;
			case YLIM_CHUNK:
				res=iload->Read(&lim[1],sizeof(lim[1]),&nb);
				break;
			case ZLIM_CHUNK:
				res=iload->Read(&lim[2],sizeof(lim[2]),&nb);
				break;

			case FREQUENCY_CHUNK:
				res=iload->Read(&frequency,sizeof(frequency),&nb);
				break;

			case ROUGHNESS_CHUNK:
				res=iload->Read(&roughness,sizeof(roughness),&nb);
				break;

			case SEED_CHUNK:
				res=iload->Read(&seed,sizeof(seed),&nb);
				break;

			case FRACTAL_CHUNK:
				res=iload->Read(&fractal,sizeof(fractal),&nb);
				break;

			case RANGE_CHUNK:
				res=iload->Read(&range,sizeof(range),&nb);
				break;

			case RAMPIN_CHUNK:
				res=iload->Read(&rampin,sizeof(rampin),&nb);
				break;

			case RAMPOUT_CHUNK:
				res=iload->Read(&rampout,sizeof(rampout),&nb);
				break;
			}

		iload->CloseChunk();
		if (res!=IO_OK)  return res;
		}
	
	if (strengthRead) {
		cont->SetValue(0,strength);
		}
	return IO_OK;
	}


void BaseNoiseControl::SetSeed(int seed)
	{
	this->seed = seed;
	NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
	}

void BaseNoiseControl::SetFrequency(float f)
	{
	frequency = f;
	NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
	}

void BaseNoiseControl::SetFractal(BOOL f)
	{
	fractal = f;
	NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
	}

void BaseNoiseControl::SetRoughness(float f)
	{
	roughness = f;
	NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
	}

void BaseNoiseControl::SetRampIn(TimeValue in)
	{
	rampin = in;
	NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
	}

void BaseNoiseControl::SetRampOut(TimeValue out)
	{
	rampout = out;
	NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
	}

void BaseNoiseControl::SetPositiveOnly(int which,BOOL onOff)
	{
	lim[which] = onOff;
	NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
	}

void BaseNoiseControl::SetStrengthController(Control *c)
	{
	ReplaceReference(Control::NumRefs(),c);
	NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
	}



//--------------------------------------------------------------------

RefTargetHandle FloatNoiseControl::Clone(RemapDir& remap)
	{
	// make a new noise controller and give it our param values.
	FloatNoiseControl *cont = new FloatNoiseControl;
	*cont = *this;
	// Clone the strength controller
	cont->ReplaceReference(Control::NumRefs(),remap.CloneRef(cont->cont));
	CloneControl(cont,remap);
	return cont;
	}

void FloatNoiseControl::GetValueLocalTime(
		TimeValue t, void *val, Interval &valid, GetSetMethod method)
	{
	float strength;
	GetStrength(t,&strength);

	// This controller is always changing.
	valid.SetInstant(t);
	
	*((float*)val) = NoiseAtTime(t,seed,0) * strength;
	}

void FloatNoiseControl::Extrapolate(
		Interval range,TimeValue t,void *val,Interval &valid,int type)
	{
	float val0, val1, val2, res;
	switch (type) {
		case ORT_LINEAR:			
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				GetValueLocalTime(range.Start()+1,&val1,valid);
				res = LinearExtrapolate(range.Start(),t,val0,val1,val0);				
			} else {
				GetValueLocalTime(range.End()-1,&val0,valid);
				GetValueLocalTime(range.End(),&val1,valid);
				res = LinearExtrapolate(range.End(),t,val0,val1,val1);
				}
			break;

		case ORT_IDENTITY:
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				res = IdentityExtrapolate(range.Start(),t,val0);
			} else {
				GetValueLocalTime(range.End(),&val0,valid);
				res = IdentityExtrapolate(range.End(),t,val0);
				}
			break;

		case ORT_RELATIVE_REPEAT:
			GetValueLocalTime(range.Start(),&val0,valid);
			GetValueLocalTime(range.End(),&val1,valid);
			GetValueLocalTime(CycleTime(range,t),&val2,valid);
			res = RepeatExtrapolate(range,t,val0,val1,val2);			
			break;
		}
	valid.Set(t,t);
	*((float*)val) = res;
	}

//--------------------------------------------------------------------------

RefTargetHandle PositionNoiseControl::Clone(RemapDir& remap)
	{
	// make a new noise controller and give it our param values.
	PositionNoiseControl *cont = new PositionNoiseControl;
	*cont = *this;
	// Clone the strength controller
	cont->ReplaceReference(Control::NumRefs(),remap.CloneRef(cont->cont));
	CloneControl(cont,remap);
	return cont;
	}

void PositionNoiseControl::GetValueLocalTime(
		TimeValue t, void *val, Interval &valid, GetSetMethod method)
	{
	float strength[3];
	GetStrength(t,strength);

	// This controller is always changing.
	valid.SetInstant(t);
	
	*((Point3*)val) = Point3(
		NoiseAtTime(t,seed,0) * strength[0],
		NoiseAtTime(t,seed+1,1) * strength[1],
		NoiseAtTime(t,seed+2,2) * strength[2] );
	}

void PositionNoiseControl::Extrapolate(
		Interval range,TimeValue t,void *val,Interval &valid,int type)
	{
	Point3 val0, val1, val2, res;
	switch (type) {
		case ORT_LINEAR:			
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				GetValueLocalTime(range.Start()+1,&val1,valid);
				res = LinearExtrapolate(range.Start(),t,val0,val1,val0);				
			} else {
				GetValueLocalTime(range.End()-1,&val0,valid);
				GetValueLocalTime(range.End(),&val1,valid);
				res = LinearExtrapolate(range.End(),t,val0,val1,val1);
				}
			break;

		case ORT_IDENTITY:
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				res = IdentityExtrapolate(range.Start(),t,val0);
			} else {
				GetValueLocalTime(range.End(),&val0,valid);
				res = IdentityExtrapolate(range.End(),t,val0);
				}
			break;

		case ORT_RELATIVE_REPEAT:
			GetValueLocalTime(range.Start(),&val0,valid);
			GetValueLocalTime(range.End(),&val1,valid);
			GetValueLocalTime(CycleTime(range,t),&val2,valid);
			res = RepeatExtrapolate(range,t,val0,val1,val2);			
			break;
		}
	valid.Set(t,t);
	*((Point3*)val) = res;
	}

//--------------------------------------------------------------------------


RefTargetHandle Point3NoiseControl::Clone(RemapDir& remap)
	{
	// make a new noise controller and give it our param values.
	Point3NoiseControl *cont = new Point3NoiseControl;
	*cont = *this;	
	// Clone the strength controller
	cont->ReplaceReference(Control::NumRefs(),remap.CloneRef(cont->cont));
	CloneControl(cont,remap);
	return cont;
	}

void Point3NoiseControl::GetValueLocalTime(
		TimeValue t, void *val, Interval &valid, GetSetMethod method)
	{
	float strength[3];
	GetStrength(t,strength);

	// This controller is always changing.
	valid.SetInstant(t);
	
	*((Point3*)val) = Point3(
		NoiseAtTime(t,seed,0) * strength[0],
		NoiseAtTime(t,seed+1,1) * strength[1],
		NoiseAtTime(t,seed+2,2) * strength[2] );
	}

void Point3NoiseControl::Extrapolate(
		Interval range,TimeValue t,void *val,Interval &valid,int type)
	{
	Point3 val0, val1, val2, res;
	switch (type) {
		case ORT_LINEAR:			
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				GetValueLocalTime(range.Start()+1,&val1,valid);
				res = LinearExtrapolate(range.Start(),t,val0,val1,val0);				
			} else {
				GetValueLocalTime(range.End()-1,&val0,valid);
				GetValueLocalTime(range.End(),&val1,valid);
				res = LinearExtrapolate(range.End(),t,val0,val1,val1);
				}
			break;

		case ORT_IDENTITY:
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				res = IdentityExtrapolate(range.Start(),t,val0);
			} else {
				GetValueLocalTime(range.End(),&val0,valid);
				res = IdentityExtrapolate(range.End(),t,val0);
				}
			break;

		case ORT_RELATIVE_REPEAT:
			GetValueLocalTime(range.Start(),&val0,valid);
			GetValueLocalTime(range.End(),&val1,valid);
			GetValueLocalTime(CycleTime(range,t),&val2,valid);
			res = RepeatExtrapolate(range,t,val0,val1,val2);			
			break;
		}
	valid.Set(t,t);
	*((Point3*)val) = res;
	}

//--------------------------------------------------------------------------



RefTargetHandle RotationNoiseControl::Clone(RemapDir& remap)
	{
	// make a new noise controller and give it our param values.
	RotationNoiseControl *cont = new RotationNoiseControl;
	*cont = *this;	
	// Clone the strength controller
	cont->ReplaceReference(Control::NumRefs(),remap.CloneRef(cont->cont));
	CloneControl(cont,remap);
	return cont;
	}

void RotationNoiseControl::GetValueLocalTime(
		TimeValue t, void *val, Interval &valid, GetSetMethod method)
	{
	float strength[3];
	GetStrength(t,strength);

	// This controller is always changing.
	valid.SetInstant(t);
	float ang[3];

	ang[0] = NoiseAtTime(t,seed,0) * strength[0];
	ang[1] = NoiseAtTime(t,seed+1,1) * strength[1];
	ang[2] = NoiseAtTime(t,seed+2,2) * strength[2];
	
	EulerToQuat(ang,*((Quat*)val));		
	}

void RotationNoiseControl::Extrapolate(
		Interval range,TimeValue t,void *val,Interval &valid,int type)
	{
	Quat val0, val1, val2, res;
	switch (type) {
		case ORT_LINEAR:			
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				GetValueLocalTime(range.Start()+1,&val1,valid);
				res = LinearExtrapolate(range.Start(),t,val0,val1,val0);				
			} else {
				GetValueLocalTime(range.End()-1,&val0,valid);
				GetValueLocalTime(range.End(),&val1,valid);
				res = LinearExtrapolate(range.End(),t,val0,val1,val1);
				}
			break;

		case ORT_IDENTITY:
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				res = IdentityExtrapolate(range.Start(),t,val0);
			} else {
				GetValueLocalTime(range.End(),&val0,valid);
				res = IdentityExtrapolate(range.End(),t,val0);
				}
			break;

		case ORT_RELATIVE_REPEAT:
			GetValueLocalTime(range.Start(),&val0,valid);
			GetValueLocalTime(range.End(),&val1,valid);
			GetValueLocalTime(CycleTime(range,t),&val2,valid);
			res = RepeatExtrapolate(range,t,val0,val1,val2);			
			break;
		}
	valid.Set(t,t);
	*((Quat*)val) = res;
	}

//--------------------------------------------------------------------------



RefTargetHandle ScaleNoiseControl::Clone(RemapDir& remap)
	{
	// make a new noise controller and give it our param values.
	ScaleNoiseControl *cont = new ScaleNoiseControl;
	*cont = *this;
	// Clone the strength controller
	cont->ReplaceReference(Control::NumRefs(),remap.CloneRef(cont->cont));
	CloneControl(cont,remap);
	return cont;
	}

void ScaleNoiseControl::GetValueLocalTime(
		TimeValue t, void *val, Interval &valid, GetSetMethod method)
	{
	float strength[3];
	GetStrength(t,strength);

	// This controller is always changing.
	valid.SetInstant(t);
	
	*((ScaleValue*)val) = ScaleValue(
		Point3(
			1.0f + NoiseAtTime(t,seed,0) * strength[0],
			1.0f + NoiseAtTime(t,seed+1,1) * strength[1],
			1.0f + NoiseAtTime(t,seed+2,2) * strength[2] ),
		Quat(0.0,0.0,0.0,1.0));
	}

void ScaleNoiseControl::Extrapolate(
		Interval range,TimeValue t,void *val,Interval &valid,int type)
	{
	ScaleValue val0, val1, val2, res;
	switch (type) {
		case ORT_LINEAR:			
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				GetValueLocalTime(range.Start()+1,&val1,valid);
				res = LinearExtrapolate(range.Start(),t,val0,val1,val0);				
			} else {
				GetValueLocalTime(range.End()-1,&val0,valid);
				GetValueLocalTime(range.End(),&val1,valid);
				res = LinearExtrapolate(range.End(),t,val0,val1,val1);
				}
			break;

		case ORT_IDENTITY:
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				res = IdentityExtrapolate(range.Start(),t,val0);
			} else {
				GetValueLocalTime(range.End(),&val0,valid);
				res = IdentityExtrapolate(range.End(),t,val0);
				}
			break;

		case ORT_RELATIVE_REPEAT:
			GetValueLocalTime(range.Start(),&val0,valid);
			GetValueLocalTime(range.End(),&val1,valid);
			GetValueLocalTime(CycleTime(range,t),&val2,valid);
			res = RepeatExtrapolate(range,t,val0,val1,val2);			
			break;
		}
	valid.Set(t,t);
	*((ScaleValue*)val) = res;
	}





