# HG changeset patch # User David Bateman # Date 1219766294 14400 # Node ID 9a6f4713f765a1154bac36847aa9f4e360208474 # Parent d51c3541be284f8bac1ac7b71d75a41e1a4c0a58 Add area, bar, quiver and stair series graphics objects. Document them diff --git a/doc/ChangeLog b/doc/ChangeLog --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,8 @@ +2008-08-26 David Bateman + + * interpreter/plot.txi: Document the group objects (bar, stem, + stair, quiver and area series), and cllback functions. + 2008-08-26 Jaroslav Hajek * interpreter/contrib.txi: New file. diff --git a/doc/interpreter/plot.txi b/doc/interpreter/plot.txi --- a/doc/interpreter/plot.txi +++ b/doc/interpreter/plot.txi @@ -439,7 +439,9 @@ * Colors:: * Line Styles:: * Marker Styles:: -* Interaction with gnuplot:: +* Callbacks:: +* Object Groups:: +* Graphics backends:: @end menu @node Graphics Objects @@ -1340,9 +1342,385 @@ of 2 is twice as large as the default, etc. @end table +@node Callbacks +@subsection Callbacks + +Callback functions can be associated with graphics objects and triggered +after certain events occur. The basic structure of all callback function +is + +@example +@group +function mycallback (src, data) +@dots{} +endfunction +@end group +@end example + +where @code{src} gives a handle to the source of the callback, and +@code{code} gives some event specific data. This can then be associated +with an object either at the objects creation or later with the +@code{set} function. For example + +@example +plot (x, "DeleteFcn", @@(s, e) disp("Window Deleted")) +@end example + +@noindent +where at the moment that the plot is deleted, the message "Window +Deleted" will be displayed. + +Additional user arguments can be passed to callback functions, and will +be passed after the 2 default arguments. For example + +@example +plot (x, "DeleteFcn", {@@mycallback, "1"}) +@dots{} +function mycall (src, data, a1) + fprintf ("Closing plot %d\n", a1); +endfunction +@end example + +The basic callback functions that are available for all graphics objects +are + +@itemize @bullet +@item CreateFcn +This is the callback that is called at the moment of the objects +creation. It is not called if the object is altered in any way, and so +it only makes sense to define this callback in the function call that +defines the object. Callbacks that are added to @code{CreateFcn} later with +the @code{set} function will never be executed. + +@item DeleteFcn +This is the callback that is called at the moment an object is deleted. + +@item ButtonDownFcn +This is the callback that is called if a mouse button is pressed while +the pointer is over this object. Note, that the gnuplot interface does +not respect this callback. +@end itemize + +The object and figure that the event occurred in that resulted in the +callback being called can be found with the @code{gcbo} and @code{gcbf} +functions. + +@DOCSTRING(gcbo) + +@DOCSTRING(gcbf) + +Callbacks can equally be added to properties with the @code{addlistener} +function described below. + +@node Object Groups +@subsection Object Groups + +A number of Octave high level plot functions return groups of other +graphics objects or they return graphics objects that are have their +properties linked in such a way that changes to one of the properties +results in changes in the others. A graphic object that groups other +objects is an @code{hggroup} + +@DOCSTRING(hggroup) + +For example a simple use of a @code{hggroup} might be + +@example +@group +x = 0:0.1:10; +hg = hggroup (); +plot (x, sin (x), "color", [1, 0, 0], "parent", hg); +hold on +plot (x, cos (x), "color", [0, 1, 0], "parent", hg); +set (hg, "visible", "off"); +@end group +@end example + +@noindent +which groups the two plots into a single object and contols their +visiblity directly. The default properties of an @code{hggroup} are +the same as the set of common properties for the other graphics +objects. Additional properties can be added with the @code{addproperty} +function. + +@DOCSTRING(addproperty) + +Once a property in added to an @code{hggroup}, it is not linked to any +other property of either the children of the group, or any other +graphics object. Add so to control the way in which this newly added +property is used, the @code{addlistener} function is used to define a +callback function that is executed when the property is altered. + +@DOCSTRING(addlistener) + +An example of the use of these two functions might be + +@example +@group +x = 0:0.1:10; +hg = hggroup (); +h = plot (x, sin (x), "color", [1, 0, 0], "parent", hg); +addproperty ("linestyle", hg, "linelinestyle", get (h, "linestyle")); +addlistener (hg, "linestyle", @@update_props); +hold on +plot (x, cos (x), "color", [0, 1, 0], "parent", hg); + +function update_props (h, d) + set (get (h, "children"), "linestyle", get (h, "linestyle")); +endfunction +@end group +@end example + +@noindent +that adds a @code{linestyle} property to the @code{hggroup} and +propagating any changes its its value to the children of the group. + +These capabilities are used in a number of basic graphics objects. +The @code{hggroup} objects created by the functions of Octave contain +one or more graphics object and are used to: + +@itemize @bullet +@item group together multiple graphics objects, +@item create linked properties between different graphics objects, and +@item to hide the nominal user data, from the actual data of the objects. +@end itemize + +@noindent +For example the @code{stem} function creates a stem series where each +@code{hggroup} of the stem series contains two line objects representing +the body and head of the stem. The @code{ydata} property of the +@code{hggroup} of the stem series represents the head of the stem, +whereas the body of the stem is between the baseline and this value. For +example + +@example +@group +h = stem (1:4) +get (h, "xdata") +@result{} [ 1 2 3 4]' +get (get (h, "children")(1), "xdata") +@result{} [ 1 1 NaN 2 2 NaN 3 3 NaN 4 4 NaN]' +@end group +@end example + +@noindent +shows the the difference between the @code{xdata} of the @code{hggroup} +of a stem series object and the underlying line. + +The basic properties of such group objects is that they consist of one +or more linked @code{hggroup}, and that changes in certain properties of +these groups are propagated to other members of the group. Whereas, +certain properties of the members of the group only apply to the current +member. + +In addition the members of the group can also be linked to other +graphics objects through callback functions. For example the baseline of +the @code{bar} or @code{stem} functions is a line object, whose length +and position are automatically adjusted, based on changes to the +corresponding hggroup elements. + +@menu +* Area series:: +* Bar series:: +* Contour groups:: +* Error bar series:: +* Line series:: +* Quiver group:: +* Scatter group:: +* Stair group:: +* Stem Series:: +* Surface group:: +@end menu + +@node Area series +@subsubsection Area series + +Area series objects are created by the @code{area} function. Each of the +@code{hggroup} elements contains a single patch object. The properties +of the area series are + +@table @code +@item basevalue +The value where the base of the area plot is drawn. + +@item linewidth +@itemx linestyle +The line width and style of the edge of the patch objects making up the +areas. @xref{Line Styles}. + +@item edgecolor +@itemx facecolor +The line and fill color of the patch objects making up the areas. @xref{Colors}. + +@item xdata +@itemx ydata +The x and y coordinates of the original columns of the data passed to +@code{area} prior to the cummulative summation used in the @code{area} +function. +@end table + +@node Bar series +@subsubsection Bar series + +Bar series objects are created by the @code{bar} or @code{barh} +functions. Each @code{hgrroup} element contains a single patch object. +The properties of the bar series are + +@table @code +@item showbaseline +@itemx baseline +@itemx basevalue +The property @code{showbaseline} flags whether the baseline of the bar +series is displayed (default is "on"). The handle of the graphics object +representing the baseline is given by the @code{baseline} property and +the y-value of the baseline by the @code{basevalue} property. + +Changes to any of these property are propagated to the other members of +the bar series and to the baseline itself. Equally changes in the +properties of the base line itself are propagated to the members of the +corresponding bar series. + +@item barwidth +@itemx barlayout +@itemx horizontal +The property @code{barwidth} is the width of the bar corresponding to +the @var{width} variable passed to @code{bar} or @var{barh}. Whether the +bar series is "grouped" or "stacked" is determined by the +@code{barlayout} property and whether the bars are horizontal or +vertical by the @code{horizontal} property. + +Changes to any of these property are propagated to the other members of +the bar series. + +@item linewidth +@itemx linestyle +The line width and style of the edge of the patch objects making up the +bars. @xref{Line Styles}. + +@item edgecolor +@itemx facecolor +The line and fill color of the patch objects making up the bars. @xref{Colors}. + +@item xdata +The nominal x positions of the bars. Changes in this property and +propagated to the other members of the bar series. + +@item ydata +The y value of the bars in the @code{hggroup}. +@end table + +@node Contour groups +@subsubsection Contour groups + +TO BE WRITTEN + +@node Error bar series +@subsubsection Error bar series + +TO BE WRITTEN + +@node Line series +@subsubsection Line series + +TO BE WRITTEN + +@node Quiver group +@subsubsection Quiver group + +Quiver series objects are created by the @code{quiver} or @code{quiver3} +functions. Each @code{hggroup} element of the series contains three line +objects as children representing the body and head of the arrow, +together with a marker as the point of original of the arrows. The +properties of the quiver series are + +@table @code +@item autoscale +@itemx autoscalefactor +Flag whether the length of the arrows is scaled or defined directly from +the @var{u}, @var{v} and @var{w} data. If the arrow length is falgged +as being scaled by the @code{autoscale} property, then the length of the +autoscaled arrow is controlled by the @code{autoscalefactor}. + +@item maxheadsize +This property controls the size of the head of the arrows in the quiver +series. The default value is 0.2. + +@item showarrowhead +Flag whether the arrow heads are displayed in the quiver plot. + +@item color +The RGB color or color name of the line objects of the quiver. @xref{Colors}. + +@item linewidth +@itemx linestyle +The line width and style of the line objects of the quiver. @xref{Line Styles}. + +@item marker +@itemx markerfacecolor +@itemx markersize +The line and fill color of the marker objects at the original of the +arrows. @xref{Colors}. + +@item xdata +@itemx ydata +@itemx zdata +The origins of the values of the vector field. + +@item udata +@itemx vdata +@itemx wdata +The values of the vector field to plot. +@end table + +@node Scatter group +@subsubsection Scatter group + +TO BE WRITTEN + +@node Stair group +@subsubsection Stair group + +Stair series objects are created by the @code{stair} function. Each +@code{hggroup} element of the series contains a single line object as a +child representing the stair. The properties of the stair series are + +@table @code +@item color +The RGB color or color name of the line objects of the stairs. @xref{Colors}. + +@item linewidth +@itemx linestyle +The line width and style of the line objects of the stairs. @xref{Line Styles}. + +@item marker +@itemx markeredgecolor +@itemx markerfacecolor +@itemx markersize +The line and fill color of the markers on the stairs. @xref{Colors}. + +@item xdata +@itemx ydata +The original x and y data of the stairs. +@end table + +@node Surface group +@subsubsection Surface group + +TO BE WRITTEN + +@node Graphics backends +@subsection Graphics backends + +@DOCSTRING(available_backends) + +@menu +* Interaction with gnuplot:: +@end menu + @node Interaction with gnuplot -@subsection Interaction with @code{gnuplot} +@subsubsection Interaction with @code{gnuplot} @DOCSTRING(gnuplot_binary) @DOCSTRING(gnuplot_use_title_option) + diff --git a/scripts/ChangeLog b/scripts/ChangeLog --- a/scripts/ChangeLog +++ b/scripts/ChangeLog @@ -1,3 +1,23 @@ +2008-08-26 David Bateman + + * plot/__area__.m: Use __next_line_color__ rather than fixed set + of colors. Convert to use area series objects. + * plot/area.m: Update documentation to correspond to + the area series usage. + * plot/__bar.m: Pass the original rather than scaled width to + __bars__. + * plot/__bars__.m: Convert to use bar series objects. + * plot/bar.m, plor/barh.m: Update documentation to correspond to + the bar series usage. + * plot/__quiver__.m: Convert to use quiver series objects. Change + the default scaling and arrowsize for compatibility. + * plot/quiver.m: Update documentation to correspond to + the quiver series usage. + * plot/stairs.m: Convert to use stair series objects. + * plot/stem.m: Don't include baseline in xlim calculation. Correct + test of whether baseline xdata needs updating in the update_xlim + callback. + 2008-08-25 Thomas L. Scofield * image/imwrite.m: Add ras and tiff to the list of accepted formats. @@ -5,11 +25,14 @@ 2008-08-25 David Bateman + * plot/__stem__.m: Use property inheritance and don't explicitly + call drawnow. + * plot/__go_draw_axes__.m: Respect the "visible" property of object and don't draw them if the object is not visible. * plot/__stem__.m: Convert to use stem series object. - * plot/stem.m, plot/stem3.m: Update documentation to correpsond to + * plot/stem.m, plot/stem3.m: Update documentation to correspond to the stem series usage. 2008-08-22 John W. Eaton @@ -37,11 +60,6 @@ * pkg/pkg.m (configure_make): Pass handle to is_architecture_dependent directly. -2008-08-25 David Bateman - - * plot/__stem__.m: Use property inheritance and don't explicitly - call drawnow. - 2008-08-20 David Bateman * plot/__go_draw_axes__.m: Don't set pm3d implicit if the plot diff --git a/scripts/plot/__area__.m b/scripts/plot/__area__.m --- a/scripts/plot/__area__.m +++ b/scripts/plot/__area__.m @@ -20,20 +20,99 @@ function retval = __area__ (ax, x, y, bv, varargin) - colors = [1, 0, 0; 0, 1, 0; 0, 0, 1; 1, 1, 0; 1, 0, 1; 0, 1, 1]; + y0 = bv * ones (1, rows (y)); + y0 = zeros (1, rows (y)); + retval = []; + for i = 1: size (y, 2); + hg = hggroup (); + x1 = x(:, 1).'; + y1 = y (:, i).'; + addproperty ("xdata", hg, "data", x1); + addproperty ("ydata", hg, "data", y1); - x = [x(1,:); x; x(end,:)]; - - y = cumsum ([[bv, ones(1, size (y, 2) - 1)]; y; - [bv, ones(1, size (y, 2) - 1)]], 2); + addlistener (hg, "xdata", @update_data); + addlistener (hg, "ydata", @update_data); - retval = patch (ax, x(:,1), y(:,1), colors(1,:), varargin{:}); + if (i == 1) + h = patch (ax, [x1(1), x1, fliplr(x1)], [bv, y1, bv*ones(1, length(y1))], + __next_line_color__ (), "parent", hg, varargin{:}); + else + y1 = y0 + y1; + h = patch (ax, [x1(1), x1, fliplr(x1)], [y0(1), y1, fliplr(y0)], + __next_line_color__ (), "parent", hg, varargin{:}); + endif + + y0 = y1; + + addproperty ("basevalue", hg, "data", bv); + addlistener (hg, "basevalue", @move_baseline); - for i = 2:size(y,2) - tmp = patch (ax, [x(:,i); flipud(x(:,i))], - [y(:,i) ; flipud(y(:, i-1))], colors(i,:), varargin{:}); + addproperty ("edgecolor", hg, "patchedgecolor", get (h, "edgecolor")); + addproperty ("linewidth", hg, "patchlinewidth", get (h, "linewidth")); + addproperty ("linestyle", hg, "patchlinestyle", get (h, "linestyle")); + addproperty ("facecolor", hg, "patchfacecolor", get (h, "facecolor")); - retval = [retval; tmp]; + addlistener (hg, "edgecolor", @update_props); + addlistener (hg, "linewidth", @update_props); + addlistener (hg, "linestyle", @update_props); + addlistener (hg, "facecolor", @update_props); + + retval = [retval; hg]; + addproperty ("areagroup", hg, "data"); + set (retval, "areagroup", retval); endfor endfunction + +function update_props (h, d) + kids = get (h, "children"); + set (kids, "edgecolor", get (h, "edgecolor"), + "linewidth", get (h, "linewidth"), + "linestyle", get (h, "linestyle"), + "facecolor", get (h, "facecolor")); +endfunction + +function move_baseline (h, d) + persistent recursion = false; + + ## Don't allow recursion + if (! recursion) + unwind_protect + recursion = true; + hlist = get (h, "areagroup"); + b0 = get (h, "basevalue"); + + for hh = hlist(:)' + if (hh != h ) + b1 = get (hh, "basevalue"); + if (b1 != b0) + set (hh, "basevalue", b0); + endif + endif + endfor + update_data (h, d); + unwind_protect_cleanup + recursion = false; + end_unwind_protect + endif +endfunction + +function update_data (h, d) + hlist = get (h, "areagroup"); + bv = get (h, "basevalue"); + for i = 1 : length (hlist) + hh = hlist(i); + x1 = get (hh, "xdata")(:); + y1 = get (hh, "ydata")(:); + + set (get (hh, "children"), "xdata", [x1(1); x1; flipud(x1)]); + if (i == 1) + set (get (hh, "children"), "ydata", [bv; y1; bv*ones(length(y1), 1)]); + else + y1 = y0 + y1; + set (get (hh, "children"), "ydata", [y0(1); y1; flipud(y0)]); + endif + + y0 = y1; + endfor +endfunction diff --git a/scripts/plot/__bar__.m b/scripts/plot/__bar__.m --- a/scripts/plot/__bar__.m +++ b/scripts/plot/__bar__.m @@ -110,18 +110,17 @@ endif ycols = size (y, 2); + cutoff = min (diff (double(x))) / 2; if (group) - width = width / ycols; + delta_p = delta_m = repmat (cutoff * width / ycols, size (x)); + else + delta_p = delta_m = repmat (cutoff * width, size (x)); endif - - cutoff = min (diff (double(x))) / 2; - delta_p = delta_m = repmat (cutoff * width, size (x)); x1 = (x - delta_m)(:)'; x2 = (x + delta_p)(:)'; xb = repmat ([x1; x1; x2; x2](:), 1, ycols); if (group) - width = width / ycols; offset = ((delta_p + delta_m) * [-(ycols - 1) / 2 : (ycols - 1) / 2]); xb(1:4:4*ylen,:) += offset; xb(2:4:4*ylen,:) += offset; diff --git a/scripts/plot/__bars__.m b/scripts/plot/__bars__.m --- a/scripts/plot/__bars__.m +++ b/scripts/plot/__bars__.m @@ -20,43 +20,245 @@ ## Author: jwe -function tmp = __bars__ (h, vertical, x, y, xb, yb, width, group, have_color_spec, base_value, varargin) - - ## Note, base_value is used by the Jhandles backend, which replaces - ## this function with its own version. +function tmp = __bars__ (ax, vertical, x, y, xb, yb, width, group, have_color_spec, base_value, varargin) ycols = columns (y); - clim = get (h, "clim"); + clim = get (ax, "clim"); + tmp = []; - if (vertical) - tmp = []; - for i = 1:ycols + for i = 1:ycols + hg = hggroup (); + tmp = [tmp; hg]; + + if (vertical) if (! have_color_spec) if (ycols == 1) lev = clim(1); else lev = (i - 1) * (clim(2) - clim(1)) / (ycols - 1) - clim(1); endif - tmp = [tmp; patch(xb(:,:,i), yb(:,:,i), "FaceColor", "flat", - "cdata", lev, varargin{:})]; + h = patch(xb(:,:,i), yb(:,:,i), "FaceColor", "flat", + "cdata", lev, "parent", hg, varargin{:}); else - tmp = [tmp; patch(xb(:,:,i), yb(:,:,i), varargin{:})]; + h = patch(xb(:,:,i), yb(:,:,i), "parent", hg, varargin{:}); endif - endfor - else - tmp = []; - for i = 1:ycols + else if (! have_color_spec) if (ycols == 1) lev = clim(1) else lev = (i - 1) * (clim(2) - clim(1)) / (ycols - 1) - clim(1); endif - tmp = [tmp; patch(yb(:,:,i), xb(:,:,i), "FaceColor", "flat", - "cdata", lev, varargin{:})]; + h = patch(yb(:,:,i), xb(:,:,i), "FaceColor", "flat", + "cdata", lev, "parent", hg, varargin{:}); else - tmp = [tmp; patch(yb(:,:,i), xb(:,:,i), varargin{:})]; + h = patch(yb(:,:,i), xb(:,:,i), "parent", hg, varargin{:}); endif - endfor + endif + + if (i == 1) + x_axis_range = get (ax, "xlim"); + h_baseline = line (x_axis_range, [0, 0], "color", [0, 0, 0]); + set (h_baseline, "handlevisibility", "off"); + set (h_baseline, "xliminclude", "off"); + addlistener (ax, "xlim", @update_xlim); + addlistener (h_baseline, "ydata", @update_baseline); + addlistener (h_baseline, "visible", @update_baseline); + endif + + ## Setup the hggroup and listeners + addproperty ("showbaseline", hg, "radio", "{on}|off"); + addproperty ("basevalue", hg, "data", base_value); + addproperty ("baseline", hg, "data", h_baseline); + + addlistener (hg, "showbaseline", @show_baseline); + addlistener (hg, "basevalue", @move_baseline); + + addproperty ("barwidth", hg, "data", width); + if (group) + addproperty ("barlayout", hg, "radio", "stacked|{grouped}", "grouped"); + else + addproperty ("barlayout", hg, "radio", "{stacked}|grouped", "stacked"); + endif + if (vertical) + addproperty ("horizontal", hg, "radio", "on|{off}", "off") + else + addproperty ("horizontal", hg, "radio", "{on}|off", "on") + endif + + addlistener (hg, "barwidth", @update_group); + addlistener (hg, "barlayout", @update_group); + addlistener (hg, "horizontal", @update_group); + + addproperty ("edgecolor", hg, "patchedgecolor", get (h, "edgecolor")); + addproperty ("linewidth", hg, "patchlinewidth", get (h, "linewidth")); + addproperty ("linestyle", hg, "patchlinestyle", get (h, "linestyle")); + addproperty ("facecolor", hg, "patchfacecolor", get (h, "facecolor")); + + addlistener (hg, "edgecolor", @update_props); + addlistener (hg, "linewidth", @update_props); + addlistener (hg, "linestyle", @update_props); + addlistener (hg, "facecolor", @update_props); + + if (isvector (x)) + addproperty ("xdata", hg, "data", x); + else + addproperty ("xdata", hg, "data", x(:, i)); + endif + addproperty ("ydata", hg, "data", y(:, i)); + + addlistener (hg, "xdata", @update_data); + addlistener (hg, "ydata", @update_data); + + addproperty ("bargroup", hg, "data"); + set (tmp, "bargroup", tmp); + endfor + + update_xlim (ax, []); +endfunction + +function update_xlim (h, d) + kids = get (h, "children"); + xlim = get (h, "xlim"); + + for i = 1 : length (kids) + obj = get (kids (i)); + if (strcmp (obj.type, "hggroup") && isfield (obj, "baseline")) + if (any (get (obj.baseline, "xdata") != xlim)) + set (obj.baseline, "xdata", xlim); + endif + endif + endfor +endfunction + +function update_baseline (h, d) + visible = get (h, "visible"); + ydata = get (h, "ydata")(1); + + kids = get (get (h, "parent"), "children"); + for i = 1 : length (kids) + obj = get (kids (i)); + if (strcmp (obj.type, "hggroup") && isfield (obj, "baseline") + && obj.baseline == h) + ## Only alter if changed to avoid recursion of the listener functions + if (! strcmp (get (kids(i), "showbaseline"), visible)) + set (kids (i), "showbaseline", visible); + endif + if (! strcmp (get (kids(i), "basevalue"), visible)) + set (kids (i), "basevalue", ydata); + endif + endif + endfor +endfunction + +function show_baseline (h, d) + persistent recursion = false; + + ## Don't allow recursion + if (! recursion) + unwind_protect + recursion = true; + hlist = get (h, "bargroup"); + showbaseline = get (h, "showbaseline"); + for hh = hlist(:)' + if (hh != h) + set (hh, "showbaseline", showbaseline); + endif + endfor + set (get (h, "baseline"), "visible", showbaseline); + unwind_protect_cleanup + recursion = false; + end_unwind_protect endif endfunction + +function move_baseline (h, d) + b0 = get (h, "basevalue"); + bl = get (h, "baseline"); + + if (get (bl, "ydata") != [b0, b0]) + set (bl, "ydata", [b0, b0]); + endif + + if (strcmp (get (h, "barlayout"), "grouped")) + update_data (h, d); + endif +endfunction + +function update_props (h, d) + kids = get (h, "children"); + set (kids, "edgecolor", get (h, "edgecolor"), + "linewidth", get (h, "linewidth"), + "linestyle", get (h, "linestyle"), + "facecolor", get (h, "facecolor")); +endfunction + +function update_data (h, d) + persistent recursion = false; + + ## Don't allow recursion + if (! recursion) + unwind_protect + recursion = true; + hlist = get (h, "bargroup"); + x = get (h, "xdata"); + if (!isvector (x)) + x = x(:); + endif + y = []; + for hh = hlist(:)' + ytmp = get (hh, "ydata"); + y = [y ytmp(:)]; + endfor + + [xb, yb] = bar (x, y, get (h, "barwidth"), get (h, "barlayout"), + "basevalue", get (h, "basevalue")); + ny = columns (y); + vert = strcmp (get (h, "horizontal"), "off"); + + for i = 1:ny + hp = get (hlist(i), "children"); + if (vert) + set (hp, "xdata", xb(:,:,i), "ydata", yb(:,:,i)); + else + set (hp, "xdata", yb(:,:,i), "ydata", xb(:,:,i)); + endif + endfor + unwind_protect_cleanup + recursion = false; + end_unwind_protect + endif +endfunction + +function update_group (h, d) + persistent recursion = false; + + ## Don't allow recursion + if (! recursion) + unwind_protect + recursion = true; + hlist = get (h, "bargroup"); + barwidth = get(h, "barwidth"); + barlayout = get (h, "barlayout"); + horizontal = get (h, "horizontal"); + + ## To prevent recursion, only change if modified + for hh = hlist(:)' + if (hh != h) + if (get (hh, "barwidth") != barwidth) + set (hh, "barwidth", barwidth); + endif + if (! strcmp (get (hh, "barlayout"), barlayout)) + set (hh, "barlayout", barlayout); + endif + if (! strcmp (get (hh, "horizontal"), horizontal)) + set (hh, "horizontal", horizontal); + endif + endif + endfor + update_data (h, d); + unwind_protect_cleanup + recursion = false; + end_unwind_protect + endif +endfunction diff --git a/scripts/plot/__quiver__.m b/scripts/plot/__quiver__.m --- a/scripts/plot/__quiver__.m +++ b/scripts/plot/__quiver__.m @@ -18,13 +18,13 @@ ## Undocumented internal function -function hlist = __quiver__ (varargin) +function hg = __quiver__ (varargin) h = varargin{1}; is3d = varargin{2}; - s = 1; - arrowsize = 0.33; + autoscale = 0.9; + arrowsize = 0.2; firstnonnumeric = Inf; for i = 3:nargin @@ -46,7 +46,7 @@ endif if (nargin >= ioff && isnumeric (varargin{ioff}) && isscalar (varargin{ioff})) - s = varargin{ioff++}; + autoscale = varargin{ioff++}; endif else x = varargin{ioff++}; @@ -69,7 +69,7 @@ endif if (nargin >= ioff && isnumeric (varargin{ioff}) && isscalar (varargin{ioff})) - s = varargin{ioff++}; + autoscale = varargin{ioff++}; endif endif @@ -95,7 +95,7 @@ endif endwhile - if (s) + if (autoscale) ## Scale the arrows to fit in the grid dx = (max(x(:)) - min(x(:))) ./ size (x, 2); dy = (max(y(:)) - min(y(:))) ./ size (y, 1); @@ -108,7 +108,219 @@ dz = 0; endif if (len > 0) - s = s / sqrt (2) * sqrt (dx.^2 + dy.^2 + dz.^2) / len; + s = 2 * autoscale / sqrt (2) * sqrt (dx.^2 + dy.^2 + dz.^2) / len; + uu = s * u; + vv = s * v; + if (is3d) + ww = s*w; + endif + endif + endif + + hstate = get (h, "nextplot"); + unwind_protect + hg = hggroup (); + hold on; + + addproperty ("xdata", hg, "data", x); + addproperty ("ydata", hg, "data", y); + + addproperty ("udata", hg, "data", u); + addproperty ("vdata", hg, "data", v); + if (is3d) + addproperty ("zdata", hg, "data", z); + addproperty ("wdata", hg, "data", w); + else + addproperty ("zdata", hg, "data", []); + addproperty ("wdata", hg, "data", []); + endif + + addlistener (hg, "xdata", @update_data); + addlistener (hg, "ydata", @update_data); + addlistener (hg, "zdata", @update_data); + addlistener (hg, "udata", @update_data); + addlistener (hg, "vdata", @update_data); + addlistener (hg, "wdata", @update_data); + + x = x(:); + y = y(:); + xend = x + uu(:); + yend = y + vv(:); + if (is3d) + z = z(:); + zend = z + ww(:); + endif + + if (have_line_spec) + if (is3d) + h1 = plot3 ([x.'; xend.'; NaN(1, length (x))](:), + [y.'; yend.'; NaN(1, length (y))](:), + [z.'; zend.'; NaN(1, length (z))](:), + "linestyle", linespec.linestyle, + "color", linespec.color, "parent", hg); + else + h1 = plot ([x.'; xend.'; NaN(1, length (x))](:), + [y.'; yend.'; NaN(1, length (y))](:), + "linestyle", linespec.linestyle, + "color", linespec.color, "parent", hg); + endif + else + if (is3d) + h1 = plot3 ([x.'; xend.'; NaN(1, length (x))](:), + [y.'; yend.'; NaN(1, length (y))](:), + [z.'; zend.'; NaN(1, length (z))](:), + "parent", hg); + else + h1 = plot ([x.'; xend.'; NaN(1, length (x))](:), + [y.'; yend.'; NaN(1, length (y))](:), + "parent", hg); + endif + endif + + xtmp = x + uu(:) .* (1 - arrowsize); + ytmp = y + vv(:) .* (1 - arrowsize); + xarrw1 = xtmp + (y - yend) * arrowsize / 3; + xarrw2 = xtmp - (y - yend) * arrowsize / 3; + yarrw1 = ytmp - (x - xend) * arrowsize / 3; + yarrw2 = ytmp + (x - xend) * arrowsize / 3; + if (is3d) + zarrw1 = zarrw2 = zend - ww(:) * arrowsize; + endif + + if (have_line_spec) + if (isfield (linespec, "marker") && + ! strncmp (linespec.marker, "none", 4)) + if (is3d) + h2 = plot3 ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), + [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), + [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:), + "linestyle", "none", "parent", hg); + else + h2 = plot ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), + [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), + "linestyle", "none", "parent", hg); + endif + else + if (is3d) + h2 = plot3 ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), + [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), + [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:), + "linestyle", linespec.linestyle, + "color", linespec.color, "parent", hg); + else + h2 = plot ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), + [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), + "linestyle", linespec.linestyle, + "color", linespec.color, "parent", hg); + endif + endif + elseif (is3d) + h2 = plot3 ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), + [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), + [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:), + "parent", hg); + else + h2 = plot ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), + [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), + "parent", hg); + endif + + if (! have_line_spec + || (isfield (linespec, "marker") + && strncmp (linespec.marker, "none", 4))) + if (is3d) + h3 = plot3 (x, y, z, "linestyle", "none", "marker", "none", + "parent", hg); + else + h3 = plot (x, y, "linestyle", "none", "marker", "none", "parent", hg); + endif + else + if (is3d) + h3 = plot3 (x, y, z, "linestyle", "none", "marker", linespec.marker, + "parent", hg); + else + + h3 = plot (x, y, "linestyle", "none", "marker", linespec.marker, + "parent", hg); + endif + endif + if (have_filled) + ## FIXME gnuplot doesn't respect the markerfacecolor field + set (h3, "markerfacecolor", get (h1, "color")); + endif + + ## Set up the hggroup properties and listeners + if (autoscale) + addproperty ("autoscale", hg, "radio", "{on}|off", "on"); + addproperty ("autoscalefactor", hg, "data", autoscale) + else + addproperty ("autoscale", hg, "radio", "{on}|off", "off"); + addproperty ("autoscalefactor", hg, "data", 1.0) + endif + addlistener (hg, "autoscale", @update_data) + addlistener (hg, "autoscalefactor", @update_data) + + addproperty ("maxheadsize", hg, "data", arrowsize) + addlistener (hg, "maxheadsize", @update_data); + + addproperty ("showarrowhead", hg, "radio", "{on}|off", "on"); + addlistener (hg, "showarrowhead", @update_props); + + addproperty ("color", hg, "linecolor", get (h1, "color")); + addproperty ("linewidth", hg, "linelinewidth", get (h1, "linewidth")); + addproperty ("linestyle", hg, "linelinestyle", get (h1, "linestyle")); + addproperty ("marker", hg, "linemarker", get (h3, "marker")); + addproperty ("markerfacecolor", hg, "linemarkerfacecolor", + get (h3, "markerfacecolor")); + addproperty ("markersize", hg, "linemarkersize", get (h3, "markersize")); + + addlistener (hg, "color", @update_props); + addlistener (hg, "linewidth", @update_props); + addlistener (hg, "linestyle", @update_props); + addlistener (hg, "marker", @update_props); + addlistener (hg, "markerfacecolor", @update_props); + addlistener (hg, "markersize", @update_props); + + unwind_protect_cleanup + set (h, "nextplot", hstate); + end_unwind_protect + +endfunction + +function update_data (h, d) + x = get (h, "xdata"); + y = get (h, "ydata"); + z = get (h, "zdata"); + + u = get (h, "udata"); + v = get (h, "vdata"); + w = get (h, "wdata"); + + s = get (h, "autoscalefactor"); + arrowsize = get (h, "maxheadsize"); + + kids = get (h, "children"); + + if (isempty (z) || isempty (w)) + is3d = false; + else + is3d = true; + endif + + if (strcmp (get (h, "autoscale"), "on") && s != 0) + ## Scale the arrows to fit in the grid + dx = (max(x(:)) - min(x(:))) ./ size (x, 2); + dy = (max(y(:)) - min(y(:))) ./ size (y, 1); + if (is3d) + ## What should this be divided by? The below seems right + dz = (max(z(:)) - min(z(:))) ./ max (size (z)); + len = max (sqrt (u(:).^2 + dy(:).^2) + dz(:).^2); + else + len = max (sqrt (u(:).^2 + dy(:).^2)); + dz = 0; + endif + if (len > 0) + s = 2 * s / sqrt (2) * sqrt (dx.^2 + dy.^2 + dz.^2) / len; u = s * u; v = s * v; if (is3d) @@ -126,103 +338,58 @@ zend = z + w(:); endif - hstate = get (h, "nextplot"); - unwind_protect - if (have_line_spec) - if (is3d) - h1 = plot3 ([x.'; xend.'; NaN(1, length (x))](:), - [y.'; yend.'; NaN(1, length (y))](:), - [z.'; zend.'; NaN(1, length (z))](:), - "linestyle", linespec.linestyle, - "color", linespec.color); - else - h1 = plot ([x.'; xend.'; NaN(1, length (x))](:), - [y.'; yend.'; NaN(1, length (y))](:), - "linestyle", linespec.linestyle, - "color", linespec.color); - endif - else - if (is3d) - h1 = plot3 ([x.'; xend.'; NaN(1, length (x))](:), - [y.'; yend.'; NaN(1, length (y))](:), - [z.'; zend.'; NaN(1, length (z))](:)); - else - h1 = plot ([x.'; xend.'; NaN(1, length (x))](:), - [y.'; yend.'; NaN(1, length (y))](:)); - endif - endif - hold on; + set (kids (1), "xdata", [x.'; xend.'; NaN(1, length (x))](:)); + set (kids (1), "ydata", [y.'; yend.'; NaN(1, length (y))](:)); + if (is3d) + set (kids (1), "zdata", [z.'; zend.'; NaN(1, length (z))](:)); + endif - xtmp = x + u(:) .* (1 - arrowsize); - ytmp = y + v(:) .* (1 - arrowsize); - xarrw1 = xtmp + (y - yend) * arrowsize / 3; - xarrw2 = xtmp - (y - yend) * arrowsize / 3; - yarrw1 = ytmp - (x - xend) * arrowsize / 3; - yarrw2 = ytmp + (x - xend) * arrowsize / 3; - if (is3d) - zarrw1 = zarrw2 = zend - w(:) * arrowsize; - endif + xtmp = x + u(:) .* (1 - arrowsize); + ytmp = y + v(:) .* (1 - arrowsize); + xarrw1 = xtmp + (y - yend) * arrowsize / 3; + xarrw2 = xtmp - (y - yend) * arrowsize / 3; + yarrw1 = ytmp - (x - xend) * arrowsize / 3; + yarrw2 = ytmp + (x - xend) * arrowsize / 3; + if (is3d) + zarrw1 = zarrw2 = zend - w(:) * arrowsize; + endif - if (have_line_spec) - if (isfield (linespec, "marker") && - ! strncmp (linespec.marker, "none", 4)) - if (is3d) - h2 = plot3 ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), - [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), - [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:), - "linestyle", "none"); - else - h2 = plot ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), - [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), - "linestyle", "none"); - endif - else - if (is3d) - h2 = plot3 ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), - [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), - [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:), - "linestyle", linespec.linestyle, - "color", linespec.color); - else - h2 = plot ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), - [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), - "linestyle", linespec.linestyle, - "color", linespec.color); - endif - endif - elseif (is3d) - h2 = plot3 ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), - [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), - [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:)); - else - h2 = plot ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), - [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:)); - endif + set (kids (2), "xdata", [x.'; xend.'; NaN(1, length (x))](:)); + set (kids (2), "ydata", [y.'; yend.'; NaN(1, length (y))](:)); + if (is3d) + set (kids (2), "zdata", [z.'; zend.'; NaN(1, length (z))](:)); + endif - if (! have_line_spec - || (isfield (linespec, "marker") - && strncmp (linespec.marker, "none", 4))) - if (is3d) - h3 = plot3 (x, y, z, "linestyle", "none", "marker", "none"); - else - h3 = plot (x, y, "linestyle", "none", "marker", "none"); - endif - else - if (is3d) - h3 = plot3 (x, y, z, "linestyle", "none", "marker", linespec.marker); - else + set (kids (2), "xdata", [xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:)); + set (kids (2), "ydata", [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:)); + if (is3d) + set (kids (2), "zdata", [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:)); + endif - h3 = plot (x, y, "linestyle", "none", "marker", linespec.marker); - endif - endif - if (have_filled) - ## FIXME gnuplot doesn't respect the markerfacecolor field - set (h3, "markerfacecolor", get (h1, "color")); - endif - unwind_protect_cleanup - set (h, "nextplot", hstate); - end_unwind_protect - - hlist = [h1; h2; h3]; + set (kids (3), "xdata", x); + set (kids (3), "ydata", y); + if (is3d) + set (kids (3), "zdata", z); + endif endfunction + +function update_props (h, d) + kids = get (h, "children"); + + set (kids(1), "color", get (h, "color"), + "linewidth", get (h, "linewidth"), + "linestyle", get (h, "linestyle")); + set (kids(2), "color", get (h, "color"), + "linewidth", get (h, "linewidth"), + "linestyle", get (h, "linestyle")); + if (strcmp (get (h, "showarrowhead"), "on")) + set (kids (2), "visible", "on"); + else + set (kids (2), "visible", "off"); + endif + set (kids(3), "color", get (h, "color"), + "marker", get (h, "marker"), + "markerfacecolor", get (h, "markerfacecolor"), + "markersize", get (h, "markersize")); +endfunction diff --git a/scripts/plot/__stem__.m b/scripts/plot/__stem__.m --- a/scripts/plot/__stem__.m +++ b/scripts/plot/__stem__.m @@ -97,6 +97,7 @@ x_axis_range = get (ax, "xlim"); h_baseline = line (x_axis_range, [0, 0], "color", [0, 0, 0]); set (h_baseline, "handlevisibility", "off"); + set (h_baseline, "xliminclude", "off"); addlistener (ax, "xlim", @update_xlim); addlistener (h_baseline, "ydata", @update_baseline); addlistener (h_baseline, "visible", @update_baseline); @@ -428,7 +429,7 @@ for i = 1 : length (kids) obj = get (kids (i)); if (strcmp (obj.type, "hggroup") && isfield (obj, "baseline")) - if (get (obj.baseline, "xdata") != xlim) + if (any (get (obj.baseline, "xdata") != xlim)) set (obj.baseline, "xdata", xlim); endif endif diff --git a/scripts/plot/area.m b/scripts/plot/area.m --- a/scripts/plot/area.m +++ b/scripts/plot/area.m @@ -34,7 +34,7 @@ ## ## Additional arguments to the @code{area} function are passed to the ## @code{patch}. The optional return value @var{h} provides a handle to -## the list of patch objects. +## area series object representing the patches of the areas. ## @seealso{plot, patch} ## @end deftypefn diff --git a/scripts/plot/bar.m b/scripts/plot/bar.m --- a/scripts/plot/bar.m +++ b/scripts/plot/bar.m @@ -37,12 +37,27 @@ ## argument, which can take the values @code{"grouped"} (the default), ## or @code{"stacked"}. ## -## The optional return value @var{h} provides a handle to the patch object. -## Whereas the option input handle @var{h} allows an axis handle to be passed. -## Properties of the patch graphics object can be changed using +## The optional return value @var{h} provides a handle to the "bar series" +## object with one handle per column of the variable @var{y}. This +## series allows common elements of the group of bar series objects to +## be changed in a single bar series and the same properties are changed +## in the other "bar series". For example +## +## @example +## @group +## h = bar (rand (5, 10)); +## set (h(1), "basevalue", 0.5); +## @end group +## @end example +## +## @noindent +## changes the position on the base of all of the bar series. +## +## The optional input handle @var{h} allows an axis handle to be passed. +## Properties of the patch graphics object can be changed using ## @var{prop}, @var{val} pairs. ## -## @seealso{barh, plot} +## @seealso{barh, plot} ## @end deftypefn ## Author: jwe diff --git a/scripts/plot/barh.m b/scripts/plot/barh.m --- a/scripts/plot/barh.m +++ b/scripts/plot/barh.m @@ -36,9 +36,11 @@ ## argument, which can take the values @code{"grouped"} (the default), ## or @code{"stacked"}. ## -## The optional return value @var{h} provides a handle to the patch object. -## Whereas the option input handle @var{h} allows an axis handle to be passed. -## Properties of the patch graphics object can be changed using +## The optional return value @var{h} provides a handle to the bar series +## object. See @code{bar} for a description of the use of the bar series. +## +## The optional input handle @var{h} allows an axis handle to be passed. +## Properties of the patch graphics object can be changed using ## @var{prop}, @var{val} pairs. ## ## @seealso{bar, plot} diff --git a/scripts/plot/quiver.m b/scripts/plot/quiver.m --- a/scripts/plot/quiver.m +++ b/scripts/plot/quiver.m @@ -43,13 +43,15 @@ ## printed rather than arrows. If the argument 'filled' is given then the ## markers as filled. ## -## The optional return value @var{h} provides a list of handles to the -## the parts of the vector field (body, arrow and marker). +## The optional return value @var{h} provides a quiver group that +## regroups the components of the quiver plot (body, arrow and marker), +## and allows them to be changed together ## ## @example ## @group ## [x, y] = meshgrid (1:2:20); -## quiver (x, y, sin (2*pi*x/10), sin (2*pi*y/10)); +## h = quiver (x, y, sin (2*pi*x/10), sin (2*pi*y/10)); +## set (h, "maxheadsize", 0.33); ## @end group ## @end example ## @@ -81,7 +83,8 @@ %!demo %! [x,y] = meshgrid(1:2:20); -%! quiver(x,y,sin(2*pi*x/10),sin(2*pi*y/10)) +%! h = quiver(x,y,sin(2*pi*x/10),sin(2*pi*y/10)) +%! set (h, "maxheadsize", 0.33); %!demo %! axis("equal"); diff --git a/scripts/plot/quiver3.m b/scripts/plot/quiver3.m --- a/scripts/plot/quiver3.m +++ b/scripts/plot/quiver3.m @@ -43,8 +43,9 @@ ## printed rather than arrows. If the argument 'filled' is given then the ## markers as filled. ## -## The optional return value @var{h} provides a list of handles to the -## the parts of the vector field (body, arrow and marker). +## The optional return value @var{h} provides a quiver group that +## regroups the components of the quiver plot (body, arrow and marker), +## and allows them to be changed together ## ## @example ## @group @@ -52,7 +53,8 @@ ## surf (x, y, z); ## hold on; ## [u, v, w] = surfnorm (x, y, z / 10); -## quiver3 (x, y, z, u, v, w); +## h = quiver3 (x, y, z, u, v, w); +## set (h, "maxheadsize", 0.33); ## @end group ## @end example ## @@ -96,4 +98,5 @@ %! surf (x, y, z); %! hold on; %! [u, v, w] = surfnorm (x, y, z / 10); -%! quiver3 (x, y, z, u, v, w); +%! h = quiver3 (x, y, z, u, v, w); +%! set (h, "maxheadsize", 0.33); diff --git a/scripts/plot/stairs.m b/scripts/plot/stairs.m --- a/scripts/plot/stairs.m +++ b/scripts/plot/stairs.m @@ -128,17 +128,59 @@ xs(1,:) = x(1,:); ys(1,:) = y(1,:); - x = x(2:nr,:); + xtmp = x(2:nr,:); ridx = 2:2:len-1; - xs(ridx,:) = x; + xs(ridx,:) = xtmp; ys(ridx,:) = y(1:nr-1,:); ridx = 3:2:len; - xs(ridx,:) = x; + xs(ridx,:) = xtmp; ys(ridx,:) = y(2:nr,:); if (doplot) - h = plot (xs, ys, varargin{idx+1:end}); + h = []; + unwind_protect + hold_state = get (gca (), "nextplot"); + for i = 1 : size(y, 2) + hg = hggroup (); + h = [h; hg]; + + if (i == 1) + set (gca (), "nextplot", "add"); + endif + + addproperty ("xdata", hg, "data", x(:,i).'); + addproperty ("ydata", hg, "data", y(:,i).'); + + addlistener (hg, "xdata", @update_data); + addlistener (hg, "ydata", @update_data); + + tmp = line (xs(:,i).', ys(:,i).', "color", __next_line_color__ (), + "parent", hg, varargin{idx+1:end}); + + addproperty ("color", hg, "linecolor", get (tmp, "color")); + addproperty ("linewidth", hg, "linelinewidth", get (tmp, "linewidth")); + addproperty ("linestyle", hg, "linelinestyle", get (tmp, "linestyle")); + + addproperty ("marker", hg, "linemarker", get (tmp, "marker")); + addproperty ("markerfacecolor", hg, "linemarkerfacecolor", + get (tmp, "markerfacecolor")); + addproperty ("markeredgecolor", hg, "linemarkeredgecolor", + get (tmp, "markeredgecolor")); + addproperty ("markersize", hg, "linemarkersize", + get (tmp, "markersize")); + + addlistener (hg, "color", @update_props); + addlistener (hg, "linewidth", @update_props); + addlistener (hg, "linestyle", @update_props); + addlistener (hg, "marker", @update_props); + addlistener (hg, "markerfacecolor", @update_props); + addlistener (hg, "markeredgecolor", @update_props); + addlistener (hg, "markersize", @update_props); + endfor + unwind_protect_cleanup + set (gca (), "nextplot", hold_state); + end_unwind_protect else h = 0; endif @@ -148,10 +190,43 @@ %!demo %! x = 1:10; %! y = rand (1, 10); -## stairs (x, y); +%! stairs (x, y); %!demo %! x = 1:10; %! y = rand (1, 10); %! [xs, ys] = stairs (x, y); %! plot (xs, ys); + +function update_props (h, d) + set (get (h, "children"), "color", get (h, "color"), + "linewidth", get (h, "linewidth"), + "linestyle", get (h, "linestyle"), + "marker", get (h, "marker"), + "markerfacecolor", get (h, "markerfacecolor"), + "markeredgecolor", get (h, "markeredgecolor"), + "markersize", get (h, "markersize")); +endfunction + +function update_data (h, d) + x = get (h, "xdata"); + y = get (h, "ydata"); + + nr = length (x); + len = 2 * nr - 1; + xs = ys = zeros (1, len); + + xs(1) = x(1); + ys(1) = y(1); + + xtmp = x(2:nr); + ridx = 2:2:len-1; + xs(ridx) = xtmp; + ys(ridx) = y(1:nr-1); + + ridx = 3:2:len; + xs(ridx) = xtmp; + ys(ridx) = y(2:nr); + + set (get (h, "children"), "xdata", xs, "ydata", ys); +endfunction