【问题标题】:3D rotations to connect balls and cylinders连接球和圆柱体的 3D 旋转
【发布时间】:2013-06-12 03:13:10
【问题描述】:

我的任务是为图形绘制程序编写一个基于 python 的插件,该程序生成图形的 STL 模型。图是由顶点和边组成的对象,其中顶点由 3D 球(镶嵌二十面体)表示,边由两端连接两个球的圆柱体表示。 3D 模型的最终结果是将其转储到 STL 文件中以进行 3D 打印。我能够毫无问题地生成球和圆柱体的 3D 模型,但是在生成整体模型以及让球和圆柱体正确连接时遇到了一些问题。

我最初的想法是在原点创建镶嵌二十面体,然后将它们转换为顶点的位置。这工作正常。然后,对于每条边,我会在原点创建一个圆柱体,将其旋转到正确的角度,使其指向正确的方向,然后将其平移到两个顶点之间的中点,以便嵌入圆柱体的末端在二十面体中。这就是事情出错的地方。我在正确旋转时遇到了一些困难。为了计算旋转,我正在执行以下操作:

首先,我找到两点之间的角度如下(其中源和目标都是图中的顶点,属于我当前正在处理的边):

        deltaX = source.x - target.x
        deltaY = source.y - target.y
        deltaZ = source.z - target.z

        xyAngle = math.atan2(deltaX, deltaY) 
        xzAngle = math.atan2(deltaX, deltaZ) 
        yzAngle = math.atan2(deltaY, deltaZ)

计算的角度看起来很合理,据我所知,它实际上代表了顶点之间的角度。例如,如果我在 (1, 1, 0) 处有一个顶点,在 (3, 3, 0) 处有另一个顶点,则连接它们的角边确实显示为两个顶点之间的 45 度角。 (那个,或 -135 度,取决于哪个顶点是源,哪个是目标)。

计算完角度后,我会创建一个圆柱体并按照已计算的角度旋转它,就像这样,使用我创建的其他一些类: c = 圆柱体() c.createCylinder(edgeThickness, edgeLength)

        c.rotateX(-yzAngle)
        c.rotateY(xzAngle)
        c.rotateZ(-xyAngle)
        c.translate(edgePosition.x, edgePosition.y, edgePosition.z)

(其中edgePosition是图中两个顶点的中点,edgeThickness是正在创建的圆柱体的半径,edgeLength是两个顶点之间的距离)。

如前所述,气缸的旋转无法按预期工作。它似乎在 x/y 平面上进行了正确的旋转,但是一旦边的顶点在所有三个分量(x、y 和 z)中都不同,旋转就会失败。这是一个 x 和 y 分量不同但 z 分量不同的图形示例:

这是生成的 STL 文件,如 Makerware 中所示(用于将 3D 模型发送到 3D 打印机):

(左下角的额外圆柱体看起来是我目前为了测试目的而留下的——一个指向 z 轴方向的圆柱体,位于原点)。

如果我使用同一张图并将中间顶点在 z 轴上移出,那么现在所有边都涉及所有三个轴上的角度,我会得到如下结果:

如应用程序所示:

生成的 STL 文件,如 Makerware 中所示:

...和从侧面看的同一模型:

如您所见,圆柱体肯定不会像我想象的那样与球相遇。我的问题是:我做这件事的方法是有缺陷的,还是我在轮换的某个地方犯了一些小但关键的错误?我很确定旋转功能本身没有问题,因为我已经能够独立验证它们是否按预期工作。我还尝试创建一个旋转函数,该函数接受偏航、俯仰和滚动并同时执行所有三项,它似乎生成了相同的结果,如下所示:

c.rotateYawPitchRoll(xzAngle, -yzAngle, -xyAngle)

所以...有人对我可能做错了什么有任何想法吗?

更新:正如 joojaa 所指出的,这是计算正确角度以及应用它们的顺序的组合。为了让事情正常进行,我首先计算 x 轴上的旋转,如下所示:

zyAngle = math.atan2(deltaVector.z, deltaVector.y)

其中 deltaVector 是目标向量和源向量之间的差异。但是,此轮换尚未应用!下一步就是计算y轴上的旋转,如下:

angle = vector.angleBetweenVectors(vector(target.x - source.x, target.y - source.y, target.z - source.z), vector(target.x - source.x, target.y - source.y, 0.0))

一旦计算了两个旋转,它们就会被应用......以相反的顺序!首先是 x,然后是 y:

c.rotateY(angle)
c.rotateX(-zyAngle) #... where c is a cylinder object

