基于python与Streamlit构建的交互式的Web应用可以在浏览器里查看和操作不同形状的分子轨道模型。可以在网页上显示3D的轨道图形有sp3、sp2、sp和p几种类型每种都有特定的空间结构。可以通过侧边栏的滑块调整参数比如原子大小、轨道长短还能旋转缩放模型从不同角度观察。还支持导出STL文件一、应用概述基于Web的交互式科学可视化工具专门用于展示和探索量子化学中的杂化轨道三维几何结构。应用通过Streamlit构建结合Plotly实现交互式3D可视化并支持STL模型导出功能。二、技术架构核心库依赖Streamlit: 创建Web应用界面NumPy: 数学计算和数组操作Plotly: 交互式3D可视化numpy-stl: STL模型生成和导出math: 基础数学运算三、核心功能模块解析1. 用户界面系统页面配置宽屏布局侧边栏默认展开st.set_page_config(page_title3D分子轨道可视化工具,layoutwide,initial_sidebar_stateexpanded)提供沉浸式的3D可视化体验控制面板与主视图分离的设计状态管理ifparamsnotinst.session_state:st.session_state.params{...}使用Streamlit的session_state持久化参数确保参数在用户交互过程中保持状态2. 轨道几何计算引擎方向向量计算(get_orbital_directions)sp³杂化4个轨道指向正四面体的四个顶点方向[1,1,1]/√3,[1,-1,-1]/√3,[-1,1,-1]/√3,[-1,-1,1]/√3键角109.5°标准的四面体角sp²杂化3个轨道在平面内互成120°方向[1,0,0],[-0.5, √3/2, 0],[-0.5, -√3/2, 0]平面三角形结构sp杂化2个轨道沿z轴反向方向[0,0,1],[0,0,-1]线性结构键角180°p轨道标准的哑铃形轨道方向[1,0,0],[-1,0,0]网格生成算法球体网格生成(create_sphere_mesh)采用经纬度划分法生成球面网格参数化方程x r·sin(φ)·cos(θ) y r·sin(φ)·sin(θ) z r·cos(φ)生成三角面片用于3D渲染轨道网格生成(create_orbital_mesh)这是应用的核心算法模拟杂化轨道的大头形状主体部分构建使用正弦曲线控制半径变化radius max_radius * sin(πt/2)实现从原点向外逐渐扩张的平滑过渡在85%位置默认结束主体部分半球封口在主体末端添加半球形封口使轨道末端呈现圆滑的大头形状几何变换构建局部坐标系方向向量两个正交基将2D圆环变换到3D空间中的正确方向3. STL模型生成defgenerate_stl_model(...):# 1. 生成正向的大轨道# 2. 生成反向的小轨道模拟电子云的另一端# 3. 添加中心原子球体# 4. 合并所有网格并导出为STL格式大轨道主电子云区域小轨道反向的较小电子云模拟轨道的完整分布原子核中心球体表示原子支持3D打印和教育模型制作4. 交互控制参数调节滑块原子半径(0.1-1.0)控制中心原子的大小轨道长度(1.0-5.0)控制轨道延伸距离轨道半径(0.1-1.5)控制轨道大头的粗细小轨道比例(0.1-0.5)控制反向轨道的大小比例主体结束位置(0.5-0.95)控制大头开始的位置轨道类型选择四种杂化类型对应不同的空间构型实时更新对称性信息和轨道数量5. 3D可视坐标系统显示XYZ坐标轴红绿蓝提供空间参考框架轨道可视化使用锥形面片表示轨道形状不同轨道用不同颜色区分透明度设置显示内部结构交互功能鼠标拖拽旋转视角滚轮缩放预设视角重置四、完整代码import streamlit as st import numpy as np import plotly.graph_objects as go import math from stl import mesh import io # 设置页面配置 st.set_page_config( page_title3D分子轨道可视化工具, layoutwide, initial_sidebar_stateexpanded ) # 标题 st.title(3D分子轨道可视化工具) st.write(交互式探索杂化轨道与量子化学可视化) # 初始化session_state if params not in st.session_state: st.session_state.params { orbital_type: sp3, atom_radius: 0.35, orbital_length: 2.2, orbital_radius: 0.55, resolution: 20, small_orbital_scale: 0.3, body_end_ratio: 0.85 } def get_orbital_directions(orbital_type): 获取轨道方向 if orbital_type sp3: return [ np.array([1, 1, 1]) / np.sqrt(3), np.array([1, -1, -1]) / np.sqrt(3), np.array([-1, 1, -1]) / np.sqrt(3), np.array([-1, -1, 1]) / np.sqrt(3) ] elif orbital_type sp2: return [ np.array([1, 0, 0]), np.array([-0.5, np.sqrt(3)/2, 0]), np.array([-0.5, -np.sqrt(3)/2, 0]) ] elif orbital_type sp: return [ np.array([0, 0, 1]), np.array([0, 0, -1]) ] elif orbital_type p: return [ np.array([1, 0, 0]), np.array([-1, 0, 0]) ] return [np.array([1, 0, 0])] def create_sphere_mesh(center, radius, n_lat16, n_lon32): 创建球体网格 vertices [] faces [] for i in range(n_lat 1): phi math.pi * i / n_lat for j in range(n_lon): theta 2 * math.pi * j / n_lon x center[0] radius * math.sin(phi) * math.cos(theta) y center[1] radius * math.sin(phi) * math.sin(theta) z center[2] radius * math.cos(phi) vertices.append([x, y, z]) # 生成三角面 for i in range(n_lat): for j in range(n_lon): next_j (j 1) % n_lon v0 i * n_lon j v1 i * n_lon next_j v2 (i 1) * n_lon j v3 (i 1) * n_lon next_j faces.append([v0, v1, v2]) faces.append([v1, v3, v2]) return np.array(vertices), np.array(faces) def create_orbital_mesh(start_point, direction, length2.2, max_radius0.55, n_lat20, n_lon32): 生成单个轨道网格精确的sp³轨道几何模型 vertices [] faces [] direction np.array(direction) / np.linalg.norm(direction) # 构建正交基 if abs(direction[2]) 0.9: v1 np.cross(direction, (0, 0, 1)) else: v1 np.cross(direction, (1, 0, 0)) v1 v1 / np.linalg.norm(v1) v2 np.cross(direction, v1) v2 v2 / np.linalg.norm(v2) body_end_t 0.85 # 主体结束位置 # 主体部分 for i in range(int(n_lat * body_end_t) 1): t i / n_lat # 使用正弦曲线实现平滑的半径增长 radius max_radius * math.sin(t / body_end_t * math.pi / 2) pos start_point direction * (t * length) for j in range(n_lon): theta 2 * math.pi * j / n_lon offset v1 * (radius * math.cos(theta)) v2 * (radius * math.sin(theta)) vertex pos offset vertices.append(vertex.tolist()) # 半球封口 sphere_center start_point direction * (body_end_t * length) n_hemi 12 for i in range(n_hemi 1): phi math.pi * i / (2 * n_hemi) sphere_r max_radius * math.sin(phi) z_offset max_radius * math.cos(phi) pos sphere_center direction * z_offset for j in range(n_lon): theta 2 * math.pi * j / n_lon offset v1 * (sphere_r * math.cos(theta)) v2 * (sphere_r * math.sin(theta)) vertices.append((pos offset).tolist()) # 生成三角面主体侧面 for i in range(int(n_lat * body_end_t)): for j in range(n_lon): next_j (j 1) % n_lon v0 i * n_lon j v1 i * n_lon next_j v2 (i 1) * n_lon next_j v3 (i 1) * n_lon j faces.append([v0, v1, v2]) faces.append([v0, v2, v3]) # 半球封口面 start_idx len(vertices) - (n_hemi 1) * n_lon for i in range(n_hemi): for j in range(n_lon): next_j (j 1) % n_lon v0 start_idx i * n_lon j v1 start_idx i * n_lon next_j v2 start_idx (i 1) * n_lon j v3 start_idx (i 1) * n_lon next_j faces.append([v0, v1, v2]) faces.append([v1, v3, v2]) return np.array(vertices), np.array(faces) def generate_stl_model(orbital_type, atom_radius, orbital_length, orbital_radius, small_scale0.3): 生成完整的STL模型 all_vertices [] all_faces [] vertex_offset 0 directions get_orbital_directions(orbital_type) # 生成大轨道 for direction in directions: v, f create_orbital_mesh( (0, 0, 0), direction, lengthorbital_length, max_radiusorbital_radius ) all_vertices.extend(v) all_faces.extend(f vertex_offset) vertex_offset len(v) # 生成小轨道反方向 for direction in directions: opposite_dir -np.array(direction) v, f create_orbital_mesh( (0, 0, 0), opposite_dir, lengthorbital_length * small_scale, max_radiusorbital_radius * small_scale ) all_vertices.extend(v) all_faces.extend(f vertex_offset) vertex_offset len(v) # 添加中心原子 v, f create_sphere_mesh((0, 0, 0), atom_radius) all_vertices.extend(v) all_faces.extend(f vertex_offset) # 创建STL网格 vertices np.array(all_vertices) faces np.array(all_faces) stl_mesh mesh.Mesh(np.zeros(faces.shape[0], dtypemesh.Mesh.dtype)) for i, f in enumerate(faces): for j in range(3): stl_mesh.vectors[i][j] vertices[f[j]] return stl_mesh # 侧边栏控制 with st.sidebar: st.header(轨道参数控制) orbital_type st.selectbox( 轨道类型, options[sp3, sp2, sp, p], format_funclambda x: { sp3: sp³杂化 (四面体), sp2: sp²杂化 (平面三角形), sp: sp杂化 (线性), p: p轨道 (哑铃形) }[x], keyorbital_type_select ) st.session_state.params[orbital_type] orbital_type st.session_state.params[atom_radius] st.slider( 原子半径, min_value0.1, max_value1.0, valuest.session_state.params[atom_radius], step0.05, keyatom_radius_slider ) st.session_state.params[orbital_length] st.slider( 轨道长度, min_value1.0, max_value5.0, valuest.session_state.params[orbital_length], step0.1, keyorbital_length_slider ) st.session_state.params[orbital_radius] st.slider( 轨道半径, min_value0.1, max_value1.5, valuest.session_state.params[orbital_radius], step0.05, keyorbital_radius_slider ) st.session_state.params[small_orbital_scale] st.slider( 小轨道比例, min_value0.1, max_value0.5, valuest.session_state.params[small_orbital_scale], step0.05, keysmall_orbital_scale_slider ) st.session_state.params[body_end_ratio] st.slider( 主体结束位置, min_value0.5, max_value0.95, valuest.session_state.params[body_end_ratio], step0.05, keybody_end_ratio_slider ) if st.button(重置视角): st.session_state.view_state {camera: {eye: {x: 2, y: 2, z: 2}}} # STL导出功能 st.markdown(---) st.header(模型导出) if st.button(导出STL模型): try: stl_mesh generate_stl_model( orbital_typest.session_state.params[orbital_type], atom_radiusst.session_state.params[atom_radius], orbital_lengthst.session_state.params[orbital_length], orbital_radiusst.session_state.params[orbital_radius], small_scalest.session_state.params[small_orbital_scale] ) # 保存STL文件到临时文件然后读取为字节流 import tempfile with tempfile.NamedTemporaryFile(suffix.stl, deleteFalse) as tmp_file: stl_mesh.save(tmp_file.name) tmp_file.flush() with open(tmp_file.name, rb) as f: stl_buffer io.BytesIO(f.read()) stl_buffer.seek(0) st.download_button( label下载STL文件, datastl_buffer, file_namef{orbital_type}_orbital.stl, mimeapplication/octet-stream ) st.success(STL模型生成成功) except Exception as e: st.error(f生成STL模型时出错: {str(e)}) # 轨道信息 st.sidebar.markdown(---) st.sidebar.header(轨道信息) orbital_names { sp3: sp³杂化, sp2: sp²杂化, sp: sp杂化, p: p轨道 } symmetry_names { sp3: 四面体 (Td), sp2: 平面三角形 (D₃h), sp: 线性 (D∞h), p: 哑铃形 (D∞h) } orbital_counts { sp3: 4, sp2: 3, sp: 2, p: 2 } st.sidebar.info(f **类型**: {orbital_names[orbital_type]} **对称性**: {symmetry_names[orbital_type]} **轨道数**: {orbital_counts[orbital_type]} **原子半径**: {st.session_state.params[atom_radius]:.2f} **轨道长度**: {st.session_state.params[orbital_length]:.2f} **轨道半径**: {st.session_state.params[orbital_radius]:.2f} **小轨道比例**: {st.session_state.params[small_orbital_scale]:.2f} **主体结束位置**: {st.session_state.params[body_end_ratio]:.2f} ) # 创建3D可视化 col1, col2 st.columns([3, 1]) with col1: st.subheader(3D可视化) # 创建3D图形 fig go.Figure() # 添加坐标轴 fig.add_trace(go.Scatter3d( x[-3, 3], y[0, 0], z[0, 0], modelines, linedict(colorred, width3), nameX轴, showlegendFalse )) fig.add_trace(go.Scatter3d( x[0, 0], y[-3, 3], z[0, 0], modelines, linedict(colorgreen, width3), nameY轴, showlegendFalse )) fig.add_trace(go.Scatter3d( x[0, 0], y[0, 0], z[-3, 3], modelines, linedict(colorblue, width3), nameZ轴, showlegendFalse )) # 添加中心原子 atom_radius st.session_state.params[atom_radius] u np.linspace(0, 2*np.pi, 20) v np.linspace(0, np.pi, 20) x atom_radius * np.outer(np.cos(u), np.sin(v)) y atom_radius * np.outer(np.sin(u), np.sin(v)) z atom_radius * np.outer(np.ones(np.size(u)), np.cos(v)) fig.add_trace(go.Surface( xx, yy, zz, colorscale[[0, #cccccc], [1, #cccccc]], showscaleFalse, name原子, opacity0.8 )) # 添加轨道 directions get_orbital_directions(orbital_type) colors [#4cc9f0, #4361ee, #3a0ca3, #7209b7] for i, direction in enumerate(directions): color colors[i % len(colors)] length st.session_state.params[orbital_length] radius st.session_state.params[orbital_radius] # 创建锥形轨道 t np.linspace(0, length, 15) theta np.linspace(0, 2*np.pi, 20) for ti in t: r radius * (1 - ti/length) circle_x r * np.cos(theta) circle_y r * np.sin(theta) circle_z np.full_like(theta, ti) # 变换到世界坐标 world_x circle_x direction[0] * ti direction[0] * length / 2 world_y circle_y direction[1] * ti direction[1] * length / 2 world_z circle_z direction[2] * ti direction[2] * length / 2 fig.add_trace(go.Scatter3d( xworld_x, yworld_y, zworld_z, modelines, linedict(colorcolor, width3), showlegendFalse, opacity0.7 )) # 设置布局 fig.update_layout( scenedict( xaxisdict(range[-4, 4], backgroundcolor#0f172a, gridcolor#1e293b, colorwhite), yaxisdict(range[-4, 4], backgroundcolor#0f172a, gridcolor#1e293b, colorwhite), zaxisdict(range[-4, 4], backgroundcolor#0f172a, gridcolor#1e293b, colorwhite), bgcolor#0f172a, cameradict( eyedict(x2, y2, z2) ) ), width800, height600, margindict(l0, r0, t0, b0), paper_bgcolor#1a1a2e ) # 显示3D图形 st.plotly_chart(fig, use_container_widthTrue, config{displayModeBar: True}) with col2: st.subheader(使用说明) st.info( **操作说明** - 鼠标左键拖拽旋转视角 - 鼠标滚轮缩放 - 右侧滑块调节参数 **轨道类型** - sp³四面体如CH₄ - sp²平面三角形如C₂H₄ - sp线性如C₂H₂ - p哑铃形 ) # 底部说明 st.markdown(---) st.markdown( div styletext-align: center; color: #94a3b8; p三维分子轨道可视/p /div , unsafe_allow_htmlTrue)