Search code examples
matlabvisualization

MATLAB volume plots via transparent isosurfaces, like in Plotly


I'm looking for a volume visualization like Plotly's Volume. I've tried most methods here, also tried vol3d.

How can I get closer to Plotly's visual? I'm simply looking to have a mostly fully transparent 3D plot, where (as I understand) values are locally grouped and represented as a colored "cloud" - and be able to use 2D colormaps (e.g. turbo). It doesn't have to be strictly accurate (e.g. antialiased).

Plots

Closest results below - describing:

  • isosurface & isocaps: colors are mapped incorrectly, max appears as min. The interior is also a bit too hollow (/ transparent).
  • slice: for colors to make sense, alpha must be too low. Higher alpha clutters everything with low values. alpha('color') isn't transparent enough on inner values.
  • vol3d: lack of transparency, and looks brickish.

Flipping sign and shifting input to isosurface / isocaps makes difference that I don't understand, and I can't make sense of docs' definition of the isovalue argument.

Code

OPTS = {'iso', 'slice', 'vol3d', 'slice-color'};
OPT = OPTS{1};

[X, Y, Z] = ndgrid(linspace(-8,8,40), linspace(-8,8,40), linspace(-8,8,40));
D = sin(X.*Y.*Z) ./ (X.*Y.*Z) * 100;
D = max(min(D, 100), 20);  % clip to [20, 100]

figure
colormap('turbo')
% make cube
xdim = size(X, 1);
daspect([size(X,2)/xdim, 1, size(X,3)/xdim])

switch OPT
    case {'iso', 'vol3d'}
        view(3)
        axis tight
        
        camlight left
        camlight
        lighting gouraud

        if strcmp(OPT, 'iso')
            isosurface(D, 20)
            isocaps(D, 20)
            alpha(.5)
        else
            vol3d('CData', D);
        end
    case {'slice', 'slice-color'}
        g = linspace(-8,8,40);
        h = slice(g,g,g, D, g,g,g);
        set(h,'EdgeColor','none',...
            'FaceColor','interp',...
            'FaceAlpha','interp');
        if strcmp(OPT, 'slice')
            alpha(.01)
        else
            alpha('color')
        end
end
import numpy as np
import plotly.graph_objs as go
import plotly.io as pio
pio.renderers.default = 'browser'

X, Y, Z = np.mgrid[-8:8:40j, -8:8:40j, -8:8:40j]
V = np.sin(X*Y*Z) / (X*Y*Z) * 100
V = np.clip(V, 20, 100)

vkw = dict(showscale=False, colorscale='turbo', surface_count=50, opacity=.2)
fig = go.Figure(go.Volume(x=X.flatten(), y=Y.flatten(), z=Z.flatten(),
                          value=V.flatten(), **vkw))
fig.show()

Solution

  • isosurface plots one surface. This surface is defined by where the volumetric data is equal to isovalue. I think this is the largest cause of the discrepancy between MATLAB and python.

    I suspect you would want to iterate over different values of isovalue in order to plot multiple surfaces.

    The closest I can get is this (thanks to Till's answer here):

    if strcmp(OPT, 'iso')
        for ii = linspace(20, 100, 50)
            patch(isosurface(D, ii, D), 'FaceAlpha', (ii/400).^2, 'FaceColor', 'interp', 'LineStyle', 'none');
            patch(isocaps(D, ii), 'FaceAlpha', (ii/400).^2, 'FaceColor', 'interp', 'LineStyle', 'none');
        end
    else ...
    

    alpha can also be a fixed value, but in this example the inner surfaces have higher alpha - you can try tuning the values differently, but that was the closest I could get to plotly.