OrientedArrow 在两个随机生成的点之间绘制一根带箭头的线,以可视化一个向量

一:主要的知识点

1、说明

本文只是教程内容的一小段,因博客字数限制,故进行拆分。主教程链接:vtk教程------逐行解析官网所有Python示例-CSDN博客

2、知识点纪要

本段代码主要涉及的有①对源数据的平移、缩放和缩放,②vtkTransform的concanate方法

二:代码及注释

python 复制代码
from vtkmodules.vtkFiltersSources import vtkArrowSource
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkMath, vtkMinimalStandardRandomSequence
from vtkmodules.vtkCommonMath import vtkMatrix4x4
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkFiltersGeneral import vtkTransformPolyDataFilter
from vtkmodules.vtkFiltersSources import (
    vtkArrowSource,
    vtkSphereSource
)
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer
)


def main():
    colors = vtkNamedColors()
    colors.SetColor("BkgColor", [26, 51, 77, 255])
    USE_MATRIX = True
    """
    arrowSource无法通过自身携带的函数设置箭头的起点、终点和方向
    很多vtk源都创建了一个标准化的模型,然后由变换负责将其移动、旋转和缩放到正确的位置
    这样做的目的是:
    解耦:将几何体的生成和它的空间变换分离开来,使得代码更清晰、更模块化
    效率:你可以在一次渲染中,对一个原始模型应用不同的变换,来创建多个实例
    """
    arrowSource = vtkArrowSource()  # 标准箭头,从(0,0,0)指向(1, 0, 0)

    """
    下面这段代码的目的就是创建一个变换矩阵,用于将一个标准化的箭头
    (从 (0,0,0) 指向 (1,0,0) 的箭头)移动、旋转和缩放,使其从一个随机起点指向另一个随机终点
    """
    startPoint = [0] * 3
    endPoint = [0] * 3
    rng = vtkMinimalStandardRandomSequence()
    rng.SetSeed(82443)
    for i in range(3):
        rng.Next()
        startPoint[i] = rng.GetRangeValue(-10, 10)
        rng.Next()
        endPoint[i] = rng.GetRangeValue(-10, 10)

    normalizedX = [0] * 3
    normalizedY = [0] * 3
    normalizedZ = [0] * 3
    vtkMath.Subtract(endPoint, startPoint, normalizedX)
    length = vtkMath.Norm(normalizedX)
    vtkMath.Normalize(normalizedX)

    arbitrary = [0] * 3
    for i in range(0, 3):
        rng.Next()
        arbitrary[i] = rng.GetRangeValue(-10, 10)
    vtkMath.Cross(normalizedX, arbitrary, normalizedZ)
    vtkMath.Normalize(normalizedZ)

    vtkMath.Cross(normalizedZ, normalizedX, normalizedZ)

    matrix = vtkMatrix4x4()
    matrix.Identity()
    for i in range(0, 3):
        matrix.SetElement(i, 0, normalizedX[i])
        matrix.SetElement(i, 1, normalizedY[i])
        matrix.SetElement(i, 2, normalizedZ[i])

    """
    必须严格按照缩放 → 旋转 → 平移的顺序来应用这些变换
    个变换矩阵可以将一个点从其局部坐标系转换到世界坐标系。
    T 代表平移矩阵(Translation)
    R 代表旋转矩阵(Rotation)
    S 代表缩放矩阵(Scale)
    P_local 代表点在局部坐标系中的位置
    P_world 代表点在世界坐标系中的位置
    最终的变换公式是:
    Pworld =T∗R∗S∗Plocal
    这个公式意味着,对一个点应用变换时,应该先缩放,再旋转,最后平移。这是因为:
    如果你先平移再旋转:物体会绕着新的平移位置旋转,而不是绕着它自身的中心旋转,这通常不是你想要的效果。
    如果你先旋转再缩放:缩放会沿着已经旋转过的轴进行,可能导致不均匀的变形。
    因此,从数学上讲,缩放 → 旋转 → 平移 的顺序是正确的。
    """

    """
    vtkTransform的Concatenate 方法
    有一个重要的特性,它执行的是矩阵后乘。这意味着,新连接的变换矩阵会放在当前变换矩阵的右边
    最终的复合变换矩阵是 T∗R∗S,这与数学上的正确顺序完全一致。
    因此,在 VTK 中使用 Concatenate 方法时,你需要按照平移、旋转、缩放的顺序调用函数,
    才能得到正确的缩放、旋转、平移效果。
    """
    transform = vtkTransform()
    transform.Translate(startPoint)
    transform.Concatenate(matrix)
    transform.Scale(length, length, length)

    """
    两种思路
    1、直接对数据进行操作,产生新的数据,
    2、在actor中,使用SetUserMatrix,不会产生新的数据,效率更高,但是不会改变底层数据
    """

    transformPD = vtkTransformPolyDataFilter()
    transformPD.SetTransform(transform)
    transformPD.SetInputConnection(arrowSource.GetOutputPort())

    mapper = vtkPolyDataMapper()
    actor = vtkActor()
    if USE_MATRIX:
        mapper.SetInputConnection(arrowSource.GetOutputPort())
        actor.SetUserMatrix(transform.GetMatrix())
    else:
        mapper.SetInputConnection(transformPD.GetOutputPort())

    actor.SetMapper(mapper)
    actor.GetProperty().SetColor(colors.GetColor3d("Cyan"))


    sphereStartSource = vtkSphereSource()
    sphereStartSource.SetCenter(startPoint)
    sphereStartSource.SetRadius(0.8)
    sphereStartMapper = vtkPolyDataMapper()
    sphereStartMapper.SetInputConnection(sphereStartSource.GetOutputPort())
    sphereStart = vtkActor()
    sphereStart.SetMapper(sphereStartMapper)
    sphereStart.GetProperty().SetColor(colors.GetColor3d('Yellow'))

    sphereEndSource = vtkSphereSource()
    sphereEndSource.SetCenter(endPoint)
    sphereEndSource.SetRadius(0.8)
    sphereEndMapper = vtkPolyDataMapper()
    sphereEndMapper.SetInputConnection(sphereEndSource.GetOutputPort())
    sphereEnd = vtkActor()
    sphereEnd.SetMapper(sphereEndMapper)
    sphereEnd.GetProperty().SetColor(colors.GetColor3d('Magenta'))

    # Create a renderer, render window, and interactor
    renderer = vtkRenderer()
    renderWindow = vtkRenderWindow()
    renderWindow.SetWindowName('OrientedArrow')
    renderWindow.AddRenderer(renderer)
    renderWindowInteractor = vtkRenderWindowInteractor()
    renderWindowInteractor.SetRenderWindow(renderWindow)

    # Add the actor to the scene
    renderer.AddActor(actor)
    renderer.AddActor(sphereStart)
    renderer.AddActor(sphereEnd)
    renderer.SetBackground(colors.GetColor3d('BkgColor'))

    # Render and interact
    renderWindow.Render()
    renderWindowInteractor.Start()


if __name__ == '__main__':
    main()
相关推荐
做怪小疯子12 小时前
华为笔试0429
python·numpy
Warson_L12 小时前
Dictionary
python
郑寿昌13 小时前
UE5与UE6在Lumen和Nanite的差异解析
游戏引擎·图形渲染·着色器
寒山李白14 小时前
解决 python-docx 生成的 Word 文档打开时弹出“无法读取内容“警告
python·word·wps·文档·docx·qoder
2401_8323655215 小时前
JavaScript中rest参数(...args)取代arguments的优势
jvm·数据库·python
Sirius.z15 小时前
第J3周:DenseNet121算法详解
python
2301_7796224115 小时前
Go语言怎么用信号量控制并发_Go语言semaphore信号量教程【入门】
jvm·数据库·python
2301_7662834415 小时前
c++如何将控制台输出保存到文件_cout重定向到txt【详解】
jvm·数据库·python
小康小小涵17 小时前
基于ESP32S3实现无人机RID模块底层源码编译
linux·开发语言·python
lzjava202417 小时前
Python的函数
开发语言·python