//=========================================================
//  MusE
//  Linux Music Editor
//    $Id: dimap.cpp,v 1.1.1.1 2003/10/29 10:06:35 wschweer Exp $

//    Copyright (C) 1997  Josef Wilgen
//	This program is free software; you can redistribute it and/or modify
//	it under the terms of the GNU General Public License, version 2,
//	as published by	the Free Software Foundation.
//
//    (C) Copyright 2000 Werner Schweer (ws@seh.de)
//=========================================================

#include <cmath>
#include "dimap.h"
#include "mmath.h"

const double DiMap::LogMin = 1.0e-150;
const double DiMap::LogMax = 1.0e150;

//	DiMap - Map a double interval into an integer interval
//
//	The DiMap class maps an interval of type double into an interval of
//	type integer. It consists
//	of two intervals D = [d1, d2] (double) and I = [i1, i2] (int), which are
//	specified with the @DiMap::setDblRange@ and @DiMap::setIntRange@
//	members. The point d1 is mapped to the point i1, and d2 is mapped to i2.
//	Any point inside or outside D can be mapped to a point inside or outside
//	I using @DiMap::transform@ or @DiMap::limTransform@ or vice versa
//	using @QwtPlot::invTransform@. D can be scaled linearly or
//	logarithmically, as specified with @DiMap::setDblRange@.

//------------------------------------------------------------
//.F	DiMap::DiMap (1)
//	Construct a DiMap instance.
//
//.u	Syntax
//.f	 DiMap::DiMap()
//
//.u	Description
//	The double and integer intervals are both set to [0,1].
//------------------------------------------------------------

DiMap::DiMap()
      {
      d_x1 = 0.0;
      d_x2 = 1.0;
      d_y1 = 0;
      d_y2 = 1;
      d_cnv = 1.0;
      }

//------------------------------------------------------------
//.F	DiMap::DiMap (2)
//	Construct a DiMap instance with initial integer
//	and double intervals
//
//.u	Syntax
//.f	 DiMap::DiMap(int i1, int i2, double d1, double d2, bool logarithmic)
//
//.u	Parameters
//.p	int i1		--	first border of integer interval
//	int i2		--	second border of integer interval
//	double d1	--	first border of double interval
//	double d2	--	second border of double interval
//	bool logarithmic  -- logarithmic mapping, TRUE or FALSE. Defaults
//					to FALSE.
//------------------------------------------------------------

DiMap::DiMap(int i1, int i2, double d1, double d2, bool logarithmic)
      {
      d_log = logarithmic;
      setIntRange(i1,i2);
      setDblRange(d1, d2);
      }

//------------------------------------------------------------
//.F	DiMap::~DiMap
//	Destroy a DiMap instance.
//
//.u	Syntax
//.f	 DiMap::~DiMap()
//------------------------------------------------------------

DiMap::~DiMap()
      {
      }

//------------------------------------------------------------
//.F	DiMap::contains (1)
//	Returns TRUE if a value x lies inside or at the border of the
//	map's double range.
//
//.u	Syntax
//.f	bool DiMap::contains(double x)
//
//.u	Parameters
//.p	double x -- value
//------------------------------------------------------------

bool DiMap::contains(double x) const
      {
      return ( (x >= qwtMin(d_x1, d_x1)) && (x <= qwtMax(d_x1, d_x2)));
      }

//------------------------------------------------------------
//.F	DiMap::contains (2)
//	Returns TRUE if a value x lies inside or at the border of the
//	map's integer range
//
//.u	Syntax
//.f	bool DiMap::contains(int x)
//
//.u	Parameters
//.p	int x -- value
//------------------------------------------------------------

bool DiMap::contains(int x) const
      {
      return ( (x >= qwtMin(d_y1, d_y1)) && (x <= qwtMax(d_y1, d_y2)));
      }

//------------------------------------------------------------
//.F	DiMap::setDblRange
//	Specify the borders of the double interval
//
//.u	Syntax
//.f	void DiMap::setDblRange(double d1, double d2, bool lg = FALSE)
//
//.u	Parameters
//.p	double d1	--	first border
//	double d2	--	second border
//	bool lg		--	logarithmic (TRUE) or linear (FALSE)
//				scaling. Defaults to FALSE.
//------------------------------------------------------------

