/***************************************************************************
                          metricscenter.cpp  -  description
                             -------------------
    begin                : Sun Oct 1 2000
    copyright            : (C) 2000 by Marc Bartsch
    email                : marc.bartsch@topmail.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "metricscenter.h"
#include <iostream.h>

MetricsCenter::MetricsCenter()
{
}

MetricsCenter::~MetricsCenter()
{
}

 	int MetricsCenter::xRealMax = 0;
	int MetricsCenter::yRealMax = 0;
 	int MetricsCenter::xMax = 0;
	int MetricsCenter::yMax = 0;
	int MetricsCenter::xMid = 0;
	int MetricsCenter::yMid = 0;
	int MetricsCenter::xDistance = 0;
	int MetricsCenter::yDistance = 0;
	int MetricsCenter::xDistanceRatio = 5;
	int MetricsCenter::yDistanceRatio = 5;
	int MetricsCenter::xRes = 0;
	int MetricsCenter::yRes = 0;
	int MetricsCenter::realXRes = 0;
	int MetricsCenter::realYRes = 0;
	int	MetricsCenter::intCanvasLeftTopX = 0;
	int	MetricsCenter::intCanvasLeftTopY = 0;
	int	MetricsCenter::extCanvasPosX = 0;
	int	MetricsCenter::extCanvasPosY = 0;
	QRect MetricsCenter::gridRect = QRect(0,0,0,0);

	bool MetricsCenter::isSquare = true;
	bool MetricsCenter::isPrinter = false;
  bool MetricsCenter::grid = true;
  bool MetricsCenter::axes = true;
  bool MetricsCenter::numbers = true;

void MetricsCenter::setXMax( int xMaxParam )
{
	xMax = xMaxParam;
}

int  MetricsCenter::getRealXMax()
{
	return xMax;
}

int  MetricsCenter::getXMid()
{
	return xMid;
}

int  MetricsCenter::getYMid()
{
	return yMid;
}


void MetricsCenter::setYMax( int yMaxParam )
{
	yMax = yMaxParam;
}

int  MetricsCenter::getRealYMax()
{
	return yMax;
}

int  MetricsCenter::getRealXRes()
{
	return realXRes;
}

int  MetricsCenter::getRealYRes()
{
	return realYRes;
}

int  MetricsCenter::getXDist()
{
	return xDistance;
}

int  MetricsCenter::getYDist()
{
	return yDistance;
}

QRect MetricsCenter::getGridRect()
{
	return gridRect;
}

void MetricsCenter::setGridRect( QRect gridRectParam )
{
	gridRect = gridRectParam;
}


void MetricsCenter::generateMetrics()
{
 	//	First try to fit screen resolution completely on PaintDevice
 	double ratio = ( double ) kapp->desktop()->width() / ( double ) kapp->desktop()->height();

 	int xMargin = 0;
 	int yMargin = 0;

	if ( getIsPrinter() )
	{
  	//	Paper metrics with frame that cannot be printed in
  	xMargin = ( int ) ( ( double ) xRes * 0.017 );
  	yMargin = ( int ) ( ( double ) yRes * 0.019 );
	}

 	int metricsWidth = xRes - xMargin;
 	int metricsHeight = yRes - yMargin;

	if ( getIsPrinter() )
 	{
  	if ( ( double ) metricsHeight * ratio > ( double ) metricsWidth )
  	{
  		realXRes = metricsWidth;
  		realYRes = ( int ) ( ( double ) metricsWidth /  ratio );
  	} else {
  		realYRes = metricsHeight;
  		realXRes = ( int ) ( ( double ) metricsHeight * ratio );
  	}
	} else {
  	realXRes = xRes;
  	realYRes = yRes;
	}

 	//	Get coords where grid is supposed to begin
 	int left = ( int ) ( ( double ) realXRes * 0.03 );
 	int top = ( int ) ( ( double ) realYRes * 0.03 );
 	int right =  realXRes - left;
 	int bottom = realYRes - top;

 	//	Get int distance from one gridpoint to the next
	double temp = ( ( ( double ) xDistanceRatio / 100 ) * (double) yRes );
 	xDistance = ( int ) temp;
	yDistance = xDistance;

	//	This is the highest possible value for x
	int xMaxTemp = ( ( right - left ) / xDistance ) / 2;
	if ( xMax <= xMaxTemp )
	{
		xRealMax = xMax;
	} else {
		xRealMax = xMaxTemp;
	}

	int yMaxTemp = ( ( bottom - top ) / yDistance ) / 2;
	if ( yMax <= yMaxTemp )
	{
		yRealMax = yMax;
	} else {
		yRealMax = yMaxTemp;
	}

// 	xDistance = ( int ) ( ( double ) ( right - left ) / ( double ) ( xMax * 2 ) );

/*	if ( getIsSquare() )
	{
 		yDistance = xDistance;
		int yMaxTemp = ( int )( ( double ) xMax / ratio );
		if ( yMaxTemp < yMax )
			yMax = yMaxTemp;
	} else {
		yDistance = ( int ) ( ( double ) ( bottom - top ) / ( double ) ( yMax * 2 ) );
	}*/

 	// Center grid
 	int xOffset = (( int )( ( double )( xRes - ( 2 * xRealMax * xDistance ) ) / 2 )) - xMargin;
 	int yOffset = (( int )( ( double )( yRes - ( 2 * yRealMax * yDistance ) ) / 2 )) - yMargin;

 	left = xOffset;
 	right = left + ( 2 * xRealMax * xDistance );
 	top = yOffset;
 	bottom = top + ( 2 * yRealMax * yDistance );

	gridRect.setLeft( left );
	gridRect.setRight( right );
	gridRect.setTop( top );
	gridRect.setBottom( bottom );
	gridRect = gridRect.normalize();

 	//   Get center of grid
  xMid = left + ( xRealMax * xDistance );
 	yMid = top + ( yRealMax * yDistance );
}

