{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Marching Cubes\n",
    "\n",
    "Marching Cubes extracts a **triangular mesh** from a scalar field. It examines each cell in the grid and generates triangles where the iso-surface passes through.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:06:20.152460Z",
     "iopub.status.busy": "2026-07-05T04:06:20.152358Z",
     "iopub.status.idle": "2026-07-05T04:06:20.986455Z",
     "shell.execute_reply": "2026-07-05T04:06:20.986097Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "import isoext\n",
    "from isoext import viewer\n",
    "\n",
    "# Setup: create a grid with a sphere\n",
    "grid = isoext.UniformGrid([64, 64, 64])\n",
    "points = grid.get_points()\n",
    "grid.set_values(points.norm(dim=-1) - 0.7)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Basic Usage\n",
    "\n",
    "```python\n",
    "vertices, faces = isoext.marching_cubes(grid, level=0.0, method=\"nagae\")\n",
    "```\n",
    "\n",
    "- `grid`: A `UniformGrid` or `SparseGrid` with values set\n",
    "- `level`: The iso-value to extract (default: 0.0)\n",
    "- `method`: Algorithm variant (`\"nagae\"` or `\"lorensen\"`)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:06:20.988789Z",
     "iopub.status.busy": "2026-07-05T04:06:20.988664Z",
     "iopub.status.idle": "2026-07-05T04:06:21.072786Z",
     "shell.execute_reply": "2026-07-05T04:06:21.072373Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vertices: torch.Size([9168, 3])\n",
      "Faces: torch.Size([18332, 3])\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"420\"\n",
       "            src=\"_static/viser/index.html?playbackPath=../scenes/a2ce8dd973f21c15.viser\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            style=\"border: 1px solid #8888; border-radius: 4px;\"\n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x7b248d90f320>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "vertices, faces = isoext.marching_cubes(grid)\n",
    "\n",
    "print(f\"Vertices: {vertices.shape}\")  # (N, 3) float32\n",
    "print(f\"Faces: {faces.shape}\")        # (M, 3) uint32\n",
    "\n",
    "viewer.embed(vertices, faces)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Algorithm Variants\n",
    "\n",
    "### `nagae` (default)\n",
    "\n",
    "Uses only rotations to handle ambiguous cases, producing **watertight meshes** without holes or cracks.\n",
    "\n",
    "From: *\"Surface construction and contour generation from volume data\"* (1991)\n",
    "\n",
    "### `lorensen`\n",
    "\n",
    "The original Marching Cubes algorithm. Uses rotations and reflections, which can create ambiguities leading to small holes in the mesh.\n",
    "\n",
    "From: *\"Marching cubes: A high resolution 3D surface construction algorithm\"* (1987)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:06:21.087022Z",
     "iopub.status.busy": "2026-07-05T04:06:21.086887Z",
     "iopub.status.idle": "2026-07-05T04:06:21.105181Z",
     "shell.execute_reply": "2026-07-05T04:06:21.104713Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Nagae:    18,332 triangles\n",
      "Lorensen: 18,332 triangles\n"
     ]
    }
   ],
   "source": [
    "v_nagae, f_nagae = isoext.marching_cubes(grid, method=\"nagae\")\n",
    "v_lorensen, f_lorensen = isoext.marching_cubes(grid, method=\"lorensen\")\n",
    "\n",
    "print(f\"Nagae:    {f_nagae.shape[0]:,} triangles\")\n",
    "print(f\"Lorensen: {f_lorensen.shape[0]:,} triangles\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Iso-Level\n",
    "\n",
    "The `level` parameter controls which iso-surface to extract. For signed distance fields, `level=0` gives the surface. Other values give offset surfaces:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:06:21.107009Z",
     "iopub.status.busy": "2026-07-05T04:06:21.106941Z",
     "iopub.status.idle": "2026-07-05T04:06:21.109825Z",
     "shell.execute_reply": "2026-07-05T04:06:21.109610Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "level=-0.2: 4,728 vertices (radius ≈ 0.9)\n",
      "level=+0.0: 9,168 vertices (radius ≈ 0.7)\n",
      "level=+0.2: 15,072 vertices (radius ≈ 0.5)\n"
     ]
    }
   ],
   "source": [
    "# Extract at different iso-levels\n",
    "for level in [-0.2, 0.0, 0.2]:\n",
    "    v, f = isoext.marching_cubes(grid, level=level)\n",
    "    print(f\"level={level:+.1f}: {v.shape[0]:,} vertices (radius ≈ {0.7 - level:.1f})\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Saving Meshes\n",
    "\n",
    "Use `write_obj` to save the mesh to an OBJ file:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:06:21.111319Z",
     "iopub.status.busy": "2026-07-05T04:06:21.111254Z",
     "iopub.status.idle": "2026-07-05T04:06:21.125969Z",
     "shell.execute_reply": "2026-07-05T04:06:21.125772Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Saved to mesh.obj\n"
     ]
    }
   ],
   "source": [
    "vertices, faces = isoext.marching_cubes(grid)\n",
    "isoext.write_obj(\"mesh.obj\", vertices, faces)\n",
    "print(\"Saved to mesh.obj\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lookup tables\n",
    "\n",
    "The lookup tables that drive marching cubes are generated by [`luts/gen_mc_lut.py`](https://github.com/GuangyanCai/isoext/blob/master/luts/gen_mc_lut.py). This script:\n",
    "\n",
    "1. Reads base cases from a JSON file (e.g., `luts/mc_methods/nagae.json`)\n",
    "2. Generates all 256 cases via rotations (and optionally reflections)\n",
    "3. Outputs lookup tables and debug meshes\n",
    "\n",
    "You can use this as a reference for:\n",
    "- **Understanding the algorithm**: See how base cases expand to cover all configurations\n",
    "- **Implementing your own variant**: Create a new JSON file with your base cases\n",
    "- **Debugging**: The script outputs OBJ files for each case to visualize the triangulations\n",
    "\n",
    "```bash\n",
    "# Generate lookup tables for a marching cubes variant\n",
    "python luts/gen_mc_lut.py luts/mc_methods/nagae.json output_dir/\n",
    "```\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "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
}
