Barrels are Cylinder bodies that roll on their sides, with exact cylinder contacts in the physics #25

Merged
agra merged 10 commits from task/3934-a-barrel-lying-on-its-side-barely-rolls into master 2026-10-30 16:42:42 +02:00
Owner

Tracker: #3934

The first map's barrels were Convex Hull bodies of Kenney's octagonal barrel. Lying on its side, each facet stopped the roll, so a barrel pushed at 2 m/s stopped within 1.6 m. This PR adds a Cylinder rigid body shape, read from Blender's own Cylinder and colliding as an exact cylinder, and makes the five barrels Cylinder bodies.

What changes

  • Physics (src/physics). A new cylinder shape along local +Y, with a solid cylinder's inertia.
    • Core. A cylinder's core for GJK is the cylinder shrunk by CYLINDER_CORE (2 cm, or a quarter of its least extent), so the cores of a cylinder and what it rests on stay apart and give the normal. Its farthest point along a direction is found in its own frame, so it lies on the cap's plane however near the axis the direction lies.
    • Points. Every contact point is on the exact, sharp-rimmed surface, at its own separation along the normal.
    • Against a flat face (a triangle's or box's face, a bound, or a cylinder's cap), a cylinder meets it by its cap when its axis lies within 45° of the face's normal, and otherwise by the line of its side nearest the face, both clipped to the face.
      • A cap gives its 16 rim points and its rim's deepest point.
      • A cap whose rim rises more than SPECULATIVE from the face keeps the deepest of those, the two nearest it and the farthest. It rests about that point, which runs round the rim as the cylinder rolls on it.
    • A cylinder's cap is a reference face for boxes, spheres, capsules and other cylinders.
    • Against an edge or another side. Where its side meets an edge, a segment or another side, the closest points come from its axis, exactly. GJK's against the core's round side are only as close as its tolerance. Along a parallel edge, capsule or side it gets two points, at the ends of their common part; every other contact is one point.
    • Map triangles. Their wedge rules apply to cylinders as to polytopes. The side's line takes a capsule's rules: its end in the gap past a smooth convex edge is kept, and turning about a sharp edge it gets the far end of the line over the face.
    • Bounds and the plane sweep read the exact surface.
  • Player (src/physics/solids.sx). Movement meets a cylinder body as the 32-sided prism whose faces touch it.
  • Bake. extract.py takes an Active Cylinder: radius half the larger of the X and Y dimensions, half length half the Z dimension, as Blender sizes it.
    • Blender 5.2's simulation scales a Cylinder's radius by the X scale alone and its length by Z (probed by drops). So the extract refuses X and Y scales that differ, and takes an uneven Z.
    • stage.sx and the world file carry shape kind 4.
  • First map. The rigid bodies of its five barrels are Cylinder in first_map.blend, and nothing else in the file changed. first_map_bodies.py and the .md say so. The README lists the shape.
  • The branch merges master ff70931 (#3904's physics bench), after 6c24bd0 (#3931's trunk-slide movement).

Evidence

  • The task's probe on the rebaked first map: both lying barrels roll 5.86 m and 5.77 m in 5 s, at 1.14 and 0.98 m/s, where the cannon ball goes 3.73 m at 0.63 m/s. Past the level clearing, the barrels roll on downhill. On master they stop at 1.55 m and 1.15 m.
  • tests/barrels.sx uses the map's own barrel as baked:
    • On the first map, both lying barrels pushed at 2 m/s roll at least 3 m in 5 s and still roll at least as fast as the cannon ball. Each rolls without slipping: spin × radius matches its speed.
    • On level ground of 2 m cells, undamped, it rolls at 1.3338 m/s (2/3 of the push is 1.3333). It covers 4.044 m in 3 s; slipping then rolling predicts 4.045 m.
    • Released across a 10° slope, it rolls down at 2.274 m/s after 2 s (2/3 g sin 10° × 2 s = 2.271) and keeps gaining speed.
    • Standing on its end, it rests within 0.002 mm of the ground. Pushed at 2 m/s it slides 0.404 m (v²/2μg = 0.408) and stays standing. Tilted 0.15 rad short of its balance it falls back onto its end, and 0.15 rad past it, onto its side.
    • A barrel stands on another, and a rolled barrel pushes a parallel one on.
    • Standing on a 15° incline, its axis along the incline's normal, it moves 1.4 mm (its 1 mm drop included) and sleeps.
    • Set at rest along a ridge (bent 3°, on its line, the world turned 0.37 rad; and bent 2°, 4 mm off it, yawed 0.005 rad), it never ends a tick more than 0.1 mm into the map.
    • Turning at 3 rad/s about a block's sharp top edge onto its top, it never ends a tick more than 0.3 mm in.
    • On its rim, tilted 15°, it never ends a tick more than 3.2 mm into the ground, whether whirled at 8 rad/s so it rolls round the rim or released to fall back onto its end.
    • Run against master's bake in a git archive arm, every rolling check fails: the barrels stop after 1.55 and 1.15 m, and the slope barrel never moves.
  • tests/physics_contacts.sx pins the manifolds:
    • lying, 2 points at the gap at the ends of its side;
    • standing, 4 rim points;
    • tilted between two rim points, the rim's deepest point;
    • on a box, crossed and parallel cylinders, stacked cylinders, a sphere or capsule on the cap and beside the side, and a bound;
    • along a sharp crease, 2 points;
    • crossing it, the far end of its side's line at its separation from the face;
    • a box's edge over the rim, the closest points' direction;
    • a capsule or a cylinder lying parallel on a lying cylinder, 2 points at all of 2000 poses each;
    • a cylinder lying across an upright one's top rim, and a sphere past the rim, each 1 mm out at 45°: 1 point, out from the rim, at the gap.
  • Other test changes:
    • tests/physics_shapes.sx: the cylinder's inertia and extents;
    • tests/solids.sx: a ray and a capsule dropped onto a lying cylinder meet it between its side and the prism's corners;
    • tests/bake_stage.sx: a Cylinder body through the stage and the world file, and kind 5 is malformed;
    • tests/bake_extract.py: Cylinder sizing, an uneven Z taken, an uneven Y refused;
    • tests/first_map_bodies.sx: barrels are cylinders.
  • Every existing test passes unchanged. With every check printing its figures, the 24 physics and sim test files on master print the same figures at master ff70931 and at the head, with one exception: in first_map_bodies, the barrels are cylinders, and they settle within 0.03 mm of where the hulls did. Beyond that, the head only adds cases to physics_contacts, physics_shapes and solids, and physics_bench_scene prints the same figures at both.
  • Across the merge of ff70931, which splits a tick into begin, solve and end, every test prints the same figures at b486ee4 and at the head, barrels.sx included, but for the two new cases' lines. The bench, built at master and at the head and run interleaved under taskpolicy -b, gives worst ticks of 0.544 and 0.538 ms at master and 0.544 and 0.537 ms at the head.
  • Mutants. These fail a test: the support point taken in world space; GJK's closest points in place of the axis's; one point along a parallel edge; no gap ends or past; no far end, or one at the core's radius; no tilted-cap rule, or one without its far point; the old checks on parallel capsules and cylinders; no interior check; and a cylinder's side taken as a capsule against another cylinder, or against a sphere or capsule, where their closest points lie past its rims (the separation then reads −0.045 m and −0.022 m for 0.001 m).
    • These survive every test:
      • the side's extent at the core's half length, and the parallel pair taken at the segment's start, both equivalent within the tests;
      • a polytope's lone point against the side by GJK's closest points in place of the axis's, which review run 1923 found equivalent over 36 poses across a ridge;
      • collideTriangle's lone point for a cylinder taken by the cores' closest points.
    • Two parameters have no case: a cap meeting a face only within 26.6° of its normal in place of 45° survives, and so do 8 rim points in place of 16.
  • Probes at the head, against the head before these contact changes:
    • 100,000 directions within 1e-6 rad of the axis put the farthest point exactly on the cap's plane (before: 98,535 more than 1 mm off).
    • A barrel lying along z has the box ±0.4917.
    • A player walking into it 0.40–0.47 m off its middle rolls it 5.1–6.2 m (before: stopped, 0 m).
    • At rest along 2°–6° ridges, 0 of 108 poses pass 6 mm, deepest 1.6 mm (before: 73, 33 mm).
    • Rolled down 7 steps along their edges in turned worlds, its axis yawed at most 1.4° in this probe (before: 3.0°). That is these configurations' figure, not a bound. Review run 1923 measured 3.2° on 0.2 × 0.3 m stairs at 3 m/s, where master's hull barrel yaws 33°: its one-point ticks came with the axis 2.9–3.6° off the edge, past PARALLEL, as a capsule's do.
    • Of this probe's 200 drops onto a 1 m block, 1 passed 15 mm, the deepest 16.5 mm (master's hull barrel: 1, 16.1 mm).
    • A barrel landing near upright sinks deeper. Review run 1923 measured 16–31 mm on flat ground for a tilt of 2–5° and a spin of 3–6 rad/s. When the tilt passes near upright, the rim point that becomes deepest jumps about 84° round the rim in one tick, between the points the tilted cap keeps. Over its 600 such landings and 200 block drops, 11 passed 15 mm and the deepest was 31 mm; master's hull barrel had 8, and 23 mm. This is the same failure as a rolling sphere's sink and a capsule's turn onto a sharp edge: contact points held fixed in the body across a tick.
    • The player walking or sprinting into a lying or standing cylinder too heavy to push, at 9 offsets, never steps back or leaves the ground.

The drawn barrel is Kenney's octagon, and its cylinder has the octagon's corner radius (0.534 m at scale 0.8). A barrel rolling with a flat face down therefore draws up to 4 cm above the ground. Agra's check in the app: pushed barrels roll.

Gates: full at ba34b30 passed (gate run 963): 42 tests, bench worst tick 0.544 ms.

Tracker: [#3934](https://sak.swipelab.com/tasks/3934) The first map's barrels were Convex Hull bodies of Kenney's octagonal barrel. Lying on its side, each facet stopped the roll, so a barrel pushed at 2 m/s stopped within 1.6 m. This PR adds a Cylinder rigid body shape, read from Blender's own Cylinder and colliding as an exact cylinder, and makes the five barrels Cylinder bodies. ## What changes - **Physics** (`src/physics`). A new `cylinder` shape along local +Y, with a solid cylinder's inertia. - **Core.** A cylinder's core for GJK is the cylinder shrunk by `CYLINDER_CORE` (2 cm, or a quarter of its least extent), so the cores of a cylinder and what it rests on stay apart and give the normal. Its farthest point along a direction is found in its own frame, so it lies on the cap's plane however near the axis the direction lies. - **Points.** Every contact point is on the exact, sharp-rimmed surface, at its own separation along the normal. - **Against a flat face** (a triangle's or box's face, a bound, or a cylinder's cap), a cylinder meets it by its cap when its axis lies within 45° of the face's normal, and otherwise by the line of its side nearest the face, both clipped to the face. - A cap gives its 16 rim points and its rim's deepest point. - A cap whose rim rises more than SPECULATIVE from the face keeps the deepest of those, the two nearest it and the farthest. It rests about that point, which runs round the rim as the cylinder rolls on it. - **A cylinder's cap** is a reference face for boxes, spheres, capsules and other cylinders. - **Against an edge or another side.** Where its side meets an edge, a segment or another side, the closest points come from its axis, exactly. GJK's against the core's round side are only as close as its tolerance. Along a parallel edge, capsule or side it gets two points, at the ends of their common part; every other contact is one point. - **Map triangles.** Their wedge rules apply to cylinders as to polytopes. The side's line takes a capsule's rules: its end in the gap past a smooth convex edge is kept, and turning about a sharp edge it gets the far end of the line over the face. - **Bounds and the plane sweep** read the exact surface. - **Player** (`src/physics/solids.sx`). Movement meets a cylinder body as the 32-sided prism whose faces touch it. - **Bake.** `extract.py` takes an Active Cylinder: radius half the larger of the X and Y dimensions, half length half the Z dimension, as Blender sizes it. - Blender 5.2's simulation scales a Cylinder's radius by the X scale alone and its length by Z (probed by drops). So the extract refuses X and Y scales that differ, and takes an uneven Z. - `stage.sx` and the world file carry shape kind 4. - **First map.** The rigid bodies of its five barrels are Cylinder in `first_map.blend`, and nothing else in the file changed. `first_map_bodies.py` and the `.md` say so. The README lists the shape. - The branch merges master ff70931 (#3904's physics bench), after 6c24bd0 (#3931's trunk-slide movement). ## Evidence - **The task's probe** on the rebaked first map: both lying barrels roll 5.86 m and 5.77 m in 5 s, at 1.14 and 0.98 m/s, where the cannon ball goes 3.73 m at 0.63 m/s. Past the level clearing, the barrels roll on downhill. On master they stop at 1.55 m and 1.15 m. - **`tests/barrels.sx`** uses the map's own barrel as baked: - On the first map, both lying barrels pushed at 2 m/s roll at least 3 m in 5 s and still roll at least as fast as the cannon ball. Each rolls without slipping: spin × radius matches its speed. - On level ground of 2 m cells, undamped, it rolls at 1.3338 m/s (2/3 of the push is 1.3333). It covers 4.044 m in 3 s; slipping then rolling predicts 4.045 m. - Released across a 10° slope, it rolls down at 2.274 m/s after 2 s (2/3 g sin 10° × 2 s = 2.271) and keeps gaining speed. - Standing on its end, it rests within 0.002 mm of the ground. Pushed at 2 m/s it slides 0.404 m (v²/2μg = 0.408) and stays standing. Tilted 0.15 rad short of its balance it falls back onto its end, and 0.15 rad past it, onto its side. - A barrel stands on another, and a rolled barrel pushes a parallel one on. - Standing on a 15° incline, its axis along the incline's normal, it moves 1.4 mm (its 1 mm drop included) and sleeps. - Set at rest along a ridge (bent 3°, on its line, the world turned 0.37 rad; and bent 2°, 4 mm off it, yawed 0.005 rad), it never ends a tick more than 0.1 mm into the map. - Turning at 3 rad/s about a block's sharp top edge onto its top, it never ends a tick more than 0.3 mm in. - On its rim, tilted 15°, it never ends a tick more than 3.2 mm into the ground, whether whirled at 8 rad/s so it rolls round the rim or released to fall back onto its end. - Run against master's bake in a `git archive` arm, every rolling check fails: the barrels stop after 1.55 and 1.15 m, and the slope barrel never moves. - **`tests/physics_contacts.sx`** pins the manifolds: - lying, 2 points at the gap at the ends of its side; - standing, 4 rim points; - tilted between two rim points, the rim's deepest point; - on a box, crossed and parallel cylinders, stacked cylinders, a sphere or capsule on the cap and beside the side, and a bound; - along a sharp crease, 2 points; - crossing it, the far end of its side's line at its separation from the face; - a box's edge over the rim, the closest points' direction; - a capsule or a cylinder lying parallel on a lying cylinder, 2 points at all of 2000 poses each; - a cylinder lying across an upright one's top rim, and a sphere past the rim, each 1 mm out at 45°: 1 point, out from the rim, at the gap. - **Other test changes:** - `tests/physics_shapes.sx`: the cylinder's inertia and extents; - `tests/solids.sx`: a ray and a capsule dropped onto a lying cylinder meet it between its side and the prism's corners; - `tests/bake_stage.sx`: a Cylinder body through the stage and the world file, and kind 5 is malformed; - `tests/bake_extract.py`: Cylinder sizing, an uneven Z taken, an uneven Y refused; - `tests/first_map_bodies.sx`: barrels are cylinders. - **Every existing test passes unchanged.** With every check printing its figures, the 24 physics and sim test files on master print the same figures at master ff70931 and at the head, with one exception: in `first_map_bodies`, the barrels are cylinders, and they settle within 0.03 mm of where the hulls did. Beyond that, the head only adds cases to `physics_contacts`, `physics_shapes` and `solids`, and `physics_bench_scene` prints the same figures at both. - **Across the merge of ff70931**, which splits a tick into begin, solve and end, every test prints the same figures at b486ee4 and at the head, `barrels.sx` included, but for the two new cases' lines. The bench, built at master and at the head and run interleaved under `taskpolicy -b`, gives worst ticks of 0.544 and 0.538 ms at master and 0.544 and 0.537 ms at the head. - **Mutants.** These fail a test: the support point taken in world space; GJK's closest points in place of the axis's; one point along a parallel edge; no gap ends or `past`; no far end, or one at the core's radius; no tilted-cap rule, or one without its far point; the old checks on parallel capsules and cylinders; no interior check; and a cylinder's side taken as a capsule against another cylinder, or against a sphere or capsule, where their closest points lie past its rims (the separation then reads −0.045 m and −0.022 m for 0.001 m). - These survive every test: - the side's extent at the core's half length, and the parallel pair taken at the segment's start, both equivalent within the tests; - a polytope's lone point against the side by GJK's closest points in place of the axis's, which review run 1923 found equivalent over 36 poses across a ridge; - `collideTriangle`'s lone point for a cylinder taken by the cores' closest points. - Two parameters have no case: a cap meeting a face only within 26.6° of its normal in place of 45° survives, and so do 8 rim points in place of 16. - **Probes** at the head, against the head before these contact changes: - 100,000 directions within 1e-6 rad of the axis put the farthest point exactly on the cap's plane (before: 98,535 more than 1 mm off). - A barrel lying along z has the box ±0.4917. - A player walking into it 0.40–0.47 m off its middle rolls it 5.1–6.2 m (before: stopped, 0 m). - At rest along 2°–6° ridges, 0 of 108 poses pass 6 mm, deepest 1.6 mm (before: 73, 33 mm). - Rolled down 7 steps along their edges in turned worlds, its axis yawed at most 1.4° in this probe (before: 3.0°). That is these configurations' figure, not a bound. Review run 1923 measured 3.2° on 0.2 × 0.3 m stairs at 3 m/s, where master's hull barrel yaws 33°: its one-point ticks came with the axis 2.9–3.6° off the edge, past `PARALLEL`, as a capsule's do. - Of this probe's 200 drops onto a 1 m block, 1 passed 15 mm, the deepest 16.5 mm (master's hull barrel: 1, 16.1 mm). - A barrel landing near upright sinks deeper. Review run 1923 measured 16–31 mm on flat ground for a tilt of 2–5° and a spin of 3–6 rad/s. When the tilt passes near upright, the rim point that becomes deepest jumps about 84° round the rim in one tick, between the points the tilted cap keeps. Over its 600 such landings and 200 block drops, 11 passed 15 mm and the deepest was 31 mm; master's hull barrel had 8, and 23 mm. This is the same failure as a rolling sphere's sink and a capsule's turn onto a sharp edge: contact points held fixed in the body across a tick. - The player walking or sprinting into a lying or standing cylinder too heavy to push, at 9 offsets, never steps back or leaves the ground. The drawn barrel is Kenney's octagon, and its cylinder has the octagon's corner radius (0.534 m at scale 0.8). A barrel rolling with a flat face down therefore draws up to 4 cm above the ground. Agra's check in the app: pushed barrels roll. Gates: `full` at ba34b30 passed (gate run 963): 42 tests, bench worst tick 0.544 ms.
cylinderPoint takes the part of the direction across the axis in the
cylinder's own frame. Taken in world space, for a direction within about
1e-6 rad of the axis that part was rounding, and scaled out to the rim it
put the point up to a radius off the cap's plane: the barrel's box, its
lowest point against a face and its tilted cap's deepest point were all
wrong. A barrel standing on a 15° incline, its axis along the incline's
normal, now stays where it stands.
- Where a cylinder's core's closest points lie on a polytope's edge or
  corner and across its side, sidePair finds them from the edge and the
  axis: GJK's against the core's round side tilt their normal by up to
  0.01 rad, so at a smooth ridge each triangle read it as nearer the
  other face and neither kept the contact.
- A side along an edge gets the part alongside it, two points, whether
  the face clip came out empty or GJK named a point at the core's end;
  a capsule's or another cylinder's parallel side likewise.
- A cylinder lying past a smooth convex edge keeps its side's end in the
  gap there (gapEnds and clipTo's `past`), and one turning about a sharp
  edge gets the far end of its side's line over the face (farEnd), as a
  capsule does.

The ridge helpers move from physics_bodies to tests/support/worlds.sx.
A cap whose rim rises more than SPECULATIVE from the face it meets keeps,
of its clipped rim points and its rim's deepest point, the deepest, the
two nearest it and the one farthest from it. The four kept for the
largest area left nothing near the deepest point, which runs round the
rim as the cylinder rolls on it: a barrel rolling on its rim sank 14 mm,
and released on its rim it slapped down 10 mm without its far side.
agra merged commit 5af4834c97 into master 2026-10-30 16:42:42 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sx/hulken!25
No description provided.