/*
 *  
 *  $Id: wangulo.cpp $
 *  Ginkgo CADx Project
 *
 *  Copyright 2008-12 MetaEmotion S.L. All rights reserved.
 *  http://ginkgo-cadx.com
 *
 *  This file is licensed under LGPL v3 license.
 *  See License.txt for details
 *
 *
 */
//#define _GINKGO_TRACE
#include <ostream>
#include <sstream>
#include <cmath>

#include <api/globals.h>
#include <api/math/geometry3d.h>
#include <api/internationalization/internationalization.h>
#include "wangulo.h"
#include <api/ievento.h>
#include <api/icontexto.h>
#include <api/iwidgetsrenderer.h>
#include <eventos/modificacionimagen.h>
#include "main/managers/widgetsmanager.h"
#include <vtkgl.h>
#include <cairo/cairo.h>
#include "openglhelper.h"
#include <wx/xml/xml.h>


//region "Implementacion de WAnguloBuilder"

GNC::GCS::Widgets::WAnguloBuilder::WAnguloBuilder(GNC::GCS::IWidgetsManager* pManager, const GNC::GCS::TriggerButton& buttonMask) :
		GNC::GCS::Widgets::IWidgetBuilder(pManager, buttonMask),
		m_Estado(WBS_Ninguno),
		m_pAnguloTemp(NULL),
		m_NodoActual(0),
		m_Modo(MA_Normal)
{
}

GNC::GCS::Widgets::WAnguloBuilder::~WAnguloBuilder()
{
	if (m_pAnguloTemp != NULL) {
		delete m_pAnguloTemp;
		m_pAnguloTemp = NULL;
		m_NodoActual = 0;
	}
}

void GNC::GCS::Widgets::WAnguloBuilder::SetModo(ModoAngulo modo)
{
	if (m_pAnguloTemp == NULL) {
		delete m_pAnguloTemp;
		m_pAnguloTemp = NULL;
		m_NodoActual = 0;
	}
	m_Modo = modo;
}

void GNC::GCS::Widgets::WAnguloBuilder::OnMouseEvents(GNC::GCS::Events::EventoRaton& evento)
{
	if (!m_pManager) {
		return;
	}
	GTRACE("GNC::GCS::Widgets::WAnguloBuilder::OnMouseEvents(wxMouseEvent&)");
	if (m_Estado == WBS_Creando && evento.ButtonUp(m_ButtonMask)) {
		if (m_pAnguloTemp != NULL) {
			if (m_Modo == MA_Normal) { // Angulo normal
				if (m_NodoActual == 1) {
					m_pAnguloTemp->m_Nodos[1] = evento.iP;
					if ( (m_pAnguloTemp->m_Nodos[1] - m_pAnguloTemp->m_Nodos[0]).Norma2Cuadrado() < GNC::GCS::Vector::EpsilonDeReales() ) {
						m_NodoActual = 0;
						delete m_pAnguloTemp;
						m_Estado = WBS_Ninguno;
						m_pAnguloTemp = NULL;
					}
					else {
						m_pAnguloTemp->m_Nodos[2] = evento.iP;
						m_NodoActual = 2;
						m_pAnguloTemp->Recalcular(evento.c->GetRenderer());
					}
				} else {
					m_pAnguloTemp->m_Nodos[2] = evento.iP;
					if ( (m_pAnguloTemp->m_Nodos[2] - m_pAnguloTemp->m_Nodos[1]).Norma2Cuadrado() < GNC::GCS::Vector::EpsilonDeReales()) {
						m_NodoActual = 0;
						delete m_pAnguloTemp;
						m_Estado = WBS_Ninguno;
						m_pAnguloTemp = NULL;
					}
					else {
						m_pAnguloTemp->Recalcular(evento.c->GetRenderer());
						m_pManager->InsertarWidget(m_pAnguloTemp);
						LanzarEventoCreacion(m_pAnguloTemp);
						m_pAnguloTemp = NULL;
						m_Estado = WBS_Ninguno;
						m_NodoActual = 0;
					}
				}
			} else { // Angulo de Cobb
				m_pAnguloTemp->m_Nodos[m_NodoActual] = evento.iP;
				m_pAnguloTemp->Recalcular(evento.c->GetRenderer());
				if (m_NodoActual == 1) {
					if ( (m_pAnguloTemp->m_Nodos[1] - m_pAnguloTemp->m_Nodos[0]).Norma2Cuadrado() < GNC::GCS::Vector::EpsilonDeReales() ) {
						m_NodoActual = 0;
						delete m_pAnguloTemp;
						m_Estado = WBS_Ninguno;
						m_pAnguloTemp = NULL;
					}
					else {
						m_NodoActual = 2;
					}
				} else {
					if ( (m_pAnguloTemp->m_Nodos[3] - m_pAnguloTemp->m_Nodos[2]).Norma2Cuadrado() < GNC::GCS::Vector::EpsilonDeReales()) {
						m_NodoActual = 0;
						delete m_pAnguloTemp;
						m_Estado = WBS_Ninguno;
						m_pAnguloTemp = NULL;
					}
					else {
						m_pManager->InsertarWidget(m_pAnguloTemp);
						m_pManager->LanzarEventoCreacion(m_pAnguloTemp);
						m_pAnguloTemp = NULL;
						m_Estado = WBS_Ninguno;
						m_NodoActual = 0;
					}
				}
			}
			m_pManager->Modificado();
			ConsumirEvento();
		}
	} else if (evento.ButtonDown(m_ButtonMask)) {
		if (m_Estado == WBS_Ninguno) {
			m_Estado = WBS_Creando;
			if (m_pAnguloTemp == NULL) {
				if (m_pAnguloTemp != NULL) {
					delete m_pAnguloTemp;
				}
				m_pAnguloTemp = new GNC::GCS::Widgets::WAngulo(m_pManager, evento.c->GetRenderer()->GetVID(), m_Modo,"Ángulo");
				m_pAnguloTemp->m_Nodos[0] = m_pAnguloTemp->m_Nodos[1] = m_pAnguloTemp->m_Nodos[2] = evento.iP;
				m_pAnguloTemp->Recalcular(evento.c->GetRenderer());
				m_NodoActual = 1;
				ConsumirEvento();
			}
		} else {
			if (m_pAnguloTemp != NULL) {
				if (m_Modo == MA_Cobb) {  // Angulo de Cobb
					if (m_NodoActual == 2) {
						m_pAnguloTemp->m_Nodos[2] = evento.iP;
						m_NodoActual = 3;
						m_pAnguloTemp->m_Nodos[3] = evento.iP;
					}
				}
				ConsumirEvento();
			} else {
				m_Estado = WBS_Ninguno;
			}
		}
		m_pManager->Modificado();
	} else if (evento.Dragging() && m_Estado == WBS_Creando) {
		if (m_pAnguloTemp != NULL) {
			m_pAnguloTemp->m_Nodos[m_NodoActual] = evento.iP;
			m_pAnguloTemp->Recalcular(evento.c->GetRenderer());
			m_pManager->Modificado();
			ConsumirEvento();
		}
	} else if (evento.Moving() && m_Estado == WBS_Creando) {
		if (m_pAnguloTemp != NULL) {
			m_pAnguloTemp->m_Nodos[m_NodoActual] = evento.iP;
			m_pAnguloTemp->Recalcular(evento.c->GetRenderer());
			m_pManager->Modificado();
			ConsumirEvento();
		}
	}
}

void GNC::GCS::Widgets::WAnguloBuilder::OnKeyEvents(GNC::GCS::Events::EventoTeclado&)
{
	GTRACE("GNC::GCS::Widgets::WAnguloBuilder::OnKeyEvents(wxKeyEvent&)");
}

