forked from 1b-agent/fieldcraft-upstream
demo video: swing the deformation from 0, close on a slow orbit
The results start on the undeformed shape (scale 0) and the deformation swings, eased, from 0 to x150, back and out again, one picture per frame. The video ends on a 60-frame camera orbit round the deformed beam instead of cuts between views. The results camera is drawn back so the contour legend leaves the beam clear, and each stage holds 2-3 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
1 parent
30812250d3
commit
8a8ed75103
2 files changed
+44
-20
No files matched your search
@@ -25,10 +25,13 @@ What it does:
|
||||
speaks MCP (JSON-RPC over stdio) to it. Every step is an MCP tool call: `open` the design,
|
||||
`mesh` (`hex_sweep`, 4 mm), `batch` (steel, a solid section, the root clamp, a kinematic
|
||||
coupling of the free end at x = 200 mm to a reference point loaded with 1000 N down, −z), `check_model`, `solve`
|
||||
(`static`), and `show_results` for the von Mises contours and the deformed shape, its `scale`
|
||||
stepped from 1 to 150 and back. Every picture is the MCP `screenshot` tool with
|
||||
`source: window` (the whole window drawn offscreen, `ui.capture`), turned to the `iso` or
|
||||
`front` view by its `view` argument.
|
||||
(`static`), and `show_results` for the von Mises contours on the undeformed shape
|
||||
(`scale: 0`) and then the deformed shape, its `scale` swung (eased) from 0 to 150, back to 0
|
||||
and out to 150 again. Every picture is the MCP `screenshot` tool with `source: window` (the
|
||||
whole window drawn offscreen, `ui.capture`): the setup in the `iso` view, the results through
|
||||
its `camera` argument (the iso orientation drawn back to 420 mm so the contour legend leaves
|
||||
the beam clear), and to close, a slow orbit of 60 pictures, one `camera` angle each, from the
|
||||
iso view half way round the beam. Each stage holds for 2 to 3 seconds.
|
||||
4. Captions each stage with the MCP tool it calls and what it does (`open · tests/corpus/…`,
|
||||
`mesh · hex sweep 4 mm`, `solve · static`, …) in a band under the window. `caption.py`
|
||||
draws the bars in the standard library: a minimal TrueType reader (`cmap` format 4, `loca`,
|
||||
|
||||
+37
-16
@@ -12,6 +12,7 @@ Python standard library only; ffmpeg encodes the frames.
|
||||
import argparse
|
||||
import base64
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
@@ -44,11 +45,19 @@ STEP = "Load"
|
||||
|
||||
# Frames per second of the video; each stage holds its picture for a number of frames.
|
||||
FPS = 10
|
||||
HOLD = 14 # a workflow stage (1.4 s)
|
||||
HOLD_SHORT = 10 # one of the model definition steps
|
||||
HOLD_LONG = 20 # the stress result
|
||||
SWING = 12 # pictures from undeformed to the largest exaggeration
|
||||
HOLD = 25 # a workflow stage (2.5 s, long enough to read its caption and the browser)
|
||||
HOLD_SHORT = 20 # one of the model definition steps
|
||||
HOLD_LONG = 30 # the stress result
|
||||
SWING = 15 # pictures from undeformed to the largest exaggeration, eased
|
||||
MAX_SCALE = 150.0 # the largest displacement (0.18 mm) drawn about 26 mm on the 200 mm beam
|
||||
# The results camera: the iso orientation (yaw -45, pitch 35.26 degrees), drawn back from the
|
||||
# fitted distance (about 300 mm for this beam) so the contour legend over the viewport's corner
|
||||
# leaves the beam clear. The closing orbit turns it round the beam, one picture per frame.
|
||||
ISO = (-45.0, 35.26)
|
||||
RESULTS_DISTANCE_MM = 420.0
|
||||
ORBIT = 60
|
||||
ORBIT_YAW = (ISO[0], -165.0)
|
||||
ORBIT_PITCH = (ISO[1], 20.0)
|
||||
|
||||
# The captured window, in points (the app's own pixels-per-point scale on top); the video's size.
|
||||
WINDOW = (1280, 800)
|
||||
@@ -253,6 +262,11 @@ def against_beam_theory(result):
|
||||
return result
|
||||
|
||||
|
||||
def ease(i, n):
|
||||
"""Step i of n of a cosine ease, 0 to 1: slow out, slow in."""
|
||||
return (1 - math.cos(math.pi * i / n)) / 2
|
||||
|
||||
|
||||
def batch(mcp, commands):
|
||||
reply = mcp.call("batch", {"commands": commands})
|
||||
if not reply.get("ok"):
|
||||
@@ -311,25 +325,32 @@ def analyse(mcp, film, design, work):
|
||||
log(f"solve: {solve.get('equations')} equations")
|
||||
film.shoot(HOLD_SHORT)
|
||||
|
||||
# 5. Results: the tip deflection, then von Mises contours on the true (scale 1) shape.
|
||||
# 5. Results: the tip deflection, then von Mises contours on the undeformed shape.
|
||||
disp = mcp.call("show_results", {"step": STEP, "field": "displacement"})
|
||||
tip = disp["contour"]["range"]["max"]["value"]
|
||||
root_stress = root_bending_stress(mcp, work)
|
||||
film.say("show_results", "von Mises")
|
||||
vm = mcp.call("show_results", {"step": STEP, "field": "von_mises", "bands": 12, "deformed": True, "scale": 1})
|
||||
vm = mcp.call("show_results", {"step": STEP, "field": "von_mises", "bands": 12, "deformed": True, "scale": 0})
|
||||
peak = vm["contour"]["range"]["max"]["value"]
|
||||
log(f"tip deflection {tip:.4f} mm, peak von Mises {peak:.1f} MPa")
|
||||
film.shoot(HOLD_LONG, view="iso")
|
||||
film.shoot(HOLD_LONG, camera={"yaw": ISO[0], "pitch": ISO[1], "distance": RESULTS_DISTANCE_MM})
|
||||
|
||||
# 6. The deformed shape, swung from the true shape to an exaggeration and back, in the iso
|
||||
# view and then from the side, ending held at the exaggeration.
|
||||
film.say("show_results", f"deformed, up to \u00d7{MAX_SCALE:g}")
|
||||
ramp = [max(1.0, MAX_SCALE * i / SWING) for i in range(SWING + 1)]
|
||||
for view, scales in (("iso", ramp[1:] + ramp[-2::-1]), ("front", ramp[1:])):
|
||||
for i, scale in enumerate(scales):
|
||||
mcp.call("show_results", {"step": STEP, "deformed": True, "scale": scale})
|
||||
film.shoot(1, view=view if i == 0 else None)
|
||||
film.shoot(HOLD_LONG)
|
||||
# 6. The deformed shape, swung from undeformed to the exaggeration and back, then out again.
|
||||
eased = [MAX_SCALE * ease(i, SWING) for i in range(1, SWING + 1)]
|
||||
for scale in eased + eased[-2::-1] + [0.0] + eased:
|
||||
film.say("show_results", f"deformed \u00d7{scale:.0f}")
|
||||
mcp.call("show_results", {"step": STEP, "deformed": True, "scale": scale})
|
||||
film.shoot(1)
|
||||
film.hold(HOLD_SHORT)
|
||||
|
||||
# 7. A slow orbit round the deformed beam, one `screenshot` camera angle per frame.
|
||||
film.say("screenshot", "camera orbit")
|
||||
for i in range(1, ORBIT + 1):
|
||||
t = ease(i, ORBIT)
|
||||
yaw = ORBIT_YAW[0] + t * (ORBIT_YAW[1] - ORBIT_YAW[0])
|
||||
pitch = ORBIT_PITCH[0] + t * (ORBIT_PITCH[1] - ORBIT_PITCH[0])
|
||||
film.shoot(1, camera={"yaw": yaw, "pitch": pitch})
|
||||
film.hold(HOLD)
|
||||
return against_beam_theory({"tip_deflection_mm": tip, "root_stress_mpa": root_stress, "peak_von_mises_mpa": peak, "elements": mesh["mesh"]["elements"], "nodes": mesh["mesh"]["nodes"]})
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user