Wildlife and nature: animals, beehives, bushes, flowers, rocks, logs
Deploy web Studio / deploy (push) Successful in 1m5s

New stdlib module wildlife.rhai:
- Animals (new palette category): Deer (stag with antlers, does, fawns),
  Wild boar, Fox, Elephant (bull with tusks, cow, calf), Rabbits. Low-poly
  solids from faces (loft, tube), parts drawn back to front by depth, faces
  edged in their own shade (class beast). Herds scatter round a spot, each
  animal its own sprite facing about the object's rotation. Grazing
  animals switch between two head poses (iso-pose2 groups), elephants curl
  their trunks, rabbits hop.
- Birds and Butterflies as flocks: swim() takes a shape (bird, fly, bee);
  wings flap out of step. Fish output unchanged.
- Beehives on stands with supers, lids, entrances and bees flying round;
  Bush (round, flowering, berries); Flowers (bed in rows or meadow); Rocks;
  Log or stump. g-meadow and g-soil ground materials.
- polyline() takes 3D points as documented (antlers).

Theme colours for dark and light, viewer CSS for poses, flapping, hopping.
examples/country: elephants in a paddock with a cut corner, deer at a
forest edge, boar, fox, rabbits, an apiary by a meadow, a pond, a lane,
fence and hedgerow at angles; Country demo in the web Studio and deploy.
MCP guidance, README, SCRIPTING, ROADMAP.

Tests: Rust 78, Studio 122; existing examples draw the same (only the
embedded theme CSS grew).
This commit is contained in:
grabowski committed 2026-10-25 13:57:23 +07:00
1 parent 96d472625f
commit e840d36000
22 files changed
+1211 -16

No files matched your search

+1
View File
@@ -150,6 +150,7 @@ def main():
(web / "house.json").write_text(json.dumps(demo_files("house"), ensure_ascii=False), encoding="utf-8")
(web / "factory.json").write_text(json.dumps(demo_files("factory"), ensure_ascii=False), encoding="utf-8")
(web / "streets.json").write_text(json.dumps(demo_files("streets"), ensure_ascii=False), encoding="utf-8")
(web / "country.json").write_text(json.dumps(demo_files("country"), ensure_ascii=False), encoding="utf-8")
h = hashlib.sha256()
for p in sorted((out / "assets").rglob("*")):
+22
View File
@@ -555,6 +555,28 @@ with sync_playwright() as p:
pg.wait_for_timeout(700)
check(len(doc()["objects"]) == n5, "four undos take the diagonals back")
# animals and an apiary from the palette: a deer herd grazes, the hives get bees
pal = pg.evaluate("() => [...document.querySelectorAll('.st-cat')].map(e => e.dataset.cat)")
check("Animals" in pal, "the palette has an Animals category")
pg.locator('.st-tile[data-type="deer"]').click()
pg.mouse.click(*at(44, 28))
pg.wait_for_timeout(900)
deer = doc()["objects"][-1]
pg.locator('.st-tile[data-type="beehive"]').click()
pg.mouse.click(*at(52, 18))
pg.wait_for_timeout(900)
hives = doc()["objects"][-1]
grazing = pg.evaluate(f"() => document.querySelectorAll('[data-obj=\"{deer['id']}\"] .iso-pose2.iso-pose0').length")
bees = pg.evaluate(f"() => document.querySelectorAll('[data-obj=\"{hives['id']}\"] .iso-bee').length")
herd = pg.evaluate(f"() => Object.fromEntries((__studio.view.info('{deer['id']}') || {{rows: []}}).rows)['Herd'] || ''")
check(deer["type"] == "deer" and hives["type"] == "beehive" and grazing == 4 and bees == 24 and herd == "1 stag, 2 does, 1 fawn",
f"a deer herd and beehives placed from the palette: {grazing} grazing, {bees} bees, herd {herd}")
pg.screenshot(path=str(OUT / "studio-wildlife.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 animals 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")
+16
View File
@@ -342,6 +342,22 @@ try:
check(meets == "east-ave, park-rd" and ring.startswith("4:"), f"streets know what they meet: cross-st meets {meets}; roundabout arms {ring}")
shot(pg, "web-streets.jpg")
# ---------------------------------------------------------------- the country demo: animals, an apiary, diagonals
pg.locator("#wb-projects").click()
pg.locator("#wb-ncountry").click()
pg.wait_for_load_state()
ready(pg)
pg.wait_for_timeout(500)
nc = len(doc(pg)["objects"])
beasts = pg.evaluate("() => new Set([...document.querySelectorAll('.iso-svg .iso-beast')].map(e => e.closest('[data-obj]')?.dataset.obj)).size")
poses = pg.evaluate("() => document.querySelectorAll('.iso-svg .iso-pose2.iso-pose0').length")
check(title(pg) == "isokit country" and nc > 30 and beasts >= 8 and poses >= 12 and pg.evaluate("() => __studio.S.warnings.length") == 0,
f"Country demo opens the countryside ({nc} objects, {beasts} animal and rock groups, {poses} grazing animals, no warnings)")
herd = pg.evaluate("() => Object.fromEntries((__studio.view.info('elephants') || {rows: []}).rows)['Herd'] || ''")
rot = pg.evaluate("() => __studio.S.doc.objects.find(o => o.id === 'lane-d').rot")
check(herd == "1 bull, 1 cow, 1 calf" and abs(rot - 45) < 1e-6, f"elephants: {herd}; the lane bends at {rot} degrees")
shot(pg, "web-country.jpg")
# ---------------------------------------------------------------- a system asking for less motion
# (Windows: Animation effects off): the view starts still, but the button stays and turns it on
rctx = b.new_context(viewport={"width": 1200, "height": 800}, reduced_motion="reduce")