| 372 | } |
| 373 | |
| 374 | void SphericalProjection::project_input(double dx, double dy, double dz, |
| 375 | double in_fov_r, int W, int H, |
| 376 | double &uf, double &vf) const { |
| 377 | if (input_model == INPUT_EQUIRECT) { |
| 378 | // Center (-Z) -> lon=0; +X (screen right) -> +lon |
| 379 | double lon = std::atan2(dx, -dz); |
| 380 | double lat = std::asin(std::clamp(dy, -1.0, 1.0)); |
| 381 | |
| 382 | if (projection_mode == MODE_RECT_HEMISPHERE) |
| 383 | lon = std::clamp(lon, -M_PI / 2.0, M_PI / 2.0); |
| 384 | |
| 385 | double horiz_span = (projection_mode == MODE_RECT_HEMISPHERE) ? M_PI : 2.0 * M_PI; |
| 386 | double lon_offset = (projection_mode == MODE_RECT_HEMISPHERE) ? M_PI / 2.0 : M_PI; |
| 387 | uf = ((lon + lon_offset) / horiz_span) * W; |
| 388 | |
| 389 | // Image Y grows downward: north (lat = +π/2) at top |
| 390 | vf = (M_PI / 2.0 - lat) / M_PI * H; |
| 391 | return; |
| 392 | } |
| 393 | |
| 394 | // -------- Fisheye inputs -------- |
| 395 | // Optical axis default is -Z; "Invert" flips hemisphere. |
| 396 | const double ax = 0.0, ay = 0.0; |
| 397 | double az = -1.0; |
| 398 | if (invert == INVERT_BACK) az = 1.0; |
| 399 | |
| 400 | double cos_t = std::clamp(dx * ax + dy * ay + dz * az, -1.0, 1.0); |
| 401 | double theta = std::acos(cos_t); |
| 402 | double tmax = std::max(1e-6, in_fov_r * 0.5); |
| 403 | |
| 404 | double r_norm = 0.0; |
| 405 | switch (input_model) { |
| 406 | case INPUT_FEQ_EQUIDISTANT: r_norm = theta / tmax; break; |
| 407 | case INPUT_FEQ_EQUISOLID: r_norm = std::sin(theta*0.5) / std::max(1e-12, std::sin(tmax*0.5)); break; |
| 408 | case INPUT_FEQ_STEREOGRAPHIC: r_norm = std::tan(theta*0.5) / std::max(1e-12, std::tan(tmax*0.5)); break; |
| 409 | case INPUT_FEQ_ORTHOGRAPHIC: r_norm = std::sin(theta) / std::max(1e-12, std::sin(tmax)); break; |
| 410 | default: r_norm = theta / tmax; break; |
| 411 | } |
| 412 | |
| 413 | // Azimuth in camera XY; final Y is downward -> subtract sine in vf |
| 414 | double phi = std::atan2(dy, dx); |
| 415 | |
| 416 | double R = 0.5 * std::min(W, H); |
| 417 | double rpx = r_norm * R; |
| 418 | uf = W * 0.5 + rpx * std::cos(phi); |
| 419 | vf = H * 0.5 - rpx * std::sin(phi); |
| 420 | } |
| 421 | |
| 422 | std::string SphericalProjection::Json() const |
| 423 | { |
nothing calls this directly
no outgoing calls
no test coverage detected