{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "a57e52e5-ca2b-44bd-8284-d92427489293", "metadata": {}, "outputs": [], "source": [ "# File: trapped.ipynb\n", "# Code: Claude Code and Codex\n", "# Review: Ryoichi Ando (ryoichi.ando@zozo.com)\n", "# License: Apache v2.0" ] }, { "cell_type": "code", "execution_count": null, "id": "57106fea-2057-4d39-a359-26e3ba067979", "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", "import numpy as np\n", "from frontend import App, get_cache_dir, sdf\n", "\n", "# create an app\n", "app = App.create(\"trapped\")\n", "filepath = os.path.join(get_cache_dir(), \"squishy.tmp.ply\")\n", "\n", "# create squishy ball mesh with spiky protrusions if not exists\n", "if not os.path.exists(filepath):\n", " V, F = app.mesh.icosphere(r=2, subdiv_count=2)\n", " func = sdf.sphere(1.1)\n", " # add capsule spikes radiating outward from center\n", " for f in F:\n", " d = np.mean(V[f], axis=0)\n", " if d[0] > 0:\n", " func = func | sdf.capsule(-d, d, 0.05)\n", " func.save(filepath, step=0.03)\n", "\n", "# load and process the squishy mesh\n", "V, F, T = (\n", " app.mesh.load_tri(filepath)\n", " .decimate(100000)\n", " .tetrahedralize() # it takes from 3 to 5 mins, be patient...\n", " .normalize()\n", " .scale(0.45)\n", ")\n", "app.asset.add.tet(\"squishy\", V, F, T)\n", "\n", "# create a scene\n", "scene = app.scene.create()\n", "\n", "# add invisible sphere container with narrow passages\n", "(\n", " scene.add.invisible.sphere([0, 0, 0], 0.98)\n", " .invert()\n", " .radius(0.4, 2)\n", " .radius(0.4, 3)\n", " .radius(10, 4)\n", ")\n", "\n", "# add two squishy balls positioned to collide\n", "scene.add(\"squishy\").at(0.52, 0, 0).jitter()\n", "scene.add(\"squishy\").at(-0.52, 0, 0).jitter()\n", "\n", "# compile the scene and report stats\n", "scene = scene.build().report()\n", "\n", "# preview the initial scene\n", "scene.preview()" ] }, { "cell_type": "code", "execution_count": null, "id": "d0fb12c0-69ba-4201-9018-4a098c428a9d", "metadata": {}, "outputs": [], "source": [ "# create a new session with the compiled scene\n", "session = app.session.create(scene)\n", "\n", "# set session parameters - disable gravity and increase sparse matrix capacity\n", "(\n", " session.param.set(\"gravity\", [0, 0, 0])\n", " .set(\"csrmat-max-nnz\", 3000000)\n", " .set(\"dt\", 0.01)\n", ")\n", "\n", "# set dynamic playback speed to slow down after initial collision\n", "session.param.dyn(\"playback\").time(2.99).hold().time(3).change(0.1)\n", "\n", "# build this session\n", "session = session.build()" ] }, { "cell_type": "code", "execution_count": null, "id": "b32dd182-3bbc-44e8-9bfd-aa0ad70fe2f8", "metadata": {}, "outputs": [], "source": [ "# start the simulation and live-preview the results\n", "session.start().preview()\n", "\n", "# also show simulation logs in realtime\n", "session.stream()" ] }, { "cell_type": "code", "execution_count": null, "id": "318a7f9c-efb6-400f-8a13-25a429e217cb", "metadata": {}, "outputs": [], "source": [ "# create an animation from the simulation results\n", "session.animate()" ] }, { "cell_type": "code", "execution_count": null, "id": "8f3bcfda-4a38-4e0f-b0f0-e35d232fca8b", "metadata": {}, "outputs": [], "source": [ "# export the animation to file\n", "session.export.animation()" ] }, { "cell_type": "code", "execution_count": null, "id": "fdeab632-6f3d-428f-8057-8b796b3ce6a4", "metadata": {}, "outputs": [], "source": [ "# this is for CI\n", "if app.ci:\n", " assert session.finished()" ] } ], "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.3" } }, "nbformat": 4, "nbformat_minor": 5 }