Parent Directory
|
Revision Log
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 |