<?xml version="1.0" encoding="windows-1252"?>
<node id="210664" title="help with Z-buffer algorithm" created="2002-11-06 00:42:52" updated="2005-07-21 01:01:22">
<type id="115">
perlquestion</type>
<author id="131128">
rbc</author>
<data>
<field name="doctext">
Dear Monks,&lt;BR&gt;
&lt;BR&gt;
I almost have a z-buffer algorithm working for some 3D&lt;BR&gt;
graphics I am doing.  But if you run the below script&lt;BR&gt;
you can see I don't have quite worked out correctly.&lt;BR&gt;
&lt;BR&gt;
Please help!  I know it looks like a lot of code but&lt;BR&gt;
I suspect the problem is in the &lt;b&gt;sub fillZbuff&lt;/b&gt; or&lt;BR&gt;
&lt;b&gt;sub buildET4Zbuff&lt;/b&gt; or the &lt;b&gt;sub ProjectLine&lt;/b&gt;&lt;BR&gt;

&lt;readmore&gt;
&lt;code&gt;
#!/usr/bin/perl -w

use strict;
use Tk;
use Tk::Canvas;
use Getopt::Std;
use Math::Trig;
use Data::Dumper;
use Math::Round qw( nearest_floor nearest_ceil );

my $width = 500;
my $height = 500;
my $screenDist = 1000;
my $background = 'blue';
my $fill = 'yellow';
my $zmin = -10;
my %opts = ();
getopts( 'w:h:b:f:z:', \%opts );
if( $opts{w} ) { $width = $opts{w} ; }
if( $opts{h} ) { $height = $opts{h} ; }
if( $opts{b} ) { $background = $opts{b} ; }
if( $opts{f} ) { $fill = $opts{f} ; }
if( $opts{z} ) { $zmin = $opts{z} ; }


my $pidiv180 = atan(1)/45;
my $top 	= MainWindow-&gt;new();
my $frame 	= $top-&gt;Frame();
my $can = $top-&gt;Canvas( -width =&gt; $width, -height=&gt; $height, -background=&gt;$background );

#my $rho = -1;  
my $rho = 0;  
my $theta = 90;  
my $phi = 0.0; 
my $rotateZ = 0.0;
my $rotateX = 0.0;
my $rotateY = 30.0;
my $TZ = 15;
my $TX = 0;
my $TY = 0;
my $N = 1;

my $colorSide_A = 240;
my $colorSide_B = 3333; # kind of red

my $x_center;
my $y_center;
my $YMAX;
my $per;
my $clipVol;

my $y_max = 1;
my $y_min = -1;
my @ZBUFFER;