void MetricsCenter::setIsSquare( bool isSquareParam )
{
	isSquare = isSquareParam;
}

bool MetricsCenter::getIsSquare()
{
	return isSquare;
}

void MetricsCenter::setIsPrinter( bool isPrinterParam )
{
	isPrinter = isPrinterParam;
}

bool MetricsCenter::getIsPrinter()
{
	return isPrinter;
}

void MetricsCenter::mapCanvasToCanvasAttachToGrid( Coordinates & coords )
{
	mapCanvasToGrid( coords );
	mapGridToCanvas( coords );
}

float MetricsCenter::mapCanvasToGridX( QPoint *pt )
{
	//	This function turns intCanvas coordinates into
	//  coordinates of 'a' virtual grid

	//	Move x to zero point
	float fx = pt->x();
  fx = fx - getXMid();
	fx = fx / ( float ) getXDist();

	//	Round result. 10 * because QPoint doesn't take real
	fx = QString().setNum( fx, 'f', 1 ).toFloat();

	if ( fx == 0 )
	{
		fx = sqrt( pow( fx, 2 ) );
	}

	return fx;
}

void MetricsCenter::mapWidgetToCanvas( Coordinates & coords )
{
	//	This function turns widget coordinates into
	//  coordinates of the pixmap canvas
	coords.setX( coords.getD_X() +  intCanvasLeftTopX - 1 );
	coords.setY( coords.getD_Y() +  intCanvasLeftTopY - 1 );
}

QPoint MetricsCenter::mapCanvasToWidget( QPoint pt )
{
	//	This function turns canvas coordinates into
	//  coordinates of the widget you see on screen

	//	Coordinates in intCanvas for X
	int x = pt.x();
	x = x - intCanvasLeftTopX;
	
	//	Coordinates in intCanvas for Y
	int y = pt.y();
	y = y - intCanvasLeftTopY;

	return QPoint( x, y );
}

