[system] / trunk / webwork / system / courseScripts / PGgraphmacros.pl Repository:
ViewVC logotype

View of /trunk/webwork/system/courseScripts/PGgraphmacros.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 27 - (download) (as text) (annotate)
Tue Jun 19 20:31:39 2001 UTC (11 years, 11 months ago) by gage
File size: 16912 byte(s)
Many modifications to courseScript scripts which will insure that the
routines are re-entrant (i.e. they can be called via mod_perl and don't
need to be re-compiled for every execution run.)

Minor modifications to processProblem8.pl and to PGtranslator.pm

    1 #!/usr/local/bin/webwork-perl
    2 
    3 =head1 NAME
    4 
    5   PGgraphmacros -- in courseScripts directory
    6 
    7 =head1 SYNPOSIS
    8 
    9 
   10 #   use Fun;
   11 #   use Label;
   12 #   use Circle;
   13 #   use WWPlot;
   14 
   15 =head1 DESCRIPTION
   16 
   17 This collection of macros provides easy access to the facilities provided by the graph
   18 module WWPlot and the modules for objects which can be drawn on a graph: functions (Fun.pm)
   19 labels (Label.pm) and images.  The only image implemented currently are open and closed circles
   20 (Circle) which can be used to mark graphs of functions defined on open and closed intervals.
   21 
   22 These macros provide an easy ability to graph simple functions.  More complicated projects
   23 may require direct access to the underlying modules.  If these complicated projects are common
   24 then it may be desirable to create additional macros.  (See numericalmacros.pl for one example.)
   25 
   26 
   27 =cut
   28 
   29 =head2 Other constructs
   30 
   31 See F<PGbasicmacros> for definitions of C<image> and C<caption>
   32 
   33 =cut
   34 
   35 
   36 #my $User = $main::studentLogin;
   37 #my $psvn = $main::psvnNumber; #$main::in{'probSetKey'};  #in{'probSetNumber'}; #$main::probSetNumber;
   38 #my $setNumber     = $main::setNumber;
   39 #my $probNum       = $main::probNum;
   40 
   41 #########################################################
   42 # this initializes a graph object
   43 #########################################################
   44 # graphObject = init_graph(xmin,ymin,xmax,ymax,options)
   45 # options include  'grid' =>[8,8] or
   46 #          'ticks'=>[8,8] and/or
   47 #                  'axes'
   48 #########################################################
   49 
   50 my %images_created = ();  # this keeps track of the base names of the images created during this session.
   51                      #  We tack on
   52                      # $imageNum  = ++$images_created{$imageName} to keep from overwriting files
   53                      # when we don't want to.
   54 
   55 
   56 
   57 
   58 =head2 init_graph
   59 
   60 =pod
   61 
   62     $graphObject = init_graph(xmin,ymin,xmax,ymax,'ticks'=>[4,4],'axes'=>[0,0])
   63     options are
   64       'grid' =>[8,8] or
   65       # there are 8 evenly spaced lines intersecting the horizontal axis
   66       'ticks'=>[8,8] and/or
   67       # there are 8 ticks on the horizontal axis, 8 on the vertical
   68       'axes' => [0,0]
   69       # axes pass through the point (0,0) in real coordinates
   70       'size' => [200,200]
   71       # dimensions of the graph in pixels.
   72       'pixels' =>[200,200]  # synonym for size
   73 
   74 Creates a graph object with the default size 200 by 200 pixels.
   75 If you want axes or grids you need to specify them in options. But the default values can be selected for you.
   76 
   77 
   78 =cut
   79 BEGIN {
   80   be_strict();
   81 }
   82 sub _PGgraphmacros_export {
   83 
   84   my @EXPORT = (
   85     '&init_graph', '&add_functions', '&plot_functions', '&open_circle',
   86     '&closed_circle', '&my_math_constants', '&string_to_sub',
   87     );
   88     @EXPORT;
   89 }
   90 
   91 sub init_graph {
   92   my ($xmin,$ymin,$xmax,$ymax,%options) = @_;
   93   my @size;
   94   if ( defined($options{'size'}) ) {
   95     @size = @{$options{'size'}};
   96   } elsif ( defined($options{'pixels'}) ) {
   97     @size = @{$options{'pixels'}};
   98   }   else {
   99     @size=(200,200);
  100   }
  101     my $graphRef = new WWPlot(@size);
  102   # select a  name for this graph based on the user, the psvn and the problem
  103   my $imageName = "$main::studentLogin-$main::psvnNumber-set${main::setNumber}prob${main::probNum}";
  104   # $imageNum counts the number of graphs with this name which have been created since PGgraphmacros.pl was initiated.
  105   my $imageNum  = ++$main::images_created{$imageName};
  106   # this provides a unique name for the graph -- it does not include an extension.
  107   $graphRef->imageName("${imageName}image${imageNum}");
  108 
  109   $graphRef->xmin($xmin) if defined($xmin);
  110   $graphRef->xmax($xmax) if defined($xmax);
  111   $graphRef->ymin($ymin) if defined($ymin);
  112   $graphRef->ymax($ymax) if defined($ymax);
  113   my $x_delta = ($graphRef->xmax -  $graphRef->xmin)/8;
  114   my $y_delta = ($graphRef->ymax -  $graphRef->ymin)/8;
  115   if (defined($options{grid})) {   #   draw grid
  116       my $xdiv = ( ${$options{'grid'}}[0]) ? ${$options{'grid'}}[0] : 8; # number of ticks (8 is default)
  117       my $ydiv = ( ${$options{'grid'}}[1] )  ? ${$options{'grid'}}[1] : 8;
  118     my $x_delta = ($graphRef->xmax -  $graphRef->xmin)/$xdiv;
  119       my $y_delta = ($graphRef->ymax -  $graphRef->ymin)/$ydiv;
  120       my $i; my @x_values=(); my @y_values=();
  121       foreach $i (1..($xdiv-1) ) {
  122         push( @x_values, $i*$x_delta+$graphRef->{xmin});
  123       }
  124       foreach $i (1..($ydiv-1) ) {
  125         push( @y_values, $i*$y_delta+$graphRef->{ymin});
  126       }
  127     $graphRef->v_grid('gray',@x_values);
  128     $graphRef->h_grid('gray',@y_values);
  129     $graphRef->lb(new Label($x_delta,0,sprintf("%1.1f",$x_delta),'black','center','middle'));
  130     $graphRef->lb(new Label(0,$y_delta,sprintf("%1.1f",$y_delta),'black','center','middle'));
  131 
  132     $graphRef->lb(new Label($xmax,0,$xmax,'black','right'));
  133     $graphRef->lb(new Label($xmin,0,$xmin,'black','left'));
  134     $graphRef->lb(new Label(0,$ymax,$ymax,'black','top'));
  135     $graphRef->lb(new Label(0,$ymin,$ymin,'black','bottom','right'));
  136 
  137   } elsif ($options{ticks}) {   #   draw ticks -- grid over rides ticks
  138     my $xdiv = ${$options{ticks}}[0]? ${$options{ticks}}[0] : 8; # number of ticks (8 is default)
  139           my $ydiv = ${$options{ticks}}[1]? ${$options{ticks}}[1] : 8;
  140     my $x_delta = ($graphRef->xmax -  $graphRef->xmin)/$xdiv;
  141           my $y_delta = ($graphRef->ymax -  $graphRef->ymin)/$ydiv;
  142           my $i; my @x_values=(); my @y_values=();
  143       foreach $i (1..($xdiv-1) ) {
  144         push( @x_values, $i*$x_delta+$graphRef->{xmin});
  145       }
  146       foreach $i (1..($ydiv-1) ) {
  147         push( @y_values, $i*$y_delta+$graphRef->{ymin});
  148       }
  149     $graphRef->v_ticks(0,'black',@x_values);
  150     $graphRef->h_ticks(0,'black',@y_values);
  151     $graphRef->lb(new Label($x_delta,0,$x_delta,'black','right'));
  152     $graphRef->lb(new Label(0,$y_delta,$y_delta,'black','top'));
  153 
  154     $graphRef->lb(new Label($xmax,0,$xmax,'black','right'));
  155     $graphRef->lb(new Label($xmin,0,$xmin,'black','left'));
  156     $graphRef->lb(new Label(0,$ymax,$ymax,'black','top'));
  157     $graphRef->lb(new Label(0,$ymin,$ymin,'black','bottom','right'));
  158   }
  159 
  160   if ($options{axes}) {   #   draw axis
  161       my $ra_axes = $options{axes};
  162     $graphRef->h_axis($ra_axes->[1],'black');
  163     $graphRef->v_axis($ra_axes->[0],'black');
  164   }
  165 
  166 
  167   $graphRef;
  168 }
  169 
  170 sub init_graph_no_labels {
  171   my ($xmin,$ymin,$xmax,$ymax,%options) = @_;
  172   my @size;
  173   if ( defined($options{'size'}) ) {
  174     @size = @{$options{'size'}};
  175   } elsif ( defined($options{'pixels'}) ) {
  176     @size = @{$options{'pixels'}};
  177   }   else {
  178     @size=(200,200);
  179   }
  180     my $graphRef = new WWPlot(@size);
  181   # select a  name for this graph based on the user, the psvn and the problem
  182   my $imageName = "$main::studentLogin-$main::psvnNumber-set${main::setNumber}prob${main::probNum}";
  183   # $imageNum counts the number of graphs with this name which have been created since PGgraphmacros.pl was initiated.
  184   my $imageNum  = ++$main::images_created{$imageName};
  185   # this provides a unique name for the graph -- it does not include an extension.
  186   $graphRef->imageName("${imageName}image${imageNum}");
  187 
  188   $graphRef->xmin($xmin) if defined($xmin);
  189   $graphRef->xmax($xmax) if defined($xmax);
  190   $graphRef->ymin($ymin) if defined($ymin);
  191   $graphRef->ymax($ymax) if defined($ymax);
  192   my $x_delta = ($graphRef->xmax -  $graphRef->xmin)/8;
  193   my $y_delta = ($graphRef->ymax -  $graphRef->ymin)/8;
  194   if (defined($options{grid})) {   #   draw grid
  195       my $xdiv = ( ${$options{'grid'}}[0]) ? ${$options{'grid'}}[0] : 8; # number of ticks (8 is default)
  196       my $ydiv = ( ${$options{'grid'}}[1] )  ? ${$options{'grid'}}[1] : 8;
  197     my $x_delta = ($graphRef->xmax -  $graphRef->xmin)/$xdiv;
  198       my $y_delta = ($graphRef->ymax -  $graphRef->ymin)/$ydiv;
  199       my $i; my @x_values=(); my @y_values=();
  200       foreach $i (1..($xdiv-1) ) {
  201         push( @x_values, $i*$x_delta+$graphRef->{xmin});
  202       }
  203       foreach $i (1..($ydiv-1) ) {
  204         push( @y_values, $i*$y_delta+$graphRef->{ymin});
  205       }
  206     $graphRef->v_grid('gray',@x_values);
  207     $graphRef->h_grid('gray',@y_values);
  208     #$graphRef->lb(new Label($x_delta,0,sprintf("%1.1f",$x_delta),'black','center','top'));
  209     #$graphRef->lb(new Label($x_delta,0,"|",'black','center','middle'));
  210     #$graphRef->lb(new Label(0,$y_delta,sprintf("%1.1f ",$y_delta),'black','right','middle'));
  211     #$graphRef->lb(new Label(0,$y_delta,"-",'black','center','middle'));
  212 
  213 
  214     $graphRef->lb(new Label($xmax,0,$xmax,'black','right'));
  215     $graphRef->lb(new Label($xmin,0,$xmin,'black','left'));
  216     $graphRef->lb(new Label(0,$ymax,$ymax,'black','top','right'));
  217     $graphRef->lb(new Label(0,$ymin,$ymin,'black','bottom','right'));
  218 
  219   } elsif ($options{ticks}) {   #   draw ticks -- grid over rides ticks
  220     my $xdiv = ${$options{ticks}}[0]? ${$options{ticks}}[0] : 8; # number of ticks (8 is default)
  221           my $ydiv = ${$options{ticks}}[1]? ${$options{ticks}}[1] : 8;
  222     my $x_delta = ($graphRef->xmax -  $graphRef->xmin)/$xdiv;
  223           my $y_delta = ($graphRef->ymax -  $graphRef->ymin)/$ydiv;
  224           my $i; my @x_values=(); my @y_values=();
  225       foreach $i (1..($xdiv-1) ) {
  226         push( @x_values, $i*$x_delta+$graphRef->{xmin});
  227       }
  228       foreach $i (1..($ydiv-1) ) {
  229         push( @y_values, $i*$y_delta+$graphRef->{ymin});
  230       }
  231     $graphRef->v_ticks(0,'black',@x_values);
  232     $graphRef->h_ticks(0,'black',@y_values);
  233     $graphRef->lb(new Label($x_delta,0,$x_delta,'black','right'));
  234     $graphRef->lb(new Label(0,$y_delta,$y_delta,'black','top'));
  235 
  236     $graphRef->lb(new Label($xmax,0,$xmax,'black','right'));
  237     $graphRef->lb(new Label($xmin,0,$xmin,'black','left'));
  238     $graphRef->lb(new Label(0,$ymax,$ymax,'black','top'));
  239     $graphRef->lb(new Label(0,$ymin,$ymin,'black','bottom','right'));
  240   }
  241 
  242   if ($options{axes}) {   #   draw axis
  243       my $ra_axes = $options{axes};
  244     $graphRef->h_axis($ra_axes->[1],'black');
  245     $graphRef->v_axis($ra_axes->[0],'black');
  246   }
  247 
  248 
  249   $graphRef;
  250 }
  251 
  252 
  253 
  254 =head2  plot_functions
  255 
  256 =pod
  257 
  258   Useage:  ($f1, $f2, $f3) = plot_functions($graph, $f1, $f2, $f3);
  259   Synonym: add_functions($graph,$f1,$f2,$f3);
  260 
  261 Where $f1 is a string of the form
  262 
  263   $f1 = qq! x^2 - 3*x + 45 for x in [0, 45) using color:red and weight:2!
  264 
  265 The phrase translates as: formula    B<for> variable B<in>  interval B<using>   option-list.
  266 The option-list contains pairs of the form attribute:value.
  267 The default for color is "default_color" which is usually black.
  268 The default for the weight (pixel width) of the pen is 2 pixels.
  269 
  270 The string_to_sub subroutine is used to translate the formula into a subroutine.
  271 
  272 The functions in the list are installed in the graph object $graph and will appear when the graph object is next drawn.
  273 
  274 =cut
  275 
  276 sub add_functions {
  277   &plot_functions;
  278 }
  279 
  280 sub plot_functions {
  281   my $graph = shift;
  282   my @function_list = @_;
  283   my $error = "";
  284   $error .= "The first argument to plot_functions must be a graph object" unless ref($graph) =~/WWPlot/;
  285   my $fn;
  286   my @functions=();
  287   foreach $fn (@function_list) {
  288 
  289       # model:   "2.5-x^2 for x in <-1,0> using color:red and weight:2"
  290     if ($fn =~ /^(.+)for\s*(\w+)\s*in\s*([\(\[\<])\s*([\d\.\-]+)\s*,\s*([\d\.\-]+)\s*([\)\]\>])\s*using\s*(.*)$/ )  {
  291       my ($rule,$var, $left_br, $left_end, $right_end, $right_br, $options)=  ($1, $2, $3, $4, $5, $6, $7);
  292 
  293       my %options = split( /\s*and\s*|\s*:\s*|\s*,\s*|\s*=\s*|\s+/,$options);
  294       my ($color, $weight);
  295       if ( defined($options{'color'})  ){
  296         $color = $options{'color'}; #set pen color
  297       } else {
  298         $color = 'default_color';
  299       }
  300       if ( defined($options{'weight'}) ) {
  301         $weight = $options{'weight'}; # set pen weight (width in pixels)
  302       } else {
  303         $weight =2;
  304       }
  305 
  306       my $subRef = string_to_sub($rule,$var);
  307       my $funRef = new Fun($subRef,$graph);
  308       $funRef->color($color);
  309       $funRef->weight($weight);
  310       $funRef->domain($left_end , $right_end);
  311       push(@functions,$funRef);
  312         # place open (1,3) or closed (1,3) circle at the endpoints or do nothing <1,3>
  313         if ($left_br eq '[' ) {
  314           $graph->stamps(closed_circle($left_end,&$subRef($left_end),$color) );
  315         } elsif ($left_br eq '(' ) {
  316           $graph->stamps(open_circle($left_end, &$subRef($left_end), $color) );
  317         }
  318         if ($right_br eq ']' ) {
  319           $graph->stamps(closed_circle($right_end,&$subRef($right_end),$color) );
  320         } elsif ($right_br eq ')' ) {
  321           $graph->stamps(open_circle($right_end, &$subRef($right_end), $color) );
  322         }
  323 
  324     } else {
  325       $error .= "Error in parsing: $fn $main::BR";
  326     }
  327 
  328   }
  329   die ("Error in plot_functions: \n\t $error ") if $error;
  330   @functions;   # return function references unless there is an error.
  331 }
  332 
  333 =head2 insertGraph
  334 
  335   $filePath = insertGraph(graphObject);
  336       returns a path to the file containing the graph image.
  337 
  338 B<Note:> Because insertGraph involves writing to the disk, it is actually defined in dangerousMacros.pl.
  339 
  340 insertGraph(graphObject) writes a image file to the C<html/tmp/gif> directory of the current course.
  341 The file name is obtained from the graphObject.  Warnings are issued if errors occur while writing to
  342 the file.
  343 
  344 The permissions and ownership of the file are controlled by C<$main::tmp_file_permission>
  345 and C<$main::numericalGroupID>.
  346 
  347 B<Returns:>   A string containing the full path to the temporary file containing the  image.
  348 
  349 
  350 
  351 InsertGraph draws the object $graph, stores it in "${tempDirectory}gif/$imageName.gif (or .png)" where
  352 the $imageName is obtained from the graph object.  ConvertPath and surePathToTmpFile are used to insure
  353 that the correct directory separators are used for the platform and that the necessary directories
  354 are created if they are not already present.  The directory address to the file is the result.
  355 
  356 The most common use of C,insertGraph> is
  357 
  358   TEXT(image(insertGraph($graph)) );
  359 
  360 where C<image> takes care of creating the proper URL for accessing the graph and for creating the HTML code to display the image.
  361 
  362 Another common useage is:
  363 
  364   TEXT(htmlLink( alias(insertGraph($graph), "picture" ) ) );
  365 
  366 which inserts the URL pointing to the picture.
  367 alias converts the directory address to a URL when serving HTML pages and insures that
  368 an eps file is generated when creating TeX code for downloading. (Image, automatically applies alias to its input
  369 in order to obtain the URL.)
  370 
  371 See the documentation in F<dangerousMacros.pl> for the latest details.
  372 
  373 =cut
  374 
  375 =head2  'Circle' lables
  376 
  377   Useage: $circle_object = open_circle( $x_position, $y_position, $color );
  378           $circle_object2 = closed_circle( $x_position, $y_position, $color );
  379 
  380 Creates a small open (resp. filled in or closed) circle for use as a stamp in marking graphs.
  381 For example
  382 
  383   $graph -> stamps($circle_object2); # puts a filled dot at $x_position, $y_position
  384 
  385 =cut
  386 
  387 #########################################################
  388 sub open_circle {
  389     my ($cx,$cy,$color) = @_;
  390   new Circle ($cx, $cy, 4,$color,'nearwhite');
  391 }
  392 
  393 sub closed_circle {
  394     my ($cx,$cy, $color) = @_;
  395     $color = 'black' unless defined $color;
  396   new Circle ($cx, $cy, 4,$color, $color);
  397 }
  398 
  399 
  400 =head2 Auxiliary macros
  401 
  402 =head3  string_to_sub and my_math_constants
  403 
  404 
  405 These are internal macros which govern the interpretation of equations.
  406 
  407 
  408   Usage: $string = my_math_constants($string)
  409          $subroutine_reference = my_string_to_sub($string)
  410 
  411 C<my_math_constants>
  412 interprets pi, e  as mathematical constants 3.1415926... and 2.71828... respectively. (Case is important).
  413 The power operator ^ is replaced by ** to conform with perl constructs
  414 
  415 C<string_to_sub>
  416 converts a string defining a single perl arithmetic expression with independent variable $XVAR into a subroutine.
  417 The string is first filtered through C<my_math_macros>. The resulting subroutine
  418 takes a single real number as input and produces a single output value.
  419 
  420 =cut
  421 
  422 sub my_math_constants {
  423   my($in) = @_;
  424   $in =~s/\bpi\b/(4*atan2(1,1))/g;
  425   $in =~s/\be\b/(exp(1))/g;
  426   $in =~s/\^/**/g;
  427   $in;
  428 }
  429 
  430 sub string_to_sub {
  431   my $str_in = shift;
  432   my $var    = shift;
  433   my $out = undef;
  434   if ( defined(&check_syntax)  ) {
  435     #prepare the correct answer and check it's syntax
  436       my $rh_correct_ans = new AnswerHash;
  437     $rh_correct_ans->input($str_in);
  438     $rh_correct_ans = check_syntax($rh_correct_ans);
  439     warn  $rh_correct_ans->{error_message} if $rh_correct_ans->{error_flag};
  440     $rh_correct_ans->clear_error();
  441     $rh_correct_ans = function_from_string2($rh_correct_ans, ra_vars => ['x'], store_in =>'rf_correct_ans');
  442     my $correct_eqn_sub = $rh_correct_ans->{rf_correct_ans};
  443     warn $rh_correct_ans->{error_message} if $rh_correct_ans->{error_flag};
  444     $out = sub{ scalar( &$correct_eqn_sub(@_) ) };  #ignore the error messages from the function.
  445 
  446   } else {
  447     my $in =$str_in;
  448 
  449     $in =~ s/\b$var\b/\$XVAR/g;
  450     $in = &my_math_constants($in);
  451     my ($subRef, $PG_eval_errors,$PG_full_error_report) = PG_restricted_eval( " sub { my \$XVAR = shift; my \$out = $in; \$out; } ");
  452     if ($PG_eval_errors) {
  453       die " ERROR while defining a function from the string:\n\n$main::BR $main::BR $str_in $main::BR $main::BR\n\n  $PG_eval_errors"
  454     } else {
  455       $out = $subRef;
  456     }
  457 
  458   }
  459   $out;
  460 }
  461 
  462 
  463 
  464 
  465 
  466 
  467 
  468 
  469 
  470 #########################################################
  471 
  472 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9