{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "12234af9-4cc3-46a7-8880-1cb68aa7d9da", "metadata": {}, "outputs": [], "source": [ "# File: woven.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": "5c8313e6-3512-49fd-9617-268ee0bd0f07", "metadata": {}, "outputs": [], "source": [ "import random\n", "\n", "import numpy as np\n", "from frontend import App\n", "\n", "\n", "# function to generate woven cylinder structure with interlaced strands\n", "def make_woven_cylinder(n: int, offset: float, scale: float):\n", " dx, width = 1.0 / (n - 1), 1.25\n", " scale = 2.0 * 1.48 * scale\n", " v_steps = int(25.0 * scale)\n", " sep, strands = 0.5, []\n", "\n", " # create vertical strands\n", " for i in range(v_steps):\n", " theta = 2.0 * np.pi * i / v_steps\n", " xyz = np.zeros((n, 3))\n", " xyz[:, 0] = width * (2.0 * dx * np.arange(n) - 1.0)\n", " xyz[:, 1], xyz[:, 2] = sep * np.sin(theta), sep * np.cos(theta)\n", " strands.append((xyz, False))\n", "\n", " # create horizontal ring strands with wave pattern\n", " h_steps = int(30.0 * scale)\n", " ring_steps = v_steps * 3\n", " assert ring_steps % 2 == 0, \"ring_steps must be even\"\n", " amp, dx_h, half_v = 1.2 * offset, 1.0 / (h_steps - 1), v_steps // 2\n", "\n", " for i in range(1, h_steps - 1):\n", " sgn = 1.0 if (i % 2 == 0) else -1.0\n", " xyz = np.zeros((ring_steps, 3))\n", " xyz[:, 0] = width * (2.0 * dx_h * i - 1.0)\n", " j_indices = np.arange(ring_steps)\n", " theta_vals = 2.0 * np.pi * j_indices / ring_steps\n", " r = sep + sgn * amp * np.cos(half_v * theta_vals)\n", " xyz[:, 1], xyz[:, 2] = r * np.sin(theta_vals), r * np.cos(theta_vals)\n", " strands.append((xyz, True))\n", "\n", " return strands\n", "\n", "\n", "# create an app\n", "app = App.create(\"woven\")\n", "\n", "# create a scene\n", "scene = app.scene.create()\n", "\n", "# generate and add all woven strands\n", "angular_vel, move_delta, t_end = 360 + 10 * random.random(), 0.15, 10\n", "for k, (V, closed) in enumerate(make_woven_cylinder(256, 4e-3, 2.0)):\n", " # create edge connectivity for rod strands\n", " E = [[i, i + 1] for i in range(len(V) - 1)]\n", " if closed:\n", " E.append([len(V) - 1, 0])\n", " name = f\"strand-{k}\"\n", " app.asset.add.rod(name, V, np.array(E, dtype=np.uint32))\n", " obj = scene.add(name)\n", " # set material properties for woven strands\n", " (\n", " obj.param.set(\"bend\", 1e-3)\n", " .set(\"young-mod\", 1e5)\n", " .set(\"contact-gap\", 1.5e-3)\n", " .set(\"contact-offset\", 2e-3)\n", " .set(\"friction\", 0.01)\n", " .set(\"length-factor\", 0.8)\n", " )\n", " # pin and animate open-ended strands\n", " if not closed:\n", " (\n", " obj.pin(obj.grab([-1, 0, 0]))\n", " .spin(axis=[1, 0, 0], angular_velocity=angular_vel)\n", " .move_by([move_delta, 0, 0], 0, t_end)\n", " )\n", " (\n", " obj.pin(obj.grab([1, 0, 0]))\n", " .spin(axis=[-1, 0, 0], angular_velocity=angular_vel)\n", " .move_by([-move_delta, 0, 0], 0, t_end)\n", " )\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": "07068865-0a55-412a-b9e9-663bf796133b", "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 add air friction\n", "(\n", " session.param.set(\"frames\", 450)\n", " .set(\"dt\", 1 / 120)\n", " .set(\"gravity\", [0, 0, 0])\n", " .set(\"isotropic-air-friction\", 1e-3)\n", " .set(\"friction-mode\", \"max\")\n", ")\n", "\n", "# build this session\n", "session = session.build()" ] }, { "cell_type": "code", "execution_count": null, "id": "3c934643-8d74-4e04-ba83-ae4cee68e108", "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": "685409d0-381d-46ed-b10d-d2245bf56698", "metadata": {}, "outputs": [], "source": [ "# create an animation from the simulation results\n", "session.animate()" ] }, { "cell_type": "code", "execution_count": null, "id": "e9fb98a8-1892-487c-a3de-2783f06eb87c", "metadata": {}, "outputs": [], "source": [ "# export the animation to file\n", "session.export.animation()" ] }, { "cell_type": "code", "execution_count": null, "id": "d542a9ed-2e99-410a-9330-37c1e0af8270", "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 }