{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "50960426",
   "metadata": {},
   "source": [
    "# Using External SDF Libraries\n",
    "\n",
    "While `isoext` includes basic SDF primitives for convenience, you can use any SDF library to generate your scalar fields. One excellent option is [`sdf`](https://github.com/fogleman/sdf) by Michael Fogleman—a powerful Python library for constructing complex SDF.\n",
    "\n",
    "It can be installed by \n",
    "```bash\n",
    "pip install git+https://github.com/fogleman/sdf\n",
    "```\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "49b9c47f-f5ed-4939-bf19-1fb7cce490e2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:06:39.462132Z",
     "iopub.status.busy": "2026-07-05T04:06:39.462049Z",
     "iopub.status.idle": "2026-07-05T04:06:40.308489Z",
     "shell.execute_reply": "2026-07-05T04:06:40.307628Z"
    }
   },
   "outputs": [],
   "source": [
    "import sdf\n",
    "import isoext\n",
    "import torch\n",
    "from isoext import viewer\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "120a4da2",
   "metadata": {},
   "source": [
    "## Example\n",
    "\n",
    "This example recreates the [weave](https://github.com/fogleman/sdf/blob/main/examples/weave.py) demo from the `sdf` library.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "8b2b316c-f7a4-4974-9b2b-0902386c2b77",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:06:40.310219Z",
     "iopub.status.busy": "2026-07-05T04:06:40.310077Z",
     "iopub.status.idle": "2026-07-05T04:06:40.313182Z",
     "shell.execute_reply": "2026-07-05T04:06:40.312206Z"
    }
   },
   "outputs": [],
   "source": [
    "# The following example is taken from https://github.com/fogleman/sdf/blob/main/examples/weave.py\n",
    "\n",
    "f = sdf.rounded_box([3.2, 1, 0.25], 0.1).translate((1.5, 0, 0.0625))\n",
    "f = f.bend_linear(sdf.X * 0.75, sdf.X * 2.25, sdf.Z * -0.1875, sdf.ease.in_out_quad)\n",
    "f = f.circular_array(3, 0)\n",
    "\n",
    "f = f.repeat((2.7, 5.4, 0), padding=1)\n",
    "f |= f.translate((2.7 / 2, 2.7, 0))\n",
    "\n",
    "f &= sdf.cylinder(10)\n",
    "f |= (sdf.cylinder(12) - sdf.cylinder(10)) & sdf.slab(z0=-0.5, z1=0.5).k(0.25)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97aee6b9",
   "metadata": {},
   "source": [
    "Sample the SDF on a grid. Since the sdf library operates on NumPy arrays, we convert between PyTorch and NumPy:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "35bdcdc0-4537-4f5b-bc06-41041b46eee7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:06:40.314748Z",
     "iopub.status.busy": "2026-07-05T04:06:40.314662Z",
     "iopub.status.idle": "2026-07-05T04:08:28.321226Z",
     "shell.execute_reply": "2026-07-05T04:08:28.320559Z"
    }
   },
   "outputs": [],
   "source": [
    "shape = [256, 256, 256]\n",
    "aabb_min = [-13, -13, -13]\n",
    "aabb_max = [13, 13, 13]\n",
    "grid = isoext.UniformGrid(shape, aabb_min, aabb_max)\n",
    "points = grid.get_points().cpu().numpy()\n",
    "points = points.reshape(-1, 3)\n",
    "values = f(points).reshape(shape)\n",
    "values = torch.from_numpy(values).cuda()\n",
    "grid.set_values(values)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04aafd6d",
   "metadata": {},
   "source": [
    "Extract the mesh with marching cubes:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f69c0611-cc63-4163-b3d2-b2a887940918",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-05T04:08:28.322567Z",
     "iopub.status.busy": "2026-07-05T04:08:28.322488Z",
     "iopub.status.idle": "2026-07-05T04:08:28.512451Z",
     "shell.execute_reply": "2026-07-05T04:08:28.511855Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vertices: torch.Size([110818, 3])\n",
      "Faces: torch.Size([222000, 3])\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"420\"\n",
       "            src=\"_static/viser/index.html?playbackPath=../scenes/7f9a18f64439d275.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 0x7ca57d79e6f0>"
      ]
     },
     "execution_count": 4,
     "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": "code",
   "execution_count": null,
   "id": "04fc3e82-e703-4992-b92e-730d66c977f8",
   "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": 5
}
