Switch TD view to perspective camera#9281
Conversation
📝 WalkthroughWalkthroughBroad change to support both OrthographicCamera and PerspectiveCamera across viewer code: trackball controls, camera controller, TD controller, views, store types, and default state. Adds explicit camera target tracking and updates TD view projection/near handling for perspective cameras. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@frontend/javascripts/viewer/controller/camera_controller.ts`:
- Around line 121-127: getTDCameraTarget's null check is ineffective because
CameraData.target is declared non-nullable; either make CameraData.target
nullable (Vector3 | null) and update deserialization/other uses so the existing
null check in getTDCameraTarget remains valid, or keep the type and replace the
null check with a meaningful-value check (e.g., add an isZeroVector(target:
Vector3) helper and use it inside getTDCameraTarget) to detect legacy/empty
targets and then fall back to voxelToUnit(state.dataset.dataSource.scale,
getPosition(state.flycam)). Ensure to update all places that construct or read
CameraData if you change the type.
🧹 Nitpick comments (4)
frontend/javascripts/viewer/controller/camera_controller.ts (2)
204-231: The zero-height early return dispatches a no-op action.When
oldHeight === 0(line 213), the dispatch on lines 214–220 writes back the sameleft/rightvalues already in the store, which is effectively a no-op. If the intent is simply to bail out, a plainreturnwould be clearer and avoid the unnecessary dispatch + state update cycle.♻️ Simplify early return
if (oldHeight === 0) { - Store.dispatch( - setTDCameraWithoutTimeTrackingAction({ - left: tdCameraData.left, - right: tdCameraData.right, - }), - ); return; }
87-127:getCameraBasisis duplicated across files with different signatures and purposes.Two
getCameraBasisfunctions exist:
camera_controller.ts(line 91): TakesVector3tuples (position,target,up) and returns a basis computed from these vectorstd_controller.tsx(line 47): Takes a Three.jsOrthographicCamera | PerspectiveCameraand extracts the basis from the camera's stateWhile they compute related concepts, the different input types and contexts make unification challenging without adding abstraction complexity. If extracting shared logic becomes valuable, consider a third utility function for the core math with separate wrapper functions for each context.
frontend/javascripts/viewer/controller/td_controller.tsx (2)
186-196: Defensive fallback fortargetis unnecessary given the type.Line 193:
cameraData.target || [0, 0, 0]—CameraData.targetisVector3(required, non-nullable), so the|| [0, 0, 0]fallback can never trigger. Same observation as incamera_controller.ts'sgetTDCameraTarget.This is harmless but adds noise. Consider removing the fallback or, if guarding against legacy state, making the type explicitly nullable.
334-368:setTargetAndFixPositionstill uses Euler-based rotation inversion with@ts-expect-errorsuppression.Lines 361–366 cast a
Vector3(fromcamera.rotation.clone()) to work with Euler-like properties (order). This was pre-existing tech debt, but now that the camera can be aPerspectiveCamera, the rotation representation is the same (both useEulerinternally), so the behavior is unchanged. Thets-expect-errorcomments remain a maintenance risk.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
frontend/javascripts/viewer/store.ts (1)
469-479: Consider replacingPartialCameraDatawithPartial<CameraData>.
PartialCameraDatais a manual mirror ofCameraDatawith every field made optional, which creates a drift risk — demonstrated by this PR requiring two symmetric edits instead of one. UsingPartial<CameraData>would automatically stay in sync with futureCameraDatachanges.♻️ Proposed simplification
-export type PartialCameraData = { - readonly near?: number; - readonly far?: number; - readonly left?: number; - readonly right?: number; - readonly top?: number; - readonly bottom?: number; - readonly up?: Vector3; - readonly position?: Vector3; - readonly target?: Vector3; -}; +export type PartialCameraData = Partial<CameraData>;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/javascripts/viewer/store.ts` around lines 469 - 479, Replace the manual mirror type PartialCameraData with a built-in Partial utility so it stays in sync with CameraData: change the export type declaration to "export type PartialCameraData = Partial<CameraData>;" (remove the explicit field list), ensure CameraData is in scope/imported, and keep the same exported symbol name (PartialCameraData) so callers don't need changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@frontend/javascripts/viewer/store.ts`:
- Around line 469-479: Replace the manual mirror type PartialCameraData with a
built-in Partial utility so it stays in sync with CameraData: change the export
type declaration to "export type PartialCameraData = Partial<CameraData>;"
(remove the explicit field list), ensure CameraData is in scope/imported, and
keep the same exported symbol name (PartialCameraData) so callers don't need
changes.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/javascripts/viewer/store.tsfrontend/javascripts/viewer/view/plane_view.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/javascripts/viewer/view/plane_view.ts
|
@Albane-dlv Norman suggested to use icons with buttons to switch between orthographic and perspective cameras. Can you please design two icons for that. |
Co-authored-by: Philipp Otto <philippotto@users.noreply.github.com>
…sos into perspective-camera
Co-authored-by: Copilot <copilot@github.com>
|
unfortunately, rotating is buggy in the perspective mode. happens always when releasing the right-mouse. sometimes, the jump is subtle, sometimes severe. ortho mode is fine. buggy-rotation-in-perspective-cam.webm |
|
@philippotto I addressed the camera jitter issue after SHIFT + left clicking on a mesh. |
buggy-rotation-in-perspective-cam-2.webmI can still reproduce the same issue (dev instance was up to date). unfortunately, the mouse isn't visible in the screencast, but I only do shift+click and then rotate. most of the time, the jump is subtle, but sometimes it's jarring (in the video at the very end). not sure how annoying this really will be in production. maybe we should merge this but not make it a default for everyone just yet? instead, we could internally guinea pig this in the team for a while and then switch everyone over in a month? |


Summary
URL of deployed dev instance (used for testing):
https://perspectivecamera.webknossos.xyz/
Testing
TODO
<Switch>with text for the settings UI.Issues: