Slowly orbit the camera around every form guide
With the equipment layer carrying world-space 3D form, the prop-free gate comes off: machines now get the same slow orbit as the bodyweight moves, their seats, cables, bars, and rollers turning with the figure. Closes out the orbit-for-all-exercises plan. Claude-Session: https://claude.ai/code/session_01HJDQQDA9QdP8zByg43H5v3
This commit is contained in:
@@ -80,23 +80,30 @@ struct ExerciseMotionTests {
|
||||
#expect(hypot(releasedHand.x - releasedHand0.x, releasedHand.y - releasedHand0.y) > 1)
|
||||
}
|
||||
|
||||
/// Prop-free bodyweight motions slowly orbit the camera while looping; motions
|
||||
/// with equipment keep their authored view (until machines orbit too).
|
||||
@Test func orbitAppliesOnlyToPropFreeMotions() throws {
|
||||
let birdDog = try #require(FigureAnimation(exerciseName: "Bird Dog"))
|
||||
#expect(birdDog.orbits)
|
||||
/// Every motion slowly orbits the camera while looping — bodyweight moves and
|
||||
/// machines alike, since the equipment layer rotates with the figure.
|
||||
@Test func everyMotionOrbits() throws {
|
||||
// Same loop phase seconds apart: the orbit yaw differs, so the projected head
|
||||
// moves even though the pose is identical.
|
||||
let duration = birdDog.timeline.duration
|
||||
let birdDog = try #require(FigureAnimation(exerciseName: "Bird Dog"))
|
||||
let head0 = birdDog.geometry(at: 0).headCenter
|
||||
let head1 = birdDog.geometry(at: duration * 2).headCenter
|
||||
let head1 = birdDog.geometry(at: birdDog.timeline.duration * 2).headCenter
|
||||
#expect(hypot(head0.x - head1.x, head0.y - head1.y) > 1)
|
||||
|
||||
// A machine orbits too, and its equipment turns with the figure: the seat
|
||||
// slab's projected points move between the same two loop phases.
|
||||
let legPress = try #require(FigureAnimation(exerciseName: "Leg Press"))
|
||||
#expect(!legPress.orbits)
|
||||
let fixed0 = legPress.geometry(at: 0).headCenter
|
||||
let fixed1 = legPress.geometry(at: legPress.timeline.duration * 2).headCenter
|
||||
#expect(abs(fixed0.x - fixed1.x) < 1e-9 && abs(fixed0.y - fixed1.y) < 1e-9)
|
||||
let geo0 = legPress.geometry(at: 0)
|
||||
let geo1 = legPress.geometry(at: legPress.timeline.duration * 2)
|
||||
#expect(hypot(geo0.headCenter.x - geo1.headCenter.x,
|
||||
geo0.headCenter.y - geo1.headCenter.y) > 1)
|
||||
guard case .poly(let seat0, _, _) = geo0.propsBackground.first,
|
||||
case .poly(let seat1, _, _) = geo1.propsBackground.first,
|
||||
let s0 = seat0.first, let s1 = seat1.first else {
|
||||
Issue.record("expected the Leg Press backrest slab as the first background prop")
|
||||
return
|
||||
}
|
||||
#expect(hypot(s0.x - s1.x, s0.y - s1.y) > 1)
|
||||
}
|
||||
|
||||
/// Every exported motion in the bundle decodes and builds a playable timeline.
|
||||
|
||||
Reference in New Issue
Block a user