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').'&nbsp;'.
  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').'&nbsp;'.
 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').'&nbsp;'.
 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').'&nbsp;'.
 1240:              &Apache::edit::select_arg('Order:','order',
 1241:                                   ['2','3','4','5','6','7','8'],$token).'&nbsp;'.
 1242:              &Apache::edit::text_arg('Initial x-value:','initx',
 1243:                                      $token,'4').'&nbsp;'.
 1244:              &Apache::edit::text_arg('Initial y-value:','inity',
 1245:                                      $token,'4').'&nbsp;'.
 1246:              &Apache::edit::text_arg('Scale x:','scalex',
 1247:                                      $token,'4').'&nbsp;'.
 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).'&nbsp;&nbsp;&nbsp;'
 1462:        .&Apache::edit::insertlist($target,$token).'&nbsp;&nbsp;&nbsp;'
 1463:        .&Apache::loncommon::help_open_topic('Function_Plot_Response_Question','Function Plot Responses')
 1464:        .'</span></td>'
 1465:        ."<td>&nbsp;"
 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').'&nbsp;'.
 1471:              &Apache::edit::text_arg('Height (pixels):','height',
 1472:                                      $token,'6').'<br />'.
 1473:              &Apache::edit::text_arg('Label x-axis:','xlabel',
 1474:                                      $token,'6').'&nbsp;'.
 1475:              &Apache::edit::text_arg('Minimum x-value:','xmin',
 1476:                                      $token,'4').'&nbsp;'.
 1477:              &Apache::edit::text_arg('Maximum x-value:','xmax',
 1478:                                      $token,'4').'&nbsp;'.
 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').'&nbsp;'.
 1483:              &Apache::edit::text_arg('Minimum y-value:','ymin',
 1484:                                      $token,'4').'&nbsp;'.
 1485:              &Apache::edit::text_arg('Maximum y-value:','ymax',
 1486:                                      $token,'4').'&nbsp;'.
 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).'&nbsp;&nbsp;&nbsp;'.
 2049:         &Apache::edit::insertlist($target,$token).'&nbsp;&nbsp;&nbsp;'
 2050:        .&Apache::loncommon::help_open_topic('Function_Plot_Response_Rule_Set','Function Plot Rules')
 2051:        .'</span></td>'
 2052:        ."<td>&nbsp;"
 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).'&nbsp;&nbsp;&nbsp;'.
 2243:         &Apache::edit::insertlist($target,$token).'&nbsp;&nbsp;&nbsp;'
 2244:        .&Apache::loncommon::help_open_topic('Function_Plot_Response_Elements','Function Plot Elements')
 2245:        .'</span></td>'
 2246:        ."<td>&nbsp;"
 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>