sub clear{ $can-&gt;delete( 'all' ); }
sub doit {
	$x_center = $can-&gt;reqwidth()/2.0;
	$y_center = $can-&gt;reqheight()/2.0;
	$YMAX = $can-&gt;reqheight();
	$per = new Perspective( $rho, $theta*$pidiv180, $phi*$pidiv180 );
	$clipVol = new Clip3D( $zmin );

	for( my $i=0; $i&lt;$can-&gt;reqwidth(); $i++ ) {
		for( my $j=0; $j&lt;$can-&gt;reqheight(); $j++ ) {
			$ZBUFFER[$i][$j] = 9999999;
		}
	}

	my @cube = (
		new Vector3D(  1, -1, -1 ), # 0 A
		new Vector3D(  1,  1, -1 ), # 1 B
		new Vector3D( -1,  1, -1 ), # 2 C
		new Vector3D( -1, -1, -1 ), # 3 D
		new Vector3D(  1, -1,  1 ), # 4 E
		new Vector3D(  1,  1,  1 ), # 5 F
		new Vector3D( -1,  1,  1 ), # 6 G
		new Vector3D( -1, -1,  1 )  # 7 H
	);

	my @negative_y_axis = (
		new Vector3D(  0,    0, 0.1 ), # 0 
		new Vector3D(  0,  -10, 0.1 ), # 1 
	);
	my @positive_y_axis = (
		new Vector3D(  0,    0, 0.1 ), # 0 
		new Vector3D(  0,   10, 0.1 ), # 1 
	);
	my @negative_x_axis = (
		new Vector3D(    0,  0, 0.1 ), # 0 
		new Vector3D(  -10,  0, 0.1 ), # 1 
	);
	my @positive_x_axis = (
		new Vector3D(   0,   0, 0.1 ), # 0 
		new Vector3D(  10,   0, 0.1 ), # 1 
	);

	for( my $i=0; $i&lt;$N; $i++ ) {
		foreach my $v ( @cube ) { $v-&gt;rotateZ( $rotateZ * $pidiv180 ); }
		foreach my $v ( @cube ) { $v-&gt;rotateX( $rotateX * $pidiv180 ); }
		foreach my $v ( @cube ) { $v-&gt;rotateY( $rotateY * $pidiv180 ); }
		foreach my $v ( @cube ) { $v-&gt;translate( new Vector3D($TX, $TY, $TZ) ); }

		my @zero = (
			new Vector3D(  $cube[0]-&gt;getx()	+ 0.05, $cube[0]-&gt;gety()      , $cube[0]-&gt;getz() ), # 0 
			new Vector3D(  $cube[0]-&gt;getx() + 0.1, $cube[0]-&gt;gety()      , $cube[0]-&gt;getz() ), # 1 
			new Vector3D(  $cube[0]-&gt;getx() + 0.1, $cube[0]-&gt;gety() + 0.1, $cube[0]-&gt;getz() ), # 1 
			new Vector3D(  $cube[0]-&gt;getx() + 0.05, $cube[0]-&gt;gety() + 0.1, $cube[0]-&gt;getz() ), # 1 
			new Vector3D(  $cube[0]-&gt;getx() + 0.05, $cube[0]-&gt;gety()      , $cube[0]-&gt;getz() ), # 0 
		);

		my @one = (
			new Vector3D(  $cube[1]-&gt;getx() + 0.1, $cube[1]-&gt;gety()      , $cube[1]-&gt;getz() ), # 1 
			new Vector3D(  $cube[1]-&gt;getx() + 0.1, $cube[1]-&gt;gety() + 0.1, $cube[1]-&gt;getz() ), # 1 
		);

		my @two = (
			new Vector3D(  $cube[2]-&gt;getx() - 0.10, $cube[2]-&gt;gety() + 0.10, $cube[2]-&gt;getz() ), # 1 
			new Vector3D(  $cube[2]-&gt;getx() - 0.05, $cube[2]-&gt;gety() + 0.10, $cube[2]-&gt;getz() ), # 1 
			new Vector3D(  $cube[2]-&gt;getx()	- 0.05, $cube[2]-&gt;gety() + 0.05, $cube[2]-&gt;getz() ), # 0 
			new Vector3D(  $cube[2]-&gt;getx()	- 0.10, $cube[2]-&gt;gety() + 0.05, $cube[2]-&gt;getz() ), # 0 
			new Vector3D(  $cube[2]-&gt;getx()	- 0.10, $cube[2]-&gt;gety()       , $cube[2]-&gt;getz() ), # 0 
			new Vector3D(  $cube[2]-&gt;getx() - 0.05, $cube[2]-&gt;gety()       , $cube[2]-&gt;getz() ), # 1 
		);

		my @three = (
			new Vector3D(  $cube[3]-&gt;getx() - 0.10, $cube[3]-&gt;gety() + 0.10, $cube[3]-&gt;getz() ), # 1 
			new Vector3D(  $cube[3]-&gt;getx() - 0.05, $cube[3]-&gt;gety() + 0.10, $cube[3]-&gt;getz() ), # 1 
			new Vector3D(  $cube[3]-&gt;getx()	- 0.05, $cube[3]-&gt;gety() + 0.05, $cube[3]-&gt;getz() ), # 0 
			new Vector3D(  $cube[3]-&gt;getx()	- 0.10, $cube[3]-&gt;gety() + 0.05, $cube[3]-&gt;getz() ), # 0 
			new Vector3D(  $cube[3]-&gt;getx()	- 0.05, $cube[3]-&gt;gety() + 0.05, $cube[3]-&gt;getz() ), # 0 
			new Vector3D(  $cube[3]-&gt;getx()	- 0.05, $cube[3]-&gt;gety()       , $cube[3]-&gt;getz() ), # 0 
			new Vector3D(  $cube[3]-&gt;getx() - 0.10, $cube[3]-&gt;gety()       , $cube[3]-&gt;getz() ), # 1 
		);

		my @four = (
			new Vector3D(  $cube[4]-&gt;getx() + 0.05, $cube[4]-&gt;gety() + 0.10, $cube[4]-&gt;getz() ), # 1 
			new Vector3D(  $cube[4]-&gt;getx()	+ 0.05, $cube[4]-&gt;gety() + 0.05, $cube[4]-&gt;getz() ), # 4 
			new Vector3D(  $cube[4]-&gt;getx()	+ 0.10, $cube[4]-&gt;gety() + 0.05, $cube[4]-&gt;getz() ), # 3 
			new Vector3D(  $cube[4]-&gt;getx() + 0.10, $cube[4]-&gt;gety() + 0.10, $cube[4]-&gt;getz() ), # 2 
			new Vector3D(  $cube[4]-&gt;getx()	+ 0.10, $cube[4]-&gt;gety()       , $cube[4]-&gt;getz() ), # 6 
		);

		my @five = (
			new Vector3D(  $cube[5]-&gt;getx() + 0.10, $cube[5]-&gt;gety() + 0.10, $cube[5]-&gt;getz() ), # 1 
			new Vector3D(  $cube[5]-&gt;getx() + 0.05, $cube[5]-&gt;gety() + 0.10, $cube[5]-&gt;getz() ), # 1 
			new Vector3D(  $cube[5]-&gt;getx()	+ 0.05, $cube[5]-&gt;gety() + 0.05, $cube[5]-&gt;getz() ), # 0 
			new Vector3D(  $cube[5]-&gt;getx()	+ 0.10, $cube[5]-&gt;gety() + 0.05, $cube[5]-&gt;getz() ), # 0 
			new Vector3D(  $cube[5]-&gt;getx()	+ 0.10, $cube[5]-&gt;gety()       , $cube[5]-&gt;getz() ), # 0 
			new Vector3D(  $cube[5]-&gt;getx() + 0.05, $cube[5]-&gt;gety()       , $cube[5]-&gt;getz() ), # 1 
		);

		my @six = (
			new Vector3D(  $cube[6]-&gt;getx() - 0.05, $cube[6]-&gt;gety() + 0.10, $cube[6]-&gt;getz() ), # 1 
			new Vector3D(  $cube[6]-&gt;getx() - 0.10, $cube[6]-&gt;gety() + 0.10, $cube[6]-&gt;getz() ), # 1 
			new Vector3D(  $cube[6]-&gt;getx()	- 0.10, $cube[6]-&gt;gety() + 0.05, $cube[6]-&gt;getz() ), # 0 
			new Vector3D(  $cube[6]-&gt;getx()	- 0.05, $cube[6]-&gt;gety() + 0.05, $cube[6]-&gt;getz() ), # 0 
			new Vector3D(  $cube[6]-&gt;getx()	- 0.05, $cube[6]-&gt;gety()       , $cube[6]-&gt;getz() ), # 0 
			new Vector3D(  $cube[6]-&gt;getx() - 0.10, $cube[6]-&gt;gety()       , $cube[6]-&gt;getz() ), # 1 
			new Vector3D(  $cube[6]-&gt;getx()	- 0.10, $cube[6]-&gt;gety() + 0.05, $cube[6]-&gt;getz() ), # 0 
		);

		my @seven = (
			new Vector3D(  $cube[7]-&gt;getx() - 0.10, $cube[7]-&gt;gety() + 0.10, $cube[7]-&gt;getz() ), # 1 
			new Vector3D(  $cube[7]-&gt;getx() - 0.05, $cube[7]-&gt;gety() + 0.10, $cube[7]-&gt;getz() ), # 1 
			new Vector3D(  $cube[7]-&gt;getx()	- 0.05, $cube[7]-&gt;gety()       , $cube[7]-&gt;getz() ), # 0 
		);

	
		DrawPolygon ( [ $cube[0], $cube[1], $cube[5], $cube[4], $cube[0] ], $colorSide_A ); #side A
		DrawPolygon ( [ $cube[4], $cube[5], $cube[6], $cube[7], $cube[4] ], 140 ); #side B
		DrawPolygon ( [ $cube[7], $cube[6], $cube[2], $cube[3], $cube[7] ], 901 ); #side C
		DrawPolygon ( [ $cube[6], $cube[2], $cube[1], $cube[5], $cube[6] ], 56 );  #side D
		DrawPolygon ( [ $cube[0], $cube[4], $cube[7], $cube[3], $cube[0] ], 591 ); #side E
		drawShape( \@zero );
		drawShape( \@one );
		drawShape( \@two );
		drawShape( \@three );
		drawShape( \@four );
		drawShape( \@five );
		drawShape( \@six );
		drawShape( \@seven );
#		drawShape( \@negative_y_axis, 'red' );
#		drawShape( \@positive_y_axis, 'white' );
#		drawShape( \@negative_x_axis, 'black' );
#		drawShape( \@positive_x_axis, 'white' );
	} # end for loop
}

