/**********************************************************************
 
	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	<math.h>
#include	<stdlib.h>
#include	"utils.h"
#include	"gbview.h"
#include	"xlerror.h"
#include	"memory_debug.h"
#include	"memory_routine.h"
#include	"lock_level.h"
#include	"u_math.h"



XL_SEXP * point_map();
XL_SEXP * triangle_map();
XL_SEXP * angle();
XL_SEXP * gv_map_status();
XL_SEXP * horizontal_affen();

SEM map_lock;

void
init_map()
{
XLISP_ENV * map_top,* map_cmd;

	map_lock = new_lock(LL_MAP);

	map_cmd = new_env(0);
	map_top = new_env(gblisp_top_env0);

	set_env(map_cmd,l_string(std_cm,"point-map"),
			get_func_prim(point_map,FO_NORMAL,0,4,4));
	set_env(map_cmd,l_string(std_cm,"triangle-map"),
			get_func_prim(triangle_map,FO_NORMAL,0,4,4));
	set_env(map_cmd,l_string(std_cm,"horizontal-affen"),
			get_func_prim(horizontal_affen,FO_APPLICATIVE,0,3,3));
	set_env(map_cmd,l_string(std_cm,"angle"),
			get_func_prim(angle,FO_NORMAL,0,2,2));
	set_env(map_top,l_string(std_cm,"gv-status"),
			get_func_prim(gv_map_status,FO_APPLICATIVE,0,1,1));

	set_env(gblisp_top_env0,l_string(std_cm,"map"),
		get_env(map_top));

	root_tag(map_top,l_string(std_cm,"map"),map_cmd);
	set_gv_resource(RT_MAP,map_top);
}

void
free_map_parameter(MAP_PARAMETER * mp)
{
TRIANGLE * t1, * t2;
	if ( mp->type != MT_TRIANGLE ) {
		mp->type = 0;
		return;
	}
	for ( t1 = mp->opt.tri.tri_list ; t1 ; ) {
		t2 = t1->next;
		d_f_ree(t1);
		t1 = t2;
	}
	mp->type = 0;
}


XL_SEXP *
gv_new_map(RESOURCE * r,XL_SEXP * s,int ds)
{
MAP_POINT_LIST * mp;
int ret_dest,ret_src;
L_CHAR * src,* dest;
char * e_param;
L_CHAR * map_url;
L_CHAR * url;


	switch ( ds ) {
	case NR_CLEAR:
		lock_task(map_lock);
		free_map_parameter(&r->map.param_reverse);
		free_map_parameter(&r->map.param_forward);
		for ( ; r->map.point_list.next !=
				&r->map.point_list ; ){
			mp = R_NEXT(MAP_POINT_LIST*,
				&r->map.point_list);
			if ( mp->tag )
				d_f_ree(mp->tag);
			DELETE_RING(&mp->h);
			d_f_ree(mp);
		}
		unlock_task(map_lock,"gv_new_map");
	case NR_NEW:
		get_field(s,
			l_string(std_cm,"src"),"s",&src,&ret_src,
			l_string(std_cm,"dest"),"s",&dest,&ret_dest,
			0);
		if ( ret_src || ret_dest ) {
			e_param = "src/dest";
			goto subtype_error;
		}
		map_url = ll_copy_str(get_url_str2(&r->h.entry),123);
		url = compose_url(map_url,src);
		get_url2(&r->map.src,url,1610);
		setup_c_unit(&r->map.cu_src,-1,url,0,0);
		d_f_ree(url);
		url = compose_url(map_url,dest);
		get_url2(&r->map.dest,url,1611);
		setup_c_unit(&r->map.cu_dest,-1,url,0,0);
		d_f_ree(url);

		d_f_ree(map_url);

		if ( r->map.cu_src.uenv == 0 ) {
			e_param = "src/uenv cannot read";
			goto subtype_error;
		}
		if ( r->map.cu_dest.uenv == 0 ) {
			e_param = "dest/uenv cannot read";
			goto subtype_error;
		}

		INIT_RING(&r->map.point_list);
		r->map.param_reverse.type = 0;
		r->map.param_forward.type = 0;
	case NR_KEEP:
		;
	}


	return 0;
subtype_error:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_PROTO_INV_PARAM,
		l_string(std_cm,"gv-new-resource(coordinate)"),
		List(n_get_string("invalid subtype in the meta info"),
			get_string(l_string(std_cm,e_param)),
			n_get_string("argument"),
			-1));
}


XL_SEXP * 
gv_map_status(XLISP_ENV * env,XL_SEXP * s,XLISP_ENV * a,XL_SYM_FIELD * sf)
{
XL_SEXP * ret;
RESOURCE * r;
	return cons(
		get_symbol(l_string(std_cm,"map")),
		get_resource_status_header(
			&r,env,s,a,0));
}

void
draw_map()
{
	er_panic("draw_map(1)");
}

void
get_point_map()
{
	er_panic("get_point_map(1)");
}

void
lock_map()
{
	lock_task(map_lock);
}

void
unlock_map()
{
	unlock_task(map_lock,"unlock_map");
}

void
free_mapping_from_sd(RESOURCE * src,RESOURCE * dest)
{
MAP * m1;
	if ( dest->h.type != RT_COORDINATE )
		er_panic("free_mapping");
	for ( m1 = R_NEXT(MAP*,&dest->c.map_children);
			m1 != (MAP*)&dest->c.map_children;
			m1 = R_NEXT(MAP*,&m1->h) )
		if ( m1->src == src )
			goto ok;
	return;
ok:
	lock_task(map_lock);
	free_url(&m1->src_url);
	free_url(&m1->dest_url);
	free_url(&m1->map_url);
	DELETE_RING(&m1->h);
	unlock_task(map_lock,"free_mapping_from_sd");

	if ( src->h.type != RT_COORDINATE )
		return;
	for ( m1 = R_NEXT(MAP*,&src->c.map_parents);
			m1 != (MAP*)&src->c.map_parents;
			m1 = R_NEXT(MAP*,&m1->h) )
		if ( m1->dest == dest )
			goto ok2;
	return;
ok2:
	free_url(&m1->src_url);
	free_url(&m1->dest_url);
	free_url(&m1->map_url);
	DELETE_RING(&m1->h);
	d_f_ree(m1);
}


REAL1
map_radius(MAP_PARAMETER * mp,GB_POINT c,REAL1 radius)
{
TRIANGLE * tt;
TRIANGLE * t;
GB_POINT ret;
GB_RECT r;
double rate,_rate,A0,A1,A2,R;
double rr;
int f;
	lock_task(map_lock);
	rr = radius;
	rate = 1;
retry:
	r.tl.x = c.x - rr;
	r.tl.y = c.y - rr;
	r.br.x = c.x + rr;
	r.br.y = c.y + rr;
	f = 0;
	for ( tt = mp->opt.tri.tri_list ; tt ; tt = tt->next ) {
		if ( cross_rect_triangle(&r,tt->ptp) == 0 )
			continue;
		A0 = tt->a.matrix[0][0]*tt->a.matrix[0][0] +
			tt->a.matrix[1][0]*tt->a.matrix[1][0];
		A1 = tt->a.matrix[0][0]*tt->a.matrix[0][1] +
			tt->a.matrix[1][0]*tt->a.matrix[1][1];
		A2 = tt->a.matrix[0][1]*tt->a.matrix[0][1] +
			tt->a.matrix[1][1]*tt->a.matrix[1][1];
		R = sqrt((A0-A2)*(A0-A2)/4 + A1*A1);
		_rate = (A0 + A2)/2 + R;
		if ( rate < _rate ) {
			f = 1;
			rate = _rate;
		}
	}
	if ( mp->opt.tri.sts != TS_NOSET &&
			cross_rect_rect(&r,&mp->opt.tri.minrect) ) {

		A0 = mp->opt.tri.a.matrix[0][0]*mp->opt.tri.a.matrix[0][0] +
			mp->opt.tri.a.matrix[1][0]*mp->opt.tri.a.matrix[1][0];
		A1 = mp->opt.tri.a.matrix[0][0]*mp->opt.tri.a.matrix[0][1] +
			mp->opt.tri.a.matrix[1][0]*mp->opt.tri.a.matrix[1][1];
		A2 = mp->opt.tri.a.matrix[0][1]*mp->opt.tri.a.matrix[0][1] +
			mp->opt.tri.a.matrix[1][1]*mp->opt.tri.a.matrix[1][1];
		R = sqrt((A0-A2)*(A0-A2)/4 + A1*A1);
		_rate = (A0 + A2)/2 + R;
		if ( rate < _rate ) {
			f = 1;
			rate = _rate;
		}
	}
	if ( f ) {
		rr = radius*rate;
		goto retry;
	}
	unlock_task(map_lock,"map_radius");
	return rr;
}


int test_ff;


GB_POINT
_map_conv_triangle(REAL1 *res,MAP_PARAMETER * mp,GB_POINT p)
{
TRIANGLE ** tt;
TRIANGLE * t;
GB_POINT ret;


	if ( mp->opt.tri.sts != TS_NOSET ) {
		if ( inside_rect(&mp->opt.tri.minrect,p) == 0 )
			goto ok_outside;
	}


	for ( tt = &mp->opt.tri.tri_list ; *tt ; tt = &(*tt)->next ) {
		if ( inside_rect(&(*tt)->minrect,p) == 0 )
			continue;
		if ( inside_triangle((*tt)->ptp,p) == 0 )
			goto ok;
	}

	if ( mp->opt.tri.sts != TS_NOSET )
		goto ok_outside;
	*res = -1;
	return p;
ok:

	t = *tt;
	*tt = t->next;
	t->next = mp->opt.tri.tri_list;
	mp->opt.tri.tri_list = t;
	*res *= t->resolution_rate;
	ret = caffen2d(&t->a,p);


	return ret;
ok_outside:


	*res *= mp->opt.tri.resolution_rate;
	ret = caffen2d(&mp->opt.tri.a,p);

	return ret;
}

GB_POINT
map_conv_triangle(REAL1 *res,MAP_PARAMETER * mp,GB_POINT p)
{
GB_POINT ret;
	lock_task(map_lock);
	ret = _map_conv_triangle(res,mp,p);
	unlock_task(map_lock,"map_conv_triangle");
	return ret;
}


void
map_conv_reverse(RESOURCE * mp,GB_POINT * p,REAL1 * reso,int size)
{
REAL1 r,d;
int i;
GB_POINT mv;

	if ( mp == 0 )
		return;
	switch ( mp->map.param_reverse.type ) {
	case 0:
		break;
	case MT_MOVE:
		mv = mp->map.param_reverse.opt.ptr;
		if ( mv.x == 0 && mv.y == 0 ) {
			break;
		}
		for ( i = 0 ; i < size ; i ++ ) {
			if ( reso[i] < 0 )
				continue;
			p[i].x += mv.x;
			p[i].y += mv.y;
		}
		break;
	case MT_ROTATE:
	case MT_LINEAR:
		for ( i = 0 ; i < size ; i ++ ) {
			if ( reso[i] < 0 )
				continue;
			p[i] = caffen2d(
				&mp->map.param_reverse.opt.affen,
				p[i]);
			reso[i] *= mp->
				map.param_reverse.
				resolution_rate;
		}
		break;
	case MT_TRIANGLE:
		lock_task(map_lock);
		for ( i = 0 ; i < size ; i ++ ) {
			if ( reso[i] < 0 )
				continue;
			p[i] = _map_conv_triangle(&reso[i],
				&mp->map.param_reverse,p[i]);
		}
		unlock_task(map_lock,"map_conv_reverse");
		break;
	default:
		er_panic("map_conv_reverse(1)");
	}
}

void
map_conv_forward_ln(MAP_HISTORY * mh,GB_POINT * p,REAL1 * reso,int size)
{
int i;
REAL1 rr;
	rr = mh->d.ln.forward_reso_rate;
	for ( i = 0 ; i < size ; i ++ ) {
		if ( reso[i] < 0 )
			continue;
		p[i] = caffen2d(
			&mh->d.ln.forward,
			p[i]);
		reso[i] *= rr;
	}
}

void
map_conv_reverse_ln(MAP_HISTORY * mh,GB_POINT * p,REAL1 * reso,int size)
{
int i;
REAL1 rr;
	rr = mh->d.ln.reverse_reso_rate;
	for ( i = 0 ; i < size ; i ++ ) {
		if ( reso[i] < 0 )
			continue;
		p[i] = caffen2d(
			&mh->d.ln.reverse,
			p[i]);
		reso[i] *= rr;
	}
}

void
map_conv_reverse_pp(MAP_HISTORY * mh,GB_POINT * p,REAL1 * reso,int size)
{
int i;
GB_POINT pp;
GB_POINT3D pp3d;
double d,rrate;

	rrate = 1/mh->d.pp.forward_rate;
	for ( i = 0 ; i < size ; i ++ , p ++ , reso ++ ) {
		if ( *reso < 0 )
			continue;
		pp = *p;
		pp = caffen2d(
			&mh->d.pp.reverse_sur,
			pp);
		d = pp.x * pp.x + pp.y * pp.y;

		if ( d > 1 ) {
			*reso = -1;
			continue;
		}
		pp3d.x = pp.x;
		pp3d.y = pp.y;
		pp3d.z = sqrt(1 - d);
		*reso = (*reso)*pp3d.z;
		pp3d = mp_mul3d(mh->d.pp.reverse_vol,pp3d);
		if ( pp3d.z || pp3d.x ) {
			if ( pp3d.z > pp3d.x ) {
				if ( pp3d.z > - pp3d.x ) {
					/* 1 */
					pp.x = atan( pp3d.x / pp3d.z );
				}
				else {
					/* 2 */
					pp.x = - M_PI/2
						- atan( pp3d.z / pp3d.x );
				}
			}
			else {
				if ( pp3d.z > - pp3d.x ) {
					/* 3 */
					pp.x = M_PI/2
						- atan( pp3d.z / pp3d.x );
				}
				else if ( pp3d.x > 0 ) {
					/* 4 */
					pp.x = M_PI
						+ atan( pp3d.x / pp3d.z );
				}
				else {
					/* 4' */
					pp.x = -M_PI
						+ atan( pp3d.x / pp3d.z );
				}
			}
		}
		else	pp.x = 0;
		pp.y = asin(-pp3d.y);
		pp.x = pp.x * rrate + mh->d.pp.lambda;
		pp.y = pp.y * rrate;
		*p = pp;
	}
}

