/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

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


#include	"machine/u_math.h"
#include	"gbgraph.h"
#include	"memory_debug.h"
#include	"viewindex.h"


GB_POINT
get_globe_position3d(GB_POINT3D p)
{
GB_POINT on_g;
	on_g.y = asin(-p.y);
	if ( p.z || p.x ) {
		if ( p.z > p.x ) {
			if ( p.z > - p.x ) {
				/* 1 */
				on_g.x = atan( p.x / p.z );
			}
			else {
				/* 2 */
				on_g.x = - M_PI/2
					- atan( p.z / p.x );
			}
		}
		else {
			if ( p.z > - p.x ) {
				/* 3 */
				on_g.x = M_PI/2
					- atan( p.z / p.x );
			}
			else if ( p.x > 0 ) {
				/* 4 */
				on_g.x = M_PI
					+ atan( p.x / p.z );
			}
			else {
				/* 4' */
				on_g.x = -M_PI
					+ atan( p.x / p.z );
			}
		}
	}
	else	on_g.x = 0;
	return on_g;
}

int
get_globe_position(GB_POINT * on_g,GB_POINT pt,double radius)
{
GB_POINT3D p;
double d;

	p.x = pt.x/radius;
	p.y = pt.y/radius;
	d = p.x*p.x + p.y*p.y;
	if ( d > 1 )
		return -1;
	p.z = sqrt(1 - d);
	*on_g = get_globe_position3d(p);
	return 0;
}


void
get_round_matrix3d_lambda(MATRIX3D rm,double lambda)
{
REAL1 c,s;
	c = cos(lambda);
	s = sin(lambda);
	rm[0][0] = c;
	rm[0][1] = 0;
	rm[0][2] = s;
	rm[1][0] = 0;
	rm[1][1] = 1;
	rm[1][2] = 0;
	rm[2][0] = -s;
	rm[2][1] = 0;
	rm[2][2] = c;
}

void
get_round_matrix3d_phi(MATRIX3D rm,double phi)
{
REAL1 c,s;
	c = cos(phi);
	s = sin(phi);
	rm[0][0] = 1;
	rm[0][1] = 0;
	rm[0][2] = 0;
	rm[1][0] = 0;
	rm[1][1] = c;
	rm[1][2] = -s;
	rm[2][0] = 0;
	rm[2][1] = s;
	rm[2][2] = c;
}

void
get_round_matrix3d_cita(MATRIX3D rm,double cita)
{
REAL1 c,s;
	c = cos(cita);
	s = sin(cita);
	rm[0][0] = c;
	rm[0][1] = s;
	rm[0][2] = 0;
	rm[1][0] = -s;
	rm[1][1] = c;
	rm[1][2] = 0;
	rm[2][0] = 0;
	rm[2][1] = 0;
	rm[2][2] = 1;
}

void
get_round_matrix3d(MATRIX3D rm,GB_POINT p)
{
MATRIX3D l_rm,p_rm;
	get_round_matrix3d_lambda(l_rm,p.x);
	get_round_matrix3d_phi(p_rm,p.y);
	m_mul3d(rm,l_rm,p_rm);
}

int
get_gmatrix(MATRIX3D rm,
	GB_POINT from,
	GB_POINT to,
	double radius)
{
int er;
GB_POINT _from,_to;
MATRIX3D l_rm1,l_rm2,p_rm,rm1;

	if ( get_globe_position(&_from,from,radius) < 0 )
		return -1;
	if ( get_globe_position(&_to,to,radius) < 0 )
		return -1;
	get_round_matrix3d_lambda(l_rm1,- _to.x);
	get_round_matrix3d_lambda(l_rm2,_from.x);
	get_round_matrix3d_phi(p_rm,_from.y - _to.y);
	m_mul3d(rm1,p_rm,l_rm1);
	m_mul3d(rm,l_rm2,rm1);
	return 0;
}


int
get_inv_gmatrix(MATRIX3D rm,
	GB_POINT from,
	GB_POINT to,
	double radius)
{
int er;
GB_POINT _from,_to;
MATRIX3D l_rm1,l_rm2,p_rm,rm1;

	if ( get_globe_position(&_from,from,radius) < 0 )
		return -1;
	if ( get_globe_position(&_to,to,radius) < 0 )
		return -1;
	get_round_matrix3d_lambda(l_rm1,- _from.x);
	get_round_matrix3d_lambda(l_rm2,_to.x);
	get_round_matrix3d_phi(p_rm,_to.y - _from.y);
	m_mul3d(rm1,p_rm,l_rm1);
	m_mul3d(rm,l_rm2,rm1);
	return 0;
}


int
wf_make_gmatrix_index(
	VPOINT * index,
	GB_POINT from,
	GB_POINT to,
	double radius,
	int width,
	int height)
{
MATRIX3D rm;
int x,y,w,h;
GB_POINT3D pt;
double d,sq_radius;

	w = width;
	h = height;
	from.x -= w/2;
	from.y -= h/2;
	to.x -= w/2;
	to.y -= h/2;
	sq_radius = radius * radius;
	if ( get_gmatrix(rm,from,to,radius) < 0 )
		return -1;
	for ( y = 0 ; y < h ; y ++ )
		for ( x = 0 ; x < w ; x ++ ) {
			pt.x = x - w/2;
			pt.y = y - h/2;
			d = pt.x*pt.x + pt.y*pt.y;
			if ( d > sq_radius )
				goto no_data;
			pt.z = sqrt(sq_radius - d);
			pt = mp_mul3d(rm,pt);
			if ( pt.z < 0 )
				goto no_data;
			index->x = rint(pt.x) + w/2;
			index->y = rint(pt.y) + h/2;
			if ( index->x < 0 )
				goto no_data;
			if ( index->x >= w )
				goto no_data;
			if ( index->y < 0 )
				goto no_data;
			if ( index->y >= h )
				goto no_data;
			index ++;
			continue;
		no_data:
			index->x = -1;
			index->y = -1;
			index ++;
		}
	return 0;
}
