File Coverage

blib/lib/VRML/VRML1.pm
Criterion Covered Total %
statement 104 515 20.1
branch 41 312 13.1
condition 5 60 8.3
subroutine 12 50 24.0
pod 1 45 2.2
total 163 982 16.6


line stmt bran cond sub pod time code
1             package VRML::VRML1;
2              
3             ############################## Copyright ##############################
4             # #
5             # This program is Copyright 1996,1998 by Hartmut Palm. #
6             # This program is free software; you can redistribute it and/or #
7             # modify it under the terms of the GNU General Public License #
8             # as published by the Free Software Foundation; either version 2 #
9             # of the License, or (at your option) any later version. #
10             # #
11             # This program is distributed in the hope that it will be useful, #
12             # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14             # GNU General Public License for more details. #
15             # #
16             # If you do not have a copy of the GNU General Public License write #
17             # to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, #
18             # MA 02139, USA. #
19             # #
20             #######################################################################
21              
22             require 5.000;
23             require VRML::VRML1::Standard;
24 1     1   3 use strict;
  1         1  
  1         26  
25 1     1   321 use VRML::Color;
  1         4  
  1         77  
26 1     1   530 use VRML::Base;
  1         2  
  1         46  
27 1     1   6 use vars qw(@ISA $AUTOLOAD $VERSION %supported);
  1         2  
  1         7250  
