view scripts/plot/private/__scatter__.m @ 17481:576cf0589c6d

Overhaul contour labeling functions. * scripts/plot/clabel.m: Use 'h', rather than 'retval', to match variables to documentation. Improve performance of input processing by using try/catch block and eliminating for loops. * scripts/plot/private/__clabel__.m: Get X and Y spacing in points from axis rather than assuming 4"x3" plot figure. Fix incorrect determination of axis limits if no contour handle provided. Rename loop vars i1, j1 to i,j. Performance improvement by using bsxfun over repmat. Use find to replace while loop (slow). Keep label rotation in the range [-90, 90] for readability.
author Rik <rik@octave.org>
date Tue, 24 Sep 2013 13:16:50 -0700
parents 177147bf7b55
children
line wrap: on
line source

## Copyright (C) 2007-2012 David Bateman
##
## 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
## <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn {Function File} {@var{hg} =} __scatter__ (@dots{})
## Undocumented internal function.
## @end deftypefn

function hg = __scatter__ (varargin)

  hax = varargin{1};  # We don't do anything with this.  Could remove it.
  nd  = varargin{2};
  fcn = varargin{3};
  x   = varargin{4}(:);
  y   = varargin{5}(:);

  if (nd == 2)
    istart = 6;
  else
    z = varargin{6}(:);
    istart = 7;
  endif

  if (istart <= nargin)
    s = varargin{istart}(:);
    if (isempty (s) || ischar (s))
      s = 6;
    endif
    if (! ischar (varargin{istart}))
      istart++;
    endif
  else
    s = 6;
  endif

  ## Remove NaNs
  idx = isnan (x) | isnan (y) | isnan (s);
  if (nd == 3)
    idx |= isnan (z);
    z(idx) = [];
  endif
  x(idx) = [];
  y(idx) = [];
  if (nd == 2)
    z = zeros (length (x), 0);
  endif
  if (numel (s) > 1)
    s(idx) = [];
  endif

  firstnonnumeric = find (! cellfun ("isnumeric", varargin(istart:nargin)), 1);
  if (isempty (firstnonnumeric))
    firstnonnumeric = Inf;
  else
    firstnonnumeric += istart - 1;
  endif

  if (istart <= nargin && firstnonnumeric > istart)
    c = varargin{istart};
    if (isvector (c) && columns (c) != 3)
      c = c(:);
    endif
  elseif (firstnonnumeric == istart && ischar (varargin{istart})
          && ! (   strcmpi (varargin{istart}, "filled")
                || strcmpi (varargin{istart}, "fill")))
    c = varargin{istart};
    firstnonnumeric++;
  else
    c = [];
  endif

  newargs = {};
  filled = false;
  have_marker = false;
  marker = "o";
  iarg = firstnonnumeric;
  while (iarg <= nargin)
    arg = varargin{iarg++};
    if (ischar (arg) && (strcmpi (arg, "filled") || strcmpi (arg, "fill")))
      filled = true;
    elseif ((ischar (arg) || iscellstr (arg)) && ! have_marker)
      [linespec, valid] = __pltopt__ (fcn, arg, false);
      if (valid)
        have_marker = true;
        marker = linespec.marker;
        if (strcmp (marker, "none"))
          marker = "o";
        elseif (isempty (marker))
          have_marker = false;
          [~, marker] = __next_line_style__ ();
        endif
      else
        error ("%s: invalid linespec", fcn);
      endif
    else
      newargs{end+1} = arg;
      if (iarg <= nargin)
        newargs{end+1} = varargin{iarg++};
      endif
    endif
  endwhile

  if (isempty (c))
    c = __next_line_color__ ();
  endif

  ## Must occur after __next_line_color__ in order to work correctly.
  hg = hggroup ();
  newargs = __add_datasource__ (fcn, hg, {"x", "y", "z", "c", "size"},
                                newargs{:});

  addproperty ("xdata", hg, "data", x);
  addproperty ("ydata", hg, "data", y);
  addproperty ("zdata", hg, "data", z);
  if (ischar (c))
    ## For single explicit color, cdata is unused
    addproperty ("cdata", hg, "data", []);
  else
    addproperty ("cdata", hg, "data", c);
  endif
  addproperty ("sizedata", hg, "data", s);
  addlistener (hg, "xdata", @update_data);
  addlistener (hg, "ydata", @update_data);
  addlistener (hg, "zdata", @update_data);
  addlistener (hg, "cdata", @update_data);
  addlistener (hg, "sizedata", @update_data);

  one_explicit_color = ischar (c) || isequal (size (c), [1, 3]);

  if (numel (x) <= 100)

    ## For small number of points, we'll construct an object for each point.

    if (numel (s) == 1)
      s = repmat (s, numel (x), 1);
    endif

    if (one_explicit_color)
      for i = 1 : numel (x)
        if (filled)
          __go_patch__ (hg, "xdata", x(i), "ydata", y(i), "zdata", z(i,:),
                        "faces", 1, "vertices", [x(i), y(i), z(i,:)],
                        "facecolor", "none", "edgecolor", "none",
                        "marker", marker,  "markersize", s(i),
                        "markeredgecolor", c, "markerfacecolor", c,
                        "linestyle", "none");
        else
          __go_patch__ (hg, "xdata", x(i), "ydata", y(i), "zdata", z(i,:),
                        "faces", 1, "vertices", [x(i), y(i), z(i,:)],
                        "facecolor", "none", "edgecolor", "none",
                        "marker", marker,  "markersize", s(i),
                        "markeredgecolor", c, "markerfacecolor", "none",
                        "linestyle", "none");
        endif
      endfor
    else
      if (rows (c) == 1)
        c = repmat (c, rows (x), 1);
      endif
      for i = 1 : numel (x)
        if (filled)
          __go_patch__ (hg, "xdata", x(i), "ydata", y(i), "zdata", z(i,:),
                        "faces", 1, "vertices", [x(i), y(i), z(i,:)],
                        "facecolor", "none", "edgecolor", "none",
                        "marker", marker, "markersize", s(i),
                        "markeredgecolor", "none",
                        "markerfacecolor", "flat",
                        "cdata", c(i,:), "facevertexcdata", c(i,:),
                        "linestyle", "none");
        else
          __go_patch__ (hg, "xdata", x(i), "ydata", y(i), "zdata", z(i,:),
                        "faces", 1, "vertices", [x(i), y(i), z(i,:)],
                        "facecolor", "none", "edgecolor", "none",
                        "marker", marker, "markersize", s(i),
                        "markeredgecolor", "flat",
                        "markerfacecolor", "none",
                        "cdata", c(i,:), "facevertexcdata", c(i,:),
                        "linestyle", "none");
        endif
      endfor
    endif

  else

    ## For larger numbers of points, we split the points by common color.

    vert = [x, y, z];
    if (one_explicit_color)
      render_size_color (hg, vert, s, c, marker, filled, true);
    else
      if (rows (c) == 1)
        c = repmat (c, rows (x), 1);
      endif
      ## We want to group points by color.  So first get all the unique colors
      [cc, ~, c_to_cc] = unique (c, "rows");

      for i = 1 : rows (cc)
        ## Now for each possible unique color, get the logical index of
        ## points that correspond to that color
        idx = (i == c_to_cc);
        if (isscalar (s))
          render_size_color (hg, vert(idx, :), s, c(idx,:),
                                 marker, filled, true);
        else
          render_size_color (hg, vert(idx, :), s(idx), c(idx,:),
                                 marker, filled, true);
        endif
      endfor

    endif
  endif

  if (! ischar (c) && rows (c) > 1)
    ax = get (hg, "parent");
    clim = get (ax, "clim");
    if (min (c(:)) < clim(1))
      clim(1) = min (c(:));
      set (ax, "clim", clim);
    endif
    if (max (c(:)) > clim(2))
      set (ax, "clim", [clim(1), max(c(:))]);
    endif
  endif

  addproperty ("linewidth", hg, "patchlinewidth", 0.5);
  addproperty ("marker", hg, "patchmarker", marker);
  if (filled)
    addproperty ("markeredgecolor", hg, "patchmarkeredgecolor", "none");
    if (one_explicit_color)
      addproperty ("markerfacecolor", hg, "patchmarkerfacecolor", c);
    else
      addproperty ("markerfacecolor", hg, "patchmarkerfacecolor", "flat");
    endif
  else
    addproperty ("markerfacecolor", hg, "patchmarkerfacecolor", "none");
    if (one_explicit_color)
      addproperty ("markeredgecolor", hg, "patchmarkeredgecolor", c);
    else
      addproperty ("markeredgecolor", hg, "patchmarkeredgecolor", "flat");
    endif
  endif
  addlistener (hg, "linewidth", @update_props);
  addlistener (hg, "marker", @update_props);
  addlistener (hg, "markerfacecolor", @update_props);
  addlistener (hg, "markeredgecolor", @update_props);

  if (! isempty (newargs))
    set (hg, newargs{:});
  endif

