1######################################################################## 2## 3## Copyright (C) 2007-2021 The Octave Project Developers 4## 5## See the file COPYRIGHT.md in the top-level directory of this 6## distribution or <https://octave.org/copyright/>. 7## 8## This file is part of Octave. 9## 10## Octave is free software: you can redistribute it and/or modify it 11## under the terms of the GNU General Public License as published by 12## the Free Software Foundation, either version 3 of the License, or 13## (at your option) any later version. 14## 15## Octave is distributed in the hope that it will be useful, but 16## 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 Octave; see the file COPYING. If not, see 22## <https://www.gnu.org/licenses/>. 23## 24######################################################################## 25 26## -*- texinfo -*- 27## @deftypefn {} {@var{hg} =} __scatter__ (@dots{}) 28## Undocumented internal function. 29## @end deftypefn 30 31function hg = __scatter__ (varargin) 32 33 hax = varargin{1}; # We don't do anything with this. Could remove it. 34 nd = varargin{2}; 35 fcn = varargin{3}; 36 x = varargin{4}(:); 37 y = varargin{5}(:); 38 39 if (nd == 2) 40 istart = 6; 41 else 42 z = varargin{6}(:); 43 istart = 7; 44 endif 45 46 ## Force mixtures of int and float data to be float (Bug #4116). 47 if (xor (isfloat (x), isfloat (y))) 48 if (isfloat (x)) 49 y = cast (y, class (x)); 50 else 51 x = cast (x, class (y)); 52 endif 53 endif 54 if (nd != 2) 55 if (xor (isfloat (x), isfloat (z))) 56 if (isfloat (x)) 57 z = cast (z, class (x)); 58 else 59 x = cast (x, class (z)); 60 y = cast (y, class (z)); 61 endif 62 endif 63 endif 64 65 if (istart <= nargin) 66 s = varargin{istart}(:); 67 if (isempty (s) || ischar (s)) 68 s = 36; 69 endif 70 if (! ischar (varargin{istart})) 71 istart += 1; 72 endif 73 else 74 s = 36; 75 endif 76 77 firstnonnumeric = find (! cellfun ("isnumeric", varargin(istart:nargin)), 1); 78 if (isempty (firstnonnumeric)) 79 firstnonnumeric = Inf; 80 else 81 firstnonnumeric += istart - 1; 82 endif 83 84 if (istart <= nargin && firstnonnumeric > istart) 85 c = varargin{istart}; 86 if (isvector (c) && columns (c) != 3) 87 c = c(:); 88 endif 89 elseif (firstnonnumeric == istart && ischar (varargin{istart}) 90 && any (tolower (varargin{istart}(1)) == "ymcrgbwk")) 91 [linespec, valid] = __pltopt__ (fcn, varargin{istart}, false); 92 if (valid) 93 c = varargin{istart}; 94 firstnonnumeric += 1; 95 else 96 c = []; 97 endif 98 else 99 c = []; 100 endif 101 102 ## Remove NaNs 103 idx = isnan (x) | isnan (y) | isnan (s); 104 if (nd == 3) 105 idx |= isnan (z); 106 z(idx) = []; 107 endif 108 x(idx) = []; 109 y(idx) = []; 110 if (nd == 2) 111 z = zeros (length (x), 0); 112 endif 113 if (numel (s) > 1) 114 s(idx) = []; 115 endif 116 if (rows (c) > 1) 117 c(idx,:) = []; 118 endif 119 120 ## Validate inputs 121 if (nd == 2 && ! size_equal (x, y)) 122 error ([fcn ": X and Y must have the same size"]); 123 elseif (nd == 3 && ! size_equal (x, y, z)) 124 error ([fcn ": X, Y, and Z must have the same size"]); 125 endif 126 127 if (! isscalar (s) && ! size_equal (x, s)) 128 error ([fcn ": size of S must match X, Y, and Z"]); 129 endif 130 131 if (rows (c) > 1 && rows (c) != rows (x)) 132 error ([fcn ": number of colors in C must match number of points in X"]); 133 endif 134 135 newargs = {}; 136 filled = false; 137 have_marker = false; 138 marker = "o"; 139 iarg = firstnonnumeric; 140 while (iarg <= nargin) 141 arg = varargin{iarg++}; 142 if (ischar (arg) && (strcmpi (arg, "filled") || strcmpi (arg, "fill"))) 143 filled = true; 144 elseif ((ischar (arg) || iscellstr (arg)) && ! have_marker) 145 [linespec, valid] = __pltopt__ (fcn, arg, false); 146 if (valid) 147 ## Valid linestyle, but possibly not valid marker 148 have_marker = true; 149 marker = linespec.marker; 150 if (strcmp (marker, "none")) 151 marker = "o"; 152 elseif (isempty (marker)) 153 have_marker = false; 154 marker = "o"; 155 endif 156 else 157 ## Prop/Val pair 158 newargs{end+1} = arg; 159 if (iarg <= nargin) 160 newargs{end+1} = varargin{iarg++}; 161 endif 162 endif 163 else 164 ## Prop/Val pair 165 newargs{end+1} = arg; 166 if (iarg <= nargin) 167 newargs{end+1} = varargin{iarg++}; 168 endif 169 endif 170 endwhile 171 172 if (isempty (c)) 173 c = __next_line_color__ (); 174 endif 175 176 ## Must occur after __next_line_color__ in order to work correctly. 177 hg = hggroup ("__appdata__", struct ("__creator__", "__scatter__")); 178 newargs = __add_datasource__ (fcn, hg, {"x", "y", "z", "c", "size"}, 179 newargs{:}); 180 181 addproperty ("xdata", hg, "data", x); 182 addproperty ("ydata", hg, "data", y); 183 addproperty ("zdata", hg, "data", z); 184 if (ischar (c)) 185 ## For single explicit color, cdata is unused 186 addproperty ("cdata", hg, "data", []); 187 else 188 addproperty ("cdata", hg, "data", c); 189 endif 190 addproperty ("sizedata", hg, "data", s); 191 addlistener (hg, "xdata", @update_data); 192 addlistener (hg, "ydata", @update_data); 193 addlistener (hg, "zdata", @update_data); 194 addlistener (hg, "cdata", @update_data); 195 addlistener (hg, "sizedata", @update_data); 196 197 one_explicit_color = ischar (c) || isequal (size (c), [1, 3]); 198 s = sqrt (s); # size adjustment for visual compatibility w/Matlab 199 200 if (numel (x) <= 100) 201 202 ## For small number of points, we'll construct an object for each point. 203 204 if (numel (s) == 1) 205 s = repmat (s, numel (x), 1); 206 endif 207 208 if (one_explicit_color) 209 for i = 1 : numel (x) 210 if (filled) 211 __go_patch__ (hg, "facecolor", "none", "edgecolor", "none", 212 "xdata", x(i), "ydata", y(i), "zdata", z(i,:), 213 "faces", 1, "vertices", [x(i), y(i), z(i,:)], 214 "marker", marker, "markersize", s(i), 215 "markeredgecolor", c, "markerfacecolor", c, 216 "linestyle", "none"); 217 else 218 __go_patch__ (hg, "facecolor", "none", "edgecolor", "none", 219 "xdata", x(i), "ydata", y(i), "zdata", z(i,:), 220 "faces", 1, "vertices", [x(i), y(i), z(i,:)], 221 "marker", marker, "markersize", s(i), 222 "markeredgecolor", c, "markerfacecolor", "none", 223 "linestyle", "none"); 224 endif 225 endfor 226 else 227 if (rows (c) == 1) 228 c = repmat (c, rows (x), 1); 229 endif 230 for i = 1 : numel (x) 231 if (filled) 232 __go_patch__ (hg, "facecolor", "none", "edgecolor", "none", 233 "xdata", x(i), "ydata", y(i), "zdata", z(i,:), 234 "faces", 1, "vertices", [x(i), y(i), z(i,:)], 235 "marker", marker, "markersize", s(i), 236 "markeredgecolor", "none", 237 "markerfacecolor", "flat", 238 "cdata", c(i,:), "facevertexcdata", c(i,:), 239 "linestyle", "none"); 240 else 241 __go_patch__ (hg, "facecolor", "none", "edgecolor", "none", 242 "xdata", x(i), "ydata", y(i), "zdata", z(i,:), 243 "faces", 1, "vertices", [x(i), y(i), z(i,:)], 244 "marker", marker, "markersize", s(i), 245 "markeredgecolor", "flat", 246 "markerfacecolor", "none", 247 "cdata", c(i,:), "facevertexcdata", c(i,:), 248 "linestyle", "none"); 249 endif 250 endfor 251 endif 252 253 else 254 255 ## For larger numbers of points, we use one single object. 256 vert = [x, y, z]; 257 render_size_color (hg, vert, s, c, marker, filled, true); 258 259 endif 260 261 if (! ischar (c) && rows (c) > 1) 262 ax = get (hg, "parent"); 263 clim = get (ax, "clim"); 264 if (min (c(:)) < clim(1)) 265 clim(1) = min (c(:)); 266 set (ax, "clim", clim); 267 endif 268 if (max (c(:)) > clim(2)) 269 set (ax, "clim", [clim(1), max(c(:))]); 270 endif 271 endif 272 273 addproperty ("linewidth", hg, "patchlinewidth", 0.5); 274 addproperty ("marker", hg, "patchmarker", marker); 275 if (filled) 276 addproperty ("markeredgecolor", hg, "patchmarkeredgecolor", "none"); 277 if (one_explicit_color) 278 addproperty ("markerfacecolor", hg, "patchmarkerfacecolor", c); 279 else 280 addproperty ("markerfacecolor", hg, "patchmarkerfacecolor", "flat"); 281 endif 282 else 283 addproperty ("markerfacecolor", hg, "patchmarkerfacecolor", "none"); 284 if (one_explicit_color) 285 addproperty ("markeredgecolor", hg, "patchmarkeredgecolor", c); 286 else 287 addproperty ("markeredgecolor", hg, "patchmarkeredgecolor", "flat"); 288 endif 289 endif 290 addlistener (hg, "linewidth", @update_props); 291 addlistener (hg, "marker", @update_props); 292 addlistener (hg, "markerfacecolor", @update_props); 293 addlistener (hg, "markeredgecolor", @update_props); 294 295 ## Matlab property, although Octave does not implement it. 296 addproperty ("hittestarea", hg, "radio", "on|{off}", "off"); 297 298 if (! isempty (newargs)) 299 set (hg, newargs{:}); 300 endif 301 302endfunction 303 304function render_size_color (hg, vert, s, c, marker, filled, isflat) 305 306 if (isscalar (s)) 307 x = vert(:,1); 308 y = vert(:,2); 309 z = vert(:,3:end); 310 toolkit = get (ancestor (hg, "figure"), "__graphics_toolkit__"); 311 ## Does gnuplot only support triangles with different vertex colors ? 312 ## FIXME: Verify gnuplot can only support one color. If RGB triplets 313 ## can be assigned to each vertex, then fix __gnuplot_draw_axes__.m 314 gnuplot_hack = (numel (x) > 1 && columns (c) == 3 315 && strcmp (toolkit, "gnuplot")); 316 if (ischar (c) || ! isflat || gnuplot_hack) 317 if (filled) 318 ## "facecolor" and "edgecolor" must be set before any other properties 319 ## to skip co-planarity check (see bug #55751). 320 __go_patch__ (hg, "facecolor", "none", "edgecolor", "none", 321 "xdata", x, "ydata", y, "zdata", z, 322 "faces", 1:numel (x), "vertices", vert, 323 "marker", marker, 324 "markeredgecolor", "none", 325 "markerfacecolor", c(1,:), 326 "markersize", s, "linestyle", "none"); 327 else 328 __go_patch__ (hg, "facecolor", "none", "edgecolor", "none", 329 "xdata", x, "ydata", y, "zdata", z, 330 "faces", 1:numel (x), "vertices", vert, 331 "marker", marker, 332 "markeredgecolor", c(1,:), 333 "markerfacecolor", "none", 334 "markersize", s, "linestyle", "none"); 335 endif 336 else 337 if (filled) 338 __go_patch__ (hg, "facecolor", "none", "edgecolor", "none", 339 "xdata", x, "ydata", y, "zdata", z, 340 "faces", 1:numel (x), "vertices", vert, 341 "marker", marker, "markersize", s, 342 "markeredgecolor", "none", 343 "markerfacecolor", "flat", 344 "cdata", c, "facevertexcdata", c, 345 "linestyle", "none"); 346 else 347 __go_patch__ (hg, "facecolor", "none", "edgecolor", "none", 348 "xdata", x, "ydata", y, "zdata", z, 349 "faces", 1:numel (x), "vertices", vert, 350 "marker", marker, "markersize", s, 351 "markeredgecolor", "flat", 352 "markerfacecolor", "none", 353 "cdata", c, "facevertexcdata", c, 354 "linestyle", "none"); 355 endif 356 endif 357 else 358 ## Round size to one decimal place. 359 [ss, ~, s_to_ss] = unique (ceil (s*10) / 10); 360 for i = 1:rows (ss) 361 idx = (i == s_to_ss); 362 render_size_color (hg, vert(idx,:), ss(i), c, 363 marker, filled, isflat); 364 endfor 365 endif 366 367endfunction 368 369function update_props (h, d) 370 371 lw = get (h, "linewidth"); 372 m = get (h, "marker"); 373 fc = get (h, "markerfacecolor"); 374 ec = get (h, "markeredgecolor"); 375 kids = get (h, "children"); 376 377 set (kids, "linewidth", lw, "marker", m, 378 "markerfacecolor", fc, "markeredgecolor", ec); 379 380endfunction 381 382## FIXME: This callback routine doesn't handle the case where N > 100. 383function update_data (h, d) 384 385 x = get (h, "xdata"); 386 y = get (h, "ydata"); 387 z = get (h, "zdata"); 388 if (numel (x) > 100) 389 error ("scatter: cannot update data with more than 100 points. Call scatter (x, y, ...) with new data instead."); 390 endif 391 c = get (h, "cdata"); 392 one_explicit_color = ischar (c) || isequal (size (c), [1, 3]); 393 if (! one_explicit_color) 394 if (rows (c) == 1) 395 c = repmat (c, numel (x), 1); 396 endif 397 endif 398 filled = ! strcmp (get (h, "markerfacecolor"), "none"); 399 s = get (h, "sizedata"); 400 ## Size adjustment for visual compatibility with Matlab. 401 s = sqrt (s); 402 if (numel (s) == 1) 403 s = repmat (s, numel (x), 1); 404 endif 405 hlist = get (h, "children"); 406 407 if (one_explicit_color) 408 if (filled) 409 if (isempty (z)) 410 for i = 1 : length (hlist) 411 set (hlist(i), "vertices", [x(i), y(i)], 412 "markersize", s(i), 413 "markeredgecolor", c, "markerfacecolor", c); 414 415 endfor 416 else 417 for i = 1 : length (hlist) 418 set (hlist(i), "vertices", [x(i), y(i), z(i)], 419 "markersize", s(i), 420 "markeredgecolor", c, "markerfacecolor", c); 421 endfor 422 endif 423 else 424 if (isempty (z)) 425 for i = 1 : length (hlist) 426 set (hlist(i), "vertices", [x(i), y(i)], 427 "markersize", s(i), 428 "markeredgecolor", c, "markerfacecolor", "none"); 429 430 endfor 431 else 432 for i = 1 : length (hlist) 433 set (hlist(i), "vertices", [x(i), y(i), z(i)], 434 "markersize", s(i), 435 "markeredgecolor", c, "markerfacecolor", "none"); 436 endfor 437 endif 438 endif 439 else 440 if (filled) 441 if (isempty (z)) 442 for i = 1 : length (hlist) 443 set (hlist(i), "vertices", [x(i), y(i)], 444 "markersize", s(i), 445 "markeredgecolor", "none", "markerfacecolor", "flat", 446 "cdata", reshape (c(i,:),[1, size(c)(2:end)]), 447 "facevertexcdata", c(i,:)); 448 endfor 449 else 450 for i = 1 : length (hlist) 451 set (hlist(i), "vertices", [x(i), y(i), z(i)], 452 "markersize", s(i), 453 "markeredgecolor", "none", "markerfacecolor", "flat", 454 "cdata", reshape (c(i,:),[1, size(c)(2:end)]), 455 "facevertexcdata", c(i,:)); 456 endfor 457 endif 458 else 459 if (isempty (z)) 460 for i = 1 : length (hlist) 461 set (hlist(i), "vertices", [x(i), y(i)], 462 "markersize", s(i), 463 "markeredgecolor", "flat", "markerfacecolor", "none", 464 "cdata", reshape (c(i,:),[1, size(c)(2:end)]), 465 "facevertexcdata", c(i,:)); 466 endfor 467 else 468 for i = 1 : length (hlist) 469 set (hlist(i), "vertices", [x(i), y(i), z(i)], 470 "markersize", s(i), 471 "markeredgecolor", "flat", "markerfacecolor", "none", 472 "cdata", reshape (c(i,:),[1, size(c)(2:end)]), 473 "facevertexcdata", c(i,:)); 474 endfor 475 endif 476 endif 477 endif 478 479endfunction 480