可视化两个球体的合并

huangapple go评论95阅读模式
英文:

Visualizing the coalescence of 2 spheres

问题

我正在尝试通过Matplotlib可视化两个球体的合并。

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. fig = plt.figure()
  4. ax = fig.add_subplot(projection='3d')
  5. r = 10
  6. Init = [0,0,0] ; Final = [5,5,5]
  7. u, v = np.mgrid[0:2 * np.pi:30j, 0:np.pi:20j]
  8. x = Init[0] + r*np.cos(u) * np.sin(v)
  9. y = Init[1] + r*np.sin(u) * np.sin(v)
  10. z = Init[2] + r*np.cos(v)
  11. ax.plot_surface(x, y, z, color='r', alpha=0.5, linewidth=0)
  12. u, v = np.mgrid[0:2 * np.pi:30j, 0:np.pi:20j]
  13. x = Final[0] + r*np.cos(u) * np.sin(v)
  14. y = Final[1] + r*np.sin(u) * np.sin(v)
  15. z = Final[2] + r*np.cos(v)
  16. ax.plot_surface(x, y, z, color='r', alpha=0.5, linewidth=0)
  17. plt.show()

我得到了两个球体,它们像预期的那样紧密排列。但是,我想删除两个球体之间的重叠部分,以便我得到一个仅由两个球体不重叠部分组成的表面。

Matplotlib中是否有任何功能可以绘制这个?

英文:

I am trying to visualize the coalescence of 2 spheres through Matplotlib.

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. fig = plt.figure()
  4. ax = fig.add_subplot(projection='3d')
  5. r = 10
  6. Init = [0,0,0] ; Final = [5,5,5]
  7. u, v = np.mgrid[0:2 * np.pi:30j, 0:np.pi:20j]
  8. x = Init[0] + r*np.cos(u) * np.sin(v)
  9. y = Init[1] + r*np.sin(u) * np.sin(v)
  10. z = Init[2] + r*np.cos(v)
  11. ax.plot_surface(x, y, z, color = 'r', alpha = 0.5, linewidth=0)
  12. u, v = np.mgrid[0:2 * np.pi:30j, 0:np.pi:20j]
  13. x = Final[0] + r*np.cos(u) * np.sin(v)
  14. y = Final[1] + r*np.sin(u) * np.sin(v)
  15. z = Final[2] + r*np.cos(v)
  16. ax.plot_surface(x, y, z, color = 'r', alpha = 0.5, linewidth=0)
  17. plt.show()

I am getting 2 spheres closely situated as expected. However, I would like to delete the overlapping (shared) portion between the two spheres, so I get a surface composed of just the non-overlapping parts of the spheres.

Is there any feature in Matplotlib that can plot this?

答案1

得分: 0

