I'd like to plot implicit equation F(x,y,z) = 0 in 3D. Is it possible in Matplotlib?
-
3You can find some examples at: http://matplotlib.sourceforge.net/examples/mplot3d/index.html – rubik Jan 13 '11 at 13:31
-
3Do you need to do it with matplotlib? If not, you might want to have a look at [3d contour plots in Mayavi](http://code.enthought.com/projects/mayavi/docs/development/html/mayavi/auto/mlab_helper_functions.html#enthought.mayavi.mlab.contour3d). – Sven Marnach Jan 13 '11 at 16:26
-
@Sven Marnach: Thank You, but unfortunately I have to do it with Matplotlib. – qutron Jan 14 '11 at 09:19
-
It looks good except you aren't shifting the location of the z contour by the value of z. They all get plotted at 0.0! Plus you need to manually define the plotted limits since the z contour solution will extend way beyond your desired contour interval. – Paul Jan 17 '11 at 16:46
-
I've updated my post with cleaner code that includes plotted contour intervals (slices) along other axes. – Paul Jan 17 '11 at 16:48
-
When I cleaned up my code, I decided to rename X, Y, Z, x, y, and z to make it very clear what is going on. This may have caused more confusion than it solved! Look closely at your `for` loops (mainly the second two) and make sure they match my example. You need to either swap all your X, Y, Z, x, y, and z's like I did, or you need to delete the lines that look like: `X,Z= A1, A2` – Paul Jan 19 '11 at 13:44
8 Answers
You can trick matplotlib into plotting implicit equations in 3D. Just make a one-level contour plot of the equation for each z value within the desired limits. You can repeat the process along the y and z axes as well for a more solid-looking shape.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
def plot_implicit(fn, bbox=(-2.5,2.5)):
''' create a plot of an implicit function
fn ...implicit function (plot where fn==0)
bbox ..the x,y,and z limits of plotted interval'''
xmin, xmax, ymin, ymax, zmin, zmax = bbox*3
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
A = np.linspace(xmin, xmax, 100) # resolution of the contour
B = np.linspace(xmin, xmax, 15) # number of slices
A1,A2 = np.meshgrid(A,A) # grid on which the contour is plotted
for z in B: # plot contours in the XY plane
X,Y = A1,A2
Z = fn(X,Y,z)
cset = ax.contour(X, Y, Z+z, [z], zdir='z')
# [z] defines the only level to plot for this contour for this value of z
for y in B: # plot contours in the XZ plane
X,Z = A1,A2
Y = fn(X,y,Z)
cset = ax.contour(X, Y+y, Z, [y], zdir='y')
for x in B: # plot contours in the YZ plane
Y,Z = A1,A2
X = fn(x,Y,Z)
cset = ax.contour(X+x, Y, Z, [x], zdir='x')
# must set plot limits because the contour will likely extend
# way beyond the displayed level. Otherwise matplotlib extends the plot limits
# to encompass all values in the contour.
ax.set_zlim3d(zmin,zmax)
ax.set_xlim3d(xmin,xmax)
ax.set_ylim3d(ymin,ymax)
plt.show()
Here's the plot of the Goursat Tangle:
def goursat_tangle(x,y,z):
a,b,c = 0.0,-5.0,11.8
return x**4+y**4+z**4+a*(x**2+y**2+z**2)**2+b*(x**2+y**2+z**2)+c
plot_implicit(goursat_tangle)
You can make it easier to visualize by adding depth cues with creative colormapping:
Here's how the OP's plot looks:
def hyp_part1(x,y,z):
return -(x**2) - (y**2) + (z**2) - 1
plot_implicit(hyp_part1, bbox=(-100.,100.))
Bonus: You can use python to functionally combine these implicit functions:
def sphere(x,y,z):
return x**2 + y**2 + z**2 - 2.0**2
def translate(fn,x,y,z):
return lambda a,b,c: fn(x-a,y-b,z-c)
def union(*fns):
return lambda x,y,z: np.min(
[fn(x,y,z) for fn in fns], 0)
def intersect(*fns):
return lambda x,y,z: np.max(
[fn(x,y,z) for fn in fns], 0)
def subtract(fn1, fn2):
return intersect(fn1, lambda *args:-fn2(*args))
plot_implicit(union(sphere,translate(sphere, 1.,1.,1.)), (-2.,3.))

- 42,322
- 15
- 106
- 123
-
@bpowah: According to matplotlib reference projection = '3d' is invalid. Perhaps projection = 'rectilinear'? – qutron Jan 14 '11 at 09:26
-
I remember getting that error before upgrading to matplotlib 1.0.1. I forget how to get around it for previous versions. – Paul Jan 14 '11 at 12:54
-
@bpowah: Oh, you're using newer matplotlib version. Anyway I'll try to figure this out. Thanks)) – qutron Jan 14 '11 at 13:12
-
@bpowah: I'm trying to get into your code, but I don't understand how Z = sphere(X,Y,z) works. – qutron Jan 14 '11 at 14:50
-
-
@qutron: sphere(X,Y,z) is being given a 2D array of X's and a 2D array of Y's and a single value for z. It returns a 2D array representing a slice of the surface at z. It's then up to `contour` to find the where in that array the values are equal to `radius` – Paul Jan 14 '11 at 15:08
-
Depending on the version of mplot3d there can appear some difficulties with your code. There are inconsistences in the repo with *args between the 2d and 3d contour plotting functions. Updating to the latest version of mplot3d and change the calls to ax.contour(X, Y, Z, levels=[y], zdir='y') will hopefully help. – atomocopter Mar 11 '11 at 01:07
-
1
-
-
@Paul, I like the way you provided surface operations, very pythonic. – jlandercy Dec 26 '18 at 12:22
Update: I finally have found an easy way to render 3D implicit surface with matplotlib
and scikit-image
, see my other answer. I left this one for whom is interested in plotting parametric 3D surfaces.
Motivation
Late answer, I just needed to do the same and I found another way to do it at some extent. So I am sharing this another perspective.
This post does not answer: (1) How to plot any implicit function F(x,y,z)=0
? But does answer: (2) How to plot parametric surfaces (not all implicit functions, but some of them) using mesh with matplotlib
?
@Paul's method has the advantage to be non parametric, therefore we can plot almost anything we want using contour method on each axe, it fully addresses (1). But matplotlib
cannot easily build a mesh from this method, so we cannot directly get a surface from it, instead we get plane curves in all directions. This is what motivated my answer, I wanted to address (2).
Rendering mesh
If we are able to parametrize (this may be hard or impossible), with at most 2 parameters, the surface we want to plot then we can plot it with matplotlib.plot_trisurf
method.
That is, from an implicit equation F(x,y,z)=0
, if we are able to get a parametric system S={x=f(u,v), y=g(u,v), z=h(u,v)}
then we can plot it easily with matplotlib
without having to resort to contour
.
Then, rendering such a 3D surface boils down to:
# Render:
ax = plt.axes(projection='3d')
ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap='jet', antialiased=True)
Where (x, y, z)
are vectors (not meshgrid
, see ravel
) functionally computed from parameters (u, v)
and triangles
parameter is a Triangulation derived from (u,v)
parameters to shoulder the mesh construction.
Imports
Required imports are:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from matplotlib.tri import Triangulation
Some surfaces
Lets parametrize some surfaces...
Sphere# Parameters:
theta = np.linspace(0, 2*np.pi, 20)
phi = np.linspace(0, np.pi, 20)
theta, phi = np.meshgrid(theta, phi)
rho = 1
# Parametrization:
x = np.ravel(rho*np.cos(theta)*np.sin(phi))
y = np.ravel(rho*np.sin(theta)*np.sin(phi))
z = np.ravel(rho*np.cos(phi))
# Triangulation:
tri = Triangulation(np.ravel(theta), np.ravel(phi))
Cone
theta = np.linspace(0, 2*np.pi, 20)
rho = np.linspace(-2, 2, 20)
theta, rho = np.meshgrid(theta, rho)
x = np.ravel(rho*np.cos(theta))
y = np.ravel(rho*np.sin(theta))
z = np.ravel(rho)
tri = Triangulation(np.ravel(theta), np.ravel(rho))
Torus
a, c = 1, 4
u = np.linspace(0, 2*np.pi, 20)
v = u.copy()
u, v = np.meshgrid(u, v)
x = np.ravel((c + a*np.cos(v))*np.cos(u))
y = np.ravel((c + a*np.cos(v))*np.sin(u))
z = np.ravel(a*np.sin(v))
tri = Triangulation(np.ravel(u), np.ravel(v))
Möbius Strip
u = np.linspace(0, 2*np.pi, 20)
v = np.linspace(-1, 1, 20)
u, v = np.meshgrid(u, v)
x = np.ravel((2 + (v/2)*np.cos(u/2))*np.cos(u))
y = np.ravel((2 + (v/2)*np.cos(u/2))*np.sin(u))
z = np.ravel(v/2*np.sin(u/2))
tri = Triangulation(np.ravel(u), np.ravel(v))
Limitation
Most of the time, Triangulation
is required in order to coordinate mesh construction of plot_trisurf
method, and this object only accepts two parameters, so we are limited to 2D parametric surfaces. It is unlikely we could represent the Goursat Tangle with this method.

