下面是我在评论中提到的解决方案的实现(仅绘制一个单一的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
而这仅仅是对补丁的快速缩放视图(以较低数量的点码重演,否则很快塞满整个图):
我认为(不知道),你可以使用zbuffer渲染模式来做到这一点。所以你设置了(gca,'Renderer','zbuffer'),然后首先绘制灰色的东西,然后绘制黑色的东西。我不知道它是否会起作用,但它可能... – 2014-12-03 17:21:30
我曾经有一个类似的问题。我只通过创建一个表面来解决这个问题,表示云的包络(由中心线上的所有光盘/盒子定义)。然后你只需要管理一个对象,并且许多对象的Alpha值不会相加,因此主中心线很容易看到。你需要提供更多的数据,然后将其应用于你的案例。 – Hoki 2014-12-03 18:23:57
@AnderBiguri,好想法。我试过你的解决方案,但它只能在2D中工作。在3D中,即使您将线条的句柄放在uistack顶部(或最后绘制它),渲染器也会检测到另一个对象(相对于相机)“后面”的部分,并且不会渲染线条(在所有)为这个隐藏的部分。 – Hoki 2014-12-03 18:27:37