在3d中绘制隐式方程

时间:2011-01-13 13:25:11

标签: python numpy matplotlib plot

我想在3D中绘制隐式方程F(x,y,z)= 0。在Matplotlib中有可能吗?

8 个答案:

答案 0 :(得分:48)

您可以使用matplotlib绘制3D中的隐式方程。只需在所需限制内为每个z值制作等式的一级等高线图。您可以沿y轴和z轴重复该过程,以获得更加坚固的形状。

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()

以下是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)

alt text

通过使用创意色彩映射添加深度提示,您可以更轻松地进行可视化:

alt text

OP的情节如下:

def hyp_part1(x,y,z):
    return -(x**2) - (y**2) + (z**2) - 1

plot_implicit(hyp_part1, bbox=(-100.,100.))

alt text

Bonus:您可以使用python在功能上组合这些隐式函数:

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.))

alt text

答案 1 :(得分:2)

Matplotlib期待一系列观点;如果你能弄清楚如何渲染你的等式,它将进行绘图。

参考Is it possible to plot implicit equations using Matplotlib? Mike Graham的回答建议使用scipy.optimize以数字方式探索隐式函数。

http://xrt.wikidot.com/gallery:implicit有一个有趣的画廊,展示了各种光线跟踪隐式函数 - 如果你的等式匹配其中一个,它可能会让你更好地了解你在看什么。

如果不这样做,如果你想分享实际的等式,也许有人可以提出一个更简单的方法。

答案 2 :(得分:2)

据我所知,这是不可能的。你必须自己用数字方法解决这个等式。使用scipy.optimize是个好主意。最简单的情况是你知道要绘制的曲面范围,并在x和y中制作一个规则网格,并尝试求解z的方程F(xi,yi,z)= 0,给出一个起点z点。以下是一个非常脏的代码,可能会帮助您

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)

答案 3 :(得分:1)

你看过matplotlib上的mplot3d吗?

答案 4 :(得分:1)

动机

最新答案,我只需要做同样的事情,在某种程度上我发现了另一种方法。所以我要分享另一个观点。

本文不回答:(1)如何绘制任何隐式函数F(x,y,z)=0?但是答案是否正确:(2)如何使用带有matplotlib的网格来绘制参数化曲面(不是所有隐式函数,而是其中一些)?

@Paul的方法具有非参数化的优点,因此我们可以在每个轴上绘制使用轮廓法绘制的几乎任何物体,它可以完全解决(1)的问题。但是matplotlib无法轻松地通过此方法构建网格,因此我们无法直接从中获取曲面,而是获取所有方向的平面曲线。这就是促使我回答这个问题的原因,我想解决(2)。

渲染网格

如果我们能够参数化(这可能是困难的或不可能的),最多可以绘制两个要绘制的参数,然后可以使用matplotlib.plot_trisurf方法对其进行绘制。

也就是说,根据隐式方程F(x,y,z)=0,如果我们能够获得参数系统S={x=f(u,v), y=g(u,v), z=h(u,v)},则可以使用matplotlib轻松绘制它,而不必求助于{{1 }}。

然后,渲染这样的3D表面归结为:

contour

其中# Render: ax = plt.axes(projection='3d') ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap='jet', antialiased=True) 是向量(不是meshgrid,请参见ravel),是根据参数(x, y, z)计算得出的向量,而(u, v)参数是从{{1 }}参数来承担网格的构建。

进口

所需的进口是:

triangles

某些表面

让某些表面参数化...

(u,v)

enter image description here

锥体
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from matplotlib.tri import Triangulation

enter image description here

圆环面
# 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))

enter image description here

Möbius Strip
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))

enter image description here

限制

大多数时候,Triangulation是必需的,以便协调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)) 方法的网格结构,并且该对象仅接受两个参数,因此我们仅限于2D参数化曲面。我们不可能用这种方法来表示古尔萨特缠结。

答案 5 :(得分:0)

最后,我做到了(我将matplotlib更新为1.0.1)。 这是代码:

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)

结果如下:alt text

谢谢你,保罗!

答案 6 :(得分:0)

MathGL(GPL绘图库)可以轻松绘制它。只需创建一个具有函数值f [i,j,k]的数据网格,并使用Surf3()函数在值f [i,j,k] = 0处生成等值面。请参阅此sample

答案 7 :(得分:0)

实际上,有一种简便的方法可以使用scikit-image包绘制隐式3D表面。关键是marching_cubes方法。

import numpy as np
from skimage import measure
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d

然后我们在3D网格上计算函数,在此示例中,我们使用其答案中定义的goursat_tangle方法@Paul

xl = np.linspace(-3, 3, 50)
X, Y, Z = np.meshgrid(xl, xl, xl)
F = goursat_tangle(X, Y, Z)

神奇的事发生在marching_cubes

verts, faces, normals, values = measure.marching_cubes(F, 0, spacing=[np.diff(xl)[0]]*3)
verts -= 3

我们只需要校正以Voxel坐标表示的顶点坐标(因此使用spacing开关进行缩放并随后进行原点平移)。

最后,它只是使用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)

哪个返回:

enter image description here