线性代数——向量运算可视化(python实现)

        研究3D图形学处理,很多地方需要用到的线性代数的知识,将线性代数结合3D可视化编程,能够加深对线性代数应用场景的理解。

一、负向量

计算公式:

-[a1, a2, ....,an]=[-a1, -a2, ....,-an]

几何意义:

一个与原向量长度相同,方向相反的向量,如下图所示,向量-a与向量a长度相等,方向相反

可视化:

代码实现:

import vtk
import math
import numpy as np


def arrowCreate(target, origin, length):
    math = vtk.vtkMath()
    math.Subtract(target, origin, target)  # 计算向量的朝向 target=target-origin

    points = vtk.vtkPoints()  # 记录起点坐标
    points.InsertNextPoint(origin)

    vertex = vtk.vtkVertex()  # 建立起点的拓扑(不建立拓扑的话是不行的)
    vertex.GetPointIds().SetNumberOfIds(points.GetNumberOfPoints())
    for i in range(points.GetNumberOfPoints()):
        vertex.GetPointIds().SetId(i, i)  # setId(拓扑的id, 顶点的id)

    normals = vtk.vtkDoubleArray()  # 创建法向量属性,存入向量的朝向target
    normals.SetNumberOfComponents(3)
    normals.InsertNextTuple(target)

    scalars = vtk.vtkDoubleArray()  # 创建标量属性,存入向量的长度length
    scalars.SetNumberOfComponents(1)
    scalars.SetName("scalars")
    scalars.InsertNextTuple1(length)

    vertices = vtk.vtkCellArray()  # 将建立的拓扑用vtkCellArray封装,用于赋予vtkPolyData
    vertices.InsertNextCell(vertex)

    polydata = vtk.vtkPolyData()  # 创建几何实体
    polydata.SetPoints(points)  # 赋予起点
    polydata.SetVerts(vertices)  # 赋予拓扑
    polydata.GetPointData().SetNormals(normals)  # 赋予向量朝向
    polydata.GetPointData().SetScalars(scalars)  # 赋予向量长度

    arrow = vtk.vtkArrowSource()
    arrow.Update()
    glyph = vtk.vtkGlyph3D()
    glyph.SetInputData(polydata)
    glyph.SetSourceData(arrow.GetOutput())
    glyph.SetScaleFactor(0.1)
    glyph.SetVectorModeToUseNormal()
    glyph.Update()

    return glyph


# 画箭头表示向量
targets = np.array([5, 5, 0])
targets2 = targets * -1 # 负向量


length = math.sqrt(targets[0]**2 + targets[1]**2 + targets[2]**2) # 原始向量的模长
length2 = math.sqrt(targets2[0]**2 + targets2[1]**2 + targets2[2]**2) # 变化后向量的模长
glyph = arrowCreate(targets, [0, 0, 0], length) # 原始向量对应的箭头
glyph2 = arrowCreate(targets2, [0, 0, 0], length) # 变换后的向量对应的箭头

mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(glyph.GetOutput())
actor = vtk.vtkActor()
actor.SetMapper(mapper)

mapper2 = vtk.vtkPolyDataMapper()
mapper2.SetInputData(glyph2.GetOutput())
actor2 = vtk.vtkActor()
actor2.SetMapper(mapper2)


# 画三维坐标轴
Axes = vtk.vtkAxesActor()
# 修改vtkAxesActor默认的字体颜色,Axes为vtkAxesActor的对象指针
Axes.GetXAxisCaptionActor2D().GetProperty().SetColor(1,0,0) #修改X字体颜色为红色
Axes.GetYAxisCaptionActor2D().GetProperty().SetColor(0,2,0) #修改Y字体颜色为绿色
Axes.GetZAxisCaptionActor2D().GetProperty().SetColor(0,0,3) #修改Z字体颜色为蓝色