void
map_conv_forward_pp(MAP_HISTORY * mh,GB_POINT * p,REAL1 * reso,int size)
{
int i;
GB_POINT pp;
GB_POINT3D pp3d;
double d,frate;

	frate = mh->d.pp.forward_rate;
 
	for ( i = 0 ; i < size ; i ++ , p ++ , reso ++ ) {
		if ( *reso < 0 )
			continue;
		pp = *p;
		pp.x = (pp.x  - mh->d.pp.lambda) * frate;
		pp.y = pp.y * frate;
		pp3d.y = - sin(pp.y);
		d = cos(pp.y);
		pp3d.z = d*cos(pp.x);
		pp3d.x = d*sin(pp.x);
		pp3d = mp_mul3d(mh->d.pp.forward_vol,pp3d);
		if ( pp3d.z < 0 ) {
			*reso = -1;
			continue;
		}
		*reso = (*reso)/pp3d.z;
		pp.x = pp3d.x;
		pp.y = pp3d.y;
		pp = caffen2d(&mh->d.pp.forward_sur,pp);
		*p = pp;
	}
}


void
map_conv_forward(RESOURCE * mp,GB_POINT * p,REAL1 * reso,int size)
{
REAL1 dummy;
int i;
GB_POINT mv;

	if ( mp == 0 )
		return; 
	switch ( mp->map.param_forward.type ) {
	case 0:
		break;
	case MT_MOVE:
		mv = mp->map.param_forward.opt.ptr;
		if ( mv.x == 0 && mv.y == 0 ) {
			break;
		}
		for ( i = 0 ; i < size ; i ++ ) {
			if ( reso[i] < 0 )
				continue;
			p[i].x += mv.x;
			p[i].y += mv.y;
		}
		break;
	case MT_ROTATE:
	case MT_LINEAR:
		for ( i = 0 ; i < size ; i ++ ) {
		  if ( reso[i] < 0 )
				continue;
			p[i] = caffen2d(
				&mp->map.param_forward.opt.affen,
				p[i]);
			reso[i] *= mp->
				map.param_forward.
				resolution_rate;
		}
		break;
	case MT_TRIANGLE:
		lock_task(map_lock);
		for ( i = 0 ; i < size ; i ++ ) {
			if ( reso[i] < 0 )
				continue;
			p[i] = _map_conv_triangle(&reso[i],
				&mp->map.param_forward,p[i]);
		}
		unlock_task(map_lock,"map_conv");
		break;
	default:
		er_panic("map_conv_forward(1)");
	}
}

