/***************************************************************************
    File                 : AbstractCoordinateSystem.cpp
    Project              : LabPlot/SciDAVis
    Description          : Base class of all worksheet coordinate systems.
    --------------------------------------------------------------------
    Copyright            : (C) 2009 Tilman Benkert (thzs@gmx.net)
    Copyright            : (C) 2012-2014 Alexander Semke (alexander.semke@web.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.                                    *
 *                                                                         *
 *  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 "backend/worksheet/plots/AbstractCoordinateSystem.h"
#include "backend/worksheet/plots/AbstractPlot.h"
#include <math.h>

/**
 * \class AbstractCoordinateSystem
 * \brief Base class of all worksheet coordinate systems.
 *
 *  \ingroup backend\worksheet
 */

AbstractCoordinateSystem::AbstractCoordinateSystem(AbstractPlot* plot){
    Q_UNUSED(plot)
}

AbstractCoordinateSystem::~AbstractCoordinateSystem(){
}

/**
 * \fn QList<QPointF> AbstractCoordinateSystem::mapLogicalToScene(const QList<QPointF> &points, const MappingFlags &flags = DefaultMapping) const;
 * \brief Map a list of points in logical coordinates into graphics scene (page) coordinates.
 *
 * The list of returned points may have less points than the input list if some points have
 * unsupported coordinates or lie in excluded areas such as coordinate gaps or outside the page rectangle.
 *
 * \param points The points to map.
 * \param flags Flags to influence the mapping behavior.
 */

/**
 * \fn QList<QPointF> AbstractCoordinateSystem::mapSceneToLogical(const QList<QPointF> &points, const MappingFlags &flags = DefaultMapping) const;
 * \brief Map a list of points in scene coordinates into logical coordinates.
 *
 * The list of returned points may have less points than the input list if some point lie in
 * areas not possible to map by the coordinate system.
 *
 * \param points The points to map.
 * \param flags Flags to influence the mapping behavior.
 */

/**
 * \fn QList<QLineF> AbstractCoordinateSystem::mapLogicalToScene(const QList<QLineF> &lines, const MappingFlags &flags = DefaultMapping) const;
 * \brief Map a list of lines in logical coordinates into graphics scene (page) coordinates.
 *
 * Lines may be clipped at coordinate gaps or completely be removed if outside the supported areas.
 *
 * \param lines The lines to map.
 * \param flags Flags to influence the mapping behavior.
 */

/**
 * \brief Line clipping using the Cohen-Sutherland algorithm.
 *
 * This is a modified version of clipLine() from Qt 4.5's qpaintengine_x11.cpp.
 *
 * \param line The line to clip.
 * \param rect The rect to clip to.
 * \param clipResult Pointer to an object describing which parts where clipped (may be NULL).
 *
 * \return false if line is completely outside, otherwise true
 */

float round(float value, int precision){
	return int(value*pow(10, precision) + (value<0 ? -0.5 : 0.5))/pow(10, precision);
}

bool AbstractCoordinateSystem::clipLineToRect(QLineF *line, const QRectF &rect, LineClipResult *clipResult){
	//we usually clip on large rectangles, so we don't need high precision here -> round to one float digit
	//this prevents some subtle float rounding artefacts that lead to disappearance
	//of lines along the boundaries of the rect. (e.g. axis lines).
	qreal x1 = (int)(line->x1()*10)/10;
    qreal x2 = (int)(line->x2()*10)/10;
    qreal y1 = (int)(line->y1()*10)/10;
    qreal y2 = (int)(line->y2()*10)/10;
    qreal left;
    qreal right;
    qreal top;
    qreal bottom;
	rect.getCoords(&left, &top, &right, &bottom);

	if (clipResult)
		clipResult->reset();

    enum { Left, Right, Top, Bottom };
    // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html
    int p1 = ((x1 < left) << Left)
             | ((x1 > right) << Right)
             | ((y1 < top) << Top)
             | ((y1 > bottom) << Bottom);
    int p2 = ((x2 < left) << Left)
             | ((x2 > right) << Right)
             | ((y2 < top) << Top)
             | ((y2 > bottom) << Bottom);

    if (p1 & p2)
        // completely outside
        return false;

    if (p1 | p2) {
        qreal dx = x2 - x1;
        qreal dy = y2 - y1;

        // clip x coordinates
        if (x1 < left) {
            y1 += dy/dx * (left - x1);
            x1 = left;
			if (clipResult)
				clipResult->xClippedLeft[0] = true;
        } else if (x1 > right) {
            y1 -= dy/dx * (x1 - right);
            x1 = right;
			if (clipResult)
				clipResult->xClippedRight[0] = true;
        }
        if (x2 < left) {
            y2 += dy/dx * (left - x2);
            x2 = left;
			if (clipResult)
				clipResult->xClippedLeft[1] = true;
        } else if (x2 > right) {
            y2 -= dy/dx * (x2 - right);
            x2 = right;
			if (clipResult)
				clipResult->xClippedRight[1] = true;
        }
        p1 = ((y1 < top) << Top)
             | ((y1 > bottom) << Bottom);
        p2 = ((y2 < top) << Top)
             | ((y2 > bottom) << Bottom);
        if (p1 & p2)
            return false;
        // clip y coordinates
        if (y1 < top) {
            x1 += dx/dy * (top - y1);
            y1 = top;
			if (clipResult) {
				clipResult->xClippedRight[0] = false;
				clipResult->xClippedLeft[0] = false;
				clipResult->yClippedTop[0] = true;
			}
        } else if (y1 > bottom) {
            x1 -= dx/dy * (y1 - bottom);
            y1 = bottom;
			if (clipResult) {
				clipResult->xClippedRight[0] = false;
				clipResult->xClippedLeft[0] = false;
				clipResult->yClippedBottom[0] = true;
			}
        }
        if (y2 < top) {
            x2 += dx/dy * (top - y2);
            y2 = top;
			if (clipResult) {
				clipResult->xClippedRight[1] = false;
				clipResult->xClippedLeft[1] = false;
				clipResult->yClippedTop[1] = true;
			}
        } else if (y2 > bottom) {
            x2 -= dx/dy * (y2 - bottom);
            y2 = bottom;
			if (clipResult) {
				clipResult->xClippedRight[1] = false;
				clipResult->xClippedLeft[1] = false;
				clipResult->yClippedBottom[1] = true;
			}
        }
        *line = QLineF(QPointF(x1, y1), QPointF(x2, y2));
    }
    return true;
}

//more intelligent comparison of floats,
//taken from Knuth's "The art of computer programming"
bool AbstractCoordinateSystem::approximatelyEqual(float a, float b, float epsilon) {
	return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool AbstractCoordinateSystem::essentiallyEqual(float a, float b, float epsilon) {
	return fabs(a - b) <= ( (fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool AbstractCoordinateSystem::definitelyGreaterThan(float a, float b, float epsilon) {
	return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool AbstractCoordinateSystem::definitelyLessThan(float a, float b, float epsilon) {
	return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}