void GNC::GCS::Widgets::WAnguloBuilder::Render(GNC::GCS::Contexto3D* c)
{

	if (m_pAnguloTemp == NULL) {
		return;
	}
	if (c->GetRenderer()->GetVID() == m_pAnguloTemp->GetVID()) {

		glLineWidth(WIDGET_GROSOR_LINEA);

		if (m_Modo == MA_Normal) {
			if (m_NodoActual < 2) {
				glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
				glBegin(GL_LINE_STRIP);
				glVertex2d(m_pAnguloTemp->m_Nodos[0].x, m_pAnguloTemp->m_Nodos[0].y);
				glVertex2d(m_pAnguloTemp->m_Nodos[1].x, m_pAnguloTemp->m_Nodos[1].y);
				glEnd();
			} else {
				m_pAnguloTemp->Render(c);
			}

		} else if (m_Modo == MA_Cobb) {
			if (m_NodoActual < 3) {
				glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
				glBegin(GL_LINES);
				glVertex2d(m_pAnguloTemp->m_Nodos[0].x, m_pAnguloTemp->m_Nodos[0].y);
				glVertex2d(m_pAnguloTemp->m_Nodos[1].x, m_pAnguloTemp->m_Nodos[1].y);
				glEnd();
			} else {
				m_pAnguloTemp->Render(c);
			}
		}
	}

}

GNC::GCS::Widgets::TipoCursor GNC::GCS::Widgets::WAnguloBuilder::GetCursor()
{
	if (m_Modo == MA_Normal) {
		return GNC::GCS::Widgets::CUR_CREAR_ANGULO_NORMAL;
	} else {
		return GNC::GCS::Widgets::CUR_CREAR_ANGULO_COBB;
	}
}

//endregion


//region "Constructor y destructor"

GNC::GCS::Widgets::WAngulo::WAngulo(IWidgetsManager* pManager, long vid, ModoAngulo modo, const char* nombre) : GNC::GCS::Widgets::IWidget(pManager, vid, nombre), GNC::GCS::Widgets::IWidgetSerializable()
{
	m_AnchoMaximo = 150.0f;
	m_Modo = modo;

	m_MouseDown = false;
	m_Oculto = false;
	m_ReservaRecursos = true;
	Recalcular(NULL);

	GNC::GCS::Events::EventoModificacionImagen evt1(m_pManager->GetVista());
}

GNC::GCS::Widgets::WAngulo::~WAngulo()
{
	LanzarEventoDestruccion();
}

//endregion

//region "Serializado y desserializado"
GNC::GCS::Widgets::WAngulo::WAngulo(IWidgetsManager* pManager, long vid, wxXmlNode* nodo):GNC::GCS::Widgets::IWidget(pManager, vid, "Ángulo"), GNC::GCS::Widgets::IWidgetSerializable(nodo)
{
	m_AnchoMaximo = 150.0f;
	if(nodo->GetName() != wxT("angle_widget")){
		std::cerr<< "Ha ocurrido un error al desserializar el widget regla"<<std::endl;
	}

	if(nodo->GetPropVal(wxT("mode"),wxT("normal")) == wxT("normal")){
		m_Modo = MA_Normal;
	} else {
		m_Modo = MA_Cobb;
	}

	wxXmlNode *child = nodo->GetChildren();
	while (child) {
		if(child->GetName() == wxT("node0")){
			m_Nodos[0] = GNC::GCS::Nodo::Deserializar(child);
		} else if(child->GetName() == wxT("node1")){
			m_Nodos[1] = GNC::GCS::Nodo::Deserializar(child);
		} else if(child->GetName() == wxT("node2")){
			m_Nodos[2] = GNC::GCS::Nodo::Deserializar(child);
		} else if(child->GetName() == wxT("node3")){
			m_Nodos[3] = GNC::GCS::Nodo::Deserializar(child);
		}
		child = child->GetNext();
	}

	m_MouseDown = false;
	m_ReservaRecursos = true;

	Recalcular(NULL);

	GNC::GCS::Events::EventoModificacionImagen evt1(m_pManager->GetVista());

}

wxXmlNode* GNC::GCS::Widgets::WAngulo::Serializar(const std::string& nombreMedico)
{
	wxXmlNode* resultado = new wxXmlNode(NULL,wxXML_ELEMENT_NODE,wxT("angle_widget"));
	if(m_Modo == MA_Normal){
		resultado->AddProperty(wxT("mode"),wxT("normal"));
	} else {
		resultado->AddProperty(wxT("mode"),wxT("cobb"));
	}
	//almacenamos los dos nodos
	wxXmlNode* nodo = m_Nodos[0].Serializar();
	nodo->SetName(wxT("node0"));
	resultado->AddChild(nodo);

	nodo = m_Nodos[1].Serializar();
	nodo->SetName(wxT("node1"));
	resultado->AddChild(nodo);

	nodo = m_Nodos[2].Serializar();
	nodo->SetName(wxT("node2"));
	resultado->AddChild(nodo);

	nodo = m_Nodos[3].Serializar();
	nodo->SetName(wxT("node3"));
	resultado->AddChild(nodo);

	//metadatos
	SerializarMetadatos(resultado,nombreMedico);
	return resultado;
}
//endregion