这是该过程的步骤:

  • 球体位于空间中。设 d 为连接它们中心的段 C1C2 的距离。
  • 让我们考虑一个参考坐标系位于第一个球的中心,正 x 轴沿着连接它们中心的段的方向。现在,第一个球的中心位于原点,第二个球的中心位于 [d, 0, 0]
  • 计算两个球之间的交点圆
  • 选择参数化方式,使得球体与它们的“极点”相交于 x 轴。这样可以轻松在新的参考坐标系中绘制直到交点圆的球冠。
  • 计算段 C1C2 在原始坐标系中与新参考坐标系的 x 轴的方向。实际上,你需要两次旋转来将 C1C2 对准新坐标系的 x 轴。
  • 最后,将球体的点旋转回它们的原始方向。这是通过旋转矩阵来实现的。
  1. fig = plt.figure()
  2. ax = fig.add_subplot(projection='3d')
  3. # 球体的中心
  4. c1 = np.array([-1, -2, -3])
  5. c2 = np.array([1, 2, 3])
  6. # 绘制中心和连接它们的段
  7. centers = np.stack([c1, c2])
  8. ax.scatter(centers[:, 0], centers[:, 1], centers[:, 2])
  9. ax.plot(centers[:, 0], centers[:, 1], centers[:, 2])
  10. r1 = 5
  11. r2 = 3
  12. # 两个球的交点是一个圆
  13. # 计算这个圆的半径。
  14. # https://mathworld.wolfram.com/Sphere-SphereIntersection.html
  15. # 中心之间的距离
  16. d = np.linalg.norm(c1 - c2)
  17. # 圆的半径
  18. a = np.sqrt((-d + r2 - r1) * (-d - r2 + r1) * (-d + r2 + r1) * (d + r2 + r1)) / (2 * d)
  19. # 我们想要绘制被截断在交点圆上的球冠。
  20. # 这些是参数化的极限角度
  21. alpha = np.arcsin(a / r2)
  22. beta = np.arcsin(a / r1)
  23. # 参数化,使得“极点”相交于 x 轴
  24. u, v = np.mgrid[0:2 * np.pi:30j, beta:np.pi:20j]
  25. x1 = r1 * np.cos(v)
  26. y1 = r1 * np.cos(u) * np.sin(v)
  27. z1 = r1 * np.sin(u) * np.sin(v)
  28. u, v = np.mgrid[0:2 * np.pi:30j, 0:np.pi-alpha:20j]
  29. x2 = d + r2 * np.cos(v)
  30. y2 = r2 * np.cos(u) * np.sin(v)
  31. z2 = r2 * np.sin(u) * np.sin(v)
  32. # 让我们计算两个旋转角度,将连接中心的段对准 x 轴
  33. c2new = c2 - c1
  34. theta = np.arctan2(c2new[1], c2new[0])
  35. phi = np.arctan2(c2new[2], np.sqrt(c2new[0]**2 + c2new[1]**2))
  36. # 旋转矩阵。4x4 因为它们更容易应用于坐标矩阵
  37. Ry = lambda t: np.array([
  38. [np.cos(t), 0, np.sin(t), 0],
  39. [0, 1, 0, 0],
  40. [-np.sin(t), 0, np.cos(t), 0],
  41. [0, 0, 0, 1]
  42. ])
  43. Rz = lambda t: np.array([
  44. [np.cos(t), np.sin(t), 0, 0],
  45. [-np.sin(t), np.cos(t), 0, 0],
  46. [0, 0, 1, 0],
  47. [0, 0, 0, 1]
  48. ])
  49. # 首先,围绕 z 轴旋转 theta,然后围绕 y 轴旋转 phi
  50. rot = Ry(phi) @ Rz(theta)
  51. def rotate_points(x, y, z):
  52. shape = x.shape
  53. # 添加一列1,以便我们可以将结果为3xN的数组与旋转矩阵相乘
  54. coord = np.stack([t.flatten() for t in [x, y, z, np.ones_like(x)]])
  55. coord = rot.T @ coord
  56. x, y, z, _ = [t.reshape(shape) for t in coord]
  57. return x, y, z
  58. x1, y1, z1 = rotate_points(x1, y1, z1)
  59. x2, y2, z2 = rotate_points(x2, y2, z2)
  60. # 对球体应用必要的偏移并绘制它们
  61. ax.plot_surface(x1 + c1[0], y1 + c1[1], z1 + c1[2], color = 'r', alpha = 0.5, linewidth=0)
  62. ax.plot_surface(x2 + c1[0], y2 + c1[1], z2 + c1[2], color = 'g', alpha = 0.5, linewidth=0)
  63. ax.set_aspect("equal")
  64. ax.set_xlabel("x")
  65. ax.set_ylabel("y")
  66. ax.view_init(30, -20)
  67. plt.show()
英文:

