# HG changeset patch # User jwe # Date 1201030968 0 # Node ID 246f905cb98459d8566596ac267ee28d34db4f0a # Parent 8433bb7865bd651a12405b4cdbf18372affb9a50 [project @ 2008-01-22 19:42:47 by jwe] diff --git a/liboctave/ChangeLog b/liboctave/ChangeLog --- a/liboctave/ChangeLog +++ b/liboctave/ChangeLog @@ -1,4 +1,4 @@ -2008-01-22 Michael Goffioul +2008-01-22 Michael Goffioul * oct-time.cc (octave_base_tim::init): Validate pointer argument; this fixes the "localtime(-1)" crash under Windows. diff --git a/scripts/ChangeLog b/scripts/ChangeLog --- a/scripts/ChangeLog +++ b/scripts/ChangeLog @@ -1,3 +1,10 @@ +2008-01-22 Michael Goffioul + + * plot/gnuplot_drawnow.m: New function corresponding to the + implementation of the gnuplot-based graphics backend (derived from + drawnow.m). + * plot/drawnow.m: Deleted (converted to C++). + 2008-01-15 Rolf Fabian * linear-algebra/__norm__.m: Only scale if inf norm is finite. diff --git a/scripts/plot/Makefile.in b/scripts/plot/Makefile.in --- a/scripts/plot/Makefile.in +++ b/scripts/plot/Makefile.in @@ -85,7 +85,7 @@ contourc.m \ contourf.m \ cylinder.m \ - drawnow.m \ + gnuplot_drawnow.m \ ellipsoid.m \ errorbar.m \ ezcontourf.m \ diff --git a/scripts/plot/drawnow.m b/scripts/plot/drawnow.m deleted file mode 100644 --- a/scripts/plot/drawnow.m +++ /dev/null @@ -1,231 +0,0 @@ -## Copyright (C) 2005, 2006, 2007 John W. Eaton -## -## This file is part of Octave. -## -## Octave is free software; you can redistribute it and/or modify it -## under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 3 of the License, or (at -## your option) any later version. -## -## Octave is distributed in the hope that it will be useful, but -## WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with Octave; see the file COPYING. If not, see -## . - -## -*- texinfo -*- -## @deftypefn {Function File} {} drawnow () -## Update and display the current graphics. -## -## Octave automatically calls drawnow just before printing a prompt, -## when @code{sleep} or @code{pause} is called, or while waiting for -## command-line input. -## @end deftypefn - -## Author: jwe - -function drawnow (term, file, mono, debug_file) - - persistent drawnow_executing = 0; - - unwind_protect - - ## If this is a recursive call, do nothing. - if (++drawnow_executing > 1) - return; - endif - - if (nargin < 3) - mono = false; - endif - - if (nargin >= 2 && nargin <= 4) - [dnm, fnm, ext] = fileparts (file); - if (! (isempty (dnm) || isdir (dnm))) - error ("drawnow: nonexistent directory `%s'", dnm); - endif - h = get (0, "currentfigure"); - if (h) - f = __get__ (h); - plot_stream = []; - fid = []; - unwind_protect - [plot_stream, enhanced] = open_gnuplot_stream ([], term, file); - __go_draw_figure__ (f, plot_stream, enhanced, mono); - if (nargin == 4) - fid = fopen (debug_file, "wb"); - enhanced = init_plot_stream (fid, [], term, file); - __go_draw_figure__ (f, fid, enhanced, mono); - endif - unwind_protect_cleanup - if (! isempty (plot_stream)) - pclose (plot_stream); - endif - if (! isempty (fid)) - fclose (fid); - endif - end_unwind_protect - else - error ("drawnow: nothing to draw"); - endif - elseif (nargin == 0) - for h = __go_figure_handles__ () - if (! (isnan (h) || h == 0)) - f = __get__ (h); - if (f.__modified__) - plot_stream = f.__plot_stream__; - figure_is_visible = strcmp (f.visible, "on"); - if (figure_is_visible) - if (isempty (plot_stream)) - [plot_stream, enhanced] = open_gnuplot_stream (h); - set (h, "__enhanced__", enhanced); - else - enhanced = f.__enhanced__; - endif - __go_draw_figure__ (f, plot_stream, enhanced, mono); - elseif (! isempty (plot_stream)) - pclose (plot_stream); - set (h, "__plot_stream__", []); - set (h, "__enhanced__", false); - endif - set (h, "__modified__", false); - endif - endif - endfor - else - print_usage (); - endif - - unwind_protect_cleanup - - drawnow_executing--; - __request_drawnow__ (false); - - end_unwind_protect - -endfunction - -function [plot_stream, enhanced] = open_gnuplot_stream (h, varargin) - - ## If drawnow is cleared, it is possible to register __go_close_all__ - ## more than once, but that is not fatal. - persistent __go_close_all_registered__; - - cmd = gnuplot_binary (); - - plot_stream = popen (cmd, "w"); - - if (plot_stream < 0) - error ("drawnow: failed to open connection to gnuplot"); - else - - if (! isempty (h)) - set (h, "__plot_stream__", plot_stream); - endif - - enhanced = init_plot_stream (plot_stream, h, varargin{:}); - - if (isempty (__go_close_all_registered__)) - atexit ("__go_close_all__"); - __go_close_all_registered__ = true; - endif - - endif - -endfunction - -function enhanced = init_plot_stream (plot_stream, h, term, file) - - if (nargin == 4) - enhanced = enhanced_term (term); - if (! isempty (term)) - if (enhanced) - fprintf (plot_stream, "set terminal %s enhanced;\n", term); - else - fprintf (plot_stream, "set terminal %s;\n", term); - endif - endif - if (! isempty (file)) - fprintf (plot_stream, "set output \"%s\";\n", file); - endif - else - - ## Guess the terminal type. - term = getenv ("GNUTERM"); - if (isempty (term)) - if (! isempty (getenv ("DISPLAY"))) - term = "x11"; - elseif (! isunix ()) - term = "windows"; - else - ## This should really be checking for os x before setting - ## the terminal type to aqua, but nobody will notice because - ## every other unix will be using x11 and windows will be - ## using windows. Those diehards still running octave from - ## a linux console know how to set the GNUTERM variable. - term = "aqua"; - endif - endif - - enhanced = enhanced_term (term); - if (enhanced) - enh_str = "enhanced"; - else - enh_str = ""; - endif - - ## If no 'h' (why not?) then open the terminal as Figure 0. - if (isempty (h)) - h = 0; - endif - - if (strcmp (term, "x11")) - fprintf (plot_stream, "set terminal x11 %s title \"Figure %d\"\n", - enh_str, h); - elseif (strcmp (term, "aqua")) - ## Aqua doesn't understand the 'title' option despite what the - ## gnuplot 4.2 documentation says. - fprintf (plot_stream, "set terminal aqua %d %s\n", h, enh_str); - elseif (strcmp (term, "wxt")) - fprintf (plot_stream, "set terminal wxt %s title \"Figure %d\"\n", - enh_str, h); - - elseif (enhanced) - fprintf (plot_stream, "set terminal %s %s\n", term, enh_str); - endif - ## gnuplot will pick up the GNUTERM environment variable itself - ## so no need to set the terminal type if not also setting the - ## figure title or enhanced mode. - - endif - -endfunction - -function have_enhanced = enhanced_term (term) - persistent enhanced_terminals; - - if (isempty (enhanced_terminals)) - ## Don't include pstex, pslatex or epslatex here as the TeX commands - ## should not be interpreted in that case. - if (compare_versions (__gnuplot_version__ (), "4.0", ">")) - enhanced_terminals = {"aqua", "dumb", "png", "jpeg", "gif", "pm", ... - "windows", "wxt", "svg", "postscript", "x11", "pdf"}; - else - enhanced_terminals = {"x11", "postscript"}; - endif - endif - - term = tolower (term); - - have_enhanced = false; - for i = 1 : length (enhanced_terminals) - t = enhanced_terminals{i}; - if (strncmp (term, t, min (length (term), length (t)))) - have_enhanced = true; - break; - endif - endfor -endfunction diff --git a/scripts/plot/gnuplot_drawnow.m b/scripts/plot/gnuplot_drawnow.m new file mode 100644 --- /dev/null +++ b/scripts/plot/gnuplot_drawnow.m @@ -0,0 +1,183 @@ +## Copyright (C) 2005, 2006, 2007 John W. Eaton +## +## This file is part of Octave. +## +## Octave is free software; you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or (at +## your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . + +## -*- texinfo -*- +## @deftypefn {Function File} {} drawnow () +## Update and display the current graphics. +## +## Octave automatically calls drawnow just before printing a prompt, +## when @code{sleep} or @code{pause} is called, or while waiting for +## command-line input. +## @end deftypefn + +## Author: jwe + +function gnuplot_drawnow (h, term, file, mono, debug_file) + + if (nargin < 4) + mono = false; + endif + + if (nargin >= 3 && nargin <= 5) + f = __get__ (h); + plot_stream = []; + fid = []; + unwind_protect + [plot_stream, enhanced] = open_gnuplot_stream ([], term, file); + __go_draw_figure__ (f, plot_stream, enhanced, mono); + if (nargin == 5) + fid = fopen (debug_file, "wb"); + enhanced = init_plot_stream (fid, [], term, file); + __go_draw_figure__ (f, fid, enhanced, mono); + endif + unwind_protect_cleanup + if (! isempty (plot_stream)) + pclose (plot_stream); + endif + if (! isempty (fid)) + fclose (fid); + endif + end_unwind_protect + elseif (nargin == 1) + f = __get__ (h); + plot_stream = f.__plot_stream__; + if (isempty (plot_stream)) + [plot_stream, enhanced] = open_gnuplot_stream (h); + set (h, "__enhanced__", enhanced); + else + enhanced = f.__enhanced__; + endif + __go_draw_figure__ (f, plot_stream, enhanced, mono); + else + print_usage (); + endif + +endfunction + +function [plot_stream, enhanced] = open_gnuplot_stream (h, varargin) + + cmd = gnuplot_binary (); + + plot_stream = popen (cmd, "w"); + + if (plot_stream < 0) + error ("drawnow: failed to open connection to gnuplot"); + else + + if (! isempty (h)) + set (h, "__plot_stream__", plot_stream); + endif + + enhanced = init_plot_stream (plot_stream, h, varargin{:}); + + endif + +endfunction + +function enhanced = init_plot_stream (plot_stream, h, term, file) + + if (nargin == 4) + enhanced = enhanced_term (term); + if (! isempty (term)) + if (enhanced) + fprintf (plot_stream, "set terminal %s enhanced;\n", term); + else + fprintf (plot_stream, "set terminal %s;\n", term); + endif + endif + if (! isempty (file)) + fprintf (plot_stream, "set output \"%s\";\n", file); + endif + else + + ## Guess the terminal type. + term = getenv ("GNUTERM"); + if (isempty (term)) + if (! isempty (getenv ("DISPLAY"))) + term = "x11"; + elseif (! isunix ()) + term = "windows"; + else + ## This should really be checking for os x before setting + ## the terminal type to aqua, but nobody will notice because + ## every other unix will be using x11 and windows will be + ## using windows. Those diehards still running octave from + ## a linux console know how to set the GNUTERM variable. + term = "aqua"; + endif + endif + + enhanced = enhanced_term (term); + if (enhanced) + enh_str = "enhanced"; + else + enh_str = ""; + endif + + ## If no 'h' (why not?) then open the terminal as Figure 0. + if (isempty (h)) + h = 0; + endif + + if (strcmp (term, "x11")) + fprintf (plot_stream, "set terminal x11 %s title \"Figure %d\"\n", + enh_str, h); + elseif (strcmp (term, "aqua")) + ## Aqua doesn't understand the 'title' option despite what the + ## gnuplot 4.2 documentation says. + fprintf (plot_stream, "set terminal aqua %d %s\n", h, enh_str); + elseif (strcmp (term, "wxt")) + fprintf (plot_stream, "set terminal wxt %s title \"Figure %d\"\n", + enh_str, h); + + elseif (enhanced) + fprintf (plot_stream, "set terminal %s %s\n", term, enh_str); + endif + ## gnuplot will pick up the GNUTERM environment variable itself + ## so no need to set the terminal type if not also setting the + ## figure title or enhanced mode. + + endif + +endfunction + +function have_enhanced = enhanced_term (term) + persistent enhanced_terminals; + + if (isempty (enhanced_terminals)) + ## Don't include pstex, pslatex or epslatex here as the TeX commands + ## should not be interpreted in that case. + if (compare_versions (__gnuplot_version__ (), "4.0", ">")) + enhanced_terminals = {"aqua", "dumb", "png", "jpeg", "gif", "pm", ... + "windows", "wxt", "svg", "postscript", "x11", "pdf"}; + else + enhanced_terminals = {"x11", "postscript"}; + endif + endif + + term = tolower (term); + + have_enhanced = false; + for i = 1 : length (enhanced_terminals) + t = enhanced_terminals{i}; + if (strncmp (term, t, min (length (term), length (t)))) + have_enhanced = true; + break; + endif + endfor +endfunction diff --git a/src/ChangeLog b/src/ChangeLog --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,19 @@ +2008-01-22 Michael Goffioul + + * graphics.h.in (base_properties::is_visible, + base_properties::set_modified): New accessors. + (class base_graphics_backend, class graphics_backend): New classes + for handling octave/backend interaction. + (figure::properties::close): Add "pop" argument controlling + whether the figure should be popped from the list of existing figures. + (class figure::properties): New backend field and accessors, holding + the graphics backend associated with the figure. + * graphics.cc (class gnuplot_backend): New class for the default + gnuplot backend. + (figure::properties::close): Add "pop" argument and transfer the + figure closing to the associated backend. + (Fdrawnow): New builtin function, converted from drawnow.m. + 2008-01-19 John W. Eaton * genprops.awk (emit_source): Use "pname" for property name argument. diff --git a/src/graphics.cc b/src/graphics.cc --- a/src/graphics.cc +++ b/src/graphics.cc @@ -43,6 +43,8 @@ #include "ov-fcn-handle.h" #include "parse.h" #include "unwind-prot.h" +#include "file-ops.h" +#include "file-stat.h" static void gripe_set_invalid (const std::string& pname) @@ -1146,6 +1148,69 @@ // --------------------------------------------------------------------- +class gnuplot_backend : public base_graphics_backend +{ +public: + gnuplot_backend (void) + : base_graphics_backend ("gnuplot") { } + + ~gnuplot_backend (void) { } + + bool is_valid (void) const { return true; } + + void close_figure (const octave_value& pstream) const + { + if (! pstream.is_empty()) + { + octave_value_list args; + args(1) = "\nquit;\n"; + args(0) = pstream; + feval ("fputs", args); + args.resize (1); + feval ("fflush", args); + feval ("pclose", args); + } + } + + void redraw_figure (const graphics_handle& fh) const + { + octave_value_list args; + args(0) = fh.as_octave_value (); + feval ("gnuplot_drawnow", args); + } + + void print_figure (const graphics_handle& fh, const std::string& term, + const std::string& file, bool mono, + const std::string& debug_file) const + { + octave_value_list args; + args.resize (4); + args(0) = fh.as_octave_value (); + args(1) = term; + args(2) = file; + args(3) = mono; + if (! debug_file.empty ()) + args(4) = debug_file; + feval ("gnuplot_drawnow", args); + } + + Matrix get_canvas_size (const graphics_handle& fh) const + { return Matrix (1, 2, 0.0); } +}; + +graphics_backend +graphics_backend::default_backend (void) +{ + if (available_backends.size () == 0) + register_backend (new gnuplot_backend ()); + + return available_backends["gnuplot"]; +} + +std::map graphics_backend::available_backends; + +// --------------------------------------------------------------------- + #include "graphics-props.cc" // --------------------------------------------------------------------- @@ -1202,24 +1267,19 @@ } void -figure::properties::close (void) +figure::properties::close (bool pop) { - if (! get___plot_stream__ ().is_empty ()) + if (backend) + backend.close_figure (get___plot_stream__ ()); + + if (pop) { - octave_value_list args; - args(1) = "\nquit;\n"; - args(0) = get___plot_stream__ (); - feval ("fputs", args); - args.resize (1); - feval ("fflush", args); - feval ("pclose", args); + gh_manager::pop_figure (__myhandle__); + + graphics_handle cf = gh_manager::current_figure (); + + xset (0, "currentfigure", cf.value ()); } - - gh_manager::pop_figure (__myhandle__); - - graphics_handle cf = gh_manager::current_figure (); - - xset (0, "currentfigure", cf.value ()); } octave_value @@ -2305,6 +2365,130 @@ return octave_value (gh_manager::figure_handle_list ()); } +DEFUN (drawnow, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} __go_drawnow__ ()\n\ +@deftypefnx {Built-in Function} {} __go_drawnow__ (@var{term}, @var{file}, @var{mono}, @var{debug_file})\n\ +Undocumented internal function.\n\ +@end deftypefn") +{ + static int drawnow_executing = 0; + static bool __go_close_all_registered__ = false; + + octave_value retval; + + if (drawnow_executing >= 1) + return retval; + + if (! __go_close_all_registered__) + { + // FIXME: is there a C++ way to do this? + int parse_status; + eval_string ("atexit (\"__go_close_all__\")", true, parse_status); + __go_close_all_registered__ = true; + } + + ++drawnow_executing; + + if (args.length () == 0) + { + Matrix hlist = gh_manager::figure_handle_list (); + + for (int i = 0; ! error_state && i < hlist.length (); i++) + { + graphics_handle h = gh_manager::lookup (hlist(i)); + + if (h.ok () && h != 0) + { + graphics_object go = gh_manager::get_object (h); + figure::properties& fprops = dynamic_cast (go.get_properties ()); + + if (fprops.is_modified ()) + { + if (fprops.is_visible ()) + fprops.get_backend ().redraw_figure (h); + else if (! fprops.get___plot_stream__ ().is_empty ()) + { + fprops.close (false); + fprops.set___plot_stream__ (Matrix ()); + fprops.set___enhanced__ (false); + } + fprops.set_modified (false); + } + } + } + } + else if (args.length () >= 2 && args.length () <= 4) + { + std::string term, file, debug_file; + bool mono; + + term = args(0).string_value (); + + if (! error_state) + { + file = args(1).string_value (); + + if (! error_state) + { + int pos = file.find_last_of (file_ops::dir_sep_chars); + + if (pos != NPOS) + { + file_stat fs (file.substr (0, pos)); + + if (! (fs && fs.is_dir ())) + error ("drawnow: nonexistent directory `%s'", + file.substr (0, pos).c_str ()); + } + + mono = (args.length () >= 3 ? args(2).bool_value () : false); + + if (! error_state) + { + debug_file = (args.length () > 3 ? args(3).string_value () + : ""); + + if (! error_state) + { + graphics_handle h = gcf (); + + if (h.ok ()) + { + graphics_object go = gh_manager::get_object (h); + figure::properties& fprops = dynamic_cast (go.get_properties ()); + + fprops.get_backend () + .print_figure (h, term, file, mono, debug_file); + } + else + error ("drawnow: nothing to draw"); + } + else + error ("drawnow: invalid debug_file, expected a string value"); + } + else + error ("drawnow: invalid colormode, expected a boolean value"); + } + else + error ("drawnow: invalid file, expected a string value"); + } + else + error ("drawnow: invalid terminal, expected a string value"); + } + else + print_usage (); + + // FIXME: is there a C++ way to do this? + octave_value_list fargs; + fargs(0) = false; + feval ("__request_drawnow__", fargs); + + --drawnow_executing; + + return retval; +} + octave_value get_property_from_handle (double handle, const std::string &property, const std::string &func) diff --git a/src/graphics.h.in b/src/graphics.h.in --- a/src/graphics.h.in +++ b/src/graphics.h.in @@ -1052,7 +1052,8 @@ octave_value get_uicontextmenu (void) const { return uicontextmenu.get (); } octave_value get_userdata (void) const { return userdata.get (); } - + + bool is_visible (void) const { return visible.is_on (); } std::string get_visible (void) const { return visible.current_value (); } bool is_beingdeleted (void) const { return beingdeleted.is_on (); } @@ -1071,6 +1072,8 @@ void set_parent (const octave_value& val); + void set_modified (const octave_value& val) { __modified__ = val; } + void set_busyaction (const octave_value& val) { if (! error_state) @@ -1632,6 +1635,121 @@ // --------------------------------------------------------------------- +class graphics_backend; + +class base_graphics_backend +{ +public: + friend class graphics_backend; + +public: + base_graphics_backend (const std::string& nm) + : name (nm), count (0) { } + + virtual ~base_graphics_backend (void) { } + + std::string get_name (void) const { return name; } + + virtual bool is_valid (void) const { return false; } + + virtual void close_figure (const octave_value&) const + { error ("close_figure: invalid graphics backend"); } + + virtual void redraw_figure (const graphics_handle&) const + { error ("redraw_figure: invalid graphics backend"); } + + virtual void print_figure (const graphics_handle&, const std::string&, + const std::string&, bool, + const std::string& = "") const + { error ("print_figure: invalid graphics backend"); } + + virtual Matrix get_canvas_size (const graphics_handle&) const + { + error ("get_canvas_size: invalid graphics backend"); + return Matrix (1, 2, 0.0); + } + +private: + std::string name; + int count; +}; + +class graphics_backend +{ +public: + graphics_backend (void) + : rep (new base_graphics_backend ("unknown")) + { + rep->count++; + } + + graphics_backend (base_graphics_backend* b) + : rep (b) + { + rep->count++; + } + + graphics_backend (const graphics_backend& b) + : rep (b.rep) + { + rep->count++; + } + + ~graphics_backend (void) + { + if (--rep->count == 0) + delete rep; + } + + graphics_backend& operator = (const graphics_backend& b) + { + if (rep != b.rep) + { + if (--rep->count == 0) + delete rep; + + rep = b.rep; + rep->count++; + } + + return *this; + } + + operator bool (void) const { return rep->is_valid (); } + + std::string get_name (void) const { return rep->get_name (); } + + void close_figure (const octave_value& pstream) const + { rep->close_figure (pstream); } + + void redraw_figure (const graphics_handle& fh) const + { rep->redraw_figure (fh); } + + void print_figure (const graphics_handle& fh, const std::string& term, + const std::string& file, bool mono, + const std::string& debug_file = "") const + { rep->print_figure (fh, term, file, mono, debug_file); } + + Matrix get_canvas_size (const graphics_handle& fh) const + { return rep->get_canvas_size (fh); } + + OCTINTERP_API static graphics_backend default_backend (void); + + static void register_backend (const graphics_backend& b) + { available_backends[b.get_name ()] = b; } + + static void unregister_backend (const std::string& name) + { available_backends.erase (name); } + +private: + base_graphics_backend *rep; + +private: + static std::map available_backends; +}; + +// --------------------------------------------------------------------- + class OCTINTERP_API root_figure : public base_graphics_object { public: @@ -1744,9 +1862,20 @@ class properties : public base_properties { public: - void close (void); + void close (bool pop = true); + void set_visible (const octave_value& val); + graphics_backend get_backend (void) const + { + if (! backend) + backend = graphics_backend::default_backend (); + + return backend; + } + + void set_backend (const graphics_backend& b) { backend = b; } + // See the genprops.awk script for an explanation of the // properties declarations. @@ -1813,6 +1942,9 @@ pointershapehotspot.add_constraint (dim_vector (1, 2)); position.add_constraint (dim_vector (1, 4)); } + + private: + mutable graphics_backend backend; }; private: @@ -1881,6 +2013,12 @@ bool valid_object (void) const { return true; } + graphics_backend get_backend (void) const + { return xproperties.get_backend (); } + + void set_backend (const graphics_backend& b) + { xproperties.set_backend (b); } + private: property_list default_properties; };