28             @ISA = qw(VRML::VRML1::Standard);
29              
30             $VERSION = "1.10";
31             %supported = ('quote' => "Live3D|WorldView|Cosmo Player",
32             'L3D_ext' => "Live3D|Cosmo Player", # not WorldView
33             'gzip' => "Live3D|WorldView|Cosmo Player|libcosmoplayer|VRweb|GLview",
34             'target' => "Live3D|WorldView|Cosmo Player|libcosmoplayer|MSVRML2OCX"
35             );
36              
37             my $PI = 3.1415926;
38             my $PI_2 = $PI / 2;
39              
40             #--------------------------------------------------------------------
41              
42             sub new {
43 6     6 1 21 my $class = shift;
44 6         7 my $version = shift;
45 6         13 my $self = new VRML::VRML1::Standard($version);
46 6         7 $self->{'viewpoint'} = [];
47 6         10 return bless $self, $class;
48             }
49              
50             sub supported {
51 0     0 0 0 my $self = shift;
52 0         0 my $feature = shift;
53 0         0 return $self->{'BROWSER'} =~ /$supported{$feature}/i;
54             }
55              
56             #--------------------------------------------------------------------
57             # VRML Grouping Methods
58             #--------------------------------------------------------------------
59             sub at {
60 0     0 0 0 my $self = shift;
61 0         0 $self->transform_begin(@_);
62 0         0 return $self;
63             }
64              
65             sub back {
66 0     0 0 0 my $self = shift;
67 0         0 $self->transform_end();
68 0         0 return $self;
69             }
70              
71             sub begin {
72 0     0 0 0 my $self = shift;
73 0         0 $self->Group($_[0]);
74 0         0 return $self;
75             }
76              
77             sub end {
78 0     0 0 0 my $self = shift;
79 0         0 $self->End($_[0]);
80 0         0 return $self;
81             }
82              
83             sub anchor_begin {
84 0     0 0 0 my $self = shift;
85 0 0       0 return $self->_put(qq{# CALL: ->anchor_begin("Url","description","target=parameter");\n}) unless @_;
86 0         0 my ($url, $description, $parameter) = @_;
87 0         0 my $target = undef;
88 0 0       0 my $quote = $self->{'BROWSER'} =~ /$supported{'quote'}/i ? '\\"' : "'";
89 0 0       0 $description =~ s/"/$quote/g if defined $description;
90 0 0 0     0 if (defined $parameter && $self->{'BROWSER'} =~ /$supported{'target'}/i) {
91 0 0       0 ($target = $1) =~ s/"/$quote/g if ($parameter =~ /target\s*=(.+)/i);
92             }
93 0         0 $self->WWWAnchor($url, $description, $target);
94 0         0 return $self;
95             }
96              
97             sub anchor_end {
98 0     0 0 0 my $self = shift;
99 0         0 $self->End($_[0]);
100 0         0 return $self;
101             }
102              
103             sub collision_begin {
104 0     0 0 0 my $self = shift;
105 0 0       0 $self->_row("CollideStyle { collide TRUE }\n") if $self->{'BROWSER'} =~ /$supported{'L3D_ext'}/i;
106 0         0 $self->_row("DEF CollisionDetection Info { string \"TRUE\" }\n");
107 0         0 return $self;
108             }
109              
110             sub collision_end {
111 0     0 0 0 return shift;
112             }
113              
114             sub group_begin {
115 0     0 0 0 my $self = shift;
116 0         0 $self->Group(@_);
117 0         0 return $self;
118             }
119              
120             sub group_end {
121 0     0 0 0 my $self = shift;
122 0         0 $self->End($_[0]);
123 0         0 return $self;
124             }
125              
126             sub lod_begin {
127 0     0 0 0 my $self = shift;
128 0 0       0 return $self->_put(qq{# CALL: ->lod_begin(range,"center");\n}) unless @_;
129 0         0 my ($range, $center) = @_;
130 0         0 $self->LOD($range,$center);
131 0         0 return $self;
132             }
133              
134             sub lod_end {
135 0     0 0 0 my $self = shift;
136 0         0 $self->End($_[0]);
137 0         0 return $self;
138             }
139              
140             sub switch_begin {
141 0     0 0 0 my $self = shift;
142 0         0 $self->Switch(@_);
143 0         0 return $self;
144             }
145              
146             sub switch_end {
147 0     0 0 0 my $self = shift;
148 0         0 $self->End($_[0]);
149 0         0 return $self;
150             }
151              
152             sub inline {
153 0     0 0 0 my $self = shift;
154 0         0 $self->WWWInline(@_);
155 0         0 return $self;
156             }
157              
158             #--------------------------------------------------------------------
159             # VRML Methods
160             #--------------------------------------------------------------------
161              
162             sub background {
163 0     0 0 0 my $self = shift;
164 0         0 my %hash = @_;
165 0 0       0 $hash{skyColor} = ${$hash{skyColor}}[0] if ref($hash{skyColor}) eq "ARRAY";
  0         0  
166 0         0 $self->backgroundcolor($hash{skyColor});
167 0 0       0 $hash{frontUrl} = ${$hash{frontUrl}}[0] if ref($hash{frontUrl}) eq "ARRAY";
  0         0  
168 0         0 $self->backgroundimage($hash{frontUrl});
169 0         0 return $self;
170             }
171              
172             sub backgroundcolor {
173 0     0 0 0 my $self = shift;
174 0         0 my ($skyColorString) = @_;
175 0         0 my $skyColor;
176 0 0       0 if (defined $skyColorString) {
177 0         0 $skyColor = rgb_color($skyColorString);
178 0         0 $self->def("BackgroundColor")->Info($skyColor)->_trim;
179             }
180 0         0 return $self;
181             }
182              
183             sub backgroundimage {
184 0     0 0 0 my $self = shift;
185 0         0 my $bgimage = shift;
186 0 0       0 if (defined $bgimage) {
187 0         0 $self->def("BackgroundImage")->Info($bgimage)->_trim;
188             }
189 0         0 return $self;
190             }
191              
192             sub title {
193 0     0 0 0 my $self = shift;
194 0 0       0 return $self->_put(qq{# CALL: ->title("string");\n}) unless @_;
195 0         0 my $title = shift;
196 0         0 $self->_row("DEF Title Info { string \"$title\" }\n");
197 0         0 return $self;
198             }
199              
200             sub info {
201 0     0 0 0 my $self = shift;
202 0         0 my $string = shift;
203 0 0       0 return $self->_put(qq{# CALL: ->info("string");\n}) unless @_;
204 0 0       0 my $quote = $self->{'BROWSER'} =~ /$supported{'quote'}/i ? '\\"' : "'";
205 0 0       0 if (defined $string) {
206 0 0       0 $string = join(',',@$string) if ref($string) eq "ARRAY";
207 0         0 $string =~ s/"/$quote/g;
208 0         0 $self->_row("Info { string \"$string\" }\n");
209             }
210 0         0 return $self;
211             }
212              
213             sub worldinfo {
214 0     0 0 0 my $self = shift;
215 0         0 my $title = shift;
216 0         0 my $string = shift;
217 0 0       0 my $quote = $self->{'BROWSER'} =~ /$supported{'quote'}/i ? '\\"' : "'";
218 0 0       0 $self->_row("DEF Title Info { string \"$title\" }\n") if defined $title;
219 0 0       0 if (defined $string) {
220 0 0       0 $string = join(',',@$string) if ref($string) eq "ARRAY";
221 0         0 $string =~ s/"/$quote/g;
222 0         0 $self->_row("Info { string \"$string\" }\n");
223             }
224 0         0 return $self;
225             }
226              
227             sub navigationinfo {
228 0     0 0 0 my $self = shift;
229 0         0 my ($type, $speed, $headlight) = @_;
230 0 0       0 $type = $$type[0] if ref($type) eq "ARRAY";
231 0         0 $self->_row("DEF Viewer Info { string \"$type\" }\n");
232 0         0 $self->_row("DEF ViewerSpeed Info { string \"$speed\" }\n");
233 0 0 0     0 $headlight = defined $headlight && !$headlight ? "FALSE" : "TRUE";
234 0         0 $self->_row("DEF Headlight Info { string \"$headlight\" }\n");
235 0         0 return $self;
236             }
237             #--------------------------------------------------------------------
238              
239             sub viewpoint_begin {
240 0     0 0 0 my $self = shift;
241 0         0 my ($whichChild) = @_;
242 0 0 0     0 $whichChild = (defined $whichChild && $whichChild > 0) ? $whichChild-1 : 0;
243 0         0 my $vrml = $self->{'TAB'}."DEF Cameras Switch {\n";
244 0         0 $vrml .= $self->{'TAB'}." whichChild $whichChild\n";
245 0         0 $self->{'TAB_VIEW'} = $self->{'TAB'}."\t";
246 0 0       0 $self->{'viewpoint_begin'} = $#{$self->{'VRML'}}+1 unless defined $self->{'viewpoint_begin'};
  0         0  
247 0         0 push @{$self->{'viewpoint'}}, $vrml;
  0         0  
248 0         0 return $self;
249             }
250              
251             sub viewpoint_end {
252 0     0 0 0 my $self = shift;
253 0         0 chop($self->{'TAB_VIEW'});
254 0         0 push @{$self->{'viewpoint'}}, $self->{'TAB_VIEW'}."}\n";
  0         0  
255 0         0 splice(@{$self->{'VRML'}}, $self->{'viewpoint_begin'}, 0, @{$self->{'viewpoint'}});
  0         0  
  0         0  
256 0         0 $self->{'viewpoint'} = [];
257 0         0 return $self;
258             }
259              
260             sub viewpoint_auto_set {
261 0     0 0 0 my $self = shift;
262 0         0 my $factor = shift;
263 0 0       0 $factor = 1 unless defined $factor;
264 0 0       0 if (defined $self->{'viewpoint_set'}) {
265 0         0 my $x = ($self->{'Xmax'}+$self->{'Xmin'})/2;
266 0         0 my $y = ($self->{'Ymax'}+$self->{'Ymin'})/2;
267 0         0 my $z = ($self->{'Zmax'}+$self->{'Zmin'})/2;
268 0         0 my $dx = abs($self->{'Xmax'}-$x); # todo: calculate angle
269 0         0 my $dy = abs($self->{'Ymax'}-$y);
270 0         0 my $dz = abs($self->{'Zmax'}-$z);
271 0         0 my $dist = 0;
272 0 0       0 $dist = $dx if $dx > $dist;
273 0 0       0 $dist = $dy if $dy > $dist;
274 0 0       0 $dist = $dz if $dz > $dist;
275 0         0 my $offset = $#{$self->{'viewpoint'}}+1;
  0         0  
276 0         0 $self->viewpoint_set("$x $y $z",$dist*$factor,60);
277 0         0 @_ = splice(@{$self->{'viewpoint'}}, $offset);
  0         0  
278 0         0 splice(@{$self->{'viewpoint'}}, $self->{'viewpoint_set'}, $#_+1, @_);
  0         0  
279             } else {
280 0         0 $self->viewpoint_set(@_);
281             }
282 0         0 return $self;
283             }
284              
285             sub viewpoint_set {
286 0     0 0 0 my $self = shift;
287 0         0 my ($center, $distance, $heightAngle) = @_;
288 0 0       0 $self->{'viewpoint_set'} = $#{$self->{'viewpoint'}}+1 unless defined $self->{'viewpoint_set'};
  0         0  
289 0 0       0 my ($x, $y, $z) = $self->string_to_array($center) if defined $center;
290 0 0       0 my ($dx, $dy, $dz) = defined $distance ? $self->string_to_array($distance) : (0,0,0);
291 0 0       0 $x = 0 unless defined $x;
292 0 0       0 $y = 0 unless defined $y;
293 0 0       0 $z = 0 unless defined $z;
294 0 0       0 $dx = 1 unless defined $dx;
295 0 0       0 $dy = $dx unless defined $dy;
296 0 0       0 $dz = $dx unless defined $dz;
297 0         0 $self->viewpoint("Front", "$x $y ".($z+$dz), "0 0 1 0",$heightAngle);
298 0         0 $self->viewpoint("Right", ($x+$dx)." $y $z", "0 1 0 90",$heightAngle);
299 0         0 $self->viewpoint("Back", "$x $y ".($z-$dz), "0 1 0 180",$heightAngle);
300 0         0 $self->viewpoint("Left", ($x-$dx)." $y $z", "0 1 0 -90",$heightAngle);
301 0         0 $self->viewpoint("Top", "$x ".($y+$dy)." $z", "1 0 0 -90",$heightAngle);
302 0         0 $self->viewpoint("Bottom", "$x ".($y-$dy)." $z", "1 0 0 90",$heightAngle);
303 0         0 return $self;
304             }
305              
306             sub viewpoint {
307 0     0 0 0 my $self = shift;
308 0         0 my ($name, $position, $orientation, $heightAngle) = @_;
309 0 0       0 if (defined $orientation) {
310 0 0       0 if ($orientation !~ /\s/) {
311 0         0 my %val = ("FRONT" => "0 0 1 0", "BACK" => "0 1 0 3.14",
312             "RIGHT" => "0 1 0 1.57", "LEFT" => "0 1 0 -1.57",
313             "TOP" => "1 0 0 -1.57", "BOTTOM" => "1 0 0 1.57");
314 0         0 my $string = uc($orientation);
315 0         0 undef $orientation;
316 0         0 $orientation = $val{$string};
317 0 0 0     0 $orientation .= " # $string" if $orientation && $self->{'DEBUG'};
318             } else {
319 0         0 my ($x,$y,$z,$angle) = $self->string_to_array($orientation);
320 0 0       0 if (defined $angle) {
321 0 0       0 $angle *= $PI/180 if $self->{'CONVERT'};
322 0         0 $orientation = "$x $y $z $angle";
323             }
324             }
325             }
326 0 0 0     0 $heightAngle *= $PI/180 if defined $heightAngle && $self->{'CONVERT'};
327 0 0       0 $self->{'TAB_VIEW'} = $self->{'TAB'} unless $self->{'TAB_VIEW'};
328 0         0 $name =~ s/^#//;
329 0         0 $name =~ s/[\x00-\x20\x22\x23\x27\x2b-\x2e\x30-\x39\x5b-\x5d\x7b\x7d\x7f]/_/g;
330 0         0 push @{$self->{'viewpoint'}}, $self->{'TAB_VIEW'}."DEF $name\n";
  0         0  
331 0         0 $self->PerspectiveCamera($position, $orientation, $heightAngle);
332 0         0 push @{$self->{'viewpoint'}}, pop @{$self->{'VRML'}};
  0         0  
  0         0  
333 0 0       0 unless (defined $self->{'viewpoint_begin'}) {
334 0         0 splice(@{$self->{'VRML'}}, @{$self->{'VRML'}}, 0, @{$self->{'viewpoint'}});
  0         0  
  0         0  
  0         0  
335 0         0 $self->{'viewpoint'} = [];
336             }
337 0         0 return $self;
338             }
339              
340             #--------------------------------------------------------------------
341              
342             sub directionallight {
343 0     0 0 0 my $self = shift;
344 0         0 my ($direction, $intensity, $ambientIntensity, $color, $on) = @_;
345 0 0       0 if (defined $on) { $on = $on ? "TRUE" : "FALSE"; }
  0 0       0  
346 0 0       0 $color = rgb_color($color) if defined $color;
347 0         0 $self->DirectionalLight($direction, $intensity, $color, $on);
348 0         0 return $self;
349             }
350              
351             #--------------------------------------------------------------------
352              
353             sub line {
354 0     0 0 0 my $self = shift;
355 0 0       0 return $self->_put(qq{# CALL: ->line("fromXYZ","toXYZ",radius,"appearance","[x][y][z]");\n}) unless @_;
356 0         0 my ($from,$to,$radius,$appearance,$order) = @_;
357 0         0 my ($x1,$y1,$z1) = $self->string_to_array($from);
358 0         0 my ($x2,$y2,$z2) = $self->string_to_array($to);
359 0         0 my ($t, $r, $length);
360              
361 0 0       0 $x1 = 0 unless $x1;
362 0 0       0 $x2 = 0 unless $x2;
363 0 0       0 $y1 = 0 unless $y1;
364 0 0       0 $y2 = 0 unless $y2;
365 0 0       0 $z1 = 0 unless $z1;
366 0 0       0 $z2 = 0 unless $z2;
367 0         0 my $dx=$x1-$x2;
368 0         0 my $dy=$y1-$y2;
369 0         0 my $dz=$z1-$z2;
370 0 0       0 unless ($radius =~ /^(-?)([0-9]*)(\.?)([0-9]+)$/) { die "'$radius' is not a number\n" };
  0         0  
371 0 0       0 $order = "" unless defined $order;
372 0 0       0 $self->comment('line("'.join('", "',@_).'")') if $self->{'DEBUG'};
373 0         0 $self->Separator;
374 0 0       0 if ($appearance) {
375 0 0 0     0 if (defined $radius && $radius==0 && ($self->{'BROWSER'} =~ /Cosmo Player/)) {
      0        
376 0         0 $self->appearance("$appearance, 0 0 0");
377             } else {
378 0         0 $self->appearance($appearance);
379             }
380             }
381 0 0 0     0 if (defined $radius && $radius>0) {
382 0 0 0     0 if ($dx && $order =~ /x/) {
383 0         0 $self->Separator("line_x");
384 0 0       0 $t = ($x1-($dx/2))." $y1 $z1" if $order =~ /^x$/i;
385 0 0       0 $t = ($x1-($dx/2))." $y1 $z1" if $order =~ /^x../i;
386 0 0       0 $t = ($x1-($dx/2))." $y2 $z1" if $order =~ /yxz/i;
387 0 0       0 $t = ($x1-($dx/2))." $y1 $z2" if $order =~ /zxy/i;
388 0 0       0 $t = ($x1-($dx/2))." $y2 $z2" if $order =~ /..x$/i;
389 0         0 $self->Transform($t,"0 0 1 ".$PI_2);
390 0         0 $self->Cylinder($radius,abs($dx));
391 0         0 $self->End();
392             }
393 0 0 0     0 if ($dy && $order =~ /y/) {
394 0         0 $self->Separator("line_y");
395 0 0       0 $t = "$x1 ".($y1-($dy/2))." $z1" if $order =~ /^y$/i;
396 0 0       0 $t = "$x1 ".($y1-($dy/2))." $z1" if $order =~ /^y../i;
397 0 0       0 $t = "$x2 ".($y1-($dy/2))." $z1" if $order =~ /xyz/i;
398 0 0       0 $t = "$x1 ".($y1-($dy/2))." $z2" if $order =~ /zyx/i;
399 0 0       0 $t = "$x2 ".($y1-($dy/2))." $z2" if $order =~ /..y$/i;
400 0         0 $self->Transform($t);
401 0         0 $self->Cylinder($radius,abs($dy));
402 0         0 $self->End();
403             }
404 0 0 0     0 if ($dz && $order =~ /z/) {
405 0         0 $self->Separator("line_z");
406 0 0       0 $t = "$x1 $y1 ".($z1-($dz/2)) if $order =~ /^z$/i;
407 0 0       0 $t = "$x1 $y1 ".($z1-($dz/2)) if $order =~ /^z../i;
408 0 0       0 $t = "$x1 $y2 ".($z1-($dz/2)) if $order =~ /yzx/i;
409 0 0       0 $t = "$x2 $y1 ".($z1-($dz/2)) if $order =~ /xzy/i;
410 0 0       0 $t = "$x2 $y2 ".($z1-($dz/2)) if $order =~ /..z$/i;
411 0         0 $self->Transform($t,"1 0 0 ".$PI_2);
412 0         0 $self->Cylinder($radius,abs($dz));
413 0         0 $self->End();
414             }
415 0 0       0 unless ($order) {
416 0         0 $length = sqrt($dx*$dx + $dy*$dy + $dz*$dz);
417 0         0 $t = ($x1-($dx/2))." ".($y1-($dy/2))." ".($z1-($dz/2));
418 0         0 $r = "$dx ".($dy+$length)." $dz ".$PI;
419 0         0 $self->Transform($t,$r);
420 0         0 $self->Cylinder($radius,$length);
421             }
422             } else {
423 0         0 $self->MaterialBinding("PER_FACE");
424 0         0 $self->Coordinate3($from,$to);
425 0         0 $self->IndexedLineSet(["0, 1"]);
426             }
427 0         0 $self->End("line");
428 0         0 return $self;
429             }
430              
431             #--------------------------------------------------------------------
432              
433             sub box {
434 1     1 0 2 my $self = shift;
435 1         2 my ($dimension, $appearance) = @_;
436 1         7 my ($width,$height,$depth) = $self->string_to_array($dimension);
437 1 50       7 $self->Group->appearance($appearance) if $appearance;
438 1         6 $self->Cube($width,$height,$depth);
439 1 50       4 $self->End if $appearance;
440 1         6 return $self;
441             }
442              
443             sub cone {
444 1     1 0 2 my $self = shift;
445 1         2 my ($dimension, $appearance) = @_;
446 1         4 my ($radius, $height) = $self->string_to_array($dimension);
447 1 50       5 $self->Group->appearance($appearance) if $appearance;
448 1         6 $self->Cone($radius, $height);
449 1 50       4 $self->End if $appearance;
450 1         4 return $self;
451             }
452              
453             sub cube {
454 1     1 0 2 my $self = shift;
455 1         2 my ($dimension, $appearance) = @_;
456 1         3 my ($width,$height,$depth) = $self->string_to_array($dimension);
457 1 50       4 $height=$width unless defined $height;
458 1 50       2 $depth=$width unless defined $depth;
459 1 50       10 $self->Group->appearance($appearance) if $appearance;
460 1         4 $self->Cube($width,$height,$depth);
461 1 50       4 $self->End if $appearance;
462 1         3 return $self;
463             }
464              
465             sub cylinder {
466 1     1 0 1 my $self = shift;
467 1         2 my ($dimension, $appearance, $top, $side, $bottom) = @_;
468 1         3 my ($radius, $height) = $self->string_to_array($dimension);
469 1         1 my @parts;
470 1 50       5 $self->Group->appearance($appearance) if $appearance;
471 1 50 33     9 if (defined $top || defined $side || defined $bottom) {
      33        
472 0 0       0 $top = 1 unless defined $top;
473 0 0       0 $side = 1 unless defined $side;
474 0 0       0 $bottom = 1 unless defined $bottom;
475 0 0       0 push @parts, "TOP" if $top;
476 0 0       0 push @parts, "SIDES" if $side;
477 0 0       0 push @parts, "BOTTOM" if $bottom;
478             }
479 1         6 $self->Cylinder($radius, $height, @parts);
480 1 50       4 $self->End if $appearance;
481 1         4 return $self;
482             }
483              
484             sub pyramid {
485 0     0 0 0 my $self = shift;
486 0         0 my ($dimension, $appearance) = @_;
487 0         0 my ($width,$height,$depth) = $self->string_to_array($dimension);
488 0         0 my $x_2 = $width/2;
489 0         0 my $y_2 = $height/2;
490 0 0       0 my $z_2 = defined $depth ? $depth/2 : $x_2;
491 0         0 my $color_prop = "";
492 0 0       0 if ($appearance) {
493 0 0       0 if ($appearance =~ /,/) {
494 0         0 $color_prop = [0..4];
495 0         0 $self->MaterialBinding("PER_FACE_INDEXED");
496             }
497 0         0 $self->Group->appearance($appearance);
498             }
499 0         0 $self->Coordinate3("-$x_2 -$y_2 $z_2","$x_2 -$y_2 $z_2","$x_2 -$y_2 -$z_2","-$x_2 -$y_2 -$z_2","0 $y_2 0")
500             ->IndexedFaceSet(["0, 1, 4","1, 2, 4","2, 3, 4","3, 0, 4","0, 3, 2, 1"],$color_prop);
501 0 0       0 $self->End("pyramid") if $appearance;
502 0         0 return $self;
503             }
504              
505             sub sphere {
506 1     1 0 1 my $self = shift;
507 1         2 my ($radius, $appearance) = @_;
508 1 50       4 $self->Group->appearance($appearance) if $appearance;
509 1         7 $self->Sphere($radius);
510 1 50       4 $self->End if $appearance;
511 1         3 return $self;
512             }
513              
514             sub text {
515 1     1 0 1 my $self = shift;
516 1         2 my ($string, $appearance, $font, $align) = @_;
517 1 50       53 my $quote = $self->{'BROWSER'} =~ /$supported{'quote'}/i ? '\\"' : "'";
518 1 50       5 $string =~ s/"/$quote/g if defined $string;
519 1 50 33     6 $self->Group->appearance($appearance) if $appearance || $font;
520 1 50       5 if (defined $string) {
521 1 50       3 if (ref($string)) {
522 0         0 $string = '["'.join('","',@$string).'"]';
523             } else {
524 1         2 $string =~ s/"/$quote/g;
525 1         2 $string = "\"$string\"";
526             }
527             }
528 1 50       2 if (defined $font) {
529 1         6 my ($size, $family, $style) = split(/\s+/,$font,3);
530 1         6 $self->FontStyle($size, $family, $style);
531             }
532 1 50       2 if (defined $align) {
533 0         0 $align =~ s/BEGIN/LEFT/i;
534 0         0 $align =~ s/MIDDLE/CENTER/i;
535 0         0 $align =~ s/END/RIGHT/i;
536             }
537 1         6 $self->AsciiText($string, undef, $align);
538 1 50 33     5 $self->End if $appearance || $font;
539 1         3 return $self;
540             }
541              
542             sub billtext {
543 0     0 0 0 my $self = shift;
544 0         0 $self->Separator;
545 0 0       0 $self->_row("AxisAlignment { fields [SFBitMask alignment] alignment ALIGNAXISXYZ }\n") if $self->{'BROWSER'} =~ /$supported{'L3D_ext'}/i;
546 0         0 $self->text(@_);
547 0         0 $self->End;
548             }
549             #--------------------------------------------------------------------
550              
551             sub appearance {
552 6     6 0 5 my $self = shift;
553 6         8 my ($appearance_list) = @_;
554 6 50       9 return $self unless $appearance_list;
555 6         5 my ($item, $color, $multi_color, $key, $value, @values, $num_color,
556             $texture, %material, $def, $defmat, $deftex);
557             ITEM:
558 6         15 foreach $item (split(/\s*;\s*/,$appearance_list)) {
559 6         11 ($key,$value) = split(/\s*=\s*/,$item,2);
560 6 50       10 unless ($value) { # color only
561 6         5 $value = $key;
562 6         6 $key = "diffuseColor";
563             }
564             MODE: {
565 6 50       4 if ($key eq "d") { $key = "diffuseColor"; last MODE; }
  6         10  
  0         0  
  0         0  
566 6 50       7 if ($key eq "e") { $key = "emissiveColor"; last MODE; }
  0         0  
  0         0  
567 6 50       11 if ($key eq "s") { $key = "specularColor"; last MODE; }
  0         0  
  0         0  
568 6 50       10 if ($key eq "a") { $key = "ambientColor"; last MODE; }
  0         0  
  0         0  
569 6 50       9 if ($key eq "sh") { $key = "shininess"; last MODE; }
  0         0  
  0         0  
570 6 50       8 if ($key eq "tr") { $key = "transparency"; last MODE; }
  0         0  
  0         0  
571 6 50       7 if ($key eq "tex") { $texture = $value; next ITEM; }
  0         0  
  0         0  
572 6 50       7 if ($key eq "def") { $def = $value; next ITEM; }
  0         0  
  0         0  
573 6 50       7 if ($key eq "defmat") { $defmat = $value; next ITEM; }
  0         0  
  0         0  
574 6 50       9 if ($key eq "deftex") { $deftex = $value; next ITEM; }
  0         0  
  0         0  
575             }
576 6 50       13 if ($key eq "diffuseColor" | $key eq "emissiveColor" | $key eq "specularColor" | $key eq "ambientColor") {
577 6 50       8 if ($value =~ /,/) { # multi color field
578 0         0 foreach $color (split(/\s*,\s*/,$value)) {
579 0         0 ($num_color,$color) = rgb_color($color);
580 0         0 $value = $num_color;
581 0 0 0     0 $value .= " # $color" if $color && $self->{'DEBUG'};
582 0         0 push @values, $value;
583             }
584 0         0 $material{$key} = [@values];
585 0         0 $multi_color = 1;
586             } else {
587 6         12 ($num_color,$color) = rgb_color($value);
588 6         6 $value = $num_color;
589 6 50 33     17 $value .= " # $color" if $color && $self->{'DEBUG'};
590 6         15 $material{$key} = $value;
591             }
592             } else {
593 0         0 $material{$key} = $value;
594             }
595             }
596 6 50       9 $self->def($def)->group_begin if $def;
597 6 50       7 $self->def($defmat) if $defmat;
598 6         25 $self->Material(%material);
599             # $self->MaterialBinding("PER_FACE_INDEXED") if $multi_color;
600 6 50       14 $self->def($deftex) if $deftex;
601 6 50       7 $self->Texture2($self->string_to_array($texture)) if defined $texture;
602 6 50       7 $self->group_end if $def;
603 6         9 return $self;
604             }
605              
606              
607             #--------------------------------------------------------------------
608              
609             sub transform_begin {
610 0     0 0   my $self = shift;
611 0 0         return $self->Separator unless @_;
612 0           my (@transform_list) = @_;
613 0           my @transform;
614 0 0         if (ref($transform_list[0])) {
615 0           @transform = @{$transform_list[0]};
  0            
616             } else {
617 0           @transform = @transform_list;
618             }
619 0           my ($item, $key, $value);
620 0           my ($x,$y,$z,$angle,$t,$r,$s,$o,$c);
621 0           foreach $item (@transform) {
622 0 0         ($key,$value) = ref($item) ? @$item : split(/\s*=\s*/,$item);
623 0 0         unless ($value) {
624 0           ($x,$y,$z) = split(/\s/,$key);
625 0 0         $x=0 unless defined $x;
626 0 0         $y=0 unless defined $y;
627 0 0         $z=0 unless defined $z;
628 0           $t = "$x $y $z";
629             }
630             MODE: {
631 0 0         if ($key eq "t") { $t = $value; last MODE; }
  0            
  0            
  0            
632 0 0 0       if ($key eq "r" || $key eq "rotation") { $r = $value; last MODE; }
  0            
  0            
633 0 0         if ($key eq "c") { $c = $value; last MODE; }
  0            
  0            
634 0 0         if ($key eq "s") { $s = $value; last MODE; }
  0            
  0            
635 0 0         if ($key eq "so") { $o = $value; last MODE; }
  0            
  0            
636             }
637 0 0 0       if ($key eq "r" || $key eq "rotation") {
638 0           ($x,$y,$z,$angle) = split(/\s/,$value);
639 0 0         unless (defined $angle) { # if one param its the angle
640 0           $angle=$x;
641 0           $x=0;
642 0           $y=0;
643 0           $z=1;
644             }
645 0 0         $angle *= $PI/180 if $self->{'CONVERT'};
646 0           $r = "$x $y $z $angle";
647             }
648             }
649 0           $self->Separator->Transform($t,$r,$s,$o,$c);
650 0           return $self;
651             }
652              
653             sub transform_end {
654 0     0 0   my $self = shift;
655 0           $self->End();
656 0           return $self;
657             }
658              
659             #--------------------------------------------------------------------
660              
661             sub sound {
662 0     0 0   my $self = shift;
663 0 0         return $self->_put(qq{# CALL: ->sound("url", "description", ...)\n}) unless @_;
664 0           my ($url, $description, $location, $direction, $intensity, $loop, $pitch, $pause) = @_;
665 0 0 0       $loop = defined $loop && $loop ? "TRUE" : "FALSE";
666 0           $self->DirectedSound($url, $description, $location, $direction, $intensity, 100, 0, 0, 0, $loop);
667 0           return $self;
668             }
669              
670             #--------------------------------------------------------------------
671              
672             sub def {
673 0     0 0   my $self = shift;
674 0           my ($name, $code) = @_;
675 0 0         $name = "_DEF_".(++$self->{'ID'}) unless defined $name;
676 0           $self->DEF($name);
677 0 0         if (defined $code) {
678 0 0         if (ref($code) eq "CODE") {
679 0           $self->{'TAB'} .= "\t";
680 0           my $pos = $#{$self->{'VRML'}}+1;
  0            
681 0           &$code;
682 0           $self->_trim($pos);
683 0           chop($self->{'TAB'});
684             } else {
685 0           $self->_put($code);
686             }
687             }
688 0           return $self;
689             }
690              
691             sub use {
692 0     0 0   my $self = shift;
693 0 0         return $self->_put(qq{# CALL: ->use("name");\n}) unless @_;
694 0           my ($name) = @_;
695 0           $self->USE($name);
696 0           return $self;
697             }
698              
699             sub AUTOLOAD {
700 0     0     my $self = shift;
701 0           $AUTOLOAD =~ s/.*:://g;
702 0 0         unless ($AUTOLOAD =~ /^route|sensor$|^interpolator$|^elevationgrid$|^indexedfaceset$/) {
703 0           my ($package, $filename, $line) = caller;
704 0           die qq{Unknown method "$AUTOLOAD" at $filename line $line.\n};
705             }
706 0           return $self->_row(qq{### "$AUTOLOAD" is not supported by VRML::VRML1\n});
707             }
708              
709             1;
710              
711             __END__