3D bar graph using gnuplot or octave

I would like to draw a three-dimensional histogram (with gnuplot or octave) to represent my data. let's say that I have a data file in the following form:

2 3 4 8 4 10 5 6 7 

I would like to draw nine color bars (matrix size) in the set [1,3] x [1,3], so the color of the bar is proportional to the height of the bar. How can i do this?

+6
source share
5 answers

Below is a function I implemented that acts as a bar3 replacement (partially).

In my version, bars are displayed by creating a graphic adapter object : we build a matrix of vertex coordinates and a list of persons connecting these vertices .

The idea is to first create one “3d cube” as a template, and then play it back as many bars as we have. Each rod is offset and scaled according to its position and height.

Vertex / face matrices are built in vector form (see ma, no loops!), And the result is a single patch object for all bars, as opposed to several patches one per bar (this is more efficient in terms of graphics performance ).

The function could be implemented by setting the coordinates of connected vertices that form polygons using the XData , YData , ZData and CData properties instead of the Vertices and Faces properties. This is actually what bar3 internally does. This approach usually requires big data to identify the patches (because we cannot have common points in the patch faces, although I didn’t care in my implementation). Here is a link where I tried to explain the structure of the data built with 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 

Here is an example to compare it with the built-in bar3 function in MATLAB:

 subplot(121), bar3(magic(7)), axis tight subplot(122), my_bar3(magic(7)), axis tight 

comparison_bar3

Please note that I chose the color of all bars in one solid color (similar to the output of hist3 function) while MATLAB emphasizes the columns of the matrix with the corresponding colors.

Easy to customize patch ; The following is an example of matching bar3 color mode using indexed color scaling :

 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 

bar3_coloring

Or say that you wanted to color the stripes using a 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) 

gradient_bars_animation

Note that in the last example, the "Renderer" property of the drawing will affect the appearance of the gradients. In MATLAB, rendering “OpenGL” will interpolate the colors along the RGB color space, while the other two renderings (“Artists” and “ZBuffer”) will interpolate according to the colors of the current color map used (so that the histograms will look like a mini colorbar passing through the jet palette , unlike the gradient from blue at the base to any color at a certain height, as shown above). See this post for more details.


I tested the function in Octave 3.6.4 and 3.8.1 , working on Windows, and they worked fine. If you run the examples above, you will find that some advanced 3D features are not yet implemented correctly in Octave (this includes transparency, lighting, etc.). In addition, I used functions not available in Octave, like membrane and spiral to create matrixes of samples, but they are not essential for the code, just replace them with your own data :)

octave_my_bar3

+11
source

I don't have access to Octave, but I believe this should do the trick:

 Z = [2 3 4 8 4 10 5 6 7]; [HW] = size(Z); h = zeros( 1, numel(Z) ); ih = 1; for ix = 1:W fx = ix-.45; tx = ix+.45; for iy = 1:W fy = iy-.45; ty = iy+.45; vert = [ fx fy 0;... fx ty 0;... tx fy 0;... tx ty 0;... fx fy Z(iy,ix);... fx ty Z(iy,ix);... tx fy Z(iy,ix);... tx ty Z(iy,ix)]; faces = [ 1 3 5;... 5 3 7;... 7 3 4;... 7 8 4;... 5 6 7;... 6 7 8;... 1 2 5;... 5 6 2;... 2 4 8;... 2 6 8]; h(ih) = patch( 'faces', faces, 'vertices', vert, 'FaceVertexCData', Z(iy,ix),... 'FaceColor', 'flat', 'EdgeColor','none' ); ih = ih+1; end end view( 60, 45 ); colorbar; 
+3
source

I think the following should do the trick. I did not use anything more complicated than colormap , surf and patch , which, as far as I know, should work like Octave.

Code:

 %# Your data Z = [2 3 4 8 4 10 5 6 7]; %# the "nominal" bar (adjusted from cylinder()) n = 4; r = [0.5; 0.5]; m = length(r); theta = (0:n)/n*2*pi + pi/4; sintheta = sin(theta); sintheta(end) = sqrt(2)/2; x0 = r * cos(theta); y0 = r * sintheta; z0 = (0:m-1)'/(m-1) * ones(1,n+1); %# get data for current colormap map = colormap; Mz = max(Z(:)); mz = min(Z(:)); % Each "bar" is 1 surf and 1 patch for ii = 1:size(Z,1) for jj = 1:size(Z,2) % Get color (linear interpolation through current colormap) cI = (Z(ii,jj)-mz)*(size(map,1)-1)/(Mz-mz) + 1; fC = floor(cI); cC = ceil(cI); color = map(fC,:) + (map(cC,:) - map(fC,:)) * (cI-fC); % Translate and rescale the nominal bar x = x0+ii; y = y0+jj; z = z0*Z(ii,jj); % Draw the bar surf(x,y,z, 'Facecolor', color) patch(x(end,:), y(end,:), z(end,:), color) end end 

Result:

enter image description here

How do I create a "nominal bar" based on code from MATLAB cylinder() . One interesting thing is that you can very easily make much more funky looking bars:

enter image description here

It was created by changing

 n = 4; r = [0.5; 0.5]; 

in

 n = 8; r = [0.5; 0.45; 0.2; 0.1; 0.2; 0.45; 0.5]; 
+3
source

Solution using only the features available in OCTAVE verified with octave-online

This solution produces a surface similar to the internal function of Matlabs hist3d .

In short:

  • creates a surface with 4 points with a "height" of each value that is applied to each edge of the hopper.
  • Each of them is surrounded by zeros, which are also displayed on each edge of the hopper.
  • The color is set based on the values ​​of the hopper and applied to 4 points and surrounding zeros. (so that the edges and vertices of the “bars” are colored according to the “height”.)

For data defined as a matrix containing bin heights (bin_values ​​in code):

code

 bin_values=rand(5,4); %some random data bin_edges_x=[0:size(bin_values,2)]; x=kron(bin_edges_x,ones(1,5)); x=x(4:end-2); bin_edges_y=[0:size(bin_values,1)]; y=kron(bin_edges_y,ones(1,5)); y=y(4:end-2); mask_z=[0,0,0,0,0;0,1,1,0,0;0,1,1,0,0;0,0,0,0,0;0,0,0,0,0]; mask_c=ones(5); z=kron(bin_values,mask_z); c=kron(bin_values,mask_c); surf(x,y,z,c) 

Output

3dhist

+3
source

Have you looked at this bar3 ?

Adapt it a bit:

 Z=[2 3 4 8 4 10 5 6 7]; % input data figure; h = bar3(Z); % get handle to graphics for k=1:numel(h), z=get(h(k),'ZData'); % old data - need for its NaN pattern nn = isnan(z); nz = kron( Z(:,k),ones(6,4) ); % map color to height 6 faces per data point nz(nn) = NaN; % used saved NaN pattern for transparent faces set(h(k),'CData', nz); % set the new colors end colorbar; 

And here is what you get at the end: enter image description here

+2
source

Source: https://habr.com/ru/post/970734/


All Articles