##############################################
#
# Given 2D data, make a mesh.
#
# This is only for a set of rectangular meshes.
#
# Use PDL::Graphics::TriD::Surface for more general stuff.
#
# I try to make this general enough that all Surface methods
# work on this data also.
#

# The different types of normals are a headache.
# If we have a normal per vertex, we get smoothing.
# If we want flat shading, ... need normal / square or triangle.

# But: if we have
# normal(3, polygons, vertices, @overdims),
# we could possibly map it for both cases.
# Flat:   normal(3,polygon, (),     @)
# Smooth: normal(3,(),      vertex, @)

package PDL::Graphics::TriD::Mesh;

BEGIN {
   use PDL::Config;
   if ( $PDL::Config{USE_POGL} ) {
      eval "use OpenGL $PDL::Config{POGL_VERSION} qw(:all)";
      eval 'use PDL::Graphics::OpenGL::Perl::OpenGL';
   } else {
      eval 'use PDL::Graphics::OpenGL';
   }
}

use PDL::LiteF;
@ISA=qw/PDL::Graphics::TriD::Object/;

# For now, x and y coordinates = values.

sub new {
	my($type,$data,$xaxis,$yaxis) = @_;
	my @dims = $data->dims;
	my @xydims = splice @dims,0,2;
	my @overdims = @dims;
	my $this = {
		Vertices => PDL->zeroes(3,@xydims,@overdims)->double,
		XYDims => [@xydims],
		OverDims => [@overdims],
		Data => $data,
	};
	PDL::Primitive::axisvalues($this->{Vertices}->slice('(0),:,:'));
	PDL::Primitive::axisvalues($this->{Vertices}->slice('(1),:,:')->xchg(0,1));
	PDL::Ops::assgn($this->{Data},$this->{Vertices}->slice('(2),:,:'));
	bless $this,$type;
}

sub printdims {print $_[0].": ".(join ', ',$_[1]->dims)," and ",
		(join ', ',$_[1]->threadids),"\n"}

sub get_boundingbox {
	my($this) = @_;
	my $foo = PDL->zeroes(6)->double;
	$a = $this->{Vertices}; printdims "A",$a;
	$b = $a->thread(0); printdims "B",$b;
	$c = $b->clump(-1); printdims "C",$c;
	$d = $c->unthread(1); printdims "D",$d;

	$this->{Vertices}->thread(0);

	PDL::Primitive::minimum($this->{Vertices}->thread(0)->clump(-1)->unthread(1),
		 $foo->slice('0:2'));
	PDL::Primitive::maximum($this->{Vertices}->thread(0)->clump(-1)->unthread(1),
		 $foo->slice('3:5'));
	print "MeshBound: ",(join ',',$foo->list()),"\n";
	return PDL::Graphics::TriD::BoundingBox->new( $foo->list() );
}

sub normals_flat {
	my($this) = @_;
	$this->_allocnormalspoly();
	my $v00 = $this->{Vertices}->slice('(2),0:-2,0:-2');
	my $v01 = $this->{Vertices}->slice('(2),0:-2,1:-1');
	my $v10 = $this->{Vertices}->slice('(2),1:-1,0:-2');
	my $v11 = $this->{Vertices}->slice('(2),1:-1,1:-1');
#	$this->{Normals}->printdims("NORMALS");
	my $nx = $this->{Normals}->slice('(0),:,:,(0),(0)');
#	$nx->printdims("NX"); $v00->printdims("V0");
	$nx *= PDL->pdl(0);
	$nx += $v11; $nx -= $v01; $nx += $v10; $nx -= $v00;
	$nx *= PDL->pdl(-0.5);
	my $ny = $this->{Normals}->slice('(1),:,:,(0),(0)');
	$ny *= PDL->pdl(0);
	$ny += $v11; $ny -= $v10; $ny += $v01; $ny -= $v00;
	$ny *= PDL->pdl(-0.5);
	my $nz = $this->{Normals}->slice('(2),:,:,(0),(0)');
	$nz .= PDL->pdl(1);
	print $this->{Vertices};
	print $this->{Normals};
	print $nx,$ny,$nz;
}

sub _allocnormalspoly {
	my($this) = @_;
	$this->{Normals} = (PDL->zeroes(3, (map {$_-1} @{$this->{XYDims}}),
						@{$this->{OverDims}})->double )
			-> dummy(3,$this->{XYDims}[1])
			-> dummy(3,$this->{XYDims}[0]);
}