- 7,183
- 1
- 39
- 57
-
@Paul, I have found a way to represent 2d parametric 3d functions as surface using `tri_surf` and `Triangulation`, do you know it? I am wondering if there is a way to build a valid mesh from your contours. – jlandercy Dec 26 '18 at 12:24
Actually there is an easy way to plot implicit 3D surface with the scikit-image
package. The key is the marching_cubes
method.
import numpy as np
from skimage import measure
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
Then we compute the function over a 3D meshgrid, in this example we use the goursat_tangle
method @Paul
defined in its answer:
xl = np.linspace(-3, 3, 50)
X, Y, Z = np.meshgrid(xl, xl, xl)
F = goursat_tangle(X, Y, Z)
The magic is happening here with marching_cubes
:
verts, faces, normals, values = measure.marching_cubes(F, 0, spacing=[np.diff(xl)[0]]*3)
verts -= 3
We just need to correct vertices coordinates as they are expressed in Voxel coordinates (hence scaling using spacing
switch and the subsequent origin shift).
Finally it is just about rendering the iso-surface using tri_surface
:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(verts[:, 0], verts[:, 1], faces, verts[:, 2], cmap='jet', lw=0)
Which returns:

- 7,183
- 1
- 39
- 57
-
@qutron and @Paul, I found an easy way to plot implicit 3D surface with `matplotlib` and `scikit-image`. Cheers – jlandercy Sep 07 '20 at 07:36
-
Had to add `plot.show()` to make it work. I'd also suggest you add the Goursat Tangle or some other function for completeness and consider adding it all together in one code block for easy copy-pasting. – achille Dec 27 '20 at 12:46
-
Great. I think x-axis and y-axis are interchanged. For a non-symmetric function `ax.plot_trisurf(verts[:, 1], verts[:, 0], faces, verts[:, 2], cmap='jet', lw=0)` suits my expectations. – JulianKarlBauer Mar 09 '21 at 10:10
Matplotlib expects a series of points; it will do the plotting if you can figure out how to render your equation.
Referring to Is it possible to plot implicit equations using Matplotlib? Mike Graham's answer suggests using scipy.optimize to numerically explore the implicit function.
There is an interesting gallery at http://xrt.wikidot.com/gallery:implicit showing a variety of raytraced implicit functions - if your equation matches one of these, it might give you a better idea what you are looking at.
Failing that, if you care to share the actual equation, maybe someone can suggest an easier approach.

