研究3D图形学处理,很多地方需要用到的线性代数的知识,将线性代数结合3D可视化编程,能够加深对线性代数应用场景的理解。
一、负向量
计算公式:
几何意义:
一个与原向量长度相同,方向相反的向量,如下图所示,向量-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()
二、向量大小(长度、模)
计算公式:
几何意义:
求向量的长度,向量的模是个标量
代码实现:
a1 = np.array([2, 2, 1])
length1 = math.sqrt(a1[0]**2 + a1[1]**2 + a1[2]**2) # 向量的模长
print("三维向量[2, 2, 1]的模为:", length1)
三、标量与向量的乘法
计算公式:
几何意义:
通过与标量相乘,可以改变向量的长度,如下图所示,向量通过与乘以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()
四、单位向量标准化
计算公式:
几何意义:
使向量变成模等于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 :
公式2:
几何意义:
由上述两个公式可以计算出两条向量之间的夹角:
可视化:
代码实现:
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()