plotly3D (3d charts in Python)可以画3维图形
在做圆柱的3D装箱项目,需要装箱的可视化,但是Mesh ( 3d mesh plots in Python)只能画三角形, 所以需要用多个三角形拼成一个圆柱(想做立方体的可视化,可以进入使用plotly dash 画3d立方体_python 3d绘图立方体-CSDN博客):
1 画上下底面的边线:因为只能画直线,所以只能用n边形代替圆(我感觉30-50就足够了)
python
# 找圆柱底边 num_points 个点
def to_cylinder_point(cylinder:Cylinder):
print(cylinder)
# 设置圆形参数
num_points = 30 # 圆形上的点的数量
# 计算圆上的点的坐标
theta = np.linspace(0, 2 * np.pi, num_points)
x = cylinder.coordinate[0] + cylinder.radius * np.cos(theta)
y = cylinder.coordinate[1] + cylinder.radius * np.sin(theta)
z_underside = np.full(num_points, cylinder.coordinate[2])
z_top = np.full(num_points, cylinder.coordinate[2]+cylinder.height)
return x,y,z_underside,z_top
python
# 画出下底边的轮廓
fig.add_scatter3d(x=x, y=y, z=z_bottom, mode="lines", line={'color': 'black', 'width': 2})
2 拼上下底面:以上下底面圆心为中心,上一步计算出来的"圆柱底边 num_points 个点"做边画三角形。
python
# 将圆心插入首位
b_x = np.insert(x,0,p.coordinate[0])
b_y =np.insert(y,0,p.coordinate[1])
b_z = np.insert(z_bottom,0,p.coordinate[2])
t_z = np.insert(z_top, 0, p.coordinate[2] + p.height)
i, j, k = to_planes(len(b_x))
# 画底面
fig.add_mesh3d(x=b_x,y=b_y,z=b_z,i=i,j=j,k=k,color='pink')
# 画顶面
fig.add_mesh3d(x=b_x,y=b_y,z=t_z,i=i,j=j,k=k,color='pink')
3 拼侧面:以上一步计算出来的 上下"圆柱底边 各num_points 个点"做边画三角形。
python
s_x = np.concatenate((x,x))
s_y = np.concatenate((y,y))
s_z = np.concatenate((z_top,z_bottom))
ii, jj, kk = to_side(len(s_z))
# 画侧面
fig.add_mesh3d(x=s_x,y=s_y,z=s_z,i=ii,j=jj,k=kk,color='pink')
所有代码:
python
import plotly.graph_objects as go
import numpy as np
import math
class Cylinder:
def __init__(self, name, diameter,height,num = 0):
self.name = name
self.diameter = int(diameter)
self.radius = int(diameter/2)
self.height = int(height)
self.num = num
self.coordinate = None
def cylinder_copy(cylinder:Cylinder,height):
new_cylinder = Cylinder(cylinder.name,cylinder.diameter,cylinder.height,cylinder.num)
new_cylinder.coordinate = cylinder.coordinate+[height]
return new_cylinder
class Box:
def __init__(self, name, long,wide,height):
self.name = name
self.long = int(long)
self.wide = int(wide)
self.height = int(height)
def toline(cx,cy,cz):
# 通过立方体的8个顶点,画出立方体的轮廓.cx(x轴8个坐标)
x = [cx[0],cx[1],cx[2],cx[3],cx[0],cx[4],cx[5],cx[1],cx[5],cx[6],cx[2],cx[6],cx[7],cx[3],cx[7],cx[4]]
y = [cy[0],cy[1],cy[2],cy[3],cy[0],cy[4],cy[5],cy[1],cy[5],cy[6],cy[2],cy[6],cy[7],cy[3],cy[7],cy[4]]
z = [cz[0],cz[1],cz[2],cz[3],cz[0],cz[4],cz[5],cz[1],cz[5],cz[6],cz[2],cz[6],cz[7],cz[3],cz[7],cz[4]]
return x,y,z
def toxyz(begin,end):
# 通过开始结束位置确定立方体的8个顶点
# 0 1 2 3 4 5 6 7
x = [begin[0],begin[0],end[0],end[0],begin[0],begin[0],end[0],end[0]]
y = [begin[1],end[1],end[1],begin[1],begin[1],end[1],end[1],begin[1]]
z = [begin[2],begin[2],begin[2],begin[2],end[2],end[2],end[2],end[2]]
return x,y,z
# 根据圆的边线,填满圆
def to_planes(n):
ii=[]
jj=[]
kk=[]
for v in range(1,n):
ii.append(0)
jj.append(v)
if v+1>=n:
kk.append(1)
else:
kk.append(v+1)
return ii,jj,kk
# 根据圆上下底面的边线,填满侧边
def to_side(n):
ii=[]
jj=[]
kk=[]
half = int(n/2)
for i in range(half):
ii.append(i)
if i+1>=half:
jj.append(0)
else:
jj.append(i+1)
kk.append(i+half)
if i+1>=half:
ii.append(0)
else:
ii.append(i+1)
jj.append(i+half)
if i+half+1>=n:
kk.append(half)
else:
kk.append(i+half+1)
return ii,jj,kk
def to_cylinder_point(cylinder:Cylinder):
print(cylinder)
# 设置圆形参数
num_points = 30 # 圆形上的点的数量
# 计算圆上的点的坐标
theta = np.linspace(0, 2 * np.pi, num_points)
x = cylinder.coordinate[0] + cylinder.radius * np.cos(theta)
y = cylinder.coordinate[1] + cylinder.radius * np.sin(theta)
z_underside = np.full(num_points, cylinder.coordinate[2]) # 在 z 轴上的坐标都为圆心的 z 坐标
z_top = np.full(num_points, cylinder.coordinate[2]+cylinder.height) # 在 z 轴上的坐标都为圆心的 z 坐标
return x,y,z_underside,z_top
def getfig(box:Box,position):
box_xyz = toxyz([0, 0, 0], [box.long, box.wide, box.height])
box_line = toline(box_xyz[0], box_xyz[1], box_xyz[2])
fig=go.Figure(data=[
go.Scatter3d(
x=box_line[0],
y=box_line[1],
z=box_line[2],
mode='lines',
line={'color': 'black', 'width': 2}
)
])
for p in position:
x, y, z_bottom, z_top = to_cylinder_point(p)
# 画下底面的线
fig.add_scatter3d(x=x, y=y, z=z_bottom, mode="lines", line={'color': 'black', 'width': 2})
# 画顶面的线
fig.add_scatter3d(x=x, y=y, z=z_top, mode="lines", line={'color': 'black', 'width': 2})
b_x = np.insert(x,0,p.coordinate[0])
b_y =np.insert(y,0,p.coordinate[1])
b_z = np.insert(z_bottom,0,p.coordinate[2])
t_z = np.insert(z_top, 0, p.coordinate[2] + p.height)
i, j, k = to_planes(len(b_x))
# 画底面
fig.add_mesh3d(x=b_x,y=b_y,z=b_z,i=i,j=j,k=k,color='pink')
# 画顶面
fig.add_mesh3d(x=b_x,y=b_y,z=t_z,i=i,j=j,k=k,color='pink')
s_x = np.concatenate((x,x))
s_y = np.concatenate((y,y))
s_z = np.concatenate((z_top,z_bottom))
ii, jj, kk = to_side(len(s_z))
# 画侧面
fig.add_mesh3d(x=s_x,y=s_y,z=s_z,i=ii,j=jj,k=kk,color='pink')
fig.update_layout(
clickmode='event+select',
# 设置xyz轴比例原本比例:draw axes in proportion to the proportion of their ranges
scene_aspectmode='data',
scene=dict(xaxis_title='x-长'+str(box.long)+'mm',
yaxis_title='y-宽'+str(box.wide)+'mm',
zaxis_title='z-高'+str(box.height)+'mm',
xaxis=dict(
backgroundcolor="rgb(230, 230,200)",
gridcolor="white",
showbackground=True,
zerolinecolor="black", ),
yaxis=dict(
backgroundcolor="rgb(230, 230,200)",
gridcolor="white",
showbackground=True,
zerolinecolor="black"),
zaxis=dict(
backgroundcolor="rgb(230, 230,200)",
gridcolor="white",
showbackground=True,
zerolinecolor="black", ),
),
height=600,
width=800,
)
return fig
cylinder = Cylinder('a',70,30)
cylinder.coordinate=[50+2*math.sqrt(3),70,10]
box = Box('b',100,200,70)
fig = getfig(box,[cylinder])
fig.show()
代码结果展示:
多个圆柱展示: