// CForma.cpp. Implementación de la clase CForma

#include "stdafx.h"
#include <stdlib.h>
#include "CForma.h"
#include "CContorno.h"
#include <math.h>
#include "funciones.h"

#define PI 3.14159265
#define RANGO_PLANO 9

// Códigos de error:
// ----------------
//  1.- No se puede abrir el archivo.
//  2.- No se puede leer el archivo.
//  3.- No se puede escribir el archivo.
//  4.- Imagen no válida: no es una gama de grises o no es de 256 colores.
//  5.- Imagen no válida: no posee un fondo homogéneo
//  6.- Imagen no válida: Contorno vacío.
//  7.- Imagen no válida: Número total de puntos demasiado grande.
//  8.- Imagen no válida: No hay puntos en la imagen.
//  9.- Imagen no válida: Sólo un punto en la imagen.
// 10.- Insuficiente memoria.

CForma::CForma()
{
	num_puntos = 0;
	dimensx = 0;
	dimensy = 0;	
	longitud = 0;
	sigma = 0.0;
	curva.intervalos = NULL;
	curva.k = NULL;
}

CForma::~CForma()
{
	num_puntos = 0;
	dimensx = 0;
	dimensy = 0;	
	longitud = 0;
	sigma = 0.0;
}

int CForma::AbrirImagen(CString nomb, CString *dimex, CString *dimey)
{
	CFile pFichRead;
	char tmp1[20], tmp2[20];
	int res = 0;

	m_DIB.CImagen::CImagen();
	
	*dimex = "0";
	*dimey = "0";

	// Abro el archivo en cuestión
	if (pFichRead.Open(nomb,CFile::modeRead) == 0)
	{
		res = 1;
		return res; // Error: No se puede abrir este archivo
	}
	
	// Leo el fichero .bmp y lo paso a la estructura m_DIB
	if (m_DIB.LeerBmp(&pFichRead) == true)
	{
		dimensx = m_DIB.m_lpBmIH->biWidth;
		ltoa(m_DIB.m_lpBmIH->biWidth,tmp1,10);
		*dimex = (CString) tmp1;
		
		dimensy = m_DIB.m_lpBmIH->biHeight;
		ltoa(m_DIB.m_lpBmIH->biHeight,tmp2,10);
		*dimey = (CString) tmp2;	
	}
	else
	{
		*dimex = "0";
		*dimey = "0";
		res = 2; // Error: Imposible leer el archivo indicado
	}
	
	pFichRead.Close();
	return res;
}
	
