C:\test\rbc\tye>3d Fri Dec 27 23:39:22 2002 Fri Dec 27 23:41:27 2002 C:\test\rbc\tye>dprofpp -F Faking 8 exit timestamp(s). Total Elapsed Time = -23.5459 Seconds User+System Time = 0 Seconds Exclusive Times %Time ExclSec CumulS #Calls sec/call Csec/c Name 0.00 53.98 206.29 88577 0.0006 0.0023 Tk::WidgetMethod 0.00 47.45 43.704 255064 0.0002 0.0002 Tk::winfo 0.00 20.56 153.56 1 20.559 153.56 main::doit 0.00 19.87 55.982 255095 0.0001 0.0002 Tk::Submethods::__ANON__ 0.00 13.24 67.606 88576 0.0001 0.0008 PolygonZbuffer::setPixel 0.00 8.775 76.860 5 1.7550 15.371 PolygonZbuffer::fillZbuff 0.00 6.884 210.54 88577 0.0001 0.0024 Tk::__ANON__ 0.00 0.459 1.695 7 0.0656 0.2421 main::BEGIN 0.00 0.240 0.629 14 0.0172 0.0449 base::import 0.00 0.229 0.239 908 0.0003 0.0003 Math::Round::nearest_ceil 0.00 0.220 0.259 3 0.0733 0.0865 Math::Trig::BEGIN 0.00 0.189 0.169 908 0.0002 0.0002 Math::Round::nearest_floor 0.00 0.161 0.200 34 0.0047 0.0059 Exporter::import 0.00 0.120 0.138 11 0.0109 0.0125 PolygonZbuffer::BEGIN 0.00 0.099 153.78 224 0.0004 0.6865 Tk::DoOneEvent #### C:\test\rbc\tye>perl -d:DProf 3d.pl Sun Dec 29 04:15:35 2002 Sun Dec 29 04:15:39 2002 C:\test\rbc\tye>dprofpp -O 30 -R -F Garbled profile, faking exit timestamp: Vector2D::BEGIN => . Total Elapsed Time = -0.40205 Seconds User+System Time = 6.797075 Seconds Exclusive Times %Time ExclSec CumulS #Calls sec/call Csec/c Name 20.9 1.423 3.923 1 1.4234 3.9228 main::doit 18.8 1.283 1.933 5 0.2565 0.3866 PolygonZbuffer::fillZbuff 8.09 0.550 4.460 916 0.0006 0.0049 Tk::WidgetMethod 7.93 0.539 1.764 13 0.0415 0.1357 main::BEGIN 5.41 0.368 4.409 277 0.0013 0.0159 Tk::DoOneEvent 2.94 0.200 0.249 3 0.0666 0.0832 Math::Trig::BEGIN 2.80 0.190 0.618 14 0.0136 0.0442 base::import 2.66 0.181 0.220 35 0.0052 0.0063 Exporter::import 1.91 0.130 0.122 545 0.0002 0.0002 Tk::winfo 1.77 0.120 0.128 19 0.0063 0.0067 PolygonZbuffer::BEGIN 1.77 0.120 4.553 916 0.0001 0.0050 Tk::__ANON__ 1.32 0.090 0.289 7 0.0128 0.0412 Tk::MainWindow::BEGIN 1.32 0.090 0.129 2 0.0449 0.0643 Tk::Widget::_AutoloadTkWidget 1.19 0.081 0.176 1 0.0809 0.1759 Tk::Widget::packAdjust 1.18 0.080 0.088 1 0.0799 0.0885 Tk::update 1.18 0.080 0.052 1870 0.0000 0.0000 POSIX::floor 1.03 0.070 0.065 325 0.0002 0.0002 Vector3D::new 1.02 0.069 0.207 130 0.0005 0.0016 Perspective::perspective 0.88 0.060 0.100 1 0.0600 0.0998 vars::BEGIN 0.88 0.060 0.060 2 0.0300 0.0299 DynaLoader::BEGIN 0.88 0.060 0.164 576 0.0001 0.0003 Tk::Submethods::__ANON__ 0.85 0.058 0.090 130 0.0004 0.0007 Perspective::eyecoord 0.77 0.052 4.456 1 0.0516 4.4563 Tk::MainLoop 0.74 0.050 0.050 3 0.0166 0.0166 Math::Complex::BEGIN 0.74 0.050 0.040 686 0.0001 0.0001 Vector3D::getz 0.74 0.050 0.090 7 0.0071 0.0128 AutoLoader::import 0.74 0.050 0.389 11 0.0045 0.0354 Tk::BEGIN 0.74 0.050 0.054 86 0.0006 0.0006 Tk::Derived::Delegate 0.59 0.040 0.040 5 0.0080 0.0080 Exporter::export 0.46 0.031 0.260 3 0.0103 0.0867 Tk::Event::BEGIN #### #!/usr/bin/perl -w ##################################################### ### 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; use Tk::Canvas; use Getopt::Std; use Math::Trig; use Math::Round qw( nearest_floor nearest_ceil ); my %opts = (); getopts( 'w:h:b:f:z:', \%opts ); my $width = $opts{w} || 500; my $height = $opts{h} || 500; my $screenDist = 1000; my $background = $opts{b} || 'black'; my $fill = $opts{f} || 'yellow'; my $zmin = $opts{z} || -10; my $pidiv180 = atan(1)/45; my $top = MainWindow->new(); my $frame = $top->Frame(); my $can = $top->Canvas( -width => $width, -height=> $height, -background=>$background ); my $rho = 0; my $theta = 90; my $phi = 0.0; my $rotateZ = 0.0; my $rotateX = -20.0; my $rotateY = 30.0; my $TZ = 10; 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; $#ZBUFFER = $can->reqwidth(); sub clear{ $can->delete( 'all' ); } sub doit { print scalar localtime, $/; $x_center = $can->reqwidth()/2.0; $y_center = $can->reqheight()/2.0; $YMAX = $can->reqheight(); $per = new Perspective( $rho, $theta*$pidiv180, $phi*$pidiv180 ); $clipVol = new Clip3D( $zmin ); @$_ = (9999999)x $can->reqheight() for @ZBUFFER; my @cube = ( new Vector3D( 1, -1, -1, "Z" ), # 0 A new Vector3D( 1, 1, -1, "1"), # 1 B new Vector3D( -1, 1, -1, "2"), # 2 C new Vector3D( -1, -1, -1, "3"), # 3 D new Vector3D( 1, -1, 1, "4"), # 4 E new Vector3D( 1, 1, 1, "5"), # 5 F new Vector3D( -1, 1, 1, "6"), # 6 G new Vector3D( -1, -1, 1, "7") # 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(1 .. $N) { my ($rx, $ry, $rz) = map{ $_ * $pidiv180 } $rotateX, $rotateY, $rotateZ; for (@cube) { $_->rotateZ( $rz ); $_->rotateX( $rx ); $_->rotateY( $ry ); $_->translate( new Vector3D($TX, $TY, $TZ, "TRANS" ) ); } my ($x, $y, $z) = $cube[0]->getxyz(); my @zero = ( new Vector3D( $x + 0.05, $y , $z ), # 0 new Vector3D( $x + 0.1, $y , $z ), # 1 new Vector3D( $x + 0.1, $y + 0.1, $z ), # 1 new Vector3D( $x + 0.05, $y + 0.1, $z ), # 1 new Vector3D( $x + 0.05, $y , $z ), # 0 ); ($x, $y, $z) = $cube[1]->getxyz(); my @one = ( new Vector3D( $x + 0.1, $y , $z ), # 1 new Vector3D( $x + 0.1, $y + 0.1, $z ), # 1 ); ($x, $y, $z) = $cube[2]->getxyz(); my @two = ( new Vector3D( $x - 0.10, $y + 0.10, $z ), # 1 new Vector3D( $x - 0.05, $y + 0.10, $z ), # 1 new Vector3D( $x - 0.05, $y + 0.05, $z ), # 0 new Vector3D( $x - 0.10, $y + 0.05, $z ), # 0 new Vector3D( $x - 0.10, $y , $z ), # 0 new Vector3D( $x - 0.05, $y , $z ), # 1 ); ($x, $y, $z) = $cube[3]->getxyz(); my @three = ( new Vector3D( $x - 0.10, $y + 0.10, $z ), # 1 new Vector3D( $x - 0.05, $y + 0.10, $z ), # 1 new Vector3D( $x - 0.05, $y + 0.05, $z ), # 0 new Vector3D( $x - 0.10, $y + 0.05, $z ), # 0 new Vector3D( $x - 0.05, $y + 0.05, $z ), # 0 new Vector3D( $x - 0.05, $y , $z ), # 0 new Vector3D( $x - 0.10, $y , $z ), # 1 ); ($x, $y, $z) = $cube[4]->getxyz(); my @four = ( new Vector3D( $x + 0.05, $y + 0.10, $z ), # 1 new Vector3D( $x + 0.05, $y + 0.05, $z ), # 4 new Vector3D( $x + 0.10, $y + 0.05, $z ), # 3 new Vector3D( $x + 0.10, $y + 0.10, $z ), # 2 new Vector3D( $x + 0.10, $y , $z ), # 6 ); ($x, $y, $z) = $cube[5]->getxyz(); my @five = ( new Vector3D( $x + 0.10, $y + 0.10, $z ), # 1 new Vector3D( $x + 0.05, $y + 0.10, $z ), # 1 new Vector3D( $x + 0.05, $y + 0.05, $z ), # 0 new Vector3D( $x + 0.10, $y + 0.05, $z ), # 0 new Vector3D( $x + 0.10, $y , $z ), # 0 new Vector3D( $x + 0.05, $y , $z ), # 1 ); ($x, $y, $z) = $cube[6]->getxyz(); my @six = ( new Vector3D( $x - 0.05, $y + 0.10, $z ), # 1 new Vector3D( $x - 0.10, $y + 0.10, $z ), # 1 new Vector3D( $x - 0.10, $y + 0.05, $z ), # 0 new Vector3D( $x - 0.05, $y + 0.05, $z ), # 0 new Vector3D( $x - 0.05, $y , $z ), # 0 new Vector3D( $x - 0.10, $y , $z ), # 1 new Vector3D( $x - 0.10, $y + 0.05, $z ), # 0 ); ($x, $y, $z) = $cube[7]->getxyz(); my @seven = ( new Vector3D( $x - 0.10, $y + 0.10, $z ), # 1 new Vector3D( $x - 0.05, $y + 0.10, $z ), # 1 new Vector3D( $x - 0.05, $y , $z ), # 0 ); ### This will do the zbuffer stuff 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 ### This does wireframe DrawLine( $cube[0], $cube[1], 'yellow' ); DrawLine( $cube[1], $cube[2], 'yellow' ); DrawLine( $cube[2], $cube[3], 'yellow' ); DrawLine( $cube[3], $cube[0], 'yellow' ); DrawLine( $cube[0], $cube[4], 'yellow' ); DrawLine( $cube[4], $cube[5], 'yellow' ); DrawLine( $cube[5], $cube[1], 'yellow' ); DrawLine( $cube[5], $cube[6], 'yellow' ); DrawLine( $cube[6], $cube[2], 'yellow' ); DrawLine( $cube[6], $cube[7], 'yellow' ); DrawLine( $cube[7], $cube[3], 'yellow' ); DrawLine( $cube[7], $cube[4], 'yellow' ); 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 print scalar localtime, $/; } use constant SHAPE => 0; use constant COLOR => 1; sub drawShape { DrawLine( $_[SHAPE]->[$_-1], $_[SHAPE]->[$_], $_[COLOR]||'red' ) for 1 .. $#{$_[SHAPE]}; } use constant POINTS => 0; sub DrawPolygon { my @edges; push @edges, ProjectLine( $_[POINTS]->[$_-1], $_[POINTS]->[$_] ) for 1 .. $#{$_[POINTS]}; my $polygon = new PolygonZbuffer( \@edges, $can, $_[COLOR] ); $polygon->fillZbuff(\@ZBUFFER); } use constant V1 => 0; use constant V2 => 1; sub ProjectLine { my( $x1, $y1, $x2, $y2 ); my ($X1, $Y1, $Z1) = $_[V1]->getxyz(); my ($X2, $Y2, $Z2) = $_[V2]->getxyz(); if ( $clipVol->Clip ( \$X1, \$Y1, \$Z1, \$X2, \$Y2, \$Z2) ) { my $V1 = new Vector3D( $X1, $Y1, $Z1, $_[V1]->{name} ); my $V2 = new Vector3D( $X2, $Y2, $Z2, $_[V2]->{name} ); $per->perspective( $V1, \$x1, \$y1 ); $per->perspective( $V2, \$x2, \$y2 ); return { edge =>[ new Vector2D ( nearest_ceil( 1, $screenDist * $x1 + $x_center ), nearest_floor( 1, $YMAX - ($screenDist * $y1 + $y_center)) ), new Vector2D ( nearest_ceil( 1, $screenDist * $x2 + $x_center ), nearest_floor( 1, $YMAX - ($screenDist * $y2 + $y_center)) ) ], edge3D => [$V1, $V2], z => ( $Y1 < $Y2 ) ? ($Z1) : ($Z2), }; } return undef; } use constant COLOR2 => 2; sub DrawLine { my( $x1, $y1, $x2, $y2 ); my ($X1, $Y1, $Z1) = $_[V1]->getxyz(); my ($X2, $Y2, $Z2) = $_[V2]->getxyz(); if ( $clipVol->Clip ( \$X1, \$Y1, \$Z1, \$X2, \$Y2, \$Z2) ) { my $V1 = new Vector3D( $X1, $Y1, $Z1 ); my $V2 = new Vector3D( $X2, $Y2, $Z2 ); $per->perspective( $_[V1], \$x1, \$y1 ); $per->perspective( $_[V2], \$x2, \$y2 ); $can->create( 'line', $screenDist * $x1 + $x_center, $YMAX - ($screenDist * $y1 + $y_center), $screenDist * $x2 + $x_center, $YMAX - ($screenDist * $y2 + $y_center), -fill => $_[COLOR2] ); return ( new Vector2D ( nearest_ceil( 1, $screenDist * $x1 + $x_center ), nearest_floor( 1, $YMAX - ($screenDist * $y1 + $y_center)) ), new Vector2D ( nearest_ceil( 1, $screenDist * $x2 + $x_center ), nearest_floor( 1, $YMAX - ($screenDist * $y2 + $y_center)) ), ) } } ### ### Set up the GUI ### $can->packAdjust( -side => 'left', -fill => 'both', -delay => 1 ); $frame->pack( -side => 'left', -fill => 'y', -expand => 'y', -anchor => 'w' ); my $rotZbutton = $frame->Button( -relief => "groove", -text => "rotZ = ", )->pack( anchor => 'w' ); my $rotZentry = $frame->Entry ( -width => 10, -textvariable => \$rotateZ)->form( -left => [$rotZbutton,0] ); my $rotXbutton = $frame->Button( -relief => "groove", -text => "rotX = ", )->form( -top => [$rotZbutton,0] ); my $rotXentry = $frame->Entry ( -width => 10, -textvariable => \$rotateX) ->form( -left => [$rotXbutton,0], -top => [$rotZbutton,0] ); my $rotYbutton = $frame->Button( -relief => "groove", -text => "rotY = ", )->form( -top => [$rotXbutton,0] ); my $rotYentry = $frame->Entry( -width => 10, -textvariable => \$rotateY) ->form( -left => [$rotYbutton,0], -top => [$rotXbutton,0] ); my $TZbutton = $frame->Button( -relief => "groove", -text => "TX = ", )->form( -top => [$rotYbutton,0] ); my $TZentry = $frame->Entry( -width => 10, -textvariable => \$TX) ->form( -left => [$TZbutton,0], -top => [$rotYbutton,0] ); my $TXbutton = $frame->Button( -relief => "groove", -text => "TY = ", )->form( -top => [$TZbutton,0] ); my $TXentry = $frame->Entry( -width => 10, -textvariable => \$TY) ->form( -left => [$TXbutton,0], -top => [$TZbutton,0] ); my $TYbutton = $frame->Button( -relief => "groove", -text => "TZ = ", )->form( -top => [$TXbutton,0] ); my $TYentry = $frame->Entry( -width => 10, -textvariable => \$TZ) ->form( -left => [$TYbutton,0], -top => [$TXbutton,0] ); my $N_button = $frame->Button( -relief => "groove", -text => "N = ", )->form( -top => [$TYbutton,0] ); my $N_entry = $frame->Entry( -width => 10, -textvariable => \$N) ->form( -left => [$N_button,0], -top => [$TYbutton,0] ); my $zmin_button = $frame->Button( -relief => "groove", -text => "zmin = ", )->form( -top => [$TYbutton,0] ); my $zmin_entry = $frame->Entry( -width => 10, -textvariable => \$zmin) ->form( -left => [$zmin_button,0], -top => [$TYbutton,0] ); my $doButton = $frame->Button( -relief => "groove", -text => "Draw", -command => \&doit ) ->form( -top => [$zmin_button,0] ); my $clearButton = $frame->Button( -relief => "groove", -text => "Clear", -command => \&clear ) ->form( -top => [$zmin_button,0], -left => [$doButton,0] ); MainLoop; ##################################################################### ### Stick on Perl Modules ##################################################################### BEGIN { #include module Vector3D { package Vector3D; use strict; use constant X => 0; use constant Y => 1; use constant Z => 2; use overload "-" => \&minus, "+" => \&plus, "*" => \&mult, "==" => \&equal, "!=" => \¬Equal; use constant NAME => 3; sub new { my $pkg = shift; bless { _x => $_[X], _y => $_[Y], _z => $_[Z], name => $_[NAME] || "Unnamed" }, $pkg; } sub equal { return ( $_[X]->getx() == $_[Y]->getx() and $_[X]->gety() == $_[Y]->gety() and $_[X]->getz() == $_[Y]->getz() ); } sub notEqual { return !equal( $_[X], $_[Y] ); } use constant OBJ => 0; use constant VALUE => 1; sub getx { $_[OBJ]->{_x}; } sub gety { $_[OBJ]->{_y}; } sub getz { $_[OBJ]->{_z}; } sub getxyz { ( $_[OBJ]->getx(), $_[OBJ]->gety(), $_[OBJ]->getz() ); } sub setx { $_[OBJ]->{_x} = $_[VALUE]; } sub sety { $_[OBJ]->{_y} = $_[VALUE]; } sub setz { $_[OBJ]->{_z} = $_[VALUE]; } sub setxyz { $_[OBJ]->setx($_[VALUE]); $_[OBJ]->sety($_[VALUE]); $_[OBJ]->setz($_[VALUE]); } sub plus { return new Vector3D ( $_[X]->getx() + $_[Y]->getx(), $_[X]->gety() + $_[Y]->gety(), $_[X]->getz() + $_[Y]->getz() ); } sub minus { return new Vector3D ( $_[X]->getx() - $_[Y]->getx(), $_[X]->gety() - $_[Y]->gety(), $_[X]->getz() - $_[Y]->getz() ); } use constant SCALE => 1; sub mult { return new Vector3D ( $_[SCALE] * $_[Y]->getx(), $_[SCALE] * $_[Y]->gety(), $_[SCALE] * $_[Y]->getz() ); } sub incr { $_[X]->{_x} += $_[Y]->{_x}; $_[X]->{_y} += $_[Y]->{_y}; $_[X]->{_z} += $_[Y]->{_z}; return $_[X]; } sub decr { $_[X]->{_x} -= $_[Y]->{_x}; $_[X]->{_y} -= $_[Y]->{_y}; $_[X]->{_z} -= $_[Y]->{_z}; return $_[X]; } sub scale { $_[Y]->{_x} *= $_[SCALE]; $_[Y]->{_y} *= $_[SCALE]; $_[Y]->{_z} *= $_[SCALE]; return $_[Y]; } sub translate { $_[X]->{_x} += $_[Y]->getx(); $_[X]->{_y} += $_[Y]->gety(); $_[X]->{_z} += $_[Y]->getz(); } use constant VECTOR1 => 0; use constant VECTOR2 => 1; sub dotproduct { return $_[VECTOR1]->getx() * $_[VECTOR2]->getx() + $_[VECTOR1]->gety() * $_[VECTOR2]->gety() + $_[VECTOR1]->getz() * $_[VECTOR2]->getz(); } sub abs { return sqrt ( $_[VECTOR1]->getx() * $_[VECTOR1]->getx()+ $_[VECTOR1]->gety() * $_[VECTOR1]->gety()+ $_[VECTOR1]->getz() * $_[VECTOR1]->getz()); } sub crossproduct { return new Vector3D( $_[VECTOR1]->gety() * $_[VECTOR2]->getz() - $_[VECTOR1]->getz() * $_[VECTOR2]->gety(), $_[VECTOR1]->getz() * $_[VECTOR2]->getx() - $_[VECTOR1]->getx() * $_[VECTOR2]->getz(), $_[VECTOR1]->getx() * $_[VECTOR2]->gety() - $_[VECTOR1]->gety() * $_[VECTOR2]->getx(), $_[VECTOR1]->{name} ); } # Following 3 subs updated to correct error introduced. use constant PHI => 1; sub rotateZ { my $cosphi = cos( $_[PHI] ); my $sinphi = sin( $_[PHI] ); my $dx = $_[VECTOR1]->{_x}; my $dy = $_[VECTOR1]->{_y}; $_[VECTOR1]->setx( $dx * $cosphi - $dy * $sinphi ); $_[VECTOR1]->sety( $dx * $sinphi + $dy * $cosphi ); } sub rotateY { my $cosphi = cos( $_[PHI] ); my $sinphi = sin( $_[PHI] ); my $dx = $_[VECTOR1]->{_x}; my $dz = $_[VECTOR1]->{_z}; $_[VECTOR1]->setx( $dx * $cosphi - $dz * $sinphi ); $_[VECTOR1]->setz( $dx * $sinphi + $dz * $cosphi ); } sub rotateX { my $cosphi = cos( $_[PHI] ); my $sinphi = sin( $_[PHI] ); my $dy = $_[VECTOR1]->{_y}; my $dz = $_[VECTOR1]->{_z}; $_[VECTOR1]->sety( $dy * $cosphi - $dz * $sinphi ); $_[VECTOR1]->setz( $dy * $sinphi + $dz * $cosphi ); } sub print { print "( " . $_[VECTOR1]->getx() . ", " . $_[VECTOR1]->gety() . ", " . $_[VECTOR1]->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 "-" => \&minus, "+" => \&plus, "*" => \&mult, "bool" => \&bool ; use constant X => 0; use constant Y => 1; sub new { my $pkg = shift; bless { _x => $_[X], _y => $_[Y] }, $pkg; } use constant V2D => 0; use constant VALUE => 1; sub getx { $_[V2D]->{_x}; } sub gety { $_[V2D]->{_y}; } sub setx { $_[V2D]->{_x} = $_[VALUE]; } sub sety { $_[V2D]->{_y} = $_[VALUE]; } sub getxy { ( $_[V2D]->getx(), $_[V2D]->gety() ); } use constant VECTOR1 => 0; use constant VECTOR2 => 1; use constant SCALE => 1; sub plus { return new Vector2D ( $_[VECTOR1]->getx() + $_[VECTOR2]->getx(), $_[VECTOR1]->gety() + $_[VECTOR2]->gety() ); } sub minus { return new Vector2D ( $_[VECTOR1]->getx() - $_[VECTOR2]->getx(), $_[VECTOR1]->gety() - $_[VECTOR2]->gety() ); } sub mult { return new Vector2D ( $_[SCALE] * $_[VECTOR2]->getx(), $_[SCALE] * $_[VECTOR2]->gety() ); } sub bool { return defined( shift ); } sub incr { $_[VECTOR1]->{_x} += $_[VECTOR2]->{_x}; $_[VECTOR1]->{_y} += $_[VECTOR2]->{_y}; return $_[VECTOR1]; } sub decr { $_[VECTOR1]->{_x} -= $_[VECTOR2]->{_x}; $_[VECTOR1]->{_y} -= $_[VECTOR2]->{_y}; return $_[VECTOR1]; } sub scale { $_[VECTOR2]->{_x} *= $_[SCALE]; $_[VECTOR2]->{_y} *= $_[SCALE]; return $_[VECTOR2]; } use constant COSPHI => 2; use constant SINPHI => 3; sub rotate { my $dx = $_[VECTOR1]->{_x} - $_[VECTOR2]->{_x}; my $dy = $_[VECTOR1]->{_y} - $_[VECTOR2]->{_y}; return new Vector2D ( $_[VECTOR2]->{_x} + $dx * $_[COSPHI] - $dy * $_[SINPHI], $_[VECTOR2]->{_y} + $dx * $_[SINPHI] + $dy * $_[COSPHI] ); } sub print { print "( " . $_[VECTOR1]->getx() . ", " . $_[VECTOR1]->gety() . ")\n"; } 1; } #end modeult Vector2D # include Perspective module { package Perspective; use strict; use Math::Trig; use constant RHO => 0; use constant THETA => 1; use constant PHI => 2; sub new { my $pkg = shift; bless { _rho => $_[RHO], _theta => $_[THETA], _phi => $_[PHI], _v11 => ( -sin( $_[THETA] ) ), _v12 => ( -cos( $_[PHI] ) * cos( $_[THETA] ) ), _v13 => ( -sin( $_[PHI] ) * cos( $_[THETA] ) ), _v21 => ( cos( $_[THETA] ) ), _v22 => ( -cos( $_[PHI] ) * sin( $_[THETA] ) ), _v23 => ( -sin( $_[PHI] ) * sin( $_[THETA] ) ), _v32 => ( sin( $_[PHI] ) ), _v33 => ( -cos( $_[PHI] ) ), _v43 => ( $_[RHO] ) }, $pkg; } use constant OBJ => 0; use constant VALUE => 1; sub getrho { $_[OBJ]->{_rho}; } sub gettheta { $_[OBJ]->{_theta}; } sub getphi { $_[OBJ]->{_phi}; } sub getv11 { $_[OBJ]->{_v11}; } sub getv12 { $_[OBJ]->{_v12}; } sub getv13 { $_[OBJ]->{_v13}; } sub getv21 { $_[OBJ]->{_v21}; } sub getv22 { $_[OBJ]->{_v22}; } sub getv23 { $_[OBJ]->{_v23}; } sub getv32 { $_[OBJ]->{_v32}; } sub getv33 { $_[OBJ]->{_v33}; } sub getv43 { $_[OBJ]->{_v43}; } sub setrho { $_[OBJ]->{_rho} = $_[VALUE]; } sub settheta { $_[OBJ]->{_theta} = $_[VALUE]; } sub setphi { $_[OBJ]->{_phi} = $_[VALUE]; } sub setv11 { $_[OBJ]->{_v11} = $_[VALUE]; } sub setv12 { $_[OBJ]->{_v12} = $_[VALUE]; } sub setv13 { $_[OBJ]->{_v13} = $_[VALUE]; } sub setv21 { $_[OBJ]->{_v21} = $_[VALUE]; } sub setv22 { $_[OBJ]->{_v22} = $_[VALUE]; } sub setv23 { $_[OBJ]->{_v23} = $_[VALUE]; } sub setv32 { $_[OBJ]->{_v32} = $_[VALUE]; } sub setv33 { $_[OBJ]->{_v33} = $_[VALUE]; } sub setv43 { $_[OBJ]->{_v43} = $_[VALUE]; } use constant PW => 1; use constant PE => 2; sub eyecoord { $_[PE]->setx( $_[OBJ]->getv11() * $_[PW]->getx() + $_[OBJ]->getv21() * $_[PW]->gety() ); $_[PE]->sety( $_[OBJ]->getv12() * $_[PW]->getx() + $_[OBJ]->getv22() * $_[PW]->gety() + $_[OBJ]->getv32() * $_[PW]->getz()); $_[PE]->setz( $_[OBJ]->getv13() * $_[PW]->getx() + $_[OBJ]->getv23() * $_[PW]->gety() + $_[OBJ]->getv33() * $_[PW]->getz() + $_[OBJ]->getv43() ); } use constant REFPX => 2; use constant REFPY => 3; sub perspective { my $pe = new Vector3D( 0.0, 0.0, 0.0, "PE" ); $_[OBJ]->eyecoord ( $_[PW], $pe ); ${$_[REFPX]} = $pe->getx() / (1E-7 + $pe->getz()); ${$_[REFPY]} = $pe->gety() / (1E-7 + $pe->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; use constant ZMIN => 0; use constant TAG =>1; sub new { my $pkg = shift; bless { _zmin => $_[ZMIN], _tag => $_[TAG] || 'CLIPVOL' }, $pkg; } use constant OBJ => 0; use constant VALUE => 1; sub setzmin { $_[OBJ]->{_zmin} = $_[VALUE]; } sub settag { $_[OBJ]->{_tag} = $_[VALUE]; } sub getzmin { $_[OBJ]->{_zmin}; } sub gettag { $_[OBJ]->{_tag}; } use constant PKG => 0; use constant X0 => 1; use constant Y0 => 2; use constant Z0 => 3; use constant X1 => 4; use constant Y1 => 5; use constant Z1 => 6; sub Clip { # refs to scalars my ($pkg, $x0, $y0, $z0, $x1, $y1, $z1) = ($_[PKG], $_[X0], $_[Y0], $_[Z0], $_[X1], $_[Y1], $_[Z1]); #scalar my $zmin = $pkg->getzmin(); my ($tmin, $tmax) = (0, 1); my $dx = $$x1 - $$x0; my $dz = $$z1 - $$z0; my $dy = $$y1 - $$y0; return 0 unless clipT( $dx+$dz, -$$x0-$$z0, $tmin, $tmax ) # Right side and clipT( -$dx+$dz, $$x0-$$z0, $tmin, $tmax ) # Left side and clipT( -$dy+$dz, $$y0-$$z0, $tmin, $tmax ) # Bottom side and clipT( $dy+$dz, -$$y0-$$z0, $tmin, $tmax ) # Top side # part of line is in -z <= x <= z, -z <= y <= z and clipT( $dz, -$$z0 + $zmin, $tmin, $tmax ) # Front and clipT( -$dz, -$$z0-1, $tmin, $tmax ); # Back # part of line is visible in -z <= x <= z, -z <= y <= z, -1 <= x <= $zmin # If end pt 1 (t=1) is not in region, compute intersection $$x1 = $$x0 + $tmax * $dx , $$y1 = $$y0 + $tmax * $dy , $$z1 = $$z0 + $tmax * $dz if $tmax < 1; # If end pt 0 (t=0) is not in region, compute intersection $$x0 = $$x0 + $tmin * $dx , $$y0 = $$y0 + $tmin * $dy , $$z0 = $$z0 + $tmin * $dz if ( $tmin > 0 ); return 1; } ################################################################### ### ### 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. ### ################################################################### use constant DENOM => 0; use constant NUM => 1; use constant TE => 2; # OUT parameter (Aliased scalar ) use constant TL => 3; # OUT parameter (Aliased scalar ) sub clipT { return ($_[NUM] > 0) ? 0 : 1 if $_[DENOM] == 0; my $t = $_[NUM]/$_[DENOM]; if ( $_[DENOM] > 0 ) { return 0 if $t > $_[TL]; $_[TE] = $t if $t > $_[TE]; } elsif ( $_[DENOM] < 0 ) { return 0 if $t < $_[TE]; $_[TL] = $t if $t < $_[TL]; } return 1; } 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 => $edges, # ref to array of edge info hash ET => undef, # edge table highestY => $highestY, can => $canvas, #Tk canvas color => $color }, $pkg; } sub print { my $pkg = shift; foreach my $e ( @{$pkg->{edges}} ) { @{$e}->[0]->print(); @{$e}->[1]->print(); } } sub dumpET { my $pkg = shift; my $et = $pkg->{ET}; my $highestY = $pkg->{highestY}; for my $i (0..$highestY-1) { if ( @{$et}[$i] ) { print "et[$i] = "; for my $list ( @{$et}[$i] ) { for my $r ( @{$list} ) { print "{" . %{$r}->{Ymax} . "|" . %{$r}->{Xbot} . "|" . %{$r}->{invSlope} . "}" ; } } print "\n"; } else { print "et[$i] = NULL\n"; } } } # Next 2 subs updated: Cleaner, more accurate refactorization 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->{edges}}) { my ($y, $Ymax) = ( $e->{edge}[0]->gety(), $e->{edge}[1]->gety() ); my ($flag) = ($y < $Ymax); ($y, $Ymax) = ($Ymax, $y) unless $flag; push @{$pkg->{ET}[$y]}, { Ymax => $Ymax, Xbot => ($flag) ? $e->{edge }[0]->getx() : $e->{edge }[1]->getx(), z => ($flag) ? $e->{edge3D}[0]->getz() : $e->{edge3D}[1]->getz(), invSlope => calcOneOverSlope($e), }; } my $highestY = $pkg->{highestY}; $pkg->{ET}[$_] and return $_ for 0..$highestY-1 } sub fillZbuff { my ($pkg, $zbuffer) = @_; my $rgb = sprintf "#%03x", $pkg->{color}; my $y = $pkg->buildET4Zbuff(); my $ET = $pkg->{ET}; my @AET; #! Active Edge Table do { # Move from ET bucket y to the AET those edges whose Ymin = y if ($ET->[$y]) { @AET = sort{ $a->{Xbot} <=> $b->{Xbot} } @AET, @{$ET->[$y]}; $ET->[$y] = undef; } for ( my $i=0; $i<@AET; $i += 2 ) { my ($left, $right) = $AET[$i+1] ? ($i, $i+1) : ($i-1, $i); my $X1 = nearest_ceil (1, $AET[$left ]->{Xbot} ); my $X2 = nearest_floor(1, $AET[$right]->{Xbot} ); my $z = $AET[$left ]->{z}; my $dz = $AET[$right]->{z} - $z; my $dx = $X2 - $X1; $z += ($AET[$right] - $z) / $dx if $dx; $z <= $zbuffer->[$_][$y] and $zbuffer->[$_][$y] = $z for ($X1 .. $X2); $pkg->{can}->create ( 'line', $X1, $y, $X2, $y, -fill => $rgb ); } @AET = grep{ $_->{Ymax} != $y #!! BEWARE !! ALL the bracketing in the next line is necessary. ? ( ($_->{invSlope} and $_->{Xbot} += $_->{invSlope}), 1) : 0 } @AET; $y++; } while( @AET and @$ET ); } sub calcOneOverSlope { my $e = shift; my $y = $e->{edge}->[0]->gety() - $e->{edge}->[1]->gety(); return undef if $y == 0; my $x = $e->{edge}->[0]->getx() - $e->{edge}->[1]->getx(); return $x/$y; } use constant EDGES => 0; sub getHighestY { my $highest = 0; $highest < $_ and $highest = $_ for map{ $_->gety() } map{ @{$_->{edge}} } @{$_[EDGES]}; return $highest; } use constant PACKAGE => 0; use constant X => 1; use constant Y => 2; use constant COLOR => 3; sub setPixel { my $rgb = sprintf "#%03x", $_[COLOR]; $_[PACKAGE]->{can}->create ( 'line', $_[X], $_[Y], $_[X]+1, $_[Y]+1, -fill => $rgb, # -outline => $rgb ); } 1; }}