Mike谈MATLAB图形

图形和数据可视化

请注意

Mike谈MATLAB图形已退役,不会更新。

符号距离字段

最近,我从一个MATLAB用户那里听说,他试图用这篇博文我之前写过。不幸的是,她的曲线比我在那篇文章中使用的曲线要复杂一些。这种沿着曲线扫描2D形状的方法有许多有趣的边缘情况,当曲线复杂时,您会遇到这些情况。

特别是,她的曲线实际上有一个“叉”。对于扫描方法来说,这是一个特别棘手的情况。你基本上需要把叉子的两半分别扫一遍,然后把其中一段扫到另一段里的部分切下来。这个问题的数学计算相当棘手。

幸运的是,还有另一种方法可以解决这个问题。这个方法需要大量计算,但是实现起来要简单得多。它被称为有符号距离字段.让我们看看如何使用SDF解决这个问题。

首先,我们需要一些样本数据。这是我做的一个。我有4个顶点。

vert = [-1 0 0;...1/2 0 0;...2 3/4 0;...2 -3/4 0];半径= .5;

我有三条线段连接这四个顶点。

分段= [1 2;2 3;2 4];

我们画出线段。

i = 1:尺寸(片段1)线(绿党(段(我:),1),绿党(段(我:),2),绿党(3段(我,:)))结束盒子Daspect([1 1 1])视图(3)

所以我们想要一个管状曲面,它沿着这些线段,平滑地融合在两个向右分叉的线段之间。

我们要做的第一件事是在我们感兴趣的空间上建立一个3D网格。网格越大,你的表面就越好,但是你使用的内存会很快增加。

[y、x、z] = ndgrid (linspace (-1.25, 1.25, 90), linspace (2.25, 90), linspace 45(-0.75、0.75));

现在我们要创建第四个数组来保存带符号的距离字段。

D = 0(大小(z));

现在我们遍历所有网格点。在网格中的每个点,我们将遍历所有线段,并计算从网格点到线段上最近点的距离。我们保留最小的距离,保存在d中。

I =1:数字(z) p = [x(I),y(I),z(I)];初始化我们的距离到inf。最近= inf;遍历所有线段。J =1:size(segments,1) q1 = vert (segments(J,1),:);Q2 = (segments(j,2),:);对于线段q1 + t*(q2-q1),计算t的值,其中到p的%距离被最小化。夹住[0 1],这样我们就不会走火%线段的结束。Invlen2 = 1/dot(q2-q1,q2-q1);T = max(0,min(1,-dot(q1-p,q2-q1)*invlen2));V = q1 + t*(q2-q1);这是我们所见过的网格元素中最小的吗?最接近= min(最接近,范数(v-p)-半径);结束将距离插入数组。D (i) =最近;结束

注意,我保存到数组d中的值是该网格点到线段的距离,减去线段上该点处曲面的半径。这意味着在网格中d小于0的任何地方都在管子内部,而d大于0的任何地方都在管子外部。正如我们在这篇文章关于隐曲面,这是一个工作等值面函数

等值面(x, y, z, d, 0) camlight

我们可以看到这个表面和我们的原始模型很好地对齐了,并且很好地混合了叉子。

让我们删除这些行并改变视图,这样我们就可以看到fork的内部。看到那个十字路口多整洁了吗?用另一种方法很难做到这一点。

还要注意它是如何使结尾圆润的。我们可以看到里面,因为我把网格做得很紧。如果我的网格再大一点,那么三个端点都将被一个半球覆盖。这是符号距离场方法的特点。

删除(findobj (gca),“类型”“行”))视图([41 18])

但是如果我们在每个顶点上都有不同的半径,并且想让管子在每段末端的半径之间进行插值呢?

假设我们想要每个顶点的半径。

半径=[。5.45.125 .25]; clai = 1:尺寸(片段1)线(绿党(段(我:),1),绿党(段(我:),2),绿党(3段(我,:)))结束[xs,ys,zs] =球面;i = 1:尺寸(绿党,1)表面(绿党(我,1)+半径(i) * x,...绿党(我,2)+半径(i) * y,...绿党(我,3)+(我)* z半径,...“FaceColor”“黄色”“EdgeColor”“没有”);结束camlight

这有点棘手,但基本上就是改变距离函数的问题。我们不能再用简单的公式来计算点到中心线的距离了。

我找到了一个距离函数这篇论文

快速计算点与圆柱、锥、线扫球和锥球之间的距离
Aurelien Barbier和Eric Galin
百合- CNRS
1 .法国里昂大学
69622 Villeurbanne Cedex,法国

作者称这种两端各有一条中心线和半径的原语为“锥球”原语。

这个距离方程更复杂,所以我将预先计算一些项来帮助提高性能。

Nsegments = size(segment,1);Invlen2 = 0 (1,nsegments);Trange = 0 (nsegments,2);Adj_radii = 0 (nsegments,2);I =1:nsegments q1 = (segment (I,1),:);Q2 = (segments(i,2),:);Len2 = dot(q2-q1,q2-q1);Invlen2 (i) = 1/len2;R1 = radii(segments(i,1));R2 = radii(segments(i,2));= r1-r2;S =√(len2 - ^2);Dl = delta * inlen2 (i);Sl = s * sqrt(invlen2(i)); trange(i,1) = r1 * dl; trange(i,2) = 1 + r2 * dl; adj_radii(i,1) = r1 * sl; adj_radii(i,2) = r2 * sl;结束

现在我们可以用这个新的距离公式重复我们的循环。

I =1:数字(z) p = [x(I),y(I),z(I)];最近= inf;J =1:size(segments,1) q1 = vert (segments(J,1),:);Q2 = (segments(j,2),:);R1 = radii(segments(j,1));R2 = radii(segments(j,2));T = -dot(q1-p,q2-q1)*invlen2(j);如果T < range(j,1) v = q1;R = r1;elseifT > range(j,2) v = q2;R = r2;其他的V = q1 + t*(q2-q1);1) adj_t = (t-trange (j) / (trange (j, 2) -trange (j, 1));r = adj_radii (j, 1) + adj_t * (adj_radii (j, 2) -adj_radii (j, 1));结束最接近= min(最接近,范数(v-p)-r);结束D (i) =最近;结束

然后我们可以用等值面,就像我们之前做的那样。

等值面(x, y, z, d, 0)

看到表面是如何与球体对齐的了吗?现在我们删除它们,以便更好地观察。

删除(findobj (gca),“类型”“行”甘氨胆酸)删除(findobj (,“类型”“表面”))

这是另一个等值面我用这个技术用四面体的这6条边做的。

= randn(4,3);段= nchoosek(1:4,2);半径= 5/8 * rand(1,4);

这花了不少时间来计算,但是如果用扫描的方法来画的话会很棘手。

当你想要包裹一个复杂形状的曲面时,有符号距离函数是一种你应该考虑的技术。




发布与MATLAB®R2015b


  • 打印
  • 发送电子邮件