-
-
Save franktoffel/f79d84319f043c1d3c897f3732489460 to your computer and use it in GitHub Desktop.
| ''' | |
| ================================= | |
| 3D heart shape in matplotlib | |
| ================================= | |
| Demonstrates how to plot a 3D function in cartesian coordinates. | |
| Uses the marching cubes algorithm in scikit-image to obtain a isosurface. | |
| Example contributed by CAChemE.org | |
| Adapted from: http://www.walkingrandomly.com/?p=2326 | |
| ''' | |
| from mpl_toolkits.mplot3d import Axes3D | |
| from matplotlib import pyplot as plt | |
| import numpy as np | |
| from skimage import measure | |
| # Set up mesh | |
| n = 100 | |
| x = np.linspace(-3,3,n) | |
| y = np.linspace(-3,3,n) | |
| z = np.linspace(-3,3,n) | |
| X, Y, Z = np.meshgrid(x, y, z) | |
| # Create cardioid function | |
| def f_heart(x,y,z): | |
| F = 320 * ((-x**2 * z**3 -9*y**2 * z**3/80) + | |
| (x**2 + 9*y**2/4 + z**2-1)**3) | |
| return F | |
| # Obtain value to at every point in mesh | |
| vol = f_heart(X,Y,Z) | |
| # Extract a 2D surface mesh from a 3D volume (F=0) | |
| verts, faces, normals, values = measure.marching_cubes_lewiner(vol, 0, spacing=(0.1, 0.1, 0.1)) | |
| # Create a 3D figure | |
| fig = plt.figure(figsize=(12,8)) | |
| ax = fig.add_subplot(111, projection='3d') | |
| # Plot the surface | |
| ax.plot_trisurf(verts[:, 0], verts[:,1], faces, verts[:, 2], | |
| cmap='Spectral', lw=1) | |
| # Change the angle of view and title | |
| ax.view_init(15, -15) | |
| # ax.set_title(u"Made with ❤ (and Python)", fontsize=15) # if you have Python 3 | |
| ax.set_title("Made with <3 (and Python)", fontsize=15) | |
| # Show me some love ^^ | |
| plt.show() |
Make sure you have matplotlib (and all the libraries installed) installed.
Try the importing lines separately in your IDLE Shell.
from matplotlib import pyplot as plt
import numpy as np
from skimage import measure
Thank you so much for putting this together! It looks amazing!!! I did, however, notice one thing when running the code. It seems that skimage might have undergone some updates, so now the code in line 35 errors. This is the error message I received:
module 'skimage.measure' has no attribute 'marching_cubes'
I was able to resolve the error by changing measure.marching_cubes to measure.marching_cubes_lewiner, according siva's and Lucan's answer here: https://stackoverflow.com/questions/54056565/attributeerror-module-skimage-measure-has-no-attribute-marching-cubes
After this modification, the code ran successfully, and the heart rendered beautifully. Thank you so much again!
I think it would be beneficial to include the requirements.txt file. This is how mine looks:
contourpy==1.0.7
cycler==0.11.0
fonttools==4.39.3
imageio==2.27.0
kiwisolver==1.4.4
lazy_loader==0.2
matplotlib==3.7.1
networkx==3.1
numpy==1.24.2
packaging==23.1
Pillow==9.5.0
pyparsing==3.0.9
PyQt6==6.11.0
PyQt6-Qt6==6.11.0
PyQt6_sip==13.11.1
python-dateutil==2.8.2
PyWavelets==1.4.1
scikit-image==0.20.0
scipy==1.10.1
six==1.16.0
tifffile==2023.4.12Also, I had to change Line 35 of python_heart.py from this:
verts, faces, normals, values = measure.marching_cubes_lewiner(vol, 0, spacing=(0.1, 0.1, 0.1))to this:
verts, faces, normals, values = measure.marching_cubes(vol, 0, spacing=(0.1, 0.1, 0.1))Here's what the Python code looks like now:
'''
=================================
3D heart shape in matplotlib
=================================
Demonstrates how to plot a 3D function in cartesian coordinates.
Uses the marching cubes algorithm in scikit-image to obtain a isosurface.
Example contributed by CAChemE.org
Adapted from: http://www.walkingrandomly.com/?p=2326
'''
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
import numpy as np
from skimage import measure
# Set up mesh
n = 100
x = np.linspace(-3,3,n)
y = np.linspace(-3,3,n)
z = np.linspace(-3,3,n)
X, Y, Z = np.meshgrid(x, y, z)
# Create cardioid function
def f_heart(x,y,z):
F = 320 * ((-x**2 * z**3 -9*y**2 * z**3/80) +
(x**2 + 9*y**2/4 + z**2-1)**3)
return F
# Obtain value to at every point in mesh
vol = f_heart(X,Y,Z)
# Extract a 2D surface mesh from a 3D volume (F=0)
verts, faces, normals, values = measure.marching_cubes(vol, 0, spacing=(0.1, 0.1, 0.1))
# Create a 3D figure
fig = plt.figure(figsize=(12,8))
ax = fig.add_subplot(111, projection='3d')
# Plot the surface
ax.plot_trisurf(verts[:, 0], verts[:,1], faces, verts[:, 2],
cmap='Spectral', lw=1)
# Change the angle of view and title
ax.view_init(15, -15)
# ax.set_title(u"Made with ❤ (and Python)", fontsize=15) # if you have Python 3
ax.set_title("Made with <3 (and Python)", fontsize=15)
# Show me some love ^^
plt.show()
that code didn't work I've attached a photo to prove it.