/**********************************************************************
 
	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.

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


#ifndef ___GBGRAPH_H___
#define ___GBGRAPH_H___

#include	"graph.h"
#include	"xllisp.h"
#include	"unit.h"



typedef R_POINT GB_POINT;
typedef R_RECT GB_RECT;
typedef R_POINT3D GB_POINT3D;

typedef struct affen2d {
	GB_POINT	org;
	REAL1		matrix[2][2];
} AFFEN2D;

typedef struct gb_color {
	REAL1		r;
	REAL1		g;
	REAL1		b;
	REAL1		t;
} GB_COLOR;

typedef struct gb_color_int {
	unsigned long	c;
	REAL1		t;
	REAL1		rev_t;
} GB_COLOR_INT;

typedef struct gb_palette {
	GB_COLOR_INT	line_color;
	GB_COLOR_INT	padding_color;
} GB_PALETTE;

typedef struct gb_time {
	REAL1		year;
	char		month;
	char		date;
	char		day;
	char		hour;
	char		min;
	char		sec;
} GB_TIME;

typedef REAL1 MATRIX2D[2][2];
typedef REAL1 MATRIX3D[3][3];


int xl_to_gb_point(GB_POINT *p, XL_SEXP * s);
int xl_to_gb_rect(GB_RECT *r, XL_SEXP * s);
XL_SEXP * gb_point_to_xl(GB_POINT *p);
XL_SEXP * gb_rect_to_xl(GB_RECT *r);


int get_matrix(AFFEN2D * m,
	GB_POINT s1,GB_POINT d1,
	GB_POINT s2,GB_POINT d2,
	GB_POINT s3,GB_POINT d3);
XL_SEXP * gbpoint2list(GB_POINT);
REAL1 get_sexp2real(int * er,COORDINATE_UNIT * cu,XL_SEXP * x,int reso_fg);
GB_POINT list2gbpoint(COORDINATE_UNIT*,XL_SEXP *);
int get_minrect(COORDINATE_UNIT * cu,GB_RECT * r,XL_SEXP * s);
int get_gbpoint(COORDINATE_UNIT * cu,GB_POINT * p,XL_SEXP * s);
int inside_triangle(GB_POINT ptp[3],GB_POINT p);
int i_rect_type(XL_SEXP * );
I_RECT xl_to_i_rect(XL_SEXP * s);
int lod_distance(GB_RECT * tr,GB_POINT pt);
int lod_select(int dist,int lod,int core);

int
list2vector(XL_SEXP ** ret,GB_POINT * p,XL_SEXP * v,XL_FILE * file,int line,
	    char * er_func);
int
list2matrix(XL_SEXP ** ret,REAL1 mret[2][2],XL_SEXP * m,
	    XL_FILE * file,int line,char * er_func);

GB_POINT p_avg(GB_POINT,GB_POINT);
int w3c_dtf_to_gb_time(GB_TIME *,char *);
int cmp_gb_time(GB_TIME * ,GB_TIME * );
int valid_gb_time(GB_TIME * t);
void invalid_gb_time(GB_TIME * t);
void gb_time_format(char * ret,GB_TIME * t);

int check_fnan(REAL1);
int cmp_gb_time_set(char a,char b);
void print_gb_time(GB_TIME * t);
void set_format(char * buf,char d);
int get_rol(AFFEN2D * m,
	GB_POINT s1,GB_POINT d1,
	GB_POINT s2,GB_POINT d2);
int get_matrix(AFFEN2D * m,
	GB_POINT s1,GB_POINT d1,
	GB_POINT s2,GB_POINT d2,
	GB_POINT s3,GB_POINT d3);
REAL1 puseudo_rate(REAL1 m[2][2]);
int rect_type(XL_SEXP * s);



#ifndef GRAPH_INLINE

void insert_rect(GB_RECT * r,GB_POINT p);
GB_POINT p_sub(GB_POINT,GB_POINT);
GB_POINT p_add(GB_POINT,GB_POINT);
GB_POINT mp_mul(REAL1 m[2][2],GB_POINT p);
void m_mul(REAL1 res[2][2],REAL1 m1[2][2],REAL1 m2[2][2]);
GB_POINT caffen2d(AFFEN2D*,GB_POINT);
void conv_affen2d(AFFEN2D * res,AFFEN2D * a,AFFEN2D * b);
int inside_rect(GB_RECT * r,GB_POINT p);
void insert_rect(GB_RECT * r,GB_POINT p);
REAL1 distance(GB_POINT,GB_POINT);
int cross_rect_circle(GB_RECT * rect,GB_POINT p,REAL1 r);
void add_rect(GB_RECT * r1,GB_RECT * r);
int cross_rect_rect(GB_RECT * r1,GB_RECT * r2);
int check_nan_rect(GB_RECT * r1);
int cross_rect_triangle(GB_RECT * r1,GB_POINT tri[3]);
void rect2circle(GB_POINT * center,REAL1 * radius,GB_RECT * r);
REAL1 inner(GB_POINT,GB_POINT);
void intersection_rect(GB_RECT * r,GB_RECT * r1,GB_RECT * r2);
REAL1 rect_resolution(GB_RECT * r,REAL1 dist,int num);

GB_POINT3D mp_mul3d(REAL1 m[3][3],GB_POINT3D p);
void m_mul3d(MATRIX3D,MATRIX3D,MATRIX3D);

#else

INLINE_STATIC
GB_POINT p_sub(GB_POINT p1,GB_POINT p2)
{
GB_POINT ret;
	ret.x = p1.x-p2.x;
	ret.y = p1.y-p2.y;
	return ret;
}

INLINE_STATIC
GB_POINT p_add(GB_POINT p1,GB_POINT p2)
{
GB_POINT ret;
	ret.x = p1.x+p2.x;
	ret.y = p1.y+p2.y;
	return ret;
}



INLINE_STATIC
GB_POINT mp_mul(REAL1 m[2][2],GB_POINT p)
{
GB_POINT ret;
	ret.x = m[0][0]*p.x + m[0][1]*p.y;
	ret.y = m[1][0]*p.x + m[1][1]*p.y;
	return ret;
}

INLINE_STATIC
void
m_mul(REAL1 res[2][2],REAL1 m1[2][2],REAL1 m2[2][2])
{
int i,j,k;
double acc;
	for ( i = 0 ; i < 2 ; i ++ )
		for ( j = 0 ; j < 2 ; j ++ ) {
			acc = 0;
			for ( k = 0 ; k < 2 ; k ++ )
				acc += m1[i][k] * m2[k][j];
			res[i][j] = acc;
		}
}

INLINE_STATIC
GB_POINT caffen2d(AFFEN2D * a,GB_POINT p)
{
GB_POINT ret;
	ret.x = a->matrix[0][0]*p.x +
		a->matrix[0][1]*p.y +
		a->org.x;
	ret.y = a->matrix[1][0]*p.x +
		a->matrix[1][1]*p.y +
		a->org.y;
	return ret;
}



INLINE_STATIC
void
conv_affen2d(AFFEN2D * res,AFFEN2D * a,AFFEN2D * b)
{
	m_mul(res->matrix,a->matrix,b->matrix);
	res->org = p_add(mp_mul(a->matrix,b->org),a->org);
}


INLINE_STATIC
int
inside_rect(GB_RECT * r,GB_POINT p)
{
	if ( r->tl.x > r->br.x )
		return 1;
	if ( r->tl.y > r->br.y )
		return 1;
	if ( r->tl.x > r->br.x )
		return 1;
	if ( r->tl.y > r->br.y )
		return 1;
	if ( r->tl.x > p.x )
		return 0;
	if ( r->tl.y > p.y )
		return 0;
	if ( r->br.x < p.x )
		return 0;
	if ( r->br.y < p.y )
		return 0;
	return 1;
}

INLINE_STATIC
void
insert_rect(GB_RECT * r,GB_POINT p)
{
	if ( r->tl.x > r->br.x ) {
		r->tl = r->br = p;
		return;
	}
	if ( r->tl.x > p.x )
		r->tl.x = p.x;
	if ( r->tl.y > p.y )
		r->tl.y = p.y;
	if ( r->br.x < p.x )
		r->br.x = p.x;
	if ( r->br.y < p.y )
		r->br.y = p.y;
	return;
}

INLINE_STATIC
REAL1
distance(GB_POINT p1,GB_POINT p2)
{
GB_POINT p;
	p = p_sub(p1,p2);
	return sqrt(p.x*p.x + p.y*p.y);
}

/*
INLINE_STATIC
int
cross_rect_circle(GB_RECT * rect,GB_POINT p,REAL1 r)
{
GB_POINT tmp;
	if ( rect->tl.x > rect->br.x )
		return 1;
	if ( rect->tl.y > rect->br.y )
		return 1;
	if ( inside_rect(rect,p) )
		return 1;
	if ( rect->tl.x <= p.x && rect->br.x >= p.x ) {
		if ( fabs(p.y-rect->tl.y) <= r )
			return 1;
		if ( fabs(p.y-rect->br.y) <= r )
			return 1;
		return 0;
	}
	else if ( rect->tl.y <= p.y && rect->br.y >= p.y ) {
		if ( fabs(p.x-rect->tl.x) <= r )
			return 1;
		if ( fabs(p.x-rect->br.x) <= r )
			return 1;
		return 0;
	}
	else if ( rect->tl.x > p.x && rect->tl.y > p.y ) {
		if ( distance(p,rect->tl) < r )
			return 1;
		else	return 0;
	}
	else if ( rect->tl.x > p.x && rect->br.y < p.y ) {
		tmp.x = rect->tl.x;
		tmp.y = rect->br.y;
		if ( distance(p,tmp) < r )
			return 1;
		else	return 0;
	}
	else if ( rect->br.x < p.x && rect->tl.y > p.y ) {
		tmp.x = rect->br.x;
		tmp.y = rect->tl.y;
		if ( distance(p,tmp) < r )
			return 1;
		else	return 0;
	}
	else {
		if ( distance(p,rect->br) < r )
			return 1;
		else	return 0;
	}
}
*/

