[system] / trunk / pg / macros / PGgraphmacros.pl Repository:
ViewVC logotype

Annotation of /trunk/pg/macros/PGgraphmacros.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1050 - (view) (download) (as text)

1 : sh002i 1050 #!/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 :     Usage: ($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 usage 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 :     Usage: $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