{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "775ca6a3-d589-4c12-948b-f3753a00329c", "metadata": {}, "outputs": [], "source": [ "# File: fishingknot.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": "bc4129bb-9186-468d-a0c5-9cf44414f120", "metadata": {}, "outputs": [], "source": [ "import os\n", "import urllib.request\n", "\n", "from frontend import App, get_cache_dir\n", "\n", "# download the fishing knot mesh if not already present\n", "fishingknot_path = os.path.join(get_cache_dir(), \"fishingknot.ply\")\n", "if not os.path.exists(fishingknot_path):\n", " fishingknot_url = (\n", " \"https://github.com/st-tech/ppf-contact-solver/releases/\"\n", " \"download/v1.0-assets/fishingknot.ply\"\n", " )\n", " urllib.request.urlretrieve(fishingknot_url, fishingknot_path)\n", "\n", "# create an app\n", "app = App.create(\"fishingknot\")\n", "\n", "# load the fishing knot ribbon mesh\n", "V, F = app.mesh.load_tri(fishingknot_path)\n", "app.asset.add.tri(\"ribbon\", V, F)\n", "\n", "# create a torus mesh\n", "V, F = app.mesh.torus(r=0.5, R=0.125)\n", "app.asset.add.tri(\"torus\", V, F)\n", "\n", "# create a scene\n", "scene = app.scene.create()\n", "\n", "# add ribbon with directional color and pin configuration\n", "ribbon = scene.add(\"ribbon\").jitter().direction_color(-1, 0, 0)\n", "ribbon.pin(ribbon.grab([-1, 0, 0], 0.4)).pull(1000.0).move_by([-10, 0, 0], 0, 18)\n", "ribbon.pin(ribbon.grab([0, -1, 0], 0.1))\n", "\n", "# set ribbon material properties\n", "(\n", " ribbon.param.set(\"model\", \"arap\")\n", " .set(\"bend\", 100.0)\n", " .set(\"young-mod\", 1000)\n", " .set(\"contact-gap\", 1e-3)\n", " .set(\"strain-limit\", 0.05)\n", ")\n", "\n", "# add torus as a fixed obstacle\n", "torus = scene.add(\"torus\").at(4.55, 1.2, 0.05)\n", "torus.pin()\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": "72e9a486-a231-4938-ab1f-09253e3009a8", "metadata": {}, "outputs": [], "source": [ "# create a new session with the compiled scene\n", "session = app.session.create(scene)\n", "\n", "# set session parameters - disable gravity for this simulation\n", "if app.ci:\n", " session.param.set(\"frames\", 350)\n", "else:\n", " session.param.set(\"frames\", 750)\n", "session.param.set(\"gravity\", [0, 0, 0])\n", "\n", "# build this session\n", "session = session.build()" ] }, { "cell_type": "code", "execution_count": null, "id": "3d3b8948-a0a3-4a26-96bd-fa68e4976467", "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": "7ffd0897-76d8-4751-8266-c8dbc40c5c96", "metadata": {}, "outputs": [], "source": [ "# create an animation from the simulation results\n", "session.animate()" ] }, { "cell_type": "code", "execution_count": null, "id": "ff49a9f0-ac37-4325-b020-35f6322e37c3", "metadata": {}, "outputs": [], "source": [ "# export the animation to file\n", "session.export.animation()" ] }, { "cell_type": "code", "execution_count": null, "id": "ae4b1170-6eba-4910-9d37-8c355841a832", "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 }