Diagonals: streets, walls, fences, hedges and house walls at any angle
Deploy web Studio / deploy (push) Successful in 1m5s
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:
1 parent
c91d63663b
commit
96d472625f
18 files changed
+1051
-178
No files matched your search
@@ -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")
|
||||
|
||||
Reference in new issue
Block a user