int	MetricsCenter::convertGridToCanvas( float length )
{
	return (int)( xDistance * length );
}

float	MetricsCenter::convertCanvasToGrid( int length )
{
	QPoint *pt = new QPoint( length + xMid, yMid );	
	return mapCanvasToGridX( pt );
}

double	MetricsCenter::convertCanvasToGrid( double length )
{
//  length -= ( double ) getXMid();
	length /= ( double ) getXDist();
	return length;
}


float MetricsCenter::getDistancePointLine( float x, float y, float x1, float y1, float x2, float y2 )
{
	//	Move everything so that starting point of line is on 0,0
	x  = x - x1;
	x2 = x2 - x1;
	x1 = 0;
	y  = y - y1;
	y2 = y2 - y1;
	y1 = 0;

	float dist;
	//	Case 1: horizontal line
	if ( y1 == y2 )
	{
		dist = sqrt( ( y1 - y ) * ( y1 - y ) );
	} else if ( x1 == x2 ){
		dist = sqrt( ( x1 - x ) * ( x1 - x ) );
	}	else {
		y = - y;
		y1 = - y1;
		y2 = - y2;

		float m1 = (y2-y1)/(x2-x1);
		float b1 = y1 - (m1*x1);
		//	Now we have y=m1x+b1
		//	And now the same for orth line

		float m2 = -(x2-x1)/(y2-y1);
		float b2 = y - ( m2 * x );

		float crossX = (b2-b1)/(m1-m2);
  	float crossY = m1*crossX + b1;

		float dx = x - crossX;
		float dy = y - crossY;

		dist = sqrt((dx * dx) + (dy * dy));
	}

  return dist;
}

double MetricsCenter::getDistancePointLine( Coordinates coords, Coordinates coord1, Coordinates coord2 )
{
	double x  = coords.getD_X();
	double y  = coords.getD_Y();
	double x1 = coord1.getD_X();
	double y1 = coord1.getD_Y();
	double x2 = coord2.getD_X();
	double y2 = coord2.getD_Y();

	//	Move everything so that starting point of line is on 0,0
	x  = x - x1;
	x2 = x2 - x1;
	x1 = 0;
	y  = y - y1;
	y2 = y2 - y1;
	y1 = 0;

	double dist;
	//	Case 1: horizontal line
	if ( y1 == y2 )
	{
		dist = sqrt( ( y1 - y ) * ( y1 - y ) );
	} else if ( x1 == x2 ){
		dist = sqrt( ( x1 - x ) * ( x1 - x ) );
	}	else {
		y = - y;
		y1 = - y1;
		y2 = - y2;

		double m1 = (y2-y1)/(x2-x1);
		double b1 = y1 - (m1*x1);
		//	Now we have y=m1x+b1
		//	And now the same for orth line

		double m2 = -(x2-x1)/(y2-y1);
		double b2 = y - ( m2 * x );

		double crossX = (b2-b1)/(m1-m2);
  	double crossY = m1*crossX + b1;

		double dx = x - crossX;
		double dy = y - crossY;

		dist = sqrt((dx * dx) + (dy * dy));
	}

  return dist;
}