endfunction

function render_size_color (hg, vert, s, c, marker, filled, isflat)
  if (isscalar (s))
    x = vert(:,1);
    y = vert(:,2);
    z = vert(:,3:end);
    toolkit = get (ancestor (hg, "figure"), "__graphics_toolkit__");
    ## Does gnuplot only support triangles with different vertex colors ?
    ## TODO: Verify gnuplot can only support one color.  If RGB triplets
    ##       can be assigned to each vertex, then fix __go_draw_axes__.m
    gnuplot_hack = (numel (x) > 1 && columns (c) == 3
                    && strcmp (toolkit, "gnuplot"));
    if (ischar (c) || ! isflat || gnuplot_hack)
      if (filled)
        __go_patch__ (hg, "xdata", x, "ydata", y, "zdata", z,
                          "faces", 1:numel (x), "vertices", vert,
                          "facecolor", "none", "edgecolor", "none",
                          "marker", marker,
                          "markeredgecolor", "none",
                          "markerfacecolor", c(1,:),
                          "markersize", s, "linestyle", "none");
      else
        __go_patch__ (hg, "xdata", x, "ydata", y, "zdata", z,
                          "faces", 1:numel (x), "vertices", vert,
                          "facecolor", "none", "edgecolor", "none",
                          "marker", marker,
                          "markeredgecolor", c(1,:),
                          "markerfacecolor", "none",
                          "markersize", s, "linestyle", "none");
      endif
    else
      if (filled)
        __go_patch__ (hg, "xdata", x, "ydata", y, "zdata", z,
                          "faces", 1:numel (x), "vertices", vert,
                          "facecolor", "none", "edgecolor", "none",
                          "marker", marker, "markersize", s,
                          "markeredgecolor", "none",
                          "markerfacecolor", "flat",
                          "cdata", c, "facevertexcdata", c,
                          "linestyle", "none");
      else
        __go_patch__ (hg, "xdata", x, "ydata", y, "zdata", z,
                          "faces", 1:numel (x), "vertices", vert,
                          "facecolor", "none", "edgecolor", "none",
                          "marker", marker, "markersize", s,
                          "markeredgecolor", "flat",
                          "markerfacecolor", "none",
                          "cdata", c, "facevertexcdata", c,
                          "linestyle", "none");
      endif
    endif
  else
    ## Round size to one decimal place.
    [ss, ~, s_to_ss] = unique (ceil (s*10) / 10);
    for i = 1:rows (ss)
      idx = (i == s_to_ss);
      render_size_color (hg, vert(idx,:), ss(i), c,
                             marker, filled, isflat);
    endfor
  endif
