--- loncom/xml/lonplot.pm 2001/12/27 22:30:01 1.20 +++ loncom/xml/lonplot.pm 2002/01/01 19:34:25 1.29 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Dynamic plot # -# $Id: lonplot.pm,v 1.20 2001/12/27 22:30:01 matthew Exp $ +# $Id: lonplot.pm,v 1.29 2002/01/01 19:34:25 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,7 +26,7 @@ # http://www.lon-capa.org/ # # 12/15/01 Matthew -# 12/17 12/18 12/19 12/20 12/21 12/27 Matthew +# 12/17 12/18 12/19 12/20 12/21 12/27 12/28 12/30 Matthew package Apache::lonplot; use strict; @@ -63,10 +63,34 @@ sub BEGIN { ## $curves[$i]->{'data'} = [ [x1,x2,x3,x4], ## [y1,y2,y3,y4] ] ## -##------------------------------------------------------------ -## -## Tests used in checking the validitity of input -## + +################################################################### +## ## +## Tests used in checking the validitity of input ## +## ## +################################################################### + +my %linestyles = + ( + lines => 2, # Maybe this will be used in the future + linespoints => 2, # to check on whether or not they have + dots => 2, # supplied enough fields + points => 2, # to use the given line style. But for + steps => 2, # now there are more important things + fsteps => 2, # for me to deal with. + histeps => 2, + errorbars => 2, + xerrorbars => 2, + yerrorbars => 2, + xyerrorbars => 2, + boxes => 2, + boxerrorbars => 2, + boxxyerrorbars => 2, + financebars => 2, + candlesticks => 2, + vector => 2 + ); + my $int_test = sub {$_[0]=~s/\s+//g;$_[0]=~/^\d+$/}; my $real_test = sub {$_[0]=~s/\s+//g;$_[0]=~/^[+-]?\d*\.?\d*([eE][+-]\d+)?$/}; @@ -74,23 +98,26 @@ my $color_test = sub {$_[0]=~s/\s+// 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 $linestyle_test = sub {exists($linestyles{$_[0]})}; my $words_test = sub {$_[0]=~s/\s+/ /g;$_[0]=~/^([\w\(\)]+ ?)+$/}; -## -## Attribute metadata -## + +################################################################### +## ## +## Attribute metadata ## +## ## +################################################################### my %plot_defaults = ( height => { default => 200, test => $int_test, - description => 'vertical size of image (pixels)', + description => 'height of image (pixels)', edit_type => 'entry' }, width => { default => 200, test => $int_test, - description => 'horizontal size of image (pixels)', + description => 'width of image (pixels)', edit_type => 'entry' }, bgcolor => { @@ -188,7 +215,7 @@ my %label_defaults = my %axis_defaults = ( - color => { + color => { default => 'x000000', test => $color_test, description => 'color of axes (x000000)', @@ -217,13 +244,6 @@ my %axis_defaults = 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'] } ); @@ -246,77 +266,65 @@ my %curve_defaults = test => $linestyle_test, description => 'Style of the axis lines', edit_type => 'choice', - choices => ['lines','linespoints','dots','points','steps'] + choices => ['lines','linespoints','dots','points','steps', + 'fsteps','histeps','errorbars','xerrorbars', + 'yerrorbars','xyerrorbars','boxes','boxerrorbars', + 'boxxyerrorbars','financebars','candlesticks', + 'vector'] } ); -## -## End of defaults -## +################################################################### +## ## +## parsing and edit rendering ## +## ## +################################################################### my (%plot,%key,%axis,$title,$xlabel,$ylabel,@labels,@curves); sub start_plot { - %plot = undef; %key = undef; %axis = undef; + %plot = (); %key = (); %axis = (); $title = undef; $xlabel = undef; $ylabel = undef; $#labels = -1; $#curves = -1; # my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; + &Apache::lonxml::register('Apache::lonplot', + ('title','xlabel','ylabel','key','axis','label','curve')); + push (@Apache::lonxml::namespace,'lonplot'); 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,'Plot'); + $result .= &edit_attributes($target,$token,\%plot_defaults); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args - ($token,$parstack,$safeeval,keys %plot_defaults); + ($token,$parstack,$safeeval,keys(%plot_defaults)); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); - $result.= &Apache::edit::handle_insert(); +# $result.= &Apache::edit::handle_insert(); } } - return ''; + 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') { - ## - ## 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 - if (! defined(%axis )) { &set_defaults(\%axis,\%axis_defaults); } - if (! defined($title )) {} # No title for this plot - if (! defined($xlabel)) {} # No xlabel for this plot - if (! defined($ylabel)) {} # No ylabel for this plot - if ($#labels < 0) { } # No labels for this plot - 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 ''; - } - } + &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'; + '_'.time.'_'.$$.int(rand(1000)).'_plot.data'; ## Write the plot description to the file my $fh=Apache::File->new(">$tmpdir$filename"); print $fh &write_gnuplot_file(); @@ -330,7 +338,7 @@ sub end_plot { alt = "/cgi-bin/plot.gif?$filename" /> ENDIMAGE } elsif ($target eq 'edit') { - } elsif ($target eq 'modified') { + $result.=&Apache::edit::tag_end($target,$token); } return $result; } @@ -343,9 +351,11 @@ sub start_key { &get_attributes(\%key,\%key_defaults,$parstack,$safeeval, $tagstack->[-1]); } elsif ($target eq 'edit') { + $result .= &Apache::edit::tag_start($target,$token,'Plot Key'); + $result .= &edit_attributes($target,$token,\%key_defaults); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args - ($token,$parstack,$safeeval,keys %key_defaults); + ($token,$parstack,$safeeval,keys(%key_defaults)); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); $result.= &Apache::edit::handle_insert(); @@ -359,10 +369,11 @@ sub end_key { my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { - } elsif ($target eq 'modified') { + $result.=&Apache::edit::tag_end($target,$token); } return $result; } + ##------------------------------------------------------------------- title sub start_title { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; @@ -370,7 +381,13 @@ sub start_title { if ($target eq 'web') { $title = &Apache::lonxml::get_all_text("/title",$$parser[-1]); } elsif ($target eq 'edit') { + $result.=&Apache::edit::tag_start($target,$token,'Plot Title'); + my $text=&Apache::lonxml::get_all_text("/title",$$parser[-1]); + $result.=''. + &Apache::edit::editfield('',$text,'',20,1); } elsif ($target eq 'modified') { + my $text=$$parser[-1]->get_text("/title"); + $result.=&Apache::edit::modifiedfield($token); } return $result; } @@ -380,7 +397,7 @@ sub end_title { my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { - } elsif ($target eq 'modified') { + $result.=&Apache::edit::tag_end($target,$token); } return $result; } @@ -391,7 +408,13 @@ sub start_xlabel { if ($target eq 'web') { $xlabel = &Apache::lonxml::get_all_text("/xlabel",$$parser[-1]); } elsif ($target eq 'edit') { + $result.=&Apache::edit::tag_start($target,$token,'Plot Xlabel'); + my $text=&Apache::lonxml::get_all_text("/xlabel",$$parser[-1]); + $result.=''. + &Apache::edit::editfield('',$text,'',20,1); } elsif ($target eq 'modified') { + my $text=$$parser[-1]->get_text("/xlabel"); + $result.=&Apache::edit::modifiedfield($token); } return $result; } @@ -401,10 +424,11 @@ sub end_xlabel { my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { - } elsif ($target eq 'modified') { + $result.=&Apache::edit::tag_end($target,$token); } return $result; } + ##------------------------------------------------------------------- ylabel sub start_ylabel { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; @@ -412,7 +436,13 @@ sub start_ylabel { if ($target eq 'web') { $ylabel = &Apache::lonxml::get_all_text("/ylabel",$$parser[-1]); } elsif ($target eq 'edit') { + $result .= &Apache::edit::tag_start($target,$token,'Plot Ylabel'); + my $text = &Apache::lonxml::get_all_text("/ylabel",$$parser[-1]); + $result .= ''. + &Apache::edit::editfield('',$text,'',20,1); } elsif ($target eq 'modified') { + my $text=$$parser[-1]->get_text("/ylabel"); + $result.=&Apache::edit::modifiedfield($token); } return $result; } @@ -422,10 +452,11 @@ sub end_ylabel { my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { - } elsif ($target eq 'modified') { + $result.=&Apache::edit::tag_end($target,$token); } return $result; } + ##------------------------------------------------------------------- label sub start_label { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; @@ -441,13 +472,20 @@ sub start_label { } push(@labels,\%label); } elsif ($target eq 'edit') { + $result .= &Apache::edit::tag_start($target,$token,'Plot Label'); + $result .= &edit_attributes($target,$token,\%label_defaults); + my $text = &Apache::lonxml::get_all_text("/label",$$parser[-1]); + $result .= ''. + &Apache::edit::editfield('',$text,'',20,1); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args - ($token,$parstack,$safeeval,keys %label_defaults); + ($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; } @@ -457,7 +495,7 @@ sub end_label { my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { - } elsif ($target eq 'modified') { + $result.=&Apache::edit::tag_end($target,$token); } return $result; } @@ -466,17 +504,19 @@ sub end_label { sub start_curve { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; + &Apache::lonxml::register('Apache::lonplot',('function','data')); + push (@Apache::lonxml::namespace,'curve'); 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,'Curve'); + $result .= &edit_attributes($target,$token,\%curve_defaults); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args - ($token,$parstack,$safeeval,keys %label_defaults); + ($token,$parstack,$safeeval,keys(%label_defaults)); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); $result.= &Apache::edit::handle_insert(); @@ -488,14 +528,15 @@ sub start_curve { 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') { - pop @Apache::lonxml::namespace; - &Apache::lonxml::deregister('Apache::lonplot',('function','data')); } elsif ($target eq 'edit') { - } elsif ($target eq 'modified') { + $result.=&Apache::edit::tag_end($target,$token); } return $result; } + ##------------------------------------------------------------ curve function sub start_function { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; @@ -508,11 +549,10 @@ sub start_function { $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(); + $result .= &Apache::edit::tag_start($target,$token,'Curve Function'); + my $text = &Apache::lonxml::get_all_text("/function",$$parser[-1]); + $result .= ''. + &Apache::edit::editfield('',$text,'',20,1); } elsif ($target eq 'modified') { # Why do I do this? my $text=$$parser[-1]->get_text("/function"); @@ -526,10 +566,11 @@ sub end_function { my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { - } elsif ($target eq 'modified') { + $result .= &Apache::edit::end_table(); } return $result; } + ##------------------------------------------------------------ curve data sub start_data { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; @@ -570,7 +611,13 @@ sub start_data { } push @{$curves[-1]->{'data'}},\@data; } elsif ($target eq 'edit') { + $result .= &Apache::edit::tag_start($target,$token,'Curve Data'); + my $text = &Apache::lonxml::get_all_text("/data",$$parser[-1]); + $result .= ''. + &Apache::edit::editfield('',$text,'',20,1); } elsif ($target eq 'modified') { + my $text=$$parser[-1]->get_text("/data"); + $result.=&Apache::edit::modifiedfield($token); } return $result; } @@ -580,7 +627,7 @@ sub end_data { my $result = ''; if ($target eq 'web') { } elsif ($target eq 'edit') { - } elsif ($target eq 'modified') { + $result .= &Apache::edit::end_table(); } return $result; } @@ -593,7 +640,15 @@ sub start_axis { &get_attributes(\%axis,\%axis_defaults,$parstack,$safeeval, $tagstack->[-1]); } elsif ($target eq 'edit') { + $result .= &Apache::edit::tag_start($target,$token,'Plot Axes'); + $result .= &edit_attributes($target,$token,\%axis_defaults); } 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; } @@ -603,35 +658,31 @@ sub end_axis { 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 = shift; - my $defaults = shift; + my ($var,$defaults) = @_; my $key; - foreach $key (keys %$defaults) { + foreach $key (keys(%$defaults)) { $var->{$key} = $defaults->{$key}->{'default'}; } } ##------------------------------------------------------------------- misc sub get_attributes{ - my $values = shift; - my $defaults = shift; - my $parstack = shift; - my $safeeval = shift; - my $tag = shift; - foreach my $attr (keys %{$defaults}) { + 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})) { @@ -678,12 +729,12 @@ sub write_gnuplot_file { $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)) { + if (%axis) { $gnuplot_input .= "set xrange \[$axis{'xmin'}:$axis{'xmax'}\]\n"; $gnuplot_input .= "set yrange \[$axis{'ymin'}:$axis{'ymax'}\]\n"; } # Key - if (defined(%key)) { + if (%key) { $gnuplot_input .= 'set key '.$key{'pos'}.' '; if ($key{'title'} ne '') { $gnuplot_input .= 'title "'.$key{'title'}.'" '; @@ -728,13 +779,36 @@ sub write_gnuplot_file { $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 (! %plot) { &set_defaults(\%plot,\%plot_defaults); } + if (! %key ) {} # No key for this plot, thats okay + if (! %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 = shift; - my $token = shift; - my $defaults = shift; + my ($target,$token,$defaults) = @_; my $result; - foreach my $attr (%{$token->[2]}) { + foreach my $attr (sort keys(%$defaults)) { if ($defaults->{$attr}->{'edit_type'} eq 'entry') { $result .= &Apache::edit::text_arg( $defaults->{$attr}->{'description'}, @@ -747,30 +821,34 @@ sub edit_attributes { $defaults->{$attr}->{'choices'}, $token); } + $result .= '
'; } return $result; } + +################################################################### +## ## +## Insertion functions for editing plots ## +## ## +################################################################### + #------------------------------------------------ insert_xxxxxxx sub insert_plot { - my $result; + my $result = ''; # plot attributes - $result .= '{'default'}. - "\"\n"; + $result .= " $attr=\"$plot_defaults{$attr}->{'default'}\"\n"; } $result .= ">\n"; # Add the components $result .= &insert_key(); $result .= &insert_axis(); - $result .= &insert_label(); + $result .= &insert_title(); + $result .= &insert_xlabel(); + $result .= &insert_ylabel(); $result .= &insert_curve(); - $result .= &insert_function(); - $result .= "\n"; - $result .= &insert_curve(); - $result .= &insert_data(); - $result .= "\n"; # close up the $result .= "\n"; return $result; @@ -778,10 +856,9 @@ sub insert_plot { sub insert_key { my $result; - $result .= ' {'default'}. - "\"\n"; + $result .= " $attr=\"$key_defaults{$attr}->{'default'}\"\n"; } $result .= " />\n"; return $result; @@ -791,18 +868,21 @@ sub insert_axis{ my $result; $result .= ' {'default'}. - "\"\n"; + $result .= " $attr=\"$axis_defaults{$attr}->{'default'}\"\n"; } $result .= " />\n"; return $result; } +sub insert_title { return " \n"; } +sub insert_xlabel { return " \n"; } +sub insert_ylabel { return " \n"; } + sub insert_label { my $result; $result .= '