Python:使用 scipy.spatial.transform.Rotation 旋转平面(点集)以匹配新的法向量
Python: Rotate plane (set of points) to match new normal vector using scipy.spatial.transform.Rotation
所以我目前正在尝试在与样条正交的平面上进行切片。
方向并不重要,因为我使用这些点来插入 3D 扫描
我主要不确定 rotmat 方法(这是我的 class 的精简版本,技术上是 NURBS-Python 曲面派生的 class),其中我' m 从平面 x/y 平面(所有 z=0)旋转平面网格以匹配新的法向量(样条的切线,存储在 der 变量中)。
有人知道如何旋转一组点以从一个法向量转到另一个法向量吗?新矢量绕轴的角度对我来说并不重要。
(抱歉 vg,有点晦涩的库,但实际上有点方便):
from scipy.interpolate import splprep, splev
import numpy as np
import vg
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.spatial.transform import Rotation as R
class SplineTube():
_points = np.array(
[[0, 0, 0],
[0, 1, 0],
[1, 1, 0],
[1, 0, 0]],
) - np.array([0.5, 0.5, 0])
_normal = np.array([0, 0, 1])
def __init__(self, x, y, z, n = 3, degree=2, **kwargs):
assert n >= 3
tck, u = splprep([x, y, z], s=0, k=2)
evalpts = np.linspace(0, 1, n)
pts = np.array(splev(evalpts, tck))
der = np.array(splev(evalpts, tck, der=1))
points = []
for i in range(n):
points_slice = self.rotmat(der[:, i], self._points)
points_slice = points_slice + pts[:, i]
points.append(points_slice)
points = np.stack(points)
return points
def rotmat(self, vector, points):
perpen = vg.perpendicular(self._normal, vector)
r = R.from_rotvec(perpen)
rotmat = r.apply(points)
return rotmat
这是一个示例,其中我使用了 meshgrid 而不是 _points,但非常相似:
Planes following spline
x = [0, 1, 2, 3, 6]
y = [0, 2, 5, 6, 2]
z = [0, 3, 5, 7, 10]
tck, u = splprep([x, y, z], s=0, k=2)
evalpts = np.linspace(0, 1, 10)
pts = splev(evalpts, tck)
der = splev(evalpts, tck, der=1)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(pts[0], pts[1], pts[2])
ax.quiver(*pts, *der, length=0.05)
ax.scatter(x, y, z)
planes = SplineTube(x, y, z, n=10)
ax.scatter(planes[:, :, 0], planes[:, :, 1], planes[:, :, 2])
我想我最终制作了一些似乎最终有用的东西:
import numpy as np
import vg
from pytransform3d.rotations import matrix_from_axis_angle
def _rotmat(self, vector, points):
"""
Rotates a 3xn array of 3D coordinates from the +z normal to an
arbitrary new normal vector.
"""
vector = vg.normalize(vector)
axis = vg.perpendicular(vg.basis.z, vector)
angle = vg.angle(vg.basis.z, vector, units='rad')
a = np.hstack((axis, (angle,)))
R = matrix_from_axis_angle(a)
r = Rot.from_matrix(R)
rotmat = r.apply(points)
return rotmat
并不太复杂,只需从与 x-y 平面对齐的点平面开始(假设您显然像我一样使用 x-y 作为水平线,请不要讨厌me), 然后它会沿着向量旋转它而不是真正关心绕轴的旋转。似乎工作正常。
所以我目前正在尝试在与样条正交的平面上进行切片。 方向并不重要,因为我使用这些点来插入 3D 扫描
我主要不确定 rotmat 方法(这是我的 class 的精简版本,技术上是 NURBS-Python 曲面派生的 class),其中我' m 从平面 x/y 平面(所有 z=0)旋转平面网格以匹配新的法向量(样条的切线,存储在 der 变量中)。
有人知道如何旋转一组点以从一个法向量转到另一个法向量吗?新矢量绕轴的角度对我来说并不重要。
(抱歉 vg,有点晦涩的库,但实际上有点方便):
from scipy.interpolate import splprep, splev
import numpy as np
import vg
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.spatial.transform import Rotation as R
class SplineTube():
_points = np.array(
[[0, 0, 0],
[0, 1, 0],
[1, 1, 0],
[1, 0, 0]],
) - np.array([0.5, 0.5, 0])
_normal = np.array([0, 0, 1])
def __init__(self, x, y, z, n = 3, degree=2, **kwargs):
assert n >= 3
tck, u = splprep([x, y, z], s=0, k=2)
evalpts = np.linspace(0, 1, n)
pts = np.array(splev(evalpts, tck))
der = np.array(splev(evalpts, tck, der=1))
points = []
for i in range(n):
points_slice = self.rotmat(der[:, i], self._points)
points_slice = points_slice + pts[:, i]
points.append(points_slice)
points = np.stack(points)
return points
def rotmat(self, vector, points):
perpen = vg.perpendicular(self._normal, vector)
r = R.from_rotvec(perpen)
rotmat = r.apply(points)
return rotmat
这是一个示例,其中我使用了 meshgrid 而不是 _points,但非常相似:
Planes following spline
x = [0, 1, 2, 3, 6]
y = [0, 2, 5, 6, 2]
z = [0, 3, 5, 7, 10]
tck, u = splprep([x, y, z], s=0, k=2)
evalpts = np.linspace(0, 1, 10)
pts = splev(evalpts, tck)
der = splev(evalpts, tck, der=1)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(pts[0], pts[1], pts[2])
ax.quiver(*pts, *der, length=0.05)
ax.scatter(x, y, z)
planes = SplineTube(x, y, z, n=10)
ax.scatter(planes[:, :, 0], planes[:, :, 1], planes[:, :, 2])
我想我最终制作了一些似乎最终有用的东西:
import numpy as np
import vg
from pytransform3d.rotations import matrix_from_axis_angle
def _rotmat(self, vector, points):
"""
Rotates a 3xn array of 3D coordinates from the +z normal to an
arbitrary new normal vector.
"""
vector = vg.normalize(vector)
axis = vg.perpendicular(vg.basis.z, vector)
angle = vg.angle(vg.basis.z, vector, units='rad')
a = np.hstack((axis, (angle,)))
R = matrix_from_axis_angle(a)
r = Rot.from_matrix(R)
rotmat = r.apply(points)
return rotmat
并不太复杂,只需从与 x-y 平面对齐的点平面开始(假设您显然像我一样使用 x-y 作为水平线,请不要讨厌me), 然后它会沿着向量旋转它而不是真正关心绕轴的旋转。似乎工作正常。