{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Gmsh模型文件可视化\n",
    "\n",
    "这个notebook演示如何使用meshio和pyvista读取并可视化Gmsh模型文件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 安装必要的库\n",
    "\n",
    "首先，我们需要安装meshio和pyvista库："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 安装必要的库\n",
    "import subprocess\n",
    "import sys\n",
    "\n",
    "def install_package(package):\n",
    "    \"\"\"安装Python包\"\"\"\n",
    "    try:\n",
    "        subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", package])\n",
    "        print(f\"✓ {package} 安装成功\")\n",
    "    except subprocess.CalledProcessError:\n",
    "        print(f\"✗ {package} 安装失败\")\n",
    "\n",
    "# 安装meshio和pyvista\n",
    "#install_package(\"meshio\")\n",
    "#install_package(\"pyvista\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 导入库\n",
    "\n",
    "导入我们将要使用的库："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✓ 所有库导入成功\n",
      "meshio版本: 5.3.5\n",
      "pyvista版本: 0.46.3\n"
     ]
    }
   ],
   "source": [
    "import meshio\n",
    "import pyvista as pv\n",
    "import numpy as np\n",
    "import os\n",
    "from pathlib import Path\n",
    "\n",
    "# 设置pyvista的显示选项\n",
    "pv.set_plot_theme(\"document\")  # 使用文档主题\n",
    "pv.set_jupyter_backend(\"static\")  # 在Jupyter中使用静态后端\n",
    "\n",
    "print(\"✓ 所有库导入成功\")\n",
    "print(f\"meshio版本: {meshio.__version__}\")\n",
    "print(f\"pyvista版本: {pv.__version__}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 读取Gmsh文件\n",
    "\n",
    "使用meshio读取Gmsh文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "✓ 成功读取文件: t1.msh\n",
      "  - 点数: 3929\n",
      "  - 维度: 3\n",
      "  - line单元数: 356\n",
      "  - triangle单元数: 7474\n",
      "\n",
      "网格详细信息:\n",
      "点坐标范围:\n",
      "  X: [0.000, 0.100]\n",
      "  Y: [0.000, 0.300]\n",
      "  Z: [0.000, 0.000]\n"
     ]
    }
   ],
   "source": [
    "def read_gmsh_file(filename):\n",
    "    \"\"\"读取Gmsh文件并返回mesh对象\"\"\"\n",
    "    try:\n",
    "        mesh = meshio.read(filename)\n",
    "        print(f\"✓ 成功读取文件: {filename}\")\n",
    "        print(f\"  - 点数: {mesh.points.shape[0]}\")\n",
    "        print(f\"  - 维度: {mesh.points.shape[1]}\")\n",
    "        \n",
    "        # 显示单元类型和数量\n",
    "        for cell_block in mesh.cells:\n",
    "            cell_type = cell_block.type\n",
    "            cells = cell_block.data\n",
    "            print(f\"  - {cell_type}单元数: {cells.shape[0]}\")\n",
    "        \n",
    "        return mesh\n",
    "    except Exception as e:\n",
    "        print(f\"✗ 读取文件失败: {e}\")\n",
    "        return None\n",
    "\n",
    "# 读取示例文件\n",
    "mesh = read_gmsh_file(\"t1.msh\")\n",
    "\n",
    "# 显示网格信息\n",
    "if mesh:\n",
    "    print(\"\\n网格详细信息:\")\n",
    "    print(f\"点坐标范围:\")\n",
    "    print(f\"  X: [{mesh.points[:, 0].min():.3f}, {mesh.points[:, 0].max():.3f}]\")\n",
    "    print(f\"  Y: [{mesh.points[:, 1].min():.3f}, {mesh.points[:, 1].max():.3f}]\")\n",
    "    print(f\"  Z: [{mesh.points[:, 2].min():.3f}, {mesh.points[:, 2].max():.3f}]\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 转换为PyVista格式\n",
    "\n",
    "将meshio网格转换为PyVista格式以便可视化："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✓ 网格转换成功\n",
      "  - PyVista网格类型: PolyData\n",
      "  - 点数: 3929\n",
      "  - 单元数: 11403\n"
     ]
    }
   ],
   "source": [
    "def meshio_to_pyvista(mesh):\n",
    "    \"\"\"将 meshio 网格转换为 PyVista 网格\"\"\"\n",
    "    points = mesh.points\n",
    "\n",
    "    # 1. 四面体\n",
    "    tetra_block = None\n",
    "    for block in mesh.cells:\n",
    "        if block.type == \"tetra\":\n",
    "            tetra_block = block\n",
    "            break\n",
    "    if tetra_block is not None:\n",
    "        # PV 要求每行前缀节点数\n",
    "        cells = np.hstack([np.full((tetra_block.data.shape[0], 1), 4), tetra_block.data]).ravel()\n",
    "        cell_types = np.full(tetra_block.data.shape[0], pv.CellType.TETRA, dtype=np.uint8)\n",
    "        return pv.UnstructuredGrid(cells, cell_types, points)\n",
    "\n",
    "    # 2. 三角形\n",
    "    tri_block = None\n",
    "    for block in mesh.cells:\n",
    "        if block.type == \"triangle\":\n",
    "            tri_block = block\n",
    "            break\n",
    "    if tri_block is not None:\n",
    "        pdata = pv.PolyData(points)\n",
    "        pdata.faces = np.hstack([np.full((tri_block.data.shape[0], 1), 3), tri_block.data]).ravel()\n",
    "        return pdata\n",
    "\n",
    "    # 3. 都不支持就退化成点云\n",
    "    return pv.PolyData(points)\n",
    "\n",
    "# 转换网格\n",
    "if mesh:\n",
    "    pv_mesh = meshio_to_pyvista(mesh)\n",
    "    print(f\"✓ 网格转换成功\")\n",
    "    print(f\"  - PyVista网格类型: {type(pv_mesh).__name__}\")\n",
    "    print(f\"  - 点数: {pv_mesh.n_points}\")\n",
    "    print(f\"  - 单元数: {pv_mesh.n_cells}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. 可视化网格\n",
    "\n",
    "使用PyVista可视化网格："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "[encoded data, 73264 characters]",
      "image/png": "[encoded data, 283324 characters]",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=1024x768>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def visualize_mesh(pv_mesh, title=\"Gmsh网格可视化\"):\n",
    "    \"\"\"可视化PyVista网格\"\"\"\n",
    "    \n",
    "    # 创建绘图器\n",
    "    plotter = pv.Plotter(notebook=True)\n",
    "    \n",
    "    # 添加网格到绘图器\n",
    "    if pv_mesh.n_cells > 0:\n",
    "        # 如果有单元，显示网格\n",
    "        plotter.add_mesh(pv_mesh, show_edges=True, color=\"lightblue\", \n",
    "                        opacity=0.8, label=\"网格\")\n",
    "    else:\n",
    "        # 如果只有点，显示点云\n",
    "        plotter.add_points(pv_mesh.points, color=\"red\", point_size=10, \n",
    "                          label=\"点云\")\n",
    "    \n",
    "    # 添加坐标轴\n",
    "    plotter.add_axes()\n",
    "    \n",
    "    # 添加标题\n",
    "    plotter.add_title(title)\n",
    "    \n",
    "    # 显示网格信息\n",
    "    plotter.add_text(f\"点数: {pv_mesh.n_points}\\n单元数: {pv_mesh.n_cells}\", \n",
    "                    position=\"upper_right\", font_size=10)\n",
    "    \n",
    "    # 显示图形\n",
    "    return plotter.show()\n",
    "\n",
    "# 可视化网格\n",
    "if mesh:\n",
    "    visualize_mesh(pv_mesh, \"示例立方体网格\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. 高级可视化选项\n",
    "\n",
    "展示一些高级可视化功能："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "[encoded data, 95036 characters]",
      "image/png": "[encoded data, 207944 characters]",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=1024x768>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def advanced_visualization(pv_mesh):\n",
    "    \"\"\"高级可视化选项\"\"\"\n",
    "    \n",
    "    # 创建子图\n",
    "    plotter = pv.Plotter(shape=(2, 2), notebook=True)\n",
    "    \n",
    "    # 子图1: 基础网格\n",
    "    plotter.subplot(0, 0)\n",
    "    plotter.add_mesh(pv_mesh, show_edges=True, color=\"lightblue\", opacity=0.8)\n",
    "    plotter.add_title(\"基础网格\")\n",
    "    plotter.add_axes()\n",
    "    \n",
    "    # 子图2: 带颜色的网格\n",
    "    plotter.subplot(0, 1)\n",
    "    # 为每个单元添加随机颜色\n",
    "    if pv_mesh.n_cells > 0:\n",
    "        pv_mesh.cell_data[\"colors\"] = np.random.rand(pv_mesh.n_cells)\n",
    "        plotter.add_mesh(pv_mesh, scalars=\"colors\", show_edges=True, cmap=\"viridis\")\n",
    "    plotter.add_title(\"彩色网格\")\n",
    "    plotter.add_axes()\n",
    "    \n",
    "    # 子图3: 线框模式\n",
    "    plotter.subplot(1, 0)\n",
    "    plotter.add_mesh(pv_mesh, style=\"wireframe\", color=\"black\", line_width=2)\n",
    "    plotter.add_title(\"线框模式\")\n",
    "    plotter.add_axes()\n",
    "    \n",
    "    # 子图4: 点模式\n",
    "    plotter.subplot(1, 1)\n",
    "    plotter.add_points(pv_mesh.points, color=\"red\", point_size=10)\n",
    "    plotter.add_title(\"点云模式\")\n",
    "    plotter.add_axes()\n",
    "    \n",
    "    # 链接所有子图的相机\n",
    "    plotter.link_views()\n",
    "    \n",
    "    return plotter.show()\n",
    "\n",
    "# 高级可视化\n",
    "if mesh:\n",
    "    advanced_visualization(pv_mesh)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8. 读取现有的Gmsh文件\n",
    "\n",
    "如果您有现有的Gmsh文件，可以使用以下代码读取和可视化："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "正在处理文件: t1.msh\n",
      "\n",
      "✓ 成功读取文件: t1.msh\n",
      "  - 点数: 3929\n",
      "  - 维度: 3\n",
      "  - line单元数: 356\n",
      "  - triangle单元数: 7474\n"
     ]
    },
    {
     "data": {
      "image/jpeg": "[encoded data, 77424 characters]",
      "image/png": "[encoded data, 287516 characters]",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=1024x768>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def load_and_visualize_gmsh(filename):\n",
    "    \"\"\"读取并可视化Gmsh文件的完整流程\"\"\"\n",
    "    \n",
    "    print(f\"正在处理文件: {filename}\")\n",
    "    \n",
    "    # 检查文件是否存在\n",
    "    if not os.path.exists(filename):\n",
    "        print(f\"✗ 文件不存在: {filename}\")\n",
    "        return None\n",
    "    \n",
    "    # 读取Gmsh文件\n",
    "    mesh = read_gmsh_file(filename)\n",
    "    if mesh is None:\n",
    "        return None\n",
    "    \n",
    "    # 转换为PyVista格式\n",
    "    pv_mesh = meshio_to_pyvista(mesh)\n",
    "    if pv_mesh is None:\n",
    "        print(\"✗ 网格转换失败\")\n",
    "        return None\n",
    "    \n",
    "    # 可视化\n",
    "    return visualize_mesh(pv_mesh, f\"Gmsh文件: {os.path.basename(filename)}\")\n",
    "\n",
    "# 示例：读取现有的Gmsh文件\n",
    "# 请将下面的文件名替换为您的Gmsh文件路径\n",
    "your_gmsh_file = \"t1.msh\"  # 替换为您的文件路径\n",
    "\n",
    "if os.path.exists(your_gmsh_file):\n",
    "    load_and_visualize_gmsh(your_gmsh_file)\n",
    "else:\n",
    "    print(f\"文件 {your_gmsh_file} 不存在，使用示例文件\")\n",
    "    load_and_visualize_gmsh(sample_file)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 10. 总结\n",
    "\n",
    "这个notebook演示了：\n",
    "\n",
    "1. ✅ 安装meshio和pyvista库\n",
    "2. ✅ 创建示例Gmsh文件\n",
    "3. ✅ 使用meshio读取Gmsh文件\n",
    "4. ✅ 将meshio网格转换为PyVista格式\n",
    "5. ✅ 使用PyVista进行网格可视化\n",
    "6. ✅ 高级可视化选项（彩色、线框、点云）\n",
    "7. ✅ 读取现有Gmsh文件的完整流程\n",
    "\n",
    "您可以使用这个notebook作为模板来处理自己的Gmsh模型文件。只需将文件路径替换为您的Gmsh文件即可。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (forward_modeling)",
   "language": "python",
   "name": "forward_modeling"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
