{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "7f82bc10-e64b-49c9-ab05-6e9763ae200c", "metadata": {}, "outputs": [], "source": [ "# File: yarn.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": "b0470560-66f1-4db7-a706-7a04fa5a4865", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from frontend import App\n", "\n", "\n", "# function to generate interwoven yarn strands with sinusoidal pattern\n", "def make_strands(offset: float, width: float, res: float):\n", "\n", " offset = 1.3 * offset\n", " n_vertical_yarns = int(res * 0.28 / offset)\n", " n_points_per_seg = int(res * 20)\n", " n_segs = int(width * n_vertical_yarns)\n", " n_points = n_segs * n_points_per_seg\n", " dx = 1.0 / (n_points - 1)\n", " strands = []\n", "\n", " # create vertical yarn strands with sinusoidal weaving pattern\n", " for k in range(n_vertical_yarns):\n", " y_base = k / (n_vertical_yarns - 1) - 0.5 if n_vertical_yarns > 1 else 0.0\n", " z_base = 0.0\n", " j_vals = np.arange(n_points)\n", " x_mod = (j_vals % n_points_per_seg) / n_points_per_seg\n", " t = 2.0 * np.pi * x_mod\n", " x_disp = -width * (0.5 / n_segs) * np.sin(2.0 * t)\n", " y_disp = (0.85 / n_vertical_yarns) * np.sin(t)\n", " z_disp = 0.75 * offset * np.cos(2.0 * t)\n", " x_coord = width * (2.0 * dx * j_vals - 1.0) + x_disp\n", " y_coord = y_base + y_disp\n", " z_coord = z_base + z_disp\n", " xyz = np.zeros((n_points, 3))\n", " xyz[:, 0] = x_coord\n", " xyz[:, 1] = y_coord\n", " xyz[:, 2] = z_coord\n", " strands.append((xyz, False))\n", "\n", " # create horizontal circular yarn loops at edges\n", " for pos_index in range(2):\n", " for k in range(n_segs - 1):\n", " dx_local = 2.0 * width / n_segs\n", " y_base = 0.5 + 0.25 * dx_local if pos_index == 0 else -0.5 - 0.25 * dx_local\n", " z_base = 0.15 * dx_local\n", " x_center = dx_local * (k + 0.77) - width\n", " if pos_index == 1:\n", " x_center += 0.5 * dx_local\n", " j_vals = np.arange(n_points_per_seg)\n", " t = 2.0 * np.pi * j_vals / n_points_per_seg\n", " r = 0.78 * width / n_segs\n", " z_val = r * np.cos(t)\n", " theta = 0.25 * np.pi\n", " x_coord = x_center + r * np.sin(t)\n", " if pos_index == 0:\n", " y_coord = y_base + z_val * np.sin(theta)\n", " else:\n", " y_coord = y_base - z_val * np.sin(theta)\n", " z_coord = z_base + z_val * np.cos(theta)\n", " xyz = np.zeros((n_points_per_seg, 3))\n", " xyz[:, 0] = x_coord\n", " xyz[:, 1] = y_coord\n", " xyz[:, 2] = z_coord\n", " strands.append((xyz, True))\n", "\n", " return strands\n", "\n", "\n", "# create an app\n", "app = App.create(\"yarn\")\n", "\n", "# create a scene\n", "scene = app.scene.create()\n", "\n", "# generate and add all yarn strands\n", "for k, (V, closed) in enumerate(make_strands(4e-3, 0.5, 1.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 yarn strands\n", " (\n", " obj.param.set(\"bend\", 0.0)\n", " .set(\"young-mod\", 1e5)\n", " .set(\"contact-gap\", 1e-3)\n", " .set(\"contact-offset\", 2.3e-3)\n", " .set(\"length-factor\", 0.85)\n", " )\n", " # pull open-ended strands from both ends\n", " if not closed:\n", " move_delta, t_end = -5, 10\n", " obj.pin(obj.grab([-1, 0, 0])).move_by([move_delta, 0, 0], 0, t_end)\n", " obj.pin(obj.grab([1, 0, 0])).move_by([-move_delta, 0, 0], 0, t_end)\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": "f15496ae-0ebc-495f-b0f7-4625915db4e4", "metadata": {}, "outputs": [], "source": [ "# create a new session with the compiled scene\n", "session = app.session.create(scene)\n", "\n", "# set session parameters - disable gravity\n", "session.param.set(\"frames\", 120).set(\"dt\", 1e-2).set(\"gravity\", [0, 0, 0]).set(\"friction-mode\", \"max\")\n", "\n", "# build this session\n", "session = session.build()" ] }, { "cell_type": "code", "execution_count": null, "id": "5854e6b9-a1db-457d-bb06-67775834fecf", "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": "934c72fb-1d70-4265-897f-39e956c1fbee", "metadata": {}, "outputs": [], "source": [ "# create an animation from the simulation results\n", "session.animate()" ] }, { "cell_type": "code", "execution_count": null, "id": "4b4f719f-49a5-4dea-80dd-a5c4c12e35c5", "metadata": {}, "outputs": [], "source": [ "# export the animation to file\n", "session.export.animation()" ] }, { "cell_type": "code", "execution_count": null, "id": "749a460a-23af-4304-9fa1-66fb17245077", "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 }