How can I set 'EdgeLighting' for a patch line across a surface, so it matches the surface lighting?

I'm plotting maps defined by a grid of [latitude, longitude, value] as a 3D globe using surf(). I want to add lines such as coastlines across the surface, and have these lines lit in the same manner as the surface.
Thanks to this answer I found I need to plot the lines as the edges of patches to enable the 'EdgeLighting' property, and then manually reset the 'VertexNormals' property of the lines to represent the normal vectors of the surface.
So far I've managed to calculate approximate surface normals at the line coordinates and the lines are now lit variably in space, but not in the same manner as the surface lighting it should match:
% The 2D elevation map of `peaks` projected onto a spheroid via a
% colatitude, longitude grid
phi = linspace(-pi,pi,41);
theta = linspace(0,pi,41);
[phi,theta] = meshgrid(phi,theta);
rho = 20 + peaks(41);
r = rho.*sin(theta);
x = r.*cos(phi);
y = r.*sin(phi);
z = rho.*cos(theta);
figure
s = surf(x,y,z, 'Facelighting', 'gouraud');
shading interp
axis equal
camlight('left', 'infinite')
% A 2D line projected across the surface of the spheroid
l_theta = linspace(pi/4,3*pi/4,50);
l_phi = linspace(-pi,pi,50);
l_rho = interp2(phi, theta, rho, l_phi, l_theta);
l_r = l_rho.*sin(l_theta);
l_x = l_r.*cos(l_phi);
l_y = l_r.*sin(l_phi);
l_z = l_rho.*cos(l_theta);
hold on
p = patch([l_x,nan],[l_y,nan],[l_z,nan],'k'); % NaNs gives a patch without a face
set(p, 'Edgecolor', 'w', 'Edgelighting' ,'gouraud', 'LineWidth', 3);
% Don't know the spheroid normal vectors at the line coordinates, so
% convert spheroid point cloud to alpha shape, then find nearest point of
% spheroid to points of line
shp = alphaShape(x(:),y(:),z(:));
idx = nearestNeighbor(shp, p.Vertices(1:end-1,1), p.Vertices(1:end-1,2), p.Vertices(1:end-1,3));
% Get surface normal vectors of spheroid points
[nx, ny, nz] = surfnorm(x,y,z);
% Set line vertex normal vectors to those of nearest spheroid vertex normal, repeat
% the last value for the patch coordinate NaNs added earlier
set(p, 'VertexNormals', [[nx(idx); nx(idx(end))], [ny(idx); ny(idx(end))], [ny(idx); nz(idx(end))]])
% Plot the normal vectors of the line and of then the surface...they don't match?
quiver3(p.Vertices(:,1), p.Vertices(:,2), p.Vertices(:,3), ...
p.VertexNormals(:,1), p.VertexNormals(:,2), p.VertexNormals(:,3))
quiver3(x,y,z,s.VertexNormals(:,:,1),s.VertexNormals(:,:,2),s.VertexNormals(:,:,3),'r')
hold off
How can I get line normal vectors parallel to the surface normal vectors?
Is there a better way to get the surface normals at my line coordinates?
I see my surface normals are defined inward, I know I can transpose x,y,z to x',y',z' to close the surface outward instead, but I can't seem to match line normals outward too.

답변 (1개)

darova
darova 2019년 7월 29일
Can you generate surface like this instead of 3D curve? Then you could just use surfnorm() again and extract normal vector you want
img1.png

댓글 수: 8

Or concantenate three 3D curves
Possibly? I'm not exactly sure what you've done there, but I'm guessing something similar to,
surfnorm(repmat(l_x,3,1), repmat(l_y,3,1), bsxfun(@times,l_z,[0.9; 1; 1.1]))
using my example, which produces not quite the same as your image:
surfnorm_of_line.png
The catch here is that the normals are assumed to be in the horizontal plane by stretching the line coordinates in the z-direction, so it won't work for any orientation of line (and the sign switch half way round the curve will mean the normals have to be forced to point outward).
How did you generate your surface? Can it be made tangent to the spheroid surface in the example?
The image i showed you is just an example (evolvent)
I just generated three (different) 3D curves and concantenated them to create a surface
img1.png
Ah ok, thanks for clarifying, and for the example code.
This seems to be the best approach so far, but to make it work for any set of generic 2D line coordinates does requires a fair bit of extra checking to catch different cases e.g.:
  • have to be careful how lines crossing 0,2pi are defined
  • jittering the spherical angle coordinates to turn a line into the required 3 lines for a surface must be done in the right direction, i.e. ideally perpendicular, but never parallel
  • lines that cross a pole must have their normal vectors extrapolated from those nearby
  • line coordinates must be sorted to define outward facing normal vectors, and in some cases will still require a check to force the normals to point outward from the spheroid centre
  • sets of line coordinates which contain NaN separaters have to be accounted for
I want you to accept my answer please
I've upvoted your contribution (which I appreciate as it has been helpful) but I'm not accepting this as an answer since I don't think the question I posed has been fully answered, and I'd hope that a more complete answer might be found if the question remains open.
In your answer you jitter the 'theta' angle coordinates to produce a surface from the line, this is not a generic solution. It works because my simple example line predominatly varies in the 'phi' angle coordinate, it does not work for lines which vary predominantly in 'theta'.
I think a complete solution is quite a bit more complex than I initial thought it would be...

댓글을 달려면 로그인하십시오.

카테고리

제품

릴리스

R2016b

질문:

2019년 7월 29일

댓글:

2019년 8월 6일

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by