1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-12-26 10:00:42 +00:00
hyperrogue/devmods/reps/rep-polar.cpp
2023-06-21 15:10:42 +02:00

262 lines
9.0 KiB
C++

bool polar_mod = true, polar_choose = true;
namespace reps {
template<class N> static void cyclefix(N& a) {
while(a > + get_deg<N>(180)) a -= get_deg<N>(360);
while(a < - get_deg<N>(180)) a += get_deg<N>(360);
}
template<class N> static N cyclefix_on(N a) { cyclefix(a); return a; }
/** the Taylor polynomial for 1-sqrt(1-y*y) */
template<class N> N ssqrt(N y) {
return y*y/2 + y*y*y*y/8 + y*y*y*y*y*y*y/16 + y*y*y*y*y*y*y*y*y/128;
}
TD struct rep_polar2 {
using data = D;
using N = typename D::Number;
struct point { N phi, r; };
struct isometry { N psi, phi, r; }; // spin by psi first
static isometry cspin(int i, int j, N alpha) {
if(i>j) std::swap(i, j), alpha = -alpha;
return isometry{.psi = -alpha, .phi = N(0), .r = N(0) };
}
static isometry cspin90(int i, int j) { return cspin(i, j, get_deg<N>(90)); }
static isometry lorentz(int i, int j, N alpha) {
if(i>j) std::swap(i, j);
if(i == 0) return isometry{.psi = N(0), .phi = N(0), .r = alpha};
if(i == 1) return isometry{.psi = N(0), .phi = get_deg<N>(90), .r = alpha};
throw hr::hr_exception("bad lorentz");
}
static isometry id() { return isometry{.psi = N(0), .phi = N(0), .r = N(0)}; };
static point center() { return point{.phi = N(0), .r = N(0)}; };
static std::string print(isometry T) {
return hr::lalign(0, "{phi=", T.phi, " r=", T.r, " psi=", T.psi, "}");
}
static std::string print(point T) {
return hr::lalign(0, "{phi=", T.phi, " r=", T.r, "}");
}
static isometry apply(isometry T, isometry U, bool need_psi = true) {
if(T.r == 0) return isometry {.psi = T.psi+U.psi, .phi = T.psi+U.phi, .r = U.r};
if(U.r == 0) return isometry {.psi = T.psi+U.psi, .phi = T.phi, .r = T.r};
N alpha = U.phi + T.psi - T.phi;
if(polar_mod) cyclefix(alpha);
isometry res;
N y1 = sinh(U.r) * sin(alpha);
auto ca = cos(alpha);
auto sa = sin(alpha);
N x1, x2;
// choose the appropriate method
if(polar_choose && ca >= N(0.5)) {
N u = ca >= N(.999999) ? ssqrt(sa) : N(1) - ca;
res.r = cosh(T.r + U.r) - u * sinh(T.r) * sinh(U.r);
x1 = sinh(T.r + U.r) - u * cosh(T.r) * sinh(U.r);
if(need_psi) x2 = sinh(T.r + U.r) - u * cosh(U.r) * sinh(T.r);
}
else if(polar_choose && ca <= N(-0.5)) {
N u = ca <= N(-.999999) ? ssqrt(-sa) : ca + N(1);
res.r = cosh(T.r - U.r) + u * sinh(T.r) * sinh(U.r);
x1 = sinh(T.r - U.r) + u * cosh(T.r) * sinh(U.r);
if(need_psi) x2 = sinh(U.r - T.r) + u * cosh(U.r) * sinh(T.r);
}
else {
res.r = sinh(T.r) * sinh(U.r) * ca + cosh(T.r) * cosh(U.r);
x1 = cosh(T.r) * sinh(U.r) * ca + cosh(U.r) * sinh(T.r);
if(need_psi) x2 = cosh(U.r) * sinh(T.r) * ca + cosh(T.r) * sinh(U.r);
}
if(res.r < N(1)) res.r = N(0); else res.r = acosh(res.r);
N beta = (y1 || x1) ? atan2(y1, x1) : N(0);
res.phi = T.phi + beta;
if(polar_mod) cyclefix(res.phi);
if(need_psi) {
N y2 = sinh(T.r) * sin(alpha);
N gamma = (y2 || x2) ? atan2(y2, x2) : N(0);
res.psi = T.psi + U.psi + beta + gamma - alpha;
if(polar_mod) cyclefix(res.psi);
}
return res;
};
static point apply(const isometry& T, const point& x) {
isometry x1 = apply(T, push(x), false);
return point { .phi = x1.phi, .r = x1.r};
};
static isometry inverse(isometry T) { return isometry{.psi = -T.psi, .phi = cyclefix_on<N>(get_deg<N>(180)+T.phi-T.psi), .r=T.r }; };
static isometry push(const point& p) { return isometry{.psi = N(0), .phi = p.phi, .r = p.r}; }
static N dist0(const point& x) { return x.r; }
static N angle(const point& x) { return x.phi; }
static N get_coord(const point& x, int i) {
if(i == 0) return cos(x.phi) * sinh(x.r);
if(i == 1) return sin(x.phi) * sinh(x.r);
if(i == 2) return cosh(x.r);
throw hr::hr_exception("bad get_coord");
}
};
TD struct rep_high_polar {
using data = D;
using N = typename D::Number;
struct sphere_data {
using Number = N;
static constexpr int Dim = D::Dim-1;
static constexpr int Flipped = -1;
};
using subsphere = rep_linear_nn<sphere_data>;
struct point { typename subsphere::point phi; N r; };
struct isometry { typename subsphere::isometry psi; typename subsphere::point phi; N r; };
static isometry cspin(int i, int j, N alpha) {
return isometry{.psi = subsphere::cspin(i, j, alpha), .phi = subsphere::center(), .r = N(0) };
}
static isometry cspin90(int i, int j) {
return isometry{.psi = subsphere::cspin90(i, j), .phi = subsphere::center(), .r = N(0) };
}
static isometry lorentz(int i, int j, N alpha) {
if(i>j) std::swap(i, j);
auto is = isometry{.psi = subsphere::id(), .phi = subsphere::center(), .r = alpha};
is.phi[D::Dim-2] = N(0);
is.phi[i] = N(1);
return is;
}
static isometry id() { return isometry{.psi = subsphere::id(), .phi = subsphere::center(), .r = N(0)}; }
static point center() { return point{.phi = subsphere::center(), .r = N(0)}; };
static std::string print(isometry T) {
return hr::lalign(0, "{phi=", subsphere::print(T.phi), " r=", T.r, " psi=", hr::kz(T.psi.values), "}");
}
static std::string print(point T) {
return hr::lalign(0, "{phi=", subsphere::print(T.phi), " r=", T.r, "}");
}
static isometry apply(isometry T, isometry U, bool need_psi = true) {
auto apsi = need_psi ? T.psi * U.psi : subsphere::id();
if(T.r == 0) return isometry {.psi = apsi, .phi = T.psi*U.phi, .r = U.r};
if(U.r == 0) return isometry {.psi = apsi, .phi = T.phi, .r = T.r};
auto aphi = T.psi * U.phi;
auto cos_alpha = inner<sphere_data>(aphi, T.phi);
auto& ca = cos_alpha;
isometry res;
N x1, x2;
auto orth = (aphi - T.phi * ca);
N sin_alpha;
if(ca > N(0.999999) || ca < N(0.999999))
sin_alpha = pow(sqnorm<sphere_data>(orth), .5);
else
sin_alpha = pow(N(1) - ca * ca, .5);
if(sin_alpha == N(0)) {
if(ca >= N(1)) {
return isometry{.psi = apsi, .phi = T.phi, .r = T.r + U.r };
}
if(ca <= N(-1)) {
if(T.r >= U.r) {
return isometry{.psi = apsi, .phi = T.phi, .r = T.r - U.r };
}
else {
return isometry{.psi = apsi, .phi = T.phi*-1, .r = U.r - T.r };
}
}
}
orth = orth / sin_alpha;
N y1 = sinh(U.r) * sin_alpha;
// choose the appropriate method
if(polar_choose && ca >= N(0.5)) {
N u = ca >= N(.999999) ? ssqrt(sin_alpha) : N(1) - ca;
res.r = cosh(T.r + U.r) - u * sinh(T.r) * sinh(U.r);
x1 = sinh(T.r + U.r) - u * cosh(T.r) * sinh(U.r);
if(need_psi) x2 = sinh(T.r + U.r) - u * cosh(U.r) * sinh(T.r);
}
else if(polar_choose && ca <= N(-0.5)) {
N u = ca <= N(-.999999) ? ssqrt(sin_alpha) : ca + N(1); // ca = u - 1
res.r = cosh(T.r - U.r) + u * sinh(T.r) * sinh(U.r);
x1 = sinh(T.r - U.r) + u * cosh(T.r) * sinh(U.r);
if(need_psi) x2 = sinh(U.r - T.r) + u * cosh(U.r) * sinh(T.r);
}
else {
res.r = sinh(T.r) * sinh(U.r) * ca + cosh(T.r) * cosh(U.r);
x1 = cosh(T.r) * sinh(U.r) * ca + cosh(U.r) * sinh(T.r);
if(need_psi) x2 = cosh(U.r) * sinh(T.r) * ca + cosh(T.r) * sinh(U.r);
}
if(res.r < N(1)) res.r = N(0); else res.r = acosh(res.r);
auto h1 = pow(x1*x1+y1*y1, -0.5);
N cos_beta = x1*h1, sin_beta = y1*h1;
res.phi = T.phi * cos_beta + orth * sin_beta;
if(need_psi) {
N y2 = sinh(T.r) * sin_alpha;
auto h2 = pow(x2*x2+y2*y2, -0.5);
N cos_gamma = x2*h2, sin_gamma = y2*h2;
// delta = beta + gamma - alpha
auto cos_beta_gamma = cos_beta * cos_gamma - sin_beta * sin_gamma;
auto sin_beta_gamma = cos_beta * sin_gamma + sin_beta * cos_gamma;
auto cos_delta = cos_beta_gamma * cos_alpha + sin_beta_gamma * sin_alpha;
auto sin_delta = sin_beta_gamma * cos_alpha - cos_beta_gamma * sin_alpha;
auto phi1 = T.phi * cos_delta + orth * sin_delta;
auto orth1 = orth * cos_delta - T.phi * sin_delta;
auto phi2 = phi1 - T.phi;
auto orth2 = orth1 - orth;
typename subsphere::isometry spinner = subsphere::id();
// Tv = v + <v, phi> * (phi'-phi) + <v, orth> * (orth'-orth)
for(int i=0; i<D::Dim-1; i++)
for(int j=0; j<D::Dim-1; j++)
spinner[i][j] += phi2[i] * T.phi[j] + orth2[i] * orth[j];
res.psi = spinner * apsi;
}
return res;
};
static point apply(const isometry& T, const point& x) {
isometry x1 = apply(T, push(x), false);
return point { .phi = x1.phi, .r = x1.r};
};
static isometry inverse(isometry T) { return isometry{.psi = subsphere::inverse(T.psi), .phi = subsphere::inverse(T.psi)*T.phi*-1, .r=T.r }; };
static isometry push(const point& p) { return isometry{.psi = subsphere::id(), .phi = p.phi, .r = p.r}; }
static N dist0(const point& x) { return x.r; }
static N angle(const point& x) { return subsphere::angle(x.phi); }
static N get_coord(const point& x, int i) { if(i == D::Dim-1) return cosh(x.r); else return x.phi[i] * sinh(x.r); }
};
template<class D> using rep_polar = typename std::conditional<D::Dim==3, rep_polar2<D>, rep_high_polar<D>>::type;
}