Robot arms pass goods from belt to belt; native beam primitive
Deploy web Studio / deploy (push) Successful in 1m3s
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:
1 parent
18dd324543
commit
f49798583e
14 files changed
+460
-31
No files matched your search
@@ -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();
|
||||
|
||||
@@ -388,6 +388,63 @@ impl Sprite {
|
||||
})
|
||||
}
|
||||
|
||||
/// A square bar `w` wide from `a` to `b` (robot arms, trusses): six faces,
|
||||
/// culled and shaded by their normals like [`Sprite::face`]. `n` is a
|
||||
/// side direction across the bar (any direction not along it).
|
||||
pub fn beam(&mut self, a: P3, b: P3, n: P3, w: f64, mat: &str) -> &mut Self {
|
||||
let sub = |p: P3, q: P3| [p[0] - q[0], p[1] - q[1], p[2] - q[2]];
|
||||
let cross = |p: P3, q: P3| {
|
||||
[
|
||||
p[1] * q[2] - p[2] * q[1],
|
||||
p[2] * q[0] - p[0] * q[2],
|
||||
p[0] * q[1] - p[1] * q[0],
|
||||
]
|
||||
};
|
||||
let unit = |p: P3| {
|
||||
let l = (p[0] * p[0] + p[1] * p[1] + p[2] * p[2]).sqrt();
|
||||
if l < 1e-12 {
|
||||
None
|
||||
} else {
|
||||
Some([p[0] / l, p[1] / l, p[2] / l])
|
||||
}
|
||||
};
|
||||
let (Some(u), Some(v0)) = (unit(sub(b, a)), unit(cross(sub(b, a), n))) else {
|
||||
return self;
|
||||
};
|
||||
// (u, n, v): a right-handed square frame, so every face below winds
|
||||
// counter-clockwise seen from outside
|
||||
let n = cross(v0, u);
|
||||
let h = w / 2.0;
|
||||
let c = |e: P3, sn: f64, sv: f64| {
|
||||
[
|
||||
e[0] + (n[0] * sn + v0[0] * sv) * h,
|
||||
e[1] + (n[1] * sn + v0[1] * sv) * h,
|
||||
e[2] + (n[2] * sn + v0[2] * sv) * h,
|
||||
]
|
||||
};
|
||||
let k = [
|
||||
c(a, 1.0, 1.0),
|
||||
c(a, -1.0, 1.0),
|
||||
c(a, -1.0, -1.0),
|
||||
c(a, 1.0, -1.0),
|
||||
c(b, 1.0, 1.0),
|
||||
c(b, -1.0, 1.0),
|
||||
c(b, -1.0, -1.0),
|
||||
c(b, 1.0, -1.0),
|
||||
];
|
||||
for f in [
|
||||
[1, 5, 4, 0],
|
||||
[3, 7, 6, 2],
|
||||
[3, 0, 4, 7],
|
||||
[1, 2, 6, 5],
|
||||
[3, 2, 1, 0],
|
||||
[4, 5, 6, 7],
|
||||
] {
|
||||
self.face(f.iter().map(|&i| k[i]).collect(), mat, None, false);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// 3D polygons as one path with a fixed class.
|
||||
pub fn poly(&mut self, polys: Vec<Vec<P3>>, cls: &str, id: Option<String>) -> &mut Self {
|
||||
let polys: Vec<Vec<P3>> = polys
|
||||
|
||||
Reference in new issue
Block a user