File:  [LON-CAPA] / loncom / xml / lonplot.pm
Revision 1.88: download - view: text, annotated - select for diffs
Wed Sep 10 13:50:29 2003 UTC (20 years, 8 months ago) by sakharuk
Branches: MAIN
CVS tags: HEAD
Bug 2138 (Gnuplot: allow separate font size for print and screen) is fixed. I've implemented one particular font "Helvetica" with default size 25pt. If you want to change these defaults feel free to do this. You even can introduce some additional attributes to have possibility dinamically overwrite these parameters. Unfortunately I do not know sufficiently well lonplot.pm module so this can be done far better by Mattew.

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

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