File:  [LON-CAPA] / loncom / xml / lonplot.pm
Revision 1.96: download - view: text, annotated - select for diffs
Wed May 26 19:44:20 2004 UTC (19 years, 11 months ago) by matthew
Branches: MAIN
CVS tags: version_1_1_99_0, HEAD
Removed help text and routines in favor of using the insertlist.tab routines.

    1: # The LearningOnline Network with CAPA
    2: # Dynamic plot
    3: #
    4: # $Id: lonplot.pm,v 1.96 2004/05/26 19:44:20 matthew 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::lonplot;
   30: 
   31: use strict;
   32: use warnings FATAL=>'all';
   33: no warnings 'uninitialized';
   34: use Apache::File;
   35: use Apache::response;
   36: use Apache::lonxml;
   37: use Apache::edit;
   38: 
   39: BEGIN {
   40:   &Apache::lonxml::register('Apache::lonplot',('gnuplot'));
   41: }
   42: 
   43: ## 
   44: ## Description of data structures:
   45: ##
   46: ##  %plot       %key    %axis
   47: ## --------------------------
   48: ##  height      title   color
   49: ##  width       box     xmin
   50: ##  bgcolor     pos     xmax
   51: ##  fgcolor             ymin
   52: ##  transparent         ymax
   53: ##  grid
   54: ##  border
   55: ##  font
   56: ##  align
   57: ##
   58: ##  @labels: $labels[$i] = \%label
   59: ##           %label: text, xpos, ypos, justify
   60: ##
   61: ##  @curves: $curves[$i] = \%curve
   62: ##           %curve: name, linestyle, ( function | data )
   63: ##
   64: ##  $curves[$i]->{'data'} = [ [x1,x2,x3,x4],
   65: ##                            [y1,y2,y3,y4] ]
   66: ##
   67: 
   68: ###################################################################
   69: ##                                                               ##
   70: ##        Tests used in checking the validitity of input         ##
   71: ##                                                               ##
   72: ###################################################################
   73: 
   74: my $max_str_len = 50;    # if a label, title, xlabel, or ylabel text
   75:                          # is longer than this, it will be truncated.
   76: 
   77: my %linestyles = 
   78:     (
   79:      lines          => 2,     # Maybe this will be used in the future
   80:      linespoints    => 2,     # to check on whether or not they have 
   81:      dots	    => 2,     # supplied enough <data></data> fields
   82:      points         => 2,     # to use the given line style.  But for
   83:      steps	    => 2,     # now there are more important things 
   84:      fsteps	    => 2,     # for me to deal with.
   85:      histeps        => 2,
   86:      errorbars	    => 3,
   87:      xerrorbars	    => [3,4],
   88:      yerrorbars	    => [3,4],
   89:      xyerrorbars    => [4,6],
   90:      boxes          => 3,
   91:      vector	    => 4
   92:     );		    
   93: 
   94: my $int_test       = sub {$_[0]=~s/\s+//g;$_[0]=~/^\d+$/};
   95: my $real_test      = 
   96:     sub {$_[0]=~s/\s+//g;$_[0]=~/^[+-]?\d*\.?\d*([eE][+-]\d+)?$/};
   97: my $pos_real_test  =
   98:     sub {$_[0]=~s/\s+//g;$_[0]=~/^[+]?\d*\.?\d*([eE][+-]\d+)?$/};
   99: my $color_test     = sub {$_[0]=~s/\s+//g;$_[0]=~/^x[\da-fA-F]{6}$/};
  100: my $onoff_test     = sub {$_[0]=~/^(on|off)$/};
  101: my $key_pos_test   = sub {$_[0]=~/^(top|bottom|right|left|outside|below| )+$/};
  102: my $sml_test       = sub {$_[0]=~/^(small|medium|large)$/};
  103: my $linestyle_test = sub {exists($linestyles{$_[0]})};
  104: my $words_test     = sub {$_[0]=~s/\s+/ /g;$_[0]=~/^([\w~!\@\#\$\%^&\*\(\)-=_\+\[\]\{\}:\;\'<>,\.\/\?\\]+ ?)+$/};
  105: 
  106: ###################################################################
  107: ##                                                               ##
  108: ##                      Attribute metadata                       ##
  109: ##                                                               ##
  110: ###################################################################
  111: my @gnuplot_edit_order = 
  112:     qw/alttag bgcolor fgcolor height width font transparent grid samples 
  113:     border align texwidth texfont plottype/;
  114: 
  115: my %gnuplot_defaults = 
  116:     (
  117:      alttag       => {
  118: 	 default     => 'dynamically generated plot',
  119: 	 test        => $words_test,
  120: 	 description => 'brief description of the plot',
  121:       	 edit_type   => 'entry',
  122: 	 size        => '40'
  123: 	 },
  124:      height       => {
  125: 	 default     => 300,
  126: 	 test        => $int_test,
  127: 	 description => 'height of image (pixels)',
  128:       	 edit_type   => 'entry',
  129: 	 size        => '10'
  130: 	 },
  131:      width        => {
  132: 	 default     => 400,
  133: 	 test        => $int_test,
  134: 	 description => 'width of image (pixels)',
  135: 	 edit_type   => 'entry',
  136: 	 size        => '10'
  137: 	 },
  138:      bgcolor      => {
  139: 	 default     => 'xffffff',
  140: 	 test        => $color_test, 
  141: 	 description => 'background color of image (xffffff)',
  142: 	 edit_type   => 'entry',
  143: 	 size        => '10'
  144: 	 },
  145:      fgcolor      => {
  146: 	 default     => 'x000000',
  147: 	 test        => $color_test,
  148: 	 description => 'foreground color of image (x000000)',
  149: 	 edit_type   => 'entry',
  150: 	 size        => '10'
  151: 	 },
  152:      transparent  => {
  153: 	 default     => 'off',
  154: 	 test        => $onoff_test, 
  155: 	 description => 'Transparent image',
  156: 	 edit_type   => 'onoff'
  157: 	 },
  158:      grid         => {
  159: 	 default     => 'on',
  160: 	 test        => $onoff_test, 
  161: 	 description => 'Display grid',
  162: 	 edit_type   => 'onoff'
  163: 	 },
  164:      border       => {
  165: 	 default     => 'on',
  166: 	 test        => $onoff_test, 
  167: 	 description => 'Draw border around plot',
  168: 	 edit_type   => 'onoff'
  169: 	 },
  170:      font         => {
  171: 	 default     => 'medium',
  172: 	 test        => $sml_test,
  173: 	 description => 'Size of font to use',
  174: 	 edit_type   => 'choice',
  175: 	 choices     => ['small','medium','large']
  176: 	 },
  177:      samples         => {
  178: 	 default     => '100',
  179: 	 test        => $int_test,
  180: 	 description => 'Number of samples for non-data plots',
  181: 	 edit_type   => 'choice',
  182: 	 choices     => ['100','200','500','1000','2000','5000']
  183: 	 },
  184:      align        => {
  185: 	 default     => 'center',
  186: 	 test        => sub {$_[0]=~/^(left|right|center)$/},
  187: 	 description => 'alignment for image in html',
  188: 	 edit_type   => 'choice',
  189: 	 choices     => ['left','right','center']
  190: 	 },
  191:      texwidth     => {
  192:          default     => '93',
  193:          test        => $int_test,
  194:          description => 'Width of plot when printed (mm)',
  195:          edit_type   => 'entry',
  196:          size        => '5'
  197:          },
  198:      texfont     => {
  199:          default     => '22',
  200:          test        => $int_test,
  201:          description => 'Font size to use in TeX output (pts):',
  202:          edit_type   => 'choice',
  203:          choices     => [qw/8 10 12 14 16 18 20 22 24 26 28 30 32 34 36/],
  204:          },
  205:      plottype  => {
  206: 	 default     => 'Cartesian',
  207: 	 test        => sub {$_[0]=~/^(Polar|Cartesian)$/},
  208: 	 description => 'Plot type:',
  209: 	 edit_type   => 'choice',
  210:          choices     => ['Cartesian','Polar']
  211:          },
  212:      );
  213: 
  214: my %key_defaults = 
  215:     (
  216:      title => { 
  217: 	 default => '',
  218: 	 test => $words_test,
  219: 	 description => 'Title of key',
  220: 	 edit_type   => 'entry',
  221: 	 size        => '40'
  222: 	 },
  223:      box   => { 
  224: 	 default => 'off',
  225: 	 test => $onoff_test,
  226: 	 description => 'Draw a box around the key?',
  227: 	 edit_type   => 'onoff'
  228: 	 },
  229:      pos   => { 
  230: 	 default => 'top right', 
  231: 	 test => $key_pos_test, 
  232: 	 description => 'position of the key on the plot',
  233: 	 edit_type   => 'choice',
  234: 	 choices     => ['top left','top right','bottom left','bottom right',
  235: 			 'outside','below']
  236: 	 }
  237:      );
  238: 
  239: my %label_defaults = 
  240:     (
  241:      xpos    => {
  242: 	 default => 0,
  243: 	 test => $real_test,
  244: 	 description => 'x position of label (graph coordinates)',
  245: 	 edit_type   => 'entry',
  246: 	 size        => '10'
  247: 	 },
  248:      ypos    => {
  249: 	 default => 0, 
  250: 	 test => $real_test,
  251: 	 description => 'y position of label (graph coordinates)',
  252: 	 edit_type   => 'entry',
  253: 	 size        => '10'
  254: 	 },
  255:      justify => {
  256: 	 default => 'left',    
  257: 	 test => sub {$_[0]=~/^(left|right|center)$/},
  258: 	 description => 'justification of the label text on the plot',
  259: 	 edit_type   => 'choice',
  260: 	 choices     => ['left','right','center']
  261:      }
  262:      );
  263: 
  264: my @tic_edit_order = ('location','mirror','start','increment','end',
  265:                       'minorfreq');
  266: my %tic_defaults =
  267:     (
  268:      location => {
  269: 	 default => 'border', 
  270: 	 test => sub {$_[0]=~/^(border|axis)$/},
  271: 	 description => 'Location of major tic marks',
  272: 	 edit_type   => 'choice',
  273: 	 choices     => ['border','axis']
  274: 	 },
  275:      mirror => {
  276: 	 default => 'on', 
  277: 	 test => $onoff_test,
  278: 	 description => 'mirror tics on opposite axis?',
  279: 	 edit_type   => 'onoff'
  280: 	 },
  281:      start => {
  282: 	 default => '-10.0',
  283: 	 test => $real_test,
  284: 	 description => 'Start major tics at',
  285: 	 edit_type   => 'entry',
  286: 	 size        => '10'
  287: 	 },
  288:      increment => {
  289: 	 default => '1.0',
  290: 	 test => $real_test,
  291: 	 description => 'Place a major tic every',
  292: 	 edit_type   => 'entry',
  293: 	 size        => '10'
  294: 	 },
  295:      end => {
  296: 	 default => ' 10.0',
  297: 	 test => $real_test,
  298: 	 description => 'Stop major tics at ',
  299: 	 edit_type   => 'entry',
  300: 	 size        => '10'
  301: 	 },
  302:      minorfreq => {
  303: 	 default => '0',
  304: 	 test => $int_test,
  305: 	 description => 'Number of minor tics between major tic marks',
  306: 	 edit_type   => 'entry',
  307: 	 size        => '10'
  308: 	 },         
  309:      );
  310: 
  311: my @axis_edit_order = ('color','xmin','xmax','ymin','ymax');
  312: my %axis_defaults = 
  313:     (
  314:      color   => {
  315: 	 default => 'x000000', 
  316: 	 test => $color_test,
  317: 	 description => 'color of grid lines (x000000)',
  318: 	 edit_type   => 'entry',
  319: 	 size        => '10'
  320: 	 },
  321:      xmin      => {
  322: 	 default => '-10.0',
  323: 	 test => $real_test,
  324: 	 description => 'minimum x-value shown in plot',
  325: 	 edit_type   => 'entry',
  326: 	 size        => '10'
  327: 	 },
  328:      xmax      => {
  329: 	 default => ' 10.0',
  330: 	 test => $real_test,
  331: 	 description => 'maximum x-value shown in plot',	 
  332: 	 edit_type   => 'entry',
  333: 	 size        => '10'
  334: 	 },
  335:      ymin      => {
  336: 	 default => '-10.0',
  337: 	 test => $real_test,
  338: 	 description => 'minimum y-value shown in plot',	 
  339: 	 edit_type   => 'entry',
  340: 	 size        => '10'
  341: 	 },
  342:      ymax      => {
  343: 	 default => ' 10.0',
  344: 	 test => $real_test,
  345: 	 description => 'maximum y-value shown in plot',	 
  346: 	 edit_type   => 'entry',
  347: 	 size        => '10'
  348: 	 }
  349:      );
  350: 
  351: my @curve_edit_order = ('color','name','linestyle','pointtype','pointsize');
  352: 
  353: my %curve_defaults = 
  354:     (
  355:      color     => {
  356: 	 default => 'x000000',
  357: 	 test => $color_test,
  358: 	 description => 'color of curve (x000000)',
  359: 	 edit_type   => 'entry',
  360: 	 size        => '10'
  361: 	 },
  362:      name      => {
  363: 	 default => '',
  364: 	 test => $words_test,
  365: 	 description => 'name of curve to appear in key',
  366: 	 edit_type   => 'entry',
  367: 	 size        => '20'
  368: 	 },
  369:      linestyle => {
  370: 	 default => 'lines',
  371: 	 test => $linestyle_test,
  372: 	 description => 'Line style',
  373: 	 edit_type   => 'choice',
  374: 	 choices     => [keys(%linestyles)]
  375: 	 },
  376: # gnuplots term=gif driver does not handle linewidth :(
  377: #     linewidth => {
  378: #         default     => 1,
  379: #         test        => $int_test,
  380: #         description => 'Line width (may not apply to all line styles)',
  381: #         edit_type   => 'choice',
  382: #         choices     => [1,2,3,4,5,6,7,8,9,10]
  383: #         },
  384:      pointsize => {
  385:          default     => 1,
  386:          test        => $pos_real_test,
  387:          description => 'point size (may not apply to all line styles)',
  388:          edit_type   => 'entry',
  389:          size        => '5'
  390:          },
  391:      pointtype => {
  392:          default     => 1,
  393:          test        => $int_test,
  394:          description => 'point type (may not apply to all line styles)',
  395:          edit_type   => 'choice',
  396:          choices     => [0,1,2,3,4,5,6]
  397:          }
  398:      );
  399: 
  400: ###################################################################
  401: ##                                                               ##
  402: ##                    parsing and edit rendering                 ##
  403: ##                                                               ##
  404: ###################################################################
  405: my (%plot,%key,%axis,$title,$xlabel,$ylabel,@labels,@curves,%xtics,%ytics);
  406: 
  407: sub start_gnuplot {
  408:     undef(%plot);   undef(%key);    undef(%axis);
  409:     undef($title);  undef($xlabel); undef($ylabel);
  410:     undef(@labels); undef(@curves);
  411:     undef(%xtics);  undef(%ytics);
  412:     #
  413:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  414:     my $result='';
  415:     &Apache::lonxml::register('Apache::lonplot',
  416: 	     ('title','xlabel','ylabel','key','axis','label','curve',
  417: 	      'xtics','ytics'));
  418:     push (@Apache::lonxml::namespace,'lonplot');
  419:     if ($target eq 'web' || $target eq 'tex') {
  420: 	&get_attributes(\%plot,\%gnuplot_defaults,$parstack,$safeeval,
  421: 			$tagstack->[-1]);
  422:     } elsif ($target eq 'edit') {
  423: 	$result .= &Apache::edit::tag_start($target,$token,'GnuPlot');
  424: 	$result .= &edit_attributes($target,$token,\%gnuplot_defaults,
  425: 				    \@gnuplot_edit_order);
  426:     } elsif ($target eq 'modified') {
  427: 	my $constructtag=&Apache::edit::get_new_args
  428: 	    ($token,$parstack,$safeeval,keys(%gnuplot_defaults));
  429: 	if ($constructtag) {
  430: 	    $result = &Apache::edit::rebuild_tag($token);
  431: 	}
  432:     }
  433:     return $result;
  434: }
  435: 
  436: sub end_gnuplot {
  437:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  438:     pop @Apache::lonxml::namespace;
  439:     &Apache::lonxml::deregister('Apache::lonplot',
  440: 	('title','xlabel','ylabel','key','axis','label','curve'));
  441:     my $result = '';
  442:     my $randnumber;
  443:     # need to call rand everytime start_script would evaluate, as the
  444:     # safe space rand number generator and the global rand generator 
  445:     # are not separate
  446:     if ($target eq 'web' || $target eq 'tex' || $target eq 'grade' ||
  447: 	$target eq 'answer') {
  448:       $randnumber=int(rand(1000));
  449:     }
  450:     if ($target eq 'web' || $target eq 'tex') {
  451: 	&check_inputs(); # Make sure we have all the data we need
  452: 	##
  453: 	## Determine filename
  454: 	my $tmpdir = '/home/httpd/perl/tmp/';
  455: 	my $filename = $ENV{'user.name'}.'_'.$ENV{'user.domain'}.
  456: 	    '_'.time.'_'.$$.$randnumber.'_plot';
  457: 	## Write the plot description to the file
  458: 	&write_gnuplot_file($tmpdir,$filename,$target);
  459: 	$filename = &Apache::lonnet::escape($filename);
  460: 	## return image tag for the plot
  461: 	if ($target eq 'web') {
  462: 	    $result .= <<"ENDIMAGE";
  463: <img src    = "/cgi-bin/plot.gif?file=$filename.data&output=gif" 
  464:      width  = "$plot{'width'}"
  465:      height = "$plot{'height'}"
  466:      align  = "$plot{'align'}"
  467:      alt    = "$plot{'alttag'}" />
  468: ENDIMAGE
  469:         } elsif ($target eq 'tex') {
  470: 	    #might be inside the safe space, register the URL for later
  471: 	    &Apache::lonxml::register_ssi("/cgi-bin/plot.gif?file=$filename.data&output=eps");
  472: 	    $result = '\graphicspath{{/home/httpd/perl/tmp/}}\includegraphics[width='.$plot{'texwidth'}.' mm]{'.&Apache::lonnet::unescape($filename).'.eps}';
  473: 	}
  474:     } elsif ($target eq 'edit') {
  475: 	$result.=&Apache::edit::tag_end($target,$token);
  476:     }
  477:     return $result;
  478: }
  479: 
  480: 
  481: ##--------------------------------------------------------------- xtics
  482: sub start_xtics {
  483:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  484:     my $result='';
  485:     if ($target eq 'web' || $target eq 'tex') {
  486: 	&get_attributes(\%xtics,\%tic_defaults,$parstack,$safeeval,
  487: 		    $tagstack->[-1]);
  488:     } elsif ($target eq 'edit') {
  489: 	$result .= &Apache::edit::tag_start($target,$token,'xtics');
  490: 	$result .= &edit_attributes($target,$token,\%tic_defaults,
  491: 				    \@tic_edit_order);
  492:     } elsif ($target eq 'modified') {
  493: 	my $constructtag=&Apache::edit::get_new_args
  494: 	    ($token,$parstack,$safeeval,keys(%tic_defaults));
  495: 	if ($constructtag) {
  496: 	    $result = &Apache::edit::rebuild_tag($token);
  497: 	}
  498:     }
  499:     return $result;
  500: }
  501: 
  502: sub end_xtics {
  503:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  504:     my $result = '';
  505:     if ($target eq 'web' || $target eq 'tex') {
  506:     } elsif ($target eq 'edit') {
  507: 	$result.=&Apache::edit::tag_end($target,$token);
  508:     }
  509:     return $result;
  510: }
  511: 
  512: ##--------------------------------------------------------------- ytics
  513: sub start_ytics {
  514:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  515:     my $result='';
  516:     if ($target eq 'web' || $target eq 'tex') {
  517: 	&get_attributes(\%ytics,\%tic_defaults,$parstack,$safeeval,
  518: 		    $tagstack->[-1]);
  519:     } elsif ($target eq 'edit') {
  520: 	$result .= &Apache::edit::tag_start($target,$token,'ytics');
  521: 	$result .= &edit_attributes($target,$token,\%tic_defaults,
  522: 				    \@tic_edit_order);
  523:     } elsif ($target eq 'modified') {
  524: 	my $constructtag=&Apache::edit::get_new_args
  525: 	    ($token,$parstack,$safeeval,keys(%tic_defaults));
  526: 	if ($constructtag) {
  527: 	    $result = &Apache::edit::rebuild_tag($token);
  528: 	}
  529:     }
  530:     return $result;
  531: }
  532: 
  533: sub end_ytics {
  534:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  535:     my $result = '';
  536:     if ($target eq 'web' || $target eq 'tex') {
  537:     } elsif ($target eq 'edit') {
  538: 	$result.=&Apache::edit::tag_end($target,$token);
  539:     }
  540:     return $result;
  541: }
  542: 
  543: 
  544: ##----------------------------------------------------------------- key
  545: sub start_key {
  546:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  547:     my $result='';
  548:     if ($target eq 'web' || $target eq 'tex') {
  549: 	&get_attributes(\%key,\%key_defaults,$parstack,$safeeval,
  550: 		    $tagstack->[-1]);
  551:     } elsif ($target eq 'edit') {
  552: 	$result .= &Apache::edit::tag_start($target,$token,'Plot Key');
  553: 	$result .= &edit_attributes($target,$token,\%key_defaults);
  554:     } elsif ($target eq 'modified') {
  555: 	my $constructtag=&Apache::edit::get_new_args
  556: 	    ($token,$parstack,$safeeval,keys(%key_defaults));
  557: 	if ($constructtag) {
  558: 	    $result = &Apache::edit::rebuild_tag($token);
  559: 	}
  560:     }
  561:     return $result;
  562: }
  563: 
  564: sub end_key {
  565:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  566:     my $result = '';
  567:     if ($target eq 'web' || $target eq 'tex') {
  568:     } elsif ($target eq 'edit') {
  569: 	$result.=&Apache::edit::tag_end($target,$token);
  570:     }
  571:     return $result;
  572: }
  573: 
  574: ##------------------------------------------------------------------- title
  575: sub start_title {
  576:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  577:     my $result='';
  578:     if ($target eq 'web' || $target eq 'tex') {
  579: 	$title = &Apache::lonxml::get_all_text("/title",$parser);
  580: 	$title=&Apache::run::evaluate($title,$safeeval,$$parstack[-1]);
  581: 	$title =~ s/\n/ /g;
  582: 	if (length($title) > $max_str_len) {
  583: 	    $title = substr($title,0,$max_str_len);
  584: 	}
  585:     } elsif ($target eq 'edit') {
  586: 	$result.=&Apache::edit::tag_start($target,$token,'Plot Title');
  587: 	my $text=&Apache::lonxml::get_all_text("/title",$parser);
  588: 	$result.=&Apache::edit::end_row().
  589: 	    &Apache::edit::start_spanning_row().
  590: 	    &Apache::edit::editline('',$text,'',60);
  591:     } elsif ($target eq 'modified') {
  592: 	$result.=&Apache::edit::rebuild_tag($token);
  593: 	$result.=&Apache::edit::modifiedfield("/title",$parser);
  594:     }
  595:     return $result;
  596: }
  597: 
  598: sub end_title {
  599:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  600:     my $result = '';
  601:     if ($target eq 'web' || $target eq 'tex') {
  602:     } elsif ($target eq 'edit') {
  603: 	$result.=&Apache::edit::tag_end($target,$token);
  604:     }
  605:     return $result;
  606: }
  607: ##------------------------------------------------------------------- xlabel
  608: sub start_xlabel {
  609:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  610:     my $result='';
  611:     if ($target eq 'web' || $target eq 'tex') {
  612: 	$xlabel = &Apache::lonxml::get_all_text("/xlabel",$parser);
  613: 	$xlabel=&Apache::run::evaluate($xlabel,$safeeval,$$parstack[-1]);
  614: 	$xlabel =~ s/\n/ /g;
  615: 	if (length($xlabel) > $max_str_len) {
  616: 	    $xlabel = substr($xlabel,0,$max_str_len);
  617: 	}
  618:     } elsif ($target eq 'edit') {
  619: 	$result.=&Apache::edit::tag_start($target,$token,'Plot Xlabel');
  620: 	my $text=&Apache::lonxml::get_all_text("/xlabel",$parser);
  621: 	$result.=&Apache::edit::end_row().
  622: 	    &Apache::edit::start_spanning_row().
  623: 	    &Apache::edit::editline('',$text,'',60);
  624:     } elsif ($target eq 'modified') {
  625: 	$result.=&Apache::edit::rebuild_tag($token);	
  626: 	$result.=&Apache::edit::modifiedfield("/xlabel",$parser);
  627:     }
  628:     return $result;
  629: }
  630: 
  631: sub end_xlabel {
  632:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  633:     my $result = '';
  634:     if ($target eq 'web' || $target eq 'tex') {
  635:     } elsif ($target eq 'edit') {
  636: 	$result.=&Apache::edit::tag_end($target,$token);
  637:     }
  638:     return $result;
  639: }
  640: 
  641: ##------------------------------------------------------------------- ylabel
  642: sub start_ylabel {
  643:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  644:     my $result='';
  645:     if ($target eq 'web' || $target eq 'tex') {
  646: 	$ylabel = &Apache::lonxml::get_all_text("/ylabel",$parser);
  647: 	$ylabel = &Apache::run::evaluate($ylabel,$safeeval,$$parstack[-1]);
  648: 	$ylabel =~ s/\n/ /g;
  649: 	if (length($ylabel) > $max_str_len) {
  650: 	    $ylabel = substr($ylabel,0,$max_str_len);
  651: 	}
  652:     } elsif ($target eq 'edit') {
  653: 	$result .= &Apache::edit::tag_start($target,$token,'Plot Ylabel');
  654: 	my $text = &Apache::lonxml::get_all_text("/ylabel",$parser);
  655: 	$result .= &Apache::edit::end_row().
  656: 	    &Apache::edit::start_spanning_row().
  657: 	    &Apache::edit::editline('',$text,'',60);
  658:     } elsif ($target eq 'modified') {
  659: 	$result.=&Apache::edit::rebuild_tag($token);
  660: 	$result.=&Apache::edit::modifiedfield("/ylabel",$parser);
  661:     }
  662:     return $result;
  663: }
  664: 
  665: sub end_ylabel {
  666:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  667:     my $result = '';
  668:     if ($target eq 'web' || $target eq 'tex') {
  669:     } elsif ($target eq 'edit') {
  670: 	$result.=&Apache::edit::tag_end($target,$token);
  671:     }
  672:     return $result;
  673: }
  674: 
  675: ##------------------------------------------------------------------- label
  676: sub start_label {
  677:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  678:     my $result='';
  679:     if ($target eq 'web' || $target eq 'tex') {
  680: 	my %label;
  681: 	&get_attributes(\%label,\%label_defaults,$parstack,$safeeval,
  682: 		    $tagstack->[-1]);
  683: 	my $text = &Apache::lonxml::get_all_text("/label",$parser);
  684: 	$text = &Apache::run::evaluate($text,$safeeval,$$parstack[-1]);
  685: 	$text =~ s/\n/ /g;
  686: 	$text = substr($text,0,$max_str_len) if (length($text) > $max_str_len);
  687: 	$label{'text'} = $text;
  688: 	push(@labels,\%label);
  689:     } elsif ($target eq 'edit') {
  690: 	$result .= &Apache::edit::tag_start($target,$token,'Plot Label');
  691: 	$result .= &edit_attributes($target,$token,\%label_defaults);
  692: 	my $text = &Apache::lonxml::get_all_text("/label",$parser);
  693: 	$result .= &Apache::edit::end_row().
  694: 	    &Apache::edit::start_spanning_row().
  695: 	    &Apache::edit::editline('',$text,'',60);
  696:     } elsif ($target eq 'modified') {
  697: 	&Apache::edit::get_new_args
  698: 	    ($token,$parstack,$safeeval,keys(%label_defaults));
  699: 	$result.=&Apache::edit::rebuild_tag($token);
  700: 	$result.=&Apache::edit::modifiedfield("/label",$parser);
  701:     }
  702:     return $result;
  703: }
  704: 
  705: sub end_label {
  706:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  707:     my $result = '';
  708:     if ($target eq 'web' || $target eq 'tex') {
  709:     } elsif ($target eq 'edit') {
  710: 	$result.=&Apache::edit::tag_end($target,$token);
  711:     }
  712:     return $result;
  713: }
  714: 
  715: ##------------------------------------------------------------------- curve
  716: sub start_curve {
  717:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  718:     my $result='';
  719:     &Apache::lonxml::register('Apache::lonplot',('function','data'));
  720:     push (@Apache::lonxml::namespace,'curve');
  721:     if ($target eq 'web' || $target eq 'tex') {
  722: 	my %curve;
  723: 	&get_attributes(\%curve,\%curve_defaults,$parstack,$safeeval,
  724: 		    $tagstack->[-1]);
  725: 	push (@curves,\%curve);
  726:     } elsif ($target eq 'edit') {
  727: 	$result .= &Apache::edit::tag_start($target,$token,'Curve');
  728: 	$result .= &edit_attributes($target,$token,\%curve_defaults,
  729:                                     \@curve_edit_order);
  730:     } elsif ($target eq 'modified') {
  731: 	my $constructtag=&Apache::edit::get_new_args
  732: 	    ($token,$parstack,$safeeval,keys(%curve_defaults));
  733: 	if ($constructtag) {
  734: 	    $result = &Apache::edit::rebuild_tag($token);
  735: 	    $result.= &Apache::edit::handle_insert();
  736: 	}
  737:     }
  738:     return $result;
  739: }
  740: 
  741: sub end_curve {
  742:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  743:     my $result = '';
  744:     pop @Apache::lonxml::namespace;
  745:     &Apache::lonxml::deregister('Apache::lonplot',('function','data'));
  746:     if ($target eq 'web' || $target eq 'tex') {
  747:     } elsif ($target eq 'edit') {
  748: 	$result.=&Apache::edit::tag_end($target,$token);
  749:     }
  750:     return $result;
  751: }
  752: 
  753: ##------------------------------------------------------------ curve function
  754: sub start_function {
  755:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  756:     my $result='';
  757:     if ($target eq 'web' || $target eq 'tex') {
  758: 	if (exists($curves[-1]->{'data'})) {
  759: 	    &Apache::lonxml::warning
  760:                 ('Use of the <b>curve function</b> tag precludes use of '.
  761:                  ' the <b>curve data</b> tag.  '.
  762:                  'The curve data tag will be omitted in favor of the '.
  763:                  'curve function declaration.');
  764: 	    delete $curves[-1]->{'data'} ;
  765: 	}
  766:         my $function = &Apache::lonxml::get_all_text("/function",$parser);
  767: 	$function = &Apache::run::evaluate($function,$safeeval,$$parstack[-1]);
  768: 	$curves[-1]->{'function'} = $function; 
  769:     } elsif ($target eq 'edit') {
  770: 	$result .= &Apache::edit::tag_start($target,$token,'Gnuplot compatible curve function');
  771: 	my $text = &Apache::lonxml::get_all_text("/function",$parser);
  772: 	$result .= &Apache::edit::end_row().
  773: 	    &Apache::edit::start_spanning_row().
  774: 	    &Apache::edit::editline('',$text,'',60);
  775:     } elsif ($target eq 'modified') {
  776: 	$result.=&Apache::edit::rebuild_tag($token);
  777: 	$result.=&Apache::edit::modifiedfield("/function",$parser);
  778:     }
  779:     return $result;
  780: }
  781: 
  782: sub end_function {
  783:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  784:     my $result = '';
  785:     if ($target eq 'web' || $target eq 'tex') {
  786:     } elsif ($target eq 'edit') {
  787: 	$result .= &Apache::edit::end_table();
  788:     }
  789:     return $result;
  790: }
  791: 
  792: ##------------------------------------------------------------ curve  data
  793: sub start_data {
  794:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  795:     my $result='';
  796:     if ($target eq 'web' || $target eq 'tex') {
  797: 	if (exists($curves[-1]->{'function'})) {
  798: 	    &Apache::lonxml::warning
  799:                 ('Use of the <b>curve function</b> tag precludes use of '.
  800:                  ' the <b>curve data</b> tag.  '.
  801:                  'The curve function tag will be omitted in favor of the '.
  802:                  'curve data declaration.');
  803: 	    delete($curves[-1]->{'function'});
  804: 	}
  805: 	my $datatext = &Apache::lonxml::get_all_text("/data",$parser);
  806: 	$datatext=&Apache::run::evaluate($datatext,$safeeval,$$parstack[-1]);
  807: 	# Deal with cases where we're given an array...
  808: 	if ($datatext =~ /^\@/) {
  809: 	    $datatext = &Apache::run::run('return "'.$datatext.'"',
  810: 					  $safeeval,1);
  811: 	}
  812: 	$datatext =~ s/\s+/ /g;
  813: 	# Need to do some error checking on the @data array - 
  814: 	# make sure it's all numbers and make sure each array 
  815: 	# is of the same length.
  816: 	my @data;
  817: 	if ($datatext =~ /,/) { # comma deliminated
  818: 	    @data = split /,/,$datatext;
  819: 	} else { # Assume it's space separated.
  820: 	    @data = split / /,$datatext;
  821: 	}
  822: 	for (my $i=0;$i<=$#data;$i++) {
  823: 	    # Check that it's non-empty
  824: 	    if (! defined($data[$i])) {
  825: 		&Apache::lonxml::warning(
  826: 		    'undefined curve data value.  Replacing with '.
  827: 		    ' pi/e = 1.15572734979092');
  828: 		$data[$i] = 1.15572734979092;
  829: 	    }
  830: 	    # Check that it's a number
  831: 	    if (! &$real_test($data[$i]) & ! &$int_test($data[$i])) {
  832: 		&Apache::lonxml::warning(
  833: 		    'Bad curve data value of '.$data[$i].'  Replacing with '.
  834: 		    ' pi/e = 1.15572734979092');
  835: 		$data[$i] = 1.15572734979092;
  836: 	    }
  837: 	}
  838: 	# complain if the number of data points is not the same as
  839: 	# in previous sets of data.
  840: 	if (($curves[-1]->{'data'}) && ($#data != $#{@{$curves[-1]->{'data'}->[0]}})){
  841: 	    &Apache::lonxml::warning
  842: 		('Number of data points is not consistent with previous '.
  843: 		 'number of data points');
  844: 	}
  845: 	push  @{$curves[-1]->{'data'}},\@data;
  846:     } elsif ($target eq 'edit') {
  847: 	$result .= &Apache::edit::tag_start($target,$token,'Comma or space deliminated curve data');
  848: 	my $text = &Apache::lonxml::get_all_text("/data",$parser);
  849: 	$result .= &Apache::edit::end_row().
  850: 	    &Apache::edit::start_spanning_row().
  851: 	    &Apache::edit::editline('',$text,'',60);
  852:     } elsif ($target eq 'modified') {
  853: 	$result.=&Apache::edit::rebuild_tag($token);
  854: 	$result.=&Apache::edit::modifiedfield("/data",$parser);
  855:     }
  856:     return $result;
  857: }
  858: 
  859: sub end_data {
  860:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  861:     my $result = '';
  862:     if ($target eq 'web' || $target eq 'tex') {
  863:     } elsif ($target eq 'edit') {
  864: 	$result .= &Apache::edit::end_table();
  865:     }
  866:     return $result;
  867: }
  868: 
  869: ##------------------------------------------------------------------- axis
  870: sub start_axis {
  871:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  872:     my $result='';
  873:     if ($target eq 'web' || $target eq 'tex') {
  874: 	&get_attributes(\%axis,\%axis_defaults,$parstack,$safeeval,
  875: 			$tagstack->[-1]);
  876:     } elsif ($target eq 'edit') {
  877: 	$result .= &Apache::edit::tag_start($target,$token,'Plot Axes');
  878: 	$result .= &edit_attributes($target,$token,\%axis_defaults,
  879: 				    \@axis_edit_order);
  880:     } elsif ($target eq 'modified') {
  881: 	my $constructtag=&Apache::edit::get_new_args
  882: 	    ($token,$parstack,$safeeval,keys(%axis_defaults));
  883: 	if ($constructtag) {
  884: 	    $result = &Apache::edit::rebuild_tag($token);
  885: 	}
  886:     }
  887:     return $result;
  888: }
  889: 
  890: sub end_axis {
  891:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  892:     my $result = '';
  893:     if ($target eq 'web' || $target eq 'tex') {
  894:     } elsif ($target eq 'edit') {
  895: 	$result.=&Apache::edit::tag_end($target,$token);
  896:     } elsif ($target eq 'modified') {
  897:     }
  898:     return $result;
  899: }
  900: 
  901: ###################################################################
  902: ##                                                               ##
  903: ##        Utility Functions                                      ##
  904: ##                                                               ##
  905: ###################################################################
  906: 
  907: ##----------------------------------------------------------- set_defaults
  908: sub set_defaults {
  909:     my ($var,$defaults) = @_;
  910:     my $key;
  911:     foreach $key (keys(%$defaults)) {
  912: 	$var->{$key} = $defaults->{$key}->{'default'};
  913:     }
  914: }
  915: 
  916: ##------------------------------------------------------------------- misc
  917: sub get_attributes{
  918:     my ($values,$defaults,$parstack,$safeeval,$tag) = @_;
  919:     foreach my $attr (keys(%{$defaults})) {
  920: 	if ($attr eq 'texwidth' || $attr eq 'texfont') {
  921: 	    $values->{$attr} = 
  922: 		&Apache::lonxml::get_param($attr,$parstack,$safeeval,undef,1);
  923: 	} else {
  924: 	    $values->{$attr} = 
  925: 		&Apache::lonxml::get_param($attr,$parstack,$safeeval);
  926: 	}
  927: 	if ($values->{$attr} eq '' | !defined($values->{$attr})) {
  928: 	    $values->{$attr} = $defaults->{$attr}->{'default'};
  929: 	    next;
  930: 	}
  931: 	my $test = $defaults->{$attr}->{'test'};
  932: 	if (! &$test($values->{$attr})) {
  933: 	    &Apache::lonxml::warning
  934: 		($tag.':'.$attr.': Bad value.'.'Replacing your value with : '
  935: 		 .$defaults->{$attr}->{'default'} );
  936: 	    $values->{$attr} = $defaults->{$attr}->{'default'};
  937: 	}
  938:     }
  939:     return ;
  940: }
  941: 
  942: ##------------------------------------------------------- write_gnuplot_file
  943: sub write_gnuplot_file {
  944:     my ($tmpdir,$filename,$target)= @_;
  945:     my $gnuplot_input = '';
  946:     my $curve;
  947:     my $pt = $plot{'texfont'};
  948:     # Collect all the colors
  949:     my @Colors;
  950:     push @Colors, $plot{'bgcolor'};
  951:     push @Colors, $plot{'fgcolor'}; 
  952:     push @Colors, (defined($axis{'color'})?$axis{'color'}:$plot{'fgcolor'});
  953:     foreach $curve (@curves) {
  954: 	push @Colors, ($curve->{'color'} ne '' ? 
  955: 		       $curve->{'color'}       : 
  956: 		       $plot{'fgcolor'}        );
  957:     }
  958:     # set term
  959:     if ($target eq 'web') {
  960: 	$gnuplot_input .= 'set term gif ';
  961: 	$gnuplot_input .= 'transparent ' if ($plot{'transparent'} eq 'on');
  962: 	$gnuplot_input .= $plot{'font'} . ' ';
  963: 	$gnuplot_input .= 'size '.$plot{'width'}.','.$plot{'height'}.' ';
  964: 	$gnuplot_input .= "@Colors\n";
  965: 	# set output
  966: 	$gnuplot_input .= "set output\n";
  967:     } elsif ($target eq 'tex') {
  968: 	$gnuplot_input .= "set term postscript eps monochrome solid \"Helvetica\" $pt \n";
  969: 	$gnuplot_input .= "set output \"/home/httpd/perl/tmp/".
  970: 	    &Apache::lonnet::unescape($filename).".eps\"\n";
  971:     }
  972:     # cartesian or polar?
  973:     if (lc($plot{'plottype'}) eq 'polar') {
  974:         $gnuplot_input .= 'set polar'.$/;
  975:     } else {
  976:         # Assume Cartesian
  977:     }
  978:     # grid
  979:     $gnuplot_input .= 'set grid'.$/ if ($plot{'grid'} eq 'on');
  980:     # border
  981:     $gnuplot_input .= ($plot{'border'} eq 'on'?
  982: 		       'set border'.$/           :
  983: 		       'set noborder'.$/         );
  984:     # sampling rate for non-data curves
  985:     $gnuplot_input .= "set samples $plot{'samples'}\n";
  986:     # title, xlabel, ylabel
  987:     # titles
  988:     if ($target eq 'tex') {
  989:         $gnuplot_input .= "set title  \"$title\" font \"Helvetica,".$pt."pt\"\n"  if (defined($title)) ;
  990:         $gnuplot_input .= "set xlabel \"$xlabel\" font \"Helvetica,".$pt."pt\" \n" if (defined($xlabel));
  991:         $gnuplot_input .= "set ylabel \"$ylabel\" font \"Helvetica,".$pt."pt\"\n" if (defined($ylabel));
  992:     } else {
  993:         $gnuplot_input .= "set title  \"$title\"  \n"  if (defined($title)) ;
  994:         $gnuplot_input .= "set xlabel \"$xlabel\" \n" if (defined($xlabel));
  995:         $gnuplot_input .= "set ylabel \"$ylabel\" \n" if (defined($ylabel));
  996:     }
  997:     # tics
  998:     if (%xtics) {    
  999: 	$gnuplot_input .= "set xtics $xtics{'location'} ";
 1000: 	$gnuplot_input .= ( $xtics{'mirror'} eq 'on'?"mirror ":"nomirror ");
 1001: 	$gnuplot_input .= "$xtics{'start'}, ";
 1002: 	$gnuplot_input .= "$xtics{'increment'}, ";
 1003: 	$gnuplot_input .= "$xtics{'end'}\n";
 1004:         if ($xtics{'minorfreq'} != 0) {
 1005:             $gnuplot_input .= "set mxtics ".$xtics{'minorfreq'}."\n";
 1006:         } 
 1007:     }
 1008:     if (%ytics) {    
 1009: 	$gnuplot_input .= "set ytics $ytics{'location'} ";
 1010: 	$gnuplot_input .= ( $ytics{'mirror'} eq 'on'?"mirror ":"nomirror ");
 1011: 	$gnuplot_input .= "$ytics{'start'}, ";
 1012: 	$gnuplot_input .= "$ytics{'increment'}, ";
 1013:         $gnuplot_input .= "$ytics{'end'}\n";
 1014:         if ($ytics{'minorfreq'} != 0) {
 1015:             $gnuplot_input .= "set mytics ".$ytics{'minorfreq'}."\n";
 1016:         } 
 1017:     }
 1018:     # axis
 1019:     if (%axis) {
 1020: 	$gnuplot_input .= "set xrange \[$axis{'xmin'}:$axis{'xmax'}\]\n";
 1021: 	$gnuplot_input .= "set yrange \[$axis{'ymin'}:$axis{'ymax'}\]\n";
 1022:     }
 1023:     # Key
 1024:     if (%key) {
 1025: 	$gnuplot_input .= 'set key '.$key{'pos'}.' ';
 1026: 	if ($key{'title'} ne '') {
 1027: 	    $gnuplot_input .= 'title "'.$key{'title'}.'" ';
 1028: 	} 
 1029: 	$gnuplot_input .= ($key{'box'} eq 'on' ? 'box ' : 'nobox ').$/;
 1030:     } else {
 1031: 	$gnuplot_input .= 'set nokey'.$/;
 1032:     }
 1033:     # labels
 1034:     my $label;
 1035:     foreach $label (@labels) {
 1036: 	$gnuplot_input .= 'set label "'.$label->{'text'}.'" at '.
 1037: 	    $label->{'xpos'}.','.$label->{'ypos'}.' '.$label->{'justify'}.' font "Helvetica,'.$pt.'pt"'.$/ ;
 1038:     }
 1039:     if ($target eq 'tex') {
 1040:         $gnuplot_input .="set size 1,".$plot{'height'}/$plot{'width'}*1.38;
 1041:         $gnuplot_input .="\n";
 1042:         }
 1043:     # curves
 1044:     $gnuplot_input .= 'plot ';
 1045:     for (my $i = 0;$i<=$#curves;$i++) {
 1046: 	$curve = $curves[$i];
 1047: 	$gnuplot_input.= ', ' if ($i > 0);
 1048: 	if (exists($curve->{'function'})) {
 1049: 	    $gnuplot_input.= 
 1050: 		$curve->{'function'}.' title "'.
 1051: 		$curve->{'name'}.'" with '.
 1052:                 $curve->{'linestyle'};
 1053:             $gnuplot_input.= ' linewidth 4 ' if ($target eq 'tex');
 1054:             if (($curve->{'linestyle'} eq 'points')      ||
 1055:                 ($curve->{'linestyle'} eq 'linespoints') ||
 1056:                 ($curve->{'linestyle'} eq 'errorbars')   ||
 1057:                 ($curve->{'linestyle'} eq 'xerrorbars')  ||
 1058:                 ($curve->{'linestyle'} eq 'yerrorbars')  ||
 1059:                 ($curve->{'linestyle'} eq 'xyerrorbars')) {
 1060:                 $gnuplot_input.=' pointtype '.$curve->{'pointtype'};
 1061:                 $gnuplot_input.=' pointsize '.$curve->{'pointsize'};
 1062:             }
 1063: 	} elsif (exists($curve->{'data'})) {
 1064: 	    # Store data values in $datatext
 1065: 	    my $datatext = '';
 1066: 	    #   get new filename
 1067: 	    my $datafilename = "$tmpdir/$filename.data.$i";
 1068: 	    my $fh=Apache::File->new(">$datafilename");
 1069: 	    # Compile data
 1070: 	    my @Data = @{$curve->{'data'}};
 1071: 	    my @Data0 = @{$Data[0]};
 1072: 	    for (my $i =0; $i<=$#Data0; $i++) {
 1073: 		my $dataset;
 1074: 		foreach $dataset (@Data) {
 1075: 		    $datatext .= $dataset->[$i] . ' ';
 1076: 		}
 1077: 		$datatext .= $/;
 1078: 	    }
 1079: 	    #   write file
 1080: 	    print $fh $datatext;
 1081: 	    close ($fh);
 1082: 	    #   generate gnuplot text
 1083: 	    $gnuplot_input.= '"'.$datafilename.'" title "'.
 1084: 		$curve->{'name'}.'" with '.
 1085: 		$curve->{'linestyle'};
 1086:             $gnuplot_input.= ' linewidth 4 ' if ($target eq 'tex');
 1087:             if (($curve->{'linestyle'} eq 'points')      ||
 1088:                 ($curve->{'linestyle'} eq 'linespoints') ||
 1089:                 ($curve->{'linestyle'} eq 'errorbars')   ||
 1090:                 ($curve->{'linestyle'} eq 'xerrorbars')  ||
 1091:                 ($curve->{'linestyle'} eq 'yerrorbars')  ||
 1092:                 ($curve->{'linestyle'} eq 'xyerrorbars')) {
 1093:                 $gnuplot_input.=' pointtype '.$curve->{'pointtype'};
 1094:                 $gnuplot_input.=' pointsize '.$curve->{'pointsize'};
 1095:             }
 1096: 	}
 1097:     }
 1098:     # Write the output to a file.
 1099:     my $fh=Apache::File->new(">$tmpdir$filename.data");
 1100:     print $fh $gnuplot_input;
 1101:     close($fh);
 1102:     # That's all folks.
 1103:     return ;
 1104: }
 1105: 
 1106: #---------------------------------------------- check_inputs
 1107: sub check_inputs {
 1108:     ## Note: no inputs, no outputs - this acts only on global variables.
 1109:     ## Make sure we have all the input we need:
 1110:     if (! %plot) { &set_defaults(\%plot,\%gnuplot_defaults); }
 1111:     if (! %key ) {} # No key for this plot, thats okay
 1112: #    if (! %axis) { &set_defaults(\%axis,\%axis_defaults); }
 1113:     if (! defined($title )) {} # No title for this plot, thats okay
 1114:     if (! defined($xlabel)) {} # No xlabel for this plot, thats okay
 1115:     if (! defined($ylabel)) {} # No ylabel for this plot, thats okay
 1116:     if ($#labels < 0) { }      # No labels for this plot, thats okay
 1117:     if ($#curves < 0) { 
 1118: 	&Apache::lonxml::warning("No curves specified for plot!!!!");
 1119: 	return '';
 1120:     }
 1121:     my $curve;
 1122:     foreach $curve (@curves) {
 1123: 	if (!defined($curve->{'function'})&&!defined($curve->{'data'})){
 1124: 	    &Apache::lonxml::warning("One of the curves specified did not contain any curve data or curve function declarations\n");
 1125: 	    return '';
 1126: 	}
 1127:     }
 1128: }
 1129: 
 1130: #------------------------------------------------ make_edit
 1131: sub edit_attributes {
 1132:     my ($target,$token,$defaults,$keys) = @_;
 1133:     my ($result,@keys);
 1134:     if ($keys && ref($keys) eq 'ARRAY') {
 1135:         @keys = @$keys;
 1136:     } else {
 1137: 	@keys = sort(keys(%$defaults));
 1138:     }
 1139:     foreach my $attr (@keys) {
 1140: 	# append a ' ' to the description if it doesn't have one already.
 1141: 	my $description = $defaults->{$attr}->{'description'};
 1142: 	$description .= ' ' if ($description !~ / $/);
 1143: 	if ($defaults->{$attr}->{'edit_type'} eq 'entry') {
 1144: 	    $result .= &Apache::edit::text_arg
 1145: 		($description,$attr,$token,
 1146: 		 $defaults->{$attr}->{'size'});
 1147: 	} elsif ($defaults->{$attr}->{'edit_type'} eq 'choice') {
 1148: 	    $result .= &Apache::edit::select_arg
 1149: 		($description,$attr,$defaults->{$attr}->{'choices'},$token);
 1150: 	} elsif ($defaults->{$attr}->{'edit_type'} eq 'onoff') {
 1151: 	    $result .= &Apache::edit::select_arg
 1152: 		($description,$attr,['on','off'],$token);
 1153: 	}
 1154: 	$result .= '<br />';
 1155:     }
 1156:     return $result;
 1157: }
 1158: 
 1159: 
 1160: ###################################################################
 1161: ##                                                               ##
 1162: ##           Insertion functions for editing plots               ##
 1163: ##                                                               ##
 1164: ###################################################################
 1165: 
 1166: sub insert_gnuplot {
 1167:     my $result = '';
 1168:     #  plot attributes
 1169:     $result .= "\n<gnuplot ";
 1170:     foreach my $attr (keys(%gnuplot_defaults)) {
 1171: 	$result .= "\n     $attr=\"$gnuplot_defaults{$attr}->{'default'}\"";
 1172:     }
 1173:     $result .= ">";
 1174:     # Add the components (most are commented out for simplicity)
 1175:     # $result .= &insert_key();
 1176:     # $result .= &insert_axis();
 1177:     # $result .= &insert_title();    
 1178:     # $result .= &insert_xlabel();    
 1179:     # $result .= &insert_ylabel();    
 1180:     $result .= &insert_curve();
 1181:     # close up the <gnuplot>
 1182:     $result .= "\n</gnuplot>";
 1183:     return $result;
 1184: }
 1185: 
 1186: sub insert_tics {
 1187:     my $result;
 1188:     $result .= &insert_xtics() . &insert_ytics;
 1189:     return $result;
 1190: }
 1191: 
 1192: sub insert_xtics {
 1193:     my $result;
 1194:     $result .= "\n    <xtics ";
 1195:     foreach my $attr (keys(%tic_defaults)) {
 1196: 	$result .= "\n        $attr=\"$tic_defaults{$attr}->{'default'}\" ";
 1197:     }
 1198:     $result .= "/>";
 1199:     return $result;
 1200: }
 1201: 
 1202: sub insert_ytics {
 1203:     my $result;
 1204:     $result .= "\n    <ytics ";
 1205:     foreach my $attr (keys(%tic_defaults)) {
 1206: 	$result .= "\n        $attr=\"$tic_defaults{$attr}->{'default'}\" ";
 1207:     }
 1208:     $result .= "/>";
 1209:     return $result;
 1210: }
 1211: 
 1212: sub insert_key {
 1213:     my $result;
 1214:     $result .= "\n    <key ";
 1215:     foreach my $attr (keys(%key_defaults)) {
 1216: 	$result .= "\n         $attr=\"$key_defaults{$attr}->{'default'}\"";
 1217:     }
 1218:     $result .= " />";
 1219:     return $result;
 1220: }
 1221: 
 1222: sub insert_axis{
 1223:     my $result;
 1224:     $result .= "\n    <axis ";
 1225:    foreach my $attr (keys(%axis_defaults)) {
 1226: 	$result .= "\n         $attr=\"$axis_defaults{$attr}->{'default'}\"";
 1227:     }
 1228:     $result .= " />";
 1229:     return $result;
 1230: }
 1231: 
 1232: sub insert_title  { return "\n    <title></title>"; }
 1233: sub insert_xlabel { return "\n    <xlabel></xlabel>"; }
 1234: sub insert_ylabel { return "\n    <ylabel></ylabel>"; }
 1235: 
 1236: sub insert_label {
 1237:     my $result;
 1238:     $result .= "\n    <label ";
 1239:     foreach my $attr (keys(%label_defaults)) {
 1240: 	$result .= "\n         $attr=\"".
 1241:             $label_defaults{$attr}->{'default'}."\"";
 1242:     }
 1243:     $result .= "></label>";
 1244:     return $result;
 1245: }
 1246: 
 1247: sub insert_curve {
 1248:     my $result;
 1249:     $result .= "\n    <curve ";
 1250:     foreach my $attr (keys(%curve_defaults)) {
 1251: 	$result .= "\n         $attr=\"".
 1252: 	    $curve_defaults{$attr}->{'default'}."\"";
 1253:     }
 1254:     $result .= " >";
 1255:     $result .= &insert_data().&insert_data()."\n    </curve>";
 1256: }
 1257: 
 1258: sub insert_function {
 1259:     my $result;
 1260:     $result .= "\n        <function></function>";
 1261:     return $result;
 1262: }
 1263: 
 1264: sub insert_data {
 1265:     my $result;
 1266:     $result .= "\n        <data></data>";
 1267:     return $result;
 1268: }
 1269: 
 1270: ##----------------------------------------------------------------------
 1271: 1;
 1272: __END__
 1273: 
 1274: 

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