This is the procedure:

  • the spheres are located in space. Let d be the distance of the segment C1C2 connecting their centers.
  • let's consider a reference frame located at the center of the first sphere, with the positive x-axis directed along the segment connecting their centers. Now, the center of the first sphere is located at the origin, the center of the second sphere is located at [d, 0, 0].
  • Compute the intersection circle between the two spheres.
  • chose a parameterization such that the sphere is plotted with their "poles" intersecting the x-axis. This make it easy to draw a sphere cap up to the intersection circle (in the new reference frame).
  • Compute the orientation of the segment C1C2 in the original frame with the x-axis of the new reference frame. Essentially, you need two rotations to align C1C2 to the x-axis of the new frame.
  • Finally, rotate the points of the spheres back to their original orientation. This is done with rotation matrices.
  1. fig = plt.figure()
  2. ax = fig.add_subplot(projection='3d')
  3. # centers of the spheres
  4. c1 = np.array([-1, -2, -3])
  5. c2 = np.array([1, 2, 3])
  6. # draw centers and a segment connecting them
  7. centers = np.stack([c1, c2])
  8. ax.scatter(centers[:, 0], centers[:, 1], centers[:, 2])
  9. ax.plot(centers[:, 0], centers[:, 1], centers[:, 2])
  10. r1 = 5
  11. r2 = 3
  12. # intersection of two sphere is a circle
  13. # compute the radius of this circle.
  14. # https://mathworld.wolfram.com/Sphere-SphereIntersection.html
  15. # distance between centers
  16. d = np.linalg.norm(c1 - c2)
  17. # radius of the circle
  18. a = np.sqrt((-d + r2 - r1) * (-d - r2 + r1) * (-d + r2 + r1) * (d + r2 + r1)) / (2 * d)
  19. # we want to draw sphere caps that are cut at the intersecting circle.
  20. # these are the limiting angles for the parameterization
  21. alpha = np.arcsin(a / r2)
  22. beta = np.arcsin(a / r1)
  23. # parameterization such that the "poles" intersect the x-axis
  24. u, v = np.mgrid[0:2 * np.pi:30j, beta:np.pi:20j]
  25. x1 = r1 * np.cos(v)
  26. y1 = r1 * np.cos(u) * np.sin(v)
  27. z1 = r1 * np.sin(u) * np.sin(v)
  28. u, v = np.mgrid[0:2 * np.pi:30j, 0:np.pi-alpha:20j]
  29. x2 = d + r2 * np.cos(v)
  30. y2 = r2 * np.cos(u) * np.sin(v)
  31. z2 = r2 * np.sin(u) * np.sin(v)
  32. # let's compute the two rotation angles that aligns the
  33. # segment connecting the centers to the x-axis
  34. c2new = c2 - c1
  35. theta = np.arctan2(c2new[1], c2new[0])
  36. phi = np.arctan2(c2new[2], np.sqrt(c2new[0]**2 + c2new[1]**2))
  37. # rotations matrices. 4x4 because they are easier to apply
  38. # to a matrix of coordinates
  39. Ry = lambda t: np.array([
  40. [np.cos(t), 0, np.sin(t), 0],
  41. [0, 1, 0, 0],
  42. [-np.sin(t), 0, np.cos(t), 0],
  43. [0, 0, 0, 1]
  44. ])
  45. Rz = lambda t: np.array([
  46. [np.cos(t), np.sin(t), 0, 0],
  47. [-np.sin(t), np.cos(t), 0, 0],
  48. [0, 0, 1, 0],
  49. [0, 0, 0, 1]
  50. ])
  51. # first, rotate by theta around the z-axis,
  52. # then rotate by phi around y-axis
  53. rot = Ry(phi) @ Rz(theta)
  54. def rotate_points(x, y, z):
  55. shape = x.shape
  56. # add a column of 1s so that we can multiply the
  57. # resulting 3xN array with the rotation matrix
  58. coord = np.stack([t.flatten() for t in [x, y, z, np.ones_like(x)]])
  59. coord = rot.T @ coord
  60. x, y, z, _ = [t.reshape(shape) for t in coord]
  61. return x, y, z
  62. x1, y1, z1 = rotate_points(x1, y1, z1)
  63. x2, y2, z2 = rotate_points(x2, y2, z2)
  64. # apply the necessary offset to the spheres and plot them
  65. ax.plot_surface(x1 + c1[0], y1 + c1[1], z1 + c1[2], color = 'r', alpha = 0.5, linewidth=0)
  66. ax.plot_surface(x2 + c1[0], y2 + c1[1], z2 + c1[2], color = 'g', alpha = 0.5, linewidth=0)
  67. ax.set_aspect("equal")
  68. ax.set_xlabel("x")
  69. ax.set_ylabel("y")
  70. ax.view_init(30, -20)
  71. plt.show()

可视化两个球体的合并

huangapple
  • 本文由 发表于 2023年6月8日 05:14:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76427158.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定