Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions c/camera_c_api.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,27 @@ void f3d_camera_get_focal_point(const f3d_camera_t* camera, f3d_point3_t focal_p
focal_point[2] = cpp_focal[2];
}

//----------------------------------------------------------------------------
f3d_angle_deg_t f3d_camera_get_world_azimuth(const f3d_camera_t* camera)
{
const f3d::camera* cpp_camera = reinterpret_cast<const f3d::camera*>(camera);
return cpp_camera->getWorldAzimuth();
}

//----------------------------------------------------------------------------
f3d_angle_deg_t f3d_camera_get_world_elevation(const f3d_camera_t* camera)
{
const f3d::camera* cpp_camera = reinterpret_cast<const f3d::camera*>(camera);
return cpp_camera->getWorldElevation();
}

//----------------------------------------------------------------------------
double f3d_camera_get_distance(const f3d_camera_t* camera)
{
const f3d::camera* cpp_camera = reinterpret_cast<const f3d::camera*>(camera);
return cpp_camera->getDistance();
}

//----------------------------------------------------------------------------
void f3d_camera_set_view_up(f3d_camera_t* camera, const f3d_vector3_t view_up)
{
Expand Down
24 changes: 24 additions & 0 deletions c/camera_c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,30 @@ extern "C"
*/
F3D_EXPORT void f3d_camera_set_view_up(f3d_camera_t* camera, const f3d_vector3_t view_up);

/**
* @brief Get the camera world azimuth angle in degrees.
*
* @param camera Camera handle.
* @return World elevation angle in degrees.
*/
F3D_EXPORT f3d_angle_deg_t f3d_camera_get_world_azimuth(const f3d_camera_t* camera);

/**
* @brief Get the camera world elevation angle in degrees.
*
* @param camera Camera handle.
* @return World elevation angle in degrees.
*/
F3D_EXPORT f3d_angle_deg_t f3d_camera_get_world_elevation(const f3d_camera_t* camera);

/**
* @brief Get the distance between the camera position and its focal point.
*
* @param camera Camera handle.
* @return Distance to focal point.
*/
F3D_EXPORT double f3d_camera_get_distance(const f3d_camera_t* camera);

/**
* @brief Get the view up vector of the camera.
*
Expand Down
8 changes: 8 additions & 0 deletions c/testing/test_camera_c_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ int test_camera_c_api()
f3d_angle_deg_t angle = f3d_camera_get_view_angle(camera);
(void)angle;

f3d_angle_deg_t azimuth = f3d_camera_get_world_azimuth(camera);
f3d_angle_deg_t elevation = f3d_camera_get_world_elevation(camera);
double distance = f3d_camera_get_distance(camera);

(void)azimuth;
(void)elevation;
(void)distance;

f3d_camera_state_t state = { 0 };
state.position[0] = 5.0;
state.position[1] = 5.0;
Expand Down
15 changes: 15 additions & 0 deletions java/F3DCameraBindings.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ extern "C"
return ret;
}

JNIEXPORT jdouble JAVA_BIND(Camera, getWorldAzimuth)(JNIEnv* env, jobject self)
{
return GetEngine(env, self)->getWindow().getCamera().getWorldAzimuth();
}

JNIEXPORT jdouble JAVA_BIND(Camera, getWorldElevation)(JNIEnv* env, jobject self)
{
return GetEngine(env, self)->getWindow().getCamera().getWorldElevation();
}

JNIEXPORT jdouble JAVA_BIND(Camera, getDistance)(JNIEnv* env, jobject self)
{
return GetEngine(env, self)->getWindow().getCamera().getDistance();
}

JNIEXPORT jobject JAVA_BIND(Camera, setViewUp)(JNIEnv* env, jobject self, jdoubleArray up)
{
double* arr = env->GetDoubleArrayElements(up, nullptr);
Expand Down
4 changes: 4 additions & 0 deletions java/testing/TestCamera.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ public static void main(String[] args) {
camera.setFocalPoint(new double[]{0.5, 0.6, 0.7});
camera.getFocalPoint();

camera.getWorldAzimuth();
camera.getWorldElevation();
camera.getDistance();

camera.setViewUp(new double[]{0.0, 1.0, 0.0});
camera.getViewUp();

Expand Down
10 changes: 10 additions & 0 deletions library/private/camera_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class camera_impl : public camera
camera& setState(const camera_state_t& state) override;
camera_state_t getState() const override;
void getState(camera_state_t& state) const override;
double getWorldAzimuth() const override;
double getWorldElevation() const override;
double getDistance() const override;

camera& dolly(double val) override;
camera& pan(double right, double up, double forward) override;
Expand Down Expand Up @@ -85,6 +88,13 @@ class camera_impl : public camera
bool GetSuccessfullyReset() const;

private:
/**
* This helper computes the direction vector pointing from the camera position
* toward the focal point in world coordinates:
* view = focal_point - position
*/
void getPositionToFocalVector(vector3_t& vec) const;

class internals;
std::unique_ptr<internals> Internals;
};
Expand Down
7 changes: 6 additions & 1 deletion library/public/camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ class F3D_EXPORT camera
[[nodiscard]] virtual camera_state_t getState() const = 0;
/** Get the complete state of the camera into the provided arg */
virtual void getState(camera_state_t& state) const = 0;

