New route prefab (routes.rhai): cars, buses, motorbikes, cyclists, people, deer or elephants move along waypoints, round a loop or there and back. Each way keeps to the left of the line (or right), corners are rounded into pieces of at most 45 degrees; there-and-back routes become loops with U-turns, so the two directions never share a strip. Engine: Prim::Travel / sprite.begin_travel / script travel(a, b, dur, windows) ... end(): the mover is drawn at a piece's start and slides to its end while it is on that piece, hidden otherwise. The renderer writes keyframes per piece (named by a hash of their content) into one <style> per view. Each piece is its own sprite with the lane strip as its sort extent (ext_reset), mitred at the corners, so movers pass in front of and behind trees, lamps and houses. Movers that look alike and never meet on a piece share one drawing. iso-travel pauses (not hides) in still, reduced-motion and ghosted views. Studio: meta shape "path" draws point by point (snapping like runs) and saves one object with pts; ending on the first point closes the loop. MCP: pts in the object schema, route guidance. Demos: streets gets traffic round the block, a bus, motorbikes, cyclists and strolling people (replacing the one-axis cars); country gets cars on the lane. Tests: Rust 79 (each mover on exactly one piece at every moment, in every rotation), Studio 125, web 99.
This commit is contained in:
1 parent
ccfd619a99
commit
5ec164a633
20 files changed
+698
-23
No files matched your search
@@ -577,6 +577,35 @@ with sync_playwright() as p:
|
||||
pg.wait_for_timeout(700)
|
||||
check(len(doc()["objects"]) == n5, "two undos take the animals back")
|
||||
|
||||
# a route drawn point by point: four corners and the first again make a loop of cars; two
|
||||
# points and Enter a there-and-back walk. Each is one object whose pts are the points
|
||||
pg.locator('.st-tile[data-type="route"]').click()
|
||||
hint = pg.locator("#hint").inner_text()
|
||||
for x, y in [(42, 14), (56, 14), (56, 24), (42, 24), (42, 14)]:
|
||||
pg.mouse.click(*at(x, y))
|
||||
pg.wait_for_timeout(150)
|
||||
pg.wait_for_timeout(1000)
|
||||
loop = doc()["objects"][-1]
|
||||
trav = pg.evaluate(f"() => document.querySelectorAll(`[data-obj='{loop['id']}'] .iso-travel`).length")
|
||||
rows = pg.evaluate(f"() => Object.fromEntries((__studio.view.info('{loop['id']}') || {{rows: []}}).rows)")
|
||||
check("point after point" in hint and loop["type"] == "route" and loop["pts"] == [[-7, -5], [7, -5], [7, 5], [-7, 5]] and loop["x"] == 49 and loop["y"] == 19
|
||||
and loop["props"].get("closed") is True and trav > 4 and rows.get("Route") == "loop, 48 m",
|
||||
f"a route drawn point by point loops: {loop.get('pts')} at ({loop['x']}, {loop['y']}), {trav} pieces, {rows}")
|
||||
pg.locator('.st-tile[data-type="route"]').click()
|
||||
for x, y in [(42, 28), (56, 28)]:
|
||||
pg.mouse.click(*at(x, y))
|
||||
pg.wait_for_timeout(150)
|
||||
pg.keyboard.press("Enter")
|
||||
pg.wait_for_timeout(1000)
|
||||
walk = doc()["objects"][-1]
|
||||
check(walk["type"] == "route" and walk["pts"] == [[-7, 0], [7, 0]] and walk["props"].get("closed") is False,
|
||||
f"two points and Enter: there and back {walk.get('pts')}")
|
||||
pg.screenshot(path=str(OUT / "studio-route.jpg"), type="jpeg", quality=80)
|
||||
for _ in range(2):
|
||||
pg.locator("#undo").click()
|
||||
pg.wait_for_timeout(700)
|
||||
check(len(doc()["objects"]) == n5, "two undos take the routes back")
|
||||
|
||||
# an agent edits over MCP: the open Studio follows
|
||||
init = mcp({"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "test", "version": "0"}}})
|
||||
check(init["result"]["serverInfo"]["name"] == "isokit", "MCP initialize")
|
||||
|
||||
Reference in new issue
Block a user