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