void
map_from_flame(MAP_HISTORY * mh,GB_POINT * ptr,REAL1 * reso,int size)
{
	for ( ; mh ; mh = mh->next ) {
		switch ( mh->type ) {
		case MHT_MAP:
			switch ( mh->dir ) {
			case MHD_FORWARD:
				map_conv_forward(mh->d.map,ptr,reso,size);
				break;
			case MHD_REVERSE:
				map_conv_reverse(mh->d.map,ptr,reso,size);
			}
			break;
		case MHT_LINEAR:
			switch ( mh->dir ) {
			case MHD_FORWARD:
				map_conv_forward_ln(mh,ptr,reso,size);
				break;
			case MHD_REVERSE:
				map_conv_reverse_ln(mh,ptr,reso,size);
			}
			break;
		case MHT_PP:
			switch ( mh->dir ) {
			case MHD_FORWARD:
				map_conv_forward_pp(mh,ptr,reso,size);
				break;
			case MHD_REVERSE:
				map_conv_reverse_pp(mh,ptr,reso,size);
			}
			break;
		default:
			er_panic("map_from_flame(1)");
		}
	}
}



void
map_from_resource(MAP_HISTORY * mh,GB_POINT * ptr,REAL1 * reso,int size)
{

	mh->prev = 0;
	for ( ; mh && mh->next ; mh = mh->next )
		mh->next->prev = mh;
	for ( ; mh ; mh = mh->prev ) {
		switch ( mh->type ) {
		case MHT_MAP:
			switch ( mh->dir ) {
			case MHD_FORWARD:
				map_conv_reverse(mh->d.map,ptr,reso,size);
				break;
			case MHD_REVERSE:
				map_conv_forward(mh->d.map,ptr,reso,size);
			}
			break;
		case MHT_LINEAR:
			switch ( mh->dir ) {
			case MHD_FORWARD:
				map_conv_reverse_ln(mh,ptr,reso,size);
				break;
			case MHD_REVERSE:
				map_conv_forward_ln(mh,ptr,reso,size);
			}
			break;
		case MHT_PP:
			switch ( mh->dir ) {
			case MHD_FORWARD:
				map_conv_reverse_pp(mh,ptr,reso,size);
				break;
			case MHD_REVERSE:
				map_conv_forward_pp(mh,ptr,reso,size);
			}
			break;
		default:
			er_panic("map_from_resource(1)");
		}
	}

}

