从两个物体创建螺旋。

6
我有一个平面和一个正弦曲线,如何旋转这两个对象?我的意思是在区间-0.1到0.4缓慢倾斜平面,以便在点0.4处垂直于z轴。经过更长时间的旋转,平面和正弦的最大值和最小值将构成“从点[0,-0.1,0]到点[0,0.4,0]的轴的圆柱体表面”。我希望我的意思清楚明白。请注意保留HTML标签。
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D                         
from mpl_toolkits.mplot3d import proj3d                         
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

plane1 = -0.1
plane2 = 0.4
h = 0.03

# Plane
xp = np.array([[0, 0], [0, 0]])
yp = np.array([[plane1, plane2], [plane1, plane2]])
zp = np.array([[-h, -h], [h, h]])

ax.plot_surface(xp, yp, zp, alpha=0.4, color = 'red')

# Sine
f = 100
amp = h
y = np.linspace(plane1, plane2, 5000)
z = amp*np.sin(y*f)
ax.plot(y, z, zdir='x')

plt.show()

enter image description here

期望的结果是一个平面,填充以下形状的正弦曲线: enter image description here 但不需要旋转太多,整个区间旋转30度即可。

如果我想象用红色背景在一张纸上打印正弦波,并且我扭曲了其中一端,那么图像就会在三维空间中扭曲。这是你要寻找的效果吗? - Multihunter
2个回答

2
回答标题问题,要创建一个螺旋,你需要寻找一个简单的三维函数:链接1
amp, f = 1, 1
low, high = 0, math.pi*20
n = 1000

y = np.linspace(low, high, n)
x = amp*np.cos(f*y)
z = amp*np.sin(f*y)

ax.plot(x,y,z)

这意味着:

A helix

找到这个函数的方法之一是思考:从每个方向看起来像什么?在 y/z 平面上制作一个二维图表,你会看到一个余弦图表,在 y/x 平面上制作一个二维图表,你会看到一个正弦图表。在 x/z 平面上制作一个二维图表,你会看到一个圆形。这告诉你关于这个三维函数的一切!
如果您真的想在三维空间中旋转正弦波形的图像,那就会变得复杂。此外,它不会创建像您期望的螺旋线一样的形状,因为您试图创建的“圆柱”的半径将随着不同的y值而改变。但是,既然您要求如何进行旋转...
您需要使用 仿射变换 矩阵。单个旋转可以表示为一个4x4矩阵,您可以将其与一个齐次坐标相乘以找到结果点。(请参见链接以获取这方面的说明)
rotate_about_y = np.array([
    [cos(theta), 0, sin(theta), 0],
    [0, 1, , 0],
    [-sin(theta), 0, cos(theta), 0],
    [0, 0, 0, 1],
])

然后,要将此应用于整个点列表,您可以这样做:
# Just using a mathematical function to generate the points for illustation
low, high = 0, math.pi*16
n = 1000

y = np.linspace(low, high, n)
x = np.zeros_like(y)
z = np.cos(y)
xyz = np.stack([x, y, z], axis=1) # shaped [n, 3]


min_rotation, max_rotation = 0, math.pi*16
homogeneous_points = np.concatenate([xyz, np.full([n, 1], 1)], axis=1) # shaped [n, 4]
rotation_angles = np.linspace(min_rotation, max_rotation, n)
rotate_about_y = np.zeros([n, 4, 4])
rotate_about_y[:, 0, 0] = np.cos(rotation_angles)
rotate_about_y[:, 0, 2] = np.sin(rotation_angles)
rotate_about_y[:, 1, 1] = 1
rotate_about_y[:, 2, 0] = -np.sin(rotation_angles)
rotate_about_y[:, 2, 2] = np.cos(rotation_angles)
rotate_about_y[:, 3, 3] = 1

# This broadcasts matrix multiplication over the whole homogeneous_points array
rotated_points = (homogeneous_points[:, None] @ rotate_about_y).squeeze(1)
# Remove tacked on 1
new_points = rotated_points[:, :3]

ax.plot(x, y, z)
ax.plot(new_points[:, 0], new_points[:, 1], new_points[:, 2])

对于这种情况(其中low == min_rotationhigh == max_rotation),您会得到一个螺旋状的结构,但是,正如我所说,它被我们围绕y轴旋转并且函数趋近于y = 0的事实所扭曲。

Rotated points

注意:在numpy中,符号@表示“矩阵乘法”。 “齐次点”只是在末尾添加1的点;我不会深入探讨为什么它们能起作用,但它们确实能。
注意2:上面的代码假定您想围绕y轴旋转(即围绕x = 0,z = 0的线旋转)。如果要围绕其他线旋转,则需要组合变换。我不会在这里过多地介绍,但大致过程如下:
  1. 将要旋转的线映射到y轴上,以便进行变换
  2. 执行上述变换
  3. 对此列表中的第一个变换执行反变换

您可以通过将每个变换与另一个变换进行矩阵乘法来组合它们。

这里有一份我找到的文档,它解释了仿射变换矩阵的工作原理和原因。但是,在该主题上有大量信息可供参考。


1
几乎所有的事情都是可能的,但难度不同。我有点难以理解你想要实现什么。通常在提问时最好谈论一下最终目标。你想在三维空间中产生一个二维平面吗?你想要一组点来制作DNA在三维空间中的图像(双螺旋带梯子)吗?还是其他什么?所需旋转角度无关紧要;你仍然需要编写代码,使其适用于所有旋转。 - Multihunter
1
你是在谈论信封吗?https://zh.wikipedia.org/wiki/%E4%BF%A1%E5%B0%81_(%E6%B3%A2) - Multihunter
1
我真正回答了你的问题吗? - Multihunter
1
我明白了。你的回答给了我灵感。谢谢。 - Elena Greg
概念的精彩解析,加上一个吸引人的解决方案。干得好! - S3DEV
显示剩余2条评论

2

建议后的解决方案:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D                         # 3d graph
from mpl_toolkits.mplot3d import proj3d                         # 3d graph
import math as m

# Matrix for rotation
def Ry(theta):
  return np.matrix([[ m.cos(theta), 0, m.sin(theta)],
                   [ 0           , 1, 0           ],
                   [-m.sin(theta), 0, m.cos(theta)]])

# Plot figure
figsize=[6,5]
fig = plt.figure(figsize=figsize)
ax = fig.add_subplot(111, projection='3d')
ax.azim = -39   # y rotation (default=270)
ax.elev = 15    # x rotation (default=0)

plane1 = -0.1
plane2 = 0.4
h = 0.03

N = 1000
r = h
t = np.linspace(plane1, plane2, N)
theta = 0.5 * np.pi * t

y = t
x = r * np.sin(theta)
z = r * np.cos(theta)

for i in range(1, N):
    xs = [[x[i], -x[i]], [x[i-1], -x[i-1]]]
    ys = [[y[i],  y[i]], [y[i-1],  y[i-1]]]
    zs = [[z[i], -z[i]], [z[i-1], -z[i-1]]]
    xs, ys, zs = np.array(xs), np.array(ys), np.array(zs)
    ax.plot_surface(xs, ys, zs, alpha=0.4, color = 'green')

# Sine function
f = 100
amp = h
function = amp*np.sin(y*f)
x2 = function * np.sin(theta)
z2 = function * np.cos(theta)
ax.plot(x2, y, z2)

ax.plot([0, 0], [plane1, plane1], [-0.05, 0.05])

plt.show()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接