colors = vtk.vtkNamedColors()
# 可视化
# 窗口1:显示原始向量
renderer1 = vtk.vtkRenderer()
renderer1.SetViewport(0.0, 0.0, 0.5, 1)
renderer1.AddActor(actor)
renderer1.AddActor(Axes)
renderer1.SetBackground(colors.GetColor3d('skyblue'))

# 窗口2:显示原始向量和变换后的向量
renderer2 = vtk.vtkRenderer()
renderer2.SetViewport(0.5, 0.0, 1, 1)
renderer2.AddActor(actor2)
# renderer2.AddActor(actor)
renderer2.AddActor(Axes)
renderer2.SetBackground(colors.GetColor3d('skyblue'))

renderWindow = vtk.vtkRenderWindow()
renderWindow.AddRenderer(renderer1)
renderWindow.AddRenderer(renderer2)
renderWindow.SetSize(640, 480)
renderWindow.Render()
renderWindow.SetWindowName('PointCloud')

renderWindowInteractor = vtk.vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.Initialize()
renderWindowInteractor.Start()

二、向量大小(长度、模)

计算公式:

||V||=\sqrt{v{_{1}}^{2}+v{_{2}}^{2}+......+v{_{n}}^{2}}

几何意义:

求向量的长度,向量的模是个标量

代码实现:

a1 = np.array([2, 2, 1])
length1 = math.sqrt(a1[0]**2 + a1[1]**2 + a1[2]**2) # 向量的模长
print("三维向量[2, 2, 1]的模为:", length1)

三、标量与向量的乘法

计算公式:

k[v_{1},v_{2},v_{3},......,v_{n}]=[kv_{1},kv_{2},kv_{3},......,kv_{n}]

几何意义:

通过与标量相乘,可以改变向量的长度,如下图所示,向量通过与乘以2,长度变为原来的一倍

可视化:

代码实现:

import vtk
import math
import numpy as np


def arrowCreate(target, origin, length):
    math = vtk.vtkMath()
    math.Subtract(target, origin, target)  # 计算向量的朝向 target=target-origin

    points = vtk.vtkPoints()  # 记录起点坐标
    points.InsertNextPoint(origin)

    vertex = vtk.vtkVertex()  # 建立起点的拓扑(不建立拓扑的话是不行的)
    vertex.GetPointIds().SetNumberOfIds(points.GetNumberOfPoints())
    for i in range(points.GetNumberOfPoints()):
        vertex.GetPointIds().SetId(i, i)  # setId(拓扑的id, 顶点的id)

    normals = vtk.vtkDoubleArray()  # 创建法向量属性,存入向量的朝向target
    normals.SetNumberOfComponents(3)
    normals.InsertNextTuple(target)

    scalars = vtk.vtkDoubleArray()  # 创建标量属性,存入向量的长度length
    scalars.SetNumberOfComponents(1)
    scalars.SetName("scalars")
    scalars.InsertNextTuple1(length)

    vertices = vtk.vtkCellArray()  # 将建立的拓扑用vtkCellArray封装,用于赋予vtkPolyData
    vertices.InsertNextCell(vertex)

    polydata = vtk.vtkPolyData()  # 创建几何实体
    polydata.SetPoints(points)  # 赋予起点
    polydata.SetVerts(vertices)  # 赋予拓扑
    polydata.GetPointData().SetNormals(normals)  # 赋予向量朝向
    polydata.GetPointData().SetScalars(scalars)  # 赋予向量长度

    arrow = vtk.vtkArrowSource()
    arrow.Update()
    glyph = vtk.vtkGlyph3D()
    glyph.SetInputData(polydata)
    glyph.SetSourceData(arrow.GetOutput())
    glyph.SetScaleFactor(0.1)
    glyph.SetVectorModeToUseNormal()
    glyph.Update()

    return glyph


# 画箭头表示向量
targets = np.array([4, 4, 3])
# targets2 = targets * -1 # 负向量
targets2 = targets * 2 # 标量与向量相乘
print(targets2)