REAL1
convert_resolution_reverse(
	REAL1		reso,
	GB_POINT	pt,
	RESOURCE *	m)
{
GB_POINT tmp;
REAL1 dummy;

	if ( reso <= 0 )
		return reso;
	switch ( m->map.param_reverse.type ) {
	case 0:
		return reso;
	case MT_MOVE:
		return reso;
	case MT_ROTATE:
		return reso/puseudo_rate
			(m->map.param_reverse.opt.affen.matrix);
	case MT_TRIANGLE:
		map_conv_triangle(&reso,
			&m->map.param_reverse,
			pt);
		return reso;
	default:
		printf("type = %i\n",m->map.param_reverse.type);
		printf("name = %s\n",
			n_string(std_cm,get_url_str(&m->h.entry)));
		er_panic("convert_resolution_reverse(1)");
	}
	return 0;
}

REAL1
convert_resolution_forward(
	REAL1		reso,
	GB_POINT	pt,
	RESOURCE *	m)
{
GB_POINT tmp;
REAL1 dummy;

	if ( reso <= 0 )
		return reso;
	switch ( m->map.param_forward.type ) {
	case 0:
		return reso;
	case MT_MOVE:
		return reso;
	case MT_ROTATE:
		return reso/puseudo_rate
			(m->map.param_forward.opt.affen.matrix);
	case MT_TRIANGLE:
		map_conv_triangle(&reso,
			&m->map.param_forward,
			pt);
		return reso;
	default:
		printf("type = %i\n",m->map.param_forward.type);
		printf("name = %s\n",
			n_string(std_cm,get_url_str(&m->h.entry)));
		er_panic("convert_resolution_forward(1)");
	}
	return 0;
}


