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), 然后它会沿着向量旋转它而不是真正关心绕轴的旋转。似乎工作正常。