--- loncom/homework/functionplotresponse.pm 2010/10/09 22:31:49 1.12 +++ loncom/homework/functionplotresponse.pm 2010/10/30 15:32:36 1.19 @@ -1,7 +1,7 @@ # LearningOnline Network with CAPA # option list style responses # -# $Id: functionplotresponse.pm,v 1.12 2010/10/09 22:31:49 www Exp $ +# $Id: functionplotresponse.pm,v 1.19 2010/10/30 15:32:36 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -101,12 +101,25 @@ ENDDEFAULTPARAMETERS # sub init_script { - if ($Apache::functionplotresponse::callscripts) { + if ($#Apache::functionplotresponse::callscripts>=0) { + my $script=''; + foreach my $id (@Apache::functionplotresponse::callscripts) { + $script.="if (param=='applet_$id') { loaded_$id=true; }\n"; + } + $script.="if (".join(' && ',map { "loaded_$_" } (@Apache::functionplotresponse::callscripts)). + ") { setTimeout('ggbInitAll()',20) }"; + my $calls=join("\n",map { "ggbInit_$_();" } (@Apache::functionplotresponse::callscripts)); return (< // @@ -194,14 +207,16 @@ sub new_coordinate { # sub start_init_script { my ($id)=@_; -# Add a line to ggbOnInit-function which calls the right function based on parameter passed from GeoGebra - $Apache::functionplotresponse::callscripts.="if (param=='applet_$id') { ggbInit_$id(); }\n"; +# Add id to the list of ggbInit_$id functions that need to be called + push(@Apache::functionplotresponse::callscripts,$id); # ... and open this function return (< // 8) { $order=8; } - $Apache::functionplotresponse::splineorder{$label}=$order; - - my $x=&Apache::lonxml::get_param('initx',$parstack,$safeeval); - unless ($x) { $x=0; } - $Apache::functionplotresponse::splineinitx{$label}=$x; - - my $y=&Apache::lonxml::get_param('inity',$parstack,$safeeval); - unless ($y) { $y=0; } - $Apache::functionplotresponse::splineinity{$label}=$y; - - my $sx=&Apache::lonxml::get_param('scalex',$parstack,$safeeval); - unless ($sx) { $sx=$order; } - $Apache::functionplotresponse::splinescalex{$label}=$sx; - - my $sy=&Apache::lonxml::get_param('scaley',$parstack,$safeeval); - unless ($sy) { $sy=2; } - $Apache::functionplotresponse::splinescaley{$label}=$sy; - return ''; + my $result=''; + if ($target eq 'web') { + my $label=&Apache::lonxml::get_param('label',$parstack,$safeeval); + $Apache::functionplotresponse::counter++; + if ($label=~/\W/) { + &Apache::lonxml::warning(&mt('Spline labels should only contain alphanumeric characters.')); + } + $label=~s/\W//gs; + unless ($label) { $label='S'.$Apache::functionplotresponse::counter; } + if ($Apache::functionplotresponse::splineorder{$label}) { + &Apache::lonxml::error(&mt('Spline labels must be unique.')); + } + + my $order=&Apache::lonxml::get_param('order',$parstack,$safeeval); + if ($order<2) { $order=2; } + if ($order>8) { $order=8; } + $Apache::functionplotresponse::splineorder{$label}=$order; + + my $x=&Apache::lonxml::get_param('initx',$parstack,$safeeval); + unless ($x) { $x=0; } + $Apache::functionplotresponse::splineinitx{$label}=$x; + + my $y=&Apache::lonxml::get_param('inity',$parstack,$safeeval); + unless ($y) { $y=0; } + $Apache::functionplotresponse::splineinity{$label}=$y; + + my $sx=&Apache::lonxml::get_param('scalex',$parstack,$safeeval); + unless ($sx) { $sx=$order; } + $Apache::functionplotresponse::splinescalex{$label}=$sx; + + my $sy=&Apache::lonxml::get_param('scaley',$parstack,$safeeval); + unless ($sy) { $sy=2; } + $Apache::functionplotresponse::splinescaley{$label}=$sy; + } elsif ($target eq 'edit') { + $result=&Apache::edit::tag_start($target,$token,'Spline'). + &Apache::edit::text_arg('Label:','label', + $token,'4').' '. + &Apache::edit::select_arg('Order:','order', + ['2','3','4','5','6','7','8','9'],$token).' '. + &Apache::edit::text_arg('Initial x-value:','initx', + $token,'4').' '. + &Apache::edit::text_arg('Initial y-value:','inity', + $token,'4').' '. + &Apache::edit::text_arg('Scale x:','scalex', + $token,'4').' '. + &Apache::edit::text_arg('Scale y:','scaley', + $token,'4'). + &Apache::edit::end_row(); + } elsif ($target eq 'modified') { + my $constructtag=&Apache::edit::get_new_args($token,$parstack, + $safeeval,'label','order','initx','inity', + 'scalex','scaley'); + if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); } + } + return $result; } sub end_spline { - return ''; + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + my $result=''; + if ($target eq 'edit') { + $result=&Apache::edit::end_table(); + } + return $result; } sub end_init_script { @@ -371,6 +468,89 @@ sub get_answer_from_form_fields { return ($answer,%coords); } +# +# The following functions calculate the cubic-hermite splines server-side +# + +sub cubic_hermite { + my ($t,$p1,$s1,$p2,$s2)=@_; + return (2.*$t*$t*$t-3.*$t*$t+1.)*$p1 + 5.*($t*$t*$t-2.*$t*$t+$t)*($s1-$p1)+ + (-2.*$t*$t*$t+3.*$t*$t) *$p2 + 5.*($t*$t*$t-$t*$t) *($s2-$p2); +} + +# +# d/dt(...) +# + +sub ddt_cubic_hermite { + my ($t,$p1,$s1,$p2,$s2)=@_; + return (6.*$t*$t-6.*$t) *$p1 + 5.*(3.*$t*$t-4.*$t+1.)*($s1-$p1)+ + (-6.*$t*$t+6.*$t)*$p2 + 5.*(3.*$t*$t-2.*$t) *($s2-$p2); +} + +# +# d^2/dt^2(...) +# + +sub d2dt2_cubic_hermite { + my ($t,$p1,$s1,$p2,$s2)=@_; + return (12.*$t-6.) *$p1 + 5.*(6.*$t-4.)*($s1-$p1)+ + (-12.*$t+6.)*$p2 + 5.*(6.*$t-2.)*($s2-$p2); +} + +# +# Array index calculation +# +sub array_index { + my ($xmin,$xmax,$x)=@_; + my $index=int(($x-$xmin)/($xmax-$xmin)*200.+0.5); + return $index; +} + +# +# Populate the arrays +# + +sub populate_arrays { + my ($id,$xmin,$xmax)=@_; + for (my $i=0; $i<=200; $i++) { + $Apache::functionplotresponse::func[$i]=undef; + $Apache::functionplotresponse::dfunddx[$i]=undef; + $Apache::functionplotresponse::d2funcd2x[$i]=undef; + } + unless ($xmax>$xmin) { return 'no_func'; } +# Run over all splines in response + foreach my $label (split(/\,/,$env{"form.HWVAL_AllSplines_$id"})) { + my $xiold=-1; +# Run over all points in spline + for (my $i=1; $i<$env{"form.HWVAL_SplineOrder_".$id."_".$label}; $i++) { + my $ni=$i+1; + my @xparms=($env{'form.HWVAL_'.$id.'_'.$label.'P'.$i.'_x'}, + $env{'form.HWVAL_'.$id.'_'.$label.'S'.$i.'_x'}, + $env{'form.HWVAL_'.$id.'_'.$label.'P'.$ni.'_x'}, + $env{'form.HWVAL_'.$id.'_'.$label.'S'.$ni.'_x'}); + my @yparms=($env{'form.HWVAL_'.$id.'_'.$label.'P'.$i.'_y'}, + $env{'form.HWVAL_'.$id.'_'.$label.'S'.$i.'_y'}, + $env{'form.HWVAL_'.$id.'_'.$label.'P'.$ni.'_y'}, + $env{'form.HWVAL_'.$id.'_'.$label.'S'.$ni.'_y'}); +# Run in small steps over spline parameter + for (my $t=0; $t<=1; $t+=0.00025) { + my $xi=&array_index($xmin,$xmax,&cubic_hermite($t,@xparms)); + if ($xi<$xiold) { return 'no_func'; } + if (($xi>$xiold) && ($xi>=0) && ($xi<=200)) { + if (defined($Apache::functionplotresponse::func[$xi])) { return 'no_func'; } + $xiold=$xi; + $Apache::functionplotresponse::func[$xi]=&cubic_hermite($t,@yparms); + } + } + } + } +} + +# +# Implentation of +# + sub start_functionplotresponse { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; @@ -394,16 +574,18 @@ sub start_functionplotresponse { # Parameters of my $xmin=&Apache::lonxml::get_param('xmin',$parstack,$safeeval); - $xmin=($xmin?$xmin:-10); + $xmin=(defined($xmin)?$xmin:-10); my $xmax=&Apache::lonxml::get_param('xmax',$parstack,$safeeval); - $xmax=($xmax?$xmax:10); + $xmax=(defined($xmax)?$xmax:10); my $ymin=&Apache::lonxml::get_param('ymin',$parstack,$safeeval); - $ymin=($ymin?$ymin:-10); + $ymin=(defined($ymin)?$ymin:-10); my $ymax=&Apache::lonxml::get_param('ymax',$parstack,$safeeval); - $ymax=($ymax?$ymax:10); + $ymax=(defined($ymax)?$ymax:10); my $xaxisvisible=(&Apache::lonxml::get_param('xaxisvisible',$parstack,$safeeval)=~/on|true|yes|1/i?'true':'false'); my $yaxisvisible=(&Apache::lonxml::get_param('yaxisvisible',$parstack,$safeeval)=~/on|true|yes|1/i?'true':'false'); my $gridvisible=(&Apache::lonxml::get_param('gridvisible',$parstack,$safeeval)=~/on|true|yes|1/i?'true':'false'); + my $xlabel=&Apache::lonxml::get_param('xlabel',$parstack,$safeeval); + my $ylabel=&Apache::lonxml::get_param('ylabel',$parstack,$safeeval); if ($target eq 'edit') { $result.=&Apache::edit::start_table($token) .''.&mt('Function Plot Question').'' @@ -414,24 +596,28 @@ sub start_functionplotresponse { .&Apache::edit::end_row() .&Apache::edit::start_spanning_row() ."\n"; - $result.=&Apache::edit::text_arg('Minimum x-value:','xmin', - $token,'4'). + $result.=&Apache::edit::text_arg('Label x-axis:','xlabel', + $token,'6').' '. + &Apache::edit::text_arg('Minimum x-value:','xmin', + $token,'4').' '. &Apache::edit::text_arg('Maximum x-value:','xmax', - $token,'4'). + $token,'4').' '. &Apache::edit::select_arg('x-axis visible:','xaxisvisible', - ['yes','no'],$token). + ['yes','no'],$token).'  '. + &Apache::edit::text_arg('Label y-axis:','ylabel', + $token,'6').' '. &Apache::edit::text_arg('Minimum y-value:','ymin', - $token,'4'). + $token,'4').' '. &Apache::edit::text_arg('Maximum y-value:','ymax', - $token,'4'). + $token,'4').' '. &Apache::edit::select_arg('y-axis visible:','yaxisvisible', - ['yes','no'],$token). + ['yes','no'],$token).'  '. &Apache::edit::select_arg('Grid visible:','gridvisible', ['yes','no'],$token). &Apache::edit::end_row().&Apache::edit::start_spanning_row(); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args($token,$parstack, - $safeeval,'xmin','xmax','ymin','ymax', + $safeeval,'xlabel','xmin','xmax','ylabel','ymin','ymax', 'xaxisvisible','yaxisvisible','gridvisible'); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); } @@ -444,6 +630,7 @@ sub start_functionplotresponse { $result.=&start_init_script($internalid); # put the axis commands inside $result.=&axes_script($internalid,$xmin,$xmax,$ymin,$ymax,$xaxisvisible,$yaxisvisible,$gridvisible); + $result.=&axes_label($internalid,$xlabel,$ylabel); # init script is left open } return $result; @@ -474,7 +661,16 @@ sub end_functionplotresponse { # # Actually grade # - my $ad='INCORRECT'; + my $xmin=&Apache::lonxml::get_param('xmin',$parstack,$safeeval); + $xmin=(defined($xmin)?$xmin:-10); + my $xmax=&Apache::lonxml::get_param('xmax',$parstack,$safeeval); + $xmax=(defined($xmax)?$xmax:10); + my $ad; + if (&populate_arrays($internalid,$xmin,$xmax) eq 'no_func') { + $ad='BAD_FORMULA'; + } else { + $ad='INCORRECT'; + } # # Store grading info # @@ -487,6 +683,13 @@ sub end_functionplotresponse { } # close the init script $result.=&end_init_script(); +# register all splines in this response + $result.=''."\n"; + foreach my $label (keys(%Apache::functionplotresponse::splineorder)) { + $result.=''."\n"; + } # generate the input fields $result.=$Apache::functionplotresponse::inputfields; # actually start the -tag