void
change_mh_memtype(MAP_HISTORY * mh,int no)
{
	for ( ; mh ; mh = mh->next )
		change_memtype(mh,no);
}

MAP_HISTORY *
add_mh(MAP_HISTORY * mh1,MAP_HISTORY * mh2)
{
MAP_HISTORY * mh3,* mh4,* ret;
	mh3 = 0;
	for ( ; mh1 ; mh1 = mh1->next ) {
		mh4 = d_alloc(sizeof(*mh4),1660);
		*mh4 = *mh1;
		mh4->next = mh3;
		mh3 = mh4;
	}
	for ( ; mh2 ; mh2 = mh2->next ) {
		mh4 = d_alloc(sizeof(*mh4),53);
		*mh4 = *mh2;
		mh4->next = mh3;
		mh3 = mh4;
	}
	ret = 0;
	for ( ; mh3 ; ) {
		mh4 = mh3;
		mh3 = mh3->next;
		mh4->next = ret;
		ret = mh4;
	}
	return ret;
}

MAP_HISTORY *
rev_mh(MAP_HISTORY * mh)
{
MAP_HISTORY * ret,* m1;
	ret = 0;
	for ( ; mh ; mh = mh->next ) {
		m1 = d_alloc(sizeof(*m1),54);
		*m1 = *mh;
		m1->dir = 1-m1->dir;
		m1->next = ret;
		ret = m1;
	}
	return ret;
}

