File:
[LON-CAPA] /
loncom /
homework /
functionplotresponse.pm
Revision
1.107:
download - view:
text,
annotated -
select for diffs
Mon Jan 19 15:35:53 2015 UTC (9 years, 4 months ago) by
goltermann
Branches:
MAIN
CVS tags:
HEAD
authoring space overhaul
this update tries to improve the user experience of the authoring space.
added codemirror for xml editor and script tags in colorful editor
added possibility to deactivate codemirror in author settings
added dropdown menu to insert problem templates into xml editor (thanks to tobias reinhardt)
added feature of saving current scrollposition on save when editing problems
added possibility to fold blocks in colorful editor, this state will be saved and restored
added shortcuts to create empty problems, html files and directories
and other smaller features and bugfixes
1: # LearningOnline Network with CAPA
2: # Functionplot responses
3: #
4: # $Id: functionplotresponse.pm,v 1.107 2015/01/19 15:35:53 goltermann Exp $
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;
34: use Apache::run;
35: use LONCAPA;
36:
37: BEGIN {
38: &Apache::lonxml::register('Apache::functionplotresponse',('functionplotresponse','backgroundplot','spline',
39: 'plotobject','plotvector',
40: 'functionplotvectorrule','functionplotvectorsumrule',
41: 'drawvectorsum',
42: 'functionplotcustomrule',
43: 'functionplotrule','functionplotruleset',
44: 'functionplotelements'));
45: }
46:
47: #
48: # Use old Java or HTML5/Javascript for GeoGebra? Depends on browser!
49: # Return a true value if HTML5 should be used.
50:
51: sub useHTML5 {
52: if ($env{'browser.type'} eq 'chrome') {
53: if ($env{'browser.version'} >= 14) {
54: return 1;
55: }
56: } elsif ($env{'browser.type'} eq 'safari') {
57: if ($env{'browser.os'} eq 'mac') {
58: my ($prefix,$version) = ($env{'browser.version'} =~ /^(\d*)(\d{3})\./);
59: if ((!$env{'browser.mobile'}) ||
60: (($env{'browser.mobile'}) && length($prefix))) {
61: if ($version >= 536) {
62: return 1;
63: }
64: }
65: }
66: } elsif ($env{'browser.type'} eq 'mozilla') {
67: if ($env{'browser.info'} =~ /^firefox\-(\d+)/) {
68: my $firefox = $1;
69: if ((($env{'browser.os'} eq 'mac') && ($firefox >= 20)) ||
70: (($env{'browser.os'} eq 'unix') && ($firefox >= 17)) ||
71: (($env{'browser.os'} eq 'win') && ($firefox >= 14))) {
72: return 1;
73: }
74: }
75: } elsif ($env{'browser.type'} eq 'explorer') {
76: if (($env{'browser.os'} eq 'win') && ($env{'browser.version'} == 10)) {
77: return 1;
78: }
79: }
80: return 0;
81: }
82:
83: #
84: # HTML5 version does not understand "_" in IDs
85: #
86: sub appid {
87: my ($id)=@_;
88: $id=~s/\_/rid/gs;
89: $id=~s/\W//gs;
90: return $id;
91: }
92:
93: #
94: # Routines to start the applet (Java) or the HTML5/JavaScript
95: #
96: # There can be a number of applets on a page, each called ggbApplet_$id,
97: # where $id is the "_"-concatenated part and responseid
98: #
99:
100: sub geogebra_startcode {
101: my ($id,$width,$height)=@_;
102: if (&useHTML5()) {
103: return &html5_geogebra_startcode(@_);
104: } else {
105: return &java_geogebra_startcode(@_).
106: &java_geogebra_code_param();
107: }
108: }
109:
110: sub geogebra_endcode {
111: if (&useHTML5()) {
112: return '';
113: } else {
114: return &java_geogebra_endcode();
115: }
116: }
117:
118: sub geogebra_default_parameters {
119: my ($id)=@_;
120: if (&useHTML5()) {
121: return '';
122: } else {
123: return &java_geogebra_default_parameters($id);
124: }
125: }
126: # === Java code
127:
128: sub java_geogebra_startcode {
129: my ($id,$width,$height)=@_;
130: my $appid=&appid($id);
131: $width=int(1.*$width);
132: $height=int(1.*$height);
133: unless ($width) { $width=700; }
134: unless ($height) { $height=400; }
135: return (<<ENDSTARTCODE);
136: <applet name="ggbApplet$appid" code="geogebra.GeoGebraApplet" archive="geogebra.jar"
137: codebase="/adm/geogebra/" width="$width" height="$height" MAYSCRIPT>
138: <param name="java_arguments" value="-Xmx512m -Djnlp.packEnabled=true"/>
139: ENDSTARTCODE
140: }
141:
142: sub java_geogebra_endcode {
143: return &Apache::lonhtmlcommon::java_not_enabled()."</applet>\n";
144: }
145:
146: sub java_geogebra_code_param {
147: return '<param name="ggbBase64" value="'.&geogebra_internal_program().'" />';
148: }
149:
150: # === HTML5 code
151:
152: sub html5_geogebra_startcode {
153: my ($id,$width,$height)=@_;
154: my $appid=&appid($id);
155: $width=int(1.*$width);
156: $height=int(1.*$height);
157: unless ($width) { $width=700; }
158: unless ($height) { $height=400; }
159: my $code=&geogebra_internal_program();
160: return (<<ENDSTARTCODE);
161: <article class="geogebraweb" data-param-enableLabelDrags="false" data-param-enableShiftDragZoom="false"
162: data-param-width="$width" data-param-height="$height" data-param-id="ggbApplet$appid"
163: data-param-ggbbase64="$code"></article>
164: ENDSTARTCODE
165: }
166:
167: #
168: # This is the internal GeoGebra bytecode which defines the spline functions
169: #
170: sub geogebra_internal_program {
171: return
172: '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';
173: }
174:
175: #
176: # The standard set of parameters inside <applet>
177: #
178: sub java_geogebra_default_parameters {
179: my ($id)=@_;
180: my $appid=&appid($id);
181: return(<<ENDDEFAULTPARAMETERS);
182: <param name="image" value="/adm/lonIcons/lonanim.gif" />
183: <param name="boxborder" value="false" />
184: <param name="centerimage" value="true" />
185: <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" />
186: <param name="cache_version" value="4.4.3.0,4.4.3.0,4.4.3.0,4.4.3.0,4.4.3.0,4.4.3.0,4.4.3.0,4.4.3.0,4.4.3.0,4.4.3.0,4.4.3.0" />
187: <param name="framePossible" value="false" />
188:
189: <param name="showResetIcon" value="false" />
190: <param name="showAnimationButton" value="false" />
191: <param name="enableRightClick" value="false" />
192: <param name="errorDialogsActive" value="true" />
193: <param name="enableLabelDrags" value="false" />
194: <param name="showMenuBar" value="false" />
195: <param name="showToolBar" value="false" />
196: <param name="showToolBarHelp" value="false" />
197: <param name="showAlgebraInput" value="false" />
198: <param name="enableShiftDragZoom" value="false" />
199: <param name="allowRescaling" value="false" />
200: <param name="enableLabelDrags" value="false" />
201: <param name="ggbOnInitParam" value="ggbApplet$appid" />
202: ENDDEFAULTPARAMETERS
203: }
204:
205: #
206: # This subroutine is called by LON-CAPA at </problem>
207: # Each applet on the page will call function ggbOnInit when it is done loading
208: # This function in turn will call the respective function registered by start_init_script
209: # Which one of the registered functions is called is determined by ggbOnInitParam, which GeoGebra passes to ggbOnInit
210: #
211:
212: sub init_script {
213: if ($#Apache::functionplotresponse::callscripts>=0) {
214: my $script='';
215: foreach my $id (@Apache::functionplotresponse::callscripts) {
216: $script.="if (param=='ggbApplet".&appid($id)."') { loaded_$id=true; }\n";
217: }
218: $script.="if (".join(' && ',map { "loaded_$_" } (@Apache::functionplotresponse::callscripts)).
219: ") { setTimeout('ggbInitAll()',200) }";
220: my $calls=join("\n",map { "ggbInit_$_();" } (@Apache::functionplotresponse::callscripts));
221: my $html5init='';
222: if (&useHTML5()) {
223: $html5init=
224: '<script type="text/javascript" language="javascript" src="/adm/geogebra/web/web.nocache.js"></script>';
225: }
226: return (<<ENDGGBINIT);
227: $html5init
228: <script type="text/javascript">
229: // <![CDATA[
230: // Function that each applet will call when loaded
231: // It will pass "its" parameter
232: // Set flags for when an applet is loaded, wait till all are loaded, and then some
233: function ggbOnInit(param) {
234: $script
235: }
236: function ggbInitAll() {
237: $calls
238: }
239: // ]]>
240: </script>
241: ENDGGBINIT
242: }
243: }
244:
245: #
246: # Each Geogebra applet is supposed to call this when parameters change
247: # Changes the hidden fields on the web page
248: #
249: sub update_script {
250: my ($id)=@_;
251: my $appid=&appid($id);
252: return (<<ENDUPDATESCRIPT);
253: <script type="text/javascript">
254: // <![CDATA[
255: function updatePointCoordinates_$id(coordinateName) {
256: var x = document.ggbApplet$appid.getXcoord(coordinateName);
257: var y = document.ggbApplet$appid.getYcoord(coordinateName);
258: document.lonhomework.elements["HWVAL_$id\_" + coordinateName + "_x"].value = x;
259: document.lonhomework.elements["HWVAL_$id\_" + coordinateName + "_y"].value = y;
260: }
261: // ]]>
262: </script>
263: ENDUPDATESCRIPT
264: }
265:
266: #
267: # Register the above update-handler for a variable
268: #
269:
270: sub update_register {
271: my ($id,$variable)=@_;
272: my $appid=&appid($id);
273: return "document.ggbApplet$appid.registerObjectUpdateListener('$variable','updatePointCoordinates_$id');\n";
274: }
275:
276: #
277: # Set a point coordinate variable
278: #
279: sub set_point_coordinate {
280: my ($id,$variable,$x,$y,$fixed)=@_;
281: my $appid=&appid($id);
282: my $mult=($fixed?'a*':'');
283: # Get rid of wild exponents, make sure it's a number
284: $x=1.*$x;
285: $y=1.*$y;
286: # GeoGebra does not understand "E"
287: $x=~s/[e|E]/\*10\^/;
288: $x=~s/\+//;
289: $y=~s/[e|E]/\*10\^/;
290: $y=~s/\+//;
291: return (<<ENDSETVARIABLE);
292: document.ggbApplet$appid.evalCommand("a=1");
293: document.ggbApplet$appid.evalCommand("$variable=$mult($x,$y)");
294: document.ggbApplet$appid.setLabelVisible("$variable",false);
295: ENDSETVARIABLE
296: }
297:
298: #
299: # Set a slope coordinate variable
300: #
301: sub set_slope_coordinate {
302: my ($id,$variable,$xrel,$yrel,$xmin,$xmax,$ymin,$ymax,$pointname,$fixed)=@_;
303: my $appid=&appid($id);
304: my $xvariable=$variable.'x';
305: my $yvariable=$variable.'y';
306: my $domain=$xmax-$xmin;
307: my $range=$ymax-$ymin;
308: my $xinterval=$domain/100.;
309: my $yinterval=$range/200.;
310: my $mult=($fixed?'a*':'');
311: return (<<ENDSETSVARIABLE);
312: document.ggbApplet$appid.evalCommand("a=1");
313: document.ggbApplet$appid.evalCommand("$xvariable=Slider[$xinterval,$domain,$xinterval]");
314: document.ggbApplet$appid.setVisible("$xvariable", false);
315: document.ggbApplet$appid.evalCommand("$xvariable=$xrel");
316: document.ggbApplet$appid.evalCommand("$yvariable=Slider[-$range,$range,$yinterval]");
317: document.ggbApplet$appid.setVisible("$yvariable", false);
318: document.ggbApplet$appid.evalCommand("$yvariable=$yrel");
319: document.ggbApplet$appid.evalCommand("$variable=$mult($xvariable+x($pointname),$yvariable+y($pointname))");
320: document.ggbApplet$appid.setLabelVisible("$variable", false);
321: ENDSETSVARIABLE
322: }
323:
324: #
325: # Input field name for a coordinate variable
326: #
327:
328: sub field_name {
329: my ($id,$variable,$name)=@_;
330: return "HWVAL_$id\_$variable\_$name";
331: }
332:
333: #
334: # Generate an input field for a coordinate variable
335: #
336:
337: sub generate_input_field {
338: my ($id,$variable,$x,$y)=@_;
339: $Apache::functionplotresponse::inputfields.=
340: "<input type='hidden' name='".&field_name($id,$variable,'x')."' value='$x' />\n".
341: "<input type='hidden' name='".&field_name($id,$variable,'y')."' value='$y' />\n";
342: }
343:
344: #
345: # Initialize a new point coordinate variable at set a listener on it
346: #
347: sub new_point_coordinate {
348: my ($id,$variable,$x,$y,$fixed)=@_;
349: if (defined($Apache::functionplotresponse::previous{&field_name($id,$variable,'x')})) {
350: $x=$Apache::functionplotresponse::previous{&field_name($id,$variable,'x')};
351: }
352: if (defined($Apache::functionplotresponse::previous{&field_name($id,$variable,'y')})) {
353: $y=$Apache::functionplotresponse::previous{&field_name($id,$variable,'y')};
354: }
355: &generate_input_field($id,$variable,$x,$y);
356: return &set_point_coordinate($id,$variable,$x,$y,$fixed).&update_register($id,$variable);
357: }
358:
359: #
360: # Initialize a new slope coordinate variable at set a listener on it
361: #
362: sub new_slope_coordinate {
363: my ($id,$variable,$x,$y,$pointname,$xp,$yp,$xmin,$xmax,$ymin,$ymax,$fixed)=@_;
364: #
365: # $variable: name of the slope point
366: # $x, $y: coordinates of the slope point
367: # $pointname: name of the associated point point
368: # $xp $yp: coordinates of the point point
369: #
370: if (defined($Apache::functionplotresponse::previous{&field_name($id,$variable,'x')})) {
371: $x=$Apache::functionplotresponse::previous{&field_name($id,$variable,'x')};
372: }
373: if (defined($Apache::functionplotresponse::previous{&field_name($id,$variable,'y')})) {
374: $y=$Apache::functionplotresponse::previous{&field_name($id,$variable,'y')};
375: }
376: if (defined($Apache::functionplotresponse::previous{&field_name($id,$pointname,'x')})) {
377: $xp=$Apache::functionplotresponse::previous{&field_name($id,$pointname,'x')};
378: }
379: if (defined($Apache::functionplotresponse::previous{&field_name($id,$pointname,'y')})) {
380: $yp=$Apache::functionplotresponse::previous{&field_name($id,$pointname,'y')};
381: }
382:
383: &generate_input_field($id,$variable,$x,$y);
384: my $xrel=$x-$xp;
385: my $yrel=$y-$yp;
386: return &set_slope_coordinate($id,$variable,$xrel,$yrel,$xmin,$xmax,$ymin,$ymax,$pointname,$fixed).&update_register($id,$variable);
387: }
388:
389: #
390: # This registers the init-function call for ggbOnInit, which LON-CAPA will place at </problem>
391: # It then starts the right headers
392: #
393: sub start_init_script {
394: my ($id)=@_;
395: # Add id to the list of ggbInit_$id functions that need to be called
396: push(@Apache::functionplotresponse::callscripts,$id);
397: # ... and open this function
398: return (<<ENDSTARTINIT);
399: <script type="text/javascript">
400: // <![CDATA[
401: // variable that will eventually be passed back to the server
402: var coordinateMap_$id = [];
403: // flag for not loaded yet
404: var loaded_$id=false;
405: // Init-function for applet
406: function ggbInit_$id() {
407: ENDSTARTINIT
408: }
409:
410: #
411: # This sets the axes inside ggbInit_$id
412: #
413:
414: sub axes_script {
415: my ($id,$xmin,$xmax,$ymin,$ymax,$xvisible,$yvisible,$gvisible)=@_;
416: my $appid=&appid($id);
417: return (<<ENDAXESSCRIPT);
418: // changes (xmin, xmax, ymin, ymax)
419: document.ggbApplet$appid.setCoordSystem($xmin,$xmax,$ymin,$ymax);
420:
421: // makes the (x,y) axis (in)visible
422: document.ggbApplet$appid.setAxesVisible($xvisible,$yvisible);
423: // makes the grid (in)visible
424: document.ggbApplet$appid.setGridVisible($gvisible);
425: ENDAXESSCRIPT
426: }
427:
428: sub axes_label {
429: my ($id,$xmin,$xmax,$ymin,$ymax,$xlabel,$ylabel)=@_;
430: my $appid=&appid($id);
431: unless ($xlabel || $ylabel) { return ''; }
432: my $return='document.ggbApplet'.$appid.'.evalCommand("topRight=Corner[3]");';
433: if ($xlabel) {
434: if (($ymin<0) && ($ymax>0)) {
435: $return.=(<<ENDXAXISLABELSCRIPT);
436: document.ggbApplet$appid.evalCommand("Xlabel=(x(topRight)-AxisStepX[],AxisStepY[]/6)");
437: document.ggbApplet$appid.setVisible("Xlabel",false);
438: document.ggbApplet$appid.evalCommand("Text[\\"$xlabel\\", Xlabel]");
439: ENDXAXISLABELSCRIPT
440: } else {
441: $return.=(<<ENDXOFFAXISLABEL);
442: document.ggbApplet$appid.evalCommand("LowerRight=Corner[2]");
443: document.ggbApplet$appid.evalCommand("Text[\\"$xlabel\\", (x(LowerRight) - AxisStepX[], y(LowerRight) + AxisStepY[] / 2)]");
444: ENDXOFFAXISLABEL
445: }
446: }
447: if ($ylabel) {
448: if (($xmin<0) && ($xmax>0)) {
449: $return.=(<<ENDYAXISLABELSCRIPT);
450: document.ggbApplet$appid.evalCommand("Ylabel=(AxisStepX[]/6,y(topRight)-AxisStepY[]/3)");
451: document.ggbApplet$appid.setVisible("Ylabel",false);
452: document.ggbApplet$appid.evalCommand("Text[\\"$ylabel\\", Ylabel]");
453: ENDYAXISLABELSCRIPT
454: } else {
455: $return.=(<<ENDYOFFAXISLABEL);
456: document.ggbApplet$appid.evalCommand("UpperLeft=Corner[4]");
457: document.ggbApplet$appid.evalCommand("Text[\\"$ylabel\\", (x(UpperLeft) + AxisStepX[] / 5, y(UpperLeft) - AxisStepY[] / 1.8)]");
458: ENDYOFFAXISLABEL
459: }
460: }
461: return $return;
462: }
463:
464: #
465: # Subroutine to produce background and answer plots
466: #
467:
468: sub plot_script {
469: my ($id,$function,$fixed,$label,$color,$xmin,$xmax,$thickness)=@_;
470: my $appid=&appid($id);
471: $label=~s/\W//g;
472: if (($label) && ($label!~/^[A-Za-z]/)) {
473: $label='C'.$label;
474: }
475: my $visible=0;
476: if ($label) {
477: $visible="1";
478: } else {
479: $Apache::functionplotresponse::counter++;
480: $label='C'.$Apache::functionplotresponse::counter;
481: }
482: my $rc=0;
483: my $gc=0;
484: my $bc=0;
485: if ($color) {
486: my ($rh,$gh,$bh)=($color=~/(..)(..)(..)/);
487: $rc=hex($rh);
488: $gc=hex($gh);
489: $bc=hex($bh);
490: }
491: if ($fixed) {
492: return "document.ggbApplet$appid.evalCommand('$label=Function[$function,$xmin,$xmax]');\n".
493: ($visible?'':"document.ggbApplet$appid.setLabelVisible('$label', false);\n").
494: ($color?"document.ggbApplet$appid.setColor('$label',$rc,$gc,$bc);\n":'').
495: ($thickness?"document.ggbApplet$appid.setLineThickness('$label',$thickness);\n":'');
496: } else {
497: return "document.ggbApplet$appid.evalCommand('y=$function');\n";
498: }
499: }
500:
501: #
502: # Subroutine to produce objects
503: #
504:
505: sub plotobject_script {
506: my ($id,$label,$x,$y)=@_;
507: my $appid=&appid($id);
508: unless ($label) {
509: $Apache::functionplotresponse::counter++;
510: $label='O'.$Apache::functionplotresponse::counter;
511: }
512: &generate_input_field($id,$label,$x,$y);
513: return "document.ggbApplet$appid.evalCommand('a=1');\n".
514: "document.ggbApplet$appid.setVisible('a', false);\n".
515: "document.ggbApplet$appid.setLabelVisible('a', false);\n".
516: "document.ggbApplet$appid.evalCommand('$label=a*($x,$y)');\n".
517: "document.ggbApplet$appid.setVisible('$label', true);\n".
518: "document.ggbApplet$appid.setLabelVisible('$label', true);\n";
519: }
520:
521: #
522: # Subroutine to produce vectors
523: #
524:
525: sub plotvector_script {
526: my ($id,$label,$xs,$ys,$xe,$ye,$xmin,$xmax,$fixed)=@_;
527: my $appid=&appid($id);
528: unless ($label) {
529: $Apache::functionplotresponse::counter++;
530: $label='V'.$Apache::functionplotresponse::counter;
531: }
532: my $startlabel=$label.'Start';
533: my $endlabel=$label.'End';
534: my $pointlabel=$label.'Point';
535: my $pointx=2.*($xmax-$xmin)+$xmax;
536: my $anglelabel=$label.'Angle';
537: return
538: &new_point_coordinate($id,$startlabel,$xs,$ys,$fixed).
539: &new_point_coordinate($id,$endlabel,$xe,$ye,$fixed).
540: (<<ENDVECTOR);
541: document.ggbApplet$appid.evalCommand("$label=Vector[$startlabel,$endlabel]");
542: document.ggbApplet$appid.setLabelVisible("$label",true);
543: document.ggbApplet$appid.setLineThickness("$label",8);
544: document.ggbApplet$appid.evalCommand("$pointlabel=($pointx,y($startlabel))");
545: document.ggbApplet$appid.evalCommand("$anglelabel=Angle[$pointlabel,$startlabel,$endlabel]");
546: document.ggbApplet$appid.setLabelVisible("$anglelabel",true);
547: document.ggbApplet$appid.setLabelStyle("$anglelabel",VALUE=2);
548: ENDVECTOR
549: }
550:
551: #
552: # Answer spline display
553: #
554: # points: x,y,slope_x,slope_y
555:
556: sub answer_spline_script {
557: my ($id,@points)=@_;
558: my $appid=&appid($id);
559: my $order=int(($#points+1)/4);
560: if ($order<2) { $order=2; }
561: if ($order>8) { $order=8; }
562: $Apache::functionplotresponse::counter++;
563: my $label='CSpline'.$Apache::functionplotresponse::counter;
564: my $output='document.ggbApplet'.$appid.'.evalCommand("'.$label.'=Spline'.$order.'[';
565: for (my $i=0;$i<=$#points;$i+=4) {
566: $output.="($points[$i],$points[$i+1]),($points[$i+2],$points[$i+3]),";
567: }
568: $output=~s/\,$//;
569: $output.=']");'."\n";
570: for (my $i=2; $i<2*$order; $i+=2) {
571: $output.='document.ggbApplet'.$appid.'.setColor("'.$label.'_'.($i>=10?'{':'').$i.($i>=10?'}':'').'",0,170,0);'."\n";
572: }
573: for (my $i=1; $i<2*$order; $i+=2) {
574: $output.='document.ggbApplet'.$appid.'.setVisible("'.$label.'_'.($i>=10?'{':'').$i.($i>=10?'}':'').'",false);'."\n";
575: }
576:
577: return $output;
578: }
579:
580: #
581: # Subroutine that generates code for spline $label based on stored information
582: #
583:
584: sub generate_spline {
585: my ($id,$label,$xmin,$xmax,$ymin,$ymax,$fixed)=@_;
586: my $appid=&appid($id);
587: my $result='';
588: my $order=$Apache::functionplotresponse::splineorder{$label};
589: my $x=$Apache::functionplotresponse::splineinitx{$label};
590: my $y=$Apache::functionplotresponse::splineinity{$label};
591: my $sx=$Apache::functionplotresponse::splinescalex{$label};
592: my $sy=$Apache::functionplotresponse::splinescaley{$label};
593: my @coords=();
594: foreach my $i (1..$order) {
595: $result.=&new_point_coordinate($id,$label.'P'.$i,$x,$y,$fixed);
596: my $xp=$x;
597: $x+=$sx/(2.*($order-1));
598: push(@coords,$label.'P'.$i);
599: $result.=&new_slope_coordinate($id,$label.'S'.$i,$x,$y+$sy,$label.'P'.$i,$xp,$y,$xmin,$xmax,$ymin,$ymax,$fixed);
600: $x+=$sx/(2.*($order-1));
601: push(@coords,$label.'S'.$i);
602: }
603: $result.='document.ggbApplet'.$appid.'.evalCommand("Spline'.$order.'['.join(',',@coords).']");'."\n";
604: return $result;
605: }
606:
607: #
608: # Object
609: #
610:
611: sub start_plotobject {
612: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
613: my $result='';
614: my $internalid = $Apache::inputtags::part.'_'.$Apache::inputtags::response[-1];
615: my $x=&Apache::lonxml::get_param('x',$parstack,$safeeval);
616: my $y=&Apache::lonxml::get_param('y',$parstack,$safeeval);
617: my $label=&Apache::lonxml::get_param('label',$parstack,$safeeval);
618: $label=~s/\W//gs;
619: $label=ucfirst($label);
620: unless ($label) { $label="NewObject"; }
621: if ($target eq 'web') {
622: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-3);
623: unless (defined($x)) { $x=$xmin; }
624: unless (defined($y)) { $y=$ymin; }
625: $result.=&plotobject_script($internalid,$label,$x,$y);
626: } elsif ($target eq 'edit') {
627: $result=&Apache::edit::tag_start($target,$token,'Plot Object').
628: &Apache::edit::text_arg('Label on Plot:','label',
629: $token,'16').
630: &Apache::edit::text_arg('x:','x',
631: $token,'8').
632: &Apache::edit::text_arg('y:','y',
633: $token,'8').
634: &Apache::edit::end_row();
635: } elsif ($target eq 'modified') {
636: $env{'form.'.&Apache::edit::html_element_name('label')}=ucfirst($env{'form.'.&Apache::edit::html_element_name('label')});
637: my $constructtag=&Apache::edit::get_new_args($token,$parstack,$safeeval,'label','x','y');
638: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
639: }
640: return $result;
641: }
642:
643: sub end_plotobject {
644: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
645: my $result='';
646: if ($target eq 'edit') {
647: $result=&Apache::edit::end_table();
648: }
649: return $result;
650: }
651:
652: #
653: # Vector
654: #
655:
656: sub start_plotvector {
657: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
658: my $result='';
659: my $internalid = $Apache::inputtags::part.'_'.$Apache::inputtags::response[-1];
660: my $tailx=&Apache::lonxml::get_param('tailx',$parstack,$safeeval);
661: my $taily=&Apache::lonxml::get_param('taily',$parstack,$safeeval);
662: my $tipx=&Apache::lonxml::get_param('tipx',$parstack,$safeeval);
663: my $tipy=&Apache::lonxml::get_param('tipy',$parstack,$safeeval);
664:
665: my $label=&Apache::lonxml::get_param('label',$parstack,$safeeval);
666: $label=~s/\W//gs;
667: $label=ucfirst($label);
668: unless ($label) { $label="NewVector"; }
669: if ($Apache::functionplotresponse::vectorlabels{$label}) {
670: &Apache::lonxml::warning(&mt('Vector labels must be unique: [_1]',$label));
671: }
672: $Apache::functionplotresponse::vectorlabels{$label}=1;
673: if ($target eq 'web') {
674: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-3);
675: unless (defined($tailx)) { $tailx=$xmin; }
676: unless (defined($taily)) { $taily=$ymin; }
677: unless (defined($tipx)) { $tipx=$xmin; }
678: unless (defined($tipy)) { $tipy=$ymin; }
679: my $fixed=0;
680: if ((&Apache::response::show_answer()) || (&Apache::response::check_status()>=2)) { $fixed=1; }
681: $result.=&plotvector_script($internalid,$label,$tailx,$taily,$tipx,$tipy,$xmin,$xmax,$fixed);
682: } elsif ($target eq 'edit') {
683: $result=&Apache::edit::tag_start($target,$token,'Plot Vector').
684: &Apache::edit::text_arg('Label on Plot:','label',
685: $token,'16').
686: &Apache::edit::text_arg('Tail x:','tailx',
687: $token,'8').
688: &Apache::edit::text_arg('Tail y:','taily',
689: $token,'8').
690: &Apache::edit::text_arg('Tip x:','tipx',
691: $token,'8').
692: &Apache::edit::text_arg('Tip y:','tipy',
693: $token,'8').
694:
695: &Apache::edit::end_row();
696: } elsif ($target eq 'modified') {
697: $env{'form.'.&Apache::edit::html_element_name('label')}=ucfirst($env{'form.'.&Apache::edit::html_element_name('label')});
698: my $constructtag=&Apache::edit::get_new_args($token,$parstack,$safeeval,'label','tailx','taily','tipx','tipy');
699: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
700: }
701: return $result;
702: }
703:
704: sub end_plotvector {
705: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
706: my $result='';
707: if ($target eq 'edit') {
708: $result=&Apache::edit::end_table();
709: }
710: return $result;
711: }
712:
713:
714: #
715: # Vector sum - have GeoGebra draw a sum of specified vectors to help students draw
716: #
717:
718: sub start_drawvectorsum {
719: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
720: my $result='';
721: my $internalid = $Apache::inputtags::part.'_'.$Apache::inputtags::response[-1];
722: my $internalappid=&appid($internalid);
723: my $tailx=&Apache::lonxml::get_param('tailx',$parstack,$safeeval);
724: my $taily=&Apache::lonxml::get_param('taily',$parstack,$safeeval);
725: my $showvalue=&Apache::lonxml::get_param('showvalue',$parstack,$safeeval);
726: my $vectorlist=&Apache::lonxml::get_param('vectorlist',$parstack,$safeeval);
727: my $label=&Apache::lonxml::get_param('label',$parstack,$safeeval);
728: $label=~s/\W//gs;
729: $label=ucfirst($label);
730: unless ($label) { $label="NewVector"; }
731: if ($target eq 'web') {
732: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-3);
733: unless (defined($tailx)) { $tailx=$xmin; }
734: unless (defined($taily)) { $taily=$ymin; }
735: unless (defined($vectorlist)) { $vectorlist=''; }
736: my @vectors=split(/\,/,$vectorlist);
737: if ($#vectors>0) {
738: my @sumx=();
739: my @sumy=();
740: foreach my $thisvector (@vectors) {
741: $thisvector=~s/\W//gs;
742: $thisvector=ucfirst($thisvector);
743: unless ($thisvector) { next; }
744: unless ($Apache::functionplotresponse::vectorlabels{$thisvector}) {
745: &Apache::lonxml::warning(&mt('Vectors must be defined before using them for drawing vector sums: [_1]',$thisvector));
746: next;
747: }
748: my $vectorx=$thisvector.'X';
749: my $vectory=$thisvector.'Y';
750: $result.=(<<ENDADDVEC);
751: document.ggbApplet$internalappid.evalCommand("$vectorx=x($thisvector)");
752: document.ggbApplet$internalappid.evalCommand("$vectory=y($thisvector)");
753: document.ggbApplet$internalappid.evalCommand("Include$thisvector$label=Checkbox[]");
754: ENDADDVEC
755: push(@sumx,"If[Include$thisvector$label,$vectorx,0]");
756: push(@sumy,"If[Include$thisvector$label,$vectory,0]");
757: }
758: $result.="document.ggbApplet$internalappid.evalCommand(".'"'."xTot$label=".join('+',@sumx).'");'."\n";
759: $result.="document.ggbApplet$internalappid.evalCommand(".'"'."yTot$label=".join('+',@sumy).'");'."\n";
760: my $show=0;
761: if ($showvalue=~/yes/i) {
762: $show=1;
763: }
764: $result.=(<<ENDMAKEVECTOR);
765: document.ggbApplet$internalappid.evalCommand("$label=Vector[($tailx,$taily),($tailx+xTot$label,$taily+yTot$label)]");
766: document.ggbApplet$internalappid.setLabelVisible("$label",true);
767: document.ggbApplet$internalappid.setLineThickness("$label",8);
768: document.ggbApplet$internalappid.setColor("$label",255,0,0);
769: document.ggbApplet$internalappid.setLabelStyle("$label",VALUE=$show);
770: ENDMAKEVECTOR
771: }
772: } elsif ($target eq 'edit') {
773: $result=&Apache::edit::tag_start($target,$token,'Draw Vector Sum').
774: &Apache::edit::text_arg('Label on Plot:','label',
775: $token,'16').
776: &Apache::edit::text_arg('Tail x:','tailx',
777: $token,'8').
778: &Apache::edit::text_arg('Tail y:','taily',
779: $token,'8').
780: &Apache::edit::select_arg('Show Value:','showvalue',
781: ['yes','no'],$token).'<br />'.
782: &Apache::edit::text_arg('Vector List:','vectorlist',
783: $token,'40').
784: &Apache::edit::end_row();
785: } elsif ($target eq 'modified') {
786: $env{'form.'.&Apache::edit::html_element_name('label')}=ucfirst($env{'form.'.&Apache::edit::html_element_name('label')});
787: my $constructtag=&Apache::edit::get_new_args($token,$parstack,$safeeval,'label','tailx','taily','showvalue','vectorlist');
788: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
789: }
790: return $result;
791: }
792:
793:
794: sub end_drawvectorsum {
795: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
796: my $result='';
797: if ($target eq 'edit') {
798: $result=&Apache::edit::end_table();
799: }
800: return $result;
801: }
802:
803:
804:
805: #
806: # <backgroundplot function="..." fixed="yes/no" />
807: #
808: sub start_backgroundplot {
809: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
810: my $result='';
811: my $internalid = $Apache::inputtags::part.'_'.$Apache::inputtags::response[-1];
812: my $function=&Apache::lonxml::get_param('function',$parstack,$safeeval);
813: my $xinitial=&Apache::lonxml::get_param('xinitial',$parstack,$safeeval);
814: my $xfinal=&Apache::lonxml::get_param('xfinal',$parstack,$safeeval);
815: my $label=&Apache::lonxml::get_param('label',$parstack,$safeeval);
816: my $color=&Apache::lonxml::get_param('color',$parstack,$safeeval);
817: $color=~s/[^a-fA-F0-9]//gs;
818: unless (length($color)==6) { $color=''; }
819: my $fixed=(&Apache::lonxml::get_param('fixed',$parstack,$safeeval)=~/on|true|yes|1/i?1:0);
820:
821: unless ($function) { $function="0"; }
822: if ($target eq 'web') {
823: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-3);
824: unless (defined($xinitial)) { $xinitial=$xmin; }
825: unless (defined($xfinal)) { $xfinal=$xmax; }
826: $result.=&plot_script($internalid,$function,$fixed,$label,$color,$xinitial,$xfinal);
827: } elsif ($target eq 'edit') {
828: $result=&Apache::edit::tag_start($target,$token,'Background Function Plot').
829: &Apache::edit::text_arg('Function:','function',
830: $token,'16').
831: &Apache::edit::text_arg('Initial x-value (optional):','xinitial',
832: $token,'8').
833: &Apache::edit::text_arg('Final x-value (optional):','xfinal',
834: $token,'8').
835: &Apache::edit::text_arg('Label on Plot:','label',
836: $token,'8').
837: &Apache::edit::text_arg('Color (hex code):','color',
838: $token,'8', 'colorchooser').
839: &Apache::edit::select_arg('Fixed location:','fixed',
840: ['yes','no'],$token).
841: &Apache::edit::end_row();
842: } elsif ($target eq 'modified') {
843: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
844: $safeeval,'function','label','xinitial','xfinal','color','fixed');
845: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
846: }
847: return $result;
848: }
849:
850: sub end_backgroundplot {
851: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
852: my $result='';
853: if ($target eq 'edit') {
854: $result=&Apache::edit::end_table();
855: }
856: return $result;
857: }
858:
859: #
860: # <functionplotrule ... />
861: #
862: sub start_functionplotrule {
863: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
864: my $result='';
865: my $label=&Apache::lonxml::get_param('index',$parstack,$safeeval);
866: $Apache::functionplotresponse::counter++;
867: if ($label=~/\W/) {
868: &Apache::lonxml::warning(&mt('Rule indices should only contain alphanumeric characters.'));
869: }
870: $label=~s/\W//gs;
871: unless ($label) {
872: $label='R'.$Apache::functionplotresponse::counter;
873: } else {
874: $label='R'.$label;
875: }
876:
877: if ($target eq 'grade') {
878: # Simply remember - in order - for later
879: my $beginninglabel=&Apache::lonxml::get_param('xinitiallabel',$parstack,$safeeval);
880: my $endinglabel=&Apache::lonxml::get_param('xfinallabel',$parstack,$safeeval);
881: if (($beginninglabel=~/\W/) || ($endinglabel=~/W/)) {
882: &Apache::lonxml::warning(&mt('Rule labels must be alphanumeric.'));
883: }
884: $beginninglabel=~s/\W//gs;
885: $endinglabel=~s/\W//gs;
886: my $relationship=&Apache::lonxml::get_param('relationship',$parstack,$safeeval);
887: $relationship=~s/\W//gs;
888: $relationship=lc($relationship);
889: unless ($relationship=~/^(eq|ge|gt|le|lt|ne)$/) {
890: &Apache::lonxml::warning(&mt('Rule relationship not defined.'));
891: $relationship='eq';
892: }
893: my $derivative=&Apache::lonxml::get_param('derivativeorder',$parstack,$safeeval);
894: unless (($derivative==-1) || ($derivative==0) || ($derivative==1) || ($derivative==2)) {
895: &Apache::lonxml::warning(&mt('Rule derivative not defined.'));
896: $derivative=0;
897: }
898: push(@Apache::functionplotresponse::functionplotrules,join(':',(
899: $label,
900: $derivative,
901: &Apache::lonxml::get_param('xinitial',$parstack,$safeeval),
902: $beginninglabel,
903: &Apache::lonxml::get_param('xfinal',$parstack,$safeeval),
904: $endinglabel,
905: &Apache::lonxml::get_param('minimumlength',$parstack,$safeeval),
906: &Apache::lonxml::get_param('maximumlength',$parstack,$safeeval),
907: $relationship,
908: &Apache::lonxml::get_param('value',$parstack,$safeeval),
909: &Apache::lonxml::get_param('percenterror',$parstack,$safeeval)
910: )));
911: } elsif ($target eq 'edit') {
912: $result=&Apache::edit::tag_start($target,$token,'Function Plot Graph Rule').
913: &Apache::edit::text_arg('Index/Name:','index',
914: $token,'10').' '.
915: &Apache::edit::select_arg('Function:','derivativeorder',
916: [['0','Function itself'],
917: ['1','First derivative'],
918: ['2','Second derivative'],
919: ['-1','Integral']],$token).'<br />'.
920: &Apache::edit::text_arg('Initial x-value:','xinitial',
921: $token,'8').
922: &Apache::edit::select_or_text_arg('Initial x-value label:','xinitiallabel',
923: [['start','Start of Plot'],
924: ['end','End of Plot']],$token,'8').'<br />'.
925:
926: &Apache::edit::text_arg('Final x-value (optional):','xfinal',
927: $token,'8').
928: &Apache::edit::select_or_text_arg('Final x-value label (optional):','xfinallabel',
929: [['end','End of Plot']],$token,'8').'<br />'.
930: &Apache::edit::text_arg('Minimum length for range (optional):','minimumlength',
931: $token,'8').
932: &Apache::edit::text_arg('Maximum length for range (optional):','maximumlength',
933: $token,'8').'<br />'.
934: &Apache::edit::select_or_text_arg(&mt('Relationship:'),'relationship',
935: [['eq','equal'],
936: ['ne','not equal'],
937: ['ge','greater than or equal'],
938: ['gt','greater than'],
939: ['lt','less than'],
940: ['le','less than or equal']],$token).
941: $result.= &Apache::edit::select_or_text_arg('Value:','value',
942: [['undef','not defined']],$token,'30').
943: &Apache::edit::text_arg('Percent error:','percenterror',
944: $token,'8').
945: &Apache::edit::end_row();
946: } elsif ($target eq 'modified') {
947: if (($env{'form.'.&Apache::edit::html_element_name('xinitial')} ne '') && ($env{'form.'.&Apache::edit::html_element_name('xinitiallabel')} eq 'start')) {
948: $env{'form.'.&Apache::edit::html_element_name('xinitiallabel')}='';
949: }
950: if (($env{'form.'.&Apache::edit::html_element_name('xfinal')} ne '') && ($env{'form.'.&Apache::edit::html_element_name('xfinallabel')} eq 'end')) {
951: $env{'form.'.&Apache::edit::html_element_name('xfinallabel')}='';
952: }
953: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
954: $safeeval,'index','derivativeorder',
955: 'xinitial','xinitiallabel','xfinal','xfinallabel',
956: 'minimumlength','maximumlength',
957: 'relationship','value','percenterror');
958: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
959: }
960: return $result;
961: }
962:
963: sub end_functionplotrule {
964: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
965: my $result='';
966: if ($target eq 'edit') {
967: $result=&Apache::edit::end_table();
968: }
969: return $result;
970: }
971:
972:
973: #
974: # <functionplotvectorrule ... />
975: #
976: sub start_functionplotvectorrule {
977: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
978: my $result='';
979: my $label=&Apache::lonxml::get_param('index',$parstack,$safeeval);
980: $Apache::functionplotresponse::counter++;
981: if ($label=~/\W/) {
982: &Apache::lonxml::warning(&mt('Rule indices should only contain alphanumeric characters.'));
983: }
984: $label=~s/\W//gs;
985: unless ($label) {
986: $label='R'.$Apache::functionplotresponse::counter;
987: } else {
988: $label='R'.$label;
989: }
990:
991: if ($target eq 'grade') {
992: # Simply remember - in order - for later
993:
994: my $id=$Apache::inputtags::response[-1];
995: my $partid=$Apache::inputtags::part;
996: my $internalid = $partid.'_'.$id;
997:
998: my $vector=&Apache::lonxml::get_param('vector',$parstack,$safeeval);
999: $vector=~s/\W//gs;
1000: $vector=ucfirst($vector);
1001:
1002: push(@Apache::functionplotresponse::functionplotvectorrules,join(':',(
1003: $label,
1004: 'vector',
1005: $internalid,
1006: $vector,
1007: &Apache::lonxml::get_param('attachpoint',$parstack,$safeeval),
1008: &Apache::lonxml::get_param('notattachpoint',$parstack,$safeeval),
1009: &Apache::lonxml::get_param('tailpoint',$parstack,$safeeval),
1010: &Apache::lonxml::get_param('tippoint',$parstack,$safeeval),
1011: &Apache::lonxml::get_param('nottailpoint',$parstack,$safeeval),
1012: &Apache::lonxml::get_param('nottippoint',$parstack,$safeeval),
1013: &Apache::lonxml::get_param('length',$parstack,$safeeval),
1014: &Apache::lonxml::get_param('angle',$parstack,$safeeval),
1015: &Apache::lonxml::get_param('lengtherror',$parstack,$safeeval),
1016: &Apache::lonxml::get_param('angleerror',$parstack,$safeeval),
1017: )));
1018: } elsif ($target eq 'edit') {
1019: $result=&Apache::edit::tag_start($target,$token,'Function Plot Vector Rule').
1020: &Apache::edit::text_arg('Index/Name:','index',
1021: $token,'10').' '.
1022: &Apache::edit::text_arg('Vector:','vector',
1023: $token,'16').'<br />'.
1024: &Apache::edit::text_arg('Attached to object:','attachpoint',
1025: $token,'16').
1026: &Apache::edit::text_arg('Not attached to object:','notattachpoint',
1027: $token,'16').'<br />'.
1028: &Apache::edit::text_arg('Tail attached to object:','tailpoint',
1029: $token,'16').
1030: &Apache::edit::text_arg('Tip attached to object:','tippoint',
1031: $token,'16').
1032: &Apache::edit::text_arg('Tail not attached to object:','nottailpoint',
1033: $token,'16').
1034: &Apache::edit::text_arg('Tip not attached to object:','nottippoint',
1035: $token,'16').'<br />'.
1036: &Apache::edit::text_arg('Length:','length',
1037: $token,'30').
1038: &Apache::edit::text_arg('Absolute error length:','lengtherror',
1039: $token,'8').'<br />'.
1040: &Apache::edit::text_arg('Angle:','angle',
1041: $token,'30').
1042: &Apache::edit::text_arg('Absolute error angle:','angleerror',
1043: $token,'8').
1044: &Apache::edit::end_row();
1045: } elsif ($target eq 'modified') {
1046: $env{'form.'.&Apache::edit::html_element_name('vector')}=ucfirst($env{'form.'.&Apache::edit::html_element_name('vector')});
1047: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
1048: $safeeval,'index','vector','attachpoint','notattachpoint',
1049: 'tailpoint','tippoint','nottailpoint','nottipoint',
1050: 'length','angle',
1051: 'lengtherror','angleerror');
1052: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
1053: }
1054: return $result;
1055: }
1056:
1057: sub end_functionplotvectorrule {
1058: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1059: my $result='';
1060: if ($target eq 'edit') {
1061: $result=&Apache::edit::end_table();
1062: }
1063: return $result;
1064: }
1065:
1066: #
1067: # <functionplotvectorsumrule ... />
1068: #
1069: sub start_functionplotvectorsumrule {
1070: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1071: my $result='';
1072: my $label=&Apache::lonxml::get_param('index',$parstack,$safeeval);
1073: $Apache::functionplotresponse::counter++;
1074: if ($label=~/\W/) {
1075: &Apache::lonxml::warning(&mt('Rule indices should only contain alphanumeric characters.'));
1076: }
1077: $label=~s/\W//gs;
1078: unless ($label) {
1079: $label='R'.$Apache::functionplotresponse::counter;
1080: } else {
1081: $label='R'.$label;
1082: }
1083: if ($target eq 'grade') {
1084: # Simply remember - in order - for later
1085: my $id=$Apache::inputtags::response[-1];
1086: my $partid=$Apache::inputtags::part;
1087: my $internalid = $partid.'_'.$id;
1088: my $vectors=&Apache::lonxml::get_param('vectors',$parstack,$safeeval);
1089: push(@Apache::functionplotresponse::functionplotvectorrules,join(':',(
1090: $label,
1091: 'sum',
1092: $internalid,
1093: $vectors,
1094: &Apache::lonxml::get_param('length',$parstack,$safeeval),
1095: &Apache::lonxml::get_param('angle',$parstack,$safeeval),
1096: &Apache::lonxml::get_param('lengtherror',$parstack,$safeeval),
1097: &Apache::lonxml::get_param('angleerror',$parstack,$safeeval),
1098: )));
1099: } elsif ($target eq 'edit') {
1100: $result=&Apache::edit::tag_start($target,$token,'Function Plot Vector Sum Rule').
1101: &Apache::edit::text_arg('Index/Name:','index',
1102: $token,'10').' '.
1103: &Apache::edit::text_arg('Comma-separated list of vectors:','vectors',
1104: $token,'30').'<br />'.
1105: &Apache::edit::text_arg('Sum vector length:','length',
1106: $token,'30').
1107: &Apache::edit::text_arg('Absolute error length:','lengtherror',
1108: $token,'8').'<br />'.
1109: &Apache::edit::text_arg('Sum vector angle:','angle',
1110: $token,'30').
1111: &Apache::edit::text_arg('Absolute error angle:','angleerror',
1112: $token,'8').
1113: &Apache::edit::end_row();
1114: } elsif ($target eq 'modified') {
1115: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
1116: $safeeval,'index','vectors',
1117: 'length','angle',
1118: 'lengtherror','angleerror');
1119: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
1120: }
1121: return $result;
1122: }
1123:
1124: sub end_functionplotvectorsumrule {
1125: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1126: my $result='';
1127: if ($target eq 'edit') {
1128: $result=&Apache::edit::end_table();
1129: }
1130: return $result;
1131: }
1132:
1133: #
1134: # <functionplotcustom ... />
1135: #
1136: sub start_functionplotcustomrule {
1137: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1138: my $result='';
1139: my $label=&Apache::lonxml::get_param('index',$parstack,$safeeval);
1140: $Apache::functionplotresponse::counter++;
1141: if ($label=~/\W/) {
1142: &Apache::lonxml::warning(&mt('Rule indices should only contain alphanumeric characters.'));
1143: }
1144: $label=~s/\W//gs;
1145: unless ($label) {
1146: $label='R'.$Apache::functionplotresponse::counter;
1147: } else {
1148: $label='R'.$label;
1149: }
1150: &Apache::lonxml::register('Apache::response',('answer'));
1151: if ($target eq 'edit') {
1152: $result=&Apache::edit::tag_start($target,$token,'Function Plot Custom Rule').
1153: &Apache::edit::text_arg('Index/Name:','index',$token,'10').
1154: &Apache::edit::end_row();
1155: } elsif ($target eq 'modified') {
1156: my $constructtag=&Apache::edit::get_new_args($token,$parstack,$safeeval,'index');
1157: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
1158: }
1159: return $result;
1160: }
1161:
1162: sub end_functionplotcustomrule {
1163: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1164: my $result='';
1165: if ($target eq 'edit') {
1166: $result=&Apache::edit::end_table();
1167: } elsif ($target eq 'grade') {
1168: # Simply remember - in order - for later
1169: my $label=&Apache::lonxml::get_param('index',$parstack,$safeeval);
1170: $Apache::functionplotresponse::counter++;
1171: if ($label=~/\W/) {
1172: &Apache::lonxml::warning(&mt('Rule indices should only contain alphanumeric characters.'));
1173: }
1174: $label=~s/\W//gs;
1175: unless ($label) {
1176: $label='R'.$Apache::functionplotresponse::counter;
1177: } else {
1178: $label='R'.$label;
1179: }
1180: push(@Apache::functionplotresponse::functionplotvectorrules,join(':',(
1181: $label,
1182: 'custom',
1183: &escape($Apache::response::custom_answer[-1])
1184: )));
1185: }
1186: &Apache::lonxml::deregister('Apache::response',('answer'));
1187: return $result;
1188: }
1189:
1190:
1191:
1192: #
1193: # <spline index="..." order="1,2,3,4" initx="..." inity="..." scalex="..." scaley="..." />
1194: #
1195: # Unfortunately, GeoGebra seems to want all splines after everything else, so we need to store them
1196: #
1197: sub start_spline {
1198: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1199: my $result='';
1200: if ($target eq 'web') {
1201: my $label=&Apache::lonxml::get_param('index',$parstack,$safeeval);
1202: $Apache::functionplotresponse::counter++;
1203: if ($label=~/\W/) {
1204: &Apache::lonxml::warning(&mt('Spline indices should only contain alphanumeric characters.'));
1205: }
1206: $label=~s/\W//gs;
1207: unless ($label) {
1208: $label='S'.$Apache::functionplotresponse::counter;
1209: } else {
1210: $label='S'.$label;
1211: }
1212: if ($Apache::functionplotresponse::splineorder{$label}) {
1213: &Apache::lonxml::error(&mt('Spline indices must be unique.'));
1214: }
1215:
1216: my $order=&Apache::lonxml::get_param('order',$parstack,$safeeval);
1217: if ($order<2) { $order=2; }
1218: if ($order>8) { $order=8; }
1219: $Apache::functionplotresponse::splineorder{$label}=$order;
1220:
1221: my $x=&Apache::lonxml::get_param('initx',$parstack,$safeeval);
1222: unless ($x) { $x=0; }
1223: $Apache::functionplotresponse::splineinitx{$label}=$x;
1224:
1225: my $y=&Apache::lonxml::get_param('inity',$parstack,$safeeval);
1226: unless ($y) { $y=0; }
1227: $Apache::functionplotresponse::splineinity{$label}=$y;
1228:
1229: my $sx=&Apache::lonxml::get_param('scalex',$parstack,$safeeval);
1230: unless ($sx) { $sx=$order; }
1231: $Apache::functionplotresponse::splinescalex{$label}=$sx;
1232:
1233: my $sy=&Apache::lonxml::get_param('scaley',$parstack,$safeeval);
1234: unless ($sy) { $sy=2; }
1235: $Apache::functionplotresponse::splinescaley{$label}=$sy;
1236: } elsif ($target eq 'edit') {
1237: $result=&Apache::edit::tag_start($target,$token,'Spline').
1238: &Apache::edit::text_arg('Index:','index',
1239: $token,'4').' '.
1240: &Apache::edit::select_arg('Order:','order',
1241: ['2','3','4','5','6','7','8'],$token).' '.
1242: &Apache::edit::text_arg('Initial x-value:','initx',
1243: $token,'4').' '.
1244: &Apache::edit::text_arg('Initial y-value:','inity',
1245: $token,'4').' '.
1246: &Apache::edit::text_arg('Scale x:','scalex',
1247: $token,'4').' '.
1248: &Apache::edit::text_arg('Scale y:','scaley',
1249: $token,'4').
1250: &Apache::edit::end_row();
1251: } elsif ($target eq 'modified') {
1252: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
1253: $safeeval,'index','order','initx','inity',
1254: 'scalex','scaley');
1255: if ($constructtag) { $result=&Apache::edit::rebuild_tag($token); }
1256: }
1257: return $result;
1258: }
1259:
1260: sub end_spline {
1261: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1262: my $result='';
1263: if ($target eq 'edit') {
1264: $result=&Apache::edit::end_table();
1265: }
1266: return $result;
1267: }
1268:
1269: sub end_init_script {
1270: return (<<ENDENDINIT);
1271: }
1272: // ]]>
1273: </script>
1274: ENDENDINIT
1275: }
1276:
1277: #
1278: # Storing and restoring spline coordinates from part answers
1279: #
1280: sub decode_previous_answer {
1281: my ($answer)=@_;
1282: foreach my $coordinate (split(/\,/,$answer)) {
1283: my ($key,$value)=split(/\=/,$coordinate);
1284: $Apache::functionplotresponse::previous{$key}=$value;
1285: }
1286: }
1287:
1288: sub get_answer_from_form_fields {
1289: my ($id)=@_;
1290: my $answer='';
1291: my %coords=();
1292: foreach my $field (keys(%env)) {
1293: if ($field=~/^form\.HWVAL\_$id/) {
1294: $field=~/^form\.(.*)$/;
1295: $coords{$1}=$env{$field};
1296: }
1297: }
1298: $answer=join(',',map { $_.'='.$coords{$_} } (sort(keys(%coords))));
1299: return ($answer,%coords);
1300: }
1301:
1302: #
1303: # The following functions calculate the cubic-hermite splines server-side
1304: #
1305:
1306: sub cubic_hermite {
1307: my ($t,$p1,$s1,$p2,$s2)=@_;
1308: return (2.*$t*$t*$t-3.*$t*$t+1.)*$p1 + 3.*($t*$t*$t-2.*$t*$t+$t)*($s1-$p1)+
1309: (-2.*$t*$t*$t+3.*$t*$t) *$p2 + 3.*($t*$t*$t-$t*$t) *($s2-$p2);
1310: }
1311:
1312: #
1313: # d/dt(...)
1314: #
1315:
1316: sub ddt_cubic_hermite {
1317: my ($t,$p1,$s1,$p2,$s2)=@_;
1318: return (6.*$t*$t-6.*$t) *$p1 + 3.*(3.*$t*$t-4.*$t+1.)*($s1-$p1)+
1319: (-6.*$t*$t+6.*$t)*$p2 + 3.*(3.*$t*$t-2.*$t) *($s2-$p2);
1320: }
1321:
1322: #
1323: # d^2/dt^2(...)
1324: #
1325:
1326: sub d2dt2_cubic_hermite {
1327: my ($t,$p1,$s1,$p2,$s2)=@_;
1328: return (12.*$t-6.) *$p1 + 3.*(6.*$t-4.)*($s1-$p1)+
1329: (-12.*$t+6.)*$p2 + 3.*(6.*$t-2.)*($s2-$p2);
1330: }
1331:
1332: #
1333: # Array index calculation
1334: #
1335: sub array_index {
1336: my ($xmin,$xmax,$x)=@_;
1337: if ($x ne '') {
1338: return int(($x-$xmin)/($xmax-$xmin)*400.+0.5);
1339: } else {
1340: return undef;
1341: }
1342: }
1343:
1344: #
1345: # Populate the arrays
1346: #
1347:
1348: sub populate_arrays {
1349: my ($id,$xmin,$xmax,$ymin,$ymax)=@_;
1350: for (my $i=0; $i<=400; $i++) {
1351: $Apache::functionplotresponse::actualxval[$i]=undef;
1352: $Apache::functionplotresponse::func[$i]=undef;
1353: $Apache::functionplotresponse::dfuncdx[$i]=undef;
1354: $Apache::functionplotresponse::d2funcd2x[$i]=undef;
1355: }
1356: unless ($xmax>$xmin) { return 'no_func'; }
1357: # Run over all splines in response
1358: foreach my $label (split(/\,/,$env{"form.HWVAL_AllSplines_$id"})) {
1359: my $xiold=-1;
1360: # Run over all points in spline
1361: for (my $i=1; $i<$env{"form.HWVAL_SplineOrder_".$id."_".$label}; $i++) {
1362: my $ni=$i+1;
1363: my @xparms=($env{'form.HWVAL_'.$id.'_'.$label.'P'.$i.'_x'},
1364: $env{'form.HWVAL_'.$id.'_'.$label.'S'.$i.'_x'},
1365: $env{'form.HWVAL_'.$id.'_'.$label.'P'.$ni.'_x'},
1366: $env{'form.HWVAL_'.$id.'_'.$label.'S'.$ni.'_x'});
1367: my @yparms=($env{'form.HWVAL_'.$id.'_'.$label.'P'.$i.'_y'},
1368: $env{'form.HWVAL_'.$id.'_'.$label.'S'.$i.'_y'},
1369: $env{'form.HWVAL_'.$id.'_'.$label.'P'.$ni.'_y'},
1370: $env{'form.HWVAL_'.$id.'_'.$label.'S'.$ni.'_y'});
1371: # Run in small steps over spline parameter
1372: for (my $t=0; $t<=1; $t+=0.0001) {
1373: my $xreal=&cubic_hermite($t,@xparms);
1374: my $xi=&array_index($xmin,$xmax,$xreal);
1375: if ($xi<$xiold) { return 'no_func'; }
1376: if (($xi>$xiold) && ($xi>=0) && ($xi<=400)) {
1377: $xiold=$xi;
1378: $Apache::functionplotresponse::actualxval[$xi]=$xreal;
1379: # Function value
1380: my $funcval=&cubic_hermite($t,@yparms);
1381:
1382: # Do we already have a value for this point, and is it different from the new one?
1383: if ((defined($Apache::functionplotresponse::func[$xi])) &&
1384: (abs($Apache::functionplotresponse::func[$xi]-$funcval)>($ymax-$ymin)/100.)) {
1385: return 'no_func';
1386: }
1387: # Okay, remember the new point
1388: $Apache::functionplotresponse::func[$xi]=$funcval;
1389:
1390: if (defined($funcval)) {
1391: if ($xi<$Apache::functionplotresponse::functionplotrulelabels{'start'}) {
1392: $Apache::functionplotresponse::functionplotrulelabels{'start'}=$xi;
1393: }
1394: if ($xi>$Apache::functionplotresponse::functionplotrulelabels{'end'}) {
1395: $Apache::functionplotresponse::functionplotrulelabels{'end'}=$xi;
1396: }
1397: }
1398: # Chain rule
1399: # dy/dx=dy/dt/(dx/dt)
1400: my $dxdt=&ddt_cubic_hermite($t,@xparms);
1401: if ($dxdt) {
1402: $Apache::functionplotresponse::dfuncdx[$xi]=&ddt_cubic_hermite($t,@yparms)/$dxdt;
1403: # Second derivative
1404: $Apache::functionplotresponse::d2funcdx2[$xi]=
1405: ($dxdt*&d2dt2_cubic_hermite($t,@yparms)-&ddt_cubic_hermite($t,@yparms)*&d2dt2_cubic_hermite($t,@xparms))/
1406: ($dxdt*$dxdt*$dxdt);
1407: }
1408: }
1409: }
1410: }
1411: }
1412: }
1413:
1414: #
1415: # Implementation of <functionplotresponse>
1416: #
1417:
1418: sub start_functionplotresponse {
1419: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1420: my $result='';
1421: # To remember the splines - somehow, they need to come last
1422: undef %Apache::functionplotresponse::splineorder;
1423: undef %Apache::functionplotresponse::splineinitx;
1424: undef %Apache::functionplotresponse::splineinity;
1425: undef %Apache::functionplotresponse::splinescalex;
1426: undef %Apache::functionplotresponse::splinescaley;
1427: # Remember input fields, etc
1428: undef %Apache::functionplotresponse::previous;
1429: $Apache::functionplotresponse::inputfields='';
1430: $Apache::functionplotresponse::counter=0;
1431: # Remember vectors
1432: undef %Apache::functionplotresponse::vectorlabels;
1433: # Remember rules
1434: undef @Apache::functionplotresponse::functionplotrules;
1435: undef @Apache::functionplotresponse::functionplotvectorrules;
1436: # Remember failed rules
1437: if ($target eq 'grade') {
1438: undef @Apache::functionplotresponse::failedrules;
1439: }
1440: # Delete previous awards
1441: undef $Apache::functionplotresponse::awarddetail;
1442: # Part and ID
1443: my $partid=$Apache::inputtags::part;
1444: my $id=&Apache::response::start_response($parstack,$safeeval);
1445: # Internal ID to mark the applet and its coordinates
1446: my $internalid = $partid.'_'.$id;
1447: # Previous answer
1448: &decode_previous_answer($Apache::lonhomework::history{"resource.$partid.$id.submission"});
1449:
1450: # Parameters of <functionplotresponse>
1451: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval);
1452: my $xaxisvisible=(&Apache::lonxml::get_param('xaxisvisible',$parstack,$safeeval)=~/on|true|yes|1/i?'true':'false');
1453: my $yaxisvisible=(&Apache::lonxml::get_param('yaxisvisible',$parstack,$safeeval)=~/on|true|yes|1/i?'true':'false');
1454: my $gridvisible=(&Apache::lonxml::get_param('gridvisible',$parstack,$safeeval)=~/on|true|yes|1/i?'true':'false');
1455: my $xlabel=&Apache::lonxml::get_param('xlabel',$parstack,$safeeval);
1456: my $ylabel=&Apache::lonxml::get_param('ylabel',$parstack,$safeeval);
1457: if ($target eq 'edit') {
1458: $result.=&Apache::edit::start_table($token)
1459: .'<tr><td><span class="LC_nobreak">'.&Apache::loncommon::insert_folding_button().&mt('Function Plot Question').'</span></td>'
1460: .'<td><span class="LC_nobreak">'.&mt('Delete?').' '
1461: .&Apache::edit::deletelist($target,$token).' '
1462: .&Apache::edit::insertlist($target,$token).' '
1463: .&Apache::loncommon::help_open_topic('Function_Plot_Response_Question','Function Plot Responses')
1464: .'</span></td>'
1465: ."<td> "
1466: .&Apache::edit::end_row()
1467: .&Apache::edit::start_spanning_row()
1468: ."\n";
1469: $result.=&Apache::edit::text_arg('Width (pixels):','width',
1470: $token,'6').' '.
1471: &Apache::edit::text_arg('Height (pixels):','height',
1472: $token,'6').'<br />'.
1473: &Apache::edit::text_arg('Label x-axis:','xlabel',
1474: $token,'6').' '.
1475: &Apache::edit::text_arg('Minimum x-value:','xmin',
1476: $token,'4').' '.
1477: &Apache::edit::text_arg('Maximum x-value:','xmax',
1478: $token,'4').' '.
1479: &Apache::edit::select_arg('x-axis visible:','xaxisvisible',
1480: ['yes','no'],$token).'<br />'.
1481: &Apache::edit::text_arg('Label y-axis:','ylabel',
1482: $token,'6').' '.
1483: &Apache::edit::text_arg('Minimum y-value:','ymin',
1484: $token,'4').' '.
1485: &Apache::edit::text_arg('Maximum y-value:','ymax',
1486: $token,'4').' '.
1487: &Apache::edit::select_arg('y-axis visible:','yaxisvisible',
1488: ['yes','no'],$token).'<br />'.
1489: &Apache::edit::select_arg('Grid visible:','gridvisible',
1490: ['yes','no'],$token).'<br />'.
1491: &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,...):',
1492: 'answerdisplay',$token,'50').
1493: &Apache::edit::end_row().&Apache::edit::start_spanning_row();
1494: } elsif ($target eq 'modified') {
1495: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
1496: $safeeval,'width','height','xlabel','xmin','xmax','ylabel','ymin','ymax',
1497: 'xaxisvisible','yaxisvisible','gridvisible','answerdisplay');
1498: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
1499:
1500: } elsif ($target eq 'meta') {
1501: $result=&Apache::response::meta_package_write('functionplotresponse');
1502: } elsif (($target eq 'answer') &&
1503: ($env{'form.answer_output_mode'} ne 'tex') &&
1504: ($Apache::lonhomework::viewgrades == 'F')) {
1505: my (undef,undef,$udom,$uname)=&Apache::lonnet::whichuser();
1506: $uname =~s/\W//g;
1507: $udom =~s/\W//g;
1508: my $function_name =
1509: join('_','LONCAPA_scriptvars',$uname,$udom,
1510: $env{'form.counter'},$Apache::lonxml::curdepth);
1511: &Apache::lonxml::add_script_result(
1512: &Apache::loncommon::modal_adhoc_window($function_name,700,500,
1513: '<pre style="background-color:#ffffff;">'.$Apache::functionplotresponse::ruleslog.'</pre>',
1514: &mt('Rules Log'))."<br />");
1515: }
1516: return $result;
1517: }
1518:
1519: sub compare_rel {
1520: my ($relationship,$value,$realval,$tol)=@_;
1521: # is the real value undefined?
1522: unless (defined($realval)) {
1523: # the real value is not defined
1524: if ($relationship eq 'eq') {
1525: if ($value eq 'undef') {
1526: return 1;
1527: } else {
1528: return 0;
1529: }
1530: } elsif ($relationship eq 'ne') {
1531: if ($value eq 'undef') {
1532: return 0;
1533: } else {
1534: return 1;
1535: }
1536: } else {
1537: return 0;
1538: }
1539: }
1540:
1541: # is the expected value undefined?
1542: if ($value eq 'undef') {
1543: # but by now we know that the real value is defined
1544: return 0;
1545: }
1546:
1547: # both are defined.
1548: if ($relationship eq 'gt') {
1549: return ($realval>$value);
1550: } elsif ($relationship eq 'ge') {
1551: return ($realval>$value-$tol);
1552: } elsif ($relationship eq 'lt') {
1553: return ($realval<$value);
1554: } elsif ($relationship eq 'le') {
1555: return ($realval<$value+$tol);
1556: } elsif ($relationship eq 'ne') {
1557: return (abs($value-$realval)>$tol);
1558: } else {
1559: return (abs($value-$realval)<$tol);
1560: }
1561: return 0;
1562: }
1563:
1564: sub addlog {
1565: my ($text)=@_;
1566: $text=~s/\'/\\\'/g;
1567: $Apache::functionplotresponse::ruleslog.=$text.'<br />';
1568: }
1569:
1570: sub actualval {
1571: my ($i,$xmin,$xmax)=@_;
1572: return $xmin+$i/400.*($xmax-$xmin);
1573: }
1574:
1575: sub fpr_val {
1576: my ($arg)=@_;
1577: return &actualval($Apache::functionplotresponse::functionplotrulelabels{$arg},
1578: $Apache::functionplotresponse::fpr_xmin,
1579: $Apache::functionplotresponse::fpr_xmax);
1580: }
1581:
1582: sub fpr_f {
1583: my ($arg)=@_;
1584: return $Apache::functionplotresponse::func[&array_index($Apache::functionplotresponse::fpr_xmin,
1585: $Apache::functionplotresponse::fpr_xmax,
1586: $arg)];
1587: }
1588:
1589: sub fpr_dfdx {
1590: my ($arg)=@_;
1591: return $Apache::functionplotresponse::dfuncdx[&array_index($Apache::functionplotresponse::fpr_xmin,
1592: $Apache::functionplotresponse::fpr_xmax,
1593: $arg)];
1594: }
1595:
1596: sub fpr_d2fdx2 {
1597: my ($arg)=@_;
1598: return $Apache::functionplotresponse::d2funcdx2[&array_index($Apache::functionplotresponse::fpr_xmin,
1599: $Apache::functionplotresponse::fpr_xmax,
1600: $arg)];
1601: }
1602:
1603: sub fpr_vectorcoords {
1604: my ($arg)=@_;
1605: $arg=~s/\W//gs;
1606: $arg=ucfirst($arg);
1607: my $id=$Apache::inputtags::response[-1];
1608: my $partid=$Apache::inputtags::part;
1609: my $internalid = $partid.'_'.$id;
1610: return ($env{'form.HWVAL_'.$internalid.'_'.$arg.'Start_x'},
1611: $env{'form.HWVAL_'.$internalid.'_'.$arg.'End_x'},
1612: $env{'form.HWVAL_'.$internalid.'_'.$arg.'Start_y'},
1613: $env{'form.HWVAL_'.$internalid.'_'.$arg.'End_y'});
1614: }
1615:
1616: sub fpr_objectcoords {
1617: my ($arg)=@_;
1618: $arg=~s/\W//gs;
1619: $arg=ucfirst($arg);
1620: my $id=$Apache::inputtags::response[-1];
1621: my $partid=$Apache::inputtags::part;
1622: my $internalid = $partid.'_'.$id;
1623: return ($env{'form.HWVAL_'.$internalid.'_'.$arg.'_x'},
1624: $env{'form.HWVAL_'.$internalid.'_'.$arg.'_y'});
1625: }
1626:
1627: sub fpr_vectorlength {
1628: my ($arg)=@_;
1629: my ($xs,$xe,$ys,$ye)=&fpr_vectorcoords($arg);
1630: return sqrt(($xe-$xs)*($xe-$xs)+($ye-$ys)*($ye-$ys));
1631: }
1632:
1633: sub fpr_vectorangle {
1634: my ($arg)=@_;
1635: my ($xs,$xe,$ys,$ye)=&fpr_vectorcoords($arg);
1636: my $angle=57.2957795*atan2(($ye-$ys),($xe-$xs));
1637: if ($angle<0) { $angle=360+$angle; }
1638: return $angle;
1639: }
1640:
1641: sub vectorcoords {
1642: my ($id,$label)=@_;
1643: return ($env{'form.HWVAL_'.$id.'_'.$label.'Start_x'},
1644: $env{'form.HWVAL_'.$id.'_'.$label.'End_x'},
1645: $env{'form.HWVAL_'.$id.'_'.$label.'Start_y'},
1646: $env{'form.HWVAL_'.$id.'_'.$label.'End_y'});
1647: }
1648:
1649: sub objectcoords {
1650: my ($id,$label)=@_;
1651: return ($env{'form.HWVAL_'.$id.'_'.$label.'_x'},
1652: $env{'form.HWVAL_'.$id.'_'.$label.'_y'});
1653: }
1654:
1655: sub attached {
1656: my ($id,$vector,$objects,$xmin,$xmax,$ymin,$ymax)=@_;
1657: my ($xs,$xe,$ys,$ye)=&vectorcoords($id,$vector);
1658: my $tolx=($xmax-$xmin)/100.;
1659: my $toly=($ymax-$ymin)/100.;
1660: my $tail=0;
1661: my $tip=0;
1662: foreach my $obj (split(/\s*\,\s*/,$objects)) {
1663: $obj=~s/\W//g;
1664: unless ($obj) { next; }
1665: $obj=ucfirst($obj);
1666: my ($xo,$yo)=&objectcoords($id,$obj);
1667: &addlog("Proximity $vector ($xs,$ys)-($xe,$ye) to $obj ($xo,$yo)");
1668: if ((abs($xs-$xo)<$tolx) && (abs($ys-$yo)<$toly)) {
1669: $tail=1;
1670: &addlog("Attached tail: $obj");
1671: }
1672: if ((abs($xe-$xo)<$tolx) && (abs($ye-$yo)<$toly)) {
1673: $tip=1;
1674: &addlog("Attached tip: $obj");
1675: }
1676: }
1677: &addlog("Result tail:$tail tip:$tip");
1678: return($tail,$tip);
1679: }
1680:
1681:
1682: sub vectorangle {
1683: my ($x,$y)=@_;
1684: my $angle=57.2957795*atan2($y,$x);
1685: if ($angle<0) { $angle=360+$angle; }
1686: return $angle;
1687: }
1688:
1689: sub vectorlength {
1690: my ($x,$y)=@_;
1691: return sqrt($x*$x+$y*$y);
1692: }
1693:
1694: sub relvector {
1695: my ($xs,$xe,$ys,$ye)=@_;
1696: return ($xe-$xs,$ye-$ys);
1697: }
1698:
1699: sub plotvectorlength {
1700: return &vectorlength(&relvector(&vectorcoords(@_)));
1701: }
1702:
1703: sub plotvectorangle {
1704: return &vectorangle(&relvector(&vectorcoords(@_)));
1705: }
1706:
1707:
1708: #
1709: # Evaluate a functionplotvectorrule
1710: #
1711:
1712: sub functionplotvectorrulecheck {
1713: my ($rule,$xmin,$xmax,$ymin,$ymax,$safeeval)=@_;
1714: &addlog("=================");
1715: my ($label,$type)=split(/\:/,$rule);
1716: if ($type eq 'vector') {
1717: return &vectorcheck($rule,$xmin,$xmax,$ymin,$ymax,$safeeval);
1718: } elsif ($type eq 'sum') {
1719: return &sumcheck($rule,$xmin,$xmax,$ymin,$ymax,$safeeval);
1720: } elsif ($type eq 'custom') {
1721: return &customcheck($rule,$safeeval);
1722: }
1723: }
1724:
1725: sub vectorcheck {
1726: my ($rule,$xmin,$xmax,$ymin,$ymax,$safeeval)=@_;
1727: my ($label,$type,$id,$vector,
1728: $attachpoint,$notattachpoint,
1729: $tailpoint,$tippoint,$nottailpoint,$nottippoint,
1730: $length,$angle,$lengtherror,$angleerror)=split(/\:/,$rule);
1731: &addlog("Vector Rule $label for vector ".$vector);
1732: if ($length ne '') {
1733: &addlog("Checking for length $length with error $lengtherror");
1734: $length=&Apache::run::run($length,$safeeval);
1735: &addlog("Length evaluated to $length");
1736: my $thislength=&plotvectorlength($id,$vector);
1737: &addlog("Found length $thislength");
1738: if (abs($thislength-$length)>$lengtherror) {
1739: &setfailed($label);
1740: return 0;
1741: }
1742: }
1743: if ($angle ne '') {
1744: &addlog("Checking for angle $angle with error $angleerror");
1745: $angle=&Apache::run::run($angle,$safeeval);
1746: &addlog("Angle evaluated to $angle");
1747: my $thisangle=&plotvectorangle($id,$vector);
1748: &addlog("Found angle $thisangle");
1749: my $anglediff=abs($thisangle-$angle);
1750: &addlog("Angle difference: $anglediff");
1751: if ($anglediff>360.-$anglediff) {
1752: $anglediff=360.-$anglediff;
1753: }
1754: &addlog("Smallest angle difference: $anglediff");
1755: if ($anglediff>$angleerror) {
1756: &setfailed($label);
1757: return 0;
1758: }
1759: }
1760: if ($attachpoint ne '') {
1761: &addlog("Checking attached: ".$attachpoint);
1762: my ($tail,$tip)=&attached($id,$vector,$attachpoint,$xmin,$xmax,$ymin,$ymax);
1763: unless ($tail || $tip) {
1764: &setfailed($label);
1765: return 0;
1766: }
1767: }
1768: if ($notattachpoint ne '') {
1769: &addlog("Checking not attached: ".$notattachpoint);
1770: my ($tail,$tip)=&attached($id,$vector,$notattachpoint,$xmin,$xmax,$ymin,$ymax);
1771: if ($tail || $tip) {
1772: &setfailed($label);
1773: return 0;
1774: }
1775: }
1776: if ($tailpoint ne '') {
1777: &addlog("Checking tail: ".$tailpoint);
1778: my ($tail,$tip)=&attached($id,$vector,$tailpoint,$xmin,$xmax,$ymin,$ymax);
1779: unless ($tail) {
1780: &setfailed($label);
1781: return 0;
1782: }
1783: }
1784: if ($nottailpoint ne '') {
1785: &addlog("Checking not tail: ".$nottailpoint);
1786: my ($tail,$tip)=&attached($id,$vector,$nottailpoint,$xmin,$xmax,$ymin,$ymax);
1787: if ($tail) {
1788: &setfailed($label);
1789: return 0;
1790: }
1791: }
1792: if ($tippoint ne '') {
1793: &addlog("Checking tip: ".$tippoint);
1794: my ($tail,$tip)=&attached($id,$vector,$tippoint,$xmin,$xmax,$ymin,$ymax);
1795: unless ($tip) {
1796: &setfailed($label);
1797: return 0;
1798: }
1799: }
1800: if ($nottippoint ne '') {
1801: &addlog("Checking not tip: ".$nottippoint);
1802: my ($tail,$tip)=&attached($id,$vector,$nottippoint,$xmin,$xmax,$ymin,$ymax);
1803: if ($tip) {
1804: &setfailed($label);
1805: return 0;
1806: }
1807: }
1808:
1809: &addlog("Rule $label passed.");
1810: return 1;
1811: }
1812:
1813: sub sumcheck {
1814: my ($rule,$xmin,$xmax,$ymin,$ymax,$safeeval)=@_;
1815: my ($label,$type,$id,$vectors,$length,$angle,$lengtherror,$angleerror)=split(/\:/,$rule);
1816: &addlog("Vector Sum Rule $label for vectors ".$vectors);
1817: my $sumx=0;
1818: my $sumy=0;
1819: foreach my $sv (split(/\s*\,\s*/,$vectors)) {
1820: my ($rx,$ry)=&relvector(&vectorcoords($id,$sv));
1821: $sumx+=$rx;
1822: $sumy+=$ry;
1823: }
1824: &addlog("Sum vector ($sumx,$sumy)");
1825: if ($length ne '') {
1826: &addlog("Checking length $length with error $lengtherror");
1827: $length=&Apache::run::run($length,$safeeval);
1828: &addlog("Evaluated to $length");
1829: my $thislength=&vectorlength($sumx,$sumy);
1830: &addlog("Actual length $thislength");
1831: if (abs($length-$thislength)>$lengtherror) {
1832: &setfailed($label);
1833: return 0;
1834: }
1835: }
1836: if ($angle ne '') {
1837: &addlog("Checking angle $angle with error $angleerror");
1838: $angle=&Apache::run::run($angle,$safeeval);
1839: &addlog("Evaluated to $angle");
1840: my $thisangle=&vectorangle($sumx,$sumy);
1841: &addlog("Actual angle $thisangle");
1842: my $anglediff=abs($thisangle-$angle);
1843: &addlog("Angle difference: $anglediff");
1844: if ($anglediff>360.-$anglediff) {
1845: $anglediff=360.-$anglediff;
1846: }
1847: &addlog("Smallest angle difference: $anglediff");
1848: if ($anglediff>$angleerror) {
1849: &setfailed($label);
1850: return 0;
1851: }
1852: }
1853: &addlog("Rule $label passed.");
1854: return 1;
1855: }
1856:
1857: sub customcheck {
1858: my ($rule,$safeeval)=@_;
1859: my ($label,$type,$prg)=split(/\:/,$rule);
1860: &addlog("Custom Rule ".$label);
1861: my $result=&Apache::run::run(&unescape($prg),$safeeval);
1862: &addlog("Algorithm returned $result");
1863: unless ($result) {
1864: &setfailed($label);
1865: return 0;
1866: }
1867: &addlog("Rule $label passed.");
1868: return 1;
1869: }
1870:
1871: #
1872: # Evaluate a functionplotrule
1873: #
1874:
1875: sub functionplotrulecheck {
1876: my ($rule,$xmin,$xmax,$ymin,$ymax,$safeeval)=@_;
1877:
1878: my ($label,$derivative,$xinitial,$xinitiallabel,$xfinal,$xfinallabel,$minimumlength,$maximumlength,$relationship,$value,$percent)
1879: =split(/\:/,$rule);
1880: $percent=($percent>0?$percent:5);
1881: &addlog("=================");
1882: &addlog("Rule $label for ".($derivative<0?'integral':('function itself','first derivative','second derivative')[$derivative])." $relationship $value");
1883: #
1884: # Evaluate the value
1885: #
1886: if (($value=~/\D/) && ($value ne 'undef')) {
1887: $Apache::functionplotresponse::fpr_xmin=$xmin;
1888: $Apache::functionplotresponse::fpr_xmax=$xmax;
1889: $value=&Apache::run::run($value,$safeeval);
1890: &addlog("Value evaluated to $value");
1891: }
1892:
1893: #
1894: # Minimum and maximum lengths of the interval
1895: #
1896: if ((defined($minimumlength)) || (defined($maximumlength))) {
1897: &addlog("Minimumlength $minimumlength Maximumlength $maximumlength");
1898: }
1899: my $li=0;
1900: my $lh=400;
1901:
1902: # Special case: the upper boundary was not defined
1903: # and needs to be set to the value where
1904: # the condition is not true anymore => set flag
1905:
1906: my $findupper=0;
1907: if (($xfinal eq '')
1908: && (!defined($Apache::functionplotresponse::functionplotrulelabels{$xfinallabel}))
1909: && ($xfinallabel)) {
1910: $findupper=1;
1911: }
1912:
1913: # if a hard value is set for the boundaries, it overrides the label
1914: if (($xinitial ne '') && ($xinitiallabel ne '') && ($xinitiallabel ne 'start')) {
1915: $li=&array_index($xmin,$xmax,$xinitial);
1916: $Apache::functionplotresponse::functionplotrulelabels{$xinitiallabel}=$li;
1917: }
1918: if (($xfinal ne '') && ($xfinallabel ne '') && ($xfinallabel ne 'end')) {
1919: $lh=&array_index($xmin,$xmax,$xfinal);
1920: $Apache::functionplotresponse::functionplotrulelabels{$xfinallabel}=$lh;
1921: }
1922: # if the label is defined, use it
1923: if (defined($Apache::functionplotresponse::functionplotrulelabels{$xinitiallabel})) {
1924: &addlog("Using lower label $xinitiallabel");
1925: $li=$Apache::functionplotresponse::functionplotrulelabels{$xinitiallabel};
1926: } else {
1927: $li=&array_index($xmin,$xmax,$xinitial);
1928: }
1929: unless ($findupper) {
1930: if (defined($Apache::functionplotresponse::functionplotrulelabels{$xfinallabel})) {
1931: &addlog("Using upper label $xfinallabel");
1932: $lh=$Apache::functionplotresponse::functionplotrulelabels{$xfinallabel}-1;
1933: } else {
1934: $lh=&array_index($xmin,$xmax,$xfinal);
1935: }
1936: }
1937: # Basic sanity checks
1938: if ($li<0) { $li=0; }
1939: if ($lh>400) { $lh=400; }
1940: if (($li>$lh) || (!defined($lh))) {
1941: $lh=$li;
1942: }
1943:
1944: &addlog("Boundaries: x=".&actualval($li,$xmin,$xmax)." (".$Apache::functionplotresponse::actualxval[$li]."; index $li)) to x=".
1945: &actualval($lh,$xmin,$xmax)." (".$Apache::functionplotresponse::actualxval[$lh]."; index $lh))");
1946: if ($findupper) {
1947: &addlog("Looking for label $xfinallabel");
1948: }
1949: my $tol=$percent*($ymax-$ymin)/100;
1950: if ($xmax>$xmin) {
1951: if ($derivative==2) {
1952: $tol=4.*$tol/($xmax-$xmin);
1953: } elsif ($derivative==1) {
1954: $tol=2.*$tol/($xmax-$xmin);
1955: } elsif ($derivative==-1) {
1956: $tol=$tol*($xmax-$xmin)/2.;
1957: }
1958: }
1959: my $integral=0;
1960: my $binwidth=($xmax-$xmin)/400.;
1961: if (($derivative<0) && (!$findupper)) {
1962: # definite integral, calculate over whole length
1963: &addlog("Calculating definite integral");
1964: for (my $i=$li; $i<=$lh; $i++) {
1965: $integral+=$Apache::functionplotresponse::func[$i]*$binwidth;
1966: }
1967: unless (&compare_rel($relationship,$value,$integral,$tol)) {
1968: &addlog("Actual integral ".(defined($integral)?$integral:'undef').", expected $value, tolerance $tol");
1969: &addlog("Rule $label failed.");
1970: &setfailed($label);
1971: return 0;
1972: }
1973: } else {
1974: for (my $i=$li; $i<=$lh; $i++) {
1975: my $val;
1976: if ($derivative==2) {
1977: $val=$Apache::functionplotresponse::d2funcdx2[$i];
1978: } elsif ($derivative==1) {
1979: $val=$Apache::functionplotresponse::dfuncdx[$i];
1980: } elsif ($derivative==-1) {
1981: $integral+=$Apache::functionplotresponse::func[$i]*$binwidth;
1982: $val=$integral;
1983: } else {
1984: $val=$Apache::functionplotresponse::func[$i];
1985: }
1986: unless (&compare_rel($relationship,$value,$val,$tol)) {
1987: &addlog("Actual value ".(defined($val)?$val:'undef').", expected $value, tolerance $tol");
1988: &addlog("Condition not fulfilled at x=".&actualval($i,$xmin,$xmax)." (".$Apache::functionplotresponse::actualxval[$i]."; index $i)");
1989: if (($findupper) && ($i>$li)) {
1990: # Check lengths
1991: unless (&checklength($i,$li,$minimumlength,$maximumlength,$xmin,$xmax,$label)) { return 0; }
1992: # Successfully found a new label, set it
1993: $Apache::functionplotresponse::functionplotrulelabels{$xfinallabel}=$i;
1994: &addlog("Rule $label passed, setting label $xfinallabel");
1995: return 1;
1996: } else {
1997: &addlog("Rule $label failed.");
1998: &setfailed($label);
1999: return 0;
2000: }
2001: }
2002: }
2003: }
2004: # Corner case where this makes sense: using start or stop as defined labels
2005: unless (&checklength($lh,$li,$minimumlength,$maximumlength,$xmin,$xmax,$label)) { return 0; }
2006: &addlog("Rule $label passed.");
2007: return 1;
2008: }
2009:
2010: #
2011: # check for minimum and maximum lengths
2012: #
2013:
2014: sub checklength {
2015: my ($i,$li,$minimumlength,$maximumlength,$xmin,$xmax,$label)=@_;
2016: unless (($minimumlength) || ($maximumlength)) { return 1; }
2017: my $length=&actualval($i,$xmin,$xmax)-&actualval($li,$xmin,$xmax);
2018: if ($minimumlength) {
2019: if ($length<$minimumlength) {
2020: &addlog("Rule $label failed, actual length $length, minimum length $minimumlength");
2021: &setfailed($label);
2022: return 0;
2023: }
2024: }
2025: if ($maximumlength) {
2026: if ($length>$maximumlength) {
2027: &addlog("Rule $label failed, actual length $length, maximum length $maximumlength");
2028: &setfailed($label);
2029: return 0;
2030: }
2031: }
2032: return 1;
2033: }
2034:
2035: sub setfailed {
2036: my ($hintlabel)=@_;
2037: $hintlabel=~s/^R//;
2038: push(@Apache::functionplotresponse::failedrules,$hintlabel);
2039: &addlog("Set hint condition $hintlabel");
2040: }
2041:
2042: sub start_functionplotruleset {
2043: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2044: if ($target eq 'edit') {
2045: return &Apache::edit::start_table($token).
2046: '<tr><td><span class="LC_nobreak">'.&Apache::loncommon::insert_folding_button().&mt('Function Plot Rule Set').'</span></td>'
2047: .'<td><span class="LC_nobreak">'.&mt('Delete?').' '
2048: .&Apache::edit::deletelist($target,$token).' '.
2049: &Apache::edit::insertlist($target,$token).' '
2050: .&Apache::loncommon::help_open_topic('Function_Plot_Response_Rule_Set','Function Plot Rules')
2051: .'</span></td>'
2052: ."<td> "
2053: .&Apache::edit::end_row()
2054: .&Apache::edit::start_spanning_row()
2055: ."\n";
2056: }
2057: }
2058:
2059: sub end_functionplotruleset {
2060: my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
2061: my $id=$Apache::inputtags::response[-1];
2062: my $partid=$Apache::inputtags::part;
2063: my $internalid = $partid.'_'.$id;
2064:
2065: if ($target eq 'edit' ) {
2066: return &Apache::edit::end_table();
2067: } elsif ($target eq 'grade'
2068: && &Apache::response::submitted()
2069: && $Apache::lonhomework::type ne 'exam') {
2070: #
2071: # Actually grade
2072: #
2073: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-2);
2074:
2075: my $ad='';
2076: undef %Apache::functionplotresponse::functionplotrulelabels;
2077: $Apache::functionplotresponse::ruleslog='';
2078: $Apache::functionplotresponse::functionplotrulelabels{'start'}=400;
2079: $Apache::functionplotresponse::functionplotrulelabels{'end'}=0;
2080: if (&populate_arrays($internalid,$xmin,$xmax,$ymin,$ymax) eq 'no_func') {
2081: $ad='NOT_FUNCTION';
2082: } else {
2083: &addlog("Start of function ".&actualval($Apache::functionplotresponse::functionplotrulelabels{'start'},$xmin,$xmax)." (index ".
2084: $Apache::functionplotresponse::functionplotrulelabels{'start'}.")");
2085: &addlog("End of function ".&actualval($Apache::functionplotresponse::functionplotrulelabels{'end'},$xmin,$xmax)." (index ".
2086: $Apache::functionplotresponse::functionplotrulelabels{'end'}.")");
2087:
2088: # We have a function that we can actually grade, go through the spline rules.
2089: foreach my $rule (@Apache::functionplotresponse::functionplotrules) {
2090: unless (&functionplotrulecheck($rule,$xmin,$xmax,$ymin,$ymax,$safeeval)) {
2091: $ad='INCORRECT';
2092: last;
2093: }
2094: }
2095: # And now go through the vector rules
2096: foreach my $rule (@Apache::functionplotresponse::functionplotvectorrules) {
2097: unless (&functionplotvectorrulecheck($rule,$xmin,$xmax,$ymin,$ymax,$safeeval)) {
2098: $ad='INCORRECT';
2099: last;
2100: }
2101: }
2102: # If it's not wrong, it's correct
2103: unless ($ad) { $ad='EXACT_ANS' };
2104: }
2105: &addlog("Set hint conditions: ".join(",",@Apache::functionplotresponse::failedrules));
2106: &addlog("Assigned award detail: $ad");
2107: # Store for later to be assigned at end_functionplotresponse
2108: $Apache::functionplotresponse::awarddetail=$ad;
2109: }
2110: }
2111:
2112:
2113: sub end_functionplotresponse {
2114: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2115: &Apache::response::end_response;
2116:
2117: my $result;
2118: my $id=$Apache::inputtags::response[-1];
2119: my $partid=$Apache::inputtags::part;
2120: my $internalid = $partid.'_'.$id;
2121:
2122: if ($target eq 'edit') { $result=&Apache::edit::end_table(); }
2123: if ($target eq 'grade'
2124: && &Apache::response::submitted()
2125: && $Apache::lonhomework::type eq 'exam') {
2126:
2127: &Apache::response::scored_response($partid,$id);
2128:
2129: } elsif ($target eq 'grade'
2130: && &Apache::response::submitted()
2131: && $Apache::lonhomework::type ne 'exam') {
2132: my ($response,%coords)=&get_answer_from_form_fields($internalid);
2133: $Apache::lonhomework::results{"resource.$partid.$id.submission"}=$response;
2134: my %previous=&Apache::response::check_for_previous($response,$partid,$id);
2135: #
2136: # Assign grade
2137: #
2138: my $ad=$Apache::functionplotresponse::awarddetail;
2139: #
2140: # Store grading info
2141: #
2142: $Apache::lonhomework::results{"resource.$partid.$id.awarddetail"}=$ad;
2143: &Apache::response::handle_previous(\%previous,$ad);
2144: } elsif ($target eq 'web') {
2145: undef @Apache::functionplotresponse::failedrules;
2146: }
2147: return $result;
2148: }
2149:
2150: sub end_functionplotelements {
2151: my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
2152: my $result='';
2153: my $id=$Apache::inputtags::response[-1];
2154: my $partid=$Apache::inputtags::part;
2155: my $internalid = $partid.'_'.$id;
2156: if ($target eq 'edit' ) {
2157: $result=&Apache::edit::end_table();
2158: } elsif ($target eq 'web') {
2159: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-2);
2160:
2161: # Are we in show answer mode?
2162: my $showanswer=&Apache::response::show_answer();
2163: if ($showanswer) {
2164: # Render answerdisplay
2165: my $answerdisplay=&Apache::lonxml::get_param('answerdisplay',$parstack,$safeeval,-2);
2166: if ($answerdisplay=~/\S/s) {
2167: foreach my $plot (split(/\s*\,\s*/,$answerdisplay)) {
2168: my @components=split(/\s*\:\s*/,$plot);
2169: if ($#components<3) {
2170: # Just a simple plot
2171: my ($func,$xl,$xh)=@components;
2172: if ((!defined($xl)) || ($xl eq '')) { $xl=$xmin; }
2173: if ((!defined($xh)) || ($xh eq '')) { $xh=$xmax; }
2174: $result.=&plot_script($internalid,$func,1,'','00aa00',$xl,$xh,6);
2175: } else {
2176: # This is a spline
2177: $result.=&answer_spline_script($internalid,@components);
2178: }
2179: }
2180: }
2181: }
2182: my $fixed=0;
2183: if (($showanswer) || (&Apache::response::check_status()>=2)) { $fixed=1; }
2184: # Now is the time to render all of the stored splines
2185: foreach my $label (keys(%Apache::functionplotresponse::splineorder)) {
2186: $result.=&generate_spline($internalid,$label,$xmin,$xmax,$ymin,$ymax,$fixed);
2187: }
2188: # close the init script
2189: $result.=&end_init_script();
2190: # register all splines in this response
2191: $result.='<input type="hidden" name="HWVAL_AllSplines_'.$internalid.'" value="'.
2192: join(',',keys(%Apache::functionplotresponse::splineorder)).'" />'."\n";
2193: foreach my $label (keys(%Apache::functionplotresponse::splineorder)) {
2194: $result.='<input type="hidden" name="HWVAL_SplineOrder_'.$internalid.'_'.$label.'" value="'.
2195: $Apache::functionplotresponse::splineorder{$label}.'" />'."\n";
2196: }
2197: # generate the input fields
2198: $result.=$Apache::functionplotresponse::inputfields;
2199: # actually start the <applet>-tag
2200: $result.=&geogebra_startcode($internalid,
2201: &Apache::lonxml::get_param('width',$parstack,$safeeval,-2),
2202: &Apache::lonxml::get_param('height',$parstack,$safeeval,-2));
2203: # set default parameters
2204: $result.=&geogebra_default_parameters($internalid);
2205: # close the <applet>-tag
2206: $result.=&geogebra_endcode();
2207: }
2208: return $result;
2209: }
2210:
2211: sub boundaries {
2212: my ($parstack,$safeeval,$level)=@_;
2213: my $xmin=&Apache::lonxml::get_param('xmin',$parstack,$safeeval,$level);
2214: $xmin=(defined($xmin)?$xmin:-10);
2215: my $xmax=&Apache::lonxml::get_param('xmax',$parstack,$safeeval,$level);
2216: $xmax=(defined($xmax)?$xmax:10);
2217: my $ymin=&Apache::lonxml::get_param('ymin',$parstack,$safeeval,$level);
2218: $ymin=(defined($ymin)?$ymin:-10);
2219: my $ymax=&Apache::lonxml::get_param('ymax',$parstack,$safeeval,$level);
2220: $ymax=(defined($ymax)?$ymax:10);
2221: if ($xmax<=$xmin) {
2222: $xmax=$xmin+20;
2223: }
2224: if ($ymax<=$ymin) {
2225: $ymax=$ymin+20;
2226: }
2227: return ($xmin,$xmax,$ymin,$ymax);
2228: }
2229:
2230: sub start_functionplotelements {
2231: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2232: my $result='';
2233: my $id=$Apache::inputtags::response[-1];
2234: my $partid=$Apache::inputtags::part;
2235: my $internalid = $partid.'_'.$id;
2236:
2237: if ($target eq 'edit') {
2238: return &Apache::edit::start_table($token).
2239: '<tr><td><span class="LC_nobreak">'.&Apache::loncommon::insert_folding_button()
2240: .&mt('Function Plot Elements').'</span></td>'
2241: .'<td><span class="LC_nobreak">'.&mt('Delete?').' '
2242: .&Apache::edit::deletelist($target,$token).' '.
2243: &Apache::edit::insertlist($target,$token).' '
2244: .&Apache::loncommon::help_open_topic('Function_Plot_Response_Elements','Function Plot Elements')
2245: .'</span></td>'
2246: ."<td> "
2247: .&Apache::edit::end_row()
2248: .&Apache::edit::start_spanning_row()
2249: ."\n";
2250: } elsif ($target eq 'web') {
2251: my ($xmin,$xmax,$ymin,$ymax)=&boundaries($parstack,$safeeval,-2);
2252: my $xaxisvisible=(&Apache::lonxml::get_param('xaxisvisible',$parstack,$safeeval,-2)=~/on|true|yes|1/i?'true':'false');
2253: my $yaxisvisible=(&Apache::lonxml::get_param('yaxisvisible',$parstack,$safeeval,-2)=~/on|true|yes|1/i?'true':'false');
2254: my $gridvisible=(&Apache::lonxml::get_param('gridvisible',$parstack,$safeeval,-2)=~/on|true|yes|1/i?'true':'false');
2255: my $xlabel=&Apache::lonxml::get_param('xlabel',$parstack,$safeeval,-2);
2256: my $ylabel=&Apache::lonxml::get_param('ylabel',$parstack,$safeeval,-2);
2257:
2258:
2259: # paste in the update routine to receive stuff back from the applet
2260: $result.=&update_script($internalid);
2261: # start the initscript for this applet
2262: $result.=&start_init_script($internalid);
2263: # put the axis commands inside
2264: $result.=&axes_script($internalid,$xmin,$xmax,$ymin,$ymax,$xaxisvisible,$yaxisvisible,$gridvisible);
2265: $result.=&axes_label($internalid,$xmin,$xmax,$ymin,$ymax,$xlabel,$ylabel);
2266: # init script is left open
2267: }
2268: return $result;
2269: }
2270:
2271: 1;
2272:
2273: __END__
2274:
2275: =head1 NAME
2276:
2277: Apache::functionplotresponse.pm;
2278:
2279: =head1 SYNOPSIS
2280:
2281: Handles tags associated with accepting function plots.
2282:
2283: This is part of the LearningOnline Network with CAPA project
2284: described at http://www.lon-capa.org.
2285:
2286: =head1 HANDLER SUBROUTINE
2287:
2288: start_functionplotresponse()
2289:
2290: =head1 OTHER SUBROUTINES
2291:
2292: =over
2293:
2294: =item end_functionplotresponse()
2295:
2296: =back
2297:
2298: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>