按距原点的距离着色点

Color point by distance from origin

这是在 Python 中使用 PyVista 创建的 Hopf 环面:

import numpy as np
import pyvista as pv

A = 0.44
n = 3
def Gamma(t):
    alpha = np.pi/2 - (np.pi/2-A)*np.cos(n*t)
    beta = t + A*np.sin(2*n*t)
    return np.array([
      np.sin(alpha) * np.cos(beta),
      np.sin(alpha) * np.sin(beta),
      np.cos(alpha)
    ])

def HopfInverse(p, phi):
    return np.array([
      (1+p[2])*np.cos(phi),
      p[0]*np.sin(phi) - p[1]*np.cos(phi), 
      p[0]*np.cos(phi) + p[1]*np.sin(phi),
      (1+p[2])*np.sin(phi)
    ]) / np.sqrt(2*(1+p[2]))

def Stereo(q):
    return 2*q[0:3] / (1-q[3])

def F(t, phi):
    return Stereo(HopfInverse(Gamma(t), phi))

angle = np.linspace(0, 2*np.pi, 300)
angle2 = np.linspace(0, np.pi, 150)
theta, phi = np.meshgrid(angle, angle2)
x, y, z = F(theta, phi)

# Display the mesh
grid = pv.StructuredGrid(x, y, z)
grid.plot(smooth_shading=True)

我想为这个表面添加一个调色板。环面以原点 (0,0,0) 为中心。我想要一种颜色与原点距离的函数。

使用 Matplotlib,我做:

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.colors as mcolors
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np

A = 0.44
n = 3

......

colorfunction = (X**2+Y**2+Z**2)
norm = mcolors.Normalize(colorfunction.min(),colorfunction.max())

# Display the mesh
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.plot_surface(z, x, y, rstride = 1, cstride = 1, facecolors=cm.jet(norm(colorfunction)))
plt.show()

编辑

我有一个解决方案,但我无法控制颜色:

grid = pv.StructuredGrid(x, y, z)
grid['Data'] = grid.points
grid.plot(smooth_shading=True, scalars="Data")

作为旁注,至少对我来说,自己计算点的大小并将它们设置为标量(而不是依赖矢量数据的大小作为颜色映射的标量,即使这是支持的有效)。

你缺少的只是a choice of colourmap。与 matplotlib 一样,默认值是 viridis。相反,您似乎想要 jet(尽管我不建议这样做;在大多数情况下,对于数据可视化而言,感知统一的颜色映射更可取):

import numpy as np
import pyvista as pv

A = 0.44
n = 3
def Gamma(t):
    alpha = np.pi/2 - (np.pi/2-A)*np.cos(n*t)
    beta = t + A*np.sin(2*n*t)
    return np.array([
      np.sin(alpha) * np.cos(beta),
      np.sin(alpha) * np.sin(beta),
      np.cos(alpha)
    ])

def HopfInverse(p, phi):
    return np.array([
      (1+p[2])*np.cos(phi),
      p[0]*np.sin(phi) - p[1]*np.cos(phi),
      p[0]*np.cos(phi) + p[1]*np.sin(phi),
      (1+p[2])*np.sin(phi)
    ]) / np.sqrt(2*(1+p[2]))

def Stereo(q):
    return 2*q[0:3] / (1-q[3])

def F(t, phi):
    return Stereo(HopfInverse(Gamma(t), phi))

angle = np.linspace(0, 2 * np.pi, 300)
theta, phi = np.meshgrid(angle, angle)
x, y, z = F(theta, phi)
grid = pv.StructuredGrid(x, y, z)

# convert to PolyData and clean to remove the seam
cleaned_poly = grid.extract_geometry().clean(tolerance=1e-6)

# add distance from origin as scalars
cleaned_poly.point_data['distance'] = np.linalg.norm(cleaned_poly.points, axis=1)
# this also makes these the default scalars

cleaned_poly.plot(smooth_shading=True, cmap='jet')  # but don't use jet if possible