MAP_HISTORY *
copy_mh(MAP_HISTORY * mh)
{
MAP_HISTORY * ret1,* ret2, * m1;
	ret1 = 0;
	for ( ; mh ; mh = mh->next ) {
		m1 = d_alloc(sizeof(*m1),55);
		*m1 = *mh;
		m1->next = ret1;
		ret1 = m1;
	}
	ret2 = 0;
	for ( ; ret1 ;) {
		m1 = ret1;
		ret1 = ret1->next;
		m1->next = ret2;
		ret2 = m1;
	}
	return ret2;
}

void
free_mh(MAP_HISTORY * mh)
{
MAP_HISTORY * mh1;
	for ( ; mh ; ) {
		mh1 = mh;
		mh = mh->next;
		d_f_ree(mh1);
	}
}

MAP_HISTORY*
new_mh(RESOURCE * m,short dir)
{
MAP_HISTORY * ret;
if ( m == 0 )
printf("***** new_mh\n");
	ret = d_alloc(sizeof(*ret),56);
	ret->dir = dir;
	ret->type = MHT_MAP;
	ret->d.map = m;
	ret->next = 0;
	return ret;
}


MAP_HISTORY *
cons_mh(RESOURCE * m,short dir,MAP_HISTORY * mh)
{
MAP_HISTORY * ret;
	ret = new_mh(m,dir);
	ret->next = mh;
	return ret;
}


void
adjust_lambda(L_CHAR * unit,REAL1 min_point,
	GB_POINT * pt_list,REAL1 * reso,int size)
{
REAL1 loop,max_point,x,y;
REAL1 vertical1,vertical2,vertical3;
int i;
int sign;

	if ( l_strcmp(unit,l_string(std_cm,"rad")) == 0 )
		loop = 2*M_PI;
	else if ( l_strcmp(unit,l_string(std_cm,"degree")) == 0 )
		loop = 360;
	else if ( l_strcmp(unit,l_string(std_cm,"'")) == 0 )
		loop = 360*60;
	else if ( l_strcmp(unit,l_string(std_cm,"''")) == 0 )
		loop = 360*60*60;
	else	return;
	vertical1 = loop/4;
	vertical2 = loop/2;
	vertical3 = loop*3/4;
	max_point = min_point + loop;
	for ( i = 0; i< size ; i ++ , pt_list ++ , reso ++ ) {
		if ( *reso < 0 )
			continue;
		x = pt_list->x;
		y = pt_list->y;
		if ( y > 0 ) {
			for ( ; y > loop ; y -= loop );
			if ( y <= vertical1 ) {
			}
			else if ( y <= vertical3 ) {
				x += vertical2;
				y = vertical2 - y;
			}
			else {
				y = y - loop;
			}
		}
		else {
			for ( ; y < -loop ; y += loop );
			if ( y >= -vertical1 ) {
			}
			else if ( y >= -vertical3 ) {
				x += vertical2;
				y = -vertical2 - y;
			}
			else {
				y = loop + y;
			}
		}
		for ( ; x < min_point ; x += loop );
		for ( ; x > max_point ; x -= loop );
		pt_list->x = x;
		pt_list->y = y;
	}
}