INLINE_STATIC
void
add_rect(GB_RECT * r1,GB_RECT * r)
{
	if ( r1->tl.x > r1->br.x ) {
		*r1 = *r;
		return;
	}
	if ( r1->tl.x > r->tl.x )
		r1->tl.x = r->tl.x;
	if ( r1->tl.y > r->tl.y )
		r1->tl.y = r->tl.y;
	if ( r1->br.x < r->br.x )
		r1->br.x = r->br.x;
	if ( r1->br.y < r->br.y )
		r1->br.y = r->br.y;
	return;
}

INLINE_STATIC
int
cross_rect_rect(GB_RECT * r1,GB_RECT * r2)
{
	if ( r1->tl.x <= r2->br.x && r1->br.x >= r2->tl.x &&
		r1->tl.y <= r2->br.y && r1->br.y >= r2->tl.y )
		return 1;
	return 0;
}

INLINE_STATIC
int
check_nan_rect(GB_RECT * r1)
{
	if ( r1->tl.x > r1->br.x )
		return 1;
	if ( r1->tl.y > r1->br.y )
		return 1;
	return 0;
}

INLINE_STATIC
int
cross_rect_triangle(GB_RECT * r1,GB_POINT tri[3])
{
GB_POINT p;
	if ( inside_rect(r1,tri[0]) == 1 )
		return 1;
	if ( inside_rect(r1,tri[1]) == 1 )
		return 1;
	if ( inside_rect(r1,tri[2]) == 1 )
		return 1;
	if ( inside_triangle(tri,r1->tl) == 0 )
		return 1;
	if ( inside_triangle(tri,r1->br) == 0 )
		return 1;
	p.x = r1->tl.x;
	p.y = r1->br.y;
	if ( inside_triangle(tri,p) == 0 )
		return 1;
	p.x = r1->br.x;
	p.y = r1->tl.y;
	if ( inside_triangle(tri,p) == 0 )
		return 1;
	return 0;
}