length = math.sqrt(targets[0]**2 + targets[1]**2 + targets[2]**2) # 原始向量的模长
length2 = math.sqrt(targets2[0]**2 + targets2[1]**2 + targets2[2]**2) # 变化后向量的模长
glyph = arrowCreate(targets, [0, 0, 0], length) # 原始向量对应的箭头
glyph2 = arrowCreate(targets2, [0, 0, 0], length2) # 变换后的向量对应的箭头

mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(glyph.GetOutput())
actor = vtk.vtkActor()
actor.SetMapper(mapper)

mapper2 = vtk.vtkPolyDataMapper()
mapper2.SetInputData(glyph2.GetOutput())
actor2 = vtk.vtkActor()
actor2.SetMapper(mapper2)


# 画三维坐标轴
Axes = vtk.vtkAxesActor()
# 修改vtkAxesActor默认的字体颜色,Axes为vtkAxesActor的对象指针
Axes.GetXAxisCaptionActor2D().GetProperty().SetColor(1,0,0) #修改X字体颜色为红色
Axes.GetYAxisCaptionActor2D().GetProperty().SetColor(0,2,0) #修改Y字体颜色为绿色
Axes.GetZAxisCaptionActor2D().GetProperty().SetColor(0,0,3) #修改Z字体颜色为蓝色


colors = vtk.vtkNamedColors()
# 可视化
# 窗口1:显示原始向量
renderer1 = vtk.vtkRenderer()
renderer1.SetViewport(0.0, 0.0, 0.5, 1)
renderer1.AddActor(actor)
renderer1.AddActor(Axes)
renderer1.SetBackground(colors.GetColor3d('skyblue'))

# 窗口2:显示原始向量和变换后的向量
renderer2 = vtk.vtkRenderer()
renderer2.SetViewport(0.5, 0.0, 1, 1)
renderer2.AddActor(actor2)
# renderer2.AddActor(actor)
renderer2.AddActor(Axes)
renderer2.SetBackground(colors.GetColor3d('skyblue'))

renderWindow = vtk.vtkRenderWindow()
renderWindow.AddRenderer(renderer1)
renderWindow.AddRenderer(renderer2)
renderWindow.SetSize(640, 480)
renderWindow.Render()
renderWindow.SetWindowName('PointCloud')

renderWindowInteractor = vtk.vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.Initialize()
renderWindowInteractor.Start()

四、单位向量标准化

计算公式:

\widehat{V}=\frac{V}{||V||}

几何意义:

使向量变成模等于1的向量,不改变向量方向

可视化:

代码实现:

import vtk
import math
import numpy as np


def arrowCreate(target, origin, length):
    math = vtk.vtkMath()
    math.Subtract(target, origin, target)  # 计算向量的朝向 target=target-origin

    points = vtk.vtkPoints()  # 记录起点坐标
    points.InsertNextPoint(origin)

    vertex = vtk.vtkVertex()  # 建立起点的拓扑(不建立拓扑的话是不行的)
    vertex.GetPointIds().SetNumberOfIds(points.GetNumberOfPoints())
    for i in range(points.GetNumberOfPoints()):
        vertex.GetPointIds().SetId(i, i)  # setId(拓扑的id, 顶点的id)

    normals = vtk.vtkDoubleArray()  # 创建法向量属性,存入向量的朝向target
    normals.SetNumberOfComponents(3)
    normals.InsertNextTuple(target)

    scalars = vtk.vtkDoubleArray()  # 创建标量属性,存入向量的长度length
    scalars.SetNumberOfComponents(1)
    scalars.SetName("scalars")
    scalars.InsertNextTuple1(length)

    vertices = vtk.vtkCellArray()  # 将建立的拓扑用vtkCellArray封装,用于赋予vtkPolyData
    vertices.InsertNextCell(vertex)

    polydata = vtk.vtkPolyData()  # 创建几何实体
    polydata.SetPoints(points)  # 赋予起点
    polydata.SetVerts(vertices)  # 赋予拓扑
    polydata.GetPointData().SetNormals(normals)  # 赋予向量朝向
    polydata.GetPointData().SetScalars(scalars)  # 赋予向量长度

    arrow = vtk.vtkArrowSource()
    arrow.Update()
    glyph = vtk.vtkGlyph3D()
    glyph.SetInputData(polydata)
    glyph.SetSourceData(arrow.GetOutput())
    glyph.SetScaleFactor(0.1)
    glyph.SetVectorModeToUseNormal()
    glyph.Update()

    return glyph