似乎仍然存在一些错误,但这似乎至少适用于一个简单的测试用例。

【问题讨论】:

    标签: python math 3d


    【解决方案1】:

    旋转是连续发生的,所以角度会相互影响。不可能使用欧拉模型一次旋转它们。这就是为什么您不能只根据第一个静态情况计算旋转。想象一下转动一个立方体,使它直立在角落里。是的,第一次旋转是 45,但第二次不是,因为此时立方体已经转动(画出序列的每一步,看看会发生什么)。空间旋转并非易事。

    所以你需要旋转一个角度然后重新计算第二个角度等等。这也是为什么您的第一次轮换工作正常的原因。除非您有兴趣确保围绕轴的旋转具有特定方向,否则您只需要旋转 2 次。

    我建议您使用轴角或矩阵来代替。主要是因为在轴角度中这是微不足道的,角度是沿管开始和结束向量之间的点,轴是这两个向量之间的交叉点。然后,如果需要,您可以将它们转换为欧拉角。但也许你可以直接使用矩阵。有关如何转换以及如何直接计算旋转的想法,请参阅:transformations.py,作者 Christoph Gohlke。另请参阅随附的 c 源代码。

    我想我需要稍微扩展一下这个答案

    对于这个问题,有一个非常简单的方法可以避开您和许多其他人的所有问题。答案是不要使用欧拉角旋转。我花了很多脑力试图将欧拉旋转解释为最终在没有欧拉旋转的情况下更容易解决的问题。如果您想更多地想出更多的答案,我将只留下一个理由来证明这一点。

    大多数使用欧拉旋转序列的原因是您可能不了解欧拉角。事实上,只有少数情况是好的。没有自尊的程序员使用欧拉旋转来解决这个问题。您所做的是使用矢量数学代替。

    所以你有通常计算的从源到目标的方向向量:

    along = normalize(target-source)
    

    这只是您的矩阵行之一(或列符号取决于模型制造商),与您的圆柱体原始方向相对应的行(行只是 x y z w),然后您需要另一个垂直于这个的向量。选择一个任意向量,如向上(如果您的沿指向靠近向上,则选择向左)。将这个向上向量与第二行方向的沿相乘。最后将您的来源作为最后一行,最后一列中为 1。完成完全形成的仿射矩阵描述圆柱优先级。因为您可以绘制矢量,所以更容易理解。

    有更短的方法,但这个很容易理解。

    【讨论】:

    • 以连续顺序发生的旋转是有意义的,并且只进行两次旋转(例如一个在 X 轴上,一个在 Y 轴上)。有一件事我仍然不清楚。完成 X 轴旋转后,如何重新计算 Y 轴旋转所需的角度?到目前为止,我这样做的尝试都失败了。我也更愿意使用矩阵,但我正在使用一组相当狭窄的函数/类,并试图将外部依赖性保持在最低限度,因此只能通过角度来完成。
    • 我对重新计算角度仍然有些困惑。假设我有一条边,顶点位于 (1, 1, 1) 和 (5, 5, 5)。我如上所述计算角度 y/z 角度,并在 x 轴上旋转该量。这种旋转如何影响我需要绕 Y 轴旋转的角度?我不太明白它会如何影响那个角度,以及为什么需要重新计算它,但试图以这种方式做事会导致看起来不正确的结果。
    • 通常 3d 图形程序员使用矩阵乘法,因为它是通用的。但是,在您的情况下,如果您围绕 y 轴(在我的情况下向上)旋转,则可以这样想象,而不是下一次旋转的角度是原始向量与 Z 分量为 0 的原始向量之间的角度,现在只需确保您的原始位置匹配这种思路。
    • 我仍然没有完全遵循你最后的声明,关于确保原始位置与修改后的行匹配。我创建了一个只有两个顶点 (1,1,1) 和 (5,5,5) 的简单示例。当我第一次计算 x 轴上的旋转角度时,我得到一个 45 度的角度,这看起来是正确的。然后,当我重新计算 y 轴旋转的角度时,我得到 35.264 度(左右)。如果我应用这个旋转,它很接近,但仍然没有完全对齐,如向下看 z 轴所示:dl.dropboxusercontent.com/u/144587/Graph6.png
    • 我几乎开始怀疑它在其他地方是否不是问题,例如旋转圆柱体的代码。如果我看一下图像,我可以计算出两个球体中心之间的角度,结果是 45 度。如果我计算圆柱的角度,它会接近 30 度。
    猜你喜欢
    • 1970-01-01
    • 2018-03-07
    • 1970-01-01
    • 2013-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多