--- loncom/xml/lonplot.pm 2001/12/18 15:33:47 1.2 +++ loncom/xml/lonplot.pm 2001/12/19 18:27:30 1.6 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Dynamic plot # -# $Id: lonplot.pm,v 1.2 2001/12/18 15:33:47 matthew Exp $ +# $Id: lonplot.pm,v 1.6 2001/12/19 18:27:30 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,7 +25,8 @@ # # http://www.lon-capa.org/ # -# 2/21 Guy +# 12/15/01 Matthew +# 12/18 Matthew package Apache::lonplot; use strict; use Apache::response; @@ -36,7 +37,6 @@ sub BEGIN { &Apache::lonxml::register('Apache::lonplot',('plot')); } - ## ## Tests used in checking the validitity of input ## @@ -47,7 +47,7 @@ my $onoff_test = sub {$_[0]=~/^(on|o 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]=~/^((\w+\b*)+$/}; ## ## Default values for attributes of elements ## @@ -55,34 +55,33 @@ my %plot_defaults = ( height => {default => 200, test => $int_test }, width => {default => 200, test => $int_test }, - bgcolor => {default => "xffffff", test => $color_test}, - fgcolor => {default => "x000000", test => $color_test}, - transparent => {default => "off", test => $onoff_test}, - grid => {default => "off", test => $onoff_test}, - border => {default => "on" , test => $onoff_test}, - font => {default => "medium", test => $sml_test } + bgcolor => {default => 'xffffff', test => $color_test}, + fgcolor => {default => 'x000000', test => $color_test}, + transparent => {default => 'off', test => $onoff_test}, + grid => {default => 'off', test => $onoff_test}, + border => {default => 'on', test => $onoff_test}, + font => {default => 'medium', test => $sml_test } ); my %key_defaults = ( - title => { default => "on" , test => $onoff_test }, - box => { default => "off" , test => $onoff_test }, - pos => { default => "top right" , test => $key_pos_test} + title => { default => '', test => $words_test }, + box => { default => 'off', test => $onoff_test }, + pos => { default => 'top right', test => $key_pos_test} ); my %label_defaults = ( xpos => {default => 0, test => $real_test }, ypos => {default => 0, test => $real_test }, - color => {default => "x000000", test => $color_test }, - justify => {default => "left", + justify => {default => 'left', test => sub {$_[0]=~/^(left|right|center)$/}} ); my %axis_defaults = ( - color => {default => "x000000", test => $color_test}, - thickness => {default => 1, test => $int_test }, + color => {default => 'x000000', test => $color_test}, +# thickness => {default => 1, test => $int_test }, xmin => {default => -10.0, test => $real_test }, xmax => {default => 10.0, test => $real_test }, ymin => {default => -10.0, test => $real_test }, @@ -91,9 +90,9 @@ my %axis_defaults = my %curve_defaults = ( - color => {default => "x000000", test => $color_test }, - name => {default => "x000000", test => sub {$_[0]=~/^[\w ]*$/} }, - linestyle => {default => "lines", test => $linestyle_test } + color => {default => 'x000000', test => $color_test }, + name => {default => 'x000000', test => sub {$_[0]=~/^[\w ]*$/} }, + linestyle => {default => 'lines', test => $linestyle_test } ); ## @@ -105,42 +104,43 @@ sub start_plot { %plot = ''; %key=''; %axis=''; $title=''; $xlabel=''; $ylabel=''; @labels = ''; @curves=''; - + # my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; &Apache::lonxml::register('Apache::plot', ('title','xlabel','ylabel','key','axis','label','curve')); push (@Apache::lonxml::namespace,'plot'); - ##------------------------------------------------------- - ## How do I do this? I need to "eval" and I need to keep the info - ## available for the parser. - ## + ## Always evaluate the insides of the tags my $inside = &Apache::lonxml::get_all_text("/plot",$$parser[-1]); - my $eval=&Apache::lonxml::get_param('eval',$parstack,$safeeval); - if ($eval eq 'on') { - $inside=&Apache::run::evaluate($inside,$safeeval,$$parstack[-1]); - #&Apache::lonxml::debug("M is evaulated to:$inside:"); - } + $inside=&Apache::run::evaluate($inside,$safeeval,$$parstack[-1]); + &Apache::lonxml::newparser($parser,\$inside); ##------------------------------------------------------- &get_attributes(\%plot,\%plot_defaults,$parstack,$safeeval,'plot'); + if ($target eq 'web') { + } return ''; } sub end_plot { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; pop @Apache::lonxml::namespace; - my $result; - ## Determine filename - my $tmpdir = '/home/httpd/perl/tmp/'; - my $filename = $tmpdir.$ENV{'user.name'}.'_'.$ENV{'user.domain'}. - '_plot.data'; - my $usersees=md5_base64($filename.'_'.$ENV{'REMOTE_ADDR'}); - - ## Write the plot description to the file - my $fh=&Apache::File->new('/home/httpd/perl/tmp/'.$realname); - ## Ack! - ## return image tag for the plot - $result = 'new('/home/httpd/perl/tmp/'.$realname); + &write_gnuplot_file($fh); + ## return image tag for the plot + $result = '{'text'} = &Apache::lonxml::get_all_text("/label",$$parser[-1]); - push(@labels,$label); + push(@labels,\%label); + if ($target eq 'web') { + # This routine should never return anything. + } return $result; } sub end_label { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; + if ($target eq 'web') { + # This routine should never return anything. + } return $result; } @@ -217,50 +247,83 @@ sub end_label { sub start_curve { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - my $curve = &newhashref(); - &get_attributes($curve,\%curve_defaults,$parstack,$safeeval,'curve'); + my %curve; + &get_attributes($curve,\%curve_defaults,$parstack,$safeeval,$tagstack); push (@curves,$curve); - - &Apache::lonxml::register('Apache::plot',('function','data')); + &Apache::lonxml::register('Apache::lonplot',('function','data')); push (@Apache::lonxml::namespace,'curve'); - + if ($target eq 'web') { + # This routine should never return anything. + } return $result; } sub end_curve { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; + pop @Apache::lonxml::namespace; + &Apache::lonxml::deregister('Apache::lonplot',('function','data')); + if ($target eq 'web') { + # This routine should never return anything. + } return $result; } - ##------------------------------------------------------------ curve function sub start_function { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - + 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]); + if ($target eq 'web') { + # This routine should never return anything. + } return $result; } sub end_function { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; + if ($target eq 'web') { + # This routine should never return anything. + } return $result; } - ##------------------------------------------------------------ curve data sub start_data { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - push( @{$curves[-1]->{'data'}}, - &Apache::lonxml::get_all_text("/data",$$parser[-1])); + 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+$|^\s+)//g; + $datatext =~ s/\s+/ /g; + if ($datatext !~ /^(([+-]?\d*\.?\d*)[, ]?)+$/) { + &Apache::lonxml::warning('Malformed data: '.$datatext); + $datatext = ''; + } + # 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 = split /[, ]/,$datatext; + push( @{$curves[-1]->{'data'}},\@data; + if ($target eq 'web') { + # This routine should never return anything. + } return $result; } sub end_data { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; + if ($target eq 'web') { + # This routine should never return anything. + } return $result; } @@ -268,22 +331,23 @@ sub end_data { sub start_axis { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - &get_attributes(\%axis,\%label_defaults,$parstack,$safeeval,'axis'); + &get_attributes(\%axis,\%label_defaults,$parstack,$safeeval,$tagstack); + if ($target eq 'web') { + # This routine should never return anything. + } return $result; } sub end_axis { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; + if ($target eq 'web') { + # This routine should never return anything. + } return $result; } ##------------------------------------------------------------------- misc -sub newhashref{ - my %hash; - return \%hash; -} - sub get_attributes{ %values = %{shift}; %defaults = %{shift}; @@ -293,13 +357,97 @@ sub get_attributes{ my $attr; foreach $attr (keys %defaults) { $values{$attr} = &Apache::lonxml::get_param($attr,$parstack,$safeeval); + if ($values{$attr} eq '' | !defined($values{$attr})) { + $values{$attr} = $defaults{$attr}; + next; + } my $test = $defaults{$attr}->{'test'}; if (! &$test($values{$attr})) { - &Apache::lonxml::warning($tag.':'.$attr.': Bad value. Replacing your value with : '.$defaults{$attr}); + &Apache::lonxml::warning + ($tag.':'.$attr.': Bad value.'.'Replacing your value with : ' + .$defaults{$attr} ); $values{$attr} = $defaults{$attr}; } return ; } +sub write_gnuplot_file { + my $fh = shift; + my $gnuplot_input = ''; + # Collect all the colors + my @Colors; + push @Colors, $plot{'bgcolor'}; + push @Colors, $plot{'fgcolor'}; + push @Colors, $axis{'color'}; + push @Colors, $axis{'color'}; + 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'} . ' '; + $gnuplot_input .= $plot{'height'} . ' '; + $gnuplot_input .= "@Colors\n"; + # title, xlabel, ylabel + { + $gnuplot_input .<<"ENDLABELS"; +set title $title->{'text'} +set xlabel $xlabel->{'text'} +set ylabel $ylabel->{'text'} +set xrange $axis->{'xmin'}:$axis->{'xmax'} +set yrange $axis->{'ymin'}:$axis->{'ymax'} +ENDLABELS + } + # Key + if (defined($key{'pos'})) { + $gnuplot_input .= 'set key '.$key->{'pos'}.' '; + $gnuplot_input .= ($key->{'box'} eq 'on' ? 'box ' : 'nobox '); + if ($key->{'title'} ne '') { + $gnuplot_input .= 'title "'$key->{'title'}.'"\n'; + } else { + $gnuplot_input .= '\n'; + } + } else { + $gnuplot_input .= 'set nokey\n'; + } + # axis + $gnuplot_input .= 'set xrange ['.$axis{'xmin'}.':'.$axis{'xmin'}.']\n'; + $gnuplot_input .= 'set yrange ['.$axis{'ymin'}.':'.$axis{'ymin'}.']\n'; + # labels + foreach $label (@labels) { + $gnuplot_input .= 'set label "'.$label->{'text'}.'" at '. + $label->{'x'}.','.$label->{'y'}.'\n'; + } + # curves + $gnuplot_input .= 'plot '; + my $datatext = ''; + foreach $curve (@curves) { + if (exists($curve->{'function'})) { + $gnuplot_input.= $curve->{'function'}.' with '.$curve->{'linestyle'}; + } elsif (exists($curve->{'data'})) { + $gnuplot_input.= '\'-\' with '.$curve->{'linestyle'}; + my @Data = @{$curve->{'data'}}; + for ($i =0; $i<=$#Data; $i++) { + foreach $dataset (@Data) { + $datatext .= $dataset[$i] . ' '; + } + $datatext .='\n'; + } + $datatext .='\n'; + } + } + $gnuplot_input .= $datatext; + print $fh $gnuplot_input; +} + 1; __END__ + + + + +