/////////////////////////////////////////////////////////////////////////////
// Name:		linear2exp.h
// Purpose:  
// Author:      Cesar Mauri Loba (cesar at crea-si dot com)
// Modified by: 
// Created:     
// Copyright:   (C) 2010-11 Cesar Mauri Loba - CREA Software Systems
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////
#ifndef LINEAR2EXP_H
#define LINEAR2EXP_H

#include <assert.h>
#include <math.h>
#include <stdexcept>

//
// This class is intended for helping mapping between linear to exp scales.
// The main purpose is to allow linear sliders produce a exp range of values
// The following function is used:
//
//  y= A*e^(x*B)-A+C  (defined for x>= 0)
//
// Where 
//  *	x is the input value (linear)
//	*	y is the output value (exp)
//	*	A is the "speed" constant (for large values the above function grows faster)
//		For convenience A=e^a where "a" is a parameter set by the user and can be 
//		either positive or negative.
//	*	B and C are constants determined by two points the function should cross
//			p1=(min_linear>= 0, min_exp)  
//				and  
//			p2=(max_linear> 0, maxexp> min_exp)
//
// For simplicity min_linear= 0. This yields the following solutions for
// the equation system:
//
//		C= min_exp	
//		B= (1/max_lin) * ln ((A+max_exp-min_exp)/A)
//
// The inverse function is:
//
//  x= (1/B) * ln ((y+A-C)/A)
//

class Linear2ExpMapping
{
public:
	Linear2ExpMapping ()
	: m_A(2.0f)
	, m_B(1.0f)
	, m_C(1.0f)
	{}

	Linear2ExpMapping (float min_linear, float min_exp, float max_linear, float max_exp, float grow= 0)
	{
		SetParams (min_linear, min_exp, max_linear, max_exp, grow);		
	}

	float ToExp (float x) const {
		return m_A * exp (x*m_B) - m_A + m_C;
	}

	float ToLinear (float y) const {
		assert ((y+m_A-m_C)>= 0);
		return (1.0f/m_B) * logf ((y+m_A-m_C)/m_A);
	}
	
	void SetParams (float min_linear, float min_exp, float max_linear, float max_exp, float grow)
	{
		assert (min_linear== 0);
		if (min_linear!= 0) throw std::out_of_range("Linear2ExpMapping: wrong min_linear");
        assert (min_exp>= 0);
		if (!(min_exp>= 0)) throw std::out_of_range("Linear2ExpMapping: wrong min_exp");
		assert (max_linear> min_linear);
		if (!(max_linear> min_linear)) throw std::out_of_range("Linear2ExpMapping: wrong max_linear");
		assert (max_exp> min_exp);
		if (!(max_exp> min_exp)) throw std::out_of_range("Linear2ExpMapping: wrong max_exp");
			
		m_A= exp (grow);
		m_C= min_exp;
		m_B= (1.0f / max_linear) * logf ((m_A + max_exp - m_C) / m_A);
	}

private:
	float m_A;
	float m_B;
	float m_C;	// Equal to min_exp

protected:
	
	float GetC() const { return m_C; }
};

#endif