{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "550b47f1-6e17-4ec6-9831-896b63ef0ce4", "metadata": {}, "outputs": [], "source": [ "# File: cards.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": "2af5eb5a-46aa-498b-bc01-08274d63100a", "metadata": {}, "outputs": [], "source": [ "from frontend import App\n", "\n", "# create an app\n", "app = App.create(\"cards\")\n", "\n", "# create a rectangular card mesh\n", "mesh_res, n_stack, card_height = 8, 8, 0.25\n", "card_width = 0.75 * card_height\n", "V, F = app.mesh.rectangle(mesh_res, card_width, card_height, [0, 0, 1], [0, 1, 0])\n", "app.asset.add.tri(\"card\", V, F)\n", "\n", "# create a sphere mesh to knock down the house of cards\n", "V, F, T = app.mesh.icosphere(r=0.15, subdiv_count=3).tetrahedralize()\n", "app.asset.add.tet(\"sphere\", V, F, T)\n", "\n", "# create a scene\n", "scene = app.scene.create()\n", "angle, gap = 25.0, 1e-3\n", "\n", "\n", "# function to build a row of card pairs with ceiling cards on top\n", "def make_row(n: int, _x: float, y: float, cards: list) -> float:\n", " x, _y, ceil_x = 0, 0, []\n", " for i in range(n):\n", " # add left card tilted at angle\n", " left = scene.add(\"card\").rotate(-angle, \"z\")\n", " left.at(x - left.min(\"x\") + (_x if i == 0 else 0), y - left.min(\"y\"), 0)\n", " if i == 0:\n", " _x = left.max(\"x\")\n", " # add right card tilted in opposite direction\n", " right = scene.add(\"card\").rotate(angle, \"z\")\n", " shift = gap + left.max(\"x\") - right.min(\"x\")\n", " right.at(shift, y - right.min(\"y\"), 0)\n", " if i < n - 1:\n", " ceil_x.append(right.max(\"x\"))\n", " x = right.max(\"x\") + gap\n", " max_y = right.max(\"y\") + gap\n", " cards.extend([left, right])\n", " # add horizontal ceiling cards\n", " for i, x in enumerate(ceil_x):\n", " z = max_y if i % 2 == 0 else max_y + gap\n", " ceil = scene.add(\"card\").rotate(-90, \"z\").at(x, z, 0)\n", " _y = max(_y, ceil.max(\"y\"))\n", " cards.append(ceil)\n", " return _x, _y + gap\n", "\n", "\n", "# build the house of cards pyramid\n", "_x, _y, _cards = -0.75, gap, []\n", "for i in reversed(range(n_stack)):\n", " _x, _y = make_row(i + 1, _x, _y, _cards)\n", "\n", "# set material properties for all cards\n", "for card in _cards:\n", " (\n", " card.param.set(\"contact-gap\", gap)\n", " .set(\"young-mod\", 30000)\n", " .set(\"bend\", 1e6)\n", " .set(\"friction\", 0.5)\n", " )\n", "\n", "# add sphere projectile with initial velocity\n", "scene.add(\"sphere\").at(-2, 1, 0).jitter().velocity(2.3, 0, 0)\n", "\n", "# add invisible floor\n", "scene.add.invisible.wall([0, 0, 0], [0, 1, 0]).param.set(\"friction\", 0.5)\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": "b3f8610c-1392-4d22-9cd1-bf7c14d20ff5", "metadata": {}, "outputs": [], "source": [ "# create a new session with the compiled scene\n", "session = app.session.create(scene)\n", "\n", "# set session parameters\n", "session.param.set(\"dt\", 0.01).set(\"min-newton-steps\", 32).set(\"friction-mode\", \"max\").set(\"frames\", 180)\n", "\n", "# build this session\n", "session = session.build()" ] }, { "cell_type": "code", "execution_count": null, "id": "5f9f0256-3de7-4a01-b05e-fb2b98794c1f", "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": "1fb38b89-3a6b-44fa-8629-6e6c21408fc8", "metadata": {}, "outputs": [], "source": [ "# create an animation from the simulation results\n", "session.animate()" ] }, { "cell_type": "code", "execution_count": null, "id": "472bf8f9-9734-4af8-a3bc-0e1312aeab39", "metadata": {}, "outputs": [], "source": [ "# export the animation to file\n", "session.export.animation()" ] }, { "cell_type": "code", "execution_count": null, "id": "726e97b1-1801-4354-bd37-04b5ad9119e6", "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 }