--- loncom/homework/functionplotresponse.pm 2010/11/19 15:41:28 1.39 +++ loncom/homework/functionplotresponse.pm 2010/12/03 00:52:59 1.49 @@ -1,7 +1,7 @@ # LearningOnline Network with CAPA # option list style responses # -# $Id: functionplotresponse.pm,v 1.39 2010/11/19 15:41:28 www Exp $ +# $Id: functionplotresponse.pm,v 1.49 2010/12/03 00:52:59 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -47,7 +47,7 @@ sub geogebra_startcode { my ($id)=@_; return (< + codebase="/adm/geogebra/" width="722" height="447" MAYSCRIPT> ENDSTARTCODE } @@ -61,7 +61,7 @@ sub geogebra_endcode { # sub geogebra_spline_program { return (< + ENDSPLINEPROGRAM } @@ -74,7 +74,6 @@ sub geogebra_default_parameters { - @@ -159,16 +158,44 @@ sub update_register { } # -# Set a coordinate variable +# Set a point coordinate variable # -sub set_coordinate { - my ($id,$variable,$x,$y)=@_; +sub set_point_coordinate { + my ($id,$variable,$x,$y,$fixed)=@_; + my $mult=($fixed?'a*':''); return (<0)) { $return.=(<0)) { $return.=(<'. - &Apache::edit::text_arg('(Initial) x-value:','xinitial', + ['2','Second derivative'], + ['-1','Integral']],$token).'
'. + &Apache::edit::text_arg('Initial x-value:','xinitial', $token,'8'). - &Apache::edit::select_or_text_arg('(Initial) x-value label:','xinitiallabel', - [['start','Start of Plot']],$token,'8').'
'. + &Apache::edit::select_or_text_arg('Initial x-value label:','xinitiallabel', + [['start','Start of Plot'], + ['end','End of Plot']],$token,'8').'
'. - &Apache::edit::text_arg('Optional final x-value for ranges:','xfinal', + &Apache::edit::text_arg('Final x-value (optional):','xfinal', $token,'8'). - &Apache::edit::select_or_text_arg('Optional final x-value label:','xfinallabel', + &Apache::edit::select_or_text_arg('Final x-value label (optional):','xfinallabel', [['end','End of Plot']],$token,'8').'
'. - &Apache::edit::text_arg('Optional minimum length for range:','minimumlength', + &Apache::edit::text_arg('Minimum length for range (optional):','minimumlength', $token,'8'). - &Apache::edit::text_arg('Optional maximum length for range:','maximumlength', + &Apache::edit::text_arg('Maximum length for range (optional):','maximumlength', $token,'8').'
'. &Apache::edit::select_or_text_arg(&mt('Relationship:'),'relationship', [['eq','equal'], @@ -499,7 +605,7 @@ sub start_spline { &Apache::edit::text_arg('Index:','index', $token,'4').' '. &Apache::edit::select_arg('Order:','order', - ['2','3','4','5','6','7','8','9'],$token).' '. + ['2','3','4','5','6','7','8'],$token).' '. &Apache::edit::text_arg('Initial x-value:','initx', $token,'4').' '. &Apache::edit::text_arg('Initial y-value:','inity', @@ -566,8 +672,8 @@ sub get_answer_from_form_fields { 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); + return (2.*$t*$t*$t-3.*$t*$t+1.)*$p1 + 3.*($t*$t*$t-2.*$t*$t+$t)*($s1-$p1)+ + (-2.*$t*$t*$t+3.*$t*$t) *$p2 + 3.*($t*$t*$t-$t*$t) *($s2-$p2); } # @@ -576,8 +682,8 @@ sub cubic_hermite { 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); + return (6.*$t*$t-6.*$t) *$p1 + 3.*(3.*$t*$t-4.*$t+1.)*($s1-$p1)+ + (-6.*$t*$t+6.*$t)*$p2 + 3.*(3.*$t*$t-2.*$t) *($s2-$p2); } # @@ -586,8 +692,8 @@ sub ddt_cubic_hermite { 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); + return (12.*$t-6.) *$p1 + 3.*(6.*$t-4.)*($s1-$p1)+ + (-12.*$t+6.)*$p2 + 3.*(6.*$t-2.)*($s2-$p2); } # @@ -699,22 +805,7 @@ sub start_functionplotresponse { &decode_previous_answer($Apache::lonhomework::history{"resource.$partid.$id.submission"}); # Parameters of - 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 $ymin=&Apache::lonxml::get_param('ymin',$parstack,$safeeval); - $ymin=(defined($ymin)?$ymin:-10); - my $ymax=&Apache::lonxml::get_param('ymax',$parstack,$safeeval); - $ymax=(defined($ymax)?$ymax:10); - if ($xmax<=$xmin) { - &Apache::lonxml::warning('Maximum x-value needs to be larger than minimum value.'); - $xmax=$xmin+20; - } - if ($ymax<=$ymin) { - &Apache::lonxml::warning('Maximum y-value needs to be larger than minimum value.'); - $ymax=$ymin+20; - } + my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval); 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'); @@ -748,12 +839,15 @@ sub start_functionplotresponse { &Apache::edit::select_arg('y-axis visible:','yaxisvisible', ['yes','no'],$token).'
'. &Apache::edit::select_arg('Grid visible:','gridvisible', - ['yes','no'],$token). + ['yes','no'],$token).'
'. + &Apache::edit::text_arg('Background plot(s) for answer (function:xmin:xmax,function:xmin:xmax,...):', + 'answerdisplay',$token,'50'); + &Apache::edit::end_row().&Apache::edit::start_spanning_row(); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args($token,$parstack, $safeeval,'xlabel','xmin','xmax','ylabel','ymin','ymax', - 'xaxisvisible','yaxisvisible','gridvisible'); + 'xaxisvisible','yaxisvisible','gridvisible','answerdisplay'); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); } } elsif ($target eq 'meta') { @@ -848,7 +942,10 @@ sub functionplotrulecheck { =split(/\:/,$rule); $percent=($percent>0?$percent:5); &addlog("================="); - &addlog("Rule $label for ".('function itself','first derivative','second derivative')[$derivative]." $relationship $value"); + &addlog("Rule $label for ".($derivative<0?'integral':('function itself','first derivative','second derivative')[$derivative])." $relationship $value"); + if ((defined($minimumlength)) || (defined($maximumlength))) { + &addlog("Minimumlength $minimumlength Maximumlength $maximumlength"); + } my $li=0; my $lh=400; @@ -905,14 +1002,37 @@ sub functionplotrulecheck { $tol=4.*$tol/($xmax-$xmin); } elsif ($derivative==1) { $tol=2.*$tol/($xmax-$xmin); + } elsif ($derivative==-1) { + $tol=$tol*($xmax-$xmin)/2.; } } - for (my $i=$li; $i<=$lh; $i++) { + my $integral=0; + my $binwidth=($xmax-$xmin)/400.; + if (($derivative<0) && (!$findupper)) { +# definite integral, calculate over whole length + &addlog("Calculating definite integral"); + for (my $i=$li; $i<=$lh; $i++) { + $integral+=$Apache::functionplotresponse::func[$i]*$binwidth; + } + unless (&compare_rel($relationship,$value,$integral,$tol)) { + &addlog("Actual integral ".(defined($integral)?$integral:'undef').", expected $value, tolerance $tol"); + &addlog("Rule $label failed."); + my $hintlabel=$label; + $hintlabel=~s/^R//; + push(@Apache::functionplotresponse::failedrules,$hintlabel); + &addlog("Set hint condition $hintlabel"); + return 0; + } + } else { + for (my $i=$li; $i<=$lh; $i++) { my $val; if ($derivative==2) { $val=$Apache::functionplotresponse::d2funcdx2[$i]; } elsif ($derivative==1) { $val=$Apache::functionplotresponse::dfuncdx[$i]; + } elsif ($derivative==-1) { + $integral+=$Apache::functionplotresponse::func[$i]*$binwidth; + $val=$integral; } else { $val=$Apache::functionplotresponse::func[$i]; } @@ -920,37 +1040,59 @@ sub functionplotrulecheck { &addlog("Actual value ".(defined($val)?$val:'undef').", expected $value, tolerance $tol"); &addlog("Condition not fulfilled at x=".&actualval($i,$xmin,$xmax)." (".$Apache::functionplotresponse::actualxval[$i]."; index $i)"); if (($findupper) && ($i>$li)) { -# check for minimum and maximum lengths - my $length=&actualval($i,$xmin,$xmax)-&actualval($li,$xmin,$xmax); - if ($minimumlength) { - if ($length<$minimumlength) { - &addlog("Rule $label failed, actual length $length, minimum length $minimumlength"); - return 0; - } - } - if ($maximumlength) { - if ($length>$maximumlength) { - &addlog("Rule $label failed, actual length $length, maximum length $maximumlength"); - return 0; - } - } +# Check lengths + unless (&checklength($i,$li,$minimumlength,$maximumlength,$xmin,$xmax,$label)) { return 0; } +# Successfully found a new label, set it $Apache::functionplotresponse::functionplotrulelabels{$xfinallabel}=$i; &addlog("Rule $label passed, setting label $xfinallabel"); return 1; } else { &addlog("Rule $label failed."); - my $hintlabel=$label; - $hintlabel=~s/^R//; - push(@Apache::functionplotresponse::failedrules,$hintlabel); - &addlog("Set hint condition $hintlabel"); + &setfailed($label); return 0; } } + } } +# Corner case where this makes sense: using start or stop as defined labels + unless (&checklength($lh,$li,$minimumlength,$maximumlength,$xmin,$xmax,$label)) { return 0; } &addlog("Rule $label passed."); return 1; } +# +# check for minimum and maximum lengths +# + +sub checklength { + my ($i,$li,$minimumlength,$maximumlength,$xmin,$xmax,$label)=@_; + unless (($minimumlength) || ($maximumlength)) { return 1; } + my $length=&actualval($i,$xmin,$xmax)-&actualval($li,$xmin,$xmax); + if ($minimumlength) { + if ($length<$minimumlength) { + &addlog("Rule $label failed, actual length $length, minimum length $minimumlength"); + &setfailed($label); + return 0; + } + } + if ($maximumlength) { + if ($length>$maximumlength) { + &addlog("Rule $label failed, actual length $length, maximum length $maximumlength"); + &setfailed($label); + return 0; + } + } + return 1; +} + +sub setfailed { + my ($label)=@_; + my $hintlabel=$label; + $hintlabel=~s/^R//; + push(@Apache::functionplotresponse::failedrules,$hintlabel); + &addlog("Set hint condition $hintlabel"); +} + sub start_functionplotruleset { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; if ($target eq 'edit') { @@ -981,14 +1123,7 @@ sub end_functionplotruleset { # # Actually grade # - my $xmin=&Apache::lonxml::get_param('xmin',$parstack,$safeeval,-2); - $xmin=(defined($xmin)?$xmin:-10); - my $xmax=&Apache::lonxml::get_param('xmax',$parstack,$safeeval,-2); - $xmax=(defined($xmax)?$xmax:10); - my $ymin=&Apache::lonxml::get_param('ymin',$parstack,$safeeval,-2); - $ymin=(defined($ymin)?$ymin:-10); - my $ymax=&Apache::lonxml::get_param('ymax',$parstack,$safeeval,-2); - $ymax=(defined($ymax)?$ymax:10); + my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-2); my $ad=''; undef %Apache::functionplotresponse::functionplotrulelabels; @@ -1067,9 +1202,27 @@ sub end_functionplotelements { if ($target eq 'edit' ) { $result=&Apache::edit::end_table(); } elsif ($target eq 'web') { + my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-2); + +# Are we in show answer mode? + my $showanswer=&Apache::response::show_answer(); + if ($showanswer) { +# Render answerdisplay + my $answerdisplay=&Apache::lonxml::get_param('answerdisplay',$parstack,$safeeval,-2); + if ($answerdisplay=~/\S/s) { + foreach my $plot (split(/\s*\,\s*/,$answerdisplay)) { + my ($func,$xl,$xh)=split(/\s*\:\s*/,$plot); + if ((!defined($xl)) || ($xl eq '')) { $xl=$xmin; } + if ((!defined($xh)) || ($xh eq '')) { $xh=$xmax; } + $result.=&plot_script($internalid,$func,1,'','00aa00',$xl,$xh,6); + } + } + } + my $fixed=0; + if (($showanswer) || (&Apache::response::check_status()>=2)) { $fixed=1; } # Now is the time to render all of the stored splines foreach my $label (keys(%Apache::functionplotresponse::splineorder)) { - $result.=&generate_spline($internalid,$label); + $result.=&generate_spline($internalid,$label,$xmin,$xmax,$ymin,$ymax,$fixed); } # close the init script $result.=&end_init_script(); @@ -1094,6 +1247,25 @@ sub end_functionplotelements { return $result; } +sub boundaries { + my ($parstack,$safeeval,$level)=@_; + my $xmin=&Apache::lonxml::get_param('xmin',$parstack,$safeeval,$level); + $xmin=(defined($xmin)?$xmin:-10); + my $xmax=&Apache::lonxml::get_param('xmax',$parstack,$safeeval,$level); + $xmax=(defined($xmax)?$xmax:10); + my $ymin=&Apache::lonxml::get_param('ymin',$parstack,$safeeval,$level); + $ymin=(defined($ymin)?$ymin:-10); + my $ymax=&Apache::lonxml::get_param('ymax',$parstack,$safeeval,$level); + $ymax=(defined($ymax)?$ymax:10); + if ($xmax<=$xmin) { + $xmax=$xmin+20; + } + if ($ymax<=$ymin) { + $ymax=$ymin+20; + } + return ($xmin,$xmax,$ymin,$ymax); +} + sub start_functionplotelements { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; @@ -1113,20 +1285,7 @@ sub start_functionplotelements { .&Apache::edit::start_spanning_row() ."\n"; } elsif ($target eq 'web') { - my $xmin=&Apache::lonxml::get_param('xmin',$parstack,$safeeval,-2); - $xmin=(defined($xmin)?$xmin:-10); - my $xmax=&Apache::lonxml::get_param('xmax',$parstack,$safeeval,-2); - $xmax=(defined($xmax)?$xmax:10); - my $ymin=&Apache::lonxml::get_param('ymin',$parstack,$safeeval,-2); - $ymin=(defined($ymin)?$ymin:-10); - my $ymax=&Apache::lonxml::get_param('ymax',$parstack,$safeeval,-2); - $ymax=(defined($ymax)?$ymax:10); - if ($xmax<=$xmin) { - $xmax=$xmin+20; - } - if ($ymax<=$ymin) { - $ymax=$ymin+20; - } + my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-2); my $xaxisvisible=(&Apache::lonxml::get_param('xaxisvisible',$parstack,$safeeval,-2)=~/on|true|yes|1/i?'true':'false'); my $yaxisvisible=(&Apache::lonxml::get_param('yaxisvisible',$parstack,$safeeval,-2)=~/on|true|yes|1/i?'true':'false'); my $gridvisible=(&Apache::lonxml::get_param('gridvisible',$parstack,$safeeval,-2)=~/on|true|yes|1/i?'true':'false'); @@ -1140,7 +1299,7 @@ sub start_functionplotelements { $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); + $result.=&axes_label($internalid,$xmin,$xmax,$ymin,$ymax,$xlabel,$ylabel); # init script is left open } return $result;