void
set_surp(SURP_SET * surp,GB_RECT * r,REAL1 reso)
{
GB_POINT * ptr;
GB_POINT center;
int i,j;
	ptr = surp->ptr;
	ptr[0] = r->tl;
	ptr[4] = r->br;
	ptr[2].x = r->tl.x;
	ptr[2].y = r->br.y;
	ptr[6].x = r->br.x;
	ptr[6].y = r->tl.y;

	ptr[1] = p_avg(ptr[0],ptr[2]);
	ptr[3] = p_avg(ptr[2],ptr[4]);
	ptr[5] = p_avg(ptr[4],ptr[6]);
	ptr[7] = p_avg(ptr[6],ptr[0]);

	center = ptr[SURP_NOS-1] = p_avg(r->tl,r->br);

	for ( i = 1 ; i < SURP_DEPTH ; i ++ ) {
		for ( j = 0 ; j < 8 ; j ++ ) {
			ptr[8*i+j].x = 
				(i * ptr[j].x + (SURP_DEPTH-i) * center.x)
				/SURP_DEPTH;
			ptr[8*i+j].y =
				(i + ptr[j].y + (SURP_DEPTH-i) * center.y)
				/SURP_DEPTH;
		}
	}

	for ( i = 0 ; i < SURP_NOS ; i ++ )
		surp->reso[i] = reso;
}

void
print_surp(SURP_SET * surp)
{
int i;
	printf("SP");
	for ( i = 0 ; i < SURP_NOS ; i ++ ) {
		if ( surp->reso[i] < 0 )
			continue;
		printf("([%i]:%f %f - %f)",
			i,
			surp->ptr[i].x,
			surp->ptr[i].y,
			surp->reso[i]);
	}
	printf("\n");
}


GB_RECT
get_surp_rect(SURP_SET * surp,RESOURCE * r)
{
GB_RECT ret;
int i;


	if ( r )
		adjust_lambda(
			r->h.cu.unit,
			r->h.minrect.tl.x,
			surp->ptr,
			surp->reso,
			SURP_NOS);

	ret.tl.x = ret.tl.y = 0;
	ret.br.x = ret.br.y = -1;
	for ( i = 0 ; i < SURP_NOS ; i ++ ) {
		if ( surp->reso[i] < 0 )
			continue;
		insert_rect(&ret,surp->ptr[i]);
	}
	return ret;
}

GB_RECT
get_surp_rect_rate(SURP_SET * surp,REAL1 rate,RESOURCE * r)
{
GB_RECT ret;
REAL1 w,h;
REAL1 x_ofs,y_ofs;
	ret = get_surp_rect(surp,r);
	w = ret.br.x - ret.tl.x;
	h = ret.br.y - ret.tl.y;
	x_ofs = w*rate;
	y_ofs = h*rate;
	ret.br.x += x_ofs;
	ret.br.y += y_ofs;
	ret.tl.x -= x_ofs;
	ret.tl.y -= y_ofs;
	return ret;
}


REAL1
get_surp_reso(SURP_SET * surp)
{
REAL1 ret;
int i;
	ret = 0;
	for ( i = 0 ; i < SURP_NOS ; i ++ )
		ret += surp->reso[i];
	return ret/SURP_NOS;
}


int
opt_mh_affen2d(AFFEN2D * a)
{
	if ( a->matrix[0][0] != 1 )
		return -1;
	if ( a->matrix[0][1] != 0 )
		return -1;
	if ( a->matrix[1][0] != 0 )
		return -1;
	if ( a->matrix[1][1] != 1 )
		return -1;
	if ( a->org.x != 0 )
		return -1;
	if ( a->org.y != 0 )
		return -1;
	return 0;
}

int
opt_mh_i_map(RESOURCE * r)
{
GB_POINT mv;
	switch ( r->map.param_forward.type ) {
	case MT_MOVE:
		mv = r->map.param_forward.opt.ptr;
		if ( mv.x == 0 && mv.y == 0 )
			return 0;
		return -1;
	case MT_ROTATE:
	case MT_LINEAR:
		return opt_mh_affen2d(&r->map.param_forward.opt.affen);
	default:
		return -1;
	}
}