sub _allocnormalsvertex {
	my($this) = @_;
	$this->{Normals} = (double zeroes(3, (@{$this->{XYDims}}),
						@{$this->{OverDims}}))
			-> dummy(1,$this->{XYDims}[1]-1)
			-> dummy(1,$this->{XYDims}[0]-1);
}

# Right now, I assume the flat model.
sub togl {
	my($this) = @_;
	my ($x,$y);
#	forextradims ([$this->{Vertices},_],
	for $x (0..$this->{XYDims}[0]-2) {
		for $y (0..$this->{XYDims}[1]-2) {
			my ($x1,$y1) = ($x+1,$y+1);
			glBegin(GL_TRIANGLE_STRIP);
			print "ONESTRIP\n", (join '',
				$this->{Normals}->slice(":,($x),($y),($x),($y)")
				),"\n";
			glNormal3d(
				$this->{Normals}->slice(":,($x),($y),($x),($y)")
					->list());
#			print "VERTEX0: ",(join ',',
#				$this->{Vertices}->slice(":,($x),($y)")->list()),
#				"\n";
			glVertex3d($this->{Vertices}->slice(":,($x),($y)")->list());
			glVertex3d($this->{Vertices}->slice(":,($x1),($y)")->list());
			glVertex3d($this->{Vertices}->slice(":,($x),($y1)")->list());
			glVertex3d($this->{Vertices}->slice(":,($x1),($y1)")->list());
			glEnd();
# Show normal
#			glBegin(&GL_LINES);
#			glVertex3d($this->{Vertices}->slice(":,($x),($y)")->list());
#			glVertex3d(
#			  ($this->{Vertices}->slice(":,($x),($y)") +
#				$this->{Normals}->slice(":,($x),($y),($x),($y)"))
#				 ->list());
#			glEnd();
		}
	}
}

package PDL::Graphics::TriD;
#use PDL::Graphics::OpenGL;
use PDL::Graphics::OpenGL::Perl::OpenGL;
use PDL::Core '';

sub pdltotrianglemesh {
	my($pdl,$x0,$x1,$y0,$y1) = @_;
	if($#{$pdl->{Dims}} != 1) { barf "Too many dimensions for PDL::GL::Mesh: $#{$pdl->{Dims}}  \n"; }
	my ($d0,$d1); my($x,$y);
	$xincr = ($x1 - $x0) / ($pdl->{Dims}[0]-1.0);
	$yincr = ($y1 - $y0) / ($pdl->{Dims}[1]-1.0);
	$x = $x0;
	my($v00,$v01,$v11,$v10);
	my($nx,$ny);
	for $d0 (0..$pdl->{Dims}[0]-2) {
		$y = $y0;
		for $d1 (0..$pdl->{Dims}[1]-2) {
			glBegin(GL_TRIANGLE_STRIP);
			($v00,$v01,$v11,$v10) =
			  (PDL::Core::at($pdl,$d0,$d1)
			  ,PDL::Core::at($pdl,$d0,$d1+1)
			  ,PDL::Core::at($pdl,$d0+1,$d1+1)
			  ,PDL::Core::at($pdl,$d0+1,$d1));

			($nx,$ny) = (-0.5*($v11+$v10-$v01-$v00)/$xincr,
			          -0.5*($v11-$v10+$v01-$v00)/$yincr);
			glNormal3d($nx,$ny,1);
			glVertex3d($x,$y,$v00);
			glVertex3d($x+$xincr,$y,$v10);
			glVertex3d($x,$y+$yincr,$v01);
			glVertex3d($x+$xincr,$y+$yincr,$v11);
			glEnd();
			if(0) {
				glBegin(GL_LINES);
				glVertex3d($x,$y,$v00);
				glVertex3d($x+$nx/10,$y+$ny/10,$v00+1/10);
				glEnd();
			}
			$y += $yincr;
		}
		$x += $xincr;
	}
}

sub pdl2normalizedmeshlist {
	my($pdl) = @_;
	my $mult = 1.0/($pdl->{Dims}[0]-1);
	my $lno = glGenLists(1);
	glNewList($lno,GL_COMPILE);
	pdltotrianglemesh($pdl, 0, 1, 0, ($pdl->{Dims}[1]-1)*$mult);
	glEndList();
	return $lno;
}


1;