# 画箭头表示向量
a1 = np.array([4, -4, 3]) # 初始化4个三维向量
a2 = np.array([-5, 3, 2])
a3 = np.array([-2, -1, -5])
a4 = np.array([6, 6, 3])

# 计算向量的模长
length1 = math.sqrt(a1[0]**2 + a1[1]**2 + a1[2]**2) # 原始向量的模长
length2 = math.sqrt(a2[0]**2 + a2[1]**2 + a2[2]**2) # 原始向量的模长
length3 = math.sqrt(a3[0]**2 + a3[1]**2 + a3[2]**2) # 原始向量的模长
length4 = math.sqrt(a4[0]**2 + a4[1]**2 + a4[2]**2) # 原始向量的模长

# 画箭头表示向量
glyph1 = arrowCreate(a1, [0, 0, 0], length1) # 原始向量对应的箭头
glyph2 = arrowCreate(a2, [0, 0, 0], length2) # 变换后的向量对应的箭头
glyph3 = arrowCreate(a3, [0, 0, 0], length3) # 原始向量对应的箭头
glyph4 = arrowCreate(a4, [0, 0, 0], length4) # 变换后的向量对应的箭头


# 经过处理后的标准化向量
b1 = a1 / length1
b2 = a2 / length2
b3 = a3 / length3
b4 = a4 / length4

length11 = math.sqrt(b1[0]**2 + b1[1]**2 + b1[2]**2) # 原始向量的模长
length22 = math.sqrt(b2[0]**2 + b2[1]**2 + b2[2]**2) # 原始向量的模长
length33 = math.sqrt(b3[0]**2 + b3[1]**2 + b3[2]**2) # 原始向量的模长
length44 = math.sqrt(b4[0]**2 + b4[1]**2 + b4[2]**2) # 原始向量的模长

glyph11 = arrowCreate(a1, [0, 0, 0], length11) # 原始向量对应的箭头
glyph22 = arrowCreate(a2, [0, 0, 0], length22) # 变换后的向量对应的箭头
glyph33 = arrowCreate(a3, [0, 0, 0], length33) # 原始向量对应的箭头
glyph44 = arrowCreate(a4, [0, 0, 0], length44) # 变换后的向量对应的箭头


# 画个球
sphereSource = vtk.vtkSphereSource()
sphereSource.SetCenter(0, 0, 0)
sphereSource.SetRadius(0.1)
sphereSource.SetThetaResolution(1000)
sphereMapper = vtk.vtkPolyDataMapper()
sphereMapper.SetInputConnection(sphereSource.GetOutputPort())
sphereActor = vtk.vtkActor()
sphereActor.SetMapper(sphereMapper)
sphereActor.GetProperty().SetOpacity(0.2)

mapper1 = vtk.vtkPolyDataMapper()
mapper1.SetInputData(glyph1.GetOutput())
actor1 = vtk.vtkActor()
actor1.SetMapper(mapper1)

mapper2 = vtk.vtkPolyDataMapper()
mapper2.SetInputData(glyph2.GetOutput())
actor2 = vtk.vtkActor()
actor2.SetMapper(mapper2)

mapper3 = vtk.vtkPolyDataMapper()
mapper3.SetInputData(glyph3.GetOutput())
actor3 = vtk.vtkActor()
actor3.SetMapper(mapper3)