- 1
- 1

- 55,315
- 8
- 84
- 99
As far as I know, it is not possible. You have to solve this equation numerically by yourself. Using scipy.optimize is a good idea. The simplest case is that you know the range of the surface that you want to plot, and just make a regular grid in x and y, and try to solve equation F(xi,yi,z)=0 for z, giving a starting point of z. Following is a very dirty code that might help you
from scipy import *
from scipy import optimize
xrange = (0,1)
yrange = (0,1)
density = 100
startz = 1
def F(x,y,z):
return x**2+y**2+z**2-10
x = linspace(xrange[0],xrange[1],density)
y = linspace(yrange[0],yrange[1],density)
points = []
for xi in x:
for yi in y:
g = lambda z:F(xi,yi,z)
res = optimize.fsolve(g, startz, full_output=1)
if res[2] == 1:
zi = res[0]
points.append([xi,yi,zi])
points = array(points)

- 41
- 3
-
I don't think that's a good idea. Try F(x, y, z) = x^2 + y^2 + z^2 - 1 to see the problem. It should plot a sphere, but your code will only plot half a shpere. – Sven Marnach Jan 13 '11 at 16:19
-
Oh, just noticed you are using basically the same function already :) – Sven Marnach Jan 13 '11 at 16:21
-
That's true. If implicit function is multi-valued, there is a problem. As I said, it's just a dirty code to make the simplest thing possible.# – hanselda Jan 13 '11 at 20:06
Have you looked at mplot3d on matplotlib?

- 7,452
- 5
- 42
- 48
-
Yes, I have. The main problem is that function is implicit. AFAIK, Matplotlib doesn't plot equations, it plots series of points. I just don't know how to calculate all series of points that fit my implicit equation. – qutron Jan 13 '11 at 13:41
Finally, I did it (I updated my matplotlib to 1.0.1). Here is code:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
def hyp_part1(x,y,z):
return -(x**2) - (y**2) + (z**2) - 1
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_range = np.arange(-100,100,10)
y_range = np.arange(-100,100,10)
X,Y = np.meshgrid(x_range,y_range)
A = np.linspace(-100, 100, 15)
A1,A2 = np.meshgrid(A,A)
for z in A:
X,Y = A1, A2
Z = hyp_part1(X,Y,z)
ax.contour(X, Y, Z+z, [z], zdir='z')
for y in A:
X,Z= A1, A2
Y = hyp_part1(X,y,Z)
ax.contour(X, Y+y, Z, [y], zdir='y')
for x in A:
Y,Z = A1, A2
X = hyp_part1(x,Y,Z)
ax.contour(X+x, Y, Z, [x], zdir='x')
ax.set_zlim3d(-100,100)
ax.set_xlim3d(-100,100)
ax.set_ylim3d(-100,100)
Here is result:
Thank You, Paul!