void GNC::GCS::Widgets::WAngulo::OnMouseEvents(GNC::GCS::Events::EventoRaton& evento)
{
	if (EstaOculto()) {
		return;
	}
	//------------------------------------------------------
	// Leaving
	if (evento.Leaving() ) {
		Iluminar(false);
	}
	//------------------------------------------------------
	// Entering
	else if (evento.Entering() ) {
		if (m_MouseDown) {
			Iluminar(true);
		}
	}
	//------------------------------------------------------
	// Dragging
	else if (evento.Dragging() && m_MouseDown) {

		Vector delta = evento.iP - m_PosCursor;
		m_PosCursor = evento.iP;

		if (EstaSeleccionado())  {
			m_Nodos[0] += delta;
			m_Nodos[1] += delta;
			m_Nodos[2] += delta;
			if (m_Modo == MA_Cobb) {
				m_Nodos[3] += delta;
			}
		}
		else if (m_Nodos[0].EstaIluminado()) {
			m_Nodos[0] += delta;
		}
		else if (m_Nodos[1].EstaIluminado()) {
			m_Nodos[1] += delta;
		}
		else if (m_Nodos[2].EstaIluminado()) {
			m_Nodos[2] += delta;
		}
		else if (m_Modo == MA_Cobb && m_Nodos[3].EstaIluminado()) {
			m_Nodos[3] += delta;
		}

		Recalcular(evento.c->GetRenderer());
		LanzarEventoModificacion();
		ConsumirEvento();

		return;
	}
	//------------------------------------------------------
	// LeftDown
	else if (evento.LeftDown()) {

		if (EventoConsumido() && !evento.m_controlDown) {
			SeleccionarNodo(m_Nodos[0], false);
			SeleccionarNodo(m_Nodos[1], false);
			SeleccionarNodo(m_Nodos[2], false);
			SeleccionarNodo(m_Nodos[3], false);
			Seleccionar(false);
			return;
		}

		bool dentro = false;

		m_PosCursor = evento.iP;

		const GNC::GCS::Vector& factor = evento.c->RelacionImagenPantalla();

		if (!dentro && m_Nodos[0].Hits(evento.iP, factor) ) {
			SeleccionarNodo(m_Nodos[0], true);
			Seleccionar(false);
			dentro = true;
		}
		else {
			SeleccionarNodo(m_Nodos[0], false);
		}

		if (!dentro && m_Nodos[1].Hits(evento.iP, factor) ) {
			SeleccionarNodo(m_Nodos[1], true);
			Seleccionar(false);
			dentro = true;
		}
		else {
			SeleccionarNodo(m_Nodos[1], false);
		}

		if (!dentro && m_Nodos[2].Hits(evento.iP, factor) ) {
			SeleccionarNodo(m_Nodos[2], true);
			Seleccionar(false);
			dentro = true;
		}
		else {
			SeleccionarNodo(m_Nodos[2], false);
		}

		if (m_Modo == MA_Cobb && !dentro &&  m_Nodos[3].Hits(evento.iP, factor) ) {
			SeleccionarNodo(m_Nodos[3], true);
			Seleccionar(false);
			dentro = true;
		}
		else {
			SeleccionarNodo(m_Nodos[3], false);
		}

		if (!dentro) {
			if (HitTest(evento.iP.x, evento.iP.y, factor) ) {
				if (evento.m_controlDown) { // Inversion de seleccion
					InvertirSeleccion();
				}
				else{
					Seleccionar(true);
				}
				dentro = true;
			}
			else {
				if (!evento.m_controlDown) {
					Seleccionar(false);
				}
				else {
					ConsumirEvento();
					if (EstaSeleccionado()) {
						m_MouseDown = true;
					}
				}
			}
		}

		if (dentro) {
			m_MouseDown = true;
			ConsumirEvento();
		}
		return;
	}
	//------------------------------------------------------
	// LeftUP
	else if (evento.LeftUp()) {
		if (m_MouseDown) {
			ActualizarTimestampModificacion();
			m_MouseDown = false;
			ConsumirEvento();
		}
	}
	//------------------------------------------------------
	// Moving
	else if (evento.Moving()) {
		if (EventoConsumido()) {
			IluminarNodo(m_Nodos[0], false);
			IluminarNodo(m_Nodos[1], false);
			IluminarNodo(m_Nodos[2], false);
			IluminarNodo(m_Nodos[3], false);
			Iluminar(false);
			return;
		}

		bool dentro = false;

		m_PosCursor = evento.iP;

		const GNC::GCS::Vector& factor = evento.c->RelacionImagenPantalla();

		if (!dentro && m_Nodos[0].Hits(evento.iP, factor) ) {
			IluminarNodo(m_Nodos[0], true);
			Iluminar(false);
			dentro = true;
		}
		else {
			IluminarNodo(m_Nodos[0], false);
		}

		if (!dentro && m_Nodos[1].Hits(evento.iP, factor) ) {
			IluminarNodo(m_Nodos[1], true);
			Iluminar(false);
			dentro = true;
		}
		else {
			IluminarNodo(m_Nodos[1], false);
		}

		if (!dentro && m_Nodos[2].Hits(evento.iP, factor) ) {
			IluminarNodo(m_Nodos[2], true);
			Iluminar(false);
			dentro = true;
		}
		else {
			IluminarNodo(m_Nodos[2], false);
		}

		if (m_Modo == MA_Cobb && !dentro &&  m_Nodos[3].Hits(evento.iP, factor) ) {
			IluminarNodo(m_Nodos[3], true);
			Iluminar(false);
			dentro = true;
		}
		else {
			IluminarNodo(m_Nodos[3], false);
		}

		if (!dentro) {
			if (HitTest(evento.iP.x, evento.iP.y, factor) ) {
				Iluminar(true);
				dentro = true;
			}
			else {
				Iluminar(false);
			}
		}

		if (dentro) {
			ConsumirEvento();
		}
		return;
	}
}

void GNC::GCS::Widgets::WAngulo::OnKeyEvents(GNC::GCS::Events::EventoTeclado& evento)
{
	if (evento.GetKeyCode() == WXK_ESCAPE) {
		Seleccionar(false);
	}
}

bool GNC::GCS::Widgets::WAngulo::HitTest(float x, float y, const GNC::GCS::Vector& rel)
{
	bool hits = false;

	GNC::GCS::Vector dpos(x,y);
	const GNC::GCS::Vector::TReal umbralCuadrado = (GNC::GCS::Vector::TReal)m_Nodos[0].m_Size * (GNC::GCS::Vector::TReal)m_Nodos[0].m_Size;

	if (m_Modo == MA_Normal) {

		if ( m_Nodos[0].DistanciaEuclideaCuadrado(m_Nodos[1]) / 4.0f > dpos.DistanciaEuclideaCuadrado(m_Nodos[0].PuntoMedio(m_Nodos[1]))) { // Hit Test Recta 1

			GNC::GCS::Vector vdir = m_Nodos[1] - m_Nodos[0];		
			GNC::GCS::Vector orto = (vdir / rel).VectorOrtogonal() * rel;

			GNC::GCS::Vector inter = GNC::GCS::Vector::InterseccionEntreRectas(dpos, dpos + orto, m_Nodos[0], m_Nodos[1]);
			GNC::GCS::Vector diff = (inter - dpos).ValorAbsoluto();
			if ((diff / rel).Norma2Cuadrado() < umbralCuadrado) {
				hits = true;
			}
		}
		if (!hits) { // Hit Test Recta 2
			if ( m_Nodos[1].DistanciaEuclideaCuadrado(m_Nodos[2]) / 4.0f > dpos.DistanciaEuclideaCuadrado(m_Nodos[1].PuntoMedio(m_Nodos[2]))) { // Recta 1

				GNC::GCS::Vector vdir = m_Nodos[2] - m_Nodos[1];
				GNC::GCS::Vector orto = (vdir / rel).VectorOrtogonal() * rel;

				GNC::GCS::Vector inter = GNC::GCS::Vector::InterseccionEntreRectas(dpos, dpos + orto, m_Nodos[1], m_Nodos[2]);
				GNC::GCS::Vector diff = (inter - dpos).ValorAbsoluto();
				if ((diff / rel).Norma2Cuadrado() < umbralCuadrado) {
					hits = true;
				}
			}
		}
	}
	else {
		if ( m_Nodos[0].DistanciaEuclideaCuadrado(m_Nodos[1]) / 4.0f > dpos.DistanciaEuclideaCuadrado(m_Nodos[0].PuntoMedio(m_Nodos[1]))) { // Hit Test Recta 1

			GNC::GCS::Vector vdir = m_Nodos[1] - m_Nodos[0];
			GNC::GCS::Vector orto = (vdir / rel).VectorOrtogonal() * rel;

			GNC::GCS::Vector inter = GNC::GCS::Vector::InterseccionEntreRectas(dpos, dpos + orto, m_Nodos[0], m_Nodos[1]);
			GNC::GCS::Vector diff = (inter - dpos).ValorAbsoluto();
			if ((diff / rel).Norma2Cuadrado() < umbralCuadrado) {
				hits = true;
			}
		}
		if (!hits) { // Hit Test Recta 2
			if ( m_Nodos[2].DistanciaEuclideaCuadrado(m_Nodos[3]) / 4.0f > dpos.DistanciaEuclideaCuadrado(m_Nodos[2].PuntoMedio(m_Nodos[3]))) { // Recta 1

				GNC::GCS::Vector vdir = m_Nodos[3] - m_Nodos[2];
				GNC::GCS::Vector orto = (vdir / rel).VectorOrtogonal() * rel;

				GNC::GCS::Vector inter = GNC::GCS::Vector::InterseccionEntreRectas(dpos, dpos + orto, m_Nodos[2], m_Nodos[3]);
				GNC::GCS::Vector diff = (inter - dpos).ValorAbsoluto();
				if ((diff / rel).Norma2Cuadrado() < umbralCuadrado) {
					hits = true;
				}
			}
		}
	}
	
	return hits;
}

bool GNC::GCS::Widgets::WAngulo::HitTest(GNC::GCS::Vector* vertices, int numVertices)
{
	if (m_Modo == MA_Normal) {
		return m_Nodos[0].DentroDePoligono2(vertices, numVertices) && m_Nodos[1].DentroDePoligono2(vertices, numVertices) && m_Nodos[2].DentroDePoligono2(vertices, numVertices);

	}
	else {
		return m_Nodos[0].DentroDePoligono2(vertices, numVertices) && m_Nodos[1].DentroDePoligono2(vertices, numVertices) && m_Nodos[2].DentroDePoligono2(vertices, numVertices) && m_Nodos[3].DentroDePoligono2(vertices, numVertices);
	}
}