INLINE_STATIC
void
rect2circle(GB_POINT * center,REAL1 * radius,GB_RECT * r)
{
REAL1 xx,yy;
	center->x = (r->tl.x + r->br.x)/2;
	center->y = (r->tl.y + r->br.y)/2;
	xx = r->tl.x - r->br.x;
	yy = r->tl.y - r->br.y;
	*radius = sqrt(xx*xx + yy*yy)/2;
}


INLINE_STATIC
REAL1
inner(GB_POINT a,GB_POINT b)
{
	return a.x*b.x + a.y*b.y;
}


INLINE_STATIC
void
intersection_rect(GB_RECT * r,GB_RECT * r1,GB_RECT * r2)
{
GB_RECT ret;
	if ( r1->tl.x > r1->br.x || r1->tl.y > r1->br.y ) {
		*r = *r2; 
		return;
	}
	if ( r2->tl.x > r2->br.x || r2->tl.y > r2->br.y ) {
		*r = *r1; 
		return;
	}
	if ( r1->tl.x < r2->tl.x )
		ret.tl.x = r2->tl.x;
	else	ret.tl.x = r1->tl.x;
	if ( r1->tl.y < r2->tl.y )
		ret.tl.y = r2->tl.y;
	else	ret.tl.y = r1->tl.y;
	if ( r1->br.x < r2->br.x )
		ret.br.x = r1->br.x;
	else	ret.br.x = r2->br.x;
	if ( r1->br.y < r2->br.y )
		ret.br.y = r1->br.y;
	else	ret.br.y = r2->br.y;
	*r = ret;
}




INLINE_STATIC
REAL1
rect_resolution(GB_RECT * r,REAL1 dist,int num)
{
double w,h,reso2,reso1;
	w = r->br.x - r->tl.x;
	h = r->br.y - r->tl.y;
	if ( w < h )
		reso1 = 1/w;
	else	reso1 = 1/h;
	reso2 = dist/sqrt(h*w/num);
	if ( reso2 < reso1 )
		return reso1;
	return reso2;
}


INLINE_STATIC
GB_POINT3D mp_mul3d(REAL1 m[3][3],GB_POINT3D p)
{
GB_POINT3D ret;
	ret.x = m[0][0]*p.x + m[0][1]*p.y + m[0][2]*p.z;
	ret.y = m[1][0]*p.x + m[1][1]*p.y + m[1][2]*p.z;
	ret.z = m[2][0]*p.x + m[2][1]*p.y + m[2][2]*p.z;
	return ret;
}


INLINE_STATIC void
m_mul3d(MATRIX3D ret,MATRIX3D a,MATRIX3D b)
{
int i,j,k;
double acc;
	for ( i = 0 ;  i < 3 ; i ++ )
		for ( j = 0; j < 3 ; j ++ ) {
			acc = 0;
			for ( k = 0 ; k < 3 ; k ++ )
				acc += a[i][k] * b[k][j];
			ret[i][j] = acc;
		}
}



#endif


#endif
