You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python Plotly可靠构造平面?含轴垂直平面解决方案

问题描述

尝试用Plotly绘制x=y平面,通过平面内一点和法向量生成了点集,但Mesh3d无法正常渲染,推测是点的顺序导致三角面构建失败,不知道如何设置i/j/k参数,需要适用于任意法向量(包括垂直于坐标轴的平面)的通用方案,同时欢迎Mesh3d的替代方案。

原代码:

import numpy as np
import plotly.graph_objects as go

p0 = np.array([0,0,0])
p1 = np.array([0,0,1])
p2 = np.array([1,1,0])

v1 = np.array([0,0,1])
v2 = np.array([1,1,0])/np.sqrt(2)

n1, n2 = v1.dot(p1-p0), v2.dot(p2-p0)
a1, a2 = np.mgrid[0:n1:10*1j,0:n2:10*1j]
a1 = a1.flatten()[np.newaxis,:]
a2 = a2.flatten()[np.newaxis,:]
pts = p0[:,np.newaxis] + a1*v1[:,np.newaxis] + a2*v2[:,np.newaxis]

go.Figure(data=[go.Mesh3d(
    x=pts[0],
    y=pts[1],
    z=pts[2]
)])
解决方案

方案1:修复Mesh3d的三角面索引

Mesh3d需要明确的三角面索引(i/j/k)才能正确渲染。仅传入扁平化的点数组时,Plotly无法推断网格拓扑结构,可基于mgrid生成的二维网格行列数自动构建索引:

import numpy as np
import plotly.graph_objects as go

p0 = np.array([0,0,0])
v1 = np.array([0,0,1])
v2 = np.array([1,1,0])/np.sqrt(2)

# 生成二维网格(保留二维结构,不立即扁平化)
a1, a2 = np.mgrid[0:1:10*1j, 0:np.sqrt(2):10*1j]
# 计算平面坐标(二维数组)
x = p0[0] + a1 * v1[0] + a2 * v2[0]
y = p0[1] + a1 * v1[1] + a2 * v2[1]
z = p0[2] + a1 * v1[2] + a2 * v2[2]

# 扁平化点数组
x_flat = x.flatten()
y_flat = y.flatten()
z_flat = z.flatten()

# 获取网格行列数
rows, cols = x.shape

# 生成三角面索引:将每个四边形拆分为两个三角形
i, j = np.meshgrid(np.arange(rows-1), np.arange(cols-1), indexing='ij')
i1, j1 = i.flatten(), j.flatten()
i2, j2 = i1 + 1, j1
i3, j3 = i1, j1 + 1
i4, j4 = i1 + 1, j1 + 1

tri1 = i1 * cols + j1
tri2 = i2 * cols + j2
tri3 = i3 * cols + j3
tri4 = i4 * cols + j4

# 合并两个三角形的索引
i_indices = np.concatenate([tri1, tri1])
j_indices = np.concatenate([tri2, tri3])
k_indices = np.concatenate([tri3, tri4])

# 渲染Mesh3d
fig = go.Figure(data=[go.Mesh3d(
    x=x_flat,
    y=y_flat,
    z=z_flat,
    i=i_indices,
    j=j_indices,
    k=k_indices,
    opacity=0.5
)])
fig.show()

方案2:用Surface简化平面绘制

如果不需要Mesh3d的复杂特性,go.Surface是更优选择——它专门处理二维网格生成的曲面,无需手动构建三角面索引,代码更简洁:

import numpy as np
import plotly.graph_objects as go

p0 = np.array([0,0,0])
v1 = np.array([0,0,1])
v2 = np.array([1,1,0])/np.sqrt(2)

# 生成二维网格
a1, a2 = np.mgrid[0:1:10*1j, 0:np.sqrt(2):10*1j]
# 计算平面坐标(保留二维数组结构)
x = p0[0] + a1 * v1[0] + a2 * v2[0]
y = p0[1] + a1 * v1[1] + a2 * v2[1]
z = p0[2] + a1 * v1[2] + a2 * v2[2]

# 渲染Surface
fig = go.Figure(data=[go.Surface(
    x=x,
    y=y,
    z=z,
    opacity=0.5
)])
fig.show()

方案3:通用平面生成函数(支持任意法向量)

以下函数可根据平面法向量和平面内一点,生成Plotly可渲染的平面数据,自动处理垂直于坐标轴的特殊情况(如x=5、z=2或x=y这类平面):

import numpy as np
import plotly.graph_objects as go

def create_plane(normal, point, size=5, num_points=10):
    """
    生成Plotly可用的平面数据
    参数:
        normal: 平面法向量,如np.array([1,-1,0])对应x=y平面
        point: 平面内任意一点,如np.array([0,0,0])
        size: 平面的边长范围(从-size到size)
        num_points: 网格的点数(每个维度)
    返回:
        x, y, z: 二维数组,可直接用于go.Surface
    """
    normal = normal / np.linalg.norm(normal)  # 归一化法向量
    
    # 生成平面内的两个正交方向向量
    if np.allclose(normal, np.array([1,0,0])):
        # 垂直于x轴的平面
        v1 = np.array([0,1,0])
        v2 = np.array([0,0,1])
    elif np.allclose(normal, np.array([0,1,0])):
        # 垂直于y轴的平面
        v1 = np.array([1,0,0])
        v2 = np.array([0,0,1])
    elif np.allclose(normal, np.array([0,0,1])):
        # 垂直于z轴的平面
        v1 = np.array([1,0,0])
        v2 = np.array([0,1,0])
    else:
        # 通用情况:生成两个与法向量正交的向量
        v1 = np.cross(normal, np.array([1,0,0]))
        if np.linalg.norm(v1) < 1e-6:
            v1 = np.cross(normal, np.array([0,1,0]))
        v1 = v1 / np.linalg.norm(v1)
        v2 = np.cross(normal, v1)
        v2 = v2 / np.linalg.norm(v2)
    
    # 生成网格参数
    u = np.linspace(-size, size, num_points)
    v = np.linspace(-size, size, num_points)
    u_grid, v_grid = np.meshgrid(u, v)
    
    # 计算平面坐标
    x = point[0] + u_grid * v1[0] + v_grid * v2[0]
    y = point[1] + u_grid * v1[1] + v_grid * v2[1]
    z = point[2] + u_grid * v1[2] + v_grid * v2[2]
    
    return x, y, z

# 示例1:绘制x=y平面(法向量[1,-1,0],过原点)
x, y, z = create_plane(np.array([1,-1,0]), np.array([0,0,0]))
fig = go.Figure(data=[go.Surface(x=x, y=y, z=z, opacity=0.5)])
fig.show()

# 示例2:绘制垂直于z轴的平面z=2
x, y, z = create_plane(np.array([0,0,1]), np.array([0,0,2]))
fig = go.Figure(data=[go.Surface(x=x, y=y, z=z, opacity=0.5)])
fig.show()

说明

  • Surface比Mesh3d更适合绘制平面这类规则曲面,无需手动处理三角面索引,可靠性更高。
  • 通用函数自动处理垂直于坐标轴的特殊场景,避免计算异常,同时支持任意法向量的平面生成。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 05:09:54