Conveyors and producers: goods flow from presses down the belts
Deploy web Studio / deploy (push) Successful in 1m4s

- Producer (category Industry): a press making cartons, crates, drums, sacks or parts at a set
  rate per minute; its ram goes down once per item, a green / red light shows running / stopped.
- Conveyor: drawn the way it runs (new prefab meta `directed`: the Studio turns the piece from
  where the drag starts), belt width, height, rise (inclines), speed, goods and spacing; legs,
  rails and a drive motor. Corners join without overlap (the receiving belt runs through, the
  feeding one stops at its side).
- A conveyor starting on a producer's output side carries its goods at its rate; one starting at
  another's end carries on with what that one carries. The gap is speed / rate. A stopped press or
  belt stops the goods after it. Tooltips show speed, rate and source.
- The goods ride a group moving one gap per loop (no reordering), first fading in, last out.
- Script API: world_of(o, u, v), info_row(key, value); objects_on falls back to half of p.d.
- Theme: industry colours, LED colours (the marker's LED had none).
- examples/factory + Factory demo in the web Studio; MCP instructions on production lines.

Checks: 68 Rust tests (conveyor joints, feed and spacing, every view of the factory demo),
Studio 108/108 (draw direction, feed from a producer, rate change), web 89/89.
This commit is contained in:
grabowski committed 2026-10-24 21:46:10 +07:00
1 parent a202283a60
commit 18dd324543
20 files changed
+617 -18

No files matched your search

+42
View File
@@ -345,6 +345,48 @@ with sync_playwright() as p:
pg.wait_for_timeout(600)
pg.evaluate("__studio.select(null)")
# a conveyor runs the way it was drawn; started at a producer's output it carries what the producer
# makes, spaced by its rate (changed in the producer's properties) and the belt's speed
pg.evaluate("__studio.select(null)")
pg.evaluate("__studio.view.focus([25, -4.5, 0], 3)")
pg.wait_for_timeout(400)
n4 = len(doc()["objects"])
pg.locator('.st-tile[data-type="conveyor"]').click()
a, b2 = at(27, -4.5), at(21, -4.5)
pg.mouse.move(*a)
pg.mouse.down()
for i in range(1, 11):
pg.mouse.move(a[0] + (b2[0] - a[0]) * i / 10, a[1] + (b2[1] - a[1]) * i / 10)
pg.wait_for_timeout(15)
check("running from where you started" in pg.locator("#hint").inner_text(), "the conveyor preview says which way it runs")
pg.mouse.up()
pg.wait_for_timeout(800)
cv = doc()["objects"][-1]
check(cv["type"] == "conveyor" and cv.get("rot") == 180 and abs(cv["x"] - 24) < 1e-6 and abs(cv["props"]["w"] - 6) < 1e-6,
f"a conveyor dragged right to left runs that way (rot {cv.get('rot')}, x {cv['x']})")
pg.evaluate("__studio.op({op: 'add', type: 'producer', x: 28.6, y: -4.5, rot: 180, props: {rate: 20}})")
pg.wait_for_timeout(900)
pid = doc()["objects"][-1]["id"]
rows = lambda: dict(pg.evaluate(f"(__studio.view.info('{cv['id']}') || {{}}).rows || []"))
check(f"(from {pid})" in rows().get("Carries", "") and "20 cartons a minute, 1.5 m apart" in rows().get("Carries", ""),
"the belt carries what the producer at its start makes: " + rows().get("Carries", "?"))
check(pg.evaluate(f"document.querySelectorAll('.iso-svg [data-obj=\"{cv['id']}\"] .iso-ride').length") == 1 and
pg.evaluate(f"document.querySelectorAll('.iso-svg [data-obj=\"{pid}\"] .iso-ram').length") == 1,
"goods ride the belt, the press's ram goes up and down")
pg.locator(f'#objects li[data-id="{pid}"]').click()
pg.wait_for_timeout(300)
pg.locator('#right input[type="number"][data-prop="rate"]').fill("30")
pg.locator('#right input[type="number"][data-prop="rate"]').press("Enter")
pg.wait_for_timeout(900)
check("30 cartons a minute, 1 m apart" in rows().get("Carries", ""), "a faster producer packs the belt closer: " + rows().get("Carries", "?"))
pg.screenshot(path=str(OUT / "studio-conveyor.jpg"), type="jpeg", quality=80)
for _ in range(3):
pg.locator("#undo").click()
pg.wait_for_timeout(500)
check(len(doc()["objects"]) == n4, "undo takes the producer and the belt back")
pg.evaluate("__studio.select(null)")
pg.wait_for_timeout(300)
# performance: animations step about ten times a second by default (no CSS animation runs on its
# own), the toolbar button switches smooth / off / light; a pan moves the drawing as one layer and
# draws it again (viewBox) only when the drag ends