2014-12-03 98 views
3

问题Matlab的:通过补丁

“X射线”的情节主线我试图显现3-d路径,具有“云”围绕它表示数据的标准偏差一起。我希望能够看到一条粗黑的线条,它周围有一个灰色的区域,没有任何线条混浊,就好像看到像X光一样穿过云层。

尝试Attempt 1

我以前plot3创建一条粗线和patch创建一系列的围绕着线的每一个点为中心的盒子(有对剧情的一些额外的东西来代表启动/停止和方向,我也希望他们很容易看到)。我尝试使用的alpha,但这会造成线条混浊,使得灰色的亮度随着灰色盒子的视线而变化。我希望alpha为1,以便每个灰色框的颜色完全相同,但我希望找到某种方法使云线均匀地穿过云。

最小例

按照要求,这里是一个小例子,其产生以下的情节。 Example 2

% 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)。其余的应由各自的意见解释。

谢谢你的帮助!

+0

我认为(不知道),你可以使用zbuffer渲染模式来做到这一点。所以你设置了(gca,'Renderer','zbuffer'),然后首先绘制灰色的东西,然后绘制黑色的东西。我不知道它是否会起作用,但它可能... – 2014-12-03 17:21:30

+1

我曾经有一个类似的问题。我只通过创建一个表面来解决这个问题,表示云的包络(由中心线上的所有光盘/盒子定义)。然后你只需要管理一个对象,并且许多对象的Alpha值不会相加,因此主中心线很容易看到。你需要提供更多的数据,然后将其应用于你的案例。 – Hoki 2014-12-03 18:23:57

+1

@AnderBiguri,好想法。我试过你的解决方案,但它只能在2D中工作。在3D中,即使您将线条的句柄放在uistack顶部(或最后绘制它),渲染器也会检测到另一个对象(相对于相机)“后面”的部分,并且不会渲染线条(在所有)为这个隐藏的部分。 – Hoki 2014-12-03 18:27:37

回答

1

下面是我在评论中提到的解决方案的实现(仅绘制一个单一的surface云)。

它没有优化,有几个for循环可以避免巧妙使用bsxfun或这些系列的帮助函数,但它运行正常。在每个点找到曲线的切线并定向(旋转)每个横截面的数学也可能被简化,但这不是我的强项,所以如果他们觉得这样,我会把它留给专家。

基本上,它定义了一个半径与某些东西(应用程序中的标准偏差,示例中的随机值)成比例的圆(通常称为“横截面”,代码中)。然后每个圆都以3D方式旋转,因此它在翻译点处与曲线垂直。然后将所有这些圆圈用作单个图形对象的信封。

当表面重叠多次(取决于视角)时,主中线仍然有一点阴影,但主线始终可见。另外你只有一个图形对象来管理。

结果看起来像这样:

当然你也可以改变表面的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 

而这仅仅是对补丁的快速缩放视图(以较低数量的点码重演,否则很快塞满整个图):

cloudenvzoom

+0

谢谢你说明你的观点!尽管我的初始代码仍然存在一些alpha问题,但它看起来比我所使用的要好得多,而且它对我的使用来说是完美的。再次感谢您明确表达您的想法和实例的时间! – sgbrown 2015-01-27 22:07:19

1

我的Matlab的版本是旧的,但像这样将有望为你工作了:

  1. 先画出阴天补丁你做了以上(不实的路径还)。
  2. 商店轴柄:

    patch_axis = gca; 
    
  3. 创建一个新的,重叠的轴

    path_axis = axes('position',get(patch_axis,'position'),'visible','off'); 
    
  4. 绘制实线路径(在该新轴)。

  5. 链接旋转和patch_axis(后面)的限制,该path_axis的(在前面):

    set(rotate3d(path_axis),'ActionPostCallback', ... 
    @(src,evt)set(patch_axis,{'view','xlim','ylim','zlim'}, ... 
    get(path_axis,{'view','xlim','ylim','zlim'}))); 
    
  6. 如果你旋转你的视图中手动,那么它应该后排队两轴您第一次调整,并让他们排队。但是,如果你设置的旋转和极限的命令,那么你可以只包括轴手柄(patch_axis & path_axis)在set命令或复制后的设置:

    set(patch_axis,{'view','xlim','ylim','zlim'}, ... 
    get(path_axis,{'view','xlim','ylim','zlim'}) 
    

注意要调整轴属性(刻度标签等),您需要将其设置为可见的patch_axis而不是不可见的path_axis。如果您希望它通过手动旋转进行实时交互,我不确定如何获得对齐功能以在每次重绘时执行。

+0

谢谢你的代码!我试了一下,但也许这是一个版本问题,因为我认为我只能得到显示的路径,虽然它的重画并不完美(它看起来很嘈杂,有很多白色区域),所以SOMETHING一定会发生。这不完全是我的解决方案,但我认为它更接近了,谢谢。 – sgbrown 2015-01-27 15:26:33