Robot arms pass goods from belt to belt; native beam primitive
Deploy web Studio / deploy (push) Successful in 1m3s

- Robot arm (category Industry): picks goods off the end of the nearest conveyor within its reach
  and puts them on the start of another conveyor within reach, or on a pallet a quarter turn on
  (pick / put: auto or a side). One move per item coming in (cycle 0) or a set cycle; waits while
  the line before it stands. The belt it feeds carries its goods at its pace and starts its loop
  when they land (feeds carry a time offset `ph`), so arm and belts stay in step.
- Drawn as a 16-pose flipbook: two-link inverse kinematics per pose, each pose shown for a
  sixteenth of the cycle by a stepped CSS animation (iso-flip; pose 0 alone when unanimated).
  Poses pointing away from the viewer are drawn before the base, the others after it.
- New sprite method beam(a, b, n, w, mat): a square bar between two 3D points, six faces culled
  and shaded like face(). Native, so the factory demo renders in 4.7 ms instead of 18.8 ms.
- Factory demo: a palletiser at the end of the carton line, a loading robot passing drums from the
  incline onto the loading belt into the truck. MCP instructions on robot arms.

Checks: 70 Rust tests (arms and belts in step, waiting arm, beam faces from every side),
Studio 109/109, web 90/90.
This commit is contained in:
grabowski committed 2026-10-24 22:05:46 +07:00
1 parent 18dd324543
commit f49798583e
14 files changed
+460 -31

No files matched your search

+29
View File
@@ -870,6 +870,35 @@ mod tests {
}
}
#[test]
fn beams_show_three_faces_from_any_side() {
for (a, b, n) in [
([0.0, 0.0, 1.0], [4.0, 0.0, 1.0], [0.0, 1.0, 0.0]),
([0.0, 0.0, 1.0], [0.0, -4.0, 1.0], [1.0, 0.0, 0.0]),
([0.0, 0.0, 0.0], [0.0, 0.0, 3.0], [1.0, 0.0, 0.0]),
([0.0, 0.0, 0.0], [2.0, 1.0, 3.0], [-1.0, 2.0, 0.0]),
] {
for rot in 0..4u8 {
let mut sc = Scene::new();
let mut s = Sprite::new("arm", "");
s.beam(a, b, n, 0.4, "iso-robot");
sc.add_sprite("main", s);
let html = render(&sc, rot, &RenderOpts::default()).html;
// three faces; two when one side is seen edge-on (an oblique bar)
let faces = html.matches("iso-robot ").count();
let oblique = a[0] != b[0] && a[2] != b[2];
assert!(
faces == 3 || (oblique && faces == 2),
"{a:?}->{b:?} rot {rot}: {faces} faces"
);
assert!(
html.contains("iso-robot iso-t"),
"{a:?}->{b:?} rot {rot}: no top"
);
}
}
}
#[test]
fn faces_cull_and_shade_by_normal() {
let mut sc = Scene::new();