Search code examples
imagematlabgeometrymatlab-figuresurface

Plot image on dome


I am struggling to plot a round image on the surface of the dome in Matlab. Here is what I have. This is the png image:

enter image description here

And this is the dome:

enter image description here

Now, I need to project this image on the dome. I've written a code to place the image on the surface:

r = 10;
r2 = 9;
cdata = imread('circle_image.png');
props.EdgeColor = 'none';
figure();
n = 50;
[X,Y,Z] = sphere(n) ;
X1 = X * r;
Y1 = Y * r;
Z1 = Z * r;

for i = 1:n+1
    for j = 1:n+1
            if Z1(i,j) < r2
                X1(i,j) = NaN;
                Y1(i,j) = NaN;
                Z1(i,j) = NaN;
            
            end
    end
end
my_dome = surf(X1,Y1,Z1,props) ;
alpha = 1;
set(kopula, 'FaceColor', 'texturemap', 'CData', cdata, 'FaceAlpha', alpha, 'EdgeColor', 'none');
axis equal

What I am getting looks like this:

enter image description here

It seems like the image is centred in the wrong place, or even in the wrong axis. How can I fix that?


Solution

  • Yes, the image is centered in the wrong place.

    The texture mapping is applied on the whole surface, not just the "active" points (the one you didn't NaN). Basically, what you are getting is the image being spread onto the full sphere, and then when you crop the top of the sphere to get your dome, the image is cropped as well:

    cropped sphere


    What you need to do, is actually remove all those points you converted to NaN, so they are not part of the surface at all and the texture mapping is aplied only on the top dome surface.

    So replace your nested for loop with the following code:

    idx_Raws2crop = Z1(:,1) < r2 ;
    X1(idx_Raws2crop,:) = [] ;
    Y1(idx_Raws2crop,:) = [] ;
    Z1(idx_Raws2crop,:) = [] ;
    

    Then continue with your code, you'll get:

    enter image description here

    Actually I also added the instruction:

    cdata = flipud(cdata) ;
    

    in order to have the THE CIRCLEwriting in the proper orientation (otherwise it appears upside down).


    Edit:

    To render the picture on the dome in the way you'd like according to your comment, I can see 2 options:

    Options 1: Build upon what we have already

    This will consist of:

    • Extend the [X,Y] domain on a square grid (so the picture is not distorted when texture mapped onto the surface).
    • Extend the picture itself (add some transparent margin), so the margin will cover the domain extension we introduced and the actual visible part of the picture will be nicely centered on the dome.

    With the same X1, Y1 and Z1 than we obtained with the code above:

    % Create a square grid large enough to cover the dome and a bit more:
    [X2,Y2] = meshgrid(linspace(-5,5,100),linspace(-5,5,100)) ;
    % reinterpolate Z1 over this new grid
    Z2 = griddata(X1,Y1,Z1,X2,Y2) ;
    

    Now your surface looks like this: enter image description here

    As I warned you, the image is now properly applied but the edge of the dome looks rather ugly. To ease that you could replace the NaN with the base value for your dome, this will transition a lot better between dome and flat domain:

    Z2(isnan(Z2)) = 9 ;
    

    Will now yield:

    enter image description here

    The next problem, as you can see, is that (as in your first question), the image is stretched onto the whole surface. So part of the writing is now on the flat part of the surface. To simply alleviate that, you can modify the picture (add a bit of transparent margin on every side), until the visible part of the image matches the dome size.


    Options 2: Build your surface differently

    This is actually easier and more straighforward. We will build a dome out of a square surface in the first place (no trimming and NaNing).

    This code does not require any part of your previous code, it is self contained:

    r = 10 ;
    % Build a square grid
    [X2,Y2] = meshgrid(linspace(-5,5,100),linspace(-5,5,100)) ;
    % build Z2 according to the sphere equation.
    Z2 = sqrt( r^2 - X2.^2 - Y2.^2) ;
    
    figure();
    my_dome = surf(X2,Y2,Z2,'EdgeColor', 'none', 'FaceColor', 'texturemap', 'CData', cdata, 'FaceAlpha', 1) ;
    axis equal
    

    Which yields a nicely centered texture map:

    enter image description here

    Note: The texture mapping works so well because your actual surface is still a square, only bent a bit to conform to a sphere. The texture mapping does not display the part of the surface where the picture is transparent, so it is invisible, but if you look at your surface without the texture mapping you'll understand what went on in the background:

    hs=surf(X2,Y2,Z2) ; shading interp ; axis equal
    

    enter image description here