File:  [LON-CAPA] / loncom / homework / imageresponse.pm
Revision 1.95: download - view: text, annotated - select for diffs
Wed Nov 12 20:01:21 2008 UTC (15 years, 6 months ago) by jms
Branches: MAIN
CVS tags: HEAD
Added/modified POD documentation

    1: #
    2: # The LearningOnline Network with CAPA
    3: # image click response style
    4: #
    5: # $Id: imageresponse.pm,v 1.95 2008/11/12 20:01:21 jms Exp $
    6: #
    7: # Copyright Michigan State University Board of Trustees
    8: #
    9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
   10: #
   11: # LON-CAPA is free software; you can redistribute it and/or modify
   12: # it under the terms of the GNU General Public License as published by
   13: # the Free Software Foundation; either version 2 of the License, or
   14: # (at your option) any later version.
   15: #
   16: # LON-CAPA is distributed in the hope that it will be useful,
   17: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   18: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   19: # GNU General Public License for more details.
   20: #
   21: # You should have received a copy of the GNU General Public License
   22: # along with LON-CAPA; if not, write to the Free Software
   23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   24: #
   25: # /home/httpd/html/adm/gpl.txt
   26: #
   27: # http://www.lon-capa.org/
   28: #
   29: #FIXME LATER assumes multiple possible submissions but only one is possible 
   30: #currently
   31: 
   32: 
   33: =head1 NAME
   34: 
   35: Apache::imageresponse
   36: 
   37: =head1 SYNOPSIS
   38: 
   39: Coordinates the response to clicking an image.
   40: 
   41: This is part of the LearningOnline Network with CAPA project
   42: described at http://www.lon-capa.org.
   43: 
   44: =head1 HANDLER SUBROUTINE
   45: 
   46: 
   47: 
   48: =head1 OTHER SUBROUTINES
   49: 
   50: =over
   51: 
   52: =item *
   53: 
   54: start_imageresponse()
   55: 
   56: =item *
   57: 
   58: end_imageresponse()
   59: 
   60: =item *
   61: 
   62: start_foilgroup()
   63: 
   64: =item *
   65: 
   66: getfoilcounts()
   67: 
   68: =item *
   69: 
   70: whichfoils()
   71: 
   72: =item *
   73: 
   74: prep_image()
   75: 
   76: =item *
   77: 
   78: draw_image()
   79: 
   80: =item *
   81: 
   82: displayfoils()
   83: 
   84: =item *
   85: 
   86: format_prior_response()
   87: 
   88: =item *
   89: 
   90: display_answers()
   91: 
   92: =item *
   93: 
   94: clean_up_image()
   95: 
   96: =item *
   97: 
   98: gradefoils()
   99: 
  100: =item *
  101: 
  102: stringify_submission()
  103: 
  104: =item *
  105: 
  106: get_submission()
  107: 
  108: =item *
  109: 
  110: end_foilgroup()
  111: 
  112: =item *
  113: 
  114: start_conceptgroup()
  115: 
  116: =item *
  117: 
  118: end_conceptgroup()
  119: 
  120: =item *
  121: 
  122: insert_foil()
  123: 
  124: =item *
  125: 
  126: start_foil()
  127: 
  128: =item *
  129: 
  130: end_foil()
  131: 
  132: =item *
  133: 
  134: start_text()
  135: 
  136: =item *
  137: 
  138: end_text()
  139: 
  140: =item *
  141: 
  142: start_image()
  143: 
  144: 
  145: =item *
  146: 
  147: end_image()
  148: 
  149: =item *
  150: 
  151: start_rectangle()
  152: 
  153: =item *
  154: 
  155: grade_rectangle()
  156: 
  157: =item *
  158: 
  159: end_rectangle()
  160: 
  161: =item *
  162: 
  163: start_polygon()
  164: 
  165: =item *
  166: 
  167: grade_polygon()
  168: 
  169: =item *
  170: 
  171: end_polygon()
  172: 
  173: =back
  174: 
  175: =cut
  176: 
  177: 
  178: package Apache::imageresponse;
  179: use strict;
  180: use Image::Magick();
  181: use Apache::randomlylabel();
  182: use Apache::londefdef();
  183: use Apache::Constants qw(:common :http);
  184: use Apache::lonlocal;
  185: use Apache::lonnet;
  186: use lib '/home/httpd/lib/perl/';
  187: use LONCAPA;
  188:  
  189: 
  190: BEGIN {
  191:     &Apache::lonxml::register('Apache::imageresponse',('imageresponse'));
  192: }
  193: 
  194: sub start_imageresponse {
  195:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  196:     my $result;
  197:     #when in a radiobutton response use these
  198:     &Apache::lonxml::register('Apache::imageresponse',
  199: 			      ('foilgroup','foil','text','image','rectangle',
  200: 			       'polygon','conceptgroup'));
  201:     push (@Apache::lonxml::namespace,'imageresponse');
  202:     my $id = &Apache::response::start_response($parstack,$safeeval);
  203:     undef(%Apache::response::foilnames);
  204:     if ($target eq 'meta') {
  205: 	$result=&Apache::response::meta_package_write('imageresponse');
  206:     } elsif ($target eq 'analyze') {
  207: 	my $part_id="$Apache::inputtags::part.$id";
  208:         $Apache::lonhomework::analyze{"$part_id.type"} = 'imageresponse';
  209: 	push (@{ $Apache::lonhomework::analyze{"parts"} },$part_id);
  210: 	push (@{ $Apache::lonhomework::analyze{"$part_id.bubble_lines"} },
  211: 	      1);
  212:     } elsif ( $target eq 'edit' ) {
  213: 	$result .= &Apache::edit::tag_start($target,$token).
  214: 	    &Apache::edit::text_arg('Max Number Of Shown Foils:',
  215: 				    'max',$token,'4').
  216: 	    &Apache::edit::end_row().
  217: 	    &Apache::edit::start_spanning_row();
  218:     } elsif ( $target eq 'modified' ) {
  219: 	my $constructtag=
  220: 	    &Apache::edit::get_new_args($token,$parstack,$safeeval,'max');
  221: 	if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
  222:     }
  223:     return $result;
  224: }
  225: 
  226: sub end_imageresponse {
  227:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  228: 
  229:     my $part_id     = $Apache::inputtags::part;
  230:     my $response_id = $Apache::inputtags::response[-1]; 
  231: 
  232:     pop(@Apache::lonxml::namespace);
  233:     &Apache::lonxml::deregister('Apache::imageresponse',('foilgroup','foil','text','image','rectangle','conceptgroup'));
  234: 
  235:     my $result;
  236:     if ($target eq 'edit') {
  237: 	$result=&Apache::edit::end_table();
  238:     } elsif ($target eq 'tex'
  239: 	     && $Apache::lonhomework::type eq 'exam') {
  240: 	$result=&Apache::inputtags::exam_score_line($target);
  241:     }
  242: 
  243:     undef(%Apache::response::foilnames);
  244:     
  245:     if ($target eq 'grade' || $target eq 'web' || $target eq 'answer' || 
  246: 	$target eq 'tex' || $target eq 'analyze') {
  247: 	&Apache::lonxml::increment_counter(&Apache::response::repetition(), 
  248: 					   "$part_id.$response_id");
  249: 	if ($target eq 'analyze') {
  250: 	    &Apache::lonhomework::set_bubble_lines();
  251: 	}
  252: 	
  253:     }
  254:     &Apache::response::end_response();
  255: 
  256:     return $result;
  257: }
  258: 
  259: %Apache::response::foilgroup=();
  260: sub start_foilgroup {
  261:     %Apache::response::foilgroup=();
  262:     $Apache::imageresponse::conceptgroup=0;
  263:     &Apache::response::pushrandomnumber();
  264:     return '';
  265: }
  266: 
  267: sub getfoilcounts {
  268:     my ($parstack,$safeeval)=@_;
  269: 
  270:     my $max = &Apache::lonxml::get_param('max',$parstack,$safeeval,'-2');
  271:     # +1 since instructors will count from 1
  272:     my $count = $#{ $Apache::response::foilgroup{'names'} }+1;
  273:     if (&Apache::response::showallfoils()) { $max=$count; }
  274:     return ($count,$max);
  275: }
  276: 
  277: sub whichfoils {
  278:     my ($max)=@_;
  279:     return if (!defined(@{ $Apache::response::foilgroup{'names'} }));
  280:     my @names = @{ $Apache::response::foilgroup{'names'} };
  281:     my @whichopt;
  282:     while ((($#whichopt+1) < $max) && ($#names > -1)) {
  283: 	&Apache::lonxml::debug("Have $#whichopt max is $max");
  284: 	my $aopt;
  285: 	if (&Apache::response::showallfoils()) {
  286: 	    $aopt=0;
  287: 	} else {
  288: 	    $aopt=int(&Math::Random::random_uniform() * ($#names+1));
  289: 	}
  290: 	&Apache::lonxml::debug("From $#names elms, picking $aopt");
  291: 	$aopt=splice(@names,$aopt,1);
  292: 	&Apache::lonxml::debug("Picked $aopt");
  293: 	push(@whichopt,$aopt);
  294:     }
  295:     return @whichopt;
  296: }
  297: 
  298: sub prep_image {
  299:     my ($image,$mode,$name)=@_;
  300: 
  301:     my ($x,$y)= &get_submission($name);
  302:     &Apache::lonxml::debug("for $name drawing click at $x and $y");
  303:     &draw_image($mode,$image,$x,$y,$Apache::response::foilgroup{"$name.area"});
  304: }
  305: 
  306: sub draw_image {
  307:     my ($mode,$image,$x,$y,$areas) = @_;
  308: 
  309:     my $id=&Apache::loncommon::get_cgi_id();
  310: 
  311:     my (%x,$i);
  312:     $x{"cgi.$id.BGIMG"}=&escape($image);
  313: 
  314:     #draws 2 xs on the image at the clicked location
  315:     #one in white and then one in red on top of the one in white
  316: 
  317:     if (defined($x)    && $x =~/\S/ 
  318: 	&& defined($y) && $y =~/\S/ 
  319: 	&& ($mode eq 'submission' || !&Apache::response::show_answer())
  320: 	&& $mode ne 'answeronly') {
  321: 	my $length = 6;
  322: 	my $width = 1;
  323: 	my $extrawidth = 2;
  324: 	my $xmin=($x-$length);
  325: 	my $xmax=($x+$length); 
  326: 	my $ymin=($y-$length);
  327: 	my $ymax=($y+$length);
  328: 
  329: 	$x{"cgi.$id.OBJTYPE"}.='LINE:';
  330: 	$i=$x{"cgi.$id.OBJCOUNT"}++;
  331: 	$x{"cgi.$id.OBJ$i"}=join(':',(($xmin),($ymin),($xmax),($ymax),
  332: 				      "FFFFFF",($width+$extrawidth)));
  333: 	$x{"cgi.$id.OBJTYPE"}.='LINE:';
  334: 	$i=$x{"cgi.$id.OBJCOUNT"}++;
  335: 	$x{"cgi.$id.OBJ$i"}=join(':',(($xmin),($ymax),($xmax),($ymin),
  336: 				      "FFFFFF",($width+$extrawidth)));
  337: 	$x{"cgi.$id.OBJTYPE"}.='LINE:';
  338: 	$i=$x{"cgi.$id.OBJCOUNT"}++;
  339: 	$x{"cgi.$id.OBJ$i"}=join(':',(($xmin),($ymin),($xmax),($ymax),
  340: 				      "FF0000",($width)));
  341: 	$x{"cgi.$id.OBJTYPE"}.='LINE:';
  342: 	$i=$x{"cgi.$id.OBJCOUNT"}++;
  343: 	$x{"cgi.$id.OBJ$i"}=join(':',(($xmin),($ymax),($xmax),($ymin),
  344: 				      "FF0000",($width)));
  345:     }
  346:     if ($mode eq 'answer' || $mode eq 'answeronly') {
  347: 	my $width = 1;
  348: 	my $extrawidth = 2;
  349: 	foreach my $area (@{ $areas }) {
  350: 	    if ($area=~/^rectangle:/) {
  351: 		$x{"cgi.$id.OBJTYPE"}.='RECTANGLE:';
  352: 		$i=$x{"cgi.$id.OBJCOUNT"}++;
  353: 		my ($x1,$y1,$x2,$y2)=
  354: 		    ($area=~m/rectangle:\(([0-9]+),([0-9]+)\)\-\(([0-9]+),([0-9]+)\)/);
  355: 		$x{"cgi.$id.OBJ$i"}=join(':',($x1,$y1,$x2,$y2,"FFFFFF",
  356: 					      ($width+$extrawidth)));
  357: 		$x{"cgi.$id.OBJTYPE"}.='RECTANGLE:';
  358: 		$i=$x{"cgi.$id.OBJCOUNT"}++;
  359: 		$x{"cgi.$id.OBJ$i"}=join(':',($x1,$y1,$x2,$y2,"00FF00",$width));
  360: 	    } elsif ($area=~/^polygon:(.*)/) {
  361: 		$x{"cgi.$id.OBJTYPE"}.='POLYGON:';
  362: 		$i=$x{"cgi.$id.OBJCOUNT"}++;
  363: 		$x{"cgi.$id.OBJ$i"}=join(':',("FFFFFF",($width+$extrawidth)));
  364: 		$x{"cgi.$id.OBJEXTRA$i"}=$1;
  365: 		$x{"cgi.$id.OBJTYPE"}.='POLYGON:';
  366: 		$i=$x{"cgi.$id.OBJCOUNT"}++;
  367: 		$x{"cgi.$id.OBJ$i"}=join(':',("00FF00",$width));
  368: 		$x{"cgi.$id.OBJEXTRA$i"}=$1;
  369: 	    }
  370: 	}
  371:     }
  372:     &Apache::lonnet::appenv(\%x);
  373:     return $id;
  374: }
  375: 
  376: sub displayfoils {
  377:     my ($target,@whichopt) = @_;
  378:     my $result ='';
  379:     my $temp=1;
  380:     my @images;
  381:     foreach my $name (@whichopt) {
  382: 	$result.=$Apache::response::foilgroup{"$name.text"};
  383: 	&Apache::lonxml::debug("Text is $result");
  384: 	if ($target eq 'tex') {$result.="\\vskip 0 mm \n";} else {$result.="<br />\n";}
  385: 	my $image=$Apache::response::foilgroup{"$name.image"};
  386: 	&Apache::lonxml::debug("image is $image");
  387: 	if ( ($target eq 'web' || $target eq 'answer') 
  388: 	     && $image !~ /^http:/ ) {
  389: 	    $image=&clean_up_image($image);
  390: 	}
  391: 	push(@images,$image);
  392: 	&Apache::lonxml::debug("image is $image");
  393: 	if ( &Apache::response::show_answer() ) {
  394: 	    if ($target eq 'tex') {
  395: 		$result.=$Apache::response::foilgroup{"$name.image"}."\\vskip 0 mm \n";
  396: 	    } else {
  397: 		my $token=&prep_image($image,'answer',$name);
  398: 		$result.="<img src=\"/adm/randomlabel.png?token=$token\" /><br />\n";
  399: 	    }
  400: 	} else {
  401: 	    if ($target eq 'tex') {
  402: 		$result.=$Apache::response::foilgroup{"$name.image"}."\\vskip 0 mm \n";
  403: 	    } else {
  404: 		my $respid=$Apache::inputtags::response['-1'];
  405: 		my $token=&prep_image($image,'submission',$name);
  406: 		my $input_id = "HWVAL_$respid:$temp";
  407: 		my $id = $env{'form.request.prefix'}.$input_id;
  408: 		$result.='<img onclick="image_response_click(\''.$id.'\',event);"
  409: 	                       src="/adm/randomlabel.png?token='.$token.'" 
  410:                                id="'.$id.'_imageresponse"
  411:                                 />'.
  412: 			       '<br />'.
  413: 			       '<input type="hidden" name="'.$input_id.'_token" value="'.$token.'" />'.
  414: 			       '<input type="hidden" name="'.$input_id.'" value="'.
  415: 			       join(':',&get_submission($name)).'" />';
  416: 	    }
  417: 	}
  418: 	$temp++;
  419:     }
  420:     if ($target eq 'web') {
  421: 	&Apache::response::setup_prior_tries_hash(\&format_prior_response,
  422: 						  [\@images,\@whichopt]);
  423:     }
  424:     return $result;
  425: }
  426: 
  427: sub format_prior_response {
  428:     my ($mode,$answer,$other_data) = @_;
  429:     
  430:     my $result;
  431: 
  432:     # make a copy of the data in the refs
  433:     my @images = @{ $other_data->[0] };
  434:     my @foils = @{ $other_data->[1] };
  435:     foreach my $name (@foils) {
  436: 	my $image = pop(@images);
  437: 	my ($x,$y) = &get_submission($name,$answer);
  438: 	my $token = &draw_image('submission',$image,$x,$y);
  439: 	$result .=
  440: 	    '<img class="LC_prior_image"
  441:                   src="/adm/randomlabel.png?token='.$token.'" /><br />';
  442:     }
  443:     return $result;
  444: }
  445: 
  446: sub display_answers {
  447:     my ($target,$whichopt)=@_;
  448: 
  449:     my $result=&Apache::response::answer_header('imageresponse');
  450:     foreach my $name (@$whichopt) {
  451: 	my $image=$Apache::response::foilgroup{"$name.image"};
  452: 	&Apache::lonxml::debug("image is $image");
  453: 	if ( ($target eq 'web' || $target eq 'answer')
  454: 	     && $image !~ /^http:/ ) {
  455: 	    $image = &clean_up_image($image);
  456: 	} 
  457: 	my $token=&prep_image($image,'answeronly',$name);
  458: 
  459: 	$result.=&Apache::response::answer_part('imageresponse',"<img src=\"/adm/randomlabel.png?token=$token\" /><br />\n");
  460:     }
  461:     $result.=&Apache::response::answer_footer('imageresponse');
  462:     return $result;
  463: }
  464: 
  465: sub clean_up_image {
  466:     my ($image)=@_;
  467:     if ($image =~ /\s*<img\s*/) {
  468: 	($image) = ($image =~ /src\s*=\s*[\"\']([^\"\']+)[\"\']/i);
  469: 	if ($image !~ /^http:/) {
  470: 	    $image=&Apache::lonnet::hreflocation('',$image);
  471: 	}
  472: 	if (!$image) {
  473: 	    $image='/home/httpd/html/adm/lonKaputt/lonlogo_broken.gif';
  474: 	}
  475:     } else {
  476: 	$image=&Apache::lonnet::filelocation($Apache::lonxml::pwd[-1],$image);
  477: 	&Apache::lonxml::debug("repcopying: $image");
  478: 	if (&Apache::lonnet::repcopy($image) ne 'ok') {
  479: 	    $image='/home/httpd/html/adm/lonKaputt/lonlogo_broken.gif';
  480: 	}
  481:     }
  482:     return $image;
  483: }
  484: 
  485: sub gradefoils {
  486:     my (@whichopt) = @_;
  487: 
  488:     my $partid = $Apache::inputtags::part;
  489:     my $id     = $Apache::inputtags::response['-1'];
  490: 
  491:     if ($Apache::lonhomework::type eq 'exam') {
  492: 	&Apache::response::scored_response($partid,$id);
  493: 	return;
  494:     }
  495:     
  496:     my @results;
  497:     my $temp=1;
  498:     my %response;
  499:     foreach my $name (@whichopt) {
  500: 	my ($x,$y) = split(':',$env{"form.HWVAL_$id:$temp"});
  501: 	$response{$name} = $env{"form.HWVAL_$id:$temp"};
  502: 	&Apache::lonxml::debug("Got a x of $x and a y of $y for $name");
  503: 	if (defined($x) && defined($y) &&
  504: 	    defined(@{ $Apache::response::foilgroup{"$name.area"} })) {
  505: 	    my @areas = @{ $Apache::response::foilgroup{"$name.area"} };
  506: 	    my $grade="INCORRECT";
  507: 	    foreach my $area (@areas) {
  508: 		&Apache::lonxml::debug("Area is $area for $name");
  509: 		$area =~ m/([a-z]*):/;
  510: 		&Apache::lonxml::debug("Area of type $1");
  511: 		if ($1 eq 'rectangle') {
  512: 		    $grade=&grade_rectangle($area,$x,$y);
  513: 		} elsif ($1 eq 'polygon') {
  514: 		    $grade=&grade_polygon($area,$x,$y);
  515: 		} else {
  516: 		    &Apache::lonxml::error(&mt('Unknown area style [_1]',$area));
  517: 		}
  518: 		&Apache::lonxml::debug("Area said $grade");
  519: 		if ($grade eq 'APPROX_ANS') { last; }
  520: 	    }
  521: 	    &Apache::lonxml::debug("Foil was $grade");
  522: 	    push(@results, $grade)
  523: 	} else {
  524: 	    push(@results, 'MISSING_ANSWER')
  525: 	}
  526: 	$temp++;
  527:     }
  528:     my ($result) = &Apache::inputtags::finalizeawards(\@results,[]);
  529:     &Apache::lonxml::debug("Question is $result");
  530: 
  531:     my $part=$Apache::inputtags::part;
  532:     my %previous=
  533: 	&Apache::response::check_for_previous(&stringify_submission(\%response),
  534: 					      $part,$id);
  535:     if ($result 
  536: 	&& $Apache::lonhomework::type eq 'survey') { $result='SUBMITTED'; }
  537:     &Apache::response::handle_previous(\%previous,$result);
  538:     $Apache::lonhomework::results{"resource.$part.$id.submission"}=
  539: 	&stringify_submission(\%response);
  540:     $Apache::lonhomework::results{"resource.$part.$id.awarddetail"}=$result;
  541:     return;
  542: }
  543: 
  544: sub stringify_submission {
  545:     my ($response) = @_;
  546:     return &Apache::lonnet::hash2str(%{ $response });
  547: 
  548:     
  549: }
  550: 
  551: sub get_submission {
  552:     my ($name,$string) = @_;
  553: 
  554:     if (!defined($string)) {
  555: 	my $part=$Apache::inputtags::part;
  556: 	my $respid=$Apache::inputtags::response['-1'];
  557:    	$string = 
  558: 	    $Apache::lonhomework::history{"resource.$part.$respid.submission"};
  559:     }
  560: 
  561:     if ($string !~ /=/) {
  562: 	return split(':',$string);
  563:     } else {
  564: 	my %response = &Apache::lonnet::str2hash($string);
  565: 	return split(':',$response{$name});
  566:     }
  567: }
  568: 
  569: sub end_foilgroup {
  570:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  571:     my $result='';
  572:     my @whichopt;
  573: 
  574:     if ($target eq 'web' || $target eq 'grade' || $target eq 'tex' ||
  575: 	$target eq 'analyze' || $target eq 'answer') {
  576: 
  577: 	my ($count,$max) = &getfoilcounts($parstack,$safeeval);
  578: 	if ($count>$max) { $count=$max }
  579: 	&Apache::lonxml::debug("Count is $count from $max");
  580: 
  581: 	@whichopt = &whichfoils($max);
  582: 
  583: 	if ($target eq 'web' || $target eq 'tex') {
  584: 	    $result=&displayfoils($target,@whichopt);
  585: 	    $Apache::lonxml::post_evaluate=0;
  586: 	} elsif ($target eq 'grade') {
  587: 	    if ( defined $env{'form.submitted'}) { &gradefoils(@whichopt); }
  588: 	} elsif ( $target eq 'analyze') {
  589: 	    &Apache::response::analyze_store_foilgroup(\@whichopt,
  590: 						      ['text','image','area']);
  591: 	} elsif ($target eq 'answer'
  592: 		 && $env{'form.answer_output_mode'} ne 'tex') {
  593: 	    $result=&display_answers($target,\@whichopt);
  594: 	}
  595: 
  596:     } elsif ($target eq 'edit') {
  597: 	$result=&Apache::edit::end_table();
  598:     }
  599:     &Apache::response::poprandomnumber();
  600:     return $result;
  601: }
  602: 
  603: sub start_conceptgroup {
  604:     $Apache::imageresponse::conceptgroup=1;
  605:     %Apache::response::conceptgroup=();
  606:     return '';
  607: }
  608: 
  609: sub end_conceptgroup {
  610:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  611:     $Apache::imageresponse::conceptgroup=0;
  612:     my $result;
  613:     if ($target eq 'web' || $target eq 'grade' || $target eq 'tex' ||
  614: 	$target eq 'analyze') {
  615: 	&Apache::response::pick_foil_for_concept($target,
  616: 						 ['area','text','image'],
  617: 						 \%Apache::hint::image,
  618: 						 $parstack,$safeeval);
  619:     } elsif ($target eq 'edit') {
  620: 	$result=&Apache::edit::end_table();
  621:     }
  622:     return $result;
  623: }
  624: 
  625: sub insert_foil {
  626:     return '
  627:        <foil>
  628:            <image></image>
  629:            <text></text>
  630:            <rectangle></rectangle>
  631:        </foil>
  632: ';
  633: }
  634: 
  635: $Apache::imageresponse::curname='';
  636: sub start_foil {
  637:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  638:     my $result;
  639:     if ($target eq 'web' || $target eq 'grade' || $target eq 'tex' ||
  640: 	$target eq 'analyze' || $target eq 'answer') {
  641: 	my $name = &Apache::lonxml::get_param('name',$parstack,$safeeval);
  642: 	if ($name eq "") {
  643: 	    &Apache::lonxml::warning(&mt('Foils without names exist. This can cause problems to malfunction.'));
  644: 	    $name=$Apache::lonxml::curdepth;
  645: 	}
  646: 	if (defined($Apache::response::foilnames{$name})) {
  647: 	    &Apache::lonxml::error(&mt("Foil name [_1] appears more than once. Foil names need to be unique."
  648:                                       ,'<b><tt>'.$name.'</tt></b>'));
  649: 	}
  650: 	$Apache::response::foilnames{$name}++;
  651: 	if ( $Apache::imageresponse::conceptgroup
  652: 	     && !&Apache::response::showallfoils()
  653: 	     ) {
  654: 	    push(@{ $Apache::response::conceptgroup{'names'} }, $name);
  655: 	} else {
  656: 	    push(@{ $Apache::response::foilgroup{'names'} }, $name);
  657: 	}
  658: 	$Apache::imageresponse::curname=$name;
  659:     } elsif ($target eq 'edit') {
  660: 	$result  = &Apache::edit::tag_start($target,$token);
  661: 	$result .= &Apache::edit::text_arg('Name:','name',$token);
  662: 	$result .= &Apache::edit::end_row().
  663: 	    &Apache::edit::start_spanning_row();
  664:     } elsif ($target eq 'modified') {
  665: 	my $constructtag=&Apache::edit::get_new_args($token,$parstack,
  666: 						     $safeeval,'name');
  667: 	if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
  668:     } 
  669:     return $result;;
  670: }
  671: 
  672: sub end_foil {
  673:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  674:     my $result;
  675:     if ($target eq 'edit') {
  676: 	$result=&Apache::edit::end_table();
  677:     }
  678:     return $result;
  679: }
  680: 
  681: sub start_text {
  682:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  683:     my $result='';
  684:     if ($target eq 'web' || $target eq 'tex' || $target eq 'analyze'
  685: 	|| $target eq 'answer') { 
  686: 	&Apache::lonxml::startredirection; 
  687:     } elsif ($target eq 'edit') {
  688: 	my $descr=&Apache::lonxml::get_all_text('/text',$parser,$style);
  689: 	$result=&Apache::edit::tag_start($target,$token,'Task Description').
  690: 	    &Apache::edit::editfield($token->[1],$descr,'Text',60,2).
  691: 	    &Apache::edit::end_row();
  692:     } elsif ($target eq "modified") {
  693: 	$result=$token->[4].&Apache::edit::modifiedfield('/text',$parser);
  694:     }
  695:     return $result;
  696: }
  697: 
  698: sub end_text {
  699:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  700:     my $result;
  701:     if ($target eq 'web' || $target eq 'tex' || $target eq 'analyze'
  702: 	|| $target eq 'answer') {
  703: 	my $name = $Apache::imageresponse::curname;
  704: 	if ( $Apache::imageresponse::conceptgroup
  705: 	     && !&Apache::response::showallfoils()
  706: 	     ) {
  707: 	    $Apache::response::conceptgroup{"$name.text"} = &Apache::lonxml::endredirection;
  708: 	} else {
  709: 	    $Apache::response::foilgroup{"$name.text"} = &Apache::lonxml::endredirection;
  710: 	}
  711:     } elsif ($target eq 'edit') {
  712: 	$result=&Apache::edit::end_table();
  713:     }
  714:     return $result;
  715: }
  716: 
  717: sub start_image {
  718:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  719:     my $result='';
  720:     my $only = join(',',&Apache::loncommon::filecategorytypes('Pictures'));
  721:     if ($target eq 'web' || $target eq 'tex' || $target eq 'analyze'
  722: 	|| $target eq 'answer') { 
  723: 	&Apache::lonxml::startredirection; 
  724:     } elsif ($target eq 'edit') {
  725: 	my $bgimg=&Apache::lonxml::get_all_text('/image',$parser,$style);
  726: 	$Apache::edit::bgimgsrc=$bgimg;
  727: 	$Apache::edit::bgimgsrcdepth=$Apache::lonxml::curdepth;
  728: 
  729: 	$result=&Apache::edit::tag_start($target,$token,'Clickable Image').
  730: 	    &Apache::edit::editline($token->[1],$bgimg,'Image Source File',40);
  731: 	$result.=&Apache::edit::browse(undef,'textnode',undef,$only).' ';
  732: 	$result.=&Apache::edit::search(undef,'textnode').
  733: 	    &Apache::edit::end_row();
  734:     } elsif ($target eq "modified") {
  735: 	$result=$token->[4].&Apache::edit::modifiedfield('/image',$parser);
  736:     }
  737:     return $result;
  738: }
  739: 
  740: sub end_image {
  741:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  742:     my $result;
  743:     my $name = $Apache::imageresponse::curname;
  744:     if ($target eq 'web' || $target eq 'answer') {
  745: 	my $image = &Apache::lonxml::endredirection();
  746: 	&Apache::lonxml::debug("original image is $image");
  747: 	if ( $Apache::imageresponse::conceptgroup
  748: 	     && !&Apache::response::showallfoils()
  749: 	     ) {
  750: 	    $Apache::response::conceptgroup{"$name.image"} = $image;
  751: 	} else {
  752: 	    $Apache::response::foilgroup{"$name.image"} = $image;
  753: 	}
  754:     } elsif ($target eq 'analyze') {
  755: 	my $image = &Apache::lonxml::endredirection();
  756: 	if ( $Apache::imageresponse::conceptgroup
  757: 	     && !&Apache::response::showallfoils()
  758: 	     ) {
  759: 	    $Apache::response::conceptgroup{"$name.image"} = $image;
  760: 	} else {
  761: 	    $Apache::response::foilgroup{"$name.image"} = $image;
  762: 	}
  763:     } elsif ($target eq 'edit') {
  764: 	$result=&Apache::edit::end_table();
  765:     } elsif ($target eq 'tex') {
  766: 	my $src = &Apache::lonxml::endredirection();
  767: 
  768: 	#  There may be all sorts of whitespace on fore and aft:
  769: 
  770: 	$src =~ s/\s+$//s;
  771: 	$src =~ s/^\s+//s;
  772: 
  773: 	#
  774: 	#  Gnuplot e.g. just generates the latex to put inplace.
  775: 	#
  776: 	my $graphinclude;
  777: 	if ($src =~ /^%DYNAMICIMAGE/) {
  778: 	    # This is needed because the newline is not always passed -> tex.
  779: 	    # At present we don't care about the sizing info.
  780: 
  781: 	    my ($commentline, $restofstuff) = split(/\n/, $src);
  782: 	    $graphinclude = $src;
  783: 	    $graphinclude =~ s/^$commentline//;
  784: 	} else {
  785: 	    my ($path,$file) = &Apache::londefdef::get_eps_image($src);
  786: 	    my ($height_param,$width_param)=
  787: 		&Apache::londefdef::image_size($src,0.3,$parstack,$safeeval);
  788: 	    $graphinclude = '\graphicspath{{'.$path.'}}\includegraphics[width='.$width_param.' mm]{'.$file.'}';
  789: 	}
  790: 	$Apache::response::foilgroup{"$name.image"} ='\vskip 0 mm \noindent '.$graphinclude;
  791:     } 
  792:     return $result;
  793: }
  794: 
  795: sub start_rectangle {
  796:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  797:     my $result='';
  798:     if ($target eq 'web' || $target eq 'grade' || $target eq 'tex' ||
  799: 	$target eq 'analyze' || $target eq 'answer') { 
  800: 	&Apache::lonxml::startredirection; 
  801:     } elsif ($target eq 'edit') {
  802: 	my $coords=&Apache::lonxml::get_all_text('/rectangle',$parser,$style);
  803: 	$result=&Apache::edit::tag_start($target,$token,'Rectangle').
  804: 	    &Apache::edit::editline($token->[1],$coords,'Coordinate Pairs',40).
  805: 	    &Apache::edit::entercoord(undef,'textnode',undef,undef,'box').
  806: 	    &Apache::edit::end_row();
  807:     } elsif ($target eq "modified") {
  808: 	&Apache::edit::deletecoorddata();
  809: 	$result=$token->[4].&Apache::edit::modifiedfield('/rectangle',$parser);
  810:     }
  811:     return $result;
  812: }
  813: 
  814: sub grade_rectangle {
  815:     my ($spec,$x,$y) = @_;
  816:     &Apache::lonxml::debug("Spec is $spec");
  817:     my ($x1,$y1,$x2,$y2)=($spec=~m/rectangle:\(([0-9]+),([0-9]+)\)\-\(([0-9]+),([0-9]+)\)/);
  818:     &Apache::lonxml::debug("Point $x1,$y1,$x2,$y2");
  819:     if ($x1 > $x2) { my $temp=$x1;$x1=$x2;$x2=$temp; }
  820:     if ($y1 > $y2) { my $temp=$y1;$y1=$y2;$y2=$temp; }
  821:     if (($x >= $x1) && ($x <= $x2) && ($y >= $y1) && ($y <= $y2)) {
  822: 	return 'APPROX_ANS';
  823:     }
  824:     return 'INCORRECT';
  825: }
  826: 
  827: sub end_rectangle {
  828:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  829:     my $result;
  830:     if ($target eq 'web' || $target eq 'grade' || $target eq 'tex' ||
  831: 	$target eq 'analyze' || $target eq 'answer') {
  832: 	my $name = $Apache::imageresponse::curname;
  833: 	my $area = &Apache::lonxml::endredirection;
  834: 	$area=~s/\s//g;
  835: 	&Apache::lonxml::debug("out is $area for $name");
  836: 	if ( $Apache::imageresponse::conceptgroup
  837: 	     && !&Apache::response::showallfoils()
  838: 	     ) {
  839: 	    push @{ $Apache::response::conceptgroup{"$name.area"} },"rectangle:$area";
  840: 	} else {
  841: 	    push @{ $Apache::response::foilgroup{"$name.area"} },"rectangle:$area";
  842: 	}
  843:     } elsif ($target eq 'edit') {
  844: 	$result=&Apache::edit::end_table();
  845:     }
  846:     return $result;
  847: }
  848: 
  849: sub start_polygon {
  850:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  851:     my $result='';
  852:     if ($target eq 'web' || $target eq 'grade' || $target eq 'tex' ||
  853: 	$target eq 'analyze' || $target eq 'answer') { 
  854: 	&Apache::lonxml::startredirection; 
  855:     } elsif ($target eq 'edit') {
  856: 	my $coords=&Apache::lonxml::get_all_text('/polygon',$parser,$style);
  857: 	$result=&Apache::edit::tag_start($target,$token,'Polygon').
  858: 	    &Apache::edit::editline($token->[1],$coords,'Coordinate list',40).
  859: 	    &Apache::edit::entercoord(undef,'textnode',undef,undef,'polygon').
  860: 	    &Apache::edit::end_row();
  861:     } elsif ($target eq "modified") {
  862: 	$result=$token->[4].&Apache::edit::modifiedfield('/polygon',$parser);
  863:     }
  864:     return $result;
  865: }
  866: 
  867: sub grade_polygon {
  868:     my ($spec,$x,$y) = @_;
  869:     &Apache::lonxml::debug("Spec is $spec");
  870:     $spec=~s/^polygon://;
  871:     my @polygon;
  872:     foreach my $coord (split('-',$spec)) {
  873: 	my ($x,$y)=($coord=~m/\(([0-9]+),([0-9]+)\)/);
  874: 	&Apache::lonxml::debug("x $x y $y");
  875: 	push @polygon, {'x'=>$x,'y'=>$y};
  876:     }
  877:     #make end point start point
  878:     push @polygon, $polygon[0];
  879:     # cribbed from
  880:     # http://geometryalgorithms.com/Archive/algorithm_0103/algorithm_0103.htm
  881:     my $crossing = 0;    # the crossing number counter
  882: 
  883:     # loop through all edges of the polygon
  884:     for (my $i=0; $i<$#polygon; $i++) {    # edge from V[i] to V[i+1]
  885: 	if ((($polygon[$i]->{'y'} <= $y)
  886: 	     && ($polygon[$i+1]->{'y'} > $y))    # an upward crossing
  887: 	    || 
  888: 	    (($polygon[$i]->{'y'} > $y) 
  889: 	     && ($polygon[$i+1]->{'y'} <= $y))) { # a downward crossing
  890: 	    # compute the actual edge-ray intersect x-coordinate
  891:             my $vt = ($y - $polygon[$i]->{'y'}) 
  892: 		/ ($polygon[$i+1]->{'y'} - $polygon[$i]->{'y'});
  893:             if ($x < $polygon[$i]->{'x'} + $vt * 
  894: 		($polygon[$i+1]->{'x'} - $polygon[$i]->{'x'})) { # x<intersect
  895:                 $crossing++;   # a valid crossing of y=P.y right of P.x
  896: 	    }
  897: 	}
  898:     }
  899: 
  900:     # 0 if even (out), and 1 if odd (in)
  901:     if ($crossing%2) {
  902: 	return 'APPROX_ANS';
  903:     } else {
  904: 	return 'INCORRECT';
  905:     }
  906: }
  907: 
  908: sub end_polygon {
  909:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  910:     my $result;
  911:     if ($target eq 'web' || $target eq 'grade' || $target eq 'tex' ||
  912: 	$target eq 'analyze' || $target eq 'answer') {
  913: 	my $name = $Apache::imageresponse::curname;
  914: 	my $area = &Apache::lonxml::endredirection;
  915: 	$area=~s/\s*//g;
  916: 	&Apache::lonxml::debug("out is $area for $name");
  917: 	if ( $Apache::imageresponse::conceptgroup
  918: 	     && !&Apache::response::showallfoils()
  919: 	     ) {
  920: 	    push @{ $Apache::response::conceptgroup{"$name.area"} },"polygon:$area";
  921: 	} else {
  922: 	    push @{ $Apache::response::foilgroup{"$name.area"} },"polygon:$area";
  923: 	}
  924:     } elsif ($target eq 'edit') {
  925: 	$result=&Apache::edit::end_table();
  926:     }
  927:     return $result;
  928: }
  929: 1;
  930: __END__
  931:  

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>