mapper4 = vtk.vtkPolyDataMapper()
mapper4.SetInputData(glyph4.GetOutput())
actor4 = vtk.vtkActor()
actor4.SetMapper(mapper4)

mapper11 = vtk.vtkPolyDataMapper()
mapper11.SetInputData(glyph11.GetOutput())
actor11 = vtk.vtkActor()
actor11.SetMapper(mapper11)

mapper22 = vtk.vtkPolyDataMapper()
mapper22.SetInputData(glyph22.GetOutput())
actor22 = vtk.vtkActor()
actor22.SetMapper(mapper22)

mapper33 = vtk.vtkPolyDataMapper()
mapper33.SetInputData(glyph33.GetOutput())
actor33 = vtk.vtkActor()
actor33.SetMapper(mapper33)

mapper44 = vtk.vtkPolyDataMapper()
mapper44.SetInputData(glyph44.GetOutput())
actor44 = vtk.vtkActor()
actor44.SetMapper(mapper44)

colors = vtk.vtkNamedColors()

# 画三维坐标轴
Axes = vtk.vtkAxesActor()


# 可视化
# 窗口1:显示原始向量
renderer1 = vtk.vtkRenderer()
renderer1.SetViewport(0.0, 0.0, 0.5, 1)
renderer1.AddActor(actor1)
renderer1.AddActor(actor2)
renderer1.AddActor(actor3)
renderer1.AddActor(actor4)
renderer1.AddActor(sphereActor)
renderer1.AddActor(Axes)
renderer1.SetBackground(colors.GetColor3d('skyblue'))

# 窗口2:显示原始向量和变换后的向量
renderer2 = vtk.vtkRenderer()
renderer2.SetViewport(0.5, 0.0, 1, 1)
renderer2.AddActor(actor11)
renderer2.AddActor(actor22)
renderer2.AddActor(actor33)
renderer2.AddActor(actor44)
renderer2.AddActor(sphereActor)
renderer2.AddActor(Axes)
renderer2.SetBackground(colors.GetColor3d('skyblue'))

renderWindow = vtk.vtkRenderWindow()
renderWindow.AddRenderer(renderer1)
renderWindow.AddRenderer(renderer2)
renderWindow.SetSize(640, 480)
renderWindow.Render()
renderWindow.SetWindowName('PointCloud')

renderWindowInteractor = vtk.vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.Initialize()
renderWindowInteractor.Start()

五、向量点乘

计算公式:

向量点乘有两个计算公式:

公式1 :[a_{1},a_{2},...,a_{n}]\cdot [b_{1},b_{2},...,b_{n}]=a_{1}b_{1}+a_{2}b_{2},...,a_{n}b_{n}

公式2:a\cdot b = ||a||||b||cos\theta

几何意义:

由上述两个公式可以计算出两条向量之间的夹角:

\theta =arccos(\frac{a\cdot b}{||a||||b||})

可视化:

代码实现:

a1 = np.array([1, 0, 0]) # 向量a1
a2 = np.array([0, 1, 0]) # 向量a2
# 分别计算两个向量的模:
l_a1=np.sqrt(a1.dot(a1))
l_a2=np.sqrt(a2.dot(a2))

# 计算两个向量的点积
d = a1.dot(a2)
print(d)
radian = np.arccos(d/(l_a1*l_a2))
angle = radian*180/np.pi

print("向量a1和向量a2之间的夹角为:", angle, "°")

  

六、向量叉乘

几何意义:

a,b两个向量的叉乘得到一个新的向量c,则向量c与向量a、b构成的平面垂直

可视化:

代码实现:

import vtk
import math
import numpy as np