void DiMap::setDblRange(double d1, double d2, bool lg)
      {
      if (lg) {
            d_log = true;
            if (d1 < LogMin)
                  d1 = LogMin;
            else if (d1 > LogMax)
                  d1 = LogMax;
      	
            if (d2 < LogMin)
                  d2 = LogMin;
            else if (d2 > LogMax)
                  d2 = LogMax;
      	
            d_x1 = log(d1);
            d_x2 = log(d2);
            }
      else {
            d_log = FALSE;
            d_x1 = d1;
            d_x2 = d2;
            }
      newFactor();
      }

//------------------------------------------------------------
//.F	DiMap::setIntRange
//	Specify the borders of the integer interval
//
//.u	Syntax
//.f	void DiMap::setIntRange(int i1, int i2)
//
//.u	Parameters
//.p	int i1	--	first border
//	int i2  --	second border
//------------------------------------------------------------

void DiMap::setIntRange(int i1, int i2)
      {
      d_y1 = i1;
      d_y2 = i2;
      newFactor();
      }

//------------------------------------------------------------
//.F	DiMap::transform
//	Transform a point in double interval into an point in the
//	integer interval
//
//.u	Syntax
//.f	int DiMap::transform(double x)
//
//.u	Parameters
//.p	double x
//
//.u	Return Value
//.t
//	linear mapping:	-- rint(i1 + (i2 - i1) / (d2 - d1) * (x - d1))
//	logarithmic mapping: -- rint(i1 + (i2 - i1) / log(d2 / d1) * log(x / d1))
//
//.u    Note
//	The specified point is allowed to lie outside the intervals. If you
//	want to limit the returned value, use @DiMap::limTransform@.
//------------------------------------------------------------

int DiMap::transform(double x) const
      {
      if (d_log)
            return (d_y1 + int(rint( (log(x) - d_x1) * d_cnv )));
      else
            return (d_y1 + int(rint( (x - d_x1) * d_cnv )));
      }

//------------------------------------------------------------
//.F	DiMap::invTransform
//	Transform an integer value into a double value
//
//.u	Syntax
//.f	double DiMap::invTransform(int y)
//
//.u	Parameters
//.p	int y	--	integer value to be transformed
//
//.u	Return Value
//.t
//	linear mapping:	-- d1 + (d2 - d1) / (i2 - i1) * (y - i1)
//	logarithmic mapping: -- d1 + (d2 - d1) / log(i2 / i1) * log(y / i1)
//------------------------------------------------------------

double DiMap::invTransform(int y) const
      {
      if (d_cnv == 0.0)
            return 0.0;
      else {
            if (d_log)
                  return exp(d_x1 + double(y - d_y1) / d_cnv );
            else
                  return ( d_x1 + double(y - d_y1) / d_cnv );	
            }
      }

//------------------------------------------------------------
//.F	DiMap::limTransform
//	Transform and limit
//
//.u	Syntax
//.f	int DiMap::limTransform(double x)
//
//.u	Parameters
//.p	double x
//
//.u	Return Value
//		transformed value
//
//.u	Description
//	The function is similar to @DiMap::transform@, but limits the input value
//	to the nearest border of the map's double interval if it lies outside
//	that interval.
//------------------------------------------------------------

int DiMap::limTransform(double x) const
      {
      if ( x > qwtMax(d_x1, d_x2) )
            x = qwtMax(d_x1, d_x2);
      else if ( x < qwtMin(d_x1, d_x2))
            x = qwtMin(d_x1, d_x2);
      return transform(x);
      }

//------------------------------------------------------------
//.F	DiMap::xTransform
//	Exact transformation
//
//.u	Syntax
//.f	double DiMap::dTransform(double x)
//
//.u	Parameters
//.p		double x	-- value to be transformed
//
//.u	Return Value
//.t
//	linear mapping:	-- i1 + (i2 - i1) / (d2 - d1) * (x - d1)
//	logarithmic mapping: -- i1 + (i2 - i1) / log(d2 / d1) * log(x / d1)
//
//.u	Description
//	This function is similar to @DiMap::transform@, but
//	makes the integer interval appear to be double.
//------------------------------------------------------------

double DiMap::xTransform(double x) const
      {
      double rv;

      if (d_log)
            rv = double(d_y1) + (log(x) - d_x1) * d_cnv;
      else
            rv = double(d_y1) + (x - d_x1) * d_cnv;
      return rv;
      }

//------------------------------------------------------------
//.F	DiMap::newFactor
//	Re-calculate the conversion factor.
//------------------------------------------------------------

void DiMap::newFactor()
      {
      if (d_x2 != d_x1)
            d_cnv = double(d_y2 - d_y1) / (d_x2 - d_x1);
      else
            d_cnv = 0.0;
      }