void GNC::GCS::Widgets::WAngulo::Render(GNC::GCS::Contexto3D* c)
{
	if (m_Oculto) {
		return;
	}

	if (m_Modo == MA_Normal)
		this->RenderNormal(c);
	else if (m_Modo == MA_Cobb)
		this->RenderCobb(c);
}

void GNC::GCS::Widgets::WAngulo::RenderNormal(GNC::GCS::Contexto3D* c)
{
	GNC::GCS::Vector escala = c->RelacionImagenPantalla();

	TVector v1 = m_Nodos[0] - m_Nodos[1];
	TVector v2 = m_Nodos[2] - m_Nodos[1];

	//norma of unscaled vector
	float n1 = (v1/escala).Norma2();
	float n2 = (v2/escala).Norma2();

	if (n1 == 0.0f || n2 == 0.0f) {
		return;
	}

	// Sombra
	float fsombrax, fsombray;
	if (m_Seleccionado && m_MouseDown)
	{
		fsombrax = WIDGET_OFFSET_X_SOMBRA_SELECCIONADO * escala.x;
		fsombray = WIDGET_OFFSET_Y_SOMBRA_SELECCIONADO * escala.y;
	}
	else
	{
		fsombrax = WIDGET_OFFSET_X_SOMBRA * escala.x;
		fsombray = WIDGET_OFFSET_Y_SOMBRA * escala.y;
	}

	glLineWidth(WIDGET_GROSOR_LINEA);

	// Sombra cuerpo
	wColorSombra.Aplicar();
	glBegin(GL_LINE_STRIP);
		glVertex2d(m_Nodos[0].x + fsombrax, m_Nodos[0].y - fsombray);
		glVertex2d(m_Nodos[1].x + fsombrax, m_Nodos[1].y - fsombray);
		glVertex2d(m_Nodos[2].x + fsombrax, m_Nodos[2].y - fsombray);
	glEnd();

	// Sombra puntas de flecha
	float anguloPunta = M_PI_4 / 2.0f;
	
	TVector puntaA0 = m_Nodos[0] + ((v1/escala).RotacionSobreZ(M_PI+anguloPunta).Normalizar() * escala * 15.0f);
	TVector puntaA1 = m_Nodos[0] + ((v1/escala).RotacionSobreZ(M_PI-anguloPunta).Normalizar() * escala * 15.0f);

	TVector puntaB0 = m_Nodos[2] + ((v2/escala).RotacionSobreZ(M_PI+anguloPunta).Normalizar() * escala * 15.0f);
	TVector puntaB1 = m_Nodos[2] + ((v2/escala).RotacionSobreZ(M_PI-anguloPunta).Normalizar() * escala * 15.0f);

	glBegin(GL_LINE_STRIP);
		glVertex2d(puntaA0.x + fsombrax, puntaA0.y - fsombray);
		glVertex2d(m_Nodos[0].x + fsombrax, m_Nodos[0].y - fsombray);
		glVertex2d(puntaA1.x + fsombrax, puntaA1.y - fsombray);
	glEnd();

	glBegin(GL_LINE_STRIP);
		glVertex2d(puntaB0.x + fsombrax, puntaB0.y - fsombray);
		glVertex2d(m_Nodos[2].x + fsombrax, m_Nodos[2].y - fsombray);
		glVertex2d(puntaB1.x + fsombrax, puntaB1.y - fsombray);
	glEnd();

	// sombra punto origen
	glBegin(GL_POINTS);
		glVertex2d(m_Nodos[1].x + fsombrax, m_Nodos[1].y - fsombray);
	glEnd();

	TMatriz mRot;
	mRot = TMatriz::MatrizRotacion(m_Angulo/60.0f);

	//we have to un scale and scale to print a circle instead an ellipse
	TVector va = ((v1/escala).Normalizado() * std::min(n1, n2))*escala * 0.3f;
	TVector vb = ((v2/escala).Normalizado() * std::min(n1, n2))*escala * 0.3f;

	double normaVa = (va/escala).Norma2();

	TVector puntoTexto;

	// Arco
	wColorAngulo.Aplicar();
	glBegin(GL_LINE_STRIP);
	for (int i = 0; i < 60; ++i) {
		if ( i == 30) {
			puntoTexto = m_Nodos[1] + va;
		}
		glVertex2d(m_Nodos[1].x + va.x, m_Nodos[1].y + va.y);
		va /= escala;
		//all va vector has the same norma unscaled
		va = mRot.ProductoMatricial(va).Normalizar() * normaVa;
		va *= escala;
	}
	glVertex2d(m_Nodos[1].x + vb.x, m_Nodos[1].y + vb.y);
	glEnd();

	// Arco interior
	va = ((v1/escala).Normalizado() * std::min(n1, n2))*escala * 0.3f;
	wColorAnguloInterior.Aplicar();

	glBegin(GL_TRIANGLE_FAN);
	glVertex2d(m_Nodos[1].x, m_Nodos[1].y);
	for (int i = 0; i < 60; ++i) {
		glVertex2d(m_Nodos[1].x + va.x, m_Nodos[1].y + va.y);
		va /= escala;
		va = mRot.ProductoMatricial(va).Normalizar() * normaVa;
		va *= escala;
	}
	glVertex2d(m_Nodos[1].x + vb.x, m_Nodos[1].y + vb.y);
	glEnd();

	// Cuerpo
	wAplicarColor(m_Iluminado, m_MouseDown, m_Seleccionado);
	glBegin(GL_LINE_STRIP);
	glVertex2d(m_Nodos[0].x, m_Nodos[0].y);
	glVertex2d(m_Nodos[1].x, m_Nodos[1].y);
	glVertex2d(m_Nodos[2].x, m_Nodos[2].y);
	glEnd();


	// Dibujo highlights puntos activos
	for (int i = 0; i < 4; ++i)
	{
		if (m_Nodos[i].m_Iluminado) {
			wDibujarElipseDegradado(m_Nodos[i], wRadioPuntoIluminado * escala, wColorPuntoIluminadoInterior, wColorPuntoIluminadoExterior);
		}
	}

	// Nodos
	if (m_Seleccionado && m_MouseDown)
		glPointSize(WIDGET_SIZE_PUNTO * WIDGET_INCREMENTO_DETALLE_SELECCIONADO);
	else
		glPointSize(WIDGET_SIZE_PUNTO);


	// Puntas de flecha
	wAplicarColor(m_Iluminado || m_Nodos[0].m_Iluminado, m_MouseDown, m_Seleccionado);
	glBegin(GL_LINE_STRIP);
		glVertex2d(puntaA0.x, puntaA0.y);
		glVertex2d(m_Nodos[0].x, m_Nodos[0].y);
		glVertex2d(puntaA1.x, puntaA1.y);
	glEnd();

	wAplicarColor(m_Iluminado || m_Nodos[2].m_Iluminado, m_MouseDown, m_Seleccionado);
	glBegin(GL_LINE_STRIP);
		glVertex2d(puntaB0.x, puntaB0.y);
		glVertex2d(m_Nodos[2].x, m_Nodos[2].y);
		glVertex2d(puntaB1.x, puntaB1.y);
	glEnd();


	wAplicarColor(m_Iluminado || m_Nodos[1].m_Iluminado, m_MouseDown, m_Seleccionado);
	glBegin(GL_POINTS);
		glVertex2d(m_Nodos[1].x, m_Nodos[1].y);
	glEnd();

	TexturaAngulo* ta = GetTextura(c->GetRenderer());
	if(ta->texto != m_Texto) {
		Recalcular(c->GetRenderer());
	}
	GNC::GCS::TexturaCairo* tc = ta->m_textura;
	// Dibujado de la medida.
	if (!tc->contextoCreado) {
		//std::cerr << "Error: Contexto no creado" << std::endl;
		return;
	}
	if (!tc->texturaCargada) {
		tc->Cargar();
	}
	if (tc->texturaModificada) {
		tc->Actualizar();
	}

	// Dibujado del texto.

	// Compute display pos
	GNC::GCS::Vector3D planePos(puntoTexto.x, puntoTexto.y, (double)c->pRenderer->m_pImageViewer->GetTindex());
	GNC::GCS::Vector3D worldPos;
	GNC::GCS::Vector3D displayPos;

	c->pRenderer->m_pImageViewer->UnProyect2D(planePos.v, worldPos.v);
	c->pRenderer->WorldToDisplay(worldPos.v, displayPos.v);

	// Alineamiento

	GNC::GCS::Vector alineacion = GNC::GCS::Vector(0.0, (tc->alto / 2.0));
	wDibujarTexturaTexto2D(c, *tc, (GNC::GCS::Vector(displayPos.x, displayPos.y) + alineacion ).Redondear());
}

