--- loncom/xml/lonplot.pm 2012/07/03 11:29:57 1.156 +++ loncom/xml/lonplot.pm 2012/07/17 10:49:53 1.162 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Dynamic plot # -# $Id: lonplot.pm,v 1.156 2012/07/03 11:29:57 foxr Exp $ +# $Id: lonplot.pm,v 1.162 2012/07/17 10:49:53 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -98,11 +98,16 @@ BEGIN { my $max_str_len = 50; # if a label, title, xlabel, or ylabel text # is longer than this, it will be truncated. -my %linetypes = +my %linetypes = # For png use these linetypes. ( solid => 1, dashed => 0 ); +my %ps_linetypes = # For ps the line types are different! + ( + solid => 0, + dashed => 7 + ); my %linestyles = ( @@ -134,6 +139,10 @@ my $sml_test = sub {$_[0]=~/^(\d+| my $linestyle_test = sub {exists($linestyles{$_[0]})}; my $words_test = sub {$_[0]=~s/\s+/ /g;$_[0]=~/^([\w~!\@\#\$\%^&\*\(\)-=_\+\[\]\{\}:\;\'<>,\.\/\?\\]+ ?)+$/}; +my $arrowhead_test = sub{$_[0]=~/^(nohead|head|heads| )+$/}; +my $arrowstyle_test= sub{$_[0]=~/^(filled|empty|nofilled)+$/}; +my $degree_test = sub{&$pos_real_test($_[0]) && ($_[0] <= 360.0)}; + ################################################################### ## ## ## Attribute metadata ## @@ -528,7 +537,10 @@ my %axis_defaults = }, ); -my @curve_edit_order = ('color','name','linestyle','linewidth','linetype','pointtype','pointsize','limit'); +my @curve_edit_order = ('color','name','linestyle','linewidth','linetype', + 'pointtype','pointsize','limit', 'arrowhead', 'arrowstyle', + 'arrowlength', 'arrowangle', 'arrowbackangle' + ); my %curve_defaults = ( @@ -589,6 +601,43 @@ my %curve_defaults = edit_type => 'choice', choices => ['above', 'below', 'closed','x1','x2','y1','y2'] }, + arrowhead => { + default => 'head', + test => $arrowhead_test, + description => 'Vector arrow head type', + edit_type => 'choice', + choices => ['nohead', 'head', 'heads'] + }, + arrowstyle => { + default => 'filled', + test => $arrowstyle_test, + description => 'Vector arrow head style', + edit_type => 'choice', + choices => ['filled', 'empty', 'nofilled'] + }, + arrowlength => { + default => 0.02, + test => $pos_real_test, + description => "Length of vector arrow (only applies to vector plots)", + edit_type => 'entry', + size => '5' + }, + arrowangle => { + default => 10.0, + test => $degree_test, + description => 'Angle of arrow branches to arrow body (only applies to vector plots)', + edit_type => 'entry', + size => '5' + }, + + arrowbackangle => { + default => 90.0, + test => $degree_test, + descripton => 'Angle of arrow back lines to branches.', + edit_type => 'entry', + size => '5' + } + ); ################################################################### @@ -1587,6 +1636,48 @@ sub get_attributes{ } return ; } +## +# Generate tic mark specifications. +# +# @param type - type of tics (xtics or ytics). +# @param spec - Reference to a hash that contains the tic specification. +# @param target - 'tex' if hard copy target. +# +# @return string - the tic specification command. +# +sub generate_tics { + my ($type, $spec, $target) = @_; + my $result = ''; + + + if (defined %$spec) { + + + + # Major tics: + + $result .= "set $type $spec->{'location'} "; + $result .= ($spec->{'mirror'} eq 'on') ? 'mirror ' : 'nomirror '; + $result .= "$spec->{'start'}, "; + $result .= "$spec->{'increment'}, "; + $result .= "$spec->{'end'} "; + if ($target eq 'tex' ) { + $result .= 'font "Helvetica,22"'; + } + $result .= "\n"; + + # minor frequency: + + if ($spec->{'minorfreq'} != 0) { + $result .= "set m$type $spec->{'minorfreq'}\n"; + } + } else { + $result .= "set $type font " . '"Helvetica,22"' ."\n"; + } + + + return $result; +} ##------------------------------------------------------- write_gnuplot_file sub write_gnuplot_file { @@ -1648,6 +1739,7 @@ sub write_gnuplot_file { $gnuplot_input .= "set encoding iso_8859_1\n"; # Get access to extended font. } + $gnuplot_input .= "set encoding utf8\n"; # cartesian or polar plot? if (lc($Apache::lonplot::plot{'plottype'}) eq 'polar') { $gnuplot_input .= 'set polar'.$/; @@ -1728,42 +1820,10 @@ sub write_gnuplot_file { $gnuplot_input .= "set ylabel \"$ylabel\" $extra_space_y \n" if (defined($ylabel)); } # tics - if (%xtics) { - $gnuplot_input .= "set xtics $xtics{'location'} "; - $gnuplot_input .= ( $xtics{'mirror'} eq 'on'?"mirror ":"nomirror "); - $gnuplot_input .= "$xtics{'start'}, "; - $gnuplot_input .= "$xtics{'increment'}, "; - $gnuplot_input .= "$xtics{'end'} "; - if ($target eq 'tex') { - $gnuplot_input .= 'font "Helvetica,22"'; # Needed in iso 8859-1 enc. - } - $gnuplot_input .= "\n"; - if ($xtics{'minorfreq'} != 0) { - $gnuplot_input .= "set mxtics ".$xtics{'minorfreq'}."\n"; - } - } else { - if ($target eq 'tex') { - $gnuplot_input .= 'set xtics font "Helvetica,22"'."\n"; # needed in iso 8859-1 enc - } - } - if (%ytics) { - $gnuplot_input .= "set ytics $ytics{'location'} "; - $gnuplot_input .= ( $ytics{'mirror'} eq 'on'?"mirror ":"nomirror "); - $gnuplot_input .= "$ytics{'start'}, "; - $gnuplot_input .= "$ytics{'increment'}, "; - $gnuplot_input .= "$ytics{'end'} "; - if ($target eq 'tex') { - $gnuplot_input .= 'font "Helvetica,22"'; # Needed in iso-8859-1 encoding. - } - $gnuplot_input .= "\n"; - if ($ytics{'minorfreq'} != 0) { - $gnuplot_input .= "set mytics ".$ytics{'minorfreq'}."\n"; - } - } else { - if ($target eq 'tex') { - $gnuplot_input .= 'set ytics font "Helvetica,22"'."\n"; # Needed for iso 8859-1 enc. - } - } + $gnuplot_input .= &generate_tics('xtics', \%xtics, $target); + + $gnuplot_input .= &generate_tics('ytics', \%ytics, $target); + # axis if (%axis) { if ($axis{'xformat'} ne 'on') { @@ -1842,10 +1902,13 @@ sub write_gnuplot_file { # type (solid/dashed), color, width # my $linestyle_index = 50; - my $line_type = ''; - my $line_color = ''; my $line_width = ''; + # If arrows are needed there will be an arrow style for each as well: + # + + my $arrow_style_index = 50; + my $plot_command; my $plot_type; @@ -1855,6 +1918,7 @@ sub write_gnuplot_file { if ($target eq 'tex') { $curve->{'linewidth'} *= 2; } + $line_width = $curve->{'linewidth'}; if (exists($curve->{'function'})) { $plot_type = $curve->{'function'}.' title "'. @@ -1884,31 +1948,75 @@ sub write_gnuplot_file { $curve->{'name'}.'" with '. $curve->{'linestyle'}; } + my $pointtype = ''; + my $pointsize = ''; + + # Figure out the linestyle: + + my $lt = $curve->{'linetype'} ne '' ? $curve->{'linetype'} + : 'solid'; # Line type defaults to solid. + # The mapping of lt -> the actual gnuplot line type depends on the target: + + if ($target eq 'tex') { + $lt = $ps_linetypes{$lt}; + } else { + $lt = $linetypes{$lt} + } + + my $color = $curve->{'color'}; + $color =~ s/^x/#/; # Convert xhex color -> #hex color. + + my $style_command = "set style line $linestyle_index $pointtype $pointsize linetype $lt linewidth $line_width lc rgb '$color'\n"; + $gnuplot_input .= $style_command; + + + if (($curve->{'linestyle'} eq 'points') || ($curve->{'linestyle'} eq 'linespoints') || ($curve->{'linestyle'} eq 'errorbars') || ($curve->{'linestyle'} eq 'xerrorbars') || ($curve->{'linestyle'} eq 'yerrorbars') || ($curve->{'linestyle'} eq 'xyerrorbars')) { - $plot_command.=' pointtype '.$curve->{'pointtype'}; - $plot_command.=' pointsize '.$curve->{'pointsize'}; + + $pointtype =' pointtype '.$curve->{'pointtype'}; + $pointsize =' pointsize '.$curve->{'pointsize'}; } elsif ($curve->{'linestyle'} eq 'filledcurves') { $plot_command.= ' '.$curve->{'limit'}; - } elsif ($curve->{'linetype'} ne '' && - $curve->{'linestyle'} eq 'lines') { - $plot_command.= ' linetype '; - $plot_command.= $linetypes{$curve->{'linetype'}}; - $plot_command.= ' linecolor rgb "'; - # convert color from xaaaaaa to #aaaaaa - $curve->{'color'} =~ s/^x/#/; - $plot_command.= $curve->{'color'}.'"'; + } elsif ($curve->{'linestyle'} eq 'vector') { + + # Create the arrow head style add it to + # $gnuplot_input..and ensure it gets + # Selected in the plot command. + + $gnuplot_input .= "set style arrow $arrow_style_index "; + $gnuplot_input .= ' ' . $curve->{'arrowhead'}; + $gnuplot_input .= ' size ' . $curve->{'arrowlength'}; + $gnuplot_input .= ','.$curve->{'arrowangle'}; + $gnuplot_input .= ',' . $curve->{'arrowbackangle'}; + $gnuplot_input .= ' ' . $curve->{'arrowstyle'} . " ls $linestyle_index\n"; + + + $plot_command .= " arrowstyle $arrow_style_index "; + $arrow_style_index++; + } + + + + + # The condition below is because gnuplot lumps the linestyle in with the + # arrowstyle _sigh_. + + if ($curve->{'linestyle'} ne 'vector') { + $plot_command.= " ls $linestyle_index"; } - $plot_command.= ' linewidth '.$curve->{'linewidth'}; + $gnuplot_input .= 'plot ' . $plot_type . ' ' . $plot_command . "\n"; + $linestyle_index++; # Each curve get a unique linestyle. } # Write the output to a file. - open (my $fh,">$tmpdir$filename.data"); - binmode($fh, ":utf8"); + &Apache::lonnet::logthis($gnuplot_input); + open (my $fh, "> $tmpdir$filename.data"); + binmode($fh, ':utf8'); print $fh $gnuplot_input; close($fh); # That's all folks.