{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Working with Occupancy Grids\n",
    "\n",
    "While isoext is designed for signed distance fields (SDFs), you can also extract surfaces from **binary occupancy grids**—voxel grids where each cell is either occupied (1) or empty (0).\n",
    "\n",
    "This is useful when:\n",
    "- You have voxelized data from 3D scanning or segmentation\n",
    "- Your data comes from a boolean CSG operation\n",
    "- You want to convert a mesh to voxels and back\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:08:30.766629Z",
     "iopub.status.busy": "2026-07-05T04:08:30.766551Z",
     "iopub.status.idle": "2026-07-05T04:08:31.397195Z",
     "shell.execute_reply": "2026-07-05T04:08:31.396762Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "import isoext\n",
    "from isoext.sdf import SphereSDF\n",
    "from isoext import viewer\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating an Occupancy Grid\n",
    "\n",
    "Let's create a binary occupancy grid from a sphere SDF. Values > 0 are outside, so we threshold to get a binary mask:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:08:31.398455Z",
     "iopub.status.busy": "2026-07-05T04:08:31.398340Z",
     "iopub.status.idle": "2026-07-05T04:08:31.589806Z",
     "shell.execute_reply": "2026-07-05T04:08:31.589409Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Occupancy values: [0.0, 1.0]\n"
     ]
    }
   ],
   "source": [
    "# Create a grid and compute sphere SDF\n",
    "grid = isoext.UniformGrid([64, 64, 64])\n",
    "sdf_values = SphereSDF(radius=0.7)(grid.get_points())\n",
    "\n",
    "# Convert to binary occupancy: 0 = outside, 1 = inside\n",
    "occupancy = (sdf_values < 0.0).float()\n",
    "\n",
    "print(f\"Occupancy values: {occupancy.unique().tolist()}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Direct Extraction (Jagged Result)\n",
    "\n",
    "You can run marching cubes directly on the binary grid using `level=0.5` (the boundary between 0 and 1). However, the result will be **jagged** because there's no gradient information—every surface point lands exactly on a voxel face:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:08:31.605989Z",
     "iopub.status.busy": "2026-07-05T04:08:31.605882Z",
     "iopub.status.idle": "2026-07-05T04:08:31.692518Z",
     "shell.execute_reply": "2026-07-05T04:08:31.692159Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Jagged mesh: 9168 vertices, 18332 faces\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"420\"\n",
       "            src=\"_static/viser/index.html?playbackPath=../scenes/2e6f8a7a7566515e.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 0x7756f1b779e0>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "grid.set_values(occupancy)\n",
    "v_jagged, f_jagged = isoext.marching_cubes(grid, level=0.5)\n",
    "\n",
    "print(f\"Jagged mesh: {v_jagged.shape[0]} vertices, {f_jagged.shape[0]} faces\")\n",
    "viewer.embed(v_jagged, f_jagged, flat_shading=True)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Smoothed Extraction\n",
    "\n",
    "To get a smoother result, apply **Gaussian smoothing** before extraction. This blurs the sharp 0/1 transitions into gradual gradients, allowing marching cubes to interpolate vertex positions:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:08:31.693808Z",
     "iopub.status.busy": "2026-07-05T04:08:31.693684Z",
     "iopub.status.idle": "2026-07-05T04:08:31.901417Z",
     "shell.execute_reply": "2026-07-05T04:08:31.901067Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Smooth mesh: 8232 vertices, 16460 faces\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"420\"\n",
       "            src=\"_static/viser/index.html?playbackPath=../scenes/cd6b06ad7f559c0a.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 0x7756f1ca23c0>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Smooth the occupancy grid\n",
    "smoothed = isoext.gaussian_smooth(occupancy, sigma=5.0)\n",
    "grid.set_values(smoothed)\n",
    "v_smooth, f_smooth = isoext.marching_cubes(grid, level=0.5)\n",
    "\n",
    "print(f\"Smooth mesh: {v_smooth.shape[0]} vertices, {f_smooth.shape[0]} faces\")\n",
    "viewer.embed(v_smooth, f_smooth)\n"
   ]
  }
 ],
 "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
}
