/***************************************************************************
    File                 : Graph3D.cpp
    Project              : SciDAVis
    --------------------------------------------------------------------
    Copyright            : (C) 2006 by Ion Vasilief, Tilman Benkert
    Email (use @ for *)  : ion_vasilief*yahoo.fr, thzs*gmx.net
    Description          : 3D graph widget

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  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.                                    *
 *                                                                         *
 *  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, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/
#include "Graph3D.h"
#include "Bar.h"
#include "Cone3D.h"
#include "MyParser.h"
#include "ColorButton.h"
#include "core/column/Column.h"

#include <QApplication>
#include <QMessageBox>
#include <QFileDialog>
#include <QPrinter>
#include <QPrintDialog>
#include <QClipboard>
#include <QPixmap>
#include <QBitmap>
#include <QDateTime>
#include <QCursor>
#include <QImageWriter>

#include <qwt3d_io_gl2ps.h>
#include <qwt3d_coordsys.h>

#include <gsl/gsl_vector.h>
#include <fstream>
#include <stdexcept>
using namespace std;

UserFunction::UserFunction(const QString& s, SurfacePlot& pw)
: Function(pw)
{
	formula=s;
}

double UserFunction::operator()(double x, double y)
{
	if (formula.isEmpty())
		return 0.0;

	MyParser parser;
	double result=0.0;
	try
	{
		parser.DefineVar("x", &x);
		parser.DefineVar("y", &y);

		parser.SetExpr((const std::string)formula.toUtf8().constData());
		result=parser.Eval();
	}
	catch(mu::ParserError &e)
	{
		QMessageBox::critical(0,"Input function error",QString::fromStdString(e.GetMsg()));
	}
	return result;
}

UserFunction::~UserFunction()
{
}

	Graph3D::Graph3D(const QString& label, QWidget* parent, const char* name, Qt::WindowFlags f)
: MyWidget(label,parent,name,f)
{
	initPlot();
}

void Graph3D::initPlot()
{
	worksheet = 0;
	d_matrix = 0;
    plotAssociation = QString();

	QDateTime dt = QDateTime::currentDateTime ();
	setBirthDate(dt.toString(Qt::LocalDate));

    color_map = QString::null;
    animation_redraw_wait = 50;
    d_timer = new QTimer(this);
    connect(d_timer, SIGNAL(timeout()), this, SLOT(rotate()) );
	ignoreFonts = false;

	QWidget* d_main_widget = new QWidget();
	sp = new SurfacePlot(d_main_widget);
	sp->resize(500,400);
	sp->installEventFilter(this);
	sp->setRotation(30,0,15);
	sp->setScale(1,1,1);
	sp->setShift(0.15,0,0);
	sp->setZoom(0.9);
	sp->setOrtho(false);

	smoothMesh = true;
	sp->setSmoothMesh(smoothMesh);

	d_autoscale = true;

	title = QString();
	sp->setTitle(title);

	titleCol = QColor(Qt::black);
	sp->setTitleColor(Qt2GL(titleCol));

	titleFnt = QFont("Times New Roman",14);
	titleFnt.setBold(true);

	sp->setTitleFont(titleFnt.family(),titleFnt.pointSize(),
			titleFnt.weight(),titleFnt.italic());

	axesCol=QColor(Qt::black);
	labelsCol=QColor(Qt::black);
	numCol=QColor(Qt::black);
	meshCol=QColor(Qt::black);
	gridCol=QColor(Qt::black);
	bgCol=QColor(255, 255, 255);
	fromColor=QColor(Qt::red);
	toColor=QColor(Qt::blue);

	col_ = 0;

	legendOn = false;
	legendMajorTicks = 5;
	sp->showColorLegend(legendOn);
	sp->legend()->setAutoScale(true);
	sp->legend()->setMajors (legendMajorTicks) ;

	labelsDist=0;

	scaleType=QVector<int>(3);
	for (int j=0;j<3;j++)
		scaleType[j]=0;

	pointStyle = None;
	func = 0;
	alpha = 1.0;
	barsRad = 0.007;
	pointSize = 5; smooth = false;
	crossHairRad = 0.03, crossHairLineWidth = 2;
	crossHairSmooth = true, crossHairBoxed = false;
	conesQuality = 32; conesRad = 0.5;

	style_ = NOPLOT;
	this->setWidget(d_main_widget);
	initCoord();

	connect(sp,SIGNAL(rotationChanged(double, double, double)),this,SLOT(rotationChanged(double, double, double)));
	connect(sp,SIGNAL(zoomChanged(double)),this,SLOT(zoomChanged(double)));
	connect(sp,SIGNAL(scaleChanged(double, double, double)),this,SLOT(scaleChanged(double, double, double)));
	connect(sp,SIGNAL(shiftChanged(double, double, double)),this,SLOT(shiftChanged(double, double, double)));
}

void Graph3D::initCoord()
{
	sp->makeCurrent();
	for (unsigned i=0; i!=sp->coordinates()->axes.size(); ++i)
	{
		sp->coordinates()->axes[i].setMajors(5);
		sp->coordinates()->axes[i].setMinors(5);
	}

	QString s = tr("X axis");
	sp->coordinates()->axes[X1].setLabelString(s);
	sp->coordinates()->axes[X2].setLabelString(s);
	sp->coordinates()->axes[X3].setLabelString(s);
	sp->coordinates()->axes[X4].setLabelString(s);
	labels<<s;

	s = tr("Y axis");
	sp->coordinates()->axes[Y1].setLabelString(s);
	sp->coordinates()->axes[Y2].setLabelString(s);
	sp->coordinates()->axes[Y3].setLabelString(s);
	sp->coordinates()->axes[Y4].setLabelString(s);
	labels<<s;

	s = tr("Z axis");
	sp->coordinates()->axes[Z1].setLabelString(s);
	sp->coordinates()->axes[Z2].setLabelString(s);
	sp->coordinates()->axes[Z3].setLabelString(s);
	sp->coordinates()->axes[Z4].setLabelString(s);
	labels<<s;

	sp->setCoordinateStyle(BOX);
	sp->coordinates()->setAutoScale(false);
}

void Graph3D::addFunction(const QString& s,double xl,double xr,double yl,
		double yr,double zl,double zr)
{
	sp->makeCurrent();
	sp->resize(this->size());

	func= new UserFunction(s, *sp);

	func->setMesh(41,31);
	func->setDomain(xl,xr,yl,yr);
	func->setMinZ(zl);
	func->setMaxZ(zr);
	func->create();

	sp->legend()->setLimits(zl,zr);

	if (sp->plotStyle() == NOPLOT)
	{
		sp->setPlotStyle(FILLED);
		style_=FILLED;
		pointStyle = None;
	}
  	sp->createCoordinateSystem(Triple(xl, yl, zl), Triple(xr, yr, zr));
	findBestLayout();
}

void Graph3D::insertFunction(const QString& s,double xl,double xr,double yl,
		double yr,double zl,double zr)
{
	addFunction(s,xl,xr,yl,yr,zl,zr);
	update();
}

void Graph3D::addData(Table* table, int xcol, int ycol)
{
	worksheet=table;
	int r=table->numRows();
	int i, xmesh=0, ymesh=2;
	for (i = 0; i < r; i++)
	{
		if (!table->column(xcol)->isInvalid(i) && !table->column(ycol)->isInvalid(i))
			xmesh++;
	}

	if (xmesh == 0)
		xmesh++;

	double **data = Matrix::allocateMatrixData(xmesh, ymesh);
	gsl_vector * x = gsl_vector_alloc (xmesh);
	gsl_vector * y = gsl_vector_alloc (xmesh);

	for (int j = 0; j < ymesh; j++)
	{
		int k=0;
		for (i = 0; i < r; i++)
		{
			if (!table->column(xcol)->isInvalid(i) && !table->column(ycol)->isInvalid(i))
			{
				gsl_vector_set (x, k, table->cell(i, xcol));

				double yv = table->cell(i, ycol);
				gsl_vector_set (y, k, yv);
				data[k][j] = yv;
				k++;
			}
		}
	}

	double maxy=gsl_vector_max(y);
	double maxz=0.6*maxy;
	sp->makeCurrent();
	sp->legend()->setLimits(gsl_vector_min(y),maxy);
	sp->loadFromData(data, xmesh, ymesh, gsl_vector_min(x),gsl_vector_max(x),0,maxz);

	if (d_autoscale)
		findBestLayout();

	gsl_vector_free (x);
	gsl_vector_free (y);
	Matrix::freeMatrixData(data, xmesh);
}

void Graph3D::addData(Table* table,const QString& colName)
{
	int ycol = table->colIndex(colName);
	int xcol=table->colX(ycol);
	plotAssociation = table->colName(xcol)+"(X)," + colName + "(Y)";

	addData(table, xcol, ycol);
}

void Graph3D::addData(Table* table,const QString& xColName,const QString& yColName)
{
	int xcol=table->colIndex(xColName);
	plotAssociation = table->colName(xcol)+"(X)," + yColName+"(Y)";

	addData(table, xcol, table->colIndex(yColName));
}

void Graph3D::changeMatrix(Matrix* m)
{
	if (d_matrix == m)
		return;

	addMatrixData(m);
}

void Graph3D::addMatrixData(Matrix* m)
{
	if (d_matrix == m)
		return;

	bool first_time = false;
	if(!d_matrix)
		first_time = true;

	d_matrix = m;
	plotAssociation = "matrix<" + QString(m->name()) + ">";

	int cols = m->numCols();
	int rows = m->numRows();
	double **data_matrix = Matrix::allocateMatrixData(rows, cols);
	for (int i = 0; i < rows; i++ )
	{
		for (int j = 0; j < cols; j++)
			data_matrix[i][j] = m->cell(i,j);
	}

	sp->makeCurrent();
	sp->loadFromData(data_matrix, rows, cols, m->xStart(), m->xEnd(),
			m->yStart(), m->yEnd());

	double start, end;
	sp->coordinates()->axes[Z1].limits (start, end);
	sp->legend()->setLimits(start, end);
	sp->legend()->setMajors(legendMajorTicks);

	Matrix::freeMatrixData(data_matrix, rows);

	if (d_autoscale || first_time)
		findBestLayout();
	update();
}

void Graph3D::addMatrixData(Matrix* m, double xl, double xr,
		double yl, double yr, double zl, double zr)
{
	d_matrix = m;
	plotAssociation = "matrix<" + QString(m->name()) + ">";

	updateScalesFromMatrix(xl, xr, yl, yr, zl, zr);
}

void Graph3D::addData(Table* table,const QString& xColName,const QString& yColName,
		double xl, double xr, double yl, double yr, double zl, double zr)
{
	worksheet=table;
	int r=table->numRows();
	int xcol=table->colIndex(xColName);
	int ycol=table->colIndex(yColName);

	QString s=table->colName(xcol)+"(X),";
	s+=yColName+"(Y)";
	plotAssociation = s;

	int i, j, xmesh=0, ymesh=2;
	double xv, yv;

	for (i = 0; i < r; i++)
	{
		if (!table->column(xcol)->isInvalid(i) && !table->column(ycol)->isInvalid(i))
		{
			xv=table->cell(i, xcol);
			if (xv>=xl && xv <= xr)
				xmesh++;
		}
	}

	if (xmesh == 0)
		xmesh++;

	double **data = Matrix::allocateMatrixData(xmesh, ymesh);
	for ( j = 0; j < ymesh; j++)
	{
		int k=0;
		for ( i = 0; i < r; i++)
		{
			if (!table->column(xcol)->isInvalid(i) && !table->column(ycol)->isInvalid(i))
			{
				xv=table->cell(i,xcol);
				if (xv>=xl && xv <= xr)
				{
					yv=table->cell(i,ycol);
					if (yv > zr)
						data[k][j] = zr;
					else if (yv < zl)
						data[k][j] = zl;
					else
						data[k][j] = yv;
					k++;
				}
			}
		}
	}
	sp->makeCurrent();
	sp->loadFromData(data, xmesh, ymesh, xl, xr, yl, yr);
	sp->createCoordinateSystem(Triple(xl, yl, zl), Triple(xr, yr, zr));
	sp->legend()->setLimits(zl, zr);
	sp->legend()->setMajors(legendMajorTicks);

	Matrix::freeMatrixData(data, xmesh);
}

void Graph3D::insertNewData(Table* table, const QString& colName)
{
	int zCol=table->colIndex(colName);
	int yCol=table->colY(zCol);
	int xCol=table->colX(zCol);

	addData(table, xCol, yCol, zCol, Trajectory);
	update();
}

void Graph3D::changeDataColumn(Table* table, const QString& colName)
{
	int zCol=table->colIndex(colName);
	int yCol=table->colY(zCol);
	int xCol=table->colX(zCol);

	plotAssociation = table->colName(xCol)+"(X)," + table->colName(yCol)+"(Y),";
	plotAssociation += colName+"(Z)";

	updateDataXYZ(table, xCol, yCol, zCol);
	if (d_autoscale)
		findBestLayout();
}

void Graph3D::addData(Table* table, int xCol,int yCol,int zCol, int type)
{
	worksheet=table;
	int r=table->numRows();

	QString s=table->colName(xCol)+"(X),";
	s+=table->colName(yCol)+"(Y),";
	s+=table->colName(zCol)+"(Z)";
	plotAssociation = s;

	int i,j,columns=0;
	for ( i = 0; i < r; i++)
	{
		if (!table->column(xCol)->isInvalid(i) && !table->column(yCol)->isInvalid(i) && !table->column(zCol)->isInvalid(i))
			columns++;
	}

	if (columns == 0)
		columns++;

	Qwt3D::Triple **data=allocateData(columns,columns);
	for (j = 0; j < columns; j++)
	{
		int k=0;
		for ( i = 0; i < r; i++)
		{
			if (!table->column(xCol)->isInvalid(i) && !table->column(yCol)->isInvalid(i) && !table->column(zCol)->isInvalid(i))
			{
				double xv=table->cell(i,xCol);
				double yv=table->cell(i,yCol);
				double zv=table->cell(i,zCol);

				data[k][j] = Triple(xv,yv,zv);
				k++;
			}
		}
	}

	sp->makeCurrent();
	sp->loadFromData (data, columns, columns, false,false);

	double start, end;
	sp->coordinates()->axes[Z1].limits (start, end);
	sp->legend()->setLimits(start, end);
	sp->legend()->setMajors(legendMajorTicks);

	if (type == Scatter)
	{
		Dot d(pointSize, smooth);
		sp->setPlotStyle(d);
		pointStyle=Dots;
		style_ = Qwt3D::USER;
	}
	else if (type == Trajectory)
	{
		legendOn=false;
		sp->showColorLegend(legendOn);
	}
	else
	{
		sp->setPlotStyle(Bar(barsRad));
		pointStyle=VerticalBars;
		style_ = Qwt3D::USER;
	}

	if (d_autoscale)
		findBestLayout();
	deleteData(data,columns);
}

void Graph3D::addData(Table* table, int xCol,int yCol,int zCol,
		double xl, double xr, double yl, double yr, double zl, double zr)
{
	worksheet=table;
	int r=table->numRows();

	QString s=table->colName(xCol)+"(X),";
	s+=table->colName(yCol)+"(Y),";
	s+=table->colName(zCol)+"(Z)";
	plotAssociation = s;

	int i,j,columns=0;
	double xv,yv;
	for ( i = 0; i < r; i++)
	{
		if (!table->column(xCol)->isInvalid(i) && !table->column(yCol)->isInvalid(i) && !table->column(zCol)->isInvalid(i))
		{
			xv=table->cell(i,xCol);
			yv=table->cell(i,yCol);
			if (xv >= xl && xv <= xr && yv >= yl && yv <= yr)
				columns++;
		}
	}

	if (columns == 0)
		columns++;

	Qwt3D::Triple **data=allocateData(columns,columns);
	for (j = 0; j < columns; j++)
	{
		int k=0;
		for ( i = 0; i < r; i++)
		{
			if (!table->column(xCol)->isInvalid(i) && !table->column(yCol)->isInvalid(i) && !table->column(zCol)->isInvalid(i))
			{
				xv=table->cell(i,xCol);
				yv=table->cell(i,yCol);
				if (xv >= xl && xv <= xr && yv >= yl && yv <= yr)
				{
					double zv=table->cell(i,zCol);
					if (zv > zr)
						data[k][j] = Triple(xv,yv,zr);
					else if (zv < zl)
						data[k][j] = Triple(xv,yv,zl);
					else
						data[k][j] = Triple(xv,yv,zv);
					k++;
				}
			}
		}
	}
	sp->makeCurrent();
	sp->loadFromData (data, columns, columns, false, false);
	sp->createCoordinateSystem(Triple(xl, yl, zl), Triple(xr, yr, zr));
	sp->legend()->setLimits(zl, zr);
	sp->legend()->setMajors(legendMajorTicks);

	deleteData(data,columns);
}

void Graph3D::updateData(Table* table)
{
	if (func)// function plot
		return;

	QString name = plotAssociation;
	int pos=name.indexOf("_",0);
	int posX=name.indexOf("(",pos);
	QString xColName=name.mid(pos+1,posX-pos-1);

	pos=name.indexOf(",",posX);
	posX=name.indexOf("(",pos);
	QString yColName=name.mid(pos+1,posX-pos-1);

	int xCol=table->colIndex(xColName);
	int yCol=table->colIndex(yColName);

	if (name.contains("(Z)",Qt::CaseSensitive))
	{
		pos=name.indexOf(",",posX);
		posX=name.indexOf("(",pos);
		QString zColName=name.mid(pos+1,posX-pos-1);
		int zCol=table->colIndex(zColName);
		updateDataXYZ(table, xCol, yCol, zCol);
	}
	else
		updateDataXY(table, xCol, yCol);

	if (d_autoscale)
		findBestLayout();
	update();
}

void Graph3D::updateDataXY(Table* table, int xCol, int yCol)
{
	int r=table->numRows();
	int i, j, xmesh=0, ymesh=2;

	for (i = 0; i < r; i++)
	{
		if (!table->column(xCol)->isInvalid(i) && !table->column(yCol)->isInvalid(i))
			xmesh++;
	}

	if (xmesh<2)
	{
		sp->setPlotStyle(NOPLOT);
		update();
		return;
	}

	double **data = Matrix::allocateMatrixData(xmesh, ymesh);
	gsl_vector * x = gsl_vector_alloc (xmesh);
	gsl_vector * y = gsl_vector_alloc (xmesh);

	for ( j = 0; j < ymesh; j++)
	{
		int k=0;
		for ( i = 0; i < r; i++)
		{
			if (!table->column(xCol)->isInvalid(i) && !table->column(yCol)->isInvalid(i))
			{
				double xv=table->cell(i,xCol);
				double yv=table->cell(i,yCol);

				gsl_vector_set (x, k, xv);
				gsl_vector_set (y, k, yv);

				data[k][j] =yv;
				k++;
			}
		}
	}

	double minx=gsl_vector_min (x);
	double maxx=gsl_vector_max(x);
	double minz=gsl_vector_min (y);
	double maxz=gsl_vector_max(y);
	double miny, maxy;

	sp->makeCurrent();
	resetNonEmptyStyle();
	sp->coordinates()->axes[Y1].limits (miny,maxy);	//actual Y scale limits
	sp->loadFromData(data, xmesh, ymesh, minx, maxx, miny, maxy);
	sp->legend()->setLimits(minz,maxz);
	sp->legend()->setMajors(legendMajorTicks);

	gsl_vector_free (x);gsl_vector_free (y);
	Matrix::freeMatrixData(data, xmesh);
}

void Graph3D::updateDataXYZ(Table* table, int xCol, int yCol, int zCol)
{
	int r=table->numRows();
	int i,j,columns=0;

	for ( i = 0; i < r; i++)
	{
		if (!table->column(xCol)->isInvalid(i) && !table->column(yCol)->isInvalid(i) && !table->column(zCol)->isInvalid(i))
			columns++;
	}

	if (columns<2)
	{
		sp->setPlotStyle(NOPLOT);
		update();
		return;
	}

	Qwt3D::Triple **data=allocateData(columns,columns);
	gsl_vector * z = gsl_vector_alloc (columns);

	for ( j = 0; j < columns; j++)
	{
		int k=0;
		for ( i = 0; i < r; i++)
		{
			if (!table->column(xCol)->isInvalid(i) && !table->column(yCol)->isInvalid(i) && !table->column(zCol)->isInvalid(i))
			{
				double xv=table->cell(i,xCol);
				double yv=table->cell(i,yCol);
				double zv=table->cell(i,zCol);

				gsl_vector_set (z, k, zv);
				data[k][j] = Triple(xv,yv,zv);
				k++;
			}
		}
	}

	double minz=gsl_vector_min(z);
	double maxz=gsl_vector_max(z);
	gsl_vector_free (z);

	sp->makeCurrent();
	resetNonEmptyStyle();

	sp->loadFromData (data, columns, columns, false,false);
	sp->legend()->setLimits(minz,maxz);
	sp->legend()->setMajors(legendMajorTicks);

	deleteData(data,columns);
}

void Graph3D::updateMatrixData(Matrix* m)
{
	int cols=m->numCols();
	int rows=m->numRows();

	double **data = Matrix::allocateMatrixData(rows, cols);
	for (int i = 0; i < rows; i++ ){
		for (int j = 0; j < cols; j++)
			data[i][j] = m->cell(i, j);
	}

	sp->loadFromData(data, rows, cols, m->xStart(), m->xEnd(), m->yStart(), m->yEnd());

	Qwt3D::Axis z_axis = sp->coordinates()->axes[Z1];
	double start, end;
	z_axis.limits (start, end);
	z_axis.setMajors(z_axis.majors());
	z_axis.setMajors(z_axis.minors());

	sp->legend()->setLimits(start, end);
	sp->legend()->setMajors(legendMajorTicks);

	Matrix::freeMatrixData(data, rows);
	if (d_autoscale)
		findBestLayout();
	update();
}

void Graph3D::resetNonEmptyStyle()
{
	if (sp->plotStyle() != Qwt3D::NOPLOT )
		return; // the plot was not previousely emptied

	if (style_== Qwt3D::USER)
	{// reseting the right user plot style
		switch (pointStyle)
		{
			case None:
				break;

			case Dots :
				sp->setPlotStyle(Dot(pointSize, smooth));
				break;

			case VerticalBars :
				sp->setPlotStyle(Bar(barsRad));
				break;

			case HairCross :
				sp->setPlotStyle(CrossHair(crossHairRad, crossHairLineWidth, crossHairSmooth, crossHairBoxed));
				break;

			case Cones :
				sp->setPlotStyle(Cone3D(conesRad, conesQuality));
				break;
		}
	}
	else
		sp->setPlotStyle(style_);
}

UserFunction* Graph3D::userFunction()
{
	if (func)
		return func;
	else
		return 0;
}

void Graph3D::update()
{
	sp->makeCurrent();

	resetAxesLabels();

	sp->updateData();
	sp->updateGL();
}

void Graph3D::adjustLabels(int val)
{
	if (labelsDist != val)
	{
		labelsDist=val;
		sp->coordinates()->adjustLabels(val);
		sp->makeCurrent();
		sp->updateGL();
	}
	emit modified();
}

QFont Graph3D::numbersFont()
{
	return sp->coordinates()->axes[X1].numberFont();
}

void Graph3D::setNumbersFont(const QFont& font)
{
	sp->coordinates()->setNumberFont (font);
	sp->makeCurrent();
	sp->updateGL();
}

void Graph3D::setNumbersFont(const QStringList& lst)
{
	QFont fnt=QFont(lst[1],lst[2].toInt(),lst[3].toInt(),lst[4].toInt());
	sp->coordinates()->setNumberFont(fnt);
}

void Graph3D::setXAxisLabelFont(const QFont& fnt)
{
	sp->coordinates()->axes[X1].setLabelFont(fnt);
	sp->coordinates()->axes[X2].setLabelFont(fnt);
	sp->coordinates()->axes[X3].setLabelFont(fnt);
	sp->coordinates()->axes[X4].setLabelFont(fnt);
}

void Graph3D::setYAxisLabelFont(const QFont& fnt)
{
	sp->coordinates()->axes[Y1].setLabelFont(fnt);
	sp->coordinates()->axes[Y2].setLabelFont(fnt);
	sp->coordinates()->axes[Y3].setLabelFont(fnt);
	sp->coordinates()->axes[Y4].setLabelFont(fnt);
}

void Graph3D::setZAxisLabelFont(const QFont& fnt)
{
	sp->coordinates()->axes[Z1].setLabelFont(fnt);
	sp->coordinates()->axes[Z2].setLabelFont(fnt);
	sp->coordinates()->axes[Z3].setLabelFont(fnt);
	sp->coordinates()->axes[Z4].setLabelFont(fnt);
}

void Graph3D::setXAxisLabelFont(const QStringList& lst)
{
	QFont fnt=QFont(lst[1],lst[2].toInt(),lst[3].toInt(),lst[4].toInt());
	sp->coordinates()->axes[X1].setLabelFont(fnt);
	sp->coordinates()->axes[X2].setLabelFont(fnt);
	sp->coordinates()->axes[X3].setLabelFont(fnt);
	sp->coordinates()->axes[X4].setLabelFont(fnt);
}

void Graph3D::setYAxisLabelFont(const QStringList& lst)
{
	QFont fnt=QFont(lst[1],lst[2].toInt(),lst[3].toInt(),lst[4].toInt());
	sp->coordinates()->axes[Y1].setLabelFont(fnt);
	sp->coordinates()->axes[Y2].setLabelFont(fnt);
	sp->coordinates()->axes[Y3].setLabelFont(fnt);
	sp->coordinates()->axes[Y4].setLabelFont(fnt);
}

void Graph3D::setZAxisLabelFont(const QStringList& lst)
{
	QFont fnt=QFont(lst[1],lst[2].toInt(),lst[3].toInt(),lst[4].toInt());
	sp->coordinates()->axes[Z1].setLabelFont(fnt);
	sp->coordinates()->axes[Z2].setLabelFont(fnt);
	sp->coordinates()->axes[Z3].setLabelFont(fnt);
	sp->coordinates()->axes[Z4].setLabelFont(fnt);
}

QStringList Graph3D::axisTickLengths()
{
	QStringList lst;
	double majorl,minorl;

	sp->coordinates()->axes[X1].ticLength (majorl,minorl);
	lst<<QString::number(majorl);
	lst<<QString::number(minorl);

	sp->coordinates()->axes[Y1].ticLength (majorl,minorl);
	lst<<QString::number(majorl);
	lst<<QString::number(minorl);

	sp->coordinates()->axes[Z1].ticLength (majorl,minorl);
	lst<<QString::number(majorl);
	lst<<QString::number(minorl);

	return lst;
}

void Graph3D::setTickLengths(const QStringList& lst)
{
	double majorl,minorl;
	QStringList tick_length = lst;
	if (int(lst.count()) > 6)
		tick_length.removeAll(tick_length.first());

	majorl=tick_length[0].toDouble();
	minorl=tick_length[1].toDouble();
	sp->coordinates()->axes[X1].setTicLength (majorl,minorl);
	sp->coordinates()->axes[X2].setTicLength (majorl,minorl);
	sp->coordinates()->axes[X3].setTicLength (majorl,minorl);
	sp->coordinates()->axes[X4].setTicLength (majorl,minorl);

	majorl=tick_length[2].toDouble();
	minorl=tick_length[3].toDouble();
	sp->coordinates()->axes[Y1].setTicLength (majorl,minorl);
	sp->coordinates()->axes[Y2].setTicLength (majorl,minorl);
	sp->coordinates()->axes[Y3].setTicLength (majorl,minorl);
	sp->coordinates()->axes[Y4].setTicLength (majorl,minorl);

	majorl=tick_length[4].toDouble();
	minorl=tick_length[5].toDouble();
	sp->coordinates()->axes[Z1].setTicLength (majorl,minorl);
	sp->coordinates()->axes[Z2].setTicLength (majorl,minorl);
	sp->coordinates()->axes[Z3].setTicLength (majorl,minorl);
	sp->coordinates()->axes[Z4].setTicLength (majorl,minorl);
}

void Graph3D::updateTickLength(int axis,double majorLength, double minorLength)
{
	double majorl,minorl;
	switch(axis)
	{
		case 0:
			sp->coordinates()->axes[X1].ticLength (majorl,minorl);
			if (majorl != majorLength || minorl != minorLength)
			{
				sp->coordinates()->axes[X1].setTicLength (majorLength,minorLength);
				sp->coordinates()->axes[X2].setTicLength (majorLength,minorLength);
				sp->coordinates()->axes[X3].setTicLength (majorLength,minorLength);
				sp->coordinates()->axes[X4].setTicLength (majorLength,minorLength);
			}
			break;

		case 1:
			sp->coordinates()->axes[Y1].ticLength (majorl,minorl);
			if (majorl != majorLength || minorl != minorLength)
			{
				sp->coordinates()->axes[Y1].setTicLength (majorLength,minorLength);
				sp->coordinates()->axes[Y2].setTicLength (majorLength,minorLength);
				sp->coordinates()->axes[Y3].setTicLength (majorLength,minorLength);
				sp->coordinates()->axes[Y4].setTicLength (majorLength,minorLength);
			}
			break;

		case 2:
			sp->coordinates()->axes[Z1].ticLength (majorl,minorl);
			if (majorl != majorLength || minorl != minorLength)
			{
				sp->coordinates()->axes[Z1].setTicLength (majorLength,minorLength);
				sp->coordinates()->axes[Z2].setTicLength (majorLength,minorLength);
				sp->coordinates()->axes[Z3].setTicLength (majorLength,minorLength);
				sp->coordinates()->axes[Z4].setTicLength (majorLength,minorLength);
			}
			break;
	}
	sp->updateGL();
}

void Graph3D::rotationChanged(double, double, double)
{
	emit modified();
}

void Graph3D::scaleChanged(double, double, double)
{
	emit modified();
}

void Graph3D::shiftChanged(double, double, double)
{
	emit modified();
}

void Graph3D::zoomChanged(double)
{
	emit modified();
}

void Graph3D::resetAxesLabels()
{
	sp->coordinates()->axes[X1].setLabelString(labels[0]);
	sp->coordinates()->axes[X2].setLabelString(labels[0]);
	sp->coordinates()->axes[X3].setLabelString(labels[0]);
	sp->coordinates()->axes[X4].setLabelString(labels[0]);

	sp->coordinates()->axes[Y1].setLabelString(labels[1]);
	sp->coordinates()->axes[Y2].setLabelString(labels[1]);
	sp->coordinates()->axes[Y3].setLabelString(labels[1]);
	sp->coordinates()->axes[Y4].setLabelString(labels[1]);

	sp->coordinates()->axes[Z1].setLabelString(labels[2]);
	sp->coordinates()->axes[Z2].setLabelString(labels[2]);
	sp->coordinates()->axes[Z3].setLabelString(labels[2]);
	sp->coordinates()->axes[Z4].setLabelString(labels[2]);
}

void Graph3D::setAxesLabels(const QStringList& l)
{
	QString label= l[0];
	sp->coordinates()->axes[X1].setLabelString(label);
	sp->coordinates()->axes[X2].setLabelString(label);
	sp->coordinates()->axes[X3].setLabelString(label);
	sp->coordinates()->axes[X4].setLabelString(label);

	label= l[1];
	sp->coordinates()->axes[Y1].setLabelString(label);
	sp->coordinates()->axes[Y2].setLabelString(label);
	sp->coordinates()->axes[Y3].setLabelString(label);
	sp->coordinates()->axes[Y4].setLabelString(label);

	label= l[2];
	sp->coordinates()->axes[Z1].setLabelString(label);
	sp->coordinates()->axes[Z2].setLabelString(label);
	sp->coordinates()->axes[Z3].setLabelString(label);
	sp->coordinates()->axes[Z4].setLabelString(label);

	labels=l;
}

void Graph3D::updateLabel(int axis,const QString& label, const QFont& f)
{
	switch(axis)
	{
		case 0:
			if (labels[0] != label)
			{
				sp->coordinates()->axes[X1].setLabelString(label);
				sp->coordinates()->axes[X2].setLabelString(label);
				sp->coordinates()->axes[X3].setLabelString(label);
				sp->coordinates()->axes[X4].setLabelString(label);
				labels[0]=label;
			}
			if (sp->coordinates()->axes[X1].labelFont() != f)
			{
				sp->coordinates()->axes[X1].setLabelFont (f);
				sp->coordinates()->axes[X2].setLabelFont (f);
				sp->coordinates()->axes[X3].setLabelFont (f);
				sp->coordinates()->axes[X4].setLabelFont (f);
			}
			break;

		case 1:
			if (labels[1] != label)
			{
				sp->coordinates()->axes[Y1].setLabelString(label);
				sp->coordinates()->axes[Y2].setLabelString(label);
				sp->coordinates()->axes[Y3].setLabelString(label);
				sp->coordinates()->axes[Y4].setLabelString(label);

				labels[1]=label;
			}
			if (sp->coordinates()->axes[Y1].labelFont() != f)
			{
				sp->coordinates()->axes[Y1].setLabelFont (f);
				sp->coordinates()->axes[Y2].setLabelFont (f);
				sp->coordinates()->axes[Y3].setLabelFont (f);
				sp->coordinates()->axes[Y4].setLabelFont (f);
			}
			break;

		case 2:
			if (labels[2] != label)
			{
				sp->coordinates()->axes[Z1].setLabelString(label);
				sp->coordinates()->axes[Z2].setLabelString(label);
				sp->coordinates()->axes[Z3].setLabelString(label);
				sp->coordinates()->axes[Z4].setLabelString(label);
				labels[2]=label;
			}
			if (sp->coordinates()->axes[Z1].labelFont() != f)
			{
				sp->coordinates()->axes[Z1].setLabelFont (f);
				sp->coordinates()->axes[Z2].setLabelFont (f);
				sp->coordinates()->axes[Z3].setLabelFont (f);
				sp->coordinates()->axes[Z4].setLabelFont (f);
			}
			break;
	}

	sp->makeCurrent();
	sp->updateGL();
	emit modified();
}

QFont Graph3D::xAxisLabelFont()
{
	return sp->coordinates()->axes[X1].labelFont();
}

QFont Graph3D::yAxisLabelFont()
{
	return sp->coordinates()->axes[Y1].labelFont();
}

QFont Graph3D::zAxisLabelFont()
{
	return sp->coordinates()->axes[Z1].labelFont();
}

double Graph3D::xStart()
{
	double start,stop;
	sp->coordinates()->axes[X1].limits (start,stop);
	return start;
}

double Graph3D::xStop()
{
	double start,stop;
	sp->coordinates()->axes[X1].limits (start,stop);
	return stop;
}

double Graph3D::yStart()
{
	double start,stop;
	sp->coordinates()->axes[Y1].limits (start,stop);
	return start;
}

double Graph3D::yStop()
{
	double start,stop;
	sp->coordinates()->axes[Y1].limits (start,stop);
	return stop;
}

double Graph3D::zStart()
{
	double start,stop;
	sp->coordinates()->axes[Z1].limits (start,stop);
	return start;
}

double Graph3D::zStop()
{
	double start,stop;
	sp->coordinates()->axes[Z1].limits(start, stop);
	return stop;
}

QStringList Graph3D::scaleLimits()
{
	QStringList limits;
	double start,stop;
	int majors,minors;

	sp->coordinates()->axes[X1].limits (start,stop);
	majors=sp->coordinates()->axes[X1].majors();
	minors=sp->coordinates()->axes[X1].minors();

	limits<<QString::number(start);
	limits<<QString::number(stop);
	limits<<QString::number(majors);
	limits<<QString::number(minors);
	limits<<QString::number(scaleType[0]);

	sp->coordinates()->axes[Y1].limits (start,stop);
	majors=sp->coordinates()->axes[Y1].majors();
	minors=sp->coordinates()->axes[Y1].minors();

	limits<<QString::number(start);
	limits<<QString::number(stop);
	limits<<QString::number(majors);
	limits<<QString::number(minors);
	limits<<QString::number(scaleType[1]);

	sp->coordinates()->axes[Z1].limits (start,stop);
	majors=sp->coordinates()->axes[Z1].majors();
	minors=sp->coordinates()->axes[Z1].minors();

	limits<<QString::number(start);
	limits<<QString::number(stop);
	limits<<QString::number(majors);
	limits<<QString::number(minors);
	limits<<QString::number(scaleType[2]);

	return limits;
}

QStringList Graph3D::scaleTicks()
{
	QStringList limits;
	int majors,minors;

	majors=sp->coordinates()->axes[X1].majors();
	minors=sp->coordinates()->axes[X1].minors();
	limits<<QString::number(majors);
	limits<<QString::number(minors);

	majors=sp->coordinates()->axes[Y1].majors();
	minors=sp->coordinates()->axes[Y1].minors();
	limits<<QString::number(majors);
	limits<<QString::number(minors);

	majors=sp->coordinates()->axes[Z1].majors();
	minors=sp->coordinates()->axes[Z1].minors();
	limits<<QString::number(majors);
	limits<<QString::number(minors);

	return limits;
}

void Graph3D::updateScale(int axis,const QStringList& options)
{
	double xMin,xMax,yMin,yMax,zMin,zMax;
	double *min, *max;
	int majors, minors, newMaj, newMin;
	Qwt3D::Axis *targetAxes[4];

	sp->makeCurrent();

	switch (axis) {
		case 0:
			targetAxes[0] = &sp->coordinates()->axes[X1];
			targetAxes[1] = &sp->coordinates()->axes[X2];
			targetAxes[2] = &sp->coordinates()->axes[X3];
			targetAxes[3] = &sp->coordinates()->axes[X4];
			min = &xMin; max = &xMax;
			break;
		case 1:
			targetAxes[0] = &sp->coordinates()->axes[Y1];
			targetAxes[1] = &sp->coordinates()->axes[Y2];
			targetAxes[2] = &sp->coordinates()->axes[Y3];
			targetAxes[3] = &sp->coordinates()->axes[Y4];
			min = &yMin; max = &yMax;
			break;
		case 2:
			targetAxes[0] = &sp->coordinates()->axes[Z1];
			targetAxes[1] = &sp->coordinates()->axes[Z2];
			targetAxes[2] = &sp->coordinates()->axes[Z3];
			targetAxes[3] = &sp->coordinates()->axes[Z4];
			min = &zMin; max = &zMax;
			break;
        default:
          throw runtime_error("invalid axis");
	}

	majors = targetAxes[0]->majors();
	minors = targetAxes[0]->minors();

	sp->coordinates()->axes[X1].limits(xMin,xMax);
	sp->coordinates()->axes[Y1].limits(yMin,yMax);
	sp->coordinates()->axes[Z1].limits(zMin,zMax);

	if (*min != options[0].toDouble() || *max != options[1].toDouble()) {
			*min = options[0].toDouble();
			*max = options[1].toDouble();
			if (func) {
					func->setDomain(xMin, xMax, yMin, yMax);
					func->setMinZ(zMin); func->setMaxZ(zMax);
					func->create();
					sp->createCoordinateSystem(Triple(xMin, yMin, zMin), Triple(xMax, yMax, zMax));
			} else
					updateScales(xMin, xMax, yMin, yMax, zMin, zMax);
			sp->legend()->setLimits(zMin, zMax);
	}

	if (QString::number(scaleType[axis]) != options[4]) {
			if (options[4] == "0") {
					targetAxes[0]->setScale(LINEARSCALE);
					if (axis == 2) sp->legend()->setScale(LINEARSCALE);
					scaleType[axis] = 0;
			} else {
					targetAxes[0]->setScale(LOG10SCALE);
					if (axis == 2) sp->legend()->setScale(LOG10SCALE);
					scaleType[axis] = 1;
			}
	}


	newMaj = options[2].toInt();
	if (majors != newMaj)
			for (int i=0; i<4; i++)
					targetAxes[i]->setMajors(newMaj);

	newMin = options[3].toInt();
	if (minors != newMin)
			for (int i=0; i<4; i++)
					targetAxes[i]->setMinors(newMin);

	update();
	emit modified();
}

void Graph3D::updateScales(double xl, double xr, double yl, double yr, double zl, double zr)
{
	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

	if (d_matrix)
		updateScalesFromMatrix(xl, xr, yl, yr, zl, zr);
	else
	{
		QString name= plotAssociation;

		int pos=name.indexOf("_",0);
		int posX=name.indexOf("(",pos);
		QString xColName=name.mid(pos+1,posX-pos-1);
		int xCol=worksheet->colIndex(xColName);

		pos=name.indexOf(",",posX);
		posX=name.indexOf("(",pos);
		QString yColName=name.mid(pos+1,posX-pos-1);
		int yCol=worksheet->colIndex(yColName);

		if (name.endsWith("(Z)",Qt::CaseSensitive))
		{
			pos=name.indexOf(",",posX);
			posX=name.indexOf("(",pos);
			QString zColName=name.mid(pos+1,posX-pos-1);
			int zCol=worksheet->colIndex(zColName);

			updateScales(xl, xr, yl, yr, zl, zr, xCol, yCol, zCol);
		}
		else if (name.endsWith("(Y)",Qt::CaseSensitive))
			updateScales(xl, xr, yl, yr, zl, zr, xCol, yCol);
	}

	QApplication::restoreOverrideCursor();
}

void Graph3D::updateScalesFromMatrix(double xl, double xr, double yl,
		double yr, double zl, double zr)
{
	double xStart = qMin(d_matrix->xStart(), d_matrix->xEnd());
	double xEnd = qMax(d_matrix->xStart(), d_matrix->xEnd());
	double yStart = qMin(d_matrix->yStart(), d_matrix->yEnd());
	double yEnd = qMax(d_matrix->yStart(), d_matrix->yEnd());

	double dx = fabs((xEnd - xStart)/double(d_matrix->numCols()-1));
	double dy = fabs((yEnd - yStart)/double(d_matrix->numRows()-1));

	int nc = int(fabs(xr - xl)/dx)+1;
	int nr = int(fabs(yr - yl)/dy)+1;

    double x_begin = qMin(xl, xr);
	double y_begin = qMin(yl, yr);

	double **data_matrix = Matrix::allocateMatrixData(nc, nr);
	for (int i = 0; i < nc; i++){
		double x = x_begin + i*dx;
		for (int j = 0; j < nr; j++){
			double y = y_begin + j*dy;
			if (x >= xStart && x <= xEnd && y >= yStart && y <= yEnd){
				int k = abs((y - yStart)/dy);
				int l = abs((x - xStart)/dx);
				double val = d_matrix->cell(k, l);
				if (val > zr)
					data_matrix[i][j] = zr;
				else if (val < zl)
					data_matrix[i][j] = zl;
				else
					data_matrix[i][j] = val;
			} else
				data_matrix[i][j] = 0.0;
		}
	}
	sp->loadFromData(data_matrix, nc, nr, xl, xr, yl, yr);
	Matrix::freeMatrixData(data_matrix, nc);

	sp->createCoordinateSystem(Triple(xl, yl, zl), Triple(xr, yr, zr));
	sp->legend()->setLimits(zl, zr);
	sp->legend()->setMajors(legendMajorTicks);
	update();
}

void Graph3D::updateScales(double xl, double xr, double yl, double yr,double zl, double zr,
		int xcol, int ycol)
{
	int r=worksheet->numRows();
	int i, j, xmesh=0, ymesh=2;
	double xv, yv;

	for (i = 0; i < r; i++)
	{
		if (!worksheet->column(xcol)->isInvalid(i) && !worksheet->column(ycol)->isInvalid(i))
		{
			xv=worksheet->cell(i,xcol);
			if (xv >= xl && xv <= xr)
				xmesh++;
		}
	}

	if (xmesh == 0)
		xmesh++;

	double **data = Matrix::allocateMatrixData(xmesh, ymesh);

	for ( j = 0; j < ymesh; j++)
	{
		int k=0;
		for ( i = 0; i < r; i++)
		{
			if (!worksheet->column(xcol)->isInvalid(i) && !worksheet->column(ycol)->isInvalid(i))
			{
				xv=worksheet->cell(i,xcol);
				if (xv >= xl && xv <= xr)
				{
					yv=worksheet->cell(i,ycol);
					if (yv > zr)
						data[k][j] = zr;
					else if (yv < zl)
						data[k][j] = zl;
					else
						data[k][j] = yv;
					k++;
				}
			}
		}
	}

	sp->loadFromData(data, xmesh, ymesh, xl, xr, yl, yr);
	sp->createCoordinateSystem(Triple(xl, yl, zl), Triple(xr, yr, zr));
	Matrix::freeMatrixData(data, xmesh);
}

void Graph3D::updateScales(double xl, double xr, double yl, double yr, double zl, double zr,
		int xCol, int yCol, int zCol)
{
	int r=worksheet->numRows();
	int i,j,columns=0;
	double xv, yv, zv;
	for ( i = 0; i < r; i++)
	{
		if (!worksheet->column(xCol)->isInvalid(i) && !worksheet->column(yCol)->isInvalid(i) && !worksheet->column(zCol)->isInvalid(i))
		{
			xv=worksheet->cell(i,xCol);
			yv=worksheet->cell(i,yCol);
			if (xv >= xl && xv <= xr && yv >= yl && yv <= yr)
				columns++;
		}
	}

	if (columns == 0)
		columns++;

	Qwt3D::Triple **data=allocateData(columns,columns);
	for ( j = 0; j < columns; j++)
	{
		int k=0;
		for ( i = 0; i < r; i++)
		{
			if (!worksheet->column(xCol)->isInvalid(i) && !worksheet->column(yCol)->isInvalid(i) && !worksheet->column(zCol)->isInvalid(i))
			{
				xv=worksheet->cell(i,xCol);
				yv=worksheet->cell(i,yCol);
				if (xv >= xl && xv <= xr && yv >= yl && yv <= yr )
				{
					zv=worksheet->cell(i,zCol);
					if (zv > zr)
						data[k][j] = Triple(xv,yv,zr);
					else if (zv < zl)
						data[k][j] = Triple(xv,yv,zl);
					else
						data[k][j] = Triple(xv,yv,zv);
					k++;
				}
			}
		}
	}
	sp->loadFromData (data, columns, columns, false,false);
	sp->createCoordinateSystem(Triple(xl, yl, zl), Triple(xr, yr, zr));
	deleteData(data,columns);
}

void Graph3D::setTicks(const QStringList& options)
{
	int min,maj;
	if (int(options.count()) == 6)
	{
		maj=options[0].toInt();
		sp->coordinates()->axes[X1].setMajors(maj);
		sp->coordinates()->axes[X2].setMajors(maj);
		sp->coordinates()->axes[X3].setMajors(maj);
		sp->coordinates()->axes[X4].setMajors(maj);

		min=options[1].toInt();
		sp->coordinates()->axes[X1].setMinors(min);
		sp->coordinates()->axes[X2].setMinors(min);
		sp->coordinates()->axes[X3].setMinors(min);
		sp->coordinates()->axes[X4].setMinors(min);

		maj=options[2].toInt();
		sp->coordinates()->axes[Y1].setMajors(maj);
		sp->coordinates()->axes[Y2].setMajors(maj);
		sp->coordinates()->axes[Y3].setMajors(maj);
		sp->coordinates()->axes[Y4].setMajors(maj);

		min=options[3].toInt();
		sp->coordinates()->axes[Y1].setMinors(min);
		sp->coordinates()->axes[Y2].setMinors(min);
		sp->coordinates()->axes[Y3].setMinors(min);
		sp->coordinates()->axes[Y4].setMinors(min);

		maj=options[4].toInt();
		sp->coordinates()->axes[Z1].setMajors(maj);
		sp->coordinates()->axes[Z2].setMajors(maj);
		sp->coordinates()->axes[Z3].setMajors(maj);
		sp->coordinates()->axes[Z4].setMajors(maj);

		min=options[5].toInt();
		sp->coordinates()->axes[Z1].setMinors(min);
		sp->coordinates()->axes[Z2].setMinors(min);
		sp->coordinates()->axes[Z3].setMinors(min);
		sp->coordinates()->axes[Z4].setMinors(min);
	}
	else
	{
		maj=options[1].toInt();
		sp->coordinates()->axes[X1].setMajors(maj);
		sp->coordinates()->axes[X2].setMajors(maj);
		sp->coordinates()->axes[X3].setMajors(maj);
		sp->coordinates()->axes[X4].setMajors(maj);

		min=options[2].toInt();
		sp->coordinates()->axes[X1].setMinors(min);
		sp->coordinates()->axes[X2].setMinors(min);
		sp->coordinates()->axes[X3].setMinors(min);
		sp->coordinates()->axes[X4].setMinors(min);

		maj=options[3].toInt();
		sp->coordinates()->axes[Y1].setMajors(maj);
		sp->coordinates()->axes[Y2].setMajors(maj);
		sp->coordinates()->axes[Y3].setMajors(maj);
		sp->coordinates()->axes[Y4].setMajors(maj);

		min=options[4].toInt();
		sp->coordinates()->axes[Y1].setMinors(min);
		sp->coordinates()->axes[Y2].setMinors(min);
		sp->coordinates()->axes[Y3].setMinors(min);
		sp->coordinates()->axes[Y4].setMinors(min);

		maj=options[5].toInt();
		sp->coordinates()->axes[Z1].setMajors(maj);
		sp->coordinates()->axes[Z2].setMajors(maj);
		sp->coordinates()->axes[Z3].setMajors(maj);
		sp->coordinates()->axes[Z4].setMajors(maj);

		min=options[6].toInt();
		sp->coordinates()->axes[Z1].setMinors(min);
		sp->coordinates()->axes[Z2].setMinors(min);
		sp->coordinates()->axes[Z3].setMinors(min);
		sp->coordinates()->axes[Z4].setMinors(min);
	}
}

void Graph3D::setColors(const QColor& meshColor,const QColor& axesColor,const QColor& numColor,
		const QColor& labelColor,const QColor& bgColor,const QColor& gridColor)
{
	if (meshCol != meshColor)
	{
		sp->setMeshColor(Qt2GL(meshColor));
		meshCol=meshColor;
	}

	if(axesCol != axesColor)
	{
		sp->coordinates()->setAxesColor(Qt2GL(axesColor));
		axesCol=axesColor;
	}

	if(numCol !=numColor)
	{
		sp->coordinates()->setNumberColor(Qt2GL(numColor));
		numCol=numColor;
	}

	if(labelsCol !=labelColor)
	{
		sp->coordinates()->setLabelColor(Qt2GL(labelColor));
		labelsCol=labelColor;
	}

	if(bgCol !=bgColor)
	{
		sp->setBackgroundColor(Qt2GL(bgColor));
		bgCol=bgColor;
	}

	if(gridCol !=gridColor)
	{
		sp->coordinates()->setGridLinesColor(Qt2GL(gridColor));
		gridCol=gridColor;
	}
}

void Graph3D::setColors(const QStringList& colors)
{
	meshCol=QColor(COLORVALUE(colors[1]));
	sp->setMeshColor(Qt2GL(meshCol));

	axesCol=QColor(COLORVALUE(colors[2]));
	sp->coordinates()->setAxesColor(Qt2GL(axesCol));

	numCol=QColor(COLORVALUE(colors[3]));
	sp->coordinates()->setNumberColor(Qt2GL(numCol));

	labelsCol=QColor(COLORVALUE(colors[4]));
	sp->coordinates()->setLabelColor(Qt2GL(labelsCol));

	bgCol=QColor(COLORVALUE(colors[5]));
	sp->setBackgroundColor(Qt2GL(bgCol));

	gridCol=QColor(COLORVALUE(colors[6]));
	sp->coordinates()->setGridLinesColor(Qt2GL(gridCol));

	if ((int)colors.count()>7)
	{
		QColor min=QColor(COLORVALUE(colors[7]));
		QColor max=QColor(COLORVALUE(colors[8]));
		alpha = colors[9].toDouble();
		if ((int)colors.count() == 11)
            setDataColorMap(colors[10]);
        else
		    setDataColors(min,max);
	}
}

void Graph3D::updateColors(const QColor& meshColor,const QColor& axesColor,const QColor& numColor,
		const QColor& labelColor,const QColor& bgColor,const QColor& gridColor)
{
	if (meshCol != meshColor)
	{
		sp->setMeshColor(Qt2GL(meshColor));
		meshCol=meshColor;
	}

	if(axesCol != axesColor)
	{
		sp->coordinates()->setAxesColor(Qt2GL(axesColor));
		axesCol=axesColor;
	}

	if(numCol !=numColor)
	{
		sp->coordinates()->setNumberColor(Qt2GL(numColor));
		numCol=numColor;
	}

	if(labelsCol !=labelColor)
	{
		sp->coordinates()->setLabelColor(Qt2GL(labelColor));
		labelsCol=labelColor;
	}

	if(bgCol !=bgColor)
	{
		sp->setBackgroundColor(Qt2GL(bgColor));
		bgCol=bgColor;
	}

	if(gridCol !=gridColor)
	{
		sp->coordinates()->setGridLinesColor(Qt2GL(gridColor));
		gridCol=gridColor;
	}

	sp->updateData();
	sp->updateGL();
	emit modified();
}

void Graph3D::scaleFonts(double factor)
{
	QFont font = sp->coordinates()->axes[X1].numberFont();
	font.setPointSizeF(font.pointSizeF()*factor);
	sp->coordinates()->setNumberFont (font);

	titleFnt.setPointSizeF(factor*titleFnt.pointSizeF());
	sp->setTitleFont(titleFnt.family(),titleFnt.pointSize(),titleFnt.weight(),titleFnt.italic());

	font = xAxisLabelFont();
	font.setPointSizeF(factor*font.pointSizeF());
	setXAxisLabelFont(font);

	font = yAxisLabelFont();
	font.setPointSizeF(factor*font.pointSizeF());
	setYAxisLabelFont(font);

	font = zAxisLabelFont();
	font.setPointSizeF(factor*font.pointSizeF());
	setZAxisLabelFont(font);
}

void Graph3D::resizeEvent ( QResizeEvent *e)
{
	sp->makeCurrent();
	sp->resize(e->size());

	if (!ignoreFonts && this->isVisible())
	{
		double ratio=(double)e->size().height()/(double)e->oldSize().height();
		scaleFonts(ratio);
	}

	sp->updateGL();
	emit resizedWindow(this);
	emit modified();
	QMdiSubWindow::resizeEvent(e);
}

void Graph3D::contextMenuEvent(QContextMenuEvent *e)
{
	if (widget()->geometry().contains(e->pos())) {
		emit showContextMenu();
	} else {
		emit showTitleBarMenu();
	}
	e->accept();
}

void Graph3D::setFramed()
{
	if (sp->coordinates()->style() == FRAME)
		return;

	sp->makeCurrent();
	sp->setCoordinateStyle(FRAME);
}

void Graph3D::setBoxed()
{
	if (sp->coordinates()->style() == BOX)
		return;

	sp->makeCurrent();
	sp->setCoordinateStyle(BOX);
}

void Graph3D::setNoAxes()
{
	if (sp->coordinates()->style() == NOCOORD)
		return;

	sp->makeCurrent();
	sp->setCoordinateStyle(NOCOORD);
}

void Graph3D::setNoGrid()
{
	if (sp->plotStyle() == FILLED)
		return;

	sp->makeCurrent();
	sp->setPlotStyle(FILLED);
	sp->updateData();
	sp->updateGL();

	style_=FILLED;
	pointStyle = None;
}

void Graph3D::setFilledMesh()
{
	if (sp->plotStyle() == FILLEDMESH)
		return;

	sp->makeCurrent();
	sp->setPlotStyle(FILLEDMESH);
	sp->updateData();
	sp->updateGL();

	style_=FILLEDMESH;
	pointStyle = None;
}

void Graph3D::setHiddenLineGrid()
{
	if (sp->plotStyle() == HIDDENLINE)
		return;

	sp->makeCurrent();
	sp->setPlotStyle(HIDDENLINE);
	sp->showColorLegend(false);
	sp->updateData();
	sp->updateGL();

	style_=HIDDENLINE;
	pointStyle = None;
	legendOn=false;
}

void Graph3D::setLineGrid()
{
	if (sp->plotStyle() == WIREFRAME)
		return;

	sp->makeCurrent();
	sp->setPlotStyle(WIREFRAME);
	sp->showColorLegend(false);
	sp->updateData();
	sp->updateGL();

	pointStyle = None;
	style_=WIREFRAME;
	legendOn=false;
}

void Graph3D::setPointsMesh()
{
	if (!sp  || pointStyle == Dots)
		return;

	pointStyle=Dots;
	style_=Qwt3D::USER;

	sp->makeCurrent();
	sp->setPlotStyle(Dot(pointSize, smooth));
	sp->updateData();
	sp->updateGL();
}

void Graph3D::setConesMesh()
{
	if (!sp  || pointStyle == Cones )
		return;

	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

	pointStyle=Cones;
	style_=Qwt3D::USER;

	sp->makeCurrent();
	sp->setPlotStyle(Cone3D(conesRad,conesQuality));
	sp->updateData();
	sp->updateGL();

	QApplication::restoreOverrideCursor();
}

void Graph3D::setCrossMesh()
{
	if (!sp || pointStyle == HairCross)
		return;

	pointStyle=HairCross;
	style_=Qwt3D::USER;

	sp->makeCurrent();
	sp->setPlotStyle(CrossHair(crossHairRad, crossHairLineWidth,crossHairSmooth,crossHairBoxed));
	sp->updateData();
	sp->updateGL();
}

void Graph3D::clearData()
{
	if (d_matrix)
		d_matrix = 0;
	else if (worksheet)
		worksheet = 0;
	else if (func)
	{
		delete func;
		func = 0;
	}
	plotAssociation = QString();

	sp->makeCurrent();
	sp->loadFromData (0, 0, 0, false, false);
	sp->updateData();
	sp->updateGL();
}

void Graph3D::setBarsPlot()
{
	if (pointStyle == VerticalBars)
		return;

	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

	pointStyle=VerticalBars;
	style_=Qwt3D::USER;

	sp->makeCurrent();
	sp->setPlotStyle(Bar(barsRad));
	sp->updateData();
	sp->updateGL();
	QApplication::restoreOverrideCursor();
}

void Graph3D::setFloorData()
{
	if (sp->floorStyle() == FLOORDATA)
		return;

	sp->makeCurrent();
	sp->setFloorStyle(FLOORDATA);
	sp->updateData();
	sp->updateGL();
}

void Graph3D::setFloorIsolines()
{
	if (sp->floorStyle() == FLOORISO)
		return;

	sp->makeCurrent();
	sp->setFloorStyle(FLOORISO);
	sp->updateData();
	sp->updateGL();
}

void Graph3D::setEmptyFloor()
{
	if (sp->floorStyle() == NOFLOOR)
		return;

	sp->makeCurrent();
	sp->setFloorStyle(NOFLOOR);
	sp->updateData();
	sp->updateGL();
}

void Graph3D::setMeshLineWidth(int lw)
{
	if ((int)sp->meshLineWidth() == lw)
		return;

	sp->makeCurrent();
	sp->setMeshLineWidth((double)lw);
	sp->updateData();
	sp->updateGL();
}

int Graph3D::grids()
{
	return sp->coordinates()->grids();
}

void Graph3D::setGrid(Qwt3D::SIDE s, bool b)
{
	if (!sp)
		return;

	int sum = sp->coordinates()->grids();

	if (b)
		sum |= s;
	else
		sum &= ~s;

	sp->coordinates()->setGridLines(sum!=Qwt3D::NOSIDEGRID, false, sum);
	sp->updateGL();
	emit modified();
}

void Graph3D::setGrid(int grids)
{
	if (!sp)
		return;

	sp->coordinates()->setGridLines(true, false,grids);
}

void Graph3D::setLeftGrid(bool b)
{
	setGrid(Qwt3D::LEFT,b);
}
void Graph3D::setRightGrid(bool b)
{
	setGrid(Qwt3D::RIGHT,b);
}
void Graph3D::setCeilGrid(bool b)
{
	setGrid(Qwt3D::CEIL,b);
}
void Graph3D::setFloorGrid(bool b)
{
	setGrid(Qwt3D::FLOOR,b);
}
void Graph3D::setFrontGrid(bool b)
{
	setGrid(Qwt3D::FRONT,b);
}
void Graph3D::setBackGrid(bool b)
{
	setGrid(Qwt3D::BACK,b);
}

void Graph3D::print()
{
	QPrinter printer;
	printer.setOrientation(QPrinter::Landscape);
	printer.setColorMode (QPrinter::Color);
	printer.setFullPage(false);

	QPrintDialog dialog(&printer, this);
	if (dialog.exec())
	{
		if (IO::save (sp,"scidavis.png","PNG"))
		{
			QPixmap p;
			p.load ("scidavis.png","PNG", Qt::ColorOnly );

			QPainter paint(&printer);
			paint.drawPixmap(QPoint(0,0),p);
			paint.end();

			QFile f("scidavis.png");
			f.remove();
		}
		else
			QMessageBox::about(0,tr("IO Error"),
					tr("Could not print: <h4>") + QString(name()) + "</h4>.");
	}
}

void Graph3D::copyImage()
{
    QApplication::clipboard()->setPixmap(sp->renderPixmap(), QClipboard::Clipboard);
    sp->updateData();
}

void Graph3D::exportImage(const QString& fileName, int quality, bool transparent)
{
	if (transparent)
	{
        QPixmap pic = sp->renderPixmap();
        sp->updateData();

        QBitmap mask(pic.size());
		mask.fill(Qt::color1);
		QPainter p;
		p.begin(&mask);
		p.setPen(Qt::color0);

		QColor background = QColor (Qt::white);
		QRgb backgroundPixel = background.rgb ();
		QImage image = pic.toImage();
		for (int y=0; y<image.height(); y++)
		{
			for ( int x=0; x<image.width(); x++ )
			{
				QRgb rgb = image.pixel(x, y);
				if (rgb == backgroundPixel) // we want the frame transparent
					p.drawPoint( x, y );
			}
		}
		p.end();
		pic.setMask(mask);
		pic.save(fileName, 0, quality);
	}
    else
    {
        QImage im = sp->grabFrameBuffer(true);
        QImageWriter iw(fileName);
        iw.setQuality(quality);
        iw.write(im);
    }
}

void Graph3D::exportPDF(const QString& fileName)
{
	exportVector(fileName);
}

void Graph3D::exportVector(const QString& fileName, const QString& fileType)
{
	if ( fileName.isEmpty() )
	{
		QMessageBox::critical(0, tr("Error"), tr("Please provide a valid file name!"));
        return;
	}

    QString format = fileType;
    format = format.toUpper();

    VectorWriter * gl2ps = (VectorWriter*)IO::outputHandler(format);
    if (gl2ps)
    {
      gl2ps->setTextMode(VectorWriter::NATIVE);
    }
    if (!IO::save(sp, fileName, format))
      throw std::runtime_error((fileType+tr(" not supported")).toStdString());
}

bool Graph3D::eventFilter(QObject *object, QEvent *e)
{
	if (e->type() == QEvent::MouseButtonDblClick && object == (QObject *)this->sp)
	{
		emit showOptionsDialog();
		return true;
	}
	return MyWidget::eventFilter(object, e);
}

void Graph3D::setPointOptions(double size, bool s)
{
	if (pointSize == size && smooth == s)
		return;

	pointSize = size;
	smooth = s;
}

double Graph3D::barsRadius()
{
	if (sp->plotStyle() == Qwt3D::USER && sp->plotStyle() != Qwt3D::POINTS)
		return barsRad;
	else
		return 0.0;
}

void Graph3D::setBarsRadius(double rad)
{
	if (barsRad == rad)
		return;

	barsRad = rad;
}

void Graph3D::updateBars(double rad)
{
	if (barsRad == rad)
		return;

	barsRad = rad;
	sp->setPlotStyle(Bar(barsRad));
	update();
}

void Graph3D::updatePoints(double size, bool sm)
{
	if (pointStyle == Dots && pointSize == size && smooth == sm)
		return;

	pointSize = size;
	smooth = sm;
	pointStyle = Dots;

	Dot d(pointSize, smooth);
	sp->setPlotStyle(d);

	update();
	emit modified();
	emit custom3DActions(this);
}

void Graph3D::updateCones(double rad, int quality)
{
	if (pointStyle == Cones && conesRad == rad && conesQuality == quality)
		return;

	conesRad = rad;
	conesQuality = quality;
	pointStyle = Cones;
	sp->setPlotStyle(Cone3D(conesRad,conesQuality));
	update();
	emit modified();
	emit custom3DActions(this);
}

void Graph3D::setConesOptions(double rad, int quality)
{
	conesRad = rad;
	conesQuality = quality;
}

void Graph3D::updateCross(double rad, double linewidth, bool smooth, bool boxed)
{
	if (pointStyle == HairCross && crossHairRad == rad &&
			crossHairSmooth == smooth && crossHairBoxed == boxed &&
			crossHairLineWidth == linewidth)
		return;

	crossHairRad = rad;
	crossHairLineWidth=linewidth;
	crossHairSmooth = smooth;
	crossHairBoxed = boxed;
	pointStyle = HairCross;

	sp->setPlotStyle(CrossHair(rad,linewidth, smooth, boxed));
	update();
	emit modified();
	emit custom3DActions(this);
}

void Graph3D::setCrossOptions(double rad, double linewidth, bool smooth, bool boxed)
{
	crossHairRad = rad ;
	crossHairLineWidth=linewidth;
	crossHairSmooth = smooth;
	crossHairBoxed = boxed;
}

void Graph3D::setStyle(Qwt3D::COORDSTYLE coord,Qwt3D::FLOORSTYLE floor,
		Qwt3D::PLOTSTYLE plot, Graph3D::PointStyle point)
{
	sp->setCoordinateStyle(coord);
	sp->setFloorStyle(floor);

	if (point == None)
		sp->setPlotStyle(plot);
	else if (point == VerticalBars)
		sp->setPlotStyle(Bar(barsRad));
	else if (point == Dots)
		sp->setPlotStyle(Dot(pointSize, smooth));
	else if (point == HairCross)
		sp->setPlotStyle(CrossHair(crossHairRad, crossHairLineWidth, crossHairSmooth, crossHairBoxed));
	else if (point == Cones)
		sp->setPlotStyle(Cone3D(conesRad, conesQuality));

	pointStyle=point;
	style_=sp->plotStyle() ;
}

void Graph3D::customPlotStyle(int style)
{
	sp->makeCurrent();
	if (sp->plotStyle() == style)
		return;

	switch (style)
	{
		case WIREFRAME  :
			{
				sp->setPlotStyle(WIREFRAME  );
				style_= WIREFRAME ;
				pointStyle = None;

				legendOn = false;
				sp->showColorLegend(legendOn);
				break;
			}

		case FILLED :
			{
				sp->setPlotStyle(FILLED );
				style_= FILLED;
				pointStyle = None;
				break;
			}

		case FILLEDMESH  :
			{
				sp->setPlotStyle(FILLEDMESH);
				style_= FILLEDMESH;
				pointStyle = None;
				break;
			}

		case HIDDENLINE:
			{
				sp->setPlotStyle(HIDDENLINE);
				style_= HIDDENLINE;
				pointStyle = None;
				legendOn = false;
				sp->showColorLegend(legendOn);
				break;
			}

		case Qwt3D::POINTS:
			{
				pointSize = 0.5;
				smooth = true;
				pointStyle=Dots;
				style_ = Qwt3D::USER;

				Dot d(pointSize, smooth);
				sp->setPlotStyle(d);
				break;
			}

		case Qwt3D::USER:
			{
				pointStyle = VerticalBars;
				style_ = Qwt3D::USER;
				sp->setPlotStyle(Bar(barsRad));
				break;
			}
	}

	sp->updateData();
	sp->updateGL();
}

void Graph3D::setStyle(const QStringList& st)
{
	if (st[1] =="nocoord")
		sp->setCoordinateStyle(NOCOORD);
	else if (st[1] =="frame")
		sp->setCoordinateStyle(FRAME);
	else if (st[1] =="box")
		sp->setCoordinateStyle(BOX);

	if (st[2] =="nofloor")
		sp->setFloorStyle(NOFLOOR);
	else if (st[2] =="flooriso")
		sp->setFloorStyle(FLOORISO);
	else if (st[2] =="floordata")
		sp->setFloorStyle(FLOORDATA);

	if (st[3] =="filledmesh")
		sp->setPlotStyle(FILLEDMESH);
	else if (st[3] =="filled")
		sp->setPlotStyle(FILLED);
	else if (st[3] =="points")
	{
		pointSize = st[4].toDouble();

		smooth=false;
		if (st[5] == "1")
			smooth=true;

		sp->setPlotStyle(Dot(pointSize, smooth));
		pointStyle = Dots;
	}
	else if (st[3] =="wireframe")
		sp->setPlotStyle(WIREFRAME);
	else if (st[3] =="hiddenline")
		sp->setPlotStyle(HIDDENLINE);
	else if (st[3] =="bars")
	{
		barsRad = (st[4]).toDouble();
		sp->setPlotStyle(Bar(barsRad));
		pointStyle = VerticalBars;
	}
	else if (st[3] =="cones")
	{
		conesRad = (st[4]).toDouble();
		conesQuality = (st[5]).toInt();

		sp->setPlotStyle(Cone3D(conesRad, conesQuality));
		pointStyle = Cones;
	}
	else if (st[3] =="cross")
	{
		crossHairRad = (st[4]).toDouble();
		crossHairLineWidth = (st[5]).toDouble();

		crossHairSmooth=false;
		if (st[6] == "1")
			crossHairSmooth=true;

		crossHairBoxed=false;
		if (st[7] == "1")
			crossHairBoxed=true;

		sp->setPlotStyle(CrossHair(crossHairRad, crossHairLineWidth, crossHairSmooth, crossHairBoxed));
		pointStyle = HairCross;
	}

	style_ = sp->plotStyle() ;
}

void Graph3D::setRotation(double  xVal,double  yVal,double  zVal)
{
	sp->setRotation(xVal,yVal,zVal);
}

void Graph3D::setScale(double  xVal,double  yVal,double  zVal)
{
	sp->setScale(xVal,yVal,zVal);
}

void Graph3D::setShift(double  xVal,double  yVal,double  zVal)
{
	sp->setShift(xVal,yVal,zVal);
}

void Graph3D::setZoom(double  val)
{
	sp->setZoom(val);
}

void Graph3D::updateZoom(double  val)
{
	if (sp->zoom() == val)
		return;

	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

	sp->makeCurrent();
	sp->setZoom(val);
	sp->updateData();
	sp->updateGL();

	QApplication::restoreOverrideCursor();
}

void Graph3D::updateScaling(double  xVal,double  yVal,double  zVal)
{
	if (sp->xScale() == xVal && sp->yScale() == yVal && sp->zScale() == zVal)
		return;

	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

	sp->setScale(xVal,yVal,zVal);
	sp->updateData();
	sp->updateGL();

	QApplication::restoreOverrideCursor();
}

Qwt3D::PLOTSTYLE Graph3D::plotStyle()
{
	return sp->plotStyle();
}

Qwt3D::FLOORSTYLE Graph3D::floorStyle()
{
	return sp->floorStyle();
}

Qwt3D::COORDSTYLE Graph3D::coordStyle()
{
	return sp->coordinates()->style();
}

QString Graph3D::formula()
{
	if (func)
		return func->function();
	else
		return plotAssociation;
}

QString Graph3D::saveToString(const QString& geometry)
{
	QString s="<SurfacePlot>\n";
	s+= QString(name())+"\t";
	s+= birthDate() + "\n";
	s+= geometry;
	s+= "SurfaceFunction\t";

	sp->makeCurrent();
	if (func)
		s+=func->function()+"\t";
	else
	{
		s+= plotAssociation;
		s+="\t";
	}

	double start,stop;
	sp->coordinates()->axes[X1].limits(start,stop);
	s+=QString::number(start)+"\t";
	s+=QString::number(stop)+"\t";
	sp->coordinates()->axes[Y1].limits(start,stop);
	s+=QString::number(start)+"\t";
	s+=QString::number(stop)+"\t";
	sp->coordinates()->axes[Z1].limits(start,stop);
	s+=QString::number(start)+"\t";
	s+=QString::number(stop)+"\n";

	QString st;
	if (sp->coordinates()->style() == Qwt3D::NOCOORD)
		st="nocoord";
	else if (sp->coordinates()->style() == Qwt3D::BOX)
		st="box";
	else
		st="frame";
	s+="Style\t"+st+"\t";

	switch(sp->floorStyle ())
	{
		case NOFLOOR:
			st="nofloor";
			break;

		case FLOORISO:
			st="flooriso";
			break;

		case FLOORDATA:
			st="floordata";
			break;
	}
	s+=st+"\t";

	switch(sp->plotStyle())
	{
		case USER:
			if (pointStyle == VerticalBars)
				st="bars\t"+QString::number(barsRad);
			else if (pointStyle == Dots)
			{
				st="points\t"+QString::number(pointSize);
				st+="\t"+QString::number(smooth);
			}
			else if (pointStyle == Cones)
			{
				st="cones\t"+QString::number(conesRad);
				st+="\t"+QString::number(conesQuality);
			}
			else if (pointStyle == HairCross)
			{
				st="cross\t"+QString::number(crossHairRad);
				st+="\t"+QString::number(crossHairLineWidth);
				st+="\t"+QString::number(crossHairSmooth);
				st+="\t"+QString::number(crossHairBoxed);
			}
			break;

		case WIREFRAME:
			st="wireframe";
			break;

		case HIDDENLINE:
			st="hiddenline";
			break;

		case FILLED:
			st="filled";
			break;

		case FILLEDMESH:
			st="filledmesh";
			break;

		default:
			;
	}
	s+=st+"\n";

	s+="grids\t";
	s+=QString::number(sp->coordinates()->grids())+"\n";

	s+="title\t";
	s+=title+"\t";
	s+=COLORNAME(titleCol)+"\t";
	s+=titleFnt.family()+"\t";
	s+=QString::number(titleFnt.pointSize())+"\t";
	s+=QString::number(titleFnt.weight())+"\t";
	s+=QString::number(titleFnt.italic())+"\n";

	s+="colors\t";
	s+=COLORNAME(meshCol)+"\t";
	s+=COLORNAME(axesCol)+"\t";
	s+=COLORNAME(numCol)+"\t";
	s+=COLORNAME(labelsCol)+"\t";
	s+=COLORNAME(bgCol)+"\t";
	s+=COLORNAME(gridCol)+"\t";
	s+=COLORNAME(fromColor)+"\t";
	s+=COLORNAME(toColor)+"\t";
	s+=QString::number(alpha) + "\t" + color_map + "\n";

	s+="axesLabels\t";
	s+=labels.join("\t")+"\n";

	s+="tics\t";
	QStringList tl=scaleTicks();
	s+=tl.join("\t")+"\n";

	s+="tickLengths\t";
	tl=axisTickLengths();
	s+=tl.join("\t")+"\n";

	s+="options\t";
	s+=QString::number(legendOn)+"\t";
	s+=QString::number(sp->resolution())+"\t";
	s+=QString::number(labelsDist)+"\n";

	s+="numbersFont\t";
	QFont fnt=sp->coordinates()->axes[X1].numberFont();
	s+=fnt.family()+"\t";
	s+=QString::number(fnt.pointSize())+"\t";
	s+=QString::number(fnt.weight())+"\t";
	s+=QString::number(fnt.italic())+"\n";

	s+="xAxisLabelFont\t";
	fnt=sp->coordinates()->axes[X1].labelFont();
	s+=fnt.family()+"\t";
	s+=QString::number(fnt.pointSize())+"\t";
	s+=QString::number(fnt.weight())+"\t";
	s+=QString::number(fnt.italic())+"\n";

	s+="yAxisLabelFont\t";
	fnt=sp->coordinates()->axes[Y1].labelFont();
	s+=fnt.family()+"\t";
	s+=QString::number(fnt.pointSize())+"\t";
	s+=QString::number(fnt.weight())+"\t";
	s+=QString::number(fnt.italic())+"\n";

	s+="zAxisLabelFont\t";
	fnt=sp->coordinates()->axes[Z1].labelFont();
	s+=fnt.family()+"\t";
	s+=QString::number(fnt.pointSize())+"\t";
	s+=QString::number(fnt.weight())+"\t";
	s+=QString::number(fnt.italic())+"\n";

	s+="rotation\t";
	s+=QString::number(sp->xRotation())+"\t";
	s+=QString::number(sp->yRotation())+"\t";
	s+=QString::number(sp->zRotation())+"\n";

	s+="zoom\t";
	s+=QString::number(sp->zoom())+"\n";

	s+="scaling\t";
	s+=QString::number(sp->xScale())+"\t";
	s+=QString::number(sp->yScale())+"\t";
	s+=QString::number(sp->zScale())+"\n";

	s+="shift\t";
	s+=QString::number(sp->xShift())+"\t";
	s+=QString::number(sp->yShift())+"\t";
	s+=QString::number(sp->zShift())+"\n";

	s+="LineWidth\t";
	s+=QString::number(sp->meshLineWidth())+"\n";
	s+="WindowLabel\t" + windowLabel() + "\t" + QString::number(captionPolicy()) + "\n";
	s+="Orthogonal\t" + QString::number(sp->ortho())+"\n";
	s+="</SurfacePlot>\n";
	return s;
}

void Graph3D::showColorLegend(bool show)
{
	if (legendOn == show)
		return;

	sp->makeCurrent();
	sp->showColorLegend(show);

	legendOn=show;
	sp->updateGL();
	emit modified();
}

void Graph3D::setResolution(int r)
{
	if (sp->resolution() == r)
		return;

	sp->makeCurrent();
	sp->setResolution(r);
	sp->updateData();
	sp->updateGL();
	emit modified();
}

void Graph3D::setTitle(const QStringList& lst)
{
	title=lst[1];
	sp->setTitle(title);

	titleCol=QColor(COLORVALUE(lst[2]));
	sp->setTitleColor(Qt2GL(titleCol));

	titleFnt=QFont(lst[3],lst[4].toInt(),lst[5].toInt(),lst[6].toInt());
	sp->setTitleFont(titleFnt.family(),titleFnt.pointSize(),titleFnt.weight(),titleFnt.italic());
}

void Graph3D::setTitle(const QString& s,const QColor& color,const QFont& font)
{
	if (title != s)
	{
		title=s;
		sp->setTitle(title);
	}

	titleCol=color;
	sp->setTitleColor(Qt2GL(color));

	if (titleFnt != font)
	{
		titleFnt=font;
		sp->setTitleFont(font.family(),font.pointSize(),font.weight(),font.italic());
	}
}

void Graph3D::updateTitle(const QString& s,const QColor& color,const QFont& font)
{
	if (title != s)
	{
		title=s;
		sp->setTitle(title);
	}

	titleCol=color;
	sp->setTitleColor(Qt2GL(color));

	if (titleFnt != font)
	{
		titleFnt=font;
		sp->setTitleFont(font.family(),font.pointSize(),font.weight(),font.italic());
	}

	sp->makeCurrent();
	sp->updateGL();
	emit modified();
}

void Graph3D::setTitleFont(const QFont& font)
{
	if (titleFnt != font)
	{
		titleFnt=font;
		sp->setTitleFont(font.family(),font.pointSize(),font.weight(),font.italic());
	}
}

void Graph3D::setOptions(const QStringList& lst)
{
	legendOn=false;
	if (lst[1].toInt() == 1)
		legendOn=true;
	sp->showColorLegend(legendOn);
	sp->setResolution(lst[2].toInt());
	adjustLabels(lst[3].toInt());
}


void Graph3D::setOptions(bool legend, int r, int dist)
{
	sp->showColorLegend(legend);
	legendOn=legend;
	sp->setResolution(r);
	adjustLabels(dist);
}

Qwt3D::Triple** Graph3D::allocateData(int columns, int rows)
{
	Qwt3D::Triple** data = new Qwt3D::Triple* [columns];

	for ( int i = 0; i < columns; ++i)
	{
		data[i] = new Qwt3D::Triple [rows];
	}
	return data;
}

void Graph3D::deleteData(Qwt3D::Triple **data, int columns)
{
	for ( int i = 0; i < columns; i++)
	{
		delete [] data[i];
	}
	delete [] data;
}

QColor Graph3D::minDataColor()
{
	return fromColor;
}

QColor Graph3D::maxDataColor()
{
	return toColor;
}

void Graph3D::setDataColors(const QColor& cMin, const QColor& cMax)
{
	if (cMin == fromColor && cMax == toColor)
		return;

	fromColor=cMin;
	toColor=cMax;

	Qwt3D::ColorVector cv;

	int size=255;
	double dsize = size;

	double r1=cMax.red()/dsize;
	double r2=cMin.red()/dsize;

	double stepR = (r1-r2)/dsize;

	double g1=cMax.green()/dsize;
	double g2=cMin.green()/dsize;

	double stepG = (g1-g2)/dsize;

	double b1=cMax.blue()/dsize;
	double b2=cMin.blue()/dsize;

	double stepB = (b1-b2)/dsize;

	RGBA rgb;
	for (int i=0; i<size; i++)
	{
		rgb.r = r1-i*stepR;
		rgb.g = g1-i*stepG;
		rgb.b = b1-i*stepB;
		rgb.a = alpha;

		cv.push_back(rgb);
	}

	col_ = new StandardColor(sp);
	col_->setColorVector(cv);
	sp->setDataColor(col_);

	if (legendOn)
	{
		sp->showColorLegend(false);
		sp->showColorLegend(legendOn);
	}
}

void Graph3D::changeTransparency(double t)
{
	if (alpha == t)
		return;

	alpha = t;

	Qwt3D::StandardColor* color=(StandardColor*) sp->dataColor ();
	color->setAlpha(t);

    sp->showColorLegend(legendOn);
	sp->updateData();
	sp->updateGL();
	emit modified();
}

void Graph3D::setTransparency(double t)
{
	if (alpha == t)
		return;

	alpha = t;

	Qwt3D::StandardColor* color=(StandardColor*) sp->dataColor ();
	color->setAlpha(t);
}

void Graph3D::showWorksheet()
{
	if (worksheet)
		worksheet->showMaximized();
	else if (d_matrix)
		d_matrix->showMaximized();
}

void Graph3D::setSmoothMesh(bool smooth)
{
	if (smoothMesh == smooth)
		return;

	smoothMesh = smooth;
	sp->setSmoothMesh(smoothMesh);
	sp->coordinates()->setLineSmooth(smoothMesh);
	sp->updateData();
	sp->updateGL();
}

QString Graph3D::saveAsTemplate(const QString& geometryInfo)
{
	QString s = saveToString(geometryInfo);
	QStringList lst = s.split("\n", QString::SkipEmptyParts);
	QStringList l = lst[3].split("\t");
	l[1] = QString();
	lst[3] = l.join("\t");
	return lst.join("\n");
}

/*!
Turns 3D animation on or off
*/
void Graph3D::animate(bool on)
{
if ( on )
   d_timer->start( animation_redraw_wait ); // Wait this many msecs before redraw
else
    d_timer->stop();
}

