Annotation of loncom/homework/functionplotresponse.pm, revision 1.68
1.1 www 1: # LearningOnline Network with CAPA
2: # option list style responses
3: #
1.68 ! www 4: # $Id: functionplotresponse.pm,v 1.67 2011/10/19 00:20:28 www Exp $
1.1 www 5: #
6: # Copyright Michigan State University Board of Trustees
7: #
8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
9: #
10: # LON-CAPA is free software; you can redistribute it and/or modify
11: # it under the terms of the GNU General Public License as published by
12: # the Free Software Foundation; either version 2 of the License, or
13: # (at your option) any later version.
14: #
15: # LON-CAPA is distributed in the hope that it will be useful,
16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18: # GNU General Public License for more details.
19: #
20: # You should have received a copy of the GNU General Public License
21: # along with LON-CAPA; if not, write to the Free Software
22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23: #
24: # /home/httpd/html/adm/gpl.txt
25: #
26: # http://www.lon-capa.org/
27: #
28:
29: package Apache::functionplotresponse;
30: use strict;
31: use Apache::response();
32: use Apache::lonlocal;
33: use Apache::lonnet;
1.65 www 34: use Apache::run;
1.36 www 35:
1.1 www 36: BEGIN {
1.37 www 37: &Apache::lonxml::register('Apache::functionplotresponse',('functionplotresponse','backgroundplot','spline',
38: 'functionplotrule','functionplotruleset',
39: 'functionplotelements'));
1.1 www 40: }
41:
1.5 www 42: #
43: # There can be a number of applets on a page, each called ggbApplet_$id,
44: # where $id is the "_"-concatenated part and responseid
45: #
46:
1.1 www 47: sub geogebra_startcode {
1.3 www 48: my ($id)=@_;
1.1 www 49: return (<<ENDSTARTCODE);
1.3 www 50: <applet name="ggbApplet_$id" code="geogebra.GeoGebraApplet" archive="geogebra.jar"
1.40 www 51: codebase="/adm/geogebra/" width="722" height="447" MAYSCRIPT>
1.1 www 52: <param name="java_arguments" value="-Xmx512m -Djnlp.packEnabled=true"/>
53: ENDSTARTCODE
54: }
55:
56: sub geogebra_endcode {
1.2 www 57: return &Apache::lonhtmlcommon::java_not_enabled()."</applet>\n";
58: }
59:
1.5 www 60: #
61: # This is the internal GeoGebra bytecode which defines the spline functions
62: #
1.2 www 63: sub geogebra_spline_program {
64: return (<<ENDSPLINEPROGRAM);
1.57 www 65: <param name="ggbBase64" value="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"/>
1.2 www 66: ENDSPLINEPROGRAM
67: }
68:
1.5 www 69: #
70: # The standard set of parameters inside <applet>
71: #
1.2 www 72: sub geogebra_default_parameters {
1.3 www 73: my ($id)=@_;
1.2 www 74: return(<<ENDDEFAULTPARAMETERS);
75: <param name="image" value="/adm/lonIcons/lonanim.gif" />
76: <param name="boxborder" value="false" />
77: <param name="centerimage" value="true" />
1.62 www 78: <param name="cache_archive" value="geogebra.jar, geogebra_main.jar, geogebra_gui.jar, geogebra_cas.jar, geogebra_export.jar, geogebra_algos.jar, geogebra_javascript.jar, geogebra_properties.jar, jlatexmath.jar, jlm_cyrillic.jar, jlm_greek.jar" />
79: <param name="cache_version" value="3.9.219.0, 3.9.219.0, 3.9.219.0, 3.9.219.0, 3.9.219.0, 3.9.219.0, 3.9.219.0, 3.9.219.0, 3.9.219.0, 3.9.219.0, 3.9.219.0" />
1.2 www 80: <param name="framePossible" value="false" />
81:
82: <param name="showResetIcon" value="false" />
83: <param name="showAnimationButton" value="false" />
84: <param name="enableRightClick" value="false" />
85: <param name="errorDialogsActive" value="true" />
86: <param name="enableLabelDrags" value="false" />
87: <param name="showMenuBar" value="false" />
88: <param name="showToolBar" value="false" />
89: <param name="showToolBarHelp" value="false" />
90: <param name="showAlgebraInput" value="false" />
91: <param name="enableShiftDragZoom" value="false" />
92: <param name="allowRescaling" value="false" />
93: <param name="enableLabelDrags" value="false" />
1.3 www 94: <param name="ggbOnInitParam" value="applet_$id" />
1.2 www 95: ENDDEFAULTPARAMETERS
1.1 www 96: }
97:
1.5 www 98: #
99: # This subroutine is called by LON-CAPA at </problem>
100: # Each applet on the page will call function ggbOnInit when it is done loading
101: # This function in turn will call the respective function registered by start_init_script
102: # Which one of the registered functions is called is determined by ggbOnInitParam, which GeoGebra passes to ggbOnInit
103: #
104:
1.3 www 105: sub init_script {
1.16 www 106: if ($#Apache::functionplotresponse::callscripts>=0) {
107: my $script='';
108: foreach my $id (@Apache::functionplotresponse::callscripts) {
109: $script.="if (param=='applet_$id') { loaded_$id=true; }\n";
110: }
111: $script.="if (".join(' && ',map { "loaded_$_" } (@Apache::functionplotresponse::callscripts)).
1.52 www 112: ") { setTimeout('ggbInitAll()',200) }";
1.16 www 113: my $calls=join("\n",map { "ggbInit_$_();" } (@Apache::functionplotresponse::callscripts));
1.3 www 114: return (<<ENDGGBINIT);
115: <script type="text/javascript">
116: // <![CDATA[
1.16 www 117: // Function that each applet will call when loaded
118: // It will pass "its" parameter
119: // Set flags for when an applet is loaded, wait till all are loaded, and then some
1.3 www 120: function ggbOnInit(param) {
1.16 www 121: $script
122: }
123: function ggbInitAll() {
1.17 www 124: $calls
1.3 www 125: }
126: // ]]>
127: </script>
128: ENDGGBINIT
129: }
130: }
131:
1.5 www 132: #
133: # Each Geogebra applet is supposed to call this when parameters change
1.10 www 134: # Changes the hidden fields on the web page
1.5 www 135: #
136: sub update_script {
137: my ($id)=@_;
138: return (<<ENDUPDATESCRIPT);
139: <script type="text/javascript">
140: // <![CDATA[
141: function updatePointCoordinates_$id(coordinateName) {
142: var x = document.ggbApplet_$id.getXcoord(coordinateName);
143: var y = document.ggbApplet_$id.getYcoord(coordinateName);
1.9 www 144: document.lonhomework.elements["HWVAL_$id\_" + coordinateName + "_x"].value = x;
145: document.lonhomework.elements["HWVAL_$id\_" + coordinateName + "_y"].value = y;
1.5 www 146: }
147: // ]]>
148: </script>
149: ENDUPDATESCRIPT
150: }
151:
152: #
153: # Register the above update-handler for a variable
154: #
155:
156: sub update_register {
157: my ($id,$variable)=@_;
1.6 www 158: return "document.ggbApplet_$id.registerObjectUpdateListener('$variable','updatePointCoordinates_$id');\n";
159: }
160:
161: #
1.40 www 162: # Set a point coordinate variable
1.6 www 163: #
1.40 www 164: sub set_point_coordinate {
1.48 www 165: my ($id,$variable,$x,$y,$fixed)=@_;
166: my $mult=($fixed?'a*':'');
1.68 ! www 167: # Get rid of wild exponents, make sure it's a number
! 168: $x=1.*$x;
! 169: $y=1.*$y;
! 170: # GeoGebra does not understand "E"
! 171: $x=~s/[e|E]/\*10\^/;
! 172: $x=~s/\+//;
! 173: $y=~s/[e|E]/\*10\^/;
! 174: $y=~s/\+//;
1.6 www 175: return (<<ENDSETVARIABLE);
1.48 www 176: document.ggbApplet_$id.evalCommand("a=1");
177: document.ggbApplet_$id.evalCommand("$variable=$mult($x,$y)");
1.40 www 178: document.ggbApplet_$id.setLabelVisible("$variable",false);
1.6 www 179: ENDSETVARIABLE
180: }
181:
182: #
1.40 www 183: # Set a slope coordinate variable
184: #
185: sub set_slope_coordinate {
1.48 www 186: my ($id,$variable,$xrel,$yrel,$xmin,$xmax,$ymin,$ymax,$pointname,$fixed)=@_;
1.40 www 187: my $xvariable=$variable.'x';
188: my $yvariable=$variable.'y';
189: my $domain=$xmax-$xmin;
190: my $range=$ymax-$ymin;
191: my $xinterval=$domain/100.;
192: my $yinterval=$range/200.;
1.48 www 193: my $mult=($fixed?'a*':'');
1.40 www 194: return (<<ENDSETSVARIABLE);
1.48 www 195: document.ggbApplet_$id.evalCommand("a=1");
1.51 www 196: document.ggbApplet_$id.evalCommand("$xvariable=Slider[$xinterval,$domain,$xinterval]");
1.40 www 197: document.ggbApplet_$id.setVisible("$xvariable", false);
198: document.ggbApplet_$id.evalCommand("$xvariable=$xrel");
199: document.ggbApplet_$id.evalCommand("$yvariable=Slider[-$range,$range,$yinterval]");
200: document.ggbApplet_$id.setVisible("$yvariable", false);
201: document.ggbApplet_$id.evalCommand("$yvariable=$yrel");
1.48 www 202: document.ggbApplet_$id.evalCommand("$variable=$mult($xvariable+x($pointname),$yvariable+y($pointname))");
1.40 www 203: document.ggbApplet_$id.setLabelVisible("$variable", false);
204: ENDSETSVARIABLE
205: }
206:
207: #
1.9 www 208: # Input field name for a coordinate variable
209: #
210:
211: sub field_name {
212: my ($id,$variable,$name)=@_;
213: return "HWVAL_$id\_$variable\_$name";
214: }
215:
216: #
217: # Generate an input field for a coordinate variable
218: #
219:
220: sub generate_input_field {
221: my ($id,$variable,$x,$y)=@_;
222: $Apache::functionplotresponse::inputfields.=
223: "<input type='hidden' name='".&field_name($id,$variable,'x')."' value='$x' />\n".
224: "<input type='hidden' name='".&field_name($id,$variable,'y')."' value='$y' />\n";
225: }
226:
227: #
1.40 www 228: # Initialize a new point coordinate variable at set a listener on it
1.6 www 229: #
1.40 www 230: sub new_point_coordinate {
1.48 www 231: my ($id,$variable,$x,$y,$fixed)=@_;
1.11 www 232: if (defined($Apache::functionplotresponse::previous{&field_name($id,$variable,'x')})) {
1.10 www 233: $x=$Apache::functionplotresponse::previous{&field_name($id,$variable,'x')};
234: }
1.11 www 235: if (defined($Apache::functionplotresponse::previous{&field_name($id,$variable,'y')})) {
1.10 www 236: $y=$Apache::functionplotresponse::previous{&field_name($id,$variable,'y')};
237: }
1.9 www 238: &generate_input_field($id,$variable,$x,$y);
1.48 www 239: return &set_point_coordinate($id,$variable,$x,$y,$fixed).&update_register($id,$variable);
1.40 www 240: }
241:
242: #
243: # Initialize a new slope coordinate variable at set a listener on it
244: #
245: sub new_slope_coordinate {
1.48 www 246: my ($id,$variable,$x,$y,$pointname,$xp,$yp,$xmin,$xmax,$ymin,$ymax,$fixed)=@_;
1.40 www 247: #
248: # $variable: name of the slope point
249: # $x, $y: coordinates of the slope point
250: # $pointname: name of the associated point point
251: # $xp $yp: coordinates of the point point
252: #
253: if (defined($Apache::functionplotresponse::previous{&field_name($id,$variable,'x')})) {
254: $x=$Apache::functionplotresponse::previous{&field_name($id,$variable,'x')};
255: }
256: if (defined($Apache::functionplotresponse::previous{&field_name($id,$variable,'y')})) {
257: $y=$Apache::functionplotresponse::previous{&field_name($id,$variable,'y')};
258: }
259: if (defined($Apache::functionplotresponse::previous{&field_name($id,$pointname,'x')})) {
260: $xp=$Apache::functionplotresponse::previous{&field_name($id,$pointname,'x')};
261: }
262: if (defined($Apache::functionplotresponse::previous{&field_name($id,$pointname,'y')})) {
263: $yp=$Apache::functionplotresponse::previous{&field_name($id,$pointname,'y')};
264: }
265:
266: &generate_input_field($id,$variable,$x,$y);
267: my $xrel=$x-$xp;
268: my $yrel=$y-$yp;
1.48 www 269: return &set_slope_coordinate($id,$variable,$xrel,$yrel,$xmin,$xmax,$ymin,$ymax,$pointname,$fixed).&update_register($id,$variable);
1.5 www 270: }
271:
272: #
273: # This registers the init-function call for ggbOnInit, which LON-CAPA will place at </problem>
274: # It then starts the right headers
275: #
1.3 www 276: sub start_init_script {
277: my ($id)=@_;
1.16 www 278: # Add id to the list of ggbInit_$id functions that need to be called
279: push(@Apache::functionplotresponse::callscripts,$id);
1.5 www 280: # ... and open this function
1.3 www 281: return (<<ENDSTARTINIT);
282: <script type="text/javascript">
283: // <![CDATA[
1.5 www 284: // variable that will eventually be passed back to the server
285: var coordinateMap_$id = [];
1.16 www 286: // flag for not loaded yet
287: var loaded_$id=false;
1.5 www 288: // Init-function for applet
1.3 www 289: function ggbInit_$id() {
290: ENDSTARTINIT
291: }
292:
1.5 www 293: #
294: # This sets the axes inside ggbInit_$id
295: #
296:
1.4 www 297: sub axes_script {
298: my ($id,$xmin,$xmax,$ymin,$ymax,$xvisible,$yvisible,$gvisible)=@_;
299: return (<<ENDAXESSCRIPT);
300: // changes (xmin, xmax, ymin, ymax)
301: document.ggbApplet_$id.setCoordSystem($xmin,$xmax,$ymin,$ymax);
302:
303: // makes the (x,y) axis (in)visible
304: document.ggbApplet_$id.setAxesVisible($xvisible,$yvisible);
305: // makes the grid (in)visible
306: document.ggbApplet_$id.setGridVisible($gvisible);
307: ENDAXESSCRIPT
308: }
309:
1.14 www 310: sub axes_label {
1.45 www 311: my ($id,$xmin,$xmax,$ymin,$ymax,$xlabel,$ylabel)=@_;
1.14 www 312: unless ($xlabel || $ylabel) { return ''; }
313: my $return='document.ggbApplet_'.$id.'.evalCommand("topRight=Corner[3]");';
314: if ($xlabel) {
1.45 www 315: if (($ymin<0) && ($ymax>0)) {
1.14 www 316: $return.=(<<ENDXAXISLABELSCRIPT);
317: document.ggbApplet_$id.evalCommand("Xlabel=(x(topRight)-AxisStepX[],AxisStepY[]/6)");
318: document.ggbApplet_$id.setVisible("Xlabel",false);
319: document.ggbApplet_$id.evalCommand("Text[\\"$xlabel\\", Xlabel]");
320: ENDXAXISLABELSCRIPT
1.45 www 321: } else {
322: $return.=(<<ENDXOFFAXISLABEL);
323: document.ggbApplet_$id.evalCommand("LowerRight=Corner[2]");
324: document.ggbApplet_$id.evalCommand("Text[\\"$xlabel\\", (x(LowerRight) - AxisStepX[], y(LowerRight) + AxisStepY[] / 2)]");
325: ENDXOFFAXISLABEL
326: }
1.14 www 327: }
328: if ($ylabel) {
1.45 www 329: if (($xmin<0) && ($xmax>0)) {
1.14 www 330: $return.=(<<ENDYAXISLABELSCRIPT);
331: document.ggbApplet_$id.evalCommand("Ylabel=(AxisStepX[]/6,y(topRight)-AxisStepY[]/3)");
332: document.ggbApplet_$id.setVisible("Ylabel",false);
333: document.ggbApplet_$id.evalCommand("Text[\\"$ylabel\\", Ylabel]");
334: ENDYAXISLABELSCRIPT
1.45 www 335: } else {
336: $return.=(<<ENDYOFFAXISLABEL);
337: document.ggbApplet_$id.evalCommand("UpperLeft=Corner[4]");
338: document.ggbApplet_$id.evalCommand("Text[\\"$ylabel\\", (x(UpperLeft) + AxisStepX[] / 5, y(UpperLeft) - AxisStepY[] / 1.8)]");
339: ENDYOFFAXISLABEL
340: }
1.14 www 341: }
342: return $return;
343: }
344:
1.58 www 345: #
346: # Subroutine to produce background and answer plots
347: #
348:
1.4 www 349: sub plot_script {
1.47 www 350: my ($id,$function,$fixed,$label,$color,$xmin,$xmax,$thickness)=@_;
1.41 www 351: $label=~s/\W//g;
352: if (($label) && ($label!~/^[A-Za-z]/)) {
353: $label='C'.$label;
354: }
355: my $visible=0;
356: if ($label) {
357: $visible="1";
358: } else {
359: $Apache::functionplotresponse::counter++;
360: $label='C'.$Apache::functionplotresponse::counter;
361: }
1.46 www 362: my $rc=0;
363: my $gc=0;
364: my $bc=0;
365: if ($color) {
366: my ($rh,$gh,$bh)=($color=~/(..)(..)(..)/);
367: $rc=hex($rh);
368: $gc=hex($gh);
369: $bc=hex($bh);
370: }
1.6 www 371: if ($fixed) {
1.43 www 372: return "document.ggbApplet_$id.evalCommand('$label=Function[$function,$xmin,$xmax]');\n".
1.46 www 373: ($visible?'':"document.ggbApplet_$id.setLabelVisible('$label', false);\n").
1.47 www 374: ($color?"document.ggbApplet_$id.setColor('$label',$rc,$gc,$bc);\n":'').
375: ($thickness?"document.ggbApplet_$id.setLineThickness('$label',$thickness);\n":'');
1.6 www 376: } else {
1.46 www 377: return "document.ggbApplet_$id.evalCommand('y=$function');\n";
1.6 www 378: }
1.4 www 379: }
380:
1.5 www 381: #
1.58 www 382: # Answer spline display
383: #
384: # points: x,y,slope_x,slope_y
385:
386: sub answer_spline_script {
387: my ($id,@points)=@_;
388: my $order=int(($#points+1)/4);
389: if ($order<2) { $order=2; }
390: if ($order>8) { $order=8; }
391: $Apache::functionplotresponse::counter++;
1.60 www 392: my $label='CSpline'.$Apache::functionplotresponse::counter;
1.59 www 393: my $output='document.ggbApplet_'.$id.'.evalCommand("'.$label.'=Spline'.$order.'[';
394: for (my $i=0;$i<=$#points;$i+=4) {
395: $output.="($points[$i],$points[$i+1]),($points[$i+2],$points[$i+3]),";
396: }
397: $output=~s/\,$//;
398: $output.=']");'."\n";
399: for (my $i=2; $i<2*$order; $i+=2) {
1.61 www 400: $output.='document.ggbApplet_'.$id.'.setColor("'.$label.'_'.($i>=10?'{':'').$i.($i>=10?'}':'').'",0,170,0);'."\n";
1.59 www 401: }
1.60 www 402: for (my $i=1; $i<2*$order; $i+=2) {
1.61 www 403: $output.='document.ggbApplet_'.$id.'.setVisible("'.$label.'_'.($i>=10?'{':'').$i.($i>=10?'}':'').'",false);'."\n";
1.60 www 404: }
405:
1.59 www 406: return $output;
1.58 www 407: }
408:
409: #
1.7 www 410: # Subroutine that generates code for spline $label based on stored information
1.6 www 411: #
412:
413: sub generate_spline {
1.48 www 414: my ($id,$label,$xmin,$xmax,$ymin,$ymax,$fixed)=@_;
1.6 www 415: my $result='';
1.7 www 416: my $order=$Apache::functionplotresponse::splineorder{$label};
417: my $x=$Apache::functionplotresponse::splineinitx{$label};
418: my $y=$Apache::functionplotresponse::splineinity{$label};
419: my $sx=$Apache::functionplotresponse::splinescalex{$label};
420: my $sy=$Apache::functionplotresponse::splinescaley{$label};
421: my @coords=();
422: foreach my $i (1..$order) {
1.48 www 423: $result.=&new_point_coordinate($id,$label.'P'.$i,$x,$y,$fixed);
1.40 www 424: my $xp=$x;
1.8 www 425: $x+=$sx/(2.*($order-1));
1.7 www 426: push(@coords,$label.'P'.$i);
1.48 www 427: $result.=&new_slope_coordinate($id,$label.'S'.$i,$x,$y+$sy,$label.'P'.$i,$xp,$y,$xmin,$xmax,$ymin,$ymax,$fixed);
1.8 www 428: $x+=$sx/(2.*($order-1));
1.7 www 429: push(@coords,$label.'S'.$i);
430: }
431: $result.='document.ggbApplet_'.$id.'.evalCommand("Spline'.$order.'['.join(',',@coords).']");'."\n";
1.6 www 432: return $result;
433: }
434: #
435: # <backgroundplot function="..." fixed="yes/no" />
1.5 www 436: #
1.4 www 437: sub start_backgroundplot {
438: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
439: my $result='';
440: my $internalid = $Apache::inputtags::part.'_'.$Apache::inputtags::response[-1];
441: my $function=&Apache::lonxml::get_param('function',$parstack,$safeeval);
1.47 www 442: my $xinitial=&Apache::lonxml::get_param('xinitial',$parstack,$safeeval);
443: my $xfinal=&Apache::lonxml::get_param('xfinal',$parstack,$safeeval);
1.41 www 444: my $label=&Apache::lonxml::get_param('label',$parstack,$safeeval);
1.46 www 445: my $color=&Apache::lonxml::get_param('color',$parstack,$safeeval);
446: $color=~s/[^a-fA-F0-9]//gs;
447: unless (length($color)==6) { $color=''; }
1.6 www 448: my $fixed=(&Apache::lonxml::get_param('fixed',$parstack,$safeeval)=~/on|true|yes|1/i?1:0);
449:
1.4 www 450: unless ($function) { $function="0"; }
451: if ($target eq 'web') {
1.41 www 452: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-3);
1.47 www 453: unless (defined($xinitial)) { $xinitial=$xmin; }
454: unless (defined($xfinal)) { $xfinal=$xmax; }
455: $result.=&plot_script($internalid,$function,$fixed,$label,$color,$xinitial,$xfinal);
1.13 www 456: } elsif ($target eq 'edit') {
457: $result=&Apache::edit::tag_start($target,$token,'Background Function Plot').
458: &Apache::edit::text_arg('Function:','function',
459: $token,'16').
1.47 www 460: &Apache::edit::text_arg('Initial x-value (optional):','xinitial',
461: $token,'8').
462: &Apache::edit::text_arg('Final x-value (optional):','xfinal',
463: $token,'8').
1.41 www 464: &Apache::edit::text_arg('Label on Plot:','label',
465: $token,'8').
1.46 www 466: &Apache::edit::text_arg('Color (hex code):','color',
467: $token,'8').
1.13 www 468: &Apache::edit::select_arg('Fixed location:','fixed',
469: ['yes','no'],$token).
470: &Apache::edit::end_row();
471: } elsif ($target eq 'modified') {
472: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
1.47 www 473: $safeeval,'function','label','xinitial','xfinal','color','fixed');
1.13 www 474: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
475: }
476: return $result;
1.4 www 477: }
478:
479: sub end_backgroundplot {
1.13 www 480: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
481: my $result='';
482: if ($target eq 'edit') {
483: $result=&Apache::edit::end_table();
484: }
485: return $result;
1.6 www 486: }
487:
488: #
1.25 www 489: # <functionplotrule ... />
1.12 www 490: #
1.25 www 491: sub start_functionplotrule {
1.12 www 492: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
493: my $result='';
1.24 www 494: my $label=&Apache::lonxml::get_param('index',$parstack,$safeeval);
495: $Apache::functionplotresponse::counter++;
496: if ($label=~/\W/) {
1.25 www 497: &Apache::lonxml::warning(&mt('Rule indices should only contain alphanumeric characters.'));
1.24 www 498: }
499: $label=~s/\W//gs;
500: unless ($label) {
501: $label='R'.$Apache::functionplotresponse::counter;
502: } else {
503: $label='R'.$label;
504: }
505: if ($Apache::functionplotresponse::splineorder{$label}) {
1.25 www 506: &Apache::lonxml::error(&mt('Rule indices must be unique.'));
1.24 www 507: }
508:
509:
510: if ($target eq 'grade') {
511: # Simply remember - in order - for later
1.25 www 512: my $beginninglabel=&Apache::lonxml::get_param('xinitiallabel',$parstack,$safeeval);
513: my $endinglabel=&Apache::lonxml::get_param('xfinallabel',$parstack,$safeeval);
1.24 www 514: if (($beginninglabel=~/\W/) || ($endinglabel=~/W/)) {
1.25 www 515: &Apache::lonxml::warning(&mt('Rule labels must be alphanumeric.'));
1.24 www 516: }
517: $beginninglabel=~s/\W//gs;
518: $endinglabel=~s/\W//gs;
519: my $relationship=&Apache::lonxml::get_param('relationship',$parstack,$safeeval);
520: $relationship=~s/\W//gs;
521: $relationship=lc($relationship);
522: unless ($relationship=~/^(eq|ge|gt|le|lt|ne)$/) {
1.25 www 523: &Apache::lonxml::warning(&mt('Rule relationship not defined.'));
1.24 www 524: $relationship='eq';
525: }
1.35 www 526: my $derivative=&Apache::lonxml::get_param('derivativeorder',$parstack,$safeeval);
1.42 www 527: unless (($derivative==-1) || ($derivative==0) || ($derivative==1) || ($derivative==2)) {
1.25 www 528: &Apache::lonxml::warning(&mt('Rule derivative not defined.'));
1.24 www 529: $derivative=0;
530: }
1.25 www 531: push(@Apache::functionplotresponse::functionplotrules,join(':',(
1.24 www 532: $label,
533: $derivative,
1.25 www 534: &Apache::lonxml::get_param('xinitial',$parstack,$safeeval),
1.24 www 535: $beginninglabel,
1.25 www 536: &Apache::lonxml::get_param('xfinal',$parstack,$safeeval),
1.24 www 537: $endinglabel,
1.35 www 538: &Apache::lonxml::get_param('minimumlength',$parstack,$safeeval),
539: &Apache::lonxml::get_param('maximumlength',$parstack,$safeeval),
1.24 www 540: $relationship,
1.33 www 541: &Apache::lonxml::get_param('value',$parstack,$safeeval),
542: &Apache::lonxml::get_param('percenterror',$parstack,$safeeval)
1.24 www 543: )));
544: } elsif ($target eq 'edit') {
1.29 www 545: $result=&Apache::edit::tag_start($target,$token,'Function Plot Evaluation Rule').
1.36 www 546: &Apache::edit::text_arg('Index/Name:','index',
547: $token,'10').' '.
1.35 www 548: &Apache::edit::select_arg(&mt('Function:'),'derivativeorder',
1.29 www 549: [['0','Function itself'],
550: ['1','First derivative'],
1.42 www 551: ['2','Second derivative'],
552: ['-1','Integral']],$token).'<br />'.
1.44 www 553: &Apache::edit::text_arg('Initial x-value:','xinitial',
1.35 www 554: $token,'8').
1.44 www 555: &Apache::edit::select_or_text_arg('Initial x-value label:','xinitiallabel',
556: [['start','Start of Plot'],
557: ['end','End of Plot']],$token,'8').'<br />'.
1.30 www 558:
1.44 www 559: &Apache::edit::text_arg('Final x-value (optional):','xfinal',
1.35 www 560: $token,'8').
1.44 www 561: &Apache::edit::select_or_text_arg('Final x-value label (optional):','xfinallabel',
1.32 www 562: [['end','End of Plot']],$token,'8').'<br />'.
1.44 www 563: &Apache::edit::text_arg('Minimum length for range (optional):','minimumlength',
1.35 www 564: $token,'8').
1.44 www 565: &Apache::edit::text_arg('Maximum length for range (optional):','maximumlength',
1.35 www 566: $token,'8').'<br />'.
1.38 www 567: &Apache::edit::select_or_text_arg(&mt('Relationship:'),'relationship',
1.29 www 568: [['eq','equal'],
569: ['ne','not equal'],
570: ['ge','greater than or equal'],
571: ['gt','greater than'],
572: ['lt','less than'],
573: ['le','less than or equal']],$token).
574: $result.= &Apache::edit::select_or_text_arg('Value:','value',
1.65 www 575: [['undef','not defined']],$token,'30').
1.29 www 576: &Apache::edit::text_arg('Percent error:','percenterror',
1.35 www 577: $token,'8').
1.29 www 578: &Apache::edit::end_row();
579: } elsif ($target eq 'modified') {
1.54 www 580: if (($env{'form.'.&Apache::edit::html_element_name('xinitial')} ne '') && ($env{'form.'.&Apache::edit::html_element_name('xinitiallabel')} eq 'start')) {
1.53 www 581: $env{'form.'.&Apache::edit::html_element_name('xinitiallabel')}='';
582: }
1.54 www 583: if (($env{'form.'.&Apache::edit::html_element_name('xfinal')} ne '') && ($env{'form.'.&Apache::edit::html_element_name('xfinallabel')} eq 'end')) {
1.53 www 584: $env{'form.'.&Apache::edit::html_element_name('xfinallabel')}='';
585: }
1.29 www 586: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
1.35 www 587: $safeeval,'index','derivativeorder',
588: 'xinitial','xinitiallabel','xfinal','xfinallabel',
589: 'minimumlength','maximumlength',
590: 'relationship','value','percenterror');
1.29 www 591: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
1.13 www 592: }
1.12 www 593: return $result;
594: }
595:
1.25 www 596: sub end_functionplotrule {
1.13 www 597: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
598: my $result='';
599: if ($target eq 'edit') {
600: $result=&Apache::edit::end_table();
601: }
602: return $result;
1.12 www 603: }
604:
605:
606: #
1.23 www 607: # <spline index="..." order="1,2,3,4" initx="..." inity="..." scalex="..." scaley="..." />
1.6 www 608: #
609: # Unfortunately, GeoGebra seems to want all splines after everything else, so we need to store them
610: #
611: sub start_spline {
612: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1.13 www 613: my $result='';
614: if ($target eq 'web') {
1.23 www 615: my $label=&Apache::lonxml::get_param('index',$parstack,$safeeval);
1.13 www 616: $Apache::functionplotresponse::counter++;
617: if ($label=~/\W/) {
1.23 www 618: &Apache::lonxml::warning(&mt('Spline indices should only contain alphanumeric characters.'));
1.13 www 619: }
620: $label=~s/\W//gs;
1.23 www 621: unless ($label) {
622: $label='S'.$Apache::functionplotresponse::counter;
623: } else {
624: $label='S'.$label;
625: }
1.13 www 626: if ($Apache::functionplotresponse::splineorder{$label}) {
1.23 www 627: &Apache::lonxml::error(&mt('Spline indices must be unique.'));
1.13 www 628: }
629:
630: my $order=&Apache::lonxml::get_param('order',$parstack,$safeeval);
631: if ($order<2) { $order=2; }
632: if ($order>8) { $order=8; }
633: $Apache::functionplotresponse::splineorder{$label}=$order;
634:
635: my $x=&Apache::lonxml::get_param('initx',$parstack,$safeeval);
636: unless ($x) { $x=0; }
637: $Apache::functionplotresponse::splineinitx{$label}=$x;
638:
639: my $y=&Apache::lonxml::get_param('inity',$parstack,$safeeval);
640: unless ($y) { $y=0; }
641: $Apache::functionplotresponse::splineinity{$label}=$y;
642:
643: my $sx=&Apache::lonxml::get_param('scalex',$parstack,$safeeval);
644: unless ($sx) { $sx=$order; }
645: $Apache::functionplotresponse::splinescalex{$label}=$sx;
646:
647: my $sy=&Apache::lonxml::get_param('scaley',$parstack,$safeeval);
648: unless ($sy) { $sy=2; }
649: $Apache::functionplotresponse::splinescaley{$label}=$sy;
650: } elsif ($target eq 'edit') {
651: $result=&Apache::edit::tag_start($target,$token,'Spline').
1.23 www 652: &Apache::edit::text_arg('Index:','index',
1.14 www 653: $token,'4').' '.
1.13 www 654: &Apache::edit::select_arg('Order:','order',
1.44 www 655: ['2','3','4','5','6','7','8'],$token).' '.
1.13 www 656: &Apache::edit::text_arg('Initial x-value:','initx',
1.14 www 657: $token,'4').' '.
1.13 www 658: &Apache::edit::text_arg('Initial y-value:','inity',
1.14 www 659: $token,'4').' '.
1.13 www 660: &Apache::edit::text_arg('Scale x:','scalex',
1.14 www 661: $token,'4').' '.
1.13 www 662: &Apache::edit::text_arg('Scale y:','scaley',
663: $token,'4').
664: &Apache::edit::end_row();
665: } elsif ($target eq 'modified') {
666: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
1.23 www 667: $safeeval,'index','order','initx','inity',
1.13 www 668: 'scalex','scaley');
669: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
670: }
671: return $result;
1.6 www 672: }
673:
674: sub end_spline {
1.13 www 675: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
676: my $result='';
677: if ($target eq 'edit') {
678: $result=&Apache::edit::end_table();
679: }
680: return $result;
1.4 www 681: }
682:
1.3 www 683: sub end_init_script {
684: return (<<ENDENDINIT);
685: }
686: // ]]>
687: </script>
688: ENDENDINIT
689: }
690:
1.10 www 691: #
692: # Storing and restoring spline coordinates from part answers
693: #
694: sub decode_previous_answer {
695: my ($answer)=@_;
696: foreach my $coordinate (split(/\,/,$answer)) {
697: my ($key,$value)=split(/\=/,$coordinate);
698: $Apache::functionplotresponse::previous{$key}=$value;
699: }
700: }
701:
702: sub get_answer_from_form_fields {
703: my ($id)=@_;
704: my $answer='';
705: my %coords=();
706: foreach my $field (keys(%env)) {
707: if ($field=~/^form\.HWVAL\_$id/) {
708: $field=~/^form\.(.*)$/;
709: $coords{$1}=$env{$field};
710: }
711: }
712: $answer=join(',',map { $_.'='.$coords{$_} } (sort(keys(%coords))));
713: return ($answer,%coords);
714: }
715:
1.15 www 716: #
717: # The following functions calculate the cubic-hermite splines server-side
718: #
719:
720: sub cubic_hermite {
721: my ($t,$p1,$s1,$p2,$s2)=@_;
1.42 www 722: return (2.*$t*$t*$t-3.*$t*$t+1.)*$p1 + 3.*($t*$t*$t-2.*$t*$t+$t)*($s1-$p1)+
723: (-2.*$t*$t*$t+3.*$t*$t) *$p2 + 3.*($t*$t*$t-$t*$t) *($s2-$p2);
1.15 www 724: }
725:
726: #
727: # d/dt(...)
728: #
729:
730: sub ddt_cubic_hermite {
731: my ($t,$p1,$s1,$p2,$s2)=@_;
1.42 www 732: return (6.*$t*$t-6.*$t) *$p1 + 3.*(3.*$t*$t-4.*$t+1.)*($s1-$p1)+
733: (-6.*$t*$t+6.*$t)*$p2 + 3.*(3.*$t*$t-2.*$t) *($s2-$p2);
1.15 www 734: }
735:
736: #
737: # d^2/dt^2(...)
738: #
739:
740: sub d2dt2_cubic_hermite {
741: my ($t,$p1,$s1,$p2,$s2)=@_;
1.42 www 742: return (12.*$t-6.) *$p1 + 3.*(6.*$t-4.)*($s1-$p1)+
743: (-12.*$t+6.)*$p2 + 3.*(6.*$t-2.)*($s2-$p2);
1.15 www 744: }
745:
746: #
1.18 www 747: # Array index calculation
748: #
749: sub array_index {
750: my ($xmin,$xmax,$x)=@_;
1.38 www 751: if ($x ne '') {
752: return int(($x-$xmin)/($xmax-$xmin)*400.+0.5);
753: } else {
754: return undef;
755: }
1.20 www 756: }
757:
758: #
1.18 www 759: # Populate the arrays
760: #
761:
762: sub populate_arrays {
1.64 www 763: my ($id,$xmin,$xmax,$ymin,$ymax)=@_;
1.26 www 764: for (my $i=0; $i<=400; $i++) {
1.35 www 765: $Apache::functionplotresponse::actualxval[$i]=undef;
1.19 www 766: $Apache::functionplotresponse::func[$i]=undef;
1.22 www 767: $Apache::functionplotresponse::dfuncdx[$i]=undef;
1.19 www 768: $Apache::functionplotresponse::d2funcd2x[$i]=undef;
1.18 www 769: }
1.19 www 770: unless ($xmax>$xmin) { return 'no_func'; }
1.18 www 771: # Run over all splines in response
772: foreach my $label (split(/\,/,$env{"form.HWVAL_AllSplines_$id"})) {
1.19 www 773: my $xiold=-1;
1.18 www 774: # Run over all points in spline
1.19 www 775: for (my $i=1; $i<$env{"form.HWVAL_SplineOrder_".$id."_".$label}; $i++) {
1.18 www 776: my $ni=$i+1;
777: my @xparms=($env{'form.HWVAL_'.$id.'_'.$label.'P'.$i.'_x'},
778: $env{'form.HWVAL_'.$id.'_'.$label.'S'.$i.'_x'},
779: $env{'form.HWVAL_'.$id.'_'.$label.'P'.$ni.'_x'},
780: $env{'form.HWVAL_'.$id.'_'.$label.'S'.$ni.'_x'});
781: my @yparms=($env{'form.HWVAL_'.$id.'_'.$label.'P'.$i.'_y'},
782: $env{'form.HWVAL_'.$id.'_'.$label.'S'.$i.'_y'},
783: $env{'form.HWVAL_'.$id.'_'.$label.'P'.$ni.'_y'},
784: $env{'form.HWVAL_'.$id.'_'.$label.'S'.$ni.'_y'});
785: # Run in small steps over spline parameter
1.26 www 786: for (my $t=0; $t<=1; $t+=0.0001) {
1.35 www 787: my $xreal=&cubic_hermite($t,@xparms);
788: my $xi=&array_index($xmin,$xmax,$xreal);
1.19 www 789: if ($xi<$xiold) { return 'no_func'; }
1.26 www 790: if (($xi>$xiold) && ($xi>=0) && ($xi<=400)) {
1.18 www 791: $xiold=$xi;
1.35 www 792: $Apache::functionplotresponse::actualxval[$xi]=$xreal;
1.22 www 793: # Function value
1.30 www 794: my $funcval=&cubic_hermite($t,@yparms);
1.64 www 795:
796: # Do we already have a value for this point, and is it different from the new one?
797: if ((defined($Apache::functionplotresponse::func[$xi])) &&
798: (abs($Apache::functionplotresponse::func[$xi]-$funcval)>($ymax-$ymin)/100.)) {
799: return 'no_func';
800: }
801: # Okay, remember the new point
1.30 www 802: $Apache::functionplotresponse::func[$xi]=$funcval;
1.64 www 803:
1.30 www 804: if (defined($funcval)) {
805: if ($xi<$Apache::functionplotresponse::functionplotrulelabels{'start'}) {
806: $Apache::functionplotresponse::functionplotrulelabels{'start'}=$xi;
807: }
808: if ($xi>$Apache::functionplotresponse::functionplotrulelabels{'end'}) {
809: $Apache::functionplotresponse::functionplotrulelabels{'end'}=$xi;
810: }
811: }
1.28 www 812: # Chain rule
1.22 www 813: # dy/dx=dy/dt/(dx/dt)
814: my $dxdt=&ddt_cubic_hermite($t,@xparms);
815: if ($dxdt) {
816: $Apache::functionplotresponse::dfuncdx[$xi]=&ddt_cubic_hermite($t,@yparms)/$dxdt;
1.34 www 817: # Second derivative
1.28 www 818: $Apache::functionplotresponse::d2funcdx2[$xi]=
1.34 www 819: ($dxdt*&d2dt2_cubic_hermite($t,@yparms)-&ddt_cubic_hermite($t,@yparms)*&d2dt2_cubic_hermite($t,@xparms))/
820: ($dxdt*$dxdt*$dxdt);
1.22 www 821: }
1.18 www 822: }
823: }
824: }
825: }
826: }
827:
828: #
1.37 www 829: # Implementation of <functionplotresponse>
1.15 www 830: #
831:
1.1 www 832: sub start_functionplotresponse {
833: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
834: my $result='';
1.6 www 835: # To remember the splines - somehow, they need to come last
836: undef %Apache::functionplotresponse::splineorder;
837: undef %Apache::functionplotresponse::splineinitx;
838: undef %Apache::functionplotresponse::splineinity;
839: undef %Apache::functionplotresponse::splinescalex;
840: undef %Apache::functionplotresponse::splinescaley;
1.10 www 841: # Remember input fields, etc
842: undef %Apache::functionplotresponse::previous;
1.9 www 843: $Apache::functionplotresponse::inputfields='';
1.6 www 844: $Apache::functionplotresponse::counter=0;
1.24 www 845: # Remember rules
1.25 www 846: undef @Apache::functionplotresponse::functionplotrules;
1.37 www 847: # Remember failed rules
848: if ($target eq 'grade') {
849: undef @Apache::functionplotresponse::failedrules;
850: }
851: # Delete previous awards
852: undef $Apache::functionplotresponse::awarddetail;
1.10 www 853: # Part and ID
854: my $partid=$Apache::inputtags::part;
855: my $id=&Apache::response::start_response($parstack,$safeeval);
1.6 www 856: # Internal ID to mark the applet and its coordinates
1.10 www 857: my $internalid = $partid.'_'.$id;
858: # Previous answer
859: &decode_previous_answer($Apache::lonhomework::history{"resource.$partid.$id.submission"});
860:
1.6 www 861: # Parameters of <functionplotresponse>
1.41 www 862: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval);
1.6 www 863: my $xaxisvisible=(&Apache::lonxml::get_param('xaxisvisible',$parstack,$safeeval)=~/on|true|yes|1/i?'true':'false');
864: my $yaxisvisible=(&Apache::lonxml::get_param('yaxisvisible',$parstack,$safeeval)=~/on|true|yes|1/i?'true':'false');
865: my $gridvisible=(&Apache::lonxml::get_param('gridvisible',$parstack,$safeeval)=~/on|true|yes|1/i?'true':'false');
1.14 www 866: my $xlabel=&Apache::lonxml::get_param('xlabel',$parstack,$safeeval);
867: my $ylabel=&Apache::lonxml::get_param('ylabel',$parstack,$safeeval);
1.12 www 868: if ($target eq 'edit') {
869: $result.=&Apache::edit::start_table($token)
870: .'<tr><td><span class="LC_nobreak">'.&mt('Function Plot Question').'</span></td>'
871: .'<td><span class="LC_nobreak">'.&mt('Delete?').' '
1.37 www 872: .&Apache::edit::deletelist($target,$token).' '
1.50 www 873: .&Apache::edit::insertlist($target,$token).' '
874: .&Apache::loncommon::help_open_topic('Function_Plot_Response_Question','Function Plot Responses')
1.12 www 875: .'</span></td>'
876: ."<td> "
877: .&Apache::edit::end_row()
878: .&Apache::edit::start_spanning_row()
879: ."\n";
1.14 www 880: $result.=&Apache::edit::text_arg('Label x-axis:','xlabel',
881: $token,'6').' '.
882: &Apache::edit::text_arg('Minimum x-value:','xmin',
883: $token,'4').' '.
1.12 www 884: &Apache::edit::text_arg('Maximum x-value:','xmax',
1.14 www 885: $token,'4').' '.
1.12 www 886: &Apache::edit::select_arg('x-axis visible:','xaxisvisible',
1.32 www 887: ['yes','no'],$token).'<br />'.
1.14 www 888: &Apache::edit::text_arg('Label y-axis:','ylabel',
889: $token,'6').' '.
1.12 www 890: &Apache::edit::text_arg('Minimum y-value:','ymin',
1.14 www 891: $token,'4').' '.
1.12 www 892: &Apache::edit::text_arg('Maximum y-value:','ymax',
1.14 www 893: $token,'4').' '.
1.12 www 894: &Apache::edit::select_arg('y-axis visible:','yaxisvisible',
1.32 www 895: ['yes','no'],$token).'<br />'.
1.12 www 896: &Apache::edit::select_arg('Grid visible:','gridvisible',
1.47 www 897: ['yes','no'],$token).'<br />'.
1.58 www 898: &Apache::edit::text_arg('Background plot(s) for answer (function(x):xmin:xmax,function(x):xmin:xmax,x1:y1:sx1:sy1:x2:y2:sx2:sy2,...):',
1.63 raeburn 899: 'answerdisplay',$token,'50').
1.12 www 900: &Apache::edit::end_row().&Apache::edit::start_spanning_row();
901: } elsif ($target eq 'modified') {
902: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
1.14 www 903: $safeeval,'xlabel','xmin','xmax','ylabel','ymin','ymax',
1.47 www 904: 'xaxisvisible','yaxisvisible','gridvisible','answerdisplay');
1.12 www 905: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
1.3 www 906:
1.12 www 907: } elsif ($target eq 'meta') {
1.10 www 908: $result=&Apache::response::meta_package_write('functionplotresponse');
1.35 www 909: } elsif (($target eq 'answer') &&
910: ($env{'form.answer_output_mode'} ne 'tex') &&
911: ($Apache::lonhomework::viewgrades == 'F')) {
912: my (undef,undef,$udom,$uname)=&Apache::lonnet::whichuser();
913: my $windowopen=&Apache::lonhtmlcommon::javascript_docopen();
914: my $start_page = &Apache::loncommon::start_page('Rules Log', undef,
915: {'only_body' => 1,
916: 'bgcolor' => '#FFFFFF',
917: 'js_ready' => 1,});
918: my $end_page = &Apache::loncommon::end_page({'js_ready' => 1,});
919: $uname =~s/\W//g;
920: $udom =~s/\W//g;
921: my $function_name =
922: join('_','LONCAPA_scriptvars',$uname,$udom,
923: $env{'form.counter'},$Apache::lonxml::curdepth);
924: my $rules_var ="<script type=\"text/javascript\">
925: // <![CDATA[
926: function $function_name() {newWindow=open('','new_W','width=500,height=500,scrollbars=1,resizable=yes');newWindow.$windowopen;newWindow.document.writeln('$start_page<pre>".
927: $Apache::functionplotresponse::ruleslog.
928: "<\\/pre>$end_page');newWindow.document.close();newWindow.focus()}
929: // ]]>
930: </script><a href=\"javascript:$function_name();void(0);\">".&mt('Rules Log')."</a><br />";
931: &Apache::lonxml::add_script_result($rules_var);
932: }
933:
1.1 www 934: return $result;
935: }
936:
1.25 www 937: sub compare_rel {
1.26 www 938: my ($relationship,$value,$realval,$tol)=@_;
1.36 www 939: # is the real value undefined?
1.26 www 940: unless (defined($realval)) {
1.36 www 941: # the real value is not defined
1.26 www 942: if ($relationship eq 'eq') {
943: if ($value eq 'undef') {
944: return 1;
945: } else {
946: return 0;
947: }
948: } elsif ($relationship eq 'ne') {
949: if ($value eq 'undef') {
950: return 0;
951: } else {
952: return 1;
953: }
954: } else {
955: return 0;
956: }
957: }
958:
1.36 www 959: # is the expected value undefined?
960: if ($value eq 'undef') {
961: # but by now we know that the real value is defined
962: return 0;
963: }
964:
965: # both are defined.
1.26 www 966: if ($relationship eq 'gt') {
967: return ($realval>$value);
968: } elsif ($relationship eq 'ge') {
969: return ($realval>$value-$tol);
970: } elsif ($relationship eq 'lt') {
971: return ($realval<$value);
972: } elsif ($relationship eq 'le') {
973: return ($realval<$value+$tol);
974: } elsif ($relationship eq 'ne') {
975: return (abs($value-$realval)>$tol);
976: } else {
977: return (abs($value-$realval)<$tol);
978: }
1.25 www 979: return 0;
980: }
981:
1.35 www 982: sub addlog {
983: my ($text)=@_;
1.67 www 984: $text=~s/\'/\\\'/g;
1.35 www 985: $Apache::functionplotresponse::ruleslog.=$text.'<br />';
986: }
987:
988: sub actualval {
989: my ($i,$xmin,$xmax)=@_;
990: return $xmin+$i/400.*($xmax-$xmin);
991: }
1.65 www 992:
1.66 www 993: sub fpr_val {
994: my ($arg)=@_;
995: return &actualval($Apache::functionplotresponse::functionplotrulelabels{$arg},
996: $Apache::functionplotresponse::fpr_xmin,
997: $Apache::functionplotresponse::fpr_xmax);
998: }
999:
1.65 www 1000: sub fpr_f {
1001: my ($arg)=@_;
1002: return $Apache::functionplotresponse::func[&array_index($Apache::functionplotresponse::fpr_xmin,
1003: $Apache::functionplotresponse::fpr_xmax,
1004: $arg)];
1005: }
1006:
1007: sub fpr_dfdx {
1008: my ($arg)=@_;
1009: return $Apache::functionplotresponse::dfuncdx[&array_index($Apache::functionplotresponse::fpr_xmin,
1010: $Apache::functionplotresponse::fpr_xmax,
1011: $arg)];
1012: }
1013:
1014: sub fpr_d2fdx2 {
1015: my ($arg)=@_;
1016: return $Apache::functionplotresponse::d2funcdx2[&array_index($Apache::functionplotresponse::fpr_xmin,
1017: $Apache::functionplotresponse::fpr_xmax,
1018: $arg)];
1019: }
1.35 www 1020:
1.25 www 1021: sub functionplotrulecheck {
1.65 www 1022: my ($rule,$xmin,$xmax,$ymin,$ymax,$safeeval)=@_;
1.33 www 1023:
1.35 www 1024: my ($label,$derivative,$xinitial,$xinitiallabel,$xfinal,$xfinallabel,$minimumlength,$maximumlength,$relationship,$value,$percent)
1.25 www 1025: =split(/\:/,$rule);
1.33 www 1026: $percent=($percent>0?$percent:5);
1.35 www 1027: &addlog("=================");
1.42 www 1028: &addlog("Rule $label for ".($derivative<0?'integral':('function itself','first derivative','second derivative')[$derivative])." $relationship $value");
1.65 www 1029: #
1030: # Evaluate the value
1031: #
1032: if ($value=~/\D/) {
1033: $Apache::functionplotresponse::fpr_xmin=$xmin;
1034: $Apache::functionplotresponse::fpr_xmax=$xmax;
1035: $value=&Apache::run::run($value,$safeeval);
1036: &addlog("Value evaluated to $value");
1037: }
1038:
1039: #
1040: # Minimum and maximum lengths of the interval
1041: #
1.49 www 1042: if ((defined($minimumlength)) || (defined($maximumlength))) {
1043: &addlog("Minimumlength $minimumlength Maximumlength $maximumlength");
1044: }
1.30 www 1045: my $li=0;
1046: my $lh=400;
1047:
1.31 www 1048: # Special case: the upper boundary was not defined
1049: # and needs to be set to the value where
1050: # the condition is not true anymore => set flag
1051:
1052: my $findupper=0;
1053: if (($xfinal eq '')
1054: && (!defined($Apache::functionplotresponse::functionplotrulelabels{$xfinallabel}))
1055: && ($xfinallabel)) {
1056: $findupper=1;
1057: }
1058:
1.25 www 1059: # if a hard value is set for the boundaries, it overrides the label
1.30 www 1060: if (($xinitial ne '') && ($xinitiallabel ne '') && ($xinitiallabel ne 'start')) {
1061: $li=&array_index($xmin,$xmax,$xinitial);
1062: $Apache::functionplotresponse::functionplotrulelabels{$xinitiallabel}=$li;
1063: }
1064: if (($xfinal ne '') && ($xfinallabel ne '') && ($xfinallabel ne 'end')) {
1065: $lh=&array_index($xmin,$xmax,$xfinal);
1066: $Apache::functionplotresponse::functionplotrulelabels{$xfinallabel}=$lh;
1.25 www 1067: }
1.31 www 1068: # if the label is defined, use it
1.25 www 1069: if (defined($Apache::functionplotresponse::functionplotrulelabels{$xinitiallabel})) {
1.35 www 1070: &addlog("Using lower label $xinitiallabel");
1.30 www 1071: $li=$Apache::functionplotresponse::functionplotrulelabels{$xinitiallabel};
1072: } else {
1073: $li=&array_index($xmin,$xmax,$xinitial);
1.25 www 1074: }
1.31 www 1075: unless ($findupper) {
1076: if (defined($Apache::functionplotresponse::functionplotrulelabels{$xfinallabel})) {
1.35 www 1077: &addlog("Using upper label $xfinallabel");
1.33 www 1078: $lh=$Apache::functionplotresponse::functionplotrulelabels{$xfinallabel}-1;
1.31 www 1079: } else {
1080: $lh=&array_index($xmin,$xmax,$xfinal);
1081: }
1.25 www 1082: }
1083: # Basic sanity checks
1.30 www 1084: if ($li<0) { $li=0; }
1085: if ($lh>400) { $lh=400; }
1.38 www 1086: if (($li>$lh) || (!defined($lh))) {
1.30 www 1087: $lh=$li;
1.25 www 1088: }
1.31 www 1089:
1.35 www 1090: &addlog("Boundaries: x=".&actualval($li,$xmin,$xmax)." (".$Apache::functionplotresponse::actualxval[$li]."; index $li)) to x=".
1091: &actualval($lh,$xmin,$xmax)." (".$Apache::functionplotresponse::actualxval[$lh]."; index $lh))");
1092: if ($findupper) {
1093: &addlog("Looking for label $xfinallabel");
1094: }
1.33 www 1095: my $tol=$percent*($ymax-$ymin)/100;
1096: if ($xmax>$xmin) {
1097: if ($derivative==2) {
1098: $tol=4.*$tol/($xmax-$xmin);
1099: } elsif ($derivative==1) {
1100: $tol=2.*$tol/($xmax-$xmin);
1.42 www 1101: } elsif ($derivative==-1) {
1102: $tol=$tol*($xmax-$xmin)/2.;
1.33 www 1103: }
1.27 www 1104: }
1.42 www 1105: my $integral=0;
1106: my $binwidth=($xmax-$xmin)/400.;
1107: if (($derivative<0) && (!$findupper)) {
1108: # definite integral, calculate over whole length
1109: &addlog("Calculating definite integral");
1110: for (my $i=$li; $i<=$lh; $i++) {
1111: $integral+=$Apache::functionplotresponse::func[$i]*$binwidth;
1112: }
1113: unless (&compare_rel($relationship,$value,$integral,$tol)) {
1114: &addlog("Actual integral ".(defined($integral)?$integral:'undef').", expected $value, tolerance $tol");
1115: &addlog("Rule $label failed.");
1116: my $hintlabel=$label;
1117: $hintlabel=~s/^R//;
1118: push(@Apache::functionplotresponse::failedrules,$hintlabel);
1119: &addlog("Set hint condition $hintlabel");
1120: return 0;
1121: }
1122: } else {
1123: for (my $i=$li; $i<=$lh; $i++) {
1.25 www 1124: my $val;
1125: if ($derivative==2) {
1.27 www 1126: $val=$Apache::functionplotresponse::d2funcdx2[$i];
1.25 www 1127: } elsif ($derivative==1) {
1.27 www 1128: $val=$Apache::functionplotresponse::dfuncdx[$i];
1.42 www 1129: } elsif ($derivative==-1) {
1130: $integral+=$Apache::functionplotresponse::func[$i]*$binwidth;
1131: $val=$integral;
1.25 www 1132: } else {
1.27 www 1133: $val=$Apache::functionplotresponse::func[$i];
1.25 www 1134: }
1.31 www 1135: unless (&compare_rel($relationship,$value,$val,$tol)) {
1.37 www 1136: &addlog("Actual value ".(defined($val)?$val:'undef').", expected $value, tolerance $tol");
1.35 www 1137: &addlog("Condition not fulfilled at x=".&actualval($i,$xmin,$xmax)." (".$Apache::functionplotresponse::actualxval[$i]."; index $i)");
1.31 www 1138: if (($findupper) && ($i>$li)) {
1.49 www 1139: # Check lengths
1140: unless (&checklength($i,$li,$minimumlength,$maximumlength,$xmin,$xmax,$label)) { return 0; }
1141: # Successfully found a new label, set it
1.31 www 1142: $Apache::functionplotresponse::functionplotrulelabels{$xfinallabel}=$i;
1.35 www 1143: &addlog("Rule $label passed, setting label $xfinallabel");
1.31 www 1144: return 1;
1145: } else {
1.35 www 1146: &addlog("Rule $label failed.");
1.49 www 1147: &setfailed($label);
1.31 www 1148: return 0;
1149: }
1150: }
1.42 www 1151: }
1.25 www 1152: }
1.49 www 1153: # Corner case where this makes sense: using start or stop as defined labels
1154: unless (&checklength($lh,$li,$minimumlength,$maximumlength,$xmin,$xmax,$label)) { return 0; }
1.35 www 1155: &addlog("Rule $label passed.");
1.27 www 1156: return 1;
1.24 www 1157: }
1158:
1.49 www 1159: #
1160: # check for minimum and maximum lengths
1161: #
1162:
1163: sub checklength {
1164: my ($i,$li,$minimumlength,$maximumlength,$xmin,$xmax,$label)=@_;
1165: unless (($minimumlength) || ($maximumlength)) { return 1; }
1166: my $length=&actualval($i,$xmin,$xmax)-&actualval($li,$xmin,$xmax);
1167: if ($minimumlength) {
1168: if ($length<$minimumlength) {
1169: &addlog("Rule $label failed, actual length $length, minimum length $minimumlength");
1170: &setfailed($label);
1171: return 0;
1172: }
1173: }
1174: if ($maximumlength) {
1175: if ($length>$maximumlength) {
1176: &addlog("Rule $label failed, actual length $length, maximum length $maximumlength");
1177: &setfailed($label);
1178: return 0;
1179: }
1180: }
1181: return 1;
1182: }
1183:
1184: sub setfailed {
1185: my ($label)=@_;
1186: my $hintlabel=$label;
1187: $hintlabel=~s/^R//;
1188: push(@Apache::functionplotresponse::failedrules,$hintlabel);
1189: &addlog("Set hint condition $hintlabel");
1190: }
1191:
1.37 www 1192: sub start_functionplotruleset {
1193: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1194: if ($target eq 'edit') {
1195: return &Apache::edit::start_table($token).
1196: '<tr><td><span class="LC_nobreak">'.&mt('Function Plot Rule Set').'</span></td>'
1197: .'<td><span class="LC_nobreak">'.&mt('Delete?').' '
1198: .&Apache::edit::deletelist($target,$token).' '.
1.50 www 1199: &Apache::edit::insertlist($target,$token).' '
1200: .&Apache::loncommon::help_open_topic('Function_Plot_Response_Rule_Set','Function Plot Rules')
1.37 www 1201: .'</span></td>'
1202: ."<td> "
1203: .&Apache::edit::end_row()
1204: .&Apache::edit::start_spanning_row()
1205: ."\n";
1206: }
1207: }
1.3 www 1208:
1.37 www 1209: sub end_functionplotruleset {
1210: my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
1211: my $id=$Apache::inputtags::response[-1];
1212: my $partid=$Apache::inputtags::part;
1213: my $internalid = $partid.'_'.$id;
1214:
1215: if ($target eq 'edit' ) {
1216: return &Apache::edit::end_table();
1217: } elsif ($target eq 'grade'
1.10 www 1218: && &Apache::response::submitted()
1219: && $Apache::lonhomework::type ne 'exam') {
1.12 www 1220: #
1221: # Actually grade
1222: #
1.41 www 1223: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-2);
1.27 www 1224:
1.24 www 1225: my $ad='';
1.30 www 1226: undef %Apache::functionplotresponse::functionplotrulelabels;
1.35 www 1227: $Apache::functionplotresponse::ruleslog='';
1.30 www 1228: $Apache::functionplotresponse::functionplotrulelabels{'start'}=400;
1229: $Apache::functionplotresponse::functionplotrulelabels{'end'}=0;
1.64 www 1230: if (&populate_arrays($internalid,$xmin,$xmax,$ymin,$ymax) eq 'no_func') {
1.21 www 1231: $ad='NOT_FUNCTION';
1.18 www 1232: } else {
1.35 www 1233: &addlog("Start of function ".&actualval($Apache::functionplotresponse::functionplotrulelabels{'start'},$xmin,$xmax)." (index ".
1234: $Apache::functionplotresponse::functionplotrulelabels{'start'}.")");
1235: &addlog("End of function ".&actualval($Apache::functionplotresponse::functionplotrulelabels{'end'},$xmin,$xmax)." (index ".
1236: $Apache::functionplotresponse::functionplotrulelabels{'end'}.")");
1237:
1.24 www 1238: # We have a function that we can actually grade, go through the spline rules.
1.25 www 1239: foreach my $rule (@Apache::functionplotresponse::functionplotrules) {
1.65 www 1240: unless (&functionplotrulecheck($rule,$xmin,$xmax,$ymin,$ymax,$safeeval)) {
1.24 www 1241: $ad='INCORRECT';
1242: last;
1243: }
1244: }
1245: # If it's not wrong, it's correct
1246: unless ($ad) { $ad='EXACT_ANS' };
1.18 www 1247: }
1.37 www 1248: &addlog("Set hint conditions: ".join(",",@Apache::functionplotresponse::failedrules));
1249: &addlog("Assigned award detail: $ad");
1250: # Store for later to be assigned at end_functionplotresponse
1251: $Apache::functionplotresponse::awarddetail=$ad;
1252: }
1253: }
1254:
1255:
1256: sub end_functionplotresponse {
1257: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1258: &Apache::response::end_response;
1259:
1260: my $result;
1261: my $id=$Apache::inputtags::response[-1];
1262: my $partid=$Apache::inputtags::part;
1263: my $internalid = $partid.'_'.$id;
1.20 www 1264:
1.37 www 1265: if ($target eq 'edit') { $result=&Apache::edit::end_table(); }
1266: if ($target eq 'grade'
1267: && &Apache::response::submitted()
1268: && $Apache::lonhomework::type eq 'exam') {
1269:
1270: &Apache::response::scored_response($partid,$id);
1271:
1272: } elsif ($target eq 'grade'
1273: && &Apache::response::submitted()
1274: && $Apache::lonhomework::type ne 'exam') {
1275: my ($response,%coords)=&get_answer_from_form_fields($internalid);
1276: $Apache::lonhomework::results{"resource.$partid.$id.submission"}=$response;
1277: my %previous=&Apache::response::check_for_previous($response,$partid,$id);
1278: #
1279: # Assign grade
1280: #
1281: my $ad=$Apache::functionplotresponse::awarddetail;
1.12 www 1282: #
1283: # Store grading info
1284: #
1285: $Apache::lonhomework::results{"resource.$partid.$id.awarddetail"}=$ad;
1286: &Apache::response::handle_previous(\%previous,$ad);
1.10 www 1287: } elsif ($target eq 'web') {
1.37 www 1288: undef @Apache::functionplotresponse::failedrules;
1289: }
1290: return $result;
1291: }
1292:
1293: sub end_functionplotelements {
1294: my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
1295: my $result='';
1296: my $id=$Apache::inputtags::response[-1];
1297: my $partid=$Apache::inputtags::part;
1298: my $internalid = $partid.'_'.$id;
1299: if ($target eq 'edit' ) {
1300: $result=&Apache::edit::end_table();
1301: } elsif ($target eq 'web') {
1.47 www 1302: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-2);
1303:
1304: # Are we in show answer mode?
1305: my $showanswer=&Apache::response::show_answer();
1306: if ($showanswer) {
1307: # Render answerdisplay
1308: my $answerdisplay=&Apache::lonxml::get_param('answerdisplay',$parstack,$safeeval,-2);
1309: if ($answerdisplay=~/\S/s) {
1310: foreach my $plot (split(/\s*\,\s*/,$answerdisplay)) {
1.58 www 1311: my @components=split(/\s*\:\s*/,$plot);
1312: if ($#components<3) {
1313: # Just a simple plot
1314: my ($func,$xl,$xh)=@components;
1315: if ((!defined($xl)) || ($xl eq '')) { $xl=$xmin; }
1316: if ((!defined($xh)) || ($xh eq '')) { $xh=$xmax; }
1317: $result.=&plot_script($internalid,$func,1,'','00aa00',$xl,$xh,6);
1318: } else {
1319: # This is a spline
1.59 www 1320: $result.=&answer_spline_script($internalid,@components);
1.58 www 1321: }
1.47 www 1322: }
1323: }
1324: }
1.48 www 1325: my $fixed=0;
1326: if (($showanswer) || (&Apache::response::check_status()>=2)) { $fixed=1; }
1.6 www 1327: # Now is the time to render all of the stored splines
1328: foreach my $label (keys(%Apache::functionplotresponse::splineorder)) {
1.48 www 1329: $result.=&generate_spline($internalid,$label,$xmin,$xmax,$ymin,$ymax,$fixed);
1.6 www 1330: }
1.5 www 1331: # close the init script
1.3 www 1332: $result.=&end_init_script();
1.17 www 1333: # register all splines in this response
1334: $result.='<input type="hidden" name="HWVAL_AllSplines_'.$internalid.'" value="'.
1335: join(',',keys(%Apache::functionplotresponse::splineorder)).'" />'."\n";
1336: foreach my $label (keys(%Apache::functionplotresponse::splineorder)) {
1337: $result.='<input type="hidden" name="HWVAL_SplineOrder_'.$internalid.'_'.$label.'" value="'.
1338: $Apache::functionplotresponse::splineorder{$label}.'" />'."\n";
1339: }
1.9 www 1340: # generate the input fields
1341: $result.=$Apache::functionplotresponse::inputfields;
1.5 www 1342: # actually start the <applet>-tag
1.3 www 1343: $result.=&geogebra_startcode($internalid);
1.5 www 1344: # load the spline bytecode
1.3 www 1345: $result.=&geogebra_spline_program();
1.5 www 1346: # set default parameters
1.3 www 1347: $result.=&geogebra_default_parameters($internalid);
1.5 www 1348: # close the <applet>-tag
1.1 www 1349: $result.=&geogebra_endcode();
1350: }
1351: return $result;
1352: }
1353:
1.41 www 1354: sub boundaries {
1355: my ($parstack,$safeeval,$level)=@_;
1356: my $xmin=&Apache::lonxml::get_param('xmin',$parstack,$safeeval,$level);
1357: $xmin=(defined($xmin)?$xmin:-10);
1358: my $xmax=&Apache::lonxml::get_param('xmax',$parstack,$safeeval,$level);
1359: $xmax=(defined($xmax)?$xmax:10);
1360: my $ymin=&Apache::lonxml::get_param('ymin',$parstack,$safeeval,$level);
1361: $ymin=(defined($ymin)?$ymin:-10);
1362: my $ymax=&Apache::lonxml::get_param('ymax',$parstack,$safeeval,$level);
1363: $ymax=(defined($ymax)?$ymax:10);
1364: if ($xmax<=$xmin) {
1365: $xmax=$xmin+20;
1366: }
1367: if ($ymax<=$ymin) {
1368: $ymax=$ymin+20;
1369: }
1370: return ($xmin,$xmax,$ymin,$ymax);
1371: }
1372:
1.37 www 1373: sub start_functionplotelements {
1374: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1375: my $result='';
1376: my $id=$Apache::inputtags::response[-1];
1377: my $partid=$Apache::inputtags::part;
1378: my $internalid = $partid.'_'.$id;
1379:
1380: if ($target eq 'edit') {
1381: return &Apache::edit::start_table($token).
1382: '<tr><td><span class="LC_nobreak">'.&mt('Function Plot Elements').'</span></td>'
1383: .'<td><span class="LC_nobreak">'.&mt('Delete?').' '
1384: .&Apache::edit::deletelist($target,$token).' '.
1.50 www 1385: &Apache::edit::insertlist($target,$token).' '
1386: .&Apache::loncommon::help_open_topic('Function_Plot_Response_Elements','Function Plot Elements')
1.37 www 1387: .'</span></td>'
1388: ."<td> "
1389: .&Apache::edit::end_row()
1390: .&Apache::edit::start_spanning_row()
1391: ."\n";
1392: } elsif ($target eq 'web') {
1.41 www 1393: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-2);
1.37 www 1394: my $xaxisvisible=(&Apache::lonxml::get_param('xaxisvisible',$parstack,$safeeval,-2)=~/on|true|yes|1/i?'true':'false');
1395: my $yaxisvisible=(&Apache::lonxml::get_param('yaxisvisible',$parstack,$safeeval,-2)=~/on|true|yes|1/i?'true':'false');
1396: my $gridvisible=(&Apache::lonxml::get_param('gridvisible',$parstack,$safeeval,-2)=~/on|true|yes|1/i?'true':'false');
1397: my $xlabel=&Apache::lonxml::get_param('xlabel',$parstack,$safeeval,-2);
1398: my $ylabel=&Apache::lonxml::get_param('ylabel',$parstack,$safeeval,-2);
1399:
1400:
1401: # paste in the update routine to receive stuff back from the applet
1402: $result.=&update_script($internalid);
1403: # start the initscript for this applet
1404: $result.=&start_init_script($internalid);
1405: # put the axis commands inside
1406: $result.=&axes_script($internalid,$xmin,$xmax,$ymin,$ymax,$xaxisvisible,$yaxisvisible,$gridvisible);
1.45 www 1407: $result.=&axes_label($internalid,$xmin,$xmax,$ymin,$ymax,$xlabel,$ylabel);
1.37 www 1408: # init script is left open
1409: }
1410: return $result;
1411: }
1412:
1.1 www 1413: 1;
1414:
1415: __END__
1416:
1417: =head1 NAME
1418:
1419: Apache::functionplotresponse.pm;
1420:
1421: =head1 SYNOPSIS
1422:
1423: Handles tags associated with accepting function plots.
1424:
1425: This is part of the LearningOnline Network with CAPA project
1426: described at http://www.lon-capa.org.
1427:
1428: =head1 HANDLER SUBROUTINE
1429:
1430: start_functionplotresponse()
1431:
1432: =head1 OTHER SUBROUTINES
1433:
1434: =over
1435:
1436: =item end_functionplotresponse()
1437:
1438: =back
1439:
1440: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>