Matlab:"X-射线"通过补丁画线 [英] Matlab: "X-Ray" plot line through patch

查看:87
本文介绍了Matlab:"X-射线"通过补丁画线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

我正在尝试可视化3-D路径以及围绕它的代表数据标准偏差的云".我希望能够看到一条较粗的黑线作为路径,周围有一个灰色区域,而且线没有任何模糊感,就像看到线一样穿过X射线一样.

I'm attempting to visualize a 3-D path, along with a "cloud" around it which represents the standard deviation of data. I would like to be able to see a thick black line as the path with an EVENLY gray area around it, without any cloudiness to the lines, as if seeing the line through the cloud like an x-ray.

尝试

我用 plot3 创建了一条粗线,并使用了 patch 创建了一系列围绕该线的每个点居中的框(该图上还有一些其他东西要代表开始/停止和方向,我也希望它们很容易被看到).我尝试使用 patch alpha ,但是这会给线造成模糊感,因此无论有多少个灰色框,灰色的亮度都会发生变化.视线.我希望 alpha 为1,以使每个灰色框的颜色完全相同,但是我希望找到某种方法使线条均匀地穿过云层.

I used plot3 to create a thick line and patch to create a series of boxes centered around each point of the line (there are some extra things on the plot to represent start/stop and direction, I would also like them to be seen easily). I tried playing with the alpha of the patch, but that creates a cloudiness to the line, such that the brightness of the gray color changes with however many gray boxes are in the line of sight. I would like the alpha to be 1, so that each gray box is exactly the same color, but I was hoping to find some way to make line seen through the cloud evenly.

最小示例

根据要求,这是一个最小的示例,它产生了下面的图.

As requested, here is a minimal example, which produces the plot below.

% 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 命令有很多参数.前三行仅通过定义中心点正负号(等于或减去该立方体的一半宽度) cloud(i)来简单地定义定义一个立方体的8个顶点的x,y和z坐标.>.其余的应通过各自的注释进行解释.

Apologies for the VERY long stretch of code in the for loop, there are quite a large number of arguments to the patch command. The first three lines are simply defining the x, y, and z coordinates of the 8 vertices which define a cube, by specifying the center point plus or minus some half-width of the cube, cloud(i). The rest should be explained by their respective comments.

谢谢您的帮助!

推荐答案

这是我在评论中提到的解决方案的实现(只绘制了一个 surface 云).

Here is an implementation of the solution I was mentioning in the comment (just drawing one single surface cloud).

它不是经过优化的,有一些 for 循环可以通过聪明地使用bsxfun或这些辅助函数系列来避免,但它可以正常运行.查找曲线在每个点处的切线并确定(旋转)每个横截面的数学方法可能也可以简化,但这并不是我的强项,因此我将其交给专家,如果他们愿意的话.

It is not optimized, there are a few for loop which could be avoided with clever use of bsxfun or these family of helper function, but it runs ok as it is. The math for finding the tangent of the curve at each point and orienting (rotating) each cross section could probably be simplified as well, but this is not my strong suit so I leave that to the experts if they feel like it.

基本上,它定义一个圆(在代码中通常称为横截面"),该圆的半径与某物(应用程序中的标准偏差,示例中为随机值)成比例.然后,每个圆都以3D旋转,因此它在平移点处垂直于曲线.然后,所有这些圆圈都用作单个 surface 图形对象的信封.

Basically, it define a circle (often referred as "cross section" in the code) with a radius proportional to something (the standard deviation in your application, a random value in the example). Each circle is then rotated in 3D so it is normal to the curve at the point where it is translated. All these circles are then used as an envelope for a single surface graphic object.

当表面自身多次重叠时(取决于视角),主中心线仍然存在一些阴影,但是主线始终可见.另外,您只需要管理一个图形对象.

There is still a bit of shadowing of the main centre line when the surface overlap itself many time (depending on the view angle), but the main line is always visible. Plus you only have one graphic object to manage.

结果看起来像这样:

当然,您可以根据自己的喜好更改表面的 AlphaValue .我定义了一个与颜色信息的数据大小相同的完整矩阵.目前,所有参数都设置为 0 (因此它指向默认颜色图中的绿色),但是如果您要创建另一个参数的颜色函数,这也很容易.相应地调整颜色矩阵(以及随之而来的颜色图).

Of course you can vary the AlphaValue of the surface to your liking. I defined a full matrix the same size as the data for the colour information. At the moment it is all set to 0 (so it points to the green colour in the default colormap), but this way it is also easy if you want to make the colour function of another parameter, just adjust the colour matrix accordingly (and the colormap that goes with it).

代码结尾处有一个选项,可将每个横截面显示为面片对象.它不打算在最终结果中使用,但可以帮助您了解如果要进行自己的修改,整个过程是如何构造的.

There is an option at the end of the code to display each cross section as a patch object. It is not intended to be used in the final result, but it can help you understand how the whole thing is constructed if you want to make your own modifications.

代码如下:

%% // 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


这只是补丁已打开的快速缩放视图(代码以较少的点数重新运行,否则会迅速使整个图形混乱):


And this is just a quick zoomed view with the patches on (code rerun with a lower number of points, otherwise it quickly clutter the whole figure):

这篇关于Matlab:"X-射线"通过补丁画线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