void Graph3D::rotate()
{
if (!sp)
   return;

sp->setRotation(int(sp->xRotation() + 1) % 360, int(sp->yRotation() + 1) % 360, int(sp->zRotation() + 1) % 360);
}

void Graph3D::setDataColorMap(const QString& fileName)
{
if (color_map == fileName)
   return;

ColorVector cv;
if (!openColorMap(cv, fileName))
   return;

color_map = fileName;

col_ = new StandardColor(sp);
col_->setColorVector(cv);

sp->setDataColor(col_);
sp->updateData();
sp->showColorLegend(legendOn);
sp->updateGL();
}

bool Graph3D::openColorMap(ColorVector& cv, QString fname)
{
if (fname.isEmpty())
   return false;

using std::ifstream;
ifstream file(QWT3DLOCAL8BIT(fname));
if (!file)
   return false;

RGBA rgb;
cv.clear();

while ( file )
      {
      file >> rgb.r >> rgb.g >> rgb.b;
      file.ignore(10000,'\n');
      if (!file.good())
         break;
      else
          {
          rgb.a = 1;
          rgb.r /= 255;
          rgb.g /= 255;
          rgb.b /= 255;
          cv.push_back(rgb);
          }
      }
return true;
}

void Graph3D::findBestLayout()
{
  	double start, end;
  	sp->coordinates()->axes[X1].limits (start, end);
  	double xScale = 1/fabs(end-start);

  	sp->coordinates()->axes[Y1].limits (start, end);
  	double yScale = 1/fabs(end-start);

  	sp->coordinates()->axes[Z1].limits (start, end);
  	double zScale = 1/fabs(end-start);

  	double d = (sp->hull().maxVertex-sp->hull().minVertex).length();
  	sp->setScale(xScale, yScale, zScale);
  	sp->setZoom(d/sqrt(3));

  	double majl = 0.1/yScale;
  	updateTickLength(0, majl, 0.6*majl);
  	majl = 0.1/xScale;
  	updateTickLength(1, majl, 0.6*majl);
  	updateTickLength(2, majl, 0.6*majl);
}

