Plotly - 如何绘制圆柱体?
Plotly - how to plot Cylinder?
我有一个使用 matplotlib 绘制圆柱体的函数。
我想知道如何使用 plotly 做同样的事情?
下面是我绘制圆柱体的matplotlib函数:
#function to plot the cylinder
def _plotCylinder(self, ax, x, y, z, dx, dy, dz, color='red',mode=2):
""" Auxiliary function to plot a Cylinder """
# plot the two circles above and below the cylinder
p = Circle((x+dx/2,y+dy/2),radius=dx/2,color=color,ec='black')
p2 = Circle((x+dx/2,y+dy/2),radius=dx/2,color=color,ec='black')
ax.add_patch(p)
ax.add_patch(p2)
art3d.pathpatch_2d_to_3d(p, z=z, zdir="z")
art3d.pathpatch_2d_to_3d(p2, z=z+dz, zdir="z")
# plot a circle in the middle of the cylinder
center_z = np.linspace(0, dz, 15)
theta = np.linspace(0, 2*np.pi, 15)
theta_grid, z_grid=np.meshgrid(theta, center_z)
x_grid = dx / 2 * np.cos(theta_grid) + x + dx / 2
y_grid = dy / 2 * np.sin(theta_grid) + y + dy / 2
z_grid = z_grid + z
ax.plot_surface(x_grid, y_grid, z_grid,shade=False,fc=color,ec='black',alpha=1,color=color)
x,y,z = item.position
[w,h,d] = item.getDimension()
# plot item of cylinder
self._plotCylinder(axGlob, float(x), float(y), float(z), float(w),float(h),float(d),color=color,mode=2)
更新 - 绘制立方体类型项目的当前代码
### PLOTLY ###
# https://plotly.com/python/3d-mesh/#mesh-cube
def vertices(xmin=0, ymin=0, zmin=0, xmax=1, ymax=1, zmax=1):
return {
"x": [xmin, xmin, xmax, xmax, xmin, xmin, xmax, xmax],
"y": [ymin, ymax, ymax, ymin, ymin, ymax, ymax, ymin],
"z": [zmin, zmin, zmin, zmin, zmax, zmax, zmax, zmax],
"i": [7, 0, 0, 0, 4, 4, 6, 1, 4, 0, 3, 6],
"j": [3, 4, 1, 2, 5, 6, 5, 2, 0, 1, 6, 3],
"k": [0, 7, 2, 3, 6, 7, 1, 6, 5, 5, 7, 2],
}
def parallelipipedic_frame(xm, xM, ym, yM, zm, zM):
# defines the coords of each segment followed by None, if the line is
# discontinuous
x = [xm, xM, xM, xm, xm, None, xm, xM, xM, xm, xm, None, xm, xm, None, xM, xM,
None, xM, xM, None, xm, xm]
y = [ym, ym, yM, yM, ym, None, ym, ym, yM, yM, ym, None, ym, ym, None, ym, ym,
None, yM, yM, None, yM, yM]
z = [zm, zm, zm, zm, zm, None, zM, zM, zM, zM, zM, None, zm, zM, None, zm, zM,
None, zm, zM, None, zm, zM]
return x, y, z
# take a packer item and build parameters to a plotly mesh3d cube
def packer_to_plotly(item):
colors = ["crimson", "limegreen", "green", "red", "cyan", "magenta", "yellow"]
ret = vertices(
*item.position, *[sum(x) for x in zip(item.position, item.getDimension())]
)
ret["name"] = item.name
ret["color"] = colors[ord(item.name.split("_")[0][-1]) - ord("A")]
return ret
# create a multi-plot figure for each bin
# fig = make_subplots(rows=len(packer.bins), cols=1, specs=[[{"type":"mesh3d"}], [{"type":"mesh3d"}]])
fig = go.Figure()
# add a trace for each packer item
for row, pbin in enumerate(packer.bins):
for item in pbin.items:
fig.add_trace(go.Mesh3d(packer_to_plotly(item)))
# sorting out layout, prmarily aspect ratio
fig.update_layout(
margin={"l": 0, "r": 0, "t": 0, "b": 0},
autosize=False,
scene=dict(
camera=dict(
# eye=dict(x=0.1, y=0.1, z=1.5)
),
aspectratio=dict(x=1, y=.2, z=0.2),
aspectmode="manual",
),
)
# push data into a data frame to enable more types of analysis
df = pd.DataFrame(
[
{
"bin_name": b.partno,
"bin_index": i,
**packer_to_plotly(item),
**{d: v for v, d in zip(item.getDimension(), list("hwl"))},
**{d + d: v for v, d in zip(item.position, list("xyz"))},
}
for i, b in enumerate(packer.bins)
for item in b.items
]
)
# create a figure for each container (bin)
for pbin, d in df.groupby("bin_name"):
fig = go.Figure()
xx = []
yy = []
zz = []
# create a trace for each box (bin)
for _, r in d.iterrows():
fig.add_trace(
go.Mesh3d(r[["x", "y", "z", "i", "j", "k", "name", "color"]].to_dict())
)
xx += [r.xx, r.xx + r.h, r.xx + r.h, r.xx, r.xx, None] * 2 + [r.xx] * 5 + [None]
yy += [r.yy, r.yy, r.yy + r.w, r.yy + r.w, r.yy, None] * 2 + [
r.yy,
r.yy + r.w,
r.yy + r.w,
r.yy,
r.yy,
None,
]
zz += (
[r.zz] * 5
+ [None]
+ [r.zz + r.l] * 5
+ [None]
+ [r.zz, r.zz, r.zz + r.l, r.zz + r.l, r.zz, None]
)
fig.add_trace(
go.Scatter3d(
x=xx,
y=yy,
z=zz,
mode="lines",
line_color="black",
line_width=2,
hoverinfo="skip",
)
)
x, y, z = parallelipipedic_frame(0, 1202.4, 0, 235, 0, 269.7)
fig.add_trace(
go.Scatter3d(
x=x,
y=y,
z=z,
mode="lines",
line_color="blue",
line_width=2,
hoverinfo="skip",
)
)
ar = 4
xr = max(d["x"].max()) - min(d["x"].min())
fig.update_layout(
showlegend=False,
title={"text": pbin, "y": 0.9, "x": 0.5, "xanchor": "center", "yanchor": "top"},
margin={"l": 0, "r": 0, "t": 0, "b": 0},
# autosize=False,
scene=dict(
camera=dict(eye=dict(x=2, y=2, z=2)),
aspectratio={
**{"x": ar},
**{
c: ((max(d[c].max()) - min(d[c].min())) / xr) * ar
for c in list("yz")
},
},
aspectmode="manual",
),
)
fig.show()
我有机会花一些时间解决创建圆柱体网格的问题,因此开始研究三角剖分。
这是结果。主要功能是 cylinder_traces
:它 returns go.Surface
轨迹,以及可选的 go.Scatter3d
代表线框网格的轨迹。
import plotly.graph_objects as go
import numpy as np
def slice_triangles(z, n, i, j, k, l):
"""Create the triangles of a single slice"""
return [[z, j, i], [i, j, l], [l, j, k], [k, n, l]]
def cylinder_mesh(r, xs, ys, zs, h, n_slices=40):
"""Create a cylindrical mesh"""
theta = np.linspace(0, 2 * np.pi, n_slices + 1)
x = xs + r * np.cos(theta)
y = ys + r * np.sin(theta)
z1 = zs + 0 * np.ones_like(x)
z2 = (zs + h) * np.ones_like(x)
# index of the final point in the mesh
n = n_slices * 2 + 1
# build triangulation
triangles = []
for s in range(1, n_slices + 1):
j = (s + 1) if (s <= n_slices - 1) else 1
k = j + n_slices if (s <= n_slices - 1) else n_slices + 1
l = s + n_slices
triangles += slice_triangles(0, n, s, j, k, l)
triangles = np.array(triangles)
# coordinates of the vertices
x_coords = np.hstack([xs, x[:-1], x[:-1], xs])
y_coords = np.hstack([ys, y[:-1], y[:-1], ys])
z_coords = np.hstack([zs, z1[:-1], z2[:-1], (zs + h)])
vertices = np.stack([x_coords, y_coords, z_coords]).T
return vertices, triangles, x, y, z1, z2
def cylinder_traces(r, xs, ys, zs, h, n_slices=40, show_mesh=True, n_sub=4, surface_kw={}, line_kw={}):
"""
r : radius
xs, ys, zs : start position of the cylinder
h : height of the cylinder
n_slices : number of slices in the circumferential direction
show_mesh : wheter to display pseudo-wireframe
n_sub : number of subdivision in along the height for the pseudo-wireframe
surface_kw : customize the appearance of the surface
line_kw : customize the appearance of the wireframe
"""
vertices, triangles, x, y, z1, z2 = cylinder_mesh(r, xs, ys, zs, h, n_slices)
surface = go.Mesh3d(
x=vertices[:, 0], y=vertices[:, 1], z=vertices[:, 2],
i=triangles[:, 0], j=triangles[:, 1], k=triangles[:, 2],
**surface_kw)
traces = [surface]
if not show_mesh:
return traces
line_kw.setdefault("showlegend", False)
# horizontal mesh lines
zsubs = np.linspace(zs, zs + h, n_sub + 1)
for zc in zsubs:
traces.append(go.Scatter3d(x=x, y=y, z=zc*np.ones_like(x), mode="lines", **line_kw))
# vertical mesh lines
for _x, _y in zip(x, y):
traces.append(go.Scatter3d(x=[_x, _x], y=[_y, _y], z=[zs, zs + h], mode="lines", **line_kw))
return traces
fig = go.Figure()
fig.add_traces(
cylinder_traces(2, 5, 3, 1, 4, 20, n_sub=4, line_kw={"line_color":"#202020", "line_width": 3})
)
fig
原始答案:OP 可能与气缸盖有关。所以,我们需要创建:
- 圆柱壁的表面,类似于您已经使用 matplotlib 所做的。
- 底盖表面。
- 顶盖表面。
注意:在下图中,我为气缸壁和气缸盖使用了不同的颜色以更好地形象化它们。
import numpy as np
import plotly.graph_objects as go
def cylinder(x, y, z, r, dz):
"""Create a cylindrical mesh located at x, y, z, with radius r and height dz"""
center_z = np.linspace(0, dz, 15)
theta = np.linspace(0, 2*np.pi, 15)
theta_grid, z_grid = np.meshgrid(theta, center_z)
x_grid = r * np.cos(theta_grid) + x
y_grid = r * np.sin(theta_grid) + y
z_grid = z_grid + z
return x_grid, y_grid, z_grid
def circle(x, y, z, r):
"""Create a circular mesh located at x, y, z with radius r"""
r_discr = np.linspace(0, r, 2)
theta_discr = np.linspace(0, 2*np.pi, 15)
r_grid, theta_grid = np.meshgrid(r_discr, theta_discr)
x_circle = r_grid * np.cos(theta_grid) + x
y_circle = r_grid * np.sin(theta_grid) + y
z_circle = np.zeros_like(x_circle) + z
return x_circle, y_circle, z_circle
# cylinder mesh
x_cyl, y_cyl, z_cyl = cylinder(0, 0, 0, 2, 8)
# bottom cap
x_circle1, y_circle1, z_circle1 = circle(0, 0, 0, 2)
# top cap
x_circle2, y_circle2, z_circle2 = circle(0, 0, 8, 2)
colorscale = [[0, '#636EFA'],
[1, '#636EFA']]
fig = go.Figure([
go.Surface(x=x_cyl, y=y_cyl, z=z_cyl, colorscale=colorscale, showscale=False, opacity=0.5),
go.Surface(x=x_circle1, y=y_circle1, z=z_circle1, showscale=False, opacity=0.5),
go.Surface(x=x_circle2, y=y_circle2, z=z_circle2, showscale=False, opacity=0.5),
])
fig
我有一个使用 matplotlib 绘制圆柱体的函数。
我想知道如何使用 plotly 做同样的事情?
下面是我绘制圆柱体的matplotlib函数:
#function to plot the cylinder
def _plotCylinder(self, ax, x, y, z, dx, dy, dz, color='red',mode=2):
""" Auxiliary function to plot a Cylinder """
# plot the two circles above and below the cylinder
p = Circle((x+dx/2,y+dy/2),radius=dx/2,color=color,ec='black')
p2 = Circle((x+dx/2,y+dy/2),radius=dx/2,color=color,ec='black')
ax.add_patch(p)
ax.add_patch(p2)
art3d.pathpatch_2d_to_3d(p, z=z, zdir="z")
art3d.pathpatch_2d_to_3d(p2, z=z+dz, zdir="z")
# plot a circle in the middle of the cylinder
center_z = np.linspace(0, dz, 15)
theta = np.linspace(0, 2*np.pi, 15)
theta_grid, z_grid=np.meshgrid(theta, center_z)
x_grid = dx / 2 * np.cos(theta_grid) + x + dx / 2
y_grid = dy / 2 * np.sin(theta_grid) + y + dy / 2
z_grid = z_grid + z
ax.plot_surface(x_grid, y_grid, z_grid,shade=False,fc=color,ec='black',alpha=1,color=color)
x,y,z = item.position
[w,h,d] = item.getDimension()
# plot item of cylinder
self._plotCylinder(axGlob, float(x), float(y), float(z), float(w),float(h),float(d),color=color,mode=2)
更新 - 绘制立方体类型项目的当前代码
### PLOTLY ###
# https://plotly.com/python/3d-mesh/#mesh-cube
def vertices(xmin=0, ymin=0, zmin=0, xmax=1, ymax=1, zmax=1):
return {
"x": [xmin, xmin, xmax, xmax, xmin, xmin, xmax, xmax],
"y": [ymin, ymax, ymax, ymin, ymin, ymax, ymax, ymin],
"z": [zmin, zmin, zmin, zmin, zmax, zmax, zmax, zmax],
"i": [7, 0, 0, 0, 4, 4, 6, 1, 4, 0, 3, 6],
"j": [3, 4, 1, 2, 5, 6, 5, 2, 0, 1, 6, 3],
"k": [0, 7, 2, 3, 6, 7, 1, 6, 5, 5, 7, 2],
}
def parallelipipedic_frame(xm, xM, ym, yM, zm, zM):
# defines the coords of each segment followed by None, if the line is
# discontinuous
x = [xm, xM, xM, xm, xm, None, xm, xM, xM, xm, xm, None, xm, xm, None, xM, xM,
None, xM, xM, None, xm, xm]
y = [ym, ym, yM, yM, ym, None, ym, ym, yM, yM, ym, None, ym, ym, None, ym, ym,
None, yM, yM, None, yM, yM]
z = [zm, zm, zm, zm, zm, None, zM, zM, zM, zM, zM, None, zm, zM, None, zm, zM,
None, zm, zM, None, zm, zM]
return x, y, z
# take a packer item and build parameters to a plotly mesh3d cube
def packer_to_plotly(item):
colors = ["crimson", "limegreen", "green", "red", "cyan", "magenta", "yellow"]
ret = vertices(
*item.position, *[sum(x) for x in zip(item.position, item.getDimension())]
)
ret["name"] = item.name
ret["color"] = colors[ord(item.name.split("_")[0][-1]) - ord("A")]
return ret
# create a multi-plot figure for each bin
# fig = make_subplots(rows=len(packer.bins), cols=1, specs=[[{"type":"mesh3d"}], [{"type":"mesh3d"}]])
fig = go.Figure()
# add a trace for each packer item
for row, pbin in enumerate(packer.bins):
for item in pbin.items:
fig.add_trace(go.Mesh3d(packer_to_plotly(item)))
# sorting out layout, prmarily aspect ratio
fig.update_layout(
margin={"l": 0, "r": 0, "t": 0, "b": 0},
autosize=False,
scene=dict(
camera=dict(
# eye=dict(x=0.1, y=0.1, z=1.5)
),
aspectratio=dict(x=1, y=.2, z=0.2),
aspectmode="manual",
),
)
# push data into a data frame to enable more types of analysis
df = pd.DataFrame(
[
{
"bin_name": b.partno,
"bin_index": i,
**packer_to_plotly(item),
**{d: v for v, d in zip(item.getDimension(), list("hwl"))},
**{d + d: v for v, d in zip(item.position, list("xyz"))},
}
for i, b in enumerate(packer.bins)
for item in b.items
]
)
# create a figure for each container (bin)
for pbin, d in df.groupby("bin_name"):
fig = go.Figure()
xx = []
yy = []
zz = []
# create a trace for each box (bin)
for _, r in d.iterrows():
fig.add_trace(
go.Mesh3d(r[["x", "y", "z", "i", "j", "k", "name", "color"]].to_dict())
)
xx += [r.xx, r.xx + r.h, r.xx + r.h, r.xx, r.xx, None] * 2 + [r.xx] * 5 + [None]
yy += [r.yy, r.yy, r.yy + r.w, r.yy + r.w, r.yy, None] * 2 + [
r.yy,
r.yy + r.w,
r.yy + r.w,
r.yy,
r.yy,
None,
]
zz += (
[r.zz] * 5
+ [None]
+ [r.zz + r.l] * 5
+ [None]
+ [r.zz, r.zz, r.zz + r.l, r.zz + r.l, r.zz, None]
)
fig.add_trace(
go.Scatter3d(
x=xx,
y=yy,
z=zz,
mode="lines",
line_color="black",
line_width=2,
hoverinfo="skip",
)
)
x, y, z = parallelipipedic_frame(0, 1202.4, 0, 235, 0, 269.7)
fig.add_trace(
go.Scatter3d(
x=x,
y=y,
z=z,
mode="lines",
line_color="blue",
line_width=2,
hoverinfo="skip",
)
)
ar = 4
xr = max(d["x"].max()) - min(d["x"].min())
fig.update_layout(
showlegend=False,
title={"text": pbin, "y": 0.9, "x": 0.5, "xanchor": "center", "yanchor": "top"},
margin={"l": 0, "r": 0, "t": 0, "b": 0},
# autosize=False,
scene=dict(
camera=dict(eye=dict(x=2, y=2, z=2)),
aspectratio={
**{"x": ar},
**{
c: ((max(d[c].max()) - min(d[c].min())) / xr) * ar
for c in list("yz")
},
},
aspectmode="manual",
),
)
fig.show()
我有机会花一些时间解决创建圆柱体网格的问题,因此开始研究三角剖分。
这是结果。主要功能是 cylinder_traces
:它 returns go.Surface
轨迹,以及可选的 go.Scatter3d
代表线框网格的轨迹。
import plotly.graph_objects as go
import numpy as np
def slice_triangles(z, n, i, j, k, l):
"""Create the triangles of a single slice"""
return [[z, j, i], [i, j, l], [l, j, k], [k, n, l]]
def cylinder_mesh(r, xs, ys, zs, h, n_slices=40):
"""Create a cylindrical mesh"""
theta = np.linspace(0, 2 * np.pi, n_slices + 1)
x = xs + r * np.cos(theta)
y = ys + r * np.sin(theta)
z1 = zs + 0 * np.ones_like(x)
z2 = (zs + h) * np.ones_like(x)
# index of the final point in the mesh
n = n_slices * 2 + 1
# build triangulation
triangles = []
for s in range(1, n_slices + 1):
j = (s + 1) if (s <= n_slices - 1) else 1
k = j + n_slices if (s <= n_slices - 1) else n_slices + 1
l = s + n_slices
triangles += slice_triangles(0, n, s, j, k, l)
triangles = np.array(triangles)
# coordinates of the vertices
x_coords = np.hstack([xs, x[:-1], x[:-1], xs])
y_coords = np.hstack([ys, y[:-1], y[:-1], ys])
z_coords = np.hstack([zs, z1[:-1], z2[:-1], (zs + h)])
vertices = np.stack([x_coords, y_coords, z_coords]).T
return vertices, triangles, x, y, z1, z2
def cylinder_traces(r, xs, ys, zs, h, n_slices=40, show_mesh=True, n_sub=4, surface_kw={}, line_kw={}):
"""
r : radius
xs, ys, zs : start position of the cylinder
h : height of the cylinder
n_slices : number of slices in the circumferential direction
show_mesh : wheter to display pseudo-wireframe
n_sub : number of subdivision in along the height for the pseudo-wireframe
surface_kw : customize the appearance of the surface
line_kw : customize the appearance of the wireframe
"""
vertices, triangles, x, y, z1, z2 = cylinder_mesh(r, xs, ys, zs, h, n_slices)
surface = go.Mesh3d(
x=vertices[:, 0], y=vertices[:, 1], z=vertices[:, 2],
i=triangles[:, 0], j=triangles[:, 1], k=triangles[:, 2],
**surface_kw)
traces = [surface]
if not show_mesh:
return traces
line_kw.setdefault("showlegend", False)
# horizontal mesh lines
zsubs = np.linspace(zs, zs + h, n_sub + 1)
for zc in zsubs:
traces.append(go.Scatter3d(x=x, y=y, z=zc*np.ones_like(x), mode="lines", **line_kw))
# vertical mesh lines
for _x, _y in zip(x, y):
traces.append(go.Scatter3d(x=[_x, _x], y=[_y, _y], z=[zs, zs + h], mode="lines", **line_kw))
return traces
fig = go.Figure()
fig.add_traces(
cylinder_traces(2, 5, 3, 1, 4, 20, n_sub=4, line_kw={"line_color":"#202020", "line_width": 3})
)
fig
原始答案:OP 可能与气缸盖有关。所以,我们需要创建:
- 圆柱壁的表面,类似于您已经使用 matplotlib 所做的。
- 底盖表面。
- 顶盖表面。
注意:在下图中,我为气缸壁和气缸盖使用了不同的颜色以更好地形象化它们。
import numpy as np
import plotly.graph_objects as go
def cylinder(x, y, z, r, dz):
"""Create a cylindrical mesh located at x, y, z, with radius r and height dz"""
center_z = np.linspace(0, dz, 15)
theta = np.linspace(0, 2*np.pi, 15)
theta_grid, z_grid = np.meshgrid(theta, center_z)
x_grid = r * np.cos(theta_grid) + x
y_grid = r * np.sin(theta_grid) + y
z_grid = z_grid + z
return x_grid, y_grid, z_grid
def circle(x, y, z, r):
"""Create a circular mesh located at x, y, z with radius r"""
r_discr = np.linspace(0, r, 2)
theta_discr = np.linspace(0, 2*np.pi, 15)
r_grid, theta_grid = np.meshgrid(r_discr, theta_discr)
x_circle = r_grid * np.cos(theta_grid) + x
y_circle = r_grid * np.sin(theta_grid) + y
z_circle = np.zeros_like(x_circle) + z
return x_circle, y_circle, z_circle
# cylinder mesh
x_cyl, y_cyl, z_cyl = cylinder(0, 0, 0, 2, 8)
# bottom cap
x_circle1, y_circle1, z_circle1 = circle(0, 0, 0, 2)
# top cap
x_circle2, y_circle2, z_circle2 = circle(0, 0, 8, 2)
colorscale = [[0, '#636EFA'],
[1, '#636EFA']]
fig = go.Figure([
go.Surface(x=x_cyl, y=y_cyl, z=z_cyl, colorscale=colorscale, showscale=False, opacity=0.5),
go.Surface(x=x_circle1, y=y_circle1, z=z_circle1, showscale=False, opacity=0.5),
go.Surface(x=x_circle2, y=y_circle2, z=z_circle2, showscale=False, opacity=0.5),
])
fig