Diagonals: streets, walls, fences, hedges and house walls at any angle
Deploy web Studio / deploy (push) Successful in 1m5s

Object frames turn by any angle (quarter turns stay exact, so scenes along
the axes render byte for byte as before); a box in a turned frame is a
prism with its own corners. Rotation is kept as given (normalised to
0..360), no longer snapped to quarter turns.

Depth sort: sprites whose plan outline is not their box carry their convex
outline; when boxes overlap, an outline edge that separates two sprites
orders them. A long diagonal wall sorts right against both sides.

Joints (line_meet, end_cut, cut_box in util.rhai): where a piece off the
axes is involved, corners are mitred and T-junctions slanted; fences put a
corner post or pillar in its own sprite and mitre stone bases; Y joints let
the lowest id run through. Streets: every end part is a cut e + k*w, so
side streets come in at any angle (slanted sidewalk gaps), two streets meet
in a mitre with seam patches; fixes a curve's outer sidewalk that was
skipped when its kerb and back lines had different point counts.

Studio: runs and drags keep to x/y or snap to 45 degree diagonals, Ctrl
gives any angle; a road run turning 45 degrees gets a 45 degree curve;
Rotation is a number box. Streets demo: a diagonal lane with a hedge.
MCP guidance, SCRIPTING/README/ROADMAP.

Tests: Rust 76, Studio and web checks green.
This commit is contained in:
grabowski committed 2026-10-25 13:22:59 +07:00
1 parent c91d63663b
commit 96d472625f
18 files changed
+1051 -178

No files matched your search

+52
View File
@@ -503,6 +503,58 @@ with sync_playwright() as p:
pg.wait_for_timeout(900)
check(len(doc()["objects"]) == n4 and kerbs() == k0, "one undo takes the road run back")
# diagonals, east of the site: a fence run snaps its middle leg to 45 degrees, Ctrl frees the angle of a
# dragged road, a road run turning 45 degrees gets a 45-degree curve, the rotation can be typed
pg.evaluate("__studio.view.focus([48, 8, 0], 1.8)")
pg.wait_for_timeout(400)
n5 = len(doc()["objects"])
pg.locator('.st-tile[data-type="fence"]').click()
for x, y in [(38, -10), (44, -10.1), (49.7, -4.2), (50.1, 4)]:
pg.mouse.click(*at(x, y))
pg.wait_for_timeout(150)
check("diagonals at 45°" in pg.locator("#hint").inner_text(), "a run says how diagonals snap")
pg.keyboard.press("Enter")
pg.wait_for_timeout(1000)
got = sorted((o["x"], o["y"], o.get("rot", 0), o["props"]["w"]) for o in doc()["objects"][n5:])
check(got == [(41, -10, 0, 6), (47, -7, 45, 8.485), (50, 0, 90, 8)], f"a fence run takes a 45-degree leg: {got}")
pg.locator('.st-tile[data-type="road"]').click()
pg.keyboard.down("Control")
pg.mouse.move(*at(38, 12))
pg.mouse.down()
for i in range(1, 11):
pg.mouse.move(*at(38 + 1.2 * i, 12 + 0.6 * i))
pg.wait_for_timeout(20)
pg.mouse.up()
pg.keyboard.up("Control")
pg.wait_for_timeout(1000)
free = doc()["objects"][-1]
check(free["type"] == "road" and abs(free["rot"] - 26.5651) < 1e-6 and abs(free["props"]["w"] - 13.416) < 1e-6,
f"Ctrl draws a road at any angle: rot {free.get('rot')}, w {free['props'].get('w')}")
n6 = len(doc()["objects"])
pg.locator('.st-tile[data-type="road"]').click()
for x, y in [(38, 24), (52, 24), (62.2, 34.1)]:
pg.mouse.click(*at(x, y))
pg.wait_for_timeout(150)
pg.keyboard.press("Enter")
pg.wait_for_timeout(1000)
got = sorted((o["type"], o["x"], o["y"], o.get("rot", 0), o["props"].get("w", o["props"].get("r")), o["props"].get("angle")) for o in doc()["objects"][n6:])
want = sorted([("road", 42.929, 24, 0, 9.858, None), ("road", 58.464, 30.464, 45, 10, None), ("road_curve", 47.858, 24, 0, 10, 45)])
check(got == want, f"a road run turning 45 degrees gets a 45-degree curve: {got}")
pg.evaluate(f"__studio.select('{free['id']}')")
pg.wait_for_timeout(300)
rin = pg.locator('#right input[data-f="rot"]')
rin.fill("30")
rin.press("Enter")
pg.wait_for_timeout(800)
check(obj(free["id"])["rot"] == 30, f"the rotation can be typed: {obj(free['id'])['rot']}")
pg.evaluate("__studio.select(null)")
pg.wait_for_timeout(300)
pg.screenshot(path=str(OUT / "studio-diagonals.jpg"), type="jpeg", quality=80)
for _ in range(4):
pg.locator("#undo").click()
pg.wait_for_timeout(700)
check(len(doc()["objects"]) == n5, "four undos take the diagonals 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")