下面是我实现的一个函数,它充当bar3 替换(部分)。
在我的版本中,条形图通过创建patch graphics object 来呈现:我们构建了vertex coordinates and a list of faces connecting those vertices 的矩阵。
我们的想法是首先构建一个“3d 立方体”作为模板,然后将其复制到尽可能多的条形图上。每个条都根据其位置和高度进行移动和缩放。
顶点/面矩阵以矢量化方式构造(看,没有循环!),结果是为所有条绘制单个patch object,而不是每个条绘制多个补丁(这更有效就graphics performance而言)。
该函数可以通过使用XData、YData、ZData 和CData 属性而不是Vertices 和Faces 属性来指定形成多边形的连接顶点的坐标来实现。事实上,这就是bar3 内部所做的。这种方法通常需要更大的数据来定义补丁(因为我们不能在补丁面上共享点,尽管在我的实现中我不太关心这一点)。这是related post,我试图解释bar3构造的数据的结构。
my_bar3.m
function pp = my_bar3(M, width)
% MY_BAR3 3D bar graph.
%
% M - 2D matrix
% width - bar width (1 means no separation between bars)
%
% See also: bar3, hist3
%% construct patch
if nargin < 2, width = 0.8; end
assert(ismatrix(M), 'Matrix expected.')
% size of matrix
[ny,nx] = size(M);
% first we build a "template" column-bar (8 vertices and 6 faces)
% (bar is initially centered at position (1,1) with width=? and height=1)
hw = width / 2; % half width
[X,Y,Z] = ndgrid([1-hw 1+hw], [1-hw 1+hw], [0 1]);
v = [X(:) Y(:) Z(:)];
f = [
1 2 4 3 ; % bottom
5 6 8 7 ; % top
1 2 6 5 ; % front
3 4 8 7 ; % back
1 5 7 3 ; % left
2 6 8 4 % right
];
% replicate vertices of "template" to form nx*ny bars
[offsetX,offsetY] = meshgrid(0:nx-1,0:ny-1);
offset = [offsetX(:) offsetY(:)]; offset(:,3) = 0;
v = bsxfun(@plus, v, permute(offset,[3 2 1]));
v = reshape(permute(v,[2 1 3]), 3,[]).';
% adjust bar heights to be equal to matrix values
v(:,3) = v(:,3) .* kron(M(:), ones(8,1));
% replicate faces of "template" to form nx*ny bars
increments = 0:8:8*(nx*ny-1);
f = bsxfun(@plus, f, permute(increments,[1 3 2]));
f = reshape(permute(f,[2 1 3]), 4,[]).';
%% plot
% prepare plot
if exist('OCTAVE_VERSION','builtin') > 0
% If running Octave, select OpenGL backend, gnuplot wont work
graphics_toolkit('fltk');
hax = gca;
else
hax = newplot();
set(ancestor(hax,'figure'), 'Renderer','opengl')
end
% draw patch specified by faces/vertices
% (we use a solid color for all faces)
p = patch('Faces',f, 'Vertices',v, ...
'FaceColor',[0.75 0.85 0.95], 'EdgeColor','k', 'Parent',hax);
view(hax,3); grid(hax,'on');
set(hax, 'XTick',1:nx, 'YTick',1:ny, 'Box','off', 'YDir','reverse', ...
'PlotBoxAspectRatio',[1 1 (sqrt(5)-1)/2]) % 1/GR (GR: golden ratio)
% return handle to patch object if requested
if nargout > 0
pp = p;
end
end
这是一个与 MATLAB 中的内置 bar3 函数进行比较的示例:
subplot(121), bar3(magic(7)), axis tight
subplot(122), my_bar3(magic(7)), axis tight
请注意,我选择用单一纯色着色所有条形(类似于 hist3 函数的输出),而 MATLAB 用匹配的颜色强调矩阵的列。
虽然customize the patch 很容易;这是一个使用 indexed color mapping (scaled) 匹配 bar3 coloring mode 的示例:
M = membrane(1); M = M(1:3:end,1:3:end);
h = my_bar3(M, 1.0);
% 6 faces per bar
fvcd = kron((1:numel(M))', ones(6,1));
set(h, 'FaceVertexCData',fvcd, 'FaceColor','flat', 'CDataMapping','scaled')
colormap hsv; axis tight; view(50,25)
set(h, 'FaceAlpha',0.85) % semi-transparent bars
或者说您想使用gradient according to their heights 为条形着色:
M = 9^2 - spiral(9);
h = my_bar3(M, 0.8);
% use Z-coordinates as vertex colors (indexed color mapping)
v = get(h, 'Vertices');
fvcd = v(:,3);
set(h, 'FaceVertexCData',fvcd, 'FaceColor','interp')
axis tight vis3d; daspect([1 1 10]); view(-40,20)
set(h, 'EdgeColor','k', 'EdgeAlpha',0.1)
请注意,在最后一个示例中,"Renderer" property of the figure 会影响渐变的外观。在 MATLAB 中,“OpenGL”渲染器将沿 RGB 颜色空间插入颜色,而其他两个渲染器(“Painters”和“ZBuffer”)将插入当前使用的颜色图的颜色(因此直方图条看起来像 mini @ 987654358@s 穿过jet 调色板,而不是从底部的蓝色渐变到定义高度的任何颜色,如上所示)。详情请见this post。
我已经在 Windows 上运行的 Octave 3.6.4 和 3.8.1 中测试了该函数,并且运行良好。如果您运行我上面展示的示例,您会发现一些高级 3D 功能尚未在 Octave 中正确实现(包括透明度、照明等)。此外,我还使用了 Octave 中不可用的函数,例如 membrane 和 spiral 来构建示例矩阵,但这些对代码来说不是必需的,只需将它们替换为您自己的数据即可 :)