Files
ConformalLabpp/code/include/hyper_ideal_visualization_utility.hpp
user2595 704f42bbfd
Some checks failed
C++ Tests / test-fast (push) Has started running
C++ Tests / test-cgal (push) Has been cancelled
API Docs / doc-build (push) Has been cancelled
Doxygen → Codeberg Pages / publish (push) Has been cancelled
Markdown link check / check (push) Has been cancelled
Mirror to Codeberg / mirror (push) Has been cancelled
Merge pull request 'ci+quality: structural gates (CI: 3 new; local: 7 new + .clang-tidy)' (#18) from ci/structural-tests into main
2026-05-26 09:14:45 +00:00

131 lines
4.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
// Copyright (c) 2024-2026 Tarik Moussa.
// SPDX-License-Identifier: MIT
// Port of the static helper
// HyperIdealVisualizationPlugin.getEuclideanCircleFromHyperbolic()
// from de.varylab.discreteconformal.plugin.
//
// Converts a hyperbolic circle (center + radius in the hyperboloid model)
// to its Euclidean representation (cx, cy, r) in the Poincaré disk model.
//
// Mathematical background
// -----------------------
// Hyperboloid model: points (x,y,z,w) with w²-x²-y²-z²=1, w>0.
// Metric signature: g = diag(+1,+1,+1,1) (spatial-first, time-last).
//
// Hyperbolic translation from the origin e₄=(0,0,0,1) to p=(a,b,c,d):
// T = [ I₃ + p'·p'ᵀ/(d+1) p' ] p' = (a,b,c)
// [ p'ᵀ d ]
// This is the standard Lorentz boost; it is in O(3,1) and maps e₄ → p.
//
// Poincaré disk projection (jReality convention):
// (x,y,z,w) → (x,y) / (w+1)
//
// The three reference points on the unit hyperbolic circle (at origin) are
// p1 = (sinh r, 0, 0, cosh r)
// p2 = (0, sinh r, 0, cosh r)
// p3 = (-sinh r, 0, 0, cosh r)
// After translation and projection to the Poincaré disk their circumcircle
// equals the image of the original hyperbolic circle.
#include <Eigen/Dense>
#include <array>
#include <cmath>
namespace conformallab {
/// Circumcenter of three 2-D points (`a`, `b`, `c`) in the Euclidean plane.
inline Eigen::Vector2d circumcenter2d(
const Eigen::Vector2d& a,
const Eigen::Vector2d& b,
const Eigen::Vector2d& c)
{
double ax = a.x(), ay = a.y();
double bx = b.x(), by = b.y();
double cx = c.x(), cy = c.y();
double D = 2.0 * (ax*(by - cy) + bx*(cy - ay) + cx*(ay - by));
double a2 = ax*ax + ay*ay;
double b2 = bx*bx + by*by;
double c2 = cx*cx + cy*cy;
double ux = (a2*(by - cy) + b2*(cy - ay) + c2*(ay - by)) / D;
double uy = (a2*(cx - bx) + b2*(ax - cx) + c2*(bx - ax)) / D;
return {ux, uy};
}
/// 4×4 Lorentz boost: maps the hyperboloid origin `e₄ = (0,0,0,1)` to
/// `center`. Precondition: `center` lies on the hyperboloid.
inline Eigen::Matrix4d hyperboloidTranslation(const Eigen::Vector4d& center)
{
Eigen::Vector3d p = center.head<3>();
double d = center(3);
Eigen::Matrix4d T = Eigen::Matrix4d::Identity();
// Upper-left 3×3 block: I + p'·p'ᵀ / (d+1)
T.block<3,3>(0,0) += p * p.transpose() / (d + 1.0);
// Right column and bottom row
T.block<3,1>(0,3) = p;
T.block<1,3>(3,0) = p.transpose();
T(3,3) = d;
return T;
}
/// Project a hyperboloid point `x` onto the Poincaré disk (jReality
/// convention: add 1 to the w-coordinate, then dehomogenise spatial part).
inline Eigen::Vector2d toPoincareDisk(const Eigen::Vector4d& x)
{
double w = x(3) + 1.0;
return {x(0) / w, x(1) / w};
}
// ---------------------------------------------------------------------------
// getEuclideanCircleFromHyperbolic
//
// Inputs
// center point on the hyperboloid, e.g. (0,0,0,1) for the origin
// radius hyperbolic radius (real number > 0)
//
// Output
// { euclidean_cx, euclidean_cy, euclidean_radius }
// describing the circle in the Poincaré disk that corresponds to the
// given hyperbolic circle.
//
// Port of HyperIdealVisualizationPlugin.getEuclideanCircleFromHyperbolic()
// ---------------------------------------------------------------------------
/// Convert a hyperbolic circle (`center` on the hyperboloid, hyperbolic
/// `radius`) to the corresponding Euclidean circle in the Poincaré disk;
/// returns `{cx, cy, r}`. Port of `HyperIdealVisualizationPlugin
/// .getEuclideanCircleFromHyperbolic()`.
inline std::array<double,3> getEuclideanCircleFromHyperbolic(
const Eigen::Vector4d& center, double radius)
{
const double s = std::sinh(radius);
const double ch = std::cosh(radius);
// Three points on the hyperbolic circle centered at the origin
Eigen::Vector4d p1( s, 0.0, 0.0, ch);
Eigen::Vector4d p2(0.0, s, 0.0, ch);
Eigen::Vector4d p3(-s, 0.0, 0.0, ch);
// Apply the hyperbolic translation to the target center
const Eigen::Matrix4d T = hyperboloidTranslation(center);
p1 = T * p1;
p2 = T * p2;
p3 = T * p3;
// Project to the Poincaré disk
const Eigen::Vector2d q1 = toPoincareDisk(p1);
const Eigen::Vector2d q2 = toPoincareDisk(p2);
const Eigen::Vector2d q3 = toPoincareDisk(p3);
// Euclidean circumcircle of the three projected points
const Eigen::Vector2d ec = circumcenter2d(q1, q2, q3);
const double r = (ec - q1).norm();
return {ec.x(), ec.y(), r};
}
} // namespace conformallab