sub drawShape { 
	my $shape = shift; # ref to array of vector3D
	my $color = shift || 'red';

	my @s = @{$shape};

	my $v1 = pop(@s);
	while (@s) {
		my $v2 = pop(@s);
		DrawLine( $v1, $v2, $color ); #, $per, $screenDist, $x_center, $y_center, $clipVol );
		$v1 = $v2;
	}
}

sub DrawPolygon {
	my $points = shift; # ref to array of Vector3Ds
	my $color  = shift;

	my @edges;
	my @pts = @{$points};
	my $curr = $pts[0];

	for ( my $i=1; $i&lt;=$#pts; $i++ ) {
		my $next = $pts[$i];
		my $ptPair = ProjectLine( $curr, $next );
		push( @edges, $ptPair );
		$curr = $next;
	}
	my $polygon = new PolygonZbuffer( \@edges, $can, $color );
	$polygon-&gt;fillZbuff(\@ZBUFFER);
}

sub ProjectLine {
	my $v1 = shift;
	my $v2 = shift;
	my $color = shift || $fill;

	my( $x1, $y1, $x2, $y2, $visible );

	my $X1 = $v1-&gt;getx();
	my $Y1 = $v1-&gt;gety();
	my $Z1 = $v1-&gt;getz();
	my $X2 = $v2-&gt;getx();
	my $Y2 = $v2-&gt;gety();
	my $Z2 = $v2-&gt;getz();
	$visible = $clipVol-&gt;Clip ( \$X1, \$Y1, \$Z1, \$X2, \$Y2, \$Z2);
	if ( $visible == 1 ) {
		my $V1 = new Vector3D( $X1, $Y1, $Z1 );
		my $V2 = new Vector3D( $X2, $Y2, $Z2 );
		$per-&gt;perspective( $V1, \$x1, \$y1 ); 
		$per-&gt;perspective( $V2, \$x2, \$y2 ); 

		my $A = new Vector2D ( 
			nearest_ceil( 1, $screenDist * $x1 + $x_center ), 
			nearest_floor( 1, $YMAX - ($screenDist * $y1 + $y_center))
		);
		my $B = new Vector2D ( 
			nearest_ceil( 1, $screenDist * $x2 + $x_center ), 
			nearest_floor( 1, $YMAX - ($screenDist * $y2 + $y_center))
		);
		#my @AB = ( $A, $B );
		my $rec = {
			#edge =&gt; @AB,
			edge =&gt; [$A, $B],
			dx   =&gt; $X1 - $X2,
			dz   =&gt; $Z1 - $Z2,
#			z    =&gt; $Z1
			z    =&gt; ( $X1 &lt; $X2 ) ? ($Z1) : ($Z2)
		};
		return $rec;
	}
	return undef;
}

sub DrawLine {
	my $v1 = shift;
	my $v2 = shift;
	my $color = shift || $fill;

	my( $x1, $y1, $x2, $y2, $visible );

	my $X1 = $v1-&gt;getx();
	my $Y1 = $v1-&gt;gety();
	my $Z1 = $v1-&gt;getz();
	my $X2 = $v2-&gt;getx();
	my $Y2 = $v2-&gt;gety();
	my $Z2 = $v2-&gt;getz();
	$visible = $clipVol-&gt;Clip ( \$X1, \$Y1, \$Z1, \$X2, \$Y2, \$Z2);
	if ( $visible == 1 ) {
		my $V1 = new Vector3D( $X1, $Y1, $Z1 );
		my $V2 = new Vector3D( $X2, $Y2, $Z2 );
		$per-&gt;perspective( $V1, \$x1, \$y1 ); 
		$per-&gt;perspective( $V2, \$x2, \$y2 ); 

		$can-&gt;create( 'line',
			$screenDist * $x1 + $x_center,
			$YMAX - ($screenDist * $y1 + $y_center),
			$screenDist * $x2 + $x_center,
			$YMAX - ($screenDist * $y2 + $y_center),
			-fill =&gt; $color
		);
		my $A = new Vector2D ( 
			nearest_ceil( 1, $screenDist * $x1 + $x_center ), 
			nearest_floor( 1, $YMAX - ($screenDist * $y1 + $y_center))
		);
		my $B = new Vector2D ( 
			nearest_ceil( 1, $screenDist * $x2 + $x_center ), 
			nearest_floor( 1, $YMAX - ($screenDist * $y2 + $y_center))
		);
		my @AB = ( $A, $B );
		return @AB;
	}
	return undef;
}