void GNC::GCS::Widgets::WAngulo::RenderCobb(GNC::GCS::Contexto3D* c)
{
	TVector v1 = m_Nodos[1] - m_Nodos[0];
	TVector v2 = m_Nodos[3] - m_Nodos[2];

	GNC::GCS::Vector escala = c->RelacionImagenPantalla();

	float n1 = (v1/escala).Norma2();
	float n2 = (v2/escala).Norma2();

	if (n1 == 0.0f || n2 == 0.0f) {
		return;
	}


	// Sombra
	float fsombrax, fsombray;
	if (m_Seleccionado && m_MouseDown)
	{
		fsombrax = WIDGET_OFFSET_X_SOMBRA_SELECCIONADO * escala.x;
		fsombray = WIDGET_OFFSET_Y_SOMBRA_SELECCIONADO * escala.y;
	}
	else
	{
		fsombrax = WIDGET_OFFSET_X_SOMBRA * escala.x;
		fsombray = WIDGET_OFFSET_Y_SOMBRA * escala.y;
	}

	glLineWidth(WIDGET_GROSOR_LINEA);

	// Sombra
	wColorSombra.Aplicar();

	// Sombra cuerpo
	glBegin(GL_LINES);
		glVertex2d(m_Nodos[0].x + fsombrax, m_Nodos[0].y - fsombray);
		glVertex2d(m_Nodos[1].x + fsombrax, m_Nodos[1].y - fsombray);
		glVertex2d(m_Nodos[2].x + fsombrax, m_Nodos[2].y - fsombray);
		glVertex2d(m_Nodos[3].x + fsombrax, m_Nodos[3].y - fsombray);
	glEnd();

	// Sombra puntos origen 0 y 2
	wColorSombra.Aplicar();
	glPointSize(WIDGET_SIZE_PUNTO);
	glBegin(GL_POINTS);
		glVertex2d(m_Nodos[0].x + fsombrax, m_Nodos[0].y - fsombray);
		glVertex2d(m_Nodos[2].x + fsombrax, m_Nodos[2].y - fsombray);
	glEnd();


	// Sombra puntas de flecha
	float anguloPunta = M_PI_4 / 2.0f;
	
	TVector puntaA0 = m_Nodos[1] + ((v1/escala).RotacionSobreZ(M_PI+anguloPunta).Normalizar() * escala * 15.0f);
	TVector puntaA1 = m_Nodos[1] + ((v1/escala).RotacionSobreZ(M_PI-anguloPunta).Normalizar() * escala * 15.0f);

	TVector puntaB0 = m_Nodos[3] + ((v2/escala).RotacionSobreZ(M_PI+anguloPunta).Normalizar() * escala * 15.0f);
	TVector puntaB1 = m_Nodos[3] + ((v2/escala).RotacionSobreZ(M_PI-anguloPunta).Normalizar() * escala * 15.0f);

	glBegin(GL_LINE_STRIP);
		glVertex2d(puntaA0.x + fsombrax, puntaA0.y - fsombray);
		glVertex2d(m_Nodos[1].x + fsombrax, m_Nodos[1].y - fsombray);
		glVertex2d(puntaA1.x + fsombrax, puntaA1.y - fsombray);
	glEnd();

	glBegin(GL_LINE_STRIP);
		glVertex2d(puntaB0.x + fsombrax, puntaB0.y - fsombray);
		glVertex2d(m_Nodos[3].x + fsombrax, m_Nodos[3].y - fsombray);
		glVertex2d(puntaB1.x + fsombrax, puntaB1.y - fsombray);
	glEnd();

	TVector puntoTexto;

	TVector interseccion = TVector::InterseccionEntreRectas(m_Nodos[0], m_Nodos[1], m_Nodos[2], m_Nodos[3]);
	// Arco
	{
		TVector p0 = m_Nodos[0].PuntoMedio(m_Nodos[1]);
		TVector p1 = m_Nodos[3].PuntoMedio(m_Nodos[2]);
		puntoTexto = p0.PuntoMedio(p1);

		// Sombra de arco
		glEnable(GL_LINE_STIPPLE);
		glLineStipple(3, 0xAAAA);
		wColorSombra.Aplicar();
		glBegin(GL_LINES);
		glVertex2d(p0.x, p0.y);
		glVertex2d(p1.x, p1.y);
		glEnd();
		glDisable(GL_LINE_STIPPLE);

		glEnable(GL_LINE_STIPPLE);
		glLineStipple(3, 0xAAAA);
		wColorAngulo.Aplicar();
		glBegin(GL_LINES);
		glVertex2d(p0.x, p0.y);
		glVertex2d(p1.x, p1.y);
		glEnd();
		glDisable(GL_LINE_STIPPLE);
	}

	//arco
	TMatriz mRot;

	mRot = TMatriz::MatrizRotacion(m_Angulo/60.0f);

	
	float normaMenor = std::min(n1, n2);
	TVector va;

	if (normaMenor == n1) {
		va = m_Nodos[0].PuntoMedio(m_Nodos[1]);
	}
	else {
		va = m_Nodos[3].PuntoMedio(m_Nodos[2]);
	}
	va -= interseccion;

	// Sombra de arco
	wColorSombra.Aplicar();

	GNC::GCS::Vector tva = va;
	double normaVa = (va/escala).Norma2();

	glBegin(GL_LINE_STRIP);
	for (int i = 0; i < 60; ++i) {
		glVertex2d(interseccion.x + tva.x + fsombrax, interseccion.y + tva.y + fsombray);
		tva /= escala;
		tva = mRot.ProductoMatricial(tva).Normalizar() * normaVa;
		tva *= escala;
	}
	glEnd();

	// Arco
	wColorAngulo.Aplicar();

	glBegin(GL_LINE_STRIP);
	for (int i = 0; i <= 60; ++i) {
		glVertex2d(interseccion.x + va.x, interseccion.y + va.y);
		va /= escala;
		va = mRot.ProductoMatricial(va).Normalizar() * normaVa;
		va *= escala;
	}
	glEnd();


    // Dibujo highlights puntos activos
	for (int i = 0; i < 4; ++i)
	{
		if (m_Nodos[i].m_Iluminado) {
			wDibujarElipseDegradado(m_Nodos[i], wRadioPuntoIluminado * escala, wColorPuntoIluminadoInterior, wColorPuntoIluminadoExterior);
		}
	}

	// Cuerpo
	wAplicarColor(m_Iluminado, m_MouseDown, m_Seleccionado);


	glBegin(GL_LINES);
		glVertex2d(m_Nodos[0].x, m_Nodos[0].y);
		glVertex2d(m_Nodos[1].x, m_Nodos[1].y);
		glVertex2d(m_Nodos[2].x, m_Nodos[2].y);
		glVertex2d(m_Nodos[3].x, m_Nodos[3].y);
	glEnd();


	// Puntas de flechas
	wAplicarColor(m_Iluminado || m_Nodos[1].m_Iluminado, m_MouseDown, m_Seleccionado);
	glBegin(GL_LINE_STRIP);
		glVertex2d(puntaA0.x, puntaA0.y);
		glVertex2d(m_Nodos[1].x, m_Nodos[1].y);
		glVertex2d(puntaA1.x, puntaA1.y);
	glEnd();

	wAplicarColor(m_Iluminado || m_Nodos[3].m_Iluminado, m_MouseDown, m_Seleccionado);
	glBegin(GL_LINE_STRIP);
		glVertex2d(puntaB0.x, puntaB0.y);
		glVertex2d(m_Nodos[3].x, m_Nodos[3].y);
		glVertex2d(puntaB1.x, puntaB1.y);
	glEnd();

	// Nodos
	if (m_Seleccionado && m_MouseDown)
		glPointSize(WIDGET_SIZE_PUNTO * WIDGET_INCREMENTO_DETALLE_SELECCIONADO);
	else
		glPointSize(WIDGET_SIZE_PUNTO);

	wAplicarColor(m_Iluminado || m_Nodos[0].m_Iluminado, m_MouseDown, m_Seleccionado);
	glBegin(GL_POINTS);
		glVertex2d(m_Nodos[0].x, m_Nodos[0].y);
	glEnd();

	wAplicarColor(m_Iluminado || m_Nodos[2].m_Iluminado, m_MouseDown, m_Seleccionado);
	glBegin(GL_POINTS);
		glVertex2d(m_Nodos[2].x, m_Nodos[2].y);
	glEnd();

	// Interseccion
	interseccion = TVector::InterseccionEntreRectas(m_Nodos[0], m_Nodos[1], m_Nodos[2], m_Nodos[3]);

	if (m_MouseDown && m_Iluminado)
		glPointSize(WIDGET_INCREMENTO_DETALLE_SELECCIONADO * WIDGET_SIZE_PUNTO);
	else
		glPointSize(WIDGET_SIZE_PUNTO);

	// Sombra
	wColorSombra.Aplicar();
	glBegin(GL_POINTS);
	glVertex2d(interseccion.x + fsombrax, interseccion.y - fsombray);
	glEnd();

	// Punto
	wColorAngulo.Aplicar();
	glBegin(GL_POINTS);
	glVertex2d(interseccion.x, interseccion.y);
	glEnd();

	TexturaAngulo* ta = GetTextura(c->GetRenderer());
	if(ta->texto != m_Texto) {
		Recalcular(c->GetRenderer());
	}
	GNC::GCS::TexturaCairo* tc = ta->m_textura;
	// Dibujado de la medida.
	if (!tc->contextoCreado) {
		//std::cerr << "Error: Contexto no creado" << std::endl;
		return;
	}
	if (!tc->texturaCargada) {
		tc->Cargar();
	}
	if (tc->texturaModificada) {
		tc->Actualizar();
	}

	// Dibujado del texto.

	// Compute display pos
	GNC::GCS::Vector3D planePos(puntoTexto.x, puntoTexto.y, (double)c->pRenderer->m_pImageViewer->GetTindex());
	GNC::GCS::Vector3D worldPos;
	GNC::GCS::Vector3D displayPos;

	c->pRenderer->m_pImageViewer->UnProyect2D(planePos.v, worldPos.v);
	c->pRenderer->WorldToDisplay(worldPos.v, displayPos.v);

	// Alineamiento

	GNC::GCS::Vector alineacion = GNC::GCS::Vector(0.0, (m_TamTexto.y / 2.0));

	wDibujarTexturaTexto2D(c, *tc, (GNC::GCS::Vector(displayPos.x, displayPos.y) + alineacion ).Redondear());

}

