[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 60 - (download) (as text) (annotate)
Fri Jun 22 20:03:01 2001 UTC (18 years, 5 months ago) by gage
File size: 16950 byte(s)
Added _PGgraphmacros_init subroutine

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9