$can-&gt;packAdjust( -side =&gt; 'left', -fill =&gt; 'both', -delay =&gt; 1 );
$frame-&gt;pack( 
	-side 	=&gt; 'left', 
	-fill 	=&gt; 'y', 
	-expand =&gt; 'y', 
	-anchor =&gt; 'w' );

my $mbutton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "Viewing Dist rho = EO " )-&gt;pack( anchor =&gt; 'w' );
my $mentry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$rho)-&gt;form( 
	-left =&gt; [$mbutton,0] );

my $nbutton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "theta = ")-&gt;form( 
	-top =&gt; [$mbutton,0] );
my $nentry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$theta)-&gt;form( 
	-left =&gt; [$nbutton,0],
	-top =&gt; [$mbutton,0] );

my $phibutton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "phi = ", 
	)-&gt;form( -top =&gt; [$nbutton,0] );
my $phientry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$phi) -&gt;form( 
	-left =&gt; [$phibutton,0],
	-top =&gt; [$nbutton,0] );
my $rotZbutton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "rotZ = ", 
	)-&gt;form( -top =&gt; [$phibutton,0] );
my $rotZentry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$rotateZ) -&gt;form( 
	-left =&gt; [$rotZbutton,0],
	-top =&gt; [$phibutton,0] );
my $rotXbutton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "rotX = ", 
	)-&gt;form( -top =&gt; [$rotZbutton,0] );
my $rotXentry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$rotateX) -&gt;form( 
	-left =&gt; [$rotXbutton,0],
	-top =&gt; [$rotZbutton,0] );
my $rotYbutton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "rotY = ", 
	)-&gt;form( -top =&gt; [$rotXbutton,0] );
my $rotYentry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$rotateY) -&gt;form( 
	-left =&gt; [$rotYbutton,0],
	-top =&gt; [$rotXbutton,0] );
#####

my $TZbutton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "TX = ", 
	)-&gt;form( -top =&gt; [$rotYbutton,0] );
my $TZentry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$TX) -&gt;form( 
	-left =&gt; [$TZbutton,0],
	-top =&gt; [$rotYbutton,0] );
my $TXbutton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "TY = ", 
	)-&gt;form( -top =&gt; [$TZbutton,0] );
my $TXentry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$TY) -&gt;form( 
	-left =&gt; [$TXbutton,0],
	-top =&gt; [$TZbutton,0] );
my $TYbutton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "TZ = ", 
	)-&gt;form( -top =&gt; [$TXbutton,0] );
my $TYentry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$TZ) -&gt;form( 
	-left =&gt; [$TYbutton,0],
	-top =&gt; [$TXbutton,0] );


my $dist_button = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "Dist = ", 
	)-&gt;form( -top =&gt; [$TYbutton,0] );
my $dist_entry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$screenDist) -&gt;form( 
	-left =&gt; [$dist_button,0],
	-top =&gt; [$TYbutton,0] );

my $N_button = $frame-&gt;Button( 

	-relief 	=&gt; "groove", 
	-text 		=&gt; "N = ", 
	)-&gt;form( -top =&gt; [$dist_button,0] );
my $N_entry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$N) -&gt;form( 
	-left =&gt; [$N_button,0],
	-top =&gt; [$dist_button,0] );

my $zmin_button = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "zmin = ", 
	)-&gt;form( -top =&gt; [$N_button,0] );
my $zmin_entry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$zmin) -&gt;form( 
	-left =&gt; [$zmin_button,0],
	-top =&gt; [$N_button,0] );

my $doButton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "Draw", 
	-command	=&gt; \&amp;doit  )-&gt;form( 
	-top =&gt; [$zmin_button,0] );

my $clearButton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "Clear", 
	-command	=&gt; \&amp;clear  )-&gt;form( 
	-top 		=&gt; [$zmin_button,0],
	-left 		=&gt; [$doButton,0] );

my $colorAButton = $frame-&gt;Button( 
	-relief 	=&gt; "groove", 
	-text 		=&gt; "Color Side A = ", 
	)-&gt;form( -top =&gt; [$clearButton,0] );
my $colorAEntry = $frame-&gt;Entry( 
	-width 	=&gt; 10,
	-textvariable 	=&gt; \$colorSide_A) -&gt;form( 
	-left =&gt; [$colorAButton,0],
	-top =&gt; [$clearButton,0] );

MainLoop;

