# The LearningOnline Network with CAPA # Dynamic plot # # $Id: lonplot.pm,v 1.22 2001/12/28 19:04:56 matthew Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # # 12/15/01 Matthew # 12/17 12/18 12/19 12/20 12/21 12/27 12/28 Matthew package Apache::lonplot; use strict; use Apache::File; use Apache::response; use Apache::lonxml; use Apache::edit; sub BEGIN { &Apache::lonxml::register('Apache::lonplot',('plot')); } ## ## Description of data structures: ## ## %plot %key %axis ## -------------------------- ## height title color ## width box xmin ## bgcolor pos xmax ## fgcolor ymin ## transparent ymax ## grid ## border ## font ## align ## ## @labels: $labels[$i] = \%label ## %label: text, xpos, ypos, justify ## ## @curves: $curves[$i] = \%curve ## %curve: name, linestyle, ( function | data ) ## ## $curves[$i]->{'data'} = [ [x1,x2,x3,x4], ## [y1,y2,y3,y4] ] ## ################################################################### ## ## ## Tests used in checking the validitity of input ## ## ## ################################################################### my $int_test = sub {$_[0]=~s/\s+//g;$_[0]=~/^\d+$/}; my $real_test = sub {$_[0]=~s/\s+//g;$_[0]=~/^[+-]?\d*\.?\d*([eE][+-]\d+)?$/}; my $color_test = sub {$_[0]=~s/\s+//g;$_[0]=~/^x[\da-f]{6}$/}; my $onoff_test = sub {$_[0]=~/^(on|off)$/}; my $key_pos_test = sub {$_[0]=~/^(top|bottom|right|left|outside|below| )+$/}; my $sml_test = sub {$_[0]=~/^(small|medium|large)$/}; my $linestyle_test = sub {$_[0]=~/^(lines|linespoints|dots|points|steps)$/}; my $words_test = sub {$_[0]=~s/\s+/ /g;$_[0]=~/^([\w\(\)]+ ?)+$/}; ################################################################### ## ## ## Attribute metadata ## ## ## ################################################################### my %plot_defaults = ( height => { default => 200, test => $int_test, description => 'vertical size of image (pixels)', edit_type => 'entry' }, width => { default => 200, test => $int_test, description => 'horizontal size of image (pixels)', edit_type => 'entry' }, bgcolor => { default => 'xffffff', test => $color_test, description => 'background color of image (xffffff)', edit_type => 'entry' }, fgcolor => { default => 'x000000', test => $color_test, description => 'foreground color of image (x000000)', edit_type => 'entry' }, transparent => { default => 'off', test => $onoff_test, description => '', edit_type => 'on_off' }, grid => { default => 'off', test => $onoff_test, description => '', edit_type => 'on_off' }, border => { default => 'on', test => $onoff_test, description => '', edit_type => 'on_off' }, font => { default => 'medium', test => $sml_test, description => 'Size of font to use', edit_type => 'choice', choices => ['small','medium','large'] }, align => { default => 'left', test => sub {$_[0]=~/^(left|right|center)$/}, description => 'alignment for image in html', edit_type => 'choice', choices => ['left','right','center'] } ); my %key_defaults = ( title => { default => '', test => $words_test, description => 'Title of key', edit_type => 'entry' }, box => { default => 'off', test => $onoff_test, description => 'Draw a box around the key?', edit_type => 'on_off' }, pos => { default => 'top right', test => $key_pos_test, description => 'position of the key on the plot', edit_type => 'choice', choices => ['top left','top right','bottom left','bottom right', 'outside','below'] } ); my %label_defaults = ( xpos => { default => 0, test => $real_test, description => 'x position of label (graph coordinates)', edit_type => 'entry' }, ypos => { default => 0, test => $real_test, description => 'y position of label (graph coordinates)', edit_type => 'entry' }, justify => { default => 'left', test => sub {$_[0]=~/^(left|right|center)$/}, description => 'justification of the label text on the plot', edit_type => 'choice', choices => ['left','right','center'] } ); my %axis_defaults = ( color => { default => 'x000000', test => $color_test, description => 'color of axes (x000000)', edit_type => 'entry' }, xmin => { default => '-10.0', test => $real_test, description => 'minimum x-value shown in plot', edit_type => 'entry' }, xmax => { default => ' 10.0', test => $real_test, description => 'maximum x-value shown in plot', edit_type => 'entry' }, ymin => { default => '-10.0', test => $real_test, description => 'minimum y-value shown in plot', edit_type => 'entry' }, ymax => { default => ' 10.0', test => $real_test, description => 'maximum y-value shown in plot', edit_type => 'entry' }, linestyle => { default => 'points', test => $linestyle_test, description => 'Style of the axis lines', edit_type => 'choice', choices => ['lines','linespoints','dots','points'] } ); my %curve_defaults = ( color => { default => 'x000000', test => $color_test, description => 'color of curve (x000000)', edit_type => 'entry' }, name => { default => '', test => $words_test, description => 'name of curve to appear in key', edit_type => 'entry' }, linestyle => { default => 'lines', test => $linestyle_test, description => 'Style of the axis lines', edit_type => 'choice', choices => ['lines','linespoints','dots','points','steps'] } ); ################################################################### ## ## ## parsing and edit rendering ## ## ## ################################################################### my (%plot,%key,%axis,$title,$xlabel,$ylabel,@labels,@curves); sub start_plot { %plot = undef; %key = undef; %axis = undef; $title = undef; $xlabel = undef; $ylabel = undef; $#labels = -1; $#curves = -1; # my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; if ($target eq 'web') { &Apache::lonxml::register('Apache::lonplot', ('title','xlabel','ylabel','key','axis','label','curve')); push (@Apache::lonxml::namespace,'plot'); ## Always evaluate the insides of the tags my $inside = &Apache::lonxml::get_all_text("/plot",$$parser[-1]); $inside=&Apache::run::evaluate($inside,$safeeval,$$parstack[-1]); &Apache::lonxml::newparser($parser,\$inside); ##------------------------------------------------------- &get_attributes(\%plot,\%plot_defaults,$parstack,$safeeval, $tagstack->[-1]); } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token); $result .= &edit_attributes($target,$token,\%plot_defaults); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args ($token,$parstack,$safeeval,keys %plot_defaults); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); $result.= &Apache::edit::handle_insert(); } } return $result; } sub end_plot { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; pop @Apache::lonxml::namespace; &Apache::lonxml::deregister('Apache::lonplot', ('title','xlabel','ylabel','key','axis','label','curve')); my $result = ''; if ($target eq 'web') { &check_inputs(); # Make sure we have all the data we need ## ## Determine filename my $tmpdir = '/home/httpd/perl/tmp/'; my $filename = $ENV{'user.name'}.'_'.$ENV{'user.domain'}. '_'.time.'_'.$$.'_plot.data'; ## Write the plot description to the file my $fh=Apache::File->new(">$tmpdir$filename"); print $fh &write_gnuplot_file(); close($fh); ## return image tag for the plot $result .= <<"ENDIMAGE"; /cgi-bin/plot.gif?$filename ENDIMAGE } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } return $result; } ##----------------------------------------------------------------- key sub start_key { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; if ($target eq 'web') { &get_attributes(\%key,\%key_defaults,$parstack,$safeeval, $tagstack->[-1]); } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token); $result .= &edit_attributes($target,$token,\%key_defaults); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args ($token,$parstack,$safeeval,keys %key_defaults); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); $result.= &Apache::edit::handle_insert(); } } return $result; } sub end_key { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } return $result; } ##------------------------------------------------------------------- title sub start_title { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; if ($target eq 'web') { $title = &Apache::lonxml::get_all_text("/title",$$parser[-1]); } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_start($target,$token); my $text=&Apache::lonxml::get_all_text("/title",$$parser[-1]); $result.=''. &Apache::edit::editfield('',$text,'',20,1). &Apache::edit::end_table(); } elsif ($target eq 'modified') { my $text=$$parser[-1]->get_text("/function"); $result.=&Apache::edit::modifiedfield($token); } return $result; } sub end_title { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } return $result; } ##------------------------------------------------------------------- xlabel sub start_xlabel { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; if ($target eq 'web') { $xlabel = &Apache::lonxml::get_all_text("/xlabel",$$parser[-1]); } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_start($target,$token); my $text=&Apache::lonxml::get_all_text("/xlabel",$$parser[-1]); $result.=''. &Apache::edit::editfield('',$text,'',20,1). &Apache::edit::end_table(); } elsif ($target eq 'modified') { my $text=$$parser[-1]->get_text("/function"); $result.=&Apache::edit::modifiedfield($token); } return $result; } sub end_xlabel { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } return $result; } ##------------------------------------------------------------------- ylabel sub start_ylabel { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; if ($target eq 'web') { $ylabel = &Apache::lonxml::get_all_text("/ylabel",$$parser[-1]); } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token); my $text = &Apache::lonxml::get_all_text("/ylabel",$$parser[-1]); $result .= ''. &Apache::edit::editfield('',$text,'',20,1). &Apache::edit::end_table(); } elsif ($target eq 'modified') { my $text=$$parser[-1]->get_text("/function"); $result.=&Apache::edit::modifiedfield($token); } return $result; } sub end_ylabel { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } return $result; } ##------------------------------------------------------------------- label sub start_label { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; if ($target eq 'web') { my %label; &get_attributes(\%label,\%label_defaults,$parstack,$safeeval, $tagstack->[-1]); $label{'text'} = &Apache::lonxml::get_all_text("/label",$$parser[-1]); if (! &$words_test($label{'text'})) { # I should probably warn about it, too. $label{'text'} = 'Illegal text'; } push(@labels,\%label); } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token); $result .= &edit_attributes($target,$token,\%label_defaults); my $text = &Apache::lonxml::get_all_text("/label",$$parser[-1]); $result .= ''. &Apache::edit::editfield('',$text,'',20,1). &Apache::edit::end_table(); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args ($token,$parstack,$safeeval,keys %label_defaults); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); $result.= &Apache::edit::handle_insert(); } my $text=$$parser[-1]->get_text("/label"); $result.=&Apache::edit::modifiedfield($token); } return $result; } sub end_label { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } return $result; } ##------------------------------------------------------------------- curve sub start_curve { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; if ($target eq 'web') { my %curve; &get_attributes(\%curve,\%curve_defaults,$parstack,$safeeval, $tagstack->[-1]); push (@curves,\%curve); &Apache::lonxml::register('Apache::lonplot',('function','data')); push (@Apache::lonxml::namespace,'curve'); } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token); $result .= &edit_attributes($target,$token,\%curve_defaults); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args ($token,$parstack,$safeeval,keys %label_defaults); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); $result.= &Apache::edit::handle_insert(); } } return $result; } sub end_curve { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; if ($target eq 'web') { pop @Apache::lonxml::namespace; &Apache::lonxml::deregister('Apache::lonplot',('function','data')); } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } return $result; } ##------------------------------------------------------------ curve function sub start_function { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; if ($target eq 'web') { if (exists($curves[-1]->{'data'})) { &Apache::lonxml::warning('Use of precludes use of . The will be omitted in favor of the declaration.'); delete $curves[-1]->{'data'} ; } $curves[-1]->{'function'} = &Apache::lonxml::get_all_text("/function",$$parser[-1]); } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token); my $text = &Apache::lonxml::get_all_text("/function",$$parser[-1]); $result .= ''. &Apache::edit::editfield('',$text,'',20,1). &Apache::edit::end_table(); } elsif ($target eq 'modified') { # Why do I do this? my $text=$$parser[-1]->get_text("/function"); $result.=&Apache::edit::modifiedfield($token); } return $result; } sub end_function { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { } return $result; } ##------------------------------------------------------------ curve data sub start_data { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; if ($target eq 'web') { if (exists($curves[-1]->{'function'})) { &Apache::lonxml::warning('Use of precludes use of .'. '. The will be omitted in favor of '. 'the declaration.'); delete($curves[-1]->{'function'}); } my $datatext = &Apache::lonxml::get_all_text("/data",$$parser[-1]); $datatext =~ s/\s+/ /g; # Need to do some error checking on the @data array - # make sure it's all numbers and make sure each array # is of the same length. my @data; if ($datatext =~ /,/) { @data = split /,/,$datatext; } else { # Assume it's space seperated. @data = split / /,$datatext; } for (my $i=0;$i<=$#data;$i++) { # Check that it's non-empty if (! defined($data[$i])) { &Apache::lonxml::warning( 'undefined value. Replacing with '. ' pi/e = 1.15572734979092'); $data[$i] = 1.15572734979092; } # Check that it's a number if (! &$real_test($data[$i]) & ! &$int_test($data[$i])) { &Apache::lonxml::warning( 'Bad value of '.$data[$i].' Replacing with '. ' pi/e = 1.15572734979092'); $data[$i] = 1.15572734979092; } } push @{$curves[-1]->{'data'}},\@data; } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token); my $text = &Apache::lonxml::get_all_text("/data",$$parser[-1]); $result .= ''. &Apache::edit::editfield('',$text,'',20,1). &Apache::edit::end_table(); } elsif ($target eq 'modified') { my $text=$$parser[-1]->get_text("/data"); $result.=&Apache::edit::modifiedfield($token); } return $result; } sub end_data { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { } return $result; } ##------------------------------------------------------------------- axis sub start_axis { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; if ($target eq 'web') { &get_attributes(\%axis,\%axis_defaults,$parstack,$safeeval, $tagstack->[-1]); } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token); $result .= &edit_attributes($target,$token,\%axis_defaults); } elsif ($target eq 'modified') { } return $result; } sub end_axis { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args ($token,$parstack,$safeeval,keys %axis_defaults); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); $result.= &Apache::edit::handle_insert(); } } return $result; } ################################################################### ## ## ## Utility Functions ## ## ## ################################################################### ##----------------------------------------------------------- set_defaults sub set_defaults { my ($var,$defaults) = @_; my $key; foreach $key (keys %$defaults) { $var->{$key} = $defaults->{$key}->{'default'}; } } ##------------------------------------------------------------------- misc sub get_attributes{ my ($values,$defaults,$parstack,$safeeval,$tag) = @_; foreach my $attr (keys %{$defaults}) { $values->{$attr} = &Apache::lonxml::get_param($attr,$parstack,$safeeval); if ($values->{$attr} eq '' | !defined($values->{$attr})) { $values->{$attr} = $defaults->{$attr}->{'default'}; next; } my $test = $defaults->{$attr}->{'test'}; if (! &$test($values->{$attr})) { &Apache::lonxml::warning ($tag.':'.$attr.': Bad value.'.'Replacing your value with : ' .$defaults->{$attr}->{'default'} ); $values->{$attr} = $defaults->{$attr}->{'default'}; } } return ; } ##------------------------------------------------------- write_gnuplot_file sub write_gnuplot_file { my $gnuplot_input = ''; my $curve; # Collect all the colors my @Colors; push @Colors, $plot{'bgcolor'}; push @Colors, $plot{'fgcolor'}; push @Colors, (defined($axis{'color'})?$axis{'color'}:$plot{'fgcolor'}); foreach $curve (@curves) { push @Colors, ($curve->{'color'} ne '' ? $curve->{'color'} : $plot{'fgcolor'} ); } # set term $gnuplot_input .= 'set term gif '; $gnuplot_input .= 'transparent ' if ($plot{'transparent'} eq 'on'); $gnuplot_input .= $plot{'font'} . ' '; $gnuplot_input .= 'size '.$plot{'width'}.','.$plot{'height'}.' '; $gnuplot_input .= "@Colors\n"; # grid $gnuplot_input .= 'set grid'.$/ if ($plot{'grid'} eq 'on'); # border $gnuplot_input .= ($plot{'border'} eq 'on'? 'set border'.$/ : 'set noborder'.$/ ); # title, xlabel, ylabel $gnuplot_input .= "set output\n"; $gnuplot_input .= "set title \"$title\"\n" if (defined($title)) ; $gnuplot_input .= "set xlabel \"$xlabel\"\n" if (defined($xlabel)); $gnuplot_input .= "set ylabel \"$ylabel\"\n" if (defined($ylabel)); if (defined(%axis)) { $gnuplot_input .= "set xrange \[$axis{'xmin'}:$axis{'xmax'}\]\n"; $gnuplot_input .= "set yrange \[$axis{'ymin'}:$axis{'ymax'}\]\n"; } # Key if (defined(%key)) { $gnuplot_input .= 'set key '.$key{'pos'}.' '; if ($key{'title'} ne '') { $gnuplot_input .= 'title "'.$key{'title'}.'" '; } $gnuplot_input .= ($key{'box'} eq 'on' ? 'box ' : 'nobox ').$/; } else { $gnuplot_input .= 'set nokey'.$/; } # labels my $label; foreach $label (@labels) { $gnuplot_input .= 'set label "'.$label->{'text'}.'" at '. $label->{'xpos'}.','.$label->{'ypos'}.' '.$label->{'justify'}.$/ ; } # curves $gnuplot_input .= 'plot '; my $datatext = ''; for (my $i = 0;$i<=$#curves;$i++) { $curve = $curves[$i]; $gnuplot_input.= ', ' if ($i > 0); if (exists($curve->{'function'})) { $gnuplot_input.= $curve->{'function'}.' title "'. $curve->{'name'}.'" with '. $curve->{'linestyle'}; } elsif (exists($curve->{'data'})) { $gnuplot_input.= '\'-\' title "'. $curve->{'name'}.'" with '. $curve->{'linestyle'}; my @Data = @{$curve->{'data'}}; my @Data0 = @{$Data[0]}; for (my $i =0; $i<=$#Data0; $i++) { my $dataset; foreach $dataset (@Data) { $datatext .= $dataset->[$i] . ' '; } $datatext .= $/; } $datatext .=$/; } } $gnuplot_input .= $/.$datatext; return $gnuplot_input; } #---------------------------------------------- check_inputs sub check_inputs { ## Note: no inputs, no outputs - this acts only on global variables. ## Make sure we have all the input we need: if (! defined(%plot )) { &set_defaults(\%plot,\%plot_defaults); } if (! defined(%key )) {} # No key for this plot, thats okay if (! defined(%axis )) { &set_defaults(\%axis,\%axis_defaults); } if (! defined($title )) {} # No title for this plot, thats okay if (! defined($xlabel)) {} # No xlabel for this plot, thats okay if (! defined($ylabel)) {} # No ylabel for this plot, thats okay if ($#labels < 0) { } # No labels for this plot, thats okay if ($#curves < 0) { &Apache::lonxml::warning("No curves specified for plot!!!!"); return ''; } my $curve; foreach $curve (@curves) { if (!defined($curve->{'function'})&&!defined($curve->{'data'})){ &Apache::lonxml::warning("One of the curves specified did not contain any or declarations\n"); return ''; } } } #------------------------------------------------ make_edit sub edit_attributes { my ($target,$token,$defaults) = @_; my $result; foreach my $attr (%$defaults) { if ($defaults->{$attr}->{'edit_type'} eq 'entry') { $result .= &Apache::edit::text_arg( $defaults->{$attr}->{'description'}, $attr, $token); } elsif ($defaults->{$attr}->{'edit_type'} eq 'choice') { $result .= &Apache::edit::select_arg( $defaults->{$attr}->{'description'}, $attr, $defaults->{$attr}->{'choices'}, $token); } } return $result; } ################################################################### ## ## ## Insertion functions for editing plots ## ## ## ################################################################### #------------------------------------------------ insert_xxxxxxx sub insert_plot { my $result; # plot attributes $result .= '{'default'}. "\"\n"; } $result .= ">\n"; # Add the components $result .= &insert_key(); $result .= &insert_axis(); $result .= &insert_label(); $result .= &insert_curve(); $result .= &insert_function(); $result .= "\n"; $result .= &insert_curve(); $result .= &insert_data(); $result .= "\n"; # close up the $result .= "\n"; return $result; } sub insert_key { my $result; $result .= ' {'default'}. "\"\n"; } $result .= " />\n"; return $result; } sub insert_axis{ my $result; $result .= ' {'default'}. "\"\n"; } $result .= " />\n"; return $result; } sub insert_label { my $result; $result .= ' \n"; return $result; } sub insert_curve { my $result; $result .= ' {'default'}."\"\n"; } $result .= " >\n"; } sub insert_function { my $result; $result .= "\n"; return $result; } sub insert_data { my $result; $result .= " \n"; $result .= " \n"; return $result; } ##---------------------------------------------------------------------- 1; __END__