void Graph3D::copy(Graph3D* g)
{
	if (!g)
        return;

	Graph3D::PointStyle pt = g->pointType();
	if (g->plotStyle() == Qwt3D::USER ){
		switch (pt){
			case Graph3D::None :
				break;

			case Graph3D::Dots :
				setPointOptions(g->pointsSize(), g->smoothPoints());
				break;

			case Graph3D::VerticalBars :
				setBarsRadius(g->barsRadius());
				break;

			case Graph3D::HairCross :
				setCrossOptions(g->crossHairRadius(), g->crossHairLinewidth(), g->smoothCrossHair(), g->boxedCrossHair());
				break;

			case Graph3D::Cones :
				setConesOptions(g->coneRadius(), g->coneQuality());
				break;
		}
	}
	setStyle(g->coordStyle(), g->floorStyle(), g->plotStyle(), pt);
	setGrid(g->grids());
	setTitle(g->plotTitle(),g->titleColor(),g->titleFont());
	setTransparency(g->transparency());
	if (!g->colorMap().isEmpty())
		setDataColorMap(g->colorMap());
	else
		setDataColors(g->minDataColor(),g->maxDataColor());
	
	setColors(g->meshColor(),g->axesColor(),g->numColor(),
				g->labelColor(), g->bgColor(),g->gridColor());
	setAxesLabels(g->axesLabels());
	setTicks(g->scaleTicks());
	setTickLengths(g->axisTickLengths());
	setOptions(g->isLegendOn(), g->resolution(),g->labelsDistance());
	setNumbersFont(g->numbersFont());
	setXAxisLabelFont(g->xAxisLabelFont());
	setYAxisLabelFont(g->yAxisLabelFont());
	setZAxisLabelFont(g->zAxisLabelFont());
	setRotation(g->xRotation(),g->yRotation(),g->zRotation());
	setZoom(g->zoom());
	setScale(g->xScale(),g->yScale(),g->zScale());
	setShift(g->xShift(),g->yShift(),g->zShift());
	setMeshLineWidth((int)g->meshLineWidth());
	setOrtho(g->isOrthogonal());
	update();
	animate(g->isAnimated());
}

Graph3D::~Graph3D()
{
	if (func)
		delete func;

	delete sp;
}
