Как осветить и затенить Poly3DCollection - PullRequest
0 голосов
/ 03 июля 2019

Я пытаюсь получить контурный контур 3D для затенения или чтобы тени выглядели как 3D. Я использую matplotlib, в основном из-за высокого качества сюжетов и предпочитаю продолжать его использовать.

В конечном итоге мне бы хотелось, чтобы поверхность была однотонной или плоской, с тенями, отбрасываемыми на графике в стиле matplotlib.

Я использую scipy для выполнения некоторого алгоритма интерполяции и Skimage и марширующего куба для генерации контуров. Затем, наконец, используйте это, чтобы создать и закрасить коллекцию поли.

import numpy as np
from skimage import measure
from scipy.interpolate import griddata
import matplotlib as mpl
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from matplotlib.colors import LightSource

# Generate an grid to inerpolate to
X, Y, Z = np.meshgrid(0.0:1.0:50j, 0.0:1.0:50j, 0.0:1.0:50j)

# Interpolate (coor and phi are the numerical grid and scalar values)
F = griddata(coor, phi, (X, Y, Z), method='nearest')

# Make the contour, marching cubes
marchCubeSpace = 1.0 / 50.0
verts, faces, normals, values = measure.marching_cubes_lewiner(F, 0.5, spacing=(marchCubeSpace, marchCubeSpace, marchCubeSpace))

# Create Ploy3D
mesh = Poly3DCollection(verts[faces], alpha=1.0)

# An attempt to get some sort of height data.
facearray = np.array([np.array((np.sum(verts[face[:], 0]/3), np.sum(verts[face[:], 1]/3), np.sum(verts[face[:], 2]/3))) for face in faces])

# light source, ultimately I want to use not `reds` but just a red for all faces.
ls = LightSource(azdeg=45.0, altdeg=90.0)
rgb = ls.blend_hsv(rgb=ls.shade(facearray, plt.cm.Reds), intensity=ls.shade_normals(normals, fraction=0.25))
mesh.set_facecolor(rgb[:, 0])

# Plot
fig = plt.figure()
ax = fig.add_subplot(0, 0, 0, projection='3d')
ax.add_collection3d(mesh)

Я хочу создать что-то вроде этого: enter image description here

1 Ответ

1 голос
/ 03 июля 2019

Хорошо, поэтому у меня есть приемлемое решение.Напишите мне, если вам нужна дополнительная помощь, я был бы рад помочь кому-нибудь через это.Обратите внимание, что приведенный ниже код требует coor и phi из вашего набора данных, поэтому этот код не будет работать, если вы не предоставите для него скалярное 3D-поле.

import numpy as np
from skimage import measure
from scipy.interpolate import griddata
import matplotlib as mpl
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from matplotlib.colors import LightSource

# Generate an grid to inerpolate to
X, Y, Z = np.meshgrid(0.0:1.0:50j, 0.0:1.0:50j, 0.0:1.0:50j)

# Interpolate (coor and phi are the numerical grid and scalar values)
F = griddata(coor, phi, (X, Y, Z), method='nearest')

# Make the contour, marching cubes
marchCubeSpace = 1.0 / 50.0
verts, faces, normals, values = measure.marching_cubes_lewiner(F, 0.5, spacing=(marchCubeSpace, marchCubeSpace, marchCubeSpace))

# Create Ploy3D and set up a light source
mesh = Poly3DCollection(verts[faces], alpha=1.0)
ls = LightSource(azdeg=225.0, altdeg=45.0)

# First change - normals are per vertex, so I made it per face.
normalsarray = np.array([np.array((np.sum(normals[face[:], 0]/3), np.sum(normals[face[:], 1]/3), np.sum(normals[face[:], 2]/3))/np.sqrt(np.sum(normals[face[:], 0]/3)**2 + np.sum(normals[face[:], 1]/3)**2 + np.sum(normals[face[:], 2]/3)**2)) for face in faces])

# Next this is more asthetic, but it prevents the shadows of the image being too dark. (linear interpolation to correct)
min = np.min(ls.shade_normals(normalsarray, fraction=1.0)) # min shade value
max = np.max(ls.shade_normals(normalsarray, fraction=1.0)) # max shade value
diff = max-min
newMin = 0.3
newMax = 0.95
newdiff = newMax-newMin

# Using a constant color, put in desired RGB values here.
colourRGB = np.array((255.0/255.0, 54.0/255.0, 57/255.0, 1.0))

# The correct shading for shadows are now applied. Use the face normals and light orientation to generate a shading value and apply to the RGB colors for each face.
rgbNew = np.array([colourRGB*(newMin + newdiff*((shade-min)/diff)) for shade in ls = LightSource(azdeg=45.0, altdeg=90.0)

# Apply color to face
mesh.set_facecolor(rgb[:, 0])

# Plot
fig = plt.figure()
ax = fig.add_subplot(0, 0, 0, projection='3d')
ax.add_collection3d(mesh)

Так вот, что яискал.(Обратите внимание, что это не совсем тот же случай, что и на рисунке выше) enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...