QPoint * MetricsCenter::getPointOfConc( QPoint pt1, QPoint pt2, QPoint pt3, QPoint pt4 )
{
	float m1,m2,b1,b2,x,y;

	//	Get beginning and end points of both segments
	//	Line 1
	float x1 = pt1.x();
	float y1 = pt1.y();
	float x2 = pt2.x();
	float y2 = pt2.y();

	//	Line 2
	float x3 = pt3.x();
	float y3 = pt3.y();
	float x4 = pt4.x();
	float y4 = pt4.y();

	//	Compute slopes (m1,m2) of both segments.
  m1 = ( y2 - y1 ) / ( x2 - x1 );
  m2 = ( y4 - y3 ) / ( x4 - x3 );

	//	Check whether lines are parallel
	if ( m1 == m2 )
		return new QPoint( -100, -100 );

	//	Compute point where lines cross y-axis (b1,b2)
	b1 = y1 - ( m1 * x1);	
	b2 = y3 - ( m2 * x3);	

	//	Compute actual point of conc
	x	 = ( b2 - b1 ) / ( m1 - m2 );
	y	 = ( m1 * (( b2 - b1 ) / ( m1 - m2 ))) + b1;

	//	Round result
  QString roundingX = QString().setNum((float) x,'f',0 );
  QString roundingY = QString().setNum((float) y,'f',0 );

	return new QPoint( roundingX.toInt(), roundingY.toInt() );
}

QPoint MetricsCenter::mapPointToLine( QPoint pt1, QPoint pt2, QPoint pt3 )
{
	//	Get beginning and end of this line
	double x1 = pt2.x();
	double x2 = pt3.x();
	double y1 = pt2.y();
	double y2 = pt3.y();

	//	Vector from x2,y2 to x1,y1
	double lineX = x1 - x2;
	double lineY = y1 - y2;
	double length = sqrt( ( lineX * lineX )	+ ( lineY * lineY ) );
 	lineX = lineX / length;
	lineY = lineY / length;

	//	Generate perpendicular vector through pt
	double orthX = lineY + pt1.x();
	double orthY = -lineX + pt1.y();

	//	Get the distance of x1,y1 to perpendicular line, because this
	//	is the distance we find our new point at on line
	//	The second distance is to be able to move point over both points
	double dist = getDistancePointLine( x2, y2, pt1.x(), pt1.y(), orthX, orthY );
	double dist2 = getDistancePointLine( x1, y1, pt1.x(), pt1.y(), orthX, orthY );

	lineX = ( lineX  * dist );
	lineY = ( lineY  * dist );

  QString roundingX = QString().setNum((float) lineX,'f',0 );
  QString roundingY = QString().setNum((float) lineY,'f',0 );

	//	First find out whether point has been moved over x2,y2
	if ( ( ( dist2 > length ) && ( dist < dist2 ) ) )
	{
		lineX = ( -1 * roundingX.toInt() ) + x2;
		lineY = ( -1 * roundingY.toInt() ) + y2;
	} else {
		lineX = roundingX.toInt() + x2;
		lineY = roundingY.toInt() + y2;
	}
	return QPoint( lineX, lineY );
}

void MetricsCenter::mapGridToCanvas( Coordinates & coords )
{
	Coordinates coord1( getXMid(), getYMid() );

	coords.set( coords.getD_X() * getXDist(), coords.getD_Y() * getYDist() );
	coords.invertY();
	coords += coord1;		
}

void MetricsCenter::mapCanvasToGrid( Coordinates & coords )
{
	//	This function turns intCanvas coordinates into
	//  coordinates of the virtual grid
	Coordinates coord1( getXMid(), getYMid() );
	coords -= coord1;
	coords.invertY();
  coords.set( coords.getD_X() / getXDist(), coords.getD_Y() / getYDist() );
	coords.set( coords.getD_X( 1 ).toDouble(), coords.getD_Y( 1 ).toDouble() );
}

void  MetricsCenter::mapPointToLine( Coordinates & coords, Coordinates coord1, Coordinates coord2 )
{
	Coordinates coord3, coord4;

	//	Vector from end point to start point
	coord3 = coord1 - coord2;
	coord4 = coord1 - coord2;
	double length = coord3.getLength();

	//	Make this vector length 1
	coord3.normalize( 1 );

	//	Make this perpendicular through coords
	coord4.orthogonalize();
	coord4 += coords;

	//	Get the distance of start and end point to perpendicular line, because this
	//	is the distance we find our new point at on line coord3.
	double dist  = MetricsCenter::getDistancePointLine( coord2, coords, coord4 );
	double dist2 = MetricsCenter::getDistancePointLine( coord1, coords, coord4 );

	//	This is the vector we have to add to coord2 to map point to line
	coord3 *= dist;

	//	Find out whether point is right of coord2.
	if ( ( ( dist2 > length ) && ( dist < dist2 ) ) )
	{
		coords = ( coord3 * (-1) ) + coord2;
	} else {
		coords = coord3 + coord2;
	}
}