GNC::GCS::Widgets::WAngulo::TexturaAngulo* GNC::GCS::Widgets::WAngulo::GetTextura(GNC::GCS::IWidgetsRenderer* renderer)
{
	if(m_mapaRecursos.find(renderer) == m_mapaRecursos.end()) {
		TexturaAngulo* pTextura = new TexturaAngulo();
		m_mapaRecursos[renderer] = pTextura;
		Recalcular(renderer);
	}
	return m_mapaRecursos[renderer];
}

void GNC::GCS::Widgets::WAngulo::Recalcular(GNC::GCS::IWidgetsRenderer* renderer)
{
	GNC::GCS::Vector3D worldNodes[4];
	if (renderer != NULL && renderer->m_pImageViewer != NULL) {
		const double tindex = renderer->m_pImageViewer->GetTindex();
		for (int i = 0; i < 4; i++) {
			double imagePosition[3] = {m_Nodos[i].x, m_Nodos[i].y, tindex};
			renderer->m_pImageViewer->CoordenadasImagenACoordenadasMundo(imagePosition, worldNodes[i].v);
		}
	} else {
		return;
	}

	m_Modificado = true;
	//std::cout << *this << std::endl;

	if (m_Modo == MA_Normal) {
		GNC::GCS::Vector3D w1 = worldNodes[0] - worldNodes[1];
		GNC::GCS::Vector3D w2 = worldNodes[2] - worldNodes[1];

		GNC::GCS::Vector i1 = m_Nodos[0] - m_Nodos[1];
		GNC::GCS::Vector i2 = m_Nodos[2] - m_Nodos[1];

		m_Angulo = w1.Angulo(w2);
		if (i1.ProductoVectorialZ(i2) > 0.0) {
			m_Angulo *= -1.0;
		}
	}
	else {
		GNC::GCS::Vector3D w1 = worldNodes[1] - worldNodes[0];
		GNC::GCS::Vector3D w2 = worldNodes[3] - worldNodes[2];

		GNC::GCS::Vector i1 = m_Nodos[1] - m_Nodos[0];
		GNC::GCS::Vector i2 = m_Nodos[3] - m_Nodos[2];

		float n1 = w1.Norma2Cuadrado();
		float n2 = w2.Norma2Cuadrado();

		if (n1 == 0.0f || n2 == 0.0f) {
			return;
		}

		float normaMenor = std::min(n1, n2);

		if (normaMenor == n1) {
			m_Angulo = w1.Angulo(w2);
			if (i1.ProductoVectorialZ(i2) > 0.0) {
				m_Angulo *= -1.0;
			}
		}
		else {
			m_Angulo = w2.Angulo(w1);
			if (i2.ProductoVectorialZ(i1) > 0.0) {
				m_Angulo *= -1.0;
			}
		}

		if (m_Angulo > M_PI ) {
			m_Angulo = m_Angulo - M_PI;
		}
		else if (m_Angulo < -M_PI ) {
			m_Angulo = m_Angulo + M_PI;
		}
	}

	std::stringstream os;
	os.setf(ios::floatfield, ios::fixed );
	os.precision(2);
	os.fill('0');

	os << (std::abs(m_Angulo) * 180 / M_PI)  << " º";

	std::string medida = os.str();

	if (renderer != NULL) {

		TexturaAngulo* ta = GetTextura(renderer);
		if (m_Texto != medida || ta->texto != medida) {
			ta->texto = medida;
			m_Texto = medida;
			//std::cout << "distancia = " << m_Texto << std::endl;
			GNC::GCS::TexturaCairo* tc = ta->m_textura;

			if (!tc->contextoCreado) {
				tc->Redimensionar(2, 2);
			}

			cairo_font_options_t* options;

			cairo_select_font_face (tc->cr, "Arial", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
			cairo_set_font_size(tc->cr, WIDGET_SIZE_FUENTE);
			options = cairo_font_options_create ();
			cairo_get_font_options(tc->cr, options);
			cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_NONE);
			cairo_set_font_options(tc->cr, options);

			m_TamTexto = GNC::GCS::GLHelper::calcularBoundingBox((*tc), m_Texto, m_AnchoMaximo); // Tamaño de la region en pixels

			tc->Redimensionar(std::ceil(m_TamTexto.x), std::ceil(m_TamTexto.y));
			m_TamTexto.Asignar(tc->ancho, tc->alto);

			cairo_select_font_face (tc->cr, "Arial", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
			cairo_set_font_size(tc->cr, WIDGET_SIZE_FUENTE);
			cairo_get_font_options(tc->cr, options);
			cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_NONE);
			cairo_set_font_options(tc->cr, options);
			cairo_font_options_destroy(options);
			options = NULL;

			cairo_set_operator(tc->cr, CAIRO_OPERATOR_SOURCE);
			cairo_set_source_rgba(tc->cr, 0.0f, 0.0f, 0.0f, 0.0f);
			cairo_paint(tc->cr);

			cairo_set_source_rgba (tc->cr, 1.0f, 1.0f, 1.0f, 1.0f);
			GNC::GCS::GLHelper::dibujarTexto((*tc), m_Texto, m_AnchoMaximo);
		}
	}
}

