################################################### # # ArcBall.pm # # From Graphics Gems IV. # # This is an example of the controller class: # the routines set_wh and mouse_moved are the standard routines. # # This needs a faster implementation (?) package PDL::Graphics::TriD::QuaterController; use base qw(PDL::Graphics::TriD::ButtonControl); use fields qw /Inv Quat/; sub new { my($type,$win,$inv,$quat) = @_; my $this = $type->SUPER::new($win); $this->{Inv} = $inv; $this->{Quat} = (defined($quat) ? $quat : new PDL::Graphics::TriD::Quaternion(1,0,0,0)); $win->add_resizecommand(sub {$this->set_wh(@_)}); return $this; } sub xy2qua { my($this,$x,$y) = @_; $x -= $this->{W}/2; $y -= $this->{H}/2; $x /= $this->{SC}; $y /= $this->{SC}; $y = -$y; return $this->normxy2qua($x,$y); } sub mouse_moved { my($this,$x0,$y0,$x1,$y1) = @_; # Copy the size of the owning viewport to our size, in case it changed... $this->{H} = $this->{Win}->{H}; $this->{W} = $this->{Win}->{W}; if($PDL::Graphics::TriD::verbose) { print "QuaterController: mouse-moved: $this: $x0,$y0,$x1,$y1,$this->{W},$this->{H},$this->{SC}\n" if($PDL::Graphics::TriD::verbose); if($PDL::Graphics::TriD::verbose > 1) { print "\tthis is:\n"; foreach my $k(sort keys %$this) { print "\t$k\t=>\t$this->{$k}\n"; } } } # Convert both to quaternions. my ($qua0,$qua1) = ($this->xy2qua($x0,$y0),$this->xy2qua($x1,$y1)); # print "ARCBALLQ: ",(join ', ',@$qua0)," ",(join ', ',@$qua1),"\n"; my $arc = $qua1->multiply($qua0->invert()); # my $arc = $qua0->invert()->multiply($qua1); if($this->{Inv}) { $arc->invert_rotation_this(); } $this->{Quat}->set($arc->multiply($this->{Quat})); # print "ARCBALLQ: ",(join ', ',@$arc)," ",(join ', ',@{$this->{Quat}}),"\n"; # $this->{Quat}->set($this->{Quat}->multiply($arc)); 1; # signals a refresh } # # Original ArcBall # package PDL::Graphics::TriD::ArcBall; use base qw/PDL::Graphics::TriD::QuaterController/; # x,y to unit quaternion on the sphere. sub normxy2qua { my($this,$x,$y) = @_; my $dist = sqrt ($x ** 2 + $y ** 2); if($dist > 1.0) {$x /= $dist; $y /= $dist; $dist = 1.0;} my $z = sqrt(1-$dist**2); return PDL::Graphics::TriD::Quaternion->new(0,$x,$y,$z); } # Tjl's version: a cone - more even change of package PDL::Graphics::TriD::ArcCone; use base qw/PDL::Graphics::TriD::QuaterController/; # x,y to unit quaternion on the sphere. sub normxy2qua { my($this,$x,$y) = @_; my $dist = sqrt ($x ** 2 + $y ** 2); if($dist > 1.0) {$x /= $dist; $y /= $dist; $dist = 1.0;} my $z = 1-$dist; my $qua = PDL::Graphics::TriD::Quaternion->new(0,$x,$y,$z); $qua->normalize_this(); return $qua; } # Tjl's version2: a bowl -- angle is proportional to displacement. package PDL::Graphics::TriD::ArcBowl; use base qw/PDL::Graphics::TriD::QuaterController/; # x,y to unit quaternion on the sphere. sub normxy2qua { my($this,$x,$y) = @_; my $dist = sqrt ($x ** 2 + $y ** 2); if($dist > 1.0) {$x /= $dist; $y /= $dist; $dist = 1.0;} my $z = cos($dist*3.142/2); my $qua = PDL::Graphics::TriD::Quaternion->new(0,$x,$y,$z); $qua->normalize_this(); return $qua; } 1;