Skip to content

File test_camera.cpp

File List > gui > tests > test_camera.cpp

Go to the documentation of this file

#include <gtest/gtest.h>

#include <QPointF>
#include <QVector3D>
#include <cmath>

#include "gui/camera.hpp"

namespace {

constexpr double TOL = 1e-4;

QVector3D focal_point(const Camera& camera) { return camera.position() + camera.direction(); }

}  // namespace

TEST(Camera, PanToTargetMovesFocalPointToTarget) {
  Camera camera(800, 600);
  const QVector3D target(12.0f, -3.0f, 7.0f);
  const float focal_distance = camera.direction().length();

  camera.pan_to_target(target);

  const QVector3D focal_after = focal_point(camera);
  EXPECT_NEAR(focal_after.x(), target.x(), TOL);
  EXPECT_NEAR(focal_after.y(), target.y(), TOL);
  EXPECT_NEAR(focal_after.z(), target.z(), TOL);
  EXPECT_NEAR(camera.direction().length(), focal_distance, TOL);
}

TEST(Camera, PanToTargetNoOpWhenAlreadyCentered) {
  Camera camera(800, 600);
  const QVector3D before_pos = camera.position();
  const QVector3D before_dir = camera.direction();

  camera.pan_to_target(focal_point(camera));

  EXPECT_EQ(camera.position(), before_pos);
  EXPECT_EQ(camera.direction(), before_dir);
}

TEST(Camera, LookAtTargetReorientsWithoutMovingPosition) {
  Camera camera(800, 600);
  const QVector3D before_pos = camera.position();
  const QVector3D target(0.0f, 0.0f, 0.0f);

  camera.look_at_target(target);

  EXPECT_EQ(camera.position(), before_pos);
  const QVector3D dir = camera.direction().normalized();
  const QVector3D expected = (target - before_pos).normalized();
  EXPECT_NEAR(dir.x(), expected.x(), TOL);
  EXPECT_NEAR(dir.y(), expected.y(), TOL);
  EXPECT_NEAR(dir.z(), expected.z(), TOL);
}

TEST(Camera, ProjectWorldToScreenRoundTripsWithUnproject) {
  Camera camera(1024, 768);
  camera.set_scene_bounds(QVector3D(0, 0, 0), 1000.0f);
  const QVector3D world = camera.position() + camera.direction() * 0.5f;

  const auto screen = camera.project_world_to_screen(world);
  ASSERT_TRUE(screen.has_value());
  ASSERT_GE(screen->x(), 0.0);
  ASSERT_GE(screen->y(), 0.0);
  ASSERT_LE(screen->x(), camera.screen_width());
  ASSERT_LE(screen->y(), camera.screen_height());

  const QVector3D unprojected = camera.unproject(*screen);
  const QVector3D view_dir = (unprojected - camera.position()).normalized();
  const QVector3D to_world = (world - camera.position()).normalized();
  EXPECT_NEAR(view_dir.x(), to_world.x(), 0.02);
  EXPECT_NEAR(view_dir.y(), to_world.y(), 0.02);
  EXPECT_NEAR(view_dir.z(), to_world.z(), 0.02);
}

TEST(Camera, ProjectWorldToScreenBehindCameraReturnsSentinel) {
  Camera camera(800, 600);
  const QVector3D behind = camera.position() - camera.direction();

  const auto screen = camera.project_world_to_screen(behind);
  EXPECT_FALSE(screen.has_value());
}