camera-restore: - IEngineStateProvider.SetCamera 반사 쓰기 (HmegDirectStateProvider) - POST /camera/restore (BridgeHttpServer, StateRouter) - Recorder --sidecar-url + camera_snapshot 캡처 - UiaPlayerHost.TryRestoreCamera, PlayerEngine 재생 전 복원 - 149 tests LauncherUI (#15): - Sidecar URL 체크박스 + 입력란 (녹화/재생 모두 연동) - 재생 속도 슬라이더 (0.25x~4.0x, 기본 1.0x) - 빌드 타임스탬프 타이틀바 표시 - 녹화 완료 후 RecordNameBox 초기화 - UiAnalysisWindow 추가 PlayerEngine (#15): - CancellationToken 지원 (중단 버튼 동작) - Focus 스텝 early return (no-op, issue #11) - Type/Drag unresolvable UIA path fallback - SpeedMultiplier 옵션 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.7 KiB
3.7 KiB
2026-04-10 — camera-restore (Sprint Contract + Implementation)
Summary
Implemented camera state capture at recording start and restore at playback start via the engine-bridge HTTP sidecar. Replays are now viewport-position-independent.
Changes
New files
docs/contracts/camera-restore.md— Sprint Contract with 10 DoD items
Modified files
| File | Change |
|---|---|
src/Recordingtest.Bridge.Abstractions/IEngineStateProvider.cs |
Added SetCamera(CameraSnapshot) to interface + NullEngineStateProvider no-op |
src/Hmeg/Recordingtest.Hmeg.Bridge/HmegDirectStateProvider.cs |
Added SetCamera via reflection (WriteVec3/WriteDouble), optional uiDispatch: Action<Action> ctor param |
src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost/IEngineStateProvider.cs |
ReflectionEngineStateProvider.SetCamera no-op |
src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost/ChainedEngineStateProvider.cs |
SetCamera delegates to primary only |
src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost/BridgeHttpServer.cs |
Reads POST body (StreamReader) before calling Route |
src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost/StateRouter.cs |
Added Route(string method, string path, string body) + POST /camera/restore + ReadVecFromJson helper; backwards-compat Route(string path) overload |
src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost/HmEgBridgePlugin.cs |
Passes Application.Current.Dispatcher.Invoke as uiDispatch to HmegDirectStateProvider |
src/Hmeg/Recordingtest.Hmeg.Bridge.Client/HmEgHttpSnapshot.cs |
Added RestoreCamera(eye,target,up,fov) POST method + BuildCameraJson helper |
src/Recordingtest.Recorder/Scenario.cs |
Added RecordedCameraSnapshot class + CameraSnapshot? field on Scenario |
src/Recordingtest.Recorder/Program.cs |
Added --sidecar-url CLI arg; TryCaptureCamera(url) internal helper (GET /camera, 2s timeout); stores snapshot in scenario |
src/Recordingtest.Player/Model/Scenario.cs |
Added ScenarioCameraSnapshot class + CameraSnapshot? field |
src/Recordingtest.Player/IPlayerHost.cs |
Added TryRestoreCamera(eye,target,up,fov) with default return false implementation |
src/Recordingtest.Player/PlayerEngine.cs |
Calls host.TryRestoreCamera(...) before first step when scenario.CameraSnapshot != null |
src/Recordingtest.Player/UiaPlayerHost.cs |
Optional sidecarUrl ctor param; TryRestoreCamera POSTs to /camera/restore |
tests/Recordingtest.Player.Tests/FakePlayerHost.cs |
TryRestoreCamera override + CameraRestoreCalls tracking |
tests/Recordingtest.Player.Tests/PlayerEngineTests.cs |
+5 camera tests (no-snapshot-skip, correct-values, false-return-continues, YAML parse, YAML null) |
tests/Recordingtest.Recorder.Tests/RecorderTests.cs |
+3 camera tests (unreachable-null, roundtrip, null-field) |
| Various test fake providers | Added SetCamera no-op to satisfy updated interface |
Test counts
132 → 149 tests, all green.
Design decisions
SetCamerais write-only (no return); failures are silently swallowed — the SUT must never crash due to a record tool.WriteVec3usesctor(double,double,double)orctor(float,float,float)reflection to construct WPF/HmEG vector types without a compile-time reference.- Camera write must be dispatched to WPF UI thread:
HmEgBridgePlugincapturesApplication.Current.Dispatcher.Invokeat construction time and passes it asAction<Action>. - Legacy scenarios (no
camera_snapshot) work unchanged — field is nullable, engine skips restore when null. UiaPlayerHostcreates a freshHttpClientperTryRestoreCameracall (short-lived; no pooling needed at this stage).
Context usage
~80K tokens