#include "contracts.hpp" #include #include int main() { std::size_t checks=0; const auto require=[&](bool condition,const char* message){++checks;if(!condition)throw std::runtime_error(message);}; try { using namespace observatory; require(!unit({0,0,0}),"zero direction rejected"); const auto normal=unit({0,3,4}); require(normal&&std::abs(normal->y-0.6F)<1e-6F&&std::abs(normal->z-0.8F)<1e-6F,"known normalization"); require(!unit({std::numeric_limits::infinity(),0,0}),"nonfinite direction rejected"); const auto rotated=multiply(identity(),rotate_z(std::numbers::pi_v/2)); require(std::abs(rotated.data[0])<1e-6F&&std::abs(rotated.data[1]-1)<1e-6F,"quarter rotation and identity multiply"); require(!perspective(45,0,0.1F,100),"zero aspect rejected"); require(!perspective(45,1,1,0.1F),"reversed depth rejected"); const auto projection=perspective(60,1,0.1F,10); require(projection.has_value(),"valid perspective accepted"); const auto ndc=[&](float z){return (projection->data[10]*z+projection->data[14])/(-z);}; require(std::abs(ndc(-0.1F)+1)<1e-5F&&std::abs(ndc(-10)-1)<1e-5F,"OpenGL near/far NDC convention"); const auto close=[](float a,float b){return std::abs(a-b)<1e-5F;}; const auto view=look_at({0,0,4},{0,0,0},{0,1,0}); require(view.has_value(),"axis camera accepted"); const auto origin=transform(*view,{0,0,0,1}); const auto eye=transform(*view,{0,0,4,1}); require(close(origin[2],-4)&&close(origin[3],1),"target is four units along view negative Z"); require(close(eye[0],0)&&close(eye[1],0)&&close(eye[2],0),"eye maps to view origin"); const auto side_view=look_at({4,0,0},{0,0,0},{0,1,0}); require(side_view.has_value(),"side camera accepted"); const auto side_target=transform(*side_view,{0,0,0,1}); const auto side_right=transform(*side_view,{0,0,-1,1}); require(close(side_target[2],-4)&&close(side_right[0],1),"side camera basis and handedness"); require(!look_at({0,0,0},{0,0,0},{0,1,0}),"coincident eye and target rejected"); require(!look_at({0,0,4},{0,0,0},{0,0,1}),"parallel up rejected"); require(!look_at({0,0,4},{0,0,0},{0,0,0}),"zero up rejected"); require(!look_at({0,0,std::numeric_limits::quiet_NaN()},{0,0,0},{0,1,0}),"nonfinite view rejected"); const auto model=translation({1,2,3}); const auto moved=transform(model,{0,0,0,1}); const auto direction=transform(model,{0,1,0,0}); require(close(moved[0],1)&&close(moved[1],2)&&close(moved[2],3),"point translation"); require(close(direction[0],0)&&close(direction[1],1)&&close(direction[2],0),"direction is not translated"); const auto composed=transform(multiply(*projection,multiply(*view,model)),{0,0,0,1}); const auto staged=transform(*projection,transform(*view,transform(model,{0,0,0,1}))); require(composed==staged&&close(composed[3],1),"projection view model agrees with staged transform"); const auto wide=perspective(60,2,0.1F,10); require(wide&&close(wide->data[0]*2,projection->data[0])&&close(wide->data[5],projection->data[5]),"resize aspect changes horizontal scale only"); const auto edge=transform(*projection,{std::tan(std::numbers::pi_v/6),0,-1,1}); require(close(edge[0]/edge[3],1),"known frustum right edge maps to NDC one"); require(!rgb_bytes(0,10)&&!rgb_bytes(-1,10),"invalid capture extents rejected"); require(rgb_bytes(4096,4096)==16U*1024U*1024U*3U,"exact capture budget"); require(!rgb_bytes(4096,4097),"one row beyond budget rejected"); std::cout<