问题
我试图可视化一个三维路径,以及它周围的一个表示数据标准差的“云”我希望能看到一条黑色的粗线,这条线周围有一个均匀的灰色区域,线上没有任何云彩,仿佛能像x光一样透过云层看到这条线。
尝试
我用plot3
创建了一条粗线,用patch
创建了一系列以线的每个点为中心的框(图上有一些额外的东西来表示开始/停止和方向,我也希望它们很容易被看到)我试着玩alpha
的patch
,但这会给线条造成一种模糊,因此灰色的亮度会随着视线中有多少灰色框而变化我希望alpha
是1,这样每个灰色框都是完全相同的颜色,但我希望找到某种方法使线均匀地穿过云层。
最小示例
根据要求,这里有一个最小的例子,它产生了下面的图。
% Create a path as an example (a circle in the x-y plane, with sinusoidal deviations in the z-axis)
t = 0:1/100:2*pi;
x = sin(t);y = cos(t);
z = cos(t).*sin(5*t);
figure;
plot3(x,y,z,'k','linewidth',7);
% Draw patches
cloud = .1*rand(size(t)); % The size of each box (make them random, "like" real data)
grayIntensity = .9; % Color of patch
faceAlpha = .15; % Alpha of patch
for i = 1:length(x)
patch([x(i) - cloud(i); x(i) + cloud(i); x(i) - cloud(i); x(i) + cloud(i); x(i) - cloud(i); x(i) + cloud(i); x(i) - cloud(i); x(i) + cloud(i)],... % X values
[y(i) - cloud(i); y(i) - cloud(i); y(i) + cloud(i); y(i) + cloud(i); y(i) - cloud(i); y(i) - cloud(i); y(i) + cloud(i); y(i) + cloud(i)],... % Y values
[z(i) + cloud(i); z(i) + cloud(i); z(i) + cloud(i); z(i) + cloud(i); z(i) - cloud(i); z(i) - cloud(i); z(i) - cloud(i); z(i) - cloud(i)],... % Z values
grayIntensity*ones(1,3),... % Color of patch
'faces', [1 2 4 3;5 6 8 7;1 2 6 5; 8 7 3 4;1 5 7 3;2 6 8 4],... % Connect vertices to form faces (a box)
'edgealpha',0,... % Make edges invisible (to get continuous cloud effect)
'facealpha',faceAlpha); % Set alpha of faces
end
抱歉,for循环中有很长一段代码,
patch
命令有很多参数前三行只是定义定义立方体的8个顶点的x、y和z坐标,通过指定中心点加上或减去立方体的一半宽度,cloud(i)
其余的应该用他们各自的评论来解释。谢谢你的帮助!
最佳答案
这是我在评论中提到的解决方案的一个实现(只画了一个surface
云)。
它没有经过优化,有一些for
循环可以通过巧妙地使用bsxfun或这些helper函数家族来避免,但是它可以正常运行找到每个点的曲线切线和确定每个横截面的方向(旋转)的数学可能也可以简化,但这不是我的强项,所以如果专家们喜欢,我就把它留给他们。
基本上,它定义了一个圆(代码中通常称为“横截面”),其半径与某个东西成比例(应用程序中的标准偏差,示例中的随机值)然后,每个圆在三维中旋转,使其在转换点处与曲线垂直所有这些圆都用作单个surface
图形对象的信封。
当曲面多次重叠(取决于视角)时,主中心线仍有一点阴影,但主中心线始终可见另外,您只有一个图形对象要管理。
结果如下:
当然,你可以根据自己的喜好改变表面的AlphaValue
我定义了一个与颜色信息数据大小相同的完整矩阵目前,它都被设置为0
(因此它指向默认颜色映射中的绿色),但是这样也很容易,如果您想创建另一个参数的颜色函数,只需相应地调整颜色矩阵(以及随它一起的颜色映射)。
在代码末尾有一个选项,可以将每个横截面显示为面片对象它不打算在最终结果中使用,但是如果您想进行自己的修改,它可以帮助您理解整个东西是如何构造的。
代码如下:
%% // Create a path as an example (a circle in the x-y plane, with sinusoidal deviations in the z-axis)
nPts = 180 ;
t = linspace(0,359,nPts)*pi/180;
x = sin(t); y = cos(t);
z = cos(t).*sin(2*t);
figure;
h.line = plot3(x,y,z,'k','linewidth',2,'Marker','none');
hold on
xlabel('X')
ylabel('Y')
zlabel('Z')
%% // Define options
%// cloud = .1*rand(size(t)) ; % The size of each box (make them random, "like" real data)
%// I used another randomization process, make that function of your stdev
r.min = 0.1 ; r.max = 0.2 ;
radius = r.min + (r.max-r.min).* rand(size(t)) ;
%// define surface and patch display options (FaceAlpha etc ...), for later
surfoptions = {'FaceAlpha',0.2 , 'EdgeColor','none' , 'EdgeAlpha',0.1 , 'DiffuseStrength',1 , 'AmbientStrength',1 } ;
patchoptions = {'FaceAlpha',0.2 , 'EdgeColor','k' , 'EdgeAlpha',0.2 , 'DiffuseStrength',1 , 'AmbientStrength',1 } ;
patchcol = [1 0 0] ; % Color of patch
%% // get the gradient at each point of the curve
Gx = diff([x,x(1)]).' ; %'//damn StackOverflow prettifier
Gy = diff([y,y(1)]).' ; %'//damn StackOverflow prettifier
Gz = diff([z,z(1)]).' ; %'//damn StackOverflow prettifier
%// get the middle gradient between 2 segments (optional, just for better rendering if low number of points)
G = [ (Gx+circshift(Gx,1))./2 (Gy+circshift(Gy,1))./2 (Gz+circshift(Gz,1))./2] ;
%% // get the angles (azimuth, elevation) of each plane normal to the curve
ux = [1 0 0] ;
uy = [0 1 0] ;
uz = [0 0 1] ;
for k = nPts:-1:1 %// running the loop in reverse does automatic preallocation
a = G(k,:) ./ norm(G(k,:)) ;
angx(k) = atan2( norm(cross(a,ux)) , dot(a,ux)) ;
angy(k) = atan2( norm(cross(a,uy)) , dot(a,uy)) ;
angz(k) = atan2( norm(cross(a,uz)) , dot(a,uz)) ;
[az(k),el(k)] = cart2sph( a(1) , a(2) , a(3) ) ;
end
%// adjustment to be normal to cross section plane the way the rotation are defined later
az = az + pi/2 ;
el = pi/2 - el ;
%% // define basic disc
discResolution = 20 ;
tt = linspace( 0 , 2*pi , discResolution ) ;
xd = cos(tt) ;
yd = sin(tt) ;
zd = zeros( size(xd) ) ;
%% // Generate coordinates for each cross section
ccylX = zeros( nPts , discResolution ) ;
ccylY = zeros( nPts , discResolution ) ;
ccylZ = zeros( nPts , discResolution ) ;
ccylC = zeros( nPts , discResolution ) ;
for ip = 1:nPts
%// cross section coordinates, with radius function of [rand] in this
%// example. Make it function of the stdev in your application.
csTemp = [ ( radius(ip) .* xd ) ; ... %// X coordinates
( radius(ip) .* yd ) ; ... %// Y coordinates
zd ] ; %// Z coordinates
%// rotate the cross section (around X axis, around origin)
elev = el(ip) ;
Rmat = [ 1 0 0 ; ...
0 cos(elev) -sin(elev) ; ...
0 sin(elev) cos(elev) ] ;
csTemp = Rmat * csTemp ;
%// do the same again to orient the azimuth (around Z axis)
azi = az(ip) ;
Rmat = [ cos(azi) -sin(azi) 0 ; ...
sin(azi) cos(azi) 0 ; ...
0 0 1 ] ;
csTemp = Rmat * csTemp ;
%// translate each cross section where it should be and store in global coordinate vector
ccylX(ip,:) = csTemp(1,:) + x(ip) ;
ccylY(ip,:) = csTemp(2,:) + y(ip) ;
ccylZ(ip,:) = csTemp(3,:) + z(ip) ;
end
%% // Display the full cylinder
hd.cyl = surf( ccylX , ccylY , ccylZ , ccylC ) ;
%// use that if the graphic object already exist but you just want to update your data:
%// set( hd.cyl , 'XData',ccylX , 'YData',ccylY ,'ZData',ccylZ )
set( hd.cyl , surfoptions{:} )
%% // this is just to avoid displaying the patches in the next block
%// comment the "return" instruction or just execute next block if you want
%// to see the building cross sections as patches
return
%% // display patches
hp = zeros(nPts,1) ;
for ip = 1:nPts
hp(ip) = patch( ccylX(ip,:) , ccylY(ip,:) , ccylZ(ip,:) , patchcol ) ;
set( hp(ip) , patchoptions{:} )
end
这只是一个快速缩放的视图,并打开了补丁(代码以较低的点数重新运行,否则会很快使整个图形混乱):
关于matlab - Matlab:“X射线”绘图线穿过补丁,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27275906/