void MetricsCenter::pointOfConc( Coordinates & coords, Coordinates coord1, Coordinates coord2, Coordinates coord3, Coordinates coord4 )
{
	double m1,m2,b1,b2,x,y;

	//	Get beginning and end points of both segments
	//	segment 1
	double x1 = coord1.getD_X();
	double y1 = coord1.getD_Y();
	double x2 = coord2.getD_X();
	double y2 = coord2.getD_Y();

	//	Line 2
	double x3 = coord3.getD_X();
	double y3 = coord3.getD_Y();
	double x4 = coord4.getD_X();
	double y4 = coord4.getD_Y();

	//	Check whether one line is horizontal
	//	First, only first line is horizontal
	if ( ( ( x2 - x1 ) == 0 ) && ( ( x4 - x3 ) != 0 ) )
	{
	  m2 = ( y4 - y3 ) / ( x4 - x3 );
		b2 = y3 - ( m2 * x3);	
		y	 = ( m2 * x1 ) + b2;
		coords.set( x1, y );
		return;
	}

	if ( ( ( x4 - x3 ) == 0 ) && ( ( x2 - x1 ) != 0 ) )
	{
	  m1 = ( y2 - y1 ) / ( x2 - x1 );
		b1 = y1 - ( m1 * x1);	
		y	 = ( m1 * x3 ) + b1;
		coords.set( x3, y );
		return;
	}

	if ( ( ( x4 - x3 ) == 0 ) && ( ( x2 - x1 ) == 0 ) )
	{
		coords.set( -100, -100 );
		return;
	}

	//	Compute slopes (m1,m2) of both segments.
  m1 = ( y2 - y1 ) / ( x2 - x1 );
  m2 = ( y4 - y3 ) / ( x4 - x3 );

	//	Check whether lines are parallel
	if ( m1 == m2 )
	{
		coords.set( -100, -100 );
		return;
	}

	//	Compute point where lines cross y-axis (b1,b2)
	b1 = y1 - ( m1 * x1);	
	b2 = y3 - ( m2 * x3);	

	//	Compute actual point of conc
	x	 = ( b2 - b1 ) / ( m1 - m2 );
	y	 = ( m1 * (( b2 - b1 ) / ( m1 - m2 ))) + b1;

	coords.set( x, y );
}

bool	MetricsCenter::lineContainsPoint( Coordinates coords, Coordinates coord1, Coordinates coord2 )
{
	bool bool1, bool2, bool3;

	//	This is the distance from line
	if ( MetricsCenter::getDistancePointLine( coords, coord1, coord2 ) < 5 )
	{
		bool1 = true;
	} else {
		bool1 = false;
	}

	//	Vector from end to start point and its length
	Coordinates coord3, coord4, coord5;
	coord3 = coord1 - coord2;
	double length = coord3.getLength();
	coord3.orthogonalize();

	//	Perpendicular vectors through start and end points
	coord4 = coord1 + coord3;
	coord5 = coord2 + coord3;

	double dist = getDistancePointLine( coords, coord1, coord4 );
	dist = dist * dist;

	if ( dist < length * length )
	{
		bool2 = true;
	} else {
		bool2 = false;
	}

	dist = getDistancePointLine( coords, coord2, coord5 );

	dist = dist * dist;

	if ( dist < length * length )
	{
		bool3 = true;
	} else {
		bool3 = false;
	}

	return ( bool1 && bool2 && bool3 );
}