int CForma::ObtenerDatos(int dimx, int dimy, float sigm, CString nombre, int margenumbral,
						 int numvecinos, int maxptos, int umbral_inf, int umbral_sup, int contraste,
						 int umbralizada, int contorno, int grafica, CString dir)
{
	RGBQUAD col, col_b, col_n;
	CContorno cnt;
	CFile pFich;
	CString nombref;
	int i, j, punto, ind, err = 0, pos;
	float *vectx, *vecty;
	unsigned char *im;

	// Guardo la dimensión de la imagen y el sigma
	dimensx = dimx;
	dimensy = dimy;
	sigma = sigm;

	// Compruebo si la imagen es una gama de grises
	if (!m_DIB.EsGamaGrises256())
		return 4; // Error: La imagen no es una gama de grises
	
	// Modifico la paleta de la imagen por la paleta de grises por defecto
	m_DIB.CrearPaletaGrises();

	// Aplicamos la técnica de aumento de contraste y guardamos la imagen en un fichero
	m_DIB.AumentoContraste();
	nombref = nombre.Left(nombre.GetLength() - 4); // menos el ".bmp"
	if (contraste == 1) // Si se ha solicitado guardar la imagen aumentada de contraste ...
	{
		pFich.Open(dir + "\\" + nombref + "v2.bmp",CFile::modeCreate|CFile::modeWrite);
		if (!m_DIB.EscribirBmp(&pFich))
			return 3; // Error: No se puede escribir en el fichero
		
		pFich.Close();
	}
	
	// Umbralizamos: ponemos solo 2 colores en la imagen (fondo negro y el resto en blanco) ...
	if (!m_DIB.Umbralizar(margenumbral))
		return 5; // Error: La imagen no tiene un fondo homogéneo

	// Si el objeto tomado debe ser el fondo, intercambiamos objeto por fondo
	if (!m_DIB.FormaValida())
	{
		// Establecemos el color blanco
		col_b.rgbBlue = 255;
		col_b.rgbGreen = 255;
		col_b.rgbRed = 255;
		col_b.rgbReserved = 0;
		// Establecemos el color negro
		col_n.rgbBlue = 0;
		col_n.rgbGreen = 0;
		col_n.rgbRed = 0;
		col_n.rgbReserved = 0;

		// Intercambiar objeto por fondo
		for (j=0;j<dimy;j++)
		{
			for (i=0;i<dimx;i++)
			{
				m_DIB.GetBmpPixel(i,j,&col,&pos);
				if (col.rgbBlue == 0)
					m_DIB.SetBmpPixel(i,j,col_b);
				else
					m_DIB.SetBmpPixel(i,j,col_n);
			}
		}
	}

	// Aplicamos un filtro que elimine aristas muy delgadas y ruido
	m_DIB.FiltroAntiRuido(numvecinos);	
	
	// Utilizamos 'im' para guardar la imagen umbralizada y filtrada
	im = (unsigned char *) malloc((size_t) (sizeof(unsigned char)*dimx*dimy));
	for (j=0;j<dimy;j++)
	{
		for (i=0;i<dimx;i++)
		{
			m_DIB.GetBmpPixel(i,j,&col,&pos);
			if (col.rgbBlue == 0)
				im[(j*dimx)+i] = 0;
			else
				im[(j*dimx)+i] = 255;
		}
	}
	// Guardo la imagen umbralizada en un fichero .bmp, si se ha solicitado.
	if (umbralizada == 1)
	{
		err = EscribirUmbralizada(dir + "\\" + nombref + "v3.bmp",im);
		if (err != 0)
			return err;
	}
	
	// Creo el contorno y lo obtengo a partir de la imagen umbralizada
	cnt.CContorno::CContorno(im,dimx,dimy,numvecinos);
	err = cnt.ObtenerContornos(im);
	free(im); // Libero la imagen 'im' porque ya no vamos a usarla más

	if (err == 1)
		return 6; // Error: Contorno vacío
	
	longitud = cnt.contorno.longitud;
	
	// Reservo memoria para el vector de las x's y el vector de las y's
	vectx = (float *) malloc((size_t) (sizeof(float) * cnt.contorno.longitud));
	vecty = (float *) malloc((size_t) (sizeof(float) * cnt.contorno.longitud));
	
	// Calculo el vector de las x's
	for (i=0;i<cnt.contorno.longitud;i++)
	{	
		vectx[i] = (float) cnt.contorno.ptos[i].x;
		vecty[i] = (float) (cnt.dimensy - cnt.contorno.ptos[i].y);
	}
	
	// Calculo la curvatura la primera vez para estudiar un punto con muy poca
	// curvatura para poder comenzar por él sin perder información
	curva.CCurvatura::CCurvatura(cnt.contorno.longitud);
	err = curva.CalcularCurvatura(vecty,vectx,cnt.contorno.longitud,sigma,maxptos,umbral_inf,umbral_sup,0);
	if (err == 1)
	{
		free(vectx);
		free(vecty);
		return 7; // Error: El nº de intervalos excede el máximo establecido.
	}
	else if (err == 2)
	{
		free(vectx);
		free(vecty);
		return 8; // Error: Cero puntos característicos.
	}

	// Calculo un punto que define un entorno de tamaño RANGO_LISO
	// en el que no existan puntos caracteristicos en la gráfica
	punto = curva.ObtenerPuntoLiso(RANGO_PLANO); // tiene q ser impar

	// Si el punto es distinto de -1, recalculamos la curvatura comenzando
	// desde un punto sin curvatura en un rango 'RANGO_LISO'. En otro caso
	// (no es posible encontrar tal rango), nos quedamos con la función de
	// curvatura hallada la primera vez y la damos por buena.
	
	if (punto != -1)
	{
		for (i=0;i<cnt.contorno.longitud;i++)
		{
			ind = (i + punto) % cnt.contorno.longitud;

			vectx[i] = (float) cnt.contorno.ptos[ind].x;
			vecty[i] = (float) (cnt.dimensy - cnt.contorno.ptos[ind].y);
		}
	
		// Obtengo la función de curvatura, ademas de todos los vértices, signos,
		// distancia entre vértices y grados de curvatura de dichos vértices.
		curva.CCurvatura::~CCurvatura();
		curva.CCurvatura::CCurvatura(cnt.contorno.longitud);
		err = curva.CalcularCurvatura(vecty,vectx,cnt.contorno.longitud,sigma,maxptos,umbral_inf,umbral_sup,punto);
		if (err == 1)
		{
			free(vectx);
			free(vecty);
			return 7; // Error: El nº de intervalos excede el máximo establecido.
		}
		else if (err == 2)
		{
			free(vectx);
			free(vecty);
			return 8; // Error: Cero puntos característicos.
		}
	}

	for (i=0;i<curva.num_intervalos;i++)
		curva.intervalos[i].pixel = cnt.contorno.ptos[curva.intervalos[i].punto];
	
	free(vectx);
	free(vecty);

	num_puntos = curva.num_intervalos;
	
	if (num_puntos == 1)
		return 9; // Error: La imagen sólo tiene un punto característico.

	if (contorno == 1)
	{
		err = EscribirContorno(dir + "\\" + nombref + "v4.bmp",&cnt.contorno,curva.intervalos,punto);
		if (err != 0)
			return err;
	}

	if (grafica == 1)
	{
		err = RepresentarGrafica(dir + "\\" + nombref + "v5.bmp");
		if (err != 0)
			return err;
	}

	return 0; // No errores
}