BEGIN {
#include module Vector3D
{
package Vector3D;
use strict;
use overload
      "-"    =&gt; \&amp;minus,
      "+"    =&gt; \&amp;plus,
      "*"    =&gt; \&amp;mult;

sub new  {
	my ($pkg,$x,$y,$z) = @_;
	bless {
		_x =&gt; $x,
		_y =&gt; $y,
		_z =&gt; $z
	}, $pkg;
}

sub getx { my $obj = shift; return $obj-&gt;{_x}; }
sub gety { my $obj = shift; return $obj-&gt;{_y}; }
sub getz { my $obj = shift; return $obj-&gt;{_z}; }
sub getxyz { 
	my $obj = shift; 
	my @xyz = ( $obj-&gt;getx(), $obj-&gt;gety(), $obj-&gt;getz() );
	return @xyz;
}

sub setx { my $obj = shift; $obj-&gt;{_x} = shift; }
sub sety { my $obj = shift; $obj-&gt;{_y} = shift; }
sub setz { my $obj = shift; $obj-&gt;{_z} = shift; }
sub setxyz { 
	my $obj = shift; 
	$obj-&gt;setx(shift);
	$obj-&gt;sety(shift);
	$obj-&gt;setz(shift);
}


sub plus {
	my $u = shift;
	my $v = shift;
	return new Vector3D ( 
		$u-&gt;getx() + $v-&gt;getx(),
		$u-&gt;gety() + $v-&gt;gety(),
		$u-&gt;getz() + $v-&gt;getz()
	);
}

sub minus {
	my $u = shift;
	my $v = shift;
	return new Vector3D ( 
		$u-&gt;getx() - $v-&gt;getx(),
		$u-&gt;gety() - $v-&gt;gety(),
		$u-&gt;getz() - $v-&gt;getz()
	);
}

sub mult {
	my $v = shift;
	my $c = shift;
	return new Vector3D ( 
		$c * $v-&gt;getx(),
		$c * $v-&gt;gety(),
		$c * $v-&gt;getz()
	);
}

sub incr {
	my $u = shift;
	my $v = shift;
	$u-&gt;{_x} += $v-&gt;{_x};
	$u-&gt;{_y} += $v-&gt;{_y};
	$u-&gt;{_z} += $v-&gt;{_z};
	return $u;
}

sub decr {
	my $u = shift;
	my $v = shift;
	$u-&gt;{_x} -= $v-&gt;{_x};
	$u-&gt;{_y} -= $v-&gt;{_y};
	$u-&gt;{_z} -= $v-&gt;{_z};
	return $u;
}

sub scale {
	my $v = shift;
	my $c = shift;
	$v-&gt;{_x} *= $c;
	$v-&gt;{_y} *= $c;
	$v-&gt;{_z} *= $c;
	return $v;
}

sub translate {
	my $u = shift;
	my $v = shift;
	$u-&gt;{_x} += $v-&gt;getx();
	$u-&gt;{_y} += $v-&gt;gety();
	$u-&gt;{_z} += $v-&gt;getz();
}

sub dotproduct {
	my $obj = shift; #vector
	my $b 	= shift; #vector
	return 	$obj-&gt;getx() * $b-&gt;getx() 	+ 
		$obj-&gt;gety() * $b-&gt;gety() 	+ 
		$obj-&gt;getz() * $b-&gt;getz();
}

sub abs {
	my $obj = shift; #vector
	return 	sqrt (	$obj-&gt;getx() * $obj-&gt;getx()+ 
			$obj-&gt;gety() * $obj-&gt;gety()+ 
			$obj-&gt;getz() * $obj-&gt;getz());
}

sub crossproduct {
	my $obj = shift; #vector
	my $b 	= shift; #vector
	return 	new Vector3D( 
		$obj-&gt;gety() * $b-&gt;getz() - $obj-&gt;getz() * $b-&gt;gety(),
		$obj-&gt;getz() * $b-&gt;getx() - $obj-&gt;getx() * $b-&gt;getz(),
		$obj-&gt;getx() * $b-&gt;gety() - $obj-&gt;gety() * $b-&gt;getx()
	);
}

sub rotateZ {
	my $P = shift; #vector
	my $phi = shift;
	my $cosphi = cos( $phi );
	my $sinphi = sin( $phi );
	my $dx = $P-&gt;{_x};
	my $dy = $P-&gt;{_y};
	$P-&gt;setx( $dx * $cosphi - $dy * $sinphi );
	$P-&gt;sety( $dx * $sinphi + $dy * $cosphi );
}

sub rotateY {
	my $P = shift; #vector
	my $phi = shift;
	my $cosphi = cos( $phi );
	my $sinphi = sin( $phi );
	my $dx = $P-&gt;{_x};
	my $dz = $P-&gt;{_z};
	$P-&gt;setx( $dx * $cosphi - $dz * $sinphi );
	$P-&gt;setz( $dx * $sinphi + $dz * $cosphi );
}

sub rotateX {
	my $P = shift; #vector
	my $phi = shift;
	my $cosphi = cos( $phi );
	my $sinphi = sin( $phi );
	my $dy = $P-&gt;{_y};
	my $dz = $P-&gt;{_z};
	$P-&gt;sety( $dy * $cosphi - $dz * $sinphi );
	$P-&gt;setz( $dy * $sinphi + $dz * $cosphi );
}

sub print {
	my $v = shift; #vector
	print 	"( " . $v-&gt;getx() . 
		", " . $v-&gt;gety() . 
		", " . $v-&gt;getz() . ")\n";
}

1;
} #end package Vector3D
#include module Vector2D
{
package Vector2D;
#####################################################
### Copyright (c) 2002 Russell B Cecala. All rights
### reserved.  This program is free software; you can
### redistribute it and/or modify it under the same 
### terms as Perl itself.
#####################################################
use strict;
use overload
      "-"    =&gt; \&amp;minus,
      "+"    =&gt; \&amp;plus,
      "*"    =&gt; \&amp;mult,
      "bool" =&gt; \&amp;bool;

sub new  {
	my ($pkg,$x,$y) = @_;
	bless {
		_x =&gt; $x,
		_y =&gt; $y
	}, $pkg;
}

sub getx { my $obj = shift; return $obj-&gt;{_x}; }
sub gety { my $obj = shift; return $obj-&gt;{_y}; }
sub setx { my $obj = shift; my $v = shift; $obj-&gt;{_x} = $v; }
sub sety { my $obj = shift; my $v = shift; $obj-&gt;{_y} = $v; }

sub getxy { 
	my $obj = shift; 
	my @xy = ( $obj-&gt;getx(), $obj-&gt;gety() );
	return @xy;
}

sub plus {
	my $u = shift;
	my $v = shift;
	return new Vector2D ( 
		$u-&gt;getx() + $v-&gt;getx(),
		$u-&gt;gety() + $v-&gt;gety()
	);
}

sub minus {
	my $u = shift;
	my $v = shift;
	return new Vector2D ( 
		$u-&gt;getx() - $v-&gt;getx(),
		$u-&gt;gety() - $v-&gt;gety()
	);
}

sub mult {
	my $v = shift;
	my $c = shift;
	return new Vector2D ( 
		$c * $v-&gt;getx(),
		$c * $v-&gt;gety()
	);
}

sub bool { return defined( shift ); }

sub incr {
	my $u = shift;
	my $v = shift;
	$u-&gt;{_x} += $v-&gt;{_x};
	$u-&gt;{_y} += $v-&gt;{_y};
	return $u;
}

sub decr {
	my $u = shift;
	my $v = shift;
	$u-&gt;{_x} -= $v-&gt;{_x};
	$u-&gt;{_y} -= $v-&gt;{_y};
	return $u;
}

sub scale {
	my $v = shift;
	my $c = shift;
	$v-&gt;{_x} *= $c;
	$v-&gt;{_y} *= $c;
	return $v;
}

sub rotate {
	my $P = shift; #vector
	my $C = shift; #vector
	my $cosphi = shift;
	my $sinphi = shift;
	my $dx = $P-&gt;{_x} - $C-&gt;{_x};
	my $dy = $P-&gt;{_y} - $C-&gt;{_y};
	return new Vector2D ( 
		$C-&gt;{_x} + $dx * $cosphi - $dy * $sinphi,
		$C-&gt;{_y} + $dx * $sinphi + $dy * $cosphi
	);
}

sub print {
	my $v = shift; #vector
	print "( " . $v-&gt;getx() . ", " . $v-&gt;gety() . ")\n";
}


1;
} #end modeult Vector2D
# include Perspective module
{
package Perspective;
use strict;

use Math::Trig;

sub new  {
	my ($pkg,$rho,$theta,$phi) = @_;
	bless {
		_rho =&gt; $rho,
		_theta =&gt; $theta,
		_phi =&gt; $phi,
		_v11 =&gt; ( -sin( $theta ) ),
		_v12 =&gt; ( -cos( $phi ) * cos( $theta ) ),
		_v13 =&gt; ( -sin( $phi ) * cos( $theta ) ),
		_v21 =&gt; (  cos( $theta ) ),
		_v22 =&gt; ( -cos( $phi ) * sin( $theta ) ),
		_v23 =&gt; ( -sin( $phi ) * sin( $theta ) ),
		_v32 =&gt; (  sin( $phi ) ),
		_v33 =&gt; ( -cos( $phi ) ),
		_v43 =&gt; ( $rho )
	}, $pkg;
}

sub getrho 	{ my $obj = shift; return $obj-&gt;{_rho}; }
sub gettheta 	{ my $obj = shift; return $obj-&gt;{_theta}; }
sub getphi 	{ my $obj = shift; return $obj-&gt;{_phi}; }
sub getv11 	{ my $obj = shift; return $obj-&gt;{_v11}; }
sub getv12 	{ my $obj = shift; return $obj-&gt;{_v12}; }
sub getv13 	{ my $obj = shift; return $obj-&gt;{_v13}; }
sub getv21 	{ my $obj = shift; return $obj-&gt;{_v21}; }
sub getv22 	{ my $obj = shift; return $obj-&gt;{_v22}; }
sub getv23 	{ my $obj = shift; return $obj-&gt;{_v23}; }
sub getv32 	{ my $obj = shift; return $obj-&gt;{_v32}; }
sub getv33 	{ my $obj = shift; return $obj-&gt;{_v33}; }
sub getv43 	{ my $obj = shift; return $obj-&gt;{_v43}; }

sub setrho 	{ my $obj = shift; $obj-&gt;{_rho} = shift; }
sub settheta 	{ my $obj = shift; $obj-&gt;{_theta} = shift; }
sub setphi 	{ my $obj = shift; $obj-&gt;{_phi} = shift; }
sub setv11 	{ my $obj = shift; $obj-&gt;{_v11} = shift; }
sub setv12 	{ my $obj = shift; $obj-&gt;{_v12} = shift; }
sub setv13 	{ my $obj = shift; $obj-&gt;{_v13} = shift; }
sub setv21 	{ my $obj = shift; $obj-&gt;{_v21} = shift; }
sub setv22 	{ my $obj = shift; $obj-&gt;{_v22} = shift; }
sub setv23 	{ my $obj = shift; $obj-&gt;{_v23} = shift; }
sub setv32 	{ my $obj = shift; $obj-&gt;{_v32} = shift; }
sub setv33 	{ my $obj = shift; $obj-&gt;{_v33} = shift; }
sub setv43 	{ my $obj = shift; $obj-&gt;{_v43} = shift; }

sub eyecoord {
	my $obj = shift;
	my $pw = shift;
	my $pe = shift;
	my $x = $pw-&gt;getx();
	my $y = $pw-&gt;gety();
	my $z = $pw-&gt;getz();
	my $v11 = $obj-&gt;getv11();
	my $v12 = $obj-&gt;getv12();
	my $v13 = $obj-&gt;getv13();
	my $v21 = $obj-&gt;getv21();
	my $v22 = $obj-&gt;getv22();
	my $v23 = $obj-&gt;getv23();
	my $v32 = $obj-&gt;getv32();
	my $v33 = $obj-&gt;getv33();
	my $v43 = $obj-&gt;getv43();
	$pe-&gt;setx( $v11 * $x + $v21 * $y );
	$pe-&gt;sety( $v12 * $x + $v22 * $y + $v32 * $z);
	$pe-&gt;setz( $v13 * $x + $v23 * $y + $v33 * $z + $v43);
}

sub perspective {
	my $obj = shift;
	my $p	= shift;
	my $refpx = shift;
	my $refpy = shift;
	my $pe 	= new Vector3D( 0.0, 0.0, 0.0 );

	$obj-&gt;eyecoord ( $p, $pe );
	$$refpx = $pe-&gt;getx() / (1E-7 + $pe-&gt;getz());
	$$refpy = $pe-&gt;gety() / (1E-7 + $pe-&gt;getz());
}	

1;
} #end Perspective
# include Clip3D;
{
package Clip3D;
#####################################################
### Copyright (c) 2002 Russell B Cecala. All rights
### reserved.  This program is free software; you can
### redistribute it and/or modify it under the same 
### terms as Perl itself.
#####################################################
use strict;
#use Tk;

sub new  {
	my ($pkg,$zmin,$tag) = @_;
	bless {
		_zmin 	=&gt; $zmin,
		_tag 	=&gt; $tag || 'CLIPVOL'
	}, $pkg;
}

sub setzmin {
	my $obj 	= shift;
	my $zmin	= shift;
	$obj-&gt;{_zmin} 	= $zmin;
}

sub settag {
	my $obj 	= shift;
	my $tag	= shift;
	$obj-&gt;{_tag} 	= $tag;
}

sub getzmin { my $obj = shift; return $obj-&gt;{_zmin}; }
sub gettag { my $obj = shift; return $obj-&gt;{_tag}; }

sub Clip  {
	# refs to scalars
	my $pkg 	= shift;
	my $x0 		= shift;
	my $y0 		= shift;
	my $z0 		= shift;
	my $x1 		= shift;
	my $y1 		= shift;
	my $z1 		= shift;

	#scalar
	my $zmin 	= $pkg-&gt;getzmin();

	my $accept 	= 0;		# assume that line is not visiable
	my $tmin	= 0;
	my $tmax	= 1;
	my $dx		= $$x1 - $$x0;
	my $dz		= $$z1 - $$z0;

	if ( clipT( $dx+$dz, -$$x0-$$z0, \$tmin, \$tmax ) ) {				# Right side
		if ( clipT( -$dx+$dz, $$x0-$$z0, \$tmin, \$tmax ) ) {			# Left side
			my $dy = $$y1 - $$y0;
			if ( clipT( -$dy+$dz, $$y0-$$z0, \$tmin, \$tmax ) ) {		# Bottom side
				if ( clipT( $dy+$dz, -$$y0-$$z0, \$tmin, \$tmax ) ) {	# Top side
					# part of line is in -z &lt;= x &lt;= z, -z &lt;= y &lt;= z
					if ( clipT( $dz, -$$z0 + $zmin, \$tmin, \$tmax ) ) {		# Front
						if ( clipT( -$dz, -$$z0-1, \$tmin, \$tmax ) ) {		# Back
							# part of line is visible in -z &lt;= x &lt;= z, -z &lt;= y &lt;= z, -1 &lt;= x &lt;= $zmin
							$accept = 1;
							# If end pt 1 (t=1) is not in region, compute intersection
							if ( $tmax &lt; 1 ) {
								$$x1 = $$x0 + $tmax * $dx;
								$$y1 = $$y0 + $tmax * $dy;
								$$z1 = $$z0 + $tmax * $dz;
							}
							# If end pt 0 (t=0) is not in region, compute intersection
							if ( $tmin &gt; 0 ) {
								$$x0 = $$x0 + $tmin * $dx;
								$$y0 = $$y0 + $tmin * $dy;
								$$z0 = $$z0 + $tmin * $dz;
							}
						}
					}
				}
			}
		}
	}
	return $accept;
}


###################################################################
###
###	sub clipT is used by the sub clipLine defined in this 
###	module.  clipT is based on pseudo code presented in 
###	Foley, van Dam, Feiner, and Hughes's book "Computer Graphics: 
###	Principles and Practive" 2nd Edition pages on page 122. The 
###	following comment is taken almost verbatum from that book. 
###
###	clipT computes a new value of tE or tL for an interior
###	intersection of a line segment and an edge.  Parameter
###	demon is -dotProduct(Ni, D), which reduces to 
###	+/- deltaX, deltaY for upright rectangles; its sign determines
###	whether the intersection is PE or PL.  Parameter num is 
###	dotProduct( Ni, Po-Pei) for a particular edge/line
###	combination, which reduces to directed horizontal and 
###	vertical distance from Po to an edg; its sign determines
###	visibility of Po and is used to trivially reject, false is 
###	returned; if it cannot be, true is returned and the value
###	of tE or tL is adjusted, if needed, for the portian of the
###	segment that is inside the edge.
###	
###################################################################
sub clipT {
	my $denom 	= shift;
	my $num		= shift;
	my $tE		= shift; # ref to scalar
	my $tL		= shift; # ref to scalar
	my $t;
	my $accept 	= 1;	# 1 implie TRUE. 0 implies FALSE
	if ( $denom &gt; 0 ) {
		$t = $num/$denom;
		if 	( $t &gt; $$tL ) {
			$accept = 0;
		} elsif ( $t &gt; $$tE ) {
			$$tE = $t;
		}
	} elsif ( $denom &lt; 0 ) {
		$t = $num/$denom;
		if 	( $t &lt; $$tE ) {
			$accept = 0;
		} elsif ( $t &lt; $$tL ) {
			$$tL = $t;
		}
	} else {
		if ( $num &gt; 0 ) {
			$accept = 0;
		}
	}
	return $accept;
}

1;
} # end Clip3D;

#include PolygonZbuffer;
{
package PolygonZbuffer;
use strict;
use Exporter;

#use Vector3D;
use Math::Round qw( nearest_floor nearest_ceil );
use Tk;
use Tk::Canvas;
use Data::Dumper;

sub new  {
	my ($pkg, $edges, $canvas, $color) = @_;
	my $highestY  = getHighestY($edges);

	bless {
		edges 	=&gt; $edges, 	# ref to array of edge info hash
		ET		=&gt; undef,	# edge table
		highestY	=&gt; $highestY,
		can		=&gt; $canvas,	#Tk canvas
		color		=&gt; $color
	}, $pkg;
}

sub print {
	my $pkg 	= shift;

	foreach my $e ( @{$pkg-&gt;{edges}} ) {
		@{$e}-&gt;[0]-&gt;print();
		@{$e}-&gt;[1]-&gt;print();
	}
}

sub dumpET {
	my $pkg 	= shift;
	my $et 		= $pkg-&gt;{ET};
	my $highestY 	= $pkg-&gt;{highestY};

	for my $i (0..$highestY-1) {
		if ( @{$et}[$i] ) {
			print "et[$i] = ";
			for my $list ( @{$et}[$i] ) {
				for my $r ( @{$list} ) {
					print "{" . %{$r}-&gt;{Ymax} . "|" . %{$r}-&gt;{Xbot} . "|" . %{$r}-&gt;{invSlope} . "}" ;
				}
			}
			print "\n";
		} else {
			print "et[$i] = NULL\n";
		}
	}
}


sub buildET4Zbuff {
	my $pkg = shift;
	# each entry in the Edge Table (ET) contains the Ymax coordinate of the edge,
	# the x cooridnate of the bottom endpoint Xbot and the x increment 
	# used in the stepping from one scan lime to the next 1/m
	for my $e (@{$pkg-&gt;{edges}}) {
		my $y    = ( $e-&gt;{edge}-&gt;[0]-&gt;gety() &lt;   $e-&gt;{edge}-&gt;[1]-&gt;gety() ) ? $e-&gt;{edge}-&gt;[0]-&gt;gety() : $e-&gt;{edge}-&gt;[1]-&gt;gety() ;
		my $Ymax = ( $e-&gt;{edge}-&gt;[0]-&gt;gety() &gt;=  $e-&gt;{edge}-&gt;[1]-&gt;gety() ) ? $e-&gt;{edge}-&gt;[0]-&gt;gety() : $e-&gt;{edge}-&gt;[1]-&gt;gety() ;
		my $z 	 = $e-&gt;{z};
		my $Xbot = ( $e-&gt;{edge}-&gt;[0]-&gt;gety() &lt;  $e-&gt;{edge}-&gt;[1]-&gt;gety() ) ? $e-&gt;{edge}-&gt;[0]-&gt;getx() : $e-&gt;{edge}-&gt;[1]-&gt;getx() ;
		my $dz   = $e-&gt;{dz};
		my $dx   = $e-&gt;{dx};

		my $invSlope 	= &amp;calcOneOverSlope($e);
		my $rec = {
			Ymax 		=&gt; $Ymax,
			Xbot 		=&gt; $Xbot,
			invSlope	=&gt; $invSlope,
			dz		=&gt; $dz,
			dx		=&gt; $dx,
			z		=&gt; $z,
		};
		push( @{$pkg-&gt;{ET}[$y]}, $rec );
	}
	my $highestY = $pkg-&gt;{highestY};
	for my $i (0..$highestY-1) { if ( $pkg-&gt;{ET}[$i] ) { return $i; } }	
}

sub fillZbuff {
	my $pkg = shift;
	my $zbuffer = shift; # ref to 2D array

	my $color = $pkg-&gt;{color};
	my $y 	= $pkg-&gt;buildET4Zbuff();
	my @ET 	= @{$pkg-&gt;{ET}};
	my @AET;	# Active Edge Table
	
	do {
		#print "================== doing scan line $y ================\n";
		# Move from ET bucket y to the AET those edges whose Ymin = y
		if ( $ET[$y] ) {
			while( @{$ET[$y]} ) { 
				my $e = pop( @{$ET[$y]} ); 
				push( @AET, $e );
			}
			$ET[$y] = undef;
		}
	
		# then sort AET on x
		@AET = sort{ %{$a}-&gt;{Xbot} &lt;=&gt; %{$b}-&gt;{Xbot} } @AET;
	
		my $last; 
		for ( my $i=0; $i&lt;$#AET+1; ) {
			my $X1;
			my $X2;
			my $x1 = $AET[$i++];
			my $x2 = $AET[$i++];
			my $left;
			my $right;
			if ( $x2 ) {
				$last = $x2;
				$X1 = nearest_ceil(1,%{$x1}-&gt;{Xbot});
				$X2 = nearest_floor(1,%{$x2}-&gt;{Xbot});
				$left = $x1;
				$right = $x2;
			} else {
				$X1 = nearest_ceil(1,%{$last}-&gt;{Xbot});
				$X2 = nearest_floor(1,%{$x1}-&gt;{Xbot});
				$left = $last;
				$right = $x1;
			}
#			my $z = %{$left}-&gt;{z};
			my $z = nearest_ceil( 1, %{$left}-&gt;{z} ); 
			for my $x ( $X1..$X2 ) { 
#$z += %{$left}-&gt;{dz}/ (%{$left}-&gt;{dx} + 1E-7) ;
#$z += %{$left}-&gt;{dz};
				$z += nearest_ceil( 1, %{$left}-&gt;{dz} );
				if( $z &lt;= @{$zbuffer}-&gt;[$x]-&gt;[$y] ) {
					$pkg-&gt;setPixel( $x, $y, $color ); 
					@{$zbuffer}-&gt;[$x]-&gt;[$y] = $z; 
				}
			}
		}
		my @AET_copy; # = @AET;
		while( @AET ) {
			my $e = pop( @AET );
			if ( %{$e}-&gt;{Ymax} != $y ) { push( @AET_copy, $e ); }
		}
		$y++;
		@AET = @AET_copy;
		
		for ( my $i=0; $i&lt;$#AET+1; $i++ ) {
			if ( %{$AET[$i]}-&gt;{invSlope} != 0 ) {
				%{$AET[$i]}-&gt;{Xbot} += %{$AET[$i]}-&gt;{invSlope};
			}
		}
	} while( $#AET &gt;= 0  and $#ET &gt;= 0 );
}


sub calcOneOverSlope {
	my $e = shift;
	my $y = $e-&gt;{edge}-&gt;[0]-&gt;gety() - $e-&gt;{edge}-&gt;[1]-&gt;gety();
	my $x =	$e-&gt;{edge}-&gt;[0]-&gt;getx() - $e-&gt;{edge}-&gt;[1]-&gt;getx();
	if ( $y == 0 ) { return undef; }
	return $x/$y;
}

sub getHighestY {
	my $edges 	= shift;

#print Dumper( $edges );
	my $highest = @{$edges}-&gt;[0]-&gt;{edge}-&gt;[0]-&gt;gety();

	for my $edge (@{$edges}) { 
		for my $vector (@{$edge-&gt;{edge}}) { 
			my $y = $vector-&gt;gety();
			if ( $y &gt; $highest ) {
				$highest = $y;
			}
		}
	}
	return $highest;
}

sub setPixel {
	my ( $pkg, $X, $Y, $color ) = @_;
	my $rgb = sprintf "#%03x", $color;

	$pkg-&gt;{can}-&gt;create ( 'rectangle',
		$X,   $Y, 
		$X+1, $Y+1,
		-fill =&gt; $rgb,
		-outline =&gt; $rgb
	);
}

1;
}}
&lt;/code&gt;
</field>
</data>
</node>
