/*********************************************************************
 * This file is part of the PRAPI library.
 *
 * Copyright (C) 2001 Jaakko Viertola
 * Copyright (C) 2002 Topi Mäenpää
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.1 $
 *********************************************************************/

#include "EdgeDetector.h"

using namespace util;
using namespace prapi::graphics;

namespace prapi { namespace texture {
		
	Matrix<double> EdgeUtils::getMagnitude(const Matrix<Point<double> >& mat)
	{
		int rows = mat.getRows();
		int columns = mat.getColumns();
		
		Matrix<double> result(rows, columns);

		// calculate the absolute value for both and then sum the values
		const Point<double>* matData = mat.getData();
		double* matRData = result.getData();
		
		for(int i=0;i<rows*columns;i++,matRData++,matData++)
			*matRData = double(sqrt(pow(double(matData->x),2)+pow(double(matData->y),2)));
		
		return result;
	}		

	Matrix<double> EdgeUtils::getAngle(const Matrix<Point<double> >& mat)
	{
		int rows = mat.getRows();
		int columns = mat.getColumns();
		
		Matrix<double> result(rows, columns);
		
		const Point<double>* matData = mat.getData();
		double* matRData = result.getData();

		for(int i=0;i<rows*columns;i++,matRData++,matData++)
			*matRData = atan2(matData->y, matData->x);
		
		return result;
	}

	Matrix<double> EdgeUtils::getGradient(const Matrix<Point<double> >& mat, Gradient gradient)
	{
		int rows = mat.getRows();
		int cols = mat.getColumns();
		const Point<double>* matData = mat.getData();
		Matrix<double> result(rows,cols);
		double* resultData = result.getData();

		if(gradient == GRADIENT_X)
			for(int i=0;i<rows*cols;i++,matData++,resultData++)
				*resultData = matData->x;
		else 
			for(int i=0;i<rows*cols;i++,matData++,resultData++)
				*resultData = matData->y;

		return result;
	}

	void EdgeUtils::setGradient(Matrix<Point<double> >& matGradient,
															const Matrix<double>& mat, Gradient gradient)
		throw (EdgeDetectorException&)
	{
		int rows = matGradient.getRows();
		int cols = matGradient.getColumns();
		if(mat.getRows() != rows || mat.getColumns() != cols)
			throw EdgeDetectorException("EdgeUtils::setGradient(Matrix<Point<double> >&, const Matrix<T>&, Gradient ): Matrix sizes differ.");
		
		Point<double>* matGradData = matGradient.getData();
		const double* matData = mat.getData();

		if(gradient == GRADIENT_X)
			for(int i=0;i<rows*cols;i++,matData++,matGradData++)
				matGradData->x = *matData;
		else 
			for(int i=0;i<rows*cols;i++,matData++,matGradData++)
				matGradData->y = *matData;
	}

	Matrix<double> EdgeThinner::suppressNonMaxima(const Matrix<Point<double> >& gradients, unsigned int radius)
	{
		if (radius == 0)
			return EdgeUtils::getGradient(gradients, GRADIENT_Y);

		int rows = gradients.getRows(), cols = gradients.getColumns();
		Matrix<double> result(rows,cols);
		Point<int> directions[8] = { Point<int>(1,0), Point<int>(1,1), Point<int>(0,1), Point<int>(-1,1),
																 Point<int>(-1,0), Point<int>(-1,-1), Point<int>(0,-1), Point<int>(1,-1) };
		for (int r=0; r<rows; r++)
			for (int c=0; c<cols; c++)
				{
					int angle = int(gradients(r,c).x / M_PI * 4);
					double magnitude = gradients(r,c).y;
					unsigned int i;
					for (i=1; i<=radius; i++)
						{
							int row = r+directions[angle].y, column = c+directions[angle].x;
							if (row < 0 || row >= rows || column < 0 || column >= cols)
								continue;
							double val = gradients(row, column).y;
							if (val > magnitude)
								break;
						}
					if (i <= radius)
						result(r,c) = 0;
					else
						result(r,c) = magnitude;
				}
		return result;
	}
}}