/** Return the camera azimuth angle in degrees */
[[nodiscard]] virtual double getWorldAzimuth() const = 0;
/** Return the camera elevation angle in degrees */
[[nodiscard]] virtual double getWorldElevation() const = 0;
/** Return the distance between the camera position and its focal point */
[[nodiscard]] virtual double getDistance() const = 0;
///@}

///@{ @name Manipulation
Expand Down
67 changes: 67 additions & 0 deletions library/src/camera_impl.cxx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#include "camera_impl.h"

#include <cmath>
#include <vtkCamera.h>
#include <vtkMatrix4x4.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkVersion.h>


namespace f3d::detail
{
class camera_impl::internals
Expand Down Expand Up @@ -103,6 +105,71 @@ void camera_impl::getFocalPoint(point3_t& foc) const
cam->GetFocalPoint(foc.data());
}

//----------------------------------------------------------------------------
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());
}

//----------------------------------------------------------------------------
double camera_impl::getWorldAzimuth() const
{
vector3_t view;
this->getPositionToFocalVector(view);
vtkMath::Normalize(view.data());

vtkRenderer* ren = this->Internals->VTKRenderer;
double* up = ren->GetEnvironmentUp();
double* right = ren->GetEnvironmentRight();

// Project view vector onto plane orthogonal to up
vector3_t projUp;
vtkMath::ProjectVector(view.data(), up, projUp.data());

vector3_t horizontal;
vtkMath::Subtract(view.data(), projUp.data(), horizontal.data());

static constexpr double EPS = 128 * std::numeric_limits<double>::epsilon();
if (vtkMath::Norm(horizontal.data()) < EPS)
{
return 0.0;
}

vtkMath::Normalize(horizontal.data());
Comment on lines +136 to +142
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you've got the tests you can double check if this is actually needed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so, is it needed @Dtsitos ?


// Signed angle between right and horizontal projection
double angleRad = vtkMath::SignedAngleBetweenVectors(right, horizontal.data(), up);

return vtkMath::DegreesFromRadians(angleRad);

}

//----------------------------------------------------------------------------
double camera_impl::getWorldElevation() const
{
vector3_t view;
this->getPositionToFocalVector(view);
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);
}

//----------------------------------------------------------------------------
double camera_impl::getDistance() const
{
vector3_t v;
this->getPositionToFocalVector(v);
return vtkMath::Norm(v.data());
}