void GNC::GCS::Widgets::WAngulo::LiberarRecursos(GNC::GCS::IWidgetsRenderer* pKey)
{
	TMapaRecursos::iterator it = m_mapaRecursos.find(pKey);
	if(it!=m_mapaRecursos.end()) {
		delete (*it).second;
		m_mapaRecursos.erase(it);
	}
}


void GNC::GCS::Widgets::WAngulo::InsertarPropiedades(TListaMapasPropiedades &listaMapaPropiedades)
{
	TMapaPropiedades& primerMapa = listaMapaPropiedades.front();
	if(m_Modo == GNC::GCS::Widgets::MA_Normal) {
		primerMapa[_Std("Type")] = _Std("Normal");
	 } else {
		primerMapa[_Std("Type")] = _Std("Cobb");
	 }
	 {
		std::stringstream os;
		os.setf(ios::floatfield, ios::fixed );
		os.precision(2);
		os.fill('0');

		os << (std::abs(m_Angulo) * 180 / M_PI)  << " º";

		std::string medida = os.str();
		primerMapa[_Std("Value")] = medida;
	 }
}

//endregion

void GNC::GCS::Widgets::WAngulo::OffscreenRender(GNC::GCS::Contexto3D* c)
{
	
	const GNC::GCS::Vector& escala = c->RefRelacionMundoPantallaOffscreen();
	GNC::GCS::Vector TamTexto;

	float Angulo;

	if (m_Modo == MA_Normal) {
		TVector v1 = m_Nodos[0] - m_Nodos[1];
		TVector v2 = m_Nodos[2] - m_Nodos[1];

		Angulo = v1.Angulo(v2);
	}
	else {
		TVector v1 = m_Nodos[1] - m_Nodos[0];
		TVector v2 = m_Nodos[3] - m_Nodos[2];

		float n1 = v1.Norma2Cuadrado();
		float n2 = v2.Norma2Cuadrado();

		if (n1 == 0.0f || n2 == 0.0f) {
			return;
		}

		float normaMenor = std::min(n1, n2);

		if (normaMenor == n1) {
			Angulo = v1.Angulo(v2);
		}
		else {
			Angulo = v2.Angulo(v1);
		}

		if (Angulo > M_PI ) {
			Angulo = Angulo - M_PI;
		}
		else if (Angulo < -M_PI ) {
			Angulo = Angulo + M_PI;
		}
	}

	std::stringstream os;
	os.setf(ios::floatfield, ios::fixed );
	os.precision(2);
	os.fill('0');

	os << (std::abs(Angulo) * 180 / M_PI)  << " º";

	std::string Texto = os.str();

	if (m_Modo == MA_Normal) {

		TVector Nodos[3] = { c->Reproyectar(m_Nodos[0]) * c->factorReescalado, c->Reproyectar(m_Nodos[1]) * c->factorReescalado, c->Reproyectar(m_Nodos[2]) * c->factorReescalado };

		TVector v1 = Nodos[0] - Nodos[1];
		TVector v2 = Nodos[2] - Nodos[1];

		float n1 = v1.Norma2();
		float n2 = v2.Norma2();

		if (n1 == 0.0f || n2 == 0.0f) {
			return;
		}
		cairo_set_line_width(c->cr, WIDGET_GROSOR_LINEA);

		float anguloPunta = M_PI_4 / 2.0f;
		TVector vOrtogonalA = (Nodos[1] - Nodos[0]).VectorOrtonormal() * escala * 15.0f;
		TVector puntaA0 = Nodos[0] + vOrtogonalA.RotacionSobreZ(M_PI_2 + anguloPunta);
		TVector puntaA1 = Nodos[0] + vOrtogonalA.RotacionSobreZ(M_PI_2 - anguloPunta);

		TVector vOrtogonalB = (Nodos[1] - Nodos[2]).VectorOrtonormal() * escala * 15.0f;
		TVector puntaB0 = Nodos[2] + vOrtogonalB.RotacionSobreZ(M_PI_2 + anguloPunta);
		TVector puntaB1 = Nodos[2] + vOrtogonalB.RotacionSobreZ(M_PI_2 - anguloPunta);
		
		TMatriz mRot;
		mRot = TMatriz::MatrizRotacion(Angulo/60.0f);

		TVector va = v1.Normalizado() * std::min(n1, n2) * 0.3f;
		TVector vb = v2.Normalizado() * std::min(n1, n2) * 0.3f;

		TVector puntoTexto;

		// Arco
		cairo_set_source_rgba(c->cr, wColorAngulo.r, wColorAngulo.g, wColorAngulo.b, wColorAngulo.a);

		cairo_move_to(c->cr, Nodos[1].x + va.x, Nodos[1].y + va.y);
		va = mRot.ProductoMatricial(va);
		for (int i = 1; i < 60; ++i) {
			if ( i == 30) {
				puntoTexto = Nodos[1] + va;
			}
			cairo_line_to(c->cr, Nodos[1].x + va.x, Nodos[1].y + va.y);
			va = mRot.ProductoMatricial(va);
		}
		cairo_line_to(c->cr, Nodos[1].x + vb.x, Nodos[1].y + vb.y);
		cairo_stroke(c->cr);

		// Arco interior
		va = v1.Normalizado() * std::min(n1, n2) * 0.3f;
		cairo_set_source_rgba(c->cr, wColorAnguloInterior.r, wColorAnguloInterior.g, wColorAnguloInterior.b, wColorAnguloInterior.a);

		cairo_move_to(c->cr, Nodos[1].x, Nodos[1].y);
		cairo_line_to(c->cr, Nodos[1].x + va.x, Nodos[1].y + va.y);
		for (int i = 0; i < 60; ++i) {
			cairo_line_to(c->cr, Nodos[1].x + va.x, Nodos[1].y + va.y);
			va = mRot.ProductoMatricial(va);
		}
		cairo_line_to(c->cr, Nodos[1].x + vb.x, Nodos[1].y + vb.y);
		cairo_close_path(c->cr);
		cairo_fill(c->cr);

		// Cuerpo
		cairo_set_source_rgba(c->cr, wColorNormal.r, wColorNormal.g, wColorNormal.b, wColorNormal.a);

		cairo_move_to(c->cr, Nodos[0].x, Nodos[0].y);
		cairo_line_to(c->cr, Nodos[1].x, Nodos[1].y);
		cairo_line_to(c->cr, Nodos[2].x, Nodos[2].y);
		cairo_stroke(c->cr);

		// Puntas de flecha
		cairo_move_to(c->cr, puntaA0.x, puntaA0.y);
		cairo_line_to(c->cr, Nodos[0].x, Nodos[0].y);
		cairo_line_to(c->cr, puntaA1.x, puntaA1.y);
		cairo_stroke(c->cr);

		cairo_move_to(c->cr, puntaB0.x, puntaB0.y);
		cairo_line_to(c->cr, Nodos[2].x, Nodos[2].y);
		cairo_line_to(c->cr, puntaB1.x, puntaB1.y);
		cairo_stroke(c->cr);

		cairo_arc(c->cr, Nodos[1].x, Nodos[1].y, ( (WIDGET_SIZE_PUNTO / 2.0f) * escala.x), 0, M_2PI);
		cairo_fill(c->cr);

		// Texto de la medida
		cairo_font_options_t* options;
		options = cairo_font_options_create ();

		cairo_select_font_face (c->cr, "Arial", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
		cairo_set_font_size(c->cr, std::max(WIDGET_SIZE_FUENTE * escala.x,(double)8.0f));
		cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_NONE);
		cairo_set_font_options(c->cr, options);

		TamTexto = GNC::GCS::Widgets::HelperTexto::calcularBoundingBox(c, Texto, m_AnchoMaximo * escala.x); // Tamaño de la region en pixels

		GNC::GCS::Vector puntoTextoCorregido(puntoTexto.x, puntoTexto.y - (TamTexto.y / 2.0f));
		puntoTextoCorregido.Redondear();
		cairo_set_source_rgba (c->cr, 1.0f, 1.0f, 1.0f, 1.0f);		
		cairo_save(c->cr);
		cairo_translate(c->cr, puntoTextoCorregido.x, puntoTextoCorregido.y);
		GNC::GCS::Widgets::HelperTexto::dibujarTexto(c, Texto, m_AnchoMaximo * escala.x);
		cairo_restore(c->cr);

		cairo_font_options_destroy(options);
	}
	else {
		TVector Nodos[4] = { c->Reproyectar(m_Nodos[0]) * c->factorReescalado, c->Reproyectar(m_Nodos[1]) * c->factorReescalado, c->Reproyectar(m_Nodos[2]) * c->factorReescalado, c->Reproyectar(m_Nodos[3]) * c->factorReescalado };
		TVector v1 = Nodos[1] - Nodos[0];
		TVector v2 = Nodos[3] - Nodos[2];

		float n1 = v1.Norma2Cuadrado();
		float n2 = v2.Norma2Cuadrado();

		if (n1 == 0.0f || n2 == 0.0f) {
			return;
		}

		cairo_set_line_width(c->cr, WIDGET_GROSOR_LINEA);

		float anguloPunta = M_PI_4 / 2.0f;
		TVector vOrtogonalA = (Nodos[0] - Nodos[1]).VectorOrtonormal() * escala * 15.0f;
		TVector puntaA0 = Nodos[1] + vOrtogonalA.RotacionSobreZ(M_PI_2 + anguloPunta);
		TVector puntaA1 = Nodos[1] + vOrtogonalA.RotacionSobreZ(M_PI_2 - anguloPunta);

		TVector vOrtogonalB = (Nodos[2] - Nodos[3]).VectorOrtonormal() * escala * 15.0f;
		TVector puntaB0 = Nodos[3] + vOrtogonalB.RotacionSobreZ(M_PI_2 + anguloPunta);
		TVector puntaB1 = Nodos[3] + vOrtogonalB.RotacionSobreZ(M_PI_2 - anguloPunta);

		float normaMenor = std::min(n1, n2);
		TVector va;

		if (normaMenor == n1) {
			va = Nodos[0].PuntoMedio(Nodos[1]);
		}
		else {
			va = Nodos[3].PuntoMedio(Nodos[2]);
		}

		TVector puntoTexto;

		TVector interseccion = TVector::InterseccionEntreRectas(Nodos[0], Nodos[1], Nodos[2], Nodos[3]);
		// Arco
		{
			TVector p0 = Nodos[0].PuntoMedio(Nodos[1]);
			TVector p1 = Nodos[3].PuntoMedio(Nodos[2]);
			puntoTexto = p0.PuntoMedio(p1);

			wColorAngulo.AplicarCairo(c->cr);
			cairo_move_to(c->cr, p0.x, p0.y);
			cairo_line_to(c->cr, p1.x, p1.y);
			cairo_stroke(c->cr);
		}

		TMatriz mRot;

		mRot = TMatriz::MatrizRotacion(Angulo/60.0f);
		va -= interseccion;

		// Arco
		wColorAngulo.AplicarCairo(c->cr);

		/////////////////////////////
		cairo_move_to(c->cr, interseccion.x + va.x, interseccion.y + va.y);
		for (int i = 1; i <= 60; ++i) {

			va = mRot.ProductoMatricial(va);
			cairo_line_to(c->cr, interseccion.x + va.x, interseccion.y + va.y);
		}
		cairo_stroke(c->cr);

		// Cuerpo
		wColorNormal.AplicarCairo(c->cr);
		cairo_move_to(c->cr, Nodos[0].x, Nodos[0].y);
		cairo_line_to(c->cr, Nodos[1].x, Nodos[1].y);
		cairo_stroke(c->cr);

		cairo_move_to(c->cr, Nodos[2].x, Nodos[2].y);
		cairo_line_to(c->cr, Nodos[3].x, Nodos[3].y);
		cairo_stroke(c->cr);

		cairo_move_to(c->cr, puntaA0.x, puntaA0.y);
		cairo_line_to(c->cr, Nodos[1].x, Nodos[1].y);
		cairo_line_to(c->cr, puntaA1.x, puntaA1.y);
		cairo_stroke(c->cr);

		cairo_move_to(c->cr, puntaB0.x, puntaB0.y);
		cairo_line_to(c->cr, Nodos[3].x, Nodos[3].y);
		cairo_line_to(c->cr, puntaB1.x, puntaB1.y);
		cairo_stroke(c->cr);

		cairo_arc(c->cr, Nodos[0].x, Nodos[0].y,  (WIDGET_SIZE_PUNTO / 2.0f) * escala.x, 0, M_2PI);
		cairo_fill(c->cr);

		cairo_arc(c->cr, Nodos[2].x, Nodos[2].y,  (WIDGET_SIZE_PUNTO / 2.0f) * escala.x, 0, M_2PI);
		cairo_fill(c->cr);

		// Interseccion
		interseccion = TVector::InterseccionEntreRectas(Nodos[0], Nodos[1], Nodos[2], Nodos[3]);
		cairo_arc(c->cr, interseccion.x, interseccion.y,  (WIDGET_SIZE_PUNTO / 2.0f) * escala.x, 0, M_2PI);
		cairo_fill(c->cr);

		// Punto
		wColorAngulo.AplicarCairo(c->cr);
		cairo_arc(c->cr, interseccion.x, interseccion.y,  (WIDGET_SIZE_PUNTO / 2.0f) * escala.x, 0, M_2PI);
		cairo_fill(c->cr);

		// Texto de la medida
		cairo_font_options_t* options;
		options = cairo_font_options_create ();

		cairo_select_font_face (c->cr, "Arial", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
		cairo_set_font_size(c->cr, std::max(WIDGET_SIZE_FUENTE * escala.x,(double)8.0f));
		cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_NONE);
		cairo_set_font_options(c->cr, options);

		TamTexto = GNC::GCS::Widgets::HelperTexto::calcularBoundingBox(c, Texto, m_AnchoMaximo * escala.x); // Tamaño de la region en pixels

		GNC::GCS::Vector puntoTextoCorregido(puntoTexto.x, puntoTexto.y - (TamTexto.y / 2.0f));
		puntoTextoCorregido.Redondear();
		cairo_set_source_rgba (c->cr, 1.0f, 1.0f, 1.0f, 1.0f);
		cairo_save(c->cr);		
		cairo_translate(c->cr, puntoTextoCorregido.x, puntoTextoCorregido.y);
		GNC::GCS::Widgets::HelperTexto::dibujarTexto(c, Texto, m_AnchoMaximo * escala.x);
		cairo_restore(c->cr);

		cairo_font_options_destroy(options);
	}
}
