//===========================================================================//
// File:	fcurve.cpp                                                          //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
//           BS   Creation                                                   //
//---------------------------------------------------------------------------//
// Copyright (C) 1997-1998 Virtual World Entertainment, Inc.                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "gosFXHeaders.hpp"

//##########################################################################
//########################      Curve          #############################
//##########################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
gosFX::Curve::IsComplex()
{
	Check_Object(this);
	return
		m_type==e_ComplexLinearType     ||
		m_type==e_ComplexComplexType	||
		m_type==e_ComplexSplineType		||
		m_type==e_ConstantComplexType	||
		m_type==e_ConstantLinearType	||
		m_type==e_ConstantSplineType	||
		m_type==e_SplineSplineType	||
		m_type==e_SplineLinearType;		
	
}

void
gosFX::Curve::Save(Stuff::MemoryStream *stream)
{
	
	Check_Object(this);
	
	switch(m_type)
	{
		
	case e_ComplexLinearType:
		{
			SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *SCurve=(SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *)this;
			 SCurve->Save(stream);
		}	
		break;
		
	case e_ComplexComplexType:
		{
			SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *SCurve=(SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *)this;
			 SCurve->Save(stream);
		}	
		break;
		
	case e_ComplexSplineType:
		{
			SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *SCurve=(SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *)this;
			 SCurve->Save(stream);
		}
		break;
		
	case e_ConstantComplexType:
		{
			SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *SCurve=(SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *)this;
			 SCurve->Save(stream);
		}
		break;
		
	case e_ConstantLinearType:
		{
			SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *SCurve=(SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *)this;
			 SCurve->Save(stream);
		}
		break;
		
	case e_ConstantSplineType:
		{
			SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *SCurve=(SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *)this;
			 SCurve->Save(stream);
		}
		break;
		
	case e_SplineLinearType:
		{
			SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *SCurve=(SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *)this;
			 SCurve->Save(stream);
		}
		break;
		
	case 	e_SplineSplineType:
		{
			SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *SCurve=(SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *)this;
			 SCurve->Save(stream);
		}
		break;
		
	default:
		break;
	}
	
}

void
	gosFX::Curve::Load(
		Stuff::MemoryStream *stream,
		int gfx_version
	)
{
	
	Check_Object(this);
	
	switch(m_type)
	{
		
	case e_ComplexLinearType:
		{
			SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *SCurve=(SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *)this;
			 SCurve->Load(stream, gfx_version);
		}	
		break;
		
	case e_ComplexComplexType:
		{
			SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *SCurve=(SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *)this;
			 SCurve->Load(stream, gfx_version);
		}	
		break;
		
	case e_ComplexSplineType:
		{
			SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *SCurve=(SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *)this;
			 SCurve->Load(stream, gfx_version);
		}
		break;
		
	case e_ConstantComplexType:
		{
			SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *SCurve=(SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *)this;
			 SCurve->Load(stream, gfx_version);
		}
		break;
		
	case e_ConstantLinearType:
		{
			SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *SCurve=(SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *)this;
			 SCurve->Load(stream, gfx_version);
		}
		break;
		
	case e_ConstantSplineType:
		{
			SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *SCurve=(SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *)this;
			 SCurve->Load(stream, gfx_version);
		}
		break;
		
	case e_SplineLinearType:
		{
			SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *SCurve=(SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *)this;
			 SCurve->Load(stream, gfx_version);
		}
		break;
		
	case 	e_SplineSplineType:
		{
			SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *SCurve=(SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *)this;
			 SCurve->Load(stream, gfx_version);
		}
		break;
		
	default:
		break;
	}

}

void 
gosFX::Curve::SetSeedFlagIfComplex(bool vflag)
{
	Check_Object(this);
	
	switch(m_type)
	{
		
	case e_ComplexLinearType:
		{
			SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *SCurve=(SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *)this;
			SCurve->m_seeded=vflag;
		}	
		break;
		
	case e_ComplexComplexType:
		{
			SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *SCurve=(SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *)this;
			SCurve->m_seeded=vflag;
		}	
		break;
		
	case e_ComplexSplineType:
		{
			SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *SCurve=(SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *)this;
			SCurve->m_seeded=vflag;
		}
		break;
		
	case e_ConstantComplexType:
		{
			SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *SCurve=(SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *)this;
			SCurve->m_seeded=vflag;
		}
		break;
		
	case e_ConstantLinearType:
		{
			SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *SCurve=(SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *)this;
			SCurve->m_seeded=vflag;
		}
		break;
		
	case e_ConstantSplineType:
		{
			SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *SCurve=(SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *)this;
			SCurve->m_seeded=vflag;
		}
		break;
		
	case e_SplineLinearType:
		{
			SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *SCurve=(SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *)this;
			SCurve->m_seeded=vflag;
		}
		break;
		
	case 	e_SplineSplineType:
		{
			SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *SCurve=(SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *)this;
			SCurve->m_seeded=vflag;
		}
		break;
		
	default:
		break;
	}
	
}

int 
gosFX::Curve::GetSeedFlagIfComplex()
{
	Check_Object(this);
	
	switch(m_type)
	{
		
	case e_ComplexLinearType:
		{
			SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *SCurve=(SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *)this;
			return SCurve->m_seeded?1:0;
		}	
		break;
		
	case e_ComplexComplexType:
		{
			SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *SCurve=(SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *)this;
			return SCurve->m_seeded?1:0;
		}	
		break;
		
	case e_ComplexSplineType:
		{
			SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *SCurve=(SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *)this;
			return SCurve->m_seeded?1:0;
		}
		break;
		
	case e_ConstantComplexType:
		{
			SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *SCurve=(SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *)this;
			return SCurve->m_seeded?1:0;
		}
		break;
		
	case e_ConstantLinearType:
		{
			SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *SCurve=(SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *)this;
			return SCurve->m_seeded?1:0;
		}
		break;
		
	case e_ConstantSplineType:
		{
			SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *SCurve=(SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *)this;
			return SCurve->m_seeded?1:0;
		}
		break;
		
	case e_SplineLinearType:
		{
			SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *SCurve=(SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *)this;
			return SCurve->m_seeded?1:0;
		}
		break;
		
	case 	e_SplineSplineType:
		{
			SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *SCurve=(SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *)this;
			return SCurve->m_seeded?1:0;
		}
		break;
		
	default:
		break;
	}
	return -1;
}




