# XXXXX print methods need to be changed to # reduce memory consumption ################### # # VRMLProto package PDL::Graphics::VRMLProto; use Exporter; use PDL::Core ''; @ISA = qw/ Exporter /; @EXPORT = qw/ vrp fv3f fmstr /; sub new { my $type = shift; my ($name,$fields,$node) = @_; my $this = bless {},$type; $this->{Name} = $name; $this->{Fields} = $fields; $this->{Node} = $node; return $this; } sub vrp { return PDL::Graphics::VRMLProto->new(@_); } sub fv3f { my ($name,$def) = @_; return ["field SFVec3f", "$name", "$def"]; } sub fmstr { my ($name,$def) = @_; return ["field MFString", "$name", defined $def ? "$def" : "[]"]; } sub to_text { my $this = shift; my $text = "PROTO $this->{Name} [\n"; for (@{$this->{Fields}}) { $text .= " $_->[0] $_->[1]\t$_->[2]\n"; } $text .= "]\n{\n"; $text .= $this->{Node}->to_text; return $text . "}\n"; } ##################### # # VRMLNode package PDL::Graphics::VRMLNode; use Exporter; @ISA = qw/ Exporter /; @EXPORT = qw/ vrn vrml3v /; @EXPORT_OK = qw/ tabs postfix prefix /; sub vrn { return PDL::Graphics::VRMLNode->new(@_); } sub new { my $type = shift; my $title = shift; my $this = bless {},$type; $this->{'Container'} = {}; $this->{'Title'} = $title; $this->add(@_); return $this; } sub add { my ($this,%items) = @_; for (keys %items) { $this->{Container}{$_} = $items{$_}; } return $this; } sub add_children { my ($this) = shift; for(@_) { push @{$this->{Container}{children}}, $_; } } sub to_text { my $this = shift; my $level = $#_ > -1 ? shift : 1; my $text = $this->prefix($level); my($k,$v); while (($k,$v) = each %{$this->{Container}}) { $text .= tabs($level) . "$k". (ref $v ? ref $v eq "ARRAY" ? $this->array_out($v,$level+1) : (" ".$v->to_text($level+1)) : "\t$v\n"); } return $text.$this->postfix($level); } sub array_out { my ($this,$array,$level) = @_; my $text = " [\n"; for (@$array) { $text .= tabs($level) . (ref $_ ? $_->to_text($level+1) : "$_,\n") } $text .= tabs($level-1) . "]\n"; return $text; } sub prefix { return $_[0]->{Title}." {\n"; } sub postfix { return "\t"x($_[1]-1)."}\n"; } sub tabs { return "\t"x$_[0]; } sub vrml3v { my $list = shift; return sprintf '%.3f %.3f %.3f', @{$list}[0..2]; } ################# # # VRMLPdlNode package PDL::Graphics::VRMLPdlNode; @ISA = qw/ PDL::Graphics::VRMLNode /; use PDL::Lite; use PDL::Core qw(barf); use PDL::Dbg; PDL::Graphics::VRMLNode->import(qw/tabs vrml3v postfix prefix/); sub new { my ($type,$points,$colors,$options) = @_; my $this = bless {},$type; $this->{'Points'} = $points; $this->{'Colors'} = $colors; $this->checkoptions($options); return $this; } sub checkoptions { my ($this,$options) = @_; my $aopts = $this->getvopts(); for (keys %$aopts) { if (!defined $options->{$_}) { $this->{$_} = $aopts->{$_}; } else { $this->{$_} = delete $options->{$_}; } } if (keys %$options) { barf "Invalid options left: ".(join ',',%$options); } } sub getvopts { my ($this) = @_; return {Title => 'PointSet', PerVertex => 0, Lighting => 0, Surface => 0, Lines => 1, Smooth => 0, IsLattice => 0, DefColors => 0}; } sub to_text { my $this = shift; my $level = $#_ > -1 ? shift : 1; my $text = $this->prefix($level); my ($vtxt,$vidx,$ctxt,$extra,$useidx) = ("","","","",0); if ($this->{Title} eq 'PointSet') { coords($this->{Points},$this->{Colors},\$vtxt,\$ctxt,tabs($level+2)); } elsif ($this->{Title} eq 'IndexedLineSet') { my @dims = $this->{Points}->dims; shift @dims; my $cols = $this->{Colors}; my $seq = PDL->sequence(@dims); require PDL::Dbg; local $PDL::debug = 0; $cols = pdl(0,0,0)->dummy(1)->dummy(2)->px if $this->{IsLattice} && $this->{Surface} && $this->{Lines}; lines($this->{Points},$cols,$seq, \$vtxt,\$ctxt,\$vidx,tabs($level+1)); lines($this->{Points}->xchg(1,2),$cols->xchg(1,2), $seq->xchg(0,1),undef,\$ctxt,\$vidx, tabs($level+1)) if $this->{IsLattice}; $useidx = 1; } elsif ($this->{Title} eq 'IndexedFaceSet') { my @dims = $this->{Points}->dims; shift @dims; my @sls1 = ("0:-2,0:-2", "1:-1,0:-2", "0:-2,1:-1"); my @sls2 = ("1:-1,1:-1", "0:-2,1:-1", "1:-1,0:-2" ); my $seq = PDL->sequence(@dims); coords($this->{Points},$this->{Colors},\$vtxt,\$ctxt,tabs($level+2)); triangles((map {$seq->slice($_)} @sls1),\$vidx,tabs($level+1)); triangles((map {$seq->slice($_)} @sls2),\$vidx,tabs($level+1)); $useidx = 1; $extra = tabs($level)."colorPerVertex\tTRUE\n". tabs($level)."solid\tFALSE\n"; $extra .= tabs($level)."creaseAngle\t3.14\n" if $this->{Smooth}; } $text .= vprefix('coord',$level).$vtxt.vpostfix('coord',$level); $text .= vprefix('index',$level).$vidx.vpostfix('index',$level) if $useidx; $text .= vprefix('color',$level).$ctxt.vpostfix('color',$level) unless $this->{DefColors}; return $text.$extra.$this->postfix($level); } sub vprefix { my ($type,$level) = @_; return tabs($level) . "coord Coordinate {\n" . tabs($level+1) . "point [\n" if $type eq 'coord'; return tabs($level) . "color Color {\n" . tabs($level+1) . "color [\n" if $type eq 'color'; return tabs($level) . "coordIndex [\n" if $type eq 'index'; } sub vpostfix { my ($type,$level) = @_; return tabs($level+1)."]\n".tabs($level)."}\n" unless $type eq 'index'; return tabs($level)."]\n"; } PDL::thread_define 'coords(vertices(n=3); colors(n)) NOtherPars => 3', PDL::over { ${$_[2]} .= $_[4] . sprintf("%.3f %.3f %.3f,\n",$_[0]->list); ${$_[3]} .= $_[4] . sprintf("%.3f %.3f %.3f,\n",$_[1]->list); }; PDL::thread_define 'v3array(vecs(n=3)) NOtherPars => 2', PDL::over { ${$_[1]} .= $_[2] . sprintf("%.3f %.3f %.3f,\n",$_[0]->list); }; PDL::thread_define 'lines(vertices(n=3,m); colors(n,m); index(m))'. 'NOtherPars => 4', PDL::over { my ($lines,$cols,$index,$vt,$ct,$it,$sp) = @_; v3array($lines,$vt,$sp."\t") if defined $vt; v3array($cols,$ct,$sp."\t") if defined $ct; $$it .= $sp.join(',',$index->list).",-1,\n" if defined $it; }; PDL::thread_define 'triangles(inda();indb();indc()), NOtherPars => 2', PDL::over { ${$_[3]} .= $_[4].join(',',map {$_->at} @_[0..2]).",-1,\n"; }; ##################### # # VRML package PDL::Graphics::VRML; use PDL::Core ''; %PDL::Graphics::VRML::Protos = (); sub new { my ($type,$title,$info) = @_; my $this = bless {},$type; $this->{Header} = '#VRML V2.0 utf8'; $this->{Info} = new PDL::Graphics::VRMLNode('WorldInfo', 'title' => $title, 'info' => $info); $this->{NaviInfo} = new PDL::Graphics::VRMLNode('NavigationInfo', 'type' => '["EXAMINE", "ANY"]'); $this->{Protos} = {}; $this->{Uses} = {}; $this->{Scene} = undef; return $this; } sub register_proto { my ($this,@protos) = @_; for (@protos) { barf "proto already registered" if defined $PDL::Graphics::VRML::Protos{$_->{Name}}; $PDL::Graphics::VRML::Protos{$_->{Name}} = $_; } } sub set_vrml { print "set_vrml ",ref($_[0]),"\n"; $_[0]->{Scene} = $_[1]; } sub uses { $_[0]->{Uses}->{$_[1]} = 1; } sub ensure_protos { my $this = shift; for (keys %{$this->{Uses}}) { barf "unknown Prototype $_" unless defined $PDL::Graphics::VRML::Protos{$_}; delete $this->{Uses}->{$_}; $this->add_proto($PDL::Graphics::VRML::Protos{$_}); } } sub add_proto { my ($this,$proto) = @_; $this->{Protos}->{$proto->{Name}} = $proto unless exists $this->{Protos}->{$proto->{Name}}; return $this; } sub print { my $this = shift; if ($#_ > -1) { my $file = ($_[0] =~ /^\s*[|>]/ ? '' : '>') .$_[0]; open VRML,"$file" or barf "can't open $file"; } else { *VRML = *STDOUT } print VRML "$this->{Header}\n"; print VRML $this->{Info}->to_text; print VRML $this->{NaviInfo}->to_text; for (keys %{$this->{Protos}}) { print VRML $this->{Protos}->{$_}->to_text } barf "no scene hierarchy" unless defined $this->{Scene}; print VRML $this->{Scene}->to_text; close VRML if $#_ > -1; } 1;