Add getters for camera parameters #1419#2767
Add getters for camera parameters #1419#2767Dtsitos wants to merge 21 commits intof3d-app:masterfrom
Conversation
|
You are modifying libf3d public API! |
|
I think this should be correct now, will work on better testing ASAP |
|
please let us know when its ready for review @Dtsitos |
|
@mwestphal Hello there, I have implemented all the bindings and some basic testing for them. Full disclosure, I have never worked with bindings again and mostly deduced what to do based on the patterns from the existing implementations. Thank you for your patience |
Thats perfectly fine and it looks good to me :) |
|
\ci full |
|
\ci full |
|
not ready for full CI yet :) |
|
do you need help with the style check @Dtsitos ? |
Yes please, I am really not sure what is wrong. Sorry for not replying earlier but exam season is starting and I am not having fun with that 🙃 |
|
|
@mwestphal Hello there. Thank you for showing me how to fix styling. I changed the tests for the new getters radically because after careful consideration (bashing my head against the keyboard) I realised that the tests produced wrong values not because the math is wrong but because I cannot play around with custom environment vectors as the test file does not have access to the renderer methods to change the environmental vectors. The tests work now locally. I will push with the proper styling and then it should be good to go. |
|
The environment vectors are indeed not accessible from the camera, however you can set the f3d/library/testing/TestSDKDynamicUpDirection.cxx Lines 53 to 57 in ae5488a you'll need to set |
|
Alas, my testing and styling is proper. What should I do now to proceed with the PR? I am guessing I need a CI full check first. |
Ask for a review from a maintainer, top right corner of the PR :) |
mwestphal
left a comment
There was a problem hiding this comment.
small questions and suggestions
…ng in TestSDKCamera.cxx
…ign issue in camera_impl.cxx
670e796 to
80df8d6
Compare
|
So I am not sure if force pushing was a nuclear move but it seemed necessary for a proper rebase. The new tests integrate properly with the new format and work now, so I think it is ready for review again |
Force push is indeed part of the normal rebase workflow. |
| test("getWorldAzimuth finite after camera operations", std::isfinite(cam.getWorldAzimuth())); | ||
|
|
||
| test("getWorldElevation finite after camera operations", std::isfinite(cam.getWorldElevation())); |
There was a problem hiding this comment.
why do you test isfinite ? test just expected value ?
There was a problem hiding this comment.
Due to the other changes requested I changed testing drastically, mostly to improve me own understanding of the math and to be more thorough
| void camera_impl::getPositionToFocalVector(vector3_t& vec) const | ||
| { | ||
| point3_t pos, focal; | ||
| this->getPosition(pos); | ||
| this->getFocalPoint(focal); | ||
|
|
||
| vtkMath::Subtract(pos.data(), focal.data(), vec.data()); | ||
| } |
There was a problem hiding this comment.
(pos - focal) is not "position to focal" vector, it's the opposite. Do you mean vtkMath::Subtract(focal.data(), pos.data(), vec.data()); or name getFocalToPositionVector?
There was a problem hiding this comment.
To avoid causing problems downstream (with bindings and such) I changed the subtract order
| double* up = ren->GetEnvironmentUp(); | ||
| double* right = ren->GetEnvironmentRight(); | ||
|
|
||
| // Project view vector onto plane orthogonal to up |
There was a problem hiding this comment.
The comment is not correct. You are not projecting the view vector to the plane orthogonal to up, you are projecting the view vector to the up direction.
There was a problem hiding this comment.
I changed the azimuth implementation based on the convention you ask for in a comment further down so that is resolved.
| vtkMath::Normalize(horizontal.data()); | ||
|
|
||
| // Signed angle between right and horizontal projection | ||
| double angleRad = vtkMath::SignedAngleBetweenVectors(right, horizontal.data(), up); |
There was a problem hiding this comment.
From my comments above, the horizontal direction computed is pointing behind the camera. Make sure it gives the same result here as I expect the sign to be flipped here.
| double angleRad = vtkMath::SignedAngleBetweenVectors(right, horizontal.data(), up); | ||
|
|
||
| return vtkMath::DegreesFromRadians(angleRad); |
There was a problem hiding this comment.
I agree with @snoyer. If we just query the azimuth after opening a file without changing the camera, I'd expect 0 to be returned.
…tion. Improved testing based on new approach
|
I changed the testing quite a lot to make it more thorough (edge case and custom environment for each getter) and changed my world azimuth convention to align with expectations from an above discussion (about azimuth being zero when looking forward). I hope my code is more up to spec now |
|
Ill let @Meakk review this one :) |
| const double* up = ren->GetEnvironmentUp(); | ||
| const double* right = ren->GetEnvironmentRight(); | ||
|
|
||
| // Derive environment forward = up × right |
There was a problem hiding this comment.
It looks like × is not an ascii character. I don't think it's necessary a problem but just wanted to make sure we agree with that @mwestphal
| vector3_t v; | ||
| this->getPositionToFocalVector(v); | ||
| return vtkMath::Norm(v.data()); |
There was a problem hiding this comment.
| vector3_t v; | |
| this->getPositionToFocalVector(v); | |
| return vtkMath::Norm(v.data()); | |
| return this->GetVTKCamera()->GetDistance(); |
is that equivalent?
|
|
||
| cam.setPosition({ 0., -10., 0. }); | ||
| cam.setFocalPoint({ 0., 0., 0 }); | ||
| test("Distance: 1", cam.getDistance(), approx(10.0)); |
There was a problem hiding this comment.
| test("Distance: 1", cam.getDistance(), approx(10.0)); | |
| test("Distance: 10", cam.getDistance(), approx(10.0)); |
| cam.setPosition({ -10., 0., 0. }); | ||
| cam.setFocalPoint({ 0., 0., 0. }); | ||
| cam.setViewUp({ 0., 1., 0 }); | ||
| test("Azimuth (Y-up): environment forward (+X)", cam.getWorldAzimuth(), approx(90.0)); |
There was a problem hiding this comment.
Let's add the code below to test setter/getter equivalence (credits to @snoyer):
std::vector<f3d::direction_t> up_directions = {
{ 0, 0, +1 },
{ 0, +1, 0 },
{ +1, 0, 0 },
{ 0, 0, -1 },
{ 0, -1, 0 },
{ -1, 0, 0 },
{ -1, +2, +3 },
{ +4, -5, -6 },
};
const std::vector<std::pair<double, double>> azimuths_elevations = {
{ 0, 0 },
{ +12, +34 },
{ +12, -34 },
{ -12, +34 },
{ -12, -34 },
};
for (const auto up : up_directions)
{
for (auto [a, e] : azimuths_elevations)
{
f3d::engine eng = f3d::engine::create(true);
f3d::window& win = eng.getWindow();
f3d::camera& cam = win.getCamera();
opt.scene.up_direction = up;
win.render();
cam.azimuth(a).elevation(e);
const std::string title = " after .azimuth(" + f3d::options::format(a) + ").elevation(" +
f3d::options::format(e) + ") with up = " + f3d::options::format(up);
test("azimuth " + title, cam.getWorldAzimuth(), approx(a, 1e-10));
test("elevation" + title, cam.getWorldElevation(), approx(e, 1e-10));
}
}| return ret; | ||
| } | ||
|
|
||
| JNIEXPORT jdouble JAVA_BIND(Camera, getWorldAzimuth)(JNIEnv* env, jobject self) |
|
Here's a suggested patch by @snoyer on discord to fix the flipped signed: diff --git a/library/src/camera_impl.cxx b/library/src/camera_impl.cxx
index 2d4bd562b..5b47f0d68 100644
--- a/library/src/camera_impl.cxx
+++ b/library/src/camera_impl.cxx
@@ -2,6 +2,7 @@
#include <vtkCamera.h>
#include <vtkMatrix4x4.h>
+#include <vtkPlane.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkVersion.h>
@@ -116,56 +117,39 @@ void camera_impl::getPositionToFocalVector(vector3_t& vec) const
//----------------------------------------------------------------------------
double camera_impl::getWorldAzimuth() const
{
- vector3_t view;
- this->getPositionToFocalVector(view);
- vtkMath::Normalize(view.data());
-
vtkRenderer* ren = this->Internals->VTKRenderer;
const double* up = ren->GetEnvironmentUp();
- const double* right = ren->GetEnvironmentRight();
-
- // Derive environment forward = up × right
- vector3_t forward;
- vtkMath::Cross(up, right, forward.data());
- vtkMath::Normalize(forward.data());
-
- vector3_t projUp;
- vtkMath::ProjectVector(view.data(), up, projUp.data());
- vector3_t horizontal;
- vtkMath::Subtract(view.data(), projUp.data(), horizontal.data());
+ double projectedView[3];
+ vtkPlane::ProjectVector(this->GetVTKCamera()->GetDirectionOfProjection(),
+ this->getFocalPoint().data(), up, projectedView);
static constexpr double EPS = 128 * std::numeric_limits<double>::epsilon();
- if (vtkMath::Norm(horizontal.data()) < EPS)
+ if (vtkMath::Norm(projectedView) < EPS)
{
return 0.0;
}
- vtkMath::Normalize(horizontal.data());
+ const double angleRad =
+ vtkMath::SignedAngleBetweenVectors(ren->GetEnvironmentRight(), projectedView, up);
- double angleRad = vtkMath::SignedAngleBetweenVectors(horizontal.data(), forward.data(), up);
-
- return vtkMath::DegreesFromRadians(angleRad);
+ return vtkMath::DegreesFromRadians(angleRad) - 90.0;
}
//----------------------------------------------------------------------------
double camera_impl::getWorldElevation() const
{
- vector3_t view;
- this->getPositionToFocalVector(view);
+ double* view = this->GetVTKCamera()->GetDirectionOfProjection();
static constexpr double EPS = 128 * std::numeric_limits<double>::epsilon();
- if (vtkMath::Norm(view.data()) < EPS)
+ if (vtkMath::Norm(view) < EPS)
{
return 0.0;
}
- vtkMath::Normalize(view.data());
vtkRenderer* ren = this->Internals->VTKRenderer;
- double* up = ren->GetEnvironmentUp();
-
- double angleRad = vtkMath::AngleBetweenVectors(view.data(), up);
- return 90.0 - vtkMath::DegreesFromRadians(angleRad);
+ const double angleRad = vtkMath::AngleBetweenVectors(ren->GetEnvironmentUp(), view);
+ return vtkMath::DegreesFromRadians(angleRad) - 90.0;
}
//---------------------------------------------------------------------------- |
|
Still working on this @Dtsitos ? |
Describe your changes
This PR adds read-only getters to the camera API to retrieve derived world-space
camera parameters (azimuth, elevation, and distance).
The values are computed from the camera position and focal point and do not
modify camera state. This enables external applications and bindings to query
camera orientation consistently without duplicating math.
No existing behavior is changed.
Original PR: #2724
Issue ticket number and link if any
Fixes #1419
Checklist for finalizing the PR
.github/workflows/versions.json, I have updateddocker_timestampContinuous integration
Please write a comment to run CI, eg:
\ci fast.See here for more info.