Stuff::Scalar 
gosFX::Curve::ExpensiveCompute(Stuff::Scalar tme,int curvenum)
{
	
	Check_Object(this);
	
	switch(m_type)
	{
	case e_ConstantType:
		{
			ConstantCurve *SCurve=(ConstantCurve *)this;
			return SCurve->ComputeValue(tme,0.0f);
		}
		break;
		
	case e_LinearType:
		{
			LinearCurve *SCurve=(LinearCurve *)this;
			return SCurve->ComputeValue(tme,0.0f);
		}
		break;
		
	case e_SplineType:
		{
			SplineCurve *SCurve=(SplineCurve *)this;
			return SCurve->ComputeValue(tme,0.0f);
		}
		break;
		
	case e_ComplexType:
		{
			ComplexCurve *SCurve=(ComplexCurve *)this;
			return SCurve->ComputeValue(tme,0.0f);
		}
		break;
		
	case e_ComplexLinearType:
		{
			SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *SCurve=(SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *)this;
			if(curvenum==0)
				return SCurve->m_ageCurve.ExpensiveCompute(tme,curvenum);
			else
				return SCurve->m_seedCurve.ExpensiveCompute(tme,curvenum);
		}	
		break;
		
	case e_ComplexComplexType:
		{
			SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *SCurve=(SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *)this;
			if(curvenum==0)
				return SCurve->m_ageCurve.ExpensiveCompute(tme,curvenum);
			else
				return SCurve->m_seedCurve.ExpensiveCompute(tme,curvenum);
		}	
		break;
		
	case e_ComplexSplineType:
		{
			SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *SCurve=(SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *)this;
			if(curvenum==0)
				return SCurve->m_ageCurve.ExpensiveCompute(tme,curvenum);
			else
				return SCurve->m_seedCurve.ExpensiveCompute(tme,curvenum);
		}
		break;
		
	case e_ConstantComplexType:
		{
			SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *SCurve=(SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *)this;
			if(curvenum==0)
				return SCurve->m_ageCurve.ExpensiveCompute(tme,curvenum);
			else
				return SCurve->m_seedCurve.ExpensiveCompute(tme,curvenum);
		}
		break;
		
	case e_ConstantLinearType:
		{
			SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *SCurve=(SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *)this;
			if(curvenum==0)
				return SCurve->m_ageCurve.ExpensiveCompute(tme,curvenum);
			else
				return SCurve->m_seedCurve.ExpensiveCompute(tme,curvenum);
		}
		break;
		
	case e_ConstantSplineType:
		{
			SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *SCurve=(SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *)this;
			if(curvenum==0)
				return SCurve->m_ageCurve.ExpensiveCompute(tme,curvenum);
			else
				return SCurve->m_seedCurve.ExpensiveCompute(tme,curvenum);
		}
		break;
		
	case e_SplineLinearType:
		{
			SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *SCurve=(SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *)this;
			if(curvenum==0)
				return SCurve->m_ageCurve.ExpensiveCompute(tme,curvenum);
			else
				return SCurve->m_seedCurve.ExpensiveCompute(tme,curvenum);
		}
		break;
		
	case e_SplineSplineType:
		{
			SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *SCurve=(SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *)this;
			if(curvenum==0)
				return SCurve->m_ageCurve.ExpensiveCompute(tme,curvenum);
			else
				return SCurve->m_seedCurve.ExpensiveCompute(tme,curvenum);
		}
		break;
		
	default:
		break;
	}
	
	return 0.0f;
}