//----------------------------------------------------------------------------
camera& camera_impl::setViewUp(const vector3_t& up)
{
Expand Down
158 changes: 158 additions & 0 deletions library/testing/TestSDKCamera.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,164 @@ int TestSDKCamera([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
return EXIT_FAILURE;
}

// Test getters: world azimuth / elevation / distance
// Case 1: Horizontal view
cam.setPosition({ 0., -11., -1. });
cam.setFocalPoint({ 0., 0., -1. });
cam.setViewUp({ 1., 0., 0. });

double azimuth = cam.getWorldAzimuth();
double elevation = cam.getWorldElevation();
double distance = cam.getDistance();

if (!compareDouble(distance, 11.0))
{
std::cerr << "getDistance (horizontal) is not behaving as expected: " << distance << "\n";
return EXIT_FAILURE;
}

if (!compareDouble(azimuth, 0.0))
{
std::cerr << "getWorldAzimuth (horizontal) is not behaving as expected: " << azimuth << "\n";
return EXIT_FAILURE;
}

if (!compareDouble(elevation, 0.0))
{
std::cerr << "getWorldElevation (horizontal) is not behaving as expected: "
<< elevation << "\n";
return EXIT_FAILURE;
}

// Case 2: Positive elevation (+45 deg)
cam.setPosition({ 0., -11., -1. });
cam.setFocalPoint({ 0., 0., 10. });
cam.setViewUp({ 1., 0., 0. });

azimuth = cam.getWorldAzimuth();
elevation = cam.getWorldElevation();
distance = cam.getDistance();

if (!compareDouble(distance, std::sqrt(11.0 * 11.0 + 11.0 * 11.0)))
{
std::cerr << "getDistance (positive elevation) is not behaving as expected: "
<< distance << "\n";
return EXIT_FAILURE;
}

if (!compareDouble(azimuth, 0.0))
{
std::cerr << "getWorldAzimuth (positive elevation) is not behaving as expected: "
<< azimuth << "\n";
return EXIT_FAILURE;
}

if (!compareDouble(elevation, 45.0))
{
std::cerr << "getWorldElevation (positive elevation) is not behaving as expected: "
<< elevation << "\n";
return EXIT_FAILURE;
}

// Case 3: Negative elevation (-45 deg)
cam.setPosition({ 0., -11., 10. });
cam.setFocalPoint({ 0., 0., -1. });
cam.setViewUp({ 1., 0., 0. });

azimuth = cam.getWorldAzimuth();
elevation = cam.getWorldElevation();
distance = cam.getDistance();

if (!compareDouble(distance, std::sqrt(11.0 * 11.0 + 11.0 * 11.0)))
{
std::cerr << "getDistance (negative elevation) is not behaving as expected: "
<< distance << "\n";
return EXIT_FAILURE;
}

if (!compareDouble(azimuth, 0.0))
{
std::cerr << "getWorldAzimuth (negative elevation) is not behaving as expected: "
<< azimuth << "\n";
return EXIT_FAILURE;
}

if (!compareDouble(elevation, -45.0))
{
std::cerr << "getWorldElevation (negative elevation) is not behaving as expected: " << elevation
<< "\n";
return EXIT_FAILURE;
}

// Case 4: Custom up direction (non-Z up)
cam.setPosition({ -11., 0., 0. });
cam.setFocalPoint({ 0., 0., 0. });
cam.setViewUp({ 0., 1., 0. });

azimuth = cam.getWorldAzimuth();
elevation = cam.getWorldElevation();
distance = cam.getDistance();

if (!compareDouble(distance, 11.0))
{
std::cerr << "getDistance (custom up) is not behaving as expected: " << distance << "\n";
return EXIT_FAILURE;
}

if (!compareDouble(azimuth, 0.0))
{
std::cerr << "getWorldAzimuth (custom up) is not behaving as expected: " << azimuth << "\n";
return EXIT_FAILURE;
}

if (!compareDouble(elevation, 0.0))
{
std::cerr << "getWorldElevation (custom up) is not behaving as expected: " << elevation << "\n";
return EXIT_FAILURE;
}

// Case 5: position equals focal point (zero direction vector)
cam.setPosition({ 0., 0., 0. });
cam.setFocalPoint({ 0., 0., 0. });
cam.setViewUp({ 0., 1., 0. });

azimuth = cam.getWorldAzimuth();
elevation = cam.getWorldElevation();

if (!compareDouble(azimuth, 0.0))
{
std::cerr << "getWorldAzimuth with zero direction vector should return 0: " << azimuth << "\n";
return EXIT_FAILURE;
}

if (!compareDouble(elevation, 0.0))
{
std::cerr << "getWorldElevation with zero direction vector should return 0: " << elevation
<< "\n";
return EXIT_FAILURE;
}

// Case 6: view direction parallel to environment up
cam.setPosition({ 0., 0., -1. });
cam.setFocalPoint({ 0., 0., 0. });
cam.setViewUp({ 0., 0., 1. });

azimuth = cam.getWorldAzimuth();
elevation = cam.getWorldElevation();

if (!compareDouble(azimuth, 0.0))
{
std::cerr << "getWorldAzimuth with forward parallel to up should return 0: " << azimuth << "\n";
return EXIT_FAILURE;
}

if (!compareDouble(elevation, 90.0))
{
std::cerr << "getWorldElevation with forward parallel to up should be 90: " << elevation
<< "\n";
return EXIT_FAILURE;
}

// Test dolly
cam.dolly(10);
expectedPos = { 20.9, -11., -12. };
Expand Down
Loading
Loading