endfunction

function update_props (h, d)
  lw = get (h, "linewidth");
  m  = get (h, "marker");
  fc = get (h, "markerfacecolor");
  ec = get (h, "markeredgecolor");
  kids = get (h, "children");

  set (kids, "linewidth", lw, "marker", m,
             "markerfacecolor", fc, "markeredgecolor", ec);
endfunction

function update_data (h, d)
  x = get (h, "xdata");
  y = get (h, "ydata");
  z = get (h, "zdata");
  c = get (h, "cdata");
  if (rows (c) == 1)
    c = repmat (c, numel (x), 1);
  endif
  s = get (h, "sizedata");
  if (numel (s) == 1)
    s = repmat (s, numel (x), 1);
  endif
  hlist = get (h, "children");

  if (isempty (z))
    for i = 1 : length (hlist)
      set (hlist(i), "vertices", [x(i), y(i)],
                     "cdata", reshape (c(i,:),[1, size(c)(2:end)]),
                     "facevertexcdata", c(i,:),
                     "markersize", s(i));
    endfor
  else
    for i = 1 : length (hlist)
      set (hlist(i), "vertices", [x(i), y(i), z(i)],
                     "cdata", reshape (cd(i,:),[1, size(cd)(2:end)]),
                     "facevertexcdata", cd(i,:),
                     "markersize", s(i));
    endfor
  endif

endfunction