def arrowCreate(target, origin, length):
    math = vtk.vtkMath()
    math.Subtract(target, origin, target)  # 计算向量的朝向 target=target-origin

    points = vtk.vtkPoints()  # 记录起点坐标
    points.InsertNextPoint(origin)

    vertex = vtk.vtkVertex()  # 建立起点的拓扑(不建立拓扑的话是不行的)
    vertex.GetPointIds().SetNumberOfIds(points.GetNumberOfPoints())
    for i in range(points.GetNumberOfPoints()):
        vertex.GetPointIds().SetId(i, i)  # setId(拓扑的id, 顶点的id)

    normals = vtk.vtkDoubleArray()  # 创建法向量属性,存入向量的朝向target
    normals.SetNumberOfComponents(3)
    normals.InsertNextTuple(target)

    scalars = vtk.vtkDoubleArray()  # 创建标量属性,存入向量的长度length
    scalars.SetNumberOfComponents(1)
    scalars.SetName("scalars")
    scalars.InsertNextTuple1(length)

    vertices = vtk.vtkCellArray()  # 将建立的拓扑用vtkCellArray封装,用于赋予vtkPolyData
    vertices.InsertNextCell(vertex)

    polydata = vtk.vtkPolyData()  # 创建几何实体
    polydata.SetPoints(points)  # 赋予起点
    polydata.SetVerts(vertices)  # 赋予拓扑
    polydata.GetPointData().SetNormals(normals)  # 赋予向量朝向
    polydata.GetPointData().SetScalars(scalars)  # 赋予向量长度

    arrow = vtk.vtkArrowSource()
    arrow.Update()
    glyph = vtk.vtkGlyph3D()
    glyph.SetInputData(polydata)
    glyph.SetSourceData(arrow.GetOutput())
    glyph.SetScaleFactor(0.1)
    glyph.SetVectorModeToUseNormal()
    glyph.Update()

    return glyph


# 画箭头表示向量
a1 = np.array([1, 0, 0]) # 向量a1
a2 = np.array([0, 1, 0]) # 向量a2
# 分别计算两个向量的模:
l_a1 = np.sqrt(a1.dot(a1))
l_a2 = np.sqrt(a2.dot(a2))
# 计算两个向量的叉积
crossd = np.cross(a1, a2)
l_crs = np.sqrt(crossd.dot(crossd))

# 画箭头表示向量
glyph1 = arrowCreate(a1, [0, 0, 0], l_a1) # 原始向量对应的箭头
glyph2 = arrowCreate(a2, [0, 0, 0], l_a2) # 原始向量对应的箭头
glyph3 = arrowCreate(crossd, [0, 0, 0], l_crs) # 原始向量对应的箭头

mapper1 = vtk.vtkPolyDataMapper()
mapper1.SetInputData(glyph1.GetOutput())
actor1 = vtk.vtkActor()
actor1.SetMapper(mapper1)

mapper2 = vtk.vtkPolyDataMapper()
mapper2.SetInputData(glyph2.GetOutput())
actor2 = vtk.vtkActor()
actor2.SetMapper(mapper2)

mapper3 = vtk.vtkPolyDataMapper()
mapper3.SetInputData(glyph3.GetOutput())
actor3 = vtk.vtkActor()
actor3.SetMapper(mapper3)

colors = vtk.vtkNamedColors()


# 可视化
renderer1 = vtk.vtkRenderer()
renderer1.SetViewport(0.0, 0.0, 0.5, 1)
renderer1.AddActor(actor1)
renderer1.AddActor(actor2)
renderer1.SetBackground(colors.GetColor3d('skyblue'))

renderer2 = vtk.vtkRenderer()
renderer2.SetViewport(0.5, 0.0, 1.0, 1)
renderer2.AddActor(actor1)
renderer2.AddActor(actor2)
renderer2.AddActor(actor3)
renderer2.SetBackground(colors.GetColor3d('skyblue'))

renderWindow = vtk.vtkRenderWindow()
renderWindow.AddRenderer(renderer1)
renderWindow.AddRenderer(renderer2)
renderWindow.SetSize(640, 480)
renderWindow.Render()
renderWindow.SetWindowName('PointCloud')

renderWindowInteractor = vtk.vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.Initialize()
renderWindowInteractor.Start()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值