int CForma::RepresentarGrafica(CString nombre)
{
	CImagen m_DIB2;
	RGBQUAD color_blanco, color_negro;
	CFile pFichWrite;
	int i, j, valor, valor2, val_lin;
	double a, b;
	bool turno = true;

	// Vamos a guardar la imagen en un fichero. Las dimensiones de la imagen
	// son 'longitud' en el eje x y 201 en el eje y
	m_DIB2.CImagen::CImagen();
	if (!m_DIB2.CrearImagenMonocromo(longitud,201))
		return 10; // Error: Insuficiente memoria para crear la imagen

	// Establecemos el color blanco y negro para usarlos a continuación
	color_blanco.rgbBlue = 255;
	color_blanco.rgbGreen = 255;
	color_blanco.rgbRed = 255;
	color_blanco.rgbReserved = 0;
	color_negro.rgbBlue = 0;
	color_negro.rgbGreen = 0;
	color_negro.rgbRed = 0;
	color_negro.rgbReserved = 0;

	// Dibujamos el fondo todo negro
	for (j=0;j<201;j++)
	{
		for (i=0;i<longitud;i++)
			m_DIB2.SetBmpPixel(i,j,color_negro);
	}

	a = 100.0;
	b = 100.0 / PI;
	
	for (i=0;i<longitud;i++)
	{
		// Para pintar las lineas discontinuas que indican el umbral
		if (turno)
		{
			// Dibujamos la linea del umbral inferior (una columna, blanco; otra negro)
			val_lin = Aproximar(a + (b * curva.umbral_inf));
			m_DIB2.SetBmpPixel(i,val_lin,color_blanco);
			// Dibujamos la linea del umbral superior (una columna, blanco; otra negro)
			val_lin = Aproximar(a + (b * curva.umbral_sup));
			m_DIB2.SetBmpPixel(i,val_lin,color_blanco);
		}
		
		turno = !turno; // Cambiamos el turno

		valor = Aproximar(a + (b * curva.k[i]));
		m_DIB2.SetBmpPixel(i,valor,color_blanco);
		if (i != 0)
		{
			if (valor2 < valor)
			{
				for(j=valor2+1;j<valor;j++)
					m_DIB2.SetBmpPixel(i-1,j,color_blanco);
			}
			else
			{
				for(j=valor2-1;j>valor;j--)
					m_DIB2.SetBmpPixel(i-1,j,color_blanco);			
			}
		}
		
		valor2 = valor;
	}
	
	if (pFichWrite.Open(nombre,CFile::modeWrite | CFile::modeCreate) == 0)
		return 3; // Error: No se puede escribir el fichero

	if (!m_DIB2.EscribirBmp(&pFichWrite))
		return 3; // Error: No se puede escribir el fichero

	pFichWrite.Close();
	return 0;	
}

int CForma::EscribirUmbralizada(CString nombre,unsigned char *s)
{
	CImagen m_DIB2;
	CFile pFichWrite;
	long i,j;

	m_DIB2.CImagen::CImagen();
	if (!m_DIB2.CrearImagenMonocromo(dimensx,dimensy))
		return 10; // Error: Insuficiente memoria para crear la imagen

	if (pFichWrite.Open(nombre,CFile::modeWrite | CFile::modeCreate) == 0)
		return 3; // Error: No se puede escribir el fichero
		
	for (j=0;j<dimensy;j++)
	{
		for (i=0;i<dimensx;i++)
		{
			if (s[(j*dimensx)+i] == 0)
				m_DIB2.SetBmpPixel(i,j,m_DIB2.m_lpBmI->bmiColors[0]);
			else
				m_DIB2.SetBmpPixel(i,j,m_DIB2.m_lpBmI->bmiColors[1]);
		}
	}		
	
	if (!m_DIB2.EscribirBmp(&pFichWrite))
		return 3; // Error: No se puede escribir el fichero
		
	pFichWrite.Close();
	return 0;
}

int CForma::EscribirContorno(CString nombre, CCamino *cnt, CIntervalo *inter, int punto)
{
	CImagen m_DIB2;
	CFile pFichWrite;
	int i,j;

	m_DIB2.CImagen::CImagen();
	if (!m_DIB2.CrearImagenMonocromo(dimensx,dimensy))
		return 10; // Error: Insuficiente memoria
	
	if (pFichWrite.Open(nombre,CFile::modeWrite | CFile::modeCreate) == 0)
		return 3;  // Error: No se puede escribir el fichero

	for (j=0;j<dimensy;j++)
	{
		for (i=0;i<dimensx;i++)
			m_DIB2.SetBmpPixel(i,j,m_DIB2.m_lpBmI->bmiColors[0]);
	}

	for (i=0;i<longitud;i++)
		m_DIB2.SetBmpPixel(cnt->ptos[i].x, cnt->ptos[i].y,m_DIB2.m_lpBmI->bmiColors[1]);

	if (!m_DIB2.EscribirBmp(&pFichWrite))
		return 3; // Error: No se puede escribir el fichero

	pFichWrite.Close();
	return 0;
}