void 
gosFX::Curve::ExpensiveComputeRange(
									Stuff::Scalar *low,
									Stuff::Scalar *hi,
									int curvenum
									)
{
	Check_Object(this);
	Check_Pointer(low);
	Check_Pointer(hi);
	
	switch(m_type)
	{
	case e_ConstantType:
		{
			ConstantCurve *SCurve=(ConstantCurve *)this;
			SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_LinearType:
		{
			LinearCurve *SCurve=(LinearCurve *)this;
			SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_SplineType:
		{
			SplineCurve *SCurve=(SplineCurve *)this;
			SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_ComplexType:
		{
			ComplexCurve *SCurve=(ComplexCurve *)this;
			SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_ComplexLinearType:
		{
			SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *SCurve=(SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.ComputeRange(low,hi);
			else
				SCurve->m_seedCurve.ComputeRange(low,hi);
		}	
		break;
		
	case e_ComplexComplexType:
		{
			SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *SCurve=(SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.ComputeRange(low,hi);
			else
				SCurve->m_seedCurve.ComputeRange(low,hi);
		}	
		break;
		
	case e_ComplexSplineType:
		{
			SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *SCurve=(SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.ComputeRange(low,hi);
			else
				SCurve->m_seedCurve.ComputeRange(low,hi);
		}
		break;
		
	case e_ConstantComplexType:
		{
			SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *SCurve=(SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.ComputeRange(low,hi);
			else
				SCurve->m_seedCurve.ComputeRange(low,hi);
		}
		break;
		
	case e_ConstantLinearType:
		{
			SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *SCurve=(SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.ComputeRange(low,hi);
			else
				SCurve->m_seedCurve.ComputeRange(low,hi);
		}
		break;
		
	case e_ConstantSplineType:
		{
			SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *SCurve=(SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.ComputeRange(low,hi);
			else
				SCurve->m_seedCurve.ComputeRange(low,hi);
		}
		break;
		
	case e_SplineLinearType:
		{
			SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *SCurve=(SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.ComputeRange(low,hi);
			else
				SCurve->m_seedCurve.ComputeRange(low,hi);
		}
		break;
		
	case e_SplineSplineType:
		{
			SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *SCurve=(SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.ComputeRange(low,hi);
			else
				SCurve->m_seedCurve.ComputeRange(low,hi);
		}
		break;
		
	default:
		break;
	}
	
}

void 
gosFX::Curve::ExpensiveComputeRange(
									Stuff::Scalar *low,
									Stuff::Scalar *hi
									)
{
	Check_Object(this);
	Check_Pointer(low);
	Check_Pointer(hi);
	
	switch(m_type)
	{
	case e_ConstantType:
		{
			ConstantCurve *SCurve=(ConstantCurve *)this;
			SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_LinearType:
		{
			LinearCurve *SCurve=(LinearCurve *)this;
			SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_SplineType:
		{
			SplineCurve *SCurve=(SplineCurve *)this;
			SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_ComplexType:
		{
			ComplexCurve *SCurve=(ComplexCurve *)this;
			SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_ComplexLinearType:
		{
			SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *SCurve=(SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *)this;
				SCurve->ComputeRange(low,hi);
		}	
		break;
		
	case e_ComplexComplexType:
		{
			SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *SCurve=(SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *)this;
				SCurve->ComputeRange(low,hi);
		}	
		break;
		
	case e_ComplexSplineType:
		{
			SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *SCurve=(SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *)this;
				SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_ConstantComplexType:
		{
			SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *SCurve=(SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *)this;
				SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_ConstantLinearType:
		{
			SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *SCurve=(SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *)this;
				SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_ConstantSplineType:
		{
			SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *SCurve=(SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *)this;
				SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_SplineLinearType:
		{
			SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *SCurve=(SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *)this;
				SCurve->ComputeRange(low,hi);
		}
		break;
		
	case e_SplineSplineType:
		{
			SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *SCurve=(SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *)this;
				SCurve->ComputeRange(low,hi);
		}
		break;
		
	default:
		break;
	}
	
}


Stuff::Scalar 
gosFX::Curve::Mid(int curvenum)
{
	Stuff::Scalar min,max;
	ExpensiveComputeRange(&min,&max,curvenum);
	return min+(max-min)*0.5f;
}

void 
gosFX::Curve::TranslateTo(Stuff::Scalar pos,int curvenum)
{
	Check_Object(this);
	
	switch(m_type)
	{
	case e_ConstantType:
		{
			ConstantCurve *SCurve=(ConstantCurve *)this;
			SCurve->SetCurve(pos);
		}
		break;
		
	case e_LinearType:
		{
			LinearCurve *SCurve=(LinearCurve *)this;
			Stuff::Scalar s;
			s=SCurve->ComputeSlope(0.0f);
			SCurve->SetCurve(pos,pos+s);
		}
		break;
		
	case e_SplineType:
		{
			SplineCurve *SCurve=(SplineCurve *)this;
			Stuff::Scalar v0,v1,s0,s1;
			v0=SCurve->ComputeValue(0.0f,0.0f);
			v1=SCurve->ComputeValue(1.0f,0.0f);
			s0=SCurve->ComputeSlope(0.0f);
			s1=SCurve->ComputeSlope(1.0f);
			
			v1+=pos-v0;
			v0=pos;
			
			SCurve->SetCurve(v0,s0,v1,s1);
		}
		break;
		
	case e_ComplexType:
		{
			ComplexCurve *SCurve=(ComplexCurve *)this;
			Stuff::Scalar delta,x1,x2,y1,y2,slp;
			
			delta=pos-SCurve->ComputeValue(0.0f,0.0f);
			
			for(int key=0;key<SCurve->GetKeyCount()-1;key++)
			{
				x1=(*SCurve)[key].m_time;
				x2=(*SCurve)[key+1].m_time;
				y1=SCurve->ComputeValue(x1,0.0f);
				y2=SCurve->ComputeValue(x2,0.0f);
				y1+=delta;
				y2+=delta;
				(*SCurve)[key].SetLinearKey(x1,y1,y2,x2-x1);
			}
			
			x1=(*SCurve)[key].m_time;
			y1=SCurve->ComputeValue(x1,0.0f);
			y1+=delta;
			slp=(*SCurve)[key].m_slope;
			(*SCurve)[SCurve->GetKeyCount()-1].SetLinearKey(x1,y1,y1+slp*0.1f,0.1f);
			
		}
		break;
		
	case e_ComplexLinearType:
		{
			SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *SCurve=(SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateTo(pos,curvenum);
			else
				SCurve->m_seedCurve.TranslateTo(pos,curvenum);
		}	
		break;
		
	case e_ComplexComplexType:
		{
			SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *SCurve=(SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateTo(pos,curvenum);
			else
				SCurve->m_seedCurve.TranslateTo(pos,curvenum);
		}	
		break;
		
	case e_ComplexSplineType:
		{
			SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *SCurve=(SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateTo(pos,curvenum);
			else
				SCurve->m_seedCurve.TranslateTo(pos,curvenum);
		}
		break;
		
	case e_ConstantComplexType:
		{
			SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *SCurve=(SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateTo(pos,curvenum);
			else
				SCurve->m_seedCurve.TranslateTo(pos,curvenum);
		}
		break;
		
	case e_ConstantLinearType:
		{
			SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *SCurve=(SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateTo(pos,curvenum);
			else
				SCurve->m_seedCurve.TranslateTo(pos,curvenum);
		}
		break;
		
	case e_ConstantSplineType:
		{
			SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *SCurve=(SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateTo(pos,curvenum);
			else
				SCurve->m_seedCurve.TranslateTo(pos,curvenum);
		}
		break;
		
	case e_SplineLinearType:
		{
			SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *SCurve=(SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateTo(pos,curvenum);
			else
				SCurve->m_seedCurve.TranslateTo(pos,curvenum);
		}
		break;
		
	case e_SplineSplineType:
		{
			SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *SCurve=(SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateTo(pos,curvenum);
			else
				SCurve->m_seedCurve.TranslateTo(pos,curvenum);
		}
		break;
		
	default:
		break;
	}
	
}

void 
gosFX::Curve::TranslateBy(Stuff::Scalar delta,int curvenum)
{
	Check_Object(this);
	
	switch(m_type)
	{
	case e_ConstantType:
		{
			ConstantCurve *SCurve=(ConstantCurve *)this;
			SCurve->SetCurve(SCurve->ComputeValue(0.0f,0.0f)+delta);
		}
		break;
		
	case e_LinearType:
		{
			LinearCurve *SCurve=(LinearCurve *)this;
			Stuff::Scalar s,v;
			s=SCurve->ComputeSlope(0.0f);
			v=SCurve->ComputeValue(0.0f,0.0f);
			SCurve->SetCurve(v+delta,v+delta+s);
		}
		break;
		
	case e_SplineType:
		{
			SplineCurve *SCurve=(SplineCurve *)this;
			Stuff::Scalar v0,v1,s0,s1;
			v0=SCurve->ComputeValue(0.0f,0.0f);
			v1=SCurve->ComputeValue(1.0f,0.0f);
			s0=SCurve->ComputeSlope(0.0f);
			s1=SCurve->ComputeSlope(1.0f);
			
			v1+=delta;
			v0+=delta;
			
			SCurve->SetCurve(v0,s0,v1,s1);
		}
		break;
		
	case e_ComplexType:
		{
			ComplexCurve *SCurve=(ComplexCurve *)this;
			Stuff::Scalar x1,x2,y1,y2;
			
			
			for(int key=0;key<SCurve->GetKeyCount()-1;key++)
			{
				x1=(*SCurve)[key].m_time;
				x2=(*SCurve)[key+1].m_time;
				y1=SCurve->ComputeValue(x1,0.0f);
				y2=SCurve->ComputeValue(x2,0.0f);
				y1+=delta;
				y2+=delta;
				(*SCurve)[key].SetLinearKey(x1,y1,y2,x2-x1);
			}
			
			x1=(*SCurve)[key].m_time;
			y1=SCurve->ComputeValue(x1,0.0f);
			y1+=delta;
			(*SCurve)[SCurve->GetKeyCount()-1].SetLinearKey(x1,y1,y1,1.0f);
			
		}
		break;
		
	case e_ComplexLinearType:
		{
			SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *SCurve=(SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateBy(delta,curvenum);
			else
				SCurve->m_seedCurve.TranslateBy(delta,curvenum);
		}	
		break;
		
	case e_ComplexComplexType:
		{
			SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *SCurve=(SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateBy(delta,curvenum);
			else
				SCurve->m_seedCurve.TranslateBy(delta,curvenum);
		}	
		break;
		
	case e_ComplexSplineType:
		{
			SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *SCurve=(SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateBy(delta,curvenum);
			else
				SCurve->m_seedCurve.TranslateBy(delta,curvenum);
		}
		break;
		
	case e_ConstantComplexType:
		{
			SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *SCurve=(SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateBy(delta,curvenum);
			else
				SCurve->m_seedCurve.TranslateBy(delta,curvenum);
		}
		break;
		
	case e_ConstantLinearType:
		{
			SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *SCurve=(SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateBy(delta,curvenum);
			else
				SCurve->m_seedCurve.TranslateBy(delta,curvenum);
		}
		break;
		
	case e_ConstantSplineType:
		{
			SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *SCurve=(SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateBy(delta,curvenum);
			else
				SCurve->m_seedCurve.TranslateBy(delta,curvenum);
		}
		break;
		
	case e_SplineLinearType:
		{
			SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *SCurve=(SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateBy(delta,curvenum);
			else
				SCurve->m_seedCurve.TranslateBy(delta,curvenum);
		}
		break;
		
	case e_SplineSplineType:
		{
			SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *SCurve=(SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.TranslateBy(delta,curvenum);
			else
				SCurve->m_seedCurve.TranslateBy(delta,curvenum);
		}
		break;
		
	default:
		break;
	}
	
}

void 
gosFX::Curve::LocalScale(Stuff::Scalar sfactor,int curvenum)
{
	Check_Object(this);
	AxisScale(sfactor,Mid(curvenum),curvenum);
}

void 
gosFX::Curve::AxisScale(Stuff::Scalar sfactor,Stuff::Scalar axis,int curvenum)
{
	Check_Object(this);
	switch(m_type)
	{
	case e_ConstantType:
		{
			ConstantCurve *SCurve=(ConstantCurve *)this;
			SCurve->SetCurve(axis+(SCurve->ComputeValue(0.0f,0.0f)-axis)*sfactor);
		}
		break;
		
	case e_LinearType:
		{
			LinearCurve *SCurve=(LinearCurve *)this;
			Stuff::Scalar v0,v1;
			v0=SCurve->ComputeValue(0.0f,0.0f);
			v1=SCurve->ComputeValue(1.0f,0.0f);
			v0=axis+(v0-axis)*sfactor;
			v1=axis+(v1-axis)*sfactor;
			SCurve->SetCurve(v0,v1);
		}
		break;
		
	case e_SplineType:
		{
			SplineCurve *SCurve=(SplineCurve *)this;
			Stuff::Scalar v0,v1,s0,s1;
			v0=SCurve->ComputeValue(0.0f,0.0f);
			v1=SCurve->ComputeValue(1.0f,0.0f);
			s0=SCurve->ComputeSlope(0.0f);
			s1=SCurve->ComputeSlope(1.0f);
			
			v0=axis+(v0-axis)*sfactor;
			v1=axis+(v1-axis)*sfactor;
			s0*=sfactor;
			s1*=sfactor;
			
			SCurve->SetCurve(v0,s0,v1,s1);
		}
		break;
		
	case e_ComplexType:
		{
			ComplexCurve *SCurve=(ComplexCurve *)this;
			Stuff::Scalar x1,x2,y1,y2;
			
			
			for(int key=0;key<SCurve->GetKeyCount()-1;key++)
			{
				x1=(*SCurve)[key].m_time;
				x2=(*SCurve)[key+1].m_time;
				y1=SCurve->ComputeValue(x1,0.0f);
				y2=SCurve->ComputeValue(x2,0.0f);
				y1=axis+(y1-axis)*sfactor;
				y2=axis+(y2-axis)*sfactor;
				(*SCurve)[key].SetLinearKey(x1,y1,y2,x2-x1);
			}
			
			x1=(*SCurve)[key].m_time;
			x2=1.0f;
			y1=SCurve->ComputeValue(x1,0.0f);
			y2=SCurve->ComputeValue(x2,0.0f);
			y1=axis+(y1-axis)*sfactor;
			y2=axis+(y2-axis)*sfactor;
			(*SCurve)[key].SetLinearKey(x1,y1,y2,x2-x1);
			
		}
		break;
		
	case e_ComplexLinearType:
		{
			SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *SCurve=(SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.AxisScale(sfactor,axis,curvenum);
			else
				SCurve->m_seedCurve.AxisScale(sfactor,axis,curvenum);
		}	
		break;
		
	case e_ComplexComplexType:
		{
			SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *SCurve=(SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.AxisScale(sfactor,axis,curvenum);
			else
				SCurve->m_seedCurve.AxisScale(sfactor,axis,curvenum);
		}	
		break;
		
	case e_ComplexSplineType:
		{
			SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *SCurve=(SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.AxisScale(sfactor,axis,curvenum);
			else
				SCurve->m_seedCurve.AxisScale(sfactor,axis,curvenum);
		}
		break;
		
	case e_ConstantComplexType:
		{
			SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *SCurve=(SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.AxisScale(sfactor,axis,curvenum);
			else
				SCurve->m_seedCurve.AxisScale(sfactor,axis,curvenum);
		}
		break;
		
	case e_ConstantLinearType:
		{
			SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *SCurve=(SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.AxisScale(sfactor,axis,curvenum);
			else
				SCurve->m_seedCurve.AxisScale(sfactor,axis,curvenum);
		}
		break;
		
	case e_ConstantSplineType:
		{
			SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *SCurve=(SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.AxisScale(sfactor,axis,curvenum);
			else
				SCurve->m_seedCurve.AxisScale(sfactor,axis,curvenum);
		}
		break;
		
	case e_SplineLinearType:
		{
			SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *SCurve=(SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.AxisScale(sfactor,axis,curvenum);
			else
				SCurve->m_seedCurve.AxisScale(sfactor,axis,curvenum);
		}
		break;
		
	case e_SplineSplineType:
		{
			SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *SCurve=(SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *)this;
			if(curvenum==0)
				SCurve->m_ageCurve.AxisScale(sfactor,axis,curvenum);
			else
				SCurve->m_seedCurve.AxisScale(sfactor,axis,curvenum);
		}
		break;
		
	default:
		break;
	}
	
}


gosFX::Curve * 
gosFX::Curve::GetSubCurve(int curvenum)
{
	Check_Object(this);
	
	switch(m_type)
	{
		
	case e_ConstantType:
	case e_LinearType:
	case e_SplineType:
	case e_ComplexType:
		{
			if(curvenum==0)
				return this;
			else
				return NULL;
		}	
		break;
		
	case e_ComplexLinearType:
		{
			SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *SCurve=(SeededCurveOf<ComplexCurve, LinearCurve,Curve::e_ComplexLinearType> *)this;
			if(curvenum==0)
				return &(SCurve->m_ageCurve);
			else
				return &(SCurve->m_seedCurve);
		}	
		break;
		
	case e_ComplexComplexType:
		{
			SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *SCurve=(SeededCurveOf<ComplexCurve, ComplexCurve,e_ComplexComplexType> *)this;
			if(curvenum==0)
				return &(SCurve->m_ageCurve);
			else
				return &(SCurve->m_seedCurve);
		}	
		break;
		
	case e_ComplexSplineType:
		{
			SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *SCurve=(SeededCurveOf<ComplexCurve, SplineCurve,e_ComplexSplineType> *)this;
			if(curvenum==0)
				return &(SCurve->m_ageCurve);
			else
				return &(SCurve->m_seedCurve);
		}
		break;
		
	case e_ConstantComplexType:
		{
			SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *SCurve=(SeededCurveOf<ConstantCurve,ComplexCurve,e_ConstantComplexType> *)this;
			if(curvenum==0)
				return &(SCurve->m_ageCurve);
			else
				return &(SCurve->m_seedCurve);
		}
		break;
		
	case e_ConstantLinearType:
		{
			SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *SCurve=(SeededCurveOf<ConstantCurve,LinearCurve,e_ConstantLinearType> *)this;
			if(curvenum==0)
				return &(SCurve->m_ageCurve);
			else
				return &(SCurve->m_seedCurve);
		}
		break;
		
	case e_ConstantSplineType:
		{
			SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *SCurve=(SeededCurveOf<ConstantCurve,SplineCurve,e_ConstantSplineType> *)this;
			if(curvenum==0)
				return &(SCurve->m_ageCurve);
			else
				return &(SCurve->m_seedCurve);
		}
		break;
		
	case e_SplineLinearType:
		{
			SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *SCurve=(SeededCurveOf<SplineCurve,LinearCurve,e_SplineLinearType> *)this;
			if(curvenum==0)
				return &(SCurve->m_ageCurve);
			else
				return &(SCurve->m_seedCurve);
		}
		break;
		
	case e_SplineSplineType:
		{
			SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *SCurve=(SeededCurveOf<SplineCurve,SplineCurve,e_SplineSplineType> *)this;
			if(curvenum==0)
				return &(SCurve->m_ageCurve);
			else
				return &(SCurve->m_seedCurve);
		}
		break;
		
	default:
		break;
	}
	return NULL;
}



//##########################################################################
//########################    ConstantCurve    #############################
//##########################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::ConstantCurve::Save(Stuff::MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);
	
	*stream << m_value;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void 
	gosFX::ConstantCurve::Load(
		Stuff::MemoryStream *stream,
		int gfx_version
	)
{
	Check_Pointer(this);
	Check_Object(stream);
	
	*stream >> m_value;
}

//##########################################################################
//#########################    LinearCurve    ##############################
//##########################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::LinearCurve::Save(Stuff::MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);
	
	BaseClass::Save(stream);
	*stream << m_slope;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void 
	gosFX::LinearCurve::Load(
		Stuff::MemoryStream *stream,
		int gfx_version
	)
{
	Check_Pointer(this);
	Check_Object(stream);
	
	BaseClass::Load(stream, gfx_version);
	*stream >> m_slope;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// return:  true=math good, false=math unstable
bool
gosFX::LinearCurve::SetCurve(Stuff::Scalar v)
{
	Check_Object(this);
	
	m_slope = 0.0f;
	m_value = v;
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// return:  true=math good, false=math unstable
bool
gosFX::LinearCurve::SetCurve(
							 Stuff::Scalar v0,
							 Stuff::Scalar v1
							 )
{
	Check_Object(this);
	
	m_slope = v1 - v0;
	m_value = v0;
	return Stuff::Close_Enough(m_slope + v0, v1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::LinearCurve::ComputeRange(
								 Stuff::Scalar *low,
								 Stuff::Scalar *hi
								 )
{
	Check_Object(this);
	Check_Pointer(low);
	Check_Pointer(hi);
	
	//
	//------------------------------------------------------------------------
	// We know that we will have to test the function at the beginning and end
	// of the segment, so go ahead and do that now
	//------------------------------------------------------------------------
	//
	*hi = *low = m_value;
	Stuff::Scalar t = m_slope+m_value;
	if (t>*hi)
		*hi = t;
	else if (t<*low)
		*low = t;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	gosFX::LinearCurve::SetMinimum(Stuff::Scalar minimum)
{
	Check_Object(this);
	
	//
	//---------------------------------
	// Clamp the end-points and rebuild
	//---------------------------------
	//
	Stuff::Scalar v1 = m_value;
	Stuff::Scalar v2 = m_slope+m_value;
	Min_Clamp(v1, minimum);
	Min_Clamp(v2, minimum);
	SetCurve(v1, v2);
	#if defined(_ARMOR)
		Stuff::Scalar low, hi;
		ComputeRange(&low, &hi);
		Verify(low >= minimum && hi >= minimum);
	#endif
}

//##########################################################################
//##########################    SplineCurve    ##############################
//##########################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
gosFX::SplineCurve&
gosFX::SplineCurve::operator=(const SplineCurve& curve)
{
	m_a = curve.m_a;
	m_b = curve.m_b;
	m_slope = curve.m_slope;
	m_value = curve.m_value;
	return *this;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::SplineCurve::Save(Stuff::MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);
	
	BaseClass::Save(stream);
	*stream << m_b << m_a;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void 
	gosFX::SplineCurve::Load(
		Stuff::MemoryStream *stream,
		int gfx_version
	)
{
	Check_Pointer(this);
	Check_Object(stream);
	
	BaseClass::Load(stream, gfx_version);
	*stream >> m_b >> m_a;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// return:  true=math good, false=math unstable
bool
gosFX::SplineCurve::SetCurve(Stuff::Scalar v)
{
	Check_Object(this);
	
	m_a = m_b = m_slope = 0.0f;
	m_value = v;
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// return:  true=math good, false=math unstable
bool
gosFX::SplineCurve::SetCurve(
							 Stuff::Scalar v0,
							 Stuff::Scalar v1
							 )
{
	Check_Object(this);
	
	m_a = 0.0f;
	m_b = 0.0f;
	m_slope = v1 - v0;
	m_value = v0;
	return Stuff::Close_Enough(m_slope + v0, v1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// return:  true=math good, false=math unstable
bool
gosFX::SplineCurve::SetCurve(
							 Stuff::Scalar v0,
							 Stuff::Scalar s0,
							 Stuff::Scalar v1,
							 Stuff::Scalar s1
							 )
{
	Check_Object(this);
	
	m_a = (s1+s0) + 2.0f*(v0-v1);
	m_b = (s1 - s0 - 3.0f*m_a) * 0.5f;
	m_slope = s0;
	m_value = v0;
	return (
		Stuff::Close_Enough(m_a + m_b + m_slope + m_value, v1) &&
		Stuff::Close_Enough(3.0f*m_a + 2.0f*m_b + m_slope, s1)
		);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::SplineCurve::ComputeRange(
								 Stuff::Scalar *low,
								 Stuff::Scalar *hi
								 )
{
	Check_Object(this);
	Check_Pointer(low);
	Check_Pointer(hi);
	
	//
	//------------------------------------------------------------------------
	// We know that we will have to test the function at the beginning and end
	// of the segment, so go ahead and do that now
	//------------------------------------------------------------------------
	//
	*hi = *low = m_value;
	Stuff::Scalar t = ComputeValue(1.0f, 0.0f);
	if (t>*hi)
		*hi = t;
	else if (t<*low)
		*low = t;
	
	//
	//----------------------------------------------------------------------
	// If the curve is not cubic, we just have to look for the local min/max
	// at the solution to 2*m_b*t + m_slope == 0.  If the curve is linear, we just
	// return
	//----------------------------------------------------------------------
	//
	if (!m_a)
	{
		if (m_b)
		{
			t = -0.5f * m_slope / m_b;
			if (t > 0.0f && t < 1.0f)
			{
				t = ComputeValue(t, 0.0f);
				if (t < *low)
					*low = t;
				else if (t > *hi)
					*hi = t;
			}
		}
		return;
	}
	
	//
	//----------------------------------------------------------------------
	// Now we need to deal with the cubic.  Its min/max will be at either of
	// the two roots of the equation 3*m_a*t*t + 2*m_b*t + m_slope == 0
	//----------------------------------------------------------------------
	//
	Stuff::Scalar da = 3.0f*m_a;
	Stuff::Scalar db = 2.0f*m_b;
	Stuff::Scalar range = db*db - 4.0f*da*m_slope;
	if (range < 0.0f)
		return;
	da = 0.5f / da;
	db = -db * da;
	range = Stuff::Sqrt(range) * da;
	
	//
	//------------------------------------------------------------------------
	// db now holds the midpoint between the two solutions, which will be at +
	// or - range from that point
	//------------------------------------------------------------------------
	//
	t = db - range;
	if (t > 0.0f && t < 1.0f)
	{
		t = ComputeValue(t, 0.0f);
		if (t < *low)
			*low = t;
		else if (t > *hi)
			*hi = t;
	}
	t = db + range;
	if (t > 0.0f && t < 1.0f)
	{
		t = ComputeValue(t, 0.0f);
		if (t < *low)
			*low = t;
		else if (t > *hi)
			*hi = t;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	gosFX::SplineCurve::SetMinimum(Stuff::Scalar minimum)
{
	Check_Object(this);
	
	STOP(("Don't know how to do this yet..."));
}

//##########################################################################
//##########################    CurveKey    ###############################
//##########################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// return:  true=math good, false=math unstable
bool
gosFX::CurveKey::SetConstantKey(
								Stuff::Scalar key_time,
								Stuff::Scalar v
								)
{
	Check_Object(this);
	
	m_time = key_time;
	m_slope = 0.0f;
	m_value = v;
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// return:  true=math good, false=math unstable
bool
gosFX::CurveKey::SetLinearKey(
							  Stuff::Scalar key_time,
							  Stuff::Scalar v0,
							  Stuff::Scalar v1,
							  Stuff::Scalar dt
							  )
{
	Check_Object(this);
	Verify(dt > Stuff::SMALL);
	
	m_time = key_time;
	m_slope = (v1 - v0) / dt;
	m_value = v0;
	return Stuff::Close_Enough(m_slope*dt + v0, v1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::CurveKey::ComputeRange(
							  Stuff::Scalar *low,
							  Stuff::Scalar *hi,
							  Stuff::Scalar dt
							  )
{
	Check_Object(this);
	Check_Pointer(low);
	Check_Pointer(hi);
	
	//
	//------------------------------------------------------------------------
	// We know that we will have to test the function at the beginning and end
	// of the segment, so go ahead and do that now
	//------------------------------------------------------------------------
	//
	*hi = *low = m_value;
	if (dt < Stuff::SMALL)
		return;
	Stuff::Scalar t = ComputeValue(dt);
	if (t>*hi)
		*hi = t;
	else if (t<*low)
		*low = t;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	gosFX::CurveKey::SetMinimum(
		Stuff::Scalar minimum,
		Stuff::Scalar dt
	)
{
	Check_Object(this);
	
	Stuff::Scalar v1 = m_value;
	Stuff::Scalar v2 = m_value + m_slope*dt;
	Min_Clamp(v1, minimum);
	Min_Clamp(v2, minimum);
	SetLinearKey(m_time, v1, v2, dt);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
gosFX::ComplexCurve::ComplexCurve():
Curve(e_ComplexType)
{
	Check_Pointer(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
gosFX::ComplexCurve::ComplexCurve(const ComplexCurve &fcurve):
Curve(e_ComplexType)
{
	Check_Pointer(this);
	
	m_keys = fcurve.m_keys;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
gosFX::ComplexCurve::ComplexCurve(
	Stuff::MemoryStream *stream,
	int gfx_version
):
Curve(e_ComplexType)
{
	Check_Pointer(this);
	Check_Object(stream);
	
	Load(stream, gfx_version);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
gosFX::ComplexCurve&
gosFX::ComplexCurve::operator=(const ComplexCurve &fcurve)
{
	Check_Pointer(this);
	
	m_keys = fcurve.m_keys;
	return *this;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::ComplexCurve::Save(Stuff::MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);

	Stuff::MemoryStreamIO_Write(stream, &m_keys);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void 
gosFX::ComplexCurve::Load(
	Stuff::MemoryStream *stream,
	int gfx_version
)
{
	Check_Pointer(this);
	Check_Object(stream);

	if (gfx_version < 15)
	{
		Stuff::Scalar duration;
		*stream >> duration;
	}
	Stuff::MemoryStreamIO_Read(stream, &m_keys);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
gosFX::ComplexCurve::InsertKey(Stuff::Scalar m_time)
{
	Check_Object(this);
	gos_PushCurrentHeap(Heap);
	
	//
	//-------------------------------------------------------------
	// Make sure that we aren't adding a key at a time already used
	//-------------------------------------------------------------
	//
	int before = GetKeyIndex(m_time);
	int key_count = m_keys.GetLength();
	if (key_count == 0)
	{
	//
	//-----------------------------------------------------------------
	// In we are inserting the first key, make m_a constant m_value of zero
	//-----------------------------------------------------------------
	//
		m_keys.SetLength(key_count+1);
		m_keys[0].SetConstantKey(m_time, 0.0f);
		return 0;
	}
		else
	{
		if (Stuff::Close_Enough(m_time, m_keys[before].m_time))
			return before;
		else if (before < key_count-1 && Stuff::Close_Enough(m_keys[before+1].m_time, m_time))
			return before+1;
	
	//
	//-------------------------------------------------------------------------
	// Increase the size of the key array and shift the m_keys after the insert
	// point up one slot
	//-------------------------------------------------------------------------
	//
	m_keys.SetLength(key_count+1);

	for (int i=key_count-1; i>=before; --i)
		m_keys[i+1] = m_keys[i];
	CurveKey* key;
		key = &m_keys[++before];
	Check_Object(key);
	
	//
	//-----------------------------------------------------------------------
	// If this is an insert as opposed to an append, we need to set the key
	// values of the new segment so they smoothly complete the original curve
	// being inserted into
	//-----------------------------------------------------------------------
	//
	if (before < key_count)
	{
		Stuff::Scalar t = m_time - key->m_time;
		Verify(t > Stuff::SMALL);
		Stuff::Scalar v0 = key->ComputeValue(t);
		t = key[1].m_time - key->m_time;
		Verify(t > Stuff::SMALL);
		Stuff::Scalar v1 = key->ComputeValue(t);
		key->SetLinearKey(m_time, v0, v1, key[1].m_time - m_time);
	}
	
	//
	//-----------------------------------------------------------------------
	// Otherwise, we are appending, so all we can do is establish m_a key equal
	// to the the previous key at this m_time
	//-----------------------------------------------------------------------
	//
	else 
	{
		Verify(before == key_count);
		Stuff::Scalar t = m_time - key->m_time;
		Stuff::Scalar v0 = key->ComputeValue(t);
		Verify(t > Stuff::SMALL);
		t += 1.0f;
		Stuff::Scalar v1 = key->ComputeValue(t);
		key->SetLinearKey(m_time, v0, v1, 1.0f);
	}

	}
	
	
	//
	//-------------------------
	// Return the new key index
	//-------------------------
	//
	gos_PopCurrentHeap();
	return before;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void gosFX::ComplexCurve::DeleteKey(int index)
{
	Check_Object(this);
	Verify(index>0 && index<m_keys.GetLength());
	gos_PushCurrentHeap(Heap);
	
	//
	//---------------------------------------------------
	// If this is the last key, we just resize and return
	//---------------------------------------------------
	//
	int key_count = m_keys.GetLength();
	if (index == key_count-1)
	{
		m_keys.SetLength(index);
		gos_PopCurrentHeap();
		return;
	}
	
	//
	//-----------------------------------------------------------
	// Compute the ending values of this key, then shift the m_keys
	//-----------------------------------------------------------
	//
	CurveKey* key = &(*this)[index];
	Check_Object(key);
	Stuff::Scalar t = key[1].m_time - key->m_time;
	Stuff::Scalar v1 = key->ComputeValue(t);
	for (int i=index+1; i<key_count; ++i)
		m_keys[i-1] = m_keys[i];
	
	//
	//-------------------------------------------------
	// Now connect the previous key to the new next key
	//-------------------------------------------------
	//
	--key;
	key->SetLinearKey(
		key->m_time,
		key->m_value,
		v1,
		key[1].m_time - key->m_time
		);
	
	m_keys.SetLength(key_count-1);
	gos_PopCurrentHeap();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::ComplexCurve::SetCurve(Stuff::Scalar m_value)
{
	Check_Object(this);
	gos_PushCurrentHeap(Heap);
	m_keys.SetLength(1);
	m_keys[0].SetConstantKey(0.0f, m_value);
	gos_PopCurrentHeap();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::ComplexCurve::SetCurve(
							  Stuff::Scalar starting_value,
							  Stuff::Scalar ending_value
							  )
{
	Check_Object(this);
	
	//
	//-----------------------------------------------------------------------
	// Build the linear step, then the constant m_value at the end of the curve
	//-----------------------------------------------------------------------
	//
	gos_PushCurrentHeap(Heap);
	m_keys.SetLength(1);
	m_keys[0].SetLinearKey(0.0f, starting_value, ending_value, 1.0f);
	gos_PopCurrentHeap();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::ComplexCurve::ComputeRange(
								  Stuff::Scalar *low,
								  Stuff::Scalar *hi
								  )
{
	Check_Object(this);
	Check_Pointer(low);
	Check_Pointer(hi);
	
	//
	//--------------------------------------------
	// If the key is empty, set everything to zero
	//--------------------------------------------
	//
	int key_count = m_keys.GetLength();
	if (!key_count)
	{
		*low = *hi = 0.0f;
		return;
	}
	
	//
	//----------------------------
	// Deal with the last keyframe
	//----------------------------
	//
	CurveKey *key = &m_keys[--key_count];
	Stuff::Scalar dt = 1.0f - key->m_time;
	Min_Clamp(dt, 0.0f);
	key->ComputeRange(low, hi, dt);



	
	//
	//------------------------------------
	// Now deal with the preceeding frames
	//------------------------------------
	//
	while (--key_count >= 0)
	{
		--key;
		Stuff::Scalar h,l;
		key->ComputeRange(&l, &h, key[1].m_time - key->m_time);
		if (h > *hi)
			*hi = h;
		if (l < *low)
			*low = l;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	gosFX::ComplexCurve::SetMinimum(Stuff::Scalar minimum)
{
	Check_Object(this);
	
	//
	//---------------------------------
	// If the key is empty, we are done
	//---------------------------------
	//
	int key_count = m_keys.GetLength();
	

	// If there are no keys we need one to set this value
	if (!key_count)
	{
	InsertKey(0.0f);
	key_count = m_keys.GetLength();
	}
	//	return;
	
	//
	//----------------------------
	// Deal with the last keyframe
	//----------------------------
	//
	CurveKey *key = &m_keys[--key_count];
	Stuff::Scalar dt = 1.0f - key->m_time;
	Min_Clamp(dt, 0.0f);
	key->SetMinimum(minimum, dt);

	//
	//------------------------------------
	// Now deal with the preceeding frames
	//------------------------------------
	//
	while (--key_count >= 0)
	{
		--key;
		key->SetMinimum(minimum, key[1].m_time - key->m_time);
	}
}