MAP_HISTORY *
optimize_mh_identical(MAP_HISTORY * mh)
{
MAP_HISTORY ** mhp, * mh2;
RESOURCE * r;
	mhp = &mh;
	for ( ; *mhp ; ) {
		mh2 = *mhp;
		switch ( mh2->type ) {
		case MHT_MAP:
			if ( opt_mh_i_map(mh2->d.map) == 0 )
				goto ident;
			goto no_ident;
		case MHT_LINEAR:
			if ( opt_mh_affen2d(&mh2->d.ln.forward) == 0 )
				goto ident;
			goto no_ident;
		default:
			break;
		}
	no_ident:
		mhp = &mh2->next;
		continue;
	ident:
		*mhp = mh2->next;
		mh2->next = 0;
		free_mh(mh2);
	}
	return mh;
}

void
set_me(REAL1 m[2][2])
{
	m[0][0] = m[1][1] = 1;
	m[0][1] = m[1][0] = 0;
}

void
copy_ln_map2mh(MAP_HISTORY * mh)
{
RESOURCE * r;
	for ( ; mh ; mh = mh->next ) {
		if ( mh->type != MHT_MAP )
			continue;
		r = mh->d.map;
		switch ( r->map.param_forward.type ) {
		case MT_MOVE:
			mh->type = MHT_LINEAR;
			set_me(mh->d.ln.forward.matrix);
			mh->d.ln.forward.org = r->map.param_forward.opt.ptr;
			set_me(mh->d.ln.reverse.matrix);
			mh->d.ln.reverse.org = r->map.param_reverse.opt.ptr;
			mh->d.ln.forward_reso_rate =
				mh->d.ln.reverse_reso_rate = 1;
			break;
		case MT_ROTATE:
		case MT_LINEAR:
			mh->type = MHT_LINEAR;
			mh->d.ln.forward = r->map.param_forward.opt.affen;
			mh->d.ln.reverse = r->map.param_reverse.opt.affen;
			mh->d.ln.forward_reso_rate
				= r->map.param_forward.resolution_rate;
			mh->d.ln.reverse_reso_rate
				= r->map.param_reverse.resolution_rate;
			break;
		default:
			break;
		}
	}
}

MAP_HISTORY *
optimize_mh_linear(MAP_HISTORY * mh)
{
MAP_HISTORY * mh1,* mh2;
AFFEN2D a;
	copy_ln_map2mh(mh);
	for ( mh1 = mh ; mh1 ; ) {
		if ( mh1->next == 0 )
			break;
		if ( mh1->type != MHT_LINEAR ) {
			mh1 = mh1->next;
			continue;
		}
		mh2 = mh1->next;
		if ( mh2->type != MHT_LINEAR ) {
			mh1 = mh1->next;
			continue;
		}
		if ( mh1->dir == MHD_REVERSE ) {
			a = mh1->d.ln.forward;
			mh1->d.ln.forward = mh1->d.ln.reverse;
			mh1->d.ln.reverse = a;
			mh1->dir = MHD_FORWARD;
		}
		if ( mh2->dir == MHD_REVERSE ) {
			conv_affen2d(&a,&mh2->d.ln.reverse,&mh1->d.ln.forward);
			mh1->d.ln.forward = a;
			mh1->d.ln.forward_reso_rate
				= mh2->d.ln.reverse_reso_rate *
				  mh1->d.ln.forward_reso_rate;
			conv_affen2d(&a,&mh1->d.ln.reverse,&mh2->d.ln.forward);
			mh1->d.ln.reverse = a;
			mh1->d.ln.reverse_reso_rate
				= mh1->d.ln.reverse_reso_rate *
				  mh2->d.ln.forward_reso_rate;
		}
		else {
			conv_affen2d(&a,&mh2->d.ln.forward,&mh1->d.ln.forward);
			mh1->d.ln.forward = a;
			mh1->d.ln.forward_reso_rate
				= mh2->d.ln.forward_reso_rate *
				  mh1->d.ln.forward_reso_rate;
			conv_affen2d(&a,&mh1->d.ln.reverse,&mh2->d.ln.reverse);
			mh1->d.ln.reverse = a;
			mh1->d.ln.reverse_reso_rate
				= mh1->d.ln.reverse_reso_rate *
				  mh2->d.ln.reverse_reso_rate;
		}
		mh1->next = mh2->next;
		mh2->next = 0;
		free_mh(mh2);
	}
	return mh;
}

MAP_HISTORY *
optimize_mh(MAP_HISTORY * mh)
{
	mh = optimize_mh_identical(mh);
	mh = optimize_mh_linear(mh);
	return mh;
}
